@@ -311,6 +311,12 @@ acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg)
311
311
acpi_status acpi_hw_write (u32 value , struct acpi_generic_address * reg )
312
312
{
313
313
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 ;
314
320
acpi_status status ;
315
321
316
322
ACPI_FUNCTION_NAME (hw_write );
@@ -322,23 +328,145 @@ acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg)
322
328
return (status );
323
329
}
324
330
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
+
325
337
/*
326
338
* Two address spaces supported: Memory or IO. PCI_Config is
327
339
* not supported here because the GAS structure is insufficient
328
340
*/
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 ++ ;
337
465
}
338
466
339
467
ACPI_DEBUG_PRINT ((ACPI_DB_IO ,
340
468
"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 ),
342
470
acpi_ut_get_region_name (reg -> space_id )));
343
471
344
472
return (status );
0 commit comments