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
|
// SPDX-License-Identifier: GPL-2.0
/*
* Generation of tables for particular device types
*
* Copyright 2019 Google LLC
* Mostly taken from coreboot file of the same name
*/
#include <common.h>
#include <dm.h>
#include <irq.h>
#include <log.h>
#include <acpi/acpi_device.h>
#include <acpi/acpigen.h>
#include <dm/acpi.h>
/**
* acpi_device_path_fill() - Find the root device and build a path from there
*
* This recursively reaches back to the root device and progressively adds path
* elements until the device is reached.
*
* @dev: Device to return path of
* @buf: Buffer to hold the path
* @buf_len: Length of buffer
* @cur: Current position in the buffer
* @return new position in buffer after adding @dev, or -ve on error
*/
static int acpi_device_path_fill(const struct udevice *dev, char *buf,
size_t buf_len, int cur)
{
char name[ACPI_NAME_MAX];
int next = 0;
int ret;
ret = acpi_get_name(dev, name);
if (ret)
return ret;
/*
* Make sure this name segment will fit, including the path segment
* separator and possible NULL terminator, if this is the last segment.
*/
if (cur + strlen(name) + 2 > buf_len)
return -ENOSPC;
/* Walk up the tree to the root device */
if (dev_get_parent(dev)) {
next = acpi_device_path_fill(dev_get_parent(dev), buf, buf_len,
cur);
if (next < 0)
return next;
}
/* Fill in the path from the root device */
next += snprintf(buf + next, buf_len - next, "%s%s",
dev_get_parent(dev) && *name ? "." : "", name);
return next;
}
int acpi_device_path(const struct udevice *dev, char *buf, int maxlen)
{
int ret;
ret = acpi_device_path_fill(dev, buf, maxlen, 0);
if (ret < 0)
return ret;
return 0;
}
int acpi_device_scope(const struct udevice *dev, char *scope, int maxlen)
{
int ret;
if (!dev_get_parent(dev))
return log_msg_ret("noparent", -EINVAL);
ret = acpi_device_path_fill(dev_get_parent(dev), scope, maxlen, 0);
if (ret < 0)
return log_msg_ret("fill", ret);
return 0;
}
enum acpi_dev_status acpi_device_status(const struct udevice *dev)
{
return ACPI_DSTATUS_ALL_ON;
}
/**
* largeres_write_len_f() - Write a placeholder word value
*
* Write a forward length for a large resource (2 bytes)
*
* @return pointer to the zero word (for fixing up later)
*/
static void *largeres_write_len_f(struct acpi_ctx *ctx)
{
u8 *p = acpigen_get_current(ctx);
acpigen_emit_word(ctx, 0);
return p;
}
/**
* largeres_fill_from_len() - Fill in a length value
*
* This calculated the number of bytes since the provided @start and writes it
* to @ptr, which was previous returned by largeres_write_len_f().
*
* @ptr: Word to update
* @start: Start address to count from to calculated the length
*/
static void largeres_fill_from_len(struct acpi_ctx *ctx, char *ptr, u8 *start)
{
u16 len = acpigen_get_current(ctx) - start;
ptr[0] = len & 0xff;
ptr[1] = (len >> 8) & 0xff;
}
/**
* largeres_fill_len() - Fill in a length value, excluding the length itself
*
* Fill in the length field with the value calculated from after the 16bit
* field to acpigen current. This is useful since the length value does not
* include the length field itself.
*
* This calls acpi_device_largeres_fill_len() passing @ptr + 2 as @start
*
* @ptr: Word to update.
*/
static void largeres_fill_len(struct acpi_ctx *ctx, void *ptr)
{
largeres_fill_from_len(ctx, ptr, ptr + sizeof(u16));
}
/* ACPI 6.3 section 6.4.3.6: Extended Interrupt Descriptor */
static int acpi_device_write_interrupt(struct acpi_ctx *ctx,
const struct acpi_irq *irq)
{
void *desc_length;
u8 flags;
if (!irq->pin)
return -ENOENT;
/* This is supported by GpioInt() but not Interrupt() */
if (irq->polarity == ACPI_IRQ_ACTIVE_BOTH)
return -EINVAL;
/* Byte 0: Descriptor Type */
acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_INTERRUPT);
/* Byte 1-2: Length (filled in later) */
desc_length = largeres_write_len_f(ctx);
/*
* Byte 3: Flags
* [7:5]: Reserved
* [4]: Wake (0=NO_WAKE 1=WAKE)
* [3]: Sharing (0=EXCLUSIVE 1=SHARED)
* [2]: Polarity (0=HIGH 1=LOW)
* [1]: Mode (0=LEVEL 1=EDGE)
* [0]: Resource (0=PRODUCER 1=CONSUMER)
*/
flags = BIT(0); /* ResourceConsumer */
if (irq->mode == ACPI_IRQ_EDGE_TRIGGERED)
flags |= BIT(1);
if (irq->polarity == ACPI_IRQ_ACTIVE_LOW)
flags |= BIT(2);
if (irq->shared == ACPI_IRQ_SHARED)
flags |= BIT(3);
if (irq->wake == ACPI_IRQ_WAKE)
flags |= BIT(4);
acpigen_emit_byte(ctx, flags);
/* Byte 4: Interrupt Table Entry Count */
acpigen_emit_byte(ctx, 1);
/* Byte 5-8: Interrupt Number */
acpigen_emit_dword(ctx, irq->pin);
/* Fill in Descriptor Length (account for len word) */
largeres_fill_len(ctx, desc_length);
return 0;
}
int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx,
const struct irq *req_irq)
{
struct acpi_irq irq;
int ret;
ret = irq_get_acpi(req_irq, &irq);
if (ret)
return log_msg_ret("get", ret);
ret = acpi_device_write_interrupt(ctx, &irq);
if (ret)
return log_msg_ret("write", ret);
return 0;
}
|