Skip to content

Commit 66b1ed5

Browse files
Lv Zhengrafaeljw
authored andcommitted
ACPICA: ACPI 2.0, Hardware: Add access_width/bit_offset support for acpi_hw_write()
ACPICA commit 48eea5e7993ccb7189bd63cd726e02adafee6057 This patch adds access_width/bit_offset support in acpi_hw_write(). Lv Zheng. Link: acpica/acpica@48eea5e7 Link: https://bugs.acpica.org/show_bug.cgi?id=1240 Signed-off-by: Lv Zheng <[email protected]> Signed-off-by: Bob Moore <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent c3bc26d commit 66b1ed5

File tree

1 file changed

+137
-9
lines changed

1 file changed

+137
-9
lines changed

drivers/acpi/acpica/hwregs.c

Lines changed: 137 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,12 @@ acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg)
311311
acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg)
312312
{
313313
u64 address;
314+
u8 access_width;
315+
u32 bit_width;
316+
u8 bit_offset;
317+
u64 value64;
318+
u32 new_value32, old_value32;
319+
u8 index;
314320
acpi_status status;
315321

316322
ACPI_FUNCTION_NAME(hw_write);
@@ -322,23 +328,145 @@ acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg)
322328
return (status);
323329
}
324330

331+
/* Convert access_width into number of bits based */
332+
333+
access_width = acpi_hw_get_access_bit_width(reg, 32);
334+
bit_width = reg->bit_offset + reg->bit_width;
335+
bit_offset = reg->bit_offset;
336+
325337
/*
326338
* Two address spaces supported: Memory or IO. PCI_Config is
327339
* not supported here because the GAS structure is insufficient
328340
*/
329-
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
330-
status = acpi_os_write_memory((acpi_physical_address)
331-
address, (u64)value,
332-
reg->bit_width);
333-
} else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
334-
335-
status = acpi_hw_write_port((acpi_io_address)
336-
address, value, reg->bit_width);
341+
index = 0;
342+
while (bit_width) {
343+
/*
344+
* Use offset style bit reads because "Index * AccessWidth" is
345+
* ensured to be less than 32-bits by acpi_hw_validate_register().
346+
*/
347+
new_value32 = ACPI_GET_BITS(&value, index * access_width,
348+
ACPI_MASK_BITS_ABOVE_32
349+
(access_width));
350+
351+
if (bit_offset >= access_width) {
352+
bit_offset -= access_width;
353+
} else {
354+
/*
355+
* Use offset style bit masks because access_width is ensured
356+
* to be less than 32-bits by acpi_hw_validate_register() and
357+
* bit_offset/bit_width is less than access_width here.
358+
*/
359+
if (bit_offset) {
360+
new_value32 &= ACPI_MASK_BITS_BELOW(bit_offset);
361+
}
362+
if (bit_width < access_width) {
363+
new_value32 &= ACPI_MASK_BITS_ABOVE(bit_width);
364+
}
365+
366+
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
367+
if (bit_offset || bit_width < access_width) {
368+
/*
369+
* Read old values in order not to modify the bits that
370+
* are beyond the register bit_width/bit_offset setting.
371+
*/
372+
status =
373+
acpi_os_read_memory((acpi_physical_address)
374+
address +
375+
index *
376+
ACPI_DIV_8
377+
(access_width),
378+
&value64,
379+
access_width);
380+
old_value32 = (u32)value64;
381+
382+
/*
383+
* Use offset style bit masks because access_width is
384+
* ensured to be less than 32-bits by
385+
* acpi_hw_validate_register() and bit_offset/bit_width is
386+
* less than access_width here.
387+
*/
388+
if (bit_offset) {
389+
old_value32 &=
390+
ACPI_MASK_BITS_ABOVE
391+
(bit_offset);
392+
bit_offset = 0;
393+
}
394+
if (bit_width < access_width) {
395+
old_value32 &=
396+
ACPI_MASK_BITS_BELOW
397+
(bit_width);
398+
}
399+
400+
new_value32 |= old_value32;
401+
}
402+
403+
value64 = (u64)new_value32;
404+
status =
405+
acpi_os_write_memory((acpi_physical_address)
406+
address +
407+
index *
408+
ACPI_DIV_8
409+
(access_width),
410+
value64, access_width);
411+
} else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
412+
413+
if (bit_offset || bit_width < access_width) {
414+
/*
415+
* Read old values in order not to modify the bits that
416+
* are beyond the register bit_width/bit_offset setting.
417+
*/
418+
status =
419+
acpi_hw_read_port((acpi_io_address)
420+
address +
421+
index *
422+
ACPI_DIV_8
423+
(access_width),
424+
&old_value32,
425+
access_width);
426+
427+
/*
428+
* Use offset style bit masks because access_width is
429+
* ensured to be less than 32-bits by
430+
* acpi_hw_validate_register() and bit_offset/bit_width is
431+
* less than access_width here.
432+
*/
433+
if (bit_offset) {
434+
old_value32 &=
435+
ACPI_MASK_BITS_ABOVE
436+
(bit_offset);
437+
bit_offset = 0;
438+
}
439+
if (bit_width < access_width) {
440+
old_value32 &=
441+
ACPI_MASK_BITS_BELOW
442+
(bit_width);
443+
}
444+
445+
new_value32 |= old_value32;
446+
}
447+
448+
status = acpi_hw_write_port((acpi_io_address)
449+
address +
450+
index *
451+
ACPI_DIV_8
452+
(access_width),
453+
new_value32,
454+
access_width);
455+
}
456+
}
457+
458+
/*
459+
* Index * access_width is ensured to be less than 32-bits by
460+
* acpi_hw_validate_register().
461+
*/
462+
bit_width -=
463+
bit_width > access_width ? access_width : bit_width;
464+
index++;
337465
}
338466

339467
ACPI_DEBUG_PRINT((ACPI_DB_IO,
340468
"Wrote: %8.8X width %2d to %8.8X%8.8X (%s)\n",
341-
value, reg->bit_width, ACPI_FORMAT_UINT64(address),
469+
value, access_width, ACPI_FORMAT_UINT64(address),
342470
acpi_ut_get_region_name(reg->space_id)));
343471

344472
return (status);

0 commit comments

Comments
 (0)