summaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/acpi
parentInitial commit. (diff)
downloadlinux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz
linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/Kconfig603
-rw-r--r--drivers/acpi/Makefile132
-rw-r--r--drivers/acpi/ac.c332
-rw-r--r--drivers/acpi/acpi_adxl.c192
-rw-r--r--drivers/acpi/acpi_amba.c130
-rw-r--r--drivers/acpi/acpi_apd.c261
-rw-r--r--drivers/acpi/acpi_cmos_rtc.c87
-rw-r--r--drivers/acpi/acpi_configfs.c286
-rw-r--r--drivers/acpi/acpi_dbg.c785
-rw-r--r--drivers/acpi/acpi_extlog.c326
-rw-r--r--drivers/acpi/acpi_fpdt.c316
-rw-r--r--drivers/acpi/acpi_ipmi.c644
-rw-r--r--drivers/acpi/acpi_lpat.c151
-rw-r--r--drivers/acpi/acpi_lpit.c155
-rw-r--r--drivers/acpi/acpi_lpss.c1378
-rw-r--r--drivers/acpi/acpi_memhotplug.c372
-rw-r--r--drivers/acpi/acpi_pad.c502
-rw-r--r--drivers/acpi/acpi_pcc.c142
-rw-r--r--drivers/acpi/acpi_platform.c171
-rw-r--r--drivers/acpi/acpi_pnp.c388
-rw-r--r--drivers/acpi/acpi_processor.c893
-rw-r--r--drivers/acpi/acpi_tad.c674
-rw-r--r--drivers/acpi/acpi_video.c2308
-rw-r--r--drivers/acpi/acpi_watchdog.c190
-rw-r--r--drivers/acpi/acpica/Makefile211
-rw-r--r--drivers/acpi/acpica/acapps.h154
-rw-r--r--drivers/acpi/acpica/accommon.h32
-rw-r--r--drivers/acpi/acpica/acconvert.h108
-rw-r--r--drivers/acpi/acpica/acdebug.h290
-rw-r--r--drivers/acpi/acpica/acdispat.h344
-rw-r--r--drivers/acpi/acpica/acevents.h252
-rw-r--r--drivers/acpi/acpica/acglobal.h400
-rw-r--r--drivers/acpi/acpica/achware.h131
-rw-r--r--drivers/acpi/acpica/acinterp.h543
-rw-r--r--drivers/acpi/acpica/aclocal.h1286
-rw-r--r--drivers/acpi/acpica/acmacros.h499
-rw-r--r--drivers/acpi/acpica/acnamesp.h402
-rw-r--r--drivers/acpi/acpica/acobject.h443
-rw-r--r--drivers/acpi/acpica/acopcode.h297
-rw-r--r--drivers/acpi/acpica/acparser.h227
-rw-r--r--drivers/acpi/acpica/acpredef.h1193
-rw-r--r--drivers/acpi/acpica/acresrc.h366
-rw-r--r--drivers/acpi/acpica/acstruct.h222
-rw-r--r--drivers/acpi/acpica/actables.h153
-rw-r--r--drivers/acpi/acpica/acutils.h744
-rw-r--r--drivers/acpi/acpica/amlcode.h452
-rw-r--r--drivers/acpi/acpica/amlresrc.h573
-rw-r--r--drivers/acpi/acpica/dbcmds.c1146
-rw-r--r--drivers/acpi/acpica/dbconvert.c453
-rw-r--r--drivers/acpi/acpica/dbdisply.c1096
-rw-r--r--drivers/acpi/acpica/dbexec.c875
-rw-r--r--drivers/acpi/acpica/dbfileio.c119
-rw-r--r--drivers/acpi/acpica/dbhistry.c204
-rw-r--r--drivers/acpi/acpica/dbinput.c1261
-rw-r--r--drivers/acpi/acpica/dbmethod.c589
-rw-r--r--drivers/acpi/acpica/dbnames.c1035
-rw-r--r--drivers/acpi/acpica/dbobject.c516
-rw-r--r--drivers/acpi/acpica/dbstats.c509
-rw-r--r--drivers/acpi/acpica/dbtest.c1104
-rw-r--r--drivers/acpi/acpica/dbutils.c421
-rw-r--r--drivers/acpi/acpica/dbxface.c522
-rw-r--r--drivers/acpi/acpica/dsargs.c374
-rw-r--r--drivers/acpi/acpica/dscontrol.c372
-rw-r--r--drivers/acpi/acpica/dsdebug.c205
-rw-r--r--drivers/acpi/acpica/dsfield.c816
-rw-r--r--drivers/acpi/acpica/dsinit.c224
-rw-r--r--drivers/acpi/acpica/dsmethod.c848
-rw-r--r--drivers/acpi/acpica/dsmthdat.c681
-rw-r--r--drivers/acpi/acpica/dsobject.c545
-rw-r--r--drivers/acpi/acpica/dsopcode.c762
-rw-r--r--drivers/acpi/acpica/dspkginit.c531
-rw-r--r--drivers/acpi/acpica/dsutils.c831
-rw-r--r--drivers/acpi/acpica/dswexec.c758
-rw-r--r--drivers/acpi/acpica/dswload.c596
-rw-r--r--drivers/acpi/acpica/dswload2.c745
-rw-r--r--drivers/acpi/acpica/dswscope.c181
-rw-r--r--drivers/acpi/acpica/dswstate.c728
-rw-r--r--drivers/acpi/acpica/evevent.c313
-rw-r--r--drivers/acpi/acpica/evglock.c310
-rw-r--r--drivers/acpi/acpica/evgpe.c849
-rw-r--r--drivers/acpi/acpica/evgpeblk.c501
-rw-r--r--drivers/acpi/acpica/evgpeinit.c423
-rw-r--r--drivers/acpi/acpica/evgpeutil.c326
-rw-r--r--drivers/acpi/acpica/evhandler.c534
-rw-r--r--drivers/acpi/acpica/evmisc.c273
-rw-r--r--drivers/acpi/acpica/evregion.c873
-rw-r--r--drivers/acpi/acpica/evrgnini.c621
-rw-r--r--drivers/acpi/acpica/evsci.c214
-rw-r--r--drivers/acpi/acpica/evxface.c1079
-rw-r--r--drivers/acpi/acpica/evxfevnt.c365
-rw-r--r--drivers/acpi/acpica/evxfgpe.c1049
-rw-r--r--drivers/acpi/acpica/evxfregn.c230
-rw-r--r--drivers/acpi/acpica/exconcat.c405
-rw-r--r--drivers/acpi/acpica/exconfig.c551
-rw-r--r--drivers/acpi/acpica/exconvrt.c692
-rw-r--r--drivers/acpi/acpica/excreate.c470
-rw-r--r--drivers/acpi/acpica/exdebug.c308
-rw-r--r--drivers/acpi/acpica/exdump.c1205
-rw-r--r--drivers/acpi/acpica/exfield.c400
-rw-r--r--drivers/acpi/acpica/exfldio.c967
-rw-r--r--drivers/acpi/acpica/exmisc.c451
-rw-r--r--drivers/acpi/acpica/exmutex.c506
-rw-r--r--drivers/acpi/acpica/exnames.c398
-rw-r--r--drivers/acpi/acpica/exoparg1.c1086
-rw-r--r--drivers/acpi/acpica/exoparg2.c557
-rw-r--r--drivers/acpi/acpica/exoparg3.c252
-rw-r--r--drivers/acpi/acpica/exoparg6.c299
-rw-r--r--drivers/acpi/acpica/exprep.c599
-rw-r--r--drivers/acpi/acpica/exregion.c544
-rw-r--r--drivers/acpi/acpica/exresnte.c245
-rw-r--r--drivers/acpi/acpica/exresolv.c539
-rw-r--r--drivers/acpi/acpica/exresop.c675
-rw-r--r--drivers/acpi/acpica/exserial.c357
-rw-r--r--drivers/acpi/acpica/exstore.c589
-rw-r--r--drivers/acpi/acpica/exstoren.c261
-rw-r--r--drivers/acpi/acpica/exstorob.c187
-rw-r--r--drivers/acpi/acpica/exsystem.c279
-rw-r--r--drivers/acpi/acpica/extrace.c318
-rw-r--r--drivers/acpi/acpica/exutils.c410
-rw-r--r--drivers/acpi/acpica/hwacpi.c147
-rw-r--r--drivers/acpi/acpica/hwesleep.c204
-rw-r--r--drivers/acpi/acpica/hwgpe.c683
-rw-r--r--drivers/acpi/acpica/hwpci.c384
-rw-r--r--drivers/acpi/acpica/hwregs.c802
-rw-r--r--drivers/acpi/acpica/hwsleep.c318
-rw-r--r--drivers/acpi/acpica/hwtimer.c175
-rw-r--r--drivers/acpi/acpica/hwvalid.c325
-rw-r--r--drivers/acpi/acpica/hwxface.c448
-rw-r--r--drivers/acpi/acpica/hwxfsleep.c368
-rw-r--r--drivers/acpi/acpica/nsaccess.c745
-rw-r--r--drivers/acpi/acpica/nsalloc.c494
-rw-r--r--drivers/acpi/acpica/nsarguments.c275
-rw-r--r--drivers/acpi/acpica/nsconvert.c490
-rw-r--r--drivers/acpi/acpica/nsdump.c855
-rw-r--r--drivers/acpi/acpica/nsdumpdv.c106
-rw-r--r--drivers/acpi/acpica/nseval.c306
-rw-r--r--drivers/acpi/acpica/nsinit.c679
-rw-r--r--drivers/acpi/acpica/nsload.c286
-rw-r--r--drivers/acpi/acpica/nsnames.c466
-rw-r--r--drivers/acpi/acpica/nsobject.c432
-rw-r--r--drivers/acpi/acpica/nsparse.c271
-rw-r--r--drivers/acpi/acpica/nspredef.c368
-rw-r--r--drivers/acpi/acpica/nsprepkg.c740
-rw-r--r--drivers/acpi/acpica/nsrepair.c562
-rw-r--r--drivers/acpi/acpica/nsrepair2.c951
-rw-r--r--drivers/acpi/acpica/nssearch.c396
-rw-r--r--drivers/acpi/acpica/nsutils.c731
-rw-r--r--drivers/acpi/acpica/nswalk.c327
-rw-r--r--drivers/acpi/acpica/nsxfeval.c982
-rw-r--r--drivers/acpi/acpica/nsxfname.c607
-rw-r--r--drivers/acpi/acpica/nsxfobj.c209
-rw-r--r--drivers/acpi/acpica/psargs.c908
-rw-r--r--drivers/acpi/acpica/psloop.c561
-rw-r--r--drivers/acpi/acpica/psobject.c728
-rw-r--r--drivers/acpi/acpica/psopcode.c629
-rw-r--r--drivers/acpi/acpica/psopinfo.c233
-rw-r--r--drivers/acpi/acpica/psparse.c687
-rw-r--r--drivers/acpi/acpica/psscope.c231
-rw-r--r--drivers/acpi/acpica/pstree.c294
-rw-r--r--drivers/acpi/acpica/psutils.c214
-rw-r--r--drivers/acpi/acpica/pswalk.c105
-rw-r--r--drivers/acpi/acpica/psxface.c341
-rw-r--r--drivers/acpi/acpica/rsaddr.c346
-rw-r--r--drivers/acpi/acpica/rscalc.c824
-rw-r--r--drivers/acpi/acpica/rscreate.c448
-rw-r--r--drivers/acpi/acpica/rsdump.c584
-rw-r--r--drivers/acpi/acpica/rsdumpinfo.c560
-rw-r--r--drivers/acpi/acpica/rsinfo.c240
-rw-r--r--drivers/acpi/acpica/rsio.c254
-rw-r--r--drivers/acpi/acpica/rsirq.c271
-rw-r--r--drivers/acpi/acpica/rslist.c229
-rw-r--r--drivers/acpi/acpica/rsmemory.c198
-rw-r--r--drivers/acpi/acpica/rsmisc.c804
-rw-r--r--drivers/acpi/acpica/rsserial.c872
-rw-r--r--drivers/acpi/acpica/rsutils.c755
-rw-r--r--drivers/acpi/acpica/rsxface.c628
-rw-r--r--drivers/acpi/acpica/tbdata.c1097
-rw-r--r--drivers/acpi/acpica/tbfadt.c722
-rw-r--r--drivers/acpi/acpica/tbfind.c122
-rw-r--r--drivers/acpi/acpica/tbinstal.c304
-rw-r--r--drivers/acpi/acpica/tbprint.c211
-rw-r--r--drivers/acpi/acpica/tbutils.c446
-rw-r--r--drivers/acpi/acpica/tbxface.c530
-rw-r--r--drivers/acpi/acpica/tbxfload.c454
-rw-r--r--drivers/acpi/acpica/tbxfroot.c267
-rw-r--r--drivers/acpi/acpica/utaddress.c256
-rw-r--r--drivers/acpi/acpica/utalloc.c357
-rw-r--r--drivers/acpi/acpica/utascii.c106
-rw-r--r--drivers/acpi/acpica/utbuffer.c307
-rw-r--r--drivers/acpi/acpica/utcache.c279
-rw-r--r--drivers/acpi/acpica/utcopy.c969
-rw-r--r--drivers/acpi/acpica/utdebug.c611
-rw-r--r--drivers/acpi/acpica/utdecode.c581
-rw-r--r--drivers/acpi/acpica/utdelete.c742
-rw-r--r--drivers/acpi/acpica/uterror.c327
-rw-r--r--drivers/acpi/acpica/uteval.c315
-rw-r--r--drivers/acpi/acpica/utexcep.c123
-rw-r--r--drivers/acpi/acpica/utglobal.c241
-rw-r--r--drivers/acpi/acpica/uthex.c106
-rw-r--r--drivers/acpi/acpica/utids.c402
-rw-r--r--drivers/acpi/acpica/utinit.c288
-rw-r--r--drivers/acpi/acpica/utlock.c141
-rw-r--r--drivers/acpi/acpica/utmath.c494
-rw-r--r--drivers/acpi/acpica/utmisc.c382
-rw-r--r--drivers/acpi/acpica/utmutex.c330
-rw-r--r--drivers/acpi/acpica/utnonansi.c175
-rw-r--r--drivers/acpi/acpica/utobject.c683
-rw-r--r--drivers/acpi/acpica/utosi.c460
-rw-r--r--drivers/acpi/acpica/utownerid.c188
-rw-r--r--drivers/acpi/acpica/utpredef.c364
-rw-r--r--drivers/acpi/acpica/utprint.c714
-rw-r--r--drivers/acpi/acpica/utresdecode.c287
-rw-r--r--drivers/acpi/acpica/utresrc.c565
-rw-r--r--drivers/acpi/acpica/utstate.c275
-rw-r--r--drivers/acpi/acpica/utstring.c211
-rw-r--r--drivers/acpi/acpica/utstrsuppt.c443
-rw-r--r--drivers/acpi/acpica/utstrtoul64.c327
-rw-r--r--drivers/acpi/acpica/uttrack.c705
-rw-r--r--drivers/acpi/acpica/utuuid.c105
-rw-r--r--drivers/acpi/acpica/utxface.c524
-rw-r--r--drivers/acpi/acpica/utxferror.c264
-rw-r--r--drivers/acpi/acpica/utxfinit.c256
-rw-r--r--drivers/acpi/acpica/utxfmutex.c154
-rw-r--r--drivers/acpi/apei/Kconfig70
-rw-r--r--drivers/acpi/apei/Makefile7
-rw-r--r--drivers/acpi/apei/apei-base.c800
-rw-r--r--drivers/acpi/apei/apei-internal.h139
-rw-r--r--drivers/acpi/apei/bert.c173
-rw-r--r--drivers/acpi/apei/einj.c799
-rw-r--r--drivers/acpi/apei/erst-dbg.c231
-rw-r--r--drivers/acpi/apei/erst.c1267
-rw-r--r--drivers/acpi/apei/ghes.c1499
-rw-r--r--drivers/acpi/apei/hest.c275
-rw-r--r--drivers/acpi/arm64/Kconfig20
-rw-r--r--drivers/acpi/arm64/Makefile5
-rw-r--r--drivers/acpi/arm64/agdi.c117
-rw-r--r--drivers/acpi/arm64/dma.c56
-rw-r--r--drivers/acpi/arm64/gtdt.c418
-rw-r--r--drivers/acpi/arm64/iort.c2023
-rw-r--r--drivers/acpi/battery.c1295
-rw-r--r--drivers/acpi/bgrt.c101
-rw-r--r--drivers/acpi/blacklist.c140
-rw-r--r--drivers/acpi/bus.c1421
-rw-r--r--drivers/acpi/button.c659
-rw-r--r--drivers/acpi/container.c117
-rw-r--r--drivers/acpi/cppc_acpi.c1589
-rw-r--r--drivers/acpi/custom_method.c103
-rw-r--r--drivers/acpi/debugfs.c19
-rw-r--r--drivers/acpi/device_pm.c1517
-rw-r--r--drivers/acpi/device_sysfs.c650
-rw-r--r--drivers/acpi/dock.c646
-rw-r--r--drivers/acpi/dptf/Kconfig46
-rw-r--r--drivers/acpi/dptf/Makefile4
-rw-r--r--drivers/acpi/dptf/dptf_pch_fivr.c173
-rw-r--r--drivers/acpi/dptf/dptf_power.c256
-rw-r--r--drivers/acpi/dptf/int340x_thermal.c75
-rw-r--r--drivers/acpi/ec.c2249
-rw-r--r--drivers/acpi/ec_sys.c144
-rw-r--r--drivers/acpi/event.c183
-rw-r--r--drivers/acpi/evged.c196
-rw-r--r--drivers/acpi/fan.h59
-rw-r--r--drivers/acpi/fan_attr.c137
-rw-r--r--drivers/acpi/fan_core.c460
-rw-r--r--drivers/acpi/glue.c414
-rw-r--r--drivers/acpi/hed.c79
-rw-r--r--drivers/acpi/internal.h284
-rw-r--r--drivers/acpi/ioapic.c248
-rw-r--r--drivers/acpi/irq.c356
-rw-r--r--drivers/acpi/nfit/Kconfig26
-rw-r--r--drivers/acpi/nfit/Makefile5
-rw-r--r--drivers/acpi/nfit/core.c3528
-rw-r--r--drivers/acpi/nfit/intel.c777
-rw-r--r--drivers/acpi/nfit/intel.h175
-rw-r--r--drivers/acpi/nfit/mce.c100
-rw-r--r--drivers/acpi/nfit/nfit.h350
-rw-r--r--drivers/acpi/numa/Kconfig18
-rw-r--r--drivers/acpi/numa/Makefile3
-rw-r--r--drivers/acpi/numa/hmat.c877
-rw-r--r--drivers/acpi/numa/srat.c575
-rw-r--r--drivers/acpi/nvs.c213
-rw-r--r--drivers/acpi/osi.c489
-rw-r--r--drivers/acpi/osl.c1770
-rw-r--r--drivers/acpi/pci_irq.c514
-rw-r--r--drivers/acpi/pci_link.c907
-rw-r--r--drivers/acpi/pci_mcfg.c324
-rw-r--r--drivers/acpi/pci_root.c1081
-rw-r--r--drivers/acpi/pci_slot.c187
-rw-r--r--drivers/acpi/pfr_telemetry.c435
-rw-r--r--drivers/acpi/pfr_update.c575
-rw-r--r--drivers/acpi/platform_profile.c181
-rw-r--r--drivers/acpi/pmic/Kconfig67
-rw-r--r--drivers/acpi/pmic/Makefile10
-rw-r--r--drivers/acpi/pmic/intel_pmic.c383
-rw-r--r--drivers/acpi/pmic/intel_pmic.h36
-rw-r--r--drivers/acpi/pmic/intel_pmic_bxtwc.c408
-rw-r--r--drivers/acpi/pmic/intel_pmic_bytcrc.c302
-rw-r--r--drivers/acpi/pmic/intel_pmic_chtcrc.c45
-rw-r--r--drivers/acpi/pmic/intel_pmic_chtdc_ti.c137
-rw-r--r--drivers/acpi/pmic/intel_pmic_chtwc.c287
-rw-r--r--drivers/acpi/pmic/intel_pmic_xpower.c367
-rw-r--r--drivers/acpi/pmic/tps68470_pmic.c447
-rw-r--r--drivers/acpi/power.c1066
-rw-r--r--drivers/acpi/pptt.c799
-rw-r--r--drivers/acpi/prmt.c344
-rw-r--r--drivers/acpi/proc.c146
-rw-r--r--drivers/acpi/processor_core.c355
-rw-r--r--drivers/acpi/processor_driver.c311
-rw-r--r--drivers/acpi/processor_idle.c1438
-rw-r--r--drivers/acpi/processor_pdc.c212
-rw-r--r--drivers/acpi/processor_perflib.c800
-rw-r--r--drivers/acpi/processor_thermal.c322
-rw-r--r--drivers/acpi/processor_throttling.c1244
-rw-r--r--drivers/acpi/property.c1635
-rw-r--r--drivers/acpi/reboot.c82
-rw-r--r--drivers/acpi/resource.c1010
-rw-r--r--drivers/acpi/sbs.c714
-rw-r--r--drivers/acpi/sbshc.c302
-rw-r--r--drivers/acpi/sbshc.h32
-rw-r--r--drivers/acpi/scan.c2707
-rw-r--r--drivers/acpi/sleep.c1128
-rw-r--r--drivers/acpi/sleep.h33
-rw-r--r--drivers/acpi/spcr.c232
-rw-r--r--drivers/acpi/sysfs.c1026
-rw-r--r--drivers/acpi/tables.c933
-rw-r--r--drivers/acpi/thermal.c1219
-rw-r--r--drivers/acpi/tiny-power-button.c43
-rw-r--r--drivers/acpi/utils.c1028
-rw-r--r--drivers/acpi/video_detect.c875
-rw-r--r--drivers/acpi/viot.c380
-rw-r--r--drivers/acpi/wakeup.c171
-rw-r--r--drivers/acpi/x86/apple.c139
-rw-r--r--drivers/acpi/x86/s2idle.c597
-rw-r--r--drivers/acpi/x86/utils.c454
333 files changed, 165478 insertions, 0 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
new file mode 100644
index 000000000..473241b51
--- /dev/null
+++ b/drivers/acpi/Kconfig
@@ -0,0 +1,603 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# ACPI Configuration
+#
+
+config ARCH_SUPPORTS_ACPI
+ bool
+
+menuconfig ACPI
+ bool "ACPI (Advanced Configuration and Power Interface) Support"
+ depends on ARCH_SUPPORTS_ACPI
+ select PNP
+ select NLS
+ select CRC32
+ default y if X86
+ help
+ Advanced Configuration and Power Interface (ACPI) support for
+ Linux requires an ACPI-compliant platform (hardware/firmware),
+ and assumes the presence of OS-directed configuration and power
+ management (OSPM) software. This option will enlarge your
+ kernel by about 70K.
+
+ Linux ACPI provides a robust functional replacement for several
+ legacy configuration and power management interfaces, including
+ the Plug-and-Play BIOS specification (PnP BIOS), the
+ MultiProcessor Specification (MPS), and the Advanced Power
+ Management (APM) specification. If both ACPI and APM support
+ are configured, ACPI is used.
+
+ Linux support for ACPI is based on Intel Corporation's ACPI
+ Component Architecture (ACPI CA). For more information on the
+ ACPI CA, see:
+ <https://acpica.org/>
+
+ ACPI is an open industry specification originally co-developed by
+ Hewlett-Packard, Intel, Microsoft, Phoenix, and Toshiba. Currently,
+ it is developed by the ACPI Specification Working Group (ASWG) under
+ the UEFI Forum and any UEFI member can join the ASWG and contribute
+ to the ACPI specification.
+ The specification is available at:
+ <https://uefi.org/specifications>
+
+if ACPI
+
+config ACPI_LEGACY_TABLES_LOOKUP
+ bool
+
+config ARCH_MIGHT_HAVE_ACPI_PDC
+ bool
+
+config ACPI_GENERIC_GSI
+ bool
+
+config ACPI_SYSTEM_POWER_STATES_SUPPORT
+ bool
+
+config ACPI_CCA_REQUIRED
+ bool
+
+config ACPI_TABLE_LIB
+ bool
+
+config ACPI_DEBUGGER
+ bool "AML debugger interface"
+ select ACPI_DEBUG
+ help
+ Enable in-kernel debugging of AML facilities: statistics,
+ internal object dump, single step control method execution.
+ This is still under development, currently enabling this only
+ results in the compilation of the ACPICA debugger files.
+
+if ACPI_DEBUGGER
+
+config ACPI_DEBUGGER_USER
+ tristate "Userspace debugger accessibility"
+ depends on DEBUG_FS
+ help
+ Export /sys/kernel/debug/acpi/acpidbg for userspace utilities
+ to access the debugger functionalities.
+
+endif
+
+config ACPI_SPCR_TABLE
+ bool "ACPI Serial Port Console Redirection Support"
+ default y if X86
+ help
+ Enable support for Serial Port Console Redirection (SPCR) Table.
+ This table provides information about the configuration of the
+ earlycon console.
+
+config ACPI_FPDT
+ bool "ACPI Firmware Performance Data Table (FPDT) support"
+ depends on X86_64
+ help
+ Enable support for the Firmware Performance Data Table (FPDT).
+ This table provides information on the timing of the system
+ boot, S3 suspend and S3 resume firmware code paths.
+
+config ACPI_LPIT
+ bool
+ depends on X86_64
+ default y
+
+config ACPI_SLEEP
+ bool
+ depends on SUSPEND || HIBERNATION
+ depends on ACPI_SYSTEM_POWER_STATES_SUPPORT
+ default y
+
+config ACPI_REV_OVERRIDE_POSSIBLE
+ bool "Allow supported ACPI revision to be overridden"
+ depends on X86
+ default y
+ help
+ The platform firmware on some systems expects Linux to return "5" as
+ the supported ACPI revision which makes it expose system configuration
+ information in a special way.
+
+ For example, based on what ACPI exports as the supported revision,
+ Dell XPS 13 (2015) configures its audio device to either work in HDA
+ mode or in I2S mode, where the former is supposed to be used on Linux
+ until the latter is fully supported (in the kernel as well as in user
+ space).
+
+ This option enables a DMI-based quirk for the above Dell machine (so
+ that HDA audio is exposed by the platform firmware to the kernel) and
+ 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_DEBUGFS
+ tristate "EC read/write access through /sys/kernel/debug/ec"
+ help
+ Say N to disable Embedded Controller /sys/kernel/debug interface
+
+ Be aware that using this interface can confuse your Embedded
+ Controller in a way that a normal reboot is not enough. You then
+ have to power off your system, and remove the laptop battery for
+ some seconds.
+ An Embedded Controller typically is available on laptops and reads
+ sensor values like battery state and temperature.
+ The kernel accesses the EC through ACPI parsed code provided by BIOS
+ tables. This option allows to access the EC directly without ACPI
+ code being involved.
+ Thus this option is a debug option that helps to write ACPI drivers
+ and can be used to identify ACPI code or EC firmware bugs.
+
+config ACPI_AC
+ tristate "AC Adapter"
+ select POWER_SUPPLY
+ default y
+ help
+ This driver supports the AC Adapter object, which indicates
+ whether a system is on AC or not. If you have a system that can
+ switch between A/C and battery, say Y.
+
+ To compile this driver as a module, choose M here:
+ the module will be called ac.
+
+config ACPI_BATTERY
+ tristate "Battery"
+ select POWER_SUPPLY
+ default y
+ help
+ This driver adds support for battery information through
+ /proc/acpi/battery. If you have a mobile system with a battery,
+ say Y.
+
+ To compile this driver as a module, choose M here:
+ the module will be called battery.
+
+config ACPI_BUTTON
+ tristate "Button"
+ depends on INPUT
+ default y
+ help
+ This driver handles events on the power, sleep, and lid buttons.
+ A daemon reads events from input devices or via netlink and
+ performs user-defined actions such as shutting down the system.
+ This is necessary for software-controlled poweroff.
+
+ To compile this driver as a module, choose M here:
+ the module will be called button.
+
+config ACPI_TINY_POWER_BUTTON
+ tristate "Tiny Power Button Driver"
+ depends on !ACPI_BUTTON
+ help
+ This driver provides a tiny alternative to the ACPI Button driver.
+ The tiny power button driver only handles the power button. Rather
+ than notifying userspace via the input layer or a netlink event, this
+ driver directly signals the init process to shut down.
+
+ This driver is particularly suitable for cloud and VM environments,
+ which use a simulated power button to initiate a controlled poweroff,
+ but which may not want to run a separate userspace daemon to process
+ input events.
+
+config ACPI_TINY_POWER_BUTTON_SIGNAL
+ int "Tiny Power Button Signal"
+ depends on ACPI_TINY_POWER_BUTTON
+ default 38
+ help
+ Default signal to send to init in response to the power button.
+
+ Likely values here include 38 (SIGRTMIN+4) to power off, or 2
+ (SIGINT) to simulate Ctrl+Alt+Del.
+
+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
+ for integrated graphics devices on motherboard, as specified in
+ ACPI 2.0 Specification, Appendix B. This supports basic operations
+ such as defining the video POST device, retrieving EDID information,
+ and setting up a video output.
+
+ To compile this driver as a module, choose M here:
+ the module will be called video.
+
+config ACPI_FAN
+ tristate "Fan"
+ depends on THERMAL
+ default y
+ help
+ This driver supports ACPI fan devices, allowing user-mode
+ applications to perform basic fan control (on, off, status).
+
+ To compile this driver as a module, choose M here:
+ the module will be called fan.
+
+config ACPI_TAD
+ tristate "ACPI Time and Alarm (TAD) Device Support"
+ depends on SYSFS && PM_SLEEP
+ help
+ The ACPI Time and Alarm (TAD) device is an alternative to the Real
+ Time Clock (RTC). Its wake timers allow the system to transition from
+ the S3 (or optionally S4/S5) state to S0 state after a time period
+ elapses. In comparison with the RTC Alarm, the TAD provides a larger
+ scale of flexibility in the wake timers. The time capabilities of the
+ TAD maintain the time of day information across platform power
+ transitions, and keep track of time even when the platform is turned
+ off.
+
+config ACPI_DOCK
+ bool "Dock"
+ help
+ This driver supports ACPI-controlled docking stations and removable
+ drive bays such as the IBM Ultrabay and the Dell Module Bay.
+
+config ACPI_CPU_FREQ_PSS
+ bool
+
+config ACPI_PROCESSOR_CSTATE
+ def_bool y
+ depends on ACPI_PROCESSOR
+ depends on IA64 || X86
+
+config ACPI_PROCESSOR_IDLE
+ bool
+ select CPU_IDLE
+
+config ACPI_MCFG
+ bool
+
+config ACPI_CPPC_LIB
+ bool
+ depends on ACPI_PROCESSOR
+ select MAILBOX
+ select PCC
+ help
+ If this option is enabled, this file implements common functionality
+ to parse CPPC tables as described in the ACPI 5.1+ spec. The
+ routines implemented are meant to be used by other
+ drivers to control CPU performance using CPPC semantics.
+ If your platform does not support CPPC in firmware,
+ leave this option disabled.
+
+config ACPI_PROCESSOR
+ tristate "Processor"
+ depends on X86 || IA64 || ARM64 || LOONGARCH
+ select ACPI_PROCESSOR_IDLE
+ select ACPI_CPU_FREQ_PSS if X86 || IA64 || LOONGARCH
+ select THERMAL
+ default y
+ help
+ This driver adds support for the ACPI Processor package. It is required
+ by several flavors of cpufreq performance-state, thermal, throttling and
+ idle drivers.
+
+ To compile this driver as a module, choose M here:
+ the module will be called processor.
+
+config ACPI_IPMI
+ tristate "IPMI"
+ depends on IPMI_HANDLER
+ help
+ This driver enables the ACPI to access the BMC controller. And it
+ uses the IPMI request/response message to communicate with BMC
+ controller, which can be found on the server.
+
+ To compile this driver as a module, choose M here:
+ the module will be called as acpi_ipmi.
+
+config ACPI_HOTPLUG_CPU
+ bool
+ depends on ACPI_PROCESSOR && HOTPLUG_CPU
+ select ACPI_CONTAINER
+ default y
+
+config ACPI_PROCESSOR_AGGREGATOR
+ tristate "Processor Aggregator"
+ depends on ACPI_PROCESSOR
+ depends on X86
+ help
+ ACPI 4.0 defines processor Aggregator, which enables OS to perform
+ specific processor configuration and control that applies to all
+ processors in the platform. Currently only logical processor idling
+ is defined, which is to reduce power consumption. This driver
+ supports the new device.
+
+config ACPI_THERMAL
+ tristate "Thermal Zone"
+ depends on ACPI_PROCESSOR
+ select THERMAL
+ default y
+ help
+ This driver supports ACPI thermal zones. Most mobile and
+ some desktop systems support ACPI thermal zones. It is HIGHLY
+ recommended that this option be enabled, as your processor(s)
+ may be damaged without it.
+
+ To compile this driver as a module, choose M here:
+ the module will be called thermal.
+
+config ACPI_PLATFORM_PROFILE
+ tristate
+
+config ACPI_CUSTOM_DSDT_FILE
+ string "Custom DSDT Table file to include"
+ default ""
+ depends on !STANDALONE
+ help
+ This option supports a custom DSDT by linking it into the kernel.
+
+ Enter the full path name to the file which includes the AmlCode
+ or dsdt_aml_code declaration.
+
+ If unsure, don't enter a file name.
+
+config ACPI_CUSTOM_DSDT
+ bool
+ default ACPI_CUSTOM_DSDT_FILE != ""
+
+config ARCH_HAS_ACPI_TABLE_UPGRADE
+ def_bool n
+
+config ACPI_TABLE_UPGRADE
+ bool "Allow upgrading ACPI tables via initrd"
+ depends on BLK_DEV_INITRD && ARCH_HAS_ACPI_TABLE_UPGRADE
+ default y
+ help
+ This option provides functionality to upgrade arbitrary ACPI tables
+ via initrd. No functional change if no ACPI tables are passed via
+ initrd, therefore it's safe to say Y.
+ See Documentation/admin-guide/acpi/initrd_table_override.rst for details
+
+config ACPI_TABLE_OVERRIDE_VIA_BUILTIN_INITRD
+ bool "Override ACPI tables from built-in initrd"
+ depends on ACPI_TABLE_UPGRADE
+ depends on INITRAMFS_SOURCE!="" && INITRAMFS_COMPRESSION_NONE
+ help
+ This option provides functionality to override arbitrary ACPI tables
+ from built-in uncompressed initrd.
+
+ See Documentation/admin-guide/acpi/initrd_table_override.rst for details
+
+config ACPI_DEBUG
+ bool "Debug Statements"
+ help
+ The ACPI subsystem can produce debug output. Saying Y enables this
+ output and increases the kernel size by around 50K.
+
+ Use the acpi.debug_layer and acpi.debug_level kernel command-line
+ parameters documented in Documentation/firmware-guide/acpi/debug.rst and
+ Documentation/admin-guide/kernel-parameters.rst to control the type and
+ amount of debug output.
+
+config ACPI_PCI_SLOT
+ bool "PCI slot detection driver"
+ depends on SYSFS && PCI
+ help
+ This driver creates entries in /sys/bus/pci/slots/ for all PCI
+ slots in the system. This can help correlate PCI bus addresses,
+ i.e., segment/bus/device/function tuples, with physical slots in
+ the system. If you are unsure, say N.
+
+config ACPI_CONTAINER
+ bool "Container and Module Devices"
+ default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU)
+ help
+ This driver supports ACPI Container and Module devices (IDs
+ ACPI0004, PNP0A05, and PNP0A06).
+
+ This helps support hotplug of nodes, CPUs, and memory.
+
+config ACPI_HOTPLUG_MEMORY
+ bool "Memory Hotplug"
+ depends on MEMORY_HOTPLUG
+ help
+ This driver supports ACPI memory hotplug. The driver
+ fields notifications on ACPI memory devices (PNP0C80),
+ which represent memory ranges that may be onlined or
+ offlined during runtime.
+
+ If your hardware and firmware do not support adding or
+ removing memory devices at runtime, you need not enable
+ this driver.
+
+config ACPI_HOTPLUG_IOAPIC
+ bool
+ depends on PCI
+ depends on X86_IO_APIC
+ default y
+
+config ACPI_SBS
+ tristate "Smart Battery System"
+ depends on X86
+ select POWER_SUPPLY
+ help
+ This driver supports the Smart Battery System, another
+ type of access to battery information, found on some laptops.
+
+ To compile this driver as a module, choose M here:
+ the modules will be called sbs and sbshc.
+
+config ACPI_HED
+ tristate "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)
+ help
+ This driver adds support for exposing the ACPI Boottime Graphics
+ Resource Table, which allows the operating system to obtain
+ data from the firmware boot splash. It will appear under
+ /sys/firmware/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
+ ACPICA that ONLY supports the ACPI "reduced hardware" mode. The
+ resulting kernel will be smaller but it will also be restricted to
+ running in ACPI reduced hardware mode ONLY.
+
+ If you are unsure what to do, do not enable this option.
+
+source "drivers/acpi/nfit/Kconfig"
+source "drivers/acpi/numa/Kconfig"
+source "drivers/acpi/apei/Kconfig"
+source "drivers/acpi/dptf/Kconfig"
+
+config ACPI_WATCHDOG
+ bool
+
+config ACPI_EXTLOG
+ tristate "Extended Error Log support"
+ depends on X86_MCE && X86_LOCAL_APIC && EDAC
+ select UEFI_CPER
+ help
+ Certain usages such as Predictive Failure Analysis (PFA) require
+ more information about the error than what can be described in
+ processor machine check banks. Most server processors log
+ additional information about the error in processor uncore
+ registers. Since the addresses and layout of these registers vary
+ widely from one processor to another, system software cannot
+ readily make use of them. To complicate matters further, some of
+ the additional error information cannot be constructed without
+ detailed knowledge about platform topology.
+
+ Enhanced MCA Logging allows firmware to provide additional error
+ information to system software, synchronous with MCE or CMCI. This
+ driver adds support for that functionality with corresponding
+ tracepoint which carries that information to userspace.
+
+config ACPI_ADXL
+ bool
+
+config ACPI_CONFIGFS
+ tristate "ACPI configfs support"
+ select CONFIGFS_FS
+ help
+ Select this option to enable support for ACPI configuration from
+ userspace. The configurable ACPI groups will be visible under
+ /config/acpi, assuming configfs is mounted under /config.
+
+config ACPI_PFRUT
+ tristate "ACPI Platform Firmware Runtime Update and Telemetry"
+ depends on 64BIT
+ help
+ This mechanism allows certain pieces of the platform firmware
+ to be updated on the fly while the system is running (runtime)
+ without the need to restart it, which is key in the cases when
+ the system needs to be available 100% of the time and it cannot
+ afford the downtime related to restarting it, or when the work
+ carried out by the system is particularly important, so it cannot
+ be interrupted, and it is not practical to wait until it is complete.
+
+ The existing firmware code can be modified (driver update) or
+ extended by adding new code to the firmware (code injection).
+
+ Besides, the telemetry driver allows user space to fetch telemetry
+ data from the firmware with the help of the Platform Firmware Runtime
+ Telemetry interface.
+
+ To compile the drivers as modules, choose M here:
+ the modules will be called pfr_update and pfr_telemetry.
+
+if ARM64
+source "drivers/acpi/arm64/Kconfig"
+
+config ACPI_PPTT
+ bool
+endif
+
+config ACPI_PCC
+ bool "ACPI PCC Address Space"
+ depends on PCC
+ default y
+ help
+ The PCC Address Space also referred as PCC Operation Region pertains
+ to the region of PCC subspace that succeeds the PCC signature.
+
+ The PCC Operation Region works in conjunction with the PCC Table
+ (Platform Communications Channel Table). PCC subspaces that are
+ marked for use as PCC Operation Regions must not be used as PCC
+ subspaces for the standard ACPI features such as CPPC, RASF, PDTT and
+ MPST. These standard features must always use the PCC Table instead.
+
+ Enable this feature if you want to set up and install the PCC Address
+ Space handler to handle PCC OpRegion in the firmware.
+
+source "drivers/acpi/pmic/Kconfig"
+
+config ACPI_VIOT
+ bool
+
+config ACPI_PRMT
+ bool "Platform Runtime Mechanism Support"
+ depends on EFI && (X86_64 || ARM64)
+ default y
+ help
+ Platform Runtime Mechanism (PRM) is a firmware interface exposing a
+ set of binary executables that can be called from the AML interpreter
+ or directly from device drivers.
+
+ Say Y to enable the AML interpreter to execute the PRM code.
+
+ While this feature is optional in principle, leaving it out may
+ substantially increase computational overhead related to the
+ initialization of some server systems.
+
+endif # ACPI
+
+config X86_PM_TIMER
+ bool "Power Management Timer Support" if EXPERT
+ depends on X86 && (ACPI || JAILHOUSE_GUEST)
+ default y
+ help
+ The Power Management Timer is available on all ACPI-capable,
+ in most cases even if ACPI is unusable or blacklisted.
+
+ This timing source is not affected by power management features
+ like aggressive processor idling, throttling, frequency and/or
+ voltage scaling, unlike the commonly used Time Stamp Counter
+ (TSC) timing source.
+
+ You should nearly always say Y here because many modern
+ systems require this timer.
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
new file mode 100644
index 000000000..0002eecbf
--- /dev/null
+++ b/drivers/acpi/Makefile
@@ -0,0 +1,132 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Linux ACPI interpreter
+#
+
+ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
+
+#
+# ACPI Boot-Time Table Parsing
+#
+ifeq ($(CONFIG_ACPI_CUSTOM_DSDT),y)
+tables.o: $(src)/../../include/$(CONFIG_ACPI_CUSTOM_DSDT_FILE) ;
+
+endif
+
+obj-$(CONFIG_ACPI) += tables.o
+obj-$(CONFIG_X86) += blacklist.o
+
+#
+# ACPI Core Subsystem (Interpreter)
+#
+obj-$(CONFIG_ACPI) += acpi.o \
+ acpica/
+
+# All the builtin files are in the "acpi." module_param namespace.
+acpi-y += osi.o osl.o utils.o reboot.o
+acpi-y += nvs.o
+
+# Power management related files
+acpi-y += wakeup.o
+acpi-$(CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT) += sleep.o
+acpi-y += device_sysfs.o device_pm.o
+acpi-$(CONFIG_ACPI_SLEEP) += proc.o
+
+
+#
+# ACPI Bus and Device Drivers
+#
+acpi-y += bus.o glue.o
+acpi-y += scan.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_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
+acpi-$(CONFIG_ACPI_LPIT) += acpi_lpit.o
+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
+
+# Address translation
+acpi-$(CONFIG_ACPI_ADXL) += acpi_adxl.o
+
+# These are (potentially) separate modules
+
+# IPMI may be used by other drivers, so it has to initialise before them
+obj-$(CONFIG_ACPI_IPMI) += acpi_ipmi.o
+
+obj-$(CONFIG_ACPI_AC) += ac.o
+obj-$(CONFIG_ACPI_BUTTON) += button.o
+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
+
+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) += thermal.o
+obj-$(CONFIG_ACPI_PLATFORM_PROFILE) += platform_profile.o
+obj-$(CONFIG_ACPI_NFIT) += nfit/
+obj-$(CONFIG_ACPI_NUMA) += numa/
+obj-$(CONFIG_ACPI) += acpi_memhotplug.o
+obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o
+obj-$(CONFIG_ACPI_BATTERY) += battery.o
+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
+obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o
+obj-$(CONFIG_ACPI_PPTT) += pptt.o
+obj-$(CONFIG_ACPI_PFRUT) += pfr_update.o pfr_telemetry.o
+
+# processor has its own "processor." module_param namespace
+processor-y := processor_driver.o processor_thermal.o
+processor-$(CONFIG_ACPI_PROCESSOR_IDLE) += processor_idle.o
+processor-$(CONFIG_ACPI_CPU_FREQ_PSS) += processor_throttling.o
+processor-$(CONFIG_CPU_FREQ) += processor_perflib.o
+
+obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
+
+obj-$(CONFIG_ACPI_APEI) += apei/
+
+obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o
+
+obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o
+
+obj-y += pmic/
+
+video-objs += acpi_video.o video_detect.o
+obj-y += dptf/
+
+obj-$(CONFIG_ARM64) += arm64/
+
+obj-$(CONFIG_ACPI_VIOT) += viot.o
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c
new file mode 100644
index 000000000..bb9fe7984
--- /dev/null
+++ b/drivers/acpi/ac.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * acpi_ac.c - ACPI AC Adapter Driver (Revision: 27)
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: AC: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/dmi.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/acpi.h>
+#include <acpi/battery.h>
+
+#define ACPI_AC_CLASS "ac_adapter"
+#define ACPI_AC_DEVICE_NAME "AC Adapter"
+#define ACPI_AC_FILE_STATE "state"
+#define ACPI_AC_NOTIFY_STATUS 0x80
+#define ACPI_AC_STATUS_OFFLINE 0x00
+#define ACPI_AC_STATUS_ONLINE 0x01
+#define ACPI_AC_STATUS_UNKNOWN 0xFF
+
+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 const struct acpi_device_id ac_device_ids[] = {
+ {"ACPI0003", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, ac_device_ids);
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_ac_resume(struct device *dev);
+#endif
+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;
+ struct acpi_device *device;
+ unsigned long long state;
+ struct notifier_block battery_nb;
+};
+
+#define to_acpi_ac(x) power_supply_get_drvdata(x)
+
+/* AC Adapter Management */
+static int acpi_ac_get_state(struct acpi_ac *ac)
+{
+ acpi_status status = AE_OK;
+
+ if (!ac)
+ return -EINVAL;
+
+ if (ac_only) {
+ ac->state = 1;
+ return 0;
+ }
+
+ status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL,
+ &ac->state);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_info(ac->device->handle,
+ "Error reading AC Adapter state: %s\n",
+ acpi_format_exception(status));
+ ac->state = ACPI_AC_STATUS_UNKNOWN;
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* sysfs I/F */
+static int get_ac_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct acpi_ac *ac = to_acpi_ac(psy);
+
+ if (!ac)
+ return -ENODEV;
+
+ if (acpi_ac_get_state(ac))
+ return -ENODEV;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = ac->state;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property ac_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+/* Driver Model */
+static void acpi_ac_notify(struct acpi_device *device, u32 event)
+{
+ struct acpi_ac *ac = acpi_driver_data(device);
+
+ if (!ac)
+ return;
+
+ switch (event) {
+ default:
+ acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
+ event);
+ fallthrough;
+ case ACPI_AC_NOTIFY_STATUS:
+ case ACPI_NOTIFY_BUS_CHECK:
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ /*
+ * A buggy BIOS may notify AC first and then sleep for
+ * a specific time before doing actual operations in the
+ * EC event handler (_Qxx). This will cause the AC state
+ * reported by the ACPI event to be incorrect, so wait for a
+ * specific time for the EC event handler to make progress.
+ */
+ if (ac_sleep_before_get_state_ms > 0)
+ 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,
+ (u32) ac->state);
+ acpi_notifier_call_chain(device, event, (u32) ac->state);
+ kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
+ }
+}
+
+static int acpi_ac_battery_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct acpi_ac *ac = container_of(nb, struct acpi_ac, battery_nb);
+ struct acpi_bus_event *event = (struct acpi_bus_event *)data;
+
+ /*
+ * On HP Pavilion dv6-6179er AC status notifications aren't triggered
+ * when adapter is plugged/unplugged. However, battery status
+ * notifications are triggered when battery starts charging or
+ * discharging. Re-reading AC status triggers lost AC notifications,
+ * if AC status has changed.
+ */
+ if (strcmp(event->device_class, ACPI_BATTERY_CLASS) == 0 &&
+ event->type == ACPI_BATTERY_NOTIFY_STATUS)
+ acpi_ac_get_state(ac);
+
+ return NOTIFY_OK;
+}
+
+static int __init thinkpad_e530_quirk(const struct dmi_system_id *d)
+{
+ ac_sleep_before_get_state_ms = 1000;
+ return 0;
+}
+
+static int __init ac_only_quirk(const struct dmi_system_id *d)
+{
+ ac_only = 1;
+ return 0;
+}
+
+/* Please keep this list alphabetically sorted */
+static const struct dmi_system_id ac_dmi_table[] __initconst = {
+ {
+ /* Kodlix GK45 returning incorrect state */
+ .callback = ac_only_quirk,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "GK45"),
+ },
+ },
+ {
+ /* Lenovo Thinkpad e530, see comment in acpi_ac_notify() */
+ .callback = thinkpad_e530_quirk,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"),
+ },
+ },
+ {},
+};
+
+static int acpi_ac_add(struct acpi_device *device)
+{
+ struct power_supply_config psy_cfg = {};
+ int result = 0;
+ struct acpi_ac *ac = NULL;
+
+
+ if (!device)
+ return -EINVAL;
+
+ 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;
+
+ result = acpi_ac_get_state(ac);
+ if (result)
+ goto end;
+
+ psy_cfg.drv_data = ac;
+
+ ac->charger_desc.name = acpi_device_bid(device);
+ 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_desc, &psy_cfg);
+ if (IS_ERR(ac->charger)) {
+ result = PTR_ERR(ac->charger);
+ goto end;
+ }
+
+ pr_info("%s [%s] (%s)\n", acpi_device_name(device),
+ acpi_device_bid(device), ac->state ? "on-line" : "off-line");
+
+ ac->battery_nb.notifier_call = acpi_ac_battery_notify;
+ register_acpi_notifier(&ac->battery_nb);
+end:
+ if (result)
+ kfree(ac);
+
+ return result;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_ac_resume(struct device *dev)
+{
+ struct acpi_ac *ac;
+ 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);
+
+ return 0;
+}
+#else
+#define acpi_ac_resume NULL
+#endif
+
+static int acpi_ac_remove(struct acpi_device *device)
+{
+ struct acpi_ac *ac = NULL;
+
+ if (!device || !acpi_driver_data(device))
+ return -EINVAL;
+
+ ac = acpi_driver_data(device);
+
+ power_supply_unregister(ac->charger);
+ unregister_acpi_notifier(&ac->battery_nb);
+
+ kfree(ac);
+
+ return 0;
+}
+
+static int __init acpi_ac_init(void)
+{
+ int result;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ if (acpi_quirk_skip_acpi_ac_and_battery())
+ return -ENODEV;
+
+ dmi_check_system(ac_dmi_table);
+
+ result = acpi_bus_register_driver(&acpi_ac_driver);
+ if (result < 0)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void __exit acpi_ac_exit(void)
+{
+ acpi_bus_unregister_driver(&acpi_ac_driver);
+}
+module_init(acpi_ac_init);
+module_exit(acpi_ac_exit);
diff --git a/drivers/acpi/acpi_adxl.c b/drivers/acpi/acpi_adxl.c
new file mode 100644
index 000000000..13c8f7b50
--- /dev/null
+++ b/drivers/acpi/acpi_adxl.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Address translation interface via ACPI DSM.
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * Specification for this interface is available at:
+ *
+ * https://cdrdv2.intel.com/v1/dl/getContent/603354
+ */
+
+#include <linux/acpi.h>
+#include <linux/adxl.h>
+
+#define ADXL_REVISION 0x1
+#define ADXL_IDX_GET_ADDR_PARAMS 0x1
+#define ADXL_IDX_FORWARD_TRANSLATE 0x2
+#define ACPI_ADXL_PATH "\\_SB.ADXL"
+
+/*
+ * The specification doesn't provide a limit on how many
+ * components are in a memory address. But since we allocate
+ * memory based on the number the BIOS tells us, we should
+ * defend against insane values.
+ */
+#define ADXL_MAX_COMPONENTS 500
+
+#undef pr_fmt
+#define pr_fmt(fmt) "ADXL: " fmt
+
+static acpi_handle handle;
+static union acpi_object *params;
+static const guid_t adxl_guid =
+ GUID_INIT(0xAA3C050A, 0x7EA4, 0x4C1F,
+ 0xAF, 0xDA, 0x12, 0x67, 0xDF, 0xD3, 0xD4, 0x8D);
+
+static int adxl_count;
+static char **adxl_component_names;
+
+static union acpi_object *adxl_dsm(int cmd, union acpi_object argv[])
+{
+ union acpi_object *obj, *o;
+
+ obj = acpi_evaluate_dsm_typed(handle, &adxl_guid, ADXL_REVISION,
+ cmd, argv, ACPI_TYPE_PACKAGE);
+ if (!obj) {
+ pr_info("DSM call failed for cmd=%d\n", cmd);
+ return NULL;
+ }
+
+ if (obj->package.count != 2) {
+ pr_info("Bad pkg count %d\n", obj->package.count);
+ goto err;
+ }
+
+ o = obj->package.elements;
+ if (o->type != ACPI_TYPE_INTEGER) {
+ pr_info("Bad 1st element type %d\n", o->type);
+ goto err;
+ }
+ if (o->integer.value) {
+ pr_info("Bad ret val %llu\n", o->integer.value);
+ goto err;
+ }
+
+ o = obj->package.elements + 1;
+ if (o->type != ACPI_TYPE_PACKAGE) {
+ pr_info("Bad 2nd element type %d\n", o->type);
+ goto err;
+ }
+ return obj;
+
+err:
+ ACPI_FREE(obj);
+ return NULL;
+}
+
+/**
+ * adxl_get_component_names - get list of memory component names
+ * Returns NULL terminated list of string names
+ *
+ * Give the caller a pointer to the list of memory component names
+ * e.g. { "SystemAddress", "ProcessorSocketId", "ChannelId", ... NULL }
+ * Caller should count how many strings in order to allocate a buffer
+ * for the return from adxl_decode().
+ */
+const char * const *adxl_get_component_names(void)
+{
+ return (const char * const *)adxl_component_names;
+}
+EXPORT_SYMBOL_GPL(adxl_get_component_names);
+
+/**
+ * adxl_decode - ask BIOS to decode a system address to memory address
+ * @addr: the address to decode
+ * @component_values: pointer to array of values for each component
+ * Returns 0 on success, negative error code otherwise
+ *
+ * The index of each value returned in the array matches the index of
+ * each component name returned by adxl_get_component_names().
+ * Components that are not defined for this address translation (e.g.
+ * mirror channel number for a non-mirrored address) are set to ~0ull.
+ */
+int adxl_decode(u64 addr, u64 component_values[])
+{
+ union acpi_object argv4[2], *results, *r;
+ int i, cnt;
+
+ if (!adxl_component_names)
+ return -EOPNOTSUPP;
+
+ argv4[0].type = ACPI_TYPE_PACKAGE;
+ argv4[0].package.count = 1;
+ argv4[0].package.elements = &argv4[1];
+ argv4[1].integer.type = ACPI_TYPE_INTEGER;
+ argv4[1].integer.value = addr;
+
+ results = adxl_dsm(ADXL_IDX_FORWARD_TRANSLATE, argv4);
+ if (!results)
+ return -EINVAL;
+
+ r = results->package.elements + 1;
+ cnt = r->package.count;
+ if (cnt != adxl_count) {
+ ACPI_FREE(results);
+ return -EINVAL;
+ }
+ r = r->package.elements;
+
+ for (i = 0; i < cnt; i++)
+ component_values[i] = r[i].integer.value;
+
+ ACPI_FREE(results);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adxl_decode);
+
+static int __init adxl_init(void)
+{
+ char *path = ACPI_ADXL_PATH;
+ union acpi_object *p;
+ acpi_status status;
+ int i;
+
+ status = acpi_get_handle(NULL, path, &handle);
+ if (ACPI_FAILURE(status)) {
+ pr_debug("No ACPI handle for path %s\n", path);
+ return -ENODEV;
+ }
+
+ if (!acpi_has_method(handle, "_DSM")) {
+ pr_info("No DSM method\n");
+ return -ENODEV;
+ }
+
+ if (!acpi_check_dsm(handle, &adxl_guid, ADXL_REVISION,
+ ADXL_IDX_GET_ADDR_PARAMS |
+ ADXL_IDX_FORWARD_TRANSLATE)) {
+ pr_info("DSM method does not support forward translate\n");
+ return -ENODEV;
+ }
+
+ params = adxl_dsm(ADXL_IDX_GET_ADDR_PARAMS, NULL);
+ if (!params) {
+ pr_info("Failed to get component names\n");
+ return -ENODEV;
+ }
+
+ p = params->package.elements + 1;
+ adxl_count = p->package.count;
+ if (adxl_count > ADXL_MAX_COMPONENTS) {
+ pr_info("Insane number of address component names %d\n", adxl_count);
+ ACPI_FREE(params);
+ return -ENODEV;
+ }
+ p = p->package.elements;
+
+ /*
+ * Allocate one extra for NULL termination.
+ */
+ adxl_component_names = kcalloc(adxl_count + 1, sizeof(char *), GFP_KERNEL);
+ if (!adxl_component_names) {
+ ACPI_FREE(params);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < adxl_count; i++)
+ adxl_component_names[i] = p[i].string.pointer;
+
+ return 0;
+}
+subsys_initcall(adxl_init);
diff --git a/drivers/acpi/acpi_amba.c b/drivers/acpi/acpi_amba.c
new file mode 100644
index 000000000..f5b443ab0
--- /dev/null
+++ b/drivers/acpi/acpi_amba.c
@@ -0,0 +1,130 @@
+// 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 */
+ {"ARMH0330", 0}, /* ARM DMA Controller DMA-330 */
+ {"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 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);
+
+ 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
new file mode 100644
index 000000000..80f945cbe
--- /dev/null
+++ b/drivers/acpi/acpi_apd.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ACPI support for ACPI2platform device.
+ *
+ * Copyright (c) 2014,2015 AMD Corporation.
+ * Authors: Ken Xue <Ken.Xue@amd.com>
+ * Wu, Jeff <Jeff.Wu@amd.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_data/clk-fch.h>
+#include <linux/platform_device.h>
+
+#include "internal.h"
+
+struct apd_private_data;
+
+/**
+ * struct apd_device_desc - a descriptor for apd device
+ * @fixed_clk_rate: fixed rate input clock source for acpi device;
+ * 0 means no fixed rate input clock source
+ * @properties: build-in properties of the device such as UART
+ * @setup: a hook routine to set device resource during create platform device
+ *
+ * Device description defined as acpi_device_id.driver_data
+ */
+struct apd_device_desc {
+ unsigned int fixed_clk_rate;
+ struct property_entry *properties;
+ int (*setup)(struct apd_private_data *pdata);
+};
+
+struct apd_private_data {
+ struct clk *clk;
+ struct acpi_device *adev;
+ const struct apd_device_desc *dev_desc;
+};
+
+#if defined(CONFIG_X86_AMD_PLATFORM_DEVICE) || defined(CONFIG_ARM64)
+#define APD_ADDR(desc) ((unsigned long)&desc)
+
+static int acpi_apd_setup(struct apd_private_data *pdata)
+{
+ const struct apd_device_desc *dev_desc = pdata->dev_desc;
+ struct clk *clk;
+
+ if (dev_desc->fixed_clk_rate) {
+ clk = clk_register_fixed_rate(&pdata->adev->dev,
+ dev_name(&pdata->adev->dev),
+ NULL, 0, dev_desc->fixed_clk_rate);
+ clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev));
+ pdata->clk = clk;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
+
+static int fch_misc_setup(struct apd_private_data *pdata)
+{
+ struct acpi_device *adev = pdata->adev;
+ const union acpi_object *obj;
+ struct platform_device *clkdev;
+ struct fch_clk_data *clk_data;
+ struct resource_entry *rentry;
+ struct list_head resource_list;
+ int ret;
+
+ clk_data = devm_kzalloc(&adev->dev, sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&resource_list);
+ 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);
+ } else {
+ /* Set default name to mclk if entry missing in firmware */
+ clk_data->name = "mclk";
+ }
+
+ list_for_each_entry(rentry, &resource_list, node) {
+ clk_data->base = devm_ioremap(&adev->dev, rentry->res->start,
+ resource_size(rentry->res));
+ break;
+ }
+ if (!clk_data->base)
+ return -ENOMEM;
+
+ acpi_dev_free_resource_list(&resource_list);
+
+ clkdev = platform_device_register_data(&adev->dev, "clk-fch",
+ PLATFORM_DEVID_NONE, clk_data,
+ sizeof(*clk_data));
+ return PTR_ERR_OR_ZERO(clkdev);
+}
+
+static const struct apd_device_desc cz_i2c_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 133000000,
+};
+
+static const struct apd_device_desc wt_i2c_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 150000000,
+};
+
+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 apd_device_desc cz_uart_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 48000000,
+ .properties = uart_properties,
+};
+
+static const struct apd_device_desc fch_misc_desc = {
+ .setup = fch_misc_setup,
+};
+#endif /* CONFIG_X86_AMD_PLATFORM_DEVICE */
+
+#ifdef CONFIG_ARM64
+static const struct apd_device_desc xgene_i2c_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 100000000,
+};
+
+static const struct apd_device_desc vulcan_spi_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 133000000,
+};
+
+static const struct apd_device_desc hip07_i2c_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 200000000,
+};
+
+static const struct apd_device_desc hip08_i2c_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 250000000,
+};
+
+static const struct apd_device_desc hip08_lite_i2c_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 125000000,
+};
+
+static const struct apd_device_desc thunderx2_i2c_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 125000000,
+};
+
+static const struct apd_device_desc nxp_i2c_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 350000000,
+};
+
+static const struct apd_device_desc hip08_spi_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 250000000,
+};
+#endif /* CONFIG_ARM64 */
+
+#endif
+
+/*
+ * Create platform device during acpi scan attach handle.
+ * Return value > 0 on success of creating device.
+ */
+static int acpi_apd_create_device(struct acpi_device *adev,
+ const struct acpi_device_id *id)
+{
+ const struct apd_device_desc *dev_desc = (void *)id->driver_data;
+ struct apd_private_data *pdata;
+ struct platform_device *pdev;
+ int ret;
+
+ 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;
+
+ pdata->adev = adev;
+ pdata->dev_desc = dev_desc;
+
+ if (dev_desc->setup) {
+ ret = dev_desc->setup(pdata);
+ if (ret)
+ goto err_out;
+ }
+
+ adev->driver_data = pdata;
+ pdev = acpi_create_platform_device(adev, dev_desc->properties);
+ if (!IS_ERR_OR_NULL(pdev))
+ return 1;
+
+ ret = PTR_ERR(pdev);
+ adev->driver_data = NULL;
+
+ err_out:
+ kfree(pdata);
+ return ret;
+}
+
+static const struct acpi_device_id acpi_apd_device_ids[] = {
+ /* Generic apd devices */
+#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
+ { "AMD0010", APD_ADDR(cz_i2c_desc) },
+ { "AMD0020", APD_ADDR(cz_uart_desc) },
+ { "AMD0030", },
+ { "AMD0040", APD_ADDR(fch_misc_desc)},
+ { "AMDI0010", APD_ADDR(wt_i2c_desc) },
+ { "AMDI0019", APD_ADDR(wt_i2c_desc) },
+ { "AMDI0020", APD_ADDR(cz_uart_desc) },
+ { "AMDI0022", APD_ADDR(cz_uart_desc) },
+ { "HYGO0010", APD_ADDR(wt_i2c_desc) },
+#endif
+#ifdef CONFIG_ARM64
+ { "APMC0D0F", APD_ADDR(xgene_i2c_desc) },
+ { "BRCM900D", APD_ADDR(vulcan_spi_desc) },
+ { "CAV900D", APD_ADDR(vulcan_spi_desc) },
+ { "CAV9007", APD_ADDR(thunderx2_i2c_desc) },
+ { "HISI02A1", APD_ADDR(hip07_i2c_desc) },
+ { "HISI02A2", APD_ADDR(hip08_i2c_desc) },
+ { "HISI02A3", APD_ADDR(hip08_lite_i2c_desc) },
+ { "HISI0173", APD_ADDR(hip08_spi_desc) },
+ { "NXP0001", APD_ADDR(nxp_i2c_desc) },
+#endif
+ { }
+};
+
+static struct acpi_scan_handler apd_handler = {
+ .ids = acpi_apd_device_ids,
+ .attach = acpi_apd_create_device,
+};
+
+void __init acpi_apd_init(void)
+{
+ acpi_scan_add_handler(&apd_handler);
+}
diff --git a/drivers/acpi/acpi_cmos_rtc.c b/drivers/acpi/acpi_cmos_rtc.c
new file mode 100644
index 000000000..4cf4aef7c
--- /dev/null
+++ b/drivers/acpi/acpi_cmos_rtc.c
@@ -0,0 +1,87 @@
+// 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_configfs.c b/drivers/acpi/acpi_configfs.c
new file mode 100644
index 000000000..c970792b1
--- /dev/null
+++ b/drivers/acpi/acpi_configfs.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI configfs support
+ *
+ * Copyright (c) 2016 Intel Corporation
+ */
+
+#define pr_fmt(fmt) "ACPI configfs: " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/configfs.h>
+#include <linux/acpi.h>
+#include <linux/security.h>
+
+static struct config_group *acpi_table_group;
+
+struct acpi_table {
+ struct config_item cfg;
+ struct acpi_table_header *header;
+ u32 index;
+};
+
+static ssize_t acpi_table_aml_write(struct config_item *cfg,
+ const void *data, size_t size)
+{
+ const struct acpi_table_header *header = data;
+ struct acpi_table *table;
+ int ret = security_locked_down(LOCKDOWN_ACPI_TABLES);
+
+ if (ret)
+ return ret;
+
+ table = container_of(cfg, struct acpi_table, cfg);
+
+ if (table->header) {
+ pr_err("table already loaded\n");
+ return -EBUSY;
+ }
+
+ if (header->length != size) {
+ pr_err("invalid table length\n");
+ return -EINVAL;
+ }
+
+ if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) {
+ pr_err("invalid table signature\n");
+ return -EINVAL;
+ }
+
+ table = container_of(cfg, struct acpi_table, cfg);
+
+ table->header = kmemdup(header, header->length, GFP_KERNEL);
+ if (!table->header)
+ return -ENOMEM;
+
+ ret = acpi_load_table(table->header, &table->index);
+ if (ret) {
+ kfree(table->header);
+ table->header = NULL;
+ }
+
+ return ret;
+}
+
+static inline struct acpi_table_header *get_header(struct config_item *cfg)
+{
+ struct acpi_table *table = container_of(cfg, struct acpi_table, cfg);
+
+ if (!table->header)
+ pr_err("table not loaded\n");
+
+ return table->header ?: ERR_PTR(-EINVAL);
+}
+
+static ssize_t acpi_table_aml_read(struct config_item *cfg,
+ void *data, size_t size)
+{
+ struct acpi_table_header *h = get_header(cfg);
+
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ if (data)
+ memcpy(data, h, h->length);
+
+ return h->length;
+}
+
+#define MAX_ACPI_TABLE_SIZE (128 * 1024)
+
+CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE);
+
+static struct configfs_bin_attribute *acpi_table_bin_attrs[] = {
+ &acpi_table_attr_aml,
+ NULL,
+};
+
+static ssize_t acpi_table_signature_show(struct config_item *cfg, char *str)
+{
+ struct acpi_table_header *h = get_header(cfg);
+
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ return sysfs_emit(str, "%.*s\n", ACPI_NAMESEG_SIZE, h->signature);
+}
+
+static ssize_t acpi_table_length_show(struct config_item *cfg, char *str)
+{
+ struct acpi_table_header *h = get_header(cfg);
+
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ return sysfs_emit(str, "%d\n", h->length);
+}
+
+static ssize_t acpi_table_revision_show(struct config_item *cfg, char *str)
+{
+ struct acpi_table_header *h = get_header(cfg);
+
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ return sysfs_emit(str, "%d\n", h->revision);
+}
+
+static ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str)
+{
+ struct acpi_table_header *h = get_header(cfg);
+
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ return sysfs_emit(str, "%.*s\n", ACPI_OEM_ID_SIZE, h->oem_id);
+}
+
+static ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str)
+{
+ struct acpi_table_header *h = get_header(cfg);
+
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ return sysfs_emit(str, "%.*s\n", ACPI_OEM_TABLE_ID_SIZE, h->oem_table_id);
+}
+
+static ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str)
+{
+ struct acpi_table_header *h = get_header(cfg);
+
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ return sysfs_emit(str, "%d\n", h->oem_revision);
+}
+
+static ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg,
+ char *str)
+{
+ struct acpi_table_header *h = get_header(cfg);
+
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ return sysfs_emit(str, "%.*s\n", ACPI_NAMESEG_SIZE, h->asl_compiler_id);
+}
+
+static ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg,
+ char *str)
+{
+ struct acpi_table_header *h = get_header(cfg);
+
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ return sysfs_emit(str, "%d\n", h->asl_compiler_revision);
+}
+
+CONFIGFS_ATTR_RO(acpi_table_, signature);
+CONFIGFS_ATTR_RO(acpi_table_, length);
+CONFIGFS_ATTR_RO(acpi_table_, revision);
+CONFIGFS_ATTR_RO(acpi_table_, oem_id);
+CONFIGFS_ATTR_RO(acpi_table_, oem_table_id);
+CONFIGFS_ATTR_RO(acpi_table_, oem_revision);
+CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id);
+CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision);
+
+static struct configfs_attribute *acpi_table_attrs[] = {
+ &acpi_table_attr_signature,
+ &acpi_table_attr_length,
+ &acpi_table_attr_revision,
+ &acpi_table_attr_oem_id,
+ &acpi_table_attr_oem_table_id,
+ &acpi_table_attr_oem_revision,
+ &acpi_table_attr_asl_compiler_id,
+ &acpi_table_attr_asl_compiler_revision,
+ NULL,
+};
+
+static const struct config_item_type acpi_table_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_bin_attrs = acpi_table_bin_attrs,
+ .ct_attrs = acpi_table_attrs,
+};
+
+static struct config_item *acpi_table_make_item(struct config_group *group,
+ const char *name)
+{
+ struct acpi_table *table;
+
+ table = kzalloc(sizeof(*table), GFP_KERNEL);
+ if (!table)
+ return ERR_PTR(-ENOMEM);
+
+ config_item_init_type_name(&table->cfg, name, &acpi_table_type);
+ return &table->cfg;
+}
+
+static void acpi_table_drop_item(struct config_group *group,
+ struct config_item *cfg)
+{
+ struct acpi_table *table = container_of(cfg, struct acpi_table, cfg);
+
+ pr_debug("Host-directed Dynamic ACPI Table Unload\n");
+ acpi_unload_table(table->index);
+ config_item_put(cfg);
+}
+
+static struct configfs_group_operations acpi_table_group_ops = {
+ .make_item = acpi_table_make_item,
+ .drop_item = acpi_table_drop_item,
+};
+
+static const struct config_item_type acpi_tables_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_group_ops = &acpi_table_group_ops,
+};
+
+static const struct config_item_type acpi_root_group_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem acpi_configfs = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "acpi",
+ .ci_type = &acpi_root_group_type,
+ },
+ },
+ .su_mutex = __MUTEX_INITIALIZER(acpi_configfs.su_mutex),
+};
+
+static int __init acpi_configfs_init(void)
+{
+ int ret;
+ struct config_group *root = &acpi_configfs.su_group;
+
+ config_group_init(root);
+
+ ret = configfs_register_subsystem(&acpi_configfs);
+ if (ret)
+ return ret;
+
+ acpi_table_group = configfs_register_default_group(root, "table",
+ &acpi_tables_type);
+ if (IS_ERR(acpi_table_group)) {
+ configfs_unregister_subsystem(&acpi_configfs);
+ return PTR_ERR(acpi_table_group);
+ }
+
+ return 0;
+}
+module_init(acpi_configfs_init);
+
+static void __exit acpi_configfs_exit(void)
+{
+ configfs_unregister_default_group(acpi_table_group);
+ configfs_unregister_subsystem(&acpi_configfs);
+}
+module_exit(acpi_configfs_exit);
+
+MODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>");
+MODULE_DESCRIPTION("ACPI configfs support");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
new file mode 100644
index 000000000..d50261d05
--- /dev/null
+++ b/drivers/acpi/acpi_dbg.c
@@ -0,0 +1,785 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI AML interfacing support
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <lv.zheng@intel.com>
+ */
+
+/* #define DEBUG */
+#define pr_fmt(fmt) "ACPI: AML: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/circ_buf.h>
+#include <linux/acpi.h>
+#include "internal.h"
+
+#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size))
+#define ACPI_AML_BUF_SIZE PAGE_SIZE
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define ACPI_AML_OPENED 0x0001
+#define ACPI_AML_CLOSED 0x0002
+#define ACPI_AML_IN_USER 0x0004 /* user space is writing cmd */
+#define ACPI_AML_IN_KERN 0x0008 /* kernel space is reading cmd */
+#define ACPI_AML_OUT_USER 0x0010 /* user space is reading log */
+#define ACPI_AML_OUT_KERN 0x0020 /* kernel space is writing log */
+#define ACPI_AML_USER (ACPI_AML_IN_USER | ACPI_AML_OUT_USER)
+#define ACPI_AML_KERN (ACPI_AML_IN_KERN | ACPI_AML_OUT_KERN)
+#define ACPI_AML_BUSY (ACPI_AML_USER | ACPI_AML_KERN)
+#define ACPI_AML_OPEN (ACPI_AML_OPENED | ACPI_AML_CLOSED)
+
+struct acpi_aml_io {
+ wait_queue_head_t wait;
+ unsigned long flags;
+ unsigned long users;
+ struct mutex lock;
+ struct task_struct *thread;
+ char out_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf out_crc;
+ char in_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf in_crc;
+ acpi_osd_exec_callback function;
+ void *context;
+ unsigned long usages;
+};
+
+static struct acpi_aml_io acpi_aml_io;
+static bool acpi_aml_initialized;
+static struct file *acpi_aml_active_reader;
+static struct dentry *acpi_aml_dentry;
+
+static inline bool __acpi_aml_running(void)
+{
+ return acpi_aml_io.thread ? true : false;
+}
+
+static inline bool __acpi_aml_access_ok(unsigned long flag)
+{
+ /*
+ * The debugger interface is in opened state (OPENED && !CLOSED),
+ * then it is allowed to access the debugger buffers from either
+ * user space or the kernel space.
+ * In addition, for the kernel space, only the debugger thread
+ * (thread ID matched) is allowed to access.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED) ||
+ (acpi_aml_io.flags & ACPI_AML_CLOSED) ||
+ !__acpi_aml_running())
+ return false;
+ if ((flag & ACPI_AML_KERN) &&
+ current != acpi_aml_io.thread)
+ return false;
+ return true;
+}
+
+static inline bool __acpi_aml_readable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another read is not in progress and there is data in buffer
+ * available for read.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_count(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_writable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another write is not in progress and there is buffer space
+ * available for write.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_space(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_busy(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_BUSY)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_used(void)
+{
+ return acpi_aml_io.usages ? true : false;
+}
+
+static inline bool acpi_aml_running(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_running();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_busy(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_busy();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_used(void)
+{
+ bool ret;
+
+ /*
+ * The usage count is prepared to avoid race conditions between the
+ * starts and the stops of the debugger thread.
+ */
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_used();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_KERN) ||
+ __acpi_aml_readable(&acpi_aml_io.in_crc, ACPI_AML_IN_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_KERN) ||
+ __acpi_aml_writable(&acpi_aml_io.out_crc, ACPI_AML_OUT_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_USER) ||
+ __acpi_aml_readable(&acpi_aml_io.out_crc, ACPI_AML_OUT_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_USER) ||
+ __acpi_aml_writable(&acpi_aml_io.in_crc, ACPI_AML_IN_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_write(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_writable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_read(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_readable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static void acpi_aml_unlock_fifo(unsigned long flag, bool wakeup)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~flag;
+ if (wakeup)
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+}
+
+static int acpi_aml_write_kern(const char *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_OUT_KERN);
+ if (ret < 0)
+ return ret;
+ /* sync tail before inserting logs */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ memcpy(p, buf, n);
+ /* sync head after inserting logs */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_KERN, true);
+ return n;
+}
+
+static int acpi_aml_readb_kern(void)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_IN_KERN);
+ if (ret < 0)
+ return ret;
+ /* sync head before removing cmds */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ ret = (int)*p;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ crc->tail = (crc->tail + 1) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_IN_KERN, true);
+ return ret;
+}
+
+/*
+ * acpi_aml_write_log() - Capture debugger output
+ * @msg: the debugger output
+ *
+ * This function should be used to implement acpi_os_printf() to filter out
+ * the debugger output and store the output into the debugger interface
+ * buffer. Return the size of stored logs or errno.
+ */
+static ssize_t acpi_aml_write_log(const char *msg)
+{
+ int ret = 0;
+ int count = 0, size = 0;
+
+ if (!acpi_aml_initialized)
+ return -ENODEV;
+ if (msg)
+ count = strlen(msg);
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_kern(msg + size, count);
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ break;
+ }
+ if (ret < 0)
+ break;
+ size += ret;
+ count -= ret;
+ }
+ return size > 0 ? size : ret;
+}
+
+/*
+ * acpi_aml_read_cmd() - Capture debugger input
+ * @msg: the debugger input
+ * @size: the size of the debugger input
+ *
+ * This function should be used to implement acpi_os_get_line() to capture
+ * the debugger input commands and store the input commands into the
+ * debugger interface buffer. Return the size of stored commands or errno.
+ */
+static ssize_t acpi_aml_read_cmd(char *msg, size_t count)
+{
+ int ret = 0;
+ int size = 0;
+
+ /*
+ * This is ensured by the running fact of the debugger thread
+ * unless a bug is introduced.
+ */
+ BUG_ON(!acpi_aml_initialized);
+ while (count > 0) {
+again:
+ /*
+ * Check each input byte to find the end of the command.
+ */
+ ret = acpi_aml_readb_kern();
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_readable());
+ /*
+ * We need to retry when the condition becomes
+ * true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ if (ret < 0)
+ break;
+ *(msg + size) = (char)ret;
+ size++;
+ count--;
+ if (ret == '\n') {
+ /*
+ * acpi_os_get_line() requires a zero terminated command
+ * string.
+ */
+ *(msg + size - 1) = '\0';
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static int acpi_aml_thread(void *unused)
+{
+ acpi_osd_exec_callback function = NULL;
+ void *context;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (acpi_aml_io.function) {
+ acpi_aml_io.usages++;
+ function = acpi_aml_io.function;
+ context = acpi_aml_io.context;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ if (function)
+ function(context);
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.usages--;
+ if (!__acpi_aml_used()) {
+ acpi_aml_io.thread = NULL;
+ wake_up(&acpi_aml_io.wait);
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ return 0;
+}
+
+/*
+ * acpi_aml_create_thread() - Create AML debugger thread
+ * @function: the debugger thread callback
+ * @context: the context to be passed to the debugger thread
+ *
+ * This function should be used to implement acpi_os_execute() which is
+ * used by the ACPICA debugger to create the debugger thread.
+ */
+static int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ struct task_struct *t;
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.function = function;
+ acpi_aml_io.context = context;
+ mutex_unlock(&acpi_aml_io.lock);
+
+ t = kthread_create(acpi_aml_thread, NULL, "aml");
+ if (IS_ERR(t)) {
+ pr_err("Failed to create AML debugger thread.\n");
+ return PTR_ERR(t);
+ }
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.thread = t;
+ acpi_set_debugger_thread_id((acpi_thread_id)(unsigned long)t);
+ wake_up_process(t);
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+
+static int acpi_aml_wait_command_ready(bool single_step,
+ char *buffer, size_t length)
+{
+ acpi_status status;
+
+ if (single_step)
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
+ else
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
+
+ status = acpi_os_get_line(buffer, length, NULL);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ return 0;
+}
+
+static int acpi_aml_notify_command_complete(void)
+{
+ return 0;
+}
+
+static int acpi_aml_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ acpi_status status;
+
+ mutex_lock(&acpi_aml_io.lock);
+ /*
+ * The debugger interface is being closed, no new user is allowed
+ * during this period.
+ */
+ if (acpi_aml_io.flags & ACPI_AML_CLOSED) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
+ /*
+ * Only one reader is allowed to initiate the debugger
+ * thread.
+ */
+ if (acpi_aml_active_reader) {
+ ret = -EBUSY;
+ goto err_lock;
+ } else {
+ pr_debug("Opening debugger reader.\n");
+ acpi_aml_active_reader = file;
+ }
+ } else {
+ /*
+ * No writer is allowed unless the debugger thread is
+ * ready.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ }
+ if (acpi_aml_active_reader == file) {
+ pr_debug("Opening debugger interface.\n");
+ mutex_unlock(&acpi_aml_io.lock);
+
+ pr_debug("Initializing debugger thread.\n");
+ status = acpi_initialize_debugger();
+ if (ACPI_FAILURE(status)) {
+ pr_err("Failed to initialize debugger.\n");
+ ret = -EINVAL;
+ goto err_exit;
+ }
+ pr_debug("Debugger thread initialized.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags |= ACPI_AML_OPENED;
+ acpi_aml_io.out_crc.head = acpi_aml_io.out_crc.tail = 0;
+ acpi_aml_io.in_crc.head = acpi_aml_io.in_crc.tail = 0;
+ pr_debug("Debugger interface opened.\n");
+ }
+ acpi_aml_io.users++;
+err_lock:
+ if (ret < 0) {
+ if (acpi_aml_active_reader == file)
+ acpi_aml_active_reader = NULL;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+err_exit:
+ return ret;
+}
+
+static int acpi_aml_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.users--;
+ if (file == acpi_aml_active_reader) {
+ pr_debug("Closing debugger reader.\n");
+ acpi_aml_active_reader = NULL;
+
+ pr_debug("Closing debugger interface.\n");
+ acpi_aml_io.flags |= ACPI_AML_CLOSED;
+
+ /*
+ * Wake up all user space/kernel space blocked
+ * readers/writers.
+ */
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+ /*
+ * Wait all user space/kernel space readers/writers to
+ * stop so that ACPICA command loop of the debugger thread
+ * should fail all its command line reads after this point.
+ */
+ wait_event(acpi_aml_io.wait, !acpi_aml_busy());
+
+ /*
+ * Then we try to terminate the debugger thread if it is
+ * not terminated.
+ */
+ pr_debug("Terminating debugger thread.\n");
+ acpi_terminate_debugger();
+ wait_event(acpi_aml_io.wait, !acpi_aml_used());
+ pr_debug("Debugger thread terminated.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~ACPI_AML_OPENED;
+ }
+ if (acpi_aml_io.users == 0) {
+ pr_debug("Debugger interface closed.\n");
+ acpi_aml_io.flags &= ~ACPI_AML_CLOSED;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+
+static int acpi_aml_read_user(char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_OUT_USER);
+ if (ret < 0)
+ return ret;
+ /* sync head before removing logs */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ n = min(len, circ_count_to_end(crc));
+ if (copy_to_user(buf, p, n)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ /* sync tail after removing logs */
+ smp_mb();
+ crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, ret >= 0);
+ return ret;
+}
+
+static ssize_t acpi_aml_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!count)
+ return 0;
+ if (!access_ok(buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_read_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_readable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (ret < 0) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static int acpi_aml_write_user(const char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_IN_USER);
+ if (ret < 0)
+ return ret;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ if (copy_from_user(p, buf, n)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ /* sync head after inserting cmds */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_IN_USER, ret >= 0);
+ return n;
+}
+
+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;
+
+ if (!count)
+ return 0;
+ if (!access_ok(buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (ret < 0) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static __poll_t acpi_aml_poll(struct file *file, poll_table *wait)
+{
+ __poll_t masks = 0;
+
+ poll_wait(file, &acpi_aml_io.wait, wait);
+ if (acpi_aml_user_readable())
+ masks |= EPOLLIN | EPOLLRDNORM;
+ if (acpi_aml_user_writable())
+ masks |= EPOLLOUT | EPOLLWRNORM;
+
+ return masks;
+}
+
+static const struct file_operations acpi_aml_operations = {
+ .read = acpi_aml_read,
+ .write = acpi_aml_write,
+ .poll = acpi_aml_poll,
+ .open = acpi_aml_open,
+ .release = acpi_aml_release,
+ .llseek = generic_file_llseek,
+};
+
+static const struct acpi_debugger_ops acpi_aml_debugger = {
+ .create_thread = acpi_aml_create_thread,
+ .read_cmd = acpi_aml_read_cmd,
+ .write_log = acpi_aml_write_log,
+ .wait_command_ready = acpi_aml_wait_command_ready,
+ .notify_command_complete = acpi_aml_notify_command_complete,
+};
+
+static int __init acpi_aml_init(void)
+{
+ int ret;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ /* Initialize AML IO interface */
+ mutex_init(&acpi_aml_io.lock);
+ init_waitqueue_head(&acpi_aml_io.wait);
+ acpi_aml_io.out_crc.buf = acpi_aml_io.out_buf;
+ acpi_aml_io.in_crc.buf = acpi_aml_io.in_buf;
+
+ acpi_aml_dentry = debugfs_create_file("acpidbg",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ acpi_debugfs_dir, NULL,
+ &acpi_aml_operations);
+
+ ret = acpi_register_debugger(THIS_MODULE, &acpi_aml_debugger);
+ if (ret) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ return ret;
+ }
+
+ acpi_aml_initialized = true;
+ return 0;
+}
+
+static void __exit acpi_aml_exit(void)
+{
+ if (acpi_aml_initialized) {
+ acpi_unregister_debugger(&acpi_aml_debugger);
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ acpi_aml_initialized = false;
+ }
+}
+
+module_init(acpi_aml_init);
+module_exit(acpi_aml_exit);
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_DESCRIPTION("ACPI debugger userspace IO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/acpi_extlog.c b/drivers/acpi/acpi_extlog.c
new file mode 100644
index 000000000..088db2356
--- /dev/null
+++ b/drivers/acpi/acpi_extlog.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Extended Error Log driver
+ *
+ * Copyright (C) 2013 Intel Corp.
+ * Author: Chen, Gong <gong.chen@intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/cper.h>
+#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 "apei/apei-internal.h"
+#include <ras/ras_event.h>
+
+#define EXT_ELOG_ENTRY_MASK GENMASK_ULL(51, 0) /* elog entry address mask */
+
+#define EXTLOG_DSM_REV 0x0
+#define EXTLOG_FN_ADDR 0x1
+
+#define FLAG_OS_OPTIN BIT(0)
+#define ELOG_ENTRY_VALID (1ULL<<63)
+#define ELOG_ENTRY_LEN 0x1000
+
+#define EMCA_BUG \
+ "Can not request iomem region <0x%016llx-0x%016llx> - eMCA disabled\n"
+
+struct extlog_l1_head {
+ u32 ver; /* Header Version */
+ u32 hdr_len; /* Header Length */
+ u64 total_len; /* entire L1 Directory length including this header */
+ u64 elog_base; /* MCA Error Log Directory base address */
+ u64 elog_len; /* MCA Error Log Directory length */
+ u32 flags; /* bit 0 - OS/VMM Opt-in */
+ u8 rev0[12];
+ u32 entries; /* Valid L1 Directory entries per logical processor */
+ u8 rev1[12];
+};
+
+static u8 extlog_dsm_uuid[] __initdata = "663E35AF-CC10-41A4-88EA-5470AF055295";
+
+/* L1 table related physical address */
+static u64 elog_base;
+static size_t elog_size;
+static u64 l1_dirbase;
+static size_t l1_size;
+
+/* L1 table related virtual address */
+static void __iomem *extlog_l1_addr;
+static void __iomem *elog_addr;
+
+static void *elog_buf;
+
+static u64 *l1_entry_base;
+static u32 l1_percpu_entry;
+
+#define ELOG_IDX(cpu, bank) \
+ (cpu_physical_id(cpu) * l1_percpu_entry + (bank))
+
+#define ELOG_ENTRY_DATA(idx) \
+ (*(l1_entry_base + (idx)))
+
+#define ELOG_ENTRY_ADDR(phyaddr) \
+ (phyaddr - elog_base + (u8 *)elog_addr)
+
+static struct acpi_hest_generic_status *extlog_elog_entry_check(int cpu, int bank)
+{
+ int idx;
+ u64 data;
+ struct acpi_hest_generic_status *estatus;
+
+ WARN_ON(cpu < 0);
+ idx = ELOG_IDX(cpu, bank);
+ data = ELOG_ENTRY_DATA(idx);
+ if ((data & ELOG_ENTRY_VALID) == 0)
+ return NULL;
+
+ data &= EXT_ELOG_ENTRY_MASK;
+ estatus = (struct acpi_hest_generic_status *)ELOG_ENTRY_ADDR(data);
+
+ /* if no valid data in elog entry, just return */
+ if (estatus->block_status == 0)
+ return NULL;
+
+ return estatus;
+}
+
+static void __print_extlog_rcd(const char *pfx,
+ struct acpi_hest_generic_status *estatus, int cpu)
+{
+ static atomic_t seqno;
+ unsigned int curr_seqno;
+ char pfx_seq[64];
+
+ if (!pfx) {
+ if (estatus->error_severity <= CPER_SEV_CORRECTED)
+ pfx = KERN_INFO;
+ else
+ pfx = KERN_ERR;
+ }
+ curr_seqno = atomic_inc_return(&seqno);
+ snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}", pfx, curr_seqno);
+ printk("%s""Hardware error detected on CPU%d\n", pfx_seq, cpu);
+ cper_estatus_print(pfx_seq, estatus);
+}
+
+static int print_extlog_rcd(const char *pfx,
+ struct acpi_hest_generic_status *estatus, int cpu)
+{
+ /* Not more than 2 messages every 5 seconds */
+ static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2);
+ static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2);
+ struct ratelimit_state *ratelimit;
+
+ if (estatus->error_severity == CPER_SEV_CORRECTED ||
+ (estatus->error_severity == CPER_SEV_INFORMATIONAL))
+ ratelimit = &ratelimit_corrected;
+ else
+ ratelimit = &ratelimit_uncorrected;
+ if (__ratelimit(ratelimit)) {
+ __print_extlog_rcd(pfx, estatus, cpu);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int extlog_print(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct mce *mce = (struct mce *)data;
+ int bank = mce->bank;
+ int cpu = mce->extcpu;
+ struct acpi_hest_generic_status *estatus, *tmp;
+ struct acpi_hest_generic_data *gdata;
+ 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)
+ 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;
+
+ tmp = (struct acpi_hest_generic_status *)elog_buf;
+
+ if (!ras_userspace_consumers()) {
+ print_extlog_rcd(NULL, tmp, cpu);
+ goto out;
+ }
+
+ /* log event via trace */
+ err_seq++;
+ 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 = (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);
+ }
+ }
+
+out:
+ mce->kflags |= MCE_HANDLED_EXTLOG;
+ return NOTIFY_OK;
+}
+
+static bool __init extlog_get_l1addr(void)
+{
+ guid_t guid;
+ acpi_handle handle;
+ union acpi_object *obj;
+
+ if (guid_parse(extlog_dsm_uuid, &guid))
+ return false;
+ if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)))
+ return false;
+ if (!acpi_check_dsm(handle, &guid, EXTLOG_DSM_REV, 1 << EXTLOG_FN_ADDR))
+ return false;
+ obj = acpi_evaluate_dsm_typed(handle, &guid, EXTLOG_DSM_REV,
+ EXTLOG_FN_ADDR, NULL, ACPI_TYPE_INTEGER);
+ if (!obj) {
+ return false;
+ } else {
+ l1_dirbase = obj->integer.value;
+ ACPI_FREE(obj);
+ }
+
+ /* Spec says L1 directory must be 4K aligned, bail out if it isn't */
+ if (l1_dirbase & ((1 << 12) - 1)) {
+ pr_warn(FW_BUG "L1 Directory is invalid at physical %llx\n",
+ l1_dirbase);
+ return false;
+ }
+
+ return true;
+}
+static struct notifier_block extlog_mce_dec = {
+ .notifier_call = extlog_print,
+ .priority = MCE_PRIO_EXTLOG,
+};
+
+static int __init extlog_init(void)
+{
+ struct extlog_l1_head *l1_head;
+ void __iomem *extlog_l1_hdr;
+ size_t l1_hdr_size;
+ struct resource *r;
+ u64 cap;
+ int rc;
+
+ if (rdmsrl_safe(MSR_IA32_MCG_CAP, &cap) ||
+ !(cap & MCG_ELOG_P) ||
+ !extlog_get_l1addr())
+ return -ENODEV;
+
+ rc = -EINVAL;
+ /* get L1 header to fetch necessary information */
+ l1_hdr_size = sizeof(struct extlog_l1_head);
+ r = request_mem_region(l1_dirbase, l1_hdr_size, "L1 DIR HDR");
+ if (!r) {
+ pr_warn(FW_BUG EMCA_BUG,
+ (unsigned long long)l1_dirbase,
+ (unsigned long long)l1_dirbase + l1_hdr_size);
+ goto err;
+ }
+
+ extlog_l1_hdr = acpi_os_map_iomem(l1_dirbase, l1_hdr_size);
+ l1_head = (struct extlog_l1_head *)extlog_l1_hdr;
+ l1_size = l1_head->total_len;
+ l1_percpu_entry = l1_head->entries;
+ elog_base = l1_head->elog_base;
+ elog_size = l1_head->elog_len;
+ acpi_os_unmap_iomem(extlog_l1_hdr, l1_hdr_size);
+ release_mem_region(l1_dirbase, l1_hdr_size);
+
+ /* remap L1 header again based on completed information */
+ r = request_mem_region(l1_dirbase, l1_size, "L1 Table");
+ if (!r) {
+ pr_warn(FW_BUG EMCA_BUG,
+ (unsigned long long)l1_dirbase,
+ (unsigned long long)l1_dirbase + l1_size);
+ goto err;
+ }
+ extlog_l1_addr = acpi_os_map_iomem(l1_dirbase, l1_size);
+ l1_entry_base = (u64 *)((u8 *)extlog_l1_addr + l1_hdr_size);
+
+ /* remap elog table */
+ r = request_mem_region(elog_base, elog_size, "Elog Table");
+ if (!r) {
+ pr_warn(FW_BUG EMCA_BUG,
+ (unsigned long long)elog_base,
+ (unsigned long long)elog_base + elog_size);
+ goto err_release_l1_dir;
+ }
+ elog_addr = acpi_os_map_iomem(elog_base, elog_size);
+
+ rc = -ENOMEM;
+ /* allocate buffer to save elog record */
+ elog_buf = kmalloc(ELOG_ENTRY_LEN, GFP_KERNEL);
+ if (elog_buf == NULL)
+ goto err_release_elog;
+
+ mce_register_decode_chain(&extlog_mce_dec);
+ /* enable OS to be involved to take over management from BIOS */
+ ((struct extlog_l1_head *)extlog_l1_addr)->flags |= FLAG_OS_OPTIN;
+
+ return 0;
+
+err_release_elog:
+ if (elog_addr)
+ acpi_os_unmap_iomem(elog_addr, elog_size);
+ release_mem_region(elog_base, elog_size);
+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:
+ pr_warn(FW_BUG "Extended error log disabled because of problems parsing f/w tables\n");
+ return rc;
+}
+
+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)
+ 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);
+ release_mem_region(l1_dirbase, l1_size);
+ kfree(elog_buf);
+}
+
+module_init(extlog_init);
+module_exit(extlog_exit);
+
+MODULE_AUTHOR("Chen, Gong <gong.chen@intel.com>");
+MODULE_DESCRIPTION("Extended MCA Error Log Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/acpi_fpdt.c b/drivers/acpi/acpi_fpdt.c
new file mode 100644
index 000000000..271092f27
--- /dev/null
+++ b/drivers/acpi/acpi_fpdt.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * FPDT support for exporting boot and suspend/resume performance data
+ *
+ * Copyright (C) 2021 Intel Corporation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "ACPI FPDT: " fmt
+
+#include <linux/acpi.h>
+
+/*
+ * FPDT contains ACPI table header and a number of fpdt_subtable_entries.
+ * Each fpdt_subtable_entry points to a subtable: FBPT or S3PT.
+ * Each FPDT subtable (FBPT/S3PT) is composed of a fpdt_subtable_header
+ * and a number of fpdt performance records.
+ * Each FPDT performance record is composed of a fpdt_record_header and
+ * performance data fields, for boot or suspend or resume phase.
+ */
+enum fpdt_subtable_type {
+ SUBTABLE_FBPT,
+ SUBTABLE_S3PT,
+};
+
+struct fpdt_subtable_entry {
+ u16 type; /* refer to enum fpdt_subtable_type */
+ u8 length;
+ u8 revision;
+ u32 reserved;
+ u64 address; /* physical address of the S3PT/FBPT table */
+};
+
+struct fpdt_subtable_header {
+ u32 signature;
+ u32 length;
+};
+
+enum fpdt_record_type {
+ RECORD_S3_RESUME,
+ RECORD_S3_SUSPEND,
+ RECORD_BOOT,
+};
+
+struct fpdt_record_header {
+ u16 type; /* refer to enum fpdt_record_type */
+ u8 length;
+ u8 revision;
+};
+
+struct resume_performance_record {
+ struct fpdt_record_header header;
+ u32 resume_count;
+ u64 resume_prev;
+ u64 resume_avg;
+} __attribute__((packed));
+
+struct boot_performance_record {
+ struct fpdt_record_header header;
+ u32 reserved;
+ u64 firmware_start;
+ u64 bootloader_load;
+ u64 bootloader_launch;
+ u64 exitbootservice_start;
+ u64 exitbootservice_end;
+} __attribute__((packed));
+
+struct suspend_performance_record {
+ struct fpdt_record_header header;
+ u64 suspend_start;
+ u64 suspend_end;
+} __attribute__((packed));
+
+
+static struct resume_performance_record *record_resume;
+static struct suspend_performance_record *record_suspend;
+static struct boot_performance_record *record_boot;
+
+#define FPDT_ATTR(phase, name) \
+static ssize_t name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ return sprintf(buf, "%llu\n", record_##phase->name); \
+} \
+static struct kobj_attribute name##_attr = \
+__ATTR(name##_ns, 0444, name##_show, NULL)
+
+FPDT_ATTR(resume, resume_prev);
+FPDT_ATTR(resume, resume_avg);
+FPDT_ATTR(suspend, suspend_start);
+FPDT_ATTR(suspend, suspend_end);
+FPDT_ATTR(boot, firmware_start);
+FPDT_ATTR(boot, bootloader_load);
+FPDT_ATTR(boot, bootloader_launch);
+FPDT_ATTR(boot, exitbootservice_start);
+FPDT_ATTR(boot, exitbootservice_end);
+
+static ssize_t resume_count_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", record_resume->resume_count);
+}
+
+static struct kobj_attribute resume_count_attr =
+__ATTR_RO(resume_count);
+
+static struct attribute *resume_attrs[] = {
+ &resume_count_attr.attr,
+ &resume_prev_attr.attr,
+ &resume_avg_attr.attr,
+ NULL
+};
+
+static const struct attribute_group resume_attr_group = {
+ .attrs = resume_attrs,
+ .name = "resume",
+};
+
+static struct attribute *suspend_attrs[] = {
+ &suspend_start_attr.attr,
+ &suspend_end_attr.attr,
+ NULL
+};
+
+static const struct attribute_group suspend_attr_group = {
+ .attrs = suspend_attrs,
+ .name = "suspend",
+};
+
+static struct attribute *boot_attrs[] = {
+ &firmware_start_attr.attr,
+ &bootloader_load_attr.attr,
+ &bootloader_launch_attr.attr,
+ &exitbootservice_start_attr.attr,
+ &exitbootservice_end_attr.attr,
+ NULL
+};
+
+static const struct attribute_group boot_attr_group = {
+ .attrs = boot_attrs,
+ .name = "boot",
+};
+
+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;
+ struct fpdt_record_header *record_header;
+ char *signature = (subtable_type == SUBTABLE_FBPT ? "FBPT" : "S3PT");
+ 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;
+
+ if (strncmp((char *)&subtable_header->signature, signature, 4)) {
+ pr_info(FW_BUG "subtable signature and type mismatch!\n");
+ return -EINVAL;
+ }
+
+ length = subtable_header->length;
+ acpi_os_unmap_memory(subtable_header, sizeof(*subtable_header));
+
+ subtable_header = acpi_os_map_memory(address, length);
+ if (!subtable_header)
+ return -ENOMEM;
+
+ offset = sizeof(*subtable_header);
+ while (offset < length) {
+ 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);
+ result = -EINVAL;
+ goto err;
+ }
+ if (record_resume) {
+ pr_err("Duplicate resume performance record found.\n");
+ continue;
+ }
+ record_resume = (struct resume_performance_record *)record_header;
+ result = sysfs_create_group(fpdt_kobj, &resume_attr_group);
+ if (result)
+ goto err;
+ break;
+ case RECORD_S3_SUSPEND:
+ if (subtable_type != SUBTABLE_S3PT) {
+ pr_err(FW_BUG "Invalid %d for subtable %s\n",
+ record_header->type, signature);
+ continue;
+ }
+ if (record_suspend) {
+ pr_err("Duplicate suspend performance record found.\n");
+ continue;
+ }
+ record_suspend = (struct suspend_performance_record *)record_header;
+ result = sysfs_create_group(fpdt_kobj, &suspend_attr_group);
+ if (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);
+ result = -EINVAL;
+ goto err;
+ }
+ if (record_boot) {
+ pr_err("Duplicate boot performance record found.\n");
+ continue;
+ }
+ record_boot = (struct boot_performance_record *)record_header;
+ result = sysfs_create_group(fpdt_kobj, &boot_attr_group);
+ if (result)
+ goto err;
+ break;
+
+ default:
+ /* Other types are reserved in ACPI 6.4 spec. */
+ break;
+ }
+ }
+ 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)
+{
+ acpi_status status;
+ 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);
+
+ if (ACPI_FAILURE(status))
+ return 0;
+
+ fpdt_kobj = kobject_create_and_add("fpdt", acpi_kobj);
+ if (!fpdt_kobj) {
+ result = -ENOMEM;
+ goto err_nomem;
+ }
+
+ while (offset < header->length) {
+ subtable = (void *)header + offset;
+ switch (subtable->type) {
+ case SUBTABLE_FBPT:
+ case SUBTABLE_S3PT:
+ result = fpdt_process_subtable(subtable->address,
+ subtable->type);
+ if (result)
+ goto err_subtable;
+ break;
+ default:
+ /* Other types are reserved in ACPI 6.4 spec. */
+ break;
+ }
+ 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
new file mode 100644
index 000000000..0555f68c2
--- /dev/null
+++ b/drivers/acpi/acpi_ipmi.c
@@ -0,0 +1,644 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * acpi_ipmi.c - ACPI IPMI opregion
+ *
+ * Copyright (C) 2010, 2013 Intel Corporation
+ * Author: Zhao Yakui <yakui.zhao@intel.com>
+ * Lv Zheng <lv.zheng@intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/ipmi.h>
+#include <linux/spinlock.h>
+
+MODULE_AUTHOR("Zhao Yakui");
+MODULE_DESCRIPTION("ACPI IPMI Opregion driver");
+MODULE_LICENSE("GPL");
+
+#define ACPI_IPMI_OK 0
+#define ACPI_IPMI_TIMEOUT 0x10
+#define ACPI_IPMI_UNKNOWN 0x07
+/* the IPMI timeout is 5s */
+#define IPMI_TIMEOUT (5000)
+#define ACPI_IPMI_MAX_MSG_LENGTH 64
+
+struct acpi_ipmi_device {
+ /* the device list attached to driver_data.ipmi_devices */
+ struct list_head head;
+
+ /* the IPMI request message list */
+ struct list_head tx_msg_list;
+
+ spinlock_t tx_msg_lock;
+ acpi_handle handle;
+ struct device *dev;
+ struct ipmi_user *user_interface;
+ int ipmi_ifnum; /* IPMI interface number */
+ long curr_msgid;
+ bool dead;
+ struct kref kref;
+};
+
+struct ipmi_driver_data {
+ struct list_head ipmi_devices;
+ struct ipmi_smi_watcher bmc_events;
+ const struct ipmi_user_hndl ipmi_hndlrs;
+ struct mutex ipmi_lock;
+
+ /*
+ * NOTE: IPMI System Interface Selection
+ * There is no system interface specified by the IPMI operation
+ * region access. We try to select one system interface with ACPI
+ * handle set. IPMI messages passed from the ACPI codes are sent
+ * to this selected global IPMI system interface.
+ */
+ struct acpi_ipmi_device *selected_smi;
+};
+
+struct acpi_ipmi_msg {
+ struct list_head head;
+
+ /*
+ * General speaking the addr type should be SI_ADDR_TYPE. And
+ * the addr channel should be BMC.
+ * In fact it can also be IPMB type. But we will have to
+ * parse it from the Netfn command buffer. It is so complex
+ * that it is skipped.
+ */
+ struct ipmi_addr addr;
+ long tx_msgid;
+
+ /* it is used to track whether the IPMI message is finished */
+ struct completion tx_complete;
+
+ struct kernel_ipmi_msg tx_message;
+ int msg_done;
+
+ /* tx/rx data . And copy it from/to ACPI object buffer */
+ u8 data[ACPI_IPMI_MAX_MSG_LENGTH];
+ u8 rx_len;
+
+ struct acpi_ipmi_device *device;
+ struct kref kref;
+};
+
+/* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */
+struct acpi_ipmi_buffer {
+ u8 status;
+ u8 length;
+ u8 data[ACPI_IPMI_MAX_MSG_LENGTH];
+};
+
+static void ipmi_register_bmc(int iface, struct device *dev);
+static void ipmi_bmc_gone(int iface);
+static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+
+static struct ipmi_driver_data driver_data = {
+ .ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices),
+ .bmc_events = {
+ .owner = THIS_MODULE,
+ .new_smi = ipmi_register_bmc,
+ .smi_gone = ipmi_bmc_gone,
+ },
+ .ipmi_hndlrs = {
+ .ipmi_recv_hndl = ipmi_msg_handler,
+ },
+ .ipmi_lock = __MUTEX_INITIALIZER(driver_data.ipmi_lock)
+};
+
+static struct acpi_ipmi_device *
+ipmi_dev_alloc(int iface, struct device *dev, acpi_handle handle)
+{
+ struct acpi_ipmi_device *ipmi_device;
+ int err;
+ struct ipmi_user *user;
+
+ ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
+ if (!ipmi_device)
+ return NULL;
+
+ kref_init(&ipmi_device->kref);
+ INIT_LIST_HEAD(&ipmi_device->head);
+ INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
+ spin_lock_init(&ipmi_device->tx_msg_lock);
+ ipmi_device->handle = handle;
+ ipmi_device->dev = get_device(dev);
+ ipmi_device->ipmi_ifnum = iface;
+
+ err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
+ ipmi_device, &user);
+ if (err) {
+ put_device(dev);
+ kfree(ipmi_device);
+ return NULL;
+ }
+ ipmi_device->user_interface = user;
+
+ return ipmi_device;
+}
+
+static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device)
+{
+ ipmi_destroy_user(ipmi_device->user_interface);
+ put_device(ipmi_device->dev);
+ kfree(ipmi_device);
+}
+
+static void ipmi_dev_release_kref(struct kref *kref)
+{
+ struct acpi_ipmi_device *ipmi =
+ container_of(kref, struct acpi_ipmi_device, kref);
+
+ ipmi_dev_release(ipmi);
+}
+
+static void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device)
+{
+ list_del(&ipmi_device->head);
+ if (driver_data.selected_smi == ipmi_device)
+ driver_data.selected_smi = NULL;
+
+ /*
+ * Always setting dead flag after deleting from the list or
+ * list_for_each_entry() codes must get changed.
+ */
+ ipmi_device->dead = true;
+}
+
+static struct acpi_ipmi_device *acpi_ipmi_dev_get(void)
+{
+ struct acpi_ipmi_device *ipmi_device = NULL;
+
+ mutex_lock(&driver_data.ipmi_lock);
+ if (driver_data.selected_smi) {
+ ipmi_device = driver_data.selected_smi;
+ kref_get(&ipmi_device->kref);
+ }
+ mutex_unlock(&driver_data.ipmi_lock);
+
+ return ipmi_device;
+}
+
+static void acpi_ipmi_dev_put(struct acpi_ipmi_device *ipmi_device)
+{
+ kref_put(&ipmi_device->kref, ipmi_dev_release_kref);
+}
+
+static struct acpi_ipmi_msg *ipmi_msg_alloc(void)
+{
+ struct acpi_ipmi_device *ipmi;
+ struct acpi_ipmi_msg *ipmi_msg;
+
+ ipmi = acpi_ipmi_dev_get();
+ if (!ipmi)
+ return NULL;
+
+ ipmi_msg = kzalloc(sizeof(struct acpi_ipmi_msg), GFP_KERNEL);
+ if (!ipmi_msg) {
+ acpi_ipmi_dev_put(ipmi);
+ return NULL;
+ }
+
+ kref_init(&ipmi_msg->kref);
+ init_completion(&ipmi_msg->tx_complete);
+ INIT_LIST_HEAD(&ipmi_msg->head);
+ ipmi_msg->device = ipmi;
+ ipmi_msg->msg_done = ACPI_IPMI_UNKNOWN;
+
+ return ipmi_msg;
+}
+
+static void ipmi_msg_release(struct acpi_ipmi_msg *tx_msg)
+{
+ acpi_ipmi_dev_put(tx_msg->device);
+ kfree(tx_msg);
+}
+
+static void ipmi_msg_release_kref(struct kref *kref)
+{
+ struct acpi_ipmi_msg *tx_msg =
+ container_of(kref, struct acpi_ipmi_msg, kref);
+
+ ipmi_msg_release(tx_msg);
+}
+
+static struct acpi_ipmi_msg *acpi_ipmi_msg_get(struct acpi_ipmi_msg *tx_msg)
+{
+ kref_get(&tx_msg->kref);
+
+ return tx_msg;
+}
+
+static void acpi_ipmi_msg_put(struct acpi_ipmi_msg *tx_msg)
+{
+ kref_put(&tx_msg->kref, ipmi_msg_release_kref);
+}
+
+#define IPMI_OP_RGN_NETFN(offset) ((offset >> 8) & 0xff)
+#define IPMI_OP_RGN_CMD(offset) (offset & 0xff)
+static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg,
+ acpi_physical_address address,
+ acpi_integer *value)
+{
+ struct kernel_ipmi_msg *msg;
+ struct acpi_ipmi_buffer *buffer;
+ struct acpi_ipmi_device *device;
+ unsigned long flags;
+
+ msg = &tx_msg->tx_message;
+
+ /*
+ * IPMI network function and command are encoded in the address
+ * within the IPMI OpRegion; see ACPI 4.0, sec 5.5.2.4.3.
+ */
+ msg->netfn = IPMI_OP_RGN_NETFN(address);
+ msg->cmd = IPMI_OP_RGN_CMD(address);
+ msg->data = tx_msg->data;
+
+ /*
+ * value is the parameter passed by the IPMI opregion space handler.
+ * It points to the IPMI request message buffer
+ */
+ buffer = (struct acpi_ipmi_buffer *)value;
+
+ /* copy the tx message data */
+ if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) {
+ dev_WARN_ONCE(tx_msg->device->dev, true,
+ "Unexpected request (msg len %d).\n",
+ buffer->length);
+ return -EINVAL;
+ }
+ msg->data_len = buffer->length;
+ memcpy(tx_msg->data, buffer->data, msg->data_len);
+
+ /*
+ * now the default type is SYSTEM_INTERFACE and channel type is BMC.
+ * If the netfn is APP_REQUEST and the cmd is SEND_MESSAGE,
+ * the addr type should be changed to IPMB. Then we will have to parse
+ * the IPMI request message buffer to get the IPMB address.
+ * If so, please fix me.
+ */
+ tx_msg->addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ tx_msg->addr.channel = IPMI_BMC_CHANNEL;
+ tx_msg->addr.data[0] = 0;
+
+ /* Get the msgid */
+ device = tx_msg->device;
+
+ spin_lock_irqsave(&device->tx_msg_lock, flags);
+ device->curr_msgid++;
+ tx_msg->tx_msgid = device->curr_msgid;
+ spin_unlock_irqrestore(&device->tx_msg_lock, flags);
+
+ return 0;
+}
+
+static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
+ acpi_integer *value)
+{
+ struct acpi_ipmi_buffer *buffer;
+
+ /*
+ * value is also used as output parameter. It represents the response
+ * IPMI message returned by IPMI command.
+ */
+ buffer = (struct acpi_ipmi_buffer *)value;
+
+ /*
+ * If the flag of msg_done is not set, it means that the IPMI command is
+ * not executed correctly.
+ */
+ buffer->status = msg->msg_done;
+ if (msg->msg_done != ACPI_IPMI_OK)
+ return;
+
+ /*
+ * If the IPMI response message is obtained correctly, the status code
+ * will be ACPI_IPMI_OK
+ */
+ buffer->length = msg->rx_len;
+ memcpy(buffer->data, msg->data, msg->rx_len);
+}
+
+static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi)
+{
+ struct acpi_ipmi_msg *tx_msg;
+ unsigned long flags;
+
+ /*
+ * NOTE: On-going ipmi_recv_msg
+ * ipmi_msg_handler() may still be invoked by ipmi_si after
+ * flushing. But it is safe to do a fast flushing on module_exit()
+ * without waiting for all ipmi_recv_msg(s) to complete from
+ * ipmi_msg_handler() as it is ensured by ipmi_si that all
+ * ipmi_recv_msg(s) are freed after invoking ipmi_destroy_user().
+ */
+ spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+ while (!list_empty(&ipmi->tx_msg_list)) {
+ tx_msg = list_first_entry(&ipmi->tx_msg_list,
+ struct acpi_ipmi_msg,
+ head);
+ list_del(&tx_msg->head);
+ spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
+ /* wake up the sleep thread on the Tx msg */
+ complete(&tx_msg->tx_complete);
+ acpi_ipmi_msg_put(tx_msg);
+ spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+ }
+ spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+}
+
+static void ipmi_cancel_tx_msg(struct acpi_ipmi_device *ipmi,
+ struct acpi_ipmi_msg *msg)
+{
+ struct acpi_ipmi_msg *tx_msg = NULL, *iter, *temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipmi->tx_msg_lock, flags);
+ list_for_each_entry_safe(iter, temp, &ipmi->tx_msg_list, head) {
+ if (msg == iter) {
+ tx_msg = iter;
+ list_del(&iter->head);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags);
+
+ if (tx_msg)
+ acpi_ipmi_msg_put(tx_msg);
+}
+
+static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+ struct acpi_ipmi_device *ipmi_device = user_msg_data;
+ struct acpi_ipmi_msg *tx_msg = NULL, *iter, *temp;
+ struct device *dev = ipmi_device->dev;
+ unsigned long flags;
+
+ if (msg->user != ipmi_device->user_interface) {
+ dev_warn(dev,
+ "Unexpected response is returned. returned user %p, expected user %p\n",
+ msg->user, ipmi_device->user_interface);
+ goto out_msg;
+ }
+
+ spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
+ list_for_each_entry_safe(iter, temp, &ipmi_device->tx_msg_list, head) {
+ if (msg->msgid == iter->tx_msgid) {
+ tx_msg = iter;
+ list_del(&iter->head);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
+
+ if (!tx_msg) {
+ dev_warn(dev,
+ "Unexpected response (msg id %ld) is returned.\n",
+ msg->msgid);
+ goto out_msg;
+ }
+
+ /* copy the response data to Rx_data buffer */
+ if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) {
+ dev_WARN_ONCE(dev, true,
+ "Unexpected response (msg len %d).\n",
+ msg->msg.data_len);
+ goto out_comp;
+ }
+
+ /* response msg is an error msg */
+ msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
+ if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE &&
+ msg->msg.data_len == 1) {
+ if (msg->msg.data[0] == IPMI_TIMEOUT_COMPLETION_CODE) {
+ dev_dbg_once(dev, "Unexpected response (timeout).\n");
+ tx_msg->msg_done = ACPI_IPMI_TIMEOUT;
+ }
+ goto out_comp;
+ }
+
+ tx_msg->rx_len = msg->msg.data_len;
+ memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len);
+ tx_msg->msg_done = ACPI_IPMI_OK;
+
+out_comp:
+ complete(&tx_msg->tx_complete);
+ acpi_ipmi_msg_put(tx_msg);
+out_msg:
+ ipmi_free_recv_msg(msg);
+}
+
+static void ipmi_register_bmc(int iface, struct device *dev)
+{
+ struct acpi_ipmi_device *ipmi_device, *temp;
+ int err;
+ struct ipmi_smi_info smi_data;
+ acpi_handle handle;
+
+ err = ipmi_get_smi_info(iface, &smi_data);
+ if (err)
+ return;
+
+ if (smi_data.addr_src != SI_ACPI)
+ goto err_ref;
+ handle = smi_data.addr_info.acpi_info.acpi_handle;
+ if (!handle)
+ goto err_ref;
+
+ ipmi_device = ipmi_dev_alloc(iface, smi_data.dev, handle);
+ if (!ipmi_device) {
+ dev_warn(smi_data.dev, "Can't create IPMI user interface\n");
+ goto err_ref;
+ }
+
+ mutex_lock(&driver_data.ipmi_lock);
+ list_for_each_entry(temp, &driver_data.ipmi_devices, head) {
+ /*
+ * if the corresponding ACPI handle is already added
+ * to the device list, don't add it again.
+ */
+ if (temp->handle == handle)
+ goto err_lock;
+ }
+ if (!driver_data.selected_smi)
+ driver_data.selected_smi = ipmi_device;
+ list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
+ mutex_unlock(&driver_data.ipmi_lock);
+
+ put_device(smi_data.dev);
+ return;
+
+err_lock:
+ mutex_unlock(&driver_data.ipmi_lock);
+ ipmi_dev_release(ipmi_device);
+err_ref:
+ put_device(smi_data.dev);
+}
+
+static void ipmi_bmc_gone(int iface)
+{
+ struct acpi_ipmi_device *ipmi_device = NULL, *iter, *temp;
+
+ mutex_lock(&driver_data.ipmi_lock);
+ list_for_each_entry_safe(iter, temp,
+ &driver_data.ipmi_devices, head) {
+ if (iter->ipmi_ifnum != iface) {
+ ipmi_device = iter;
+ __ipmi_dev_kill(iter);
+ break;
+ }
+ }
+ if (!driver_data.selected_smi)
+ driver_data.selected_smi = list_first_entry_or_null(
+ &driver_data.ipmi_devices,
+ struct acpi_ipmi_device, head);
+ mutex_unlock(&driver_data.ipmi_lock);
+
+ if (ipmi_device) {
+ ipmi_flush_tx_msg(ipmi_device);
+ acpi_ipmi_dev_put(ipmi_device);
+ }
+}
+
+/*
+ * This is the IPMI opregion space handler.
+ * @function: indicates the read/write. In fact as the IPMI message is driven
+ * by command, only write is meaningful.
+ * @address: This contains the netfn/command of IPMI request message.
+ * @bits : not used.
+ * @value : it is an in/out parameter. It points to the IPMI message buffer.
+ * Before the IPMI message is sent, it represents the actual request
+ * IPMI message. After the IPMI message is finished, it represents
+ * the response IPMI message returned by IPMI command.
+ * @handler_context: IPMI device context.
+ */
+static acpi_status
+acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
+ u32 bits, acpi_integer *value,
+ void *handler_context, void *region_context)
+{
+ struct acpi_ipmi_msg *tx_msg;
+ struct acpi_ipmi_device *ipmi_device;
+ int err;
+ acpi_status status;
+ unsigned long flags;
+
+ /*
+ * IPMI opregion message.
+ * IPMI message is firstly written to the BMC and system software
+ * can get the respsonse. So it is unmeaningful for the read access
+ * of IPMI opregion.
+ */
+ if ((function & ACPI_IO_MASK) == ACPI_READ)
+ return AE_TYPE;
+
+ tx_msg = ipmi_msg_alloc();
+ if (!tx_msg)
+ return AE_NOT_EXIST;
+ ipmi_device = tx_msg->device;
+
+ if (acpi_format_ipmi_request(tx_msg, address, value) != 0) {
+ ipmi_msg_release(tx_msg);
+ return AE_TYPE;
+ }
+
+ acpi_ipmi_msg_get(tx_msg);
+ mutex_lock(&driver_data.ipmi_lock);
+ /* Do not add a tx_msg that can not be flushed. */
+ if (ipmi_device->dead) {
+ mutex_unlock(&driver_data.ipmi_lock);
+ ipmi_msg_release(tx_msg);
+ return AE_NOT_EXIST;
+ }
+ spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
+ list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
+ spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
+ mutex_unlock(&driver_data.ipmi_lock);
+
+ err = ipmi_request_settime(ipmi_device->user_interface,
+ &tx_msg->addr,
+ tx_msg->tx_msgid,
+ &tx_msg->tx_message,
+ NULL, 0, 0, IPMI_TIMEOUT);
+ if (err) {
+ status = AE_ERROR;
+ goto out_msg;
+ }
+ wait_for_completion(&tx_msg->tx_complete);
+
+ acpi_format_ipmi_response(tx_msg, value);
+ status = AE_OK;
+
+out_msg:
+ ipmi_cancel_tx_msg(ipmi_device, tx_msg);
+ acpi_ipmi_msg_put(tx_msg);
+ return status;
+}
+
+static int __init acpi_ipmi_init(void)
+{
+ int result;
+ acpi_status status;
+
+ if (acpi_disabled)
+ return 0;
+
+ status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_IPMI,
+ &acpi_ipmi_space_handler,
+ NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ pr_warn("Can't register IPMI opregion space handle\n");
+ return -EINVAL;
+ }
+
+ result = ipmi_smi_watcher_register(&driver_data.bmc_events);
+ if (result) {
+ acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_IPMI,
+ &acpi_ipmi_space_handler);
+ pr_err("Can't register IPMI system interface watcher\n");
+ }
+
+ return result;
+}
+
+static void __exit acpi_ipmi_exit(void)
+{
+ struct acpi_ipmi_device *ipmi_device;
+
+ if (acpi_disabled)
+ return;
+
+ ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+
+ /*
+ * When one smi_watcher is unregistered, it is only deleted
+ * from the smi_watcher list. But the smi_gone callback function
+ * is not called. So explicitly uninstall the ACPI IPMI oregion
+ * handler and free it.
+ */
+ mutex_lock(&driver_data.ipmi_lock);
+ while (!list_empty(&driver_data.ipmi_devices)) {
+ ipmi_device = list_first_entry(&driver_data.ipmi_devices,
+ struct acpi_ipmi_device,
+ head);
+ __ipmi_dev_kill(ipmi_device);
+ mutex_unlock(&driver_data.ipmi_lock);
+
+ ipmi_flush_tx_msg(ipmi_device);
+ acpi_ipmi_dev_put(ipmi_device);
+
+ mutex_lock(&driver_data.ipmi_lock);
+ }
+ mutex_unlock(&driver_data.ipmi_lock);
+ acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_IPMI,
+ &acpi_ipmi_space_handler);
+}
+
+module_init(acpi_ipmi_init);
+module_exit(acpi_ipmi_exit);
diff --git a/drivers/acpi/acpi_lpat.c b/drivers/acpi/acpi_lpat.c
new file mode 100644
index 000000000..851f67c96
--- /dev/null
+++ b/drivers/acpi/acpi_lpat.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * acpi_lpat.c - LPAT table processing functions
+ *
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/export.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_lpat.h>
+
+/**
+ * acpi_lpat_raw_to_temp(): Return temperature from raw value through
+ * LPAT conversion table
+ *
+ * @lpat_table: the temperature_raw mapping table structure
+ * @raw: the raw value, used as a key to get the temperature from the
+ * above mapping table
+ *
+ * A positive converted temperature value will be returned on success,
+ * a negative errno will be returned in error cases.
+ */
+int acpi_lpat_raw_to_temp(struct acpi_lpat_conversion_table *lpat_table,
+ int raw)
+{
+ int i, delta_temp, delta_raw, temp;
+ struct acpi_lpat *lpat = lpat_table->lpat;
+
+ for (i = 0; i < lpat_table->lpat_count - 1; i++) {
+ if ((raw >= lpat[i].raw && raw <= lpat[i+1].raw) ||
+ (raw <= lpat[i].raw && raw >= lpat[i+1].raw))
+ break;
+ }
+
+ if (i == lpat_table->lpat_count - 1)
+ return -ENOENT;
+
+ delta_temp = lpat[i+1].temp - lpat[i].temp;
+ delta_raw = lpat[i+1].raw - lpat[i].raw;
+ temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw;
+
+ return temp;
+}
+EXPORT_SYMBOL_GPL(acpi_lpat_raw_to_temp);
+
+/**
+ * acpi_lpat_temp_to_raw(): Return raw value from temperature through
+ * LPAT conversion table
+ *
+ * @lpat_table: the temperature_raw mapping table
+ * @temp: the temperature, used as a key to get the raw value from the
+ * above mapping table
+ *
+ * The raw value will be returned on success,
+ * a negative errno will be returned in error cases.
+ */
+int acpi_lpat_temp_to_raw(struct acpi_lpat_conversion_table *lpat_table,
+ int temp)
+{
+ int i, delta_temp, delta_raw, raw;
+ struct acpi_lpat *lpat = lpat_table->lpat;
+
+ for (i = 0; i < lpat_table->lpat_count - 1; i++) {
+ if (temp >= lpat[i].temp && temp <= lpat[i+1].temp)
+ break;
+ }
+
+ if (i == lpat_table->lpat_count - 1)
+ return -ENOENT;
+
+ delta_temp = lpat[i+1].temp - lpat[i].temp;
+ delta_raw = lpat[i+1].raw - lpat[i].raw;
+ raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp;
+
+ return raw;
+}
+EXPORT_SYMBOL_GPL(acpi_lpat_temp_to_raw);
+
+/**
+ * acpi_lpat_get_conversion_table(): Parse ACPI LPAT table if present.
+ *
+ * @handle: Handle to acpi device
+ *
+ * Parse LPAT table to a struct of type acpi_lpat_table. On success
+ * it returns a pointer to newly allocated table. This table must
+ * be freed by the caller when finished processing, using a call to
+ * acpi_lpat_free_conversion_table.
+ */
+struct acpi_lpat_conversion_table *acpi_lpat_get_conversion_table(acpi_handle
+ handle)
+{
+ struct acpi_lpat_conversion_table *lpat_table = NULL;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj_p, *obj_e;
+ int *lpat, i;
+ acpi_status status;
+
+ status = acpi_evaluate_object(handle, "LPAT", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return NULL;
+
+ obj_p = (union acpi_object *)buffer.pointer;
+ if (!obj_p || (obj_p->type != ACPI_TYPE_PACKAGE) ||
+ (obj_p->package.count % 2) || (obj_p->package.count < 4))
+ goto out;
+
+ lpat = kcalloc(obj_p->package.count, sizeof(int), GFP_KERNEL);
+ if (!lpat)
+ goto out;
+
+ for (i = 0; i < obj_p->package.count; i++) {
+ obj_e = &obj_p->package.elements[i];
+ if (obj_e->type != ACPI_TYPE_INTEGER) {
+ kfree(lpat);
+ goto out;
+ }
+ lpat[i] = (s64)obj_e->integer.value;
+ }
+
+ lpat_table = kzalloc(sizeof(*lpat_table), GFP_KERNEL);
+ if (!lpat_table) {
+ kfree(lpat);
+ goto out;
+ }
+
+ lpat_table->lpat = (struct acpi_lpat *)lpat;
+ lpat_table->lpat_count = obj_p->package.count / 2;
+
+out:
+ kfree(buffer.pointer);
+ return lpat_table;
+}
+EXPORT_SYMBOL_GPL(acpi_lpat_get_conversion_table);
+
+/**
+ * acpi_lpat_free_conversion_table(): Free LPAT table.
+ *
+ * @lpat_table: the temperature_raw mapping table structure
+ *
+ * Frees the LPAT table previously allocated by a call to
+ * acpi_lpat_get_conversion_table.
+ */
+void acpi_lpat_free_conversion_table(struct acpi_lpat_conversion_table
+ *lpat_table)
+{
+ if (lpat_table) {
+ kfree(lpat_table->lpat);
+ kfree(lpat_table);
+ }
+}
+EXPORT_SYMBOL_GPL(acpi_lpat_free_conversion_table);
diff --git a/drivers/acpi/acpi_lpit.c b/drivers/acpi/acpi_lpit.c
new file mode 100644
index 000000000..2c015ecf7
--- /dev/null
+++ b/drivers/acpi/acpi_lpit.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * acpi_lpit.c - LPIT table processing functions
+ *
+ * Copyright (C) 2017 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/cpu.h>
+#include <linux/acpi.h>
+#include <asm/msr.h>
+#include <asm/tsc.h>
+
+struct lpit_residency_info {
+ struct acpi_generic_address gaddr;
+ u64 frequency;
+ void __iomem *iomem_addr;
+};
+
+/* Storage for an memory mapped and FFH based entries */
+static struct lpit_residency_info residency_info_mem;
+static struct lpit_residency_info residency_info_ffh;
+
+static int lpit_read_residency_counter_us(u64 *counter, bool io_mem)
+{
+ int err;
+
+ if (io_mem) {
+ u64 count = 0;
+ int error;
+
+ error = acpi_os_read_iomem(residency_info_mem.iomem_addr, &count,
+ residency_info_mem.gaddr.bit_width);
+ if (error)
+ return error;
+
+ *counter = div64_u64(count * 1000000ULL, residency_info_mem.frequency);
+ return 0;
+ }
+
+ err = rdmsrl_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,
+ residency_info_ffh.gaddr.bit_offset);
+
+ *counter &= mask;
+ *counter >>= residency_info_ffh.gaddr.bit_offset;
+ *counter = div64_u64(*counter * 1000000ULL, residency_info_ffh.frequency);
+ return 0;
+ }
+
+ return -ENODATA;
+}
+
+static ssize_t low_power_idle_system_residency_us_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u64 counter;
+ int ret;
+
+ ret = lpit_read_residency_counter_us(&counter, true);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%llu\n", counter);
+}
+static DEVICE_ATTR_RO(low_power_idle_system_residency_us);
+
+static ssize_t low_power_idle_cpu_residency_us_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u64 counter;
+ int ret;
+
+ ret = lpit_read_residency_counter_us(&counter, false);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%llu\n", counter);
+}
+static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us);
+
+int lpit_read_residency_count_address(u64 *address)
+{
+ if (!residency_info_mem.gaddr.address)
+ return -EINVAL;
+
+ *address = residency_info_mem.gaddr.address;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lpit_read_residency_count_address);
+
+static void lpit_update_residency(struct lpit_residency_info *info,
+ struct acpi_lpit_native *lpit_native)
+{
+ info->frequency = lpit_native->counter_frequency ?
+ lpit_native->counter_frequency : mul_u32_u32(tsc_khz, 1000U);
+ if (!info->frequency)
+ info->frequency = 1;
+
+ info->gaddr = lpit_native->residency_counter;
+ if (info->gaddr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ info->iomem_addr = ioremap(info->gaddr.address,
+ info->gaddr.bit_width / 8);
+ if (!info->iomem_addr)
+ return;
+
+ /* Silently fail, if cpuidle attribute group is not present */
+ sysfs_add_file_to_group(&cpu_subsys.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,
+ &dev_attr_low_power_idle_cpu_residency_us.attr,
+ "cpuidle");
+ }
+}
+
+static void lpit_process(u64 begin, u64 end)
+{
+ while (begin + sizeof(struct acpi_lpit_native) <= end) {
+ struct acpi_lpit_native *lpit_native = (struct acpi_lpit_native *)begin;
+
+ if (!lpit_native->header.type && !lpit_native->header.flags) {
+ if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY &&
+ !residency_info_mem.gaddr.address) {
+ lpit_update_residency(&residency_info_mem, lpit_native);
+ } else if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE &&
+ !residency_info_ffh.gaddr.address) {
+ lpit_update_residency(&residency_info_ffh, lpit_native);
+ }
+ }
+ begin += lpit_native->header.length;
+ }
+}
+
+void acpi_init_lpit(void)
+{
+ acpi_status status;
+ struct acpi_table_lpit *lpit;
+
+ status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit);
+ if (ACPI_FAILURE(status))
+ return;
+
+ lpit_process((u64)lpit + sizeof(*lpit),
+ (u64)lpit + lpit->header.length);
+
+ acpi_put_table((struct acpi_table_header *)lpit);
+}
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
new file mode 100644
index 000000000..8b4474394
--- /dev/null
+++ b/drivers/acpi/acpi_lpss.c
@@ -0,0 +1,1378 @@
+// 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)
+{
+ u64 uid;
+
+ /* Only call pwm_add_table for the first PWM controller */
+ if (acpi_dev_uid_to_integer(pdata->adev, &uid) || uid != 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)
+{
+ acpi_handle handle = pdata->adev->handle;
+ unsigned long long shared_host = 0;
+ acpi_status status;
+ u64 uid;
+
+ /* Expected to always be successfull, 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 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)
+{
+ u64 uid;
+
+ /* Only call pwm_add_table for the first PWM controller */
+ if (acpi_dev_uid_to_integer(pdata->adev, &uid) || uid != 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
+
+/* 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 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_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);
+
+#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
new file mode 100644
index 000000000..24f662d8b
--- /dev/null
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2004, 2013 Intel Corporation
+ * Author: Naveen B S <naveen.b.s@intel.com>
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *
+ * All rights reserved.
+ *
+ * ACPI based HotPlug driver that supports Memory Hotplug
+ * This driver fields notifications from firmware for memory add
+ * and remove operations and alerts the VM of the affected memory
+ * ranges.
+ */
+
+#include <linux/acpi.h>
+#include <linux/memory.h>
+#include <linux/memory_hotplug.h>
+
+#include "internal.h"
+
+#define ACPI_MEMORY_DEVICE_CLASS "memory"
+#define ACPI_MEMORY_DEVICE_HID "PNP0C80"
+#define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device"
+
+static const struct acpi_device_id memory_device_ids[] = {
+ {ACPI_MEMORY_DEVICE_HID, 0},
+ {"", 0},
+};
+
+#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
+
+static int acpi_memory_device_add(struct acpi_device *device,
+ const struct acpi_device_id *not_used);
+static void acpi_memory_device_remove(struct acpi_device *device);
+
+static struct acpi_scan_handler memory_device_handler = {
+ .ids = memory_device_ids,
+ .attach = acpi_memory_device_add,
+ .detach = acpi_memory_device_remove,
+ .hotplug = {
+ .enabled = true,
+ },
+};
+
+struct acpi_memory_info {
+ struct list_head list;
+ u64 start_addr; /* Memory Range start physical addr */
+ u64 length; /* Memory Range length */
+ unsigned short caching; /* memory cache attribute */
+ unsigned short write_protect; /* memory read/write attribute */
+ unsigned int enabled:1;
+};
+
+struct acpi_memory_device {
+ struct acpi_device *device;
+ struct list_head res_list;
+ int mgid;
+};
+
+static acpi_status
+acpi_memory_get_resource(struct acpi_resource *resource, void *context)
+{
+ struct acpi_memory_device *mem_device = context;
+ struct acpi_resource_address64 address64;
+ struct acpi_memory_info *info, *new;
+ acpi_status status;
+
+ status = acpi_resource_to_address64(resource, &address64);
+ if (ACPI_FAILURE(status) ||
+ (address64.resource_type != ACPI_MEMORY_RANGE))
+ return AE_OK;
+
+ list_for_each_entry(info, &mem_device->res_list, list) {
+ /* Can we combine the resource range information? */
+ if ((info->caching == address64.info.mem.caching) &&
+ (info->write_protect == address64.info.mem.write_protect) &&
+ (info->start_addr + info->length == address64.address.minimum)) {
+ info->length += address64.address.address_length;
+ return AE_OK;
+ }
+ }
+
+ new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
+ if (!new)
+ return AE_ERROR;
+
+ INIT_LIST_HEAD(&new->list);
+ new->caching = address64.info.mem.caching;
+ new->write_protect = address64.info.mem.write_protect;
+ new->start_addr = address64.address.minimum;
+ new->length = address64.address.address_length;
+ list_add_tail(&new->list, &mem_device->res_list);
+
+ return AE_OK;
+}
+
+static void
+acpi_memory_free_device_resources(struct acpi_memory_device *mem_device)
+{
+ struct acpi_memory_info *info, *n;
+
+ list_for_each_entry_safe(info, n, &mem_device->res_list, list)
+ kfree(info);
+ INIT_LIST_HEAD(&mem_device->res_list);
+}
+
+static int
+acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
+{
+ acpi_status status;
+
+ if (!list_empty(&mem_device->res_list))
+ return 0;
+
+ status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,
+ acpi_memory_get_resource, mem_device);
+ if (ACPI_FAILURE(status)) {
+ acpi_memory_free_device_resources(mem_device);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
+{
+ unsigned long long current_status;
+
+ /* Get device present/absent information from the _STA */
+ if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle,
+ METHOD_NAME__STA, NULL,
+ &current_status)))
+ return -ENODEV;
+ /*
+ * Check for device status. Device should be
+ * present/enabled/functioning.
+ */
+ if (!((current_status & ACPI_STA_DEVICE_PRESENT)
+ && (current_status & ACPI_STA_DEVICE_ENABLED)
+ && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
+ return -ENODEV;
+
+ return 0;
+}
+
+static int acpi_bind_memblk(struct memory_block *mem, void *arg)
+{
+ return acpi_bind_one(&mem->dev, arg);
+}
+
+static int acpi_bind_memory_blocks(struct acpi_memory_info *info,
+ struct acpi_device *adev)
+{
+ return walk_memory_blocks(info->start_addr, info->length, adev,
+ acpi_bind_memblk);
+}
+
+static int acpi_unbind_memblk(struct memory_block *mem, void *arg)
+{
+ acpi_unbind_one(&mem->dev);
+ return 0;
+}
+
+static void acpi_unbind_memory_blocks(struct acpi_memory_info *info)
+{
+ walk_memory_blocks(info->start_addr, info->length, NULL,
+ acpi_unbind_memblk);
+}
+
+static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
+{
+ acpi_handle handle = mem_device->device->handle;
+ mhp_t mhp_flags = MHP_NID_IS_MGID;
+ int result, num_enabled = 0;
+ struct acpi_memory_info *info;
+ u64 total_length = 0;
+ int node, mgid;
+
+ node = acpi_get_node(handle);
+
+ list_for_each_entry(info, &mem_device->res_list, list) {
+ if (!info->length)
+ continue;
+ /* We want a single node for the whole memory group */
+ if (node < 0)
+ node = memory_add_physaddr_to_nid(info->start_addr);
+ total_length += info->length;
+ }
+
+ if (!total_length) {
+ dev_err(&mem_device->device->dev, "device is empty\n");
+ return -EINVAL;
+ }
+
+ mgid = memory_group_register_static(node, PFN_UP(total_length));
+ if (mgid < 0)
+ return mgid;
+ mem_device->mgid = mgid;
+
+ /*
+ * Tell the VM there is more memory here...
+ * Note: Assume that this function returns zero on success
+ * We don't have memory-hot-add rollback function,now.
+ * (i.e. memory-hot-remove function)
+ */
+ list_for_each_entry(info, &mem_device->res_list, list) {
+ /*
+ * If the memory block size is zero, please ignore it.
+ * Don't try to do the following memory hotplug flowchart.
+ */
+ if (!info->length)
+ continue;
+
+ if (mhp_supports_memmap_on_memory(info->length))
+ mhp_flags |= MHP_MEMMAP_ON_MEMORY;
+ result = __add_memory(mgid, info->start_addr, info->length,
+ mhp_flags);
+
+ /*
+ * If the memory block has been used by the kernel, add_memory()
+ * returns -EEXIST. If add_memory() returns the other error, it
+ * means that this memory block is not used by the kernel.
+ */
+ if (result && result != -EEXIST)
+ continue;
+
+ result = acpi_bind_memory_blocks(info, mem_device->device);
+ if (result) {
+ acpi_unbind_memory_blocks(info);
+ return -ENODEV;
+ }
+
+ info->enabled = 1;
+
+ /*
+ * Add num_enable even if add_memory() returns -EEXIST, so the
+ * device is bound to this driver.
+ */
+ num_enabled++;
+ }
+ if (!num_enabled) {
+ dev_err(&mem_device->device->dev, "add_memory failed\n");
+ return -EINVAL;
+ }
+ /*
+ * Sometimes the memory device will contain several memory blocks.
+ * When one memory block is hot-added to the system memory, it will
+ * be regarded as a success.
+ * Otherwise if the last memory block can't be hot-added to the system
+ * memory, it will be failure and the memory device can't be bound with
+ * driver.
+ */
+ return 0;
+}
+
+static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
+{
+ struct acpi_memory_info *info, *n;
+
+ list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
+ if (!info->enabled)
+ continue;
+
+ acpi_unbind_memory_blocks(info);
+ __remove_memory(info->start_addr, info->length);
+ list_del(&info->list);
+ kfree(info);
+ }
+}
+
+static void acpi_memory_device_free(struct acpi_memory_device *mem_device)
+{
+ if (!mem_device)
+ return;
+
+ /* In case we succeeded adding *some* memory, unregistering fails. */
+ if (mem_device->mgid >= 0)
+ memory_group_unregister(mem_device->mgid);
+
+ acpi_memory_free_device_resources(mem_device);
+ mem_device->device->driver_data = NULL;
+ kfree(mem_device);
+}
+
+static int acpi_memory_device_add(struct acpi_device *device,
+ const struct acpi_device_id *not_used)
+{
+ struct acpi_memory_device *mem_device;
+ int result;
+
+ if (!device)
+ return -EINVAL;
+
+ mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
+ if (!mem_device)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&mem_device->res_list);
+ mem_device->device = device;
+ mem_device->mgid = -1;
+ sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
+ sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
+ device->driver_data = mem_device;
+
+ /* Get the range from the _CRS */
+ result = acpi_memory_get_device_resources(mem_device);
+ if (result) {
+ device->driver_data = NULL;
+ kfree(mem_device);
+ return result;
+ }
+
+ result = acpi_memory_check_device(mem_device);
+ if (result) {
+ acpi_memory_device_free(mem_device);
+ return 0;
+ }
+
+ result = acpi_memory_enable_device(mem_device);
+ if (result) {
+ dev_err(&device->dev, "acpi_memory_enable_device() error\n");
+ acpi_memory_device_free(mem_device);
+ return result;
+ }
+
+ dev_dbg(&device->dev, "Memory device configured by ACPI\n");
+ return 1;
+}
+
+static void acpi_memory_device_remove(struct acpi_device *device)
+{
+ struct acpi_memory_device *mem_device;
+
+ if (!device || !acpi_driver_data(device))
+ return;
+
+ mem_device = acpi_driver_data(device);
+ acpi_memory_remove_memory(mem_device);
+ acpi_memory_device_free(mem_device);
+}
+
+static bool __initdata acpi_no_memhotplug;
+
+void __init acpi_memory_hotplug_init(void)
+{
+ if (acpi_no_memhotplug) {
+ memory_device_handler.attach = NULL;
+ acpi_scan_add_handler(&memory_device_handler);
+ return;
+ }
+ acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory");
+}
+
+static int __init disable_acpi_memory_hotplug(char *str)
+{
+ acpi_no_memhotplug = true;
+ return 1;
+}
+__setup("acpi_no_memhotplug", disable_acpi_memory_hotplug);
+
+#else
+
+static struct acpi_scan_handler memory_device_handler = {
+ .ids = memory_device_ids,
+};
+
+void __init acpi_memory_hotplug_init(void)
+{
+ acpi_scan_add_handler(&memory_device_handler);
+}
+
+#endif /* CONFIG_ACPI_HOTPLUG_MEMORY */
diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c
new file mode 100644
index 000000000..ec0e22a1e
--- /dev/null
+++ b/drivers/acpi/acpi_pad.c
@@ -0,0 +1,502 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * acpi_pad.c ACPI Processor Aggregator Driver
+ *
+ * Copyright (c) 2009, Intel Corporation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpumask.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kthread.h>
+#include <uapi/linux/sched/types.h>
+#include <linux/freezer.h>
+#include <linux/cpu.h>
+#include <linux/tick.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/perf_event.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
+static DEFINE_MUTEX(isolated_cpus_lock);
+static DEFINE_MUTEX(round_robin_lock);
+
+static unsigned long power_saving_mwait_eax;
+
+static unsigned char tsc_detected_unstable;
+static unsigned char tsc_marked_unstable;
+
+static void power_saving_mwait_init(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+ unsigned int highest_cstate = 0;
+ unsigned int highest_subcstate = 0;
+ int i;
+
+ 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);
+
+ if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) ||
+ !(ecx & CPUID5_ECX_INTERRUPT_BREAK))
+ return;
+
+ edx >>= MWAIT_SUBSTATE_SIZE;
+ for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) {
+ if (edx & MWAIT_SUBSTATE_MASK) {
+ highest_cstate = i;
+ highest_subcstate = edx & MWAIT_SUBSTATE_MASK;
+ }
+ }
+ power_saving_mwait_eax = (highest_cstate << MWAIT_SUBSTATE_SIZE) |
+ (highest_subcstate - 1);
+
+#if defined(CONFIG_X86)
+ switch (boot_cpu_data.x86_vendor) {
+ case X86_VENDOR_HYGON:
+ case X86_VENDOR_AMD:
+ case X86_VENDOR_INTEL:
+ case X86_VENDOR_ZHAOXIN:
+ /*
+ * AMD Fam10h TSC will tick in all
+ * C/P/S0/S1 states when this bit is set.
+ */
+ if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
+ tsc_detected_unstable = 1;
+ break;
+ default:
+ /* TSC could halt in idle */
+ tsc_detected_unstable = 1;
+ }
+#endif
+}
+
+static unsigned long cpu_weight[NR_CPUS];
+static int tsk_in_cpu[NR_CPUS] = {[0 ... NR_CPUS-1] = -1};
+static DECLARE_BITMAP(pad_busy_cpus_bits, NR_CPUS);
+static void round_robin_cpu(unsigned int tsk_index)
+{
+ struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits);
+ cpumask_var_t tmp;
+ int cpu;
+ unsigned long min_weight = -1;
+ unsigned long preferred_cpu;
+
+ if (!alloc_cpumask_var(&tmp, GFP_KERNEL))
+ return;
+
+ mutex_lock(&round_robin_lock);
+ cpumask_clear(tmp);
+ 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 */
+ if (cpumask_empty(tmp))
+ cpumask_andnot(tmp, cpu_online_mask, pad_busy_cpus);
+ if (cpumask_empty(tmp)) {
+ mutex_unlock(&round_robin_lock);
+ free_cpumask_var(tmp);
+ return;
+ }
+ for_each_cpu(cpu, tmp) {
+ if (cpu_weight[cpu] < min_weight) {
+ min_weight = cpu_weight[cpu];
+ preferred_cpu = cpu;
+ }
+ }
+
+ if (tsk_in_cpu[tsk_index] != -1)
+ cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus);
+ tsk_in_cpu[tsk_index] = preferred_cpu;
+ cpumask_set_cpu(preferred_cpu, pad_busy_cpus);
+ cpu_weight[preferred_cpu]++;
+ mutex_unlock(&round_robin_lock);
+
+ set_cpus_allowed_ptr(current, cpumask_of(preferred_cpu));
+
+ free_cpumask_var(tmp);
+}
+
+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;
+}
+
+static unsigned int idle_pct = 5; /* percentage */
+static unsigned int round_robin_time = 1; /* second */
+static int power_saving_thread(void *data)
+{
+ int do_sleep;
+ unsigned int tsk_index = (unsigned long)data;
+ u64 last_jiffies = 0;
+
+ sched_set_fifo_low(current);
+
+ while (!kthread_should_stop()) {
+ unsigned long expire_time;
+
+ /* round robin to cpus */
+ expire_time = last_jiffies + round_robin_time * HZ;
+ if (time_before(expire_time, jiffies)) {
+ last_jiffies = jiffies;
+ round_robin_cpu(tsk_index);
+ }
+
+ do_sleep = 0;
+
+ expire_time = jiffies + HZ * (100 - idle_pct) / 100;
+
+ while (!need_resched()) {
+ if (tsc_detected_unstable && !tsc_marked_unstable) {
+ /* TSC could halt in idle, so notify users */
+ mark_tsc_unstable("TSC halts in idle");
+ tsc_marked_unstable = 1;
+ }
+ local_irq_disable();
+
+ perf_lopwr_cb(true);
+
+ tick_broadcast_enable();
+ tick_broadcast_enter();
+ stop_critical_timings();
+
+ mwait_idle_with_hints(power_saving_mwait_eax, 1);
+
+ start_critical_timings();
+ tick_broadcast_exit();
+
+ perf_lopwr_cb(false);
+
+ local_irq_enable();
+
+ if (time_before(expire_time, jiffies)) {
+ do_sleep = 1;
+ break;
+ }
+ }
+
+ /*
+ * current sched_rt has threshold for rt task running time.
+ * When a rt task uses 95% CPU time, the rt thread will be
+ * scheduled out for 5% CPU time to not starve other tasks. But
+ * the mechanism only works when all CPUs have RT task running,
+ * as if one CPU hasn't RT task, RT task from other CPUs will
+ * borrow CPU time from this CPU and cause RT task use > 95%
+ * CPU time. To make 'avoid starvation' work, takes a nap here.
+ */
+ if (unlikely(do_sleep))
+ schedule_timeout_killable(HZ * idle_pct / 100);
+
+ /* If an external event has set the need_resched flag, then
+ * we need to deal with it, or this loop will continue to
+ * spin without calling __mwait().
+ */
+ if (unlikely(need_resched()))
+ schedule();
+ }
+
+ exit_round_robin(tsk_index);
+ return 0;
+}
+
+static struct task_struct *ps_tsks[NR_CPUS];
+static unsigned int ps_tsk_num;
+static int create_power_saving_task(void)
+{
+ int rc;
+
+ ps_tsks[ps_tsk_num] = kthread_run(power_saving_thread,
+ (void *)(unsigned long)ps_tsk_num,
+ "acpi_pad/%d", ps_tsk_num);
+
+ if (IS_ERR(ps_tsks[ps_tsk_num])) {
+ rc = PTR_ERR(ps_tsks[ps_tsk_num]);
+ ps_tsks[ps_tsk_num] = NULL;
+ } else {
+ rc = 0;
+ ps_tsk_num++;
+ }
+
+ return rc;
+}
+
+static void destroy_power_saving_task(void)
+{
+ if (ps_tsk_num > 0) {
+ ps_tsk_num--;
+ kthread_stop(ps_tsks[ps_tsk_num]);
+ ps_tsks[ps_tsk_num] = NULL;
+ }
+}
+
+static void set_power_saving_task_num(unsigned int num)
+{
+ if (num > ps_tsk_num) {
+ while (ps_tsk_num < num) {
+ if (create_power_saving_task())
+ return;
+ }
+ } else if (num < ps_tsk_num) {
+ while (ps_tsk_num > num)
+ destroy_power_saving_task();
+ }
+}
+
+static void acpi_pad_idle_cpus(unsigned int num_cpus)
+{
+ cpus_read_lock();
+
+ num_cpus = min_t(unsigned int, num_cpus, num_online_cpus());
+ set_power_saving_task_num(num_cpus);
+
+ cpus_read_unlock();
+}
+
+static uint32_t acpi_pad_idle_cpus_num(void)
+{
+ return ps_tsk_num;
+}
+
+static ssize_t rrtime_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long num;
+
+ if (kstrtoul(buf, 0, &num))
+ return -EINVAL;
+ if (num < 1 || num >= 100)
+ return -EINVAL;
+ mutex_lock(&isolated_cpus_lock);
+ round_robin_time = num;
+ mutex_unlock(&isolated_cpus_lock);
+ return count;
+}
+
+static ssize_t rrtime_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", round_robin_time);
+}
+static DEVICE_ATTR_RW(rrtime);
+
+static ssize_t idlepct_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long num;
+
+ if (kstrtoul(buf, 0, &num))
+ return -EINVAL;
+ if (num < 1 || num >= 100)
+ return -EINVAL;
+ mutex_lock(&isolated_cpus_lock);
+ idle_pct = num;
+ mutex_unlock(&isolated_cpus_lock);
+ return count;
+}
+
+static ssize_t idlepct_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", idle_pct);
+}
+static DEVICE_ATTR_RW(idlepct);
+
+static ssize_t idlecpus_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long num;
+
+ if (kstrtoul(buf, 0, &num))
+ return -EINVAL;
+ mutex_lock(&isolated_cpus_lock);
+ acpi_pad_idle_cpus(num);
+ mutex_unlock(&isolated_cpus_lock);
+ return count;
+}
+
+static ssize_t idlecpus_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return cpumap_print_to_pagebuf(false, buf,
+ to_cpumask(pad_busy_cpus_bits));
+}
+
+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 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);
+}
+
+/*
+ * Query firmware how many CPUs should be idle
+ * return -1 on failure
+ */
+static int acpi_pad_pur(acpi_handle handle)
+{
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *package;
+ int num = -1;
+
+ if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PUR", NULL, &buffer)))
+ return num;
+
+ if (!buffer.length || !buffer.pointer)
+ return num;
+
+ package = buffer.pointer;
+
+ if (package->type == ACPI_TYPE_PACKAGE &&
+ package->package.count == 2 &&
+ package->package.elements[0].integer.value == 1) /* rev 1 */
+
+ num = package->package.elements[1].integer.value;
+
+ kfree(buffer.pointer);
+ return num;
+}
+
+static void acpi_pad_handle_notify(acpi_handle handle)
+{
+ int num_cpus;
+ uint32_t idle_cpus;
+ struct acpi_buffer param = {
+ .length = 4,
+ .pointer = (void *)&idle_cpus,
+ };
+
+ mutex_lock(&isolated_cpus_lock);
+ num_cpus = acpi_pad_pur(handle);
+ if (num_cpus < 0) {
+ mutex_unlock(&isolated_cpus_lock);
+ return;
+ }
+ acpi_pad_idle_cpus(num_cpus);
+ idle_cpus = acpi_pad_idle_cpus_num();
+ acpi_evaluate_ost(handle, ACPI_PROCESSOR_AGGREGATOR_NOTIFY, 0, &param);
+ mutex_unlock(&isolated_cpus_lock);
+}
+
+static void acpi_pad_notify(acpi_handle handle, u32 event,
+ void *data)
+{
+ struct acpi_device *device = 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);
+ break;
+ default:
+ pr_warn("Unsupported event [0x%x]\n", event);
+ break;
+ }
+}
+
+static int acpi_pad_add(struct acpi_device *device)
+{
+ acpi_status status;
+
+ strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS);
+
+ if (acpi_pad_add_sysfs(device))
+ return -ENODEV;
+
+ status = acpi_install_notify_handler(device->handle,
+ ACPI_DEVICE_NOTIFY, acpi_pad_notify, device);
+ if (ACPI_FAILURE(status)) {
+ acpi_pad_remove_sysfs(device);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int acpi_pad_remove(struct acpi_device *device)
+{
+ mutex_lock(&isolated_cpus_lock);
+ acpi_pad_idle_cpus(0);
+ mutex_unlock(&isolated_cpus_lock);
+
+ acpi_remove_notify_handler(device->handle,
+ ACPI_DEVICE_NOTIFY, acpi_pad_notify);
+ acpi_pad_remove_sysfs(device);
+ return 0;
+}
+
+static const struct acpi_device_id pad_device_ids[] = {
+ {"ACPI000C", 0},
+ {"", 0},
+};
+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 int __init acpi_pad_init(void)
+{
+ /* Xen ACPI PAD is used when running as Xen Dom0. */
+ if (xen_initial_domain())
+ return -ENODEV;
+
+ power_saving_mwait_init();
+ if (power_saving_mwait_eax == 0)
+ return -EINVAL;
+
+ return acpi_bus_register_driver(&acpi_pad_driver);
+}
+
+static void __exit acpi_pad_exit(void)
+{
+ acpi_bus_unregister_driver(&acpi_pad_driver);
+}
+
+module_init(acpi_pad_init);
+module_exit(acpi_pad_exit);
+MODULE_AUTHOR("Shaohua Li<shaohua.li@intel.com>");
+MODULE_DESCRIPTION("ACPI Processor Aggregator Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/acpi_pcc.c b/drivers/acpi/acpi_pcc.c
new file mode 100644
index 000000000..3e252be04
--- /dev/null
+++ b/drivers/acpi/acpi_pcc.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Author: Sudeep Holla <sudeep.holla@arm.com>
+ * Copyright 2021 Arm Limited
+ *
+ * The PCC Address Space also referred as PCC Operation Region pertains to the
+ * region of PCC subspace that succeeds the PCC signature. The PCC Operation
+ * Region works in conjunction with the PCC Table(Platform Communications
+ * Channel Table). PCC subspaces that are marked for use as PCC Operation
+ * Regions must not be used as PCC subspaces for the standard ACPI features
+ * such as CPPC, RASF, PDTT and MPST. These standard features must always use
+ * the PCC Table instead.
+ *
+ * This driver sets up the PCC Address Space and installs an handler to enable
+ * handling of PCC OpRegion in the firmware.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/completion.h>
+#include <linux/idr.h>
+#include <linux/io.h>
+
+#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;
+};
+
+static struct acpi_pcc_info pcc_ctx;
+
+static void pcc_rx_callback(struct mbox_client *cl, void *m)
+{
+ struct pcc_data *data = container_of(cl, struct pcc_data, cl);
+
+ complete(&data->done);
+}
+
+static acpi_status
+acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function,
+ void *handler_context, void **region_context)
+{
+ struct pcc_data *data;
+ struct acpi_pcc_info *ctx = handler_context;
+ struct pcc_mbox_chan *pcc_chan;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return AE_NO_MEMORY;
+
+ data->cl.rx_callback = pcc_rx_callback;
+ data->cl.knows_txdone = true;
+ data->ctx.length = ctx->length;
+ data->ctx.subspace_id = ctx->subspace_id;
+ data->ctx.internal_buffer = ctx->internal_buffer;
+
+ init_completion(&data->done);
+ data->pcc_chan = pcc_mbox_request_channel(&data->cl, ctx->subspace_id);
+ if (IS_ERR(data->pcc_chan)) {
+ pr_err("Failed to find PCC channel for subspace %d\n",
+ ctx->subspace_id);
+ kfree(data);
+ return AE_NOT_FOUND;
+ }
+
+ 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",
+ ctx->subspace_id);
+ pcc_mbox_free_channel(data->pcc_chan);
+ kfree(data);
+ return AE_NO_MEMORY;
+ }
+
+ *region_context = data;
+ return AE_OK;
+}
+
+static acpi_status
+acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr,
+ u32 bits, acpi_integer *value,
+ void *handler_context, void *region_context)
+{
+ 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);
+
+ ret = mbox_send_message(data->pcc_chan->mchan, NULL);
+ if (ret < 0)
+ return AE_ERROR;
+
+ if (data->pcc_chan->mchan->mbox->txdone_irq) {
+ /*
+ * 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_chan_txdone(data->pcc_chan->mchan, ret);
+
+ memcpy_fromio(value, data->pcc_comm_addr, data->ctx.length);
+
+ return AE_OK;
+}
+
+void __init acpi_init_pcc(void)
+{
+ acpi_status status;
+
+ status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_PLATFORM_COMM,
+ &acpi_pcc_address_space_handler,
+ &acpi_pcc_address_space_setup,
+ &pcc_ctx);
+ if (ACPI_FAILURE(status))
+ pr_alert("OperationRegion handler could not be installed\n");
+}
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
new file mode 100644
index 000000000..fe00a5783
--- /dev/null
+++ b/drivers/acpi/acpi_platform.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI support for platform bus type.
+ *
+ * Copyright (C) 2012, Intel Corporation
+ * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
+ * Mathias Nyman <mathias.nyman@linux.intel.com>
+ * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include "internal.h"
+
+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 */
+ {"SMB0001", 0}, /* ACPI SMBUS virtual device */
+ { }
+};
+
+static struct platform_device *acpi_platform_device_find_by_companion(struct acpi_device *adev)
+{
+ struct device *dev;
+
+ dev = bus_find_device_by_acpi_dev(&platform_bus_type, adev);
+ return dev ? to_platform_device(dev) : NULL;
+}
+
+static int acpi_platform_device_remove_notify(struct notifier_block *nb,
+ unsigned long value, void *arg)
+{
+ struct acpi_device *adev = arg;
+ struct platform_device *pdev;
+
+ switch (value) {
+ case ACPI_RECONFIG_DEVICE_ADD:
+ /* Nothing to do here */
+ break;
+ case ACPI_RECONFIG_DEVICE_REMOVE:
+ if (!acpi_device_enumerated(adev))
+ break;
+
+ pdev = acpi_platform_device_find_by_companion(adev);
+ if (!pdev)
+ break;
+
+ platform_device_unregister(pdev);
+ put_device(&pdev->dev);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block acpi_platform_notifier = {
+ .notifier_call = acpi_platform_device_remove_notify,
+};
+
+static void acpi_platform_fill_resource(struct acpi_device *adev,
+ const struct resource *src, struct resource *dest)
+{
+ struct device *parent;
+
+ *dest = *src;
+
+ /*
+ * 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(acpi_dev_parent(adev));
+ if (parent && dev_is_pci(parent))
+ dest->parent = pci_find_resource(to_pci_dev(parent), dest);
+}
+
+/**
+ * acpi_create_platform_device - Create platform device for ACPI device node
+ * @adev: ACPI device node to create a platform device for.
+ * @properties: Optional collection of build-in properties.
+ *
+ * Check if the given @adev can be represented as a platform device and, if
+ * that's the case, create and register a platform device, populate its common
+ * resources and returns a pointer to it. Otherwise, return %NULL.
+ *
+ * Name of the platform device will be the same as @adev's.
+ */
+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;
+ struct resource_entry *rentry;
+ struct list_head resource_list;
+ struct resource *resources = NULL;
+ int count;
+
+ /* If the ACPI node already has a physical device attached, skip it. */
+ if (adev->physical_node_count)
+ return NULL;
+
+ if (!acpi_match_device_ids(adev, forbidden_id_list))
+ return ERR_PTR(-EINVAL);
+
+ INIT_LIST_HEAD(&resource_list);
+ count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
+ if (count < 0)
+ return NULL;
+ if (count > 0) {
+ resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
+ if (!resources) {
+ acpi_dev_free_resource_list(&resource_list);
+ return ERR_PTR(-ENOMEM);
+ }
+ count = 0;
+ list_for_each_entry(rentry, &resource_list, node)
+ acpi_platform_fill_resource(adev, rentry->res,
+ &resources[count++]);
+
+ acpi_dev_free_resource_list(&resource_list);
+ }
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ /*
+ * 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
+ * platform device we are about to create.
+ */
+ pdevinfo.parent = parent ? acpi_get_first_physical_node(parent) : NULL;
+ pdevinfo.name = dev_name(&adev->dev);
+ pdevinfo.id = PLATFORM_DEVID_NONE;
+ pdevinfo.res = resources;
+ pdevinfo.num_res = count;
+ pdevinfo.fwnode = acpi_fwnode_handle(adev);
+ pdevinfo.properties = properties;
+
+ if (acpi_dma_supported(adev))
+ pdevinfo.dma_mask = DMA_BIT_MASK(32);
+ else
+ pdevinfo.dma_mask = 0;
+
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev))
+ dev_err(&adev->dev, "platform device creation failed: %ld\n",
+ PTR_ERR(pdev));
+ else {
+ set_dev_node(&pdev->dev, acpi_get_node(adev->handle));
+ dev_dbg(&adev->dev, "created platform device %s\n",
+ dev_name(&pdev->dev));
+ }
+
+ kfree(resources);
+
+ return pdev;
+}
+EXPORT_SYMBOL_GPL(acpi_create_platform_device);
+
+void __init acpi_platform_init(void)
+{
+ acpi_reconfig_notifier_register(&acpi_platform_notifier);
+}
diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c
new file mode 100644
index 000000000..ffdcfcd4a
--- /dev/null
+++ b/drivers/acpi/acpi_pnp.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI support for PNP bus type
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Authors: Zhang Rui <rui.zhang@intel.com>
+ * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+
+#include "internal.h"
+
+static const struct acpi_device_id acpi_pnp_device_ids[] = {
+ /* pata_isapnp */
+ {"PNP0600"}, /* Generic ESDI/IDE/ATA compatible hard disk controller */
+ /* floppy */
+ {"PNP0700"},
+ /* tpm_inf_pnp */
+ {"IFX0101"}, /* Infineon TPMs */
+ {"IFX0102"}, /* Infineon TPMs */
+ /*tpm_tis */
+ {"PNP0C31"}, /* TPM */
+ {"ATM1200"}, /* Atmel */
+ {"IFX0102"}, /* Infineon */
+ {"BCM0101"}, /* Broadcom */
+ {"BCM0102"}, /* Broadcom */
+ {"NSC1200"}, /* National */
+ {"ICO0102"}, /* Intel */
+ /* ide */
+ {"PNP0600"}, /* Generic ESDI/IDE/ATA compatible hard disk controller */
+ /* ns558 */
+ {"ASB16fd"}, /* AdLib NSC16 */
+ {"AZT3001"}, /* AZT1008 */
+ {"CDC0001"}, /* Opl3-SAx */
+ {"CSC0001"}, /* CS4232 */
+ {"CSC000f"}, /* CS4236 */
+ {"CSC0101"}, /* CS4327 */
+ {"CTL7001"}, /* SB16 */
+ {"CTL7002"}, /* AWE64 */
+ {"CTL7005"}, /* Vibra16 */
+ {"ENS2020"}, /* SoundscapeVIVO */
+ {"ESS0001"}, /* ES1869 */
+ {"ESS0005"}, /* ES1878 */
+ {"ESS6880"}, /* ES688 */
+ {"IBM0012"}, /* CS4232 */
+ {"OPT0001"}, /* OPTi Audio16 */
+ {"YMH0006"}, /* Opl3-SA */
+ {"YMH0022"}, /* Opl3-SAx */
+ {"PNPb02f"}, /* Generic */
+ /* i8042 kbd */
+ {"PNP0300"},
+ {"PNP0301"},
+ {"PNP0302"},
+ {"PNP0303"},
+ {"PNP0304"},
+ {"PNP0305"},
+ {"PNP0306"},
+ {"PNP0309"},
+ {"PNP030a"},
+ {"PNP030b"},
+ {"PNP0320"},
+ {"PNP0343"},
+ {"PNP0344"},
+ {"PNP0345"},
+ {"CPQA0D7"},
+ /* i8042 aux */
+ {"AUI0200"},
+ {"FJC6000"},
+ {"FJC6001"},
+ {"PNP0f03"},
+ {"PNP0f0b"},
+ {"PNP0f0e"},
+ {"PNP0f12"},
+ {"PNP0f13"},
+ {"PNP0f19"},
+ {"PNP0f1c"},
+ {"SYN0801"},
+ /* fcpnp */
+ {"AVM0900"},
+ /* radio-cadet */
+ {"MSM0c24"}, /* ADS Cadet AM/FM Radio Card */
+ /* radio-gemtek */
+ {"ADS7183"}, /* AOpen FX-3D/Pro Radio */
+ /* radio-sf16fmr2 */
+ {"MFRad13"}, /* tuner subdevice of SF16-FMD2 */
+ /* ene_ir */
+ {"ENE0100"},
+ {"ENE0200"},
+ {"ENE0201"},
+ {"ENE0202"},
+ /* fintek-cir */
+ {"FIT0002"}, /* CIR */
+ /* ite-cir */
+ {"ITE8704"}, /* Default model */
+ {"ITE8713"}, /* CIR found in EEEBox 1501U */
+ {"ITE8708"}, /* Bridged IT8512 */
+ {"ITE8709"}, /* SRAM-Bridged IT8512 */
+ /* nuvoton-cir */
+ {"WEC0530"}, /* CIR */
+ {"NTN0530"}, /* CIR for new chip's pnp id */
+ /* Winbond CIR */
+ {"WEC1022"},
+ /* wbsd */
+ {"WEC0517"},
+ {"WEC0518"},
+ /* Winbond CIR */
+ {"TCM5090"}, /* 3Com Etherlink III (TP) */
+ {"TCM5091"}, /* 3Com Etherlink III */
+ {"TCM5094"}, /* 3Com Etherlink III (combo) */
+ {"TCM5095"}, /* 3Com Etherlink III (TPO) */
+ {"TCM5098"}, /* 3Com Etherlink III (TPC) */
+ {"PNP80f7"}, /* 3Com Etherlink III compatible */
+ {"PNP80f8"}, /* 3Com Etherlink III compatible */
+ /* nsc-ircc */
+ {"NSC6001"},
+ {"HWPC224"},
+ {"IBM0071"},
+ /* smsc-ircc2 */
+ {"SMCf010"},
+ /* sb1000 */
+ {"GIC1000"},
+ /* parport_pc */
+ {"PNP0400"}, /* Standard LPT Printer Port */
+ {"PNP0401"}, /* ECP Printer Port */
+ /* apple-gmux */
+ {"APP000B"},
+ /* system */
+ {"PNP0c02"}, /* General ID for reserving resources */
+ {"PNP0c01"}, /* memory controller */
+ /* rtc_cmos */
+ {"PNP0b00"},
+ {"PNP0b01"},
+ {"PNP0b02"},
+ /* c6xdigio */
+ {"PNP0400"}, /* Standard LPT Printer Port */
+ {"PNP0401"}, /* ECP Printer Port */
+ /* ni_atmio.c */
+ {"NIC1900"},
+ {"NIC2400"},
+ {"NIC2500"},
+ {"NIC2600"},
+ {"NIC2700"},
+ /* serial */
+ {"AAC000F"}, /* Archtek America Corp. Archtek SmartLink Modem 3334BT Plug & Play */
+ {"ADC0001"}, /* Anchor Datacomm BV. SXPro 144 External Data Fax Modem Plug & Play */
+ {"ADC0002"}, /* SXPro 288 External Data Fax Modem Plug & Play */
+ {"AEI0250"}, /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */
+ {"AEI1240"}, /* Actiontec ISA PNP 56K X2 Fax Modem */
+ {"AKY1021"}, /* Rockwell 56K ACF II Fax+Data+Voice Modem */
+ {"ALI5123"}, /* ALi Fast Infrared Controller */
+ {"AZT4001"}, /* AZT3005 PnP SOUND DEVICE */
+ {"BDP3336"}, /* Best Data Products Inc. Smart One 336F PnP Modem */
+ {"BRI0A49"}, /* Boca Complete Ofc Communicator 14.4 Data-FAX */
+ {"BRI1400"}, /* Boca Research 33,600 ACF Modem */
+ {"BRI3400"}, /* Boca 33.6 Kbps Internal FD34FSVD */
+ {"CPI4050"}, /* Computer Peripherals Inc. EuroViVa CommCenter-33.6 SP PnP */
+ {"CTL3001"}, /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */
+ {"CTL3011"}, /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */
+ {"DAV0336"}, /* Davicom ISA 33.6K Modem */
+ {"DMB1032"}, /* Creative Modem Blaster Flash56 DI5601-1 */
+ {"DMB2001"}, /* Creative Modem Blaster V.90 DI5660 */
+ {"ETT0002"}, /* E-Tech CyberBULLET PC56RVP */
+ {"FUJ0202"}, /* Fujitsu 33600 PnP-I2 R Plug & Play */
+ {"FUJ0205"}, /* Fujitsu FMV-FX431 Plug & Play */
+ {"FUJ0206"}, /* Fujitsu 33600 PnP-I4 R Plug & Play */
+ {"FUJ0209"}, /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */
+ {"GVC000F"}, /* Archtek SmartLink Modem 3334BT Plug & Play */
+ {"GVC0303"}, /* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */
+ {"HAY0001"}, /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */
+ {"HAY000C"}, /* Hayes Optima 336 V.34 + FAX + Voice PnP */
+ {"HAY000D"}, /* Hayes Optima 336B V.34 + FAX + Voice PnP */
+ {"HAY5670"}, /* Hayes Accura 56K Ext Fax Modem PnP */
+ {"HAY5674"}, /* Hayes Accura 56K Ext Fax Modem PnP */
+ {"HAY5675"}, /* Hayes Accura 56K Fax Modem PnP */
+ {"HAYF000"}, /* Hayes 288, V.34 + FAX */
+ {"HAYF001"}, /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */
+ {"IBM0033"}, /* IBM Thinkpad 701 Internal Modem Voice */
+ {"PNP4972"}, /* Intermec CV60 touchscreen port */
+ {"IXDC801"}, /* Intertex 28k8 33k6 Voice EXT PnP */
+ {"IXDC901"}, /* Intertex 33k6 56k Voice EXT PnP */
+ {"IXDD801"}, /* Intertex 28k8 33k6 Voice SP EXT PnP */
+ {"IXDD901"}, /* Intertex 33k6 56k Voice SP EXT PnP */
+ {"IXDF401"}, /* Intertex 28k8 33k6 Voice SP INT PnP */
+ {"IXDF801"}, /* Intertex 28k8 33k6 Voice SP EXT PnP */
+ {"IXDF901"}, /* Intertex 33k6 56k Voice SP EXT PnP */
+ {"KOR4522"}, /* KORTEX 28800 Externe PnP */
+ {"KORF661"}, /* KXPro 33.6 Vocal ASVD PnP */
+ {"LAS4040"}, /* LASAT Internet 33600 PnP */
+ {"LAS4540"}, /* Lasat Safire 560 PnP */
+ {"LAS5440"}, /* Lasat Safire 336 PnP */
+ {"MNP0281"}, /* Microcom TravelPorte FAST V.34 Plug & Play */
+ {"MNP0336"}, /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */
+ {"MNP0339"}, /* Microcom DeskPorte FAST EP 28.8 Plug & Play */
+ {"MNP0342"}, /* Microcom DeskPorte 28.8P Plug & Play */
+ {"MNP0500"}, /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
+ {"MNP0501"}, /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
+ {"MNP0502"}, /* Microcom DeskPorte 28.8S Internal Plug & Play */
+ {"MOT1105"}, /* Motorola BitSURFR Plug & Play */
+ {"MOT1111"}, /* Motorola TA210 Plug & Play */
+ {"MOT1114"}, /* Motorola HMTA 200 (ISDN) Plug & Play */
+ {"MOT1115"}, /* Motorola BitSURFR Plug & Play */
+ {"MOT1190"}, /* Motorola Lifestyle 28.8 Internal */
+ {"MOT1501"}, /* Motorola V.3400 Plug & Play */
+ {"MOT1502"}, /* Motorola Lifestyle 28.8 V.34 Plug & Play */
+ {"MOT1505"}, /* Motorola Power 28.8 V.34 Plug & Play */
+ {"MOT1509"}, /* Motorola ModemSURFR External 28.8 Plug & Play */
+ {"MOT150A"}, /* Motorola Premier 33.6 Desktop Plug & Play */
+ {"MOT150F"}, /* Motorola VoiceSURFR 56K External PnP */
+ {"MOT1510"}, /* Motorola ModemSURFR 56K External PnP */
+ {"MOT1550"}, /* Motorola ModemSURFR 56K Internal PnP */
+ {"MOT1560"}, /* Motorola ModemSURFR Internal 28.8 Plug & Play */
+ {"MOT1580"}, /* Motorola Premier 33.6 Internal Plug & Play */
+ {"MOT15B0"}, /* Motorola OnlineSURFR 28.8 Internal Plug & Play */
+ {"MOT15F0"}, /* Motorola VoiceSURFR 56K Internal PnP */
+ {"MVX00A1"}, /* Deskline K56 Phone System PnP */
+ {"MVX00F2"}, /* PC Rider K56 Phone System PnP */
+ {"nEC8241"}, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */
+ {"PMC2430"}, /* Pace 56 Voice Internal Plug & Play Modem */
+ {"PNP0500"}, /* Generic standard PC COM port */
+ {"PNP0501"}, /* Generic 16550A-compatible COM port */
+ {"PNPC000"}, /* Compaq 14400 Modem */
+ {"PNPC001"}, /* Compaq 2400/9600 Modem */
+ {"PNPC031"}, /* Dial-Up Networking Serial Cable between 2 PCs */
+ {"PNPC032"}, /* Dial-Up Networking Parallel Cable between 2 PCs */
+ {"PNPC100"}, /* Standard 9600 bps Modem */
+ {"PNPC101"}, /* Standard 14400 bps Modem */
+ {"PNPC102"}, /* Standard 28800 bps Modem */
+ {"PNPC103"}, /* Standard Modem */
+ {"PNPC104"}, /* Standard 9600 bps Modem */
+ {"PNPC105"}, /* Standard 14400 bps Modem */
+ {"PNPC106"}, /* Standard 28800 bps Modem */
+ {"PNPC107"}, /* Standard Modem */
+ {"PNPC108"}, /* Standard 9600 bps Modem */
+ {"PNPC109"}, /* Standard 14400 bps Modem */
+ {"PNPC10A"}, /* Standard 28800 bps Modem */
+ {"PNPC10B"}, /* Standard Modem */
+ {"PNPC10C"}, /* Standard 9600 bps Modem */
+ {"PNPC10D"}, /* Standard 14400 bps Modem */
+ {"PNPC10E"}, /* Standard 28800 bps Modem */
+ {"PNPC10F"}, /* Standard Modem */
+ {"PNP2000"}, /* Standard PCMCIA Card Modem */
+ {"ROK0030"}, /* Rockwell 33.6 DPF Internal PnP, Modular Technology 33.6 Internal PnP */
+ {"ROK0100"}, /* KORTEX 14400 Externe PnP */
+ {"ROK4120"}, /* Rockwell 28.8 */
+ {"ROK4920"}, /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */
+ {"RSS00A0"}, /* Rockwell 33.6 DPF External PnP, BT Prologue 33.6 External PnP, Modular Technology 33.6 External PnP */
+ {"RSS0262"}, /* Viking 56K FAX INT */
+ {"RSS0250"}, /* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */
+ {"SUP1310"}, /* SupraExpress 28.8 Data/Fax PnP modem */
+ {"SUP1381"}, /* SupraExpress 336i PnP Voice Modem */
+ {"SUP1421"}, /* SupraExpress 33.6 Data/Fax PnP modem */
+ {"SUP1590"}, /* SupraExpress 33.6 Data/Fax PnP modem */
+ {"SUP1620"}, /* SupraExpress 336i Sp ASVD */
+ {"SUP1760"}, /* SupraExpress 33.6 Data/Fax PnP modem */
+ {"SUP2171"}, /* SupraExpress 56i Sp Intl */
+ {"TEX0011"}, /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */
+ {"UAC000F"}, /* Archtek SmartLink Modem 3334BT Plug & Play */
+ {"USR0000"}, /* 3Com Corp. Gateway Telepath IIvi 33.6 */
+ {"USR0002"}, /* U.S. Robotics Sporster 33.6K Fax INT PnP */
+ {"USR0004"}, /* Sportster Vi 14.4 PnP FAX Voicemail */
+ {"USR0006"}, /* U.S. Robotics 33.6K Voice INT PnP */
+ {"USR0007"}, /* U.S. Robotics 33.6K Voice EXT PnP */
+ {"USR0009"}, /* U.S. Robotics Courier V.Everything INT PnP */
+ {"USR2002"}, /* U.S. Robotics 33.6K Voice INT PnP */
+ {"USR2070"}, /* U.S. Robotics 56K Voice INT PnP */
+ {"USR2080"}, /* U.S. Robotics 56K Voice EXT PnP */
+ {"USR3031"}, /* U.S. Robotics 56K FAX INT */
+ {"USR3050"}, /* U.S. Robotics 56K FAX INT */
+ {"USR3070"}, /* U.S. Robotics 56K Voice INT PnP */
+ {"USR3080"}, /* U.S. Robotics 56K Voice EXT PnP */
+ {"USR3090"}, /* U.S. Robotics 56K Voice INT PnP */
+ {"USR9100"}, /* U.S. Robotics 56K Message */
+ {"USR9160"}, /* U.S. Robotics 56K FAX EXT PnP */
+ {"USR9170"}, /* U.S. Robotics 56K FAX INT PnP */
+ {"USR9180"}, /* U.S. Robotics 56K Voice EXT PnP */
+ {"USR9190"}, /* U.S. Robotics 56K Voice INT PnP */
+ {"WACFXXX"}, /* Wacom tablets */
+ {"FPI2002"}, /* Compaq touchscreen */
+ {"FUJ02B2"}, /* Fujitsu Stylistic touchscreens */
+ {"FUJ02B3"},
+ {"FUJ02B4"}, /* Fujitsu Stylistic LT touchscreens */
+ {"FUJ02B6"}, /* Passive Fujitsu Stylistic touchscreens */
+ {"FUJ02B7"},
+ {"FUJ02B8"},
+ {"FUJ02B9"},
+ {"FUJ02BC"},
+ {"FUJ02E5"}, /* Fujitsu Wacom Tablet PC device */
+ {"FUJ02E6"}, /* Fujitsu P-series tablet PC device */
+ {"FUJ02E7"}, /* Fujitsu Wacom 2FGT Tablet PC device */
+ {"FUJ02E9"}, /* Fujitsu Wacom 1FGT Tablet PC device */
+ {"LTS0001"}, /* LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in disguise) */
+ {"WCI0003"}, /* Rockwell's (PORALiNK) 33600 INT PNP */
+ {"WEC1022"}, /* Winbond CIR port, should not be probed. We should keep track of it to prevent the legacy serial driver from probing it */
+ /* scl200wdt */
+ {"NSC0800"}, /* National Semiconductor PC87307/PC97307 watchdog component */
+ /* mpu401 */
+ {"PNPb006"},
+ /* cs423x-pnpbios */
+ {"CSC0100"},
+ {"CSC0103"},
+ {"CSC0110"},
+ {"CSC0000"},
+ {"GIM0100"}, /* Guillemot Turtlebeach something appears to be cs4232 compatible */
+ /* es18xx-pnpbios */
+ {"ESS1869"},
+ {"ESS1879"},
+ /* snd-opl3sa2-pnpbios */
+ {"YMH0021"},
+ {"NMX2210"}, /* Gateway Solo 2500 */
+ {""},
+};
+
+static bool matching_id(const char *idstr, const char *list_id)
+{
+ int i;
+
+ if (strlen(idstr) != strlen(list_id))
+ return false;
+
+ if (memcmp(idstr, list_id, 3))
+ return false;
+
+ for (i = 3; i < 7; i++) {
+ char c = toupper(idstr[i]);
+
+ if (!isxdigit(c)
+ || (list_id[i] != 'X' && c != toupper(list_id[i])))
+ return false;
+ }
+ return true;
+}
+
+static bool acpi_pnp_match(const char *idstr, const struct acpi_device_id **matchid)
+{
+ const struct acpi_device_id *devid;
+
+ for (devid = acpi_pnp_device_ids; devid->id[0]; devid++)
+ if (matching_id(idstr, (char *)devid->id)) {
+ if (matchid)
+ *matchid = devid;
+
+ return true;
+ }
+
+ return false;
+}
+
+static int acpi_pnp_attach(struct acpi_device *adev,
+ const struct acpi_device_id *id)
+{
+ return 1;
+}
+
+static struct acpi_scan_handler acpi_pnp_handler = {
+ .ids = acpi_pnp_device_ids,
+ .match = acpi_pnp_match,
+ .attach = acpi_pnp_attach,
+};
+
+/*
+ * For CMOS RTC devices, the PNP ACPI scan handler does not work, because
+ * there is a CMOS RTC ACPI scan handler installed already, so we need to
+ * check those devices and enumerate them to the PNP bus directly.
+ */
+static int is_cmos_rtc_device(struct acpi_device *adev)
+{
+ static const struct acpi_device_id ids[] = {
+ { "PNP0B00" },
+ { "PNP0B01" },
+ { "PNP0B02" },
+ {""},
+ };
+ return !acpi_match_device_ids(adev, ids);
+}
+
+bool acpi_is_pnp_device(struct acpi_device *adev)
+{
+ return adev->handler == &acpi_pnp_handler || is_cmos_rtc_device(adev);
+}
+EXPORT_SYMBOL_GPL(acpi_is_pnp_device);
+
+void __init acpi_pnp_init(void)
+{
+ acpi_scan_add_handler(&acpi_pnp_handler);
+}
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
new file mode 100644
index 000000000..6737b1cbf
--- /dev/null
+++ b/drivers/acpi/acpi_processor.c
@@ -0,0 +1,893 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * acpi_processor.c - ACPI processor enumeration support
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de>
+ * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ * Copyright (C) 2013, Intel Corporation
+ * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include <acpi/processor.h>
+
+#include <asm/cpu.h>
+
+#include "internal.h"
+
+DEFINE_PER_CPU(struct acpi_processor *, processors);
+EXPORT_PER_CPU_SYMBOL(processors);
+
+/* Errata Handling */
+struct acpi_processor_errata errata __read_mostly;
+EXPORT_SYMBOL_GPL(errata);
+
+static int acpi_processor_errata_piix4(struct pci_dev *dev)
+{
+ u8 value1 = 0;
+ u8 value2 = 0;
+
+
+ if (!dev)
+ return -EINVAL;
+
+ /*
+ * Note that 'dev' references the PIIX4 ACPI Controller.
+ */
+
+ switch (dev->revision) {
+ case 0:
+ dev_dbg(&dev->dev, "Found PIIX4 A-step\n");
+ break;
+ case 1:
+ dev_dbg(&dev->dev, "Found PIIX4 B-step\n");
+ break;
+ case 2:
+ dev_dbg(&dev->dev, "Found PIIX4E\n");
+ break;
+ case 3:
+ dev_dbg(&dev->dev, "Found PIIX4M\n");
+ break;
+ default:
+ dev_dbg(&dev->dev, "Found unknown PIIX4\n");
+ break;
+ }
+
+ switch (dev->revision) {
+
+ case 0: /* PIIX4 A-step */
+ case 1: /* PIIX4 B-step */
+ /*
+ * See specification changes #13 ("Manual Throttle Duty Cycle")
+ * and #14 ("Enabling and Disabling Manual Throttle"), plus
+ * erratum #5 ("STPCLK# Deassertion Time") from the January
+ * 2002 PIIX4 specification update. Applies to only older
+ * PIIX4 models.
+ */
+ errata.piix4.throttle = 1;
+ fallthrough;
+
+ case 2: /* PIIX4E */
+ case 3: /* PIIX4M */
+ /*
+ * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA
+ * Livelock") from the January 2002 PIIX4 specification update.
+ * Applies to all PIIX4 models.
+ */
+
+ /*
+ * BM-IDE
+ * ------
+ * Find the PIIX4 IDE Controller and get the Bus Master IDE
+ * Status register address. We'll use this later to read
+ * each IDE controller's DMA status to make sure we catch all
+ * DMA activity.
+ */
+ dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82371AB,
+ PCI_ANY_ID, PCI_ANY_ID, NULL);
+ if (dev) {
+ errata.piix4.bmisx = pci_resource_start(dev, 4);
+ pci_dev_put(dev);
+ }
+
+ /*
+ * Type-F DMA
+ * ----------
+ * Find the PIIX4 ISA Controller and read the Motherboard
+ * DMA controller's status to see if Type-F (Fast) DMA mode
+ * is enabled (bit 7) on either channel. Note that we'll
+ * disable C3 support if this is enabled, as some legacy
+ * devices won't operate well if fast DMA is disabled.
+ */
+ dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82371AB_0,
+ PCI_ANY_ID, PCI_ANY_ID, NULL);
+ if (dev) {
+ pci_read_config_byte(dev, 0x76, &value1);
+ pci_read_config_byte(dev, 0x77, &value2);
+ if ((value1 & 0x80) || (value2 & 0x80))
+ errata.piix4.fdma = 1;
+ pci_dev_put(dev);
+ }
+
+ break;
+ }
+
+ if (errata.piix4.bmisx)
+ dev_dbg(&dev->dev, "Bus master activity detection (BM-IDE) erratum enabled\n");
+ if (errata.piix4.fdma)
+ dev_dbg(&dev->dev, "Type-F DMA livelock erratum (C3 disabled)\n");
+
+ return 0;
+}
+
+static int acpi_processor_errata(void)
+{
+ int result = 0;
+ struct pci_dev *dev = NULL;
+
+ /*
+ * PIIX4
+ */
+ dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID,
+ PCI_ANY_ID, NULL);
+ if (dev) {
+ result = acpi_processor_errata_piix4(dev);
+ pci_dev_put(dev);
+ }
+
+ 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)
+{
+ return -ENODEV;
+}
+
+int __weak acpi_unmap_cpu(int cpu)
+{
+ return -ENODEV;
+}
+
+int __weak arch_register_cpu(int cpu)
+{
+ return -ENODEV;
+}
+
+void __weak arch_unregister_cpu(int cpu) {}
+
+static int acpi_processor_hotadd_init(struct acpi_processor *pr)
+{
+ 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();
+
+ ret = acpi_map_cpu(pr->handle, pr->phys_id, pr->acpi_id, &pr->id);
+ if (ret)
+ goto out;
+
+ ret = arch_register_cpu(pr->id);
+ if (ret) {
+ 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.
+ */
+ pr_info("CPU%d has been hot-added\n", pr->id);
+ pr->flags.need_hotplug_init = 1;
+
+out:
+ cpus_write_unlock();
+ cpu_maps_update_done();
+ return ret;
+}
+#else
+static inline int acpi_processor_hotadd_init(struct acpi_processor *pr)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_ACPI_HOTPLUG_CPU */
+
+static int acpi_processor_get_info(struct acpi_device *device)
+{
+ union acpi_object object = { 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;
+
+ acpi_processor_errata();
+
+ /*
+ * Check to see if we have bus mastering arbitration control. This
+ * is required for proper C3 usage (to maintain cache coherency).
+ */
+ if (acpi_gbl_FADT.pm2_control_block && acpi_gbl_FADT.pm2_control_length) {
+ pr->flags.bm_control = 1;
+ dev_dbg(&device->dev, "Bus mastering arbitration control present\n");
+ } else
+ dev_dbg(&device->dev, "No bus mastering arbitration control\n");
+
+ if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) {
+ /* Declared with "Processor" statement; match ProcessorID */
+ status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&device->dev,
+ "Failed to evaluate processor object (0x%x)\n",
+ status);
+ return -ENODEV;
+ }
+
+ pr->acpi_id = object.processor.proc_id;
+ } else {
+ /*
+ * Declared with "Device" statement; match _UID.
+ */
+ status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID,
+ NULL, &value);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&device->dev,
+ "Failed to evaluate processor _UID (0x%x)\n",
+ status);
+ return -ENODEV;
+ }
+ device_declaration = 1;
+ pr->acpi_id = value;
+ }
+
+ if (acpi_duplicate_processor_id(pr->acpi_id)) {
+ if (pr->acpi_id == 0xff)
+ dev_info_once(&device->dev,
+ "Entry not well-defined, consider updating BIOS\n");
+ else
+ dev_err(&device->dev,
+ "Failed to get unique processor _UID (0x%x)\n",
+ pr->acpi_id);
+ return -ENODEV;
+ }
+
+ pr->phys_id = acpi_get_phys_id(pr->handle, device_declaration,
+ pr->acpi_id);
+ if (invalid_phys_cpuid(pr->phys_id))
+ 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()) {
+ 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))
+ pr->id = 0;
+ }
+
+ /*
+ * 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.
+ */
+ if (invalid_logical_cpuid(pr->id) || !cpu_present(pr->id)) {
+ int ret = acpi_processor_hotadd_init(pr);
+
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * On some boxes several processors use the same processor bus id.
+ * But they are located in different scope. For example:
+ * \_SB.SCK0.CPU0
+ * \_SB.SCK1.CPU0
+ * Rename the processor device bus id. And the new bus id will be
+ * generated as the following format:
+ * CPU+CPU ID.
+ */
+ sprintf(acpi_device_bid(device), "CPU%X", pr->id);
+ dev_dbg(&device->dev, "Processor [%d:%d]\n", pr->id, pr->acpi_id);
+
+ if (!object.processor.pblk_address)
+ dev_dbg(&device->dev, "No PBLK (NULL address)\n");
+ else if (object.processor.pblk_length != 6)
+ dev_err(&device->dev, "Invalid PBLK length [%d]\n",
+ object.processor.pblk_length);
+ else {
+ pr->throttling.address = object.processor.pblk_address;
+ pr->throttling.duty_offset = acpi_gbl_FADT.duty_offset;
+ pr->throttling.duty_width = acpi_gbl_FADT.duty_width;
+
+ pr->pblk = object.processor.pblk_address;
+ }
+
+ /*
+ * If ACPI describes a slot number for this CPU, we can use it to
+ * ensure we get the right value in the "physical id" field
+ * of /proc/cpuinfo
+ */
+ status = acpi_evaluate_integer(pr->handle, "_SUN", NULL, &value);
+ if (ACPI_SUCCESS(status))
+ arch_fix_phys_package_id(pr->id, value);
+
+ return 0;
+}
+
+/*
+ * Do not put anything in here which needs the core to be online.
+ * For example MSR access or setting up things which check for cpuinfo_x86
+ * (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)
+{
+ struct acpi_processor *pr;
+ struct device *dev;
+ int result = 0;
+
+ pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL);
+ if (!pr)
+ return -ENOMEM;
+
+ if (!zalloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) {
+ result = -ENOMEM;
+ goto err_free_pr;
+ }
+
+ pr->handle = device->handle;
+ strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME);
+ strcpy(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;
+
+ dev = get_cpu_device(pr->id);
+ if (!dev) {
+ result = -ENODEV;
+ goto err;
+ }
+
+ result = acpi_bind_one(dev, device);
+ if (result)
+ goto err;
+
+ pr->dev = dev;
+
+ /* Trigger the processor driver's .probe() if present. */
+ if (device_attach(dev) >= 0)
+ return 1;
+
+ 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;
+ per_cpu(processors, pr->id) = NULL;
+ err_free_pr:
+ kfree(pr);
+ return result;
+}
+
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+/* Removal */
+static void acpi_processor_remove(struct acpi_device *device)
+{
+ struct acpi_processor *pr;
+
+ if (!device || !acpi_driver_data(device))
+ return;
+
+ pr = acpi_driver_data(device);
+ if (pr->id >= nr_cpu_ids)
+ goto out;
+
+ /*
+ * The only reason why we ever get here is CPU hot-removal. The CPU is
+ * already offline and the ACPI device removal locking prevents it from
+ * being put back online at this point.
+ *
+ * Unbind the driver from the processor device and detach it from the
+ * ACPI companion object.
+ */
+ 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();
+
+ /* Remove the CPU. */
+ arch_unregister_cpu(pr->id);
+ acpi_unmap_cpu(pr->id);
+
+ cpus_write_unlock();
+ cpu_maps_update_done();
+
+ try_offline_node(cpu_to_node(pr->id));
+
+ out:
+ free_cpumask_var(pr->throttling.shared_cpu_map);
+ kfree(pr);
+}
+#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)
+{
+ u8 sb_uuid_str[] = "4077A616-290C-47BE-9EBD-D87058713953";
+ u32 capbuf[2];
+ struct acpi_osc_context osc_context = {
+ .uuid_str = sb_uuid_str,
+ .rev = 1,
+ .cap.length = 8,
+ .cap.pointer = capbuf,
+ };
+
+ if (acpi_hwp_native_thermal_lvt_set)
+ return AE_CTRL_TERMINATE;
+
+ capbuf[0] = 0x0000;
+ capbuf[1] = 0x1000; /* set bit 12 */
+
+ 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;
+
+ 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);
+ }
+
+ return AE_OK;
+}
+
+void __init acpi_early_processor_osc(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);
+ }
+}
+#endif
+
+/*
+ * The following ACPI IDs are known to be suitable for representing as
+ * processor devices.
+ */
+static const struct acpi_device_id processor_device_ids[] = {
+
+ { ACPI_PROCESSOR_OBJECT_HID, },
+ { ACPI_PROCESSOR_DEVICE_HID, },
+
+ { }
+};
+
+static struct acpi_scan_handler processor_handler = {
+ .ids = processor_device_ids,
+ .attach = acpi_processor_add,
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+ .detach = acpi_processor_remove,
+#endif
+ .hotplug = {
+ .enabled = true,
+ },
+};
+
+static int acpi_processor_container_attach(struct acpi_device *dev,
+ const struct acpi_device_id *id)
+{
+ return 1;
+}
+
+static const struct acpi_device_id processor_container_ids[] = {
+ { ACPI_PROCESSOR_CONTAINER_HID, },
+ { }
+};
+
+static struct acpi_scan_handler processor_container_handler = {
+ .ids = processor_container_ids,
+ .attach = acpi_processor_container_attach,
+};
+
+/* The number of the unique processor IDs */
+static int nr_unique_ids __initdata;
+
+/* The number of the duplicate processor IDs */
+static int nr_duplicate_ids;
+
+/* Used to store the unique processor IDs */
+static int unique_processor_ids[] __initdata = {
+ [0 ... NR_CPUS - 1] = -1,
+};
+
+/* Used to store the duplicate processor IDs */
+static int duplicate_processor_ids[] = {
+ [0 ... NR_CPUS - 1] = -1,
+};
+
+static void __init processor_validated_ids_update(int proc_id)
+{
+ int i;
+
+ if (nr_unique_ids == NR_CPUS||nr_duplicate_ids == NR_CPUS)
+ return;
+
+ /*
+ * Firstly, compare the proc_id with duplicate IDs, if the proc_id is
+ * already in the IDs, do nothing.
+ */
+ for (i = 0; i < nr_duplicate_ids; i++) {
+ if (duplicate_processor_ids[i] == proc_id)
+ return;
+ }
+
+ /*
+ * Secondly, compare the proc_id with unique IDs, if the proc_id is in
+ * the IDs, put it in the duplicate IDs.
+ */
+ for (i = 0; i < nr_unique_ids; i++) {
+ if (unique_processor_ids[i] == proc_id) {
+ duplicate_processor_ids[nr_duplicate_ids] = proc_id;
+ nr_duplicate_ids++;
+ return;
+ }
+ }
+
+ /*
+ * Lastly, the proc_id is a unique ID, put it in the unique IDs.
+ */
+ unique_processor_ids[nr_unique_ids] = proc_id;
+ nr_unique_ids++;
+}
+
+static acpi_status __init acpi_processor_ids_walk(acpi_handle handle,
+ u32 lvl,
+ void *context,
+ void **rv)
+{
+ acpi_status status;
+ acpi_object_type acpi_type;
+ unsigned long long uid;
+ 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 status;
+
+ switch (acpi_type) {
+ case ACPI_TYPE_PROCESSOR:
+ status = acpi_evaluate_object(handle, NULL, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ goto err;
+ uid = object.processor.proc_id;
+ break;
+
+ case ACPI_TYPE_DEVICE:
+ status = acpi_evaluate_integer(handle, "_UID", NULL, &uid);
+ if (ACPI_FAILURE(status))
+ goto err;
+ break;
+ default:
+ goto err;
+ }
+
+ processor_validated_ids_update(uid);
+ return AE_OK;
+
+err:
+ /* Exit on error, but don't abort the namespace walk */
+ acpi_handle_info(handle, "Invalid processor object\n");
+ return AE_OK;
+
+}
+
+static void __init acpi_processor_check_duplicates(void)
+{
+ /* check the correctness for all processors in ACPI namespace */
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ acpi_processor_ids_walk,
+ NULL, NULL, NULL);
+ acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID, acpi_processor_ids_walk,
+ NULL, NULL);
+}
+
+bool acpi_duplicate_processor_id(int proc_id)
+{
+ int i;
+
+ /*
+ * compare the proc_id with duplicate IDs, if the proc_id is already
+ * in the duplicate IDs, return true, otherwise, return false.
+ */
+ for (i = 0; i < nr_duplicate_ids; i++) {
+ if (duplicate_processor_ids[i] == proc_id)
+ return true;
+ }
+ return false;
+}
+
+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);
+}
+
+#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
+/**
+ * acpi_processor_claim_cst_control - Request _CST control from the platform.
+ */
+bool acpi_processor_claim_cst_control(void)
+{
+ static bool cst_control_claimed;
+ acpi_status status;
+
+ if (!acpi_gbl_FADT.cst_control || cst_control_claimed)
+ return true;
+
+ status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
+ acpi_gbl_FADT.cst_control, 8);
+ if (ACPI_FAILURE(status)) {
+ pr_warn("ACPI: Failed to claim processor _CST control\n");
+ return false;
+ }
+
+ cst_control_claimed = true;
+ return true;
+}
+EXPORT_SYMBOL_GPL(acpi_processor_claim_cst_control);
+
+/**
+ * acpi_processor_evaluate_cst - Evaluate the processor _CST control method.
+ * @handle: ACPI handle of the processor object containing the _CST.
+ * @cpu: The numeric ID of the target CPU.
+ * @info: Object write the C-states information into.
+ *
+ * Extract the C-state information for the given CPU from the output of the _CST
+ * control method under the corresponding ACPI processor object (or processor
+ * device object) and populate @info with it.
+ *
+ * If any ACPI_ADR_SPACE_FIXED_HARDWARE C-states are found, invoke
+ * acpi_processor_ffh_cstate_probe() to verify them and update the
+ * cpu_cstate_entry data for @cpu.
+ */
+int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu,
+ struct acpi_processor_power *info)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *cst;
+ acpi_status status;
+ u64 count;
+ int last_index = 0;
+ int i, ret = 0;
+
+ status = acpi_evaluate_object(handle, "_CST", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_debug(handle, "No _CST\n");
+ return -ENODEV;
+ }
+
+ cst = buffer.pointer;
+
+ /* There must be at least 2 elements. */
+ if (!cst || cst->type != ACPI_TYPE_PACKAGE || cst->package.count < 2) {
+ acpi_handle_warn(handle, "Invalid _CST output\n");
+ ret = -EFAULT;
+ goto end;
+ }
+
+ count = cst->package.elements[0].integer.value;
+
+ /* Validate the number of C-states. */
+ if (count < 1 || count != cst->package.count - 1) {
+ acpi_handle_warn(handle, "Inconsistent _CST data\n");
+ ret = -EFAULT;
+ goto end;
+ }
+
+ for (i = 1; i <= count; i++) {
+ union acpi_object *element;
+ union acpi_object *obj;
+ struct acpi_power_register *reg;
+ struct acpi_processor_cx cx;
+
+ /*
+ * If there is not enough space for all C-states, skip the
+ * excess ones and log a warning.
+ */
+ if (last_index >= ACPI_PROCESSOR_MAX_POWER - 1) {
+ acpi_handle_warn(handle,
+ "No room for more idle states (limit: %d)\n",
+ ACPI_PROCESSOR_MAX_POWER - 1);
+ break;
+ }
+
+ memset(&cx, 0, sizeof(cx));
+
+ element = &cst->package.elements[i];
+ if (element->type != ACPI_TYPE_PACKAGE) {
+ acpi_handle_info(handle, "_CST C%d type(%x) is not package, skip...\n",
+ i, element->type);
+ continue;
+ }
+
+ if (element->package.count != 4) {
+ acpi_handle_info(handle, "_CST C%d package count(%d) is not 4, skip...\n",
+ i, element->package.count);
+ continue;
+ }
+
+ obj = &element->package.elements[0];
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ acpi_handle_info(handle, "_CST C%d package element[0] type(%x) is not buffer, skip...\n",
+ i, obj->type);
+ continue;
+ }
+
+ reg = (struct acpi_power_register *)obj->buffer.pointer;
+
+ obj = &element->package.elements[1];
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ acpi_handle_info(handle, "_CST C[%d] package element[1] type(%x) is not integer, skip...\n",
+ i, obj->type);
+ continue;
+ }
+
+ cx.type = obj->integer.value;
+ /*
+ * There are known cases in which the _CST output does not
+ * contain C1, so if the type of the first state found is not
+ * C1, leave an empty slot for C1 to be filled in later.
+ */
+ if (i == 1 && cx.type != ACPI_STATE_C1)
+ last_index = 1;
+
+ cx.address = reg->address;
+ cx.index = last_index + 1;
+
+ if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
+ if (!acpi_processor_ffh_cstate_probe(cpu, &cx, reg)) {
+ /*
+ * In the majority of cases _CST describes C1 as
+ * a FIXED_HARDWARE C-state, but if the command
+ * line forbids using MWAIT, use CSTATE_HALT for
+ * C1 regardless.
+ */
+ if (cx.type == ACPI_STATE_C1 &&
+ boot_option_idle_override == IDLE_NOMWAIT) {
+ cx.entry_method = ACPI_CSTATE_HALT;
+ snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT");
+ } else {
+ cx.entry_method = ACPI_CSTATE_FFH;
+ }
+ } else if (cx.type == ACPI_STATE_C1) {
+ /*
+ * In the special case of C1, FIXED_HARDWARE can
+ * be handled by executing the HLT instruction.
+ */
+ cx.entry_method = ACPI_CSTATE_HALT;
+ snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT");
+ } else {
+ acpi_handle_info(handle, "_CST C%d declares FIXED_HARDWARE C-state but not supported in hardware, skip...\n",
+ i);
+ continue;
+ }
+ } else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ cx.entry_method = ACPI_CSTATE_SYSTEMIO;
+ snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI IOPORT 0x%x",
+ cx.address);
+ } else {
+ acpi_handle_info(handle, "_CST C%d space_id(%x) neither FIXED_HARDWARE nor SYSTEM_IO, skip...\n",
+ i, reg->space_id);
+ continue;
+ }
+
+ if (cx.type == ACPI_STATE_C1)
+ cx.valid = 1;
+
+ obj = &element->package.elements[2];
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ acpi_handle_info(handle, "_CST C%d package element[2] type(%x) not integer, skip...\n",
+ i, obj->type);
+ continue;
+ }
+
+ cx.latency = obj->integer.value;
+
+ obj = &element->package.elements[3];
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ acpi_handle_info(handle, "_CST C%d package element[3] type(%x) not integer, skip...\n",
+ i, obj->type);
+ continue;
+ }
+
+ memcpy(&info->states[++last_index], &cx, sizeof(cx));
+ }
+
+ acpi_handle_info(handle, "Found %d idle states\n", last_index);
+
+ info->count = last_index;
+
+end:
+ kfree(buffer.pointer);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_processor_evaluate_cst);
+#endif /* CONFIG_ACPI_PROCESSOR_CSTATE */
diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
new file mode 100644
index 000000000..e9b8e8305
--- /dev/null
+++ b/drivers/acpi/acpi_tad.c
@@ -0,0 +1,674 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ACPI Time and Alarm (TAD) Device Driver
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *
+ * This driver is based on Section 9.18 of the ACPI 6.2 specification revision.
+ *
+ * It only supports the system wakeup capabilities of the TAD.
+ *
+ * Provided are sysfs attributes, available under the TAD platform device,
+ * allowing user space to manage the AC and DC wakeup timers of the TAD:
+ * set and read their values, set and check their expire timer wake policies,
+ * check and clear their status and check the capabilities of the TAD reported
+ * by AML. The DC timer attributes are only present if the TAD supports a
+ * separate DC alarm timer.
+ *
+ * The wakeup events handling and power management of the TAD is expected to
+ * be taken care of by the ACPI PM domain attached to its platform device.
+ */
+
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rafael J. Wysocki");
+
+/* ACPI TAD capability flags (ACPI 6.2, Section 9.18.2) */
+#define ACPI_TAD_AC_WAKE BIT(0)
+#define ACPI_TAD_DC_WAKE BIT(1)
+#define ACPI_TAD_RT BIT(2)
+#define ACPI_TAD_RT_IN_MS BIT(3)
+#define ACPI_TAD_S4_S5__GWS BIT(4)
+#define ACPI_TAD_AC_S4_WAKE BIT(5)
+#define ACPI_TAD_AC_S5_WAKE BIT(6)
+#define ACPI_TAD_DC_S4_WAKE BIT(7)
+#define ACPI_TAD_DC_S5_WAKE BIT(8)
+
+/* ACPI TAD alarm timer selection */
+#define ACPI_TAD_AC_TIMER (u32)0
+#define ACPI_TAD_DC_TIMER (u32)1
+
+/* Special value for disabled timer or expired timer wake policy. */
+#define ACPI_TAD_WAKE_DISABLED (~(u32)0)
+
+struct acpi_tad_driver_data {
+ u32 capabilities;
+};
+
+struct acpi_tad_rt {
+ u16 year; /* 1900 - 9999 */
+ u8 month; /* 1 - 12 */
+ u8 day; /* 1 - 31 */
+ u8 hour; /* 0 - 23 */
+ u8 minute; /* 0 - 59 */
+ u8 second; /* 0 - 59 */
+ u8 valid; /* 0 (failed) or 1 (success) for reads, 0 for writes */
+ u16 msec; /* 1 - 1000 */
+ s16 tz; /* -1440 to 1440 or 2047 (unspecified) */
+ u8 daylight;
+ u8 padding[3]; /* must be 0 */
+} __packed;
+
+static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object args[] = {
+ { .type = ACPI_TYPE_BUFFER, },
+ };
+ struct acpi_object_list arg_list = {
+ .pointer = args,
+ .count = ARRAY_SIZE(args),
+ };
+ unsigned long long retval;
+ acpi_status status;
+
+ if (rt->year < 1900 || rt->year > 9999 ||
+ rt->month < 1 || rt->month > 12 ||
+ rt->hour > 23 || rt->minute > 59 || rt->second > 59 ||
+ rt->tz < -1440 || (rt->tz > 1440 && rt->tz != 2047) ||
+ rt->daylight > 3)
+ return -ERANGE;
+
+ args[0].buffer.pointer = (u8 *)rt;
+ args[0].buffer.length = sizeof(*rt);
+
+ pm_runtime_get_sync(dev);
+
+ 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)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER };
+ union acpi_object *out_obj;
+ struct acpi_tad_rt *data;
+ 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;
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_BUFFER)
+ goto out_free;
+
+ if (out_obj->buffer.length != sizeof(*rt))
+ goto out_free;
+
+ data = (struct acpi_tad_rt *)(out_obj->buffer.pointer);
+ if (!data->valid)
+ goto out_free;
+
+ memcpy(rt, data, sizeof(*rt));
+ ret = 0;
+
+out_free:
+ ACPI_FREE(output.pointer);
+ return ret;
+}
+
+static char *acpi_tad_rt_next_field(char *s, int *val)
+{
+ char *p;
+
+ p = strchr(s, ':');
+ if (!p)
+ return NULL;
+
+ *p = '\0';
+ if (kstrtoint(s, 10, val))
+ return NULL;
+
+ return p + 1;
+}
+
+static ssize_t time_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_tad_rt rt;
+ char *str, *s;
+ int val, ret = -ENODATA;
+
+ str = kmemdup_nul(buf, count, GFP_KERNEL);
+ if (!str)
+ return -ENOMEM;
+
+ s = acpi_tad_rt_next_field(str, &val);
+ if (!s)
+ goto out_free;
+
+ rt.year = val;
+
+ s = acpi_tad_rt_next_field(s, &val);
+ if (!s)
+ goto out_free;
+
+ rt.month = val;
+
+ s = acpi_tad_rt_next_field(s, &val);
+ if (!s)
+ goto out_free;
+
+ rt.day = val;
+
+ s = acpi_tad_rt_next_field(s, &val);
+ if (!s)
+ goto out_free;
+
+ rt.hour = val;
+
+ s = acpi_tad_rt_next_field(s, &val);
+ if (!s)
+ goto out_free;
+
+ rt.minute = val;
+
+ s = acpi_tad_rt_next_field(s, &val);
+ if (!s)
+ goto out_free;
+
+ rt.second = val;
+
+ s = acpi_tad_rt_next_field(s, &val);
+ if (!s)
+ goto out_free;
+
+ rt.tz = val;
+
+ if (kstrtoint(s, 10, &val))
+ goto out_free;
+
+ rt.daylight = val;
+
+ rt.valid = 0;
+ rt.msec = 0;
+ memset(rt.padding, 0, 3);
+
+ ret = acpi_tad_set_real_time(dev, &rt);
+
+out_free:
+ kfree(str);
+ return ret ? ret : count;
+}
+
+static ssize_t time_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_tad_rt rt;
+ int ret;
+
+ ret = acpi_tad_get_real_time(dev, &rt);
+ if (ret)
+ return ret;
+
+ return sprintf(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);
+}
+
+static DEVICE_ATTR_RW(time);
+
+static struct attribute *acpi_tad_time_attrs[] = {
+ &dev_attr_time.attr,
+ NULL,
+};
+static const struct attribute_group acpi_tad_time_attr_group = {
+ .attrs = acpi_tad_time_attrs,
+};
+
+static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id,
+ u32 value)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object args[] = {
+ { .type = ACPI_TYPE_INTEGER, },
+ { .type = ACPI_TYPE_INTEGER, },
+ };
+ struct acpi_object_list arg_list = {
+ .pointer = args,
+ .count = ARRAY_SIZE(args),
+ };
+ unsigned long long retval;
+ acpi_status status;
+
+ args[0].integer.value = timer_id;
+ args[1].integer.value = value;
+
+ pm_runtime_get_sync(dev);
+
+ status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
+
+ pm_runtime_put_sync(dev);
+
+ if (ACPI_FAILURE(status) || retval)
+ return -EIO;
+
+ return 0;
+}
+
+static int acpi_tad_wake_write(struct device *dev, const char *buf, char *method,
+ u32 timer_id, const char *specval)
+{
+ u32 value;
+
+ if (sysfs_streq(buf, specval)) {
+ value = ACPI_TAD_WAKE_DISABLED;
+ } else {
+ int ret = kstrtou32(buf, 0, &value);
+
+ if (ret)
+ return ret;
+
+ if (value == ACPI_TAD_WAKE_DISABLED)
+ return -EINVAL;
+ }
+
+ return acpi_tad_wake_set(dev, method, timer_id, value);
+}
+
+static ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method,
+ u32 timer_id, const char *specval)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object args[] = {
+ { .type = ACPI_TYPE_INTEGER, },
+ };
+ struct acpi_object_list arg_list = {
+ .pointer = args,
+ .count = ARRAY_SIZE(args),
+ };
+ unsigned long long retval;
+ acpi_status status;
+
+ args[0].integer.value = timer_id;
+
+ pm_runtime_get_sync(dev);
+
+ status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
+
+ pm_runtime_put_sync(dev);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if ((u32)retval == ACPI_TAD_WAKE_DISABLED)
+ return sprintf(buf, "%s\n", specval);
+
+ return sprintf(buf, "%u\n", (u32)retval);
+}
+
+static const char *alarm_specval = "disabled";
+
+static int acpi_tad_alarm_write(struct device *dev, const char *buf,
+ u32 timer_id)
+{
+ return acpi_tad_wake_write(dev, buf, "_STV", timer_id, alarm_specval);
+}
+
+static ssize_t acpi_tad_alarm_read(struct device *dev, char *buf, u32 timer_id)
+{
+ return acpi_tad_wake_read(dev, buf, "_TIV", timer_id, alarm_specval);
+}
+
+static const char *policy_specval = "never";
+
+static int acpi_tad_policy_write(struct device *dev, const char *buf,
+ u32 timer_id)
+{
+ return acpi_tad_wake_write(dev, buf, "_STP", timer_id, policy_specval);
+}
+
+static ssize_t acpi_tad_policy_read(struct device *dev, char *buf, u32 timer_id)
+{
+ return acpi_tad_wake_read(dev, buf, "_TIP", timer_id, policy_specval);
+}
+
+static int acpi_tad_clear_status(struct device *dev, u32 timer_id)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object args[] = {
+ { .type = ACPI_TYPE_INTEGER, },
+ };
+ struct acpi_object_list arg_list = {
+ .pointer = args,
+ .count = ARRAY_SIZE(args),
+ };
+ unsigned long long retval;
+ acpi_status status;
+
+ args[0].integer.value = timer_id;
+
+ pm_runtime_get_sync(dev);
+
+ status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval);
+
+ pm_runtime_put_sync(dev);
+
+ if (ACPI_FAILURE(status) || retval)
+ return -EIO;
+
+ return 0;
+}
+
+static int acpi_tad_status_write(struct device *dev, const char *buf, u32 timer_id)
+{
+ int ret, value;
+
+ ret = kstrtoint(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ if (value)
+ return -EINVAL;
+
+ return acpi_tad_clear_status(dev, timer_id);
+}
+
+static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object args[] = {
+ { .type = ACPI_TYPE_INTEGER, },
+ };
+ struct acpi_object_list arg_list = {
+ .pointer = args,
+ .count = ARRAY_SIZE(args),
+ };
+ unsigned long long retval;
+ acpi_status status;
+
+ args[0].integer.value = timer_id;
+
+ pm_runtime_get_sync(dev);
+
+ status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval);
+
+ pm_runtime_put_sync(dev);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return sprintf(buf, "0x%02X\n", (u32)retval);
+}
+
+static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "0x%02X\n", dd->capabilities);
+}
+
+static DEVICE_ATTR_RO(caps);
+
+static ssize_t ac_alarm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_AC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t ac_alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_alarm_read(dev, buf, ACPI_TAD_AC_TIMER);
+}
+
+static DEVICE_ATTR_RW(ac_alarm);
+
+static ssize_t ac_policy_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_AC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t ac_policy_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_policy_read(dev, buf, ACPI_TAD_AC_TIMER);
+}
+
+static DEVICE_ATTR_RW(ac_policy);
+
+static ssize_t ac_status_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_AC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t ac_status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_status_read(dev, buf, ACPI_TAD_AC_TIMER);
+}
+
+static DEVICE_ATTR_RW(ac_status);
+
+static struct attribute *acpi_tad_attrs[] = {
+ &dev_attr_caps.attr,
+ &dev_attr_ac_alarm.attr,
+ &dev_attr_ac_policy.attr,
+ &dev_attr_ac_status.attr,
+ NULL,
+};
+static const struct attribute_group acpi_tad_attr_group = {
+ .attrs = acpi_tad_attrs,
+};
+
+static ssize_t dc_alarm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_DC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t dc_alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_alarm_read(dev, buf, ACPI_TAD_DC_TIMER);
+}
+
+static DEVICE_ATTR_RW(dc_alarm);
+
+static ssize_t dc_policy_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_DC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t dc_policy_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_policy_read(dev, buf, ACPI_TAD_DC_TIMER);
+}
+
+static DEVICE_ATTR_RW(dc_policy);
+
+static ssize_t dc_status_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_DC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t dc_status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_status_read(dev, buf, ACPI_TAD_DC_TIMER);
+}
+
+static DEVICE_ATTR_RW(dc_status);
+
+static struct attribute *acpi_tad_dc_attrs[] = {
+ &dev_attr_dc_alarm.attr,
+ &dev_attr_dc_policy.attr,
+ &dev_attr_dc_status.attr,
+ NULL,
+};
+static const struct attribute_group acpi_tad_dc_attr_group = {
+ .attrs = acpi_tad_dc_attrs,
+};
+
+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)
+{
+ struct device *dev = &pdev->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_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);
+ }
+
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ return 0;
+}
+
+static int acpi_tad_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ acpi_handle handle = ACPI_HANDLE(dev);
+ struct acpi_tad_driver_data *dd;
+ acpi_status status;
+ unsigned long long caps;
+ int ret;
+
+ /*
+ * Initialization failure messages are mostly about firmware issues, so
+ * print them at the "info" level.
+ */
+ status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps);
+ if (ACPI_FAILURE(status)) {
+ dev_info(dev, "Unable to get capabilities\n");
+ return -ENODEV;
+ }
+
+ if (!(caps & ACPI_TAD_AC_WAKE)) {
+ dev_info(dev, "Unsupported capabilities\n");
+ return -ENODEV;
+ }
+
+ if (!acpi_has_method(handle, "_PRW")) {
+ dev_info(dev, "Missing _PRW\n");
+ return -ENODEV;
+ }
+
+ dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ dd->capabilities = caps;
+ dev_set_drvdata(dev, dd);
+
+ /*
+ * Assume that the ACPI PM domain has been attached to the device and
+ * simply enable system wakeup and runtime PM and put the device into
+ * runtime suspend. Everything else should be taken care of by the ACPI
+ * PM domain callbacks.
+ */
+ device_init_wakeup(dev, true);
+ dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND |
+ DPM_FLAG_MAY_SKIP_RESUME);
+ /*
+ * The platform bus type layer tells the ACPI PM domain powers up the
+ * device, so set the runtime PM status of it to "active".
+ */
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_suspend(dev);
+
+ ret = sysfs_create_group(&dev->kobj, &acpi_tad_attr_group);
+ if (ret)
+ goto fail;
+
+ if (caps & ACPI_TAD_DC_WAKE) {
+ ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group);
+ if (ret)
+ goto fail;
+ }
+
+ if (caps & ACPI_TAD_RT) {
+ ret = sysfs_create_group(&dev->kobj, &acpi_tad_time_attr_group);
+ if (ret)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ acpi_tad_remove(pdev);
+ return ret;
+}
+
+static const struct acpi_device_id acpi_tad_ids[] = {
+ {"ACPI000E", 0},
+ {}
+};
+
+static struct platform_driver acpi_tad_driver = {
+ .driver = {
+ .name = "acpi-tad",
+ .acpi_match_table = acpi_tad_ids,
+ },
+ .probe = acpi_tad_probe,
+ .remove = acpi_tad_remove,
+};
+MODULE_DEVICE_TABLE(acpi, acpi_tad_ids);
+
+module_platform_driver(acpi_tad_driver);
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c
new file mode 100644
index 000000000..f7852fb75
--- /dev/null
+++ b/drivers/acpi/acpi_video.c
@@ -0,0 +1,2308 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * video.c - ACPI Video Driver
+ *
+ * Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
+ * Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org>
+ * Copyright (C) 2006 Thomas Tuttle <linux-kernel@ttuttle.net>
+ */
+
+#define pr_fmt(fmt) "ACPI: video: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/input.h>
+#include <linux/backlight.h>
+#include <linux/thermal.h>
+#include <linux/sort.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/slab.h>
+#include <linux/dmi.h>
+#include <linux/suspend.h>
+#include <linux/acpi.h>
+#include <acpi/video.h>
+#include <linux/uaccess.h>
+
+#define ACPI_VIDEO_BUS_NAME "Video Bus"
+#define ACPI_VIDEO_DEVICE_NAME "Video Device"
+
+#define MAX_NAME_LEN 20
+
+MODULE_AUTHOR("Bruno Ducrot");
+MODULE_DESCRIPTION("ACPI Video Driver");
+MODULE_LICENSE("GPL");
+
+static bool brightness_switch_enabled = true;
+module_param(brightness_switch_enabled, bool, 0644);
+
+/*
+ * By default, we don't allow duplicate ACPI video bus devices
+ * under the same VGA controller
+ */
+static bool allow_duplicates;
+module_param(allow_duplicates, bool, 0644);
+
+#define REPORT_OUTPUT_KEY_EVENTS 0x01
+#define REPORT_BRIGHTNESS_KEY_EVENTS 0x02
+static int report_key_events = -1;
+module_param(report_key_events, int, 0644);
+MODULE_PARM_DESC(report_key_events,
+ "0: none, 1: output changes, 2: brightness changes, 3: all");
+
+static int hw_changes_brightness = -1;
+module_param(hw_changes_brightness, int, 0644);
+MODULE_PARM_DESC(hw_changes_brightness,
+ "Set this to 1 on buggy hw which changes the brightness itself when "
+ "a hotkey is pressed: -1: auto, 0: normal 1: hw-changes-brightness");
+
+/*
+ * Whether the struct acpi_video_device_attrib::device_id_scheme bit should be
+ * assumed even if not actually set.
+ */
+static bool device_id_scheme = false;
+module_param(device_id_scheme, bool, 0444);
+
+static int only_lcd = -1;
+module_param(only_lcd, int, 0444);
+
+static int register_backlight_delay;
+module_param(register_backlight_delay, int, 0444);
+MODULE_PARM_DESC(register_backlight_delay,
+ "Delay in seconds before doing fallback (non GPU driver triggered) "
+ "backlight registration, set to 0 to disable.");
+
+static bool may_report_brightness_keys;
+static int register_count;
+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);
+static void acpi_video_bus_register_backlight_work(struct work_struct *ignored);
+static DECLARE_DELAYED_WORK(video_bus_register_backlight_work,
+ acpi_video_bus_register_backlight_work);
+
+/*
+ * Indices in the _BCL method response: the first two items are special,
+ * the rest are all supported levels.
+ *
+ * See page 575 of the ACPI spec 3.0
+ */
+enum acpi_video_level_idx {
+ ACPI_VIDEO_AC_LEVEL, /* level when machine has full power */
+ ACPI_VIDEO_BATTERY_LEVEL, /* level when machine is on batteries */
+ ACPI_VIDEO_FIRST_LEVEL, /* actual supported levels begin here */
+};
+
+static const struct acpi_device_id video_device_ids[] = {
+ {ACPI_VIDEO_HID, 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, video_device_ids);
+
+static struct acpi_driver acpi_video_bus = {
+ .name = "video",
+ .class = ACPI_VIDEO_CLASS,
+ .ids = video_device_ids,
+ .ops = {
+ .add = acpi_video_bus_add,
+ .remove = acpi_video_bus_remove,
+ .notify = acpi_video_bus_notify,
+ },
+};
+
+struct acpi_video_bus_flags {
+ u8 multihead:1; /* can switch video heads */
+ u8 rom:1; /* can retrieve a video rom */
+ u8 post:1; /* can configure the head to */
+ u8 reserved:5;
+};
+
+struct acpi_video_bus_cap {
+ u8 _DOS:1; /* Enable/Disable output switching */
+ u8 _DOD:1; /* Enumerate all devices attached to display adapter */
+ u8 _ROM:1; /* Get ROM Data */
+ u8 _GPD:1; /* Get POST Device */
+ u8 _SPD:1; /* Set POST Device */
+ u8 _VPO:1; /* Video POST Options */
+ u8 reserved:2;
+};
+
+struct acpi_video_device_attrib {
+ u32 display_index:4; /* A zero-based instance of the Display */
+ u32 display_port_attachment:4; /* This field differentiates the display type */
+ u32 display_type:4; /* Describe the specific type in use */
+ u32 vendor_specific:4; /* Chipset Vendor Specific */
+ u32 bios_can_detect:1; /* BIOS can detect the device */
+ u32 depend_on_vga:1; /* Non-VGA output device whose power is related to
+ the VGA device. */
+ u32 pipe_id:3; /* For VGA multiple-head devices. */
+ u32 reserved:10; /* Must be 0 */
+
+ /*
+ * The device ID might not actually follow the scheme described by this
+ * struct acpi_video_device_attrib. If it does, then this bit
+ * device_id_scheme is set; otherwise, other fields should be ignored.
+ *
+ * (but also see the global flag device_id_scheme)
+ */
+ u32 device_id_scheme:1;
+};
+
+struct acpi_video_enumerated_device {
+ union {
+ u32 int_val;
+ struct acpi_video_device_attrib attrib;
+ } value;
+ struct acpi_video_device *bind_info;
+};
+
+struct acpi_video_bus {
+ struct acpi_device *device;
+ bool backlight_registered;
+ u8 dos_setting;
+ struct acpi_video_enumerated_device *attached_array;
+ u8 attached_count;
+ u8 child_count;
+ struct acpi_video_bus_cap cap;
+ struct acpi_video_bus_flags flags;
+ struct list_head video_device_list;
+ struct mutex device_list_lock; /* protects video_device_list */
+ struct list_head entry;
+ struct input_dev *input;
+ char phys[32]; /* for input device */
+ struct notifier_block pm_nb;
+};
+
+struct acpi_video_device_flags {
+ u8 crt:1;
+ u8 lcd:1;
+ u8 tvout:1;
+ u8 dvi:1;
+ u8 bios:1;
+ u8 unknown:1;
+ u8 notify:1;
+ u8 reserved:1;
+};
+
+struct acpi_video_device_cap {
+ u8 _ADR:1; /* Return the unique ID */
+ u8 _BCL:1; /* Query list of brightness control levels supported */
+ u8 _BCM:1; /* Set the brightness level */
+ u8 _BQC:1; /* Get current brightness level */
+ u8 _BCQ:1; /* Some buggy BIOS uses _BCQ instead of _BQC */
+ u8 _DDC:1; /* Return the EDID for this device */
+};
+
+struct acpi_video_device {
+ unsigned long device_id;
+ struct acpi_video_device_flags flags;
+ struct acpi_video_device_cap cap;
+ struct list_head entry;
+ struct delayed_work switch_brightness_work;
+ int switch_brightness_event;
+ struct acpi_video_bus *video;
+ struct acpi_device *dev;
+ struct acpi_video_device_brightness *brightness;
+ struct backlight_device *backlight;
+ struct thermal_cooling_device *cooling_dev;
+};
+
+static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data);
+static void acpi_video_device_rebind(struct acpi_video_bus *video);
+static void acpi_video_device_bind(struct acpi_video_bus *video,
+ struct acpi_video_device *device);
+static int acpi_video_device_enumerate(struct acpi_video_bus *video);
+static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,
+ int level);
+static int acpi_video_device_lcd_get_level_current(
+ struct acpi_video_device *device,
+ unsigned long long *level, bool raw);
+static int acpi_video_get_next_level(struct acpi_video_device *device,
+ u32 level_current, u32 event);
+static void acpi_video_switch_brightness(struct work_struct *work);
+
+/* backlight device sysfs support */
+static int acpi_video_get_brightness(struct backlight_device *bd)
+{
+ unsigned long long cur_level;
+ int i;
+ struct acpi_video_device *vd = bl_get_data(bd);
+
+ if (acpi_video_device_lcd_get_level_current(vd, &cur_level, false))
+ return -EINVAL;
+ for (i = ACPI_VIDEO_FIRST_LEVEL; i < vd->brightness->count; i++) {
+ if (vd->brightness->levels[i] == cur_level)
+ return i - ACPI_VIDEO_FIRST_LEVEL;
+ }
+ return 0;
+}
+
+static int acpi_video_set_brightness(struct backlight_device *bd)
+{
+ int request_level = bd->props.brightness + ACPI_VIDEO_FIRST_LEVEL;
+ struct acpi_video_device *vd = bl_get_data(bd);
+
+ cancel_delayed_work(&vd->switch_brightness_work);
+ return acpi_video_device_lcd_set_level(vd,
+ vd->brightness->levels[request_level]);
+}
+
+static const struct backlight_ops acpi_backlight_ops = {
+ .get_brightness = acpi_video_get_brightness,
+ .update_status = acpi_video_set_brightness,
+};
+
+/* thermal cooling device callbacks */
+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);
+
+ *state = video->brightness->count - ACPI_VIDEO_FIRST_LEVEL - 1;
+ return 0;
+}
+
+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);
+ unsigned long long level;
+ int offset;
+
+ if (acpi_video_device_lcd_get_level_current(video, &level, false))
+ return -EINVAL;
+ for (offset = ACPI_VIDEO_FIRST_LEVEL; offset < video->brightness->count;
+ offset++)
+ if (level == video->brightness->levels[offset]) {
+ *state = video->brightness->count - offset - 1;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+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);
+ int level;
+
+ if (state >= video->brightness->count - ACPI_VIDEO_FIRST_LEVEL)
+ return -EINVAL;
+
+ state = video->brightness->count - state;
+ level = video->brightness->levels[state - 1];
+ return acpi_video_device_lcd_set_level(video, level);
+}
+
+static const struct thermal_cooling_device_ops video_cooling_ops = {
+ .get_max_state = video_get_max_state,
+ .get_cur_state = video_get_cur_state,
+ .set_cur_state = video_set_cur_state,
+};
+
+/*
+ * --------------------------------------------------------------------------
+ * Video Management
+ * --------------------------------------------------------------------------
+ */
+
+static int
+acpi_video_device_lcd_query_levels(acpi_handle handle,
+ union acpi_object **levels)
+{
+ int status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+
+
+ *levels = NULL;
+
+ status = acpi_evaluate_object(handle, "_BCL", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return status;
+ obj = (union acpi_object *)buffer.pointer;
+ if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
+ acpi_handle_info(handle, "Invalid _BCL data\n");
+ status = -EFAULT;
+ goto err;
+ }
+
+ *levels = obj;
+
+ return 0;
+
+err:
+ kfree(buffer.pointer);
+
+ return status;
+}
+
+static int
+acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
+{
+ int status;
+ int state;
+
+ status = acpi_execute_simple_method(device->dev->handle,
+ "_BCM", level);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_info(device->dev->handle, "_BCM evaluation failed\n");
+ return -EIO;
+ }
+
+ device->brightness->curr = level;
+ for (state = ACPI_VIDEO_FIRST_LEVEL; state < device->brightness->count;
+ state++)
+ if (level == device->brightness->levels[state]) {
+ if (device->backlight)
+ device->backlight->props.brightness =
+ state - ACPI_VIDEO_FIRST_LEVEL;
+ return 0;
+ }
+
+ acpi_handle_info(device->dev->handle, "Current brightness invalid\n");
+ return -EINVAL;
+}
+
+/*
+ * For some buggy _BQC methods, we need to add a constant value to
+ * the _BQC return value to get the actual current brightness level
+ */
+
+static int bqc_offset_aml_bug_workaround;
+static int video_set_bqc_offset(const struct dmi_system_id *d)
+{
+ bqc_offset_aml_bug_workaround = 9;
+ return 0;
+}
+
+static int video_set_device_id_scheme(const struct dmi_system_id *d)
+{
+ device_id_scheme = true;
+ return 0;
+}
+
+static int video_enable_only_lcd(const struct dmi_system_id *d)
+{
+ only_lcd = true;
+ return 0;
+}
+
+static int video_set_report_key_events(const struct dmi_system_id *id)
+{
+ if (report_key_events == -1)
+ report_key_events = (uintptr_t)id->driver_data;
+ return 0;
+}
+
+static int video_hw_changes_brightness(
+ const struct dmi_system_id *d)
+{
+ if (hw_changes_brightness == -1)
+ hw_changes_brightness = 1;
+ return 0;
+}
+
+static const struct dmi_system_id video_dmi_table[] = {
+ /*
+ * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121
+ */
+ {
+ .callback = video_set_bqc_offset,
+ .ident = "Acer Aspire 5720",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
+ },
+ },
+ {
+ .callback = video_set_bqc_offset,
+ .ident = "Acer Aspire 5710Z",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"),
+ },
+ },
+ {
+ .callback = video_set_bqc_offset,
+ .ident = "eMachines E510",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"),
+ },
+ },
+ {
+ .callback = video_set_bqc_offset,
+ .ident = "Acer Aspire 5315",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
+ },
+ },
+ {
+ .callback = video_set_bqc_offset,
+ .ident = "Acer Aspire 7720",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
+ },
+ },
+
+ /*
+ * Some machine's _DOD IDs don't have bit 31(Device ID Scheme) set
+ * but the IDs actually follow the Device ID Scheme.
+ */
+ {
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=104121 */
+ .callback = video_set_device_id_scheme,
+ .ident = "ESPRIMO Mobile M9410",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile M9410"),
+ },
+ },
+ /*
+ * Some machines have multiple video output devices, but only the one
+ * that is the type of LCD can do the backlight control so we should not
+ * register backlight interface for other video output devices.
+ */
+ {
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=104121 */
+ .callback = video_enable_only_lcd,
+ .ident = "ESPRIMO Mobile M9410",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile M9410"),
+ },
+ },
+ /*
+ * Some machines report wrong key events on the acpi-bus, suppress
+ * key event reporting on these. Note this is only intended to work
+ * around events which are plain wrong. In some cases we get double
+ * events, in this case acpi-video is considered the canonical source
+ * and the events from the other source should be filtered. E.g.
+ * by calling acpi_video_handles_brightness_key_presses() from the
+ * vendor acpi/wmi driver or by using /lib/udev/hwdb.d/60-keyboard.hwdb
+ */
+ {
+ .callback = video_set_report_key_events,
+ .driver_data = (void *)((uintptr_t)REPORT_OUTPUT_KEY_EVENTS),
+ .ident = "Dell Vostro V131",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
+ },
+ },
+ {
+ .callback = video_set_report_key_events,
+ .driver_data = (void *)((uintptr_t)REPORT_BRIGHTNESS_KEY_EVENTS),
+ .ident = "Dell Vostro 3350",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"),
+ },
+ },
+ /*
+ * Some machines change the brightness themselves when a brightness
+ * hotkey gets pressed, despite us telling them not to. In this case
+ * acpi_video_device_notify() should only call backlight_force_update(
+ * BACKLIGHT_UPDATE_HOTKEY) and not do anything else.
+ */
+ {
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=204077 */
+ .callback = video_hw_changes_brightness,
+ .ident = "Packard Bell EasyNote MZ35",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Packard Bell"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "EasyNote MZ35"),
+ },
+ },
+ {}
+};
+
+static unsigned long long
+acpi_video_bqc_value_to_level(struct acpi_video_device *device,
+ unsigned long long bqc_value)
+{
+ unsigned long long level;
+
+ if (device->brightness->flags._BQC_use_index) {
+ /*
+ * _BQC returns an index that doesn't account for the first 2
+ * items with special meaning (see enum acpi_video_level_idx),
+ * so we need to compensate for that by offsetting ourselves
+ */
+ if (device->brightness->flags._BCL_reversed)
+ bqc_value = device->brightness->count -
+ ACPI_VIDEO_FIRST_LEVEL - 1 - bqc_value;
+
+ level = device->brightness->levels[bqc_value +
+ ACPI_VIDEO_FIRST_LEVEL];
+ } else {
+ level = bqc_value;
+ }
+
+ level += bqc_offset_aml_bug_workaround;
+
+ return level;
+}
+
+static int
+acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
+ unsigned long long *level, bool raw)
+{
+ acpi_status status = AE_OK;
+ int i;
+
+ if (device->cap._BQC || device->cap._BCQ) {
+ char *buf = device->cap._BQC ? "_BQC" : "_BCQ";
+
+ status = acpi_evaluate_integer(device->dev->handle, buf,
+ NULL, level);
+ if (ACPI_SUCCESS(status)) {
+ if (raw) {
+ /*
+ * Caller has indicated he wants the raw
+ * value returned by _BQC, so don't furtherly
+ * mess with the value.
+ */
+ return 0;
+ }
+
+ *level = acpi_video_bqc_value_to_level(device, *level);
+
+ for (i = ACPI_VIDEO_FIRST_LEVEL;
+ i < device->brightness->count; i++)
+ if (device->brightness->levels[i] == *level) {
+ device->brightness->curr = *level;
+ return 0;
+ }
+ /*
+ * BQC returned an invalid level.
+ * Stop using it.
+ */
+ acpi_handle_info(device->dev->handle,
+ "%s returned an invalid level", buf);
+ device->cap._BQC = device->cap._BCQ = 0;
+ } else {
+ /*
+ * Fixme:
+ * should we return an error or ignore this failure?
+ * dev->brightness->curr is a cached value which stores
+ * the correct current backlight level in most cases.
+ * ACPI video backlight still works w/ buggy _BQC.
+ * http://bugzilla.kernel.org/show_bug.cgi?id=12233
+ */
+ acpi_handle_info(device->dev->handle,
+ "%s evaluation failed", buf);
+ device->cap._BQC = device->cap._BCQ = 0;
+ }
+ }
+
+ *level = device->brightness->curr;
+ return 0;
+}
+
+static int
+acpi_video_device_EDID(struct acpi_video_device *device,
+ union acpi_object **edid, ssize_t length)
+{
+ int 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 };
+
+
+ *edid = NULL;
+
+ if (!device)
+ return -ENODEV;
+ if (length == 128)
+ arg0.integer.value = 1;
+ else if (length == 256)
+ arg0.integer.value = 2;
+ else
+ return -EINVAL;
+
+ 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);
+ }
+
+ return status;
+}
+
+/* bus */
+
+/*
+ * Arg:
+ * video : video bus device pointer
+ * bios_flag :
+ * 0. The system BIOS should NOT automatically switch(toggle)
+ * the active display output.
+ * 1. The system BIOS should automatically switch (toggle) the
+ * active display output. No switch event.
+ * 2. The _DGS value should be locked.
+ * 3. The system BIOS should not automatically switch (toggle) the
+ * active display output, but instead generate the display switch
+ * event notify code.
+ * lcd_flag :
+ * 0. The system BIOS should automatically control the brightness level
+ * of the LCD when:
+ * - the power changes from AC to DC (ACPI appendix B)
+ * - a brightness hotkey gets pressed (implied by Win7/8 backlight docs)
+ * 1. The system BIOS should NOT automatically control the brightness
+ * level of the LCD when:
+ * - the power changes from AC to DC (ACPI appendix B)
+ * - a brightness hotkey gets pressed (implied by Win7/8 backlight docs)
+ * Return Value:
+ * -EINVAL wrong arg.
+ */
+
+static int
+acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
+{
+ acpi_status status;
+
+ if (!video->cap._DOS)
+ return 0;
+
+ if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1)
+ return -EINVAL;
+ video->dos_setting = (lcd_flag << 2) | bios_flag;
+ status = acpi_execute_simple_method(video->device->handle, "_DOS",
+ (lcd_flag << 2) | bios_flag);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Simple comparison function used to sort backlight levels.
+ */
+
+static int
+acpi_video_cmp_level(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+/*
+ * Decides if _BQC/_BCQ for this system is usable
+ *
+ * We do this by changing the level first and then read out the current
+ * brightness level, if the value does not match, find out if it is using
+ * index. If not, clear the _BQC/_BCQ capability.
+ */
+static int acpi_video_bqc_quirk(struct acpi_video_device *device,
+ int max_level, int current_level)
+{
+ struct acpi_video_device_brightness *br = device->brightness;
+ int result;
+ unsigned long long level;
+ int test_level;
+
+ /* don't mess with existing known broken systems */
+ if (bqc_offset_aml_bug_workaround)
+ return 0;
+
+ /*
+ * Some systems always report current brightness level as maximum
+ * through _BQC, we need to test another value for them. However,
+ * there is a subtlety:
+ *
+ * If the _BCL package ordering is descending, the first level
+ * (br->levels[2]) is likely to be 0, and if the number of levels
+ * matches the number of steps, we might confuse a returned level to
+ * mean the index.
+ *
+ * For example:
+ *
+ * current_level = max_level = 100
+ * test_level = 0
+ * returned level = 100
+ *
+ * In this case 100 means the level, not the index, and _BCM failed.
+ * Still, if the _BCL package ordering is descending, the index of
+ * level 0 is also 100, so we assume _BQC is indexed, when it's not.
+ *
+ * This causes all _BQC calls to return bogus values causing weird
+ * behavior from the user's perspective. For example:
+ *
+ * xbacklight -set 10; xbacklight -set 20;
+ *
+ * would flash to 90% and then slowly down to the desired level (20).
+ *
+ * The solution is simple; test anything other than the first level
+ * (e.g. 1).
+ */
+ test_level = current_level == max_level
+ ? br->levels[ACPI_VIDEO_FIRST_LEVEL + 1]
+ : max_level;
+
+ result = acpi_video_device_lcd_set_level(device, test_level);
+ if (result)
+ return result;
+
+ result = acpi_video_device_lcd_get_level_current(device, &level, true);
+ if (result)
+ return result;
+
+ if (level != test_level) {
+ /* buggy _BQC found, need to find out if it uses index */
+ if (level < br->count) {
+ if (br->flags._BCL_reversed)
+ level = br->count - ACPI_VIDEO_FIRST_LEVEL - 1 - level;
+ if (br->levels[level + ACPI_VIDEO_FIRST_LEVEL] == test_level)
+ br->flags._BQC_use_index = 1;
+ }
+
+ if (!br->flags._BQC_use_index)
+ device->cap._BQC = device->cap._BCQ = 0;
+ }
+
+ return 0;
+}
+
+int acpi_video_get_levels(struct acpi_device *device,
+ struct acpi_video_device_brightness **dev_br,
+ int *pmax_level)
+{
+ union acpi_object *obj = NULL;
+ int i, max_level = 0, count = 0, level_ac_battery = 0;
+ union acpi_object *o;
+ struct acpi_video_device_brightness *br = NULL;
+ int result = 0;
+ u32 value;
+
+ if (ACPI_FAILURE(acpi_video_device_lcd_query_levels(device->handle, &obj))) {
+ acpi_handle_debug(device->handle,
+ "Could not query available LCD brightness level\n");
+ result = -ENODEV;
+ goto out;
+ }
+
+ if (obj->package.count < ACPI_VIDEO_FIRST_LEVEL) {
+ result = -EINVAL;
+ goto out;
+ }
+
+ br = kzalloc(sizeof(*br), GFP_KERNEL);
+ if (!br) {
+ result = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Note that we have to reserve 2 extra items (ACPI_VIDEO_FIRST_LEVEL),
+ * in order to account for buggy BIOS which don't export the first two
+ * special levels (see below)
+ */
+ br->levels = kmalloc_array(obj->package.count + ACPI_VIDEO_FIRST_LEVEL,
+ sizeof(*br->levels),
+ GFP_KERNEL);
+ if (!br->levels) {
+ result = -ENOMEM;
+ goto out_free;
+ }
+
+ for (i = 0; i < obj->package.count; i++) {
+ o = (union acpi_object *)&obj->package.elements[i];
+ if (o->type != ACPI_TYPE_INTEGER) {
+ acpi_handle_info(device->handle, "Invalid data\n");
+ continue;
+ }
+ value = (u32) o->integer.value;
+ /* Skip duplicate entries */
+ if (count > ACPI_VIDEO_FIRST_LEVEL
+ && br->levels[count - 1] == value)
+ continue;
+
+ br->levels[count] = value;
+
+ if (br->levels[count] > max_level)
+ max_level = br->levels[count];
+ count++;
+ }
+
+ /*
+ * some buggy BIOS don't export the levels
+ * when machine is on AC/Battery in _BCL package.
+ * In this case, the first two elements in _BCL packages
+ * are also supported brightness levels that OS should take care of.
+ */
+ for (i = ACPI_VIDEO_FIRST_LEVEL; i < count; i++) {
+ if (br->levels[i] == br->levels[ACPI_VIDEO_AC_LEVEL])
+ level_ac_battery++;
+ if (br->levels[i] == br->levels[ACPI_VIDEO_BATTERY_LEVEL])
+ level_ac_battery++;
+ }
+
+ if (level_ac_battery < ACPI_VIDEO_FIRST_LEVEL) {
+ level_ac_battery = ACPI_VIDEO_FIRST_LEVEL - level_ac_battery;
+ br->flags._BCL_no_ac_battery_levels = 1;
+ for (i = (count - 1 + level_ac_battery);
+ i >= ACPI_VIDEO_FIRST_LEVEL; i--)
+ br->levels[i] = br->levels[i - level_ac_battery];
+ count += level_ac_battery;
+ } else if (level_ac_battery > ACPI_VIDEO_FIRST_LEVEL)
+ acpi_handle_info(device->handle,
+ "Too many duplicates in _BCL package");
+
+ /* Check if the _BCL package is in a reversed order */
+ if (max_level == br->levels[ACPI_VIDEO_FIRST_LEVEL]) {
+ br->flags._BCL_reversed = 1;
+ sort(&br->levels[ACPI_VIDEO_FIRST_LEVEL],
+ count - ACPI_VIDEO_FIRST_LEVEL,
+ sizeof(br->levels[ACPI_VIDEO_FIRST_LEVEL]),
+ acpi_video_cmp_level, NULL);
+ } else if (max_level != br->levels[count - 1])
+ acpi_handle_info(device->handle,
+ "Found unordered _BCL package");
+
+ br->count = count;
+ *dev_br = br;
+ if (pmax_level)
+ *pmax_level = max_level;
+
+out:
+ kfree(obj);
+ return result;
+out_free:
+ kfree(br);
+ goto out;
+}
+EXPORT_SYMBOL(acpi_video_get_levels);
+
+/*
+ * Arg:
+ * device : video output device (LCD, CRT, ..)
+ *
+ * Return Value:
+ * Maximum brightness level
+ *
+ * Allocate and initialize device->brightness.
+ */
+
+static int
+acpi_video_init_brightness(struct acpi_video_device *device)
+{
+ int i, max_level = 0;
+ unsigned long long level, level_old;
+ struct acpi_video_device_brightness *br = NULL;
+ int result;
+
+ result = acpi_video_get_levels(device->dev, &br, &max_level);
+ if (result)
+ return result;
+ device->brightness = br;
+
+ /* _BQC uses INDEX while _BCL uses VALUE in some laptops */
+ br->curr = level = max_level;
+
+ if (!device->cap._BQC)
+ goto set_level;
+
+ result = acpi_video_device_lcd_get_level_current(device,
+ &level_old, true);
+ if (result)
+ goto out_free_levels;
+
+ result = acpi_video_bqc_quirk(device, max_level, level_old);
+ if (result)
+ goto out_free_levels;
+ /*
+ * cap._BQC may get cleared due to _BQC is found to be broken
+ * in acpi_video_bqc_quirk, so check again here.
+ */
+ if (!device->cap._BQC)
+ goto set_level;
+
+ level = acpi_video_bqc_value_to_level(device, level_old);
+ /*
+ * On some buggy laptops, _BQC returns an uninitialized
+ * value when invoked for the first time, i.e.
+ * level_old is invalid (no matter whether it's a level
+ * or an index). Set the backlight to max_level in this case.
+ */
+ for (i = ACPI_VIDEO_FIRST_LEVEL; i < br->count; i++)
+ if (level == br->levels[i])
+ break;
+ if (i == br->count || !level)
+ level = max_level;
+
+set_level:
+ result = acpi_video_device_lcd_set_level(device, level);
+ if (result)
+ goto out_free_levels;
+
+ acpi_handle_debug(device->dev->handle, "found %d brightness levels\n",
+ br->count - ACPI_VIDEO_FIRST_LEVEL);
+
+ return 0;
+
+out_free_levels:
+ kfree(br->levels);
+ kfree(br);
+ device->brightness = NULL;
+ return result;
+}
+
+/*
+ * Arg:
+ * device : video output device (LCD, CRT, ..)
+ *
+ * Return Value:
+ * None
+ *
+ * Find out all required AML methods defined under the output
+ * device.
+ */
+
+static void acpi_video_device_find_cap(struct acpi_video_device *device)
+{
+ if (acpi_has_method(device->dev->handle, "_ADR"))
+ device->cap._ADR = 1;
+ if (acpi_has_method(device->dev->handle, "_BCL"))
+ device->cap._BCL = 1;
+ if (acpi_has_method(device->dev->handle, "_BCM"))
+ device->cap._BCM = 1;
+ if (acpi_has_method(device->dev->handle, "_BQC")) {
+ device->cap._BQC = 1;
+ } else if (acpi_has_method(device->dev->handle, "_BCQ")) {
+ acpi_handle_info(device->dev->handle,
+ "_BCQ is used instead of _BQC\n");
+ device->cap._BCQ = 1;
+ }
+
+ if (acpi_has_method(device->dev->handle, "_DDC"))
+ device->cap._DDC = 1;
+}
+
+/*
+ * Arg:
+ * device : video output device (VGA)
+ *
+ * Return Value:
+ * None
+ *
+ * Find out all required AML methods defined under the video bus device.
+ */
+
+static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
+{
+ if (acpi_has_method(video->device->handle, "_DOS"))
+ video->cap._DOS = 1;
+ if (acpi_has_method(video->device->handle, "_DOD"))
+ video->cap._DOD = 1;
+ if (acpi_has_method(video->device->handle, "_ROM"))
+ video->cap._ROM = 1;
+ if (acpi_has_method(video->device->handle, "_GPD"))
+ video->cap._GPD = 1;
+ if (acpi_has_method(video->device->handle, "_SPD"))
+ video->cap._SPD = 1;
+ if (acpi_has_method(video->device->handle, "_VPO"))
+ video->cap._VPO = 1;
+}
+
+/*
+ * Check whether the video bus device has required AML method to
+ * support the desired features
+ */
+
+static int acpi_video_bus_check(struct acpi_video_bus *video)
+{
+ acpi_status status = -ENOENT;
+ struct pci_dev *dev;
+
+ if (!video)
+ return -EINVAL;
+
+ dev = acpi_get_pci_dev(video->device->handle);
+ if (!dev)
+ return -ENODEV;
+ pci_dev_put(dev);
+
+ /*
+ * Since there is no HID, CID and so on for VGA driver, we have
+ * to check well known required nodes.
+ */
+
+ /* Does this device support video switching? */
+ if (video->cap._DOS || video->cap._DOD) {
+ if (!video->cap._DOS) {
+ pr_info(FW_BUG "ACPI(%s) defines _DOD but not _DOS\n",
+ acpi_device_bid(video->device));
+ }
+ video->flags.multihead = 1;
+ status = 0;
+ }
+
+ /* Does this device support retrieving a video ROM? */
+ if (video->cap._ROM) {
+ video->flags.rom = 1;
+ status = 0;
+ }
+
+ /* Does this device support configuring which video device to POST? */
+ if (video->cap._GPD && video->cap._SPD && video->cap._VPO) {
+ video->flags.post = 1;
+ status = 0;
+ }
+
+ return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * Driver Interface
+ * --------------------------------------------------------------------------
+ */
+
+/* device interface */
+static struct acpi_video_device_attrib *
+acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
+{
+ struct acpi_video_enumerated_device *ids;
+ int i;
+
+ for (i = 0; i < video->attached_count; i++) {
+ ids = &video->attached_array[i];
+ if ((ids->value.int_val & 0xffff) == device_id)
+ return &ids->value.attrib;
+ }
+
+ return NULL;
+}
+
+static int
+acpi_video_get_device_type(struct acpi_video_bus *video,
+ unsigned long device_id)
+{
+ struct acpi_video_enumerated_device *ids;
+ int i;
+
+ for (i = 0; i < video->attached_count; i++) {
+ ids = &video->attached_array[i];
+ if ((ids->value.int_val & 0xffff) == device_id)
+ return ids->value.int_val;
+ }
+
+ return 0;
+}
+
+static int acpi_video_bus_get_one_device(struct acpi_device *device, void *arg)
+{
+ struct acpi_video_bus *video = arg;
+ struct acpi_video_device_attrib *attribute;
+ struct acpi_video_device *data;
+ unsigned long long device_id;
+ acpi_status status;
+ int device_type;
+
+ status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
+ /* Skip devices without _ADR instead of failing. */
+ if (ACPI_FAILURE(status))
+ goto exit;
+
+ data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
+ if (!data) {
+ dev_dbg(&device->dev, "Cannot attach\n");
+ return -ENOMEM;
+ }
+
+ strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
+ device->driver_data = data;
+
+ data->device_id = device_id;
+ data->video = video;
+ data->dev = device;
+ INIT_DELAYED_WORK(&data->switch_brightness_work,
+ acpi_video_switch_brightness);
+
+ attribute = acpi_video_get_device_attr(video, device_id);
+
+ if (attribute && (attribute->device_id_scheme || device_id_scheme)) {
+ switch (attribute->display_type) {
+ case ACPI_VIDEO_DISPLAY_CRT:
+ data->flags.crt = 1;
+ break;
+ case ACPI_VIDEO_DISPLAY_TV:
+ data->flags.tvout = 1;
+ break;
+ case ACPI_VIDEO_DISPLAY_DVI:
+ data->flags.dvi = 1;
+ break;
+ case ACPI_VIDEO_DISPLAY_LCD:
+ data->flags.lcd = 1;
+ break;
+ default:
+ data->flags.unknown = 1;
+ break;
+ }
+ if (attribute->bios_can_detect)
+ data->flags.bios = 1;
+ } else {
+ /* Check for legacy IDs */
+ device_type = acpi_video_get_device_type(video, device_id);
+ /* Ignore bits 16 and 18-20 */
+ switch (device_type & 0xffe2ffff) {
+ case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
+ data->flags.crt = 1;
+ break;
+ case ACPI_VIDEO_DISPLAY_LEGACY_PANEL:
+ data->flags.lcd = 1;
+ break;
+ case ACPI_VIDEO_DISPLAY_LEGACY_TV:
+ data->flags.tvout = 1;
+ break;
+ default:
+ data->flags.unknown = 1;
+ }
+ }
+
+ acpi_video_device_bind(video, data);
+ acpi_video_device_find_cap(data);
+
+ if (data->cap._BCM && data->cap._BCL)
+ may_report_brightness_keys = true;
+
+ mutex_lock(&video->device_list_lock);
+ list_add_tail(&data->entry, &video->video_device_list);
+ mutex_unlock(&video->device_list_lock);
+
+exit:
+ video->child_count++;
+ return 0;
+}
+
+/*
+ * Arg:
+ * video : video bus device
+ *
+ * Return:
+ * none
+ *
+ * Enumerate the video device list of the video bus,
+ * bind the ids with the corresponding video devices
+ * under the video bus.
+ */
+
+static void acpi_video_device_rebind(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)
+ acpi_video_device_bind(video, dev);
+
+ mutex_unlock(&video->device_list_lock);
+}
+
+/*
+ * Arg:
+ * video : video bus device
+ * device : video output device under the video
+ * bus
+ *
+ * Return:
+ * none
+ *
+ * Bind the ids with the corresponding video devices
+ * under the video bus.
+ */
+
+static void
+acpi_video_device_bind(struct acpi_video_bus *video,
+ struct acpi_video_device *device)
+{
+ struct acpi_video_enumerated_device *ids;
+ int i;
+
+ for (i = 0; i < video->attached_count; i++) {
+ ids = &video->attached_array[i];
+ if (device->device_id == (ids->value.int_val & 0xffff)) {
+ ids->bind_info = device;
+ acpi_handle_debug(video->device->handle, "%s: %d\n",
+ __func__, i);
+ }
+ }
+}
+
+static bool acpi_video_device_in_dod(struct acpi_video_device *device)
+{
+ struct acpi_video_bus *video = device->video;
+ int i;
+
+ /*
+ * If we have a broken _DOD or we have more than 8 output devices
+ * under the graphics controller node that we can't proper deal with
+ * in the operation region code currently, no need to test.
+ */
+ if (!video->attached_count || video->child_count > 8)
+ return true;
+
+ for (i = 0; i < video->attached_count; i++) {
+ if ((video->attached_array[i].value.int_val & 0xfff) ==
+ (device->device_id & 0xfff))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Arg:
+ * video : video bus device
+ *
+ * Return:
+ * < 0 : error
+ *
+ * Call _DOD to enumerate all devices attached to display adapter
+ *
+ */
+
+static int acpi_video_device_enumerate(struct acpi_video_bus *video)
+{
+ int status;
+ int count;
+ int i;
+ struct acpi_video_enumerated_device *active_list;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *dod = NULL;
+ union acpi_object *obj;
+
+ if (!video->cap._DOD)
+ return AE_NOT_EXIST;
+
+ status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_info(video->device->handle,
+ "_DOD evaluation failed: %s\n",
+ acpi_format_exception(status));
+ return status;
+ }
+
+ dod = buffer.pointer;
+ if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
+ acpi_handle_info(video->device->handle, "Invalid _DOD data\n");
+ status = -EFAULT;
+ goto out;
+ }
+
+ acpi_handle_debug(video->device->handle, "Found %d video heads in _DOD\n",
+ dod->package.count);
+
+ active_list = kcalloc(1 + dod->package.count,
+ sizeof(struct acpi_video_enumerated_device),
+ GFP_KERNEL);
+ if (!active_list) {
+ status = -ENOMEM;
+ goto out;
+ }
+
+ count = 0;
+ for (i = 0; i < dod->package.count; i++) {
+ obj = &dod->package.elements[i];
+
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ acpi_handle_info(video->device->handle,
+ "Invalid _DOD data in element %d\n", i);
+ continue;
+ }
+
+ active_list[count].value.int_val = obj->integer.value;
+ active_list[count].bind_info = NULL;
+
+ acpi_handle_debug(video->device->handle,
+ "_DOD element[%d] = %d\n", i,
+ (int)obj->integer.value);
+
+ count++;
+ }
+
+ kfree(video->attached_array);
+
+ video->attached_array = active_list;
+ video->attached_count = count;
+
+out:
+ kfree(buffer.pointer);
+ return status;
+}
+
+static int
+acpi_video_get_next_level(struct acpi_video_device *device,
+ u32 level_current, u32 event)
+{
+ int min, max, min_above, max_below, i, l, delta = 255;
+ max = max_below = 0;
+ min = min_above = 255;
+ /* Find closest level to level_current */
+ for (i = ACPI_VIDEO_FIRST_LEVEL; i < device->brightness->count; i++) {
+ l = device->brightness->levels[i];
+ if (abs(l - level_current) < abs(delta)) {
+ delta = l - level_current;
+ if (!delta)
+ break;
+ }
+ }
+ /* Adjust level_current to closest available level */
+ level_current += delta;
+ for (i = ACPI_VIDEO_FIRST_LEVEL; i < device->brightness->count; i++) {
+ l = device->brightness->levels[i];
+ if (l < min)
+ min = l;
+ if (l > max)
+ max = l;
+ if (l < min_above && l > level_current)
+ min_above = l;
+ if (l > max_below && l < level_current)
+ max_below = l;
+ }
+
+ switch (event) {
+ case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:
+ return (level_current < max) ? min_above : min;
+ case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:
+ return (level_current < max) ? min_above : max;
+ case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:
+ return (level_current > min) ? max_below : min;
+ case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:
+ case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:
+ return 0;
+ default:
+ return level_current;
+ }
+}
+
+static void
+acpi_video_switch_brightness(struct work_struct *work)
+{
+ struct acpi_video_device *device = container_of(to_delayed_work(work),
+ struct acpi_video_device, switch_brightness_work);
+ unsigned long long level_current, level_next;
+ int event = device->switch_brightness_event;
+ int result = -EINVAL;
+
+ /* no warning message if acpi_backlight=vendor or a quirk is used */
+ if (!device->backlight)
+ return;
+
+ if (!device->brightness)
+ goto out;
+
+ result = acpi_video_device_lcd_get_level_current(device,
+ &level_current,
+ false);
+ if (result)
+ goto out;
+
+ level_next = acpi_video_get_next_level(device, level_current, event);
+
+ result = acpi_video_device_lcd_set_level(device, level_next);
+
+ if (!result)
+ backlight_force_update(device->backlight,
+ BACKLIGHT_UPDATE_HOTKEY);
+
+out:
+ if (result)
+ acpi_handle_info(device->dev->handle,
+ "Failed to switch brightness\n");
+}
+
+int acpi_video_get_edid(struct acpi_device *device, int type, int device_id,
+ void **edid)
+{
+ struct acpi_video_bus *video;
+ struct acpi_video_device *video_device;
+ union acpi_object *buffer = NULL;
+ acpi_status status;
+ int i, length;
+
+ if (!device || !acpi_driver_data(device))
+ return -EINVAL;
+
+ video = acpi_driver_data(device);
+
+ for (i = 0; i < video->attached_count; i++) {
+ video_device = video->attached_array[i].bind_info;
+ length = 256;
+
+ if (!video_device)
+ continue;
+
+ if (!video_device->cap._DDC)
+ continue;
+
+ if (type) {
+ switch (type) {
+ case ACPI_VIDEO_DISPLAY_CRT:
+ if (!video_device->flags.crt)
+ continue;
+ break;
+ case ACPI_VIDEO_DISPLAY_TV:
+ if (!video_device->flags.tvout)
+ continue;
+ break;
+ case ACPI_VIDEO_DISPLAY_DVI:
+ if (!video_device->flags.dvi)
+ continue;
+ break;
+ case ACPI_VIDEO_DISPLAY_LCD:
+ if (!video_device->flags.lcd)
+ continue;
+ break;
+ }
+ } else if (video_device->device_id != 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;
+ }
+ }
+
+ *edid = buffer->buffer.pointer;
+ return length;
+ }
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL(acpi_video_get_edid);
+
+static int
+acpi_video_bus_get_devices(struct acpi_video_bus *video,
+ struct acpi_device *device)
+{
+ /*
+ * There are systems where video module known to work fine regardless
+ * of broken _DOD and ignoring returned value here doesn't cause
+ * any issues later.
+ */
+ acpi_video_device_enumerate(video);
+
+ return acpi_dev_for_each_child(device, acpi_video_bus_get_one_device, video);
+}
+
+/* acpi_video interface */
+
+/*
+ * Win8 requires setting bit2 of _DOS to let firmware know it shouldn't
+ * perform any automatic brightness change on receiving a notification.
+ */
+static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
+{
+ return acpi_video_bus_DOS(video, 0,
+ acpi_osi_is_win8() ? 1 : 0);
+}
+
+static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
+{
+ return acpi_video_bus_DOS(video, 0,
+ acpi_osi_is_win8() ? 0 : 1);
+}
+
+static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
+{
+ struct acpi_video_bus *video = acpi_driver_data(device);
+ struct input_dev *input;
+ int keycode = 0;
+
+ if (!video || !video->input)
+ return;
+
+ input = video->input;
+
+ switch (event) {
+ case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch,
+ * most likely via hotkey. */
+ keycode = KEY_SWITCHVIDEOMODE;
+ break;
+
+ case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video
+ * connector. */
+ acpi_video_device_enumerate(video);
+ acpi_video_device_rebind(video);
+ keycode = KEY_SWITCHVIDEOMODE;
+ break;
+
+ case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */
+ keycode = KEY_SWITCHVIDEOMODE;
+ break;
+ case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */
+ keycode = KEY_VIDEO_NEXT;
+ break;
+ case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */
+ keycode = KEY_VIDEO_PREV;
+ break;
+
+ default:
+ acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
+ event);
+ break;
+ }
+
+ if (acpi_notifier_call_chain(device, event, 0))
+ /* Something vetoed the keypress. */
+ keycode = 0;
+
+ if (keycode && (report_key_events & REPORT_OUTPUT_KEY_EVENTS)) {
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+ }
+}
+
+static void brightness_switch_event(struct acpi_video_device *video_device,
+ u32 event)
+{
+ if (!brightness_switch_enabled)
+ return;
+
+ video_device->switch_brightness_event = event;
+ schedule_delayed_work(&video_device->switch_brightness_work, HZ / 10);
+}
+
+static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct acpi_video_device *video_device = data;
+ struct acpi_device *device = NULL;
+ struct acpi_video_bus *bus;
+ struct input_dev *input;
+ int keycode = 0;
+
+ if (!video_device)
+ return;
+
+ device = video_device->dev;
+ bus = video_device->video;
+ input = bus->input;
+
+ if (hw_changes_brightness > 0) {
+ if (video_device->backlight)
+ backlight_force_update(video_device->backlight,
+ BACKLIGHT_UPDATE_HOTKEY);
+ acpi_notifier_call_chain(device, event, 0);
+ return;
+ }
+
+ switch (event) {
+ case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */
+ brightness_switch_event(video_device, event);
+ keycode = KEY_BRIGHTNESS_CYCLE;
+ break;
+ case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */
+ brightness_switch_event(video_device, event);
+ keycode = KEY_BRIGHTNESSUP;
+ break;
+ case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */
+ brightness_switch_event(video_device, event);
+ keycode = KEY_BRIGHTNESSDOWN;
+ break;
+ case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightness */
+ brightness_switch_event(video_device, event);
+ keycode = KEY_BRIGHTNESS_ZERO;
+ break;
+ case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */
+ brightness_switch_event(video_device, event);
+ keycode = KEY_DISPLAY_OFF;
+ break;
+ default:
+ acpi_handle_debug(handle, "Unsupported event [0x%x]\n", event);
+ break;
+ }
+
+ if (keycode)
+ may_report_brightness_keys = true;
+
+ acpi_notifier_call_chain(device, event, 0);
+
+ if (keycode && (report_key_events & REPORT_BRIGHTNESS_KEY_EVENTS)) {
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+ }
+}
+
+static int acpi_video_resume(struct notifier_block *nb,
+ unsigned long val, void *ign)
+{
+ struct acpi_video_bus *video;
+ struct acpi_video_device *video_device;
+ int i;
+
+ switch (val) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ case PM_POST_RESTORE:
+ video = container_of(nb, struct acpi_video_bus, pm_nb);
+
+ dev_info(&video->device->dev, "Restoring backlight state\n");
+
+ for (i = 0; i < video->attached_count; i++) {
+ video_device = video->attached_array[i].bind_info;
+ if (video_device && video_device->brightness)
+ acpi_video_device_lcd_set_level(video_device,
+ video_device->brightness->curr);
+ }
+
+ return NOTIFY_OK;
+ }
+ return NOTIFY_DONE;
+}
+
+static acpi_status
+acpi_video_bus_match(acpi_handle handle, u32 level, void *context,
+ void **return_value)
+{
+ struct acpi_device *device = context;
+ struct acpi_device *sibling;
+
+ if (handle == device->handle)
+ return AE_CTRL_TERMINATE;
+
+ sibling = acpi_fetch_acpi_dev(handle);
+ if (!sibling)
+ return AE_OK;
+
+ if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME))
+ return AE_ALREADY_EXISTS;
+
+ return AE_OK;
+}
+
+static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
+{
+ struct backlight_properties props;
+ struct pci_dev *pdev;
+ acpi_handle acpi_parent;
+ struct device *parent = NULL;
+ int result;
+ static int count;
+ char *name;
+
+ result = acpi_video_init_brightness(device);
+ if (result)
+ return;
+
+ name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
+ if (!name)
+ return;
+ count++;
+
+ 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));
+ props.type = BACKLIGHT_FIRMWARE;
+ props.max_brightness =
+ device->brightness->count - ACPI_VIDEO_FIRST_LEVEL - 1;
+ device->backlight = backlight_device_register(name,
+ parent,
+ device,
+ &acpi_backlight_ops,
+ &props);
+ kfree(name);
+ if (IS_ERR(device->backlight)) {
+ device->backlight = NULL;
+ return;
+ }
+
+ /*
+ * Save current brightness level in case we have to restore it
+ * before acpi_video_device_lcd_set_level() is called next time.
+ */
+ device->backlight->props.brightness =
+ acpi_video_get_brightness(device->backlight);
+
+ device->cooling_dev = thermal_cooling_device_register("LCD",
+ device->dev, &video_cooling_ops);
+ if (IS_ERR(device->cooling_dev)) {
+ /*
+ * Set cooling_dev to NULL so we don't crash trying to free it.
+ * Also, why the hell we are returning early and not attempt to
+ * register video output if cooling device registration failed?
+ * -- dtor
+ */
+ device->cooling_dev = NULL;
+ return;
+ }
+
+ dev_info(&device->dev->dev, "registered as cooling_device%d\n",
+ device->cooling_dev->id);
+ result = sysfs_create_link(&device->dev->dev.kobj,
+ &device->cooling_dev->device.kobj,
+ "thermal_cooling");
+ if (result)
+ pr_info("sysfs link creation failed\n");
+
+ result = sysfs_create_link(&device->cooling_dev->device.kobj,
+ &device->dev->dev.kobj, "device");
+ if (result)
+ pr_info("Reverse sysfs link creation failed\n");
+}
+
+static void acpi_video_run_bcl_for_osi(struct acpi_video_bus *video)
+{
+ struct acpi_video_device *dev;
+ union acpi_object *levels;
+
+ mutex_lock(&video->device_list_lock);
+ list_for_each_entry(dev, &video->video_device_list, entry) {
+ if (!acpi_video_device_lcd_query_levels(dev->dev->handle, &levels))
+ kfree(levels);
+ }
+ mutex_unlock(&video->device_list_lock);
+}
+
+static bool acpi_video_should_register_backlight(struct acpi_video_device *dev)
+{
+ /*
+ * Do not create backlight device for video output
+ * device that is not in the enumerated list.
+ */
+ if (!acpi_video_device_in_dod(dev)) {
+ dev_dbg(&dev->dev->dev, "not in _DOD list, ignore\n");
+ return false;
+ }
+
+ if (only_lcd)
+ return dev->flags.lcd;
+ return true;
+}
+
+static int acpi_video_bus_register_backlight(struct acpi_video_bus *video)
+{
+ struct acpi_video_device *dev;
+
+ if (video->backlight_registered)
+ return 0;
+
+ if (acpi_video_get_backlight_type() != acpi_backlight_video)
+ return 0;
+
+ mutex_lock(&video->device_list_lock);
+ list_for_each_entry(dev, &video->video_device_list, entry) {
+ if (acpi_video_should_register_backlight(dev))
+ acpi_video_dev_register_backlight(dev);
+ }
+ mutex_unlock(&video->device_list_lock);
+
+ video->backlight_registered = true;
+
+ video->pm_nb.notifier_call = acpi_video_resume;
+ video->pm_nb.priority = 0;
+ return register_pm_notifier(&video->pm_nb);
+}
+
+static void acpi_video_dev_unregister_backlight(struct acpi_video_device *device)
+{
+ if (device->backlight) {
+ backlight_device_unregister(device->backlight);
+ device->backlight = NULL;
+ }
+ if (device->brightness) {
+ kfree(device->brightness->levels);
+ kfree(device->brightness);
+ device->brightness = NULL;
+ }
+ if (device->cooling_dev) {
+ sysfs_remove_link(&device->dev->dev.kobj, "thermal_cooling");
+ sysfs_remove_link(&device->cooling_dev->device.kobj, "device");
+ thermal_cooling_device_unregister(device->cooling_dev);
+ device->cooling_dev = NULL;
+ }
+}
+
+static int acpi_video_bus_unregister_backlight(struct acpi_video_bus *video)
+{
+ struct acpi_video_device *dev;
+ int error;
+
+ if (!video->backlight_registered)
+ return 0;
+
+ error = unregister_pm_notifier(&video->pm_nb);
+
+ mutex_lock(&video->device_list_lock);
+ list_for_each_entry(dev, &video->video_device_list, entry)
+ acpi_video_dev_unregister_backlight(dev);
+ mutex_unlock(&video->device_list_lock);
+
+ video->backlight_registered = false;
+
+ return error;
+}
+
+static void acpi_video_dev_add_notify_handler(struct acpi_video_device *device)
+{
+ acpi_status status;
+ struct acpi_device *adev = device->dev;
+
+ status = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
+ acpi_video_device_notify, device);
+ if (ACPI_FAILURE(status))
+ dev_err(&adev->dev, "Error installing notify handler\n");
+ else
+ device->flags.notify = 1;
+}
+
+static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video)
+{
+ struct input_dev *input;
+ struct acpi_video_device *dev;
+ int error;
+
+ video->input = input = input_allocate_device();
+ if (!input) {
+ error = -ENOMEM;
+ goto out;
+ }
+
+ error = acpi_video_bus_start_devices(video);
+ if (error)
+ goto err_free_input;
+
+ snprintf(video->phys, sizeof(video->phys),
+ "%s/video/input0", acpi_device_hid(video->device));
+
+ input->name = acpi_device_name(video->device);
+ input->phys = video->phys;
+ input->id.bustype = BUS_HOST;
+ input->id.product = 0x06;
+ input->dev.parent = &video->device->dev;
+ input->evbit[0] = BIT(EV_KEY);
+ set_bit(KEY_SWITCHVIDEOMODE, input->keybit);
+ set_bit(KEY_VIDEO_NEXT, input->keybit);
+ set_bit(KEY_VIDEO_PREV, input->keybit);
+ set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit);
+ set_bit(KEY_BRIGHTNESSUP, input->keybit);
+ set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
+ set_bit(KEY_BRIGHTNESS_ZERO, input->keybit);
+ set_bit(KEY_DISPLAY_OFF, input->keybit);
+
+ error = input_register_device(input);
+ if (error)
+ goto err_stop_dev;
+
+ mutex_lock(&video->device_list_lock);
+ list_for_each_entry(dev, &video->video_device_list, entry)
+ acpi_video_dev_add_notify_handler(dev);
+ mutex_unlock(&video->device_list_lock);
+
+ return 0;
+
+err_stop_dev:
+ acpi_video_bus_stop_devices(video);
+err_free_input:
+ input_free_device(input);
+ video->input = NULL;
+out:
+ return error;
+}
+
+static void acpi_video_dev_remove_notify_handler(struct acpi_video_device *dev)
+{
+ if (dev->flags.notify) {
+ acpi_remove_notify_handler(dev->dev->handle, ACPI_DEVICE_NOTIFY,
+ acpi_video_device_notify);
+ dev->flags.notify = 0;
+ }
+}
+
+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)
+ acpi_video_dev_remove_notify_handler(dev);
+ mutex_unlock(&video->device_list_lock);
+
+ acpi_video_bus_stop_devices(video);
+ input_unregister_device(video->input);
+ video->input = NULL;
+}
+
+static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
+{
+ struct acpi_video_device *dev, *next;
+
+ mutex_lock(&video->device_list_lock);
+ list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
+ list_del(&dev->entry);
+ kfree(dev);
+ }
+ mutex_unlock(&video->device_list_lock);
+
+ return 0;
+}
+
+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,
+ acpi_dev_parent(device)->handle, 1,
+ acpi_video_bus_match, NULL,
+ device, NULL);
+ if (status == AE_ALREADY_EXISTS) {
+ pr_info(FW_BUG
+ "Duplicate ACPI video bus devices for the"
+ " same VGA controller, please try module "
+ "parameter \"video.allow_duplicates=1\""
+ "if the current driver doesn't work.\n");
+ if (!allow_duplicates)
+ return -ENODEV;
+ }
+
+ video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
+ if (!video)
+ return -ENOMEM;
+
+ /* a hack to fix the duplicate name "VID" problem on T61 */
+ if (!strcmp(device->pnp.bus_id, "VID")) {
+ if (instance)
+ device->pnp.bus_id[3] = '0' + instance;
+ instance++;
+ }
+ /* a hack to fix the duplicate name "VGA" problem on Pa 3553 */
+ if (!strcmp(device->pnp.bus_id, "VGA")) {
+ if (instance)
+ device->pnp.bus_id[3] = '0' + instance;
+ instance++;
+ }
+
+ video->device = device;
+ strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
+ strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
+ device->driver_data = video;
+
+ acpi_video_bus_find_cap(video);
+ error = acpi_video_bus_check(video);
+ if (error)
+ goto err_free_video;
+
+ mutex_init(&video->device_list_lock);
+ INIT_LIST_HEAD(&video->video_device_list);
+
+ error = acpi_video_bus_get_devices(video, device);
+ if (error)
+ goto err_put_video;
+
+ 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");
+ mutex_lock(&video_list_lock);
+ list_add_tail(&video->entry, &video_bus_head);
+ mutex_unlock(&video_list_lock);
+
+ /*
+ * 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);
+
+ acpi_video_bus_add_notify_handler(video);
+
+ return 0;
+
+err_put_video:
+ acpi_video_bus_put_devices(video);
+ kfree(video->attached_array);
+err_free_video:
+ kfree(video);
+ device->driver_data = NULL;
+
+ return error;
+}
+
+static int acpi_video_bus_remove(struct acpi_device *device)
+{
+ struct acpi_video_bus *video = NULL;
+
+
+ if (!device || !acpi_driver_data(device))
+ return -EINVAL;
+
+ video = acpi_driver_data(device);
+
+ 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 void acpi_video_bus_register_backlight_work(struct work_struct *ignored)
+{
+ acpi_video_register_backlight();
+}
+
+static int __init is_i740(struct pci_dev *dev)
+{
+ if (dev->device == 0x00D1)
+ return 1;
+ if (dev->device == 0x7000)
+ return 1;
+ return 0;
+}
+
+static int __init intel_opregion_present(void)
+{
+ int opregion = 0;
+ struct pci_dev *dev = NULL;
+ u32 address;
+
+ for_each_pci_dev(dev) {
+ if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+ continue;
+ if (dev->vendor != PCI_VENDOR_ID_INTEL)
+ continue;
+ /* We don't want to poke around undefined i740 registers */
+ if (is_i740(dev))
+ continue;
+ pci_read_config_dword(dev, 0xfc, &address);
+ if (!address)
+ continue;
+ opregion = 1;
+ }
+ 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;
+}
+
+/*
+ * At least one graphics driver has reported that no LCD is connected
+ * via the native interface. cancel the registration for fallback acpi_video0.
+ * If another driver still deems this necessary, it can explicitly register it.
+ */
+void acpi_video_report_nolcd(void)
+{
+ cancel_delayed_work(&video_bus_register_backlight_work);
+}
+EXPORT_SYMBOL(acpi_video_report_nolcd);
+
+int acpi_video_register(void)
+{
+ int ret = 0;
+
+ mutex_lock(&register_count_mutex);
+ if (register_count) {
+ /*
+ * if the function of acpi_video_register is already called,
+ * don't register the acpi_video_bus again and return no error.
+ */
+ 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);
+ if (ret)
+ goto leave;
+
+ /*
+ * When the acpi_video_bus is loaded successfully, increase
+ * the counter reference.
+ */
+ register_count = 1;
+
+ /*
+ * acpi_video_bus_add() skips registering the userspace visible
+ * backlight_device. The intend is for this to be registered by the
+ * drm/kms driver calling acpi_video_register_backlight() *after* it is
+ * done setting up its own native backlight device. The delayed work
+ * ensures that acpi_video_register_backlight() always gets called
+ * eventually, in case there is no drm/kms driver or it is disabled.
+ */
+ if (register_backlight_delay)
+ schedule_delayed_work(&video_bus_register_backlight_work,
+ register_backlight_delay * HZ);
+
+leave:
+ mutex_unlock(&register_count_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(acpi_video_register);
+
+void acpi_video_unregister(void)
+{
+ mutex_lock(&register_count_mutex);
+ if (register_count) {
+ cancel_delayed_work_sync(&video_bus_register_backlight_work);
+ acpi_bus_unregister_driver(&acpi_video_bus);
+ register_count = 0;
+ may_report_brightness_keys = false;
+ }
+ mutex_unlock(&register_count_mutex);
+}
+EXPORT_SYMBOL(acpi_video_unregister);
+
+void acpi_video_register_backlight(void)
+{
+ struct acpi_video_bus *video;
+
+ 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)
+{
+ return may_report_brightness_keys &&
+ (report_key_events & REPORT_BRIGHTNESS_KEY_EVENTS);
+}
+EXPORT_SYMBOL(acpi_video_handles_brightness_key_presses);
+
+/*
+ * This is kind of nasty. Hardware using Intel chipsets may require
+ * the video opregion code to be run first in order to initialise
+ * state before any ACPI video calls are made. To handle this we defer
+ * registration of the video class until the opregion code has run.
+ */
+
+static int __init acpi_video_init(void)
+{
+ /*
+ * Let the module load even if ACPI is disabled (e.g. due to
+ * a broken BIOS) so that i915.ko can still be loaded on such
+ * old systems without an AcpiOpRegion.
+ *
+ * acpi_video_register() will report -ENODEV later as well due
+ * to acpi_disabled when i915.ko tries to register itself afterwards.
+ */
+ if (acpi_disabled)
+ return 0;
+
+ if (intel_opregion_present())
+ return 0;
+
+ return acpi_video_register();
+}
+
+static void __exit acpi_video_exit(void)
+{
+ acpi_video_unregister();
+}
+
+module_init(acpi_video_init);
+module_exit(acpi_video_exit);
diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c
new file mode 100644
index 000000000..ca28183f4
--- /dev/null
+++ b/drivers/acpi/acpi_watchdog.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI watchdog table parsing support.
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: watchdog: " fmt
+
+#include <linux/acpi.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+
+#include "internal.h"
+
+#ifdef CONFIG_RTC_MC146818_LIB
+#include <linux/mc146818rtc.h>
+
+/*
+ * There are several systems where the WDAT table is accessing RTC SRAM to
+ * store persistent information. This does not work well with the Linux RTC
+ * driver so on those systems we skip WDAT driver and prefer iTCO_wdt
+ * instead.
+ *
+ * See also https://bugzilla.kernel.org/show_bug.cgi?id=199033.
+ */
+static bool acpi_watchdog_uses_rtc(const struct acpi_table_wdat *wdat)
+{
+ const struct acpi_wdat_entry *entries;
+ int i;
+
+ entries = (struct acpi_wdat_entry *)(wdat + 1);
+ for (i = 0; i < wdat->entries; i++) {
+ const struct acpi_generic_address *gas;
+
+ gas = &entries[i].register_region;
+ if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ switch (gas->address) {
+ case RTC_PORT(0):
+ case RTC_PORT(1):
+ case RTC_PORT(2):
+ case RTC_PORT(3):
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+#else
+static bool acpi_watchdog_uses_rtc(const struct acpi_table_wdat *wdat)
+{
+ return false;
+}
+#endif
+
+static bool acpi_no_watchdog;
+
+static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)
+{
+ const struct acpi_table_wdat *wdat = NULL;
+ acpi_status status;
+
+ if (acpi_disabled || acpi_no_watchdog)
+ return NULL;
+
+ status = acpi_get_table(ACPI_SIG_WDAT, 0,
+ (struct acpi_table_header **)&wdat);
+ if (ACPI_FAILURE(status)) {
+ /* It is fine if there is no WDAT */
+ return NULL;
+ }
+
+ if (acpi_watchdog_uses_rtc(wdat)) {
+ acpi_put_table((struct acpi_table_header *)wdat);
+ pr_info("Skipping WDAT on this system because it uses RTC SRAM\n");
+ return NULL;
+ }
+
+ return wdat;
+}
+
+/**
+ * Returns true if this system should prefer ACPI based watchdog instead of
+ * the native one (which are typically the same hardware).
+ */
+bool acpi_has_watchdog(void)
+{
+ return !!acpi_watchdog_get_wdat();
+}
+EXPORT_SYMBOL_GPL(acpi_has_watchdog);
+
+/* ACPI watchdog can be disabled on boot command line */
+static int __init disable_acpi_watchdog(char *str)
+{
+ acpi_no_watchdog = true;
+ return 1;
+}
+__setup("acpi_no_watchdog", disable_acpi_watchdog);
+
+void __init acpi_watchdog_init(void)
+{
+ const struct acpi_wdat_entry *entries;
+ const struct acpi_table_wdat *wdat;
+ struct list_head resource_list;
+ struct resource_entry *rentry;
+ struct platform_device *pdev;
+ struct resource *resources;
+ size_t nresources = 0;
+ int i;
+
+ wdat = acpi_watchdog_get_wdat();
+ if (!wdat) {
+ /* It is fine if there is no WDAT */
+ return;
+ }
+
+ /* Watchdog disabled by BIOS */
+ if (!(wdat->flags & ACPI_WDAT_ENABLED))
+ goto fail_put_wdat;
+
+ /* Skip legacy PCI WDT devices */
+ if (wdat->pci_segment != 0xff || wdat->pci_bus != 0xff ||
+ wdat->pci_device != 0xff || wdat->pci_function != 0xff)
+ goto fail_put_wdat;
+
+ INIT_LIST_HEAD(&resource_list);
+
+ entries = (struct acpi_wdat_entry *)(wdat + 1);
+ for (i = 0; i < wdat->entries; i++) {
+ const struct acpi_generic_address *gas;
+ struct resource_entry *rentry;
+ struct resource res = {};
+ bool found;
+
+ gas = &entries[i].register_region;
+
+ res.start = gas->address;
+ res.end = res.start + ACPI_ACCESS_BYTE_WIDTH(gas->access_width) - 1;
+ if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ res.flags = IORESOURCE_MEM;
+ } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ res.flags = IORESOURCE_IO;
+ } else {
+ pr_warn("Unsupported address space: %u\n",
+ gas->space_id);
+ goto fail_free_resource_list;
+ }
+
+ found = false;
+ resource_list_for_each_entry(rentry, &resource_list) {
+ if (rentry->res->flags == res.flags &&
+ resource_union(rentry->res, &res, rentry->res)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ rentry = resource_list_create_entry(NULL, 0);
+ if (!rentry)
+ goto fail_free_resource_list;
+
+ *rentry->res = res;
+ resource_list_add_tail(rentry, &resource_list);
+ nresources++;
+ }
+ }
+
+ resources = kcalloc(nresources, sizeof(*resources), GFP_KERNEL);
+ if (!resources)
+ goto fail_free_resource_list;
+
+ i = 0;
+ resource_list_for_each_entry(rentry, &resource_list)
+ resources[i++] = *rentry->res;
+
+ 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));
+
+ kfree(resources);
+
+fail_free_resource_list:
+ resource_list_free(&resource_list);
+fail_put_wdat:
+ acpi_put_table((struct acpi_table_header *)wdat);
+}
diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile
new file mode 100644
index 000000000..f91981115
--- /dev/null
+++ b/drivers/acpi/acpica/Makefile
@@ -0,0 +1,211 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for ACPICA Core interpreter
+#
+
+ccflags-y := -D_LINUX -DBUILDING_ACPICA
+ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
+
+# use acpi.o to put all files here into acpi.o modparam namespace
+obj-y += acpi.o
+
+acpi-y := \
+ dsargs.o \
+ dscontrol.o \
+ dsdebug.o \
+ dsfield.o \
+ dsinit.o \
+ dsmethod.o \
+ dsmthdat.o \
+ dsobject.o \
+ dsopcode.o \
+ dspkginit.o \
+ dsutils.o \
+ dswexec.o \
+ dswload.o \
+ dswload2.o \
+ dswscope.o \
+ dswstate.o
+
+acpi-y += \
+ evevent.o \
+ evgpe.o \
+ evgpeblk.o \
+ evgpeinit.o \
+ evgpeutil.o \
+ evglock.o \
+ evhandler.o \
+ evmisc.o \
+ evregion.o \
+ evrgnini.o \
+ evsci.o \
+ evxface.o \
+ evxfevnt.o \
+ evxfgpe.o \
+ evxfregn.o
+
+acpi-y += \
+ exconcat.o \
+ exconfig.o \
+ exconvrt.o \
+ excreate.o \
+ exdebug.o \
+ exdump.o \
+ exfield.o \
+ exfldio.o \
+ exmisc.o \
+ exmutex.o \
+ exnames.o \
+ exoparg1.o \
+ exoparg2.o \
+ exoparg3.o \
+ exoparg6.o \
+ exprep.o \
+ exregion.o \
+ exresnte.o \
+ exresolv.o \
+ exresop.o \
+ exserial.o \
+ exstore.o \
+ exstoren.o \
+ exstorob.o \
+ exsystem.o \
+ extrace.o \
+ exutils.o
+
+acpi-y += \
+ hwacpi.o \
+ hwesleep.o \
+ hwgpe.o \
+ hwregs.o \
+ hwsleep.o \
+ hwvalid.o \
+ hwxface.o \
+ hwxfsleep.o
+
+acpi-$(CONFIG_PCI) += hwpci.o
+acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o
+
+acpi-y += \
+ nsaccess.o \
+ nsalloc.o \
+ nsarguments.o \
+ nsconvert.o \
+ nsdump.o \
+ nseval.o \
+ nsinit.o \
+ nsload.o \
+ nsnames.o \
+ nsobject.o \
+ nsparse.o \
+ nspredef.o \
+ nsprepkg.o \
+ nsrepair.o \
+ nsrepair2.o \
+ nssearch.o \
+ nsutils.o \
+ nswalk.o \
+ nsxfeval.o \
+ nsxfname.o \
+ nsxfobj.o
+
+acpi-$(ACPI_FUTURE_USAGE) += nsdumpdv.o
+
+acpi-y += \
+ psargs.o \
+ psloop.o \
+ psobject.o \
+ psopcode.o \
+ psopinfo.o \
+ psparse.o \
+ psscope.o \
+ pstree.o \
+ psutils.o \
+ pswalk.o \
+ psxface.o
+
+acpi-y += \
+ rsaddr.o \
+ rscalc.o \
+ rscreate.o \
+ rsdumpinfo.o \
+ rsinfo.o \
+ rsio.o \
+ rsirq.o \
+ rslist.o \
+ rsmemory.o \
+ rsmisc.o \
+ rsserial.o \
+ rsutils.o \
+ rsxface.o
+
+acpi-y += \
+ tbdata.o \
+ tbfadt.o \
+ tbfind.o \
+ tbinstal.o \
+ tbprint.o \
+ tbutils.o \
+ tbxface.o \
+ tbxfload.o \
+ tbxfroot.o
+
+acpi-y += \
+ utaddress.o \
+ utalloc.o \
+ utascii.o \
+ utbuffer.o \
+ utcopy.o \
+ utexcep.o \
+ utdebug.o \
+ utdecode.o \
+ utdelete.o \
+ uterror.o \
+ uteval.o \
+ utglobal.o \
+ uthex.o \
+ utids.o \
+ utinit.o \
+ utlock.o \
+ utmath.o \
+ utmisc.o \
+ utmutex.o \
+ utnonansi.o \
+ utobject.o \
+ utosi.o \
+ utownerid.o \
+ utpredef.o \
+ utresdecode.o \
+ utresrc.o \
+ utstate.o \
+ utstring.o \
+ utstrsuppt.o \
+ utstrtoul64.o \
+ utxface.o \
+ utxfinit.o \
+ utxferror.o \
+ utxfmutex.o
+
+acpi-$(CONFIG_ACPI_DEBUGGER) += \
+ dbcmds.o \
+ dbconvert.o \
+ dbdisply.o \
+ dbexec.o \
+ dbhistry.o \
+ dbinput.o \
+ dbmethod.o \
+ dbnames.o \
+ dbobject.o \
+ dbstats.o \
+ dbutils.o \
+ dbxface.o \
+ rsdump.o \
+
+acpi-$(ACPI_FUTURE_USAGE) += \
+ dbfileio.o \
+ dbtest.o \
+ utcache.o \
+ utprint.o \
+ uttrack.o \
+ utuuid.o
+
diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h
new file mode 100644
index 000000000..0a50b4912
--- /dev/null
+++ b/drivers/acpi/acpica/acapps.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Module Name: acapps - common include for ACPI applications/tools
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef _ACAPPS
+#define _ACAPPS
+
+#ifdef ACPI_USE_STANDARD_HEADERS
+#include <sys/stat.h>
+#endif /* ACPI_USE_STANDARD_HEADERS */
+
+/* Common info for tool signons */
+
+#define ACPICA_NAME "Intel ACPI Component Architecture"
+#define ACPICA_COPYRIGHT "Copyright (c) 2000 - 2022 Intel Corporation"
+
+#if ACPI_MACHINE_WIDTH == 64
+#define ACPI_WIDTH " (64-bit version)"
+
+#elif ACPI_MACHINE_WIDTH == 32
+#define ACPI_WIDTH " (32-bit version)"
+
+#else
+#error unknown ACPI_MACHINE_WIDTH
+#define ACPI_WIDTH " (unknown bit width, not 32 or 64)"
+
+#endif
+
+/* Macros for signons and file headers */
+
+#define ACPI_COMMON_SIGNON(utility_name) \
+ "\n%s\n%s version %8.8X\n%s\n\n", \
+ ACPICA_NAME, \
+ utility_name, ((u32) ACPI_CA_VERSION), \
+ ACPICA_COPYRIGHT
+
+#define ACPI_COMMON_HEADER(utility_name, prefix) \
+ "%s%s\n%s%s version %8.8X%s\n%s%s\n%s\n", \
+ prefix, ACPICA_NAME, \
+ prefix, utility_name, ((u32) ACPI_CA_VERSION), ACPI_WIDTH, \
+ prefix, ACPICA_COPYRIGHT, \
+ prefix
+
+#define ACPI_COMMON_BUILD_TIME \
+ "Build date/time: %s %s\n", __DATE__, __TIME__
+
+/* Macros for usage messages */
+
+#define ACPI_USAGE_HEADER(usage) \
+ printf ("Usage: %s\nOptions:\n", usage);
+
+#define ACPI_USAGE_TEXT(description) \
+ printf (description);
+
+#define ACPI_OPTION(name, description) \
+ printf (" %-20s%s\n", name, description);
+
+/* Check for unexpected exceptions */
+
+#define ACPI_CHECK_STATUS(name, status, expected) \
+ if (status != expected) \
+ { \
+ acpi_os_printf ("Unexpected %s from %s (%s-%d)\n", \
+ acpi_format_exception (status), #name, _acpi_module_name, __LINE__); \
+ }
+
+/* Check for unexpected non-AE_OK errors */
+
+#define ACPI_CHECK_OK(name, status) ACPI_CHECK_STATUS (name, status, AE_OK);
+
+#define FILE_SUFFIX_DISASSEMBLY "dsl"
+#define FILE_SUFFIX_BINARY_TABLE ".dat" /* Needs the dot */
+
+/* acfileio */
+
+acpi_status
+ac_get_all_tables_from_file(char *filename,
+ u8 get_only_aml_tables,
+ struct acpi_new_table_desc **return_list_head);
+
+void ac_delete_table_list(struct acpi_new_table_desc *list_head);
+
+u8 ac_is_file_binary(FILE * file);
+
+acpi_status ac_validate_table_header(FILE * file, long table_offset);
+
+/* Values for get_only_aml_tables */
+
+#define ACPI_GET_ONLY_AML_TABLES TRUE
+#define ACPI_GET_ALL_TABLES FALSE
+
+/*
+ * getopt
+ */
+int acpi_getopt(int argc, char **argv, char *opts);
+
+int acpi_getopt_argument(int argc, char **argv);
+
+extern int acpi_gbl_optind;
+extern int acpi_gbl_opterr;
+extern int acpi_gbl_sub_opt_char;
+extern char *acpi_gbl_optarg;
+
+/*
+ * cmfsize - Common get file size function
+ */
+u32 cm_get_file_size(ACPI_FILE file);
+
+/*
+ * adwalk
+ */
+void
+acpi_dm_cross_reference_namespace(union acpi_parse_object *parse_tree_root,
+ struct acpi_namespace_node *namespace_root,
+ acpi_owner_id owner_id);
+
+void acpi_dm_dump_tree(union acpi_parse_object *origin);
+
+void acpi_dm_find_orphan_methods(union acpi_parse_object *origin);
+
+void
+acpi_dm_finish_namespace_load(union acpi_parse_object *parse_tree_root,
+ struct acpi_namespace_node *namespace_root,
+ acpi_owner_id owner_id);
+
+void
+acpi_dm_convert_parse_objects(union acpi_parse_object *parse_tree_root,
+ struct acpi_namespace_node *namespace_root);
+
+/*
+ * adfile
+ */
+acpi_status ad_initialize(void);
+
+char *fl_generate_filename(char *input_filename, char *suffix);
+
+acpi_status
+fl_split_input_pathname(char *input_path,
+ char **out_directory_path, char **out_filename);
+
+char *fl_get_file_basename(char *file_pathname);
+
+char *ad_generate_filename(char *prefix, char *table_id);
+
+void
+ad_write_table(struct acpi_table_header *table,
+ u32 length, char *table_name, char *oem_table_id);
+
+#endif /* _ACAPPS */
diff --git a/drivers/acpi/acpica/accommon.h b/drivers/acpi/acpica/accommon.h
new file mode 100644
index 000000000..bb329e34e
--- /dev/null
+++ b/drivers/acpi/acpica/accommon.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: accommon.h - Common include files for generation of ACPICA source
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACCOMMON_H__
+#define __ACCOMMON_H__
+
+/*
+ * Common set of includes for all ACPICA source files.
+ * We put them here because we don't want to duplicate them
+ * in the source code again and again.
+ *
+ * Note: The order of these include files is important.
+ */
+#include <acpi/acconfig.h> /* Global configuration constants */
+#include "acmacros.h" /* C macros */
+#include "aclocal.h" /* Internal data types */
+#include "acobject.h" /* ACPI internal object */
+#include "acstruct.h" /* Common structures */
+#include "acglobal.h" /* All global variables */
+#include "achware.h" /* Hardware defines and interfaces */
+#include "acutils.h" /* Utility interfaces */
+#ifndef ACPI_USE_SYSTEM_CLIBRARY
+#include "acclib.h" /* C library interfaces */
+#endif /* !ACPI_USE_SYSTEM_CLIBRARY */
+
+#endif /* __ACCOMMON_H__ */
diff --git a/drivers/acpi/acpica/acconvert.h b/drivers/acpi/acpica/acconvert.h
new file mode 100644
index 000000000..476d21e67
--- /dev/null
+++ b/drivers/acpi/acpica/acconvert.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Module Name: acapps - common include for ACPI applications/tools
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef _ACCONVERT
+#define _ACCONVERT
+
+/* Definitions for comment state */
+
+#define ASL_COMMENT_STANDARD 1
+#define ASLCOMMENT_INLINE 2
+#define ASL_COMMENT_OPEN_PAREN 3
+#define ASL_COMMENT_CLOSE_PAREN 4
+#define ASL_COMMENT_CLOSE_BRACE 5
+
+/* Definitions for comment print function*/
+
+#define AML_COMMENT_STANDARD 1
+#define AMLCOMMENT_INLINE 2
+#define AML_COMMENT_END_NODE 3
+#define AML_NAMECOMMENT 4
+#define AML_COMMENT_CLOSE_BRACE 5
+#define AML_COMMENT_ENDBLK 6
+#define AML_COMMENT_INCLUDE 7
+
+#ifdef ACPI_ASL_COMPILER
+/*
+ * cvcompiler
+ */
+void
+cv_process_comment(struct asl_comment_state current_state,
+ char *string_buffer, int c1);
+
+void
+cv_process_comment_type2(struct asl_comment_state current_state,
+ char *string_buffer);
+
+u32 cv_calculate_comment_lengths(union acpi_parse_object *op);
+
+void cv_process_comment_state(char input);
+
+char *cv_append_inline_comment(char *inline_comment, char *to_add);
+
+void cv_add_to_comment_list(char *to_add);
+
+void cv_place_comment(u8 type, char *comment_string);
+
+u32 cv_parse_op_block_type(union acpi_parse_object *op);
+
+struct acpi_comment_node *cv_comment_node_calloc(void);
+
+void cg_write_aml_def_block_comment(union acpi_parse_object *op);
+
+void
+cg_write_one_aml_comment(union acpi_parse_object *op,
+ char *comment_to_print, u8 input_option);
+
+void cg_write_aml_comment(union acpi_parse_object *op);
+
+/*
+ * cvparser
+ */
+void cv_init_file_tree(struct acpi_table_header *table, FILE * root_file);
+
+void cv_clear_op_comments(union acpi_parse_object *op);
+
+struct acpi_file_node *cv_filename_exists(char *filename,
+ struct acpi_file_node *head);
+
+void cv_label_file_node(union acpi_parse_object *op);
+
+void
+cv_capture_list_comments(struct acpi_parse_state *parser_state,
+ struct acpi_comment_node *list_head,
+ struct acpi_comment_node *list_tail);
+
+void cv_capture_comments_only(struct acpi_parse_state *parser_state);
+
+void cv_capture_comments(struct acpi_walk_state *walk_state);
+
+void cv_transfer_comments(union acpi_parse_object *op);
+
+/*
+ * cvdisasm
+ */
+void cv_switch_files(u32 level, union acpi_parse_object *op);
+
+u8 cv_file_has_switched(union acpi_parse_object *op);
+
+void cv_close_paren_write_comment(union acpi_parse_object *op, u32 level);
+
+void cv_close_brace_write_comment(union acpi_parse_object *op, u32 level);
+
+void
+cv_print_one_comment_list(struct acpi_comment_node *comment_list, u32 level);
+
+void
+cv_print_one_comment_type(union acpi_parse_object *op,
+ u8 comment_type, char *end_str, u32 level);
+
+#endif
+
+#endif /* _ACCONVERT */
diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
new file mode 100644
index 000000000..d629716aa
--- /dev/null
+++ b/drivers/acpi/acpica/acdebug.h
@@ -0,0 +1,290 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acdebug.h - ACPI/AML debugger
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACDEBUG_H__
+#define __ACDEBUG_H__
+
+/* The debugger is used in conjunction with the disassembler most of time */
+
+#ifdef ACPI_DISASSEMBLER
+#include "acdisasm.h"
+#endif
+
+#define ACPI_DEBUG_BUFFER_SIZE 0x4000 /* 16K buffer for return objects */
+#define ACPI_DEBUG_LENGTH_FORMAT " (%.4X bits, %.3X bytes)"
+
+struct acpi_db_command_info {
+ const char *name; /* Command Name */
+ u8 min_args; /* Minimum arguments required */
+};
+
+struct acpi_db_command_help {
+ u8 line_count; /* Number of help lines */
+ char *invocation; /* Command Invocation */
+ char *description; /* Command Description */
+};
+
+struct acpi_db_argument_info {
+ const char *name; /* Argument Name */
+};
+
+struct acpi_db_execute_walk {
+ u32 count;
+ u32 max_count;
+ char name_seg[ACPI_NAMESEG_SIZE + 1];
+};
+
+#define PARAM_LIST(pl) pl
+
+#define EX_NO_SINGLE_STEP 1
+#define EX_SINGLE_STEP 2
+#define EX_ALL 4
+
+/*
+ * dbxface - external debugger interfaces
+ */
+ACPI_DBR_DEPENDENT_RETURN_OK(acpi_status
+ acpi_db_single_step(struct acpi_walk_state
+ *walk_state,
+ union acpi_parse_object *op,
+ u32 op_type))
+ ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_signal_break_point(struct
+ acpi_walk_state
+ *walk_state))
+
+/*
+ * dbcmds - debug commands and output routines
+ */
+struct acpi_namespace_node *acpi_db_convert_to_node(char *in_string);
+
+void acpi_db_display_table_info(char *table_arg);
+
+void acpi_db_display_template(char *buffer_arg);
+
+void acpi_db_unload_acpi_table(char *name);
+
+void acpi_db_send_notify(char *name, u32 value);
+
+void acpi_db_display_interfaces(char *action_arg, char *interface_name_arg);
+
+acpi_status acpi_db_sleep(char *object_arg);
+
+void acpi_db_trace(char *enable_arg, char *method_arg, char *once_arg);
+
+void acpi_db_display_locks(void);
+
+void acpi_db_display_resources(char *object_arg);
+
+ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_db_display_gpes(void))
+
+void acpi_db_display_handlers(void);
+
+ACPI_HW_DEPENDENT_RETURN_VOID(void
+ acpi_db_generate_gpe(char *gpe_arg,
+ char *block_arg))
+ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_db_generate_sci(void))
+
+void acpi_db_execute_test(char *type_arg);
+
+/*
+ * dbconvert - miscellaneous conversion routines
+ */
+acpi_status acpi_db_hex_char_to_value(int hex_char, u8 *return_value);
+
+acpi_status acpi_db_convert_to_package(char *string, union acpi_object *object);
+
+acpi_status
+acpi_db_convert_to_object(acpi_object_type type,
+ char *string, union acpi_object *object);
+
+u8 *acpi_db_encode_pld_buffer(struct acpi_pld_info *pld_info);
+
+void acpi_db_dump_pld_buffer(union acpi_object *obj_desc);
+
+/*
+ * dbmethod - control method commands
+ */
+void
+acpi_db_set_method_breakpoint(char *location,
+ struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+void acpi_db_set_method_call_breakpoint(union acpi_parse_object *op);
+
+void acpi_db_set_method_data(char *type_arg, char *index_arg, char *value_arg);
+
+acpi_status acpi_db_disassemble_method(char *name);
+
+void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op);
+
+void acpi_db_evaluate_predefined_names(void);
+
+void acpi_db_evaluate_all(char *name_seg);
+
+/*
+ * dbnames - namespace commands
+ */
+void acpi_db_set_scope(char *name);
+
+void acpi_db_dump_namespace(char *start_arg, char *depth_arg);
+
+void acpi_db_dump_namespace_paths(void);
+
+void acpi_db_dump_namespace_by_owner(char *owner_arg, char *depth_arg);
+
+acpi_status acpi_db_find_name_in_namespace(char *name_arg);
+
+void acpi_db_check_predefined_names(void);
+
+acpi_status
+acpi_db_display_objects(char *obj_type_arg, char *display_count_arg);
+
+void acpi_db_check_integrity(void);
+
+void acpi_db_find_references(char *object_arg);
+
+void acpi_db_get_bus_info(void);
+
+acpi_status acpi_db_display_fields(u32 address_space_id);
+
+/*
+ * dbdisply - debug display commands
+ */
+void acpi_db_display_method_info(union acpi_parse_object *op);
+
+void acpi_db_decode_and_display_object(char *target, char *output_type);
+
+ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_display_result_object(union
+ acpi_operand_object
+ *obj_desc,
+ struct
+ acpi_walk_state
+ *walk_state))
+
+acpi_status acpi_db_display_all_methods(char *display_count_arg);
+
+void acpi_db_display_arguments(void);
+
+void acpi_db_display_locals(void);
+
+void acpi_db_display_results(void);
+
+void acpi_db_display_calling_tree(void);
+
+void acpi_db_display_object_type(char *object_arg);
+
+ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_display_argument_object(union
+ acpi_operand_object
+ *obj_desc,
+ struct
+ acpi_walk_state
+ *walk_state))
+
+/*
+ * dbexec - debugger control method execution
+ */
+void
+acpi_db_execute(char *name, char **args, acpi_object_type *types, u32 flags);
+
+void
+acpi_db_create_execution_thread(char *method_name_arg,
+ char **arguments, acpi_object_type *types);
+
+void
+acpi_db_create_execution_threads(char *num_threads_arg,
+ char *num_loops_arg, char *method_name_arg);
+
+void acpi_db_delete_objects(u32 count, union acpi_object *objects);
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+u32 acpi_db_get_cache_info(struct acpi_memory_list *cache);
+#endif
+
+/*
+ * dbfileio - Debugger file I/O commands
+ */
+acpi_object_type
+acpi_db_match_argument(char *user_argument,
+ struct acpi_db_argument_info *arguments);
+
+void acpi_db_close_debug_file(void);
+
+void acpi_db_open_debug_file(char *name);
+
+acpi_status acpi_db_load_acpi_table(char *filename);
+
+acpi_status acpi_db_load_tables(struct acpi_new_table_desc *list_head);
+
+/*
+ * dbhistry - debugger HISTORY command
+ */
+void acpi_db_add_to_history(char *command_line);
+
+void acpi_db_display_history(void);
+
+char *acpi_db_get_from_history(char *command_num_arg);
+
+char *acpi_db_get_history_by_index(u32 commandd_num);
+
+/*
+ * dbinput - user front-end to the AML debugger
+ */
+acpi_status
+acpi_db_command_dispatch(char *input_buffer,
+ struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context);
+
+acpi_status acpi_db_user_commands(void);
+
+char *acpi_db_get_next_token(char *string,
+ char **next, acpi_object_type *return_type);
+
+/*
+ * dbobject
+ */
+void acpi_db_decode_internal_object(union acpi_operand_object *obj_desc);
+
+void
+acpi_db_display_internal_object(union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state);
+
+void acpi_db_decode_arguments(struct acpi_walk_state *walk_state);
+
+void acpi_db_decode_locals(struct acpi_walk_state *walk_state);
+
+void
+acpi_db_dump_method_info(acpi_status status,
+ struct acpi_walk_state *walk_state);
+
+/*
+ * dbstats - Generation and display of ACPI table statistics
+ */
+void acpi_db_generate_statistics(union acpi_parse_object *root, u8 is_method);
+
+acpi_status acpi_db_display_statistics(char *type_arg);
+
+/*
+ * dbutils - AML debugger utilities
+ */
+void acpi_db_set_output_destination(u32 where);
+
+void acpi_db_dump_external_object(union acpi_object *obj_desc, u32 level);
+
+void acpi_db_prep_namestring(char *name);
+
+struct acpi_namespace_node *acpi_db_local_ns_lookup(char *name);
+
+void acpi_db_uint32_to_hex_string(u32 value, char *buffer);
+
+#endif /* __ACDEBUG_H__ */
diff --git a/drivers/acpi/acpica/acdispat.h b/drivers/acpi/acpica/acdispat.h
new file mode 100644
index 000000000..fe2c3630a
--- /dev/null
+++ b/drivers/acpi/acpica/acdispat.h
@@ -0,0 +1,344 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acdispat.h - dispatcher (parser to interpreter interface)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef _ACDISPAT_H_
+#define _ACDISPAT_H_
+
+#define NAMEOF_LOCAL_NTE "__L0"
+#define NAMEOF_ARG_NTE "__A0"
+
+/*
+ * dsargs - execution of dynamic arguments for static objects
+ */
+acpi_status
+acpi_ds_get_buffer_field_arguments(union acpi_operand_object *obj_desc);
+
+acpi_status
+acpi_ds_get_bank_field_arguments(union acpi_operand_object *obj_desc);
+
+acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *rgn_desc);
+
+acpi_status acpi_ds_get_buffer_arguments(union acpi_operand_object *obj_desc);
+
+acpi_status acpi_ds_get_package_arguments(union acpi_operand_object *obj_desc);
+
+/*
+ * dscontrol - support for execution control opcodes
+ */
+acpi_status
+acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+acpi_status
+acpi_ds_exec_end_control_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+/*
+ * dsopcode - support for late operand evaluation
+ */
+acpi_status
+acpi_ds_eval_buffer_field_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+acpi_status
+acpi_ds_eval_region_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+acpi_status
+acpi_ds_eval_table_region_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+acpi_status
+acpi_ds_eval_data_object_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ union acpi_operand_object *obj_desc);
+
+acpi_status
+acpi_ds_eval_bank_field_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+acpi_status acpi_ds_initialize_region(acpi_handle obj_handle);
+
+/*
+ * dsexec - Parser/Interpreter interface, method execution callbacks
+ */
+acpi_status
+acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state,
+ union acpi_operand_object *result_obj);
+
+acpi_status
+acpi_ds_exec_begin_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object **out_op);
+
+acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *state);
+
+/*
+ * dsfield - Parser/Interpreter interface for AML fields
+ */
+acpi_status
+acpi_ds_create_field(union acpi_parse_object *op,
+ struct acpi_namespace_node *region_node,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_create_bank_field(union acpi_parse_object *op,
+ struct acpi_namespace_node *region_node,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_create_index_field(union acpi_parse_object *op,
+ struct acpi_namespace_node *region_node,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_create_buffer_field(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_init_field_objects(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state);
+
+/*
+ * dsload - Parser/Interpreter interface
+ */
+acpi_status
+acpi_ds_init_callbacks(struct acpi_walk_state *walk_state, u32 pass_number);
+
+/* dsload - pass 1 namespace load callbacks */
+
+acpi_status
+acpi_ds_load1_begin_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object **out_op);
+
+acpi_status acpi_ds_load1_end_op(struct acpi_walk_state *walk_state);
+
+/* dsload - pass 2 namespace load callbacks */
+
+acpi_status
+acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object **out_op);
+
+acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state);
+
+/*
+ * dsmthdat - method data (locals/args)
+ */
+acpi_status
+acpi_ds_store_object_to_local(u8 type,
+ u32 index,
+ union acpi_operand_object *src_desc,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_method_data_get_entry(u16 opcode,
+ u32 index,
+ struct acpi_walk_state *walk_state,
+ union acpi_operand_object ***node);
+
+void acpi_ds_method_data_delete_all(struct acpi_walk_state *walk_state);
+
+u8 acpi_ds_is_method_value(union acpi_operand_object *obj_desc);
+
+acpi_status
+acpi_ds_method_data_get_value(u8 type,
+ u32 index,
+ struct acpi_walk_state *walk_state,
+ union acpi_operand_object **dest_desc);
+
+acpi_status
+acpi_ds_method_data_init_args(union acpi_operand_object **params,
+ u32 max_param_count,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_method_data_get_node(u8 type,
+ u32 index,
+ struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node **node);
+
+void acpi_ds_method_data_init(struct acpi_walk_state *walk_state);
+
+/*
+ * dsmethod - Parser/Interpreter interface - control method parsing
+ */
+acpi_status
+acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
+ union acpi_operand_object *obj_desc);
+
+acpi_status
+acpi_ds_call_control_method(struct acpi_thread_state *thread,
+ struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+acpi_status
+acpi_ds_restart_control_method(struct acpi_walk_state *walk_state,
+ union acpi_operand_object *return_desc);
+
+void
+acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_method_error(acpi_status status, struct acpi_walk_state *walk_state);
+
+/*
+ * dsinit
+ */
+acpi_status
+acpi_ds_initialize_objects(u32 table_index,
+ struct acpi_namespace_node *start_node);
+
+/*
+ * dsobject - Parser/Interpreter interface - object initialization and conversion
+ */
+acpi_status
+acpi_ds_build_internal_object(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ union acpi_operand_object **obj_desc_ptr);
+
+acpi_status
+acpi_ds_build_internal_buffer_obj(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ u32 buffer_length,
+ union acpi_operand_object **obj_desc_ptr);
+
+acpi_status
+acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ u32 package_length,
+ union acpi_operand_object **obj_desc);
+
+acpi_status
+acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ u16 opcode, union acpi_operand_object **obj_desc);
+
+acpi_status
+acpi_ds_create_node(struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node *node,
+ union acpi_parse_object *op);
+
+/*
+ * dspkginit - Package object initialization
+ */
+acpi_status
+acpi_ds_init_package_element(u8 object_type,
+ union acpi_operand_object *source_object,
+ union acpi_generic_state *state, void *context);
+
+/*
+ * dsutils - Parser/Interpreter interface utility routines
+ */
+void acpi_ds_clear_implicit_return(struct acpi_walk_state *walk_state);
+
+u8
+acpi_ds_do_implicit_return(union acpi_operand_object *return_desc,
+ struct acpi_walk_state *walk_state,
+ u8 add_reference);
+
+u8
+acpi_ds_is_result_used(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state);
+
+void
+acpi_ds_delete_result_if_not_used(union acpi_parse_object *op,
+ union acpi_operand_object *result_obj,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_create_operand(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *arg, u32 args_remaining);
+
+acpi_status
+acpi_ds_create_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *first_arg);
+
+acpi_status acpi_ds_resolve_operands(struct acpi_walk_state *walk_state);
+
+void acpi_ds_clear_operands(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ds_evaluate_name_path(struct acpi_walk_state *walk_state);
+
+/*
+ * dswscope - Scope Stack manipulation
+ */
+acpi_status
+acpi_ds_scope_stack_push(struct acpi_namespace_node *node,
+ acpi_object_type type,
+ struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ds_scope_stack_pop(struct acpi_walk_state *walk_state);
+
+void acpi_ds_scope_stack_clear(struct acpi_walk_state *walk_state);
+
+/*
+ * dswstate - parser WALK_STATE management routines
+ */
+acpi_status
+acpi_ds_obj_stack_push(void *object, struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_obj_stack_pop(u32 pop_count, struct acpi_walk_state *walk_state);
+
+struct acpi_walk_state * acpi_ds_create_walk_state(acpi_owner_id owner_id,
+ union acpi_parse_object
+ *origin,
+ union acpi_operand_object
+ *mth_desc,
+ struct acpi_thread_state
+ *thread);
+
+acpi_status
+acpi_ds_init_aml_walk(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ struct acpi_namespace_node *method_node,
+ u8 * aml_start,
+ u32 aml_length,
+ struct acpi_evaluate_info *info, u8 pass_number);
+
+void
+acpi_ds_obj_stack_pop_and_delete(u32 pop_count,
+ struct acpi_walk_state *walk_state);
+
+void acpi_ds_delete_walk_state(struct acpi_walk_state *walk_state);
+
+struct acpi_walk_state *acpi_ds_pop_walk_state(struct acpi_thread_state
+ *thread);
+
+void
+acpi_ds_push_walk_state(struct acpi_walk_state *walk_state,
+ struct acpi_thread_state *thread);
+
+acpi_status acpi_ds_result_stack_clear(struct acpi_walk_state *walk_state);
+
+struct acpi_walk_state *acpi_ds_get_current_walk_state(struct acpi_thread_state
+ *thread);
+
+acpi_status
+acpi_ds_result_pop(union acpi_operand_object **object,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ds_result_push(union acpi_operand_object *object,
+ struct acpi_walk_state *walk_state);
+
+/*
+ * dsdebug - parser debugging routines
+ */
+void
+acpi_ds_dump_method_stack(acpi_status status,
+ struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+#endif /* _ACDISPAT_H_ */
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
new file mode 100644
index 000000000..922f559a3
--- /dev/null
+++ b/drivers/acpi/acpica/acevents.h
@@ -0,0 +1,252 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acevents.h - Event subcomponent prototypes and defines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACEVENTS_H__
+#define __ACEVENTS_H__
+
+/*
+ * Conditions to trigger post enabling GPE polling:
+ * It is not sufficient to trigger edge-triggered GPE with specific GPE
+ * chips, software need to poll once after enabling.
+ */
+#ifdef ACPI_USE_GPE_POLLING
+#define ACPI_GPE_IS_POLLING_NEEDED(__gpe__) \
+ ((__gpe__)->runtime_count == 1 && \
+ (__gpe__)->flags & ACPI_GPE_INITIALIZED && \
+ ((__gpe__)->flags & ACPI_GPE_XRUPT_TYPE_MASK) == ACPI_GPE_EDGE_TRIGGERED)
+#else
+#define ACPI_GPE_IS_POLLING_NEEDED(__gpe__) FALSE
+#endif
+
+/*
+ * evevent
+ */
+acpi_status acpi_ev_initialize_events(void);
+
+acpi_status acpi_ev_install_xrupt_handlers(void);
+
+u32 acpi_ev_fixed_event_detect(void);
+
+/*
+ * evmisc
+ */
+u8 acpi_ev_is_notify_object(struct acpi_namespace_node *node);
+
+u32 acpi_ev_get_gpe_number_index(u32 gpe_number);
+
+acpi_status
+acpi_ev_queue_notify_request(struct acpi_namespace_node *node,
+ u32 notify_value);
+
+/*
+ * evglock - Global Lock support
+ */
+acpi_status acpi_ev_init_global_lock_handler(void);
+
+ACPI_HW_DEPENDENT_RETURN_OK(acpi_status
+ acpi_ev_acquire_global_lock(u16 timeout))
+ACPI_HW_DEPENDENT_RETURN_OK(acpi_status acpi_ev_release_global_lock(void))
+
+acpi_status acpi_ev_remove_global_lock_handler(void);
+
+/*
+ * evgpe - Low-level GPE support
+ */
+u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list);
+
+acpi_status
+acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info);
+
+acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info);
+
+acpi_status
+acpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked);
+
+acpi_status
+acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info,
+ u8 clear_on_enable);
+
+acpi_status
+acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info);
+
+struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device,
+ u32 gpe_number);
+
+struct acpi_gpe_event_info *acpi_ev_low_get_gpe_info(u32 gpe_number,
+ struct acpi_gpe_block_info
+ *gpe_block);
+
+acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info);
+
+u32
+acpi_ev_detect_gpe(struct acpi_namespace_node *gpe_device,
+ struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number);
+
+/*
+ * evgpeblk - Upper-level GPE block support
+ */
+acpi_status
+acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
+ u64 address,
+ u8 space_id,
+ u32 register_count,
+ u16 gpe_block_base_number,
+ u32 interrupt_number,
+ struct acpi_gpe_block_info **return_gpe_block);
+
+acpi_status
+acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *context);
+
+ACPI_HW_DEPENDENT_RETURN_OK(acpi_status
+ acpi_ev_delete_gpe_block(struct acpi_gpe_block_info
+ *gpe_block))
+
+u32
+acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
+ struct acpi_gpe_event_info *gpe_event_info,
+ u32 gpe_number);
+
+/*
+ * evgpeinit - GPE initialization and update
+ */
+acpi_status acpi_ev_gpe_initialize(void);
+
+ACPI_HW_DEPENDENT_RETURN_VOID(void
+ acpi_ev_update_gpes(acpi_owner_id table_owner_id))
+
+acpi_status
+acpi_ev_match_gpe_method(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value);
+
+/*
+ * evgpeutil - GPE utilities
+ */
+acpi_status
+acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context);
+
+acpi_status
+acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block, void *context);
+
+acpi_status
+acpi_ev_get_gpe_xrupt_block(u32 interrupt_number,
+ struct acpi_gpe_xrupt_info **gpe_xrupt_block);
+
+acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt);
+
+acpi_status
+acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *context);
+
+/*
+ * evhandler - Address space handling
+ */
+union acpi_operand_object *acpi_ev_find_region_handler(acpi_adr_space_type
+ space_id,
+ union acpi_operand_object
+ *handler_obj);
+
+u8
+acpi_ev_has_default_handler(struct acpi_namespace_node *node,
+ acpi_adr_space_type space_id);
+
+acpi_status acpi_ev_install_region_handlers(void);
+
+acpi_status
+acpi_ev_install_space_handler(struct acpi_namespace_node *node,
+ acpi_adr_space_type space_id,
+ acpi_adr_space_handler handler,
+ acpi_adr_space_setup setup, void *context);
+
+/*
+ * evregion - Operation region support
+ */
+acpi_status acpi_ev_initialize_op_regions(void);
+
+acpi_status
+acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
+ union acpi_operand_object *field_obj,
+ u32 function,
+ u32 region_offset, u32 bit_width, u64 *value);
+
+acpi_status
+acpi_ev_attach_region(union acpi_operand_object *handler_obj,
+ union acpi_operand_object *region_obj,
+ u8 acpi_ns_is_locked);
+
+void
+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_adr_space_type space_id, u32 function);
+
+acpi_status
+acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function);
+
+/*
+ * evregini - Region initialization and setup
+ */
+acpi_status
+acpi_ev_system_memory_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context,
+ void **region_context);
+
+acpi_status
+acpi_ev_io_space_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context);
+
+acpi_status
+acpi_ev_pci_config_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context);
+
+acpi_status
+acpi_ev_cmos_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context);
+
+acpi_status
+acpi_ev_pci_bar_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context);
+
+acpi_status
+acpi_ev_data_table_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context);
+
+acpi_status
+acpi_ev_default_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context);
+
+acpi_status acpi_ev_initialize_region(union acpi_operand_object *region_obj);
+
+u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node);
+
+/*
+ * evsci - SCI (System Control Interrupt) handling/dispatch
+ */
+u32 ACPI_SYSTEM_XFACE acpi_ev_gpe_xrupt_handler(void *context);
+
+u32 acpi_ev_sci_dispatch(void);
+
+u32 acpi_ev_install_sci_handler(void);
+
+acpi_status acpi_ev_remove_all_sci_handlers(void);
+
+ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_ev_terminate(void))
+#endif /* __ACEVENTS_H__ */
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
new file mode 100644
index 000000000..088d6a7d0
--- /dev/null
+++ b/drivers/acpi/acpica/acglobal.h
@@ -0,0 +1,400 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acglobal.h - Declarations for global variables
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACGLOBAL_H__
+#define __ACGLOBAL_H__
+
+/*****************************************************************************
+ *
+ * Globals related to the incoming ACPI tables
+ *
+ ****************************************************************************/
+
+/* Master list of all ACPI tables that were found in the RSDT/XSDT */
+
+ACPI_GLOBAL(struct acpi_table_list, acpi_gbl_root_table_list);
+
+/* DSDT information. Used to check for DSDT corruption */
+
+ACPI_GLOBAL(struct acpi_table_header *, acpi_gbl_DSDT);
+ACPI_GLOBAL(struct acpi_table_header, acpi_gbl_original_dsdt_header);
+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 */
+
+/* These addresses are calculated from the FADT Event Block addresses */
+
+ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1a_status);
+ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1a_enable);
+
+ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1b_status);
+ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1b_enable);
+
+#ifdef ACPI_GPE_USE_LOGICAL_ADDRESSES
+ACPI_GLOBAL(unsigned long, acpi_gbl_xgpe0_block_logical_address);
+ACPI_GLOBAL(unsigned long, acpi_gbl_xgpe1_block_logical_address);
+
+#endif /* ACPI_GPE_USE_LOGICAL_ADDRESSES */
+
+/*
+ * Handle both ACPI 1.0 and ACPI 2.0+ Integer widths. The integer width is
+ * determined by the revision of the DSDT: If the DSDT revision is less than
+ * 2, use only the lower 32 bits of the internal 64-bit Integer.
+ */
+ACPI_GLOBAL(u8, acpi_gbl_integer_bit_width);
+ACPI_GLOBAL(u8, acpi_gbl_integer_byte_width);
+ACPI_GLOBAL(u8, acpi_gbl_integer_nybble_width);
+
+/*****************************************************************************
+ *
+ * Mutual exclusion within the ACPICA subsystem
+ *
+ ****************************************************************************/
+
+/*
+ * Predefined mutex objects. This array contains the
+ * actual OS mutex handles, indexed by the local ACPI_MUTEX_HANDLEs.
+ * (The table maps local handles to the real OS handles)
+ */
+ACPI_GLOBAL(struct acpi_mutex_info, acpi_gbl_mutex_info[ACPI_NUM_MUTEX]);
+
+/*
+ * Global lock mutex is an actual AML mutex object
+ * Global lock semaphore works in conjunction with the actual global lock
+ * Global lock spinlock is used for "pending" handshake
+ */
+ACPI_GLOBAL(union acpi_operand_object *, acpi_gbl_global_lock_mutex);
+ACPI_GLOBAL(acpi_semaphore, acpi_gbl_global_lock_semaphore);
+ACPI_GLOBAL(acpi_spinlock, acpi_gbl_global_lock_pending_lock);
+ACPI_GLOBAL(u16, acpi_gbl_global_lock_handle);
+ACPI_GLOBAL(u8, acpi_gbl_global_lock_acquired);
+ACPI_GLOBAL(u8, acpi_gbl_global_lock_present);
+ACPI_GLOBAL(u8, acpi_gbl_global_lock_pending);
+
+/*
+ * Spinlocks are used for interfaces that can be possibly called at
+ * interrupt level
+ */
+ACPI_GLOBAL(acpi_spinlock, acpi_gbl_gpe_lock); /* For GPE data structs and registers */
+ACPI_GLOBAL(acpi_raw_spinlock, acpi_gbl_hardware_lock); /* For ACPI H/W except GPE registers */
+ACPI_GLOBAL(acpi_spinlock, acpi_gbl_reference_count_lock);
+
+/* Mutex for _OSI support */
+
+ACPI_GLOBAL(acpi_mutex, acpi_gbl_osi_mutex);
+
+/* Reader/Writer lock is used for namespace walk and dynamic table unload */
+
+ACPI_GLOBAL(struct acpi_rw_lock, acpi_gbl_namespace_rw_lock);
+
+/*****************************************************************************
+ *
+ * Miscellaneous globals
+ *
+ ****************************************************************************/
+
+/* Object caches */
+
+ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_namespace_cache);
+ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_state_cache);
+ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_ps_node_cache);
+ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_ps_node_ext_cache);
+ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_operand_cache);
+
+/* System */
+
+ACPI_INIT_GLOBAL(u32, acpi_gbl_startup_flags, 0);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_shutdown, TRUE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_early_initialization, TRUE);
+
+/* Global handlers */
+
+ACPI_GLOBAL(struct acpi_global_notify_handler, acpi_gbl_global_notify[2]);
+ACPI_GLOBAL(acpi_exception_handler, acpi_gbl_exception_handler);
+ACPI_GLOBAL(acpi_init_handler, acpi_gbl_init_handler);
+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);
+
+/* Owner ID support */
+
+ACPI_GLOBAL(u32, acpi_gbl_owner_id_mask[ACPI_NUM_OWNERID_MASKS]);
+ACPI_GLOBAL(u8, acpi_gbl_last_owner_id_index);
+ACPI_GLOBAL(u8, acpi_gbl_next_owner_id_offset);
+
+/* Initialization sequencing */
+
+ACPI_INIT_GLOBAL(u8, acpi_gbl_namespace_initialized, FALSE);
+
+/* Miscellaneous */
+
+ACPI_GLOBAL(u32, acpi_gbl_original_mode);
+ACPI_GLOBAL(u32, acpi_gbl_ns_lookup_count);
+ACPI_GLOBAL(u32, acpi_gbl_ps_find_count);
+ACPI_GLOBAL(u16, acpi_gbl_pm1_enable_register_save);
+ACPI_GLOBAL(u8, acpi_gbl_debugger_configuration);
+ACPI_GLOBAL(u8, acpi_gbl_step_to_next_call);
+ACPI_GLOBAL(u8, acpi_gbl_acpi_hardware_present);
+ACPI_GLOBAL(u8, acpi_gbl_events_initialized);
+ACPI_GLOBAL(struct acpi_interface_info *, acpi_gbl_supported_interfaces);
+ACPI_GLOBAL(struct acpi_address_range *,
+ acpi_gbl_address_range_list[ACPI_ADDRESS_RANGE_MAX]);
+
+/* Other miscellaneous, declared and initialized in utglobal */
+
+extern const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT];
+extern const char *acpi_gbl_lowest_dstate_names[ACPI_NUM_sx_w_METHODS];
+extern const char *acpi_gbl_highest_dstate_names[ACPI_NUM_sx_d_METHODS];
+extern const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS];
+extern const char acpi_gbl_lower_hex_digits[];
+extern const char acpi_gbl_upper_hex_digits[];
+extern const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES];
+
+/* Lists for tracking memory allocations (debug only) */
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ACPI_GLOBAL(struct acpi_memory_list *, acpi_gbl_global_list);
+ACPI_GLOBAL(struct acpi_memory_list *, acpi_gbl_ns_node_list);
+ACPI_GLOBAL(u8, acpi_gbl_display_final_mem_stats);
+ACPI_GLOBAL(u8, acpi_gbl_disable_mem_tracking);
+ACPI_GLOBAL(u8, acpi_gbl_verbose_leak_dump);
+#endif
+
+/*****************************************************************************
+ *
+ * ACPI Namespace
+ *
+ ****************************************************************************/
+
+#define NUM_PREDEFINED_NAMES 10
+
+ACPI_GLOBAL(struct acpi_namespace_node, acpi_gbl_root_node_struct);
+ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_root_node);
+ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_fadt_gpe_device);
+
+extern const u8 acpi_gbl_ns_properties[ACPI_NUM_NS_TYPES];
+extern const struct acpi_predefined_names
+ acpi_gbl_pre_defined_names[NUM_PREDEFINED_NAMES];
+
+#ifdef ACPI_DEBUG_OUTPUT
+ACPI_GLOBAL(u32, acpi_gbl_current_node_count);
+ACPI_GLOBAL(u32, acpi_gbl_current_node_size);
+ACPI_GLOBAL(u32, acpi_gbl_max_concurrent_node_count);
+ACPI_GLOBAL(acpi_size *, acpi_gbl_entry_stack_pointer);
+ACPI_GLOBAL(acpi_size *, acpi_gbl_lowest_stack_pointer);
+ACPI_GLOBAL(u32, acpi_gbl_deepest_nesting);
+ACPI_INIT_GLOBAL(u32, acpi_gbl_nesting_level, 0);
+#endif
+
+/*****************************************************************************
+ *
+ * Interpreter/Parser globals
+ *
+ ****************************************************************************/
+
+/* Control method single step flag */
+
+ACPI_GLOBAL(u8, acpi_gbl_cm_single_step);
+ACPI_GLOBAL(struct acpi_thread_state *, acpi_gbl_current_walk_list);
+ACPI_INIT_GLOBAL(union acpi_parse_object, *acpi_gbl_current_scope, NULL);
+
+/* ASL/ASL+ converter */
+
+ACPI_INIT_GLOBAL(u8, acpi_gbl_capture_comments, FALSE);
+ACPI_INIT_GLOBAL(struct acpi_comment_node, *acpi_gbl_last_list_head, NULL);
+
+/*****************************************************************************
+ *
+ * Hardware globals
+ *
+ ****************************************************************************/
+
+extern struct acpi_bit_register_info
+ acpi_gbl_bit_register_info[ACPI_NUM_BITREG];
+ACPI_GLOBAL(u8, acpi_gbl_sleep_type_a);
+ACPI_GLOBAL(u8, acpi_gbl_sleep_type_b);
+ACPI_GLOBAL(u8, acpi_gbl_sleep_type_a_s0);
+ACPI_GLOBAL(u8, acpi_gbl_sleep_type_b_s0);
+
+/*****************************************************************************
+ *
+ * Event and GPE globals
+ *
+ ****************************************************************************/
+
+#if (!ACPI_REDUCED_HARDWARE)
+ACPI_GLOBAL(u8, acpi_gbl_all_gpes_initialized);
+ACPI_GLOBAL(struct acpi_gpe_xrupt_info *, acpi_gbl_gpe_xrupt_list_head);
+ACPI_GLOBAL(struct acpi_gpe_block_info *,
+ acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS]);
+ACPI_GLOBAL(acpi_gbl_event_handler, acpi_gbl_global_event_handler);
+ACPI_GLOBAL(void *, acpi_gbl_global_event_handler_context);
+ACPI_GLOBAL(struct acpi_fixed_event_handler,
+ acpi_gbl_fixed_event_handlers[ACPI_NUM_FIXED_EVENTS]);
+extern struct acpi_fixed_event_info
+ acpi_gbl_fixed_event_info[ACPI_NUM_FIXED_EVENTS];
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+/*****************************************************************************
+ *
+ * Debug support
+ *
+ ****************************************************************************/
+
+/* Event counters */
+
+ACPI_GLOBAL(u32, acpi_method_count);
+ACPI_GLOBAL(u32, acpi_gpe_count);
+ACPI_GLOBAL(u32, acpi_sci_count);
+ACPI_GLOBAL(u32, acpi_fixed_event_count[ACPI_NUM_FIXED_EVENTS]);
+
+/* Dynamic control method tracing mechanism */
+
+ACPI_GLOBAL(u32, acpi_gbl_original_dbg_level);
+ACPI_GLOBAL(u32, acpi_gbl_original_dbg_layer);
+
+/*****************************************************************************
+ *
+ * Debugger and Disassembler
+ *
+ ****************************************************************************/
+
+ACPI_INIT_GLOBAL(u8, acpi_gbl_db_output_flags, ACPI_DB_CONSOLE_OUTPUT);
+
+#ifdef ACPI_DISASSEMBLER
+
+/* Do not disassemble buffers to resource descriptors */
+
+ACPI_INIT_GLOBAL(u8, acpi_gbl_no_resource_disassembly, FALSE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_ignore_noop_operator, FALSE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_cstyle_disassembly, TRUE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_force_aml_disassembly, FALSE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_dm_opt_verbose, TRUE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_dm_emit_external_opcodes, FALSE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_do_disassembler_optimizations, TRUE);
+ACPI_INIT_GLOBAL(ACPI_PARSE_OBJECT_LIST, *acpi_gbl_temp_list_head, NULL);
+
+ACPI_GLOBAL(u8, acpi_gbl_dm_opt_disasm);
+ACPI_GLOBAL(u8, acpi_gbl_dm_opt_listing);
+ACPI_GLOBAL(u8, acpi_gbl_num_external_methods);
+ACPI_GLOBAL(u32, acpi_gbl_resolved_external_methods);
+ACPI_GLOBAL(struct acpi_external_list *, acpi_gbl_external_list);
+ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list);
+#endif
+
+#ifdef ACPI_DEBUGGER
+ACPI_INIT_GLOBAL(u8, acpi_gbl_abort_method, FALSE);
+ACPI_INIT_GLOBAL(acpi_thread_id, acpi_gbl_db_thread_id, ACPI_INVALID_THREAD_ID);
+ACPI_INIT_GLOBAL(u32, acpi_gbl_next_cmd_num, 1);
+
+ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_ini_methods);
+ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_region_support);
+ACPI_GLOBAL(u8, acpi_gbl_db_output_to_file);
+ACPI_GLOBAL(char *, acpi_gbl_db_buffer);
+ACPI_GLOBAL(char *, acpi_gbl_db_filename);
+ACPI_GLOBAL(u32, acpi_gbl_db_debug_level);
+ACPI_GLOBAL(u32, acpi_gbl_db_console_debug_level);
+ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_db_scope_node);
+ACPI_GLOBAL(u8, acpi_gbl_db_terminate_loop);
+ACPI_GLOBAL(u8, acpi_gbl_db_threads_terminated);
+ACPI_GLOBAL(char *, acpi_gbl_db_args[ACPI_DEBUGGER_MAX_ARGS]);
+ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]);
+
+/* These buffers should all be the same size */
+
+ACPI_GLOBAL(char, acpi_gbl_db_parsed_buf[ACPI_DB_LINE_BUFFER_SIZE]);
+ACPI_GLOBAL(char, acpi_gbl_db_scope_buf[ACPI_DB_LINE_BUFFER_SIZE]);
+ACPI_GLOBAL(char, acpi_gbl_db_debug_filename[ACPI_DB_LINE_BUFFER_SIZE]);
+
+/* Statistics globals */
+
+ACPI_GLOBAL(u16, acpi_gbl_obj_type_count[ACPI_TOTAL_TYPES]);
+ACPI_GLOBAL(u16, acpi_gbl_node_type_count[ACPI_TOTAL_TYPES]);
+ACPI_GLOBAL(u16, acpi_gbl_obj_type_count_misc);
+ACPI_GLOBAL(u16, acpi_gbl_node_type_count_misc);
+ACPI_GLOBAL(u32, acpi_gbl_num_nodes);
+ACPI_GLOBAL(u32, acpi_gbl_num_objects);
+#endif /* ACPI_DEBUGGER */
+
+#if defined (ACPI_DISASSEMBLER) || defined (ACPI_ASL_COMPILER)
+ACPI_GLOBAL(const char, *acpi_gbl_pld_panel_list[]);
+ACPI_GLOBAL(const char, *acpi_gbl_pld_vertical_position_list[]);
+ACPI_GLOBAL(const char, *acpi_gbl_pld_horizontal_position_list[]);
+ACPI_GLOBAL(const char, *acpi_gbl_pld_shape_list[]);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_disasm_flag, FALSE);
+#endif
+
+/*****************************************************************************
+ *
+ * ACPICA application-specific globals
+ *
+ ****************************************************************************/
+
+/* ASL-to-ASL+ conversion utility (implemented within the iASL compiler) */
+
+#ifdef ACPI_ASL_COMPILER
+ACPI_INIT_GLOBAL(char *, acpi_gbl_current_inline_comment, NULL);
+ACPI_INIT_GLOBAL(char *, acpi_gbl_current_end_node_comment, NULL);
+ACPI_INIT_GLOBAL(char *, acpi_gbl_current_open_brace_comment, NULL);
+ACPI_INIT_GLOBAL(char *, acpi_gbl_current_close_brace_comment, NULL);
+
+ACPI_INIT_GLOBAL(char *, acpi_gbl_root_filename, NULL);
+ACPI_INIT_GLOBAL(char *, acpi_gbl_current_filename, NULL);
+ACPI_INIT_GLOBAL(char *, acpi_gbl_current_parent_filename, NULL);
+ACPI_INIT_GLOBAL(char *, acpi_gbl_current_include_filename, NULL);
+
+ACPI_INIT_GLOBAL(struct acpi_comment_node, *acpi_gbl_def_blk_comment_list_head,
+ NULL);
+ACPI_INIT_GLOBAL(struct acpi_comment_node, *acpi_gbl_def_blk_comment_list_tail,
+ NULL);
+ACPI_INIT_GLOBAL(struct acpi_comment_node, *acpi_gbl_reg_comment_list_head,
+ NULL);
+ACPI_INIT_GLOBAL(struct acpi_comment_node, *acpi_gbl_reg_comment_list_tail,
+ NULL);
+ACPI_INIT_GLOBAL(struct acpi_comment_node, *acpi_gbl_inc_comment_list_head,
+ NULL);
+ACPI_INIT_GLOBAL(struct acpi_comment_node, *acpi_gbl_inc_comment_list_tail,
+ NULL);
+ACPI_INIT_GLOBAL(struct acpi_comment_node, *acpi_gbl_end_blk_comment_list_head,
+ NULL);
+ACPI_INIT_GLOBAL(struct acpi_comment_node, *acpi_gbl_end_blk_comment_list_tail,
+ NULL);
+
+ACPI_INIT_GLOBAL(struct acpi_comment_addr_node,
+ *acpi_gbl_comment_addr_list_head, NULL);
+ACPI_INIT_GLOBAL(struct acpi_file_node, *acpi_gbl_file_tree_root, NULL);
+
+ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_reg_comment_cache);
+ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_comment_addr_cache);
+ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_file_cache);
+
+ACPI_INIT_GLOBAL(u8, acpi_gbl_debug_asl_conversion, FALSE);
+ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_conv_debug_file, NULL);
+ACPI_GLOBAL(char, acpi_gbl_table_sig[4]);
+#endif
+
+#ifdef ACPI_APPLICATION
+ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_debug_file, NULL);
+ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_output_file, NULL);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_debug_timeout, FALSE);
+
+/* Print buffer */
+
+ACPI_GLOBAL(acpi_spinlock, acpi_gbl_print_lock); /* For print buffer */
+ACPI_GLOBAL(char, acpi_gbl_print_buffer[1024]);
+#endif /* ACPI_APPLICATION */
+
+#endif /* __ACGLOBAL_H__ */
diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h
new file mode 100644
index 000000000..c1b6e7bf4
--- /dev/null
+++ b/drivers/acpi/acpica/achware.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: achware.h -- hardware specific interfaces
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACHWARE_H__
+#define __ACHWARE_H__
+
+/* Values for the _SST predefined method */
+
+#define ACPI_SST_INDICATOR_OFF 0
+#define ACPI_SST_WORKING 1
+#define ACPI_SST_WAKING 2
+#define ACPI_SST_SLEEPING 3
+#define ACPI_SST_SLEEP_CONTEXT 4
+
+/*
+ * hwacpi - high level functions
+ */
+acpi_status acpi_hw_set_mode(u32 mode);
+
+u32 acpi_hw_get_mode(void);
+
+/*
+ * hwregs - ACPI Register I/O
+ */
+acpi_status
+acpi_hw_validate_register(struct acpi_generic_address *reg,
+ u8 max_bit_width, u64 *address);
+
+acpi_status acpi_hw_read(u64 *value, struct acpi_generic_address *reg);
+
+acpi_status acpi_hw_write(u64 value, struct acpi_generic_address *reg);
+
+struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id);
+
+acpi_status acpi_hw_write_pm1_control(u32 pm1a_control, u32 pm1b_control);
+
+acpi_status acpi_hw_register_read(u32 register_id, u32 *return_value);
+
+acpi_status acpi_hw_register_write(u32 register_id, u32 value);
+
+acpi_status acpi_hw_clear_acpi_status(void);
+
+/*
+ * hwsleep - sleep/wake support (Legacy sleep registers)
+ */
+acpi_status acpi_hw_legacy_sleep(u8 sleep_state);
+
+acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state);
+
+acpi_status acpi_hw_legacy_wake(u8 sleep_state);
+
+/*
+ * hwesleep - sleep/wake support (Extended FADT-V5 sleep registers)
+ */
+void acpi_hw_execute_sleep_method(char *method_name, u32 integer_argument);
+
+acpi_status acpi_hw_extended_sleep(u8 sleep_state);
+
+acpi_status acpi_hw_extended_wake_prep(u8 sleep_state);
+
+acpi_status acpi_hw_extended_wake(u8 sleep_state);
+
+/*
+ * hwvalid - Port I/O with validation
+ */
+acpi_status acpi_hw_read_port(acpi_io_address address, u32 *value, u32 width);
+
+acpi_status acpi_hw_write_port(acpi_io_address address, u32 value, u32 width);
+
+acpi_status acpi_hw_validate_io_block(u64 address, u32 bit_width, u32 count);
+
+/*
+ * hwgpe - GPE support
+ */
+acpi_status acpi_hw_gpe_read(u64 *value, struct acpi_gpe_address *reg);
+
+acpi_status acpi_hw_gpe_write(u64 value, struct acpi_gpe_address *reg);
+
+u32 acpi_hw_get_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info);
+
+acpi_status
+acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action);
+
+acpi_status
+acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block, void *context);
+
+acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info *gpe_event_info);
+
+acpi_status
+acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block, void *context);
+
+acpi_status
+acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info,
+ acpi_event_status *event_status);
+
+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
+acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *context);
+
+#ifdef ACPI_PCI_CONFIGURED
+/*
+ * hwpci - PCI configuration support
+ */
+acpi_status
+acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
+ acpi_handle root_pci_device, acpi_handle pci_region);
+#else
+static inline acpi_status
+acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id, acpi_handle root_pci_device,
+ acpi_handle pci_region)
+{
+ return AE_SUPPORT;
+}
+#endif
+
+#endif /* __ACHWARE_H__ */
diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h
new file mode 100644
index 000000000..6bdf133a2
--- /dev/null
+++ b/drivers/acpi/acpica/acinterp.h
@@ -0,0 +1,543 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acinterp.h - Interpreter subcomponent prototypes and defines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACINTERP_H__
+#define __ACINTERP_H__
+
+#define ACPI_WALK_OPERANDS (&(walk_state->operands [walk_state->num_operands -1]))
+
+/* Macros for tables used for debug output */
+
+#define ACPI_EXD_OFFSET(f) (u8) ACPI_OFFSET (union acpi_operand_object,f)
+#define ACPI_EXD_NSOFFSET(f) (u8) ACPI_OFFSET (struct acpi_namespace_node,f)
+#define ACPI_EXD_TABLE_SIZE(name) (sizeof(name) / sizeof (struct acpi_exdump_info))
+
+/*
+ * If possible, pack the following structures to byte alignment, since we
+ * don't care about performance for debug output. Two cases where we cannot
+ * pack the structures:
+ *
+ * 1) Hardware does not support misaligned memory transfers
+ * 2) Compiler does not support pointers within packed structures
+ */
+#if (!defined(ACPI_MISALIGNMENT_NOT_SUPPORTED) && !defined(ACPI_PACKED_POINTERS_NOT_SUPPORTED))
+#pragma pack(1)
+#endif
+
+typedef const struct acpi_exdump_info {
+ u8 opcode;
+ u8 offset;
+ const char *name;
+
+} acpi_exdump_info;
+
+/* Values for the Opcode field above */
+
+#define ACPI_EXD_INIT 0
+#define ACPI_EXD_TYPE 1
+#define ACPI_EXD_UINT8 2
+#define ACPI_EXD_UINT16 3
+#define ACPI_EXD_UINT32 4
+#define ACPI_EXD_UINT64 5
+#define ACPI_EXD_LITERAL 6
+#define ACPI_EXD_POINTER 7
+#define ACPI_EXD_ADDRESS 8
+#define ACPI_EXD_STRING 9
+#define ACPI_EXD_BUFFER 10
+#define ACPI_EXD_PACKAGE 11
+#define ACPI_EXD_FIELD 12
+#define ACPI_EXD_REFERENCE 13
+#define ACPI_EXD_LIST 14 /* Operand object list */
+#define ACPI_EXD_HDLR_LIST 15 /* Address Handler list */
+#define ACPI_EXD_RGN_LIST 16 /* Region list */
+#define ACPI_EXD_NODE 17 /* Namespace Node */
+
+/* restore default alignment */
+
+#pragma pack()
+
+/*
+ * exconvrt - object conversion
+ */
+acpi_status
+acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **result_desc,
+ u32 implicit_conversion);
+
+acpi_status
+acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **result_desc);
+
+acpi_status
+acpi_ex_convert_to_string(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **result_desc, u32 type);
+
+/* Types for ->String conversion */
+
+#define ACPI_EXPLICIT_BYTE_COPY 0x00000000
+#define ACPI_EXPLICIT_CONVERT_HEX 0x00000001
+#define ACPI_IMPLICIT_CONVERT_HEX 0x00000002
+#define ACPI_EXPLICIT_CONVERT_DECIMAL 0x00000003
+
+acpi_status
+acpi_ex_convert_to_target_type(acpi_object_type destination_type,
+ union acpi_operand_object *source_desc,
+ union acpi_operand_object **result_desc,
+ struct acpi_walk_state *walk_state);
+
+/*
+ * exdebug - AML debug object
+ */
+void
+acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
+ u32 level, u32 index);
+
+void
+acpi_ex_start_trace_method(struct acpi_namespace_node *method_node,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state);
+
+void
+acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state);
+
+void
+acpi_ex_start_trace_opcode(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state);
+
+void
+acpi_ex_stop_trace_opcode(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state);
+
+void
+acpi_ex_trace_point(acpi_trace_event_type type,
+ u8 begin, u8 *aml, char *pathname);
+
+/*
+ * exfield - ACPI AML (p-code) execution - field manipulation
+ */
+acpi_status
+acpi_ex_get_protocol_buffer_length(u32 protocol_id, u32 *return_length);
+
+acpi_status
+acpi_ex_common_buffer_setup(union acpi_operand_object *obj_desc,
+ u32 buffer_length, u32 * datum_count);
+
+acpi_status
+acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc,
+ u64 mask,
+ u64 field_value, u32 field_datum_byte_offset);
+
+void
+acpi_ex_get_buffer_datum(u64 *datum,
+ void *buffer,
+ u32 buffer_length,
+ u32 byte_granularity, u32 buffer_offset);
+
+void
+acpi_ex_set_buffer_datum(u64 merged_datum,
+ void *buffer,
+ u32 buffer_length,
+ u32 byte_granularity, u32 buffer_offset);
+
+acpi_status
+acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
+ union acpi_operand_object *obj_desc,
+ union acpi_operand_object **ret_buffer_desc);
+
+acpi_status
+acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
+ union acpi_operand_object *obj_desc,
+ union acpi_operand_object **result_desc);
+
+/*
+ * exfldio - low level field I/O
+ */
+acpi_status
+acpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
+ void *buffer, u32 buffer_length);
+
+acpi_status
+acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
+ void *buffer, u32 buffer_length);
+
+acpi_status
+acpi_ex_access_region(union acpi_operand_object *obj_desc,
+ u32 field_datum_byte_offset, u64 *value, u32 read_write);
+
+/*
+ * exmisc - misc support routines
+ */
+acpi_status
+acpi_ex_get_object_reference(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **return_desc,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ex_concat_template(union acpi_operand_object *obj_desc,
+ union acpi_operand_object *obj_desc2,
+ union acpi_operand_object **actual_return_desc,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ex_do_concatenate(union acpi_operand_object *obj_desc,
+ union acpi_operand_object *obj_desc2,
+ union acpi_operand_object **actual_return_desc,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ex_do_logical_numeric_op(u16 opcode,
+ u64 integer0, u64 integer1, u8 *logical_result);
+
+acpi_status
+acpi_ex_do_logical_op(u16 opcode,
+ union acpi_operand_object *operand0,
+ union acpi_operand_object *operand1, u8 *logical_result);
+
+u64 acpi_ex_do_math_op(u16 opcode, u64 operand0, u64 operand1);
+
+acpi_status acpi_ex_create_mutex(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_create_processor(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_create_power_resource(struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ex_create_region(u8 * aml_start,
+ u32 aml_length,
+ u8 region_space, struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_create_event(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_create_alias(struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ex_create_method(u8 * aml_start,
+ u32 aml_length, struct acpi_walk_state *walk_state);
+
+/*
+ * exconfig - dynamic table load/unload
+ */
+acpi_status
+acpi_ex_load_op(union acpi_operand_object *obj_desc,
+ union acpi_operand_object *target,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
+ union acpi_operand_object **return_desc);
+
+acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle);
+
+/*
+ * exmutex - mutex support
+ */
+acpi_status
+acpi_ex_acquire_mutex(union acpi_operand_object *time_desc,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ex_acquire_mutex_object(u16 timeout,
+ union acpi_operand_object *obj_desc,
+ acpi_thread_id thread_id);
+
+acpi_status
+acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_release_mutex_object(union acpi_operand_object *obj_desc);
+
+void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread);
+
+void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc);
+
+/*
+ * exprep - ACPI AML execution - prep utilities
+ */
+acpi_status
+acpi_ex_prep_common_field_object(union acpi_operand_object *obj_desc,
+ u8 field_flags,
+ u8 field_attribute,
+ u32 field_bit_position, u32 field_bit_length);
+
+acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info);
+
+/*
+ * exserial - field_unit support for serial address spaces
+ */
+acpi_status
+acpi_ex_read_serial_bus(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **return_buffer);
+
+acpi_status
+acpi_ex_write_serial_bus(union acpi_operand_object *source_desc,
+ union acpi_operand_object *obj_desc,
+ union acpi_operand_object **return_buffer);
+
+acpi_status
+acpi_ex_read_gpio(union acpi_operand_object *obj_desc, void *buffer);
+
+acpi_status
+acpi_ex_write_gpio(union acpi_operand_object *source_desc,
+ union acpi_operand_object *obj_desc,
+ union acpi_operand_object **return_buffer);
+
+/*
+ * exsystem - Interface to OS services
+ */
+acpi_status
+acpi_ex_system_do_notify_op(union acpi_operand_object *value,
+ union acpi_operand_object *obj_desc);
+
+acpi_status acpi_ex_system_do_sleep(u64 time);
+
+acpi_status acpi_ex_system_do_stall(u32 time);
+
+acpi_status acpi_ex_system_signal_event(union acpi_operand_object *obj_desc);
+
+acpi_status
+acpi_ex_system_wait_event(union acpi_operand_object *time,
+ union acpi_operand_object *obj_desc);
+
+acpi_status acpi_ex_system_reset_event(union acpi_operand_object *obj_desc);
+
+acpi_status
+acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout);
+
+acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout);
+
+/*
+ * exoparg1 - ACPI AML execution, 1 operand
+ */
+acpi_status acpi_ex_opcode_0A_0T_1R(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_opcode_1A_0T_0R(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_opcode_1A_1T_0R(struct acpi_walk_state *walk_state);
+
+/*
+ * exoparg2 - ACPI AML execution, 2 operands
+ */
+acpi_status acpi_ex_opcode_2A_0T_0R(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_opcode_2A_0T_1R(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_opcode_2A_2T_1R(struct acpi_walk_state *walk_state);
+
+/*
+ * exoparg3 - ACPI AML execution, 3 operands
+ */
+acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state);
+
+acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state);
+
+/*
+ * exoparg6 - ACPI AML execution, 6 operands
+ */
+acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state *walk_state);
+
+/*
+ * exresolv - Object resolution and get value functions
+ */
+acpi_status
+acpi_ex_resolve_to_value(union acpi_operand_object **stack_ptr,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ex_resolve_multiple(struct acpi_walk_state *walk_state,
+ union acpi_operand_object *operand,
+ acpi_object_type *return_type,
+ union acpi_operand_object **return_desc);
+
+/*
+ * exresnte - resolve namespace node
+ */
+acpi_status
+acpi_ex_resolve_node_to_value(struct acpi_namespace_node **stack_ptr,
+ struct acpi_walk_state *walk_state);
+
+/*
+ * exresop - resolve operand to value
+ */
+acpi_status
+acpi_ex_resolve_operands(u16 opcode,
+ union acpi_operand_object **stack_ptr,
+ struct acpi_walk_state *walk_state);
+
+/*
+ * exdump - Interpreter debug output routines
+ */
+void acpi_ex_dump_operand(union acpi_operand_object *obj_desc, u32 depth);
+
+void
+acpi_ex_dump_operands(union acpi_operand_object **operands,
+ const char *opcode_name, u32 num_opcodes);
+
+void
+acpi_ex_dump_object_descriptor(union acpi_operand_object *object, u32 flags);
+
+void acpi_ex_dump_namespace_node(struct acpi_namespace_node *node, u32 flags);
+
+/*
+ * exnames - AML namestring support
+ */
+acpi_status
+acpi_ex_get_name_string(acpi_object_type data_type,
+ u8 * in_aml_address,
+ char **out_name_string, u32 * out_name_length);
+
+/*
+ * exstore - Object store support
+ */
+acpi_status
+acpi_ex_store(union acpi_operand_object *val_desc,
+ union acpi_operand_object *dest_desc,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ex_store_object_to_node(union acpi_operand_object *source_desc,
+ struct acpi_namespace_node *node,
+ struct acpi_walk_state *walk_state,
+ u8 implicit_conversion);
+
+/*
+ * exstoren - resolve/store object
+ */
+acpi_status
+acpi_ex_resolve_object(union acpi_operand_object **source_desc_ptr,
+ acpi_object_type target_type,
+ struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ex_store_object_to_object(union acpi_operand_object *source_desc,
+ union acpi_operand_object *dest_desc,
+ union acpi_operand_object **new_desc,
+ struct acpi_walk_state *walk_state);
+
+/*
+ * exstorob - store object - buffer/string
+ */
+acpi_status
+acpi_ex_store_buffer_to_buffer(union acpi_operand_object *source_desc,
+ union acpi_operand_object *target_desc);
+
+acpi_status
+acpi_ex_store_string_to_string(union acpi_operand_object *source_desc,
+ union acpi_operand_object *target_desc);
+
+/*
+ * excopy - object copy
+ */
+acpi_status
+acpi_ex_copy_integer_to_index_field(union acpi_operand_object *source_desc,
+ union acpi_operand_object *target_desc);
+
+acpi_status
+acpi_ex_copy_integer_to_bank_field(union acpi_operand_object *source_desc,
+ union acpi_operand_object *target_desc);
+
+acpi_status
+acpi_ex_copy_data_to_named_field(union acpi_operand_object *source_desc,
+ struct acpi_namespace_node *node);
+
+acpi_status
+acpi_ex_copy_integer_to_buffer_field(union acpi_operand_object *source_desc,
+ union acpi_operand_object *target_desc);
+
+/*
+ * exutils - interpreter/scanner utilities
+ */
+void acpi_ex_enter_interpreter(void);
+
+void acpi_ex_exit_interpreter(void);
+
+u8 acpi_ex_truncate_for32bit_table(union acpi_operand_object *obj_desc);
+
+void acpi_ex_acquire_global_lock(u32 rule);
+
+void acpi_ex_release_global_lock(u32 rule);
+
+void acpi_ex_eisa_id_to_string(char *dest, u64 compressed_id);
+
+void acpi_ex_integer_to_string(char *dest, u64 value);
+
+void acpi_ex_pci_cls_to_string(char *dest, u8 class_code[3]);
+
+u8 acpi_is_valid_space_id(u8 space_id);
+
+/*
+ * exregion - default op_region handlers
+ */
+acpi_status
+acpi_ex_system_memory_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context,
+ void *region_context);
+
+acpi_status
+acpi_ex_system_io_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context);
+
+acpi_status
+acpi_ex_pci_config_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context);
+
+acpi_status
+acpi_ex_cmos_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context);
+
+acpi_status
+acpi_ex_pci_bar_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context);
+
+acpi_status
+acpi_ex_embedded_controller_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context,
+ void *region_context);
+
+acpi_status
+acpi_ex_sm_bus_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context);
+
+acpi_status
+acpi_ex_data_table_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context);
+
+#endif /* __INTERP_H__ */
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
new file mode 100644
index 000000000..901b1543b
--- /dev/null
+++ b/drivers/acpi/acpica/aclocal.h
@@ -0,0 +1,1286 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: aclocal.h - Internal data types used across the ACPI subsystem
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACLOCAL_H__
+#define __ACLOCAL_H__
+
+/* acpisrc:struct_defs -- for acpisrc conversion */
+
+#define ACPI_SERIALIZED 0xFF
+
+typedef u32 acpi_mutex_handle;
+#define ACPI_GLOBAL_LOCK (acpi_semaphore) (-1)
+
+/* Total number of aml opcodes defined */
+
+#define AML_NUM_OPCODES 0x83
+
+/* Forward declarations */
+
+struct acpi_walk_state;
+struct acpi_obj_mutex;
+union acpi_parse_object;
+
+/*****************************************************************************
+ *
+ * Mutex typedefs and structs
+ *
+ ****************************************************************************/
+
+/*
+ * Predefined handles for the mutex objects used within the subsystem
+ * All mutex objects are automatically created by acpi_ut_mutex_initialize.
+ *
+ * The acquire/release ordering protocol is implied via this list. Mutexes
+ * with a lower value must be acquired before mutexes with a higher value.
+ *
+ * NOTE: any changes here must be reflected in the acpi_gbl_mutex_names
+ * table below also!
+ */
+#define ACPI_MTX_INTERPRETER 0 /* AML Interpreter, main lock */
+#define ACPI_MTX_NAMESPACE 1 /* ACPI Namespace */
+#define ACPI_MTX_TABLES 2 /* Data for ACPI tables */
+#define ACPI_MTX_EVENTS 3 /* Data for ACPI events */
+#define ACPI_MTX_CACHES 4 /* Internal caches, general purposes */
+#define ACPI_MTX_MEMORY 5 /* Debug memory tracking lists */
+
+#define ACPI_MAX_MUTEX 5
+#define ACPI_NUM_MUTEX (ACPI_MAX_MUTEX+1)
+
+/* Lock structure for reader/writer interfaces */
+
+struct acpi_rw_lock {
+ acpi_mutex writer_mutex;
+ acpi_mutex reader_mutex;
+ u32 num_readers;
+};
+
+/*
+ * Predefined handles for spinlocks used within the subsystem.
+ * These spinlocks are created by acpi_ut_mutex_initialize
+ */
+#define ACPI_LOCK_GPES 0
+#define ACPI_LOCK_HARDWARE 1
+
+#define ACPI_MAX_LOCK 1
+#define ACPI_NUM_LOCK (ACPI_MAX_LOCK+1)
+
+/* This Thread ID means that the mutex is not in use (unlocked) */
+
+#define ACPI_MUTEX_NOT_ACQUIRED ((acpi_thread_id) 0)
+
+/* This Thread ID means an invalid thread ID */
+
+#ifdef ACPI_OS_INVALID_THREAD_ID
+#define ACPI_INVALID_THREAD_ID ACPI_OS_INVALID_THREAD_ID
+#else
+#define ACPI_INVALID_THREAD_ID ((acpi_thread_id) 0xFFFFFFFF)
+#endif
+
+/* Table for the global mutexes */
+
+struct acpi_mutex_info {
+ acpi_mutex mutex;
+ u32 use_count;
+ acpi_thread_id thread_id;
+};
+
+/* Lock flag parameter for various interfaces */
+
+#define ACPI_MTX_DO_NOT_LOCK 0
+#define ACPI_MTX_LOCK 1
+
+/* Field access granularities */
+
+#define ACPI_FIELD_BYTE_GRANULARITY 1
+#define ACPI_FIELD_WORD_GRANULARITY 2
+#define ACPI_FIELD_DWORD_GRANULARITY 4
+#define ACPI_FIELD_QWORD_GRANULARITY 8
+
+#define ACPI_ENTRY_NOT_FOUND NULL
+
+/*****************************************************************************
+ *
+ * Namespace typedefs and structs
+ *
+ ****************************************************************************/
+
+/* Operational modes of the AML interpreter/scanner */
+
+typedef enum {
+ ACPI_IMODE_LOAD_PASS1 = 0x01,
+ ACPI_IMODE_LOAD_PASS2 = 0x02,
+ ACPI_IMODE_EXECUTE = 0x03
+} acpi_interpreter_mode;
+
+/*
+ * The Namespace Node describes a named object that appears in the AML.
+ * descriptor_type is used to differentiate between internal descriptors.
+ *
+ * The node is optimized for both 32-bit and 64-bit platforms:
+ * 20 bytes for the 32-bit case, 32 bytes for the 64-bit case.
+ *
+ * Note: The descriptor_type and Type fields must appear in the identical
+ * position in both the struct acpi_namespace_node and union acpi_operand_object
+ * structures.
+ */
+struct acpi_namespace_node {
+ union acpi_operand_object *object; /* Interpreter object */
+ u8 descriptor_type; /* Differentiate object descriptor types */
+ u8 type; /* ACPI Type associated with this name */
+ u16 flags; /* Miscellaneous flags */
+ union acpi_name_union name; /* ACPI Name, always 4 chars per ACPI spec */
+ struct acpi_namespace_node *parent; /* Parent node */
+ struct acpi_namespace_node *child; /* First child */
+ struct acpi_namespace_node *peer; /* First peer */
+ acpi_owner_id owner_id; /* Node creator */
+
+ /*
+ * The following fields are used by the ASL compiler and disassembler only
+ */
+#ifdef ACPI_LARGE_NAMESPACE_NODE
+ union acpi_parse_object *op;
+ void *method_locals;
+ void *method_args;
+ u32 value;
+ u32 length;
+ u8 arg_count;
+
+#endif
+};
+
+/* Namespace Node flags */
+
+#define ANOBJ_RESERVED 0x01 /* Available for use */
+#define ANOBJ_TEMPORARY 0x02 /* Node is create by a method and is temporary */
+#define ANOBJ_METHOD_ARG 0x04 /* Node is a method argument */
+#define ANOBJ_METHOD_LOCAL 0x08 /* Node is a method local */
+#define ANOBJ_SUBTREE_HAS_INI 0x10 /* Used to optimize device initialization */
+#define ANOBJ_EVALUATED 0x20 /* Set on first evaluation of node */
+#define ANOBJ_ALLOCATED_BUFFER 0x40 /* Method AML buffer is dynamic (install_method) */
+#define ANOBJ_NODE_EARLY_INIT 0x80 /* acpi_exec only: Node was create via init file (-fi) */
+
+#define ANOBJ_IS_EXTERNAL 0x08 /* iASL only: This object created via External() */
+#define ANOBJ_METHOD_NO_RETVAL 0x10 /* iASL only: Method has no return value */
+#define ANOBJ_METHOD_SOME_NO_RETVAL 0x20 /* iASL only: Method has at least one return value */
+#define ANOBJ_IS_REFERENCED 0x80 /* iASL only: Object was referenced */
+
+/* Internal ACPI table management - master table list */
+
+struct acpi_table_list {
+ struct acpi_table_desc *tables; /* Table descriptor array */
+ u32 current_table_count; /* Tables currently in the array */
+ u32 max_table_count; /* Max tables array will hold */
+ u8 flags;
+};
+
+/* Flags for above */
+
+#define ACPI_ROOT_ORIGIN_UNKNOWN (0) /* ~ORIGIN_ALLOCATED */
+#define ACPI_ROOT_ORIGIN_ALLOCATED (1)
+#define ACPI_ROOT_ALLOW_RESIZE (2)
+
+/* List to manage incoming ACPI tables */
+
+struct acpi_new_table_desc {
+ struct acpi_table_header *table;
+ struct acpi_new_table_desc *next;
+};
+
+/* Predefined table indexes */
+
+#define ACPI_INVALID_TABLE_INDEX (0xFFFFFFFF)
+
+struct acpi_find_context {
+ char *search_for;
+ acpi_handle *list;
+ u32 *count;
+};
+
+struct acpi_ns_search_data {
+ struct acpi_namespace_node *node;
+};
+
+/* Object types used during package copies */
+
+#define ACPI_COPY_TYPE_SIMPLE 0
+#define ACPI_COPY_TYPE_PACKAGE 1
+
+/* Info structure used to convert external<->internal namestrings */
+
+struct acpi_namestring_info {
+ const char *external_name;
+ const char *next_external_char;
+ char *internal_name;
+ u32 length;
+ u32 num_segments;
+ u32 num_carats;
+ u8 fully_qualified;
+};
+
+/* Field creation info */
+
+struct acpi_create_field_info {
+ struct acpi_namespace_node *region_node;
+ struct acpi_namespace_node *field_node;
+ struct acpi_namespace_node *register_node;
+ struct acpi_namespace_node *data_register_node;
+ struct acpi_namespace_node *connection_node;
+ u8 *resource_buffer;
+ u32 bank_value;
+ u32 field_bit_position;
+ u32 field_bit_length;
+ u16 resource_length;
+ u16 pin_number_index;
+ u8 field_flags;
+ u8 attribute;
+ u8 field_type;
+ u8 access_length;
+};
+
+typedef
+acpi_status (*acpi_internal_method) (struct acpi_walk_state * walk_state);
+
+/*
+ * Bitmapped ACPI types. Used internally only
+ */
+#define ACPI_BTYPE_ANY 0x00000000
+#define ACPI_BTYPE_INTEGER 0x00000001
+#define ACPI_BTYPE_STRING 0x00000002
+#define ACPI_BTYPE_BUFFER 0x00000004
+#define ACPI_BTYPE_PACKAGE 0x00000008
+#define ACPI_BTYPE_FIELD_UNIT 0x00000010
+#define ACPI_BTYPE_DEVICE 0x00000020
+#define ACPI_BTYPE_EVENT 0x00000040
+#define ACPI_BTYPE_METHOD 0x00000080
+#define ACPI_BTYPE_MUTEX 0x00000100
+#define ACPI_BTYPE_REGION 0x00000200
+#define ACPI_BTYPE_POWER 0x00000400
+#define ACPI_BTYPE_PROCESSOR 0x00000800
+#define ACPI_BTYPE_THERMAL 0x00001000
+#define ACPI_BTYPE_BUFFER_FIELD 0x00002000
+#define ACPI_BTYPE_DDB_HANDLE 0x00004000
+#define ACPI_BTYPE_DEBUG_OBJECT 0x00008000
+#define ACPI_BTYPE_REFERENCE_OBJECT 0x00010000 /* From Index(), ref_of(), etc (type6_opcodes) */
+#define ACPI_BTYPE_RESOURCE 0x00020000
+#define ACPI_BTYPE_NAMED_REFERENCE 0x00040000 /* Generic unresolved Name or Namepath */
+
+#define ACPI_BTYPE_COMPUTE_DATA (ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING | ACPI_BTYPE_BUFFER)
+
+#define ACPI_BTYPE_DATA (ACPI_BTYPE_COMPUTE_DATA | ACPI_BTYPE_PACKAGE)
+
+ /* Used by Copy, de_ref_of, Store, Printf, Fprintf */
+
+#define ACPI_BTYPE_DATA_REFERENCE (ACPI_BTYPE_DATA | ACPI_BTYPE_REFERENCE_OBJECT | ACPI_BTYPE_DDB_HANDLE)
+#define ACPI_BTYPE_DEVICE_OBJECTS (ACPI_BTYPE_DEVICE | ACPI_BTYPE_THERMAL | ACPI_BTYPE_PROCESSOR)
+#define ACPI_BTYPE_OBJECTS_AND_REFS 0x0001FFFF /* ARG or LOCAL */
+#define ACPI_BTYPE_ALL_OBJECTS 0x0000FFFF
+
+#pragma pack(1)
+
+/*
+ * Information structure for ACPI predefined names.
+ * Each entry in the table contains the following items:
+ *
+ * name - The ACPI reserved name
+ * param_count - Number of arguments to the method
+ * expected_return_btypes - Allowed type(s) for the return value
+ */
+struct acpi_name_info {
+ char name[ACPI_NAMESEG_SIZE];
+ u16 argument_list;
+ u8 expected_btypes;
+};
+
+/*
+ * Secondary information structures for ACPI predefined objects that return
+ * package objects. This structure appears as the next entry in the table
+ * after the NAME_INFO structure above.
+ *
+ * The reason for this is to minimize the size of the predefined name table.
+ */
+
+/*
+ * Used for ACPI_PTYPE1_FIXED, ACPI_PTYPE1_VAR, ACPI_PTYPE2,
+ * ACPI_PTYPE2_MIN, ACPI_PTYPE2_PKG_COUNT, ACPI_PTYPE2_COUNT,
+ * ACPI_PTYPE2_FIX_VAR
+ */
+struct acpi_package_info {
+ u8 type;
+ u8 object_type1;
+ u8 count1;
+ u8 object_type2;
+ u8 count2;
+ u16 reserved;
+};
+
+/* Used for ACPI_PTYPE2_FIXED */
+
+struct acpi_package_info2 {
+ u8 type;
+ u8 count;
+ u8 object_type[4];
+ u8 reserved;
+};
+
+/* Used for ACPI_PTYPE1_OPTION */
+
+struct acpi_package_info3 {
+ u8 type;
+ u8 count;
+ u8 object_type[2];
+ u8 tail_object_type;
+ u16 reserved;
+};
+
+struct acpi_package_info4 {
+ u8 type;
+ u8 object_type1;
+ u8 count1;
+ u8 sub_object_types;
+ u8 pkg_count;
+ u16 reserved;
+};
+
+union acpi_predefined_info {
+ struct acpi_name_info info;
+ struct acpi_package_info ret_info;
+ struct acpi_package_info2 ret_info2;
+ struct acpi_package_info3 ret_info3;
+ struct acpi_package_info4 ret_info4;
+};
+
+/* Reset to default packing */
+
+#pragma pack()
+
+/* Return object auto-repair info */
+
+typedef acpi_status (*acpi_object_converter) (struct acpi_namespace_node *
+ scope,
+ union acpi_operand_object *
+ original_object,
+ union acpi_operand_object **
+ converted_object);
+
+struct acpi_simple_repair_info {
+ char name[ACPI_NAMESEG_SIZE];
+ u32 unexpected_btypes;
+ u32 package_index;
+ acpi_object_converter object_converter;
+};
+
+/*
+ * Bitmapped return value types
+ * Note: the actual data types must be contiguous, a loop in nspredef.c
+ * depends on this.
+ */
+#define ACPI_RTYPE_ANY 0x00
+#define ACPI_RTYPE_NONE 0x01
+#define ACPI_RTYPE_INTEGER 0x02
+#define ACPI_RTYPE_STRING 0x04
+#define ACPI_RTYPE_BUFFER 0x08
+#define ACPI_RTYPE_PACKAGE 0x10
+#define ACPI_RTYPE_REFERENCE 0x20
+#define ACPI_RTYPE_ALL 0x3F
+
+#define ACPI_NUM_RTYPES 5 /* Number of actual object types */
+
+/* Info for running the _REG methods */
+
+struct acpi_reg_walk_info {
+ u32 function;
+ u32 reg_run_count;
+ acpi_adr_space_type space_id;
+};
+
+/*****************************************************************************
+ *
+ * Event typedefs and structs
+ *
+ ****************************************************************************/
+
+/* Dispatch info for each host-installed SCI handler */
+
+struct acpi_sci_handler_info {
+ struct acpi_sci_handler_info *next;
+ acpi_sci_handler address; /* Address of handler */
+ void *context; /* Context to be passed to handler */
+};
+
+/* Dispatch info for each GPE -- either a method or handler, cannot be both */
+
+struct acpi_gpe_handler_info {
+ acpi_gpe_handler address; /* Address of handler, if any */
+ void *context; /* Context to be passed to handler */
+ struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */
+ u8 original_flags; /* Original (pre-handler) GPE info */
+ u8 originally_enabled; /* True if GPE was originally enabled */
+};
+
+/* Notify info for implicit notify, multiple device objects */
+
+struct acpi_gpe_notify_info {
+ struct acpi_namespace_node *device_node; /* Device to be notified */
+ struct acpi_gpe_notify_info *next;
+};
+
+/*
+ * GPE dispatch info. At any time, the GPE can have at most one type
+ * of dispatch - Method, Handler, or Implicit Notify.
+ */
+union acpi_gpe_dispatch_info {
+ struct acpi_namespace_node *method_node; /* Method node for this GPE level */
+ struct acpi_gpe_handler_info *handler; /* Installed GPE handler */
+ struct acpi_gpe_notify_info *notify_list; /* List of _PRW devices for implicit notifies */
+};
+
+/*
+ * Information about a GPE, one per each GPE in an array.
+ * NOTE: Important to keep this struct as small as possible.
+ */
+struct acpi_gpe_event_info {
+ union acpi_gpe_dispatch_info dispatch; /* Either Method, Handler, or notify_list */
+ struct acpi_gpe_register_info *register_info; /* Backpointer to register info */
+ u8 flags; /* Misc info about this GPE */
+ u8 gpe_number; /* This GPE */
+ u8 runtime_count; /* References to a run GPE */
+ u8 disable_for_dispatch; /* Masked during dispatching */
+};
+
+/* GPE register address */
+
+struct acpi_gpe_address {
+ u8 space_id; /* Address space where the register exists */
+ u64 address; /* 64-bit address of the register */
+};
+
+/* Information about a GPE register pair, one per each status/enable pair in an array */
+
+struct acpi_gpe_register_info {
+ struct acpi_gpe_address status_address; /* Address of status reg */
+ struct acpi_gpe_address enable_address; /* Address of enable reg */
+ u16 base_gpe_number; /* Base GPE number for this register */
+ u8 enable_for_wake; /* GPEs to keep enabled when sleeping */
+ u8 enable_for_run; /* GPEs to keep enabled when running */
+ u8 mask_for_run; /* GPEs to keep masked when running */
+ u8 enable_mask; /* Current mask of enabled GPEs */
+};
+
+/*
+ * Information about a GPE register block, one per each installed block --
+ * GPE0, GPE1, and one per each installed GPE Block Device.
+ */
+struct acpi_gpe_block_info {
+ struct acpi_namespace_node *node;
+ struct acpi_gpe_block_info *previous;
+ struct acpi_gpe_block_info *next;
+ struct acpi_gpe_xrupt_info *xrupt_block; /* Backpointer to interrupt block */
+ struct acpi_gpe_register_info *register_info; /* One per GPE register pair */
+ struct acpi_gpe_event_info *event_info; /* One for each GPE */
+ u64 address; /* Base address of the block */
+ u32 register_count; /* Number of register pairs in block */
+ u16 gpe_count; /* Number of individual GPEs in block */
+ u16 block_base_number; /* Base GPE number for this block */
+ u8 space_id;
+ u8 initialized; /* TRUE if this block is initialized */
+};
+
+/* Information about GPE interrupt handlers, one per each interrupt level used for GPEs */
+
+struct acpi_gpe_xrupt_info {
+ struct acpi_gpe_xrupt_info *previous;
+ struct acpi_gpe_xrupt_info *next;
+ struct acpi_gpe_block_info *gpe_block_list_head; /* List of GPE blocks for this xrupt */
+ u32 interrupt_number; /* System interrupt number */
+};
+
+struct acpi_gpe_walk_info {
+ struct acpi_namespace_node *gpe_device;
+ struct acpi_gpe_block_info *gpe_block;
+ u16 count;
+ acpi_owner_id owner_id;
+ u8 execute_by_owner_id;
+};
+
+struct acpi_gpe_device_info {
+ u32 index;
+ u32 next_block_base_index;
+ acpi_status status;
+ struct acpi_namespace_node *gpe_device;
+};
+
+typedef acpi_status (*acpi_gpe_callback) (struct acpi_gpe_xrupt_info *
+ gpe_xrupt_info,
+ struct acpi_gpe_block_info *
+ gpe_block, void *context);
+
+/* Information about each particular fixed event */
+
+struct acpi_fixed_event_handler {
+ acpi_event_handler handler; /* Address of handler. */
+ void *context; /* Context to be passed to handler */
+};
+
+struct acpi_fixed_event_info {
+ u8 status_register_id;
+ u8 enable_register_id;
+ u16 status_bit_mask;
+ u16 enable_bit_mask;
+};
+
+/* Information used during field processing */
+
+struct acpi_field_info {
+ u8 skip_field;
+ u8 field_flag;
+ u32 pkg_length;
+};
+
+/*****************************************************************************
+ *
+ * Generic "state" object for stacks
+ *
+ ****************************************************************************/
+
+#define ACPI_CONTROL_NORMAL 0xC0
+#define ACPI_CONTROL_CONDITIONAL_EXECUTING 0xC1
+#define ACPI_CONTROL_PREDICATE_EXECUTING 0xC2
+#define ACPI_CONTROL_PREDICATE_FALSE 0xC3
+#define ACPI_CONTROL_PREDICATE_TRUE 0xC4
+
+#define ACPI_STATE_COMMON \
+ void *next; \
+ u8 descriptor_type; /* To differentiate various internal objs */\
+ u8 flags; \
+ u16 value; \
+ u16 state;
+
+ /* There are 2 bytes available here until the next natural alignment boundary */
+
+struct acpi_common_state {
+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;
+};
+
+/*
+ * Pkg state - used to traverse nested package structures
+ */
+struct acpi_pkg_state {
+ ACPI_STATE_COMMON u32 index;
+ union acpi_operand_object *source_object;
+ union acpi_operand_object *dest_object;
+ struct acpi_walk_state *walk_state;
+ void *this_target_obj;
+ u32 num_packages;
+};
+
+/*
+ * Control state - one per if/else and while constructs.
+ * Allows nesting of these constructs
+ */
+struct acpi_control_state {
+ 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 */
+ u64 loop_timeout; /* While() loop timeout */
+};
+
+/*
+ * Scope state - current scope during namespace lookups
+ */
+struct acpi_scope_state {
+ ACPI_STATE_COMMON struct acpi_namespace_node *node;
+};
+
+struct acpi_pscope_state {
+ 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 */
+ u32 arg_list; /* Next argument to parse */
+};
+
+/*
+ * Thread state - one per thread across multiple walk states. Multiple walk
+ * 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 */
+ 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 */
+};
+
+/*
+ * Result values - used to accumulate the results of nested
+ * AML arguments
+ */
+struct acpi_result_values {
+ ACPI_STATE_COMMON
+ union acpi_operand_object *obj_desc[ACPI_RESULTS_FRAME_OBJ_NUM];
+};
+
+typedef
+acpi_status (*acpi_parse_downwards) (struct acpi_walk_state * walk_state,
+ union acpi_parse_object ** out_op);
+
+typedef
+acpi_status (*acpi_parse_upwards) (struct acpi_walk_state * walk_state);
+
+/* Global handlers for AML Notifies */
+
+struct acpi_global_notify_handler {
+ acpi_notify_handler handler;
+ void *context;
+};
+
+/*
+ * Notify info - used to pass info to the deferred notify
+ * handler/dispatcher.
+ */
+struct acpi_notify_info {
+ 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;
+};
+
+/* Generic state is union of structs above */
+
+union acpi_generic_state {
+ struct acpi_common_state common;
+ struct acpi_control_state control;
+ struct acpi_update_state update;
+ struct acpi_scope_state scope;
+ struct acpi_pscope_state parse_scope;
+ struct acpi_pkg_state pkg;
+ struct acpi_thread_state thread;
+ struct acpi_result_values results;
+ struct acpi_notify_info notify;
+};
+
+/*****************************************************************************
+ *
+ * Interpreter typedefs and structs
+ *
+ ****************************************************************************/
+
+typedef
+acpi_status (*acpi_execute_op) (struct acpi_walk_state * walk_state);
+
+/* Address Range info block */
+
+struct acpi_address_range {
+ struct acpi_address_range *next;
+ struct acpi_namespace_node *region_node;
+ acpi_physical_address start_address;
+ acpi_physical_address end_address;
+};
+
+/*****************************************************************************
+ *
+ * Parser typedefs and structs
+ *
+ ****************************************************************************/
+
+/*
+ * AML opcode, name, and argument layout
+ */
+struct acpi_opcode_info {
+#if defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUG_OUTPUT)
+ char *name; /* Opcode name (disassembler/debug only) */
+#endif
+ u32 parse_args; /* Grammar/Parse time arguments */
+ u32 runtime_args; /* Interpret time arguments */
+ u16 flags; /* Misc flags */
+ u8 object_type; /* Corresponding internal object type */
+ u8 class; /* Opcode class */
+ u8 type; /* Opcode type */
+};
+
+/* Value associated with the parse object */
+
+union acpi_parse_value {
+ u64 integer; /* Integer constant (Up to 64 bits) */
+ u32 size; /* bytelist or field size */
+ char *string; /* NULL terminated string */
+ u8 *buffer; /* buffer or string */
+ char *name; /* NULL terminated string */
+ union acpi_parse_object *arg; /* arguments and contained ops */
+};
+
+#if defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUG_OUTPUT)
+#define ACPI_DISASM_ONLY_MEMBERS(a) a;
+#else
+#define ACPI_DISASM_ONLY_MEMBERS(a)
+#endif
+
+#if defined(ACPI_ASL_COMPILER)
+#define ACPI_CONVERTER_ONLY_MEMBERS(a) a;
+#else
+#define ACPI_CONVERTER_ONLY_MEMBERS(a)
+#endif
+
+#define ACPI_PARSE_COMMON \
+ union acpi_parse_object *parent; /* Parent op */\
+ u8 descriptor_type; /* To differentiate various internal objs */\
+ u8 flags; /* Type of Op */\
+ u16 aml_opcode; /* AML opcode */\
+ u8 *aml; /* Address of declaration in AML */\
+ union acpi_parse_object *next; /* Next op */\
+ struct acpi_namespace_node *node; /* For use by interpreter */\
+ union acpi_parse_value value; /* Value or args associated with the opcode */\
+ u8 arg_list_length; /* Number of elements in the arg list */\
+ ACPI_DISASM_ONLY_MEMBERS (\
+ u16 disasm_flags; /* Used during AML disassembly */\
+ u8 disasm_opcode; /* Subtype used for disassembly */\
+ char *operator_symbol; /* Used for C-style operator name strings */\
+ char aml_op_name[16]) /* Op name (debug only) */\
+ ACPI_CONVERTER_ONLY_MEMBERS (\
+ char *inline_comment; /* Inline comment */\
+ char *end_node_comment; /* End of node comment */\
+ char *name_comment; /* Comment associated with the first parameter of the name node */\
+ char *close_brace_comment; /* Comments that come after } on the same as } */\
+ struct acpi_comment_node *comment_list; /* comments that appears before this node */\
+ struct acpi_comment_node *end_blk_comment; /* comments that at the end of a block but before ) or } */\
+ char *cv_filename; /* Filename associated with this node. Used for ASL/ASL+ converter */\
+ char *cv_parent_filename) /* Parent filename associated with this node. Used for ASL/ASL+ converter */
+
+/* categories of comments */
+
+typedef enum {
+ STANDARD_COMMENT = 1,
+ INLINE_COMMENT,
+ ENDNODE_COMMENT,
+ OPENBRACE_COMMENT,
+ CLOSE_BRACE_COMMENT,
+ STD_DEFBLK_COMMENT,
+ END_DEFBLK_COMMENT,
+ FILENAME_COMMENT,
+ PARENTFILENAME_COMMENT,
+ ENDBLK_COMMENT,
+ INCLUDE_COMMENT
+} asl_comment_types;
+
+/* Internal opcodes for disasm_opcode field above */
+
+#define ACPI_DASM_BUFFER 0x00 /* Buffer is a simple data buffer */
+#define ACPI_DASM_RESOURCE 0x01 /* Buffer is a Resource Descriptor */
+#define ACPI_DASM_STRING 0x02 /* Buffer is a ASCII string */
+#define ACPI_DASM_UNICODE 0x03 /* Buffer is a Unicode string */
+#define ACPI_DASM_PLD_METHOD 0x04 /* Buffer is a _PLD method bit-packed buffer */
+#define ACPI_DASM_UUID 0x05 /* Buffer is a UUID/GUID */
+#define ACPI_DASM_EISAID 0x06 /* Integer is an EISAID */
+#define ACPI_DASM_MATCHOP 0x07 /* Parent opcode is a Match() operator */
+#define ACPI_DASM_LNOT_PREFIX 0x08 /* Start of a Lnot_equal (etc.) pair of opcodes */
+#define ACPI_DASM_LNOT_SUFFIX 0x09 /* End of a Lnot_equal (etc.) pair of opcodes */
+#define ACPI_DASM_HID_STRING 0x0A /* String is a _HID or _CID */
+#define ACPI_DASM_IGNORE_SINGLE 0x0B /* Ignore the opcode but not it's children */
+#define ACPI_DASM_SWITCH 0x0C /* While is a Switch */
+#define ACPI_DASM_SWITCH_PREDICATE 0x0D /* Object is a predicate for a Switch or Case block */
+#define ACPI_DASM_CASE 0x0E /* If/Else is a Case in a Switch/Case block */
+#define ACPI_DASM_DEFAULT 0x0F /* Else is a Default in a Switch/Case block */
+
+/*
+ * List struct used in the -ca option
+ */
+struct acpi_comment_node {
+ char *comment;
+ struct acpi_comment_node *next;
+};
+
+struct acpi_comment_addr_node {
+ u8 *addr;
+ struct acpi_comment_addr_node *next;
+};
+
+/*
+ * File node - used for "Include" operator file stack and
+ * dependency tree for the -ca option
+ */
+struct acpi_file_node {
+ void *file;
+ char *filename;
+ char *file_start; /* Points to AML and indicates when the AML for this particular file starts. */
+ char *file_end; /* Points to AML and indicates when the AML for this particular file ends. */
+ struct acpi_file_node *next;
+ struct acpi_file_node *parent;
+ u8 include_written;
+ struct acpi_comment_node *include_comment;
+};
+
+/*
+ * Generic operation (for example: If, While, Store)
+ */
+struct acpi_parse_obj_common {
+ACPI_PARSE_COMMON};
+
+/*
+ * Extended Op for named ops (Scope, Method, etc.), deferred ops (Methods and op_regions),
+ * and bytelists.
+ */
+struct acpi_parse_obj_named {
+ ACPI_PARSE_COMMON char *path;
+ u8 *data; /* AML body or bytelist data */
+ u32 length; /* AML length */
+ u32 name; /* 4-byte name or zero if no name */
+};
+
+/* This version is used by the iASL compiler only */
+
+#define ACPI_MAX_PARSEOP_NAME 20
+
+struct acpi_parse_obj_asl {
+ ACPI_PARSE_COMMON union acpi_parse_object *child;
+ union acpi_parse_object *parent_method;
+ char *filename;
+ u8 file_changed;
+ char *parent_filename;
+ char *external_name;
+ char *namepath;
+ char name_seg[4];
+ u32 extra_value;
+ u32 column;
+ u32 line_number;
+ u32 logical_line_number;
+ u32 logical_byte_offset;
+ u32 end_line;
+ u32 end_logical_line;
+ u32 acpi_btype;
+ u32 aml_length;
+ u32 aml_subtree_length;
+ u32 final_aml_length;
+ u32 final_aml_offset;
+ u32 compile_flags;
+ u16 parse_opcode;
+ u8 aml_opcode_length;
+ u8 aml_pkg_len_bytes;
+ u8 extra;
+ char parse_op_name[ACPI_MAX_PARSEOP_NAME];
+};
+
+union acpi_parse_object {
+ struct acpi_parse_obj_common common;
+ struct acpi_parse_obj_named named;
+ struct acpi_parse_obj_asl asl;
+};
+
+struct asl_comment_state {
+ u8 comment_type;
+ u32 spaces_before;
+ union acpi_parse_object *latest_parse_op;
+ union acpi_parse_object *parsing_paren_brace_node;
+ u8 capture_comments;
+};
+
+/*
+ * Parse state - one state per parser invocation and each control
+ * method.
+ */
+struct acpi_parse_state {
+ u8 *aml_start; /* First AML byte */
+ u8 *aml; /* Next AML byte */
+ u8 *aml_end; /* (last + 1) AML byte */
+ u8 *pkg_start; /* Current package begin */
+ u8 *pkg_end; /* Current package end */
+ union acpi_parse_object *start_op; /* Root of parse tree */
+ struct acpi_namespace_node *start_node;
+ union acpi_generic_state *scope; /* Current scope */
+ union acpi_parse_object *start_scope;
+ u32 aml_size;
+};
+
+/* Parse object flags */
+
+#define ACPI_PARSEOP_GENERIC 0x01
+#define ACPI_PARSEOP_NAMED_OBJECT 0x02
+#define ACPI_PARSEOP_DEFERRED 0x04
+#define ACPI_PARSEOP_BYTELIST 0x08
+#define ACPI_PARSEOP_IN_STACK 0x10
+#define ACPI_PARSEOP_TARGET 0x20
+#define ACPI_PARSEOP_IN_CACHE 0x80
+
+/* Parse object disasm_flags */
+
+#define ACPI_PARSEOP_IGNORE 0x0001
+#define ACPI_PARSEOP_PARAMETER_LIST 0x0002
+#define ACPI_PARSEOP_EMPTY_TERMLIST 0x0004
+#define ACPI_PARSEOP_PREDEFINED_CHECKED 0x0008
+#define ACPI_PARSEOP_CLOSING_PAREN 0x0010
+#define ACPI_PARSEOP_COMPOUND_ASSIGNMENT 0x0020
+#define ACPI_PARSEOP_ASSIGNMENT 0x0040
+#define ACPI_PARSEOP_ELSEIF 0x0080
+#define ACPI_PARSEOP_LEGACY_ASL_ONLY 0x0100
+
+/*****************************************************************************
+ *
+ * Hardware (ACPI registers) and PNP
+ *
+ ****************************************************************************/
+
+struct acpi_bit_register_info {
+ u8 parent_register;
+ u8 bit_position;
+ u16 access_bit_mask;
+};
+
+/*
+ * Some ACPI registers have bits that must be ignored -- meaning that they
+ * must be preserved.
+ */
+#define ACPI_PM1_STATUS_PRESERVED_BITS 0x0800 /* Bit 11 */
+
+/* Write-only bits must be zeroed by software */
+
+#define ACPI_PM1_CONTROL_WRITEONLY_BITS 0x2004 /* Bits 13, 2 */
+
+/* For control registers, both ignored and reserved bits must be preserved */
+
+/*
+ * For PM1 control, the SCI enable bit (bit 0, SCI_EN) is defined by the
+ * ACPI specification to be a "preserved" bit - "OSPM always preserves this
+ * bit position", section 4.7.3.2.1. However, on some machines the OS must
+ * write a one to this bit after resume for the machine to work properly.
+ * To enable this, we no longer attempt to preserve this bit. No machines
+ * are known to fail if the bit is not preserved. (May 2009)
+ */
+#define ACPI_PM1_CONTROL_IGNORED_BITS 0x0200 /* Bit 9 */
+#define ACPI_PM1_CONTROL_RESERVED_BITS 0xC1F8 /* Bits 14-15, 3-8 */
+#define ACPI_PM1_CONTROL_PRESERVED_BITS \
+ (ACPI_PM1_CONTROL_IGNORED_BITS | ACPI_PM1_CONTROL_RESERVED_BITS)
+
+#define ACPI_PM2_CONTROL_PRESERVED_BITS 0xFFFFFFFE /* All except bit 0 */
+
+/*
+ * Register IDs
+ * These are the full ACPI registers
+ */
+#define ACPI_REGISTER_PM1_STATUS 0x01
+#define ACPI_REGISTER_PM1_ENABLE 0x02
+#define ACPI_REGISTER_PM1_CONTROL 0x03
+#define ACPI_REGISTER_PM2_CONTROL 0x04
+#define ACPI_REGISTER_PM_TIMER 0x05
+#define ACPI_REGISTER_PROCESSOR_BLOCK 0x06
+#define ACPI_REGISTER_SMI_COMMAND_BLOCK 0x07
+
+/* Masks used to access the bit_registers */
+
+#define ACPI_BITMASK_TIMER_STATUS 0x0001
+#define ACPI_BITMASK_BUS_MASTER_STATUS 0x0010
+#define ACPI_BITMASK_GLOBAL_LOCK_STATUS 0x0020
+#define ACPI_BITMASK_POWER_BUTTON_STATUS 0x0100
+#define ACPI_BITMASK_SLEEP_BUTTON_STATUS 0x0200
+#define ACPI_BITMASK_RT_CLOCK_STATUS 0x0400
+#define ACPI_BITMASK_PCIEXP_WAKE_STATUS 0x4000 /* ACPI 3.0 */
+#define ACPI_BITMASK_WAKE_STATUS 0x8000
+
+#define ACPI_BITMASK_ALL_FIXED_STATUS (\
+ ACPI_BITMASK_TIMER_STATUS | \
+ ACPI_BITMASK_BUS_MASTER_STATUS | \
+ ACPI_BITMASK_GLOBAL_LOCK_STATUS | \
+ ACPI_BITMASK_POWER_BUTTON_STATUS | \
+ ACPI_BITMASK_SLEEP_BUTTON_STATUS | \
+ ACPI_BITMASK_RT_CLOCK_STATUS | \
+ ACPI_BITMASK_PCIEXP_WAKE_STATUS | \
+ ACPI_BITMASK_WAKE_STATUS)
+
+#define ACPI_BITMASK_TIMER_ENABLE 0x0001
+#define ACPI_BITMASK_GLOBAL_LOCK_ENABLE 0x0020
+#define ACPI_BITMASK_POWER_BUTTON_ENABLE 0x0100
+#define ACPI_BITMASK_SLEEP_BUTTON_ENABLE 0x0200
+#define ACPI_BITMASK_RT_CLOCK_ENABLE 0x0400
+#define ACPI_BITMASK_PCIEXP_WAKE_DISABLE 0x4000 /* ACPI 3.0 */
+
+#define ACPI_BITMASK_SCI_ENABLE 0x0001
+#define ACPI_BITMASK_BUS_MASTER_RLD 0x0002
+#define ACPI_BITMASK_GLOBAL_LOCK_RELEASE 0x0004
+#define ACPI_BITMASK_SLEEP_TYPE 0x1C00
+#define ACPI_BITMASK_SLEEP_ENABLE 0x2000
+
+#define ACPI_BITMASK_ARB_DISABLE 0x0001
+
+/* Raw bit position of each bit_register */
+
+#define ACPI_BITPOSITION_TIMER_STATUS 0x00
+#define ACPI_BITPOSITION_BUS_MASTER_STATUS 0x04
+#define ACPI_BITPOSITION_GLOBAL_LOCK_STATUS 0x05
+#define ACPI_BITPOSITION_POWER_BUTTON_STATUS 0x08
+#define ACPI_BITPOSITION_SLEEP_BUTTON_STATUS 0x09
+#define ACPI_BITPOSITION_RT_CLOCK_STATUS 0x0A
+#define ACPI_BITPOSITION_PCIEXP_WAKE_STATUS 0x0E /* ACPI 3.0 */
+#define ACPI_BITPOSITION_WAKE_STATUS 0x0F
+
+#define ACPI_BITPOSITION_TIMER_ENABLE 0x00
+#define ACPI_BITPOSITION_GLOBAL_LOCK_ENABLE 0x05
+#define ACPI_BITPOSITION_POWER_BUTTON_ENABLE 0x08
+#define ACPI_BITPOSITION_SLEEP_BUTTON_ENABLE 0x09
+#define ACPI_BITPOSITION_RT_CLOCK_ENABLE 0x0A
+#define ACPI_BITPOSITION_PCIEXP_WAKE_DISABLE 0x0E /* ACPI 3.0 */
+
+#define ACPI_BITPOSITION_SCI_ENABLE 0x00
+#define ACPI_BITPOSITION_BUS_MASTER_RLD 0x01
+#define ACPI_BITPOSITION_GLOBAL_LOCK_RELEASE 0x02
+#define ACPI_BITPOSITION_SLEEP_TYPE 0x0A
+#define ACPI_BITPOSITION_SLEEP_ENABLE 0x0D
+
+#define ACPI_BITPOSITION_ARB_DISABLE 0x00
+
+/* Structs and definitions for _OSI support and I/O port validation */
+
+#define ACPI_ALWAYS_ILLEGAL 0x00
+
+struct acpi_interface_info {
+ char *name;
+ struct acpi_interface_info *next;
+ u8 flags;
+ u8 value;
+};
+
+#define ACPI_OSI_INVALID 0x01
+#define ACPI_OSI_DYNAMIC 0x02
+#define ACPI_OSI_FEATURE 0x04
+#define ACPI_OSI_DEFAULT_INVALID 0x08
+#define ACPI_OSI_OPTIONAL_FEATURE (ACPI_OSI_FEATURE | ACPI_OSI_DEFAULT_INVALID | ACPI_OSI_INVALID)
+
+struct acpi_port_info {
+ char *name;
+ u16 start;
+ u16 end;
+ u8 osi_dependency;
+};
+
+/*****************************************************************************
+ *
+ * Resource descriptors
+ *
+ ****************************************************************************/
+
+/* resource_type values */
+
+#define ACPI_ADDRESS_TYPE_MEMORY_RANGE 0
+#define ACPI_ADDRESS_TYPE_IO_RANGE 1
+#define ACPI_ADDRESS_TYPE_BUS_NUMBER_RANGE 2
+
+/* Resource descriptor types and masks */
+
+#define ACPI_RESOURCE_NAME_LARGE 0x80
+#define ACPI_RESOURCE_NAME_SMALL 0x00
+
+#define ACPI_RESOURCE_NAME_SMALL_MASK 0x78 /* Bits 6:3 contain the type */
+#define ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK 0x07 /* Bits 2:0 contain the length */
+#define ACPI_RESOURCE_NAME_LARGE_MASK 0x7F /* Bits 6:0 contain the type */
+
+/*
+ * Small resource descriptor "names" as defined by the ACPI specification.
+ * Note: Bits 2:0 are used for the descriptor length
+ */
+#define ACPI_RESOURCE_NAME_IRQ 0x20
+#define ACPI_RESOURCE_NAME_DMA 0x28
+#define ACPI_RESOURCE_NAME_START_DEPENDENT 0x30
+#define ACPI_RESOURCE_NAME_END_DEPENDENT 0x38
+#define ACPI_RESOURCE_NAME_IO 0x40
+#define ACPI_RESOURCE_NAME_FIXED_IO 0x48
+#define ACPI_RESOURCE_NAME_FIXED_DMA 0x50
+#define ACPI_RESOURCE_NAME_RESERVED_S2 0x58
+#define ACPI_RESOURCE_NAME_RESERVED_S3 0x60
+#define ACPI_RESOURCE_NAME_RESERVED_S4 0x68
+#define ACPI_RESOURCE_NAME_VENDOR_SMALL 0x70
+#define ACPI_RESOURCE_NAME_END_TAG 0x78
+
+/*
+ * Large resource descriptor "names" as defined by the ACPI specification.
+ * Note: includes the Large Descriptor bit in bit[7]
+ */
+#define ACPI_RESOURCE_NAME_MEMORY24 0x81
+#define ACPI_RESOURCE_NAME_GENERIC_REGISTER 0x82
+#define ACPI_RESOURCE_NAME_RESERVED_L1 0x83
+#define ACPI_RESOURCE_NAME_VENDOR_LARGE 0x84
+#define ACPI_RESOURCE_NAME_MEMORY32 0x85
+#define ACPI_RESOURCE_NAME_FIXED_MEMORY32 0x86
+#define ACPI_RESOURCE_NAME_ADDRESS32 0x87
+#define ACPI_RESOURCE_NAME_ADDRESS16 0x88
+#define ACPI_RESOURCE_NAME_EXTENDED_IRQ 0x89
+#define ACPI_RESOURCE_NAME_ADDRESS64 0x8A
+#define ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 0x8B
+#define ACPI_RESOURCE_NAME_GPIO 0x8C
+#define ACPI_RESOURCE_NAME_PIN_FUNCTION 0x8D
+#define ACPI_RESOURCE_NAME_SERIAL_BUS 0x8E
+#define ACPI_RESOURCE_NAME_PIN_CONFIG 0x8F
+#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
+
+/*****************************************************************************
+ *
+ * Miscellaneous
+ *
+ ****************************************************************************/
+
+#define ACPI_ASCII_ZERO 0x30
+
+/*****************************************************************************
+ *
+ * Disassembler
+ *
+ ****************************************************************************/
+
+struct acpi_external_list {
+ char *path;
+ char *internal_path;
+ struct acpi_external_list *next;
+ u32 value;
+ u16 length;
+ u16 flags;
+ u8 type;
+};
+
+/* Values for Flags field above */
+
+#define ACPI_EXT_RESOLVED_REFERENCE 0x01 /* Object was resolved during cross ref */
+#define ACPI_EXT_ORIGIN_FROM_FILE 0x02 /* External came from a file */
+#define ACPI_EXT_INTERNAL_PATH_ALLOCATED 0x04 /* Deallocate internal path on completion */
+#define ACPI_EXT_EXTERNAL_EMITTED 0x08 /* External() statement has been emitted */
+#define ACPI_EXT_ORIGIN_FROM_OPCODE 0x10 /* External came from a External() opcode */
+#define ACPI_EXT_CONFLICTING_DECLARATION 0x20 /* External has a conflicting declaration within AML */
+
+struct acpi_external_file {
+ char *path;
+ struct acpi_external_file *next;
+};
+
+struct acpi_parse_object_list {
+ union acpi_parse_object *op;
+ struct acpi_parse_object_list *next;
+};
+
+/*****************************************************************************
+ *
+ * Debugger
+ *
+ ****************************************************************************/
+
+struct acpi_db_method_info {
+ acpi_handle method;
+ acpi_handle main_thread_gate;
+ acpi_handle thread_complete_gate;
+ acpi_handle info_gate;
+ acpi_thread_id *threads;
+ u32 num_threads;
+ u32 num_created;
+ u32 num_completed;
+
+ char *name;
+ u32 flags;
+ u32 num_loops;
+ char pathname[ACPI_DB_LINE_BUFFER_SIZE];
+ char **args;
+ acpi_object_type *types;
+
+ /*
+ * Arguments to be passed to method for the commands Threads and
+ * Background. Note, ACPI specifies a maximum of 7 arguments (0 - 6).
+ *
+ * For the Threads command, the Number of threads, ID of current
+ * thread and Index of current thread inside all them created.
+ */
+ char init_args;
+#ifdef ACPI_DEBUGGER
+ acpi_object_type arg_types[ACPI_METHOD_NUM_ARGS];
+#endif
+ char *arguments[ACPI_METHOD_NUM_ARGS];
+ char num_threads_str[11];
+ char id_of_thread_str[11];
+ char index_of_thread_str[11];
+};
+
+struct acpi_integrity_info {
+ u32 nodes;
+ u32 objects;
+};
+
+#define ACPI_DB_DISABLE_OUTPUT 0x00
+#define ACPI_DB_REDIRECTABLE_OUTPUT 0x01
+#define ACPI_DB_CONSOLE_OUTPUT 0x02
+#define ACPI_DB_DUPLICATE_OUTPUT 0x03
+
+struct acpi_object_info {
+ u32 types[ACPI_TOTAL_TYPES];
+};
+
+/*****************************************************************************
+ *
+ * Debug
+ *
+ ****************************************************************************/
+
+/* Entry for a memory allocation (debug only) */
+
+#define ACPI_MEM_MALLOC 0
+#define ACPI_MEM_CALLOC 1
+#define ACPI_MAX_MODULE_NAME 16
+
+#define ACPI_COMMON_DEBUG_MEM_HEADER \
+ struct acpi_debug_mem_block *previous; \
+ struct acpi_debug_mem_block *next; \
+ u32 size; \
+ u32 component; \
+ u32 line; \
+ char module[ACPI_MAX_MODULE_NAME]; \
+ u8 alloc_type;
+
+struct acpi_debug_mem_header {
+ACPI_COMMON_DEBUG_MEM_HEADER};
+
+struct acpi_debug_mem_block {
+ ACPI_COMMON_DEBUG_MEM_HEADER u64 user_space;
+};
+
+#define ACPI_MEM_LIST_GLOBAL 0
+#define ACPI_MEM_LIST_NSNODE 1
+#define ACPI_MEM_LIST_MAX 1
+#define ACPI_NUM_MEM_LISTS 2
+
+/*****************************************************************************
+ *
+ * Info/help support
+ *
+ ****************************************************************************/
+
+struct ah_predefined_name {
+ char *name;
+ char *description;
+#ifndef ACPI_ASL_COMPILER
+ char *action;
+#endif
+};
+
+struct ah_device_id {
+ char *name;
+ char *description;
+};
+
+struct ah_uuid {
+ char *description;
+ char *string;
+};
+
+struct ah_table {
+ char *signature;
+ char *description;
+};
+
+#endif /* __ACLOCAL_H__ */
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
new file mode 100644
index 000000000..2f3e609df
--- /dev/null
+++ b/drivers/acpi/acpica/acmacros.h
@@ -0,0 +1,499 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acmacros.h - C macros for the entire subsystem.
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACMACROS_H__
+#define __ACMACROS_H__
+
+/*
+ * Extract data using a pointer. Any more than a byte and we
+ * get into potential alignment issues -- see the STORE macros below.
+ * Use with care.
+ */
+#define ACPI_CAST8(ptr) ACPI_CAST_PTR (u8, (ptr))
+#define ACPI_CAST16(ptr) ACPI_CAST_PTR (u16, (ptr))
+#define ACPI_CAST32(ptr) ACPI_CAST_PTR (u32, (ptr))
+#define ACPI_CAST64(ptr) ACPI_CAST_PTR (u64, (ptr))
+#define ACPI_GET8(ptr) (*ACPI_CAST8 (ptr))
+#define ACPI_GET16(ptr) (*ACPI_CAST16 (ptr))
+#define ACPI_GET32(ptr) (*ACPI_CAST32 (ptr))
+#define ACPI_GET64(ptr) (*ACPI_CAST64 (ptr))
+#define ACPI_SET8(ptr, val) (*ACPI_CAST8 (ptr) = (u8) (val))
+#define ACPI_SET16(ptr, val) (*ACPI_CAST16 (ptr) = (u16) (val))
+#define ACPI_SET32(ptr, val) (*ACPI_CAST32 (ptr) = (u32) (val))
+#define ACPI_SET64(ptr, val) (*ACPI_CAST64 (ptr) = (u64) (val))
+
+/*
+ * printf() format helper. This macro is a workaround for the difficulties
+ * with emitting 64-bit integers and 64-bit pointers with the same code
+ * for both 32-bit and 64-bit hosts.
+ */
+#define ACPI_FORMAT_UINT64(i) ACPI_HIDWORD(i), ACPI_LODWORD(i)
+
+/*
+ * Macros for moving data around to/from buffers that are possibly unaligned.
+ * If the hardware supports the transfer of unaligned data, just do the store.
+ * Otherwise, we have to move one byte at a time.
+ */
+#ifdef ACPI_BIG_ENDIAN
+/*
+ * Macros for big-endian machines
+ */
+
+/* These macros reverse the bytes during the move, converting little-endian to big endian */
+
+ /* Big Endian <== Little Endian */
+ /* Hi...Lo Lo...Hi */
+/* 16-bit source, 16/32/64 destination */
+
+#define ACPI_MOVE_16_TO_16(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[1];\
+ (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[0];}
+
+#define ACPI_MOVE_16_TO_32(d, s) {(*(u32 *)(void *)(d))=0;\
+ ((u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[1];\
+ ((u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[0];}
+
+#define ACPI_MOVE_16_TO_64(d, s) {(*(u64 *)(void *)(d))=0;\
+ ((u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[1];\
+ ((u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[0];}
+
+/* 32-bit source, 16/32/64 destination */
+
+#define ACPI_MOVE_32_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */
+
+#define ACPI_MOVE_32_TO_32(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[3];\
+ (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[2];\
+ (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[1];\
+ (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[0];}
+
+#define ACPI_MOVE_32_TO_64(d, s) {(*(u64 *)(void *)(d))=0;\
+ ((u8 *)(void *)(d))[4] = ((u8 *)(void *)(s))[3];\
+ ((u8 *)(void *)(d))[5] = ((u8 *)(void *)(s))[2];\
+ ((u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[1];\
+ ((u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[0];}
+
+/* 64-bit source, 16/32/64 destination */
+
+#define ACPI_MOVE_64_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */
+
+#define ACPI_MOVE_64_TO_32(d, s) ACPI_MOVE_32_TO_32(d, s) /* Truncate to 32 */
+
+#define ACPI_MOVE_64_TO_64(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[7];\
+ (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[6];\
+ (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[5];\
+ (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[4];\
+ (( u8 *)(void *)(d))[4] = ((u8 *)(void *)(s))[3];\
+ (( u8 *)(void *)(d))[5] = ((u8 *)(void *)(s))[2];\
+ (( u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[1];\
+ (( u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[0];}
+#else
+/*
+ * Macros for little-endian machines
+ */
+
+#ifndef ACPI_MISALIGNMENT_NOT_SUPPORTED
+
+/* The hardware supports unaligned transfers, just do the little-endian move */
+
+/* 16-bit source, 16/32/64 destination */
+
+#define ACPI_MOVE_16_TO_16(d, s) *(u16 *)(void *)(d) = *(u16 *)(void *)(s)
+#define ACPI_MOVE_16_TO_32(d, s) *(u32 *)(void *)(d) = *(u16 *)(void *)(s)
+#define ACPI_MOVE_16_TO_64(d, s) *(u64 *)(void *)(d) = *(u16 *)(void *)(s)
+
+/* 32-bit source, 16/32/64 destination */
+
+#define ACPI_MOVE_32_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */
+#define ACPI_MOVE_32_TO_32(d, s) *(u32 *)(void *)(d) = *(u32 *)(void *)(s)
+#define ACPI_MOVE_32_TO_64(d, s) *(u64 *)(void *)(d) = *(u32 *)(void *)(s)
+
+/* 64-bit source, 16/32/64 destination */
+
+#define ACPI_MOVE_64_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */
+#define ACPI_MOVE_64_TO_32(d, s) ACPI_MOVE_32_TO_32(d, s) /* Truncate to 32 */
+#define ACPI_MOVE_64_TO_64(d, s) *(u64 *)(void *)(d) = *(u64 *)(void *)(s)
+
+#else
+/*
+ * The hardware does not support unaligned transfers. We must move the
+ * data one byte at a time. These macros work whether the source or
+ * the destination (or both) is/are unaligned. (Little-endian move)
+ */
+
+/* 16-bit source, 16/32/64 destination */
+
+#define ACPI_MOVE_16_TO_16(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[0];\
+ (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[1];}
+
+#define ACPI_MOVE_16_TO_32(d, s) {(*(u32 *)(void *)(d)) = 0; ACPI_MOVE_16_TO_16(d, s);}
+#define ACPI_MOVE_16_TO_64(d, s) {(*(u64 *)(void *)(d)) = 0; ACPI_MOVE_16_TO_16(d, s);}
+
+/* 32-bit source, 16/32/64 destination */
+
+#define ACPI_MOVE_32_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */
+
+#define ACPI_MOVE_32_TO_32(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[0];\
+ (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[1];\
+ (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[2];\
+ (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[3];}
+
+#define ACPI_MOVE_32_TO_64(d, s) {(*(u64 *)(void *)(d)) = 0; ACPI_MOVE_32_TO_32(d, s);}
+
+/* 64-bit source, 16/32/64 destination */
+
+#define ACPI_MOVE_64_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */
+#define ACPI_MOVE_64_TO_32(d, s) ACPI_MOVE_32_TO_32(d, s) /* Truncate to 32 */
+#define ACPI_MOVE_64_TO_64(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[0];\
+ (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[1];\
+ (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[2];\
+ (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[3];\
+ (( u8 *)(void *)(d))[4] = ((u8 *)(void *)(s))[4];\
+ (( u8 *)(void *)(d))[5] = ((u8 *)(void *)(s))[5];\
+ (( u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[6];\
+ (( u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[7];}
+#endif
+#endif
+
+/*
+ * Fast power-of-two math macros for non-optimized compilers
+ */
+#define _ACPI_DIV(value, power_of2) ((u32) ((value) >> (power_of2)))
+#define _ACPI_MUL(value, power_of2) ((u32) ((value) << (power_of2)))
+#define _ACPI_MOD(value, divisor) ((u32) ((value) & ((divisor) -1)))
+
+#define ACPI_DIV_2(a) _ACPI_DIV(a, 1)
+#define ACPI_MUL_2(a) _ACPI_MUL(a, 1)
+#define ACPI_MOD_2(a) _ACPI_MOD(a, 2)
+
+#define ACPI_DIV_4(a) _ACPI_DIV(a, 2)
+#define ACPI_MUL_4(a) _ACPI_MUL(a, 2)
+#define ACPI_MOD_4(a) _ACPI_MOD(a, 4)
+
+#define ACPI_DIV_8(a) _ACPI_DIV(a, 3)
+#define ACPI_MUL_8(a) _ACPI_MUL(a, 3)
+#define ACPI_MOD_8(a) _ACPI_MOD(a, 8)
+
+#define ACPI_DIV_16(a) _ACPI_DIV(a, 4)
+#define ACPI_MUL_16(a) _ACPI_MUL(a, 4)
+#define ACPI_MOD_16(a) _ACPI_MOD(a, 16)
+
+#define ACPI_DIV_32(a) _ACPI_DIV(a, 5)
+#define ACPI_MUL_32(a) _ACPI_MUL(a, 5)
+#define ACPI_MOD_32(a) _ACPI_MOD(a, 32)
+
+/* Test for ASCII character */
+
+#define ACPI_IS_ASCII(c) ((c) < 0x80)
+
+/* Signed integers */
+
+#define ACPI_SIGN_POSITIVE 0
+#define ACPI_SIGN_NEGATIVE 1
+
+/*
+ * Rounding macros (Power of two boundaries only)
+ */
+#define ACPI_ROUND_DOWN(value, boundary) (((acpi_size)(value)) & \
+ (~(((acpi_size) boundary)-1)))
+
+#define ACPI_ROUND_UP(value, boundary) ((((acpi_size)(value)) + \
+ (((acpi_size) boundary)-1)) & \
+ (~(((acpi_size) boundary)-1)))
+
+/* Note: sizeof(acpi_size) evaluates to either 4 or 8 (32- vs 64-bit mode) */
+
+#define ACPI_ROUND_DOWN_TO_32BIT(a) ACPI_ROUND_DOWN(a, 4)
+#define ACPI_ROUND_DOWN_TO_64BIT(a) ACPI_ROUND_DOWN(a, 8)
+#define ACPI_ROUND_DOWN_TO_NATIVE_WORD(a) ACPI_ROUND_DOWN(a, sizeof(acpi_size))
+
+#define ACPI_ROUND_UP_TO_32BIT(a) ACPI_ROUND_UP(a, 4)
+#define ACPI_ROUND_UP_TO_64BIT(a) ACPI_ROUND_UP(a, 8)
+#define ACPI_ROUND_UP_TO_NATIVE_WORD(a) ACPI_ROUND_UP(a, sizeof(acpi_size))
+
+#define ACPI_ROUND_BITS_UP_TO_BYTES(a) ACPI_DIV_8((a) + 7)
+#define ACPI_ROUND_BITS_DOWN_TO_BYTES(a) ACPI_DIV_8((a))
+
+#define ACPI_ROUND_UP_TO_1K(a) (((a) + 1023) >> 10)
+
+/* Generic (non-power-of-two) rounding */
+
+#define ACPI_ROUND_UP_TO(value, boundary) (((value) + ((boundary)-1)) / (boundary))
+
+#define ACPI_IS_MISALIGNED(value) (((acpi_size) value) & (sizeof(acpi_size)-1))
+
+/* Generic bit manipulation */
+
+#ifndef ACPI_USE_NATIVE_BIT_FINDER
+
+#define __ACPI_FIND_LAST_BIT_2(a, r) ((((u8) (a)) & 0x02) ? (r)+1 : (r))
+#define __ACPI_FIND_LAST_BIT_4(a, r) ((((u8) (a)) & 0x0C) ? \
+ __ACPI_FIND_LAST_BIT_2 ((a)>>2, (r)+2) : \
+ __ACPI_FIND_LAST_BIT_2 ((a), (r)))
+#define __ACPI_FIND_LAST_BIT_8(a, r) ((((u8) (a)) & 0xF0) ? \
+ __ACPI_FIND_LAST_BIT_4 ((a)>>4, (r)+4) : \
+ __ACPI_FIND_LAST_BIT_4 ((a), (r)))
+#define __ACPI_FIND_LAST_BIT_16(a, r) ((((u16) (a)) & 0xFF00) ? \
+ __ACPI_FIND_LAST_BIT_8 ((a)>>8, (r)+8) : \
+ __ACPI_FIND_LAST_BIT_8 ((a), (r)))
+#define __ACPI_FIND_LAST_BIT_32(a, r) ((((u32) (a)) & 0xFFFF0000) ? \
+ __ACPI_FIND_LAST_BIT_16 ((a)>>16, (r)+16) : \
+ __ACPI_FIND_LAST_BIT_16 ((a), (r)))
+#define __ACPI_FIND_LAST_BIT_64(a, r) ((((u64) (a)) & 0xFFFFFFFF00000000) ? \
+ __ACPI_FIND_LAST_BIT_32 ((a)>>32, (r)+32) : \
+ __ACPI_FIND_LAST_BIT_32 ((a), (r)))
+
+#define ACPI_FIND_LAST_BIT_8(a) ((a) ? __ACPI_FIND_LAST_BIT_8 (a, 1) : 0)
+#define ACPI_FIND_LAST_BIT_16(a) ((a) ? __ACPI_FIND_LAST_BIT_16 (a, 1) : 0)
+#define ACPI_FIND_LAST_BIT_32(a) ((a) ? __ACPI_FIND_LAST_BIT_32 (a, 1) : 0)
+#define ACPI_FIND_LAST_BIT_64(a) ((a) ? __ACPI_FIND_LAST_BIT_64 (a, 1) : 0)
+
+#define __ACPI_FIND_FIRST_BIT_2(a, r) ((((u8) (a)) & 0x01) ? (r) : (r)+1)
+#define __ACPI_FIND_FIRST_BIT_4(a, r) ((((u8) (a)) & 0x03) ? \
+ __ACPI_FIND_FIRST_BIT_2 ((a), (r)) : \
+ __ACPI_FIND_FIRST_BIT_2 ((a)>>2, (r)+2))
+#define __ACPI_FIND_FIRST_BIT_8(a, r) ((((u8) (a)) & 0x0F) ? \
+ __ACPI_FIND_FIRST_BIT_4 ((a), (r)) : \
+ __ACPI_FIND_FIRST_BIT_4 ((a)>>4, (r)+4))
+#define __ACPI_FIND_FIRST_BIT_16(a, r) ((((u16) (a)) & 0x00FF) ? \
+ __ACPI_FIND_FIRST_BIT_8 ((a), (r)) : \
+ __ACPI_FIND_FIRST_BIT_8 ((a)>>8, (r)+8))
+#define __ACPI_FIND_FIRST_BIT_32(a, r) ((((u32) (a)) & 0x0000FFFF) ? \
+ __ACPI_FIND_FIRST_BIT_16 ((a), (r)) : \
+ __ACPI_FIND_FIRST_BIT_16 ((a)>>16, (r)+16))
+#define __ACPI_FIND_FIRST_BIT_64(a, r) ((((u64) (a)) & 0x00000000FFFFFFFF) ? \
+ __ACPI_FIND_FIRST_BIT_32 ((a), (r)) : \
+ __ACPI_FIND_FIRST_BIT_32 ((a)>>32, (r)+32))
+
+#define ACPI_FIND_FIRST_BIT_8(a) ((a) ? __ACPI_FIND_FIRST_BIT_8 (a, 1) : 0)
+#define ACPI_FIND_FIRST_BIT_16(a) ((a) ? __ACPI_FIND_FIRST_BIT_16 (a, 1) : 0)
+#define ACPI_FIND_FIRST_BIT_32(a) ((a) ? __ACPI_FIND_FIRST_BIT_32 (a, 1) : 0)
+#define ACPI_FIND_FIRST_BIT_64(a) ((a) ? __ACPI_FIND_FIRST_BIT_64 (a, 1) : 0)
+
+#endif /* ACPI_USE_NATIVE_BIT_FINDER */
+
+/* Generic (power-of-two) rounding */
+
+#define ACPI_ROUND_UP_POWER_OF_TWO_8(a) ((u8) \
+ (((u16) 1) << ACPI_FIND_LAST_BIT_8 ((a) - 1)))
+#define ACPI_ROUND_DOWN_POWER_OF_TWO_8(a) ((u8) \
+ (((u16) 1) << (ACPI_FIND_LAST_BIT_8 ((a)) - 1)))
+#define ACPI_ROUND_UP_POWER_OF_TWO_16(a) ((u16) \
+ (((u32) 1) << ACPI_FIND_LAST_BIT_16 ((a) - 1)))
+#define ACPI_ROUND_DOWN_POWER_OF_TWO_16(a) ((u16) \
+ (((u32) 1) << (ACPI_FIND_LAST_BIT_16 ((a)) - 1)))
+#define ACPI_ROUND_UP_POWER_OF_TWO_32(a) ((u32) \
+ (((u64) 1) << ACPI_FIND_LAST_BIT_32 ((a) - 1)))
+#define ACPI_ROUND_DOWN_POWER_OF_TWO_32(a) ((u32) \
+ (((u64) 1) << (ACPI_FIND_LAST_BIT_32 ((a)) - 1)))
+#define ACPI_IS_ALIGNED(a, s) (((a) & ((s) - 1)) == 0)
+#define ACPI_IS_POWER_OF_TWO(a) ACPI_IS_ALIGNED(a, a)
+
+/*
+ * Bitmask creation
+ * Bit positions start at zero.
+ * MASK_BITS_ABOVE creates a mask starting AT the position and above
+ * MASK_BITS_BELOW creates a mask starting one bit BELOW the position
+ * MASK_BITS_ABOVE/BELOW accepts a bit offset to create a mask
+ * MASK_BITS_ABOVE/BELOW_32/64 accepts a bit width to create a mask
+ * Note: The ACPI_INTEGER_BIT_SIZE check is used to bypass compiler
+ * differences with the shift operator
+ */
+#define ACPI_MASK_BITS_ABOVE(position) (~((ACPI_UINT64_MAX) << ((u32) (position))))
+#define ACPI_MASK_BITS_BELOW(position) ((ACPI_UINT64_MAX) << ((u32) (position)))
+#define ACPI_MASK_BITS_ABOVE_32(width) ((u32) ACPI_MASK_BITS_ABOVE(width))
+#define ACPI_MASK_BITS_BELOW_32(width) ((u32) ACPI_MASK_BITS_BELOW(width))
+#define ACPI_MASK_BITS_ABOVE_64(width) ((width) == ACPI_INTEGER_BIT_SIZE ? \
+ ACPI_UINT64_MAX : \
+ ACPI_MASK_BITS_ABOVE(width))
+#define ACPI_MASK_BITS_BELOW_64(width) ((width) == ACPI_INTEGER_BIT_SIZE ? \
+ (u64) 0 : \
+ ACPI_MASK_BITS_BELOW(width))
+
+/* Bitfields within ACPI registers */
+
+#define ACPI_REGISTER_PREPARE_BITS(val, pos, mask) \
+ ((val << pos) & mask)
+
+#define ACPI_REGISTER_INSERT_VALUE(reg, pos, mask, val) \
+ reg = (reg & (~(mask))) | ACPI_REGISTER_PREPARE_BITS(val, pos, mask)
+
+#define ACPI_INSERT_BITS(target, mask, source) \
+ target = ((target & (~(mask))) | (source & mask))
+
+/* Generic bitfield macros and masks */
+
+#define ACPI_GET_BITS(source_ptr, position, mask) \
+ ((*(source_ptr) >> (position)) & (mask))
+
+#define ACPI_SET_BITS(target_ptr, position, mask, value) \
+ (*(target_ptr) |= (((value) & (mask)) << (position)))
+
+#define ACPI_1BIT_MASK 0x00000001
+#define ACPI_2BIT_MASK 0x00000003
+#define ACPI_3BIT_MASK 0x00000007
+#define ACPI_4BIT_MASK 0x0000000F
+#define ACPI_5BIT_MASK 0x0000001F
+#define ACPI_6BIT_MASK 0x0000003F
+#define ACPI_7BIT_MASK 0x0000007F
+#define ACPI_8BIT_MASK 0x000000FF
+#define ACPI_16BIT_MASK 0x0000FFFF
+#define ACPI_24BIT_MASK 0x00FFFFFF
+
+/* Macros to extract flag bits from position zero */
+
+#define ACPI_GET_1BIT_FLAG(value) ((value) & ACPI_1BIT_MASK)
+#define ACPI_GET_2BIT_FLAG(value) ((value) & ACPI_2BIT_MASK)
+#define ACPI_GET_3BIT_FLAG(value) ((value) & ACPI_3BIT_MASK)
+#define ACPI_GET_4BIT_FLAG(value) ((value) & ACPI_4BIT_MASK)
+
+/* Macros to extract flag bits from position one and above */
+
+#define ACPI_EXTRACT_1BIT_FLAG(field, position) (ACPI_GET_1BIT_FLAG ((field) >> position))
+#define ACPI_EXTRACT_2BIT_FLAG(field, position) (ACPI_GET_2BIT_FLAG ((field) >> position))
+#define ACPI_EXTRACT_3BIT_FLAG(field, position) (ACPI_GET_3BIT_FLAG ((field) >> position))
+#define ACPI_EXTRACT_4BIT_FLAG(field, position) (ACPI_GET_4BIT_FLAG ((field) >> position))
+
+/* ACPI Pathname helpers */
+
+#define ACPI_IS_ROOT_PREFIX(c) ((c) == (u8) 0x5C) /* Backslash */
+#define ACPI_IS_PARENT_PREFIX(c) ((c) == (u8) 0x5E) /* Carat */
+#define ACPI_IS_PATH_SEPARATOR(c) ((c) == (u8) 0x2E) /* Period (dot) */
+
+/*
+ * An object of type struct acpi_namespace_node can appear in some contexts
+ * where a pointer to an object of type union acpi_operand_object can also
+ * appear. This macro is used to distinguish them.
+ *
+ * The "DescriptorType" field is the second field in both structures.
+ */
+#define ACPI_GET_DESCRIPTOR_PTR(d) (((union acpi_descriptor *)(void *)(d))->common.common_pointer)
+#define ACPI_SET_DESCRIPTOR_PTR(d, p) (((union acpi_descriptor *)(void *)(d))->common.common_pointer = (p))
+#define ACPI_GET_DESCRIPTOR_TYPE(d) (((union acpi_descriptor *)(void *)(d))->common.descriptor_type)
+#define ACPI_SET_DESCRIPTOR_TYPE(d, t) (((union acpi_descriptor *)(void *)(d))->common.descriptor_type = (t))
+
+/*
+ * Macros for the master AML opcode table
+ */
+#if defined (ACPI_DISASSEMBLER) || defined (ACPI_DEBUG_OUTPUT)
+#define ACPI_OP(name, Pargs, Iargs, obj_type, class, type, flags) \
+ {name, (u32)(Pargs), (u32)(Iargs), (u32)(flags), obj_type, class, type}
+#else
+#define ACPI_OP(name, Pargs, Iargs, obj_type, class, type, flags) \
+ {(u32)(Pargs), (u32)(Iargs), (u32)(flags), obj_type, class, type}
+#endif
+
+#define ARG_TYPE_WIDTH 5
+#define ARG_1(x) ((u32)(x))
+#define ARG_2(x) ((u32)(x) << (1 * ARG_TYPE_WIDTH))
+#define ARG_3(x) ((u32)(x) << (2 * ARG_TYPE_WIDTH))
+#define ARG_4(x) ((u32)(x) << (3 * ARG_TYPE_WIDTH))
+#define ARG_5(x) ((u32)(x) << (4 * ARG_TYPE_WIDTH))
+#define ARG_6(x) ((u32)(x) << (5 * ARG_TYPE_WIDTH))
+
+#define ARGI_LIST1(a) (ARG_1(a))
+#define ARGI_LIST2(a, b) (ARG_1(b)|ARG_2(a))
+#define ARGI_LIST3(a, b, c) (ARG_1(c)|ARG_2(b)|ARG_3(a))
+#define ARGI_LIST4(a, b, c, d) (ARG_1(d)|ARG_2(c)|ARG_3(b)|ARG_4(a))
+#define ARGI_LIST5(a, b, c, d, e) (ARG_1(e)|ARG_2(d)|ARG_3(c)|ARG_4(b)|ARG_5(a))
+#define ARGI_LIST6(a, b, c, d, e, f) (ARG_1(f)|ARG_2(e)|ARG_3(d)|ARG_4(c)|ARG_5(b)|ARG_6(a))
+
+#define ARGP_LIST1(a) (ARG_1(a))
+#define ARGP_LIST2(a, b) (ARG_1(a)|ARG_2(b))
+#define ARGP_LIST3(a, b, c) (ARG_1(a)|ARG_2(b)|ARG_3(c))
+#define ARGP_LIST4(a, b, c, d) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d))
+#define ARGP_LIST5(a, b, c, d, e) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)|ARG_5(e))
+#define ARGP_LIST6(a, b, c, d, e, f) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)|ARG_5(e)|ARG_6(f))
+
+#define GET_CURRENT_ARG_TYPE(list) (list & ((u32) 0x1F))
+#define INCREMENT_ARG_LIST(list) (list >>= ((u32) ARG_TYPE_WIDTH))
+
+/*
+ * Ascii error messages can be configured out
+ */
+#ifndef ACPI_NO_ERROR_MESSAGES
+/*
+ * Error reporting. The callers module and line number are inserted by AE_INFO,
+ * the plist contains a set of parens to allow variable-length lists.
+ * These macros are used for both the debug and non-debug versions of the code.
+ */
+#define ACPI_ERROR_NAMESPACE(s, p, e) acpi_ut_prefixed_namespace_error (AE_INFO, s, p, e);
+#define ACPI_ERROR_METHOD(s, n, p, e) acpi_ut_method_error (AE_INFO, s, n, p, e);
+#define ACPI_WARN_PREDEFINED(plist) acpi_ut_predefined_warning plist
+#define ACPI_INFO_PREDEFINED(plist) acpi_ut_predefined_info plist
+#define ACPI_BIOS_ERROR_PREDEFINED(plist) acpi_ut_predefined_bios_error plist
+#define ACPI_ERROR_ONLY(s) s
+
+#else
+
+/* No error messages */
+
+#define ACPI_ERROR_NAMESPACE(s, p, e)
+#define ACPI_ERROR_METHOD(s, n, p, e)
+#define ACPI_WARN_PREDEFINED(plist)
+#define ACPI_INFO_PREDEFINED(plist)
+#define ACPI_BIOS_ERROR_PREDEFINED(plist)
+#define ACPI_ERROR_ONLY(s)
+
+#endif /* ACPI_NO_ERROR_MESSAGES */
+
+#if (!ACPI_REDUCED_HARDWARE)
+#define ACPI_HW_OPTIONAL_FUNCTION(addr) addr
+#else
+#define ACPI_HW_OPTIONAL_FUNCTION(addr) NULL
+#endif
+
+/*
+ * Macros used for ACPICA utilities only
+ */
+
+/* Generate a UUID */
+
+#define ACPI_INIT_UUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \
+ (a) & 0xFF, ((a) >> 8) & 0xFF, ((a) >> 16) & 0xFF, ((a) >> 24) & 0xFF, \
+ (b) & 0xFF, ((b) >> 8) & 0xFF, \
+ (c) & 0xFF, ((c) >> 8) & 0xFF, \
+ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)
+
+#define ACPI_IS_OCTAL_DIGIT(d) (((char)(d) >= '0') && ((char)(d) <= '7'))
+
+/*
+ * Macros used for the ASL-/ASL+ converter utility
+ */
+#ifdef ACPI_ASL_COMPILER
+
+#define ASL_CV_LABEL_FILENODE(a) cv_label_file_node(a);
+#define ASL_CV_CAPTURE_COMMENTS_ONLY(a) cv_capture_comments_only (a);
+#define ASL_CV_CAPTURE_COMMENTS(a) cv_capture_comments (a);
+#define ASL_CV_TRANSFER_COMMENTS(a) cv_transfer_comments (a);
+#define ASL_CV_CLOSE_PAREN(a,b) cv_close_paren_write_comment(a,b);
+#define ASL_CV_CLOSE_BRACE(a,b) cv_close_brace_write_comment(a,b);
+#define ASL_CV_SWITCH_FILES(a,b) cv_switch_files(a,b);
+#define ASL_CV_CLEAR_OP_COMMENTS(a) cv_clear_op_comments(a);
+#define ASL_CV_PRINT_ONE_COMMENT(a,b,c,d) cv_print_one_comment_type (a,b,c,d);
+#define ASL_CV_PRINT_ONE_COMMENT_LIST(a,b) cv_print_one_comment_list (a,b);
+#define ASL_CV_FILE_HAS_SWITCHED(a) cv_file_has_switched(a)
+#define ASL_CV_INIT_FILETREE(a,b) cv_init_file_tree(a,b);
+
+#else
+
+#define ASL_CV_LABEL_FILENODE(a)
+#define ASL_CV_CAPTURE_COMMENTS_ONLY(a)
+#define ASL_CV_CAPTURE_COMMENTS(a)
+#define ASL_CV_TRANSFER_COMMENTS(a)
+#define ASL_CV_CLOSE_PAREN(a,b) acpi_os_printf (")");
+#define ASL_CV_CLOSE_BRACE(a,b) acpi_os_printf ("}");
+#define ASL_CV_SWITCH_FILES(a,b)
+#define ASL_CV_CLEAR_OP_COMMENTS(a)
+#define ASL_CV_PRINT_ONE_COMMENT(a,b,c,d)
+#define ASL_CV_PRINT_ONE_COMMENT_LIST(a,b)
+#define ASL_CV_FILE_HAS_SWITCHED(a) 0
+#define ASL_CV_INIT_FILETREE(a,b)
+
+#endif
+
+#endif /* ACMACROS_H */
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
new file mode 100644
index 000000000..7b27b9cc5
--- /dev/null
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -0,0 +1,402 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acnamesp.h - Namespace subcomponent prototypes and defines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACNAMESP_H__
+#define __ACNAMESP_H__
+
+/* To search the entire name space, pass this as search_base */
+
+#define ACPI_NS_ALL ((acpi_handle)0)
+
+/*
+ * Elements of acpi_ns_properties are bit significant
+ * and should be one-to-one with values of acpi_object_type
+ */
+#define ACPI_NS_NORMAL 0
+#define ACPI_NS_NEWSCOPE 1 /* a definition of this type opens a name scope */
+#define ACPI_NS_LOCAL 2 /* suppress search of enclosing scopes */
+
+/* Flags for acpi_ns_lookup, acpi_ns_search_and_enter */
+
+#define ACPI_NS_NO_UPSEARCH 0
+#define ACPI_NS_SEARCH_PARENT 0x0001
+#define ACPI_NS_DONT_OPEN_SCOPE 0x0002
+#define ACPI_NS_NO_PEER_SEARCH 0x0004
+#define ACPI_NS_ERROR_IF_FOUND 0x0008
+#define ACPI_NS_PREFIX_IS_SCOPE 0x0010
+#define ACPI_NS_EXTERNAL 0x0020
+#define ACPI_NS_TEMPORARY 0x0040
+#define ACPI_NS_OVERRIDE_IF_FOUND 0x0080
+#define ACPI_NS_EARLY_INIT 0x0100
+#define ACPI_NS_PREFIX_MUST_EXIST 0x0200
+
+/* Flags for acpi_ns_walk_namespace */
+
+#define ACPI_NS_WALK_NO_UNLOCK 0
+#define ACPI_NS_WALK_UNLOCK 0x01
+#define ACPI_NS_WALK_TEMP_NODES 0x02
+
+/* Object is not a package element */
+
+#define ACPI_NOT_PACKAGE_ELEMENT ACPI_UINT32_MAX
+#define ACPI_ALL_PACKAGE_ELEMENTS (ACPI_UINT32_MAX-1)
+
+/* Always emit warning message, not dependent on node flags */
+
+#define ACPI_WARN_ALWAYS 0
+
+/*
+ * nsinit - Namespace initialization
+ */
+acpi_status acpi_ns_initialize_objects(void);
+
+acpi_status acpi_ns_initialize_devices(u32 flags);
+
+acpi_status
+acpi_ns_init_one_package(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value);
+
+/*
+ * nsload - Namespace loading
+ */
+acpi_status acpi_ns_load_namespace(void);
+
+acpi_status
+acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node);
+
+/*
+ * nswalk - walk the namespace
+ */
+acpi_status
+acpi_ns_walk_namespace(acpi_object_type type,
+ acpi_handle start_object,
+ u32 max_depth,
+ u32 flags,
+ acpi_walk_callback descending_callback,
+ acpi_walk_callback ascending_callback,
+ void *context, void **return_value);
+
+struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node
+ *parent,
+ struct acpi_namespace_node
+ *child);
+
+struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
+ struct
+ acpi_namespace_node
+ *parent,
+ struct
+ acpi_namespace_node
+ *child);
+
+/*
+ * nsparse - table parsing
+ */
+acpi_status
+acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node);
+
+acpi_status
+acpi_ns_execute_table(u32 table_index, struct acpi_namespace_node *start_node);
+
+acpi_status
+acpi_ns_one_complete_parse(u32 pass_number,
+ u32 table_index,
+ struct acpi_namespace_node *start_node);
+
+/*
+ * nsaccess - Top-level namespace access
+ */
+acpi_status acpi_ns_root_initialize(void);
+
+acpi_status
+acpi_ns_lookup(union acpi_generic_state *scope_info,
+ char *name,
+ acpi_object_type type,
+ acpi_interpreter_mode interpreter_mode,
+ u32 flags,
+ struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node **ret_node);
+
+/*
+ * nsalloc - Named object allocation/deallocation
+ */
+struct acpi_namespace_node *acpi_ns_create_node(u32 name);
+
+void acpi_ns_delete_node(struct acpi_namespace_node *node);
+
+void acpi_ns_remove_node(struct acpi_namespace_node *node);
+
+void
+acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_handle);
+
+void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id);
+
+void acpi_ns_detach_object(struct acpi_namespace_node *node);
+
+void acpi_ns_delete_children(struct acpi_namespace_node *parent);
+
+int acpi_ns_compare_names(char *name1, char *name2);
+
+/*
+ * nsconvert - Dynamic object conversion routines
+ */
+acpi_status
+acpi_ns_convert_to_integer(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
+acpi_status
+acpi_ns_convert_to_string(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
+acpi_status
+acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
+acpi_status
+acpi_ns_convert_to_unicode(struct acpi_namespace_node *scope,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
+acpi_status
+acpi_ns_convert_to_resource(struct acpi_namespace_node *scope,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
+acpi_status
+acpi_ns_convert_to_reference(struct acpi_namespace_node *scope,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
+/*
+ * nsdump - Namespace dump/print utilities
+ */
+void acpi_ns_dump_tables(acpi_handle search_base, u32 max_depth);
+
+void acpi_ns_dump_entry(acpi_handle handle, u32 debug_level);
+
+void
+acpi_ns_dump_pathname(acpi_handle handle,
+ const char *msg, u32 level, u32 component);
+
+void acpi_ns_print_pathname(u32 num_segments, const char *pathname);
+
+acpi_status
+acpi_ns_dump_one_object(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value);
+
+void
+acpi_ns_dump_objects(acpi_object_type type,
+ u8 display_type,
+ u32 max_depth,
+ acpi_owner_id owner_id, acpi_handle start_handle);
+
+void
+acpi_ns_dump_object_paths(acpi_object_type type,
+ u8 display_type,
+ u32 max_depth,
+ acpi_owner_id owner_id, acpi_handle start_handle);
+
+/*
+ * nseval - Namespace evaluation functions
+ */
+acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info);
+
+/*
+ * nsarguments - Argument count/type checking for predefined/reserved names
+ */
+void
+acpi_ns_check_argument_count(char *pathname,
+ struct acpi_namespace_node *node,
+ u32 user_param_count,
+ const union acpi_predefined_info *info);
+
+void
+acpi_ns_check_acpi_compliance(char *pathname,
+ struct acpi_namespace_node *node,
+ const union acpi_predefined_info *predefined);
+
+void acpi_ns_check_argument_types(struct acpi_evaluate_info *info);
+
+/*
+ * nspredef - Return value checking for predefined/reserved names
+ */
+acpi_status
+acpi_ns_check_return_value(struct acpi_namespace_node *node,
+ struct acpi_evaluate_info *info,
+ u32 user_param_count,
+ acpi_status return_status,
+ union acpi_operand_object **return_object);
+
+acpi_status
+acpi_ns_check_object_type(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr,
+ u32 expected_btypes, u32 package_index);
+
+/*
+ * nsprepkg - Validation of predefined name packages
+ */
+acpi_status
+acpi_ns_check_package(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr);
+
+/*
+ * nsnames - Name and Scope manipulation
+ */
+u32 acpi_ns_opens_scope(acpi_object_type type);
+
+char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node);
+
+u32
+acpi_ns_build_normalized_path(struct acpi_namespace_node *node,
+ char *full_path, u32 path_size, u8 no_trailing);
+
+void acpi_ns_normalize_pathname(char *original_path);
+
+char *acpi_ns_get_normalized_pathname(struct acpi_namespace_node *node,
+ u8 no_trailing);
+
+char *acpi_ns_build_prefixed_pathname(union acpi_generic_state *prefix_scope,
+ const char *internal_path);
+
+char *acpi_ns_name_of_current_scope(struct acpi_walk_state *walk_state);
+
+acpi_status
+acpi_ns_handle_to_name(acpi_handle target_handle, struct acpi_buffer *buffer);
+
+acpi_status
+acpi_ns_handle_to_pathname(acpi_handle target_handle,
+ struct acpi_buffer *buffer, u8 no_trailing);
+
+u8
+acpi_ns_pattern_match(struct acpi_namespace_node *obj_node, char *search_for);
+
+acpi_status
+acpi_ns_get_node_unlocked(struct acpi_namespace_node *prefix_node,
+ const char *external_pathname,
+ u32 flags, struct acpi_namespace_node **out_node);
+
+acpi_status
+acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
+ const char *external_pathname,
+ u32 flags, struct acpi_namespace_node **out_node);
+
+acpi_size acpi_ns_get_pathname_length(struct acpi_namespace_node *node);
+
+/*
+ * nsobject - Object management for namespace nodes
+ */
+acpi_status
+acpi_ns_attach_object(struct acpi_namespace_node *node,
+ union acpi_operand_object *object, acpi_object_type type);
+
+union acpi_operand_object *acpi_ns_get_attached_object(struct
+ acpi_namespace_node
+ *node);
+
+union acpi_operand_object *acpi_ns_get_secondary_object(union
+ acpi_operand_object
+ *obj_desc);
+
+acpi_status
+acpi_ns_attach_data(struct acpi_namespace_node *node,
+ acpi_object_handler handler, void *data);
+
+acpi_status
+acpi_ns_detach_data(struct acpi_namespace_node *node,
+ acpi_object_handler handler);
+
+acpi_status
+acpi_ns_get_attached_data(struct acpi_namespace_node *node,
+ acpi_object_handler handler, void **data);
+
+/*
+ * nsrepair - General return object repair for all
+ * predefined methods/objects
+ */
+acpi_status
+acpi_ns_simple_repair(struct acpi_evaluate_info *info,
+ u32 expected_btypes,
+ u32 package_index,
+ union acpi_operand_object **return_object_ptr);
+
+acpi_status
+acpi_ns_wrap_with_package(struct acpi_evaluate_info *info,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **obj_desc_ptr);
+
+acpi_status
+acpi_ns_repair_null_element(struct acpi_evaluate_info *info,
+ u32 expected_btypes,
+ u32 package_index,
+ union acpi_operand_object **return_object_ptr);
+
+void
+acpi_ns_remove_null_elements(struct acpi_evaluate_info *info,
+ u8 package_type,
+ union acpi_operand_object *obj_desc);
+
+/*
+ * nsrepair2 - Return object repair for specific
+ * predefined methods/objects
+ */
+acpi_status
+acpi_ns_complex_repairs(struct acpi_evaluate_info *info,
+ struct acpi_namespace_node *node,
+ acpi_status validate_status,
+ union acpi_operand_object **return_object_ptr);
+
+/*
+ * nssearch - Namespace searching and entry
+ */
+acpi_status
+acpi_ns_search_and_enter(u32 entry_name,
+ struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node *node,
+ acpi_interpreter_mode interpreter_mode,
+ acpi_object_type type,
+ u32 flags, struct acpi_namespace_node **ret_node);
+
+acpi_status
+acpi_ns_search_one_scope(u32 entry_name,
+ struct acpi_namespace_node *node,
+ acpi_object_type type,
+ struct acpi_namespace_node **ret_node);
+
+void
+acpi_ns_install_node(struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node *parent_node,
+ struct acpi_namespace_node *node, acpi_object_type type);
+
+/*
+ * nsutils - Utility functions
+ */
+acpi_object_type acpi_ns_get_type(struct acpi_namespace_node *node);
+
+u32 acpi_ns_local(acpi_object_type type);
+
+void
+acpi_ns_print_node_pathname(struct acpi_namespace_node *node, const char *msg);
+
+acpi_status acpi_ns_build_internal_name(struct acpi_namestring_info *info);
+
+void acpi_ns_get_internal_name_length(struct acpi_namestring_info *info);
+
+acpi_status
+acpi_ns_internalize_name(const char *dotted_name, char **converted_name);
+
+acpi_status
+acpi_ns_externalize_name(u32 internal_name_length,
+ const char *internal_name,
+ u32 * converted_name_length, char **converted_name);
+
+struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle);
+
+void acpi_ns_terminate(void);
+
+#endif /* __ACNAMESP_H__ */
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
new file mode 100644
index 000000000..6af5dc995
--- /dev/null
+++ b/drivers/acpi/acpica/acobject.h
@@ -0,0 +1,443 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acobject.h - Definition of union acpi_operand_object (Internal object only)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef _ACOBJECT_H
+#define _ACOBJECT_H
+
+/* acpisrc:struct_defs -- for acpisrc conversion */
+
+/*
+ * The union acpi_operand_object is used to pass AML operands from the dispatcher
+ * to the interpreter, and to keep track of the various handlers such as
+ * address space handlers and notify handlers. The object is a constant
+ * size in order to allow it to be cached and reused.
+ *
+ * Note: The object is optimized to be aligned and will not work if it is
+ * byte-packed.
+ */
+#if ACPI_MACHINE_WIDTH == 64
+#pragma pack(8)
+#else
+#pragma pack(4)
+#endif
+
+/*******************************************************************************
+ *
+ * Common Descriptors
+ *
+ ******************************************************************************/
+
+/*
+ * Common area for all objects.
+ *
+ * descriptor_type is used to differentiate between internal descriptors, and
+ * must be in the same place across all descriptors
+ *
+ * Note: The descriptor_type and Type fields must appear in the identical
+ * position in both the struct acpi_namespace_node and union acpi_operand_object
+ * structures.
+ */
+#define ACPI_OBJECT_COMMON_HEADER \
+ union acpi_operand_object *next_object; /* Objects linked to parent NS node */\
+ u8 descriptor_type; /* To differentiate various internal objs */\
+ u8 type; /* acpi_object_type */\
+ u16 reference_count; /* For object deletion management */\
+ u8 flags;
+ /*
+ * Note: There are 3 bytes available here before the
+ * next natural alignment boundary (for both 32/64 cases)
+ */
+
+/* Values for Flag byte above */
+
+#define AOPOBJ_AML_CONSTANT 0x01 /* Integer is an AML constant */
+#define AOPOBJ_STATIC_POINTER 0x02 /* Data is part of an ACPI table, don't delete */
+#define AOPOBJ_DATA_VALID 0x04 /* Object is initialized and data is valid */
+#define AOPOBJ_OBJECT_INITIALIZED 0x08 /* Region is initialized */
+#define AOPOBJ_REG_CONNECTED 0x10 /* _REG was run */
+#define AOPOBJ_SETUP_COMPLETE 0x20 /* Region setup is complete */
+#define AOPOBJ_INVALID 0x40 /* Host OS won't allow a Region address */
+
+/******************************************************************************
+ *
+ * Basic data types
+ *
+ *****************************************************************************/
+
+struct acpi_object_common {
+ACPI_OBJECT_COMMON_HEADER};
+
+struct acpi_object_integer {
+ ACPI_OBJECT_COMMON_HEADER u8 fill[3]; /* Prevent warning on some compilers */
+ u64 value;
+};
+
+/*
+ * Note: The String and Buffer object must be identical through the
+ * pointer and length elements. There is code that depends on this.
+ *
+ * Fields common to both Strings and Buffers
+ */
+#define ACPI_COMMON_BUFFER_INFO(_type) \
+ _type *pointer; \
+ 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 */
+};
+
+struct acpi_object_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 */
+ union acpi_operand_object **elements; /* Array of pointers to acpi_objects */
+ u8 *aml_start;
+ u32 aml_length;
+ u32 count; /* # of elements in package */
+};
+
+/******************************************************************************
+ *
+ * Complex data types
+ *
+ *****************************************************************************/
+
+struct acpi_object_event {
+ 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 */
+ 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 */
+ struct acpi_thread_state *owner_thread; /* Current owner of the mutex */
+ union acpi_operand_object *prev; /* Link for list of acquired mutexes */
+ union acpi_operand_object *next; /* Link for list of acquired mutexes */
+ struct acpi_namespace_node *node; /* Containing namespace node */
+ u8 original_sync_level; /* Owner's original sync level (0-15) */
+};
+
+struct acpi_object_region {
+ 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;
+ acpi_physical_address address;
+ u32 length;
+ void *pointer; /* Only for data table regions */
+};
+
+struct acpi_object_method {
+ ACPI_OBJECT_COMMON_HEADER u8 info_flags;
+ u8 param_count;
+ u8 sync_level;
+ union acpi_operand_object *mutex;
+ union acpi_operand_object *node;
+ u8 *aml_start;
+ union {
+ acpi_internal_method implementation;
+ union acpi_operand_object *handler;
+ } dispatch;
+
+ u32 aml_length;
+ acpi_owner_id owner_id;
+ u8 thread_count;
+};
+
+/* Flags for info_flags field above */
+
+#define ACPI_METHOD_MODULE_LEVEL 0x01 /* Method is actually module-level code */
+#define ACPI_METHOD_INTERNAL_ONLY 0x02 /* Method is implemented internally (_OSI) */
+#define ACPI_METHOD_SERIALIZED 0x04 /* Method is serialized */
+#define ACPI_METHOD_SERIALIZED_PENDING 0x08 /* Method is to be marked serialized */
+#define ACPI_METHOD_IGNORE_SYNC_LEVEL 0x10 /* Method was auto-serialized at table load time */
+#define ACPI_METHOD_MODIFIED_NAMESPACE 0x20 /* Method modified the namespace */
+
+/******************************************************************************
+ *
+ * Objects that can be notified. All share a common notify_info area.
+ *
+ *****************************************************************************/
+
+/*
+ * Common fields for objects that support ASL notifications
+ */
+#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 */
+
+/* COMMON NOTIFY for POWER, PROCESSOR, DEVICE, and THERMAL */
+
+struct acpi_object_notify_common {
+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;
+};
+
+struct acpi_object_power_resource {
+ 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 */
+ u8 proc_id;
+ u8 length;
+ ACPI_COMMON_NOTIFY_INFO acpi_io_address address;
+};
+
+struct acpi_object_thermal_zone {
+ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO};
+
+/******************************************************************************
+ *
+ * Fields. All share a common header/info field.
+ *
+ *****************************************************************************/
+
+/*
+ * Common bitfield for the field objects
+ * "Field Datum" -- a datum from the actual field object
+ * "Buffer Datum" -- a datum from a user buffer, read from or to be written to the field
+ */
+#define ACPI_COMMON_FIELD_INFO \
+ u8 field_flags; /* Access, update, and lock bits */\
+ u8 attribute; /* From access_as keyword */\
+ u8 access_byte_width; /* Read/Write size in bytes */\
+ struct acpi_namespace_node *node; /* Link back to parent node */\
+ u32 bit_length; /* Length of field in bits */\
+ 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 */
+
+
+/* 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) */
+};
+
+struct acpi_object_region_field {
+ 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 */
+ u8 *internal_pcc_buffer; /* Internal buffer for fields associated with PCC */
+};
+
+struct acpi_object_bank_field {
+ 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.
+ */
+ union acpi_operand_object *index_obj; /* Index register */
+ union acpi_operand_object *data_obj; /* Data register */
+};
+
+/* 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() */
+ union acpi_operand_object *buffer_obj; /* Containing Buffer object */
+};
+
+/******************************************************************************
+ *
+ * Objects for handlers
+ *
+ *****************************************************************************/
+
+struct acpi_object_notify_handler {
+ 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;
+ union acpi_operand_object *next[2]; /* Device and System handler lists */
+};
+
+struct acpi_object_addr_handler {
+ ACPI_OBJECT_COMMON_HEADER u8 space_id;
+ u8 handler_flags;
+ acpi_adr_space_handler handler;
+ struct acpi_namespace_node *node; /* Parent device */
+ void *context;
+ acpi_mutex context_mutex;
+ acpi_adr_space_setup setup;
+ union acpi_operand_object *region_list; /* Regions using this handler */
+ union acpi_operand_object *next;
+};
+
+/* Flags for address handler (handler_flags) */
+
+#define ACPI_ADDR_HANDLER_DEFAULT_INSTALLED 0x01
+
+/******************************************************************************
+ *
+ * Special internal objects
+ *
+ *****************************************************************************/
+
+/*
+ * The Reference object is used for these opcodes:
+ * Arg[0-6], Local[0-7], index_op, name_op, ref_of_op, load_op, load_table_op, debug_op
+ * The Reference.Class differentiates these types.
+ */
+struct acpi_object_reference {
+ 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 */
+ struct acpi_namespace_node *node; /* ref_of or Namepath */
+ union acpi_operand_object **where; /* Target of Index */
+ u8 *index_pointer; /* Used for Buffers and Strings */
+ u8 *aml; /* Used for deferred resolution of the ref */
+ u32 value; /* Used for Local/Arg/Index/ddb_handle */
+};
+
+/* Values for Reference.Class above */
+
+typedef enum {
+ ACPI_REFCLASS_LOCAL = 0, /* Method local */
+ ACPI_REFCLASS_ARG = 1, /* Method argument */
+ ACPI_REFCLASS_REFOF = 2, /* Result of ref_of() TBD: Split to Ref/Node and Ref/operand_obj? */
+ ACPI_REFCLASS_INDEX = 3, /* Result of Index() */
+ ACPI_REFCLASS_TABLE = 4, /* ddb_handle - Load(), load_table() */
+ ACPI_REFCLASS_NAME = 5, /* Reference to a named object */
+ ACPI_REFCLASS_DEBUG = 6, /* Debug object */
+
+ ACPI_REFCLASS_MAX = 6
+} ACPI_REFERENCE_CLASSES;
+
+/*
+ * Extra object is used as additional storage for types that
+ * have AML code in their declarations (term_args) that must be
+ * evaluated at run time.
+ *
+ * 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) */
+ struct acpi_namespace_node *scope_node;
+ void *region_context; /* Region-specific data */
+ u8 *aml_start;
+ u32 aml_length;
+};
+
+/* Additional data that can be attached to namespace nodes */
+
+struct acpi_object_data {
+ 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 */
+};
+
+/******************************************************************************
+ *
+ * union acpi_operand_object descriptor - a giant union of all of the above
+ *
+ *****************************************************************************/
+
+union acpi_operand_object {
+ struct acpi_object_common common;
+ struct acpi_object_integer integer;
+ struct acpi_object_string string;
+ struct acpi_object_buffer buffer;
+ struct acpi_object_package package;
+ struct acpi_object_event event;
+ struct acpi_object_method method;
+ struct acpi_object_mutex mutex;
+ struct acpi_object_region region;
+ struct acpi_object_notify_common common_notify;
+ struct acpi_object_device device;
+ struct acpi_object_power_resource power_resource;
+ struct acpi_object_processor processor;
+ struct acpi_object_thermal_zone thermal_zone;
+ struct acpi_object_field_common common_field;
+ struct acpi_object_region_field field;
+ struct acpi_object_buffer_field buffer_field;
+ struct acpi_object_bank_field bank_field;
+ struct acpi_object_index_field index_field;
+ struct acpi_object_notify_handler notify;
+ struct acpi_object_addr_handler address_space;
+ struct acpi_object_reference reference;
+ struct acpi_object_extra extra;
+ struct acpi_object_data data;
+ struct acpi_object_cache_list cache;
+
+ /*
+ * Add namespace node to union in order to simplify code that accepts both
+ * ACPI_OPERAND_OBJECTs and ACPI_NAMESPACE_NODEs. The structures share
+ * a common descriptor_type field in order to differentiate them.
+ */
+ struct acpi_namespace_node node;
+};
+
+/******************************************************************************
+ *
+ * union acpi_descriptor - objects that share a common descriptor identifier
+ *
+ *****************************************************************************/
+
+/* Object descriptor types */
+
+#define ACPI_DESC_TYPE_CACHED 0x01 /* Used only when object is cached */
+#define ACPI_DESC_TYPE_STATE 0x02
+#define ACPI_DESC_TYPE_STATE_UPDATE 0x03
+#define ACPI_DESC_TYPE_STATE_PACKAGE 0x04
+#define ACPI_DESC_TYPE_STATE_CONTROL 0x05
+#define ACPI_DESC_TYPE_STATE_RPSCOPE 0x06
+#define ACPI_DESC_TYPE_STATE_PSCOPE 0x07
+#define ACPI_DESC_TYPE_STATE_WSCOPE 0x08
+#define ACPI_DESC_TYPE_STATE_RESULT 0x09
+#define ACPI_DESC_TYPE_STATE_NOTIFY 0x0A
+#define ACPI_DESC_TYPE_STATE_THREAD 0x0B
+#define ACPI_DESC_TYPE_WALK 0x0C
+#define ACPI_DESC_TYPE_PARSER 0x0D
+#define ACPI_DESC_TYPE_OPERAND 0x0E
+#define ACPI_DESC_TYPE_NAMED 0x0F
+#define ACPI_DESC_TYPE_MAX 0x0F
+
+struct acpi_common_descriptor {
+ void *common_pointer;
+ u8 descriptor_type; /* To differentiate various internal objs */
+};
+
+union acpi_descriptor {
+ struct acpi_common_descriptor common;
+ union acpi_operand_object object;
+ struct acpi_namespace_node node;
+ union acpi_parse_object op;
+};
+
+#pragma pack()
+
+#endif /* _ACOBJECT_H */
diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h
new file mode 100644
index 000000000..a224926bd
--- /dev/null
+++ b/drivers/acpi/acpica/acopcode.h
@@ -0,0 +1,297 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acopcode.h - AML opcode information for the AML parser and interpreter
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACOPCODE_H__
+#define __ACOPCODE_H__
+
+#define MAX_EXTENDED_OPCODE 0x88
+#define NUM_EXTENDED_OPCODE (MAX_EXTENDED_OPCODE + 1)
+#define MAX_INTERNAL_OPCODE
+#define NUM_INTERNAL_OPCODE (MAX_INTERNAL_OPCODE + 1)
+
+/* Used for non-assigned opcodes */
+
+#define _UNK 0x6B
+
+/*
+ * Reserved ASCII characters. Do not use any of these for
+ * internal opcodes, since they are used to differentiate
+ * name strings from AML opcodes
+ */
+#define _ASC 0x6C
+#define _NAM 0x6C
+#define _PFX 0x6D
+
+/*
+ * All AML opcodes and the parse-time arguments for each. Used by the AML
+ * parser Each list is compressed into a 32-bit number and stored in the
+ * master opcode table (in psopcode.c).
+ */
+#define ARGP_ACCESSFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING)
+#define ARGP_ACQUIRE_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_WORDDATA)
+#define ARGP_ADD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_ALIAS_OP ARGP_LIST2 (ARGP_NAMESTRING, ARGP_NAME)
+#define ARGP_ARG0 ARG_NONE
+#define ARGP_ARG1 ARG_NONE
+#define ARGP_ARG2 ARG_NONE
+#define ARGP_ARG3 ARG_NONE
+#define ARGP_ARG4 ARG_NONE
+#define ARGP_ARG5 ARG_NONE
+#define ARGP_ARG6 ARG_NONE
+#define ARGP_BANK_FIELD_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_NAMESTRING,ARGP_TERMARG, ARGP_BYTEDATA, ARGP_FIELDLIST)
+#define ARGP_BIT_AND_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_BIT_NAND_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_BIT_NOR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_BIT_NOT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_BIT_OR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_BIT_XOR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_BREAK_OP ARG_NONE
+#define ARGP_BREAK_POINT_OP ARG_NONE
+#define ARGP_BUFFER_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_BYTELIST)
+#define ARGP_BYTE_OP ARGP_LIST1 (ARGP_BYTEDATA)
+#define ARGP_BYTELIST_OP ARGP_LIST1 (ARGP_NAMESTRING)
+#define ARGP_COMMENT_OP ARGP_LIST2 (ARGP_BYTEDATA, ARGP_COMMENT)
+#define ARGP_CONCAT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_CONCAT_RES_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_SIMPLENAME, ARGP_TARGET)
+#define ARGP_CONNECTFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING)
+#define ARGP_CONTINUE_OP ARG_NONE
+#define ARGP_COPY_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SIMPLENAME)
+#define ARGP_CREATE_BIT_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME)
+#define ARGP_CREATE_BYTE_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME)
+#define ARGP_CREATE_DWORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME)
+#define ARGP_CREATE_FIELD_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME)
+#define ARGP_CREATE_QWORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME)
+#define ARGP_CREATE_WORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME)
+#define ARGP_DATA_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_DEBUG_OP ARG_NONE
+#define ARGP_DECREMENT_OP ARGP_LIST1 (ARGP_SUPERNAME)
+#define ARGP_DEREF_OF_OP ARGP_LIST1 (ARGP_SUPERNAME)
+#define ARGP_DEVICE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_OBJLIST)
+#define ARGP_DIVIDE_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET, ARGP_TARGET)
+#define ARGP_DWORD_OP ARGP_LIST1 (ARGP_DWORDDATA)
+#define ARGP_ELSE_OP ARGP_LIST2 (ARGP_PKGLENGTH, ARGP_TERMLIST)
+#define ARGP_EVENT_OP ARGP_LIST1 (ARGP_NAME)
+#define ARGP_EXTERNAL_OP ARGP_LIST3 (ARGP_NAME, ARGP_BYTEDATA, ARGP_BYTEDATA)
+#define ARGP_FATAL_OP ARGP_LIST3 (ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_TERMARG)
+#define ARGP_FIELD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_BYTEDATA, ARGP_FIELDLIST)
+#define ARGP_FIND_SET_LEFT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_FIND_SET_RIGHT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_FROM_BCD_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_IF_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_TERMLIST)
+#define ARGP_INCREMENT_OP ARGP_LIST1 (ARGP_SUPERNAME)
+#define ARGP_INDEX_FIELD_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_NAMESTRING,ARGP_BYTEDATA, ARGP_FIELDLIST)
+#define ARGP_INDEX_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_LAND_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_LEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_LGREATER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_LGREATEREQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_LLESS_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_LLESSEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_LNOT_OP ARGP_LIST1 (ARGP_TERMARG)
+#define ARGP_LNOTEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_LOAD_OP ARGP_LIST2 (ARGP_NAMESTRING, ARGP_SUPERNAME)
+#define ARGP_LOAD_TABLE_OP ARGP_LIST6 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_LOCAL0 ARG_NONE
+#define ARGP_LOCAL1 ARG_NONE
+#define ARGP_LOCAL2 ARG_NONE
+#define ARGP_LOCAL3 ARG_NONE
+#define ARGP_LOCAL4 ARG_NONE
+#define ARGP_LOCAL5 ARG_NONE
+#define ARGP_LOCAL6 ARG_NONE
+#define ARGP_LOCAL7 ARG_NONE
+#define ARGP_LOR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_MATCH_OP ARGP_LIST6 (ARGP_TERMARG, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_METHOD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMLIST)
+#define ARGP_METHODCALL_OP ARGP_LIST1 (ARGP_NAMESTRING)
+#define ARGP_MID_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_MOD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_MULTIPLY_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_MUTEX_OP ARGP_LIST2 (ARGP_NAME, ARGP_BYTEDATA)
+#define ARGP_NAME_OP ARGP_LIST2 (ARGP_NAME, ARGP_DATAOBJ)
+#define ARGP_NAMEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING)
+#define ARGP_NAMEPATH_OP ARGP_LIST1 (ARGP_NAMESTRING)
+#define ARGP_NOOP_OP ARG_NONE
+#define ARGP_NOTIFY_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG)
+#define ARGP_OBJECT_TYPE_OP ARGP_LIST1 (ARGP_SIMPLENAME)
+#define ARGP_ONE_OP ARG_NONE
+#define ARGP_ONES_OP ARG_NONE
+#define ARGP_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_BYTEDATA, ARGP_DATAOBJLIST)
+#define ARGP_POWER_RES_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_WORDDATA, ARGP_OBJLIST)
+#define ARGP_PROCESSOR_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_BYTEDATA, ARGP_OBJLIST)
+#define ARGP_QWORD_OP ARGP_LIST1 (ARGP_QWORDDATA)
+#define ARGP_REF_OF_OP ARGP_LIST1 (ARGP_SIMPLENAME)
+#define ARGP_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG)
+#define ARGP_RELEASE_OP ARGP_LIST1 (ARGP_SUPERNAME)
+#define ARGP_RESERVEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING)
+#define ARGP_RESET_OP ARGP_LIST1 (ARGP_SUPERNAME)
+#define ARGP_RETURN_OP ARGP_LIST1 (ARGP_TERMARG)
+#define ARGP_REVISION_OP ARG_NONE
+#define ARGP_SCOPE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_TERMLIST)
+#define ARGP_SERIALFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING)
+#define ARGP_SHIFT_LEFT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_SHIFT_RIGHT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_SIGNAL_OP ARGP_LIST1 (ARGP_SUPERNAME)
+#define ARGP_SIZE_OF_OP ARGP_LIST1 (ARGP_SUPERNAME)
+#define ARGP_SLEEP_OP ARGP_LIST1 (ARGP_TERMARG)
+#define ARGP_STALL_OP ARGP_LIST1 (ARGP_TERMARG)
+#define ARGP_STATICSTRING_OP ARGP_LIST1 (ARGP_NAMESTRING)
+#define ARGP_STORE_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SUPERNAME)
+#define ARGP_STRING_OP ARGP_LIST1 (ARGP_CHARLIST)
+#define ARGP_SUBTRACT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_THERMAL_ZONE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_OBJLIST)
+#define ARGP_TIMER_OP ARG_NONE
+#define ARGP_TO_BCD_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_TO_BUFFER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_TO_DEC_STR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_TO_HEX_STR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_TO_INTEGER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_TO_STRING_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
+#define ARGP_UNLOAD_OP ARGP_LIST1 (ARGP_SUPERNAME)
+#define ARGP_VAR_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_DATAOBJLIST)
+#define ARGP_WAIT_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG)
+#define ARGP_WHILE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_TERMLIST)
+#define ARGP_WORD_OP ARGP_LIST1 (ARGP_WORDDATA)
+#define ARGP_ZERO_OP ARG_NONE
+
+/*
+ * All AML opcodes and the runtime arguments for each. Used by the AML
+ * interpreter Each list is compressed into a 32-bit number and stored
+ * in the master opcode table (in psopcode.c).
+ *
+ * (Used by prep_operands procedure and the ASL Compiler)
+ */
+#define ARGI_ACCESSFIELD_OP ARGI_INVALID_OPCODE
+#define ARGI_ACQUIRE_OP ARGI_LIST2 (ARGI_MUTEX, ARGI_INTEGER)
+#define ARGI_ADD_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_ALIAS_OP ARGI_INVALID_OPCODE
+#define ARGI_ARG0 ARG_NONE
+#define ARGI_ARG1 ARG_NONE
+#define ARGI_ARG2 ARG_NONE
+#define ARGI_ARG3 ARG_NONE
+#define ARGI_ARG4 ARG_NONE
+#define ARGI_ARG5 ARG_NONE
+#define ARGI_ARG6 ARG_NONE
+#define ARGI_BANK_FIELD_OP ARGI_LIST1 (ARGI_INTEGER)
+#define ARGI_BIT_AND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_BIT_NAND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_BIT_NOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_BIT_NOT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_BIT_OR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_BIT_XOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_BREAK_OP ARG_NONE
+#define ARGI_BREAK_POINT_OP ARG_NONE
+#define ARGI_BUFFER_OP ARGI_LIST1 (ARGI_INTEGER)
+#define ARGI_BYTE_OP ARGI_INVALID_OPCODE
+#define ARGI_BYTELIST_OP ARGI_INVALID_OPCODE
+#define ARGI_COMMENT_OP ARGI_INVALID_OPCODE
+#define ARGI_CONCAT_OP ARGI_LIST3 (ARGI_ANYTYPE, ARGI_ANYTYPE, ARGI_TARGETREF)
+#define ARGI_CONCAT_RES_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_BUFFER, ARGI_TARGETREF)
+#define ARGI_COND_REF_OF_OP ARGI_LIST2 (ARGI_OBJECT_REF, ARGI_TARGETREF)
+#define ARGI_CONNECTFIELD_OP ARGI_INVALID_OPCODE
+#define ARGI_CONTINUE_OP ARGI_INVALID_OPCODE
+#define ARGI_COPY_OP ARGI_LIST2 (ARGI_ANYTYPE, ARGI_SIMPLE_TARGET)
+#define ARGI_CREATE_BIT_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE)
+#define ARGI_CREATE_BYTE_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE)
+#define ARGI_CREATE_DWORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE)
+#define ARGI_CREATE_FIELD_OP ARGI_LIST4 (ARGI_BUFFER, ARGI_INTEGER, ARGI_INTEGER, ARGI_REFERENCE)
+#define ARGI_CREATE_QWORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE)
+#define ARGI_CREATE_WORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE)
+#define ARGI_DATA_REGION_OP ARGI_LIST3 (ARGI_STRING, ARGI_STRING, ARGI_STRING)
+#define ARGI_DEBUG_OP ARG_NONE
+#define ARGI_DECREMENT_OP ARGI_LIST1 (ARGI_TARGETREF)
+#define ARGI_DEREF_OF_OP ARGI_LIST1 (ARGI_REF_OR_STRING)
+#define ARGI_DEVICE_OP ARGI_INVALID_OPCODE
+#define ARGI_DIVIDE_OP ARGI_LIST4 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF, ARGI_TARGETREF)
+#define ARGI_DWORD_OP ARGI_INVALID_OPCODE
+#define ARGI_ELSE_OP ARGI_INVALID_OPCODE
+#define ARGI_EVENT_OP ARGI_INVALID_OPCODE
+#define ARGI_EXTERNAL_OP ARGI_LIST3 (ARGI_STRING, ARGI_INTEGER, ARGI_INTEGER)
+#define ARGI_FATAL_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_INTEGER)
+#define ARGI_FIELD_OP ARGI_INVALID_OPCODE
+#define ARGI_FIND_SET_LEFT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_FIND_SET_RIGHT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_FROM_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_FIXED_TARGET)
+#define ARGI_IF_OP ARGI_INVALID_OPCODE
+#define ARGI_INCREMENT_OP ARGI_LIST1 (ARGI_TARGETREF)
+#define ARGI_INDEX_FIELD_OP ARGI_INVALID_OPCODE
+#define ARGI_INDEX_OP ARGI_LIST3 (ARGI_COMPLEXOBJ, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_LAND_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER)
+#define ARGI_LEQUAL_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA)
+#define ARGI_LGREATER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA)
+#define ARGI_LGREATEREQUAL_OP ARGI_INVALID_OPCODE
+#define ARGI_LLESS_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA)
+#define ARGI_LLESSEQUAL_OP ARGI_INVALID_OPCODE
+#define ARGI_LNOT_OP ARGI_LIST1 (ARGI_INTEGER)
+#define ARGI_LNOTEQUAL_OP ARGI_INVALID_OPCODE
+#define ARGI_LOAD_OP ARGI_LIST2 (ARGI_REGION_OR_BUFFER,ARGI_TARGETREF)
+#define ARGI_LOAD_TABLE_OP ARGI_LIST6 (ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_ANYTYPE)
+#define ARGI_LOCAL0 ARG_NONE
+#define ARGI_LOCAL1 ARG_NONE
+#define ARGI_LOCAL2 ARG_NONE
+#define ARGI_LOCAL3 ARG_NONE
+#define ARGI_LOCAL4 ARG_NONE
+#define ARGI_LOCAL5 ARG_NONE
+#define ARGI_LOCAL6 ARG_NONE
+#define ARGI_LOCAL7 ARG_NONE
+#define ARGI_LOR_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER)
+#define ARGI_MATCH_OP ARGI_LIST6 (ARGI_PACKAGE, ARGI_INTEGER, ARGI_COMPUTEDATA, ARGI_INTEGER,ARGI_COMPUTEDATA,ARGI_INTEGER)
+#define ARGI_METHOD_OP ARGI_INVALID_OPCODE
+#define ARGI_METHODCALL_OP ARGI_INVALID_OPCODE
+#define ARGI_MID_OP ARGI_LIST4 (ARGI_BUFFER_OR_STRING,ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_MOD_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_MULTIPLY_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_MUTEX_OP ARGI_INVALID_OPCODE
+#define ARGI_NAME_OP ARGI_INVALID_OPCODE
+#define ARGI_NAMEDFIELD_OP ARGI_INVALID_OPCODE
+#define ARGI_NAMEPATH_OP ARGI_INVALID_OPCODE
+#define ARGI_NOOP_OP ARG_NONE
+#define ARGI_NOTIFY_OP ARGI_LIST2 (ARGI_DEVICE_REF, ARGI_INTEGER)
+#define ARGI_OBJECT_TYPE_OP ARGI_LIST1 (ARGI_ANYTYPE)
+#define ARGI_ONE_OP ARG_NONE
+#define ARGI_ONES_OP ARG_NONE
+#define ARGI_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER)
+#define ARGI_POWER_RES_OP ARGI_INVALID_OPCODE
+#define ARGI_PROCESSOR_OP ARGI_INVALID_OPCODE
+#define ARGI_QWORD_OP ARGI_INVALID_OPCODE
+#define ARGI_REF_OF_OP ARGI_LIST1 (ARGI_OBJECT_REF)
+#define ARGI_REGION_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER)
+#define ARGI_RELEASE_OP ARGI_LIST1 (ARGI_MUTEX)
+#define ARGI_RESERVEDFIELD_OP ARGI_INVALID_OPCODE
+#define ARGI_RESET_OP ARGI_LIST1 (ARGI_EVENT)
+#define ARGI_RETURN_OP ARGI_INVALID_OPCODE
+#define ARGI_REVISION_OP ARG_NONE
+#define ARGI_SCOPE_OP ARGI_INVALID_OPCODE
+#define ARGI_SERIALFIELD_OP ARGI_INVALID_OPCODE
+#define ARGI_SHIFT_LEFT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_SHIFT_RIGHT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_SIGNAL_OP ARGI_LIST1 (ARGI_EVENT)
+#define ARGI_SIZE_OF_OP ARGI_LIST1 (ARGI_DATAOBJECT)
+#define ARGI_SLEEP_OP ARGI_LIST1 (ARGI_INTEGER)
+#define ARGI_STALL_OP ARGI_LIST1 (ARGI_INTEGER)
+#define ARGI_STATICSTRING_OP ARGI_INVALID_OPCODE
+#define ARGI_STORE_OP ARGI_LIST2 (ARGI_DATAREFOBJ, ARGI_STORE_TARGET)
+#define ARGI_STRING_OP ARGI_INVALID_OPCODE
+#define ARGI_SUBTRACT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_THERMAL_ZONE_OP ARGI_INVALID_OPCODE
+#define ARGI_TIMER_OP ARG_NONE
+#define ARGI_TO_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_FIXED_TARGET)
+#define ARGI_TO_BUFFER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
+#define ARGI_TO_DEC_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
+#define ARGI_TO_HEX_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
+#define ARGI_TO_INTEGER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
+#define ARGI_TO_STRING_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_FIXED_TARGET)
+#define ARGI_UNLOAD_OP ARGI_LIST1 (ARGI_DDBHANDLE)
+#define ARGI_VAR_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER)
+#define ARGI_WAIT_OP ARGI_LIST2 (ARGI_EVENT, ARGI_INTEGER)
+#define ARGI_WHILE_OP ARGI_INVALID_OPCODE
+#define ARGI_WORD_OP ARGI_INVALID_OPCODE
+#define ARGI_ZERO_OP ARG_NONE
+
+#endif /* __ACOPCODE_H__ */
diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h
new file mode 100644
index 000000000..4511c2bd8
--- /dev/null
+++ b/drivers/acpi/acpica/acparser.h
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Module Name: acparser.h - AML Parser subcomponent prototypes and defines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACPARSER_H__
+#define __ACPARSER_H__
+
+#define OP_HAS_RETURN_VALUE 1
+
+/* Variable number of arguments. This field must be 32 bits */
+
+#define ACPI_VAR_ARGS ACPI_UINT32_MAX
+
+#define ACPI_PARSE_DELETE_TREE 0x0001
+#define ACPI_PARSE_NO_TREE_DELETE 0x0000
+#define ACPI_PARSE_TREE_MASK 0x0001
+
+#define ACPI_PARSE_LOAD_PASS1 0x0010
+#define ACPI_PARSE_LOAD_PASS2 0x0020
+#define ACPI_PARSE_EXECUTE 0x0030
+#define ACPI_PARSE_MODE_MASK 0x0030
+
+#define ACPI_PARSE_DEFERRED_OP 0x0100
+#define ACPI_PARSE_DISASSEMBLE 0x0200
+
+#define ACPI_PARSE_MODULE_LEVEL 0x0400
+
+/******************************************************************************
+ *
+ * Parser interfaces
+ *
+ *****************************************************************************/
+
+extern const u8 acpi_gbl_short_op_index[];
+extern const u8 acpi_gbl_long_op_index[];
+
+/*
+ * psxface - Parser external interfaces
+ */
+acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info);
+
+acpi_status acpi_ps_execute_table(struct acpi_evaluate_info *info);
+
+/*
+ * psargs - Parse AML opcode arguments
+ */
+u8 *acpi_ps_get_next_package_end(struct acpi_parse_state *parser_state);
+
+char *acpi_ps_get_next_namestring(struct acpi_parse_state *parser_state);
+
+void
+acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state,
+ u32 arg_type, union acpi_parse_object *arg);
+
+acpi_status
+acpi_ps_get_next_namepath(struct acpi_walk_state *walk_state,
+ struct acpi_parse_state *parser_state,
+ union acpi_parse_object *arg,
+ u8 possible_method_call);
+
+/* Values for u8 above */
+
+#define ACPI_NOT_METHOD_CALL FALSE
+#define ACPI_POSSIBLE_METHOD_CALL TRUE
+
+acpi_status
+acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
+ struct acpi_parse_state *parser_state,
+ u32 arg_type, union acpi_parse_object **return_arg);
+
+/*
+ * psfind
+ */
+union acpi_parse_object *acpi_ps_find_name(union acpi_parse_object *scope,
+ u32 name, u32 opcode);
+
+union acpi_parse_object *acpi_ps_get_parent(union acpi_parse_object *op);
+
+/*
+ * psobject - support for parse object processing
+ */
+acpi_status
+acpi_ps_build_named_op(struct acpi_walk_state *walk_state,
+ u8 *aml_op_start,
+ union acpi_parse_object *unnamed_op,
+ union acpi_parse_object **op);
+
+acpi_status
+acpi_ps_create_op(struct acpi_walk_state *walk_state,
+ u8 *aml_op_start, union acpi_parse_object **new_op);
+
+acpi_status
+acpi_ps_complete_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object **op, acpi_status status);
+
+acpi_status
+acpi_ps_complete_final_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op, acpi_status status);
+
+/*
+ * psopinfo - AML Opcode information
+ */
+const struct acpi_opcode_info *acpi_ps_get_opcode_info(u16 opcode);
+
+const char *acpi_ps_get_opcode_name(u16 opcode);
+
+u8 acpi_ps_get_argument_count(u32 op_type);
+
+/*
+ * psparse - top level parsing routines
+ */
+acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state);
+
+u32 acpi_ps_get_opcode_size(u32 opcode);
+
+u16 acpi_ps_peek_opcode(struct acpi_parse_state *state);
+
+acpi_status
+acpi_ps_complete_this_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+acpi_status
+acpi_ps_next_parse_state(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ acpi_status callback_status);
+
+/*
+ * psloop - main parse loop
+ */
+acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state);
+
+/*
+ * psscope - Scope stack management routines
+ */
+acpi_status
+acpi_ps_init_scope(struct acpi_parse_state *parser_state,
+ union acpi_parse_object *root);
+
+union acpi_parse_object *acpi_ps_get_parent_scope(struct acpi_parse_state
+ *state);
+
+u8 acpi_ps_has_completed_scope(struct acpi_parse_state *parser_state);
+
+void
+acpi_ps_pop_scope(struct acpi_parse_state *parser_state,
+ union acpi_parse_object **op, u32 *arg_list, u32 *arg_count);
+
+acpi_status
+acpi_ps_push_scope(struct acpi_parse_state *parser_state,
+ union acpi_parse_object *op,
+ u32 remaining_args, u32 arg_count);
+
+void acpi_ps_cleanup_scope(struct acpi_parse_state *state);
+
+/*
+ * pstree - parse tree manipulation routines
+ */
+void
+acpi_ps_append_arg(union acpi_parse_object *op, union acpi_parse_object *arg);
+
+union acpi_parse_object *acpi_ps_find(union acpi_parse_object *scope,
+ char *path, u16 opcode, u32 create);
+
+union acpi_parse_object *acpi_ps_get_arg(union acpi_parse_object *op, u32 argn);
+
+union acpi_parse_object *acpi_ps_get_depth_next(union acpi_parse_object *origin,
+ union acpi_parse_object *op);
+
+/*
+ * pswalk - parse tree walk routines
+ */
+acpi_status
+acpi_ps_walk_parsed_aml(union acpi_parse_object *start_op,
+ union acpi_parse_object *end_op,
+ union acpi_operand_object *mth_desc,
+ struct acpi_namespace_node *start_node,
+ union acpi_operand_object **params,
+ union acpi_operand_object **caller_return_desc,
+ acpi_owner_id owner_id,
+ acpi_parse_downwards descending_callback,
+ acpi_parse_upwards ascending_callback);
+
+acpi_status
+acpi_ps_get_next_walk_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ acpi_parse_upwards ascending_callback);
+
+acpi_status acpi_ps_delete_completed_op(struct acpi_walk_state *walk_state);
+
+void acpi_ps_delete_parse_tree(union acpi_parse_object *root);
+
+/*
+ * psutils - parser utilities
+ */
+union acpi_parse_object *acpi_ps_create_scope_op(u8 *aml);
+
+void acpi_ps_init_op(union acpi_parse_object *op, u16 opcode);
+
+union acpi_parse_object *acpi_ps_alloc_op(u16 opcode, u8 *aml);
+
+void acpi_ps_free_op(union acpi_parse_object *op);
+
+u8 acpi_ps_is_leading_char(u32 c);
+
+u32 acpi_ps_get_name(union acpi_parse_object *op);
+
+void acpi_ps_set_name(union acpi_parse_object *op, u32 name);
+
+/*
+ * psdump - display parser tree
+ */
+u32
+acpi_ps_sprint_path(char *buffer_start,
+ u32 buffer_size, union acpi_parse_object *op);
+
+u32
+acpi_ps_sprint_op(char *buffer_start,
+ u32 buffer_size, union acpi_parse_object *op);
+
+void acpi_ps_show(union acpi_parse_object *op);
+
+#endif /* __ACPARSER_H__ */
diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h
new file mode 100644
index 000000000..f7d65a200
--- /dev/null
+++ b/drivers/acpi/acpica/acpredef.h
@@ -0,0 +1,1193 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acpredef - Information table for ACPI predefined methods and objects
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACPREDEF_H__
+#define __ACPREDEF_H__
+
+/******************************************************************************
+ *
+ * Return Package types
+ *
+ * 1) PTYPE1 packages do not contain subpackages.
+ *
+ * ACPI_PTYPE1_FIXED: Fixed-length length, 1 or 2 object types:
+ * object type
+ * count
+ * object type
+ * count
+ *
+ * ACPI_PTYPE1_VAR: Variable-length length. Zero-length package is allowed:
+ * object type (Int/Buf/Ref)
+ *
+ * ACPI_PTYPE1_OPTION: Package has some required and some optional elements
+ * (Used for _PRW)
+ *
+ *
+ * 2) PTYPE2 packages contain a Variable-length number of subpackages. Each
+ * of the different types describe the contents of each of the subpackages.
+ *
+ * ACPI_PTYPE2: Each subpackage contains 1 or 2 object types. Zero-length
+ * parent package is allowed:
+ * object type
+ * count
+ * object type
+ * count
+ * (Used for _ALR,_MLS,_PSS,_TRT,_TSS)
+ *
+ * ACPI_PTYPE2_COUNT: Each subpackage has a count as first element.
+ * Zero-length parent package is allowed:
+ * object type
+ * (Used for _CSD,_PSD,_TSD)
+ *
+ * ACPI_PTYPE2_PKG_COUNT: Count of subpackages at start, 1 or 2 object types:
+ * object type
+ * count
+ * object type
+ * count
+ * (Used for _CST)
+ *
+ * ACPI_PTYPE2_FIXED: Each subpackage is of Fixed-length. Zero-length
+ * parent package is allowed.
+ * (Used for _PRT)
+ *
+ * ACPI_PTYPE2_MIN: Each subpackage has a Variable-length but minimum length.
+ * Zero-length parent package is allowed:
+ * (Used for _HPX)
+ *
+ * ACPI_PTYPE2_REV_FIXED: Revision at start, each subpackage is Fixed-length
+ * (Used for _ART, _FPS)
+ *
+ * ACPI_PTYPE2_FIX_VAR: Each subpackage consists of some fixed-length elements
+ * followed by an optional element. Zero-length parent package is allowed.
+ * object type
+ * count
+ * object type
+ * count = 0 (optional)
+ * (Used for _DLM)
+ *
+ * ACPI_PTYPE2_VAR_VAR: Variable number of subpackages, each of either a
+ * constant or variable length. The subpackages are preceded by a
+ * constant number of objects.
+ * (Used for _LPI, _RDI)
+ *
+ * ACPI_PTYPE2_UUID_PAIR: Each subpackage is preceded by a UUID Buffer. The UUID
+ * defines the format of the package. Zero-length parent package is
+ * allowed.
+ * (Used for _DSD)
+ *
+ *****************************************************************************/
+
+enum acpi_return_package_types {
+ ACPI_PTYPE1_FIXED = 1,
+ ACPI_PTYPE1_VAR = 2,
+ ACPI_PTYPE1_OPTION = 3,
+ ACPI_PTYPE2 = 4,
+ ACPI_PTYPE2_COUNT = 5,
+ ACPI_PTYPE2_PKG_COUNT = 6,
+ ACPI_PTYPE2_FIXED = 7,
+ ACPI_PTYPE2_MIN = 8,
+ ACPI_PTYPE2_REV_FIXED = 9,
+ ACPI_PTYPE2_FIX_VAR = 10,
+ ACPI_PTYPE2_VAR_VAR = 11,
+ ACPI_PTYPE2_UUID_PAIR = 12,
+ ACPI_PTYPE_CUSTOM = 13
+};
+
+/* Support macros for users of the predefined info table */
+
+#define METHOD_PREDEF_ARGS_MAX 5
+#define METHOD_ARG_BIT_WIDTH 3
+#define METHOD_ARG_MASK 0x0007
+#define ARG_COUNT_IS_MINIMUM 0x8000
+#define METHOD_MAX_ARG_TYPE ACPI_TYPE_PACKAGE
+
+#define METHOD_GET_ARG_COUNT(arg_list) ((arg_list) & METHOD_ARG_MASK)
+#define METHOD_GET_NEXT_TYPE(arg_list) (((arg_list) >>= METHOD_ARG_BIT_WIDTH) & METHOD_ARG_MASK)
+
+/* Macros used to build the predefined info table */
+
+#define METHOD_0ARGS 0
+#define METHOD_1ARGS(a1) (1 | (a1 << 3))
+#define METHOD_2ARGS(a1,a2) (2 | (a1 << 3) | (a2 << 6))
+#define METHOD_3ARGS(a1,a2,a3) (3 | (a1 << 3) | (a2 << 6) | (a3 << 9))
+#define METHOD_4ARGS(a1,a2,a3,a4) (4 | (a1 << 3) | (a2 << 6) | (a3 << 9) | (a4 << 12))
+#define METHOD_5ARGS(a1,a2,a3,a4,a5) (5 | (a1 << 3) | (a2 << 6) | (a3 << 9) | (a4 << 12) | (a5 << 15))
+
+#define METHOD_RETURNS(type) (type)
+#define METHOD_NO_RETURN_VALUE 0
+
+#define PACKAGE_INFO(a,b,c,d,e,f) {{{(a),(b),(c),(d)}, ((((u16)(f)) << 8) | (e)), 0}}
+
+/* Support macros for the resource descriptor info table */
+
+#define WIDTH_1 0x0001
+#define WIDTH_2 0x0002
+#define WIDTH_3 0x0004
+#define WIDTH_8 0x0008
+#define WIDTH_16 0x0010
+#define WIDTH_32 0x0020
+#define WIDTH_64 0x0040
+#define VARIABLE_DATA 0x0080
+#define NUM_RESOURCE_WIDTHS 8
+
+#define WIDTH_ADDRESS WIDTH_16 | WIDTH_32 | WIDTH_64
+
+#ifdef ACPI_CREATE_PREDEFINED_TABLE
+/******************************************************************************
+ *
+ * Predefined method/object information table.
+ *
+ * These are the names that can actually be evaluated via acpi_evaluate_object.
+ * Not present in this table are the following:
+ *
+ * 1) Predefined/Reserved names that are not usually evaluated via
+ * acpi_evaluate_object:
+ * _Lxx and _Exx GPE methods
+ * _Qxx EC methods
+ * _T_x compiler temporary variables
+ * _Wxx wake events
+ *
+ * 2) Predefined names that never actually exist within the AML code:
+ * Predefined resource descriptor field names
+ *
+ * 3) Predefined names that are implemented within ACPICA:
+ * _OSI
+ *
+ * The main entries in the table each contain the following items:
+ *
+ * name - The ACPI reserved name
+ * argument_list - Contains (in 16 bits), the number of required
+ * arguments to the method (3 bits), and a 3-bit type
+ * field for each argument (up to 4 arguments). The
+ * METHOD_?ARGS macros generate the correct packed data.
+ * expected_btypes - Allowed type(s) for the return value.
+ * 0 means that no return value is expected.
+ *
+ * For methods that return packages, the next entry in the table contains
+ * information about the expected structure of the package. This information
+ * is saved here (rather than in a separate table) in order to minimize the
+ * overall size of the stored data.
+ *
+ * Note: The additional braces are intended to promote portability.
+ *
+ * Note2: Table is used by the kernel-resident subsystem, the iASL compiler,
+ * and the acpi_help utility.
+ *
+ * TBD: _PRT - currently ignore reversed entries. Attempt to fix in nsrepair.
+ * Possibly fixing package elements like _BIF, etc.
+ *
+ *****************************************************************************/
+
+const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
+ {{"_AC0", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC1", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC2", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC3", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC4", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC5", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC6", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC7", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC8", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC9", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_ADR", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_AEI", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_AL0", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_AL1", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_AL2", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_AL3", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_AL4", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_AL5", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_AL6", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_AL7", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_AL8", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_AL9", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_ALC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_ALI", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_ALP", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_ALR", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 2 (Ints) */
+ PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 2, 0, 0, 0),
+
+ {{"_ALT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_ART", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (2 Ref/11 Int) */
+ PACKAGE_INFO(ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_REFERENCE, 2,
+ ACPI_RTYPE_INTEGER, 11, 0),
+
+ {{"_BBN", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_BCL", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0),
+
+ {{"_BCM", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_BCT", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_BDN", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_BFS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_BIF", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (9 Int),(4 Str) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 9,
+ ACPI_RTYPE_STRING, 4, 0),
+
+ {{"_BIX", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (16 Int),(4 Str) */
+ PACKAGE_INFO(ACPI_PTYPE_CUSTOM, ACPI_RTYPE_INTEGER, 16,
+ ACPI_RTYPE_STRING, 4, 0),
+
+ {{"_BLT",
+ METHOD_3ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_BMA", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_BMC", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_BMD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (5 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 5, 0, 0, 0),
+
+ {{"_BMS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_BPC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0),
+
+ {{"_BPS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (5 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 5, 0, 0, 0),
+
+ {{"_BPT", METHOD_1ARGS(ACPI_TYPE_PACKAGE),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_BQC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_BST", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0),
+
+ {{"_BTH", METHOD_1ARGS(ACPI_TYPE_INTEGER), /* ACPI 6.0 */
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_BTM", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_BTP", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_CBA", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* See PCI firmware spec 3.0 */
+
+ {{"_CBR", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (3 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, 0, 0, 0),
+
+ {{"_CCA", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* ACPI 5.1 */
+
+ {{"_CDM", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_CID", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints/Strs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING, 0,
+ 0, 0, 0),
+
+ {{"_CLS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (3 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, 0, 0, 0),
+
+ {{"_CPC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints/Bufs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER, 0,
+ 0, 0, 0),
+
+ {{"_CR3", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_CRS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_CRT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_CSD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(n), n-1 Int) */
+ PACKAGE_INFO(ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0, 0, 0, 0),
+
+ {{"_CST", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(n), n Pkg (1 Buf/3 Int) */
+ PACKAGE_INFO(ACPI_PTYPE2_PKG_COUNT, ACPI_RTYPE_BUFFER, 1,
+ ACPI_RTYPE_INTEGER, 3, 0),
+
+ {{"_CWS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_DCK", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_DCS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_DDC", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER)}},
+
+ {{"_DDN", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_STRING)}},
+
+ {{"_DEP", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_DGS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_DIS", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_DLM", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (1 Ref, 0/1 Optional Buf/Ref) */
+ PACKAGE_INFO(ACPI_PTYPE2_FIX_VAR, ACPI_RTYPE_REFERENCE, 1,
+ ACPI_RTYPE_REFERENCE | ACPI_RTYPE_BUFFER, 0, 0),
+
+ {{"_DMA", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_DOD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0),
+
+ {{"_DOS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_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,
+ ACPI_RTYPE_PACKAGE, 1, 0),
+
+ {{"_DSM",
+ METHOD_4ARGS(ACPI_TYPE_BUFFER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER,
+ ACPI_TYPE_PACKAGE),
+ METHOD_RETURNS(ACPI_RTYPE_ALL)}}, /* Must return a value, but it can be of any type */
+
+ {{"_DSS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_DSW",
+ METHOD_3ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_DTI", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EC_", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_EDL", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_EJ0", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EJ1", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EJ2", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EJ3", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EJ4", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EJD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_STRING)}},
+
+ {{"_ERR",
+ METHOD_3ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_STRING, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* Internal use only, used by ACPICA test suites */
+
+ {{"_EVT", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_FDE", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_FDI", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (16 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16, 0, 0, 0),
+
+ {{"_FDM", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_FIF", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0),
+
+ {{"_FIT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, /* ACPI 6.0 */
+
+ {{"_FIX", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0),
+
+ {{"_FPS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (5 Int) */
+ PACKAGE_INFO(ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_INTEGER, 5, 0, 0, 0),
+
+ {{"_FSL", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_FST", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (3 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, 0, 0, 0),
+
+ {{"_GAI", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_GCP", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_GHL", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_GLK", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_GPD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_GPE", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* _GPE method, not _GPE scope */
+
+ {{"_GRT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_GSB", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_GTF", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_GTM", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_GTS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_GWS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_HID", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING)}},
+
+ {{"_HMA", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_HOT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_HPP", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0),
+
+ /*
+ * For _HPX, a single package is returned, containing a variable-length number
+ * of subpackages. Each subpackage contains a PCI record setting.
+ * There are several different type of record settings, of different
+ * lengths, but all elements of all settings are Integers.
+ */
+ {{"_HPX", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (var Ints) */
+ PACKAGE_INFO(ACPI_PTYPE2_MIN, ACPI_RTYPE_INTEGER, 5, 0, 0, 0),
+
+ {{"_HRV", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_IFT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* See IPMI spec */
+
+ {{"_INI", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_IRC", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_LCK", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_LID", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_LPD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (2 Int) */
+ PACKAGE_INFO(ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0),
+
+ {{"_LPI", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (3 Int, n Pkg (10 Int/Buf) */
+ PACKAGE_INFO(ACPI_PTYPE2_VAR_VAR, ACPI_RTYPE_INTEGER, 3,
+ ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER | ACPI_RTYPE_STRING,
+ 10, 0),
+
+ {{"_LSI", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}},
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, 0, 0, 0),
+
+ {{"_LSR", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}},
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 1,
+ ACPI_RTYPE_BUFFER, 1, 0),
+
+ {{"_LSW",
+ METHOD_3ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_BUFFER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_MAT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_MBM", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (8 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 8, 0, 0, 0),
+
+ {{"_MLS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (1 Str/1 Buf) */
+ PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_STRING, 1, ACPI_RTYPE_BUFFER, 1,
+ 0),
+
+ {{"_MSG", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_MSM",
+ METHOD_4ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER,
+ ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_MTL", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_NBS", METHOD_0ARGS, /* ACPI 6.3 */
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_NCH", METHOD_0ARGS, /* ACPI 6.3 */
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_NIC", METHOD_0ARGS, /* ACPI 6.3 */
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_NIG", METHOD_0ARGS, /* ACPI 6.3 */
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_NIH", METHOD_1ARGS(ACPI_TYPE_BUFFER), /* ACPI 6.3 */
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_NTT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_OFF", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_ON_", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_OS_", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_STRING)}},
+
+ {{"_OSC",
+ METHOD_4ARGS(ACPI_TYPE_BUFFER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER,
+ ACPI_TYPE_BUFFER),
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_OST",
+ METHOD_3ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_BUFFER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PAI", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_PCL", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_PCT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Buf) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2, 0, 0, 0),
+
+ {{"_PDC", METHOD_1ARGS(ACPI_TYPE_BUFFER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PDL", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_PIC", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PIF", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (3 Int),(3 Str) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3,
+ ACPI_RTYPE_STRING, 3, 0),
+
+ {{"_PLD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Bufs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_BUFFER, 0, 0, 0, 0),
+
+ {{"_PMC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (11 Int),(3 Str) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 11,
+ ACPI_RTYPE_STRING, 3, 0),
+
+ {{"_PMD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_PMM", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_PPC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_PPE", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* See dig64 spec */
+
+ {{"_PR0", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_PR1", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_PR2", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_PR3", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_PRE", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_PRL", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_PRR", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Ref) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_REFERENCE, 1, 0, 0, 0),
+
+ {{"_PRS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ /*
+ * For _PRT, many BIOSs reverse the 3rd and 4th Package elements (Source
+ * and source_index). This bug is so prevalent that there is code in the
+ * ACPICA Resource Manager to detect this and switch them back. For now,
+ * do not allow and issue a warning. To allow this and eliminate the
+ * warning, add the ACPI_RTYPE_REFERENCE type to the 4th element (index 3)
+ * in the statement below.
+ */
+ {{"_PRT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (4): Int,Int,Int/Ref,Int */
+ PACKAGE_INFO(ACPI_PTYPE2_FIXED, 4, ACPI_RTYPE_INTEGER,
+ ACPI_RTYPE_INTEGER,
+ ACPI_RTYPE_INTEGER | ACPI_RTYPE_REFERENCE,
+ ACPI_RTYPE_INTEGER),
+
+ {{"_PRW", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: Pkg/Int,Int,[Variable-length Refs] (Pkg is Ref/Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_OPTION, 2,
+ ACPI_RTYPE_INTEGER | ACPI_RTYPE_PACKAGE,
+ ACPI_RTYPE_INTEGER, ACPI_RTYPE_REFERENCE, 0),
+
+ {{"_PS0", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PS1", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PS2", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PS3", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PSC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_PSD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (5 Int) with count */
+ PACKAGE_INFO(ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0, 0, 0, 0),
+
+ {{"_PSE", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PSL", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_PSR", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_PSS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (6 Int) */
+ PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 6, 0, 0, 0),
+
+ {{"_PSV", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_PSW", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PTC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Buf) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2, 0, 0, 0),
+
+ {{"_PTP", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_PTS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PUR", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0),
+
+ {{"_PXM", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_RDI", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int, n Pkg (m Ref)) */
+ PACKAGE_INFO(ACPI_PTYPE2_VAR_VAR, ACPI_RTYPE_INTEGER, 1,
+ ACPI_RTYPE_REFERENCE, 0, 0),
+
+ {{"_REG", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_REV", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_RMV", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_ROM", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_RST", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_RTV", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ /*
+ * For _S0_ through _S5_, the ACPI spec defines a return Package
+ * containing 1 Integer, but most DSDTs have it wrong - 2,3, or 4 integers.
+ * Allow this by making the objects "Variable-length length", but all elements
+ * must be Integers.
+ */
+ {{"_S0_", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0),
+
+ {{"_S1_", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0),
+
+ {{"_S2_", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0),
+
+ {{"_S3_", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0),
+
+ {{"_S4_", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0),
+
+ {{"_S5_", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0),
+
+ {{"_S1D", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_S2D", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_S3D", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_S4D", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_S0W", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_S1W", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_S2W", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_S3W", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_S4W", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_SBA", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0),
+
+ {{"_SBI", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int, 1 Buf) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 1,
+ ACPI_RTYPE_BUFFER, 1, 0),
+
+ {{"_SBR",
+ METHOD_3ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER,
+ ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2,
+ ACPI_RTYPE_BUFFER | ACPI_RTYPE_INTEGER, 1, 0),
+
+ {{"_SBS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_SBT",
+ METHOD_4ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER,
+ ACPI_TYPE_ANY),
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Int, 1 Buf | Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2,
+ ACPI_RTYPE_BUFFER | ACPI_RTYPE_INTEGER, 1, 0),
+
+ {{"_SBW",
+ METHOD_5ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER,
+ ACPI_TYPE_INTEGER, ACPI_TYPE_ANY),
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}},
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER | ACPI_RTYPE_INTEGER,
+ 1, 0, 0, 0),
+
+ {{"_SCP", METHOD_1ARGS(ACPI_TYPE_INTEGER) | ARG_COUNT_IS_MINIMUM,
+ METHOD_NO_RETURN_VALUE}}, /* Acpi 1.0 allowed 1 integer arg. Acpi 3.0 expanded to 3 args. Allow both. */
+
+ {{"_SDD", METHOD_1ARGS(ACPI_TYPE_BUFFER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_SEG", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_SHL", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_SLI", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_SPD", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_SRS", METHOD_1ARGS(ACPI_TYPE_BUFFER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_SRT", METHOD_1ARGS(ACPI_TYPE_BUFFER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_SRV", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* See IPMI spec */
+
+ {{"_SST", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_STA", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_STM",
+ METHOD_3ARGS(ACPI_TYPE_BUFFER, ACPI_TYPE_BUFFER, ACPI_TYPE_BUFFER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_STP", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_STR", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_STV", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_SUB", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_STRING)}},
+
+ {{"_SUN", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_SWS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TC1", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TC2", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TDL", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TFP", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TIP", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TIV", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TMP", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TPC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TPT", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_TRT", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 2 Ref/6 Int */
+ PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_REFERENCE, 2, ACPI_RTYPE_INTEGER,
+ 6, 0),
+
+ {{"_TSD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 5 Int with count */
+ PACKAGE_INFO(ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 5, 0, 0, 0),
+
+ {{"_TSN", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_REFERENCE)}},
+
+ {{"_TSP", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TSS", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 5 Int */
+ PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 5, 0, 0, 0),
+
+ {{"_TST", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_TTS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_TZD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+
+ {{"_TZM", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_REFERENCE)}},
+
+ {{"_TZP", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_UID", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING)}},
+
+ {{"_UPC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0),
+
+ {{"_UPD", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_UPP", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ {{"_VPO", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
+ /* Acpi 1.0 defined _WAK with no return value. Later, it was changed to return a package */
+
+ {{"_WAK", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_NONE | ACPI_RTYPE_INTEGER |
+ ACPI_RTYPE_PACKAGE)}},
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0), /* Fixed-length (2 Int), but is optional */
+
+ /* _WDG/_WED are MS extensions defined by "Windows Instrumentation" */
+
+ {{"_WDG", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+
+ {{"_WED", METHOD_1ARGS(ACPI_TYPE_INTEGER),
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING |
+ ACPI_RTYPE_BUFFER)}},
+
+ {{"_WPC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* ACPI 6.1 */
+
+ {{"_WPP", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* ACPI 6.1 */
+
+ PACKAGE_INFO(0, 0, 0, 0, 0, 0) /* Table terminator */
+};
+#else
+extern const union acpi_predefined_info acpi_gbl_predefined_methods[];
+#endif
+
+#if (defined ACPI_CREATE_RESOURCE_TABLE && defined ACPI_APPLICATION)
+/******************************************************************************
+ *
+ * Predefined names for use in Resource Descriptors. These names do not
+ * appear in the global Predefined Name table (since these names never
+ * appear in actual AML byte code, only in the original ASL)
+ *
+ * Note: Used by iASL compiler and acpi_help utility only.
+ *
+ *****************************************************************************/
+
+const union acpi_predefined_info acpi_gbl_resource_names[] = {
+ {{"_ADR", WIDTH_16 | WIDTH_64, 0}},
+ {{"_ALN", WIDTH_8 | WIDTH_16 | WIDTH_32, 0}},
+ {{"_ASI", WIDTH_8, 0}},
+ {{"_ASZ", WIDTH_8, 0}},
+ {{"_ATT", WIDTH_64, 0}},
+ {{"_BAS", WIDTH_16 | WIDTH_32, 0}},
+ {{"_BM_", WIDTH_1, 0}},
+ {{"_DBT", WIDTH_16, 0}}, /* Acpi 5.0 */
+ {{"_DEC", WIDTH_1, 0}},
+ {{"_DMA", WIDTH_8, 0}},
+ {{"_DPL", WIDTH_1, 0}}, /* Acpi 5.0 */
+ {{"_DRS", WIDTH_16, 0}}, /* Acpi 5.0 */
+ {{"_END", WIDTH_1, 0}}, /* Acpi 5.0 */
+ {{"_FLC", WIDTH_2, 0}}, /* Acpi 5.0 */
+ {{"_GRA", WIDTH_ADDRESS, 0}},
+ {{"_HE_", WIDTH_1, 0}},
+ {{"_INT", WIDTH_16 | WIDTH_32, 0}},
+ {{"_IOR", WIDTH_2, 0}}, /* Acpi 5.0 */
+ {{"_LEN", WIDTH_8 | WIDTH_ADDRESS, 0}},
+ {{"_LIN", WIDTH_8, 0}}, /* Acpi 5.0 */
+ {{"_LL_", WIDTH_1, 0}},
+ {{"_MAF", WIDTH_1, 0}},
+ {{"_MAX", WIDTH_ADDRESS, 0}},
+ {{"_MEM", WIDTH_2, 0}},
+ {{"_MIF", WIDTH_1, 0}},
+ {{"_MIN", WIDTH_ADDRESS, 0}},
+ {{"_MOD", WIDTH_1, 0}}, /* Acpi 5.0 */
+ {{"_MTP", WIDTH_2, 0}},
+ {{"_PAR", WIDTH_8, 0}}, /* Acpi 5.0 */
+ {{"_PHA", WIDTH_1, 0}}, /* Acpi 5.0 */
+ {{"_PIN", WIDTH_16, 0}}, /* Acpi 5.0 */
+ {{"_PPI", WIDTH_8, 0}}, /* Acpi 5.0 */
+ {{"_POL", WIDTH_1 | WIDTH_2, 0}}, /* Acpi 5.0 */
+ {{"_RBO", WIDTH_8, 0}},
+ {{"_RBW", WIDTH_8, 0}},
+ {{"_RNG", WIDTH_1, 0}},
+ {{"_RT_", WIDTH_8, 0}}, /* Acpi 3.0 */
+ {{"_RW_", WIDTH_1, 0}},
+ {{"_RXL", WIDTH_16, 0}}, /* Acpi 5.0 */
+ {{"_SHR", WIDTH_2, 0}},
+ {{"_SIZ", WIDTH_2, 0}},
+ {{"_SLV", WIDTH_1, 0}}, /* Acpi 5.0 */
+ {{"_SPE", WIDTH_32, 0}}, /* Acpi 5.0 */
+ {{"_STB", WIDTH_2, 0}}, /* Acpi 5.0 */
+ {{"_TRA", WIDTH_ADDRESS, 0}},
+ {{"_TRS", WIDTH_1, 0}},
+ {{"_TSF", WIDTH_8, 0}}, /* Acpi 3.0 */
+ {{"_TTP", WIDTH_1, 0}},
+ {{"_TXL", WIDTH_16, 0}}, /* Acpi 5.0 */
+ {{"_TYP", WIDTH_2 | WIDTH_16, 0}},
+ {{"_VEN", VARIABLE_DATA, 0}}, /* Acpi 5.0 */
+ PACKAGE_INFO(0, 0, 0, 0, 0, 0) /* Table terminator */
+};
+
+const union acpi_predefined_info acpi_gbl_scope_names[] = {
+ {{"_GPE", 0, 0}},
+ {{"_PR_", 0, 0}},
+ {{"_SB_", 0, 0}},
+ {{"_SI_", 0, 0}},
+ {{"_TZ_", 0, 0}},
+ PACKAGE_INFO(0, 0, 0, 0, 0, 0) /* Table terminator */
+};
+#else
+extern const union acpi_predefined_info acpi_gbl_resource_names[];
+#endif
+
+#endif
diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h
new file mode 100644
index 000000000..f7749c63d
--- /dev/null
+++ b/drivers/acpi/acpica/acresrc.h
@@ -0,0 +1,366 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acresrc.h - Resource Manager function prototypes
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACRESRC_H__
+#define __ACRESRC_H__
+
+/* Need the AML resource descriptor structs */
+
+#include "amlresrc.h"
+
+/*
+ * If possible, pack the following structures to byte alignment, since we
+ * don't care about performance for debug output. Two cases where we cannot
+ * pack the structures:
+ *
+ * 1) Hardware does not support misaligned memory transfers
+ * 2) Compiler does not support pointers within packed structures
+ */
+#if (!defined(ACPI_MISALIGNMENT_NOT_SUPPORTED) && !defined(ACPI_PACKED_POINTERS_NOT_SUPPORTED))
+#pragma pack(1)
+#endif
+
+/*
+ * Individual entry for the resource conversion tables
+ */
+typedef const struct acpi_rsconvert_info {
+ u8 opcode;
+ u8 resource_offset;
+ u8 aml_offset;
+ u8 value;
+
+} acpi_rsconvert_info;
+
+/* Resource conversion opcodes */
+
+typedef enum {
+ ACPI_RSC_INITGET = 0,
+ ACPI_RSC_INITSET,
+ ACPI_RSC_FLAGINIT,
+ ACPI_RSC_1BITFLAG,
+ ACPI_RSC_2BITFLAG,
+ ACPI_RSC_3BITFLAG,
+ ACPI_RSC_6BITFLAG,
+ ACPI_RSC_ADDRESS,
+ ACPI_RSC_BITMASK,
+ ACPI_RSC_BITMASK16,
+ ACPI_RSC_COUNT,
+ ACPI_RSC_COUNT16,
+ ACPI_RSC_COUNT_GPIO_PIN,
+ ACPI_RSC_COUNT_GPIO_RES,
+ ACPI_RSC_COUNT_GPIO_VEN,
+ ACPI_RSC_COUNT_SERIAL_RES,
+ ACPI_RSC_COUNT_SERIAL_VEN,
+ ACPI_RSC_DATA8,
+ ACPI_RSC_EXIT_EQ,
+ ACPI_RSC_EXIT_LE,
+ ACPI_RSC_EXIT_NE,
+ ACPI_RSC_LENGTH,
+ ACPI_RSC_MOVE_GPIO_PIN,
+ ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RSC_MOVE_SERIAL_RES,
+ ACPI_RSC_MOVE_SERIAL_VEN,
+ ACPI_RSC_MOVE8,
+ ACPI_RSC_MOVE16,
+ ACPI_RSC_MOVE32,
+ ACPI_RSC_MOVE64,
+ ACPI_RSC_SET8,
+ ACPI_RSC_SOURCE,
+ ACPI_RSC_SOURCEX
+} ACPI_RSCONVERT_OPCODES;
+
+/* Resource Conversion sub-opcodes */
+
+#define ACPI_RSC_COMPARE_AML_LENGTH 0
+#define ACPI_RSC_COMPARE_VALUE 1
+
+#define ACPI_RSC_TABLE_SIZE(d) (sizeof (d) / sizeof (struct acpi_rsconvert_info))
+
+#define ACPI_RS_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_resource,f)
+#define AML_OFFSET(f) (u8) ACPI_OFFSET (union aml_resource,f)
+
+/*
+ * Individual entry for the resource dump tables
+ */
+typedef const struct acpi_rsdump_info {
+ u8 opcode;
+ u8 offset;
+ const char *name;
+ const char **pointer;
+
+} acpi_rsdump_info;
+
+/* Values for the Opcode field above */
+
+typedef enum {
+ ACPI_RSD_TITLE = 0,
+ ACPI_RSD_1BITFLAG,
+ ACPI_RSD_2BITFLAG,
+ ACPI_RSD_3BITFLAG,
+ ACPI_RSD_6BITFLAG,
+ ACPI_RSD_ADDRESS,
+ ACPI_RSD_DWORDLIST,
+ ACPI_RSD_LITERAL,
+ ACPI_RSD_LONGLIST,
+ ACPI_RSD_SHORTLIST,
+ ACPI_RSD_SHORTLISTX,
+ ACPI_RSD_SOURCE,
+ ACPI_RSD_STRING,
+ ACPI_RSD_UINT8,
+ ACPI_RSD_UINT16,
+ ACPI_RSD_UINT32,
+ ACPI_RSD_UINT64,
+ ACPI_RSD_WORDLIST,
+ ACPI_RSD_LABEL,
+ ACPI_RSD_SOURCE_LABEL,
+
+} ACPI_RSDUMP_OPCODES;
+
+/* restore default alignment */
+
+#pragma pack()
+
+/* Resource tables indexed by internal resource type */
+
+extern const u8 acpi_gbl_aml_resource_sizes[];
+extern const u8 acpi_gbl_aml_resource_serial_bus_sizes[];
+extern struct acpi_rsconvert_info *acpi_gbl_set_resource_dispatch[];
+
+/* Resource tables indexed by raw AML resource descriptor type */
+
+extern const u8 acpi_gbl_resource_struct_sizes[];
+extern const u8 acpi_gbl_resource_struct_serial_bus_sizes[];
+extern struct acpi_rsconvert_info *acpi_gbl_get_resource_dispatch[];
+
+extern struct acpi_rsconvert_info
+ *acpi_gbl_convert_resource_serial_bus_dispatch[];
+
+struct acpi_vendor_walk_info {
+ struct acpi_vendor_uuid *uuid;
+ struct acpi_buffer *buffer;
+ acpi_status status;
+};
+
+/*
+ * rscreate
+ */
+acpi_status
+acpi_rs_create_resource_list(union acpi_operand_object *aml_buffer,
+ struct acpi_buffer *output_buffer);
+
+acpi_status
+acpi_rs_create_aml_resources(struct acpi_buffer *resource_list,
+ struct acpi_buffer *output_buffer);
+
+acpi_status
+acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object,
+ struct acpi_buffer *output_buffer);
+
+/*
+ * rsutils
+ */
+
+acpi_status
+acpi_rs_get_prt_method_data(struct acpi_namespace_node *node,
+ struct acpi_buffer *ret_buffer);
+
+acpi_status
+acpi_rs_get_crs_method_data(struct acpi_namespace_node *node,
+ struct acpi_buffer *ret_buffer);
+
+acpi_status
+acpi_rs_get_prs_method_data(struct acpi_namespace_node *node,
+ struct acpi_buffer *ret_buffer);
+
+acpi_status
+acpi_rs_get_method_data(acpi_handle handle,
+ const char *path, struct acpi_buffer *ret_buffer);
+
+acpi_status
+acpi_rs_set_srs_method_data(struct acpi_namespace_node *node,
+ struct acpi_buffer *ret_buffer);
+
+acpi_status
+acpi_rs_get_aei_method_data(struct acpi_namespace_node *node,
+ struct acpi_buffer *ret_buffer);
+
+/*
+ * rscalc
+ */
+acpi_status
+acpi_rs_get_list_length(u8 *aml_buffer,
+ u32 aml_buffer_length, acpi_size *size_needed);
+
+acpi_status
+acpi_rs_get_aml_length(struct acpi_resource *resource_list,
+ acpi_size resource_list_size, acpi_size *size_needed);
+
+acpi_status
+acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object,
+ acpi_size *buffer_size_needed);
+
+acpi_status
+acpi_rs_convert_aml_to_resources(u8 * aml,
+ u32 length,
+ u32 offset, u8 resource_index, void **context);
+
+acpi_status
+acpi_rs_convert_resources_to_aml(struct acpi_resource *resource,
+ acpi_size aml_size_needed, u8 * output_buffer);
+
+/*
+ * rsaddr
+ */
+void
+acpi_rs_set_address_common(union aml_resource *aml,
+ struct acpi_resource *resource);
+
+u8
+acpi_rs_get_address_common(struct acpi_resource *resource,
+ union aml_resource *aml);
+
+/*
+ * rsmisc
+ */
+acpi_status
+acpi_rs_convert_aml_to_resource(struct acpi_resource *resource,
+ union aml_resource *aml,
+ struct acpi_rsconvert_info *info);
+
+acpi_status
+acpi_rs_convert_resource_to_aml(struct acpi_resource *resource,
+ union aml_resource *aml,
+ struct acpi_rsconvert_info *info);
+
+/*
+ * rsutils
+ */
+void
+acpi_rs_move_data(void *destination,
+ void *source, u16 item_count, u8 move_type);
+
+u8 acpi_rs_decode_bitmask(u16 mask, u8 * list);
+
+u16 acpi_rs_encode_bitmask(u8 * list, u8 count);
+
+acpi_rs_length
+acpi_rs_get_resource_source(acpi_rs_length resource_length,
+ acpi_rs_length minimum_length,
+ struct acpi_resource_source *resource_source,
+ union aml_resource *aml, char *string_ptr);
+
+acpi_rsdesc_size
+acpi_rs_set_resource_source(union aml_resource *aml,
+ acpi_rs_length minimum_length,
+ struct acpi_resource_source *resource_source);
+
+void
+acpi_rs_set_resource_header(u8 descriptor_type,
+ acpi_rsdesc_size total_length,
+ union aml_resource *aml);
+
+void
+acpi_rs_set_resource_length(acpi_rsdesc_size total_length,
+ union aml_resource *aml);
+
+/*
+ * rsdump - Debugger support
+ */
+#ifdef ACPI_DEBUGGER
+void acpi_rs_dump_resource_list(struct acpi_resource *resource);
+
+void acpi_rs_dump_irq_list(u8 *route_table);
+#endif
+
+/*
+ * Resource conversion tables
+ */
+extern struct acpi_rsconvert_info acpi_rs_convert_dma[];
+extern struct acpi_rsconvert_info acpi_rs_convert_end_dpf[];
+extern struct acpi_rsconvert_info acpi_rs_convert_io[];
+extern struct acpi_rsconvert_info acpi_rs_convert_fixed_io[];
+extern struct acpi_rsconvert_info acpi_rs_convert_end_tag[];
+extern struct acpi_rsconvert_info acpi_rs_convert_memory24[];
+extern struct acpi_rsconvert_info acpi_rs_convert_generic_reg[];
+extern struct acpi_rsconvert_info acpi_rs_convert_memory32[];
+extern struct acpi_rsconvert_info acpi_rs_convert_fixed_memory32[];
+extern struct acpi_rsconvert_info acpi_rs_convert_address32[];
+extern struct acpi_rsconvert_info acpi_rs_convert_address16[];
+extern struct acpi_rsconvert_info acpi_rs_convert_ext_irq[];
+extern struct acpi_rsconvert_info acpi_rs_convert_address64[];
+extern struct acpi_rsconvert_info acpi_rs_convert_ext_address64[];
+extern struct acpi_rsconvert_info acpi_rs_convert_gpio[];
+extern struct acpi_rsconvert_info acpi_rs_convert_fixed_dma[];
+extern struct acpi_rsconvert_info acpi_rs_convert_csi2_serial_bus[];
+extern struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[];
+extern struct acpi_rsconvert_info acpi_rs_convert_spi_serial_bus[];
+extern struct acpi_rsconvert_info acpi_rs_convert_uart_serial_bus[];
+extern struct acpi_rsconvert_info acpi_rs_convert_pin_function[];
+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[];
+
+/* These resources require separate get/set tables */
+
+extern struct acpi_rsconvert_info acpi_rs_get_irq[];
+extern struct acpi_rsconvert_info acpi_rs_get_start_dpf[];
+extern struct acpi_rsconvert_info acpi_rs_get_vendor_small[];
+extern struct acpi_rsconvert_info acpi_rs_get_vendor_large[];
+
+extern struct acpi_rsconvert_info acpi_rs_set_irq[];
+extern struct acpi_rsconvert_info acpi_rs_set_start_dpf[];
+extern struct acpi_rsconvert_info acpi_rs_set_vendor[];
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+/*
+ * rsinfo
+ */
+extern struct acpi_rsdump_info *acpi_gbl_dump_resource_dispatch[];
+extern struct acpi_rsdump_info *acpi_gbl_dump_serial_bus_dispatch[];
+
+/*
+ * rsdumpinfo
+ */
+extern struct acpi_rsdump_info acpi_rs_dump_irq[];
+extern struct acpi_rsdump_info acpi_rs_dump_prt[];
+extern struct acpi_rsdump_info acpi_rs_dump_dma[];
+extern struct acpi_rsdump_info acpi_rs_dump_start_dpf[];
+extern struct acpi_rsdump_info acpi_rs_dump_end_dpf[];
+extern struct acpi_rsdump_info acpi_rs_dump_io[];
+extern struct acpi_rsdump_info acpi_rs_dump_io_flags[];
+extern struct acpi_rsdump_info acpi_rs_dump_fixed_io[];
+extern struct acpi_rsdump_info acpi_rs_dump_vendor[];
+extern struct acpi_rsdump_info acpi_rs_dump_end_tag[];
+extern struct acpi_rsdump_info acpi_rs_dump_memory24[];
+extern struct acpi_rsdump_info acpi_rs_dump_memory32[];
+extern struct acpi_rsdump_info acpi_rs_dump_memory_flags[];
+extern struct acpi_rsdump_info acpi_rs_dump_fixed_memory32[];
+extern struct acpi_rsdump_info acpi_rs_dump_address16[];
+extern struct acpi_rsdump_info acpi_rs_dump_address32[];
+extern struct acpi_rsdump_info acpi_rs_dump_address64[];
+extern struct acpi_rsdump_info acpi_rs_dump_ext_address64[];
+extern struct acpi_rsdump_info acpi_rs_dump_ext_irq[];
+extern struct acpi_rsdump_info acpi_rs_dump_generic_reg[];
+extern struct acpi_rsdump_info acpi_rs_dump_gpio[];
+extern struct acpi_rsdump_info acpi_rs_dump_pin_function[];
+extern struct acpi_rsdump_info acpi_rs_dump_fixed_dma[];
+extern struct acpi_rsdump_info acpi_rs_dump_common_serial_bus[];
+extern struct acpi_rsdump_info acpi_rs_dump_csi2_serial_bus[];
+extern struct acpi_rsdump_info acpi_rs_dump_i2c_serial_bus[];
+extern struct acpi_rsdump_info acpi_rs_dump_spi_serial_bus[];
+extern struct acpi_rsdump_info acpi_rs_dump_uart_serial_bus[];
+extern struct acpi_rsdump_info acpi_rs_dump_general_flags[];
+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[];
+#endif
+
+#endif /* __ACRESRC_H__ */
diff --git a/drivers/acpi/acpica/acstruct.h b/drivers/acpi/acpica/acstruct.h
new file mode 100644
index 000000000..b859de96a
--- /dev/null
+++ b/drivers/acpi/acpica/acstruct.h
@@ -0,0 +1,222 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acstruct.h - Internal structs
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACSTRUCT_H__
+#define __ACSTRUCT_H__
+
+/* acpisrc:struct_defs -- for acpisrc conversion */
+
+/*****************************************************************************
+ *
+ * Tree walking typedefs and structs
+ *
+ ****************************************************************************/
+
+/*
+ * Walk state - current state of a parse tree walk. Used for both a leisurely
+ * stroll through the tree (for whatever reason), and for control method
+ * execution.
+ */
+#define ACPI_NEXT_OP_DOWNWARD 1
+#define ACPI_NEXT_OP_UPWARD 2
+
+/*
+ * Groups of definitions for walk_type used for different implementations of
+ * walkers (never simultaneously) - flags for interpreter:
+ */
+#define ACPI_WALK_NON_METHOD 0
+#define ACPI_WALK_METHOD 0x01
+#define ACPI_WALK_METHOD_RESTART 0x02
+
+struct acpi_walk_state {
+ struct acpi_walk_state *next; /* Next walk_state in list */
+ u8 descriptor_type; /* To differentiate various internal objs */
+ u8 walk_type;
+ u16 opcode; /* Current AML opcode */
+ u8 next_op_info; /* Info about next_op */
+ u8 num_operands; /* Stack pointer for Operands[] array */
+ u8 operand_index; /* Index into operand stack, to be used by acpi_ds_obj_stack_push */
+ acpi_owner_id owner_id; /* Owner of objects created during the walk */
+ u8 last_predicate; /* Result of last predicate */
+ u8 current_result;
+ u8 return_used;
+ u8 scope_depth;
+ u8 pass_number; /* Parse pass during table load */
+ u8 namespace_override; /* Override existing objects */
+ u8 result_size; /* Total elements for the result stack */
+ u8 result_count; /* Current number of occupied elements of result stack */
+ u8 *aml;
+ u32 arg_types;
+ u32 method_breakpoint; /* For single stepping */
+ u32 user_breakpoint; /* User AML breakpoint */
+ u32 parse_flags;
+
+ struct acpi_parse_state parser_state; /* Current state of parser */
+ u32 prev_arg_types;
+ u32 arg_count; /* push for fixed or var args */
+ u16 method_nesting_depth;
+ u8 method_is_nested;
+
+ struct acpi_namespace_node arguments[ACPI_METHOD_NUM_ARGS]; /* Control method arguments */
+ struct acpi_namespace_node local_variables[ACPI_METHOD_NUM_LOCALS]; /* Control method locals */
+ union acpi_operand_object *operands[ACPI_OBJ_NUM_OPERANDS + 1]; /* Operands passed to the interpreter (+1 for NULL terminator) */
+ union acpi_operand_object **params;
+
+ u8 *aml_last_while;
+ union acpi_operand_object **caller_return_desc;
+ union acpi_generic_state *control_state; /* List of control states (nested IFs) */
+ struct acpi_namespace_node *deferred_node; /* Used when executing deferred opcodes */
+ union acpi_operand_object *implicit_return_obj;
+ struct acpi_namespace_node *method_call_node; /* Called method Node */
+ union acpi_parse_object *method_call_op; /* method_call Op if running a method */
+ union acpi_operand_object *method_desc; /* Method descriptor if running a method */
+ struct acpi_namespace_node *method_node; /* Method node if running a method */
+ char *method_pathname; /* Full pathname of running method */
+ union acpi_parse_object *op; /* Current parser op */
+ const struct acpi_opcode_info *op_info; /* Info on current opcode */
+ union acpi_parse_object *origin; /* Start of walk [Obsolete] */
+ union acpi_operand_object *result_obj;
+ union acpi_generic_state *results; /* Stack of accumulated results */
+ union acpi_operand_object *return_desc; /* Return object, if any */
+ union acpi_generic_state *scope_info; /* Stack of nested scopes */
+ union acpi_parse_object *prev_op; /* Last op that was processed */
+ union acpi_parse_object *next_op; /* next op to be processed */
+ struct acpi_thread_state *thread;
+ acpi_parse_downwards descending_callback;
+ acpi_parse_upwards ascending_callback;
+};
+
+/* Info used by acpi_ns_initialize_objects and acpi_ds_initialize_objects */
+
+struct acpi_init_walk_info {
+ u32 table_index;
+ u32 object_count;
+ u32 method_count;
+ u32 serial_method_count;
+ u32 non_serial_method_count;
+ u32 serialized_method_count;
+ u32 device_count;
+ u32 op_region_count;
+ u32 field_count;
+ u32 buffer_count;
+ u32 package_count;
+ u32 op_region_init;
+ u32 field_init;
+ u32 buffer_init;
+ u32 package_init;
+ acpi_owner_id owner_id;
+};
+
+struct acpi_get_devices_info {
+ acpi_walk_callback user_function;
+ void *context;
+ const char *hid;
+};
+
+union acpi_aml_operands {
+ union acpi_operand_object *operands[7];
+
+ struct {
+ struct acpi_object_integer *type;
+ struct acpi_object_integer *code;
+ struct acpi_object_integer *argument;
+
+ } fatal;
+
+ struct {
+ union acpi_operand_object *source;
+ struct acpi_object_integer *index;
+ union acpi_operand_object *target;
+
+ } index;
+
+ struct {
+ union acpi_operand_object *source;
+ struct acpi_object_integer *index;
+ struct acpi_object_integer *length;
+ union acpi_operand_object *target;
+
+ } mid;
+};
+
+/*
+ * Structure used to pass object evaluation information and parameters.
+ * Purpose is to reduce CPU stack use.
+ */
+struct acpi_evaluate_info {
+ /* The first 3 elements are passed by the caller to acpi_ns_evaluate */
+
+ struct acpi_namespace_node *prefix_node; /* Input: starting node */
+ const char *relative_pathname; /* Input: path relative to prefix_node */
+ union acpi_operand_object **parameters; /* Input: argument list */
+
+ struct acpi_namespace_node *node; /* Resolved node (prefix_node:relative_pathname) */
+ union acpi_operand_object *obj_desc; /* Object attached to the resolved node */
+ char *full_pathname; /* Full pathname of the resolved node */
+
+ const union acpi_predefined_info *predefined; /* Used if Node is a predefined name */
+ union acpi_operand_object *return_object; /* Object returned from the evaluation */
+ union acpi_operand_object *parent_package; /* Used if return object is a Package */
+
+ u32 return_flags; /* Used for return value analysis */
+ u32 return_btype; /* Bitmapped type of the returned object */
+ u16 param_count; /* Count of the input argument list */
+ u16 node_flags; /* Same as Node->Flags */
+ u8 pass_number; /* Parser pass number */
+ u8 return_object_type; /* Object type of the returned object */
+ u8 flags; /* General flags */
+};
+
+/* Values for Flags above */
+
+#define ACPI_IGNORE_RETURN_VALUE 1
+
+/* Defines for return_flags field above */
+
+#define ACPI_OBJECT_REPAIRED 1
+#define ACPI_OBJECT_WRAPPED 2
+
+/* Info used by acpi_ns_initialize_devices */
+
+struct acpi_device_walk_info {
+ struct acpi_table_desc *table_desc;
+ struct acpi_evaluate_info *evaluate_info;
+ u32 device_count;
+ u32 num_STA;
+ u32 num_INI;
+};
+
+/* Info used by Acpi acpi_db_display_fields */
+
+struct acpi_region_walk_info {
+ u32 debug_level;
+ u32 count;
+ acpi_owner_id owner_id;
+ u8 display_type;
+ u32 address_space_id;
+};
+
+/* TBD: [Restructure] Merge with struct above */
+
+struct acpi_walk_info {
+ u32 debug_level;
+ u32 count;
+ acpi_owner_id owner_id;
+ u8 display_type;
+};
+
+/* Display Types */
+
+#define ACPI_DISPLAY_SUMMARY (u8) 0
+#define ACPI_DISPLAY_OBJECTS (u8) 1
+#define ACPI_DISPLAY_MASK (u8) 1
+
+#define ACPI_DISPLAY_SHORT (u8) 2
+
+#endif
diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h
new file mode 100644
index 000000000..f8d7bfd73
--- /dev/null
+++ b/drivers/acpi/acpica/actables.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: actables.h - ACPI table management
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __ACTABLES_H__
+#define __ACTABLES_H__
+
+acpi_status acpi_allocate_root_table(u32 initial_table_count);
+
+/*
+ * tbxfroot - Root pointer utilities
+ */
+u32 acpi_tb_get_rsdp_length(struct acpi_table_rsdp *rsdp);
+
+acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp);
+
+u8 *acpi_tb_scan_memory_for_rsdp(u8 *start_address, u32 length);
+
+/*
+ * tbdata - table data structure management
+ */
+acpi_status
+acpi_tb_get_next_table_descriptor(u32 *table_index,
+ struct acpi_table_desc **table_desc);
+
+void
+acpi_tb_init_table_descriptor(struct acpi_table_desc *table_desc,
+ acpi_physical_address address,
+ u8 flags, struct acpi_table_header *table);
+
+acpi_status
+acpi_tb_acquire_temp_table(struct acpi_table_desc *table_desc,
+ acpi_physical_address address,
+ u8 flags, struct acpi_table_header *table);
+
+void acpi_tb_release_temp_table(struct acpi_table_desc *table_desc);
+
+acpi_status acpi_tb_validate_temp_table(struct acpi_table_desc *table_desc);
+
+acpi_status
+acpi_tb_verify_temp_table(struct acpi_table_desc *table_desc,
+ char *signature, u32 *table_index);
+
+u8 acpi_tb_is_table_loaded(u32 table_index);
+
+void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded);
+
+/*
+ * tbfadt - FADT parse/convert/validate
+ */
+void acpi_tb_parse_fadt(void);
+
+void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length);
+
+/*
+ * tbfind - find ACPI table
+ */
+acpi_status
+acpi_tb_find_table(char *signature,
+ char *oem_id, char *oem_table_id, u32 *table_index);
+
+/*
+ * tbinstal - Table removal and deletion
+ */
+acpi_status acpi_tb_resize_root_table_list(void);
+
+acpi_status acpi_tb_validate_table(struct acpi_table_desc *table_desc);
+
+void acpi_tb_invalidate_table(struct acpi_table_desc *table_desc);
+
+void acpi_tb_override_table(struct acpi_table_desc *old_table_desc);
+
+acpi_status
+acpi_tb_acquire_table(struct acpi_table_desc *table_desc,
+ struct acpi_table_header **table_ptr,
+ u32 *table_length, u8 *table_flags);
+
+void
+acpi_tb_release_table(struct acpi_table_header *table,
+ u32 table_length, u8 table_flags);
+
+acpi_status
+acpi_tb_install_standard_table(acpi_physical_address address,
+ u8 flags,
+ struct acpi_table_header *table,
+ u8 reload, u8 override, u32 *table_index);
+
+void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc);
+
+acpi_status
+acpi_tb_load_table(u32 table_index, struct acpi_namespace_node *parent_node);
+
+acpi_status
+acpi_tb_install_and_load_table(acpi_physical_address address,
+ u8 flags,
+ struct acpi_table_header *table,
+ u8 override, u32 *table_index);
+
+acpi_status acpi_tb_unload_table(u32 table_index);
+
+void acpi_tb_notify_table(u32 event, void *table);
+
+void acpi_tb_terminate(void);
+
+acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index);
+
+acpi_status acpi_tb_allocate_owner_id(u32 table_index);
+
+acpi_status acpi_tb_release_owner_id(u32 table_index);
+
+acpi_status acpi_tb_get_owner_id(u32 table_index, acpi_owner_id *owner_id);
+
+/*
+ * tbutils - table manager utilities
+ */
+acpi_status acpi_tb_initialize_facs(void);
+
+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);
+
+void
+acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc,
+ u8 override, u32 *table_index);
+
+acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address);
+
+acpi_status
+acpi_tb_get_table(struct acpi_table_desc *table_desc,
+ struct acpi_table_header **out_table);
+
+void acpi_tb_put_table(struct acpi_table_desc *table_desc);
+
+/*
+ * tbxfload
+ */
+acpi_status acpi_tb_load_namespace(void);
+
+#endif /* __ACTABLES_H__ */
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
new file mode 100644
index 000000000..6e6270f96
--- /dev/null
+++ b/drivers/acpi/acpica/acutils.h
@@ -0,0 +1,744 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: acutils.h -- prototypes for the common (subsystem-wide) procedures
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef _ACUTILS_H
+#define _ACUTILS_H
+
+extern const u8 acpi_gbl_resource_aml_sizes[];
+extern const u8 acpi_gbl_resource_aml_serial_bus_sizes[];
+
+/* Strings used by the disassembler and debugger resource dump routines */
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined (ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER)
+
+extern const char *acpi_gbl_bm_decode[];
+extern const char *acpi_gbl_config_decode[];
+extern const char *acpi_gbl_consume_decode[];
+extern const char *acpi_gbl_dec_decode[];
+extern const char *acpi_gbl_he_decode[];
+extern const char *acpi_gbl_io_decode[];
+extern const char *acpi_gbl_ll_decode[];
+extern const char *acpi_gbl_max_decode[];
+extern const char *acpi_gbl_mem_decode[];
+extern const char *acpi_gbl_min_decode[];
+extern const char *acpi_gbl_mtp_decode[];
+extern const char *acpi_gbl_phy_decode[];
+extern const char *acpi_gbl_rng_decode[];
+extern const char *acpi_gbl_rw_decode[];
+extern const char *acpi_gbl_shr_decode[];
+extern const char *acpi_gbl_siz_decode[];
+extern const char *acpi_gbl_trs_decode[];
+extern const char *acpi_gbl_ttp_decode[];
+extern const char *acpi_gbl_typ_decode[];
+extern const char *acpi_gbl_ppc_decode[];
+extern const char *acpi_gbl_ior_decode[];
+extern const char *acpi_gbl_dts_decode[];
+extern const char *acpi_gbl_ct_decode[];
+extern const char *acpi_gbl_sbt_decode[];
+extern const char *acpi_gbl_am_decode[];
+extern const char *acpi_gbl_sm_decode[];
+extern const char *acpi_gbl_wm_decode[];
+extern const char *acpi_gbl_cph_decode[];
+extern const char *acpi_gbl_cpo_decode[];
+extern const char *acpi_gbl_dp_decode[];
+extern const char *acpi_gbl_ed_decode[];
+extern const char *acpi_gbl_bpb_decode[];
+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[];
+#endif
+
+/*
+ * For the iASL compiler case, the output is redirected to stderr so that
+ * any of the various ACPI errors and warnings do not appear in the output
+ * files, for either the compiler or disassembler portions of the tool.
+ */
+#ifdef ACPI_ASL_COMPILER
+
+#include <stdio.h>
+
+#define ACPI_MSG_REDIRECT_BEGIN \
+ FILE *output_file = acpi_gbl_output_file; \
+ acpi_os_redirect_output (stderr);
+
+#define ACPI_MSG_REDIRECT_END \
+ acpi_os_redirect_output (output_file);
+
+#else
+/*
+ * non-iASL case - no redirection, nothing to do
+ */
+#define ACPI_MSG_REDIRECT_BEGIN
+#define ACPI_MSG_REDIRECT_END
+#endif
+
+/*
+ * Common error message prefixes
+ */
+#ifndef ACPI_MSG_ERROR
+#define ACPI_MSG_ERROR "ACPI Error: "
+#endif
+#ifndef ACPI_MSG_WARNING
+#define ACPI_MSG_WARNING "ACPI Warning: "
+#endif
+#ifndef ACPI_MSG_INFO
+#define ACPI_MSG_INFO "ACPI: "
+#endif
+
+#ifndef ACPI_MSG_BIOS_ERROR
+#define ACPI_MSG_BIOS_ERROR "Firmware Error (ACPI): "
+#endif
+#ifndef ACPI_MSG_BIOS_WARNING
+#define ACPI_MSG_BIOS_WARNING "Firmware Warning (ACPI): "
+#endif
+
+/*
+ * Common message suffix
+ */
+#define ACPI_MSG_SUFFIX \
+ acpi_os_printf (" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, module_name, line_number)
+
+/* Flags to indicate implicit or explicit string-to-integer conversion */
+
+#define ACPI_IMPLICIT_CONVERSION TRUE
+#define ACPI_NO_IMPLICIT_CONVERSION FALSE
+
+/* Types for Resource descriptor entries */
+
+#define ACPI_INVALID_RESOURCE 0
+#define ACPI_FIXED_LENGTH 1
+#define ACPI_VARIABLE_LENGTH 2
+#define ACPI_SMALL_VARIABLE_LENGTH 3
+
+typedef
+acpi_status (*acpi_walk_aml_callback) (u8 *aml,
+ u32 length,
+ u32 offset,
+ u8 resource_index, void **context);
+
+typedef
+acpi_status (*acpi_pkg_callback) (u8 object_type,
+ union acpi_operand_object * source_object,
+ union acpi_generic_state * state,
+ void *context);
+
+struct acpi_pkg_info {
+ u8 *free_space;
+ acpi_size length;
+ u32 object_space;
+ u32 num_packages;
+};
+
+/* Object reference counts */
+
+#define REF_INCREMENT (u16) 0
+#define REF_DECREMENT (u16) 1
+
+/* acpi_ut_dump_buffer */
+
+#define DB_BYTE_DISPLAY 0x01
+#define DB_WORD_DISPLAY 0x02
+#define DB_DWORD_DISPLAY 0x04
+#define DB_QWORD_DISPLAY 0x08
+#define DB_DISPLAY_DATA_ONLY 0x10
+
+/*
+ * utascii - ASCII utilities
+ */
+u8 acpi_ut_valid_nameseg(char *signature);
+
+u8 acpi_ut_valid_name_char(char character, u32 position);
+
+void acpi_ut_check_and_repair_ascii(u8 *name, char *repaired_name, u32 count);
+
+/*
+ * utnonansi - Non-ANSI C library functions
+ */
+void acpi_ut_strupr(char *src_string);
+
+void acpi_ut_strlwr(char *src_string);
+
+int acpi_ut_stricmp(char *string1, char *string2);
+
+/*
+ * utstrsuppt - string-to-integer conversion support functions
+ */
+acpi_status acpi_ut_convert_octal_string(char *string, u64 *return_value);
+
+acpi_status acpi_ut_convert_decimal_string(char *string, u64 *return_value_ptr);
+
+acpi_status acpi_ut_convert_hex_string(char *string, u64 *return_value_ptr);
+
+char acpi_ut_remove_whitespace(char **string);
+
+char acpi_ut_remove_leading_zeros(char **string);
+
+u8 acpi_ut_detect_hex_prefix(char **string);
+
+void acpi_ut_remove_hex_prefix(char **string);
+
+u8 acpi_ut_detect_octal_prefix(char **string);
+
+/*
+ * utstrtoul64 - string-to-integer conversion functions
+ */
+acpi_status acpi_ut_strtoul64(char *string, u64 *ret_integer);
+
+u64 acpi_ut_explicit_strtoul64(char *string);
+
+u64 acpi_ut_implicit_strtoul64(char *string);
+
+/*
+ * utglobal - Global data structures and procedures
+ */
+acpi_status acpi_ut_init_globals(void);
+
+const char *acpi_ut_get_mutex_name(u32 mutex_id);
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+
+const char *acpi_ut_get_notify_name(u32 notify_value, acpi_object_type type);
+#endif
+
+const char *acpi_ut_get_type_name(acpi_object_type type);
+
+const char *acpi_ut_get_node_name(void *object);
+
+const char *acpi_ut_get_descriptor_name(void *object);
+
+const char *acpi_ut_get_reference_name(union acpi_operand_object *object);
+
+const char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc);
+
+const char *acpi_ut_get_region_name(u8 space_id);
+
+const char *acpi_ut_get_event_name(u32 event_id);
+
+const char *acpi_ut_get_argument_type_name(u32 arg_type);
+
+char acpi_ut_hex_to_ascii_char(u64 integer, u32 position);
+
+acpi_status acpi_ut_ascii_to_hex_byte(char *two_ascii_chars, u8 *return_byte);
+
+u8 acpi_ut_ascii_char_to_hex(int hex_char);
+
+u8 acpi_ut_valid_object_type(acpi_object_type type);
+
+/*
+ * utinit - miscellaneous initialization and shutdown
+ */
+acpi_status acpi_ut_hardware_initialize(void);
+
+void acpi_ut_subsystem_shutdown(void);
+
+/*
+ * utcopy - Object construction and conversion interfaces
+ */
+acpi_status
+acpi_ut_build_simple_object(union acpi_operand_object *obj,
+ union acpi_object *user_obj,
+ u8 *data_space, u32 *buffer_space_used);
+
+acpi_status
+acpi_ut_build_package_object(union acpi_operand_object *obj,
+ u8 *buffer, u32 *space_used);
+
+acpi_status
+acpi_ut_copy_iobject_to_eobject(union acpi_operand_object *obj,
+ struct acpi_buffer *ret_buffer);
+
+acpi_status
+acpi_ut_copy_eobject_to_iobject(union acpi_object *obj,
+ union acpi_operand_object **internal_obj);
+
+acpi_status
+acpi_ut_copy_isimple_to_isimple(union acpi_operand_object *source_obj,
+ union acpi_operand_object *dest_obj);
+
+acpi_status
+acpi_ut_copy_iobject_to_iobject(union acpi_operand_object *source_desc,
+ union acpi_operand_object **dest_desc,
+ struct acpi_walk_state *walk_state);
+
+/*
+ * utcreate - Object creation
+ */
+acpi_status
+acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action);
+
+/*
+ * utdebug - Debug interfaces
+ */
+void acpi_ut_init_stack_ptr_trace(void);
+
+void acpi_ut_track_stack_ptr(void);
+
+void
+acpi_ut_trace(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id);
+
+void
+acpi_ut_trace_ptr(u32 line_number,
+ const char *function_name,
+ const char *module_name,
+ u32 component_id, const void *pointer);
+
+void
+acpi_ut_trace_u32(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, u32 integer);
+
+void
+acpi_ut_trace_str(u32 line_number,
+ const char *function_name,
+ const char *module_name,
+ u32 component_id, const char *string);
+
+void
+acpi_ut_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id);
+
+void
+acpi_ut_status_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name,
+ u32 component_id, acpi_status status);
+
+void
+acpi_ut_value_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, u64 value);
+
+void
+acpi_ut_ptr_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, u8 *ptr);
+
+void
+acpi_ut_str_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, const char *string);
+
+void
+acpi_ut_debug_dump_buffer(u8 *buffer, u32 count, u32 display, u32 component_id);
+
+void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 offset);
+
+#ifdef ACPI_APPLICATION
+void
+acpi_ut_dump_buffer_to_file(ACPI_FILE file,
+ u8 *buffer,
+ u32 count, u32 display, u32 base_offset);
+#endif
+
+void acpi_ut_report_error(char *module_name, u32 line_number);
+
+void acpi_ut_report_info(char *module_name, u32 line_number);
+
+void acpi_ut_report_warning(char *module_name, u32 line_number);
+
+/*
+ * utdelete - Object deletion and reference counts
+ */
+void acpi_ut_add_reference(union acpi_operand_object *object);
+
+void acpi_ut_remove_reference(union acpi_operand_object *object);
+
+void acpi_ut_delete_internal_package_object(union acpi_operand_object *object);
+
+void acpi_ut_delete_internal_simple_object(union acpi_operand_object *object);
+
+void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list);
+
+/*
+ * uteval - object evaluation
+ */
+acpi_status
+acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node,
+ const char *path,
+ u32 expected_return_btypes,
+ union acpi_operand_object **return_desc);
+
+acpi_status
+acpi_ut_evaluate_numeric_object(const char *object_name,
+ struct acpi_namespace_node *device_node,
+ u64 *value);
+
+acpi_status
+acpi_ut_execute_STA(struct acpi_namespace_node *device_node, u32 *status_flags);
+
+acpi_status
+acpi_ut_execute_power_methods(struct acpi_namespace_node *device_node,
+ const char **method_names,
+ u8 method_count, u8 *out_values);
+
+/*
+ * utids - device ID support
+ */
+acpi_status
+acpi_ut_execute_HID(struct acpi_namespace_node *device_node,
+ struct acpi_pnp_device_id ** return_id);
+
+acpi_status
+acpi_ut_execute_UID(struct acpi_namespace_node *device_node,
+ struct acpi_pnp_device_id ** return_id);
+
+acpi_status
+acpi_ut_execute_CID(struct acpi_namespace_node *device_node,
+ struct acpi_pnp_device_id_list ** return_cid_list);
+
+acpi_status
+acpi_ut_execute_CLS(struct acpi_namespace_node *device_node,
+ struct acpi_pnp_device_id **return_id);
+
+/*
+ * utlock - reader/writer locks
+ */
+acpi_status acpi_ut_create_rw_lock(struct acpi_rw_lock *lock);
+
+void acpi_ut_delete_rw_lock(struct acpi_rw_lock *lock);
+
+acpi_status acpi_ut_acquire_read_lock(struct acpi_rw_lock *lock);
+
+acpi_status acpi_ut_release_read_lock(struct acpi_rw_lock *lock);
+
+acpi_status acpi_ut_acquire_write_lock(struct acpi_rw_lock *lock);
+
+void acpi_ut_release_write_lock(struct acpi_rw_lock *lock);
+
+/*
+ * utobject - internal object create/delete/cache routines
+ */
+union acpi_operand_object *acpi_ut_create_internal_object_dbg(const char
+ *module_name,
+ u32 line_number,
+ u32 component_id,
+ acpi_object_type
+ type);
+
+void *acpi_ut_allocate_object_desc_dbg(const char *module_name,
+ u32 line_number, u32 component_id);
+
+#define acpi_ut_create_internal_object(t) acpi_ut_create_internal_object_dbg (_acpi_module_name,__LINE__,_COMPONENT,t)
+#define acpi_ut_allocate_object_desc() acpi_ut_allocate_object_desc_dbg (_acpi_module_name,__LINE__,_COMPONENT)
+
+void acpi_ut_delete_object_desc(union acpi_operand_object *object);
+
+u8 acpi_ut_valid_internal_object(void *object);
+
+union acpi_operand_object *acpi_ut_create_package_object(u32 count);
+
+union acpi_operand_object *acpi_ut_create_integer_object(u64 value);
+
+union acpi_operand_object *acpi_ut_create_buffer_object(acpi_size buffer_size);
+
+union acpi_operand_object *acpi_ut_create_string_object(acpi_size string_size);
+
+acpi_status
+acpi_ut_get_object_size(union acpi_operand_object *obj, acpi_size *obj_length);
+
+/*
+ * utosi - Support for the _OSI predefined control method
+ */
+acpi_status acpi_ut_initialize_interfaces(void);
+
+acpi_status acpi_ut_interface_terminate(void);
+
+acpi_status acpi_ut_install_interface(acpi_string interface_name);
+
+acpi_status acpi_ut_remove_interface(acpi_string interface_name);
+
+acpi_status acpi_ut_update_interfaces(u8 action);
+
+struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name);
+
+acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state);
+
+/*
+ * utpredef - support for predefined names
+ */
+const union acpi_predefined_info *acpi_ut_get_next_predefined_method(const union
+ acpi_predefined_info
+ *this_name);
+
+const union acpi_predefined_info *acpi_ut_match_predefined_method(char *name);
+
+void acpi_ut_get_expected_return_types(char *buffer, u32 expected_btypes);
+
+#if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP)
+const union acpi_predefined_info *acpi_ut_match_resource_name(char *name);
+
+void
+acpi_ut_display_predefined_method(char *buffer,
+ const union acpi_predefined_info *this_name,
+ u8 multi_line);
+
+u32 acpi_ut_get_resource_bit_width(char *buffer, u16 types);
+#endif
+
+/*
+ * utstate - Generic state creation/cache routines
+ */
+void
+acpi_ut_push_generic_state(union acpi_generic_state **list_head,
+ union acpi_generic_state *state);
+
+union acpi_generic_state *acpi_ut_pop_generic_state(union acpi_generic_state
+ **list_head);
+
+union acpi_generic_state *acpi_ut_create_generic_state(void);
+
+struct acpi_thread_state *acpi_ut_create_thread_state(void);
+
+union acpi_generic_state *acpi_ut_create_update_state(union acpi_operand_object
+ *object, u16 action);
+
+union acpi_generic_state *acpi_ut_create_pkg_state(void *internal_object,
+ void *external_object,
+ u32 index);
+
+acpi_status
+acpi_ut_create_update_state_and_push(union acpi_operand_object *object,
+ u16 action,
+ union acpi_generic_state **state_list);
+
+union acpi_generic_state *acpi_ut_create_control_state(void);
+
+void acpi_ut_delete_generic_state(union acpi_generic_state *state);
+
+/*
+ * utmath
+ */
+acpi_status
+acpi_ut_divide(u64 in_dividend,
+ u64 in_divisor, u64 *out_quotient, u64 *out_remainder);
+
+acpi_status
+acpi_ut_short_divide(u64 in_dividend,
+ u32 divisor, u64 *out_quotient, u32 *out_remainder);
+
+acpi_status
+acpi_ut_short_multiply(u64 in_multiplicand, u32 multiplier, u64 *outproduct);
+
+acpi_status acpi_ut_short_shift_left(u64 operand, u32 count, u64 *out_result);
+
+acpi_status acpi_ut_short_shift_right(u64 operand, u32 count, u64 *out_result);
+
+/*
+ * utmisc
+ */
+const struct acpi_exception_info *acpi_ut_validate_exception(acpi_status
+ status);
+
+u8 acpi_ut_is_pci_root_bridge(char *id);
+
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_NAMES_APP)
+u8 acpi_ut_is_aml_table(struct acpi_table_header *table);
+#endif
+
+acpi_status
+acpi_ut_walk_package_tree(union acpi_operand_object *source_object,
+ void *target_object,
+ acpi_pkg_callback walk_callback, void *context);
+
+/* Values for Base above (16=Hex, 10=Decimal) */
+
+#define ACPI_ANY_BASE 0
+
+u32 acpi_ut_dword_byte_swap(u32 value);
+
+void acpi_ut_set_integer_width(u8 revision);
+
+#ifdef ACPI_DEBUG_OUTPUT
+void
+acpi_ut_display_init_pathname(u8 type,
+ struct acpi_namespace_node *obj_handle,
+ const char *path);
+#endif
+
+/*
+ * utownerid - Support for Table/Method Owner IDs
+ */
+acpi_status acpi_ut_allocate_owner_id(acpi_owner_id *owner_id);
+
+void acpi_ut_release_owner_id(acpi_owner_id *owner_id);
+
+/*
+ * utresrc
+ */
+acpi_status
+acpi_ut_walk_aml_resources(struct acpi_walk_state *walk_state,
+ u8 *aml,
+ acpi_size aml_length,
+ acpi_walk_aml_callback user_function,
+ void **context);
+
+acpi_status
+acpi_ut_validate_resource(struct acpi_walk_state *walk_state,
+ void *aml, u8 *return_index);
+
+u32 acpi_ut_get_descriptor_length(void *aml);
+
+u16 acpi_ut_get_resource_length(void *aml);
+
+u8 acpi_ut_get_resource_header_length(void *aml);
+
+u8 acpi_ut_get_resource_type(void *aml);
+
+acpi_status
+acpi_ut_get_resource_end_tag(union acpi_operand_object *obj_desc, u8 **end_tag);
+
+/*
+ * utstring - String and character utilities
+ */
+void acpi_ut_print_string(char *string, u16 max_length);
+
+#if defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP
+void ut_convert_backslashes(char *pathname);
+#endif
+
+void acpi_ut_repair_name(char *name);
+
+#if defined (ACPI_DEBUGGER) || defined (ACPI_APPLICATION) || defined (ACPI_DEBUG_OUTPUT)
+u8 acpi_ut_safe_strcpy(char *dest, acpi_size dest_size, char *source);
+
+void acpi_ut_safe_strncpy(char *dest, char *source, acpi_size dest_size);
+
+u8 acpi_ut_safe_strcat(char *dest, acpi_size dest_size, char *source);
+
+u8
+acpi_ut_safe_strncat(char *dest,
+ acpi_size dest_size,
+ char *source, acpi_size max_transfer_length);
+#endif
+
+/*
+ * utmutex - mutex support
+ */
+acpi_status acpi_ut_mutex_initialize(void);
+
+void acpi_ut_mutex_terminate(void);
+
+acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id);
+
+acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id);
+
+/*
+ * utalloc - memory allocation and object caching
+ */
+acpi_status acpi_ut_create_caches(void);
+
+acpi_status acpi_ut_delete_caches(void);
+
+acpi_status acpi_ut_validate_buffer(struct acpi_buffer *buffer);
+
+acpi_status
+acpi_ut_initialize_buffer(struct acpi_buffer *buffer,
+ acpi_size required_length);
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+void *acpi_ut_allocate_and_track(acpi_size size,
+ u32 component, const char *module, u32 line);
+
+void *acpi_ut_allocate_zeroed_and_track(acpi_size size,
+ u32 component,
+ const char *module, u32 line);
+
+void
+acpi_ut_free_and_track(void *address,
+ u32 component, const char *module, u32 line);
+
+void acpi_ut_dump_allocation_info(void);
+
+void acpi_ut_dump_allocations(u32 component, const char *module);
+
+acpi_status
+acpi_ut_create_list(const char *list_name,
+ u16 object_size, struct acpi_memory_list **return_cache);
+
+#endif /* ACPI_DBG_TRACK_ALLOCATIONS */
+
+/*
+ * utaddress - address range check
+ */
+acpi_status
+acpi_ut_add_address_range(acpi_adr_space_type space_id,
+ acpi_physical_address address,
+ u32 length, struct acpi_namespace_node *region_node);
+
+void
+acpi_ut_remove_address_range(acpi_adr_space_type space_id,
+ struct acpi_namespace_node *region_node);
+
+u32
+acpi_ut_check_address_range(acpi_adr_space_type space_id,
+ acpi_physical_address address, u32 length, u8 warn);
+
+void acpi_ut_delete_address_lists(void);
+
+/*
+ * utxferror - various error/warning output functions
+ */
+ACPI_PRINTF_LIKE(5)
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_warning(const char *module_name,
+ u32 line_number,
+ char *pathname,
+ u16 node_flags, const char *format, ...);
+
+ACPI_PRINTF_LIKE(5)
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_info(const char *module_name,
+ u32 line_number,
+ char *pathname,
+ u16 node_flags, const char *format, ...);
+
+ACPI_PRINTF_LIKE(5)
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_bios_error(const char *module_name,
+ u32 line_number,
+ char *pathname,
+ u16 node_flags, const char *format, ...);
+
+void
+acpi_ut_prefixed_namespace_error(const char *module_name,
+ u32 line_number,
+ union acpi_generic_state *prefix_scope,
+ const char *internal_name,
+ acpi_status lookup_status);
+
+void
+acpi_ut_method_error(const char *module_name,
+ u32 line_number,
+ const char *message,
+ struct acpi_namespace_node *node,
+ const char *path, acpi_status lookup_status);
+
+/*
+ * Utility functions for ACPI names and IDs
+ */
+const struct ah_predefined_name *acpi_ah_match_predefined_name(char *nameseg);
+
+const struct ah_device_id *acpi_ah_match_hardware_id(char *hid);
+
+const char *acpi_ah_match_uuid(u8 *data);
+
+/*
+ * utuuid -- UUID support functions
+ */
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP)
+void acpi_ut_convert_string_to_uuid(char *in_string, u8 *uuid_buffer);
+
+acpi_status acpi_ut_convert_uuid_to_string(char *uuid_buffer, char *out_string);
+#endif
+
+#endif /* _ACUTILS_H */
diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h
new file mode 100644
index 000000000..62a7ec277
--- /dev/null
+++ b/drivers/acpi/acpica/amlcode.h
@@ -0,0 +1,452 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Name: amlcode.h - Definitions for AML, as included in "definition blocks"
+ * Declarations and definitions contained herein are derived
+ * directly from the ACPI specification.
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#ifndef __AMLCODE_H__
+#define __AMLCODE_H__
+
+/* primary opcodes */
+
+#define AML_ZERO_OP (u16) 0x00
+#define AML_ONE_OP (u16) 0x01
+#define AML_ALIAS_OP (u16) 0x06
+#define AML_NAME_OP (u16) 0x08
+#define AML_BYTE_OP (u16) 0x0a
+#define AML_WORD_OP (u16) 0x0b
+#define AML_DWORD_OP (u16) 0x0c
+#define AML_STRING_OP (u16) 0x0d
+#define AML_QWORD_OP (u16) 0x0e /* ACPI 2.0 */
+#define AML_SCOPE_OP (u16) 0x10
+#define AML_BUFFER_OP (u16) 0x11
+#define AML_PACKAGE_OP (u16) 0x12
+#define AML_VARIABLE_PACKAGE_OP (u16) 0x13 /* ACPI 2.0 */
+#define AML_METHOD_OP (u16) 0x14
+#define AML_EXTERNAL_OP (u16) 0x15 /* ACPI 6.0 */
+#define AML_DUAL_NAME_PREFIX (u16) 0x2e
+#define AML_MULTI_NAME_PREFIX (u16) 0x2f
+#define AML_EXTENDED_PREFIX (u16) 0x5b
+#define AML_ROOT_PREFIX (u16) 0x5c
+#define AML_PARENT_PREFIX (u16) 0x5e
+#define AML_FIRST_LOCAL_OP (u16) 0x60 /* Used for Local op # calculations */
+#define AML_LOCAL0 (u16) 0x60
+#define AML_LOCAL1 (u16) 0x61
+#define AML_LOCAL2 (u16) 0x62
+#define AML_LOCAL3 (u16) 0x63
+#define AML_LOCAL4 (u16) 0x64
+#define AML_LOCAL5 (u16) 0x65
+#define AML_LOCAL6 (u16) 0x66
+#define AML_LOCAL7 (u16) 0x67
+#define AML_FIRST_ARG_OP (u16) 0x68 /* Used for Arg op # calculations */
+#define AML_ARG0 (u16) 0x68
+#define AML_ARG1 (u16) 0x69
+#define AML_ARG2 (u16) 0x6a
+#define AML_ARG3 (u16) 0x6b
+#define AML_ARG4 (u16) 0x6c
+#define AML_ARG5 (u16) 0x6d
+#define AML_ARG6 (u16) 0x6e
+#define AML_STORE_OP (u16) 0x70
+#define AML_REF_OF_OP (u16) 0x71
+#define AML_ADD_OP (u16) 0x72
+#define AML_CONCATENATE_OP (u16) 0x73
+#define AML_SUBTRACT_OP (u16) 0x74
+#define AML_INCREMENT_OP (u16) 0x75
+#define AML_DECREMENT_OP (u16) 0x76
+#define AML_MULTIPLY_OP (u16) 0x77
+#define AML_DIVIDE_OP (u16) 0x78
+#define AML_SHIFT_LEFT_OP (u16) 0x79
+#define AML_SHIFT_RIGHT_OP (u16) 0x7a
+#define AML_BIT_AND_OP (u16) 0x7b
+#define AML_BIT_NAND_OP (u16) 0x7c
+#define AML_BIT_OR_OP (u16) 0x7d
+#define AML_BIT_NOR_OP (u16) 0x7e
+#define AML_BIT_XOR_OP (u16) 0x7f
+#define AML_BIT_NOT_OP (u16) 0x80
+#define AML_FIND_SET_LEFT_BIT_OP (u16) 0x81
+#define AML_FIND_SET_RIGHT_BIT_OP (u16) 0x82
+#define AML_DEREF_OF_OP (u16) 0x83
+#define AML_CONCATENATE_TEMPLATE_OP (u16) 0x84 /* ACPI 2.0 */
+#define AML_MOD_OP (u16) 0x85 /* ACPI 2.0 */
+#define AML_NOTIFY_OP (u16) 0x86
+#define AML_SIZE_OF_OP (u16) 0x87
+#define AML_INDEX_OP (u16) 0x88
+#define AML_MATCH_OP (u16) 0x89
+#define AML_CREATE_DWORD_FIELD_OP (u16) 0x8a
+#define AML_CREATE_WORD_FIELD_OP (u16) 0x8b
+#define AML_CREATE_BYTE_FIELD_OP (u16) 0x8c
+#define AML_CREATE_BIT_FIELD_OP (u16) 0x8d
+#define AML_OBJECT_TYPE_OP (u16) 0x8e
+#define AML_CREATE_QWORD_FIELD_OP (u16) 0x8f /* ACPI 2.0 */
+#define AML_LOGICAL_AND_OP (u16) 0x90
+#define AML_LOGICAL_OR_OP (u16) 0x91
+#define AML_LOGICAL_NOT_OP (u16) 0x92
+#define AML_LOGICAL_EQUAL_OP (u16) 0x93
+#define AML_LOGICAL_GREATER_OP (u16) 0x94
+#define AML_LOGICAL_LESS_OP (u16) 0x95
+#define AML_TO_BUFFER_OP (u16) 0x96 /* ACPI 2.0 */
+#define AML_TO_DECIMAL_STRING_OP (u16) 0x97 /* ACPI 2.0 */
+#define AML_TO_HEX_STRING_OP (u16) 0x98 /* ACPI 2.0 */
+#define AML_TO_INTEGER_OP (u16) 0x99 /* ACPI 2.0 */
+#define AML_TO_STRING_OP (u16) 0x9c /* ACPI 2.0 */
+#define AML_COPY_OBJECT_OP (u16) 0x9d /* ACPI 2.0 */
+#define AML_MID_OP (u16) 0x9e /* ACPI 2.0 */
+#define AML_CONTINUE_OP (u16) 0x9f /* ACPI 2.0 */
+#define AML_IF_OP (u16) 0xa0
+#define AML_ELSE_OP (u16) 0xa1
+#define AML_WHILE_OP (u16) 0xa2
+#define AML_NOOP_OP (u16) 0xa3
+#define AML_RETURN_OP (u16) 0xa4
+#define AML_BREAK_OP (u16) 0xa5
+#define AML_COMMENT_OP (u16) 0xa9
+#define AML_BREAKPOINT_OP (u16) 0xcc
+#define AML_ONES_OP (u16) 0xff
+
+/*
+ * Combination opcodes (actually two one-byte opcodes)
+ * Used by the disassembler and iASL compiler
+ */
+#define AML_LOGICAL_GREATER_EQUAL_OP (u16) 0x9295 /* LNot (LLess) */
+#define AML_LOGICAL_LESS_EQUAL_OP (u16) 0x9294 /* LNot (LGreater) */
+#define AML_LOGICAL_NOT_EQUAL_OP (u16) 0x9293 /* LNot (LEqual) */
+
+/* Prefixed (2-byte) opcodes (with AML_EXTENDED_PREFIX) */
+
+#define AML_EXTENDED_OPCODE (u16) 0x5b00 /* Prefix for 2-byte opcodes */
+
+#define AML_MUTEX_OP (u16) 0x5b01
+#define AML_EVENT_OP (u16) 0x5b02
+#define AML_SHIFT_RIGHT_BIT_OP (u16) 0x5b10 /* Obsolete, not in ACPI spec */
+#define AML_SHIFT_LEFT_BIT_OP (u16) 0x5b11 /* Obsolete, not in ACPI spec */
+#define AML_CONDITIONAL_REF_OF_OP (u16) 0x5b12
+#define AML_CREATE_FIELD_OP (u16) 0x5b13
+#define AML_LOAD_TABLE_OP (u16) 0x5b1f /* ACPI 2.0 */
+#define AML_LOAD_OP (u16) 0x5b20
+#define AML_STALL_OP (u16) 0x5b21
+#define AML_SLEEP_OP (u16) 0x5b22
+#define AML_ACQUIRE_OP (u16) 0x5b23
+#define AML_SIGNAL_OP (u16) 0x5b24
+#define AML_WAIT_OP (u16) 0x5b25
+#define AML_RESET_OP (u16) 0x5b26
+#define AML_RELEASE_OP (u16) 0x5b27
+#define AML_FROM_BCD_OP (u16) 0x5b28
+#define AML_TO_BCD_OP (u16) 0x5b29
+#define AML_UNLOAD_OP (u16) 0x5b2a
+#define AML_REVISION_OP (u16) 0x5b30
+#define AML_DEBUG_OP (u16) 0x5b31
+#define AML_FATAL_OP (u16) 0x5b32
+#define AML_TIMER_OP (u16) 0x5b33 /* ACPI 3.0 */
+#define AML_REGION_OP (u16) 0x5b80
+#define AML_FIELD_OP (u16) 0x5b81
+#define AML_DEVICE_OP (u16) 0x5b82
+#define AML_PROCESSOR_OP (u16) 0x5b83
+#define AML_POWER_RESOURCE_OP (u16) 0x5b84
+#define AML_THERMAL_ZONE_OP (u16) 0x5b85
+#define AML_INDEX_FIELD_OP (u16) 0x5b86
+#define AML_BANK_FIELD_OP (u16) 0x5b87
+#define AML_DATA_REGION_OP (u16) 0x5b88 /* ACPI 2.0 */
+
+/*
+ * Opcodes for "Field" operators
+ */
+#define AML_FIELD_OFFSET_OP (u8) 0x00
+#define AML_FIELD_ACCESS_OP (u8) 0x01
+#define AML_FIELD_CONNECTION_OP (u8) 0x02 /* ACPI 5.0 */
+#define AML_FIELD_EXT_ACCESS_OP (u8) 0x03 /* ACPI 5.0 */
+
+/*
+ * Internal opcodes
+ * Use only "Unknown" AML opcodes, don't attempt to use
+ * any valid ACPI ASCII values (A-Z, 0-9, '-')
+ */
+#define AML_INT_NAMEPATH_OP (u16) 0x002d
+#define AML_INT_NAMEDFIELD_OP (u16) 0x0030
+#define AML_INT_RESERVEDFIELD_OP (u16) 0x0031
+#define AML_INT_ACCESSFIELD_OP (u16) 0x0032
+#define AML_INT_BYTELIST_OP (u16) 0x0033
+#define AML_INT_METHODCALL_OP (u16) 0x0035
+#define AML_INT_RETURN_VALUE_OP (u16) 0x0036
+#define AML_INT_EVAL_SUBTREE_OP (u16) 0x0037
+#define AML_INT_CONNECTION_OP (u16) 0x0038
+#define AML_INT_EXTACCESSFIELD_OP (u16) 0x0039
+
+#define ARG_NONE 0x0
+
+/*
+ * Argument types for the AML Parser
+ * Each field in the arg_types u32 is 5 bits, allowing for a maximum of 6 arguments.
+ * There can be up to 31 unique argument types
+ * Zero is reserved as end-of-list indicator
+ */
+#define ARGP_BYTEDATA 0x01
+#define ARGP_BYTELIST 0x02
+#define ARGP_CHARLIST 0x03
+#define ARGP_DATAOBJ 0x04
+#define ARGP_DATAOBJLIST 0x05
+#define ARGP_DWORDDATA 0x06
+#define ARGP_FIELDLIST 0x07
+#define ARGP_NAME 0x08
+#define ARGP_NAMESTRING 0x09
+#define ARGP_OBJLIST 0x0A
+#define ARGP_PKGLENGTH 0x0B
+#define ARGP_SUPERNAME 0x0C
+#define ARGP_TARGET 0x0D
+#define ARGP_TERMARG 0x0E
+#define ARGP_TERMLIST 0x0F
+#define ARGP_WORDDATA 0x10
+#define ARGP_QWORDDATA 0x11
+#define ARGP_SIMPLENAME 0x12 /* name_string | local_term | arg_term */
+#define ARGP_NAME_OR_REF 0x13 /* For object_type only */
+#define ARGP_MAX 0x13
+#define ARGP_COMMENT 0x14
+
+/*
+ * Resolved argument types for the AML Interpreter
+ * Each field in the arg_types u32 is 5 bits, allowing for a maximum of 6 arguments.
+ * There can be up to 31 unique argument types (0 is end-of-arg-list indicator)
+ *
+ * Note1: These values are completely independent from the ACPI_TYPEs
+ * i.e., ARGI_INTEGER != ACPI_TYPE_INTEGER
+ *
+ * Note2: If and when 5 bits becomes insufficient, it would probably be best
+ * to convert to a 6-byte array of argument types, allowing 8 bits per argument.
+ */
+
+/* Single, simple types */
+
+#define ARGI_ANYTYPE 0x01 /* Don't care */
+#define ARGI_PACKAGE 0x02
+#define ARGI_EVENT 0x03
+#define ARGI_MUTEX 0x04
+#define ARGI_DDBHANDLE 0x05
+
+/* Interchangeable types (via implicit conversion) */
+
+#define ARGI_INTEGER 0x06
+#define ARGI_STRING 0x07
+#define ARGI_BUFFER 0x08
+#define ARGI_BUFFER_OR_STRING 0x09 /* Used by MID op only */
+#define ARGI_COMPUTEDATA 0x0A /* Buffer, String, or Integer */
+
+/* Reference objects */
+
+#define ARGI_INTEGER_REF 0x0B
+#define ARGI_OBJECT_REF 0x0C
+#define ARGI_DEVICE_REF 0x0D
+#define ARGI_REFERENCE 0x0E
+#define ARGI_TARGETREF 0x0F /* Target, subject to implicit conversion */
+#define ARGI_FIXED_TARGET 0x10 /* Target, no implicit conversion */
+#define ARGI_SIMPLE_TARGET 0x11 /* Name, Local, Arg -- no implicit conversion */
+#define ARGI_STORE_TARGET 0x12 /* Target for store is TARGETREF + package objects */
+
+/* Multiple/complex types */
+
+#define ARGI_DATAOBJECT 0x13 /* Buffer, String, package or reference to a node - Used only by size_of operator */
+#define ARGI_COMPLEXOBJ 0x14 /* Buffer, String, or package (Used by INDEX op only) */
+#define ARGI_REF_OR_STRING 0x15 /* Reference or String (Used by DEREFOF op only) */
+#define ARGI_REGION_OR_BUFFER 0x16 /* Used by LOAD op only */
+#define ARGI_DATAREFOBJ 0x17
+
+/* Note: types above can expand to 0x1F maximum */
+
+#define ARGI_INVALID_OPCODE 0xFFFFFFFF
+
+/*
+ * Some of the flags and types below are of the form:
+ *
+ * AML_FLAGS_EXEC_#A_#T,#R, or
+ * AML_TYPE_EXEC_#A_#T,#R where:
+ *
+ * #A is the number of required arguments
+ * #T is the number of target operands
+ * #R indicates whether there is a return value
+ *
+ * These types are used for the top-level dispatch of the AML
+ * opcode. They group similar operators that can share common
+ * front-end code before dispatch to the final code that implements
+ * the operator.
+ */
+
+/*
+ * Opcode information flags
+ */
+#define AML_LOGICAL 0x0001
+#define AML_LOGICAL_NUMERIC 0x0002
+#define AML_MATH 0x0004
+#define AML_CREATE 0x0008
+#define AML_FIELD 0x0010
+#define AML_DEFER 0x0020
+#define AML_NAMED 0x0040
+#define AML_NSNODE 0x0080
+#define AML_NSOPCODE 0x0100
+#define AML_NSOBJECT 0x0200
+#define AML_HAS_RETVAL 0x0400
+#define AML_HAS_TARGET 0x0800
+#define AML_HAS_ARGS 0x1000
+#define AML_CONSTANT 0x2000
+#define AML_NO_OPERAND_RESOLVE 0x4000
+
+/* Convenient flag groupings of the flags above */
+
+#define AML_FLAGS_EXEC_0A_0T_1R AML_HAS_RETVAL
+#define AML_FLAGS_EXEC_1A_0T_0R AML_HAS_ARGS /* Monadic1 */
+#define AML_FLAGS_EXEC_1A_0T_1R AML_HAS_ARGS | AML_HAS_RETVAL /* Monadic2 */
+#define AML_FLAGS_EXEC_1A_1T_0R AML_HAS_ARGS | AML_HAS_TARGET
+#define AML_FLAGS_EXEC_1A_1T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL /* monadic2_r */
+#define AML_FLAGS_EXEC_2A_0T_0R AML_HAS_ARGS /* Dyadic1 */
+#define AML_FLAGS_EXEC_2A_0T_1R AML_HAS_ARGS | AML_HAS_RETVAL /* Dyadic2 */
+#define AML_FLAGS_EXEC_2A_1T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL /* dyadic2_r */
+#define AML_FLAGS_EXEC_2A_2T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL
+#define AML_FLAGS_EXEC_3A_0T_0R AML_HAS_ARGS
+#define AML_FLAGS_EXEC_3A_1T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL
+#define AML_FLAGS_EXEC_6A_0T_1R AML_HAS_ARGS | AML_HAS_RETVAL
+
+/*
+ * The opcode Type is used in a dispatch table, do not change
+ * or add anything new without updating the table.
+ */
+#define AML_TYPE_EXEC_0A_0T_1R 0x00 /* 0 Args, 0 Target, 1 ret_val */
+#define AML_TYPE_EXEC_1A_0T_0R 0x01 /* 1 Args, 0 Target, 0 ret_val */
+#define AML_TYPE_EXEC_1A_0T_1R 0x02 /* 1 Args, 0 Target, 1 ret_val */
+#define AML_TYPE_EXEC_1A_1T_0R 0x03 /* 1 Args, 1 Target, 0 ret_val */
+#define AML_TYPE_EXEC_1A_1T_1R 0x04 /* 1 Args, 1 Target, 1 ret_val */
+#define AML_TYPE_EXEC_2A_0T_0R 0x05 /* 2 Args, 0 Target, 0 ret_val */
+#define AML_TYPE_EXEC_2A_0T_1R 0x06 /* 2 Args, 0 Target, 1 ret_val */
+#define AML_TYPE_EXEC_2A_1T_1R 0x07 /* 2 Args, 1 Target, 1 ret_val */
+#define AML_TYPE_EXEC_2A_2T_1R 0x08 /* 2 Args, 2 Target, 1 ret_val */
+#define AML_TYPE_EXEC_3A_0T_0R 0x09 /* 3 Args, 0 Target, 0 ret_val */
+#define AML_TYPE_EXEC_3A_1T_1R 0x0A /* 3 Args, 1 Target, 1 ret_val */
+#define AML_TYPE_EXEC_6A_0T_1R 0x0B /* 6 Args, 0 Target, 1 ret_val */
+/* End of types used in dispatch table */
+
+#define AML_TYPE_LITERAL 0x0C
+#define AML_TYPE_CONSTANT 0x0D
+#define AML_TYPE_METHOD_ARGUMENT 0x0E
+#define AML_TYPE_LOCAL_VARIABLE 0x0F
+#define AML_TYPE_DATA_TERM 0x10
+
+/* Generic for an op that returns a value */
+
+#define AML_TYPE_METHOD_CALL 0x11
+
+/* Miscellaneous types */
+
+#define AML_TYPE_CREATE_FIELD 0x12
+#define AML_TYPE_CREATE_OBJECT 0x13
+#define AML_TYPE_CONTROL 0x14
+#define AML_TYPE_NAMED_NO_OBJ 0x15
+#define AML_TYPE_NAMED_FIELD 0x16
+#define AML_TYPE_NAMED_SIMPLE 0x17
+#define AML_TYPE_NAMED_COMPLEX 0x18
+#define AML_TYPE_RETURN 0x19
+#define AML_TYPE_UNDEFINED 0x1A
+#define AML_TYPE_BOGUS 0x1B
+
+/* AML Package Length encodings */
+
+#define ACPI_AML_PACKAGE_TYPE1 0x40
+#define ACPI_AML_PACKAGE_TYPE2 0x4000
+#define ACPI_AML_PACKAGE_TYPE3 0x400000
+#define ACPI_AML_PACKAGE_TYPE4 0x40000000
+
+/*
+ * Opcode classes
+ */
+#define AML_CLASS_EXECUTE 0x00
+#define AML_CLASS_CREATE 0x01
+#define AML_CLASS_ARGUMENT 0x02
+#define AML_CLASS_NAMED_OBJECT 0x03
+#define AML_CLASS_CONTROL 0x04
+#define AML_CLASS_ASCII 0x05
+#define AML_CLASS_PREFIX 0x06
+#define AML_CLASS_INTERNAL 0x07
+#define AML_CLASS_RETURN_VALUE 0x08
+#define AML_CLASS_METHOD_CALL 0x09
+#define AML_CLASS_UNKNOWN 0x0A
+
+/* Comparison operation codes for match_op operator */
+
+typedef enum {
+ MATCH_MTR = 0,
+ MATCH_MEQ = 1,
+ MATCH_MLE = 2,
+ MATCH_MLT = 3,
+ MATCH_MGE = 4,
+ MATCH_MGT = 5
+} AML_MATCH_OPERATOR;
+
+#define MAX_MATCH_OPERATOR 5
+
+/*
+ * field_flags
+ *
+ * This byte is extracted from the AML and includes three separate
+ * pieces of information about the field:
+ * 1) The field access type
+ * 2) The field update rule
+ * 3) The lock rule for the field
+ *
+ * Bits 00 - 03 : access_type (any_acc, byte_acc, etc.)
+ * 04 : lock_rule (1 == Lock)
+ * 05 - 06 : update_rule
+ */
+#define AML_FIELD_ACCESS_TYPE_MASK 0x0F
+#define AML_FIELD_LOCK_RULE_MASK 0x10
+#define AML_FIELD_UPDATE_RULE_MASK 0x60
+
+/* 1) Field Access Types */
+
+typedef enum {
+ AML_FIELD_ACCESS_ANY = 0x00,
+ AML_FIELD_ACCESS_BYTE = 0x01,
+ AML_FIELD_ACCESS_WORD = 0x02,
+ AML_FIELD_ACCESS_DWORD = 0x03,
+ AML_FIELD_ACCESS_QWORD = 0x04, /* ACPI 2.0 */
+ AML_FIELD_ACCESS_BUFFER = 0x05 /* ACPI 2.0 */
+} AML_ACCESS_TYPE;
+
+/* 2) Field Lock Rules */
+
+typedef enum {
+ AML_FIELD_LOCK_NEVER = 0x00,
+ AML_FIELD_LOCK_ALWAYS = 0x10
+} AML_LOCK_RULE;
+
+/* 3) Field Update Rules */
+
+typedef enum {
+ AML_FIELD_UPDATE_PRESERVE = 0x00,
+ AML_FIELD_UPDATE_WRITE_AS_ONES = 0x20,
+ AML_FIELD_UPDATE_WRITE_AS_ZEROS = 0x40
+} AML_UPDATE_RULE;
+
+/*
+ * Field Access Attributes.
+ * This byte is extracted from the AML via the
+ * access_as keyword
+ */
+typedef enum {
+ AML_FIELD_ATTRIB_QUICK = 0x02,
+ AML_FIELD_ATTRIB_SEND_RECEIVE = 0x04,
+ AML_FIELD_ATTRIB_BYTE = 0x06,
+ AML_FIELD_ATTRIB_WORD = 0x08,
+ AML_FIELD_ATTRIB_BLOCK = 0x0A,
+ AML_FIELD_ATTRIB_BYTES = 0x0B,
+ AML_FIELD_ATTRIB_PROCESS_CALL = 0x0C,
+ AML_FIELD_ATTRIB_BLOCK_PROCESS_CALL = 0x0D,
+ AML_FIELD_ATTRIB_RAW_BYTES = 0x0E,
+ AML_FIELD_ATTRIB_RAW_PROCESS_BYTES = 0x0F
+} AML_ACCESS_ATTRIBUTE;
+
+/* Bit fields in the AML method_flags byte */
+
+#define AML_METHOD_ARG_COUNT 0x07
+#define AML_METHOD_SERIALIZED 0x08
+#define AML_METHOD_SYNC_LEVEL 0xF0
+
+#endif /* __AMLCODE_H__ */
diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h
new file mode 100644
index 000000000..b31779ce2
--- /dev/null
+++ b/drivers/acpi/acpica/amlresrc.h
@@ -0,0 +1,573 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/******************************************************************************
+ *
+ * Module Name: amlresrc.h - AML resource descriptors
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+/* acpisrc:struct_defs -- for acpisrc conversion */
+
+#ifndef __AMLRESRC_H
+#define __AMLRESRC_H
+
+/*
+ * Resource descriptor tags, as defined in the ACPI specification.
+ * Used to symbolically reference fields within a descriptor.
+ */
+#define ACPI_RESTAG_ADDRESS "_ADR"
+#define ACPI_RESTAG_ALIGNMENT "_ALN"
+#define ACPI_RESTAG_ADDRESSSPACE "_ASI"
+#define ACPI_RESTAG_ACCESSSIZE "_ASZ"
+#define ACPI_RESTAG_TYPESPECIFICATTRIBUTES "_ATT"
+#define ACPI_RESTAG_BASEADDRESS "_BAS"
+#define ACPI_RESTAG_BUSMASTER "_BM_" /* Master(1), Slave(0) */
+#define ACPI_RESTAG_DEBOUNCETIME "_DBT"
+#define ACPI_RESTAG_DECODE "_DEC"
+#define ACPI_RESTAG_DEVICEPOLARITY "_DPL"
+#define ACPI_RESTAG_DMA "_DMA"
+#define ACPI_RESTAG_DMATYPE "_TYP" /* Compatible(0), A(1), B(2), F(3) */
+#define ACPI_RESTAG_DRIVESTRENGTH "_DRS"
+#define ACPI_RESTAG_ENDIANNESS "_END"
+#define ACPI_RESTAG_FLOWCONTROL "_FLC"
+#define ACPI_RESTAG_FUNCTION "_FUN"
+#define ACPI_RESTAG_GRANULARITY "_GRA"
+#define ACPI_RESTAG_INTERRUPT "_INT"
+#define ACPI_RESTAG_INTERRUPTLEVEL "_LL_" /* active_lo(1), active_hi(0) */
+#define ACPI_RESTAG_INTERRUPTSHARE "_SHR" /* Shareable(1), no_share(0) */
+#define ACPI_RESTAG_INTERRUPTTYPE "_HE_" /* Edge(1), Level(0) */
+#define ACPI_RESTAG_IORESTRICTION "_IOR"
+#define ACPI_RESTAG_LENGTH "_LEN"
+#define ACPI_RESTAG_LINE "_LIN"
+#define ACPI_RESTAG_LOCALPORT "_PRT"
+#define ACPI_RESTAG_MEMATTRIBUTES "_MTP" /* Memory(0), Reserved(1), ACPI(2), NVS(3) */
+#define ACPI_RESTAG_MEMTYPE "_MEM" /* non_cache(0), Cacheable(1) Cache+combine(2), Cache+prefetch(3) */
+#define ACPI_RESTAG_MAXADDR "_MAX"
+#define ACPI_RESTAG_MINADDR "_MIN"
+#define ACPI_RESTAG_MAXTYPE "_MAF"
+#define ACPI_RESTAG_MINTYPE "_MIF"
+#define ACPI_RESTAG_MODE "_MOD"
+#define ACPI_RESTAG_PARITY "_PAR"
+#define ACPI_RESTAG_PHASE "_PHA"
+#define ACPI_RESTAG_PHYTYPE "_PHY"
+#define ACPI_RESTAG_PIN "_PIN"
+#define ACPI_RESTAG_PINCONFIG "_PPI"
+#define ACPI_RESTAG_PINCONFIG_TYPE "_TYP"
+#define ACPI_RESTAG_PINCONFIG_VALUE "_VAL"
+#define ACPI_RESTAG_POLARITY "_POL"
+#define ACPI_RESTAG_REGISTERBITOFFSET "_RBO"
+#define ACPI_RESTAG_REGISTERBITWIDTH "_RBW"
+#define ACPI_RESTAG_RANGETYPE "_RNG"
+#define ACPI_RESTAG_READWRITETYPE "_RW_" /* read_only(0), Writeable (1) */
+#define ACPI_RESTAG_LENGTH_RX "_RXL"
+#define ACPI_RESTAG_LENGTH_TX "_TXL"
+#define ACPI_RESTAG_SLAVEMODE "_SLV"
+#define ACPI_RESTAG_SPEED "_SPE"
+#define ACPI_RESTAG_STOPBITS "_STB"
+#define ACPI_RESTAG_TRANSLATION "_TRA"
+#define ACPI_RESTAG_TRANSTYPE "_TRS" /* Sparse(1), Dense(0) */
+#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"
+
+/* Default sizes for "small" resource descriptors */
+
+#define ASL_RDESC_IRQ_SIZE 0x02
+#define ASL_RDESC_DMA_SIZE 0x02
+#define ASL_RDESC_ST_DEPEND_SIZE 0x00
+#define ASL_RDESC_END_DEPEND_SIZE 0x00
+#define ASL_RDESC_IO_SIZE 0x07
+#define ASL_RDESC_FIXED_IO_SIZE 0x03
+#define ASL_RDESC_FIXED_DMA_SIZE 0x05
+#define ASL_RDESC_END_TAG_SIZE 0x01
+
+struct asl_resource_node {
+ u32 buffer_length;
+ void *buffer;
+ struct asl_resource_node *next;
+};
+
+struct asl_resource_info {
+ union acpi_parse_object *descriptor_type_op; /* Resource descriptor parse node */
+ union acpi_parse_object *mapping_op; /* Used for mapfile support */
+ u32 current_byte_offset; /* Offset in resource template */
+};
+
+/* Macros used to generate AML resource length fields */
+
+#define ACPI_AML_SIZE_LARGE(r) (sizeof (r) - sizeof (struct aml_resource_large_header))
+#define ACPI_AML_SIZE_SMALL(r) (sizeof (r) - sizeof (struct aml_resource_small_header))
+
+/*
+ * Resource descriptors defined in the ACPI specification.
+ *
+ * Packing/alignment must be BYTE because these descriptors
+ * are used to overlay the raw AML byte stream.
+ */
+#pragma pack(1)
+
+/*
+ * SMALL descriptors
+ */
+#define AML_RESOURCE_SMALL_HEADER_COMMON \
+ u8 descriptor_type;
+
+struct aml_resource_small_header {
+AML_RESOURCE_SMALL_HEADER_COMMON};
+
+struct aml_resource_irq {
+ AML_RESOURCE_SMALL_HEADER_COMMON u16 irq_mask;
+ u8 flags;
+};
+
+struct aml_resource_irq_noflags {
+ AML_RESOURCE_SMALL_HEADER_COMMON u16 irq_mask;
+};
+
+struct aml_resource_dma {
+ AML_RESOURCE_SMALL_HEADER_COMMON u8 dma_channel_mask;
+ u8 flags;
+};
+
+struct aml_resource_start_dependent {
+ AML_RESOURCE_SMALL_HEADER_COMMON u8 flags;
+};
+
+struct aml_resource_start_dependent_noprio {
+AML_RESOURCE_SMALL_HEADER_COMMON};
+
+struct aml_resource_end_dependent {
+AML_RESOURCE_SMALL_HEADER_COMMON};
+
+struct aml_resource_io {
+ AML_RESOURCE_SMALL_HEADER_COMMON u8 flags;
+ u16 minimum;
+ u16 maximum;
+ u8 alignment;
+ u8 address_length;
+};
+
+struct aml_resource_fixed_io {
+ AML_RESOURCE_SMALL_HEADER_COMMON u16 address;
+ u8 address_length;
+};
+
+struct aml_resource_vendor_small {
+AML_RESOURCE_SMALL_HEADER_COMMON};
+
+struct aml_resource_end_tag {
+ AML_RESOURCE_SMALL_HEADER_COMMON u8 checksum;
+};
+
+struct aml_resource_fixed_dma {
+ AML_RESOURCE_SMALL_HEADER_COMMON u16 request_lines;
+ u16 channels;
+ u8 width;
+};
+
+/*
+ * LARGE descriptors
+ */
+#define AML_RESOURCE_LARGE_HEADER_COMMON \
+ u8 descriptor_type;\
+ u16 resource_length;
+
+struct aml_resource_large_header {
+AML_RESOURCE_LARGE_HEADER_COMMON};
+
+/* General Flags for address space resource descriptors */
+
+#define ACPI_RESOURCE_FLAG_DEC 2
+#define ACPI_RESOURCE_FLAG_MIF 4
+#define ACPI_RESOURCE_FLAG_MAF 8
+
+struct aml_resource_memory24 {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 flags;
+ u16 minimum;
+ u16 maximum;
+ u16 alignment;
+ u16 address_length;
+};
+
+struct aml_resource_vendor_large {
+AML_RESOURCE_LARGE_HEADER_COMMON};
+
+struct aml_resource_memory32 {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 flags;
+ u32 minimum;
+ u32 maximum;
+ u32 alignment;
+ u32 address_length;
+};
+
+struct aml_resource_fixed_memory32 {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 flags;
+ u32 address;
+ u32 address_length;
+};
+
+#define AML_RESOURCE_ADDRESS_COMMON \
+ u8 resource_type; \
+ u8 flags; \
+ u8 specific_flags;
+
+struct aml_resource_address {
+AML_RESOURCE_LARGE_HEADER_COMMON AML_RESOURCE_ADDRESS_COMMON};
+
+struct aml_resource_extended_address64 {
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_ADDRESS_COMMON u8 revision_ID;
+ u8 reserved;
+ u64 granularity;
+ u64 minimum;
+ u64 maximum;
+ u64 translation_offset;
+ u64 address_length;
+ u64 type_specific;
+};
+
+#define AML_RESOURCE_EXTENDED_ADDRESS_REVISION 1 /* ACPI 3.0 */
+
+struct aml_resource_address64 {
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_ADDRESS_COMMON u64 granularity;
+ u64 minimum;
+ u64 maximum;
+ u64 translation_offset;
+ u64 address_length;
+};
+
+struct aml_resource_address32 {
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_ADDRESS_COMMON u32 granularity;
+ u32 minimum;
+ u32 maximum;
+ u32 translation_offset;
+ u32 address_length;
+};
+
+struct aml_resource_address16 {
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_ADDRESS_COMMON u16 granularity;
+ u16 minimum;
+ u16 maximum;
+ u16 translation_offset;
+ u16 address_length;
+};
+
+struct aml_resource_extended_irq {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 flags;
+ u8 interrupt_count;
+ u32 interrupts[1];
+ /* res_source_index, res_source optional fields follow */
+};
+
+struct aml_resource_generic_register {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 address_space_id;
+ u8 bit_width;
+ u8 bit_offset;
+ u8 access_size; /* ACPI 3.0, was previously Reserved */
+ u64 address;
+};
+
+/* Common descriptor for gpio_int and gpio_io (ACPI 5.0) */
+
+struct aml_resource_gpio {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 revision_id;
+ u8 connection_type;
+ u16 flags;
+ u16 int_flags;
+ u8 pin_config;
+ u16 drive_strength;
+ u16 debounce_timeout;
+ u16 pin_table_offset;
+ u8 res_source_index;
+ u16 res_source_offset;
+ u16 vendor_offset;
+ u16 vendor_length;
+ /*
+ * Optional fields follow immediately:
+ * 1) PIN list (Words)
+ * 2) Resource Source String
+ * 3) Vendor Data bytes
+ */
+};
+
+#define AML_RESOURCE_GPIO_REVISION 1 /* ACPI 5.0 */
+
+/* Values for connection_type above */
+
+#define AML_RESOURCE_GPIO_TYPE_INT 0
+#define AML_RESOURCE_GPIO_TYPE_IO 1
+#define AML_RESOURCE_MAX_GPIOTYPE 1
+
+/* Common preamble for all serial descriptors (ACPI 5.0) */
+
+#define AML_RESOURCE_SERIAL_COMMON \
+ u8 revision_id; \
+ u8 res_source_index; \
+ u8 type; \
+ u8 flags; \
+ u16 type_specific_flags; \
+ u8 type_revision_id; \
+ u16 type_data_length; \
+
+/* Values for the type field above */
+
+#define AML_RESOURCE_I2C_SERIALBUSTYPE 1
+#define AML_RESOURCE_SPI_SERIALBUSTYPE 2
+#define AML_RESOURCE_UART_SERIALBUSTYPE 3
+#define AML_RESOURCE_CSI2_SERIALBUSTYPE 4
+#define AML_RESOURCE_MAX_SERIALBUSTYPE 4
+#define AML_RESOURCE_VENDOR_SERIALBUSTYPE 192 /* Vendor defined is 0xC0-0xFF (NOT SUPPORTED) */
+
+struct aml_resource_common_serialbus {
+AML_RESOURCE_LARGE_HEADER_COMMON AML_RESOURCE_SERIAL_COMMON};
+
+struct aml_resource_csi2_serialbus {
+ AML_RESOURCE_LARGE_HEADER_COMMON AML_RESOURCE_SERIAL_COMMON
+ /*
+ * Optional fields follow immediately:
+ * 1) Vendor Data bytes
+ * 2) Resource Source String
+ */
+};
+
+#define AML_RESOURCE_CSI2_REVISION 1 /* ACPI 6.4 */
+#define AML_RESOURCE_CSI2_TYPE_REVISION 1 /* ACPI 6.4 */
+#define AML_RESOURCE_CSI2_MIN_DATA_LEN 0 /* ACPI 6.4 */
+
+struct aml_resource_i2c_serialbus {
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_SERIAL_COMMON u32 connection_speed;
+ u16 slave_address;
+ /*
+ * Optional fields follow immediately:
+ * 1) Vendor Data bytes
+ * 2) Resource Source String
+ */
+};
+
+#define AML_RESOURCE_I2C_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_I2C_TYPE_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_I2C_MIN_DATA_LEN 6
+
+struct aml_resource_spi_serialbus {
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_SERIAL_COMMON u32 connection_speed;
+ u8 data_bit_length;
+ u8 clock_phase;
+ u8 clock_polarity;
+ u16 device_selection;
+ /*
+ * Optional fields follow immediately:
+ * 1) Vendor Data bytes
+ * 2) Resource Source String
+ */
+};
+
+#define AML_RESOURCE_SPI_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_SPI_TYPE_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_SPI_MIN_DATA_LEN 9
+
+struct aml_resource_uart_serialbus {
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_SERIAL_COMMON u32 default_baud_rate;
+ u16 rx_fifo_size;
+ u16 tx_fifo_size;
+ u8 parity;
+ u8 lines_enabled;
+ /*
+ * Optional fields follow immediately:
+ * 1) Vendor Data bytes
+ * 2) Resource Source String
+ */
+};
+
+#define AML_RESOURCE_UART_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_UART_TYPE_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_UART_MIN_DATA_LEN 10
+
+struct aml_resource_pin_function {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 revision_id;
+ u16 flags;
+ u8 pin_config;
+ u16 function_number;
+ u16 pin_table_offset;
+ u8 res_source_index;
+ u16 res_source_offset;
+ u16 vendor_offset;
+ u16 vendor_length;
+ /*
+ * Optional fields follow immediately:
+ * 1) PIN list (Words)
+ * 2) Resource Source String
+ * 3) Vendor Data bytes
+ */
+};
+
+#define AML_RESOURCE_PIN_FUNCTION_REVISION 1 /* ACPI 6.2 */
+
+struct aml_resource_pin_config {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 revision_id;
+ u16 flags;
+ u8 pin_config_type;
+ u32 pin_config_value;
+ u16 pin_table_offset;
+ u8 res_source_index;
+ u16 res_source_offset;
+ u16 vendor_offset;
+ u16 vendor_length;
+ /*
+ * Optional fields follow immediately:
+ * 1) PIN list (Words)
+ * 2) Resource Source String
+ * 3) Vendor Data bytes
+ */
+};
+
+#define AML_RESOURCE_PIN_CONFIG_REVISION 1 /* ACPI 6.2 */
+
+struct aml_resource_pin_group {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 revision_id;
+ u16 flags;
+ u16 pin_table_offset;
+ u16 label_offset;
+ u16 vendor_offset;
+ u16 vendor_length;
+ /*
+ * Optional fields follow immediately:
+ * 1) PIN list (Words)
+ * 2) Resource Label String
+ * 3) Vendor Data bytes
+ */
+};
+
+#define AML_RESOURCE_PIN_GROUP_REVISION 1 /* ACPI 6.2 */
+
+struct aml_resource_pin_group_function {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 revision_id;
+ u16 flags;
+ u16 function_number;
+ u8 res_source_index;
+ u16 res_source_offset;
+ u16 res_source_label_offset;
+ u16 vendor_offset;
+ u16 vendor_length;
+ /*
+ * Optional fields follow immediately:
+ * 1) Resource Source String
+ * 2) Resource Source Label String
+ * 3) Vendor Data bytes
+ */
+};
+
+#define AML_RESOURCE_PIN_GROUP_FUNCTION_REVISION 1 /* ACPI 6.2 */
+
+struct aml_resource_pin_group_config {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 revision_id;
+ u16 flags;
+ u8 pin_config_type;
+ u32 pin_config_value;
+ u8 res_source_index;
+ u16 res_source_offset;
+ u16 res_source_label_offset;
+ u16 vendor_offset;
+ u16 vendor_length;
+ /*
+ * Optional fields follow immediately:
+ * 1) Resource Source String
+ * 2) Resource Source Label String
+ * 3) Vendor Data bytes
+ */
+};
+
+#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 {
+ /* Descriptor headers */
+
+ u8 descriptor_type;
+ struct aml_resource_small_header small_header;
+ struct aml_resource_large_header large_header;
+
+ /* Small resource descriptors */
+
+ struct aml_resource_irq irq;
+ struct aml_resource_dma dma;
+ struct aml_resource_start_dependent start_dpf;
+ struct aml_resource_end_dependent end_dpf;
+ struct aml_resource_io io;
+ struct aml_resource_fixed_io fixed_io;
+ struct aml_resource_fixed_dma fixed_dma;
+ struct aml_resource_vendor_small vendor_small;
+ struct aml_resource_end_tag end_tag;
+
+ /* Large resource descriptors */
+
+ struct aml_resource_memory24 memory24;
+ struct aml_resource_generic_register generic_reg;
+ struct aml_resource_vendor_large vendor_large;
+ struct aml_resource_memory32 memory32;
+ struct aml_resource_fixed_memory32 fixed_memory32;
+ struct aml_resource_address16 address16;
+ struct aml_resource_address32 address32;
+ struct aml_resource_address64 address64;
+ struct aml_resource_extended_address64 ext_address64;
+ struct aml_resource_extended_irq extended_irq;
+ struct aml_resource_gpio gpio;
+ struct aml_resource_i2c_serialbus i2c_serial_bus;
+ struct aml_resource_spi_serialbus spi_serial_bus;
+ struct aml_resource_uart_serialbus uart_serial_bus;
+ struct aml_resource_csi2_serialbus csi2_serial_bus;
+ struct aml_resource_common_serialbus common_serial_bus;
+ struct aml_resource_pin_function pin_function;
+ struct aml_resource_pin_config pin_config;
+ 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;
+
+ /* Utility overlays */
+
+ struct aml_resource_address address;
+ u32 dword_item;
+ u16 word_item;
+ u8 byte_item;
+};
+
+/* Interfaces used by both the disassembler and compiler */
+
+void
+mp_save_gpio_info(union acpi_parse_object *op,
+ union aml_resource *resource,
+ u32 pin_count, u16 *pin_list, char *device_name);
+
+void
+mp_save_serial_info(union acpi_parse_object *op,
+ union aml_resource *resource, char *device_name);
+
+char *mp_get_hid_from_parse_tree(struct acpi_namespace_node *hid_node);
+
+char *mp_get_hid_via_namestring(char *device_name);
+
+char *mp_get_connection_info(union acpi_parse_object *op,
+ u32 pin_index,
+ struct acpi_namespace_node **target_node,
+ char **target_name);
+
+char *mp_get_parent_device_hid(union acpi_parse_object *op,
+ struct acpi_namespace_node **target_node,
+ char **parent_device_name);
+
+char *mp_get_ddn_value(char *device_name);
+
+char *mp_get_hid_value(struct acpi_namespace_node *device_node);
+
+#endif
diff --git a/drivers/acpi/acpica/dbcmds.c b/drivers/acpi/acpica/dbcmds.c
new file mode 100644
index 000000000..9eb68e075
--- /dev/null
+++ b/drivers/acpi/acpica/dbcmds.c
@@ -0,0 +1,1146 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbcmds - Miscellaneous debug commands and output routines
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acdebug.h"
+#include "acnamesp.h"
+#include "acresrc.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbcmds")
+
+/* Local prototypes */
+static void
+acpi_dm_compare_aml_resources(u8 *aml1_buffer,
+ acpi_rsdesc_size aml1_buffer_length,
+ u8 *aml2_buffer,
+ acpi_rsdesc_size aml2_buffer_length);
+
+static acpi_status
+acpi_dm_test_resource_conversion(struct acpi_namespace_node *node, char *name);
+
+static acpi_status
+acpi_db_resource_callback(struct acpi_resource *resource, void *context);
+
+static acpi_status
+acpi_db_device_resources(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value);
+
+static void acpi_db_do_one_sleep_state(u8 sleep_state);
+
+static char *acpi_db_trace_method_name = NULL;
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_convert_to_node
+ *
+ * PARAMETERS: in_string - String to convert
+ *
+ * RETURN: Pointer to a NS node
+ *
+ * DESCRIPTION: Convert a string to a valid NS pointer. Handles numeric or
+ * alphanumeric strings.
+ *
+ ******************************************************************************/
+
+struct acpi_namespace_node *acpi_db_convert_to_node(char *in_string)
+{
+ struct acpi_namespace_node *node;
+ acpi_size address;
+
+ if ((*in_string >= 0x30) && (*in_string <= 0x39)) {
+
+ /* Numeric argument, convert */
+
+ address = strtoul(in_string, NULL, 16);
+ node = ACPI_TO_POINTER(address);
+ if (!acpi_os_readable(node, sizeof(struct acpi_namespace_node))) {
+ acpi_os_printf("Address %p is invalid", node);
+ return (NULL);
+ }
+
+ /* Make sure pointer is valid NS node */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) {
+ acpi_os_printf
+ ("Address %p is not a valid namespace node [%s]\n",
+ node, acpi_ut_get_descriptor_name(node));
+ return (NULL);
+ }
+ } else {
+ /*
+ * Alpha argument: The parameter is a name string that must be
+ * resolved to a Namespace object.
+ */
+ node = acpi_db_local_ns_lookup(in_string);
+ if (!node) {
+ acpi_os_printf
+ ("Could not find [%s] in namespace, defaulting to root node\n",
+ in_string);
+ node = acpi_gbl_root_node;
+ }
+ }
+
+ return (node);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_sleep
+ *
+ * PARAMETERS: object_arg - Desired sleep state (0-5). NULL means
+ * invoke all possible sleep states.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Simulate sleep/wake sequences
+ *
+ ******************************************************************************/
+
+acpi_status acpi_db_sleep(char *object_arg)
+{
+ u8 sleep_state;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(acpi_db_sleep);
+
+ /* Null input (no arguments) means to invoke all sleep states */
+
+ if (!object_arg) {
+ acpi_os_printf("Invoking all possible sleep states, 0-%d\n",
+ ACPI_S_STATES_MAX);
+
+ for (i = 0; i <= ACPI_S_STATES_MAX; i++) {
+ acpi_db_do_one_sleep_state((u8)i);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Convert argument to binary and invoke the sleep state */
+
+ sleep_state = (u8)strtoul(object_arg, NULL, 0);
+ acpi_db_do_one_sleep_state(sleep_state);
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_do_one_sleep_state
+ *
+ * PARAMETERS: sleep_state - Desired sleep state (0-5)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Simulate a sleep/wake sequence
+ *
+ ******************************************************************************/
+
+static void acpi_db_do_one_sleep_state(u8 sleep_state)
+{
+ acpi_status status;
+ u8 sleep_type_a;
+ u8 sleep_type_b;
+
+ /* Validate parameter */
+
+ if (sleep_state > ACPI_S_STATES_MAX) {
+ acpi_os_printf("Sleep state %d out of range (%d max)\n",
+ sleep_state, ACPI_S_STATES_MAX);
+ return;
+ }
+
+ acpi_os_printf("\n---- Invoking sleep state S%d (%s):\n",
+ sleep_state, acpi_gbl_sleep_state_names[sleep_state]);
+
+ /* Get the values for the sleep type registers (for display only) */
+
+ status =
+ acpi_get_sleep_type_data(sleep_state, &sleep_type_a, &sleep_type_b);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not evaluate [%s] method, %s\n",
+ acpi_gbl_sleep_state_names[sleep_state],
+ acpi_format_exception(status));
+ return;
+ }
+
+ acpi_os_printf
+ ("Register values for sleep state S%d: Sleep-A: %.2X, Sleep-B: %.2X\n",
+ sleep_state, sleep_type_a, sleep_type_b);
+
+ /* Invoke the various sleep/wake interfaces */
+
+ acpi_os_printf("**** Sleep: Prepare to sleep (S%d) ****\n",
+ sleep_state);
+ status = acpi_enter_sleep_state_prep(sleep_state);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ acpi_os_printf("**** Sleep: Going to sleep (S%d) ****\n", sleep_state);
+ status = acpi_enter_sleep_state(sleep_state);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ acpi_os_printf("**** Wake: Prepare to return from sleep (S%d) ****\n",
+ sleep_state);
+ status = acpi_leave_sleep_state_prep(sleep_state);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ acpi_os_printf("**** Wake: Return from sleep (S%d) ****\n",
+ sleep_state);
+ status = acpi_leave_sleep_state(sleep_state);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ return;
+
+error_exit:
+ ACPI_EXCEPTION((AE_INFO, status, "During invocation of sleep state S%d",
+ sleep_state));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_locks
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display information about internal mutexes.
+ *
+ ******************************************************************************/
+
+void acpi_db_display_locks(void)
+{
+ u32 i;
+
+ for (i = 0; i < ACPI_MAX_MUTEX; i++) {
+ acpi_os_printf("%26s : %s\n", acpi_ut_get_mutex_name(i),
+ acpi_gbl_mutex_info[i].thread_id ==
+ ACPI_MUTEX_NOT_ACQUIRED ? "Locked" : "Unlocked");
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_table_info
+ *
+ * PARAMETERS: table_arg - Name of table to be displayed
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display information about loaded tables. Current
+ * implementation displays all loaded tables.
+ *
+ ******************************************************************************/
+
+void acpi_db_display_table_info(char *table_arg)
+{
+ u32 i;
+ struct acpi_table_desc *table_desc;
+ acpi_status status;
+
+ /* Header */
+
+ acpi_os_printf("Idx ID Status Type "
+ "TableHeader (Sig, Address, Length, Misc)\n");
+
+ /* Walk the entire root table list */
+
+ for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) {
+ table_desc = &acpi_gbl_root_table_list.tables[i];
+
+ /* Index and Table ID */
+
+ acpi_os_printf("%3u %.2u ", i, table_desc->owner_id);
+
+ /* Decode the table flags */
+
+ if (!(table_desc->flags & ACPI_TABLE_IS_LOADED)) {
+ acpi_os_printf("NotLoaded ");
+ } else {
+ acpi_os_printf(" Loaded ");
+ }
+
+ switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) {
+ case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+
+ acpi_os_printf("External/virtual ");
+ break;
+
+ case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+ acpi_os_printf("Internal/physical ");
+ break;
+
+ case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+
+ acpi_os_printf("Internal/virtual ");
+ break;
+
+ default:
+
+ acpi_os_printf("INVALID TYPE ");
+ break;
+ }
+
+ /* Make sure that the table is mapped */
+
+ status = acpi_tb_validate_table(table_desc);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+
+ /* Dump the table header */
+
+ if (table_desc->pointer) {
+ acpi_tb_print_table_header(table_desc->address,
+ table_desc->pointer);
+ } else {
+ /* If the pointer is null, the table has been unloaded */
+
+ ACPI_INFO(("%4.4s - Table has been unloaded",
+ table_desc->signature.ascii));
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_unload_acpi_table
+ *
+ * PARAMETERS: object_name - Namespace pathname for an object that
+ * is owned by the table to be unloaded
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Unload an ACPI table, via any namespace node that is owned
+ * by the table.
+ *
+ ******************************************************************************/
+
+void acpi_db_unload_acpi_table(char *object_name)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ /* Translate name to an Named object */
+
+ node = acpi_db_convert_to_node(object_name);
+ if (!node) {
+ return;
+ }
+
+ status = acpi_unload_parent_table(ACPI_CAST_PTR(acpi_handle, node));
+ if (ACPI_SUCCESS(status)) {
+ acpi_os_printf("Parent of [%s] (%p) unloaded and uninstalled\n",
+ object_name, node);
+ } else {
+ acpi_os_printf("%s, while unloading parent table of [%s]\n",
+ acpi_format_exception(status), object_name);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_send_notify
+ *
+ * PARAMETERS: name - Name of ACPI object where to send notify
+ * value - Value of the notify to send.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Send an ACPI notification. The value specified is sent to the
+ * named object as an ACPI notify.
+ *
+ ******************************************************************************/
+
+void acpi_db_send_notify(char *name, u32 value)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ /* Translate name to an Named object */
+
+ node = acpi_db_convert_to_node(name);
+ if (!node) {
+ return;
+ }
+
+ /* Dispatch the notify if legal */
+
+ if (acpi_ev_is_notify_object(node)) {
+ status = acpi_ev_queue_notify_request(node, value);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not queue notify\n");
+ }
+ } else {
+ acpi_os_printf("Named object [%4.4s] Type %s, "
+ "must be Device/Thermal/Processor type\n",
+ acpi_ut_get_node_name(node),
+ acpi_ut_get_type_name(node->type));
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_interfaces
+ *
+ * PARAMETERS: action_arg - Null, "install", or "remove"
+ * interface_name_arg - Name for install/remove options
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display or modify the global _OSI interface list
+ *
+ ******************************************************************************/
+
+void acpi_db_display_interfaces(char *action_arg, char *interface_name_arg)
+{
+ struct acpi_interface_info *next_interface;
+ char *sub_string;
+ acpi_status status;
+
+ /* If no arguments, just display current interface list */
+
+ if (!action_arg) {
+ (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex,
+ ACPI_WAIT_FOREVER);
+
+ next_interface = acpi_gbl_supported_interfaces;
+ while (next_interface) {
+ if (!(next_interface->flags & ACPI_OSI_INVALID)) {
+ acpi_os_printf("%s\n", next_interface->name);
+ }
+
+ next_interface = next_interface->next;
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return;
+ }
+
+ /* If action_arg exists, so must interface_name_arg */
+
+ if (!interface_name_arg) {
+ acpi_os_printf("Missing Interface Name argument\n");
+ return;
+ }
+
+ /* Uppercase the action for match below */
+
+ acpi_ut_strupr(action_arg);
+
+ /* install - install an interface */
+
+ sub_string = strstr("INSTALL", action_arg);
+ if (sub_string) {
+ status = acpi_install_interface(interface_name_arg);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("%s, while installing \"%s\"\n",
+ acpi_format_exception(status),
+ interface_name_arg);
+ }
+ return;
+ }
+
+ /* remove - remove an interface */
+
+ sub_string = strstr("REMOVE", action_arg);
+ if (sub_string) {
+ status = acpi_remove_interface(interface_name_arg);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("%s, while removing \"%s\"\n",
+ acpi_format_exception(status),
+ interface_name_arg);
+ }
+ return;
+ }
+
+ /* Invalid action_arg */
+
+ acpi_os_printf("Invalid action argument: %s\n", action_arg);
+ return;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_template
+ *
+ * PARAMETERS: buffer_arg - Buffer name or address
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump a buffer that contains a resource template
+ *
+ ******************************************************************************/
+
+void acpi_db_display_template(char *buffer_arg)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+ struct acpi_buffer return_buffer;
+
+ /* Translate buffer_arg to an Named object */
+
+ node = acpi_db_convert_to_node(buffer_arg);
+ if (!node || (node == acpi_gbl_root_node)) {
+ acpi_os_printf("Invalid argument: %s\n", buffer_arg);
+ return;
+ }
+
+ /* We must have a buffer object */
+
+ if (node->type != ACPI_TYPE_BUFFER) {
+ acpi_os_printf
+ ("Not a Buffer object, cannot be a template: %s\n",
+ buffer_arg);
+ return;
+ }
+
+ return_buffer.length = ACPI_DEBUG_BUFFER_SIZE;
+ return_buffer.pointer = acpi_gbl_db_buffer;
+
+ /* Attempt to convert the raw buffer to a resource list */
+
+ status = acpi_rs_create_resource_list(node->object, &return_buffer);
+
+ acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT);
+ acpi_dbg_level |= ACPI_LV_RESOURCES;
+
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf
+ ("Could not convert Buffer to a resource list: %s, %s\n",
+ buffer_arg, acpi_format_exception(status));
+ goto dump_buffer;
+ }
+
+ /* Now we can dump the resource list */
+
+ acpi_rs_dump_resource_list(ACPI_CAST_PTR(struct acpi_resource,
+ return_buffer.pointer));
+
+dump_buffer:
+ acpi_os_printf("\nRaw data buffer:\n");
+ acpi_ut_debug_dump_buffer((u8 *)node->object->buffer.pointer,
+ node->object->buffer.length,
+ DB_BYTE_DISPLAY, ACPI_UINT32_MAX);
+
+ acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+ return;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_dm_compare_aml_resources
+ *
+ * PARAMETERS: aml1_buffer - Contains first resource list
+ * aml1_buffer_length - Length of first resource list
+ * aml2_buffer - Contains second resource list
+ * aml2_buffer_length - Length of second resource list
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Compare two AML resource lists, descriptor by descriptor (in
+ * order to isolate a miscompare to an individual resource)
+ *
+ ******************************************************************************/
+
+static void
+acpi_dm_compare_aml_resources(u8 *aml1_buffer,
+ acpi_rsdesc_size aml1_buffer_length,
+ u8 *aml2_buffer,
+ acpi_rsdesc_size aml2_buffer_length)
+{
+ u8 *aml1;
+ u8 *aml2;
+ u8 *aml1_end;
+ u8 *aml2_end;
+ acpi_rsdesc_size aml1_length;
+ acpi_rsdesc_size aml2_length;
+ acpi_rsdesc_size offset = 0;
+ u8 resource_type;
+ u32 count = 0;
+ u32 i;
+
+ /* Compare overall buffer sizes (may be different due to size rounding) */
+
+ if (aml1_buffer_length != aml2_buffer_length) {
+ acpi_os_printf("**** Buffer length mismatch in converted "
+ "AML: Original %X, New %X ****\n",
+ aml1_buffer_length, aml2_buffer_length);
+ }
+
+ aml1 = aml1_buffer;
+ aml2 = aml2_buffer;
+ aml1_end = aml1_buffer + aml1_buffer_length;
+ aml2_end = aml2_buffer + aml2_buffer_length;
+
+ /* Walk the descriptor lists, comparing each descriptor */
+
+ while ((aml1 < aml1_end) && (aml2 < aml2_end)) {
+
+ /* Get the lengths of each descriptor */
+
+ aml1_length = acpi_ut_get_descriptor_length(aml1);
+ aml2_length = acpi_ut_get_descriptor_length(aml2);
+ resource_type = acpi_ut_get_resource_type(aml1);
+
+ /* Check for descriptor length match */
+
+ if (aml1_length != aml2_length) {
+ acpi_os_printf
+ ("**** Length mismatch in descriptor [%.2X] type %2.2X, "
+ "Offset %8.8X Len1 %X, Len2 %X ****\n", count,
+ resource_type, offset, aml1_length, aml2_length);
+ }
+
+ /* Check for descriptor byte match */
+
+ else if (memcmp(aml1, aml2, aml1_length)) {
+ acpi_os_printf
+ ("**** Data mismatch in descriptor [%.2X] type %2.2X, "
+ "Offset %8.8X ****\n", count, resource_type,
+ offset);
+
+ for (i = 0; i < aml1_length; i++) {
+ if (aml1[i] != aml2[i]) {
+ acpi_os_printf
+ ("Mismatch at byte offset %.2X: is %2.2X, "
+ "should be %2.2X\n", i, aml2[i],
+ aml1[i]);
+ }
+ }
+ }
+
+ /* Exit on end_tag descriptor */
+
+ if (resource_type == ACPI_RESOURCE_NAME_END_TAG) {
+ return;
+ }
+
+ /* Point to next descriptor in each buffer */
+
+ count++;
+ offset += aml1_length;
+ aml1 += aml1_length;
+ aml2 += aml2_length;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_dm_test_resource_conversion
+ *
+ * PARAMETERS: node - Parent device node
+ * name - resource method name (_CRS)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Compare the original AML with a conversion of the AML to
+ * internal resource list, then back to AML.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_dm_test_resource_conversion(struct acpi_namespace_node *node, char *name)
+{
+ acpi_status status;
+ struct acpi_buffer return_buffer;
+ struct acpi_buffer resource_buffer;
+ struct acpi_buffer new_aml;
+ union acpi_object *original_aml;
+
+ acpi_os_printf("Resource Conversion Comparison:\n");
+
+ new_aml.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ return_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ resource_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+
+ /* Get the original _CRS AML resource template */
+
+ status = acpi_evaluate_object(node, name, NULL, &return_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not obtain %s: %s\n",
+ name, acpi_format_exception(status));
+ return (status);
+ }
+
+ /* Get the AML resource template, converted to internal resource structs */
+
+ status = acpi_get_current_resources(node, &resource_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("AcpiGetCurrentResources failed: %s\n",
+ acpi_format_exception(status));
+ goto exit1;
+ }
+
+ /* Convert internal resource list to external AML resource template */
+
+ status = acpi_rs_create_aml_resources(&resource_buffer, &new_aml);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("AcpiRsCreateAmlResources failed: %s\n",
+ acpi_format_exception(status));
+ goto exit2;
+ }
+
+ /* Compare original AML to the newly created AML resource list */
+
+ original_aml = return_buffer.pointer;
+
+ acpi_dm_compare_aml_resources(original_aml->buffer.pointer,
+ (acpi_rsdesc_size)original_aml->buffer.
+ length, new_aml.pointer,
+ (acpi_rsdesc_size)new_aml.length);
+
+ /* Cleanup and exit */
+
+ ACPI_FREE(new_aml.pointer);
+exit2:
+ ACPI_FREE(resource_buffer.pointer);
+exit1:
+ ACPI_FREE(return_buffer.pointer);
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_resource_callback
+ *
+ * PARAMETERS: acpi_walk_resource_callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Simple callback to exercise acpi_walk_resources and
+ * acpi_walk_resource_buffer.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_resource_callback(struct acpi_resource *resource, void *context)
+{
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_device_resources
+ *
+ * PARAMETERS: acpi_walk_callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Display the _PRT/_CRS/_PRS resources for a device object.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_device_resources(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value)
+{
+ struct acpi_namespace_node *node;
+ struct acpi_namespace_node *prt_node = NULL;
+ struct acpi_namespace_node *crs_node = NULL;
+ struct acpi_namespace_node *prs_node = NULL;
+ struct acpi_namespace_node *aei_node = NULL;
+ char *parent_path;
+ struct acpi_buffer return_buffer;
+ acpi_status status;
+
+ node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle);
+ parent_path = acpi_ns_get_normalized_pathname(node, TRUE);
+ if (!parent_path) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Get handles to the resource methods for this device */
+
+ (void)acpi_get_handle(node, METHOD_NAME__PRT,
+ ACPI_CAST_PTR(acpi_handle, &prt_node));
+ (void)acpi_get_handle(node, METHOD_NAME__CRS,
+ ACPI_CAST_PTR(acpi_handle, &crs_node));
+ (void)acpi_get_handle(node, METHOD_NAME__PRS,
+ ACPI_CAST_PTR(acpi_handle, &prs_node));
+ (void)acpi_get_handle(node, METHOD_NAME__AEI,
+ ACPI_CAST_PTR(acpi_handle, &aei_node));
+
+ if (!prt_node && !crs_node && !prs_node && !aei_node) {
+ goto cleanup; /* Nothing to do */
+ }
+
+ acpi_os_printf("\nDevice: %s\n", parent_path);
+
+ /* Prepare for a return object of arbitrary size */
+
+ return_buffer.pointer = acpi_gbl_db_buffer;
+ return_buffer.length = ACPI_DEBUG_BUFFER_SIZE;
+
+ /* _PRT */
+
+ if (prt_node) {
+ acpi_os_printf("Evaluating _PRT\n");
+
+ status =
+ acpi_evaluate_object(prt_node, NULL, NULL, &return_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not evaluate _PRT: %s\n",
+ acpi_format_exception(status));
+ goto get_crs;
+ }
+
+ return_buffer.pointer = acpi_gbl_db_buffer;
+ return_buffer.length = ACPI_DEBUG_BUFFER_SIZE;
+
+ status = acpi_get_irq_routing_table(node, &return_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("GetIrqRoutingTable failed: %s\n",
+ acpi_format_exception(status));
+ goto get_crs;
+ }
+
+ acpi_rs_dump_irq_list(ACPI_CAST_PTR(u8, acpi_gbl_db_buffer));
+ }
+
+ /* _CRS */
+
+get_crs:
+ if (crs_node) {
+ acpi_os_printf("Evaluating _CRS\n");
+
+ return_buffer.pointer = acpi_gbl_db_buffer;
+ return_buffer.length = ACPI_DEBUG_BUFFER_SIZE;
+
+ status =
+ acpi_evaluate_object(crs_node, NULL, NULL, &return_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not evaluate _CRS: %s\n",
+ acpi_format_exception(status));
+ goto get_prs;
+ }
+
+ /* This code exercises the acpi_walk_resources interface */
+
+ status = acpi_walk_resources(node, METHOD_NAME__CRS,
+ acpi_db_resource_callback, NULL);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("AcpiWalkResources failed: %s\n",
+ acpi_format_exception(status));
+ goto get_prs;
+ }
+
+ /* Get the _CRS resource list (test ALLOCATE buffer) */
+
+ return_buffer.pointer = NULL;
+ return_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+
+ status = acpi_get_current_resources(node, &return_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("AcpiGetCurrentResources failed: %s\n",
+ acpi_format_exception(status));
+ goto get_prs;
+ }
+
+ /* This code exercises the acpi_walk_resource_buffer interface */
+
+ status = acpi_walk_resource_buffer(&return_buffer,
+ acpi_db_resource_callback,
+ NULL);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("AcpiWalkResourceBuffer failed: %s\n",
+ acpi_format_exception(status));
+ goto end_crs;
+ }
+
+ /* Dump the _CRS resource list */
+
+ acpi_rs_dump_resource_list(ACPI_CAST_PTR(struct acpi_resource,
+ return_buffer.
+ pointer));
+
+ /*
+ * Perform comparison of original AML to newly created AML. This
+ * tests both the AML->Resource conversion and the Resource->AML
+ * conversion.
+ */
+ (void)acpi_dm_test_resource_conversion(node, METHOD_NAME__CRS);
+
+ /* Execute _SRS with the resource list */
+
+ acpi_os_printf("Evaluating _SRS\n");
+
+ status = acpi_set_current_resources(node, &return_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("AcpiSetCurrentResources failed: %s\n",
+ acpi_format_exception(status));
+ goto end_crs;
+ }
+
+end_crs:
+ ACPI_FREE(return_buffer.pointer);
+ }
+
+ /* _PRS */
+
+get_prs:
+ if (prs_node) {
+ acpi_os_printf("Evaluating _PRS\n");
+
+ return_buffer.pointer = acpi_gbl_db_buffer;
+ return_buffer.length = ACPI_DEBUG_BUFFER_SIZE;
+
+ status =
+ acpi_evaluate_object(prs_node, NULL, NULL, &return_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not evaluate _PRS: %s\n",
+ acpi_format_exception(status));
+ goto get_aei;
+ }
+
+ return_buffer.pointer = acpi_gbl_db_buffer;
+ return_buffer.length = ACPI_DEBUG_BUFFER_SIZE;
+
+ status = acpi_get_possible_resources(node, &return_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("AcpiGetPossibleResources failed: %s\n",
+ acpi_format_exception(status));
+ goto get_aei;
+ }
+
+ acpi_rs_dump_resource_list(ACPI_CAST_PTR
+ (struct acpi_resource,
+ acpi_gbl_db_buffer));
+ }
+
+ /* _AEI */
+
+get_aei:
+ if (aei_node) {
+ acpi_os_printf("Evaluating _AEI\n");
+
+ return_buffer.pointer = acpi_gbl_db_buffer;
+ return_buffer.length = ACPI_DEBUG_BUFFER_SIZE;
+
+ status =
+ acpi_evaluate_object(aei_node, NULL, NULL, &return_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not evaluate _AEI: %s\n",
+ acpi_format_exception(status));
+ goto cleanup;
+ }
+
+ return_buffer.pointer = acpi_gbl_db_buffer;
+ return_buffer.length = ACPI_DEBUG_BUFFER_SIZE;
+
+ status = acpi_get_event_resources(node, &return_buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("AcpiGetEventResources failed: %s\n",
+ acpi_format_exception(status));
+ goto cleanup;
+ }
+
+ acpi_rs_dump_resource_list(ACPI_CAST_PTR
+ (struct acpi_resource,
+ acpi_gbl_db_buffer));
+ }
+
+cleanup:
+ ACPI_FREE(parent_path);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_resources
+ *
+ * PARAMETERS: object_arg - String object name or object pointer.
+ * NULL or "*" means "display resources for
+ * all devices"
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display the resource objects associated with a device.
+ *
+ ******************************************************************************/
+
+void acpi_db_display_resources(char *object_arg)
+{
+ struct acpi_namespace_node *node;
+
+ acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT);
+ acpi_dbg_level |= ACPI_LV_RESOURCES;
+
+ /* Asterisk means "display resources for all devices" */
+
+ if (!object_arg || (!strcmp(object_arg, "*"))) {
+ (void)acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ acpi_db_device_resources, NULL, NULL,
+ NULL);
+ } else {
+ /* Convert string to object pointer */
+
+ node = acpi_db_convert_to_node(object_arg);
+ if (node) {
+ if (node->type != ACPI_TYPE_DEVICE) {
+ acpi_os_printf
+ ("%4.4s: Name is not a device object (%s)\n",
+ node->name.ascii,
+ acpi_ut_get_type_name(node->type));
+ } else {
+ (void)acpi_db_device_resources(node, 0, NULL,
+ NULL);
+ }
+ }
+ }
+
+ acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+}
+
+#if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_generate_gpe
+ *
+ * PARAMETERS: gpe_arg - Raw GPE number, ascii string
+ * block_arg - GPE block number, ascii string
+ * 0 or 1 for FADT GPE blocks
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Simulate firing of a GPE
+ *
+ ******************************************************************************/
+
+void acpi_db_generate_gpe(char *gpe_arg, char *block_arg)
+{
+ u32 block_number = 0;
+ u32 gpe_number;
+ struct acpi_gpe_event_info *gpe_event_info;
+
+ gpe_number = strtoul(gpe_arg, NULL, 0);
+
+ /*
+ * If no block arg, or block arg == 0 or 1, use the FADT-defined
+ * GPE blocks.
+ */
+ if (block_arg) {
+ block_number = strtoul(block_arg, NULL, 0);
+ if (block_number == 1) {
+ block_number = 0;
+ }
+ }
+
+ gpe_event_info =
+ acpi_ev_get_gpe_event_info(ACPI_TO_POINTER(block_number),
+ gpe_number);
+ if (!gpe_event_info) {
+ acpi_os_printf("Invalid GPE\n");
+ return;
+ }
+
+ (void)acpi_ev_gpe_dispatch(NULL, gpe_event_info, gpe_number);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_generate_sci
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Simulate an SCI -- just call the SCI dispatch.
+ *
+ ******************************************************************************/
+
+void acpi_db_generate_sci(void)
+{
+ acpi_ev_sci_dispatch();
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_trace
+ *
+ * PARAMETERS: enable_arg - ENABLE/AML to enable tracer
+ * DISABLE to disable tracer
+ * method_arg - Method to trace
+ * once_arg - Whether trace once
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Control method tracing facility
+ *
+ ******************************************************************************/
+
+void acpi_db_trace(char *enable_arg, char *method_arg, char *once_arg)
+{
+ u32 debug_level = 0;
+ u32 debug_layer = 0;
+ u32 flags = 0;
+
+ acpi_ut_strupr(enable_arg);
+ acpi_ut_strupr(once_arg);
+
+ if (method_arg) {
+ if (acpi_db_trace_method_name) {
+ ACPI_FREE(acpi_db_trace_method_name);
+ acpi_db_trace_method_name = NULL;
+ }
+
+ acpi_db_trace_method_name =
+ ACPI_ALLOCATE(strlen(method_arg) + 1);
+ if (!acpi_db_trace_method_name) {
+ acpi_os_printf("Failed to allocate method name (%s)\n",
+ method_arg);
+ return;
+ }
+
+ strcpy(acpi_db_trace_method_name, method_arg);
+ }
+
+ if (!strcmp(enable_arg, "ENABLE") ||
+ !strcmp(enable_arg, "METHOD") || !strcmp(enable_arg, "OPCODE")) {
+ if (!strcmp(enable_arg, "ENABLE")) {
+
+ /* Inherit current console settings */
+
+ debug_level = acpi_gbl_db_console_debug_level;
+ debug_layer = acpi_dbg_layer;
+ } else {
+ /* Restrict console output to trace points only */
+
+ debug_level = ACPI_LV_TRACE_POINT;
+ debug_layer = ACPI_EXECUTER;
+ }
+
+ flags = ACPI_TRACE_ENABLED;
+
+ if (!strcmp(enable_arg, "OPCODE")) {
+ flags |= ACPI_TRACE_OPCODE;
+ }
+
+ if (once_arg && !strcmp(once_arg, "ONCE")) {
+ flags |= ACPI_TRACE_ONESHOT;
+ }
+ }
+
+ (void)acpi_debug_trace(acpi_db_trace_method_name,
+ debug_level, debug_layer, flags);
+}
diff --git a/drivers/acpi/acpica/dbconvert.c b/drivers/acpi/acpica/dbconvert.c
new file mode 100644
index 000000000..2b84ac093
--- /dev/null
+++ b/drivers/acpi/acpica/dbconvert.c
@@ -0,0 +1,453 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbconvert - debugger miscellaneous conversion routines
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdebug.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbconvert")
+
+#define DB_DEFAULT_PKG_ELEMENTS 33
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_hex_char_to_value
+ *
+ * PARAMETERS: hex_char - Ascii Hex digit, 0-9|a-f|A-F
+ * return_value - Where the converted value is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert a single hex character to a 4-bit number (0-16).
+ *
+ ******************************************************************************/
+acpi_status acpi_db_hex_char_to_value(int hex_char, u8 *return_value)
+{
+ u8 value;
+
+ /* Digit must be ascii [0-9a-fA-F] */
+
+ if (!isxdigit(hex_char)) {
+ return (AE_BAD_HEX_CONSTANT);
+ }
+
+ if (hex_char <= 0x39) {
+ value = (u8)(hex_char - 0x30);
+ } else {
+ value = (u8)(toupper(hex_char) - 0x37);
+ }
+
+ *return_value = value;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_hex_byte_to_binary
+ *
+ * PARAMETERS: hex_byte - Double hex digit (0x00 - 0xFF) in format:
+ * hi_byte then lo_byte.
+ * return_value - Where the converted value is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert two hex characters to an 8 bit number (0 - 255).
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_db_hex_byte_to_binary(char *hex_byte, u8 *return_value)
+{
+ u8 local0;
+ u8 local1;
+ acpi_status status;
+
+ /* High byte */
+
+ status = acpi_db_hex_char_to_value(hex_byte[0], &local0);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Low byte */
+
+ status = acpi_db_hex_char_to_value(hex_byte[1], &local1);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ *return_value = (u8)((local0 << 4) | local1);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_convert_to_buffer
+ *
+ * PARAMETERS: string - Input string to be converted
+ * object - Where the buffer object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert a string to a buffer object. String is treated a list
+ * of buffer elements, each separated by a space or comma.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_convert_to_buffer(char *string, union acpi_object *object)
+{
+ u32 i;
+ u32 j;
+ u32 length;
+ u8 *buffer;
+ acpi_status status;
+
+ /* Skip all preceding white space */
+
+ acpi_ut_remove_whitespace(&string);
+
+ /* Generate the final buffer length */
+
+ for (i = 0, length = 0; string[i];) {
+ i += 2;
+ length++;
+
+ while (string[i] && ((string[i] == ',') || (string[i] == ' '))) {
+ i++;
+ }
+ }
+
+ buffer = ACPI_ALLOCATE(length);
+ if (!buffer) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Convert the command line bytes to the buffer */
+
+ for (i = 0, j = 0; string[i];) {
+ status = acpi_db_hex_byte_to_binary(&string[i], &buffer[j]);
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(buffer);
+ return (status);
+ }
+
+ j++;
+ i += 2;
+ while (string[i] && ((string[i] == ',') || (string[i] == ' '))) {
+ i++;
+ }
+ }
+
+ object->type = ACPI_TYPE_BUFFER;
+ object->buffer.pointer = buffer;
+ object->buffer.length = length;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_convert_to_package
+ *
+ * PARAMETERS: string - Input string to be converted
+ * object - Where the package object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert a string to a package object. Handles nested packages
+ * via recursion with acpi_db_convert_to_object.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_db_convert_to_package(char *string, union acpi_object *object)
+{
+ char *this;
+ char *next;
+ u32 i;
+ acpi_object_type type;
+ union acpi_object *elements;
+ acpi_status status;
+
+ elements =
+ ACPI_ALLOCATE_ZEROED(DB_DEFAULT_PKG_ELEMENTS *
+ sizeof(union acpi_object));
+
+ this = string;
+ for (i = 0; i < (DB_DEFAULT_PKG_ELEMENTS - 1); i++) {
+ this = acpi_db_get_next_token(this, &next, &type);
+ if (!this) {
+ break;
+ }
+
+ /* Recursive call to convert each package element */
+
+ status = acpi_db_convert_to_object(type, this, &elements[i]);
+ if (ACPI_FAILURE(status)) {
+ acpi_db_delete_objects(i + 1, elements);
+ ACPI_FREE(elements);
+ return (status);
+ }
+
+ this = next;
+ }
+
+ object->type = ACPI_TYPE_PACKAGE;
+ object->package.count = i;
+ object->package.elements = elements;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_convert_to_object
+ *
+ * PARAMETERS: type - Object type as determined by parser
+ * string - Input string to be converted
+ * object - Where the new object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert a typed and tokenized string to a union acpi_object. Typing:
+ * 1) String objects were surrounded by quotes.
+ * 2) Buffer objects were surrounded by parentheses.
+ * 3) Package objects were surrounded by brackets "[]".
+ * 4) All standalone tokens are treated as integers.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_db_convert_to_object(acpi_object_type type,
+ char *string, union acpi_object *object)
+{
+ acpi_status status = AE_OK;
+
+ switch (type) {
+ case ACPI_TYPE_STRING:
+
+ object->type = ACPI_TYPE_STRING;
+ object->string.pointer = string;
+ object->string.length = (u32)strlen(string);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ status = acpi_db_convert_to_buffer(string, object);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ status = acpi_db_convert_to_package(string, object);
+ break;
+
+ default:
+
+ object->type = ACPI_TYPE_INTEGER;
+ status = acpi_ut_strtoul64(string, &object->integer.value);
+ break;
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_encode_pld_buffer
+ *
+ * PARAMETERS: pld_info - _PLD buffer struct (Using local struct)
+ *
+ * RETURN: Encode _PLD buffer suitable for return value from _PLD
+ *
+ * DESCRIPTION: Bit-packs a _PLD buffer struct. Used to test the _PLD macros
+ *
+ ******************************************************************************/
+
+u8 *acpi_db_encode_pld_buffer(struct acpi_pld_info *pld_info)
+{
+ u32 *buffer;
+ u32 dword;
+
+ buffer = ACPI_ALLOCATE_ZEROED(ACPI_PLD_BUFFER_SIZE);
+ if (!buffer) {
+ return (NULL);
+ }
+
+ /* First 32 bits */
+
+ dword = 0;
+ ACPI_PLD_SET_REVISION(&dword, pld_info->revision);
+ ACPI_PLD_SET_IGNORE_COLOR(&dword, pld_info->ignore_color);
+ ACPI_PLD_SET_RED(&dword, pld_info->red);
+ ACPI_PLD_SET_GREEN(&dword, pld_info->green);
+ ACPI_PLD_SET_BLUE(&dword, pld_info->blue);
+ ACPI_MOVE_32_TO_32(&buffer[0], &dword);
+
+ /* Second 32 bits */
+
+ dword = 0;
+ ACPI_PLD_SET_WIDTH(&dword, pld_info->width);
+ ACPI_PLD_SET_HEIGHT(&dword, pld_info->height);
+ ACPI_MOVE_32_TO_32(&buffer[1], &dword);
+
+ /* Third 32 bits */
+
+ dword = 0;
+ ACPI_PLD_SET_USER_VISIBLE(&dword, pld_info->user_visible);
+ ACPI_PLD_SET_DOCK(&dword, pld_info->dock);
+ ACPI_PLD_SET_LID(&dword, pld_info->lid);
+ ACPI_PLD_SET_PANEL(&dword, pld_info->panel);
+ ACPI_PLD_SET_VERTICAL(&dword, pld_info->vertical_position);
+ ACPI_PLD_SET_HORIZONTAL(&dword, pld_info->horizontal_position);
+ ACPI_PLD_SET_SHAPE(&dword, pld_info->shape);
+ ACPI_PLD_SET_ORIENTATION(&dword, pld_info->group_orientation);
+ ACPI_PLD_SET_TOKEN(&dword, pld_info->group_token);
+ ACPI_PLD_SET_POSITION(&dword, pld_info->group_position);
+ ACPI_PLD_SET_BAY(&dword, pld_info->bay);
+ ACPI_MOVE_32_TO_32(&buffer[2], &dword);
+
+ /* Fourth 32 bits */
+
+ dword = 0;
+ ACPI_PLD_SET_EJECTABLE(&dword, pld_info->ejectable);
+ ACPI_PLD_SET_OSPM_EJECT(&dword, pld_info->ospm_eject_required);
+ ACPI_PLD_SET_CABINET(&dword, pld_info->cabinet_number);
+ ACPI_PLD_SET_CARD_CAGE(&dword, pld_info->card_cage_number);
+ ACPI_PLD_SET_REFERENCE(&dword, pld_info->reference);
+ ACPI_PLD_SET_ROTATION(&dword, pld_info->rotation);
+ ACPI_PLD_SET_ORDER(&dword, pld_info->order);
+ ACPI_MOVE_32_TO_32(&buffer[3], &dword);
+
+ if (pld_info->revision >= 2) {
+
+ /* Fifth 32 bits */
+
+ dword = 0;
+ ACPI_PLD_SET_VERT_OFFSET(&dword, pld_info->vertical_offset);
+ ACPI_PLD_SET_HORIZ_OFFSET(&dword, pld_info->horizontal_offset);
+ ACPI_MOVE_32_TO_32(&buffer[4], &dword);
+ }
+
+ return (ACPI_CAST_PTR(u8, buffer));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_dump_pld_buffer
+ *
+ * PARAMETERS: obj_desc - Object returned from _PLD method
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Dumps formatted contents of a _PLD return buffer.
+ *
+ ******************************************************************************/
+
+#define ACPI_PLD_OUTPUT "%20s : %-6X\n"
+
+void acpi_db_dump_pld_buffer(union acpi_object *obj_desc)
+{
+ union acpi_object *buffer_desc;
+ struct acpi_pld_info *pld_info;
+ u8 *new_buffer;
+ acpi_status status;
+
+ /* Object must be of type Package with at least one Buffer element */
+
+ if (obj_desc->type != ACPI_TYPE_PACKAGE) {
+ return;
+ }
+
+ buffer_desc = &obj_desc->package.elements[0];
+ if (buffer_desc->type != ACPI_TYPE_BUFFER) {
+ return;
+ }
+
+ /* Convert _PLD buffer to local _PLD struct */
+
+ status = acpi_decode_pld_buffer(buffer_desc->buffer.pointer,
+ buffer_desc->buffer.length, &pld_info);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+
+ /* Encode local _PLD struct back to a _PLD buffer */
+
+ new_buffer = acpi_db_encode_pld_buffer(pld_info);
+ if (!new_buffer) {
+ goto exit;
+ }
+
+ /* The two bit-packed buffers should match */
+
+ if (memcmp(new_buffer, buffer_desc->buffer.pointer,
+ buffer_desc->buffer.length)) {
+ acpi_os_printf
+ ("Converted _PLD buffer does not compare. New:\n");
+
+ acpi_ut_dump_buffer(new_buffer,
+ buffer_desc->buffer.length, DB_BYTE_DISPLAY,
+ 0);
+ }
+
+ /* First 32-bit dword */
+
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Revision", pld_info->revision);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_IgnoreColor",
+ pld_info->ignore_color);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Red", pld_info->red);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Green", pld_info->green);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Blue", pld_info->blue);
+
+ /* Second 32-bit dword */
+
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Width", pld_info->width);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Height", pld_info->height);
+
+ /* Third 32-bit dword */
+
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_UserVisible",
+ pld_info->user_visible);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Dock", pld_info->dock);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Lid", pld_info->lid);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Panel", pld_info->panel);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_VerticalPosition",
+ pld_info->vertical_position);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_HorizontalPosition",
+ pld_info->horizontal_position);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Shape", pld_info->shape);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_GroupOrientation",
+ pld_info->group_orientation);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_GroupToken",
+ pld_info->group_token);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_GroupPosition",
+ pld_info->group_position);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Bay", pld_info->bay);
+
+ /* Fourth 32-bit dword */
+
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Ejectable", pld_info->ejectable);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_EjectRequired",
+ pld_info->ospm_eject_required);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_CabinetNumber",
+ pld_info->cabinet_number);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_CardCageNumber",
+ pld_info->card_cage_number);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Reference", pld_info->reference);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Rotation", pld_info->rotation);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Order", pld_info->order);
+
+ /* Fifth 32-bit dword */
+
+ if (buffer_desc->buffer.length > 16) {
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_VerticalOffset",
+ pld_info->vertical_offset);
+ acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_HorizontalOffset",
+ pld_info->horizontal_offset);
+ }
+
+ ACPI_FREE(new_buffer);
+exit:
+ ACPI_FREE(pld_info);
+}
diff --git a/drivers/acpi/acpica/dbdisply.c b/drivers/acpi/acpica/dbdisply.c
new file mode 100644
index 000000000..d41eb9e67
--- /dev/null
+++ b/drivers/acpi/acpica/dbdisply.c
@@ -0,0 +1,1096 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbdisply - debug display commands
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acnamesp.h"
+#include "acparser.h"
+#include "acinterp.h"
+#include "acevents.h"
+#include "acdebug.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbdisply")
+
+/* Local prototypes */
+static void acpi_db_dump_parser_descriptor(union acpi_parse_object *op);
+
+static void *acpi_db_get_pointer(void *target);
+
+static acpi_status
+acpi_db_display_non_root_handlers(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value);
+
+/*
+ * System handler information.
+ * Used for Handlers command, in acpi_db_display_handlers.
+ */
+#define ACPI_PREDEFINED_PREFIX "%25s (%.2X) : "
+#define ACPI_HANDLER_NAME_STRING "%30s : "
+#define ACPI_HANDLER_PRESENT_STRING "%-9s (%p)\n"
+#define ACPI_HANDLER_PRESENT_STRING2 "%-9s (%p)"
+#define ACPI_HANDLER_NOT_PRESENT_STRING "%-9s\n"
+
+/* All predefined Address Space IDs */
+
+static acpi_adr_space_type acpi_gbl_space_id_list[] = {
+ ACPI_ADR_SPACE_SYSTEM_MEMORY,
+ ACPI_ADR_SPACE_SYSTEM_IO,
+ ACPI_ADR_SPACE_PCI_CONFIG,
+ ACPI_ADR_SPACE_EC,
+ ACPI_ADR_SPACE_SMBUS,
+ ACPI_ADR_SPACE_CMOS,
+ ACPI_ADR_SPACE_PCI_BAR_TARGET,
+ ACPI_ADR_SPACE_IPMI,
+ ACPI_ADR_SPACE_GPIO,
+ ACPI_ADR_SPACE_GSBUS,
+ ACPI_ADR_SPACE_PLATFORM_COMM,
+ ACPI_ADR_SPACE_PLATFORM_RT,
+ ACPI_ADR_SPACE_DATA_TABLE,
+ ACPI_ADR_SPACE_FIXED_HARDWARE
+};
+
+/* Global handler information */
+
+typedef struct acpi_handler_info {
+ void *handler;
+ char *name;
+
+} acpi_handler_info;
+
+static struct acpi_handler_info acpi_gbl_handler_list[] = {
+ {&acpi_gbl_global_notify[0].handler, "System Notifications"},
+ {&acpi_gbl_global_notify[1].handler, "Device Notifications"},
+ {&acpi_gbl_table_handler, "ACPI Table Events"},
+ {&acpi_gbl_exception_handler, "Control Method Exceptions"},
+ {&acpi_gbl_interface_handler, "OSI Invocations"}
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_get_pointer
+ *
+ * PARAMETERS: target - Pointer to string to be converted
+ *
+ * RETURN: Converted pointer
+ *
+ * DESCRIPTION: Convert an ascii pointer value to a real value
+ *
+ ******************************************************************************/
+
+static void *acpi_db_get_pointer(void *target)
+{
+ void *obj_ptr;
+ acpi_size address;
+
+ address = strtoul(target, NULL, 16);
+ obj_ptr = ACPI_TO_POINTER(address);
+ return (obj_ptr);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_dump_parser_descriptor
+ *
+ * PARAMETERS: op - A parser Op descriptor
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display a formatted parser object
+ *
+ ******************************************************************************/
+
+static void acpi_db_dump_parser_descriptor(union acpi_parse_object *op)
+{
+ const struct acpi_opcode_info *info;
+
+ info = acpi_ps_get_opcode_info(op->common.aml_opcode);
+
+ acpi_os_printf("Parser Op Descriptor:\n");
+ acpi_os_printf("%20.20s : %4.4X\n", "Opcode", op->common.aml_opcode);
+
+ ACPI_DEBUG_ONLY_MEMBERS(acpi_os_printf("%20.20s : %s\n", "Opcode Name",
+ info->name));
+
+ acpi_os_printf("%20.20s : %p\n", "Value/ArgList", op->common.value.arg);
+ acpi_os_printf("%20.20s : %p\n", "Parent", op->common.parent);
+ acpi_os_printf("%20.20s : %p\n", "NextOp", op->common.next);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_decode_and_display_object
+ *
+ * PARAMETERS: target - String with object to be displayed. Names
+ * and hex pointers are supported.
+ * output_type - Byte, Word, Dword, or Qword (B|W|D|Q)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display a formatted ACPI object
+ *
+ ******************************************************************************/
+
+void acpi_db_decode_and_display_object(char *target, char *output_type)
+{
+ void *obj_ptr;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *obj_desc;
+ u32 display = DB_BYTE_DISPLAY;
+ char buffer[80];
+ struct acpi_buffer ret_buf;
+ acpi_status status;
+ u32 size;
+
+ if (!target) {
+ return;
+ }
+
+ /* Decode the output type */
+
+ if (output_type) {
+ acpi_ut_strupr(output_type);
+ if (output_type[0] == 'W') {
+ display = DB_WORD_DISPLAY;
+ } else if (output_type[0] == 'D') {
+ display = DB_DWORD_DISPLAY;
+ } else if (output_type[0] == 'Q') {
+ display = DB_QWORD_DISPLAY;
+ }
+ }
+
+ ret_buf.length = sizeof(buffer);
+ ret_buf.pointer = buffer;
+
+ /* Differentiate between a number and a name */
+
+ if ((target[0] >= 0x30) && (target[0] <= 0x39)) {
+ obj_ptr = acpi_db_get_pointer(target);
+ if (!acpi_os_readable(obj_ptr, 16)) {
+ acpi_os_printf
+ ("Address %p is invalid in this address space\n",
+ obj_ptr);
+ return;
+ }
+
+ /* Decode the object type */
+
+ switch (ACPI_GET_DESCRIPTOR_TYPE(obj_ptr)) {
+ case ACPI_DESC_TYPE_NAMED:
+
+ /* This is a namespace Node */
+
+ if (!acpi_os_readable
+ (obj_ptr, sizeof(struct acpi_namespace_node))) {
+ acpi_os_printf
+ ("Cannot read entire Named object at address %p\n",
+ obj_ptr);
+ return;
+ }
+
+ node = obj_ptr;
+ goto dump_node;
+
+ case ACPI_DESC_TYPE_OPERAND:
+
+ /* This is a ACPI OPERAND OBJECT */
+
+ if (!acpi_os_readable
+ (obj_ptr, sizeof(union acpi_operand_object))) {
+ acpi_os_printf
+ ("Cannot read entire ACPI object at address %p\n",
+ obj_ptr);
+ return;
+ }
+
+ acpi_ut_debug_dump_buffer(obj_ptr,
+ sizeof(union
+ acpi_operand_object),
+ display, ACPI_UINT32_MAX);
+ acpi_ex_dump_object_descriptor(obj_ptr, 1);
+ break;
+
+ case ACPI_DESC_TYPE_PARSER:
+
+ /* This is a Parser Op object */
+
+ if (!acpi_os_readable
+ (obj_ptr, sizeof(union acpi_parse_object))) {
+ acpi_os_printf
+ ("Cannot read entire Parser object at address %p\n",
+ obj_ptr);
+ return;
+ }
+
+ acpi_ut_debug_dump_buffer(obj_ptr,
+ sizeof(union
+ acpi_parse_object),
+ display, ACPI_UINT32_MAX);
+ acpi_db_dump_parser_descriptor((union acpi_parse_object
+ *)obj_ptr);
+ break;
+
+ default:
+
+ /* Is not a recognizable object */
+
+ acpi_os_printf
+ ("Not a known ACPI internal object, descriptor type %2.2X\n",
+ ACPI_GET_DESCRIPTOR_TYPE(obj_ptr));
+
+ size = 16;
+ if (acpi_os_readable(obj_ptr, 64)) {
+ size = 64;
+ }
+
+ /* Just dump some memory */
+
+ acpi_ut_debug_dump_buffer(obj_ptr, size, display,
+ ACPI_UINT32_MAX);
+ break;
+ }
+
+ return;
+ }
+
+ /* The parameter is a name string that must be resolved to a Named obj */
+
+ node = acpi_db_local_ns_lookup(target);
+ if (!node) {
+ return;
+ }
+
+dump_node:
+ /* Now dump the NS node */
+
+ status = acpi_get_name(node, ACPI_FULL_PATHNAME_NO_TRAILING, &ret_buf);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not convert name to pathname\n");
+ }
+
+ else {
+ acpi_os_printf("Object %p: Namespace Node - Pathname: %s\n",
+ node, (char *)ret_buf.pointer);
+ }
+
+ if (!acpi_os_readable(node, sizeof(struct acpi_namespace_node))) {
+ acpi_os_printf("Invalid Named object at address %p\n", node);
+ return;
+ }
+
+ acpi_ut_debug_dump_buffer((void *)node,
+ sizeof(struct acpi_namespace_node), display,
+ ACPI_UINT32_MAX);
+ acpi_ex_dump_namespace_node(node, 1);
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (obj_desc) {
+ acpi_os_printf("\nAttached Object %p:", obj_desc);
+ if (!acpi_os_readable
+ (obj_desc, sizeof(union acpi_operand_object))) {
+ acpi_os_printf
+ ("Invalid internal ACPI Object at address %p\n",
+ obj_desc);
+ return;
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(((struct acpi_namespace_node *)
+ obj_desc)) ==
+ ACPI_DESC_TYPE_NAMED) {
+ acpi_os_printf(" Namespace Node - ");
+ status =
+ acpi_get_name((struct acpi_namespace_node *)
+ obj_desc,
+ ACPI_FULL_PATHNAME_NO_TRAILING,
+ &ret_buf);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf
+ ("Could not convert name to pathname\n");
+ } else {
+ acpi_os_printf("Pathname: %s",
+ (char *)ret_buf.pointer);
+ }
+
+ acpi_os_printf("\n");
+ acpi_ut_debug_dump_buffer((void *)obj_desc,
+ sizeof(struct
+ acpi_namespace_node),
+ display, ACPI_UINT32_MAX);
+ } else {
+ acpi_os_printf("\n");
+ acpi_ut_debug_dump_buffer((void *)obj_desc,
+ sizeof(union
+ acpi_operand_object),
+ display, ACPI_UINT32_MAX);
+ }
+
+ acpi_ex_dump_object_descriptor(obj_desc, 1);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_method_info
+ *
+ * PARAMETERS: start_op - Root of the control method parse tree
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display information about the current method
+ *
+ ******************************************************************************/
+
+void acpi_db_display_method_info(union acpi_parse_object *start_op)
+{
+ struct acpi_walk_state *walk_state;
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *node;
+ union acpi_parse_object *root_op;
+ union acpi_parse_object *op;
+ const struct acpi_opcode_info *op_info;
+ u32 num_ops = 0;
+ u32 num_operands = 0;
+ u32 num_operators = 0;
+ u32 num_remaining_ops = 0;
+ u32 num_remaining_operands = 0;
+ u32 num_remaining_operators = 0;
+ u8 count_remaining = FALSE;
+
+ walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list);
+ if (!walk_state) {
+ acpi_os_printf("There is no method currently executing\n");
+ return;
+ }
+
+ obj_desc = walk_state->method_desc;
+ node = walk_state->method_node;
+
+ acpi_os_printf("Currently executing control method is [%4.4s]\n",
+ acpi_ut_get_node_name(node));
+ acpi_os_printf("%X Arguments, SyncLevel = %X\n",
+ (u32)obj_desc->method.param_count,
+ (u32)obj_desc->method.sync_level);
+
+ root_op = start_op;
+ while (root_op->common.parent) {
+ root_op = root_op->common.parent;
+ }
+
+ op = root_op;
+
+ while (op) {
+ if (op == start_op) {
+ count_remaining = TRUE;
+ }
+
+ num_ops++;
+ if (count_remaining) {
+ num_remaining_ops++;
+ }
+
+ /* Decode the opcode */
+
+ op_info = acpi_ps_get_opcode_info(op->common.aml_opcode);
+ switch (op_info->class) {
+ case AML_CLASS_ARGUMENT:
+
+ if (count_remaining) {
+ num_remaining_operands++;
+ }
+
+ num_operands++;
+ break;
+
+ case AML_CLASS_UNKNOWN:
+
+ /* Bad opcode or ASCII character */
+
+ continue;
+
+ default:
+
+ if (count_remaining) {
+ num_remaining_operators++;
+ }
+
+ num_operators++;
+ break;
+ }
+
+ op = acpi_ps_get_depth_next(start_op, op);
+ }
+
+ acpi_os_printf
+ ("Method contains: %X AML Opcodes - %X Operators, %X Operands\n",
+ num_ops, num_operators, num_operands);
+
+ acpi_os_printf
+ ("Remaining to execute: %X AML Opcodes - %X Operators, %X Operands\n",
+ num_remaining_ops, num_remaining_operators,
+ num_remaining_operands);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_locals
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display all locals for the currently running control method
+ *
+ ******************************************************************************/
+
+void acpi_db_display_locals(void)
+{
+ struct acpi_walk_state *walk_state;
+
+ walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list);
+ if (!walk_state) {
+ acpi_os_printf("There is no method currently executing\n");
+ return;
+ }
+
+ acpi_db_decode_locals(walk_state);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_arguments
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display all arguments for the currently running control method
+ *
+ ******************************************************************************/
+
+void acpi_db_display_arguments(void)
+{
+ struct acpi_walk_state *walk_state;
+
+ walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list);
+ if (!walk_state) {
+ acpi_os_printf("There is no method currently executing\n");
+ return;
+ }
+
+ acpi_db_decode_arguments(walk_state);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_results
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display current contents of a method result stack
+ *
+ ******************************************************************************/
+
+void acpi_db_display_results(void)
+{
+ u32 i;
+ struct acpi_walk_state *walk_state;
+ union acpi_operand_object *obj_desc;
+ u32 result_count = 0;
+ struct acpi_namespace_node *node;
+ union acpi_generic_state *frame;
+ u32 index; /* Index onto current frame */
+
+ walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list);
+ if (!walk_state) {
+ acpi_os_printf("There is no method currently executing\n");
+ return;
+ }
+
+ node = walk_state->method_node;
+
+ if (walk_state->results) {
+ result_count = walk_state->result_count;
+ }
+
+ acpi_os_printf("Method [%4.4s] has %X stacked result objects\n",
+ acpi_ut_get_node_name(node), result_count);
+
+ /* From the top element of result stack */
+
+ frame = walk_state->results;
+ index = (result_count - 1) % ACPI_RESULTS_FRAME_OBJ_NUM;
+
+ for (i = 0; i < result_count; i++) {
+ obj_desc = frame->results.obj_desc[index];
+ acpi_os_printf("Result%u: ", i);
+ acpi_db_display_internal_object(obj_desc, walk_state);
+
+ if (index == 0) {
+ frame = frame->results.next;
+ index = ACPI_RESULTS_FRAME_OBJ_NUM;
+ }
+
+ index--;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_calling_tree
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display current calling tree of nested control methods
+ *
+ ******************************************************************************/
+
+void acpi_db_display_calling_tree(void)
+{
+ struct acpi_walk_state *walk_state;
+ struct acpi_namespace_node *node;
+
+ walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list);
+ if (!walk_state) {
+ acpi_os_printf("There is no method currently executing\n");
+ return;
+ }
+
+ acpi_os_printf("Current Control Method Call Tree\n");
+
+ while (walk_state) {
+ node = walk_state->method_node;
+ acpi_os_printf(" [%4.4s]\n", acpi_ut_get_node_name(node));
+
+ walk_state = walk_state->next;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_object_type
+ *
+ * PARAMETERS: object_arg - User entered NS node handle
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display type of an arbitrary NS node
+ *
+ ******************************************************************************/
+
+void acpi_db_display_object_type(char *object_arg)
+{
+ acpi_size arg;
+ acpi_handle handle;
+ struct acpi_device_info *info;
+ acpi_status status;
+ u32 i;
+
+ arg = strtoul(object_arg, NULL, 16);
+ handle = ACPI_TO_POINTER(arg);
+
+ status = acpi_get_object_info(handle, &info);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not get object info, %s\n",
+ acpi_format_exception(status));
+ return;
+ }
+
+ acpi_os_printf("ADR: %8.8X%8.8X, Flags: %X\n",
+ ACPI_FORMAT_UINT64(info->address), info->flags);
+
+ acpi_os_printf("S1D-%2.2X S2D-%2.2X S3D-%2.2X S4D-%2.2X\n",
+ info->highest_dstates[0], info->highest_dstates[1],
+ info->highest_dstates[2], info->highest_dstates[3]);
+
+ acpi_os_printf("S0W-%2.2X S1W-%2.2X S2W-%2.2X S3W-%2.2X S4W-%2.2X\n",
+ info->lowest_dstates[0], info->lowest_dstates[1],
+ info->lowest_dstates[2], info->lowest_dstates[3],
+ info->lowest_dstates[4]);
+
+ if (info->valid & ACPI_VALID_HID) {
+ acpi_os_printf("HID: %s\n", info->hardware_id.string);
+ }
+
+ if (info->valid & ACPI_VALID_UID) {
+ acpi_os_printf("UID: %s\n", info->unique_id.string);
+ }
+
+ if (info->valid & ACPI_VALID_CID) {
+ for (i = 0; i < info->compatible_id_list.count; i++) {
+ acpi_os_printf("CID %u: %s\n", i,
+ info->compatible_id_list.ids[i].string);
+ }
+ }
+
+ ACPI_FREE(info);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_result_object
+ *
+ * PARAMETERS: obj_desc - Object to be displayed
+ * walk_state - Current walk state
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display the result of an AML opcode
+ *
+ * Note: Currently only displays the result object if we are single stepping.
+ * However, this output may be useful in other contexts and could be enabled
+ * to do so if needed.
+ *
+ ******************************************************************************/
+
+void
+acpi_db_display_result_object(union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+
+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
+ /* Only display if single stepping */
+
+ if (!acpi_gbl_cm_single_step) {
+ return;
+ }
+
+ acpi_os_printf("ResultObj: ");
+ acpi_db_display_internal_object(obj_desc, walk_state);
+ acpi_os_printf("\n");
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_argument_object
+ *
+ * PARAMETERS: obj_desc - Object to be displayed
+ * walk_state - Current walk state
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display the result of an AML opcode
+ *
+ ******************************************************************************/
+
+void
+acpi_db_display_argument_object(union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+
+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
+ if (!acpi_gbl_cm_single_step) {
+ return;
+ }
+
+ acpi_os_printf("ArgObj: ");
+ acpi_db_display_internal_object(obj_desc, walk_state);
+}
+
+#if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_gpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display the current GPE structures
+ *
+ ******************************************************************************/
+
+void acpi_db_display_gpes(void)
+{
+ struct acpi_gpe_block_info *gpe_block;
+ struct acpi_gpe_xrupt_info *gpe_xrupt_info;
+ struct acpi_gpe_event_info *gpe_event_info;
+ struct acpi_gpe_register_info *gpe_register_info;
+ char *gpe_type;
+ struct acpi_gpe_notify_info *notify;
+ u32 gpe_index;
+ u32 block = 0;
+ u32 i;
+ u32 j;
+ u32 count;
+ char buffer[80];
+ struct acpi_buffer ret_buf;
+ acpi_status status;
+
+ ret_buf.length = sizeof(buffer);
+ ret_buf.pointer = buffer;
+
+ block = 0;
+
+ /* Walk the GPE lists */
+
+ gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head;
+ while (gpe_xrupt_info) {
+ gpe_block = gpe_xrupt_info->gpe_block_list_head;
+ while (gpe_block) {
+ status = acpi_get_name(gpe_block->node,
+ ACPI_FULL_PATHNAME_NO_TRAILING,
+ &ret_buf);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf
+ ("Could not convert name to pathname\n");
+ }
+
+ if (gpe_block->node == acpi_gbl_fadt_gpe_device) {
+ gpe_type = "FADT-defined GPE block";
+ } else {
+ gpe_type = "GPE Block Device";
+ }
+
+ acpi_os_printf
+ ("\nBlock %u - Info %p DeviceNode %p [%s] - %s\n",
+ block, gpe_block, gpe_block->node, buffer,
+ gpe_type);
+
+ acpi_os_printf(" Registers: %u (%u GPEs)\n",
+ gpe_block->register_count,
+ gpe_block->gpe_count);
+
+ acpi_os_printf
+ (" GPE range: 0x%X to 0x%X on interrupt %u\n",
+ gpe_block->block_base_number,
+ gpe_block->block_base_number +
+ (gpe_block->gpe_count - 1),
+ gpe_xrupt_info->interrupt_number);
+
+ acpi_os_printf
+ (" RegisterInfo: %p Status %8.8X%8.8X Enable %8.8X%8.8X\n",
+ gpe_block->register_info,
+ ACPI_FORMAT_UINT64(gpe_block->register_info->
+ status_address.address),
+ ACPI_FORMAT_UINT64(gpe_block->register_info->
+ enable_address.address));
+
+ acpi_os_printf(" EventInfo: %p\n",
+ gpe_block->event_info);
+
+ /* Examine each GPE Register within the block */
+
+ for (i = 0; i < gpe_block->register_count; i++) {
+ gpe_register_info =
+ &gpe_block->register_info[i];
+
+ acpi_os_printf(" Reg %u: (GPE %.2X-%.2X) "
+ "RunEnable %2.2X WakeEnable %2.2X"
+ " Status %8.8X%8.8X Enable %8.8X%8.8X\n",
+ i,
+ gpe_register_info->
+ base_gpe_number,
+ gpe_register_info->
+ base_gpe_number +
+ (ACPI_GPE_REGISTER_WIDTH - 1),
+ gpe_register_info->
+ enable_for_run,
+ gpe_register_info->
+ enable_for_wake,
+ ACPI_FORMAT_UINT64
+ (gpe_register_info->
+ status_address.address),
+ ACPI_FORMAT_UINT64
+ (gpe_register_info->
+ enable_address.address));
+
+ /* Now look at the individual GPEs in this byte register */
+
+ for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
+ gpe_index =
+ (i * ACPI_GPE_REGISTER_WIDTH) + j;
+ gpe_event_info =
+ &gpe_block->event_info[gpe_index];
+
+ if (ACPI_GPE_DISPATCH_TYPE
+ (gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_NONE) {
+
+ /* This GPE is not used (no method or handler), ignore it */
+
+ continue;
+ }
+
+ acpi_os_printf
+ (" GPE %.2X: %p RunRefs %2.2X Flags %2.2X (",
+ gpe_block->block_base_number +
+ gpe_index, gpe_event_info,
+ gpe_event_info->runtime_count,
+ gpe_event_info->flags);
+
+ /* Decode the flags byte */
+
+ if (gpe_event_info->
+ flags & ACPI_GPE_LEVEL_TRIGGERED) {
+ acpi_os_printf("Level, ");
+ } else {
+ acpi_os_printf("Edge, ");
+ }
+
+ if (gpe_event_info->
+ flags & ACPI_GPE_CAN_WAKE) {
+ acpi_os_printf("CanWake, ");
+ } else {
+ acpi_os_printf("RunOnly, ");
+ }
+
+ switch (ACPI_GPE_DISPATCH_TYPE
+ (gpe_event_info->flags)) {
+ case ACPI_GPE_DISPATCH_NONE:
+
+ acpi_os_printf("NotUsed");
+ break;
+
+ case ACPI_GPE_DISPATCH_METHOD:
+
+ acpi_os_printf("Method");
+ break;
+
+ case ACPI_GPE_DISPATCH_HANDLER:
+
+ acpi_os_printf("Handler");
+ break;
+
+ case ACPI_GPE_DISPATCH_NOTIFY:
+
+ count = 0;
+ notify =
+ gpe_event_info->dispatch.
+ notify_list;
+ while (notify) {
+ count++;
+ notify = notify->next;
+ }
+
+ acpi_os_printf
+ ("Implicit Notify on %u devices",
+ count);
+ break;
+
+ case ACPI_GPE_DISPATCH_RAW_HANDLER:
+
+ acpi_os_printf("RawHandler");
+ break;
+
+ default:
+
+ acpi_os_printf("UNKNOWN: %X",
+ ACPI_GPE_DISPATCH_TYPE
+ (gpe_event_info->
+ flags));
+ break;
+ }
+
+ acpi_os_printf(")\n");
+ }
+ }
+
+ block++;
+ gpe_block = gpe_block->next;
+ }
+
+ gpe_xrupt_info = gpe_xrupt_info->next;
+ }
+}
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_handlers
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display the currently installed global handlers
+ *
+ ******************************************************************************/
+
+void acpi_db_display_handlers(void)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *handler_obj;
+ acpi_adr_space_type space_id;
+ u32 i;
+
+ /* Operation region handlers */
+
+ acpi_os_printf("\nOperation Region Handlers at the namespace root:\n");
+
+ obj_desc = acpi_ns_get_attached_object(acpi_gbl_root_node);
+ if (obj_desc) {
+ for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_gbl_space_id_list); i++) {
+ space_id = acpi_gbl_space_id_list[i];
+
+ acpi_os_printf(ACPI_PREDEFINED_PREFIX,
+ acpi_ut_get_region_name((u8)space_id),
+ space_id);
+
+ handler_obj =
+ acpi_ev_find_region_handler(space_id,
+ obj_desc->common_notify.
+ handler);
+ if (handler_obj) {
+ acpi_os_printf(ACPI_HANDLER_PRESENT_STRING,
+ (handler_obj->address_space.
+ handler_flags &
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)
+ ? "Default" : "User",
+ handler_obj->address_space.
+ handler);
+
+ goto found_handler;
+ }
+
+ /* There is no handler for this space_id */
+
+ acpi_os_printf("None\n");
+
+found_handler: ;
+ }
+
+ /* Find all handlers for user-defined space_IDs */
+
+ handler_obj = obj_desc->common_notify.handler;
+ while (handler_obj) {
+ if (handler_obj->address_space.space_id >=
+ ACPI_USER_REGION_BEGIN) {
+ acpi_os_printf(ACPI_PREDEFINED_PREFIX,
+ "User-defined ID",
+ handler_obj->address_space.
+ space_id);
+ acpi_os_printf(ACPI_HANDLER_PRESENT_STRING,
+ (handler_obj->address_space.
+ handler_flags &
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)
+ ? "Default" : "User",
+ handler_obj->address_space.
+ handler);
+ }
+
+ handler_obj = handler_obj->address_space.next;
+ }
+ }
+#if (!ACPI_REDUCED_HARDWARE)
+
+ /* Fixed event handlers */
+
+ acpi_os_printf("\nFixed Event Handlers:\n");
+
+ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) {
+ acpi_os_printf(ACPI_PREDEFINED_PREFIX,
+ acpi_ut_get_event_name(i), i);
+ if (acpi_gbl_fixed_event_handlers[i].handler) {
+ acpi_os_printf(ACPI_HANDLER_PRESENT_STRING, "User",
+ acpi_gbl_fixed_event_handlers[i].
+ handler);
+ } else {
+ acpi_os_printf(ACPI_HANDLER_NOT_PRESENT_STRING, "None");
+ }
+ }
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+ /* Miscellaneous global handlers */
+
+ acpi_os_printf("\nMiscellaneous Global Handlers:\n");
+
+ for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_gbl_handler_list); i++) {
+ acpi_os_printf(ACPI_HANDLER_NAME_STRING,
+ acpi_gbl_handler_list[i].name);
+
+ if (acpi_gbl_handler_list[i].handler) {
+ acpi_os_printf(ACPI_HANDLER_PRESENT_STRING, "User",
+ acpi_gbl_handler_list[i].handler);
+ } else {
+ acpi_os_printf(ACPI_HANDLER_NOT_PRESENT_STRING, "None");
+ }
+ }
+
+ /* Other handlers that are installed throughout the namespace */
+
+ acpi_os_printf("\nOperation Region Handlers for specific devices:\n");
+
+ (void)acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ acpi_db_display_non_root_handlers, NULL, NULL,
+ NULL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_non_root_handlers
+ *
+ * PARAMETERS: acpi_walk_callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Display information about all handlers installed for a
+ * device object.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_display_non_root_handlers(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ struct acpi_namespace_node *node =
+ ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle);
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *handler_obj;
+ char *pathname;
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ return (AE_OK);
+ }
+
+ pathname = acpi_ns_get_normalized_pathname(node, TRUE);
+ if (!pathname) {
+ return (AE_OK);
+ }
+
+ /* Display all handlers associated with this device */
+
+ handler_obj = obj_desc->common_notify.handler;
+ while (handler_obj) {
+ acpi_os_printf(ACPI_PREDEFINED_PREFIX,
+ acpi_ut_get_region_name((u8)handler_obj->
+ address_space.space_id),
+ handler_obj->address_space.space_id);
+
+ acpi_os_printf(ACPI_HANDLER_PRESENT_STRING2,
+ (handler_obj->address_space.handler_flags &
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) ? "Default"
+ : "User", handler_obj->address_space.handler);
+
+ acpi_os_printf(" Device Name: %s (%p)\n", pathname, node);
+
+ handler_obj = handler_obj->address_space.next;
+ }
+
+ ACPI_FREE(pathname);
+ return (AE_OK);
+}
diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c
new file mode 100644
index 000000000..d3a9521e2
--- /dev/null
+++ b/drivers/acpi/acpica/dbexec.c
@@ -0,0 +1,875 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbexec - debugger control method execution
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdebug.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbexec")
+
+static struct acpi_db_method_info acpi_gbl_db_method_info;
+
+/* Local prototypes */
+
+static acpi_status
+acpi_db_execute_method(struct acpi_db_method_info *info,
+ struct acpi_buffer *return_obj);
+
+static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info);
+
+static u32 acpi_db_get_outstanding_allocations(void);
+
+static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context);
+
+static acpi_status
+acpi_db_execution_walk(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value);
+
+static void ACPI_SYSTEM_XFACE acpi_db_single_execution_thread(void *context);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_delete_objects
+ *
+ * PARAMETERS: count - Count of objects in the list
+ * objects - Array of ACPI_OBJECTs to be deleted
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested
+ * packages via recursion.
+ *
+ ******************************************************************************/
+
+void acpi_db_delete_objects(u32 count, union acpi_object *objects)
+{
+ u32 i;
+
+ for (i = 0; i < count; i++) {
+ switch (objects[i].type) {
+ case ACPI_TYPE_BUFFER:
+
+ ACPI_FREE(objects[i].buffer.pointer);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ /* Recursive call to delete package elements */
+
+ acpi_db_delete_objects(objects[i].package.count,
+ objects[i].package.elements);
+
+ /* Free the elements array */
+
+ ACPI_FREE(objects[i].package.elements);
+ break;
+
+ default:
+
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_execute_method
+ *
+ * PARAMETERS: info - Valid info segment
+ * return_obj - Where to put return object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute a control method. Used to evaluate objects via the
+ * "EXECUTE" or "EVALUATE" commands.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_execute_method(struct acpi_db_method_info *info,
+ struct acpi_buffer *return_obj)
+{
+ acpi_status status;
+ struct acpi_object_list param_objects;
+ union acpi_object params[ACPI_DEBUGGER_MAX_ARGS + 1];
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(db_execute_method);
+
+ if (acpi_gbl_db_output_to_file && !acpi_dbg_level) {
+ acpi_os_printf("Warning: debug output is not enabled!\n");
+ }
+
+ param_objects.count = 0;
+ param_objects.pointer = NULL;
+
+ /* Pass through any command-line arguments */
+
+ if (info->args && info->args[0]) {
+
+ /* Get arguments passed on the command line */
+
+ for (i = 0; (info->args[i] && *(info->args[i])); i++) {
+
+ /* Convert input string (token) to an actual union acpi_object */
+
+ status = acpi_db_convert_to_object(info->types[i],
+ info->args[i],
+ &params[i]);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While parsing method arguments"));
+ goto cleanup;
+ }
+ }
+
+ param_objects.count = i;
+ param_objects.pointer = params;
+ }
+
+ /* Prepare for a return object of arbitrary size */
+
+ return_obj->pointer = acpi_gbl_db_buffer;
+ return_obj->length = ACPI_DEBUG_BUFFER_SIZE;
+
+ /* Do the actual method execution */
+
+ acpi_gbl_method_executing = TRUE;
+ status = acpi_evaluate_object(NULL, info->pathname,
+ &param_objects, return_obj);
+
+ acpi_gbl_cm_single_step = FALSE;
+ acpi_gbl_method_executing = FALSE;
+
+ if (ACPI_FAILURE(status)) {
+ if ((status == AE_ABORT_METHOD) || acpi_gbl_abort_method) {
+
+ /* Clear the abort and fall back to the debugger prompt */
+
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Aborting top-level method"));
+
+ acpi_gbl_abort_method = FALSE;
+ status = AE_OK;
+ goto cleanup;
+ }
+
+ ACPI_EXCEPTION((AE_INFO, status,
+ "while executing %s from AML Debugger",
+ info->pathname));
+
+ if (status == AE_BUFFER_OVERFLOW) {
+ ACPI_ERROR((AE_INFO,
+ "Possible buffer overflow within AML Debugger "
+ "buffer (size 0x%X needed 0x%X)",
+ ACPI_DEBUG_BUFFER_SIZE,
+ (u32)return_obj->length));
+ }
+ }
+
+cleanup:
+ acpi_db_delete_objects(param_objects.count, params);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_execute_setup
+ *
+ * PARAMETERS: info - Valid method info
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Setup info segment prior to method execution
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_NAME(db_execute_setup);
+
+ /* Concatenate the current scope to the supplied name */
+
+ info->pathname[0] = 0;
+ if ((info->name[0] != '\\') && (info->name[0] != '/')) {
+ if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname),
+ acpi_gbl_db_scope_buf)) {
+ status = AE_BUFFER_OVERFLOW;
+ goto error_exit;
+ }
+ }
+
+ if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname),
+ info->name)) {
+ status = AE_BUFFER_OVERFLOW;
+ goto error_exit;
+ }
+
+ acpi_db_prep_namestring(info->pathname);
+
+ acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
+ acpi_os_printf("Evaluating %s\n", info->pathname);
+
+ if (info->flags & EX_SINGLE_STEP) {
+ acpi_gbl_cm_single_step = TRUE;
+ acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+ }
+
+ else {
+ /* No single step, allow redirection to a file */
+
+ acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT);
+ }
+
+ return (AE_OK);
+
+error_exit:
+
+ ACPI_EXCEPTION((AE_INFO, status, "During setup for method execution"));
+ return (status);
+}
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+u32 acpi_db_get_cache_info(struct acpi_memory_list *cache)
+{
+
+ return (cache->total_allocated - cache->total_freed -
+ cache->current_depth);
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_get_outstanding_allocations
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Current global allocation count minus cache entries
+ *
+ * DESCRIPTION: Determine the current number of "outstanding" allocations --
+ * those allocations that have not been freed and also are not
+ * in one of the various object caches.
+ *
+ ******************************************************************************/
+
+static u32 acpi_db_get_outstanding_allocations(void)
+{
+ u32 outstanding = 0;
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+
+ outstanding += acpi_db_get_cache_info(acpi_gbl_state_cache);
+ outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_cache);
+ outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_ext_cache);
+ outstanding += acpi_db_get_cache_info(acpi_gbl_operand_cache);
+#endif
+
+ return (outstanding);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_execution_walk
+ *
+ * PARAMETERS: WALK_CALLBACK
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute a control method. Name is relative to the current
+ * scope.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_execution_walk(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value)
+{
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+ struct acpi_buffer return_obj;
+ acpi_status status;
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (obj_desc->method.param_count) {
+ return (AE_OK);
+ }
+
+ return_obj.pointer = NULL;
+ return_obj.length = ACPI_ALLOCATE_BUFFER;
+
+ acpi_ns_print_node_pathname(node, "Evaluating");
+
+ /* Do the actual method execution */
+
+ acpi_os_printf("\n");
+ acpi_gbl_method_executing = TRUE;
+
+ status = acpi_evaluate_object(node, NULL, NULL, &return_obj);
+
+ acpi_gbl_method_executing = FALSE;
+
+ acpi_os_printf("Evaluation of [%4.4s] returned %s\n",
+ acpi_ut_get_node_name(node),
+ acpi_format_exception(status));
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_execute
+ *
+ * PARAMETERS: name - Name of method to execute
+ * args - Parameters to the method
+ * Types -
+ * flags - single step/no single step
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Execute a control method. Name is relative to the current
+ * scope. Function used for the "EXECUTE", "EVALUATE", and
+ * "ALL" commands
+ *
+ ******************************************************************************/
+
+void
+acpi_db_execute(char *name, char **args, acpi_object_type *types, u32 flags)
+{
+ acpi_status status;
+ struct acpi_buffer return_obj;
+ char *name_string;
+
+#ifdef ACPI_DEBUG_OUTPUT
+ u32 previous_allocations;
+ u32 allocations;
+#endif
+
+ /*
+ * Allow one execution to be performed by debugger or single step
+ * execution will be dead locked by the interpreter mutexes.
+ */
+ if (acpi_gbl_method_executing) {
+ acpi_os_printf("Only one debugger execution is allowed.\n");
+ return;
+ }
+#ifdef ACPI_DEBUG_OUTPUT
+ /* Memory allocation tracking */
+
+ previous_allocations = acpi_db_get_outstanding_allocations();
+#endif
+
+ if (*name == '*') {
+ (void)acpi_walk_namespace(ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ acpi_db_execution_walk, NULL, NULL,
+ NULL);
+ return;
+ }
+
+ if ((flags & EX_ALL) && (strlen(name) > 4)) {
+ acpi_os_printf("Input name (%s) must be a 4-char NameSeg\n",
+ name);
+ return;
+ }
+
+ name_string = ACPI_ALLOCATE(strlen(name) + 1);
+ if (!name_string) {
+ return;
+ }
+
+ memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info));
+ strcpy(name_string, name);
+ acpi_ut_strupr(name_string);
+
+ /* Subcommand to Execute all predefined names in the namespace */
+
+ if (!strncmp(name_string, "PREDEF", 6)) {
+ acpi_db_evaluate_predefined_names();
+ ACPI_FREE(name_string);
+ return;
+ }
+
+ /* Command (ALL <nameseg>) to execute all methods of a particular name */
+
+ else if (flags & EX_ALL) {
+ acpi_gbl_db_method_info.name = name_string;
+ return_obj.pointer = NULL;
+ return_obj.length = ACPI_ALLOCATE_BUFFER;
+ acpi_db_evaluate_all(name_string);
+ ACPI_FREE(name_string);
+ return;
+ } else {
+ acpi_gbl_db_method_info.name = name_string;
+ acpi_gbl_db_method_info.args = args;
+ acpi_gbl_db_method_info.types = types;
+ acpi_gbl_db_method_info.flags = flags;
+
+ return_obj.pointer = NULL;
+ return_obj.length = ACPI_ALLOCATE_BUFFER;
+ }
+
+ status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(name_string);
+ return;
+ }
+
+ /* Get the NS node, determines existence also */
+
+ status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
+ &acpi_gbl_db_method_info.method);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_db_execute_method(&acpi_gbl_db_method_info,
+ &return_obj);
+ }
+ ACPI_FREE(name_string);
+
+ /*
+ * Allow any handlers in separate threads to complete.
+ * (Such as Notify handlers invoked from AML executed above).
+ */
+ acpi_os_sleep((u64)10);
+
+#ifdef ACPI_DEBUG_OUTPUT
+
+ /* Memory allocation tracking */
+
+ allocations =
+ acpi_db_get_outstanding_allocations() - previous_allocations;
+
+ acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
+
+ if (allocations > 0) {
+ acpi_os_printf
+ ("0x%X Outstanding allocations after evaluation of %s\n",
+ allocations, acpi_gbl_db_method_info.pathname);
+ }
+#endif
+
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Evaluation of %s failed with status %s\n",
+ acpi_gbl_db_method_info.pathname,
+ acpi_format_exception(status));
+ } else {
+ /* Display a return object, if any */
+
+ if (return_obj.length) {
+ acpi_os_printf("Evaluation of %s returned object %p, "
+ "external buffer length %X\n",
+ acpi_gbl_db_method_info.pathname,
+ return_obj.pointer,
+ (u32)return_obj.length);
+
+ acpi_db_dump_external_object(return_obj.pointer, 1);
+ acpi_os_printf("\n");
+
+ /* Dump a _PLD buffer if present */
+
+ if (ACPI_COMPARE_NAMESEG
+ ((ACPI_CAST_PTR
+ (struct acpi_namespace_node,
+ acpi_gbl_db_method_info.method)->name.ascii),
+ METHOD_NAME__PLD)) {
+ acpi_db_dump_pld_buffer(return_obj.pointer);
+ }
+ } else {
+ acpi_os_printf
+ ("No object was returned from evaluation of %s\n",
+ acpi_gbl_db_method_info.pathname);
+ }
+ }
+
+ acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_method_thread
+ *
+ * PARAMETERS: context - Execution info segment
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Debugger execute thread. Waits for a command line, then
+ * simply dispatches it.
+ *
+ ******************************************************************************/
+
+static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context)
+{
+ acpi_status status;
+ struct acpi_db_method_info *info = context;
+ struct acpi_db_method_info local_info;
+ u32 i;
+ u8 allow;
+ struct acpi_buffer return_obj;
+
+ /*
+ * acpi_gbl_db_method_info.Arguments will be passed as method arguments.
+ * Prevent acpi_gbl_db_method_info from being modified by multiple threads
+ * concurrently.
+ *
+ * Note: The arguments we are passing are used by the ASL test suite
+ * (aslts). Do not change them without updating the tests.
+ */
+ (void)acpi_os_wait_semaphore(info->info_gate, 1, ACPI_WAIT_FOREVER);
+
+ if (info->init_args) {
+ acpi_db_uint32_to_hex_string(info->num_created,
+ info->index_of_thread_str);
+ acpi_db_uint32_to_hex_string((u32)acpi_os_get_thread_id(),
+ info->id_of_thread_str);
+ }
+
+ if (info->threads && (info->num_created < info->num_threads)) {
+ info->threads[info->num_created++] = acpi_os_get_thread_id();
+ }
+
+ local_info = *info;
+ local_info.args = local_info.arguments;
+ local_info.arguments[0] = local_info.num_threads_str;
+ local_info.arguments[1] = local_info.id_of_thread_str;
+ local_info.arguments[2] = local_info.index_of_thread_str;
+ local_info.arguments[3] = NULL;
+
+ local_info.types = local_info.arg_types;
+
+ (void)acpi_os_signal_semaphore(info->info_gate, 1);
+
+ for (i = 0; i < info->num_loops; i++) {
+ status = acpi_db_execute_method(&local_info, &return_obj);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf
+ ("%s During evaluation of %s at iteration %X\n",
+ acpi_format_exception(status), info->pathname, i);
+ if (status == AE_ABORT_METHOD) {
+ break;
+ }
+ }
+#if 0
+ if ((i % 100) == 0) {
+ acpi_os_printf("%u loops, Thread 0x%x\n",
+ i, acpi_os_get_thread_id());
+ }
+
+ if (return_obj.length) {
+ acpi_os_printf
+ ("Evaluation of %s returned object %p Buflen %X\n",
+ info->pathname, return_obj.pointer,
+ (u32)return_obj.length);
+ acpi_db_dump_external_object(return_obj.pointer, 1);
+ }
+#endif
+ }
+
+ /* Signal our completion */
+
+ allow = 0;
+ (void)acpi_os_wait_semaphore(info->thread_complete_gate,
+ 1, ACPI_WAIT_FOREVER);
+ info->num_completed++;
+
+ if (info->num_completed == info->num_threads) {
+
+ /* Do signal for main thread once only */
+ allow = 1;
+ }
+
+ (void)acpi_os_signal_semaphore(info->thread_complete_gate, 1);
+
+ if (allow) {
+ status = acpi_os_signal_semaphore(info->main_thread_gate, 1);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf
+ ("Could not signal debugger thread sync semaphore, %s\n",
+ acpi_format_exception(status));
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_single_execution_thread
+ *
+ * PARAMETERS: context - Method info struct
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Create one thread and execute a method
+ *
+ ******************************************************************************/
+
+static void ACPI_SYSTEM_XFACE acpi_db_single_execution_thread(void *context)
+{
+ struct acpi_db_method_info *info = context;
+ acpi_status status;
+ struct acpi_buffer return_obj;
+
+ acpi_os_printf("\n");
+
+ status = acpi_db_execute_method(info, &return_obj);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("%s During evaluation of %s\n",
+ acpi_format_exception(status), info->pathname);
+ return;
+ }
+
+ /* Display a return object, if any */
+
+ if (return_obj.length) {
+ acpi_os_printf("Evaluation of %s returned object %p, "
+ "external buffer length %X\n",
+ acpi_gbl_db_method_info.pathname,
+ return_obj.pointer, (u32)return_obj.length);
+
+ acpi_db_dump_external_object(return_obj.pointer, 1);
+ }
+
+ acpi_os_printf("\nBackground thread completed\n%c ",
+ ACPI_DEBUGGER_COMMAND_PROMPT);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_create_execution_thread
+ *
+ * PARAMETERS: method_name_arg - Control method to execute
+ * arguments - Array of arguments to the method
+ * types - Corresponding array of object types
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Create a single thread to evaluate a namespace object. Handles
+ * arguments passed on command line for control methods.
+ *
+ ******************************************************************************/
+
+void
+acpi_db_create_execution_thread(char *method_name_arg,
+ char **arguments, acpi_object_type *types)
+{
+ acpi_status status;
+ u32 i;
+
+ memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info));
+ acpi_gbl_db_method_info.name = method_name_arg;
+ acpi_gbl_db_method_info.init_args = 1;
+ acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments;
+ acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types;
+
+ /* Setup method arguments, up to 7 (0-6) */
+
+ for (i = 0; (i < ACPI_METHOD_NUM_ARGS) && *arguments; i++) {
+ acpi_gbl_db_method_info.arguments[i] = *arguments;
+ arguments++;
+
+ acpi_gbl_db_method_info.arg_types[i] = *types;
+ types++;
+ }
+
+ status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+
+ /* Get the NS node, determines existence also */
+
+ status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
+ &acpi_gbl_db_method_info.method);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("%s Could not get handle for %s\n",
+ acpi_format_exception(status),
+ acpi_gbl_db_method_info.pathname);
+ return;
+ }
+
+ status = acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD,
+ acpi_db_single_execution_thread,
+ &acpi_gbl_db_method_info);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+
+ acpi_os_printf("\nBackground thread started\n");
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_create_execution_threads
+ *
+ * PARAMETERS: num_threads_arg - Number of threads to create
+ * num_loops_arg - Loop count for the thread(s)
+ * method_name_arg - Control method to execute
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Create threads to execute method(s)
+ *
+ ******************************************************************************/
+
+void
+acpi_db_create_execution_threads(char *num_threads_arg,
+ char *num_loops_arg, char *method_name_arg)
+{
+ acpi_status status;
+ u32 num_threads;
+ u32 num_loops;
+ u32 i;
+ u32 size;
+ acpi_mutex main_thread_gate;
+ acpi_mutex thread_complete_gate;
+ acpi_mutex info_gate;
+
+ /* Get the arguments */
+
+ num_threads = strtoul(num_threads_arg, NULL, 0);
+ num_loops = strtoul(num_loops_arg, NULL, 0);
+
+ if (!num_threads || !num_loops) {
+ acpi_os_printf("Bad argument: Threads %X, Loops %X\n",
+ num_threads, num_loops);
+ return;
+ }
+
+ /*
+ * Create the semaphore for synchronization of
+ * the created threads with the main thread.
+ */
+ status = acpi_os_create_semaphore(1, 0, &main_thread_gate);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not create semaphore for "
+ "synchronization with the main thread, %s\n",
+ acpi_format_exception(status));
+ return;
+ }
+
+ /*
+ * Create the semaphore for synchronization
+ * between the created threads.
+ */
+ status = acpi_os_create_semaphore(1, 1, &thread_complete_gate);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not create semaphore for "
+ "synchronization between the created threads, %s\n",
+ acpi_format_exception(status));
+
+ (void)acpi_os_delete_semaphore(main_thread_gate);
+ return;
+ }
+
+ status = acpi_os_create_semaphore(1, 1, &info_gate);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not create semaphore for "
+ "synchronization of AcpiGbl_DbMethodInfo, %s\n",
+ acpi_format_exception(status));
+
+ (void)acpi_os_delete_semaphore(thread_complete_gate);
+ (void)acpi_os_delete_semaphore(main_thread_gate);
+ return;
+ }
+
+ memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info));
+
+ /* Array to store IDs of threads */
+
+ acpi_gbl_db_method_info.num_threads = num_threads;
+ size = sizeof(acpi_thread_id) * acpi_gbl_db_method_info.num_threads;
+
+ acpi_gbl_db_method_info.threads = acpi_os_allocate(size);
+ if (acpi_gbl_db_method_info.threads == NULL) {
+ acpi_os_printf("No memory for thread IDs array\n");
+ (void)acpi_os_delete_semaphore(main_thread_gate);
+ (void)acpi_os_delete_semaphore(thread_complete_gate);
+ (void)acpi_os_delete_semaphore(info_gate);
+ return;
+ }
+ memset(acpi_gbl_db_method_info.threads, 0, size);
+
+ /* Setup the context to be passed to each thread */
+
+ acpi_gbl_db_method_info.name = method_name_arg;
+ acpi_gbl_db_method_info.flags = 0;
+ acpi_gbl_db_method_info.num_loops = num_loops;
+ acpi_gbl_db_method_info.main_thread_gate = main_thread_gate;
+ acpi_gbl_db_method_info.thread_complete_gate = thread_complete_gate;
+ acpi_gbl_db_method_info.info_gate = info_gate;
+
+ /* Init arguments to be passed to method */
+
+ acpi_gbl_db_method_info.init_args = 1;
+ acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments;
+ acpi_gbl_db_method_info.arguments[0] =
+ acpi_gbl_db_method_info.num_threads_str;
+ acpi_gbl_db_method_info.arguments[1] =
+ acpi_gbl_db_method_info.id_of_thread_str;
+ acpi_gbl_db_method_info.arguments[2] =
+ acpi_gbl_db_method_info.index_of_thread_str;
+ acpi_gbl_db_method_info.arguments[3] = NULL;
+
+ acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types;
+ acpi_gbl_db_method_info.arg_types[0] = ACPI_TYPE_INTEGER;
+ acpi_gbl_db_method_info.arg_types[1] = ACPI_TYPE_INTEGER;
+ acpi_gbl_db_method_info.arg_types[2] = ACPI_TYPE_INTEGER;
+
+ acpi_db_uint32_to_hex_string(num_threads,
+ acpi_gbl_db_method_info.num_threads_str);
+
+ status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup_and_exit;
+ }
+
+ /* Get the NS node, determines existence also */
+
+ status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
+ &acpi_gbl_db_method_info.method);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("%s Could not get handle for %s\n",
+ acpi_format_exception(status),
+ acpi_gbl_db_method_info.pathname);
+ goto cleanup_and_exit;
+ }
+
+ /* Create the threads */
+
+ acpi_os_printf("Creating %X threads to execute %X times each\n",
+ num_threads, num_loops);
+
+ for (i = 0; i < (num_threads); i++) {
+ status =
+ acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD,
+ acpi_db_method_thread,
+ &acpi_gbl_db_method_info);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+ }
+
+ /* Wait for all threads to complete */
+
+ (void)acpi_os_wait_semaphore(main_thread_gate, 1, ACPI_WAIT_FOREVER);
+
+ acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
+ acpi_os_printf("All threads (%X) have completed\n", num_threads);
+ acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+
+cleanup_and_exit:
+
+ /* Cleanup and exit */
+
+ (void)acpi_os_delete_semaphore(main_thread_gate);
+ (void)acpi_os_delete_semaphore(thread_complete_gate);
+ (void)acpi_os_delete_semaphore(info_gate);
+
+ acpi_os_free(acpi_gbl_db_method_info.threads);
+ acpi_gbl_db_method_info.threads = NULL;
+}
diff --git a/drivers/acpi/acpica/dbfileio.c b/drivers/acpi/acpica/dbfileio.c
new file mode 100644
index 000000000..e1b6e54a9
--- /dev/null
+++ b/drivers/acpi/acpica/dbfileio.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbfileio - Debugger file I/O commands. These can't usually
+ * be used when running the debugger in Ring 0 (Kernel mode)
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdebug.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbfileio")
+
+#ifdef ACPI_APPLICATION
+#include "acapps.h"
+#ifdef ACPI_DEBUGGER
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_close_debug_file
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: If open, close the current debug output file
+ *
+ ******************************************************************************/
+void acpi_db_close_debug_file(void)
+{
+
+ if (acpi_gbl_debug_file) {
+ fclose(acpi_gbl_debug_file);
+ acpi_gbl_debug_file = NULL;
+ acpi_gbl_db_output_to_file = FALSE;
+ acpi_os_printf("Debug output file %s closed\n",
+ acpi_gbl_db_debug_filename);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_open_debug_file
+ *
+ * PARAMETERS: name - Filename to open
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Open a file where debug output will be directed.
+ *
+ ******************************************************************************/
+
+void acpi_db_open_debug_file(char *name)
+{
+
+ acpi_db_close_debug_file();
+ acpi_gbl_debug_file = fopen(name, "w+");
+ if (!acpi_gbl_debug_file) {
+ acpi_os_printf("Could not open debug file %s\n", name);
+ return;
+ }
+
+ acpi_os_printf("Debug output file %s opened\n", name);
+ acpi_ut_safe_strncpy(acpi_gbl_db_debug_filename, name,
+ sizeof(acpi_gbl_db_debug_filename));
+ acpi_gbl_db_output_to_file = TRUE;
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_load_tables
+ *
+ * PARAMETERS: list_head - List of ACPI tables to load
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load ACPI tables from a previously constructed table list.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_db_load_tables(struct acpi_new_table_desc *list_head)
+{
+ acpi_status status;
+ struct acpi_new_table_desc *table_list_head;
+ struct acpi_table_header *table;
+
+ /* Load all ACPI tables in the list */
+
+ table_list_head = list_head;
+ while (table_list_head) {
+ table = table_list_head->table;
+
+ status = acpi_load_table(table, NULL);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_ALREADY_EXISTS) {
+ acpi_os_printf
+ ("Table %4.4s is already installed\n",
+ table->signature);
+ } else {
+ acpi_os_printf("Could not install table, %s\n",
+ acpi_format_exception(status));
+ }
+
+ return (status);
+ }
+
+ acpi_os_printf
+ ("Acpi table [%4.4s] successfully installed and loaded\n",
+ table->signature);
+
+ table_list_head = table_list_head->next;
+ }
+
+ return (AE_OK);
+}
+#endif
diff --git a/drivers/acpi/acpica/dbhistry.c b/drivers/acpi/acpica/dbhistry.c
new file mode 100644
index 000000000..105e6ceaa
--- /dev/null
+++ b/drivers/acpi/acpica/dbhistry.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dbhistry - debugger HISTORY command
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdebug.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbhistry")
+
+#define HI_NO_HISTORY 0
+#define HI_RECORD_HISTORY 1
+#define HISTORY_SIZE 40
+typedef struct history_info {
+ char *command;
+ u32 cmd_num;
+
+} HISTORY_INFO;
+
+static HISTORY_INFO acpi_gbl_history_buffer[HISTORY_SIZE];
+static u16 acpi_gbl_lo_history = 0;
+static u16 acpi_gbl_num_history = 0;
+static u16 acpi_gbl_next_history_index = 0;
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_add_to_history
+ *
+ * PARAMETERS: command_line - Command to add
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Add a command line to the history buffer.
+ *
+ ******************************************************************************/
+
+void acpi_db_add_to_history(char *command_line)
+{
+ u16 cmd_len;
+ u16 buffer_len;
+
+ /* Put command into the next available slot */
+
+ cmd_len = (u16)strlen(command_line);
+ if (!cmd_len) {
+ return;
+ }
+
+ if (acpi_gbl_history_buffer[acpi_gbl_next_history_index].command !=
+ NULL) {
+ buffer_len =
+ (u16)
+ strlen(acpi_gbl_history_buffer[acpi_gbl_next_history_index].
+ command);
+
+ if (cmd_len > buffer_len) {
+ acpi_os_free(acpi_gbl_history_buffer
+ [acpi_gbl_next_history_index].command);
+ acpi_gbl_history_buffer[acpi_gbl_next_history_index].
+ command = acpi_os_allocate(cmd_len + 1);
+ }
+ } else {
+ acpi_gbl_history_buffer[acpi_gbl_next_history_index].command =
+ acpi_os_allocate(cmd_len + 1);
+ }
+
+ strcpy(acpi_gbl_history_buffer[acpi_gbl_next_history_index].command,
+ command_line);
+
+ acpi_gbl_history_buffer[acpi_gbl_next_history_index].cmd_num =
+ acpi_gbl_next_cmd_num;
+
+ /* Adjust indexes */
+
+ if ((acpi_gbl_num_history == HISTORY_SIZE) &&
+ (acpi_gbl_next_history_index == acpi_gbl_lo_history)) {
+ acpi_gbl_lo_history++;
+ if (acpi_gbl_lo_history >= HISTORY_SIZE) {
+ acpi_gbl_lo_history = 0;
+ }
+ }
+
+ acpi_gbl_next_history_index++;
+ if (acpi_gbl_next_history_index >= HISTORY_SIZE) {
+ acpi_gbl_next_history_index = 0;
+ }
+
+ acpi_gbl_next_cmd_num++;
+ if (acpi_gbl_num_history < HISTORY_SIZE) {
+ acpi_gbl_num_history++;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_history
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display the contents of the history buffer
+ *
+ ******************************************************************************/
+
+void acpi_db_display_history(void)
+{
+ u32 i;
+ u16 history_index;
+
+ history_index = acpi_gbl_lo_history;
+
+ /* Dump entire history buffer */
+
+ for (i = 0; i < acpi_gbl_num_history; i++) {
+ if (acpi_gbl_history_buffer[history_index].command) {
+ acpi_os_printf("%3u %s\n",
+ acpi_gbl_history_buffer[history_index].
+ cmd_num,
+ acpi_gbl_history_buffer[history_index].
+ command);
+ }
+
+ history_index++;
+ if (history_index >= HISTORY_SIZE) {
+ history_index = 0;
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_get_from_history
+ *
+ * PARAMETERS: command_num_arg - String containing the number of the
+ * command to be retrieved
+ *
+ * RETURN: Pointer to the retrieved command. Null on error.
+ *
+ * DESCRIPTION: Get a command from the history buffer
+ *
+ ******************************************************************************/
+
+char *acpi_db_get_from_history(char *command_num_arg)
+{
+ u32 cmd_num;
+
+ if (command_num_arg == NULL) {
+ cmd_num = acpi_gbl_next_cmd_num - 1;
+ }
+
+ else {
+ cmd_num = strtoul(command_num_arg, NULL, 0);
+ }
+
+ return (acpi_db_get_history_by_index(cmd_num));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_get_history_by_index
+ *
+ * PARAMETERS: cmd_num - Index of the desired history entry.
+ * Values are 0...(acpi_gbl_next_cmd_num - 1)
+ *
+ * RETURN: Pointer to the retrieved command. Null on error.
+ *
+ * DESCRIPTION: Get a command from the history buffer
+ *
+ ******************************************************************************/
+
+char *acpi_db_get_history_by_index(u32 cmd_num)
+{
+ u32 i;
+ u16 history_index;
+
+ /* Search history buffer */
+
+ history_index = acpi_gbl_lo_history;
+ for (i = 0; i < acpi_gbl_num_history; i++) {
+ if (acpi_gbl_history_buffer[history_index].cmd_num == cmd_num) {
+
+ /* Found the command, return it */
+
+ return (acpi_gbl_history_buffer[history_index].command);
+ }
+
+ /* History buffer is circular */
+
+ history_index++;
+ if (history_index >= HISTORY_SIZE) {
+ history_index = 0;
+ }
+ }
+
+ acpi_os_printf("Invalid history number: %u\n", history_index);
+ return (NULL);
+}
diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c
new file mode 100644
index 000000000..b8a489230
--- /dev/null
+++ b/drivers/acpi/acpica/dbinput.c
@@ -0,0 +1,1261 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbinput - user front-end to the AML debugger
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdebug.h"
+
+#ifdef ACPI_APPLICATION
+#include "acapps.h"
+#endif
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbinput")
+
+/* Local prototypes */
+static u32 acpi_db_get_line(char *input_buffer);
+
+static u32 acpi_db_match_command(char *user_command);
+
+static void acpi_db_display_command_info(const char *command, u8 display_all);
+
+static void acpi_db_display_help(char *command);
+
+static u8
+acpi_db_match_command_help(const char *command,
+ const struct acpi_db_command_help *help);
+
+/*
+ * Top-level debugger commands.
+ *
+ * This list of commands must match the string table below it
+ */
+enum acpi_ex_debugger_commands {
+ CMD_NOT_FOUND = 0,
+ CMD_NULL,
+ CMD_ALL,
+ CMD_ALLOCATIONS,
+ CMD_ARGS,
+ CMD_ARGUMENTS,
+ CMD_BREAKPOINT,
+ CMD_BUSINFO,
+ CMD_CALL,
+ CMD_DEBUG,
+ CMD_DISASSEMBLE,
+ CMD_DISASM,
+ CMD_DUMP,
+ CMD_EVALUATE,
+ CMD_EXECUTE,
+ CMD_EXIT,
+ CMD_FIELDS,
+ CMD_FIND,
+ CMD_GO,
+ CMD_HANDLERS,
+ CMD_HELP,
+ CMD_HELP2,
+ CMD_HISTORY,
+ CMD_HISTORY_EXE,
+ CMD_HISTORY_LAST,
+ CMD_INFORMATION,
+ CMD_INTEGRITY,
+ CMD_INTO,
+ CMD_LEVEL,
+ CMD_LIST,
+ CMD_LOCALS,
+ CMD_LOCKS,
+ CMD_METHODS,
+ CMD_NAMESPACE,
+ CMD_NOTIFY,
+ CMD_OBJECTS,
+ CMD_OSI,
+ CMD_OWNER,
+ CMD_PATHS,
+ CMD_PREDEFINED,
+ CMD_PREFIX,
+ CMD_QUIT,
+ CMD_REFERENCES,
+ CMD_RESOURCES,
+ CMD_RESULTS,
+ CMD_SET,
+ CMD_STATS,
+ CMD_STOP,
+ CMD_TABLES,
+ CMD_TEMPLATE,
+ CMD_TRACE,
+ CMD_TREE,
+ CMD_TYPE,
+#ifdef ACPI_APPLICATION
+ CMD_ENABLEACPI,
+ CMD_EVENT,
+ CMD_GPE,
+ CMD_GPES,
+ CMD_SCI,
+ CMD_SLEEP,
+
+ CMD_CLOSE,
+ CMD_LOAD,
+ CMD_OPEN,
+ CMD_UNLOAD,
+
+ CMD_TERMINATE,
+ CMD_BACKGROUND,
+ CMD_THREADS,
+
+ CMD_TEST,
+#endif
+};
+
+#define CMD_FIRST_VALID 2
+
+/* Second parameter is the required argument count */
+
+static const struct acpi_db_command_info acpi_gbl_db_commands[] = {
+ {"<NOT FOUND>", 0},
+ {"<NULL>", 0},
+ {"ALL", 1},
+ {"ALLOCATIONS", 0},
+ {"ARGS", 0},
+ {"ARGUMENTS", 0},
+ {"BREAKPOINT", 1},
+ {"BUSINFO", 0},
+ {"CALL", 0},
+ {"DEBUG", 1},
+ {"DISASSEMBLE", 1},
+ {"DISASM", 1},
+ {"DUMP", 1},
+ {"EVALUATE", 1},
+ {"EXECUTE", 1},
+ {"EXIT", 0},
+ {"FIELDS", 1},
+ {"FIND", 1},
+ {"GO", 0},
+ {"HANDLERS", 0},
+ {"HELP", 0},
+ {"?", 0},
+ {"HISTORY", 0},
+ {"!", 1},
+ {"!!", 0},
+ {"INFORMATION", 0},
+ {"INTEGRITY", 0},
+ {"INTO", 0},
+ {"LEVEL", 0},
+ {"LIST", 0},
+ {"LOCALS", 0},
+ {"LOCKS", 0},
+ {"METHODS", 0},
+ {"NAMESPACE", 0},
+ {"NOTIFY", 2},
+ {"OBJECTS", 0},
+ {"OSI", 0},
+ {"OWNER", 1},
+ {"PATHS", 0},
+ {"PREDEFINED", 0},
+ {"PREFIX", 0},
+ {"QUIT", 0},
+ {"REFERENCES", 1},
+ {"RESOURCES", 0},
+ {"RESULTS", 0},
+ {"SET", 3},
+ {"STATS", 1},
+ {"STOP", 0},
+ {"TABLES", 0},
+ {"TEMPLATE", 1},
+ {"TRACE", 1},
+ {"TREE", 0},
+ {"TYPE", 1},
+#ifdef ACPI_APPLICATION
+ {"ENABLEACPI", 0},
+ {"EVENT", 1},
+ {"GPE", 1},
+ {"GPES", 0},
+ {"SCI", 0},
+ {"SLEEP", 0},
+
+ {"CLOSE", 0},
+ {"LOAD", 1},
+ {"OPEN", 1},
+ {"UNLOAD", 1},
+
+ {"TERMINATE", 0},
+ {"BACKGROUND", 1},
+ {"THREADS", 3},
+
+ {"TEST", 1},
+#endif
+ {NULL, 0}
+};
+
+/*
+ * Help for all debugger commands. First argument is the number of lines
+ * of help to output for the command.
+ *
+ * Note: Some commands are not supported by the kernel-level version of
+ * the debugger.
+ */
+static const struct acpi_db_command_help acpi_gbl_db_command_help[] = {
+ {0, "\nNamespace Access:", "\n"},
+ {1, " Businfo", "Display system bus info\n"},
+ {1, " Disassemble <Method>", "Disassemble a control method\n"},
+ {1, " Find <AcpiName> (? is wildcard)",
+ "Find ACPI name(s) with wildcards\n"},
+ {1, " Integrity", "Validate namespace integrity\n"},
+ {1, " Methods", "Display list of loaded control methods\n"},
+ {1, " Fields <AddressSpaceId>",
+ "Display list of loaded field units by space ID\n"},
+ {1, " Namespace [Object] [Depth]",
+ "Display loaded namespace tree/subtree\n"},
+ {1, " Notify <Object> <Value>", "Send a notification on Object\n"},
+ {1, " Objects [ObjectType]",
+ "Display summary of all objects or just given type\n"},
+ {1, " Owner <OwnerId> [Depth]",
+ "Display loaded namespace by object owner\n"},
+ {1, " Paths", "Display full pathnames of namespace objects\n"},
+ {1, " Predefined", "Check all predefined names\n"},
+ {1, " Prefix [<Namepath>]", "Set or Get current execution prefix\n"},
+ {1, " References <Addr>", "Find all references to object at addr\n"},
+ {1, " Resources [DeviceName]",
+ "Display Device resources (no arg = all devices)\n"},
+ {1, " Set N <NamedObject> <Value>", "Set value for named integer\n"},
+ {1, " Template <Object>", "Format/dump a Buffer/ResourceTemplate\n"},
+ {1, " Type <Object>", "Display object type\n"},
+
+ {0, "\nControl Method Execution:", "\n"},
+ {1, " All <NameSeg>", "Evaluate all objects named NameSeg\n"},
+ {1, " Evaluate <Namepath> [Arguments]",
+ "Evaluate object or control method\n"},
+ {1, " Execute <Namepath> [Arguments]", "Synonym for Evaluate\n"},
+#ifdef ACPI_APPLICATION
+ {1, " Background <Namepath> [Arguments]",
+ "Evaluate object/method in a separate thread\n"},
+ {1, " Thread <Threads><Loops><NamePath>",
+ "Spawn threads to execute method(s)\n"},
+#endif
+ {1, " Debug <Namepath> [Arguments]", "Single-Step a control method\n"},
+ {7, " [Arguments] formats:", "Control method argument formats\n"},
+ {1, " Hex Integer", "Integer\n"},
+ {1, " \"Ascii String\"", "String\n"},
+ {1, " (Hex Byte List)", "Buffer\n"},
+ {1, " (01 42 7A BF)", "Buffer example (4 bytes)\n"},
+ {1, " [Package Element List]", "Package\n"},
+ {1, " [0x01 0x1234 \"string\"]",
+ "Package example (3 elements)\n"},
+
+ {0, "\nMiscellaneous:", "\n"},
+ {1, " Allocations", "Display list of current memory allocations\n"},
+ {2, " Dump <Address>|<Namepath>", "\n"},
+ {0, " [Byte|Word|Dword|Qword]",
+ "Display ACPI objects or memory\n"},
+ {1, " Handlers", "Info about global handlers\n"},
+ {1, " Help [Command]", "This help screen or individual command\n"},
+ {1, " History", "Display command history buffer\n"},
+ {1, " Level <DebugLevel>] [console]",
+ "Get/Set debug level for file or console\n"},
+ {1, " Locks", "Current status of internal mutexes\n"},
+ {1, " Osi [Install|Remove <name>]",
+ "Display or modify global _OSI list\n"},
+ {1, " Quit or Exit", "Exit this command\n"},
+ {8, " Stats <SubCommand>",
+ "Display namespace and memory statistics\n"},
+ {1, " Allocations", "Display list of current memory allocations\n"},
+ {1, " Memory", "Dump internal memory lists\n"},
+ {1, " Misc", "Namespace search and mutex stats\n"},
+ {1, " Objects", "Summary of namespace objects\n"},
+ {1, " Sizes", "Sizes for each of the internal objects\n"},
+ {1, " Stack", "Display CPU stack usage\n"},
+ {1, " Tables", "Info about current ACPI table(s)\n"},
+ {1, " Tables", "Display info about loaded ACPI tables\n"},
+#ifdef ACPI_APPLICATION
+ {1, " Terminate", "Delete namespace and all internal objects\n"},
+#endif
+ {1, " ! <CommandNumber>", "Execute command from history buffer\n"},
+ {1, " !!", "Execute last command again\n"},
+
+ {0, "\nMethod and Namespace Debugging:", "\n"},
+ {5, " Trace <State> [<Namepath>] [Once]",
+ "Trace control method execution\n"},
+ {1, " Enable", "Enable all messages\n"},
+ {1, " Disable", "Disable tracing\n"},
+ {1, " Method", "Enable method execution messages\n"},
+ {1, " Opcode", "Enable opcode execution messages\n"},
+ {3, " Test <TestName>", "Invoke a debug test\n"},
+ {1, " Objects", "Read/write/compare all namespace data objects\n"},
+ {1, " Predefined",
+ "Validate all ACPI predefined names (_STA, etc.)\n"},
+ {1, " Execute predefined",
+ "Execute all predefined (public) methods\n"},
+
+ {0, "\nControl Method Single-Step Execution:", "\n"},
+ {1, " Arguments (or Args)", "Display method arguments\n"},
+ {1, " Breakpoint <AmlOffset>", "Set an AML execution breakpoint\n"},
+ {1, " Call", "Run to next control method invocation\n"},
+ {1, " Go", "Allow method to run to completion\n"},
+ {1, " Information", "Display info about the current method\n"},
+ {1, " Into", "Step into (not over) a method call\n"},
+ {1, " List [# of Aml Opcodes]", "Display method ASL statements\n"},
+ {1, " Locals", "Display method local variables\n"},
+ {1, " Results", "Display method result stack\n"},
+ {1, " Set <A|L> <#> <Value>", "Set method data (Arguments/Locals)\n"},
+ {1, " Stop", "Terminate control method\n"},
+ {1, " Tree", "Display control method calling tree\n"},
+ {1, " <Enter>", "Single step next AML opcode (over calls)\n"},
+
+#ifdef ACPI_APPLICATION
+ {0, "\nFile Operations:", "\n"},
+ {1, " Close", "Close debug output file\n"},
+ {1, " Load <Input Filename>", "Load ACPI table from a file\n"},
+ {1, " Open <Output Filename>", "Open a file for debug output\n"},
+ {1, " Unload <Namepath>",
+ "Unload an ACPI table via namespace object\n"},
+
+ {0, "\nHardware Simulation:", "\n"},
+ {1, " EnableAcpi", "Enable ACPI (hardware) mode\n"},
+ {1, " Event <F|G> <Value>", "Generate AcpiEvent (Fixed/GPE)\n"},
+ {1, " Gpe <GpeNum> [GpeBlockDevice]", "Simulate a GPE\n"},
+ {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"},
+#endif
+ {0, NULL, NULL}
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_match_command_help
+ *
+ * PARAMETERS: command - Command string to match
+ * help - Help table entry to attempt match
+ *
+ * RETURN: TRUE if command matched, FALSE otherwise
+ *
+ * DESCRIPTION: Attempt to match a command in the help table in order to
+ * print help information for a single command.
+ *
+ ******************************************************************************/
+
+static u8
+acpi_db_match_command_help(const char *command,
+ const struct acpi_db_command_help *help)
+{
+ char *invocation = help->invocation;
+ u32 line_count;
+
+ /* Valid commands in the help table begin with a couple of spaces */
+
+ if (*invocation != ' ') {
+ return (FALSE);
+ }
+
+ while (*invocation == ' ') {
+ invocation++;
+ }
+
+ /* Match command name (full command or substring) */
+
+ while ((*command) && (*invocation) && (*invocation != ' ')) {
+ if (tolower((int)*command) != tolower((int)*invocation)) {
+ return (FALSE);
+ }
+
+ invocation++;
+ command++;
+ }
+
+ /* Print the appropriate number of help lines */
+
+ line_count = help->line_count;
+ while (line_count) {
+ acpi_os_printf("%-38s : %s", help->invocation,
+ help->description);
+ help++;
+ line_count--;
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_command_info
+ *
+ * PARAMETERS: command - Command string to match
+ * display_all - Display all matching commands, or just
+ * the first one (substring match)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display help information for a Debugger command.
+ *
+ ******************************************************************************/
+
+static void acpi_db_display_command_info(const char *command, u8 display_all)
+{
+ const struct acpi_db_command_help *next;
+ u8 matched;
+
+ next = acpi_gbl_db_command_help;
+ while (next->invocation) {
+ matched = acpi_db_match_command_help(command, next);
+ if (!display_all && matched) {
+ return;
+ }
+
+ next++;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_help
+ *
+ * PARAMETERS: command - Optional command string to display help.
+ * if not specified, all debugger command
+ * help strings are displayed
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display help for a single debugger command, or all of them.
+ *
+ ******************************************************************************/
+
+static void acpi_db_display_help(char *command)
+{
+ const struct acpi_db_command_help *next = acpi_gbl_db_command_help;
+
+ if (!command) {
+
+ /* No argument to help, display help for all commands */
+
+ acpi_os_printf("\nSummary of AML Debugger Commands\n\n");
+
+ while (next->invocation) {
+ acpi_os_printf("%-38s%s", next->invocation,
+ next->description);
+ next++;
+ }
+ acpi_os_printf("\n");
+
+ } else {
+ /* Display help for all commands that match the substring */
+
+ acpi_db_display_command_info(command, TRUE);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_get_next_token
+ *
+ * PARAMETERS: string - Command buffer
+ * next - Return value, end of next token
+ *
+ * RETURN: Pointer to the start of the next token.
+ *
+ * DESCRIPTION: Command line parsing. Get the next token on the command line
+ *
+ ******************************************************************************/
+
+char *acpi_db_get_next_token(char *string,
+ char **next, acpi_object_type *return_type)
+{
+ char *start;
+ u32 depth;
+ acpi_object_type type = ACPI_TYPE_INTEGER;
+
+ /* At end of buffer? */
+
+ if (!string || !(*string)) {
+ return (NULL);
+ }
+
+ /* Remove any spaces at the beginning, ignore blank lines */
+
+ while (*string && isspace((int)*string)) {
+ string++;
+ }
+
+ if (!(*string)) {
+ return (NULL);
+ }
+
+ switch (*string) {
+ case '"':
+
+ /* This is a quoted string, scan until closing quote */
+
+ string++;
+ start = string;
+ type = ACPI_TYPE_STRING;
+
+ /* Find end of string */
+
+ while (*string && (*string != '"')) {
+ string++;
+ }
+ break;
+
+ case '(':
+
+ /* This is the start of a buffer, scan until closing paren */
+
+ string++;
+ start = string;
+ type = ACPI_TYPE_BUFFER;
+
+ /* Find end of buffer */
+
+ while (*string && (*string != ')')) {
+ string++;
+ }
+ break;
+
+ case '{':
+
+ /* This is the start of a field unit, scan until closing brace */
+
+ string++;
+ start = string;
+ type = ACPI_TYPE_FIELD_UNIT;
+
+ /* Find end of buffer */
+
+ while (*string && (*string != '}')) {
+ string++;
+ }
+ break;
+
+ case '[':
+
+ /* This is the start of a package, scan until closing bracket */
+
+ string++;
+ depth = 1;
+ start = string;
+ type = ACPI_TYPE_PACKAGE;
+
+ /* Find end of package (closing bracket) */
+
+ while (*string) {
+
+ /* Handle String package elements */
+
+ if (*string == '"') {
+ /* Find end of string */
+
+ string++;
+ while (*string && (*string != '"')) {
+ string++;
+ }
+ if (!(*string)) {
+ break;
+ }
+ } else if (*string == '[') {
+ depth++; /* A nested package declaration */
+ } else if (*string == ']') {
+ depth--;
+ if (depth == 0) { /* Found final package closing bracket */
+ break;
+ }
+ }
+
+ string++;
+ }
+ break;
+
+ default:
+
+ start = string;
+
+ /* Find end of token */
+
+ while (*string && !isspace((int)*string)) {
+ string++;
+ }
+ break;
+ }
+
+ if (!(*string)) {
+ *next = NULL;
+ } else {
+ *string = 0;
+ *next = string + 1;
+ }
+
+ *return_type = type;
+ return (start);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_get_line
+ *
+ * PARAMETERS: input_buffer - Command line buffer
+ *
+ * RETURN: Count of arguments to the command
+ *
+ * DESCRIPTION: Get the next command line from the user. Gets entire line
+ * up to the next newline
+ *
+ ******************************************************************************/
+
+static u32 acpi_db_get_line(char *input_buffer)
+{
+ u32 i;
+ u32 count;
+ char *next;
+ char *this;
+
+ if (acpi_ut_safe_strcpy
+ (acpi_gbl_db_parsed_buf, sizeof(acpi_gbl_db_parsed_buf),
+ input_buffer)) {
+ acpi_os_printf
+ ("Buffer overflow while parsing input line (max %u characters)\n",
+ (u32)sizeof(acpi_gbl_db_parsed_buf));
+ return (0);
+ }
+
+ this = acpi_gbl_db_parsed_buf;
+ for (i = 0; i < ACPI_DEBUGGER_MAX_ARGS; i++) {
+ acpi_gbl_db_args[i] = acpi_db_get_next_token(this, &next,
+ &acpi_gbl_db_arg_types
+ [i]);
+ if (!acpi_gbl_db_args[i]) {
+ break;
+ }
+
+ this = next;
+ }
+
+ /* Uppercase the actual command */
+
+ acpi_ut_strupr(acpi_gbl_db_args[0]);
+
+ count = i;
+ if (count) {
+ count--; /* Number of args only */
+ }
+
+ return (count);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_match_command
+ *
+ * PARAMETERS: user_command - User command line
+ *
+ * RETURN: Index into command array, -1 if not found
+ *
+ * DESCRIPTION: Search command array for a command match
+ *
+ ******************************************************************************/
+
+static u32 acpi_db_match_command(char *user_command)
+{
+ u32 i;
+
+ if (!user_command || user_command[0] == 0) {
+ return (CMD_NULL);
+ }
+
+ for (i = CMD_FIRST_VALID; acpi_gbl_db_commands[i].name; i++) {
+ if (strstr
+ (ACPI_CAST_PTR(char, acpi_gbl_db_commands[i].name),
+ user_command) == acpi_gbl_db_commands[i].name) {
+ return (i);
+ }
+ }
+
+ /* Command not recognized */
+
+ return (CMD_NOT_FOUND);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_command_dispatch
+ *
+ * PARAMETERS: input_buffer - Command line buffer
+ * walk_state - Current walk
+ * op - Current (executing) parse op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Command dispatcher.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_db_command_dispatch(char *input_buffer,
+ struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ u32 temp;
+ u64 temp64;
+ u32 command_index;
+ u32 param_count;
+ char *command_line;
+ acpi_status status = AE_CTRL_TRUE;
+
+ /* If acpi_terminate has been called, terminate this thread */
+
+ if (acpi_gbl_db_terminate_loop) {
+ return (AE_CTRL_TERMINATE);
+ }
+
+ /* Find command and add to the history buffer */
+
+ param_count = acpi_db_get_line(input_buffer);
+ command_index = acpi_db_match_command(acpi_gbl_db_args[0]);
+
+ /*
+ * We don't want to add the !! command to the history buffer. It
+ * would cause an infinite loop because it would always be the
+ * previous command.
+ */
+ if (command_index != CMD_HISTORY_LAST) {
+ acpi_db_add_to_history(input_buffer);
+ }
+
+ /* Verify that we have the minimum number of params */
+
+ if (param_count < acpi_gbl_db_commands[command_index].min_args) {
+ acpi_os_printf
+ ("%u parameters entered, [%s] requires %u parameters\n",
+ param_count, acpi_gbl_db_commands[command_index].name,
+ acpi_gbl_db_commands[command_index].min_args);
+
+ acpi_db_display_command_info(acpi_gbl_db_commands
+ [command_index].name, FALSE);
+ return (AE_CTRL_TRUE);
+ }
+
+ /* Decode and dispatch the command */
+
+ switch (command_index) {
+ case CMD_NULL:
+
+ if (op) {
+ return (AE_OK);
+ }
+ break;
+
+ case CMD_ALL:
+
+ acpi_os_printf("Executing all objects with NameSeg: %s\n",
+ acpi_gbl_db_args[1]);
+ acpi_db_execute(acpi_gbl_db_args[1], &acpi_gbl_db_args[2],
+ &acpi_gbl_db_arg_types[2],
+ EX_NO_SINGLE_STEP | EX_ALL);
+ break;
+
+ case CMD_ALLOCATIONS:
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ acpi_ut_dump_allocations((u32)-1, NULL);
+#endif
+ break;
+
+ case CMD_ARGS:
+ case CMD_ARGUMENTS:
+
+ acpi_db_display_arguments();
+ break;
+
+ case CMD_BREAKPOINT:
+
+ acpi_db_set_method_breakpoint(acpi_gbl_db_args[1], walk_state,
+ op);
+ break;
+
+ case CMD_BUSINFO:
+
+ acpi_db_get_bus_info();
+ break;
+
+ case CMD_CALL:
+
+ acpi_db_set_method_call_breakpoint(op);
+ status = AE_OK;
+ break;
+
+ case CMD_DEBUG:
+
+ acpi_db_execute(acpi_gbl_db_args[1],
+ &acpi_gbl_db_args[2], &acpi_gbl_db_arg_types[2],
+ EX_SINGLE_STEP);
+ break;
+
+ case CMD_DISASSEMBLE:
+ case CMD_DISASM:
+
+#ifdef ACPI_DISASSEMBLER
+ (void)acpi_db_disassemble_method(acpi_gbl_db_args[1]);
+#else
+ acpi_os_printf
+ ("The AML Disassembler is not configured/present\n");
+#endif
+ break;
+
+ case CMD_DUMP:
+
+ acpi_db_decode_and_display_object(acpi_gbl_db_args[1],
+ acpi_gbl_db_args[2]);
+ break;
+
+ case CMD_EVALUATE:
+ case CMD_EXECUTE:
+
+ acpi_db_execute(acpi_gbl_db_args[1],
+ &acpi_gbl_db_args[2], &acpi_gbl_db_arg_types[2],
+ EX_NO_SINGLE_STEP);
+ break;
+
+ case CMD_FIND:
+
+ status = acpi_db_find_name_in_namespace(acpi_gbl_db_args[1]);
+ break;
+
+ case CMD_FIELDS:
+
+ status = acpi_ut_strtoul64(acpi_gbl_db_args[1], &temp64);
+
+ if (ACPI_FAILURE(status)
+ || temp64 >= ACPI_NUM_PREDEFINED_REGIONS) {
+ acpi_os_printf
+ ("Invalid address space ID: must be between 0 and %u inclusive\n",
+ ACPI_NUM_PREDEFINED_REGIONS - 1);
+ return (AE_OK);
+ }
+
+ status = acpi_db_display_fields((u32)temp64);
+ break;
+
+ case CMD_GO:
+
+ acpi_gbl_cm_single_step = FALSE;
+ return (AE_OK);
+
+ case CMD_HANDLERS:
+
+ acpi_db_display_handlers();
+ break;
+
+ case CMD_HELP:
+ case CMD_HELP2:
+
+ acpi_db_display_help(acpi_gbl_db_args[1]);
+ break;
+
+ case CMD_HISTORY:
+
+ acpi_db_display_history();
+ break;
+
+ case CMD_HISTORY_EXE: /* ! command */
+
+ command_line = acpi_db_get_from_history(acpi_gbl_db_args[1]);
+ if (!command_line) {
+ return (AE_CTRL_TRUE);
+ }
+
+ status = acpi_db_command_dispatch(command_line, walk_state, op);
+ return (status);
+
+ case CMD_HISTORY_LAST: /* !! command */
+
+ command_line = acpi_db_get_from_history(NULL);
+ if (!command_line) {
+ return (AE_CTRL_TRUE);
+ }
+
+ status = acpi_db_command_dispatch(command_line, walk_state, op);
+ return (status);
+
+ case CMD_INFORMATION:
+
+ acpi_db_display_method_info(op);
+ break;
+
+ case CMD_INTEGRITY:
+
+ acpi_db_check_integrity();
+ break;
+
+ case CMD_INTO:
+
+ if (op) {
+ acpi_gbl_cm_single_step = TRUE;
+ return (AE_OK);
+ }
+ break;
+
+ case CMD_LEVEL:
+
+ if (param_count == 0) {
+ acpi_os_printf
+ ("Current debug level for file output is: %8.8X\n",
+ acpi_gbl_db_debug_level);
+ acpi_os_printf
+ ("Current debug level for console output is: %8.8X\n",
+ acpi_gbl_db_console_debug_level);
+ } else if (param_count == 2) {
+ temp = acpi_gbl_db_console_debug_level;
+ acpi_gbl_db_console_debug_level =
+ strtoul(acpi_gbl_db_args[1], NULL, 16);
+ acpi_os_printf
+ ("Debug Level for console output was %8.8X, now %8.8X\n",
+ temp, acpi_gbl_db_console_debug_level);
+ } else {
+ temp = acpi_gbl_db_debug_level;
+ acpi_gbl_db_debug_level =
+ strtoul(acpi_gbl_db_args[1], NULL, 16);
+ acpi_os_printf
+ ("Debug Level for file output was %8.8X, now %8.8X\n",
+ temp, acpi_gbl_db_debug_level);
+ }
+ break;
+
+ case CMD_LIST:
+
+#ifdef ACPI_DISASSEMBLER
+ acpi_db_disassemble_aml(acpi_gbl_db_args[1], op);
+#else
+ acpi_os_printf
+ ("The AML Disassembler is not configured/present\n");
+#endif
+ break;
+
+ case CMD_LOCKS:
+
+ acpi_db_display_locks();
+ break;
+
+ case CMD_LOCALS:
+
+ acpi_db_display_locals();
+ break;
+
+ case CMD_METHODS:
+
+ status = acpi_db_display_objects("METHOD", acpi_gbl_db_args[1]);
+ break;
+
+ case CMD_NAMESPACE:
+
+ acpi_db_dump_namespace(acpi_gbl_db_args[1],
+ acpi_gbl_db_args[2]);
+ break;
+
+ case CMD_NOTIFY:
+
+ temp = strtoul(acpi_gbl_db_args[2], NULL, 0);
+ acpi_db_send_notify(acpi_gbl_db_args[1], temp);
+ break;
+
+ case CMD_OBJECTS:
+
+ acpi_ut_strupr(acpi_gbl_db_args[1]);
+ status =
+ acpi_db_display_objects(acpi_gbl_db_args[1],
+ acpi_gbl_db_args[2]);
+ break;
+
+ case CMD_OSI:
+
+ acpi_db_display_interfaces(acpi_gbl_db_args[1],
+ acpi_gbl_db_args[2]);
+ break;
+
+ case CMD_OWNER:
+
+ acpi_db_dump_namespace_by_owner(acpi_gbl_db_args[1],
+ acpi_gbl_db_args[2]);
+ break;
+
+ case CMD_PATHS:
+
+ acpi_db_dump_namespace_paths();
+ break;
+
+ case CMD_PREFIX:
+
+ acpi_db_set_scope(acpi_gbl_db_args[1]);
+ break;
+
+ case CMD_REFERENCES:
+
+ acpi_db_find_references(acpi_gbl_db_args[1]);
+ break;
+
+ case CMD_RESOURCES:
+
+ acpi_db_display_resources(acpi_gbl_db_args[1]);
+ break;
+
+ case CMD_RESULTS:
+
+ acpi_db_display_results();
+ break;
+
+ case CMD_SET:
+
+ acpi_db_set_method_data(acpi_gbl_db_args[1],
+ acpi_gbl_db_args[2],
+ acpi_gbl_db_args[3]);
+ break;
+
+ case CMD_STATS:
+
+ status = acpi_db_display_statistics(acpi_gbl_db_args[1]);
+ break;
+
+ case CMD_STOP:
+
+ return (AE_NOT_IMPLEMENTED);
+
+ case CMD_TABLES:
+
+ acpi_db_display_table_info(acpi_gbl_db_args[1]);
+ break;
+
+ case CMD_TEMPLATE:
+
+ acpi_db_display_template(acpi_gbl_db_args[1]);
+ break;
+
+ case CMD_TRACE:
+
+ acpi_db_trace(acpi_gbl_db_args[1], acpi_gbl_db_args[2],
+ acpi_gbl_db_args[3]);
+ break;
+
+ case CMD_TREE:
+
+ acpi_db_display_calling_tree();
+ break;
+
+ case CMD_TYPE:
+
+ acpi_db_display_object_type(acpi_gbl_db_args[1]);
+ break;
+
+#ifdef ACPI_APPLICATION
+
+ /* Hardware simulation commands. */
+
+ case CMD_ENABLEACPI:
+#if (!ACPI_REDUCED_HARDWARE)
+
+ status = acpi_enable();
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("AcpiEnable failed (Status=%X)\n",
+ status);
+ return (status);
+ }
+#endif /* !ACPI_REDUCED_HARDWARE */
+ break;
+
+ case CMD_EVENT:
+
+ acpi_os_printf("Event command not implemented\n");
+ break;
+
+ case CMD_GPE:
+
+ acpi_db_generate_gpe(acpi_gbl_db_args[1], acpi_gbl_db_args[2]);
+ break;
+
+ case CMD_GPES:
+
+ acpi_db_display_gpes();
+ break;
+
+ case CMD_SCI:
+
+ acpi_db_generate_sci();
+ break;
+
+ case CMD_SLEEP:
+
+ status = acpi_db_sleep(acpi_gbl_db_args[1]);
+ break;
+
+ /* File I/O commands. */
+
+ case CMD_CLOSE:
+
+ acpi_db_close_debug_file();
+ break;
+
+ case CMD_LOAD:{
+ struct acpi_new_table_desc *list_head = NULL;
+
+ status =
+ ac_get_all_tables_from_file(acpi_gbl_db_args[1],
+ ACPI_GET_ALL_TABLES,
+ &list_head);
+ if (ACPI_SUCCESS(status)) {
+ acpi_db_load_tables(list_head);
+ }
+ }
+ break;
+
+ case CMD_OPEN:
+
+ acpi_db_open_debug_file(acpi_gbl_db_args[1]);
+ break;
+
+ /* User space commands. */
+
+ case CMD_TERMINATE:
+
+ acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT);
+ acpi_ut_subsystem_shutdown();
+
+ /*
+ * TBD: [Restructure] Need some way to re-initialize without
+ * re-creating the semaphores!
+ */
+
+ acpi_gbl_db_terminate_loop = TRUE;
+ /* acpi_initialize (NULL); */
+ break;
+
+ case CMD_BACKGROUND:
+
+ acpi_db_create_execution_thread(acpi_gbl_db_args[1],
+ &acpi_gbl_db_args[2],
+ &acpi_gbl_db_arg_types[2]);
+ break;
+
+ case CMD_THREADS:
+
+ acpi_db_create_execution_threads(acpi_gbl_db_args[1],
+ acpi_gbl_db_args[2],
+ acpi_gbl_db_args[3]);
+ break;
+
+ /* Debug test commands. */
+
+ case CMD_PREDEFINED:
+
+ acpi_db_check_predefined_names();
+ break;
+
+ case CMD_TEST:
+
+ acpi_db_execute_test(acpi_gbl_db_args[1]);
+ break;
+
+ case CMD_UNLOAD:
+
+ acpi_db_unload_acpi_table(acpi_gbl_db_args[1]);
+ break;
+#endif
+
+ case CMD_EXIT:
+ case CMD_QUIT:
+
+ if (op) {
+ acpi_os_printf("Method execution terminated\n");
+ return (AE_CTRL_TERMINATE);
+ }
+
+ if (!acpi_gbl_db_output_to_file) {
+ acpi_dbg_level = ACPI_DEBUG_DEFAULT;
+ }
+#ifdef ACPI_APPLICATION
+ acpi_db_close_debug_file();
+#endif
+ acpi_gbl_db_terminate_loop = TRUE;
+ return (AE_CTRL_TERMINATE);
+
+ case CMD_NOT_FOUND:
+ default:
+
+ acpi_os_printf("%s: unknown command\n", acpi_gbl_db_args[0]);
+ return (AE_CTRL_TRUE);
+ }
+
+ if (ACPI_SUCCESS(status)) {
+ status = AE_CTRL_TRUE;
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_execute_thread
+ *
+ * PARAMETERS: context - Not used
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Debugger execute thread. Waits for a command line, then
+ * simply dispatches it.
+ *
+ ******************************************************************************/
+
+void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context)
+{
+
+ (void)acpi_db_user_commands();
+ acpi_gbl_db_threads_terminated = TRUE;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_user_commands
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Command line execution for the AML debugger. Commands are
+ * matched and dispatched here.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_db_user_commands(void)
+{
+ acpi_status status = AE_OK;
+
+ acpi_os_printf("\n");
+
+ /* TBD: [Restructure] Need a separate command line buffer for step mode */
+
+ while (!acpi_gbl_db_terminate_loop) {
+
+ /* Wait the readiness of the command */
+
+ status = acpi_os_wait_command_ready();
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+
+ /* Just call to the command line interpreter */
+
+ acpi_gbl_method_executing = FALSE;
+ acpi_gbl_step_to_next_call = FALSE;
+
+ (void)acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL,
+ NULL);
+
+ /* Notify the completion of the command */
+
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+ }
+
+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status, "While parsing command line"));
+ }
+ return (status);
+}
diff --git a/drivers/acpi/acpica/dbmethod.c b/drivers/acpi/acpica/dbmethod.c
new file mode 100644
index 000000000..889d13828
--- /dev/null
+++ b/drivers/acpi/acpica/dbmethod.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbmethod - Debug commands for control methods
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdispat.h"
+#include "acnamesp.h"
+#include "acdebug.h"
+#include "acparser.h"
+#include "acpredef.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbmethod")
+
+/* Local prototypes */
+static acpi_status
+acpi_db_walk_for_execute(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value);
+
+static acpi_status acpi_db_evaluate_object(struct acpi_namespace_node *node);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_set_method_breakpoint
+ *
+ * PARAMETERS: location - AML offset of breakpoint
+ * walk_state - Current walk info
+ * op - Current Op (from parse walk)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Set a breakpoint in a control method at the specified
+ * AML offset
+ *
+ ******************************************************************************/
+
+void
+acpi_db_set_method_breakpoint(char *location,
+ struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ u32 address;
+ u32 aml_offset;
+
+ if (!op) {
+ acpi_os_printf("There is no method currently executing\n");
+ return;
+ }
+
+ /* Get and verify the breakpoint address */
+
+ address = strtoul(location, NULL, 16);
+ aml_offset = (u32)ACPI_PTR_DIFF(op->common.aml,
+ walk_state->parser_state.aml_start);
+ if (address <= aml_offset) {
+ acpi_os_printf("Breakpoint %X is beyond current address %X\n",
+ address, aml_offset);
+ }
+
+ /* Save breakpoint in current walk */
+
+ walk_state->user_breakpoint = address;
+ acpi_os_printf("Breakpoint set at AML offset %X\n", address);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_set_method_call_breakpoint
+ *
+ * PARAMETERS: op - Current Op (from parse walk)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Set a breakpoint in a control method at the specified
+ * AML offset
+ *
+ ******************************************************************************/
+
+void acpi_db_set_method_call_breakpoint(union acpi_parse_object *op)
+{
+
+ if (!op) {
+ acpi_os_printf("There is no method currently executing\n");
+ return;
+ }
+
+ acpi_gbl_step_to_next_call = TRUE;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_set_method_data
+ *
+ * PARAMETERS: type_arg - L for local, A for argument
+ * index_arg - which one
+ * value_arg - Value to set.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Set a local or argument for the running control method.
+ * NOTE: only object supported is Number.
+ *
+ ******************************************************************************/
+
+void acpi_db_set_method_data(char *type_arg, char *index_arg, char *value_arg)
+{
+ char type;
+ u32 index;
+ u32 value;
+ struct acpi_walk_state *walk_state;
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ /* Validate type_arg */
+
+ acpi_ut_strupr(type_arg);
+ type = type_arg[0];
+ if ((type != 'L') && (type != 'A') && (type != 'N')) {
+ acpi_os_printf("Invalid SET operand: %s\n", type_arg);
+ return;
+ }
+
+ value = strtoul(value_arg, NULL, 16);
+
+ if (type == 'N') {
+ node = acpi_db_convert_to_node(index_arg);
+ if (!node) {
+ return;
+ }
+
+ if (node->type != ACPI_TYPE_INTEGER) {
+ acpi_os_printf("Can only set Integer nodes\n");
+ return;
+ }
+ obj_desc = node->object;
+ obj_desc->integer.value = value;
+ return;
+ }
+
+ /* Get the index and value */
+
+ index = strtoul(index_arg, NULL, 16);
+
+ walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list);
+ if (!walk_state) {
+ acpi_os_printf("There is no method currently executing\n");
+ return;
+ }
+
+ /* Create and initialize the new object */
+
+ obj_desc = acpi_ut_create_integer_object((u64)value);
+ if (!obj_desc) {
+ acpi_os_printf("Could not create an internal object\n");
+ return;
+ }
+
+ /* Store the new object into the target */
+
+ switch (type) {
+ case 'A':
+
+ /* Set a method argument */
+
+ if (index > ACPI_METHOD_MAX_ARG) {
+ acpi_os_printf("Arg%u - Invalid argument name\n",
+ index);
+ goto cleanup;
+ }
+
+ status = acpi_ds_store_object_to_local(ACPI_REFCLASS_ARG,
+ index, obj_desc,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ obj_desc = walk_state->arguments[index].object;
+
+ acpi_os_printf("Arg%u: ", index);
+ acpi_db_display_internal_object(obj_desc, walk_state);
+ break;
+
+ case 'L':
+
+ /* Set a method local */
+
+ if (index > ACPI_METHOD_MAX_LOCAL) {
+ acpi_os_printf
+ ("Local%u - Invalid local variable name\n", index);
+ goto cleanup;
+ }
+
+ status = acpi_ds_store_object_to_local(ACPI_REFCLASS_LOCAL,
+ index, obj_desc,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ obj_desc = walk_state->local_variables[index].object;
+
+ acpi_os_printf("Local%u: ", index);
+ acpi_db_display_internal_object(obj_desc, walk_state);
+ break;
+
+ default:
+
+ break;
+ }
+
+cleanup:
+ acpi_ut_remove_reference(obj_desc);
+}
+
+#ifdef ACPI_DISASSEMBLER
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_disassemble_aml
+ *
+ * PARAMETERS: statements - Number of statements to disassemble
+ * op - Current Op (from parse walk)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display disassembled AML (ASL) starting from Op for the number
+ * of statements specified.
+ *
+ ******************************************************************************/
+
+void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op)
+{
+ u32 num_statements = 8;
+
+ if (!op) {
+ acpi_os_printf("There is no method currently executing\n");
+ return;
+ }
+
+ if (statements) {
+ num_statements = strtoul(statements, NULL, 0);
+ }
+
+ acpi_dm_disassemble(NULL, op, num_statements);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_disassemble_method
+ *
+ * PARAMETERS: name - Name of control method
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display disassembled AML (ASL) starting from Op for the number
+ * of statements specified.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_db_disassemble_method(char *name)
+{
+ acpi_status status;
+ union acpi_parse_object *op;
+ struct acpi_walk_state *walk_state;
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *method;
+
+ method = acpi_db_convert_to_node(name);
+ if (!method) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ if (method->type != ACPI_TYPE_METHOD) {
+ ACPI_ERROR((AE_INFO, "%s (%s): Object must be a control method",
+ name, acpi_ut_get_type_name(method->type)));
+ return (AE_BAD_PARAMETER);
+ }
+
+ obj_desc = method->object;
+
+ op = acpi_ps_create_scope_op(obj_desc->method.aml_start);
+ if (!op) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Create and initialize a new walk state */
+
+ walk_state = acpi_ds_create_walk_state(0, op, NULL, NULL);
+ if (!walk_state) {
+ return (AE_NO_MEMORY);
+ }
+
+ status = acpi_ds_init_aml_walk(walk_state, op, NULL,
+ obj_desc->method.aml_start,
+ obj_desc->method.aml_length, NULL,
+ ACPI_IMODE_LOAD_PASS1);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status = acpi_ut_allocate_owner_id(&obj_desc->method.owner_id);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ walk_state->owner_id = obj_desc->method.owner_id;
+
+ /* Push start scope on scope stack and make it current */
+
+ status = acpi_ds_scope_stack_push(method, method->type, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Parse the entire method AML including deferred operators */
+
+ walk_state->parse_flags &= ~ACPI_PARSE_DELETE_TREE;
+ walk_state->parse_flags |= ACPI_PARSE_DISASSEMBLE;
+
+ status = acpi_ps_parse_aml(walk_state);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ (void)acpi_dm_parse_deferred_ops(op);
+
+ /* Now we can disassemble the method */
+
+ acpi_gbl_dm_opt_verbose = FALSE;
+ acpi_dm_disassemble(NULL, op, 0);
+ acpi_gbl_dm_opt_verbose = TRUE;
+
+ acpi_ps_delete_parse_tree(op);
+
+ /* Method cleanup */
+
+ acpi_ns_delete_namespace_subtree(method);
+ acpi_ns_delete_namespace_by_owner(obj_desc->method.owner_id);
+ acpi_ut_release_owner_id(&obj_desc->method.owner_id);
+ return (AE_OK);
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_evaluate_object
+ *
+ * PARAMETERS: node - Namespace node for the object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Main execution function for the Evaluate/Execute/All debugger
+ * commands.
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_db_evaluate_object(struct acpi_namespace_node *node)
+{
+ char *pathname;
+ u32 i;
+ struct acpi_device_info *obj_info;
+ struct acpi_object_list param_objects;
+ union acpi_object params[ACPI_METHOD_NUM_ARGS];
+ struct acpi_buffer return_obj;
+ acpi_status status;
+
+ pathname = acpi_ns_get_external_pathname(node);
+ if (!pathname) {
+ return (AE_OK);
+ }
+
+ /* Get the object info for number of method parameters */
+
+ status = acpi_get_object_info(node, &obj_info);
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(pathname);
+ return (status);
+ }
+
+ param_objects.pointer = NULL;
+ param_objects.count = 0;
+
+ if (obj_info->type == ACPI_TYPE_METHOD) {
+
+ /* Setup default parameters */
+
+ for (i = 0; i < obj_info->param_count; i++) {
+ params[i].type = ACPI_TYPE_INTEGER;
+ params[i].integer.value = 1;
+ }
+
+ param_objects.pointer = params;
+ param_objects.count = obj_info->param_count;
+ }
+
+ ACPI_FREE(obj_info);
+ return_obj.pointer = NULL;
+ return_obj.length = ACPI_ALLOCATE_BUFFER;
+
+ /* Do the actual method execution */
+
+ acpi_gbl_method_executing = TRUE;
+
+ status = acpi_evaluate_object(node, NULL, &param_objects, &return_obj);
+ acpi_gbl_method_executing = FALSE;
+
+ acpi_os_printf("%-32s returned %s\n", pathname,
+ acpi_format_exception(status));
+ if (return_obj.length) {
+ acpi_os_printf("Evaluation of %s returned object %p, "
+ "external buffer length %X\n",
+ pathname, return_obj.pointer,
+ (u32)return_obj.length);
+
+ acpi_db_dump_external_object(return_obj.pointer, 1);
+ acpi_os_printf("\n");
+ }
+
+ ACPI_FREE(pathname);
+
+ /* Ignore status from method execution */
+
+ return (AE_OK);
+
+ /* Update count, check if we have executed enough methods */
+
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_walk_for_execute
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Batch execution function. Evaluates all "predefined" objects --
+ * the nameseg begins with an underscore.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_walk_for_execute(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value)
+{
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+ struct acpi_db_execute_walk *info =
+ (struct acpi_db_execute_walk *)context;
+ acpi_status status;
+ const union acpi_predefined_info *predefined;
+
+ predefined = acpi_ut_match_predefined_method(node->name.ascii);
+ if (!predefined) {
+ return (AE_OK);
+ }
+
+ if (node->type == ACPI_TYPE_LOCAL_SCOPE) {
+ return (AE_OK);
+ }
+
+ acpi_db_evaluate_object(node);
+
+ /* Ignore status from object evaluation */
+
+ status = AE_OK;
+
+ /* Update count, check if we have executed enough methods */
+
+ info->count++;
+ if (info->count >= info->max_count) {
+ status = AE_CTRL_TERMINATE;
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_walk_for_execute_all
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Batch execution function. Evaluates all objects whose path ends
+ * with the nameseg "Info->NameSeg". Used for the "ALL" command.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_walk_for_execute_all(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+ struct acpi_db_execute_walk *info =
+ (struct acpi_db_execute_walk *)context;
+ acpi_status status;
+
+ if (!ACPI_COMPARE_NAMESEG(node->name.ascii, info->name_seg)) {
+ return (AE_OK);
+ }
+
+ if (node->type == ACPI_TYPE_LOCAL_SCOPE) {
+ return (AE_OK);
+ }
+
+ /* Now evaluate the input object (node) */
+
+ acpi_db_evaluate_object(node);
+
+ /* Ignore status from method execution */
+
+ status = AE_OK;
+
+ /* Update count of executed methods/objects */
+
+ info->count++;
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_evaluate_predefined_names
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Namespace batch execution. Execute predefined names in the
+ * namespace, up to the max count, if specified.
+ *
+ ******************************************************************************/
+
+void acpi_db_evaluate_predefined_names(void)
+{
+ struct acpi_db_execute_walk info;
+
+ info.count = 0;
+ info.max_count = ACPI_UINT32_MAX;
+
+ /* Search all nodes in namespace */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_db_walk_for_execute,
+ NULL, (void *)&info, NULL);
+
+ acpi_os_printf("Evaluated %u predefined names in the namespace\n",
+ info.count);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_evaluate_all
+ *
+ * PARAMETERS: none_acpi_gbl_db_method_info
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Namespace batch execution. Implements the "ALL" command.
+ * Execute all namepaths whose final nameseg matches the
+ * input nameseg.
+ *
+ ******************************************************************************/
+
+void acpi_db_evaluate_all(char *name_seg)
+{
+ struct acpi_db_execute_walk info;
+
+ info.count = 0;
+ info.max_count = ACPI_UINT32_MAX;
+ ACPI_COPY_NAMESEG(info.name_seg, name_seg);
+ info.name_seg[ACPI_NAMESEG_SIZE] = 0;
+
+ /* Search all nodes in namespace */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_db_walk_for_execute_all,
+ NULL, (void *)&info, NULL);
+
+ acpi_os_printf("Evaluated %u names in the namespace\n", info.count);
+}
diff --git a/drivers/acpi/acpica/dbnames.c b/drivers/acpi/acpica/dbnames.c
new file mode 100644
index 000000000..b91155ea9
--- /dev/null
+++ b/drivers/acpi/acpica/dbnames.c
@@ -0,0 +1,1035 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbnames - Debugger commands for the acpi namespace
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acdebug.h"
+#include "acpredef.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbnames")
+
+/* Local prototypes */
+static acpi_status
+acpi_db_walk_and_match_name(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value);
+
+static acpi_status
+acpi_db_walk_for_predefined_names(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value);
+
+static acpi_status
+acpi_db_walk_for_specific_objects(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value);
+
+static acpi_status
+acpi_db_walk_for_object_counts(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value);
+
+static acpi_status
+acpi_db_integrity_walk(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value);
+
+static acpi_status
+acpi_db_walk_for_references(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value);
+
+static acpi_status
+acpi_db_bus_walk(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value);
+
+/*
+ * Arguments for the Objects command
+ * These object types map directly to the ACPI_TYPES
+ */
+static struct acpi_db_argument_info acpi_db_object_types[] = {
+ {"ANY"},
+ {"INTEGERS"},
+ {"STRINGS"},
+ {"BUFFERS"},
+ {"PACKAGES"},
+ {"FIELDS"},
+ {"DEVICES"},
+ {"EVENTS"},
+ {"METHODS"},
+ {"MUTEXES"},
+ {"REGIONS"},
+ {"POWERRESOURCES"},
+ {"PROCESSORS"},
+ {"THERMALZONES"},
+ {"BUFFERFIELDS"},
+ {"DDBHANDLES"},
+ {"DEBUG"},
+ {"REGIONFIELDS"},
+ {"BANKFIELDS"},
+ {"INDEXFIELDS"},
+ {"REFERENCES"},
+ {"ALIASES"},
+ {"METHODALIASES"},
+ {"NOTIFY"},
+ {"ADDRESSHANDLER"},
+ {"RESOURCE"},
+ {"RESOURCEFIELD"},
+ {"SCOPES"},
+ {NULL} /* Must be null terminated */
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_set_scope
+ *
+ * PARAMETERS: name - New scope path
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Set the "current scope" as maintained by this utility.
+ * The scope is used as a prefix to ACPI paths.
+ *
+ ******************************************************************************/
+
+void acpi_db_set_scope(char *name)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ if (!name || name[0] == 0) {
+ acpi_os_printf("Current scope: %s\n", acpi_gbl_db_scope_buf);
+ return;
+ }
+
+ acpi_db_prep_namestring(name);
+
+ if (ACPI_IS_ROOT_PREFIX(name[0])) {
+
+ /* Validate new scope from the root */
+
+ status = acpi_ns_get_node(acpi_gbl_root_node, name,
+ ACPI_NS_NO_UPSEARCH, &node);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ acpi_gbl_db_scope_buf[0] = 0;
+ } else {
+ /* Validate new scope relative to old scope */
+
+ status = acpi_ns_get_node(acpi_gbl_db_scope_node, name,
+ ACPI_NS_NO_UPSEARCH, &node);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+ }
+
+ /* Build the final pathname */
+
+ if (acpi_ut_safe_strcat
+ (acpi_gbl_db_scope_buf, sizeof(acpi_gbl_db_scope_buf), name)) {
+ status = AE_BUFFER_OVERFLOW;
+ goto error_exit;
+ }
+
+ if (acpi_ut_safe_strcat
+ (acpi_gbl_db_scope_buf, sizeof(acpi_gbl_db_scope_buf), "\\")) {
+ status = AE_BUFFER_OVERFLOW;
+ goto error_exit;
+ }
+
+ acpi_gbl_db_scope_node = node;
+ acpi_os_printf("New scope: %s\n", acpi_gbl_db_scope_buf);
+ return;
+
+error_exit:
+
+ acpi_os_printf("Could not attach scope: %s, %s\n",
+ name, acpi_format_exception(status));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_dump_namespace
+ *
+ * PARAMETERS: start_arg - Node to begin namespace dump
+ * depth_arg - Maximum tree depth to be dumped
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump entire namespace or a subtree. Each node is displayed
+ * with type and other information.
+ *
+ ******************************************************************************/
+
+void acpi_db_dump_namespace(char *start_arg, char *depth_arg)
+{
+ acpi_handle subtree_entry = acpi_gbl_root_node;
+ u32 max_depth = ACPI_UINT32_MAX;
+
+ /* No argument given, just start at the root and dump entire namespace */
+
+ if (start_arg) {
+ subtree_entry = acpi_db_convert_to_node(start_arg);
+ if (!subtree_entry) {
+ return;
+ }
+
+ /* Now we can check for the depth argument */
+
+ if (depth_arg) {
+ max_depth = strtoul(depth_arg, NULL, 0);
+ }
+ }
+
+ acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
+
+ if (((struct acpi_namespace_node *)subtree_entry)->parent) {
+ acpi_os_printf("ACPI Namespace (from %4.4s (%p) subtree):\n",
+ ((struct acpi_namespace_node *)subtree_entry)->
+ name.ascii, subtree_entry);
+ } else {
+ acpi_os_printf("ACPI Namespace (from %s):\n",
+ ACPI_NAMESPACE_ROOT);
+ }
+
+ /* Display the subtree */
+
+ acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT);
+ acpi_ns_dump_objects(ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY, max_depth,
+ ACPI_OWNER_ID_MAX, subtree_entry);
+ acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_dump_namespace_paths
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump entire namespace with full object pathnames and object
+ * type information. Alternative to "namespace" command.
+ *
+ ******************************************************************************/
+
+void acpi_db_dump_namespace_paths(void)
+{
+
+ acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
+ acpi_os_printf("ACPI Namespace (from root):\n");
+
+ /* Display the entire namespace */
+
+ acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT);
+ acpi_ns_dump_object_paths(ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY,
+ ACPI_UINT32_MAX, ACPI_OWNER_ID_MAX,
+ acpi_gbl_root_node);
+
+ acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_dump_namespace_by_owner
+ *
+ * PARAMETERS: owner_arg - Owner ID whose nodes will be displayed
+ * depth_arg - Maximum tree depth to be dumped
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump elements of the namespace that are owned by the owner_id.
+ *
+ ******************************************************************************/
+
+void acpi_db_dump_namespace_by_owner(char *owner_arg, char *depth_arg)
+{
+ acpi_handle subtree_entry = acpi_gbl_root_node;
+ u32 max_depth = ACPI_UINT32_MAX;
+ acpi_owner_id owner_id;
+
+ owner_id = (acpi_owner_id)strtoul(owner_arg, NULL, 0);
+
+ /* Now we can check for the depth argument */
+
+ if (depth_arg) {
+ max_depth = strtoul(depth_arg, NULL, 0);
+ }
+
+ acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
+ acpi_os_printf("ACPI Namespace by owner %X:\n", owner_id);
+
+ /* Display the subtree */
+
+ acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT);
+ acpi_ns_dump_objects(ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY, max_depth,
+ owner_id, subtree_entry);
+ acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_walk_and_match_name
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Find a particular name/names within the namespace. Wildcards
+ * are supported -- '?' matches any character.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_walk_and_match_name(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ acpi_status status;
+ char *requested_name = (char *)context;
+ u32 i;
+ struct acpi_buffer buffer;
+ struct acpi_walk_info info;
+
+ /* Check for a name match */
+
+ for (i = 0; i < 4; i++) {
+
+ /* Wildcard support */
+
+ if ((requested_name[i] != '?') &&
+ (requested_name[i] != ((struct acpi_namespace_node *)
+ obj_handle)->name.ascii[i])) {
+
+ /* No match, just exit */
+
+ return (AE_OK);
+ }
+ }
+
+ /* Get the full pathname to this object */
+
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could Not get pathname for object %p\n",
+ obj_handle);
+ } else {
+ info.count = 0;
+ info.owner_id = ACPI_OWNER_ID_MAX;
+ info.debug_level = ACPI_UINT32_MAX;
+ info.display_type = ACPI_DISPLAY_SUMMARY | ACPI_DISPLAY_SHORT;
+
+ acpi_os_printf("%32s", (char *)buffer.pointer);
+ (void)acpi_ns_dump_one_object(obj_handle, nesting_level, &info,
+ NULL);
+ ACPI_FREE(buffer.pointer);
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_find_name_in_namespace
+ *
+ * PARAMETERS: name_arg - The 4-character ACPI name to find.
+ * wildcards are supported.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Search the namespace for a given name (with wildcards)
+ *
+ ******************************************************************************/
+
+acpi_status acpi_db_find_name_in_namespace(char *name_arg)
+{
+ char acpi_name[5] = "____";
+ char *acpi_name_ptr = acpi_name;
+
+ if (strlen(name_arg) > ACPI_NAMESEG_SIZE) {
+ acpi_os_printf("Name must be no longer than 4 characters\n");
+ return (AE_OK);
+ }
+
+ /* Pad out name with underscores as necessary to create a 4-char name */
+
+ acpi_ut_strupr(name_arg);
+ while (*name_arg) {
+ *acpi_name_ptr = *name_arg;
+ acpi_name_ptr++;
+ name_arg++;
+ }
+
+ /* Walk the namespace from the root */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_db_walk_and_match_name,
+ NULL, acpi_name, NULL);
+
+ acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_walk_for_predefined_names
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Detect and display predefined ACPI names (names that start with
+ * an underscore)
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_walk_for_predefined_names(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+ u32 *count = (u32 *)context;
+ const union acpi_predefined_info *predefined;
+ const union acpi_predefined_info *package = NULL;
+ char *pathname;
+ char string_buffer[48];
+
+ predefined = acpi_ut_match_predefined_method(node->name.ascii);
+ if (!predefined) {
+ return (AE_OK);
+ }
+
+ pathname = acpi_ns_get_normalized_pathname(node, TRUE);
+ if (!pathname) {
+ return (AE_OK);
+ }
+
+ /* If method returns a package, the info is in the next table entry */
+
+ if (predefined->info.expected_btypes & ACPI_RTYPE_PACKAGE) {
+ package = predefined + 1;
+ }
+
+ acpi_ut_get_expected_return_types(string_buffer,
+ predefined->info.expected_btypes);
+
+ acpi_os_printf("%-32s Arguments %X, Return Types: %s", pathname,
+ METHOD_GET_ARG_COUNT(predefined->info.argument_list),
+ string_buffer);
+
+ if (package) {
+ acpi_os_printf(" (PkgType %2.2X, ObjType %2.2X, Count %2.2X)",
+ package->ret_info.type,
+ package->ret_info.object_type1,
+ package->ret_info.count1);
+ }
+
+ acpi_os_printf("\n");
+
+ /* Check that the declared argument count matches the ACPI spec */
+
+ acpi_ns_check_acpi_compliance(pathname, node, predefined);
+
+ ACPI_FREE(pathname);
+ (*count)++;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_check_predefined_names
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Validate all predefined names in the namespace
+ *
+ ******************************************************************************/
+
+void acpi_db_check_predefined_names(void)
+{
+ u32 count = 0;
+
+ /* Search all nodes in namespace */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ acpi_db_walk_for_predefined_names, NULL,
+ (void *)&count, NULL);
+
+ acpi_os_printf("Found %u predefined names in the namespace\n", count);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_walk_for_object_counts
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Display short info about objects in the namespace
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_walk_for_object_counts(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ struct acpi_object_info *info = (struct acpi_object_info *)context;
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+
+ if (node->type > ACPI_TYPE_NS_NODE_MAX) {
+ acpi_os_printf("[%4.4s]: Unknown object type %X\n",
+ node->name.ascii, node->type);
+ } else {
+ info->types[node->type]++;
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_walk_for_fields
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Display short info about objects in the namespace
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_walk_for_fields(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value)
+{
+ union acpi_object *ret_value;
+ struct acpi_region_walk_info *info =
+ (struct acpi_region_walk_info *)context;
+ struct acpi_buffer buffer;
+ acpi_status status;
+ struct acpi_namespace_node *node = acpi_ns_validate_handle(obj_handle);
+
+ if (!node) {
+ return (AE_OK);
+ }
+ if (node->object->field.region_obj->region.space_id !=
+ info->address_space_id) {
+ return (AE_OK);
+ }
+
+ info->count++;
+
+ /* Get and display the full pathname to this object */
+
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could Not get pathname for object %p\n",
+ obj_handle);
+ return (AE_OK);
+ }
+
+ acpi_os_printf("%s ", (char *)buffer.pointer);
+ ACPI_FREE(buffer.pointer);
+
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ acpi_evaluate_object(obj_handle, NULL, NULL, &buffer);
+
+ /*
+ * Since this is a field unit, surround the output in braces
+ */
+ acpi_os_printf("{");
+
+ ret_value = (union acpi_object *)buffer.pointer;
+ switch (ret_value->type) {
+ case ACPI_TYPE_INTEGER:
+
+ acpi_os_printf("%8.8X%8.8X",
+ ACPI_FORMAT_UINT64(ret_value->integer.value));
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ acpi_ut_dump_buffer(ret_value->buffer.pointer,
+ ret_value->buffer.length,
+ DB_DISPLAY_DATA_ONLY | DB_BYTE_DISPLAY, 0);
+ break;
+
+ default:
+
+ break;
+ }
+ acpi_os_printf("}\n");
+
+ ACPI_FREE(buffer.pointer);
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_walk_for_specific_objects
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Display short info about objects in the namespace
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_walk_for_specific_objects(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ struct acpi_walk_info *info = (struct acpi_walk_info *)context;
+ struct acpi_buffer buffer;
+ acpi_status status;
+
+ info->count++;
+
+ /* Get and display the full pathname to this object */
+
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could Not get pathname for object %p\n",
+ obj_handle);
+ return (AE_OK);
+ }
+
+ acpi_os_printf("%32s", (char *)buffer.pointer);
+ ACPI_FREE(buffer.pointer);
+
+ /* Dump short info about the object */
+
+ (void)acpi_ns_dump_one_object(obj_handle, nesting_level, info, NULL);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_objects
+ *
+ * PARAMETERS: obj_type_arg - Type of object to display
+ * display_count_arg - Max depth to display
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display objects in the namespace of the requested type
+ *
+ ******************************************************************************/
+
+acpi_status acpi_db_display_objects(char *obj_type_arg, char *display_count_arg)
+{
+ struct acpi_walk_info info;
+ acpi_object_type type;
+ struct acpi_object_info *object_info;
+ u32 i;
+ u32 total_objects = 0;
+
+ /* No argument means display summary/count of all object types */
+
+ if (!obj_type_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,
+ ACPI_UINT32_MAX,
+ acpi_db_walk_for_object_counts, NULL,
+ (void *)object_info, NULL);
+
+ acpi_os_printf("\nSummary of namespace objects:\n\n");
+
+ for (i = 0; i < ACPI_TOTAL_TYPES; i++) {
+ acpi_os_printf("%8u %s\n", object_info->types[i],
+ acpi_ut_get_type_name(i));
+
+ total_objects += object_info->types[i];
+ }
+
+ acpi_os_printf("\n%8u Total namespace objects\n\n",
+ total_objects);
+
+ ACPI_FREE(object_info);
+ return (AE_OK);
+ }
+
+ /* Get the object type */
+
+ type = acpi_db_match_argument(obj_type_arg, acpi_db_object_types);
+ if (type == ACPI_TYPE_NOT_FOUND) {
+ acpi_os_printf("Invalid or unsupported argument\n");
+ return (AE_OK);
+ }
+
+ acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
+ acpi_os_printf
+ ("Objects of type [%s] defined in the current ACPI Namespace:\n",
+ acpi_ut_get_type_name(type));
+
+ acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT);
+
+ info.count = 0;
+ info.owner_id = ACPI_OWNER_ID_MAX;
+ info.debug_level = ACPI_UINT32_MAX;
+ info.display_type = ACPI_DISPLAY_SUMMARY | ACPI_DISPLAY_SHORT;
+
+ /* Walk the namespace from the root */
+
+ (void)acpi_walk_namespace(type, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
+ acpi_db_walk_for_specific_objects, NULL,
+ (void *)&info, NULL);
+
+ acpi_os_printf
+ ("\nFound %u objects of type [%s] in the current ACPI Namespace\n",
+ info.count, acpi_ut_get_type_name(type));
+
+ acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_fields
+ *
+ * PARAMETERS: obj_type_arg - Type of object to display
+ * display_count_arg - Max depth to display
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display objects in the namespace of the requested type
+ *
+ ******************************************************************************/
+
+acpi_status acpi_db_display_fields(u32 address_space_id)
+{
+ struct acpi_region_walk_info info;
+
+ info.count = 0;
+ info.owner_id = ACPI_OWNER_ID_MAX;
+ info.debug_level = ACPI_UINT32_MAX;
+ info.display_type = ACPI_DISPLAY_SUMMARY | ACPI_DISPLAY_SHORT;
+ info.address_space_id = address_space_id;
+
+ /* Walk the namespace from the root */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_LOCAL_REGION_FIELD,
+ ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
+ acpi_db_walk_for_fields, NULL, (void *)&info,
+ NULL);
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_integrity_walk
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Examine one NS node for valid values.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_integrity_walk(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value)
+{
+ struct acpi_integrity_info *info =
+ (struct acpi_integrity_info *)context;
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+ union acpi_operand_object *object;
+ u8 alias = TRUE;
+
+ info->nodes++;
+
+ /* Verify the NS node, and dereference aliases */
+
+ while (alias) {
+ if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) {
+ acpi_os_printf
+ ("Invalid Descriptor Type for Node %p [%s] - "
+ "is %2.2X should be %2.2X\n", node,
+ acpi_ut_get_descriptor_name(node),
+ ACPI_GET_DESCRIPTOR_TYPE(node),
+ ACPI_DESC_TYPE_NAMED);
+ return (AE_OK);
+ }
+
+ if ((node->type == ACPI_TYPE_LOCAL_ALIAS) ||
+ (node->type == ACPI_TYPE_LOCAL_METHOD_ALIAS)) {
+ node = (struct acpi_namespace_node *)node->object;
+ } else {
+ alias = FALSE;
+ }
+ }
+
+ if (node->type > ACPI_TYPE_LOCAL_MAX) {
+ acpi_os_printf("Invalid Object Type for Node %p, Type = %X\n",
+ node, node->type);
+ return (AE_OK);
+ }
+
+ if (!acpi_ut_valid_nameseg(node->name.ascii)) {
+ acpi_os_printf("Invalid AcpiName for Node %p\n", node);
+ return (AE_OK);
+ }
+
+ object = acpi_ns_get_attached_object(node);
+ if (object) {
+ info->objects++;
+ if (ACPI_GET_DESCRIPTOR_TYPE(object) != ACPI_DESC_TYPE_OPERAND) {
+ acpi_os_printf
+ ("Invalid Descriptor Type for Object %p [%s]\n",
+ object, acpi_ut_get_descriptor_name(object));
+ }
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_check_integrity
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Check entire namespace for data structure integrity
+ *
+ ******************************************************************************/
+
+void acpi_db_check_integrity(void)
+{
+ struct acpi_integrity_info info = { 0, 0 };
+
+ /* Search all nodes in namespace */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_db_integrity_walk, NULL,
+ (void *)&info, NULL);
+
+ acpi_os_printf("Verified %u namespace nodes with %u Objects\n",
+ info.nodes, info.objects);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_walk_for_references
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check if this namespace object refers to the target object
+ * that is passed in as the context value.
+ *
+ * Note: Currently doesn't check subobjects within the Node's object
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_walk_for_references(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ union acpi_operand_object *obj_desc =
+ (union acpi_operand_object *)context;
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+
+ /* Check for match against the namespace node itself */
+
+ if (node == (void *)obj_desc) {
+ acpi_os_printf("Object is a Node [%4.4s]\n",
+ acpi_ut_get_node_name(node));
+ }
+
+ /* Check for match against the object attached to the node */
+
+ if (acpi_ns_get_attached_object(node) == obj_desc) {
+ acpi_os_printf("Reference at Node->Object %p [%4.4s]\n",
+ node, acpi_ut_get_node_name(node));
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_find_references
+ *
+ * PARAMETERS: object_arg - String with hex value of the object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Search namespace for all references to the input object
+ *
+ ******************************************************************************/
+
+void acpi_db_find_references(char *object_arg)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_size address;
+
+ /* Convert string to object pointer */
+
+ address = strtoul(object_arg, NULL, 16);
+ obj_desc = ACPI_TO_POINTER(address);
+
+ /* Search all nodes in namespace */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_db_walk_for_references,
+ NULL, (void *)obj_desc, NULL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_bus_walk
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Display info about device objects that have a corresponding
+ * _PRT method.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_bus_walk(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value)
+{
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+ acpi_status status;
+ struct acpi_buffer buffer;
+ struct acpi_namespace_node *temp_node;
+ struct acpi_device_info *info;
+ u32 i;
+
+ if ((node->type != ACPI_TYPE_DEVICE) &&
+ (node->type != ACPI_TYPE_PROCESSOR)) {
+ return (AE_OK);
+ }
+
+ /* Exit if there is no _PRT under this device */
+
+ status = acpi_get_handle(node, METHOD_NAME__PRT,
+ ACPI_CAST_PTR(acpi_handle, &temp_node));
+ if (ACPI_FAILURE(status)) {
+ return (AE_OK);
+ }
+
+ /* Get the full path to this device object */
+
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could Not get pathname for object %p\n",
+ obj_handle);
+ return (AE_OK);
+ }
+
+ status = acpi_get_object_info(obj_handle, &info);
+ if (ACPI_FAILURE(status)) {
+ return (AE_OK);
+ }
+
+ /* Display the full path */
+
+ acpi_os_printf("%-32s Type %X", (char *)buffer.pointer, node->type);
+ ACPI_FREE(buffer.pointer);
+
+ if (info->flags & ACPI_PCI_ROOT_BRIDGE) {
+ acpi_os_printf(" - Is PCI Root Bridge");
+ }
+ acpi_os_printf("\n");
+
+ /* _PRT info */
+
+ acpi_os_printf("_PRT: %p\n", temp_node);
+
+ /* Dump _ADR, _HID, _UID, _CID */
+
+ if (info->valid & ACPI_VALID_ADR) {
+ acpi_os_printf("_ADR: %8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64(info->address));
+ } else {
+ acpi_os_printf("_ADR: <Not Present>\n");
+ }
+
+ if (info->valid & ACPI_VALID_HID) {
+ acpi_os_printf("_HID: %s\n", info->hardware_id.string);
+ } else {
+ acpi_os_printf("_HID: <Not Present>\n");
+ }
+
+ if (info->valid & ACPI_VALID_UID) {
+ acpi_os_printf("_UID: %s\n", info->unique_id.string);
+ } else {
+ acpi_os_printf("_UID: <Not Present>\n");
+ }
+
+ if (info->valid & ACPI_VALID_CID) {
+ for (i = 0; i < info->compatible_id_list.count; i++) {
+ acpi_os_printf("_CID: %s\n",
+ info->compatible_id_list.ids[i].string);
+ }
+ } else {
+ acpi_os_printf("_CID: <Not Present>\n");
+ }
+
+ ACPI_FREE(info);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_get_bus_info
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display info about system buses.
+ *
+ ******************************************************************************/
+
+void acpi_db_get_bus_info(void)
+{
+ /* Search all nodes in namespace */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_db_bus_walk, NULL, NULL,
+ NULL);
+}
diff --git a/drivers/acpi/acpica/dbobject.c b/drivers/acpi/acpica/dbobject.c
new file mode 100644
index 000000000..95ab91b35
--- /dev/null
+++ b/drivers/acpi/acpica/dbobject.c
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbobject - ACPI object decode and display
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acdebug.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbobject")
+
+/* Local prototypes */
+static void acpi_db_decode_node(struct acpi_namespace_node *node);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_dump_method_info
+ *
+ * PARAMETERS: status - Method execution status
+ * walk_state - Current state of the parse tree walk
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Called when a method has been aborted because of an error.
+ * Dumps the method execution stack, and the method locals/args,
+ * and disassembles the AML opcode that failed.
+ *
+ ******************************************************************************/
+
+void
+acpi_db_dump_method_info(acpi_status status, struct acpi_walk_state *walk_state)
+{
+ struct acpi_thread_state *thread;
+ struct acpi_namespace_node *node;
+
+ node = walk_state->method_node;
+
+ /* There are no locals or arguments for the module-level code case */
+
+ if (node == acpi_gbl_root_node) {
+ return;
+ }
+
+ /* Ignore control codes, they are not errors */
+
+ if (ACPI_CNTL_EXCEPTION(status)) {
+ return;
+ }
+
+ /* We may be executing a deferred opcode */
+
+ if (walk_state->deferred_node) {
+ acpi_os_printf("Executing subtree for Buffer/Package/Region\n");
+ return;
+ }
+
+ /*
+ * If there is no Thread, we are not actually executing a method.
+ * This can happen when the iASL compiler calls the interpreter
+ * to perform constant folding.
+ */
+ thread = walk_state->thread;
+ if (!thread) {
+ return;
+ }
+
+ /* Display the method locals and arguments */
+
+ acpi_os_printf("\n");
+ acpi_db_decode_locals(walk_state);
+ acpi_os_printf("\n");
+ acpi_db_decode_arguments(walk_state);
+ acpi_os_printf("\n");
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_decode_internal_object
+ *
+ * PARAMETERS: obj_desc - Object to be displayed
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Short display of an internal object. Numbers/Strings/Buffers.
+ *
+ ******************************************************************************/
+
+void acpi_db_decode_internal_object(union acpi_operand_object *obj_desc)
+{
+ u32 i;
+
+ if (!obj_desc) {
+ acpi_os_printf(" Uninitialized");
+ return;
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) {
+ acpi_os_printf(" %p [%s]", obj_desc,
+ acpi_ut_get_descriptor_name(obj_desc));
+ return;
+ }
+
+ acpi_os_printf(" %s", acpi_ut_get_object_type_name(obj_desc));
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ acpi_os_printf(" %8.8X%8.8X",
+ ACPI_FORMAT_UINT64(obj_desc->integer.value));
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ acpi_os_printf("(%u) \"%.60s",
+ obj_desc->string.length,
+ obj_desc->string.pointer);
+
+ if (obj_desc->string.length > 60) {
+ acpi_os_printf("...");
+ } else {
+ acpi_os_printf("\"");
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ acpi_os_printf("(%u)", obj_desc->buffer.length);
+ for (i = 0; (i < 8) && (i < obj_desc->buffer.length); i++) {
+ acpi_os_printf(" %2.2X", obj_desc->buffer.pointer[i]);
+ }
+ break;
+
+ default:
+
+ acpi_os_printf(" %p", obj_desc);
+ break;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_decode_node
+ *
+ * PARAMETERS: node - Object to be displayed
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Short display of a namespace node
+ *
+ ******************************************************************************/
+
+static void acpi_db_decode_node(struct acpi_namespace_node *node)
+{
+
+ acpi_os_printf("<Node> Name %4.4s",
+ acpi_ut_get_node_name(node));
+
+ if (node->flags & ANOBJ_METHOD_ARG) {
+ acpi_os_printf(" [Method Arg]");
+ }
+ if (node->flags & ANOBJ_METHOD_LOCAL) {
+ acpi_os_printf(" [Method Local]");
+ }
+
+ switch (node->type) {
+
+ /* These types have no attached object */
+
+ case ACPI_TYPE_DEVICE:
+
+ acpi_os_printf(" Device");
+ break;
+
+ case ACPI_TYPE_THERMAL:
+
+ acpi_os_printf(" Thermal Zone");
+ break;
+
+ default:
+
+ acpi_db_decode_internal_object(acpi_ns_get_attached_object
+ (node));
+ break;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_internal_object
+ *
+ * PARAMETERS: obj_desc - Object to be displayed
+ * walk_state - Current walk state
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Short display of an internal object
+ *
+ ******************************************************************************/
+
+void
+acpi_db_display_internal_object(union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+ u8 type;
+
+ acpi_os_printf("%p ", obj_desc);
+
+ if (!obj_desc) {
+ acpi_os_printf("<Null Object>\n");
+ return;
+ }
+
+ /* Decode the object type */
+
+ switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) {
+ case ACPI_DESC_TYPE_PARSER:
+
+ acpi_os_printf("<Parser> ");
+ break;
+
+ case ACPI_DESC_TYPE_NAMED:
+
+ acpi_db_decode_node((struct acpi_namespace_node *)obj_desc);
+ break;
+
+ case ACPI_DESC_TYPE_OPERAND:
+
+ type = obj_desc->common.type;
+ if (type > ACPI_TYPE_LOCAL_MAX) {
+ acpi_os_printf(" Type %X [Invalid Type]", (u32)type);
+ return;
+ }
+
+ /* Decode the ACPI object type */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ acpi_os_printf("[%s] ",
+ acpi_ut_get_reference_name(obj_desc));
+
+ /* Decode the reference */
+
+ switch (obj_desc->reference.class) {
+ case ACPI_REFCLASS_LOCAL:
+
+ acpi_os_printf("%X ",
+ obj_desc->reference.value);
+ if (walk_state) {
+ obj_desc = walk_state->local_variables
+ [obj_desc->reference.value].object;
+ acpi_os_printf("%p", obj_desc);
+ acpi_db_decode_internal_object
+ (obj_desc);
+ }
+ break;
+
+ case ACPI_REFCLASS_ARG:
+
+ acpi_os_printf("%X ",
+ obj_desc->reference.value);
+ if (walk_state) {
+ obj_desc = walk_state->arguments
+ [obj_desc->reference.value].object;
+ acpi_os_printf("%p", obj_desc);
+ acpi_db_decode_internal_object
+ (obj_desc);
+ }
+ break;
+
+ case ACPI_REFCLASS_INDEX:
+
+ switch (obj_desc->reference.target_type) {
+ case ACPI_TYPE_BUFFER_FIELD:
+
+ acpi_os_printf("%p",
+ obj_desc->reference.
+ object);
+ acpi_db_decode_internal_object
+ (obj_desc->reference.object);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ acpi_os_printf("%p",
+ obj_desc->reference.
+ where);
+ if (!obj_desc->reference.where) {
+ acpi_os_printf
+ (" Uninitialized WHERE pointer");
+ } else {
+ acpi_db_decode_internal_object(*
+ (obj_desc->
+ reference.
+ where));
+ }
+ break;
+
+ default:
+
+ acpi_os_printf
+ ("Unknown index target type");
+ break;
+ }
+ break;
+
+ case ACPI_REFCLASS_REFOF:
+
+ if (!obj_desc->reference.object) {
+ acpi_os_printf
+ ("Uninitialized reference subobject pointer");
+ break;
+ }
+
+ /* Reference can be to a Node or an Operand object */
+
+ switch (ACPI_GET_DESCRIPTOR_TYPE
+ (obj_desc->reference.object)) {
+ case ACPI_DESC_TYPE_NAMED:
+
+ acpi_db_decode_node(obj_desc->reference.
+ object);
+ break;
+
+ case ACPI_DESC_TYPE_OPERAND:
+
+ acpi_db_decode_internal_object
+ (obj_desc->reference.object);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case ACPI_REFCLASS_NAME:
+
+ acpi_db_decode_node(obj_desc->reference.node);
+ break;
+
+ case ACPI_REFCLASS_DEBUG:
+ case ACPI_REFCLASS_TABLE:
+
+ acpi_os_printf("\n");
+ break;
+
+ default: /* Unknown reference class */
+
+ acpi_os_printf("%2.2X\n",
+ obj_desc->reference.class);
+ break;
+ }
+ break;
+
+ default:
+
+ acpi_os_printf("<Obj> ");
+ acpi_db_decode_internal_object(obj_desc);
+ break;
+ }
+ break;
+
+ default:
+
+ acpi_os_printf("<Not a valid ACPI Object Descriptor> [%s]",
+ acpi_ut_get_descriptor_name(obj_desc));
+ break;
+ }
+
+ acpi_os_printf("\n");
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_decode_locals
+ *
+ * PARAMETERS: walk_state - State for current method
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display all locals for the currently running control method
+ *
+ ******************************************************************************/
+
+void acpi_db_decode_locals(struct acpi_walk_state *walk_state)
+{
+ u32 i;
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *node;
+ u8 display_locals = FALSE;
+
+ node = walk_state->method_node;
+
+ /* There are no locals for the module-level code case */
+
+ if (node == acpi_gbl_root_node) {
+ return;
+ }
+
+ if (!node) {
+ acpi_os_printf
+ ("No method node (Executing subtree for buffer or opregion)\n");
+ return;
+ }
+
+ if (node->type != ACPI_TYPE_METHOD) {
+ acpi_os_printf("Executing subtree for Buffer/Package/Region\n");
+ return;
+ }
+
+ /* Are any locals actually set? */
+
+ for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) {
+ obj_desc = walk_state->local_variables[i].object;
+ if (obj_desc) {
+ display_locals = TRUE;
+ break;
+ }
+ }
+
+ /* If any are set, only display the ones that are set */
+
+ if (display_locals) {
+ acpi_os_printf
+ ("\nInitialized Local Variables for Method [%4.4s]:\n",
+ acpi_ut_get_node_name(node));
+
+ for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) {
+ obj_desc = walk_state->local_variables[i].object;
+ if (obj_desc) {
+ acpi_os_printf(" Local%X: ", i);
+ acpi_db_display_internal_object(obj_desc,
+ walk_state);
+ }
+ }
+ } else {
+ acpi_os_printf
+ ("No Local Variables are initialized for Method [%4.4s]\n",
+ acpi_ut_get_node_name(node));
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_decode_arguments
+ *
+ * PARAMETERS: walk_state - State for current method
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display all arguments for the currently running control method
+ *
+ ******************************************************************************/
+
+void acpi_db_decode_arguments(struct acpi_walk_state *walk_state)
+{
+ u32 i;
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *node;
+ u8 display_args = FALSE;
+
+ node = walk_state->method_node;
+
+ /* There are no arguments for the module-level code case */
+
+ if (node == acpi_gbl_root_node) {
+ return;
+ }
+
+ if (!node) {
+ acpi_os_printf
+ ("No method node (Executing subtree for buffer or opregion)\n");
+ return;
+ }
+
+ if (node->type != ACPI_TYPE_METHOD) {
+ acpi_os_printf("Executing subtree for Buffer/Package/Region\n");
+ return;
+ }
+
+ /* Are any arguments actually set? */
+
+ for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++) {
+ obj_desc = walk_state->arguments[i].object;
+ if (obj_desc) {
+ display_args = TRUE;
+ break;
+ }
+ }
+
+ /* If any are set, only display the ones that are set */
+
+ if (display_args) {
+ acpi_os_printf("Initialized Arguments for Method [%4.4s]: "
+ "(%X arguments defined for method invocation)\n",
+ acpi_ut_get_node_name(node),
+ node->object->method.param_count);
+
+ for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++) {
+ obj_desc = walk_state->arguments[i].object;
+ if (obj_desc) {
+ acpi_os_printf(" Arg%u: ", i);
+ acpi_db_display_internal_object(obj_desc,
+ walk_state);
+ }
+ }
+ } else {
+ acpi_os_printf
+ ("No Arguments are initialized for method [%4.4s]\n",
+ acpi_ut_get_node_name(node));
+ }
+}
diff --git a/drivers/acpi/acpica/dbstats.c b/drivers/acpi/acpica/dbstats.c
new file mode 100644
index 000000000..3af88e702
--- /dev/null
+++ b/drivers/acpi/acpica/dbstats.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbstats - Generation and display of ACPI table statistics
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdebug.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbstats")
+
+/* Local prototypes */
+static void acpi_db_count_namespace_objects(void);
+
+static void acpi_db_enumerate_object(union acpi_operand_object *obj_desc);
+
+static acpi_status
+acpi_db_classify_one_object(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value);
+
+#if defined ACPI_DBG_TRACK_ALLOCATIONS || defined ACPI_USE_LOCAL_CACHE
+static void acpi_db_list_info(struct acpi_memory_list *list);
+#endif
+
+/*
+ * Statistics subcommands
+ */
+static struct acpi_db_argument_info acpi_db_stat_types[] = {
+ {"ALLOCATIONS"},
+ {"OBJECTS"},
+ {"MEMORY"},
+ {"MISC"},
+ {"TABLES"},
+ {"SIZES"},
+ {"STACK"},
+ {NULL} /* Must be null terminated */
+};
+
+#define CMD_STAT_ALLOCATIONS 0
+#define CMD_STAT_OBJECTS 1
+#define CMD_STAT_MEMORY 2
+#define CMD_STAT_MISC 3
+#define CMD_STAT_TABLES 4
+#define CMD_STAT_SIZES 5
+#define CMD_STAT_STACK 6
+
+#if defined ACPI_DBG_TRACK_ALLOCATIONS || defined ACPI_USE_LOCAL_CACHE
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_list_info
+ *
+ * PARAMETERS: list - Memory list/cache to be displayed
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display information about the input memory list or cache.
+ *
+ ******************************************************************************/
+
+static void acpi_db_list_info(struct acpi_memory_list *list)
+{
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ u32 outstanding;
+#endif
+
+ acpi_os_printf("\n%s\n", list->list_name);
+
+ /* max_depth > 0 indicates a cache object */
+
+ if (list->max_depth > 0) {
+ acpi_os_printf
+ (" Cache: [Depth MaxD Avail Size] "
+ "%8.2X %8.2X %8.2X %8.2X\n", list->current_depth,
+ list->max_depth, list->max_depth - list->current_depth,
+ (list->current_depth * list->object_size));
+ }
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ if (list->max_depth > 0) {
+ acpi_os_printf
+ (" Cache: [Requests Hits Misses ObjSize] "
+ "%8.2X %8.2X %8.2X %8.2X\n", list->requests, list->hits,
+ list->requests - list->hits, list->object_size);
+ }
+
+ outstanding = acpi_db_get_cache_info(list);
+
+ if (list->object_size) {
+ acpi_os_printf
+ (" Mem: [Alloc Free Max CurSize Outstanding] "
+ "%8.2X %8.2X %8.2X %8.2X %8.2X\n", list->total_allocated,
+ list->total_freed, list->max_occupied,
+ outstanding * list->object_size, outstanding);
+ } else {
+ acpi_os_printf
+ (" Mem: [Alloc Free Max CurSize Outstanding Total] "
+ "%8.2X %8.2X %8.2X %8.2X %8.2X %8.2X\n",
+ list->total_allocated, list->total_freed,
+ list->max_occupied, list->current_total_size, outstanding,
+ list->total_size);
+ }
+#endif
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_enumerate_object
+ *
+ * PARAMETERS: obj_desc - Object to be counted
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Add this object to the global counts, by object type.
+ * Limited recursion handles subobjects and packages, and this
+ * is probably acceptable within the AML debugger only.
+ *
+ ******************************************************************************/
+
+static void acpi_db_enumerate_object(union acpi_operand_object *obj_desc)
+{
+ u32 i;
+
+ if (!obj_desc) {
+ return;
+ }
+
+ /* Enumerate this object first */
+
+ acpi_gbl_num_objects++;
+
+ if (obj_desc->common.type > ACPI_TYPE_NS_NODE_MAX) {
+ acpi_gbl_obj_type_count_misc++;
+ } else {
+ acpi_gbl_obj_type_count[obj_desc->common.type]++;
+ }
+
+ /* Count the sub-objects */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_PACKAGE:
+
+ for (i = 0; i < obj_desc->package.count; i++) {
+ acpi_db_enumerate_object(obj_desc->package.elements[i]);
+ }
+ break;
+
+ case ACPI_TYPE_DEVICE:
+
+ acpi_db_enumerate_object(obj_desc->device.notify_list[0]);
+ acpi_db_enumerate_object(obj_desc->device.notify_list[1]);
+ acpi_db_enumerate_object(obj_desc->device.handler);
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+
+ if (acpi_ns_get_secondary_object(obj_desc)) {
+ acpi_gbl_obj_type_count[ACPI_TYPE_BUFFER_FIELD]++;
+ }
+ break;
+
+ case ACPI_TYPE_REGION:
+
+ acpi_gbl_obj_type_count[ACPI_TYPE_LOCAL_REGION_FIELD]++;
+ acpi_db_enumerate_object(obj_desc->region.handler);
+ break;
+
+ case ACPI_TYPE_POWER:
+
+ acpi_db_enumerate_object(obj_desc->power_resource.
+ notify_list[0]);
+ acpi_db_enumerate_object(obj_desc->power_resource.
+ notify_list[1]);
+ break;
+
+ case ACPI_TYPE_PROCESSOR:
+
+ acpi_db_enumerate_object(obj_desc->processor.notify_list[0]);
+ acpi_db_enumerate_object(obj_desc->processor.notify_list[1]);
+ acpi_db_enumerate_object(obj_desc->processor.handler);
+ break;
+
+ case ACPI_TYPE_THERMAL:
+
+ acpi_db_enumerate_object(obj_desc->thermal_zone.notify_list[0]);
+ acpi_db_enumerate_object(obj_desc->thermal_zone.notify_list[1]);
+ acpi_db_enumerate_object(obj_desc->thermal_zone.handler);
+ break;
+
+ default:
+
+ break;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_classify_one_object
+ *
+ * PARAMETERS: Callback for walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enumerate both the object descriptor (including subobjects) and
+ * the parent namespace node.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_classify_one_object(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *obj_desc;
+ u32 type;
+
+ acpi_gbl_num_nodes++;
+
+ node = (struct acpi_namespace_node *)obj_handle;
+ obj_desc = acpi_ns_get_attached_object(node);
+
+ acpi_db_enumerate_object(obj_desc);
+
+ type = node->type;
+ if (type > ACPI_TYPE_NS_NODE_MAX) {
+ acpi_gbl_node_type_count_misc++;
+ } else {
+ acpi_gbl_node_type_count[type]++;
+ }
+
+ return (AE_OK);
+
+#ifdef ACPI_FUTURE_IMPLEMENTATION
+
+ /* TBD: These need to be counted during the initial parsing phase */
+
+ if (acpi_ps_is_named_op(op->opcode)) {
+ num_nodes++;
+ }
+
+ if (is_method) {
+ num_method_elements++;
+ }
+
+ num_grammar_elements++;
+ op = acpi_ps_get_depth_next(root, op);
+
+ size_of_parse_tree = (num_grammar_elements - num_method_elements) *
+ (u32)sizeof(union acpi_parse_object);
+ size_of_method_trees =
+ num_method_elements * (u32)sizeof(union acpi_parse_object);
+ size_of_node_entries =
+ num_nodes * (u32)sizeof(struct acpi_namespace_node);
+ size_of_acpi_objects =
+ num_nodes * (u32)sizeof(union acpi_operand_object);
+#endif
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_count_namespace_objects
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Count and classify the entire namespace, including all
+ * namespace nodes and attached objects.
+ *
+ ******************************************************************************/
+
+static void acpi_db_count_namespace_objects(void)
+{
+ u32 i;
+
+ acpi_gbl_num_nodes = 0;
+ acpi_gbl_num_objects = 0;
+
+ acpi_gbl_obj_type_count_misc = 0;
+ for (i = 0; i < (ACPI_TYPE_NS_NODE_MAX - 1); i++) {
+ acpi_gbl_obj_type_count[i] = 0;
+ acpi_gbl_node_type_count[i] = 0;
+ }
+
+ (void)acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, FALSE,
+ acpi_db_classify_one_object, NULL, NULL,
+ NULL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_display_statistics
+ *
+ * PARAMETERS: type_arg - Subcommand
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Display various statistics
+ *
+ ******************************************************************************/
+
+acpi_status acpi_db_display_statistics(char *type_arg)
+{
+ u32 i;
+ u32 temp;
+
+ acpi_ut_strupr(type_arg);
+ temp = acpi_db_match_argument(type_arg, acpi_db_stat_types);
+ if (temp == ACPI_TYPE_NOT_FOUND) {
+ acpi_os_printf("Invalid or unsupported argument\n");
+ return (AE_OK);
+ }
+
+ switch (temp) {
+ case CMD_STAT_ALLOCATIONS:
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ acpi_ut_dump_allocation_info();
+#endif
+ break;
+
+ case CMD_STAT_TABLES:
+
+ acpi_os_printf("ACPI Table Information (not implemented):\n\n");
+ break;
+
+ case CMD_STAT_OBJECTS:
+
+ acpi_db_count_namespace_objects();
+
+ acpi_os_printf
+ ("\nObjects defined in the current namespace:\n\n");
+
+ acpi_os_printf("%16.16s %10.10s %10.10s\n",
+ "ACPI_TYPE", "NODES", "OBJECTS");
+
+ for (i = 0; i < ACPI_TYPE_NS_NODE_MAX; i++) {
+ acpi_os_printf("%16.16s %10u %10u\n",
+ acpi_ut_get_type_name(i),
+ acpi_gbl_node_type_count[i],
+ acpi_gbl_obj_type_count[i]);
+ }
+
+ acpi_os_printf("%16.16s %10u %10u\n", "Misc/Unknown",
+ acpi_gbl_node_type_count_misc,
+ acpi_gbl_obj_type_count_misc);
+
+ acpi_os_printf("%16.16s %10u %10u\n", "TOTALS:",
+ acpi_gbl_num_nodes, acpi_gbl_num_objects);
+ break;
+
+ case CMD_STAT_MEMORY:
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ acpi_os_printf
+ ("\n----Object Statistics (all in hex)---------\n");
+
+ acpi_db_list_info(acpi_gbl_global_list);
+ acpi_db_list_info(acpi_gbl_ns_node_list);
+#endif
+
+#ifdef ACPI_USE_LOCAL_CACHE
+ acpi_os_printf
+ ("\n----Cache Statistics (all in hex)---------\n");
+ acpi_db_list_info(acpi_gbl_operand_cache);
+ acpi_db_list_info(acpi_gbl_ps_node_cache);
+ acpi_db_list_info(acpi_gbl_ps_node_ext_cache);
+ acpi_db_list_info(acpi_gbl_state_cache);
+#endif
+
+ break;
+
+ case CMD_STAT_MISC:
+
+ acpi_os_printf("\nMiscellaneous Statistics:\n\n");
+ acpi_os_printf("%-28s: %7u\n", "Calls to AcpiPsFind",
+ acpi_gbl_ps_find_count);
+ acpi_os_printf("%-28s: %7u\n", "Calls to AcpiNsLookup",
+ acpi_gbl_ns_lookup_count);
+
+ acpi_os_printf("\nMutex usage:\n\n");
+ for (i = 0; i < ACPI_NUM_MUTEX; i++) {
+ acpi_os_printf("%-28s: %7u\n",
+ acpi_ut_get_mutex_name(i),
+ acpi_gbl_mutex_info[i].use_count);
+ }
+ break;
+
+ case CMD_STAT_SIZES:
+
+ acpi_os_printf("\nInternal object sizes:\n\n");
+
+ acpi_os_printf("Common %3d\n",
+ (u32)sizeof(struct acpi_object_common));
+ acpi_os_printf("Number %3d\n",
+ (u32)sizeof(struct acpi_object_integer));
+ acpi_os_printf("String %3d\n",
+ (u32)sizeof(struct acpi_object_string));
+ acpi_os_printf("Buffer %3d\n",
+ (u32)sizeof(struct acpi_object_buffer));
+ acpi_os_printf("Package %3d\n",
+ (u32)sizeof(struct acpi_object_package));
+ acpi_os_printf("BufferField %3d\n",
+ (u32)sizeof(struct acpi_object_buffer_field));
+ acpi_os_printf("Device %3d\n",
+ (u32)sizeof(struct acpi_object_device));
+ acpi_os_printf("Event %3d\n",
+ (u32)sizeof(struct acpi_object_event));
+ acpi_os_printf("Method %3d\n",
+ (u32)sizeof(struct acpi_object_method));
+ acpi_os_printf("Mutex %3d\n",
+ (u32)sizeof(struct acpi_object_mutex));
+ acpi_os_printf("Region %3d\n",
+ (u32)sizeof(struct acpi_object_region));
+ acpi_os_printf("PowerResource %3d\n",
+ (u32)sizeof(struct acpi_object_power_resource));
+ acpi_os_printf("Processor %3d\n",
+ (u32)sizeof(struct acpi_object_processor));
+ acpi_os_printf("ThermalZone %3d\n",
+ (u32)sizeof(struct acpi_object_thermal_zone));
+ acpi_os_printf("RegionField %3d\n",
+ (u32)sizeof(struct acpi_object_region_field));
+ acpi_os_printf("BankField %3d\n",
+ (u32)sizeof(struct acpi_object_bank_field));
+ acpi_os_printf("IndexField %3d\n",
+ (u32)sizeof(struct acpi_object_index_field));
+ acpi_os_printf("Reference %3d\n",
+ (u32)sizeof(struct acpi_object_reference));
+ acpi_os_printf("Notify %3d\n",
+ (u32)sizeof(struct acpi_object_notify_handler));
+ acpi_os_printf("AddressSpace %3d\n",
+ (u32)sizeof(struct acpi_object_addr_handler));
+ acpi_os_printf("Extra %3d\n",
+ (u32)sizeof(struct acpi_object_extra));
+ acpi_os_printf("Data %3d\n",
+ (u32)sizeof(struct acpi_object_data));
+
+ acpi_os_printf("\n");
+
+ acpi_os_printf("ParseObject %3d\n",
+ (u32)sizeof(struct acpi_parse_obj_common));
+ acpi_os_printf("ParseObjectNamed %3d\n",
+ (u32)sizeof(struct acpi_parse_obj_named));
+ acpi_os_printf("ParseObjectAsl %3d\n",
+ (u32)sizeof(struct acpi_parse_obj_asl));
+ acpi_os_printf("OperandObject %3d\n",
+ (u32)sizeof(union acpi_operand_object));
+ acpi_os_printf("NamespaceNode %3d\n",
+ (u32)sizeof(struct acpi_namespace_node));
+ acpi_os_printf("AcpiObject %3d\n",
+ (u32)sizeof(union acpi_object));
+
+ acpi_os_printf("\n");
+
+ acpi_os_printf("Generic State %3d\n",
+ (u32)sizeof(union acpi_generic_state));
+ acpi_os_printf("Common State %3d\n",
+ (u32)sizeof(struct acpi_common_state));
+ acpi_os_printf("Control State %3d\n",
+ (u32)sizeof(struct acpi_control_state));
+ acpi_os_printf("Update State %3d\n",
+ (u32)sizeof(struct acpi_update_state));
+ acpi_os_printf("Scope State %3d\n",
+ (u32)sizeof(struct acpi_scope_state));
+ acpi_os_printf("Parse Scope %3d\n",
+ (u32)sizeof(struct acpi_pscope_state));
+ acpi_os_printf("Package State %3d\n",
+ (u32)sizeof(struct acpi_pkg_state));
+ acpi_os_printf("Thread State %3d\n",
+ (u32)sizeof(struct acpi_thread_state));
+ acpi_os_printf("Result Values %3d\n",
+ (u32)sizeof(struct acpi_result_values));
+ acpi_os_printf("Notify Info %3d\n",
+ (u32)sizeof(struct acpi_notify_info));
+ break;
+
+ case CMD_STAT_STACK:
+#if defined(ACPI_DEBUG_OUTPUT)
+
+ temp =
+ (u32)ACPI_PTR_DIFF(acpi_gbl_entry_stack_pointer,
+ acpi_gbl_lowest_stack_pointer);
+
+ acpi_os_printf("\nSubsystem Stack Usage:\n\n");
+ acpi_os_printf("Entry Stack Pointer %p\n",
+ acpi_gbl_entry_stack_pointer);
+ acpi_os_printf("Lowest Stack Pointer %p\n",
+ acpi_gbl_lowest_stack_pointer);
+ acpi_os_printf("Stack Use %X (%u)\n", temp,
+ temp);
+ acpi_os_printf("Deepest Procedure Nesting %u\n",
+ acpi_gbl_deepest_nesting);
+#endif
+ break;
+
+ default:
+
+ break;
+ }
+
+ acpi_os_printf("\n");
+ return (AE_OK);
+}
diff --git a/drivers/acpi/acpica/dbtest.c b/drivers/acpi/acpica/dbtest.c
new file mode 100644
index 000000000..6db44a5ac
--- /dev/null
+++ b/drivers/acpi/acpica/dbtest.c
@@ -0,0 +1,1104 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbtest - Various debug-related tests
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdebug.h"
+#include "acnamesp.h"
+#include "acpredef.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbtest")
+
+/* Local prototypes */
+static void acpi_db_test_all_objects(void);
+
+static acpi_status
+acpi_db_test_one_object(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value);
+
+static acpi_status
+acpi_db_test_integer_type(struct acpi_namespace_node *node, u32 bit_length);
+
+static acpi_status
+acpi_db_test_buffer_type(struct acpi_namespace_node *node, u32 bit_length);
+
+static acpi_status
+acpi_db_test_string_type(struct acpi_namespace_node *node, u32 byte_length);
+
+static acpi_status acpi_db_test_package_type(struct acpi_namespace_node *node);
+
+static acpi_status
+acpi_db_test_field_unit_type(union acpi_operand_object *obj_desc);
+
+static acpi_status
+acpi_db_read_from_object(struct acpi_namespace_node *node,
+ acpi_object_type expected_type,
+ union acpi_object **value);
+
+static acpi_status
+acpi_db_write_to_object(struct acpi_namespace_node *node,
+ union acpi_object *value);
+
+static void acpi_db_evaluate_all_predefined_names(char *count_arg);
+
+static acpi_status
+acpi_db_evaluate_one_predefined_name(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value);
+
+/*
+ * Test subcommands
+ */
+static struct acpi_db_argument_info acpi_db_test_types[] = {
+ {"OBJECTS"},
+ {"PREDEFINED"},
+ {NULL} /* Must be null terminated */
+};
+
+#define CMD_TEST_OBJECTS 0
+#define CMD_TEST_PREDEFINED 1
+
+#define BUFFER_FILL_VALUE 0xFF
+
+/*
+ * Support for the special debugger read/write control methods.
+ * These methods are installed into the current namespace and are
+ * used to read and write the various namespace objects. The point
+ * is to force the AML interpreter do all of the work.
+ */
+#define ACPI_DB_READ_METHOD "\\_T98"
+#define ACPI_DB_WRITE_METHOD "\\_T99"
+
+static acpi_handle read_handle = NULL;
+static acpi_handle write_handle = NULL;
+
+/* ASL Definitions of the debugger read/write control methods. AML below. */
+
+#if 0
+definition_block("ssdt.aml", "SSDT", 2, "Intel", "DEBUG", 0x00000001)
+{
+ method(_T98, 1, not_serialized) { /* Read */
+ return (de_ref_of(arg0))
+ }
+}
+
+definition_block("ssdt2.aml", "SSDT", 2, "Intel", "DEBUG", 0x00000001)
+{
+ method(_T99, 2, not_serialized) { /* Write */
+ store(arg1, arg0)
+ }
+}
+#endif
+
+static unsigned char read_method_code[] = {
+ 0x53, 0x53, 0x44, 0x54, 0x2E, 0x00, 0x00, 0x00, /* 00000000 "SSDT...." */
+ 0x02, 0xC9, 0x49, 0x6E, 0x74, 0x65, 0x6C, 0x00, /* 00000008 "..Intel." */
+ 0x44, 0x45, 0x42, 0x55, 0x47, 0x00, 0x00, 0x00, /* 00000010 "DEBUG..." */
+ 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, /* 00000018 "....INTL" */
+ 0x18, 0x12, 0x13, 0x20, 0x14, 0x09, 0x5F, 0x54, /* 00000020 "... .._T" */
+ 0x39, 0x38, 0x01, 0xA4, 0x83, 0x68 /* 00000028 "98...h" */
+};
+
+static unsigned char write_method_code[] = {
+ 0x53, 0x53, 0x44, 0x54, 0x2E, 0x00, 0x00, 0x00, /* 00000000 "SSDT...." */
+ 0x02, 0x15, 0x49, 0x6E, 0x74, 0x65, 0x6C, 0x00, /* 00000008 "..Intel." */
+ 0x44, 0x45, 0x42, 0x55, 0x47, 0x00, 0x00, 0x00, /* 00000010 "DEBUG..." */
+ 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, /* 00000018 "....INTL" */
+ 0x18, 0x12, 0x13, 0x20, 0x14, 0x09, 0x5F, 0x54, /* 00000020 "... .._T" */
+ 0x39, 0x39, 0x02, 0x70, 0x69, 0x68 /* 00000028 "99.pih" */
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_execute_test
+ *
+ * PARAMETERS: type_arg - Subcommand
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Execute various debug tests.
+ *
+ * Note: Code is prepared for future expansion of the TEST command.
+ *
+ ******************************************************************************/
+
+void acpi_db_execute_test(char *type_arg)
+{
+ u32 temp;
+
+ acpi_ut_strupr(type_arg);
+ temp = acpi_db_match_argument(type_arg, acpi_db_test_types);
+ if (temp == ACPI_TYPE_NOT_FOUND) {
+ acpi_os_printf("Invalid or unsupported argument\n");
+ return;
+ }
+
+ switch (temp) {
+ case CMD_TEST_OBJECTS:
+
+ acpi_db_test_all_objects();
+ break;
+
+ case CMD_TEST_PREDEFINED:
+
+ acpi_db_evaluate_all_predefined_names(NULL);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_test_all_objects
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: This test implements the OBJECTS subcommand. It exercises the
+ * namespace by reading/writing/comparing all data objects such
+ * as integers, strings, buffers, fields, buffer fields, etc.
+ *
+ ******************************************************************************/
+
+static void acpi_db_test_all_objects(void)
+{
+ acpi_status status;
+
+ /* Install the debugger read-object control method if necessary */
+
+ if (!read_handle) {
+ status = acpi_install_method(read_method_code);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf
+ ("%s, Could not install debugger read method\n",
+ acpi_format_exception(status));
+ return;
+ }
+
+ status =
+ acpi_get_handle(NULL, ACPI_DB_READ_METHOD, &read_handle);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf
+ ("Could not obtain handle for debug method %s\n",
+ ACPI_DB_READ_METHOD);
+ return;
+ }
+ }
+
+ /* Install the debugger write-object control method if necessary */
+
+ if (!write_handle) {
+ status = acpi_install_method(write_method_code);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf
+ ("%s, Could not install debugger write method\n",
+ acpi_format_exception(status));
+ return;
+ }
+
+ status =
+ acpi_get_handle(NULL, ACPI_DB_WRITE_METHOD, &write_handle);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf
+ ("Could not obtain handle for debug method %s\n",
+ ACPI_DB_WRITE_METHOD);
+ return;
+ }
+ }
+
+ /* Walk the entire namespace, testing each supported named data object */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_db_test_one_object,
+ NULL, NULL, NULL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_test_one_object
+ *
+ * PARAMETERS: acpi_walk_callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Test one namespace object. Supported types are Integer,
+ * String, Buffer, Package, buffer_field, and field_unit.
+ * All other object types are simply ignored.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_test_one_object(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value)
+{
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *obj_desc;
+ acpi_object_type local_type;
+ u32 bit_length = 0;
+ u32 byte_length = 0;
+ acpi_status status = AE_OK;
+
+ node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle);
+ obj_desc = node->object;
+
+ /*
+ * For the supported types, get the actual bit length or
+ * byte length. Map the type to one of Integer/String/Buffer.
+ */
+ switch (node->type) {
+ case ACPI_TYPE_INTEGER:
+
+ /* Integer width is either 32 or 64 */
+
+ local_type = ACPI_TYPE_INTEGER;
+ bit_length = acpi_gbl_integer_bit_width;
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ local_type = ACPI_TYPE_STRING;
+ byte_length = obj_desc->string.length;
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ local_type = ACPI_TYPE_BUFFER;
+ byte_length = obj_desc->buffer.length;
+ bit_length = byte_length * 8;
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ local_type = ACPI_TYPE_PACKAGE;
+ break;
+
+ case ACPI_TYPE_FIELD_UNIT:
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+
+ local_type = ACPI_TYPE_FIELD_UNIT;
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+ /*
+ * The returned object will be a Buffer if the field length
+ * is larger than the size of an Integer (32 or 64 bits
+ * depending on the DSDT version).
+ */
+ local_type = ACPI_TYPE_INTEGER;
+ if (obj_desc) {
+ bit_length = obj_desc->common_field.bit_length;
+ byte_length = ACPI_ROUND_BITS_UP_TO_BYTES(bit_length);
+ if (bit_length > acpi_gbl_integer_bit_width) {
+ local_type = ACPI_TYPE_BUFFER;
+ }
+ }
+ break;
+
+ default:
+
+ /* Ignore all non-data types - Methods, Devices, Scopes, etc. */
+
+ return (AE_OK);
+ }
+
+ /* Emit the common prefix: Type:Name */
+
+ acpi_os_printf("%14s: %4.4s",
+ acpi_ut_get_type_name(node->type), node->name.ascii);
+
+ if (!obj_desc) {
+ acpi_os_printf(" No attached sub-object, ignoring\n");
+ return (AE_OK);
+ }
+
+ /* At this point, we have resolved the object to one of the major types */
+
+ switch (local_type) {
+ case ACPI_TYPE_INTEGER:
+
+ status = acpi_db_test_integer_type(node, bit_length);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ status = acpi_db_test_string_type(node, byte_length);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ status = acpi_db_test_buffer_type(node, bit_length);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ status = acpi_db_test_package_type(node);
+ break;
+
+ case ACPI_TYPE_FIELD_UNIT:
+
+ status = acpi_db_test_field_unit_type(obj_desc);
+ break;
+
+ default:
+
+ acpi_os_printf(" Ignoring, type not implemented (%2.2X)",
+ local_type);
+ break;
+ }
+
+ /* Exit on error, but don't abort the namespace walk */
+
+ if (ACPI_FAILURE(status)) {
+ status = AE_OK;
+ }
+
+ acpi_os_printf("\n");
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_test_integer_type
+ *
+ * PARAMETERS: node - Parent NS node for the object
+ * bit_length - Actual length of the object. Used for
+ * support of arbitrary length field_unit
+ * and buffer_field objects.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Test read/write for an Integer-valued object. Performs a
+ * write/read/compare of an arbitrary new value, then performs
+ * a write/read/compare of the original value.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_test_integer_type(struct acpi_namespace_node *node, u32 bit_length)
+{
+ union acpi_object *temp1 = NULL;
+ union acpi_object *temp2 = NULL;
+ union acpi_object *temp3 = NULL;
+ union acpi_object write_value;
+ u64 value_to_write;
+ acpi_status status;
+
+ if (bit_length > 64) {
+ acpi_os_printf(" Invalid length for an Integer: %u",
+ bit_length);
+ return (AE_OK);
+ }
+
+ /* Read the original value */
+
+ status = acpi_db_read_from_object(node, ACPI_TYPE_INTEGER, &temp1);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ acpi_os_printf(ACPI_DEBUG_LENGTH_FORMAT " %8.8X%8.8X",
+ bit_length, ACPI_ROUND_BITS_UP_TO_BYTES(bit_length),
+ ACPI_FORMAT_UINT64(temp1->integer.value));
+
+ value_to_write = ACPI_UINT64_MAX >> (64 - bit_length);
+ if (temp1->integer.value == value_to_write) {
+ value_to_write = 0;
+ }
+ /* Write a new value */
+
+ write_value.type = ACPI_TYPE_INTEGER;
+ write_value.integer.value = value_to_write;
+ status = acpi_db_write_to_object(node, &write_value);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Ensure that we can read back the new value */
+
+ status = acpi_db_read_from_object(node, ACPI_TYPE_INTEGER, &temp2);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ if (temp2->integer.value != value_to_write) {
+ acpi_os_printf(" MISMATCH 2: %8.8X%8.8X, expecting %8.8X%8.8X",
+ ACPI_FORMAT_UINT64(temp2->integer.value),
+ ACPI_FORMAT_UINT64(value_to_write));
+ }
+
+ /* Write back the original value */
+
+ write_value.integer.value = temp1->integer.value;
+ status = acpi_db_write_to_object(node, &write_value);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Ensure that we can read back the original value */
+
+ status = acpi_db_read_from_object(node, ACPI_TYPE_INTEGER, &temp3);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ if (temp3->integer.value != temp1->integer.value) {
+ acpi_os_printf(" MISMATCH 3: %8.8X%8.8X, expecting %8.8X%8.8X",
+ ACPI_FORMAT_UINT64(temp3->integer.value),
+ ACPI_FORMAT_UINT64(temp1->integer.value));
+ }
+
+exit:
+ if (temp1) {
+ acpi_os_free(temp1);
+ }
+ if (temp2) {
+ acpi_os_free(temp2);
+ }
+ if (temp3) {
+ acpi_os_free(temp3);
+ }
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_test_buffer_type
+ *
+ * PARAMETERS: node - Parent NS node for the object
+ * bit_length - Actual length of the object.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Test read/write for an Buffer-valued object. Performs a
+ * write/read/compare of an arbitrary new value, then performs
+ * a write/read/compare of the original value.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_test_buffer_type(struct acpi_namespace_node *node, u32 bit_length)
+{
+ union acpi_object *temp1 = NULL;
+ union acpi_object *temp2 = NULL;
+ union acpi_object *temp3 = NULL;
+ u8 *buffer;
+ union acpi_object write_value;
+ acpi_status status;
+ u32 byte_length;
+ u32 i;
+ u8 extra_bits;
+
+ byte_length = ACPI_ROUND_BITS_UP_TO_BYTES(bit_length);
+ if (byte_length == 0) {
+ acpi_os_printf(" Ignoring zero length buffer");
+ return (AE_OK);
+ }
+
+ /* Allocate a local buffer */
+
+ buffer = ACPI_ALLOCATE_ZEROED(byte_length);
+ if (!buffer) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Read the original value */
+
+ status = acpi_db_read_from_object(node, ACPI_TYPE_BUFFER, &temp1);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Emit a few bytes of the buffer */
+
+ acpi_os_printf(ACPI_DEBUG_LENGTH_FORMAT, bit_length,
+ temp1->buffer.length);
+ for (i = 0; ((i < 8) && (i < byte_length)); i++) {
+ acpi_os_printf(" %2.2X", temp1->buffer.pointer[i]);
+ }
+ acpi_os_printf("... ");
+
+ /*
+ * Write a new value.
+ *
+ * Handle possible extra bits at the end of the buffer. Can
+ * happen for field_units larger than an integer, but the bit
+ * count is not an integral number of bytes. Zero out the
+ * unused bits.
+ */
+ memset(buffer, BUFFER_FILL_VALUE, byte_length);
+ extra_bits = bit_length % 8;
+ if (extra_bits) {
+ buffer[byte_length - 1] = ACPI_MASK_BITS_ABOVE(extra_bits);
+ }
+
+ write_value.type = ACPI_TYPE_BUFFER;
+ write_value.buffer.length = byte_length;
+ write_value.buffer.pointer = buffer;
+
+ status = acpi_db_write_to_object(node, &write_value);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Ensure that we can read back the new value */
+
+ status = acpi_db_read_from_object(node, ACPI_TYPE_BUFFER, &temp2);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ if (memcmp(temp2->buffer.pointer, buffer, byte_length)) {
+ acpi_os_printf(" MISMATCH 2: New buffer value");
+ }
+
+ /* Write back the original value */
+
+ write_value.buffer.length = byte_length;
+ write_value.buffer.pointer = temp1->buffer.pointer;
+
+ status = acpi_db_write_to_object(node, &write_value);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Ensure that we can read back the original value */
+
+ status = acpi_db_read_from_object(node, ACPI_TYPE_BUFFER, &temp3);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ if (memcmp(temp1->buffer.pointer, temp3->buffer.pointer, byte_length)) {
+ acpi_os_printf(" MISMATCH 3: While restoring original buffer");
+ }
+
+exit:
+ ACPI_FREE(buffer);
+ if (temp1) {
+ acpi_os_free(temp1);
+ }
+ if (temp2) {
+ acpi_os_free(temp2);
+ }
+ if (temp3) {
+ acpi_os_free(temp3);
+ }
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_test_string_type
+ *
+ * PARAMETERS: node - Parent NS node for the object
+ * byte_length - Actual length of the object.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Test read/write for an String-valued object. Performs a
+ * write/read/compare of an arbitrary new value, then performs
+ * a write/read/compare of the original value.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_test_string_type(struct acpi_namespace_node *node, u32 byte_length)
+{
+ union acpi_object *temp1 = NULL;
+ union acpi_object *temp2 = NULL;
+ union acpi_object *temp3 = NULL;
+ char *value_to_write = "Test String from AML Debugger";
+ union acpi_object write_value;
+ acpi_status status;
+
+ /* Read the original value */
+
+ status = acpi_db_read_from_object(node, ACPI_TYPE_STRING, &temp1);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ acpi_os_printf(ACPI_DEBUG_LENGTH_FORMAT " \"%s\"",
+ (temp1->string.length * 8), temp1->string.length,
+ temp1->string.pointer);
+
+ /* Write a new value */
+
+ write_value.type = ACPI_TYPE_STRING;
+ write_value.string.length = strlen(value_to_write);
+ write_value.string.pointer = value_to_write;
+
+ status = acpi_db_write_to_object(node, &write_value);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Ensure that we can read back the new value */
+
+ status = acpi_db_read_from_object(node, ACPI_TYPE_STRING, &temp2);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ if (strcmp(temp2->string.pointer, value_to_write)) {
+ acpi_os_printf(" MISMATCH 2: %s, expecting %s",
+ temp2->string.pointer, value_to_write);
+ }
+
+ /* Write back the original value */
+
+ write_value.string.length = strlen(temp1->string.pointer);
+ write_value.string.pointer = temp1->string.pointer;
+
+ status = acpi_db_write_to_object(node, &write_value);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Ensure that we can read back the original value */
+
+ status = acpi_db_read_from_object(node, ACPI_TYPE_STRING, &temp3);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ if (strcmp(temp1->string.pointer, temp3->string.pointer)) {
+ acpi_os_printf(" MISMATCH 3: %s, expecting %s",
+ temp3->string.pointer, temp1->string.pointer);
+ }
+
+exit:
+ if (temp1) {
+ acpi_os_free(temp1);
+ }
+ if (temp2) {
+ acpi_os_free(temp2);
+ }
+ if (temp3) {
+ acpi_os_free(temp3);
+ }
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_test_package_type
+ *
+ * PARAMETERS: node - Parent NS node for the object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Test read for a Package object.
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_db_test_package_type(struct acpi_namespace_node *node)
+{
+ union acpi_object *temp1 = NULL;
+ acpi_status status;
+
+ /* Read the original value */
+
+ status = acpi_db_read_from_object(node, ACPI_TYPE_PACKAGE, &temp1);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ acpi_os_printf(" %.2X Elements", temp1->package.count);
+ acpi_os_free(temp1);
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_test_field_unit_type
+ *
+ * PARAMETERS: obj_desc - A field unit object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Test read/write on a named field unit.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_test_field_unit_type(union acpi_operand_object *obj_desc)
+{
+ union acpi_operand_object *region_obj;
+ u32 bit_length = 0;
+ u32 byte_length = 0;
+ acpi_status status = AE_OK;
+ union acpi_operand_object *ret_buffer_desc;
+
+ /* Supported spaces are memory/io/pci_config */
+
+ region_obj = obj_desc->field.region_obj;
+ switch (region_obj->region.space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ case ACPI_ADR_SPACE_PCI_CONFIG:
+
+ /* Need the interpreter to execute */
+
+ acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
+ acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+
+ /* Exercise read-then-write */
+
+ status =
+ acpi_ex_read_data_from_field(NULL, obj_desc,
+ &ret_buffer_desc);
+ if (status == AE_OK) {
+ acpi_ex_write_data_to_field(ret_buffer_desc, obj_desc,
+ NULL);
+ acpi_ut_remove_reference(ret_buffer_desc);
+ }
+
+ acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
+
+ bit_length = obj_desc->common_field.bit_length;
+ byte_length = ACPI_ROUND_BITS_UP_TO_BYTES(bit_length);
+
+ acpi_os_printf(ACPI_DEBUG_LENGTH_FORMAT " [%s]", bit_length,
+ byte_length,
+ acpi_ut_get_region_name(region_obj->region.
+ space_id));
+ return (status);
+
+ default:
+
+ acpi_os_printf
+ (" %s address space is not supported in this command [%4.4s]",
+ acpi_ut_get_region_name(region_obj->region.space_id),
+ region_obj->region.node->name.ascii);
+ return (AE_OK);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_read_from_object
+ *
+ * PARAMETERS: node - Parent NS node for the object
+ * expected_type - Object type expected from the read
+ * value - Where the value read is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Performs a read from the specified object by invoking the
+ * special debugger control method that reads the object. Thus,
+ * the AML interpreter is doing all of the work, increasing the
+ * validity of the test.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_read_from_object(struct acpi_namespace_node *node,
+ acpi_object_type expected_type,
+ union acpi_object **value)
+{
+ union acpi_object *ret_value;
+ struct acpi_object_list param_objects;
+ union acpi_object params[2];
+ struct acpi_buffer return_obj;
+ acpi_status status;
+
+ params[0].type = ACPI_TYPE_LOCAL_REFERENCE;
+ params[0].reference.actual_type = node->type;
+ params[0].reference.handle = ACPI_CAST_PTR(acpi_handle, node);
+
+ param_objects.count = 1;
+ param_objects.pointer = params;
+
+ return_obj.length = ACPI_ALLOCATE_BUFFER;
+
+ acpi_gbl_method_executing = TRUE;
+ status = acpi_evaluate_object(read_handle, NULL,
+ &param_objects, &return_obj);
+
+ acpi_gbl_method_executing = FALSE;
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not read from object, %s",
+ acpi_format_exception(status));
+ return (status);
+ }
+
+ ret_value = (union acpi_object *)return_obj.pointer;
+
+ switch (ret_value->type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * Did we receive the type we wanted? Most important for the
+ * Integer/Buffer case (when a field is larger than an Integer,
+ * it should return a Buffer).
+ */
+ if (ret_value->type != expected_type) {
+ acpi_os_printf
+ (" Type mismatch: Expected %s, Received %s",
+ acpi_ut_get_type_name(expected_type),
+ acpi_ut_get_type_name(ret_value->type));
+
+ acpi_os_free(return_obj.pointer);
+ return (AE_TYPE);
+ }
+
+ *value = ret_value;
+ break;
+
+ default:
+
+ acpi_os_printf(" Unsupported return object type, %s",
+ acpi_ut_get_type_name(ret_value->type));
+
+ acpi_os_free(return_obj.pointer);
+ return (AE_TYPE);
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_write_to_object
+ *
+ * PARAMETERS: node - Parent NS node for the object
+ * value - Value to be written
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Performs a write to the specified object by invoking the
+ * special debugger control method that writes the object. Thus,
+ * the AML interpreter is doing all of the work, increasing the
+ * validity of the test.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_write_to_object(struct acpi_namespace_node *node,
+ union acpi_object *value)
+{
+ struct acpi_object_list param_objects;
+ union acpi_object params[2];
+ acpi_status status;
+
+ params[0].type = ACPI_TYPE_LOCAL_REFERENCE;
+ params[0].reference.actual_type = node->type;
+ params[0].reference.handle = ACPI_CAST_PTR(acpi_handle, node);
+
+ /* Copy the incoming user parameter */
+
+ memcpy(&params[1], value, sizeof(union acpi_object));
+
+ param_objects.count = 2;
+ param_objects.pointer = params;
+
+ acpi_gbl_method_executing = TRUE;
+ status = acpi_evaluate_object(write_handle, NULL, &param_objects, NULL);
+ acpi_gbl_method_executing = FALSE;
+
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not write to object, %s",
+ acpi_format_exception(status));
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_evaluate_all_predefined_names
+ *
+ * PARAMETERS: count_arg - Max number of methods to execute
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Namespace batch execution. Execute predefined names in the
+ * namespace, up to the max count, if specified.
+ *
+ ******************************************************************************/
+
+static void acpi_db_evaluate_all_predefined_names(char *count_arg)
+{
+ struct acpi_db_execute_walk info;
+
+ info.count = 0;
+ info.max_count = ACPI_UINT32_MAX;
+
+ if (count_arg) {
+ info.max_count = strtoul(count_arg, NULL, 0);
+ }
+
+ /* Search all nodes in namespace */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ acpi_db_evaluate_one_predefined_name, NULL,
+ (void *)&info, NULL);
+
+ acpi_os_printf("Evaluated %u predefined names in the namespace\n",
+ info.count);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_evaluate_one_predefined_name
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Batch execution module. Currently only executes predefined
+ * ACPI names.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_evaluate_one_predefined_name(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+ struct acpi_db_execute_walk *info =
+ (struct acpi_db_execute_walk *)context;
+ char *pathname;
+ const union acpi_predefined_info *predefined;
+ struct acpi_device_info *obj_info;
+ struct acpi_object_list param_objects;
+ union acpi_object params[ACPI_METHOD_NUM_ARGS];
+ union acpi_object *this_param;
+ struct acpi_buffer return_obj;
+ acpi_status status;
+ u16 arg_type_list;
+ u8 arg_count;
+ u8 arg_type;
+ u32 i;
+
+ /* The name must be a predefined ACPI name */
+
+ predefined = acpi_ut_match_predefined_method(node->name.ascii);
+ if (!predefined) {
+ return (AE_OK);
+ }
+
+ if (node->type == ACPI_TYPE_LOCAL_SCOPE) {
+ return (AE_OK);
+ }
+
+ pathname = acpi_ns_get_normalized_pathname(node, TRUE);
+ if (!pathname) {
+ return (AE_OK);
+ }
+
+ /* Get the object info for number of method parameters */
+
+ status = acpi_get_object_info(obj_handle, &obj_info);
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(pathname);
+ return (status);
+ }
+
+ param_objects.count = 0;
+ param_objects.pointer = NULL;
+
+ if (obj_info->type == ACPI_TYPE_METHOD) {
+
+ /* Setup default parameters (with proper types) */
+
+ arg_type_list = predefined->info.argument_list;
+ arg_count = METHOD_GET_ARG_COUNT(arg_type_list);
+
+ /*
+ * Setup the ACPI-required number of arguments, regardless of what
+ * the actual method defines. If there is a difference, then the
+ * method is wrong and a warning will be issued during execution.
+ */
+ this_param = params;
+ for (i = 0; i < arg_count; i++) {
+ arg_type = METHOD_GET_NEXT_TYPE(arg_type_list);
+ this_param->type = arg_type;
+
+ switch (arg_type) {
+ case ACPI_TYPE_INTEGER:
+
+ this_param->integer.value = 1;
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ this_param->string.pointer =
+ "This is the default argument string";
+ this_param->string.length =
+ strlen(this_param->string.pointer);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ this_param->buffer.pointer = (u8 *)params; /* just a garbage buffer */
+ this_param->buffer.length = 48;
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ this_param->package.elements = NULL;
+ this_param->package.count = 0;
+ break;
+
+ default:
+
+ acpi_os_printf
+ ("%s: Unsupported argument type: %u\n",
+ pathname, arg_type);
+ break;
+ }
+
+ this_param++;
+ }
+
+ param_objects.count = arg_count;
+ param_objects.pointer = params;
+ }
+
+ ACPI_FREE(obj_info);
+ return_obj.pointer = NULL;
+ return_obj.length = ACPI_ALLOCATE_BUFFER;
+
+ /* Do the actual method execution */
+
+ acpi_gbl_method_executing = TRUE;
+
+ status = acpi_evaluate_object(node, NULL, &param_objects, &return_obj);
+
+ acpi_os_printf("%-32s returned %s\n",
+ pathname, acpi_format_exception(status));
+ acpi_gbl_method_executing = FALSE;
+ ACPI_FREE(pathname);
+
+ /* Ignore status from method execution */
+
+ status = AE_OK;
+
+ /* Update count, check if we have executed enough methods */
+
+ info->count++;
+ if (info->count >= info->max_count) {
+ status = AE_CTRL_TERMINATE;
+ }
+
+ return (status);
+}
diff --git a/drivers/acpi/acpica/dbutils.c b/drivers/acpi/acpica/dbutils.c
new file mode 100644
index 000000000..58b039dd7
--- /dev/null
+++ b/drivers/acpi/acpica/dbutils.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbutils - AML debugger utilities
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acdebug.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbutils")
+
+/* Local prototypes */
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+acpi_status acpi_db_second_pass_parse(union acpi_parse_object *root);
+
+void acpi_db_dump_buffer(u32 address);
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_match_argument
+ *
+ * PARAMETERS: user_argument - User command line
+ * arguments - Array of commands to match against
+ *
+ * RETURN: Index into command array or ACPI_TYPE_NOT_FOUND if not found
+ *
+ * DESCRIPTION: Search command array for a command match
+ *
+ ******************************************************************************/
+
+acpi_object_type
+acpi_db_match_argument(char *user_argument,
+ struct acpi_db_argument_info *arguments)
+{
+ u32 i;
+
+ if (!user_argument || user_argument[0] == 0) {
+ return (ACPI_TYPE_NOT_FOUND);
+ }
+
+ for (i = 0; arguments[i].name; i++) {
+ if (strstr(ACPI_CAST_PTR(char, arguments[i].name),
+ ACPI_CAST_PTR(char,
+ user_argument)) == arguments[i].name) {
+ return (i);
+ }
+ }
+
+ /* Argument not recognized */
+
+ return (ACPI_TYPE_NOT_FOUND);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_set_output_destination
+ *
+ * PARAMETERS: output_flags - Current flags word
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Set the current destination for debugger output. Also sets
+ * the debug output level accordingly.
+ *
+ ******************************************************************************/
+
+void acpi_db_set_output_destination(u32 output_flags)
+{
+
+ acpi_gbl_db_output_flags = (u8)output_flags;
+
+ if ((output_flags & ACPI_DB_REDIRECTABLE_OUTPUT) &&
+ acpi_gbl_db_output_to_file) {
+ acpi_dbg_level = acpi_gbl_db_debug_level;
+ } else {
+ acpi_dbg_level = acpi_gbl_db_console_debug_level;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_dump_external_object
+ *
+ * PARAMETERS: obj_desc - External ACPI object to dump
+ * level - Nesting level.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump the contents of an ACPI external object
+ *
+ ******************************************************************************/
+
+void acpi_db_dump_external_object(union acpi_object *obj_desc, u32 level)
+{
+ u32 i;
+
+ if (!obj_desc) {
+ acpi_os_printf("[Null Object]\n");
+ return;
+ }
+
+ for (i = 0; i < level; i++) {
+ acpi_os_printf(" ");
+ }
+
+ switch (obj_desc->type) {
+ case ACPI_TYPE_ANY:
+
+ acpi_os_printf("[Null Object] (Type=0)\n");
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ acpi_os_printf("[Integer] = %8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64(obj_desc->integer.value));
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ acpi_os_printf("[String] Length %.2X = ",
+ obj_desc->string.length);
+ acpi_ut_print_string(obj_desc->string.pointer, ACPI_UINT8_MAX);
+ acpi_os_printf("\n");
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ acpi_os_printf("[Buffer] Length %.2X = ",
+ obj_desc->buffer.length);
+ if (obj_desc->buffer.length) {
+ if (obj_desc->buffer.length > 16) {
+ acpi_os_printf("\n");
+ }
+
+ acpi_ut_debug_dump_buffer(ACPI_CAST_PTR
+ (u8,
+ obj_desc->buffer.pointer),
+ obj_desc->buffer.length,
+ DB_BYTE_DISPLAY, _COMPONENT);
+ } else {
+ acpi_os_printf("\n");
+ }
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ acpi_os_printf("[Package] Contains %u Elements:\n",
+ obj_desc->package.count);
+
+ for (i = 0; i < obj_desc->package.count; i++) {
+ acpi_db_dump_external_object(&obj_desc->package.
+ elements[i], level + 1);
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ acpi_os_printf("[Object Reference] = ");
+ acpi_db_display_internal_object(obj_desc->reference.handle,
+ NULL);
+ break;
+
+ case ACPI_TYPE_PROCESSOR:
+
+ acpi_os_printf("[Processor]\n");
+ break;
+
+ case ACPI_TYPE_POWER:
+
+ acpi_os_printf("[Power Resource]\n");
+ break;
+
+ default:
+
+ acpi_os_printf("[Unknown Type] %X\n", obj_desc->type);
+ break;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_prep_namestring
+ *
+ * PARAMETERS: name - String to prepare
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Translate all forward slashes and dots to backslashes.
+ *
+ ******************************************************************************/
+
+void acpi_db_prep_namestring(char *name)
+{
+
+ if (!name) {
+ return;
+ }
+
+ acpi_ut_strupr(name);
+
+ /* Convert a leading forward slash to a backslash */
+
+ if (*name == '/') {
+ *name = '\\';
+ }
+
+ /* Ignore a leading backslash, this is the root prefix */
+
+ if (ACPI_IS_ROOT_PREFIX(*name)) {
+ name++;
+ }
+
+ /* Convert all slash path separators to dots */
+
+ while (*name) {
+ if ((*name == '/') || (*name == '\\')) {
+ *name = '.';
+ }
+
+ name++;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_local_ns_lookup
+ *
+ * PARAMETERS: name - Name to lookup
+ *
+ * RETURN: Pointer to a namespace node, null on failure
+ *
+ * DESCRIPTION: Lookup a name in the ACPI namespace
+ *
+ * Note: Currently begins search from the root. Could be enhanced to use
+ * the current prefix (scope) node as the search beginning point.
+ *
+ ******************************************************************************/
+
+struct acpi_namespace_node *acpi_db_local_ns_lookup(char *name)
+{
+ char *internal_path;
+ acpi_status status;
+ struct acpi_namespace_node *node = NULL;
+
+ acpi_db_prep_namestring(name);
+
+ /* Build an internal namestring */
+
+ status = acpi_ns_internalize_name(name, &internal_path);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Invalid namestring: %s\n", name);
+ return (NULL);
+ }
+
+ /*
+ * Lookup the name.
+ * (Uses root node as the search starting point)
+ */
+ status = acpi_ns_lookup(NULL, internal_path, ACPI_TYPE_ANY,
+ ACPI_IMODE_EXECUTE,
+ ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE,
+ NULL, &node);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not locate name: %s, %s\n",
+ name, acpi_format_exception(status));
+ }
+
+ ACPI_FREE(internal_path);
+ return (node);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_uint32_to_hex_string
+ *
+ * PARAMETERS: value - The value to be converted to string
+ * buffer - Buffer for result (not less than 11 bytes)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Convert the unsigned 32-bit value to the hexadecimal image
+ *
+ * NOTE: It is the caller's responsibility to ensure that the length of buffer
+ * is sufficient.
+ *
+ ******************************************************************************/
+
+void acpi_db_uint32_to_hex_string(u32 value, char *buffer)
+{
+ int i;
+
+ if (value == 0) {
+ strcpy(buffer, "0");
+ return;
+ }
+
+ buffer[8] = '\0';
+
+ for (i = 7; i >= 0; i--) {
+ buffer[i] = acpi_gbl_upper_hex_digits[value & 0x0F];
+ value = value >> 4;
+ }
+}
+
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_second_pass_parse
+ *
+ * PARAMETERS: root - Root of the parse tree
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Second pass parse of the ACPI tables. We need to wait until
+ * second pass to parse the control methods
+ *
+ ******************************************************************************/
+
+acpi_status acpi_db_second_pass_parse(union acpi_parse_object *root)
+{
+ union acpi_parse_object *op = root;
+ union acpi_parse_object *method;
+ union acpi_parse_object *search_op;
+ union acpi_parse_object *start_op;
+ acpi_status status = AE_OK;
+ u32 base_aml_offset;
+ struct acpi_walk_state *walk_state;
+
+ ACPI_FUNCTION_ENTRY();
+
+ acpi_os_printf("Pass two parse ....\n");
+
+ while (op) {
+ if (op->common.aml_opcode == AML_METHOD_OP) {
+ method = op;
+
+ /* Create a new walk state for the parse */
+
+ walk_state =
+ acpi_ds_create_walk_state(0, NULL, NULL, NULL);
+ if (!walk_state) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Init the Walk State */
+
+ walk_state->parser_state.aml =
+ walk_state->parser_state.aml_start =
+ method->named.data;
+ walk_state->parser_state.aml_end =
+ walk_state->parser_state.pkg_end =
+ method->named.data + method->named.length;
+ walk_state->parser_state.start_scope = op;
+
+ walk_state->descending_callback =
+ acpi_ds_load1_begin_op;
+ walk_state->ascending_callback = acpi_ds_load1_end_op;
+
+ /* Perform the AML parse */
+
+ status = acpi_ps_parse_aml(walk_state);
+
+ base_aml_offset =
+ (method->common.value.arg)->common.aml_offset + 1;
+ start_op = (method->common.value.arg)->common.next;
+ search_op = start_op;
+
+ while (search_op) {
+ search_op->common.aml_offset += base_aml_offset;
+ search_op =
+ acpi_ps_get_depth_next(start_op, search_op);
+ }
+ }
+
+ if (op->common.aml_opcode == AML_REGION_OP) {
+
+ /* TBD: [Investigate] this isn't quite the right thing to do! */
+ /*
+ *
+ * Method = (ACPI_DEFERRED_OP *) Op;
+ * Status = acpi_ps_parse_aml (Op, Method->Body, Method->body_length);
+ */
+ }
+
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+
+ op = acpi_ps_get_depth_next(root, op);
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_dump_buffer
+ *
+ * PARAMETERS: address - Pointer to the buffer
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print a portion of a buffer
+ *
+ ******************************************************************************/
+
+void acpi_db_dump_buffer(u32 address)
+{
+
+ acpi_os_printf("\nLocation %X:\n", address);
+
+ acpi_dbg_level |= ACPI_LV_TABLES;
+ acpi_ut_debug_dump_buffer(ACPI_TO_POINTER(address), 64, DB_BYTE_DISPLAY,
+ ACPI_UINT32_MAX);
+}
+#endif
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
new file mode 100644
index 000000000..9dfd693cd
--- /dev/null
+++ b/drivers/acpi/acpica/dbxface.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dbxface - AML Debugger external interfaces
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "amlcode.h"
+#include "acdebug.h"
+#include "acinterp.h"
+#include "acparser.h"
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ACPI_MODULE_NAME("dbxface")
+
+/* Local prototypes */
+static acpi_status
+acpi_db_start_command(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op);
+
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+void acpi_db_method_end(struct acpi_walk_state *walk_state);
+#endif
+
+#ifdef ACPI_DISASSEMBLER
+static union acpi_parse_object *acpi_db_get_display_op(struct acpi_walk_state
+ *walk_state,
+ union acpi_parse_object
+ *op);
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_start_command
+ *
+ * PARAMETERS: walk_state - Current walk
+ * op - Current executing Op, from AML interpreter
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enter debugger command loop
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_start_command(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ acpi_status status;
+
+ /* TBD: [Investigate] are there namespace locking issues here? */
+
+ /* acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); */
+
+ /* Go into the command loop and await next user command */
+
+ acpi_gbl_method_executing = TRUE;
+ status = AE_CTRL_TRUE;
+
+ while (status == AE_CTRL_TRUE) {
+
+ /* Notify the completion of the command */
+
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ /* Wait the readiness of the command */
+
+ status = acpi_os_wait_command_ready();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ status =
+ acpi_db_command_dispatch(acpi_gbl_db_line_buf, walk_state,
+ op);
+ }
+
+ /* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */
+
+error_exit:
+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While parsing/handling command line"));
+ }
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_signal_break_point
+ *
+ * PARAMETERS: walk_state - Current walk
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Called for AML_BREAKPOINT_OP
+ *
+ ******************************************************************************/
+
+void acpi_db_signal_break_point(struct acpi_walk_state *walk_state)
+{
+
+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
+ /*
+ * Set the single-step flag. This will cause the debugger (if present)
+ * to break to the console within the AML debugger at the start of the
+ * next AML instruction.
+ */
+ acpi_gbl_cm_single_step = TRUE;
+ acpi_os_printf("**break** Executed AML BreakPoint opcode\n");
+}
+
+#ifdef ACPI_DISASSEMBLER
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_get_display_op
+ *
+ * PARAMETERS: walk_state - Current walk
+ * op - Current executing op (from aml interpreter)
+ *
+ * RETURN: Opcode to display
+ *
+ * DESCRIPTION: Find the opcode to display during single stepping
+ *
+ ******************************************************************************/
+
+static union acpi_parse_object *acpi_db_get_display_op(struct acpi_walk_state
+ *walk_state,
+ union acpi_parse_object
+ *op)
+{
+ union acpi_parse_object *display_op;
+ union acpi_parse_object *parent_op;
+
+ display_op = op;
+ parent_op = op->common.parent;
+ if (parent_op) {
+ if ((walk_state->control_state) &&
+ (walk_state->control_state->common.state ==
+ ACPI_CONTROL_PREDICATE_EXECUTING)) {
+ /*
+ * We are executing the predicate of an IF or WHILE statement
+ * Search upwards for the containing IF or WHILE so that the
+ * entire predicate can be displayed.
+ */
+ while (parent_op) {
+ if ((parent_op->common.aml_opcode == AML_IF_OP)
+ || (parent_op->common.aml_opcode ==
+ AML_WHILE_OP)) {
+ display_op = parent_op;
+ break;
+ }
+ parent_op = parent_op->common.parent;
+ }
+ } else {
+ while (parent_op) {
+ if ((parent_op->common.aml_opcode == AML_IF_OP)
+ || (parent_op->common.aml_opcode ==
+ AML_ELSE_OP)
+ || (parent_op->common.aml_opcode ==
+ AML_SCOPE_OP)
+ || (parent_op->common.aml_opcode ==
+ AML_METHOD_OP)
+ || (parent_op->common.aml_opcode ==
+ AML_WHILE_OP)) {
+ break;
+ }
+ display_op = parent_op;
+ parent_op = parent_op->common.parent;
+ }
+ }
+ }
+ return display_op;
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_single_step
+ *
+ * PARAMETERS: walk_state - Current walk
+ * op - Current executing op (from aml interpreter)
+ * opcode_class - Class of the current AML Opcode
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Called just before execution of an AML opcode.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_db_single_step(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op, u32 opcode_class)
+{
+ union acpi_parse_object *next;
+ acpi_status status = AE_OK;
+ u32 original_debug_level;
+ u32 aml_offset;
+
+ ACPI_FUNCTION_ENTRY();
+
+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return (AE_OK);
+ }
+#endif
+
+ /* Check the abort flag */
+
+ if (acpi_gbl_abort_method) {
+ acpi_gbl_abort_method = FALSE;
+ return (AE_ABORT_METHOD);
+ }
+
+ aml_offset = (u32)ACPI_PTR_DIFF(op->common.aml,
+ walk_state->parser_state.aml_start);
+
+ /* Check for single-step breakpoint */
+
+ if (walk_state->method_breakpoint &&
+ (walk_state->method_breakpoint <= aml_offset)) {
+
+ /* Check if the breakpoint has been reached or passed */
+ /* Hit the breakpoint, resume single step, reset breakpoint */
+
+ acpi_os_printf("***Break*** at AML offset %X\n", aml_offset);
+ acpi_gbl_cm_single_step = TRUE;
+ acpi_gbl_step_to_next_call = FALSE;
+ walk_state->method_breakpoint = 0;
+ }
+
+ /* Check for user breakpoint (Must be on exact Aml offset) */
+
+ else if (walk_state->user_breakpoint &&
+ (walk_state->user_breakpoint == aml_offset)) {
+ acpi_os_printf("***UserBreakpoint*** at AML offset %X\n",
+ aml_offset);
+ acpi_gbl_cm_single_step = TRUE;
+ acpi_gbl_step_to_next_call = FALSE;
+ walk_state->method_breakpoint = 0;
+ }
+
+ /*
+ * Check if this is an opcode that we are interested in --
+ * namely, opcodes that have arguments
+ */
+ if (op->common.aml_opcode == AML_INT_NAMEDFIELD_OP) {
+ return (AE_OK);
+ }
+
+ switch (opcode_class) {
+ case AML_CLASS_UNKNOWN:
+ case AML_CLASS_ARGUMENT: /* constants, literals, etc. do nothing */
+
+ return (AE_OK);
+
+ default:
+
+ /* All other opcodes -- continue */
+ break;
+ }
+
+ /*
+ * Under certain debug conditions, display this opcode and its operands
+ */
+ if ((acpi_gbl_db_output_to_file) ||
+ (acpi_gbl_cm_single_step) || (acpi_dbg_level & ACPI_LV_PARSE)) {
+ if ((acpi_gbl_db_output_to_file) ||
+ (acpi_dbg_level & ACPI_LV_PARSE)) {
+ acpi_os_printf
+ ("\nAML Debug: Next AML Opcode to execute:\n");
+ }
+
+ /*
+ * Display this op (and only this op - zero out the NEXT field
+ * temporarily, and disable parser trace output for the duration of
+ * the display because we don't want the extraneous debug output)
+ */
+ original_debug_level = acpi_dbg_level;
+ acpi_dbg_level &= ~(ACPI_LV_PARSE | ACPI_LV_FUNCTIONS);
+ next = op->common.next;
+ op->common.next = NULL;
+
+ /* Now we can disassemble and display it */
+
+#ifdef ACPI_DISASSEMBLER
+ acpi_dm_disassemble(walk_state,
+ acpi_db_get_display_op(walk_state, op),
+ ACPI_UINT32_MAX);
+#else
+ /*
+ * The AML Disassembler is not configured - at least we can
+ * display the opcode value and name
+ */
+ acpi_os_printf("AML Opcode: %4.4X %s\n", op->common.aml_opcode,
+ acpi_ps_get_opcode_name(op->common.aml_opcode));
+#endif
+
+ if ((op->common.aml_opcode == AML_IF_OP) ||
+ (op->common.aml_opcode == AML_WHILE_OP)) {
+ if (walk_state->control_state->common.value) {
+ acpi_os_printf
+ ("Predicate = [True], IF block was executed\n");
+ } else {
+ acpi_os_printf
+ ("Predicate = [False], Skipping IF block\n");
+ }
+ } else if (op->common.aml_opcode == AML_ELSE_OP) {
+ acpi_os_printf
+ ("Predicate = [False], ELSE block was executed\n");
+ }
+
+ /* Restore everything */
+
+ op->common.next = next;
+ acpi_os_printf("\n");
+ if ((acpi_gbl_db_output_to_file) ||
+ (acpi_dbg_level & ACPI_LV_PARSE)) {
+ acpi_os_printf("\n");
+ }
+ acpi_dbg_level = original_debug_level;
+ }
+
+ /* If we are not single stepping, just continue executing the method */
+
+ if (!acpi_gbl_cm_single_step) {
+ return (AE_OK);
+ }
+
+ /*
+ * If we are executing a step-to-call command,
+ * Check if this is a method call.
+ */
+ if (acpi_gbl_step_to_next_call) {
+ if (op->common.aml_opcode != AML_INT_METHODCALL_OP) {
+
+ /* Not a method call, just keep executing */
+
+ return (AE_OK);
+ }
+
+ /* Found a method call, stop executing */
+
+ acpi_gbl_step_to_next_call = FALSE;
+ }
+
+ /*
+ * If the next opcode is a method call, we will "step over" it
+ * by default.
+ */
+ if (op->common.aml_opcode == AML_INT_METHODCALL_OP) {
+
+ /* Force no more single stepping while executing called method */
+
+ acpi_gbl_cm_single_step = FALSE;
+
+ /*
+ * Set the breakpoint on/before the call, it will stop execution
+ * as soon as we return
+ */
+ walk_state->method_breakpoint = 1; /* Must be non-zero! */
+ }
+
+ acpi_ex_exit_interpreter();
+ status = acpi_db_start_command(walk_state, op);
+ acpi_ex_enter_interpreter();
+
+ /* User commands complete, continue execution of the interrupted method */
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_initialize_debugger
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Init and start debugger
+ *
+ ******************************************************************************/
+
+acpi_status acpi_initialize_debugger(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_initialize_debugger);
+
+ /* Init globals */
+
+ acpi_gbl_db_buffer = NULL;
+ acpi_gbl_db_filename = NULL;
+ acpi_gbl_db_output_to_file = FALSE;
+
+ acpi_gbl_db_debug_level = ACPI_LV_VERBOSITY2;
+ acpi_gbl_db_console_debug_level = ACPI_NORMAL_DEFAULT | ACPI_LV_TABLES;
+ acpi_gbl_db_output_flags = ACPI_DB_CONSOLE_OUTPUT;
+
+ acpi_gbl_db_opt_no_ini_methods = FALSE;
+ acpi_gbl_db_opt_no_region_support = FALSE;
+
+ acpi_gbl_db_buffer = acpi_os_allocate(ACPI_DEBUG_BUFFER_SIZE);
+ if (!acpi_gbl_db_buffer) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+ memset(acpi_gbl_db_buffer, 0, ACPI_DEBUG_BUFFER_SIZE);
+
+ /* Initial scope is the root */
+
+ acpi_gbl_db_scope_buf[0] = AML_ROOT_PREFIX;
+ acpi_gbl_db_scope_buf[1] = 0;
+ acpi_gbl_db_scope_node = acpi_gbl_root_node;
+
+ /* Initialize user commands loop */
+
+ acpi_gbl_db_terminate_loop = FALSE;
+
+ /*
+ * If configured for multi-thread support, the debug executor runs in
+ * a separate thread so that the front end can be in another address
+ * space, environment, or even another machine.
+ */
+ if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) {
+
+ /* These were created with one unit, grab it */
+
+ status = acpi_os_initialize_debugger();
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not get debugger mutex\n");
+ return_ACPI_STATUS(status);
+ }
+
+ /* Create the debug execution thread to execute commands */
+
+ acpi_gbl_db_threads_terminated = FALSE;
+ status = acpi_os_execute(OSL_DEBUGGER_MAIN_THREAD,
+ acpi_db_execute_thread, NULL);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not start debugger thread"));
+ acpi_gbl_db_threads_terminated = TRUE;
+ return_ACPI_STATUS(status);
+ }
+ } else {
+ acpi_gbl_db_thread_id = acpi_os_get_thread_id();
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_initialize_debugger)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_terminate_debugger
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Stop debugger
+ *
+ ******************************************************************************/
+void acpi_terminate_debugger(void)
+{
+
+ /* Terminate the AML Debugger */
+
+ acpi_gbl_db_terminate_loop = TRUE;
+
+ if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) {
+
+ /* Wait the AML Debugger threads */
+
+ while (!acpi_gbl_db_threads_terminated) {
+ acpi_os_sleep(100);
+ }
+
+ acpi_os_terminate_debugger();
+ }
+
+ if (acpi_gbl_db_buffer) {
+ acpi_os_free(acpi_gbl_db_buffer);
+ acpi_gbl_db_buffer = NULL;
+ }
+
+ /* Ensure that debug output is now disabled */
+
+ acpi_gbl_db_output_flags = ACPI_DB_DISABLE_OUTPUT;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_terminate_debugger)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_set_debugger_thread_id
+ *
+ * PARAMETERS: thread_id - Debugger thread ID
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Set debugger thread ID
+ *
+ ******************************************************************************/
+void acpi_set_debugger_thread_id(acpi_thread_id thread_id)
+{
+ acpi_gbl_db_thread_id = thread_id;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_set_debugger_thread_id)
diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c
new file mode 100644
index 000000000..2963d1579
--- /dev/null
+++ b/drivers/acpi/acpica/dsargs.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dsargs - Support for execution of dynamic arguments for static
+ * objects (regions, fields, buffer fields, etc.)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dsargs")
+
+/* Local prototypes */
+static acpi_status
+acpi_ds_execute_arguments(struct acpi_namespace_node *node,
+ struct acpi_namespace_node *scope_node,
+ u32 aml_length, u8 *aml_start);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_execute_arguments
+ *
+ * PARAMETERS: node - Object NS node
+ * scope_node - Parent NS node
+ * aml_length - Length of executable AML
+ * aml_start - Pointer to the AML
+ *
+ * RETURN: Status.
+ *
+ * DESCRIPTION: Late (deferred) execution of region or field arguments
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ds_execute_arguments(struct acpi_namespace_node *node,
+ struct acpi_namespace_node *scope_node,
+ u32 aml_length, u8 *aml_start)
+{
+ acpi_status status;
+ union acpi_parse_object *op;
+ struct acpi_walk_state *walk_state;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_execute_arguments, aml_start);
+
+ /* Allocate a new parser op to be the root of the parsed tree */
+
+ op = acpi_ps_alloc_op(AML_INT_EVAL_SUBTREE_OP, aml_start);
+ if (!op) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Save the Node for use in acpi_ps_parse_aml */
+
+ op->common.node = scope_node;
+
+ /* Create and initialize a new parser state */
+
+ walk_state = acpi_ds_create_walk_state(0, NULL, NULL, NULL);
+ if (!walk_state) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ status = acpi_ds_init_aml_walk(walk_state, op, NULL, aml_start,
+ aml_length, NULL, ACPI_IMODE_LOAD_PASS1);
+ if (ACPI_FAILURE(status)) {
+ acpi_ds_delete_walk_state(walk_state);
+ goto cleanup;
+ }
+
+ /* Mark this parse as a deferred opcode */
+
+ walk_state->parse_flags = ACPI_PARSE_DEFERRED_OP;
+ walk_state->deferred_node = node;
+
+ /* Pass1: Parse the entire declaration */
+
+ status = acpi_ps_parse_aml(walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /* Get and init the Op created above */
+
+ op->common.node = node;
+ acpi_ps_delete_parse_tree(op);
+
+ /* Evaluate the deferred arguments */
+
+ op = acpi_ps_alloc_op(AML_INT_EVAL_SUBTREE_OP, aml_start);
+ if (!op) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ op->common.node = scope_node;
+
+ /* Create and initialize a new parser state */
+
+ walk_state = acpi_ds_create_walk_state(0, NULL, NULL, NULL);
+ if (!walk_state) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Execute the opcode and arguments */
+
+ status = acpi_ds_init_aml_walk(walk_state, op, NULL, aml_start,
+ aml_length, NULL, ACPI_IMODE_EXECUTE);
+ if (ACPI_FAILURE(status)) {
+ acpi_ds_delete_walk_state(walk_state);
+ goto cleanup;
+ }
+
+ /* Mark this execution as a deferred opcode */
+
+ walk_state->deferred_node = node;
+ status = acpi_ps_parse_aml(walk_state);
+
+cleanup:
+ acpi_ps_delete_parse_tree(op);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_get_buffer_field_arguments
+ *
+ * PARAMETERS: obj_desc - A valid buffer_field object
+ *
+ * RETURN: Status.
+ *
+ * DESCRIPTION: Get buffer_field Buffer and Index. This implements the late
+ * evaluation of these field attributes.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_get_buffer_field_arguments(union acpi_operand_object *obj_desc)
+{
+ union acpi_operand_object *extra_desc;
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_get_buffer_field_arguments, obj_desc);
+
+ if (obj_desc->common.flags & AOPOBJ_DATA_VALID) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Get the AML pointer (method object) and buffer_field node */
+
+ extra_desc = acpi_ns_get_secondary_object(obj_desc);
+ node = obj_desc->buffer_field.node;
+
+ ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname
+ (ACPI_TYPE_BUFFER_FIELD, node, NULL));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "[%4.4s] BufferField Arg Init\n",
+ acpi_ut_get_node_name(node)));
+
+ /* Execute the AML code for the term_arg arguments */
+
+ status = acpi_ds_execute_arguments(node, node->parent,
+ extra_desc->extra.aml_length,
+ extra_desc->extra.aml_start);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_get_bank_field_arguments
+ *
+ * PARAMETERS: obj_desc - A valid bank_field object
+ *
+ * RETURN: Status.
+ *
+ * DESCRIPTION: Get bank_field bank_value. This implements the late
+ * evaluation of these field attributes.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_get_bank_field_arguments(union acpi_operand_object *obj_desc)
+{
+ union acpi_operand_object *extra_desc;
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_get_bank_field_arguments, obj_desc);
+
+ if (obj_desc->common.flags & AOPOBJ_DATA_VALID) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Get the AML pointer (method object) and bank_field node */
+
+ extra_desc = acpi_ns_get_secondary_object(obj_desc);
+ node = obj_desc->bank_field.node;
+
+ ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname
+ (ACPI_TYPE_LOCAL_BANK_FIELD, node, NULL));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "[%4.4s] BankField Arg Init\n",
+ acpi_ut_get_node_name(node)));
+
+ /* Execute the AML code for the term_arg arguments */
+
+ status = acpi_ds_execute_arguments(node, node->parent,
+ extra_desc->extra.aml_length,
+ extra_desc->extra.aml_start);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ut_add_address_range(obj_desc->region.space_id,
+ obj_desc->region.address,
+ obj_desc->region.length, node);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_get_buffer_arguments
+ *
+ * PARAMETERS: obj_desc - A valid Buffer object
+ *
+ * RETURN: Status.
+ *
+ * DESCRIPTION: Get Buffer length and initializer byte list. This implements
+ * the late evaluation of these attributes.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ds_get_buffer_arguments(union acpi_operand_object *obj_desc)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_get_buffer_arguments, obj_desc);
+
+ if (obj_desc->common.flags & AOPOBJ_DATA_VALID) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Get the Buffer node */
+
+ node = obj_desc->buffer.node;
+ if (!node) {
+ ACPI_ERROR((AE_INFO,
+ "No pointer back to namespace node in buffer object %p",
+ obj_desc));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Buffer Arg Init\n"));
+
+ /* Execute the AML code for the term_arg arguments */
+
+ status = acpi_ds_execute_arguments(node, node,
+ obj_desc->buffer.aml_length,
+ obj_desc->buffer.aml_start);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_get_package_arguments
+ *
+ * PARAMETERS: obj_desc - A valid Package object
+ *
+ * RETURN: Status.
+ *
+ * DESCRIPTION: Get Package length and initializer byte list. This implements
+ * the late evaluation of these attributes.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ds_get_package_arguments(union acpi_operand_object *obj_desc)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_get_package_arguments, obj_desc);
+
+ if (obj_desc->common.flags & AOPOBJ_DATA_VALID) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Get the Package node */
+
+ node = obj_desc->package.node;
+ if (!node) {
+ ACPI_ERROR((AE_INFO,
+ "No pointer back to namespace node in package %p",
+ obj_desc));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Package Argument Init, AML Ptr: %p\n",
+ obj_desc->package.aml_start));
+
+ /* Execute the AML code for the term_arg arguments */
+
+ status = acpi_ds_execute_arguments(node, node,
+ obj_desc->package.aml_length,
+ obj_desc->package.aml_start);
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_get_region_arguments
+ *
+ * PARAMETERS: obj_desc - A valid region object
+ *
+ * RETURN: Status.
+ *
+ * DESCRIPTION: Get region address and length. This implements the late
+ * evaluation of these region attributes.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+ union acpi_operand_object *extra_desc;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_get_region_arguments, obj_desc);
+
+ if (obj_desc->region.flags & AOPOBJ_DATA_VALID) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ extra_desc = acpi_ns_get_secondary_object(obj_desc);
+ if (!extra_desc) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /* Get the Region node */
+
+ node = obj_desc->region.node;
+
+ ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname
+ (ACPI_TYPE_REGION, node, NULL));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[%4.4s] OpRegion Arg Init at AML %p\n",
+ acpi_ut_get_node_name(node),
+ extra_desc->extra.aml_start));
+
+ /* Execute the argument AML */
+
+ status = acpi_ds_execute_arguments(node, extra_desc->extra.scope_node,
+ extra_desc->extra.aml_length,
+ extra_desc->extra.aml_start);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ut_add_address_range(obj_desc->region.space_id,
+ obj_desc->region.address,
+ obj_desc->region.length, node);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c
new file mode 100644
index 000000000..849261914
--- /dev/null
+++ b/drivers/acpi/acpica/dscontrol.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dscontrol - Support for execution control opcodes -
+ * if/else/while/return
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acdebug.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dscontrol")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_exec_begin_control_op
+ *
+ * PARAMETERS: walk_list - The list that owns the walk stack
+ * op - The control Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Handles all control ops encountered during control method
+ * execution.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ acpi_status status = AE_OK;
+ union acpi_generic_state *control_state;
+
+ ACPI_FUNCTION_NAME(ds_exec_begin_control_op);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p Opcode=%2.2X State=%p\n",
+ op, op->common.aml_opcode, walk_state));
+
+ switch (op->common.aml_opcode) {
+ case AML_WHILE_OP:
+ /*
+ * If this is an additional iteration of a while loop, continue.
+ * There is no need to allocate a new control state.
+ */
+ if (walk_state->control_state) {
+ if (walk_state->control_state->control.
+ aml_predicate_start ==
+ (walk_state->parser_state.aml - 1)) {
+
+ /* Reset the state to start-of-loop */
+
+ walk_state->control_state->common.state =
+ ACPI_CONTROL_CONDITIONAL_EXECUTING;
+ break;
+ }
+ }
+
+ ACPI_FALLTHROUGH;
+
+ case AML_IF_OP:
+ /*
+ * IF/WHILE: Create a new control state to manage these
+ * constructs. We need to manage these as a stack, in order
+ * to handle nesting.
+ */
+ control_state = acpi_ut_create_control_state();
+ if (!control_state) {
+ status = AE_NO_MEMORY;
+ break;
+ }
+ /*
+ * Save a pointer to the predicate for multiple executions
+ * of a loop
+ */
+ control_state->control.aml_predicate_start =
+ walk_state->parser_state.aml - 1;
+ control_state->control.package_end =
+ walk_state->parser_state.pkg_end;
+ control_state->control.opcode = op->common.aml_opcode;
+ control_state->control.loop_timeout = acpi_os_get_timer() +
+ ((u64)acpi_gbl_max_loop_iterations * ACPI_100NSEC_PER_SEC);
+
+ /* Push the control state on this walk's control stack */
+
+ acpi_ut_push_generic_state(&walk_state->control_state,
+ control_state);
+ break;
+
+ case AML_ELSE_OP:
+
+ /* Predicate is in the state object */
+ /* If predicate is true, the IF was executed, ignore ELSE part */
+
+ if (walk_state->last_predicate) {
+ status = AE_CTRL_TRUE;
+ }
+
+ break;
+
+ case AML_RETURN_OP:
+
+ break;
+
+ default:
+
+ break;
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_exec_end_control_op
+ *
+ * PARAMETERS: walk_list - The list that owns the walk stack
+ * op - The control Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Handles all control ops encountered during control method
+ * execution.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_exec_end_control_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ acpi_status status = AE_OK;
+ union acpi_generic_state *control_state;
+
+ ACPI_FUNCTION_NAME(ds_exec_end_control_op);
+
+ switch (op->common.aml_opcode) {
+ case AML_IF_OP:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "[IF_OP] Op=%p\n", op));
+
+ /*
+ * Save the result of the predicate in case there is an
+ * ELSE to come
+ */
+ walk_state->last_predicate =
+ (u8)walk_state->control_state->common.value;
+
+ /*
+ * Pop the control state that was created at the start
+ * of the IF and free it
+ */
+ control_state =
+ acpi_ut_pop_generic_state(&walk_state->control_state);
+ acpi_ut_delete_generic_state(control_state);
+ break;
+
+ case AML_ELSE_OP:
+
+ break;
+
+ case AML_WHILE_OP:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", op));
+
+ control_state = walk_state->control_state;
+ if (control_state->common.value) {
+
+ /* Predicate was true, the body of the loop was just executed */
+
+ /*
+ * This infinite loop detection mechanism allows the interpreter
+ * to escape possibly infinite loops. This can occur in poorly
+ * written AML when the hardware does not respond within a while
+ * loop and the loop does not implement a timeout.
+ */
+ if (ACPI_TIME_AFTER(acpi_os_get_timer(),
+ control_state->control.
+ loop_timeout)) {
+ status = AE_AML_LOOP_TIMEOUT;
+ break;
+ }
+
+ /*
+ * Go back and evaluate the predicate and maybe execute the loop
+ * another time
+ */
+ status = AE_CTRL_PENDING;
+ walk_state->aml_last_while =
+ control_state->control.aml_predicate_start;
+ break;
+ }
+
+ /* Predicate was false, terminate this while loop */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "[WHILE_OP] termination! Op=%p\n", op));
+
+ /* Pop this control state and free it */
+
+ control_state =
+ acpi_ut_pop_generic_state(&walk_state->control_state);
+ acpi_ut_delete_generic_state(control_state);
+ break;
+
+ case AML_RETURN_OP:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "[RETURN_OP] Op=%p Arg=%p\n", op,
+ op->common.value.arg));
+
+ /*
+ * One optional operand -- the return value
+ * It can be either an immediate operand or a result that
+ * has been bubbled up the tree
+ */
+ if (op->common.value.arg) {
+
+ /* Since we have a real Return(), delete any implicit return */
+
+ acpi_ds_clear_implicit_return(walk_state);
+
+ /* Return statement has an immediate operand */
+
+ status =
+ acpi_ds_create_operands(walk_state,
+ op->common.value.arg);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /*
+ * If value being returned is a Reference (such as
+ * an arg or local), resolve it now because it may
+ * cease to exist at the end of the method.
+ */
+ status =
+ acpi_ex_resolve_to_value(&walk_state->operands[0],
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /*
+ * Get the return value and save as the last result
+ * value. This is the only place where walk_state->return_desc
+ * is set to anything other than zero!
+ */
+ walk_state->return_desc = walk_state->operands[0];
+ } else if (walk_state->result_count) {
+
+ /* Since we have a real Return(), delete any implicit return */
+
+ acpi_ds_clear_implicit_return(walk_state);
+
+ /*
+ * The return value has come from a previous calculation.
+ *
+ * If value being returned is a Reference (such as
+ * an arg or local), resolve it now because it may
+ * cease to exist at the end of the method.
+ *
+ * Allow references created by the Index operator to return
+ * unchanged.
+ */
+ if ((ACPI_GET_DESCRIPTOR_TYPE
+ (walk_state->results->results.obj_desc[0]) ==
+ ACPI_DESC_TYPE_OPERAND)
+ && ((walk_state->results->results.obj_desc[0])->
+ common.type == ACPI_TYPE_LOCAL_REFERENCE)
+ && ((walk_state->results->results.obj_desc[0])->
+ reference.class != ACPI_REFCLASS_INDEX)) {
+ status =
+ acpi_ex_resolve_to_value(&walk_state->
+ results->results.
+ obj_desc[0],
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ walk_state->return_desc =
+ walk_state->results->results.obj_desc[0];
+ } else {
+ /* No return operand */
+
+ if (walk_state->num_operands) {
+ acpi_ut_remove_reference(walk_state->
+ operands[0]);
+ }
+
+ walk_state->operands[0] = NULL;
+ walk_state->num_operands = 0;
+ walk_state->return_desc = NULL;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Completed RETURN_OP State=%p, RetVal=%p\n",
+ walk_state, walk_state->return_desc));
+
+ /* End the control method execution right now */
+
+ status = AE_CTRL_TERMINATE;
+ break;
+
+ case AML_NOOP_OP:
+
+ /* Just do nothing! */
+
+ break;
+
+ case AML_BREAKPOINT_OP:
+
+ acpi_db_signal_break_point(walk_state);
+
+ /* Call to the OSL in case OS wants a piece of the action */
+
+ status = acpi_os_signal(ACPI_SIGNAL_BREAKPOINT,
+ "Executed AML Breakpoint opcode");
+ break;
+
+ case AML_BREAK_OP:
+ case AML_CONTINUE_OP: /* ACPI 2.0 */
+
+ /* Pop and delete control states until we find a while */
+
+ while (walk_state->control_state &&
+ (walk_state->control_state->control.opcode !=
+ AML_WHILE_OP)) {
+ control_state =
+ acpi_ut_pop_generic_state(&walk_state->
+ control_state);
+ acpi_ut_delete_generic_state(control_state);
+ }
+
+ /* No while found? */
+
+ if (!walk_state->control_state) {
+ return (AE_AML_NO_WHILE);
+ }
+
+ /* Was: walk_state->aml_last_while = walk_state->control_state->Control.aml_predicate_start; */
+
+ walk_state->aml_last_while =
+ walk_state->control_state->control.package_end;
+
+ /* Return status depending on opcode */
+
+ if (op->common.aml_opcode == AML_BREAK_OP) {
+ status = AE_CTRL_BREAK;
+ } else {
+ status = AE_CTRL_CONTINUE;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown control opcode=0x%X Op=%p",
+ op->common.aml_opcode, op));
+
+ status = AE_AML_BAD_OPCODE;
+ break;
+ }
+
+ return (status);
+}
diff --git a/drivers/acpi/acpica/dsdebug.c b/drivers/acpi/acpica/dsdebug.c
new file mode 100644
index 000000000..2d99ccf5b
--- /dev/null
+++ b/drivers/acpi/acpica/dsdebug.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dsdebug - Parser/Interpreter interface - debugging
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdispat.h"
+#include "acnamesp.h"
+#ifdef ACPI_DISASSEMBLER
+#include "acdisasm.h"
+#endif
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dsdebug")
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+/* Local prototypes */
+static void
+acpi_ds_print_node_pathname(struct acpi_namespace_node *node,
+ const char *message);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_print_node_pathname
+ *
+ * PARAMETERS: node - Object
+ * message - Prefix message
+ *
+ * DESCRIPTION: Print an object's full namespace pathname
+ * Manages allocation/freeing of a pathname buffer
+ *
+ ******************************************************************************/
+
+static void
+acpi_ds_print_node_pathname(struct acpi_namespace_node *node,
+ const char *message)
+{
+ struct acpi_buffer buffer;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ds_print_node_pathname);
+
+ if (!node) {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_DISPATCH, "[NULL NAME]"));
+ return_VOID;
+ }
+
+ /* Convert handle to full pathname and print it (with supplied message) */
+
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+
+ status = acpi_ns_handle_to_pathname(node, &buffer, TRUE);
+ if (ACPI_SUCCESS(status)) {
+ if (message) {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_DISPATCH, "%s ",
+ message));
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_DISPATCH, "[%s] (Node %p)",
+ (char *)buffer.pointer, node));
+ ACPI_FREE(buffer.pointer);
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_dump_method_stack
+ *
+ * PARAMETERS: status - Method execution status
+ * walk_state - Current state of the parse tree walk
+ * op - Executing parse op
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Called when a method has been aborted because of an error.
+ * Dumps the method execution stack.
+ *
+ ******************************************************************************/
+
+void
+acpi_ds_dump_method_stack(acpi_status status,
+ struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ union acpi_parse_object *next;
+ struct acpi_thread_state *thread;
+ struct acpi_walk_state *next_walk_state;
+ struct acpi_namespace_node *previous_method = NULL;
+ union acpi_operand_object *method_desc;
+
+ ACPI_FUNCTION_TRACE(ds_dump_method_stack);
+
+ /* Ignore control codes, they are not errors */
+
+ if (ACPI_CNTL_EXCEPTION(status)) {
+ return_VOID;
+ }
+
+ /* We may be executing a deferred opcode */
+
+ if (walk_state->deferred_node) {
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Executing subtree for Buffer/Package/Region\n"));
+ return_VOID;
+ }
+
+ /*
+ * If there is no Thread, we are not actually executing a method.
+ * This can happen when the iASL compiler calls the interpreter
+ * to perform constant folding.
+ */
+ thread = walk_state->thread;
+ if (!thread) {
+ return_VOID;
+ }
+
+ /* Display exception and method name */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "\n**** Exception %s during execution of method ",
+ acpi_format_exception(status)));
+
+ acpi_ds_print_node_pathname(walk_state->method_node, NULL);
+
+ /* Display stack of executing methods */
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_DISPATCH,
+ "\n\nMethod Execution Stack:\n"));
+ next_walk_state = thread->walk_state_list;
+
+ /* Walk list of linked walk states */
+
+ while (next_walk_state) {
+ method_desc = next_walk_state->method_desc;
+ if (method_desc) {
+ acpi_ex_stop_trace_method((struct acpi_namespace_node *)
+ method_desc->method.node,
+ method_desc, walk_state);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ " Method [%4.4s] executing: ",
+ acpi_ut_get_node_name(next_walk_state->
+ method_node)));
+
+ /* First method is the currently executing method */
+
+ if (next_walk_state == walk_state) {
+ if (op) {
+
+ /* Display currently executing ASL statement */
+
+ next = op->common.next;
+ op->common.next = NULL;
+
+#ifdef ACPI_DISASSEMBLER
+ if (walk_state->method_node !=
+ acpi_gbl_root_node) {
+
+ /* More verbose if not module-level code */
+
+ acpi_os_printf("Failed at ");
+ acpi_dm_disassemble(next_walk_state, op,
+ ACPI_UINT32_MAX);
+ }
+#endif
+ op->common.next = next;
+ }
+ } else {
+ /*
+ * This method has called another method
+ * NOTE: the method call parse subtree is already deleted at
+ * this point, so we cannot disassemble the method invocation.
+ */
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_DISPATCH,
+ "Call to method "));
+ acpi_ds_print_node_pathname(previous_method, NULL);
+ }
+
+ previous_method = next_walk_state->method_node;
+ next_walk_state = next_walk_state->next;
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_DISPATCH, "\n"));
+ }
+
+ return_VOID;
+}
+
+#else
+void
+acpi_ds_dump_method_stack(acpi_status status,
+ struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ return;
+}
+
+#endif
diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c
new file mode 100644
index 000000000..de175f1b4
--- /dev/null
+++ b/drivers/acpi/acpica/dsfield.c
@@ -0,0 +1,816 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dsfield - Dispatcher field routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+#include "acparser.h"
+
+#ifdef ACPI_EXEC_APP
+#include "aecommon.h"
+#endif
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dsfield")
+
+/* Local prototypes */
+#ifdef ACPI_ASL_COMPILER
+#include "acdisasm.h"
+static acpi_status
+acpi_ds_create_external_region(acpi_status lookup_status,
+ union acpi_parse_object *op,
+ char *path,
+ struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node **node);
+#endif
+
+static acpi_status
+acpi_ds_get_field_names(struct acpi_create_field_info *info,
+ struct acpi_walk_state *walk_state,
+ union acpi_parse_object *arg);
+
+#ifdef ACPI_ASL_COMPILER
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_create_external_region (iASL Disassembler only)
+ *
+ * PARAMETERS: lookup_status - Status from ns_lookup operation
+ * op - Op containing the Field definition and args
+ * path - Pathname of the region
+ * ` walk_state - Current method state
+ * node - Where the new region node is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Add region to the external list if NOT_FOUND. Create a new
+ * region node/object.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ds_create_external_region(acpi_status lookup_status,
+ union acpi_parse_object *op,
+ char *path,
+ struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node **node)
+{
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+
+ if (lookup_status != AE_NOT_FOUND) {
+ return (lookup_status);
+ }
+
+ /*
+ * Table disassembly:
+ * operation_region not found. Generate an External for it, and
+ * insert the name into the namespace.
+ */
+ acpi_dm_add_op_to_external_list(op, path, ACPI_TYPE_REGION, 0, 0);
+
+ status = acpi_ns_lookup(walk_state->scope_info, path, ACPI_TYPE_REGION,
+ ACPI_IMODE_LOAD_PASS1, ACPI_NS_SEARCH_PARENT,
+ walk_state, node);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Must create and install a region object for the new node */
+
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_REGION);
+ if (!obj_desc) {
+ return (AE_NO_MEMORY);
+ }
+
+ obj_desc->region.node = *node;
+ status = acpi_ns_attach_object(*node, obj_desc, ACPI_TYPE_REGION);
+ return (status);
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_create_buffer_field
+ *
+ * PARAMETERS: op - Current parse op (create_XXField)
+ * walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute the create_field operators:
+ * create_bit_field_op,
+ * create_byte_field_op,
+ * create_word_field_op,
+ * create_dword_field_op,
+ * create_qword_field_op,
+ * create_field_op (all of which define a field in a buffer)
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_create_buffer_field(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state)
+{
+ union acpi_parse_object *arg;
+ struct acpi_namespace_node *node;
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *second_desc = NULL;
+ u32 flags;
+
+ ACPI_FUNCTION_TRACE(ds_create_buffer_field);
+
+ /*
+ * Get the name_string argument (name of the new buffer_field)
+ */
+ if (op->common.aml_opcode == AML_CREATE_FIELD_OP) {
+
+ /* For create_field, name is the 4th argument */
+
+ arg = acpi_ps_get_arg(op, 3);
+ } else {
+ /* For all other create_XXXField operators, name is the 3rd argument */
+
+ arg = acpi_ps_get_arg(op, 2);
+ }
+
+ if (!arg) {
+ return_ACPI_STATUS(AE_AML_NO_OPERAND);
+ }
+
+ if (walk_state->deferred_node) {
+ node = walk_state->deferred_node;
+ } else {
+ /* Execute flag should always be set when this function is entered */
+
+ if (!(walk_state->parse_flags & ACPI_PARSE_EXECUTE)) {
+ ACPI_ERROR((AE_INFO, "Parse execute mode is not set"));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ /* Creating new namespace node, should not already exist */
+
+ flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE |
+ ACPI_NS_ERROR_IF_FOUND;
+
+ /*
+ * Mark node temporary if we are executing a normal control
+ * method. (Don't mark if this is a module-level code method)
+ */
+ if (walk_state->method_node &&
+ !(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) {
+ flags |= ACPI_NS_TEMPORARY;
+ }
+
+ /* Enter the name_string into the namespace */
+
+ status = acpi_ns_lookup(walk_state->scope_info,
+ arg->common.value.string, ACPI_TYPE_ANY,
+ ACPI_IMODE_LOAD_PASS1, flags,
+ walk_state, &node);
+ if ((walk_state->parse_flags & ACPI_PARSE_DISASSEMBLE)
+ && status == AE_ALREADY_EXISTS) {
+ status = AE_OK;
+ } else if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ arg->common.value.string, status);
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * We could put the returned object (Node) on the object stack for later,
+ * but for now, we will put it in the "op" object that the parser uses,
+ * so we can get it again at the end of this scope.
+ */
+ op->common.node = node;
+
+ /*
+ * If there is no object attached to the node, this node was just created
+ * and we need to create the field object. Otherwise, this was a lookup
+ * of an existing node and we don't want to create the field object again.
+ */
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (obj_desc) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * The Field definition is not fully parsed at this time.
+ * (We must save the address of the AML for the buffer and index operands)
+ */
+
+ /* Create the buffer field object */
+
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_BUFFER_FIELD);
+ if (!obj_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /*
+ * Remember location in AML stream of the field unit opcode and operands
+ * -- since the buffer and index operands must be evaluated.
+ */
+ second_desc = obj_desc->common.next_object;
+ second_desc->extra.aml_start = op->named.data;
+ second_desc->extra.aml_length = op->named.length;
+ obj_desc->buffer_field.node = node;
+
+ /* Attach constructed field descriptors to parent node */
+
+ status = acpi_ns_attach_object(node, obj_desc, ACPI_TYPE_BUFFER_FIELD);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+cleanup:
+
+ /* Remove local reference to the object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_get_field_names
+ *
+ * PARAMETERS: info - create_field info structure
+ * walk_state - Current method state
+ * arg - First parser arg for the field name list
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Process all named fields in a field declaration. Names are
+ * entered into the namespace.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ds_get_field_names(struct acpi_create_field_info *info,
+ struct acpi_walk_state *walk_state,
+ union acpi_parse_object *arg)
+{
+ acpi_status status;
+ u64 position;
+ union acpi_parse_object *child;
+
+#ifdef ACPI_EXEC_APP
+ union acpi_operand_object *result_desc;
+ union acpi_operand_object *obj_desc;
+ char *name_path;
+#endif
+
+ ACPI_FUNCTION_TRACE_PTR(ds_get_field_names, info);
+
+ /* First field starts at bit zero */
+
+ info->field_bit_position = 0;
+
+ /* Process all elements in the field list (of parse nodes) */
+
+ while (arg) {
+ /*
+ * Four types of field elements are handled:
+ * 1) name - Enters a new named field into the namespace
+ * 2) offset - specifies a bit offset
+ * 3) access_as - changes the access mode/attributes
+ * 4) connection - Associate a resource template with the field
+ */
+ switch (arg->common.aml_opcode) {
+ case AML_INT_RESERVEDFIELD_OP:
+
+ position = (u64)info->field_bit_position +
+ (u64)arg->common.value.size;
+
+ if (position > ACPI_UINT32_MAX) {
+ ACPI_ERROR((AE_INFO,
+ "Bit offset within field too large (> 0xFFFFFFFF)"));
+ return_ACPI_STATUS(AE_SUPPORT);
+ }
+
+ info->field_bit_position = (u32) position;
+ break;
+
+ case AML_INT_ACCESSFIELD_OP:
+ case AML_INT_EXTACCESSFIELD_OP:
+ /*
+ * Get new access_type, access_attribute, and access_length fields
+ * -- to be used for all field units that follow, until the
+ * end-of-field or another access_as keyword is encountered.
+ * NOTE. These three bytes are encoded in the integer value
+ * of the parseop for convenience.
+ *
+ * In field_flags, preserve the flag bits other than the
+ * ACCESS_TYPE bits.
+ */
+
+ /* access_type (byte_acc, word_acc, etc.) */
+
+ info->field_flags = (u8)
+ ((info->
+ field_flags & ~(AML_FIELD_ACCESS_TYPE_MASK)) |
+ ((u8)((u32)(arg->common.value.integer & 0x07))));
+
+ /* access_attribute (attrib_quick, attrib_byte, etc.) */
+
+ info->attribute = (u8)
+ ((arg->common.value.integer >> 8) & 0xFF);
+
+ /* access_length (for serial/buffer protocols) */
+
+ info->access_length = (u8)
+ ((arg->common.value.integer >> 16) & 0xFF);
+ break;
+
+ case AML_INT_CONNECTION_OP:
+ /*
+ * Clear any previous connection. New connection is used for all
+ * fields that follow, similar to access_as
+ */
+ info->resource_buffer = NULL;
+ info->connection_node = NULL;
+ info->pin_number_index = 0;
+
+ /*
+ * A Connection() is either an actual resource descriptor (buffer)
+ * or a named reference to a resource template
+ */
+ child = arg->common.value.arg;
+ if (child->common.aml_opcode == AML_INT_BYTELIST_OP) {
+ info->resource_buffer = child->named.data;
+ info->resource_length =
+ (u16)child->named.value.integer;
+ } else {
+ /* Lookup the Connection() namepath, it should already exist */
+
+ status = acpi_ns_lookup(walk_state->scope_info,
+ child->common.value.
+ name, ACPI_TYPE_ANY,
+ ACPI_IMODE_EXECUTE,
+ ACPI_NS_DONT_OPEN_SCOPE,
+ walk_state,
+ &info->connection_node);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->
+ scope_info,
+ child->common.
+ value.name,
+ status);
+ return_ACPI_STATUS(status);
+ }
+ }
+ break;
+
+ case AML_INT_NAMEDFIELD_OP:
+
+ /* Lookup the name, it should already exist */
+
+ status = acpi_ns_lookup(walk_state->scope_info,
+ (char *)&arg->named.name,
+ info->field_type,
+ ACPI_IMODE_EXECUTE,
+ ACPI_NS_DONT_OPEN_SCOPE,
+ walk_state, &info->field_node);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ (char *)&arg->named.name,
+ status);
+ return_ACPI_STATUS(status);
+ } else {
+ arg->common.node = info->field_node;
+ info->field_bit_length = arg->common.value.size;
+
+ /*
+ * If there is no object attached to the node, this node was
+ * just created and we need to create the field object.
+ * Otherwise, this was a lookup of an existing node and we
+ * don't want to create the field object again.
+ */
+ if (!acpi_ns_get_attached_object
+ (info->field_node)) {
+ status = acpi_ex_prep_field_value(info);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+#ifdef ACPI_EXEC_APP
+ name_path =
+ acpi_ns_get_external_pathname(info->
+ field_node);
+ if (ACPI_SUCCESS
+ (ae_lookup_init_file_entry
+ (name_path, &obj_desc))) {
+ acpi_ex_write_data_to_field
+ (obj_desc,
+ acpi_ns_get_attached_object
+ (info->field_node),
+ &result_desc);
+ acpi_ut_remove_reference
+ (obj_desc);
+ }
+ ACPI_FREE(name_path);
+#endif
+ }
+ }
+
+ /* Keep track of bit position for the next field */
+
+ position = (u64)info->field_bit_position +
+ (u64)arg->common.value.size;
+
+ if (position > ACPI_UINT32_MAX) {
+ ACPI_ERROR((AE_INFO,
+ "Field [%4.4s] bit offset too large (> 0xFFFFFFFF)",
+ ACPI_CAST_PTR(char,
+ &info->field_node->
+ name)));
+ return_ACPI_STATUS(AE_SUPPORT);
+ }
+
+ info->field_bit_position += info->field_bit_length;
+ info->pin_number_index++; /* Index relative to previous Connection() */
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid opcode in field list: 0x%X",
+ arg->common.aml_opcode));
+ return_ACPI_STATUS(AE_AML_BAD_OPCODE);
+ }
+
+ arg = arg->common.next;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_create_field
+ *
+ * PARAMETERS: op - Op containing the Field definition and args
+ * region_node - Object for the containing Operation Region
+ * ` walk_state - Current method state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new field in the specified operation region
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_create_field(union acpi_parse_object *op,
+ struct acpi_namespace_node *region_node,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ union acpi_parse_object *arg;
+ struct acpi_create_field_info info;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_create_field, op);
+
+ /* First arg is the name of the parent op_region (must already exist) */
+
+ arg = op->common.value.arg;
+
+ if (!region_node) {
+ status =
+ acpi_ns_lookup(walk_state->scope_info,
+ arg->common.value.name, ACPI_TYPE_REGION,
+ ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT,
+ walk_state, &region_node);
+#ifdef ACPI_ASL_COMPILER
+ status = acpi_ds_create_external_region(status, arg,
+ arg->common.value.name,
+ walk_state,
+ &region_node);
+#endif
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ arg->common.value.name, status);
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ memset(&info, 0, sizeof(struct acpi_create_field_info));
+
+ /* Second arg is the field flags */
+
+ arg = arg->common.next;
+ info.field_flags = (u8) arg->common.value.integer;
+ info.attribute = 0;
+
+ /* Each remaining arg is a Named Field */
+
+ info.field_type = ACPI_TYPE_LOCAL_REGION_FIELD;
+ info.region_node = region_node;
+
+ status = acpi_ds_get_field_names(&info, walk_state, arg->common.next);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (info.region_node->object->region.space_id ==
+ ACPI_ADR_SPACE_PLATFORM_COMM) {
+ region_node->object->field.internal_pcc_buffer =
+ ACPI_ALLOCATE_ZEROED(info.region_node->object->region.
+ length);
+ if (!region_node->object->field.internal_pcc_buffer) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_init_field_objects
+ *
+ * PARAMETERS: op - Op containing the Field definition and args
+ * ` walk_state - Current method state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: For each "Field Unit" name in the argument list that is
+ * part of the field declaration, enter the name into the
+ * namespace.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_init_field_objects(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ union acpi_parse_object *arg = NULL;
+ struct acpi_namespace_node *node;
+ u8 type = 0;
+ u32 flags;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_init_field_objects, op);
+
+ /* Execute flag should always be set when this function is entered */
+
+ if (!(walk_state->parse_flags & ACPI_PARSE_EXECUTE)) {
+ if (walk_state->parse_flags & ACPI_PARSE_DEFERRED_OP) {
+
+ /* bank_field Op is deferred, just return OK */
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ ACPI_ERROR((AE_INFO, "Parse deferred mode is not set"));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ /*
+ * Get the field_list argument for this opcode. This is the start of the
+ * list of field elements.
+ */
+ switch (walk_state->opcode) {
+ case AML_FIELD_OP:
+
+ arg = acpi_ps_get_arg(op, 2);
+ type = ACPI_TYPE_LOCAL_REGION_FIELD;
+ break;
+
+ case AML_BANK_FIELD_OP:
+
+ arg = acpi_ps_get_arg(op, 4);
+ type = ACPI_TYPE_LOCAL_BANK_FIELD;
+ break;
+
+ case AML_INDEX_FIELD_OP:
+
+ arg = acpi_ps_get_arg(op, 3);
+ type = ACPI_TYPE_LOCAL_INDEX_FIELD;
+ break;
+
+ default:
+
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Creating new namespace node(s), should not already exist */
+
+ flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE |
+ ACPI_NS_ERROR_IF_FOUND;
+
+ /*
+ * Mark node(s) temporary if we are executing a normal control
+ * method. (Don't mark if this is a module-level code method)
+ */
+ if (walk_state->method_node &&
+ !(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) {
+ flags |= ACPI_NS_TEMPORARY;
+ }
+#ifdef ACPI_EXEC_APP
+ flags |= ACPI_NS_OVERRIDE_IF_FOUND;
+#endif
+ /*
+ * Walk the list of entries in the field_list
+ * Note: field_list can be of zero length. In this case, Arg will be NULL.
+ */
+ while (arg) {
+ /*
+ * Ignore OFFSET/ACCESSAS/CONNECTION terms here; we are only interested
+ * in the field names in order to enter them into the namespace.
+ */
+ if (arg->common.aml_opcode == AML_INT_NAMEDFIELD_OP) {
+ status = acpi_ns_lookup(walk_state->scope_info,
+ (char *)&arg->named.name, type,
+ ACPI_IMODE_LOAD_PASS1, flags,
+ walk_state, &node);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ (char *)&arg->named.name,
+ status);
+ if (status != AE_ALREADY_EXISTS) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Name already exists, just ignore this error */
+ }
+
+ arg->common.node = node;
+ }
+
+ /* Get the next field element in the list */
+
+ arg = arg->common.next;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_create_bank_field
+ *
+ * PARAMETERS: op - Op containing the Field definition and args
+ * region_node - Object for the containing Operation Region
+ * walk_state - Current method state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new bank field in the specified operation region
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_create_bank_field(union acpi_parse_object *op,
+ struct acpi_namespace_node *region_node,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ union acpi_parse_object *arg;
+ struct acpi_create_field_info info;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_create_bank_field, op);
+
+ /* First arg is the name of the parent op_region (must already exist) */
+
+ arg = op->common.value.arg;
+ if (!region_node) {
+ status =
+ acpi_ns_lookup(walk_state->scope_info,
+ arg->common.value.name, ACPI_TYPE_REGION,
+ ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT,
+ walk_state, &region_node);
+#ifdef ACPI_ASL_COMPILER
+ status = acpi_ds_create_external_region(status, arg,
+ arg->common.value.name,
+ walk_state,
+ &region_node);
+#endif
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ arg->common.value.name, status);
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* Second arg is the Bank Register (Field) (must already exist) */
+
+ arg = arg->common.next;
+ status =
+ acpi_ns_lookup(walk_state->scope_info, arg->common.value.string,
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, walk_state,
+ &info.register_node);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ arg->common.value.string, status);
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Third arg is the bank_value
+ * This arg is a term_arg, not a constant
+ * It will be evaluated later, by acpi_ds_eval_bank_field_operands
+ */
+ arg = arg->common.next;
+
+ /* Fourth arg is the field flags */
+
+ arg = arg->common.next;
+ info.field_flags = (u8) arg->common.value.integer;
+
+ /* Each remaining arg is a Named Field */
+
+ info.field_type = ACPI_TYPE_LOCAL_BANK_FIELD;
+ info.region_node = region_node;
+
+ /*
+ * Use Info.data_register_node to store bank_field Op
+ * It's safe because data_register_node will never be used when create
+ * bank field \we store aml_start and aml_length in the bank_field Op for
+ * late evaluation. Used in acpi_ex_prep_field_value(Info)
+ *
+ * TBD: Or, should we add a field in struct acpi_create_field_info, like
+ * "void *ParentOp"?
+ */
+ info.data_register_node = (struct acpi_namespace_node *)op;
+
+ status = acpi_ds_get_field_names(&info, walk_state, arg->common.next);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_create_index_field
+ *
+ * PARAMETERS: op - Op containing the Field definition and args
+ * region_node - Object for the containing Operation Region
+ * ` walk_state - Current method state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new index field in the specified operation region
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_create_index_field(union acpi_parse_object *op,
+ struct acpi_namespace_node *region_node,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ union acpi_parse_object *arg;
+ struct acpi_create_field_info info;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_create_index_field, op);
+
+ /* First arg is the name of the Index register (must already exist) */
+
+ arg = op->common.value.arg;
+ status =
+ acpi_ns_lookup(walk_state->scope_info, arg->common.value.string,
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, walk_state,
+ &info.register_node);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ arg->common.value.string, status);
+ return_ACPI_STATUS(status);
+ }
+
+ /* Second arg is the data register (must already exist) */
+
+ arg = arg->common.next;
+ status =
+ acpi_ns_lookup(walk_state->scope_info, arg->common.value.string,
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, walk_state,
+ &info.data_register_node);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ arg->common.value.string, status);
+ return_ACPI_STATUS(status);
+ }
+
+ /* Next arg is the field flags */
+
+ arg = arg->common.next;
+ info.field_flags = (u8) arg->common.value.integer;
+
+ /* Each remaining arg is a Named Field */
+
+ info.field_type = ACPI_TYPE_LOCAL_INDEX_FIELD;
+ info.region_node = region_node;
+
+ status = acpi_ds_get_field_names(&info, walk_state, arg->common.next);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c
new file mode 100644
index 000000000..dffd54fdb
--- /dev/null
+++ b/drivers/acpi/acpica/dsinit.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dsinit - Object initialization namespace walk
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdispat.h"
+#include "acnamesp.h"
+#include "actables.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dsinit")
+
+/* Local prototypes */
+static acpi_status
+acpi_ds_init_one_object(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_init_one_object
+ *
+ * PARAMETERS: obj_handle - Node for the object
+ * level - Current nesting level
+ * context - Points to a init info struct
+ * return_value - Not used
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Callback from acpi_walk_namespace. Invoked for every object
+ * within the namespace.
+ *
+ * Currently, the only objects that require initialization are:
+ * 1) Methods
+ * 2) Operation Regions
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ds_init_one_object(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value)
+{
+ struct acpi_init_walk_info *info =
+ (struct acpi_init_walk_info *)context;
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * We are only interested in NS nodes owned by the table that
+ * was just loaded
+ */
+ if (node->owner_id != info->owner_id) {
+ return (AE_OK);
+ }
+
+ info->object_count++;
+
+ /* And even then, we are only interested in a few object types */
+
+ switch (acpi_ns_get_type(obj_handle)) {
+ case ACPI_TYPE_REGION:
+
+ status = acpi_ds_initialize_region(obj_handle);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During Region initialization %p [%4.4s]",
+ obj_handle,
+ acpi_ut_get_node_name(obj_handle)));
+ }
+
+ info->op_region_count++;
+ break;
+
+ case ACPI_TYPE_METHOD:
+ /*
+ * Auto-serialization support. We will examine each method that is
+ * not_serialized to determine if it creates any Named objects. If
+ * it does, it will be marked serialized to prevent problems if
+ * the method is entered by two or more threads and an attempt is
+ * made to create the same named object twice -- which results in
+ * an AE_ALREADY_EXISTS exception and method abort.
+ */
+ info->method_count++;
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ break;
+ }
+
+ /* Ignore if already serialized */
+
+ if (obj_desc->method.info_flags & ACPI_METHOD_SERIALIZED) {
+ info->serial_method_count++;
+ break;
+ }
+
+ if (acpi_gbl_auto_serialize_methods) {
+
+ /* Parse/scan method and serialize it if necessary */
+
+ acpi_ds_auto_serialize_method(node, obj_desc);
+ if (obj_desc->method.
+ info_flags & ACPI_METHOD_SERIALIZED) {
+
+ /* Method was just converted to Serialized */
+
+ info->serial_method_count++;
+ info->serialized_method_count++;
+ break;
+ }
+ }
+
+ info->non_serial_method_count++;
+ break;
+
+ case ACPI_TYPE_DEVICE:
+
+ info->device_count++;
+ break;
+
+ default:
+
+ break;
+ }
+
+ /*
+ * We ignore errors from above, and always return OK, since
+ * we don't want to abort the walk on a single error.
+ */
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_initialize_objects
+ *
+ * PARAMETERS: table_desc - Descriptor for parent ACPI table
+ * start_node - Root of subtree to be initialized.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Walk the namespace starting at "StartNode" and perform any
+ * necessary initialization on the objects found therein
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_initialize_objects(u32 table_index,
+ struct acpi_namespace_node *start_node)
+{
+ acpi_status status;
+ struct acpi_init_walk_info info;
+ struct acpi_table_header *table;
+ acpi_owner_id owner_id;
+
+ ACPI_FUNCTION_TRACE(ds_initialize_objects);
+
+ status = acpi_tb_get_owner_id(table_index, &owner_id);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "**** Starting initialization of namespace objects ****\n"));
+
+ /* Set all init info to zero */
+
+ memset(&info, 0, sizeof(struct acpi_init_walk_info));
+
+ info.owner_id = owner_id;
+ info.table_index = table_index;
+
+ /* Walk entire namespace from the supplied root */
+
+ /*
+ * We don't use acpi_walk_namespace since we do not want to acquire
+ * the namespace reader lock.
+ */
+ status =
+ acpi_ns_walk_namespace(ACPI_TYPE_ANY, start_node, ACPI_UINT32_MAX,
+ ACPI_NS_WALK_NO_UNLOCK,
+ acpi_ds_init_one_object, NULL, &info, NULL);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace"));
+ }
+
+ status = acpi_get_table_by_index(table_index, &table);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* DSDT is always the first AML table */
+
+ if (ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_DSDT)) {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
+ "\nACPI table initialization:\n"));
+ }
+
+ /* Summary of objects initialized */
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
+ "Table [%4.4s: %-8.8s] (id %.2X) - %4u Objects with %3u Devices, "
+ "%3u Regions, %4u Methods (%u/%u/%u Serial/Non/Cvt)\n",
+ table->signature, table->oem_table_id, owner_id,
+ info.object_count, info.device_count,
+ info.op_region_count, info.method_count,
+ info.serial_method_count,
+ info.non_serial_method_count,
+ info.serialized_method_count));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "%u Methods, %u Regions\n",
+ info.method_count, info.op_region_count));
+
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c
new file mode 100644
index 000000000..9332bc688
--- /dev/null
+++ b/drivers/acpi/acpica/dsmethod.c
@@ -0,0 +1,848 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dsmethod - Parser/Interpreter interface - control method parsing
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acdebug.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dsmethod")
+
+/* Local prototypes */
+static acpi_status
+acpi_ds_detect_named_opcodes(struct acpi_walk_state *walk_state,
+ union acpi_parse_object **out_op);
+
+static acpi_status
+acpi_ds_create_method_mutex(union acpi_operand_object *method_desc);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_auto_serialize_method
+ *
+ * PARAMETERS: node - Namespace Node of the method
+ * obj_desc - Method object attached to node
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Parse a control method AML to scan for control methods that
+ * need serialization due to the creation of named objects.
+ *
+ * NOTE: It is a bit of overkill to mark all such methods serialized, since
+ * there is only a problem if the method actually blocks during execution.
+ * A blocking operation is, for example, a Sleep() operation, or any access
+ * to an operation region. However, it is probably not possible to easily
+ * detect whether a method will block or not, so we simply mark all suspicious
+ * methods as serialized.
+ *
+ * NOTE2: This code is essentially a generic routine for parsing a single
+ * control method.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
+ union acpi_operand_object *obj_desc)
+{
+ acpi_status status;
+ union acpi_parse_object *op = NULL;
+ struct acpi_walk_state *walk_state;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_auto_serialize_method, node);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Method auto-serialization parse [%4.4s] %p\n",
+ acpi_ut_get_node_name(node), node));
+
+ /* Create/Init a root op for the method parse tree */
+
+ op = acpi_ps_alloc_op(AML_METHOD_OP, obj_desc->method.aml_start);
+ if (!op) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ acpi_ps_set_name(op, node->name.integer);
+ op->common.node = node;
+
+ /* Create and initialize a new walk state */
+
+ walk_state =
+ acpi_ds_create_walk_state(node->owner_id, NULL, NULL, NULL);
+ if (!walk_state) {
+ acpi_ps_free_op(op);
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ status = acpi_ds_init_aml_walk(walk_state, op, node,
+ obj_desc->method.aml_start,
+ obj_desc->method.aml_length, NULL, 0);
+ if (ACPI_FAILURE(status)) {
+ acpi_ds_delete_walk_state(walk_state);
+ acpi_ps_free_op(op);
+ return_ACPI_STATUS(status);
+ }
+
+ walk_state->descending_callback = acpi_ds_detect_named_opcodes;
+
+ /* Parse the method, scan for creation of named objects */
+
+ status = acpi_ps_parse_aml(walk_state);
+
+ acpi_ps_delete_parse_tree(op);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_detect_named_opcodes
+ *
+ * PARAMETERS: walk_state - Current state of the parse tree walk
+ * out_op - Unused, required for parser interface
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Descending callback used during the loading of ACPI tables.
+ * Currently used to detect methods that must be marked serialized
+ * in order to avoid problems with the creation of named objects.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ds_detect_named_opcodes(struct acpi_walk_state *walk_state,
+ union acpi_parse_object **out_op)
+{
+
+ ACPI_FUNCTION_NAME(acpi_ds_detect_named_opcodes);
+
+ /* We are only interested in opcodes that create a new name */
+
+ if (!
+ (walk_state->op_info->
+ flags & (AML_NAMED | AML_CREATE | AML_FIELD))) {
+ return (AE_OK);
+ }
+
+ /*
+ * At this point, we know we have a Named object opcode.
+ * Mark the method as serialized. Later code will create a mutex for
+ * this method to enforce serialization.
+ *
+ * Note, ACPI_METHOD_IGNORE_SYNC_LEVEL flag means that we will ignore the
+ * Sync Level mechanism for this method, even though it is now serialized.
+ * Otherwise, there can be conflicts with existing ASL code that actually
+ * uses sync levels.
+ */
+ walk_state->method_desc->method.sync_level = 0;
+ walk_state->method_desc->method.info_flags |=
+ (ACPI_METHOD_SERIALIZED | ACPI_METHOD_IGNORE_SYNC_LEVEL);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Method serialized [%4.4s] %p - [%s] (%4.4X)\n",
+ walk_state->method_node->name.ascii,
+ walk_state->method_node, walk_state->op_info->name,
+ walk_state->opcode));
+
+ /* Abort the parse, no need to examine this method any further */
+
+ return (AE_CTRL_TERMINATE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_method_error
+ *
+ * PARAMETERS: status - Execution status
+ * walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Called on method error. Invoke the global exception handler if
+ * present, dump the method data if the debugger is configured
+ *
+ * Note: Allows the exception handler to change the status code
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_method_error(acpi_status status, struct acpi_walk_state *walk_state)
+{
+ u32 aml_offset;
+ acpi_name name = 0;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Ignore AE_OK and control exception codes */
+
+ if (ACPI_SUCCESS(status) || (status & AE_CODE_CONTROL)) {
+ return (status);
+ }
+
+ /* Invoke the global exception handler */
+
+ if (acpi_gbl_exception_handler) {
+
+ /* Exit the interpreter, allow handler to execute methods */
+
+ acpi_ex_exit_interpreter();
+
+ /*
+ * Handler can map the exception code to anything it wants, including
+ * AE_OK, in which case the executing method will not be aborted.
+ */
+ aml_offset = (u32)ACPI_PTR_DIFF(walk_state->aml,
+ walk_state->parser_state.
+ aml_start);
+
+ if (walk_state->method_node) {
+ name = walk_state->method_node->name.integer;
+ } else if (walk_state->deferred_node) {
+ name = walk_state->deferred_node->name.integer;
+ }
+
+ status = acpi_gbl_exception_handler(status, name,
+ walk_state->opcode,
+ aml_offset, NULL);
+ acpi_ex_enter_interpreter();
+ }
+
+ acpi_ds_clear_implicit_return(walk_state);
+
+ if (ACPI_FAILURE(status)) {
+ acpi_ds_dump_method_stack(status, walk_state, walk_state->op);
+
+ /* Display method locals/args if debugger is present */
+
+#ifdef ACPI_DEBUGGER
+ acpi_db_dump_method_info(status, walk_state);
+#endif
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_create_method_mutex
+ *
+ * PARAMETERS: obj_desc - The method object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a mutex object for a serialized control method
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ds_create_method_mutex(union acpi_operand_object *method_desc)
+{
+ union acpi_operand_object *mutex_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ds_create_method_mutex);
+
+ /* Create the new mutex object */
+
+ mutex_desc = acpi_ut_create_internal_object(ACPI_TYPE_MUTEX);
+ if (!mutex_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Create the actual OS Mutex */
+
+ status = acpi_os_create_mutex(&mutex_desc->mutex.os_mutex);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_delete_object_desc(mutex_desc);
+ return_ACPI_STATUS(status);
+ }
+
+ mutex_desc->mutex.sync_level = method_desc->method.sync_level;
+ method_desc->method.mutex = mutex_desc;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_begin_method_execution
+ *
+ * PARAMETERS: method_node - Node of the method
+ * obj_desc - The method object
+ * walk_state - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Prepare a method for execution. Parses the method if necessary,
+ * increments the thread count, and waits at the method semaphore
+ * for clearance to execute.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_begin_method_execution, method_node);
+
+ if (!method_node) {
+ return_ACPI_STATUS(AE_NULL_ENTRY);
+ }
+
+ acpi_ex_start_trace_method(method_node, obj_desc, walk_state);
+
+ /* Prevent wraparound of thread count */
+
+ if (obj_desc->method.thread_count == ACPI_UINT8_MAX) {
+ ACPI_ERROR((AE_INFO,
+ "Method reached maximum reentrancy limit (255)"));
+ return_ACPI_STATUS(AE_AML_METHOD_LIMIT);
+ }
+
+ /*
+ * If this method is serialized, we need to acquire the method mutex.
+ */
+ if (obj_desc->method.info_flags & ACPI_METHOD_SERIALIZED) {
+ /*
+ * Create a mutex for the method if it is defined to be Serialized
+ * and a mutex has not already been created. We defer the mutex creation
+ * until a method is actually executed, to minimize the object count
+ */
+ if (!obj_desc->method.mutex) {
+ status = acpi_ds_create_method_mutex(obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * The current_sync_level (per-thread) must be less than or equal to
+ * the sync level of the method. This mechanism provides some
+ * deadlock prevention.
+ *
+ * If the method was auto-serialized, we just ignore the sync level
+ * mechanism, because auto-serialization of methods can interfere
+ * with ASL code that actually uses sync levels.
+ *
+ * Top-level method invocation has no walk state at this point
+ */
+ if (walk_state &&
+ (!(obj_desc->method.
+ info_flags & ACPI_METHOD_IGNORE_SYNC_LEVEL))
+ && (walk_state->thread->current_sync_level >
+ obj_desc->method.mutex->mutex.sync_level)) {
+ ACPI_ERROR((AE_INFO,
+ "Cannot acquire Mutex for method [%4.4s]"
+ ", current SyncLevel is too large (%u)",
+ acpi_ut_get_node_name(method_node),
+ walk_state->thread->current_sync_level));
+
+ return_ACPI_STATUS(AE_AML_MUTEX_ORDER);
+ }
+
+ /*
+ * Obtain the method mutex if necessary. Do not acquire mutex for a
+ * recursive call.
+ */
+ if (!walk_state ||
+ !obj_desc->method.mutex->mutex.thread_id ||
+ (walk_state->thread->thread_id !=
+ obj_desc->method.mutex->mutex.thread_id)) {
+ /*
+ * Acquire the method mutex. This releases the interpreter if we
+ * block (and reacquires it before it returns)
+ */
+ status =
+ acpi_ex_system_wait_mutex(obj_desc->method.mutex->
+ mutex.os_mutex,
+ ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Update the mutex and walk info and save the original sync_level */
+
+ if (walk_state) {
+ obj_desc->method.mutex->mutex.
+ original_sync_level =
+ walk_state->thread->current_sync_level;
+
+ obj_desc->method.mutex->mutex.thread_id =
+ walk_state->thread->thread_id;
+
+ /*
+ * Update the current sync_level only if this is not an auto-
+ * serialized method. In the auto case, we have to ignore
+ * the sync level for the method mutex (created for the
+ * auto-serialization) because we have no idea of what the
+ * sync level should be. Therefore, just ignore it.
+ */
+ if (!(obj_desc->method.info_flags &
+ ACPI_METHOD_IGNORE_SYNC_LEVEL)) {
+ walk_state->thread->current_sync_level =
+ obj_desc->method.sync_level;
+ }
+ } else {
+ obj_desc->method.mutex->mutex.
+ original_sync_level =
+ obj_desc->method.mutex->mutex.sync_level;
+
+ obj_desc->method.mutex->mutex.thread_id =
+ acpi_os_get_thread_id();
+ }
+ }
+
+ /* Always increase acquisition depth */
+
+ obj_desc->method.mutex->mutex.acquisition_depth++;
+ }
+
+ /*
+ * Allocate an Owner ID for this method, only if this is the first thread
+ * to begin concurrent execution. We only need one owner_id, even if the
+ * method is invoked recursively.
+ */
+ if (!obj_desc->method.owner_id) {
+ status = acpi_ut_allocate_owner_id(&obj_desc->method.owner_id);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Increment the method parse tree thread count since it has been
+ * reentered one more time (even if it is the same thread)
+ */
+ obj_desc->method.thread_count++;
+ acpi_method_count++;
+ return_ACPI_STATUS(status);
+
+cleanup:
+ /* On error, must release the method mutex (if present) */
+
+ if (obj_desc->method.mutex) {
+ acpi_os_release_mutex(obj_desc->method.mutex->mutex.os_mutex);
+ }
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_call_control_method
+ *
+ * PARAMETERS: thread - Info for this thread
+ * this_walk_state - Current walk state
+ * op - Current Op to be walked
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Transfer execution to a called control method
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_call_control_method(struct acpi_thread_state *thread,
+ struct acpi_walk_state *this_walk_state,
+ union acpi_parse_object *op)
+{
+ acpi_status status;
+ struct acpi_namespace_node *method_node;
+ 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);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Calling method %p, currentstate=%p\n",
+ this_walk_state->prev_op, this_walk_state));
+
+ /*
+ * Get the namespace entry for the control method we are about to call
+ */
+ method_node = this_walk_state->method_call_node;
+ if (!method_node) {
+ return_ACPI_STATUS(AE_NULL_ENTRY);
+ }
+
+ obj_desc = acpi_ns_get_attached_object(method_node);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NULL_OBJECT);
+ }
+
+ /* Init for new method, possibly wait on method mutex */
+
+ status =
+ acpi_ds_begin_method_execution(method_node, obj_desc,
+ this_walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Begin method parse/execution. Create a new walk state */
+
+ next_walk_state =
+ acpi_ds_create_walk_state(obj_desc->method.owner_id, NULL, obj_desc,
+ thread);
+ if (!next_walk_state) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /*
+ * The resolved arguments were put on the previous walk state's operand
+ * stack. Operands on the previous walk state stack always
+ * start at index 0. Also, null terminate the list of arguments
+ */
+ this_walk_state->operands[this_walk_state->num_operands] = NULL;
+
+ /*
+ * Allocate and initialize the evaluation information block
+ * TBD: this is somewhat inefficient, should change interface to
+ * ds_init_aml_walk. For now, keeps this struct off the CPU stack
+ */
+ info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
+ if (!info) {
+ status = AE_NO_MEMORY;
+ goto pop_walk_state;
+ }
+
+ info->parameters = &this_walk_state->operands[0];
+
+ status = acpi_ds_init_aml_walk(next_walk_state, NULL, method_node,
+ obj_desc->method.aml_start,
+ obj_desc->method.aml_length, info,
+ ACPI_IMODE_EXECUTE);
+
+ ACPI_FREE(info);
+ if (ACPI_FAILURE(status)) {
+ goto pop_walk_state;
+ }
+
+ next_walk_state->method_nesting_depth =
+ this_walk_state->method_nesting_depth + 1;
+
+ /*
+ * 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_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "**** Begin nested execution of [%4.4s] **** WalkState=%p\n",
+ method_node->name.ascii, next_walk_state));
+
+ this_walk_state->method_pathname =
+ acpi_ns_get_normalized_pathname(method_node, TRUE);
+ this_walk_state->method_is_nested = TRUE;
+
+ /* Optional object evaluation log */
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EVALUATION,
+ "%-26s: %*s%s\n", " Nested method call",
+ next_walk_state->method_nesting_depth * 3, " ",
+ &this_walk_state->method_pathname[1]));
+
+ /* Invoke an internal method if necessary */
+
+ if (obj_desc->method.info_flags & ACPI_METHOD_INTERNAL_ONLY) {
+ status =
+ obj_desc->method.dispatch.implementation(next_walk_state);
+ if (status == AE_OK) {
+ status = AE_CTRL_TERMINATE;
+ }
+ }
+
+ 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 */
+
+ acpi_ds_terminate_control_method(obj_desc, next_walk_state);
+ acpi_ds_delete_walk_state(next_walk_state);
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_restart_control_method
+ *
+ * PARAMETERS: walk_state - State for preempted method (caller)
+ * return_desc - Return value from the called method
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Restart a method that was preempted by another (nested) method
+ * invocation. Handle the return value (if any) from the callee.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_restart_control_method(struct acpi_walk_state *walk_state,
+ union acpi_operand_object *return_desc)
+{
+ acpi_status status;
+ int same_as_implicit_return;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_restart_control_method, walk_state);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "****Restart [%4.4s] Op %p ReturnValueFromCallee %p\n",
+ acpi_ut_get_node_name(walk_state->method_node),
+ walk_state->method_call_op, return_desc));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ " ReturnFromThisMethodUsed?=%X ResStack %p Walk %p\n",
+ walk_state->return_used,
+ walk_state->results, walk_state));
+
+ /* Did the called method return a value? */
+
+ if (return_desc) {
+
+ /* Is the implicit return object the same as the return desc? */
+
+ same_as_implicit_return =
+ (walk_state->implicit_return_obj == return_desc);
+
+ /* Are we actually going to use the return value? */
+
+ if (walk_state->return_used) {
+
+ /* Save the return value from the previous method */
+
+ status = acpi_ds_result_push(return_desc, walk_state);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(return_desc);
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Save as THIS method's return value in case it is returned
+ * immediately to yet another method
+ */
+ walk_state->return_desc = return_desc;
+ }
+
+ /*
+ * The following code is the optional support for the so-called
+ * "implicit return". Some AML code assumes that the last value of the
+ * method is "implicitly" returned to the caller, in the absence of an
+ * explicit return value.
+ *
+ * Just save the last result of the method as the return value.
+ *
+ * NOTE: this is optional because the ASL language does not actually
+ * support this behavior.
+ */
+ else if (!acpi_ds_do_implicit_return
+ (return_desc, walk_state, FALSE)
+ || same_as_implicit_return) {
+ /*
+ * Delete the return value if it will not be used by the
+ * calling method or remove one reference if the explicit return
+ * is the same as the implicit return value.
+ */
+ acpi_ut_remove_reference(return_desc);
+ }
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_terminate_control_method
+ *
+ * PARAMETERS: method_desc - Method object
+ * walk_state - State associated with the method
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Terminate a control method. Delete everything that the method
+ * created, delete all locals and arguments, and delete the parse
+ * tree if requested.
+ *
+ * MUTEX: Interpreter is locked
+ *
+ ******************************************************************************/
+
+void
+acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
+ struct acpi_walk_state *walk_state)
+{
+
+ ACPI_FUNCTION_TRACE_PTR(ds_terminate_control_method, walk_state);
+
+ /* method_desc is required, walk_state is optional */
+
+ if (!method_desc) {
+ return_VOID;
+ }
+
+ if (walk_state) {
+
+ /* Delete all arguments and locals */
+
+ acpi_ds_method_data_delete_all(walk_state);
+
+ /*
+ * Delete any namespace objects created anywhere within the
+ * namespace by the execution of this method. Unless:
+ * 1) This method is a module-level executable code method, in which
+ * case we want make the objects permanent.
+ * 2) There are other threads executing the method, in which case we
+ * will wait until the last thread has completed.
+ */
+ if (!(method_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL)
+ && (method_desc->method.thread_count == 1)) {
+
+ /* Delete any direct children of (created by) this method */
+
+ (void)acpi_ex_exit_interpreter();
+ acpi_ns_delete_namespace_subtree(walk_state->
+ method_node);
+ (void)acpi_ex_enter_interpreter();
+
+ /*
+ * Delete any objects that were created by this method
+ * elsewhere in the namespace (if any were created).
+ * Use of the ACPI_METHOD_MODIFIED_NAMESPACE optimizes the
+ * deletion such that we don't have to perform an entire
+ * namespace walk for every control method execution.
+ */
+ if (method_desc->method.
+ info_flags & ACPI_METHOD_MODIFIED_NAMESPACE) {
+ (void)acpi_ex_exit_interpreter();
+ acpi_ns_delete_namespace_by_owner(method_desc->
+ method.
+ owner_id);
+ (void)acpi_ex_enter_interpreter();
+ method_desc->method.info_flags &=
+ ~ACPI_METHOD_MODIFIED_NAMESPACE;
+ }
+ }
+
+ /*
+ * If method is serialized, release the mutex and restore the
+ * current sync level for this thread
+ */
+ if (method_desc->method.mutex) {
+
+ /* Acquisition Depth handles recursive calls */
+
+ method_desc->method.mutex->mutex.acquisition_depth--;
+ if (!method_desc->method.mutex->mutex.acquisition_depth) {
+ walk_state->thread->current_sync_level =
+ method_desc->method.mutex->mutex.
+ original_sync_level;
+
+ acpi_os_release_mutex(method_desc->method.
+ mutex->mutex.os_mutex);
+ method_desc->method.mutex->mutex.thread_id = 0;
+ }
+ }
+ }
+
+ /* Decrement the thread count on the method */
+
+ if (method_desc->method.thread_count) {
+ method_desc->method.thread_count--;
+ } else {
+ ACPI_ERROR((AE_INFO, "Invalid zero thread count in method"));
+ }
+
+ /* Are there any other threads currently executing this method? */
+
+ if (method_desc->method.thread_count) {
+ /*
+ * Additional threads. Do not release the owner_id in this case,
+ * we immediately reuse it for the next thread executing this method
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "*** Completed execution of one thread, %u threads remaining\n",
+ method_desc->method.thread_count));
+ } else {
+ /* This is the only executing thread for this method */
+
+ /*
+ * Support to dynamically change a method from not_serialized to
+ * Serialized if it appears that the method is incorrectly written and
+ * does not support multiple thread execution. The best example of this
+ * is if such a method creates namespace objects and blocks. A second
+ * thread will fail with an AE_ALREADY_EXISTS exception.
+ *
+ * This code is here because we must wait until the last thread exits
+ * before marking the method as serialized.
+ */
+ if (method_desc->method.
+ info_flags & ACPI_METHOD_SERIALIZED_PENDING) {
+ if (walk_state) {
+ ACPI_INFO(("Marking method %4.4s as Serialized "
+ "because of AE_ALREADY_EXISTS error",
+ walk_state->method_node->name.
+ ascii));
+ }
+
+ /*
+ * Method tried to create an object twice and was marked as
+ * "pending serialized". The probable cause is that the method
+ * cannot handle reentrancy.
+ *
+ * The method was created as not_serialized, but it tried to create
+ * a named object and then blocked, causing the second thread
+ * entrance to begin and then fail. Workaround this problem by
+ * marking the method permanently as Serialized when the last
+ * thread exits here.
+ */
+ method_desc->method.info_flags &=
+ ~ACPI_METHOD_SERIALIZED_PENDING;
+
+ method_desc->method.info_flags |=
+ (ACPI_METHOD_SERIALIZED |
+ ACPI_METHOD_IGNORE_SYNC_LEVEL);
+ method_desc->method.sync_level = 0;
+ }
+
+ /* No more threads, we can free the owner_id */
+
+ if (!
+ (method_desc->method.
+ info_flags & ACPI_METHOD_MODULE_LEVEL)) {
+ acpi_ut_release_owner_id(&method_desc->method.owner_id);
+ }
+ }
+
+ acpi_ex_stop_trace_method((struct acpi_namespace_node *)method_desc->
+ method.node, method_desc, walk_state);
+
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c
new file mode 100644
index 000000000..eca50517a
--- /dev/null
+++ b/drivers/acpi/acpica/dsmthdat.c
@@ -0,0 +1,681 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dsmthdat - control method arguments and local variables
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdispat.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dsmthdat")
+
+/* Local prototypes */
+static void
+acpi_ds_method_data_delete_value(u8 type,
+ u32 index, struct acpi_walk_state *walk_state);
+
+static acpi_status
+acpi_ds_method_data_set_value(u8 type,
+ u32 index,
+ union acpi_operand_object *object,
+ struct acpi_walk_state *walk_state);
+
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+acpi_object_type
+acpi_ds_method_data_get_type(u16 opcode,
+ u32 index, struct acpi_walk_state *walk_state);
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_method_data_init
+ *
+ * PARAMETERS: walk_state - Current walk state object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize the data structures that hold the method's arguments
+ * and locals. The data struct is an array of namespace nodes for
+ * each - this allows ref_of and de_ref_of to work properly for these
+ * special data types.
+ *
+ * NOTES: walk_state fields are initialized to zero by the
+ * ACPI_ALLOCATE_ZEROED().
+ *
+ * A pseudo-Namespace Node is assigned to each argument and local
+ * so that ref_of() can return a pointer to the Node.
+ *
+ ******************************************************************************/
+
+void acpi_ds_method_data_init(struct acpi_walk_state *walk_state)
+{
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ds_method_data_init);
+
+ /* Init the method arguments */
+
+ for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++) {
+ ACPI_MOVE_32_TO_32(&walk_state->arguments[i].name,
+ NAMEOF_ARG_NTE);
+
+ walk_state->arguments[i].name.integer |= (i << 24);
+ walk_state->arguments[i].descriptor_type = ACPI_DESC_TYPE_NAMED;
+ walk_state->arguments[i].type = ACPI_TYPE_ANY;
+ walk_state->arguments[i].flags = ANOBJ_METHOD_ARG;
+ }
+
+ /* Init the method locals */
+
+ for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) {
+ ACPI_MOVE_32_TO_32(&walk_state->local_variables[i].name,
+ NAMEOF_LOCAL_NTE);
+
+ walk_state->local_variables[i].name.integer |= (i << 24);
+ walk_state->local_variables[i].descriptor_type =
+ ACPI_DESC_TYPE_NAMED;
+ walk_state->local_variables[i].type = ACPI_TYPE_ANY;
+ walk_state->local_variables[i].flags = ANOBJ_METHOD_LOCAL;
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_method_data_delete_all
+ *
+ * PARAMETERS: walk_state - Current walk state object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete method locals and arguments. Arguments are only
+ * deleted if this method was called from another method.
+ *
+ ******************************************************************************/
+
+void acpi_ds_method_data_delete_all(struct acpi_walk_state *walk_state)
+{
+ u32 index;
+
+ ACPI_FUNCTION_TRACE(ds_method_data_delete_all);
+
+ /* Detach the locals */
+
+ for (index = 0; index < ACPI_METHOD_NUM_LOCALS; index++) {
+ if (walk_state->local_variables[index].object) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Deleting Local%u=%p\n",
+ index,
+ walk_state->local_variables[index].
+ object));
+
+ /* Detach object (if present) and remove a reference */
+
+ acpi_ns_detach_object(&walk_state->
+ local_variables[index]);
+ }
+ }
+
+ /* Detach the arguments */
+
+ for (index = 0; index < ACPI_METHOD_NUM_ARGS; index++) {
+ if (walk_state->arguments[index].object) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Deleting Arg%u=%p\n",
+ index,
+ walk_state->arguments[index].object));
+
+ /* Detach object (if present) and remove a reference */
+
+ acpi_ns_detach_object(&walk_state->arguments[index]);
+ }
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_method_data_init_args
+ *
+ * PARAMETERS: *params - Pointer to a parameter list for the method
+ * max_param_count - The arg count for this method
+ * walk_state - Current walk state object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize arguments for a method. The parameter list is a list
+ * of ACPI operand objects, either null terminated or whose length
+ * is defined by max_param_count.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_method_data_init_args(union acpi_operand_object **params,
+ u32 max_param_count,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ u32 index = 0;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_method_data_init_args, params);
+
+ if (!params) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "No parameter list passed to method\n"));
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Copy passed parameters into the new method stack frame */
+
+ while ((index < ACPI_METHOD_NUM_ARGS) &&
+ (index < max_param_count) && params[index]) {
+ /*
+ * A valid parameter.
+ * Store the argument in the method/walk descriptor.
+ * Do not copy the arg in order to implement call by reference
+ */
+ status =
+ acpi_ds_method_data_set_value(ACPI_REFCLASS_ARG, index,
+ params[index], walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ index++;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%u args passed to method\n", index));
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_method_data_get_node
+ *
+ * PARAMETERS: type - Either ACPI_REFCLASS_LOCAL or
+ * ACPI_REFCLASS_ARG
+ * index - Which Local or Arg whose type to get
+ * walk_state - Current walk state object
+ * node - Where the node is returned.
+ *
+ * RETURN: Status and node
+ *
+ * DESCRIPTION: Get the Node associated with a local or arg.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_method_data_get_node(u8 type,
+ u32 index,
+ struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node **node)
+{
+ ACPI_FUNCTION_TRACE(ds_method_data_get_node);
+
+ /*
+ * Method Locals and Arguments are supported
+ */
+ switch (type) {
+ case ACPI_REFCLASS_LOCAL:
+
+ if (index > ACPI_METHOD_MAX_LOCAL) {
+ ACPI_ERROR((AE_INFO,
+ "Local index %u is invalid (max %u)",
+ index, ACPI_METHOD_MAX_LOCAL));
+ return_ACPI_STATUS(AE_AML_INVALID_INDEX);
+ }
+
+ /* Return a pointer to the pseudo-node */
+
+ *node = &walk_state->local_variables[index];
+ break;
+
+ case ACPI_REFCLASS_ARG:
+
+ if (index > ACPI_METHOD_MAX_ARG) {
+ ACPI_ERROR((AE_INFO,
+ "Arg index %u is invalid (max %u)",
+ index, ACPI_METHOD_MAX_ARG));
+ return_ACPI_STATUS(AE_AML_INVALID_INDEX);
+ }
+
+ /* Return a pointer to the pseudo-node */
+
+ *node = &walk_state->arguments[index];
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Type %u is invalid", type));
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_method_data_set_value
+ *
+ * PARAMETERS: type - Either ACPI_REFCLASS_LOCAL or
+ * ACPI_REFCLASS_ARG
+ * index - Which Local or Arg to get
+ * object - Object to be inserted into the stack entry
+ * walk_state - Current walk state object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Insert an object onto the method stack at entry Opcode:Index.
+ * Note: There is no "implicit conversion" for locals.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ds_method_data_set_value(u8 type,
+ u32 index,
+ union acpi_operand_object *object,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ ACPI_FUNCTION_TRACE(ds_method_data_set_value);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "NewObj %p Type %2.2X, Refs=%u [%s]\n", object,
+ type, object->common.reference_count,
+ acpi_ut_get_type_name(object->common.type)));
+
+ /* Get the namespace node for the arg/local */
+
+ status = acpi_ds_method_data_get_node(type, index, walk_state, &node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Increment ref count so object can't be deleted while installed.
+ * NOTE: We do not copy the object in order to preserve the call by
+ * reference semantics of ACPI Control Method invocation.
+ * (See ACPI Specification 2.0C)
+ */
+ acpi_ut_add_reference(object);
+
+ /* Install the object */
+
+ node->object = object;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_method_data_get_value
+ *
+ * PARAMETERS: type - Either ACPI_REFCLASS_LOCAL or
+ * ACPI_REFCLASS_ARG
+ * index - Which localVar or argument to get
+ * walk_state - Current walk state object
+ * dest_desc - Where Arg or Local value is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Retrieve value of selected Arg or Local for this method
+ * Used only in acpi_ex_resolve_to_value().
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_method_data_get_value(u8 type,
+ u32 index,
+ struct acpi_walk_state *walk_state,
+ union acpi_operand_object **dest_desc)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *object;
+
+ ACPI_FUNCTION_TRACE(ds_method_data_get_value);
+
+ /* Validate the object descriptor */
+
+ if (!dest_desc) {
+ ACPI_ERROR((AE_INFO, "Null object descriptor pointer"));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Get the namespace node for the arg/local */
+
+ status = acpi_ds_method_data_get_node(type, index, walk_state, &node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Get the object from the node */
+
+ object = node->object;
+
+ /* Examine the returned object, it must be valid. */
+
+ if (!object) {
+ /*
+ * Index points to uninitialized object.
+ * This means that either 1) The expected argument was
+ * not passed to the method, or 2) A local variable
+ * was referenced by the method (via the ASL)
+ * before it was initialized. Either case is an error.
+ */
+
+ /* If slack enabled, init the local_x/arg_x to an Integer of value zero */
+
+ if (acpi_gbl_enable_interpreter_slack) {
+ object = acpi_ut_create_integer_object((u64) 0);
+ if (!object) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ node->object = object;
+ }
+
+ /* Otherwise, return the error */
+
+ else
+ switch (type) {
+ case ACPI_REFCLASS_ARG:
+
+ ACPI_ERROR((AE_INFO,
+ "Uninitialized Arg[%u] at node %p",
+ index, node));
+
+ return_ACPI_STATUS(AE_AML_UNINITIALIZED_ARG);
+
+ case ACPI_REFCLASS_LOCAL:
+ /*
+ * No error message for this case, will be trapped again later to
+ * detect and ignore cases of Store(local_x,local_x)
+ */
+ return_ACPI_STATUS(AE_AML_UNINITIALIZED_LOCAL);
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Not a Arg/Local opcode: 0x%X",
+ type));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+ }
+
+ /*
+ * The Index points to an initialized and valid object.
+ * Return an additional reference to the object
+ */
+ *dest_desc = object;
+ acpi_ut_add_reference(object);
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_method_data_delete_value
+ *
+ * PARAMETERS: type - Either ACPI_REFCLASS_LOCAL or
+ * ACPI_REFCLASS_ARG
+ * index - Which localVar or argument to delete
+ * walk_state - Current walk state object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete the entry at Opcode:Index. Inserts
+ * a null into the stack slot after the object is deleted.
+ *
+ ******************************************************************************/
+
+static void
+acpi_ds_method_data_delete_value(u8 type,
+ u32 index, struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *object;
+
+ ACPI_FUNCTION_TRACE(ds_method_data_delete_value);
+
+ /* Get the namespace node for the arg/local */
+
+ status = acpi_ds_method_data_get_node(type, index, walk_state, &node);
+ if (ACPI_FAILURE(status)) {
+ return_VOID;
+ }
+
+ /* Get the associated object */
+
+ object = acpi_ns_get_attached_object(node);
+
+ /*
+ * Undefine the Arg or Local by setting its descriptor
+ * pointer to NULL. Locals/Args can contain both
+ * ACPI_OPERAND_OBJECTS and ACPI_NAMESPACE_NODEs
+ */
+ node->object = NULL;
+
+ if ((object) &&
+ (ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_OPERAND)) {
+ /*
+ * There is a valid object.
+ * Decrement the reference count by one to balance the
+ * increment when the object was stored.
+ */
+ acpi_ut_remove_reference(object);
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_store_object_to_local
+ *
+ * PARAMETERS: type - Either ACPI_REFCLASS_LOCAL or
+ * ACPI_REFCLASS_ARG
+ * index - Which Local or Arg to set
+ * obj_desc - Value to be stored
+ * walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Store a value in an Arg or Local. The obj_desc is installed
+ * as the new value for the Arg or Local and the reference count
+ * for obj_desc is incremented.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_store_object_to_local(u8 type,
+ u32 index,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *current_obj_desc;
+ union acpi_operand_object *new_obj_desc;
+
+ ACPI_FUNCTION_TRACE(ds_store_object_to_local);
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Type=%2.2X Index=%u Obj=%p\n",
+ type, index, obj_desc));
+
+ /* Parameter validation */
+
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Get the namespace node for the arg/local */
+
+ status = acpi_ds_method_data_get_node(type, index, walk_state, &node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ current_obj_desc = acpi_ns_get_attached_object(node);
+ if (current_obj_desc == obj_desc) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Obj=%p already installed!\n",
+ obj_desc));
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * If the reference count on the object is more than one, we must
+ * take a copy of the object before we store. A reference count
+ * of exactly 1 means that the object was just created during the
+ * evaluation of an expression, and we can safely use it since it
+ * is not used anywhere else.
+ */
+ new_obj_desc = obj_desc;
+ if (obj_desc->common.reference_count > 1) {
+ status =
+ acpi_ut_copy_iobject_to_iobject(obj_desc, &new_obj_desc,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * If there is an object already in this slot, we either
+ * have to delete it, or if this is an argument and there
+ * is an object reference stored there, we have to do
+ * an indirect store!
+ */
+ if (current_obj_desc) {
+ /*
+ * Check for an indirect store if an argument
+ * contains an object reference (stored as an Node).
+ * We don't allow this automatic dereferencing for
+ * locals, since a store to a local should overwrite
+ * anything there, including an object reference.
+ *
+ * If both Arg0 and Local0 contain ref_of (Local4):
+ *
+ * Store (1, Arg0) - Causes indirect store to local4
+ * Store (1, Local0) - Stores 1 in local0, overwriting
+ * the reference to local4
+ * Store (1, de_refof (Local0)) - Causes indirect store to local4
+ *
+ * Weird, but true.
+ */
+ if (type == ACPI_REFCLASS_ARG) {
+ /*
+ * If we have a valid reference object that came from ref_of(),
+ * do the indirect store
+ */
+ if ((ACPI_GET_DESCRIPTOR_TYPE(current_obj_desc) ==
+ ACPI_DESC_TYPE_OPERAND) &&
+ (current_obj_desc->common.type ==
+ ACPI_TYPE_LOCAL_REFERENCE) &&
+ (current_obj_desc->reference.class ==
+ ACPI_REFCLASS_REFOF)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Arg (%p) is an ObjRef(Node), storing in node %p\n",
+ new_obj_desc,
+ current_obj_desc));
+
+ /*
+ * Store this object to the Node (perform the indirect store)
+ * NOTE: No implicit conversion is performed, as per the ACPI
+ * specification rules on storing to Locals/Args.
+ */
+ status =
+ acpi_ex_store_object_to_node(new_obj_desc,
+ current_obj_desc->
+ reference.
+ object,
+ walk_state,
+ ACPI_NO_IMPLICIT_CONVERSION);
+
+ /* Remove local reference if we copied the object above */
+
+ if (new_obj_desc != obj_desc) {
+ acpi_ut_remove_reference(new_obj_desc);
+ }
+
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* Delete the existing object before storing the new one */
+
+ acpi_ds_method_data_delete_value(type, index, walk_state);
+ }
+
+ /*
+ * Install the Obj descriptor (*new_obj_desc) into
+ * the descriptor for the Arg or Local.
+ * (increments the object reference count by one)
+ */
+ status =
+ acpi_ds_method_data_set_value(type, index, new_obj_desc,
+ walk_state);
+
+ /* Remove local reference if we copied the object above */
+
+ if (new_obj_desc != obj_desc) {
+ acpi_ut_remove_reference(new_obj_desc);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_method_data_get_type
+ *
+ * PARAMETERS: opcode - Either AML_FIRST LOCAL_OP or
+ * AML_FIRST_ARG_OP
+ * index - Which Local or Arg whose type to get
+ * walk_state - Current walk state object
+ *
+ * RETURN: Data type of current value of the selected Arg or Local
+ *
+ * DESCRIPTION: Get the type of the object stored in the Local or Arg
+ *
+ ******************************************************************************/
+
+acpi_object_type
+acpi_ds_method_data_get_type(u16 opcode,
+ u32 index, struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *object;
+
+ ACPI_FUNCTION_TRACE(ds_method_data_get_type);
+
+ /* Get the namespace node for the arg/local */
+
+ status = acpi_ds_method_data_get_node(opcode, index, walk_state, &node);
+ if (ACPI_FAILURE(status)) {
+ return_VALUE((ACPI_TYPE_NOT_FOUND));
+ }
+
+ /* Get the object */
+
+ object = acpi_ns_get_attached_object(node);
+ if (!object) {
+
+ /* Uninitialized local/arg, return TYPE_ANY */
+
+ return_VALUE(ACPI_TYPE_ANY);
+ }
+
+ /* Get the object type */
+
+ return_VALUE(object->type);
+}
+#endif
diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c
new file mode 100644
index 000000000..e3dfc734a
--- /dev/null
+++ b/drivers/acpi/acpica/dsobject.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dsobject - Dispatcher object management routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dsobject")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_build_internal_object
+ *
+ * PARAMETERS: walk_state - Current walk state
+ * op - Parser object to be translated
+ * obj_desc_ptr - Where the ACPI internal object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Translate a parser Op object to the equivalent namespace object
+ * Simple objects are any objects other than a package object!
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ds_build_internal_object(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ union acpi_operand_object **obj_desc_ptr)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ds_build_internal_object);
+
+ *obj_desc_ptr = NULL;
+ if (op->common.aml_opcode == AML_INT_NAMEPATH_OP) {
+ /*
+ * This is a named object reference. If this name was
+ * previously looked up in the namespace, it was stored in
+ * this op. Otherwise, go ahead and look it up now
+ */
+ if (!op->common.node) {
+
+ /* Check if we are resolving a named reference within a package */
+
+ if ((op->common.parent->common.aml_opcode ==
+ AML_PACKAGE_OP)
+ || (op->common.parent->common.aml_opcode ==
+ AML_VARIABLE_PACKAGE_OP)) {
+ /*
+ * We won't resolve package elements here, we will do this
+ * after all ACPI tables are loaded into the namespace. This
+ * behavior supports both forward references to named objects
+ * and external references to objects in other tables.
+ */
+ goto create_new_object;
+ } else {
+ status = acpi_ns_lookup(walk_state->scope_info,
+ op->common.value.string,
+ ACPI_TYPE_ANY,
+ ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT |
+ ACPI_NS_DONT_OPEN_SCOPE,
+ NULL,
+ ACPI_CAST_INDIRECT_PTR
+ (struct
+ acpi_namespace_node,
+ &(op->common.node)));
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->
+ scope_info,
+ op->common.value.
+ string, status);
+ return_ACPI_STATUS(status);
+ }
+ }
+ }
+ }
+
+create_new_object:
+
+ /* Create and init a new internal ACPI object */
+
+ obj_desc = acpi_ut_create_internal_object((acpi_ps_get_opcode_info
+ (op->common.aml_opcode))->
+ object_type);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ status =
+ acpi_ds_init_object_from_op(walk_state, op, op->common.aml_opcode,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Handling for unresolved package reference elements.
+ * These are elements that are namepaths.
+ */
+ if ((op->common.parent->common.aml_opcode == AML_PACKAGE_OP) ||
+ (op->common.parent->common.aml_opcode == AML_VARIABLE_PACKAGE_OP)) {
+ obj_desc->reference.resolved = TRUE;
+
+ if ((op->common.aml_opcode == AML_INT_NAMEPATH_OP) &&
+ !obj_desc->reference.node) {
+ /*
+ * Name was unresolved above.
+ * Get the prefix node for later lookup
+ */
+ obj_desc->reference.node =
+ walk_state->scope_info->scope.node;
+ obj_desc->reference.aml = op->common.aml;
+ obj_desc->reference.resolved = FALSE;
+ }
+ }
+
+ *obj_desc_ptr = obj_desc;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_build_internal_buffer_obj
+ *
+ * PARAMETERS: walk_state - Current walk state
+ * op - Parser object to be translated
+ * buffer_length - Length of the buffer
+ * obj_desc_ptr - Where the ACPI internal object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Translate a parser Op package object to the equivalent
+ * namespace object
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_build_internal_buffer_obj(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ u32 buffer_length,
+ union acpi_operand_object **obj_desc_ptr)
+{
+ union acpi_parse_object *arg;
+ union acpi_operand_object *obj_desc;
+ union acpi_parse_object *byte_list;
+ u32 byte_list_length = 0;
+
+ ACPI_FUNCTION_TRACE(ds_build_internal_buffer_obj);
+
+ /*
+ * If we are evaluating a Named buffer object "Name (xxxx, Buffer)".
+ * The buffer object already exists (from the NS node), otherwise it must
+ * be created.
+ */
+ obj_desc = *obj_desc_ptr;
+ if (!obj_desc) {
+
+ /* Create a new buffer object */
+
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_BUFFER);
+ *obj_desc_ptr = obj_desc;
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+ }
+
+ /*
+ * Second arg is the buffer data (optional) byte_list can be either
+ * individual bytes or a string initializer. In either case, a
+ * byte_list appears in the AML.
+ */
+ arg = op->common.value.arg; /* skip first arg */
+
+ byte_list = arg->named.next;
+ if (byte_list) {
+ if (byte_list->common.aml_opcode != AML_INT_BYTELIST_OP) {
+ ACPI_ERROR((AE_INFO,
+ "Expecting bytelist, found AML opcode 0x%X in op %p",
+ byte_list->common.aml_opcode, byte_list));
+
+ acpi_ut_remove_reference(obj_desc);
+ return (AE_TYPE);
+ }
+
+ byte_list_length = (u32) byte_list->common.value.integer;
+ }
+
+ /*
+ * The buffer length (number of bytes) will be the larger of:
+ * 1) The specified buffer length and
+ * 2) The length of the initializer byte list
+ */
+ obj_desc->buffer.length = buffer_length;
+ if (byte_list_length > buffer_length) {
+ obj_desc->buffer.length = byte_list_length;
+ }
+
+ /* Allocate the buffer */
+
+ if (obj_desc->buffer.length == 0) {
+ obj_desc->buffer.pointer = NULL;
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Buffer defined with zero length in AML, creating\n"));
+ } else {
+ obj_desc->buffer.pointer =
+ ACPI_ALLOCATE_ZEROED(obj_desc->buffer.length);
+ if (!obj_desc->buffer.pointer) {
+ acpi_ut_delete_object_desc(obj_desc);
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Initialize buffer from the byte_list (if present) */
+
+ if (byte_list) {
+ memcpy(obj_desc->buffer.pointer, byte_list->named.data,
+ byte_list_length);
+ }
+ }
+
+ obj_desc->buffer.flags |= AOPOBJ_DATA_VALID;
+ op->common.node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_desc);
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_create_node
+ *
+ * PARAMETERS: walk_state - Current walk state
+ * node - NS Node to be initialized
+ * op - Parser object to be translated
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create the object to be associated with a namespace node
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_create_node(struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node *node,
+ union acpi_parse_object *op)
+{
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_create_node, op);
+
+ /*
+ * Because of the execution pass through the non-control-method
+ * parts of the table, we can arrive here twice. Only init
+ * the named object node the first time through
+ */
+ if (acpi_ns_get_attached_object(node)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ if (!op->common.value.arg) {
+
+ /* No arguments, there is nothing to do */
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Build an internal object for the argument(s) */
+
+ status =
+ acpi_ds_build_internal_object(walk_state, op->common.value.arg,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Re-type the object according to its argument */
+
+ node->type = obj_desc->common.type;
+
+ /* Attach obj to node */
+
+ status = acpi_ns_attach_object(node, obj_desc, node->type);
+
+ /* Remove local reference to the object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_init_object_from_op
+ *
+ * PARAMETERS: walk_state - Current walk state
+ * op - Parser op used to init the internal object
+ * opcode - AML opcode associated with the object
+ * ret_obj_desc - Namespace object to be initialized
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize a namespace object from a parser Op and its
+ * associated arguments. The namespace object is a more compact
+ * representation of the Op and its arguments.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ u16 opcode,
+ union acpi_operand_object **ret_obj_desc)
+{
+ const struct acpi_opcode_info *op_info;
+ union acpi_operand_object *obj_desc;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ds_init_object_from_op);
+
+ obj_desc = *ret_obj_desc;
+ op_info = acpi_ps_get_opcode_info(opcode);
+ if (op_info->class == AML_CLASS_UNKNOWN) {
+
+ /* Unknown opcode */
+
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ /* Perform per-object initialization */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_BUFFER:
+ /*
+ * Defer evaluation of Buffer term_arg operand
+ */
+ obj_desc->buffer.node =
+ ACPI_CAST_PTR(struct acpi_namespace_node,
+ walk_state->operands[0]);
+ obj_desc->buffer.aml_start = op->named.data;
+ obj_desc->buffer.aml_length = op->named.length;
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * Defer evaluation of Package term_arg operand and all
+ * package elements. (01/2017): We defer the element
+ * resolution to allow forward references from the package
+ * in order to provide compatibility with other ACPI
+ * implementations.
+ */
+ obj_desc->package.node =
+ ACPI_CAST_PTR(struct acpi_namespace_node,
+ walk_state->operands[0]);
+
+ if (!op->named.data) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ obj_desc->package.aml_start = op->named.data;
+ obj_desc->package.aml_length = op->named.length;
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ switch (op_info->type) {
+ case AML_TYPE_CONSTANT:
+ /*
+ * Resolve AML Constants here - AND ONLY HERE!
+ * All constants are integers.
+ * We mark the integer with a flag that indicates that it started
+ * life as a constant -- so that stores to constants will perform
+ * as expected (noop). zero_op is used as a placeholder for optional
+ * target operands.
+ */
+ obj_desc->common.flags = AOPOBJ_AML_CONSTANT;
+
+ switch (opcode) {
+ case AML_ZERO_OP:
+
+ obj_desc->integer.value = 0;
+ break;
+
+ case AML_ONE_OP:
+
+ obj_desc->integer.value = 1;
+ break;
+
+ case AML_ONES_OP:
+
+ obj_desc->integer.value = ACPI_UINT64_MAX;
+
+ /* Truncate value if we are executing from a 32-bit ACPI table */
+
+ (void)acpi_ex_truncate_for32bit_table(obj_desc);
+ break;
+
+ case AML_REVISION_OP:
+
+ obj_desc->integer.value = ACPI_CA_VERSION;
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown constant opcode 0x%X",
+ opcode));
+ status = AE_AML_OPERAND_TYPE;
+ break;
+ }
+ break;
+
+ case AML_TYPE_LITERAL:
+
+ obj_desc->integer.value = op->common.value.integer;
+
+ if (acpi_ex_truncate_for32bit_table(obj_desc)) {
+
+ /* Warn if we found a 64-bit constant in a 32-bit table */
+
+ ACPI_WARNING((AE_INFO,
+ "Truncated 64-bit constant found in 32-bit table: %8.8X%8.8X => %8.8X",
+ ACPI_FORMAT_UINT64(op->common.
+ value.integer),
+ (u32)obj_desc->integer.value));
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown Integer type 0x%X",
+ op_info->type));
+ status = AE_AML_OPERAND_TYPE;
+ break;
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ obj_desc->string.pointer = op->common.value.string;
+ obj_desc->string.length = (u32)strlen(op->common.value.string);
+
+ /*
+ * The string is contained in the ACPI table, don't ever try
+ * to delete it
+ */
+ obj_desc->common.flags |= AOPOBJ_STATIC_POINTER;
+ break;
+
+ case ACPI_TYPE_METHOD:
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ switch (op_info->type) {
+ case AML_TYPE_LOCAL_VARIABLE:
+
+ /* Local ID (0-7) is (AML opcode - base AML_FIRST_LOCAL_OP) */
+
+ obj_desc->reference.value =
+ ((u32)opcode) - AML_FIRST_LOCAL_OP;
+ obj_desc->reference.class = ACPI_REFCLASS_LOCAL;
+
+ status =
+ acpi_ds_method_data_get_node(ACPI_REFCLASS_LOCAL,
+ obj_desc->reference.
+ value, walk_state,
+ ACPI_CAST_INDIRECT_PTR
+ (struct
+ acpi_namespace_node,
+ &obj_desc->reference.
+ object));
+ break;
+
+ case AML_TYPE_METHOD_ARGUMENT:
+
+ /* Arg ID (0-6) is (AML opcode - base AML_FIRST_ARG_OP) */
+
+ obj_desc->reference.value =
+ ((u32)opcode) - AML_FIRST_ARG_OP;
+ obj_desc->reference.class = ACPI_REFCLASS_ARG;
+
+ status = acpi_ds_method_data_get_node(ACPI_REFCLASS_ARG,
+ obj_desc->
+ reference.value,
+ walk_state,
+ ACPI_CAST_INDIRECT_PTR
+ (struct
+ acpi_namespace_node,
+ &obj_desc->
+ reference.
+ object));
+ break;
+
+ default: /* Object name or Debug object */
+
+ switch (op->common.aml_opcode) {
+ case AML_INT_NAMEPATH_OP:
+
+ /* Node was saved in Op */
+
+ obj_desc->reference.node = op->common.node;
+ obj_desc->reference.class = ACPI_REFCLASS_NAME;
+ if (op->common.node) {
+ obj_desc->reference.object =
+ op->common.node->object;
+ }
+ break;
+
+ case AML_DEBUG_OP:
+
+ obj_desc->reference.class = ACPI_REFCLASS_DEBUG;
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unimplemented reference type for AML opcode: 0x%4.4X",
+ opcode));
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ break;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unimplemented data type: 0x%X",
+ obj_desc->common.type));
+
+ status = AE_AML_OPERAND_TYPE;
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c
new file mode 100644
index 000000000..2b9b6a974
--- /dev/null
+++ b/drivers/acpi/acpica/dsopcode.c
@@ -0,0 +1,762 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dsopcode - Dispatcher support for regions and fields
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+#include "acevents.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dsopcode")
+
+/* Local prototypes */
+static acpi_status
+acpi_ds_init_buffer_field(u16 aml_opcode,
+ union acpi_operand_object *obj_desc,
+ union acpi_operand_object *buffer_desc,
+ union acpi_operand_object *offset_desc,
+ union acpi_operand_object *length_desc,
+ union acpi_operand_object *result_desc);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_initialize_region
+ *
+ * PARAMETERS: obj_handle - Region namespace node
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Front end to ev_initialize_region
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ds_initialize_region(acpi_handle obj_handle)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ obj_desc = acpi_ns_get_attached_object(obj_handle);
+
+ /* Namespace is NOT locked */
+
+ status = acpi_ev_initialize_region(obj_desc);
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_init_buffer_field
+ *
+ * PARAMETERS: aml_opcode - create_xxx_field
+ * obj_desc - buffer_field object
+ * buffer_desc - Host Buffer
+ * offset_desc - Offset into buffer
+ * length_desc - Length of field (CREATE_FIELD_OP only)
+ * result_desc - Where to store the result
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform actual initialization of a buffer field
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ds_init_buffer_field(u16 aml_opcode,
+ union acpi_operand_object *obj_desc,
+ union acpi_operand_object *buffer_desc,
+ union acpi_operand_object *offset_desc,
+ union acpi_operand_object *length_desc,
+ union acpi_operand_object *result_desc)
+{
+ u32 offset;
+ u32 bit_offset;
+ u32 bit_count;
+ u8 field_flags;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_init_buffer_field, obj_desc);
+
+ /* Host object must be a Buffer */
+
+ if (buffer_desc->common.type != ACPI_TYPE_BUFFER) {
+ ACPI_ERROR((AE_INFO,
+ "Target of Create Field is not a Buffer object - %s",
+ acpi_ut_get_object_type_name(buffer_desc)));
+
+ status = AE_AML_OPERAND_TYPE;
+ goto cleanup;
+ }
+
+ /*
+ * The last parameter to all of these opcodes (result_desc) started
+ * out as a name_string, and should therefore now be a NS node
+ * after resolution in acpi_ex_resolve_operands().
+ */
+ if (ACPI_GET_DESCRIPTOR_TYPE(result_desc) != ACPI_DESC_TYPE_NAMED) {
+ ACPI_ERROR((AE_INFO,
+ "(%s) destination not a NS Node [%s]",
+ acpi_ps_get_opcode_name(aml_opcode),
+ acpi_ut_get_descriptor_name(result_desc)));
+
+ status = AE_AML_OPERAND_TYPE;
+ goto cleanup;
+ }
+
+ offset = (u32) offset_desc->integer.value;
+
+ /*
+ * Setup the Bit offsets and counts, according to the opcode
+ */
+ switch (aml_opcode) {
+ case AML_CREATE_FIELD_OP:
+
+ /* Offset is in bits, count is in bits */
+
+ field_flags = AML_FIELD_ACCESS_BYTE;
+ bit_offset = offset;
+ bit_count = (u32) length_desc->integer.value;
+
+ /* Must have a valid (>0) bit count */
+
+ if (bit_count == 0) {
+ ACPI_BIOS_ERROR((AE_INFO,
+ "Attempt to CreateField of length zero"));
+ status = AE_AML_OPERAND_VALUE;
+ goto cleanup;
+ }
+ break;
+
+ case AML_CREATE_BIT_FIELD_OP:
+
+ /* Offset is in bits, Field is one bit */
+
+ bit_offset = offset;
+ bit_count = 1;
+ field_flags = AML_FIELD_ACCESS_BYTE;
+ break;
+
+ case AML_CREATE_BYTE_FIELD_OP:
+
+ /* Offset is in bytes, field is one byte */
+
+ bit_offset = 8 * offset;
+ bit_count = 8;
+ field_flags = AML_FIELD_ACCESS_BYTE;
+ break;
+
+ case AML_CREATE_WORD_FIELD_OP:
+
+ /* Offset is in bytes, field is one word */
+
+ bit_offset = 8 * offset;
+ bit_count = 16;
+ field_flags = AML_FIELD_ACCESS_WORD;
+ break;
+
+ case AML_CREATE_DWORD_FIELD_OP:
+
+ /* Offset is in bytes, field is one dword */
+
+ bit_offset = 8 * offset;
+ bit_count = 32;
+ field_flags = AML_FIELD_ACCESS_DWORD;
+ break;
+
+ case AML_CREATE_QWORD_FIELD_OP:
+
+ /* Offset is in bytes, field is one qword */
+
+ bit_offset = 8 * offset;
+ bit_count = 64;
+ field_flags = AML_FIELD_ACCESS_QWORD;
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown field creation opcode 0x%02X",
+ aml_opcode));
+ status = AE_AML_BAD_OPCODE;
+ goto cleanup;
+ }
+
+ /* Entire field must fit within the current length of the buffer */
+
+ if ((bit_offset + bit_count) > (8 * (u32)buffer_desc->buffer.length)) {
+ status = AE_AML_BUFFER_LIMIT;
+ ACPI_BIOS_EXCEPTION((AE_INFO, status,
+ "Field [%4.4s] at bit offset/length %u/%u "
+ "exceeds size of target Buffer (%u bits)",
+ acpi_ut_get_node_name(result_desc),
+ bit_offset, bit_count,
+ 8 * (u32)buffer_desc->buffer.length));
+ goto cleanup;
+ }
+
+ /*
+ * Initialize areas of the field object that are common to all fields
+ * For field_flags, use LOCK_RULE = 0 (NO_LOCK),
+ * UPDATE_RULE = 0 (UPDATE_PRESERVE)
+ */
+ status =
+ acpi_ex_prep_common_field_object(obj_desc, field_flags, 0,
+ bit_offset, bit_count);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ obj_desc->buffer_field.buffer_obj = buffer_desc;
+ obj_desc->buffer_field.is_create_field =
+ aml_opcode == AML_CREATE_FIELD_OP;
+
+ /* Reference count for buffer_desc inherits obj_desc count */
+
+ buffer_desc->common.reference_count = (u16)
+ (buffer_desc->common.reference_count +
+ obj_desc->common.reference_count);
+
+cleanup:
+
+ /* Always delete the operands */
+
+ acpi_ut_remove_reference(offset_desc);
+ acpi_ut_remove_reference(buffer_desc);
+
+ if (aml_opcode == AML_CREATE_FIELD_OP) {
+ acpi_ut_remove_reference(length_desc);
+ }
+
+ /* On failure, delete the result descriptor */
+
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(result_desc); /* Result descriptor */
+ } else {
+ /* Now the address and length are valid for this buffer_field */
+
+ obj_desc->buffer_field.flags |= AOPOBJ_DATA_VALID;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_eval_buffer_field_operands
+ *
+ * PARAMETERS: walk_state - Current walk
+ * op - A valid buffer_field Op object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get buffer_field Buffer and Index
+ * Called from acpi_ds_exec_end_op during buffer_field parse tree walk
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_eval_buffer_field_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *node;
+ union acpi_parse_object *next_op;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_eval_buffer_field_operands, op);
+
+ /*
+ * This is where we evaluate the address and length fields of the
+ * create_xxx_field declaration
+ */
+ node = op->common.node;
+
+ /* next_op points to the op that holds the Buffer */
+
+ next_op = op->common.value.arg;
+
+ /* Evaluate/create the address and length operands */
+
+ status = acpi_ds_create_operands(walk_state, next_op);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /* Resolve the operands */
+
+ status =
+ acpi_ex_resolve_operands(op->common.aml_opcode, ACPI_WALK_OPERANDS,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO, "(%s) bad operand(s), status 0x%X",
+ acpi_ps_get_opcode_name(op->common.aml_opcode),
+ status));
+
+ return_ACPI_STATUS(status);
+ }
+
+ /* Initialize the Buffer Field */
+
+ if (op->common.aml_opcode == AML_CREATE_FIELD_OP) {
+
+ /* NOTE: Slightly different operands for this opcode */
+
+ status =
+ acpi_ds_init_buffer_field(op->common.aml_opcode, obj_desc,
+ walk_state->operands[0],
+ walk_state->operands[1],
+ walk_state->operands[2],
+ walk_state->operands[3]);
+ } else {
+ /* All other, create_xxx_field opcodes */
+
+ status =
+ acpi_ds_init_buffer_field(op->common.aml_opcode, obj_desc,
+ walk_state->operands[0],
+ walk_state->operands[1], NULL,
+ walk_state->operands[2]);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_eval_region_operands
+ *
+ * PARAMETERS: walk_state - Current walk
+ * op - A valid region Op object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get region address and length
+ * Called from acpi_ds_exec_end_op during op_region parse tree walk
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_eval_region_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *operand_desc;
+ struct acpi_namespace_node *node;
+ union acpi_parse_object *next_op;
+ acpi_adr_space_type space_id;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_eval_region_operands, op);
+
+ /*
+ * This is where we evaluate the address and length fields of the
+ * op_region declaration
+ */
+ node = op->common.node;
+
+ /* next_op points to the op that holds the space_ID */
+
+ next_op = op->common.value.arg;
+ space_id = (acpi_adr_space_type)next_op->common.value.integer;
+
+ /* next_op points to address op */
+
+ next_op = next_op->common.next;
+
+ /* Evaluate/create the address and length operands */
+
+ status = acpi_ds_create_operands(walk_state, next_op);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Resolve the length and address operands to numbers */
+
+ status =
+ acpi_ex_resolve_operands(op->common.aml_opcode, ACPI_WALK_OPERANDS,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /*
+ * Get the length operand and save it
+ * (at Top of stack)
+ */
+ operand_desc = walk_state->operands[walk_state->num_operands - 1];
+
+ obj_desc->region.length = (u32) operand_desc->integer.value;
+ acpi_ut_remove_reference(operand_desc);
+
+ /* A zero-length operation region is unusable. Just warn */
+
+ if (!obj_desc->region.length
+ && (space_id < ACPI_NUM_PREDEFINED_REGIONS)) {
+ ACPI_WARNING((AE_INFO,
+ "Operation Region [%4.4s] has zero length (SpaceId %X)",
+ node->name.ascii, space_id));
+ }
+
+ /*
+ * Get the address and save it
+ * (at top of stack - 1)
+ */
+ operand_desc = walk_state->operands[walk_state->num_operands - 2];
+
+ obj_desc->region.address = (acpi_physical_address)
+ operand_desc->integer.value;
+ acpi_ut_remove_reference(operand_desc);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "RgnObj %p Addr %8.8X%8.8X Len %X\n",
+ obj_desc,
+ ACPI_FORMAT_UINT64(obj_desc->region.address),
+ obj_desc->region.length));
+
+ status = acpi_ut_add_address_range(obj_desc->region.space_id,
+ obj_desc->region.address,
+ obj_desc->region.length, node);
+
+ /* Now the address and length are valid for this opregion */
+
+ obj_desc->region.flags |= AOPOBJ_DATA_VALID;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_eval_table_region_operands
+ *
+ * PARAMETERS: walk_state - Current walk
+ * op - A valid region Op object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get region address and length.
+ * Called from acpi_ds_exec_end_op during data_table_region parse
+ * tree walk.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_eval_table_region_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object **operand;
+ struct acpi_namespace_node *node;
+ union acpi_parse_object *next_op;
+ struct acpi_table_header *table;
+ u32 table_index;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_eval_table_region_operands, op);
+
+ /*
+ * This is where we evaluate the Signature string, oem_id string,
+ * and oem_table_id string of the Data Table Region declaration
+ */
+ node = op->common.node;
+
+ /* next_op points to Signature string op */
+
+ next_op = op->common.value.arg;
+
+ /*
+ * Evaluate/create the Signature string, oem_id string,
+ * and oem_table_id string operands
+ */
+ status = acpi_ds_create_operands(walk_state, next_op);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ operand = &walk_state->operands[0];
+
+ /*
+ * Resolve the Signature string, oem_id string,
+ * and oem_table_id string operands
+ */
+ status =
+ acpi_ex_resolve_operands(op->common.aml_opcode, ACPI_WALK_OPERANDS,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /* Find the ACPI table */
+
+ status = acpi_tb_find_table(operand[0]->string.pointer,
+ operand[1]->string.pointer,
+ operand[2]->string.pointer, &table_index);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_NOT_FOUND) {
+ ACPI_ERROR((AE_INFO,
+ "ACPI Table [%4.4s] OEM:(%s, %s) not found in RSDT/XSDT",
+ operand[0]->string.pointer,
+ operand[1]->string.pointer,
+ operand[2]->string.pointer));
+ }
+ goto cleanup;
+ }
+
+ status = acpi_get_table_by_index(table_index, &table);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ status = AE_NOT_EXIST;
+ goto cleanup;
+ }
+
+ obj_desc->region.address = ACPI_PTR_TO_PHYSADDR(table);
+ obj_desc->region.length = table->length;
+ obj_desc->region.pointer = table;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "RgnObj %p Addr %8.8X%8.8X Len %X\n",
+ obj_desc,
+ ACPI_FORMAT_UINT64(obj_desc->region.address),
+ obj_desc->region.length));
+
+ /* Now the address and length are valid for this opregion */
+
+ obj_desc->region.flags |= AOPOBJ_DATA_VALID;
+
+cleanup:
+ acpi_ut_remove_reference(operand[0]);
+ acpi_ut_remove_reference(operand[1]);
+ acpi_ut_remove_reference(operand[2]);
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_eval_data_object_operands
+ *
+ * PARAMETERS: walk_state - Current walk
+ * op - A valid data_object Op object
+ * obj_desc - data_object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get the operands and complete the following data object types:
+ * Buffer, Package.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_eval_data_object_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ union acpi_operand_object *obj_desc)
+{
+ acpi_status status;
+ union acpi_operand_object *arg_desc;
+ u32 length;
+
+ ACPI_FUNCTION_TRACE(ds_eval_data_object_operands);
+
+ /* The first operand (for all of these data objects) is the length */
+
+ /*
+ * Set proper index into operand stack for acpi_ds_obj_stack_push
+ * invoked inside acpi_ds_create_operand.
+ */
+ walk_state->operand_index = walk_state->num_operands;
+
+ /* Ignore if child is not valid */
+
+ if (!op->common.value.arg) {
+ ACPI_ERROR((AE_INFO,
+ "Missing child while evaluating opcode %4.4X, Op %p",
+ op->common.aml_opcode, op));
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ status = acpi_ds_create_operand(walk_state, op->common.value.arg, 1);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ex_resolve_operands(walk_state->opcode,
+ &(walk_state->
+ operands[walk_state->num_operands -
+ 1]), walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Extract length operand */
+
+ arg_desc = walk_state->operands[walk_state->num_operands - 1];
+ length = (u32) arg_desc->integer.value;
+
+ /* Cleanup for length operand */
+
+ status = acpi_ds_obj_stack_pop(1, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_ut_remove_reference(arg_desc);
+
+ /*
+ * Create the actual data object
+ */
+ switch (op->common.aml_opcode) {
+ case AML_BUFFER_OP:
+
+ status =
+ acpi_ds_build_internal_buffer_obj(walk_state, op, length,
+ &obj_desc);
+ break;
+
+ case AML_PACKAGE_OP:
+ case AML_VARIABLE_PACKAGE_OP:
+
+ status =
+ acpi_ds_build_internal_package_obj(walk_state, op, length,
+ &obj_desc);
+ break;
+
+ default:
+
+ return_ACPI_STATUS(AE_AML_BAD_OPCODE);
+ }
+
+ if (ACPI_SUCCESS(status)) {
+ /*
+ * Return the object in the walk_state, unless the parent is a package -
+ * in this case, the return object will be stored in the parse tree
+ * for the package.
+ */
+ if ((!op->common.parent) ||
+ ((op->common.parent->common.aml_opcode != AML_PACKAGE_OP) &&
+ (op->common.parent->common.aml_opcode !=
+ AML_VARIABLE_PACKAGE_OP)
+ && (op->common.parent->common.aml_opcode !=
+ AML_NAME_OP))) {
+ walk_state->result_obj = obj_desc;
+ }
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_eval_bank_field_operands
+ *
+ * PARAMETERS: walk_state - Current walk
+ * op - A valid bank_field Op object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get bank_field bank_value
+ * Called from acpi_ds_exec_end_op during bank_field parse tree walk
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_eval_bank_field_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *operand_desc;
+ struct acpi_namespace_node *node;
+ union acpi_parse_object *next_op;
+ union acpi_parse_object *arg;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_eval_bank_field_operands, op);
+
+ /*
+ * This is where we evaluate the bank_value field of the
+ * bank_field declaration
+ */
+
+ /* next_op points to the op that holds the Region */
+
+ next_op = op->common.value.arg;
+
+ /* next_op points to the op that holds the Bank Register */
+
+ next_op = next_op->common.next;
+
+ /* next_op points to the op that holds the Bank Value */
+
+ next_op = next_op->common.next;
+
+ /*
+ * Set proper index into operand stack for acpi_ds_obj_stack_push
+ * invoked inside acpi_ds_create_operand.
+ *
+ * We use walk_state->Operands[0] to store the evaluated bank_value
+ */
+ walk_state->operand_index = 0;
+
+ status = acpi_ds_create_operand(walk_state, next_op, 0);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ex_resolve_to_value(&walk_state->operands[0], walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ ACPI_DUMP_OPERANDS(ACPI_WALK_OPERANDS,
+ acpi_ps_get_opcode_name(op->common.aml_opcode), 1);
+ /*
+ * Get the bank_value operand and save it
+ * (at Top of stack)
+ */
+ operand_desc = walk_state->operands[0];
+
+ /* Arg points to the start Bank Field */
+
+ arg = acpi_ps_get_arg(op, 4);
+ while (arg) {
+
+ /* Ignore OFFSET and ACCESSAS terms here */
+
+ if (arg->common.aml_opcode == AML_INT_NAMEDFIELD_OP) {
+ node = arg->common.node;
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ obj_desc->bank_field.value =
+ (u32) operand_desc->integer.value;
+ }
+
+ /* Move to next field in the list */
+
+ arg = arg->common.next;
+ }
+
+ acpi_ut_remove_reference(operand_desc);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/dspkginit.c b/drivers/acpi/acpica/dspkginit.c
new file mode 100644
index 000000000..1624d6e7d
--- /dev/null
+++ b/drivers/acpi/acpica/dspkginit.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dspkginit - Completion of deferred package initialization
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acparser.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("dspkginit")
+
+/* Local prototypes */
+static void
+acpi_ds_resolve_package_element(union acpi_operand_object **element);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_build_internal_package_obj
+ *
+ * PARAMETERS: walk_state - Current walk state
+ * op - Parser object to be translated
+ * element_count - Number of elements in the package - this is
+ * the num_elements argument to Package()
+ * obj_desc_ptr - Where the ACPI internal object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Translate a parser Op package object to the equivalent
+ * namespace object
+ *
+ * NOTE: The number of elements in the package will be always be the num_elements
+ * count, regardless of the number of elements in the package list. If
+ * num_elements is smaller, only that many package list elements are used.
+ * if num_elements is larger, the Package object is padded out with
+ * objects of type Uninitialized (as per ACPI spec.)
+ *
+ * Even though the ASL compilers do not allow num_elements to be smaller
+ * than the Package list length (for the fixed length package opcode), some
+ * BIOS code modifies the AML on the fly to adjust the num_elements, and
+ * this code compensates for that. This also provides compatibility with
+ * other AML interpreters.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ u32 element_count,
+ union acpi_operand_object **obj_desc_ptr)
+{
+ union acpi_parse_object *arg;
+ union acpi_parse_object *parent;
+ union acpi_operand_object *obj_desc = NULL;
+ acpi_status status = AE_OK;
+ u8 module_level_code = FALSE;
+ u16 reference_count;
+ u32 index;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ds_build_internal_package_obj);
+
+ /* Check if we are executing module level code */
+
+ if (walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL) {
+ module_level_code = TRUE;
+ }
+
+ /* Find the parent of a possibly nested package */
+
+ parent = op->common.parent;
+ while ((parent->common.aml_opcode == AML_PACKAGE_OP) ||
+ (parent->common.aml_opcode == AML_VARIABLE_PACKAGE_OP)) {
+ parent = parent->common.parent;
+ }
+
+ /*
+ * If we are evaluating a Named package object of the form:
+ * Name (xxxx, Package)
+ * the package object already exists, otherwise it must be created.
+ */
+ obj_desc = *obj_desc_ptr;
+ if (!obj_desc) {
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_PACKAGE);
+ *obj_desc_ptr = obj_desc;
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ obj_desc->package.node = parent->common.node;
+ }
+
+ if (obj_desc->package.flags & AOPOBJ_DATA_VALID) { /* Just in case */
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Allocate the element array (array of pointers to the individual
+ * objects) if necessary. the count is based on the num_elements
+ * parameter. Add an extra pointer slot so that the list is always
+ * null terminated.
+ */
+ if (!obj_desc->package.elements) {
+ obj_desc->package.elements = ACPI_ALLOCATE_ZEROED(((acpi_size)
+ element_count
+ +
+ 1) *
+ sizeof(void
+ *));
+
+ if (!obj_desc->package.elements) {
+ acpi_ut_delete_object_desc(obj_desc);
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ obj_desc->package.count = element_count;
+ }
+
+ /* First arg is element count. Second arg begins the initializer list */
+
+ arg = op->common.value.arg;
+ arg = arg->common.next;
+
+ /*
+ * If we are executing module-level code, we will defer the
+ * full resolution of the package elements in order to support
+ * forward references from the elements. This provides
+ * compatibility with other ACPI implementations.
+ */
+ if (module_level_code) {
+ obj_desc->package.aml_start = walk_state->aml;
+ obj_desc->package.aml_length = 0;
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
+ "%s: Deferring resolution of Package elements\n",
+ ACPI_GET_FUNCTION_NAME));
+ }
+
+ /*
+ * Initialize the elements of the package, up to the num_elements count.
+ * Package is automatically padded with uninitialized (NULL) elements
+ * if num_elements is greater than the package list length. Likewise,
+ * Package is truncated if num_elements is less than the list length.
+ */
+ for (i = 0; arg && (i < element_count); i++) {
+ if (arg->common.aml_opcode == AML_INT_RETURN_VALUE_OP) {
+ if (!arg->common.node) {
+ /*
+ * This is the case where an expression has returned a value.
+ * The use of expressions (term_args) within individual
+ * package elements is not supported by the AML interpreter,
+ * even though the ASL grammar supports it. Example:
+ *
+ * Name (INT1, 0x1234)
+ *
+ * Name (PKG3, Package () {
+ * Add (INT1, 0xAAAA0000)
+ * })
+ *
+ * 1) No known AML interpreter supports this type of construct
+ * 2) This fixes a fault if the construct is encountered
+ */
+ ACPI_EXCEPTION((AE_INFO, AE_SUPPORT,
+ "Expressions within package elements are not supported"));
+
+ /* Cleanup the return object, it is not needed */
+
+ acpi_ut_remove_reference(walk_state->results->
+ results.obj_desc[0]);
+ return_ACPI_STATUS(AE_SUPPORT);
+ }
+
+ if (arg->common.node->type == ACPI_TYPE_METHOD) {
+ /*
+ * A method reference "looks" to the parser to be a method
+ * invocation, so we special case it here
+ */
+ arg->common.aml_opcode = AML_INT_NAMEPATH_OP;
+ status =
+ acpi_ds_build_internal_object(walk_state,
+ arg,
+ &obj_desc->
+ package.
+ elements[i]);
+ } else {
+ /* This package element is already built, just get it */
+
+ obj_desc->package.elements[i] =
+ ACPI_CAST_PTR(union acpi_operand_object,
+ arg->common.node);
+ }
+ } else {
+ status =
+ acpi_ds_build_internal_object(walk_state, arg,
+ &obj_desc->package.
+ elements[i]);
+ if (status == AE_NOT_FOUND) {
+ ACPI_ERROR((AE_INFO, "%-48s",
+ "****DS namepath not found"));
+ }
+
+ if (!module_level_code) {
+ /*
+ * Initialize this package element. This function handles the
+ * resolution of named references within the package.
+ * Forward references from module-level code are deferred
+ * until all ACPI tables are loaded.
+ */
+ acpi_ds_init_package_element(0,
+ obj_desc->package.
+ elements[i], NULL,
+ &obj_desc->package.
+ elements[i]);
+ }
+ }
+
+ if (*obj_desc_ptr) {
+
+ /* Existing package, get existing reference count */
+
+ reference_count =
+ (*obj_desc_ptr)->common.reference_count;
+ if (reference_count > 1) {
+
+ /* Make new element ref count match original ref count */
+ /* TBD: Probably need an acpi_ut_add_references function */
+
+ for (index = 0;
+ index < ((u32)reference_count - 1);
+ index++) {
+ acpi_ut_add_reference((obj_desc->
+ package.
+ elements[i]));
+ }
+ }
+ }
+
+ arg = arg->common.next;
+ }
+
+ /* Check for match between num_elements and actual length of package_list */
+
+ if (arg) {
+ /*
+ * num_elements was exhausted, but there are remaining elements in
+ * the package_list. Truncate the package to num_elements.
+ *
+ * Note: technically, this is an error, from ACPI spec: "It is an
+ * error for NumElements to be less than the number of elements in
+ * the PackageList". However, we just print a message and no
+ * exception is returned. This provides compatibility with other
+ * ACPI implementations. Some firmware implementations will alter
+ * the num_elements on the fly, possibly creating this type of
+ * ill-formed package object.
+ */
+ while (arg) {
+ /*
+ * We must delete any package elements that were created earlier
+ * and are not going to be used because of the package truncation.
+ */
+ if (arg->common.node) {
+ acpi_ut_remove_reference(ACPI_CAST_PTR
+ (union
+ acpi_operand_object,
+ arg->common.node));
+ arg->common.node = NULL;
+ }
+
+ /* Find out how many elements there really are */
+
+ i++;
+ arg = arg->common.next;
+ }
+
+ ACPI_INFO(("Actual Package length (%u) is larger than "
+ "NumElements field (%u), truncated",
+ i, element_count));
+ } else if (i < element_count) {
+ /*
+ * Arg list (elements) was exhausted, but we did not reach
+ * num_elements count.
+ *
+ * Note: this is not an error, the package is padded out
+ * with NULLs as per the ACPI specification.
+ */
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO,
+ "%s: Package List length (%u) smaller than NumElements "
+ "count (%u), padded with null elements\n",
+ ACPI_GET_FUNCTION_NAME, i,
+ element_count));
+ }
+
+ /* Module-level packages will be resolved later */
+
+ if (!module_level_code) {
+ obj_desc->package.flags |= AOPOBJ_DATA_VALID;
+ }
+
+ op->common.node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_init_package_element
+ *
+ * PARAMETERS: acpi_pkg_callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Resolve a named reference element within a package object
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_init_package_element(u8 object_type,
+ union acpi_operand_object *source_object,
+ union acpi_generic_state *state, void *context)
+{
+ union acpi_operand_object **element_ptr;
+
+ ACPI_FUNCTION_TRACE(ds_init_package_element);
+
+ if (!source_object) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * The following code is a bit of a hack to workaround a (current)
+ * limitation of the acpi_pkg_callback interface. We need a pointer
+ * to the location within the element array because a new object
+ * may be created and stored there.
+ */
+ if (context) {
+
+ /* A direct call was made to this function */
+
+ element_ptr = (union acpi_operand_object **)context;
+ } else {
+ /* Call came from acpi_ut_walk_package_tree */
+
+ element_ptr = state->pkg.this_target_obj;
+ }
+
+ /* We are only interested in reference objects/elements */
+
+ if (source_object->common.type == ACPI_TYPE_LOCAL_REFERENCE) {
+
+ /* Attempt to resolve the (named) reference to a namespace node */
+
+ acpi_ds_resolve_package_element(element_ptr);
+ } else if (source_object->common.type == ACPI_TYPE_PACKAGE) {
+ source_object->package.flags |= AOPOBJ_DATA_VALID;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_resolve_package_element
+ *
+ * PARAMETERS: element_ptr - Pointer to a reference object
+ *
+ * RETURN: Possible new element is stored to the indirect element_ptr
+ *
+ * DESCRIPTION: Resolve a package element that is a reference to a named
+ * object.
+ *
+ ******************************************************************************/
+
+static void
+acpi_ds_resolve_package_element(union acpi_operand_object **element_ptr)
+{
+ acpi_status status;
+ acpi_status status2;
+ union acpi_generic_state scope_info;
+ union acpi_operand_object *element = *element_ptr;
+ struct acpi_namespace_node *resolved_node;
+ struct acpi_namespace_node *original_node;
+ char *external_path = "";
+ acpi_object_type type;
+
+ ACPI_FUNCTION_TRACE(ds_resolve_package_element);
+
+ /* Check if reference element is already resolved */
+
+ if (element->reference.resolved) {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
+ "%s: Package element is already resolved\n",
+ ACPI_GET_FUNCTION_NAME));
+
+ return_VOID;
+ }
+
+ /* Element must be a reference object of correct type */
+
+ scope_info.scope.node = element->reference.node; /* Prefix node */
+
+ status = acpi_ns_lookup(&scope_info, (char *)element->reference.aml,
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE,
+ NULL, &resolved_node);
+ if (ACPI_FAILURE(status)) {
+ if ((status == AE_NOT_FOUND)
+ && acpi_gbl_ignore_package_resolution_errors) {
+ /*
+ * Optionally be silent about the NOT_FOUND case for the referenced
+ * name. Although this is potentially a serious problem,
+ * it can generate a lot of noise/errors on platforms whose
+ * firmware carries around a bunch of unused Package objects.
+ * To disable these errors, set this global to TRUE:
+ * acpi_gbl_ignore_package_resolution_errors
+ *
+ * If the AML actually tries to use such a package, the unresolved
+ * element(s) will be replaced with NULL elements.
+ */
+
+ /* Referenced name not found, set the element to NULL */
+
+ acpi_ut_remove_reference(*element_ptr);
+ *element_ptr = NULL;
+ return_VOID;
+ }
+
+ status2 = acpi_ns_externalize_name(ACPI_UINT32_MAX,
+ (char *)element->reference.
+ aml, NULL, &external_path);
+
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While resolving a named reference package element - %s",
+ external_path));
+ if (ACPI_SUCCESS(status2)) {
+ ACPI_FREE(external_path);
+ }
+
+ /* Could not resolve name, set the element to NULL */
+
+ acpi_ut_remove_reference(*element_ptr);
+ *element_ptr = NULL;
+ return_VOID;
+ } else if (resolved_node->type == ACPI_TYPE_ANY) {
+
+ /* Named reference not resolved, return a NULL package element */
+
+ ACPI_ERROR((AE_INFO,
+ "Could not resolve named package element [%4.4s] in [%4.4s]",
+ resolved_node->name.ascii,
+ scope_info.scope.node->name.ascii));
+ *element_ptr = NULL;
+ return_VOID;
+ }
+
+ /*
+ * Special handling for Alias objects. We need resolved_node to point
+ * to the Alias target. This effectively "resolves" the alias.
+ */
+ if (resolved_node->type == ACPI_TYPE_LOCAL_ALIAS) {
+ resolved_node = ACPI_CAST_PTR(struct acpi_namespace_node,
+ resolved_node->object);
+ }
+
+ /* Update the reference object */
+
+ element->reference.resolved = TRUE;
+ element->reference.node = resolved_node;
+ type = element->reference.node->type;
+
+ /*
+ * Attempt to resolve the node to a value before we insert it into
+ * the package. If this is a reference to a common data type,
+ * resolve it immediately. According to the ACPI spec, package
+ * elements can only be "data objects" or method references.
+ * Attempt to resolve to an Integer, Buffer, String or Package.
+ * If cannot, return the named reference (for things like Devices,
+ * Methods, etc.) Buffer Fields and Fields will resolve to simple
+ * objects (int/buf/str/pkg).
+ *
+ * NOTE: References to things like Devices, Methods, Mutexes, etc.
+ * will remain as named references. This behavior is not described
+ * in the ACPI spec, but it appears to be an oversight.
+ */
+ original_node = resolved_node;
+ status = acpi_ex_resolve_node_to_value(&resolved_node, NULL);
+ if (ACPI_FAILURE(status)) {
+ return_VOID;
+ }
+
+ switch (type) {
+ /*
+ * These object types are a result of named references, so we will
+ * leave them as reference objects. In other words, these types
+ * have no intrinsic "value".
+ */
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_THERMAL:
+ case ACPI_TYPE_METHOD:
+ break;
+
+ case ACPI_TYPE_MUTEX:
+ case ACPI_TYPE_POWER:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_EVENT:
+ case ACPI_TYPE_REGION:
+
+ /* acpi_ex_resolve_node_to_value gave these an extra reference */
+
+ acpi_ut_remove_reference(original_node->object);
+ break;
+
+ default:
+ /*
+ * For all other types - the node was resolved to an actual
+ * operand object with a value, return the object. Remove
+ * a reference on the existing object.
+ */
+ acpi_ut_remove_reference(element);
+ *element_ptr = (union acpi_operand_object *)resolved_node;
+ break;
+ }
+
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c
new file mode 100644
index 000000000..fb9ed5e1d
--- /dev/null
+++ b/drivers/acpi/acpica/dsutils.c
@@ -0,0 +1,831 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: dsutils - Dispatcher utilities
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+#include "acdebug.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dsutils")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_clear_implicit_return
+ *
+ * PARAMETERS: walk_state - Current State
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Clear and remove a reference on an implicit return value. Used
+ * to delete "stale" return values (if enabled, the return value
+ * from every operator is saved at least momentarily, in case the
+ * parent method exits.)
+ *
+ ******************************************************************************/
+void acpi_ds_clear_implicit_return(struct acpi_walk_state *walk_state)
+{
+ ACPI_FUNCTION_NAME(ds_clear_implicit_return);
+
+ /*
+ * Slack must be enabled for this feature
+ */
+ if (!acpi_gbl_enable_interpreter_slack) {
+ return;
+ }
+
+ if (walk_state->implicit_return_obj) {
+ /*
+ * Delete any "stale" implicit return. However, in
+ * complex statements, the implicit return value can be
+ * bubbled up several levels.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Removing reference on stale implicit return obj %p\n",
+ walk_state->implicit_return_obj));
+
+ acpi_ut_remove_reference(walk_state->implicit_return_obj);
+ walk_state->implicit_return_obj = NULL;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_do_implicit_return
+ *
+ * PARAMETERS: return_desc - The return value
+ * walk_state - Current State
+ * add_reference - True if a reference should be added to the
+ * return object
+ *
+ * RETURN: TRUE if implicit return enabled, FALSE otherwise
+ *
+ * DESCRIPTION: Implements the optional "implicit return". We save the result
+ * of every ASL operator and control method invocation in case the
+ * parent method exit. Before storing a new return value, we
+ * delete the previous return value.
+ *
+ ******************************************************************************/
+
+u8
+acpi_ds_do_implicit_return(union acpi_operand_object *return_desc,
+ struct acpi_walk_state *walk_state, u8 add_reference)
+{
+ ACPI_FUNCTION_NAME(ds_do_implicit_return);
+
+ /*
+ * Slack must be enabled for this feature, and we must
+ * have a valid return object
+ */
+ if ((!acpi_gbl_enable_interpreter_slack) || (!return_desc)) {
+ return (FALSE);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Result %p will be implicitly returned; Prev=%p\n",
+ return_desc, walk_state->implicit_return_obj));
+
+ /*
+ * Delete any "stale" implicit return value first. However, in
+ * complex statements, the implicit return value can be
+ * bubbled up several levels, so we don't clear the value if it
+ * is the same as the return_desc.
+ */
+ if (walk_state->implicit_return_obj) {
+ if (walk_state->implicit_return_obj == return_desc) {
+ return (TRUE);
+ }
+ acpi_ds_clear_implicit_return(walk_state);
+ }
+
+ /* Save the implicit return value, add a reference if requested */
+
+ walk_state->implicit_return_obj = return_desc;
+ if (add_reference) {
+ acpi_ut_add_reference(return_desc);
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_is_result_used
+ *
+ * PARAMETERS: op - Current Op
+ * walk_state - Current State
+ *
+ * RETURN: TRUE if result is used, FALSE otherwise
+ *
+ * DESCRIPTION: Check if a result object will be used by the parent
+ *
+ ******************************************************************************/
+
+u8
+acpi_ds_is_result_used(union acpi_parse_object * op,
+ struct acpi_walk_state * walk_state)
+{
+ const struct acpi_opcode_info *parent_info;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_is_result_used, op);
+
+ /* Must have both an Op and a Result Object */
+
+ if (!op) {
+ ACPI_ERROR((AE_INFO, "Null Op"));
+ return_UINT8(TRUE);
+ }
+
+ /*
+ * We know that this operator is not a
+ * Return() operator (would not come here.) The following code is the
+ * optional support for a so-called "implicit return". Some AML code
+ * assumes that the last value of the method is "implicitly" returned
+ * to the caller. Just save the last result as the return value.
+ * NOTE: this is optional because the ASL language does not actually
+ * support this behavior.
+ */
+ (void)acpi_ds_do_implicit_return(walk_state->result_obj, walk_state,
+ TRUE);
+
+ /*
+ * Now determine if the parent will use the result
+ *
+ * If there is no parent, or the parent is a scope_op, we are executing
+ * at the method level. An executing method typically has no parent,
+ * since each method is parsed separately. A method invoked externally
+ * via execute_control_method has a scope_op as the parent.
+ */
+ if ((!op->common.parent) ||
+ (op->common.parent->common.aml_opcode == AML_SCOPE_OP)) {
+
+ /* No parent, the return value cannot possibly be used */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "At Method level, result of [%s] not used\n",
+ acpi_ps_get_opcode_name(op->common.
+ aml_opcode)));
+ return_UINT8(FALSE);
+ }
+
+ /* Get info on the parent. The root_op is AML_SCOPE */
+
+ parent_info =
+ acpi_ps_get_opcode_info(op->common.parent->common.aml_opcode);
+ if (parent_info->class == AML_CLASS_UNKNOWN) {
+ ACPI_ERROR((AE_INFO, "Unknown parent opcode Op=%p", op));
+ return_UINT8(FALSE);
+ }
+
+ /*
+ * Decide what to do with the result based on the parent. If
+ * the parent opcode will not use the result, delete the object.
+ * Otherwise leave it as is, it will be deleted when it is used
+ * as an operand later.
+ */
+ switch (parent_info->class) {
+ case AML_CLASS_CONTROL:
+
+ switch (op->common.parent->common.aml_opcode) {
+ case AML_RETURN_OP:
+
+ /* Never delete the return value associated with a return opcode */
+
+ goto result_used;
+
+ case AML_IF_OP:
+ case AML_WHILE_OP:
+ /*
+ * If we are executing the predicate AND this is the predicate op,
+ * we will use the return value
+ */
+ if ((walk_state->control_state->common.state ==
+ ACPI_CONTROL_PREDICATE_EXECUTING) &&
+ (walk_state->control_state->control.predicate_op ==
+ op)) {
+ goto result_used;
+ }
+ break;
+
+ default:
+
+ /* Ignore other control opcodes */
+
+ break;
+ }
+
+ /* The general control opcode returns no result */
+
+ goto result_not_used;
+
+ case AML_CLASS_CREATE:
+ /*
+ * These opcodes allow term_arg(s) as operands and therefore
+ * the operands can be method calls. The result is used.
+ */
+ goto result_used;
+
+ case AML_CLASS_NAMED_OBJECT:
+
+ if ((op->common.parent->common.aml_opcode == AML_REGION_OP) ||
+ (op->common.parent->common.aml_opcode == AML_DATA_REGION_OP)
+ || (op->common.parent->common.aml_opcode == AML_PACKAGE_OP)
+ || (op->common.parent->common.aml_opcode == AML_BUFFER_OP)
+ || (op->common.parent->common.aml_opcode ==
+ AML_VARIABLE_PACKAGE_OP)
+ || (op->common.parent->common.aml_opcode ==
+ AML_INT_EVAL_SUBTREE_OP)
+ || (op->common.parent->common.aml_opcode ==
+ AML_BANK_FIELD_OP)) {
+ /*
+ * These opcodes allow term_arg(s) as operands and therefore
+ * the operands can be method calls. The result is used.
+ */
+ goto result_used;
+ }
+
+ goto result_not_used;
+
+ default:
+ /*
+ * In all other cases. the parent will actually use the return
+ * object, so keep it.
+ */
+ goto result_used;
+ }
+
+result_used:
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Result of [%s] used by Parent [%s] Op=%p\n",
+ acpi_ps_get_opcode_name(op->common.aml_opcode),
+ acpi_ps_get_opcode_name(op->common.parent->common.
+ aml_opcode), op));
+
+ return_UINT8(TRUE);
+
+result_not_used:
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Result of [%s] not used by Parent [%s] Op=%p\n",
+ acpi_ps_get_opcode_name(op->common.aml_opcode),
+ acpi_ps_get_opcode_name(op->common.parent->common.
+ aml_opcode), op));
+
+ return_UINT8(FALSE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_delete_result_if_not_used
+ *
+ * PARAMETERS: op - Current parse Op
+ * result_obj - Result of the operation
+ * walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Used after interpretation of an opcode. If there is an internal
+ * result descriptor, check if the parent opcode will actually use
+ * this result. If not, delete the result now so that it will
+ * not become orphaned.
+ *
+ ******************************************************************************/
+
+void
+acpi_ds_delete_result_if_not_used(union acpi_parse_object *op,
+ union acpi_operand_object *result_obj,
+ struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_delete_result_if_not_used, result_obj);
+
+ if (!op) {
+ ACPI_ERROR((AE_INFO, "Null Op"));
+ return_VOID;
+ }
+
+ if (!result_obj) {
+ return_VOID;
+ }
+
+ if (!acpi_ds_is_result_used(op, walk_state)) {
+
+ /* Must pop the result stack (obj_desc should be equal to result_obj) */
+
+ status = acpi_ds_result_pop(&obj_desc, walk_state);
+ if (ACPI_SUCCESS(status)) {
+ acpi_ut_remove_reference(result_obj);
+ }
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_resolve_operands
+ *
+ * PARAMETERS: walk_state - Current walk state with operands on stack
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Resolve all operands to their values. Used to prepare
+ * arguments to a control method invocation (a call from one
+ * method to another.)
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ds_resolve_operands(struct acpi_walk_state *walk_state)
+{
+ u32 i;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_resolve_operands, walk_state);
+
+ /*
+ * Attempt to resolve each of the valid operands
+ * Method arguments are passed by reference, not by value. This means
+ * that the actual objects are passed, not copies of the objects.
+ */
+ for (i = 0; i < walk_state->num_operands; i++) {
+ status =
+ acpi_ex_resolve_to_value(&walk_state->operands[i],
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_clear_operands
+ *
+ * PARAMETERS: walk_state - Current walk state with operands on stack
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Clear all operands on the current walk state operand stack.
+ *
+ ******************************************************************************/
+
+void acpi_ds_clear_operands(struct acpi_walk_state *walk_state)
+{
+ u32 i;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_clear_operands, walk_state);
+
+ /* Remove a reference on each operand on the stack */
+
+ for (i = 0; i < walk_state->num_operands; i++) {
+ /*
+ * Remove a reference to all operands, including both
+ * "Arguments" and "Targets".
+ */
+ acpi_ut_remove_reference(walk_state->operands[i]);
+ walk_state->operands[i] = NULL;
+ }
+
+ walk_state->num_operands = 0;
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_create_operand
+ *
+ * PARAMETERS: walk_state - Current walk state
+ * arg - Parse object for the argument
+ * arg_index - Which argument (zero based)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Translate a parse tree object that is an argument to an AML
+ * opcode to the equivalent interpreter object. This may include
+ * looking up a name or entering a new name into the internal
+ * namespace.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_create_operand(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *arg, u32 arg_index)
+{
+ acpi_status status = AE_OK;
+ char *name_string;
+ u32 name_length;
+ union acpi_operand_object *obj_desc;
+ union acpi_parse_object *parent_op;
+ u16 opcode;
+ acpi_interpreter_mode interpreter_mode;
+ const struct acpi_opcode_info *op_info;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_create_operand, arg);
+
+ /* A valid name must be looked up in the namespace */
+
+ if ((arg->common.aml_opcode == AML_INT_NAMEPATH_OP) &&
+ (arg->common.value.string) &&
+ !(arg->common.flags & ACPI_PARSEOP_IN_STACK)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Getting a name: Arg=%p\n",
+ arg));
+
+ /* Get the entire name string from the AML stream */
+
+ status = acpi_ex_get_name_string(ACPI_TYPE_ANY,
+ arg->common.value.buffer,
+ &name_string, &name_length);
+
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* All prefixes have been handled, and the name is in name_string */
+
+ /*
+ * Special handling for buffer_field declarations. This is a deferred
+ * opcode that unfortunately defines the field name as the last
+ * parameter instead of the first. We get here when we are performing
+ * the deferred execution, so the actual name of the field is already
+ * in the namespace. We don't want to attempt to look it up again
+ * because we may be executing in a different scope than where the
+ * actual opcode exists.
+ */
+ if ((walk_state->deferred_node) &&
+ (walk_state->deferred_node->type == ACPI_TYPE_BUFFER_FIELD)
+ && (arg_index == (u32)
+ ((walk_state->opcode == AML_CREATE_FIELD_OP) ? 3 : 2))) {
+ obj_desc =
+ ACPI_CAST_PTR(union acpi_operand_object,
+ walk_state->deferred_node);
+ status = AE_OK;
+ } else { /* All other opcodes */
+
+ /*
+ * Differentiate between a namespace "create" operation
+ * versus a "lookup" operation (IMODE_LOAD_PASS2 vs.
+ * IMODE_EXECUTE) in order to support the creation of
+ * namespace objects during the execution of control methods.
+ */
+ parent_op = arg->common.parent;
+ op_info =
+ acpi_ps_get_opcode_info(parent_op->common.
+ aml_opcode);
+
+ if ((op_info->flags & AML_NSNODE) &&
+ (parent_op->common.aml_opcode !=
+ AML_INT_METHODCALL_OP)
+ && (parent_op->common.aml_opcode != AML_REGION_OP)
+ && (parent_op->common.aml_opcode !=
+ AML_INT_NAMEPATH_OP)) {
+
+ /* Enter name into namespace if not found */
+
+ interpreter_mode = ACPI_IMODE_LOAD_PASS2;
+ } else {
+ /* Return a failure if name not found */
+
+ interpreter_mode = ACPI_IMODE_EXECUTE;
+ }
+
+ status =
+ acpi_ns_lookup(walk_state->scope_info, name_string,
+ ACPI_TYPE_ANY, interpreter_mode,
+ ACPI_NS_SEARCH_PARENT |
+ ACPI_NS_DONT_OPEN_SCOPE, walk_state,
+ ACPI_CAST_INDIRECT_PTR(struct
+ acpi_namespace_node,
+ &obj_desc));
+ /*
+ * The only case where we pass through (ignore) a NOT_FOUND
+ * error is for the cond_ref_of opcode.
+ */
+ if (status == AE_NOT_FOUND) {
+ if (parent_op->common.aml_opcode ==
+ AML_CONDITIONAL_REF_OF_OP) {
+ /*
+ * For the Conditional Reference op, it's OK if
+ * the name is not found; We just need a way to
+ * indicate this to the interpreter, set the
+ * object to the root
+ */
+ obj_desc =
+ ACPI_CAST_PTR(union
+ acpi_operand_object,
+ acpi_gbl_root_node);
+ status = AE_OK;
+ } else if (parent_op->common.aml_opcode ==
+ AML_EXTERNAL_OP) {
+ /*
+ * This opcode should never appear here. It is used only
+ * by AML disassemblers and is surrounded by an If(0)
+ * by the ASL compiler.
+ *
+ * Therefore, if we see it here, it is a serious error.
+ */
+ status = AE_AML_BAD_OPCODE;
+ } else {
+ /*
+ * We just plain didn't find it -- which is a
+ * very serious error at this point
+ */
+ status = AE_AML_NAME_NOT_FOUND;
+ }
+ }
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ name_string, status);
+ }
+ }
+
+ /* Free the namestring created above */
+
+ ACPI_FREE(name_string);
+
+ /* Check status from the lookup */
+
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Put the resulting object onto the current object stack */
+
+ status = acpi_ds_obj_stack_push(obj_desc, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_db_display_argument_object(obj_desc, walk_state);
+ } else {
+ /* Check for null name case */
+
+ if ((arg->common.aml_opcode == AML_INT_NAMEPATH_OP) &&
+ !(arg->common.flags & ACPI_PARSEOP_IN_STACK)) {
+ /*
+ * If the name is null, this means that this is an
+ * optional result parameter that was not specified
+ * in the original ASL. Create a Zero Constant for a
+ * placeholder. (Store to a constant is a Noop.)
+ */
+ opcode = AML_ZERO_OP; /* Has no arguments! */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Null namepath: Arg=%p\n", arg));
+ } else {
+ opcode = arg->common.aml_opcode;
+ }
+
+ /* Get the object type of the argument */
+
+ op_info = acpi_ps_get_opcode_info(opcode);
+ if (op_info->object_type == ACPI_TYPE_INVALID) {
+ return_ACPI_STATUS(AE_NOT_IMPLEMENTED);
+ }
+
+ if ((op_info->flags & AML_HAS_RETVAL) ||
+ (arg->common.flags & ACPI_PARSEOP_IN_STACK)) {
+ /*
+ * Use value that was already previously returned
+ * by the evaluation of this argument
+ */
+ status = acpi_ds_result_pop(&obj_desc, walk_state);
+ if (ACPI_FAILURE(status)) {
+ /*
+ * Only error is underflow, and this indicates
+ * a missing or null operand!
+ */
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Missing or null operand"));
+ return_ACPI_STATUS(status);
+ }
+ } else {
+ /* Create an ACPI_INTERNAL_OBJECT for the argument */
+
+ obj_desc =
+ acpi_ut_create_internal_object(op_info->
+ object_type);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Initialize the new object */
+
+ status =
+ acpi_ds_init_object_from_op(walk_state, arg, opcode,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_delete_object_desc(obj_desc);
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* Put the operand object on the object stack */
+
+ status = acpi_ds_obj_stack_push(obj_desc, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_db_display_argument_object(obj_desc, walk_state);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_create_operands
+ *
+ * PARAMETERS: walk_state - Current state
+ * first_arg - First argument of a parser argument tree
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert an operator's arguments from a parse tree format to
+ * namespace objects and place those argument object on the object
+ * stack in preparation for evaluation by the interpreter.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_create_operands(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *first_arg)
+{
+ acpi_status status = AE_OK;
+ union acpi_parse_object *arg;
+ union acpi_parse_object *arguments[ACPI_OBJ_NUM_OPERANDS];
+ u32 arg_count = 0;
+ u32 index = walk_state->num_operands;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_create_operands, first_arg);
+
+ /* Get all arguments in the list */
+
+ arg = first_arg;
+ while (arg) {
+ if (index >= ACPI_OBJ_NUM_OPERANDS) {
+ return_ACPI_STATUS(AE_BAD_DATA);
+ }
+
+ arguments[index] = arg;
+ walk_state->operands[index] = NULL;
+
+ /* Move on to next argument, if any */
+
+ arg = arg->common.next;
+ arg_count++;
+ index++;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "NumOperands %d, ArgCount %d, Index %d\n",
+ walk_state->num_operands, arg_count, index));
+
+ /* Create the interpreter arguments, in reverse order */
+
+ index--;
+ for (i = 0; i < arg_count; i++) {
+ arg = arguments[index];
+ walk_state->operand_index = (u8)index;
+
+ status = acpi_ds_create_operand(walk_state, arg, index);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Created Arg #%u (%p) %u args total\n",
+ index, arg, arg_count));
+ index--;
+ }
+
+ return_ACPI_STATUS(status);
+
+cleanup:
+ /*
+ * We must undo everything done above; meaning that we must
+ * pop everything off of the operand stack and delete those
+ * objects
+ */
+ acpi_ds_obj_stack_pop_and_delete(arg_count, walk_state);
+
+ ACPI_EXCEPTION((AE_INFO, status, "While creating Arg %u", index));
+ return_ACPI_STATUS(status);
+}
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_ds_evaluate_name_path
+ *
+ * PARAMETERS: walk_state - Current state of the parse tree walk,
+ * the opcode of current operation should be
+ * AML_INT_NAMEPATH_OP
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Translate the -name_path- parse tree object to the equivalent
+ * interpreter object, convert it to value, if needed, duplicate
+ * it, if needed, and push it onto the current result stack.
+ *
+ ****************************************************************************/
+
+acpi_status acpi_ds_evaluate_name_path(struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+ union acpi_parse_object *op = walk_state->op;
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ union acpi_operand_object *new_obj_desc;
+ u8 type;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_evaluate_name_path, walk_state);
+
+ if (!op->common.parent) {
+
+ /* This happens after certain exception processing */
+
+ goto exit;
+ }
+
+ if ((op->common.parent->common.aml_opcode == AML_PACKAGE_OP) ||
+ (op->common.parent->common.aml_opcode == AML_VARIABLE_PACKAGE_OP) ||
+ (op->common.parent->common.aml_opcode == AML_REF_OF_OP)) {
+
+ /* TBD: Should we specify this feature as a bit of op_info->Flags of these opcodes? */
+
+ goto exit;
+ }
+
+ status = acpi_ds_create_operand(walk_state, op, 0);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ if (op->common.flags & ACPI_PARSEOP_TARGET) {
+ new_obj_desc = *operand;
+ goto push_result;
+ }
+
+ type = (*operand)->common.type;
+
+ status = acpi_ex_resolve_to_value(operand, walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ if (type == ACPI_TYPE_INTEGER) {
+
+ /* It was incremented by acpi_ex_resolve_to_value */
+
+ acpi_ut_remove_reference(*operand);
+
+ status =
+ acpi_ut_copy_iobject_to_iobject(*operand, &new_obj_desc,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+ } else {
+ /*
+ * The object either was anew created or is
+ * a Namespace node - don't decrement it.
+ */
+ new_obj_desc = *operand;
+ }
+
+ /* Cleanup for name-path operand */
+
+ status = acpi_ds_obj_stack_pop(1, walk_state);
+ if (ACPI_FAILURE(status)) {
+ walk_state->result_obj = new_obj_desc;
+ goto exit;
+ }
+
+push_result:
+
+ walk_state->result_obj = new_obj_desc;
+
+ status = acpi_ds_result_push(walk_state->result_obj, walk_state);
+ if (ACPI_SUCCESS(status)) {
+
+ /* Force to take it from stack */
+
+ op->common.flags |= ACPI_PARSEOP_IN_STACK;
+ }
+
+exit:
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c
new file mode 100644
index 000000000..e8ad41387
--- /dev/null
+++ b/drivers/acpi/acpica/dswexec.c
@@ -0,0 +1,758 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dswexec - Dispatcher method execution callbacks;
+ * dispatch to interpreter.
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+#include "acdebug.h"
+#ifdef ACPI_EXEC_APP
+#include "aecommon.h"
+#endif
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dswexec")
+
+/*
+ * Dispatch table for opcode classes
+ */
+static acpi_execute_op acpi_gbl_op_type_dispatch[] = {
+ acpi_ex_opcode_0A_0T_1R,
+ acpi_ex_opcode_1A_0T_0R,
+ acpi_ex_opcode_1A_0T_1R,
+ NULL, /* Was: acpi_ex_opcode_1A_0T_0R (Was for Load operator) */
+ acpi_ex_opcode_1A_1T_1R,
+ acpi_ex_opcode_2A_0T_0R,
+ acpi_ex_opcode_2A_0T_1R,
+ acpi_ex_opcode_2A_1T_1R,
+ acpi_ex_opcode_2A_2T_1R,
+ acpi_ex_opcode_3A_0T_0R,
+ acpi_ex_opcode_3A_1T_1R,
+ acpi_ex_opcode_6A_0T_1R
+};
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_ds_get_predicate_value
+ *
+ * PARAMETERS: walk_state - Current state of the parse tree walk
+ * result_obj - if non-zero, pop result from result stack
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get the result of a predicate evaluation
+ *
+ ****************************************************************************/
+
+acpi_status
+acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state,
+ union acpi_operand_object *result_obj)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *local_obj_desc = NULL;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_get_predicate_value, walk_state);
+
+ walk_state->control_state->common.state = 0;
+
+ if (result_obj) {
+ status = acpi_ds_result_pop(&obj_desc, walk_state);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not get result from predicate evaluation"));
+
+ return_ACPI_STATUS(status);
+ }
+ } else {
+ status = acpi_ds_create_operand(walk_state, walk_state->op, 0);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status =
+ acpi_ex_resolve_to_value(&walk_state->operands[0],
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ obj_desc = walk_state->operands[0];
+ }
+
+ if (!obj_desc) {
+ ACPI_ERROR((AE_INFO,
+ "No predicate ObjDesc=%p State=%p",
+ obj_desc, walk_state));
+
+ return_ACPI_STATUS(AE_AML_NO_OPERAND);
+ }
+
+ /*
+ * Result of predicate evaluation must be an Integer
+ * object. Implicitly convert the argument if necessary.
+ */
+ status = acpi_ex_convert_to_integer(obj_desc, &local_obj_desc,
+ ACPI_IMPLICIT_CONVERSION);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ if (local_obj_desc->common.type != ACPI_TYPE_INTEGER) {
+ ACPI_ERROR((AE_INFO,
+ "Bad predicate (not an integer) ObjDesc=%p State=%p Type=0x%X",
+ obj_desc, walk_state, obj_desc->common.type));
+
+ status = AE_AML_OPERAND_TYPE;
+ goto cleanup;
+ }
+
+ /* Truncate the predicate to 32-bits if necessary */
+
+ (void)acpi_ex_truncate_for32bit_table(local_obj_desc);
+
+ /*
+ * Save the result of the predicate evaluation on
+ * the control stack
+ */
+ if (local_obj_desc->integer.value) {
+ walk_state->control_state->common.value = TRUE;
+ } else {
+ /*
+ * Predicate is FALSE, we will just toss the
+ * rest of the package
+ */
+ walk_state->control_state->common.value = FALSE;
+ status = AE_CTRL_FALSE;
+ }
+
+ /* Predicate can be used for an implicit return value */
+
+ (void)acpi_ds_do_implicit_return(local_obj_desc, walk_state, TRUE);
+
+cleanup:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Completed a predicate eval=%X Op=%p\n",
+ walk_state->control_state->common.value,
+ walk_state->op));
+
+ /* Break to debugger to display result */
+
+ acpi_db_display_result_object(local_obj_desc, walk_state);
+
+ /*
+ * Delete the predicate result object (we know that
+ * we don't need it anymore)
+ */
+ if (local_obj_desc != obj_desc) {
+ acpi_ut_remove_reference(local_obj_desc);
+ }
+ acpi_ut_remove_reference(obj_desc);
+
+ walk_state->control_state->common.state = ACPI_CONTROL_NORMAL;
+ return_ACPI_STATUS(status);
+}
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_ds_exec_begin_op
+ *
+ * PARAMETERS: walk_state - Current state of the parse tree walk
+ * out_op - Where to return op if a new one is created
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Descending callback used during the execution of control
+ * methods. This is where most operators and operands are
+ * dispatched to the interpreter.
+ *
+ ****************************************************************************/
+
+acpi_status
+acpi_ds_exec_begin_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object **out_op)
+{
+ union acpi_parse_object *op;
+ acpi_status status = AE_OK;
+ u32 opcode_class;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_exec_begin_op, walk_state);
+
+ op = walk_state->op;
+ if (!op) {
+ status = acpi_ds_load2_begin_op(walk_state, out_op);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ op = *out_op;
+ walk_state->op = op;
+ walk_state->opcode = op->common.aml_opcode;
+ walk_state->op_info =
+ acpi_ps_get_opcode_info(op->common.aml_opcode);
+
+ if (acpi_ns_opens_scope(walk_state->op_info->object_type)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "(%s) Popping scope for Op %p\n",
+ acpi_ut_get_type_name(walk_state->
+ op_info->
+ object_type),
+ op));
+
+ status = acpi_ds_scope_stack_pop(walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+ }
+ }
+
+ if (op == walk_state->origin) {
+ if (out_op) {
+ *out_op = op;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * If the previous opcode was a conditional, this opcode
+ * must be the beginning of the associated predicate.
+ * Save this knowledge in the current scope descriptor
+ */
+ if ((walk_state->control_state) &&
+ (walk_state->control_state->common.state ==
+ ACPI_CONTROL_CONDITIONAL_EXECUTING)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Exec predicate Op=%p State=%p\n",
+ op, walk_state));
+
+ walk_state->control_state->common.state =
+ ACPI_CONTROL_PREDICATE_EXECUTING;
+
+ /* Save start of predicate */
+
+ walk_state->control_state->control.predicate_op = op;
+ }
+
+ opcode_class = walk_state->op_info->class;
+
+ /* We want to send namepaths to the load code */
+
+ if (op->common.aml_opcode == AML_INT_NAMEPATH_OP) {
+ opcode_class = AML_CLASS_NAMED_OBJECT;
+ }
+
+ /*
+ * Handle the opcode based upon the opcode type
+ */
+ switch (opcode_class) {
+ case AML_CLASS_CONTROL:
+
+ status = acpi_ds_exec_begin_control_op(walk_state, op);
+ break;
+
+ case AML_CLASS_NAMED_OBJECT:
+
+ if (walk_state->walk_type & ACPI_WALK_METHOD) {
+ /*
+ * Found a named object declaration during method execution;
+ * we must enter this object into the namespace. The created
+ * object is temporary and will be deleted upon completion of
+ * the execution of this method.
+ *
+ * Note 10/2010: Except for the Scope() op. This opcode does
+ * not actually create a new object, it refers to an existing
+ * object. However, for Scope(), we want to indeed open a
+ * new scope.
+ */
+ if (op->common.aml_opcode != AML_SCOPE_OP) {
+ status =
+ acpi_ds_load2_begin_op(walk_state, NULL);
+ } else {
+ status =
+ acpi_ds_scope_stack_push(op->named.node,
+ op->named.node->
+ type, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+ }
+ break;
+
+ case AML_CLASS_EXECUTE:
+ case AML_CLASS_CREATE:
+
+ break;
+
+ default:
+
+ break;
+ }
+
+ /* Nothing to do here during method execution */
+
+ return_ACPI_STATUS(status);
+
+error_exit:
+ status = acpi_ds_method_error(status, walk_state);
+ return_ACPI_STATUS(status);
+}
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_ds_exec_end_op
+ *
+ * PARAMETERS: walk_state - Current state of the parse tree walk
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Ascending callback used during the execution of control
+ * methods. The only thing we really need to do here is to
+ * notice the beginning of IF, ELSE, and WHILE blocks.
+ *
+ ****************************************************************************/
+
+acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)
+{
+ union acpi_parse_object *op;
+ acpi_status status = AE_OK;
+ u32 op_type;
+ u32 op_class;
+ union acpi_parse_object *next_op;
+ union acpi_parse_object *first_arg;
+#ifdef ACPI_EXEC_APP
+ char *namepath;
+ union acpi_operand_object *obj_desc;
+#endif
+
+ ACPI_FUNCTION_TRACE_PTR(ds_exec_end_op, walk_state);
+
+ op = walk_state->op;
+ op_type = walk_state->op_info->type;
+ op_class = walk_state->op_info->class;
+
+ if (op_class == AML_CLASS_UNKNOWN) {
+ ACPI_ERROR((AE_INFO, "Unknown opcode 0x%X",
+ op->common.aml_opcode));
+ return_ACPI_STATUS(AE_NOT_IMPLEMENTED);
+ }
+
+ first_arg = op->common.value.arg;
+
+ /* Init the walk state */
+
+ walk_state->num_operands = 0;
+ walk_state->operand_index = 0;
+ walk_state->return_desc = NULL;
+ walk_state->result_obj = NULL;
+
+ /* Call debugger for single step support (DEBUG build only) */
+
+ status = acpi_db_single_step(walk_state, op, op_class);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Decode the Opcode Class */
+
+ switch (op_class) {
+ case AML_CLASS_ARGUMENT: /* Constants, literals, etc. */
+
+ if (walk_state->opcode == AML_INT_NAMEPATH_OP) {
+ status = acpi_ds_evaluate_name_path(walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+ }
+ break;
+
+ case AML_CLASS_EXECUTE: /* Most operators with arguments */
+
+ /* Build resolved operand stack */
+
+ status = acpi_ds_create_operands(walk_state, first_arg);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /*
+ * All opcodes require operand resolution, with the only exceptions
+ * being the object_type and size_of operators.
+ */
+ if (!(walk_state->op_info->flags & AML_NO_OPERAND_RESOLVE)) {
+
+ /* Resolve all operands */
+
+ status = acpi_ex_resolve_operands(walk_state->opcode,
+ &(walk_state->
+ operands
+ [walk_state->
+ num_operands - 1]),
+ walk_state);
+ }
+
+ if (ACPI_SUCCESS(status)) {
+ /*
+ * Dispatch the request to the appropriate interpreter handler
+ * routine. There is one routine per opcode "type" based upon the
+ * number of opcode arguments and return type.
+ */
+ status =
+ acpi_gbl_op_type_dispatch[op_type] (walk_state);
+ } else {
+ /*
+ * Treat constructs of the form "Store(LocalX,LocalX)" as noops when the
+ * Local is uninitialized.
+ */
+ if ((status == AE_AML_UNINITIALIZED_LOCAL) &&
+ (walk_state->opcode == AML_STORE_OP) &&
+ (walk_state->operands[0]->common.type ==
+ ACPI_TYPE_LOCAL_REFERENCE)
+ && (walk_state->operands[1]->common.type ==
+ ACPI_TYPE_LOCAL_REFERENCE)
+ && (walk_state->operands[0]->reference.class ==
+ walk_state->operands[1]->reference.class)
+ && (walk_state->operands[0]->reference.value ==
+ walk_state->operands[1]->reference.value)) {
+ status = AE_OK;
+ } else {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While resolving operands for [%s]",
+ acpi_ps_get_opcode_name
+ (walk_state->opcode)));
+ }
+ }
+
+ /* Always delete the argument objects and clear the operand stack */
+
+ acpi_ds_clear_operands(walk_state);
+
+ /*
+ * If a result object was returned from above, push it on the
+ * current result stack
+ */
+ if (ACPI_SUCCESS(status) && walk_state->result_obj) {
+ status =
+ acpi_ds_result_push(walk_state->result_obj,
+ walk_state);
+ }
+ break;
+
+ default:
+
+ switch (op_type) {
+ case AML_TYPE_CONTROL: /* Type 1 opcode, IF/ELSE/WHILE/NOOP */
+
+ /* 1 Operand, 0 external_result, 0 internal_result */
+
+ status = acpi_ds_exec_end_control_op(walk_state, op);
+
+ break;
+
+ case AML_TYPE_METHOD_CALL:
+ /*
+ * If the method is referenced from within a package
+ * declaration, it is not a invocation of the method, just
+ * a reference to it.
+ */
+ if ((op->asl.parent) &&
+ ((op->asl.parent->asl.aml_opcode == AML_PACKAGE_OP)
+ || (op->asl.parent->asl.aml_opcode ==
+ AML_VARIABLE_PACKAGE_OP))) {
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Method Reference in a Package, Op=%p\n",
+ op));
+
+ op->common.node = (struct acpi_namespace_node *)
+ op->asl.value.arg->asl.node;
+ acpi_ut_add_reference(op->asl.value.arg->asl.
+ node->object);
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Method invocation, Op=%p\n", op));
+
+ /*
+ * (AML_METHODCALL) Op->Asl.Value.Arg->Asl.Node contains
+ * the method Node pointer
+ */
+ /* next_op points to the op that holds the method name */
+
+ next_op = first_arg;
+
+ /* next_op points to first argument op */
+
+ next_op = next_op->common.next;
+
+ /*
+ * Get the method's arguments and put them on the operand stack
+ */
+ status = acpi_ds_create_operands(walk_state, next_op);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+
+ /*
+ * Since the operands will be passed to another control method,
+ * we must resolve all local references here (Local variables,
+ * arguments to *this* method, etc.)
+ */
+ status = acpi_ds_resolve_operands(walk_state);
+ if (ACPI_FAILURE(status)) {
+
+ /* On error, clear all resolved operands */
+
+ acpi_ds_clear_operands(walk_state);
+ break;
+ }
+
+ /*
+ * Tell the walk loop to preempt this running method and
+ * execute the new method
+ */
+ status = AE_CTRL_TRANSFER;
+
+ /*
+ * Return now; we don't want to disturb anything,
+ * especially the operand count!
+ */
+ return_ACPI_STATUS(status);
+
+ case AML_TYPE_CREATE_FIELD:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Executing CreateField Buffer/Index Op=%p\n",
+ op));
+
+ status = acpi_ds_load2_end_op(walk_state);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+
+ status =
+ acpi_ds_eval_buffer_field_operands(walk_state, op);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+#ifdef ACPI_EXEC_APP
+ /*
+ * acpi_exec support for namespace initialization file (initialize
+ * buffer_fields in this code.)
+ */
+ namepath =
+ acpi_ns_get_external_pathname(op->common.node);
+ status = ae_lookup_init_file_entry(namepath, &obj_desc);
+ if (ACPI_SUCCESS(status)) {
+ status =
+ acpi_ex_write_data_to_field(obj_desc,
+ op->common.
+ node->object,
+ NULL);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While writing to buffer field"));
+ }
+ }
+ ACPI_FREE(namepath);
+ status = AE_OK;
+#endif
+ break;
+
+ case AML_TYPE_CREATE_OBJECT:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Executing CreateObject (Buffer/Package) Op=%p Child=%p ParentOpcode=%4.4X\n",
+ op, op->named.value.arg,
+ op->common.parent->common.
+ aml_opcode));
+
+ switch (op->common.parent->common.aml_opcode) {
+ case AML_NAME_OP:
+ /*
+ * Put the Node on the object stack (Contains the ACPI Name
+ * of this object)
+ */
+ walk_state->operands[0] = (void *)
+ op->common.parent->common.node;
+ walk_state->num_operands = 1;
+
+ status = acpi_ds_create_node(walk_state,
+ op->common.parent->
+ common.node,
+ op->common.parent);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+
+ ACPI_FALLTHROUGH;
+
+ case AML_INT_EVAL_SUBTREE_OP:
+
+ status =
+ acpi_ds_eval_data_object_operands
+ (walk_state, op,
+ acpi_ns_get_attached_object(op->common.
+ parent->common.
+ node));
+ break;
+
+ default:
+
+ status =
+ acpi_ds_eval_data_object_operands
+ (walk_state, op, NULL);
+ break;
+ }
+
+ /*
+ * If a result object was returned from above, push it on the
+ * current result stack
+ */
+ if (walk_state->result_obj) {
+ status =
+ acpi_ds_result_push(walk_state->result_obj,
+ walk_state);
+ }
+ break;
+
+ case AML_TYPE_NAMED_FIELD:
+ case AML_TYPE_NAMED_COMPLEX:
+ case AML_TYPE_NAMED_SIMPLE:
+ case AML_TYPE_NAMED_NO_OBJ:
+
+ status = acpi_ds_load2_end_op(walk_state);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+
+ if (op->common.aml_opcode == AML_REGION_OP) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Executing OpRegion Address/Length Op=%p\n",
+ op));
+
+ status =
+ acpi_ds_eval_region_operands(walk_state,
+ op);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+ } else if (op->common.aml_opcode == AML_DATA_REGION_OP) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Executing DataTableRegion Strings Op=%p\n",
+ op));
+
+ status =
+ acpi_ds_eval_table_region_operands
+ (walk_state, op);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+ } else if (op->common.aml_opcode == AML_BANK_FIELD_OP) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Executing BankField Op=%p\n",
+ op));
+
+ status =
+ acpi_ds_eval_bank_field_operands(walk_state,
+ op);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+ }
+ break;
+
+ case AML_TYPE_UNDEFINED:
+
+ ACPI_ERROR((AE_INFO,
+ "Undefined opcode type Op=%p", op));
+ return_ACPI_STATUS(AE_NOT_IMPLEMENTED);
+
+ case AML_TYPE_BOGUS:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Internal opcode=%X type Op=%p\n",
+ walk_state->opcode, op));
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unimplemented opcode, class=0x%X "
+ "type=0x%X Opcode=0x%X Op=%p",
+ op_class, op_type, op->common.aml_opcode,
+ op));
+
+ status = AE_NOT_IMPLEMENTED;
+ break;
+ }
+ }
+
+ /*
+ * ACPI 2.0 support for 64-bit integers: Truncate numeric
+ * result value if we are executing from a 32-bit ACPI table
+ */
+ (void)acpi_ex_truncate_for32bit_table(walk_state->result_obj);
+
+ /*
+ * Check if we just completed the evaluation of a
+ * conditional predicate
+ */
+ if ((ACPI_SUCCESS(status)) &&
+ (walk_state->control_state) &&
+ (walk_state->control_state->common.state ==
+ ACPI_CONTROL_PREDICATE_EXECUTING) &&
+ (walk_state->control_state->control.predicate_op == op)) {
+ status =
+ acpi_ds_get_predicate_value(walk_state,
+ walk_state->result_obj);
+ walk_state->result_obj = NULL;
+ }
+
+cleanup:
+
+ if (walk_state->result_obj) {
+
+ /* Break to debugger to display result */
+
+ acpi_db_display_result_object(walk_state->result_obj,
+ walk_state);
+
+ /*
+ * Delete the result op if and only if:
+ * Parent will not use the result -- such as any
+ * non-nested type2 op in a method (parent will be method)
+ */
+ acpi_ds_delete_result_if_not_used(op, walk_state->result_obj,
+ walk_state);
+ }
+#ifdef _UNDER_DEVELOPMENT
+
+ if (walk_state->parser_state.aml == walk_state->parser_state.aml_end) {
+ acpi_db_method_end(walk_state);
+ }
+#endif
+
+ /* Invoke exception handler on error */
+
+ if (ACPI_FAILURE(status)) {
+ status = acpi_ds_method_error(status, walk_state);
+ }
+
+ /* Always clear the object stack */
+
+ walk_state->num_operands = 0;
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c
new file mode 100644
index 000000000..9f6573646
--- /dev/null
+++ b/drivers/acpi/acpica/dswload.c
@@ -0,0 +1,596 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dswload - Dispatcher first pass namespace load callbacks
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+#ifdef ACPI_ASL_COMPILER
+#include "acdisasm.h"
+#endif
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dswload")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_init_callbacks
+ *
+ * PARAMETERS: walk_state - Current state of the parse tree walk
+ * pass_number - 1, 2, or 3
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Init walk state callbacks
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ds_init_callbacks(struct acpi_walk_state *walk_state, u32 pass_number)
+{
+
+ switch (pass_number) {
+ case 0:
+
+ /* Parse only - caller will setup callbacks */
+
+ walk_state->parse_flags = ACPI_PARSE_LOAD_PASS1 |
+ ACPI_PARSE_DELETE_TREE | ACPI_PARSE_DISASSEMBLE;
+ walk_state->descending_callback = NULL;
+ walk_state->ascending_callback = NULL;
+ break;
+
+ case 1:
+
+ /* Load pass 1 */
+
+ walk_state->parse_flags = ACPI_PARSE_LOAD_PASS1 |
+ ACPI_PARSE_DELETE_TREE;
+ walk_state->descending_callback = acpi_ds_load1_begin_op;
+ walk_state->ascending_callback = acpi_ds_load1_end_op;
+ break;
+
+ case 2:
+
+ /* Load pass 2 */
+
+ walk_state->parse_flags = ACPI_PARSE_LOAD_PASS1 |
+ ACPI_PARSE_DELETE_TREE;
+ walk_state->descending_callback = acpi_ds_load2_begin_op;
+ walk_state->ascending_callback = acpi_ds_load2_end_op;
+ break;
+
+ case 3:
+
+ /* Execution pass */
+
+ walk_state->parse_flags |= ACPI_PARSE_EXECUTE |
+ ACPI_PARSE_DELETE_TREE;
+ walk_state->descending_callback = acpi_ds_exec_begin_op;
+ walk_state->ascending_callback = acpi_ds_exec_end_op;
+ break;
+
+ default:
+
+ return (AE_BAD_PARAMETER);
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_load1_begin_op
+ *
+ * PARAMETERS: walk_state - Current state of the parse tree walk
+ * out_op - Where to return op if a new one is created
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Descending callback used during the loading of ACPI tables.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_load1_begin_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object **out_op)
+{
+ union acpi_parse_object *op;
+ struct acpi_namespace_node *node;
+ acpi_status status;
+ acpi_object_type object_type;
+ char *path;
+ u32 flags;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_load1_begin_op, walk_state->op);
+
+ op = walk_state->op;
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op,
+ walk_state));
+
+ /* We are only interested in opcodes that have an associated name */
+
+ if (op) {
+ if (!(walk_state->op_info->flags & AML_NAMED)) {
+ *out_op = op;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Check if this object has already been installed in the namespace */
+
+ if (op->common.node) {
+ *out_op = op;
+ return_ACPI_STATUS(AE_OK);
+ }
+ }
+
+ path = acpi_ps_get_next_namestring(&walk_state->parser_state);
+
+ /* Map the raw opcode into an internal object type */
+
+ object_type = walk_state->op_info->object_type;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "State=%p Op=%p [%s]\n", walk_state, op,
+ acpi_ut_get_type_name(object_type)));
+
+ switch (walk_state->opcode) {
+ case AML_SCOPE_OP:
+ /*
+ * The target name of the Scope() operator must exist at this point so
+ * that we can actually open the scope to enter new names underneath it.
+ * Allow search-to-root for single namesegs.
+ */
+ status =
+ acpi_ns_lookup(walk_state->scope_info, path, object_type,
+ ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT,
+ walk_state, &(node));
+#ifdef ACPI_ASL_COMPILER
+ if (status == AE_NOT_FOUND) {
+ /*
+ * Table disassembly:
+ * Target of Scope() not found. Generate an External for it, and
+ * insert the name into the namespace.
+ */
+ acpi_dm_add_op_to_external_list(op, path,
+ ACPI_TYPE_DEVICE, 0, 0);
+ status =
+ acpi_ns_lookup(walk_state->scope_info, path,
+ object_type, ACPI_IMODE_LOAD_PASS1,
+ ACPI_NS_SEARCH_PARENT, walk_state,
+ &node);
+ }
+#endif
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info, path,
+ status);
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Check to make sure that the target is
+ * one of the opcodes that actually opens a scope
+ */
+ switch (node->type) {
+ case ACPI_TYPE_ANY:
+ case ACPI_TYPE_LOCAL_SCOPE: /* Scope */
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_POWER:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+
+ /* These are acceptable types */
+ break;
+
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+ /*
+ * These types we will allow, but we will change the type.
+ * This enables some existing code of the form:
+ *
+ * Name (DEB, 0)
+ * Scope (DEB) { ... }
+ *
+ * Note: silently change the type here. On the second pass,
+ * we will report a warning
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Type override - [%4.4s] had invalid type (%s) "
+ "for Scope operator, changed to type ANY\n",
+ acpi_ut_get_node_name(node),
+ acpi_ut_get_type_name(node->type)));
+
+ node->type = ACPI_TYPE_ANY;
+ walk_state->scope_info->common.value = ACPI_TYPE_ANY;
+ break;
+
+ case ACPI_TYPE_METHOD:
+ /*
+ * Allow scope change to root during execution of module-level
+ * code. Root is typed METHOD during this time.
+ */
+ if ((node == acpi_gbl_root_node) &&
+ (walk_state->
+ parse_flags & ACPI_PARSE_MODULE_LEVEL)) {
+ break;
+ }
+
+ ACPI_FALLTHROUGH;
+
+ default:
+
+ /* All other types are an error */
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid type (%s) for target of "
+ "Scope operator [%4.4s] (Cannot override)",
+ acpi_ut_get_type_name(node->type),
+ acpi_ut_get_node_name(node)));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ break;
+
+ default:
+ /*
+ * For all other named opcodes, we will enter the name into
+ * the namespace.
+ *
+ * Setup the search flags.
+ * Since we are entering a name into the namespace, we do not want to
+ * enable the search-to-root upsearch.
+ *
+ * There are only two conditions where it is acceptable that the name
+ * already exists:
+ * 1) the Scope() operator can reopen a scoping object that was
+ * previously defined (Scope, Method, Device, etc.)
+ * 2) Whenever we are parsing a deferred opcode (op_region, Buffer,
+ * buffer_field, or Package), the name of the object is already
+ * in the namespace.
+ */
+ if (walk_state->deferred_node) {
+
+ /* This name is already in the namespace, get the node */
+
+ node = walk_state->deferred_node;
+ status = AE_OK;
+ break;
+ }
+
+ /*
+ * If we are executing a method, do not create any namespace objects
+ * during the load phase, only during execution.
+ */
+ if (walk_state->method_node) {
+ node = NULL;
+ status = AE_OK;
+ break;
+ }
+
+ flags = ACPI_NS_NO_UPSEARCH;
+ if ((walk_state->opcode != AML_SCOPE_OP) &&
+ (!(walk_state->parse_flags & ACPI_PARSE_DEFERRED_OP))) {
+ if (walk_state->namespace_override) {
+ flags |= ACPI_NS_OVERRIDE_IF_FOUND;
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "[%s] Override allowed\n",
+ acpi_ut_get_type_name
+ (object_type)));
+ } else {
+ flags |= ACPI_NS_ERROR_IF_FOUND;
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "[%s] Cannot already exist\n",
+ acpi_ut_get_type_name
+ (object_type)));
+ }
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "[%s] Both Find or Create allowed\n",
+ acpi_ut_get_type_name(object_type)));
+ }
+
+ /*
+ * Enter the named type into the internal namespace. We enter the name
+ * as we go downward in the parse tree. Any necessary subobjects that
+ * involve arguments to the opcode must be created as we go back up the
+ * parse tree later.
+ */
+ status =
+ acpi_ns_lookup(walk_state->scope_info, path, object_type,
+ ACPI_IMODE_LOAD_PASS1, flags, walk_state,
+ &node);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_ALREADY_EXISTS) {
+
+ /* The name already exists in this scope */
+
+ if (node->flags & ANOBJ_IS_EXTERNAL) {
+ /*
+ * Allow one create on an object or segment that was
+ * previously declared External
+ */
+ node->flags &= ~ANOBJ_IS_EXTERNAL;
+ node->type = (u8) object_type;
+
+ /* Just retyped a node, probably will need to open a scope */
+
+ if (acpi_ns_opens_scope(object_type)) {
+ status =
+ acpi_ds_scope_stack_push
+ (node, object_type,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS
+ (status);
+ }
+ }
+
+ status = AE_OK;
+ }
+ }
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ path, status);
+ return_ACPI_STATUS(status);
+ }
+ }
+ break;
+ }
+
+ /* Common exit */
+
+ if (!op) {
+
+ /* Create a new op */
+
+ op = acpi_ps_alloc_op(walk_state->opcode, walk_state->aml);
+ if (!op) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+ }
+
+ /* Initialize the op */
+
+#ifdef ACPI_CONSTANT_EVAL_ONLY
+ op->named.path = path;
+#endif
+
+ if (node) {
+ /*
+ * Put the Node in the "op" object that the parser uses, so we
+ * can get it again quickly when this scope is closed
+ */
+ op->common.node = node;
+ op->named.name = node->name.integer;
+ }
+
+ acpi_ps_append_arg(acpi_ps_get_parent_scope(&walk_state->parser_state),
+ op);
+ *out_op = op;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_load1_end_op
+ *
+ * PARAMETERS: walk_state - Current state of the parse tree walk
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Ascending callback used during the loading of the namespace,
+ * both control methods and everything else.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ds_load1_end_op(struct acpi_walk_state *walk_state)
+{
+ union acpi_parse_object *op;
+ acpi_object_type object_type;
+ acpi_status status = AE_OK;
+#ifdef ACPI_ASL_COMPILER
+ u8 param_count;
+#endif
+
+ ACPI_FUNCTION_TRACE(ds_load1_end_op);
+
+ op = walk_state->op;
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op,
+ walk_state));
+
+ /*
+ * Disassembler: handle create field operators here.
+ *
+ * create_buffer_field is a deferred op that is typically processed in load
+ * pass 2. However, disassembly of control method contents walk the parse
+ * tree with ACPI_PARSE_LOAD_PASS1 and AML_CREATE operators are processed
+ * in a later walk. This is a problem when there is a control method that
+ * has the same name as the AML_CREATE object. In this case, any use of the
+ * name segment will be detected as a method call rather than a reference
+ * to a buffer field.
+ *
+ * This earlier creation during disassembly solves this issue by inserting
+ * the named object in the ACPI namespace so that references to this name
+ * would be a name string rather than a method call.
+ */
+ if ((walk_state->parse_flags & ACPI_PARSE_DISASSEMBLE) &&
+ (walk_state->op_info->flags & AML_CREATE)) {
+ status = acpi_ds_create_buffer_field(op, walk_state);
+ return_ACPI_STATUS(status);
+ }
+
+ /* We are only interested in opcodes that have an associated name */
+
+ if (!(walk_state->op_info->flags & (AML_NAMED | AML_FIELD))) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Get the object type to determine if we should pop the scope */
+
+ object_type = walk_state->op_info->object_type;
+
+ if (walk_state->op_info->flags & AML_FIELD) {
+ /*
+ * If we are executing a method, do not create any namespace objects
+ * during the load phase, only during execution.
+ */
+ if (!walk_state->method_node) {
+ if (walk_state->opcode == AML_FIELD_OP ||
+ walk_state->opcode == AML_BANK_FIELD_OP ||
+ walk_state->opcode == AML_INDEX_FIELD_OP) {
+ status =
+ acpi_ds_init_field_objects(op, walk_state);
+ }
+ }
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * If we are executing a method, do not create any namespace objects
+ * during the load phase, only during execution.
+ */
+ if (!walk_state->method_node) {
+ if (op->common.aml_opcode == AML_REGION_OP) {
+ status =
+ acpi_ex_create_region(op->named.data,
+ op->named.length,
+ (acpi_adr_space_type)
+ ((op->common.value.arg)->
+ common.value.integer),
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ } else if (op->common.aml_opcode == AML_DATA_REGION_OP) {
+ status =
+ acpi_ex_create_region(op->named.data,
+ op->named.length,
+ ACPI_ADR_SPACE_DATA_TABLE,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+ }
+
+ if (op->common.aml_opcode == AML_NAME_OP) {
+
+ /* For Name opcode, get the object type from the argument */
+
+ if (op->common.value.arg) {
+ object_type = (acpi_ps_get_opcode_info((op->common.
+ value.arg)->
+ common.
+ aml_opcode))->
+ object_type;
+
+ /* Set node type if we have a namespace node */
+
+ if (op->common.node) {
+ op->common.node->type = (u8) object_type;
+ }
+ }
+ }
+#ifdef ACPI_ASL_COMPILER
+ /*
+ * For external opcode, get the object type from the argument and
+ * get the parameter count from the argument's next.
+ */
+ if (acpi_gbl_disasm_flag &&
+ op->common.node && op->common.aml_opcode == AML_EXTERNAL_OP) {
+ /*
+ * Note, if this external is not a method
+ * Op->Common.Value.Arg->Common.Next->Common.Value.Integer == 0
+ * Therefore, param_count will be 0.
+ */
+ param_count =
+ (u8)op->common.value.arg->common.next->common.value.integer;
+ object_type = (u8)op->common.value.arg->common.value.integer;
+ op->common.node->flags |= ANOBJ_IS_EXTERNAL;
+ op->common.node->type = (u8)object_type;
+
+ acpi_dm_create_subobject_for_external((u8)object_type,
+ &op->common.node,
+ param_count);
+
+ /*
+ * Add the external to the external list because we may be
+ * emitting code based off of the items within the external list.
+ */
+ acpi_dm_add_op_to_external_list(op, op->named.path,
+ (u8)object_type, param_count,
+ ACPI_EXT_ORIGIN_FROM_OPCODE |
+ ACPI_EXT_RESOLVED_REFERENCE);
+ }
+#endif
+
+ /*
+ * If we are executing a method, do not create any namespace objects
+ * during the load phase, only during execution.
+ */
+ if (!walk_state->method_node) {
+ if (op->common.aml_opcode == AML_METHOD_OP) {
+ /*
+ * method_op pkg_length name_string method_flags term_list
+ *
+ * Note: We must create the method node/object pair as soon as we
+ * see the method declaration. This allows later pass1 parsing
+ * of invocations of the method (need to know the number of
+ * arguments.)
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "LOADING-Method: State=%p Op=%p NamedObj=%p\n",
+ walk_state, op, op->named.node));
+
+ if (!acpi_ns_get_attached_object(op->named.node)) {
+ walk_state->operands[0] =
+ ACPI_CAST_PTR(void, op->named.node);
+ walk_state->num_operands = 1;
+
+ status =
+ acpi_ds_create_operands(walk_state,
+ op->common.value.
+ arg);
+ if (ACPI_SUCCESS(status)) {
+ status =
+ acpi_ex_create_method(op->named.
+ data,
+ op->named.
+ length,
+ walk_state);
+ }
+
+ walk_state->operands[0] = NULL;
+ walk_state->num_operands = 0;
+
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+ }
+ }
+
+ /* Pop the scope stack (only if loading a table) */
+
+ if (!walk_state->method_node &&
+ op->common.aml_opcode != AML_EXTERNAL_OP &&
+ acpi_ns_opens_scope(object_type)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "(%s): Popping scope for Op %p\n",
+ acpi_ut_get_type_name(object_type), op));
+
+ status = acpi_ds_scope_stack_pop(walk_state);
+ }
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/dswload2.c b/drivers/acpi/acpica/dswload2.c
new file mode 100644
index 000000000..778df616a
--- /dev/null
+++ b/drivers/acpi/acpica/dswload2.c
@@ -0,0 +1,745 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dswload2 - Dispatcher second pass namespace load callbacks
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+#include "acevents.h"
+#ifdef ACPI_EXEC_APP
+#include "aecommon.h"
+#endif
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dswload2")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_load2_begin_op
+ *
+ * PARAMETERS: walk_state - Current state of the parse tree walk
+ * out_op - Where to return op if a new one is created
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Descending callback used during the loading of ACPI tables.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object **out_op)
+{
+ union acpi_parse_object *op;
+ struct acpi_namespace_node *node;
+ acpi_status status;
+ acpi_object_type object_type;
+ char *buffer_ptr;
+ u32 flags;
+
+ ACPI_FUNCTION_TRACE(ds_load2_begin_op);
+
+ op = walk_state->op;
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op,
+ walk_state));
+
+ if (op) {
+ if ((walk_state->control_state) &&
+ (walk_state->control_state->common.state ==
+ ACPI_CONTROL_CONDITIONAL_EXECUTING)) {
+
+ /* We are executing a while loop outside of a method */
+
+ status = acpi_ds_exec_begin_op(walk_state, out_op);
+ return_ACPI_STATUS(status);
+ }
+
+ /* We only care about Namespace opcodes here */
+
+ if ((!(walk_state->op_info->flags & AML_NSOPCODE) &&
+ (walk_state->opcode != AML_INT_NAMEPATH_OP)) ||
+ (!(walk_state->op_info->flags & AML_NAMED))) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Get the name we are going to enter or lookup in the namespace */
+
+ if (walk_state->opcode == AML_INT_NAMEPATH_OP) {
+
+ /* For Namepath op, get the path string */
+
+ buffer_ptr = op->common.value.string;
+ if (!buffer_ptr) {
+
+ /* No name, just exit */
+
+ return_ACPI_STATUS(AE_OK);
+ }
+ } else {
+ /* Get name from the op */
+
+ buffer_ptr = ACPI_CAST_PTR(char, &op->named.name);
+ }
+ } else {
+ /* Get the namestring from the raw AML */
+
+ buffer_ptr =
+ acpi_ps_get_next_namestring(&walk_state->parser_state);
+ }
+
+ /* Map the opcode into an internal object type */
+
+ object_type = walk_state->op_info->object_type;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "State=%p Op=%p Type=%X\n", walk_state, op,
+ object_type));
+
+ switch (walk_state->opcode) {
+ case AML_FIELD_OP:
+ case AML_BANK_FIELD_OP:
+ case AML_INDEX_FIELD_OP:
+
+ node = NULL;
+ status = AE_OK;
+ break;
+
+ case AML_INT_NAMEPATH_OP:
+ /*
+ * The name_path is an object reference to an existing object.
+ * Don't enter the name into the namespace, but look it up
+ * for use later.
+ */
+ status =
+ acpi_ns_lookup(walk_state->scope_info, buffer_ptr,
+ object_type, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, walk_state, &(node));
+ break;
+
+ case AML_SCOPE_OP:
+
+ /* Special case for Scope(\) -> refers to the Root node */
+
+ if (op && (op->named.node == acpi_gbl_root_node)) {
+ node = op->named.node;
+
+ status =
+ acpi_ds_scope_stack_push(node, object_type,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ } else {
+ /*
+ * The Path is an object reference to an existing object.
+ * Don't enter the name into the namespace, but look it up
+ * for use later.
+ */
+ status =
+ acpi_ns_lookup(walk_state->scope_info, buffer_ptr,
+ object_type, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, walk_state,
+ &(node));
+ if (ACPI_FAILURE(status)) {
+#ifdef ACPI_ASL_COMPILER
+ if (status == AE_NOT_FOUND) {
+ status = AE_OK;
+ } else {
+ ACPI_ERROR_NAMESPACE(walk_state->
+ scope_info,
+ buffer_ptr,
+ status);
+ }
+#else
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ buffer_ptr, status);
+#endif
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * We must check to make sure that the target is
+ * one of the opcodes that actually opens a scope
+ */
+ switch (node->type) {
+ case ACPI_TYPE_ANY:
+ case ACPI_TYPE_LOCAL_SCOPE: /* Scope */
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_POWER:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+
+ /* These are acceptable types */
+ break;
+
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ /*
+ * These types we will allow, but we will change the type.
+ * This enables some existing code of the form:
+ *
+ * Name (DEB, 0)
+ * Scope (DEB) { ... }
+ */
+ ACPI_WARNING((AE_INFO,
+ "Type override - [%4.4s] had invalid type (%s) "
+ "for Scope operator, changed to type ANY",
+ acpi_ut_get_node_name(node),
+ acpi_ut_get_type_name(node->type)));
+
+ node->type = ACPI_TYPE_ANY;
+ walk_state->scope_info->common.value = ACPI_TYPE_ANY;
+ break;
+
+ case ACPI_TYPE_METHOD:
+
+ /*
+ * Allow scope change to root during execution of module-level
+ * code. Root is typed METHOD during this time.
+ */
+ if ((node == acpi_gbl_root_node) &&
+ (walk_state->
+ parse_flags & ACPI_PARSE_MODULE_LEVEL)) {
+ break;
+ }
+
+ ACPI_FALLTHROUGH;
+
+ default:
+
+ /* All other types are an error */
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid type (%s) for target of "
+ "Scope operator [%4.4s] (Cannot override)",
+ acpi_ut_get_type_name(node->type),
+ acpi_ut_get_node_name(node)));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ break;
+
+ default:
+
+ /* All other opcodes */
+
+ if (op && op->common.node) {
+
+ /* This op/node was previously entered into the namespace */
+
+ node = op->common.node;
+
+ if (acpi_ns_opens_scope(object_type)) {
+ status =
+ acpi_ds_scope_stack_push(node, object_type,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Enter the named type into the internal namespace. We enter the name
+ * as we go downward in the parse tree. Any necessary subobjects that
+ * involve arguments to the opcode must be created as we go back up the
+ * parse tree later.
+ *
+ * Note: Name may already exist if we are executing a deferred opcode.
+ */
+ if (walk_state->deferred_node) {
+
+ /* This name is already in the namespace, get the node */
+
+ node = walk_state->deferred_node;
+ status = AE_OK;
+ break;
+ }
+
+ flags = ACPI_NS_NO_UPSEARCH;
+ if (walk_state->pass_number == ACPI_IMODE_EXECUTE) {
+
+ /* Execution mode, node cannot already exist, node is temporary */
+
+ flags |= ACPI_NS_ERROR_IF_FOUND;
+
+ if (!
+ (walk_state->
+ parse_flags & ACPI_PARSE_MODULE_LEVEL)) {
+ flags |= ACPI_NS_TEMPORARY;
+ }
+ }
+#ifdef ACPI_ASL_COMPILER
+
+ /*
+ * Do not open a scope for AML_EXTERNAL_OP
+ * acpi_ns_lookup can open a new scope based on the object type
+ * of this op. AML_EXTERNAL_OP is a declaration rather than a
+ * definition. In the case that this external is a method object,
+ * acpi_ns_lookup will open a new scope. However, an AML_EXTERNAL_OP
+ * associated with the ACPI_TYPE_METHOD is a declaration, rather than
+ * a definition. Flags is set to avoid opening a scope for any
+ * AML_EXTERNAL_OP.
+ */
+ if (walk_state->opcode == AML_EXTERNAL_OP) {
+ flags |= ACPI_NS_DONT_OPEN_SCOPE;
+ }
+#endif
+
+ /*
+ * For name creation opcodes, the full namepath prefix must
+ * exist, except for the final (new) nameseg.
+ */
+ if (walk_state->op_info->flags & AML_NAMED) {
+ flags |= ACPI_NS_PREFIX_MUST_EXIST;
+ }
+
+ /* Add new entry or lookup existing entry */
+
+ status =
+ acpi_ns_lookup(walk_state->scope_info, buffer_ptr,
+ object_type, ACPI_IMODE_LOAD_PASS2, flags,
+ walk_state, &node);
+
+ if (ACPI_SUCCESS(status) && (flags & ACPI_NS_TEMPORARY)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "***New Node [%4.4s] %p is temporary\n",
+ acpi_ut_get_node_name(node), node));
+ }
+ break;
+ }
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ buffer_ptr, status);
+ return_ACPI_STATUS(status);
+ }
+
+ if (!op) {
+
+ /* Create a new op */
+
+ op = acpi_ps_alloc_op(walk_state->opcode, walk_state->aml);
+ if (!op) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Initialize the new op */
+
+ if (node) {
+ op->named.name = node->name.integer;
+ }
+ *out_op = op;
+ }
+
+ /*
+ * Put the Node in the "op" object that the parser uses, so we
+ * can get it again quickly when this scope is closed
+ */
+ op->common.node = node;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_load2_end_op
+ *
+ * PARAMETERS: walk_state - Current state of the parse tree walk
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Ascending callback used during the loading of the namespace,
+ * both control methods and everything else.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
+{
+ union acpi_parse_object *op;
+ acpi_status status = AE_OK;
+ acpi_object_type object_type;
+ struct acpi_namespace_node *node;
+ union acpi_parse_object *arg;
+ struct acpi_namespace_node *new_node;
+ u32 i;
+ u8 region_space;
+#ifdef ACPI_EXEC_APP
+ union acpi_operand_object *obj_desc;
+ char *namepath;
+#endif
+
+ ACPI_FUNCTION_TRACE(ds_load2_end_op);
+
+ op = walk_state->op;
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Opcode [%s] Op %p State %p\n",
+ walk_state->op_info->name, op, walk_state));
+
+ /* Check if opcode had an associated namespace object */
+
+ if (!(walk_state->op_info->flags & AML_NSOBJECT)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ if (op->common.aml_opcode == AML_SCOPE_OP) {
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Ending scope Op=%p State=%p\n", op,
+ walk_state));
+ }
+
+ object_type = walk_state->op_info->object_type;
+
+ /*
+ * Get the Node/name from the earlier lookup
+ * (It was saved in the *op structure)
+ */
+ node = op->common.node;
+
+ /*
+ * Put the Node on the object stack (Contains the ACPI Name of
+ * this object)
+ */
+ walk_state->operands[0] = (void *)node;
+ walk_state->num_operands = 1;
+
+ /* Pop the scope stack */
+
+ if (acpi_ns_opens_scope(object_type) &&
+ (op->common.aml_opcode != AML_INT_METHODCALL_OP)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "(%s) Popping scope for Op %p\n",
+ acpi_ut_get_type_name(object_type), op));
+
+ status = acpi_ds_scope_stack_pop(walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Named operations are as follows:
+ *
+ * AML_ALIAS
+ * AML_BANKFIELD
+ * AML_CREATEBITFIELD
+ * AML_CREATEBYTEFIELD
+ * AML_CREATEDWORDFIELD
+ * AML_CREATEFIELD
+ * AML_CREATEQWORDFIELD
+ * AML_CREATEWORDFIELD
+ * AML_DATA_REGION
+ * AML_DEVICE
+ * AML_EVENT
+ * AML_FIELD
+ * AML_INDEXFIELD
+ * AML_METHOD
+ * AML_METHODCALL
+ * AML_MUTEX
+ * AML_NAME
+ * AML_NAMEDFIELD
+ * AML_OPREGION
+ * AML_POWERRES
+ * AML_PROCESSOR
+ * AML_SCOPE
+ * AML_THERMALZONE
+ */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "Create-Load [%s] State=%p Op=%p NamedObj=%p\n",
+ acpi_ps_get_opcode_name(op->common.aml_opcode),
+ walk_state, op, node));
+
+ /* Decode the opcode */
+
+ arg = op->common.value.arg;
+
+ switch (walk_state->op_info->type) {
+
+ case AML_TYPE_CREATE_FIELD:
+ /*
+ * Create the field object, but the field buffer and index must
+ * be evaluated later during the execution phase
+ */
+ status = acpi_ds_create_buffer_field(op, walk_state);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "CreateBufferField failure"));
+ goto cleanup;
+ }
+ break;
+
+ case AML_TYPE_NAMED_FIELD:
+ /*
+ * If we are executing a method, initialize the field
+ */
+ if (walk_state->method_node) {
+ status = acpi_ds_init_field_objects(op, walk_state);
+ }
+
+ switch (op->common.aml_opcode) {
+ case AML_INDEX_FIELD_OP:
+
+ status =
+ acpi_ds_create_index_field(op,
+ (acpi_handle)arg->common.
+ node, walk_state);
+ break;
+
+ case AML_BANK_FIELD_OP:
+
+ status =
+ acpi_ds_create_bank_field(op, arg->common.node,
+ walk_state);
+ break;
+
+ case AML_FIELD_OP:
+
+ status =
+ acpi_ds_create_field(op, arg->common.node,
+ walk_state);
+ break;
+
+ default:
+
+ /* All NAMED_FIELD opcodes must be handled above */
+ break;
+ }
+ break;
+
+ case AML_TYPE_NAMED_SIMPLE:
+
+ status = acpi_ds_create_operands(walk_state, arg);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ switch (op->common.aml_opcode) {
+ case AML_PROCESSOR_OP:
+
+ status = acpi_ex_create_processor(walk_state);
+ break;
+
+ case AML_POWER_RESOURCE_OP:
+
+ status = acpi_ex_create_power_resource(walk_state);
+ break;
+
+ case AML_MUTEX_OP:
+
+ status = acpi_ex_create_mutex(walk_state);
+ break;
+
+ case AML_EVENT_OP:
+
+ status = acpi_ex_create_event(walk_state);
+ break;
+
+ case AML_ALIAS_OP:
+
+ status = acpi_ex_create_alias(walk_state);
+ break;
+
+ default:
+
+ /* Unknown opcode */
+
+ status = AE_OK;
+ goto cleanup;
+ }
+
+ /* Delete operands */
+
+ for (i = 1; i < walk_state->num_operands; i++) {
+ acpi_ut_remove_reference(walk_state->operands[i]);
+ walk_state->operands[i] = NULL;
+ }
+
+ break;
+
+ case AML_TYPE_NAMED_COMPLEX:
+
+ switch (op->common.aml_opcode) {
+ case AML_REGION_OP:
+ case AML_DATA_REGION_OP:
+
+ if (op->common.aml_opcode == AML_REGION_OP) {
+ region_space = (acpi_adr_space_type)
+ ((op->common.value.arg)->common.value.
+ integer);
+ } else {
+ region_space = ACPI_ADR_SPACE_DATA_TABLE;
+ }
+
+ /*
+ * The op_region is not fully parsed at this time. The only valid
+ * argument is the space_id. (We must save the address of the
+ * AML of the address and length operands)
+ *
+ * If we have a valid region, initialize it. The namespace is
+ * unlocked at this point.
+ *
+ * Need to unlock interpreter if it is locked (if we are running
+ * a control method), in order to allow _REG methods to be run
+ * during acpi_ev_initialize_region.
+ */
+ if (walk_state->method_node) {
+ /*
+ * Executing a method: initialize the region and unlock
+ * the interpreter
+ */
+ status = acpi_ex_create_region(op->named.data,
+ op->named.length,
+ region_space,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ status =
+ acpi_ev_initialize_region
+ (acpi_ns_get_attached_object(node));
+ break;
+
+ case AML_NAME_OP:
+
+ status = acpi_ds_create_node(walk_state, node, op);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+#ifdef ACPI_EXEC_APP
+ /*
+ * acpi_exec support for namespace initialization file (initialize
+ * Name opcodes in this code.)
+ */
+ namepath = acpi_ns_get_external_pathname(node);
+ status = ae_lookup_init_file_entry(namepath, &obj_desc);
+ if (ACPI_SUCCESS(status)) {
+
+ /* Detach any existing object, attach new object */
+
+ if (node->object) {
+ acpi_ns_detach_object(node);
+ }
+ acpi_ns_attach_object(node, obj_desc,
+ obj_desc->common.type);
+ }
+ ACPI_FREE(namepath);
+ status = AE_OK;
+#endif
+ break;
+
+ case AML_METHOD_OP:
+ /*
+ * method_op pkg_length name_string method_flags term_list
+ *
+ * Note: We must create the method node/object pair as soon as we
+ * see the method declaration. This allows later pass1 parsing
+ * of invocations of the method (need to know the number of
+ * arguments.)
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "LOADING-Method: State=%p Op=%p NamedObj=%p\n",
+ walk_state, op, op->named.node));
+
+ if (!acpi_ns_get_attached_object(op->named.node)) {
+ walk_state->operands[0] =
+ ACPI_CAST_PTR(void, op->named.node);
+ walk_state->num_operands = 1;
+
+ status =
+ acpi_ds_create_operands(walk_state,
+ op->common.value.
+ arg);
+ if (ACPI_SUCCESS(status)) {
+ status =
+ acpi_ex_create_method(op->named.
+ data,
+ op->named.
+ length,
+ walk_state);
+ }
+
+ walk_state->operands[0] = NULL;
+ walk_state->num_operands = 0;
+
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+ break;
+
+ default:
+
+ /* All NAMED_COMPLEX opcodes must be handled above */
+ break;
+ }
+ break;
+
+ case AML_CLASS_INTERNAL:
+
+ /* case AML_INT_NAMEPATH_OP: */
+ break;
+
+ case AML_CLASS_METHOD_CALL:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "RESOLVING-MethodCall: State=%p Op=%p NamedObj=%p\n",
+ walk_state, op, node));
+
+ /*
+ * Lookup the method name and save the Node
+ */
+ status =
+ acpi_ns_lookup(walk_state->scope_info,
+ arg->common.value.string, ACPI_TYPE_ANY,
+ ACPI_IMODE_LOAD_PASS2,
+ ACPI_NS_SEARCH_PARENT |
+ ACPI_NS_DONT_OPEN_SCOPE, walk_state,
+ &(new_node));
+ if (ACPI_SUCCESS(status)) {
+ /*
+ * Make sure that what we found is indeed a method
+ * We didn't search for a method on purpose, to see if the name
+ * would resolve
+ */
+ if (new_node->type != ACPI_TYPE_METHOD) {
+ status = AE_AML_OPERAND_TYPE;
+ }
+
+ /* We could put the returned object (Node) on the object stack for
+ * later, but for now, we will put it in the "op" object that the
+ * parser uses, so we can get it again at the end of this scope
+ */
+ op->common.node = new_node;
+ } else {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info,
+ arg->common.value.string, status);
+ }
+ break;
+
+ default:
+
+ break;
+ }
+
+cleanup:
+
+ /* Remove the Node pushed at the very beginning */
+
+ walk_state->operands[0] = NULL;
+ walk_state->num_operands = 0;
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/dswscope.c b/drivers/acpi/acpica/dswscope.c
new file mode 100644
index 000000000..634b9100f
--- /dev/null
+++ b/drivers/acpi/acpica/dswscope.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dswscope - Scope stack manipulation
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdispat.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dswscope")
+
+/****************************************************************************
+ *
+ * FUNCTION: acpi_ds_scope_stack_clear
+ *
+ * PARAMETERS: walk_state - Current state
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Pop (and free) everything on the scope stack except the
+ * root scope object (which remains at the stack top.)
+ *
+ ***************************************************************************/
+void acpi_ds_scope_stack_clear(struct acpi_walk_state *walk_state)
+{
+ union acpi_generic_state *scope_info;
+
+ ACPI_FUNCTION_NAME(ds_scope_stack_clear);
+
+ while (walk_state->scope_info) {
+
+ /* Pop a scope off the stack */
+
+ scope_info = walk_state->scope_info;
+ walk_state->scope_info = scope_info->scope.next;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Popped object type (%s)\n",
+ acpi_ut_get_type_name(scope_info->common.
+ value)));
+
+ acpi_ut_delete_generic_state(scope_info);
+ }
+}
+
+/****************************************************************************
+ *
+ * FUNCTION: acpi_ds_scope_stack_push
+ *
+ * PARAMETERS: node - Name to be made current
+ * type - Type of frame being pushed
+ * walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Push the current scope on the scope stack, and make the
+ * passed Node current.
+ *
+ ***************************************************************************/
+
+acpi_status
+acpi_ds_scope_stack_push(struct acpi_namespace_node *node,
+ acpi_object_type type,
+ struct acpi_walk_state *walk_state)
+{
+ union acpi_generic_state *scope_info;
+ union acpi_generic_state *old_scope_info;
+
+ ACPI_FUNCTION_TRACE(ds_scope_stack_push);
+
+ if (!node) {
+
+ /* Invalid scope */
+
+ ACPI_ERROR((AE_INFO, "Null scope parameter"));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Make sure object type is valid */
+
+ if (!acpi_ut_valid_object_type(type)) {
+ ACPI_WARNING((AE_INFO, "Invalid object type: 0x%X", type));
+ }
+
+ /* Allocate a new scope object */
+
+ scope_info = acpi_ut_create_generic_state();
+ if (!scope_info) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Init new scope object */
+
+ scope_info->common.descriptor_type = ACPI_DESC_TYPE_STATE_WSCOPE;
+ scope_info->scope.node = node;
+ scope_info->common.value = (u16) type;
+
+ walk_state->scope_depth++;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[%.2d] Pushed scope ",
+ (u32) walk_state->scope_depth));
+
+ old_scope_info = walk_state->scope_info;
+ if (old_scope_info) {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC,
+ "[%4.4s] (%s)",
+ acpi_ut_get_node_name(old_scope_info->
+ scope.node),
+ acpi_ut_get_type_name(old_scope_info->
+ common.value)));
+ } else {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, ACPI_NAMESPACE_ROOT));
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC,
+ ", New scope -> [%4.4s] (%s)\n",
+ acpi_ut_get_node_name(scope_info->scope.node),
+ acpi_ut_get_type_name(scope_info->common.value)));
+
+ /* Push new scope object onto stack */
+
+ acpi_ut_push_generic_state(&walk_state->scope_info, scope_info);
+ return_ACPI_STATUS(AE_OK);
+}
+
+/****************************************************************************
+ *
+ * FUNCTION: acpi_ds_scope_stack_pop
+ *
+ * PARAMETERS: walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Pop the scope stack once.
+ *
+ ***************************************************************************/
+
+acpi_status acpi_ds_scope_stack_pop(struct acpi_walk_state *walk_state)
+{
+ union acpi_generic_state *scope_info;
+ union acpi_generic_state *new_scope_info;
+
+ ACPI_FUNCTION_TRACE(ds_scope_stack_pop);
+
+ /*
+ * Pop scope info object off the stack.
+ */
+ scope_info = acpi_ut_pop_generic_state(&walk_state->scope_info);
+ if (!scope_info) {
+ return_ACPI_STATUS(AE_STACK_UNDERFLOW);
+ }
+
+ walk_state->scope_depth--;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[%.2d] Popped scope [%4.4s] (%s), New scope -> ",
+ (u32) walk_state->scope_depth,
+ acpi_ut_get_node_name(scope_info->scope.node),
+ acpi_ut_get_type_name(scope_info->common.value)));
+
+ new_scope_info = walk_state->scope_info;
+ if (new_scope_info) {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "[%4.4s] (%s)\n",
+ acpi_ut_get_node_name(new_scope_info->
+ scope.node),
+ acpi_ut_get_type_name(new_scope_info->
+ common.value)));
+ } else {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "%s\n",
+ ACPI_NAMESPACE_ROOT));
+ }
+
+ acpi_ut_delete_generic_state(scope_info);
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c
new file mode 100644
index 000000000..77076da20
--- /dev/null
+++ b/drivers/acpi/acpica/dswstate.c
@@ -0,0 +1,728 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: dswstate - Dispatcher parse tree walk management routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "acdispat.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_DISPATCHER
+ACPI_MODULE_NAME("dswstate")
+
+ /* Local prototypes */
+static acpi_status
+acpi_ds_result_stack_push(struct acpi_walk_state *walk_state);
+static acpi_status acpi_ds_result_stack_pop(struct acpi_walk_state *walk_state);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_result_pop
+ *
+ * PARAMETERS: object - Where to return the popped object
+ * walk_state - Current Walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Pop an object off the top of this walk's result stack
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_result_pop(union acpi_operand_object **object,
+ struct acpi_walk_state *walk_state)
+{
+ u32 index;
+ union acpi_generic_state *state;
+ acpi_status status;
+
+ ACPI_FUNCTION_NAME(ds_result_pop);
+
+ state = walk_state->results;
+
+ /* Incorrect state of result stack */
+
+ if (state && !walk_state->result_count) {
+ ACPI_ERROR((AE_INFO, "No results on result stack"));
+ return (AE_AML_INTERNAL);
+ }
+
+ if (!state && walk_state->result_count) {
+ ACPI_ERROR((AE_INFO, "No result state for result stack"));
+ return (AE_AML_INTERNAL);
+ }
+
+ /* Empty result stack */
+
+ if (!state) {
+ ACPI_ERROR((AE_INFO, "Result stack is empty! State=%p",
+ walk_state));
+ return (AE_AML_NO_RETURN_VALUE);
+ }
+
+ /* Return object of the top element and clean that top element result stack */
+
+ walk_state->result_count--;
+ index = (u32)walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM;
+
+ *object = state->results.obj_desc[index];
+ if (!*object) {
+ ACPI_ERROR((AE_INFO,
+ "No result objects on result stack, State=%p",
+ walk_state));
+ return (AE_AML_NO_RETURN_VALUE);
+ }
+
+ state->results.obj_desc[index] = NULL;
+ if (index == 0) {
+ status = acpi_ds_result_stack_pop(walk_state);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Obj=%p [%s] Index=%X State=%p Num=%X\n", *object,
+ acpi_ut_get_object_type_name(*object),
+ index, walk_state, walk_state->result_count));
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_result_push
+ *
+ * PARAMETERS: object - Where to return the popped object
+ * walk_state - Current Walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Push an object onto the current result stack
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_result_push(union acpi_operand_object *object,
+ struct acpi_walk_state *walk_state)
+{
+ union acpi_generic_state *state;
+ acpi_status status;
+ u32 index;
+
+ ACPI_FUNCTION_NAME(ds_result_push);
+
+ if (walk_state->result_count > walk_state->result_size) {
+ ACPI_ERROR((AE_INFO, "Result stack is full"));
+ return (AE_AML_INTERNAL);
+ } else if (walk_state->result_count == walk_state->result_size) {
+
+ /* Extend the result stack */
+
+ status = acpi_ds_result_stack_push(walk_state);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO,
+ "Failed to extend the result stack"));
+ return (status);
+ }
+ }
+
+ if (!(walk_state->result_count < walk_state->result_size)) {
+ ACPI_ERROR((AE_INFO, "No free elements in result stack"));
+ return (AE_AML_INTERNAL);
+ }
+
+ state = walk_state->results;
+ if (!state) {
+ ACPI_ERROR((AE_INFO, "No result stack frame during push"));
+ return (AE_AML_INTERNAL);
+ }
+
+ if (!object) {
+ ACPI_ERROR((AE_INFO,
+ "Null Object! Obj=%p State=%p Num=%u",
+ object, walk_state, walk_state->result_count));
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Assign the address of object to the top free element of result stack */
+
+ index = (u32)walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM;
+ state->results.obj_desc[index] = object;
+ walk_state->result_count++;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Obj=%p [%s] State=%p Num=%X Cur=%X\n",
+ object,
+ acpi_ut_get_object_type_name((union
+ acpi_operand_object *)
+ object), walk_state,
+ walk_state->result_count,
+ walk_state->current_result));
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_result_stack_push
+ *
+ * PARAMETERS: walk_state - Current Walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Push an object onto the walk_state result stack
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_ds_result_stack_push(struct acpi_walk_state *walk_state)
+{
+ union acpi_generic_state *state;
+
+ ACPI_FUNCTION_NAME(ds_result_stack_push);
+
+ /* Check for stack overflow */
+
+ if (((u32) walk_state->result_size + ACPI_RESULTS_FRAME_OBJ_NUM) >
+ ACPI_RESULTS_OBJ_NUM_MAX) {
+ ACPI_ERROR((AE_INFO, "Result stack overflow: State=%p Num=%u",
+ walk_state, walk_state->result_size));
+ return (AE_STACK_OVERFLOW);
+ }
+
+ state = acpi_ut_create_generic_state();
+ if (!state) {
+ return (AE_NO_MEMORY);
+ }
+
+ state->common.descriptor_type = ACPI_DESC_TYPE_STATE_RESULT;
+ acpi_ut_push_generic_state(&walk_state->results, state);
+
+ /* Increase the length of the result stack by the length of frame */
+
+ walk_state->result_size += ACPI_RESULTS_FRAME_OBJ_NUM;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Results=%p State=%p\n",
+ state, walk_state));
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_result_stack_pop
+ *
+ * PARAMETERS: walk_state - Current Walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Pop an object off of the walk_state result stack
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_ds_result_stack_pop(struct acpi_walk_state *walk_state)
+{
+ union acpi_generic_state *state;
+
+ ACPI_FUNCTION_NAME(ds_result_stack_pop);
+
+ /* Check for stack underflow */
+
+ if (walk_state->results == NULL) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Result stack underflow - State=%p\n",
+ walk_state));
+ return (AE_AML_NO_OPERAND);
+ }
+
+ if (walk_state->result_size < ACPI_RESULTS_FRAME_OBJ_NUM) {
+ ACPI_ERROR((AE_INFO, "Insufficient result stack size"));
+ return (AE_AML_INTERNAL);
+ }
+
+ state = acpi_ut_pop_generic_state(&walk_state->results);
+ acpi_ut_delete_generic_state(state);
+
+ /* Decrease the length of result stack by the length of frame */
+
+ walk_state->result_size -= ACPI_RESULTS_FRAME_OBJ_NUM;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Result=%p RemainingResults=%X State=%p\n",
+ state, walk_state->result_count, walk_state));
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_obj_stack_push
+ *
+ * PARAMETERS: object - Object to push
+ * walk_state - Current Walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Push an object onto this walk's object/operand stack
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_obj_stack_push(void *object, struct acpi_walk_state *walk_state)
+{
+ ACPI_FUNCTION_NAME(ds_obj_stack_push);
+
+ /* Check for stack overflow */
+
+ if (walk_state->num_operands >= ACPI_OBJ_NUM_OPERANDS) {
+ ACPI_ERROR((AE_INFO,
+ "Object stack overflow! Obj=%p State=%p #Ops=%u",
+ object, walk_state, walk_state->num_operands));
+ return (AE_STACK_OVERFLOW);
+ }
+
+ /* Put the object onto the stack */
+
+ walk_state->operands[walk_state->operand_index] = object;
+ walk_state->num_operands++;
+
+ /* For the usual order of filling the operand stack */
+
+ walk_state->operand_index++;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Obj=%p [%s] State=%p #Ops=%X\n",
+ object,
+ acpi_ut_get_object_type_name((union
+ acpi_operand_object *)
+ object), walk_state,
+ walk_state->num_operands));
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_obj_stack_pop
+ *
+ * PARAMETERS: pop_count - Number of objects/entries to pop
+ * walk_state - Current Walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Pop this walk's object stack. Objects on the stack are NOT
+ * deleted by this routine.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_obj_stack_pop(u32 pop_count, struct acpi_walk_state *walk_state)
+{
+ u32 i;
+
+ ACPI_FUNCTION_NAME(ds_obj_stack_pop);
+
+ for (i = 0; i < pop_count; i++) {
+
+ /* Check for stack underflow */
+
+ if (walk_state->num_operands == 0) {
+ ACPI_ERROR((AE_INFO,
+ "Object stack underflow! Count=%X State=%p #Ops=%u",
+ pop_count, walk_state,
+ walk_state->num_operands));
+ return (AE_STACK_UNDERFLOW);
+ }
+
+ /* Just set the stack entry to null */
+
+ walk_state->num_operands--;
+ walk_state->operands[walk_state->num_operands] = NULL;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Count=%X State=%p #Ops=%u\n",
+ pop_count, walk_state, walk_state->num_operands));
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_obj_stack_pop_and_delete
+ *
+ * PARAMETERS: pop_count - Number of objects/entries to pop
+ * walk_state - Current Walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Pop this walk's object stack and delete each object that is
+ * popped off.
+ *
+ ******************************************************************************/
+
+void
+acpi_ds_obj_stack_pop_and_delete(u32 pop_count,
+ struct acpi_walk_state *walk_state)
+{
+ s32 i;
+ union acpi_operand_object *obj_desc;
+
+ ACPI_FUNCTION_NAME(ds_obj_stack_pop_and_delete);
+
+ if (pop_count == 0) {
+ return;
+ }
+
+ for (i = (s32)pop_count - 1; i >= 0; i--) {
+ if (walk_state->num_operands == 0) {
+ return;
+ }
+
+ /* Pop the stack and delete an object if present in this stack entry */
+
+ walk_state->num_operands--;
+ obj_desc = walk_state->operands[i];
+ if (obj_desc) {
+ acpi_ut_remove_reference(walk_state->operands[i]);
+ walk_state->operands[i] = NULL;
+ }
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Count=%X State=%p #Ops=%X\n",
+ pop_count, walk_state, walk_state->num_operands));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_get_current_walk_state
+ *
+ * PARAMETERS: thread - Get current active state for this Thread
+ *
+ * RETURN: Pointer to the current walk state
+ *
+ * DESCRIPTION: Get the walk state that is at the head of the list (the "current"
+ * walk state.)
+ *
+ ******************************************************************************/
+
+struct acpi_walk_state *acpi_ds_get_current_walk_state(struct acpi_thread_state
+ *thread)
+{
+ ACPI_FUNCTION_NAME(ds_get_current_walk_state);
+
+ if (!thread) {
+ return (NULL);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "Current WalkState %p\n",
+ thread->walk_state_list));
+
+ return (thread->walk_state_list);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_push_walk_state
+ *
+ * PARAMETERS: walk_state - State to push
+ * thread - Thread state object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Place the Thread state at the head of the state list
+ *
+ ******************************************************************************/
+
+void
+acpi_ds_push_walk_state(struct acpi_walk_state *walk_state,
+ struct acpi_thread_state *thread)
+{
+ ACPI_FUNCTION_TRACE(ds_push_walk_state);
+
+ walk_state->next = thread->walk_state_list;
+ thread->walk_state_list = walk_state;
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_pop_walk_state
+ *
+ * PARAMETERS: thread - Current thread state
+ *
+ * RETURN: A walk_state object popped from the thread's stack
+ *
+ * DESCRIPTION: Remove and return the walkstate object that is at the head of
+ * the walk stack for the given walk list. NULL indicates that
+ * the list is empty.
+ *
+ ******************************************************************************/
+
+struct acpi_walk_state *acpi_ds_pop_walk_state(struct acpi_thread_state *thread)
+{
+ struct acpi_walk_state *walk_state;
+
+ ACPI_FUNCTION_TRACE(ds_pop_walk_state);
+
+ walk_state = thread->walk_state_list;
+
+ if (walk_state) {
+
+ /* Next walk state becomes the current walk state */
+
+ thread->walk_state_list = walk_state->next;
+
+ /*
+ * Don't clear the NEXT field, this serves as an indicator
+ * that there is a parent WALK STATE
+ * Do Not: walk_state->Next = NULL;
+ */
+ }
+
+ return_PTR(walk_state);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_create_walk_state
+ *
+ * PARAMETERS: owner_id - ID for object creation
+ * origin - Starting point for this walk
+ * method_desc - Method object
+ * thread - Current thread state
+ *
+ * RETURN: Pointer to the new walk state.
+ *
+ * DESCRIPTION: Allocate and initialize a new walk state. The current walk
+ * state is set to this new state.
+ *
+ ******************************************************************************/
+
+struct acpi_walk_state *acpi_ds_create_walk_state(acpi_owner_id owner_id,
+ union acpi_parse_object
+ *origin,
+ union acpi_operand_object
+ *method_desc,
+ struct acpi_thread_state
+ *thread)
+{
+ struct acpi_walk_state *walk_state;
+
+ ACPI_FUNCTION_TRACE(ds_create_walk_state);
+
+ walk_state = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_walk_state));
+ if (!walk_state) {
+ return_PTR(NULL);
+ }
+
+ walk_state->descriptor_type = ACPI_DESC_TYPE_WALK;
+ walk_state->method_desc = method_desc;
+ walk_state->owner_id = owner_id;
+ walk_state->origin = origin;
+ walk_state->thread = thread;
+
+ walk_state->parser_state.start_op = origin;
+
+ /* Init the method args/local */
+
+#ifndef ACPI_CONSTANT_EVAL_ONLY
+ acpi_ds_method_data_init(walk_state);
+#endif
+
+ /* Put the new state at the head of the walk list */
+
+ if (thread) {
+ acpi_ds_push_walk_state(walk_state, thread);
+ }
+
+ return_PTR(walk_state);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_init_aml_walk
+ *
+ * PARAMETERS: walk_state - New state to be initialized
+ * op - Current parse op
+ * method_node - Control method NS node, if any
+ * aml_start - Start of AML
+ * aml_length - Length of AML
+ * info - Method info block (params, etc.)
+ * pass_number - 1, 2, or 3
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize a walk state for a pass 1 or 2 parse tree walk
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ds_init_aml_walk(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ struct acpi_namespace_node *method_node,
+ u8 * aml_start,
+ u32 aml_length,
+ struct acpi_evaluate_info *info, u8 pass_number)
+{
+ acpi_status status;
+ struct acpi_parse_state *parser_state = &walk_state->parser_state;
+ union acpi_parse_object *extra_op;
+
+ ACPI_FUNCTION_TRACE(ds_init_aml_walk);
+
+ walk_state->parser_state.aml =
+ 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 */
+
+ walk_state->next_op = NULL;
+ walk_state->pass_number = pass_number;
+
+ if (info) {
+ walk_state->params = info->parameters;
+ walk_state->caller_return_desc = &info->return_object;
+ }
+
+ status = acpi_ps_init_scope(&walk_state->parser_state, op);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (method_node) {
+ walk_state->parser_state.start_node = method_node;
+ walk_state->walk_type = ACPI_WALK_METHOD;
+ walk_state->method_node = method_node;
+ walk_state->method_desc =
+ acpi_ns_get_attached_object(method_node);
+
+ /* Push start scope on scope stack and make it current */
+
+ status =
+ acpi_ds_scope_stack_push(method_node, ACPI_TYPE_METHOD,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Init the method arguments */
+
+ status = acpi_ds_method_data_init_args(walk_state->params,
+ ACPI_METHOD_NUM_ARGS,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ } else {
+ /*
+ * Setup the current scope.
+ * Find a Named Op that has a namespace node associated with it.
+ * search upwards from this Op. Current scope is the first
+ * Op with a namespace node.
+ */
+ extra_op = parser_state->start_op;
+ while (extra_op && !extra_op->common.node) {
+ extra_op = extra_op->common.parent;
+ }
+
+ if (!extra_op) {
+ parser_state->start_node = NULL;
+ } else {
+ parser_state->start_node = extra_op->common.node;
+ }
+
+ if (parser_state->start_node) {
+
+ /* Push start scope on scope stack and make it current */
+
+ status =
+ acpi_ds_scope_stack_push(parser_state->start_node,
+ parser_state->start_node->
+ type, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+ }
+
+ status = acpi_ds_init_callbacks(walk_state, pass_number);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ds_delete_walk_state
+ *
+ * PARAMETERS: walk_state - State to delete
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Delete a walk state including all internal data structures
+ *
+ ******************************************************************************/
+
+void acpi_ds_delete_walk_state(struct acpi_walk_state *walk_state)
+{
+ union acpi_generic_state *state;
+
+ ACPI_FUNCTION_TRACE_PTR(ds_delete_walk_state, walk_state);
+
+ if (!walk_state) {
+ return_VOID;
+ }
+
+ if (walk_state->descriptor_type != ACPI_DESC_TYPE_WALK) {
+ ACPI_ERROR((AE_INFO, "%p is not a valid walk state",
+ walk_state));
+ return_VOID;
+ }
+
+ /* There should not be any open scopes */
+
+ if (walk_state->parser_state.scope) {
+ ACPI_ERROR((AE_INFO, "%p walk still has a scope list",
+ walk_state));
+ acpi_ps_cleanup_scope(&walk_state->parser_state);
+ }
+
+ /* Always must free any linked control states */
+
+ while (walk_state->control_state) {
+ state = walk_state->control_state;
+ walk_state->control_state = state->common.next;
+
+ acpi_ut_delete_generic_state(state);
+ }
+
+ /* Always must free any linked parse states */
+
+ while (walk_state->scope_info) {
+ state = walk_state->scope_info;
+ walk_state->scope_info = state->common.next;
+
+ acpi_ut_delete_generic_state(state);
+ }
+
+ /* Always must free any stacked result states */
+
+ while (walk_state->results) {
+ state = walk_state->results;
+ walk_state->results = state->common.next;
+
+ acpi_ut_delete_generic_state(state);
+ }
+
+ ACPI_FREE(walk_state);
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c
new file mode 100644
index 000000000..df596d46d
--- /dev/null
+++ b/drivers/acpi/acpica/evevent.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evevent - Fixed Event handling and dispatch
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evevent")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/* Local prototypes */
+static acpi_status acpi_ev_fixed_event_initialize(void);
+
+static u32 acpi_ev_fixed_event_dispatch(u32 event);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_initialize_events
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize global data structures for ACPI events (Fixed, GPE)
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_initialize_events(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_initialize_events);
+
+ /* If Hardware Reduced flag is set, there are no fixed events */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Initialize the Fixed and General Purpose Events. This is done prior to
+ * enabling SCIs to prevent interrupts from occurring before the handlers
+ * are installed.
+ */
+ status = acpi_ev_fixed_event_initialize();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Unable to initialize fixed events"));
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ev_gpe_initialize();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Unable to initialize general purpose events"));
+ return_ACPI_STATUS(status);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_install_xrupt_handlers
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install interrupt handlers for the SCI and Global Lock
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_install_xrupt_handlers(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_install_xrupt_handlers);
+
+ /* If Hardware Reduced flag is set, there is no ACPI h/w */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Install the SCI handler */
+
+ status = acpi_ev_install_sci_handler();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Unable to install System Control Interrupt handler"));
+ return_ACPI_STATUS(status);
+ }
+
+ /* Install the handler for the Global Lock */
+
+ status = acpi_ev_init_global_lock_handler();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Unable to initialize Global Lock handler"));
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_gbl_events_initialized = TRUE;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_fixed_event_initialize
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install the fixed event handlers and disable all fixed events.
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_ev_fixed_event_initialize(void)
+{
+ u32 i;
+ acpi_status status;
+
+ /*
+ * Initialize the structure that keeps track of fixed event handlers and
+ * disable all of the fixed events.
+ */
+ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) {
+ acpi_gbl_fixed_event_handlers[i].handler = NULL;
+ acpi_gbl_fixed_event_handlers[i].context = NULL;
+
+ /* Disable the fixed event */
+
+ if (acpi_gbl_fixed_event_info[i].enable_register_id != 0xFF) {
+ status =
+ acpi_write_bit_register(acpi_gbl_fixed_event_info
+ [i].enable_register_id,
+ ACPI_DISABLE_EVENT);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_fixed_event_detect
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
+ *
+ * DESCRIPTION: Checks the PM status register for active fixed events
+ *
+ ******************************************************************************/
+
+u32 acpi_ev_fixed_event_detect(void)
+{
+ u32 int_status = ACPI_INTERRUPT_NOT_HANDLED;
+ u32 fixed_status;
+ u32 fixed_enable;
+ u32 i;
+ acpi_status status;
+
+ ACPI_FUNCTION_NAME(ev_fixed_event_detect);
+
+ /*
+ * Read the fixed feature status and enable registers, as all the cases
+ * depend on their values. Ignore errors here.
+ */
+ status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &fixed_status);
+ status |=
+ acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &fixed_enable);
+ if (ACPI_FAILURE(status)) {
+ return (int_status);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS,
+ "Fixed Event Block: Enable %08X Status %08X\n",
+ fixed_enable, fixed_status));
+
+ /*
+ * Check for all possible Fixed Events and dispatch those that are active
+ */
+ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) {
+
+ /* Both the status and enable bits must be on for this event */
+
+ if ((fixed_status & acpi_gbl_fixed_event_info[i].
+ status_bit_mask)
+ && (fixed_enable & acpi_gbl_fixed_event_info[i].
+ enable_bit_mask)) {
+ /*
+ * Found an active (signalled) event. Invoke global event
+ * handler if present.
+ */
+ acpi_fixed_event_count[i]++;
+ if (acpi_gbl_global_event_handler) {
+ acpi_gbl_global_event_handler
+ (ACPI_EVENT_TYPE_FIXED, NULL, i,
+ acpi_gbl_global_event_handler_context);
+ }
+
+ int_status |= acpi_ev_fixed_event_dispatch(i);
+ }
+ }
+
+ return (int_status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_fixed_event_dispatch
+ *
+ * PARAMETERS: event - Event type
+ *
+ * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
+ *
+ * DESCRIPTION: Clears the status bit for the requested event, calls the
+ * handler that previously registered for the event.
+ * NOTE: If there is no handler for the event, the event is
+ * disabled to prevent further interrupts.
+ *
+ ******************************************************************************/
+
+static u32 acpi_ev_fixed_event_dispatch(u32 event)
+{
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Clear the status bit */
+
+ (void)acpi_write_bit_register(acpi_gbl_fixed_event_info[event].
+ status_register_id, ACPI_CLEAR_STATUS);
+
+ /*
+ * Make sure that a handler exists. If not, report an error
+ * and disable the event to prevent further interrupts.
+ */
+ if (!acpi_gbl_fixed_event_handlers[event].handler) {
+ (void)acpi_write_bit_register(acpi_gbl_fixed_event_info[event].
+ enable_register_id,
+ ACPI_DISABLE_EVENT);
+
+ ACPI_ERROR((AE_INFO,
+ "No installed handler for fixed event - %s (%u), disabling",
+ acpi_ut_get_event_name(event), event));
+
+ return (ACPI_INTERRUPT_NOT_HANDLED);
+ }
+
+ /* Invoke the Fixed Event handler */
+
+ return ((acpi_gbl_fixed_event_handlers[event].
+ handler) (acpi_gbl_fixed_event_handlers[event].context));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_any_fixed_event_status_set
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: TRUE or FALSE
+ *
+ * DESCRIPTION: Checks the PM status register for active fixed events
+ *
+ ******************************************************************************/
+
+u32 acpi_any_fixed_event_status_set(void)
+{
+ acpi_status status;
+ u32 in_status;
+ u32 in_enable;
+ u32 i;
+
+ status = acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &in_enable);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+
+ status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &in_status);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+
+ /*
+ * Check for all possible Fixed Events and dispatch those that are active
+ */
+ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) {
+
+ /* Both the status and enable bits must be on for this event */
+
+ if ((in_status & acpi_gbl_fixed_event_info[i].status_bit_mask) &&
+ (in_enable & acpi_gbl_fixed_event_info[i].enable_bit_mask)) {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evglock.c b/drivers/acpi/acpica/evglock.c
new file mode 100644
index 000000000..9aab54797
--- /dev/null
+++ b/drivers/acpi/acpica/evglock.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evglock - Global Lock support
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evglock")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/* Local prototypes */
+static u32 acpi_ev_global_lock_handler(void *context);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_init_global_lock_handler
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for the global lock release event
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_init_global_lock_handler(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_init_global_lock_handler);
+
+ /* If Hardware Reduced flag is set, there is no global lock */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Attempt installation of the global lock handler */
+
+ status = acpi_install_fixed_event_handler(ACPI_EVENT_GLOBAL,
+ acpi_ev_global_lock_handler,
+ NULL);
+
+ /*
+ * If the global lock does not exist on this platform, the attempt to
+ * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick).
+ * Map to AE_OK, but mark global lock as not present. Any attempt to
+ * actually use the global lock will be flagged with an error.
+ */
+ acpi_gbl_global_lock_present = FALSE;
+ if (status == AE_NO_HARDWARE_RESPONSE) {
+ ACPI_ERROR((AE_INFO,
+ "No response from Global Lock hardware, disabling lock"));
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ status = acpi_os_create_lock(&acpi_gbl_global_lock_pending_lock);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_gbl_global_lock_pending = FALSE;
+ acpi_gbl_global_lock_present = TRUE;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_remove_global_lock_handler
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove the handler for the Global Lock
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_remove_global_lock_handler(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_remove_global_lock_handler);
+
+ acpi_gbl_global_lock_present = FALSE;
+ status = acpi_remove_fixed_event_handler(ACPI_EVENT_GLOBAL,
+ acpi_ev_global_lock_handler);
+
+ acpi_os_delete_lock(acpi_gbl_global_lock_pending_lock);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_global_lock_handler
+ *
+ * PARAMETERS: context - From thread interface, not used
+ *
+ * RETURN: ACPI_INTERRUPT_HANDLED
+ *
+ * DESCRIPTION: Invoked directly from the SCI handler when a global lock
+ * release interrupt occurs. If there is actually a pending
+ * request for the lock, signal the waiting thread.
+ *
+ ******************************************************************************/
+
+static u32 acpi_ev_global_lock_handler(void *context)
+{
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
+
+ /*
+ * If a request for the global lock is not actually pending,
+ * we are done. This handles "spurious" global lock interrupts
+ * which are possible (and have been seen) with bad BIOSs.
+ */
+ if (!acpi_gbl_global_lock_pending) {
+ goto cleanup_and_exit;
+ }
+
+ /*
+ * Send a unit to the global lock semaphore. The actual acquisition
+ * of the global lock will be performed by the waiting thread.
+ */
+ status = acpi_os_signal_semaphore(acpi_gbl_global_lock_semaphore, 1);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO, "Could not signal Global Lock semaphore"));
+ }
+
+ acpi_gbl_global_lock_pending = FALSE;
+
+cleanup_and_exit:
+
+ acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
+ return (ACPI_INTERRUPT_HANDLED);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ev_acquire_global_lock
+ *
+ * PARAMETERS: timeout - Max time to wait for the lock, in millisec.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Attempt to gain ownership of the Global Lock.
+ *
+ * MUTEX: Interpreter must be locked
+ *
+ * Note: The original implementation allowed multiple threads to "acquire" the
+ * Global Lock, and the OS would hold the lock until the last thread had
+ * released it. However, this could potentially starve the BIOS out of the
+ * lock, especially in the case where there is a tight handshake between the
+ * Embedded Controller driver and the BIOS. Therefore, this implementation
+ * allows only one thread to acquire the HW Global Lock at a time, and makes
+ * the global lock appear as a standard mutex on the OS side.
+ *
+ *****************************************************************************/
+
+acpi_status acpi_ev_acquire_global_lock(u16 timeout)
+{
+ acpi_cpu_flags flags;
+ acpi_status status;
+ u8 acquired = FALSE;
+
+ ACPI_FUNCTION_TRACE(ev_acquire_global_lock);
+
+ /*
+ * Only one thread can acquire the GL at a time, the global_lock_mutex
+ * enforces this. This interface releases the interpreter if we must wait.
+ */
+ status =
+ acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex->mutex.
+ os_mutex, timeout);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Update the global lock handle and check for wraparound. The handle is
+ * only used for the external global lock interfaces, but it is updated
+ * here to properly handle the case where a single thread may acquire the
+ * lock via both the AML and the acpi_acquire_global_lock interfaces. The
+ * handle is therefore updated on the first acquire from a given thread
+ * regardless of where the acquisition request originated.
+ */
+ acpi_gbl_global_lock_handle++;
+ if (acpi_gbl_global_lock_handle == 0) {
+ acpi_gbl_global_lock_handle = 1;
+ }
+
+ /*
+ * Make sure that a global lock actually exists. If not, just
+ * treat the lock as a standard mutex.
+ */
+ if (!acpi_gbl_global_lock_present) {
+ acpi_gbl_global_lock_acquired = TRUE;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
+
+ do {
+
+ /* Attempt to acquire the actual hardware lock */
+
+ ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired);
+ if (acquired) {
+ acpi_gbl_global_lock_acquired = TRUE;
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Acquired hardware Global Lock\n"));
+ break;
+ }
+
+ /*
+ * Did not get the lock. The pending bit was set above, and
+ * we must now wait until we receive the global lock
+ * released interrupt.
+ */
+ acpi_gbl_global_lock_pending = TRUE;
+ acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Waiting for hardware Global Lock\n"));
+
+ /*
+ * Wait for handshake with the global lock interrupt handler.
+ * This interface releases the interpreter if we must wait.
+ */
+ status =
+ acpi_ex_system_wait_semaphore
+ (acpi_gbl_global_lock_semaphore, ACPI_WAIT_FOREVER);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock);
+
+ } while (ACPI_SUCCESS(status));
+
+ acpi_gbl_global_lock_pending = FALSE;
+ acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags);
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_release_global_lock
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Releases ownership of the Global Lock.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_release_global_lock(void)
+{
+ u8 pending = FALSE;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ev_release_global_lock);
+
+ /* Lock must be already acquired */
+
+ if (!acpi_gbl_global_lock_acquired) {
+ ACPI_WARNING((AE_INFO,
+ "Cannot release the ACPI Global Lock, it has not been acquired"));
+ return_ACPI_STATUS(AE_NOT_ACQUIRED);
+ }
+
+ if (acpi_gbl_global_lock_present) {
+
+ /* Allow any thread to release the lock */
+
+ ACPI_RELEASE_GLOBAL_LOCK(acpi_gbl_FACS, pending);
+
+ /*
+ * If the pending bit was set, we must write GBL_RLS to the control
+ * register
+ */
+ if (pending) {
+ status =
+ acpi_write_bit_register
+ (ACPI_BITREG_GLOBAL_LOCK_RELEASE,
+ ACPI_ENABLE_EVENT);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Released hardware Global Lock\n"));
+ }
+
+ acpi_gbl_global_lock_acquired = FALSE;
+
+ /* Release the local GL mutex */
+
+ acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex);
+ return_ACPI_STATUS(status);
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
new file mode 100644
index 000000000..a6bb480d6
--- /dev/null
+++ b/drivers/acpi/acpica/evgpe.c
@@ -0,0 +1,849 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evgpe - General Purpose Event handling and dispatch
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evgpe")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/* Local prototypes */
+static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context);
+
+static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_update_gpe_enable_mask
+ *
+ * PARAMETERS: gpe_event_info - GPE to update
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Updates GPE register enable mask based upon whether there are
+ * runtime references to this GPE
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info)
+{
+ struct acpi_gpe_register_info *gpe_register_info;
+ u32 register_bit;
+
+ ACPI_FUNCTION_TRACE(ev_update_gpe_enable_mask);
+
+ gpe_register_info = gpe_event_info->register_info;
+ if (!gpe_register_info) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
+
+ /* Clear the run bit up front */
+
+ ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit);
+
+ /* Set the mask bit only if there are references to this GPE */
+
+ if (gpe_event_info->runtime_count) {
+ ACPI_SET_BIT(gpe_register_info->enable_for_run,
+ (u8)register_bit);
+ }
+
+ gpe_register_info->enable_mask = gpe_register_info->enable_for_run;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_enable_gpe
+ *
+ * PARAMETERS: gpe_event_info - GPE to enable
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable a GPE.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_enable_gpe);
+
+ /* Enable the requested GPE */
+
+ status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_mask_gpe
+ *
+ * PARAMETERS: gpe_event_info - GPE to be blocked/unblocked
+ * is_masked - Whether the GPE is masked or not
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Unconditionally mask/unmask a GPE during runtime.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked)
+{
+ struct acpi_gpe_register_info *gpe_register_info;
+ u32 register_bit;
+
+ ACPI_FUNCTION_TRACE(ev_mask_gpe);
+
+ gpe_register_info = gpe_event_info->register_info;
+ if (!gpe_register_info) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
+
+ /* Perform the action */
+
+ if (is_masked) {
+ if (register_bit & gpe_register_info->mask_for_run) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
+ ACPI_SET_BIT(gpe_register_info->mask_for_run, (u8)register_bit);
+ } else {
+ if (!(register_bit & gpe_register_info->mask_for_run)) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ ACPI_CLEAR_BIT(gpe_register_info->mask_for_run,
+ (u8)register_bit);
+ if (gpe_event_info->runtime_count
+ && !gpe_event_info->disable_for_dispatch) {
+ (void)acpi_hw_low_set_gpe(gpe_event_info,
+ ACPI_GPE_ENABLE);
+ }
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_add_gpe_reference
+ *
+ * PARAMETERS: gpe_event_info - Add a reference to this GPE
+ * clear_on_enable - Clear GPE status before enabling it
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is
+ * hardware-enabled.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info,
+ u8 clear_on_enable)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ev_add_gpe_reference);
+
+ if (gpe_event_info->runtime_count == ACPI_UINT8_MAX) {
+ return_ACPI_STATUS(AE_LIMIT);
+ }
+
+ gpe_event_info->runtime_count++;
+ if (gpe_event_info->runtime_count == 1) {
+
+ /* Enable on first reference */
+
+ if (clear_on_enable) {
+ (void)acpi_hw_clear_gpe(gpe_event_info);
+ }
+
+ status = acpi_ev_update_gpe_enable_mask(gpe_event_info);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_ev_enable_gpe(gpe_event_info);
+ }
+
+ if (ACPI_FAILURE(status)) {
+ gpe_event_info->runtime_count--;
+ }
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_remove_gpe_reference
+ *
+ * PARAMETERS: gpe_event_info - Remove a reference to this GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove a reference to a GPE. When the last reference is
+ * removed, the GPE is hardware-disabled.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ev_remove_gpe_reference);
+
+ if (!gpe_event_info->runtime_count) {
+ return_ACPI_STATUS(AE_LIMIT);
+ }
+
+ gpe_event_info->runtime_count--;
+ if (!gpe_event_info->runtime_count) {
+
+ /* Disable on last reference */
+
+ status = acpi_ev_update_gpe_enable_mask(gpe_event_info);
+ if (ACPI_SUCCESS(status)) {
+ status =
+ acpi_hw_low_set_gpe(gpe_event_info,
+ ACPI_GPE_DISABLE);
+ }
+
+ if (ACPI_FAILURE(status)) {
+ gpe_event_info->runtime_count++;
+ }
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_low_get_gpe_info
+ *
+ * PARAMETERS: gpe_number - Raw GPE number
+ * gpe_block - A GPE info block
+ *
+ * RETURN: A GPE event_info struct. NULL if not a valid GPE (The gpe_number
+ * is not within the specified GPE block)
+ *
+ * DESCRIPTION: Returns the event_info struct associated with this GPE. This is
+ * the low-level implementation of ev_get_gpe_event_info.
+ *
+ ******************************************************************************/
+
+struct acpi_gpe_event_info *acpi_ev_low_get_gpe_info(u32 gpe_number,
+ struct acpi_gpe_block_info
+ *gpe_block)
+{
+ u32 gpe_index;
+
+ /*
+ * Validate that the gpe_number is within the specified gpe_block.
+ * (Two steps)
+ */
+ if (!gpe_block || (gpe_number < gpe_block->block_base_number)) {
+ return (NULL);
+ }
+
+ gpe_index = gpe_number - gpe_block->block_base_number;
+ if (gpe_index >= gpe_block->gpe_count) {
+ return (NULL);
+ }
+
+ return (&gpe_block->event_info[gpe_index]);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_get_gpe_event_info
+ *
+ * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1
+ * gpe_number - Raw GPE number
+ *
+ * RETURN: A GPE event_info struct. NULL if not a valid GPE
+ *
+ * DESCRIPTION: Returns the event_info struct associated with this GPE.
+ * Validates the gpe_block and the gpe_number
+ *
+ * Should be called only when the GPE lists are semaphore locked
+ * and not subject to change.
+ *
+ ******************************************************************************/
+
+struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device,
+ u32 gpe_number)
+{
+ union acpi_operand_object *obj_desc;
+ struct acpi_gpe_event_info *gpe_info;
+ u32 i;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* A NULL gpe_device means use the FADT-defined GPE block(s) */
+
+ if (!gpe_device) {
+
+ /* Examine GPE Block 0 and 1 (These blocks are permanent) */
+
+ for (i = 0; i < ACPI_MAX_GPE_BLOCKS; i++) {
+ gpe_info = acpi_ev_low_get_gpe_info(gpe_number,
+ acpi_gbl_gpe_fadt_blocks
+ [i]);
+ if (gpe_info) {
+ return (gpe_info);
+ }
+ }
+
+ /* The gpe_number was not in the range of either FADT GPE block */
+
+ return (NULL);
+ }
+
+ /* A Non-NULL gpe_device means this is a GPE Block Device */
+
+ obj_desc =
+ acpi_ns_get_attached_object((struct acpi_namespace_node *)
+ gpe_device);
+ if (!obj_desc || !obj_desc->device.gpe_block) {
+ return (NULL);
+ }
+
+ return (acpi_ev_low_get_gpe_info
+ (gpe_number, obj_desc->device.gpe_block));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_gpe_detect
+ *
+ * PARAMETERS: gpe_xrupt_list - Interrupt block for this interrupt.
+ * Can have multiple GPE blocks attached.
+ *
+ * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
+ *
+ * DESCRIPTION: Detect if any GP events have occurred. This function is
+ * executed at interrupt level.
+ *
+ ******************************************************************************/
+
+u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list)
+{
+ struct acpi_gpe_block_info *gpe_block;
+ struct acpi_namespace_node *gpe_device;
+ struct acpi_gpe_register_info *gpe_register_info;
+ struct acpi_gpe_event_info *gpe_event_info;
+ u32 gpe_number;
+ u32 int_status = ACPI_INTERRUPT_NOT_HANDLED;
+ acpi_cpu_flags flags;
+ u32 i;
+ u32 j;
+
+ ACPI_FUNCTION_NAME(ev_gpe_detect);
+
+ /* Check for the case where there are no GPEs */
+
+ if (!gpe_xrupt_list) {
+ return (int_status);
+ }
+
+ /*
+ * We need to obtain the GPE lock for both the data structs and registers
+ * Note: Not necessary to obtain the hardware lock, since the GPE
+ * registers are owned by the gpe_lock.
+ */
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Examine all GPE blocks attached to this interrupt level */
+
+ gpe_block = gpe_xrupt_list->gpe_block_list_head;
+ while (gpe_block) {
+ gpe_device = gpe_block->node;
+
+ /*
+ * Read all of the 8-bit GPE status and enable registers in this GPE
+ * block, saving all of them. Find all currently active GP events.
+ */
+ for (i = 0; i < gpe_block->register_count; i++) {
+
+ /* Get the next status/enable pair */
+
+ gpe_register_info = &gpe_block->register_info[i];
+
+ /*
+ * Optimization: If there are no GPEs enabled within this
+ * register, we can safely ignore the entire register.
+ */
+ if (!(gpe_register_info->enable_for_run |
+ gpe_register_info->enable_for_wake)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS,
+ "Ignore disabled registers for GPE %02X-%02X: "
+ "RunEnable=%02X, WakeEnable=%02X\n",
+ gpe_register_info->
+ base_gpe_number,
+ gpe_register_info->
+ base_gpe_number +
+ (ACPI_GPE_REGISTER_WIDTH - 1),
+ gpe_register_info->
+ enable_for_run,
+ gpe_register_info->
+ enable_for_wake));
+ continue;
+ }
+
+ /* Now look at the individual GPEs in this byte register */
+
+ for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
+
+ /* Detect and dispatch one GPE bit */
+
+ gpe_event_info =
+ &gpe_block->
+ event_info[((acpi_size)i *
+ ACPI_GPE_REGISTER_WIDTH) + j];
+ gpe_number =
+ j + gpe_register_info->base_gpe_number;
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ int_status |=
+ acpi_ev_detect_gpe(gpe_device,
+ gpe_event_info,
+ gpe_number);
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ }
+ }
+
+ gpe_block = gpe_block->next;
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return (int_status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_asynch_execute_gpe_method
+ *
+ * PARAMETERS: Context (gpe_event_info) - Info for this GPE
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Perform the actual execution of a GPE control method. This
+ * function is called from an invocation of acpi_os_execute and
+ * therefore does NOT execute at interrupt level - so that
+ * the control method itself is not executed in the context of
+ * an interrupt handler.
+ *
+ ******************************************************************************/
+
+static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
+{
+ struct acpi_gpe_event_info *gpe_event_info = context;
+ acpi_status status = AE_OK;
+ struct acpi_evaluate_info *info;
+ struct acpi_gpe_notify_info *notify;
+
+ ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method);
+
+ /* Do the correct dispatch - normal method or implicit notify */
+
+ switch (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)) {
+ case ACPI_GPE_DISPATCH_NOTIFY:
+ /*
+ * Implicit notify.
+ * Dispatch a DEVICE_WAKE notify to the appropriate handler.
+ * NOTE: the request is queued for execution after this method
+ * completes. The notify handlers are NOT invoked synchronously
+ * from this thread -- because handlers may in turn run other
+ * control methods.
+ *
+ * June 2012: Expand implicit notify mechanism to support
+ * notifies on multiple device objects.
+ */
+ notify = gpe_event_info->dispatch.notify_list;
+ while (ACPI_SUCCESS(status) && notify) {
+ status =
+ acpi_ev_queue_notify_request(notify->device_node,
+ ACPI_NOTIFY_DEVICE_WAKE);
+
+ notify = notify->next;
+ }
+
+ break;
+
+ case ACPI_GPE_DISPATCH_METHOD:
+
+ /* Allocate the evaluation information block */
+
+ info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
+ if (!info) {
+ status = AE_NO_MEMORY;
+ } else {
+ /*
+ * Invoke the GPE Method (_Lxx, _Exx) i.e., evaluate the
+ * _Lxx/_Exx control method that corresponds to this GPE
+ */
+ info->prefix_node =
+ gpe_event_info->dispatch.method_node;
+ info->flags = ACPI_IGNORE_RETURN_VALUE;
+
+ status = acpi_ns_evaluate(info);
+ ACPI_FREE(info);
+ }
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "while evaluating GPE method [%4.4s]",
+ acpi_ut_get_node_name(gpe_event_info->
+ dispatch.
+ method_node)));
+ }
+ break;
+
+ default:
+
+ goto error_exit; /* Should never happen */
+ }
+
+ /* Defer enabling of GPE until all notify handlers are done */
+
+ status = acpi_os_execute(OSL_NOTIFY_HANDLER,
+ acpi_ev_asynch_enable_gpe, gpe_event_info);
+ if (ACPI_SUCCESS(status)) {
+ return_VOID;
+ }
+
+error_exit:
+ acpi_ev_asynch_enable_gpe(gpe_event_info);
+ return_VOID;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_asynch_enable_gpe
+ *
+ * PARAMETERS: Context (gpe_event_info) - Info for this GPE
+ * Callback from acpi_os_execute
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Asynchronous clear/enable for GPE. This allows the GPE to
+ * complete (i.e., finish execution of Notify)
+ *
+ ******************************************************************************/
+
+static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context)
+{
+ struct acpi_gpe_event_info *gpe_event_info = context;
+ acpi_cpu_flags flags;
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ (void)acpi_ev_finish_gpe(gpe_event_info);
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+
+ return;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_finish_gpe
+ *
+ * PARAMETERS: gpe_event_info - Info for this GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Clear/Enable a GPE. Common code that is used after execution
+ * of a GPE method or a synchronous or asynchronous GPE handler.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info)
+{
+ acpi_status status;
+
+ if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) ==
+ ACPI_GPE_LEVEL_TRIGGERED) {
+ /*
+ * GPE is level-triggered, we clear the GPE status bit after
+ * handling the event.
+ */
+ status = acpi_hw_clear_gpe(gpe_event_info);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ /*
+ * Enable this GPE, conditionally. This means that the GPE will
+ * only be physically enabled if the enable_mask bit is set
+ * in the event_info.
+ */
+ (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE);
+ gpe_event_info->disable_for_dispatch = FALSE;
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_detect_gpe
+ *
+ * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1
+ * gpe_event_info - Info for this GPE
+ * gpe_number - Number relative to the parent GPE block
+ *
+ * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
+ *
+ * DESCRIPTION: Detect and dispatch a General Purpose Event to either a function
+ * (e.g. EC) or method (e.g. _Lxx/_Exx) handler.
+ * NOTE: GPE is W1C, so it is possible to handle a single GPE from both
+ * task and irq context in parallel as long as the process to
+ * detect and mask the GPE is atomic.
+ * However the atomicity of ACPI_GPE_DISPATCH_RAW_HANDLER is
+ * dependent on the raw handler itself.
+ *
+ ******************************************************************************/
+
+u32
+acpi_ev_detect_gpe(struct acpi_namespace_node *gpe_device,
+ struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number)
+{
+ u32 int_status = ACPI_INTERRUPT_NOT_HANDLED;
+ u8 enabled_status_byte;
+ u64 status_reg;
+ u64 enable_reg;
+ u32 register_bit;
+ struct acpi_gpe_register_info *gpe_register_info;
+ struct acpi_gpe_handler_info *gpe_handler_info;
+ acpi_cpu_flags flags;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_gpe_detect);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ if (!gpe_event_info) {
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info)
+ goto error_exit;
+ }
+
+ /* Get the info block for the entire GPE register */
+
+ gpe_register_info = gpe_event_info->register_info;
+
+ /* Get the register bitmask for this GPE */
+
+ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
+
+ /* GPE currently enabled (enable bit == 1)? */
+
+ status = acpi_hw_gpe_read(&enable_reg, &gpe_register_info->enable_address);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ /* GPE currently active (status bit == 1)? */
+
+ status = acpi_hw_gpe_read(&status_reg, &gpe_register_info->status_address);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ /* Check if there is anything active at all in this GPE */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS,
+ "Read registers for GPE %02X: Status=%02X, Enable=%02X, "
+ "RunEnable=%02X, WakeEnable=%02X\n",
+ gpe_number,
+ (u32)(status_reg & register_bit),
+ (u32)(enable_reg & register_bit),
+ gpe_register_info->enable_for_run,
+ gpe_register_info->enable_for_wake));
+
+ enabled_status_byte = (u8)(status_reg & enable_reg);
+ if (!(enabled_status_byte & register_bit)) {
+ goto error_exit;
+ }
+
+ /* Invoke global event handler if present */
+
+ acpi_gpe_count++;
+ if (acpi_gbl_global_event_handler) {
+ acpi_gbl_global_event_handler(ACPI_EVENT_TYPE_GPE,
+ gpe_device, gpe_number,
+ acpi_gbl_global_event_handler_context);
+ }
+
+ /* Found an active GPE */
+
+ if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_RAW_HANDLER) {
+
+ /* Dispatch the event to a raw handler */
+
+ gpe_handler_info = gpe_event_info->dispatch.handler;
+
+ /*
+ * There is no protection around the namespace node
+ * and the GPE handler to ensure a safe destruction
+ * because:
+ * 1. The namespace node is expected to always
+ * exist after loading a table.
+ * 2. The GPE handler is expected to be flushed by
+ * acpi_os_wait_events_complete() before the
+ * destruction.
+ */
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ int_status |=
+ gpe_handler_info->address(gpe_device, gpe_number,
+ gpe_handler_info->context);
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ } else {
+ /* Dispatch the event to a standard handler or method. */
+
+ int_status |= acpi_ev_gpe_dispatch(gpe_device,
+ gpe_event_info, gpe_number);
+ }
+
+error_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return (int_status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_gpe_dispatch
+ *
+ * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1
+ * gpe_event_info - Info for this GPE
+ * gpe_number - Number relative to the parent GPE block
+ *
+ * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
+ *
+ * DESCRIPTION: Dispatch a General Purpose Event to either a function (e.g. EC)
+ * or method (e.g. _Lxx/_Exx) handler.
+ *
+ ******************************************************************************/
+
+u32
+acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
+ struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number)
+{
+ acpi_status status;
+ u32 return_value;
+
+ ACPI_FUNCTION_TRACE(ev_gpe_dispatch);
+
+ /*
+ * Always disable the GPE so that it does not keep firing before
+ * any asynchronous activity completes (either from the execution
+ * of a GPE method or an asynchronous GPE handler.)
+ *
+ * If there is no handler or method to run, just disable the
+ * GPE and leave it disabled permanently to prevent further such
+ * pointless events from firing.
+ */
+ status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Unable to disable GPE %02X", gpe_number));
+ return_UINT32(ACPI_INTERRUPT_NOT_HANDLED);
+ }
+
+ /*
+ * If edge-triggered, clear the GPE status bit now. Note that
+ * level-triggered events are cleared after the GPE is serviced.
+ */
+ if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) ==
+ ACPI_GPE_EDGE_TRIGGERED) {
+ status = acpi_hw_clear_gpe(gpe_event_info);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Unable to clear GPE %02X",
+ gpe_number));
+ (void)acpi_hw_low_set_gpe(gpe_event_info,
+ ACPI_GPE_CONDITIONAL_ENABLE);
+ return_UINT32(ACPI_INTERRUPT_NOT_HANDLED);
+ }
+ }
+
+ gpe_event_info->disable_for_dispatch = TRUE;
+
+ /*
+ * Dispatch the GPE to either an installed handler or the control
+ * method associated with this GPE (_Lxx or _Exx). If a handler
+ * exists, we invoke it and do not attempt to run the method.
+ * If there is neither a handler nor a method, leave the GPE
+ * disabled.
+ */
+ switch (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)) {
+ case ACPI_GPE_DISPATCH_HANDLER:
+
+ /* Invoke the installed handler (at interrupt level) */
+
+ return_value =
+ gpe_event_info->dispatch.handler->address(gpe_device,
+ gpe_number,
+ gpe_event_info->
+ dispatch.handler->
+ context);
+
+ /* If requested, clear (if level-triggered) and re-enable the GPE */
+
+ if (return_value & ACPI_REENABLE_GPE) {
+ (void)acpi_ev_finish_gpe(gpe_event_info);
+ }
+ break;
+
+ case ACPI_GPE_DISPATCH_METHOD:
+ case ACPI_GPE_DISPATCH_NOTIFY:
+ /*
+ * Execute the method associated with the GPE
+ * NOTE: Level-triggered GPEs are cleared after the method completes.
+ */
+ status = acpi_os_execute(OSL_GPE_HANDLER,
+ acpi_ev_asynch_execute_gpe_method,
+ gpe_event_info);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Unable to queue handler for GPE %02X - event disabled",
+ gpe_number));
+ }
+ break;
+
+ default:
+ /*
+ * No handler or method to run!
+ * 03/2010: This case should no longer be possible. We will not allow
+ * a GPE to be enabled if it has no handler or method.
+ */
+ ACPI_ERROR((AE_INFO,
+ "No handler or method for GPE %02X, disabling event",
+ gpe_number));
+
+ break;
+ }
+
+ return_UINT32(ACPI_INTERRUPT_HANDLED);
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c
new file mode 100644
index 000000000..39fe45663
--- /dev/null
+++ b/drivers/acpi/acpica/evgpeblk.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evgpeblk - GPE block creation and initialization.
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evgpeblk")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/* Local prototypes */
+static acpi_status
+acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block,
+ u32 interrupt_number);
+
+static acpi_status
+acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_install_gpe_block
+ *
+ * PARAMETERS: gpe_block - New GPE block
+ * interrupt_number - Xrupt to be associated with this
+ * GPE block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install new GPE block with mutex support
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block,
+ u32 interrupt_number)
+{
+ struct acpi_gpe_block_info *next_gpe_block;
+ struct acpi_gpe_xrupt_info *gpe_xrupt_block;
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(ev_install_gpe_block);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status =
+ acpi_ev_get_gpe_xrupt_block(interrupt_number, &gpe_xrupt_block);
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+
+ /* Install the new block at the end of the list with lock */
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ if (gpe_xrupt_block->gpe_block_list_head) {
+ next_gpe_block = gpe_xrupt_block->gpe_block_list_head;
+ while (next_gpe_block->next) {
+ next_gpe_block = next_gpe_block->next;
+ }
+
+ next_gpe_block->next = gpe_block;
+ gpe_block->previous = next_gpe_block;
+ } else {
+ gpe_xrupt_block->gpe_block_list_head = gpe_block;
+ }
+
+ gpe_block->xrupt_block = gpe_xrupt_block;
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_delete_gpe_block
+ *
+ * PARAMETERS: gpe_block - Existing GPE block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove a GPE block
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block)
+{
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(ev_install_gpe_block);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Disable all GPEs in this block */
+
+ status =
+ acpi_hw_disable_gpe_block(gpe_block->xrupt_block, gpe_block, NULL);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (!gpe_block->previous && !gpe_block->next) {
+
+ /* This is the last gpe_block on this interrupt */
+
+ status = acpi_ev_delete_gpe_xrupt(gpe_block->xrupt_block);
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+ } else {
+ /* Remove the block on this interrupt with lock */
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ if (gpe_block->previous) {
+ gpe_block->previous->next = gpe_block->next;
+ } else {
+ gpe_block->xrupt_block->gpe_block_list_head =
+ gpe_block->next;
+ }
+
+ if (gpe_block->next) {
+ gpe_block->next->previous = gpe_block->previous;
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ }
+
+ acpi_current_gpe_count -= gpe_block->gpe_count;
+
+ /* Free the gpe_block */
+
+ ACPI_FREE(gpe_block->register_info);
+ ACPI_FREE(gpe_block->event_info);
+ ACPI_FREE(gpe_block);
+
+unlock_and_exit:
+ status = acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_create_gpe_info_blocks
+ *
+ * PARAMETERS: gpe_block - New GPE block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create the register_info and event_info blocks for this GPE block
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block)
+{
+ struct acpi_gpe_register_info *gpe_register_info = NULL;
+ struct acpi_gpe_event_info *gpe_event_info = NULL;
+ struct acpi_gpe_event_info *this_event;
+ struct acpi_gpe_register_info *this_register;
+ u32 i;
+ u32 j;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_create_gpe_info_blocks);
+
+ /* Allocate the GPE register information block */
+
+ gpe_register_info = ACPI_ALLOCATE_ZEROED((acpi_size)gpe_block->
+ register_count *
+ sizeof(struct
+ acpi_gpe_register_info));
+ if (!gpe_register_info) {
+ ACPI_ERROR((AE_INFO,
+ "Could not allocate the GpeRegisterInfo table"));
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /*
+ * Allocate the GPE event_info block. There are eight distinct GPEs
+ * per register. Initialization to zeros is sufficient.
+ */
+ gpe_event_info = ACPI_ALLOCATE_ZEROED((acpi_size)gpe_block->gpe_count *
+ sizeof(struct
+ acpi_gpe_event_info));
+ if (!gpe_event_info) {
+ ACPI_ERROR((AE_INFO,
+ "Could not allocate the GpeEventInfo table"));
+ status = AE_NO_MEMORY;
+ goto error_exit;
+ }
+
+ /* Save the new Info arrays in the GPE block */
+
+ gpe_block->register_info = gpe_register_info;
+ gpe_block->event_info = gpe_event_info;
+
+ /*
+ * Initialize the GPE Register and Event structures. A goal of these
+ * tables is to hide the fact that there are two separate GPE register
+ * sets in a given GPE hardware block, the status registers occupy the
+ * first half, and the enable registers occupy the second half.
+ */
+ this_register = gpe_register_info;
+ this_event = gpe_event_info;
+
+ for (i = 0; i < gpe_block->register_count; i++) {
+
+ /* Init the register_info for this GPE register (8 GPEs) */
+
+ this_register->base_gpe_number = (u16)
+ (gpe_block->block_base_number +
+ (i * ACPI_GPE_REGISTER_WIDTH));
+
+ this_register->status_address.address = gpe_block->address + i;
+
+ this_register->enable_address.address =
+ gpe_block->address + i + gpe_block->register_count;
+
+ this_register->status_address.space_id = gpe_block->space_id;
+ this_register->enable_address.space_id = gpe_block->space_id;
+
+ /* Init the event_info for each GPE within this register */
+
+ for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
+ this_event->gpe_number =
+ (u8) (this_register->base_gpe_number + j);
+ this_event->register_info = this_register;
+ this_event++;
+ }
+
+ /* Disable all GPEs within this register */
+
+ status = acpi_hw_gpe_write(0x00, &this_register->enable_address);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ /* Clear any pending GPE events within this register */
+
+ status = acpi_hw_gpe_write(0xFF, &this_register->status_address);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ this_register++;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+
+error_exit:
+ if (gpe_register_info) {
+ ACPI_FREE(gpe_register_info);
+ }
+ if (gpe_event_info) {
+ ACPI_FREE(gpe_event_info);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_create_gpe_block
+ *
+ * PARAMETERS: gpe_device - Handle to the parent GPE block
+ * gpe_block_address - Address and space_ID
+ * register_count - Number of GPE register pairs in the block
+ * gpe_block_base_number - Starting GPE number for the block
+ * interrupt_number - H/W interrupt for the block
+ * return_gpe_block - Where the new block descriptor is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create and Install a block of GPE registers. All GPEs within
+ * the block are disabled at exit.
+ * Note: Assumes namespace is locked.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
+ u64 address,
+ u8 space_id,
+ u32 register_count,
+ u16 gpe_block_base_number,
+ u32 interrupt_number,
+ struct acpi_gpe_block_info **return_gpe_block)
+{
+ acpi_status status;
+ struct acpi_gpe_block_info *gpe_block;
+ struct acpi_gpe_walk_info walk_info;
+
+ ACPI_FUNCTION_TRACE(ev_create_gpe_block);
+
+ if (!register_count) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Validate the space_ID */
+
+ if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
+ (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
+ ACPI_ERROR((AE_INFO,
+ "Unsupported address space: 0x%X", space_id));
+ return_ACPI_STATUS(AE_SUPPORT);
+ }
+
+ if (space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ status = acpi_hw_validate_io_block(address,
+ ACPI_GPE_REGISTER_WIDTH,
+ register_count);
+ if (ACPI_FAILURE(status))
+ return_ACPI_STATUS(status);
+ }
+
+ /* Allocate a new GPE block */
+
+ gpe_block = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_block_info));
+ if (!gpe_block) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Initialize the new GPE block */
+
+ gpe_block->address = address;
+ gpe_block->space_id = space_id;
+ gpe_block->node = gpe_device;
+ gpe_block->gpe_count = (u16)(register_count * ACPI_GPE_REGISTER_WIDTH);
+ gpe_block->initialized = FALSE;
+ gpe_block->register_count = register_count;
+ gpe_block->block_base_number = gpe_block_base_number;
+
+ /*
+ * Create the register_info and event_info sub-structures
+ * Note: disables and clears all GPEs in the block
+ */
+ status = acpi_ev_create_gpe_info_blocks(gpe_block);
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(gpe_block);
+ return_ACPI_STATUS(status);
+ }
+
+ /* Install the new block in the global lists */
+
+ status = acpi_ev_install_gpe_block(gpe_block, interrupt_number);
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(gpe_block->register_info);
+ ACPI_FREE(gpe_block->event_info);
+ ACPI_FREE(gpe_block);
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_gbl_all_gpes_initialized = FALSE;
+
+ /* Find all GPE methods (_Lxx or_Exx) for this block */
+
+ walk_info.gpe_block = gpe_block;
+ walk_info.gpe_device = gpe_device;
+ walk_info.execute_by_owner_id = FALSE;
+
+ (void)acpi_ns_walk_namespace(ACPI_TYPE_METHOD, gpe_device,
+ ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
+ acpi_ev_match_gpe_method, NULL, &walk_info,
+ NULL);
+
+ /* Return the new block */
+
+ if (return_gpe_block) {
+ (*return_gpe_block) = gpe_block;
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
+ " Initialized GPE %02X to %02X [%4.4s] %u regs on interrupt 0x%X%s\n",
+ (u32)gpe_block->block_base_number,
+ (u32)(gpe_block->block_base_number +
+ (gpe_block->gpe_count - 1)),
+ gpe_device->name.ascii, gpe_block->register_count,
+ interrupt_number,
+ interrupt_number ==
+ acpi_gbl_FADT.sci_interrupt ? " (SCI)" : ""));
+
+ /* Update global count of currently available GPEs */
+
+ acpi_current_gpe_count += gpe_block->gpe_count;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_initialize_gpe_block
+ *
+ * PARAMETERS: acpi_gpe_callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize and enable a GPE block. Enable GPEs that have
+ * associated methods.
+ * Note: Assumes namespace is locked.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *context)
+{
+ acpi_status status;
+ struct acpi_gpe_event_info *gpe_event_info;
+ u32 gpe_enabled_count;
+ u32 gpe_index;
+ u32 i;
+ u32 j;
+ u8 *is_polling_needed = context;
+ ACPI_ERROR_ONLY(u32 gpe_number);
+
+ ACPI_FUNCTION_TRACE(ev_initialize_gpe_block);
+
+ /*
+ * Ignore a null GPE block (e.g., if no GPE block 1 exists), and
+ * any GPE blocks that have been initialized already.
+ */
+ if (!gpe_block || gpe_block->initialized) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Enable all GPEs that have a corresponding method and have the
+ * ACPI_GPE_CAN_WAKE flag unset. Any other GPEs within this block
+ * must be enabled via the acpi_enable_gpe() interface.
+ */
+ gpe_enabled_count = 0;
+
+ for (i = 0; i < gpe_block->register_count; i++) {
+ for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
+
+ /* Get the info block for this particular GPE */
+
+ gpe_index = (i * ACPI_GPE_REGISTER_WIDTH) + j;
+ gpe_event_info = &gpe_block->event_info[gpe_index];
+ ACPI_ERROR_ONLY(gpe_number =
+ gpe_block->block_base_number +
+ gpe_index);
+ gpe_event_info->flags |= ACPI_GPE_INITIALIZED;
+
+ /*
+ * Ignore GPEs that have no corresponding _Lxx/_Exx method
+ * and GPEs that are used for wakeup
+ */
+ if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) !=
+ ACPI_GPE_DISPATCH_METHOD)
+ || (gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) {
+ continue;
+ }
+
+ status = acpi_ev_add_gpe_reference(gpe_event_info, FALSE);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not enable GPE 0x%02X",
+ gpe_number));
+ continue;
+ }
+
+ gpe_event_info->flags |= ACPI_GPE_AUTO_ENABLED;
+
+ if (is_polling_needed &&
+ ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) {
+ *is_polling_needed = TRUE;
+ }
+
+ gpe_enabled_count++;
+ }
+ }
+
+ if (gpe_enabled_count) {
+ ACPI_INFO(("Enabled %u GPEs in block %02X to %02X",
+ gpe_enabled_count, (u32)gpe_block->block_base_number,
+ (u32)(gpe_block->block_base_number +
+ (gpe_block->gpe_count - 1))));
+ }
+
+ gpe_block->initialized = TRUE;
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c
new file mode 100644
index 000000000..2f1a75fee
--- /dev/null
+++ b/drivers/acpi/acpica/evgpeinit.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evgpeinit - System GPE initialization and update
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evgpeinit")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/*
+ * Note: History of _PRW support in ACPICA
+ *
+ * Originally (2000 - 2010), the GPE initialization code performed a walk of
+ * the entire namespace to execute the _PRW methods and detect all GPEs
+ * capable of waking the system.
+ *
+ * As of 10/2010, the _PRW method execution has been removed since it is
+ * actually unnecessary. The host OS must in fact execute all _PRW methods
+ * in order to identify the device/power-resource dependencies. We now put
+ * the onus on the host OS to identify the wake GPEs as part of this process
+ * and to inform ACPICA of these GPEs via the acpi_setup_gpe_for_wake interface. This
+ * not only reduces the complexity of the ACPICA initialization code, but in
+ * some cases (on systems with very large namespaces) it should reduce the
+ * kernel boot time as well.
+ */
+
+#ifdef ACPI_GPE_USE_LOGICAL_ADDRESSES
+#define ACPI_FADT_GPE_BLOCK_ADDRESS(N) \
+ acpi_gbl_FADT.xgpe##N##_block.space_id == \
+ ACPI_ADR_SPACE_SYSTEM_MEMORY ? \
+ (u64)acpi_gbl_xgpe##N##_block_logical_address : \
+ acpi_gbl_FADT.xgpe##N##_block.address
+#else
+#define ACPI_FADT_GPE_BLOCK_ADDRESS(N) acpi_gbl_FADT.xgpe##N##_block.address
+#endif /* ACPI_GPE_USE_LOGICAL_ADDRESSES */
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_gpe_initialize
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize the GPE data structures and the FADT GPE 0/1 blocks
+ *
+ ******************************************************************************/
+acpi_status acpi_ev_gpe_initialize(void)
+{
+ u32 register_count0 = 0;
+ u32 register_count1 = 0;
+ u32 gpe_number_max = 0;
+ acpi_status status;
+ u64 address;
+
+ ACPI_FUNCTION_TRACE(ev_gpe_initialize);
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
+ "Initializing General Purpose Events (GPEs):\n"));
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Initialize the GPE Block(s) defined in the FADT
+ *
+ * Why the GPE register block lengths are divided by 2: From the ACPI
+ * Spec, section "General-Purpose Event Registers", we have:
+ *
+ * "Each register block contains two registers of equal length
+ * GPEx_STS and GPEx_EN (where x is 0 or 1). The length of the
+ * GPE0_STS and GPE0_EN registers is equal to half the GPE0_LEN
+ * The length of the GPE1_STS and GPE1_EN registers is equal to
+ * half the GPE1_LEN. If a generic register block is not supported
+ * then its respective block pointer and block length values in the
+ * FADT table contain zeros. The GPE0_LEN and GPE1_LEN do not need
+ * to be the same size."
+ */
+
+ /*
+ * Determine the maximum GPE number for this machine.
+ *
+ * Note: both GPE0 and GPE1 are optional, and either can exist without
+ * the other.
+ *
+ * If EITHER the register length OR the block address are zero, then that
+ * particular block is not supported.
+ */
+ address = ACPI_FADT_GPE_BLOCK_ADDRESS(0);
+
+ if (acpi_gbl_FADT.gpe0_block_length && address) {
+
+ /* GPE block 0 exists (has both length and address > 0) */
+
+ register_count0 = (u16)(acpi_gbl_FADT.gpe0_block_length / 2);
+ gpe_number_max =
+ (register_count0 * ACPI_GPE_REGISTER_WIDTH) - 1;
+
+ /* Install GPE Block 0 */
+
+ status = acpi_ev_create_gpe_block(acpi_gbl_fadt_gpe_device,
+ address,
+ acpi_gbl_FADT.xgpe0_block.
+ space_id, register_count0, 0,
+ acpi_gbl_FADT.sci_interrupt,
+ &acpi_gbl_gpe_fadt_blocks[0]);
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not create GPE Block 0"));
+ }
+ }
+
+ address = ACPI_FADT_GPE_BLOCK_ADDRESS(1);
+
+ if (acpi_gbl_FADT.gpe1_block_length && address) {
+
+ /* GPE block 1 exists (has both length and address > 0) */
+
+ register_count1 = (u16)(acpi_gbl_FADT.gpe1_block_length / 2);
+
+ /* Check for GPE0/GPE1 overlap (if both banks exist) */
+
+ if ((register_count0) &&
+ (gpe_number_max >= acpi_gbl_FADT.gpe1_base)) {
+ ACPI_ERROR((AE_INFO,
+ "GPE0 block (GPE 0 to %u) overlaps the GPE1 block "
+ "(GPE %u to %u) - Ignoring GPE1",
+ gpe_number_max, acpi_gbl_FADT.gpe1_base,
+ acpi_gbl_FADT.gpe1_base +
+ ((register_count1 *
+ ACPI_GPE_REGISTER_WIDTH) - 1)));
+
+ /* Ignore GPE1 block by setting the register count to zero */
+
+ register_count1 = 0;
+ } else {
+ /* Install GPE Block 1 */
+
+ status =
+ acpi_ev_create_gpe_block(acpi_gbl_fadt_gpe_device,
+ address,
+ acpi_gbl_FADT.xgpe1_block.
+ space_id, register_count1,
+ acpi_gbl_FADT.gpe1_base,
+ acpi_gbl_FADT.
+ sci_interrupt,
+ &acpi_gbl_gpe_fadt_blocks
+ [1]);
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not create GPE Block 1"));
+ }
+
+ /*
+ * GPE0 and GPE1 do not have to be contiguous in the GPE number
+ * space. However, GPE0 always starts at GPE number zero.
+ */
+ }
+ }
+
+ /* Exit if there are no GPE registers */
+
+ if ((register_count0 + register_count1) == 0) {
+
+ /* GPEs are not required by ACPI, this is OK */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INIT,
+ "There are no GPE blocks defined in the FADT\n"));
+ goto cleanup;
+ }
+
+cleanup:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_update_gpes
+ *
+ * PARAMETERS: table_owner_id - ID of the newly-loaded ACPI table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Check for new GPE methods (_Lxx/_Exx) made available as a
+ * result of a Load() or load_table() operation. If new GPE
+ * methods have been installed, register the new methods.
+ *
+ ******************************************************************************/
+
+void acpi_ev_update_gpes(acpi_owner_id table_owner_id)
+{
+ struct acpi_gpe_xrupt_info *gpe_xrupt_info;
+ struct acpi_gpe_block_info *gpe_block;
+ struct acpi_gpe_walk_info walk_info;
+ acpi_status status = AE_OK;
+
+ /*
+ * Find any _Lxx/_Exx GPE methods that have just been loaded.
+ *
+ * Any GPEs that correspond to new _Lxx/_Exx methods are immediately
+ * enabled.
+ *
+ * Examine the namespace underneath each gpe_device within the
+ * gpe_block lists.
+ */
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+
+ walk_info.count = 0;
+ walk_info.owner_id = table_owner_id;
+ walk_info.execute_by_owner_id = TRUE;
+
+ /* Walk the interrupt level descriptor list */
+
+ gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head;
+ while (gpe_xrupt_info) {
+
+ /* Walk all Gpe Blocks attached to this interrupt level */
+
+ gpe_block = gpe_xrupt_info->gpe_block_list_head;
+ while (gpe_block) {
+ walk_info.gpe_block = gpe_block;
+ walk_info.gpe_device = gpe_block->node;
+
+ status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD,
+ walk_info.gpe_device,
+ ACPI_UINT32_MAX,
+ ACPI_NS_WALK_NO_UNLOCK,
+ acpi_ev_match_gpe_method,
+ NULL, &walk_info, NULL);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While decoding _Lxx/_Exx methods"));
+ }
+
+ gpe_block = gpe_block->next;
+ }
+
+ gpe_xrupt_info = gpe_xrupt_info->next;
+ }
+
+ if (walk_info.count) {
+ ACPI_INFO(("Enabled %u new GPEs", walk_info.count));
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_match_gpe_method
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Called from acpi_walk_namespace. Expects each object to be a
+ * control method under the _GPE portion of the namespace.
+ * Extract the name and GPE type from the object, saving this
+ * information for quick lookup during GPE dispatch. Allows a
+ * per-owner_id evaluation if execute_by_owner_id is TRUE in the
+ * walk_info parameter block.
+ *
+ * The name of each GPE control method is of the form:
+ * "_Lxx" or "_Exx", where:
+ * L - means that the GPE is level triggered
+ * E - means that the GPE is edge triggered
+ * xx - is the GPE number [in HEX]
+ *
+ * If walk_info->execute_by_owner_id is TRUE, we only execute examine GPE methods
+ * with that owner.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_match_gpe_method(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value)
+{
+ struct acpi_namespace_node *method_node =
+ ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle);
+ struct acpi_gpe_walk_info *walk_info =
+ ACPI_CAST_PTR(struct acpi_gpe_walk_info, context);
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_status status;
+ u32 gpe_number;
+ u8 temp_gpe_number;
+ char name[ACPI_NAMESEG_SIZE + 1];
+ u8 type;
+
+ ACPI_FUNCTION_TRACE(ev_match_gpe_method);
+
+ /* Check if requested owner_id matches this owner_id */
+
+ if ((walk_info->execute_by_owner_id) &&
+ (method_node->owner_id != walk_info->owner_id)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Match and decode the _Lxx and _Exx GPE method names
+ *
+ * 1) Extract the method name and null terminate it
+ */
+ ACPI_MOVE_32_TO_32(name, &method_node->name.integer);
+ name[ACPI_NAMESEG_SIZE] = 0;
+
+ /* 2) Name must begin with an underscore */
+
+ if (name[0] != '_') {
+ return_ACPI_STATUS(AE_OK); /* Ignore this method */
+ }
+
+ /*
+ * 3) Edge/Level determination is based on the 2nd character
+ * of the method name
+ */
+ switch (name[1]) {
+ case 'L':
+
+ type = ACPI_GPE_LEVEL_TRIGGERED;
+ break;
+
+ case 'E':
+
+ type = ACPI_GPE_EDGE_TRIGGERED;
+ break;
+
+ default:
+
+ /* Unknown method type, just ignore it */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
+ "Ignoring unknown GPE method type: %s "
+ "(name not of form _Lxx or _Exx)", name));
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* 4) The last two characters of the name are the hex GPE Number */
+
+ status = acpi_ut_ascii_to_hex_byte(&name[2], &temp_gpe_number);
+ if (ACPI_FAILURE(status)) {
+
+ /* Conversion failed; invalid method, just ignore it */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
+ "Could not extract GPE number from name: %s "
+ "(name is not of form _Lxx or _Exx)", name));
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Ensure that we have a valid GPE number for this GPE block */
+
+ gpe_number = (u32)temp_gpe_number;
+ gpe_event_info =
+ acpi_ev_low_get_gpe_info(gpe_number, walk_info->gpe_block);
+ if (!gpe_event_info) {
+ /*
+ * This gpe_number is not valid for this GPE block, just ignore it.
+ * However, it may be valid for a different GPE block, since GPE0
+ * and GPE1 methods both appear under \_GPE.
+ */
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_HANDLER) ||
+ (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_RAW_HANDLER)) {
+
+ /* If there is already a handler, ignore this GPE method */
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_METHOD) {
+ /*
+ * If there is already a method, ignore this method. But check
+ * for a type mismatch (if both the _Lxx AND _Exx exist)
+ */
+ if (type != (gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK)) {
+ ACPI_ERROR((AE_INFO,
+ "For GPE 0x%.2X, found both _L%2.2X and _E%2.2X methods",
+ gpe_number, gpe_number, gpe_number));
+ }
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Disable the GPE in case it's been enabled already. */
+
+ (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
+
+ /*
+ * Add the GPE information from above to the gpe_event_info block for
+ * use during dispatch of this GPE.
+ */
+ 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;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
+ "Registered GPE method %s as GPE number 0x%.2X\n",
+ name, gpe_number));
+ return_ACPI_STATUS(AE_OK);
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c
new file mode 100644
index 000000000..c32eb57aa
--- /dev/null
+++ b/drivers/acpi/acpica/evgpeutil.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evgpeutil - GPE utilities
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evgpeutil")
+
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_walk_gpe_list
+ *
+ * PARAMETERS: gpe_walk_callback - Routine called for each GPE block
+ * context - Value passed to callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Walk the GPE lists.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context)
+{
+ struct acpi_gpe_block_info *gpe_block;
+ struct acpi_gpe_xrupt_info *gpe_xrupt_info;
+ acpi_status status = AE_OK;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(ev_walk_gpe_list);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Walk the interrupt level descriptor list */
+
+ gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head;
+ while (gpe_xrupt_info) {
+
+ /* Walk all Gpe Blocks attached to this interrupt level */
+
+ gpe_block = gpe_xrupt_info->gpe_block_list_head;
+ while (gpe_block) {
+
+ /* One callback per GPE block */
+
+ status =
+ gpe_walk_callback(gpe_xrupt_info, gpe_block,
+ context);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_CTRL_END) { /* Callback abort */
+ status = AE_OK;
+ }
+ goto unlock_and_exit;
+ }
+
+ gpe_block = gpe_block->next;
+ }
+
+ gpe_xrupt_info = gpe_xrupt_info->next;
+ }
+
+unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_get_gpe_device
+ *
+ * PARAMETERS: GPE_WALK_CALLBACK
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Matches the input GPE index (0-current_gpe_count) with a GPE
+ * block device. NULL if the GPE is one of the FADT-defined GPEs.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block, void *context)
+{
+ struct acpi_gpe_device_info *info = context;
+
+ /* Increment Index by the number of GPEs in this block */
+
+ info->next_block_base_index += gpe_block->gpe_count;
+
+ if (info->index < info->next_block_base_index) {
+ /*
+ * The GPE index is within this block, get the node. Leave the node
+ * NULL for the FADT-defined GPEs
+ */
+ if ((gpe_block->node)->type == ACPI_TYPE_DEVICE) {
+ info->gpe_device = gpe_block->node;
+ }
+
+ info->status = AE_OK;
+ return (AE_CTRL_END);
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_get_gpe_xrupt_block
+ *
+ * PARAMETERS: interrupt_number - Interrupt for a GPE block
+ * gpe_xrupt_block - Where the block is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt
+ * block per unique interrupt level used for GPEs. Should be
+ * called only when the GPE lists are semaphore locked and not
+ * subject to change.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_get_gpe_xrupt_block(u32 interrupt_number,
+ struct acpi_gpe_xrupt_info **gpe_xrupt_block)
+{
+ struct acpi_gpe_xrupt_info *next_gpe_xrupt;
+ struct acpi_gpe_xrupt_info *gpe_xrupt;
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(ev_get_gpe_xrupt_block);
+
+ /* No need for lock since we are not changing any list elements here */
+
+ next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head;
+ while (next_gpe_xrupt) {
+ if (next_gpe_xrupt->interrupt_number == interrupt_number) {
+ *gpe_xrupt_block = next_gpe_xrupt;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ next_gpe_xrupt = next_gpe_xrupt->next;
+ }
+
+ /* Not found, must allocate a new xrupt descriptor */
+
+ gpe_xrupt = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_xrupt_info));
+ if (!gpe_xrupt) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ gpe_xrupt->interrupt_number = interrupt_number;
+
+ /* Install new interrupt descriptor with spin lock */
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ if (acpi_gbl_gpe_xrupt_list_head) {
+ next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head;
+ while (next_gpe_xrupt->next) {
+ next_gpe_xrupt = next_gpe_xrupt->next;
+ }
+
+ next_gpe_xrupt->next = gpe_xrupt;
+ gpe_xrupt->previous = next_gpe_xrupt;
+ } else {
+ acpi_gbl_gpe_xrupt_list_head = gpe_xrupt;
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+
+ /* Install new interrupt handler if not SCI_INT */
+
+ if (interrupt_number != acpi_gbl_FADT.sci_interrupt) {
+ status = acpi_os_install_interrupt_handler(interrupt_number,
+ acpi_ev_gpe_xrupt_handler,
+ gpe_xrupt);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not install GPE interrupt handler at level 0x%X",
+ interrupt_number));
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ *gpe_xrupt_block = gpe_xrupt;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_delete_gpe_xrupt
+ *
+ * PARAMETERS: gpe_xrupt - A GPE interrupt info block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove and free a gpe_xrupt block. Remove an associated
+ * interrupt handler if not the SCI interrupt.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt)
+{
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(ev_delete_gpe_xrupt);
+
+ /* We never want to remove the SCI interrupt handler */
+
+ if (gpe_xrupt->interrupt_number == acpi_gbl_FADT.sci_interrupt) {
+ gpe_xrupt->gpe_block_list_head = NULL;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Disable this interrupt */
+
+ status =
+ acpi_os_remove_interrupt_handler(gpe_xrupt->interrupt_number,
+ acpi_ev_gpe_xrupt_handler);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Unlink the interrupt block with lock */
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ if (gpe_xrupt->previous) {
+ gpe_xrupt->previous->next = gpe_xrupt->next;
+ } else {
+ /* No previous, update list head */
+
+ acpi_gbl_gpe_xrupt_list_head = gpe_xrupt->next;
+ }
+
+ if (gpe_xrupt->next) {
+ gpe_xrupt->next->previous = gpe_xrupt->previous;
+ }
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+
+ /* Free the block */
+
+ ACPI_FREE(gpe_xrupt);
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_delete_gpe_handlers
+ *
+ * PARAMETERS: gpe_xrupt_info - GPE Interrupt info
+ * gpe_block - Gpe Block info
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Delete all Handler objects found in the GPE data structs.
+ * Used only prior to termination.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *context)
+{
+ struct acpi_gpe_event_info *gpe_event_info;
+ struct acpi_gpe_notify_info *notify;
+ struct acpi_gpe_notify_info *next;
+ u32 i;
+ u32 j;
+
+ ACPI_FUNCTION_TRACE(ev_delete_gpe_handlers);
+
+ /* Examine each GPE Register within the block */
+
+ for (i = 0; i < gpe_block->register_count; i++) {
+
+ /* Now look at the individual GPEs in this byte register */
+
+ for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
+ gpe_event_info = &gpe_block->event_info[((acpi_size)i *
+ ACPI_GPE_REGISTER_WIDTH)
+ + j];
+
+ if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_HANDLER) ||
+ (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_RAW_HANDLER)) {
+
+ /* Delete an installed handler block */
+
+ ACPI_FREE(gpe_event_info->dispatch.handler);
+ gpe_event_info->dispatch.handler = NULL;
+ gpe_event_info->flags &=
+ ~ACPI_GPE_DISPATCH_MASK;
+ } else if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)
+ == ACPI_GPE_DISPATCH_NOTIFY) {
+
+ /* Delete the implicit notification device list */
+
+ notify = gpe_event_info->dispatch.notify_list;
+ while (notify) {
+ next = notify->next;
+ ACPI_FREE(notify);
+ notify = next;
+ }
+
+ gpe_event_info->dispatch.notify_list = NULL;
+ gpe_event_info->flags &=
+ ~ACPI_GPE_DISPATCH_MASK;
+ }
+ }
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c
new file mode 100644
index 000000000..be9a05498
--- /dev/null
+++ b/drivers/acpi/acpica/evhandler.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evhandler - Support for Address Space handlers
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evhandler")
+
+/* Local prototypes */
+static acpi_status
+acpi_ev_install_handler(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value);
+
+/* These are the address spaces that will get default handlers */
+
+u8 acpi_gbl_default_address_spaces[ACPI_NUM_DEFAULT_SPACES] = {
+ ACPI_ADR_SPACE_SYSTEM_MEMORY,
+ ACPI_ADR_SPACE_SYSTEM_IO,
+ ACPI_ADR_SPACE_PCI_CONFIG,
+ ACPI_ADR_SPACE_DATA_TABLE
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_install_region_handlers
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Installs the core subsystem default address space handlers.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_install_region_handlers(void)
+{
+ acpi_status status;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ev_install_region_handlers);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * All address spaces (PCI Config, EC, SMBus) are scope dependent and
+ * registration must occur for a specific device.
+ *
+ * In the case of the system memory and IO address spaces there is
+ * currently no device associated with the address space. For these we
+ * use the root.
+ *
+ * We install the default PCI config space handler at the root so that
+ * this space is immediately available even though the we have not
+ * enumerated all the PCI Root Buses yet. This is to conform to the ACPI
+ * specification which states that the PCI config space must be always
+ * available -- even though we are nowhere near ready to find the PCI root
+ * buses at this point.
+ *
+ * NOTE: We ignore AE_ALREADY_EXISTS because this means that a handler
+ * has already been installed (via acpi_install_address_space_handler).
+ * Similar for AE_SAME_HANDLER.
+ */
+ for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) {
+ status = acpi_ev_install_space_handler(acpi_gbl_root_node,
+ acpi_gbl_default_address_spaces
+ [i],
+ ACPI_DEFAULT_HANDLER,
+ NULL, NULL);
+ switch (status) {
+ case AE_OK:
+ case AE_SAME_HANDLER:
+ case AE_ALREADY_EXISTS:
+
+ /* These exceptions are all OK */
+
+ status = AE_OK;
+ break;
+
+ default:
+
+ goto unlock_and_exit;
+ }
+ }
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_has_default_handler
+ *
+ * PARAMETERS: node - Namespace node for the device
+ * space_id - The address space ID
+ *
+ * RETURN: TRUE if default handler is installed, FALSE otherwise
+ *
+ * DESCRIPTION: Check if the default handler is installed for the requested
+ * space ID.
+ *
+ ******************************************************************************/
+
+u8
+acpi_ev_has_default_handler(struct acpi_namespace_node *node,
+ acpi_adr_space_type space_id)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *handler_obj;
+
+ /* Must have an existing internal object */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (obj_desc) {
+ handler_obj = obj_desc->common_notify.handler;
+
+ /* Walk the linked list of handlers for this object */
+
+ while (handler_obj) {
+ if (handler_obj->address_space.space_id == space_id) {
+ if (handler_obj->address_space.handler_flags &
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) {
+ return (TRUE);
+ }
+ }
+
+ handler_obj = handler_obj->address_space.next;
+ }
+ }
+
+ return (FALSE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_install_handler
+ *
+ * PARAMETERS: walk_namespace callback
+ *
+ * DESCRIPTION: This routine installs an address handler into objects that are
+ * of type Region or Device.
+ *
+ * If the Object is a Device, and the device has a handler of
+ * the same type then the search is terminated in that branch.
+ *
+ * This is because the existing handler is closer in proximity
+ * to any more regions than the one we are trying to install.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ev_install_handler(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value)
+{
+ union acpi_operand_object *handler_obj;
+ union acpi_operand_object *next_handler_obj;
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ ACPI_FUNCTION_NAME(ev_install_handler);
+
+ handler_obj = (union acpi_operand_object *)context;
+
+ /* Parameter validation */
+
+ if (!handler_obj) {
+ return (AE_OK);
+ }
+
+ /* Convert and validate the device handle */
+
+ node = acpi_ns_validate_handle(obj_handle);
+ if (!node) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /*
+ * We only care about regions and objects that are allowed to have
+ * address space handlers
+ */
+ if ((node->type != ACPI_TYPE_DEVICE) &&
+ (node->type != ACPI_TYPE_REGION) && (node != acpi_gbl_root_node)) {
+ return (AE_OK);
+ }
+
+ /* Check for an existing internal object */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+
+ /* No object, just exit */
+
+ return (AE_OK);
+ }
+
+ /* Devices are handled different than regions */
+
+ if (obj_desc->common.type == ACPI_TYPE_DEVICE) {
+
+ /* Check if this Device already has a handler for this address space */
+
+ next_handler_obj =
+ acpi_ev_find_region_handler(handler_obj->address_space.
+ space_id,
+ obj_desc->common_notify.
+ handler);
+ if (next_handler_obj) {
+
+ /* Found a handler, is it for the same address space? */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Found handler for region [%s] in device %p(%p) handler %p\n",
+ acpi_ut_get_region_name(handler_obj->
+ address_space.
+ space_id),
+ obj_desc, next_handler_obj,
+ handler_obj));
+
+ /*
+ * Since the object we found it on was a device, then it means
+ * that someone has already installed a handler for the branch
+ * of the namespace from this device on. Just bail out telling
+ * the walk routine to not traverse this branch. This preserves
+ * the scoping rule for handlers.
+ */
+ return (AE_CTRL_DEPTH);
+ }
+
+ /*
+ * As long as the device didn't have a handler for this space we
+ * don't care about it. We just ignore it and proceed.
+ */
+ return (AE_OK);
+ }
+
+ /* Object is a Region */
+
+ if (obj_desc->region.space_id != handler_obj->address_space.space_id) {
+
+ /* This region is for a different address space, just ignore it */
+
+ return (AE_OK);
+ }
+
+ /*
+ * Now we have a region and it is for the handler's address space type.
+ *
+ * First disconnect region for any previous handler (if any)
+ */
+ acpi_ev_detach_region(obj_desc, FALSE);
+
+ /* Connect the region to the new handler */
+
+ status = acpi_ev_attach_region(handler_obj, obj_desc, FALSE);
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_find_region_handler
+ *
+ * PARAMETERS: space_id - The address space ID
+ * handler_obj - Head of the handler object list
+ *
+ * RETURN: Matching handler object. NULL if space ID not matched
+ *
+ * DESCRIPTION: Search a handler object list for a match on the address
+ * space ID.
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ev_find_region_handler(acpi_adr_space_type
+ space_id,
+ union acpi_operand_object
+ *handler_obj)
+{
+
+ /* Walk the handler list for this device */
+
+ while (handler_obj) {
+
+ /* Same space_id indicates a handler is installed */
+
+ if (handler_obj->address_space.space_id == space_id) {
+ return (handler_obj);
+ }
+
+ /* Next handler object */
+
+ handler_obj = handler_obj->address_space.next;
+ }
+
+ return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_install_space_handler
+ *
+ * PARAMETERS: node - Namespace node 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
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for all op_regions of a given space_id.
+ * Assumes namespace is locked
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_install_space_handler(struct acpi_namespace_node *node,
+ acpi_adr_space_type space_id,
+ acpi_adr_space_handler handler,
+ acpi_adr_space_setup setup, void *context)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *handler_obj;
+ acpi_status status = AE_OK;
+ acpi_object_type type;
+ u8 flags = 0;
+
+ ACPI_FUNCTION_TRACE(ev_install_space_handler);
+
+ /*
+ * This registration is valid for only the types below and the root.
+ * The root node is where the default handlers get installed.
+ */
+ if ((node->type != ACPI_TYPE_DEVICE) &&
+ (node->type != ACPI_TYPE_PROCESSOR) &&
+ (node->type != ACPI_TYPE_THERMAL) && (node != acpi_gbl_root_node)) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ if (handler == ACPI_DEFAULT_HANDLER) {
+ flags = ACPI_ADDR_HANDLER_DEFAULT_INSTALLED;
+
+ switch (space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+
+ handler = acpi_ex_system_memory_space_handler;
+ setup = acpi_ev_system_memory_region_setup;
+ break;
+
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+
+ handler = acpi_ex_system_io_space_handler;
+ setup = acpi_ev_io_space_region_setup;
+ break;
+#ifdef ACPI_PCI_CONFIGURED
+ case ACPI_ADR_SPACE_PCI_CONFIG:
+
+ handler = acpi_ex_pci_config_space_handler;
+ setup = acpi_ev_pci_config_region_setup;
+ break;
+#endif
+ case ACPI_ADR_SPACE_CMOS:
+
+ handler = acpi_ex_cmos_space_handler;
+ setup = acpi_ev_cmos_region_setup;
+ break;
+#ifdef ACPI_PCI_CONFIGURED
+ case ACPI_ADR_SPACE_PCI_BAR_TARGET:
+
+ handler = acpi_ex_pci_bar_space_handler;
+ setup = acpi_ev_pci_bar_region_setup;
+ break;
+#endif
+ case ACPI_ADR_SPACE_DATA_TABLE:
+
+ handler = acpi_ex_data_table_space_handler;
+ setup = acpi_ev_data_table_region_setup;
+ break;
+
+ default:
+
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+ }
+
+ /* If the caller hasn't specified a setup routine, use the default */
+
+ if (!setup) {
+ setup = acpi_ev_default_region_setup;
+ }
+
+ /* Check for an existing internal object */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (obj_desc) {
+ /*
+ * The attached device object already exists. Now make sure
+ * the handler is not already installed.
+ */
+ handler_obj = acpi_ev_find_region_handler(space_id,
+ obj_desc->
+ common_notify.
+ handler);
+
+ if (handler_obj) {
+ if (handler_obj->address_space.handler == handler) {
+ /*
+ * It is (relatively) OK to attempt to install the SAME
+ * handler twice. This can easily happen with the
+ * PCI_Config space.
+ */
+ status = AE_SAME_HANDLER;
+ goto unlock_and_exit;
+ } else {
+ /* A handler is already installed */
+
+ status = AE_ALREADY_EXISTS;
+ }
+
+ goto unlock_and_exit;
+ }
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Creating object on Device %p while installing handler\n",
+ node));
+
+ /* obj_desc does not exist, create one */
+
+ if (node->type == ACPI_TYPE_ANY) {
+ type = ACPI_TYPE_DEVICE;
+ } else {
+ type = node->type;
+ }
+
+ obj_desc = acpi_ut_create_internal_object(type);
+ if (!obj_desc) {
+ status = AE_NO_MEMORY;
+ goto unlock_and_exit;
+ }
+
+ /* Init new descriptor */
+
+ obj_desc->common.type = (u8)type;
+
+ /* Attach the new object to the Node */
+
+ status = acpi_ns_attach_object(node, obj_desc, type);
+
+ /* Remove local reference to the object */
+
+ acpi_ut_remove_reference(obj_desc);
+
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Installing address handler for region %s(%X) "
+ "on Device %4.4s %p(%p)\n",
+ acpi_ut_get_region_name(space_id), space_id,
+ acpi_ut_get_node_name(node), node, obj_desc));
+
+ /*
+ * Install the handler
+ *
+ * At this point there is no existing handler. Just allocate the object
+ * for the handler and link it into the list.
+ */
+ handler_obj =
+ acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_ADDRESS_HANDLER);
+ if (!handler_obj) {
+ status = AE_NO_MEMORY;
+ goto unlock_and_exit;
+ }
+
+ /* Init handler obj */
+
+ status =
+ acpi_os_create_mutex(&handler_obj->address_space.context_mutex);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(handler_obj);
+ goto unlock_and_exit;
+ }
+
+ handler_obj->address_space.space_id = (u8)space_id;
+ handler_obj->address_space.handler_flags = flags;
+ handler_obj->address_space.region_list = NULL;
+ handler_obj->address_space.node = node;
+ handler_obj->address_space.handler = handler;
+ handler_obj->address_space.context = context;
+ handler_obj->address_space.setup = setup;
+
+ /* Install at head of Device.address_space list */
+
+ handler_obj->address_space.next = obj_desc->common_notify.handler;
+
+ /*
+ * The Device object is the first reference on the handler_obj.
+ * Each region that uses the handler adds a reference.
+ */
+ obj_desc->common_notify.handler = handler_obj;
+
+ /*
+ * Walk the namespace finding all of the regions this handler will
+ * manage.
+ *
+ * Start at the device and search the branch toward the leaf nodes
+ * until either the leaf is encountered or a device is detected that
+ * has an address handler of the same type.
+ *
+ * In either case, back up and search down the remainder of the branch
+ */
+ status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node,
+ ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
+ acpi_ev_install_handler, NULL,
+ handler_obj, NULL);
+
+unlock_and_exit:
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c
new file mode 100644
index 000000000..6172cddc1
--- /dev/null
+++ b/drivers/acpi/acpica/evmisc.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evmisc - Miscellaneous event manager support functions
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evmisc")
+
+/* Local prototypes */
+static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_is_notify_object
+ *
+ * PARAMETERS: node - Node to check
+ *
+ * RETURN: TRUE if notifies allowed on this object
+ *
+ * DESCRIPTION: Check type of node for a object that supports notifies.
+ *
+ * TBD: This could be replaced by a flag bit in the node.
+ *
+ ******************************************************************************/
+
+u8 acpi_ev_is_notify_object(struct acpi_namespace_node *node)
+{
+
+ switch (node->type) {
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+ /*
+ * These are the ONLY objects that can receive ACPI notifications
+ */
+ return (TRUE);
+
+ default:
+
+ return (FALSE);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_queue_notify_request
+ *
+ * PARAMETERS: node - NS node for the notified object
+ * notify_value - Value from the Notify() request
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dispatch a device notification event to a previously
+ * installed handler.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_queue_notify_request(struct acpi_namespace_node *node, u32 notify_value)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *handler_list_head = NULL;
+ union acpi_generic_state *info;
+ u8 handler_list_id = 0;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_NAME(ev_queue_notify_request);
+
+ /* Are Notifies allowed on this object? */
+
+ if (!acpi_ev_is_notify_object(node)) {
+ return (AE_TYPE);
+ }
+
+ /* Get the correct notify list type (System or Device) */
+
+ if (notify_value <= ACPI_MAX_SYS_NOTIFY) {
+ handler_list_id = ACPI_SYSTEM_HANDLER_LIST;
+ } else {
+ handler_list_id = ACPI_DEVICE_HANDLER_LIST;
+ }
+
+ /* Get the notify object attached to the namespace Node */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (obj_desc) {
+
+ /* We have an attached object, Get the correct handler list */
+
+ handler_list_head =
+ obj_desc->common_notify.notify_list[handler_list_id];
+ }
+
+ /*
+ * If there is no notify handler (Global or Local)
+ * for this object, just ignore the notify
+ */
+ if (!acpi_gbl_global_notify[handler_list_id].handler
+ && !handler_list_head) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "No notify handler for Notify, ignoring (%4.4s, %X) node %p\n",
+ acpi_ut_get_node_name(node), notify_value,
+ node));
+
+ return (AE_OK);
+ }
+
+ /* Setup notify info and schedule the notify dispatcher */
+
+ info = acpi_ut_create_generic_state();
+ if (!info) {
+ return (AE_NO_MEMORY);
+ }
+
+ info->common.descriptor_type = ACPI_DESC_TYPE_STATE_NOTIFY;
+
+ info->notify.node = node;
+ info->notify.value = (u16)notify_value;
+ info->notify.handler_list_id = handler_list_id;
+ info->notify.handler_list_head = handler_list_head;
+ info->notify.global = &acpi_gbl_global_notify[handler_list_id];
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n",
+ acpi_ut_get_node_name(node),
+ acpi_ut_get_type_name(node->type), notify_value,
+ acpi_ut_get_notify_name(notify_value, ACPI_TYPE_ANY),
+ node));
+
+ status = acpi_os_execute(OSL_NOTIFY_HANDLER,
+ acpi_ev_notify_dispatch, info);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_delete_generic_state(info);
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_notify_dispatch
+ *
+ * PARAMETERS: context - To be passed to the notify handler
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Dispatch a device notification event to a previously
+ * installed handler.
+ *
+ ******************************************************************************/
+
+static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context)
+{
+ union acpi_generic_state *info = (union acpi_generic_state *)context;
+ union acpi_operand_object *handler_obj;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Invoke a global notify handler if installed */
+
+ if (info->notify.global->handler) {
+ info->notify.global->handler(info->notify.node,
+ info->notify.value,
+ info->notify.global->context);
+ }
+
+ /* Now invoke the local notify handler(s) if any are installed */
+
+ handler_obj = info->notify.handler_list_head;
+ while (handler_obj) {
+ handler_obj->notify.handler(info->notify.node,
+ info->notify.value,
+ handler_obj->notify.context);
+
+ handler_obj =
+ handler_obj->notify.next[info->notify.handler_list_id];
+ }
+
+ /* All done with the info object */
+
+ acpi_ut_delete_generic_state(info);
+}
+
+#if (!ACPI_REDUCED_HARDWARE)
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ev_terminate
+ *
+ * PARAMETERS: none
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: Disable events and free memory allocated for table storage.
+ *
+ ******************************************************************************/
+
+void acpi_ev_terminate(void)
+{
+ u32 i;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_terminate);
+
+ if (acpi_gbl_events_initialized) {
+ /*
+ * Disable all event-related functionality. In all cases, on error,
+ * print a message but obviously we don't abort.
+ */
+
+ /* Disable all fixed events */
+
+ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) {
+ status = acpi_disable_event(i, 0);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO,
+ "Could not disable fixed event %u",
+ (u32) i));
+ }
+ }
+
+ /* Disable all GPEs in all GPE blocks */
+
+ status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not disable GPEs in GPE block"));
+ }
+
+ status = acpi_ev_remove_global_lock_handler();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not remove Global Lock handler"));
+ }
+
+ acpi_gbl_events_initialized = FALSE;
+ }
+
+ /* Remove SCI handlers */
+
+ status = acpi_ev_remove_all_sci_handlers();
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO, "Could not remove SCI handler"));
+ }
+
+ /* Deallocate all handler objects installed within GPE info structs */
+
+ status = acpi_ev_walk_gpe_list(acpi_ev_delete_gpe_handlers, NULL);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not delete GPE handlers"));
+ }
+
+ /* Return to original mode if necessary */
+
+ if (acpi_gbl_original_mode == ACPI_SYS_MODE_LEGACY) {
+ status = acpi_disable();
+ if (ACPI_FAILURE(status)) {
+ ACPI_WARNING((AE_INFO, "AcpiDisable failed"));
+ }
+ }
+ return_VOID;
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
new file mode 100644
index 000000000..b96b3a7e7
--- /dev/null
+++ b/drivers/acpi/acpica/evregion.c
@@ -0,0 +1,873 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evregion - Operation Region support
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evregion")
+
+extern u8 acpi_gbl_default_address_spaces[];
+
+/* Local prototypes */
+
+static void
+acpi_ev_execute_orphan_reg_method(struct acpi_namespace_node *device_node,
+ acpi_adr_space_type space_id);
+
+static acpi_status
+acpi_ev_reg_run(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_initialize_op_regions
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute _REG methods for all Operation Regions that have
+ * an installed default region handler.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_initialize_op_regions(void)
+{
+ acpi_status status;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ev_initialize_op_regions);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Run the _REG methods for op_regions in each default address space */
+
+ for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) {
+ /*
+ * Make sure the installed handler is the DEFAULT handler. If not the
+ * default, the _REG methods will have already been run (when the
+ * handler was installed)
+ */
+ if (acpi_ev_has_default_handler(acpi_gbl_root_node,
+ acpi_gbl_default_address_spaces
+ [i])) {
+ acpi_ev_execute_reg_methods(acpi_gbl_root_node,
+ acpi_gbl_default_address_spaces
+ [i], ACPI_REG_CONNECT);
+ }
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_address_space_dispatch
+ *
+ * PARAMETERS: region_obj - Internal region object
+ * field_obj - Corresponding field. Can be NULL.
+ * function - Read or Write operation
+ * region_offset - Where in the region to read or write
+ * bit_width - Field width in bits (8, 16, 32, or 64)
+ * value - Pointer to in or out value, must be
+ * a full 64-bit integer
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dispatch an address space or operation region access to
+ * a previously installed handler.
+ *
+ * NOTE: During early initialization, we always install the default region
+ * handlers for Memory, I/O and PCI_Config. This ensures that these operation
+ * region address spaces are always available as per the ACPI specification.
+ * This is especially needed in order to support the execution of
+ * module-level AML code during loading of the ACPI tables.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
+ union acpi_operand_object *field_obj,
+ u32 function,
+ u32 region_offset, u32 bit_width, u64 *value)
+{
+ acpi_status status;
+ acpi_adr_space_handler handler;
+ acpi_adr_space_setup region_setup;
+ union acpi_operand_object *handler_desc;
+ union acpi_operand_object *region_obj2;
+ void *region_context = NULL;
+ struct acpi_connection_info *context;
+ acpi_mutex context_mutex;
+ u8 context_locked;
+ acpi_physical_address address;
+
+ ACPI_FUNCTION_TRACE(ev_address_space_dispatch);
+
+ region_obj2 = acpi_ns_get_secondary_object(region_obj);
+ if (!region_obj2) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /* Ensure that there is a handler associated with this region */
+
+ handler_desc = region_obj->region.handler;
+ if (!handler_desc) {
+ ACPI_ERROR((AE_INFO,
+ "No handler for Region [%4.4s] (%p) [%s]",
+ acpi_ut_get_node_name(region_obj->region.node),
+ region_obj,
+ acpi_ut_get_region_name(region_obj->region.
+ space_id)));
+
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ context = handler_desc->address_space.context;
+ context_mutex = handler_desc->address_space.context_mutex;
+ context_locked = FALSE;
+
+ /*
+ * It may be the case that the region has never been initialized.
+ * Some types of regions require special init code
+ */
+ if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) {
+
+ /* This region has not been initialized yet, do it */
+
+ region_setup = handler_desc->address_space.setup;
+ if (!region_setup) {
+
+ /* No initialization routine, exit with error */
+
+ ACPI_ERROR((AE_INFO,
+ "No init routine for region(%p) [%s]",
+ region_obj,
+ acpi_ut_get_region_name(region_obj->region.
+ space_id)));
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ if (region_obj->region.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
+ struct acpi_pcc_info *ctx =
+ handler_desc->address_space.context;
+
+ ctx->internal_buffer =
+ field_obj->field.internal_pcc_buffer;
+ ctx->length = (u16)region_obj->region.length;
+ ctx->subspace_id = (u8)region_obj->region.address;
+ }
+
+ /*
+ * We must exit the interpreter because the region setup will
+ * potentially execute control methods (for example, the _REG method
+ * for this region)
+ */
+ acpi_ex_exit_interpreter();
+
+ status = region_setup(region_obj, ACPI_REGION_ACTIVATE,
+ context, &region_context);
+
+ /* Re-enter the interpreter */
+
+ acpi_ex_enter_interpreter();
+
+ /* Check for failure of the Region Setup */
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During region initialization: [%s]",
+ acpi_ut_get_region_name(region_obj->
+ region.
+ space_id)));
+ return_ACPI_STATUS(status);
+ }
+
+ /* Region initialization may have been completed by region_setup */
+
+ if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) {
+ region_obj->region.flags |= AOPOBJ_SETUP_COMPLETE;
+
+ /*
+ * Save the returned context for use in all accesses to
+ * the handler for this particular region
+ */
+ if (!(region_obj2->extra.region_context)) {
+ region_obj2->extra.region_context =
+ region_context;
+ }
+ }
+ }
+
+ /* We have everything we need, we can invoke the address space handler */
+
+ handler = handler_desc->address_space.handler;
+ address = (region_obj->region.address + region_offset);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
+ &region_obj->region.handler->address_space, handler,
+ ACPI_FORMAT_UINT64(address),
+ acpi_ut_get_region_name(region_obj->region.
+ space_id)));
+
+ if (!(handler_desc->address_space.handler_flags &
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
+ /*
+ * For handlers other than the default (supplied) handlers, we must
+ * exit the interpreter because the handler *might* block -- we don't
+ * know what it will do, so we can't hold the lock on the interpreter.
+ */
+ acpi_ex_exit_interpreter();
+ }
+
+ /*
+ * Special handling for generic_serial_bus and general_purpose_io:
+ * There are three extra parameters that must be passed to the
+ * handler via the context:
+ * 1) Connection buffer, a resource template from Connection() op
+ * 2) Length of the above buffer
+ * 3) Actual access length from the access_as() op
+ *
+ * Since we pass these extra parameters via the context, which is
+ * shared between threads, we must lock the context to avoid these
+ * parameters being changed from another thread before the handler
+ * has completed running.
+ *
+ * In addition, for general_purpose_io, the Address and bit_width fields
+ * are defined as follows:
+ * 1) Address is the pin number index of the field (bit offset from
+ * the previous Connection)
+ * 2) bit_width is the actual bit length of the field (number of pins)
+ */
+ if ((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS ||
+ region_obj->region.space_id == ACPI_ADR_SPACE_GPIO) &&
+ context && field_obj) {
+
+ status =
+ acpi_os_acquire_mutex(context_mutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ goto re_enter_interpreter;
+ }
+
+ context_locked = TRUE;
+
+ /* Get the Connection (resource_template) buffer */
+
+ context->connection = field_obj->field.resource_buffer;
+ context->length = field_obj->field.resource_length;
+ context->access_length = field_obj->field.access_length;
+
+ if (region_obj->region.space_id == ACPI_ADR_SPACE_GPIO) {
+ address = field_obj->field.pin_number_index;
+ bit_width = field_obj->field.bit_length;
+ }
+ }
+
+ /* Call the handler */
+
+ status = handler(function, address, bit_width, value, context,
+ region_obj2->extra.region_context);
+
+ if (context_locked) {
+ acpi_os_release_mutex(context_mutex);
+ }
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Returned by Handler for [%s]",
+ acpi_ut_get_region_name(region_obj->region.
+ space_id)));
+
+ /*
+ * Special case for an EC timeout. These are seen so frequently
+ * that an additional error message is helpful
+ */
+ if ((region_obj->region.space_id == ACPI_ADR_SPACE_EC) &&
+ (status == AE_TIME)) {
+ ACPI_ERROR((AE_INFO,
+ "Timeout from EC hardware or EC device driver"));
+ }
+ }
+
+re_enter_interpreter:
+ if (!(handler_desc->address_space.handler_flags &
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
+ /*
+ * We just returned from a non-default handler, we must re-enter the
+ * interpreter
+ */
+ acpi_ex_enter_interpreter();
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_detach_region
+ *
+ * PARAMETERS: region_obj - Region Object
+ * acpi_ns_is_locked - Namespace Region Already Locked?
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Break the association between the handler and the region
+ * this is a two way association.
+ *
+ ******************************************************************************/
+
+void
+acpi_ev_detach_region(union acpi_operand_object *region_obj,
+ u8 acpi_ns_is_locked)
+{
+ union acpi_operand_object *handler_obj;
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *start_desc;
+ union acpi_operand_object **last_obj_ptr;
+ acpi_adr_space_setup region_setup;
+ void **region_context;
+ union acpi_operand_object *region_obj2;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_detach_region);
+
+ region_obj2 = acpi_ns_get_secondary_object(region_obj);
+ if (!region_obj2) {
+ return_VOID;
+ }
+ region_context = &region_obj2->extra.region_context;
+
+ /* Get the address handler from the region object */
+
+ handler_obj = region_obj->region.handler;
+ if (!handler_obj) {
+
+ /* This region has no handler, all done */
+
+ return_VOID;
+ }
+
+ /* Find this region in the handler's list */
+
+ obj_desc = handler_obj->address_space.region_list;
+ start_desc = obj_desc;
+ last_obj_ptr = &handler_obj->address_space.region_list;
+
+ while (obj_desc) {
+
+ /* Is this the correct Region? */
+
+ if (obj_desc == region_obj) {
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Removing Region %p from address handler %p\n",
+ region_obj, handler_obj));
+
+ /* This is it, remove it from the handler's list */
+
+ *last_obj_ptr = obj_desc->region.next;
+ obj_desc->region.next = NULL; /* Must clear field */
+
+ if (acpi_ns_is_locked) {
+ status =
+ acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_VOID;
+ }
+ }
+
+ /* Now stop region accesses by executing the _REG method */
+
+ status =
+ acpi_ev_execute_reg_method(region_obj,
+ ACPI_REG_DISCONNECT);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "from region _REG, [%s]",
+ acpi_ut_get_region_name
+ (region_obj->region.space_id)));
+ }
+
+ if (acpi_ns_is_locked) {
+ status =
+ acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_VOID;
+ }
+ }
+
+ /*
+ * If the region has been activated, call the setup handler with
+ * the deactivate notification
+ */
+ if (region_obj->region.flags & AOPOBJ_SETUP_COMPLETE) {
+ region_setup = handler_obj->address_space.setup;
+ status =
+ region_setup(region_obj,
+ ACPI_REGION_DEACTIVATE,
+ handler_obj->address_space.
+ context, region_context);
+
+ /*
+ * region_context should have been released by the deactivate
+ * operation. We don't need access to it anymore here.
+ */
+ if (region_context) {
+ *region_context = NULL;
+ }
+
+ /* Init routine may fail, Just ignore errors */
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "from region handler - deactivate, [%s]",
+ acpi_ut_get_region_name
+ (region_obj->region.
+ space_id)));
+ }
+
+ region_obj->region.flags &=
+ ~(AOPOBJ_SETUP_COMPLETE);
+ }
+
+ /*
+ * Remove handler reference in the region
+ *
+ * NOTE: this doesn't mean that the region goes away, the region
+ * is just inaccessible as indicated to the _REG method
+ *
+ * If the region is on the handler's list, this must be the
+ * region's handler
+ */
+ region_obj->region.handler = NULL;
+ acpi_ut_remove_reference(handler_obj);
+
+ return_VOID;
+ }
+
+ /* Walk the linked list of handlers */
+
+ last_obj_ptr = &obj_desc->region.next;
+ obj_desc = obj_desc->region.next;
+
+ /* Prevent infinite loop if list is corrupted */
+
+ if (obj_desc == start_desc) {
+ ACPI_ERROR((AE_INFO,
+ "Circular handler list in region object %p",
+ region_obj));
+ return_VOID;
+ }
+ }
+
+ /* If we get here, the region was not in the handler's region list */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Cannot remove region %p from address handler %p\n",
+ region_obj, handler_obj));
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_attach_region
+ *
+ * PARAMETERS: handler_obj - Handler Object
+ * region_obj - Region Object
+ * acpi_ns_is_locked - Namespace Region Already Locked?
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Create the association between the handler and the region
+ * this is a two way association.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_attach_region(union acpi_operand_object *handler_obj,
+ union acpi_operand_object *region_obj,
+ u8 acpi_ns_is_locked)
+{
+
+ ACPI_FUNCTION_TRACE(ev_attach_region);
+
+ /* Install the region's handler */
+
+ if (region_obj->region.handler) {
+ return_ACPI_STATUS(AE_ALREADY_EXISTS);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Adding Region [%4.4s] %p to address handler %p [%s]\n",
+ acpi_ut_get_node_name(region_obj->region.node),
+ region_obj, handler_obj,
+ acpi_ut_get_region_name(region_obj->region.
+ space_id)));
+
+ /* Link this region to the front of the handler's list */
+
+ region_obj->region.next = handler_obj->address_space.region_list;
+ handler_obj->address_space.region_list = region_obj;
+ region_obj->region.handler = handler_obj;
+ acpi_ut_add_reference(handler_obj);
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_execute_reg_method
+ *
+ * PARAMETERS: region_obj - Region object
+ * function - Passed to _REG: On (1) or Off (0)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute _REG method for a region
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function)
+{
+ struct acpi_evaluate_info *info;
+ union acpi_operand_object *args[3];
+ union acpi_operand_object *region_obj2;
+ const acpi_name *reg_name_ptr =
+ ACPI_CAST_PTR(acpi_name, METHOD_NAME__REG);
+ struct acpi_namespace_node *method_node;
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_execute_reg_method);
+
+ if (!acpi_gbl_namespace_initialized ||
+ region_obj->region.handler == NULL) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ region_obj2 = acpi_ns_get_secondary_object(region_obj);
+ if (!region_obj2) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /*
+ * Find any "_REG" method associated with this region definition.
+ * The method should always be updated as this function may be
+ * invoked after a namespace change.
+ */
+ node = region_obj->region.node->parent;
+ status =
+ acpi_ns_search_one_scope(*reg_name_ptr, node, ACPI_TYPE_METHOD,
+ &method_node);
+ if (ACPI_SUCCESS(status)) {
+ /*
+ * The _REG method is optional and there can be only one per
+ * region definition. This will be executed when the handler is
+ * attached or removed.
+ */
+ region_obj2->extra.method_REG = method_node;
+ }
+ if (region_obj2->extra.method_REG == NULL) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* _REG(DISCONNECT) should be paired with _REG(CONNECT) */
+
+ if ((function == ACPI_REG_CONNECT &&
+ region_obj->common.flags & AOPOBJ_REG_CONNECTED) ||
+ (function == ACPI_REG_DISCONNECT &&
+ !(region_obj->common.flags & AOPOBJ_REG_CONNECTED))) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Allocate and initialize the evaluation information block */
+
+ info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
+ if (!info) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ info->prefix_node = region_obj2->extra.method_REG;
+ info->relative_pathname = NULL;
+ info->parameters = args;
+ info->flags = ACPI_IGNORE_RETURN_VALUE;
+
+ /*
+ * The _REG method has two arguments:
+ *
+ * arg0 - Integer:
+ * Operation region space ID Same value as region_obj->Region.space_id
+ *
+ * arg1 - Integer:
+ * connection status 1 for connecting the handler, 0 for disconnecting
+ * the handler (Passed as a parameter)
+ */
+ args[0] =
+ acpi_ut_create_integer_object((u64)region_obj->region.space_id);
+ if (!args[0]) {
+ status = AE_NO_MEMORY;
+ goto cleanup1;
+ }
+
+ args[1] = acpi_ut_create_integer_object((u64)function);
+ if (!args[1]) {
+ status = AE_NO_MEMORY;
+ goto cleanup2;
+ }
+
+ args[2] = NULL; /* Terminate list */
+
+ /* Execute the method, no return value */
+
+ ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname
+ (ACPI_TYPE_METHOD, info->prefix_node, NULL));
+
+ status = acpi_ns_evaluate(info);
+ acpi_ut_remove_reference(args[1]);
+
+ if (ACPI_FAILURE(status)) {
+ goto cleanup2;
+ }
+
+ if (function == ACPI_REG_CONNECT) {
+ region_obj->common.flags |= AOPOBJ_REG_CONNECTED;
+ } else {
+ region_obj->common.flags &= ~AOPOBJ_REG_CONNECTED;
+ }
+
+cleanup2:
+ acpi_ut_remove_reference(args[0]);
+
+cleanup1:
+ ACPI_FREE(info);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_execute_reg_methods
+ *
+ * PARAMETERS: node - Namespace node for the device
+ * space_id - The address space ID
+ * function - Passed to _REG: On (1) or Off (0)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Run all _REG methods for the input Space ID;
+ * Note: assumes namespace is locked, or system init time.
+ *
+ ******************************************************************************/
+
+void
+acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
+ acpi_adr_space_type space_id, u32 function)
+{
+ struct acpi_reg_walk_info info;
+
+ ACPI_FUNCTION_TRACE(ev_execute_reg_methods);
+
+ /*
+ * These address spaces do not need a call to _REG, since the ACPI
+ * specification defines them as: "must always be accessible". Since
+ * they never change state (never become unavailable), no need to ever
+ * call _REG on them. Also, a data_table is not a "real" address space,
+ * so do not call _REG. September 2018.
+ */
+ if ((space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) ||
+ (space_id == ACPI_ADR_SPACE_SYSTEM_IO) ||
+ (space_id == ACPI_ADR_SPACE_DATA_TABLE)) {
+ return_VOID;
+ }
+
+ info.space_id = space_id;
+ info.function = function;
+ info.reg_run_count = 0;
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_NAMES,
+ " Running _REG methods for SpaceId %s\n",
+ acpi_ut_get_region_name(info.space_id)));
+
+ /*
+ * Run all _REG methods for all Operation Regions for this space ID. This
+ * is a separate walk in order to handle any interdependencies between
+ * 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,
+ ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run, NULL,
+ &info, NULL);
+
+ /*
+ * Special case for EC and GPIO: handle "orphan" _REG methods with
+ * no region.
+ */
+ if (space_id == ACPI_ADR_SPACE_EC || space_id == ACPI_ADR_SPACE_GPIO) {
+ acpi_ev_execute_orphan_reg_method(node, space_id);
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_NAMES,
+ " Executed %u _REG methods for SpaceId %s\n",
+ info.reg_run_count,
+ acpi_ut_get_region_name(info.space_id)));
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_reg_run
+ *
+ * PARAMETERS: walk_namespace callback
+ *
+ * DESCRIPTION: Run _REG method for region objects of the requested spaceID
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ev_reg_run(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value)
+{
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *node;
+ acpi_status status;
+ struct acpi_reg_walk_info *info;
+
+ info = ACPI_CAST_PTR(struct acpi_reg_walk_info, context);
+
+ /* Convert and validate the device handle */
+
+ node = acpi_ns_validate_handle(obj_handle);
+ if (!node) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /*
+ * We only care about regions and objects that are allowed to have
+ * address space handlers
+ */
+ if ((node->type != ACPI_TYPE_REGION) && (node != acpi_gbl_root_node)) {
+ return (AE_OK);
+ }
+
+ /* Check for an existing internal object */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+
+ /* No object, just exit */
+
+ return (AE_OK);
+ }
+
+ /* Object is a Region */
+
+ if (obj_desc->region.space_id != info->space_id) {
+
+ /* This region is for a different address space, just ignore it */
+
+ return (AE_OK);
+ }
+
+ info->reg_run_count++;
+ status = acpi_ev_execute_reg_method(obj_desc, info->function);
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_execute_orphan_reg_method
+ *
+ * PARAMETERS: device_node - Namespace node for an ACPI device
+ * space_id - The address space ID
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Execute an "orphan" _REG method that appears under an ACPI
+ * device. This is a _REG method that has no corresponding region
+ * within the device's scope. ACPI tables depending on these
+ * "orphan" _REG methods have been seen for both EC and GPIO
+ * Operation Regions. Presumably the Windows ACPI implementation
+ * always calls the _REG method independent of the presence of
+ * an actual Operation Region with the correct address space ID.
+ *
+ * MUTEX: Assumes the namespace is locked
+ *
+ ******************************************************************************/
+
+static void
+acpi_ev_execute_orphan_reg_method(struct acpi_namespace_node *device_node,
+ acpi_adr_space_type space_id)
+{
+ acpi_handle reg_method;
+ struct acpi_namespace_node *next_node;
+ acpi_status status;
+ struct acpi_object_list args;
+ union acpi_object objects[2];
+
+ ACPI_FUNCTION_TRACE(ev_execute_orphan_reg_method);
+
+ if (!device_node) {
+ return_VOID;
+ }
+
+ /* Namespace is currently locked, must release */
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+ /* Get a handle to a _REG method immediately under the EC device */
+
+ status = acpi_get_handle(device_node, METHOD_NAME__REG, &reg_method);
+ if (ACPI_FAILURE(status)) {
+ goto exit; /* There is no _REG method present */
+ }
+
+ /*
+ * Execute the _REG method only if there is no Operation Region in
+ * this scope with the Embedded Controller space ID. Otherwise, it
+ * will already have been executed. Note, this allows for Regions
+ * with other space IDs to be present; but the code below will then
+ * execute the _REG method with the embedded_control space_ID argument.
+ */
+ next_node = acpi_ns_get_next_node(device_node, NULL);
+ while (next_node) {
+ if ((next_node->type == ACPI_TYPE_REGION) &&
+ (next_node->object) &&
+ (next_node->object->region.space_id == space_id)) {
+ goto exit; /* Do not execute the _REG */
+ }
+
+ next_node = acpi_ns_get_next_node(device_node, next_node);
+ }
+
+ /* Evaluate the _REG(space_id,Connect) method */
+
+ args.count = 2;
+ args.pointer = objects;
+ objects[0].type = ACPI_TYPE_INTEGER;
+ objects[0].integer.value = space_id;
+ objects[1].type = ACPI_TYPE_INTEGER;
+ objects[1].integer.value = ACPI_REG_CONNECT;
+
+ (void)acpi_evaluate_object(reg_method, NULL, &args, NULL);
+
+exit:
+ /* We ignore all errors from above, don't care */
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c
new file mode 100644
index 000000000..ca4ba6b35
--- /dev/null
+++ b/drivers/acpi/acpica/evrgnini.c
@@ -0,0 +1,621 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evrgnini- ACPI address_space (op_region) init
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evrgnini")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_system_memory_region_setup
+ *
+ * PARAMETERS: handle - Region we are interested in
+ * function - Start or stop
+ * handler_context - Address space handler context
+ * region_context - Region specific context
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Setup a system_memory operation region
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ev_system_memory_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context)
+{
+ union acpi_operand_object *region_desc =
+ (union acpi_operand_object *)handle;
+ struct acpi_mem_space_context *local_region_context;
+ struct acpi_mem_mapping *mm;
+
+ ACPI_FUNCTION_TRACE(ev_system_memory_region_setup);
+
+ if (function == ACPI_REGION_DEACTIVATE) {
+ if (*region_context) {
+ local_region_context =
+ (struct acpi_mem_space_context *)*region_context;
+
+ /* Delete memory mappings if present */
+
+ while (local_region_context->first_mm) {
+ mm = local_region_context->first_mm;
+ local_region_context->first_mm = mm->next_mm;
+ acpi_os_unmap_memory(mm->logical_address,
+ mm->length);
+ ACPI_FREE(mm);
+ }
+ ACPI_FREE(local_region_context);
+ *region_context = NULL;
+ }
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Create a new context */
+
+ local_region_context =
+ ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_mem_space_context));
+ if (!(local_region_context)) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Save the region length and address for use in the handler */
+
+ local_region_context->length = region_desc->region.length;
+ local_region_context->address = region_desc->region.address;
+
+ *region_context = local_region_context;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_io_space_region_setup
+ *
+ * PARAMETERS: handle - Region we are interested in
+ * function - Start or stop
+ * handler_context - Address space handler context
+ * region_context - Region specific context
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Setup a IO operation region
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_io_space_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context)
+{
+ ACPI_FUNCTION_TRACE(ev_io_space_region_setup);
+
+ if (function == ACPI_REGION_DEACTIVATE) {
+ *region_context = NULL;
+ } else {
+ *region_context = handler_context;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_pci_config_region_setup
+ *
+ * PARAMETERS: handle - Region we are interested in
+ * function - Start or stop
+ * handler_context - Address space handler context
+ * region_context - Region specific context
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Setup a PCI_Config operation region
+ *
+ * MUTEX: Assumes namespace is not locked
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_pci_config_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context)
+{
+ acpi_status status = AE_OK;
+ u64 pci_value;
+ struct acpi_pci_id *pci_id = *region_context;
+ union acpi_operand_object *handler_obj;
+ struct acpi_namespace_node *parent_node;
+ struct acpi_namespace_node *pci_root_node;
+ struct acpi_namespace_node *pci_device_node;
+ union acpi_operand_object *region_obj =
+ (union acpi_operand_object *)handle;
+
+ ACPI_FUNCTION_TRACE(ev_pci_config_region_setup);
+
+ handler_obj = region_obj->region.handler;
+ if (!handler_obj) {
+ /*
+ * No installed handler. This shouldn't happen because the dispatch
+ * routine checks before we get here, but we check again just in case.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Attempting to init a region %p, with no handler\n",
+ region_obj));
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ *region_context = NULL;
+ if (function == ACPI_REGION_DEACTIVATE) {
+ if (pci_id) {
+ ACPI_FREE(pci_id);
+ }
+ return_ACPI_STATUS(status);
+ }
+
+ parent_node = region_obj->region.node->parent;
+
+ /*
+ * Get the _SEG and _BBN values from the device upon which the handler
+ * is installed.
+ *
+ * We need to get the _SEG and _BBN objects relative to the PCI BUS device.
+ * This is the device the handler has been registered to handle.
+ */
+
+ /*
+ * If the address_space.Node is still pointing to the root, we need
+ * to scan upward for a PCI Root bridge and re-associate the op_region
+ * handlers with that device.
+ */
+ if (handler_obj->address_space.node == acpi_gbl_root_node) {
+
+ /* Start search from the parent object */
+
+ pci_root_node = parent_node;
+ while (pci_root_node != acpi_gbl_root_node) {
+
+ /* Get the _HID/_CID in order to detect a root_bridge */
+
+ if (acpi_ev_is_pci_root_bridge(pci_root_node)) {
+
+ /* Install a handler for this PCI root bridge */
+
+ status = acpi_install_address_space_handler((acpi_handle)pci_root_node, ACPI_ADR_SPACE_PCI_CONFIG, ACPI_DEFAULT_HANDLER, NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_SAME_HANDLER) {
+ /*
+ * It is OK if the handler is already installed on the
+ * root bridge. Still need to return a context object
+ * for the new PCI_Config operation region, however.
+ */
+ } else {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not install PciConfig handler "
+ "for Root Bridge %4.4s",
+ acpi_ut_get_node_name
+ (pci_root_node)));
+ }
+ }
+ break;
+ }
+
+ pci_root_node = pci_root_node->parent;
+ }
+
+ /* PCI root bridge not found, use namespace root node */
+ } else {
+ pci_root_node = handler_obj->address_space.node;
+ }
+
+ /*
+ * If this region is now initialized, we are done.
+ * (install_address_space_handler could have initialized it)
+ */
+ if (region_obj->region.flags & AOPOBJ_SETUP_COMPLETE) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Region is still not initialized. Create a new context */
+
+ pci_id = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pci_id));
+ if (!pci_id) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /*
+ * For PCI_Config space access, we need the segment, bus, device and
+ * function numbers. Acquire them here.
+ *
+ * Find the parent device object. (This allows the operation region to be
+ * within a subscope under the device, such as a control method.)
+ */
+ pci_device_node = region_obj->region.node;
+ while (pci_device_node && (pci_device_node->type != ACPI_TYPE_DEVICE)) {
+ pci_device_node = pci_device_node->parent;
+ }
+
+ if (!pci_device_node) {
+ ACPI_FREE(pci_id);
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * Get the PCI device and function numbers from the _ADR object
+ * contained in the parent's scope.
+ */
+ status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR,
+ pci_device_node, &pci_value);
+
+ /*
+ * The default is zero, and since the allocation above zeroed the data,
+ * just do nothing on failure.
+ */
+ if (ACPI_SUCCESS(status)) {
+ pci_id->device = ACPI_HIWORD(ACPI_LODWORD(pci_value));
+ pci_id->function = ACPI_LOWORD(ACPI_LODWORD(pci_value));
+ }
+
+ /* The PCI segment number comes from the _SEG method */
+
+ status = acpi_ut_evaluate_numeric_object(METHOD_NAME__SEG,
+ pci_root_node, &pci_value);
+ if (ACPI_SUCCESS(status)) {
+ pci_id->segment = ACPI_LOWORD(pci_value);
+ }
+
+ /* The PCI bus number comes from the _BBN method */
+
+ status = acpi_ut_evaluate_numeric_object(METHOD_NAME__BBN,
+ pci_root_node, &pci_value);
+ if (ACPI_SUCCESS(status)) {
+ pci_id->bus = ACPI_LOWORD(pci_value);
+ }
+
+ /* Complete/update the PCI ID for this device */
+
+ status =
+ acpi_hw_derive_pci_id(pci_id, pci_root_node,
+ region_obj->region.node);
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(pci_id);
+ return_ACPI_STATUS(status);
+ }
+
+ *region_context = pci_id;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_is_pci_root_bridge
+ *
+ * PARAMETERS: node - Device node being examined
+ *
+ * RETURN: TRUE if device is a PCI/PCI-Express Root Bridge
+ *
+ * DESCRIPTION: Determine if the input device represents a PCI Root Bridge by
+ * examining the _HID and _CID for the device.
+ *
+ ******************************************************************************/
+
+u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node)
+{
+ acpi_status status;
+ struct acpi_pnp_device_id *hid;
+ struct acpi_pnp_device_id_list *cid;
+ u32 i;
+ u8 match;
+
+ /* Get the _HID and check for a PCI Root Bridge */
+
+ status = acpi_ut_execute_HID(node, &hid);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+
+ match = acpi_ut_is_pci_root_bridge(hid->string);
+ ACPI_FREE(hid);
+
+ if (match) {
+ return (TRUE);
+ }
+
+ /* The _HID did not match. Get the _CID and check for a PCI Root Bridge */
+
+ status = acpi_ut_execute_CID(node, &cid);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+
+ /* Check all _CIDs in the returned list */
+
+ for (i = 0; i < cid->count; i++) {
+ if (acpi_ut_is_pci_root_bridge(cid->ids[i].string)) {
+ ACPI_FREE(cid);
+ return (TRUE);
+ }
+ }
+
+ ACPI_FREE(cid);
+ return (FALSE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_pci_bar_region_setup
+ *
+ * PARAMETERS: handle - Region we are interested in
+ * function - Start or stop
+ * handler_context - Address space handler context
+ * region_context - Region specific context
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Setup a pci_BAR operation region
+ *
+ * MUTEX: Assumes namespace is not locked
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_pci_bar_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context)
+{
+ ACPI_FUNCTION_TRACE(ev_pci_bar_region_setup);
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_cmos_region_setup
+ *
+ * PARAMETERS: handle - Region we are interested in
+ * function - Start or stop
+ * handler_context - Address space handler context
+ * region_context - Region specific context
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Setup a CMOS operation region
+ *
+ * MUTEX: Assumes namespace is not locked
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_cmos_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context)
+{
+ ACPI_FUNCTION_TRACE(ev_cmos_region_setup);
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_data_table_region_setup
+ *
+ * PARAMETERS: handle - Region we are interested in
+ * function - Start or stop
+ * handler_context - Address space handler context
+ * region_context - Region specific context
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Setup a data_table_region
+ *
+ * MUTEX: Assumes namespace is not locked
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_data_table_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context)
+{
+ union acpi_operand_object *region_desc =
+ (union acpi_operand_object *)handle;
+ struct acpi_data_table_space_context *local_region_context;
+
+ ACPI_FUNCTION_TRACE(ev_data_table_region_setup);
+
+ if (function == ACPI_REGION_DEACTIVATE) {
+ if (*region_context) {
+ ACPI_FREE(*region_context);
+ *region_context = NULL;
+ }
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Create a new context */
+
+ local_region_context =
+ ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_data_table_space_context));
+ if (!(local_region_context)) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Save the data table pointer for use in the handler */
+
+ local_region_context->pointer = region_desc->region.pointer;
+
+ *region_context = local_region_context;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_default_region_setup
+ *
+ * PARAMETERS: handle - Region we are interested in
+ * function - Start or stop
+ * handler_context - Address space handler context
+ * region_context - Region specific context
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Default region initialization
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_default_region_setup(acpi_handle handle,
+ u32 function,
+ void *handler_context, void **region_context)
+{
+ ACPI_FUNCTION_TRACE(ev_default_region_setup);
+
+ if (function == ACPI_REGION_DEACTIVATE) {
+ *region_context = NULL;
+ } else {
+ *region_context = handler_context;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_initialize_region
+ *
+ * PARAMETERS: region_obj - Region we are initializing
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initializes the region, finds any _REG methods and saves them
+ * for execution at a later time
+ *
+ * Get the appropriate address space handler for a newly
+ * created region.
+ *
+ * This also performs address space specific initialization. For
+ * example, PCI regions must have an _ADR object that contains
+ * a PCI address in the scope of the definition. This address is
+ * required to perform an access to PCI config space.
+ *
+ * MUTEX: Interpreter should be unlocked, because we may run the _REG
+ * method for this region.
+ *
+ * NOTE: Possible incompliance:
+ * There is a behavior conflict in automatic _REG execution:
+ * 1. When the interpreter is evaluating a method, we can only
+ * automatically run _REG for the following case:
+ * operation_region (OPR1, 0x80, 0x1000010, 0x4)
+ * 2. When the interpreter is loading a table, we can also
+ * automatically run _REG for the following case:
+ * operation_region (OPR1, 0x80, 0x1000010, 0x4)
+ * Though this may not be compliant to the de-facto standard, the
+ * logic is kept in order not to trigger regressions. And keeping
+ * this logic should be taken care by the caller of this function.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_initialize_region(union acpi_operand_object *region_obj)
+{
+ union acpi_operand_object *handler_obj;
+ union acpi_operand_object *obj_desc;
+ acpi_adr_space_type space_id;
+ struct acpi_namespace_node *node;
+
+ ACPI_FUNCTION_TRACE(ev_initialize_region);
+
+ if (!region_obj) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if (region_obj->common.flags & AOPOBJ_OBJECT_INITIALIZED) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ region_obj->common.flags |= AOPOBJ_OBJECT_INITIALIZED;
+
+ node = region_obj->region.node->parent;
+ space_id = region_obj->region.space_id;
+
+ /*
+ * The following loop depends upon the root Node having no parent
+ * ie: acpi_gbl_root_node->Parent being set to NULL
+ */
+ while (node) {
+
+ /* Check to see if a handler exists */
+
+ handler_obj = NULL;
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (obj_desc) {
+
+ /* Can only be a handler if the object exists */
+
+ switch (node->type) {
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+
+ handler_obj = obj_desc->common_notify.handler;
+ break;
+
+ default:
+
+ /* Ignore other objects */
+
+ break;
+ }
+
+ handler_obj =
+ acpi_ev_find_region_handler(space_id, handler_obj);
+ if (handler_obj) {
+
+ /* Found correct handler */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Found handler %p for region %p in obj %p\n",
+ handler_obj, region_obj,
+ obj_desc));
+
+ (void)acpi_ev_attach_region(handler_obj,
+ region_obj, FALSE);
+
+ /*
+ * Tell all users that this region is usable by
+ * running the _REG method
+ */
+ acpi_ex_exit_interpreter();
+ (void)acpi_ev_execute_reg_method(region_obj,
+ ACPI_REG_CONNECT);
+ acpi_ex_enter_interpreter();
+ return_ACPI_STATUS(AE_OK);
+ }
+ }
+
+ /* This node does not have the handler we need; Pop up one level */
+
+ node = node->parent;
+ }
+
+ /*
+ * If we get here, there is no handler for this region. This is not
+ * fatal because many regions get created before a handler is installed
+ * for said region.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "No handler for RegionType %s(%X) (RegionObj %p)\n",
+ acpi_ut_get_region_name(space_id), space_id,
+ region_obj));
+
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/evsci.c b/drivers/acpi/acpica/evsci.c
new file mode 100644
index 000000000..3915ff614
--- /dev/null
+++ b/drivers/acpi/acpica/evsci.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: evsci - System Control Interrupt configuration and
+ * legacy to ACPI mode state transition functions
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evsci")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/* Local prototypes */
+static u32 ACPI_SYSTEM_XFACE acpi_ev_sci_xrupt_handler(void *context);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_sci_dispatch
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status code indicates whether interrupt was handled.
+ *
+ * DESCRIPTION: Dispatch the SCI to all host-installed SCI handlers.
+ *
+ ******************************************************************************/
+
+u32 acpi_ev_sci_dispatch(void)
+{
+ struct acpi_sci_handler_info *sci_handler;
+ acpi_cpu_flags flags;
+ u32 int_status = ACPI_INTERRUPT_NOT_HANDLED;
+
+ ACPI_FUNCTION_NAME(ev_sci_dispatch);
+
+ /* Are there any host-installed SCI handlers? */
+
+ if (!acpi_gbl_sci_handler_list) {
+ return (int_status);
+ }
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Invoke all host-installed SCI handlers */
+
+ sci_handler = acpi_gbl_sci_handler_list;
+ while (sci_handler) {
+
+ /* Invoke the installed handler (at interrupt level) */
+
+ int_status |= sci_handler->address(sci_handler->context);
+
+ sci_handler = sci_handler->next;
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return (int_status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_sci_xrupt_handler
+ *
+ * PARAMETERS: context - Calling Context
+ *
+ * RETURN: Status code indicates whether interrupt was handled.
+ *
+ * DESCRIPTION: Interrupt handler that will figure out what function or
+ * control method to call to deal with a SCI.
+ *
+ ******************************************************************************/
+
+static u32 ACPI_SYSTEM_XFACE acpi_ev_sci_xrupt_handler(void *context)
+{
+ struct acpi_gpe_xrupt_info *gpe_xrupt_list = context;
+ u32 interrupt_handled = ACPI_INTERRUPT_NOT_HANDLED;
+
+ ACPI_FUNCTION_TRACE(ev_sci_xrupt_handler);
+
+ /*
+ * We are guaranteed by the ACPICA initialization/shutdown code that
+ * if this interrupt handler is installed, ACPI is enabled.
+ */
+
+ /*
+ * Fixed Events:
+ * Check for and dispatch any Fixed Events that have occurred
+ */
+ interrupt_handled |= acpi_ev_fixed_event_detect();
+
+ /*
+ * General Purpose Events:
+ * Check for and dispatch any GPEs that have occurred
+ */
+ interrupt_handled |= acpi_ev_gpe_detect(gpe_xrupt_list);
+
+ /* Invoke all host-installed SCI handlers */
+
+ interrupt_handled |= acpi_ev_sci_dispatch();
+
+ acpi_sci_count++;
+ return_UINT32(interrupt_handled);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_gpe_xrupt_handler
+ *
+ * PARAMETERS: context - Calling Context
+ *
+ * RETURN: Status code indicates whether interrupt was handled.
+ *
+ * DESCRIPTION: Handler for GPE Block Device interrupts
+ *
+ ******************************************************************************/
+
+u32 ACPI_SYSTEM_XFACE acpi_ev_gpe_xrupt_handler(void *context)
+{
+ struct acpi_gpe_xrupt_info *gpe_xrupt_list = context;
+ u32 interrupt_handled = ACPI_INTERRUPT_NOT_HANDLED;
+
+ ACPI_FUNCTION_TRACE(ev_gpe_xrupt_handler);
+
+ /*
+ * We are guaranteed by the ACPICA initialization/shutdown code that
+ * if this interrupt handler is installed, ACPI is enabled.
+ */
+
+ /* GPEs: Check for and dispatch any GPEs that have occurred */
+
+ interrupt_handled |= acpi_ev_gpe_detect(gpe_xrupt_list);
+ return_UINT32(interrupt_handled);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ev_install_sci_handler
+ *
+ * PARAMETERS: none
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Installs SCI handler.
+ *
+ ******************************************************************************/
+
+u32 acpi_ev_install_sci_handler(void)
+{
+ u32 status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ev_install_sci_handler);
+
+ status =
+ acpi_os_install_interrupt_handler((u32) acpi_gbl_FADT.sci_interrupt,
+ acpi_ev_sci_xrupt_handler,
+ acpi_gbl_gpe_xrupt_list_head);
+ return_ACPI_STATUS(status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ev_remove_all_sci_handlers
+ *
+ * PARAMETERS: none
+ *
+ * RETURN: AE_OK if handler uninstalled, AE_ERROR if handler was not
+ * installed to begin with
+ *
+ * DESCRIPTION: Remove the SCI interrupt handler. No further SCIs will be
+ * taken. Remove all host-installed SCI handlers.
+ *
+ * Note: It doesn't seem important to disable all events or set the event
+ * enable registers to their original values. The OS should disable
+ * the SCI interrupt level when the handler is removed, so no more
+ * events will come in.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_remove_all_sci_handlers(void)
+{
+ struct acpi_sci_handler_info *sci_handler;
+ acpi_cpu_flags flags;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_remove_all_sci_handlers);
+
+ /* Just let the OS remove the handler and disable the level */
+
+ status =
+ acpi_os_remove_interrupt_handler((u32) acpi_gbl_FADT.sci_interrupt,
+ acpi_ev_sci_xrupt_handler);
+
+ if (!acpi_gbl_sci_handler_list) {
+ return (status);
+ }
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Free all host-installed SCI handlers */
+
+ while (acpi_gbl_sci_handler_list) {
+ sci_handler = acpi_gbl_sci_handler_list;
+ acpi_gbl_sci_handler_list = sci_handler->next;
+ ACPI_FREE(sci_handler);
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
new file mode 100644
index 000000000..18219abba
--- /dev/null
+++ b/drivers/acpi/acpica/evxface.c
@@ -0,0 +1,1079 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evxface - External interfaces for ACPI events
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acevents.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evxface")
+#if (!ACPI_REDUCED_HARDWARE)
+/* Local prototypes */
+static acpi_status
+acpi_ev_install_gpe_handler(acpi_handle gpe_device,
+ u32 gpe_number,
+ u32 type,
+ u8 is_raw_handler,
+ acpi_gpe_handler address, void *context);
+
+#endif
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_notify_handler
+ *
+ * PARAMETERS: device - The device for which notifies will be handled
+ * handler_type - The type of handler:
+ * ACPI_SYSTEM_NOTIFY: System Handler (00-7F)
+ * ACPI_DEVICE_NOTIFY: Device Handler (80-FF)
+ * ACPI_ALL_NOTIFY: Both System and Device
+ * handler - Address of the handler
+ * context - Value passed to the handler on each GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for notifications on an ACPI Device,
+ * thermal_zone, or Processor object.
+ *
+ * NOTES: The Root namespace object may have only one handler for each
+ * type of notify (System/Device). Device/Thermal/Processor objects
+ * may have one device notify handler, and multiple system notify
+ * handlers.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_install_notify_handler(acpi_handle device,
+ u32 handler_type,
+ acpi_notify_handler handler, void *context)
+{
+ struct acpi_namespace_node *node =
+ ACPI_CAST_PTR(struct acpi_namespace_node, device);
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *handler_obj;
+ acpi_status status;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(acpi_install_notify_handler);
+
+ /* Parameter validation */
+
+ if ((!device) || (!handler) || (!handler_type) ||
+ (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Root Object:
+ * Registering a notify handler on the root object indicates that the
+ * caller wishes to receive notifications for all objects. Note that
+ * only one global handler can be registered per notify type.
+ * Ensure that a handler is not already installed.
+ */
+ if (device == ACPI_ROOT_OBJECT) {
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
+ if (handler_type & (i + 1)) {
+ if (acpi_gbl_global_notify[i].handler) {
+ status = AE_ALREADY_EXISTS;
+ goto unlock_and_exit;
+ }
+
+ acpi_gbl_global_notify[i].handler = handler;
+ acpi_gbl_global_notify[i].context = context;
+ }
+ }
+
+ goto unlock_and_exit; /* Global notify handler installed, all done */
+ }
+
+ /*
+ * All Other Objects:
+ * Caller will only receive notifications specific to the target
+ * object. Note that only certain object types are allowed to
+ * receive notifications.
+ */
+
+ /* Are Notifies allowed on this object? */
+
+ if (!acpi_ev_is_notify_object(node)) {
+ status = AE_TYPE;
+ goto unlock_and_exit;
+ }
+
+ /* Check for an existing internal object, might not exist */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+
+ /* Create a new object */
+
+ obj_desc = acpi_ut_create_internal_object(node->type);
+ if (!obj_desc) {
+ status = AE_NO_MEMORY;
+ goto unlock_and_exit;
+ }
+
+ /* Attach new object to the Node, remove local reference */
+
+ status = acpi_ns_attach_object(device, obj_desc, node->type);
+ acpi_ut_remove_reference(obj_desc);
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+ }
+
+ /* Ensure that the handler is not already installed in the lists */
+
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
+ if (handler_type & (i + 1)) {
+ handler_obj = obj_desc->common_notify.notify_list[i];
+ while (handler_obj) {
+ if (handler_obj->notify.handler == handler) {
+ status = AE_ALREADY_EXISTS;
+ goto unlock_and_exit;
+ }
+
+ handler_obj = handler_obj->notify.next[i];
+ }
+ }
+ }
+
+ /* Create and populate a new notify handler object */
+
+ handler_obj = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_NOTIFY);
+ if (!handler_obj) {
+ status = AE_NO_MEMORY;
+ goto unlock_and_exit;
+ }
+
+ handler_obj->notify.node = node;
+ handler_obj->notify.handler_type = handler_type;
+ handler_obj->notify.handler = handler;
+ handler_obj->notify.context = context;
+
+ /* Install the handler at the list head(s) */
+
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
+ if (handler_type & (i + 1)) {
+ handler_obj->notify.next[i] =
+ obj_desc->common_notify.notify_list[i];
+
+ obj_desc->common_notify.notify_list[i] = handler_obj;
+ }
+ }
+
+ /* Add an extra reference if handler was installed in both lists */
+
+ if (handler_type == ACPI_ALL_NOTIFY) {
+ acpi_ut_add_reference(handler_obj);
+ }
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_notify_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_remove_notify_handler
+ *
+ * PARAMETERS: device - The device for which the handler is installed
+ * handler_type - The type of handler:
+ * ACPI_SYSTEM_NOTIFY: System Handler (00-7F)
+ * ACPI_DEVICE_NOTIFY: Device Handler (80-FF)
+ * ACPI_ALL_NOTIFY: Both System and Device
+ * handler - Address of the handler
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove a handler for notifies on an ACPI device
+ *
+ ******************************************************************************/
+acpi_status
+acpi_remove_notify_handler(acpi_handle device,
+ u32 handler_type, acpi_notify_handler handler)
+{
+ struct acpi_namespace_node *node =
+ ACPI_CAST_PTR(struct acpi_namespace_node, device);
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *handler_obj;
+ union acpi_operand_object *previous_handler_obj;
+ acpi_status status = AE_OK;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(acpi_remove_notify_handler);
+
+ /* Parameter validation */
+
+ if ((!device) || (!handler) || (!handler_type) ||
+ (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Root Object. Global handlers are removed here */
+
+ if (device == ACPI_ROOT_OBJECT) {
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
+ if (handler_type & (i + 1)) {
+ status =
+ acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (!acpi_gbl_global_notify[i].handler ||
+ (acpi_gbl_global_notify[i].handler !=
+ handler)) {
+ status = AE_NOT_EXIST;
+ goto unlock_and_exit;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Removing global notify handler\n"));
+
+ acpi_gbl_global_notify[i].handler = NULL;
+ acpi_gbl_global_notify[i].context = NULL;
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+ /* Make sure all deferred notify tasks are completed */
+
+ acpi_os_wait_events_complete();
+ }
+ }
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* All other objects: Are Notifies allowed on this object? */
+
+ if (!acpi_ev_is_notify_object(node)) {
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ /* Must have an existing internal object */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /* Internal object exists. Find the handler and remove it */
+
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
+ if (handler_type & (i + 1)) {
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ handler_obj = obj_desc->common_notify.notify_list[i];
+ previous_handler_obj = NULL;
+
+ /* Attempt to find the handler in the handler list */
+
+ while (handler_obj &&
+ (handler_obj->notify.handler != handler)) {
+ previous_handler_obj = handler_obj;
+ handler_obj = handler_obj->notify.next[i];
+ }
+
+ if (!handler_obj) {
+ status = AE_NOT_EXIST;
+ goto unlock_and_exit;
+ }
+
+ /* Remove the handler object from the list */
+
+ if (previous_handler_obj) { /* Handler is not at the list head */
+ previous_handler_obj->notify.next[i] =
+ handler_obj->notify.next[i];
+ } else { /* Handler is at the list head */
+
+ obj_desc->common_notify.notify_list[i] =
+ handler_obj->notify.next[i];
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+ /* Make sure all deferred notify tasks are completed */
+
+ acpi_os_wait_events_complete();
+ acpi_ut_remove_reference(handler_obj);
+ }
+ }
+
+ return_ACPI_STATUS(status);
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_notify_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_exception_handler
+ *
+ * PARAMETERS: handler - Pointer to the handler function for the
+ * event
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Saves the pointer to the handler function
+ *
+ ******************************************************************************/
+#ifdef ACPI_FUTURE_USAGE
+acpi_status acpi_install_exception_handler(acpi_exception_handler handler)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_exception_handler);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Don't allow two handlers. */
+
+ if (acpi_gbl_exception_handler) {
+ status = AE_ALREADY_EXISTS;
+ goto cleanup;
+ }
+
+ /* Install the handler */
+
+ acpi_gbl_exception_handler = handler;
+
+cleanup:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_exception_handler)
+#endif
+
+#if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_sci_handler
+ *
+ * PARAMETERS: address - Address of the handler
+ * context - Value passed to the handler on each SCI
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for a System Control Interrupt.
+ *
+ ******************************************************************************/
+acpi_status acpi_install_sci_handler(acpi_sci_handler address, void *context)
+{
+ struct acpi_sci_handler_info *new_sci_handler;
+ struct acpi_sci_handler_info *sci_handler;
+ acpi_cpu_flags flags;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_sci_handler);
+
+ if (!address) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Allocate and init a handler object */
+
+ new_sci_handler = ACPI_ALLOCATE(sizeof(struct acpi_sci_handler_info));
+ if (!new_sci_handler) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ new_sci_handler->address = address;
+ new_sci_handler->context = context;
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Lock list during installation */
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ sci_handler = acpi_gbl_sci_handler_list;
+
+ /* Ensure handler does not already exist */
+
+ while (sci_handler) {
+ if (address == sci_handler->address) {
+ status = AE_ALREADY_EXISTS;
+ goto unlock_and_exit;
+ }
+
+ sci_handler = sci_handler->next;
+ }
+
+ /* Install the new handler into the global list (at head) */
+
+ new_sci_handler->next = acpi_gbl_sci_handler_list;
+ acpi_gbl_sci_handler_list = new_sci_handler;
+
+unlock_and_exit:
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+exit:
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(new_sci_handler);
+ }
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_sci_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_remove_sci_handler
+ *
+ * PARAMETERS: address - Address of the handler
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove a handler for a System Control Interrupt.
+ *
+ ******************************************************************************/
+acpi_status acpi_remove_sci_handler(acpi_sci_handler address)
+{
+ struct acpi_sci_handler_info *prev_sci_handler;
+ struct acpi_sci_handler_info *next_sci_handler;
+ acpi_cpu_flags flags;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_remove_sci_handler);
+
+ if (!address) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Remove the SCI handler with lock */
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ prev_sci_handler = NULL;
+ next_sci_handler = acpi_gbl_sci_handler_list;
+ while (next_sci_handler) {
+ if (next_sci_handler->address == address) {
+
+ /* Unlink and free the SCI handler info block */
+
+ if (prev_sci_handler) {
+ prev_sci_handler->next = next_sci_handler->next;
+ } else {
+ acpi_gbl_sci_handler_list =
+ next_sci_handler->next;
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ ACPI_FREE(next_sci_handler);
+ goto unlock_and_exit;
+ }
+
+ prev_sci_handler = next_sci_handler;
+ next_sci_handler = next_sci_handler->next;
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ status = AE_NOT_EXIST;
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_sci_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_global_event_handler
+ *
+ * PARAMETERS: handler - Pointer to the global event handler function
+ * context - Value passed to the handler on each event
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Saves the pointer to the handler function. The global handler
+ * is invoked upon each incoming GPE and Fixed Event. It is
+ * invoked at interrupt level at the time of the event dispatch.
+ * Can be used to update event counters, etc.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_install_global_event_handler(acpi_gbl_event_handler handler, void *context)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_global_event_handler);
+
+ /* Parameter validation */
+
+ if (!handler) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Don't allow two handlers. */
+
+ if (acpi_gbl_global_event_handler) {
+ status = AE_ALREADY_EXISTS;
+ goto cleanup;
+ }
+
+ acpi_gbl_global_event_handler = handler;
+ acpi_gbl_global_event_handler_context = context;
+
+cleanup:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_global_event_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_fixed_event_handler
+ *
+ * PARAMETERS: event - Event type to enable.
+ * handler - Pointer to the handler function for the
+ * event
+ * context - Value passed to the handler on each GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Saves the pointer to the handler function and then enables the
+ * event.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_install_fixed_event_handler(u32 event,
+ acpi_event_handler handler, void *context)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_fixed_event_handler);
+
+ /* Parameter validation */
+
+ if (event > ACPI_EVENT_MAX) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Do not allow multiple handlers */
+
+ if (acpi_gbl_fixed_event_handlers[event].handler) {
+ status = AE_ALREADY_EXISTS;
+ goto cleanup;
+ }
+
+ /* Install the handler before enabling the event */
+
+ acpi_gbl_fixed_event_handlers[event].handler = handler;
+ acpi_gbl_fixed_event_handlers[event].context = context;
+
+ status = acpi_clear_event(event);
+ if (ACPI_SUCCESS(status))
+ status = acpi_enable_event(event, 0);
+ if (ACPI_FAILURE(status)) {
+ ACPI_WARNING((AE_INFO,
+ "Could not enable fixed event - %s (%u)",
+ acpi_ut_get_event_name(event), event));
+
+ /* Remove the handler */
+
+ acpi_gbl_fixed_event_handlers[event].handler = NULL;
+ acpi_gbl_fixed_event_handlers[event].context = NULL;
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Enabled fixed event %s (%X), Handler=%p\n",
+ acpi_ut_get_event_name(event), event,
+ handler));
+ }
+
+cleanup:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_fixed_event_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_remove_fixed_event_handler
+ *
+ * PARAMETERS: event - Event type to disable.
+ * handler - Address of the handler
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Disables the event and unregisters the event handler.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_remove_fixed_event_handler(u32 event, acpi_event_handler handler)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(acpi_remove_fixed_event_handler);
+
+ /* Parameter validation */
+
+ if (event > ACPI_EVENT_MAX) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Disable the event before removing the handler */
+
+ status = acpi_disable_event(event, 0);
+
+ /* Always Remove the handler */
+
+ acpi_gbl_fixed_event_handlers[event].handler = NULL;
+ acpi_gbl_fixed_event_handlers[event].context = NULL;
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_WARNING((AE_INFO,
+ "Could not disable fixed event - %s (%u)",
+ acpi_ut_get_event_name(event), event));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Disabled fixed event - %s (%X)\n",
+ acpi_ut_get_event_name(event), event));
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_fixed_event_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_install_gpe_handler
+ *
+ * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT
+ * defined GPEs)
+ * gpe_number - The GPE number within the GPE block
+ * type - Whether this GPE should be treated as an
+ * edge- or level-triggered interrupt.
+ * is_raw_handler - Whether this GPE should be handled using
+ * the special GPE handler mode.
+ * address - Address of the handler
+ * context - Value passed to the handler on each GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Internal function to install a handler for a General Purpose
+ * Event.
+ *
+ ******************************************************************************/
+static acpi_status
+acpi_ev_install_gpe_handler(acpi_handle gpe_device,
+ u32 gpe_number,
+ u32 type,
+ u8 is_raw_handler,
+ acpi_gpe_handler address, void *context)
+{
+ struct acpi_gpe_event_info *gpe_event_info;
+ struct acpi_gpe_handler_info *handler;
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(ev_install_gpe_handler);
+
+ /* Parameter validation */
+
+ if ((!address) || (type & ~ACPI_GPE_XRUPT_TYPE_MASK)) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Allocate and init handler object (before lock) */
+
+ handler = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_handler_info));
+ if (!handler) {
+ status = AE_NO_MEMORY;
+ goto unlock_and_exit;
+ }
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info) {
+ status = AE_BAD_PARAMETER;
+ goto free_and_exit;
+ }
+
+ /* Make sure that there isn't a handler there already */
+
+ if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_HANDLER) ||
+ (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_RAW_HANDLER)) {
+ status = AE_ALREADY_EXISTS;
+ goto free_and_exit;
+ }
+
+ handler->address = address;
+ handler->context = context;
+ handler->method_node = gpe_event_info->dispatch.method_node;
+ handler->original_flags = (u8)(gpe_event_info->flags &
+ (ACPI_GPE_XRUPT_TYPE_MASK |
+ ACPI_GPE_DISPATCH_MASK));
+
+ /*
+ * If the GPE is associated with a method, it may have been enabled
+ * automatically during initialization, in which case it has to be
+ * disabled now to avoid spurious execution of the handler.
+ */
+ if (((ACPI_GPE_DISPATCH_TYPE(handler->original_flags) ==
+ ACPI_GPE_DISPATCH_METHOD) ||
+ (ACPI_GPE_DISPATCH_TYPE(handler->original_flags) ==
+ ACPI_GPE_DISPATCH_NOTIFY)) && gpe_event_info->runtime_count) {
+ handler->originally_enabled = TRUE;
+ (void)acpi_ev_remove_gpe_reference(gpe_event_info);
+
+ /* Sanity check of original type against new type */
+
+ if (type !=
+ (u32)(gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK)) {
+ ACPI_WARNING((AE_INFO,
+ "GPE type mismatch (level/edge)"));
+ }
+ }
+
+ /* Install the handler */
+
+ gpe_event_info->dispatch.handler = handler;
+
+ /* Setup up dispatch flags to indicate handler (vs. method/notify) */
+
+ gpe_event_info->flags &=
+ ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK);
+ gpe_event_info->flags |=
+ (u8)(type |
+ (is_raw_handler ? ACPI_GPE_DISPATCH_RAW_HANDLER :
+ ACPI_GPE_DISPATCH_HANDLER));
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+
+free_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ ACPI_FREE(handler);
+ goto unlock_and_exit;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_gpe_handler
+ *
+ * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT
+ * defined GPEs)
+ * gpe_number - The GPE number within the GPE block
+ * type - Whether this GPE should be treated as an
+ * edge- or level-triggered interrupt.
+ * address - Address of the handler
+ * context - Value passed to the handler on each GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for a General Purpose Event.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_install_gpe_handler(acpi_handle gpe_device,
+ u32 gpe_number,
+ u32 type, acpi_gpe_handler address, void *context)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_gpe_handler);
+
+ status = acpi_ev_install_gpe_handler(gpe_device, gpe_number, type,
+ FALSE, address, context);
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_gpe_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_gpe_raw_handler
+ *
+ * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT
+ * defined GPEs)
+ * gpe_number - The GPE number within the GPE block
+ * type - Whether this GPE should be treated as an
+ * edge- or level-triggered interrupt.
+ * address - Address of the handler
+ * context - Value passed to the handler on each GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for a General Purpose Event.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_install_gpe_raw_handler(acpi_handle gpe_device,
+ u32 gpe_number,
+ u32 type, acpi_gpe_handler address, void *context)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_gpe_raw_handler);
+
+ status = acpi_ev_install_gpe_handler(gpe_device, gpe_number, type,
+ TRUE, address, context);
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_gpe_raw_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_remove_gpe_handler
+ *
+ * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT
+ * defined GPEs)
+ * gpe_number - The event to remove a handler
+ * address - Address of the handler
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove a handler for a General Purpose acpi_event.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_remove_gpe_handler(acpi_handle gpe_device,
+ u32 gpe_number, acpi_gpe_handler address)
+{
+ struct acpi_gpe_event_info *gpe_event_info;
+ struct acpi_gpe_handler_info *handler;
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_remove_gpe_handler);
+
+ /* Parameter validation */
+
+ if (!address) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /* Make sure that a handler is indeed installed */
+
+ if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) !=
+ ACPI_GPE_DISPATCH_HANDLER) &&
+ (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) !=
+ ACPI_GPE_DISPATCH_RAW_HANDLER)) {
+ status = AE_NOT_EXIST;
+ goto unlock_and_exit;
+ }
+
+ /* Make sure that the installed handler is the same */
+
+ if (gpe_event_info->dispatch.handler->address != address) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /* Remove the handler */
+
+ handler = gpe_event_info->dispatch.handler;
+ gpe_event_info->dispatch.handler = NULL;
+
+ /* Restore Method node (if any), set dispatch flags */
+
+ gpe_event_info->dispatch.method_node = handler->method_node;
+ gpe_event_info->flags &=
+ ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK);
+ gpe_event_info->flags |= handler->original_flags;
+
+ /*
+ * If the GPE was previously associated with a method and it was
+ * enabled, it should be enabled at this point to restore the
+ * post-initialization configuration.
+ */
+ if (((ACPI_GPE_DISPATCH_TYPE(handler->original_flags) ==
+ ACPI_GPE_DISPATCH_METHOD) ||
+ (ACPI_GPE_DISPATCH_TYPE(handler->original_flags) ==
+ ACPI_GPE_DISPATCH_NOTIFY)) && handler->originally_enabled) {
+ (void)acpi_ev_add_gpe_reference(gpe_event_info, FALSE);
+ if (ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) {
+
+ /* Poll edge triggered GPEs to handle existing events */
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ (void)acpi_ev_detect_gpe(gpe_device, gpe_event_info,
+ gpe_number);
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ }
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+ /* Make sure all deferred GPE tasks are completed */
+
+ acpi_os_wait_events_complete();
+
+ /* Now we can free the handler object */
+
+ ACPI_FREE(handler);
+ return_ACPI_STATUS(status);
+
+unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_gpe_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_acquire_global_lock
+ *
+ * PARAMETERS: timeout - How long the caller is willing to wait
+ * handle - Where the handle to the lock is returned
+ * (if acquired)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Acquire the ACPI Global Lock
+ *
+ * Note: Allows callers with the same thread ID to acquire the global lock
+ * multiple times. In other words, externally, the behavior of the global lock
+ * is identical to an AML mutex. On the first acquire, a new handle is
+ * returned. On any subsequent calls to acquire by the same thread, the same
+ * handle is returned.
+ *
+ ******************************************************************************/
+acpi_status acpi_acquire_global_lock(u16 timeout, u32 *handle)
+{
+ acpi_status status;
+
+ if (!handle) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Must lock interpreter to prevent race conditions */
+
+ acpi_ex_enter_interpreter();
+
+ status = acpi_ex_acquire_mutex_object(timeout,
+ acpi_gbl_global_lock_mutex,
+ acpi_os_get_thread_id());
+
+ if (ACPI_SUCCESS(status)) {
+
+ /* Return the global lock handle (updated in acpi_ev_acquire_global_lock) */
+
+ *handle = acpi_gbl_global_lock_handle;
+ }
+
+ acpi_ex_exit_interpreter();
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_acquire_global_lock)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_release_global_lock
+ *
+ * PARAMETERS: handle - Returned from acpi_acquire_global_lock
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Release the ACPI Global Lock. The handle must be valid.
+ *
+ ******************************************************************************/
+acpi_status acpi_release_global_lock(u32 handle)
+{
+ acpi_status status;
+
+ if (!handle || (handle != acpi_gbl_global_lock_handle)) {
+ return (AE_NOT_ACQUIRED);
+ }
+
+ status = acpi_ex_release_mutex_object(acpi_gbl_global_lock_mutex);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_release_global_lock)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
new file mode 100644
index 000000000..8187b081e
--- /dev/null
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -0,0 +1,365 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evxfevnt - External Interfaces, ACPI event disable/enable
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evxfevnt")
+
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_enable
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Transfers the system into ACPI mode.
+ *
+ ******************************************************************************/
+acpi_status acpi_enable(void)
+{
+ acpi_status status;
+ int retry;
+
+ ACPI_FUNCTION_TRACE(acpi_enable);
+
+ /* ACPI tables must be present */
+
+ if (acpi_gbl_fadt_index == ACPI_INVALID_TABLE_INDEX) {
+ return_ACPI_STATUS(AE_NO_ACPI_TABLES);
+ }
+
+ /* If the Hardware Reduced flag is set, machine is always in acpi mode */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Check current mode */
+
+ if (acpi_hw_get_mode() == ACPI_SYS_MODE_ACPI) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INIT,
+ "System is already in ACPI mode\n"));
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Transition to ACPI mode */
+
+ status = acpi_hw_set_mode(ACPI_SYS_MODE_ACPI);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO,
+ "Could not transition to ACPI mode"));
+ return_ACPI_STATUS(status);
+ }
+
+ /* Sanity check that transition succeeded */
+
+ for (retry = 0; retry < 30000; ++retry) {
+ if (acpi_hw_get_mode() == ACPI_SYS_MODE_ACPI) {
+ if (retry != 0)
+ ACPI_WARNING((AE_INFO,
+ "Platform took > %d00 usec to enter ACPI mode", retry));
+ return_ACPI_STATUS(AE_OK);
+ }
+ acpi_os_stall(100); /* 100 usec */
+ }
+
+ ACPI_ERROR((AE_INFO, "Hardware did not enter ACPI mode"));
+ return_ACPI_STATUS(AE_NO_HARDWARE_RESPONSE);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enable)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_disable
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Transfers the system into LEGACY (non-ACPI) mode.
+ *
+ ******************************************************************************/
+acpi_status acpi_disable(void)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(acpi_disable);
+
+ /* If the Hardware Reduced flag is set, machine is always in acpi mode */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ if (acpi_hw_get_mode() == ACPI_SYS_MODE_LEGACY) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INIT,
+ "System is already in legacy (non-ACPI) mode\n"));
+ } else {
+ /* Transition to LEGACY mode */
+
+ status = acpi_hw_set_mode(ACPI_SYS_MODE_LEGACY);
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO,
+ "Could not exit ACPI mode to legacy mode"));
+ return_ACPI_STATUS(status);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INIT, "ACPI mode disabled\n"));
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_disable)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_enable_event
+ *
+ * PARAMETERS: event - The fixed eventto be enabled
+ * flags - Reserved
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable an ACPI event (fixed)
+ *
+ ******************************************************************************/
+acpi_status acpi_enable_event(u32 event, u32 flags)
+{
+ acpi_status status = AE_OK;
+ u32 value;
+
+ ACPI_FUNCTION_TRACE(acpi_enable_event);
+
+ /* If Hardware Reduced flag is set, there are no fixed events */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Decode the Fixed Event */
+
+ if (event > ACPI_EVENT_MAX) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Enable the requested fixed event (by writing a one to the enable
+ * register bit)
+ */
+ status =
+ acpi_write_bit_register(acpi_gbl_fixed_event_info[event].
+ enable_register_id, ACPI_ENABLE_EVENT);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Make sure that the hardware responded */
+
+ status =
+ acpi_read_bit_register(acpi_gbl_fixed_event_info[event].
+ enable_register_id, &value);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (value != 1) {
+ ACPI_ERROR((AE_INFO,
+ "Could not enable %s event",
+ acpi_ut_get_event_name(event)));
+ return_ACPI_STATUS(AE_NO_HARDWARE_RESPONSE);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enable_event)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_disable_event
+ *
+ * PARAMETERS: event - The fixed event to be disabled
+ * flags - Reserved
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Disable an ACPI event (fixed)
+ *
+ ******************************************************************************/
+acpi_status acpi_disable_event(u32 event, u32 flags)
+{
+ acpi_status status = AE_OK;
+ u32 value;
+
+ ACPI_FUNCTION_TRACE(acpi_disable_event);
+
+ /* If Hardware Reduced flag is set, there are no fixed events */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Decode the Fixed Event */
+
+ if (event > ACPI_EVENT_MAX) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Disable the requested fixed event (by writing a zero to the enable
+ * register bit)
+ */
+ status =
+ acpi_write_bit_register(acpi_gbl_fixed_event_info[event].
+ enable_register_id, ACPI_DISABLE_EVENT);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status =
+ acpi_read_bit_register(acpi_gbl_fixed_event_info[event].
+ enable_register_id, &value);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (value != 0) {
+ ACPI_ERROR((AE_INFO,
+ "Could not disable %s events",
+ acpi_ut_get_event_name(event)));
+ return_ACPI_STATUS(AE_NO_HARDWARE_RESPONSE);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_disable_event)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_clear_event
+ *
+ * PARAMETERS: event - The fixed event to be cleared
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Clear an ACPI event (fixed)
+ *
+ ******************************************************************************/
+acpi_status acpi_clear_event(u32 event)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(acpi_clear_event);
+
+ /* If Hardware Reduced flag is set, there are no fixed events */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Decode the Fixed Event */
+
+ if (event > ACPI_EVENT_MAX) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Clear the requested fixed event (By writing a one to the status
+ * register bit)
+ */
+ status =
+ acpi_write_bit_register(acpi_gbl_fixed_event_info[event].
+ status_register_id, ACPI_CLEAR_STATUS);
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_clear_event)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_event_status
+ *
+ * PARAMETERS: event - The fixed event
+ * event_status - Where the current status of the event will
+ * be returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Obtains and returns the current status of the event
+ *
+ ******************************************************************************/
+acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status)
+{
+ acpi_status status;
+ acpi_event_status local_event_status = 0;
+ u32 in_byte;
+
+ ACPI_FUNCTION_TRACE(acpi_get_event_status);
+
+ if (!event_status) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Decode the Fixed Event */
+
+ if (event > ACPI_EVENT_MAX) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Fixed event currently can be dispatched? */
+
+ if (acpi_gbl_fixed_event_handlers[event].handler) {
+ local_event_status |= ACPI_EVENT_FLAG_HAS_HANDLER;
+ }
+
+ /* Fixed event currently enabled? */
+
+ status =
+ acpi_read_bit_register(acpi_gbl_fixed_event_info[event].
+ enable_register_id, &in_byte);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (in_byte) {
+ local_event_status |=
+ (ACPI_EVENT_FLAG_ENABLED | ACPI_EVENT_FLAG_ENABLE_SET);
+ }
+
+ /* Fixed event currently active? */
+
+ status =
+ acpi_read_bit_register(acpi_gbl_fixed_event_info[event].
+ status_register_id, &in_byte);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (in_byte) {
+ local_event_status |= ACPI_EVENT_FLAG_STATUS_SET;
+ }
+
+ (*event_status) = local_event_status;
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_event_status)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c
new file mode 100644
index 000000000..340947e41
--- /dev/null
+++ b/drivers/acpi/acpica/evxfgpe.c
@@ -0,0 +1,1049 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evxfgpe - External Interfaces for General Purpose Events (GPEs)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evxfgpe")
+
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_update_all_gpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Complete GPE initialization and enable all GPEs that have
+ * associated _Lxx or _Exx methods and are not pointed to by any
+ * device _PRW methods (this indicates that these GPEs are
+ * generally intended for system or device wakeup. Such GPEs
+ * have to be enabled directly when the devices whose _PRW
+ * methods point to them are set up for wakeup signaling.)
+ *
+ * NOTE: Should be called after any GPEs are added to the system. Primarily,
+ * after the system _PRW methods have been run, but also after a GPE Block
+ * Device has been added or if any new GPE methods have been added via a
+ * dynamic table load.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_update_all_gpes(void)
+{
+ acpi_status status;
+ u8 is_polling_needed = FALSE;
+
+ ACPI_FUNCTION_TRACE(acpi_update_all_gpes);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (acpi_gbl_all_gpes_initialized) {
+ goto unlock_and_exit;
+ }
+
+ status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block,
+ &is_polling_needed);
+ if (ACPI_SUCCESS(status)) {
+ acpi_gbl_all_gpes_initialized = TRUE;
+ }
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+ if (is_polling_needed && acpi_gbl_all_gpes_initialized) {
+
+ /* Poll GPEs to handle already triggered events */
+
+ acpi_ev_gpe_detect(acpi_gbl_gpe_xrupt_list_head);
+ }
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_update_all_gpes)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_enable_gpe
+ *
+ * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is
+ * hardware-enabled.
+ *
+ ******************************************************************************/
+acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number)
+{
+ acpi_status status = AE_BAD_PARAMETER;
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_enable_gpe);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /*
+ * Ensure that we have a valid GPE number and that there is some way
+ * of handling the GPE (handler or a GPE method). In other words, we
+ * won't allow a valid GPE to be enabled if there is no way to handle it.
+ */
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (gpe_event_info) {
+ if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) !=
+ ACPI_GPE_DISPATCH_NONE) {
+ status = acpi_ev_add_gpe_reference(gpe_event_info, TRUE);
+ if (ACPI_SUCCESS(status) &&
+ ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) {
+
+ /* Poll edge-triggered GPEs to handle existing events */
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ (void)acpi_ev_detect_gpe(gpe_device,
+ gpe_event_info,
+ gpe_number);
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ }
+ } else {
+ status = AE_NO_HANDLER;
+ }
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+ACPI_EXPORT_SYMBOL(acpi_enable_gpe)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_disable_gpe
+ *
+ * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove a reference to a GPE. When the last reference is
+ * removed, only then is the GPE disabled (for runtime GPEs), or
+ * the GPE mask bit disabled (for wake GPEs)
+ *
+ ******************************************************************************/
+
+acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number)
+{
+ acpi_status status = AE_BAD_PARAMETER;
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_disable_gpe);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (gpe_event_info) {
+ status = acpi_ev_remove_gpe_reference(gpe_event_info) ;
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_disable_gpe)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_set_gpe
+ *
+ * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ * action - ACPI_GPE_ENABLE or ACPI_GPE_DISABLE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable or disable an individual GPE. This function bypasses
+ * the reference count mechanism used in the acpi_enable_gpe(),
+ * acpi_disable_gpe() interfaces.
+ * This API is typically used by the GPE raw handler mode driver
+ * to switch between the polling mode and the interrupt mode after
+ * the driver has enabled the GPE.
+ * The APIs should be invoked in this order:
+ * acpi_enable_gpe() <- Ensure the reference count > 0
+ * acpi_set_gpe(ACPI_GPE_DISABLE) <- Enter polling mode
+ * acpi_set_gpe(ACPI_GPE_ENABLE) <- Leave polling mode
+ * acpi_disable_gpe() <- Decrease the reference count
+ *
+ * Note: If a GPE is shared by 2 silicon components, then both the drivers
+ * should support GPE polling mode or disabling the GPE for long period
+ * for one driver may break the other. So use it with care since all
+ * firmware _Lxx/_Exx handlers currently rely on the GPE interrupt mode.
+ *
+ ******************************************************************************/
+acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action)
+{
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_set_gpe);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /* Perform the action */
+
+ switch (action) {
+ case ACPI_GPE_ENABLE:
+
+ status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE);
+ gpe_event_info->disable_for_dispatch = FALSE;
+ break;
+
+ case ACPI_GPE_DISABLE:
+
+ status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
+ gpe_event_info->disable_for_dispatch = TRUE;
+ break;
+
+ default:
+
+ status = AE_BAD_PARAMETER;
+ break;
+ }
+
+unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_set_gpe)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_mask_gpe
+ *
+ * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ * is_masked - Whether the GPE is masked or not
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Unconditionally mask/unmask the an individual GPE, ex., to
+ * prevent a GPE flooding.
+ *
+ ******************************************************************************/
+acpi_status acpi_mask_gpe(acpi_handle gpe_device, u32 gpe_number, u8 is_masked)
+{
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_mask_gpe);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ status = acpi_ev_mask_gpe(gpe_event_info, is_masked);
+
+unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_mask_gpe)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_mark_gpe_for_wake
+ *
+ * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Mark a GPE as having the ability to wake the system. Simply
+ * sets the ACPI_GPE_CAN_WAKE flag.
+ *
+ * Some potential callers of acpi_setup_gpe_for_wake may know in advance that
+ * there won't be any notify handlers installed for device wake notifications
+ * from the given GPE (one example is a button GPE in Linux). For these cases,
+ * acpi_mark_gpe_for_wake should be used instead of acpi_setup_gpe_for_wake.
+ * This will set the ACPI_GPE_CAN_WAKE flag for the GPE without trying to
+ * setup implicit wake notification for it (since there's no handler method).
+ *
+ ******************************************************************************/
+acpi_status acpi_mark_gpe_for_wake(acpi_handle gpe_device, u32 gpe_number)
+{
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_status status = AE_BAD_PARAMETER;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_mark_gpe_for_wake);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (gpe_event_info) {
+
+ /* Mark the GPE as a possible wake event */
+
+ gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
+ status = AE_OK;
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_mark_gpe_for_wake)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_setup_gpe_for_wake
+ *
+ * PARAMETERS: wake_device - Device associated with the GPE (via _PRW)
+ * gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Mark a GPE as having the ability to wake the system. This
+ * interface is intended to be used as the host executes the
+ * _PRW methods (Power Resources for Wake) in the system tables.
+ * Each _PRW appears under a Device Object (The wake_device), and
+ * contains the info for the wake GPE associated with the
+ * wake_device.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_setup_gpe_for_wake(acpi_handle wake_device,
+ acpi_handle gpe_device, u32 gpe_number)
+{
+ acpi_status status;
+ struct acpi_gpe_event_info *gpe_event_info;
+ struct acpi_namespace_node *device_node;
+ struct acpi_gpe_notify_info *notify;
+ struct acpi_gpe_notify_info *new_notify;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake);
+
+ /* Parameter Validation */
+
+ if (!wake_device) {
+ /*
+ * By forcing wake_device to be valid, we automatically enable the
+ * implicit notify feature on all hosts.
+ */
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Handle root object case */
+
+ if (wake_device == ACPI_ROOT_OBJECT) {
+ device_node = acpi_gbl_root_node;
+ } else {
+ device_node =
+ ACPI_CAST_PTR(struct acpi_namespace_node, wake_device);
+ }
+
+ /* Validate wake_device is of type Device */
+
+ if (device_node->type != ACPI_TYPE_DEVICE) {
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Allocate a new notify object up front, in case it is needed.
+ * Memory allocation while holding a spinlock is a big no-no
+ * on some hosts.
+ */
+ new_notify = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_notify_info));
+ if (!new_notify) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /*
+ * If there is no method or handler for this GPE, then the
+ * wake_device will be notified whenever this GPE fires. This is
+ * known as an "implicit notify". Note: The GPE is assumed to be
+ * level-triggered (for windows compatibility).
+ */
+ if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_NONE) {
+ /*
+ * This is the first device for implicit notify on this GPE.
+ * Just set the flags here, and enter the NOTIFY block below.
+ */
+ gpe_event_info->flags =
+ (ACPI_GPE_DISPATCH_NOTIFY | ACPI_GPE_LEVEL_TRIGGERED);
+ } else if (gpe_event_info->flags & ACPI_GPE_AUTO_ENABLED) {
+ /*
+ * A reference to this GPE has been added during the GPE block
+ * initialization, so drop it now to prevent the GPE from being
+ * permanently enabled and clear its ACPI_GPE_AUTO_ENABLED flag.
+ */
+ (void)acpi_ev_remove_gpe_reference(gpe_event_info);
+ gpe_event_info->flags &= ~ACPI_GPE_AUTO_ENABLED;
+ }
+
+ /*
+ * If we already have an implicit notify on this GPE, add
+ * this device to the notify list.
+ */
+ if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_NOTIFY) {
+
+ /* Ensure that the device is not already in the list */
+
+ notify = gpe_event_info->dispatch.notify_list;
+ while (notify) {
+ if (notify->device_node == device_node) {
+ status = AE_ALREADY_EXISTS;
+ goto unlock_and_exit;
+ }
+ notify = notify->next;
+ }
+
+ /* Add this device to the notify list for this GPE */
+
+ new_notify->device_node = device_node;
+ new_notify->next = gpe_event_info->dispatch.notify_list;
+ gpe_event_info->dispatch.notify_list = new_notify;
+ new_notify = NULL;
+ }
+
+ /* Mark the GPE as a possible wake event */
+
+ gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
+ status = AE_OK;
+
+unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+
+ /* Delete the notify object if it was not used above */
+
+ if (new_notify) {
+ ACPI_FREE(new_notify);
+ }
+ return_ACPI_STATUS(status);
+}
+ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_set_gpe_wake_mask
+ *
+ * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ * action - Enable or Disable
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Set or clear the GPE's wakeup enable mask bit. The GPE must
+ * already be marked as a WAKE GPE.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 action)
+{
+ acpi_status status = AE_OK;
+ struct acpi_gpe_event_info *gpe_event_info;
+ struct acpi_gpe_register_info *gpe_register_info;
+ acpi_cpu_flags flags;
+ u32 register_bit;
+
+ ACPI_FUNCTION_TRACE(acpi_set_gpe_wake_mask);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /*
+ * Ensure that we have a valid GPE number and that this GPE is in
+ * fact a wake GPE
+ */
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) {
+ status = AE_TYPE;
+ goto unlock_and_exit;
+ }
+
+ gpe_register_info = gpe_event_info->register_info;
+ if (!gpe_register_info) {
+ status = AE_NOT_EXIST;
+ goto unlock_and_exit;
+ }
+
+ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
+
+ /* Perform the action */
+
+ switch (action) {
+ case ACPI_GPE_ENABLE:
+
+ ACPI_SET_BIT(gpe_register_info->enable_for_wake,
+ (u8)register_bit);
+ break;
+
+ case ACPI_GPE_DISABLE:
+
+ ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake,
+ (u8)register_bit);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "%u, Invalid action", action));
+ status = AE_BAD_PARAMETER;
+ break;
+ }
+
+unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_set_gpe_wake_mask)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_clear_gpe
+ *
+ * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Clear an ACPI event (general purpose)
+ *
+ ******************************************************************************/
+acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number)
+{
+ acpi_status status = AE_OK;
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_clear_gpe);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ status = acpi_hw_clear_gpe(gpe_event_info);
+
+ unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_clear_gpe)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_gpe_status
+ *
+ * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ * event_status - Where the current status of the event
+ * will be returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get the current status of a GPE (signalled/not_signalled)
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_gpe_status(acpi_handle gpe_device,
+ u32 gpe_number, acpi_event_status *event_status)
+{
+ acpi_status status = AE_OK;
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_get_gpe_status);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /* Obtain status on the requested GPE number */
+
+ status = acpi_hw_get_gpe_status(gpe_event_info, event_status);
+
+unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_gpe_status)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_gispatch_gpe
+ *
+ * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ *
+ * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
+ *
+ * DESCRIPTION: Detect and dispatch a General Purpose Event to either a function
+ * (e.g. EC) or method (e.g. _Lxx/_Exx) handler.
+ *
+ ******************************************************************************/
+u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number)
+{
+ ACPI_FUNCTION_TRACE(acpi_dispatch_gpe);
+
+ return acpi_ev_detect_gpe(gpe_device, NULL, gpe_number);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_dispatch_gpe)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_finish_gpe
+ *
+ * PARAMETERS: gpe_device - Namespace node for the GPE Block
+ * (NULL for FADT defined GPEs)
+ * gpe_number - GPE level within the GPE block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Clear and conditionally re-enable a GPE. This completes the GPE
+ * processing. Intended for use by asynchronous host-installed
+ * GPE handlers. The GPE is only re-enabled if the enable_for_run bit
+ * is set in the GPE info.
+ *
+ ******************************************************************************/
+acpi_status acpi_finish_gpe(acpi_handle gpe_device, u32 gpe_number)
+{
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_finish_gpe);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ status = acpi_ev_finish_gpe(gpe_event_info);
+
+unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_finish_gpe)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_disable_all_gpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Disable and clear all GPEs in all GPE blocks
+ *
+ ******************************************************************************/
+
+acpi_status acpi_disable_all_gpes(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_disable_all_gpes);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_hw_disable_all_gpes();
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_disable_all_gpes)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_enable_all_runtime_gpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable all "runtime" GPEs, in all GPE blocks
+ *
+ ******************************************************************************/
+
+acpi_status acpi_enable_all_runtime_gpes(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_enable_all_runtime_gpes);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_hw_enable_all_runtime_gpes();
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enable_all_runtime_gpes)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_enable_all_wakeup_gpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable all "wakeup" GPEs and disable all of the other GPEs, in
+ * all GPE blocks.
+ *
+ ******************************************************************************/
+acpi_status acpi_enable_all_wakeup_gpes(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_enable_all_wakeup_gpes);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_hw_enable_all_wakeup_gpes();
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enable_all_wakeup_gpes)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_any_gpe_status_set
+ *
+ * PARAMETERS: gpe_skip_number - Number of the GPE to skip
+ *
+ * RETURN: Whether or not the status bit is set for any GPE
+ *
+ * DESCRIPTION: Check the status bits of all enabled GPEs, except for the one
+ * represented by the "skip" argument, and return TRUE if any of
+ * them is set or FALSE otherwise.
+ *
+ ******************************************************************************/
+u32 acpi_any_gpe_status_set(u32 gpe_skip_number)
+{
+ acpi_status status;
+ acpi_handle gpe_device;
+ u8 ret;
+
+ ACPI_FUNCTION_TRACE(acpi_any_gpe_status_set);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+
+ status = acpi_get_gpe_device(gpe_skip_number, &gpe_device);
+ if (ACPI_FAILURE(status)) {
+ gpe_device = NULL;
+ }
+
+ ret = acpi_hw_check_all_gpes(gpe_device, gpe_skip_number);
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+ return (ret);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_any_gpe_status_set)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_gpe_block
+ *
+ * PARAMETERS: gpe_device - Handle to the parent GPE Block Device
+ * gpe_block_address - Address and space_ID
+ * register_count - Number of GPE register pairs in the block
+ * interrupt_number - H/W interrupt for the block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create and Install a block of GPE registers. The GPEs are not
+ * enabled here.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_install_gpe_block(acpi_handle gpe_device,
+ struct acpi_generic_address *gpe_block_address,
+ u32 register_count, u32 interrupt_number)
+{
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *node;
+ struct acpi_gpe_block_info *gpe_block;
+
+ ACPI_FUNCTION_TRACE(acpi_install_gpe_block);
+
+ if ((!gpe_device) || (!gpe_block_address) || (!register_count)) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ node = acpi_ns_validate_handle(gpe_device);
+ if (!node) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /* Validate the parent device */
+
+ if (node->type != ACPI_TYPE_DEVICE) {
+ status = AE_TYPE;
+ goto unlock_and_exit;
+ }
+
+ if (node->object) {
+ status = AE_ALREADY_EXISTS;
+ goto unlock_and_exit;
+ }
+
+ /*
+ * For user-installed GPE Block Devices, the gpe_block_base_number
+ * is always zero
+ */
+ status = acpi_ev_create_gpe_block(node, gpe_block_address->address,
+ gpe_block_address->space_id,
+ register_count, 0, interrupt_number,
+ &gpe_block);
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+
+ /* Install block in the device_object attached to the node */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+
+ /*
+ * No object, create a new one (Device nodes do not always have
+ * an attached object)
+ */
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_DEVICE);
+ if (!obj_desc) {
+ status = AE_NO_MEMORY;
+ goto unlock_and_exit;
+ }
+
+ status =
+ acpi_ns_attach_object(node, obj_desc, ACPI_TYPE_DEVICE);
+
+ /* Remove local reference to the object */
+
+ acpi_ut_remove_reference(obj_desc);
+
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+ }
+
+ /* Now install the GPE block in the device_object */
+
+ obj_desc->device.gpe_block = gpe_block;
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_gpe_block)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_remove_gpe_block
+ *
+ * PARAMETERS: gpe_device - Handle to the parent GPE Block Device
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove a previously installed block of GPE registers
+ *
+ ******************************************************************************/
+acpi_status acpi_remove_gpe_block(acpi_handle gpe_device)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ ACPI_FUNCTION_TRACE(acpi_remove_gpe_block);
+
+ if (!gpe_device) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ node = acpi_ns_validate_handle(gpe_device);
+ if (!node) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /* Validate the parent device */
+
+ if (node->type != ACPI_TYPE_DEVICE) {
+ status = AE_TYPE;
+ goto unlock_and_exit;
+ }
+
+ /* Get the device_object attached to the node */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc || !obj_desc->device.gpe_block) {
+ return_ACPI_STATUS(AE_NULL_OBJECT);
+ }
+
+ /* Delete the GPE block (but not the device_object) */
+
+ status = acpi_ev_delete_gpe_block(obj_desc->device.gpe_block);
+ if (ACPI_SUCCESS(status)) {
+ obj_desc->device.gpe_block = NULL;
+ }
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_gpe_block)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_gpe_device
+ *
+ * PARAMETERS: index - System GPE index (0-current_gpe_count)
+ * gpe_device - Where the parent GPE Device is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Obtain the GPE device associated with the input index. A NULL
+ * gpe device indicates that the gpe number is contained in one of
+ * the FADT-defined gpe blocks. Otherwise, the GPE block device.
+ *
+ ******************************************************************************/
+acpi_status acpi_get_gpe_device(u32 index, acpi_handle *gpe_device)
+{
+ struct acpi_gpe_device_info info;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_get_gpe_device);
+
+ if (!gpe_device) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if (index >= acpi_current_gpe_count) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /* Setup and walk the GPE list */
+
+ info.index = index;
+ info.status = AE_NOT_EXIST;
+ info.gpe_device = NULL;
+ info.next_block_base_index = 0;
+
+ status = acpi_ev_walk_gpe_list(acpi_ev_get_gpe_device, &info);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ *gpe_device = ACPI_CAST_PTR(acpi_handle, info.gpe_device);
+ return_ACPI_STATUS(info.status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_gpe_device)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
new file mode 100644
index 000000000..0a8372bf6
--- /dev/null
+++ b/drivers/acpi/acpica/evxfregn.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: evxfregn - External Interfaces, ACPI Operation Regions and
+ * Address Spaces.
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_EVENTS
+ACPI_MODULE_NAME("evxfregn")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_address_space_handler
+ *
+ * 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
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for all op_regions of a given space_id.
+ *
+ * NOTE: This function should only be called after acpi_enable_subsystem has
+ * been called. This is because any _REG methods associated with the Space ID
+ * 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.)
+ *
+ ******************************************************************************/
+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)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_address_space_handler);
+
+ /* 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) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /* Install the handler for all Regions for this Space ID */
+
+ status =
+ acpi_ev_install_space_handler(node, space_id, handler, setup,
+ context);
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+
+ /* Run all _REG methods for this address space */
+
+ acpi_ev_execute_reg_methods(node, space_id, ACPI_REG_CONNECT);
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_address_space_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_remove_address_space_handler
+ *
+ * PARAMETERS: device - Handle for the device
+ * space_id - The address space ID
+ * handler - Address of the handler
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove a previously installed handler.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_remove_address_space_handler(acpi_handle device,
+ acpi_adr_space_type space_id,
+ acpi_adr_space_handler handler)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *handler_obj;
+ union acpi_operand_object *region_obj;
+ union acpi_operand_object **last_obj_ptr;
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_remove_address_space_handler);
+
+ /* 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 ||
+ ((node->type != ACPI_TYPE_DEVICE) &&
+ (node->type != ACPI_TYPE_PROCESSOR) &&
+ (node->type != ACPI_TYPE_THERMAL) &&
+ (node != acpi_gbl_root_node))) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /* Make sure the internal object exists */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ status = AE_NOT_EXIST;
+ goto unlock_and_exit;
+ }
+
+ /* Find the address handler the user requested */
+
+ handler_obj = obj_desc->common_notify.handler;
+ last_obj_ptr = &obj_desc->common_notify.handler;
+ while (handler_obj) {
+
+ /* We have a handler, see if user requested this one */
+
+ if (handler_obj->address_space.space_id == space_id) {
+
+ /* Handler must be the same as the installed handler */
+
+ if (handler_obj->address_space.handler != handler) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /* Matched space_id, first dereference this in the Regions */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Removing address handler %p(%p) for region %s "
+ "on Device %p(%p)\n",
+ handler_obj, handler,
+ acpi_ut_get_region_name(space_id),
+ node, obj_desc));
+
+ region_obj = handler_obj->address_space.region_list;
+
+ /* Walk the handler's region list */
+
+ while (region_obj) {
+ /*
+ * First disassociate the handler from the region.
+ *
+ * NOTE: this doesn't mean that the region goes away
+ * The region is just inaccessible as indicated to
+ * the _REG method
+ */
+ acpi_ev_detach_region(region_obj, TRUE);
+
+ /*
+ * Walk the list: Just grab the head because the
+ * detach_region removed the previous head.
+ */
+ region_obj =
+ handler_obj->address_space.region_list;
+ }
+
+ /* Remove this Handler object from the list */
+
+ *last_obj_ptr = handler_obj->address_space.next;
+
+ /* 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;
+ }
+
+ /* Walk the linked list of handlers */
+
+ last_obj_ptr = &handler_obj->address_space.next;
+ handler_obj = handler_obj->address_space.next;
+ }
+
+ /* The handler does not exist */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Unable to remove address handler %p for %s(%X), DevNode %p, obj %p\n",
+ handler, acpi_ut_get_region_name(space_id), space_id,
+ node, obj_desc));
+
+ status = AE_NOT_EXIST;
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_address_space_handler)
diff --git a/drivers/acpi/acpica/exconcat.c b/drivers/acpi/acpica/exconcat.c
new file mode 100644
index 000000000..66201742f
--- /dev/null
+++ b/drivers/acpi/acpica/exconcat.c
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exconcat - Concatenate-type AML operators
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlresrc.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exconcat")
+
+/* Local Prototypes */
+static acpi_status
+acpi_ex_convert_to_object_type_string(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **result_desc);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_do_concatenate
+ *
+ * PARAMETERS: operand0 - First source object
+ * operand1 - Second source object
+ * actual_return_desc - Where to place the return object
+ * walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Concatenate two objects with the ACPI-defined conversion
+ * rules as necessary.
+ * NOTE:
+ * Per the ACPI spec (up to 6.1), Concatenate only supports Integer,
+ * String, and Buffer objects. However, we support all objects here
+ * as an extension. This improves the usefulness of both Concatenate
+ * and the Printf/Fprintf macros. The extension returns a string
+ * describing the object type for the other objects.
+ * 02/2016.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_do_concatenate(union acpi_operand_object *operand0,
+ union acpi_operand_object *operand1,
+ union acpi_operand_object **actual_return_desc,
+ struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object *local_operand0 = operand0;
+ union acpi_operand_object *local_operand1 = operand1;
+ union acpi_operand_object *temp_operand1 = NULL;
+ union acpi_operand_object *return_desc;
+ char *buffer;
+ acpi_object_type operand0_type;
+ acpi_object_type operand1_type;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ex_do_concatenate);
+
+ /* Operand 0 preprocessing */
+
+ switch (operand0->common.type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ operand0_type = operand0->common.type;
+ break;
+
+ default:
+
+ /* For all other types, get the "object type" string */
+
+ status =
+ acpi_ex_convert_to_object_type_string(operand0,
+ &local_operand0);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ operand0_type = ACPI_TYPE_STRING;
+ break;
+ }
+
+ /* Operand 1 preprocessing */
+
+ switch (operand1->common.type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ operand1_type = operand1->common.type;
+ break;
+
+ default:
+
+ /* For all other types, get the "object type" string */
+
+ status =
+ acpi_ex_convert_to_object_type_string(operand1,
+ &local_operand1);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ operand1_type = ACPI_TYPE_STRING;
+ break;
+ }
+
+ /*
+ * Convert the second operand if necessary. The first operand (0)
+ * determines the type of the second operand (1) (See the Data Types
+ * section of the ACPI specification). Both object types are
+ * guaranteed to be either Integer/String/Buffer by the operand
+ * resolution mechanism.
+ */
+ switch (operand0_type) {
+ case ACPI_TYPE_INTEGER:
+
+ status =
+ acpi_ex_convert_to_integer(local_operand1, &temp_operand1,
+ ACPI_IMPLICIT_CONVERSION);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ status =
+ acpi_ex_convert_to_buffer(local_operand1, &temp_operand1);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ switch (operand1_type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ /* Other types have already been converted to string */
+
+ status =
+ acpi_ex_convert_to_string(local_operand1,
+ &temp_operand1,
+ ACPI_IMPLICIT_CONVERT_HEX);
+ break;
+
+ default:
+
+ status = AE_OK;
+ break;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X",
+ operand0->common.type));
+ status = AE_AML_INTERNAL;
+ }
+
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /* Take care with any newly created operand objects */
+
+ if ((local_operand1 != operand1) && (local_operand1 != temp_operand1)) {
+ acpi_ut_remove_reference(local_operand1);
+ }
+
+ local_operand1 = temp_operand1;
+
+ /*
+ * Both operands are now known to be the same object type
+ * (Both are Integer, String, or Buffer), and we can now perform
+ * the concatenation.
+ *
+ * There are three cases to handle, as per the ACPI spec:
+ *
+ * 1) Two Integers concatenated to produce a new Buffer
+ * 2) Two Strings concatenated to produce a new String
+ * 3) Two Buffers concatenated to produce a new Buffer
+ */
+ switch (operand0_type) {
+ case ACPI_TYPE_INTEGER:
+
+ /* Result of two Integers is a Buffer */
+ /* Need enough buffer space for two integers */
+
+ return_desc = acpi_ut_create_buffer_object((acpi_size)
+ ACPI_MUL_2
+ (acpi_gbl_integer_byte_width));
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ buffer = (char *)return_desc->buffer.pointer;
+
+ /* Copy the first integer, LSB first */
+
+ memcpy(buffer, &operand0->integer.value,
+ acpi_gbl_integer_byte_width);
+
+ /* Copy the second integer (LSB first) after the first */
+
+ memcpy(buffer + acpi_gbl_integer_byte_width,
+ &local_operand1->integer.value,
+ acpi_gbl_integer_byte_width);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ /* Result of two Strings is a String */
+
+ return_desc = acpi_ut_create_string_object(((acpi_size)
+ local_operand0->
+ string.length +
+ local_operand1->
+ string.length));
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ buffer = return_desc->string.pointer;
+
+ /* Concatenate the strings */
+
+ strcpy(buffer, local_operand0->string.pointer);
+ strcat(buffer, local_operand1->string.pointer);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ /* Result of two Buffers is a Buffer */
+
+ return_desc = acpi_ut_create_buffer_object(((acpi_size)
+ operand0->buffer.
+ length +
+ local_operand1->
+ buffer.length));
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ buffer = (char *)return_desc->buffer.pointer;
+
+ /* Concatenate the buffers */
+
+ memcpy(buffer, operand0->buffer.pointer,
+ operand0->buffer.length);
+ memcpy(buffer + operand0->buffer.length,
+ local_operand1->buffer.pointer,
+ local_operand1->buffer.length);
+ break;
+
+ default:
+
+ /* Invalid object type, should not happen here */
+
+ ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X",
+ operand0->common.type));
+ status = AE_AML_INTERNAL;
+ goto cleanup;
+ }
+
+ *actual_return_desc = return_desc;
+
+cleanup:
+ if (local_operand0 != operand0) {
+ acpi_ut_remove_reference(local_operand0);
+ }
+
+ if (local_operand1 != operand1) {
+ acpi_ut_remove_reference(local_operand1);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_convert_to_object_type_string
+ *
+ * PARAMETERS: obj_desc - Object to be converted
+ * return_desc - Where to place the return object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert an object of arbitrary type to a string object that
+ * contains the namestring for the object. Used for the
+ * concatenate operator.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_convert_to_object_type_string(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **result_desc)
+{
+ union acpi_operand_object *return_desc;
+ const char *type_string;
+
+ type_string = acpi_ut_get_type_name(obj_desc->common.type);
+
+ return_desc = acpi_ut_create_string_object(((acpi_size)strlen(type_string) + 9)); /* 9 For "[ Object]" */
+ if (!return_desc) {
+ return (AE_NO_MEMORY);
+ }
+
+ strcpy(return_desc->string.pointer, "[");
+ strcat(return_desc->string.pointer, type_string);
+ strcat(return_desc->string.pointer, " Object]");
+
+ *result_desc = return_desc;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_concat_template
+ *
+ * PARAMETERS: operand0 - First source object
+ * operand1 - Second source object
+ * actual_return_desc - Where to place the return object
+ * walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Concatenate two resource templates
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_concat_template(union acpi_operand_object *operand0,
+ union acpi_operand_object *operand1,
+ union acpi_operand_object **actual_return_desc,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ union acpi_operand_object *return_desc;
+ u8 *new_buf;
+ u8 *end_tag;
+ acpi_size length0;
+ acpi_size length1;
+ acpi_size new_length;
+
+ ACPI_FUNCTION_TRACE(ex_concat_template);
+
+ /*
+ * Find the end_tag descriptor in each resource template.
+ * Note1: returned pointers point TO the end_tag, not past it.
+ * Note2: zero-length buffers are allowed; treated like one end_tag
+ */
+
+ /* Get the length of the first resource template */
+
+ status = acpi_ut_get_resource_end_tag(operand0, &end_tag);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ length0 = ACPI_PTR_DIFF(end_tag, operand0->buffer.pointer);
+
+ /* Get the length of the second resource template */
+
+ status = acpi_ut_get_resource_end_tag(operand1, &end_tag);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ length1 = ACPI_PTR_DIFF(end_tag, operand1->buffer.pointer);
+
+ /* Combine both lengths, minimum size will be 2 for end_tag */
+
+ new_length = length0 + length1 + sizeof(struct aml_resource_end_tag);
+
+ /* Create a new buffer object for the result (with one end_tag) */
+
+ return_desc = acpi_ut_create_buffer_object(new_length);
+ if (!return_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /*
+ * Copy the templates to the new buffer, 0 first, then 1 follows. One
+ * end_tag descriptor is copied from Operand1.
+ */
+ new_buf = return_desc->buffer.pointer;
+ memcpy(new_buf, operand0->buffer.pointer, length0);
+ memcpy(new_buf + length0, operand1->buffer.pointer, length1);
+
+ /* Insert end_tag and set the checksum to zero, means "ignore checksum" */
+
+ new_buf[new_length - 1] = 0;
+ new_buf[new_length - 2] = ACPI_RESOURCE_NAME_END_TAG | 1;
+
+ /* Return the completed resource template */
+
+ *actual_return_desc = return_desc;
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c
new file mode 100644
index 000000000..d7d74ef87
--- /dev/null
+++ b/drivers/acpi/acpica/exconfig.c
@@ -0,0 +1,551 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exconfig - Namespace reconfiguration (Load/Unload opcodes)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+#include "actables.h"
+#include "acdispat.h"
+#include "acevents.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exconfig")
+
+/* Local prototypes */
+static acpi_status
+acpi_ex_add_table(u32 table_index, union acpi_operand_object **ddb_handle);
+
+static acpi_status
+acpi_ex_region_read(union acpi_operand_object *obj_desc,
+ u32 length, u8 *buffer);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_add_table
+ *
+ * PARAMETERS: table - Pointer to raw table
+ * parent_node - Where to load the table (scope)
+ * ddb_handle - Where to return the table handle.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Common function to Install and Load an ACPI table with a
+ * returned table handle.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_add_table(u32 table_index, union acpi_operand_object **ddb_handle)
+{
+ union acpi_operand_object *obj_desc;
+
+ ACPI_FUNCTION_TRACE(ex_add_table);
+
+ /* Create an object to be the table handle */
+
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Init the table handle */
+
+ obj_desc->common.flags |= AOPOBJ_DATA_VALID;
+ obj_desc->reference.class = ACPI_REFCLASS_TABLE;
+ obj_desc->reference.value = table_index;
+ *ddb_handle = obj_desc;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_load_table_op
+ *
+ * PARAMETERS: walk_state - Current state with operands
+ * return_desc - Where to store the return object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load an ACPI table from the RSDT/XSDT
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
+ union acpi_operand_object **return_desc)
+{
+ acpi_status status;
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ struct acpi_namespace_node *parent_node;
+ struct acpi_namespace_node *start_node;
+ struct acpi_namespace_node *parameter_node = NULL;
+ union acpi_operand_object *return_obj;
+ union acpi_operand_object *ddb_handle;
+ u32 table_index;
+
+ ACPI_FUNCTION_TRACE(ex_load_table_op);
+
+ /* Create the return object */
+
+ return_obj = acpi_ut_create_integer_object((u64)0);
+ if (!return_obj) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ *return_desc = return_obj;
+
+ /* Find the ACPI table in the RSDT/XSDT */
+
+ acpi_ex_exit_interpreter();
+ status = acpi_tb_find_table(operand[0]->string.pointer,
+ operand[1]->string.pointer,
+ operand[2]->string.pointer, &table_index);
+ acpi_ex_enter_interpreter();
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Table not found, return an Integer=0 and AE_OK */
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Default nodes */
+
+ start_node = walk_state->scope_info->scope.node;
+ parent_node = acpi_gbl_root_node;
+
+ /* root_path (optional parameter) */
+
+ if (operand[3]->string.length > 0) {
+ /*
+ * Find the node referenced by the root_path_string. This is the
+ * location within the namespace where the table will be loaded.
+ */
+ status = acpi_ns_get_node_unlocked(start_node,
+ operand[3]->string.pointer,
+ ACPI_NS_SEARCH_PARENT,
+ &parent_node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* parameter_path (optional parameter) */
+
+ if (operand[4]->string.length > 0) {
+ if ((operand[4]->string.pointer[0] != AML_ROOT_PREFIX) &&
+ (operand[4]->string.pointer[0] != AML_PARENT_PREFIX)) {
+ /*
+ * Path is not absolute, so it will be relative to the node
+ * referenced by the root_path_string (or the NS root if omitted)
+ */
+ start_node = parent_node;
+ }
+
+ /* Find the node referenced by the parameter_path_string */
+
+ status = acpi_ns_get_node_unlocked(start_node,
+ operand[4]->string.pointer,
+ ACPI_NS_SEARCH_PARENT,
+ &parameter_node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* Load the table into the namespace */
+
+ ACPI_INFO(("Dynamic OEM Table Load:"));
+ acpi_ex_exit_interpreter();
+ status = acpi_tb_load_table(table_index, parent_node);
+ acpi_ex_enter_interpreter();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ex_add_table(table_index, &ddb_handle);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Complete the initialization/resolution of new objects */
+
+ acpi_ex_exit_interpreter();
+ acpi_ns_initialize_objects();
+ acpi_ex_enter_interpreter();
+
+ /* Parameter Data (optional) */
+
+ if (parameter_node) {
+
+ /* Store the parameter data into the optional parameter object */
+
+ status = acpi_ex_store(operand[5],
+ ACPI_CAST_PTR(union acpi_operand_object,
+ parameter_node),
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ (void)acpi_ex_unload_table(ddb_handle);
+
+ acpi_ut_remove_reference(ddb_handle);
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* Remove the reference to ddb_handle created by acpi_ex_add_table above */
+
+ acpi_ut_remove_reference(ddb_handle);
+
+ /* Return -1 (non-zero) indicates success */
+
+ return_obj->integer.value = 0xFFFFFFFFFFFFFFFF;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_region_read
+ *
+ * PARAMETERS: obj_desc - Region descriptor
+ * length - Number of bytes to read
+ * buffer - Pointer to where to put the data
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read data from an operation region. The read starts from the
+ * beginning of the region.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_region_read(union acpi_operand_object *obj_desc, u32 length, u8 *buffer)
+{
+ acpi_status status;
+ u64 value;
+ u32 region_offset = 0;
+ u32 i;
+
+ /* Bytewise reads */
+
+ for (i = 0; i < length; i++) {
+ status =
+ acpi_ev_address_space_dispatch(obj_desc, NULL, ACPI_READ,
+ region_offset, 8, &value);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ *buffer = (u8)value;
+ buffer++;
+ region_offset++;
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_load_op
+ *
+ * PARAMETERS: obj_desc - Region or Buffer/Field where the table will be
+ * obtained
+ * target - Where the status of the load will be stored
+ * walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load an ACPI table from a field or operation region
+ *
+ * NOTE: Region Fields (Field, bank_field, index_fields) are resolved to buffer
+ * objects before this code is reached.
+ *
+ * If source is an operation region, it must refer to system_memory, as
+ * per the ACPI specification.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_load_op(union acpi_operand_object *obj_desc,
+ union acpi_operand_object *target,
+ struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object *ddb_handle;
+ struct acpi_table_header *table_header;
+ struct acpi_table_header *table;
+ u32 table_index;
+ acpi_status status;
+ u32 length;
+
+ ACPI_FUNCTION_TRACE(ex_load_op);
+
+ if (target->common.descriptor_type == ACPI_DESC_TYPE_NAMED) {
+ target =
+ acpi_ns_get_attached_object(ACPI_CAST_PTR
+ (struct acpi_namespace_node,
+ target));
+ }
+ if (target->common.type != ACPI_TYPE_INTEGER) {
+ ACPI_EXCEPTION((AE_INFO, AE_TYPE,
+ "Type not integer: %X\n", target->common.type));
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ target->integer.value = 0;
+
+ /* Source Object can be either an op_region or a Buffer/Field */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_REGION:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Load table from Region %p\n", obj_desc));
+
+ /* Region must be system_memory (from ACPI spec) */
+
+ if (obj_desc->region.space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * If the Region Address and Length have not been previously
+ * evaluated, evaluate them now and save the results.
+ */
+ if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
+ status = acpi_ds_get_region_arguments(obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* Get the table header first so we can get the table length */
+
+ table_header = ACPI_ALLOCATE(sizeof(struct acpi_table_header));
+ if (!table_header) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ status =
+ acpi_ex_region_read(obj_desc,
+ sizeof(struct acpi_table_header),
+ ACPI_CAST_PTR(u8, table_header));
+ length = table_header->length;
+ ACPI_FREE(table_header);
+
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Must have at least an ACPI table header */
+
+ if (length < sizeof(struct acpi_table_header)) {
+ return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
+ }
+
+ /*
+ * The original implementation simply mapped the table, with no copy.
+ * However, the memory region is not guaranteed to remain stable and
+ * we must copy the table to a local buffer. For example, the memory
+ * region is corrupted after suspend on some machines. Dynamically
+ * loaded tables are usually small, so this overhead is minimal.
+ *
+ * The latest implementation (5/2009) does not use a mapping at all.
+ * We use the low-level operation region interface to read the table
+ * instead of the obvious optimization of using a direct mapping.
+ * This maintains a consistent use of operation regions across the
+ * entire subsystem. This is important if additional processing must
+ * be performed in the (possibly user-installed) operation region
+ * handler. For example, acpi_exec and ASLTS depend on this.
+ */
+
+ /* Allocate a buffer for the table */
+
+ table = ACPI_ALLOCATE(length);
+ if (!table) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Read the entire table */
+
+ status = acpi_ex_region_read(obj_desc, length,
+ ACPI_CAST_PTR(u8, table));
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(table);
+ return_ACPI_STATUS(status);
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER: /* Buffer or resolved region_field */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Load table from Buffer or Field %p\n",
+ obj_desc));
+
+ /* Must have at least an ACPI table header */
+
+ if (obj_desc->buffer.length < sizeof(struct acpi_table_header)) {
+ return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
+ }
+
+ /* Get the actual table length from the table header */
+
+ table_header =
+ ACPI_CAST_PTR(struct acpi_table_header,
+ obj_desc->buffer.pointer);
+ length = table_header->length;
+
+ /* Table cannot extend beyond the buffer */
+
+ if (length > obj_desc->buffer.length) {
+ return_ACPI_STATUS(AE_AML_BUFFER_LIMIT);
+ }
+ if (length < sizeof(struct acpi_table_header)) {
+ return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
+ }
+
+ /*
+ * Copy the table from the buffer because the buffer could be
+ * modified or even deleted in the future
+ */
+ table = ACPI_ALLOCATE(length);
+ if (!table) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ memcpy(table, table_header, length);
+ break;
+
+ default:
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /* Install the new table into the local data structures */
+
+ ACPI_INFO(("Dynamic OEM Table Load:"));
+ acpi_ex_exit_interpreter();
+ status = acpi_tb_install_and_load_table(ACPI_PTR_TO_PHYSADDR(table),
+ ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL,
+ table, TRUE, &table_index);
+ acpi_ex_enter_interpreter();
+ if (ACPI_FAILURE(status)) {
+
+ /* Delete allocated table buffer */
+
+ ACPI_FREE(table);
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Add the table to the namespace.
+ *
+ * Note: Load the table objects relative to the root of the namespace.
+ * This appears to go against the ACPI specification, but we do it for
+ * compatibility with other ACPI implementations.
+ */
+ status = acpi_ex_add_table(table_index, &ddb_handle);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Complete the initialization/resolution of new objects */
+
+ acpi_ex_exit_interpreter();
+ acpi_ns_initialize_objects();
+ acpi_ex_enter_interpreter();
+
+ /* Remove the reference to ddb_handle created by acpi_ex_add_table above */
+
+ acpi_ut_remove_reference(ddb_handle);
+
+ /* Return -1 (non-zero) indicates success */
+
+ target->integer.value = 0xFFFFFFFFFFFFFFFF;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_unload_table
+ *
+ * PARAMETERS: ddb_handle - Handle to a previously loaded table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Unload an ACPI table
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *table_desc = ddb_handle;
+ u32 table_index;
+
+ ACPI_FUNCTION_TRACE(ex_unload_table);
+
+ /*
+ * Temporarily emit a warning so that the ASL for the machine can be
+ * hopefully obtained. This is to say that the Unload() operator is
+ * extremely rare if not completely unused.
+ */
+ ACPI_WARNING((AE_INFO, "Received request to unload an ACPI table"));
+
+ /*
+ * May 2018: Unload is no longer supported for the following reasons:
+ * 1) A correct implementation on some hosts may not be possible.
+ * 2) Other ACPI implementations do not correctly/fully support it.
+ * 3) It requires host device driver support which does not exist.
+ * (To properly support namespace unload out from underneath.)
+ * 4) This AML operator has never been seen in the field.
+ */
+ ACPI_EXCEPTION((AE_INFO, AE_NOT_IMPLEMENTED,
+ "AML Unload operator is not supported"));
+
+ /*
+ * Validate the handle
+ * Although the handle is partially validated in acpi_ex_reconfiguration()
+ * when it calls acpi_ex_resolve_operands(), the handle is more completely
+ * validated here.
+ *
+ * Handle must be a valid operand object of type reference. Also, the
+ * ddb_handle must still be marked valid (table has not been previously
+ * unloaded)
+ */
+ if ((!ddb_handle) ||
+ (ACPI_GET_DESCRIPTOR_TYPE(ddb_handle) != ACPI_DESC_TYPE_OPERAND) ||
+ (ddb_handle->common.type != ACPI_TYPE_LOCAL_REFERENCE) ||
+ (!(ddb_handle->common.flags & AOPOBJ_DATA_VALID))) {
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /* Get the table index from the ddb_handle */
+
+ table_index = table_desc->reference.value;
+
+ /*
+ * Release the interpreter lock so that the table lock won't have
+ * strict order requirement against it.
+ */
+ acpi_ex_exit_interpreter();
+ status = acpi_tb_unload_table(table_index);
+ acpi_ex_enter_interpreter();
+
+ /*
+ * Invalidate the handle. We do this because the handle may be stored
+ * in a named object and may not be actually deleted until much later.
+ */
+ if (ACPI_SUCCESS(status)) {
+ ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID;
+ }
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c
new file mode 100644
index 000000000..8de5d47ad
--- /dev/null
+++ b/drivers/acpi/acpica/exconvrt.c
@@ -0,0 +1,692 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exconvrt - Object conversion routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exconvrt")
+
+/* Local prototypes */
+static u32
+acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 max_length);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_convert_to_integer
+ *
+ * PARAMETERS: obj_desc - Object to be converted. Must be an
+ * Integer, Buffer, or String
+ * result_desc - Where the new Integer object is returned
+ * implicit_conversion - Used for string conversion
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert an ACPI Object to an integer.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **result_desc,
+ u32 implicit_conversion)
+{
+ union acpi_operand_object *return_desc;
+ u8 *pointer;
+ u64 result;
+ u32 i;
+ u32 count;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_convert_to_integer, obj_desc);
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ /* No conversion necessary */
+
+ *result_desc = obj_desc;
+ return_ACPI_STATUS(AE_OK);
+
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_STRING:
+
+ /* Note: Takes advantage of common buffer/string fields */
+
+ pointer = obj_desc->buffer.pointer;
+ count = obj_desc->buffer.length;
+ break;
+
+ default:
+
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ /*
+ * Convert the buffer/string to an integer. Note that both buffers and
+ * strings are treated as raw data - we don't convert ascii to hex for
+ * strings.
+ *
+ * There are two terminating conditions for the loop:
+ * 1) The size of an integer has been reached, or
+ * 2) The end of the buffer or string has been reached
+ */
+ result = 0;
+
+ /* String conversion is different than Buffer conversion */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_STRING:
+ /*
+ * Convert string to an integer - for most cases, the string must be
+ * hexadecimal as per the ACPI specification. The only exception (as
+ * of ACPI 3.0) is that the to_integer() operator allows both decimal
+ * and hexadecimal strings (hex prefixed with "0x").
+ *
+ * Explicit conversion is used only by to_integer.
+ * All other string-to-integer conversions are implicit conversions.
+ */
+ if (implicit_conversion) {
+ result =
+ acpi_ut_implicit_strtoul64(ACPI_CAST_PTR
+ (char, pointer));
+ } else {
+ result =
+ acpi_ut_explicit_strtoul64(ACPI_CAST_PTR
+ (char, pointer));
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ /* Check for zero-length buffer */
+
+ if (!count) {
+ return_ACPI_STATUS(AE_AML_BUFFER_LIMIT);
+ }
+
+ /* Transfer no more than an integer's worth of data */
+
+ if (count > acpi_gbl_integer_byte_width) {
+ count = acpi_gbl_integer_byte_width;
+ }
+
+ /*
+ * Convert buffer to an integer - we simply grab enough raw data
+ * from the buffer to fill an integer
+ */
+ for (i = 0; i < count; i++) {
+ /*
+ * Get next byte and shift it into the Result.
+ * Little endian is used, meaning that the first byte of the buffer
+ * is the LSB of the integer
+ */
+ result |= (((u64) pointer[i]) << (i * 8));
+ }
+ break;
+
+ default:
+
+ /* No other types can get here */
+
+ break;
+ }
+
+ /* Create a new integer */
+
+ return_desc = acpi_ut_create_integer_object(result);
+ if (!return_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64(result)));
+
+ /* Save the Result */
+
+ (void)acpi_ex_truncate_for32bit_table(return_desc);
+ *result_desc = return_desc;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_convert_to_buffer
+ *
+ * PARAMETERS: obj_desc - Object to be converted. Must be an
+ * Integer, Buffer, or String
+ * result_desc - Where the new buffer object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert an ACPI Object to a Buffer
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **result_desc)
+{
+ union acpi_operand_object *return_desc;
+ u8 *new_buf;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_convert_to_buffer, obj_desc);
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_BUFFER:
+
+ /* No conversion necessary */
+
+ *result_desc = obj_desc;
+ return_ACPI_STATUS(AE_OK);
+
+ case ACPI_TYPE_INTEGER:
+ /*
+ * Create a new Buffer object.
+ * Need enough space for one integer
+ */
+ return_desc =
+ acpi_ut_create_buffer_object(acpi_gbl_integer_byte_width);
+ if (!return_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Copy the integer to the buffer, LSB first */
+
+ new_buf = return_desc->buffer.pointer;
+ memcpy(new_buf, &obj_desc->integer.value,
+ acpi_gbl_integer_byte_width);
+ break;
+
+ case ACPI_TYPE_STRING:
+ /*
+ * Create a new Buffer object
+ * Size will be the string length
+ *
+ * NOTE: Add one to the string length to include the null terminator.
+ * The ACPI spec is unclear on this subject, but there is existing
+ * ASL/AML code that depends on the null being transferred to the new
+ * buffer.
+ */
+ return_desc = acpi_ut_create_buffer_object((acpi_size)
+ obj_desc->string.
+ length + 1);
+ if (!return_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* 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);
+ break;
+
+ default:
+
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ /* Mark buffer initialized */
+
+ return_desc->common.flags |= AOPOBJ_DATA_VALID;
+ *result_desc = return_desc;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_convert_to_ascii
+ *
+ * PARAMETERS: integer - Value to be converted
+ * 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
+ *
+ * RETURN: Actual string length
+ *
+ * DESCRIPTION: Convert an ACPI Integer to a hex or decimal string
+ *
+ ******************************************************************************/
+
+static u32
+acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 data_width)
+{
+ u64 digit;
+ u32 i;
+ u32 j;
+ u32 k = 0;
+ u32 hex_length;
+ u32 decimal_length;
+ u32 remainder;
+ u8 supress_zeros;
+
+ ACPI_FUNCTION_ENTRY();
+
+ switch (base) {
+ case 10:
+
+ /* Setup max length for the decimal number */
+
+ switch (data_width) {
+ case 1:
+
+ decimal_length = ACPI_MAX8_DECIMAL_DIGITS;
+ break;
+
+ case 4:
+
+ decimal_length = ACPI_MAX32_DECIMAL_DIGITS;
+ break;
+
+ case 8:
+ default:
+
+ decimal_length = ACPI_MAX64_DECIMAL_DIGITS;
+ break;
+ }
+
+ supress_zeros = TRUE; /* No leading zeros */
+ remainder = 0;
+
+ for (i = decimal_length; i > 0; i--) {
+
+ /* Divide by nth factor of 10 */
+
+ digit = integer;
+ for (j = 0; j < i; j++) {
+ (void)acpi_ut_short_divide(digit, 10, &digit,
+ &remainder);
+ }
+
+ /* Handle leading zeros */
+
+ if (remainder != 0) {
+ supress_zeros = FALSE;
+ }
+
+ if (!supress_zeros) {
+ string[k] = (u8) (ACPI_ASCII_ZERO + remainder);
+ k++;
+ }
+ }
+ break;
+
+ case 16:
+
+ /* hex_length: 2 ascii hex chars per data byte */
+
+ hex_length = (data_width * 2);
+ for (i = 0, j = (hex_length - 1); i < hex_length; i++, j--) {
+
+ /* Get one hex digit, most significant digits first */
+
+ string[k] = (u8)
+ acpi_ut_hex_to_ascii_char(integer, ACPI_MUL_4(j));
+ k++;
+ }
+ break;
+
+ default:
+ return (0);
+ }
+
+ /*
+ * Since leading zeros are suppressed, we must check for the case where
+ * the integer equals 0
+ *
+ * Finally, null terminate the string and return the length
+ */
+ if (!k) {
+ string[0] = ACPI_ASCII_ZERO;
+ k = 1;
+ }
+
+ string[k] = 0;
+ return ((u32) k);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_convert_to_string
+ *
+ * PARAMETERS: obj_desc - Object to be converted. Must be an
+ * Integer, Buffer, or String
+ * result_desc - Where the string object is returned
+ * type - String flags (base and conversion type)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert an ACPI Object to a string. Supports both implicit
+ * and explicit conversions and related rules.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_convert_to_string(union acpi_operand_object * obj_desc,
+ union acpi_operand_object ** result_desc, u32 type)
+{
+ union acpi_operand_object *return_desc;
+ u8 *new_buf;
+ u32 i;
+ u32 string_length = 0;
+ u16 base = 16;
+ u8 separator = ',';
+
+ ACPI_FUNCTION_TRACE_PTR(ex_convert_to_string, obj_desc);
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_STRING:
+
+ /* No conversion necessary */
+
+ *result_desc = obj_desc;
+ return_ACPI_STATUS(AE_OK);
+
+ case ACPI_TYPE_INTEGER:
+
+ switch (type) {
+ case ACPI_EXPLICIT_CONVERT_DECIMAL:
+ /*
+ * From to_decimal_string, integer source.
+ *
+ * Make room for the maximum decimal number size
+ */
+ string_length = ACPI_MAX_DECIMAL_DIGITS;
+ base = 10;
+ break;
+
+ default:
+
+ /* Two hex string characters for each integer byte */
+
+ string_length = ACPI_MUL_2(acpi_gbl_integer_byte_width);
+ break;
+ }
+
+ /*
+ * Create a new String
+ * Need enough space for one ASCII integer (plus null terminator)
+ */
+ return_desc =
+ acpi_ut_create_string_object((acpi_size)string_length);
+ if (!return_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ new_buf = return_desc->buffer.pointer;
+
+ /* Convert integer to string */
+
+ string_length =
+ acpi_ex_convert_to_ascii(obj_desc->integer.value, base,
+ new_buf,
+ acpi_gbl_integer_byte_width);
+
+ /* Null terminate at the correct place */
+
+ return_desc->string.length = string_length;
+ new_buf[string_length] = 0;
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ /* Setup string length, base, and separator */
+
+ switch (type) {
+ case ACPI_EXPLICIT_CONVERT_DECIMAL: /* Used by to_decimal_string */
+ /*
+ * Explicit conversion from the to_decimal_string ASL operator.
+ *
+ * From ACPI: "If the input is a buffer, it is converted to a
+ * a string of decimal values separated by commas."
+ */
+ base = 10;
+
+ /*
+ * Calculate the final string length. Individual string values
+ * are variable length (include separator for each)
+ */
+ for (i = 0; i < obj_desc->buffer.length; i++) {
+ if (obj_desc->buffer.pointer[i] >= 100) {
+ string_length += 4;
+ } else if (obj_desc->buffer.pointer[i] >= 10) {
+ string_length += 3;
+ } else {
+ string_length += 2;
+ }
+ }
+ break;
+
+ case ACPI_IMPLICIT_CONVERT_HEX:
+ /*
+ * Implicit buffer-to-string conversion
+ *
+ * From the ACPI spec:
+ * "The entire contents of the buffer are converted to a string of
+ * two-character hexadecimal numbers, each separated by a space."
+ *
+ * Each hex number is prefixed with 0x (11/2018)
+ */
+ separator = ' ';
+ string_length = (obj_desc->buffer.length * 5);
+ break;
+
+ case ACPI_EXPLICIT_CONVERT_HEX:
+ /*
+ * Explicit conversion from the to_hex_string ASL operator.
+ *
+ * From ACPI: "If Data is a buffer, it is converted to a string of
+ * hexadecimal values separated by commas."
+ *
+ * Each hex number is prefixed with 0x (11/2018)
+ */
+ separator = ',';
+ string_length = (obj_desc->buffer.length * 5);
+ break;
+
+ default:
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Create a new string object and string buffer
+ * (-1 because of extra separator included in string_length from above)
+ * Allow creation of zero-length strings from zero-length buffers.
+ */
+ if (string_length) {
+ string_length--;
+ }
+
+ return_desc =
+ acpi_ut_create_string_object((acpi_size)string_length);
+ if (!return_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ new_buf = return_desc->buffer.pointer;
+
+ /*
+ * Convert buffer bytes to hex or decimal values
+ * (separated by commas or spaces)
+ */
+ for (i = 0; i < obj_desc->buffer.length; i++) {
+ if (base == 16) {
+
+ /* Emit 0x prefix for explicit/implicit hex conversion */
+
+ *new_buf++ = '0';
+ *new_buf++ = 'x';
+ }
+
+ new_buf += acpi_ex_convert_to_ascii((u64) obj_desc->
+ buffer.pointer[i],
+ base, new_buf, 1);
+
+ /* Each digit is separated by either a comma or space */
+
+ *new_buf++ = separator;
+ }
+
+ /*
+ * Null terminate the string
+ * (overwrites final comma/space from above)
+ */
+ if (obj_desc->buffer.length) {
+ new_buf--;
+ }
+ *new_buf = 0;
+ break;
+
+ default:
+
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ *result_desc = return_desc;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_convert_to_target_type
+ *
+ * PARAMETERS: destination_type - Current type of the destination
+ * source_desc - Source object to be converted.
+ * result_desc - Where the converted object is returned
+ * walk_state - Current method state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Implements "implicit conversion" rules for storing an object.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_convert_to_target_type(acpi_object_type destination_type,
+ union acpi_operand_object *source_desc,
+ union acpi_operand_object **result_desc,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ex_convert_to_target_type);
+
+ /* Default behavior */
+
+ *result_desc = source_desc;
+
+ /*
+ * If required by the target,
+ * perform implicit conversion on the source before we store it.
+ */
+ switch (GET_CURRENT_ARG_TYPE(walk_state->op_info->runtime_args)) {
+ case ARGI_SIMPLE_TARGET:
+ case ARGI_FIXED_TARGET:
+ case ARGI_INTEGER_REF: /* Handles Increment, Decrement cases */
+
+ switch (destination_type) {
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ /*
+ * Named field can always handle conversions
+ */
+ break;
+
+ default:
+
+ /* No conversion allowed for these types */
+
+ if (destination_type != source_desc->common.type) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Explicit operator, will store (%s) over existing type (%s)\n",
+ acpi_ut_get_object_type_name
+ (source_desc),
+ acpi_ut_get_type_name
+ (destination_type)));
+ status = AE_TYPE;
+ }
+ }
+ break;
+
+ case ARGI_TARGETREF:
+ case ARGI_STORE_TARGET:
+
+ switch (destination_type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+ /*
+ * These types require an Integer operand. We can convert
+ * a Buffer or a String to an Integer if necessary.
+ */
+ status =
+ acpi_ex_convert_to_integer(source_desc, result_desc,
+ ACPI_IMPLICIT_CONVERSION);
+ break;
+
+ case ACPI_TYPE_STRING:
+ /*
+ * The operand must be a String. We can convert an
+ * Integer or Buffer if necessary
+ */
+ status =
+ acpi_ex_convert_to_string(source_desc, result_desc,
+ ACPI_IMPLICIT_CONVERT_HEX);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+ /*
+ * The operand must be a Buffer. We can convert an
+ * Integer or String if necessary
+ */
+ status =
+ acpi_ex_convert_to_buffer(source_desc, result_desc);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Bad destination type during conversion: 0x%X",
+ destination_type));
+ status = AE_AML_INTERNAL;
+ break;
+ }
+ break;
+
+ case ARGI_REFERENCE:
+ /*
+ * create_xxxx_field cases - we are storing the field object into the name
+ */
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown Target type ID 0x%X AmlOpcode 0x%X DestType %s",
+ GET_CURRENT_ARG_TYPE(walk_state->op_info->
+ runtime_args),
+ walk_state->opcode,
+ acpi_ut_get_type_name(destination_type)));
+ status = AE_AML_INTERNAL;
+ }
+
+ /*
+ * Source-to-Target conversion semantics:
+ *
+ * If conversion to the target type cannot be performed, then simply
+ * overwrite the target with the new object and type.
+ */
+ if (status == AE_TYPE) {
+ status = AE_OK;
+ }
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c
new file mode 100644
index 000000000..fb2453fa9
--- /dev/null
+++ b/drivers/acpi/acpica/excreate.c
@@ -0,0 +1,470 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: excreate - Named object creation
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlcode.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("excreate")
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_create_alias
+ *
+ * PARAMETERS: walk_state - Current state, contains operands
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new named alias
+ *
+ ******************************************************************************/
+acpi_status acpi_ex_create_alias(struct acpi_walk_state *walk_state)
+{
+ struct acpi_namespace_node *target_node;
+ struct acpi_namespace_node *alias_node;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ex_create_alias);
+
+ /* Get the source/alias operands (both namespace nodes) */
+
+ alias_node = (struct acpi_namespace_node *)walk_state->operands[0];
+ target_node = (struct acpi_namespace_node *)walk_state->operands[1];
+
+ if ((target_node->type == ACPI_TYPE_LOCAL_ALIAS) ||
+ (target_node->type == ACPI_TYPE_LOCAL_METHOD_ALIAS)) {
+ /*
+ * Dereference an existing alias so that we don't create a chain
+ * of aliases. With this code, we guarantee that an alias is
+ * always exactly one level of indirection away from the
+ * actual aliased name.
+ */
+ target_node =
+ ACPI_CAST_PTR(struct acpi_namespace_node,
+ target_node->object);
+ }
+
+ /* Ensure that the target node is valid */
+
+ if (!target_node) {
+ return_ACPI_STATUS(AE_NULL_OBJECT);
+ }
+
+ /* Construct the alias object (a namespace node) */
+
+ switch (target_node->type) {
+ case ACPI_TYPE_METHOD:
+ /*
+ * Control method aliases need to be differentiated with
+ * a special type
+ */
+ alias_node->type = ACPI_TYPE_LOCAL_METHOD_ALIAS;
+ break;
+
+ default:
+ /*
+ * All other object types.
+ *
+ * The new alias has the type ALIAS and points to the original
+ * NS node, not the object itself.
+ */
+ alias_node->type = ACPI_TYPE_LOCAL_ALIAS;
+ alias_node->object =
+ ACPI_CAST_PTR(union acpi_operand_object, target_node);
+ break;
+ }
+
+ /* Since both operands are Nodes, we don't need to delete them */
+
+ alias_node->object =
+ ACPI_CAST_PTR(union acpi_operand_object, target_node);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_create_event
+ *
+ * PARAMETERS: walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new event object
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_create_event(struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+
+ ACPI_FUNCTION_TRACE(ex_create_event);
+
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_EVENT);
+ if (!obj_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /*
+ * Create the actual OS semaphore, with zero initial units -- meaning
+ * that the event is created in an unsignalled state
+ */
+ status = acpi_os_create_semaphore(ACPI_NO_UNIT_LIMIT, 0,
+ &obj_desc->event.os_semaphore);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /* Attach object to the Node */
+
+ status = acpi_ns_attach_object((struct acpi_namespace_node *)
+ walk_state->operands[0], obj_desc,
+ ACPI_TYPE_EVENT);
+
+cleanup:
+ /*
+ * Remove local reference to the object (on error, will cause deletion
+ * of both object and semaphore if present.)
+ */
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_create_mutex
+ *
+ * PARAMETERS: walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new mutex object
+ *
+ * Mutex (Name[0], sync_level[1])
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_create_mutex(struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *obj_desc;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_create_mutex, ACPI_WALK_OPERANDS);
+
+ /* Create the new mutex object */
+
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_MUTEX);
+ if (!obj_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Create the actual OS Mutex */
+
+ status = acpi_os_create_mutex(&obj_desc->mutex.os_mutex);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /* Init object and attach to NS node */
+
+ obj_desc->mutex.sync_level = (u8)walk_state->operands[1]->integer.value;
+ obj_desc->mutex.node =
+ (struct acpi_namespace_node *)walk_state->operands[0];
+
+ status =
+ acpi_ns_attach_object(obj_desc->mutex.node, obj_desc,
+ ACPI_TYPE_MUTEX);
+
+cleanup:
+ /*
+ * Remove local reference to the object (on error, will cause deletion
+ * of both object and semaphore if present.)
+ */
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_create_region
+ *
+ * PARAMETERS: aml_start - Pointer to the region declaration AML
+ * aml_length - Max length of the declaration AML
+ * space_id - Address space ID for the region
+ * walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new operation region object
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_create_region(u8 * aml_start,
+ u32 aml_length,
+ u8 space_id, struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *region_obj2;
+
+ ACPI_FUNCTION_TRACE(ex_create_region);
+
+ /* Get the Namespace Node */
+
+ node = walk_state->op->common.node;
+
+ /*
+ * If the region object is already attached to this node,
+ * just return
+ */
+ if (acpi_ns_get_attached_object(node)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Space ID must be one of the predefined IDs, or in the user-defined
+ * range
+ */
+ if (!acpi_is_valid_space_id(space_id)) {
+ /*
+ * Print an error message, but continue. We don't want to abort
+ * a table load for this exception. Instead, if the region is
+ * actually used at runtime, abort the executing method.
+ */
+ ACPI_ERROR((AE_INFO,
+ "Invalid/unknown Address Space ID: 0x%2.2X",
+ space_id));
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Region Type - %s (0x%X)\n",
+ acpi_ut_get_region_name(space_id), space_id));
+
+ /* Create the region descriptor */
+
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_REGION);
+ if (!obj_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /*
+ * Remember location in AML stream of address & length
+ * operands since they need to be evaluated at run time.
+ */
+ region_obj2 = acpi_ns_get_secondary_object(obj_desc);
+ region_obj2->extra.aml_start = aml_start;
+ region_obj2->extra.aml_length = aml_length;
+ region_obj2->extra.method_REG = NULL;
+ if (walk_state->scope_info) {
+ region_obj2->extra.scope_node =
+ walk_state->scope_info->scope.node;
+ } else {
+ region_obj2->extra.scope_node = node;
+ }
+
+ /* Init the region from the operands */
+
+ obj_desc->region.space_id = space_id;
+ obj_desc->region.address = 0;
+ obj_desc->region.length = 0;
+ obj_desc->region.pointer = NULL;
+ obj_desc->region.node = node;
+ obj_desc->region.handler = NULL;
+ obj_desc->common.flags &=
+ ~(AOPOBJ_SETUP_COMPLETE | AOPOBJ_REG_CONNECTED |
+ AOPOBJ_OBJECT_INITIALIZED);
+
+ /* Install the new region object in the parent Node */
+
+ status = acpi_ns_attach_object(node, obj_desc, ACPI_TYPE_REGION);
+
+cleanup:
+
+ /* Remove local reference to the object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_create_processor
+ *
+ * PARAMETERS: walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new processor object and populate the fields
+ *
+ * Processor (Name[0], cpu_ID[1], pblock_addr[2], pblock_length[3])
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_create_processor(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_create_processor, walk_state);
+
+ /* Create the processor object */
+
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_PROCESSOR);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Initialize the processor object from the operands */
+
+ obj_desc->processor.proc_id = (u8) operand[1]->integer.value;
+ obj_desc->processor.length = (u8) operand[3]->integer.value;
+ obj_desc->processor.address =
+ (acpi_io_address)operand[2]->integer.value;
+
+ /* Install the processor object in the parent Node */
+
+ status = acpi_ns_attach_object((struct acpi_namespace_node *)operand[0],
+ obj_desc, ACPI_TYPE_PROCESSOR);
+
+ /* Remove local reference to the object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_create_power_resource
+ *
+ * PARAMETERS: walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new power_resource object and populate the fields
+ *
+ * power_resource (Name[0], system_level[1], resource_order[2])
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_create_power_resource(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_create_power_resource, walk_state);
+
+ /* Create the power resource object */
+
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_POWER);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Initialize the power object from the operands */
+
+ obj_desc->power_resource.system_level = (u8) operand[1]->integer.value;
+ obj_desc->power_resource.resource_order =
+ (u16) operand[2]->integer.value;
+
+ /* Install the power resource object in the parent Node */
+
+ status = acpi_ns_attach_object((struct acpi_namespace_node *)operand[0],
+ obj_desc, ACPI_TYPE_POWER);
+
+ /* Remove local reference to the object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_create_method
+ *
+ * PARAMETERS: aml_start - First byte of the method's AML
+ * aml_length - AML byte count for this method
+ * walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new method object
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_create_method(u8 * aml_start,
+ u32 aml_length, struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+ u8 method_flags;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_create_method, walk_state);
+
+ /* Create a new method object */
+
+ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_METHOD);
+ if (!obj_desc) {
+ status = AE_NO_MEMORY;
+ goto exit;
+ }
+
+ /* Save the method's AML pointer and length */
+
+ obj_desc->method.aml_start = aml_start;
+ obj_desc->method.aml_length = aml_length;
+ obj_desc->method.node = operand[0];
+
+ /*
+ * Disassemble the method flags. Split off the arg_count, Serialized
+ * flag, and sync_level for efficiency.
+ */
+ method_flags = (u8)operand[1]->integer.value;
+ obj_desc->method.param_count = (u8)
+ (method_flags & AML_METHOD_ARG_COUNT);
+
+ /*
+ * Get the sync_level. If method is serialized, a mutex will be
+ * created for this method when it is parsed.
+ */
+ if (method_flags & AML_METHOD_SERIALIZED) {
+ obj_desc->method.info_flags = ACPI_METHOD_SERIALIZED;
+
+ /*
+ * ACPI 1.0: sync_level = 0
+ * ACPI 2.0: sync_level = sync_level in method declaration
+ */
+ obj_desc->method.sync_level = (u8)
+ ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4);
+ }
+
+ /* Attach the new object to the method Node */
+
+ status = acpi_ns_attach_object((struct acpi_namespace_node *)operand[0],
+ obj_desc, ACPI_TYPE_METHOD);
+
+ /* Remove local reference to the object */
+
+ acpi_ut_remove_reference(obj_desc);
+
+exit:
+ /* Remove a reference to the operand */
+
+ acpi_ut_remove_reference(operand[1]);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exdebug.c b/drivers/acpi/acpica/exdebug.c
new file mode 100644
index 000000000..8a99aadb9
--- /dev/null
+++ b/drivers/acpi/acpica/exdebug.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exdebug - Support for stores to the AML Debug Object
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exdebug")
+
+#ifndef ACPI_NO_ERROR_MESSAGES
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_do_debug_object
+ *
+ * PARAMETERS: source_desc - Object to be output to "Debug Object"
+ * level - Indentation level (used for packages)
+ * index - Current package element, zero if not pkg
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Handles stores to the AML Debug Object. For example:
+ * Store(INT1, Debug)
+ *
+ * This function is not compiled if ACPI_NO_ERROR_MESSAGES is set.
+ *
+ * This function is only enabled if acpi_gbl_enable_aml_debug_object is set, or
+ * if ACPI_LV_DEBUG_OBJECT is set in the acpi_dbg_level. Thus, in the normal
+ * operational case, stores to the debug object are ignored but can be easily
+ * enabled if necessary.
+ *
+ ******************************************************************************/
+void
+acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
+ u32 level, u32 index)
+{
+ u32 i;
+ u32 timer;
+ union acpi_operand_object *object_desc;
+ u32 value;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_do_debug_object, source_desc);
+
+ /* Output must be enabled via the debug_object global or the dbg_level */
+
+ if (!acpi_gbl_enable_aml_debug_object &&
+ !(acpi_dbg_level & ACPI_LV_DEBUG_OBJECT)) {
+ return_VOID;
+ }
+
+ /* Newline -- don't emit the line header */
+
+ if (source_desc &&
+ (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_OPERAND) &&
+ (source_desc->common.type == ACPI_TYPE_STRING)) {
+ if ((source_desc->string.length == 1) &&
+ (*source_desc->string.pointer == '\n')) {
+ acpi_os_printf("\n");
+ return_VOID;
+ }
+ }
+
+ /*
+ * Print line header as long as we are not in the middle of an
+ * object display
+ */
+ if (!((level > 0) && index == 0)) {
+ if (acpi_gbl_display_debug_timer) {
+ /*
+ * We will emit the current timer value (in microseconds) with each
+ * debug output. Only need the lower 26 bits. This allows for 67
+ * million microseconds or 67 seconds before rollover.
+ *
+ * Convert 100 nanosecond units to microseconds
+ */
+ timer = ((u32)acpi_os_get_timer() / 10);
+ timer &= 0x03FFFFFF;
+
+ acpi_os_printf("ACPI Debug: T=0x%8.8X %*s", timer,
+ level, " ");
+ } else {
+ acpi_os_printf("ACPI Debug: %*s", level, " ");
+ }
+ }
+
+ /* Display the index for package output only */
+
+ if (index > 0) {
+ acpi_os_printf("(%.2u) ", index - 1);
+ }
+
+ if (!source_desc) {
+ acpi_os_printf("[Null Object]\n");
+ return_VOID;
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_OPERAND) {
+
+ /* No object type prefix needed for integers and strings */
+
+ if ((source_desc->common.type != ACPI_TYPE_INTEGER) &&
+ (source_desc->common.type != ACPI_TYPE_STRING)) {
+ acpi_os_printf("%s ",
+ acpi_ut_get_object_type_name
+ (source_desc));
+ }
+
+ if (!acpi_ut_valid_internal_object(source_desc)) {
+ acpi_os_printf("%p, Invalid Internal Object!\n",
+ source_desc);
+ return_VOID;
+ }
+ } else if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) ==
+ ACPI_DESC_TYPE_NAMED) {
+ acpi_os_printf("%s (Node %p)\n",
+ acpi_ut_get_type_name(((struct
+ acpi_namespace_node *)
+ source_desc)->type),
+ source_desc);
+ return_VOID;
+ } else {
+ return_VOID;
+ }
+
+ /* source_desc is of type ACPI_DESC_TYPE_OPERAND */
+
+ switch (source_desc->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ /* Output correct integer width */
+
+ if (acpi_gbl_integer_byte_width == 4) {
+ acpi_os_printf("0x%8.8X\n",
+ (u32)source_desc->integer.value);
+ } else {
+ acpi_os_printf("0x%8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64(source_desc->integer.
+ value));
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ acpi_os_printf("[0x%.2X]\n", (u32)source_desc->buffer.length);
+ acpi_ut_dump_buffer(source_desc->buffer.pointer,
+ (source_desc->buffer.length < 256) ?
+ source_desc->buffer.length : 256,
+ DB_BYTE_DISPLAY, 0);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ acpi_os_printf("\"%s\"\n", source_desc->string.pointer);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ acpi_os_printf("(Contains 0x%.2X Elements):\n",
+ source_desc->package.count);
+
+ /* Output the entire contents of the package */
+
+ for (i = 0; i < source_desc->package.count; i++) {
+ acpi_ex_do_debug_object(source_desc->package.
+ elements[i], level + 4, i + 1);
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ acpi_os_printf("[%s] ",
+ acpi_ut_get_reference_name(source_desc));
+
+ /* Decode the reference */
+
+ switch (source_desc->reference.class) {
+ case ACPI_REFCLASS_INDEX:
+
+ acpi_os_printf("0x%X\n", source_desc->reference.value);
+ break;
+
+ case ACPI_REFCLASS_TABLE:
+
+ /* Case for ddb_handle */
+
+ acpi_os_printf("Table Index 0x%X\n",
+ source_desc->reference.value);
+ return_VOID;
+
+ default:
+
+ break;
+ }
+
+ acpi_os_printf(" ");
+
+ /* Check for valid node first, then valid object */
+
+ if (source_desc->reference.node) {
+ if (ACPI_GET_DESCRIPTOR_TYPE
+ (source_desc->reference.node) !=
+ ACPI_DESC_TYPE_NAMED) {
+ acpi_os_printf
+ (" %p - Not a valid namespace node\n",
+ source_desc->reference.node);
+ } else {
+ acpi_os_printf("Node %p [%4.4s] ",
+ source_desc->reference.node,
+ (source_desc->reference.node)->
+ name.ascii);
+
+ switch ((source_desc->reference.node)->type) {
+
+ /* These types have no attached object */
+
+ case ACPI_TYPE_DEVICE:
+ acpi_os_printf("Device\n");
+ break;
+
+ case ACPI_TYPE_THERMAL:
+ acpi_os_printf("Thermal Zone\n");
+ break;
+
+ default:
+
+ acpi_ex_do_debug_object((source_desc->
+ reference.
+ node)->object,
+ level + 4, 0);
+ break;
+ }
+ }
+ } else if (source_desc->reference.object) {
+ if (ACPI_GET_DESCRIPTOR_TYPE
+ (source_desc->reference.object) ==
+ ACPI_DESC_TYPE_NAMED) {
+
+ /* Reference object is a namespace node */
+
+ acpi_ex_do_debug_object(ACPI_CAST_PTR
+ (union
+ acpi_operand_object,
+ source_desc->reference.
+ object), level + 4, 0);
+ } else {
+ object_desc = source_desc->reference.object;
+ value = source_desc->reference.value;
+
+ switch (object_desc->common.type) {
+ case ACPI_TYPE_BUFFER:
+
+ acpi_os_printf("Buffer[%u] = 0x%2.2X\n",
+ value,
+ *source_desc->reference.
+ index_pointer);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ acpi_os_printf
+ ("String[%u] = \"%c\" (0x%2.2X)\n",
+ value,
+ *source_desc->reference.
+ index_pointer,
+ *source_desc->reference.
+ index_pointer);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ acpi_os_printf("Package[%u] = ", value);
+ if (!(*source_desc->reference.where)) {
+ acpi_os_printf
+ ("[Uninitialized Package Element]\n");
+ } else {
+ acpi_ex_do_debug_object
+ (*source_desc->reference.
+ where, level + 4, 0);
+ }
+ break;
+
+ default:
+
+ acpi_os_printf
+ ("Unknown Reference object type %X\n",
+ object_desc->common.type);
+ break;
+ }
+ }
+ }
+ break;
+
+ default:
+
+ acpi_os_printf("(Descriptor %p)\n", source_desc);
+ break;
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "\n"));
+ return_VOID;
+}
+#endif
diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c
new file mode 100644
index 000000000..24b3d041b
--- /dev/null
+++ b/drivers/acpi/acpica/exdump.c
@@ -0,0 +1,1205 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exdump - Interpreter debug output routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlcode.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exdump")
+
+/*
+ * The following routines are used for debug output only
+ */
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+/* Local prototypes */
+static void acpi_ex_out_string(const char *title, const char *value);
+
+static void acpi_ex_out_pointer(const char *title, const void *value);
+
+static void
+acpi_ex_dump_object(union acpi_operand_object *obj_desc,
+ struct acpi_exdump_info *info);
+
+static void acpi_ex_dump_reference_obj(union acpi_operand_object *obj_desc);
+
+static void
+acpi_ex_dump_package_obj(union acpi_operand_object *obj_desc,
+ u32 level, u32 index);
+
+/*******************************************************************************
+ *
+ * Object Descriptor info tables
+ *
+ * Note: The first table entry must be an INIT opcode and must contain
+ * the table length (number of table entries)
+ *
+ ******************************************************************************/
+
+static struct acpi_exdump_info acpi_ex_dump_integer[2] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_integer), NULL},
+ {ACPI_EXD_UINT64, ACPI_EXD_OFFSET(integer.value), "Value"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_string[4] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_string), NULL},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(string.length), "Length"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(string.pointer), "Pointer"},
+ {ACPI_EXD_STRING, 0, NULL}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_buffer[5] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_buffer), NULL},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(buffer.length), "Length"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(buffer.pointer), "Pointer"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET(buffer.node), "Parent Node"},
+ {ACPI_EXD_BUFFER, 0, NULL}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_package[6] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_package), NULL},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET(package.node), "Parent Node"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(package.flags), "Flags"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(package.count), "Element Count"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(package.elements), "Element List"},
+ {ACPI_EXD_PACKAGE, 0, NULL}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_device[4] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_device), NULL},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.notify_list[0]),
+ "System Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.notify_list[1]),
+ "Device Notify"},
+ {ACPI_EXD_HDLR_LIST, ACPI_EXD_OFFSET(device.handler), "Handler"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_event[2] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_event), NULL},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(event.os_semaphore), "OsSemaphore"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_method[9] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_method), NULL},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.info_flags), "Info Flags"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.param_count),
+ "Parameter Count"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.sync_level), "Sync Level"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(method.mutex), "Mutex"},
+ {ACPI_EXD_UINT16, ACPI_EXD_OFFSET(method.owner_id), "Owner Id"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.thread_count), "Thread Count"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(method.aml_length), "Aml Length"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(method.aml_start), "Aml Start"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_mutex[6] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_mutex), NULL},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(mutex.sync_level), "Sync Level"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(mutex.original_sync_level),
+ "Original Sync Level"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.owner_thread), "Owner Thread"},
+ {ACPI_EXD_UINT16, ACPI_EXD_OFFSET(mutex.acquisition_depth),
+ "Acquire Depth"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.os_mutex), "OsMutex"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_region[8] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_region), NULL},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(region.space_id), "Space Id"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(region.flags), "Flags"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET(region.node), "Parent Node"},
+ {ACPI_EXD_ADDRESS, ACPI_EXD_OFFSET(region.address), "Address"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(region.length), "Length"},
+ {ACPI_EXD_HDLR_LIST, ACPI_EXD_OFFSET(region.handler), "Handler"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(region.next), "Next"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_power[6] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_power), NULL},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(power_resource.system_level),
+ "System Level"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(power_resource.resource_order),
+ "Resource Order"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.notify_list[0]),
+ "System Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.notify_list[1]),
+ "Device Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.handler), "Handler"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_processor[7] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_processor), NULL},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(processor.proc_id), "Processor ID"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(processor.length), "Length"},
+ {ACPI_EXD_ADDRESS, ACPI_EXD_OFFSET(processor.address), "Address"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.notify_list[0]),
+ "System Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.notify_list[1]),
+ "Device Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.handler), "Handler"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_thermal[4] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_thermal), NULL},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.notify_list[0]),
+ "System Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.notify_list[1]),
+ "Device Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.handler), "Handler"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_buffer_field[3] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_buffer_field), NULL},
+ {ACPI_EXD_FIELD, 0, NULL},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(buffer_field.buffer_obj),
+ "Buffer Object"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_region_field[5] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_region_field), NULL},
+ {ACPI_EXD_FIELD, 0, NULL},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(field.access_length), "AccessLength"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(field.region_obj), "Region Object"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(field.resource_buffer),
+ "ResourceBuffer"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_bank_field[5] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_bank_field), NULL},
+ {ACPI_EXD_FIELD, 0, NULL},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(bank_field.value), "Value"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(bank_field.region_obj),
+ "Region Object"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(bank_field.bank_obj), "Bank Object"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_index_field[5] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_bank_field), NULL},
+ {ACPI_EXD_FIELD, 0, NULL},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(index_field.value), "Value"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(index_field.index_obj),
+ "Index Object"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(index_field.data_obj), "Data Object"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_reference[9] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_reference), NULL},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(reference.class), "Class"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(reference.target_type), "Target Type"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(reference.value), "Value"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(reference.object), "Object Desc"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET(reference.node), "Node"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(reference.where), "Where"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(reference.index_pointer),
+ "Index Pointer"},
+ {ACPI_EXD_REFERENCE, 0, NULL}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_address_handler[6] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_address_handler),
+ NULL},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(address_space.space_id), "Space Id"},
+ {ACPI_EXD_HDLR_LIST, ACPI_EXD_OFFSET(address_space.next), "Next"},
+ {ACPI_EXD_RGN_LIST, ACPI_EXD_OFFSET(address_space.region_list),
+ "Region List"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET(address_space.node), "Node"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(address_space.context), "Context"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_notify[7] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_notify), NULL},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET(notify.node), "Node"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(notify.handler_type), "Handler Type"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.handler), "Handler"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.context), "Context"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.next[0]),
+ "Next System Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.next[1]), "Next Device Notify"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_extra[6] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_extra), NULL},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(extra.method_REG), "_REG Method"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET(extra.scope_node), "Scope Node"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(extra.region_context),
+ "Region Context"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(extra.aml_start), "Aml Start"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(extra.aml_length), "Aml Length"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_data[3] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_data), NULL},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(data.handler), "Handler"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(data.pointer), "Raw Data"}
+};
+
+/* Miscellaneous tables */
+
+static struct acpi_exdump_info acpi_ex_dump_common[5] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_common), NULL},
+ {ACPI_EXD_TYPE, 0, NULL},
+ {ACPI_EXD_UINT16, ACPI_EXD_OFFSET(common.reference_count),
+ "Reference Count"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(common.flags), "Flags"},
+ {ACPI_EXD_LIST, ACPI_EXD_OFFSET(common.next_object), "Object List"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_field_common[7] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_field_common), NULL},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(common_field.field_flags),
+ "Field Flags"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(common_field.access_byte_width),
+ "Access Byte Width"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(common_field.bit_length),
+ "Bit Length"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(common_field.start_field_bit_offset),
+ "Field Bit Offset"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(common_field.base_byte_offset),
+ "Base Byte Offset"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET(common_field.node), "Parent Node"}
+};
+
+static struct acpi_exdump_info acpi_ex_dump_node[7] = {
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_node), NULL},
+ {ACPI_EXD_UINT16, ACPI_EXD_NSOFFSET(flags), "Flags"},
+ {ACPI_EXD_UINT16, ACPI_EXD_NSOFFSET(owner_id), "Owner Id"},
+ {ACPI_EXD_LIST, ACPI_EXD_NSOFFSET(object), "Object List"},
+ {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET(parent), "Parent"},
+ {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET(child), "Child"},
+ {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET(peer), "Peer"}
+};
+
+/* Dispatch table, indexed by object type */
+
+static struct acpi_exdump_info *acpi_ex_dump_info[] = {
+ NULL,
+ acpi_ex_dump_integer,
+ acpi_ex_dump_string,
+ acpi_ex_dump_buffer,
+ acpi_ex_dump_package,
+ NULL,
+ acpi_ex_dump_device,
+ acpi_ex_dump_event,
+ acpi_ex_dump_method,
+ acpi_ex_dump_mutex,
+ acpi_ex_dump_region,
+ acpi_ex_dump_power,
+ acpi_ex_dump_processor,
+ acpi_ex_dump_thermal,
+ acpi_ex_dump_buffer_field,
+ NULL,
+ NULL,
+ acpi_ex_dump_region_field,
+ acpi_ex_dump_bank_field,
+ acpi_ex_dump_index_field,
+ acpi_ex_dump_reference,
+ NULL,
+ NULL,
+ acpi_ex_dump_notify,
+ acpi_ex_dump_address_handler,
+ NULL,
+ NULL,
+ NULL,
+ acpi_ex_dump_extra,
+ acpi_ex_dump_data
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_dump_object
+ *
+ * PARAMETERS: obj_desc - Descriptor to dump
+ * info - Info table corresponding to this object
+ * type
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Walk the info table for this object
+ *
+ ******************************************************************************/
+
+static void
+acpi_ex_dump_object(union acpi_operand_object *obj_desc,
+ struct acpi_exdump_info *info)
+{
+ u8 *target;
+ const char *name;
+ u8 count;
+ union acpi_operand_object *start;
+ union acpi_operand_object *data = NULL;
+ union acpi_operand_object *next;
+ struct acpi_namespace_node *node;
+
+ if (!info) {
+ acpi_os_printf
+ ("ExDumpObject: Display not implemented for object type %s\n",
+ acpi_ut_get_object_type_name(obj_desc));
+ return;
+ }
+
+ /* First table entry must contain the table length (# of table entries) */
+
+ count = info->offset;
+
+ while (count) {
+ if (!obj_desc) {
+ return;
+ }
+
+ target = ACPI_ADD_PTR(u8, obj_desc, info->offset);
+ name = info->name;
+
+ switch (info->opcode) {
+ case ACPI_EXD_INIT:
+
+ break;
+
+ case ACPI_EXD_TYPE:
+
+ acpi_os_printf("%20s : %2.2X [%s]\n", "Type",
+ obj_desc->common.type,
+ acpi_ut_get_object_type_name(obj_desc));
+ break;
+
+ case ACPI_EXD_UINT8:
+
+ acpi_os_printf("%20s : %2.2X\n", name, *target);
+ break;
+
+ case ACPI_EXD_UINT16:
+
+ acpi_os_printf("%20s : %4.4X\n", name,
+ ACPI_GET16(target));
+ break;
+
+ case ACPI_EXD_UINT32:
+
+ acpi_os_printf("%20s : %8.8X\n", name,
+ ACPI_GET32(target));
+ break;
+
+ case ACPI_EXD_UINT64:
+
+ acpi_os_printf("%20s : %8.8X%8.8X\n", "Value",
+ ACPI_FORMAT_UINT64(ACPI_GET64(target)));
+ break;
+
+ case ACPI_EXD_POINTER:
+ case ACPI_EXD_ADDRESS:
+
+ acpi_ex_out_pointer(name,
+ *ACPI_CAST_PTR(void *, target));
+ break;
+
+ case ACPI_EXD_STRING:
+
+ acpi_ut_print_string(obj_desc->string.pointer,
+ ACPI_UINT8_MAX);
+ acpi_os_printf("\n");
+ break;
+
+ case ACPI_EXD_BUFFER:
+
+ ACPI_DUMP_BUFFER(obj_desc->buffer.pointer,
+ obj_desc->buffer.length);
+ break;
+
+ case ACPI_EXD_PACKAGE:
+
+ /* Dump the package contents */
+
+ acpi_os_printf("\nPackage Contents:\n");
+ acpi_ex_dump_package_obj(obj_desc, 0, 0);
+ break;
+
+ case ACPI_EXD_FIELD:
+
+ acpi_ex_dump_object(obj_desc,
+ acpi_ex_dump_field_common);
+ break;
+
+ case ACPI_EXD_REFERENCE:
+
+ acpi_ex_out_string("Class Name",
+ acpi_ut_get_reference_name
+ (obj_desc));
+ acpi_ex_dump_reference_obj(obj_desc);
+ break;
+
+ case ACPI_EXD_LIST:
+
+ start = *ACPI_CAST_PTR(void *, target);
+ next = start;
+
+ acpi_os_printf("%20s : %p ", name, next);
+ if (next) {
+ acpi_os_printf("%s (Type %2.2X)",
+ acpi_ut_get_object_type_name
+ (next), next->common.type);
+
+ while (next->common.next_object) {
+ if ((next->common.type ==
+ ACPI_TYPE_LOCAL_DATA) && !data) {
+ data = next;
+ }
+
+ next = next->common.next_object;
+ acpi_os_printf("->%p(%s %2.2X)", next,
+ acpi_ut_get_object_type_name
+ (next),
+ next->common.type);
+
+ if ((next == start) || (next == data)) {
+ acpi_os_printf
+ ("\n**** Error: Object list appears to be circular linked");
+ break;
+ }
+ }
+ } else {
+ acpi_os_printf("- No attached objects");
+ }
+
+ acpi_os_printf("\n");
+ break;
+
+ case ACPI_EXD_HDLR_LIST:
+
+ start = *ACPI_CAST_PTR(void *, target);
+ next = start;
+
+ acpi_os_printf("%20s : %p", name, next);
+ if (next) {
+ acpi_os_printf("(%s %2.2X)",
+ acpi_ut_get_object_type_name
+ (next),
+ next->address_space.space_id);
+
+ while (next->address_space.next) {
+ if ((next->common.type ==
+ ACPI_TYPE_LOCAL_DATA) && !data) {
+ data = next;
+ }
+
+ next = next->address_space.next;
+ acpi_os_printf("->%p(%s %2.2X)", next,
+ acpi_ut_get_object_type_name
+ (next),
+ next->address_space.
+ space_id);
+
+ if ((next == start) || (next == data)) {
+ acpi_os_printf
+ ("\n**** Error: Handler list appears to be circular linked");
+ break;
+ }
+ }
+ }
+
+ acpi_os_printf("\n");
+ break;
+
+ case ACPI_EXD_RGN_LIST:
+
+ start = *ACPI_CAST_PTR(void *, target);
+ next = start;
+
+ acpi_os_printf("%20s : %p", name, next);
+ if (next) {
+ acpi_os_printf("(%s %2.2X)",
+ acpi_ut_get_object_type_name
+ (next), next->common.type);
+
+ while (next->region.next) {
+ if ((next->common.type ==
+ ACPI_TYPE_LOCAL_DATA) && !data) {
+ data = next;
+ }
+
+ next = next->region.next;
+ acpi_os_printf("->%p(%s %2.2X)", next,
+ acpi_ut_get_object_type_name
+ (next),
+ next->common.type);
+
+ if ((next == start) || (next == data)) {
+ acpi_os_printf
+ ("\n**** Error: Region list appears to be circular linked");
+ break;
+ }
+ }
+ }
+
+ acpi_os_printf("\n");
+ break;
+
+ case ACPI_EXD_NODE:
+
+ node =
+ *ACPI_CAST_PTR(struct acpi_namespace_node *,
+ target);
+
+ acpi_os_printf("%20s : %p", name, node);
+ if (node) {
+ acpi_os_printf(" [%4.4s]", node->name.ascii);
+ }
+ acpi_os_printf("\n");
+ break;
+
+ default:
+
+ acpi_os_printf("**** Invalid table opcode [%X] ****\n",
+ info->opcode);
+ return;
+ }
+
+ info++;
+ count--;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_dump_operand
+ *
+ * PARAMETERS: *obj_desc - Pointer to entry to be dumped
+ * depth - Current nesting depth
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump an operand object
+ *
+ ******************************************************************************/
+
+void acpi_ex_dump_operand(union acpi_operand_object *obj_desc, u32 depth)
+{
+ u32 length;
+ u32 index;
+
+ ACPI_FUNCTION_NAME(ex_dump_operand);
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_EXEC, _COMPONENT)) {
+ return;
+ }
+
+ if (!obj_desc) {
+
+ /* This could be a null element of a package */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Null Object Descriptor\n"));
+ return;
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == ACPI_DESC_TYPE_NAMED) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%p Namespace Node: ",
+ obj_desc));
+ ACPI_DUMP_ENTRY(obj_desc, ACPI_LV_EXEC);
+ return;
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "%p is not a node or operand object: [%s]\n",
+ obj_desc,
+ acpi_ut_get_descriptor_name(obj_desc)));
+ ACPI_DUMP_BUFFER(obj_desc, sizeof(union acpi_operand_object));
+ return;
+ }
+
+ /* obj_desc is a valid object */
+
+ if (depth > 0) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%*s[%u] %p Refs=%u ",
+ depth, " ", depth, obj_desc,
+ obj_desc->common.reference_count));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%p Refs=%u ",
+ obj_desc, obj_desc->common.reference_count));
+ }
+
+ /* Decode object type */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ acpi_os_printf("Reference: [%s] ",
+ acpi_ut_get_reference_name(obj_desc));
+
+ switch (obj_desc->reference.class) {
+ case ACPI_REFCLASS_DEBUG:
+
+ acpi_os_printf("\n");
+ break;
+
+ case ACPI_REFCLASS_INDEX:
+
+ acpi_os_printf("%p\n", obj_desc->reference.object);
+ break;
+
+ case ACPI_REFCLASS_TABLE:
+
+ acpi_os_printf("Table Index %X\n",
+ obj_desc->reference.value);
+ break;
+
+ case ACPI_REFCLASS_REFOF:
+
+ acpi_os_printf("%p [%s]\n", obj_desc->reference.object,
+ acpi_ut_get_type_name(((union
+ acpi_operand_object
+ *)
+ obj_desc->
+ reference.
+ object)->common.
+ type));
+ break;
+
+ case ACPI_REFCLASS_NAME:
+
+ acpi_ut_repair_name(obj_desc->reference.node->name.
+ ascii);
+ acpi_os_printf("- [%4.4s] (Node %p)\n",
+ obj_desc->reference.node->name.ascii,
+ obj_desc->reference.node);
+ break;
+
+ case ACPI_REFCLASS_ARG:
+ case ACPI_REFCLASS_LOCAL:
+
+ acpi_os_printf("%X\n", obj_desc->reference.value);
+ break;
+
+ default: /* Unknown reference class */
+
+ acpi_os_printf("%2.2X\n", obj_desc->reference.class);
+ break;
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ acpi_os_printf("Buffer length %.2X @ %p\n",
+ obj_desc->buffer.length,
+ obj_desc->buffer.pointer);
+
+ /* Debug only -- dump the buffer contents */
+
+ if (obj_desc->buffer.pointer) {
+ length = obj_desc->buffer.length;
+ if (length > 128) {
+ length = 128;
+ }
+
+ acpi_os_printf
+ ("Buffer Contents: (displaying length 0x%.2X)\n",
+ length);
+ ACPI_DUMP_BUFFER(obj_desc->buffer.pointer, length);
+ }
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ acpi_os_printf("Integer %8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64(obj_desc->integer.value));
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ acpi_os_printf("Package [Len %X] ElementArray %p\n",
+ obj_desc->package.count,
+ obj_desc->package.elements);
+
+ /*
+ * If elements exist, package element pointer is valid,
+ * and debug_level exceeds 1, dump package's elements.
+ */
+ if (obj_desc->package.count &&
+ obj_desc->package.elements && acpi_dbg_level > 1) {
+ for (index = 0; index < obj_desc->package.count;
+ index++) {
+ acpi_ex_dump_operand(obj_desc->package.
+ elements[index],
+ depth + 1);
+ }
+ }
+ break;
+
+ case ACPI_TYPE_REGION:
+
+ acpi_os_printf("Region %s (%X)",
+ acpi_ut_get_region_name(obj_desc->region.
+ space_id),
+ obj_desc->region.space_id);
+
+ /*
+ * If the address and length have not been evaluated,
+ * don't print them.
+ */
+ if (!(obj_desc->region.flags & AOPOBJ_DATA_VALID)) {
+ acpi_os_printf("\n");
+ } else {
+ acpi_os_printf(" base %8.8X%8.8X Length %X\n",
+ ACPI_FORMAT_UINT64(obj_desc->region.
+ address),
+ obj_desc->region.length);
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ acpi_os_printf("String length %X @ %p ",
+ obj_desc->string.length,
+ obj_desc->string.pointer);
+
+ acpi_ut_print_string(obj_desc->string.pointer, ACPI_UINT8_MAX);
+ acpi_os_printf("\n");
+ break;
+
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+
+ acpi_os_printf("BankField\n");
+ break;
+
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+
+ acpi_os_printf
+ ("RegionField: Bits=%X AccWidth=%X Lock=%X Update=%X at "
+ "byte=%X bit=%X of below:\n", obj_desc->field.bit_length,
+ obj_desc->field.access_byte_width,
+ obj_desc->field.field_flags & AML_FIELD_LOCK_RULE_MASK,
+ obj_desc->field.field_flags & AML_FIELD_UPDATE_RULE_MASK,
+ obj_desc->field.base_byte_offset,
+ obj_desc->field.start_field_bit_offset);
+
+ acpi_ex_dump_operand(obj_desc->field.region_obj, depth + 1);
+ break;
+
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
+ acpi_os_printf("IndexField\n");
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+
+ acpi_os_printf("BufferField: %X bits at byte %X bit %X of\n",
+ obj_desc->buffer_field.bit_length,
+ obj_desc->buffer_field.base_byte_offset,
+ obj_desc->buffer_field.start_field_bit_offset);
+
+ if (!obj_desc->buffer_field.buffer_obj) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "*NULL*\n"));
+ } else if ((obj_desc->buffer_field.buffer_obj)->common.type !=
+ ACPI_TYPE_BUFFER) {
+ acpi_os_printf("*not a Buffer*\n");
+ } else {
+ acpi_ex_dump_operand(obj_desc->buffer_field.buffer_obj,
+ depth + 1);
+ }
+ break;
+
+ case ACPI_TYPE_EVENT:
+
+ acpi_os_printf("Event\n");
+ break;
+
+ case ACPI_TYPE_METHOD:
+
+ acpi_os_printf("Method(%X) @ %p:%X\n",
+ obj_desc->method.param_count,
+ obj_desc->method.aml_start,
+ obj_desc->method.aml_length);
+ break;
+
+ case ACPI_TYPE_MUTEX:
+
+ acpi_os_printf("Mutex\n");
+ break;
+
+ case ACPI_TYPE_DEVICE:
+
+ acpi_os_printf("Device\n");
+ break;
+
+ case ACPI_TYPE_POWER:
+
+ acpi_os_printf("Power\n");
+ break;
+
+ case ACPI_TYPE_PROCESSOR:
+
+ acpi_os_printf("Processor\n");
+ break;
+
+ case ACPI_TYPE_THERMAL:
+
+ acpi_os_printf("Thermal\n");
+ break;
+
+ default:
+
+ /* Unknown Type */
+
+ acpi_os_printf("Unknown Type %X\n", obj_desc->common.type);
+ break;
+ }
+
+ return;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_dump_operands
+ *
+ * PARAMETERS: operands - A list of Operand objects
+ * opcode_name - AML opcode name
+ * num_operands - Operand count for this opcode
+ *
+ * DESCRIPTION: Dump the operands associated with the opcode
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_dump_operands(union acpi_operand_object **operands,
+ const char *opcode_name, u32 num_operands)
+{
+ ACPI_FUNCTION_TRACE(ex_dump_operands);
+
+ if (!opcode_name) {
+ opcode_name = "UNKNOWN";
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "**** Start operand dump for opcode [%s], %u operands\n",
+ opcode_name, num_operands));
+
+ if (num_operands == 0) {
+ num_operands = 1;
+ }
+
+ /* Dump the individual operands */
+
+ while (num_operands) {
+ acpi_ex_dump_operand(*operands, 0);
+ operands++;
+ num_operands--;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "**** End operand dump for [%s]\n", opcode_name));
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_out* functions
+ *
+ * PARAMETERS: title - Descriptive text
+ * value - Value to be displayed
+ *
+ * DESCRIPTION: Object dump output formatting functions. These functions
+ * reduce the number of format strings required and keeps them
+ * all in one place for easy modification.
+ *
+ ******************************************************************************/
+
+static void acpi_ex_out_string(const char *title, const char *value)
+{
+ acpi_os_printf("%20s : %s\n", title, value);
+}
+
+static void acpi_ex_out_pointer(const char *title, const void *value)
+{
+ acpi_os_printf("%20s : %p\n", title, value);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_dump_namespace_node
+ *
+ * PARAMETERS: node - Descriptor to dump
+ * flags - Force display if TRUE
+ *
+ * DESCRIPTION: Dumps the members of the given.Node
+ *
+ ******************************************************************************/
+
+void acpi_ex_dump_namespace_node(struct acpi_namespace_node *node, u32 flags)
+{
+
+ ACPI_FUNCTION_ENTRY();
+
+ if (!flags) {
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_OBJECTS, _COMPONENT)) {
+ return;
+ }
+ }
+
+ acpi_os_printf("%20s : %4.4s\n", "Name", acpi_ut_get_node_name(node));
+ acpi_os_printf("%20s : %2.2X [%s]\n", "Type",
+ node->type, acpi_ut_get_type_name(node->type));
+
+ acpi_ex_dump_object(ACPI_CAST_PTR(union acpi_operand_object, node),
+ acpi_ex_dump_node);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_dump_reference_obj
+ *
+ * PARAMETERS: object - Descriptor to dump
+ *
+ * DESCRIPTION: Dumps a reference object
+ *
+ ******************************************************************************/
+
+static void acpi_ex_dump_reference_obj(union acpi_operand_object *obj_desc)
+{
+ struct acpi_buffer ret_buf;
+ acpi_status status;
+
+ ret_buf.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+
+ if (obj_desc->reference.class == ACPI_REFCLASS_NAME) {
+ acpi_os_printf(" %p ", obj_desc->reference.node);
+
+ status = acpi_ns_handle_to_pathname(obj_desc->reference.node,
+ &ret_buf, TRUE);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf
+ (" Could not convert name to pathname: %s\n",
+ acpi_format_exception(status));
+ } else {
+ acpi_os_printf("%s: %s\n",
+ acpi_ut_get_type_name(obj_desc->
+ reference.node->
+ type),
+ (char *)ret_buf.pointer);
+ ACPI_FREE(ret_buf.pointer);
+ }
+ } else if (obj_desc->reference.object) {
+ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) ==
+ ACPI_DESC_TYPE_OPERAND) {
+ acpi_os_printf("%22s %p", "Target :",
+ obj_desc->reference.object);
+ if (obj_desc->reference.class == ACPI_REFCLASS_TABLE) {
+ acpi_os_printf(" Table Index: %X\n",
+ obj_desc->reference.value);
+ } else {
+ acpi_os_printf(" [%s]\n",
+ acpi_ut_get_type_name(((union
+ acpi_operand_object
+ *)
+ obj_desc->
+ reference.
+ object)->
+ common.
+ type));
+ }
+ } else {
+ acpi_os_printf(" Target: %p\n",
+ obj_desc->reference.object);
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_dump_package_obj
+ *
+ * PARAMETERS: obj_desc - Descriptor to dump
+ * level - Indentation Level
+ * index - Package index for this object
+ *
+ * DESCRIPTION: Dumps the elements of the package
+ *
+ ******************************************************************************/
+
+static void
+acpi_ex_dump_package_obj(union acpi_operand_object *obj_desc,
+ u32 level, u32 index)
+{
+ u32 i;
+
+ /* Indentation and index output */
+
+ if (level > 0) {
+ for (i = 0; i < level; i++) {
+ acpi_os_printf(" ");
+ }
+
+ acpi_os_printf("[%.2d] ", index);
+ }
+
+ acpi_os_printf("%p ", obj_desc);
+
+ /* Null package elements are allowed */
+
+ if (!obj_desc) {
+ acpi_os_printf("[Null Object]\n");
+ return;
+ }
+
+ /* Packages may only contain a few object types */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ acpi_os_printf("[Integer] = %8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64(obj_desc->integer.value));
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ acpi_os_printf("[String] Value: ");
+ acpi_ut_print_string(obj_desc->string.pointer, ACPI_UINT8_MAX);
+ acpi_os_printf("\n");
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ acpi_os_printf("[Buffer] Length %.2X = ",
+ obj_desc->buffer.length);
+ if (obj_desc->buffer.length) {
+ acpi_ut_debug_dump_buffer(ACPI_CAST_PTR
+ (u8,
+ obj_desc->buffer.pointer),
+ obj_desc->buffer.length,
+ DB_DWORD_DISPLAY, _COMPONENT);
+ } else {
+ acpi_os_printf("\n");
+ }
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ acpi_os_printf("[Package] Contains %u Elements:\n",
+ obj_desc->package.count);
+
+ for (i = 0; i < obj_desc->package.count; i++) {
+ acpi_ex_dump_package_obj(obj_desc->package.elements[i],
+ level + 1, i);
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ acpi_os_printf("[Object Reference] Class [%s]",
+ acpi_ut_get_reference_name(obj_desc));
+ acpi_ex_dump_reference_obj(obj_desc);
+ break;
+
+ default:
+
+ acpi_os_printf("[%s] Type: %2.2X\n",
+ acpi_ut_get_type_name(obj_desc->common.type),
+ obj_desc->common.type);
+ break;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_dump_object_descriptor
+ *
+ * PARAMETERS: obj_desc - Descriptor to dump
+ * flags - Force display if TRUE
+ *
+ * DESCRIPTION: Dumps the members of the object descriptor given.
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_dump_object_descriptor(union acpi_operand_object *obj_desc, u32 flags)
+{
+ ACPI_FUNCTION_TRACE(ex_dump_object_descriptor);
+
+ if (!obj_desc) {
+ return_VOID;
+ }
+
+ if (!flags) {
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_OBJECTS, _COMPONENT)) {
+ return_VOID;
+ }
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == ACPI_DESC_TYPE_NAMED) {
+ acpi_ex_dump_namespace_node((struct acpi_namespace_node *)
+ obj_desc, flags);
+
+ obj_desc = ((struct acpi_namespace_node *)obj_desc)->object;
+ if (!obj_desc) {
+ return_VOID;
+ }
+
+ acpi_os_printf("\nAttached Object %p", obj_desc);
+ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == ACPI_DESC_TYPE_NAMED) {
+ acpi_os_printf(" - Namespace Node");
+ }
+
+ acpi_os_printf(":\n");
+ goto dump_object;
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) {
+ acpi_os_printf("%p is not an ACPI operand object: [%s]\n",
+ obj_desc, acpi_ut_get_descriptor_name(obj_desc));
+ return_VOID;
+ }
+
+ /* Validate the object type */
+
+ if (obj_desc->common.type > ACPI_TYPE_LOCAL_MAX) {
+ acpi_os_printf("Not a known object type: %2.2X\n",
+ obj_desc->common.type);
+ return_VOID;
+ }
+
+dump_object:
+
+ if (!obj_desc) {
+ return_VOID;
+ }
+
+ /* Common Fields */
+
+ acpi_ex_dump_object(obj_desc, acpi_ex_dump_common);
+
+ /* Object-specific fields */
+
+ acpi_ex_dump_object(obj_desc, acpi_ex_dump_info[obj_desc->common.type]);
+
+ if (obj_desc->common.type == ACPI_TYPE_REGION) {
+ obj_desc = obj_desc->common.next_object;
+ if (obj_desc->common.type > ACPI_TYPE_LOCAL_MAX) {
+ acpi_os_printf
+ ("Secondary object is not a known object type: %2.2X\n",
+ obj_desc->common.type);
+
+ return_VOID;
+ }
+
+ acpi_os_printf("\nExtra attached Object (%p):\n", obj_desc);
+ acpi_ex_dump_object(obj_desc,
+ acpi_ex_dump_info[obj_desc->common.type]);
+ }
+
+ return_VOID;
+}
+
+#endif
diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c
new file mode 100644
index 000000000..2b89a496d
--- /dev/null
+++ b/drivers/acpi/acpica/exfield.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exfield - AML execution - field_unit read/write
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exfield")
+
+/*
+ * This table maps the various Attrib protocols to the byte transfer
+ * length. Used for the generic serial bus.
+ */
+#define ACPI_INVALID_PROTOCOL_ID 0x80
+#define ACPI_MAX_PROTOCOL_ID 0x0F
+static const u8 acpi_protocol_lengths[] = {
+ ACPI_INVALID_PROTOCOL_ID, /* 0 - reserved */
+ ACPI_INVALID_PROTOCOL_ID, /* 1 - reserved */
+ 0x00, /* 2 - ATTRIB_QUICK */
+ ACPI_INVALID_PROTOCOL_ID, /* 3 - reserved */
+ 0x01, /* 4 - ATTRIB_SEND_RECEIVE */
+ ACPI_INVALID_PROTOCOL_ID, /* 5 - reserved */
+ 0x01, /* 6 - ATTRIB_BYTE */
+ ACPI_INVALID_PROTOCOL_ID, /* 7 - reserved */
+ 0x02, /* 8 - ATTRIB_WORD */
+ ACPI_INVALID_PROTOCOL_ID, /* 9 - reserved */
+ 0xFF, /* A - ATTRIB_BLOCK */
+ 0xFF, /* B - ATTRIB_BYTES */
+ 0x02, /* C - ATTRIB_PROCESS_CALL */
+ 0xFF, /* D - ATTRIB_BLOCK_PROCESS_CALL */
+ 0xFF, /* E - ATTRIB_RAW_BYTES */
+ 0xFF /* F - ATTRIB_RAW_PROCESS_BYTES */
+};
+
+#define PCC_MASTER_SUBSPACE 3
+
+/*
+ * The following macros determine a given offset is a COMD field.
+ * According to the specification, generic subspaces (types 0-2) contains a
+ * 2-byte COMD field at offset 4 and master subspaces (type 3) contains a 4-byte
+ * COMD field starting at offset 12.
+ */
+#define GENERIC_SUBSPACE_COMMAND(a) (4 == a || a == 5)
+#define MASTER_SUBSPACE_COMMAND(a) (12 <= a && a <= 15)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_get_protocol_buffer_length
+ *
+ * PARAMETERS: protocol_id - The type of the protocol indicated by region
+ * field access attributes
+ * return_length - Where the protocol byte transfer length is
+ * returned
+ *
+ * RETURN: Status and decoded byte transfer length
+ *
+ * DESCRIPTION: This routine returns the length of the generic_serial_bus
+ * protocol bytes
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_get_protocol_buffer_length(u32 protocol_id, u32 *return_length)
+{
+
+ if ((protocol_id > ACPI_MAX_PROTOCOL_ID) ||
+ (acpi_protocol_lengths[protocol_id] == ACPI_INVALID_PROTOCOL_ID)) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid Field/AccessAs protocol ID: 0x%4.4X",
+ protocol_id));
+
+ return (AE_AML_PROTOCOL);
+ }
+
+ *return_length = acpi_protocol_lengths[protocol_id];
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_read_data_from_field
+ *
+ * PARAMETERS: walk_state - Current execution state
+ * obj_desc - The named field
+ * ret_buffer_desc - Where the return data object is stored
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read from a named field. Returns either an Integer or a
+ * Buffer, depending on the size of the field and whether if a
+ * field is created by the create_field() operator.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
+ union acpi_operand_object *obj_desc,
+ union acpi_operand_object **ret_buffer_desc)
+{
+ acpi_status status;
+ union acpi_operand_object *buffer_desc;
+ void *buffer;
+ u32 buffer_length;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc);
+
+ /* Parameter validation */
+
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_AML_NO_OPERAND);
+ }
+ if (!ret_buffer_desc) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) {
+ /*
+ * If the buffer_field arguments have not been previously evaluated,
+ * evaluate them now and save the results.
+ */
+ if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
+ status = acpi_ds_get_buffer_field_arguments(obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+ } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
+ (obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_SMBUS
+ || obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_GSBUS
+ || 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)) {
+
+ /* SMBus, GSBus, IPMI serial */
+
+ status = acpi_ex_read_serial_bus(obj_desc, ret_buffer_desc);
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Allocate a buffer for the contents of the field.
+ *
+ * If the field is larger than the current integer width, create
+ * a BUFFER to hold it. Otherwise, use an INTEGER. This allows
+ * the use of arithmetic operators on the returned value if the
+ * field size is equal or smaller than an Integer.
+ *
+ * However, all buffer fields created by create_field operator needs to
+ * remain as a buffer to match other AML interpreter implementations.
+ *
+ * Note: Field.length is in bits.
+ */
+ buffer_length =
+ (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length);
+
+ if (buffer_length > acpi_gbl_integer_byte_width ||
+ (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD &&
+ obj_desc->buffer_field.is_create_field)) {
+
+ /* Field is too large for an Integer, create a Buffer instead */
+
+ buffer_desc = acpi_ut_create_buffer_object(buffer_length);
+ if (!buffer_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+ buffer = buffer_desc->buffer.pointer;
+ } else {
+ /* Field will fit within an Integer (normal case) */
+
+ buffer_desc = acpi_ut_create_integer_object((u64) 0);
+ if (!buffer_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ buffer_length = acpi_gbl_integer_byte_width;
+ buffer = &buffer_desc->integer.value;
+ }
+
+ if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
+ (obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_GPIO)) {
+
+ /* General Purpose I/O */
+
+ status = acpi_ex_read_gpio(obj_desc, buffer);
+ goto exit;
+ } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
+ (obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_PLATFORM_COMM)) {
+ /*
+ * Reading from a PCC field unit does not require the handler because
+ * it only requires reading from the internal_pcc_buffer.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "PCC FieldRead bits %u\n",
+ obj_desc->field.bit_length));
+
+ memcpy(buffer,
+ obj_desc->field.region_obj->field.internal_pcc_buffer +
+ obj_desc->field.base_byte_offset,
+ (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.
+ bit_length));
+
+ *ret_buffer_desc = buffer_desc;
+ return AE_OK;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "FieldRead [TO]: Obj %p, Type %X, Buf %p, ByteLen %X\n",
+ obj_desc, obj_desc->common.type, buffer,
+ buffer_length));
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "FieldRead [FROM]: BitLen %X, BitOff %X, ByteOff %X\n",
+ obj_desc->common_field.bit_length,
+ obj_desc->common_field.start_field_bit_offset,
+ obj_desc->common_field.base_byte_offset));
+
+ /* Lock entire transaction if requested */
+
+ acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
+
+ /* Read from the field */
+
+ status = acpi_ex_extract_from_field(obj_desc, buffer, buffer_length);
+ acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
+
+exit:
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(buffer_desc);
+ } else {
+ *ret_buffer_desc = buffer_desc;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_write_data_to_field
+ *
+ * PARAMETERS: source_desc - Contains data to write
+ * obj_desc - The named field
+ * result_desc - Where the return value is returned, if any
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write to a named field
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
+ union acpi_operand_object *obj_desc,
+ union acpi_operand_object **result_desc)
+{
+ acpi_status status;
+ u32 buffer_length;
+ u32 data_length;
+ void *buffer;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_write_data_to_field, obj_desc);
+
+ /* Parameter validation */
+
+ if (!source_desc || !obj_desc) {
+ return_ACPI_STATUS(AE_AML_NO_OPERAND);
+ }
+
+ if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) {
+ /*
+ * If the buffer_field arguments have not been previously evaluated,
+ * evaluate them now and save the results.
+ */
+ if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
+ status = acpi_ds_get_buffer_field_arguments(obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+ } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
+ (obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_GPIO)) {
+
+ /* General Purpose I/O */
+
+ status = acpi_ex_write_gpio(source_desc, obj_desc, result_desc);
+ return_ACPI_STATUS(status);
+ } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
+ (obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_SMBUS
+ || obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_GSBUS
+ || 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)) {
+
+ /* SMBus, GSBus, IPMI serial */
+
+ status =
+ acpi_ex_write_serial_bus(source_desc, obj_desc,
+ result_desc);
+ return_ACPI_STATUS(status);
+ } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
+ (obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_PLATFORM_COMM)) {
+ /*
+ * According to the spec a write to the COMD field will invoke the
+ * region handler. Otherwise, write to the pcc_internal buffer. This
+ * implementation will use the offsets specified rather than the name
+ * of the field. This is considered safer because some firmware tools
+ * are known to obfiscate named objects.
+ */
+ data_length =
+ (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.
+ bit_length);
+ memcpy(obj_desc->field.region_obj->field.internal_pcc_buffer +
+ obj_desc->field.base_byte_offset,
+ source_desc->buffer.pointer, data_length);
+
+ if (MASTER_SUBSPACE_COMMAND(obj_desc->field.base_byte_offset)) {
+
+ /* Perform the write */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "PCC COMD field has been written. Invoking PCC handler now.\n"));
+
+ status =
+ acpi_ex_access_region(obj_desc, 0,
+ (u64 *)obj_desc->field.
+ region_obj->field.
+ internal_pcc_buffer,
+ ACPI_WRITE);
+ return_ACPI_STATUS(status);
+ }
+ return (AE_OK);
+ }
+
+ /* Get a pointer to the data to be written */
+
+ switch (source_desc->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ buffer = &source_desc->integer.value;
+ buffer_length = sizeof(source_desc->integer.value);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ buffer = source_desc->buffer.pointer;
+ buffer_length = source_desc->buffer.length;
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ buffer = source_desc->string.pointer;
+ buffer_length = source_desc->string.length;
+ break;
+
+ default:
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "FieldWrite [FROM]: Obj %p (%s:%X), Buf %p, ByteLen %X\n",
+ source_desc,
+ acpi_ut_get_type_name(source_desc->common.type),
+ source_desc->common.type, buffer, buffer_length));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "FieldWrite [TO]: Obj %p (%s:%X), BitLen %X, BitOff %X, ByteOff %X\n",
+ obj_desc,
+ acpi_ut_get_type_name(obj_desc->common.type),
+ obj_desc->common.type,
+ obj_desc->common_field.bit_length,
+ obj_desc->common_field.start_field_bit_offset,
+ obj_desc->common_field.base_byte_offset));
+
+ /* Lock entire transaction if requested */
+
+ acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
+
+ /* Write to the field */
+
+ status = acpi_ex_insert_into_field(obj_desc, buffer, buffer_length);
+ acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c
new file mode 100644
index 000000000..d769cea14
--- /dev/null
+++ b/drivers/acpi/acpica/exfldio.c
@@ -0,0 +1,967 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exfldio - Aml Field I/O
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlcode.h"
+#include "acevents.h"
+#include "acdispat.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exfldio")
+
+/* Local prototypes */
+static acpi_status
+acpi_ex_field_datum_io(union acpi_operand_object *obj_desc,
+ u32 field_datum_byte_offset, u64 *value, u32 read_write);
+
+static u8
+acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value);
+
+static acpi_status
+acpi_ex_setup_region(union acpi_operand_object *obj_desc,
+ u32 field_datum_byte_offset);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_setup_region
+ *
+ * PARAMETERS: obj_desc - Field to be read or written
+ * field_datum_byte_offset - Byte offset of this datum within the
+ * parent field
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Common processing for acpi_ex_extract_from_field and
+ * acpi_ex_insert_into_field. Initialize the Region if necessary and
+ * validate the request.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_setup_region(union acpi_operand_object *obj_desc,
+ u32 field_datum_byte_offset)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *rgn_desc;
+ u8 space_id;
+
+ ACPI_FUNCTION_TRACE_U32(ex_setup_region, field_datum_byte_offset);
+
+ rgn_desc = obj_desc->common_field.region_obj;
+
+ /* We must have a valid region */
+
+ if (rgn_desc->common.type != ACPI_TYPE_REGION) {
+ ACPI_ERROR((AE_INFO, "Needed Region, found type 0x%X (%s)",
+ rgn_desc->common.type,
+ acpi_ut_get_object_type_name(rgn_desc)));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ space_id = rgn_desc->region.space_id;
+
+ /* Validate the Space ID */
+
+ if (!acpi_is_valid_space_id(space_id)) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid/unknown Address Space ID: 0x%2.2X",
+ space_id));
+ return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
+ }
+
+ /*
+ * If the Region Address and Length have not been previously evaluated,
+ * evaluate them now and save the results.
+ */
+ if (!(rgn_desc->common.flags & AOPOBJ_DATA_VALID)) {
+ status = acpi_ds_get_region_arguments(rgn_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * Exit now for SMBus, GSBus or IPMI address space, it has a non-linear
+ * address space and the request cannot be directly validated
+ */
+ if (space_id == ACPI_ADR_SPACE_SMBUS ||
+ space_id == ACPI_ADR_SPACE_GSBUS ||
+ space_id == ACPI_ADR_SPACE_IPMI) {
+
+ /* SMBus or IPMI has a non-linear address space */
+
+ return_ACPI_STATUS(AE_OK);
+ }
+#ifdef ACPI_UNDER_DEVELOPMENT
+ /*
+ * If the Field access is any_acc, we can now compute the optimal
+ * access (because we know the length of the parent region)
+ */
+ if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+#endif
+
+ /*
+ * Validate the request. The entire request from the byte offset for a
+ * length of one field datum (access width) must fit within the region.
+ * (Region length is specified in bytes)
+ */
+ if (rgn_desc->region.length <
+ (obj_desc->common_field.base_byte_offset + field_datum_byte_offset +
+ obj_desc->common_field.access_byte_width)) {
+ if (acpi_gbl_enable_interpreter_slack) {
+ /*
+ * Slack mode only: We will go ahead and allow access to this
+ * field if it is within the region length rounded up to the next
+ * access width boundary. acpi_size cast for 64-bit compile.
+ */
+ if (ACPI_ROUND_UP(rgn_desc->region.length,
+ obj_desc->common_field.
+ access_byte_width) >=
+ ((acpi_size)obj_desc->common_field.
+ base_byte_offset +
+ obj_desc->common_field.access_byte_width +
+ field_datum_byte_offset)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+ }
+
+ if (rgn_desc->region.length <
+ obj_desc->common_field.access_byte_width) {
+ /*
+ * This is the case where the access_type (acc_word, etc.) is wider
+ * than the region itself. For example, a region of length one
+ * byte, and a field with Dword access specified.
+ */
+ ACPI_ERROR((AE_INFO,
+ "Field [%4.4s] access width (%u bytes) "
+ "too large for region [%4.4s] (length %u)",
+ acpi_ut_get_node_name(obj_desc->
+ common_field.node),
+ obj_desc->common_field.access_byte_width,
+ acpi_ut_get_node_name(rgn_desc->region.
+ node),
+ rgn_desc->region.length));
+ }
+
+ /*
+ * Offset rounded up to next multiple of field width
+ * exceeds region length, indicate an error
+ */
+ ACPI_ERROR((AE_INFO,
+ "Field [%4.4s] Base+Offset+Width %u+%u+%u "
+ "is beyond end of region [%4.4s] (length %u)",
+ acpi_ut_get_node_name(obj_desc->common_field.node),
+ obj_desc->common_field.base_byte_offset,
+ field_datum_byte_offset,
+ obj_desc->common_field.access_byte_width,
+ acpi_ut_get_node_name(rgn_desc->region.node),
+ rgn_desc->region.length));
+
+ return_ACPI_STATUS(AE_AML_REGION_LIMIT);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_access_region
+ *
+ * PARAMETERS: obj_desc - Field to be read
+ * field_datum_byte_offset - Byte offset of this datum within the
+ * parent field
+ * value - Where to store value (must at least
+ * 64 bits)
+ * function - Read or Write flag plus other region-
+ * dependent flags
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read or Write a single field datum to an Operation Region.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_access_region(union acpi_operand_object *obj_desc,
+ u32 field_datum_byte_offset, u64 *value, u32 function)
+{
+ acpi_status status;
+ union acpi_operand_object *rgn_desc;
+ u32 region_offset;
+
+ ACPI_FUNCTION_TRACE(ex_access_region);
+
+ /*
+ * Ensure that the region operands are fully evaluated and verify
+ * the validity of the request
+ */
+ status = acpi_ex_setup_region(obj_desc, field_datum_byte_offset);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * The physical address of this field datum is:
+ *
+ * 1) The base of the region, plus
+ * 2) The base offset of the field, plus
+ * 3) The current offset into the field
+ */
+ rgn_desc = obj_desc->common_field.region_obj;
+ region_offset =
+ obj_desc->common_field.base_byte_offset + field_datum_byte_offset;
+
+ if ((function & ACPI_IO_MASK) == ACPI_READ) {
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[READ]"));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[WRITE]"));
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_BFIELD,
+ " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %8.8X%8.8X\n",
+ acpi_ut_get_region_name(rgn_desc->region.
+ space_id),
+ rgn_desc->region.space_id,
+ obj_desc->common_field.access_byte_width,
+ obj_desc->common_field.base_byte_offset,
+ field_datum_byte_offset,
+ ACPI_FORMAT_UINT64(rgn_desc->region.address +
+ region_offset)));
+
+ /* Invoke the appropriate address_space/op_region handler */
+
+ status = acpi_ev_address_space_dispatch(rgn_desc, obj_desc,
+ function, region_offset,
+ ACPI_MUL_8(obj_desc->
+ common_field.
+ access_byte_width),
+ value);
+
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_NOT_IMPLEMENTED) {
+ ACPI_ERROR((AE_INFO,
+ "Region %s (ID=%u) not implemented",
+ acpi_ut_get_region_name(rgn_desc->region.
+ space_id),
+ rgn_desc->region.space_id));
+ } else if (status == AE_NOT_EXIST) {
+ ACPI_ERROR((AE_INFO,
+ "Region %s (ID=%u) has no handler",
+ acpi_ut_get_region_name(rgn_desc->region.
+ space_id),
+ rgn_desc->region.space_id));
+ }
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_register_overflow
+ *
+ * PARAMETERS: obj_desc - Register(Field) to be written
+ * value - Value to be stored
+ *
+ * RETURN: TRUE if value overflows the field, FALSE otherwise
+ *
+ * DESCRIPTION: Check if a value is out of range of the field being written.
+ * Used to check if the values written to Index and Bank registers
+ * are out of range. Normally, the value is simply truncated
+ * to fit the field, but this case is most likely a serious
+ * coding error in the ASL.
+ *
+ ******************************************************************************/
+
+static u8
+acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value)
+{
+
+ if (obj_desc->common_field.bit_length >= ACPI_INTEGER_BIT_SIZE) {
+ /*
+ * The field is large enough to hold the maximum integer, so we can
+ * never overflow it.
+ */
+ return (FALSE);
+ }
+
+ if (value >= ((u64) 1 << obj_desc->common_field.bit_length)) {
+ /*
+ * The Value is larger than the maximum value that can fit into
+ * the register.
+ */
+ ACPI_ERROR((AE_INFO,
+ "Index value 0x%8.8X%8.8X overflows field width 0x%X",
+ ACPI_FORMAT_UINT64(value),
+ obj_desc->common_field.bit_length));
+
+ return (TRUE);
+ }
+
+ /* The Value will fit into the field with no truncation */
+
+ return (FALSE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_field_datum_io
+ *
+ * PARAMETERS: obj_desc - Field to be read
+ * field_datum_byte_offset - Byte offset of this datum within the
+ * parent field
+ * value - Where to store value (must be 64 bits)
+ * read_write - Read or Write flag
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read or Write a single datum of a field. The field_type is
+ * demultiplexed here to handle the different types of fields
+ * (buffer_field, region_field, index_field, bank_field)
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_field_datum_io(union acpi_operand_object *obj_desc,
+ u32 field_datum_byte_offset, u64 *value, u32 read_write)
+{
+ acpi_status status;
+ u64 local_value;
+
+ ACPI_FUNCTION_TRACE_U32(ex_field_datum_io, field_datum_byte_offset);
+
+ if (read_write == ACPI_READ) {
+ if (!value) {
+ local_value = 0;
+
+ /* To support reads without saving return value */
+ value = &local_value;
+ }
+
+ /* Clear the entire return buffer first, [Very Important!] */
+
+ *value = 0;
+ }
+
+ /*
+ * The four types of fields are:
+ *
+ * buffer_field - Read/write from/to a Buffer
+ * region_field - Read/write from/to a Operation Region.
+ * bank_field - Write to a Bank Register, then read/write from/to an
+ * operation_region
+ * index_field - Write to an Index Register, then read/write from/to a
+ * Data Register
+ */
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_BUFFER_FIELD:
+ /*
+ * If the buffer_field arguments have not been previously evaluated,
+ * evaluate them now and save the results.
+ */
+ if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
+ status = acpi_ds_get_buffer_field_arguments(obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ if (read_write == ACPI_READ) {
+ /*
+ * Copy the data from the source buffer.
+ * Length is the field width in bytes.
+ */
+ memcpy(value,
+ (obj_desc->buffer_field.buffer_obj)->buffer.
+ pointer +
+ obj_desc->buffer_field.base_byte_offset +
+ field_datum_byte_offset,
+ obj_desc->common_field.access_byte_width);
+ } else {
+ /*
+ * Copy the data to the target buffer.
+ * Length is the field width in bytes.
+ */
+ memcpy((obj_desc->buffer_field.buffer_obj)->buffer.
+ pointer +
+ obj_desc->buffer_field.base_byte_offset +
+ field_datum_byte_offset, value,
+ obj_desc->common_field.access_byte_width);
+ }
+
+ status = AE_OK;
+ break;
+
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ /*
+ * Ensure that the bank_value is not beyond the capacity of
+ * the register
+ */
+ if (acpi_ex_register_overflow(obj_desc->bank_field.bank_obj,
+ (u64) obj_desc->bank_field.
+ value)) {
+ return_ACPI_STATUS(AE_AML_REGISTER_LIMIT);
+ }
+
+ /*
+ * For bank_fields, we must write the bank_value to the bank_register
+ * (itself a region_field) before we can access the data.
+ */
+ status =
+ acpi_ex_insert_into_field(obj_desc->bank_field.bank_obj,
+ &obj_desc->bank_field.value,
+ sizeof(obj_desc->bank_field.
+ value));
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Now that the Bank has been selected, fall through to the
+ * region_field case and write the datum to the Operation Region
+ */
+
+ ACPI_FALLTHROUGH;
+
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ /*
+ * For simple region_fields, we just directly access the owning
+ * Operation Region.
+ */
+ status =
+ acpi_ex_access_region(obj_desc, field_datum_byte_offset,
+ value, read_write);
+ break;
+
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+ /*
+ * Ensure that the index_value is not beyond the capacity of
+ * the register
+ */
+ if (acpi_ex_register_overflow(obj_desc->index_field.index_obj,
+ (u64) obj_desc->index_field.
+ value)) {
+ return_ACPI_STATUS(AE_AML_REGISTER_LIMIT);
+ }
+
+ /* Write the index value to the index_register (itself a region_field) */
+
+ field_datum_byte_offset += obj_desc->index_field.value;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Write to Index Register: Value %8.8X\n",
+ field_datum_byte_offset));
+
+ status =
+ acpi_ex_insert_into_field(obj_desc->index_field.index_obj,
+ &field_datum_byte_offset,
+ sizeof(field_datum_byte_offset));
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (read_write == ACPI_READ) {
+
+ /* Read the datum from the data_register */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Read from Data Register\n"));
+
+ status =
+ acpi_ex_extract_from_field(obj_desc->index_field.
+ data_obj, value,
+ sizeof(u64));
+ } else {
+ /* Write the datum to the data_register */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Write to Data Register: Value %8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64(*value)));
+
+ status =
+ acpi_ex_insert_into_field(obj_desc->index_field.
+ data_obj, value,
+ sizeof(u64));
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Wrong object type in field I/O %u",
+ obj_desc->common.type));
+ status = AE_AML_INTERNAL;
+ break;
+ }
+
+ if (ACPI_SUCCESS(status)) {
+ if (read_write == ACPI_READ) {
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Value Read %8.8X%8.8X, Width %u\n",
+ ACPI_FORMAT_UINT64(*value),
+ obj_desc->common_field.
+ access_byte_width));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Value Written %8.8X%8.8X, Width %u\n",
+ ACPI_FORMAT_UINT64(*value),
+ obj_desc->common_field.
+ access_byte_width));
+ }
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_write_with_update_rule
+ *
+ * PARAMETERS: obj_desc - Field to be written
+ * mask - bitmask within field datum
+ * field_value - Value to write
+ * field_datum_byte_offset - Offset of datum within field
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Apply the field update rule to a field write
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc,
+ u64 mask,
+ u64 field_value, u32 field_datum_byte_offset)
+{
+ acpi_status status = AE_OK;
+ u64 merged_value;
+ u64 current_value;
+
+ ACPI_FUNCTION_TRACE_U32(ex_write_with_update_rule, mask);
+
+ /* Start with the new bits */
+
+ merged_value = field_value;
+
+ /* If the mask is all ones, we don't need to worry about the update rule */
+
+ if (mask != ACPI_UINT64_MAX) {
+
+ /* Decode the update rule */
+
+ switch (obj_desc->common_field.
+ field_flags & AML_FIELD_UPDATE_RULE_MASK) {
+ case AML_FIELD_UPDATE_PRESERVE:
+ /*
+ * Check if update rule needs to be applied (not if mask is all
+ * ones) The left shift drops the bits we want to ignore.
+ */
+ if ((~mask << (ACPI_MUL_8(sizeof(mask)) -
+ ACPI_MUL_8(obj_desc->common_field.
+ access_byte_width))) != 0) {
+ /*
+ * Read the current contents of the byte/word/dword containing
+ * the field, and merge with the new field value.
+ */
+ status =
+ acpi_ex_field_datum_io(obj_desc,
+ field_datum_byte_offset,
+ &current_value,
+ ACPI_READ);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ merged_value |= (current_value & ~mask);
+ }
+ break;
+
+ case AML_FIELD_UPDATE_WRITE_AS_ONES:
+
+ /* Set positions outside the field to all ones */
+
+ merged_value |= ~mask;
+ break;
+
+ case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
+
+ /* Set positions outside the field to all zeros */
+
+ merged_value &= mask;
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown UpdateRule value: 0x%X",
+ (obj_desc->common_field.field_flags &
+ AML_FIELD_UPDATE_RULE_MASK)));
+ return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
+ }
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Mask %8.8X%8.8X, DatumOffset %X, Width %X, "
+ "Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64(mask),
+ field_datum_byte_offset,
+ obj_desc->common_field.access_byte_width,
+ ACPI_FORMAT_UINT64(field_value),
+ ACPI_FORMAT_UINT64(merged_value)));
+
+ /* Write the merged value */
+
+ status =
+ acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset,
+ &merged_value, ACPI_WRITE);
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_extract_from_field
+ *
+ * PARAMETERS: obj_desc - Field to be read
+ * buffer - Where to store the field data
+ * buffer_length - Length of Buffer
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Retrieve the current value of the given field
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
+ void *buffer, u32 buffer_length)
+{
+ acpi_status status;
+ u64 raw_datum;
+ u64 merged_datum;
+ u32 field_offset = 0;
+ u32 buffer_offset = 0;
+ u32 buffer_tail_bits;
+ u32 datum_count;
+ u32 field_datum_count;
+ u32 access_bit_width;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ex_extract_from_field);
+
+ /* Validate target buffer and clear it */
+
+ if (buffer_length <
+ ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length)) {
+ ACPI_ERROR((AE_INFO,
+ "Field size %u (bits) is too large for buffer (%u)",
+ obj_desc->common_field.bit_length, buffer_length));
+
+ return_ACPI_STATUS(AE_BUFFER_OVERFLOW);
+ }
+
+ memset(buffer, 0, buffer_length);
+ access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width);
+
+ /* Handle the simple case here */
+
+ if ((obj_desc->common_field.start_field_bit_offset == 0) &&
+ (obj_desc->common_field.bit_length == access_bit_width)) {
+ if (buffer_length >= sizeof(u64)) {
+ status =
+ acpi_ex_field_datum_io(obj_desc, 0, buffer,
+ ACPI_READ);
+ } else {
+ /* Use raw_datum (u64) to handle buffers < 64 bits */
+
+ status =
+ acpi_ex_field_datum_io(obj_desc, 0, &raw_datum,
+ ACPI_READ);
+ memcpy(buffer, &raw_datum, buffer_length);
+ }
+
+ return_ACPI_STATUS(status);
+ }
+
+/* TBD: Move to common setup code */
+
+ /* Field algorithm is limited to sizeof(u64), truncate if needed */
+
+ if (obj_desc->common_field.access_byte_width > sizeof(u64)) {
+ obj_desc->common_field.access_byte_width = sizeof(u64);
+ access_bit_width = sizeof(u64) * 8;
+ }
+
+ /* Compute the number of datums (access width data items) */
+
+ datum_count =
+ ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
+ access_bit_width);
+
+ field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
+ obj_desc->common_field.
+ start_field_bit_offset,
+ access_bit_width);
+
+ /* Priming read from the field */
+
+ status =
+ acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum,
+ ACPI_READ);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ merged_datum =
+ raw_datum >> obj_desc->common_field.start_field_bit_offset;
+
+ /* Read the rest of the field */
+
+ for (i = 1; i < field_datum_count; i++) {
+
+ /* Get next input datum from the field */
+
+ field_offset += obj_desc->common_field.access_byte_width;
+ status =
+ acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum,
+ ACPI_READ);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Merge with previous datum if necessary.
+ *
+ * Note: Before the shift, check if the shift value will be larger than
+ * the integer size. If so, there is no need to perform the operation.
+ * This avoids the differences in behavior between different compilers
+ * concerning shift values larger than the target data width.
+ */
+ if (access_bit_width -
+ obj_desc->common_field.start_field_bit_offset <
+ ACPI_INTEGER_BIT_SIZE) {
+ merged_datum |=
+ raw_datum << (access_bit_width -
+ obj_desc->common_field.
+ start_field_bit_offset);
+ }
+
+ if (i == datum_count) {
+ break;
+ }
+
+ /* Write merged datum to target buffer */
+
+ memcpy(((char *)buffer) + buffer_offset, &merged_datum,
+ ACPI_MIN(obj_desc->common_field.access_byte_width,
+ buffer_length - buffer_offset));
+
+ buffer_offset += obj_desc->common_field.access_byte_width;
+ merged_datum =
+ raw_datum >> obj_desc->common_field.start_field_bit_offset;
+ }
+
+ /* Mask off any extra bits in the last datum */
+
+ buffer_tail_bits = obj_desc->common_field.bit_length % access_bit_width;
+ if (buffer_tail_bits) {
+ merged_datum &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
+ }
+
+ /* Write the last datum to the buffer */
+
+ memcpy(((char *)buffer) + buffer_offset, &merged_datum,
+ ACPI_MIN(obj_desc->common_field.access_byte_width,
+ buffer_length - buffer_offset));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_insert_into_field
+ *
+ * PARAMETERS: obj_desc - Field to be written
+ * buffer - Data to be written
+ * buffer_length - Length of Buffer
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Store the Buffer contents into the given field
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
+ void *buffer, u32 buffer_length)
+{
+ void *new_buffer;
+ acpi_status status;
+ u64 mask;
+ u64 width_mask;
+ u64 merged_datum;
+ u64 raw_datum = 0;
+ u32 field_offset = 0;
+ u32 buffer_offset = 0;
+ u32 buffer_tail_bits;
+ u32 datum_count;
+ u32 field_datum_count;
+ u32 access_bit_width;
+ u32 required_length;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ex_insert_into_field);
+
+ /* Validate input buffer */
+
+ new_buffer = NULL;
+ required_length =
+ ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length);
+
+ /*
+ * We must have a buffer that is at least as long as the field
+ * we are writing to. This is because individual fields are
+ * indivisible and partial writes are not supported -- as per
+ * the ACPI specification.
+ */
+ if (buffer_length < required_length) {
+
+ /* We need to create a new buffer */
+
+ new_buffer = ACPI_ALLOCATE_ZEROED(required_length);
+ if (!new_buffer) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /*
+ * Copy the original data to the new buffer, starting
+ * at Byte zero. All unused (upper) bytes of the
+ * buffer will be 0.
+ */
+ memcpy((char *)new_buffer, (char *)buffer, buffer_length);
+ buffer = new_buffer;
+ buffer_length = required_length;
+ }
+
+/* TBD: Move to common setup code */
+
+ /* Algo is limited to sizeof(u64), so cut the access_byte_width */
+ if (obj_desc->common_field.access_byte_width > sizeof(u64)) {
+ obj_desc->common_field.access_byte_width = sizeof(u64);
+ }
+
+ access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width);
+
+ /* Create the bitmasks used for bit insertion */
+
+ width_mask = ACPI_MASK_BITS_ABOVE_64(access_bit_width);
+ mask = width_mask &
+ ACPI_MASK_BITS_BELOW(obj_desc->common_field.start_field_bit_offset);
+
+ /* Compute the number of datums (access width data items) */
+
+ datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
+ access_bit_width);
+
+ field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
+ obj_desc->common_field.
+ start_field_bit_offset,
+ access_bit_width);
+
+ /* Get initial Datum from the input buffer */
+
+ memcpy(&raw_datum, buffer,
+ ACPI_MIN(obj_desc->common_field.access_byte_width,
+ buffer_length - buffer_offset));
+
+ merged_datum =
+ raw_datum << obj_desc->common_field.start_field_bit_offset;
+
+ /* Write the entire field */
+
+ for (i = 1; i < field_datum_count; i++) {
+
+ /* Write merged datum to the target field */
+
+ merged_datum &= mask;
+ status =
+ acpi_ex_write_with_update_rule(obj_desc, mask, merged_datum,
+ field_offset);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ field_offset += obj_desc->common_field.access_byte_width;
+
+ /*
+ * Start new output datum by merging with previous input datum
+ * if necessary.
+ *
+ * Note: Before the shift, check if the shift value will be larger than
+ * the integer size. If so, there is no need to perform the operation.
+ * This avoids the differences in behavior between different compilers
+ * concerning shift values larger than the target data width.
+ */
+ if ((access_bit_width -
+ obj_desc->common_field.start_field_bit_offset) <
+ ACPI_INTEGER_BIT_SIZE) {
+ merged_datum =
+ raw_datum >> (access_bit_width -
+ obj_desc->common_field.
+ start_field_bit_offset);
+ } else {
+ merged_datum = 0;
+ }
+
+ mask = width_mask;
+
+ if (i == datum_count) {
+ break;
+ }
+
+ /* Get the next input datum from the buffer */
+
+ buffer_offset += obj_desc->common_field.access_byte_width;
+ memcpy(&raw_datum, ((char *)buffer) + buffer_offset,
+ ACPI_MIN(obj_desc->common_field.access_byte_width,
+ buffer_length - buffer_offset));
+
+ merged_datum |=
+ raw_datum << obj_desc->common_field.start_field_bit_offset;
+ }
+
+ /* Mask off any extra bits in the last datum */
+
+ buffer_tail_bits = (obj_desc->common_field.bit_length +
+ obj_desc->common_field.start_field_bit_offset) %
+ access_bit_width;
+ if (buffer_tail_bits) {
+ mask &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
+ }
+
+ /* Write the last datum to the field */
+
+ merged_datum &= mask;
+ status =
+ acpi_ex_write_with_update_rule(obj_desc, mask, merged_datum,
+ field_offset);
+
+exit:
+ /* Free temporary buffer if we used one */
+
+ if (new_buffer) {
+ ACPI_FREE(new_buffer);
+ }
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c
new file mode 100644
index 000000000..b4bac8c60
--- /dev/null
+++ b/drivers/acpi/acpica/exmisc.c
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exmisc - ACPI AML (p-code) execution - specific opcodes
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exmisc")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_get_object_reference
+ *
+ * PARAMETERS: obj_desc - Create a reference to this object
+ * return_desc - Where to store the reference
+ * walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Obtain and return a "reference" to the target object
+ * Common code for the ref_of_op and the cond_ref_of_op.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ex_get_object_reference(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **return_desc,
+ struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object *reference_obj;
+ union acpi_operand_object *referenced_obj;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_get_object_reference, obj_desc);
+
+ *return_desc = NULL;
+
+ switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) {
+ case ACPI_DESC_TYPE_OPERAND:
+
+ if (obj_desc->common.type != ACPI_TYPE_LOCAL_REFERENCE) {
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * Must be a reference to a Local or Arg
+ */
+ switch (obj_desc->reference.class) {
+ case ACPI_REFCLASS_LOCAL:
+ case ACPI_REFCLASS_ARG:
+ case ACPI_REFCLASS_DEBUG:
+
+ /* The referenced object is the pseudo-node for the local/arg */
+
+ referenced_obj = obj_desc->reference.object;
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Invalid Reference Class 0x%2.2X",
+ obj_desc->reference.class));
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ break;
+
+ case ACPI_DESC_TYPE_NAMED:
+ /*
+ * A named reference that has already been resolved to a Node
+ */
+ referenced_obj = obj_desc;
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Invalid descriptor type 0x%X",
+ ACPI_GET_DESCRIPTOR_TYPE(obj_desc)));
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ /* Create a new reference object */
+
+ reference_obj =
+ acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE);
+ if (!reference_obj) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ reference_obj->reference.class = ACPI_REFCLASS_REFOF;
+ reference_obj->reference.object = referenced_obj;
+ *return_desc = reference_obj;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Object %p Type [%s], returning Reference %p\n",
+ obj_desc, acpi_ut_get_object_type_name(obj_desc),
+ *return_desc));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_do_math_op
+ *
+ * PARAMETERS: opcode - AML opcode
+ * integer0 - Integer operand #0
+ * integer1 - Integer operand #1
+ *
+ * RETURN: Integer result of the operation
+ *
+ * DESCRIPTION: Execute a math AML opcode. The purpose of having all of the
+ * math functions here is to prevent a lot of pointer dereferencing
+ * to obtain the operands.
+ *
+ ******************************************************************************/
+
+u64 acpi_ex_do_math_op(u16 opcode, u64 integer0, u64 integer1)
+{
+
+ ACPI_FUNCTION_ENTRY();
+
+ switch (opcode) {
+ case AML_ADD_OP: /* Add (Integer0, Integer1, Result) */
+
+ return (integer0 + integer1);
+
+ case AML_BIT_AND_OP: /* And (Integer0, Integer1, Result) */
+
+ return (integer0 & integer1);
+
+ case AML_BIT_NAND_OP: /* NAnd (Integer0, Integer1, Result) */
+
+ return (~(integer0 & integer1));
+
+ case AML_BIT_OR_OP: /* Or (Integer0, Integer1, Result) */
+
+ return (integer0 | integer1);
+
+ case AML_BIT_NOR_OP: /* NOr (Integer0, Integer1, Result) */
+
+ return (~(integer0 | integer1));
+
+ case AML_BIT_XOR_OP: /* XOr (Integer0, Integer1, Result) */
+
+ return (integer0 ^ integer1);
+
+ case AML_MULTIPLY_OP: /* Multiply (Integer0, Integer1, Result) */
+
+ return (integer0 * integer1);
+
+ case AML_SHIFT_LEFT_OP: /* shift_left (Operand, shift_count, Result) */
+
+ /*
+ * We need to check if the shiftcount is larger than the integer bit
+ * width since the behavior of this is not well-defined in the C language.
+ */
+ if (integer1 >= acpi_gbl_integer_bit_width) {
+ return (0);
+ }
+ return (integer0 << integer1);
+
+ case AML_SHIFT_RIGHT_OP: /* shift_right (Operand, shift_count, Result) */
+
+ /*
+ * We need to check if the shiftcount is larger than the integer bit
+ * width since the behavior of this is not well-defined in the C language.
+ */
+ if (integer1 >= acpi_gbl_integer_bit_width) {
+ return (0);
+ }
+ return (integer0 >> integer1);
+
+ case AML_SUBTRACT_OP: /* Subtract (Integer0, Integer1, Result) */
+
+ return (integer0 - integer1);
+
+ default:
+
+ return (0);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_do_logical_numeric_op
+ *
+ * PARAMETERS: opcode - AML opcode
+ * integer0 - Integer operand #0
+ * integer1 - Integer operand #1
+ * logical_result - TRUE/FALSE result of the operation
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute a logical "Numeric" AML opcode. For these Numeric
+ * operators (LAnd and LOr), both operands must be integers.
+ *
+ * Note: cleanest machine code seems to be produced by the code
+ * below, rather than using statements of the form:
+ * Result = (Integer0 && Integer1);
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_do_logical_numeric_op(u16 opcode,
+ u64 integer0, u64 integer1, u8 *logical_result)
+{
+ acpi_status status = AE_OK;
+ u8 local_result = FALSE;
+
+ ACPI_FUNCTION_TRACE(ex_do_logical_numeric_op);
+
+ switch (opcode) {
+ case AML_LOGICAL_AND_OP: /* LAnd (Integer0, Integer1) */
+
+ if (integer0 && integer1) {
+ local_result = TRUE;
+ }
+ break;
+
+ case AML_LOGICAL_OR_OP: /* LOr (Integer0, Integer1) */
+
+ if (integer0 || integer1) {
+ local_result = TRUE;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid numeric logical opcode: %X", opcode));
+ status = AE_AML_INTERNAL;
+ break;
+ }
+
+ /* Return the logical result and status */
+
+ *logical_result = local_result;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_do_logical_op
+ *
+ * PARAMETERS: opcode - AML opcode
+ * operand0 - operand #0
+ * operand1 - operand #1
+ * logical_result - TRUE/FALSE result of the operation
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute a logical AML opcode. The purpose of having all of the
+ * functions here is to prevent a lot of pointer dereferencing
+ * to obtain the operands and to simplify the generation of the
+ * logical value. For the Numeric operators (LAnd and LOr), both
+ * operands must be integers. For the other logical operators,
+ * operands can be any combination of Integer/String/Buffer. The
+ * first operand determines the type to which the second operand
+ * will be converted.
+ *
+ * Note: cleanest machine code seems to be produced by the code
+ * below, rather than using statements of the form:
+ * Result = (Operand0 == Operand1);
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_do_logical_op(u16 opcode,
+ union acpi_operand_object *operand0,
+ union acpi_operand_object *operand1, u8 * logical_result)
+{
+ union acpi_operand_object *local_operand1 = operand1;
+ u64 integer0;
+ u64 integer1;
+ u32 length0;
+ u32 length1;
+ acpi_status status = AE_OK;
+ u8 local_result = FALSE;
+ int compare;
+
+ ACPI_FUNCTION_TRACE(ex_do_logical_op);
+
+ /*
+ * Convert the second operand if necessary. The first operand
+ * determines the type of the second operand, (See the Data Types
+ * section of the ACPI 3.0+ specification.) Both object types are
+ * guaranteed to be either Integer/String/Buffer by the operand
+ * resolution mechanism.
+ */
+ switch (operand0->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ status = acpi_ex_convert_to_integer(operand1, &local_operand1,
+ ACPI_IMPLICIT_CONVERSION);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ status =
+ acpi_ex_convert_to_string(operand1, &local_operand1,
+ ACPI_IMPLICIT_CONVERT_HEX);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ status = acpi_ex_convert_to_buffer(operand1, &local_operand1);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid object type for logical operator: %X",
+ operand0->common.type));
+ status = AE_AML_INTERNAL;
+ break;
+ }
+
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /*
+ * Two cases: 1) Both Integers, 2) Both Strings or Buffers
+ */
+ if (operand0->common.type == ACPI_TYPE_INTEGER) {
+ /*
+ * 1) Both operands are of type integer
+ * Note: local_operand1 may have changed above
+ */
+ integer0 = operand0->integer.value;
+ integer1 = local_operand1->integer.value;
+
+ switch (opcode) {
+ case AML_LOGICAL_EQUAL_OP: /* LEqual (Operand0, Operand1) */
+
+ if (integer0 == integer1) {
+ local_result = TRUE;
+ }
+ break;
+
+ case AML_LOGICAL_GREATER_OP: /* LGreater (Operand0, Operand1) */
+
+ if (integer0 > integer1) {
+ local_result = TRUE;
+ }
+ break;
+
+ case AML_LOGICAL_LESS_OP: /* LLess (Operand0, Operand1) */
+
+ if (integer0 < integer1) {
+ local_result = TRUE;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid comparison opcode: %X", opcode));
+ status = AE_AML_INTERNAL;
+ break;
+ }
+ } else {
+ /*
+ * 2) Both operands are Strings or both are Buffers
+ * Note: Code below takes advantage of common Buffer/String
+ * object fields. local_operand1 may have changed above. Use
+ * memcmp to handle nulls in buffers.
+ */
+ length0 = operand0->buffer.length;
+ length1 = local_operand1->buffer.length;
+
+ /* Lexicographic compare: compare the data bytes */
+
+ compare = memcmp(operand0->buffer.pointer,
+ local_operand1->buffer.pointer,
+ (length0 > length1) ? length1 : length0);
+
+ switch (opcode) {
+ case AML_LOGICAL_EQUAL_OP: /* LEqual (Operand0, Operand1) */
+
+ /* Length and all bytes must be equal */
+
+ if ((length0 == length1) && (compare == 0)) {
+
+ /* Length and all bytes match ==> TRUE */
+
+ local_result = TRUE;
+ }
+ break;
+
+ case AML_LOGICAL_GREATER_OP: /* LGreater (Operand0, Operand1) */
+
+ if (compare > 0) {
+ local_result = TRUE;
+ goto cleanup; /* TRUE */
+ }
+ if (compare < 0) {
+ goto cleanup; /* FALSE */
+ }
+
+ /* Bytes match (to shortest length), compare lengths */
+
+ if (length0 > length1) {
+ local_result = TRUE;
+ }
+ break;
+
+ case AML_LOGICAL_LESS_OP: /* LLess (Operand0, Operand1) */
+
+ if (compare > 0) {
+ goto cleanup; /* FALSE */
+ }
+ if (compare < 0) {
+ local_result = TRUE;
+ goto cleanup; /* TRUE */
+ }
+
+ /* Bytes match (to shortest length), compare lengths */
+
+ if (length0 < length1) {
+ local_result = TRUE;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid comparison opcode: %X", opcode));
+ status = AE_AML_INTERNAL;
+ break;
+ }
+ }
+
+cleanup:
+
+ /* New object was created if implicit conversion performed - delete */
+
+ if (local_operand1 != operand1) {
+ acpi_ut_remove_reference(local_operand1);
+ }
+
+ /* Return the logical result and status */
+
+ *logical_result = local_result;
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c
new file mode 100644
index 000000000..e9dcfa1e9
--- /dev/null
+++ b/drivers/acpi/acpica/exmutex.c
@@ -0,0 +1,506 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exmutex - ASL Mutex Acquire/Release functions
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exmutex")
+
+/* Local prototypes */
+static void
+acpi_ex_link_mutex(union acpi_operand_object *obj_desc,
+ struct acpi_thread_state *thread);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_unlink_mutex
+ *
+ * PARAMETERS: obj_desc - The mutex to be unlinked
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Remove a mutex from the "AcquiredMutex" list
+ *
+ ******************************************************************************/
+
+void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc)
+{
+ struct acpi_thread_state *thread = obj_desc->mutex.owner_thread;
+
+ if (!thread) {
+ return;
+ }
+
+ /* Doubly linked list */
+
+ if (obj_desc->mutex.next) {
+ (obj_desc->mutex.next)->mutex.prev = obj_desc->mutex.prev;
+ }
+
+ if (obj_desc->mutex.prev) {
+ (obj_desc->mutex.prev)->mutex.next = obj_desc->mutex.next;
+
+ /*
+ * Migrate the previous sync level associated with this mutex to
+ * the previous mutex on the list so that it may be preserved.
+ * This handles the case where several mutexes have been acquired
+ * at the same level, but are not released in opposite order.
+ */
+ (obj_desc->mutex.prev)->mutex.original_sync_level =
+ obj_desc->mutex.original_sync_level;
+ } else {
+ thread->acquired_mutex_list = obj_desc->mutex.next;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_link_mutex
+ *
+ * PARAMETERS: obj_desc - The mutex to be linked
+ * thread - Current executing thread object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Add a mutex to the "AcquiredMutex" list for this walk
+ *
+ ******************************************************************************/
+
+static void
+acpi_ex_link_mutex(union acpi_operand_object *obj_desc,
+ struct acpi_thread_state *thread)
+{
+ union acpi_operand_object *list_head;
+
+ list_head = thread->acquired_mutex_list;
+
+ /* This object will be the first object in the list */
+
+ obj_desc->mutex.prev = NULL;
+ obj_desc->mutex.next = list_head;
+
+ /* Update old first object to point back to this object */
+
+ if (list_head) {
+ list_head->mutex.prev = obj_desc;
+ }
+
+ /* Update list head */
+
+ thread->acquired_mutex_list = obj_desc;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_acquire_mutex_object
+ *
+ * PARAMETERS: timeout - Timeout in milliseconds
+ * obj_desc - Mutex object
+ * thread_id - Current thread state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Acquire an AML mutex, low-level interface. Provides a common
+ * path that supports multiple acquires by the same thread.
+ *
+ * MUTEX: Interpreter must be locked
+ *
+ * NOTE: This interface is called from three places:
+ * 1) From acpi_ex_acquire_mutex, via an AML Acquire() operator
+ * 2) From acpi_ex_acquire_global_lock when an AML Field access requires the
+ * global lock
+ * 3) From the external interface, acpi_acquire_global_lock
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_acquire_mutex_object(u16 timeout,
+ union acpi_operand_object *obj_desc,
+ acpi_thread_id thread_id)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_acquire_mutex_object, obj_desc);
+
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Support for multiple acquires by the owning thread */
+
+ if (obj_desc->mutex.thread_id == thread_id) {
+ /*
+ * The mutex is already owned by this thread, just increment the
+ * acquisition depth
+ */
+ obj_desc->mutex.acquisition_depth++;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Acquire the mutex, wait if necessary. Special case for Global Lock */
+
+ if (obj_desc == acpi_gbl_global_lock_mutex) {
+ status = acpi_ev_acquire_global_lock(timeout);
+ } else {
+ status =
+ acpi_ex_system_wait_mutex(obj_desc->mutex.os_mutex,
+ timeout);
+ }
+
+ if (ACPI_FAILURE(status)) {
+
+ /* Includes failure from a timeout on time_desc */
+
+ return_ACPI_STATUS(status);
+ }
+
+ /* Acquired the mutex: update mutex object */
+
+ obj_desc->mutex.thread_id = thread_id;
+ obj_desc->mutex.acquisition_depth = 1;
+ obj_desc->mutex.original_sync_level = 0;
+ obj_desc->mutex.owner_thread = NULL; /* Used only for AML Acquire() */
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_acquire_mutex
+ *
+ * PARAMETERS: time_desc - Timeout integer
+ * obj_desc - Mutex object
+ * walk_state - Current method execution state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Acquire an AML mutex
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_acquire_mutex(union acpi_operand_object *time_desc,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_acquire_mutex, obj_desc);
+
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Must have a valid thread state struct */
+
+ if (!walk_state->thread) {
+ ACPI_ERROR((AE_INFO,
+ "Cannot acquire Mutex [%4.4s], null thread info",
+ acpi_ut_get_node_name(obj_desc->mutex.node)));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ /*
+ * Current sync level must be less than or equal to the sync level
+ * of the mutex. This mechanism provides some deadlock prevention.
+ */
+ if (walk_state->thread->current_sync_level > obj_desc->mutex.sync_level) {
+ ACPI_ERROR((AE_INFO,
+ "Cannot acquire Mutex [%4.4s], "
+ "current SyncLevel is too large (%u)",
+ acpi_ut_get_node_name(obj_desc->mutex.node),
+ walk_state->thread->current_sync_level));
+ return_ACPI_STATUS(AE_AML_MUTEX_ORDER);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Acquiring: Mutex SyncLevel %u, Thread SyncLevel %u, "
+ "Depth %u TID %p\n",
+ obj_desc->mutex.sync_level,
+ walk_state->thread->current_sync_level,
+ obj_desc->mutex.acquisition_depth,
+ walk_state->thread));
+
+ status = acpi_ex_acquire_mutex_object((u16)time_desc->integer.value,
+ obj_desc,
+ walk_state->thread->thread_id);
+
+ if (ACPI_SUCCESS(status) && obj_desc->mutex.acquisition_depth == 1) {
+
+ /* Save Thread object, original/current sync levels */
+
+ obj_desc->mutex.owner_thread = walk_state->thread;
+ obj_desc->mutex.original_sync_level =
+ walk_state->thread->current_sync_level;
+ walk_state->thread->current_sync_level =
+ obj_desc->mutex.sync_level;
+
+ /* Link the mutex to the current thread for force-unlock at method exit */
+
+ acpi_ex_link_mutex(obj_desc, walk_state->thread);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Acquired: Mutex SyncLevel %u, Thread SyncLevel %u, Depth %u\n",
+ obj_desc->mutex.sync_level,
+ walk_state->thread->current_sync_level,
+ obj_desc->mutex.acquisition_depth));
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_release_mutex_object
+ *
+ * PARAMETERS: obj_desc - The object descriptor for this op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Release a previously acquired Mutex, low level interface.
+ * Provides a common path that supports multiple releases (after
+ * previous multiple acquires) by the same thread.
+ *
+ * MUTEX: Interpreter must be locked
+ *
+ * NOTE: This interface is called from three places:
+ * 1) From acpi_ex_release_mutex, via an AML Acquire() operator
+ * 2) From acpi_ex_release_global_lock when an AML Field access requires the
+ * global lock
+ * 3) From the external interface, acpi_release_global_lock
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_release_mutex_object(union acpi_operand_object *obj_desc)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ex_release_mutex_object);
+
+ if (obj_desc->mutex.acquisition_depth == 0) {
+ return_ACPI_STATUS(AE_NOT_ACQUIRED);
+ }
+
+ /* Match multiple Acquires with multiple Releases */
+
+ obj_desc->mutex.acquisition_depth--;
+ if (obj_desc->mutex.acquisition_depth != 0) {
+
+ /* Just decrement the depth and return */
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ if (obj_desc->mutex.owner_thread) {
+
+ /* Unlink the mutex from the owner's list */
+
+ acpi_ex_unlink_mutex(obj_desc);
+ obj_desc->mutex.owner_thread = NULL;
+ }
+
+ /* Release the mutex, special case for Global Lock */
+
+ if (obj_desc == acpi_gbl_global_lock_mutex) {
+ status = acpi_ev_release_global_lock();
+ } else {
+ acpi_os_release_mutex(obj_desc->mutex.os_mutex);
+ }
+
+ /* Clear mutex info */
+
+ obj_desc->mutex.thread_id = 0;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_release_mutex
+ *
+ * PARAMETERS: obj_desc - The object descriptor for this op
+ * walk_state - Current method execution state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Release a previously acquired Mutex.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+ u8 previous_sync_level;
+ struct acpi_thread_state *owner_thread;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ex_release_mutex);
+
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ owner_thread = obj_desc->mutex.owner_thread;
+
+ /* The mutex must have been previously acquired in order to release it */
+
+ if (!owner_thread) {
+ ACPI_ERROR((AE_INFO,
+ "Cannot release Mutex [%4.4s], not acquired",
+ acpi_ut_get_node_name(obj_desc->mutex.node)));
+ return_ACPI_STATUS(AE_AML_MUTEX_NOT_ACQUIRED);
+ }
+
+ /* Must have a valid thread ID */
+
+ if (!walk_state->thread) {
+ ACPI_ERROR((AE_INFO,
+ "Cannot release Mutex [%4.4s], null thread info",
+ acpi_ut_get_node_name(obj_desc->mutex.node)));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ /*
+ * The Mutex is owned, but this thread must be the owner.
+ * Special case for Global Lock, any thread can release
+ */
+ if ((owner_thread->thread_id != walk_state->thread->thread_id) &&
+ (obj_desc != acpi_gbl_global_lock_mutex)) {
+ ACPI_ERROR((AE_INFO,
+ "Thread %u cannot release Mutex [%4.4s] acquired by thread %u",
+ (u32)walk_state->thread->thread_id,
+ acpi_ut_get_node_name(obj_desc->mutex.node),
+ (u32)owner_thread->thread_id));
+ return_ACPI_STATUS(AE_AML_NOT_OWNER);
+ }
+
+ /*
+ * The sync level of the mutex must be equal to the current sync level. In
+ * other words, the current level means that at least one mutex at that
+ * level is currently being held. Attempting to release a mutex of a
+ * different level can only mean that the mutex ordering rule is being
+ * violated. This behavior is clarified in ACPI 4.0 specification.
+ */
+ if (obj_desc->mutex.sync_level != owner_thread->current_sync_level) {
+ ACPI_ERROR((AE_INFO,
+ "Cannot release Mutex [%4.4s], SyncLevel mismatch: "
+ "mutex %u current %u",
+ acpi_ut_get_node_name(obj_desc->mutex.node),
+ obj_desc->mutex.sync_level,
+ walk_state->thread->current_sync_level));
+ return_ACPI_STATUS(AE_AML_MUTEX_ORDER);
+ }
+
+ /*
+ * Get the previous sync_level from the head of the acquired mutex list.
+ * This handles the case where several mutexes at the same level have been
+ * acquired, but are not released in reverse order.
+ */
+ previous_sync_level =
+ owner_thread->acquired_mutex_list->mutex.original_sync_level;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Releasing: Object SyncLevel %u, Thread SyncLevel %u, "
+ "Prev SyncLevel %u, Depth %u TID %p\n",
+ obj_desc->mutex.sync_level,
+ walk_state->thread->current_sync_level,
+ previous_sync_level,
+ obj_desc->mutex.acquisition_depth,
+ walk_state->thread));
+
+ status = acpi_ex_release_mutex_object(obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (obj_desc->mutex.acquisition_depth == 0) {
+
+ /* Restore the previous sync_level */
+
+ owner_thread->current_sync_level = previous_sync_level;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Released: Object SyncLevel %u, Thread SyncLevel, %u, "
+ "Prev SyncLevel %u, Depth %u\n",
+ obj_desc->mutex.sync_level,
+ walk_state->thread->current_sync_level,
+ previous_sync_level,
+ obj_desc->mutex.acquisition_depth));
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_release_all_mutexes
+ *
+ * PARAMETERS: thread - Current executing thread object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Release all mutexes held by this thread
+ *
+ * NOTE: This function is called as the thread is exiting the interpreter.
+ * Mutexes are not released when an individual control method is exited, but
+ * only when the parent thread actually exits the interpreter. This allows one
+ * method to acquire a mutex, and a different method to release it, as long as
+ * this is performed underneath a single parent control method.
+ *
+ ******************************************************************************/
+
+void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread)
+{
+ union acpi_operand_object *next = thread->acquired_mutex_list;
+ union acpi_operand_object *obj_desc;
+
+ ACPI_FUNCTION_TRACE(ex_release_all_mutexes);
+
+ /* Traverse the list of owned mutexes, releasing each one */
+
+ while (next) {
+ obj_desc = next;
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Mutex [%4.4s] force-release, SyncLevel %u Depth %u\n",
+ obj_desc->mutex.node->name.ascii,
+ obj_desc->mutex.sync_level,
+ obj_desc->mutex.acquisition_depth));
+
+ /* Release the mutex, special case for Global Lock */
+
+ if (obj_desc == acpi_gbl_global_lock_mutex) {
+
+ /* Ignore errors */
+
+ (void)acpi_ev_release_global_lock();
+ } else {
+ acpi_os_release_mutex(obj_desc->mutex.os_mutex);
+ }
+
+ /* Update Thread sync_level (Last mutex is the important one) */
+
+ thread->current_sync_level =
+ obj_desc->mutex.original_sync_level;
+
+ /* Mark mutex unowned */
+
+ next = obj_desc->mutex.next;
+
+ obj_desc->mutex.prev = NULL;
+ obj_desc->mutex.next = NULL;
+ obj_desc->mutex.acquisition_depth = 0;
+ obj_desc->mutex.owner_thread = NULL;
+ obj_desc->mutex.thread_id = 0;
+ }
+
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/exnames.c b/drivers/acpi/acpica/exnames.c
new file mode 100644
index 000000000..318eb7690
--- /dev/null
+++ b/drivers/acpi/acpica/exnames.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exnames - interpreter/scanner name load/execute
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exnames")
+
+/* Local prototypes */
+static char *acpi_ex_allocate_name_string(u32 prefix_count, u32 num_name_segs);
+
+static acpi_status acpi_ex_name_segment(u8 **in_aml_address, char *name_string);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_allocate_name_string
+ *
+ * PARAMETERS: prefix_count - Count of parent levels. Special cases:
+ * (-1)==root, 0==none
+ * num_name_segs - count of 4-character name segments
+ *
+ * RETURN: A pointer to the allocated string segment. This segment must
+ * be deleted by the caller.
+ *
+ * DESCRIPTION: Allocate a buffer for a name string. Ensure allocated name
+ * string is long enough, and set up prefix if any.
+ *
+ ******************************************************************************/
+
+static char *acpi_ex_allocate_name_string(u32 prefix_count, u32 num_name_segs)
+{
+ char *temp_ptr;
+ char *name_string;
+ u32 size_needed;
+
+ ACPI_FUNCTION_TRACE(ex_allocate_name_string);
+
+ /*
+ * Allow room for all \ and ^ prefixes, all segments and a multi_name_prefix.
+ * Also, one byte for the null terminator.
+ * This may actually be somewhat longer than needed.
+ */
+ if (prefix_count == ACPI_UINT32_MAX) {
+
+ /* Special case for root */
+
+ size_needed = 1 + (ACPI_NAMESEG_SIZE * num_name_segs) + 2 + 1;
+ } else {
+ size_needed =
+ prefix_count + (ACPI_NAMESEG_SIZE * num_name_segs) + 2 + 1;
+ }
+
+ /*
+ * Allocate a buffer for the name.
+ * This buffer must be deleted by the caller!
+ */
+ name_string = ACPI_ALLOCATE(size_needed);
+ if (!name_string) {
+ ACPI_ERROR((AE_INFO,
+ "Could not allocate size %u", size_needed));
+ return_PTR(NULL);
+ }
+
+ temp_ptr = name_string;
+
+ /* Set up Root or Parent prefixes if needed */
+
+ if (prefix_count == ACPI_UINT32_MAX) {
+ *temp_ptr++ = AML_ROOT_PREFIX;
+ } else {
+ while (prefix_count--) {
+ *temp_ptr++ = AML_PARENT_PREFIX;
+ }
+ }
+
+ /* Set up Dual or Multi prefixes if needed */
+
+ if (num_name_segs > 2) {
+
+ /* Set up multi prefixes */
+
+ *temp_ptr++ = AML_MULTI_NAME_PREFIX;
+ *temp_ptr++ = (char)num_name_segs;
+ } else if (2 == num_name_segs) {
+
+ /* Set up dual prefixes */
+
+ *temp_ptr++ = AML_DUAL_NAME_PREFIX;
+ }
+
+ /*
+ * Terminate string following prefixes. acpi_ex_name_segment() will
+ * append the segment(s)
+ */
+ *temp_ptr = 0;
+
+ return_PTR(name_string);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_name_segment
+ *
+ * PARAMETERS: in_aml_address - Pointer to the name in the AML code
+ * name_string - Where to return the name. The name is appended
+ * to any existing string to form a namepath
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Extract an ACPI name (4 bytes) from the AML byte stream
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_ex_name_segment(u8 ** in_aml_address, char *name_string)
+{
+ char *aml_address = (void *)*in_aml_address;
+ acpi_status status = AE_OK;
+ u32 index;
+ char char_buf[5];
+
+ ACPI_FUNCTION_TRACE(ex_name_segment);
+
+ /*
+ * If first character is a digit, then we know that we aren't looking
+ * at a valid name segment
+ */
+ char_buf[0] = *aml_address;
+
+ if ('0' <= char_buf[0] && char_buf[0] <= '9') {
+ ACPI_ERROR((AE_INFO, "Invalid leading digit: %c", char_buf[0]));
+ return_ACPI_STATUS(AE_CTRL_PENDING);
+ }
+
+ for (index = 0;
+ (index < ACPI_NAMESEG_SIZE)
+ && (acpi_ut_valid_name_char(*aml_address, 0)); index++) {
+ char_buf[index] = *aml_address++;
+ }
+
+ /* Valid name segment */
+
+ if (index == 4) {
+
+ /* Found 4 valid characters */
+
+ char_buf[4] = '\0';
+
+ if (name_string) {
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Appending NameSeg %s\n", char_buf));
+ strcat(name_string, char_buf);
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "No Name string - %s\n", char_buf));
+ }
+ } else if (index == 0) {
+ /*
+ * First character was not a valid name character,
+ * so we are looking at something other than a name.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Leading character is not alpha: %02Xh (not a name)\n",
+ char_buf[0]));
+ status = AE_CTRL_PENDING;
+ } else {
+ /*
+ * Segment started with one or more valid characters, but fewer than
+ * the required 4
+ */
+ status = AE_AML_BAD_NAME;
+ ACPI_ERROR((AE_INFO,
+ "Bad character 0x%02x in name, at %p",
+ *aml_address, aml_address));
+ }
+
+ *in_aml_address = ACPI_CAST_PTR(u8, aml_address);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_get_name_string
+ *
+ * PARAMETERS: data_type - Object type to be associated with this
+ * name
+ * in_aml_address - Pointer to the namestring in the AML code
+ * out_name_string - Where the namestring is returned
+ * out_name_length - Length of the returned string
+ *
+ * RETURN: Status, namestring and length
+ *
+ * DESCRIPTION: Extract a full namepath from the AML byte stream,
+ * including any prefixes.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_get_name_string(acpi_object_type data_type,
+ u8 * in_aml_address,
+ char **out_name_string, u32 * out_name_length)
+{
+ acpi_status status = AE_OK;
+ u8 *aml_address = in_aml_address;
+ char *name_string = NULL;
+ u32 num_segments;
+ u32 prefix_count = 0;
+ u8 has_prefix = FALSE;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_get_name_string, aml_address);
+
+ if (ACPI_TYPE_LOCAL_REGION_FIELD == data_type ||
+ ACPI_TYPE_LOCAL_BANK_FIELD == data_type ||
+ ACPI_TYPE_LOCAL_INDEX_FIELD == data_type) {
+
+ /* Disallow prefixes for types associated with field_unit names */
+
+ name_string = acpi_ex_allocate_name_string(0, 1);
+ if (!name_string) {
+ status = AE_NO_MEMORY;
+ } else {
+ status =
+ acpi_ex_name_segment(&aml_address, name_string);
+ }
+ } else {
+ /*
+ * data_type is not a field name.
+ * Examine first character of name for root or parent prefix operators
+ */
+ switch (*aml_address) {
+ case AML_ROOT_PREFIX:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
+ "RootPrefix(\\) at %p\n",
+ aml_address));
+
+ /*
+ * Remember that we have a root_prefix --
+ * see comment in acpi_ex_allocate_name_string()
+ */
+ aml_address++;
+ prefix_count = ACPI_UINT32_MAX;
+ has_prefix = TRUE;
+ break;
+
+ case AML_PARENT_PREFIX:
+
+ /* Increment past possibly multiple parent prefixes */
+
+ do {
+ ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
+ "ParentPrefix (^) at %p\n",
+ aml_address));
+
+ aml_address++;
+ prefix_count++;
+
+ } while (*aml_address == AML_PARENT_PREFIX);
+
+ has_prefix = TRUE;
+ break;
+
+ default:
+
+ /* Not a prefix character */
+
+ break;
+ }
+
+ /* Examine first character of name for name segment prefix operator */
+
+ switch (*aml_address) {
+ case AML_DUAL_NAME_PREFIX:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
+ "DualNamePrefix at %p\n",
+ aml_address));
+
+ aml_address++;
+ name_string =
+ acpi_ex_allocate_name_string(prefix_count, 2);
+ if (!name_string) {
+ status = AE_NO_MEMORY;
+ break;
+ }
+
+ /* Indicate that we processed a prefix */
+
+ has_prefix = TRUE;
+
+ status =
+ acpi_ex_name_segment(&aml_address, name_string);
+ if (ACPI_SUCCESS(status)) {
+ status =
+ acpi_ex_name_segment(&aml_address,
+ name_string);
+ }
+ break;
+
+ case AML_MULTI_NAME_PREFIX:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
+ "MultiNamePrefix at %p\n",
+ aml_address));
+
+ /* Fetch count of segments remaining in name path */
+
+ aml_address++;
+ num_segments = *aml_address;
+
+ name_string =
+ acpi_ex_allocate_name_string(prefix_count,
+ num_segments);
+ if (!name_string) {
+ status = AE_NO_MEMORY;
+ break;
+ }
+
+ /* Indicate that we processed a prefix */
+
+ aml_address++;
+ has_prefix = TRUE;
+
+ while (num_segments &&
+ (status =
+ acpi_ex_name_segment(&aml_address,
+ name_string)) == AE_OK) {
+ num_segments--;
+ }
+
+ break;
+
+ case 0:
+
+ /* null_name valid as of 8-12-98 ASL/AML Grammar Update */
+
+ if (prefix_count == ACPI_UINT32_MAX) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "NameSeg is \"\\\" followed by NULL\n"));
+ }
+
+ /* Consume the NULL byte */
+
+ aml_address++;
+ name_string =
+ acpi_ex_allocate_name_string(prefix_count, 0);
+ if (!name_string) {
+ status = AE_NO_MEMORY;
+ break;
+ }
+
+ break;
+
+ default:
+
+ /* Name segment string */
+
+ name_string =
+ acpi_ex_allocate_name_string(prefix_count, 1);
+ if (!name_string) {
+ status = AE_NO_MEMORY;
+ break;
+ }
+
+ status =
+ acpi_ex_name_segment(&aml_address, name_string);
+ break;
+ }
+ }
+
+ if (AE_CTRL_PENDING == status && has_prefix) {
+
+ /* Ran out of segments after processing a prefix */
+
+ ACPI_ERROR((AE_INFO, "Malformed Name at %p", name_string));
+ status = AE_AML_BAD_NAME;
+ }
+
+ if (ACPI_FAILURE(status)) {
+ if (name_string) {
+ ACPI_FREE(name_string);
+ }
+ return_ACPI_STATUS(status);
+ }
+
+ *out_name_string = name_string;
+ *out_name_length = (u32) (aml_address - in_aml_address);
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c
new file mode 100644
index 000000000..d108a1a86
--- /dev/null
+++ b/drivers/acpi/acpica/exoparg1.c
@@ -0,0 +1,1086 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exoparg1 - AML execution - opcodes with 1 argument
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "amlcode.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exoparg1")
+
+/*!
+ * Naming convention for AML interpreter execution routines.
+ *
+ * The routines that begin execution of AML opcodes are named with a common
+ * convention based upon the number of arguments, the number of target operands,
+ * and whether or not a value is returned:
+ *
+ * AcpiExOpcode_xA_yT_zR
+ *
+ * Where:
+ *
+ * xA - ARGUMENTS: The number of arguments (input operands) that are
+ * required for this opcode type (0 through 6 args).
+ * yT - TARGETS: The number of targets (output operands) that are required
+ * for this opcode type (0, 1, or 2 targets).
+ * zR - RETURN VALUE: Indicates whether this opcode type returns a value
+ * as the function return (0 or 1).
+ *
+ * The AcpiExOpcode* functions are called via the Dispatcher component with
+ * fully resolved operands.
+!*/
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_0A_0T_1R
+ *
+ * PARAMETERS: walk_state - Current state (contains AML opcode)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute operator with no operands, one return value
+ *
+ ******************************************************************************/
+acpi_status acpi_ex_opcode_0A_0T_1R(struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *return_desc = NULL;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_0A_0T_1R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ /* Examine the AML opcode */
+
+ switch (walk_state->opcode) {
+ case AML_TIMER_OP: /* Timer () */
+
+ /* Create a return object of type Integer */
+
+ return_desc =
+ acpi_ut_create_integer_object(acpi_os_get_timer());
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+ break;
+
+ default: /* Unknown opcode */
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+ status = AE_AML_BAD_OPCODE;
+ break;
+ }
+
+cleanup:
+
+ /* Delete return object on error */
+
+ if ((ACPI_FAILURE(status)) || walk_state->result_obj) {
+ acpi_ut_remove_reference(return_desc);
+ walk_state->result_obj = NULL;
+ } else {
+ /* Save the return value */
+
+ walk_state->result_obj = return_desc;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_1A_0T_0R
+ *
+ * PARAMETERS: walk_state - Current state (contains AML opcode)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute Type 1 monadic operator with numeric operand on
+ * object stack
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_opcode_1A_0T_0R(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_0T_0R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ /* Examine the AML opcode */
+
+ switch (walk_state->opcode) {
+ case AML_RELEASE_OP: /* Release (mutex_object) */
+
+ status = acpi_ex_release_mutex(operand[0], walk_state);
+ break;
+
+ case AML_RESET_OP: /* Reset (event_object) */
+
+ status = acpi_ex_system_reset_event(operand[0]);
+ break;
+
+ case AML_SIGNAL_OP: /* Signal (event_object) */
+
+ status = acpi_ex_system_signal_event(operand[0]);
+ break;
+
+ case AML_SLEEP_OP: /* Sleep (msec_time) */
+
+ status = acpi_ex_system_do_sleep(operand[0]->integer.value);
+ break;
+
+ case AML_STALL_OP: /* Stall (usec_time) */
+
+ status =
+ acpi_ex_system_do_stall((u32) operand[0]->integer.value);
+ break;
+
+ case AML_UNLOAD_OP: /* Unload (Handle) */
+
+ status = acpi_ex_unload_table(operand[0]);
+ break;
+
+ default: /* Unknown opcode */
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+ status = AE_AML_BAD_OPCODE;
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+#ifdef _OBSOLETE_CODE /* Was originally used for Load() operator */
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_1A_1T_0R
+ *
+ * PARAMETERS: walk_state - Current state (contains AML opcode)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute opcode with one argument, one target, and no
+ * return value.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_opcode_1A_1T_0R(struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object **operand = &walk_state->operands[0];
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_1T_0R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ /* Examine the AML opcode */
+
+ switch (walk_state->opcode) {
+#ifdef _OBSOLETE_CODE
+ case AML_LOAD_OP:
+
+ status = acpi_ex_load_op(operand[0], operand[1], walk_state);
+ break;
+#endif
+
+ default: /* Unknown opcode */
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+ status = AE_AML_BAD_OPCODE;
+ goto cleanup;
+ }
+
+cleanup:
+
+ return_ACPI_STATUS(status);
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_1A_1T_1R
+ *
+ * PARAMETERS: walk_state - Current state (contains AML opcode)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute opcode with one argument, one target, and a
+ * return value.
+ * January 2022: Added Load operator, with new ACPI 6.4
+ * semantics.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ union acpi_operand_object *return_desc = NULL;
+ union acpi_operand_object *return_desc2 = NULL;
+ u32 temp32;
+ u32 i;
+ u64 power_of_ten;
+ u64 digit;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_1T_1R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ /* Examine the AML opcode */
+
+ switch (walk_state->opcode) {
+ case AML_BIT_NOT_OP:
+ case AML_FIND_SET_LEFT_BIT_OP:
+ case AML_FIND_SET_RIGHT_BIT_OP:
+ case AML_FROM_BCD_OP:
+ case AML_LOAD_OP:
+ case AML_TO_BCD_OP:
+ case AML_CONDITIONAL_REF_OF_OP:
+
+ /* Create a return object of type Integer for these opcodes */
+
+ return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ switch (walk_state->opcode) {
+ case AML_BIT_NOT_OP: /* Not (Operand, Result) */
+
+ return_desc->integer.value = ~operand[0]->integer.value;
+ break;
+
+ case AML_FIND_SET_LEFT_BIT_OP: /* find_set_left_bit (Operand, Result) */
+
+ return_desc->integer.value = operand[0]->integer.value;
+
+ /*
+ * Acpi specification describes Integer type as a little
+ * endian unsigned value, so this boundary condition is valid.
+ */
+ for (temp32 = 0; return_desc->integer.value &&
+ temp32 < ACPI_INTEGER_BIT_SIZE; ++temp32) {
+ return_desc->integer.value >>= 1;
+ }
+
+ return_desc->integer.value = temp32;
+ break;
+
+ case AML_FIND_SET_RIGHT_BIT_OP: /* find_set_right_bit (Operand, Result) */
+
+ return_desc->integer.value = operand[0]->integer.value;
+
+ /*
+ * The Acpi specification describes Integer type as a little
+ * endian unsigned value, so this boundary condition is valid.
+ */
+ for (temp32 = 0; return_desc->integer.value &&
+ temp32 < ACPI_INTEGER_BIT_SIZE; ++temp32) {
+ return_desc->integer.value <<= 1;
+ }
+
+ /* Since the bit position is one-based, subtract from 33 (65) */
+
+ return_desc->integer.value =
+ temp32 ==
+ 0 ? 0 : (ACPI_INTEGER_BIT_SIZE + 1) - temp32;
+ break;
+
+ case AML_FROM_BCD_OP: /* from_bcd (BCDValue, Result) */
+ /*
+ * The 64-bit ACPI integer can hold 16 4-bit BCD characters
+ * (if table is 32-bit, integer can hold 8 BCD characters)
+ * Convert each 4-bit BCD value
+ */
+ power_of_ten = 1;
+ return_desc->integer.value = 0;
+ digit = operand[0]->integer.value;
+
+ /* Convert each BCD digit (each is one nybble wide) */
+
+ for (i = 0;
+ (i < acpi_gbl_integer_nybble_width) && (digit > 0);
+ i++) {
+
+ /* Get the least significant 4-bit BCD digit */
+
+ temp32 = ((u32) digit) & 0xF;
+
+ /* Check the range of the digit */
+
+ if (temp32 > 9) {
+ ACPI_ERROR((AE_INFO,
+ "BCD digit too large (not decimal): 0x%X",
+ temp32));
+
+ status = AE_AML_NUMERIC_OVERFLOW;
+ goto cleanup;
+ }
+
+ /* Sum the digit into the result with the current power of 10 */
+
+ return_desc->integer.value +=
+ (((u64) temp32) * power_of_ten);
+
+ /* Shift to next BCD digit */
+
+ digit >>= 4;
+
+ /* Next power of 10 */
+
+ power_of_ten *= 10;
+ }
+ break;
+
+ case AML_LOAD_OP: /* Result1 = Load (Operand[0], Result1) */
+
+ return_desc->integer.value = 0;
+ status =
+ acpi_ex_load_op(operand[0], return_desc,
+ walk_state);
+ if (ACPI_SUCCESS(status)) {
+
+ /* Return -1 (non-zero) indicates success */
+
+ return_desc->integer.value = 0xFFFFFFFFFFFFFFFF;
+ }
+ break;
+
+ case AML_TO_BCD_OP: /* to_bcd (Operand, Result) */
+
+ return_desc->integer.value = 0;
+ digit = operand[0]->integer.value;
+
+ /* Each BCD digit is one nybble wide */
+
+ for (i = 0;
+ (i < acpi_gbl_integer_nybble_width) && (digit > 0);
+ i++) {
+ (void)acpi_ut_short_divide(digit, 10, &digit,
+ &temp32);
+
+ /*
+ * Insert the BCD digit that resides in the
+ * remainder from above
+ */
+ return_desc->integer.value |=
+ (((u64) temp32) << ACPI_MUL_4(i));
+ }
+
+ /* Overflow if there is any data left in Digit */
+
+ if (digit > 0) {
+ ACPI_ERROR((AE_INFO,
+ "Integer too large to convert to BCD: 0x%8.8X%8.8X",
+ ACPI_FORMAT_UINT64(operand[0]->
+ integer.value)));
+ status = AE_AML_NUMERIC_OVERFLOW;
+ goto cleanup;
+ }
+ break;
+
+ case AML_CONDITIONAL_REF_OF_OP: /* cond_ref_of (source_object, Result) */
+ /*
+ * This op is a little strange because the internal return value is
+ * different than the return value stored in the result descriptor
+ * (There are really two return values)
+ */
+ if ((struct acpi_namespace_node *)operand[0] ==
+ acpi_gbl_root_node) {
+ /*
+ * This means that the object does not exist in the namespace,
+ * return FALSE
+ */
+ return_desc->integer.value = 0;
+ goto cleanup;
+ }
+
+ /* Get the object reference, store it, and remove our reference */
+
+ status = acpi_ex_get_object_reference(operand[0],
+ &return_desc2,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ status =
+ acpi_ex_store(return_desc2, operand[1], walk_state);
+ acpi_ut_remove_reference(return_desc2);
+
+ /* The object exists in the namespace, return TRUE */
+
+ return_desc->integer.value = ACPI_UINT64_MAX;
+ goto cleanup;
+
+ default:
+
+ /* No other opcodes get here */
+
+ break;
+ }
+ break;
+
+ case AML_STORE_OP: /* Store (Source, Target) */
+ /*
+ * A store operand is typically a number, string, buffer or lvalue
+ * Be careful about deleting the source object,
+ * since the object itself may have been stored.
+ */
+ status = acpi_ex_store(operand[0], operand[1], walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* It is possible that the Store already produced a return object */
+
+ if (!walk_state->result_obj) {
+ /*
+ * Normally, we would remove a reference on the Operand[0]
+ * parameter; But since it is being used as the internal return
+ * object (meaning we would normally increment it), the two
+ * cancel out, and we simply don't do anything.
+ */
+ walk_state->result_obj = operand[0];
+ walk_state->operands[0] = NULL; /* Prevent deletion */
+ }
+ return_ACPI_STATUS(status);
+
+ /*
+ * ACPI 2.0 Opcodes
+ */
+ case AML_COPY_OBJECT_OP: /* copy_object (Source, Target) */
+
+ status =
+ acpi_ut_copy_iobject_to_iobject(operand[0], &return_desc,
+ walk_state);
+ break;
+
+ case AML_TO_DECIMAL_STRING_OP: /* to_decimal_string (Data, Result) */
+
+ status =
+ acpi_ex_convert_to_string(operand[0], &return_desc,
+ ACPI_EXPLICIT_CONVERT_DECIMAL);
+ if (return_desc == operand[0]) {
+
+ /* No conversion performed, add ref to handle return value */
+
+ acpi_ut_add_reference(return_desc);
+ }
+ break;
+
+ case AML_TO_HEX_STRING_OP: /* to_hex_string (Data, Result) */
+
+ status =
+ acpi_ex_convert_to_string(operand[0], &return_desc,
+ ACPI_EXPLICIT_CONVERT_HEX);
+ if (return_desc == operand[0]) {
+
+ /* No conversion performed, add ref to handle return value */
+
+ acpi_ut_add_reference(return_desc);
+ }
+ break;
+
+ case AML_TO_BUFFER_OP: /* to_buffer (Data, Result) */
+
+ status = acpi_ex_convert_to_buffer(operand[0], &return_desc);
+ if (return_desc == operand[0]) {
+
+ /* No conversion performed, add ref to handle return value */
+
+ acpi_ut_add_reference(return_desc);
+ }
+ break;
+
+ case AML_TO_INTEGER_OP: /* to_integer (Data, Result) */
+
+ /* Perform "explicit" conversion */
+
+ status =
+ acpi_ex_convert_to_integer(operand[0], &return_desc, 0);
+ if (return_desc == operand[0]) {
+
+ /* No conversion performed, add ref to handle return value */
+
+ acpi_ut_add_reference(return_desc);
+ }
+ break;
+
+ case AML_SHIFT_LEFT_BIT_OP: /* shift_left_bit (Source, bit_num) */
+ case AML_SHIFT_RIGHT_BIT_OP: /* shift_right_bit (Source, bit_num) */
+
+ /* These are two obsolete opcodes */
+
+ ACPI_ERROR((AE_INFO,
+ "%s is obsolete and not implemented",
+ acpi_ps_get_opcode_name(walk_state->opcode)));
+ status = AE_SUPPORT;
+ goto cleanup;
+
+ default: /* Unknown opcode */
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+ status = AE_AML_BAD_OPCODE;
+ goto cleanup;
+ }
+
+ if (ACPI_SUCCESS(status)) {
+
+ /* Store the return value computed above into the target object */
+
+ status = acpi_ex_store(return_desc, operand[1], walk_state);
+ }
+
+cleanup:
+
+ /* Delete return object on error */
+
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(return_desc);
+ }
+
+ /* Save return object on success */
+
+ else if (!walk_state->result_obj) {
+ walk_state->result_obj = return_desc;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_1A_0T_1R
+ *
+ * PARAMETERS: walk_state - Current state (contains AML opcode)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute opcode with one argument, no target, and a return value
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ union acpi_operand_object *temp_desc;
+ union acpi_operand_object *return_desc = NULL;
+ acpi_status status = AE_OK;
+ u32 type;
+ u64 value;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_0T_1R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ /* Examine the AML opcode */
+
+ switch (walk_state->opcode) {
+ case AML_LOGICAL_NOT_OP: /* LNot (Operand) */
+
+ return_desc = acpi_ut_create_integer_object((u64) 0);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /*
+ * Set result to ONES (TRUE) if Value == 0. Note:
+ * return_desc->Integer.Value is initially == 0 (FALSE) from above.
+ */
+ if (!operand[0]->integer.value) {
+ return_desc->integer.value = ACPI_UINT64_MAX;
+ }
+ break;
+
+ case AML_DECREMENT_OP: /* Decrement (Operand) */
+ case AML_INCREMENT_OP: /* Increment (Operand) */
+ /*
+ * Create a new integer. Can't just get the base integer and
+ * increment it because it may be an Arg or Field.
+ */
+ return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /*
+ * Since we are expecting a Reference operand, it can be either a
+ * NS Node or an internal object.
+ */
+ temp_desc = operand[0];
+ if (ACPI_GET_DESCRIPTOR_TYPE(temp_desc) ==
+ ACPI_DESC_TYPE_OPERAND) {
+
+ /* Internal reference object - prevent deletion */
+
+ acpi_ut_add_reference(temp_desc);
+ }
+
+ /*
+ * Convert the Reference operand to an Integer (This removes a
+ * reference on the Operand[0] object)
+ *
+ * NOTE: We use LNOT_OP here in order to force resolution of the
+ * reference operand to an actual integer.
+ */
+ status = acpi_ex_resolve_operands(AML_LOGICAL_NOT_OP,
+ &temp_desc, walk_state);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While resolving operands for [%s]",
+ acpi_ps_get_opcode_name(walk_state->
+ opcode)));
+
+ goto cleanup;
+ }
+
+ /*
+ * temp_desc is now guaranteed to be an Integer object --
+ * Perform the actual increment or decrement
+ */
+ if (walk_state->opcode == AML_INCREMENT_OP) {
+ return_desc->integer.value =
+ temp_desc->integer.value + 1;
+ } else {
+ return_desc->integer.value =
+ temp_desc->integer.value - 1;
+ }
+
+ /* Finished with this Integer object */
+
+ acpi_ut_remove_reference(temp_desc);
+
+ /*
+ * Store the result back (indirectly) through the original
+ * Reference object
+ */
+ status = acpi_ex_store(return_desc, operand[0], walk_state);
+ break;
+
+ case AML_OBJECT_TYPE_OP: /* object_type (source_object) */
+ /*
+ * Note: The operand is not resolved at this point because we want to
+ * get the associated object, not its value. For example, we don't
+ * want to resolve a field_unit to its value, we want the actual
+ * field_unit object.
+ */
+
+ /* Get the type of the base object */
+
+ status =
+ acpi_ex_resolve_multiple(walk_state, operand[0], &type,
+ NULL);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /* Allocate a descriptor to hold the type. */
+
+ return_desc = acpi_ut_create_integer_object((u64) type);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+ break;
+
+ case AML_SIZE_OF_OP: /* size_of (source_object) */
+ /*
+ * Note: The operand is not resolved at this point because we want to
+ * get the associated object, not its value.
+ */
+
+ /* Get the base object */
+
+ status =
+ acpi_ex_resolve_multiple(walk_state, operand[0], &type,
+ &temp_desc);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /*
+ * The type of the base object must be integer, buffer, string, or
+ * package. All others are not supported.
+ *
+ * NOTE: Integer is not specifically supported by the ACPI spec,
+ * but is supported implicitly via implicit operand conversion.
+ * rather than bother with conversion, we just use the byte width
+ * global (4 or 8 bytes).
+ */
+ switch (type) {
+ case ACPI_TYPE_INTEGER:
+
+ value = acpi_gbl_integer_byte_width;
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ value = temp_desc->string.length;
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ /* Buffer arguments may not be evaluated at this point */
+
+ status = acpi_ds_get_buffer_arguments(temp_desc);
+ value = temp_desc->buffer.length;
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ /* Package arguments may not be evaluated at this point */
+
+ status = acpi_ds_get_package_arguments(temp_desc);
+ value = temp_desc->package.count;
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Operand must be Buffer/Integer/String/Package"
+ " - found type %s",
+ acpi_ut_get_type_name(type)));
+
+ status = AE_AML_OPERAND_TYPE;
+ goto cleanup;
+ }
+
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /*
+ * Now that we have the size of the object, create a result
+ * object to hold the value
+ */
+ return_desc = acpi_ut_create_integer_object(value);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+ break;
+
+ case AML_REF_OF_OP: /* ref_of (source_object) */
+
+ status =
+ acpi_ex_get_object_reference(operand[0], &return_desc,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+ break;
+
+ case AML_DEREF_OF_OP: /* deref_of (obj_reference | String) */
+
+ /* Check for a method local or argument, or standalone String */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(operand[0]) ==
+ ACPI_DESC_TYPE_NAMED) {
+ temp_desc =
+ acpi_ns_get_attached_object((struct
+ acpi_namespace_node *)
+ operand[0]);
+ if (temp_desc
+ && ((temp_desc->common.type == ACPI_TYPE_STRING)
+ || (temp_desc->common.type ==
+ ACPI_TYPE_LOCAL_REFERENCE))) {
+ operand[0] = temp_desc;
+ acpi_ut_add_reference(temp_desc);
+ } else {
+ status = AE_AML_OPERAND_TYPE;
+ goto cleanup;
+ }
+ } else {
+ switch ((operand[0])->common.type) {
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ /*
+ * This is a deref_of (local_x | arg_x)
+ *
+ * Must resolve/dereference the local/arg reference first
+ */
+ switch (operand[0]->reference.class) {
+ case ACPI_REFCLASS_LOCAL:
+ case ACPI_REFCLASS_ARG:
+
+ /* Set Operand[0] to the value of the local/arg */
+
+ status =
+ acpi_ds_method_data_get_value
+ (operand[0]->reference.class,
+ operand[0]->reference.value,
+ walk_state, &temp_desc);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /*
+ * Delete our reference to the input object and
+ * point to the object just retrieved
+ */
+ acpi_ut_remove_reference(operand[0]);
+ operand[0] = temp_desc;
+ break;
+
+ case ACPI_REFCLASS_REFOF:
+
+ /* Get the object to which the reference refers */
+
+ temp_desc =
+ operand[0]->reference.object;
+ acpi_ut_remove_reference(operand[0]);
+ operand[0] = temp_desc;
+ break;
+
+ default:
+
+ /* Must be an Index op - handled below */
+ break;
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ break;
+
+ default:
+
+ status = AE_AML_OPERAND_TYPE;
+ goto cleanup;
+ }
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(operand[0]) !=
+ ACPI_DESC_TYPE_NAMED) {
+ if ((operand[0])->common.type == ACPI_TYPE_STRING) {
+ /*
+ * This is a deref_of (String). The string is a reference
+ * to a named ACPI object.
+ *
+ * 1) Find the owning Node
+ * 2) Dereference the node to an actual object. Could be a
+ * Field, so we need to resolve the node to a value.
+ */
+ status =
+ acpi_ns_get_node_unlocked(walk_state->
+ scope_info->scope.
+ node,
+ operand[0]->
+ string.pointer,
+ ACPI_NS_SEARCH_PARENT,
+ ACPI_CAST_INDIRECT_PTR
+ (struct
+ acpi_namespace_node,
+ &return_desc));
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ status =
+ acpi_ex_resolve_node_to_value
+ (ACPI_CAST_INDIRECT_PTR
+ (struct acpi_namespace_node, &return_desc),
+ walk_state);
+ goto cleanup;
+ }
+ }
+
+ /* Operand[0] may have changed from the code above */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(operand[0]) ==
+ ACPI_DESC_TYPE_NAMED) {
+ /*
+ * This is a deref_of (object_reference)
+ * Get the actual object from the Node (This is the dereference).
+ * This case may only happen when a local_x or arg_x is
+ * dereferenced above, or for references to device and
+ * thermal objects.
+ */
+ switch (((struct acpi_namespace_node *)operand[0])->
+ type) {
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_THERMAL:
+
+ /* These types have no node subobject, return the NS node */
+
+ return_desc = operand[0];
+ break;
+
+ default:
+ /* For most types, get the object attached to the node */
+
+ return_desc = acpi_ns_get_attached_object((struct acpi_namespace_node *)operand[0]);
+ acpi_ut_add_reference(return_desc);
+ break;
+ }
+ } else {
+ /*
+ * This must be a reference object produced by either the
+ * Index() or ref_of() operator
+ */
+ switch (operand[0]->reference.class) {
+ case ACPI_REFCLASS_INDEX:
+ /*
+ * The target type for the Index operator must be
+ * either a Buffer or a Package
+ */
+ switch (operand[0]->reference.target_type) {
+ case ACPI_TYPE_BUFFER_FIELD:
+
+ temp_desc =
+ operand[0]->reference.object;
+
+ /*
+ * Create a new object that contains one element of the
+ * buffer -- the element pointed to by the index.
+ *
+ * NOTE: index into a buffer is NOT a pointer to a
+ * sub-buffer of the main buffer, it is only a pointer to a
+ * single element (byte) of the buffer!
+ *
+ * Since we are returning the value of the buffer at the
+ * indexed location, we don't need to add an additional
+ * reference to the buffer itself.
+ */
+ return_desc =
+ acpi_ut_create_integer_object((u64)
+ temp_desc->buffer.pointer[operand[0]->reference.value]);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * Return the referenced element of the package. We must
+ * add another reference to the referenced object, however.
+ */
+ return_desc =
+ *(operand[0]->reference.where);
+ if (!return_desc) {
+ /*
+ * Element is NULL, do not allow the dereference.
+ * This provides compatibility with other ACPI
+ * implementations.
+ */
+ return_ACPI_STATUS
+ (AE_AML_UNINITIALIZED_ELEMENT);
+ }
+
+ acpi_ut_add_reference(return_desc);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown Index TargetType 0x%X in reference object %p",
+ operand[0]->reference.
+ target_type, operand[0]));
+
+ status = AE_AML_OPERAND_TYPE;
+ goto cleanup;
+ }
+ break;
+
+ case ACPI_REFCLASS_REFOF:
+
+ return_desc = operand[0]->reference.object;
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(return_desc) ==
+ ACPI_DESC_TYPE_NAMED) {
+ return_desc =
+ acpi_ns_get_attached_object((struct
+ acpi_namespace_node
+ *)
+ return_desc);
+ if (!return_desc) {
+ break;
+ }
+
+ /*
+ * June 2013:
+ * buffer_fields/field_units require additional resolution
+ */
+ switch (return_desc->common.type) {
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
+ status =
+ acpi_ex_read_data_from_field
+ (walk_state, return_desc,
+ &temp_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS
+ (status);
+ }
+
+ return_desc = temp_desc;
+ break;
+
+ default:
+
+ /* Add another reference to the object */
+
+ acpi_ut_add_reference
+ (return_desc);
+ break;
+ }
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown class in reference(%p) - 0x%2.2X",
+ operand[0],
+ operand[0]->reference.class));
+
+ status = AE_TYPE;
+ goto cleanup;
+ }
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+
+ status = AE_AML_BAD_OPCODE;
+ goto cleanup;
+ }
+
+cleanup:
+
+ /* Delete return object on error */
+
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(return_desc);
+ }
+
+ /* Save return object on success */
+
+ else {
+ walk_state->result_obj = return_desc;
+ }
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exoparg2.c b/drivers/acpi/acpica/exoparg2.c
new file mode 100644
index 000000000..ebf7c89d5
--- /dev/null
+++ b/drivers/acpi/acpica/exoparg2.c
@@ -0,0 +1,557 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exoparg2 - AML execution - opcodes with 2 arguments
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "acinterp.h"
+#include "acevents.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exoparg2")
+
+/*!
+ * Naming convention for AML interpreter execution routines.
+ *
+ * The routines that begin execution of AML opcodes are named with a common
+ * convention based upon the number of arguments, the number of target operands,
+ * and whether or not a value is returned:
+ *
+ * AcpiExOpcode_xA_yT_zR
+ *
+ * Where:
+ *
+ * xA - ARGUMENTS: The number of arguments (input operands) that are
+ * required for this opcode type (1 through 6 args).
+ * yT - TARGETS: The number of targets (output operands) that are required
+ * for this opcode type (0, 1, or 2 targets).
+ * zR - RETURN VALUE: Indicates whether this opcode type returns a value
+ * as the function return (0 or 1).
+ *
+ * The AcpiExOpcode* functions are called via the Dispatcher component with
+ * fully resolved operands.
+!*/
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_2A_0T_0R
+ *
+ * PARAMETERS: walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute opcode with two arguments, no target, and no return
+ * value.
+ *
+ * ALLOCATION: Deletes both operands
+ *
+ ******************************************************************************/
+acpi_status acpi_ex_opcode_2A_0T_0R(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ struct acpi_namespace_node *node;
+ u32 value;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_0T_0R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ /* Examine the opcode */
+
+ switch (walk_state->opcode) {
+ case AML_NOTIFY_OP: /* Notify (notify_object, notify_value) */
+
+ /* The first operand is a namespace node */
+
+ node = (struct acpi_namespace_node *)operand[0];
+
+ /* Second value is the notify value */
+
+ value = (u32) operand[1]->integer.value;
+
+ /* Are notifies allowed on this object? */
+
+ if (!acpi_ev_is_notify_object(node)) {
+ ACPI_ERROR((AE_INFO,
+ "Unexpected notify object type [%s]",
+ acpi_ut_get_type_name(node->type)));
+
+ status = AE_AML_OPERAND_TYPE;
+ break;
+ }
+
+ /*
+ * Dispatch the notify to the appropriate handler
+ * NOTE: the request is queued for execution after this method
+ * completes. The notify handlers are NOT invoked synchronously
+ * from this thread -- because handlers may in turn run other
+ * control methods.
+ */
+ status = acpi_ev_queue_notify_request(node, value);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+ status = AE_AML_BAD_OPCODE;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_2A_2T_1R
+ *
+ * PARAMETERS: walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute a dyadic operator (2 operands) with 2 output targets
+ * and one implicit return value.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_opcode_2A_2T_1R(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ union acpi_operand_object *return_desc1 = NULL;
+ union acpi_operand_object *return_desc2 = NULL;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_2T_1R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ /* Execute the opcode */
+
+ switch (walk_state->opcode) {
+ case AML_DIVIDE_OP:
+
+ /* Divide (Dividend, Divisor, remainder_result quotient_result) */
+
+ return_desc1 =
+ acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!return_desc1) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ return_desc2 =
+ acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!return_desc2) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Quotient to return_desc1, remainder to return_desc2 */
+
+ status = acpi_ut_divide(operand[0]->integer.value,
+ operand[1]->integer.value,
+ &return_desc1->integer.value,
+ &return_desc2->integer.value);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+
+ status = AE_AML_BAD_OPCODE;
+ goto cleanup;
+ }
+
+ /* Store the results to the target reference operands */
+
+ status = acpi_ex_store(return_desc2, operand[2], walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ status = acpi_ex_store(return_desc1, operand[3], walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+cleanup:
+ /*
+ * Since the remainder is not returned indirectly, remove a reference to
+ * it. Only the quotient is returned indirectly.
+ */
+ acpi_ut_remove_reference(return_desc2);
+
+ if (ACPI_FAILURE(status)) {
+
+ /* Delete the return object */
+
+ acpi_ut_remove_reference(return_desc1);
+ }
+
+ /* Save return object (the remainder) on success */
+
+ else {
+ walk_state->result_obj = return_desc1;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_2A_1T_1R
+ *
+ * PARAMETERS: walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute opcode with two arguments, one target, and a return
+ * value.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ union acpi_operand_object *return_desc = NULL;
+ u64 index;
+ acpi_status status = AE_OK;
+ acpi_size length = 0;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_1T_1R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ /* Execute the opcode */
+
+ if (walk_state->op_info->flags & AML_MATH) {
+
+ /* All simple math opcodes (add, etc.) */
+
+ return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ return_desc->integer.value =
+ acpi_ex_do_math_op(walk_state->opcode,
+ operand[0]->integer.value,
+ operand[1]->integer.value);
+ goto store_result_to_target;
+ }
+
+ switch (walk_state->opcode) {
+ case AML_MOD_OP: /* Mod (Dividend, Divisor, remainder_result (ACPI 2.0) */
+
+ return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* return_desc will contain the remainder */
+
+ status = acpi_ut_divide(operand[0]->integer.value,
+ operand[1]->integer.value,
+ NULL, &return_desc->integer.value);
+ break;
+
+ case AML_CONCATENATE_OP: /* Concatenate (Data1, Data2, Result) */
+
+ status =
+ acpi_ex_do_concatenate(operand[0], operand[1], &return_desc,
+ walk_state);
+ break;
+
+ case AML_TO_STRING_OP: /* to_string (Buffer, Length, Result) (ACPI 2.0) */
+ /*
+ * Input object is guaranteed to be a buffer at this point (it may have
+ * been converted.) Copy the raw buffer data to a new object of
+ * type String.
+ */
+
+ /*
+ * Get the length of the new string. It is the smallest of:
+ * 1) Length of the input buffer
+ * 2) Max length as specified in the to_string operator
+ * 3) Length of input buffer up to a zero byte (null terminator)
+ *
+ * NOTE: A length of zero is ok, and will create a zero-length, null
+ * terminated string.
+ */
+ while ((length < operand[0]->buffer.length) && /* Length of input buffer */
+ (length < operand[1]->integer.value) && /* Length operand */
+ (operand[0]->buffer.pointer[length])) { /* Null terminator */
+ length++;
+ }
+
+ /* Allocate a new string object */
+
+ return_desc = acpi_ut_create_string_object(length);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /*
+ * Copy the raw buffer data with no transform.
+ * (NULL terminated already)
+ */
+ memcpy(return_desc->string.pointer,
+ operand[0]->buffer.pointer, length);
+ break;
+
+ case AML_CONCATENATE_TEMPLATE_OP:
+
+ /* concatenate_res_template (Buffer, Buffer, Result) (ACPI 2.0) */
+
+ status =
+ acpi_ex_concat_template(operand[0], operand[1],
+ &return_desc, walk_state);
+ break;
+
+ case AML_INDEX_OP: /* Index (Source Index Result) */
+
+ /* Create the internal return object */
+
+ return_desc =
+ acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Initialize the Index reference object */
+
+ index = operand[1]->integer.value;
+ return_desc->reference.value = (u32) index;
+ return_desc->reference.class = ACPI_REFCLASS_INDEX;
+
+ /*
+ * At this point, the Source operand is a String, Buffer, or Package.
+ * Verify that the index is within range.
+ */
+ switch ((operand[0])->common.type) {
+ case ACPI_TYPE_STRING:
+
+ if (index >= operand[0]->string.length) {
+ length = operand[0]->string.length;
+ status = AE_AML_STRING_LIMIT;
+ }
+
+ return_desc->reference.target_type =
+ ACPI_TYPE_BUFFER_FIELD;
+ return_desc->reference.index_pointer =
+ &(operand[0]->buffer.pointer[index]);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ if (index >= operand[0]->buffer.length) {
+ length = operand[0]->buffer.length;
+ status = AE_AML_BUFFER_LIMIT;
+ }
+
+ return_desc->reference.target_type =
+ ACPI_TYPE_BUFFER_FIELD;
+ return_desc->reference.index_pointer =
+ &(operand[0]->buffer.pointer[index]);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ if (index >= operand[0]->package.count) {
+ length = operand[0]->package.count;
+ status = AE_AML_PACKAGE_LIMIT;
+ }
+
+ return_desc->reference.target_type = ACPI_TYPE_PACKAGE;
+ return_desc->reference.where =
+ &operand[0]->package.elements[index];
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid object type: %X",
+ (operand[0])->common.type));
+ status = AE_AML_INTERNAL;
+ goto cleanup;
+ }
+
+ /* Failure means that the Index was beyond the end of the object */
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_BIOS_EXCEPTION((AE_INFO, status,
+ "Index (0x%X%8.8X) is beyond end of object (length 0x%X)",
+ ACPI_FORMAT_UINT64(index),
+ (u32)length));
+ goto cleanup;
+ }
+
+ /*
+ * Save the target object and add a reference to it for the life
+ * of the index
+ */
+ return_desc->reference.object = operand[0];
+ acpi_ut_add_reference(operand[0]);
+
+ /* Store the reference to the Target */
+
+ status = acpi_ex_store(return_desc, operand[2], walk_state);
+
+ /* Return the reference */
+
+ walk_state->result_obj = return_desc;
+ goto cleanup;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+ status = AE_AML_BAD_OPCODE;
+ break;
+ }
+
+store_result_to_target:
+
+ if (ACPI_SUCCESS(status)) {
+ /*
+ * Store the result of the operation (which is now in return_desc) into
+ * the Target descriptor.
+ */
+ status = acpi_ex_store(return_desc, operand[2], walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ if (!walk_state->result_obj) {
+ walk_state->result_obj = return_desc;
+ }
+ }
+
+cleanup:
+
+ /* Delete return object on error */
+
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(return_desc);
+ walk_state->result_obj = NULL;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_2A_0T_1R
+ *
+ * PARAMETERS: walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute opcode with 2 arguments, no target, and a return value
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_opcode_2A_0T_1R(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ union acpi_operand_object *return_desc = NULL;
+ acpi_status status = AE_OK;
+ u8 logical_result = FALSE;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_0T_1R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ /* Create the internal return object */
+
+ return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Execute the Opcode */
+
+ if (walk_state->op_info->flags & AML_LOGICAL_NUMERIC) {
+
+ /* logical_op (Operand0, Operand1) */
+
+ status = acpi_ex_do_logical_numeric_op(walk_state->opcode,
+ operand[0]->integer.
+ value,
+ operand[1]->integer.
+ value, &logical_result);
+ goto store_logical_result;
+ } else if (walk_state->op_info->flags & AML_LOGICAL) {
+
+ /* logical_op (Operand0, Operand1) */
+
+ status = acpi_ex_do_logical_op(walk_state->opcode, operand[0],
+ operand[1], &logical_result);
+ goto store_logical_result;
+ }
+
+ switch (walk_state->opcode) {
+ case AML_ACQUIRE_OP: /* Acquire (mutex_object, Timeout) */
+
+ status =
+ acpi_ex_acquire_mutex(operand[1], operand[0], walk_state);
+ if (status == AE_TIME) {
+ logical_result = TRUE; /* TRUE = Acquire timed out */
+ status = AE_OK;
+ }
+ break;
+
+ case AML_WAIT_OP: /* Wait (event_object, Timeout) */
+
+ status = acpi_ex_system_wait_event(operand[1], operand[0]);
+ if (status == AE_TIME) {
+ logical_result = TRUE; /* TRUE, Wait timed out */
+ status = AE_OK;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+
+ status = AE_AML_BAD_OPCODE;
+ goto cleanup;
+ }
+
+store_logical_result:
+ /*
+ * Set return value to according to logical_result. logical TRUE (all ones)
+ * Default is FALSE (zero)
+ */
+ if (logical_result) {
+ return_desc->integer.value = ACPI_UINT64_MAX;
+ }
+
+cleanup:
+
+ /* Delete return object on error */
+
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(return_desc);
+ }
+
+ /* Save return object on success */
+
+ else {
+ walk_state->result_obj = return_desc;
+ }
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c
new file mode 100644
index 000000000..4b069bd6b
--- /dev/null
+++ b/drivers/acpi/acpica/exoparg3.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exoparg3 - AML execution - opcodes with 3 arguments
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "acparser.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exoparg3")
+
+/*!
+ * Naming convention for AML interpreter execution routines.
+ *
+ * The routines that begin execution of AML opcodes are named with a common
+ * convention based upon the number of arguments, the number of target operands,
+ * and whether or not a value is returned:
+ *
+ * AcpiExOpcode_xA_yT_zR
+ *
+ * Where:
+ *
+ * xA - ARGUMENTS: The number of arguments (input operands) that are
+ * required for this opcode type (1 through 6 args).
+ * yT - TARGETS: The number of targets (output operands) that are required
+ * for this opcode type (0, 1, or 2 targets).
+ * zR - RETURN VALUE: Indicates whether this opcode type returns a value
+ * as the function return (0 or 1).
+ *
+ * The AcpiExOpcode* functions are called via the Dispatcher component with
+ * fully resolved operands.
+!*/
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_3A_0T_0R
+ *
+ * PARAMETERS: walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute Triadic operator (3 operands)
+ *
+ ******************************************************************************/
+acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ struct acpi_signal_fatal_info *fatal;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_3A_0T_0R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ switch (walk_state->opcode) {
+ case AML_FATAL_OP: /* Fatal (fatal_type fatal_code fatal_arg) */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "FatalOp: Type %X Code %X Arg %X "
+ "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n",
+ (u32)operand[0]->integer.value,
+ (u32)operand[1]->integer.value,
+ (u32)operand[2]->integer.value));
+
+ fatal = ACPI_ALLOCATE(sizeof(struct acpi_signal_fatal_info));
+ if (fatal) {
+ fatal->type = (u32) operand[0]->integer.value;
+ fatal->code = (u32) operand[1]->integer.value;
+ fatal->argument = (u32) operand[2]->integer.value;
+ }
+
+ /* Always signal the OS! */
+
+ status = acpi_os_signal(ACPI_SIGNAL_FATAL, fatal);
+
+ /* Might return while OS is shutting down, just continue */
+
+ ACPI_FREE(fatal);
+ goto cleanup;
+
+ case AML_EXTERNAL_OP:
+ /*
+ * If the interpreter sees this opcode, just ignore it. The External
+ * op is intended for use by disassemblers in order to properly
+ * disassemble control method invocations. The opcode or group of
+ * opcodes should be surrounded by an "if (0)" clause to ensure that
+ * AML interpreters never see the opcode. Thus, something is
+ * wrong if an external opcode ever gets here.
+ */
+ ACPI_ERROR((AE_INFO, "Executed External Op"));
+ status = AE_OK;
+ goto cleanup;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+
+ status = AE_AML_BAD_OPCODE;
+ goto cleanup;
+ }
+
+cleanup:
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_3A_1T_1R
+ *
+ * PARAMETERS: walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute Triadic operator (3 operands)
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ union acpi_operand_object *return_desc = NULL;
+ char *buffer = NULL;
+ acpi_status status = AE_OK;
+ u64 index;
+ acpi_size length;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_3A_1T_1R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ switch (walk_state->opcode) {
+ case AML_MID_OP: /* Mid (Source[0], Index[1], Length[2], Result[3]) */
+ /*
+ * Create the return object. The Source operand is guaranteed to be
+ * either a String or a Buffer, so just use its type.
+ */
+ return_desc = acpi_ut_create_internal_object((operand[0])->
+ common.type);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Get the Integer values from the objects */
+
+ index = operand[1]->integer.value;
+ length = (acpi_size)operand[2]->integer.value;
+
+ /*
+ * If the index is beyond the length of the String/Buffer, or if the
+ * requested length is zero, return a zero-length String/Buffer
+ */
+ if (index >= operand[0]->string.length) {
+ length = 0;
+ }
+
+ /* Truncate request if larger than the actual String/Buffer */
+
+ else if ((index + length) > operand[0]->string.length) {
+ length =
+ (acpi_size)operand[0]->string.length -
+ (acpi_size)index;
+ }
+
+ /* Strings always have a sub-pointer, not so for buffers */
+
+ switch ((operand[0])->common.type) {
+ case ACPI_TYPE_STRING:
+
+ /* Always allocate a new buffer for the String */
+
+ buffer = ACPI_ALLOCATE_ZEROED((acpi_size)length + 1);
+ if (!buffer) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ /* If the requested length is zero, don't allocate a buffer */
+
+ if (length > 0) {
+
+ /* Allocate a new buffer for the Buffer */
+
+ buffer = ACPI_ALLOCATE_ZEROED(length);
+ if (!buffer) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+ }
+ break;
+
+ default: /* Should not happen */
+
+ status = AE_AML_OPERAND_TYPE;
+ goto cleanup;
+ }
+
+ if (buffer) {
+
+ /* We have a buffer, copy the portion requested */
+
+ memcpy(buffer,
+ operand[0]->string.pointer + index, length);
+ }
+
+ /* Set the length of the new String/Buffer */
+
+ return_desc->string.pointer = buffer;
+ return_desc->string.length = (u32) length;
+
+ /* Mark buffer initialized */
+
+ return_desc->buffer.flags |= AOPOBJ_DATA_VALID;
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+
+ status = AE_AML_BAD_OPCODE;
+ goto cleanup;
+ }
+
+ /* Store the result in the target */
+
+ status = acpi_ex_store(return_desc, operand[3], walk_state);
+
+cleanup:
+
+ /* Delete return object on error */
+
+ if (ACPI_FAILURE(status) || walk_state->result_obj) {
+ acpi_ut_remove_reference(return_desc);
+ walk_state->result_obj = NULL;
+ } else {
+ /* Set the return object and exit */
+
+ walk_state->result_obj = return_desc;
+ }
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c
new file mode 100644
index 000000000..2a506ef38
--- /dev/null
+++ b/drivers/acpi/acpica/exoparg6.c
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exoparg6 - AML execution - opcodes with 6 arguments
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "acparser.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exoparg6")
+
+/*!
+ * Naming convention for AML interpreter execution routines.
+ *
+ * The routines that begin execution of AML opcodes are named with a common
+ * convention based upon the number of arguments, the number of target operands,
+ * and whether or not a value is returned:
+ *
+ * AcpiExOpcode_xA_yT_zR
+ *
+ * Where:
+ *
+ * xA - ARGUMENTS: The number of arguments (input operands) that are
+ * required for this opcode type (1 through 6 args).
+ * yT - TARGETS: The number of targets (output operands) that are required
+ * for this opcode type (0, 1, or 2 targets).
+ * zR - RETURN VALUE: Indicates whether this opcode type returns a value
+ * as the function return (0 or 1).
+ *
+ * The AcpiExOpcode* functions are called via the Dispatcher component with
+ * fully resolved operands.
+!*/
+/* Local prototypes */
+static u8
+acpi_ex_do_match(u32 match_op,
+ union acpi_operand_object *package_obj,
+ union acpi_operand_object *match_obj);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_do_match
+ *
+ * PARAMETERS: match_op - The AML match operand
+ * package_obj - Object from the target package
+ * match_obj - Object to be matched
+ *
+ * RETURN: TRUE if the match is successful, FALSE otherwise
+ *
+ * DESCRIPTION: Implements the low-level match for the ASL Match operator.
+ * Package elements will be implicitly converted to the type of
+ * the match object (Integer/Buffer/String).
+ *
+ ******************************************************************************/
+
+static u8
+acpi_ex_do_match(u32 match_op,
+ union acpi_operand_object *package_obj,
+ union acpi_operand_object *match_obj)
+{
+ u8 logical_result = TRUE;
+ acpi_status status;
+
+ /*
+ * Note: Since the package_obj/match_obj ordering is opposite to that of
+ * the standard logical operators, we have to reverse them when we call
+ * do_logical_op in order to make the implicit conversion rules work
+ * correctly. However, this means we have to flip the entire equation
+ * also. A bit ugly perhaps, but overall, better than fussing the
+ * parameters around at runtime, over and over again.
+ *
+ * Below, P[i] refers to the package element, M refers to the Match object.
+ */
+ switch (match_op) {
+ case MATCH_MTR:
+
+ /* Always true */
+
+ break;
+
+ case MATCH_MEQ:
+ /*
+ * True if equal: (P[i] == M)
+ * Change to: (M == P[i])
+ */
+ status =
+ acpi_ex_do_logical_op(AML_LOGICAL_EQUAL_OP, match_obj,
+ package_obj, &logical_result);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+ break;
+
+ case MATCH_MLE:
+ /*
+ * True if less than or equal: (P[i] <= M) (P[i] not_greater than M)
+ * Change to: (M >= P[i]) (M not_less than P[i])
+ */
+ status =
+ acpi_ex_do_logical_op(AML_LOGICAL_LESS_OP, match_obj,
+ package_obj, &logical_result);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+ logical_result = (u8) ! logical_result;
+ break;
+
+ case MATCH_MLT:
+ /*
+ * True if less than: (P[i] < M)
+ * Change to: (M > P[i])
+ */
+ status =
+ acpi_ex_do_logical_op(AML_LOGICAL_GREATER_OP, match_obj,
+ package_obj, &logical_result);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+ break;
+
+ case MATCH_MGE:
+ /*
+ * True if greater than or equal: (P[i] >= M) (P[i] not_less than M)
+ * Change to: (M <= P[i]) (M not_greater than P[i])
+ */
+ status =
+ acpi_ex_do_logical_op(AML_LOGICAL_GREATER_OP, match_obj,
+ package_obj, &logical_result);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+ logical_result = (u8) ! logical_result;
+ break;
+
+ case MATCH_MGT:
+ /*
+ * True if greater than: (P[i] > M)
+ * Change to: (M < P[i])
+ */
+ status =
+ acpi_ex_do_logical_op(AML_LOGICAL_LESS_OP, match_obj,
+ package_obj, &logical_result);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+ break;
+
+ default:
+
+ /* Undefined */
+
+ return (FALSE);
+ }
+
+ return (logical_result);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_opcode_6A_0T_1R
+ *
+ * PARAMETERS: walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute opcode with 6 arguments, no target, and a return value
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object **operand = &walk_state->operands[0];
+ union acpi_operand_object *return_desc = NULL;
+ acpi_status status = AE_OK;
+ u64 index;
+ union acpi_operand_object *this_element;
+
+ ACPI_FUNCTION_TRACE_STR(ex_opcode_6A_0T_1R,
+ acpi_ps_get_opcode_name(walk_state->opcode));
+
+ switch (walk_state->opcode) {
+ case AML_MATCH_OP:
+ /*
+ * Match (search_pkg[0], match_op1[1], match_obj1[2],
+ * match_op2[3], match_obj2[4], start_index[5])
+ */
+
+ /* Validate both Match Term Operators (MTR, MEQ, etc.) */
+
+ if ((operand[1]->integer.value > MAX_MATCH_OPERATOR) ||
+ (operand[3]->integer.value > MAX_MATCH_OPERATOR)) {
+ ACPI_ERROR((AE_INFO, "Match operator out of range"));
+ status = AE_AML_OPERAND_VALUE;
+ goto cleanup;
+ }
+
+ /* Get the package start_index, validate against the package length */
+
+ index = operand[5]->integer.value;
+ if (index >= operand[0]->package.count) {
+ ACPI_ERROR((AE_INFO,
+ "Index (0x%8.8X%8.8X) beyond package end (0x%X)",
+ ACPI_FORMAT_UINT64(index),
+ operand[0]->package.count));
+ status = AE_AML_PACKAGE_LIMIT;
+ goto cleanup;
+ }
+
+ /* Create an integer for the return value */
+ /* Default return value is ACPI_UINT64_MAX if no match found */
+
+ return_desc = acpi_ut_create_integer_object(ACPI_UINT64_MAX);
+ if (!return_desc) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+
+ }
+
+ /*
+ * Examine each element until a match is found. Both match conditions
+ * must be satisfied for a match to occur. Within the loop,
+ * "continue" signifies that the current element does not match
+ * and the next should be examined.
+ *
+ * Upon finding a match, the loop will terminate via "break" at
+ * the bottom. If it terminates "normally", match_value will be
+ * ACPI_UINT64_MAX (Ones) (its initial value) indicating that no
+ * match was found.
+ */
+ for (; index < operand[0]->package.count; index++) {
+
+ /* Get the current package element */
+
+ this_element = operand[0]->package.elements[index];
+
+ /* Treat any uninitialized (NULL) elements as non-matching */
+
+ if (!this_element) {
+ continue;
+ }
+
+ /*
+ * Both match conditions must be satisfied. Execution of a continue
+ * (proceed to next iteration of enclosing for loop) signifies a
+ * non-match.
+ */
+ if (!acpi_ex_do_match((u32) operand[1]->integer.value,
+ this_element, operand[2])) {
+ continue;
+ }
+
+ if (!acpi_ex_do_match((u32) operand[3]->integer.value,
+ this_element, operand[4])) {
+ continue;
+ }
+
+ /* Match found: Index is the return value */
+
+ return_desc->integer.value = index;
+ break;
+ }
+ break;
+
+ case AML_LOAD_TABLE_OP:
+
+ status = acpi_ex_load_table_op(walk_state, &return_desc);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
+ walk_state->opcode));
+
+ status = AE_AML_BAD_OPCODE;
+ goto cleanup;
+ }
+
+cleanup:
+
+ /* Delete return object on error */
+
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(return_desc);
+ }
+
+ /* Save return object on success */
+
+ else {
+ walk_state->result_obj = return_desc;
+ }
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c
new file mode 100644
index 000000000..08f067973
--- /dev/null
+++ b/drivers/acpi/acpica/exprep.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exprep - ACPI AML field prep utilities
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlcode.h"
+#include "acnamesp.h"
+#include "acdispat.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exprep")
+
+/* Local prototypes */
+static u32
+acpi_ex_decode_field_access(union acpi_operand_object *obj_desc,
+ u8 field_flags, u32 * return_byte_alignment);
+
+#ifdef ACPI_UNDER_DEVELOPMENT
+
+static u32
+acpi_ex_generate_access(u32 field_bit_offset,
+ u32 field_bit_length, u32 region_length);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_generate_access
+ *
+ * PARAMETERS: field_bit_offset - Start of field within parent region/buffer
+ * field_bit_length - Length of field in bits
+ * region_length - Length of parent in bytes
+ *
+ * RETURN: Field granularity (8, 16, 32 or 64) and
+ * byte_alignment (1, 2, 3, or 4)
+ *
+ * DESCRIPTION: Generate an optimal access width for fields defined with the
+ * any_acc keyword.
+ *
+ * NOTE: Need to have the region_length in order to check for boundary
+ * conditions (end-of-region). However, the region_length is a deferred
+ * operation. Therefore, to complete this implementation, the generation
+ * of this access width must be deferred until the region length has
+ * been evaluated.
+ *
+ ******************************************************************************/
+
+static u32
+acpi_ex_generate_access(u32 field_bit_offset,
+ u32 field_bit_length, u32 region_length)
+{
+ u32 field_byte_length;
+ u32 field_byte_offset;
+ u32 field_byte_end_offset;
+ u32 access_byte_width;
+ u32 field_start_offset;
+ u32 field_end_offset;
+ u32 minimum_access_width = 0xFFFFFFFF;
+ u32 minimum_accesses = 0xFFFFFFFF;
+ u32 accesses;
+
+ ACPI_FUNCTION_TRACE(ex_generate_access);
+
+ /* Round Field start offset and length to "minimal" byte boundaries */
+
+ field_byte_offset = ACPI_DIV_8(ACPI_ROUND_DOWN(field_bit_offset, 8));
+
+ field_byte_end_offset =
+ ACPI_DIV_8(ACPI_ROUND_UP(field_bit_length + field_bit_offset, 8));
+
+ field_byte_length = field_byte_end_offset - field_byte_offset;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Bit length %u, Bit offset %u\n",
+ field_bit_length, field_bit_offset));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Byte Length %u, Byte Offset %u, End Offset %u\n",
+ field_byte_length, field_byte_offset,
+ field_byte_end_offset));
+
+ /*
+ * Iterative search for the maximum access width that is both aligned
+ * and does not go beyond the end of the region
+ *
+ * Start at byte_acc and work upwards to qword_acc max. (1,2,4,8 bytes)
+ */
+ for (access_byte_width = 1; access_byte_width <= 8;
+ access_byte_width <<= 1) {
+ /*
+ * 1) Round end offset up to next access boundary and make sure that
+ * this does not go beyond the end of the parent region.
+ * 2) When the Access width is greater than the field_byte_length, we
+ * are done. (This does not optimize for the perfectly aligned
+ * case yet).
+ */
+ if (ACPI_ROUND_UP(field_byte_end_offset, access_byte_width) <=
+ region_length) {
+ field_start_offset =
+ ACPI_ROUND_DOWN(field_byte_offset,
+ access_byte_width) /
+ access_byte_width;
+
+ field_end_offset =
+ ACPI_ROUND_UP((field_byte_length +
+ field_byte_offset),
+ access_byte_width) /
+ access_byte_width;
+
+ accesses = field_end_offset - field_start_offset;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "AccessWidth %u end is within region\n",
+ access_byte_width));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Field Start %u, Field End %u -- requires %u accesses\n",
+ field_start_offset, field_end_offset,
+ accesses));
+
+ /* Single access is optimal */
+
+ if (accesses <= 1) {
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Entire field can be accessed "
+ "with one operation of size %u\n",
+ access_byte_width));
+ return_VALUE(access_byte_width);
+ }
+
+ /*
+ * Fits in the region, but requires more than one read/write.
+ * try the next wider access on next iteration
+ */
+ if (accesses < minimum_accesses) {
+ minimum_accesses = accesses;
+ minimum_access_width = access_byte_width;
+ }
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "AccessWidth %u end is NOT within region\n",
+ access_byte_width));
+ if (access_byte_width == 1) {
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Field goes beyond end-of-region!\n"));
+
+ /* Field does not fit in the region at all */
+
+ return_VALUE(0);
+ }
+
+ /*
+ * This width goes beyond the end-of-region, back off to
+ * previous access
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Backing off to previous optimal access width of %u\n",
+ minimum_access_width));
+ return_VALUE(minimum_access_width);
+ }
+ }
+
+ /*
+ * Could not read/write field with one operation,
+ * just use max access width
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Cannot access field in one operation, using width 8\n"));
+
+ return_VALUE(8);
+}
+#endif /* ACPI_UNDER_DEVELOPMENT */
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_decode_field_access
+ *
+ * PARAMETERS: obj_desc - Field object
+ * field_flags - Encoded fieldflags (contains access bits)
+ * return_byte_alignment - Where the byte alignment is returned
+ *
+ * RETURN: Field granularity (8, 16, 32 or 64) and
+ * byte_alignment (1, 2, 3, or 4)
+ *
+ * DESCRIPTION: Decode the access_type bits of a field definition.
+ *
+ ******************************************************************************/
+
+static u32
+acpi_ex_decode_field_access(union acpi_operand_object *obj_desc,
+ u8 field_flags, u32 * return_byte_alignment)
+{
+ u32 access;
+ u32 byte_alignment;
+ u32 bit_length;
+
+ ACPI_FUNCTION_TRACE(ex_decode_field_access);
+
+ access = (field_flags & AML_FIELD_ACCESS_TYPE_MASK);
+
+ switch (access) {
+ case AML_FIELD_ACCESS_ANY:
+
+#ifdef ACPI_UNDER_DEVELOPMENT
+ byte_alignment =
+ acpi_ex_generate_access(obj_desc->common_field.
+ start_field_bit_offset,
+ obj_desc->common_field.bit_length,
+ 0xFFFFFFFF
+ /* Temp until we pass region_length as parameter */
+ );
+ bit_length = byte_alignment * 8;
+#endif
+
+ byte_alignment = 1;
+ bit_length = 8;
+ break;
+
+ case AML_FIELD_ACCESS_BYTE:
+ case AML_FIELD_ACCESS_BUFFER: /* ACPI 2.0 (SMBus Buffer) */
+
+ byte_alignment = 1;
+ bit_length = 8;
+ break;
+
+ case AML_FIELD_ACCESS_WORD:
+
+ byte_alignment = 2;
+ bit_length = 16;
+ break;
+
+ case AML_FIELD_ACCESS_DWORD:
+
+ byte_alignment = 4;
+ bit_length = 32;
+ break;
+
+ case AML_FIELD_ACCESS_QWORD: /* ACPI 2.0 */
+
+ byte_alignment = 8;
+ bit_length = 64;
+ break;
+
+ default:
+
+ /* Invalid field access type */
+
+ ACPI_ERROR((AE_INFO, "Unknown field access type 0x%X", access));
+
+ return_UINT32(0);
+ }
+
+ if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) {
+ /*
+ * buffer_field access can be on any byte boundary, so the
+ * byte_alignment is always 1 byte -- regardless of any byte_alignment
+ * implied by the field access type.
+ */
+ byte_alignment = 1;
+ }
+
+ *return_byte_alignment = byte_alignment;
+ return_UINT32(bit_length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_prep_common_field_object
+ *
+ * PARAMETERS: obj_desc - The field object
+ * field_flags - Access, lock_rule, and update_rule.
+ * The format of a field_flag is described
+ * in the ACPI specification
+ * field_attribute - Special attributes (not used)
+ * field_bit_position - Field start position
+ * field_bit_length - Field length in number of bits
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize the areas of the field object that are common
+ * to the various types of fields. Note: This is very "sensitive"
+ * code because we are solving the general case for field
+ * alignment.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_prep_common_field_object(union acpi_operand_object *obj_desc,
+ u8 field_flags,
+ u8 field_attribute,
+ u32 field_bit_position, u32 field_bit_length)
+{
+ u32 access_bit_width;
+ u32 byte_alignment;
+ u32 nearest_byte_address;
+
+ ACPI_FUNCTION_TRACE(ex_prep_common_field_object);
+
+ /*
+ * Note: the structure being initialized is the
+ * ACPI_COMMON_FIELD_INFO; No structure fields outside of the common
+ * area are initialized by this procedure.
+ */
+ obj_desc->common_field.field_flags = field_flags;
+ obj_desc->common_field.attribute = field_attribute;
+ obj_desc->common_field.bit_length = field_bit_length;
+
+ /*
+ * Decode the access type so we can compute offsets. The access type gives
+ * two pieces of information - the width of each field access and the
+ * necessary byte_alignment (address granularity) of the access.
+ *
+ * For any_acc, the access_bit_width is the largest width that is both
+ * necessary and possible in an attempt to access the whole field in one
+ * I/O operation. However, for any_acc, the byte_alignment is always one
+ * byte.
+ *
+ * For all Buffer Fields, the byte_alignment is always one byte.
+ *
+ * For all other access types (Byte, Word, Dword, Qword), the Bitwidth is
+ * the same (equivalent) as the byte_alignment.
+ */
+ access_bit_width =
+ acpi_ex_decode_field_access(obj_desc, field_flags, &byte_alignment);
+ if (!access_bit_width) {
+ return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
+ }
+
+ /* Setup width (access granularity) fields (values are: 1, 2, 4, 8) */
+
+ obj_desc->common_field.access_byte_width = (u8)
+ ACPI_DIV_8(access_bit_width);
+
+ /*
+ * base_byte_offset is the address of the start of the field within the
+ * region. It is the byte address of the first *datum* (field-width data
+ * unit) of the field. (i.e., the first datum that contains at least the
+ * first *bit* of the field.)
+ *
+ * Note: byte_alignment is always either equal to the access_bit_width or 8
+ * (Byte access), and it defines the addressing granularity of the parent
+ * region or buffer.
+ */
+ nearest_byte_address =
+ ACPI_ROUND_BITS_DOWN_TO_BYTES(field_bit_position);
+ obj_desc->common_field.base_byte_offset = (u32)
+ ACPI_ROUND_DOWN(nearest_byte_address, byte_alignment);
+
+ /*
+ * start_field_bit_offset is the offset of the first bit of the field within
+ * a field datum.
+ */
+ obj_desc->common_field.start_field_bit_offset = (u8)
+ (field_bit_position -
+ ACPI_MUL_8(obj_desc->common_field.base_byte_offset));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_prep_field_value
+ *
+ * PARAMETERS: info - Contains all field creation info
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Construct an object of type union acpi_operand_object with a
+ * subtype of def_field and connect it to the parent Node.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *second_desc = NULL;
+ acpi_status status;
+ u32 access_byte_width;
+ u32 type;
+
+ ACPI_FUNCTION_TRACE(ex_prep_field_value);
+
+ /* Parameter validation */
+
+ if (info->field_type != ACPI_TYPE_LOCAL_INDEX_FIELD) {
+ if (!info->region_node) {
+ ACPI_ERROR((AE_INFO, "Null RegionNode"));
+ return_ACPI_STATUS(AE_AML_NO_OPERAND);
+ }
+
+ type = acpi_ns_get_type(info->region_node);
+ if (type != ACPI_TYPE_REGION) {
+ ACPI_ERROR((AE_INFO,
+ "Needed Region, found type 0x%X (%s)", type,
+ acpi_ut_get_type_name(type)));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ }
+
+ /* Allocate a new field object */
+
+ obj_desc = acpi_ut_create_internal_object(info->field_type);
+ if (!obj_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Initialize areas of the object that are common to all fields */
+
+ obj_desc->common_field.node = info->field_node;
+ status = acpi_ex_prep_common_field_object(obj_desc,
+ info->field_flags,
+ info->attribute,
+ info->field_bit_position,
+ info->field_bit_length);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_delete_object_desc(obj_desc);
+ return_ACPI_STATUS(status);
+ }
+
+ /* Initialize areas of the object that are specific to the field type */
+
+ switch (info->field_type) {
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+
+ obj_desc->field.region_obj =
+ acpi_ns_get_attached_object(info->region_node);
+
+ /* Fields specific to generic_serial_bus fields */
+
+ obj_desc->field.access_length = info->access_length;
+
+ if (info->connection_node) {
+ second_desc = info->connection_node->object;
+ if (!(second_desc->common.flags & AOPOBJ_DATA_VALID)) {
+ status =
+ acpi_ds_get_buffer_arguments(second_desc);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_delete_object_desc(obj_desc);
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ obj_desc->field.resource_buffer =
+ second_desc->buffer.pointer;
+ obj_desc->field.resource_length =
+ (u16)second_desc->buffer.length;
+ } else if (info->resource_buffer) {
+ obj_desc->field.resource_buffer = info->resource_buffer;
+ obj_desc->field.resource_length = info->resource_length;
+ }
+
+ obj_desc->field.pin_number_index = info->pin_number_index;
+
+ /* Allow full data read from EC address space */
+
+ if ((obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_EC)
+ && (obj_desc->common_field.bit_length > 8)) {
+ access_byte_width =
+ ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.
+ bit_length);
+
+ /* Maximum byte width supported is 255 */
+
+ if (access_byte_width < 256) {
+ obj_desc->common_field.access_byte_width =
+ (u8)access_byte_width;
+ }
+ }
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "RegionField: BitOff %X, Off %X, Gran %X, Region %p\n",
+ obj_desc->field.start_field_bit_offset,
+ obj_desc->field.base_byte_offset,
+ obj_desc->field.access_byte_width,
+ obj_desc->field.region_obj));
+ break;
+
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+
+ obj_desc->bank_field.value = info->bank_value;
+ obj_desc->bank_field.region_obj =
+ acpi_ns_get_attached_object(info->region_node);
+ obj_desc->bank_field.bank_obj =
+ acpi_ns_get_attached_object(info->register_node);
+
+ /* An additional reference for the attached objects */
+
+ acpi_ut_add_reference(obj_desc->bank_field.region_obj);
+ acpi_ut_add_reference(obj_desc->bank_field.bank_obj);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Bank Field: BitOff %X, Off %X, Gran %X, Region %p, BankReg %p\n",
+ obj_desc->bank_field.start_field_bit_offset,
+ obj_desc->bank_field.base_byte_offset,
+ obj_desc->field.access_byte_width,
+ obj_desc->bank_field.region_obj,
+ obj_desc->bank_field.bank_obj));
+
+ /*
+ * Remember location in AML stream of the field unit
+ * opcode and operands -- since the bank_value
+ * operands must be evaluated.
+ */
+ second_desc = obj_desc->common.next_object;
+ second_desc->extra.aml_start =
+ ACPI_CAST_PTR(union acpi_parse_object,
+ info->data_register_node)->named.data;
+ second_desc->extra.aml_length =
+ ACPI_CAST_PTR(union acpi_parse_object,
+ info->data_register_node)->named.length;
+
+ break;
+
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
+ /* Get the Index and Data registers */
+
+ obj_desc->index_field.index_obj =
+ acpi_ns_get_attached_object(info->register_node);
+ obj_desc->index_field.data_obj =
+ acpi_ns_get_attached_object(info->data_register_node);
+
+ if (!obj_desc->index_field.data_obj
+ || !obj_desc->index_field.index_obj) {
+ ACPI_ERROR((AE_INFO,
+ "Null Index Object during field prep"));
+ acpi_ut_delete_object_desc(obj_desc);
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ /* An additional reference for the attached objects */
+
+ acpi_ut_add_reference(obj_desc->index_field.data_obj);
+ acpi_ut_add_reference(obj_desc->index_field.index_obj);
+
+ /*
+ * April 2006: Changed to match MS behavior
+ *
+ * The value written to the Index register is the byte offset of the
+ * target field in units of the granularity of the index_field
+ *
+ * Previously, the value was calculated as an index in terms of the
+ * width of the Data register, as below:
+ *
+ * obj_desc->index_field.Value = (u32)
+ * (Info->field_bit_position / ACPI_MUL_8 (
+ * obj_desc->Field.access_byte_width));
+ *
+ * February 2006: Tried value as a byte offset:
+ * obj_desc->index_field.Value = (u32)
+ * ACPI_DIV_8 (Info->field_bit_position);
+ */
+ obj_desc->index_field.value =
+ (u32) ACPI_ROUND_DOWN(ACPI_DIV_8(info->field_bit_position),
+ obj_desc->index_field.
+ access_byte_width);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "IndexField: BitOff %X, Off %X, Value %X, "
+ "Gran %X, Index %p, Data %p\n",
+ obj_desc->index_field.start_field_bit_offset,
+ obj_desc->index_field.base_byte_offset,
+ obj_desc->index_field.value,
+ obj_desc->field.access_byte_width,
+ obj_desc->index_field.index_obj,
+ obj_desc->index_field.data_obj));
+ break;
+
+ default:
+
+ /* No other types should get here */
+
+ break;
+ }
+
+ /*
+ * Store the constructed descriptor (obj_desc) into the parent Node,
+ * preserving the current type of that named_obj.
+ */
+ status =
+ acpi_ns_attach_object(info->field_node, obj_desc,
+ acpi_ns_get_type(info->field_node));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "Set NamedObj %p [%4.4s], ObjDesc %p\n",
+ info->field_node,
+ acpi_ut_get_node_name(info->field_node), obj_desc));
+
+ /* Remove local reference to the object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c
new file mode 100644
index 000000000..4ff35852c
--- /dev/null
+++ b/drivers/acpi/acpica/exregion.c
@@ -0,0 +1,544 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exregion - ACPI default op_region (address space) handlers
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exregion")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_system_memory_space_handler
+ *
+ * PARAMETERS: function - Read or Write operation
+ * address - Where in the space to read or write
+ * bit_width - Field width in bits (8, 16, or 32)
+ * value - Pointer to in or out value
+ * handler_context - Pointer to Handler's context
+ * region_context - Pointer to context specific to the
+ * accessed region
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Handler for the System Memory address space (Op Region)
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ex_system_memory_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context)
+{
+ acpi_status status = AE_OK;
+ void *logical_addr_ptr = NULL;
+ struct acpi_mem_space_context *mem_info = region_context;
+ 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
+
+ ACPI_FUNCTION_TRACE(ex_system_memory_space_handler);
+
+ /* Validate and translate the bit width */
+
+ switch (bit_width) {
+ case 8:
+
+ length = 1;
+ break;
+
+ case 16:
+
+ length = 2;
+ break;
+
+ case 32:
+
+ length = 4;
+ break;
+
+ case 64:
+
+ length = 8;
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Invalid SystemMemory width %u",
+ bit_width));
+ return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
+ }
+
+#ifdef ACPI_MISALIGNMENT_NOT_SUPPORTED
+ /*
+ * Hardware does not support non-aligned data transfers, we must verify
+ * the request.
+ */
+ (void)acpi_ut_short_divide((u64) address, length, NULL, &remainder);
+ if (remainder != 0) {
+ return_ACPI_STATUS(AE_AML_ALIGNMENT);
+ }
+#endif
+
+ /*
+ * Does the request fit into the cached memory mapping?
+ * Is 1) Address below the current mapping? OR
+ * 2) Address beyond the current mapping?
+ */
+ if (!mm || (address < mm->physical_address) ||
+ ((u64) address + length > (u64) mm->physical_address + mm->length)) {
+ /*
+ * The request cannot be resolved by the current memory mapping.
+ *
+ * Look for an existing saved mapping covering the address range
+ * at hand. If found, save it as the current one and carry out
+ * the access.
+ */
+ for (mm = mem_info->first_mm; mm; mm = mm->next_mm) {
+ if (mm == mem_info->cur_mm)
+ continue;
+
+ if (address < mm->physical_address)
+ continue;
+
+ if ((u64) address + length >
+ (u64) mm->physical_address + mm->length)
+ continue;
+
+ mem_info->cur_mm = mm;
+ goto access;
+ }
+
+ /* Create a new mappings list entry */
+ mm = ACPI_ALLOCATE_ZEROED(sizeof(*mm));
+ if (!mm) {
+ ACPI_ERROR((AE_INFO,
+ "Unable to save memory mapping at 0x%8.8X%8.8X, size %u",
+ ACPI_FORMAT_UINT64(address), length));
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /*
+ * October 2009: Attempt to map from the requested address to the
+ * end of the region. However, we will never map more than one
+ * page, nor will we cross a page boundary.
+ */
+ 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;
+ }
+
+ /* Create a new mapping starting at the address given */
+
+ logical_addr_ptr = acpi_os_map_memory(address, map_length);
+ if (!logical_addr_ptr) {
+ ACPI_ERROR((AE_INFO,
+ "Could not map memory at 0x%8.8X%8.8X, size %u",
+ ACPI_FORMAT_UINT64(address),
+ (u32)map_length));
+ ACPI_FREE(mm);
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Save the physical address and mapping size */
+
+ mm->logical_address = logical_addr_ptr;
+ mm->physical_address = address;
+ mm->length = map_length;
+
+ /*
+ * Add the new entry to the mappigs list and save it as the
+ * current mapping.
+ */
+ mm->next_mm = mem_info->first_mm;
+ mem_info->first_mm = mm;
+
+ mem_info->cur_mm = mm;
+ }
+
+access:
+ /*
+ * Generate a logical pointer corresponding to the address we want to
+ * access
+ */
+ logical_addr_ptr = mm->logical_address +
+ ((u64) address - (u64) mm->physical_address);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "System-Memory (width %u) R/W %u Address=%8.8X%8.8X\n",
+ bit_width, function, ACPI_FORMAT_UINT64(address)));
+
+ /*
+ * Perform the memory read or write
+ *
+ * Note: For machines that do not support non-aligned transfers, the target
+ * address was checked for alignment above. We do not attempt to break the
+ * transfer up into smaller (byte-size) chunks because the AML specifically
+ * asked for a transfer width that the hardware may require.
+ */
+ switch (function) {
+ case ACPI_READ:
+
+ *value = 0;
+ switch (bit_width) {
+ case 8:
+
+ *value = (u64)ACPI_GET8(logical_addr_ptr);
+ break;
+
+ case 16:
+
+ *value = (u64)ACPI_GET16(logical_addr_ptr);
+ break;
+
+ case 32:
+
+ *value = (u64)ACPI_GET32(logical_addr_ptr);
+ break;
+
+ case 64:
+
+ *value = (u64)ACPI_GET64(logical_addr_ptr);
+ break;
+
+ default:
+
+ /* bit_width was already validated */
+
+ break;
+ }
+ break;
+
+ case ACPI_WRITE:
+
+ switch (bit_width) {
+ case 8:
+
+ ACPI_SET8(logical_addr_ptr, *value);
+ break;
+
+ case 16:
+
+ ACPI_SET16(logical_addr_ptr, *value);
+ break;
+
+ case 32:
+
+ ACPI_SET32(logical_addr_ptr, *value);
+ break;
+
+ case 64:
+
+ ACPI_SET64(logical_addr_ptr, *value);
+ break;
+
+ default:
+
+ /* bit_width was already validated */
+
+ break;
+ }
+ break;
+
+ default:
+
+ status = AE_BAD_PARAMETER;
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_system_io_space_handler
+ *
+ * PARAMETERS: function - Read or Write operation
+ * address - Where in the space to read or write
+ * bit_width - Field width in bits (8, 16, or 32)
+ * value - Pointer to in or out value
+ * handler_context - Pointer to Handler's context
+ * region_context - Pointer to context specific to the
+ * accessed region
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Handler for the System IO address space (Op Region)
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_system_io_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context)
+{
+ acpi_status status = AE_OK;
+ u32 value32;
+
+ ACPI_FUNCTION_TRACE(ex_system_io_space_handler);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "System-IO (width %u) R/W %u Address=%8.8X%8.8X\n",
+ bit_width, function, ACPI_FORMAT_UINT64(address)));
+
+ /* Decode the function parameter */
+
+ switch (function) {
+ case ACPI_READ:
+
+ status = acpi_hw_read_port((acpi_io_address)address,
+ &value32, bit_width);
+ *value = value32;
+ break;
+
+ case ACPI_WRITE:
+
+ status = acpi_hw_write_port((acpi_io_address)address,
+ (u32)*value, bit_width);
+ break;
+
+ default:
+
+ status = AE_BAD_PARAMETER;
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+#ifdef ACPI_PCI_CONFIGURED
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_pci_config_space_handler
+ *
+ * PARAMETERS: function - Read or Write operation
+ * address - Where in the space to read or write
+ * bit_width - Field width in bits (8, 16, or 32)
+ * value - Pointer to in or out value
+ * handler_context - Pointer to Handler's context
+ * region_context - Pointer to context specific to the
+ * accessed region
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Handler for the PCI Config address space (Op Region)
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_pci_config_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context)
+{
+ acpi_status status = AE_OK;
+ struct acpi_pci_id *pci_id;
+ u16 pci_register;
+
+ ACPI_FUNCTION_TRACE(ex_pci_config_space_handler);
+
+ /*
+ * The arguments to acpi_os(Read|Write)pci_configuration are:
+ *
+ * pci_segment is the PCI bus segment range 0-31
+ * pci_bus is the PCI bus number range 0-255
+ * pci_device is the PCI device number range 0-31
+ * pci_function is the PCI device function number
+ * pci_register is the Config space register range 0-255 bytes
+ *
+ * value - input value for write, output address for read
+ *
+ */
+ pci_id = (struct acpi_pci_id *)region_context;
+ pci_register = (u16) (u32) address;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Pci-Config %u (%u) Seg(%04x) Bus(%04x) "
+ "Dev(%04x) Func(%04x) Reg(%04x)\n",
+ function, bit_width, pci_id->segment, pci_id->bus,
+ pci_id->device, pci_id->function, pci_register));
+
+ switch (function) {
+ case ACPI_READ:
+
+ *value = 0;
+ status =
+ acpi_os_read_pci_configuration(pci_id, pci_register, value,
+ bit_width);
+ break;
+
+ case ACPI_WRITE:
+
+ status =
+ acpi_os_write_pci_configuration(pci_id, pci_register,
+ *value, bit_width);
+ break;
+
+ default:
+
+ status = AE_BAD_PARAMETER;
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_cmos_space_handler
+ *
+ * PARAMETERS: function - Read or Write operation
+ * address - Where in the space to read or write
+ * bit_width - Field width in bits (8, 16, or 32)
+ * value - Pointer to in or out value
+ * handler_context - Pointer to Handler's context
+ * region_context - Pointer to context specific to the
+ * accessed region
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Handler for the CMOS address space (Op Region)
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_cmos_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ex_cmos_space_handler);
+
+ return_ACPI_STATUS(status);
+}
+
+#ifdef ACPI_PCI_CONFIGURED
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_pci_bar_space_handler
+ *
+ * PARAMETERS: function - Read or Write operation
+ * address - Where in the space to read or write
+ * bit_width - Field width in bits (8, 16, or 32)
+ * value - Pointer to in or out value
+ * handler_context - Pointer to Handler's context
+ * region_context - Pointer to context specific to the
+ * accessed region
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Handler for the PCI bar_target address space (Op Region)
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_pci_bar_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ex_pci_bar_space_handler);
+
+ return_ACPI_STATUS(status);
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_data_table_space_handler
+ *
+ * PARAMETERS: function - Read or Write operation
+ * address - Where in the space to read or write
+ * bit_width - Field width in bits (8, 16, or 32)
+ * value - Pointer to in or out value
+ * handler_context - Pointer to Handler's context
+ * region_context - Pointer to context specific to the
+ * accessed region
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Handler for the Data Table address space (Op Region)
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_data_table_space_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width,
+ u64 *value,
+ void *handler_context, void *region_context)
+{
+ struct acpi_data_table_space_context *mapping;
+ char *pointer;
+
+ ACPI_FUNCTION_TRACE(ex_data_table_space_handler);
+
+ mapping = (struct acpi_data_table_space_context *) region_context;
+ pointer = ACPI_CAST_PTR(char, mapping->pointer) +
+ (address - ACPI_PTR_TO_PHYSADDR(mapping->pointer));
+
+ /*
+ * Perform the memory read or write. The bit_width was already
+ * validated.
+ */
+ switch (function) {
+ case ACPI_READ:
+
+ memcpy(ACPI_CAST_PTR(char, value), pointer,
+ ACPI_DIV_8(bit_width));
+ break;
+
+ case ACPI_WRITE:
+
+ memcpy(pointer, ACPI_CAST_PTR(char, value),
+ ACPI_DIV_8(bit_width));
+ break;
+
+ default:
+
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/exresnte.c b/drivers/acpi/acpica/exresnte.c
new file mode 100644
index 000000000..b81506d73
--- /dev/null
+++ b/drivers/acpi/acpica/exresnte.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exresnte - AML Interpreter object resolution
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exresnte")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_resolve_node_to_value
+ *
+ * PARAMETERS: object_ptr - Pointer to a location that contains
+ * a pointer to a NS node, and will receive a
+ * pointer to the resolved object.
+ * walk_state - Current state. Valid only if executing AML
+ * code. NULL if simply resolving an object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Resolve a Namespace node to a valued object
+ *
+ * Note: for some of the data types, the pointer attached to the Node
+ * can be either a pointer to an actual internal object or a pointer into the
+ * AML stream itself. These types are currently:
+ *
+ * ACPI_TYPE_INTEGER
+ * ACPI_TYPE_STRING
+ * ACPI_TYPE_BUFFER
+ * ACPI_TYPE_MUTEX
+ * ACPI_TYPE_PACKAGE
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ex_resolve_node_to_value(struct acpi_namespace_node **object_ptr,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *source_desc;
+ union acpi_operand_object *obj_desc = NULL;
+ struct acpi_namespace_node *node;
+ acpi_object_type entry_type;
+
+ ACPI_FUNCTION_TRACE(ex_resolve_node_to_value);
+
+ /*
+ * The stack pointer points to a struct acpi_namespace_node (Node). Get the
+ * object that is attached to the Node.
+ */
+ node = *object_ptr;
+ source_desc = acpi_ns_get_attached_object(node);
+ entry_type = acpi_ns_get_type((acpi_handle)node);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Entry=%p SourceDesc=%p [%s]\n",
+ node, source_desc,
+ acpi_ut_get_type_name(entry_type)));
+
+ if ((entry_type == ACPI_TYPE_LOCAL_ALIAS) ||
+ (entry_type == ACPI_TYPE_LOCAL_METHOD_ALIAS)) {
+
+ /* There is always exactly one level of indirection */
+
+ node = ACPI_CAST_PTR(struct acpi_namespace_node, node->object);
+ source_desc = acpi_ns_get_attached_object(node);
+ entry_type = acpi_ns_get_type((acpi_handle)node);
+ *object_ptr = node;
+ }
+
+ /*
+ * Several object types require no further processing:
+ * 1) Device/Thermal objects don't have a "real" subobject, return Node
+ * 2) Method locals and arguments have a pseudo-Node
+ * 3) 10/2007: Added method type to assist with Package construction.
+ */
+ if ((entry_type == ACPI_TYPE_DEVICE) ||
+ (entry_type == ACPI_TYPE_THERMAL) ||
+ (entry_type == ACPI_TYPE_METHOD) ||
+ (node->flags & (ANOBJ_METHOD_ARG | ANOBJ_METHOD_LOCAL))) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ if (!source_desc) {
+ ACPI_ERROR((AE_INFO, "No object attached to node [%4.4s] %p",
+ node->name.ascii, node));
+ return_ACPI_STATUS(AE_AML_UNINITIALIZED_NODE);
+ }
+
+ /*
+ * Action is based on the type of the Node, which indicates the type
+ * of the attached object or pointer
+ */
+ switch (entry_type) {
+ case ACPI_TYPE_PACKAGE:
+
+ if (source_desc->common.type != ACPI_TYPE_PACKAGE) {
+ ACPI_ERROR((AE_INFO, "Object not a Package, type %s",
+ acpi_ut_get_object_type_name(source_desc)));
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ status = acpi_ds_get_package_arguments(source_desc);
+ if (ACPI_SUCCESS(status)) {
+
+ /* Return an additional reference to the object */
+
+ obj_desc = source_desc;
+ acpi_ut_add_reference(obj_desc);
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ if (source_desc->common.type != ACPI_TYPE_BUFFER) {
+ ACPI_ERROR((AE_INFO, "Object not a Buffer, type %s",
+ acpi_ut_get_object_type_name(source_desc)));
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ status = acpi_ds_get_buffer_arguments(source_desc);
+ if (ACPI_SUCCESS(status)) {
+
+ /* Return an additional reference to the object */
+
+ obj_desc = source_desc;
+ acpi_ut_add_reference(obj_desc);
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ if (source_desc->common.type != ACPI_TYPE_STRING) {
+ ACPI_ERROR((AE_INFO, "Object not a String, type %s",
+ acpi_ut_get_object_type_name(source_desc)));
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /* Return an additional reference to the object */
+
+ obj_desc = source_desc;
+ acpi_ut_add_reference(obj_desc);
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ if (source_desc->common.type != ACPI_TYPE_INTEGER) {
+ ACPI_ERROR((AE_INFO, "Object not a Integer, type %s",
+ acpi_ut_get_object_type_name(source_desc)));
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /* Return an additional reference to the object */
+
+ obj_desc = source_desc;
+ acpi_ut_add_reference(obj_desc);
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "FieldRead Node=%p SourceDesc=%p Type=%X\n",
+ node, source_desc, entry_type));
+
+ status =
+ acpi_ex_read_data_from_field(walk_state, source_desc,
+ &obj_desc);
+ break;
+
+ /* For these objects, just return the object attached to the Node */
+
+ case ACPI_TYPE_MUTEX:
+ case ACPI_TYPE_POWER:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_EVENT:
+ case ACPI_TYPE_REGION:
+
+ /* Return an additional reference to the object */
+
+ obj_desc = source_desc;
+ acpi_ut_add_reference(obj_desc);
+ break;
+
+ /* TYPE_ANY is untyped, and thus there is no object associated with it */
+
+ case ACPI_TYPE_ANY:
+
+ ACPI_ERROR((AE_INFO,
+ "Untyped entry %p, no attached object!", node));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE); /* Cannot be AE_TYPE */
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ switch (source_desc->reference.class) {
+ case ACPI_REFCLASS_TABLE: /* This is a ddb_handle */
+ case ACPI_REFCLASS_REFOF:
+ case ACPI_REFCLASS_INDEX:
+
+ /* Return an additional reference to the object */
+
+ obj_desc = source_desc;
+ acpi_ut_add_reference(obj_desc);
+ break;
+
+ default:
+
+ /* No named references are allowed here */
+
+ ACPI_ERROR((AE_INFO,
+ "Unsupported Reference type 0x%X",
+ source_desc->reference.class));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ break;
+
+ default:
+
+ /* Default case is for unknown types */
+
+ ACPI_ERROR((AE_INFO,
+ "Node %p - Unknown object type 0x%X",
+ node, entry_type));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+
+ } /* switch (entry_type) */
+
+ /* Return the object descriptor */
+
+ *object_ptr = (void *)obj_desc;
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c
new file mode 100644
index 000000000..61ee7fb46
--- /dev/null
+++ b/drivers/acpi/acpica/exresolv.c
@@ -0,0 +1,539 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exresolv - AML Interpreter object resolution
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "amlcode.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exresolv")
+
+/* Local prototypes */
+static acpi_status
+acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr,
+ struct acpi_walk_state *walk_state);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_resolve_to_value
+ *
+ * PARAMETERS: **stack_ptr - Points to entry on obj_stack, which can
+ * be either an (union acpi_operand_object *)
+ * or an acpi_handle.
+ * walk_state - Current method state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert Reference objects to values
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_resolve_to_value(union acpi_operand_object **stack_ptr,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_resolve_to_value, stack_ptr);
+
+ if (!stack_ptr || !*stack_ptr) {
+ ACPI_ERROR((AE_INFO, "Internal - null pointer"));
+ return_ACPI_STATUS(AE_AML_NO_OPERAND);
+ }
+
+ /*
+ * The entity pointed to by the stack_ptr can be either
+ * 1) A valid union acpi_operand_object, or
+ * 2) A struct acpi_namespace_node (named_obj)
+ */
+ if (ACPI_GET_DESCRIPTOR_TYPE(*stack_ptr) == ACPI_DESC_TYPE_OPERAND) {
+ status = acpi_ex_resolve_object_to_value(stack_ptr, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (!*stack_ptr) {
+ ACPI_ERROR((AE_INFO, "Internal - null pointer"));
+ return_ACPI_STATUS(AE_AML_NO_OPERAND);
+ }
+ }
+
+ /*
+ * Object on the stack may have changed if acpi_ex_resolve_object_to_value()
+ * was called (i.e., we can't use an _else_ here.)
+ */
+ if (ACPI_GET_DESCRIPTOR_TYPE(*stack_ptr) == ACPI_DESC_TYPE_NAMED) {
+ status =
+ acpi_ex_resolve_node_to_value(ACPI_CAST_INDIRECT_PTR
+ (struct acpi_namespace_node,
+ stack_ptr), walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Resolved object %p\n", *stack_ptr));
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_resolve_object_to_value
+ *
+ * PARAMETERS: stack_ptr - Pointer to an internal object
+ * walk_state - Current method state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Retrieve the value from an internal object. The Reference type
+ * uses the associated AML opcode to determine the value.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *stack_desc;
+ union acpi_operand_object *obj_desc = NULL;
+ u8 ref_type;
+
+ ACPI_FUNCTION_TRACE(ex_resolve_object_to_value);
+
+ stack_desc = *stack_ptr;
+
+ /* This is an object of type union acpi_operand_object */
+
+ switch (stack_desc->common.type) {
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ ref_type = stack_desc->reference.class;
+
+ switch (ref_type) {
+ case ACPI_REFCLASS_LOCAL:
+ case ACPI_REFCLASS_ARG:
+ /*
+ * Get the local from the method's state info
+ * Note: this increments the local's object reference count
+ */
+ status = acpi_ds_method_data_get_value(ref_type,
+ stack_desc->
+ reference.value,
+ walk_state,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[Arg/Local %X] ValueObj is %p\n",
+ stack_desc->reference.value,
+ obj_desc));
+
+ /*
+ * Now we can delete the original Reference Object and
+ * replace it with the resolved value
+ */
+ acpi_ut_remove_reference(stack_desc);
+ *stack_ptr = obj_desc;
+ break;
+
+ case ACPI_REFCLASS_INDEX:
+
+ switch (stack_desc->reference.target_type) {
+ case ACPI_TYPE_BUFFER_FIELD:
+
+ /* Just return - do not dereference */
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ /* If method call or copy_object - do not dereference */
+
+ if ((walk_state->opcode ==
+ AML_INT_METHODCALL_OP)
+ || (walk_state->opcode ==
+ AML_COPY_OBJECT_OP)) {
+ break;
+ }
+
+ /* Otherwise, dereference the package_index to a package element */
+
+ obj_desc = *stack_desc->reference.where;
+ if (obj_desc) {
+ /*
+ * Valid object descriptor, copy pointer to return value
+ * (i.e., dereference the package index)
+ * Delete the ref object, increment the returned object
+ */
+ acpi_ut_add_reference(obj_desc);
+ *stack_ptr = obj_desc;
+ } else {
+ /*
+ * A NULL object descriptor means an uninitialized element of
+ * the package, can't dereference it
+ */
+ ACPI_ERROR((AE_INFO,
+ "Attempt to dereference an Index to "
+ "NULL package element Idx=%p",
+ stack_desc));
+ status = AE_AML_UNINITIALIZED_ELEMENT;
+ }
+ break;
+
+ default:
+
+ /* Invalid reference object */
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown TargetType 0x%X in Index/Reference object %p",
+ stack_desc->reference.target_type,
+ stack_desc));
+ status = AE_AML_INTERNAL;
+ break;
+ }
+ break;
+
+ case ACPI_REFCLASS_REFOF:
+ case ACPI_REFCLASS_DEBUG:
+ case ACPI_REFCLASS_TABLE:
+
+ /* Just leave the object as-is, do not dereference */
+
+ break;
+
+ case ACPI_REFCLASS_NAME: /* Reference to a named object */
+
+ /* Dereference the name */
+
+ if ((stack_desc->reference.node->type ==
+ ACPI_TYPE_DEVICE)
+ || (stack_desc->reference.node->type ==
+ ACPI_TYPE_THERMAL)) {
+
+ /* These node types do not have 'real' subobjects */
+
+ *stack_ptr = (void *)stack_desc->reference.node;
+ } else {
+ /* Get the object pointed to by the namespace node */
+
+ *stack_ptr =
+ (stack_desc->reference.node)->object;
+ acpi_ut_add_reference(*stack_ptr);
+ }
+
+ acpi_ut_remove_reference(stack_desc);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown Reference type 0x%X in %p",
+ ref_type, stack_desc));
+ status = AE_AML_INTERNAL;
+ break;
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ status = acpi_ds_get_buffer_arguments(stack_desc);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ status = acpi_ds_get_package_arguments(stack_desc);
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "FieldRead SourceDesc=%p Type=%X\n",
+ stack_desc, stack_desc->common.type));
+
+ status =
+ acpi_ex_read_data_from_field(walk_state, stack_desc,
+ &obj_desc);
+
+ /* Remove a reference to the original operand, then override */
+
+ acpi_ut_remove_reference(*stack_ptr);
+ *stack_ptr = (void *)obj_desc;
+ break;
+
+ default:
+
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_resolve_multiple
+ *
+ * PARAMETERS: walk_state - Current state (contains AML opcode)
+ * operand - Starting point for resolution
+ * return_type - Where the object type is returned
+ * return_desc - Where the resolved object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Return the base object and type. Traverse a reference list if
+ * necessary to get to the base object.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_resolve_multiple(struct acpi_walk_state *walk_state,
+ union acpi_operand_object *operand,
+ acpi_object_type *return_type,
+ union acpi_operand_object **return_desc)
+{
+ union acpi_operand_object *obj_desc = ACPI_CAST_PTR(void, operand);
+ struct acpi_namespace_node *node =
+ ACPI_CAST_PTR(struct acpi_namespace_node, operand);
+ acpi_object_type type;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_ex_resolve_multiple);
+
+ /* Operand can be either a namespace node or an operand descriptor */
+
+ switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) {
+ case ACPI_DESC_TYPE_OPERAND:
+
+ type = obj_desc->common.type;
+ break;
+
+ case ACPI_DESC_TYPE_NAMED:
+
+ type = ((struct acpi_namespace_node *)obj_desc)->type;
+ obj_desc = acpi_ns_get_attached_object(node);
+
+ /* If we had an Alias node, use the attached object for type info */
+
+ if (type == ACPI_TYPE_LOCAL_ALIAS) {
+ type = ((struct acpi_namespace_node *)obj_desc)->type;
+ obj_desc = acpi_ns_get_attached_object((struct
+ acpi_namespace_node
+ *)obj_desc);
+ }
+
+ switch (type) {
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_THERMAL:
+
+ /* These types have no attached subobject */
+ break;
+
+ default:
+
+ /* All other types require a subobject */
+
+ if (!obj_desc) {
+ ACPI_ERROR((AE_INFO,
+ "[%4.4s] Node is unresolved or uninitialized",
+ acpi_ut_get_node_name(node)));
+ return_ACPI_STATUS(AE_AML_UNINITIALIZED_NODE);
+ }
+ break;
+ }
+ break;
+
+ default:
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /* If type is anything other than a reference, we are done */
+
+ if (type != ACPI_TYPE_LOCAL_REFERENCE) {
+ goto exit;
+ }
+
+ /*
+ * For reference objects created via the ref_of, Index, or Load/load_table
+ * operators, we need to get to the base object (as per the ACPI
+ * specification of the object_type and size_of operators). This means
+ * traversing the list of possibly many nested references.
+ */
+ while (obj_desc->common.type == ACPI_TYPE_LOCAL_REFERENCE) {
+ switch (obj_desc->reference.class) {
+ case ACPI_REFCLASS_REFOF:
+ case ACPI_REFCLASS_NAME:
+
+ /* Dereference the reference pointer */
+
+ if (obj_desc->reference.class == ACPI_REFCLASS_REFOF) {
+ node = obj_desc->reference.object;
+ } else { /* AML_INT_NAMEPATH_OP */
+
+ node = obj_desc->reference.node;
+ }
+
+ /* All "References" point to a NS node */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(node) !=
+ ACPI_DESC_TYPE_NAMED) {
+ ACPI_ERROR((AE_INFO,
+ "Not a namespace node %p [%s]",
+ node,
+ acpi_ut_get_descriptor_name(node)));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ /* Get the attached object */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+
+ /* No object, use the NS node type */
+
+ type = acpi_ns_get_type(node);
+ goto exit;
+ }
+
+ /* Check for circular references */
+
+ if (obj_desc == operand) {
+ return_ACPI_STATUS(AE_AML_CIRCULAR_REFERENCE);
+ }
+ break;
+
+ case ACPI_REFCLASS_INDEX:
+
+ /* Get the type of this reference (index into another object) */
+
+ type = obj_desc->reference.target_type;
+ if (type != ACPI_TYPE_PACKAGE) {
+ goto exit;
+ }
+
+ /*
+ * The main object is a package, we want to get the type
+ * of the individual package element that is referenced by
+ * the index.
+ *
+ * This could of course in turn be another reference object.
+ */
+ obj_desc = *(obj_desc->reference.where);
+ if (!obj_desc) {
+
+ /* NULL package elements are allowed */
+
+ type = 0; /* Uninitialized */
+ goto exit;
+ }
+ break;
+
+ case ACPI_REFCLASS_TABLE:
+
+ type = ACPI_TYPE_DDB_HANDLE;
+ goto exit;
+
+ case ACPI_REFCLASS_LOCAL:
+ case ACPI_REFCLASS_ARG:
+
+ if (return_desc) {
+ status =
+ acpi_ds_method_data_get_value(obj_desc->
+ reference.
+ class,
+ obj_desc->
+ reference.
+ value,
+ walk_state,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ acpi_ut_remove_reference(obj_desc);
+ } else {
+ status =
+ acpi_ds_method_data_get_node(obj_desc->
+ reference.
+ class,
+ obj_desc->
+ reference.
+ value,
+ walk_state,
+ &node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ type = ACPI_TYPE_ANY;
+ goto exit;
+ }
+ }
+ break;
+
+ case ACPI_REFCLASS_DEBUG:
+
+ /* The Debug Object is of type "DebugObject" */
+
+ type = ACPI_TYPE_DEBUG_OBJECT;
+ goto exit;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown Reference Class 0x%2.2X",
+ obj_desc->reference.class));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+ }
+
+ /*
+ * Now we are guaranteed to have an object that has not been created
+ * via the ref_of or Index operators.
+ */
+ type = obj_desc->common.type;
+
+exit:
+ /* Convert internal types to external types */
+
+ switch (type) {
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
+ type = ACPI_TYPE_FIELD_UNIT;
+ break;
+
+ case ACPI_TYPE_LOCAL_SCOPE:
+
+ /* Per ACPI Specification, Scope is untyped */
+
+ type = ACPI_TYPE_ANY;
+ break;
+
+ default:
+
+ /* No change to Type required */
+
+ break;
+ }
+
+ *return_type = type;
+ if (return_desc) {
+ *return_desc = obj_desc;
+ }
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c
new file mode 100644
index 000000000..334278023
--- /dev/null
+++ b/drivers/acpi/acpica/exresop.c
@@ -0,0 +1,675 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exresop - AML Interpreter operand/object resolution
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "amlcode.h"
+#include "acparser.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exresop")
+
+/* Local prototypes */
+static acpi_status
+acpi_ex_check_object_type(acpi_object_type type_needed,
+ acpi_object_type this_type, void *object);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_check_object_type
+ *
+ * PARAMETERS: type_needed Object type needed
+ * this_type Actual object type
+ * Object Object pointer
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check required type against actual type
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_check_object_type(acpi_object_type type_needed,
+ acpi_object_type this_type, void *object)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ if (type_needed == ACPI_TYPE_ANY) {
+
+ /* All types OK, so we don't perform any typechecks */
+
+ return (AE_OK);
+ }
+
+ if (type_needed == ACPI_TYPE_LOCAL_REFERENCE) {
+ /*
+ * Allow the AML "Constant" opcodes (Zero, One, etc.) to be reference
+ * objects and thus allow them to be targets. (As per the ACPI
+ * specification, a store to a constant is a noop.)
+ */
+ if ((this_type == ACPI_TYPE_INTEGER) &&
+ (((union acpi_operand_object *)object)->common.flags &
+ AOPOBJ_AML_CONSTANT)) {
+ return (AE_OK);
+ }
+ }
+
+ if (type_needed != this_type) {
+ ACPI_ERROR((AE_INFO,
+ "Needed type [%s], found [%s] %p",
+ acpi_ut_get_type_name(type_needed),
+ acpi_ut_get_type_name(this_type), object));
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_resolve_operands
+ *
+ * PARAMETERS: opcode - Opcode being interpreted
+ * stack_ptr - Pointer to the operand stack to be
+ * resolved
+ * walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert multiple input operands to the types required by the
+ * target operator.
+ *
+ * Each 5-bit group in arg_types represents one required
+ * operand and indicates the required Type. The corresponding operand
+ * will be converted to the required type if possible, otherwise we
+ * abort with an exception.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_resolve_operands(u16 opcode,
+ union acpi_operand_object **stack_ptr,
+ struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status = AE_OK;
+ u8 object_type;
+ u32 arg_types;
+ const struct acpi_opcode_info *op_info;
+ u32 this_arg_type;
+ acpi_object_type type_needed;
+ u16 target_op = 0;
+
+ ACPI_FUNCTION_TRACE_U32(ex_resolve_operands, opcode);
+
+ op_info = acpi_ps_get_opcode_info(opcode);
+ if (op_info->class == AML_CLASS_UNKNOWN) {
+ return_ACPI_STATUS(AE_AML_BAD_OPCODE);
+ }
+
+ arg_types = op_info->runtime_args;
+ if (arg_types == ARGI_INVALID_OPCODE) {
+ ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", opcode));
+
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Opcode %X [%s] RequiredOperandTypes=%8.8X\n",
+ opcode, op_info->name, arg_types));
+
+ /*
+ * Normal exit is with (arg_types == 0) at end of argument list.
+ * Function will return an exception from within the loop upon
+ * finding an entry which is not (or cannot be converted
+ * to) the required type; if stack underflows; or upon
+ * finding a NULL stack entry (which should not happen).
+ */
+ while (GET_CURRENT_ARG_TYPE(arg_types)) {
+ if (!stack_ptr || !*stack_ptr) {
+ ACPI_ERROR((AE_INFO, "Null stack entry at %p",
+ stack_ptr));
+
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ /* Extract useful items */
+
+ obj_desc = *stack_ptr;
+
+ /* Decode the descriptor type */
+
+ switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) {
+ case ACPI_DESC_TYPE_NAMED:
+
+ /* Namespace Node */
+
+ object_type =
+ ((struct acpi_namespace_node *)obj_desc)->type;
+
+ /*
+ * Resolve an alias object. The construction of these objects
+ * guarantees that there is only one level of alias indirection;
+ * thus, the attached object is always the aliased namespace node
+ */
+ if (object_type == ACPI_TYPE_LOCAL_ALIAS) {
+ obj_desc = acpi_ns_get_attached_object((struct
+ acpi_namespace_node
+ *)
+ obj_desc);
+ *stack_ptr = obj_desc;
+ object_type =
+ ((struct acpi_namespace_node *)obj_desc)->
+ type;
+ }
+ break;
+
+ case ACPI_DESC_TYPE_OPERAND:
+
+ /* ACPI internal object */
+
+ object_type = obj_desc->common.type;
+
+ /* Check for bad acpi_object_type */
+
+ if (!acpi_ut_valid_object_type(object_type)) {
+ ACPI_ERROR((AE_INFO,
+ "Bad operand object type [0x%X]",
+ object_type));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ if (object_type == (u8) ACPI_TYPE_LOCAL_REFERENCE) {
+
+ /* Validate the Reference */
+
+ switch (obj_desc->reference.class) {
+ case ACPI_REFCLASS_DEBUG:
+
+ target_op = AML_DEBUG_OP;
+
+ ACPI_FALLTHROUGH;
+
+ case ACPI_REFCLASS_ARG:
+ case ACPI_REFCLASS_LOCAL:
+ case ACPI_REFCLASS_INDEX:
+ case ACPI_REFCLASS_REFOF:
+ case ACPI_REFCLASS_TABLE: /* ddb_handle from LOAD_OP or LOAD_TABLE_OP */
+ case ACPI_REFCLASS_NAME: /* Reference to a named object */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Operand is a Reference, Class [%s] %2.2X\n",
+ acpi_ut_get_reference_name
+ (obj_desc),
+ obj_desc->reference.
+ class));
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown Reference Class 0x%2.2X in %p",
+ obj_desc->reference.class,
+ obj_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ }
+ break;
+
+ default:
+
+ /* Invalid descriptor */
+
+ ACPI_ERROR((AE_INFO, "Invalid descriptor %p [%s]",
+ obj_desc,
+ acpi_ut_get_descriptor_name(obj_desc)));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /* Get one argument type, point to the next */
+
+ this_arg_type = GET_CURRENT_ARG_TYPE(arg_types);
+ INCREMENT_ARG_LIST(arg_types);
+
+ /*
+ * Handle cases where the object does not need to be
+ * resolved to a value
+ */
+ switch (this_arg_type) {
+ case ARGI_REF_OR_STRING: /* Can be a String or Reference */
+
+ if ((ACPI_GET_DESCRIPTOR_TYPE(obj_desc) ==
+ ACPI_DESC_TYPE_OPERAND) &&
+ (obj_desc->common.type == ACPI_TYPE_STRING)) {
+ /*
+ * String found - the string references a named object and
+ * must be resolved to a node
+ */
+ goto next_operand;
+ }
+
+ /*
+ * Else not a string - fall through to the normal Reference
+ * case below
+ */
+ ACPI_FALLTHROUGH;
+
+ case ARGI_REFERENCE: /* References: */
+ case ARGI_INTEGER_REF:
+ case ARGI_OBJECT_REF:
+ case ARGI_DEVICE_REF:
+ case ARGI_TARGETREF: /* Allows implicit conversion rules before store */
+ case ARGI_FIXED_TARGET: /* No implicit conversion before store to target */
+ case ARGI_SIMPLE_TARGET: /* Name, Local, or arg - no implicit conversion */
+ case ARGI_STORE_TARGET:
+
+ /*
+ * Need an operand of type ACPI_TYPE_LOCAL_REFERENCE
+ * A Namespace Node is OK as-is
+ */
+ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) ==
+ ACPI_DESC_TYPE_NAMED) {
+ goto next_operand;
+ }
+
+ status =
+ acpi_ex_check_object_type(ACPI_TYPE_LOCAL_REFERENCE,
+ object_type, obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ goto next_operand;
+
+ case ARGI_DATAREFOBJ: /* Store operator only */
+ /*
+ * We don't want to resolve index_op reference objects during
+ * a store because this would be an implicit de_ref_of operation.
+ * Instead, we just want to store the reference object.
+ * -- All others must be resolved below.
+ */
+ if ((opcode == AML_STORE_OP) &&
+ ((*stack_ptr)->common.type ==
+ ACPI_TYPE_LOCAL_REFERENCE)
+ && ((*stack_ptr)->reference.class ==
+ ACPI_REFCLASS_INDEX)) {
+ goto next_operand;
+ }
+ break;
+
+ default:
+
+ /* All cases covered above */
+
+ break;
+ }
+
+ /*
+ * Resolve this object to a value
+ */
+ status = acpi_ex_resolve_to_value(stack_ptr, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Get the resolved object */
+
+ obj_desc = *stack_ptr;
+
+ /*
+ * Check the resulting object (value) type
+ */
+ switch (this_arg_type) {
+ /*
+ * For the simple cases, only one type of resolved object
+ * is allowed
+ */
+ case ARGI_MUTEX:
+
+ /* Need an operand of type ACPI_TYPE_MUTEX */
+
+ type_needed = ACPI_TYPE_MUTEX;
+ break;
+
+ case ARGI_EVENT:
+
+ /* Need an operand of type ACPI_TYPE_EVENT */
+
+ type_needed = ACPI_TYPE_EVENT;
+ break;
+
+ case ARGI_PACKAGE: /* Package */
+
+ /* Need an operand of type ACPI_TYPE_PACKAGE */
+
+ type_needed = ACPI_TYPE_PACKAGE;
+ break;
+
+ case ARGI_ANYTYPE:
+
+ /* Any operand type will do */
+
+ type_needed = ACPI_TYPE_ANY;
+ break;
+
+ case ARGI_DDBHANDLE:
+
+ /* Need an operand of type ACPI_TYPE_DDB_HANDLE */
+
+ type_needed = ACPI_TYPE_LOCAL_REFERENCE;
+ break;
+
+ /*
+ * The more complex cases allow multiple resolved object types
+ */
+ case ARGI_INTEGER:
+
+ /*
+ * Need an operand of type ACPI_TYPE_INTEGER, but we can
+ * implicitly convert from a STRING or BUFFER.
+ *
+ * Known as "Implicit Source Operand Conversion"
+ */
+ status = acpi_ex_convert_to_integer(obj_desc, stack_ptr,
+ ACPI_IMPLICIT_CONVERSION);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_TYPE) {
+ ACPI_ERROR((AE_INFO,
+ "Needed [Integer/String/Buffer], found [%s] %p",
+ acpi_ut_get_object_type_name
+ (obj_desc), obj_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ return_ACPI_STATUS(status);
+ }
+
+ if (obj_desc != *stack_ptr) {
+ acpi_ut_remove_reference(obj_desc);
+ }
+ goto next_operand;
+
+ case ARGI_BUFFER:
+ /*
+ * Need an operand of type ACPI_TYPE_BUFFER,
+ * But we can implicitly convert from a STRING or INTEGER
+ * aka - "Implicit Source Operand Conversion"
+ */
+ status = acpi_ex_convert_to_buffer(obj_desc, stack_ptr);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_TYPE) {
+ ACPI_ERROR((AE_INFO,
+ "Needed [Integer/String/Buffer], found [%s] %p",
+ acpi_ut_get_object_type_name
+ (obj_desc), obj_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ return_ACPI_STATUS(status);
+ }
+
+ if (obj_desc != *stack_ptr) {
+ acpi_ut_remove_reference(obj_desc);
+ }
+ goto next_operand;
+
+ case ARGI_STRING:
+ /*
+ * Need an operand of type ACPI_TYPE_STRING,
+ * But we can implicitly convert from a BUFFER or INTEGER
+ * aka - "Implicit Source Operand Conversion"
+ */
+ status =
+ acpi_ex_convert_to_string(obj_desc, stack_ptr,
+ ACPI_IMPLICIT_CONVERT_HEX);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_TYPE) {
+ ACPI_ERROR((AE_INFO,
+ "Needed [Integer/String/Buffer], found [%s] %p",
+ acpi_ut_get_object_type_name
+ (obj_desc), obj_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ return_ACPI_STATUS(status);
+ }
+
+ if (obj_desc != *stack_ptr) {
+ acpi_ut_remove_reference(obj_desc);
+ }
+ goto next_operand;
+
+ case ARGI_COMPUTEDATA:
+
+ /* Need an operand of type INTEGER, STRING or BUFFER */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ /* Valid operand */
+ break;
+
+ default:
+ ACPI_ERROR((AE_INFO,
+ "Needed [Integer/String/Buffer], found [%s] %p",
+ acpi_ut_get_object_type_name
+ (obj_desc), obj_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ goto next_operand;
+
+ case ARGI_BUFFER_OR_STRING:
+
+ /* Need an operand of type STRING or BUFFER */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ /* Valid operand */
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ /* Highest priority conversion is to type Buffer */
+
+ status =
+ acpi_ex_convert_to_buffer(obj_desc,
+ stack_ptr);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (obj_desc != *stack_ptr) {
+ acpi_ut_remove_reference(obj_desc);
+ }
+ break;
+
+ default:
+ ACPI_ERROR((AE_INFO,
+ "Needed [Integer/String/Buffer], found [%s] %p",
+ acpi_ut_get_object_type_name
+ (obj_desc), obj_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ goto next_operand;
+
+ case ARGI_DATAOBJECT:
+ /*
+ * ARGI_DATAOBJECT is only used by the size_of operator.
+ * Need a buffer, string, package, or ref_of reference.
+ *
+ * The only reference allowed here is a direct reference to
+ * a namespace node.
+ */
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_PACKAGE:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ /* Valid operand */
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Needed [Buffer/String/Package/Reference], found [%s] %p",
+ acpi_ut_get_object_type_name
+ (obj_desc), obj_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ goto next_operand;
+
+ case ARGI_COMPLEXOBJ:
+
+ /* Need a buffer or package or (ACPI 2.0) String */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_PACKAGE:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ /* Valid operand */
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Needed [Buffer/String/Package], found [%s] %p",
+ acpi_ut_get_object_type_name
+ (obj_desc), obj_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ goto next_operand;
+
+ case ARGI_REGION_OR_BUFFER: /* Used by Load() only */
+
+ /*
+ * Need an operand of type REGION or a BUFFER
+ * (which could be a resolved region field)
+ */
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_REGION:
+
+ /* Valid operand */
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Needed [Region/Buffer], found [%s] %p",
+ acpi_ut_get_object_type_name
+ (obj_desc), obj_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ goto next_operand;
+
+ case ARGI_DATAREFOBJ:
+
+ /* Used by the Store() operator only */
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_PACKAGE:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+ case ACPI_TYPE_DDB_HANDLE:
+
+ /* Valid operand */
+ break;
+
+ default:
+
+ if (acpi_gbl_enable_interpreter_slack) {
+ /*
+ * Enable original behavior of Store(), allowing any
+ * and all objects as the source operand. The ACPI
+ * spec does not allow this, however.
+ */
+ break;
+ }
+
+ if (target_op == AML_DEBUG_OP) {
+
+ /* Allow store of any object to the Debug object */
+
+ break;
+ }
+
+ ACPI_ERROR((AE_INFO,
+ "Needed Integer/Buffer/String/Package/Ref/Ddb]"
+ ", found [%s] %p",
+ acpi_ut_get_object_type_name
+ (obj_desc), obj_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+ goto next_operand;
+
+ default:
+
+ /* Unknown type */
+
+ ACPI_ERROR((AE_INFO,
+ "Internal - Unknown ARGI (required operand) type 0x%X",
+ this_arg_type));
+
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Make sure that the original object was resolved to the
+ * required object type (Simple cases only).
+ */
+ status =
+ acpi_ex_check_object_type(type_needed,
+ (*stack_ptr)->common.type,
+ *stack_ptr);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+next_operand:
+ /*
+ * If more operands needed, decrement stack_ptr to point
+ * to next operand on stack
+ */
+ if (GET_CURRENT_ARG_TYPE(arg_types)) {
+ stack_ptr--;
+ }
+ }
+
+ ACPI_DUMP_OPERANDS(walk_state->operands,
+ acpi_ps_get_opcode_name(opcode),
+ walk_state->num_operands);
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exserial.c b/drivers/acpi/acpica/exserial.c
new file mode 100644
index 000000000..4da20d784
--- /dev/null
+++ b/drivers/acpi/acpica/exserial.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exserial - field_unit support for serial address spaces
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exserial")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_read_gpio
+ *
+ * PARAMETERS: obj_desc - The named field to read
+ * buffer - Where the return data is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read from a named field that references a Generic Serial Bus
+ * field
+ *
+ ******************************************************************************/
+acpi_status acpi_ex_read_gpio(union acpi_operand_object *obj_desc, void *buffer)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_read_gpio, obj_desc);
+
+ /*
+ * For GPIO (general_purpose_io), the Address will be the bit offset
+ * from the previous Connection() operator, making it effectively a
+ * pin number index. The bit_length is the length of the field, which
+ * is thus the number of pins.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "GPIO FieldRead [FROM]: Pin %u Bits %u\n",
+ obj_desc->field.pin_number_index,
+ obj_desc->field.bit_length));
+
+ /* Lock entire transaction if requested */
+
+ acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
+
+ /* Perform the read */
+
+ status = acpi_ex_access_region(obj_desc, 0, (u64 *)buffer, ACPI_READ);
+
+ acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_write_gpio
+ *
+ * PARAMETERS: source_desc - Contains data to write. Expect to be
+ * an Integer object.
+ * obj_desc - The named field
+ * result_desc - Where the return value is returned, if any
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write to a named field that references a General Purpose I/O
+ * field.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_write_gpio(union acpi_operand_object *source_desc,
+ union acpi_operand_object *obj_desc,
+ union acpi_operand_object **return_buffer)
+{
+ acpi_status status;
+ void *buffer;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_write_gpio, obj_desc);
+
+ /*
+ * For GPIO (general_purpose_io), we will bypass the entire field
+ * mechanism and handoff the bit address and bit width directly to
+ * the handler. The Address will be the bit offset
+ * from the previous Connection() operator, making it effectively a
+ * pin number index. The bit_length is the length of the field, which
+ * is thus the number of pins.
+ */
+ if (source_desc->common.type != ACPI_TYPE_INTEGER) {
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+ "GPIO FieldWrite [FROM]: (%s:%X), Value %.8X [TO]: Pin %u Bits %u\n",
+ acpi_ut_get_type_name(source_desc->common.type),
+ source_desc->common.type,
+ (u32)source_desc->integer.value,
+ obj_desc->field.pin_number_index,
+ obj_desc->field.bit_length));
+
+ buffer = &source_desc->integer.value;
+
+ /* Lock entire transaction if requested */
+
+ acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
+
+ /* Perform the write */
+
+ status = acpi_ex_access_region(obj_desc, 0, (u64 *)buffer, ACPI_WRITE);
+ acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_read_serial_bus
+ *
+ * PARAMETERS: obj_desc - The named field to read
+ * return_buffer - Where the return value is returned, if any
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read from a named field that references a serial bus
+ * (SMBus, IPMI, or GSBus).
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_read_serial_bus(union acpi_operand_object *obj_desc,
+ union acpi_operand_object **return_buffer)
+{
+ acpi_status status;
+ u32 buffer_length;
+ union acpi_operand_object *buffer_desc;
+ u32 function;
+ u16 accessor_type;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_read_serial_bus, obj_desc);
+
+ /*
+ * This is an SMBus, GSBus or IPMI read. We must create a buffer to
+ * hold the data and then directly access the region handler.
+ *
+ * Note: SMBus and GSBus protocol value is passed in upper 16-bits
+ * of Function
+ *
+ * Common buffer format:
+ * Status; (Byte 0 of the data buffer)
+ * Length; (Byte 1 of the data buffer)
+ * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer)
+ */
+ switch (obj_desc->field.region_obj->region.space_id) {
+ case ACPI_ADR_SPACE_SMBUS:
+
+ buffer_length = ACPI_SMBUS_BUFFER_SIZE;
+ function = ACPI_READ | (obj_desc->field.attribute << 16);
+ break;
+
+ case ACPI_ADR_SPACE_IPMI:
+
+ buffer_length = ACPI_IPMI_BUFFER_SIZE;
+ function = ACPI_READ;
+ break;
+
+ case ACPI_ADR_SPACE_GSBUS:
+
+ accessor_type = obj_desc->field.attribute;
+ if (accessor_type == AML_FIELD_ATTRIB_RAW_PROCESS_BYTES) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid direct read using bidirectional write-then-read protocol"));
+
+ return_ACPI_STATUS(AE_AML_PROTOCOL);
+ }
+
+ status =
+ acpi_ex_get_protocol_buffer_length(accessor_type,
+ &buffer_length);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid protocol ID for GSBus: 0x%4.4X",
+ accessor_type));
+
+ return_ACPI_STATUS(status);
+ }
+
+ /* Add header length to get the full size of the buffer */
+
+ buffer_length += ACPI_SERIAL_HEADER_SIZE;
+ function = ACPI_READ | (accessor_type << 16);
+ break;
+
+ case ACPI_ADR_SPACE_PLATFORM_RT:
+
+ buffer_length = ACPI_PRM_INPUT_BUFFER_SIZE;
+ function = ACPI_READ;
+ break;
+
+ default:
+ return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
+ }
+
+ /* Create the local transfer buffer that is returned to the caller */
+
+ buffer_desc = acpi_ut_create_buffer_object(buffer_length);
+ if (!buffer_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Lock entire transaction if requested */
+
+ acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
+
+ /* Call the region handler for the write-then-read */
+
+ status = acpi_ex_access_region(obj_desc, 0,
+ ACPI_CAST_PTR(u64,
+ buffer_desc->buffer.
+ pointer), function);
+ acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
+
+ *return_buffer = buffer_desc;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_write_serial_bus
+ *
+ * PARAMETERS: source_desc - Contains data to write
+ * obj_desc - The named field
+ * return_buffer - Where the return value is returned, if any
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write to a named field that references a serial bus
+ * (SMBus, IPMI, GSBus).
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_write_serial_bus(union acpi_operand_object *source_desc,
+ union acpi_operand_object *obj_desc,
+ union acpi_operand_object **return_buffer)
+{
+ acpi_status status;
+ u32 buffer_length;
+ u32 data_length;
+ void *buffer;
+ union acpi_operand_object *buffer_desc;
+ u32 function;
+ u16 accessor_type;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_write_serial_bus, obj_desc);
+
+ /*
+ * This is an SMBus, GSBus or IPMI write. We will bypass the entire
+ * field mechanism and handoff the buffer directly to the handler.
+ * For these address spaces, the buffer is bidirectional; on a
+ * write, return data is returned in the same buffer.
+ *
+ * Source must be a buffer of sufficient size, these are fixed size:
+ * ACPI_SMBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE.
+ *
+ * Note: SMBus and GSBus protocol type is passed in upper 16-bits
+ * of Function
+ *
+ * Common buffer format:
+ * Status; (Byte 0 of the data buffer)
+ * Length; (Byte 1 of the data buffer)
+ * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer)
+ */
+ if (source_desc->common.type != ACPI_TYPE_BUFFER) {
+ ACPI_ERROR((AE_INFO,
+ "SMBus/IPMI/GenericSerialBus write requires "
+ "Buffer, found type %s",
+ acpi_ut_get_object_type_name(source_desc)));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ switch (obj_desc->field.region_obj->region.space_id) {
+ case ACPI_ADR_SPACE_SMBUS:
+
+ buffer_length = ACPI_SMBUS_BUFFER_SIZE;
+ function = ACPI_WRITE | (obj_desc->field.attribute << 16);
+ break;
+
+ case ACPI_ADR_SPACE_IPMI:
+
+ buffer_length = ACPI_IPMI_BUFFER_SIZE;
+ function = ACPI_WRITE;
+ break;
+
+ case ACPI_ADR_SPACE_GSBUS:
+
+ accessor_type = obj_desc->field.attribute;
+ status =
+ acpi_ex_get_protocol_buffer_length(accessor_type,
+ &buffer_length);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid protocol ID for GSBus: 0x%4.4X",
+ accessor_type));
+
+ return_ACPI_STATUS(status);
+ }
+
+ /* Add header length to get the full size of the buffer */
+
+ buffer_length += ACPI_SERIAL_HEADER_SIZE;
+ function = ACPI_WRITE | (accessor_type << 16);
+ break;
+
+ case ACPI_ADR_SPACE_PLATFORM_RT:
+
+ buffer_length = ACPI_PRM_INPUT_BUFFER_SIZE;
+ function = ACPI_WRITE;
+ break;
+
+ default:
+ return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
+ }
+
+ /* Create the transfer/bidirectional/return buffer */
+
+ buffer_desc = acpi_ut_create_buffer_object(buffer_length);
+ if (!buffer_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* 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);
+ memcpy(buffer, source_desc->buffer.pointer, data_length);
+
+ /* Lock entire transaction if requested */
+
+ acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
+
+ /*
+ * Perform the write (returns status and perhaps data in the
+ * same buffer)
+ */
+ status = acpi_ex_access_region(obj_desc, 0, (u64 *)buffer, function);
+ acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
+
+ *return_buffer = buffer_desc;
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c
new file mode 100644
index 000000000..f99e8cf27
--- /dev/null
+++ b/drivers/acpi/acpica/exstore.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exstore - AML Interpreter object store support
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "amlcode.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exstore")
+
+/* Local prototypes */
+static acpi_status
+acpi_ex_store_object_to_index(union acpi_operand_object *val_desc,
+ union acpi_operand_object *dest_desc,
+ struct acpi_walk_state *walk_state);
+
+static acpi_status
+acpi_ex_store_direct_to_node(union acpi_operand_object *source_desc,
+ struct acpi_namespace_node *node,
+ struct acpi_walk_state *walk_state);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_store
+ *
+ * PARAMETERS: *source_desc - Value to be stored
+ * *dest_desc - Where to store it. Must be an NS node
+ * or union acpi_operand_object of type
+ * Reference;
+ * walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Store the value described by source_desc into the location
+ * described by dest_desc. Called by various interpreter
+ * functions to store the result of an operation into
+ * the destination operand -- not just simply the actual "Store"
+ * ASL operator.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_store(union acpi_operand_object *source_desc,
+ union acpi_operand_object *dest_desc,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *ref_desc = dest_desc;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_store, dest_desc);
+
+ /* Validate parameters */
+
+ if (!source_desc || !dest_desc) {
+ ACPI_ERROR((AE_INFO, "Null parameter"));
+ return_ACPI_STATUS(AE_AML_NO_OPERAND);
+ }
+
+ /* dest_desc can be either a namespace node or an ACPI object */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(dest_desc) == ACPI_DESC_TYPE_NAMED) {
+ /*
+ * Dest is a namespace node,
+ * Storing an object into a Named node.
+ */
+ status = acpi_ex_store_object_to_node(source_desc,
+ (struct
+ acpi_namespace_node *)
+ dest_desc, walk_state,
+ ACPI_IMPLICIT_CONVERSION);
+
+ return_ACPI_STATUS(status);
+ }
+
+ /* Destination object must be a Reference or a Constant object */
+
+ switch (dest_desc->common.type) {
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ /* Allow stores to Constants -- a Noop as per ACPI spec */
+
+ if (dest_desc->common.flags & AOPOBJ_AML_CONSTANT) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ ACPI_FALLTHROUGH;
+
+ default:
+
+ /* Destination is not a Reference object */
+
+ ACPI_ERROR((AE_INFO,
+ "Target is not a Reference or Constant object - [%s] %p",
+ acpi_ut_get_object_type_name(dest_desc),
+ dest_desc));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * Examine the Reference class. These cases are handled:
+ *
+ * 1) Store to Name (Change the object associated with a name)
+ * 2) Store to an indexed area of a Buffer or Package
+ * 3) Store to a Method Local or Arg
+ * 4) Store to the debug object
+ */
+ switch (ref_desc->reference.class) {
+ case ACPI_REFCLASS_REFOF:
+
+ /* Storing an object into a Name "container" */
+
+ status = acpi_ex_store_object_to_node(source_desc,
+ ref_desc->reference.
+ object, walk_state,
+ ACPI_IMPLICIT_CONVERSION);
+ break;
+
+ case ACPI_REFCLASS_INDEX:
+
+ /* Storing to an Index (pointer into a packager or buffer) */
+
+ status =
+ acpi_ex_store_object_to_index(source_desc, ref_desc,
+ walk_state);
+ break;
+
+ case ACPI_REFCLASS_LOCAL:
+ case ACPI_REFCLASS_ARG:
+
+ /* Store to a method local/arg */
+
+ status =
+ acpi_ds_store_object_to_local(ref_desc->reference.class,
+ ref_desc->reference.value,
+ source_desc, walk_state);
+ break;
+
+ case ACPI_REFCLASS_DEBUG:
+ /*
+ * Storing to the Debug object causes the value stored to be
+ * displayed and otherwise has no effect -- see ACPI Specification
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "**** Write to Debug Object: Object %p [%s] ****:\n\n",
+ source_desc,
+ acpi_ut_get_object_type_name(source_desc)));
+
+ ACPI_DEBUG_OBJECT(source_desc, 0, 0);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown Reference Class 0x%2.2X",
+ ref_desc->reference.class));
+ ACPI_DUMP_ENTRY(ref_desc, ACPI_LV_INFO);
+
+ status = AE_AML_INTERNAL;
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_store_object_to_index
+ *
+ * PARAMETERS: *source_desc - Value to be stored
+ * *dest_desc - Named object to receive the value
+ * walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Store the object to indexed Buffer or Package element
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_store_object_to_index(union acpi_operand_object *source_desc,
+ union acpi_operand_object *index_desc,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *new_desc;
+ u8 value = 0;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ex_store_object_to_index);
+
+ /*
+ * Destination must be a reference pointer, and
+ * must point to either a buffer or a package
+ */
+ switch (index_desc->reference.target_type) {
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * Storing to a package element. Copy the object and replace
+ * any existing object with the new object. No implicit
+ * conversion is performed.
+ *
+ * The object at *(index_desc->Reference.Where) is the
+ * element within the package that is to be modified.
+ * The parent package object is at index_desc->Reference.Object
+ */
+ obj_desc = *(index_desc->reference.where);
+
+ if (source_desc->common.type == ACPI_TYPE_LOCAL_REFERENCE &&
+ source_desc->reference.class == ACPI_REFCLASS_TABLE) {
+
+ /* This is a DDBHandle, just add a reference to it */
+
+ acpi_ut_add_reference(source_desc);
+ new_desc = source_desc;
+ } else {
+ /* Normal object, copy it */
+
+ status =
+ acpi_ut_copy_iobject_to_iobject(source_desc,
+ &new_desc,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ if (obj_desc) {
+
+ /* Decrement reference count by the ref count of the parent package */
+
+ for (i = 0; i < ((union acpi_operand_object *)
+ index_desc->reference.object)->common.
+ reference_count; i++) {
+ acpi_ut_remove_reference(obj_desc);
+ }
+ }
+
+ *(index_desc->reference.where) = new_desc;
+
+ /* Increment ref count by the ref count of the parent package-1 */
+
+ for (i = 1; i < ((union acpi_operand_object *)
+ index_desc->reference.object)->common.
+ reference_count; i++) {
+ acpi_ut_add_reference(new_desc);
+ }
+
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+ /*
+ * Store into a Buffer or String (not actually a real buffer_field)
+ * at a location defined by an Index.
+ *
+ * The first 8-bit element of the source object is written to the
+ * 8-bit Buffer location defined by the Index destination object,
+ * according to the ACPI 2.0 specification.
+ */
+
+ /*
+ * Make sure the target is a Buffer or String. An error should
+ * not happen here, since the reference_object was constructed
+ * by the INDEX_OP code.
+ */
+ obj_desc = index_desc->reference.object;
+ if ((obj_desc->common.type != ACPI_TYPE_BUFFER) &&
+ (obj_desc->common.type != ACPI_TYPE_STRING)) {
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * The assignment of the individual elements will be slightly
+ * different for each source type.
+ */
+ switch (source_desc->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ /* Use the least-significant byte of the integer */
+
+ value = (u8) (source_desc->integer.value);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_STRING:
+
+ /* Note: Takes advantage of common string/buffer fields */
+
+ value = source_desc->buffer.pointer[0];
+ break;
+
+ default:
+
+ /* All other types are invalid */
+
+ ACPI_ERROR((AE_INFO,
+ "Source must be type [Integer/Buffer/String], found [%s]",
+ acpi_ut_get_object_type_name(source_desc)));
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /* Store the source value into the target buffer byte */
+
+ obj_desc->buffer.pointer[index_desc->reference.value] = value;
+ break;
+
+ default:
+ ACPI_ERROR((AE_INFO,
+ "Target is not of type [Package/BufferField]"));
+ status = AE_AML_TARGET_TYPE;
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_store_object_to_node
+ *
+ * PARAMETERS: source_desc - Value to be stored
+ * node - Named object to receive the value
+ * walk_state - Current walk state
+ * implicit_conversion - Perform implicit conversion (yes/no)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Store the object to the named object.
+ *
+ * The assignment of an object to a named object is handled here.
+ * The value passed in will replace the current value (if any)
+ * with the input value.
+ *
+ * When storing into an object the data is converted to the
+ * target object type then stored in the object. This means
+ * that the target object type (for an initialized target) will
+ * not be changed by a store operation. A copy_object can change
+ * the target type, however.
+ *
+ * The implicit_conversion flag is set to NO/FALSE only when
+ * storing to an arg_x -- as per the rules of the ACPI spec.
+ *
+ * Assumes parameters are already validated.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_store_object_to_node(union acpi_operand_object *source_desc,
+ struct acpi_namespace_node *node,
+ struct acpi_walk_state *walk_state,
+ u8 implicit_conversion)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *target_desc;
+ union acpi_operand_object *new_desc;
+ acpi_object_type target_type;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_store_object_to_node, source_desc);
+
+ /* Get current type of the node, and object attached to Node */
+
+ target_type = acpi_ns_get_type(node);
+ target_desc = acpi_ns_get_attached_object(node);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Storing %p [%s] to node %p [%s]\n",
+ source_desc,
+ acpi_ut_get_object_type_name(source_desc), node,
+ acpi_ut_get_type_name(target_type)));
+
+ /* Only limited target types possible for everything except copy_object */
+
+ if (walk_state->opcode != AML_COPY_OBJECT_OP) {
+ /*
+ * Only copy_object allows all object types to be overwritten. For
+ * target_ref(s), there are restrictions on the object types that
+ * are allowed.
+ *
+ * Allowable operations/typing for Store:
+ *
+ * 1) Simple Store
+ * Integer --> Integer (Named/Local/Arg)
+ * String --> String (Named/Local/Arg)
+ * Buffer --> Buffer (Named/Local/Arg)
+ * Package --> Package (Named/Local/Arg)
+ *
+ * 2) Store with implicit conversion
+ * Integer --> String or Buffer (Named)
+ * String --> Integer or Buffer (Named)
+ * Buffer --> Integer or String (Named)
+ */
+ switch (target_type) {
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * Here, can only store a package to an existing package.
+ * Storing a package to a Local/Arg is OK, and handled
+ * elsewhere.
+ */
+ if (walk_state->opcode == AML_STORE_OP) {
+ if (source_desc->common.type !=
+ ACPI_TYPE_PACKAGE) {
+ ACPI_ERROR((AE_INFO,
+ "Cannot assign type [%s] to [Package] "
+ "(source must be type Pkg)",
+ acpi_ut_get_object_type_name
+ (source_desc)));
+
+ return_ACPI_STATUS(AE_AML_TARGET_TYPE);
+ }
+ break;
+ }
+
+ ACPI_FALLTHROUGH;
+
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_EVENT:
+ case ACPI_TYPE_MUTEX:
+ case ACPI_TYPE_REGION:
+ case ACPI_TYPE_POWER:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+
+ ACPI_ERROR((AE_INFO,
+ "Target must be [Buffer/Integer/String/Reference]"
+ ", found [%s] (%4.4s)",
+ acpi_ut_get_type_name(node->type),
+ node->name.ascii));
+
+ return_ACPI_STATUS(AE_AML_TARGET_TYPE);
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Resolve the source object to an actual value
+ * (If it is a reference object)
+ */
+ status = acpi_ex_resolve_object(&source_desc, target_type, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Do the actual store operation */
+
+ switch (target_type) {
+ /*
+ * The simple data types all support implicit source operand
+ * conversion before the store.
+ */
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ if ((walk_state->opcode == AML_COPY_OBJECT_OP) ||
+ !implicit_conversion) {
+ /*
+ * However, copy_object and Stores to arg_x do not perform
+ * an implicit conversion, as per the ACPI specification.
+ * A direct store is performed instead.
+ */
+ status =
+ acpi_ex_store_direct_to_node(source_desc, node,
+ walk_state);
+ break;
+ }
+
+ /* Store with implicit source operand conversion support */
+
+ status =
+ acpi_ex_store_object_to_object(source_desc, target_desc,
+ &new_desc, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (new_desc != target_desc) {
+ /*
+ * Store the new new_desc as the new value of the Name, and set
+ * the Name's type to that of the value being stored in it.
+ * source_desc reference count is incremented by attach_object.
+ *
+ * Note: This may change the type of the node if an explicit
+ * store has been performed such that the node/object type
+ * has been changed.
+ */
+ status =
+ acpi_ns_attach_object(node, new_desc,
+ new_desc->common.type);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Store type [%s] into [%s] via Convert/Attach\n",
+ acpi_ut_get_object_type_name
+ (source_desc),
+ acpi_ut_get_object_type_name
+ (new_desc)));
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+ /*
+ * For all fields, always write the source data to the target
+ * field. Any required implicit source operand conversion is
+ * performed in the function below as necessary. Note, field
+ * objects must retain their original type permanently.
+ */
+ status = acpi_ex_write_data_to_field(source_desc, target_desc,
+ &walk_state->result_obj);
+ break;
+
+ default:
+ /*
+ * copy_object operator: No conversions for all other types.
+ * Instead, directly store a copy of the source object.
+ *
+ * This is the ACPI spec-defined behavior for the copy_object
+ * operator. (Note, for this default case, all normal
+ * Store/Target operations exited above with an error).
+ */
+ status =
+ acpi_ex_store_direct_to_node(source_desc, node, walk_state);
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_store_direct_to_node
+ *
+ * PARAMETERS: source_desc - Value to be stored
+ * node - Named object to receive the value
+ * walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: "Store" an object directly to a node. This involves a copy
+ * and an attach.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_store_direct_to_node(union acpi_operand_object *source_desc,
+ struct acpi_namespace_node *node,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ union acpi_operand_object *new_desc;
+
+ ACPI_FUNCTION_TRACE(ex_store_direct_to_node);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Storing [%s] (%p) directly into node [%s] (%p)"
+ " with no implicit conversion\n",
+ acpi_ut_get_object_type_name(source_desc),
+ source_desc, acpi_ut_get_type_name(node->type),
+ node));
+
+ /* Copy the source object to a new object */
+
+ status =
+ acpi_ut_copy_iobject_to_iobject(source_desc, &new_desc, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Attach the new object to the node */
+
+ status = acpi_ns_attach_object(node, new_desc, new_desc->common.type);
+ acpi_ut_remove_reference(new_desc);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exstoren.c b/drivers/acpi/acpica/exstoren.c
new file mode 100644
index 000000000..c848b328e
--- /dev/null
+++ b/drivers/acpi/acpica/exstoren.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exstoren - AML Interpreter object store support,
+ * Store to Node (namespace object)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exstoren")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_resolve_object
+ *
+ * PARAMETERS: source_desc_ptr - Pointer to the source object
+ * target_type - Current type of the target
+ * walk_state - Current walk state
+ *
+ * RETURN: Status, resolved object in source_desc_ptr.
+ *
+ * DESCRIPTION: Resolve an object. If the object is a reference, dereference
+ * it and return the actual object in the source_desc_ptr.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ex_resolve_object(union acpi_operand_object **source_desc_ptr,
+ acpi_object_type target_type,
+ struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object *source_desc = *source_desc_ptr;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ex_resolve_object);
+
+ /* Ensure we have a Target that can be stored to */
+
+ switch (target_type) {
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+ /*
+ * These cases all require only Integers or values that
+ * can be converted to Integers (Strings or Buffers)
+ */
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+ /*
+ * Stores into a Field/Region or into a Integer/Buffer/String
+ * are all essentially the same. This case handles the
+ * "interchangeable" types Integer, String, and Buffer.
+ */
+ if (source_desc->common.type == ACPI_TYPE_LOCAL_REFERENCE) {
+
+ /* Resolve a reference object first */
+
+ status =
+ acpi_ex_resolve_to_value(source_desc_ptr,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+ }
+
+ /* For copy_object, no further validation necessary */
+
+ if (walk_state->opcode == AML_COPY_OBJECT_OP) {
+ break;
+ }
+
+ /* Must have a Integer, Buffer, or String */
+
+ if ((source_desc->common.type != ACPI_TYPE_INTEGER) &&
+ (source_desc->common.type != ACPI_TYPE_BUFFER) &&
+ (source_desc->common.type != ACPI_TYPE_STRING) &&
+ !((source_desc->common.type == ACPI_TYPE_LOCAL_REFERENCE) &&
+ (source_desc->reference.class == ACPI_REFCLASS_TABLE))) {
+
+ /* Conversion successful but still not a valid type */
+
+ ACPI_ERROR((AE_INFO,
+ "Cannot assign type [%s] to [%s] (must be type Int/Str/Buf)",
+ acpi_ut_get_object_type_name(source_desc),
+ acpi_ut_get_type_name(target_type)));
+
+ status = AE_AML_OPERAND_TYPE;
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_ALIAS:
+ case ACPI_TYPE_LOCAL_METHOD_ALIAS:
+ /*
+ * All aliases should have been resolved earlier, during the
+ * operand resolution phase.
+ */
+ ACPI_ERROR((AE_INFO, "Store into an unresolved Alias object"));
+ status = AE_AML_INTERNAL;
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+ default:
+ /*
+ * All other types than Alias and the various Fields come here,
+ * including the untyped case - ACPI_TYPE_ANY.
+ */
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_store_object_to_object
+ *
+ * PARAMETERS: source_desc - Object to store
+ * dest_desc - Object to receive a copy of the source
+ * new_desc - New object if dest_desc is obsoleted
+ * walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: "Store" an object to another object. This may include
+ * converting the source type to the target type (implicit
+ * conversion), and a copy of the value of the source to
+ * the target.
+ *
+ * The Assignment of an object to another (not named) object
+ * is handled here.
+ * The Source passed in will replace the current value (if any)
+ * with the input value.
+ *
+ * When storing into an object the data is converted to the
+ * target object type then stored in the object. This means
+ * that the target object type (for an initialized target) will
+ * not be changed by a store operation.
+ *
+ * This module allows destination types of Number, String,
+ * Buffer, and Package.
+ *
+ * Assumes parameters are already validated. NOTE: source_desc
+ * resolution (from a reference object) must be performed by
+ * the caller if necessary.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_store_object_to_object(union acpi_operand_object *source_desc,
+ union acpi_operand_object *dest_desc,
+ union acpi_operand_object **new_desc,
+ struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object *actual_src_desc;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_store_object_to_object, source_desc);
+
+ actual_src_desc = source_desc;
+ if (!dest_desc) {
+ /*
+ * There is no destination object (An uninitialized node or
+ * package element), so we can simply copy the source object
+ * creating a new destination object
+ */
+ status =
+ acpi_ut_copy_iobject_to_iobject(actual_src_desc, new_desc,
+ walk_state);
+ return_ACPI_STATUS(status);
+ }
+
+ if (source_desc->common.type != dest_desc->common.type) {
+ /*
+ * The source type does not match the type of the destination.
+ * Perform the "implicit conversion" of the source to the current type
+ * of the target as per the ACPI specification.
+ *
+ * If no conversion performed, actual_src_desc = source_desc.
+ * Otherwise, actual_src_desc is a temporary object to hold the
+ * converted object.
+ */
+ status = acpi_ex_convert_to_target_type(dest_desc->common.type,
+ source_desc,
+ &actual_src_desc,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (source_desc == actual_src_desc) {
+ /*
+ * No conversion was performed. Return the source_desc as the
+ * new object.
+ */
+ *new_desc = source_desc;
+ return_ACPI_STATUS(AE_OK);
+ }
+ }
+
+ /*
+ * We now have two objects of identical types, and we can perform a
+ * copy of the *value* of the source object.
+ */
+ switch (dest_desc->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ dest_desc->integer.value = actual_src_desc->integer.value;
+
+ /* Truncate value if we are executing from a 32-bit ACPI table */
+
+ (void)acpi_ex_truncate_for32bit_table(dest_desc);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ status =
+ acpi_ex_store_string_to_string(actual_src_desc, dest_desc);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ status =
+ acpi_ex_store_buffer_to_buffer(actual_src_desc, dest_desc);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ status =
+ acpi_ut_copy_iobject_to_iobject(actual_src_desc, &dest_desc,
+ walk_state);
+ break;
+
+ default:
+ /*
+ * All other types come here.
+ */
+ ACPI_WARNING((AE_INFO, "Store into type [%s] not implemented",
+ acpi_ut_get_object_type_name(dest_desc)));
+
+ status = AE_NOT_IMPLEMENTED;
+ break;
+ }
+
+ if (actual_src_desc != source_desc) {
+
+ /* Delete the intermediate (temporary) source object */
+
+ acpi_ut_remove_reference(actual_src_desc);
+ }
+
+ *new_desc = dest_desc;
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/exstorob.c b/drivers/acpi/acpica/exstorob.c
new file mode 100644
index 000000000..45c757bbf
--- /dev/null
+++ b/drivers/acpi/acpica/exstorob.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exstorob - AML object store support, store to object
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exstorob")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_store_buffer_to_buffer
+ *
+ * PARAMETERS: source_desc - Source object to copy
+ * target_desc - Destination object of the copy
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Copy a buffer object to another buffer object.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ex_store_buffer_to_buffer(union acpi_operand_object *source_desc,
+ union acpi_operand_object *target_desc)
+{
+ u32 length;
+ u8 *buffer;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_store_buffer_to_buffer, source_desc);
+
+ /* If Source and Target are the same, just return */
+
+ if (source_desc == target_desc) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* We know that source_desc is a buffer by now */
+
+ buffer = ACPI_CAST_PTR(u8, source_desc->buffer.pointer);
+ length = source_desc->buffer.length;
+
+ /*
+ * If target is a buffer of length zero or is a static buffer,
+ * allocate a new buffer of the proper length
+ */
+ if ((target_desc->buffer.length == 0) ||
+ (target_desc->common.flags & AOPOBJ_STATIC_POINTER)) {
+ target_desc->buffer.pointer = ACPI_ALLOCATE(length);
+ if (!target_desc->buffer.pointer) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ target_desc->buffer.length = length;
+ }
+
+ /* Copy source buffer to target buffer */
+
+ if (length <= target_desc->buffer.length) {
+
+ /* Clear existing buffer and copy in the new one */
+
+ memset(target_desc->buffer.pointer, 0,
+ target_desc->buffer.length);
+ memcpy(target_desc->buffer.pointer, buffer, length);
+
+#ifdef ACPI_OBSOLETE_BEHAVIOR
+ /*
+ * NOTE: ACPI versions up to 3.0 specified that the buffer must be
+ * truncated if the string is smaller than the buffer. However, "other"
+ * implementations of ACPI never did this and thus became the defacto
+ * standard. ACPI 3.0A changes this behavior such that the buffer
+ * is no longer truncated.
+ */
+
+ /*
+ * OBSOLETE BEHAVIOR:
+ * If the original source was a string, we must truncate the buffer,
+ * according to the ACPI spec. Integer-to-Buffer and Buffer-to-Buffer
+ * copy must not truncate the original buffer.
+ */
+ if (original_src_type == ACPI_TYPE_STRING) {
+
+ /* Set the new length of the target */
+
+ target_desc->buffer.length = length;
+ }
+#endif
+ } else {
+ /* Truncate the source, copy only what will fit */
+
+ memcpy(target_desc->buffer.pointer, buffer,
+ target_desc->buffer.length);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Truncating source buffer from %X to %X\n",
+ length, target_desc->buffer.length));
+ }
+
+ /* Copy flags */
+
+ target_desc->buffer.flags = source_desc->buffer.flags;
+ target_desc->common.flags &= ~AOPOBJ_STATIC_POINTER;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_store_string_to_string
+ *
+ * PARAMETERS: source_desc - Source object to copy
+ * target_desc - Destination object of the copy
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Copy a String object to another String object
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_store_string_to_string(union acpi_operand_object *source_desc,
+ union acpi_operand_object *target_desc)
+{
+ u32 length;
+ u8 *buffer;
+
+ ACPI_FUNCTION_TRACE_PTR(ex_store_string_to_string, source_desc);
+
+ /* If Source and Target are the same, just return */
+
+ if (source_desc == target_desc) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* We know that source_desc is a string by now */
+
+ buffer = ACPI_CAST_PTR(u8, source_desc->string.pointer);
+ length = source_desc->string.length;
+
+ /*
+ * Replace existing string value if it will fit and the string
+ * pointer is not a static pointer (part of an ACPI table)
+ */
+ if ((length < target_desc->string.length) &&
+ (!(target_desc->common.flags & AOPOBJ_STATIC_POINTER))) {
+ /*
+ * String will fit in existing non-static buffer.
+ * Clear old string and copy in the new one
+ */
+ memset(target_desc->string.pointer, 0,
+ (acpi_size)target_desc->string.length + 1);
+ memcpy(target_desc->string.pointer, buffer, length);
+ } else {
+ /*
+ * Free the current buffer, then allocate a new buffer
+ * large enough to hold the value
+ */
+ if (target_desc->string.pointer &&
+ (!(target_desc->common.flags & AOPOBJ_STATIC_POINTER))) {
+
+ /* Only free if not a pointer into the DSDT */
+
+ ACPI_FREE(target_desc->string.pointer);
+ }
+
+ target_desc->string.pointer =
+ ACPI_ALLOCATE_ZEROED((acpi_size)length + 1);
+
+ if (!target_desc->string.pointer) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ target_desc->common.flags &= ~AOPOBJ_STATIC_POINTER;
+ memcpy(target_desc->string.pointer, buffer, length);
+ }
+
+ /* Set the new target length */
+
+ target_desc->string.length = length;
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/exsystem.c b/drivers/acpi/acpica/exsystem.c
new file mode 100644
index 000000000..7b5470f40
--- /dev/null
+++ b/drivers/acpi/acpica/exsystem.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exsystem - Interface to OS services
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exsystem")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_system_wait_semaphore
+ *
+ * PARAMETERS: semaphore - Semaphore to wait on
+ * timeout - Max time to wait
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Implements a semaphore wait with a check to see if the
+ * semaphore is available immediately. If it is not, the
+ * interpreter is released before waiting.
+ *
+ ******************************************************************************/
+acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ex_system_wait_semaphore);
+
+ status = acpi_os_wait_semaphore(semaphore, 1, ACPI_DO_NOT_WAIT);
+ if (ACPI_SUCCESS(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (status == AE_TIME) {
+
+ /* We must wait, so unlock the interpreter */
+
+ acpi_ex_exit_interpreter();
+ status = acpi_os_wait_semaphore(semaphore, 1, timeout);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "*** Thread awake after blocking, %s\n",
+ acpi_format_exception(status)));
+
+ /* Reacquire the interpreter */
+
+ acpi_ex_enter_interpreter();
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_system_wait_mutex
+ *
+ * PARAMETERS: mutex - Mutex to wait on
+ * timeout - Max time to wait
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Implements a mutex wait with a check to see if the
+ * mutex is available immediately. If it is not, the
+ * interpreter is released before waiting.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ex_system_wait_mutex);
+
+ status = acpi_os_acquire_mutex(mutex, ACPI_DO_NOT_WAIT);
+ if (ACPI_SUCCESS(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (status == AE_TIME) {
+
+ /* We must wait, so unlock the interpreter */
+
+ acpi_ex_exit_interpreter();
+ status = acpi_os_acquire_mutex(mutex, timeout);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "*** Thread awake after blocking, %s\n",
+ acpi_format_exception(status)));
+
+ /* Reacquire the interpreter */
+
+ acpi_ex_enter_interpreter();
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_system_do_stall
+ *
+ * PARAMETERS: how_long_us - The amount of time to stall,
+ * in microseconds
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Suspend running thread for specified amount of time.
+ * Note: ACPI specification requires that Stall() does not
+ * relinquish the processor, and delays longer than 100 usec
+ * should use Sleep() instead. We allow stalls up to 255 usec
+ * for compatibility with other interpreters and existing BIOSs.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_system_do_stall(u32 how_long_us)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_ENTRY();
+
+ if (how_long_us > 255) {
+ /*
+ * Longer than 255 microseconds, this is an error
+ *
+ * (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));
+ 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_os_stall(how_long_us);
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_system_do_sleep
+ *
+ * PARAMETERS: how_long_ms - The amount of time to sleep,
+ * in milliseconds
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Sleep the running thread for specified amount of time.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_system_do_sleep(u64 how_long_ms)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /* Since this thread will sleep, we must release the interpreter */
+
+ acpi_ex_exit_interpreter();
+
+ /*
+ * For compatibility with other ACPI implementations and to prevent
+ * accidental deep sleeps, limit the sleep time to something reasonable.
+ */
+ if (how_long_ms > ACPI_MAX_SLEEP) {
+ how_long_ms = ACPI_MAX_SLEEP;
+ }
+
+ acpi_os_sleep(how_long_ms);
+
+ /* And now we must get the interpreter again */
+
+ acpi_ex_enter_interpreter();
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_system_signal_event
+ *
+ * PARAMETERS: obj_desc - The object descriptor for this op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Provides an access point to perform synchronization operations
+ * within the AML.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_system_signal_event(union acpi_operand_object * obj_desc)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ex_system_signal_event);
+
+ if (obj_desc) {
+ status =
+ acpi_os_signal_semaphore(obj_desc->event.os_semaphore, 1);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_system_wait_event
+ *
+ * PARAMETERS: time_desc - The 'time to delay' object descriptor
+ * obj_desc - The object descriptor for this op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Provides an access point to perform synchronization operations
+ * within the AML. This operation is a request to wait for an
+ * event.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ex_system_wait_event(union acpi_operand_object *time_desc,
+ union acpi_operand_object *obj_desc)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ex_system_wait_event);
+
+ if (obj_desc) {
+ status =
+ acpi_ex_system_wait_semaphore(obj_desc->event.os_semaphore,
+ (u16) time_desc->integer.
+ value);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_system_reset_event
+ *
+ * PARAMETERS: obj_desc - The object descriptor for this op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Reset an event to a known state.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ex_system_reset_event(union acpi_operand_object *obj_desc)
+{
+ acpi_status status = AE_OK;
+ acpi_semaphore temp_semaphore;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * We are going to simply delete the existing semaphore and
+ * create a new one!
+ */
+ status =
+ acpi_os_create_semaphore(ACPI_NO_UNIT_LIMIT, 0, &temp_semaphore);
+ if (ACPI_SUCCESS(status)) {
+ (void)acpi_os_delete_semaphore(obj_desc->event.os_semaphore);
+ obj_desc->event.os_semaphore = temp_semaphore;
+ }
+
+ return (status);
+}
diff --git a/drivers/acpi/acpica/extrace.c b/drivers/acpi/acpica/extrace.c
new file mode 100644
index 000000000..b570d7a7e
--- /dev/null
+++ b/drivers/acpi/acpica/extrace.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: extrace - Support for interpreter execution tracing
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("extrace")
+
+static union acpi_operand_object *acpi_gbl_trace_method_object = NULL;
+
+/* Local prototypes */
+
+#ifdef ACPI_DEBUG_OUTPUT
+static const char *acpi_ex_get_trace_event_name(acpi_trace_event_type type);
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_interpreter_trace_enabled
+ *
+ * PARAMETERS: name - Whether method name should be matched,
+ * this should be checked before starting
+ * the tracer
+ *
+ * RETURN: TRUE if interpreter trace is enabled.
+ *
+ * DESCRIPTION: Check whether interpreter trace is enabled
+ *
+ ******************************************************************************/
+
+static u8 acpi_ex_interpreter_trace_enabled(char *name)
+{
+
+ /* Check if tracing is enabled */
+
+ if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED)) {
+ return (FALSE);
+ }
+
+ /*
+ * Check if tracing is filtered:
+ *
+ * 1. If the tracer is started, acpi_gbl_trace_method_object should have
+ * been filled by the trace starter
+ * 2. If the tracer is not started, acpi_gbl_trace_method_name should be
+ * matched if it is specified
+ * 3. If the tracer is oneshot style, acpi_gbl_trace_method_name should
+ * not be cleared by the trace stopper during the first match
+ */
+ if (acpi_gbl_trace_method_object) {
+ return (TRUE);
+ }
+
+ if (name &&
+ (acpi_gbl_trace_method_name &&
+ strcmp(acpi_gbl_trace_method_name, name))) {
+ return (FALSE);
+ }
+
+ if ((acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT) &&
+ !acpi_gbl_trace_method_name) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_get_trace_event_name
+ *
+ * PARAMETERS: type - Trace event type
+ *
+ * RETURN: Trace event name.
+ *
+ * DESCRIPTION: Used to obtain the full trace event name.
+ *
+ ******************************************************************************/
+
+#ifdef ACPI_DEBUG_OUTPUT
+
+static const char *acpi_ex_get_trace_event_name(acpi_trace_event_type type)
+{
+
+ switch (type) {
+ case ACPI_TRACE_AML_METHOD:
+
+ return "Method";
+
+ case ACPI_TRACE_AML_OPCODE:
+
+ return "Opcode";
+
+ case ACPI_TRACE_AML_REGION:
+
+ return "Region";
+
+ default:
+
+ return "";
+ }
+}
+
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_trace_point
+ *
+ * PARAMETERS: type - Trace event type
+ * begin - TRUE if before execution
+ * aml - Executed AML address
+ * pathname - Object path
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Internal interpreter execution trace.
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_trace_point(acpi_trace_event_type type,
+ u8 begin, u8 *aml, char *pathname)
+{
+
+ ACPI_FUNCTION_NAME(ex_trace_point);
+
+ if (pathname) {
+ ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT,
+ "%s %s [0x%p:%s] execution.\n",
+ acpi_ex_get_trace_event_name(type),
+ begin ? "Begin" : "End", aml, pathname));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT,
+ "%s %s [0x%p] execution.\n",
+ acpi_ex_get_trace_event_name(type),
+ begin ? "Begin" : "End", aml));
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_start_trace_method
+ *
+ * PARAMETERS: method_node - Node of the method
+ * obj_desc - The method object
+ * walk_state - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Start control method execution trace
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_start_trace_method(struct acpi_namespace_node *method_node,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+ char *pathname = NULL;
+ u8 enabled = FALSE;
+
+ ACPI_FUNCTION_NAME(ex_start_trace_method);
+
+ if (method_node) {
+ pathname = acpi_ns_get_normalized_pathname(method_node, TRUE);
+ }
+
+ enabled = acpi_ex_interpreter_trace_enabled(pathname);
+ if (enabled && !acpi_gbl_trace_method_object) {
+ acpi_gbl_trace_method_object = obj_desc;
+ acpi_gbl_original_dbg_level = acpi_dbg_level;
+ acpi_gbl_original_dbg_layer = acpi_dbg_layer;
+ acpi_dbg_level = ACPI_TRACE_LEVEL_ALL;
+ acpi_dbg_layer = ACPI_TRACE_LAYER_ALL;
+
+ if (acpi_gbl_trace_dbg_level) {
+ acpi_dbg_level = acpi_gbl_trace_dbg_level;
+ }
+
+ if (acpi_gbl_trace_dbg_layer) {
+ acpi_dbg_layer = acpi_gbl_trace_dbg_layer;
+ }
+ }
+
+ if (enabled) {
+ ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, TRUE,
+ obj_desc ? obj_desc->method.aml_start : NULL,
+ pathname);
+ }
+
+ if (pathname) {
+ ACPI_FREE(pathname);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_stop_trace_method
+ *
+ * PARAMETERS: method_node - Node of the method
+ * obj_desc - The method object
+ * walk_state - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Stop control method execution trace
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+ char *pathname = NULL;
+ u8 enabled;
+
+ ACPI_FUNCTION_NAME(ex_stop_trace_method);
+
+ if (method_node) {
+ pathname = acpi_ns_get_normalized_pathname(method_node, TRUE);
+ }
+
+ enabled = acpi_ex_interpreter_trace_enabled(NULL);
+
+ if (enabled) {
+ ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, FALSE,
+ obj_desc ? obj_desc->method.aml_start : NULL,
+ pathname);
+ }
+
+ /* Check whether the tracer should be stopped */
+
+ if (acpi_gbl_trace_method_object == obj_desc) {
+
+ /* Disable further tracing if type is one-shot */
+
+ if (acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT) {
+ acpi_gbl_trace_method_name = NULL;
+ }
+
+ acpi_dbg_level = acpi_gbl_original_dbg_level;
+ acpi_dbg_layer = acpi_gbl_original_dbg_layer;
+ acpi_gbl_trace_method_object = NULL;
+ }
+
+ if (pathname) {
+ ACPI_FREE(pathname);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_start_trace_opcode
+ *
+ * PARAMETERS: op - The parser opcode object
+ * walk_state - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Start opcode execution trace
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_start_trace_opcode(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state)
+{
+
+ ACPI_FUNCTION_NAME(ex_start_trace_opcode);
+
+ if (acpi_ex_interpreter_trace_enabled(NULL) &&
+ (acpi_gbl_trace_flags & ACPI_TRACE_OPCODE)) {
+ ACPI_TRACE_POINT(ACPI_TRACE_AML_OPCODE, TRUE,
+ op->common.aml, op->common.aml_op_name);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_stop_trace_opcode
+ *
+ * PARAMETERS: op - The parser opcode object
+ * walk_state - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Stop opcode execution trace
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_stop_trace_opcode(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state)
+{
+
+ ACPI_FUNCTION_NAME(ex_stop_trace_opcode);
+
+ if (acpi_ex_interpreter_trace_enabled(NULL) &&
+ (acpi_gbl_trace_flags & ACPI_TRACE_OPCODE)) {
+ ACPI_TRACE_POINT(ACPI_TRACE_AML_OPCODE, FALSE,
+ op->common.aml, op->common.aml_op_name);
+ }
+}
diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c
new file mode 100644
index 000000000..87f01ce1c
--- /dev/null
+++ b/drivers/acpi/acpica/exutils.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: exutils - interpreter/scanner utilities
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+/*
+ * DEFINE_AML_GLOBALS is tested in amlcode.h
+ * to determine whether certain global names should be "defined" or only
+ * "declared" in the current compilation. This enhances maintainability
+ * by enabling a single header file to embody all knowledge of the names
+ * in question.
+ *
+ * Exactly one module of any executable should #define DEFINE_GLOBALS
+ * before #including the header files which use this convention. The
+ * names in question will be defined and initialized in that module,
+ * and declared as extern in all other modules which #include those
+ * header files.
+ */
+
+#define DEFINE_AML_GLOBALS
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("exutils")
+
+/* Local prototypes */
+static u32 acpi_ex_digits_needed(u64 value, u32 base);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_enter_interpreter
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Enter the interpreter execution region. Failure to enter
+ * the interpreter region is a fatal system error. Used in
+ * conjunction with exit_interpreter.
+ *
+ ******************************************************************************/
+
+void acpi_ex_enter_interpreter(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ex_enter_interpreter);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO,
+ "Could not acquire AML Interpreter mutex"));
+ }
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO, "Could not acquire AML Namespace mutex"));
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_exit_interpreter
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Exit the interpreter execution region. This is the top level
+ * routine used to exit the interpreter when all processing has
+ * been completed, or when the method blocks.
+ *
+ * Cases where the interpreter is unlocked internally:
+ * 1) Method will be blocked on a Sleep() AML opcode
+ * 2) Method will be blocked on an Acquire() AML opcode
+ * 3) Method will be blocked on a Wait() AML opcode
+ * 4) Method will be blocked to acquire the global lock
+ * 5) Method will be blocked waiting to execute a serialized control
+ * method that is currently executing
+ * 6) About to invoke a user-installed opregion handler
+ *
+ ******************************************************************************/
+
+void acpi_ex_exit_interpreter(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ex_exit_interpreter);
+
+ status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO, "Could not release AML Namespace mutex"));
+ }
+ status = acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO,
+ "Could not release AML Interpreter mutex"));
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_truncate_for32bit_table
+ *
+ * PARAMETERS: obj_desc - Object to be truncated
+ *
+ * RETURN: TRUE if a truncation was performed, FALSE otherwise.
+ *
+ * DESCRIPTION: Truncate an ACPI Integer to 32 bits if the execution mode is
+ * 32-bit, as determined by the revision of the DSDT.
+ *
+ ******************************************************************************/
+
+u8 acpi_ex_truncate_for32bit_table(union acpi_operand_object *obj_desc)
+{
+
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * Object must be a valid number and we must be executing
+ * a control method. Object could be NS node for AML_INT_NAMEPATH_OP.
+ */
+ if ((!obj_desc) ||
+ (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) ||
+ (obj_desc->common.type != ACPI_TYPE_INTEGER)) {
+ return (FALSE);
+ }
+
+ if ((acpi_gbl_integer_byte_width == 4) &&
+ (obj_desc->integer.value > (u64)ACPI_UINT32_MAX)) {
+ /*
+ * We are executing in a 32-bit ACPI table. Truncate
+ * the value to 32 bits by zeroing out the upper 32-bit field
+ */
+ obj_desc->integer.value &= (u64)ACPI_UINT32_MAX;
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_acquire_global_lock
+ *
+ * PARAMETERS: field_flags - Flags with Lock rule:
+ * always_lock or never_lock
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Obtain the ACPI hardware Global Lock, only if the field
+ * flags specify that it is to be obtained before field access.
+ *
+ ******************************************************************************/
+
+void acpi_ex_acquire_global_lock(u32 field_flags)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ex_acquire_global_lock);
+
+ /* Only use the lock if the always_lock bit is set */
+
+ if (!(field_flags & AML_FIELD_LOCK_RULE_MASK)) {
+ return_VOID;
+ }
+
+ /* Attempt to get the global lock, wait forever */
+
+ status = acpi_ex_acquire_mutex_object(ACPI_WAIT_FOREVER,
+ acpi_gbl_global_lock_mutex,
+ acpi_os_get_thread_id());
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not acquire Global Lock"));
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_release_global_lock
+ *
+ * PARAMETERS: field_flags - Flags with Lock rule:
+ * always_lock or never_lock
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Release the ACPI hardware Global Lock
+ *
+ ******************************************************************************/
+
+void acpi_ex_release_global_lock(u32 field_flags)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ex_release_global_lock);
+
+ /* Only use the lock if the always_lock bit is set */
+
+ if (!(field_flags & AML_FIELD_LOCK_RULE_MASK)) {
+ return_VOID;
+ }
+
+ /* Release the global lock */
+
+ status = acpi_ex_release_mutex_object(acpi_gbl_global_lock_mutex);
+ if (ACPI_FAILURE(status)) {
+
+ /* Report the error, but there isn't much else we can do */
+
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not release Global Lock"));
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_digits_needed
+ *
+ * PARAMETERS: value - Value to be represented
+ * base - Base of representation
+ *
+ * RETURN: The number of digits.
+ *
+ * DESCRIPTION: Calculate the number of digits needed to represent the Value
+ * in the given Base (Radix)
+ *
+ ******************************************************************************/
+
+static u32 acpi_ex_digits_needed(u64 value, u32 base)
+{
+ u32 num_digits;
+ u64 current_value;
+
+ ACPI_FUNCTION_TRACE(ex_digits_needed);
+
+ /* u64 is unsigned, so we don't worry about a '-' prefix */
+
+ if (value == 0) {
+ return_UINT32(1);
+ }
+
+ current_value = value;
+ num_digits = 0;
+
+ /* Count the digits in the requested base */
+
+ while (current_value) {
+ (void)acpi_ut_short_divide(current_value, base, &current_value,
+ NULL);
+ num_digits++;
+ }
+
+ return_UINT32(num_digits);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_eisa_id_to_string
+ *
+ * PARAMETERS: out_string - Where to put the converted string (8 bytes)
+ * compressed_id - EISAID to be converted
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Convert a numeric EISAID to string representation. Return
+ * buffer must be large enough to hold the string. The string
+ * returned is always exactly of length ACPI_EISAID_STRING_SIZE
+ * (includes null terminator). The EISAID is always 32 bits.
+ *
+ ******************************************************************************/
+
+void acpi_ex_eisa_id_to_string(char *out_string, u64 compressed_id)
+{
+ u32 swapped_id;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* The EISAID should be a 32-bit integer */
+
+ if (compressed_id > ACPI_UINT32_MAX) {
+ ACPI_WARNING((AE_INFO,
+ "Expected EISAID is larger than 32 bits: "
+ "0x%8.8X%8.8X, truncating",
+ ACPI_FORMAT_UINT64(compressed_id)));
+ }
+
+ /* Swap ID to big-endian to get contiguous bits */
+
+ swapped_id = acpi_ut_dword_byte_swap((u32)compressed_id);
+
+ /* First 3 bytes are uppercase letters. Next 4 bytes are hexadecimal */
+
+ out_string[0] =
+ (char)(0x40 + (((unsigned long)swapped_id >> 26) & 0x1F));
+ out_string[1] = (char)(0x40 + ((swapped_id >> 21) & 0x1F));
+ out_string[2] = (char)(0x40 + ((swapped_id >> 16) & 0x1F));
+ out_string[3] = acpi_ut_hex_to_ascii_char((u64) swapped_id, 12);
+ out_string[4] = acpi_ut_hex_to_ascii_char((u64) swapped_id, 8);
+ out_string[5] = acpi_ut_hex_to_ascii_char((u64) swapped_id, 4);
+ out_string[6] = acpi_ut_hex_to_ascii_char((u64) swapped_id, 0);
+ out_string[7] = 0;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_integer_to_string
+ *
+ * PARAMETERS: out_string - Where to put the converted string. At least
+ * 21 bytes are needed to hold the largest
+ * possible 64-bit integer.
+ * value - Value to be converted
+ *
+ * RETURN: Converted string in out_string
+ *
+ * DESCRIPTION: Convert a 64-bit integer to decimal string representation.
+ * Assumes string buffer is large enough to hold the string. The
+ * largest string is (ACPI_MAX64_DECIMAL_DIGITS + 1).
+ *
+ ******************************************************************************/
+
+void acpi_ex_integer_to_string(char *out_string, u64 value)
+{
+ u32 count;
+ u32 digits_needed;
+ u32 remainder;
+
+ ACPI_FUNCTION_ENTRY();
+
+ digits_needed = acpi_ex_digits_needed(value, 10);
+ out_string[digits_needed] = 0;
+
+ for (count = digits_needed; count > 0; count--) {
+ (void)acpi_ut_short_divide(value, 10, &value, &remainder);
+ out_string[count - 1] = (char)('0' + remainder);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_pci_cls_to_string
+ *
+ * PARAMETERS: out_string - Where to put the converted string (7 bytes)
+ * class_code - PCI class code to be converted (3 bytes)
+ *
+ * RETURN: Converted string in out_string
+ *
+ * DESCRIPTION: Convert 3-bytes PCI class code to string representation.
+ * Return buffer must be large enough to hold the string. The
+ * string returned is always exactly of length
+ * ACPI_PCICLS_STRING_SIZE (includes null terminator).
+ *
+ ******************************************************************************/
+
+void acpi_ex_pci_cls_to_string(char *out_string, u8 class_code[3])
+{
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* All 3 bytes are hexadecimal */
+
+ out_string[0] = acpi_ut_hex_to_ascii_char((u64)class_code[0], 4);
+ out_string[1] = acpi_ut_hex_to_ascii_char((u64)class_code[0], 0);
+ out_string[2] = acpi_ut_hex_to_ascii_char((u64)class_code[1], 4);
+ out_string[3] = acpi_ut_hex_to_ascii_char((u64)class_code[1], 0);
+ out_string[4] = acpi_ut_hex_to_ascii_char((u64)class_code[2], 4);
+ out_string[5] = acpi_ut_hex_to_ascii_char((u64)class_code[2], 0);
+ out_string[6] = 0;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_is_valid_space_id
+ *
+ * PARAMETERS: space_id - ID to be validated
+ *
+ * RETURN: TRUE if space_id is a valid/supported ID.
+ *
+ * DESCRIPTION: Validate an operation region space_ID.
+ *
+ ******************************************************************************/
+
+u8 acpi_is_valid_space_id(u8 space_id)
+{
+
+ if ((space_id >= ACPI_NUM_PREDEFINED_REGIONS) &&
+ (space_id < ACPI_USER_REGION_BEGIN) &&
+ (space_id != ACPI_ADR_SPACE_DATA_TABLE) &&
+ (space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
diff --git a/drivers/acpi/acpica/hwacpi.c b/drivers/acpi/acpica/hwacpi.c
new file mode 100644
index 000000000..2f1c2fc8b
--- /dev/null
+++ b/drivers/acpi/acpica/hwacpi.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: hwacpi - ACPI Hardware Initialization/Mode Interface
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ACPI_MODULE_NAME("hwacpi")
+
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_set_mode
+ *
+ * PARAMETERS: mode - SYS_MODE_ACPI or SYS_MODE_LEGACY
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Transitions the system into the requested mode.
+ *
+ ******************************************************************************/
+acpi_status acpi_hw_set_mode(u32 mode)
+{
+
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(hw_set_mode);
+
+ /* If the Hardware Reduced flag is set, machine is always in acpi mode */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * ACPI 2.0 clarified that if SMI_CMD in FADT is zero,
+ * system does not support mode transition.
+ */
+ if (!acpi_gbl_FADT.smi_command) {
+ ACPI_ERROR((AE_INFO,
+ "No SMI_CMD in FADT, mode transition failed"));
+ return_ACPI_STATUS(AE_NO_HARDWARE_RESPONSE);
+ }
+
+ /*
+ * ACPI 2.0 clarified the meaning of ACPI_ENABLE and ACPI_DISABLE
+ * in FADT: If it is zero, enabling or disabling is not supported.
+ * As old systems may have used zero for mode transition,
+ * we make sure both the numbers are zero to determine these
+ * transitions are not supported.
+ */
+ if (!acpi_gbl_FADT.acpi_enable && !acpi_gbl_FADT.acpi_disable) {
+ ACPI_ERROR((AE_INFO,
+ "No ACPI mode transition supported in this system "
+ "(enable/disable both zero)"));
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ switch (mode) {
+ case ACPI_SYS_MODE_ACPI:
+
+ /* BIOS should have disabled ALL fixed and GP events */
+
+ status = acpi_hw_write_port(acpi_gbl_FADT.smi_command,
+ (u32) acpi_gbl_FADT.acpi_enable, 8);
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Attempting to enable ACPI mode\n"));
+ break;
+
+ case ACPI_SYS_MODE_LEGACY:
+ /*
+ * BIOS should clear all fixed status bits and restore fixed event
+ * enable bits to default
+ */
+ status = acpi_hw_write_port(acpi_gbl_FADT.smi_command,
+ (u32)acpi_gbl_FADT.acpi_disable, 8);
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Attempting to enable Legacy (non-ACPI) mode\n"));
+ break;
+
+ default:
+
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not write ACPI mode change"));
+ return_ACPI_STATUS(status);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_get_mode
+ *
+ * PARAMETERS: none
+ *
+ * RETURN: SYS_MODE_ACPI or SYS_MODE_LEGACY
+ *
+ * DESCRIPTION: Return current operating state of system. Determined by
+ * querying the SCI_EN bit.
+ *
+ ******************************************************************************/
+
+u32 acpi_hw_get_mode(void)
+{
+ acpi_status status;
+ u32 value;
+
+ ACPI_FUNCTION_TRACE(hw_get_mode);
+
+ /* If the Hardware Reduced flag is set, machine is always in acpi mode */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_UINT32(ACPI_SYS_MODE_ACPI);
+ }
+
+ /*
+ * ACPI 2.0 clarified that if SMI_CMD in FADT is zero,
+ * system does not support mode transition.
+ */
+ if (!acpi_gbl_FADT.smi_command) {
+ return_UINT32(ACPI_SYS_MODE_ACPI);
+ }
+
+ status = acpi_read_bit_register(ACPI_BITREG_SCI_ENABLE, &value);
+ if (ACPI_FAILURE(status)) {
+ return_UINT32(ACPI_SYS_MODE_LEGACY);
+ }
+
+ if (value) {
+ return_UINT32(ACPI_SYS_MODE_ACPI);
+ } else {
+ return_UINT32(ACPI_SYS_MODE_LEGACY);
+ }
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c
new file mode 100644
index 000000000..d8597e052
--- /dev/null
+++ b/drivers/acpi/acpica/hwesleep.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Name: hwesleep.c - ACPI Hardware Sleep/Wake Support functions for the
+ * extended FADT-V5 sleep registers.
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ACPI_MODULE_NAME("hwesleep")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_execute_sleep_method
+ *
+ * PARAMETERS: method_pathname - Pathname of method to execute
+ * integer_argument - Argument to pass to the method
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Execute a sleep/wake related method with one integer argument
+ * and no return value.
+ *
+ ******************************************************************************/
+void acpi_hw_execute_sleep_method(char *method_pathname, u32 integer_argument)
+{
+ struct acpi_object_list arg_list;
+ union acpi_object arg;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(hw_execute_sleep_method);
+
+ /* One argument, integer_argument; No return value expected */
+
+ arg_list.count = 1;
+ arg_list.pointer = &arg;
+ arg.type = ACPI_TYPE_INTEGER;
+ arg.integer.value = (u64)integer_argument;
+
+ status = acpi_evaluate_object(NULL, method_pathname, &arg_list, NULL);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ ACPI_EXCEPTION((AE_INFO, status, "While executing method %s",
+ method_pathname));
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_extended_sleep
+ *
+ * PARAMETERS: sleep_state - Which sleep state to enter
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enter a system sleep state via the extended FADT sleep
+ * registers (V5 FADT).
+ * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_extended_sleep(u8 sleep_state)
+{
+ acpi_status status;
+ u8 sleep_control;
+ u64 sleep_status;
+
+ ACPI_FUNCTION_TRACE(hw_extended_sleep);
+
+ /* Extended sleep registers must be valid */
+
+ if (!acpi_gbl_FADT.sleep_control.address ||
+ !acpi_gbl_FADT.sleep_status.address) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /* Clear wake status (WAK_STS) */
+
+ status = acpi_write((u64)ACPI_X_WAKE_STATUS,
+ &acpi_gbl_FADT.sleep_status);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_gbl_system_awake_and_running = FALSE;
+
+ /*
+ * Set the SLP_TYP and SLP_EN bits.
+ *
+ * Note: We only use the first value returned by the \_Sx method
+ * (acpi_gbl_sleep_type_a) - As per ACPI specification.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_INIT,
+ "Entering sleep state [S%u]\n", sleep_state));
+
+ sleep_control = ((acpi_gbl_sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) &
+ ACPI_X_SLEEP_TYPE_MASK) | ACPI_X_SLEEP_ENABLE;
+
+ /* Flush caches, as per ACPI specification */
+
+ if (sleep_state < ACPI_STATE_S4) {
+ ACPI_FLUSH_CPU_CACHE();
+ }
+
+ status = acpi_os_enter_sleep(sleep_state, sleep_control, 0);
+ if (status == AE_CTRL_TERMINATE) {
+ return_ACPI_STATUS(AE_OK);
+ }
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_write((u64)sleep_control, &acpi_gbl_FADT.sleep_control);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Wait for transition back to Working State */
+
+ do {
+ status = acpi_read(&sleep_status, &acpi_gbl_FADT.sleep_status);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ } while (!(((u8)sleep_status) & ACPI_X_WAKE_STATUS));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_extended_wake_prep
+ *
+ * PARAMETERS: sleep_state - Which sleep state we just exited
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform first part of OS-independent ACPI cleanup after
+ * a sleep. Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_extended_wake_prep(u8 sleep_state)
+{
+ u8 sleep_type_value;
+
+ ACPI_FUNCTION_TRACE(hw_extended_wake_prep);
+
+ if (acpi_gbl_sleep_type_a_s0 != ACPI_SLEEP_TYPE_INVALID) {
+ sleep_type_value =
+ ((acpi_gbl_sleep_type_a_s0 << ACPI_X_SLEEP_TYPE_POSITION) &
+ ACPI_X_SLEEP_TYPE_MASK);
+
+ (void)acpi_write((u64)(sleep_type_value | ACPI_X_SLEEP_ENABLE),
+ &acpi_gbl_FADT.sleep_control);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_extended_wake
+ *
+ * PARAMETERS: sleep_state - Which sleep state we just exited
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ * Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_extended_wake(u8 sleep_state)
+{
+ ACPI_FUNCTION_TRACE(hw_extended_wake);
+
+ /* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */
+
+ acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID;
+
+ /* Execute the wake methods */
+
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WAKING);
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__WAK, sleep_state);
+
+ /*
+ * Some BIOS code assumes that WAK_STS will be cleared on resume
+ * and use it to determine whether the system is rebooting or
+ * resuming. Clear WAK_STS for compatibility.
+ */
+ (void)acpi_write((u64)ACPI_X_WAKE_STATUS, &acpi_gbl_FADT.sleep_status);
+ acpi_gbl_system_awake_and_running = TRUE;
+
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING);
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c
new file mode 100644
index 000000000..13d54a48e
--- /dev/null
+++ b/drivers/acpi/acpica/hwgpe.c
@@ -0,0 +1,683 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: hwgpe - Low level GPE enable/disable/clear functions
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ACPI_MODULE_NAME("hwgpe")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/* Local prototypes */
+static acpi_status
+acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *context);
+
+static acpi_status
+acpi_hw_gpe_enable_write(u8 enable_mask,
+ struct acpi_gpe_register_info *gpe_register_info);
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_gpe_read
+ *
+ * PARAMETERS: value - Where the value is returned
+ * reg - GPE register structure
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read from a GPE register in either memory or IO space.
+ *
+ * LIMITATIONS: <These limitations also apply to acpi_hw_gpe_write>
+ * space_ID must be system_memory or system_IO.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_gpe_read(u64 *value, struct acpi_gpe_address *reg)
+{
+ acpi_status status;
+ u32 value32;
+
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+#ifdef ACPI_GPE_USE_LOGICAL_ADDRESSES
+ *value = (u64)ACPI_GET8((unsigned long)reg->address);
+ return_ACPI_STATUS(AE_OK);
+#else
+ return acpi_os_read_memory((acpi_physical_address)reg->address,
+ value, ACPI_GPE_REGISTER_WIDTH);
+#endif
+ }
+
+ status = acpi_os_read_port((acpi_io_address)reg->address,
+ &value32, ACPI_GPE_REGISTER_WIDTH);
+ if (ACPI_FAILURE(status))
+ return_ACPI_STATUS(status);
+
+ *value = (u64)value32;
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_gpe_write
+ *
+ * PARAMETERS: value - Value to be written
+ * reg - GPE register structure
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write to a GPE register in either memory or IO space.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_gpe_write(u64 value, struct acpi_gpe_address *reg)
+{
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+#ifdef ACPI_GPE_USE_LOGICAL_ADDRESSES
+ ACPI_SET8((unsigned long)reg->address, value);
+ return_ACPI_STATUS(AE_OK);
+#else
+ return acpi_os_write_memory((acpi_physical_address)reg->address,
+ value, ACPI_GPE_REGISTER_WIDTH);
+#endif
+ }
+
+ return acpi_os_write_port((acpi_io_address)reg->address, (u32)value,
+ ACPI_GPE_REGISTER_WIDTH);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_get_gpe_register_bit
+ *
+ * PARAMETERS: gpe_event_info - Info block for the GPE
+ *
+ * RETURN: Register mask with a one in the GPE bit position
+ *
+ * DESCRIPTION: Compute the register mask for this GPE. One bit is set in the
+ * correct position for the input GPE.
+ *
+ ******************************************************************************/
+
+u32 acpi_hw_get_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info)
+{
+
+ return ((u32)1 <<
+ (gpe_event_info->gpe_number -
+ gpe_event_info->register_info->base_gpe_number));
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_low_set_gpe
+ *
+ * PARAMETERS: gpe_event_info - Info block for the GPE to be disabled
+ * action - Enable or disable
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable or disable a single GPE in the parent enable register.
+ * The enable_mask field of the involved GPE register must be
+ * updated by the caller if necessary.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action)
+{
+ struct acpi_gpe_register_info *gpe_register_info;
+ acpi_status status = AE_OK;
+ u64 enable_mask;
+ u32 register_bit;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Get the info block for the entire GPE register */
+
+ gpe_register_info = gpe_event_info->register_info;
+ if (!gpe_register_info) {
+ return (AE_NOT_EXIST);
+ }
+
+ /* Get current value of the enable register that contains this GPE */
+
+ status = acpi_hw_gpe_read(&enable_mask,
+ &gpe_register_info->enable_address);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Set or clear just the bit that corresponds to this GPE */
+
+ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
+ switch (action) {
+ case ACPI_GPE_CONDITIONAL_ENABLE:
+
+ /* Only enable if the corresponding enable_mask bit is set */
+
+ if (!(register_bit & gpe_register_info->enable_mask)) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ ACPI_FALLTHROUGH;
+
+ case ACPI_GPE_ENABLE:
+
+ ACPI_SET_BIT(enable_mask, register_bit);
+ break;
+
+ case ACPI_GPE_DISABLE:
+
+ ACPI_CLEAR_BIT(enable_mask, register_bit);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Invalid GPE Action, %u", action));
+ return (AE_BAD_PARAMETER);
+ }
+
+ if (!(register_bit & gpe_register_info->mask_for_run)) {
+
+ /* Write the updated enable mask */
+
+ status = acpi_hw_gpe_write(enable_mask,
+ &gpe_register_info->enable_address);
+ }
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_clear_gpe
+ *
+ * PARAMETERS: gpe_event_info - Info block for the GPE to be cleared
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Clear the status bit for a single GPE.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info *gpe_event_info)
+{
+ struct acpi_gpe_register_info *gpe_register_info;
+ acpi_status status;
+ u32 register_bit;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Get the info block for the entire GPE register */
+
+ gpe_register_info = gpe_event_info->register_info;
+ if (!gpe_register_info) {
+ return (AE_NOT_EXIST);
+ }
+
+ /*
+ * Write a one to the appropriate bit in the status register to
+ * clear this GPE.
+ */
+ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
+
+ status = acpi_hw_gpe_write(register_bit,
+ &gpe_register_info->status_address);
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_get_gpe_status
+ *
+ * PARAMETERS: gpe_event_info - Info block for the GPE to queried
+ * event_status - Where the GPE status is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Return the status of a single GPE.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info,
+ acpi_event_status *event_status)
+{
+ u64 in_byte;
+ u32 register_bit;
+ struct acpi_gpe_register_info *gpe_register_info;
+ acpi_event_status local_event_status = 0;
+ acpi_status status;
+
+ ACPI_FUNCTION_ENTRY();
+
+ if (!event_status) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* GPE currently handled? */
+
+ if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) !=
+ ACPI_GPE_DISPATCH_NONE) {
+ local_event_status |= ACPI_EVENT_FLAG_HAS_HANDLER;
+ }
+
+ /* Get the info block for the entire GPE register */
+
+ gpe_register_info = gpe_event_info->register_info;
+
+ /* Get the register bitmask for this GPE */
+
+ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
+
+ /* GPE currently enabled? (enabled for runtime?) */
+
+ if (register_bit & gpe_register_info->enable_for_run) {
+ local_event_status |= ACPI_EVENT_FLAG_ENABLED;
+ }
+
+ /* GPE currently masked? (masked for runtime?) */
+
+ if (register_bit & gpe_register_info->mask_for_run) {
+ local_event_status |= ACPI_EVENT_FLAG_MASKED;
+ }
+
+ /* GPE enabled for wake? */
+
+ if (register_bit & gpe_register_info->enable_for_wake) {
+ local_event_status |= ACPI_EVENT_FLAG_WAKE_ENABLED;
+ }
+
+ /* GPE currently enabled (enable bit == 1)? */
+
+ status = acpi_hw_gpe_read(&in_byte, &gpe_register_info->enable_address);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if (register_bit & in_byte) {
+ local_event_status |= ACPI_EVENT_FLAG_ENABLE_SET;
+ }
+
+ /* GPE currently active (status bit == 1)? */
+
+ status = acpi_hw_gpe_read(&in_byte, &gpe_register_info->status_address);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if (register_bit & in_byte) {
+ local_event_status |= ACPI_EVENT_FLAG_STATUS_SET;
+ }
+
+ /* Set return value */
+
+ (*event_status) = local_event_status;
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_gpe_enable_write
+ *
+ * PARAMETERS: enable_mask - Bit mask to write to the GPE register
+ * gpe_register_info - Gpe Register info
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write the enable mask byte to the given GPE register.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_gpe_enable_write(u8 enable_mask,
+ struct acpi_gpe_register_info *gpe_register_info)
+{
+ acpi_status status;
+
+ gpe_register_info->enable_mask = enable_mask;
+
+ status = acpi_hw_gpe_write(enable_mask,
+ &gpe_register_info->enable_address);
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_disable_gpe_block
+ *
+ * PARAMETERS: gpe_xrupt_info - GPE Interrupt info
+ * gpe_block - Gpe Block info
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Disable all GPEs within a single GPE block
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block, void *context)
+{
+ u32 i;
+ acpi_status status;
+
+ /* Examine each GPE Register within the block */
+
+ for (i = 0; i < gpe_block->register_count; i++) {
+
+ /* Disable all GPEs in this register */
+
+ status =
+ acpi_hw_gpe_enable_write(0x00,
+ &gpe_block->register_info[i]);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_clear_gpe_block
+ *
+ * PARAMETERS: gpe_xrupt_info - GPE Interrupt info
+ * gpe_block - Gpe Block info
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Clear status bits for all GPEs within a single GPE block
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block, void *context)
+{
+ u32 i;
+ acpi_status status;
+
+ /* Examine each GPE Register within the block */
+
+ for (i = 0; i < gpe_block->register_count; i++) {
+
+ /* Clear status on all GPEs in this register */
+
+ status = acpi_hw_gpe_write(0xFF,
+ &gpe_block->register_info[i].status_address);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_enable_runtime_gpe_block
+ *
+ * PARAMETERS: gpe_xrupt_info - GPE Interrupt info
+ * gpe_block - Gpe Block info
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable all "runtime" GPEs within a single GPE block. Includes
+ * combination wake/run GPEs.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *context)
+{
+ u32 i;
+ acpi_status status;
+ struct acpi_gpe_register_info *gpe_register_info;
+ u8 enable_mask;
+
+ /* NOTE: assumes that all GPEs are currently disabled */
+
+ /* Examine each GPE Register within the block */
+
+ for (i = 0; i < gpe_block->register_count; i++) {
+ gpe_register_info = &gpe_block->register_info[i];
+ if (!gpe_register_info->enable_for_run) {
+ continue;
+ }
+
+ /* Enable all "runtime" GPEs in this register */
+
+ enable_mask = gpe_register_info->enable_for_run &
+ ~gpe_register_info->mask_for_run;
+ status =
+ acpi_hw_gpe_enable_write(enable_mask, gpe_register_info);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_enable_wakeup_gpe_block
+ *
+ * PARAMETERS: gpe_xrupt_info - GPE Interrupt info
+ * gpe_block - Gpe Block info
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable all "wake" GPEs within a single GPE block. Includes
+ * combination wake/run GPEs.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *context)
+{
+ u32 i;
+ acpi_status status;
+ struct acpi_gpe_register_info *gpe_register_info;
+
+ /* Examine each GPE Register within the block */
+
+ for (i = 0; i < gpe_block->register_count; i++) {
+ gpe_register_info = &gpe_block->register_info[i];
+
+ /*
+ * Enable all "wake" GPEs in this register and disable the
+ * remaining ones.
+ */
+
+ status =
+ acpi_hw_gpe_enable_write(gpe_register_info->enable_for_wake,
+ gpe_register_info);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ return (AE_OK);
+}
+
+struct acpi_gpe_block_status_context {
+ struct acpi_gpe_register_info *gpe_skip_register_info;
+ u8 gpe_skip_mask;
+ u8 retval;
+};
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_get_gpe_block_status
+ *
+ * PARAMETERS: gpe_xrupt_info - GPE Interrupt info
+ * gpe_block - Gpe Block info
+ * context - GPE list walk context data
+ *
+ * RETURN: Success
+ *
+ * DESCRIPTION: Produce a combined GPE status bits mask for the given block.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_get_gpe_block_status(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *context)
+{
+ struct acpi_gpe_block_status_context *c = context;
+ struct acpi_gpe_register_info *gpe_register_info;
+ u64 in_enable, in_status;
+ acpi_status status;
+ u8 ret_mask;
+ u32 i;
+
+ /* Examine each GPE Register within the block */
+
+ for (i = 0; i < gpe_block->register_count; i++) {
+ gpe_register_info = &gpe_block->register_info[i];
+
+ status = acpi_hw_gpe_read(&in_enable,
+ &gpe_register_info->enable_address);
+ if (ACPI_FAILURE(status)) {
+ continue;
+ }
+
+ status = acpi_hw_gpe_read(&in_status,
+ &gpe_register_info->status_address);
+ if (ACPI_FAILURE(status)) {
+ continue;
+ }
+
+ ret_mask = in_enable & in_status;
+ if (ret_mask && c->gpe_skip_register_info == gpe_register_info) {
+ ret_mask &= ~c->gpe_skip_mask;
+ }
+ c->retval |= ret_mask;
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_disable_all_gpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Disable and clear all GPEs in all GPE blocks
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_disable_all_gpes(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(hw_disable_all_gpes);
+
+ status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL);
+ return_ACPI_STATUS(status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_enable_all_runtime_gpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable all "runtime" GPEs, in all GPE blocks
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_enable_all_runtime_gpes(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(hw_enable_all_runtime_gpes);
+
+ status = acpi_ev_walk_gpe_list(acpi_hw_enable_runtime_gpe_block, NULL);
+ return_ACPI_STATUS(status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_enable_all_wakeup_gpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable all "wakeup" GPEs, in all GPE blocks
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_enable_all_wakeup_gpes(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(hw_enable_all_wakeup_gpes);
+
+ status = acpi_ev_walk_gpe_list(acpi_hw_enable_wakeup_gpe_block, NULL);
+ return_ACPI_STATUS(status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_check_all_gpes
+ *
+ * PARAMETERS: gpe_skip_device - GPE devoce of the GPE to skip
+ * gpe_skip_number - Number of the GPE to skip
+ *
+ * RETURN: Combined status of all GPEs
+ *
+ * DESCRIPTION: Check all enabled GPEs in all GPE blocks, except for the one
+ * represented by the "skip" arguments, and return TRUE if the
+ * status bit is set for at least one of them of FALSE otherwise.
+ *
+ ******************************************************************************/
+
+u8 acpi_hw_check_all_gpes(acpi_handle gpe_skip_device, u32 gpe_skip_number)
+{
+ struct acpi_gpe_block_status_context context = {
+ .gpe_skip_register_info = NULL,
+ .retval = 0,
+ };
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_hw_check_all_gpes);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_skip_device,
+ gpe_skip_number);
+ if (gpe_event_info) {
+ context.gpe_skip_register_info = gpe_event_info->register_info;
+ context.gpe_skip_mask = acpi_hw_get_gpe_register_bit(gpe_event_info);
+ }
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+
+ (void)acpi_ev_walk_gpe_list(acpi_hw_get_gpe_block_status, &context);
+ return (context.retval != 0);
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwpci.c b/drivers/acpi/acpica/hwpci.c
new file mode 100644
index 000000000..3bd5ebc9c
--- /dev/null
+++ b/drivers/acpi/acpica/hwpci.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: hwpci - Obtain PCI bus, device, and function numbers
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("hwpci")
+
+/* PCI configuration space values */
+#define PCI_CFG_HEADER_TYPE_REG 0x0E
+#define PCI_CFG_PRIMARY_BUS_NUMBER_REG 0x18
+#define PCI_CFG_SECONDARY_BUS_NUMBER_REG 0x19
+/* PCI header values */
+#define PCI_HEADER_TYPE_MASK 0x7F
+#define PCI_TYPE_BRIDGE 0x01
+#define PCI_TYPE_CARDBUS_BRIDGE 0x02
+typedef struct acpi_pci_device {
+ acpi_handle device;
+ struct acpi_pci_device *next;
+
+} acpi_pci_device;
+
+/* Local prototypes */
+
+static acpi_status
+acpi_hw_build_pci_list(acpi_handle root_pci_device,
+ acpi_handle pci_region,
+ struct acpi_pci_device **return_list_head);
+
+static acpi_status
+acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
+ struct acpi_pci_device *list_head);
+
+static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head);
+
+static acpi_status
+acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,
+ acpi_handle pci_device,
+ u16 *bus_number, u8 *is_bridge);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_derive_pci_id
+ *
+ * PARAMETERS: pci_id - Initial values for the PCI ID. May be
+ * modified by this function.
+ * root_pci_device - A handle to a PCI device object. This
+ * object must be a PCI Root Bridge having a
+ * _HID value of either PNP0A03 or PNP0A08
+ * pci_region - A handle to a PCI configuration space
+ * Operation Region being initialized
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function derives a full PCI ID for a PCI device,
+ * consisting of a Segment number, Bus number, Device number,
+ * and function code.
+ *
+ * The PCI hardware dynamically configures PCI bus numbers
+ * depending on the bus topology discovered during system
+ * initialization. This function is invoked during configuration
+ * of a PCI_Config Operation Region in order to (possibly) update
+ * the Bus/Device/Function numbers in the pci_id with the actual
+ * values as determined by the hardware and operating system
+ * configuration.
+ *
+ * The pci_id parameter is initially populated during the Operation
+ * Region initialization. This function is then called, and is
+ * will make any necessary modifications to the Bus, Device, or
+ * Function number PCI ID subfields as appropriate for the
+ * current hardware and OS configuration.
+ *
+ * NOTE: Created 08/2010. Replaces the previous OSL acpi_os_derive_pci_id
+ * interface since this feature is OS-independent. This module
+ * specifically avoids any use of recursion by building a local
+ * temporary device list.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
+ acpi_handle root_pci_device, acpi_handle pci_region)
+{
+ acpi_status status;
+ struct acpi_pci_device *list_head;
+
+ ACPI_FUNCTION_TRACE(hw_derive_pci_id);
+
+ if (!pci_id) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Build a list of PCI devices, from pci_region up to root_pci_device */
+
+ status =
+ acpi_hw_build_pci_list(root_pci_device, pci_region, &list_head);
+ if (ACPI_SUCCESS(status)) {
+
+ /* Walk the list, updating the PCI device/function/bus numbers */
+
+ status = acpi_hw_process_pci_list(pci_id, list_head);
+
+ /* Delete the list */
+
+ acpi_hw_delete_pci_list(list_head);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_build_pci_list
+ *
+ * PARAMETERS: root_pci_device - A handle to a PCI device object. This
+ * object is guaranteed to be a PCI Root
+ * Bridge having a _HID value of either
+ * PNP0A03 or PNP0A08
+ * pci_region - A handle to the PCI configuration space
+ * Operation Region
+ * return_list_head - Where the PCI device list is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Builds a list of devices from the input PCI region up to the
+ * Root PCI device for this namespace subtree.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_build_pci_list(acpi_handle root_pci_device,
+ acpi_handle pci_region,
+ struct acpi_pci_device **return_list_head)
+{
+ acpi_handle current_device;
+ acpi_handle parent_device;
+ acpi_status status;
+ struct acpi_pci_device *list_element;
+
+ /*
+ * Ascend namespace branch until the root_pci_device is reached, building
+ * a list of device nodes. Loop will exit when either the PCI device is
+ * found, or the root of the namespace is reached.
+ */
+ *return_list_head = NULL;
+ current_device = pci_region;
+ while (1) {
+ status = acpi_get_parent(current_device, &parent_device);
+ if (ACPI_FAILURE(status)) {
+
+ /* Must delete the list before exit */
+
+ acpi_hw_delete_pci_list(*return_list_head);
+ return (status);
+ }
+
+ /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */
+
+ if (parent_device == root_pci_device) {
+ return (AE_OK);
+ }
+
+ list_element = ACPI_ALLOCATE(sizeof(struct acpi_pci_device));
+ if (!list_element) {
+
+ /* Must delete the list before exit */
+
+ acpi_hw_delete_pci_list(*return_list_head);
+ return (AE_NO_MEMORY);
+ }
+
+ /* Put new element at the head of the list */
+
+ list_element->next = *return_list_head;
+ list_element->device = parent_device;
+ *return_list_head = list_element;
+
+ current_device = parent_device;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_process_pci_list
+ *
+ * PARAMETERS: pci_id - Initial values for the PCI ID. May be
+ * modified by this function.
+ * list_head - Device list created by
+ * acpi_hw_build_pci_list
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Walk downward through the PCI device list, getting the device
+ * info for each, via the PCI configuration space and updating
+ * the PCI ID as necessary. Deletes the list during traversal.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
+ struct acpi_pci_device *list_head)
+{
+ acpi_status status = AE_OK;
+ struct acpi_pci_device *info;
+ u16 bus_number;
+ u8 is_bridge = TRUE;
+
+ ACPI_FUNCTION_NAME(hw_process_pci_list);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Input PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n",
+ pci_id->segment, pci_id->bus, pci_id->device,
+ pci_id->function));
+
+ bus_number = pci_id->bus;
+
+ /*
+ * Descend down the namespace tree, collecting PCI device, function,
+ * and bus numbers. bus_number is only important for PCI bridges.
+ * Algorithm: As we descend the tree, use the last valid PCI device,
+ * function, and bus numbers that are discovered, and assign them
+ * to the PCI ID for the target device.
+ */
+ info = list_head;
+ while (info) {
+ status = acpi_hw_get_pci_device_info(pci_id, info->device,
+ &bus_number, &is_bridge);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ info = info->next;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X "
+ "Status %X BusNumber %X IsBridge %X\n",
+ pci_id->segment, pci_id->bus, pci_id->device,
+ pci_id->function, status, bus_number, is_bridge));
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_delete_pci_list
+ *
+ * PARAMETERS: list_head - Device list created by
+ * acpi_hw_build_pci_list
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Free the entire PCI list.
+ *
+ ******************************************************************************/
+
+static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head)
+{
+ struct acpi_pci_device *next;
+ struct acpi_pci_device *previous;
+
+ next = list_head;
+ while (next) {
+ previous = next;
+ next = previous->next;
+ ACPI_FREE(previous);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_get_pci_device_info
+ *
+ * PARAMETERS: pci_id - Initial values for the PCI ID. May be
+ * modified by this function.
+ * pci_device - Handle for the PCI device object
+ * bus_number - Where a PCI bridge bus number is returned
+ * is_bridge - Return value, indicates if this PCI
+ * device is a PCI bridge
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get the device info for a single PCI device object. Get the
+ * _ADR (contains PCI device and function numbers), and for PCI
+ * bridge devices, get the bus number from PCI configuration
+ * space.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,
+ acpi_handle pci_device,
+ u16 *bus_number, u8 *is_bridge)
+{
+ acpi_status status;
+ acpi_object_type object_type;
+ u64 return_value;
+ u64 pci_value;
+
+ /* We only care about objects of type Device */
+
+ status = acpi_get_type(pci_device, &object_type);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if (object_type != ACPI_TYPE_DEVICE) {
+ return (AE_OK);
+ }
+
+ /* We need an _ADR. Ignore device if not present */
+
+ status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR,
+ pci_device, &return_value);
+ if (ACPI_FAILURE(status)) {
+ return (AE_OK);
+ }
+
+ /*
+ * From _ADR, get the PCI Device and Function and
+ * update the PCI ID.
+ */
+ pci_id->device = ACPI_HIWORD(ACPI_LODWORD(return_value));
+ pci_id->function = ACPI_LOWORD(ACPI_LODWORD(return_value));
+
+ /*
+ * If the previous device was a bridge, use the previous
+ * device bus number
+ */
+ if (*is_bridge) {
+ pci_id->bus = *bus_number;
+ }
+
+ /*
+ * Get the bus numbers from PCI Config space:
+ *
+ * First, get the PCI header_type
+ */
+ *is_bridge = FALSE;
+ status = acpi_os_read_pci_configuration(pci_id,
+ PCI_CFG_HEADER_TYPE_REG,
+ &pci_value, 8);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* We only care about bridges (1=pci_bridge, 2=card_bus_bridge) */
+
+ pci_value &= PCI_HEADER_TYPE_MASK;
+
+ if ((pci_value != PCI_TYPE_BRIDGE) &&
+ (pci_value != PCI_TYPE_CARDBUS_BRIDGE)) {
+ return (AE_OK);
+ }
+
+ /* Bridge: Get the Primary bus_number */
+
+ status = acpi_os_read_pci_configuration(pci_id,
+ PCI_CFG_PRIMARY_BUS_NUMBER_REG,
+ &pci_value, 8);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ *is_bridge = TRUE;
+ pci_id->bus = (u16)pci_value;
+
+ /* Bridge: Get the Secondary bus_number */
+
+ status = acpi_os_read_pci_configuration(pci_id,
+ PCI_CFG_SECONDARY_BUS_NUMBER_REG,
+ &pci_value, 8);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ *bus_number = (u16)pci_value;
+ return (AE_OK);
+}
diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c
new file mode 100644
index 000000000..f62d5d024
--- /dev/null
+++ b/drivers/acpi/acpica/hwregs.c
@@ -0,0 +1,802 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: hwregs - Read/write access functions for the various ACPI
+ * control and status registers.
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ACPI_MODULE_NAME("hwregs")
+
+#if (!ACPI_REDUCED_HARDWARE)
+/* Local Prototypes */
+static u8
+acpi_hw_get_access_bit_width(u64 address,
+ struct acpi_generic_address *reg,
+ u8 max_bit_width);
+
+static acpi_status
+acpi_hw_read_multiple(u32 *value,
+ struct acpi_generic_address *register_a,
+ struct acpi_generic_address *register_b);
+
+static acpi_status
+acpi_hw_write_multiple(u32 value,
+ struct acpi_generic_address *register_a,
+ struct acpi_generic_address *register_b);
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_get_access_bit_width
+ *
+ * PARAMETERS: address - GAS register address
+ * reg - GAS register structure
+ * max_bit_width - Max bit_width supported (32 or 64)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Obtain optimal access bit width
+ *
+ ******************************************************************************/
+
+static u8
+acpi_hw_get_access_bit_width(u64 address,
+ struct acpi_generic_address *reg, u8 max_bit_width)
+{
+ u8 access_bit_width;
+
+ /*
+ * GAS format "register", used by FADT:
+ * 1. Detected if bit_offset is 0 and bit_width is 8/16/32/64;
+ * 2. access_size field is ignored and bit_width field is used for
+ * determining the boundary of the IO accesses.
+ * GAS format "region", used by APEI registers:
+ * 1. Detected if bit_offset is not 0 or bit_width is not 8/16/32/64;
+ * 2. access_size field is used for determining the boundary of the
+ * IO accesses;
+ * 3. bit_offset/bit_width fields are used to describe the "region".
+ *
+ * Note: This algorithm assumes that the "Address" fields should always
+ * contain aligned values.
+ */
+ if (!reg->bit_offset && reg->bit_width &&
+ ACPI_IS_POWER_OF_TWO(reg->bit_width) &&
+ ACPI_IS_ALIGNED(reg->bit_width, 8)) {
+ access_bit_width = reg->bit_width;
+ } else if (reg->access_width) {
+ access_bit_width = ACPI_ACCESS_BIT_WIDTH(reg->access_width);
+ } else {
+ access_bit_width =
+ ACPI_ROUND_UP_POWER_OF_TWO_8(reg->bit_offset +
+ reg->bit_width);
+ if (access_bit_width <= 8) {
+ access_bit_width = 8;
+ } else {
+ while (!ACPI_IS_ALIGNED(address, access_bit_width >> 3)) {
+ access_bit_width >>= 1;
+ }
+ }
+ }
+
+ /* Maximum IO port access bit width is 32 */
+
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ max_bit_width = 32;
+ }
+
+ /*
+ * Return access width according to the requested maximum access bit width,
+ * as the caller should know the format of the register and may enforce
+ * a 32-bit accesses.
+ */
+ if (access_bit_width < max_bit_width) {
+ return (access_bit_width);
+ }
+ return (max_bit_width);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_validate_register
+ *
+ * PARAMETERS: reg - GAS register structure
+ * max_bit_width - Max bit_width supported (32 or 64)
+ * address - Pointer to where the gas->address
+ * is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Validate the contents of a GAS register. Checks the GAS
+ * pointer, Address, space_id, bit_width, and bit_offset.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_hw_validate_register(struct acpi_generic_address *reg,
+ u8 max_bit_width, u64 *address)
+{
+ u8 bit_width;
+ u8 access_width;
+
+ /* Must have a valid pointer to a GAS structure */
+
+ if (!reg) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Copy the target address. This handles possible alignment issues.
+ * Address must not be null. A null address also indicates an optional
+ * ACPI register that is not supported, so no error message.
+ */
+ ACPI_MOVE_64_TO_64(address, &reg->address);
+ if (!(*address)) {
+ return (AE_BAD_ADDRESS);
+ }
+
+ /* Validate the space_ID */
+
+ if ((reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
+ (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
+ ACPI_ERROR((AE_INFO,
+ "Unsupported address space: 0x%X", reg->space_id));
+ return (AE_SUPPORT);
+ }
+
+ /* Validate the access_width */
+
+ if (reg->access_width > 4) {
+ ACPI_ERROR((AE_INFO,
+ "Unsupported register access width: 0x%X",
+ reg->access_width));
+ return (AE_SUPPORT);
+ }
+
+ /* Validate the bit_width, convert access_width into number of bits */
+
+ access_width =
+ acpi_hw_get_access_bit_width(*address, reg, max_bit_width);
+ bit_width =
+ ACPI_ROUND_UP(reg->bit_offset + reg->bit_width, access_width);
+ if (max_bit_width < bit_width) {
+ ACPI_WARNING((AE_INFO,
+ "Requested bit width 0x%X is smaller than register bit width 0x%X",
+ max_bit_width, bit_width));
+ return (AE_SUPPORT);
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_read
+ *
+ * PARAMETERS: value - Where the value is returned
+ * reg - GAS register structure
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read from either memory or IO space. This is a 64-bit max
+ * version of acpi_read.
+ *
+ * LIMITATIONS: <These limitations also apply to acpi_hw_write>
+ * space_ID must be system_memory or system_IO.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_read(u64 *value, struct acpi_generic_address *reg)
+{
+ u64 address;
+ u8 access_width;
+ u32 bit_width;
+ u8 bit_offset;
+ u64 value64;
+ u32 value32;
+ u8 index;
+ acpi_status status;
+
+ ACPI_FUNCTION_NAME(hw_read);
+
+ /* Validate contents of the GAS register */
+
+ status = acpi_hw_validate_register(reg, 64, &address);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /*
+ * Initialize entire 64-bit return value to zero, convert access_width
+ * into number of bits based
+ */
+ *value = 0;
+ access_width = acpi_hw_get_access_bit_width(address, reg, 64);
+ bit_width = reg->bit_offset + reg->bit_width;
+ bit_offset = reg->bit_offset;
+
+ /*
+ * Two address spaces supported: Memory or IO. PCI_Config is
+ * not supported here because the GAS structure is insufficient
+ */
+ index = 0;
+ while (bit_width) {
+ if (bit_offset >= access_width) {
+ value64 = 0;
+ bit_offset -= access_width;
+ } else {
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ status =
+ acpi_os_read_memory((acpi_physical_address)
+ address +
+ index *
+ ACPI_DIV_8
+ (access_width),
+ &value64, access_width);
+ } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
+
+ status = acpi_hw_read_port((acpi_io_address)
+ address +
+ index *
+ ACPI_DIV_8
+ (access_width),
+ &value32,
+ access_width);
+ value64 = (u64)value32;
+ }
+ }
+
+ /*
+ * Use offset style bit writes because "Index * AccessWidth" is
+ * ensured to be less than 64-bits by acpi_hw_validate_register().
+ */
+ ACPI_SET_BITS(value, index * access_width,
+ ACPI_MASK_BITS_ABOVE_64(access_width), value64);
+
+ bit_width -=
+ bit_width > access_width ? access_width : bit_width;
+ index++;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_IO,
+ "Read: %8.8X%8.8X width %2d from %8.8X%8.8X (%s)\n",
+ ACPI_FORMAT_UINT64(*value), access_width,
+ ACPI_FORMAT_UINT64(address),
+ acpi_ut_get_region_name(reg->space_id)));
+
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_write
+ *
+ * PARAMETERS: value - Value to be written
+ * reg - GAS register structure
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write to either memory or IO space. This is a 64-bit max
+ * version of acpi_write.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_write(u64 value, struct acpi_generic_address *reg)
+{
+ u64 address;
+ u8 access_width;
+ u32 bit_width;
+ u8 bit_offset;
+ u64 value64;
+ u8 index;
+ acpi_status status;
+
+ ACPI_FUNCTION_NAME(hw_write);
+
+ /* Validate contents of the GAS register */
+
+ status = acpi_hw_validate_register(reg, 64, &address);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Convert access_width into number of bits based */
+
+ access_width = acpi_hw_get_access_bit_width(address, reg, 64);
+ bit_width = reg->bit_offset + reg->bit_width;
+ bit_offset = reg->bit_offset;
+
+ /*
+ * Two address spaces supported: Memory or IO. PCI_Config is
+ * not supported here because the GAS structure is insufficient
+ */
+ index = 0;
+ while (bit_width) {
+ /*
+ * Use offset style bit reads because "Index * AccessWidth" is
+ * ensured to be less than 64-bits by acpi_hw_validate_register().
+ */
+ value64 = ACPI_GET_BITS(&value, index * access_width,
+ ACPI_MASK_BITS_ABOVE_64(access_width));
+
+ if (bit_offset >= access_width) {
+ bit_offset -= access_width;
+ } else {
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ status =
+ acpi_os_write_memory((acpi_physical_address)
+ address +
+ index *
+ ACPI_DIV_8
+ (access_width),
+ value64, access_width);
+ } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
+
+ status = acpi_hw_write_port((acpi_io_address)
+ address +
+ index *
+ ACPI_DIV_8
+ (access_width),
+ (u32)value64,
+ access_width);
+ }
+ }
+
+ /*
+ * Index * access_width is ensured to be less than 32-bits by
+ * acpi_hw_validate_register().
+ */
+ bit_width -=
+ bit_width > access_width ? access_width : bit_width;
+ index++;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_IO,
+ "Wrote: %8.8X%8.8X width %2d to %8.8X%8.8X (%s)\n",
+ ACPI_FORMAT_UINT64(value), access_width,
+ ACPI_FORMAT_UINT64(address),
+ acpi_ut_get_region_name(reg->space_id)));
+
+ return (status);
+}
+
+#if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_clear_acpi_status
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Clears all fixed and general purpose status bits
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_clear_acpi_status(void)
+{
+ acpi_status status;
+ acpi_cpu_flags lock_flags = 0;
+
+ ACPI_FUNCTION_TRACE(hw_clear_acpi_status);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %8.8X%8.8X\n",
+ ACPI_BITMASK_ALL_FIXED_STATUS,
+ ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address)));
+
+ lock_flags = acpi_os_acquire_raw_lock(acpi_gbl_hardware_lock);
+
+ /* Clear the fixed events in PM1 A/B */
+
+ status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS,
+ ACPI_BITMASK_ALL_FIXED_STATUS);
+
+ acpi_os_release_raw_lock(acpi_gbl_hardware_lock, lock_flags);
+
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Clear the GPE Bits in all GPE registers in all GPE blocks */
+
+ status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL);
+
+exit:
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_get_bit_register_info
+ *
+ * PARAMETERS: register_id - Index of ACPI Register to access
+ *
+ * RETURN: The bitmask to be used when accessing the register
+ *
+ * DESCRIPTION: Map register_id into a register bitmask.
+ *
+ ******************************************************************************/
+
+struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ if (register_id > ACPI_BITREG_MAX) {
+ ACPI_ERROR((AE_INFO, "Invalid BitRegister ID: 0x%X",
+ register_id));
+ return (NULL);
+ }
+
+ return (&acpi_gbl_bit_register_info[register_id]);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_write_pm1_control
+ *
+ * PARAMETERS: pm1a_control - Value to be written to PM1A control
+ * pm1b_control - Value to be written to PM1B control
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write the PM1 A/B control registers. These registers are
+ * different than the PM1 A/B status and enable registers
+ * in that different values can be written to the A/B registers.
+ * Most notably, the SLP_TYP bits can be different, as per the
+ * values returned from the _Sx predefined methods.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_write_pm1_control(u32 pm1a_control, u32 pm1b_control)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(hw_write_pm1_control);
+
+ status =
+ acpi_hw_write(pm1a_control, &acpi_gbl_FADT.xpm1a_control_block);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (acpi_gbl_FADT.xpm1b_control_block.address) {
+ status =
+ acpi_hw_write(pm1b_control,
+ &acpi_gbl_FADT.xpm1b_control_block);
+ }
+ return_ACPI_STATUS(status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_register_read
+ *
+ * PARAMETERS: register_id - ACPI Register ID
+ * return_value - Where the register value is returned
+ *
+ * RETURN: Status and the value read.
+ *
+ * DESCRIPTION: Read from the specified ACPI register
+ *
+ ******************************************************************************/
+acpi_status acpi_hw_register_read(u32 register_id, u32 *return_value)
+{
+ u32 value = 0;
+ u64 value64;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(hw_register_read);
+
+ switch (register_id) {
+ case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */
+
+ status = acpi_hw_read_multiple(&value,
+ &acpi_gbl_xpm1a_status,
+ &acpi_gbl_xpm1b_status);
+ break;
+
+ case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access each */
+
+ status = acpi_hw_read_multiple(&value,
+ &acpi_gbl_xpm1a_enable,
+ &acpi_gbl_xpm1b_enable);
+ break;
+
+ case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */
+
+ status = acpi_hw_read_multiple(&value,
+ &acpi_gbl_FADT.
+ xpm1a_control_block,
+ &acpi_gbl_FADT.
+ xpm1b_control_block);
+
+ /*
+ * Zero the write-only bits. From the ACPI specification, "Hardware
+ * Write-Only Bits": "Upon reads to registers with write-only bits,
+ * software masks out all write-only bits."
+ */
+ value &= ~ACPI_PM1_CONTROL_WRITEONLY_BITS;
+ break;
+
+ case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */
+
+ status =
+ acpi_hw_read(&value64, &acpi_gbl_FADT.xpm2_control_block);
+ if (ACPI_SUCCESS(status)) {
+ value = (u32)value64;
+ }
+ break;
+
+ case ACPI_REGISTER_PM_TIMER: /* 32-bit access */
+
+ status = acpi_hw_read(&value64, &acpi_gbl_FADT.xpm_timer_block);
+ if (ACPI_SUCCESS(status)) {
+ value = (u32)value64;
+ }
+
+ break;
+
+ case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */
+
+ status =
+ acpi_hw_read_port(acpi_gbl_FADT.smi_command, &value, 8);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id));
+ status = AE_BAD_PARAMETER;
+ break;
+ }
+
+ if (ACPI_SUCCESS(status)) {
+ *return_value = (u32)value;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_register_write
+ *
+ * PARAMETERS: register_id - ACPI Register ID
+ * value - The value to write
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write to the specified ACPI register
+ *
+ * NOTE: In accordance with the ACPI specification, this function automatically
+ * preserves the value of the following bits, meaning that these bits cannot be
+ * changed via this interface:
+ *
+ * PM1_CONTROL[0] = SCI_EN
+ * PM1_CONTROL[9]
+ * PM1_STATUS[11]
+ *
+ * ACPI References:
+ * 1) Hardware Ignored Bits: When software writes to a register with ignored
+ * bit fields, it preserves the ignored bit fields
+ * 2) SCI_EN: OSPM always preserves this bit position
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_register_write(u32 register_id, u32 value)
+{
+ acpi_status status;
+ u32 read_value;
+ u64 read_value64;
+
+ ACPI_FUNCTION_TRACE(hw_register_write);
+
+ switch (register_id) {
+ case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */
+ /*
+ * Handle the "ignored" bit in PM1 Status. According to the ACPI
+ * specification, ignored bits are to be preserved when writing.
+ * Normally, this would mean a read/modify/write sequence. However,
+ * preserving a bit in the status register is different. Writing a
+ * one clears the status, and writing a zero preserves the status.
+ * Therefore, we must always write zero to the ignored bit.
+ *
+ * This behavior is clarified in the ACPI 4.0 specification.
+ */
+ value &= ~ACPI_PM1_STATUS_PRESERVED_BITS;
+
+ status = acpi_hw_write_multiple(value,
+ &acpi_gbl_xpm1a_status,
+ &acpi_gbl_xpm1b_status);
+ break;
+
+ case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access each */
+
+ status = acpi_hw_write_multiple(value,
+ &acpi_gbl_xpm1a_enable,
+ &acpi_gbl_xpm1b_enable);
+ break;
+
+ case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */
+ /*
+ * Perform a read first to preserve certain bits (per ACPI spec)
+ * Note: This includes SCI_EN, we never want to change this bit
+ */
+ status = acpi_hw_read_multiple(&read_value,
+ &acpi_gbl_FADT.
+ xpm1a_control_block,
+ &acpi_gbl_FADT.
+ xpm1b_control_block);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Insert the bits to be preserved */
+
+ ACPI_INSERT_BITS(value, ACPI_PM1_CONTROL_PRESERVED_BITS,
+ read_value);
+
+ /* Now we can write the data */
+
+ status = acpi_hw_write_multiple(value,
+ &acpi_gbl_FADT.
+ xpm1a_control_block,
+ &acpi_gbl_FADT.
+ xpm1b_control_block);
+ break;
+
+ case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */
+ /*
+ * For control registers, all reserved bits must be preserved,
+ * as per the ACPI spec.
+ */
+ status =
+ acpi_hw_read(&read_value64,
+ &acpi_gbl_FADT.xpm2_control_block);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+ read_value = (u32)read_value64;
+
+ /* Insert the bits to be preserved */
+
+ ACPI_INSERT_BITS(value, ACPI_PM2_CONTROL_PRESERVED_BITS,
+ read_value);
+
+ status =
+ acpi_hw_write(value, &acpi_gbl_FADT.xpm2_control_block);
+ break;
+
+ case ACPI_REGISTER_PM_TIMER: /* 32-bit access */
+
+ status = acpi_hw_write(value, &acpi_gbl_FADT.xpm_timer_block);
+ break;
+
+ case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */
+
+ /* SMI_CMD is currently always in IO space */
+
+ status =
+ acpi_hw_write_port(acpi_gbl_FADT.smi_command, value, 8);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id));
+ status = AE_BAD_PARAMETER;
+ break;
+ }
+
+exit:
+ return_ACPI_STATUS(status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_read_multiple
+ *
+ * PARAMETERS: value - Where the register value is returned
+ * register_a - First ACPI register (required)
+ * register_b - Second ACPI register (optional)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read from the specified two-part ACPI register (such as PM1 A/B)
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_read_multiple(u32 *value,
+ struct acpi_generic_address *register_a,
+ struct acpi_generic_address *register_b)
+{
+ u32 value_a = 0;
+ u32 value_b = 0;
+ u64 value64;
+ acpi_status status;
+
+ /* The first register is always required */
+
+ status = acpi_hw_read(&value64, register_a);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ value_a = (u32)value64;
+
+ /* Second register is optional */
+
+ if (register_b->address) {
+ status = acpi_hw_read(&value64, register_b);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ value_b = (u32)value64;
+ }
+
+ /*
+ * OR the two return values together. No shifting or masking is necessary,
+ * because of how the PM1 registers are defined in the ACPI specification:
+ *
+ * "Although the bits can be split between the two register blocks (each
+ * register block has a unique pointer within the FADT), the bit positions
+ * are maintained. The register block with unimplemented bits (that is,
+ * those implemented in the other register block) always returns zeros,
+ * and writes have no side effects"
+ */
+ *value = (value_a | value_b);
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_write_multiple
+ *
+ * PARAMETERS: value - The value to write
+ * register_a - First ACPI register (required)
+ * register_b - Second ACPI register (optional)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write to the specified two-part ACPI register (such as PM1 A/B)
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_write_multiple(u32 value,
+ struct acpi_generic_address *register_a,
+ struct acpi_generic_address *register_b)
+{
+ acpi_status status;
+
+ /* The first register is always required */
+
+ status = acpi_hw_write(value, register_a);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /*
+ * Second register is optional
+ *
+ * No bit shifting or clearing is necessary, because of how the PM1
+ * registers are defined in the ACPI specification:
+ *
+ * "Although the bits can be split between the two register blocks (each
+ * register block has a unique pointer within the FADT), the bit positions
+ * are maintained. The register block with unimplemented bits (that is,
+ * those implemented in the other register block) always returns zeros,
+ * and writes have no side effects"
+ */
+ if (register_b->address) {
+ status = acpi_hw_write(value, register_b);
+ }
+
+ return (status);
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
new file mode 100644
index 000000000..bd936476d
--- /dev/null
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the
+ * original/legacy sleep/PM registers.
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ACPI_MODULE_NAME("hwsleep")
+
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_legacy_sleep
+ *
+ * PARAMETERS: sleep_state - Which sleep state to enter
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enter a system sleep state via the legacy FADT PM registers
+ * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+acpi_status acpi_hw_legacy_sleep(u8 sleep_state)
+{
+ struct acpi_bit_register_info *sleep_type_reg_info;
+ struct acpi_bit_register_info *sleep_enable_reg_info;
+ u32 pm1a_control;
+ u32 pm1b_control;
+ u32 in_value;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(hw_legacy_sleep);
+
+ sleep_type_reg_info =
+ acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE);
+ sleep_enable_reg_info =
+ acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_ENABLE);
+
+ /* Clear wake status */
+
+ status = acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS,
+ ACPI_CLEAR_STATUS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Disable all GPEs */
+ status = acpi_hw_disable_all_gpes();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ status = acpi_hw_clear_acpi_status();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ acpi_gbl_system_awake_and_running = FALSE;
+
+ /* Enable all wakeup GPEs */
+ status = acpi_hw_enable_all_wakeup_gpes();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Get current value of PM1A control */
+
+ status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL,
+ &pm1a_control);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ ACPI_DEBUG_PRINT((ACPI_DB_INIT,
+ "Entering sleep state [S%u]\n", sleep_state));
+
+ /* Clear the SLP_EN and SLP_TYP fields */
+
+ pm1a_control &= ~(sleep_type_reg_info->access_bit_mask |
+ sleep_enable_reg_info->access_bit_mask);
+ pm1b_control = pm1a_control;
+
+ /* Insert the SLP_TYP bits */
+
+ pm1a_control |=
+ (acpi_gbl_sleep_type_a << sleep_type_reg_info->bit_position);
+ pm1b_control |=
+ (acpi_gbl_sleep_type_b << sleep_type_reg_info->bit_position);
+
+ /*
+ * We split the writes of SLP_TYP and SLP_EN to workaround
+ * poorly implemented hardware.
+ */
+
+ /* Write #1: write the SLP_TYP data to the PM1 Control registers */
+
+ status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Insert the sleep enable (SLP_EN) bit */
+
+ pm1a_control |= sleep_enable_reg_info->access_bit_mask;
+ pm1b_control |= sleep_enable_reg_info->access_bit_mask;
+
+ /* Flush caches, as per ACPI specification */
+
+ if (sleep_state < ACPI_STATE_S4) {
+ ACPI_FLUSH_CPU_CACHE();
+ }
+
+ status = acpi_os_enter_sleep(sleep_state, pm1a_control, pm1b_control);
+ if (status == AE_CTRL_TERMINATE) {
+ return_ACPI_STATUS(AE_OK);
+ }
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Write #2: Write both SLP_TYP + SLP_EN */
+
+ status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (sleep_state > ACPI_STATE_S3) {
+ /*
+ * We wanted to sleep > S3, but it didn't happen (by virtue of the
+ * fact that we are still executing!)
+ *
+ * Wait ten seconds, then try again. This is to get S4/S5 to work on
+ * all machines.
+ *
+ * We wait so long to allow chipsets that poll this reg very slowly
+ * to still read the right value. Ideally, this block would go
+ * away entirely.
+ */
+ acpi_os_stall(10 * ACPI_USEC_PER_SEC);
+
+ status = acpi_hw_register_write(ACPI_REGISTER_PM1_CONTROL,
+ sleep_enable_reg_info->
+ access_bit_mask);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* Wait for transition back to Working State */
+
+ do {
+ status =
+ acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ } while (!in_value);
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_legacy_wake_prep
+ *
+ * PARAMETERS: sleep_state - Which sleep state we just exited
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
+ * sleep.
+ * Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state)
+{
+ acpi_status status = AE_OK;
+ struct acpi_bit_register_info *sleep_type_reg_info;
+ struct acpi_bit_register_info *sleep_enable_reg_info;
+ u32 pm1a_control;
+ u32 pm1b_control;
+
+ ACPI_FUNCTION_TRACE(hw_legacy_wake_prep);
+
+ /*
+ * Set SLP_TYPE and SLP_EN to state S0.
+ * This is unclear from the ACPI Spec, but it is required
+ * by some machines.
+ */
+ if (acpi_gbl_sleep_type_a_s0 != ACPI_SLEEP_TYPE_INVALID) {
+ sleep_type_reg_info =
+ acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE);
+ sleep_enable_reg_info =
+ acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_ENABLE);
+
+ /* Get current value of PM1A control */
+
+ status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL,
+ &pm1a_control);
+ if (ACPI_SUCCESS(status)) {
+
+ /* Clear the SLP_EN and SLP_TYP fields */
+
+ pm1a_control &= ~(sleep_type_reg_info->access_bit_mask |
+ sleep_enable_reg_info->
+ access_bit_mask);
+ pm1b_control = pm1a_control;
+
+ /* Insert the SLP_TYP bits */
+
+ pm1a_control |= (acpi_gbl_sleep_type_a_s0 <<
+ sleep_type_reg_info->bit_position);
+ pm1b_control |= (acpi_gbl_sleep_type_b_s0 <<
+ sleep_type_reg_info->bit_position);
+
+ /* Write the control registers and ignore any errors */
+
+ (void)acpi_hw_write_pm1_control(pm1a_control,
+ pm1b_control);
+ }
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_legacy_wake
+ *
+ * PARAMETERS: sleep_state - Which sleep state we just exited
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ * Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_legacy_wake(u8 sleep_state)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(hw_legacy_wake);
+
+ /* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */
+
+ acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID;
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WAKING);
+
+ /*
+ * GPEs must be enabled before _WAK is called as GPEs
+ * might get fired there
+ *
+ * Restore the GPEs:
+ * 1) Disable all GPEs
+ * 2) Enable all runtime GPEs
+ */
+ status = acpi_hw_disable_all_gpes();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_hw_enable_all_runtime_gpes();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Now we can execute _WAK, etc. Some machines require that the GPEs
+ * are enabled before the wake methods are executed.
+ */
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__WAK, sleep_state);
+
+ /*
+ * Some BIOS code assumes that WAK_STS will be cleared on resume
+ * and use it to determine whether the system is rebooting or
+ * resuming. Clear WAK_STS for compatibility.
+ */
+ (void)acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS,
+ ACPI_CLEAR_STATUS);
+ acpi_gbl_system_awake_and_running = TRUE;
+
+ /* Enable power button */
+
+ (void)
+ acpi_write_bit_register(acpi_gbl_fixed_event_info
+ [ACPI_EVENT_POWER_BUTTON].
+ enable_register_id, ACPI_ENABLE_EVENT);
+
+ (void)
+ acpi_write_bit_register(acpi_gbl_fixed_event_info
+ [ACPI_EVENT_POWER_BUTTON].
+ status_register_id, ACPI_CLEAR_STATUS);
+
+ /* Enable sleep button */
+
+ (void)
+ acpi_write_bit_register(acpi_gbl_fixed_event_info
+ [ACPI_EVENT_SLEEP_BUTTON].
+ enable_register_id, ACPI_ENABLE_EVENT);
+
+ (void)
+ acpi_write_bit_register(acpi_gbl_fixed_event_info
+ [ACPI_EVENT_SLEEP_BUTTON].
+ status_register_id, ACPI_CLEAR_STATUS);
+
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING);
+ return_ACPI_STATUS(status);
+}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwtimer.c b/drivers/acpi/acpica/hwtimer.c
new file mode 100644
index 000000000..46f3ae03a
--- /dev/null
+++ b/drivers/acpi/acpica/hwtimer.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Name: hwtimer.c - ACPI Power Management Timer Interface
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ACPI_MODULE_NAME("hwtimer")
+
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+/******************************************************************************
+ *
+ * FUNCTION: acpi_get_timer_resolution
+ *
+ * PARAMETERS: resolution - Where the resolution is returned
+ *
+ * RETURN: Status and timer resolution
+ *
+ * DESCRIPTION: Obtains resolution of the ACPI PM Timer (24 or 32 bits).
+ *
+ ******************************************************************************/
+acpi_status acpi_get_timer_resolution(u32 * resolution)
+{
+ ACPI_FUNCTION_TRACE(acpi_get_timer_resolution);
+
+ if (!resolution) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if ((acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER) == 0) {
+ *resolution = 24;
+ } else {
+ *resolution = 32;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_timer_resolution)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_get_timer
+ *
+ * PARAMETERS: ticks - Where the timer value is returned
+ *
+ * RETURN: Status and current timer value (ticks)
+ *
+ * DESCRIPTION: Obtains current value of ACPI PM Timer (in ticks).
+ *
+ ******************************************************************************/
+acpi_status acpi_get_timer(u32 * ticks)
+{
+ acpi_status status;
+ u64 timer_value;
+
+ ACPI_FUNCTION_TRACE(acpi_get_timer);
+
+ if (!ticks) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* ACPI 5.0A: PM Timer is optional */
+
+ if (!acpi_gbl_FADT.xpm_timer_block.address) {
+ return_ACPI_STATUS(AE_SUPPORT);
+ }
+
+ status = acpi_hw_read(&timer_value, &acpi_gbl_FADT.xpm_timer_block);
+ if (ACPI_SUCCESS(status)) {
+
+ /* ACPI PM Timer is defined to be 32 bits (PM_TMR_LEN) */
+
+ *ticks = (u32)timer_value;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_timer)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_get_timer_duration
+ *
+ * PARAMETERS: start_ticks - Starting timestamp
+ * end_ticks - End timestamp
+ * time_elapsed - Where the elapsed time is returned
+ *
+ * RETURN: Status and time_elapsed
+ *
+ * DESCRIPTION: Computes the time elapsed (in microseconds) between two
+ * PM Timer time stamps, taking into account the possibility of
+ * rollovers, the timer resolution, and timer frequency.
+ *
+ * The PM Timer's clock ticks at roughly 3.6 times per
+ * _microsecond_, and its clock continues through Cx state
+ * transitions (unlike many CPU timestamp counters) -- making it
+ * a versatile and accurate timer.
+ *
+ * Note that this function accommodates only a single timer
+ * rollover. Thus for 24-bit timers, this function should only
+ * be used for calculating durations less than ~4.6 seconds
+ * (~20 minutes for 32-bit timers) -- calculations below:
+ *
+ * 2**24 Ticks / 3,600,000 Ticks/Sec = 4.66 sec
+ * 2**32 Ticks / 3,600,000 Ticks/Sec = 1193 sec or 19.88 minutes
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_timer_duration(u32 start_ticks, u32 end_ticks, u32 *time_elapsed)
+{
+ acpi_status status;
+ u64 delta_ticks;
+ u64 quotient;
+
+ ACPI_FUNCTION_TRACE(acpi_get_timer_duration);
+
+ if (!time_elapsed) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* ACPI 5.0A: PM Timer is optional */
+
+ if (!acpi_gbl_FADT.xpm_timer_block.address) {
+ return_ACPI_STATUS(AE_SUPPORT);
+ }
+
+ if (start_ticks == end_ticks) {
+ *time_elapsed = 0;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Compute Tick Delta:
+ * Handle (max one) timer rollovers on 24-bit versus 32-bit timers.
+ */
+ delta_ticks = end_ticks;
+ if (start_ticks > end_ticks) {
+ if ((acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER) == 0) {
+
+ /* 24-bit Timer */
+
+ delta_ticks |= (u64)1 << 24;
+ } else {
+ /* 32-bit Timer */
+
+ delta_ticks |= (u64)1 << 32;
+ }
+ }
+ delta_ticks -= start_ticks;
+
+ /*
+ * Compute Duration (Requires a 64-bit multiply and divide):
+ *
+ * time_elapsed (microseconds) =
+ * (delta_ticks * ACPI_USEC_PER_SEC) / ACPI_PM_TIMER_FREQUENCY;
+ */
+ status = acpi_ut_short_divide(delta_ticks * ACPI_USEC_PER_SEC,
+ ACPI_PM_TIMER_FREQUENCY, &quotient, NULL);
+
+ *time_elapsed = (u32)quotient;
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_timer_duration)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c
new file mode 100644
index 000000000..0d392e7b0
--- /dev/null
+++ b/drivers/acpi/acpica/hwvalid.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: hwvalid - I/O request validation
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ACPI_MODULE_NAME("hwvalid")
+
+/* Local prototypes */
+static acpi_status
+acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width);
+
+/*
+ * Protected I/O ports. Some ports are always illegal, and some are
+ * conditionally illegal. This table must remain ordered by port address.
+ *
+ * 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 nothing newer than
+ * the specific _OSI strings.
+ *
+ * This provides ACPICA with the desired port protections and
+ * Microsoft compatibility.
+ *
+ * Description of port entries:
+ * DMA: DMA controller
+ * PIC0: Programmable Interrupt Controller (8259A)
+ * PIT1: System Timer 1
+ * PIT2: System Timer 2 failsafe
+ * RTC: Real-time clock
+ * CMOS: Extended CMOS
+ * DMA1: DMA 1 page registers
+ * DMA1L: DMA 1 Ch 0 low page
+ * DMA2: DMA 2 page registers
+ * DMA2L: DMA 2 low page refresh
+ * ARBC: Arbitration control
+ * SETUP: Reserved system board setup
+ * POS: POS channel select
+ * PIC1: Cascaded PIC
+ * IDMA: ISA DMA
+ * ELCR: PIC edge/level registers
+ * PCI: PCI configuration space
+ */
+static const struct acpi_port_info acpi_protected_ports[] = {
+ {"DMA", 0x0000, 0x000F, ACPI_OSI_WIN_XP},
+ {"PIC0", 0x0020, 0x0021, ACPI_ALWAYS_ILLEGAL},
+ {"PIT1", 0x0040, 0x0043, ACPI_OSI_WIN_XP},
+ {"PIT2", 0x0048, 0x004B, ACPI_OSI_WIN_XP},
+ {"RTC", 0x0070, 0x0071, ACPI_OSI_WIN_XP},
+ {"CMOS", 0x0074, 0x0076, ACPI_OSI_WIN_XP},
+ {"DMA1", 0x0081, 0x0083, ACPI_OSI_WIN_XP},
+ {"DMA1L", 0x0087, 0x0087, ACPI_OSI_WIN_XP},
+ {"DMA2", 0x0089, 0x008B, ACPI_OSI_WIN_XP},
+ {"DMA2L", 0x008F, 0x008F, ACPI_OSI_WIN_XP},
+ {"ARBC", 0x0090, 0x0091, ACPI_OSI_WIN_XP},
+ {"SETUP", 0x0093, 0x0094, ACPI_OSI_WIN_XP},
+ {"POS", 0x0096, 0x0097, ACPI_OSI_WIN_XP},
+ {"PIC1", 0x00A0, 0x00A1, ACPI_ALWAYS_ILLEGAL},
+ {"IDMA", 0x00C0, 0x00DF, ACPI_OSI_WIN_XP},
+ {"ELCR", 0x04D0, 0x04D1, ACPI_ALWAYS_ILLEGAL},
+ {"PCI", 0x0CF8, 0x0CFF, ACPI_OSI_WIN_XP}
+};
+
+#define ACPI_PORT_INFO_ENTRIES ACPI_ARRAY_LENGTH (acpi_protected_ports)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_validate_io_request
+ *
+ * PARAMETERS: Address Address of I/O port/register
+ * bit_width Number of bits (8,16,32)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Validates an I/O request (address/length). Certain ports are
+ * always illegal and some ports are only illegal depending on
+ * the requests the BIOS AML code makes to the predefined
+ * _OSI method.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width)
+{
+ u32 i;
+ u32 byte_width;
+ acpi_io_address last_address;
+ const struct acpi_port_info *port_info;
+
+ ACPI_FUNCTION_TRACE(hw_validate_io_request);
+
+ /* Supported widths are 8/16/32 */
+
+ if ((bit_width != 8) && (bit_width != 16) && (bit_width != 32)) {
+ ACPI_ERROR((AE_INFO,
+ "Bad BitWidth parameter: %8.8X", bit_width));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ port_info = acpi_protected_ports;
+ byte_width = ACPI_DIV_8(bit_width);
+ last_address = address + byte_width - 1;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_IO,
+ "Address %8.8X%8.8X LastAddress %8.8X%8.8X Length %X",
+ ACPI_FORMAT_UINT64(address),
+ ACPI_FORMAT_UINT64(last_address), byte_width));
+
+ /* Maximum 16-bit address in I/O space */
+
+ if (last_address > ACPI_UINT16_MAX) {
+ ACPI_ERROR((AE_INFO,
+ "Illegal I/O port address/length above 64K: %8.8X%8.8X/0x%X",
+ ACPI_FORMAT_UINT64(address), byte_width));
+ return_ACPI_STATUS(AE_LIMIT);
+ }
+
+ /* Exit if requested address is not within the protected port table */
+
+ if (address > acpi_protected_ports[ACPI_PORT_INFO_ENTRIES - 1].end) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Check request against the list of protected I/O ports */
+
+ for (i = 0; i < ACPI_PORT_INFO_ENTRIES; i++, port_info++) {
+ /*
+ * Check if the requested address range will write to a reserved
+ * port. There are four cases to consider:
+ *
+ * 1) Address range is contained completely in the port address range
+ * 2) Address range overlaps port range at the port range start
+ * 3) Address range overlaps port range at the port range end
+ * 4) Address range completely encompasses the port range
+ */
+ if ((address <= port_info->end)
+ && (last_address >= port_info->start)) {
+
+ /* Port illegality may depend on the _OSI calls made by the BIOS */
+
+ 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),
+ byte_width, port_info->name,
+ port_info->start,
+ port_info->end));
+
+ return_ACPI_STATUS(AE_AML_ILLEGAL_ADDRESS);
+ }
+ }
+
+ /* Finished if address range ends before the end of this port */
+
+ if (last_address <= port_info->end) {
+ break;
+ }
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_read_port
+ *
+ * PARAMETERS: Address Address of I/O port/register to read
+ * Value Where value (data) is returned
+ * Width Number of bits
+ *
+ * RETURN: Status and value read from port
+ *
+ * DESCRIPTION: Read data from an I/O port or register. This is a front-end
+ * to acpi_os_read_port that performs validation on both the port
+ * address and the length.
+ *
+ *****************************************************************************/
+
+acpi_status acpi_hw_read_port(acpi_io_address address, u32 *value, u32 width)
+{
+ acpi_status status;
+ u32 one_byte;
+ u32 i;
+
+ /* Truncate address to 16 bits if requested */
+
+ if (acpi_gbl_truncate_io_addresses) {
+ address &= ACPI_UINT16_MAX;
+ }
+
+ /* Validate the entire request and perform the I/O */
+
+ status = acpi_hw_validate_io_request(address, width);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_os_read_port(address, value, width);
+ return (status);
+ }
+
+ if (status != AE_AML_ILLEGAL_ADDRESS) {
+ return (status);
+ }
+
+ /*
+ * There has been a protection violation within the request. Fall
+ * back to byte granularity port I/O and ignore the failing bytes.
+ * This provides compatibility with other ACPI implementations.
+ */
+ for (i = 0, *value = 0; i < width; i += 8) {
+
+ /* Validate and read one byte */
+
+ if (acpi_hw_validate_io_request(address, 8) == AE_OK) {
+ status = acpi_os_read_port(address, &one_byte, 8);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ *value |= (one_byte << i);
+ }
+
+ address++;
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_write_port
+ *
+ * PARAMETERS: Address Address of I/O port/register to write
+ * Value Value to write
+ * Width Number of bits
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write data to an I/O port or register. This is a front-end
+ * to acpi_os_write_port that performs validation on both the port
+ * address and the length.
+ *
+ *****************************************************************************/
+
+acpi_status acpi_hw_write_port(acpi_io_address address, u32 value, u32 width)
+{
+ acpi_status status;
+ u32 i;
+
+ /* Truncate address to 16 bits if requested */
+
+ if (acpi_gbl_truncate_io_addresses) {
+ address &= ACPI_UINT16_MAX;
+ }
+
+ /* Validate the entire request and perform the I/O */
+
+ status = acpi_hw_validate_io_request(address, width);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_os_write_port(address, value, width);
+ return (status);
+ }
+
+ if (status != AE_AML_ILLEGAL_ADDRESS) {
+ return (status);
+ }
+
+ /*
+ * There has been a protection violation within the request. Fall
+ * back to byte granularity port I/O and ignore the failing bytes.
+ * This provides compatibility with other ACPI implementations.
+ */
+ for (i = 0; i < width; i += 8) {
+
+ /* Validate and write one byte */
+
+ if (acpi_hw_validate_io_request(address, 8) == AE_OK) {
+ status =
+ acpi_os_write_port(address, (value >> i) & 0xFF, 8);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ address++;
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_hw_validate_io_block
+ *
+ * PARAMETERS: Address Address of I/O port/register blobk
+ * bit_width Number of bits (8,16,32) in each register
+ * count Number of registers in the block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Validates a block of I/O ports/registers.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_validate_io_block(u64 address, u32 bit_width, u32 count)
+{
+ acpi_status status;
+
+ while (count--) {
+ status = acpi_hw_validate_io_request((acpi_io_address)address,
+ bit_width);
+ if (ACPI_FAILURE(status))
+ return_ACPI_STATUS(status);
+
+ address += ACPI_DIV_8(bit_width);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c
new file mode 100644
index 000000000..55d9b897e
--- /dev/null
+++ b/drivers/acpi/acpica/hwxface.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: hwxface - Public ACPICA hardware interfaces
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ACPI_MODULE_NAME("hwxface")
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_reset
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Set reset register in memory or IO space. Note: Does not
+ * support reset register in PCI config space, this must be
+ * handled separately.
+ *
+ ******************************************************************************/
+acpi_status acpi_reset(void)
+{
+ struct acpi_generic_address *reset_reg;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_reset);
+
+ reset_reg = &acpi_gbl_FADT.reset_register;
+
+ /* Check if the reset register is supported */
+
+ if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) ||
+ !reset_reg->address) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ if (reset_reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ /*
+ * For I/O space, write directly to the OSL. This bypasses the port
+ * validation mechanism, which may block a valid write to the reset
+ * register.
+ *
+ * NOTE:
+ * The ACPI spec requires the reset register width to be 8, so we
+ * hardcode it here and ignore the FADT value. This maintains
+ * compatibility with other ACPI implementations that have allowed
+ * BIOS code with bad register width values to go unnoticed.
+ */
+ status = acpi_os_write_port((acpi_io_address)reset_reg->address,
+ acpi_gbl_FADT.reset_value,
+ ACPI_RESET_REGISTER_WIDTH);
+ } else {
+ /* Write the reset value to the reset register */
+
+ status = acpi_hw_write(acpi_gbl_FADT.reset_value, reset_reg);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_reset)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_read
+ *
+ * PARAMETERS: value - Where the value is returned
+ * reg - GAS register structure
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read from either memory or IO space.
+ *
+ * LIMITATIONS: <These limitations also apply to acpi_write>
+ * bit_width must be exactly 8, 16, 32, or 64.
+ * space_ID must be system_memory or system_IO.
+ * bit_offset and access_width are currently ignored, as there has
+ * not been a need to implement these.
+ *
+ ******************************************************************************/
+acpi_status acpi_read(u64 *return_value, struct acpi_generic_address *reg)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_NAME(acpi_read);
+
+ status = acpi_hw_read(return_value, reg);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_read)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_write
+ *
+ * PARAMETERS: value - Value to be written
+ * reg - GAS register structure
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write to either memory or IO space.
+ *
+ ******************************************************************************/
+acpi_status acpi_write(u64 value, struct acpi_generic_address *reg)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_NAME(acpi_write);
+
+ status = acpi_hw_write(value, reg);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_write)
+
+#if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_read_bit_register
+ *
+ * PARAMETERS: register_id - ID of ACPI Bit Register to access
+ * return_value - Value that was read from the register,
+ * normalized to bit position zero.
+ *
+ * RETURN: Status and the value read from the specified Register. Value
+ * returned is normalized to bit0 (is shifted all the way right)
+ *
+ * DESCRIPTION: ACPI bit_register read function. Does not acquire the HW lock.
+ *
+ * SUPPORTS: Bit fields in PM1 Status, PM1 Enable, PM1 Control, and
+ * PM2 Control.
+ *
+ * Note: The hardware lock is not required when reading the ACPI bit registers
+ * since almost all of them are single bit and it does not matter that
+ * the parent hardware register can be split across two physical
+ * registers. The only multi-bit field is SLP_TYP in the PM1 control
+ * register, but this field does not cross an 8-bit boundary (nor does
+ * it make much sense to actually read this field.)
+ *
+ ******************************************************************************/
+acpi_status acpi_read_bit_register(u32 register_id, u32 *return_value)
+{
+ struct acpi_bit_register_info *bit_reg_info;
+ u32 register_value;
+ u32 value;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_U32(acpi_read_bit_register, register_id);
+
+ /* Get the info structure corresponding to the requested ACPI Register */
+
+ bit_reg_info = acpi_hw_get_bit_register_info(register_id);
+ if (!bit_reg_info) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Read the entire parent register */
+
+ status = acpi_hw_register_read(bit_reg_info->parent_register,
+ &register_value);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Normalize the value that was read, mask off other bits */
+
+ value = ((register_value & bit_reg_info->access_bit_mask)
+ >> bit_reg_info->bit_position);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_IO,
+ "BitReg %X, ParentReg %X, Actual %8.8X, ReturnValue %8.8X\n",
+ register_id, bit_reg_info->parent_register,
+ register_value, value));
+
+ *return_value = value;
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_read_bit_register)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_write_bit_register
+ *
+ * PARAMETERS: register_id - ID of ACPI Bit Register to access
+ * value - Value to write to the register, in bit
+ * position zero. The bit is automatically
+ * shifted to the correct position.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: ACPI Bit Register write function. Acquires the hardware lock
+ * since most operations require a read/modify/write sequence.
+ *
+ * SUPPORTS: Bit fields in PM1 Status, PM1 Enable, PM1 Control, and
+ * PM2 Control.
+ *
+ * Note that at this level, the fact that there may be actually two
+ * hardware registers (A and B - and B may not exist) is abstracted.
+ *
+ ******************************************************************************/
+acpi_status acpi_write_bit_register(u32 register_id, u32 value)
+{
+ struct acpi_bit_register_info *bit_reg_info;
+ acpi_cpu_flags lock_flags;
+ u32 register_value;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_U32(acpi_write_bit_register, register_id);
+
+ /* Get the info structure corresponding to the requested ACPI Register */
+
+ bit_reg_info = acpi_hw_get_bit_register_info(register_id);
+ if (!bit_reg_info) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ lock_flags = acpi_os_acquire_raw_lock(acpi_gbl_hardware_lock);
+
+ /*
+ * At this point, we know that the parent register is one of the
+ * following: PM1 Status, PM1 Enable, PM1 Control, or PM2 Control
+ */
+ if (bit_reg_info->parent_register != ACPI_REGISTER_PM1_STATUS) {
+ /*
+ * 1) Case for PM1 Enable, PM1 Control, and PM2 Control
+ *
+ * Perform a register read to preserve the bits that we are not
+ * interested in
+ */
+ status = acpi_hw_register_read(bit_reg_info->parent_register,
+ &register_value);
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+
+ /*
+ * Insert the input bit into the value that was just read
+ * and write the register
+ */
+ ACPI_REGISTER_INSERT_VALUE(register_value,
+ bit_reg_info->bit_position,
+ bit_reg_info->access_bit_mask,
+ value);
+
+ status = acpi_hw_register_write(bit_reg_info->parent_register,
+ register_value);
+ } else {
+ /*
+ * 2) Case for PM1 Status
+ *
+ * The Status register is different from the rest. Clear an event
+ * by writing 1, writing 0 has no effect. So, the only relevant
+ * information is the single bit we're interested in, all others
+ * should be written as 0 so they will be left unchanged.
+ */
+ register_value = ACPI_REGISTER_PREPARE_BITS(value,
+ bit_reg_info->
+ bit_position,
+ bit_reg_info->
+ access_bit_mask);
+
+ /* No need to write the register if value is all zeros */
+
+ if (register_value) {
+ status =
+ acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS,
+ register_value);
+ }
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_IO,
+ "BitReg %X, ParentReg %X, Value %8.8X, Actual %8.8X\n",
+ register_id, bit_reg_info->parent_register, value,
+ register_value));
+
+unlock_and_exit:
+
+ acpi_os_release_raw_lock(acpi_gbl_hardware_lock, lock_flags);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_write_bit_register)
+#endif /* !ACPI_REDUCED_HARDWARE */
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_sleep_type_data
+ *
+ * PARAMETERS: sleep_state - Numeric sleep state
+ * *sleep_type_a - Where SLP_TYPa is returned
+ * *sleep_type_b - Where SLP_TYPb is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Obtain the SLP_TYPa and SLP_TYPb values for the requested
+ * sleep state via the appropriate \_Sx object.
+ *
+ * The sleep state package returned from the corresponding \_Sx_ object
+ * must contain at least one integer.
+ *
+ * March 2005:
+ * Added support for a package that contains two integers. This
+ * goes against the ACPI specification which defines this object as a
+ * package with one encoded DWORD integer. However, existing practice
+ * by many BIOS vendors is to return a package with 2 or more integer
+ * elements, at least one per sleep type (A/B).
+ *
+ * January 2013:
+ * Therefore, we must be prepared to accept a package with either a
+ * single integer or multiple integers.
+ *
+ * The single integer DWORD format is as follows:
+ * BYTE 0 - Value for the PM1A SLP_TYP register
+ * BYTE 1 - Value for the PM1B SLP_TYP register
+ * BYTE 2-3 - Reserved
+ *
+ * The dual integer format is as follows:
+ * Integer 0 - Value for the PM1A SLP_TYP register
+ * Integer 1 - Value for the PM1A SLP_TYP register
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
+{
+ acpi_status status;
+ struct acpi_evaluate_info *info;
+ union acpi_operand_object **elements;
+
+ ACPI_FUNCTION_TRACE(acpi_get_sleep_type_data);
+
+ /* Validate parameters */
+
+ if ((sleep_state > ACPI_S_STATES_MAX) || !sleep_type_a || !sleep_type_b) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Allocate the evaluation information block */
+
+ info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
+ if (!info) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /*
+ * Evaluate the \_Sx namespace object containing the register values
+ * for this state
+ */
+ info->relative_pathname = acpi_gbl_sleep_state_names[sleep_state];
+
+ status = acpi_ns_evaluate(info);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_NOT_FOUND) {
+
+ /* The _Sx states are optional, ignore NOT_FOUND */
+
+ goto final_cleanup;
+ }
+
+ goto warning_cleanup;
+ }
+
+ /* Must have a return object */
+
+ if (!info->return_object) {
+ ACPI_ERROR((AE_INFO, "No Sleep State object returned from [%s]",
+ info->relative_pathname));
+ status = AE_AML_NO_RETURN_VALUE;
+ goto warning_cleanup;
+ }
+
+ /* Return object must be of type Package */
+
+ if (info->return_object->common.type != ACPI_TYPE_PACKAGE) {
+ ACPI_ERROR((AE_INFO,
+ "Sleep State return object is not a Package"));
+ status = AE_AML_OPERAND_TYPE;
+ goto return_value_cleanup;
+ }
+
+ /*
+ * Any warnings about the package length or the object types have
+ * already been issued by the predefined name module -- there is no
+ * need to repeat them here.
+ */
+ elements = info->return_object->package.elements;
+ switch (info->return_object->package.count) {
+ case 0:
+
+ status = AE_AML_PACKAGE_LIMIT;
+ break;
+
+ case 1:
+
+ if (elements[0]->common.type != ACPI_TYPE_INTEGER) {
+ status = AE_AML_OPERAND_TYPE;
+ break;
+ }
+
+ /* A valid _Sx_ package with one integer */
+
+ *sleep_type_a = (u8)elements[0]->integer.value;
+ *sleep_type_b = (u8)(elements[0]->integer.value >> 8);
+ break;
+
+ case 2:
+ default:
+
+ if ((elements[0]->common.type != ACPI_TYPE_INTEGER) ||
+ (elements[1]->common.type != ACPI_TYPE_INTEGER)) {
+ status = AE_AML_OPERAND_TYPE;
+ break;
+ }
+
+ /* A valid _Sx_ package with two integers */
+
+ *sleep_type_a = (u8)elements[0]->integer.value;
+ *sleep_type_b = (u8)elements[1]->integer.value;
+ break;
+ }
+
+return_value_cleanup:
+ acpi_ut_remove_reference(info->return_object);
+
+warning_cleanup:
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While evaluating Sleep State [%s]",
+ info->relative_pathname));
+ }
+
+final_cleanup:
+ ACPI_FREE(info);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_sleep_type_data)
diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c
new file mode 100644
index 000000000..aff51ceea
--- /dev/null
+++ b/drivers/acpi/acpica/hwxfsleep.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Name: hwxfsleep.c - ACPI Hardware Sleep/Wake External Interfaces
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_HARDWARE
+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
+ *
+ * PARAMETERS: facs - Pointer to FACS table
+ * physical_address - 32-bit physical address of ACPI real mode
+ * entry point
+ * physical_address64 - 64-bit physical address of ACPI protected
+ * mode entry point
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Sets the firmware_waking_vector fields of the FACS
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_set_firmware_waking_vector(struct acpi_table_facs *facs,
+ acpi_physical_address physical_address,
+ acpi_physical_address physical_address64)
+{
+ ACPI_FUNCTION_TRACE(acpi_hw_set_firmware_waking_vector);
+
+
+ /*
+ * According to the ACPI specification 2.0c and later, the 64-bit
+ * waking vector should be cleared and the 32-bit waking vector should
+ * be used, unless we want the wake-up code to be called by the BIOS in
+ * Protected Mode. Some systems (for example HP dv5-1004nr) are known
+ * to fail to resume if the 64-bit vector is used.
+ */
+
+ /* Set the 32-bit vector */
+
+ facs->firmware_waking_vector = (u32)physical_address;
+
+ if (facs->length > 32) {
+ if (facs->version >= 1) {
+
+ /* Set the 64-bit vector */
+
+ facs->xfirmware_waking_vector = physical_address64;
+ } else {
+ /* Clear the 64-bit vector if it exists */
+
+ facs->xfirmware_waking_vector = 0;
+ }
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_set_firmware_waking_vector
+ *
+ * PARAMETERS: physical_address - 32-bit physical address of ACPI real mode
+ * entry point
+ * physical_address64 - 64-bit physical address of ACPI protected
+ * mode entry point
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Sets the firmware_waking_vector fields of the FACS
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_set_firmware_waking_vector(acpi_physical_address physical_address,
+ acpi_physical_address physical_address64)
+{
+
+ ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector);
+
+ if (acpi_gbl_FACS) {
+ (void)acpi_hw_set_firmware_waking_vector(acpi_gbl_FACS,
+ physical_address,
+ physical_address64);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_enter_sleep_state_s4bios
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform a S4 bios request.
+ * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+acpi_status acpi_enter_sleep_state_s4bios(void)
+{
+ u32 in_value;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_s4bios);
+
+ /* Clear the wake status bit (PM1) */
+
+ status =
+ acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_hw_clear_acpi_status();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * 1) Disable all GPEs
+ * 2) Enable all wakeup GPEs
+ */
+ status = acpi_hw_disable_all_gpes();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ acpi_gbl_system_awake_and_running = FALSE;
+
+ status = acpi_hw_enable_all_wakeup_gpes();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_hw_write_port(acpi_gbl_FADT.smi_command,
+ (u32)acpi_gbl_FADT.s4_bios_request, 8);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ do {
+ acpi_os_stall(ACPI_USEC_PER_MSEC);
+ status =
+ acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ } while (!in_value);
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios)
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_enter_sleep_state_prep
+ *
+ * PARAMETERS: sleep_state - Which sleep state to enter
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Prepare to enter a system sleep state.
+ * This function must execute with interrupts enabled.
+ * We break sleeping into 2 stages so that OSPM can handle
+ * various OS-specific tasks between the two steps.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_enter_sleep_state_prep(u8 sleep_state)
+{
+ acpi_status status;
+ struct acpi_object_list arg_list;
+ union acpi_object arg;
+ u32 sst_value;
+
+ ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_prep);
+
+ status = acpi_get_sleep_type_data(sleep_state,
+ &acpi_gbl_sleep_type_a,
+ &acpi_gbl_sleep_type_b);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_get_sleep_type_data(ACPI_STATE_S0,
+ &acpi_gbl_sleep_type_a_s0,
+ &acpi_gbl_sleep_type_b_s0);
+ if (ACPI_FAILURE(status)) {
+ acpi_gbl_sleep_type_a_s0 = ACPI_SLEEP_TYPE_INVALID;
+ }
+
+ /* Execute the _PTS method (Prepare To Sleep) */
+
+ arg_list.count = 1;
+ arg_list.pointer = &arg;
+ arg.type = ACPI_TYPE_INTEGER;
+ arg.integer.value = sleep_state;
+
+ status =
+ acpi_evaluate_object(NULL, METHOD_PATHNAME__PTS, &arg_list, NULL);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Setup the argument to the _SST method (System STatus) */
+
+ switch (sleep_state) {
+ case ACPI_STATE_S0:
+
+ sst_value = ACPI_SST_WORKING;
+ break;
+
+ case ACPI_STATE_S1:
+ case ACPI_STATE_S2:
+ case ACPI_STATE_S3:
+
+ sst_value = ACPI_SST_SLEEPING;
+ break;
+
+ case ACPI_STATE_S4:
+
+ sst_value = ACPI_SST_SLEEP_CONTEXT;
+ break;
+
+ default:
+
+ sst_value = ACPI_SST_INDICATOR_OFF; /* Default is off */
+ break;
+ }
+
+ /*
+ * Set the system indicators to show the desired sleep state.
+ * _SST is an optional method (return no error if not found)
+ */
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, sst_value);
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_enter_sleep_state
+ *
+ * PARAMETERS: sleep_state - Which sleep state to enter
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enter a system sleep state
+ * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+acpi_status acpi_enter_sleep_state(u8 sleep_state)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_enter_sleep_state);
+
+ if ((acpi_gbl_sleep_type_a > ACPI_SLEEP_TYPE_MAX) ||
+ (acpi_gbl_sleep_type_b > ACPI_SLEEP_TYPE_MAX)) {
+ ACPI_ERROR((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X",
+ acpi_gbl_sleep_type_a, acpi_gbl_sleep_type_b));
+ return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
+ }
+
+#if !ACPI_REDUCED_HARDWARE
+ if (!acpi_gbl_reduced_hardware)
+ status = acpi_hw_legacy_sleep(sleep_state);
+ else
+#endif
+ status = acpi_hw_extended_sleep(sleep_state);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_leave_sleep_state_prep
+ *
+ * PARAMETERS: sleep_state - Which sleep state we are exiting
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
+ * sleep. Called with interrupts DISABLED.
+ * We break wake/resume into 2 stages so that OSPM can handle
+ * various OS-specific tasks between the two steps.
+ *
+ ******************************************************************************/
+acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep);
+
+#if !ACPI_REDUCED_HARDWARE
+ if (!acpi_gbl_reduced_hardware)
+ status = acpi_hw_legacy_wake_prep(sleep_state);
+ else
+#endif
+ status = acpi_hw_extended_wake_prep(sleep_state);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state_prep)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_leave_sleep_state
+ *
+ * PARAMETERS: sleep_state - Which sleep state we are exiting
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ * Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+acpi_status acpi_leave_sleep_state(u8 sleep_state)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_leave_sleep_state);
+
+#if !ACPI_REDUCED_HARDWARE
+ if (!acpi_gbl_reduced_hardware)
+ status = acpi_hw_legacy_wake(sleep_state);
+ else
+#endif
+ status = acpi_hw_extended_wake(sleep_state);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state)
diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c
new file mode 100644
index 000000000..a0c1a665d
--- /dev/null
+++ b/drivers/acpi/acpica/nsaccess.c
@@ -0,0 +1,745 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: nsaccess - Top-level functions for accessing ACPI namespace
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "amlcode.h"
+#include "acnamesp.h"
+#include "acdispat.h"
+
+#ifdef ACPI_ASL_COMPILER
+#include "acdisasm.h"
+#endif
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsaccess")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_root_initialize
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Allocate and initialize the default root named objects
+ *
+ * MUTEX: Locks namespace for entire execution
+ *
+ ******************************************************************************/
+acpi_status acpi_ns_root_initialize(void)
+{
+ acpi_status status;
+ const struct acpi_predefined_names *init_val = NULL;
+ struct acpi_namespace_node *new_node;
+ struct acpi_namespace_node *prev_node = NULL;
+ union acpi_operand_object *obj_desc;
+ acpi_string val = NULL;
+
+ ACPI_FUNCTION_TRACE(ns_root_initialize);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * The global root ptr is initially NULL, so a non-NULL value indicates
+ * that acpi_ns_root_initialize() has already been called; just return.
+ */
+ if (acpi_gbl_root_node) {
+ status = AE_OK;
+ goto unlock_and_exit;
+ }
+
+ /*
+ * Tell the rest of the subsystem that the root is initialized
+ * (This is OK because the namespace is locked)
+ */
+ acpi_gbl_root_node = &acpi_gbl_root_node_struct;
+
+ /* Enter the predefined names in the name table */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Entering predefined entries into namespace\n"));
+
+ /*
+ * Create the initial (default) namespace.
+ * This namespace looks like something similar to this:
+ *
+ * ACPI Namespace (from Namespace Root):
+ * 0 _GPE Scope 00203160 00
+ * 0 _PR_ Scope 002031D0 00
+ * 0 _SB_ Device 00203240 00 Notify Object: 0020ADD8
+ * 0 _SI_ Scope 002032B0 00
+ * 0 _TZ_ Device 00203320 00
+ * 0 _REV Integer 00203390 00 = 0000000000000002
+ * 0 _OS_ String 00203488 00 Len 14 "Microsoft Windows NT"
+ * 0 _GL_ Mutex 00203580 00 Object 002035F0
+ * 0 _OSI Method 00203678 00 Args 1 Len 0000 Aml 00000000
+ */
+ for (init_val = acpi_gbl_pre_defined_names; init_val->name; init_val++) {
+ status = AE_OK;
+
+ /* _OSI is optional for now, will be permanent later */
+
+ if (!strcmp(init_val->name, "_OSI")
+ && !acpi_gbl_create_osi_method) {
+ continue;
+ }
+
+ /*
+ * Create, init, and link the new predefined name
+ * Note: No need to use acpi_ns_lookup here because all the
+ * predefined names are at the root level. It is much easier to
+ * just create and link the new node(s) here.
+ */
+ new_node =
+ acpi_ns_create_node(*ACPI_CAST_PTR(u32, init_val->name));
+ if (!new_node) {
+ status = AE_NO_MEMORY;
+ goto unlock_and_exit;
+ }
+
+ new_node->descriptor_type = ACPI_DESC_TYPE_NAMED;
+ new_node->type = init_val->type;
+
+ if (!prev_node) {
+ acpi_gbl_root_node_struct.child = new_node;
+ } else {
+ prev_node->peer = new_node;
+ }
+
+ new_node->parent = &acpi_gbl_root_node_struct;
+ prev_node = new_node;
+
+ /*
+ * Name entered successfully. If entry in pre_defined_names[] specifies
+ * an initial value, create the initial value.
+ */
+ if (init_val->val) {
+ status = acpi_os_predefined_override(init_val, &val);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO,
+ "Could not override predefined %s",
+ init_val->name));
+ }
+
+ if (!val) {
+ val = init_val->val;
+ }
+
+ /*
+ * Entry requests an initial value, allocate a
+ * descriptor for it.
+ */
+ obj_desc =
+ acpi_ut_create_internal_object(init_val->type);
+ if (!obj_desc) {
+ status = AE_NO_MEMORY;
+ goto unlock_and_exit;
+ }
+
+ /*
+ * Convert value string from table entry to
+ * internal representation. Only types actually
+ * used for initial values are implemented here.
+ */
+ switch (init_val->type) {
+ case ACPI_TYPE_METHOD:
+
+ obj_desc->method.param_count =
+ (u8) ACPI_TO_INTEGER(val);
+ obj_desc->common.flags |= AOPOBJ_DATA_VALID;
+
+#if defined (ACPI_ASL_COMPILER)
+
+ /* Save the parameter count for the iASL compiler */
+
+ new_node->value = obj_desc->method.param_count;
+#else
+ /* Mark this as a very SPECIAL method (_OSI) */
+
+ obj_desc->method.info_flags =
+ ACPI_METHOD_INTERNAL_ONLY;
+ obj_desc->method.dispatch.implementation =
+ acpi_ut_osi_implementation;
+#endif
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ obj_desc->integer.value = ACPI_TO_INTEGER(val);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ /* Build an object around the static string */
+
+ obj_desc->string.length = (u32)strlen(val);
+ obj_desc->string.pointer = val;
+ obj_desc->common.flags |= AOPOBJ_STATIC_POINTER;
+ break;
+
+ case ACPI_TYPE_MUTEX:
+
+ obj_desc->mutex.node = new_node;
+ obj_desc->mutex.sync_level =
+ (u8) (ACPI_TO_INTEGER(val) - 1);
+
+ /* Create a mutex */
+
+ status =
+ acpi_os_create_mutex(&obj_desc->mutex.
+ os_mutex);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(obj_desc);
+ goto unlock_and_exit;
+ }
+
+ /* Special case for ACPI Global Lock */
+
+ if (strcmp(init_val->name, "_GL_") == 0) {
+ acpi_gbl_global_lock_mutex = obj_desc;
+
+ /* Create additional counting semaphore for global lock */
+
+ status =
+ acpi_os_create_semaphore(1, 0,
+ &acpi_gbl_global_lock_semaphore);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference
+ (obj_desc);
+ goto unlock_and_exit;
+ }
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Unsupported initial type value 0x%X",
+ init_val->type));
+ acpi_ut_remove_reference(obj_desc);
+ obj_desc = NULL;
+ continue;
+ }
+
+ /* Store pointer to value descriptor in the Node */
+
+ status = acpi_ns_attach_object(new_node, obj_desc,
+ obj_desc->common.type);
+
+ /* Remove local reference to the object */
+
+ acpi_ut_remove_reference(obj_desc);
+ }
+ }
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+ /* Save a handle to "_GPE", it is always present */
+
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_ns_get_node(NULL, "\\_GPE", ACPI_NS_NO_UPSEARCH,
+ &acpi_gbl_fadt_gpe_device);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_lookup
+ *
+ * PARAMETERS: scope_info - Current scope info block
+ * pathname - Search pathname, in internal format
+ * (as represented in the AML stream)
+ * type - Type associated with name
+ * interpreter_mode - IMODE_LOAD_PASS2 => add name if not found
+ * flags - Flags describing the search restrictions
+ * walk_state - Current state of the walk
+ * return_node - Where the Node is placed (if found
+ * or created successfully)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Find or enter the passed name in the name space.
+ * Log an error if name not found in Exec mode.
+ *
+ * MUTEX: Assumes namespace is locked.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_lookup(union acpi_generic_state *scope_info,
+ char *pathname,
+ acpi_object_type type,
+ acpi_interpreter_mode interpreter_mode,
+ u32 flags,
+ struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node **return_node)
+{
+ acpi_status status;
+ char *path = pathname;
+ char *external_path;
+ struct acpi_namespace_node *prefix_node;
+ struct acpi_namespace_node *current_node = NULL;
+ struct acpi_namespace_node *this_node = NULL;
+ u32 num_segments;
+ u32 num_carats;
+ acpi_name simple_name;
+ acpi_object_type type_to_check_for;
+ acpi_object_type this_search_type;
+ u32 search_parent_flag = ACPI_NS_SEARCH_PARENT;
+ u32 local_flags;
+ acpi_interpreter_mode local_interpreter_mode;
+
+ ACPI_FUNCTION_TRACE(ns_lookup);
+
+ if (!return_node) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ local_flags = flags &
+ ~(ACPI_NS_ERROR_IF_FOUND | ACPI_NS_OVERRIDE_IF_FOUND |
+ ACPI_NS_SEARCH_PARENT);
+ *return_node = ACPI_ENTRY_NOT_FOUND;
+ acpi_gbl_ns_lookup_count++;
+
+ if (!acpi_gbl_root_node) {
+ return_ACPI_STATUS(AE_NO_NAMESPACE);
+ }
+
+ /* Get the prefix scope. A null scope means use the root scope */
+
+ if ((!scope_info) || (!scope_info->scope.node)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Null scope prefix, using root node (%p)\n",
+ acpi_gbl_root_node));
+
+ prefix_node = acpi_gbl_root_node;
+ } else {
+ prefix_node = scope_info->scope.node;
+ if (ACPI_GET_DESCRIPTOR_TYPE(prefix_node) !=
+ ACPI_DESC_TYPE_NAMED) {
+ ACPI_ERROR((AE_INFO, "%p is not a namespace node [%s]",
+ prefix_node,
+ acpi_ut_get_descriptor_name(prefix_node)));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ if (!(flags & ACPI_NS_PREFIX_IS_SCOPE)) {
+ /*
+ * This node might not be a actual "scope" node (such as a
+ * Device/Method, etc.) It could be a Package or other object
+ * node. Backup up the tree to find the containing scope node.
+ */
+ while (!acpi_ns_opens_scope(prefix_node->type) &&
+ prefix_node->type != ACPI_TYPE_ANY) {
+ prefix_node = prefix_node->parent;
+ }
+ }
+ }
+
+ /* Save type. TBD: may be no longer necessary */
+
+ type_to_check_for = type;
+
+ /*
+ * Begin examination of the actual pathname
+ */
+ if (!pathname) {
+
+ /* A Null name_path is allowed and refers to the root */
+
+ num_segments = 0;
+ this_node = acpi_gbl_root_node;
+ path = "";
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Null Pathname (Zero segments), Flags=%X\n",
+ flags));
+ } else {
+ /*
+ * Name pointer is valid (and must be in internal name format)
+ *
+ * Check for scope prefixes:
+ *
+ * As represented in the AML stream, a namepath consists of an
+ * optional scope prefix followed by a name segment part.
+ *
+ * If present, the scope prefix is either a Root Prefix (in
+ * which case the name is fully qualified), or one or more
+ * Parent Prefixes (in which case the name's scope is relative
+ * to the current scope).
+ */
+ if (*path == (u8) AML_ROOT_PREFIX) {
+
+ /* Pathname is fully qualified, start from the root */
+
+ this_node = acpi_gbl_root_node;
+ search_parent_flag = ACPI_NS_NO_UPSEARCH;
+
+ /* Point to name segment part */
+
+ path++;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Path is absolute from root [%p]\n",
+ this_node));
+ } else {
+ /* Pathname is relative to current scope, start there */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Searching relative to prefix scope [%4.4s] (%p)\n",
+ acpi_ut_get_node_name(prefix_node),
+ prefix_node));
+
+ /*
+ * Handle multiple Parent Prefixes (carat) by just getting
+ * the parent node for each prefix instance.
+ */
+ this_node = prefix_node;
+ num_carats = 0;
+ while (*path == (u8) AML_PARENT_PREFIX) {
+
+ /* Name is fully qualified, no search rules apply */
+
+ search_parent_flag = ACPI_NS_NO_UPSEARCH;
+
+ /*
+ * Point past this prefix to the name segment
+ * part or the next Parent Prefix
+ */
+ path++;
+
+ /* Backup to the parent node */
+
+ num_carats++;
+ this_node = this_node->parent;
+ if (!this_node) {
+ /*
+ * Current scope has no parent scope. Externalize
+ * the internal path for error message.
+ */
+ status =
+ acpi_ns_externalize_name
+ (ACPI_UINT32_MAX, pathname, NULL,
+ &external_path);
+ if (ACPI_SUCCESS(status)) {
+ ACPI_ERROR((AE_INFO,
+ "%s: Path has too many parent prefixes (^)",
+ external_path));
+
+ ACPI_FREE(external_path);
+ }
+
+ return_ACPI_STATUS(AE_NOT_FOUND);
+ }
+ }
+
+ if (search_parent_flag == ACPI_NS_NO_UPSEARCH) {
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Search scope is [%4.4s], path has %u carat(s)\n",
+ acpi_ut_get_node_name
+ (this_node), num_carats));
+ }
+ }
+
+ /*
+ * Determine the number of ACPI name segments in this pathname.
+ *
+ * The segment part consists of either:
+ * - A Null name segment (0)
+ * - A dual_name_prefix followed by two 4-byte name segments
+ * - A multi_name_prefix followed by a byte indicating the
+ * number of segments and the segments themselves.
+ * - A single 4-byte name segment
+ *
+ * Examine the name prefix opcode, if any, to determine the number of
+ * segments.
+ */
+ switch (*path) {
+ case 0:
+ /*
+ * Null name after a root or parent prefixes. We already
+ * have the correct target node and there are no name segments.
+ */
+ num_segments = 0;
+ type = this_node->type;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Prefix-only Pathname (Zero name segments), Flags=%X\n",
+ flags));
+ break;
+
+ case AML_DUAL_NAME_PREFIX:
+
+ /* More than one name_seg, search rules do not apply */
+
+ search_parent_flag = ACPI_NS_NO_UPSEARCH;
+
+ /* Two segments, point to first name segment */
+
+ num_segments = 2;
+ path++;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Dual Pathname (2 segments, Flags=%X)\n",
+ flags));
+ break;
+
+ case AML_MULTI_NAME_PREFIX:
+
+ /* More than one name_seg, search rules do not apply */
+
+ search_parent_flag = ACPI_NS_NO_UPSEARCH;
+
+ /* Extract segment count, point to first name segment */
+
+ path++;
+ num_segments = (u32) (u8) * path;
+ path++;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Multi Pathname (%u Segments, Flags=%X)\n",
+ num_segments, flags));
+ break;
+
+ default:
+ /*
+ * Not a Null name, no Dual or Multi prefix, hence there is
+ * only one name segment and Pathname is already pointing to it.
+ */
+ num_segments = 1;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Simple Pathname (1 segment, Flags=%X)\n",
+ flags));
+ break;
+ }
+
+ ACPI_DEBUG_EXEC(acpi_ns_print_pathname(num_segments, path));
+ }
+
+ /*
+ * Search namespace for each segment of the name. Loop through and
+ * verify (or add to the namespace) each name segment.
+ *
+ * The object type is significant only at the last name
+ * segment. (We don't care about the types along the path, only
+ * the type of the final target object.)
+ */
+ this_search_type = ACPI_TYPE_ANY;
+ current_node = this_node;
+
+ while (num_segments && current_node) {
+ num_segments--;
+ if (!num_segments) {
+
+ /* This is the last segment, enable typechecking */
+
+ this_search_type = type;
+
+ /*
+ * Only allow automatic parent search (search rules) if the caller
+ * requested it AND we have a single, non-fully-qualified name_seg
+ */
+ if ((search_parent_flag != ACPI_NS_NO_UPSEARCH) &&
+ (flags & ACPI_NS_SEARCH_PARENT)) {
+ local_flags |= ACPI_NS_SEARCH_PARENT;
+ }
+
+ /* Set error flag according to caller */
+
+ if (flags & ACPI_NS_ERROR_IF_FOUND) {
+ local_flags |= ACPI_NS_ERROR_IF_FOUND;
+ }
+
+ /* Set override flag according to caller */
+
+ if (flags & ACPI_NS_OVERRIDE_IF_FOUND) {
+ local_flags |= ACPI_NS_OVERRIDE_IF_FOUND;
+ }
+ }
+
+ /* Handle opcodes that create a new name_seg via a full name_path */
+
+ local_interpreter_mode = interpreter_mode;
+ if ((flags & ACPI_NS_PREFIX_MUST_EXIST) && (num_segments > 0)) {
+
+ /* Every element of the path must exist (except for the final name_seg) */
+
+ local_interpreter_mode = ACPI_IMODE_EXECUTE;
+ }
+
+ /* Extract one ACPI name from the front of the pathname */
+
+ ACPI_MOVE_32_TO_32(&simple_name, path);
+
+ /* Try to find the single (4 character) ACPI name */
+
+ status =
+ acpi_ns_search_and_enter(simple_name, walk_state,
+ current_node,
+ local_interpreter_mode,
+ this_search_type, local_flags,
+ &this_node);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_NOT_FOUND) {
+#if !defined ACPI_ASL_COMPILER /* Note: iASL reports this error by itself, not needed here */
+ if (flags & ACPI_NS_PREFIX_MUST_EXIST) {
+ acpi_os_printf(ACPI_MSG_BIOS_ERROR
+ "Object does not exist: %4.4s\n",
+ (char *)&simple_name);
+ }
+#endif
+ /* Name not found in ACPI namespace */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Name [%4.4s] not found in scope [%4.4s] %p\n",
+ (char *)&simple_name,
+ (char *)&current_node->name,
+ current_node));
+ }
+#ifdef ACPI_EXEC_APP
+ if ((status == AE_ALREADY_EXISTS) &&
+ (this_node->flags & ANOBJ_NODE_EARLY_INIT)) {
+ this_node->flags &= ~ANOBJ_NODE_EARLY_INIT;
+ status = AE_OK;
+ }
+#endif
+
+#ifdef ACPI_ASL_COMPILER
+ /*
+ * If this ACPI name already exists within the namespace as an
+ * external declaration, then mark the external as a conflicting
+ * declaration and proceed to process the current node as if it did
+ * not exist in the namespace. If this node is not processed as
+ * normal, then it could cause improper namespace resolution
+ * by failing to open a new scope.
+ */
+ if (acpi_gbl_disasm_flag &&
+ (status == AE_ALREADY_EXISTS) &&
+ ((this_node->flags & ANOBJ_IS_EXTERNAL) ||
+ (walk_state
+ && walk_state->opcode == AML_EXTERNAL_OP))) {
+ this_node->flags &= ~ANOBJ_IS_EXTERNAL;
+ this_node->type = (u8)this_search_type;
+ if (walk_state->opcode != AML_EXTERNAL_OP) {
+ acpi_dm_mark_external_conflict
+ (this_node);
+ }
+ break;
+ }
+#endif
+
+ *return_node = this_node;
+ return_ACPI_STATUS(status);
+ }
+
+ /* More segments to follow? */
+
+ if (num_segments > 0) {
+ /*
+ * If we have an alias to an object that opens a scope (such as a
+ * device or processor), we need to dereference the alias here so
+ * that we can access any children of the original node (via the
+ * remaining segments).
+ */
+ if (this_node->type == ACPI_TYPE_LOCAL_ALIAS) {
+ if (!this_node->object) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ if (acpi_ns_opens_scope
+ (((struct acpi_namespace_node *)
+ this_node->object)->type)) {
+ this_node =
+ (struct acpi_namespace_node *)
+ this_node->object;
+ }
+ }
+ }
+
+ /* Special handling for the last segment (num_segments == 0) */
+
+ else {
+ /*
+ * Sanity typecheck of the target object:
+ *
+ * If 1) This is the last segment (num_segments == 0)
+ * 2) And we are looking for a specific type
+ * (Not checking for TYPE_ANY)
+ * 3) Which is not an alias
+ * 4) Which is not a local type (TYPE_SCOPE)
+ * 5) And the type of target object is known (not TYPE_ANY)
+ * 6) And target object does not match what we are looking for
+ *
+ * Then we have a type mismatch. Just warn and ignore it.
+ */
+ if ((type_to_check_for != ACPI_TYPE_ANY) &&
+ (type_to_check_for != ACPI_TYPE_LOCAL_ALIAS) &&
+ (type_to_check_for != ACPI_TYPE_LOCAL_METHOD_ALIAS)
+ && (type_to_check_for != ACPI_TYPE_LOCAL_SCOPE)
+ && (this_node->type != ACPI_TYPE_ANY)
+ && (this_node->type != type_to_check_for)) {
+
+ /* Complain about a type mismatch */
+
+ ACPI_WARNING((AE_INFO,
+ "NsLookup: Type mismatch on %4.4s (%s), searching for (%s)",
+ ACPI_CAST_PTR(char, &simple_name),
+ acpi_ut_get_type_name(this_node->
+ type),
+ acpi_ut_get_type_name
+ (type_to_check_for)));
+ }
+
+ /*
+ * If this is the last name segment and we are not looking for a
+ * specific type, but the type of found object is known, use that
+ * type to (later) see if it opens a scope.
+ */
+ if (type == ACPI_TYPE_ANY) {
+ type = this_node->type;
+ }
+ }
+
+ /* Point to next name segment and make this node current */
+
+ path += ACPI_NAMESEG_SIZE;
+ current_node = this_node;
+ }
+
+ /* Always check if we need to open a new scope */
+
+ if (!(flags & ACPI_NS_DONT_OPEN_SCOPE) && (walk_state)) {
+ /*
+ * If entry is a type which opens a scope, push the new scope on the
+ * scope stack.
+ */
+ if (acpi_ns_opens_scope(type)) {
+ status =
+ acpi_ds_scope_stack_push(this_node, type,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+ }
+#ifdef ACPI_EXEC_APP
+ if (flags & ACPI_NS_EARLY_INIT) {
+ this_node->flags |= ANOBJ_NODE_EARLY_INIT;
+ }
+#endif
+
+ *return_node = this_node;
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c
new file mode 100644
index 000000000..83d26abcf
--- /dev/null
+++ b/drivers/acpi/acpica/nsalloc.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: nsalloc - Namespace allocation and deletion utilities
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsalloc")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_create_node
+ *
+ * PARAMETERS: name - Name of the new node (4 char ACPI name)
+ *
+ * RETURN: New namespace node (Null on failure)
+ *
+ * DESCRIPTION: Create a namespace node
+ *
+ ******************************************************************************/
+struct acpi_namespace_node *acpi_ns_create_node(u32 name)
+{
+ struct acpi_namespace_node *node;
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ u32 temp;
+#endif
+
+ ACPI_FUNCTION_TRACE(ns_create_node);
+
+ node = acpi_os_acquire_object(acpi_gbl_namespace_cache);
+ if (!node) {
+ return_PTR(NULL);
+ }
+
+ ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_allocated++);
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ temp = acpi_gbl_ns_node_list->total_allocated -
+ acpi_gbl_ns_node_list->total_freed;
+ if (temp > acpi_gbl_ns_node_list->max_occupied) {
+ acpi_gbl_ns_node_list->max_occupied = temp;
+ }
+#endif
+
+ node->name.integer = name;
+ ACPI_SET_DESCRIPTOR_TYPE(node, ACPI_DESC_TYPE_NAMED);
+ return_PTR(node);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_delete_node
+ *
+ * PARAMETERS: node - Node to be deleted
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete a namespace node. All node deletions must come through
+ * here. Detaches any attached objects, including any attached
+ * data. If a handler is associated with attached data, it is
+ * invoked before the node is deleted.
+ *
+ ******************************************************************************/
+
+void acpi_ns_delete_node(struct acpi_namespace_node *node)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *next_desc;
+
+ ACPI_FUNCTION_NAME(ns_delete_node);
+
+ if (!node) {
+ return_VOID;
+ }
+
+ /* Detach an object if there is one */
+
+ acpi_ns_detach_object(node);
+
+ /*
+ * Delete an attached data object list if present (objects that were
+ * attached via acpi_attach_data). Note: After any normal object is
+ * detached above, the only possible remaining object(s) are data
+ * objects, in a linked list.
+ */
+ obj_desc = node->object;
+ while (obj_desc && (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA)) {
+
+ /* Invoke the attached data deletion handler if present */
+
+ if (obj_desc->data.handler) {
+ obj_desc->data.handler(node, obj_desc->data.pointer);
+ }
+
+ next_desc = obj_desc->common.next_object;
+ acpi_ut_remove_reference(obj_desc);
+ obj_desc = next_desc;
+ }
+
+ /* Special case for the statically allocated root node */
+
+ if (node == acpi_gbl_root_node) {
+ return;
+ }
+
+ /* Now we can delete the node */
+
+ (void)acpi_os_release_object(acpi_gbl_namespace_cache, node);
+
+ ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_freed++);
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Node %p, Remaining %X\n",
+ node, acpi_gbl_current_node_count));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_remove_node
+ *
+ * PARAMETERS: node - Node to be removed/deleted
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Remove (unlink) and delete a namespace node
+ *
+ ******************************************************************************/
+
+void acpi_ns_remove_node(struct acpi_namespace_node *node)
+{
+ struct acpi_namespace_node *parent_node;
+ struct acpi_namespace_node *prev_node;
+ struct acpi_namespace_node *next_node;
+
+ ACPI_FUNCTION_TRACE_PTR(ns_remove_node, node);
+
+ parent_node = node->parent;
+
+ prev_node = NULL;
+ next_node = parent_node->child;
+
+ /* Find the node that is the previous peer in the parent's child list */
+
+ while (next_node != node) {
+ prev_node = next_node;
+ next_node = next_node->peer;
+ }
+
+ if (prev_node) {
+
+ /* Node is not first child, unlink it */
+
+ prev_node->peer = node->peer;
+ } else {
+ /*
+ * Node is first child (has no previous peer).
+ * Link peer list to parent
+ */
+ parent_node->child = node->peer;
+ }
+
+ /* Delete the node and any attached objects */
+
+ acpi_ns_delete_node(node);
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_install_node
+ *
+ * PARAMETERS: walk_state - Current state of the walk
+ * parent_node - The parent of the new Node
+ * node - The new Node to install
+ * type - ACPI object type of the new Node
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Initialize a new namespace node and install it amongst
+ * its peers.
+ *
+ * Note: Current namespace lookup is linear search. This appears
+ * to be sufficient as namespace searches consume only a small
+ * fraction of the execution time of the ACPI subsystem.
+ *
+ ******************************************************************************/
+
+void acpi_ns_install_node(struct acpi_walk_state *walk_state, struct acpi_namespace_node *parent_node, /* Parent */
+ struct acpi_namespace_node *node, /* New Child */
+ acpi_object_type type)
+{
+ acpi_owner_id owner_id = 0;
+ struct acpi_namespace_node *child_node;
+
+ ACPI_FUNCTION_TRACE(ns_install_node);
+
+ if (walk_state) {
+ /*
+ * Get the owner ID from the Walk state. The owner ID is used to
+ * track table deletion and deletion of objects created by methods.
+ */
+ owner_id = walk_state->owner_id;
+
+ if ((walk_state->method_desc) &&
+ (parent_node != walk_state->method_node)) {
+ /*
+ * A method is creating a new node that is not a child of the
+ * method (it is non-local). Mark the executing method as having
+ * modified the namespace. This is used for cleanup when the
+ * method exits.
+ */
+ walk_state->method_desc->method.info_flags |=
+ ACPI_METHOD_MODIFIED_NAMESPACE;
+ }
+ }
+
+ /* Link the new entry into the parent and existing children */
+
+ node->peer = NULL;
+ node->parent = parent_node;
+ child_node = parent_node->child;
+
+ if (!child_node) {
+ parent_node->child = node;
+ } else {
+ /* Add node to the end of the peer list */
+
+ while (child_node->peer) {
+ child_node = child_node->peer;
+ }
+
+ child_node->peer = node;
+ }
+
+ /* Init the new entry */
+
+ node->owner_id = owner_id;
+ node->type = (u8) type;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "%4.4s (%s) [Node %p Owner %3.3X] added to %4.4s (%s) [Node %p]\n",
+ acpi_ut_get_node_name(node),
+ acpi_ut_get_type_name(node->type), node, owner_id,
+ acpi_ut_get_node_name(parent_node),
+ acpi_ut_get_type_name(parent_node->type),
+ parent_node));
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_delete_children
+ *
+ * PARAMETERS: parent_node - Delete this objects children
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Delete all children of the parent object. In other words,
+ * deletes a "scope".
+ *
+ ******************************************************************************/
+
+void acpi_ns_delete_children(struct acpi_namespace_node *parent_node)
+{
+ struct acpi_namespace_node *next_node;
+ struct acpi_namespace_node *node_to_delete;
+
+ ACPI_FUNCTION_TRACE_PTR(ns_delete_children, parent_node);
+
+ if (!parent_node) {
+ return_VOID;
+ }
+
+ /* Deallocate all children at this level */
+
+ next_node = parent_node->child;
+ while (next_node) {
+
+ /* Grandchildren should have all been deleted already */
+
+ if (next_node->child) {
+ ACPI_ERROR((AE_INFO, "Found a grandchild! P=%p C=%p",
+ parent_node, next_node));
+ }
+
+ /*
+ * Delete this child node and move on to the next child in the list.
+ * No need to unlink the node since we are deleting the entire branch.
+ */
+ node_to_delete = next_node;
+ next_node = next_node->peer;
+ acpi_ns_delete_node(node_to_delete);
+ }
+
+ /* Clear the parent's child pointer */
+
+ parent_node->child = NULL;
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_delete_namespace_subtree
+ *
+ * PARAMETERS: parent_node - Root of the subtree to be deleted
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Delete a subtree of the namespace. This includes all objects
+ * stored within the subtree.
+ *
+ ******************************************************************************/
+
+void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node)
+{
+ struct acpi_namespace_node *child_node = NULL;
+ u32 level = 1;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ns_delete_namespace_subtree);
+
+ if (!parent_node) {
+ return_VOID;
+ }
+
+ /* Lock namespace for possible update */
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_VOID;
+ }
+
+ /*
+ * Traverse the tree of objects until we bubble back up
+ * to where we started.
+ */
+ while (level > 0) {
+
+ /* Get the next node in this scope (NULL if none) */
+
+ child_node = acpi_ns_get_next_node(parent_node, child_node);
+ if (child_node) {
+
+ /* Found a child node - detach any attached object */
+
+ acpi_ns_detach_object(child_node);
+
+ /* Check if this node has any children */
+
+ if (child_node->child) {
+ /*
+ * There is at least one child of this node,
+ * visit the node
+ */
+ level++;
+ parent_node = child_node;
+ child_node = NULL;
+ }
+ } else {
+ /*
+ * No more children of this parent node.
+ * Move up to the grandparent.
+ */
+ level--;
+
+ /*
+ * Now delete all of the children of this parent
+ * all at the same time.
+ */
+ acpi_ns_delete_children(parent_node);
+
+ /* New "last child" is this parent node */
+
+ child_node = parent_node;
+
+ /* Move up the tree to the grandparent */
+
+ parent_node = parent_node->parent;
+ }
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_delete_namespace_by_owner
+ *
+ * PARAMETERS: owner_id - All nodes with this owner will be deleted
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Delete entries within the namespace that are owned by a
+ * specific ID. Used to delete entire ACPI tables. All
+ * reference counts are updated.
+ *
+ * MUTEX: Locks namespace during deletion walk.
+ *
+ ******************************************************************************/
+
+void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id)
+{
+ struct acpi_namespace_node *child_node;
+ struct acpi_namespace_node *deletion_node;
+ struct acpi_namespace_node *parent_node;
+ u32 level;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_U32(ns_delete_namespace_by_owner, owner_id);
+
+ if (owner_id == 0) {
+ return_VOID;
+ }
+
+ /* Lock namespace for possible update */
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_VOID;
+ }
+
+ deletion_node = NULL;
+ parent_node = acpi_gbl_root_node;
+ child_node = NULL;
+ level = 1;
+
+ /*
+ * Traverse the tree of nodes until we bubble back up
+ * to where we started.
+ */
+ while (level > 0) {
+ /*
+ * Get the next child of this parent node. When child_node is NULL,
+ * the first child of the parent is returned
+ */
+ child_node = acpi_ns_get_next_node(parent_node, child_node);
+
+ if (deletion_node) {
+ acpi_ns_delete_children(deletion_node);
+ acpi_ns_remove_node(deletion_node);
+ deletion_node = NULL;
+ }
+
+ if (child_node) {
+ if (child_node->owner_id == owner_id) {
+
+ /* Found a matching child node - detach any attached object */
+
+ acpi_ns_detach_object(child_node);
+ }
+
+ /* Check if this node has any children */
+
+ if (child_node->child) {
+ /*
+ * There is at least one child of this node,
+ * visit the node
+ */
+ level++;
+ parent_node = child_node;
+ child_node = NULL;
+ } else if (child_node->owner_id == owner_id) {
+ deletion_node = child_node;
+ }
+ } else {
+ /*
+ * No more children of this parent node.
+ * Move up to the grandparent.
+ */
+ level--;
+ if (level != 0) {
+ if (parent_node->owner_id == owner_id) {
+ deletion_node = parent_node;
+ }
+ }
+
+ /* New "last child" is this parent node */
+
+ child_node = parent_node;
+
+ /* Move up the tree to the grandparent */
+
+ parent_node = parent_node->parent;
+ }
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/nsarguments.c b/drivers/acpi/acpica/nsarguments.c
new file mode 100644
index 000000000..22586b90e
--- /dev/null
+++ b/drivers/acpi/acpica/nsarguments.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsarguments - Validation of args for ACPI predefined methods
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acpredef.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsarguments")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_argument_types
+ *
+ * PARAMETERS: info - Method execution information block
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Check the incoming argument count and all argument types
+ * against the argument type list for a predefined name.
+ *
+ ******************************************************************************/
+void acpi_ns_check_argument_types(struct acpi_evaluate_info *info)
+{
+ u16 arg_type_list;
+ u8 arg_count;
+ u8 arg_type;
+ u8 user_arg_type;
+ u32 i;
+
+ /*
+ * If not a predefined name, cannot typecheck args, because
+ * we have no idea what argument types are expected.
+ * Also, ignore typecheck if warnings/errors if this method
+ * has already been evaluated at least once -- in order
+ * to suppress repetitive messages.
+ */
+ if (!info->predefined || (info->node->flags & ANOBJ_EVALUATED)) {
+ return;
+ }
+
+ arg_type_list = info->predefined->info.argument_list;
+ arg_count = METHOD_GET_ARG_COUNT(arg_type_list);
+
+ /* Typecheck all arguments */
+
+ for (i = 0; ((i < arg_count) && (i < info->param_count)); i++) {
+ arg_type = METHOD_GET_NEXT_TYPE(arg_type_list);
+ user_arg_type = info->parameters[i]->common.type;
+
+ /* No typechecking for ACPI_TYPE_ANY */
+
+ if ((user_arg_type != arg_type) && (arg_type != ACPI_TYPE_ANY)) {
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ ACPI_WARN_ALWAYS,
+ "Argument #%u type mismatch - "
+ "Found [%s], ACPI requires [%s]",
+ (i + 1),
+ acpi_ut_get_type_name
+ (user_arg_type),
+ acpi_ut_get_type_name(arg_type)));
+
+ /* Prevent any additional typechecking for this method */
+
+ info->node->flags |= ANOBJ_EVALUATED;
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_acpi_compliance
+ *
+ * PARAMETERS: pathname - Full pathname to the node (for error msgs)
+ * node - Namespace node for the method/object
+ * predefined - Pointer to entry in predefined name table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Check that the declared parameter count (in ASL/AML) for a
+ * predefined name is what is expected (matches what is defined in
+ * the ACPI specification for this predefined name.)
+ *
+ ******************************************************************************/
+
+void
+acpi_ns_check_acpi_compliance(char *pathname,
+ struct acpi_namespace_node *node,
+ const union acpi_predefined_info *predefined)
+{
+ u32 aml_param_count;
+ u32 required_param_count;
+
+ if (!predefined || (node->flags & ANOBJ_EVALUATED)) {
+ return;
+ }
+
+ /* Get the ACPI-required arg count from the predefined info table */
+
+ required_param_count =
+ METHOD_GET_ARG_COUNT(predefined->info.argument_list);
+
+ /*
+ * If this object is not a control method, we can check if the ACPI
+ * spec requires that it be a method.
+ */
+ if (node->type != ACPI_TYPE_METHOD) {
+ if (required_param_count > 0) {
+
+ /* Object requires args, must be implemented as a method */
+
+ ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname,
+ ACPI_WARN_ALWAYS,
+ "Object (%s) must be a control method with %u arguments",
+ acpi_ut_get_type_name(node->
+ type),
+ required_param_count));
+ } else if (!required_param_count
+ && !predefined->info.expected_btypes) {
+
+ /* Object requires no args and no return value, must be a method */
+
+ ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname,
+ ACPI_WARN_ALWAYS,
+ "Object (%s) must be a control method "
+ "with no arguments and no return value",
+ acpi_ut_get_type_name(node->
+ type)));
+ }
+
+ return;
+ }
+
+ /*
+ * This is a control method.
+ * Check that the ASL/AML-defined parameter count for this method
+ * matches the ACPI-required parameter count
+ *
+ * Some methods are allowed to have a "minimum" number of args (_SCP)
+ * because their definition in ACPI has changed over time.
+ *
+ * Note: These are BIOS errors in the declaration of the object
+ */
+ aml_param_count = node->object->method.param_count;
+
+ if (aml_param_count < required_param_count) {
+ ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS,
+ "Insufficient arguments - "
+ "ASL declared %u, ACPI requires %u",
+ aml_param_count,
+ required_param_count));
+ } else if ((aml_param_count > required_param_count)
+ && !(predefined->info.
+ argument_list & ARG_COUNT_IS_MINIMUM)) {
+ ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS,
+ "Excess arguments - "
+ "ASL declared %u, ACPI requires %u",
+ aml_param_count,
+ required_param_count));
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_argument_count
+ *
+ * PARAMETERS: pathname - Full pathname to the node (for error msgs)
+ * node - Namespace node for the method/object
+ * user_param_count - Number of args passed in by the caller
+ * predefined - Pointer to entry in predefined name table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Check that incoming argument count matches the declared
+ * parameter count (in the ASL/AML) for an object.
+ *
+ ******************************************************************************/
+
+void
+acpi_ns_check_argument_count(char *pathname,
+ struct acpi_namespace_node *node,
+ u32 user_param_count,
+ const union acpi_predefined_info *predefined)
+{
+ u32 aml_param_count;
+ u32 required_param_count;
+
+ if (node->flags & ANOBJ_EVALUATED) {
+ return;
+ }
+
+ if (!predefined) {
+ /*
+ * Not a predefined name. Check the incoming user argument count
+ * against the count that is specified in the method/object.
+ */
+ if (node->type != ACPI_TYPE_METHOD) {
+ if (user_param_count) {
+ ACPI_INFO_PREDEFINED((AE_INFO, pathname,
+ ACPI_WARN_ALWAYS,
+ "%u arguments were passed to a non-method ACPI object (%s)",
+ user_param_count,
+ acpi_ut_get_type_name
+ (node->type)));
+ }
+
+ return;
+ }
+
+ /*
+ * This is a control method. Check the parameter count.
+ * We can only check the incoming argument count against the
+ * argument count declared for the method in the ASL/AML.
+ *
+ * Emit a message if too few or too many arguments have been passed
+ * by the caller.
+ *
+ * Note: Too many arguments will not cause the method to
+ * fail. However, the method will fail if there are too few
+ * arguments and the method attempts to use one of the missing ones.
+ */
+ aml_param_count = node->object->method.param_count;
+
+ if (user_param_count < aml_param_count) {
+ ACPI_WARN_PREDEFINED((AE_INFO, pathname,
+ ACPI_WARN_ALWAYS,
+ "Insufficient arguments - "
+ "Caller passed %u, method requires %u",
+ user_param_count,
+ aml_param_count));
+ } else if (user_param_count > aml_param_count) {
+ ACPI_INFO_PREDEFINED((AE_INFO, pathname,
+ ACPI_WARN_ALWAYS,
+ "Excess arguments - "
+ "Caller passed %u, method requires %u",
+ user_param_count,
+ aml_param_count));
+ }
+
+ return;
+ }
+
+ /*
+ * This is a predefined name. Validate the user-supplied parameter
+ * count against the ACPI specification. We don't validate against
+ * the method itself because what is important here is that the
+ * caller is in conformance with the spec. (The arg count for the
+ * method was checked against the ACPI spec earlier.)
+ *
+ * Some methods are allowed to have a "minimum" number of args (_SCP)
+ * because their definition in ACPI has changed over time.
+ */
+ required_param_count =
+ METHOD_GET_ARG_COUNT(predefined->info.argument_list);
+
+ if (user_param_count < required_param_count) {
+ ACPI_WARN_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS,
+ "Insufficient arguments - "
+ "Caller passed %u, ACPI requires %u",
+ user_param_count, required_param_count));
+ } else if ((user_param_count > required_param_count) &&
+ !(predefined->info.argument_list & ARG_COUNT_IS_MINIMUM)) {
+ ACPI_INFO_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS,
+ "Excess arguments - "
+ "Caller passed %u, ACPI requires %u",
+ user_param_count, required_param_count));
+ }
+}
diff --git a/drivers/acpi/acpica/nsconvert.c b/drivers/acpi/acpica/nsconvert.c
new file mode 100644
index 000000000..b02555fe3
--- /dev/null
+++ b/drivers/acpi/acpica/nsconvert.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsconvert - Object conversions for objects returned by
+ * predefined methods
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+#include "acpredef.h"
+#include "amlresrc.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsconvert")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_integer
+ *
+ * PARAMETERS: original_object - Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a String/Buffer object to an Integer.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ns_convert_to_integer(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object;
+ acpi_status status;
+ u64 value = 0;
+ u32 i;
+
+ switch (original_object->common.type) {
+ case ACPI_TYPE_STRING:
+
+ /* String-to-Integer conversion */
+
+ status =
+ acpi_ut_strtoul64(original_object->string.pointer, &value);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ /* Buffer-to-Integer conversion. Max buffer size is 64 bits. */
+
+ if (original_object->buffer.length > 8) {
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /* Extract each buffer byte to create the integer */
+
+ for (i = 0; i < original_object->buffer.length; i++) {
+ value |= ((u64)
+ original_object->buffer.pointer[i] << (i *
+ 8));
+ }
+ break;
+
+ default:
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ new_object = acpi_ut_create_integer_object(value);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ *return_object = new_object;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_string
+ *
+ * PARAMETERS: original_object - Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a Integer/Buffer object to a String.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_convert_to_string(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object;
+ acpi_size length;
+ acpi_status status;
+
+ switch (original_object->common.type) {
+ case ACPI_TYPE_INTEGER:
+ /*
+ * Integer-to-String conversion. Commonly, convert
+ * an integer of value 0 to a NULL string. The last element of
+ * _BIF and _BIX packages occasionally need this fix.
+ */
+ if (original_object->integer.value == 0) {
+
+ /* Allocate a new NULL string object */
+
+ new_object = acpi_ut_create_string_object(0);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
+ } else {
+ status = acpi_ex_convert_to_string(original_object,
+ &new_object,
+ ACPI_IMPLICIT_CONVERT_HEX);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+ /*
+ * Buffer-to-String conversion. Use a to_string
+ * conversion, no transform performed on the buffer data. The best
+ * example of this is the _BIF method, where the string data from
+ * the battery is often (incorrectly) returned as buffer object(s).
+ */
+ length = 0;
+ while ((length < original_object->buffer.length) &&
+ (original_object->buffer.pointer[length])) {
+ length++;
+ }
+
+ /* Allocate a new string object */
+
+ new_object = acpi_ut_create_string_object(length);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ /*
+ * Copy the raw buffer data with no transform. String is already NULL
+ * terminated at Length+1.
+ */
+ memcpy(new_object->string.pointer,
+ original_object->buffer.pointer, length);
+ break;
+
+ default:
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ *return_object = new_object;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_buffer
+ *
+ * PARAMETERS: original_object - Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a Integer/String/Package object to a Buffer.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object;
+ acpi_status status;
+ union acpi_operand_object **elements;
+ u32 *dword_buffer;
+ u32 count;
+ u32 i;
+
+ switch (original_object->common.type) {
+ case ACPI_TYPE_INTEGER:
+ /*
+ * Integer-to-Buffer conversion.
+ * Convert the Integer to a packed-byte buffer. _MAT and other
+ * objects need this sometimes, if a read has been performed on a
+ * Field object that is less than or equal to the global integer
+ * size (32 or 64 bits).
+ */
+ status =
+ acpi_ex_convert_to_buffer(original_object, &new_object);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ /* String-to-Buffer conversion. Simple data copy */
+
+ new_object = acpi_ut_create_buffer_object
+ (original_object->string.length);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ memcpy(new_object->buffer.pointer,
+ original_object->string.pointer,
+ original_object->string.length);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * This case is often seen for predefined names that must return a
+ * Buffer object with multiple DWORD integers within. For example,
+ * _FDE and _GTM. The Package can be converted to a Buffer.
+ */
+
+ /* All elements of the Package must be integers */
+
+ elements = original_object->package.elements;
+ count = original_object->package.count;
+
+ for (i = 0; i < count; i++) {
+ if ((!*elements) ||
+ ((*elements)->common.type != ACPI_TYPE_INTEGER)) {
+ return (AE_AML_OPERAND_TYPE);
+ }
+ elements++;
+ }
+
+ /* Create the new buffer object to replace the Package */
+
+ new_object = acpi_ut_create_buffer_object(ACPI_MUL_4(count));
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Copy the package elements (integers) to the buffer as DWORDs */
+
+ elements = original_object->package.elements;
+ dword_buffer = ACPI_CAST_PTR(u32, new_object->buffer.pointer);
+
+ for (i = 0; i < count; i++) {
+ *dword_buffer = (u32)(*elements)->integer.value;
+ dword_buffer++;
+ elements++;
+ }
+ break;
+
+ default:
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ *return_object = new_object;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_unicode
+ *
+ * PARAMETERS: scope - Namespace node for the method/object
+ * original_object - ASCII String Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a String object to a Unicode string Buffer.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_convert_to_unicode(struct acpi_namespace_node *scope,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object;
+ char *ascii_string;
+ u16 *unicode_buffer;
+ u32 unicode_length;
+ u32 i;
+
+ if (!original_object) {
+ return (AE_OK);
+ }
+
+ /* If a Buffer was returned, it must be at least two bytes long */
+
+ if (original_object->common.type == ACPI_TYPE_BUFFER) {
+ if (original_object->buffer.length < 2) {
+ return (AE_AML_OPERAND_VALUE);
+ }
+
+ *return_object = NULL;
+ return (AE_OK);
+ }
+
+ /*
+ * The original object is an ASCII string. Convert this string to
+ * a unicode buffer.
+ */
+ ascii_string = original_object->string.pointer;
+ unicode_length = (original_object->string.length * 2) + 2;
+
+ /* Create a new buffer object for the Unicode data */
+
+ new_object = acpi_ut_create_buffer_object(unicode_length);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ unicode_buffer = ACPI_CAST_PTR(u16, new_object->buffer.pointer);
+
+ /* Convert ASCII to Unicode */
+
+ for (i = 0; i < original_object->string.length; i++) {
+ unicode_buffer[i] = (u16)ascii_string[i];
+ }
+
+ *return_object = new_object;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_resource
+ *
+ * PARAMETERS: scope - Namespace node for the method/object
+ * original_object - Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful
+ *
+ * DESCRIPTION: Attempt to convert a Integer object to a resource_template
+ * Buffer.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_convert_to_resource(struct acpi_namespace_node *scope,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object;
+ u8 *buffer;
+
+ /*
+ * We can fix the following cases for an expected resource template:
+ * 1. No return value (interpreter slack mode is disabled)
+ * 2. A "Return (Zero)" statement
+ * 3. A "Return empty buffer" statement
+ *
+ * We will return a buffer containing a single end_tag
+ * resource descriptor.
+ */
+ if (original_object) {
+ switch (original_object->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ /* We can only repair an Integer==0 */
+
+ if (original_object->integer.value) {
+ return (AE_AML_OPERAND_TYPE);
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ if (original_object->buffer.length) {
+
+ /* Additional checks can be added in the future */
+
+ *return_object = NULL;
+ return (AE_OK);
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+ default:
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+ }
+
+ /* Create the new buffer object for the resource descriptor */
+
+ new_object = acpi_ut_create_buffer_object(2);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ buffer = ACPI_CAST_PTR(u8, new_object->buffer.pointer);
+
+ /* Initialize the Buffer with a single end_tag descriptor */
+
+ buffer[0] = (ACPI_RESOURCE_NAME_END_TAG | ASL_RDESC_END_TAG_SIZE);
+ buffer[1] = 0x00;
+
+ *return_object = new_object;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_reference
+ *
+ * PARAMETERS: scope - Namespace node for the method/object
+ * original_object - Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful
+ *
+ * DESCRIPTION: Attempt to convert a Integer object to a object_reference.
+ * Buffer.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_convert_to_reference(struct acpi_namespace_node *scope,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object = NULL;
+ acpi_status status;
+ struct acpi_namespace_node *node;
+ union acpi_generic_state scope_info;
+ char *name;
+
+ ACPI_FUNCTION_NAME(ns_convert_to_reference);
+
+ /* Convert path into internal presentation */
+
+ status =
+ acpi_ns_internalize_name(original_object->string.pointer, &name);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Find the namespace node */
+
+ scope_info.scope.node =
+ ACPI_CAST_PTR(struct acpi_namespace_node, scope);
+ status =
+ acpi_ns_lookup(&scope_info, name, ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE,
+ NULL, &node);
+ if (ACPI_FAILURE(status)) {
+
+ /* Check if we are resolving a named reference within a package */
+
+ ACPI_ERROR_NAMESPACE(&scope_info,
+ original_object->string.pointer, status);
+ goto error_exit;
+ }
+
+ /* Create and init a new internal ACPI object */
+
+ new_object = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE);
+ if (!new_object) {
+ status = AE_NO_MEMORY;
+ goto error_exit;
+ }
+ new_object->reference.node = node;
+ new_object->reference.object = node->object;
+ new_object->reference.class = ACPI_REFCLASS_NAME;
+
+ /*
+ * Increase reference of the object if needed (the object is likely a
+ * null for device nodes).
+ */
+ acpi_ut_add_reference(node->object);
+
+error_exit:
+ ACPI_FREE(name);
+ *return_object = new_object;
+ return (status);
+}
diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c
new file mode 100644
index 000000000..f154824d4
--- /dev/null
+++ b/drivers/acpi/acpica/nsdump.c
@@ -0,0 +1,855 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsdump - table dumping routines for debug
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include <acpi/acoutput.h>
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsdump")
+
+/* Local prototypes */
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+void acpi_ns_dump_root_devices(void);
+
+static acpi_status
+acpi_ns_dump_one_device(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value);
+#endif
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+
+static acpi_status
+acpi_ns_dump_one_object_path(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value);
+
+static acpi_status
+acpi_ns_get_max_depth(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_print_pathname
+ *
+ * PARAMETERS: num_segments - Number of ACPI name segments
+ * pathname - The compressed (internal) path
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print an object's full namespace pathname
+ *
+ ******************************************************************************/
+
+void acpi_ns_print_pathname(u32 num_segments, const char *pathname)
+{
+ u32 i;
+
+ ACPI_FUNCTION_NAME(ns_print_pathname);
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_NAMES, ACPI_NAMESPACE)) {
+ return;
+ }
+
+ /* Print the entire name */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "["));
+
+ while (num_segments) {
+ for (i = 0; i < 4; i++) {
+ isprint((int)pathname[i]) ?
+ acpi_os_printf("%c", pathname[i]) :
+ acpi_os_printf("?");
+ }
+
+ pathname += ACPI_NAMESEG_SIZE;
+ num_segments--;
+ if (num_segments) {
+ acpi_os_printf(".");
+ }
+ }
+
+ acpi_os_printf("]\n");
+}
+
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+/* Not used at this time, perhaps later */
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_dump_pathname
+ *
+ * PARAMETERS: handle - Object
+ * msg - Prefix message
+ * level - Desired debug level
+ * component - Caller's component ID
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print an object's full namespace pathname
+ * Manages allocation/freeing of a pathname buffer
+ *
+ ******************************************************************************/
+
+void
+acpi_ns_dump_pathname(acpi_handle handle,
+ const char *msg, u32 level, u32 component)
+{
+
+ ACPI_FUNCTION_TRACE(ns_dump_pathname);
+
+ /* Do this only if the requested debug level and component are enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(level, component)) {
+ return_VOID;
+ }
+
+ /* Convert handle to a full pathname and print it (with supplied message) */
+
+ acpi_ns_print_node_pathname(handle, msg);
+ acpi_os_printf("\n");
+ return_VOID;
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_dump_one_object
+ *
+ * PARAMETERS: obj_handle - Node to be dumped
+ * level - Nesting level of the handle
+ * context - Passed into walk_namespace
+ * return_value - Not used
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dump a single Node
+ * This procedure is a user_function called by acpi_ns_walk_namespace.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_dump_one_object(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value)
+{
+ struct acpi_walk_info *info = (struct acpi_walk_info *)context;
+ struct acpi_namespace_node *this_node;
+ union acpi_operand_object *obj_desc = NULL;
+ acpi_object_type obj_type;
+ acpi_object_type type;
+ u32 bytes_to_dump;
+ u32 dbg_level;
+ u32 i;
+
+ ACPI_FUNCTION_NAME(ns_dump_one_object);
+
+ /* Is output enabled? */
+
+ if (!(acpi_dbg_level & info->debug_level)) {
+ return (AE_OK);
+ }
+
+ if (!obj_handle) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Null object handle\n"));
+ return (AE_OK);
+ }
+
+ this_node = acpi_ns_validate_handle(obj_handle);
+ if (!this_node) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid object handle %p\n",
+ obj_handle));
+ return (AE_OK);
+ }
+
+ type = this_node->type;
+ info->count++;
+
+ /* Check if the owner matches */
+
+ if ((info->owner_id != ACPI_OWNER_ID_MAX) &&
+ (info->owner_id != this_node->owner_id)) {
+ return (AE_OK);
+ }
+
+ if (!(info->display_type & ACPI_DISPLAY_SHORT)) {
+
+ /* Indent the object according to the level */
+
+ acpi_os_printf("%2d%*s", (u32) level - 1, (int)level * 2, " ");
+
+ /* Check the node type and name */
+
+ if (type > ACPI_TYPE_LOCAL_MAX) {
+ ACPI_WARNING((AE_INFO,
+ "Invalid ACPI Object Type 0x%08X", type));
+ }
+
+ acpi_os_printf("%4.4s", acpi_ut_get_node_name(this_node));
+ }
+
+ /* Now we can print out the pertinent information */
+
+ acpi_os_printf(" %-12s %p %3.3X ",
+ acpi_ut_get_type_name(type), this_node,
+ this_node->owner_id);
+
+ dbg_level = acpi_dbg_level;
+ acpi_dbg_level = 0;
+ obj_desc = acpi_ns_get_attached_object(this_node);
+ acpi_dbg_level = dbg_level;
+
+ /* Temp nodes are those nodes created by a control method */
+
+ if (this_node->flags & ANOBJ_TEMPORARY) {
+ acpi_os_printf("(T) ");
+ }
+
+ switch (info->display_type & ACPI_DISPLAY_MASK) {
+ case ACPI_DISPLAY_SUMMARY:
+
+ if (!obj_desc) {
+
+ /* No attached object. Some types should always have an object */
+
+ switch (type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_PACKAGE:
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_METHOD:
+
+ acpi_os_printf("<No attached object>");
+ break;
+
+ default:
+
+ break;
+ }
+
+ acpi_os_printf("\n");
+ return (AE_OK);
+ }
+
+ switch (type) {
+ case ACPI_TYPE_PROCESSOR:
+
+ acpi_os_printf("ID %02X Len %02X Addr %8.8X%8.8X\n",
+ obj_desc->processor.proc_id,
+ obj_desc->processor.length,
+ ACPI_FORMAT_UINT64(obj_desc->processor.
+ address));
+ break;
+
+ case ACPI_TYPE_DEVICE:
+
+ acpi_os_printf("Notify Object: %p\n", obj_desc);
+ break;
+
+ case ACPI_TYPE_METHOD:
+
+ acpi_os_printf("Args %X Len %.4X Aml %p\n",
+ (u32) obj_desc->method.param_count,
+ obj_desc->method.aml_length,
+ obj_desc->method.aml_start);
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ acpi_os_printf("= %8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64(obj_desc->integer.
+ value));
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ if (obj_desc->common.flags & AOPOBJ_DATA_VALID) {
+ acpi_os_printf("Elements %.2X\n",
+ obj_desc->package.count);
+ } else {
+ acpi_os_printf("[Length not yet evaluated]\n");
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ if (obj_desc->common.flags & AOPOBJ_DATA_VALID) {
+ acpi_os_printf("Len %.2X",
+ obj_desc->buffer.length);
+
+ /* Dump some of the buffer */
+
+ if (obj_desc->buffer.length > 0) {
+ acpi_os_printf(" =");
+ for (i = 0;
+ (i < obj_desc->buffer.length
+ && i < 12); i++) {
+ acpi_os_printf(" %2.2X",
+ obj_desc->buffer.
+ pointer[i]);
+ }
+ }
+ acpi_os_printf("\n");
+ } else {
+ acpi_os_printf("[Length not yet evaluated]\n");
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ acpi_os_printf("Len %.2X ", obj_desc->string.length);
+ acpi_ut_print_string(obj_desc->string.pointer, 80);
+ acpi_os_printf("\n");
+ break;
+
+ case ACPI_TYPE_REGION:
+
+ acpi_os_printf("[%s]",
+ acpi_ut_get_region_name(obj_desc->region.
+ space_id));
+ if (obj_desc->region.flags & AOPOBJ_DATA_VALID) {
+ acpi_os_printf(" Addr %8.8X%8.8X Len %.4X\n",
+ ACPI_FORMAT_UINT64(obj_desc->
+ region.
+ address),
+ obj_desc->region.length);
+ } else {
+ acpi_os_printf
+ (" [Address/Length not yet evaluated]\n");
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ acpi_os_printf("[%s]\n",
+ acpi_ut_get_reference_name(obj_desc));
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+
+ if (obj_desc->buffer_field.buffer_obj &&
+ obj_desc->buffer_field.buffer_obj->buffer.node) {
+ acpi_os_printf("Buf [%4.4s]",
+ acpi_ut_get_node_name(obj_desc->
+ buffer_field.
+ buffer_obj->
+ buffer.
+ node));
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+
+ acpi_os_printf("Rgn [%4.4s]",
+ acpi_ut_get_node_name(obj_desc->
+ common_field.
+ region_obj->region.
+ node));
+ break;
+
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+
+ acpi_os_printf("Rgn [%4.4s] Bnk [%4.4s]",
+ acpi_ut_get_node_name(obj_desc->
+ common_field.
+ region_obj->region.
+ node),
+ acpi_ut_get_node_name(obj_desc->
+ bank_field.
+ bank_obj->
+ common_field.
+ node));
+ break;
+
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
+ acpi_os_printf("Idx [%4.4s] Dat [%4.4s]",
+ acpi_ut_get_node_name(obj_desc->
+ index_field.
+ index_obj->
+ common_field.node),
+ acpi_ut_get_node_name(obj_desc->
+ index_field.
+ data_obj->
+ common_field.
+ node));
+ break;
+
+ case ACPI_TYPE_LOCAL_ALIAS:
+ case ACPI_TYPE_LOCAL_METHOD_ALIAS:
+
+ acpi_os_printf("Target %4.4s (%p)\n",
+ acpi_ut_get_node_name(obj_desc),
+ obj_desc);
+ break;
+
+ default:
+
+ acpi_os_printf("Object %p\n", obj_desc);
+ break;
+ }
+
+ /* Common field handling */
+
+ switch (type) {
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
+ acpi_os_printf(" Off %.3X Len %.2X Acc %.2X\n",
+ (obj_desc->common_field.
+ base_byte_offset * 8)
+ +
+ obj_desc->common_field.
+ start_field_bit_offset,
+ obj_desc->common_field.bit_length,
+ obj_desc->common_field.
+ access_byte_width);
+ break;
+
+ default:
+
+ break;
+ }
+ break;
+
+ case ACPI_DISPLAY_OBJECTS:
+
+ acpi_os_printf("O:%p", obj_desc);
+ if (!obj_desc) {
+
+ /* No attached object, we are done */
+
+ acpi_os_printf("\n");
+ return (AE_OK);
+ }
+
+ acpi_os_printf("(R%u)", obj_desc->common.reference_count);
+
+ switch (type) {
+ case ACPI_TYPE_METHOD:
+
+ /* Name is a Method and its AML offset/length are set */
+
+ acpi_os_printf(" M:%p-%X\n", obj_desc->method.aml_start,
+ obj_desc->method.aml_length);
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ acpi_os_printf(" I:%8.8X8.8%X\n",
+ ACPI_FORMAT_UINT64(obj_desc->integer.
+ value));
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ acpi_os_printf(" S:%p-%X\n", obj_desc->string.pointer,
+ obj_desc->string.length);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ acpi_os_printf(" B:%p-%X\n", obj_desc->buffer.pointer,
+ obj_desc->buffer.length);
+ break;
+
+ default:
+
+ acpi_os_printf("\n");
+ break;
+ }
+ break;
+
+ default:
+ acpi_os_printf("\n");
+ break;
+ }
+
+ /* If debug turned off, done */
+
+ if (!(acpi_dbg_level & ACPI_LV_VALUES)) {
+ return (AE_OK);
+ }
+
+ /* If there is an attached object, display it */
+
+ dbg_level = acpi_dbg_level;
+ acpi_dbg_level = 0;
+ obj_desc = acpi_ns_get_attached_object(this_node);
+ acpi_dbg_level = dbg_level;
+
+ /* Dump attached objects */
+
+ while (obj_desc) {
+ obj_type = ACPI_TYPE_INVALID;
+ acpi_os_printf("Attached Object %p: ", obj_desc);
+
+ /* Decode the type of attached object and dump the contents */
+
+ switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) {
+ case ACPI_DESC_TYPE_NAMED:
+
+ acpi_os_printf("(Ptr to Node)\n");
+ bytes_to_dump = sizeof(struct acpi_namespace_node);
+ ACPI_DUMP_BUFFER(obj_desc, bytes_to_dump);
+ break;
+
+ case ACPI_DESC_TYPE_OPERAND:
+
+ obj_type = obj_desc->common.type;
+
+ if (obj_type > ACPI_TYPE_LOCAL_MAX) {
+ acpi_os_printf
+ ("(Pointer to ACPI Object type %.2X [UNKNOWN])\n",
+ obj_type);
+
+ bytes_to_dump = 32;
+ } else {
+ acpi_os_printf
+ ("(Pointer to ACPI Object type %.2X [%s])\n",
+ obj_type, acpi_ut_get_type_name(obj_type));
+
+ bytes_to_dump =
+ sizeof(union acpi_operand_object);
+ }
+
+ ACPI_DUMP_BUFFER(obj_desc, bytes_to_dump);
+ break;
+
+ default:
+
+ break;
+ }
+
+ /* If value is NOT an internal object, we are done */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) !=
+ ACPI_DESC_TYPE_OPERAND) {
+ goto cleanup;
+ }
+
+ /* Valid object, get the pointer to next level, if any */
+
+ switch (obj_type) {
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_STRING:
+ /*
+ * NOTE: takes advantage of common fields between string/buffer
+ */
+ bytes_to_dump = obj_desc->string.length;
+ obj_desc = (void *)obj_desc->string.pointer;
+
+ acpi_os_printf("(Buffer/String pointer %p length %X)\n",
+ obj_desc, bytes_to_dump);
+ ACPI_DUMP_BUFFER(obj_desc, bytes_to_dump);
+ goto cleanup;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+
+ obj_desc =
+ (union acpi_operand_object *)obj_desc->buffer_field.
+ buffer_obj;
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ obj_desc = (void *)obj_desc->package.elements;
+ break;
+
+ case ACPI_TYPE_METHOD:
+
+ obj_desc = (void *)obj_desc->method.aml_start;
+ break;
+
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+
+ obj_desc = (void *)obj_desc->field.region_obj;
+ break;
+
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+
+ obj_desc = (void *)obj_desc->bank_field.region_obj;
+ break;
+
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
+ obj_desc = (void *)obj_desc->index_field.index_obj;
+ break;
+
+ default:
+
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ acpi_os_printf("\n");
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_dump_objects
+ *
+ * PARAMETERS: type - Object type to be dumped
+ * display_type - 0 or ACPI_DISPLAY_SUMMARY
+ * max_depth - Maximum depth of dump. Use ACPI_UINT32_MAX
+ * for an effectively unlimited depth.
+ * owner_id - Dump only objects owned by this ID. Use
+ * ACPI_UINT32_MAX to match all owners.
+ * start_handle - Where in namespace to start/end search
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump typed objects within the loaded namespace. Uses
+ * acpi_ns_walk_namespace in conjunction with acpi_ns_dump_one_object.
+ *
+ ******************************************************************************/
+
+void
+acpi_ns_dump_objects(acpi_object_type type,
+ u8 display_type,
+ u32 max_depth,
+ acpi_owner_id owner_id, acpi_handle start_handle)
+{
+ struct acpi_walk_info info;
+ acpi_status status;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * Just lock the entire namespace for the duration of the dump.
+ * We don't want any changes to the namespace during this time,
+ * especially the temporary nodes since we are going to display
+ * them also.
+ */
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not acquire namespace mutex\n");
+ return;
+ }
+
+ info.count = 0;
+ info.debug_level = ACPI_LV_TABLES;
+ info.owner_id = owner_id;
+ info.display_type = display_type;
+
+ (void)acpi_ns_walk_namespace(type, start_handle, max_depth,
+ ACPI_NS_WALK_NO_UNLOCK |
+ ACPI_NS_WALK_TEMP_NODES,
+ acpi_ns_dump_one_object, NULL,
+ (void *)&info, NULL);
+
+ acpi_os_printf("\nNamespace node count: %u\n\n", info.count);
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_dump_one_object_path, acpi_ns_get_max_depth
+ *
+ * PARAMETERS: obj_handle - Node to be dumped
+ * level - Nesting level of the handle
+ * context - Passed into walk_namespace
+ * return_value - Not used
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dump the full pathname to a namespace object. acp_ns_get_max_depth
+ * computes the maximum nesting depth in the namespace tree, in
+ * order to simplify formatting in acpi_ns_dump_one_object_path.
+ * These procedures are user_functions called by acpi_ns_walk_namespace.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_dump_one_object_path(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value)
+{
+ u32 max_level = *((u32 *)context);
+ char *pathname;
+ struct acpi_namespace_node *node;
+ int path_indent;
+
+ if (!obj_handle) {
+ return (AE_OK);
+ }
+
+ node = acpi_ns_validate_handle(obj_handle);
+ if (!node) {
+
+ /* Ignore bad node during namespace walk */
+
+ return (AE_OK);
+ }
+
+ pathname = acpi_ns_get_normalized_pathname(node, TRUE);
+
+ path_indent = 1;
+ if (level <= max_level) {
+ path_indent = max_level - level + 1;
+ }
+
+ acpi_os_printf("%2d%*s%-12s%*s",
+ level, level, " ", acpi_ut_get_type_name(node->type),
+ path_indent, " ");
+
+ acpi_os_printf("%s\n", &pathname[1]);
+ ACPI_FREE(pathname);
+ return (AE_OK);
+}
+
+static acpi_status
+acpi_ns_get_max_depth(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value)
+{
+ u32 *max_level = (u32 *)context;
+
+ if (level > *max_level) {
+ *max_level = level;
+ }
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_dump_object_paths
+ *
+ * PARAMETERS: type - Object type to be dumped
+ * display_type - 0 or ACPI_DISPLAY_SUMMARY
+ * max_depth - Maximum depth of dump. Use ACPI_UINT32_MAX
+ * for an effectively unlimited depth.
+ * owner_id - Dump only objects owned by this ID. Use
+ * ACPI_UINT32_MAX to match all owners.
+ * start_handle - Where in namespace to start/end search
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump full object pathnames within the loaded namespace. Uses
+ * acpi_ns_walk_namespace in conjunction with acpi_ns_dump_one_object_path.
+ *
+ ******************************************************************************/
+
+void
+acpi_ns_dump_object_paths(acpi_object_type type,
+ u8 display_type,
+ u32 max_depth,
+ acpi_owner_id owner_id, acpi_handle start_handle)
+{
+ acpi_status status;
+ u32 max_level = 0;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * Just lock the entire namespace for the duration of the dump.
+ * We don't want any changes to the namespace during this time,
+ * especially the temporary nodes since we are going to display
+ * them also.
+ */
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not acquire namespace mutex\n");
+ return;
+ }
+
+ /* Get the max depth of the namespace tree, for formatting later */
+
+ (void)acpi_ns_walk_namespace(type, start_handle, max_depth,
+ ACPI_NS_WALK_NO_UNLOCK |
+ ACPI_NS_WALK_TEMP_NODES,
+ acpi_ns_get_max_depth, NULL,
+ (void *)&max_level, NULL);
+
+ /* Now dump the entire namespace */
+
+ (void)acpi_ns_walk_namespace(type, start_handle, max_depth,
+ ACPI_NS_WALK_NO_UNLOCK |
+ ACPI_NS_WALK_TEMP_NODES,
+ acpi_ns_dump_one_object_path, NULL,
+ (void *)&max_level, NULL);
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_dump_entry
+ *
+ * PARAMETERS: handle - Node to be dumped
+ * debug_level - Output level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump a single Node
+ *
+ ******************************************************************************/
+
+void acpi_ns_dump_entry(acpi_handle handle, u32 debug_level)
+{
+ struct acpi_walk_info info;
+
+ ACPI_FUNCTION_ENTRY();
+
+ info.debug_level = debug_level;
+ info.owner_id = ACPI_OWNER_ID_MAX;
+ info.display_type = ACPI_DISPLAY_SUMMARY;
+
+ (void)acpi_ns_dump_one_object(handle, 1, &info, NULL);
+}
+
+#ifdef ACPI_ASL_COMPILER
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_dump_tables
+ *
+ * PARAMETERS: search_base - Root of subtree to be dumped, or
+ * NS_ALL to dump the entire namespace
+ * max_depth - Maximum depth of dump. Use INT_MAX
+ * for an effectively unlimited depth.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump the name space, or a portion of it.
+ *
+ ******************************************************************************/
+
+void acpi_ns_dump_tables(acpi_handle search_base, u32 max_depth)
+{
+ acpi_handle search_handle = search_base;
+
+ ACPI_FUNCTION_TRACE(ns_dump_tables);
+
+ if (!acpi_gbl_root_node) {
+ /*
+ * If the name space has not been initialized,
+ * there is nothing to dump.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_TABLES,
+ "namespace not initialized!\n"));
+ return_VOID;
+ }
+
+ if (ACPI_NS_ALL == search_base) {
+
+ /* Entire namespace */
+
+ search_handle = acpi_gbl_root_node;
+ ACPI_DEBUG_PRINT((ACPI_DB_TABLES, "\\\n"));
+ }
+
+ acpi_ns_dump_objects(ACPI_TYPE_ANY, ACPI_DISPLAY_OBJECTS, max_depth,
+ ACPI_OWNER_ID_MAX, search_handle);
+ return_VOID;
+}
+#endif
+#endif
diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c
new file mode 100644
index 000000000..b9a88b7b5
--- /dev/null
+++ b/drivers/acpi/acpica/nsdumpdv.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsdump - table dumping routines for debug
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+
+/* TBD: This entire module is apparently obsolete and should be removed */
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsdumpdv")
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+#include "acnamesp.h"
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_dump_one_device
+ *
+ * PARAMETERS: handle - Node to be dumped
+ * level - Nesting level of the handle
+ * context - Passed into walk_namespace
+ * return_value - Not used
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dump a single Node that represents a device
+ * This procedure is a user_function called by acpi_ns_walk_namespace.
+ *
+ ******************************************************************************/
+static acpi_status
+acpi_ns_dump_one_device(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value)
+{
+ struct acpi_buffer buffer;
+ struct acpi_device_info *info;
+ acpi_status status;
+ u32 i;
+
+ ACPI_FUNCTION_NAME(ns_dump_one_device);
+
+ status =
+ acpi_ns_dump_one_object(obj_handle, level, context, return_value);
+
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ status = acpi_get_object_info(obj_handle, &buffer);
+ if (ACPI_SUCCESS(status)) {
+ info = buffer.pointer;
+ for (i = 0; i < level; i++) {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_TABLES, " "));
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_TABLES,
+ " HID: %s, ADR: %8.8X%8.8X\n",
+ info->hardware_id.value,
+ ACPI_FORMAT_UINT64(info->address)));
+ ACPI_FREE(info);
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_dump_root_devices
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump all objects of type "device"
+ *
+ ******************************************************************************/
+
+void acpi_ns_dump_root_devices(void)
+{
+ acpi_handle sys_bus_handle;
+ acpi_status status;
+
+ ACPI_FUNCTION_NAME(ns_dump_root_devices);
+
+ /* Only dump the table if tracing is enabled */
+
+ if (!(ACPI_LV_TABLES & acpi_dbg_level)) {
+ return;
+ }
+
+ status = acpi_get_handle(NULL, METHOD_NAME__SB_, &sys_bus_handle);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_TABLES,
+ "Display of all devices in the namespace:\n"));
+
+ status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, sys_bus_handle,
+ ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
+ acpi_ns_dump_one_device, NULL, NULL,
+ NULL);
+}
+
+#endif
+#endif
diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c
new file mode 100644
index 000000000..63748ac69
--- /dev/null
+++ b/drivers/acpi/acpica/nseval.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: nseval - Object evaluation, includes control method execution
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nseval")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_evaluate
+ *
+ * PARAMETERS: info - Evaluation info block, contains these fields
+ * and more:
+ * prefix_node - Prefix or Method/Object Node to execute
+ * relative_path - Name of method to execute, If NULL, the
+ * Node is the object to execute
+ * parameters - List of parameters to pass to the method,
+ * terminated by NULL. Params itself may be
+ * NULL if no parameters are being passed.
+ * parameter_type - Type of Parameter list
+ * return_object - Where to put method's return value (if
+ * any). If NULL, no value is returned.
+ * flags - ACPI_IGNORE_RETURN_VALUE to delete return
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute a control method or return the current value of an
+ * ACPI namespace object.
+ *
+ * MUTEX: Locks interpreter
+ *
+ ******************************************************************************/
+acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ns_evaluate);
+
+ if (!info) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if (!info->node) {
+ /*
+ * Get the actual namespace node for the target object if we
+ * need to. Handles these cases:
+ *
+ * 1) Null node, valid pathname from root (absolute path)
+ * 2) Node and valid pathname (path relative to Node)
+ * 3) Node, Null pathname
+ */
+ status =
+ acpi_ns_get_node(info->prefix_node, info->relative_pathname,
+ ACPI_NS_NO_UPSEARCH, &info->node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * For a method alias, we must grab the actual method node so that
+ * proper scoping context will be established before execution.
+ */
+ if (acpi_ns_get_type(info->node) == ACPI_TYPE_LOCAL_METHOD_ALIAS) {
+ info->node =
+ ACPI_CAST_PTR(struct acpi_namespace_node,
+ info->node->object);
+ }
+
+ /* Complete the info block initialization */
+
+ info->return_object = NULL;
+ info->node_flags = info->node->flags;
+ info->obj_desc = acpi_ns_get_attached_object(info->node);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "%s [%p] Value %p\n",
+ info->relative_pathname, info->node,
+ acpi_ns_get_attached_object(info->node)));
+
+ /* Get info if we have a predefined name (_HID, etc.) */
+
+ info->predefined =
+ acpi_ut_match_predefined_method(info->node->name.ascii);
+
+ /* Get the full pathname to the object, for use in warning messages */
+
+ info->full_pathname = acpi_ns_get_normalized_pathname(info->node, TRUE);
+ if (!info->full_pathname) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Optional object evaluation log */
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EVALUATION,
+ "%-26s: %s (%s)\n", " Enter evaluation",
+ &info->full_pathname[1],
+ acpi_ut_get_type_name(info->node->type)));
+
+ /* Count the number of arguments being passed in */
+
+ info->param_count = 0;
+ if (info->parameters) {
+ while (info->parameters[info->param_count]) {
+ info->param_count++;
+ }
+
+ /* Warn on impossible argument count */
+
+ if (info->param_count > ACPI_METHOD_NUM_ARGS) {
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ ACPI_WARN_ALWAYS,
+ "Excess arguments (%u) - using only %u",
+ info->param_count,
+ ACPI_METHOD_NUM_ARGS));
+
+ info->param_count = ACPI_METHOD_NUM_ARGS;
+ }
+ }
+
+ /*
+ * For predefined names: Check that the declared argument count
+ * matches the ACPI spec -- otherwise this is a BIOS error.
+ */
+ acpi_ns_check_acpi_compliance(info->full_pathname, info->node,
+ info->predefined);
+
+ /*
+ * For all names: Check that the incoming argument count for
+ * this method/object matches the actual ASL/AML definition.
+ */
+ acpi_ns_check_argument_count(info->full_pathname, info->node,
+ info->param_count, info->predefined);
+
+ /* For predefined names: Typecheck all incoming arguments */
+
+ acpi_ns_check_argument_types(info);
+
+ /*
+ * Three major evaluation cases:
+ *
+ * 1) Object types that cannot be evaluated by definition
+ * 2) The object is a control method -- execute it
+ * 3) The object is not a method -- just return it's current value
+ */
+ switch (acpi_ns_get_type(info->node)) {
+ case ACPI_TYPE_ANY:
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_EVENT:
+ case ACPI_TYPE_MUTEX:
+ case ACPI_TYPE_REGION:
+ case ACPI_TYPE_THERMAL:
+ case ACPI_TYPE_LOCAL_SCOPE:
+ /*
+ * 1) Disallow evaluation of these object types. For these,
+ * object evaluation is undefined.
+ */
+ ACPI_ERROR((AE_INFO,
+ "%s: This object type [%s] "
+ "never contains data and cannot be evaluated",
+ info->full_pathname,
+ acpi_ut_get_type_name(info->node->type)));
+
+ status = AE_TYPE;
+ goto cleanup;
+
+ case ACPI_TYPE_METHOD:
+ /*
+ * 2) Object is a control method - execute it
+ */
+
+ /* Verify that there is a method object associated with this node */
+
+ if (!info->obj_desc) {
+ ACPI_ERROR((AE_INFO,
+ "%s: Method has no attached sub-object",
+ info->full_pathname));
+ status = AE_NULL_OBJECT;
+ goto cleanup;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "**** Execute method [%s] at AML address %p length %X\n",
+ info->full_pathname,
+ info->obj_desc->method.aml_start + 1,
+ info->obj_desc->method.aml_length - 1));
+
+ /*
+ * Any namespace deletion must acquire both the namespace and
+ * interpreter locks to ensure that no thread is using the portion of
+ * the namespace that is being deleted.
+ *
+ * Execute the method via the interpreter. The interpreter is locked
+ * here before calling into the AML parser
+ */
+ acpi_ex_enter_interpreter();
+ status = acpi_ps_execute_method(info);
+ acpi_ex_exit_interpreter();
+ break;
+
+ default:
+ /*
+ * 3) All other non-method objects -- get the current object value
+ */
+
+ /*
+ * Some objects require additional resolution steps (e.g., the Node
+ * may be a field that must be read, etc.) -- we can't just grab
+ * the object out of the node.
+ *
+ * Use resolve_node_to_value() to get the associated value.
+ *
+ * NOTE: we can get away with passing in NULL for a walk state because
+ * the Node is guaranteed to not be a reference to either a method
+ * local or a method argument (because this interface is never called
+ * from a running method.)
+ *
+ * Even though we do not directly invoke the interpreter for object
+ * resolution, we must lock it because we could access an op_region.
+ * The op_region access code assumes that the interpreter is locked.
+ */
+ acpi_ex_enter_interpreter();
+
+ /* TBD: resolve_node_to_value has a strange interface, fix */
+
+ info->return_object =
+ ACPI_CAST_PTR(union acpi_operand_object, info->node);
+
+ status =
+ acpi_ex_resolve_node_to_value(ACPI_CAST_INDIRECT_PTR
+ (struct acpi_namespace_node,
+ &info->return_object), NULL);
+ acpi_ex_exit_interpreter();
+
+ if (ACPI_FAILURE(status)) {
+ info->return_object = NULL;
+ goto cleanup;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "Returned object %p [%s]\n",
+ info->return_object,
+ acpi_ut_get_object_type_name(info->
+ return_object)));
+
+ status = AE_CTRL_RETURN_VALUE; /* Always has a "return value" */
+ break;
+ }
+
+ /*
+ * For predefined names, check the return value against the ACPI
+ * specification. Some incorrect return value types are repaired.
+ */
+ (void)acpi_ns_check_return_value(info->node, info, info->param_count,
+ status, &info->return_object);
+
+ /* Check if there is a return value that must be dealt with */
+
+ if (status == AE_CTRL_RETURN_VALUE) {
+
+ /* If caller does not want the return value, delete it */
+
+ if (info->flags & ACPI_IGNORE_RETURN_VALUE) {
+ acpi_ut_remove_reference(info->return_object);
+ info->return_object = NULL;
+ }
+
+ /* Map AE_CTRL_RETURN_VALUE to AE_OK, we are done with it */
+
+ status = AE_OK;
+ } else if (ACPI_FAILURE(status)) {
+
+ /* If return_object exists, delete it */
+
+ if (info->return_object) {
+ acpi_ut_remove_reference(info->return_object);
+ info->return_object = NULL;
+ }
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "*** Completed evaluation of object %s ***\n",
+ info->relative_pathname));
+
+cleanup:
+ /* Optional object evaluation log */
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EVALUATION,
+ "%-26s: %s\n", " Exit evaluation",
+ &info->full_pathname[1]));
+
+ /*
+ * Namespace was unlocked by the handling acpi_ns* function, so we
+ * just free the pathname and return
+ */
+ ACPI_FREE(info->full_pathname);
+ info->full_pathname = NULL;
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c
new file mode 100644
index 000000000..3e6207ad1
--- /dev/null
+++ b/drivers/acpi/acpica/nsinit.c
@@ -0,0 +1,679 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsinit - namespace initialization
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsinit")
+
+/* Local prototypes */
+static acpi_status
+acpi_ns_init_one_object(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value);
+
+static acpi_status
+acpi_ns_init_one_device(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value);
+
+static acpi_status
+acpi_ns_find_ini_methods(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_initialize_objects
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Walk the entire namespace and perform any necessary
+ * initialization on the objects found therein
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ns_initialize_objects(void)
+{
+ acpi_status status;
+ struct acpi_init_walk_info info;
+
+ ACPI_FUNCTION_TRACE(ns_initialize_objects);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[Init] Completing Initialization of ACPI Objects\n"));
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "**** Starting initialization of namespace objects ****\n"));
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
+ "Final data object initialization: "));
+
+ /* Clear the info block */
+
+ memset(&info, 0, sizeof(struct acpi_init_walk_info));
+
+ /* Walk entire namespace from the supplied root */
+
+ /*
+ * TBD: will become ACPI_TYPE_PACKAGE as this type object
+ * is now the only one that supports deferred initialization
+ * (forward references).
+ */
+ status = acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_ns_init_one_object,
+ NULL, &info, NULL);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace"));
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
+ "Namespace contains %u (0x%X) objects\n",
+ info.object_count, info.object_count));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
+ "%u Control Methods found\n%u Op Regions found\n",
+ info.method_count, info.op_region_count));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_initialize_devices
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: acpi_status
+ *
+ * DESCRIPTION: Walk the entire namespace and initialize all ACPI devices.
+ * This means running _INI on all present devices.
+ *
+ * Note: We install PCI config space handler on region access,
+ * not here.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ns_initialize_devices(u32 flags)
+{
+ acpi_status status = AE_OK;
+ struct acpi_device_walk_info info;
+ acpi_handle handle;
+
+ ACPI_FUNCTION_TRACE(ns_initialize_devices);
+
+ if (!(flags & ACPI_NO_DEVICE_INIT)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[Init] Initializing ACPI Devices\n"));
+
+ /* Init counters */
+
+ info.device_count = 0;
+ info.num_STA = 0;
+ info.num_INI = 0;
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
+ "Initializing Device/Processor/Thermal objects "
+ "and executing _INI/_STA methods:\n"));
+
+ /* Tree analysis: find all subtrees that contain _INI methods */
+
+ status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, FALSE,
+ acpi_ns_find_ini_methods, NULL,
+ &info, NULL);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ /* Allocate the evaluation information block */
+
+ info.evaluate_info =
+ ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
+ if (!info.evaluate_info) {
+ status = AE_NO_MEMORY;
+ goto error_exit;
+ }
+
+ /*
+ * Execute the "global" _INI method that may appear at the root.
+ * This support is provided for Windows compatibility (Vista+) and
+ * is not part of the ACPI specification.
+ */
+ info.evaluate_info->prefix_node = acpi_gbl_root_node;
+ info.evaluate_info->relative_pathname = METHOD_NAME__INI;
+ info.evaluate_info->parameters = NULL;
+ info.evaluate_info->flags = ACPI_IGNORE_RETURN_VALUE;
+
+ status = acpi_ns_evaluate(info.evaluate_info);
+ if (ACPI_SUCCESS(status)) {
+ info.num_INI++;
+ }
+
+ /*
+ * Execute \_SB._INI.
+ * There appears to be a strict order requirement for \_SB._INI,
+ * which should be evaluated before any _REG evaluations.
+ */
+ status = acpi_get_handle(NULL, "\\_SB", &handle);
+ if (ACPI_SUCCESS(status)) {
+ memset(info.evaluate_info, 0,
+ sizeof(struct acpi_evaluate_info));
+ info.evaluate_info->prefix_node = handle;
+ info.evaluate_info->relative_pathname =
+ METHOD_NAME__INI;
+ info.evaluate_info->parameters = NULL;
+ info.evaluate_info->flags = ACPI_IGNORE_RETURN_VALUE;
+
+ status = acpi_ns_evaluate(info.evaluate_info);
+ if (ACPI_SUCCESS(status)) {
+ info.num_INI++;
+ }
+ }
+ }
+
+ /*
+ * Run all _REG methods
+ *
+ * Note: Any objects accessed by the _REG methods will be automatically
+ * initialized, even if they contain executable AML (see the call to
+ * acpi_ns_initialize_objects below).
+ *
+ * Note: According to the ACPI specification, we actually needn't execute
+ * _REG for system_memory/system_io operation regions, but for PCI_Config
+ * operation regions, it is required to evaluate _REG for those on a PCI
+ * root bus that doesn't contain _BBN object. So this code is kept here
+ * in order not to break things.
+ */
+ if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[Init] Executing _REG OpRegion methods\n"));
+
+ status = acpi_ev_initialize_op_regions();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+ }
+
+ if (!(flags & ACPI_NO_DEVICE_INIT)) {
+
+ /* Walk namespace to execute all _INIs on present devices */
+
+ status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, FALSE,
+ acpi_ns_init_one_device, NULL,
+ &info, NULL);
+
+ /*
+ * Any _OSI requests should be completed by now. If the BIOS has
+ * requested any Windows OSI strings, we will always truncate
+ * I/O addresses to 16 bits -- for Windows compatibility.
+ */
+ if (acpi_gbl_osi_data >= ACPI_OSI_WIN_2000) {
+ acpi_gbl_truncate_io_addresses = TRUE;
+ }
+
+ ACPI_FREE(info.evaluate_info);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
+ " Executed %u _INI methods requiring %u _STA executions "
+ "(examined %u objects)\n",
+ info.num_INI, info.num_STA,
+ info.device_count));
+ }
+
+ return_ACPI_STATUS(status);
+
+error_exit:
+ ACPI_EXCEPTION((AE_INFO, status, "During device initialization"));
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_init_one_package
+ *
+ * PARAMETERS: obj_handle - Node
+ * level - Current nesting level
+ * context - Not used
+ * return_value - Not used
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Callback from acpi_walk_namespace. Invoked for every package
+ * within the namespace. Used during dynamic load of an SSDT.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_init_one_package(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value)
+{
+ acpi_status status;
+ union acpi_operand_object *obj_desc;
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ return (AE_OK);
+ }
+
+ /* Exit if package is already initialized */
+
+ if (obj_desc->package.flags & AOPOBJ_DATA_VALID) {
+ return (AE_OK);
+ }
+
+ status = acpi_ds_get_package_arguments(obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return (AE_OK);
+ }
+
+ status =
+ acpi_ut_walk_package_tree(obj_desc, NULL,
+ acpi_ds_init_package_element, NULL);
+ if (ACPI_FAILURE(status)) {
+ return (AE_OK);
+ }
+
+ obj_desc->package.flags |= AOPOBJ_DATA_VALID;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_init_one_object
+ *
+ * PARAMETERS: obj_handle - Node
+ * level - Current nesting level
+ * context - Points to a init info struct
+ * return_value - Not used
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Callback from acpi_walk_namespace. Invoked for every object
+ * within the namespace.
+ *
+ * Currently, the only objects that require initialization are:
+ * 1) Methods
+ * 2) Op Regions
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_init_one_object(acpi_handle obj_handle,
+ u32 level, void *context, void **return_value)
+{
+ acpi_object_type type;
+ acpi_status status = AE_OK;
+ struct acpi_init_walk_info *info =
+ (struct acpi_init_walk_info *)context;
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+ union acpi_operand_object *obj_desc;
+
+ ACPI_FUNCTION_NAME(ns_init_one_object);
+
+ info->object_count++;
+
+ /* And even then, we are only interested in a few object types */
+
+ type = acpi_ns_get_type(obj_handle);
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (!obj_desc) {
+ return (AE_OK);
+ }
+
+ /* Increment counters for object types we are looking for */
+
+ switch (type) {
+ case ACPI_TYPE_REGION:
+
+ info->op_region_count++;
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+
+ info->field_count++;
+ break;
+
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+
+ info->field_count++;
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ info->buffer_count++;
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ info->package_count++;
+ break;
+
+ default:
+
+ /* No init required, just exit now */
+
+ return (AE_OK);
+ }
+
+ /* If the object is already initialized, nothing else to do */
+
+ if (obj_desc->common.flags & AOPOBJ_DATA_VALID) {
+ return (AE_OK);
+ }
+
+ /* Must lock the interpreter before executing AML code */
+
+ acpi_ex_enter_interpreter();
+
+ /*
+ * Only initialization of Package objects can be deferred, in order
+ * to support forward references.
+ */
+ switch (type) {
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+
+ /* TBD: bank_fields do not require deferred init, remove this code */
+
+ info->field_init++;
+ status = acpi_ds_get_bank_field_arguments(obj_desc);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ /* Complete the initialization/resolution of the package object */
+
+ info->package_init++;
+ status =
+ acpi_ns_init_one_package(obj_handle, level, NULL, NULL);
+ break;
+
+ default:
+
+ /* No other types should get here */
+
+ status = AE_TYPE;
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Opcode is not deferred [%4.4s] (%s)",
+ acpi_ut_get_node_name(node),
+ acpi_ut_get_type_name(type)));
+ break;
+ }
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not execute arguments for [%4.4s] (%s)",
+ acpi_ut_get_node_name(node),
+ acpi_ut_get_type_name(type)));
+ }
+
+ /*
+ * We ignore errors from above, and always return OK, since we don't want
+ * to abort the walk on any single error.
+ */
+ acpi_ex_exit_interpreter();
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_find_ini_methods
+ *
+ * PARAMETERS: acpi_walk_callback
+ *
+ * RETURN: acpi_status
+ *
+ * DESCRIPTION: Called during namespace walk. Finds objects named _INI under
+ * device/processor/thermal objects, and marks the entire subtree
+ * with a SUBTREE_HAS_INI flag. This flag is used during the
+ * subsequent device initialization walk to avoid entire subtrees
+ * that do not contain an _INI.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_find_ini_methods(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value)
+{
+ struct acpi_device_walk_info *info =
+ ACPI_CAST_PTR(struct acpi_device_walk_info, context);
+ struct acpi_namespace_node *node;
+ struct acpi_namespace_node *parent_node;
+
+ /* Keep count of device/processor/thermal objects */
+
+ node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle);
+ if ((node->type == ACPI_TYPE_DEVICE) ||
+ (node->type == ACPI_TYPE_PROCESSOR) ||
+ (node->type == ACPI_TYPE_THERMAL)) {
+ info->device_count++;
+ return (AE_OK);
+ }
+
+ /* We are only looking for methods named _INI */
+
+ if (!ACPI_COMPARE_NAMESEG(node->name.ascii, METHOD_NAME__INI)) {
+ return (AE_OK);
+ }
+
+ /*
+ * The only _INI methods that we care about are those that are
+ * present under Device, Processor, and Thermal objects.
+ */
+ parent_node = node->parent;
+ switch (parent_node->type) {
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+
+ /* Mark parent and bubble up the INI present flag to the root */
+
+ while (parent_node) {
+ parent_node->flags |= ANOBJ_SUBTREE_HAS_INI;
+ parent_node = parent_node->parent;
+ }
+ break;
+
+ default:
+
+ break;
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_init_one_device
+ *
+ * PARAMETERS: acpi_walk_callback
+ *
+ * RETURN: acpi_status
+ *
+ * DESCRIPTION: This is called once per device soon after ACPI is enabled
+ * to initialize each device. It determines if the device is
+ * present, and if so, calls _INI.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_init_one_device(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value)
+{
+ struct acpi_device_walk_info *walk_info =
+ ACPI_CAST_PTR(struct acpi_device_walk_info, context);
+ struct acpi_evaluate_info *info = walk_info->evaluate_info;
+ u32 flags;
+ acpi_status status;
+ struct acpi_namespace_node *device_node;
+
+ ACPI_FUNCTION_TRACE(ns_init_one_device);
+
+ /* We are interested in Devices, Processors and thermal_zones only */
+
+ device_node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle);
+ if ((device_node->type != ACPI_TYPE_DEVICE) &&
+ (device_node->type != ACPI_TYPE_PROCESSOR) &&
+ (device_node->type != ACPI_TYPE_THERMAL)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Because of an earlier namespace analysis, all subtrees that contain an
+ * _INI method are tagged.
+ *
+ * If this device subtree does not contain any _INI methods, we
+ * can exit now and stop traversing this entire subtree.
+ */
+ if (!(device_node->flags & ANOBJ_SUBTREE_HAS_INI)) {
+ return_ACPI_STATUS(AE_CTRL_DEPTH);
+ }
+
+ /*
+ * Run _STA to determine if this device is present and functioning. We
+ * must know this information for two important reasons (from ACPI spec):
+ *
+ * 1) We can only run _INI if the device is present.
+ * 2) We must abort the device tree walk on this subtree if the device is
+ * not present and is not functional (we will not examine the children)
+ *
+ * The _STA method is not required to be present under the device, we
+ * assume the device is present if _STA does not exist.
+ */
+ ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname
+ (ACPI_TYPE_METHOD, device_node, METHOD_NAME__STA));
+
+ status = acpi_ut_execute_STA(device_node, &flags);
+ if (ACPI_FAILURE(status)) {
+
+ /* Ignore error and move on to next device */
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Flags == -1 means that _STA was not found. In this case, we assume that
+ * the device is both present and functional.
+ *
+ * From the ACPI spec, description of _STA:
+ *
+ * "If a device object (including the processor object) does not have an
+ * _STA object, then OSPM assumes that all of the above bits are set (in
+ * other words, the device is present, ..., and functioning)"
+ */
+ if (flags != ACPI_UINT32_MAX) {
+ walk_info->num_STA++;
+ }
+
+ /*
+ * Examine the PRESENT and FUNCTIONING status bits
+ *
+ * Note: ACPI spec does not seem to specify behavior for the present but
+ * not functioning case, so we assume functioning if present.
+ */
+ if (!(flags & ACPI_STA_DEVICE_PRESENT)) {
+
+ /* Device is not present, we must examine the Functioning bit */
+
+ if (flags & ACPI_STA_DEVICE_FUNCTIONING) {
+ /*
+ * Device is not present but is "functioning". In this case,
+ * we will not run _INI, but we continue to examine the children
+ * of this device.
+ *
+ * From the ACPI spec, description of _STA: (note - no mention
+ * of whether to run _INI or not on the device in question)
+ *
+ * "_STA may return bit 0 clear (not present) with bit 3 set
+ * (device is functional). This case is used to indicate a valid
+ * device for which no device driver should be loaded (for example,
+ * a bridge device.) Children of this device may be present and
+ * valid. OSPM should continue enumeration below a device whose
+ * _STA returns this bit combination"
+ */
+ return_ACPI_STATUS(AE_OK);
+ } else {
+ /*
+ * Device is not present and is not functioning. We must abort the
+ * walk of this subtree immediately -- don't look at the children
+ * of such a device.
+ *
+ * From the ACPI spec, description of _INI:
+ *
+ * "If the _STA method indicates that the device is not present,
+ * OSPM will not run the _INI and will not examine the children
+ * of the device for _INI methods"
+ */
+ return_ACPI_STATUS(AE_CTRL_DEPTH);
+ }
+ }
+
+ /*
+ * The device is present or is assumed present if no _STA exists.
+ * Run the _INI if it exists (not required to exist)
+ *
+ * Note: We know there is an _INI within this subtree, but it may not be
+ * under this particular device, it may be lower in the branch.
+ */
+ if (!ACPI_COMPARE_NAMESEG(device_node->name.ascii, "_SB_") ||
+ device_node->parent != acpi_gbl_root_node) {
+ ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname
+ (ACPI_TYPE_METHOD, device_node,
+ METHOD_NAME__INI));
+
+ memset(info, 0, sizeof(struct acpi_evaluate_info));
+ info->prefix_node = device_node;
+ info->relative_pathname = METHOD_NAME__INI;
+ info->parameters = NULL;
+ info->flags = ACPI_IGNORE_RETURN_VALUE;
+
+ status = acpi_ns_evaluate(info);
+ if (ACPI_SUCCESS(status)) {
+ walk_info->num_INI++;
+ }
+#ifdef ACPI_DEBUG_OUTPUT
+ else if (status != AE_NOT_FOUND) {
+
+ /* Ignore error and move on to next device */
+
+ char *scope_name =
+ acpi_ns_get_normalized_pathname(device_node, TRUE);
+
+ ACPI_EXCEPTION((AE_INFO, status,
+ "during %s._INI execution",
+ scope_name));
+ ACPI_FREE(scope_name);
+ }
+#endif
+ }
+
+ /* Ignore errors from above */
+
+ status = AE_OK;
+
+ /*
+ * The _INI method has been run if present; call the Global Initialization
+ * Handler for this device.
+ */
+ if (acpi_gbl_init_handler) {
+ status =
+ acpi_gbl_init_handler(device_node, ACPI_INIT_DEVICE_INI);
+ }
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c
new file mode 100644
index 000000000..880260a30
--- /dev/null
+++ b/drivers/acpi/acpica/nsload.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsload - namespace loading/expanding/contracting procedures
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acdispat.h"
+#include "actables.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsload")
+
+/* Local prototypes */
+#ifdef ACPI_FUTURE_IMPLEMENTATION
+acpi_status acpi_ns_unload_namespace(acpi_handle handle);
+
+static acpi_status acpi_ns_delete_subtree(acpi_handle start_handle);
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_load_table
+ *
+ * PARAMETERS: table_index - Index for table to be loaded
+ * node - Owning NS node
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load one ACPI table into the namespace
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ns_load_table);
+
+ /* If table already loaded into namespace, just return */
+
+ if (acpi_tb_is_table_loaded(table_index)) {
+ status = AE_ALREADY_EXISTS;
+ goto unlock;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "**** Loading table into namespace ****\n"));
+
+ status = acpi_tb_allocate_owner_id(table_index);
+ if (ACPI_FAILURE(status)) {
+ goto unlock;
+ }
+
+ /*
+ * Parse the table and load the namespace with all named
+ * objects found within. Control methods are NOT parsed
+ * at this time. In fact, the control methods cannot be
+ * parsed until the entire namespace is loaded, because
+ * if a control method makes a forward reference (call)
+ * to another control method, we can't continue parsing
+ * because we don't know how many arguments to parse next!
+ */
+ status = acpi_ns_parse_table(table_index, node);
+ if (ACPI_SUCCESS(status)) {
+ acpi_tb_set_table_loaded_flag(table_index, TRUE);
+ } else {
+ /*
+ * On error, delete any namespace objects created by this table.
+ * We cannot initialize these objects, so delete them. There are
+ * a couple of especially bad cases:
+ * AE_ALREADY_EXISTS - namespace collision.
+ * AE_NOT_FOUND - the target of a Scope operator does not
+ * exist. This target of Scope must already exist in the
+ * namespace, as per the ACPI specification.
+ */
+ acpi_ns_delete_namespace_by_owner(acpi_gbl_root_table_list.
+ tables[table_index].owner_id);
+
+ acpi_tb_release_owner_id(table_index);
+ return_ACPI_STATUS(status);
+ }
+
+unlock:
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Now we can parse the control methods. We always parse
+ * them here for a sanity check, and if configured for
+ * just-in-time parsing, we delete the control method
+ * parse trees.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "**** Begin Table Object Initialization\n"));
+
+ acpi_ex_enter_interpreter();
+ status = acpi_ds_initialize_objects(table_index, node);
+ acpi_ex_exit_interpreter();
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "**** Completed Table Object Initialization\n"));
+
+ return_ACPI_STATUS(status);
+}
+
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_load_namespace
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load the name space from what ever is pointed to by DSDT.
+ * (DSDT points to either the BIOS or a buffer.)
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ns_load_namespace(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_load_name_space);
+
+ /* There must be at least a DSDT installed */
+
+ if (acpi_gbl_DSDT == NULL) {
+ ACPI_ERROR((AE_INFO, "DSDT is not in memory"));
+ return_ACPI_STATUS(AE_NO_ACPI_TABLES);
+ }
+
+ /*
+ * Load the namespace. The DSDT is required,
+ * but the SSDT and PSDT tables are optional.
+ */
+ status = acpi_ns_load_table_by_type(ACPI_TABLE_ID_DSDT);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Ignore exceptions from these */
+
+ (void)acpi_ns_load_table_by_type(ACPI_TABLE_ID_SSDT);
+ (void)acpi_ns_load_table_by_type(ACPI_TABLE_ID_PSDT);
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
+ "ACPI Namespace successfully loaded at root %p\n",
+ acpi_gbl_root_node));
+
+ return_ACPI_STATUS(status);
+}
+#endif
+
+#ifdef ACPI_FUTURE_IMPLEMENTATION
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_delete_subtree
+ *
+ * PARAMETERS: start_handle - Handle in namespace where search begins
+ *
+ * RETURNS Status
+ *
+ * DESCRIPTION: Walks the namespace starting at the given handle and deletes
+ * all objects, entries, and scopes in the entire subtree.
+ *
+ * Namespace/Interpreter should be locked or the subsystem should
+ * be in shutdown before this routine is called.
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_ns_delete_subtree(acpi_handle start_handle)
+{
+ acpi_status status;
+ acpi_handle child_handle;
+ acpi_handle parent_handle;
+ acpi_handle next_child_handle;
+ acpi_handle dummy;
+ u32 level;
+
+ ACPI_FUNCTION_TRACE(ns_delete_subtree);
+
+ parent_handle = start_handle;
+ child_handle = NULL;
+ level = 1;
+
+ /*
+ * Traverse the tree of objects until we bubble back up
+ * to where we started.
+ */
+ while (level > 0) {
+
+ /* Attempt to get the next object in this scope */
+
+ status = acpi_get_next_object(ACPI_TYPE_ANY, parent_handle,
+ child_handle, &next_child_handle);
+
+ child_handle = next_child_handle;
+
+ /* Did we get a new object? */
+
+ if (ACPI_SUCCESS(status)) {
+
+ /* Check if this object has any children */
+
+ if (ACPI_SUCCESS
+ (acpi_get_next_object
+ (ACPI_TYPE_ANY, child_handle, NULL, &dummy))) {
+ /*
+ * There is at least one child of this object,
+ * visit the object
+ */
+ level++;
+ parent_handle = child_handle;
+ child_handle = NULL;
+ }
+ } else {
+ /*
+ * No more children in this object, go back up to
+ * the object's parent
+ */
+ level--;
+
+ /* Delete all children now */
+
+ acpi_ns_delete_children(child_handle);
+
+ child_handle = parent_handle;
+ status = acpi_get_parent(parent_handle, &parent_handle);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+ }
+
+ /* Now delete the starting object, and we are done */
+
+ acpi_ns_remove_node(child_handle);
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_unload_name_space
+ *
+ * PARAMETERS: handle - Root of namespace subtree to be deleted
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Shrinks the namespace, typically in response to an undocking
+ * event. Deletes an entire subtree starting from (and
+ * including) the given handle.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ns_unload_namespace(acpi_handle handle)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ns_unload_name_space);
+
+ /* Parameter validation */
+
+ if (!acpi_gbl_root_node) {
+ return_ACPI_STATUS(AE_NO_NAMESPACE);
+ }
+
+ if (!handle) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* This function does the real work */
+
+ status = acpi_ns_delete_subtree(handle);
+ return_ACPI_STATUS(status);
+}
+#endif
diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c
new file mode 100644
index 000000000..d91153f65
--- /dev/null
+++ b/drivers/acpi/acpica/nsnames.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: nsnames - Name manipulation and search
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "amlcode.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsnames")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_external_pathname
+ *
+ * PARAMETERS: node - Namespace node whose pathname is needed
+ *
+ * RETURN: Pointer to storage containing the fully qualified name of
+ * the node, In external format (name segments separated by path
+ * separators.)
+ *
+ * DESCRIPTION: Used to obtain the full pathname to a namespace node, usually
+ * for error and debug statements.
+ *
+ ******************************************************************************/
+char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node)
+{
+ char *name_buffer;
+
+ ACPI_FUNCTION_TRACE_PTR(ns_get_external_pathname, node);
+
+ name_buffer = acpi_ns_get_normalized_pathname(node, FALSE);
+ return_PTR(name_buffer);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_pathname_length
+ *
+ * PARAMETERS: node - Namespace node
+ *
+ * RETURN: Length of path, including prefix
+ *
+ * DESCRIPTION: Get the length of the pathname string for this node
+ *
+ ******************************************************************************/
+
+acpi_size acpi_ns_get_pathname_length(struct acpi_namespace_node *node)
+{
+ acpi_size size;
+
+ /* Validate the Node */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid/cached reference target node: %p, descriptor type %d",
+ node, ACPI_GET_DESCRIPTOR_TYPE(node)));
+ return (0);
+ }
+
+ size = acpi_ns_build_normalized_path(node, NULL, 0, FALSE);
+ return (size);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_handle_to_name
+ *
+ * PARAMETERS: target_handle - Handle of named object whose name is
+ * to be found
+ * buffer - Where the name is returned
+ *
+ * RETURN: Status, Buffer is filled with name if status is AE_OK
+ *
+ * DESCRIPTION: Build and return a full namespace name
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_handle_to_name(acpi_handle target_handle, struct acpi_buffer *buffer)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+ const char *node_name;
+
+ ACPI_FUNCTION_TRACE_PTR(ns_handle_to_name, target_handle);
+
+ node = acpi_ns_validate_handle(target_handle);
+ if (!node) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Validate/Allocate/Clear caller buffer */
+
+ status = acpi_ut_initialize_buffer(buffer, ACPI_PATH_SEGMENT_LENGTH);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Just copy the ACPI name from the Node and zero terminate it */
+
+ node_name = acpi_ut_get_node_name(node);
+ ACPI_COPY_NAMESEG(buffer->pointer, node_name);
+ ((char *)buffer->pointer)[ACPI_NAMESEG_SIZE] = 0;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%4.4s\n", (char *)buffer->pointer));
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_handle_to_pathname
+ *
+ * PARAMETERS: target_handle - Handle of named object whose name is
+ * to be found
+ * buffer - Where the pathname is returned
+ * no_trailing - Remove trailing '_' for each name
+ * segment
+ *
+ * RETURN: Status, Buffer is filled with pathname if status is AE_OK
+ *
+ * DESCRIPTION: Build and return a full namespace pathname
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_handle_to_pathname(acpi_handle target_handle,
+ struct acpi_buffer *buffer, u8 no_trailing)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+ acpi_size required_size;
+
+ ACPI_FUNCTION_TRACE_PTR(ns_handle_to_pathname, target_handle);
+
+ node = acpi_ns_validate_handle(target_handle);
+ if (!node) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Determine size required for the caller buffer */
+
+ required_size =
+ acpi_ns_build_normalized_path(node, NULL, 0, no_trailing);
+ if (!required_size) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Validate/Allocate/Clear caller buffer */
+
+ status = acpi_ut_initialize_buffer(buffer, required_size);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Build the path in the caller buffer */
+
+ (void)acpi_ns_build_normalized_path(node, buffer->pointer,
+ (u32)required_size, no_trailing);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%s [%X]\n",
+ (char *)buffer->pointer, (u32) required_size));
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_build_normalized_path
+ *
+ * PARAMETERS: node - Namespace node
+ * full_path - Where the path name is returned
+ * path_size - Size of returned path name buffer
+ * no_trailing - Remove trailing '_' from each name segment
+ *
+ * RETURN: Return 1 if the AML path is empty, otherwise returning (length
+ * of pathname + 1) which means the 'FullPath' contains a trailing
+ * null.
+ *
+ * DESCRIPTION: Build and return a full namespace pathname.
+ * Note that if the size of 'FullPath' isn't large enough to
+ * contain the namespace node's path name, the actual required
+ * buffer length is returned, and it should be greater than
+ * 'PathSize'. So callers are able to check the returning value
+ * to determine the buffer size of 'FullPath'.
+ *
+ ******************************************************************************/
+
+u32
+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];
+ u8 do_no_trailing;
+ char c, *left, *right;
+ struct acpi_namespace_node *next_node;
+
+ ACPI_FUNCTION_TRACE_PTR(ns_build_normalized_path, node);
+
+#define ACPI_PATH_PUT8(path, size, byte, length) \
+ do { \
+ if ((length) < (size)) \
+ { \
+ (path)[(length)] = (byte); \
+ } \
+ (length)++; \
+ } while (0)
+
+ /*
+ * Make sure the path_size is correct, so that we don't need to
+ * validate both full_path and path_size.
+ */
+ if (!full_path) {
+ path_size = 0;
+ }
+
+ if (!node) {
+ goto build_trailing_null;
+ }
+
+ next_node = node;
+ while (next_node && next_node != acpi_gbl_root_node) {
+ if (next_node != node) {
+ ACPI_PATH_PUT8(full_path, path_size,
+ AML_DUAL_NAME_PREFIX, length);
+ }
+
+ ACPI_MOVE_32_TO_32(name, &next_node->name);
+ do_no_trailing = no_trailing;
+ for (i = 0; i < 4; i++) {
+ c = name[4 - i - 1];
+ if (do_no_trailing && c != '_') {
+ do_no_trailing = FALSE;
+ }
+ if (!do_no_trailing) {
+ ACPI_PATH_PUT8(full_path, path_size, c, length);
+ }
+ }
+
+ next_node = next_node->parent;
+ }
+
+ ACPI_PATH_PUT8(full_path, path_size, AML_ROOT_PREFIX, length);
+
+ /* Reverse the path string */
+
+ if (length <= path_size) {
+ left = full_path;
+ right = full_path + length - 1;
+
+ while (left < right) {
+ c = *left;
+ *left++ = *right;
+ *right-- = c;
+ }
+ }
+
+ /* Append the trailing null */
+
+build_trailing_null:
+ ACPI_PATH_PUT8(full_path, path_size, '\0', length);
+
+#undef ACPI_PATH_PUT8
+
+ return_UINT32(length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_normalized_pathname
+ *
+ * PARAMETERS: node - Namespace node whose pathname is needed
+ * no_trailing - Remove trailing '_' from each name segment
+ *
+ * RETURN: Pointer to storage containing the fully qualified name of
+ * the node, In external format (name segments separated by path
+ * separators.)
+ *
+ * DESCRIPTION: Used to obtain the full pathname to a namespace node, usually
+ * for error and debug statements. All trailing '_' will be
+ * removed from the full pathname if 'NoTrailing' is specified..
+ *
+ ******************************************************************************/
+
+char *acpi_ns_get_normalized_pathname(struct acpi_namespace_node *node,
+ u8 no_trailing)
+{
+ char *name_buffer;
+ acpi_size size;
+
+ ACPI_FUNCTION_TRACE_PTR(ns_get_normalized_pathname, node);
+
+ /* Calculate required buffer size based on depth below root */
+
+ size = acpi_ns_build_normalized_path(node, NULL, 0, no_trailing);
+ if (!size) {
+ return_PTR(NULL);
+ }
+
+ /* Allocate a buffer to be returned to caller */
+
+ name_buffer = ACPI_ALLOCATE_ZEROED(size);
+ if (!name_buffer) {
+ ACPI_ERROR((AE_INFO, "Could not allocate %u bytes", (u32)size));
+ return_PTR(NULL);
+ }
+
+ /* Build the path in the allocated buffer */
+
+ (void)acpi_ns_build_normalized_path(node, name_buffer, (u32)size,
+ no_trailing);
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_NAMES, "%s: Path \"%s\"\n",
+ ACPI_GET_FUNCTION_NAME, name_buffer));
+
+ return_PTR(name_buffer);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_build_prefixed_pathname
+ *
+ * PARAMETERS: prefix_scope - Scope/Path that prefixes the internal path
+ * internal_path - Name or path of the namespace node
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Construct a fully qualified pathname from a concatenation of:
+ * 1) Path associated with the prefix_scope namespace node
+ * 2) External path representation of the Internal path
+ *
+ ******************************************************************************/
+
+char *acpi_ns_build_prefixed_pathname(union acpi_generic_state *prefix_scope,
+ const char *internal_path)
+{
+ acpi_status status;
+ char *full_path = NULL;
+ char *external_path = NULL;
+ char *prefix_path = NULL;
+ acpi_size prefix_path_length = 0;
+
+ /* If there is a prefix, get the pathname to it */
+
+ if (prefix_scope && prefix_scope->scope.node) {
+ prefix_path =
+ acpi_ns_get_normalized_pathname(prefix_scope->scope.node,
+ TRUE);
+ if (prefix_path) {
+ prefix_path_length = strlen(prefix_path);
+ }
+ }
+
+ status = acpi_ns_externalize_name(ACPI_UINT32_MAX, internal_path,
+ NULL, &external_path);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /* Merge the prefix path and the path. 2 is for one dot and trailing null */
+
+ full_path =
+ ACPI_ALLOCATE_ZEROED(prefix_path_length + strlen(external_path) +
+ 2);
+ if (!full_path) {
+ goto cleanup;
+ }
+
+ /* Don't merge if the External path is already fully qualified */
+
+ if (prefix_path && (*external_path != '\\') && (*external_path != '^')) {
+ strcat(full_path, prefix_path);
+ if (prefix_path[1]) {
+ strcat(full_path, ".");
+ }
+ }
+
+ acpi_ns_normalize_pathname(external_path);
+ strcat(full_path, external_path);
+
+cleanup:
+ if (prefix_path) {
+ ACPI_FREE(prefix_path);
+ }
+ if (external_path) {
+ ACPI_FREE(external_path);
+ }
+
+ return (full_path);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_normalize_pathname
+ *
+ * PARAMETERS: original_path - Path to be normalized, in External format
+ *
+ * RETURN: The original path is processed in-place
+ *
+ * DESCRIPTION: Remove trailing underscores from each element of a path.
+ *
+ * For example: \A___.B___.C___ becomes \A.B.C
+ *
+ ******************************************************************************/
+
+void acpi_ns_normalize_pathname(char *original_path)
+{
+ char *input_path = original_path;
+ char *new_path_buffer;
+ char *new_path;
+ u32 i;
+
+ /* Allocate a temp buffer in which to construct the new path */
+
+ new_path_buffer = ACPI_ALLOCATE_ZEROED(strlen(input_path) + 1);
+ new_path = new_path_buffer;
+ if (!new_path_buffer) {
+ return;
+ }
+
+ /* Special characters may appear at the beginning of the path */
+
+ if (*input_path == '\\') {
+ *new_path = *input_path;
+ new_path++;
+ input_path++;
+ }
+
+ while (*input_path == '^') {
+ *new_path = *input_path;
+ new_path++;
+ input_path++;
+ }
+
+ /* Remainder of the path */
+
+ while (*input_path) {
+
+ /* Do one nameseg at a time */
+
+ for (i = 0; (i < ACPI_NAMESEG_SIZE) && *input_path; i++) {
+ if ((i == 0) || (*input_path != '_')) { /* First char is allowed to be underscore */
+ *new_path = *input_path;
+ new_path++;
+ }
+
+ input_path++;
+ }
+
+ /* Dot means that there are more namesegs to come */
+
+ if (*input_path == '.') {
+ *new_path = *input_path;
+ new_path++;
+ input_path++;
+ }
+ }
+
+ *new_path = 0;
+ strcpy(original_path, new_path_buffer);
+ ACPI_FREE(new_path_buffer);
+}
diff --git a/drivers/acpi/acpica/nsobject.c b/drivers/acpi/acpica/nsobject.c
new file mode 100644
index 000000000..79d86da1c
--- /dev/null
+++ b/drivers/acpi/acpica/nsobject.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: nsobject - Utilities for objects attached to namespace
+ * table entries
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsobject")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_attach_object
+ *
+ * PARAMETERS: node - Parent Node
+ * object - Object to be attached
+ * type - Type of object, or ACPI_TYPE_ANY if not
+ * known
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Record the given object as the value associated with the
+ * name whose acpi_handle is passed. If Object is NULL
+ * and Type is ACPI_TYPE_ANY, set the name as having no value.
+ * Note: Future may require that the Node->Flags field be passed
+ * as a parameter.
+ *
+ * MUTEX: Assumes namespace is locked
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ns_attach_object(struct acpi_namespace_node *node,
+ union acpi_operand_object *object, acpi_object_type type)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *last_obj_desc;
+ acpi_object_type object_type = ACPI_TYPE_ANY;
+
+ ACPI_FUNCTION_TRACE(ns_attach_object);
+
+ /*
+ * Parameter validation
+ */
+ if (!node) {
+
+ /* Invalid handle */
+
+ ACPI_ERROR((AE_INFO, "Null NamedObj handle"));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if (!object && (ACPI_TYPE_ANY != type)) {
+
+ /* Null object */
+
+ ACPI_ERROR((AE_INFO,
+ "Null object, but type not ACPI_TYPE_ANY"));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) {
+
+ /* Not a name handle */
+
+ ACPI_ERROR((AE_INFO, "Invalid handle %p [%s]",
+ node, acpi_ut_get_descriptor_name(node)));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Check if this object is already attached */
+
+ if (node->object == object) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Obj %p already installed in NameObj %p\n",
+ object, node));
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* If null object, we will just install it */
+
+ if (!object) {
+ obj_desc = NULL;
+ object_type = ACPI_TYPE_ANY;
+ }
+
+ /*
+ * If the source object is a namespace Node with an attached object,
+ * we will use that (attached) object
+ */
+ else if ((ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED) &&
+ ((struct acpi_namespace_node *)object)->object) {
+ /*
+ * Value passed is a name handle and that name has a
+ * non-null value. Use that name's value and type.
+ */
+ obj_desc = ((struct acpi_namespace_node *)object)->object;
+ object_type = ((struct acpi_namespace_node *)object)->type;
+ }
+
+ /*
+ * Otherwise, we will use the parameter object, but we must type
+ * it first
+ */
+ else {
+ obj_desc = (union acpi_operand_object *)object;
+
+ /* Use the given type */
+
+ object_type = type;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Installing %p into Node %p [%4.4s]\n",
+ obj_desc, node, acpi_ut_get_node_name(node)));
+
+ /* Detach an existing attached object if present */
+
+ if (node->object) {
+ acpi_ns_detach_object(node);
+ }
+
+ if (obj_desc) {
+ /*
+ * Must increment the new value's reference count
+ * (if it is an internal object)
+ */
+ acpi_ut_add_reference(obj_desc);
+
+ /*
+ * Handle objects with multiple descriptors - walk
+ * to the end of the descriptor list
+ */
+ last_obj_desc = obj_desc;
+ while (last_obj_desc->common.next_object) {
+ last_obj_desc = last_obj_desc->common.next_object;
+ }
+
+ /* Install the object at the front of the object list */
+
+ last_obj_desc->common.next_object = node->object;
+ }
+
+ node->type = (u8) object_type;
+ node->object = obj_desc;
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_detach_object
+ *
+ * PARAMETERS: node - A Namespace node whose object will be detached
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Detach/delete an object associated with a namespace node.
+ * if the object is an allocated object, it is freed.
+ * Otherwise, the field is simply cleared.
+ *
+ ******************************************************************************/
+
+void acpi_ns_detach_object(struct acpi_namespace_node *node)
+{
+ union acpi_operand_object *obj_desc;
+
+ ACPI_FUNCTION_TRACE(ns_detach_object);
+
+ obj_desc = node->object;
+
+ if (!obj_desc || (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA)) {
+ return_VOID;
+ }
+
+ if (node->flags & ANOBJ_ALLOCATED_BUFFER) {
+
+ /* Free the dynamic aml buffer */
+
+ if (obj_desc->common.type == ACPI_TYPE_METHOD) {
+ ACPI_FREE(obj_desc->method.aml_start);
+ }
+ }
+
+ if (obj_desc->common.type == ACPI_TYPE_REGION) {
+ acpi_ut_remove_address_range(obj_desc->region.space_id, node);
+ }
+
+ /* Clear the Node entry in all cases */
+
+ node->object = NULL;
+ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == ACPI_DESC_TYPE_OPERAND) {
+
+ /* Unlink object from front of possible object list */
+
+ node->object = obj_desc->common.next_object;
+
+ /* Handle possible 2-descriptor object */
+
+ if (node->object &&
+ (node->object->common.type != ACPI_TYPE_LOCAL_DATA)) {
+ node->object = node->object->common.next_object;
+ }
+
+ /*
+ * Detach the object from any data objects (which are still held by
+ * the namespace node)
+ */
+ if (obj_desc->common.next_object &&
+ ((obj_desc->common.next_object)->common.type ==
+ ACPI_TYPE_LOCAL_DATA)) {
+ obj_desc->common.next_object = NULL;
+ }
+ }
+
+ /* Reset the node type to untyped */
+
+ node->type = ACPI_TYPE_ANY;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "Node %p [%4.4s] Object %p\n",
+ node, acpi_ut_get_node_name(node), obj_desc));
+
+ /* Remove one reference on the object (and all subobjects) */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_attached_object
+ *
+ * PARAMETERS: node - Namespace node
+ *
+ * RETURN: Current value of the object field from the Node whose
+ * handle is passed
+ *
+ * DESCRIPTION: Obtain the object attached to a namespace node.
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ns_get_attached_object(struct
+ acpi_namespace_node
+ *node)
+{
+ ACPI_FUNCTION_TRACE_PTR(ns_get_attached_object, node);
+
+ if (!node) {
+ ACPI_WARNING((AE_INFO, "Null Node ptr"));
+ return_PTR(NULL);
+ }
+
+ if (!node->object ||
+ ((ACPI_GET_DESCRIPTOR_TYPE(node->object) != ACPI_DESC_TYPE_OPERAND)
+ && (ACPI_GET_DESCRIPTOR_TYPE(node->object) !=
+ ACPI_DESC_TYPE_NAMED))
+ || ((node->object)->common.type == ACPI_TYPE_LOCAL_DATA)) {
+ return_PTR(NULL);
+ }
+
+ return_PTR(node->object);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_secondary_object
+ *
+ * PARAMETERS: node - Namespace node
+ *
+ * RETURN: Current value of the object field from the Node whose
+ * handle is passed.
+ *
+ * DESCRIPTION: Obtain a secondary object associated with a namespace node.
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ns_get_secondary_object(union
+ acpi_operand_object
+ *obj_desc)
+{
+ ACPI_FUNCTION_TRACE_PTR(ns_get_secondary_object, obj_desc);
+
+ if ((!obj_desc) ||
+ (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA) ||
+ (!obj_desc->common.next_object) ||
+ ((obj_desc->common.next_object)->common.type ==
+ ACPI_TYPE_LOCAL_DATA)) {
+ return_PTR(NULL);
+ }
+
+ return_PTR(obj_desc->common.next_object);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_attach_data
+ *
+ * PARAMETERS: node - Namespace node
+ * handler - Handler to be associated with the data
+ * data - Data to be attached
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Low-level attach data. Create and attach a Data object.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_attach_data(struct acpi_namespace_node *node,
+ acpi_object_handler handler, void *data)
+{
+ union acpi_operand_object *prev_obj_desc;
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *data_desc;
+
+ /* We only allow one attachment per handler */
+
+ prev_obj_desc = NULL;
+ obj_desc = node->object;
+ while (obj_desc) {
+ if ((obj_desc->common.type == ACPI_TYPE_LOCAL_DATA) &&
+ (obj_desc->data.handler == handler)) {
+ return (AE_ALREADY_EXISTS);
+ }
+
+ prev_obj_desc = obj_desc;
+ obj_desc = obj_desc->common.next_object;
+ }
+
+ /* Create an internal object for the data */
+
+ data_desc = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_DATA);
+ if (!data_desc) {
+ return (AE_NO_MEMORY);
+ }
+
+ data_desc->data.handler = handler;
+ data_desc->data.pointer = data;
+
+ /* Install the data object */
+
+ if (prev_obj_desc) {
+ prev_obj_desc->common.next_object = data_desc;
+ } else {
+ node->object = data_desc;
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_detach_data
+ *
+ * PARAMETERS: node - Namespace node
+ * handler - Handler associated with the data
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Low-level detach data. Delete the data node, but the caller
+ * is responsible for the actual data.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_detach_data(struct acpi_namespace_node *node,
+ acpi_object_handler handler)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *prev_obj_desc;
+
+ prev_obj_desc = NULL;
+ obj_desc = node->object;
+ while (obj_desc) {
+ if ((obj_desc->common.type == ACPI_TYPE_LOCAL_DATA) &&
+ (obj_desc->data.handler == handler)) {
+ if (prev_obj_desc) {
+ prev_obj_desc->common.next_object =
+ obj_desc->common.next_object;
+ } else {
+ node->object = obj_desc->common.next_object;
+ }
+
+ acpi_ut_remove_reference(obj_desc);
+ return (AE_OK);
+ }
+
+ prev_obj_desc = obj_desc;
+ obj_desc = obj_desc->common.next_object;
+ }
+
+ return (AE_NOT_FOUND);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_attached_data
+ *
+ * PARAMETERS: node - Namespace node
+ * handler - Handler associated with the data
+ * data - Where the data is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Low level interface to obtain data previously associated with
+ * a namespace node.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_get_attached_data(struct acpi_namespace_node *node,
+ acpi_object_handler handler, void **data)
+{
+ union acpi_operand_object *obj_desc;
+
+ obj_desc = node->object;
+ while (obj_desc) {
+ if ((obj_desc->common.type == ACPI_TYPE_LOCAL_DATA) &&
+ (obj_desc->data.handler == handler)) {
+ *data = obj_desc->data.pointer;
+ return (AE_OK);
+ }
+
+ obj_desc = obj_desc->common.next_object;
+ }
+
+ return (AE_NOT_FOUND);
+}
diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c
new file mode 100644
index 000000000..4b893676a
--- /dev/null
+++ b/drivers/acpi/acpica/nsparse.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsparse - namespace interface to AML parser
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acparser.h"
+#include "acdispat.h"
+#include "actables.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsparse")
+
+/*******************************************************************************
+ *
+ * FUNCTION: ns_execute_table
+ *
+ * PARAMETERS: table_desc - An ACPI table descriptor for table to parse
+ * start_node - Where to enter the table into the namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load ACPI/AML table by executing the entire table as a single
+ * large control method.
+ *
+ * NOTE: The point of this is to execute any module-level code in-place
+ * as the table is parsed. Some AML code depends on this behavior.
+ *
+ * It is a run-time option at this time, but will eventually become
+ * the default.
+ *
+ * Note: This causes the table to only have a single-pass parse.
+ * However, this is compatible with other ACPI implementations.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ns_execute_table(u32 table_index, struct acpi_namespace_node *start_node)
+{
+ acpi_status status;
+ struct acpi_table_header *table;
+ acpi_owner_id owner_id;
+ struct acpi_evaluate_info *info = NULL;
+ u32 aml_length;
+ u8 *aml_start;
+ union acpi_operand_object *method_obj = NULL;
+
+ ACPI_FUNCTION_TRACE(ns_execute_table);
+
+ status = acpi_get_table_by_index(table_index, &table);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Table must consist of at least a complete header */
+
+ if (table->length < sizeof(struct acpi_table_header)) {
+ return_ACPI_STATUS(AE_BAD_HEADER);
+ }
+
+ aml_start = (u8 *)table + sizeof(struct acpi_table_header);
+ aml_length = table->length - sizeof(struct acpi_table_header);
+
+ status = acpi_tb_get_owner_id(table_index, &owner_id);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Create, initialize, and link a new temporary method object */
+
+ method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD);
+ if (!method_obj) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Allocate the evaluation information block */
+
+ info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
+ if (!info) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
+ "%s: Create table pseudo-method for [%4.4s] @%p, method %p\n",
+ ACPI_GET_FUNCTION_NAME, table->signature, table,
+ method_obj));
+
+ method_obj->method.aml_start = aml_start;
+ method_obj->method.aml_length = aml_length;
+ method_obj->method.owner_id = owner_id;
+ method_obj->method.info_flags |= ACPI_METHOD_MODULE_LEVEL;
+
+ info->pass_number = ACPI_IMODE_EXECUTE;
+ info->node = start_node;
+ info->obj_desc = method_obj;
+ info->node_flags = info->node->flags;
+ info->full_pathname = acpi_ns_get_normalized_pathname(info->node, TRUE);
+ if (!info->full_pathname) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Optional object evaluation log */
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EVALUATION,
+ "%-26s: (Definition Block level)\n",
+ "Module-level evaluation"));
+
+ status = acpi_ps_execute_table(info);
+
+ /* Optional object evaluation log */
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EVALUATION,
+ "%-26s: (Definition Block level)\n",
+ "Module-level complete"));
+
+cleanup:
+ if (info) {
+ ACPI_FREE(info->full_pathname);
+ info->full_pathname = NULL;
+ }
+ ACPI_FREE(info);
+ acpi_ut_remove_reference(method_obj);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: ns_one_complete_parse
+ *
+ * PARAMETERS: pass_number - 1 or 2
+ * table_desc - The table to be parsed.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform one complete parse of an ACPI/AML table.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_one_complete_parse(u32 pass_number,
+ u32 table_index,
+ struct acpi_namespace_node *start_node)
+{
+ union acpi_parse_object *parse_root;
+ acpi_status status;
+ u32 aml_length;
+ u8 *aml_start;
+ struct acpi_walk_state *walk_state;
+ struct acpi_table_header *table;
+ acpi_owner_id owner_id;
+
+ ACPI_FUNCTION_TRACE(ns_one_complete_parse);
+
+ status = acpi_get_table_by_index(table_index, &table);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Table must consist of at least a complete header */
+
+ if (table->length < sizeof(struct acpi_table_header)) {
+ return_ACPI_STATUS(AE_BAD_HEADER);
+ }
+
+ aml_start = (u8 *)table + sizeof(struct acpi_table_header);
+ aml_length = table->length - sizeof(struct acpi_table_header);
+
+ status = acpi_tb_get_owner_id(table_index, &owner_id);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Create and init a Root Node */
+
+ parse_root = acpi_ps_create_scope_op(aml_start);
+ if (!parse_root) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Create and initialize a new walk state */
+
+ walk_state = acpi_ds_create_walk_state(owner_id, NULL, NULL, NULL);
+ if (!walk_state) {
+ acpi_ps_free_op(parse_root);
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ status = acpi_ds_init_aml_walk(walk_state, parse_root, NULL,
+ aml_start, aml_length, NULL,
+ (u8)pass_number);
+ if (ACPI_FAILURE(status)) {
+ acpi_ds_delete_walk_state(walk_state);
+ goto cleanup;
+ }
+
+ /* Found OSDT table, enable the namespace override feature */
+
+ if (ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_OSDT) &&
+ pass_number == ACPI_IMODE_LOAD_PASS1) {
+ walk_state->namespace_override = TRUE;
+ }
+
+ /* start_node is the default location to load the table */
+
+ if (start_node && start_node != acpi_gbl_root_node) {
+ status =
+ acpi_ds_scope_stack_push(start_node, ACPI_TYPE_METHOD,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ acpi_ds_delete_walk_state(walk_state);
+ goto cleanup;
+ }
+ }
+
+ /* Parse the AML */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "*PARSE* pass %u parse\n", pass_number));
+ acpi_ex_enter_interpreter();
+ status = acpi_ps_parse_aml(walk_state);
+ acpi_ex_exit_interpreter();
+
+cleanup:
+ acpi_ps_delete_parse_tree(parse_root);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_parse_table
+ *
+ * PARAMETERS: table_desc - An ACPI table descriptor for table to parse
+ * start_node - Where to enter the table into the namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Parse AML within an ACPI table and return a tree of ops
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ns_parse_table);
+
+ /*
+ * Executes the AML table as one large control method.
+ * The point of this is to execute any module-level code in-place
+ * as the table is parsed. Some AML code depends on this behavior.
+ *
+ * Note: This causes the table to only have a single-pass parse.
+ * However, this is compatible with other ACPI implementations.
+ */
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
+ "%s: **** Start table execution pass\n",
+ ACPI_GET_FUNCTION_NAME));
+
+ status = acpi_ns_execute_table(table_index, start_node);
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c
new file mode 100644
index 000000000..c0db6690b
--- /dev/null
+++ b/drivers/acpi/acpica/nspredef.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nspredef - Validation of ACPI predefined methods and objects
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define ACPI_CREATE_PREDEFINED_TABLE
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acpredef.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nspredef")
+
+/*******************************************************************************
+ *
+ * This module validates predefined ACPI objects that appear in the namespace,
+ * at the time they are evaluated (via acpi_evaluate_object). The purpose of this
+ * validation is to detect problems with BIOS-exposed predefined ACPI objects
+ * before the results are returned to the ACPI-related drivers.
+ *
+ * There are several areas that are validated:
+ *
+ * 1) The number of input arguments as defined by the method/object in the
+ * ASL is validated against the ACPI specification.
+ * 2) The type of the return object (if any) is validated against the ACPI
+ * specification.
+ * 3) For returned package objects, the count of package elements is
+ * validated, as well as the type of each package element. Nested
+ * packages are supported.
+ *
+ * For any problems found, a warning message is issued.
+ *
+ ******************************************************************************/
+/* Local prototypes */
+static acpi_status
+acpi_ns_check_reference(struct acpi_evaluate_info *info,
+ union acpi_operand_object *return_object);
+
+static u32 acpi_ns_get_bitmapped_type(union acpi_operand_object *return_object);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_return_value
+ *
+ * PARAMETERS: node - Namespace node for the method/object
+ * info - Method execution information block
+ * user_param_count - Number of parameters actually passed
+ * return_status - Status from the object evaluation
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check the value returned from a predefined name.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_check_return_value(struct acpi_namespace_node *node,
+ struct acpi_evaluate_info *info,
+ u32 user_param_count,
+ acpi_status return_status,
+ union acpi_operand_object **return_object_ptr)
+{
+ acpi_status status;
+ const union acpi_predefined_info *predefined;
+
+ ACPI_FUNCTION_TRACE(ns_check_return_value);
+
+ /* If not a predefined name, we cannot validate the return object */
+
+ predefined = info->predefined;
+ if (!predefined) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * If the method failed or did not actually return an object, we cannot
+ * validate the return object
+ */
+ if ((return_status != AE_OK) && (return_status != AE_CTRL_RETURN_VALUE)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Return value validation and possible repair.
+ *
+ * 1) Don't perform return value validation/repair if this feature
+ * has been disabled via a global option.
+ *
+ * 2) We have a return value, but if one wasn't expected, just exit,
+ * this is not a problem. For example, if the "Implicit Return"
+ * feature is enabled, methods will always return a value.
+ *
+ * 3) If the return value can be of any type, then we cannot perform
+ * any validation, just exit.
+ */
+ if (acpi_gbl_disable_auto_repair ||
+ (!predefined->info.expected_btypes) ||
+ (predefined->info.expected_btypes == ACPI_RTYPE_ALL)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Check that the type of the main return object is what is expected
+ * for this predefined name
+ */
+ status = acpi_ns_check_object_type(info, return_object_ptr,
+ predefined->info.expected_btypes,
+ ACPI_NOT_PACKAGE_ELEMENT);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /*
+ *
+ * 4) If there is no return value and it is optional, just return
+ * AE_OK (_WAK).
+ */
+ if (!(*return_object_ptr)) {
+ goto exit;
+ }
+
+ /*
+ * For returned Package objects, check the type of all sub-objects.
+ * Note: Package may have been newly created by call above.
+ */
+ if ((*return_object_ptr)->common.type == ACPI_TYPE_PACKAGE) {
+ info->parent_package = *return_object_ptr;
+ status = acpi_ns_check_package(info, return_object_ptr);
+ if (ACPI_FAILURE(status)) {
+
+ /* We might be able to fix some errors */
+
+ if ((status != AE_AML_OPERAND_TYPE) &&
+ (status != AE_AML_OPERAND_VALUE)) {
+ goto exit;
+ }
+ }
+ }
+
+ /*
+ * The return object was OK, or it was successfully repaired above.
+ * Now make some additional checks such as verifying that package
+ * objects are sorted correctly (if required) or buffer objects have
+ * the correct data width (bytes vs. dwords). These repairs are
+ * performed on a per-name basis, i.e., the code is specific to
+ * particular predefined names.
+ */
+ status = acpi_ns_complex_repairs(info, node, status, return_object_ptr);
+
+exit:
+ /*
+ * If the object validation failed or if we successfully repaired one
+ * or more objects, mark the parent node to suppress further warning
+ * messages during the next evaluation of the same method/object.
+ */
+ if (ACPI_FAILURE(status) || (info->return_flags & ACPI_OBJECT_REPAIRED)) {
+ node->flags |= ANOBJ_EVALUATED;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_object_type
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ * expected_btypes - Bitmap of expected return type(s)
+ * package_index - Index of object within parent package (if
+ * applicable - ACPI_NOT_PACKAGE_ELEMENT
+ * otherwise)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check the type of the return object against the expected object
+ * type(s). Use of Btype allows multiple expected object types.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_check_object_type(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr,
+ u32 expected_btypes, u32 package_index)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ acpi_status status = AE_OK;
+ char type_buffer[96]; /* Room for 10 types */
+
+ /* A Namespace node should not get here, but make sure */
+
+ if (return_object &&
+ ACPI_GET_DESCRIPTOR_TYPE(return_object) == ACPI_DESC_TYPE_NAMED) {
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ info->node_flags,
+ "Invalid return type - Found a Namespace node [%4.4s] type %s",
+ return_object->node.name.ascii,
+ acpi_ut_get_type_name(return_object->node.
+ type)));
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * Convert the object type (ACPI_TYPE_xxx) to a bitmapped object type.
+ * The bitmapped type allows multiple possible return types.
+ *
+ * Note, the cases below must handle all of the possible types returned
+ * from all of the predefined names (including elements of returned
+ * packages)
+ */
+ info->return_btype = acpi_ns_get_bitmapped_type(return_object);
+ if (info->return_btype == ACPI_RTYPE_ANY) {
+
+ /* Not one of the supported objects, must be incorrect */
+ goto type_error_exit;
+ }
+
+ /* For reference objects, check that the reference type is correct */
+
+ if ((info->return_btype & expected_btypes) == ACPI_RTYPE_REFERENCE) {
+ status = acpi_ns_check_reference(info, return_object);
+ return (status);
+ }
+
+ /* Attempt simple repair of the returned object if necessary */
+
+ status = acpi_ns_simple_repair(info, expected_btypes,
+ package_index, return_object_ptr);
+ if (ACPI_SUCCESS(status)) {
+ return (AE_OK); /* Successful repair */
+ }
+
+type_error_exit:
+
+ /* Create a string with all expected types for this predefined object */
+
+ acpi_ut_get_expected_return_types(type_buffer, expected_btypes);
+
+ if (!return_object) {
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ info->node_flags,
+ "Expected return object of type %s",
+ type_buffer));
+ } else if (package_index == ACPI_NOT_PACKAGE_ELEMENT) {
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ info->node_flags,
+ "Return type mismatch - found %s, expected %s",
+ acpi_ut_get_object_type_name
+ (return_object), type_buffer));
+ } else {
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ info->node_flags,
+ "Return Package type mismatch at index %u - "
+ "found %s, expected %s", package_index,
+ acpi_ut_get_object_type_name
+ (return_object), type_buffer));
+ }
+
+ return (AE_AML_OPERAND_TYPE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_reference
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object - Object returned from the evaluation of a
+ * method or object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check a returned reference object for the correct reference
+ * type. The only reference type that can be returned from a
+ * predefined method is a named reference. All others are invalid.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_check_reference(struct acpi_evaluate_info *info,
+ union acpi_operand_object *return_object)
+{
+
+ /*
+ * Check the reference object for the correct reference type (opcode).
+ * The only type of reference that can be converted to a union acpi_object is
+ * a reference to a named object (reference class: NAME)
+ */
+ if (return_object->reference.class == ACPI_REFCLASS_NAME) {
+ return (AE_OK);
+ }
+
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags,
+ "Return type mismatch - unexpected reference object type [%s] %2.2X",
+ acpi_ut_get_reference_name(return_object),
+ return_object->reference.class));
+
+ return (AE_AML_OPERAND_TYPE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_bitmapped_type
+ *
+ * PARAMETERS: return_object - Object returned from method/obj evaluation
+ *
+ * RETURN: Object return type. ACPI_RTYPE_ANY indicates that the object
+ * type is not supported. ACPI_RTYPE_NONE indicates that no
+ * object was returned (return_object is NULL).
+ *
+ * DESCRIPTION: Convert object type into a bitmapped object return type.
+ *
+ ******************************************************************************/
+
+static u32 acpi_ns_get_bitmapped_type(union acpi_operand_object *return_object)
+{
+ u32 return_btype;
+
+ if (!return_object) {
+ return (ACPI_RTYPE_NONE);
+ }
+
+ /* Map acpi_object_type to internal bitmapped type */
+
+ switch (return_object->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ return_btype = ACPI_RTYPE_INTEGER;
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ return_btype = ACPI_RTYPE_BUFFER;
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ return_btype = ACPI_RTYPE_STRING;
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ return_btype = ACPI_RTYPE_PACKAGE;
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ return_btype = ACPI_RTYPE_REFERENCE;
+ break;
+
+ default:
+
+ /* Not one of the supported objects, must be incorrect */
+
+ return_btype = ACPI_RTYPE_ANY;
+ break;
+ }
+
+ return (return_btype);
+}
diff --git a/drivers/acpi/acpica/nsprepkg.c b/drivers/acpi/acpica/nsprepkg.c
new file mode 100644
index 000000000..82932c9a7
--- /dev/null
+++ b/drivers/acpi/acpica/nsprepkg.c
@@ -0,0 +1,740 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsprepkg - Validation of package objects for predefined names
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acpredef.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsprepkg")
+
+/* Local prototypes */
+static acpi_status
+acpi_ns_check_package_list(struct acpi_evaluate_info *info,
+ const union acpi_predefined_info *package,
+ union acpi_operand_object **elements, u32 count);
+
+static acpi_status
+acpi_ns_check_package_elements(struct acpi_evaluate_info *info,
+ union acpi_operand_object **elements,
+ u8 type1,
+ u32 count1,
+ u8 type2, u32 count2, u32 start_index);
+
+static acpi_status
+acpi_ns_custom_package(struct acpi_evaluate_info *info,
+ union acpi_operand_object **elements, u32 count);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_package
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check a returned package object for the correct count and
+ * correct type of all sub-objects.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_check_package(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ const union acpi_predefined_info *package;
+ union acpi_operand_object **elements;
+ acpi_status status = AE_OK;
+ u32 expected_count;
+ u32 count;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ns_check_package);
+
+ /* The package info for this name is in the next table entry */
+
+ package = info->predefined + 1;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "%s Validating return Package of Type %X, Count %X\n",
+ info->full_pathname, package->ret_info.type,
+ return_object->package.count));
+
+ /*
+ * For variable-length Packages, we can safely remove all embedded
+ * and trailing NULL package elements
+ */
+ acpi_ns_remove_null_elements(info, package->ret_info.type,
+ return_object);
+
+ /* Extract package count and elements array */
+
+ elements = return_object->package.elements;
+ count = return_object->package.count;
+
+ /*
+ * Most packages must have at least one element. The only exception
+ * is the variable-length package (ACPI_PTYPE1_VAR).
+ */
+ if (!count) {
+ if (package->ret_info.type == ACPI_PTYPE1_VAR) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ info->node_flags,
+ "Return Package has no elements (empty)"));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
+ }
+
+ /*
+ * Decode the type of the expected package contents
+ *
+ * PTYPE1 packages contain no subpackages
+ * PTYPE2 packages contain subpackages
+ */
+ switch (package->ret_info.type) {
+ case ACPI_PTYPE_CUSTOM:
+
+ status = acpi_ns_custom_package(info, elements, count);
+ break;
+
+ case ACPI_PTYPE1_FIXED:
+ /*
+ * The package count is fixed and there are no subpackages
+ *
+ * If package is too small, exit.
+ * If package is larger than expected, issue warning but continue
+ */
+ expected_count =
+ package->ret_info.count1 + package->ret_info.count2;
+ if (count < expected_count) {
+ goto package_too_small;
+ } else if (count > expected_count) {
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Return Package is larger than needed - "
+ "found %u, expected %u\n",
+ info->full_pathname, count,
+ expected_count));
+ }
+
+ /* Validate all elements of the returned package */
+
+ status = acpi_ns_check_package_elements(info, elements,
+ package->ret_info.
+ object_type1,
+ package->ret_info.
+ count1,
+ package->ret_info.
+ object_type2,
+ package->ret_info.
+ count2, 0);
+ break;
+
+ case ACPI_PTYPE1_VAR:
+ /*
+ * The package count is variable, there are no subpackages, and all
+ * elements must be of the same type
+ */
+ for (i = 0; i < count; i++) {
+ status = acpi_ns_check_object_type(info, elements,
+ package->ret_info.
+ object_type1, i);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ elements++;
+ }
+ break;
+
+ case ACPI_PTYPE1_OPTION:
+ /*
+ * The package count is variable, there are no subpackages. There are
+ * a fixed number of required elements, and a variable number of
+ * optional elements.
+ *
+ * Check if package is at least as large as the minimum required
+ */
+ expected_count = package->ret_info3.count;
+ if (count < expected_count) {
+ goto package_too_small;
+ }
+
+ /* Variable number of sub-objects */
+
+ for (i = 0; i < count; i++) {
+ if (i < package->ret_info3.count) {
+
+ /* These are the required package elements (0, 1, or 2) */
+
+ status =
+ acpi_ns_check_object_type(info, elements,
+ package->
+ ret_info3.
+ object_type[i],
+ i);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ } else {
+ /* These are the optional package elements */
+
+ status =
+ acpi_ns_check_object_type(info, elements,
+ package->
+ ret_info3.
+ tail_object_type,
+ i);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ elements++;
+ }
+ break;
+
+ case ACPI_PTYPE2_REV_FIXED:
+
+ /* First element is the (Integer) revision */
+
+ status =
+ acpi_ns_check_object_type(info, elements,
+ ACPI_RTYPE_INTEGER, 0);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ elements++;
+ count--;
+
+ /* Examine the subpackages */
+
+ status =
+ acpi_ns_check_package_list(info, package, elements, count);
+ break;
+
+ case ACPI_PTYPE2_PKG_COUNT:
+
+ /* First element is the (Integer) count of subpackages to follow */
+
+ status =
+ acpi_ns_check_object_type(info, elements,
+ ACPI_RTYPE_INTEGER, 0);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Count cannot be larger than the parent package length, but allow it
+ * to be smaller. The >= accounts for the Integer above.
+ */
+ expected_count = (u32)(*elements)->integer.value;
+ if (expected_count >= count) {
+ goto package_too_small;
+ }
+
+ count = expected_count;
+ elements++;
+
+ /* Examine the subpackages */
+
+ status =
+ acpi_ns_check_package_list(info, package, elements, count);
+ break;
+
+ case ACPI_PTYPE2:
+ case ACPI_PTYPE2_FIXED:
+ case ACPI_PTYPE2_MIN:
+ case ACPI_PTYPE2_COUNT:
+ case ACPI_PTYPE2_FIX_VAR:
+ /*
+ * These types all return a single Package that consists of a
+ * variable number of subpackages.
+ *
+ * First, ensure that the first element is a subpackage. If not,
+ * the BIOS may have incorrectly returned the object as a single
+ * package instead of a Package of Packages (a common error if
+ * there is only one entry). We may be able to repair this by
+ * wrapping the returned Package with a new outer Package.
+ */
+ if (*elements
+ && ((*elements)->common.type != ACPI_TYPE_PACKAGE)) {
+
+ /* Create the new outer package and populate it */
+
+ status =
+ acpi_ns_wrap_with_package(info, return_object,
+ return_object_ptr);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Update locals to point to the new package (of 1 element) */
+
+ return_object = *return_object_ptr;
+ elements = return_object->package.elements;
+ count = 1;
+ }
+
+ /* Examine the subpackages */
+
+ status =
+ acpi_ns_check_package_list(info, package, elements, count);
+ break;
+
+ case ACPI_PTYPE2_VAR_VAR:
+ /*
+ * Returns a variable list of packages, each with a variable list
+ * of objects.
+ */
+ break;
+
+ case ACPI_PTYPE2_UUID_PAIR:
+
+ /* The package must contain pairs of (UUID + type) */
+
+ if (count & 1) {
+ expected_count = count + 1;
+ goto package_too_small;
+ }
+
+ while (count > 0) {
+ status = acpi_ns_check_object_type(info, elements,
+ package->ret_info.
+ object_type1, 0);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Validate length of the UUID buffer */
+
+ if ((*elements)->buffer.length != 16) {
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname,
+ info->node_flags,
+ "Invalid length for UUID Buffer"));
+ return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
+ }
+
+ status = acpi_ns_check_object_type(info, elements + 1,
+ package->ret_info.
+ object_type2, 0);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ elements += 2;
+ count -= 2;
+ }
+ break;
+
+ default:
+
+ /* Should not get here if predefined info table is correct */
+
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ info->node_flags,
+ "Invalid internal return type in table entry: %X",
+ package->ret_info.type));
+
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ return_ACPI_STATUS(status);
+
+package_too_small:
+
+ /* Error exit for the case with an incorrect package count */
+
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags,
+ "Return Package is too small - found %u elements, expected %u",
+ count, expected_count));
+
+ return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_package_list
+ *
+ * PARAMETERS: info - Method execution information block
+ * package - Pointer to package-specific info for method
+ * elements - Element list of parent package. All elements
+ * of this list should be of type Package.
+ * count - Count of subpackages
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Examine a list of subpackages
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_check_package_list(struct acpi_evaluate_info *info,
+ const union acpi_predefined_info *package,
+ union acpi_operand_object **elements, u32 count)
+{
+ union acpi_operand_object *sub_package;
+ union acpi_operand_object **sub_elements;
+ acpi_status status;
+ u32 expected_count;
+ u32 i;
+ u32 j;
+
+ /*
+ * Validate each subpackage in the parent Package
+ *
+ * NOTE: assumes list of subpackages contains no NULL elements.
+ * Any NULL elements should have been removed by earlier call
+ * to acpi_ns_remove_null_elements.
+ */
+ for (i = 0; i < count; i++) {
+ sub_package = *elements;
+ sub_elements = sub_package->package.elements;
+ info->parent_package = sub_package;
+
+ /* Each sub-object must be of type Package */
+
+ status = acpi_ns_check_object_type(info, &sub_package,
+ ACPI_RTYPE_PACKAGE, i);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Examine the different types of expected subpackages */
+
+ info->parent_package = sub_package;
+ switch (package->ret_info.type) {
+ case ACPI_PTYPE2:
+ case ACPI_PTYPE2_PKG_COUNT:
+ case ACPI_PTYPE2_REV_FIXED:
+
+ /* Each subpackage has a fixed number of elements */
+
+ expected_count =
+ package->ret_info.count1 + package->ret_info.count2;
+ if (sub_package->package.count < expected_count) {
+ goto package_too_small;
+ }
+
+ status =
+ acpi_ns_check_package_elements(info, sub_elements,
+ package->ret_info.
+ object_type1,
+ package->ret_info.
+ count1,
+ package->ret_info.
+ object_type2,
+ package->ret_info.
+ count2, 0);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
+
+ case ACPI_PTYPE2_FIX_VAR:
+ /*
+ * Each subpackage has a fixed number of elements and an
+ * optional element
+ */
+ expected_count =
+ package->ret_info.count1 + package->ret_info.count2;
+ if (sub_package->package.count < expected_count) {
+ goto package_too_small;
+ }
+
+ status =
+ acpi_ns_check_package_elements(info, sub_elements,
+ package->ret_info.
+ object_type1,
+ package->ret_info.
+ count1,
+ package->ret_info.
+ object_type2,
+ sub_package->package.
+ count -
+ package->ret_info.
+ count1, 0);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
+
+ case ACPI_PTYPE2_VAR_VAR:
+ /*
+ * Each subpackage has a fixed or variable number of elements
+ */
+ break;
+
+ case ACPI_PTYPE2_FIXED:
+
+ /* Each subpackage has a fixed length */
+
+ expected_count = package->ret_info2.count;
+ if (sub_package->package.count < expected_count) {
+ goto package_too_small;
+ }
+
+ /* Check the type of each subpackage element */
+
+ for (j = 0; j < expected_count; j++) {
+ status =
+ acpi_ns_check_object_type(info,
+ &sub_elements[j],
+ package->
+ ret_info2.
+ object_type[j],
+ j);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+ break;
+
+ case ACPI_PTYPE2_MIN:
+
+ /* Each subpackage has a variable but minimum length */
+
+ expected_count = package->ret_info.count1;
+ if (sub_package->package.count < expected_count) {
+ goto package_too_small;
+ }
+
+ /* Check the type of each subpackage element */
+
+ status =
+ acpi_ns_check_package_elements(info, sub_elements,
+ package->ret_info.
+ object_type1,
+ sub_package->package.
+ count, 0, 0, 0);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
+
+ case ACPI_PTYPE2_COUNT:
+ /*
+ * First element is the (Integer) count of elements, including
+ * the count field (the ACPI name is num_elements)
+ */
+ status = acpi_ns_check_object_type(info, sub_elements,
+ ACPI_RTYPE_INTEGER,
+ 0);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /*
+ * Make sure package is large enough for the Count and is
+ * is as large as the minimum size
+ */
+ expected_count = (u32)(*sub_elements)->integer.value;
+ if (sub_package->package.count < expected_count) {
+ goto package_too_small;
+ }
+
+ if (sub_package->package.count <
+ package->ret_info.count1) {
+ expected_count = package->ret_info.count1;
+ goto package_too_small;
+ }
+
+ if (expected_count == 0) {
+ /*
+ * Either the num_entries element was originally zero or it was
+ * a NULL element and repaired to an Integer of value zero.
+ * In either case, repair it by setting num_entries to be the
+ * actual size of the subpackage.
+ */
+ expected_count = sub_package->package.count;
+ (*sub_elements)->integer.value = expected_count;
+ }
+
+ /* Check the type of each subpackage element */
+
+ status =
+ acpi_ns_check_package_elements(info,
+ (sub_elements + 1),
+ package->ret_info.
+ object_type1,
+ (expected_count - 1),
+ 0, 0, 1);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
+
+ default: /* Should not get here, type was validated by caller */
+
+ ACPI_ERROR((AE_INFO, "Invalid Package type: %X",
+ package->ret_info.type));
+ return (AE_AML_INTERNAL);
+ }
+
+ elements++;
+ }
+
+ return (AE_OK);
+
+package_too_small:
+
+ /* The subpackage count was smaller than required */
+
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags,
+ "Return SubPackage[%u] is too small - found %u elements, expected %u",
+ i, sub_package->package.count, expected_count));
+
+ return (AE_AML_OPERAND_VALUE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_custom_package
+ *
+ * PARAMETERS: info - Method execution information block
+ * elements - Pointer to the package elements array
+ * count - Element count for the package
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check a returned package object for the correct count and
+ * correct type of all sub-objects.
+ *
+ * NOTE: Currently used for the _BIX method only. When needed for two or more
+ * methods, probably a detect/dispatch mechanism will be required.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_custom_package(struct acpi_evaluate_info *info,
+ union acpi_operand_object **elements, u32 count)
+{
+ u32 expected_count;
+ u32 version;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_NAME(ns_custom_package);
+
+ /* Get version number, must be Integer */
+
+ if ((*elements)->common.type != ACPI_TYPE_INTEGER) {
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ info->node_flags,
+ "Return Package has invalid object type for version number"));
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ version = (u32)(*elements)->integer.value;
+ expected_count = 21; /* Version 1 */
+
+ if (version == 0) {
+ expected_count = 20; /* Version 0 */
+ }
+
+ if (count < expected_count) {
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ info->node_flags,
+ "Return Package is too small - found %u elements, expected %u",
+ count, expected_count));
+ return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
+ } else if (count > expected_count) {
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Return Package is larger than needed - "
+ "found %u, expected %u\n",
+ info->full_pathname, count, expected_count));
+ }
+
+ /* Validate all elements of the returned package */
+
+ status = acpi_ns_check_package_elements(info, elements,
+ ACPI_RTYPE_INTEGER, 16,
+ ACPI_RTYPE_STRING, 4, 0);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Version 1 has a single trailing integer */
+
+ if (version > 0) {
+ status = acpi_ns_check_package_elements(info, elements + 20,
+ ACPI_RTYPE_INTEGER, 1,
+ 0, 0, 20);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_package_elements
+ *
+ * PARAMETERS: info - Method execution information block
+ * elements - Pointer to the package elements array
+ * type1 - Object type for first group
+ * count1 - Count for first group
+ * type2 - Object type for second group
+ * count2 - Count for second group
+ * start_index - Start of the first group of elements
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check that all elements of a package are of the correct object
+ * type. Supports up to two groups of different object types.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_check_package_elements(struct acpi_evaluate_info *info,
+ union acpi_operand_object **elements,
+ u8 type1,
+ u32 count1,
+ u8 type2, u32 count2, u32 start_index)
+{
+ union acpi_operand_object **this_element = elements;
+ acpi_status status;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ns_check_package_elements);
+
+ /*
+ * Up to two groups of package elements are supported by the data
+ * structure. All elements in each group must be of the same type.
+ * The second group can have a count of zero.
+ */
+ for (i = 0; i < count1; i++) {
+ status = acpi_ns_check_object_type(info, this_element,
+ type1, i + start_index);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ this_element++;
+ }
+
+ for (i = 0; i < count2; i++) {
+ status = acpi_ns_check_object_type(info, this_element,
+ type2,
+ (i + count1 + start_index));
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ this_element++;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c
new file mode 100644
index 000000000..ec512e06a
--- /dev/null
+++ b/drivers/acpi/acpica/nsrepair.c
@@ -0,0 +1,562 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsrepair - Repair for objects returned by predefined methods
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+#include "acpredef.h"
+#include "amlresrc.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsrepair")
+
+/*******************************************************************************
+ *
+ * This module attempts to repair or convert objects returned by the
+ * predefined methods to an object type that is expected, as per the ACPI
+ * specification. The need for this code is dictated by the many machines that
+ * return incorrect types for the standard predefined methods. Performing these
+ * conversions here, in one place, eliminates the need for individual ACPI
+ * device drivers to do the same. Note: Most of these conversions are different
+ * than the internal object conversion routines used for implicit object
+ * conversion.
+ *
+ * The following conversions can be performed as necessary:
+ *
+ * Integer -> String
+ * Integer -> Buffer
+ * String -> Integer
+ * String -> Buffer
+ * Buffer -> Integer
+ * Buffer -> String
+ * Buffer -> Package of Integers
+ * Package -> Package of one Package
+ *
+ * Additional conversions that are available:
+ * Convert a null return or zero return value to an end_tag descriptor
+ * Convert an ASCII string to a Unicode buffer
+ *
+ * An incorrect standalone object is wrapped with required outer package
+ *
+ * Additional possible repairs:
+ * Required package elements that are NULL replaced by Integer/String/Buffer
+ *
+ ******************************************************************************/
+/* Local prototypes */
+static const struct acpi_simple_repair_info *acpi_ns_match_simple_repair(struct
+ acpi_namespace_node
+ *node,
+ u32
+ return_btype,
+ u32
+ package_index);
+
+/*
+ * Special but simple repairs for some names.
+ *
+ * 2nd argument: Unexpected types that can be repaired
+ */
+static const struct acpi_simple_repair_info acpi_object_repair_info[] = {
+ /* Resource descriptor conversions */
+
+ {"_CRS",
+ ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER |
+ ACPI_RTYPE_NONE,
+ ACPI_NOT_PACKAGE_ELEMENT,
+ acpi_ns_convert_to_resource},
+ {"_DMA",
+ ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER |
+ ACPI_RTYPE_NONE,
+ ACPI_NOT_PACKAGE_ELEMENT,
+ acpi_ns_convert_to_resource},
+ {"_PRS",
+ ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER |
+ ACPI_RTYPE_NONE,
+ ACPI_NOT_PACKAGE_ELEMENT,
+ acpi_ns_convert_to_resource},
+
+ /* Object reference conversions */
+
+ {"_DEP", ACPI_RTYPE_STRING, ACPI_ALL_PACKAGE_ELEMENTS,
+ acpi_ns_convert_to_reference},
+
+ /* Unicode conversions */
+
+ {"_MLS", ACPI_RTYPE_STRING, 1,
+ acpi_ns_convert_to_unicode},
+ {"_STR", ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER,
+ ACPI_NOT_PACKAGE_ELEMENT,
+ acpi_ns_convert_to_unicode},
+ {{0, 0, 0, 0}, 0, 0, NULL} /* Table terminator */
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_simple_repair
+ *
+ * PARAMETERS: info - Method execution information block
+ * expected_btypes - Object types expected
+ * package_index - Index of object within parent package (if
+ * applicable - ACPI_NOT_PACKAGE_ELEMENT
+ * otherwise)
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if repair was successful.
+ *
+ * DESCRIPTION: Attempt to repair/convert a return object of a type that was
+ * not expected.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_simple_repair(struct acpi_evaluate_info *info,
+ u32 expected_btypes,
+ u32 package_index,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ union acpi_operand_object *new_object = NULL;
+ acpi_status status;
+ const struct acpi_simple_repair_info *predefined;
+
+ ACPI_FUNCTION_NAME(ns_simple_repair);
+
+ /*
+ * Special repairs for certain names that are in the repair table.
+ * Check if this name is in the list of repairable names.
+ */
+ predefined = acpi_ns_match_simple_repair(info->node,
+ info->return_btype,
+ package_index);
+ if (predefined) {
+ if (!return_object) {
+ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ ACPI_WARN_ALWAYS,
+ "Missing expected return value"));
+ }
+
+ status = predefined->object_converter(info->node, return_object,
+ &new_object);
+ if (ACPI_FAILURE(status)) {
+
+ /* A fatal error occurred during a conversion */
+
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During return object analysis"));
+ return (status);
+ }
+ if (new_object) {
+ goto object_repaired;
+ }
+ }
+
+ /*
+ * Do not perform simple object repair unless the return type is not
+ * expected.
+ */
+ if (info->return_btype & expected_btypes) {
+ return (AE_OK);
+ }
+
+ /*
+ * At this point, we know that the type of the returned object was not
+ * one of the expected types for this predefined name. Attempt to
+ * repair the object by converting it to one of the expected object
+ * types for this predefined name.
+ */
+
+ /*
+ * If there is no return value, check if we require a return value for
+ * this predefined name. Either one return value is expected, or none,
+ * for both methods and other objects.
+ *
+ * Try to fix if there was no return object. Warning if failed to fix.
+ */
+ if (!return_object) {
+ 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,
+ "Found unexpected NULL package element"));
+
+ status =
+ acpi_ns_repair_null_element(info,
+ expected_btypes,
+ package_index,
+ return_object_ptr);
+ if (ACPI_SUCCESS(status)) {
+ return (AE_OK); /* Repair was successful */
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ if (expected_btypes & ACPI_RTYPE_INTEGER) {
+ status = acpi_ns_convert_to_integer(return_object, &new_object);
+ if (ACPI_SUCCESS(status)) {
+ goto object_repaired;
+ }
+ }
+ if (expected_btypes & ACPI_RTYPE_STRING) {
+ status = acpi_ns_convert_to_string(return_object, &new_object);
+ if (ACPI_SUCCESS(status)) {
+ goto object_repaired;
+ }
+ }
+ if (expected_btypes & ACPI_RTYPE_BUFFER) {
+ status = acpi_ns_convert_to_buffer(return_object, &new_object);
+ if (ACPI_SUCCESS(status)) {
+ goto object_repaired;
+ }
+ }
+ if (expected_btypes & ACPI_RTYPE_PACKAGE) {
+ /*
+ * A package is expected. We will wrap the existing object with a
+ * new package object. It is often the case that if a variable-length
+ * package is required, but there is only a single object needed, the
+ * BIOS will return that object instead of wrapping it with a Package
+ * object. Note: after the wrapping, the package will be validated
+ * for correct contents (expected object type or types).
+ */
+ status =
+ acpi_ns_wrap_with_package(info, return_object, &new_object);
+ if (ACPI_SUCCESS(status)) {
+ /*
+ * The original object just had its reference count
+ * incremented for being inserted into the new package.
+ */
+ *return_object_ptr = new_object; /* New Package object */
+ info->return_flags |= ACPI_OBJECT_REPAIRED;
+ return (AE_OK);
+ }
+ }
+
+ /* We cannot repair this object */
+
+ return (AE_AML_OPERAND_TYPE);
+
+object_repaired:
+
+ /* Object was successfully repaired */
+
+ if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
+
+ /* Update reference count of new object */
+
+ if (!(info->return_flags & ACPI_OBJECT_WRAPPED)) {
+ new_object->common.reference_count =
+ return_object->common.reference_count;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Converted %s to expected %s at Package index %u\n",
+ info->full_pathname,
+ acpi_ut_get_object_type_name(return_object),
+ acpi_ut_get_object_type_name(new_object),
+ package_index));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Converted %s to expected %s\n",
+ info->full_pathname,
+ acpi_ut_get_object_type_name(return_object),
+ acpi_ut_get_object_type_name(new_object)));
+ }
+
+ /* Delete old object, install the new return object */
+
+ acpi_ut_remove_reference(return_object);
+ *return_object_ptr = new_object;
+ info->return_flags |= ACPI_OBJECT_REPAIRED;
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_match_simple_repair
+ *
+ * PARAMETERS: node - Namespace node for the method/object
+ * return_btype - Object type that was returned
+ * package_index - Index of object within parent package (if
+ * applicable - ACPI_NOT_PACKAGE_ELEMENT
+ * otherwise)
+ *
+ * RETURN: Pointer to entry in repair table. NULL indicates not found.
+ *
+ * DESCRIPTION: Check an object name against the repairable object list.
+ *
+ *****************************************************************************/
+
+static const struct acpi_simple_repair_info *acpi_ns_match_simple_repair(struct
+ acpi_namespace_node
+ *node,
+ u32
+ return_btype,
+ u32
+ package_index)
+{
+ const struct acpi_simple_repair_info *this_name;
+
+ /* Search info table for a repairable predefined method/object name */
+
+ this_name = acpi_object_repair_info;
+ while (this_name->object_converter) {
+ if (ACPI_COMPARE_NAMESEG(node->name.ascii, this_name->name)) {
+
+ /* Check if we can actually repair this name/type combination */
+
+ if ((return_btype & this_name->unexpected_btypes) &&
+ (this_name->package_index ==
+ ACPI_ALL_PACKAGE_ELEMENTS
+ || package_index == this_name->package_index)) {
+ return (this_name);
+ }
+
+ return (NULL);
+ }
+
+ this_name++;
+ }
+
+ return (NULL); /* Name was not found in the repair table */
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_null_element
+ *
+ * PARAMETERS: info - Method execution information block
+ * expected_btypes - Object types expected
+ * package_index - Index of object within parent package (if
+ * applicable - ACPI_NOT_PACKAGE_ELEMENT
+ * otherwise)
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if repair was successful.
+ *
+ * DESCRIPTION: Attempt to repair a NULL element of a returned Package object.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_repair_null_element(struct acpi_evaluate_info *info,
+ u32 expected_btypes,
+ u32 package_index,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ union acpi_operand_object *new_object;
+
+ ACPI_FUNCTION_NAME(ns_repair_null_element);
+
+ /* No repair needed if return object is non-NULL */
+
+ if (return_object) {
+ return (AE_OK);
+ }
+
+ /*
+ * Attempt to repair a NULL element of a Package object. This applies to
+ * predefined names that return a fixed-length package and each element
+ * is required. It does not apply to variable-length packages where NULL
+ * elements are allowed, especially at the end of the package.
+ */
+ if (expected_btypes & ACPI_RTYPE_INTEGER) {
+
+ /* Need an integer - create a zero-value integer */
+
+ new_object = acpi_ut_create_integer_object((u64)0);
+ } else if (expected_btypes & ACPI_RTYPE_STRING) {
+
+ /* Need a string - create a NULL string */
+
+ new_object = acpi_ut_create_string_object(0);
+ } else if (expected_btypes & ACPI_RTYPE_BUFFER) {
+
+ /* Need a buffer - create a zero-length buffer */
+
+ new_object = acpi_ut_create_buffer_object(0);
+ } else {
+ /* Error for all other expected types */
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Set the reference count according to the parent Package object */
+
+ new_object->common.reference_count =
+ info->parent_package->common.reference_count;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Converted NULL package element to expected %s at index %u\n",
+ info->full_pathname,
+ acpi_ut_get_object_type_name(new_object),
+ package_index));
+
+ *return_object_ptr = new_object;
+ info->return_flags |= ACPI_OBJECT_REPAIRED;
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_remove_null_elements
+ *
+ * PARAMETERS: info - Method execution information block
+ * package_type - An acpi_return_package_types value
+ * obj_desc - A Package object
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Remove all NULL package elements from packages that contain
+ * a variable number of subpackages. For these types of
+ * packages, NULL elements can be safely removed.
+ *
+ *****************************************************************************/
+
+void
+acpi_ns_remove_null_elements(struct acpi_evaluate_info *info,
+ u8 package_type,
+ union acpi_operand_object *obj_desc)
+{
+ union acpi_operand_object **source;
+ union acpi_operand_object **dest;
+ u32 count;
+ u32 new_count;
+ u32 i;
+
+ ACPI_FUNCTION_NAME(ns_remove_null_elements);
+
+ /*
+ * We can safely remove all NULL elements from these package types:
+ * PTYPE1_VAR packages contain a variable number of simple data types.
+ * PTYPE2 packages contain a variable number of subpackages.
+ */
+ switch (package_type) {
+ case ACPI_PTYPE1_VAR:
+ case ACPI_PTYPE2:
+ case ACPI_PTYPE2_COUNT:
+ case ACPI_PTYPE2_PKG_COUNT:
+ case ACPI_PTYPE2_FIXED:
+ case ACPI_PTYPE2_MIN:
+ case ACPI_PTYPE2_REV_FIXED:
+ case ACPI_PTYPE2_FIX_VAR:
+ break;
+
+ default:
+ case ACPI_PTYPE2_VAR_VAR:
+ case ACPI_PTYPE1_FIXED:
+ case ACPI_PTYPE1_OPTION:
+ return;
+ }
+
+ count = obj_desc->package.count;
+ new_count = count;
+
+ source = obj_desc->package.elements;
+ dest = source;
+
+ /* Examine all elements of the package object, remove nulls */
+
+ for (i = 0; i < count; i++) {
+ if (!*source) {
+ new_count--;
+ } else {
+ *dest = *source;
+ dest++;
+ }
+
+ source++;
+ }
+
+ /* Update parent package if any null elements were removed */
+
+ if (new_count < count) {
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Found and removed %u NULL elements\n",
+ info->full_pathname, (count - new_count)));
+
+ /* NULL terminate list and update the package count */
+
+ *dest = NULL;
+ obj_desc->package.count = new_count;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_wrap_with_package
+ *
+ * PARAMETERS: info - Method execution information block
+ * original_object - Pointer to the object to repair.
+ * obj_desc_ptr - The new package object is returned here
+ *
+ * RETURN: Status, new object in *obj_desc_ptr
+ *
+ * DESCRIPTION: Repair a common problem with objects that are defined to
+ * return a variable-length Package of sub-objects. If there is
+ * only one sub-object, some BIOS code mistakenly simply declares
+ * the single object instead of a Package with one sub-object.
+ * This function attempts to repair this error by wrapping a
+ * Package object around the original object, creating the
+ * correct and expected Package with one sub-object.
+ *
+ * Names that can be repaired in this manner include:
+ * _ALR, _CSD, _HPX, _MLS, _PLD, _PRT, _PSS, _TRT, _TSS,
+ * _BCL, _DOD, _FIX, _Sx
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_wrap_with_package(struct acpi_evaluate_info *info,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **obj_desc_ptr)
+{
+ union acpi_operand_object *pkg_obj_desc;
+
+ ACPI_FUNCTION_NAME(ns_wrap_with_package);
+
+ /*
+ * Create the new outer package and populate it. The new
+ * package will have a single element, the lone sub-object.
+ */
+ pkg_obj_desc = acpi_ut_create_package_object(1);
+ if (!pkg_obj_desc) {
+ return (AE_NO_MEMORY);
+ }
+
+ pkg_obj_desc->package.elements[0] = original_object;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Wrapped %s with expected Package object\n",
+ info->full_pathname,
+ acpi_ut_get_object_type_name(original_object)));
+
+ /* Return the new object in the object pointer */
+
+ *obj_desc_ptr = pkg_obj_desc;
+ info->return_flags |= ACPI_OBJECT_REPAIRED | ACPI_OBJECT_WRAPPED;
+ return (AE_OK);
+}
diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c
new file mode 100644
index 000000000..dd533c887
--- /dev/null
+++ b/drivers/acpi/acpica/nsrepair2.c
@@ -0,0 +1,951 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsrepair2 - Repair for objects returned by specific
+ * predefined methods
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsrepair2")
+
+/*
+ * Information structure and handler for ACPI predefined names that can
+ * be repaired on a per-name basis.
+ */
+typedef
+acpi_status (*acpi_repair_function) (struct acpi_evaluate_info * info,
+ union acpi_operand_object **
+ return_object_ptr);
+
+typedef struct acpi_repair_info {
+ char name[ACPI_NAMESEG_SIZE];
+ acpi_repair_function repair_function;
+
+} acpi_repair_info;
+
+/* Local prototypes */
+
+static const struct acpi_repair_info *acpi_ns_match_complex_repair(struct
+ acpi_namespace_node
+ *node);
+
+static acpi_status
+acpi_ns_repair_ALR(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_CID(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_CST(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_FDE(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_HID(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_PRT(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_PSS(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_TSS(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_check_sorted_list(struct acpi_evaluate_info *info,
+ union acpi_operand_object *return_object,
+ u32 start_index,
+ u32 expected_count,
+ u32 sort_index,
+ u8 sort_direction, char *sort_key_name);
+
+/* Values for sort_direction above */
+
+#define ACPI_SORT_ASCENDING 0
+#define ACPI_SORT_DESCENDING 1
+
+static void
+acpi_ns_remove_element(union acpi_operand_object *obj_desc, u32 index);
+
+static void
+acpi_ns_sort_list(union acpi_operand_object **elements,
+ u32 count, u32 index, u8 sort_direction);
+
+/*
+ * This table contains the names of the predefined methods for which we can
+ * perform more complex repairs.
+ *
+ * As necessary:
+ *
+ * _ALR: Sort the list ascending by ambient_illuminance
+ * _CID: Strings: uppercase all, remove any leading asterisk
+ * _CST: Sort the list ascending by C state type
+ * _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs
+ * _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs
+ * _HID: Strings: uppercase all, remove any leading asterisk
+ * _PRT: Fix reversed source_name and source_index
+ * _PSS: Sort the list descending by Power
+ * _TSS: Sort the list descending by Power
+ *
+ * Names that must be packages, but cannot be sorted:
+ *
+ * _BCL: Values are tied to the Package index where they appear, and cannot
+ * be moved or sorted. These index values are used for _BQC and _BCM.
+ * However, we can fix the case where a buffer is returned, by converting
+ * it to a Package of integers.
+ */
+static const struct acpi_repair_info acpi_ns_repairable_names[] = {
+ {"_ALR", acpi_ns_repair_ALR},
+ {"_CID", acpi_ns_repair_CID},
+ {"_CST", acpi_ns_repair_CST},
+ {"_FDE", acpi_ns_repair_FDE},
+ {"_GTM", acpi_ns_repair_FDE}, /* _GTM has same repair as _FDE */
+ {"_HID", acpi_ns_repair_HID},
+ {"_PRT", acpi_ns_repair_PRT},
+ {"_PSS", acpi_ns_repair_PSS},
+ {"_TSS", acpi_ns_repair_TSS},
+ {{0, 0, 0, 0}, NULL} /* Table terminator */
+};
+
+#define ACPI_FDE_FIELD_COUNT 5
+#define ACPI_FDE_BYTE_BUFFER_SIZE 5
+#define ACPI_FDE_DWORD_BUFFER_SIZE (ACPI_FDE_FIELD_COUNT * (u32) sizeof (u32))
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_complex_repairs
+ *
+ * PARAMETERS: info - Method execution information block
+ * node - Namespace node for the method/object
+ * validate_status - Original status of earlier validation
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if repair was successful. If name is not
+ * matched, validate_status is returned.
+ *
+ * DESCRIPTION: Attempt to repair/convert a return object of a type that was
+ * not expected.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_ns_complex_repairs(struct acpi_evaluate_info *info,
+ struct acpi_namespace_node *node,
+ acpi_status validate_status,
+ union acpi_operand_object **return_object_ptr)
+{
+ const struct acpi_repair_info *predefined;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ns_complex_repairs);
+
+ /* Check if this name is in the list of repairable names */
+
+ predefined = acpi_ns_match_complex_repair(node);
+ if (!predefined) {
+ return_ACPI_STATUS(validate_status);
+ }
+
+ status = predefined->repair_function(info, return_object_ptr);
+ return_ACPI_STATUS(status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_match_complex_repair
+ *
+ * PARAMETERS: node - Namespace node for the method/object
+ *
+ * RETURN: Pointer to entry in repair table. NULL indicates not found.
+ *
+ * DESCRIPTION: Check an object name against the repairable object list.
+ *
+ *****************************************************************************/
+
+static const struct acpi_repair_info *acpi_ns_match_complex_repair(struct
+ acpi_namespace_node
+ *node)
+{
+ const struct acpi_repair_info *this_name;
+
+ /* Search info table for a repairable predefined method/object name */
+
+ this_name = acpi_ns_repairable_names;
+ while (this_name->repair_function) {
+ if (ACPI_COMPARE_NAMESEG(node->name.ascii, this_name->name)) {
+ return (this_name);
+ }
+
+ this_name++;
+ }
+
+ return (NULL); /* Not found */
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_ALR
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _ALR object. If necessary, sort the object list
+ * ascending by the ambient illuminance values.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_ALR(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ acpi_status status;
+
+ status = acpi_ns_check_sorted_list(info, return_object, 0, 2, 1,
+ ACPI_SORT_ASCENDING,
+ "AmbientIlluminance");
+
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_FDE
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _FDE and _GTM objects. The expected return
+ * value is a Buffer of 5 DWORDs. This function repairs a common
+ * problem where the return value is a Buffer of BYTEs, not
+ * DWORDs.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_FDE(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ union acpi_operand_object *buffer_object;
+ u8 *byte_buffer;
+ u32 *dword_buffer;
+ u32 i;
+
+ ACPI_FUNCTION_NAME(ns_repair_FDE);
+
+ switch (return_object->common.type) {
+ case ACPI_TYPE_BUFFER:
+
+ /* This is the expected type. Length should be (at least) 5 DWORDs */
+
+ if (return_object->buffer.length >= ACPI_FDE_DWORD_BUFFER_SIZE) {
+ return (AE_OK);
+ }
+
+ /* We can only repair if we have exactly 5 BYTEs */
+
+ if (return_object->buffer.length != ACPI_FDE_BYTE_BUFFER_SIZE) {
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname,
+ info->node_flags,
+ "Incorrect return buffer length %u, expected %u",
+ return_object->buffer.length,
+ ACPI_FDE_DWORD_BUFFER_SIZE));
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /* Create the new (larger) buffer object */
+
+ buffer_object =
+ acpi_ut_create_buffer_object(ACPI_FDE_DWORD_BUFFER_SIZE);
+ if (!buffer_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Expand each byte to a DWORD */
+
+ byte_buffer = return_object->buffer.pointer;
+ dword_buffer = ACPI_CAST_PTR(u32,
+ buffer_object->buffer.pointer);
+
+ for (i = 0; i < ACPI_FDE_FIELD_COUNT; i++) {
+ *dword_buffer = (u32) *byte_buffer;
+ dword_buffer++;
+ byte_buffer++;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s Expanded Byte Buffer to expected DWord Buffer\n",
+ info->full_pathname));
+ break;
+
+ default:
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /* Delete the original return object, return the new buffer object */
+
+ acpi_ut_remove_reference(return_object);
+ *return_object_ptr = buffer_object;
+
+ info->return_flags |= ACPI_OBJECT_REPAIRED;
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_CID
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _CID object. If a string, ensure that all
+ * letters are uppercase and that there is no leading asterisk.
+ * If a Package, ensure same for all string elements.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_CID(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr)
+{
+ acpi_status status;
+ union acpi_operand_object *return_object = *return_object_ptr;
+ union acpi_operand_object **element_ptr;
+ union acpi_operand_object *original_element;
+ u16 original_ref_count;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ns_repair_CID);
+
+ /* Check for _CID as a simple string */
+
+ if (return_object->common.type == ACPI_TYPE_STRING) {
+ status = acpi_ns_repair_HID(info, return_object_ptr);
+ return_ACPI_STATUS(status);
+ }
+
+ /* Exit if not a Package */
+
+ if (return_object->common.type != ACPI_TYPE_PACKAGE) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Examine each element of the _CID package */
+
+ element_ptr = return_object->package.elements;
+ for (i = 0; i < return_object->package.count; i++) {
+ original_element = *element_ptr;
+ original_ref_count = original_element->common.reference_count;
+
+ status = acpi_ns_repair_HID(info, element_ptr);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (original_element != *element_ptr) {
+
+ /* Update reference count of new object */
+
+ (*element_ptr)->common.reference_count =
+ original_ref_count;
+ }
+
+ element_ptr++;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_CST
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _CST object:
+ * 1. Sort the list ascending by C state type
+ * 2. Ensure type cannot be zero
+ * 3. A subpackage count of zero means _CST is meaningless
+ * 4. Count must match the number of C state subpackages
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_CST(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ union acpi_operand_object **outer_elements;
+ u32 outer_element_count;
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+ u8 removing;
+ u32 i;
+
+ ACPI_FUNCTION_NAME(ns_repair_CST);
+
+ /*
+ * Check if the C-state type values are proportional.
+ */
+ outer_element_count = return_object->package.count - 1;
+ i = 0;
+ while (i < outer_element_count) {
+ outer_elements = &return_object->package.elements[i + 1];
+ removing = FALSE;
+
+ if ((*outer_elements)->package.count == 0) {
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname,
+ info->node_flags,
+ "SubPackage[%u] - removing entry due to zero count",
+ i));
+ removing = TRUE;
+ goto remove_element;
+ }
+
+ obj_desc = (*outer_elements)->package.elements[1]; /* Index1 = Type */
+ if ((u32)obj_desc->integer.value == 0) {
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname,
+ info->node_flags,
+ "SubPackage[%u] - removing entry due to invalid Type(0)",
+ i));
+ removing = TRUE;
+ }
+
+remove_element:
+ if (removing) {
+ acpi_ns_remove_element(return_object, i + 1);
+ outer_element_count--;
+ } else {
+ i++;
+ }
+ }
+
+ /* Update top-level package count, Type "Integer" checked elsewhere */
+
+ obj_desc = return_object->package.elements[0];
+ obj_desc->integer.value = outer_element_count;
+
+ /*
+ * Entries (subpackages) in the _CST Package must be sorted by the
+ * C-state type, in ascending order.
+ */
+ status = acpi_ns_check_sorted_list(info, return_object, 1, 4, 1,
+ ACPI_SORT_ASCENDING, "C-State Type");
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_HID
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _HID object. If a string, ensure that all
+ * letters are uppercase and that there is no leading asterisk.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_HID(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ union acpi_operand_object *new_string;
+ char *source;
+ char *dest;
+
+ ACPI_FUNCTION_NAME(ns_repair_HID);
+
+ /* We only care about string _HID objects (not integers) */
+
+ if (return_object->common.type != ACPI_TYPE_STRING) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ if (return_object->string.length == 0) {
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname, info->node_flags,
+ "Invalid zero-length _HID or _CID string"));
+
+ /* Return AE_OK anyway, let driver handle it */
+
+ info->return_flags |= ACPI_OBJECT_REPAIRED;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* It is simplest to always create a new string object */
+
+ new_string = acpi_ut_create_string_object(return_object->string.length);
+ if (!new_string) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /*
+ * Remove a leading asterisk if present. For some unknown reason, there
+ * are many machines in the field that contains IDs like this.
+ *
+ * Examples: "*PNP0C03", "*ACPI0003"
+ */
+ source = return_object->string.pointer;
+ if (*source == '*') {
+ source++;
+ new_string->string.length--;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Removed invalid leading asterisk\n",
+ info->full_pathname));
+ }
+
+ /*
+ * Copy and uppercase the string. From the ACPI 5.0 specification:
+ *
+ * A valid PNP ID must be of the form "AAA####" where A is an uppercase
+ * letter and # is a hex digit. A valid ACPI ID must be of the form
+ * "NNNN####" where N is an uppercase letter or decimal digit, and
+ * # is a hex digit.
+ */
+ for (dest = new_string->string.pointer; *source; dest++, source++) {
+ *dest = (char)toupper((int)*source);
+ }
+
+ acpi_ut_remove_reference(return_object);
+ *return_object_ptr = new_string;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_PRT
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _PRT object. If necessary, fix reversed
+ * source_name and source_index field, a common BIOS bug.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_PRT(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *package_object = *return_object_ptr;
+ union acpi_operand_object **top_object_list;
+ union acpi_operand_object **sub_object_list;
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *sub_package;
+ u32 element_count;
+ u32 index;
+
+ /* Each element in the _PRT package is a subpackage */
+
+ top_object_list = package_object->package.elements;
+ element_count = package_object->package.count;
+
+ /* Examine each subpackage */
+
+ for (index = 0; index < element_count; index++, top_object_list++) {
+ sub_package = *top_object_list;
+ sub_object_list = sub_package->package.elements;
+
+ /* Check for minimum required element count */
+
+ if (sub_package->package.count < 4) {
+ continue;
+ }
+
+ /*
+ * If the BIOS has erroneously reversed the _PRT source_name (index 2)
+ * and the source_index (index 3), fix it. _PRT is important enough to
+ * workaround this BIOS error. This also provides compatibility with
+ * other ACPI implementations.
+ */
+ obj_desc = sub_object_list[3];
+ if (!obj_desc || (obj_desc->common.type != ACPI_TYPE_INTEGER)) {
+ sub_object_list[3] = sub_object_list[2];
+ sub_object_list[2] = obj_desc;
+ info->return_flags |= ACPI_OBJECT_REPAIRED;
+
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname,
+ info->node_flags,
+ "PRT[%X]: Fixed reversed SourceName and SourceIndex",
+ index));
+ }
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_PSS
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _PSS object. If necessary, sort the object list
+ * by the CPU frequencies. Check that the power dissipation values
+ * are all proportional to CPU frequency (i.e., sorting by
+ * frequency should be the same as sorting by power.)
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_PSS(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ union acpi_operand_object **outer_elements;
+ u32 outer_element_count;
+ union acpi_operand_object **elements;
+ union acpi_operand_object *obj_desc;
+ u32 previous_value;
+ acpi_status status;
+ u32 i;
+
+ /*
+ * Entries (subpackages) in the _PSS Package must be sorted by power
+ * dissipation, in descending order. If it appears that the list is
+ * incorrectly sorted, sort it. We sort by cpu_frequency, since this
+ * should be proportional to the power.
+ */
+ status = acpi_ns_check_sorted_list(info, return_object, 0, 6, 0,
+ ACPI_SORT_DESCENDING,
+ "CpuFrequency");
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /*
+ * We now know the list is correctly sorted by CPU frequency. Check if
+ * the power dissipation values are proportional.
+ */
+ previous_value = ACPI_UINT32_MAX;
+ outer_elements = return_object->package.elements;
+ outer_element_count = return_object->package.count;
+
+ for (i = 0; i < outer_element_count; i++) {
+ elements = (*outer_elements)->package.elements;
+ obj_desc = elements[1]; /* Index1 = power_dissipation */
+
+ if ((u32)obj_desc->integer.value > previous_value) {
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname,
+ info->node_flags,
+ "SubPackage[%u,%u] - suspicious power dissipation values",
+ i - 1, i));
+ }
+
+ previous_value = (u32) obj_desc->integer.value;
+ outer_elements++;
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_TSS
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _TSS object. If necessary, sort the object list
+ * descending by the power dissipation values.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_TSS(struct acpi_evaluate_info *info,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ /*
+ * We can only sort the _TSS return package if there is no _PSS in the
+ * same scope. This is because if _PSS is present, the ACPI specification
+ * dictates that the _TSS Power Dissipation field is to be ignored, and
+ * therefore some BIOSs leave garbage values in the _TSS Power field(s).
+ * In this case, it is best to just return the _TSS package as-is.
+ * (May, 2011)
+ */
+ status = acpi_ns_get_node(info->node, "^_PSS",
+ ACPI_NS_NO_UPSEARCH, &node);
+ if (ACPI_SUCCESS(status)) {
+ return (AE_OK);
+ }
+
+ status = acpi_ns_check_sorted_list(info, return_object, 0, 5, 1,
+ ACPI_SORT_DESCENDING,
+ "PowerDissipation");
+
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_sorted_list
+ *
+ * PARAMETERS: info - Method execution information block
+ * return_object - Pointer to the top-level returned object
+ * start_index - Index of the first subpackage
+ * expected_count - Minimum length of each subpackage
+ * sort_index - Subpackage entry to sort on
+ * sort_direction - Ascending or descending
+ * sort_key_name - Name of the sort_index field
+ *
+ * RETURN: Status. AE_OK if the list is valid and is sorted correctly or
+ * has been repaired by sorting the list.
+ *
+ * DESCRIPTION: Check if the package list is valid and sorted correctly by the
+ * sort_index. If not, then sort the list.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_check_sorted_list(struct acpi_evaluate_info *info,
+ union acpi_operand_object *return_object,
+ u32 start_index,
+ u32 expected_count,
+ u32 sort_index,
+ u8 sort_direction, char *sort_key_name)
+{
+ u32 outer_element_count;
+ union acpi_operand_object **outer_elements;
+ union acpi_operand_object **elements;
+ union acpi_operand_object *obj_desc;
+ u32 i;
+ u32 previous_value;
+
+ ACPI_FUNCTION_NAME(ns_check_sorted_list);
+
+ /* The top-level object must be a package */
+
+ if (return_object->common.type != ACPI_TYPE_PACKAGE) {
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * NOTE: assumes list of subpackages contains no NULL elements.
+ * Any NULL elements should have been removed by earlier call
+ * to acpi_ns_remove_null_elements.
+ */
+ outer_element_count = return_object->package.count;
+ if (!outer_element_count || start_index >= outer_element_count) {
+ return (AE_AML_PACKAGE_LIMIT);
+ }
+
+ outer_elements = &return_object->package.elements[start_index];
+ outer_element_count -= start_index;
+
+ previous_value = 0;
+ if (sort_direction == ACPI_SORT_DESCENDING) {
+ previous_value = ACPI_UINT32_MAX;
+ }
+
+ /* Examine each subpackage */
+
+ for (i = 0; i < outer_element_count; i++) {
+
+ /* Each element of the top-level package must also be a package */
+
+ if ((*outer_elements)->common.type != ACPI_TYPE_PACKAGE) {
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /* Each subpackage must have the minimum length */
+
+ if ((*outer_elements)->package.count < expected_count) {
+ return (AE_AML_PACKAGE_LIMIT);
+ }
+
+ elements = (*outer_elements)->package.elements;
+ obj_desc = elements[sort_index];
+
+ if (obj_desc->common.type != ACPI_TYPE_INTEGER) {
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * The list must be sorted in the specified order. If we detect a
+ * discrepancy, sort the entire list.
+ */
+ if (((sort_direction == ACPI_SORT_ASCENDING) &&
+ (obj_desc->integer.value < previous_value)) ||
+ ((sort_direction == ACPI_SORT_DESCENDING) &&
+ (obj_desc->integer.value > previous_value))) {
+ acpi_ns_sort_list(&return_object->package.
+ elements[start_index],
+ outer_element_count, sort_index,
+ sort_direction);
+
+ info->return_flags |= ACPI_OBJECT_REPAIRED;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Repaired unsorted list - now sorted by %s\n",
+ info->full_pathname, sort_key_name));
+ return (AE_OK);
+ }
+
+ previous_value = (u32) obj_desc->integer.value;
+ outer_elements++;
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_sort_list
+ *
+ * PARAMETERS: elements - Package object element list
+ * count - Element count for above
+ * index - Sort by which package element
+ * sort_direction - Ascending or Descending sort
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Sort the objects that are in a package element list.
+ *
+ * NOTE: Assumes that all NULL elements have been removed from the package,
+ * and that all elements have been verified to be of type Integer.
+ *
+ *****************************************************************************/
+
+static void
+acpi_ns_sort_list(union acpi_operand_object **elements,
+ u32 count, u32 index, u8 sort_direction)
+{
+ union acpi_operand_object *obj_desc1;
+ union acpi_operand_object *obj_desc2;
+ union acpi_operand_object *temp_obj;
+ u32 i;
+ u32 j;
+
+ /* Simple bubble sort */
+
+ for (i = 1; i < count; i++) {
+ for (j = (count - 1); j >= i; j--) {
+ obj_desc1 = elements[j - 1]->package.elements[index];
+ obj_desc2 = elements[j]->package.elements[index];
+
+ if (((sort_direction == ACPI_SORT_ASCENDING) &&
+ (obj_desc1->integer.value >
+ obj_desc2->integer.value))
+ || ((sort_direction == ACPI_SORT_DESCENDING)
+ && (obj_desc1->integer.value <
+ obj_desc2->integer.value))) {
+ temp_obj = elements[j - 1];
+ elements[j - 1] = elements[j];
+ elements[j] = temp_obj;
+ }
+ }
+ }
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_remove_element
+ *
+ * PARAMETERS: obj_desc - Package object element list
+ * index - Index of element to remove
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Remove the requested element of a package and delete it.
+ *
+ *****************************************************************************/
+
+static void
+acpi_ns_remove_element(union acpi_operand_object *obj_desc, u32 index)
+{
+ union acpi_operand_object **source;
+ union acpi_operand_object **dest;
+ u32 count;
+ u32 new_count;
+ u32 i;
+
+ ACPI_FUNCTION_NAME(ns_remove_element);
+
+ count = obj_desc->package.count;
+ new_count = count - 1;
+
+ source = obj_desc->package.elements;
+ dest = source;
+
+ /* Examine all elements of the package object, remove matched index */
+
+ for (i = 0; i < count; i++) {
+ if (i == index) {
+ acpi_ut_remove_reference(*source); /* Remove one ref for being in pkg */
+ acpi_ut_remove_reference(*source);
+ } else {
+ *dest = *source;
+ dest++;
+ }
+
+ source++;
+ }
+
+ /* NULL terminate list and update the package count */
+
+ *dest = NULL;
+ obj_desc->package.count = new_count;
+}
diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c
new file mode 100644
index 000000000..f594ab75a
--- /dev/null
+++ b/drivers/acpi/acpica/nssearch.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: nssearch - Namespace search
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#ifdef ACPI_ASL_COMPILER
+#include "amlcode.h"
+#endif
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nssearch")
+
+/* Local prototypes */
+static acpi_status
+acpi_ns_search_parent_tree(u32 target_name,
+ struct acpi_namespace_node *node,
+ acpi_object_type type,
+ struct acpi_namespace_node **return_node);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_search_one_scope
+ *
+ * PARAMETERS: target_name - Ascii ACPI name to search for
+ * parent_node - Starting node where search will begin
+ * type - Object type to match
+ * return_node - Where the matched Named obj is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Search a single level of the namespace. Performs a
+ * simple search of the specified level, and does not add
+ * entries or search parents.
+ *
+ *
+ * Named object lists are built (and subsequently dumped) in the
+ * order in which the names are encountered during the namespace load;
+ *
+ * All namespace searching is linear in this implementation, but
+ * could be easily modified to support any improved search
+ * algorithm. However, the linear search was chosen for simplicity
+ * and because the trees are small and the other interpreter
+ * execution overhead is relatively high.
+ *
+ * Note: CPU execution analysis has shown that the AML interpreter spends
+ * a very small percentage of its time searching the namespace. Therefore,
+ * the linear search seems to be sufficient, as there would seem to be
+ * little value in improving the search.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_search_one_scope(u32 target_name,
+ struct acpi_namespace_node *parent_node,
+ acpi_object_type type,
+ struct acpi_namespace_node **return_node)
+{
+ struct acpi_namespace_node *node;
+
+ ACPI_FUNCTION_TRACE(ns_search_one_scope);
+
+#ifdef ACPI_DEBUG_OUTPUT
+ if (ACPI_LV_NAMES & acpi_dbg_level) {
+ char *scope_name;
+
+ scope_name = acpi_ns_get_normalized_pathname(parent_node, TRUE);
+ if (scope_name) {
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Searching %s (%p) For [%4.4s] (%s)\n",
+ scope_name, parent_node,
+ ACPI_CAST_PTR(char, &target_name),
+ acpi_ut_get_type_name(type)));
+
+ ACPI_FREE(scope_name);
+ }
+ }
+#endif
+
+ /*
+ * Search for name at this namespace level, which is to say that we
+ * must search for the name among the children of this object
+ */
+ node = parent_node->child;
+ while (node) {
+
+ /* Check for match against the name */
+
+ if (node->name.integer == target_name) {
+
+ /* Resolve a control method alias if any */
+
+ if (acpi_ns_get_type(node) ==
+ ACPI_TYPE_LOCAL_METHOD_ALIAS) {
+ node =
+ ACPI_CAST_PTR(struct acpi_namespace_node,
+ node->object);
+ }
+
+ /* Found matching entry */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Name [%4.4s] (%s) %p found in scope [%4.4s] %p\n",
+ ACPI_CAST_PTR(char, &target_name),
+ acpi_ut_get_type_name(node->type),
+ node,
+ acpi_ut_get_node_name(parent_node),
+ parent_node));
+
+ *return_node = node;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Didn't match name, move on to the next peer object */
+
+ node = node->peer;
+ }
+
+ /* Searched entire namespace level, not found */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Name [%4.4s] (%s) not found in search in scope [%4.4s] "
+ "%p first child %p\n",
+ ACPI_CAST_PTR(char, &target_name),
+ acpi_ut_get_type_name(type),
+ acpi_ut_get_node_name(parent_node), parent_node,
+ parent_node->child));
+
+ return_ACPI_STATUS(AE_NOT_FOUND);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_search_parent_tree
+ *
+ * PARAMETERS: target_name - Ascii ACPI name to search for
+ * node - Starting node where search will begin
+ * type - Object type to match
+ * return_node - Where the matched Node is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Called when a name has not been found in the current namespace
+ * level. Before adding it or giving up, ACPI scope rules require
+ * searching enclosing scopes in cases identified by acpi_ns_local().
+ *
+ * "A name is located by finding the matching name in the current
+ * name space, and then in the parent name space. If the parent
+ * name space does not contain the name, the search continues
+ * recursively until either the name is found or the name space
+ * does not have a parent (the root of the name space). This
+ * indicates that the name is not found" (From ACPI Specification,
+ * section 5.3)
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_search_parent_tree(u32 target_name,
+ struct acpi_namespace_node *node,
+ acpi_object_type type,
+ struct acpi_namespace_node **return_node)
+{
+ acpi_status status;
+ struct acpi_namespace_node *parent_node;
+
+ ACPI_FUNCTION_TRACE(ns_search_parent_tree);
+
+ parent_node = node->parent;
+
+ /*
+ * If there is no parent (i.e., we are at the root) or type is "local",
+ * we won't be searching the parent tree.
+ */
+ if (!parent_node) {
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "[%4.4s] has no parent\n",
+ ACPI_CAST_PTR(char, &target_name)));
+ return_ACPI_STATUS(AE_NOT_FOUND);
+ }
+
+ if (acpi_ns_local(type)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "[%4.4s] type [%s] must be local to this scope (no parent search)\n",
+ ACPI_CAST_PTR(char, &target_name),
+ acpi_ut_get_type_name(type)));
+ return_ACPI_STATUS(AE_NOT_FOUND);
+ }
+
+ /* Search the parent tree */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Searching parent [%4.4s] for [%4.4s]\n",
+ acpi_ut_get_node_name(parent_node),
+ ACPI_CAST_PTR(char, &target_name)));
+
+ /* Search parents until target is found or we have backed up to the root */
+
+ while (parent_node) {
+ /*
+ * Search parent scope. Use TYPE_ANY because we don't care about the
+ * object type at this point, we only care about the existence of
+ * the actual name we are searching for. Typechecking comes later.
+ */
+ status =
+ acpi_ns_search_one_scope(target_name, parent_node,
+ ACPI_TYPE_ANY, return_node);
+ if (ACPI_SUCCESS(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Not found here, go up another level (until we reach the root) */
+
+ parent_node = parent_node->parent;
+ }
+
+ /* Not found in parent tree */
+
+ return_ACPI_STATUS(AE_NOT_FOUND);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_search_and_enter
+ *
+ * PARAMETERS: target_name - Ascii ACPI name to search for (4 chars)
+ * walk_state - Current state of the walk
+ * node - Starting node where search will begin
+ * interpreter_mode - Add names only in ACPI_MODE_LOAD_PASS_x.
+ * Otherwise,search only.
+ * type - Object type to match
+ * flags - Flags describing the search restrictions
+ * return_node - Where the Node is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Search for a name segment in a single namespace level,
+ * optionally adding it if it is not found. If the passed
+ * Type is not Any and the type previously stored in the
+ * entry was Any (i.e. unknown), update the stored type.
+ *
+ * In ACPI_IMODE_EXECUTE, search only.
+ * In other modes, search and add if not found.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_search_and_enter(u32 target_name,
+ struct acpi_walk_state *walk_state,
+ struct acpi_namespace_node *node,
+ acpi_interpreter_mode interpreter_mode,
+ acpi_object_type type,
+ u32 flags, struct acpi_namespace_node **return_node)
+{
+ acpi_status status;
+ struct acpi_namespace_node *new_node;
+
+ ACPI_FUNCTION_TRACE(ns_search_and_enter);
+
+ /* Parameter validation */
+
+ if (!node || !target_name || !return_node) {
+ ACPI_ERROR((AE_INFO,
+ "Null parameter: Node %p Name 0x%X ReturnNode %p",
+ node, target_name, return_node));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Name must consist of valid ACPI characters. We will repair the name if
+ * necessary because we don't want to abort because of this, but we want
+ * all namespace names to be printable. A warning message is appropriate.
+ *
+ * This issue came up because there are in fact machines that exhibit
+ * this problem, and we want to be able to enable ACPI support for them,
+ * even though there are a few bad names.
+ */
+ acpi_ut_repair_name(ACPI_CAST_PTR(char, &target_name));
+
+ /* Try to find the name in the namespace level specified by the caller */
+
+ *return_node = ACPI_ENTRY_NOT_FOUND;
+ status = acpi_ns_search_one_scope(target_name, node, type, return_node);
+ if (status != AE_NOT_FOUND) {
+ /*
+ * If we found it AND the request specifies that a find is an error,
+ * return the error
+ */
+ if (status == AE_OK) {
+
+ /* The node was found in the namespace */
+
+ /*
+ * If the namespace override feature is enabled for this node,
+ * delete any existing attached sub-object and make the node
+ * look like a new node that is owned by the override table.
+ */
+ if (flags & ACPI_NS_OVERRIDE_IF_FOUND) {
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "Namespace override: %4.4s pass %u type %X Owner %X\n",
+ ACPI_CAST_PTR(char,
+ &target_name),
+ interpreter_mode,
+ (*return_node)->type,
+ walk_state->owner_id));
+
+ acpi_ns_delete_children(*return_node);
+ if (acpi_gbl_runtime_namespace_override) {
+ acpi_ut_remove_reference((*return_node)->object);
+ (*return_node)->object = NULL;
+ (*return_node)->owner_id =
+ walk_state->owner_id;
+ } else {
+ acpi_ns_remove_node(*return_node);
+ *return_node = ACPI_ENTRY_NOT_FOUND;
+ }
+ }
+
+ /* Return an error if we don't expect to find the object */
+
+ else if (flags & ACPI_NS_ERROR_IF_FOUND) {
+ status = AE_ALREADY_EXISTS;
+ }
+ }
+#ifdef ACPI_ASL_COMPILER
+ if (*return_node && (*return_node)->type == ACPI_TYPE_ANY) {
+ (*return_node)->flags |= ANOBJ_IS_EXTERNAL;
+ }
+#endif
+
+ /* Either found it or there was an error: finished either way */
+
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * The name was not found. If we are NOT performing the first pass
+ * (name entry) of loading the namespace, search the parent tree (all the
+ * way to the root if necessary.) We don't want to perform the parent
+ * search when the namespace is actually being loaded. We want to perform
+ * the search when namespace references are being resolved (load pass 2)
+ * and during the execution phase.
+ */
+ if ((interpreter_mode != ACPI_IMODE_LOAD_PASS1) &&
+ (flags & ACPI_NS_SEARCH_PARENT)) {
+ /*
+ * Not found at this level - search parent tree according to the
+ * ACPI specification
+ */
+ status =
+ acpi_ns_search_parent_tree(target_name, node, type,
+ return_node);
+ if (ACPI_SUCCESS(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* In execute mode, just search, never add names. Exit now */
+
+ if (interpreter_mode == ACPI_IMODE_EXECUTE) {
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "%4.4s Not found in %p [Not adding]\n",
+ ACPI_CAST_PTR(char, &target_name), node));
+
+ return_ACPI_STATUS(AE_NOT_FOUND);
+ }
+
+ /* Create the new named object */
+
+ new_node = acpi_ns_create_node(target_name);
+ if (!new_node) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+#ifdef ACPI_ASL_COMPILER
+
+ /* Node is an object defined by an External() statement */
+
+ if (flags & ACPI_NS_EXTERNAL ||
+ (walk_state && walk_state->opcode == AML_SCOPE_OP)) {
+ new_node->flags |= ANOBJ_IS_EXTERNAL;
+ }
+#endif
+
+ if (flags & ACPI_NS_TEMPORARY) {
+ new_node->flags |= ANOBJ_TEMPORARY;
+ }
+
+ /* Install the new object into the parent's list of children */
+
+ acpi_ns_install_node(walk_state, node, new_node, type);
+ *return_node = new_node;
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c
new file mode 100644
index 000000000..ef531b145
--- /dev/null
+++ b/drivers/acpi/acpica/nsutils.c
@@ -0,0 +1,731 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsutils - Utilities for accessing ACPI namespace, accessing
+ * parents and siblings and Scope manipulation
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsutils")
+
+/* Local prototypes */
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+acpi_name acpi_ns_find_parent_name(struct acpi_namespace_node *node_to_search);
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_print_node_pathname
+ *
+ * PARAMETERS: node - Object
+ * message - Prefix message
+ *
+ * DESCRIPTION: Print an object's full namespace pathname
+ * Manages allocation/freeing of a pathname buffer
+ *
+ ******************************************************************************/
+
+void
+acpi_ns_print_node_pathname(struct acpi_namespace_node *node,
+ const char *message)
+{
+ struct acpi_buffer buffer;
+ acpi_status status;
+
+ if (!node) {
+ acpi_os_printf("[NULL NAME]");
+ return;
+ }
+
+ /* Convert handle to full pathname and print it (with supplied message) */
+
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+
+ status = acpi_ns_handle_to_pathname(node, &buffer, TRUE);
+ if (ACPI_SUCCESS(status)) {
+ if (message) {
+ acpi_os_printf("%s ", message);
+ }
+
+ acpi_os_printf("%s", (char *)buffer.pointer);
+ ACPI_FREE(buffer.pointer);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_type
+ *
+ * PARAMETERS: node - Parent Node to be examined
+ *
+ * RETURN: Type field from Node whose handle is passed
+ *
+ * DESCRIPTION: Return the type of a Namespace node
+ *
+ ******************************************************************************/
+
+acpi_object_type acpi_ns_get_type(struct acpi_namespace_node * node)
+{
+ ACPI_FUNCTION_TRACE(ns_get_type);
+
+ if (!node) {
+ ACPI_WARNING((AE_INFO, "Null Node parameter"));
+ return_UINT8(ACPI_TYPE_ANY);
+ }
+
+ return_UINT8(node->type);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_local
+ *
+ * PARAMETERS: type - A namespace object type
+ *
+ * RETURN: LOCAL if names must be found locally in objects of the
+ * passed type, 0 if enclosing scopes should be searched
+ *
+ * DESCRIPTION: Returns scope rule for the given object type.
+ *
+ ******************************************************************************/
+
+u32 acpi_ns_local(acpi_object_type type)
+{
+ ACPI_FUNCTION_TRACE(ns_local);
+
+ if (!acpi_ut_valid_object_type(type)) {
+
+ /* Type code out of range */
+
+ ACPI_WARNING((AE_INFO, "Invalid Object Type 0x%X", type));
+ return_UINT32(ACPI_NS_NORMAL);
+ }
+
+ return_UINT32(acpi_gbl_ns_properties[type] & ACPI_NS_LOCAL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_internal_name_length
+ *
+ * PARAMETERS: info - Info struct initialized with the
+ * external name pointer.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Calculate the length of the internal (AML) namestring
+ * corresponding to the external (ASL) namestring.
+ *
+ ******************************************************************************/
+
+void acpi_ns_get_internal_name_length(struct acpi_namestring_info *info)
+{
+ const char *next_external_char;
+ u32 i;
+
+ ACPI_FUNCTION_ENTRY();
+
+ next_external_char = info->external_name;
+ info->num_carats = 0;
+ info->num_segments = 0;
+ info->fully_qualified = FALSE;
+
+ /*
+ * For the internal name, the required length is 4 bytes per segment,
+ * plus 1 each for root_prefix, multi_name_prefix_op, segment count,
+ * trailing null (which is not really needed, but no there's harm in
+ * putting it there)
+ *
+ * strlen() + 1 covers the first name_seg, which has no path separator
+ */
+ if (ACPI_IS_ROOT_PREFIX(*next_external_char)) {
+ info->fully_qualified = TRUE;
+ next_external_char++;
+
+ /* Skip redundant root_prefix, like \\_SB.PCI0.SBRG.EC0 */
+
+ while (ACPI_IS_ROOT_PREFIX(*next_external_char)) {
+ next_external_char++;
+ }
+ } else {
+ /* Handle Carat prefixes */
+
+ while (ACPI_IS_PARENT_PREFIX(*next_external_char)) {
+ info->num_carats++;
+ next_external_char++;
+ }
+ }
+
+ /*
+ * Determine the number of ACPI name "segments" by counting the number of
+ * path separators within the string. Start with one segment since the
+ * segment count is [(# separators) + 1], and zero separators is ok.
+ */
+ if (*next_external_char) {
+ info->num_segments = 1;
+ for (i = 0; next_external_char[i]; i++) {
+ if (ACPI_IS_PATH_SEPARATOR(next_external_char[i])) {
+ info->num_segments++;
+ }
+ }
+ }
+
+ info->length = (ACPI_NAMESEG_SIZE * info->num_segments) +
+ 4 + info->num_carats;
+
+ info->next_external_char = next_external_char;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_build_internal_name
+ *
+ * PARAMETERS: info - Info struct fully initialized
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Construct the internal (AML) namestring
+ * corresponding to the external (ASL) namestring.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ns_build_internal_name(struct acpi_namestring_info *info)
+{
+ u32 num_segments = info->num_segments;
+ char *internal_name = info->internal_name;
+ const char *external_name = info->next_external_char;
+ char *result = NULL;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ns_build_internal_name);
+
+ /* Setup the correct prefixes, counts, and pointers */
+
+ if (info->fully_qualified) {
+ internal_name[0] = AML_ROOT_PREFIX;
+
+ if (num_segments <= 1) {
+ result = &internal_name[1];
+ } else if (num_segments == 2) {
+ internal_name[1] = AML_DUAL_NAME_PREFIX;
+ result = &internal_name[2];
+ } else {
+ internal_name[1] = AML_MULTI_NAME_PREFIX;
+ internal_name[2] = (char)num_segments;
+ result = &internal_name[3];
+ }
+ } else {
+ /*
+ * Not fully qualified.
+ * Handle Carats first, then append the name segments
+ */
+ i = 0;
+ if (info->num_carats) {
+ for (i = 0; i < info->num_carats; i++) {
+ internal_name[i] = AML_PARENT_PREFIX;
+ }
+ }
+
+ if (num_segments <= 1) {
+ result = &internal_name[i];
+ } else if (num_segments == 2) {
+ internal_name[i] = AML_DUAL_NAME_PREFIX;
+ result = &internal_name[(acpi_size)i + 1];
+ } else {
+ internal_name[i] = AML_MULTI_NAME_PREFIX;
+ internal_name[(acpi_size)i + 1] = (char)num_segments;
+ result = &internal_name[(acpi_size)i + 2];
+ }
+ }
+
+ /* Build the name (minus path separators) */
+
+ for (; num_segments; num_segments--) {
+ for (i = 0; i < ACPI_NAMESEG_SIZE; i++) {
+ if (ACPI_IS_PATH_SEPARATOR(*external_name) ||
+ (*external_name == 0)) {
+
+ /* Pad the segment with underscore(s) if segment is short */
+
+ result[i] = '_';
+ } else {
+ /* Convert the character to uppercase and save it */
+
+ result[i] = (char)toupper((int)*external_name);
+ external_name++;
+ }
+ }
+
+ /* Now we must have a path separator, or the pathname is bad */
+
+ if (!ACPI_IS_PATH_SEPARATOR(*external_name) &&
+ (*external_name != 0)) {
+ return_ACPI_STATUS(AE_BAD_PATHNAME);
+ }
+
+ /* Move on the next segment */
+
+ external_name++;
+ result += ACPI_NAMESEG_SIZE;
+ }
+
+ /* Terminate the string */
+
+ *result = 0;
+
+ if (info->fully_qualified) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Returning [%p] (abs) \"\\%s\"\n",
+ internal_name, internal_name));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Returning [%p] (rel) \"%s\"\n",
+ internal_name, internal_name));
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_internalize_name
+ *
+ * PARAMETERS: *external_name - External representation of name
+ * **Converted name - Where to return the resulting
+ * internal represention of the name
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert an external representation (e.g. "\_PR_.CPU0")
+ * to internal form (e.g. 5c 2f 02 5f 50 52 5f 43 50 55 30)
+ *
+ *******************************************************************************/
+
+acpi_status
+acpi_ns_internalize_name(const char *external_name, char **converted_name)
+{
+ char *internal_name;
+ struct acpi_namestring_info info;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ns_internalize_name);
+
+ if ((!external_name) || (*external_name == 0) || (!converted_name)) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Get the length of the new internal name */
+
+ info.external_name = external_name;
+ acpi_ns_get_internal_name_length(&info);
+
+ /* We need a segment to store the internal name */
+
+ internal_name = ACPI_ALLOCATE_ZEROED(info.length);
+ if (!internal_name) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Build the name */
+
+ info.internal_name = internal_name;
+ status = acpi_ns_build_internal_name(&info);
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(internal_name);
+ return_ACPI_STATUS(status);
+ }
+
+ *converted_name = internal_name;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_externalize_name
+ *
+ * PARAMETERS: internal_name_length - Length of the internal name below
+ * internal_name - Internal representation of name
+ * converted_name_length - Where the length is returned
+ * converted_name - Where the resulting external name
+ * is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert internal name (e.g. 5c 2f 02 5f 50 52 5f 43 50 55 30)
+ * to its external (printable) form (e.g. "\_PR_.CPU0")
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_externalize_name(u32 internal_name_length,
+ const char *internal_name,
+ u32 * converted_name_length, char **converted_name)
+{
+ u32 names_index = 0;
+ u32 num_segments = 0;
+ u32 required_length;
+ u32 prefix_length = 0;
+ u32 i = 0;
+ u32 j = 0;
+
+ ACPI_FUNCTION_TRACE(ns_externalize_name);
+
+ if (!internal_name_length || !internal_name || !converted_name) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Check for a prefix (one '\' | one or more '^') */
+
+ switch (internal_name[0]) {
+ case AML_ROOT_PREFIX:
+
+ prefix_length = 1;
+ break;
+
+ case AML_PARENT_PREFIX:
+
+ for (i = 0; i < internal_name_length; i++) {
+ if (ACPI_IS_PARENT_PREFIX(internal_name[i])) {
+ prefix_length = i + 1;
+ } else {
+ break;
+ }
+ }
+
+ if (i == internal_name_length) {
+ prefix_length = i;
+ }
+
+ break;
+
+ default:
+
+ break;
+ }
+
+ /*
+ * Check for object names. Note that there could be 0-255 of these
+ * 4-byte elements.
+ */
+ if (prefix_length < internal_name_length) {
+ switch (internal_name[prefix_length]) {
+ case AML_MULTI_NAME_PREFIX:
+
+ /* <count> 4-byte names */
+
+ names_index = prefix_length + 2;
+ num_segments = (u8)
+ internal_name[(acpi_size)prefix_length + 1];
+ break;
+
+ case AML_DUAL_NAME_PREFIX:
+
+ /* Two 4-byte names */
+
+ names_index = prefix_length + 1;
+ num_segments = 2;
+ break;
+
+ case 0:
+
+ /* null_name */
+
+ names_index = 0;
+ num_segments = 0;
+ break;
+
+ default:
+
+ /* one 4-byte name */
+
+ names_index = prefix_length;
+ num_segments = 1;
+ break;
+ }
+ }
+
+ /*
+ * Calculate the length of converted_name, which equals the length
+ * of the prefix, length of all object names, length of any required
+ * punctuation ('.') between object names, plus the NULL terminator.
+ */
+ required_length = prefix_length + (4 * num_segments) +
+ ((num_segments > 0) ? (num_segments - 1) : 0) + 1;
+
+ /*
+ * Check to see if we're still in bounds. If not, there's a problem
+ * with internal_name (invalid format).
+ */
+ if (required_length > internal_name_length) {
+ ACPI_ERROR((AE_INFO, "Invalid internal name"));
+ return_ACPI_STATUS(AE_BAD_PATHNAME);
+ }
+
+ /* Build the converted_name */
+
+ *converted_name = ACPI_ALLOCATE_ZEROED(required_length);
+ if (!(*converted_name)) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ j = 0;
+
+ for (i = 0; i < prefix_length; i++) {
+ (*converted_name)[j++] = internal_name[i];
+ }
+
+ if (num_segments > 0) {
+ for (i = 0; i < num_segments; i++) {
+ if (i > 0) {
+ (*converted_name)[j++] = '.';
+ }
+
+ /* Copy and validate the 4-char name segment */
+
+ ACPI_COPY_NAMESEG(&(*converted_name)[j],
+ &internal_name[names_index]);
+ acpi_ut_repair_name(&(*converted_name)[j]);
+
+ j += ACPI_NAMESEG_SIZE;
+ names_index += ACPI_NAMESEG_SIZE;
+ }
+ }
+
+ if (converted_name_length) {
+ *converted_name_length = (u32) required_length;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_validate_handle
+ *
+ * PARAMETERS: handle - Handle to be validated and typecast to a
+ * namespace node.
+ *
+ * RETURN: A pointer to a namespace node
+ *
+ * DESCRIPTION: Convert a namespace handle to a namespace node. Handles special
+ * cases for the root node.
+ *
+ * NOTE: Real integer handles would allow for more verification
+ * and keep all pointers within this subsystem - however this introduces
+ * more overhead and has not been necessary to this point. Drivers
+ * holding handles are typically notified before a node becomes invalid
+ * due to a table unload.
+ *
+ ******************************************************************************/
+
+struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle)
+{
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Parameter validation */
+
+ if ((!handle) || (handle == ACPI_ROOT_OBJECT)) {
+ return (acpi_gbl_root_node);
+ }
+
+ /* We can at least attempt to verify the handle */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(handle) != ACPI_DESC_TYPE_NAMED) {
+ return (NULL);
+ }
+
+ return (ACPI_CAST_PTR(struct acpi_namespace_node, handle));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_terminate
+ *
+ * PARAMETERS: none
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: free memory allocated for namespace and ACPI table storage.
+ *
+ ******************************************************************************/
+
+void acpi_ns_terminate(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ns_terminate);
+
+ /*
+ * Free the entire namespace -- all nodes and all objects
+ * attached to the nodes
+ */
+ acpi_ns_delete_namespace_subtree(acpi_gbl_root_node);
+
+ /* Delete any objects attached to the root node */
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_VOID;
+ }
+
+ acpi_ns_delete_node(acpi_gbl_root_node);
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Namespace freed\n"));
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_opens_scope
+ *
+ * PARAMETERS: type - A valid namespace type
+ *
+ * RETURN: NEWSCOPE if the passed type "opens a name scope" according
+ * to the ACPI specification, else 0
+ *
+ ******************************************************************************/
+
+u32 acpi_ns_opens_scope(acpi_object_type type)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ if (type > ACPI_TYPE_LOCAL_MAX) {
+
+ /* type code out of range */
+
+ ACPI_WARNING((AE_INFO, "Invalid Object Type 0x%X", type));
+ return (ACPI_NS_NORMAL);
+ }
+
+ return (((u32)acpi_gbl_ns_properties[type]) & ACPI_NS_NEWSCOPE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_node_unlocked
+ *
+ * PARAMETERS: *pathname - Name to be found, in external (ASL) format. The
+ * \ (backslash) and ^ (carat) prefixes, and the
+ * . (period) to separate segments are supported.
+ * prefix_node - Root of subtree to be searched, or NS_ALL for the
+ * root of the name space. If Name is fully
+ * qualified (first s8 is '\'), the passed value
+ * of Scope will not be accessed.
+ * flags - Used to indicate whether to perform upsearch or
+ * not.
+ * return_node - Where the Node is returned
+ *
+ * DESCRIPTION: Look up a name relative to a given scope and return the
+ * corresponding Node. NOTE: Scope can be null.
+ *
+ * MUTEX: Doesn't locks namespace
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_get_node_unlocked(struct acpi_namespace_node *prefix_node,
+ const char *pathname,
+ u32 flags, struct acpi_namespace_node **return_node)
+{
+ union acpi_generic_state scope_info;
+ acpi_status status;
+ char *internal_path;
+
+ ACPI_FUNCTION_TRACE_PTR(ns_get_node_unlocked,
+ ACPI_CAST_PTR(char, pathname));
+
+ /* Simplest case is a null pathname */
+
+ if (!pathname) {
+ *return_node = prefix_node;
+ if (!prefix_node) {
+ *return_node = acpi_gbl_root_node;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Quick check for a reference to the root */
+
+ if (ACPI_IS_ROOT_PREFIX(pathname[0]) && (!pathname[1])) {
+ *return_node = acpi_gbl_root_node;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Convert path to internal representation */
+
+ status = acpi_ns_internalize_name(pathname, &internal_path);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Setup lookup scope (search starting point) */
+
+ scope_info.scope.node = prefix_node;
+
+ /* Lookup the name in the namespace */
+
+ status = acpi_ns_lookup(&scope_info, internal_path, ACPI_TYPE_ANY,
+ ACPI_IMODE_EXECUTE,
+ (flags | ACPI_NS_DONT_OPEN_SCOPE), NULL,
+ return_node);
+ if (ACPI_FAILURE(status)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%s, %s\n",
+ pathname, acpi_format_exception(status)));
+ }
+
+ ACPI_FREE(internal_path);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_node
+ *
+ * PARAMETERS: *pathname - Name to be found, in external (ASL) format. The
+ * \ (backslash) and ^ (carat) prefixes, and the
+ * . (period) to separate segments are supported.
+ * prefix_node - Root of subtree to be searched, or NS_ALL for the
+ * root of the name space. If Name is fully
+ * qualified (first s8 is '\'), the passed value
+ * of Scope will not be accessed.
+ * flags - Used to indicate whether to perform upsearch or
+ * not.
+ * return_node - Where the Node is returned
+ *
+ * DESCRIPTION: Look up a name relative to a given scope and return the
+ * corresponding Node. NOTE: Scope can be null.
+ *
+ * MUTEX: Locks namespace
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
+ const char *pathname,
+ u32 flags, struct acpi_namespace_node **return_node)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ns_get_node, ACPI_CAST_PTR(char, pathname));
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ns_get_node_unlocked(prefix_node, pathname,
+ flags, return_node);
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c
new file mode 100644
index 000000000..82a0dae34
--- /dev/null
+++ b/drivers/acpi/acpica/nswalk.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nswalk - Functions for walking the ACPI namespace
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nswalk")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_next_node
+ *
+ * PARAMETERS: parent_node - Parent node whose children we are
+ * getting
+ * child_node - Previous child that was found.
+ * The NEXT child will be returned
+ *
+ * RETURN: struct acpi_namespace_node - Pointer to the NEXT child or NULL if
+ * none is found.
+ *
+ * DESCRIPTION: Return the next peer node within the namespace. If Handle
+ * is valid, Scope is ignored. Otherwise, the first node
+ * within Scope is returned.
+ *
+ ******************************************************************************/
+struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node
+ *parent_node,
+ struct acpi_namespace_node
+ *child_node)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ if (!child_node) {
+
+ /* It's really the parent's _scope_ that we want */
+
+ return (parent_node->child);
+ }
+
+ /* Otherwise just return the next peer */
+
+ return (child_node->peer);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_next_node_typed
+ *
+ * PARAMETERS: type - Type of node to be searched for
+ * parent_node - Parent node whose children we are
+ * getting
+ * child_node - Previous child that was found.
+ * The NEXT child will be returned
+ *
+ * RETURN: struct acpi_namespace_node - Pointer to the NEXT child or NULL if
+ * none is found.
+ *
+ * DESCRIPTION: Return the next peer node within the namespace. If Handle
+ * is valid, Scope is ignored. Otherwise, the first node
+ * within Scope is returned.
+ *
+ ******************************************************************************/
+
+struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
+ struct
+ acpi_namespace_node
+ *parent_node,
+ struct
+ acpi_namespace_node
+ *child_node)
+{
+ struct acpi_namespace_node *next_node = NULL;
+
+ ACPI_FUNCTION_ENTRY();
+
+ next_node = acpi_ns_get_next_node(parent_node, child_node);
+
+
+ /* If any type is OK, we are done */
+
+ if (type == ACPI_TYPE_ANY) {
+
+ /* next_node is NULL if we are at the end-of-list */
+
+ return (next_node);
+ }
+
+ /* Must search for the node -- but within this scope only */
+
+ while (next_node) {
+
+ /* If type matches, we are done */
+
+ if (next_node->type == type) {
+ return (next_node);
+ }
+
+ /* Otherwise, move on to the next peer node */
+
+ next_node = next_node->peer;
+ }
+
+ /* Not found */
+
+ return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_walk_namespace
+ *
+ * PARAMETERS: type - acpi_object_type to search for
+ * start_node - Handle in namespace where search begins
+ * max_depth - Depth to which search is to reach
+ * flags - Whether to unlock the NS before invoking
+ * the callback routine
+ * descending_callback - Called during tree descent
+ * when an object of "Type" is found
+ * ascending_callback - Called during tree ascent
+ * when an object of "Type" is found
+ * context - Passed to user function(s) above
+ * return_value - from the user_function if terminated
+ * early. Otherwise, returns NULL.
+ * RETURNS: Status
+ *
+ * DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
+ * starting (and ending) at the node specified by start_handle.
+ * The callback function is called whenever a node that matches
+ * the type parameter is found. If the callback function returns
+ * a non-zero value, the search is terminated immediately and
+ * this value is returned to the caller.
+ *
+ * The point of this procedure is to provide a generic namespace
+ * walk routine that can be called from multiple places to
+ * provide multiple services; the callback function(s) can be
+ * tailored to each task, whether it is a print function,
+ * a compare function, etc.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_walk_namespace(acpi_object_type type,
+ acpi_handle start_node,
+ u32 max_depth,
+ u32 flags,
+ acpi_walk_callback descending_callback,
+ acpi_walk_callback ascending_callback,
+ void *context, void **return_value)
+{
+ acpi_status status;
+ acpi_status mutex_status;
+ struct acpi_namespace_node *child_node;
+ struct acpi_namespace_node *parent_node;
+ acpi_object_type child_type;
+ u32 level;
+ u8 node_previously_visited = FALSE;
+
+ ACPI_FUNCTION_TRACE(ns_walk_namespace);
+
+ /* Special case for the namespace Root Node */
+
+ if (start_node == ACPI_ROOT_OBJECT) {
+ start_node = acpi_gbl_root_node;
+ if (!start_node) {
+ return_ACPI_STATUS(AE_NO_NAMESPACE);
+ }
+ }
+
+ /* Null child means "get first node" */
+
+ parent_node = start_node;
+ child_node = acpi_ns_get_next_node(parent_node, NULL);
+ child_type = ACPI_TYPE_ANY;
+ level = 1;
+
+ /*
+ * Traverse the tree of nodes until we bubble back up to where we
+ * started. When Level is zero, the loop is done because we have
+ * bubbled up to (and passed) the original parent handle (start_entry)
+ */
+ while (level > 0 && child_node) {
+ status = AE_OK;
+
+ /* Found next child, get the type if we are not searching for ANY */
+
+ if (type != ACPI_TYPE_ANY) {
+ child_type = child_node->type;
+ }
+
+ /*
+ * Ignore all temporary namespace nodes (created during control
+ * method execution) unless told otherwise. These temporary nodes
+ * can cause a race condition because they can be deleted during
+ * the execution of the user function (if the namespace is
+ * unlocked before invocation of the user function.) Only the
+ * debugger namespace dump will examine the temporary nodes.
+ */
+ if ((child_node->flags & ANOBJ_TEMPORARY) &&
+ !(flags & ACPI_NS_WALK_TEMP_NODES)) {
+ status = AE_CTRL_DEPTH;
+ }
+
+ /* Type must match requested type */
+
+ else if (child_type == type) {
+ /*
+ * Found a matching node, invoke the user callback function.
+ * Unlock the namespace if flag is set.
+ */
+ if (flags & ACPI_NS_WALK_UNLOCK) {
+ mutex_status =
+ acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(mutex_status)) {
+ return_ACPI_STATUS(mutex_status);
+ }
+ }
+
+ /*
+ * Invoke the user function, either descending, ascending,
+ * or both.
+ */
+ if (!node_previously_visited) {
+ if (descending_callback) {
+ status =
+ descending_callback(child_node,
+ level, context,
+ return_value);
+ }
+ } else {
+ if (ascending_callback) {
+ status =
+ ascending_callback(child_node,
+ level, context,
+ return_value);
+ }
+ }
+
+ if (flags & ACPI_NS_WALK_UNLOCK) {
+ mutex_status =
+ acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(mutex_status)) {
+ return_ACPI_STATUS(mutex_status);
+ }
+ }
+
+ switch (status) {
+ case AE_OK:
+ case AE_CTRL_DEPTH:
+
+ /* Just keep going */
+ break;
+
+ case AE_CTRL_TERMINATE:
+
+ /* Exit now, with OK status */
+
+ return_ACPI_STATUS(AE_OK);
+
+ default:
+
+ /* All others are valid exceptions */
+
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * Depth first search: Attempt to go down another level in the
+ * namespace if we are allowed to. Don't go any further if we have
+ * reached the caller specified maximum depth or if the user
+ * function has specified that the maximum depth has been reached.
+ */
+ if (!node_previously_visited &&
+ (level < max_depth) && (status != AE_CTRL_DEPTH)) {
+ if (child_node->child) {
+
+ /* There is at least one child of this node, visit it */
+
+ level++;
+ parent_node = child_node;
+ child_node =
+ acpi_ns_get_next_node(parent_node, NULL);
+ continue;
+ }
+ }
+
+ /* No more children, re-visit this node */
+
+ if (!node_previously_visited) {
+ node_previously_visited = TRUE;
+ continue;
+ }
+
+ /* No more children, visit peers */
+
+ child_node = acpi_ns_get_next_node(parent_node, child_node);
+ if (child_node) {
+ node_previously_visited = FALSE;
+ }
+
+ /* No peers, re-visit parent */
+
+ else {
+ /*
+ * No more children of this node (acpi_ns_get_next_node failed), go
+ * back upwards in the namespace tree to the node's parent.
+ */
+ level--;
+ child_node = parent_node;
+ parent_node = parent_node->parent;
+
+ node_previously_visited = TRUE;
+ }
+ }
+
+ /* Complete walk, not terminated by user function */
+
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c
new file mode 100644
index 000000000..f9d059647
--- /dev/null
+++ b/drivers/acpi/acpica/nsxfeval.c
@@ -0,0 +1,982 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: nsxfeval - Public interfaces to the ACPI subsystem
+ * ACPI Object evaluation interfaces
+ *
+ ******************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsxfeval")
+
+/* Local prototypes */
+static void acpi_ns_resolve_references(struct acpi_evaluate_info *info);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_evaluate_object_typed
+ *
+ * PARAMETERS: handle - Object handle (optional)
+ * pathname - Object pathname (optional)
+ * external_params - List of parameters to pass to a method,
+ * terminated by NULL. May be NULL
+ * if no parameters are being passed.
+ * return_buffer - Where to put the object's return value (if
+ * any). If NULL, no value is returned.
+ * return_type - Expected type of return object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Find and evaluate the given object, passing the given
+ * parameters if necessary. One of "Handle" or "Pathname" must
+ * be valid (non-null)
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_evaluate_object_typed(acpi_handle handle,
+ acpi_string pathname,
+ struct acpi_object_list *external_params,
+ struct acpi_buffer *return_buffer,
+ acpi_object_type return_type)
+{
+ acpi_status status;
+ u8 free_buffer_on_error = FALSE;
+ acpi_handle target_handle;
+ char *full_pathname;
+
+ ACPI_FUNCTION_TRACE(acpi_evaluate_object_typed);
+
+ /* Return buffer must be valid */
+
+ if (!return_buffer) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if (return_buffer->length == ACPI_ALLOCATE_BUFFER) {
+ free_buffer_on_error = TRUE;
+ }
+
+ /* Get a handle here, in order to build an error message if needed */
+
+ target_handle = handle;
+ if (pathname) {
+ status = acpi_get_handle(handle, pathname, &target_handle);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ full_pathname = acpi_ns_get_external_pathname(target_handle);
+ if (!full_pathname) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Evaluate the object */
+
+ status = acpi_evaluate_object(target_handle, NULL, external_params,
+ return_buffer);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ /* Type ANY means "don't care about return value type" */
+
+ if (return_type == ACPI_TYPE_ANY) {
+ goto exit;
+ }
+
+ if (return_buffer->length == 0) {
+
+ /* Error because caller specifically asked for a return value */
+
+ ACPI_ERROR((AE_INFO, "%s did not return any object",
+ full_pathname));
+ status = AE_NULL_OBJECT;
+ goto exit;
+ }
+
+ /* Examine the object type returned from evaluate_object */
+
+ if (((union acpi_object *)return_buffer->pointer)->type == return_type) {
+ goto exit;
+ }
+
+ /* Return object type does not match requested type */
+
+ ACPI_ERROR((AE_INFO,
+ "Incorrect return type from %s - received [%s], requested [%s]",
+ full_pathname,
+ acpi_ut_get_type_name(((union acpi_object *)return_buffer->
+ pointer)->type),
+ acpi_ut_get_type_name(return_type)));
+
+ if (free_buffer_on_error) {
+ /*
+ * Free a buffer created via ACPI_ALLOCATE_BUFFER.
+ * Note: We use acpi_os_free here because acpi_os_allocate was used
+ * to allocate the buffer. This purposefully bypasses the
+ * (optionally enabled) allocation tracking mechanism since we
+ * only want to track internal allocations.
+ */
+ acpi_os_free(return_buffer->pointer);
+ return_buffer->pointer = NULL;
+ }
+
+ return_buffer->length = 0;
+ status = AE_TYPE;
+
+exit:
+ ACPI_FREE(full_pathname);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_evaluate_object_typed)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_evaluate_object
+ *
+ * PARAMETERS: handle - Object handle (optional)
+ * pathname - Object pathname (optional)
+ * external_params - List of parameters to pass to method,
+ * terminated by NULL. May be NULL
+ * if no parameters are being passed.
+ * return_buffer - Where to put method's return value (if
+ * any). If NULL, no value is returned.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Find and evaluate the given object, passing the given
+ * parameters if necessary. One of "Handle" or "Pathname" must
+ * be valid (non-null)
+ *
+ ******************************************************************************/
+acpi_status
+acpi_evaluate_object(acpi_handle handle,
+ acpi_string pathname,
+ struct acpi_object_list *external_params,
+ struct acpi_buffer *return_buffer)
+{
+ acpi_status status;
+ struct acpi_evaluate_info *info;
+ acpi_size buffer_space_needed;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(acpi_evaluate_object);
+
+ /* Allocate and initialize the evaluation information block */
+
+ info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
+ if (!info) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Convert and validate the device handle */
+
+ info->prefix_node = acpi_ns_validate_handle(handle);
+ if (!info->prefix_node) {
+ status = AE_BAD_PARAMETER;
+ goto cleanup;
+ }
+
+ /*
+ * Get the actual namespace node for the target object.
+ * Handles these cases:
+ *
+ * 1) Null node, valid pathname from root (absolute path)
+ * 2) Node and valid pathname (path relative to Node)
+ * 3) Node, Null pathname
+ */
+ if ((pathname) && (ACPI_IS_ROOT_PREFIX(pathname[0]))) {
+
+ /* The path is fully qualified, just evaluate by name */
+
+ info->prefix_node = NULL;
+ } else if (!handle) {
+ /*
+ * A handle is optional iff a fully qualified pathname is specified.
+ * Since we've already handled fully qualified names above, this is
+ * an error.
+ */
+ if (!pathname) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Both Handle and Pathname are NULL"));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Null Handle with relative pathname [%s]",
+ pathname));
+ }
+
+ status = AE_BAD_PARAMETER;
+ goto cleanup;
+ }
+
+ info->relative_pathname = pathname;
+
+ /*
+ * Convert all external objects passed as arguments to the
+ * internal version(s).
+ */
+ if (external_params && external_params->count) {
+ info->param_count = (u16)external_params->count;
+
+ /* Warn on impossible argument count */
+
+ if (info->param_count > ACPI_METHOD_NUM_ARGS) {
+ ACPI_WARN_PREDEFINED((AE_INFO, pathname,
+ ACPI_WARN_ALWAYS,
+ "Excess arguments (%u) - using only %u",
+ info->param_count,
+ ACPI_METHOD_NUM_ARGS));
+
+ info->param_count = ACPI_METHOD_NUM_ARGS;
+ }
+
+ /*
+ * Allocate a new parameter block for the internal objects
+ * Add 1 to count to allow for null terminated internal list
+ */
+ info->parameters = ACPI_ALLOCATE_ZEROED(((acpi_size)info->
+ param_count +
+ 1) * sizeof(void *));
+ if (!info->parameters) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Convert each external object in the list to an internal object */
+
+ for (i = 0; i < info->param_count; i++) {
+ status =
+ acpi_ut_copy_eobject_to_iobject(&external_params->
+ pointer[i],
+ &info->
+ parameters[i]);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+ }
+
+ info->parameters[info->param_count] = NULL;
+ }
+
+#ifdef _FUTURE_FEATURE
+
+ /*
+ * Begin incoming argument count analysis. Check for too few args
+ * and too many args.
+ */
+ switch (acpi_ns_get_type(info->node)) {
+ case ACPI_TYPE_METHOD:
+
+ /* Check incoming argument count against the method definition */
+
+ if (info->obj_desc->method.param_count > info->param_count) {
+ ACPI_ERROR((AE_INFO,
+ "Insufficient arguments (%u) - %u are required",
+ info->param_count,
+ info->obj_desc->method.param_count));
+
+ status = AE_MISSING_ARGUMENTS;
+ goto cleanup;
+ }
+
+ else if (info->obj_desc->method.param_count < info->param_count) {
+ ACPI_WARNING((AE_INFO,
+ "Excess arguments (%u) - only %u are required",
+ info->param_count,
+ info->obj_desc->method.param_count));
+
+ /* Just pass the required number of arguments */
+
+ info->param_count = info->obj_desc->method.param_count;
+ }
+
+ /*
+ * Any incoming external objects to be passed as arguments to the
+ * method must be converted to internal objects
+ */
+ if (info->param_count) {
+ /*
+ * Allocate a new parameter block for the internal objects
+ * Add 1 to count to allow for null terminated internal list
+ */
+ info->parameters = ACPI_ALLOCATE_ZEROED(((acpi_size)
+ info->
+ param_count +
+ 1) *
+ sizeof(void *));
+ if (!info->parameters) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Convert each external object in the list to an internal object */
+
+ for (i = 0; i < info->param_count; i++) {
+ status =
+ acpi_ut_copy_eobject_to_iobject
+ (&external_params->pointer[i],
+ &info->parameters[i]);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+ }
+
+ info->parameters[info->param_count] = NULL;
+ }
+ break;
+
+ default:
+
+ /* Warn if arguments passed to an object that is not a method */
+
+ if (info->param_count) {
+ ACPI_WARNING((AE_INFO,
+ "%u arguments were passed to a non-method ACPI object",
+ info->param_count));
+ }
+ break;
+ }
+
+#endif
+
+ /* Now we can evaluate the object */
+
+ status = acpi_ns_evaluate(info);
+
+ /*
+ * If we are expecting a return value, and all went well above,
+ * copy the return value to an external object.
+ */
+ if (!return_buffer) {
+ goto cleanup_return_object;
+ }
+
+ if (!info->return_object) {
+ return_buffer->length = 0;
+ goto cleanup;
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(info->return_object) ==
+ ACPI_DESC_TYPE_NAMED) {
+ /*
+ * If we received a NS Node as a return object, this means that
+ * the object we are evaluating has nothing interesting to
+ * return (such as a mutex, etc.) We return an error because
+ * these types are essentially unsupported by this interface.
+ * We don't check up front because this makes it easier to add
+ * support for various types at a later date if necessary.
+ */
+ status = AE_TYPE;
+ info->return_object = NULL; /* No need to delete a NS Node */
+ return_buffer->length = 0;
+ }
+
+ if (ACPI_FAILURE(status)) {
+ goto cleanup_return_object;
+ }
+
+ /* Dereference Index and ref_of references */
+
+ acpi_ns_resolve_references(info);
+
+ /* Get the size of the returned object */
+
+ status = acpi_ut_get_object_size(info->return_object,
+ &buffer_space_needed);
+ if (ACPI_SUCCESS(status)) {
+
+ /* Validate/Allocate/Clear caller buffer */
+
+ status = acpi_ut_initialize_buffer(return_buffer,
+ buffer_space_needed);
+ if (ACPI_FAILURE(status)) {
+ /*
+ * Caller's buffer is too small or a new one can't
+ * be allocated
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Needed buffer size %X, %s\n",
+ (u32)buffer_space_needed,
+ acpi_format_exception(status)));
+ } else {
+ /* We have enough space for the object, build it */
+
+ status =
+ acpi_ut_copy_iobject_to_eobject(info->return_object,
+ return_buffer);
+ }
+ }
+
+cleanup_return_object:
+
+ if (info->return_object) {
+ /*
+ * Delete the internal return object. NOTE: Interpreter must be
+ * locked to avoid race condition.
+ */
+ acpi_ex_enter_interpreter();
+
+ /* Remove one reference on the return object (should delete it) */
+
+ acpi_ut_remove_reference(info->return_object);
+ acpi_ex_exit_interpreter();
+ }
+
+cleanup:
+
+ /* Free the input parameter list (if we created one) */
+
+ if (info->parameters) {
+
+ /* Free the allocated parameter block */
+
+ acpi_ut_delete_internal_object_list(info->parameters);
+ }
+
+ ACPI_FREE(info);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_evaluate_object)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_resolve_references
+ *
+ * PARAMETERS: info - Evaluation info block
+ *
+ * RETURN: Info->return_object is replaced with the dereferenced object
+ *
+ * DESCRIPTION: Dereference certain reference objects. Called before an
+ * internal return object is converted to an external union acpi_object.
+ *
+ * Performs an automatic dereference of Index and ref_of reference objects.
+ * These reference objects are not supported by the union acpi_object, so this is a
+ * last resort effort to return something useful. Also, provides compatibility
+ * with other ACPI implementations.
+ *
+ * NOTE: does not handle references within returned package objects or nested
+ * references, but this support could be added later if found to be necessary.
+ *
+ ******************************************************************************/
+static void acpi_ns_resolve_references(struct acpi_evaluate_info *info)
+{
+ union acpi_operand_object *obj_desc = NULL;
+ struct acpi_namespace_node *node;
+
+ /* We are interested in reference objects only */
+
+ if ((info->return_object)->common.type != ACPI_TYPE_LOCAL_REFERENCE) {
+ return;
+ }
+
+ /*
+ * Two types of references are supported - those created by Index and
+ * ref_of operators. A name reference (AML_NAMEPATH_OP) can be converted
+ * to a union acpi_object, so it is not dereferenced here. A ddb_handle
+ * (AML_LOAD_OP) cannot be dereferenced, nor can it be converted to
+ * a union acpi_object.
+ */
+ switch (info->return_object->reference.class) {
+ case ACPI_REFCLASS_INDEX:
+
+ obj_desc = *(info->return_object->reference.where);
+ break;
+
+ case ACPI_REFCLASS_REFOF:
+
+ node = info->return_object->reference.object;
+ if (node) {
+ obj_desc = node->object;
+ }
+ break;
+
+ default:
+
+ return;
+ }
+
+ /* Replace the existing reference object */
+
+ if (obj_desc) {
+ acpi_ut_add_reference(obj_desc);
+ acpi_ut_remove_reference(info->return_object);
+ info->return_object = obj_desc;
+ }
+
+ return;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_walk_namespace
+ *
+ * PARAMETERS: type - acpi_object_type to search for
+ * start_object - Handle in namespace where search begins
+ * max_depth - Depth to which search is to reach
+ * descending_callback - Called during tree descent
+ * when an object of "Type" is found
+ * ascending_callback - Called during tree ascent
+ * when an object of "Type" is found
+ * context - Passed to user function(s) above
+ * return_value - Location where return value of
+ * user_function is put if terminated early
+ *
+ * RETURNS Return value from the user_function if terminated early.
+ * Otherwise, returns NULL.
+ *
+ * DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
+ * starting (and ending) at the object specified by start_handle.
+ * The callback function is called whenever an object that matches
+ * the type parameter is found. If the callback function returns
+ * a non-zero value, the search is terminated immediately and this
+ * value is returned to the caller.
+ *
+ * The point of this procedure is to provide a generic namespace
+ * walk routine that can be called from multiple places to
+ * provide multiple services; the callback function(s) can be
+ * tailored to each task, whether it is a print function,
+ * a compare function, etc.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_walk_namespace(acpi_object_type type,
+ acpi_handle start_object,
+ u32 max_depth,
+ acpi_walk_callback descending_callback,
+ acpi_walk_callback ascending_callback,
+ void *context, void **return_value)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_walk_namespace);
+
+ /* Parameter validation */
+
+ if ((type > ACPI_TYPE_LOCAL_MAX) ||
+ (!max_depth) || (!descending_callback && !ascending_callback)) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Need to acquire the namespace reader lock to prevent interference
+ * with any concurrent table unloads (which causes the deletion of
+ * namespace objects). We cannot allow the deletion of a namespace node
+ * while the user function is using it. The exception to this are the
+ * nodes created and deleted during control method execution -- these
+ * nodes are marked as temporary nodes and are ignored by the namespace
+ * walk. Thus, control methods can be executed while holding the
+ * namespace deletion lock (and the user function can execute control
+ * methods.)
+ */
+ status = acpi_ut_acquire_read_lock(&acpi_gbl_namespace_rw_lock);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Lock the namespace around the walk. The namespace will be
+ * unlocked/locked around each call to the user function - since the user
+ * function must be allowed to make ACPICA calls itself (for example, it
+ * will typically execute control methods during device enumeration.)
+ */
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+
+ /* Now we can validate the starting node */
+
+ if (!acpi_ns_validate_handle(start_object)) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit2;
+ }
+
+ status = acpi_ns_walk_namespace(type, start_object, max_depth,
+ ACPI_NS_WALK_UNLOCK,
+ descending_callback, ascending_callback,
+ context, return_value);
+
+unlock_and_exit2:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+unlock_and_exit:
+ (void)acpi_ut_release_read_lock(&acpi_gbl_namespace_rw_lock);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_walk_namespace)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_device_callback
+ *
+ * PARAMETERS: Callback from acpi_get_device
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Takes callbacks from walk_namespace and filters out all non-
+ * present devices, or if they specified a HID, it filters based
+ * on that.
+ *
+ ******************************************************************************/
+static acpi_status
+acpi_ns_get_device_callback(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ struct acpi_get_devices_info *info = context;
+ acpi_status status;
+ struct acpi_namespace_node *node;
+ u32 flags;
+ struct acpi_pnp_device_id *hid;
+ struct acpi_pnp_device_id_list *cid;
+ u32 i;
+ u8 found;
+ int no_match;
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ node = acpi_ns_validate_handle(obj_handle);
+ status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if (!node) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /*
+ * First, filter based on the device HID and CID.
+ *
+ * 01/2010: For this case where a specific HID is requested, we don't
+ * want to run _STA until we have an actual HID match. Thus, we will
+ * not unnecessarily execute _STA on devices for which the caller
+ * doesn't care about. Previously, _STA was executed unconditionally
+ * on all devices found here.
+ *
+ * A side-effect of this change is that now we will continue to search
+ * for a matching HID even under device trees where the parent device
+ * would have returned a _STA that indicates it is not present or
+ * not functioning (thus aborting the search on that branch).
+ */
+ if (info->hid != NULL) {
+ status = acpi_ut_execute_HID(node, &hid);
+ if (status == AE_NOT_FOUND) {
+ return (AE_OK);
+ } else if (ACPI_FAILURE(status)) {
+ return (AE_CTRL_DEPTH);
+ }
+
+ no_match = strcmp(hid->string, info->hid);
+ ACPI_FREE(hid);
+
+ if (no_match) {
+ /*
+ * HID does not match, attempt match within the
+ * list of Compatible IDs (CIDs)
+ */
+ status = acpi_ut_execute_CID(node, &cid);
+ if (status == AE_NOT_FOUND) {
+ return (AE_OK);
+ } else if (ACPI_FAILURE(status)) {
+ return (AE_CTRL_DEPTH);
+ }
+
+ /* Walk the CID list */
+
+ found = FALSE;
+ for (i = 0; i < cid->count; i++) {
+ if (strcmp(cid->ids[i].string, info->hid) == 0) {
+
+ /* Found a matching CID */
+
+ found = TRUE;
+ break;
+ }
+ }
+
+ ACPI_FREE(cid);
+ if (!found) {
+ return (AE_OK);
+ }
+ }
+ }
+
+ /* Run _STA to determine if device is present */
+
+ status = acpi_ut_execute_STA(node, &flags);
+ if (ACPI_FAILURE(status)) {
+ return (AE_CTRL_DEPTH);
+ }
+
+ if (!(flags & ACPI_STA_DEVICE_PRESENT) &&
+ !(flags & ACPI_STA_DEVICE_FUNCTIONING)) {
+ /*
+ * Don't examine the children of the device only when the
+ * device is neither present nor functional. See ACPI spec,
+ * description of _STA for more information.
+ */
+ return (AE_CTRL_DEPTH);
+ }
+
+ /* We have a valid device, invoke the user function */
+
+ status = info->user_function(obj_handle, nesting_level,
+ info->context, return_value);
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_devices
+ *
+ * PARAMETERS: HID - HID to search for. Can be NULL.
+ * user_function - Called when a matching object is found
+ * context - Passed to user function
+ * return_value - Location where return value of
+ * user_function is put if terminated early
+ *
+ * RETURNS Return value from the user_function if terminated early.
+ * Otherwise, returns NULL.
+ *
+ * DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
+ * starting (and ending) at the object specified by start_handle.
+ * The user_function is called whenever an object of type
+ * Device is found. If the user function returns
+ * a non-zero value, the search is terminated immediately and this
+ * value is returned to the caller.
+ *
+ * This is a wrapper for walk_namespace, but the callback performs
+ * additional filtering. Please see acpi_ns_get_device_callback.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_get_devices(const char *HID,
+ acpi_walk_callback user_function,
+ void *context, void **return_value)
+{
+ acpi_status status;
+ struct acpi_get_devices_info info;
+
+ ACPI_FUNCTION_TRACE(acpi_get_devices);
+
+ /* Parameter validation */
+
+ if (!user_function) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * We're going to call their callback from OUR callback, so we need
+ * to know what it is, and their context parameter.
+ */
+ info.hid = HID;
+ info.context = context;
+ info.user_function = user_function;
+
+ /*
+ * Lock the namespace around the walk.
+ * The namespace will be unlocked/locked around each call
+ * to the user function - since this function
+ * must be allowed to make Acpi calls itself.
+ */
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
+ acpi_ns_get_device_callback, NULL,
+ &info, return_value);
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_devices)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_attach_data
+ *
+ * PARAMETERS: obj_handle - Namespace node
+ * handler - Handler for this attachment
+ * data - Pointer to data to be attached
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Attach arbitrary data and handler to a namespace node.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_attach_data(acpi_handle obj_handle,
+ acpi_object_handler handler, void *data)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (!obj_handle || !handler || !data) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Convert and validate the handle */
+
+ node = acpi_ns_validate_handle(obj_handle);
+ if (!node) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ status = acpi_ns_attach_data(node, handler, data);
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_attach_data)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_detach_data
+ *
+ * PARAMETERS: obj_handle - Namespace node handle
+ * handler - Handler used in call to acpi_attach_data
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove data that was previously attached to a node.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_detach_data(acpi_handle obj_handle, acpi_object_handler handler)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (!obj_handle || !handler) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Convert and validate the handle */
+
+ node = acpi_ns_validate_handle(obj_handle);
+ if (!node) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ status = acpi_ns_detach_data(node, handler);
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_detach_data)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_data_full
+ *
+ * PARAMETERS: obj_handle - Namespace node
+ * handler - Handler used in call to attach_data
+ * data - Where the data is returned
+ * callback - function to execute before returning
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Retrieve data that was previously attached to a namespace node
+ * and execute a callback before returning.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_data_full(acpi_handle obj_handle, acpi_object_handler handler,
+ void **data, void (*callback)(void *))
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (!obj_handle || !handler || !data) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Convert and validate the handle */
+
+ node = acpi_ns_validate_handle(obj_handle);
+ if (!node) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ status = acpi_ns_get_attached_data(node, handler, data);
+ if (ACPI_SUCCESS(status) && callback) {
+ callback(*data);
+ }
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_data_full)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_data
+ *
+ * PARAMETERS: obj_handle - Namespace node
+ * handler - Handler used in call to attach_data
+ * data - Where the data is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Retrieve data that was previously attached to a namespace node.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data)
+{
+ return acpi_get_data_full(obj_handle, handler, data, NULL);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_data)
diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c
new file mode 100644
index 000000000..b2cfdfef3
--- /dev/null
+++ b/drivers/acpi/acpica/nsxfname.c
@@ -0,0 +1,607 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: nsxfname - Public interfaces to the ACPI subsystem
+ * ACPI Namespace oriented interfaces
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acparser.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsxfname")
+
+/* Local prototypes */
+static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest,
+ struct acpi_pnp_device_id *source,
+ char *string_area);
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_get_handle
+ *
+ * PARAMETERS: parent - Object to search under (search scope).
+ * pathname - Pointer to an asciiz string containing the
+ * name
+ * ret_handle - Where the return handle is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This routine will search for a caller specified name in the
+ * name space. The caller can restrict the search region by
+ * specifying a non NULL parent. The parent value is itself a
+ * namespace handle.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_get_handle(acpi_handle parent,
+ acpi_string pathname, acpi_handle *ret_handle)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node = NULL;
+ struct acpi_namespace_node *prefix_node = NULL;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Parameter Validation */
+
+ if (!ret_handle || !pathname) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Convert a parent handle to a prefix node */
+
+ if (parent) {
+ prefix_node = acpi_ns_validate_handle(parent);
+ if (!prefix_node) {
+ return (AE_BAD_PARAMETER);
+ }
+ }
+
+ /*
+ * Valid cases are:
+ * 1) Fully qualified pathname
+ * 2) Parent + Relative pathname
+ *
+ * Error for <null Parent + relative path>
+ */
+ if (ACPI_IS_ROOT_PREFIX(pathname[0])) {
+
+ /* Pathname is fully qualified (starts with '\') */
+
+ /* Special case for root-only, since we can't search for it */
+
+ if (!strcmp(pathname, ACPI_NS_ROOT_PATH)) {
+ *ret_handle =
+ ACPI_CAST_PTR(acpi_handle, acpi_gbl_root_node);
+ return (AE_OK);
+ }
+ } else if (!prefix_node) {
+
+ /* Relative path with null prefix is disallowed */
+
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Find the Node and convert to a handle */
+
+ status =
+ acpi_ns_get_node(prefix_node, pathname, ACPI_NS_NO_UPSEARCH, &node);
+ if (ACPI_SUCCESS(status)) {
+ *ret_handle = ACPI_CAST_PTR(acpi_handle, node);
+ }
+
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_handle)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_get_name
+ *
+ * PARAMETERS: handle - Handle to be converted to a pathname
+ * name_type - Full pathname or single segment
+ * buffer - Buffer for returned path
+ *
+ * RETURN: Pointer to a string containing the fully qualified Name.
+ *
+ * DESCRIPTION: This routine returns the fully qualified name associated with
+ * the Handle parameter. This and the acpi_pathname_to_handle are
+ * complementary functions.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_name(acpi_handle handle, u32 name_type, struct acpi_buffer *buffer)
+{
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (name_type > ACPI_NAME_TYPE_MAX) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_validate_buffer(buffer);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /*
+ * Wants the single segment ACPI name.
+ * Validate handle and convert to a namespace Node
+ */
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if (name_type == ACPI_FULL_PATHNAME ||
+ name_type == ACPI_FULL_PATHNAME_NO_TRAILING) {
+
+ /* Get the full pathname (From the namespace root) */
+
+ status = acpi_ns_handle_to_pathname(handle, buffer,
+ name_type ==
+ ACPI_FULL_PATHNAME ? FALSE :
+ TRUE);
+ } else {
+ /* Get the single name */
+
+ status = acpi_ns_handle_to_name(handle, buffer);
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_name)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_copy_device_id
+ *
+ * PARAMETERS: dest - Pointer to the destination PNP_DEVICE_ID
+ * source - Pointer to the source PNP_DEVICE_ID
+ * string_area - Pointer to where to copy the dest string
+ *
+ * RETURN: Pointer to the next string area
+ *
+ * DESCRIPTION: Copy a single PNP_DEVICE_ID, including the string data.
+ *
+ ******************************************************************************/
+static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest,
+ struct acpi_pnp_device_id *source,
+ char *string_area)
+{
+ /* Create the destination PNP_DEVICE_ID */
+
+ dest->string = string_area;
+ dest->length = source->length;
+
+ /* Copy actual string and return a pointer to the next string area */
+
+ memcpy(string_area, source->string, source->length);
+ return (string_area + source->length);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_get_object_info
+ *
+ * PARAMETERS: handle - Object Handle
+ * return_buffer - Where the info is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Returns information about an object as gleaned from the
+ * namespace node and possibly by running several standard
+ * control methods (Such as in the case of a device.)
+ *
+ * For Device and Processor objects, run the Device _HID, _UID, _CID,
+ * _CLS, _ADR, _sx_w, and _sx_d methods.
+ *
+ * Note: Allocates the return buffer, must be freed by the caller.
+ *
+ * Note: This interface is intended to be used during the initial device
+ * discovery namespace traversal. Therefore, no complex methods can be
+ * executed, especially those that access operation regions. Therefore, do
+ * not add any additional methods that could cause problems in this area.
+ * Because of this reason support for the following methods has been removed:
+ * 1) _SUB method was removed (11/2015)
+ * 2) _STA method was removed (02/2018)
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_get_object_info(acpi_handle handle,
+ struct acpi_device_info **return_buffer)
+{
+ struct acpi_namespace_node *node;
+ struct acpi_device_info *info;
+ struct acpi_pnp_device_id_list *cid_list = NULL;
+ struct acpi_pnp_device_id *hid = NULL;
+ struct acpi_pnp_device_id *uid = NULL;
+ struct acpi_pnp_device_id *cls = NULL;
+ char *next_id_string;
+ acpi_object_type type;
+ acpi_name name;
+ u8 param_count = 0;
+ u16 valid = 0;
+ u32 info_size;
+ u32 i;
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (!handle || !return_buffer) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ node = acpi_ns_validate_handle(handle);
+ if (!node) {
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Get the namespace node data while the namespace is locked */
+
+ info_size = sizeof(struct acpi_device_info);
+ type = node->type;
+ name = node->name.integer;
+
+ if (node->type == ACPI_TYPE_METHOD) {
+ param_count = node->object->method.param_count;
+ }
+
+ status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) {
+ /*
+ * Get extra info for ACPI Device/Processor objects only:
+ * Run the Device _HID, _UID, _CLS, and _CID methods.
+ *
+ * Note: none of these methods are required, so they may or may
+ * not be present for this device. The Info->Valid bitfield is used
+ * to indicate which methods were found and run successfully.
+ */
+
+ /* Execute the Device._HID method */
+
+ status = acpi_ut_execute_HID(node, &hid);
+ if (ACPI_SUCCESS(status)) {
+ info_size += hid->length;
+ valid |= ACPI_VALID_HID;
+ }
+
+ /* Execute the Device._UID method */
+
+ status = acpi_ut_execute_UID(node, &uid);
+ if (ACPI_SUCCESS(status)) {
+ info_size += uid->length;
+ valid |= ACPI_VALID_UID;
+ }
+
+ /* Execute the Device._CID method */
+
+ status = acpi_ut_execute_CID(node, &cid_list);
+ if (ACPI_SUCCESS(status)) {
+
+ /* Add size of CID strings and CID pointer array */
+
+ info_size +=
+ (cid_list->list_size -
+ sizeof(struct acpi_pnp_device_id_list));
+ valid |= ACPI_VALID_CID;
+ }
+
+ /* Execute the Device._CLS method */
+
+ status = acpi_ut_execute_CLS(node, &cls);
+ if (ACPI_SUCCESS(status)) {
+ info_size += cls->length;
+ valid |= ACPI_VALID_CLS;
+ }
+ }
+
+ /*
+ * Now that we have the variable-length data, we can allocate the
+ * return buffer
+ */
+ info = ACPI_ALLOCATE_ZEROED(info_size);
+ if (!info) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Get the fixed-length data */
+
+ if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) {
+ /*
+ * Get extra info for ACPI Device/Processor objects only:
+ * Run the _ADR and, sx_w, and _sx_d methods.
+ *
+ * Notes: none of these methods are required, so they may or may
+ * not be present for this device. The Info->Valid bitfield is used
+ * to indicate which methods were found and run successfully.
+ */
+
+ /* Execute the Device._ADR method */
+
+ status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, node,
+ &info->address);
+ if (ACPI_SUCCESS(status)) {
+ valid |= ACPI_VALID_ADR;
+ }
+
+ /* Execute the Device._sx_w methods */
+
+ status = acpi_ut_execute_power_methods(node,
+ acpi_gbl_lowest_dstate_names,
+ ACPI_NUM_sx_w_METHODS,
+ info->lowest_dstates);
+ if (ACPI_SUCCESS(status)) {
+ valid |= ACPI_VALID_SXWS;
+ }
+
+ /* Execute the Device._sx_d methods */
+
+ status = acpi_ut_execute_power_methods(node,
+ acpi_gbl_highest_dstate_names,
+ ACPI_NUM_sx_d_METHODS,
+ info->highest_dstates);
+ if (ACPI_SUCCESS(status)) {
+ valid |= ACPI_VALID_SXDS;
+ }
+ }
+
+ /*
+ * Create a pointer to the string area of the return buffer.
+ * Point to the end of the base struct acpi_device_info structure.
+ */
+ next_id_string = ACPI_CAST_PTR(char, info->compatible_id_list.ids);
+ if (cid_list) {
+
+ /* Point past the CID PNP_DEVICE_ID array */
+
+ next_id_string +=
+ ((acpi_size)cid_list->count *
+ sizeof(struct acpi_pnp_device_id));
+ }
+
+ /*
+ * Copy the HID, UID, and CIDs to the return buffer. The variable-length
+ * strings are copied to the reserved area at the end of the buffer.
+ *
+ * For HID and CID, check if the ID is a PCI Root Bridge.
+ */
+ if (hid) {
+ next_id_string = acpi_ns_copy_device_id(&info->hardware_id,
+ hid, next_id_string);
+
+ if (acpi_ut_is_pci_root_bridge(hid->string)) {
+ info->flags |= ACPI_PCI_ROOT_BRIDGE;
+ }
+ }
+
+ if (uid) {
+ next_id_string = acpi_ns_copy_device_id(&info->unique_id,
+ uid, next_id_string);
+ }
+
+ if (cid_list) {
+ info->compatible_id_list.count = cid_list->count;
+ info->compatible_id_list.list_size = cid_list->list_size;
+
+ /* Copy each CID */
+
+ for (i = 0; i < cid_list->count; i++) {
+ next_id_string =
+ acpi_ns_copy_device_id(&info->compatible_id_list.
+ ids[i], &cid_list->ids[i],
+ next_id_string);
+
+ if (acpi_ut_is_pci_root_bridge(cid_list->ids[i].string)) {
+ info->flags |= ACPI_PCI_ROOT_BRIDGE;
+ }
+ }
+ }
+
+ if (cls) {
+ (void)acpi_ns_copy_device_id(&info->class_code,
+ cls, next_id_string);
+ }
+
+ /* Copy the fixed-length data */
+
+ info->info_size = info_size;
+ info->type = type;
+ info->name = name;
+ info->param_count = param_count;
+ info->valid = valid;
+
+ *return_buffer = info;
+ status = AE_OK;
+
+cleanup:
+ if (hid) {
+ ACPI_FREE(hid);
+ }
+ if (uid) {
+ ACPI_FREE(uid);
+ }
+ if (cid_list) {
+ ACPI_FREE(cid_list);
+ }
+ if (cls) {
+ ACPI_FREE(cls);
+ }
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_object_info)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_install_method
+ *
+ * PARAMETERS: buffer - An ACPI table containing one control method
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a control method into the namespace. If the method
+ * name already exists in the namespace, it is overwritten. The
+ * input buffer must contain a valid DSDT or SSDT containing a
+ * single control method.
+ *
+ ******************************************************************************/
+acpi_status acpi_install_method(u8 *buffer)
+{
+ struct acpi_table_header *table =
+ ACPI_CAST_PTR(struct acpi_table_header, buffer);
+ u8 *aml_buffer;
+ u8 *aml_start;
+ char *path;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *method_obj;
+ struct acpi_parse_state parser_state;
+ u32 aml_length;
+ u16 opcode;
+ u8 method_flags;
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (!buffer) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Table must be a DSDT or SSDT */
+
+ if (!ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_DSDT) &&
+ !ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_SSDT)) {
+ return (AE_BAD_HEADER);
+ }
+
+ /* First AML opcode in the table must be a control method */
+
+ parser_state.aml = buffer + sizeof(struct acpi_table_header);
+ opcode = acpi_ps_peek_opcode(&parser_state);
+ if (opcode != AML_METHOD_OP) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Extract method information from the raw AML */
+
+ parser_state.aml += acpi_ps_get_opcode_size(opcode);
+ parser_state.pkg_end = acpi_ps_get_next_package_end(&parser_state);
+ path = acpi_ps_get_next_namestring(&parser_state);
+
+ method_flags = *parser_state.aml++;
+ aml_start = parser_state.aml;
+ aml_length = (u32)ACPI_PTR_DIFF(parser_state.pkg_end, aml_start);
+
+ /*
+ * Allocate resources up-front. We don't want to have to delete a new
+ * node from the namespace if we cannot allocate memory.
+ */
+ aml_buffer = ACPI_ALLOCATE(aml_length);
+ if (!aml_buffer) {
+ return (AE_NO_MEMORY);
+ }
+
+ method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD);
+ if (!method_obj) {
+ ACPI_FREE(aml_buffer);
+ return (AE_NO_MEMORY);
+ }
+
+ /* Lock namespace for acpi_ns_lookup, we may be creating a new node */
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ /* The lookup either returns an existing node or creates a new one */
+
+ status =
+ acpi_ns_lookup(NULL, path, ACPI_TYPE_METHOD, ACPI_IMODE_LOAD_PASS1,
+ ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND,
+ NULL, &node);
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+ if (ACPI_FAILURE(status)) { /* ns_lookup */
+ if (status != AE_ALREADY_EXISTS) {
+ goto error_exit;
+ }
+
+ /* Node existed previously, make sure it is a method node */
+
+ if (node->type != ACPI_TYPE_METHOD) {
+ status = AE_TYPE;
+ goto error_exit;
+ }
+ }
+
+ /* Copy the method AML to the local buffer */
+
+ memcpy(aml_buffer, aml_start, aml_length);
+
+ /* Initialize the method object with the new method's information */
+
+ method_obj->method.aml_start = aml_buffer;
+ method_obj->method.aml_length = aml_length;
+
+ method_obj->method.param_count = (u8)
+ (method_flags & AML_METHOD_ARG_COUNT);
+
+ if (method_flags & AML_METHOD_SERIALIZED) {
+ method_obj->method.info_flags = ACPI_METHOD_SERIALIZED;
+
+ method_obj->method.sync_level = (u8)
+ ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4);
+ }
+
+ /*
+ * Now that it is complete, we can attach the new method object to
+ * the method Node (detaches/deletes any existing object)
+ */
+ status = acpi_ns_attach_object(node, method_obj, ACPI_TYPE_METHOD);
+
+ /*
+ * Flag indicates AML buffer is dynamic, must be deleted later.
+ * Must be set only after attach above.
+ */
+ node->flags |= ANOBJ_ALLOCATED_BUFFER;
+
+ /* Remove local reference to the method object */
+
+ acpi_ut_remove_reference(method_obj);
+ return (status);
+
+error_exit:
+
+ ACPI_FREE(aml_buffer);
+ ACPI_FREE(method_obj);
+ return (status);
+}
+ACPI_EXPORT_SYMBOL(acpi_install_method)
diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c
new file mode 100644
index 000000000..324269481
--- /dev/null
+++ b/drivers/acpi/acpica/nsxfobj.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: nsxfobj - Public interfaces to the ACPI subsystem
+ * ACPI Object oriented interfaces
+ *
+ ******************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsxfobj")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_type
+ *
+ * PARAMETERS: handle - Handle of object whose type is desired
+ * ret_type - Where the type will be placed
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This routine returns the type associated with a particular
+ * handle
+ *
+ ******************************************************************************/
+acpi_status acpi_get_type(acpi_handle handle, acpi_object_type *ret_type)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ /* Parameter Validation */
+
+ if (!ret_type) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Special case for the predefined Root Node (return type ANY) */
+
+ if (handle == ACPI_ROOT_OBJECT) {
+ *ret_type = ACPI_TYPE_ANY;
+ return (AE_OK);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Convert and validate the handle */
+
+ node = acpi_ns_validate_handle(handle);
+ if (!node) {
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (AE_BAD_PARAMETER);
+ }
+
+ *ret_type = node->type;
+
+ status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_type)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_parent
+ *
+ * PARAMETERS: handle - Handle of object whose parent is desired
+ * ret_handle - Where the parent handle will be placed
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Returns a handle to the parent of the object represented by
+ * Handle.
+ *
+ ******************************************************************************/
+acpi_status acpi_get_parent(acpi_handle handle, acpi_handle *ret_handle)
+{
+ struct acpi_namespace_node *node;
+ struct acpi_namespace_node *parent_node;
+ acpi_status status;
+
+ if (!ret_handle) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Special case for the predefined Root Node (no parent) */
+
+ if (handle == ACPI_ROOT_OBJECT) {
+ return (AE_NULL_ENTRY);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Convert and validate the handle */
+
+ node = acpi_ns_validate_handle(handle);
+ if (!node) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ /* Get the parent entry */
+
+ parent_node = node->parent;
+ *ret_handle = ACPI_CAST_PTR(acpi_handle, parent_node);
+
+ /* Return exception if parent is null */
+
+ if (!parent_node) {
+ status = AE_NULL_ENTRY;
+ }
+
+unlock_and_exit:
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_parent)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_next_object
+ *
+ * PARAMETERS: type - Type of object to be searched for
+ * parent - Parent object whose children we are getting
+ * last_child - Previous child that was found.
+ * The NEXT child will be returned
+ * ret_handle - Where handle to the next object is placed
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Return the next peer object within the namespace. If Handle is
+ * valid, Scope is ignored. Otherwise, the first object within
+ * Scope is returned.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_next_object(acpi_object_type type,
+ acpi_handle parent,
+ acpi_handle child, acpi_handle *ret_handle)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+ struct acpi_namespace_node *parent_node = NULL;
+ struct acpi_namespace_node *child_node = NULL;
+
+ /* Parameter validation */
+
+ if (type > ACPI_TYPE_EXTERNAL_MAX) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* If null handle, use the parent */
+
+ if (!child) {
+
+ /* Start search at the beginning of the specified scope */
+
+ parent_node = acpi_ns_validate_handle(parent);
+ if (!parent_node) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+ } else {
+ /* Non-null handle, ignore the parent */
+ /* Convert and validate the handle */
+
+ child_node = acpi_ns_validate_handle(child);
+ if (!child_node) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+ }
+
+ /* Internal function does the real work */
+
+ node = acpi_ns_get_next_node_typed(type, parent_node, child_node);
+ if (!node) {
+ status = AE_NOT_FOUND;
+ goto unlock_and_exit;
+ }
+
+ if (ret_handle) {
+ *ret_handle = ACPI_CAST_PTR(acpi_handle, node);
+ }
+
+unlock_and_exit:
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_next_object)
diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c
new file mode 100644
index 000000000..f7ec56060
--- /dev/null
+++ b/drivers/acpi/acpica/psargs.c
@@ -0,0 +1,908 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: psargs - Parse AML opcode arguments
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acnamesp.h"
+#include "acdispat.h"
+#include "acconvert.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("psargs")
+
+/* Local prototypes */
+static u32
+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);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_next_package_length
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ *
+ * RETURN: Decoded package length. On completion, the AML pointer points
+ * past the length byte or bytes.
+ *
+ * DESCRIPTION: Decode and return a package length field.
+ * Note: Largest package length is 28 bits, from ACPI specification
+ *
+ ******************************************************************************/
+
+static u32
+acpi_ps_get_next_package_length(struct acpi_parse_state *parser_state)
+{
+ u8 *aml = parser_state->aml;
+ u32 package_length = 0;
+ u32 byte_count;
+ u8 byte_zero_mask = 0x3F; /* Default [0:5] */
+
+ ACPI_FUNCTION_TRACE(ps_get_next_package_length);
+
+ /*
+ * Byte 0 bits [6:7] contain the number of additional bytes
+ * used to encode the package length, either 0,1,2, or 3
+ */
+ byte_count = (aml[0] >> 6);
+ parser_state->aml += ((acpi_size)byte_count + 1);
+
+ /* Get bytes 3, 2, 1 as needed */
+
+ while (byte_count) {
+ /*
+ * Final bit positions for the package length bytes:
+ * Byte3->[20:27]
+ * Byte2->[12:19]
+ * Byte1->[04:11]
+ * Byte0->[00:03]
+ */
+ package_length |= (aml[byte_count] << ((byte_count << 3) - 4));
+
+ byte_zero_mask = 0x0F; /* Use bits [0:3] of byte 0 */
+ byte_count--;
+ }
+
+ /* Byte 0 is a special case, either bits [0:3] or [0:5] are used */
+
+ package_length |= (aml[0] & byte_zero_mask);
+ return_UINT32(package_length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_next_package_end
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ *
+ * RETURN: Pointer to end-of-package +1
+ *
+ * DESCRIPTION: Get next package length and return a pointer past the end of
+ * the package. Consumes the package length field
+ *
+ ******************************************************************************/
+
+u8 *acpi_ps_get_next_package_end(struct acpi_parse_state *parser_state)
+{
+ u8 *start = parser_state->aml;
+ u32 package_length;
+
+ ACPI_FUNCTION_TRACE(ps_get_next_package_end);
+
+ /* Function below updates parser_state->Aml */
+
+ package_length = acpi_ps_get_next_package_length(parser_state);
+
+ return_PTR(start + package_length); /* end of package */
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_next_namestring
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ *
+ * RETURN: Pointer to the start of the name string (pointer points into
+ * the AML.
+ *
+ * DESCRIPTION: Get next raw namestring within the AML stream. Handles all name
+ * prefix characters. Set parser state to point past the string.
+ * (Name is consumed from the AML.)
+ *
+ ******************************************************************************/
+
+char *acpi_ps_get_next_namestring(struct acpi_parse_state *parser_state)
+{
+ u8 *start = parser_state->aml;
+ u8 *end = parser_state->aml;
+
+ ACPI_FUNCTION_TRACE(ps_get_next_namestring);
+
+ /* Point past any namestring prefix characters (backslash or carat) */
+
+ while (ACPI_IS_ROOT_PREFIX(*end) || ACPI_IS_PARENT_PREFIX(*end)) {
+ end++;
+ }
+
+ /* Decode the path prefix character */
+
+ switch (*end) {
+ case 0:
+
+ /* null_name */
+
+ if (end == start) {
+ start = NULL;
+ }
+ end++;
+ break;
+
+ case AML_DUAL_NAME_PREFIX:
+
+ /* Two name segments */
+
+ end += 1 + (2 * ACPI_NAMESEG_SIZE);
+ break;
+
+ case AML_MULTI_NAME_PREFIX:
+
+ /* Multiple name segments, 4 chars each, count in next byte */
+
+ end += 2 + (*(end + 1) * ACPI_NAMESEG_SIZE);
+ break;
+
+ default:
+
+ /* Single name segment */
+
+ end += ACPI_NAMESEG_SIZE;
+ break;
+ }
+
+ parser_state->aml = end;
+ return_PTR((char *)start);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_next_namepath
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ * arg - Where the namepath will be stored
+ * arg_count - If the namepath points to a control method
+ * the method's argument is returned here.
+ * possible_method_call - Whether the namepath can possibly be the
+ * start of a method call
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get next name (if method call, return # of required args).
+ * Names are looked up in the internal namespace to determine
+ * if the name represents a control method. If a method
+ * is found, the number of arguments to the method is returned.
+ * This information is critical for parsing to continue correctly.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ps_get_next_namepath(struct acpi_walk_state *walk_state,
+ struct acpi_parse_state *parser_state,
+ union acpi_parse_object *arg, u8 possible_method_call)
+{
+ acpi_status status;
+ char *path;
+ union acpi_parse_object *name_op;
+ union acpi_operand_object *method_desc;
+ struct acpi_namespace_node *node;
+ u8 *start = parser_state->aml;
+
+ ACPI_FUNCTION_TRACE(ps_get_next_namepath);
+
+ path = acpi_ps_get_next_namestring(parser_state);
+ acpi_ps_init_op(arg, AML_INT_NAMEPATH_OP);
+
+ /* Null path case is allowed, just exit */
+
+ if (!path) {
+ arg->common.value.name = path;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Lookup the name in the internal namespace, starting with the current
+ * scope. We don't want to add anything new to the namespace here,
+ * however, so we use MODE_EXECUTE.
+ * Allow searching of the parent tree, but don't open a new scope -
+ * we just want to lookup the object (must be mode EXECUTE to perform
+ * the upsearch)
+ */
+ status = acpi_ns_lookup(walk_state->scope_info, path,
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE,
+ NULL, &node);
+
+ /*
+ * If this name is a control method invocation, we must
+ * setup the method call
+ */
+ if (ACPI_SUCCESS(status) &&
+ possible_method_call && (node->type == ACPI_TYPE_METHOD)) {
+ if ((GET_CURRENT_ARG_TYPE(walk_state->arg_types) ==
+ ARGP_SUPERNAME)
+ || (GET_CURRENT_ARG_TYPE(walk_state->arg_types) ==
+ ARGP_TARGET)) {
+ /*
+ * acpi_ps_get_next_namestring has increased the AML pointer past
+ * the method invocation namestring, so we need to restore the
+ * saved AML pointer back to the original method invocation
+ * namestring.
+ */
+ walk_state->parser_state.aml = start;
+ walk_state->arg_count = 1;
+ acpi_ps_init_op(arg, AML_INT_METHODCALL_OP);
+ }
+
+ /* This name is actually a control method invocation */
+
+ method_desc = acpi_ns_get_attached_object(node);
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Control Method invocation %4.4s - %p Desc %p Path=%p\n",
+ node->name.ascii, node, method_desc, path));
+
+ name_op = acpi_ps_alloc_op(AML_INT_NAMEPATH_OP, start);
+ if (!name_op) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Change Arg into a METHOD CALL and attach name to it */
+
+ acpi_ps_init_op(arg, AML_INT_METHODCALL_OP);
+ name_op->common.value.name = path;
+
+ /* Point METHODCALL/NAME to the METHOD Node */
+
+ name_op->common.node = node;
+ acpi_ps_append_arg(arg, name_op);
+
+ if (!method_desc) {
+ ACPI_ERROR((AE_INFO,
+ "Control Method %p has no attached object",
+ node));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Control Method - %p Args %X\n",
+ node, method_desc->method.param_count));
+
+ /* Get the number of arguments to expect */
+
+ walk_state->arg_count = method_desc->method.param_count;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Special handling if the name was not found during the lookup -
+ * some not_found cases are allowed
+ */
+ if (status == AE_NOT_FOUND) {
+
+ /* 1) not_found is ok during load pass 1/2 (allow forward references) */
+
+ if ((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) !=
+ ACPI_PARSE_EXECUTE) {
+ status = AE_OK;
+ }
+
+ /* 2) not_found during a cond_ref_of(x) is ok by definition */
+
+ else if (walk_state->op->common.aml_opcode ==
+ AML_CONDITIONAL_REF_OF_OP) {
+ status = AE_OK;
+ }
+
+ /*
+ * 3) not_found while building a Package is ok at this point, we
+ * may flag as an error later if slack mode is not enabled.
+ * (Some ASL code depends on allowing this behavior)
+ */
+ else if ((arg->common.parent) &&
+ ((arg->common.parent->common.aml_opcode ==
+ AML_PACKAGE_OP)
+ || (arg->common.parent->common.aml_opcode ==
+ AML_VARIABLE_PACKAGE_OP))) {
+ status = AE_OK;
+ }
+ }
+
+ /* Final exception check (may have been changed from code above) */
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR_NAMESPACE(walk_state->scope_info, path, status);
+
+ if ((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) ==
+ ACPI_PARSE_EXECUTE) {
+
+ /* Report a control method execution error */
+
+ status = acpi_ds_method_error(status, walk_state);
+ }
+ }
+
+ /* Save the namepath */
+
+ arg->common.value.name = path;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_next_simple_arg
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ * arg_type - The argument type (AML_*_ARG)
+ * arg - Where the argument is returned
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Get the next simple argument (constant, string, or namestring)
+ *
+ ******************************************************************************/
+
+void
+acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state,
+ u32 arg_type, union acpi_parse_object *arg)
+{
+ u32 length;
+ u16 opcode;
+ u8 *aml = parser_state->aml;
+
+ ACPI_FUNCTION_TRACE_U32(ps_get_next_simple_arg, arg_type);
+
+ switch (arg_type) {
+ case ARGP_BYTEDATA:
+
+ /* Get 1 byte from the AML stream */
+
+ opcode = AML_BYTE_OP;
+ arg->common.value.integer = (u64) *aml;
+ length = 1;
+ break;
+
+ case ARGP_WORDDATA:
+
+ /* Get 2 bytes from the AML stream */
+
+ opcode = AML_WORD_OP;
+ ACPI_MOVE_16_TO_64(&arg->common.value.integer, aml);
+ length = 2;
+ break;
+
+ case ARGP_DWORDDATA:
+
+ /* Get 4 bytes from the AML stream */
+
+ opcode = AML_DWORD_OP;
+ ACPI_MOVE_32_TO_64(&arg->common.value.integer, aml);
+ length = 4;
+ break;
+
+ case ARGP_QWORDDATA:
+
+ /* Get 8 bytes from the AML stream */
+
+ opcode = AML_QWORD_OP;
+ ACPI_MOVE_64_TO_64(&arg->common.value.integer, aml);
+ length = 8;
+ break;
+
+ case ARGP_CHARLIST:
+
+ /* Get a pointer to the string, point past the string */
+
+ opcode = AML_STRING_OP;
+ arg->common.value.string = ACPI_CAST_PTR(char, aml);
+
+ /* Find the null terminator */
+
+ length = 0;
+ while (aml[length]) {
+ length++;
+ }
+ length++;
+ break;
+
+ case ARGP_NAME:
+ case ARGP_NAMESTRING:
+
+ acpi_ps_init_op(arg, AML_INT_NAMEPATH_OP);
+ arg->common.value.name =
+ acpi_ps_get_next_namestring(parser_state);
+ return_VOID;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Invalid ArgType 0x%X", arg_type));
+ return_VOID;
+ }
+
+ acpi_ps_init_op(arg, opcode);
+ parser_state->aml += length;
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_next_field
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ *
+ * RETURN: A newly allocated FIELD op
+ *
+ * DESCRIPTION: Get next field (named_field, reserved_field, or access_field)
+ *
+ ******************************************************************************/
+
+static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state
+ *parser_state)
+{
+ u8 *aml;
+ union acpi_parse_object *field;
+ union acpi_parse_object *arg = NULL;
+ u16 opcode;
+ u32 name;
+ u8 access_type;
+ u8 access_attribute;
+ u8 access_length;
+ u32 pkg_length;
+ u8 *pkg_end;
+ u32 buffer_length;
+
+ ACPI_FUNCTION_TRACE(ps_get_next_field);
+
+ ASL_CV_CAPTURE_COMMENTS_ONLY(parser_state);
+ aml = parser_state->aml;
+
+ /* Determine field type */
+
+ switch (ACPI_GET8(parser_state->aml)) {
+ case AML_FIELD_OFFSET_OP:
+
+ opcode = AML_INT_RESERVEDFIELD_OP;
+ parser_state->aml++;
+ break;
+
+ case AML_FIELD_ACCESS_OP:
+
+ opcode = AML_INT_ACCESSFIELD_OP;
+ parser_state->aml++;
+ break;
+
+ case AML_FIELD_CONNECTION_OP:
+
+ opcode = AML_INT_CONNECTION_OP;
+ parser_state->aml++;
+ break;
+
+ case AML_FIELD_EXT_ACCESS_OP:
+
+ opcode = AML_INT_EXTACCESSFIELD_OP;
+ parser_state->aml++;
+ break;
+
+ default:
+
+ opcode = AML_INT_NAMEDFIELD_OP;
+ break;
+ }
+
+ /* Allocate a new field op */
+
+ field = acpi_ps_alloc_op(opcode, aml);
+ if (!field) {
+ return_PTR(NULL);
+ }
+
+ /* Decode the field type */
+
+ ASL_CV_CAPTURE_COMMENTS_ONLY(parser_state);
+ switch (opcode) {
+ case AML_INT_NAMEDFIELD_OP:
+
+ /* Get the 4-character name */
+
+ ACPI_MOVE_32_TO_32(&name, parser_state->aml);
+ acpi_ps_set_name(field, name);
+ parser_state->aml += ACPI_NAMESEG_SIZE;
+
+ ASL_CV_CAPTURE_COMMENTS_ONLY(parser_state);
+
+#ifdef ACPI_ASL_COMPILER
+ /*
+ * Because the package length isn't represented as a parse tree object,
+ * take comments surrounding this and add to the previously created
+ * parse node.
+ */
+ if (field->common.inline_comment) {
+ field->common.name_comment =
+ field->common.inline_comment;
+ }
+ field->common.inline_comment = acpi_gbl_current_inline_comment;
+ acpi_gbl_current_inline_comment = NULL;
+#endif
+
+ /* Get the length which is encoded as a package length */
+
+ field->common.value.size =
+ acpi_ps_get_next_package_length(parser_state);
+ break;
+
+ case AML_INT_RESERVEDFIELD_OP:
+
+ /* Get the length which is encoded as a package length */
+
+ field->common.value.size =
+ acpi_ps_get_next_package_length(parser_state);
+ break;
+
+ case AML_INT_ACCESSFIELD_OP:
+ case AML_INT_EXTACCESSFIELD_OP:
+
+ /*
+ * Get access_type and access_attrib and merge into the field Op
+ * access_type is first operand, access_attribute is second. stuff
+ * these bytes into the node integer value for convenience.
+ */
+
+ /* Get the two bytes (Type/Attribute) */
+
+ access_type = ACPI_GET8(parser_state->aml);
+ parser_state->aml++;
+ access_attribute = ACPI_GET8(parser_state->aml);
+ parser_state->aml++;
+
+ field->common.value.integer = (u8)access_type;
+ field->common.value.integer |= (u16)(access_attribute << 8);
+
+ /* This opcode has a third byte, access_length */
+
+ if (opcode == AML_INT_EXTACCESSFIELD_OP) {
+ access_length = ACPI_GET8(parser_state->aml);
+ parser_state->aml++;
+
+ field->common.value.integer |=
+ (u32)(access_length << 16);
+ }
+ break;
+
+ case AML_INT_CONNECTION_OP:
+
+ /*
+ * Argument for Connection operator can be either a Buffer
+ * (resource descriptor), or a name_string.
+ */
+ aml = parser_state->aml;
+ if (ACPI_GET8(parser_state->aml) == AML_BUFFER_OP) {
+ parser_state->aml++;
+
+ ASL_CV_CAPTURE_COMMENTS_ONLY(parser_state);
+ pkg_end = parser_state->aml;
+ pkg_length =
+ acpi_ps_get_next_package_length(parser_state);
+ pkg_end += pkg_length;
+
+ ASL_CV_CAPTURE_COMMENTS_ONLY(parser_state);
+ if (parser_state->aml < pkg_end) {
+
+ /* Non-empty list */
+
+ arg =
+ acpi_ps_alloc_op(AML_INT_BYTELIST_OP, aml);
+ if (!arg) {
+ acpi_ps_free_op(field);
+ return_PTR(NULL);
+ }
+
+ /* Get the actual buffer length argument */
+
+ opcode = ACPI_GET8(parser_state->aml);
+ parser_state->aml++;
+
+ ASL_CV_CAPTURE_COMMENTS_ONLY(parser_state);
+ switch (opcode) {
+ case AML_BYTE_OP: /* AML_BYTEDATA_ARG */
+
+ buffer_length =
+ ACPI_GET8(parser_state->aml);
+ parser_state->aml += 1;
+ break;
+
+ case AML_WORD_OP: /* AML_WORDDATA_ARG */
+
+ buffer_length =
+ ACPI_GET16(parser_state->aml);
+ parser_state->aml += 2;
+ break;
+
+ case AML_DWORD_OP: /* AML_DWORDATA_ARG */
+
+ buffer_length =
+ ACPI_GET32(parser_state->aml);
+ parser_state->aml += 4;
+ break;
+
+ default:
+
+ buffer_length = 0;
+ break;
+ }
+
+ /* Fill in bytelist data */
+
+ ASL_CV_CAPTURE_COMMENTS_ONLY(parser_state);
+ arg->named.value.size = buffer_length;
+ arg->named.data = parser_state->aml;
+ }
+
+ /* Skip to End of byte data */
+
+ parser_state->aml = pkg_end;
+ } else {
+ arg = acpi_ps_alloc_op(AML_INT_NAMEPATH_OP, aml);
+ if (!arg) {
+ acpi_ps_free_op(field);
+ return_PTR(NULL);
+ }
+
+ /* Get the Namestring argument */
+
+ arg->common.value.name =
+ acpi_ps_get_next_namestring(parser_state);
+ }
+
+ /* Link the buffer/namestring to parent (CONNECTION_OP) */
+
+ acpi_ps_append_arg(field, arg);
+ break;
+
+ default:
+
+ /* Opcode was set in previous switch */
+ break;
+ }
+
+ return_PTR(field);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_next_arg
+ *
+ * PARAMETERS: walk_state - Current state
+ * parser_state - Current parser state object
+ * arg_type - The argument type (AML_*_ARG)
+ * return_arg - Where the next arg is returned
+ *
+ * RETURN: Status, and an op object containing the next argument.
+ *
+ * DESCRIPTION: Get next argument (including complex list arguments that require
+ * pushing the parser stack)
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
+ struct acpi_parse_state *parser_state,
+ u32 arg_type, union acpi_parse_object **return_arg)
+{
+ union acpi_parse_object *arg = NULL;
+ union acpi_parse_object *prev = NULL;
+ union acpi_parse_object *field;
+ u32 subop;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_get_next_arg, parser_state);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Expected argument type ARGP: %s (%2.2X)\n",
+ acpi_ut_get_argument_type_name(arg_type), arg_type));
+
+ switch (arg_type) {
+ case ARGP_BYTEDATA:
+ case ARGP_WORDDATA:
+ case ARGP_DWORDDATA:
+ case ARGP_CHARLIST:
+ case ARGP_NAME:
+ case ARGP_NAMESTRING:
+
+ /* Constants, strings, and namestrings are all the same size */
+
+ arg = acpi_ps_alloc_op(AML_BYTE_OP, parser_state->aml);
+ if (!arg) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ acpi_ps_get_next_simple_arg(parser_state, arg_type, arg);
+ break;
+
+ case ARGP_PKGLENGTH:
+
+ /* Package length, nothing returned */
+
+ parser_state->pkg_end =
+ acpi_ps_get_next_package_end(parser_state);
+ break;
+
+ case ARGP_FIELDLIST:
+
+ if (parser_state->aml < parser_state->pkg_end) {
+
+ /* Non-empty list */
+
+ while (parser_state->aml < parser_state->pkg_end) {
+ field = acpi_ps_get_next_field(parser_state);
+ if (!field) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ if (prev) {
+ prev->common.next = field;
+ } else {
+ arg = field;
+ }
+ prev = field;
+ }
+
+ /* Skip to End of byte data */
+
+ parser_state->aml = parser_state->pkg_end;
+ }
+ break;
+
+ case ARGP_BYTELIST:
+
+ if (parser_state->aml < parser_state->pkg_end) {
+
+ /* Non-empty list */
+
+ arg = acpi_ps_alloc_op(AML_INT_BYTELIST_OP,
+ parser_state->aml);
+ if (!arg) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Fill in bytelist data */
+
+ arg->common.value.size = (u32)
+ ACPI_PTR_DIFF(parser_state->pkg_end,
+ parser_state->aml);
+ arg->named.data = parser_state->aml;
+
+ /* Skip to End of byte data */
+
+ parser_state->aml = parser_state->pkg_end;
+ }
+ break;
+
+ case ARGP_SIMPLENAME:
+ case ARGP_NAME_OR_REF:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "**** SimpleName/NameOrRef: %s (%2.2X)\n",
+ acpi_ut_get_argument_type_name(arg_type),
+ arg_type));
+
+ subop = acpi_ps_peek_opcode(parser_state);
+ if (subop == 0 ||
+ acpi_ps_is_leading_char(subop) ||
+ ACPI_IS_ROOT_PREFIX(subop) ||
+ ACPI_IS_PARENT_PREFIX(subop)) {
+
+ /* null_name or name_string */
+
+ arg =
+ acpi_ps_alloc_op(AML_INT_NAMEPATH_OP,
+ parser_state->aml);
+ if (!arg) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ status =
+ acpi_ps_get_next_namepath(walk_state, parser_state,
+ arg,
+ ACPI_NOT_METHOD_CALL);
+ } else {
+ /* Single complex argument, nothing returned */
+
+ walk_state->arg_count = 1;
+ }
+ break;
+
+ case ARGP_TARGET:
+ case ARGP_SUPERNAME:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "**** Target/Supername: %s (%2.2X)\n",
+ acpi_ut_get_argument_type_name(arg_type),
+ arg_type));
+
+ subop = acpi_ps_peek_opcode(parser_state);
+ if (subop == 0 ||
+ acpi_ps_is_leading_char(subop) ||
+ ACPI_IS_ROOT_PREFIX(subop) ||
+ ACPI_IS_PARENT_PREFIX(subop)) {
+
+ /* NULL target (zero). Convert to a NULL namepath */
+
+ arg =
+ acpi_ps_alloc_op(AML_INT_NAMEPATH_OP,
+ parser_state->aml);
+ if (!arg) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ status =
+ acpi_ps_get_next_namepath(walk_state, parser_state,
+ arg,
+ ACPI_POSSIBLE_METHOD_CALL);
+
+ if (arg->common.aml_opcode == AML_INT_METHODCALL_OP) {
+
+ /* Free method call op and corresponding namestring sub-ob */
+
+ acpi_ps_free_op(arg->common.value.arg);
+ acpi_ps_free_op(arg);
+ arg = NULL;
+ walk_state->arg_count = 1;
+ }
+ } else {
+ /* Single complex argument, nothing returned */
+
+ walk_state->arg_count = 1;
+ }
+ break;
+
+ case ARGP_DATAOBJ:
+ case ARGP_TERMARG:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "**** TermArg/DataObj: %s (%2.2X)\n",
+ acpi_ut_get_argument_type_name(arg_type),
+ arg_type));
+
+ /* Single complex argument, nothing returned */
+
+ walk_state->arg_count = 1;
+ break;
+
+ case ARGP_DATAOBJLIST:
+ case ARGP_TERMLIST:
+ case ARGP_OBJLIST:
+
+ if (parser_state->aml < parser_state->pkg_end) {
+
+ /* Non-empty list of variable arguments, nothing returned */
+
+ walk_state->arg_count = ACPI_VAR_ARGS;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Invalid ArgType: 0x%X", arg_type));
+ status = AE_AML_OPERAND_TYPE;
+ break;
+ }
+
+ *return_arg = arg;
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c
new file mode 100644
index 000000000..840512fa9
--- /dev/null
+++ b/drivers/acpi/acpica/psloop.c
@@ -0,0 +1,561 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: psloop - Main AML parse loop
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+/*
+ * Parse the AML and build an operation tree as most interpreters, (such as
+ * Perl) do. Parsing is done by hand rather than with a YACC generated parser
+ * to tightly constrain stack and dynamic memory usage. Parsing is kept
+ * flexible and the code fairly compact by parsing based on a list of AML
+ * opcode templates in aml_op_info[].
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "acparser.h"
+#include "acdispat.h"
+#include "amlcode.h"
+#include "acconvert.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("psloop")
+
+/* Local prototypes */
+static acpi_status
+acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
+ u8 * aml_op_start, union acpi_parse_object *op);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_arguments
+ *
+ * PARAMETERS: walk_state - Current state
+ * aml_op_start - Op start in AML
+ * op - Current Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get arguments for passed Op.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
+ u8 * aml_op_start, union acpi_parse_object *op)
+{
+ acpi_status status = AE_OK;
+ union acpi_parse_object *arg = NULL;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_get_arguments, walk_state);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Get arguments for opcode [%s]\n",
+ op->common.aml_op_name));
+
+ switch (op->common.aml_opcode) {
+ case AML_BYTE_OP: /* AML_BYTEDATA_ARG */
+ case AML_WORD_OP: /* AML_WORDDATA_ARG */
+ case AML_DWORD_OP: /* AML_DWORDATA_ARG */
+ case AML_QWORD_OP: /* AML_QWORDATA_ARG */
+ case AML_STRING_OP: /* AML_ASCIICHARLIST_ARG */
+
+ /* Fill in constant or string argument directly */
+
+ acpi_ps_get_next_simple_arg(&(walk_state->parser_state),
+ GET_CURRENT_ARG_TYPE(walk_state->
+ arg_types),
+ op);
+ break;
+
+ case AML_INT_NAMEPATH_OP: /* AML_NAMESTRING_ARG */
+
+ status = acpi_ps_get_next_namepath(walk_state,
+ &(walk_state->parser_state),
+ op,
+ ACPI_POSSIBLE_METHOD_CALL);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ walk_state->arg_types = 0;
+ break;
+
+ default:
+ /*
+ * Op is not a constant or string, append each argument to the Op
+ */
+ while (GET_CURRENT_ARG_TYPE(walk_state->arg_types) &&
+ !walk_state->arg_count) {
+ walk_state->aml = walk_state->parser_state.aml;
+
+ switch (op->common.aml_opcode) {
+ case AML_METHOD_OP:
+ case AML_BUFFER_OP:
+ case AML_PACKAGE_OP:
+ case AML_VARIABLE_PACKAGE_OP:
+ case AML_WHILE_OP:
+
+ break;
+
+ default:
+
+ ASL_CV_CAPTURE_COMMENTS(walk_state);
+ break;
+ }
+
+ status =
+ acpi_ps_get_next_arg(walk_state,
+ &(walk_state->parser_state),
+ GET_CURRENT_ARG_TYPE
+ (walk_state->arg_types), &arg);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (arg) {
+ acpi_ps_append_arg(op, arg);
+ }
+
+ INCREMENT_ARG_LIST(walk_state->arg_types);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Final argument count: %8.8X pass %u\n",
+ walk_state->arg_count,
+ walk_state->pass_number));
+
+ /* Special processing for certain opcodes */
+
+ switch (op->common.aml_opcode) {
+ case AML_METHOD_OP:
+ /*
+ * Skip parsing of control method because we don't have enough
+ * info in the first pass to parse it correctly.
+ *
+ * Save the length and address of the body
+ */
+ op->named.data = walk_state->parser_state.aml;
+ op->named.length = (u32)
+ (walk_state->parser_state.pkg_end -
+ walk_state->parser_state.aml);
+
+ /* Skip body of method */
+
+ walk_state->parser_state.aml =
+ walk_state->parser_state.pkg_end;
+ walk_state->arg_count = 0;
+ break;
+
+ case AML_BUFFER_OP:
+ case AML_PACKAGE_OP:
+ case AML_VARIABLE_PACKAGE_OP:
+
+ if ((op->common.parent) &&
+ (op->common.parent->common.aml_opcode ==
+ AML_NAME_OP)
+ && (walk_state->pass_number <=
+ ACPI_IMODE_LOAD_PASS2)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Setup Package/Buffer: Pass %u, AML Ptr: %p\n",
+ walk_state->pass_number,
+ aml_op_start));
+
+ /*
+ * Skip parsing of Buffers and Packages because we don't have
+ * enough info in the first pass to parse them correctly.
+ */
+ op->named.data = aml_op_start;
+ op->named.length = (u32)
+ (walk_state->parser_state.pkg_end -
+ aml_op_start);
+
+ /* Skip body */
+
+ walk_state->parser_state.aml =
+ walk_state->parser_state.pkg_end;
+ walk_state->arg_count = 0;
+ }
+ break;
+
+ case AML_WHILE_OP:
+
+ if (walk_state->control_state) {
+ walk_state->control_state->control.package_end =
+ walk_state->parser_state.pkg_end;
+ }
+ break;
+
+ default:
+
+ /* No action for all other opcodes */
+
+ break;
+ }
+
+ break;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_parse_loop
+ *
+ * PARAMETERS: walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Parse AML (pointed to by the current parser state) and return
+ * a tree of ops.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+ union acpi_parse_object *op = NULL; /* current op */
+ struct acpi_parse_state *parser_state;
+ u8 *aml_op_start = NULL;
+ u8 opcode_length;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_parse_loop, walk_state);
+
+ if (walk_state->descending_callback == NULL) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ parser_state = &walk_state->parser_state;
+ walk_state->arg_types = 0;
+
+#ifndef ACPI_CONSTANT_EVAL_ONLY
+
+ if (walk_state->walk_type & ACPI_WALK_METHOD_RESTART) {
+
+ /* We are restarting a preempted control method */
+
+ if (acpi_ps_has_completed_scope(parser_state)) {
+ /*
+ * We must check if a predicate to an IF or WHILE statement
+ * was just completed
+ */
+ if ((parser_state->scope->parse_scope.op) &&
+ ((parser_state->scope->parse_scope.op->common.
+ aml_opcode == AML_IF_OP)
+ || (parser_state->scope->parse_scope.op->common.
+ aml_opcode == AML_WHILE_OP))
+ && (walk_state->control_state)
+ && (walk_state->control_state->common.state ==
+ ACPI_CONTROL_PREDICATE_EXECUTING)) {
+ /*
+ * A predicate was just completed, get the value of the
+ * predicate and branch based on that value
+ */
+ walk_state->op = NULL;
+ status =
+ acpi_ds_get_predicate_value(walk_state,
+ ACPI_TO_POINTER
+ (TRUE));
+ if (ACPI_FAILURE(status)
+ && !ACPI_CNTL_EXCEPTION(status)) {
+ if (status == AE_AML_NO_RETURN_VALUE) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Invoked method did not return a value"));
+ }
+
+ ACPI_EXCEPTION((AE_INFO, status,
+ "GetPredicate Failed"));
+ return_ACPI_STATUS(status);
+ }
+
+ status =
+ acpi_ps_next_parse_state(walk_state, op,
+ status);
+ }
+
+ acpi_ps_pop_scope(parser_state, &op,
+ &walk_state->arg_types,
+ &walk_state->arg_count);
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Popped scope, Op=%p\n", op));
+ } else if (walk_state->prev_op) {
+
+ /* We were in the middle of an op */
+
+ op = walk_state->prev_op;
+ walk_state->arg_types = walk_state->prev_arg_types;
+ }
+ }
+#endif
+
+ /* Iterative parsing loop, while there is more AML to process: */
+
+ while ((parser_state->aml < parser_state->aml_end) || (op)) {
+ ASL_CV_CAPTURE_COMMENTS(walk_state);
+
+ aml_op_start = parser_state->aml;
+ if (!op) {
+ status =
+ acpi_ps_create_op(walk_state, aml_op_start, &op);
+ if (ACPI_FAILURE(status)) {
+ /*
+ * ACPI_PARSE_MODULE_LEVEL means that we are loading a table by
+ * executing it as a control method. However, if we encounter
+ * an error while loading the table, we need to keep trying to
+ * load the table rather than aborting the table load. Set the
+ * status to AE_OK to proceed with the table load.
+ */
+ if ((walk_state->
+ parse_flags & ACPI_PARSE_MODULE_LEVEL)
+ && ((status == AE_ALREADY_EXISTS)
+ || (status == AE_NOT_FOUND))) {
+ status = AE_OK;
+ }
+ if (status == AE_CTRL_PARSE_CONTINUE) {
+ continue;
+ }
+
+ if (status == AE_CTRL_PARSE_PENDING) {
+ status = AE_OK;
+ }
+
+ if (status == AE_CTRL_TERMINATE) {
+ return_ACPI_STATUS(status);
+ }
+
+ status =
+ acpi_ps_complete_op(walk_state, &op,
+ status);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ if (acpi_ns_opens_scope
+ (acpi_ps_get_opcode_info
+ (walk_state->opcode)->object_type)) {
+ /*
+ * If the scope/device op fails to parse, skip the body of
+ * the scope op because the parse failure indicates that
+ * the device may not exist.
+ */
+ ACPI_INFO(("Skipping parse of AML opcode: %s (0x%4.4X)", acpi_ps_get_opcode_name(walk_state->opcode), walk_state->opcode));
+
+ /*
+ * Determine the opcode length before skipping the opcode.
+ * An opcode can be 1 byte or 2 bytes in length.
+ */
+ opcode_length = 1;
+ if ((walk_state->opcode & 0xFF00) ==
+ AML_EXTENDED_OPCODE) {
+ opcode_length = 2;
+ }
+ walk_state->parser_state.aml =
+ walk_state->aml + opcode_length;
+
+ walk_state->parser_state.aml =
+ acpi_ps_get_next_package_end
+ (&walk_state->parser_state);
+ walk_state->aml =
+ walk_state->parser_state.aml;
+ }
+
+ continue;
+ }
+
+ acpi_ex_start_trace_opcode(op, walk_state);
+ }
+
+ /*
+ * Start arg_count at zero because we don't know if there are
+ * any args yet
+ */
+ walk_state->arg_count = 0;
+
+ switch (op->common.aml_opcode) {
+ case AML_BYTE_OP:
+ case AML_WORD_OP:
+ case AML_DWORD_OP:
+ case AML_QWORD_OP:
+
+ break;
+
+ default:
+
+ ASL_CV_CAPTURE_COMMENTS(walk_state);
+ break;
+ }
+
+ /* Are there any arguments that must be processed? */
+
+ if (walk_state->arg_types) {
+
+ /* Get arguments */
+
+ status =
+ acpi_ps_get_arguments(walk_state, aml_op_start, op);
+ if (ACPI_FAILURE(status)) {
+ status =
+ acpi_ps_complete_op(walk_state, &op,
+ status);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ if ((walk_state->control_state) &&
+ ((walk_state->control_state->control.
+ opcode == AML_IF_OP)
+ || (walk_state->control_state->control.
+ opcode == AML_WHILE_OP))) {
+ /*
+ * If the if/while op fails to parse, we will skip parsing
+ * the body of the op.
+ */
+ parser_state->aml =
+ walk_state->control_state->control.
+ aml_predicate_start + 1;
+ parser_state->aml =
+ acpi_ps_get_next_package_end
+ (parser_state);
+ walk_state->aml = parser_state->aml;
+
+ ACPI_ERROR((AE_INFO,
+ "Skipping While/If block"));
+ if (*walk_state->aml == AML_ELSE_OP) {
+ ACPI_ERROR((AE_INFO,
+ "Skipping Else block"));
+ walk_state->parser_state.aml =
+ walk_state->aml + 1;
+ walk_state->parser_state.aml =
+ acpi_ps_get_next_package_end
+ (parser_state);
+ walk_state->aml =
+ parser_state->aml;
+ }
+ ACPI_FREE(acpi_ut_pop_generic_state
+ (&walk_state->control_state));
+ }
+ op = NULL;
+ continue;
+ }
+ }
+
+ /* Check for arguments that need to be processed */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Parseloop: argument count: %8.8X\n",
+ walk_state->arg_count));
+
+ if (walk_state->arg_count) {
+ /*
+ * There are arguments (complex ones), push Op and
+ * prepare for argument
+ */
+ status = acpi_ps_push_scope(parser_state, op,
+ walk_state->arg_types,
+ walk_state->arg_count);
+ if (ACPI_FAILURE(status)) {
+ status =
+ acpi_ps_complete_op(walk_state, &op,
+ status);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ continue;
+ }
+
+ op = NULL;
+ continue;
+ }
+
+ /*
+ * All arguments have been processed -- Op is complete,
+ * prepare for next
+ */
+ walk_state->op_info =
+ acpi_ps_get_opcode_info(op->common.aml_opcode);
+ if (walk_state->op_info->flags & AML_NAMED) {
+ if (op->common.aml_opcode == AML_REGION_OP ||
+ op->common.aml_opcode == AML_DATA_REGION_OP) {
+ /*
+ * Skip parsing of control method or opregion body,
+ * because we don't have enough info in the first pass
+ * to parse them correctly.
+ *
+ * Completed parsing an op_region declaration, we now
+ * know the length.
+ */
+ op->named.length =
+ (u32) (parser_state->aml - op->named.data);
+ }
+ }
+
+ if (walk_state->op_info->flags & AML_CREATE) {
+ /*
+ * Backup to beginning of create_XXXfield declaration (1 for
+ * Opcode)
+ *
+ * body_length is unknown until we parse the body
+ */
+ op->named.length =
+ (u32) (parser_state->aml - op->named.data);
+ }
+
+ if (op->common.aml_opcode == AML_BANK_FIELD_OP) {
+ /*
+ * Backup to beginning of bank_field declaration
+ *
+ * body_length is unknown until we parse the body
+ */
+ op->named.length =
+ (u32) (parser_state->aml - op->named.data);
+ }
+
+ /* This op complete, notify the dispatcher */
+
+ if (walk_state->ascending_callback != NULL) {
+ walk_state->op = op;
+ walk_state->opcode = op->common.aml_opcode;
+
+ status = walk_state->ascending_callback(walk_state);
+ status =
+ acpi_ps_next_parse_state(walk_state, op, status);
+ if (status == AE_CTRL_PENDING) {
+ status = AE_OK;
+ } else
+ if ((walk_state->
+ parse_flags & ACPI_PARSE_MODULE_LEVEL)
+ && (ACPI_AML_EXCEPTION(status)
+ || status == AE_ALREADY_EXISTS
+ || status == AE_NOT_FOUND)) {
+ /*
+ * ACPI_PARSE_MODULE_LEVEL flag means that we
+ * are currently loading a table by executing
+ * it as a control method. However, if we
+ * encounter an error while loading the table,
+ * we need to keep trying to load the table
+ * rather than aborting the table load (setting
+ * the status to AE_OK continues the table
+ * load). If we get a failure at this point, it
+ * means that the dispatcher got an error while
+ * trying to execute the Op.
+ */
+ status = AE_OK;
+ }
+ }
+
+ status = acpi_ps_complete_op(walk_state, &op, status);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ } /* while parser_state->Aml */
+
+ status = acpi_ps_complete_final_op(walk_state, op, status);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/psobject.c b/drivers/acpi/acpica/psobject.c
new file mode 100644
index 000000000..bca249e67
--- /dev/null
+++ b/drivers/acpi/acpica/psobject.c
@@ -0,0 +1,728 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: psobject - Support for parse objects
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acconvert.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("psobject")
+
+/* Local prototypes */
+static acpi_status acpi_ps_get_aml_opcode(struct acpi_walk_state *walk_state);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_aml_opcode
+ *
+ * PARAMETERS: walk_state - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Extract the next AML opcode from the input stream.
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_ps_get_aml_opcode(struct acpi_walk_state *walk_state)
+{
+ ACPI_ERROR_ONLY(u32 aml_offset);
+
+ ACPI_FUNCTION_TRACE_PTR(ps_get_aml_opcode, walk_state);
+
+ walk_state->aml = walk_state->parser_state.aml;
+ walk_state->opcode = acpi_ps_peek_opcode(&(walk_state->parser_state));
+
+ /*
+ * First cut to determine what we have found:
+ * 1) A valid AML opcode
+ * 2) A name string
+ * 3) An unknown/invalid opcode
+ */
+ walk_state->op_info = acpi_ps_get_opcode_info(walk_state->opcode);
+
+ switch (walk_state->op_info->class) {
+ case AML_CLASS_ASCII:
+ case AML_CLASS_PREFIX:
+ /*
+ * Starts with a valid prefix or ASCII char, this is a name
+ * string. Convert the bare name string to a namepath.
+ */
+ walk_state->opcode = AML_INT_NAMEPATH_OP;
+ walk_state->arg_types = ARGP_NAMESTRING;
+ break;
+
+ case AML_CLASS_UNKNOWN:
+
+ /* The opcode is unrecognized. Complain and skip unknown opcodes */
+
+ if (walk_state->pass_number == 2) {
+ ACPI_ERROR_ONLY(aml_offset =
+ (u32)ACPI_PTR_DIFF(walk_state->aml,
+ walk_state->
+ parser_state.
+ aml_start));
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown opcode 0x%.2X at table offset 0x%.4X, ignoring",
+ walk_state->opcode,
+ (u32)(aml_offset +
+ sizeof(struct acpi_table_header))));
+
+ ACPI_DUMP_BUFFER((walk_state->parser_state.aml - 16),
+ 48);
+
+#ifdef ACPI_ASL_COMPILER
+ /*
+ * This is executed for the disassembler only. Output goes
+ * to the disassembled ASL output file.
+ */
+ acpi_os_printf
+ ("/*\nError: Unknown opcode 0x%.2X at table offset 0x%.4X, context:\n",
+ walk_state->opcode,
+ (u32)(aml_offset +
+ sizeof(struct acpi_table_header)));
+
+ ACPI_ERROR((AE_INFO,
+ "Aborting disassembly, AML byte code is corrupt"));
+
+ /* Dump the context surrounding the invalid opcode */
+
+ acpi_ut_dump_buffer(((u8 *)walk_state->parser_state.
+ aml - 16), 48, DB_BYTE_DISPLAY,
+ (aml_offset +
+ sizeof(struct acpi_table_header) -
+ 16));
+ acpi_os_printf(" */\n");
+
+ /*
+ * Just abort the disassembly, cannot continue because the
+ * parser is essentially lost. The disassembler can then
+ * randomly fail because an ill-constructed parse tree
+ * can result.
+ */
+ return_ACPI_STATUS(AE_AML_BAD_OPCODE);
+#endif
+ }
+
+ /* Increment past one-byte or two-byte opcode */
+
+ walk_state->parser_state.aml++;
+ if (walk_state->opcode > 0xFF) { /* Can only happen if first byte is 0x5B */
+ walk_state->parser_state.aml++;
+ }
+
+ return_ACPI_STATUS(AE_CTRL_PARSE_CONTINUE);
+
+ default:
+
+ /* Found opcode info, this is a normal opcode */
+
+ walk_state->parser_state.aml +=
+ acpi_ps_get_opcode_size(walk_state->opcode);
+ walk_state->arg_types = walk_state->op_info->parse_args;
+ break;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_build_named_op
+ *
+ * PARAMETERS: walk_state - Current state
+ * aml_op_start - Begin of named Op in AML
+ * unnamed_op - Early Op (not a named Op)
+ * op - Returned Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Parse a named Op
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ps_build_named_op(struct acpi_walk_state *walk_state,
+ u8 *aml_op_start,
+ union acpi_parse_object *unnamed_op,
+ union acpi_parse_object **op)
+{
+ acpi_status status = AE_OK;
+ union acpi_parse_object *arg = NULL;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_build_named_op, walk_state);
+
+ unnamed_op->common.value.arg = NULL;
+ unnamed_op->common.arg_list_length = 0;
+ unnamed_op->common.aml_opcode = walk_state->opcode;
+
+ /*
+ * Get and append arguments until we find the node that contains
+ * the name (the type ARGP_NAME).
+ */
+ while (GET_CURRENT_ARG_TYPE(walk_state->arg_types) &&
+ (GET_CURRENT_ARG_TYPE(walk_state->arg_types) != ARGP_NAME)) {
+ ASL_CV_CAPTURE_COMMENTS(walk_state);
+ status =
+ acpi_ps_get_next_arg(walk_state,
+ &(walk_state->parser_state),
+ GET_CURRENT_ARG_TYPE(walk_state->
+ arg_types), &arg);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_ps_append_arg(unnamed_op, arg);
+ INCREMENT_ARG_LIST(walk_state->arg_types);
+ }
+
+ /* are there any inline comments associated with the name_seg?? If so, save this. */
+
+ ASL_CV_CAPTURE_COMMENTS(walk_state);
+
+#ifdef ACPI_ASL_COMPILER
+ if (acpi_gbl_current_inline_comment != NULL) {
+ unnamed_op->common.name_comment =
+ acpi_gbl_current_inline_comment;
+ acpi_gbl_current_inline_comment = NULL;
+ }
+#endif
+
+ /*
+ * Make sure that we found a NAME and didn't run out of arguments
+ */
+ if (!GET_CURRENT_ARG_TYPE(walk_state->arg_types)) {
+ return_ACPI_STATUS(AE_AML_NO_OPERAND);
+ }
+
+ /* We know that this arg is a name, move to next arg */
+
+ INCREMENT_ARG_LIST(walk_state->arg_types);
+
+ /*
+ * Find the object. This will either insert the object into
+ * the namespace or simply look it up
+ */
+ walk_state->op = NULL;
+
+ status = walk_state->descending_callback(walk_state, op);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During name lookup/catalog"));
+ }
+ return_ACPI_STATUS(status);
+ }
+
+ if (!*op) {
+ return_ACPI_STATUS(AE_CTRL_PARSE_CONTINUE);
+ }
+
+ status = acpi_ps_next_parse_state(walk_state, *op, status);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_CTRL_PENDING) {
+ status = AE_CTRL_PARSE_PENDING;
+ }
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_ps_append_arg(*op, unnamed_op->common.value.arg);
+
+#ifdef ACPI_ASL_COMPILER
+
+ /* save any comments that might be associated with unnamed_op. */
+
+ (*op)->common.inline_comment = unnamed_op->common.inline_comment;
+ (*op)->common.end_node_comment = unnamed_op->common.end_node_comment;
+ (*op)->common.close_brace_comment =
+ unnamed_op->common.close_brace_comment;
+ (*op)->common.name_comment = unnamed_op->common.name_comment;
+ (*op)->common.comment_list = unnamed_op->common.comment_list;
+ (*op)->common.end_blk_comment = unnamed_op->common.end_blk_comment;
+ (*op)->common.cv_filename = unnamed_op->common.cv_filename;
+ (*op)->common.cv_parent_filename =
+ unnamed_op->common.cv_parent_filename;
+ (*op)->named.aml = unnamed_op->common.aml;
+
+ unnamed_op->common.inline_comment = NULL;
+ unnamed_op->common.end_node_comment = NULL;
+ unnamed_op->common.close_brace_comment = NULL;
+ unnamed_op->common.name_comment = NULL;
+ unnamed_op->common.comment_list = NULL;
+ unnamed_op->common.end_blk_comment = NULL;
+#endif
+
+ if ((*op)->common.aml_opcode == AML_REGION_OP ||
+ (*op)->common.aml_opcode == AML_DATA_REGION_OP) {
+ /*
+ * Defer final parsing of an operation_region body, because we don't
+ * have enough info in the first pass to parse it correctly (i.e.,
+ * there may be method calls within the term_arg elements of the body.)
+ *
+ * However, we must continue parsing because the opregion is not a
+ * standalone package -- we don't know where the end is at this point.
+ *
+ * (Length is unknown until parse of the body complete)
+ */
+ (*op)->named.data = aml_op_start;
+ (*op)->named.length = 0;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_create_op
+ *
+ * PARAMETERS: walk_state - Current state
+ * aml_op_start - Op start in AML
+ * new_op - Returned Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get Op from AML
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ps_create_op(struct acpi_walk_state *walk_state,
+ u8 *aml_op_start, union acpi_parse_object **new_op)
+{
+ acpi_status status = AE_OK;
+ union acpi_parse_object *op;
+ union acpi_parse_object *named_op = NULL;
+ union acpi_parse_object *parent_scope;
+ u8 argument_count;
+ const struct acpi_opcode_info *op_info;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_create_op, walk_state);
+
+ status = acpi_ps_get_aml_opcode(walk_state);
+ if (status == AE_CTRL_PARSE_CONTINUE) {
+ return_ACPI_STATUS(AE_CTRL_PARSE_CONTINUE);
+ }
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Create Op structure and append to parent's argument list */
+
+ walk_state->op_info = acpi_ps_get_opcode_info(walk_state->opcode);
+ op = acpi_ps_alloc_op(walk_state->opcode, aml_op_start);
+ if (!op) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ if (walk_state->op_info->flags & AML_NAMED) {
+ status =
+ acpi_ps_build_named_op(walk_state, aml_op_start, op,
+ &named_op);
+ acpi_ps_free_op(op);
+
+#ifdef ACPI_ASL_COMPILER
+ if (acpi_gbl_disasm_flag
+ && walk_state->opcode == AML_EXTERNAL_OP
+ && status == AE_NOT_FOUND) {
+ /*
+ * If parsing of AML_EXTERNAL_OP's name path fails, then skip
+ * past this opcode and keep parsing. This is a much better
+ * alternative than to abort the entire disassembler. At this
+ * point, the parser_state is at the end of the namepath of the
+ * external declaration opcode. Setting walk_state->Aml to
+ * walk_state->parser_state.Aml + 2 moves increments the
+ * walk_state->Aml past the object type and the paramcount of the
+ * external opcode.
+ */
+ walk_state->aml = walk_state->parser_state.aml + 2;
+ walk_state->parser_state.aml = walk_state->aml;
+ return_ACPI_STATUS(AE_CTRL_PARSE_CONTINUE);
+ }
+#endif
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ *new_op = named_op;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Not a named opcode, just allocate Op and append to parent */
+
+ if (walk_state->op_info->flags & AML_CREATE) {
+ /*
+ * Backup to beginning of create_XXXfield declaration
+ * body_length is unknown until we parse the body
+ */
+ op->named.data = aml_op_start;
+ op->named.length = 0;
+ }
+
+ if (walk_state->opcode == AML_BANK_FIELD_OP) {
+ /*
+ * Backup to beginning of bank_field declaration
+ * body_length is unknown until we parse the body
+ */
+ op->named.data = aml_op_start;
+ op->named.length = 0;
+ }
+
+ parent_scope = acpi_ps_get_parent_scope(&(walk_state->parser_state));
+ acpi_ps_append_arg(parent_scope, op);
+
+ if (parent_scope) {
+ op_info =
+ acpi_ps_get_opcode_info(parent_scope->common.aml_opcode);
+ if (op_info->flags & AML_HAS_TARGET) {
+ argument_count =
+ acpi_ps_get_argument_count(op_info->type);
+ if (parent_scope->common.arg_list_length >
+ argument_count) {
+ op->common.flags |= ACPI_PARSEOP_TARGET;
+ }
+ }
+
+ /*
+ * Special case for both Increment() and Decrement(), where
+ * the lone argument is both a source and a target.
+ */
+ else if ((parent_scope->common.aml_opcode == AML_INCREMENT_OP)
+ || (parent_scope->common.aml_opcode ==
+ AML_DECREMENT_OP)) {
+ op->common.flags |= ACPI_PARSEOP_TARGET;
+ }
+ }
+
+ if (walk_state->descending_callback != NULL) {
+ /*
+ * Find the object. This will either insert the object into
+ * the namespace or simply look it up
+ */
+ walk_state->op = *new_op = op;
+
+ status = walk_state->descending_callback(walk_state, &op);
+ status = acpi_ps_next_parse_state(walk_state, op, status);
+ if (status == AE_CTRL_PENDING) {
+ status = AE_CTRL_PARSE_PENDING;
+ }
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_complete_op
+ *
+ * PARAMETERS: walk_state - Current state
+ * op - Returned Op
+ * status - Parse status before complete Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Complete Op
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ps_complete_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object **op, acpi_status status)
+{
+ acpi_status status2;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_complete_op, walk_state);
+
+ /*
+ * Finished one argument of the containing scope
+ */
+ walk_state->parser_state.scope->parse_scope.arg_count--;
+
+ /* Close this Op (will result in parse subtree deletion) */
+
+ status2 = acpi_ps_complete_this_op(walk_state, *op);
+ if (ACPI_FAILURE(status2)) {
+ return_ACPI_STATUS(status2);
+ }
+
+ *op = NULL;
+
+ switch (status) {
+ case AE_OK:
+
+ break;
+
+ case AE_CTRL_TRANSFER:
+
+ /* We are about to transfer to a called method */
+
+ walk_state->prev_op = NULL;
+ walk_state->prev_arg_types = walk_state->arg_types;
+ return_ACPI_STATUS(status);
+
+ case AE_CTRL_END:
+
+ acpi_ps_pop_scope(&(walk_state->parser_state), op,
+ &walk_state->arg_types,
+ &walk_state->arg_count);
+
+ if (*op) {
+ walk_state->op = *op;
+ walk_state->op_info =
+ acpi_ps_get_opcode_info((*op)->common.aml_opcode);
+ walk_state->opcode = (*op)->common.aml_opcode;
+
+ status = walk_state->ascending_callback(walk_state);
+ (void)acpi_ps_next_parse_state(walk_state, *op, status);
+
+ status2 = acpi_ps_complete_this_op(walk_state, *op);
+ if (ACPI_FAILURE(status2)) {
+ return_ACPI_STATUS(status2);
+ }
+ }
+
+ break;
+
+ case AE_CTRL_BREAK:
+ case AE_CTRL_CONTINUE:
+
+ /* Pop off scopes until we find the While */
+
+ while (!(*op) || ((*op)->common.aml_opcode != AML_WHILE_OP)) {
+ acpi_ps_pop_scope(&(walk_state->parser_state), op,
+ &walk_state->arg_types,
+ &walk_state->arg_count);
+ }
+
+ /* Close this iteration of the While loop */
+
+ walk_state->op = *op;
+ walk_state->op_info =
+ acpi_ps_get_opcode_info((*op)->common.aml_opcode);
+ walk_state->opcode = (*op)->common.aml_opcode;
+
+ status = walk_state->ascending_callback(walk_state);
+ (void)acpi_ps_next_parse_state(walk_state, *op, status);
+
+ status2 = acpi_ps_complete_this_op(walk_state, *op);
+ if (ACPI_FAILURE(status2)) {
+ return_ACPI_STATUS(status2);
+ }
+
+ break;
+
+ case AE_CTRL_TERMINATE:
+
+ /* Clean up */
+ do {
+ if (*op) {
+ status2 =
+ acpi_ps_complete_this_op(walk_state, *op);
+ if (ACPI_FAILURE(status2)) {
+ return_ACPI_STATUS(status2);
+ }
+
+ acpi_ut_delete_generic_state
+ (acpi_ut_pop_generic_state
+ (&walk_state->control_state));
+ }
+
+ acpi_ps_pop_scope(&(walk_state->parser_state), op,
+ &walk_state->arg_types,
+ &walk_state->arg_count);
+
+ } while (*op);
+
+ return_ACPI_STATUS(AE_OK);
+
+ default: /* All other non-AE_OK status */
+
+ do {
+ if (*op) {
+ /*
+ * These Opcodes need to be removed from the namespace because they
+ * get created even if these opcodes cannot be created due to
+ * errors.
+ */
+ if (((*op)->common.aml_opcode == AML_REGION_OP)
+ || ((*op)->common.aml_opcode ==
+ AML_DATA_REGION_OP)) {
+ acpi_ns_delete_children((*op)->common.
+ node);
+ acpi_ns_remove_node((*op)->common.node);
+ (*op)->common.node = NULL;
+ acpi_ps_delete_parse_tree(*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);
+
+#if 0
+ /*
+ * TBD: Cleanup parse ops on error
+ */
+ if (*op == NULL) {
+ acpi_ps_pop_scope(parser_state, op,
+ &walk_state->arg_types,
+ &walk_state->arg_count);
+ }
+#endif
+ walk_state->prev_op = NULL;
+ walk_state->prev_arg_types = walk_state->arg_types;
+
+ if (walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL) {
+ /*
+ * There was something that went wrong while executing code at the
+ * module-level. We need to skip parsing whatever caused the
+ * error and keep going. One runtime error during the table load
+ * should not cause the entire table to not be loaded. This is
+ * because there could be correct AML beyond the parts that caused
+ * the runtime error.
+ */
+ ACPI_INFO(("Ignoring error and continuing table load"));
+ return_ACPI_STATUS(AE_OK);
+ }
+ return_ACPI_STATUS(status);
+ }
+
+ /* This scope complete? */
+
+ if (acpi_ps_has_completed_scope(&(walk_state->parser_state))) {
+ acpi_ps_pop_scope(&(walk_state->parser_state), op,
+ &walk_state->arg_types,
+ &walk_state->arg_count);
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "Popped scope, Op=%p\n", *op));
+ } else {
+ *op = NULL;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_complete_final_op
+ *
+ * PARAMETERS: walk_state - Current state
+ * op - Current Op
+ * status - Current parse status before complete last
+ * Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Complete last Op.
+ *
+ ******************************************************************************/
+
+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_FUNCTION_TRACE_PTR(ps_complete_final_op, walk_state);
+
+ /*
+ * Complete the last Op (if not completed), and clear the scope stack.
+ * It is easily possible to end an AML "package" with an unbounded number
+ * of open scopes (such as when several ASL blocks are closed with
+ * sequential closing braces). We want to terminate each one cleanly.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "AML package complete at Op %p\n",
+ op));
+ do {
+ if (op) {
+ if (walk_state->ascending_callback != NULL) {
+ walk_state->op = op;
+ walk_state->op_info =
+ acpi_ps_get_opcode_info(op->common.
+ aml_opcode);
+ walk_state->opcode = op->common.aml_opcode;
+
+ status =
+ walk_state->ascending_callback(walk_state);
+ status =
+ acpi_ps_next_parse_state(walk_state, op,
+ status);
+ if (status == AE_CTRL_PENDING) {
+ status =
+ acpi_ps_complete_op(walk_state, &op,
+ AE_OK);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ 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);
+ }
+
+ else if (ACPI_FAILURE(status)) {
+
+ /* First error is most important */
+
+ (void)
+ acpi_ps_complete_this_op(walk_state,
+ op);
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ 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);
+}
diff --git a/drivers/acpi/acpica/psopcode.c b/drivers/acpi/acpica/psopcode.c
new file mode 100644
index 000000000..8c34c0ffb
--- /dev/null
+++ b/drivers/acpi/acpica/psopcode.c
@@ -0,0 +1,629 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: psopcode - Parser/Interpreter opcode information table
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acopcode.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("psopcode")
+
+/*******************************************************************************
+ *
+ * NAME: acpi_gbl_aml_op_info
+ *
+ * DESCRIPTION: Opcode table. Each entry contains <opcode, type, name, operands>
+ * The name is a simple ascii string, the operand specifier is an
+ * ascii string with one letter per operand. The letter specifies
+ * the operand type.
+ *
+ ******************************************************************************/
+/*
+ * Summary of opcode types/flags
+ *
+
+ Opcodes that have associated namespace objects (AML_NSOBJECT flag)
+
+ AML_SCOPE_OP
+ AML_DEVICE_OP
+ AML_THERMAL_ZONE_OP
+ AML_METHOD_OP
+ AML_POWER_RESOURCE_OP
+ AML_PROCESSOR_OP
+ AML_FIELD_OP
+ AML_INDEX_FIELD_OP
+ AML_BANK_FIELD_OP
+ AML_NAME_OP
+ AML_ALIAS_OP
+ AML_MUTEX_OP
+ AML_EVENT_OP
+ AML_REGION_OP
+ AML_CREATE_FIELD_OP
+ AML_CREATE_BIT_FIELD_OP
+ AML_CREATE_BYTE_FIELD_OP
+ AML_CREATE_WORD_FIELD_OP
+ AML_CREATE_DWORD_FIELD_OP
+ AML_CREATE_QWORD_FIELD_OP
+ AML_INT_NAMEDFIELD_OP
+ AML_INT_METHODCALL_OP
+ AML_INT_NAMEPATH_OP
+
+ Opcodes that are "namespace" opcodes (AML_NSOPCODE flag)
+
+ AML_SCOPE_OP
+ AML_DEVICE_OP
+ AML_THERMAL_ZONE_OP
+ AML_METHOD_OP
+ AML_POWER_RESOURCE_OP
+ AML_PROCESSOR_OP
+ AML_FIELD_OP
+ AML_INDEX_FIELD_OP
+ AML_BANK_FIELD_OP
+ AML_NAME_OP
+ AML_ALIAS_OP
+ AML_MUTEX_OP
+ AML_EVENT_OP
+ AML_REGION_OP
+ AML_INT_NAMEDFIELD_OP
+
+ Opcodes that have an associated namespace node (AML_NSNODE flag)
+
+ AML_SCOPE_OP
+ AML_DEVICE_OP
+ AML_THERMAL_ZONE_OP
+ AML_METHOD_OP
+ AML_POWER_RESOURCE_OP
+ AML_PROCESSOR_OP
+ AML_NAME_OP
+ AML_ALIAS_OP
+ AML_MUTEX_OP
+ AML_EVENT_OP
+ AML_REGION_OP
+ AML_CREATE_FIELD_OP
+ AML_CREATE_BIT_FIELD_OP
+ AML_CREATE_BYTE_FIELD_OP
+ AML_CREATE_WORD_FIELD_OP
+ AML_CREATE_DWORD_FIELD_OP
+ AML_CREATE_QWORD_FIELD_OP
+ AML_INT_NAMEDFIELD_OP
+ AML_INT_METHODCALL_OP
+ AML_INT_NAMEPATH_OP
+
+ Opcodes that define named ACPI objects (AML_NAMED flag)
+
+ AML_SCOPE_OP
+ AML_DEVICE_OP
+ AML_THERMAL_ZONE_OP
+ AML_METHOD_OP
+ AML_POWER_RESOURCE_OP
+ AML_PROCESSOR_OP
+ AML_NAME_OP
+ AML_ALIAS_OP
+ AML_MUTEX_OP
+ AML_EVENT_OP
+ AML_REGION_OP
+ AML_INT_NAMEDFIELD_OP
+
+ Opcodes that contain executable AML as part of the definition that
+ must be deferred until needed
+
+ AML_METHOD_OP
+ AML_VARIABLE_PACKAGE_OP
+ AML_CREATE_FIELD_OP
+ AML_CREATE_BIT_FIELD_OP
+ AML_CREATE_BYTE_FIELD_OP
+ AML_CREATE_WORD_FIELD_OP
+ AML_CREATE_DWORD_FIELD_OP
+ AML_CREATE_QWORD_FIELD_OP
+ AML_REGION_OP
+ AML_BUFFER_OP
+
+ Field opcodes
+
+ AML_CREATE_FIELD_OP
+ AML_FIELD_OP
+ AML_INDEX_FIELD_OP
+ AML_BANK_FIELD_OP
+
+ Field "Create" opcodes
+
+ AML_CREATE_FIELD_OP
+ AML_CREATE_BIT_FIELD_OP
+ AML_CREATE_BYTE_FIELD_OP
+ AML_CREATE_WORD_FIELD_OP
+ AML_CREATE_DWORD_FIELD_OP
+ AML_CREATE_QWORD_FIELD_OP
+
+ ******************************************************************************/
+/*
+ * Master Opcode information table. A summary of everything we know about each
+ * opcode, all in one place.
+ */
+const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES] = {
+/*! [Begin] no source code translation */
+/* Index Name Parser Args Interpreter Args ObjectType Class Type Flags */
+
+/* 00 */ ACPI_OP("Zero", ARGP_ZERO_OP, ARGI_ZERO_OP, ACPI_TYPE_INTEGER,
+ AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT),
+/* 01 */ ACPI_OP("One", ARGP_ONE_OP, ARGI_ONE_OP, ACPI_TYPE_INTEGER,
+ AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT),
+/* 02 */ ACPI_OP("Alias", ARGP_ALIAS_OP, ARGI_ALIAS_OP,
+ ACPI_TYPE_LOCAL_ALIAS, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_SIMPLE,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
+/* 03 */ ACPI_OP("Name", ARGP_NAME_OP, ARGI_NAME_OP, ACPI_TYPE_ANY,
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
+/* 04 */ ACPI_OP("ByteConst", ARGP_BYTE_OP, ARGI_BYTE_OP,
+ ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT),
+/* 05 */ ACPI_OP("WordConst", ARGP_WORD_OP, ARGI_WORD_OP,
+ ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT),
+/* 06 */ ACPI_OP("DwordConst", ARGP_DWORD_OP, ARGI_DWORD_OP,
+ ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT),
+/* 07 */ ACPI_OP("String", ARGP_STRING_OP, ARGI_STRING_OP,
+ ACPI_TYPE_STRING, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT),
+/* 08 */ ACPI_OP("Scope", ARGP_SCOPE_OP, ARGI_SCOPE_OP,
+ ACPI_TYPE_LOCAL_SCOPE, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_NO_OBJ,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
+/* 09 */ ACPI_OP("Buffer", ARGP_BUFFER_OP, ARGI_BUFFER_OP,
+ ACPI_TYPE_BUFFER, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_OBJECT,
+ AML_HAS_ARGS | AML_DEFER | AML_CONSTANT),
+/* 0A */ ACPI_OP("Package", ARGP_PACKAGE_OP, ARGI_PACKAGE_OP,
+ ACPI_TYPE_PACKAGE, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_OBJECT,
+ AML_HAS_ARGS | AML_DEFER | AML_CONSTANT),
+/* 0B */ ACPI_OP("Method", ARGP_METHOD_OP, ARGI_METHOD_OP,
+ ACPI_TYPE_METHOD, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_COMPLEX,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED | AML_DEFER),
+/* 0C */ ACPI_OP("Local0", ARGP_LOCAL0, ARGI_LOCAL0,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
+/* 0D */ ACPI_OP("Local1", ARGP_LOCAL1, ARGI_LOCAL1,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
+/* 0E */ ACPI_OP("Local2", ARGP_LOCAL2, ARGI_LOCAL2,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
+/* 0F */ ACPI_OP("Local3", ARGP_LOCAL3, ARGI_LOCAL3,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
+/* 10 */ ACPI_OP("Local4", ARGP_LOCAL4, ARGI_LOCAL4,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
+/* 11 */ ACPI_OP("Local5", ARGP_LOCAL5, ARGI_LOCAL5,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
+/* 12 */ ACPI_OP("Local6", ARGP_LOCAL6, ARGI_LOCAL6,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
+/* 13 */ ACPI_OP("Local7", ARGP_LOCAL7, ARGI_LOCAL7,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
+/* 14 */ ACPI_OP("Arg0", ARGP_ARG0, ARGI_ARG0,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
+/* 15 */ ACPI_OP("Arg1", ARGP_ARG1, ARGI_ARG1,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
+/* 16 */ ACPI_OP("Arg2", ARGP_ARG2, ARGI_ARG2,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
+/* 17 */ ACPI_OP("Arg3", ARGP_ARG3, ARGI_ARG3,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
+/* 18 */ ACPI_OP("Arg4", ARGP_ARG4, ARGI_ARG4,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
+/* 19 */ ACPI_OP("Arg5", ARGP_ARG5, ARGI_ARG5,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
+/* 1A */ ACPI_OP("Arg6", ARGP_ARG6, ARGI_ARG6,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
+/* 1B */ ACPI_OP("Store", ARGP_STORE_OP, ARGI_STORE_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R),
+/* 1C */ ACPI_OP("RefOf", ARGP_REF_OF_OP, ARGI_REF_OF_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R),
+/* 1D */ ACPI_OP("Add", ARGP_ADD_OP, ARGI_ADD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+/* 1E */ ACPI_OP("Concatenate", ARGP_CONCAT_OP, ARGI_CONCAT_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
+/* 1F */ ACPI_OP("Subtract", ARGP_SUBTRACT_OP, ARGI_SUBTRACT_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+/* 20 */ ACPI_OP("Increment", ARGP_INCREMENT_OP, ARGI_INCREMENT_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT),
+/* 21 */ ACPI_OP("Decrement", ARGP_DECREMENT_OP, ARGI_DECREMENT_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT),
+/* 22 */ ACPI_OP("Multiply", ARGP_MULTIPLY_OP, ARGI_MULTIPLY_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+/* 23 */ ACPI_OP("Divide", ARGP_DIVIDE_OP, ARGI_DIVIDE_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_2T_1R,
+ AML_FLAGS_EXEC_2A_2T_1R | AML_CONSTANT),
+/* 24 */ ACPI_OP("ShiftLeft", ARGP_SHIFT_LEFT_OP, ARGI_SHIFT_LEFT_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+/* 25 */ ACPI_OP("ShiftRight", ARGP_SHIFT_RIGHT_OP, ARGI_SHIFT_RIGHT_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+/* 26 */ ACPI_OP("And", ARGP_BIT_AND_OP, ARGI_BIT_AND_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+/* 27 */ ACPI_OP("NAnd", ARGP_BIT_NAND_OP, ARGI_BIT_NAND_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+/* 28 */ ACPI_OP("Or", ARGP_BIT_OR_OP, ARGI_BIT_OR_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+/* 29 */ ACPI_OP("NOr", ARGP_BIT_NOR_OP, ARGI_BIT_NOR_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+/* 2A */ ACPI_OP("XOr", ARGP_BIT_XOR_OP, ARGI_BIT_XOR_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+/* 2B */ ACPI_OP("Not", ARGP_BIT_NOT_OP, ARGI_BIT_NOT_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+/* 2C */ ACPI_OP("FindSetLeftBit", ARGP_FIND_SET_LEFT_BIT_OP,
+ ARGI_FIND_SET_LEFT_BIT_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+/* 2D */ ACPI_OP("FindSetRightBit", ARGP_FIND_SET_RIGHT_BIT_OP,
+ ARGI_FIND_SET_RIGHT_BIT_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+/* 2E */ ACPI_OP("DerefOf", ARGP_DEREF_OF_OP, ARGI_DEREF_OF_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R),
+/* 2F */ ACPI_OP("Notify", ARGP_NOTIFY_OP, ARGI_NOTIFY_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_0T_0R, AML_FLAGS_EXEC_2A_0T_0R),
+/* 30 */ ACPI_OP("SizeOf", ARGP_SIZE_OF_OP, ARGI_SIZE_OF_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE),
+/* 31 */ ACPI_OP("Index", ARGP_INDEX_OP, ARGI_INDEX_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R),
+/* 32 */ ACPI_OP("Match", ARGP_MATCH_OP, ARGI_MATCH_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_6A_0T_1R,
+ AML_FLAGS_EXEC_6A_0T_1R | AML_CONSTANT),
+/* 33 */ ACPI_OP("CreateDWordField", ARGP_CREATE_DWORD_FIELD_OP,
+ ARGI_CREATE_DWORD_FIELD_OP,
+ ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_CREATE),
+/* 34 */ ACPI_OP("CreateWordField", ARGP_CREATE_WORD_FIELD_OP,
+ ARGI_CREATE_WORD_FIELD_OP,
+ ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_CREATE),
+/* 35 */ ACPI_OP("CreateByteField", ARGP_CREATE_BYTE_FIELD_OP,
+ ARGI_CREATE_BYTE_FIELD_OP,
+ ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_CREATE),
+/* 36 */ ACPI_OP("CreateBitField", ARGP_CREATE_BIT_FIELD_OP,
+ ARGI_CREATE_BIT_FIELD_OP,
+ ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_CREATE),
+/* 37 */ ACPI_OP("ObjectType", ARGP_OBJECT_TYPE_OP, ARGI_OBJECT_TYPE_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE),
+/* 38 */ ACPI_OP("LAnd", ARGP_LAND_OP, ARGI_LAND_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC |
+ AML_CONSTANT),
+/* 39 */ ACPI_OP("LOr", ARGP_LOR_OP, ARGI_LOR_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC |
+ AML_CONSTANT),
+/* 3A */ ACPI_OP("LNot", ARGP_LNOT_OP, ARGI_LNOT_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT),
+/* 3B */ ACPI_OP("LEqual", ARGP_LEQUAL_OP, ARGI_LEQUAL_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT),
+/* 3C */ ACPI_OP("LGreater", ARGP_LGREATER_OP, ARGI_LGREATER_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT),
+/* 3D */ ACPI_OP("LLess", ARGP_LLESS_OP, ARGI_LLESS_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT),
+/* 3E */ ACPI_OP("If", ARGP_IF_OP, ARGI_IF_OP, ACPI_TYPE_ANY,
+ AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS),
+/* 3F */ ACPI_OP("Else", ARGP_ELSE_OP, ARGI_ELSE_OP, ACPI_TYPE_ANY,
+ AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS),
+/* 40 */ ACPI_OP("While", ARGP_WHILE_OP, ARGI_WHILE_OP, ACPI_TYPE_ANY,
+ AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS),
+/* 41 */ ACPI_OP("Noop", ARGP_NOOP_OP, ARGI_NOOP_OP, ACPI_TYPE_ANY,
+ AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
+/* 42 */ ACPI_OP("Return", ARGP_RETURN_OP, ARGI_RETURN_OP,
+ ACPI_TYPE_ANY, AML_CLASS_CONTROL,
+ AML_TYPE_CONTROL, AML_HAS_ARGS),
+/* 43 */ ACPI_OP("Break", ARGP_BREAK_OP, ARGI_BREAK_OP, ACPI_TYPE_ANY,
+ AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
+/* 44 */ ACPI_OP("BreakPoint", ARGP_BREAK_POINT_OP, ARGI_BREAK_POINT_OP,
+ ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
+/* 45 */ ACPI_OP("Ones", ARGP_ONES_OP, ARGI_ONES_OP, ACPI_TYPE_INTEGER,
+ AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT),
+
+/* Prefixed opcodes (Two-byte opcodes with a prefix op) */
+
+/* 46 */ ACPI_OP("Mutex", ARGP_MUTEX_OP, ARGI_MUTEX_OP, ACPI_TYPE_MUTEX,
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
+/* 47 */ ACPI_OP("Event", ARGP_EVENT_OP, ARGI_EVENT_OP, ACPI_TYPE_EVENT,
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE,
+ AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED),
+/* 48 */ ACPI_OP("CondRefOf", ARGP_COND_REF_OF_OP, ARGI_COND_REF_OF_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R),
+/* 49 */ ACPI_OP("CreateField", ARGP_CREATE_FIELD_OP,
+ ARGI_CREATE_FIELD_OP, ACPI_TYPE_BUFFER_FIELD,
+ AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_FIELD | AML_CREATE),
+/* 4A */ ACPI_OP("Load", ARGP_LOAD_OP, ARGI_LOAD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R),
+/* 4B */ ACPI_OP("Stall", ARGP_STALL_OP, ARGI_STALL_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R,
+ AML_FLAGS_EXEC_1A_0T_0R),
+/* 4C */ ACPI_OP("Sleep", ARGP_SLEEP_OP, ARGI_SLEEP_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R,
+ AML_FLAGS_EXEC_1A_0T_0R),
+/* 4D */ ACPI_OP("Acquire", ARGP_ACQUIRE_OP, ARGI_ACQUIRE_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R),
+/* 4E */ ACPI_OP("Signal", ARGP_SIGNAL_OP, ARGI_SIGNAL_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R),
+/* 4F */ ACPI_OP("Wait", ARGP_WAIT_OP, ARGI_WAIT_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R),
+/* 50 */ ACPI_OP("Reset", ARGP_RESET_OP, ARGI_RESET_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R,
+ AML_FLAGS_EXEC_1A_0T_0R),
+/* 51 */ ACPI_OP("Release", ARGP_RELEASE_OP, ARGI_RELEASE_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R),
+/* 52 */ ACPI_OP("FromBCD", ARGP_FROM_BCD_OP, ARGI_FROM_BCD_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+/* 53 */ ACPI_OP("ToBCD", ARGP_TO_BCD_OP, ARGI_TO_BCD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+/* 54 */ ACPI_OP("Unload", ARGP_UNLOAD_OP, ARGI_UNLOAD_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R),
+/* 55 */ ACPI_OP("Revision", ARGP_REVISION_OP, ARGI_REVISION_OP,
+ ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
+ AML_TYPE_CONSTANT, 0),
+/* 56 */ ACPI_OP("Debug", ARGP_DEBUG_OP, ARGI_DEBUG_OP,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_CONSTANT, 0),
+/* 57 */ ACPI_OP("Fatal", ARGP_FATAL_OP, ARGI_FATAL_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_0T_0R,
+ AML_FLAGS_EXEC_3A_0T_0R),
+/* 58 */ ACPI_OP("OperationRegion", ARGP_REGION_OP, ARGI_REGION_OP,
+ ACPI_TYPE_REGION, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_COMPLEX,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED | AML_DEFER),
+/* 59 */ ACPI_OP("Field", ARGP_FIELD_OP, ARGI_FIELD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_FIELD),
+/* 5A */ ACPI_OP("Device", ARGP_DEVICE_OP, ARGI_DEVICE_OP,
+ ACPI_TYPE_DEVICE, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_NO_OBJ,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
+/* 5B */ ACPI_OP("Processor", ARGP_PROCESSOR_OP, ARGI_PROCESSOR_OP,
+ ACPI_TYPE_PROCESSOR, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_SIMPLE,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
+/* 5C */ ACPI_OP("PowerResource", ARGP_POWER_RES_OP, ARGI_POWER_RES_OP,
+ ACPI_TYPE_POWER, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_SIMPLE,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
+/* 5D */ ACPI_OP("ThermalZone", ARGP_THERMAL_ZONE_OP,
+ ARGI_THERMAL_ZONE_OP, ACPI_TYPE_THERMAL,
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
+/* 5E */ ACPI_OP("IndexField", ARGP_INDEX_FIELD_OP, ARGI_INDEX_FIELD_OP,
+ ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_FIELD),
+/* 5F */ ACPI_OP("BankField", ARGP_BANK_FIELD_OP, ARGI_BANK_FIELD_OP,
+ ACPI_TYPE_LOCAL_BANK_FIELD,
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_FIELD | AML_DEFER),
+
+/* Internal opcodes that map to invalid AML opcodes */
+
+/* 60 */ ACPI_OP("LNotEqual", ARGP_LNOTEQUAL_OP, ARGI_LNOTEQUAL_OP,
+ ACPI_TYPE_ANY, AML_CLASS_INTERNAL,
+ AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT),
+/* 61 */ ACPI_OP("LLessEqual", ARGP_LLESSEQUAL_OP, ARGI_LLESSEQUAL_OP,
+ ACPI_TYPE_ANY, AML_CLASS_INTERNAL,
+ AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT),
+/* 62 */ ACPI_OP("LGreaterEqual", ARGP_LGREATEREQUAL_OP,
+ ARGI_LGREATEREQUAL_OP, ACPI_TYPE_ANY,
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS,
+ AML_HAS_ARGS | AML_CONSTANT),
+/* 63 */ ACPI_OP("-NamePath-", ARGP_NAMEPATH_OP, ARGI_NAMEPATH_OP,
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_NSOBJECT | AML_NSNODE),
+/* 64 */ ACPI_OP("-MethodCall-", ARGP_METHODCALL_OP, ARGI_METHODCALL_OP,
+ ACPI_TYPE_METHOD, AML_CLASS_METHOD_CALL,
+ AML_TYPE_METHOD_CALL,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE),
+/* 65 */ ACPI_OP("-ByteList-", ARGP_BYTELIST_OP, ARGI_BYTELIST_OP,
+ ACPI_TYPE_ANY, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, 0),
+/* 66 */ ACPI_OP("-ReservedField-", ARGP_RESERVEDFIELD_OP,
+ ARGI_RESERVEDFIELD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
+/* 67 */ ACPI_OP("-NamedField-", ARGP_NAMEDFIELD_OP, ARGI_NAMEDFIELD_OP,
+ ACPI_TYPE_ANY, AML_CLASS_INTERNAL,
+ AML_TYPE_BOGUS,
+ AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED),
+/* 68 */ ACPI_OP("-AccessField-", ARGP_ACCESSFIELD_OP,
+ ARGI_ACCESSFIELD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
+/* 69 */ ACPI_OP("-StaticString", ARGP_STATICSTRING_OP,
+ ARGI_STATICSTRING_OP, ACPI_TYPE_ANY,
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
+/* 6A */ ACPI_OP("-Return Value-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY,
+ AML_CLASS_RETURN_VALUE, AML_TYPE_RETURN,
+ AML_HAS_ARGS | AML_HAS_RETVAL),
+/* 6B */ ACPI_OP("-UNKNOWN_OP-", ARG_NONE, ARG_NONE, ACPI_TYPE_INVALID,
+ AML_CLASS_UNKNOWN, AML_TYPE_BOGUS, AML_HAS_ARGS),
+/* 6C */ ACPI_OP("-ASCII_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY,
+ AML_CLASS_ASCII, AML_TYPE_BOGUS, AML_HAS_ARGS),
+/* 6D */ ACPI_OP("-PREFIX_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY,
+ AML_CLASS_PREFIX, AML_TYPE_BOGUS, AML_HAS_ARGS),
+
+/* ACPI 2.0 opcodes */
+
+/* 6E */ ACPI_OP("QwordConst", ARGP_QWORD_OP, ARGI_QWORD_OP,
+ ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT),
+ /* 6F */ ACPI_OP("Package", /* Var */ ARGP_VAR_PACKAGE_OP,
+ ARGI_VAR_PACKAGE_OP, ACPI_TYPE_PACKAGE,
+ AML_CLASS_CREATE, AML_TYPE_CREATE_OBJECT,
+ AML_HAS_ARGS | AML_DEFER),
+/* 70 */ ACPI_OP("ConcatenateResTemplate", ARGP_CONCAT_RES_OP,
+ ARGI_CONCAT_RES_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
+/* 71 */ ACPI_OP("Mod", ARGP_MOD_OP, ARGI_MOD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
+/* 72 */ ACPI_OP("CreateQWordField", ARGP_CREATE_QWORD_FIELD_OP,
+ ARGI_CREATE_QWORD_FIELD_OP,
+ ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_CREATE),
+/* 73 */ ACPI_OP("ToBuffer", ARGP_TO_BUFFER_OP, ARGI_TO_BUFFER_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+/* 74 */ ACPI_OP("ToDecimalString", ARGP_TO_DEC_STR_OP,
+ ARGI_TO_DEC_STR_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+/* 75 */ ACPI_OP("ToHexString", ARGP_TO_HEX_STR_OP, ARGI_TO_HEX_STR_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+/* 76 */ ACPI_OP("ToInteger", ARGP_TO_INTEGER_OP, ARGI_TO_INTEGER_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+/* 77 */ ACPI_OP("ToString", ARGP_TO_STRING_OP, ARGI_TO_STRING_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
+/* 78 */ ACPI_OP("CopyObject", ARGP_COPY_OP, ARGI_COPY_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R),
+/* 79 */ ACPI_OP("Mid", ARGP_MID_OP, ARGI_MID_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_1T_1R,
+ AML_FLAGS_EXEC_3A_1T_1R | AML_CONSTANT),
+/* 7A */ ACPI_OP("Continue", ARGP_CONTINUE_OP, ARGI_CONTINUE_OP,
+ ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
+/* 7B */ ACPI_OP("LoadTable", ARGP_LOAD_TABLE_OP, ARGI_LOAD_TABLE_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_6A_0T_1R, AML_FLAGS_EXEC_6A_0T_1R),
+/* 7C */ ACPI_OP("DataTableRegion", ARGP_DATA_REGION_OP,
+ ARGI_DATA_REGION_OP, ACPI_TYPE_REGION,
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED | AML_DEFER),
+/* 7D */ ACPI_OP("[EvalSubTree]", ARGP_SCOPE_OP, ARGI_SCOPE_OP,
+ ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_NO_OBJ,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE),
+
+/* ACPI 3.0 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_NO_OPERAND_RESOLVE),
+
+/* ACPI 5.0 opcodes */
+
+/* 7F */ ACPI_OP("-ConnectField-", ARGP_CONNECTFIELD_OP,
+ ARGI_CONNECTFIELD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS),
+/* 80 */ ACPI_OP("-ExtAccessField-", ARGP_CONNECTFIELD_OP,
+ ARGI_CONNECTFIELD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
+
+/* ACPI 6.0 opcodes */
+
+/* 81 */ ACPI_OP("External", ARGP_EXTERNAL_OP, ARGI_EXTERNAL_OP,
+ ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_SIMPLE,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
+/* 82 */ ACPI_OP("Comment", ARGP_COMMENT_OP, ARGI_COMMENT_OP,
+ ACPI_TYPE_STRING, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT)
+
+/*! [End] no source code translation !*/
+};
diff --git a/drivers/acpi/acpica/psopinfo.c b/drivers/acpi/acpica/psopinfo.c
new file mode 100644
index 000000000..f10afe699
--- /dev/null
+++ b/drivers/acpi/acpica/psopinfo.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: psopinfo - AML opcode information functions and dispatch tables
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "acopcode.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("psopinfo")
+
+static const u8 acpi_gbl_argument_count[] =
+ { 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 6 };
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_opcode_info
+ *
+ * PARAMETERS: opcode - The AML opcode
+ *
+ * RETURN: A pointer to the info about the opcode.
+ *
+ * DESCRIPTION: Find AML opcode description based on the opcode.
+ * NOTE: This procedure must ALWAYS return a valid pointer!
+ *
+ ******************************************************************************/
+
+const struct acpi_opcode_info *acpi_ps_get_opcode_info(u16 opcode)
+{
+#ifdef ACPI_DEBUG_OUTPUT
+ const char *opcode_name = "Unknown AML opcode";
+#endif
+
+ ACPI_FUNCTION_NAME(ps_get_opcode_info);
+
+ /*
+ * Detect normal 8-bit opcode or extended 16-bit opcode
+ */
+ if (!(opcode & 0xFF00)) {
+
+ /* Simple (8-bit) opcode: 0-255, can't index beyond table */
+
+ return (&acpi_gbl_aml_op_info
+ [acpi_gbl_short_op_index[(u8)opcode]]);
+ }
+
+ if (((opcode & 0xFF00) == AML_EXTENDED_OPCODE) &&
+ (((u8)opcode) <= MAX_EXTENDED_OPCODE)) {
+
+ /* Valid extended (16-bit) opcode */
+
+ return (&acpi_gbl_aml_op_info
+ [acpi_gbl_long_op_index[(u8)opcode]]);
+ }
+#if defined ACPI_ASL_COMPILER && defined ACPI_DEBUG_OUTPUT
+#include "asldefine.h"
+
+ switch (opcode) {
+ case AML_RAW_DATA_BYTE:
+ opcode_name = "-Raw Data Byte-";
+ break;
+
+ case AML_RAW_DATA_WORD:
+ opcode_name = "-Raw Data Word-";
+ break;
+
+ case AML_RAW_DATA_DWORD:
+ opcode_name = "-Raw Data Dword-";
+ break;
+
+ case AML_RAW_DATA_QWORD:
+ opcode_name = "-Raw Data Qword-";
+ break;
+
+ case AML_RAW_DATA_BUFFER:
+ opcode_name = "-Raw Data Buffer-";
+ break;
+
+ case AML_RAW_DATA_CHAIN:
+ opcode_name = "-Raw Data Buffer Chain-";
+ break;
+
+ case AML_PACKAGE_LENGTH:
+ opcode_name = "-Package Length-";
+ break;
+
+ case AML_UNASSIGNED_OPCODE:
+ opcode_name = "-Unassigned Opcode-";
+ break;
+
+ case AML_DEFAULT_ARG_OP:
+ opcode_name = "-Default Arg-";
+ break;
+
+ default:
+ break;
+ }
+#endif
+
+ /* Unknown AML opcode */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%s [%4.4X]\n", opcode_name, opcode));
+
+ return (&acpi_gbl_aml_op_info[_UNK]);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_opcode_name
+ *
+ * PARAMETERS: opcode - The AML opcode
+ *
+ * RETURN: A pointer to the name of the opcode (ASCII String)
+ * Note: Never returns NULL.
+ *
+ * DESCRIPTION: Translate an opcode into a human-readable string
+ *
+ ******************************************************************************/
+
+const char *acpi_ps_get_opcode_name(u16 opcode)
+{
+#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUG_OUTPUT)
+
+ const struct acpi_opcode_info *op;
+
+ op = acpi_ps_get_opcode_info(opcode);
+
+ /* Always guaranteed to return a valid pointer */
+
+ return (op->name);
+
+#else
+ return ("OpcodeName unavailable");
+
+#endif
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_argument_count
+ *
+ * PARAMETERS: op_type - Type associated with the AML opcode
+ *
+ * RETURN: Argument count
+ *
+ * DESCRIPTION: Obtain the number of expected arguments for an AML opcode
+ *
+ ******************************************************************************/
+
+u8 acpi_ps_get_argument_count(u32 op_type)
+{
+
+ if (op_type <= AML_TYPE_EXEC_6A_0T_1R) {
+ return (acpi_gbl_argument_count[op_type]);
+ }
+
+ return (0);
+}
+
+/*
+ * This table is directly indexed by the opcodes It returns
+ * an index into the opcode table (acpi_gbl_aml_op_info)
+ */
+const u8 acpi_gbl_short_op_index[256] = {
+/* 0 1 2 3 4 5 6 7 */
+/* 8 9 A B C D E F */
+/* 0x00 */ 0x00, 0x01, _UNK, _UNK, _UNK, _UNK, 0x02, _UNK,
+/* 0x08 */ 0x03, _UNK, 0x04, 0x05, 0x06, 0x07, 0x6E, _UNK,
+/* 0x10 */ 0x08, 0x09, 0x0a, 0x6F, 0x0b, 0x81, _UNK, _UNK,
+/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x20 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x28 */ _UNK, _UNK, _UNK, _UNK, _UNK, 0x63, _PFX, _PFX,
+/* 0x30 */ 0x67, 0x66, 0x68, 0x65, 0x69, 0x64, 0x6A, 0x7D,
+/* 0x38 */ 0x7F, 0x80, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x40 */ _UNK, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC,
+/* 0x48 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC,
+/* 0x50 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC,
+/* 0x58 */ _ASC, _ASC, _ASC, _UNK, _PFX, _UNK, _PFX, _ASC,
+/* 0x60 */ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
+/* 0x68 */ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, _UNK,
+/* 0x70 */ 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+/* 0x78 */ 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+/* 0x80 */ 0x2b, 0x2c, 0x2d, 0x2e, 0x70, 0x71, 0x2f, 0x30,
+/* 0x88 */ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x72,
+/* 0x90 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x73, 0x74,
+/* 0x98 */ 0x75, 0x76, _UNK, _UNK, 0x77, 0x78, 0x79, 0x7A,
+/* 0xA0 */ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x60, 0x61,
+/* 0xA8 */ 0x62, 0x82, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xB0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xB8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xC0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xC8 */ _UNK, _UNK, _UNK, _UNK, 0x44, _UNK, _UNK, _UNK,
+/* 0xD0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xD8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xE0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xE8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xF0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xF8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x45,
+};
+
+/*
+ * This table is indexed by the second opcode of the extended opcode
+ * pair. It returns an index into the opcode table (acpi_gbl_aml_op_info)
+ */
+const u8 acpi_gbl_long_op_index[NUM_EXTENDED_OPCODE] = {
+/* 0 1 2 3 4 5 6 7 */
+/* 8 9 A B C D E F */
+/* 0x00 */ _UNK, 0x46, 0x47, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x08 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x10 */ _UNK, _UNK, 0x48, 0x49, _UNK, _UNK, _UNK, _UNK,
+/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x7B,
+/* 0x20 */ 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51,
+/* 0x28 */ 0x52, 0x53, 0x54, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x30 */ 0x55, 0x56, 0x57, 0x7e, _UNK, _UNK, _UNK, _UNK,
+/* 0x38 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x40 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x48 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x50 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x58 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x60 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x68 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x70 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x78 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x80 */ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+/* 0x88 */ 0x7C,
+};
diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c
new file mode 100644
index 000000000..ba93f3597
--- /dev/null
+++ b/drivers/acpi/acpica/psparse.c
@@ -0,0 +1,687 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: psparse - Parser top level AML parse routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+/*
+ * Parse the AML and build an operation tree as most interpreters,
+ * like Perl, do. Parsing is done by hand rather than with a YACC
+ * generated parser to tightly constrain stack and dynamic memory
+ * usage. At the same time, parsing is kept flexible and the code
+ * fairly compact by parsing based on a list of AML opcode
+ * templates in aml_op_info[]
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "acdispat.h"
+#include "amlcode.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("psparse")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_opcode_size
+ *
+ * PARAMETERS: opcode - An AML opcode
+ *
+ * RETURN: Size of the opcode, in bytes (1 or 2)
+ *
+ * DESCRIPTION: Get the size of the current opcode.
+ *
+ ******************************************************************************/
+u32 acpi_ps_get_opcode_size(u32 opcode)
+{
+
+ /* Extended (2-byte) opcode if > 255 */
+
+ if (opcode > 0x00FF) {
+ return (2);
+ }
+
+ /* Otherwise, just a single byte opcode */
+
+ return (1);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_peek_opcode
+ *
+ * PARAMETERS: parser_state - A parser state object
+ *
+ * RETURN: Next AML opcode
+ *
+ * DESCRIPTION: Get next AML opcode (without incrementing AML pointer)
+ *
+ ******************************************************************************/
+
+u16 acpi_ps_peek_opcode(struct acpi_parse_state * parser_state)
+{
+ u8 *aml;
+ u16 opcode;
+
+ aml = parser_state->aml;
+ opcode = (u16) ACPI_GET8(aml);
+
+ if (opcode == AML_EXTENDED_PREFIX) {
+
+ /* Extended opcode, get the second opcode byte */
+
+ aml++;
+ opcode = (u16) ((opcode << 8) | ACPI_GET8(aml));
+ }
+
+ return (opcode);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_complete_this_op
+ *
+ * PARAMETERS: walk_state - Current State
+ * op - Op to complete
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform any cleanup at the completion of an Op.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ps_complete_this_op(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op)
+{
+ union acpi_parse_object *prev;
+ union acpi_parse_object *next;
+ const struct acpi_opcode_info *parent_info;
+ union acpi_parse_object *replacement_op = NULL;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_complete_this_op, op);
+
+ /* Check for null Op, can happen if AML code is corrupt */
+
+ if (!op) {
+ return_ACPI_STATUS(AE_OK); /* OK for now */
+ }
+
+ acpi_ex_stop_trace_opcode(op, walk_state);
+
+ /* Delete this op and the subtree below it if asked to */
+
+ if (((walk_state->parse_flags & ACPI_PARSE_TREE_MASK) !=
+ ACPI_PARSE_DELETE_TREE)
+ || (walk_state->op_info->class == AML_CLASS_ARGUMENT)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Make sure that we only delete this subtree */
+
+ if (op->common.parent) {
+ prev = op->common.parent->common.value.arg;
+ if (!prev) {
+
+ /* Nothing more to do */
+
+ goto cleanup;
+ }
+
+ /*
+ * Check if we need to replace the operator and its subtree
+ * with a return value op (placeholder op)
+ */
+ parent_info =
+ acpi_ps_get_opcode_info(op->common.parent->common.
+ aml_opcode);
+
+ switch (parent_info->class) {
+ case AML_CLASS_CONTROL:
+
+ break;
+
+ case AML_CLASS_CREATE:
+ /*
+ * These opcodes contain term_arg operands. The current
+ * op must be replaced by a placeholder return op
+ */
+ replacement_op =
+ acpi_ps_alloc_op(AML_INT_RETURN_VALUE_OP,
+ op->common.aml);
+ if (!replacement_op) {
+ status = AE_NO_MEMORY;
+ }
+ break;
+
+ case AML_CLASS_NAMED_OBJECT:
+ /*
+ * These opcodes contain term_arg operands. The current
+ * op must be replaced by a placeholder return op
+ */
+ if ((op->common.parent->common.aml_opcode ==
+ AML_REGION_OP)
+ || (op->common.parent->common.aml_opcode ==
+ AML_DATA_REGION_OP)
+ || (op->common.parent->common.aml_opcode ==
+ AML_BUFFER_OP)
+ || (op->common.parent->common.aml_opcode ==
+ AML_PACKAGE_OP)
+ || (op->common.parent->common.aml_opcode ==
+ AML_BANK_FIELD_OP)
+ || (op->common.parent->common.aml_opcode ==
+ AML_VARIABLE_PACKAGE_OP)) {
+ replacement_op =
+ acpi_ps_alloc_op(AML_INT_RETURN_VALUE_OP,
+ op->common.aml);
+ if (!replacement_op) {
+ status = AE_NO_MEMORY;
+ }
+ } else
+ if ((op->common.parent->common.aml_opcode ==
+ AML_NAME_OP)
+ && (walk_state->pass_number <=
+ ACPI_IMODE_LOAD_PASS2)) {
+ if ((op->common.aml_opcode == AML_BUFFER_OP)
+ || (op->common.aml_opcode == AML_PACKAGE_OP)
+ || (op->common.aml_opcode ==
+ AML_VARIABLE_PACKAGE_OP)) {
+ replacement_op =
+ acpi_ps_alloc_op(op->common.
+ aml_opcode,
+ op->common.aml);
+ if (!replacement_op) {
+ status = AE_NO_MEMORY;
+ } else {
+ replacement_op->named.data =
+ op->named.data;
+ replacement_op->named.length =
+ op->named.length;
+ }
+ }
+ }
+ break;
+
+ default:
+
+ replacement_op =
+ acpi_ps_alloc_op(AML_INT_RETURN_VALUE_OP,
+ op->common.aml);
+ if (!replacement_op) {
+ status = AE_NO_MEMORY;
+ }
+ }
+
+ /* We must unlink this op from the parent tree */
+
+ if (prev == op) {
+
+ /* This op is the first in the list */
+
+ if (replacement_op) {
+ replacement_op->common.parent =
+ op->common.parent;
+ replacement_op->common.value.arg = NULL;
+ replacement_op->common.node = op->common.node;
+ op->common.parent->common.value.arg =
+ replacement_op;
+ replacement_op->common.next = op->common.next;
+ } else {
+ op->common.parent->common.value.arg =
+ op->common.next;
+ }
+ }
+
+ /* Search the parent list */
+
+ else
+ while (prev) {
+
+ /* Traverse all siblings in the parent's argument list */
+
+ next = prev->common.next;
+ if (next == op) {
+ if (replacement_op) {
+ replacement_op->common.parent =
+ op->common.parent;
+ replacement_op->common.value.
+ arg = NULL;
+ replacement_op->common.node =
+ op->common.node;
+ prev->common.next =
+ replacement_op;
+ replacement_op->common.next =
+ op->common.next;
+ next = NULL;
+ } else {
+ prev->common.next =
+ op->common.next;
+ next = NULL;
+ }
+ }
+ prev = next;
+ }
+ }
+
+cleanup:
+
+ /* Now we can actually delete the subtree rooted at Op */
+
+ acpi_ps_delete_parse_tree(op);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_next_parse_state
+ *
+ * PARAMETERS: walk_state - Current state
+ * op - Current parse op
+ * callback_status - Status from previous operation
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Update the parser state based upon the return exception from
+ * the parser callback.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ps_next_parse_state(struct acpi_walk_state *walk_state,
+ union acpi_parse_object *op,
+ acpi_status callback_status)
+{
+ struct acpi_parse_state *parser_state = &walk_state->parser_state;
+ acpi_status status = AE_CTRL_PENDING;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_next_parse_state, op);
+
+ switch (callback_status) {
+ case AE_CTRL_TERMINATE:
+ /*
+ * A control method was terminated via a RETURN statement.
+ * The walk of this method is complete.
+ */
+ parser_state->aml = parser_state->aml_end;
+ status = AE_CTRL_TERMINATE;
+ break;
+
+ case AE_CTRL_BREAK:
+
+ parser_state->aml = walk_state->aml_last_while;
+ walk_state->control_state->common.value = FALSE;
+ status = AE_CTRL_BREAK;
+ break;
+
+ case AE_CTRL_CONTINUE:
+
+ parser_state->aml = walk_state->aml_last_while;
+ status = AE_CTRL_CONTINUE;
+ break;
+
+ case AE_CTRL_PENDING:
+
+ parser_state->aml = walk_state->aml_last_while;
+ break;
+
+#if 0
+ case AE_CTRL_SKIP:
+
+ parser_state->aml = parser_state->scope->parse_scope.pkg_end;
+ status = AE_OK;
+ break;
+#endif
+
+ case AE_CTRL_TRUE:
+ /*
+ * Predicate of an IF was true, and we are at the matching ELSE.
+ * Just close out this package
+ */
+ parser_state->aml = acpi_ps_get_next_package_end(parser_state);
+ status = AE_CTRL_PENDING;
+ break;
+
+ case AE_CTRL_FALSE:
+ /*
+ * Either an IF/WHILE Predicate was false or we encountered a BREAK
+ * opcode. In both cases, we do not execute the rest of the
+ * package; We simply close out the parent (finishing the walk of
+ * this branch of the tree) and continue execution at the parent
+ * level.
+ */
+ parser_state->aml = parser_state->scope->parse_scope.pkg_end;
+
+ /* In the case of a BREAK, just force a predicate (if any) to FALSE */
+
+ walk_state->control_state->common.value = FALSE;
+ status = AE_CTRL_END;
+ break;
+
+ case AE_CTRL_TRANSFER:
+
+ /* A method call (invocation) -- transfer control */
+
+ status = AE_CTRL_TRANSFER;
+ walk_state->prev_op = op;
+ walk_state->method_call_op = op;
+ walk_state->method_call_node =
+ (op->common.value.arg)->common.node;
+
+ /* Will return value (if any) be used by the caller? */
+
+ walk_state->return_used =
+ acpi_ds_is_result_used(op, walk_state);
+ break;
+
+ default:
+
+ status = callback_status;
+ if (ACPI_CNTL_EXCEPTION(callback_status)) {
+ status = AE_OK;
+ }
+ break;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_parse_aml
+ *
+ * PARAMETERS: walk_state - Current state
+ *
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Parse raw AML and return a tree of ops
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ struct acpi_thread_state *thread;
+ struct acpi_thread_state *prev_walk_list = acpi_gbl_current_walk_list;
+ struct acpi_walk_state *previous_walk_state;
+
+ ACPI_FUNCTION_TRACE(ps_parse_aml);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Entered with WalkState=%p Aml=%p size=%X\n",
+ walk_state, walk_state->parser_state.aml,
+ walk_state->parser_state.aml_size));
+
+ if (!walk_state->parser_state.aml) {
+ return_ACPI_STATUS(AE_BAD_ADDRESS);
+ }
+
+ /* Create and initialize a new thread state */
+
+ thread = acpi_ut_create_thread_state();
+ if (!thread) {
+ if (walk_state->method_desc) {
+
+ /* Executing a control method - additional cleanup */
+
+ acpi_ds_terminate_control_method(walk_state->
+ method_desc,
+ walk_state);
+ }
+
+ acpi_ds_delete_walk_state(walk_state);
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ walk_state->thread = thread;
+
+ /*
+ * If executing a method, the starting sync_level is this method's
+ * sync_level
+ */
+ if (walk_state->method_desc) {
+ walk_state->thread->current_sync_level =
+ walk_state->method_desc->method.sync_level;
+ }
+
+ acpi_ds_push_walk_state(walk_state, thread);
+
+ /*
+ * This global allows the AML debugger to get a handle to the currently
+ * executing control method.
+ */
+ acpi_gbl_current_walk_list = thread;
+
+ /*
+ * Execute the walk loop as long as there is a valid Walk State. This
+ * handles nested control method invocations without recursion.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "State=%p\n", walk_state));
+
+ status = AE_OK;
+ while (walk_state) {
+ if (ACPI_SUCCESS(status)) {
+ /*
+ * The parse_loop executes AML until the method terminates
+ * or calls another method.
+ */
+ status = acpi_ps_parse_loop(walk_state);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Completed one call to walk loop, %s State=%p\n",
+ acpi_format_exception(status), walk_state));
+
+ if (walk_state->method_pathname && walk_state->method_is_nested) {
+
+ /* Optional object evaluation log */
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EVALUATION,
+ "%-26s: %*s%s\n",
+ " Exit nested method",
+ (walk_state->
+ method_nesting_depth + 1) * 3,
+ " ",
+ &walk_state->method_pathname[1]));
+
+ ACPI_FREE(walk_state->method_pathname);
+ walk_state->method_is_nested = FALSE;
+ }
+ if (status == AE_CTRL_TRANSFER) {
+ /*
+ * A method call was detected.
+ * Transfer control to the called control method
+ */
+ status =
+ acpi_ds_call_control_method(thread, walk_state,
+ NULL);
+ if (ACPI_FAILURE(status)) {
+ status =
+ acpi_ds_method_error(status, walk_state);
+ }
+
+ /*
+ * If the transfer to the new method method call worked,
+ * a new walk state was created -- get it
+ */
+ walk_state = acpi_ds_get_current_walk_state(thread);
+ continue;
+ } else if (status == AE_CTRL_TERMINATE) {
+ status = AE_OK;
+ } else if ((status != AE_OK) && (walk_state->method_desc)) {
+
+ /* Either the method parse or actual execution failed */
+
+ acpi_ex_exit_interpreter();
+ if (status == AE_ABORT_METHOD) {
+ acpi_ns_print_node_pathname(walk_state->
+ method_node,
+ "Aborting method");
+ acpi_os_printf("\n");
+ } else {
+ ACPI_ERROR_METHOD("Aborting method",
+ walk_state->method_node, NULL,
+ status);
+ }
+ acpi_ex_enter_interpreter();
+
+ /* Check for possible multi-thread reentrancy problem */
+
+ if ((status == AE_ALREADY_EXISTS) &&
+ (!(walk_state->method_desc->method.info_flags &
+ ACPI_METHOD_SERIALIZED))) {
+ /*
+ * Method is not serialized and tried to create an object
+ * twice. The probable cause is that the method cannot
+ * handle reentrancy. Mark as "pending serialized" now, and
+ * then mark "serialized" when the last thread exits.
+ */
+ walk_state->method_desc->method.info_flags |=
+ ACPI_METHOD_SERIALIZED_PENDING;
+ }
+ }
+
+ /* We are done with this walk, move on to the parent if any */
+
+ walk_state = acpi_ds_pop_walk_state(thread);
+
+ /* Reset the current scope to the beginning of scope stack */
+
+ acpi_ds_scope_stack_clear(walk_state);
+
+ /*
+ * If we just returned from the execution of a control method or if we
+ * encountered an error during the method parse phase, there's lots of
+ * cleanup to do
+ */
+ if (((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) ==
+ ACPI_PARSE_EXECUTE &&
+ !(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) ||
+ (ACPI_FAILURE(status))) {
+ acpi_ds_terminate_control_method(walk_state->
+ method_desc,
+ walk_state);
+ }
+
+ /* Delete this walk state and all linked control states */
+
+ acpi_ps_cleanup_scope(&walk_state->parser_state);
+ previous_walk_state = walk_state;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "ReturnValue=%p, ImplicitValue=%p State=%p\n",
+ walk_state->return_desc,
+ walk_state->implicit_return_obj, walk_state));
+
+ /* Check if we have restarted a preempted walk */
+
+ walk_state = acpi_ds_get_current_walk_state(thread);
+ if (walk_state) {
+ if (ACPI_SUCCESS(status)) {
+ /*
+ * There is another walk state, restart it.
+ * If the method return value is not used by the parent,
+ * The object is deleted
+ */
+ if (!previous_walk_state->return_desc) {
+ /*
+ * In slack mode execution, if there is no return value
+ * we should implicitly return zero (0) as a default value.
+ */
+ if (acpi_gbl_enable_interpreter_slack &&
+ !previous_walk_state->
+ implicit_return_obj) {
+ previous_walk_state->
+ implicit_return_obj =
+ acpi_ut_create_integer_object
+ ((u64) 0);
+ if (!previous_walk_state->
+ implicit_return_obj) {
+ return_ACPI_STATUS
+ (AE_NO_MEMORY);
+ }
+ }
+
+ /* Restart the calling control method */
+
+ status =
+ acpi_ds_restart_control_method
+ (walk_state,
+ previous_walk_state->
+ implicit_return_obj);
+ } else {
+ /*
+ * We have a valid return value, delete any implicit
+ * return value.
+ */
+ acpi_ds_clear_implicit_return
+ (previous_walk_state);
+
+ status =
+ acpi_ds_restart_control_method
+ (walk_state,
+ previous_walk_state->return_desc);
+ }
+ if (ACPI_SUCCESS(status)) {
+ walk_state->walk_type |=
+ ACPI_WALK_METHOD_RESTART;
+ }
+ } else {
+ /* On error, delete any return object or implicit return */
+
+ acpi_ut_remove_reference(previous_walk_state->
+ return_desc);
+ acpi_ds_clear_implicit_return
+ (previous_walk_state);
+ }
+ }
+
+ /*
+ * Just completed a 1st-level method, save the final internal return
+ * value (if any)
+ */
+ else if (previous_walk_state->caller_return_desc) {
+ if (previous_walk_state->implicit_return_obj) {
+ *(previous_walk_state->caller_return_desc) =
+ previous_walk_state->implicit_return_obj;
+ } else {
+ /* NULL if no return value */
+
+ *(previous_walk_state->caller_return_desc) =
+ previous_walk_state->return_desc;
+ }
+ } else {
+ if (previous_walk_state->return_desc) {
+
+ /* Caller doesn't want it, must delete it */
+
+ acpi_ut_remove_reference(previous_walk_state->
+ return_desc);
+ }
+ if (previous_walk_state->implicit_return_obj) {
+
+ /* Caller doesn't want it, must delete it */
+
+ acpi_ut_remove_reference(previous_walk_state->
+ implicit_return_obj);
+ }
+ }
+
+ acpi_ds_delete_walk_state(previous_walk_state);
+ }
+
+ /* Normal exit */
+
+ acpi_ex_release_all_mutexes(thread);
+ acpi_ut_delete_generic_state(ACPI_CAST_PTR
+ (union acpi_generic_state, thread));
+ acpi_gbl_current_walk_list = prev_walk_list;
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/psscope.c b/drivers/acpi/acpica/psscope.c
new file mode 100644
index 000000000..400f00163
--- /dev/null
+++ b/drivers/acpi/acpica/psscope.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: psscope - Parser scope stack management routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("psscope")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_parent_scope
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ *
+ * RETURN: Pointer to an Op object
+ *
+ * DESCRIPTION: Get parent of current op being parsed
+ *
+ ******************************************************************************/
+union acpi_parse_object *acpi_ps_get_parent_scope(struct acpi_parse_state
+ *parser_state)
+{
+
+ return (parser_state->scope->parse_scope.op);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_has_completed_scope
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ *
+ * RETURN: Boolean, TRUE = scope completed.
+ *
+ * DESCRIPTION: Is parsing of current argument complete? Determined by
+ * 1) AML pointer is at or beyond the end of the scope
+ * 2) The scope argument count has reached zero.
+ *
+ ******************************************************************************/
+
+u8 acpi_ps_has_completed_scope(struct acpi_parse_state * parser_state)
+{
+
+ return ((u8)
+ ((parser_state->aml >= parser_state->scope->parse_scope.arg_end
+ || !parser_state->scope->parse_scope.arg_count)));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_init_scope
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ * root - the Root Node of this new scope
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Allocate and init a new scope object
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ps_init_scope(struct acpi_parse_state * parser_state,
+ union acpi_parse_object * root_op)
+{
+ union acpi_generic_state *scope;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_init_scope, root_op);
+
+ scope = acpi_ut_create_generic_state();
+ if (!scope) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ scope->common.descriptor_type = ACPI_DESC_TYPE_STATE_RPSCOPE;
+ scope->parse_scope.op = root_op;
+ scope->parse_scope.arg_count = ACPI_VAR_ARGS;
+ scope->parse_scope.arg_end = parser_state->aml_end;
+ scope->parse_scope.pkg_end = parser_state->aml_end;
+
+ parser_state->scope = scope;
+ parser_state->start_op = root_op;
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_push_scope
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ * op - Current op to be pushed
+ * remaining_args - List of args remaining
+ * arg_count - Fixed or variable number of args
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Push current op to begin parsing its argument
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ps_push_scope(struct acpi_parse_state *parser_state,
+ union acpi_parse_object *op,
+ u32 remaining_args, u32 arg_count)
+{
+ union acpi_generic_state *scope;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_push_scope, op);
+
+ scope = acpi_ut_create_generic_state();
+ if (!scope) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ scope->common.descriptor_type = ACPI_DESC_TYPE_STATE_PSCOPE;
+ scope->parse_scope.op = op;
+ scope->parse_scope.arg_list = remaining_args;
+ scope->parse_scope.arg_count = arg_count;
+ scope->parse_scope.pkg_end = parser_state->pkg_end;
+
+ /* Push onto scope stack */
+
+ acpi_ut_push_generic_state(&parser_state->scope, scope);
+
+ if (arg_count == ACPI_VAR_ARGS) {
+
+ /* Multiple arguments */
+
+ scope->parse_scope.arg_end = parser_state->pkg_end;
+ } else {
+ /* Single argument */
+
+ scope->parse_scope.arg_end = ACPI_TO_POINTER(ACPI_MAX_PTR);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_pop_scope
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ * op - Where the popped op is returned
+ * arg_list - Where the popped "next argument" is
+ * returned
+ * arg_count - Count of objects in arg_list
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Return to parsing a previous op
+ *
+ ******************************************************************************/
+
+void
+acpi_ps_pop_scope(struct acpi_parse_state *parser_state,
+ union acpi_parse_object **op, u32 * arg_list, u32 * arg_count)
+{
+ union acpi_generic_state *scope = parser_state->scope;
+
+ ACPI_FUNCTION_TRACE(ps_pop_scope);
+
+ /* Only pop the scope if there is in fact a next scope */
+
+ if (scope->common.next) {
+ scope = acpi_ut_pop_generic_state(&parser_state->scope);
+
+ /* Return to parsing previous op */
+
+ *op = scope->parse_scope.op;
+ *arg_list = scope->parse_scope.arg_list;
+ *arg_count = scope->parse_scope.arg_count;
+ parser_state->pkg_end = scope->parse_scope.pkg_end;
+
+ /* All done with this scope state structure */
+
+ acpi_ut_delete_generic_state(scope);
+ } else {
+ /* Empty parse stack, prepare to fetch next opcode */
+
+ *op = NULL;
+ *arg_list = 0;
+ *arg_count = 0;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Popped Op %p Args %X\n", *op, *arg_count));
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_cleanup_scope
+ *
+ * PARAMETERS: parser_state - Current parser state object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Destroy available list, remaining stack levels, and return
+ * root scope
+ *
+ ******************************************************************************/
+
+void acpi_ps_cleanup_scope(struct acpi_parse_state *parser_state)
+{
+ union acpi_generic_state *scope;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_cleanup_scope, parser_state);
+
+ if (!parser_state) {
+ return_VOID;
+ }
+
+ /* Delete anything on the scope stack */
+
+ while (parser_state->scope) {
+ scope = acpi_ut_pop_generic_state(&parser_state->scope);
+ acpi_ut_delete_generic_state(scope);
+ }
+
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c
new file mode 100644
index 000000000..3012a9342
--- /dev/null
+++ b/drivers/acpi/acpica/pstree.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: pstree - Parser op tree manipulation/traversal/search
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acconvert.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("pstree")
+
+/* Local prototypes */
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+union acpi_parse_object *acpi_ps_get_child(union acpi_parse_object *op);
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_arg
+ *
+ * PARAMETERS: op - Get an argument for this op
+ * argn - Nth argument to get
+ *
+ * RETURN: The argument (as an Op object). NULL if argument does not exist
+ *
+ * DESCRIPTION: Get the specified op's argument.
+ *
+ ******************************************************************************/
+
+union acpi_parse_object *acpi_ps_get_arg(union acpi_parse_object *op, u32 argn)
+{
+ union acpi_parse_object *arg = NULL;
+ const struct acpi_opcode_info *op_info;
+
+ ACPI_FUNCTION_ENTRY();
+
+/*
+ if (Op->Common.aml_opcode == AML_INT_CONNECTION_OP)
+ {
+ return (Op->Common.Value.Arg);
+ }
+*/
+ /* Get the info structure for this opcode */
+
+ op_info = acpi_ps_get_opcode_info(op->common.aml_opcode);
+ if (op_info->class == AML_CLASS_UNKNOWN) {
+
+ /* Invalid opcode or ASCII character */
+
+ return (NULL);
+ }
+
+ /* Check if this opcode requires argument sub-objects */
+
+ if (!(op_info->flags & AML_HAS_ARGS)) {
+
+ /* Has no linked argument objects */
+
+ return (NULL);
+ }
+
+ /* Get the requested argument object */
+
+ arg = op->common.value.arg;
+ while (arg && argn) {
+ argn--;
+ arg = arg->common.next;
+ }
+
+ return (arg);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_append_arg
+ *
+ * PARAMETERS: op - Append an argument to this Op.
+ * arg - Argument Op to append
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Append an argument to an op's argument list (a NULL arg is OK)
+ *
+ ******************************************************************************/
+
+void
+acpi_ps_append_arg(union acpi_parse_object *op, union acpi_parse_object *arg)
+{
+ union acpi_parse_object *prev_arg;
+ const struct acpi_opcode_info *op_info;
+
+ ACPI_FUNCTION_TRACE(ps_append_arg);
+
+ if (!op) {
+ return_VOID;
+ }
+
+ /* Get the info structure for this opcode */
+
+ op_info = acpi_ps_get_opcode_info(op->common.aml_opcode);
+ if (op_info->class == AML_CLASS_UNKNOWN) {
+
+ /* Invalid opcode */
+
+ ACPI_ERROR((AE_INFO, "Invalid AML Opcode: 0x%2.2X",
+ op->common.aml_opcode));
+ return_VOID;
+ }
+
+ /* Check if this opcode requires argument sub-objects */
+
+ if (!(op_info->flags & AML_HAS_ARGS)) {
+
+ /* Has no linked argument objects */
+
+ return_VOID;
+ }
+
+ /* Append the argument to the linked argument list */
+
+ if (op->common.value.arg) {
+
+ /* Append to existing argument list */
+
+ prev_arg = op->common.value.arg;
+ while (prev_arg->common.next) {
+ prev_arg = prev_arg->common.next;
+ }
+ prev_arg->common.next = arg;
+ } else {
+ /* No argument list, this will be the first argument */
+
+ op->common.value.arg = arg;
+ }
+
+ /* Set the parent in this arg and any args linked after it */
+
+ while (arg) {
+ arg->common.parent = op;
+ arg = arg->common.next;
+
+ op->common.arg_list_length++;
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_depth_next
+ *
+ * PARAMETERS: origin - Root of subtree to search
+ * op - Last (previous) Op that was found
+ *
+ * RETURN: Next Op found in the search.
+ *
+ * DESCRIPTION: Get next op in tree (walking the tree in depth-first order)
+ * Return NULL when reaching "origin" or when walking up from root
+ *
+ ******************************************************************************/
+
+union acpi_parse_object *acpi_ps_get_depth_next(union acpi_parse_object *origin,
+ union acpi_parse_object *op)
+{
+ union acpi_parse_object *next = NULL;
+ union acpi_parse_object *parent;
+ union acpi_parse_object *arg;
+
+ ACPI_FUNCTION_ENTRY();
+
+ if (!op) {
+ return (NULL);
+ }
+
+ /* Look for an argument or child */
+
+ next = acpi_ps_get_arg(op, 0);
+ if (next) {
+ ASL_CV_LABEL_FILENODE(next);
+ return (next);
+ }
+
+ /* Look for a sibling */
+
+ next = op->common.next;
+ if (next) {
+ ASL_CV_LABEL_FILENODE(next);
+ return (next);
+ }
+
+ /* Look for a sibling of parent */
+
+ parent = op->common.parent;
+
+ while (parent) {
+ arg = acpi_ps_get_arg(parent, 0);
+ while (arg && (arg != origin) && (arg != op)) {
+
+ ASL_CV_LABEL_FILENODE(arg);
+ arg = arg->common.next;
+ }
+
+ if (arg == origin) {
+
+ /* Reached parent of origin, end search */
+
+ return (NULL);
+ }
+
+ if (parent->common.next) {
+
+ /* Found sibling of parent */
+
+ ASL_CV_LABEL_FILENODE(parent->common.next);
+ return (parent->common.next);
+ }
+
+ op = parent;
+ parent = parent->common.parent;
+ }
+
+ ASL_CV_LABEL_FILENODE(next);
+ return (next);
+}
+
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_get_child
+ *
+ * PARAMETERS: op - Get the child of this Op
+ *
+ * RETURN: Child Op, Null if none is found.
+ *
+ * DESCRIPTION: Get op's children or NULL if none
+ *
+ ******************************************************************************/
+
+union acpi_parse_object *acpi_ps_get_child(union acpi_parse_object *op)
+{
+ union acpi_parse_object *child = NULL;
+
+ ACPI_FUNCTION_ENTRY();
+
+ switch (op->common.aml_opcode) {
+ case AML_SCOPE_OP:
+ case AML_ELSE_OP:
+ case AML_DEVICE_OP:
+ case AML_THERMAL_ZONE_OP:
+ case AML_INT_METHODCALL_OP:
+
+ child = acpi_ps_get_arg(op, 0);
+ break;
+
+ case AML_BUFFER_OP:
+ case AML_PACKAGE_OP:
+ case AML_VARIABLE_PACKAGE_OP:
+ case AML_METHOD_OP:
+ case AML_IF_OP:
+ case AML_WHILE_OP:
+ case AML_FIELD_OP:
+
+ child = acpi_ps_get_arg(op, 1);
+ break;
+
+ case AML_POWER_RESOURCE_OP:
+ case AML_INDEX_FIELD_OP:
+
+ child = acpi_ps_get_arg(op, 2);
+ break;
+
+ case AML_PROCESSOR_OP:
+ case AML_BANK_FIELD_OP:
+
+ child = acpi_ps_get_arg(op, 3);
+ break;
+
+ default:
+
+ /* All others have no children */
+
+ break;
+ }
+
+ return (child);
+}
+#endif
diff --git a/drivers/acpi/acpica/psutils.c b/drivers/acpi/acpica/psutils.c
new file mode 100644
index 000000000..49b39aede
--- /dev/null
+++ b/drivers/acpi/acpica/psutils.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: psutils - Parser miscellaneous utilities (Parser only)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acconvert.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("psutils")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_create_scope_op
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: A new Scope object, null on failure
+ *
+ * DESCRIPTION: Create a Scope and associated namepath op with the root name
+ *
+ ******************************************************************************/
+union acpi_parse_object *acpi_ps_create_scope_op(u8 *aml)
+{
+ union acpi_parse_object *scope_op;
+
+ scope_op = acpi_ps_alloc_op(AML_SCOPE_OP, aml);
+ if (!scope_op) {
+ return (NULL);
+ }
+
+ scope_op->named.name = ACPI_ROOT_NAME;
+ return (scope_op);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_init_op
+ *
+ * PARAMETERS: op - A newly allocated Op object
+ * opcode - Opcode to store in the Op
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Initialize a parse (Op) object
+ *
+ ******************************************************************************/
+
+void acpi_ps_init_op(union acpi_parse_object *op, u16 opcode)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ op->common.descriptor_type = ACPI_DESC_TYPE_PARSER;
+ op->common.aml_opcode = opcode;
+
+ ACPI_DISASM_ONLY_MEMBERS(acpi_ut_safe_strncpy(op->common.aml_op_name,
+ (acpi_ps_get_opcode_info
+ (opcode))->name,
+ sizeof(op->common.
+ aml_op_name)));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_alloc_op
+ *
+ * PARAMETERS: opcode - Opcode that will be stored in the new Op
+ * aml - Address of the opcode
+ *
+ * RETURN: Pointer to the new Op, null on failure
+ *
+ * DESCRIPTION: Allocate an acpi_op, choose op type (and thus size) based on
+ * opcode. A cache of opcodes is available for the pure
+ * GENERIC_OP, since this is by far the most commonly used.
+ *
+ ******************************************************************************/
+
+union acpi_parse_object *acpi_ps_alloc_op(u16 opcode, u8 *aml)
+{
+ union acpi_parse_object *op;
+ const struct acpi_opcode_info *op_info;
+ u8 flags = ACPI_PARSEOP_GENERIC;
+
+ ACPI_FUNCTION_ENTRY();
+
+ op_info = acpi_ps_get_opcode_info(opcode);
+
+ /* Determine type of parse_op required */
+
+ if (op_info->flags & AML_DEFER) {
+ flags = ACPI_PARSEOP_DEFERRED;
+ } else if (op_info->flags & AML_NAMED) {
+ flags = ACPI_PARSEOP_NAMED_OBJECT;
+ } else if (opcode == AML_INT_BYTELIST_OP) {
+ flags = ACPI_PARSEOP_BYTELIST;
+ }
+
+ /* Allocate the minimum required size object */
+
+ if (flags == ACPI_PARSEOP_GENERIC) {
+
+ /* The generic op (default) is by far the most common (16 to 1) */
+
+ op = acpi_os_acquire_object(acpi_gbl_ps_node_cache);
+ } else {
+ /* Extended parseop */
+
+ op = acpi_os_acquire_object(acpi_gbl_ps_node_ext_cache);
+ }
+
+ /* Initialize the Op */
+
+ if (op) {
+ acpi_ps_init_op(op, opcode);
+ op->common.aml = aml;
+ op->common.flags = flags;
+ ASL_CV_CLEAR_OP_COMMENTS(op);
+
+ if (opcode == AML_SCOPE_OP) {
+ acpi_gbl_current_scope = op;
+ }
+
+ if (acpi_gbl_capture_comments) {
+ ASL_CV_TRANSFER_COMMENTS(op);
+ }
+ }
+
+ return (op);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_free_op
+ *
+ * PARAMETERS: op - Op to be freed
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Free an Op object. Either put it on the GENERIC_OP cache list
+ * or actually free it.
+ *
+ ******************************************************************************/
+
+void acpi_ps_free_op(union acpi_parse_object *op)
+{
+ ACPI_FUNCTION_NAME(ps_free_op);
+
+ ASL_CV_CLEAR_OP_COMMENTS(op);
+ if (op->common.aml_opcode == AML_INT_RETURN_VALUE_OP) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "Free retval op: %p\n", op));
+ }
+
+ if (op->common.flags & ACPI_PARSEOP_GENERIC) {
+ (void)acpi_os_release_object(acpi_gbl_ps_node_cache, op);
+ } else {
+ (void)acpi_os_release_object(acpi_gbl_ps_node_ext_cache, op);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: Utility functions
+ *
+ * DESCRIPTION: Low level character and object functions
+ *
+ ******************************************************************************/
+
+/*
+ * Is "c" a namestring lead character?
+ */
+u8 acpi_ps_is_leading_char(u32 c)
+{
+ return ((u8) (c == '_' || (c >= 'A' && c <= 'Z')));
+}
+
+/*
+ * Get op's name (4-byte name segment) or 0 if unnamed
+ */
+u32 acpi_ps_get_name(union acpi_parse_object * op)
+{
+
+ /* The "generic" object has no name associated with it */
+
+ if (op->common.flags & ACPI_PARSEOP_GENERIC) {
+ return (0);
+ }
+
+ /* Only the "Extended" parse objects have a name */
+
+ return (op->named.name);
+}
+
+/*
+ * Set op's name
+ */
+void acpi_ps_set_name(union acpi_parse_object *op, u32 name)
+{
+
+ /* The "generic" object has no name associated with it */
+
+ if (op->common.flags & ACPI_PARSEOP_GENERIC) {
+ return;
+ }
+
+ op->named.name = name;
+}
diff --git a/drivers/acpi/acpica/pswalk.c b/drivers/acpi/acpica/pswalk.c
new file mode 100644
index 000000000..7735a01da
--- /dev/null
+++ b/drivers/acpi/acpica/pswalk.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: pswalk - Parser routines to walk parsed op tree(s)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("pswalk")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_delete_parse_tree
+ *
+ * PARAMETERS: subtree_root - Root of tree (or subtree) to delete
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete a portion of or an entire parse tree.
+ *
+ ******************************************************************************/
+#include "amlcode.h"
+void acpi_ps_delete_parse_tree(union acpi_parse_object *subtree_root)
+{
+ union acpi_parse_object *op = subtree_root;
+ union acpi_parse_object *next = NULL;
+ union acpi_parse_object *parent = NULL;
+ u32 level = 0;
+
+ ACPI_FUNCTION_TRACE_PTR(ps_delete_parse_tree, subtree_root);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE_TREES, " root %p\n", subtree_root));
+
+ /* Visit all nodes in the subtree */
+
+ while (op) {
+ if (op != parent) {
+
+ /* This is the descending case */
+
+ if (ACPI_IS_DEBUG_ENABLED
+ (ACPI_LV_PARSE_TREES, _COMPONENT)) {
+
+ /* This debug option will print the entire parse tree */
+
+ acpi_os_printf(" %*.s%s %p", (level * 4),
+ " ",
+ acpi_ps_get_opcode_name(op->
+ common.
+ aml_opcode),
+ op);
+
+ if (op->named.aml_opcode == AML_INT_NAMEPATH_OP) {
+ acpi_os_printf(" %4.4s",
+ op->common.value.string);
+ }
+ if (op->named.aml_opcode == AML_STRING_OP) {
+ acpi_os_printf(" %s",
+ op->common.value.string);
+ }
+ acpi_os_printf("\n");
+ }
+
+ /* Look for an argument or child of the current op */
+
+ next = acpi_ps_get_arg(op, 0);
+ if (next) {
+
+ /* Still going downward in tree (Op is not completed yet) */
+
+ op = next;
+ level++;
+ continue;
+ }
+ }
+
+ /* No more children, this Op is complete. */
+
+ next = op->common.next;
+ parent = op->common.parent;
+
+ acpi_ps_free_op(op);
+
+ /* If we are back to the starting point, the walk is complete. */
+
+ if (op == subtree_root) {
+ return_VOID;
+ }
+
+ if (next) {
+ op = next;
+ } else {
+ level--;
+ op = parent;
+ }
+ }
+
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c
new file mode 100644
index 000000000..a6509aeb2
--- /dev/null
+++ b/drivers/acpi/acpica/psxface.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: psxface - Parser external interfaces
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acparser.h"
+#include "acdispat.h"
+#include "acinterp.h"
+#include "actables.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_PARSER
+ACPI_MODULE_NAME("psxface")
+
+/* Local Prototypes */
+static void
+acpi_ps_update_parameter_list(struct acpi_evaluate_info *info, u16 action);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_debug_trace
+ *
+ * PARAMETERS: method_name - Valid ACPI name string
+ * debug_level - Optional level mask. 0 to use default
+ * debug_layer - Optional layer mask. 0 to use default
+ * flags - bit 1: one shot(1) or persistent(0)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: External interface to enable debug tracing during control
+ * method execution
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_debug_trace(const char *name, u32 debug_level, u32 debug_layer, u32 flags)
+{
+ acpi_status status;
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ acpi_gbl_trace_method_name = name;
+ acpi_gbl_trace_flags = flags;
+ acpi_gbl_trace_dbg_level = debug_level;
+ acpi_gbl_trace_dbg_layer = debug_layer;
+ status = AE_OK;
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_execute_method
+ *
+ * PARAMETERS: info - Method info block, contains:
+ * node - Method Node to execute
+ * obj_desc - Method object
+ * parameters - List of parameters to pass to the method,
+ * terminated by NULL. Params itself may be
+ * NULL if no parameters are being passed.
+ * return_object - Where to put method's return value (if
+ * any). If NULL, no value is returned.
+ * parameter_type - Type of Parameter list
+ * return_object - Where to put method's return value (if
+ * any). If NULL, no value is returned.
+ * pass_number - Parse or execute pass
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute a control method
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info)
+{
+ acpi_status status;
+ union acpi_parse_object *op;
+ struct acpi_walk_state *walk_state;
+
+ ACPI_FUNCTION_TRACE(ps_execute_method);
+
+ /* Quick validation of DSDT header */
+
+ acpi_tb_check_dsdt_header();
+
+ /* Validate the Info and method Node */
+
+ if (!info || !info->node) {
+ return_ACPI_STATUS(AE_NULL_ENTRY);
+ }
+
+ /* Init for new method, wait on concurrency semaphore */
+
+ status =
+ acpi_ds_begin_method_execution(info->node, info->obj_desc, NULL);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * The caller "owns" the parameters, so give each one an extra reference
+ */
+ acpi_ps_update_parameter_list(info, REF_INCREMENT);
+
+ /*
+ * Execute the method. Performs parse simultaneously
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "**** Begin Method Parse/Execute [%4.4s] **** Node=%p Obj=%p\n",
+ info->node->name.ascii, info->node, info->obj_desc));
+
+ /* Create and init a Root Node */
+
+ op = acpi_ps_create_scope_op(info->obj_desc->method.aml_start);
+ if (!op) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Create and initialize a new walk state */
+
+ info->pass_number = ACPI_IMODE_EXECUTE;
+ walk_state =
+ acpi_ds_create_walk_state(info->obj_desc->method.owner_id, NULL,
+ NULL, NULL);
+ if (!walk_state) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ status = acpi_ds_init_aml_walk(walk_state, op, info->node,
+ info->obj_desc->method.aml_start,
+ info->obj_desc->method.aml_length, info,
+ info->pass_number);
+ if (ACPI_FAILURE(status)) {
+ acpi_ds_delete_walk_state(walk_state);
+ goto cleanup;
+ }
+
+ walk_state->method_pathname = info->full_pathname;
+ walk_state->method_is_nested = FALSE;
+
+ if (info->obj_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL) {
+ walk_state->parse_flags |= ACPI_PARSE_MODULE_LEVEL;
+ }
+
+ /* Invoke an internal method if necessary */
+
+ if (info->obj_desc->method.info_flags & ACPI_METHOD_INTERNAL_ONLY) {
+ status =
+ info->obj_desc->method.dispatch.implementation(walk_state);
+ info->return_object = walk_state->return_desc;
+
+ /* Cleanup states */
+
+ acpi_ds_scope_stack_clear(walk_state);
+ acpi_ps_cleanup_scope(&walk_state->parser_state);
+ acpi_ds_terminate_control_method(walk_state->method_desc,
+ walk_state);
+ acpi_ds_delete_walk_state(walk_state);
+ goto cleanup;
+ }
+
+ /*
+ * Start method evaluation with an implicit return of zero.
+ * This is done for Windows compatibility.
+ */
+ if (acpi_gbl_enable_interpreter_slack) {
+ walk_state->implicit_return_obj =
+ acpi_ut_create_integer_object((u64) 0);
+ if (!walk_state->implicit_return_obj) {
+ status = AE_NO_MEMORY;
+ acpi_ds_delete_walk_state(walk_state);
+ goto cleanup;
+ }
+ }
+
+ /* Parse the AML */
+
+ status = acpi_ps_parse_aml(walk_state);
+
+ /* walk_state was deleted by parse_aml */
+
+cleanup:
+ acpi_ps_delete_parse_tree(op);
+
+ /* Take away the extra reference that we gave the parameters above */
+
+ acpi_ps_update_parameter_list(info, REF_DECREMENT);
+
+ /* Exit now if error above */
+
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * If the method has returned an object, signal this to the caller with
+ * a control exception code
+ */
+ if (info->return_object) {
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "Method returned ObjDesc=%p\n",
+ info->return_object));
+ ACPI_DUMP_STACK_ENTRY(info->return_object);
+
+ status = AE_CTRL_RETURN_VALUE;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_execute_table
+ *
+ * PARAMETERS: info - Method info block, contains:
+ * node - Node to where the is entered into the
+ * namespace
+ * obj_desc - Pseudo method object describing the AML
+ * code of the entire table
+ * pass_number - Parse or execute pass
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute a table
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ps_execute_table(struct acpi_evaluate_info *info)
+{
+ acpi_status status;
+ union acpi_parse_object *op = NULL;
+ struct acpi_walk_state *walk_state = NULL;
+
+ ACPI_FUNCTION_TRACE(ps_execute_table);
+
+ /* Create and init a Root Node */
+
+ op = acpi_ps_create_scope_op(info->obj_desc->method.aml_start);
+ if (!op) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Create and initialize a new walk state */
+
+ walk_state =
+ acpi_ds_create_walk_state(info->obj_desc->method.owner_id, NULL,
+ NULL, NULL);
+ if (!walk_state) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ status = acpi_ds_init_aml_walk(walk_state, op, info->node,
+ info->obj_desc->method.aml_start,
+ info->obj_desc->method.aml_length, info,
+ info->pass_number);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ walk_state->method_pathname = info->full_pathname;
+ walk_state->method_is_nested = FALSE;
+
+ if (info->obj_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL) {
+ walk_state->parse_flags |= ACPI_PARSE_MODULE_LEVEL;
+ }
+
+ /* Info->Node is the default location to load the table */
+
+ if (info->node && info->node != acpi_gbl_root_node) {
+ status =
+ acpi_ds_scope_stack_push(info->node, ACPI_TYPE_METHOD,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Parse the AML, walk_state will be deleted by parse_aml
+ */
+ acpi_ex_enter_interpreter();
+ status = acpi_ps_parse_aml(walk_state);
+ acpi_ex_exit_interpreter();
+ walk_state = NULL;
+
+cleanup:
+ if (walk_state) {
+ acpi_ds_delete_walk_state(walk_state);
+ }
+ if (op) {
+ acpi_ps_delete_parse_tree(op);
+ }
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ps_update_parameter_list
+ *
+ * PARAMETERS: info - See struct acpi_evaluate_info
+ * (Used: parameter_type and Parameters)
+ * action - Add or Remove reference
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Update reference count on all method parameter objects
+ *
+ ******************************************************************************/
+
+static void
+acpi_ps_update_parameter_list(struct acpi_evaluate_info *info, u16 action)
+{
+ u32 i;
+
+ if (info->parameters) {
+
+ /* Update reference count for each parameter */
+
+ for (i = 0; info->parameters[i]; i++) {
+
+ /* Ignore errors, just do them all */
+
+ (void)acpi_ut_update_object_reference(info->
+ parameters[i],
+ action);
+ }
+ }
+}
diff --git a/drivers/acpi/acpica/rsaddr.c b/drivers/acpi/acpica/rsaddr.c
new file mode 100644
index 000000000..5737c3af1
--- /dev/null
+++ b/drivers/acpi/acpica/rsaddr.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsaddr - Address resource descriptors (16/32/64)
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsaddr")
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_address16 - All WORD (16-bit) address resources
+ *
+ ******************************************************************************/
+struct acpi_rsconvert_info acpi_rs_convert_address16[5] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_ADDRESS16,
+ ACPI_RS_SIZE(struct acpi_resource_address16),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_address16)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_ADDRESS16,
+ sizeof(struct aml_resource_address16),
+ 0},
+
+ /* Resource Type, General Flags, and Type-Specific Flags */
+
+ {ACPI_RSC_ADDRESS, 0, 0, 0},
+
+ /*
+ * These fields are contiguous in both the source and destination:
+ * Address Granularity
+ * Address Range Minimum
+ * Address Range Maximum
+ * Address Translation Offset
+ * Address Length
+ */
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.address16.address.granularity),
+ AML_OFFSET(address16.granularity),
+ 5},
+
+ /* Optional resource_source (Index and String) */
+
+ {ACPI_RSC_SOURCE, ACPI_RS_OFFSET(data.address16.resource_source),
+ 0,
+ sizeof(struct aml_resource_address16)}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_address32 - All DWORD (32-bit) address resources
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_address32[5] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_ADDRESS32,
+ ACPI_RS_SIZE(struct acpi_resource_address32),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_address32)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_ADDRESS32,
+ sizeof(struct aml_resource_address32),
+ 0},
+
+ /* Resource Type, General Flags, and Type-Specific Flags */
+
+ {ACPI_RSC_ADDRESS, 0, 0, 0},
+
+ /*
+ * These fields are contiguous in both the source and destination:
+ * Address Granularity
+ * Address Range Minimum
+ * Address Range Maximum
+ * Address Translation Offset
+ * Address Length
+ */
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.address32.address.granularity),
+ AML_OFFSET(address32.granularity),
+ 5},
+
+ /* Optional resource_source (Index and String) */
+
+ {ACPI_RSC_SOURCE, ACPI_RS_OFFSET(data.address32.resource_source),
+ 0,
+ sizeof(struct aml_resource_address32)}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_address64 - All QWORD (64-bit) address resources
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_address64[5] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_ADDRESS64,
+ ACPI_RS_SIZE(struct acpi_resource_address64),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_address64)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_ADDRESS64,
+ sizeof(struct aml_resource_address64),
+ 0},
+
+ /* Resource Type, General Flags, and Type-Specific Flags */
+
+ {ACPI_RSC_ADDRESS, 0, 0, 0},
+
+ /*
+ * These fields are contiguous in both the source and destination:
+ * Address Granularity
+ * Address Range Minimum
+ * Address Range Maximum
+ * Address Translation Offset
+ * Address Length
+ */
+ {ACPI_RSC_MOVE64, ACPI_RS_OFFSET(data.address64.address.granularity),
+ AML_OFFSET(address64.granularity),
+ 5},
+
+ /* Optional resource_source (Index and String) */
+
+ {ACPI_RSC_SOURCE, ACPI_RS_OFFSET(data.address64.resource_source),
+ 0,
+ sizeof(struct aml_resource_address64)}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_ext_address64 - All Extended (64-bit) address resources
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_ext_address64[5] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64,
+ ACPI_RS_SIZE(struct acpi_resource_extended_address64),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_ext_address64)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64,
+ sizeof(struct aml_resource_extended_address64),
+ 0},
+
+ /* Resource Type, General Flags, and Type-Specific Flags */
+
+ {ACPI_RSC_ADDRESS, 0, 0, 0},
+
+ /* Revision ID */
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.ext_address64.revision_ID),
+ AML_OFFSET(ext_address64.revision_ID),
+ 1},
+ /*
+ * These fields are contiguous in both the source and destination:
+ * Address Granularity
+ * Address Range Minimum
+ * Address Range Maximum
+ * Address Translation Offset
+ * Address Length
+ * Type-Specific Attribute
+ */
+ {ACPI_RSC_MOVE64,
+ ACPI_RS_OFFSET(data.ext_address64.address.granularity),
+ AML_OFFSET(ext_address64.granularity),
+ 6}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_general_flags - Flags common to all address descriptors
+ *
+ ******************************************************************************/
+
+static struct acpi_rsconvert_info acpi_rs_convert_general_flags[6] = {
+ {ACPI_RSC_FLAGINIT, 0, AML_OFFSET(address.flags),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_general_flags)},
+
+ /* Resource Type (Memory, Io, bus_number, etc.) */
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.address.resource_type),
+ AML_OFFSET(address.resource_type),
+ 1},
+
+ /* General flags - Consume, Decode, min_fixed, max_fixed */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.producer_consumer),
+ AML_OFFSET(address.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.decode),
+ AML_OFFSET(address.flags),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.min_address_fixed),
+ AML_OFFSET(address.flags),
+ 2},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.max_address_fixed),
+ AML_OFFSET(address.flags),
+ 3}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_mem_flags - Flags common to Memory address descriptors
+ *
+ ******************************************************************************/
+
+static struct acpi_rsconvert_info acpi_rs_convert_mem_flags[5] = {
+ {ACPI_RSC_FLAGINIT, 0, AML_OFFSET(address.specific_flags),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_mem_flags)},
+
+ /* Memory-specific flags */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.info.mem.write_protect),
+ AML_OFFSET(address.specific_flags),
+ 0},
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.address.info.mem.caching),
+ AML_OFFSET(address.specific_flags),
+ 1},
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.address.info.mem.range_type),
+ AML_OFFSET(address.specific_flags),
+ 3},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.info.mem.translation),
+ AML_OFFSET(address.specific_flags),
+ 5}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_io_flags - Flags common to I/O address descriptors
+ *
+ ******************************************************************************/
+
+static struct acpi_rsconvert_info acpi_rs_convert_io_flags[4] = {
+ {ACPI_RSC_FLAGINIT, 0, AML_OFFSET(address.specific_flags),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_io_flags)},
+
+ /* I/O-specific flags */
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.address.info.io.range_type),
+ AML_OFFSET(address.specific_flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.info.io.translation),
+ AML_OFFSET(address.specific_flags),
+ 4},
+
+ {ACPI_RSC_1BITFLAG,
+ ACPI_RS_OFFSET(data.address.info.io.translation_type),
+ AML_OFFSET(address.specific_flags),
+ 5}
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_get_address_common
+ *
+ * PARAMETERS: resource - Pointer to the internal resource struct
+ * aml - Pointer to the AML resource descriptor
+ *
+ * RETURN: TRUE if the resource_type field is OK, FALSE otherwise
+ *
+ * DESCRIPTION: Convert common flag fields from a raw AML resource descriptor
+ * to an internal resource descriptor
+ *
+ ******************************************************************************/
+
+u8
+acpi_rs_get_address_common(struct acpi_resource *resource,
+ union aml_resource *aml)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /* Validate the Resource Type */
+
+ if ((aml->address.resource_type > 2) &&
+ (aml->address.resource_type < 0xC0)) {
+ return (FALSE);
+ }
+
+ /* Get the Resource Type and General Flags */
+
+ (void)acpi_rs_convert_aml_to_resource(resource, aml,
+ acpi_rs_convert_general_flags);
+
+ /* Get the Type-Specific Flags (Memory and I/O descriptors only) */
+
+ if (resource->data.address.resource_type == ACPI_MEMORY_RANGE) {
+ (void)acpi_rs_convert_aml_to_resource(resource, aml,
+ acpi_rs_convert_mem_flags);
+ } else if (resource->data.address.resource_type == ACPI_IO_RANGE) {
+ (void)acpi_rs_convert_aml_to_resource(resource, aml,
+ acpi_rs_convert_io_flags);
+ } else {
+ /* Generic resource type, just grab the type_specific byte */
+
+ resource->data.address.info.type_specific =
+ aml->address.specific_flags;
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_set_address_common
+ *
+ * PARAMETERS: aml - Pointer to the AML resource descriptor
+ * resource - Pointer to the internal resource struct
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Convert common flag fields from a resource descriptor to an
+ * AML descriptor
+ *
+ ******************************************************************************/
+
+void
+acpi_rs_set_address_common(union aml_resource *aml,
+ struct acpi_resource *resource)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /* Set the Resource Type and General Flags */
+
+ (void)acpi_rs_convert_resource_to_aml(resource, aml,
+ acpi_rs_convert_general_flags);
+
+ /* Set the Type-Specific Flags (Memory and I/O descriptors only) */
+
+ if (resource->data.address.resource_type == ACPI_MEMORY_RANGE) {
+ (void)acpi_rs_convert_resource_to_aml(resource, aml,
+ acpi_rs_convert_mem_flags);
+ } else if (resource->data.address.resource_type == ACPI_IO_RANGE) {
+ (void)acpi_rs_convert_resource_to_aml(resource, aml,
+ acpi_rs_convert_io_flags);
+ } else {
+ /* Generic resource type, just copy the type_specific byte */
+
+ aml->address.specific_flags =
+ resource->data.address.info.type_specific;
+ }
+}
diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c
new file mode 100644
index 000000000..90583db45
--- /dev/null
+++ b/drivers/acpi/acpica/rscalc.c
@@ -0,0 +1,824 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rscalc - Calculate stream and list lengths
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rscalc")
+
+/* Local prototypes */
+static u8 acpi_rs_count_set_bits(u16 bit_field);
+
+static acpi_rs_length
+acpi_rs_struct_option_length(struct acpi_resource_source *resource_source);
+
+static u32
+acpi_rs_stream_option_length(u32 resource_length, u32 minimum_total_length);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_count_set_bits
+ *
+ * PARAMETERS: bit_field - Field in which to count bits
+ *
+ * RETURN: Number of bits set within the field
+ *
+ * DESCRIPTION: Count the number of bits set in a resource field. Used for
+ * (Short descriptor) interrupt and DMA lists.
+ *
+ ******************************************************************************/
+
+static u8 acpi_rs_count_set_bits(u16 bit_field)
+{
+ u8 bits_set;
+
+ ACPI_FUNCTION_ENTRY();
+
+ for (bits_set = 0; bit_field; bits_set++) {
+
+ /* Zero the least significant bit that is set */
+
+ bit_field &= (u16) (bit_field - 1);
+ }
+
+ return (bits_set);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_struct_option_length
+ *
+ * PARAMETERS: resource_source - Pointer to optional descriptor field
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Common code to handle optional resource_source_index and
+ * resource_source fields in some Large descriptors. Used during
+ * list-to-stream conversion
+ *
+ ******************************************************************************/
+
+static acpi_rs_length
+acpi_rs_struct_option_length(struct acpi_resource_source *resource_source)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * If the resource_source string is valid, return the size of the string
+ * (string_length includes the NULL terminator) plus the size of the
+ * resource_source_index (1).
+ */
+ if (resource_source->string_ptr) {
+ return ((acpi_rs_length)(resource_source->string_length + 1));
+ }
+
+ return (0);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_stream_option_length
+ *
+ * PARAMETERS: resource_length - Length from the resource header
+ * minimum_total_length - Minimum length of this resource, before
+ * any optional fields. Includes header size
+ *
+ * RETURN: Length of optional string (0 if no string present)
+ *
+ * DESCRIPTION: Common code to handle optional resource_source_index and
+ * resource_source fields in some Large descriptors. Used during
+ * stream-to-list conversion
+ *
+ ******************************************************************************/
+
+static u32
+acpi_rs_stream_option_length(u32 resource_length,
+ u32 minimum_aml_resource_length)
+{
+ u32 string_length = 0;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * The resource_source_index and resource_source are optional elements of
+ * some Large-type resource descriptors.
+ */
+
+ /*
+ * If the length of the actual resource descriptor is greater than the
+ * ACPI spec-defined minimum length, it means that a resource_source_index
+ * exists and is followed by a (required) null terminated string. The
+ * string length (including the null terminator) is the resource length
+ * minus the minimum length, minus one byte for the resource_source_index
+ * itself.
+ */
+ if (resource_length > minimum_aml_resource_length) {
+
+ /* Compute the length of the optional string */
+
+ string_length =
+ resource_length - minimum_aml_resource_length - 1;
+ }
+
+ /*
+ * Round the length up to a multiple of the native word in order to
+ * guarantee that the entire resource descriptor is native word aligned
+ */
+ return ((u32) ACPI_ROUND_UP_TO_NATIVE_WORD(string_length));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_get_aml_length
+ *
+ * PARAMETERS: resource - Pointer to the resource linked list
+ * resource_list_size - Size of the resource linked list
+ * size_needed - Where the required size is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Takes a linked list of internal resource descriptors and
+ * calculates the size buffer needed to hold the corresponding
+ * external resource byte stream.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_get_aml_length(struct acpi_resource *resource,
+ acpi_size resource_list_size, acpi_size *size_needed)
+{
+ acpi_size aml_size_needed = 0;
+ struct acpi_resource *resource_end;
+ acpi_rs_length total_size;
+
+ ACPI_FUNCTION_TRACE(rs_get_aml_length);
+
+ /* Traverse entire list of internal resource descriptors */
+
+ resource_end =
+ ACPI_ADD_PTR(struct acpi_resource, resource, resource_list_size);
+ while (resource < resource_end) {
+
+ /* Validate the descriptor type */
+
+ if (resource->type > ACPI_RESOURCE_TYPE_MAX) {
+ return_ACPI_STATUS(AE_AML_INVALID_RESOURCE_TYPE);
+ }
+
+ /* Sanity check the length. It must not be zero, or we loop forever */
+
+ if (!resource->length) {
+ return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH);
+ }
+
+ /* Get the base size of the (external stream) resource descriptor */
+
+ total_size = acpi_gbl_aml_resource_sizes[resource->type];
+
+ /*
+ * Augment the base size for descriptors with optional and/or
+ * variable-length fields
+ */
+ switch (resource->type) {
+ case ACPI_RESOURCE_TYPE_IRQ:
+
+ /* Length can be 3 or 2 */
+
+ if (resource->data.irq.descriptor_length == 2) {
+ total_size--;
+ }
+ break;
+
+ case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+
+ /* Length can be 1 or 0 */
+
+ if (resource->data.irq.descriptor_length == 0) {
+ total_size--;
+ }
+ break;
+
+ case ACPI_RESOURCE_TYPE_VENDOR:
+ /*
+ * Vendor Defined Resource:
+ * For a Vendor Specific resource, if the Length is between 1 and 7
+ * it will be created as a Small Resource data type, otherwise it
+ * is a Large Resource data type.
+ */
+ if (resource->data.vendor.byte_length > 7) {
+
+ /* Base size of a Large resource descriptor */
+
+ total_size =
+ sizeof(struct aml_resource_large_header);
+ }
+
+ /* Add the size of the vendor-specific data */
+
+ total_size = (acpi_rs_length)
+ (total_size + resource->data.vendor.byte_length);
+ break;
+
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ /*
+ * End Tag:
+ * We are done -- return the accumulated total size.
+ */
+ *size_needed = aml_size_needed + total_size;
+
+ /* Normal exit */
+
+ return_ACPI_STATUS(AE_OK);
+
+ case ACPI_RESOURCE_TYPE_ADDRESS16:
+ /*
+ * 16-Bit Address Resource:
+ * Add the size of the optional resource_source info
+ */
+ total_size = (acpi_rs_length)(total_size +
+ acpi_rs_struct_option_length
+ (&resource->data.
+ address16.
+ resource_source));
+ break;
+
+ case ACPI_RESOURCE_TYPE_ADDRESS32:
+ /*
+ * 32-Bit Address Resource:
+ * Add the size of the optional resource_source info
+ */
+ total_size = (acpi_rs_length)(total_size +
+ acpi_rs_struct_option_length
+ (&resource->data.
+ address32.
+ resource_source));
+ break;
+
+ case ACPI_RESOURCE_TYPE_ADDRESS64:
+ /*
+ * 64-Bit Address Resource:
+ * Add the size of the optional resource_source info
+ */
+ total_size = (acpi_rs_length)(total_size +
+ acpi_rs_struct_option_length
+ (&resource->data.
+ address64.
+ resource_source));
+ break;
+
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ /*
+ * Extended IRQ Resource:
+ * Add the size of each additional optional interrupt beyond the
+ * required 1 (4 bytes for each u32 interrupt number)
+ */
+ total_size = (acpi_rs_length)(total_size +
+ ((resource->data.
+ extended_irq.
+ interrupt_count -
+ 1) * 4) +
+ /* Add the size of the optional resource_source info */
+ acpi_rs_struct_option_length
+ (&resource->data.
+ extended_irq.
+ resource_source));
+ break;
+
+ case ACPI_RESOURCE_TYPE_GPIO:
+
+ total_size = (acpi_rs_length)(total_size +
+ (resource->data.gpio.
+ pin_table_length * 2) +
+ resource->data.gpio.
+ resource_source.
+ string_length +
+ resource->data.gpio.
+ vendor_length);
+
+ break;
+
+ case ACPI_RESOURCE_TYPE_PIN_FUNCTION:
+
+ total_size = (acpi_rs_length)(total_size +
+ (resource->data.
+ pin_function.
+ pin_table_length * 2) +
+ resource->data.
+ pin_function.
+ resource_source.
+ string_length +
+ resource->data.
+ pin_function.
+ vendor_length);
+
+ break;
+
+ case ACPI_RESOURCE_TYPE_SERIAL_BUS:
+
+ total_size =
+ acpi_gbl_aml_resource_serial_bus_sizes[resource->
+ data.
+ common_serial_bus.
+ type];
+
+ total_size = (acpi_rs_length)(total_size +
+ resource->data.
+ i2c_serial_bus.
+ resource_source.
+ string_length +
+ resource->data.
+ i2c_serial_bus.
+ vendor_length);
+
+ break;
+
+ case ACPI_RESOURCE_TYPE_PIN_CONFIG:
+
+ total_size = (acpi_rs_length)(total_size +
+ (resource->data.
+ pin_config.
+ pin_table_length * 2) +
+ resource->data.pin_config.
+ resource_source.
+ string_length +
+ resource->data.pin_config.
+ vendor_length);
+
+ break;
+
+ case ACPI_RESOURCE_TYPE_PIN_GROUP:
+
+ total_size = (acpi_rs_length)(total_size +
+ (resource->data.pin_group.
+ pin_table_length * 2) +
+ resource->data.pin_group.
+ resource_label.
+ string_length +
+ resource->data.pin_group.
+ vendor_length);
+
+ break;
+
+ case ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION:
+
+ total_size = (acpi_rs_length)(total_size +
+ resource->data.
+ pin_group_function.
+ resource_source.
+ string_length +
+ resource->data.
+ pin_group_function.
+ resource_source_label.
+ string_length +
+ resource->data.
+ pin_group_function.
+ vendor_length);
+
+ break;
+
+ case ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG:
+
+ total_size = (acpi_rs_length)(total_size +
+ resource->data.
+ pin_group_config.
+ resource_source.
+ string_length +
+ resource->data.
+ pin_group_config.
+ resource_source_label.
+ string_length +
+ resource->data.
+ pin_group_config.
+ vendor_length);
+
+ break;
+
+ default:
+
+ break;
+ }
+
+ /* Update the total */
+
+ aml_size_needed += total_size;
+
+ /* Point to the next object */
+
+ resource =
+ ACPI_ADD_PTR(struct acpi_resource, resource,
+ resource->length);
+ }
+
+ /* Did not find an end_tag resource descriptor */
+
+ return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_get_list_length
+ *
+ * PARAMETERS: aml_buffer - Pointer to the resource byte stream
+ * aml_buffer_length - Size of aml_buffer
+ * size_needed - Where the size needed is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Takes an external resource byte stream and calculates the size
+ * buffer needed to hold the corresponding internal resource
+ * descriptor linked list.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_get_list_length(u8 *aml_buffer,
+ u32 aml_buffer_length, acpi_size *size_needed)
+{
+ acpi_status status;
+ u8 *end_aml;
+ u8 *buffer;
+ u32 buffer_size;
+ u16 temp16;
+ u16 resource_length;
+ u32 extra_struct_bytes;
+ u8 resource_index;
+ u8 minimum_aml_resource_length;
+ union aml_resource *aml_resource;
+
+ ACPI_FUNCTION_TRACE(rs_get_list_length);
+
+ *size_needed = ACPI_RS_SIZE_MIN; /* Minimum size is one end_tag */
+ end_aml = aml_buffer + aml_buffer_length;
+
+ /* Walk the list of AML resource descriptors */
+
+ while (aml_buffer < end_aml) {
+
+ /* Validate the Resource Type and Resource Length */
+
+ status =
+ acpi_ut_validate_resource(NULL, aml_buffer,
+ &resource_index);
+ if (ACPI_FAILURE(status)) {
+ /*
+ * Exit on failure. Cannot continue because the descriptor length
+ * may be bogus also.
+ */
+ return_ACPI_STATUS(status);
+ }
+
+ aml_resource = (void *)aml_buffer;
+
+ /* Get the resource length and base (minimum) AML size */
+
+ resource_length = acpi_ut_get_resource_length(aml_buffer);
+ minimum_aml_resource_length =
+ acpi_gbl_resource_aml_sizes[resource_index];
+
+ /*
+ * Augment the size for descriptors with optional
+ * and/or variable length fields
+ */
+ extra_struct_bytes = 0;
+ buffer =
+ aml_buffer + acpi_ut_get_resource_header_length(aml_buffer);
+
+ switch (acpi_ut_get_resource_type(aml_buffer)) {
+ case ACPI_RESOURCE_NAME_IRQ:
+ /*
+ * IRQ Resource:
+ * Get the number of bits set in the 16-bit IRQ mask
+ */
+ ACPI_MOVE_16_TO_16(&temp16, buffer);
+ extra_struct_bytes = acpi_rs_count_set_bits(temp16);
+ break;
+
+ case ACPI_RESOURCE_NAME_DMA:
+ /*
+ * DMA Resource:
+ * Get the number of bits set in the 8-bit DMA mask
+ */
+ extra_struct_bytes = acpi_rs_count_set_bits(*buffer);
+ break;
+
+ case ACPI_RESOURCE_NAME_VENDOR_SMALL:
+ case ACPI_RESOURCE_NAME_VENDOR_LARGE:
+ /*
+ * Vendor Resource:
+ * Get the number of vendor data bytes
+ */
+ extra_struct_bytes = resource_length;
+
+ /*
+ * There is already one byte included in the minimum
+ * descriptor size. If there are extra struct bytes,
+ * subtract one from the count.
+ */
+ if (extra_struct_bytes) {
+ extra_struct_bytes--;
+ }
+ break;
+
+ case ACPI_RESOURCE_NAME_END_TAG:
+ /*
+ * End Tag: This is the normal exit
+ */
+ return_ACPI_STATUS(AE_OK);
+
+ case ACPI_RESOURCE_NAME_ADDRESS32:
+ case ACPI_RESOURCE_NAME_ADDRESS16:
+ case ACPI_RESOURCE_NAME_ADDRESS64:
+ /*
+ * Address Resource:
+ * Add the size of the optional resource_source
+ */
+ extra_struct_bytes =
+ acpi_rs_stream_option_length(resource_length,
+ minimum_aml_resource_length);
+ break;
+
+ case ACPI_RESOURCE_NAME_EXTENDED_IRQ:
+ /*
+ * Extended IRQ Resource:
+ * Using the interrupt_table_length, add 4 bytes for each additional
+ * interrupt. Note: at least one interrupt is required and is
+ * included in the minimum descriptor size (reason for the -1)
+ */
+ extra_struct_bytes = (buffer[1] - 1) * sizeof(u32);
+
+ /* Add the size of the optional resource_source */
+
+ extra_struct_bytes +=
+ acpi_rs_stream_option_length(resource_length -
+ extra_struct_bytes,
+ minimum_aml_resource_length);
+ break;
+
+ case ACPI_RESOURCE_NAME_GPIO:
+
+ /* Vendor data is optional */
+
+ if (aml_resource->gpio.vendor_length) {
+ extra_struct_bytes +=
+ aml_resource->gpio.vendor_offset -
+ aml_resource->gpio.pin_table_offset +
+ aml_resource->gpio.vendor_length;
+ } else {
+ extra_struct_bytes +=
+ aml_resource->large_header.resource_length +
+ sizeof(struct aml_resource_large_header) -
+ aml_resource->gpio.pin_table_offset;
+ }
+ break;
+
+ case ACPI_RESOURCE_NAME_PIN_FUNCTION:
+
+ /* Vendor data is optional */
+
+ if (aml_resource->pin_function.vendor_length) {
+ extra_struct_bytes +=
+ aml_resource->pin_function.vendor_offset -
+ aml_resource->pin_function.
+ pin_table_offset +
+ aml_resource->pin_function.vendor_length;
+ } else {
+ extra_struct_bytes +=
+ aml_resource->large_header.resource_length +
+ sizeof(struct aml_resource_large_header) -
+ aml_resource->pin_function.pin_table_offset;
+ }
+ break;
+
+ 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;
+
+ case ACPI_RESOURCE_NAME_PIN_CONFIG:
+
+ /* Vendor data is optional */
+
+ if (aml_resource->pin_config.vendor_length) {
+ extra_struct_bytes +=
+ aml_resource->pin_config.vendor_offset -
+ aml_resource->pin_config.pin_table_offset +
+ aml_resource->pin_config.vendor_length;
+ } else {
+ extra_struct_bytes +=
+ aml_resource->large_header.resource_length +
+ sizeof(struct aml_resource_large_header) -
+ aml_resource->pin_config.pin_table_offset;
+ }
+ break;
+
+ case ACPI_RESOURCE_NAME_PIN_GROUP:
+
+ extra_struct_bytes +=
+ aml_resource->pin_group.vendor_offset -
+ aml_resource->pin_group.pin_table_offset +
+ aml_resource->pin_group.vendor_length;
+
+ break;
+
+ case ACPI_RESOURCE_NAME_PIN_GROUP_FUNCTION:
+
+ extra_struct_bytes +=
+ aml_resource->pin_group_function.vendor_offset -
+ aml_resource->pin_group_function.res_source_offset +
+ aml_resource->pin_group_function.vendor_length;
+
+ break;
+
+ case ACPI_RESOURCE_NAME_PIN_GROUP_CONFIG:
+
+ extra_struct_bytes +=
+ aml_resource->pin_group_config.vendor_offset -
+ aml_resource->pin_group_config.res_source_offset +
+ aml_resource->pin_group_config.vendor_length;
+
+ break;
+
+ default:
+
+ break;
+ }
+
+ /*
+ * Update the required buffer size for the internal descriptor structs
+ *
+ * Important: Round the size up for the appropriate alignment. This
+ * is a requirement on IA64.
+ */
+ if (acpi_ut_get_resource_type(aml_buffer) ==
+ ACPI_RESOURCE_NAME_SERIAL_BUS) {
+ buffer_size =
+ acpi_gbl_resource_struct_serial_bus_sizes
+ [aml_resource->common_serial_bus.type] +
+ extra_struct_bytes;
+ } else {
+ buffer_size =
+ acpi_gbl_resource_struct_sizes[resource_index] +
+ extra_struct_bytes;
+ }
+
+ buffer_size = (u32)ACPI_ROUND_UP_TO_NATIVE_WORD(buffer_size);
+ *size_needed += buffer_size;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES,
+ "Type %.2X, AmlLength %.2X InternalLength %.2X%8X\n",
+ acpi_ut_get_resource_type(aml_buffer),
+ acpi_ut_get_descriptor_length(aml_buffer),
+ ACPI_FORMAT_UINT64(*size_needed)));
+
+ /*
+ * Point to the next resource within the AML stream using the length
+ * contained in the resource descriptor header
+ */
+ aml_buffer += acpi_ut_get_descriptor_length(aml_buffer);
+ }
+
+ /* Did not find an end_tag resource descriptor */
+
+ return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_get_pci_routing_table_length
+ *
+ * PARAMETERS: package_object - Pointer to the package object
+ * buffer_size_needed - u32 pointer of the size buffer
+ * needed to properly return the
+ * parsed data
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Given a package representing a PCI routing table, this
+ * calculates the size of the corresponding linked list of
+ * descriptions.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object,
+ acpi_size *buffer_size_needed)
+{
+ u32 number_of_elements;
+ acpi_size temp_size_needed = 0;
+ union acpi_operand_object **top_object_list;
+ u32 index;
+ union acpi_operand_object *package_element;
+ union acpi_operand_object **sub_object_list;
+ u8 name_found;
+ u32 table_index;
+
+ ACPI_FUNCTION_TRACE(rs_get_pci_routing_table_length);
+
+ number_of_elements = package_object->package.count;
+
+ /*
+ * Calculate the size of the return buffer.
+ * The base size is the number of elements * the sizes of the
+ * structures. Additional space for the strings is added below.
+ * The minus one is to subtract the size of the u8 Source[1]
+ * member because it is added below.
+ *
+ * But each PRT_ENTRY structure has a pointer to a string and
+ * the size of that string must be found.
+ */
+ top_object_list = package_object->package.elements;
+
+ for (index = 0; index < number_of_elements; index++) {
+
+ /* Dereference the subpackage */
+
+ package_element = *top_object_list;
+
+ /* We must have a valid Package object */
+
+ if (!package_element ||
+ (package_element->common.type != ACPI_TYPE_PACKAGE)) {
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * The sub_object_list will now point to an array of the
+ * four IRQ elements: Address, Pin, Source and source_index
+ */
+ sub_object_list = package_element->package.elements;
+
+ /* Scan the irq_table_elements for the Source Name String */
+
+ name_found = FALSE;
+
+ for (table_index = 0;
+ table_index < package_element->package.count
+ && !name_found; table_index++) {
+ if (*sub_object_list && /* Null object allowed */
+ ((ACPI_TYPE_STRING ==
+ (*sub_object_list)->common.type) ||
+ ((ACPI_TYPE_LOCAL_REFERENCE ==
+ (*sub_object_list)->common.type) &&
+ ((*sub_object_list)->reference.class ==
+ ACPI_REFCLASS_NAME)))) {
+ name_found = TRUE;
+ } else {
+ /* Look at the next element */
+
+ sub_object_list++;
+ }
+ }
+
+ temp_size_needed += (sizeof(struct acpi_pci_routing_table) - 4);
+
+ /* Was a String type found? */
+
+ if (name_found) {
+ if ((*sub_object_list)->common.type == ACPI_TYPE_STRING) {
+ /*
+ * The length String.Length field does not include the
+ * terminating NULL, add 1
+ */
+ temp_size_needed += ((acpi_size)
+ (*sub_object_list)->string.
+ length + 1);
+ } else {
+ temp_size_needed += acpi_ns_get_pathname_length((*sub_object_list)->reference.node);
+ }
+ } else {
+ /*
+ * If no name was found, then this is a NULL, which is
+ * translated as a u32 zero.
+ */
+ temp_size_needed += sizeof(u32);
+ }
+
+ /* Round up the size since each element must be aligned */
+
+ temp_size_needed = ACPI_ROUND_UP_TO_64BIT(temp_size_needed);
+
+ /* Point to the next union acpi_operand_object */
+
+ top_object_list++;
+ }
+
+ /*
+ * Add an extra element to the end of the list, essentially a
+ * NULL terminator
+ */
+ *buffer_size_needed =
+ temp_size_needed + sizeof(struct acpi_pci_routing_table);
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c
new file mode 100644
index 000000000..c659b5498
--- /dev/null
+++ b/drivers/acpi/acpica/rscreate.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rscreate - Create resource lists/tables
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rscreate")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_buffer_to_resource
+ *
+ * PARAMETERS: aml_buffer - Pointer to the resource byte stream
+ * aml_buffer_length - Length of the aml_buffer
+ * resource_ptr - Where the converted resource is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert a raw AML buffer to a resource list
+ *
+ ******************************************************************************/
+acpi_status
+acpi_buffer_to_resource(u8 *aml_buffer,
+ u16 aml_buffer_length,
+ struct acpi_resource **resource_ptr)
+{
+ acpi_status status;
+ acpi_size list_size_needed;
+ void *resource;
+ void *current_resource_ptr;
+
+ ACPI_FUNCTION_TRACE(acpi_buffer_to_resource);
+
+ /*
+ * Note: we allow AE_AML_NO_RESOURCE_END_TAG, since an end tag
+ * is not required here.
+ */
+
+ /* Get the required length for the converted resource */
+
+ status =
+ acpi_rs_get_list_length(aml_buffer, aml_buffer_length,
+ &list_size_needed);
+ if (status == AE_AML_NO_RESOURCE_END_TAG) {
+ status = AE_OK;
+ }
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Allocate a buffer for the converted resource */
+
+ resource = ACPI_ALLOCATE_ZEROED(list_size_needed);
+ current_resource_ptr = resource;
+ if (!resource) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Perform the AML-to-Resource conversion */
+
+ status = acpi_ut_walk_aml_resources(NULL, aml_buffer, aml_buffer_length,
+ acpi_rs_convert_aml_to_resources,
+ &current_resource_ptr);
+ if (status == AE_AML_NO_RESOURCE_END_TAG) {
+ status = AE_OK;
+ }
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(resource);
+ } else {
+ *resource_ptr = resource;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_buffer_to_resource)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_create_resource_list
+ *
+ * PARAMETERS: aml_buffer - Pointer to the resource byte stream
+ * output_buffer - Pointer to the user's buffer
+ *
+ * RETURN: Status: AE_OK if okay, else a valid acpi_status code
+ * If output_buffer is not large enough, output_buffer_length
+ * indicates how large output_buffer should be, else it
+ * indicates how may u8 elements of output_buffer are valid.
+ *
+ * DESCRIPTION: Takes the byte stream returned from a _CRS, _PRS control method
+ * execution and parses the stream to create a linked list
+ * of device resources.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_rs_create_resource_list(union acpi_operand_object *aml_buffer,
+ struct acpi_buffer *output_buffer)
+{
+
+ acpi_status status;
+ u8 *aml_start;
+ acpi_size list_size_needed = 0;
+ u32 aml_buffer_length;
+ void *resource;
+
+ ACPI_FUNCTION_TRACE(rs_create_resource_list);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "AmlBuffer = %p\n", aml_buffer));
+
+ /* Params already validated, so we don't re-validate here */
+
+ aml_buffer_length = aml_buffer->buffer.length;
+ aml_start = aml_buffer->buffer.pointer;
+
+ /*
+ * Pass the aml_buffer into a module that can calculate
+ * the buffer size needed for the linked list
+ */
+ status = acpi_rs_get_list_length(aml_start, aml_buffer_length,
+ &list_size_needed);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Status=%X ListSizeNeeded=%X\n",
+ status, (u32) list_size_needed));
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Validate/Allocate/Clear caller buffer */
+
+ status = acpi_ut_initialize_buffer(output_buffer, list_size_needed);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Do the conversion */
+
+ resource = output_buffer->pointer;
+ status = acpi_ut_walk_aml_resources(NULL, aml_start, aml_buffer_length,
+ acpi_rs_convert_aml_to_resources,
+ &resource);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "OutputBuffer %p Length %X\n",
+ output_buffer->pointer, (u32) output_buffer->length));
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_create_pci_routing_table
+ *
+ * PARAMETERS: package_object - Pointer to a package containing one
+ * of more ACPI_OPERAND_OBJECTs
+ * output_buffer - Pointer to the user's buffer
+ *
+ * RETURN: Status AE_OK if okay, else a valid acpi_status code.
+ * If the output_buffer is too small, the error will be
+ * AE_BUFFER_OVERFLOW and output_buffer->Length will point
+ * to the size buffer needed.
+ *
+ * DESCRIPTION: Takes the union acpi_operand_object package and creates a
+ * linked list of PCI interrupt descriptions
+ *
+ * NOTE: It is the caller's responsibility to ensure that the start of the
+ * output buffer is aligned properly (if necessary).
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object,
+ struct acpi_buffer *output_buffer)
+{
+ u8 *buffer;
+ union acpi_operand_object **top_object_list;
+ union acpi_operand_object **sub_object_list;
+ union acpi_operand_object *obj_desc;
+ acpi_size buffer_size_needed = 0;
+ u32 number_of_elements;
+ u32 index;
+ struct acpi_pci_routing_table *user_prt;
+ struct acpi_namespace_node *node;
+ acpi_status status;
+ struct acpi_buffer path_buffer;
+
+ ACPI_FUNCTION_TRACE(rs_create_pci_routing_table);
+
+ /* Params already validated, so we don't re-validate here */
+
+ /* Get the required buffer length */
+
+ status =
+ acpi_rs_get_pci_routing_table_length(package_object,
+ &buffer_size_needed);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "BufferSizeNeeded = %X\n",
+ (u32) buffer_size_needed));
+
+ /* Validate/Allocate/Clear caller buffer */
+
+ status = acpi_ut_initialize_buffer(output_buffer, buffer_size_needed);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Loop through the ACPI_INTERNAL_OBJECTS - Each object should be a
+ * package that in turn contains an u64 Address, a u8 Pin,
+ * a Name, and a u8 source_index.
+ */
+ top_object_list = package_object->package.elements;
+ number_of_elements = package_object->package.count;
+ buffer = output_buffer->pointer;
+ user_prt = ACPI_CAST_PTR(struct acpi_pci_routing_table, buffer);
+
+ for (index = 0; index < number_of_elements; index++) {
+
+ /*
+ * Point user_prt past this current structure
+ *
+ * NOTE: On the first iteration, user_prt->Length will
+ * be zero because we cleared the return buffer earlier
+ */
+ buffer += user_prt->length;
+ user_prt = ACPI_CAST_PTR(struct acpi_pci_routing_table, buffer);
+
+ /*
+ * Fill in the Length field with the information we have at this
+ * point. The minus four is to subtract the size of the u8
+ * Source[4] member because it is added below.
+ */
+ user_prt->length = (sizeof(struct acpi_pci_routing_table) - 4);
+
+ /* Each subpackage must be of length 4 */
+
+ if ((*top_object_list)->package.count != 4) {
+ ACPI_ERROR((AE_INFO,
+ "(PRT[%u]) Need package of length 4, found length %u",
+ index, (*top_object_list)->package.count));
+ return_ACPI_STATUS(AE_AML_PACKAGE_LIMIT);
+ }
+
+ /*
+ * Dereference the subpackage.
+ * The sub_object_list will now point to an array of the four IRQ
+ * elements: [Address, Pin, Source, source_index]
+ */
+ sub_object_list = (*top_object_list)->package.elements;
+
+ /* 1) First subobject: Dereference the PRT.Address */
+
+ obj_desc = sub_object_list[0];
+ if (!obj_desc || obj_desc->common.type != ACPI_TYPE_INTEGER) {
+ ACPI_ERROR((AE_INFO,
+ "(PRT[%u].Address) Need Integer, found %s",
+ index,
+ acpi_ut_get_object_type_name(obj_desc)));
+ return_ACPI_STATUS(AE_BAD_DATA);
+ }
+
+ user_prt->address = obj_desc->integer.value;
+
+ /* 2) Second subobject: Dereference the PRT.Pin */
+
+ obj_desc = sub_object_list[1];
+ if (!obj_desc || obj_desc->common.type != ACPI_TYPE_INTEGER) {
+ ACPI_ERROR((AE_INFO,
+ "(PRT[%u].Pin) Need Integer, found %s",
+ index,
+ acpi_ut_get_object_type_name(obj_desc)));
+ return_ACPI_STATUS(AE_BAD_DATA);
+ }
+
+ user_prt->pin = (u32) obj_desc->integer.value;
+
+ /*
+ * 3) Third subobject: Dereference the PRT.source_name
+ * The name may be unresolved (slack mode), so allow a null object
+ */
+ obj_desc = sub_object_list[2];
+ if (obj_desc) {
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ if (obj_desc->reference.class !=
+ ACPI_REFCLASS_NAME) {
+ ACPI_ERROR((AE_INFO,
+ "(PRT[%u].Source) Need name, found Reference Class 0x%X",
+ index,
+ obj_desc->reference.class));
+ return_ACPI_STATUS(AE_BAD_DATA);
+ }
+
+ node = obj_desc->reference.node;
+
+ /* Use *remaining* length of the buffer as max for pathname */
+
+ path_buffer.length = output_buffer->length -
+ (u32) ((u8 *) user_prt->source -
+ (u8 *) output_buffer->pointer);
+ path_buffer.pointer = user_prt->source;
+
+ status = acpi_ns_handle_to_pathname((acpi_handle)node, &path_buffer, FALSE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* +1 to include null terminator */
+
+ user_prt->length +=
+ (u32)strlen(user_prt->source) + 1;
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ strcpy(user_prt->source,
+ obj_desc->string.pointer);
+
+ /*
+ * Add to the Length field the length of the string
+ * (add 1 for terminator)
+ */
+ user_prt->length += obj_desc->string.length + 1;
+ break;
+
+ case ACPI_TYPE_INTEGER:
+ /*
+ * If this is a number, then the Source Name is NULL, since
+ * the entire buffer was zeroed out, we can leave this alone.
+ *
+ * Add to the Length field the length of the u32 NULL
+ */
+ user_prt->length += sizeof(u32);
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "(PRT[%u].Source) Need Ref/String/Integer, found %s",
+ index,
+ acpi_ut_get_object_type_name
+ (obj_desc)));
+ return_ACPI_STATUS(AE_BAD_DATA);
+ }
+ }
+
+ /* Now align the current length */
+
+ user_prt->length =
+ (u32) ACPI_ROUND_UP_TO_64BIT(user_prt->length);
+
+ /* 4) Fourth subobject: Dereference the PRT.source_index */
+
+ obj_desc = sub_object_list[3];
+ if (!obj_desc || obj_desc->common.type != ACPI_TYPE_INTEGER) {
+ ACPI_ERROR((AE_INFO,
+ "(PRT[%u].SourceIndex) Need Integer, found %s",
+ index,
+ acpi_ut_get_object_type_name(obj_desc)));
+ return_ACPI_STATUS(AE_BAD_DATA);
+ }
+
+ user_prt->source_index = (u32) obj_desc->integer.value;
+
+ /* Point to the next union acpi_operand_object in the top level package */
+
+ top_object_list++;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "OutputBuffer %p Length %X\n",
+ output_buffer->pointer, (u32) output_buffer->length));
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_create_aml_resources
+ *
+ * PARAMETERS: resource_list - Pointer to the resource list buffer
+ * output_buffer - Where the AML buffer is returned
+ *
+ * RETURN: Status AE_OK if okay, else a valid acpi_status code.
+ * If the output_buffer is too small, the error will be
+ * AE_BUFFER_OVERFLOW and output_buffer->Length will point
+ * to the size buffer needed.
+ *
+ * DESCRIPTION: Converts a list of device resources to an AML bytestream
+ * to be used as input for the _SRS control method.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_create_aml_resources(struct acpi_buffer *resource_list,
+ struct acpi_buffer *output_buffer)
+{
+ acpi_status status;
+ acpi_size aml_size_needed = 0;
+
+ ACPI_FUNCTION_TRACE(rs_create_aml_resources);
+
+ /* Params already validated, no need to re-validate here */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "ResourceList Buffer = %p\n",
+ resource_list->pointer));
+
+ /* Get the buffer size needed for the AML byte stream */
+
+ status =
+ acpi_rs_get_aml_length(resource_list->pointer,
+ resource_list->length, &aml_size_needed);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "AmlSizeNeeded=%X, %s\n",
+ (u32)aml_size_needed, acpi_format_exception(status)));
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Validate/Allocate/Clear caller buffer */
+
+ status = acpi_ut_initialize_buffer(output_buffer, aml_size_needed);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Do the conversion */
+
+ status = acpi_rs_convert_resources_to_aml(resource_list->pointer,
+ aml_size_needed,
+ output_buffer->pointer);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "OutputBuffer %p Length %X\n",
+ output_buffer->pointer, (u32) output_buffer->length));
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c
new file mode 100644
index 000000000..611bc71c1
--- /dev/null
+++ b/drivers/acpi/acpica/rsdump.c
@@ -0,0 +1,584 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsdump - AML debugger support for resource structures.
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsdump")
+
+/*
+ * All functions in this module are used by the AML Debugger only
+ */
+/* Local prototypes */
+static void acpi_rs_out_string(const char *title, const char *value);
+
+static void acpi_rs_out_integer8(const char *title, u8 value);
+
+static void acpi_rs_out_integer16(const char *title, u16 value);
+
+static void acpi_rs_out_integer32(const char *title, u32 value);
+
+static void acpi_rs_out_integer64(const char *title, u64 value);
+
+static void acpi_rs_out_title(const char *title);
+
+static void acpi_rs_dump_byte_list(u16 length, u8 *data);
+
+static void acpi_rs_dump_word_list(u16 length, u16 *data);
+
+static void acpi_rs_dump_dword_list(u8 length, u32 *data);
+
+static void acpi_rs_dump_short_byte_list(u8 length, u8 *data);
+
+static void
+acpi_rs_dump_resource_source(struct acpi_resource_source *resource_source);
+
+static void
+acpi_rs_dump_resource_label(char *title,
+ struct acpi_resource_label *resource_label);
+
+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);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_dump_resource_list
+ *
+ * PARAMETERS: resource_list - Pointer to a resource descriptor list
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dispatches the structure to the correct dump routine.
+ *
+ ******************************************************************************/
+
+void acpi_rs_dump_resource_list(struct acpi_resource *resource_list)
+{
+ u32 count = 0;
+ u32 type;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_RESOURCES, _COMPONENT)) {
+ return;
+ }
+
+ /* Walk list and dump all resource descriptors (END_TAG terminates) */
+
+ do {
+ acpi_os_printf("\n[%02X] ", count);
+ count++;
+
+ /* Validate Type before dispatch */
+
+ type = resource_list->type;
+ if (type > ACPI_RESOURCE_TYPE_MAX) {
+ acpi_os_printf
+ ("Invalid descriptor type (%X) in resource list\n",
+ resource_list->type);
+ return;
+ } else if (!resource_list->type) {
+ ACPI_ERROR((AE_INFO, "Invalid Zero Resource Type"));
+ return;
+ }
+
+ /* Sanity check the length. It must not be zero, or we loop forever */
+
+ if (!resource_list->length) {
+ acpi_os_printf
+ ("Invalid zero length descriptor in resource list\n");
+ return;
+ }
+
+ /* Dump the resource descriptor */
+
+ if (type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+ acpi_rs_dump_descriptor(&resource_list->data,
+ acpi_gbl_dump_serial_bus_dispatch
+ [resource_list->data.
+ common_serial_bus.type]);
+ } else {
+ acpi_rs_dump_descriptor(&resource_list->data,
+ acpi_gbl_dump_resource_dispatch
+ [type]);
+ }
+
+ /* Point to the next resource structure */
+
+ resource_list = ACPI_NEXT_RESOURCE(resource_list);
+
+ /* Exit when END_TAG descriptor is reached */
+
+ } while (type != ACPI_RESOURCE_TYPE_END_TAG);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_dump_irq_list
+ *
+ * PARAMETERS: route_table - Pointer to the routing table to dump.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print IRQ routing table
+ *
+ ******************************************************************************/
+
+void acpi_rs_dump_irq_list(u8 *route_table)
+{
+ struct acpi_pci_routing_table *prt_element;
+ u8 count;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_RESOURCES, _COMPONENT)) {
+ return;
+ }
+
+ prt_element = ACPI_CAST_PTR(struct acpi_pci_routing_table, route_table);
+
+ /* Dump all table elements, Exit on zero length element */
+
+ for (count = 0; prt_element->length; count++) {
+ acpi_os_printf("\n[%02X] PCI IRQ Routing Table Package\n",
+ count);
+ acpi_rs_dump_descriptor(prt_element, acpi_rs_dump_prt);
+
+ prt_element = ACPI_ADD_PTR(struct acpi_pci_routing_table,
+ prt_element, prt_element->length);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_dump_descriptor
+ *
+ * PARAMETERS: resource - Buffer containing the resource
+ * table - Table entry to decode the resource
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump a resource descriptor based on a dump table entry.
+ *
+ ******************************************************************************/
+
+static void
+acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table)
+{
+ u8 *target = NULL;
+ u8 *previous_target;
+ const char *name;
+ u8 count;
+
+ /* First table entry must contain the table length (# of table entries) */
+
+ count = table->offset;
+
+ while (count) {
+ previous_target = target;
+ target = ACPI_ADD_PTR(u8, resource, table->offset);
+ name = table->name;
+
+ switch (table->opcode) {
+ case ACPI_RSD_TITLE:
+ /*
+ * Optional resource title
+ */
+ if (table->name) {
+ acpi_os_printf("%s Resource\n", name);
+ }
+ break;
+
+ /* Strings */
+
+ case ACPI_RSD_LITERAL:
+
+ acpi_rs_out_string(name,
+ ACPI_CAST_PTR(char, table->pointer));
+ break;
+
+ case ACPI_RSD_STRING:
+
+ acpi_rs_out_string(name, ACPI_CAST_PTR(char, target));
+ break;
+
+ /* Data items, 8/16/32/64 bit */
+
+ case ACPI_RSD_UINT8:
+
+ if (table->pointer) {
+ acpi_rs_out_string(name,
+ table->pointer[*target]);
+ } else {
+ acpi_rs_out_integer8(name, ACPI_GET8(target));
+ }
+ break;
+
+ case ACPI_RSD_UINT16:
+
+ acpi_rs_out_integer16(name, ACPI_GET16(target));
+ break;
+
+ case ACPI_RSD_UINT32:
+
+ acpi_rs_out_integer32(name, ACPI_GET32(target));
+ break;
+
+ case ACPI_RSD_UINT64:
+
+ acpi_rs_out_integer64(name, ACPI_GET64(target));
+ break;
+
+ /* Flags: 1-bit and 2-bit flags supported */
+
+ case ACPI_RSD_1BITFLAG:
+
+ acpi_rs_out_string(name,
+ table->pointer[*target & 0x01]);
+ break;
+
+ case ACPI_RSD_2BITFLAG:
+
+ acpi_rs_out_string(name,
+ table->pointer[*target & 0x03]);
+ break;
+
+ case ACPI_RSD_3BITFLAG:
+
+ acpi_rs_out_string(name,
+ table->pointer[*target & 0x07]);
+ break;
+
+ case ACPI_RSD_6BITFLAG:
+
+ acpi_rs_out_integer8(name, (ACPI_GET8(target) & 0x3F));
+ break;
+
+ case ACPI_RSD_SHORTLIST:
+ /*
+ * Short byte list (single line output) for DMA and IRQ resources
+ * Note: The list length is obtained from the previous table entry
+ */
+ if (previous_target) {
+ acpi_rs_out_title(name);
+ acpi_rs_dump_short_byte_list(*previous_target,
+ target);
+ }
+ break;
+
+ case ACPI_RSD_SHORTLISTX:
+ /*
+ * Short byte list (single line output) for GPIO vendor data
+ * Note: The list length is obtained from the previous table entry
+ */
+ if (previous_target) {
+ acpi_rs_out_title(name);
+ acpi_rs_dump_short_byte_list(*previous_target,
+ *
+ (ACPI_CAST_INDIRECT_PTR
+ (u8, target)));
+ }
+ break;
+
+ case ACPI_RSD_LONGLIST:
+ /*
+ * Long byte list for Vendor resource data
+ * Note: The list length is obtained from the previous table entry
+ */
+ if (previous_target) {
+ acpi_rs_dump_byte_list(ACPI_GET16
+ (previous_target),
+ target);
+ }
+ break;
+
+ case ACPI_RSD_DWORDLIST:
+ /*
+ * Dword list for Extended Interrupt resources
+ * Note: The list length is obtained from the previous table entry
+ */
+ if (previous_target) {
+ acpi_rs_dump_dword_list(*previous_target,
+ ACPI_CAST_PTR(u32,
+ target));
+ }
+ break;
+
+ case ACPI_RSD_WORDLIST:
+ /*
+ * Word list for GPIO Pin Table
+ * Note: The list length is obtained from the previous table entry
+ */
+ if (previous_target) {
+ acpi_rs_dump_word_list(*previous_target,
+ *(ACPI_CAST_INDIRECT_PTR
+ (u16, target)));
+ }
+ break;
+
+ case ACPI_RSD_ADDRESS:
+ /*
+ * Common flags for all Address resources
+ */
+ acpi_rs_dump_address_common(ACPI_CAST_PTR
+ (union acpi_resource_data,
+ target));
+ break;
+
+ case ACPI_RSD_SOURCE:
+ /*
+ * Optional resource_source for Address resources
+ */
+ acpi_rs_dump_resource_source(ACPI_CAST_PTR
+ (struct
+ acpi_resource_source,
+ target));
+ break;
+
+ case ACPI_RSD_LABEL:
+ /*
+ * resource_label
+ */
+ acpi_rs_dump_resource_label("Resource Label",
+ ACPI_CAST_PTR(struct
+ acpi_resource_label,
+ target));
+ break;
+
+ case ACPI_RSD_SOURCE_LABEL:
+ /*
+ * resource_source_label
+ */
+ acpi_rs_dump_resource_label("Resource Source Label",
+ ACPI_CAST_PTR(struct
+ acpi_resource_label,
+ target));
+ break;
+
+ default:
+
+ acpi_os_printf("**** Invalid table opcode [%X] ****\n",
+ table->opcode);
+ return;
+ }
+
+ table++;
+ count--;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_dump_resource_source
+ *
+ * PARAMETERS: resource_source - Pointer to a Resource Source struct
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Common routine for dumping the optional resource_source and the
+ * corresponding resource_source_index.
+ *
+ ******************************************************************************/
+
+static void
+acpi_rs_dump_resource_source(struct acpi_resource_source *resource_source)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ if (resource_source->index == 0xFF) {
+ return;
+ }
+
+ acpi_rs_out_integer8("Resource Source Index", resource_source->index);
+
+ acpi_rs_out_string("Resource Source",
+ resource_source->string_ptr ?
+ resource_source->string_ptr : "[Not Specified]");
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_dump_resource_label
+ *
+ * PARAMETERS: title - Title of the dumped resource field
+ * resource_label - Pointer to a Resource Label struct
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Common routine for dumping the resource_label
+ *
+ ******************************************************************************/
+
+static void
+acpi_rs_dump_resource_label(char *title,
+ struct acpi_resource_label *resource_label)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ acpi_rs_out_string(title,
+ resource_label->string_ptr ?
+ resource_label->string_ptr : "[Not Specified]");
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_dump_address_common
+ *
+ * PARAMETERS: resource - Pointer to an internal resource descriptor
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump the fields that are common to all Address resource
+ * descriptors
+ *
+ ******************************************************************************/
+
+static void acpi_rs_dump_address_common(union acpi_resource_data *resource)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /* Decode the type-specific flags */
+
+ switch (resource->address.resource_type) {
+ case ACPI_MEMORY_RANGE:
+
+ acpi_rs_dump_descriptor(resource, acpi_rs_dump_memory_flags);
+ break;
+
+ case ACPI_IO_RANGE:
+
+ acpi_rs_dump_descriptor(resource, acpi_rs_dump_io_flags);
+ break;
+
+ case ACPI_BUS_NUMBER_RANGE:
+
+ acpi_rs_out_string("Resource Type", "Bus Number Range");
+ break;
+
+ default:
+
+ acpi_rs_out_integer8("Resource Type",
+ (u8) resource->address.resource_type);
+ break;
+ }
+
+ /* Decode the general flags */
+
+ acpi_rs_dump_descriptor(resource, acpi_rs_dump_general_flags);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_out*
+ *
+ * PARAMETERS: title - Name of the resource field
+ * value - Value of the resource field
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Miscellaneous helper functions to consistently format the
+ * output of the resource dump routines
+ *
+ ******************************************************************************/
+
+static void acpi_rs_out_string(const char *title, const char *value)
+{
+
+ acpi_os_printf("%27s : %s", title, value);
+ if (!*value) {
+ acpi_os_printf("[NULL NAMESTRING]");
+ }
+ acpi_os_printf("\n");
+}
+
+static void acpi_rs_out_integer8(const char *title, u8 value)
+{
+ acpi_os_printf("%27s : %2.2X\n", title, value);
+}
+
+static void acpi_rs_out_integer16(const char *title, u16 value)
+{
+
+ acpi_os_printf("%27s : %4.4X\n", title, value);
+}
+
+static void acpi_rs_out_integer32(const char *title, u32 value)
+{
+
+ acpi_os_printf("%27s : %8.8X\n", title, value);
+}
+
+static void acpi_rs_out_integer64(const char *title, u64 value)
+{
+
+ acpi_os_printf("%27s : %8.8X%8.8X\n", title, ACPI_FORMAT_UINT64(value));
+}
+
+static void acpi_rs_out_title(const char *title)
+{
+
+ acpi_os_printf("%27s : ", title);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_dump*List
+ *
+ * PARAMETERS: length - Number of elements in the list
+ * data - Start of the list
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Miscellaneous functions to dump lists of raw data
+ *
+ ******************************************************************************/
+
+static void acpi_rs_dump_byte_list(u16 length, u8 * data)
+{
+ u16 i;
+
+ for (i = 0; i < length; i++) {
+ acpi_os_printf("%25s%2.2X : %2.2X\n", "Byte", i, data[i]);
+ }
+}
+
+static void acpi_rs_dump_short_byte_list(u8 length, u8 * data)
+{
+ u8 i;
+
+ for (i = 0; i < length; i++) {
+ acpi_os_printf("%X ", data[i]);
+ }
+
+ acpi_os_printf("\n");
+}
+
+static void acpi_rs_dump_dword_list(u8 length, u32 * data)
+{
+ u8 i;
+
+ for (i = 0; i < length; i++) {
+ acpi_os_printf("%25s%2.2X : %8.8X\n", "Dword", i, data[i]);
+ }
+}
+
+static void acpi_rs_dump_word_list(u16 length, u16 *data)
+{
+ u16 i;
+
+ for (i = 0; i < length; i++) {
+ acpi_os_printf("%25s%2.2X : %4.4X\n", "Word", i, data[i]);
+ }
+}
diff --git a/drivers/acpi/acpica/rsdumpinfo.c b/drivers/acpi/acpica/rsdumpinfo.c
new file mode 100644
index 000000000..b8b374490
--- /dev/null
+++ b/drivers/acpi/acpica/rsdumpinfo.c
@@ -0,0 +1,560 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsdumpinfo - Tables used to display resource descriptors.
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsdumpinfo")
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUGGER)
+#define ACPI_RSD_OFFSET(f) (u8) ACPI_OFFSET (union acpi_resource_data,f)
+#define ACPI_PRT_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_pci_routing_table,f)
+#define ACPI_RSD_TABLE_SIZE(name) (sizeof(name) / sizeof (struct acpi_rsdump_info))
+/*******************************************************************************
+ *
+ * Resource Descriptor info tables
+ *
+ * Note: The first table entry must be a Title or Literal and must contain
+ * the table length (number of table entries)
+ *
+ ******************************************************************************/
+struct acpi_rsdump_info acpi_rs_dump_irq[7] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_irq), "IRQ", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(irq.descriptor_length),
+ "Descriptor Length", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(irq.triggering), "Triggering",
+ acpi_gbl_he_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(irq.polarity), "Polarity",
+ acpi_gbl_ll_decode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(irq.shareable), "Sharing",
+ acpi_gbl_shr_decode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(irq.interrupt_count),
+ "Interrupt Count", NULL},
+ {ACPI_RSD_SHORTLIST, ACPI_RSD_OFFSET(irq.interrupts[0]),
+ "Interrupt List", NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_dma[6] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_dma), "DMA", NULL},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(dma.type), "Speed",
+ acpi_gbl_typ_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(dma.bus_master), "Mastering",
+ acpi_gbl_bm_decode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(dma.transfer), "Transfer Type",
+ acpi_gbl_siz_decode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(dma.channel_count), "Channel Count",
+ NULL},
+ {ACPI_RSD_SHORTLIST, ACPI_RSD_OFFSET(dma.channels[0]), "Channel List",
+ NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_start_dpf[4] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_start_dpf),
+ "Start-Dependent-Functions", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(start_dpf.descriptor_length),
+ "Descriptor Length", NULL},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(start_dpf.compatibility_priority),
+ "Compatibility Priority", acpi_gbl_config_decode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(start_dpf.performance_robustness),
+ "Performance/Robustness", acpi_gbl_config_decode}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_end_dpf[1] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_end_dpf),
+ "End-Dependent-Functions", NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_io[6] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_io), "I/O", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(io.io_decode), "Address Decoding",
+ acpi_gbl_io_decode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(io.minimum), "Address Minimum", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(io.maximum), "Address Maximum", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(io.alignment), "Alignment", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(io.address_length), "Address Length",
+ NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_fixed_io[3] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_fixed_io),
+ "Fixed I/O", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(fixed_io.address), "Address", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(fixed_io.address_length),
+ "Address Length", NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_vendor[3] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_vendor),
+ "Vendor Specific", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(vendor.byte_length), "Length", NULL},
+ {ACPI_RSD_LONGLIST, ACPI_RSD_OFFSET(vendor.byte_data[0]), "Vendor Data",
+ NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_end_tag[1] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_end_tag), "EndTag",
+ NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_memory24[6] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_memory24),
+ "24-Bit Memory Range", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(memory24.write_protect),
+ "Write Protect", acpi_gbl_rw_decode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(memory24.minimum), "Address Minimum",
+ NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(memory24.maximum), "Address Maximum",
+ NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(memory24.alignment), "Alignment",
+ NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(memory24.address_length),
+ "Address Length", NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_memory32[6] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_memory32),
+ "32-Bit Memory Range", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(memory32.write_protect),
+ "Write Protect", acpi_gbl_rw_decode},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(memory32.minimum), "Address Minimum",
+ NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(memory32.maximum), "Address Maximum",
+ NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(memory32.alignment), "Alignment",
+ NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(memory32.address_length),
+ "Address Length", NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_fixed_memory32[4] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_fixed_memory32),
+ "32-Bit Fixed Memory Range", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(fixed_memory32.write_protect),
+ "Write Protect", acpi_gbl_rw_decode},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(fixed_memory32.address), "Address",
+ NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(fixed_memory32.address_length),
+ "Address Length", NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_address16[8] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_address16),
+ "16-Bit WORD Address Space", NULL},
+ {ACPI_RSD_ADDRESS, 0, NULL, NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.granularity),
+ "Granularity", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.minimum),
+ "Address Minimum", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.maximum),
+ "Address Maximum", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.translation_offset),
+ "Translation Offset", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.address_length),
+ "Address Length", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(address16.resource_source), NULL, NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_address32[8] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_address32),
+ "32-Bit DWORD Address Space", NULL},
+ {ACPI_RSD_ADDRESS, 0, NULL, NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.granularity),
+ "Granularity", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.minimum),
+ "Address Minimum", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.maximum),
+ "Address Maximum", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.translation_offset),
+ "Translation Offset", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.address_length),
+ "Address Length", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(address32.resource_source), NULL, NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_address64[8] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_address64),
+ "64-Bit QWORD Address Space", NULL},
+ {ACPI_RSD_ADDRESS, 0, NULL, NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.granularity),
+ "Granularity", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.minimum),
+ "Address Minimum", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.maximum),
+ "Address Maximum", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.translation_offset),
+ "Translation Offset", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.address_length),
+ "Address Length", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(address64.resource_source), NULL, NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_ext_address64[8] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_ext_address64),
+ "64-Bit Extended Address Space", NULL},
+ {ACPI_RSD_ADDRESS, 0, NULL, NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.granularity),
+ "Granularity", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.minimum),
+ "Address Minimum", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.maximum),
+ "Address Maximum", NULL},
+ {ACPI_RSD_UINT64,
+ ACPI_RSD_OFFSET(ext_address64.address.translation_offset),
+ "Translation Offset", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.address_length),
+ "Address Length", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.type_specific),
+ "Type-Specific Attribute", NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_ext_irq[8] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_ext_irq),
+ "Extended IRQ", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(extended_irq.producer_consumer),
+ "Type", acpi_gbl_consume_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(extended_irq.triggering),
+ "Triggering", acpi_gbl_he_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(extended_irq.polarity), "Polarity",
+ acpi_gbl_ll_decode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(extended_irq.shareable), "Sharing",
+ acpi_gbl_shr_decode},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(extended_irq.resource_source), NULL,
+ NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(extended_irq.interrupt_count),
+ "Interrupt Count", NULL},
+ {ACPI_RSD_DWORDLIST, ACPI_RSD_OFFSET(extended_irq.interrupts[0]),
+ "Interrupt List", NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_generic_reg[6] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_generic_reg),
+ "Generic Register", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(generic_reg.space_id), "Space ID",
+ NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(generic_reg.bit_width), "Bit Width",
+ NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(generic_reg.bit_offset), "Bit Offset",
+ NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(generic_reg.access_size),
+ "Access Size", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(generic_reg.address), "Address", NULL}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_gpio[16] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_gpio), "GPIO", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(gpio.revision_id), "RevisionId", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(gpio.connection_type),
+ "ConnectionType", acpi_gbl_ct_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(gpio.producer_consumer),
+ "ProducerConsumer", acpi_gbl_consume_decode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(gpio.pin_config), "PinConfig",
+ acpi_gbl_ppc_decode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(gpio.shareable), "Sharing",
+ acpi_gbl_shr_decode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(gpio.io_restriction),
+ "IoRestriction", acpi_gbl_ior_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(gpio.triggering), "Triggering",
+ acpi_gbl_he_decode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(gpio.polarity), "Polarity",
+ acpi_gbl_ll_decode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.drive_strength), "DriveStrength",
+ NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.debounce_timeout),
+ "DebounceTimeout", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(gpio.resource_source),
+ "ResourceSource", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.pin_table_length),
+ "PinTableLength", NULL},
+ {ACPI_RSD_WORDLIST, ACPI_RSD_OFFSET(gpio.pin_table), "PinTable", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.vendor_length), "VendorLength",
+ NULL},
+ {ACPI_RSD_SHORTLISTX, ACPI_RSD_OFFSET(gpio.vendor_data), "VendorData",
+ NULL},
+};
+
+struct acpi_rsdump_info acpi_rs_dump_pin_function[10] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_pin_function),
+ "PinFunction", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(pin_function.revision_id),
+ "RevisionId", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(pin_function.pin_config), "PinConfig",
+ acpi_gbl_ppc_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(pin_function.shareable), "Sharing",
+ acpi_gbl_shr_decode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(pin_function.function_number),
+ "FunctionNumber", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(pin_function.resource_source),
+ "ResourceSource", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(pin_function.pin_table_length),
+ "PinTableLength", NULL},
+ {ACPI_RSD_WORDLIST, ACPI_RSD_OFFSET(pin_function.pin_table), "PinTable",
+ NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(pin_function.vendor_length),
+ "VendorLength", NULL},
+ {ACPI_RSD_SHORTLISTX, ACPI_RSD_OFFSET(pin_function.vendor_data),
+ "VendorData", 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},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(pin_config.revision_id), "RevisionId",
+ NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(pin_config.producer_consumer),
+ "ProducerConsumer", acpi_gbl_consume_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(pin_config.shareable), "Sharing",
+ acpi_gbl_shr_decode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(pin_config.pin_config_type),
+ "PinConfigType", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(pin_config.pin_config_value),
+ "PinConfigValue", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(pin_config.resource_source),
+ "ResourceSource", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(pin_config.pin_table_length),
+ "PinTableLength", NULL},
+ {ACPI_RSD_WORDLIST, ACPI_RSD_OFFSET(pin_config.pin_table), "PinTable",
+ NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(pin_config.vendor_length),
+ "VendorLength", NULL},
+ {ACPI_RSD_SHORTLISTX, ACPI_RSD_OFFSET(pin_config.vendor_data),
+ "VendorData", NULL},
+};
+
+struct acpi_rsdump_info acpi_rs_dump_pin_group[8] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_pin_group),
+ "PinGroup", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(pin_group.revision_id), "RevisionId",
+ NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(pin_group.producer_consumer),
+ "ProducerConsumer", acpi_gbl_consume_decode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(pin_group.pin_table_length),
+ "PinTableLength", NULL},
+ {ACPI_RSD_WORDLIST, ACPI_RSD_OFFSET(pin_group.pin_table), "PinTable",
+ NULL},
+ {ACPI_RSD_LABEL, ACPI_RSD_OFFSET(pin_group.resource_label),
+ "ResourceLabel", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(pin_group.vendor_length),
+ "VendorLength", NULL},
+ {ACPI_RSD_SHORTLISTX, ACPI_RSD_OFFSET(pin_group.vendor_data),
+ "VendorData", NULL},
+};
+
+struct acpi_rsdump_info acpi_rs_dump_pin_group_function[9] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_pin_group_function),
+ "PinGroupFunction", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(pin_group_function.revision_id),
+ "RevisionId", NULL},
+ {ACPI_RSD_1BITFLAG,
+ ACPI_RSD_OFFSET(pin_group_function.producer_consumer),
+ "ProducerConsumer", acpi_gbl_consume_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(pin_group_function.shareable),
+ "Sharing", acpi_gbl_shr_decode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(pin_group_function.function_number),
+ "FunctionNumber", NULL},
+ {ACPI_RSD_SOURCE_LABEL,
+ ACPI_RSD_OFFSET(pin_group_function.resource_source_label),
+ "ResourceSourceLabel", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(pin_group_function.resource_source),
+ "ResourceSource", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(pin_group_function.vendor_length),
+ "VendorLength", NULL},
+ {ACPI_RSD_SHORTLISTX, ACPI_RSD_OFFSET(pin_group_function.vendor_data),
+ "VendorData", NULL},
+};
+
+struct acpi_rsdump_info acpi_rs_dump_pin_group_config[10] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_pin_group_config),
+ "PinGroupConfig", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(pin_group_config.revision_id),
+ "RevisionId", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(pin_group_config.producer_consumer),
+ "ProducerConsumer", acpi_gbl_consume_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(pin_group_config.shareable),
+ "Sharing", acpi_gbl_shr_decode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(pin_group_config.pin_config_type),
+ "PinConfigType", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(pin_group_config.pin_config_value),
+ "PinConfigValue", NULL},
+ {ACPI_RSD_SOURCE_LABEL,
+ ACPI_RSD_OFFSET(pin_group_config.resource_source_label),
+ "ResourceSourceLabel", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(pin_group_config.resource_source),
+ "ResourceSource", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(pin_group_config.vendor_length),
+ "VendorLength", NULL},
+ {ACPI_RSD_SHORTLISTX, ACPI_RSD_OFFSET(pin_group_config.vendor_data),
+ "VendorData", NULL},
+};
+
+struct acpi_rsdump_info acpi_rs_dump_fixed_dma[4] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_fixed_dma),
+ "FixedDma", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(fixed_dma.request_lines),
+ "RequestLines", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(fixed_dma.channels), "Channels",
+ NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(fixed_dma.width), "TransferWidth",
+ acpi_gbl_dts_decode},
+};
+
+#define ACPI_RS_DUMP_COMMON_SERIAL_BUS \
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (common_serial_bus.revision_id), "RevisionId", NULL}, \
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (common_serial_bus.type), "Type", acpi_gbl_sbt_decode}, \
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (common_serial_bus.producer_consumer), "ProducerConsumer", acpi_gbl_consume_decode}, \
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (common_serial_bus.slave_mode), "SlaveMode", acpi_gbl_sm_decode}, \
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (common_serial_bus.connection_sharing),"ConnectionSharing", acpi_gbl_shr_decode}, \
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (common_serial_bus.type_revision_id), "TypeRevisionId", NULL}, \
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (common_serial_bus.type_data_length), "TypeDataLength", NULL}, \
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (common_serial_bus.resource_source), "ResourceSource", NULL}, \
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (common_serial_bus.vendor_length), "VendorLength", NULL}, \
+ {ACPI_RSD_SHORTLISTX,ACPI_RSD_OFFSET (common_serial_bus.vendor_data), "VendorData", NULL},
+
+struct acpi_rsdump_info acpi_rs_dump_common_serial_bus[11] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_common_serial_bus),
+ "Common Serial Bus", NULL},
+ ACPI_RS_DUMP_COMMON_SERIAL_BUS
+};
+
+struct acpi_rsdump_info acpi_rs_dump_csi2_serial_bus[11] = {
+ { ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_csi2_serial_bus),
+ "Camera Serial Bus", NULL },
+ { ACPI_RSD_UINT8, ACPI_RSD_OFFSET(csi2_serial_bus.revision_id),
+ "RevisionId", NULL },
+ { ACPI_RSD_UINT8, ACPI_RSD_OFFSET(csi2_serial_bus.type), "Type",
+ acpi_gbl_sbt_decode },
+ { ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(csi2_serial_bus.producer_consumer),
+ "ProducerConsumer", acpi_gbl_consume_decode },
+ { ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(csi2_serial_bus.slave_mode),
+ "SlaveMode", acpi_gbl_sm_decode },
+ { ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(csi2_serial_bus.phy_type),
+ "PhyType", acpi_gbl_phy_decode },
+ { ACPI_RSD_6BITFLAG,
+ ACPI_RSD_OFFSET(csi2_serial_bus.local_port_instance),
+ "LocalPortInstance", NULL },
+ { ACPI_RSD_UINT8, ACPI_RSD_OFFSET(csi2_serial_bus.type_revision_id),
+ "TypeRevisionId", NULL },
+ { ACPI_RSD_UINT16, ACPI_RSD_OFFSET(csi2_serial_bus.vendor_length),
+ "VendorLength", NULL },
+ { ACPI_RSD_SHORTLISTX, ACPI_RSD_OFFSET(csi2_serial_bus.vendor_data),
+ "VendorData", NULL },
+ { ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(csi2_serial_bus.resource_source),
+ "ResourceSource", NULL },
+};
+
+struct acpi_rsdump_info acpi_rs_dump_i2c_serial_bus[14] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_i2c_serial_bus),
+ "I2C Serial Bus", NULL},
+ ACPI_RS_DUMP_COMMON_SERIAL_BUS {ACPI_RSD_1BITFLAG,
+ ACPI_RSD_OFFSET(i2c_serial_bus.
+ access_mode),
+ "AccessMode", acpi_gbl_am_decode},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(i2c_serial_bus.connection_speed),
+ "ConnectionSpeed", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(i2c_serial_bus.slave_address),
+ "SlaveAddress", NULL},
+};
+
+struct acpi_rsdump_info acpi_rs_dump_spi_serial_bus[18] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_spi_serial_bus),
+ "Spi Serial Bus", NULL},
+ ACPI_RS_DUMP_COMMON_SERIAL_BUS {ACPI_RSD_1BITFLAG,
+ ACPI_RSD_OFFSET(spi_serial_bus.
+ wire_mode), "WireMode",
+ acpi_gbl_wm_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(spi_serial_bus.device_polarity),
+ "DevicePolarity", acpi_gbl_dp_decode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(spi_serial_bus.data_bit_length),
+ "DataBitLength", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(spi_serial_bus.clock_phase),
+ "ClockPhase", acpi_gbl_cph_decode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(spi_serial_bus.clock_polarity),
+ "ClockPolarity", acpi_gbl_cpo_decode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(spi_serial_bus.device_selection),
+ "DeviceSelection", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(spi_serial_bus.connection_speed),
+ "ConnectionSpeed", NULL},
+};
+
+struct acpi_rsdump_info acpi_rs_dump_uart_serial_bus[20] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_uart_serial_bus),
+ "Uart Serial Bus", NULL},
+ ACPI_RS_DUMP_COMMON_SERIAL_BUS {ACPI_RSD_2BITFLAG,
+ ACPI_RSD_OFFSET(uart_serial_bus.
+ flow_control),
+ "FlowControl", acpi_gbl_fc_decode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(uart_serial_bus.stop_bits),
+ "StopBits", acpi_gbl_sb_decode},
+ {ACPI_RSD_3BITFLAG, ACPI_RSD_OFFSET(uart_serial_bus.data_bits),
+ "DataBits", acpi_gbl_bpb_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(uart_serial_bus.endian), "Endian",
+ acpi_gbl_ed_decode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(uart_serial_bus.parity), "Parity",
+ acpi_gbl_pt_decode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(uart_serial_bus.lines_enabled),
+ "LinesEnabled", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(uart_serial_bus.rx_fifo_size),
+ "RxFifoSize", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(uart_serial_bus.tx_fifo_size),
+ "TxFifoSize", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(uart_serial_bus.default_baud_rate),
+ "ConnectionSpeed", NULL},
+};
+
+/*
+ * Tables used for common address descriptor flag fields
+ */
+struct acpi_rsdump_info acpi_rs_dump_general_flags[5] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_general_flags), NULL,
+ NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.producer_consumer),
+ "Consumer/Producer", acpi_gbl_consume_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.decode), "Address Decode",
+ acpi_gbl_dec_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.min_address_fixed),
+ "Min Relocatability", acpi_gbl_min_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.max_address_fixed),
+ "Max Relocatability", acpi_gbl_max_decode}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_memory_flags[5] = {
+ {ACPI_RSD_LITERAL, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_memory_flags),
+ "Resource Type", (void *)"Memory Range"},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.info.mem.write_protect),
+ "Write Protect", acpi_gbl_rw_decode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(address.info.mem.caching),
+ "Caching", acpi_gbl_mem_decode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(address.info.mem.range_type),
+ "Range Type", acpi_gbl_mtp_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.info.mem.translation),
+ "Translation", acpi_gbl_ttp_decode}
+};
+
+struct acpi_rsdump_info acpi_rs_dump_io_flags[4] = {
+ {ACPI_RSD_LITERAL, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_io_flags),
+ "Resource Type", (void *)"I/O Range"},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(address.info.io.range_type),
+ "Range Type", acpi_gbl_rng_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.info.io.translation),
+ "Translation", acpi_gbl_ttp_decode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.info.io.translation_type),
+ "Translation Type", acpi_gbl_trs_decode}
+};
+
+/*
+ * Table used to dump _PRT contents
+ */
+struct acpi_rsdump_info acpi_rs_dump_prt[5] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_prt), NULL, NULL},
+ {ACPI_RSD_UINT64, ACPI_PRT_OFFSET(address), "Address", NULL},
+ {ACPI_RSD_UINT32, ACPI_PRT_OFFSET(pin), "Pin", NULL},
+ {ACPI_RSD_STRING, ACPI_PRT_OFFSET(source[0]), "Source", NULL},
+ {ACPI_RSD_UINT32, ACPI_PRT_OFFSET(source_index), "Source Index", NULL}
+};
+
+#endif
diff --git a/drivers/acpi/acpica/rsinfo.c b/drivers/acpi/acpica/rsinfo.c
new file mode 100644
index 000000000..eaeb7ab58
--- /dev/null
+++ b/drivers/acpi/acpica/rsinfo.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsinfo - Dispatch and Info tables
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsinfo")
+
+/*
+ * Resource dispatch and information tables. Any new resource types (either
+ * Large or Small) must be reflected in each of these tables, so they are here
+ * in one place.
+ *
+ * The tables for Large descriptors are indexed by bits 6:0 of the AML
+ * descriptor type byte. The tables for Small descriptors are indexed by
+ * bits 6:3 of the descriptor byte. The tables for internal resource
+ * descriptors are indexed by the acpi_resource_type field.
+ */
+/* Dispatch table for resource-to-AML (Set Resource) conversion functions */
+struct acpi_rsconvert_info *acpi_gbl_set_resource_dispatch[] = {
+ acpi_rs_set_irq, /* 0x00, ACPI_RESOURCE_TYPE_IRQ */
+ acpi_rs_convert_dma, /* 0x01, ACPI_RESOURCE_TYPE_DMA */
+ acpi_rs_set_start_dpf, /* 0x02, ACPI_RESOURCE_TYPE_START_DEPENDENT */
+ acpi_rs_convert_end_dpf, /* 0x03, ACPI_RESOURCE_TYPE_END_DEPENDENT */
+ acpi_rs_convert_io, /* 0x04, ACPI_RESOURCE_TYPE_IO */
+ acpi_rs_convert_fixed_io, /* 0x05, ACPI_RESOURCE_TYPE_FIXED_IO */
+ acpi_rs_set_vendor, /* 0x06, ACPI_RESOURCE_TYPE_VENDOR */
+ acpi_rs_convert_end_tag, /* 0x07, ACPI_RESOURCE_TYPE_END_TAG */
+ acpi_rs_convert_memory24, /* 0x08, ACPI_RESOURCE_TYPE_MEMORY24 */
+ acpi_rs_convert_memory32, /* 0x09, ACPI_RESOURCE_TYPE_MEMORY32 */
+ acpi_rs_convert_fixed_memory32, /* 0x0A, ACPI_RESOURCE_TYPE_FIXED_MEMORY32 */
+ acpi_rs_convert_address16, /* 0x0B, ACPI_RESOURCE_TYPE_ADDRESS16 */
+ acpi_rs_convert_address32, /* 0x0C, ACPI_RESOURCE_TYPE_ADDRESS32 */
+ acpi_rs_convert_address64, /* 0x0D, ACPI_RESOURCE_TYPE_ADDRESS64 */
+ acpi_rs_convert_ext_address64, /* 0x0E, ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */
+ acpi_rs_convert_ext_irq, /* 0x0F, ACPI_RESOURCE_TYPE_EXTENDED_IRQ */
+ acpi_rs_convert_generic_reg, /* 0x10, ACPI_RESOURCE_TYPE_GENERIC_REGISTER */
+ acpi_rs_convert_gpio, /* 0x11, ACPI_RESOURCE_TYPE_GPIO */
+ acpi_rs_convert_fixed_dma, /* 0x12, ACPI_RESOURCE_TYPE_FIXED_DMA */
+ NULL, /* 0x13, ACPI_RESOURCE_TYPE_SERIAL_BUS - Use subtype table below */
+ acpi_rs_convert_pin_function, /* 0x14, ACPI_RESOURCE_TYPE_PIN_FUNCTION */
+ acpi_rs_convert_pin_config, /* 0x15, ACPI_RESOURCE_TYPE_PIN_CONFIG */
+ 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 */
+};
+
+/* Dispatch tables for AML-to-resource (Get Resource) conversion functions */
+
+struct acpi_rsconvert_info *acpi_gbl_get_resource_dispatch[] = {
+ /* Small descriptors */
+
+ NULL, /* 0x00, Reserved */
+ NULL, /* 0x01, Reserved */
+ NULL, /* 0x02, Reserved */
+ NULL, /* 0x03, Reserved */
+ acpi_rs_get_irq, /* 0x04, ACPI_RESOURCE_NAME_IRQ */
+ acpi_rs_convert_dma, /* 0x05, ACPI_RESOURCE_NAME_DMA */
+ acpi_rs_get_start_dpf, /* 0x06, ACPI_RESOURCE_NAME_START_DEPENDENT */
+ acpi_rs_convert_end_dpf, /* 0x07, ACPI_RESOURCE_NAME_END_DEPENDENT */
+ acpi_rs_convert_io, /* 0x08, ACPI_RESOURCE_NAME_IO */
+ acpi_rs_convert_fixed_io, /* 0x09, ACPI_RESOURCE_NAME_FIXED_IO */
+ acpi_rs_convert_fixed_dma, /* 0x0A, ACPI_RESOURCE_NAME_FIXED_DMA */
+ NULL, /* 0x0B, Reserved */
+ NULL, /* 0x0C, Reserved */
+ NULL, /* 0x0D, Reserved */
+ acpi_rs_get_vendor_small, /* 0x0E, ACPI_RESOURCE_NAME_VENDOR_SMALL */
+ acpi_rs_convert_end_tag, /* 0x0F, ACPI_RESOURCE_NAME_END_TAG */
+
+ /* Large descriptors */
+
+ NULL, /* 0x00, Reserved */
+ acpi_rs_convert_memory24, /* 0x01, ACPI_RESOURCE_NAME_MEMORY24 */
+ acpi_rs_convert_generic_reg, /* 0x02, ACPI_RESOURCE_NAME_GENERIC_REGISTER */
+ NULL, /* 0x03, Reserved */
+ acpi_rs_get_vendor_large, /* 0x04, ACPI_RESOURCE_NAME_VENDOR_LARGE */
+ acpi_rs_convert_memory32, /* 0x05, ACPI_RESOURCE_NAME_MEMORY32 */
+ acpi_rs_convert_fixed_memory32, /* 0x06, ACPI_RESOURCE_NAME_FIXED_MEMORY32 */
+ acpi_rs_convert_address32, /* 0x07, ACPI_RESOURCE_NAME_ADDRESS32 */
+ acpi_rs_convert_address16, /* 0x08, ACPI_RESOURCE_NAME_ADDRESS16 */
+ acpi_rs_convert_ext_irq, /* 0x09, ACPI_RESOURCE_NAME_EXTENDED_IRQ */
+ acpi_rs_convert_address64, /* 0x0A, ACPI_RESOURCE_NAME_ADDRESS64 */
+ acpi_rs_convert_ext_address64, /* 0x0B, ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 */
+ acpi_rs_convert_gpio, /* 0x0C, ACPI_RESOURCE_NAME_GPIO */
+ acpi_rs_convert_pin_function, /* 0x0D, ACPI_RESOURCE_NAME_PIN_FUNCTION */
+ NULL, /* 0x0E, ACPI_RESOURCE_NAME_SERIAL_BUS - Use subtype table below */
+ acpi_rs_convert_pin_config, /* 0x0F, ACPI_RESOURCE_NAME_PIN_CONFIG */
+ 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 */
+};
+
+/* Subtype table for serial_bus -- I2C, SPI, UART, and CSI2 */
+
+struct acpi_rsconvert_info *acpi_gbl_convert_resource_serial_bus_dispatch[] = {
+ NULL,
+ acpi_rs_convert_i2c_serial_bus,
+ acpi_rs_convert_spi_serial_bus,
+ acpi_rs_convert_uart_serial_bus,
+ acpi_rs_convert_csi2_serial_bus
+};
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUGGER)
+
+/* Dispatch table for resource dump functions */
+
+struct acpi_rsdump_info *acpi_gbl_dump_resource_dispatch[] = {
+ acpi_rs_dump_irq, /* ACPI_RESOURCE_TYPE_IRQ */
+ acpi_rs_dump_dma, /* ACPI_RESOURCE_TYPE_DMA */
+ acpi_rs_dump_start_dpf, /* ACPI_RESOURCE_TYPE_START_DEPENDENT */
+ acpi_rs_dump_end_dpf, /* ACPI_RESOURCE_TYPE_END_DEPENDENT */
+ acpi_rs_dump_io, /* ACPI_RESOURCE_TYPE_IO */
+ acpi_rs_dump_fixed_io, /* ACPI_RESOURCE_TYPE_FIXED_IO */
+ acpi_rs_dump_vendor, /* ACPI_RESOURCE_TYPE_VENDOR */
+ acpi_rs_dump_end_tag, /* ACPI_RESOURCE_TYPE_END_TAG */
+ acpi_rs_dump_memory24, /* ACPI_RESOURCE_TYPE_MEMORY24 */
+ acpi_rs_dump_memory32, /* ACPI_RESOURCE_TYPE_MEMORY32 */
+ acpi_rs_dump_fixed_memory32, /* ACPI_RESOURCE_TYPE_FIXED_MEMORY32 */
+ acpi_rs_dump_address16, /* ACPI_RESOURCE_TYPE_ADDRESS16 */
+ acpi_rs_dump_address32, /* ACPI_RESOURCE_TYPE_ADDRESS32 */
+ acpi_rs_dump_address64, /* ACPI_RESOURCE_TYPE_ADDRESS64 */
+ acpi_rs_dump_ext_address64, /* ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */
+ acpi_rs_dump_ext_irq, /* ACPI_RESOURCE_TYPE_EXTENDED_IRQ */
+ acpi_rs_dump_generic_reg, /* ACPI_RESOURCE_TYPE_GENERIC_REGISTER */
+ acpi_rs_dump_gpio, /* ACPI_RESOURCE_TYPE_GPIO */
+ acpi_rs_dump_fixed_dma, /* ACPI_RESOURCE_TYPE_FIXED_DMA */
+ NULL, /* ACPI_RESOURCE_TYPE_SERIAL_BUS */
+ acpi_rs_dump_pin_function, /* ACPI_RESOURCE_TYPE_PIN_FUNCTION */
+ acpi_rs_dump_pin_config, /* ACPI_RESOURCE_TYPE_PIN_CONFIG */
+ 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 */
+};
+
+struct acpi_rsdump_info *acpi_gbl_dump_serial_bus_dispatch[] = {
+ NULL,
+ acpi_rs_dump_i2c_serial_bus, /* AML_RESOURCE_I2C_BUS_TYPE */
+ acpi_rs_dump_spi_serial_bus, /* AML_RESOURCE_SPI_BUS_TYPE */
+ acpi_rs_dump_uart_serial_bus, /* AML_RESOURCE_UART_BUS_TYPE */
+ acpi_rs_dump_csi2_serial_bus, /* AML_RESOURCE_CSI2_BUS_TYPE */
+};
+#endif
+
+/*
+ * Base sizes for external AML resource descriptors, indexed by internal type.
+ * Includes size of the descriptor header (1 byte for small descriptors,
+ * 3 bytes for large descriptors)
+ */
+const u8 acpi_gbl_aml_resource_sizes[] = {
+ sizeof(struct aml_resource_irq), /* ACPI_RESOURCE_TYPE_IRQ (optional Byte 3 always created) */
+ sizeof(struct aml_resource_dma), /* ACPI_RESOURCE_TYPE_DMA */
+ sizeof(struct aml_resource_start_dependent), /* ACPI_RESOURCE_TYPE_START_DEPENDENT (optional Byte 1 always created) */
+ sizeof(struct aml_resource_end_dependent), /* ACPI_RESOURCE_TYPE_END_DEPENDENT */
+ sizeof(struct aml_resource_io), /* ACPI_RESOURCE_TYPE_IO */
+ sizeof(struct aml_resource_fixed_io), /* ACPI_RESOURCE_TYPE_FIXED_IO */
+ sizeof(struct aml_resource_vendor_small), /* ACPI_RESOURCE_TYPE_VENDOR */
+ sizeof(struct aml_resource_end_tag), /* ACPI_RESOURCE_TYPE_END_TAG */
+ sizeof(struct aml_resource_memory24), /* ACPI_RESOURCE_TYPE_MEMORY24 */
+ sizeof(struct aml_resource_memory32), /* ACPI_RESOURCE_TYPE_MEMORY32 */
+ sizeof(struct aml_resource_fixed_memory32), /* ACPI_RESOURCE_TYPE_FIXED_MEMORY32 */
+ sizeof(struct aml_resource_address16), /* ACPI_RESOURCE_TYPE_ADDRESS16 */
+ sizeof(struct aml_resource_address32), /* ACPI_RESOURCE_TYPE_ADDRESS32 */
+ sizeof(struct aml_resource_address64), /* ACPI_RESOURCE_TYPE_ADDRESS64 */
+ sizeof(struct aml_resource_extended_address64), /*ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */
+ sizeof(struct aml_resource_extended_irq), /* ACPI_RESOURCE_TYPE_EXTENDED_IRQ */
+ sizeof(struct aml_resource_generic_register), /* ACPI_RESOURCE_TYPE_GENERIC_REGISTER */
+ sizeof(struct aml_resource_gpio), /* ACPI_RESOURCE_TYPE_GPIO */
+ sizeof(struct aml_resource_fixed_dma), /* ACPI_RESOURCE_TYPE_FIXED_DMA */
+ sizeof(struct aml_resource_common_serialbus), /* ACPI_RESOURCE_TYPE_SERIAL_BUS */
+ sizeof(struct aml_resource_pin_function), /* ACPI_RESOURCE_TYPE_PIN_FUNCTION */
+ sizeof(struct aml_resource_pin_config), /* ACPI_RESOURCE_TYPE_PIN_CONFIG */
+ 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 */
+};
+
+const u8 acpi_gbl_resource_struct_sizes[] = {
+ /* Small descriptors */
+
+ 0,
+ 0,
+ 0,
+ 0,
+ ACPI_RS_SIZE(struct acpi_resource_irq),
+ ACPI_RS_SIZE(struct acpi_resource_dma),
+ ACPI_RS_SIZE(struct acpi_resource_start_dependent),
+ ACPI_RS_SIZE_MIN,
+ ACPI_RS_SIZE(struct acpi_resource_io),
+ ACPI_RS_SIZE(struct acpi_resource_fixed_io),
+ ACPI_RS_SIZE(struct acpi_resource_fixed_dma),
+ 0,
+ 0,
+ 0,
+ ACPI_RS_SIZE(struct acpi_resource_vendor),
+ ACPI_RS_SIZE_MIN,
+
+ /* Large descriptors */
+
+ 0,
+ ACPI_RS_SIZE(struct acpi_resource_memory24),
+ ACPI_RS_SIZE(struct acpi_resource_generic_register),
+ 0,
+ ACPI_RS_SIZE(struct acpi_resource_vendor),
+ ACPI_RS_SIZE(struct acpi_resource_memory32),
+ ACPI_RS_SIZE(struct acpi_resource_fixed_memory32),
+ ACPI_RS_SIZE(struct acpi_resource_address32),
+ ACPI_RS_SIZE(struct acpi_resource_address16),
+ ACPI_RS_SIZE(struct acpi_resource_extended_irq),
+ ACPI_RS_SIZE(struct acpi_resource_address64),
+ ACPI_RS_SIZE(struct acpi_resource_extended_address64),
+ ACPI_RS_SIZE(struct acpi_resource_gpio),
+ ACPI_RS_SIZE(struct acpi_resource_pin_function),
+ ACPI_RS_SIZE(struct acpi_resource_common_serialbus),
+ ACPI_RS_SIZE(struct acpi_resource_pin_config),
+ 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),
+};
+
+const u8 acpi_gbl_aml_resource_serial_bus_sizes[] = {
+ 0,
+ sizeof(struct aml_resource_i2c_serialbus),
+ sizeof(struct aml_resource_spi_serialbus),
+ sizeof(struct aml_resource_uart_serialbus),
+ sizeof(struct aml_resource_csi2_serialbus),
+};
+
+const u8 acpi_gbl_resource_struct_serial_bus_sizes[] = {
+ 0,
+ ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus),
+ ACPI_RS_SIZE(struct acpi_resource_spi_serialbus),
+ ACPI_RS_SIZE(struct acpi_resource_uart_serialbus),
+ ACPI_RS_SIZE(struct acpi_resource_csi2_serialbus),
+};
diff --git a/drivers/acpi/acpica/rsio.c b/drivers/acpi/acpica/rsio.c
new file mode 100644
index 000000000..687aaed96
--- /dev/null
+++ b/drivers/acpi/acpica/rsio.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsio - IO and DMA resource descriptors
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsio")
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_io
+ *
+ ******************************************************************************/
+struct acpi_rsconvert_info acpi_rs_convert_io[5] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_IO,
+ ACPI_RS_SIZE(struct acpi_resource_io),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_io)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_IO,
+ sizeof(struct aml_resource_io),
+ 0},
+
+ /* Decode flag */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.io.io_decode),
+ AML_OFFSET(io.flags),
+ 0},
+ /*
+ * These fields are contiguous in both the source and destination:
+ * Address Alignment
+ * Length
+ * Minimum Base Address
+ * Maximum Base Address
+ */
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.io.alignment),
+ AML_OFFSET(io.alignment),
+ 2},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.io.minimum),
+ AML_OFFSET(io.minimum),
+ 2}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_fixed_io
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_fixed_io[4] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_FIXED_IO,
+ ACPI_RS_SIZE(struct acpi_resource_fixed_io),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_fixed_io)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_FIXED_IO,
+ sizeof(struct aml_resource_fixed_io),
+ 0},
+ /*
+ * These fields are contiguous in both the source and destination:
+ * Base Address
+ * Length
+ */
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.fixed_io.address_length),
+ AML_OFFSET(fixed_io.address_length),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.fixed_io.address),
+ AML_OFFSET(fixed_io.address),
+ 1}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_generic_reg
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_generic_reg[4] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_GENERIC_REGISTER,
+ ACPI_RS_SIZE(struct acpi_resource_generic_register),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_generic_reg)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_GENERIC_REGISTER,
+ sizeof(struct aml_resource_generic_register),
+ 0},
+ /*
+ * These fields are contiguous in both the source and destination:
+ * Address Space ID
+ * Register Bit Width
+ * Register Bit Offset
+ * Access Size
+ */
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.generic_reg.space_id),
+ AML_OFFSET(generic_reg.address_space_id),
+ 4},
+
+ /* Get the Register Address */
+
+ {ACPI_RSC_MOVE64, ACPI_RS_OFFSET(data.generic_reg.address),
+ AML_OFFSET(generic_reg.address),
+ 1}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_end_dpf
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_end_dpf[2] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_END_DEPENDENT,
+ ACPI_RS_SIZE_MIN,
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_end_dpf)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_END_DEPENDENT,
+ sizeof(struct aml_resource_end_dependent),
+ 0}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_end_tag
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_end_tag[2] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_END_TAG,
+ ACPI_RS_SIZE_MIN,
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_end_tag)},
+
+ /*
+ * Note: The checksum field is set to zero, meaning that the resource
+ * data is treated as if the checksum operation succeeded.
+ * (ACPI Spec 1.0b Section 6.4.2.8)
+ */
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_END_TAG,
+ sizeof(struct aml_resource_end_tag),
+ 0}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_get_start_dpf
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_get_start_dpf[6] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_START_DEPENDENT,
+ ACPI_RS_SIZE(struct acpi_resource_start_dependent),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_get_start_dpf)},
+
+ /* Defaults for Compatibility and Performance priorities */
+
+ {ACPI_RSC_SET8, ACPI_RS_OFFSET(data.start_dpf.compatibility_priority),
+ ACPI_ACCEPTABLE_CONFIGURATION,
+ 2},
+
+ /* Get the descriptor length (0 or 1 for Start Dpf descriptor) */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.start_dpf.descriptor_length),
+ AML_OFFSET(start_dpf.descriptor_type),
+ 0},
+
+ /* All done if there is no flag byte present in the descriptor */
+
+ {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_AML_LENGTH, 0, 1},
+
+ /* Flag byte is present, get the flags */
+
+ {ACPI_RSC_2BITFLAG,
+ ACPI_RS_OFFSET(data.start_dpf.compatibility_priority),
+ AML_OFFSET(start_dpf.flags),
+ 0},
+
+ {ACPI_RSC_2BITFLAG,
+ ACPI_RS_OFFSET(data.start_dpf.performance_robustness),
+ AML_OFFSET(start_dpf.flags),
+ 2}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_set_start_dpf
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_set_start_dpf[10] = {
+ /* Start with a default descriptor of length 1 */
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_START_DEPENDENT,
+ sizeof(struct aml_resource_start_dependent),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_set_start_dpf)},
+
+ /* Set the default flag values */
+
+ {ACPI_RSC_2BITFLAG,
+ ACPI_RS_OFFSET(data.start_dpf.compatibility_priority),
+ AML_OFFSET(start_dpf.flags),
+ 0},
+
+ {ACPI_RSC_2BITFLAG,
+ ACPI_RS_OFFSET(data.start_dpf.performance_robustness),
+ AML_OFFSET(start_dpf.flags),
+ 2},
+ /*
+ * All done if the output descriptor length is required to be 1
+ * (i.e., optimization to 0 bytes cannot be attempted)
+ */
+ {ACPI_RSC_EXIT_EQ, ACPI_RSC_COMPARE_VALUE,
+ ACPI_RS_OFFSET(data.start_dpf.descriptor_length),
+ 1},
+
+ /* Set length to 0 bytes (no flags byte) */
+
+ {ACPI_RSC_LENGTH, 0, 0,
+ sizeof(struct aml_resource_start_dependent_noprio)},
+
+ /*
+ * All done if the output descriptor length is required to be 0.
+ *
+ * TBD: Perhaps we should check for error if input flags are not
+ * compatible with a 0-byte descriptor.
+ */
+ {ACPI_RSC_EXIT_EQ, ACPI_RSC_COMPARE_VALUE,
+ ACPI_RS_OFFSET(data.start_dpf.descriptor_length),
+ 0},
+
+ /* Reset length to 1 byte (descriptor with flags byte) */
+
+ {ACPI_RSC_LENGTH, 0, 0, sizeof(struct aml_resource_start_dependent)},
+
+ /*
+ * All done if flags byte is necessary -- if either priority value
+ * is not ACPI_ACCEPTABLE_CONFIGURATION
+ */
+ {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_VALUE,
+ ACPI_RS_OFFSET(data.start_dpf.compatibility_priority),
+ ACPI_ACCEPTABLE_CONFIGURATION},
+
+ {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_VALUE,
+ ACPI_RS_OFFSET(data.start_dpf.performance_robustness),
+ ACPI_ACCEPTABLE_CONFIGURATION},
+
+ /* Flag byte is not necessary */
+
+ {ACPI_RSC_LENGTH, 0, 0,
+ sizeof(struct aml_resource_start_dependent_noprio)}
+};
diff --git a/drivers/acpi/acpica/rsirq.c b/drivers/acpi/acpica/rsirq.c
new file mode 100644
index 000000000..b0d970efa
--- /dev/null
+++ b/drivers/acpi/acpica/rsirq.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsirq - IRQ resource descriptors
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsirq")
+
+/*******************************************************************************
+ *
+ * acpi_rs_get_irq
+ *
+ ******************************************************************************/
+struct acpi_rsconvert_info acpi_rs_get_irq[9] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_IRQ,
+ ACPI_RS_SIZE(struct acpi_resource_irq),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_get_irq)},
+
+ /* Get the IRQ mask (bytes 1:2) */
+
+ {ACPI_RSC_BITMASK16, ACPI_RS_OFFSET(data.irq.interrupts[0]),
+ AML_OFFSET(irq.irq_mask),
+ ACPI_RS_OFFSET(data.irq.interrupt_count)},
+
+ /* Set default flags (others are zero) */
+
+ {ACPI_RSC_SET8, ACPI_RS_OFFSET(data.irq.triggering),
+ ACPI_EDGE_SENSITIVE,
+ 1},
+
+ /* Get the descriptor length (2 or 3 for IRQ descriptor) */
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.irq.descriptor_length),
+ AML_OFFSET(irq.descriptor_type),
+ 0},
+
+ /* All done if no flag byte present in descriptor */
+
+ {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_AML_LENGTH, 0, 3},
+
+ /* Get flags: Triggering[0], Polarity[3], Sharing[4], Wake[5] */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.triggering),
+ AML_OFFSET(irq.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.polarity),
+ AML_OFFSET(irq.flags),
+ 3},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.shareable),
+ AML_OFFSET(irq.flags),
+ 4},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.wake_capable),
+ AML_OFFSET(irq.flags),
+ 5}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_set_irq
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_set_irq[14] = {
+ /* Start with a default descriptor of length 3 */
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_IRQ,
+ sizeof(struct aml_resource_irq),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_set_irq)},
+
+ /* Convert interrupt list to 16-bit IRQ bitmask */
+
+ {ACPI_RSC_BITMASK16, ACPI_RS_OFFSET(data.irq.interrupts[0]),
+ AML_OFFSET(irq.irq_mask),
+ ACPI_RS_OFFSET(data.irq.interrupt_count)},
+
+ /* Set flags: Triggering[0], Polarity[3], Sharing[4], Wake[5] */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.triggering),
+ AML_OFFSET(irq.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.polarity),
+ AML_OFFSET(irq.flags),
+ 3},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.shareable),
+ AML_OFFSET(irq.flags),
+ 4},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.wake_capable),
+ AML_OFFSET(irq.flags),
+ 5},
+
+ /*
+ * All done if the output descriptor length is required to be 3
+ * (i.e., optimization to 2 bytes cannot be attempted)
+ */
+ {ACPI_RSC_EXIT_EQ, ACPI_RSC_COMPARE_VALUE,
+ ACPI_RS_OFFSET(data.irq.descriptor_length),
+ 3},
+
+ /* Set length to 2 bytes (no flags byte) */
+
+ {ACPI_RSC_LENGTH, 0, 0, sizeof(struct aml_resource_irq_noflags)},
+
+ /*
+ * All done if the output descriptor length is required to be 2.
+ *
+ * TBD: Perhaps we should check for error if input flags are not
+ * compatible with a 2-byte descriptor.
+ */
+ {ACPI_RSC_EXIT_EQ, ACPI_RSC_COMPARE_VALUE,
+ ACPI_RS_OFFSET(data.irq.descriptor_length),
+ 2},
+
+ /* Reset length to 3 bytes (descriptor with flags byte) */
+
+ {ACPI_RSC_LENGTH, 0, 0, sizeof(struct aml_resource_irq)},
+
+ /*
+ * Check if the flags byte is necessary. Not needed if the flags are:
+ * ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_HIGH, ACPI_EXCLUSIVE
+ */
+ {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_VALUE,
+ ACPI_RS_OFFSET(data.irq.triggering),
+ ACPI_EDGE_SENSITIVE},
+
+ {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_VALUE,
+ ACPI_RS_OFFSET(data.irq.polarity),
+ ACPI_ACTIVE_HIGH},
+
+ {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_VALUE,
+ ACPI_RS_OFFSET(data.irq.shareable),
+ ACPI_EXCLUSIVE},
+
+ /* We can optimize to a 2-byte irq_no_flags() descriptor */
+
+ {ACPI_RSC_LENGTH, 0, 0, sizeof(struct aml_resource_irq_noflags)}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_ext_irq
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_ext_irq[10] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_EXTENDED_IRQ,
+ ACPI_RS_SIZE(struct acpi_resource_extended_irq),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_ext_irq)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_EXTENDED_IRQ,
+ sizeof(struct aml_resource_extended_irq),
+ 0},
+
+ /*
+ * Flags: Producer/Consumer[0], Triggering[1], Polarity[2],
+ * Sharing[3], Wake[4]
+ */
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.extended_irq.producer_consumer),
+ AML_OFFSET(extended_irq.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.extended_irq.triggering),
+ AML_OFFSET(extended_irq.flags),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.extended_irq.polarity),
+ AML_OFFSET(extended_irq.flags),
+ 2},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.extended_irq.shareable),
+ AML_OFFSET(extended_irq.flags),
+ 3},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.extended_irq.wake_capable),
+ AML_OFFSET(extended_irq.flags),
+ 4},
+
+ /* IRQ Table length (Byte4) */
+
+ {ACPI_RSC_COUNT, ACPI_RS_OFFSET(data.extended_irq.interrupt_count),
+ AML_OFFSET(extended_irq.interrupt_count),
+ sizeof(u32)},
+
+ /* Copy every IRQ in the table, each is 32 bits */
+
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.extended_irq.interrupts[0]),
+ AML_OFFSET(extended_irq.interrupts[0]),
+ 0},
+
+ /* Optional resource_source (Index and String) */
+
+ {ACPI_RSC_SOURCEX, ACPI_RS_OFFSET(data.extended_irq.resource_source),
+ ACPI_RS_OFFSET(data.extended_irq.interrupts[0]),
+ sizeof(struct aml_resource_extended_irq)}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_dma
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_dma[6] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_DMA,
+ ACPI_RS_SIZE(struct acpi_resource_dma),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_dma)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_DMA,
+ sizeof(struct aml_resource_dma),
+ 0},
+
+ /* Flags: transfer preference, bus mastering, channel speed */
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.dma.transfer),
+ AML_OFFSET(dma.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.dma.bus_master),
+ AML_OFFSET(dma.flags),
+ 2},
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.dma.type),
+ AML_OFFSET(dma.flags),
+ 5},
+
+ /* DMA channel mask bits */
+
+ {ACPI_RSC_BITMASK, ACPI_RS_OFFSET(data.dma.channels[0]),
+ AML_OFFSET(dma.dma_channel_mask),
+ ACPI_RS_OFFSET(data.dma.channel_count)}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_fixed_dma
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_fixed_dma[4] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_FIXED_DMA,
+ ACPI_RS_SIZE(struct acpi_resource_fixed_dma),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_fixed_dma)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_FIXED_DMA,
+ sizeof(struct aml_resource_fixed_dma),
+ 0},
+
+ /*
+ * These fields are contiguous in both the source and destination:
+ * request_lines
+ * Channels
+ */
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.fixed_dma.request_lines),
+ AML_OFFSET(fixed_dma.request_lines),
+ 2},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.fixed_dma.width),
+ AML_OFFSET(fixed_dma.width),
+ 1},
+};
diff --git a/drivers/acpi/acpica/rslist.c b/drivers/acpi/acpica/rslist.c
new file mode 100644
index 000000000..e46efaa88
--- /dev/null
+++ b/drivers/acpi/acpica/rslist.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rslist - Linked list utilities
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rslist")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_convert_aml_to_resources
+ *
+ * PARAMETERS: acpi_walk_aml_callback
+ * resource_ptr - Pointer to the buffer that will
+ * contain the output structures
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert an AML resource to an internal representation of the
+ * resource that is aligned and easier to access.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_rs_convert_aml_to_resources(u8 * aml,
+ u32 length,
+ u32 offset, u8 resource_index, void **context)
+{
+ struct acpi_resource **resource_ptr =
+ ACPI_CAST_INDIRECT_PTR(struct acpi_resource, context);
+ struct acpi_resource *resource;
+ union aml_resource *aml_resource;
+ struct acpi_rsconvert_info *conversion_table;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(rs_convert_aml_to_resources);
+
+ /*
+ * Check that the input buffer and all subsequent pointers into it
+ * are aligned on a native word boundary. Most important on IA64
+ */
+ resource = *resource_ptr;
+ if (ACPI_IS_MISALIGNED(resource)) {
+ ACPI_WARNING((AE_INFO,
+ "Misaligned resource pointer %p", resource));
+ }
+
+ /* Get the appropriate conversion info table */
+
+ aml_resource = ACPI_CAST_PTR(union aml_resource, aml);
+
+ if (acpi_ut_get_resource_type(aml) == ACPI_RESOURCE_NAME_SERIAL_BUS) {
+ if (aml_resource->common_serial_bus.type >
+ AML_RESOURCE_MAX_SERIALBUSTYPE) {
+ conversion_table = NULL;
+ } else {
+ /* This is an I2C, SPI, UART, or CSI2 serial_bus descriptor */
+
+ conversion_table =
+ acpi_gbl_convert_resource_serial_bus_dispatch
+ [aml_resource->common_serial_bus.type];
+ }
+ } else {
+ conversion_table =
+ acpi_gbl_get_resource_dispatch[resource_index];
+ }
+
+ if (!conversion_table) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid/unsupported resource descriptor: Type 0x%2.2X",
+ resource_index));
+ return_ACPI_STATUS(AE_AML_INVALID_RESOURCE_TYPE);
+ }
+
+ /* Convert the AML byte stream resource to a local resource struct */
+
+ status =
+ acpi_rs_convert_aml_to_resource(resource, aml_resource,
+ conversion_table);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not convert AML resource (Type 0x%X)",
+ *aml));
+ return_ACPI_STATUS(status);
+ }
+
+ if (!resource->length) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Zero-length resource returned from RsConvertAmlToResource"));
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES,
+ "Type %.2X, AmlLength %.2X InternalLength %.2X\n",
+ acpi_ut_get_resource_type(aml), length,
+ resource->length));
+
+ /* Point to the next structure in the output buffer */
+
+ *resource_ptr = ACPI_NEXT_RESOURCE(resource);
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_convert_resources_to_aml
+ *
+ * PARAMETERS: resource - Pointer to the resource linked list
+ * aml_size_needed - Calculated size of the byte stream
+ * needed from calling acpi_rs_get_aml_length()
+ * The size of the output_buffer is
+ * guaranteed to be >= aml_size_needed
+ * output_buffer - Pointer to the buffer that will
+ * contain the byte stream
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Takes the resource linked list and parses it, creating a
+ * byte stream of resources in the caller's output buffer
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_convert_resources_to_aml(struct acpi_resource *resource,
+ acpi_size aml_size_needed, u8 * output_buffer)
+{
+ u8 *aml = output_buffer;
+ u8 *end_aml = output_buffer + aml_size_needed;
+ struct acpi_rsconvert_info *conversion_table;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(rs_convert_resources_to_aml);
+
+ /* Walk the resource descriptor list, convert each descriptor */
+
+ while (aml < end_aml) {
+
+ /* Validate the (internal) Resource Type */
+
+ if (resource->type > ACPI_RESOURCE_TYPE_MAX) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid descriptor type (0x%X) in resource list",
+ resource->type));
+ return_ACPI_STATUS(AE_BAD_DATA);
+ }
+
+ /* Sanity check the length. It must not be zero, or we loop forever */
+
+ if (!resource->length) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid zero length descriptor in resource list\n"));
+ return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH);
+ }
+
+ /* Perform the conversion */
+
+ if (resource->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+ if (resource->data.common_serial_bus.type >
+ AML_RESOURCE_MAX_SERIALBUSTYPE) {
+ conversion_table = NULL;
+ } else {
+ /* This is an I2C, SPI, UART or CSI2 serial_bus descriptor */
+
+ conversion_table =
+ acpi_gbl_convert_resource_serial_bus_dispatch
+ [resource->data.common_serial_bus.type];
+ }
+ } else {
+ conversion_table =
+ acpi_gbl_set_resource_dispatch[resource->type];
+ }
+
+ if (!conversion_table) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid/unsupported resource descriptor: Type 0x%2.2X",
+ resource->type));
+ return_ACPI_STATUS(AE_AML_INVALID_RESOURCE_TYPE);
+ }
+
+ status = acpi_rs_convert_resource_to_aml(resource,
+ ACPI_CAST_PTR(union
+ aml_resource,
+ aml),
+ conversion_table);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not convert resource (type 0x%X) to AML",
+ resource->type));
+ return_ACPI_STATUS(status);
+ }
+
+ /* Perform final sanity check on the new AML resource descriptor */
+
+ status =
+ acpi_ut_validate_resource(NULL,
+ ACPI_CAST_PTR(union aml_resource,
+ aml), NULL);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Check for end-of-list, normal exit */
+
+ if (resource->type == ACPI_RESOURCE_TYPE_END_TAG) {
+
+ /* An End Tag indicates the end of the input Resource Template */
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Extract the total length of the new descriptor and set the
+ * Aml to point to the next (output) resource descriptor
+ */
+ aml += acpi_ut_get_descriptor_length(aml);
+
+ /* Point to the next input resource descriptor */
+
+ resource = ACPI_NEXT_RESOURCE(resource);
+ }
+
+ /* Completed buffer, but did not find an end_tag resource descriptor */
+
+ return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG);
+}
diff --git a/drivers/acpi/acpica/rsmemory.c b/drivers/acpi/acpica/rsmemory.c
new file mode 100644
index 000000000..44fa16dde
--- /dev/null
+++ b/drivers/acpi/acpica/rsmemory.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsmem24 - Memory resource descriptors
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsmemory")
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_memory24
+ *
+ ******************************************************************************/
+struct acpi_rsconvert_info acpi_rs_convert_memory24[4] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_MEMORY24,
+ ACPI_RS_SIZE(struct acpi_resource_memory24),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_memory24)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_MEMORY24,
+ sizeof(struct aml_resource_memory24),
+ 0},
+
+ /* Read/Write bit */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.memory24.write_protect),
+ AML_OFFSET(memory24.flags),
+ 0},
+ /*
+ * These fields are contiguous in both the source and destination:
+ * Minimum Base Address
+ * Maximum Base Address
+ * Address Base Alignment
+ * Range Length
+ */
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.memory24.minimum),
+ AML_OFFSET(memory24.minimum),
+ 4}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_memory32
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_memory32[4] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_MEMORY32,
+ ACPI_RS_SIZE(struct acpi_resource_memory32),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_memory32)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_MEMORY32,
+ sizeof(struct aml_resource_memory32),
+ 0},
+
+ /* Read/Write bit */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.memory32.write_protect),
+ AML_OFFSET(memory32.flags),
+ 0},
+ /*
+ * These fields are contiguous in both the source and destination:
+ * Minimum Base Address
+ * Maximum Base Address
+ * Address Base Alignment
+ * Range Length
+ */
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.memory32.minimum),
+ AML_OFFSET(memory32.minimum),
+ 4}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_fixed_memory32
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_fixed_memory32[4] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_FIXED_MEMORY32,
+ ACPI_RS_SIZE(struct acpi_resource_fixed_memory32),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_fixed_memory32)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_FIXED_MEMORY32,
+ sizeof(struct aml_resource_fixed_memory32),
+ 0},
+
+ /* Read/Write bit */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.fixed_memory32.write_protect),
+ AML_OFFSET(fixed_memory32.flags),
+ 0},
+ /*
+ * These fields are contiguous in both the source and destination:
+ * Base Address
+ * Range Length
+ */
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.fixed_memory32.address),
+ AML_OFFSET(fixed_memory32.address),
+ 2}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_get_vendor_small
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_get_vendor_small[3] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_VENDOR,
+ ACPI_RS_SIZE(struct acpi_resource_vendor),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_get_vendor_small)},
+
+ /* Length of the vendor data (byte count) */
+
+ {ACPI_RSC_COUNT16, ACPI_RS_OFFSET(data.vendor.byte_length),
+ 0,
+ sizeof(u8)},
+
+ /* Vendor data */
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.vendor.byte_data[0]),
+ sizeof(struct aml_resource_small_header),
+ 0}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_get_vendor_large
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_get_vendor_large[3] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_VENDOR,
+ ACPI_RS_SIZE(struct acpi_resource_vendor),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_get_vendor_large)},
+
+ /* Length of the vendor data (byte count) */
+
+ {ACPI_RSC_COUNT16, ACPI_RS_OFFSET(data.vendor.byte_length),
+ 0,
+ sizeof(u8)},
+
+ /* Vendor data */
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.vendor.byte_data[0]),
+ sizeof(struct aml_resource_large_header),
+ 0}
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_set_vendor
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_set_vendor[7] = {
+ /* Default is a small vendor descriptor */
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_VENDOR_SMALL,
+ sizeof(struct aml_resource_small_header),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_set_vendor)},
+
+ /* Get the length and copy the data */
+
+ {ACPI_RSC_COUNT16, ACPI_RS_OFFSET(data.vendor.byte_length),
+ 0,
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.vendor.byte_data[0]),
+ sizeof(struct aml_resource_small_header),
+ 0},
+
+ /*
+ * All done if the Vendor byte length is 7 or less, meaning that it will
+ * fit within a small descriptor
+ */
+ {ACPI_RSC_EXIT_LE, 0, 0, 7},
+
+ /* Must create a large vendor descriptor */
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_VENDOR_LARGE,
+ sizeof(struct aml_resource_large_header),
+ 0},
+
+ {ACPI_RSC_COUNT16, ACPI_RS_OFFSET(data.vendor.byte_length),
+ 0,
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.vendor.byte_data[0]),
+ sizeof(struct aml_resource_large_header),
+ 0}
+};
diff --git a/drivers/acpi/acpica/rsmisc.c b/drivers/acpi/acpica/rsmisc.c
new file mode 100644
index 000000000..c2dd9aae4
--- /dev/null
+++ b/drivers/acpi/acpica/rsmisc.c
@@ -0,0 +1,804 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsmisc - Miscellaneous resource descriptors
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsmisc")
+#define INIT_RESOURCE_TYPE(i) i->resource_offset
+#define INIT_RESOURCE_LENGTH(i) i->aml_offset
+#define INIT_TABLE_LENGTH(i) i->value
+#define COMPARE_OPCODE(i) i->resource_offset
+#define COMPARE_TARGET(i) i->aml_offset
+#define COMPARE_VALUE(i) i->value
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_convert_aml_to_resource
+ *
+ * PARAMETERS: resource - Pointer to the resource descriptor
+ * aml - Where the AML descriptor is returned
+ * info - Pointer to appropriate conversion table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert an external AML resource descriptor to the corresponding
+ * internal resource descriptor
+ *
+ ******************************************************************************/
+acpi_status
+acpi_rs_convert_aml_to_resource(struct acpi_resource *resource,
+ union aml_resource *aml,
+ struct acpi_rsconvert_info *info)
+{
+ acpi_rs_length aml_resource_length;
+ void *source;
+ void *destination;
+ char *target;
+ u8 count;
+ u8 flags_mode = FALSE;
+ u16 item_count = 0;
+ u16 temp16 = 0;
+
+ ACPI_FUNCTION_TRACE(rs_convert_aml_to_resource);
+
+ if (!info) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if (((acpi_size)resource) & 0x3) {
+
+ /* Each internal resource struct is expected to be 32-bit aligned */
+
+ ACPI_WARNING((AE_INFO,
+ "Misaligned resource pointer (get): %p Type 0x%2.2X Length %u",
+ resource, resource->type, resource->length));
+ }
+
+ /* Extract the resource Length field (does not include header length) */
+
+ aml_resource_length = acpi_ut_get_resource_length(aml);
+
+ /*
+ * First table entry must be ACPI_RSC_INITxxx and must contain the
+ * table length (# of table entries)
+ */
+ count = INIT_TABLE_LENGTH(info);
+ while (count) {
+ target = NULL;
+
+ /*
+ * Source is the external AML byte stream buffer,
+ * destination is the internal resource descriptor
+ */
+ source = ACPI_ADD_PTR(void, aml, info->aml_offset);
+ destination =
+ ACPI_ADD_PTR(void, resource, info->resource_offset);
+
+ switch (info->opcode) {
+ case ACPI_RSC_INITGET:
+ /*
+ * Get the resource type and the initial (minimum) length
+ */
+ memset(resource, 0, INIT_RESOURCE_LENGTH(info));
+ resource->type = INIT_RESOURCE_TYPE(info);
+ resource->length = INIT_RESOURCE_LENGTH(info);
+ break;
+
+ case ACPI_RSC_INITSET:
+ break;
+
+ case ACPI_RSC_FLAGINIT:
+
+ flags_mode = TRUE;
+ break;
+
+ case ACPI_RSC_1BITFLAG:
+ /*
+ * Mask and shift the flag bit
+ */
+ ACPI_SET8(destination,
+ ((ACPI_GET8(source) >> info->value) & 0x01));
+ break;
+
+ case ACPI_RSC_2BITFLAG:
+ /*
+ * Mask and shift the flag bits
+ */
+ ACPI_SET8(destination,
+ ((ACPI_GET8(source) >> info->value) & 0x03));
+ break;
+
+ case ACPI_RSC_3BITFLAG:
+ /*
+ * Mask and shift the flag bits
+ */
+ ACPI_SET8(destination,
+ ((ACPI_GET8(source) >> info->value) & 0x07));
+ break;
+
+ case ACPI_RSC_6BITFLAG:
+ /*
+ * Mask and shift the flag bits
+ */
+ ACPI_SET8(destination,
+ ((ACPI_GET8(source) >> info->value) & 0x3F));
+ break;
+
+ case ACPI_RSC_COUNT:
+
+ item_count = ACPI_GET8(source);
+ ACPI_SET8(destination, item_count);
+
+ resource->length = resource->length +
+ (info->value * (item_count - 1));
+ break;
+
+ case ACPI_RSC_COUNT16:
+
+ item_count = aml_resource_length;
+ ACPI_SET16(destination, item_count);
+
+ resource->length = resource->length +
+ (info->value * (item_count - 1));
+ break;
+
+ case ACPI_RSC_COUNT_GPIO_PIN:
+
+ target = ACPI_ADD_PTR(void, aml, info->value);
+ item_count = ACPI_GET16(target) - ACPI_GET16(source);
+
+ resource->length = resource->length + item_count;
+ item_count = item_count / 2;
+ ACPI_SET16(destination, item_count);
+ break;
+
+ case ACPI_RSC_COUNT_GPIO_VEN:
+
+ item_count = ACPI_GET8(source);
+ ACPI_SET8(destination, item_count);
+
+ resource->length =
+ resource->length + (info->value * item_count);
+ break;
+
+ case ACPI_RSC_COUNT_GPIO_RES:
+ /*
+ * Vendor data is optional (length/offset may both be zero)
+ * Examine vendor data length field first
+ */
+ target = ACPI_ADD_PTR(void, aml, (info->value + 2));
+ if (ACPI_GET16(target)) {
+
+ /* Use vendor offset to get resource source length */
+
+ target = ACPI_ADD_PTR(void, aml, info->value);
+ item_count =
+ ACPI_GET16(target) - ACPI_GET16(source);
+ } else {
+ /* No vendor data to worry about */
+
+ item_count = aml->large_header.resource_length +
+ sizeof(struct aml_resource_large_header) -
+ ACPI_GET16(source);
+ }
+
+ resource->length = resource->length + item_count;
+ ACPI_SET16(destination, item_count);
+ break;
+
+ case ACPI_RSC_COUNT_SERIAL_VEN:
+
+ item_count = ACPI_GET16(source) - info->value;
+
+ resource->length = resource->length + item_count;
+ ACPI_SET16(destination, item_count);
+ break;
+
+ case ACPI_RSC_COUNT_SERIAL_RES:
+
+ item_count = (aml_resource_length +
+ sizeof(struct aml_resource_large_header))
+ - ACPI_GET16(source) - info->value;
+
+ resource->length = resource->length + item_count;
+ ACPI_SET16(destination, item_count);
+ break;
+
+ case ACPI_RSC_LENGTH:
+
+ resource->length = resource->length + info->value;
+ break;
+
+ case ACPI_RSC_MOVE8:
+ case ACPI_RSC_MOVE16:
+ case ACPI_RSC_MOVE32:
+ case ACPI_RSC_MOVE64:
+ /*
+ * Raw data move. Use the Info value field unless item_count has
+ * been previously initialized via a COUNT opcode
+ */
+ if (info->value) {
+ item_count = info->value;
+ }
+ acpi_rs_move_data(destination, source, item_count,
+ info->opcode);
+ break;
+
+ case ACPI_RSC_MOVE_GPIO_PIN:
+
+ /* Generate and set the PIN data pointer */
+
+ target = (char *)ACPI_ADD_PTR(void, resource,
+ (resource->length -
+ item_count * 2));
+ *(u16 **)destination = ACPI_CAST_PTR(u16, target);
+
+ /* Copy the PIN data */
+
+ source = ACPI_ADD_PTR(void, aml, ACPI_GET16(source));
+ acpi_rs_move_data(target, source, item_count,
+ info->opcode);
+ break;
+
+ case ACPI_RSC_MOVE_GPIO_RES:
+
+ /* Generate and set the resource_source string pointer */
+
+ target = (char *)ACPI_ADD_PTR(void, resource,
+ (resource->length -
+ item_count));
+ *(u8 **)destination = ACPI_CAST_PTR(u8, target);
+
+ /* Copy the resource_source string */
+
+ source = ACPI_ADD_PTR(void, aml, ACPI_GET16(source));
+ acpi_rs_move_data(target, source, item_count,
+ info->opcode);
+ break;
+
+ case ACPI_RSC_MOVE_SERIAL_VEN:
+
+ /* Generate and set the Vendor Data pointer */
+
+ target = (char *)ACPI_ADD_PTR(void, resource,
+ (resource->length -
+ item_count));
+ *(u8 **)destination = ACPI_CAST_PTR(u8, target);
+
+ /* Copy the Vendor Data */
+
+ source = ACPI_ADD_PTR(void, aml, info->value);
+ acpi_rs_move_data(target, source, item_count,
+ info->opcode);
+ break;
+
+ case ACPI_RSC_MOVE_SERIAL_RES:
+
+ /* Generate and set the resource_source string pointer */
+
+ target = (char *)ACPI_ADD_PTR(void, resource,
+ (resource->length -
+ item_count));
+ *(u8 **)destination = ACPI_CAST_PTR(u8, target);
+
+ /* Copy the resource_source string */
+
+ source =
+ ACPI_ADD_PTR(void, aml,
+ (ACPI_GET16(source) + info->value));
+ acpi_rs_move_data(target, source, item_count,
+ info->opcode);
+ break;
+
+ case ACPI_RSC_SET8:
+
+ memset(destination, info->aml_offset, info->value);
+ break;
+
+ case ACPI_RSC_DATA8:
+
+ target = ACPI_ADD_PTR(char, resource, info->value);
+ memcpy(destination, source, ACPI_GET16(target));
+ break;
+
+ case ACPI_RSC_ADDRESS:
+ /*
+ * Common handler for address descriptor flags
+ */
+ if (!acpi_rs_get_address_common(resource, aml)) {
+ return_ACPI_STATUS
+ (AE_AML_INVALID_RESOURCE_TYPE);
+ }
+ break;
+
+ case ACPI_RSC_SOURCE:
+ /*
+ * Optional resource_source (Index and String)
+ */
+ resource->length +=
+ acpi_rs_get_resource_source(aml_resource_length,
+ info->value,
+ destination, aml, NULL);
+ break;
+
+ case ACPI_RSC_SOURCEX:
+ /*
+ * Optional resource_source (Index and String). This is the more
+ * complicated case used by the Interrupt() macro
+ */
+ target = ACPI_ADD_PTR(char, resource,
+ info->aml_offset +
+ (item_count * 4));
+
+ resource->length +=
+ acpi_rs_get_resource_source(aml_resource_length,
+ (acpi_rs_length)
+ (((item_count -
+ 1) * sizeof(u32)) +
+ info->value),
+ destination, aml,
+ target);
+ break;
+
+ case ACPI_RSC_BITMASK:
+ /*
+ * 8-bit encoded bitmask (DMA macro)
+ */
+ item_count =
+ acpi_rs_decode_bitmask(ACPI_GET8(source),
+ destination);
+ if (item_count) {
+ resource->length += (item_count - 1);
+ }
+
+ target = ACPI_ADD_PTR(char, resource, info->value);
+ ACPI_SET8(target, item_count);
+ break;
+
+ case ACPI_RSC_BITMASK16:
+ /*
+ * 16-bit encoded bitmask (IRQ macro)
+ */
+ ACPI_MOVE_16_TO_16(&temp16, source);
+
+ item_count =
+ acpi_rs_decode_bitmask(temp16, destination);
+ if (item_count) {
+ resource->length += (item_count - 1);
+ }
+
+ target = ACPI_ADD_PTR(char, resource, info->value);
+ ACPI_SET8(target, item_count);
+ break;
+
+ case ACPI_RSC_EXIT_NE:
+ /*
+ * control - Exit conversion if not equal
+ */
+ switch (info->resource_offset) {
+ case ACPI_RSC_COMPARE_AML_LENGTH:
+
+ if (aml_resource_length != info->value) {
+ goto exit;
+ }
+ break;
+
+ case ACPI_RSC_COMPARE_VALUE:
+
+ if (ACPI_GET8(source) != info->value) {
+ goto exit;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid conversion sub-opcode"));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Invalid conversion opcode"));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ count--;
+ info++;
+ }
+
+exit:
+ if (!flags_mode) {
+
+ /* Round the resource struct length up to the next boundary (32 or 64) */
+
+ resource->length = (u32)
+ ACPI_ROUND_UP_TO_NATIVE_WORD(resource->length);
+ }
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_convert_resource_to_aml
+ *
+ * PARAMETERS: resource - Pointer to the resource descriptor
+ * aml - Where the AML descriptor is returned
+ * info - Pointer to appropriate conversion table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert an internal resource descriptor to the corresponding
+ * external AML resource descriptor.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_convert_resource_to_aml(struct acpi_resource *resource,
+ union aml_resource *aml,
+ struct acpi_rsconvert_info *info)
+{
+ void *source = NULL;
+ void *destination;
+ char *target;
+ acpi_rsdesc_size aml_length = 0;
+ u8 count;
+ u16 temp16 = 0;
+ u16 item_count = 0;
+
+ ACPI_FUNCTION_TRACE(rs_convert_resource_to_aml);
+
+ if (!info) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * First table entry must be ACPI_RSC_INITxxx and must contain the
+ * table length (# of table entries)
+ */
+ count = INIT_TABLE_LENGTH(info);
+
+ while (count) {
+ /*
+ * Source is the internal resource descriptor,
+ * destination is the external AML byte stream buffer
+ */
+ source = ACPI_ADD_PTR(void, resource, info->resource_offset);
+ destination = ACPI_ADD_PTR(void, aml, info->aml_offset);
+
+ switch (info->opcode) {
+ case ACPI_RSC_INITSET:
+
+ memset(aml, 0, INIT_RESOURCE_LENGTH(info));
+ aml_length = INIT_RESOURCE_LENGTH(info);
+ acpi_rs_set_resource_header(INIT_RESOURCE_TYPE(info),
+ aml_length, aml);
+ break;
+
+ case ACPI_RSC_INITGET:
+ break;
+
+ case ACPI_RSC_FLAGINIT:
+ /*
+ * Clear the flag byte
+ */
+ ACPI_SET8(destination, 0);
+ break;
+
+ case ACPI_RSC_1BITFLAG:
+ /*
+ * Mask and shift the flag bit
+ */
+ ACPI_SET_BIT(*ACPI_CAST8(destination), (u8)
+ ((ACPI_GET8(source) & 0x01) << info->
+ value));
+ break;
+
+ case ACPI_RSC_2BITFLAG:
+ /*
+ * Mask and shift the flag bits
+ */
+ ACPI_SET_BIT(*ACPI_CAST8(destination), (u8)
+ ((ACPI_GET8(source) & 0x03) << info->
+ value));
+ break;
+
+ case ACPI_RSC_3BITFLAG:
+ /*
+ * Mask and shift the flag bits
+ */
+ ACPI_SET_BIT(*ACPI_CAST8(destination), (u8)
+ ((ACPI_GET8(source) & 0x07) << info->
+ value));
+ break;
+
+ case ACPI_RSC_6BITFLAG:
+ /*
+ * Mask and shift the flag bits
+ */
+ ACPI_SET_BIT(*ACPI_CAST8(destination), (u8)
+ ((ACPI_GET8(source) & 0x3F) << info->
+ value));
+ break;
+
+ case ACPI_RSC_COUNT:
+
+ item_count = ACPI_GET8(source);
+ ACPI_SET8(destination, item_count);
+
+ aml_length = (u16)
+ (aml_length + (info->value * (item_count - 1)));
+ break;
+
+ case ACPI_RSC_COUNT16:
+
+ item_count = ACPI_GET16(source);
+ aml_length = (u16) (aml_length + item_count);
+ acpi_rs_set_resource_length(aml_length, aml);
+ break;
+
+ case ACPI_RSC_COUNT_GPIO_PIN:
+
+ item_count = ACPI_GET16(source);
+ ACPI_SET16(destination, aml_length);
+
+ aml_length = (u16)(aml_length + item_count * 2);
+ target = ACPI_ADD_PTR(void, aml, info->value);
+ ACPI_SET16(target, aml_length);
+ acpi_rs_set_resource_length(aml_length, aml);
+ break;
+
+ case ACPI_RSC_COUNT_GPIO_VEN:
+
+ item_count = ACPI_GET16(source);
+ ACPI_SET16(destination, item_count);
+
+ aml_length =
+ (u16)(aml_length + (info->value * item_count));
+ acpi_rs_set_resource_length(aml_length, aml);
+ break;
+
+ case ACPI_RSC_COUNT_GPIO_RES:
+
+ /* Set resource source string length */
+
+ item_count = ACPI_GET16(source);
+ ACPI_SET16(destination, aml_length);
+
+ /* Compute offset for the Vendor Data */
+
+ aml_length = (u16)(aml_length + item_count);
+ target = ACPI_ADD_PTR(void, aml, info->value);
+
+ /* Set vendor offset only if there is vendor data */
+
+ ACPI_SET16(target, aml_length);
+
+ acpi_rs_set_resource_length(aml_length, aml);
+ break;
+
+ case ACPI_RSC_COUNT_SERIAL_VEN:
+
+ item_count = ACPI_GET16(source);
+ ACPI_SET16(destination, item_count + info->value);
+ aml_length = (u16)(aml_length + item_count);
+ acpi_rs_set_resource_length(aml_length, aml);
+ break;
+
+ case ACPI_RSC_COUNT_SERIAL_RES:
+
+ item_count = ACPI_GET16(source);
+ aml_length = (u16)(aml_length + item_count);
+ acpi_rs_set_resource_length(aml_length, aml);
+ break;
+
+ case ACPI_RSC_LENGTH:
+
+ acpi_rs_set_resource_length(info->value, aml);
+ break;
+
+ case ACPI_RSC_MOVE8:
+ case ACPI_RSC_MOVE16:
+ case ACPI_RSC_MOVE32:
+ case ACPI_RSC_MOVE64:
+
+ if (info->value) {
+ item_count = info->value;
+ }
+ acpi_rs_move_data(destination, source, item_count,
+ info->opcode);
+ break;
+
+ case ACPI_RSC_MOVE_GPIO_PIN:
+
+ destination = (char *)ACPI_ADD_PTR(void, aml,
+ ACPI_GET16
+ (destination));
+ source = *(u16 **)source;
+ acpi_rs_move_data(destination, source, item_count,
+ info->opcode);
+ break;
+
+ case ACPI_RSC_MOVE_GPIO_RES:
+
+ /* Used for both resource_source string and vendor_data */
+
+ destination = (char *)ACPI_ADD_PTR(void, aml,
+ ACPI_GET16
+ (destination));
+ source = *(u8 **)source;
+ acpi_rs_move_data(destination, source, item_count,
+ info->opcode);
+ break;
+
+ case ACPI_RSC_MOVE_SERIAL_VEN:
+
+ destination = (char *)ACPI_ADD_PTR(void, aml,
+ (aml_length -
+ item_count));
+ source = *(u8 **)source;
+ acpi_rs_move_data(destination, source, item_count,
+ info->opcode);
+ break;
+
+ case ACPI_RSC_MOVE_SERIAL_RES:
+
+ destination = (char *)ACPI_ADD_PTR(void, aml,
+ (aml_length -
+ item_count));
+ source = *(u8 **)source;
+ acpi_rs_move_data(destination, source, item_count,
+ info->opcode);
+ break;
+
+ case ACPI_RSC_ADDRESS:
+
+ /* Set the Resource Type, General Flags, and Type-Specific Flags */
+
+ acpi_rs_set_address_common(aml, resource);
+ break;
+
+ case ACPI_RSC_SOURCEX:
+ /*
+ * Optional resource_source (Index and String)
+ */
+ aml_length =
+ acpi_rs_set_resource_source(aml,
+ (acpi_rs_length)
+ aml_length, source);
+ acpi_rs_set_resource_length(aml_length, aml);
+ break;
+
+ case ACPI_RSC_SOURCE:
+ /*
+ * Optional resource_source (Index and String). This is the more
+ * complicated case used by the Interrupt() macro
+ */
+ aml_length =
+ acpi_rs_set_resource_source(aml, info->value,
+ source);
+ acpi_rs_set_resource_length(aml_length, aml);
+ break;
+
+ case ACPI_RSC_BITMASK:
+ /*
+ * 8-bit encoded bitmask (DMA macro)
+ */
+ ACPI_SET8(destination,
+ acpi_rs_encode_bitmask(source,
+ *ACPI_ADD_PTR(u8,
+ resource,
+ info->
+ value)));
+ break;
+
+ case ACPI_RSC_BITMASK16:
+ /*
+ * 16-bit encoded bitmask (IRQ macro)
+ */
+ temp16 =
+ acpi_rs_encode_bitmask(source,
+ *ACPI_ADD_PTR(u8, resource,
+ info->value));
+ ACPI_MOVE_16_TO_16(destination, &temp16);
+ break;
+
+ case ACPI_RSC_EXIT_LE:
+ /*
+ * control - Exit conversion if less than or equal
+ */
+ if (item_count <= info->value) {
+ goto exit;
+ }
+ break;
+
+ case ACPI_RSC_EXIT_NE:
+ /*
+ * control - Exit conversion if not equal
+ */
+ switch (COMPARE_OPCODE(info)) {
+ case ACPI_RSC_COMPARE_VALUE:
+
+ if (*ACPI_ADD_PTR(u8, resource,
+ COMPARE_TARGET(info)) !=
+ COMPARE_VALUE(info)) {
+ goto exit;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid conversion sub-opcode"));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+ break;
+
+ case ACPI_RSC_EXIT_EQ:
+ /*
+ * control - Exit conversion if equal
+ */
+ if (*ACPI_ADD_PTR(u8, resource,
+ COMPARE_TARGET(info)) ==
+ COMPARE_VALUE(info)) {
+ goto exit;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Invalid conversion opcode"));
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ count--;
+ info++;
+ }
+
+exit:
+ return_ACPI_STATUS(AE_OK);
+}
+
+#if 0
+/* Previous resource validations */
+
+if (aml->ext_address64.revision_ID != AML_RESOURCE_EXTENDED_ADDRESS_REVISION) {
+ return_ACPI_STATUS(AE_SUPPORT);
+}
+
+if (resource->data.start_dpf.performance_robustness >= 3) {
+ return_ACPI_STATUS(AE_AML_BAD_RESOURCE_VALUE);
+}
+
+if (((aml->irq.flags & 0x09) == 0x00) || ((aml->irq.flags & 0x09) == 0x09)) {
+ /*
+ * Only [active_high, edge_sensitive] or [active_low, level_sensitive]
+ * polarity/trigger interrupts are allowed (ACPI spec, section
+ * "IRQ Format"), so 0x00 and 0x09 are illegal.
+ */
+ ACPI_ERROR((AE_INFO,
+ "Invalid interrupt polarity/trigger in resource list, 0x%X",
+ aml->irq.flags));
+ return_ACPI_STATUS(AE_BAD_DATA);
+}
+
+resource->data.extended_irq.interrupt_count = temp8;
+if (temp8 < 1) {
+
+ /* Must have at least one IRQ */
+
+ return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH);
+}
+
+if (resource->data.dma.transfer == 0x03) {
+ ACPI_ERROR((AE_INFO, "Invalid DMA.Transfer preference (3)"));
+ return_ACPI_STATUS(AE_BAD_DATA);
+}
+#endif
diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c
new file mode 100644
index 000000000..f92679565
--- /dev/null
+++ b/drivers/acpi/acpica/rsserial.c
@@ -0,0 +1,872 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsserial - GPIO/serial_bus resource descriptors
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsserial")
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_gpio
+ *
+ ******************************************************************************/
+struct acpi_rsconvert_info acpi_rs_convert_gpio[18] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_GPIO,
+ ACPI_RS_SIZE(struct acpi_resource_gpio),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_gpio)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_GPIO,
+ sizeof(struct aml_resource_gpio),
+ 0},
+
+ /*
+ * These fields are contiguous in both the source and destination:
+ * revision_id
+ * connection_type
+ */
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.gpio.revision_id),
+ AML_OFFSET(gpio.revision_id),
+ 2},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.gpio.producer_consumer),
+ AML_OFFSET(gpio.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.gpio.shareable),
+ AML_OFFSET(gpio.int_flags),
+ 3},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.gpio.wake_capable),
+ AML_OFFSET(gpio.int_flags),
+ 4},
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.gpio.io_restriction),
+ AML_OFFSET(gpio.int_flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.gpio.triggering),
+ AML_OFFSET(gpio.int_flags),
+ 0},
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.gpio.polarity),
+ AML_OFFSET(gpio.int_flags),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.gpio.pin_config),
+ AML_OFFSET(gpio.pin_config),
+ 1},
+
+ /*
+ * These fields are contiguous in both the source and destination:
+ * drive_strength
+ * debounce_timeout
+ */
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.gpio.drive_strength),
+ AML_OFFSET(gpio.drive_strength),
+ 2},
+
+ /* Pin Table */
+
+ {ACPI_RSC_COUNT_GPIO_PIN, ACPI_RS_OFFSET(data.gpio.pin_table_length),
+ AML_OFFSET(gpio.pin_table_offset),
+ AML_OFFSET(gpio.res_source_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_PIN, ACPI_RS_OFFSET(data.gpio.pin_table),
+ AML_OFFSET(gpio.pin_table_offset),
+ 0},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.gpio.resource_source.index),
+ AML_OFFSET(gpio.res_source_index),
+ 1},
+
+ {ACPI_RSC_COUNT_GPIO_RES,
+ ACPI_RS_OFFSET(data.gpio.resource_source.string_length),
+ AML_OFFSET(gpio.res_source_offset),
+ AML_OFFSET(gpio.vendor_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RS_OFFSET(data.gpio.resource_source.string_ptr),
+ AML_OFFSET(gpio.res_source_offset),
+ 0},
+
+ /* Vendor Data */
+
+ {ACPI_RSC_COUNT_GPIO_VEN, ACPI_RS_OFFSET(data.gpio.vendor_length),
+ AML_OFFSET(gpio.vendor_length),
+ 1},
+
+ {ACPI_RSC_MOVE_GPIO_RES, ACPI_RS_OFFSET(data.gpio.vendor_data),
+ AML_OFFSET(gpio.vendor_offset),
+ 0},
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_pinfunction
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_pin_function[13] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_PIN_FUNCTION,
+ ACPI_RS_SIZE(struct acpi_resource_pin_function),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_pin_function)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_PIN_FUNCTION,
+ sizeof(struct aml_resource_pin_function),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.pin_function.revision_id),
+ AML_OFFSET(pin_function.revision_id),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.pin_function.shareable),
+ AML_OFFSET(pin_function.flags),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.pin_function.pin_config),
+ AML_OFFSET(pin_function.pin_config),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.pin_function.function_number),
+ AML_OFFSET(pin_function.function_number),
+ 2},
+
+ /* Pin Table */
+
+ /*
+ * It is OK to use GPIO operations here because none of them refer GPIO
+ * structures directly but instead use offsets given here.
+ */
+
+ {ACPI_RSC_COUNT_GPIO_PIN,
+ ACPI_RS_OFFSET(data.pin_function.pin_table_length),
+ AML_OFFSET(pin_function.pin_table_offset),
+ AML_OFFSET(pin_function.res_source_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_PIN, ACPI_RS_OFFSET(data.pin_function.pin_table),
+ AML_OFFSET(pin_function.pin_table_offset),
+ 0},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8,
+ ACPI_RS_OFFSET(data.pin_function.resource_source.index),
+ AML_OFFSET(pin_function.res_source_index),
+ 1},
+
+ {ACPI_RSC_COUNT_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_function.resource_source.string_length),
+ AML_OFFSET(pin_function.res_source_offset),
+ AML_OFFSET(pin_function.vendor_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_function.resource_source.string_ptr),
+ AML_OFFSET(pin_function.res_source_offset),
+ 0},
+
+ /* Vendor Data */
+
+ {ACPI_RSC_COUNT_GPIO_VEN,
+ ACPI_RS_OFFSET(data.pin_function.vendor_length),
+ AML_OFFSET(pin_function.vendor_length),
+ 1},
+
+ {ACPI_RSC_MOVE_GPIO_RES, ACPI_RS_OFFSET(data.pin_function.vendor_data),
+ AML_OFFSET(pin_function.vendor_offset),
+ 0},
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_csi2_serial_bus
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_csi2_serial_bus[14] = {
+ { ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS,
+ ACPI_RS_SIZE(struct acpi_resource_csi2_serialbus),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_csi2_serial_bus) },
+
+ { ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS,
+ sizeof(struct aml_resource_csi2_serialbus),
+ 0 },
+
+ { ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.revision_id),
+ AML_OFFSET(common_serial_bus.revision_id),
+ 1 },
+
+ { ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.csi2_serial_bus.type),
+ AML_OFFSET(csi2_serial_bus.type),
+ 1 },
+
+ { ACPI_RSC_1BITFLAG,
+ ACPI_RS_OFFSET(data.csi2_serial_bus.producer_consumer),
+ AML_OFFSET(csi2_serial_bus.flags),
+ 1 },
+
+ { ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.csi2_serial_bus.slave_mode),
+ AML_OFFSET(csi2_serial_bus.flags),
+ 0 },
+
+ { ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.csi2_serial_bus.phy_type),
+ AML_OFFSET(csi2_serial_bus.type_specific_flags),
+ 0 },
+
+ { ACPI_RSC_6BITFLAG,
+ ACPI_RS_OFFSET(data.csi2_serial_bus.local_port_instance),
+ AML_OFFSET(csi2_serial_bus.type_specific_flags),
+ 2 },
+
+ { ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.csi2_serial_bus.type_revision_id),
+ AML_OFFSET(csi2_serial_bus.type_revision_id),
+ 1 },
+
+ /* Vendor data */
+
+ { ACPI_RSC_COUNT_SERIAL_VEN,
+ ACPI_RS_OFFSET(data.csi2_serial_bus.vendor_length),
+ AML_OFFSET(csi2_serial_bus.type_data_length),
+ AML_RESOURCE_CSI2_MIN_DATA_LEN },
+
+ { ACPI_RSC_MOVE_SERIAL_VEN,
+ ACPI_RS_OFFSET(data.csi2_serial_bus.vendor_data),
+ 0,
+ sizeof(struct aml_resource_csi2_serialbus) },
+
+ /* Resource Source */
+
+ { ACPI_RSC_MOVE8,
+ ACPI_RS_OFFSET(data.csi2_serial_bus.resource_source.index),
+ AML_OFFSET(csi2_serial_bus.res_source_index),
+ 1 },
+
+ { ACPI_RSC_COUNT_SERIAL_RES,
+ ACPI_RS_OFFSET(data.csi2_serial_bus.resource_source.string_length),
+ AML_OFFSET(csi2_serial_bus.type_data_length),
+ sizeof(struct aml_resource_csi2_serialbus) },
+
+ { ACPI_RSC_MOVE_SERIAL_RES,
+ ACPI_RS_OFFSET(data.csi2_serial_bus.resource_source.string_ptr),
+ AML_OFFSET(csi2_serial_bus.type_data_length),
+ sizeof(struct aml_resource_csi2_serialbus) },
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_i2c_serial_bus
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[17] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS,
+ ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_i2c_serial_bus)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS,
+ sizeof(struct aml_resource_i2c_serialbus),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.revision_id),
+ AML_OFFSET(common_serial_bus.revision_id),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.type),
+ AML_OFFSET(common_serial_bus.type),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.common_serial_bus.slave_mode),
+ AML_OFFSET(common_serial_bus.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG,
+ ACPI_RS_OFFSET(data.common_serial_bus.producer_consumer),
+ AML_OFFSET(common_serial_bus.flags),
+ 1},
+
+ {ACPI_RSC_1BITFLAG,
+ ACPI_RS_OFFSET(data.common_serial_bus.connection_sharing),
+ AML_OFFSET(common_serial_bus.flags),
+ 2},
+
+ {ACPI_RSC_MOVE8,
+ ACPI_RS_OFFSET(data.common_serial_bus.type_revision_id),
+ AML_OFFSET(common_serial_bus.type_revision_id),
+ 1},
+
+ {ACPI_RSC_MOVE16,
+ ACPI_RS_OFFSET(data.common_serial_bus.type_data_length),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ 1},
+
+ /* Vendor data */
+
+ {ACPI_RSC_COUNT_SERIAL_VEN,
+ ACPI_RS_OFFSET(data.common_serial_bus.vendor_length),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ AML_RESOURCE_I2C_MIN_DATA_LEN},
+
+ {ACPI_RSC_MOVE_SERIAL_VEN,
+ ACPI_RS_OFFSET(data.common_serial_bus.vendor_data),
+ 0,
+ sizeof(struct aml_resource_i2c_serialbus)},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8,
+ ACPI_RS_OFFSET(data.common_serial_bus.resource_source.index),
+ AML_OFFSET(common_serial_bus.res_source_index),
+ 1},
+
+ {ACPI_RSC_COUNT_SERIAL_RES,
+ ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_length),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ sizeof(struct aml_resource_common_serialbus)},
+
+ {ACPI_RSC_MOVE_SERIAL_RES,
+ ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_ptr),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ sizeof(struct aml_resource_common_serialbus)},
+
+ /* I2C bus type specific */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.i2c_serial_bus.access_mode),
+ AML_OFFSET(i2c_serial_bus.type_specific_flags),
+ 0},
+
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.i2c_serial_bus.connection_speed),
+ AML_OFFSET(i2c_serial_bus.connection_speed),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.i2c_serial_bus.slave_address),
+ AML_OFFSET(i2c_serial_bus.slave_address),
+ 1},
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_spi_serial_bus
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_spi_serial_bus[21] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS,
+ ACPI_RS_SIZE(struct acpi_resource_spi_serialbus),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_spi_serial_bus)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS,
+ sizeof(struct aml_resource_spi_serialbus),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.revision_id),
+ AML_OFFSET(common_serial_bus.revision_id),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.type),
+ AML_OFFSET(common_serial_bus.type),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.common_serial_bus.slave_mode),
+ AML_OFFSET(common_serial_bus.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG,
+ ACPI_RS_OFFSET(data.common_serial_bus.producer_consumer),
+ AML_OFFSET(common_serial_bus.flags),
+ 1},
+
+ {ACPI_RSC_1BITFLAG,
+ ACPI_RS_OFFSET(data.common_serial_bus.connection_sharing),
+ AML_OFFSET(common_serial_bus.flags),
+ 2},
+
+ {ACPI_RSC_MOVE8,
+ ACPI_RS_OFFSET(data.common_serial_bus.type_revision_id),
+ AML_OFFSET(common_serial_bus.type_revision_id),
+ 1},
+
+ {ACPI_RSC_MOVE16,
+ ACPI_RS_OFFSET(data.common_serial_bus.type_data_length),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ 1},
+
+ /* Vendor data */
+
+ {ACPI_RSC_COUNT_SERIAL_VEN,
+ ACPI_RS_OFFSET(data.common_serial_bus.vendor_length),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ AML_RESOURCE_SPI_MIN_DATA_LEN},
+
+ {ACPI_RSC_MOVE_SERIAL_VEN,
+ ACPI_RS_OFFSET(data.common_serial_bus.vendor_data),
+ 0,
+ sizeof(struct aml_resource_spi_serialbus)},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8,
+ ACPI_RS_OFFSET(data.common_serial_bus.resource_source.index),
+ AML_OFFSET(common_serial_bus.res_source_index),
+ 1},
+
+ {ACPI_RSC_COUNT_SERIAL_RES,
+ ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_length),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ sizeof(struct aml_resource_common_serialbus)},
+
+ {ACPI_RSC_MOVE_SERIAL_RES,
+ ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_ptr),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ sizeof(struct aml_resource_common_serialbus)},
+
+ /* Spi bus type specific */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.spi_serial_bus.wire_mode),
+ AML_OFFSET(spi_serial_bus.type_specific_flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.spi_serial_bus.device_polarity),
+ AML_OFFSET(spi_serial_bus.type_specific_flags),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.spi_serial_bus.data_bit_length),
+ AML_OFFSET(spi_serial_bus.data_bit_length),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.spi_serial_bus.clock_phase),
+ AML_OFFSET(spi_serial_bus.clock_phase),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.spi_serial_bus.clock_polarity),
+ AML_OFFSET(spi_serial_bus.clock_polarity),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.spi_serial_bus.device_selection),
+ AML_OFFSET(spi_serial_bus.device_selection),
+ 1},
+
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.spi_serial_bus.connection_speed),
+ AML_OFFSET(spi_serial_bus.connection_speed),
+ 1},
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_uart_serial_bus
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_uart_serial_bus[23] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS,
+ ACPI_RS_SIZE(struct acpi_resource_uart_serialbus),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_uart_serial_bus)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS,
+ sizeof(struct aml_resource_uart_serialbus),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.revision_id),
+ AML_OFFSET(common_serial_bus.revision_id),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.type),
+ AML_OFFSET(common_serial_bus.type),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.common_serial_bus.slave_mode),
+ AML_OFFSET(common_serial_bus.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG,
+ ACPI_RS_OFFSET(data.common_serial_bus.producer_consumer),
+ AML_OFFSET(common_serial_bus.flags),
+ 1},
+
+ {ACPI_RSC_1BITFLAG,
+ ACPI_RS_OFFSET(data.common_serial_bus.connection_sharing),
+ AML_OFFSET(common_serial_bus.flags),
+ 2},
+
+ {ACPI_RSC_MOVE8,
+ ACPI_RS_OFFSET(data.common_serial_bus.type_revision_id),
+ AML_OFFSET(common_serial_bus.type_revision_id),
+ 1},
+
+ {ACPI_RSC_MOVE16,
+ ACPI_RS_OFFSET(data.common_serial_bus.type_data_length),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ 1},
+
+ /* Vendor data */
+
+ {ACPI_RSC_COUNT_SERIAL_VEN,
+ ACPI_RS_OFFSET(data.common_serial_bus.vendor_length),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ AML_RESOURCE_UART_MIN_DATA_LEN},
+
+ {ACPI_RSC_MOVE_SERIAL_VEN,
+ ACPI_RS_OFFSET(data.common_serial_bus.vendor_data),
+ 0,
+ sizeof(struct aml_resource_uart_serialbus)},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8,
+ ACPI_RS_OFFSET(data.common_serial_bus.resource_source.index),
+ AML_OFFSET(common_serial_bus.res_source_index),
+ 1},
+
+ {ACPI_RSC_COUNT_SERIAL_RES,
+ ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_length),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ sizeof(struct aml_resource_common_serialbus)},
+
+ {ACPI_RSC_MOVE_SERIAL_RES,
+ ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_ptr),
+ AML_OFFSET(common_serial_bus.type_data_length),
+ sizeof(struct aml_resource_common_serialbus)},
+
+ /* Uart bus type specific */
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.flow_control),
+ AML_OFFSET(uart_serial_bus.type_specific_flags),
+ 0},
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.stop_bits),
+ AML_OFFSET(uart_serial_bus.type_specific_flags),
+ 2},
+
+ {ACPI_RSC_3BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.data_bits),
+ AML_OFFSET(uart_serial_bus.type_specific_flags),
+ 4},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.endian),
+ AML_OFFSET(uart_serial_bus.type_specific_flags),
+ 7},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.uart_serial_bus.parity),
+ AML_OFFSET(uart_serial_bus.parity),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.uart_serial_bus.lines_enabled),
+ AML_OFFSET(uart_serial_bus.lines_enabled),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.uart_serial_bus.rx_fifo_size),
+ AML_OFFSET(uart_serial_bus.rx_fifo_size),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.uart_serial_bus.tx_fifo_size),
+ AML_OFFSET(uart_serial_bus.tx_fifo_size),
+ 1},
+
+ {ACPI_RSC_MOVE32,
+ ACPI_RS_OFFSET(data.uart_serial_bus.default_baud_rate),
+ AML_OFFSET(uart_serial_bus.default_baud_rate),
+ 1},
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_pin_config
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_pin_config[14] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_PIN_CONFIG,
+ ACPI_RS_SIZE(struct acpi_resource_pin_config),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_pin_config)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_PIN_CONFIG,
+ sizeof(struct aml_resource_pin_config),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.pin_config.revision_id),
+ AML_OFFSET(pin_config.revision_id),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.pin_config.shareable),
+ AML_OFFSET(pin_config.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.pin_config.producer_consumer),
+ AML_OFFSET(pin_config.flags),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.pin_config.pin_config_type),
+ AML_OFFSET(pin_config.pin_config_type),
+ 1},
+
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.pin_config.pin_config_value),
+ AML_OFFSET(pin_config.pin_config_value),
+ 1},
+
+ /* Pin Table */
+
+ /*
+ * It is OK to use GPIO operations here because none of them refer GPIO
+ * structures directly but instead use offsets given here.
+ */
+
+ {ACPI_RSC_COUNT_GPIO_PIN,
+ ACPI_RS_OFFSET(data.pin_config.pin_table_length),
+ AML_OFFSET(pin_config.pin_table_offset),
+ AML_OFFSET(pin_config.res_source_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_PIN, ACPI_RS_OFFSET(data.pin_config.pin_table),
+ AML_OFFSET(pin_config.pin_table_offset),
+ 0},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.pin_config.resource_source.index),
+ AML_OFFSET(pin_config.res_source_index),
+ 1},
+
+ {ACPI_RSC_COUNT_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_config.resource_source.string_length),
+ AML_OFFSET(pin_config.res_source_offset),
+ AML_OFFSET(pin_config.vendor_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_config.resource_source.string_ptr),
+ AML_OFFSET(pin_config.res_source_offset),
+ 0},
+
+ /* Vendor Data */
+
+ {ACPI_RSC_COUNT_GPIO_VEN, ACPI_RS_OFFSET(data.pin_config.vendor_length),
+ AML_OFFSET(pin_config.vendor_length),
+ 1},
+
+ {ACPI_RSC_MOVE_GPIO_RES, ACPI_RS_OFFSET(data.pin_config.vendor_data),
+ AML_OFFSET(pin_config.vendor_offset),
+ 0},
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_pin_group
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_pin_group[10] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_PIN_GROUP,
+ ACPI_RS_SIZE(struct acpi_resource_pin_group),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_pin_group)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_PIN_GROUP,
+ sizeof(struct aml_resource_pin_group),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.pin_group.revision_id),
+ AML_OFFSET(pin_group.revision_id),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.pin_group.producer_consumer),
+ AML_OFFSET(pin_group.flags),
+ 0},
+
+ /* Pin Table */
+
+ /*
+ * It is OK to use GPIO operations here because none of them refer GPIO
+ * structures directly but instead use offsets given here.
+ */
+
+ {ACPI_RSC_COUNT_GPIO_PIN,
+ ACPI_RS_OFFSET(data.pin_group.pin_table_length),
+ AML_OFFSET(pin_group.pin_table_offset),
+ AML_OFFSET(pin_group.label_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_PIN, ACPI_RS_OFFSET(data.pin_group.pin_table),
+ AML_OFFSET(pin_group.pin_table_offset),
+ 0},
+
+ /* Resource Label */
+
+ {ACPI_RSC_COUNT_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group.resource_label.string_length),
+ AML_OFFSET(pin_group.label_offset),
+ AML_OFFSET(pin_group.vendor_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group.resource_label.string_ptr),
+ AML_OFFSET(pin_group.label_offset),
+ 0},
+
+ /* Vendor Data */
+
+ {ACPI_RSC_COUNT_GPIO_VEN, ACPI_RS_OFFSET(data.pin_group.vendor_length),
+ AML_OFFSET(pin_group.vendor_length),
+ 1},
+
+ {ACPI_RSC_MOVE_GPIO_RES, ACPI_RS_OFFSET(data.pin_group.vendor_data),
+ AML_OFFSET(pin_group.vendor_offset),
+ 0},
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_pin_group_function
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_pin_group_function[13] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION,
+ ACPI_RS_SIZE(struct acpi_resource_pin_group_function),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_pin_group_function)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_PIN_GROUP_FUNCTION,
+ sizeof(struct aml_resource_pin_group_function),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.pin_group_function.revision_id),
+ AML_OFFSET(pin_group_function.revision_id),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.pin_group_function.shareable),
+ AML_OFFSET(pin_group_function.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG,
+ ACPI_RS_OFFSET(data.pin_group_function.producer_consumer),
+ AML_OFFSET(pin_group_function.flags),
+ 1},
+
+ {ACPI_RSC_MOVE16,
+ ACPI_RS_OFFSET(data.pin_group_function.function_number),
+ AML_OFFSET(pin_group_function.function_number),
+ 1},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8,
+ ACPI_RS_OFFSET(data.pin_group_function.resource_source.index),
+ AML_OFFSET(pin_group_function.res_source_index),
+ 1},
+
+ {ACPI_RSC_COUNT_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group_function.resource_source.string_length),
+ AML_OFFSET(pin_group_function.res_source_offset),
+ AML_OFFSET(pin_group_function.res_source_label_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group_function.resource_source.string_ptr),
+ AML_OFFSET(pin_group_function.res_source_offset),
+ 0},
+
+ /* Resource Source Label */
+
+ {ACPI_RSC_COUNT_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group_function.resource_source_label.
+ string_length),
+ AML_OFFSET(pin_group_function.res_source_label_offset),
+ AML_OFFSET(pin_group_function.vendor_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group_function.resource_source_label.
+ string_ptr),
+ AML_OFFSET(pin_group_function.res_source_label_offset),
+ 0},
+
+ /* Vendor Data */
+
+ {ACPI_RSC_COUNT_GPIO_VEN,
+ ACPI_RS_OFFSET(data.pin_group_function.vendor_length),
+ AML_OFFSET(pin_group_function.vendor_length),
+ 1},
+
+ {ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group_function.vendor_data),
+ AML_OFFSET(pin_group_function.vendor_offset),
+ 0},
+};
+
+/*******************************************************************************
+ *
+ * acpi_rs_convert_pin_group_config
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_pin_group_config[14] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG,
+ ACPI_RS_SIZE(struct acpi_resource_pin_group_config),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_pin_group_config)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_PIN_GROUP_CONFIG,
+ sizeof(struct aml_resource_pin_group_config),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.pin_group_config.revision_id),
+ AML_OFFSET(pin_group_config.revision_id),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.pin_group_config.shareable),
+ AML_OFFSET(pin_group_config.flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG,
+ ACPI_RS_OFFSET(data.pin_group_config.producer_consumer),
+ AML_OFFSET(pin_group_config.flags),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.pin_group_config.pin_config_type),
+ AML_OFFSET(pin_group_config.pin_config_type),
+ 1},
+
+ {ACPI_RSC_MOVE32,
+ ACPI_RS_OFFSET(data.pin_group_config.pin_config_value),
+ AML_OFFSET(pin_group_config.pin_config_value),
+ 1},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8,
+ ACPI_RS_OFFSET(data.pin_group_config.resource_source.index),
+ AML_OFFSET(pin_group_config.res_source_index),
+ 1},
+
+ {ACPI_RSC_COUNT_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group_config.resource_source.string_length),
+ AML_OFFSET(pin_group_config.res_source_offset),
+ AML_OFFSET(pin_group_config.res_source_label_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group_config.resource_source.string_ptr),
+ AML_OFFSET(pin_group_config.res_source_offset),
+ 0},
+
+ /* Resource Source Label */
+
+ {ACPI_RSC_COUNT_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group_config.resource_source_label.
+ string_length),
+ AML_OFFSET(pin_group_config.res_source_label_offset),
+ AML_OFFSET(pin_group_config.vendor_offset)},
+
+ {ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group_config.resource_source_label.string_ptr),
+ AML_OFFSET(pin_group_config.res_source_label_offset),
+ 0},
+
+ /* Vendor Data */
+
+ {ACPI_RSC_COUNT_GPIO_VEN,
+ ACPI_RS_OFFSET(data.pin_group_config.vendor_length),
+ AML_OFFSET(pin_group_config.vendor_length),
+ 1},
+
+ {ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RS_OFFSET(data.pin_group_config.vendor_data),
+ AML_OFFSET(pin_group_config.vendor_offset),
+ 0},
+};
diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c
new file mode 100644
index 000000000..8c1a68629
--- /dev/null
+++ b/drivers/acpi/acpica/rsutils.c
@@ -0,0 +1,755 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsutils - Utilities for the resource manager
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsutils")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_decode_bitmask
+ *
+ * PARAMETERS: mask - Bitmask to decode
+ * list - Where the converted list is returned
+ *
+ * RETURN: Count of bits set (length of list)
+ *
+ * DESCRIPTION: Convert a bit mask into a list of values
+ *
+ ******************************************************************************/
+u8 acpi_rs_decode_bitmask(u16 mask, u8 * list)
+{
+ u8 i;
+ u8 bit_count;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Decode the mask bits */
+
+ for (i = 0, bit_count = 0; mask; i++) {
+ if (mask & 0x0001) {
+ list[bit_count] = i;
+ bit_count++;
+ }
+
+ mask >>= 1;
+ }
+
+ return (bit_count);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_encode_bitmask
+ *
+ * PARAMETERS: list - List of values to encode
+ * count - Length of list
+ *
+ * RETURN: Encoded bitmask
+ *
+ * DESCRIPTION: Convert a list of values to an encoded bitmask
+ *
+ ******************************************************************************/
+
+u16 acpi_rs_encode_bitmask(u8 * list, u8 count)
+{
+ u32 i;
+ u16 mask;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Encode the list into a single bitmask */
+
+ for (i = 0, mask = 0; i < count; i++) {
+ mask |= (0x1 << list[i]);
+ }
+
+ return (mask);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_move_data
+ *
+ * PARAMETERS: destination - Pointer to the destination descriptor
+ * source - Pointer to the source descriptor
+ * item_count - How many items to move
+ * move_type - Byte width
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Move multiple data items from one descriptor to another. Handles
+ * alignment issues and endian issues if necessary, as configured
+ * via the ACPI_MOVE_* macros. (This is why a memcpy is not used)
+ *
+ ******************************************************************************/
+
+void
+acpi_rs_move_data(void *destination, void *source, u16 item_count, u8 move_type)
+{
+ u32 i;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* One move per item */
+
+ for (i = 0; i < item_count; i++) {
+ switch (move_type) {
+ /*
+ * For the 8-bit case, we can perform the move all at once
+ * since there are no alignment or endian issues
+ */
+ case ACPI_RSC_MOVE8:
+ case ACPI_RSC_MOVE_GPIO_RES:
+ case ACPI_RSC_MOVE_SERIAL_VEN:
+ case ACPI_RSC_MOVE_SERIAL_RES:
+
+ memcpy(destination, source, item_count);
+ return;
+
+ /*
+ * 16-, 32-, and 64-bit cases must use the move macros that perform
+ * endian conversion and/or accommodate hardware that cannot perform
+ * misaligned memory transfers
+ */
+ case ACPI_RSC_MOVE16:
+ case ACPI_RSC_MOVE_GPIO_PIN:
+
+ ACPI_MOVE_16_TO_16(&ACPI_CAST_PTR(u16, destination)[i],
+ &ACPI_CAST_PTR(u16, source)[i]);
+ break;
+
+ case ACPI_RSC_MOVE32:
+
+ ACPI_MOVE_32_TO_32(&ACPI_CAST_PTR(u32, destination)[i],
+ &ACPI_CAST_PTR(u32, source)[i]);
+ break;
+
+ case ACPI_RSC_MOVE64:
+
+ ACPI_MOVE_64_TO_64(&ACPI_CAST_PTR(u64, destination)[i],
+ &ACPI_CAST_PTR(u64, source)[i]);
+ break;
+
+ default:
+
+ return;
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_set_resource_length
+ *
+ * PARAMETERS: total_length - Length of the AML descriptor, including
+ * the header and length fields.
+ * aml - Pointer to the raw AML descriptor
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Set the resource_length field of an AML
+ * resource descriptor, both Large and Small descriptors are
+ * supported automatically. Note: Descriptor Type field must
+ * be valid.
+ *
+ ******************************************************************************/
+
+void
+acpi_rs_set_resource_length(acpi_rsdesc_size total_length,
+ union aml_resource *aml)
+{
+ acpi_rs_length resource_length;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Length is the total descriptor length minus the header length */
+
+ resource_length = (acpi_rs_length)
+ (total_length - acpi_ut_get_resource_header_length(aml));
+
+ /* Length is stored differently for large and small descriptors */
+
+ if (aml->small_header.descriptor_type & ACPI_RESOURCE_NAME_LARGE) {
+
+ /* Large descriptor -- bytes 1-2 contain the 16-bit length */
+
+ ACPI_MOVE_16_TO_16(&aml->large_header.resource_length,
+ &resource_length);
+ } else {
+ /*
+ * Small descriptor -- bits 2:0 of byte 0 contain the length
+ * Clear any existing length, preserving descriptor type bits
+ */
+ aml->small_header.descriptor_type = (u8)
+ ((aml->small_header.descriptor_type &
+ ~ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK)
+ | resource_length);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_set_resource_header
+ *
+ * PARAMETERS: descriptor_type - Byte to be inserted as the type
+ * total_length - Length of the AML descriptor, including
+ * the header and length fields.
+ * aml - Pointer to the raw AML descriptor
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Set the descriptor_type and resource_length fields of an AML
+ * resource descriptor, both Large and Small descriptors are
+ * supported automatically
+ *
+ ******************************************************************************/
+
+void
+acpi_rs_set_resource_header(u8 descriptor_type,
+ acpi_rsdesc_size total_length,
+ union aml_resource *aml)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /* Set the Resource Type */
+
+ aml->small_header.descriptor_type = descriptor_type;
+
+ /* Set the Resource Length */
+
+ acpi_rs_set_resource_length(total_length, aml);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_strcpy
+ *
+ * PARAMETERS: destination - Pointer to the destination string
+ * source - Pointer to the source string
+ *
+ * RETURN: String length, including NULL terminator
+ *
+ * DESCRIPTION: Local string copy that returns the string length, saving a
+ * strcpy followed by a strlen.
+ *
+ ******************************************************************************/
+
+static u16 acpi_rs_strcpy(char *destination, char *source)
+{
+ u16 i;
+
+ ACPI_FUNCTION_ENTRY();
+
+ for (i = 0; source[i]; i++) {
+ destination[i] = source[i];
+ }
+
+ destination[i] = 0;
+
+ /* Return string length including the NULL terminator */
+
+ return ((u16) (i + 1));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_get_resource_source
+ *
+ * PARAMETERS: resource_length - Length field of the descriptor
+ * minimum_length - Minimum length of the descriptor (minus
+ * any optional fields)
+ * resource_source - Where the resource_source is returned
+ * aml - Pointer to the raw AML descriptor
+ * string_ptr - (optional) where to store the actual
+ * resource_source string
+ *
+ * RETURN: Length of the string plus NULL terminator, rounded up to native
+ * word boundary
+ *
+ * DESCRIPTION: Copy the optional resource_source data from a raw AML descriptor
+ * to an internal resource descriptor
+ *
+ ******************************************************************************/
+
+acpi_rs_length
+acpi_rs_get_resource_source(acpi_rs_length resource_length,
+ acpi_rs_length minimum_length,
+ struct acpi_resource_source * resource_source,
+ union aml_resource * aml, char *string_ptr)
+{
+ acpi_rsdesc_size total_length;
+ u8 *aml_resource_source;
+
+ ACPI_FUNCTION_ENTRY();
+
+ total_length =
+ resource_length + sizeof(struct aml_resource_large_header);
+ aml_resource_source = ACPI_ADD_PTR(u8, aml, minimum_length);
+
+ /*
+ * resource_source is present if the length of the descriptor is longer
+ * than the minimum length.
+ *
+ * Note: Some resource descriptors will have an additional null, so
+ * we add 1 to the minimum length.
+ */
+ if (total_length > (acpi_rsdesc_size)(minimum_length + 1)) {
+
+ /* Get the resource_source_index */
+
+ resource_source->index = aml_resource_source[0];
+
+ resource_source->string_ptr = string_ptr;
+ if (!string_ptr) {
+ /*
+ * String destination pointer is not specified; Set the String
+ * pointer to the end of the current resource_source structure.
+ */
+ resource_source->string_ptr =
+ ACPI_ADD_PTR(char, resource_source,
+ sizeof(struct acpi_resource_source));
+ }
+
+ /*
+ * In order for the Resource length to be a multiple of the native
+ * word, calculate the length of the string (+1 for NULL terminator)
+ * and expand to the next word multiple.
+ *
+ * Zero the entire area of the buffer.
+ */
+ total_length =
+ (u32)strlen(ACPI_CAST_PTR(char, &aml_resource_source[1])) +
+ 1;
+
+ total_length = (u32)ACPI_ROUND_UP_TO_NATIVE_WORD(total_length);
+
+ memset(resource_source->string_ptr, 0, total_length);
+
+ /* Copy the resource_source string to the destination */
+
+ resource_source->string_length =
+ acpi_rs_strcpy(resource_source->string_ptr,
+ ACPI_CAST_PTR(char,
+ &aml_resource_source[1]));
+
+ return ((acpi_rs_length)total_length);
+ }
+
+ /* resource_source is not present */
+
+ resource_source->index = 0;
+ resource_source->string_length = 0;
+ resource_source->string_ptr = NULL;
+ return (0);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_set_resource_source
+ *
+ * PARAMETERS: aml - Pointer to the raw AML descriptor
+ * minimum_length - Minimum length of the descriptor (minus
+ * any optional fields)
+ * resource_source - Internal resource_source
+
+ *
+ * RETURN: Total length of the AML descriptor
+ *
+ * DESCRIPTION: Convert an optional resource_source from internal format to a
+ * raw AML resource descriptor
+ *
+ ******************************************************************************/
+
+acpi_rsdesc_size
+acpi_rs_set_resource_source(union aml_resource *aml,
+ acpi_rs_length minimum_length,
+ struct acpi_resource_source *resource_source)
+{
+ u8 *aml_resource_source;
+ acpi_rsdesc_size descriptor_length;
+
+ ACPI_FUNCTION_ENTRY();
+
+ descriptor_length = minimum_length;
+
+ /* Non-zero string length indicates presence of a resource_source */
+
+ if (resource_source->string_length) {
+
+ /* Point to the end of the AML descriptor */
+
+ aml_resource_source = ACPI_ADD_PTR(u8, aml, minimum_length);
+
+ /* Copy the resource_source_index */
+
+ aml_resource_source[0] = (u8) resource_source->index;
+
+ /* Copy the resource_source string */
+
+ strcpy(ACPI_CAST_PTR(char, &aml_resource_source[1]),
+ resource_source->string_ptr);
+
+ /*
+ * Add the length of the string (+ 1 for null terminator) to the
+ * final descriptor length
+ */
+ descriptor_length += ((acpi_rsdesc_size)
+ resource_source->string_length + 1);
+ }
+
+ /* Return the new total length of the AML descriptor */
+
+ return (descriptor_length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_get_prt_method_data
+ *
+ * PARAMETERS: node - Device node
+ * ret_buffer - Pointer to a buffer structure for the
+ * results
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get the _PRT value of an object
+ * contained in an object specified by the handle passed in
+ *
+ * If the function fails an appropriate status will be returned
+ * and the contents of the callers buffer is undefined.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_get_prt_method_data(struct acpi_namespace_node *node,
+ struct acpi_buffer *ret_buffer)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(rs_get_prt_method_data);
+
+ /* Parameters guaranteed valid by caller */
+
+ /* Execute the method, no parameters */
+
+ status =
+ acpi_ut_evaluate_object(node, METHOD_NAME__PRT, ACPI_BTYPE_PACKAGE,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Create a resource linked list from the byte stream buffer that comes
+ * back from the _CRS method execution.
+ */
+ status = acpi_rs_create_pci_routing_table(obj_desc, ret_buffer);
+
+ /* On exit, we must delete the object returned by evaluate_object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_get_crs_method_data
+ *
+ * PARAMETERS: node - Device node
+ * ret_buffer - Pointer to a buffer structure for the
+ * results
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get the _CRS value of an object
+ * contained in an object specified by the handle passed in
+ *
+ * If the function fails an appropriate status will be returned
+ * and the contents of the callers buffer is undefined.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_get_crs_method_data(struct acpi_namespace_node *node,
+ struct acpi_buffer *ret_buffer)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(rs_get_crs_method_data);
+
+ /* Parameters guaranteed valid by caller */
+
+ /* Execute the method, no parameters */
+
+ status =
+ acpi_ut_evaluate_object(node, METHOD_NAME__CRS, ACPI_BTYPE_BUFFER,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Make the call to create a resource linked list from the
+ * byte stream buffer that comes back from the _CRS method
+ * execution.
+ */
+ status = acpi_rs_create_resource_list(obj_desc, ret_buffer);
+
+ /* On exit, we must delete the object returned by evaluateObject */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_get_prs_method_data
+ *
+ * PARAMETERS: node - Device node
+ * ret_buffer - Pointer to a buffer structure for the
+ * results
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get the _PRS value of an object
+ * contained in an object specified by the handle passed in
+ *
+ * If the function fails an appropriate status will be returned
+ * and the contents of the callers buffer is undefined.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_get_prs_method_data(struct acpi_namespace_node *node,
+ struct acpi_buffer *ret_buffer)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(rs_get_prs_method_data);
+
+ /* Parameters guaranteed valid by caller */
+
+ /* Execute the method, no parameters */
+
+ status =
+ acpi_ut_evaluate_object(node, METHOD_NAME__PRS, ACPI_BTYPE_BUFFER,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Make the call to create a resource linked list from the
+ * byte stream buffer that comes back from the _CRS method
+ * execution.
+ */
+ status = acpi_rs_create_resource_list(obj_desc, ret_buffer);
+
+ /* On exit, we must delete the object returned by evaluateObject */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_get_aei_method_data
+ *
+ * PARAMETERS: node - Device node
+ * ret_buffer - Pointer to a buffer structure for the
+ * results
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get the _AEI value of an object
+ * contained in an object specified by the handle passed in
+ *
+ * If the function fails an appropriate status will be returned
+ * and the contents of the callers buffer is undefined.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_get_aei_method_data(struct acpi_namespace_node *node,
+ struct acpi_buffer *ret_buffer)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(rs_get_aei_method_data);
+
+ /* Parameters guaranteed valid by caller */
+
+ /* Execute the method, no parameters */
+
+ status =
+ acpi_ut_evaluate_object(node, METHOD_NAME__AEI, ACPI_BTYPE_BUFFER,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Make the call to create a resource linked list from the
+ * byte stream buffer that comes back from the _CRS method
+ * execution.
+ */
+ status = acpi_rs_create_resource_list(obj_desc, ret_buffer);
+
+ /* On exit, we must delete the object returned by evaluateObject */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_get_method_data
+ *
+ * PARAMETERS: handle - Handle to the containing object
+ * path - Path to method, relative to Handle
+ * ret_buffer - Pointer to a buffer structure for the
+ * results
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get the _CRS or _PRS value of an
+ * object contained in an object specified by the handle passed in
+ *
+ * If the function fails an appropriate status will be returned
+ * and the contents of the callers buffer is undefined.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_get_method_data(acpi_handle handle,
+ const char *path, struct acpi_buffer *ret_buffer)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(rs_get_method_data);
+
+ /* Parameters guaranteed valid by caller */
+
+ /* Execute the method, no parameters */
+
+ status =
+ acpi_ut_evaluate_object(ACPI_CAST_PTR
+ (struct acpi_namespace_node, handle), path,
+ ACPI_BTYPE_BUFFER, &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Make the call to create a resource linked list from the
+ * byte stream buffer that comes back from the method
+ * execution.
+ */
+ status = acpi_rs_create_resource_list(obj_desc, ret_buffer);
+
+ /* On exit, we must delete the object returned by evaluate_object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_set_srs_method_data
+ *
+ * PARAMETERS: node - Device node
+ * in_buffer - Pointer to a buffer structure of the
+ * parameter
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to set the _SRS of an object contained
+ * in an object specified by the handle passed in
+ *
+ * If the function fails an appropriate status will be returned
+ * and the contents of the callers buffer is undefined.
+ *
+ * Note: Parameters guaranteed valid by caller
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_rs_set_srs_method_data(struct acpi_namespace_node *node,
+ struct acpi_buffer *in_buffer)
+{
+ struct acpi_evaluate_info *info;
+ union acpi_operand_object *args[2];
+ acpi_status status;
+ struct acpi_buffer buffer;
+
+ ACPI_FUNCTION_TRACE(rs_set_srs_method_data);
+
+ /* Allocate and initialize the evaluation information block */
+
+ info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
+ if (!info) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ info->prefix_node = node;
+ info->relative_pathname = METHOD_NAME__SRS;
+ info->parameters = args;
+ info->flags = ACPI_IGNORE_RETURN_VALUE;
+
+ /*
+ * The in_buffer parameter will point to a linked list of
+ * resource parameters. It needs to be formatted into a
+ * byte stream to be sent in as an input parameter to _SRS
+ *
+ * Convert the linked list into a byte stream
+ */
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ status = acpi_rs_create_aml_resources(in_buffer, &buffer);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ /* Create and initialize the method parameter object */
+
+ args[0] = acpi_ut_create_internal_object(ACPI_TYPE_BUFFER);
+ if (!args[0]) {
+ /*
+ * Must free the buffer allocated above (otherwise it is freed
+ * later)
+ */
+ ACPI_FREE(buffer.pointer);
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ args[0]->buffer.length = (u32) buffer.length;
+ args[0]->buffer.pointer = buffer.pointer;
+ args[0]->common.flags = AOPOBJ_DATA_VALID;
+ args[1] = NULL;
+
+ /* Execute the method, no return value is expected */
+
+ status = acpi_ns_evaluate(info);
+
+ /* Clean up and return the status from acpi_ns_evaluate */
+
+ acpi_ut_remove_reference(args[0]);
+
+cleanup:
+ ACPI_FREE(info);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c
new file mode 100644
index 000000000..c62be3d91
--- /dev/null
+++ b/drivers/acpi/acpica/rsxface.c
@@ -0,0 +1,628 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: rsxface - Public interfaces to the resource manager
+ *
+ ******************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ACPI_MODULE_NAME("rsxface")
+
+/* Local macros for 16,32-bit to 64-bit conversion */
+#define ACPI_COPY_FIELD(out, in, field) ((out)->field = (in)->field)
+#define ACPI_COPY_ADDRESS(out, in) \
+ ACPI_COPY_FIELD(out, in, resource_type); \
+ ACPI_COPY_FIELD(out, in, producer_consumer); \
+ ACPI_COPY_FIELD(out, in, decode); \
+ ACPI_COPY_FIELD(out, in, min_address_fixed); \
+ ACPI_COPY_FIELD(out, in, max_address_fixed); \
+ ACPI_COPY_FIELD(out, in, info); \
+ ACPI_COPY_FIELD(out, in, address.granularity); \
+ ACPI_COPY_FIELD(out, in, address.minimum); \
+ ACPI_COPY_FIELD(out, in, address.maximum); \
+ ACPI_COPY_FIELD(out, in, address.translation_offset); \
+ ACPI_COPY_FIELD(out, in, address.address_length); \
+ ACPI_COPY_FIELD(out, in, resource_source);
+/* Local prototypes */
+static acpi_status
+acpi_rs_match_vendor_resource(struct acpi_resource *resource, void *context);
+
+static acpi_status
+acpi_rs_validate_parameters(acpi_handle device_handle,
+ struct acpi_buffer *buffer,
+ struct acpi_namespace_node **return_node);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_validate_parameters
+ *
+ * PARAMETERS: device_handle - Handle to a device
+ * buffer - Pointer to a data buffer
+ * return_node - Pointer to where the device node is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Common parameter validation for resource interfaces
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_rs_validate_parameters(acpi_handle device_handle,
+ struct acpi_buffer *buffer,
+ struct acpi_namespace_node **return_node)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ ACPI_FUNCTION_TRACE(rs_validate_parameters);
+
+ /*
+ * Must have a valid handle to an ACPI device
+ */
+ if (!device_handle) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ node = acpi_ns_validate_handle(device_handle);
+ if (!node) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ if (node->type != ACPI_TYPE_DEVICE) {
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ /*
+ * Validate the user buffer object
+ *
+ * if there is a non-zero buffer length we also need a valid pointer in
+ * the buffer. If it's a zero buffer length, we'll be returning the
+ * needed buffer size (later), so keep going.
+ */
+ status = acpi_ut_validate_buffer(buffer);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ *return_node = node;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_irq_routing_table
+ *
+ * PARAMETERS: device_handle - Handle to the Bus device we are querying
+ * ret_buffer - Pointer to a buffer to receive the
+ * current resources for the device
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get the IRQ routing table for a
+ * specific bus. The caller must first acquire a handle for the
+ * desired bus. The routine table is placed in the buffer pointed
+ * to by the ret_buffer variable parameter.
+ *
+ * If the function fails an appropriate status will be returned
+ * and the value of ret_buffer is undefined.
+ *
+ * This function attempts to execute the _PRT method contained in
+ * the object indicated by the passed device_handle.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_get_irq_routing_table(acpi_handle device_handle,
+ struct acpi_buffer *ret_buffer)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ ACPI_FUNCTION_TRACE(acpi_get_irq_routing_table);
+
+ /* Validate parameters then dispatch to internal routine */
+
+ status = acpi_rs_validate_parameters(device_handle, ret_buffer, &node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_rs_get_prt_method_data(node, ret_buffer);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_irq_routing_table)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_current_resources
+ *
+ * PARAMETERS: device_handle - Handle to the device object for the
+ * device we are querying
+ * ret_buffer - Pointer to a buffer to receive the
+ * current resources for the device
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get the current resources for a
+ * specific device. The caller must first acquire a handle for
+ * the desired device. The resource data is placed in the buffer
+ * pointed to by the ret_buffer variable parameter.
+ *
+ * If the function fails an appropriate status will be returned
+ * and the value of ret_buffer is undefined.
+ *
+ * This function attempts to execute the _CRS method contained in
+ * the object indicated by the passed device_handle.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_current_resources(acpi_handle device_handle,
+ struct acpi_buffer *ret_buffer)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ ACPI_FUNCTION_TRACE(acpi_get_current_resources);
+
+ /* Validate parameters then dispatch to internal routine */
+
+ status = acpi_rs_validate_parameters(device_handle, ret_buffer, &node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_rs_get_crs_method_data(node, ret_buffer);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_current_resources)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_possible_resources
+ *
+ * PARAMETERS: device_handle - Handle to the device object for the
+ * device we are querying
+ * ret_buffer - Pointer to a buffer to receive the
+ * resources for the device
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get a list of the possible resources
+ * for a specific device. The caller must first acquire a handle
+ * for the desired device. The resource data is placed in the
+ * buffer pointed to by the ret_buffer variable.
+ *
+ * If the function fails an appropriate status will be returned
+ * and the value of ret_buffer is undefined.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_possible_resources(acpi_handle device_handle,
+ struct acpi_buffer *ret_buffer)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ ACPI_FUNCTION_TRACE(acpi_get_possible_resources);
+
+ /* Validate parameters then dispatch to internal routine */
+
+ status = acpi_rs_validate_parameters(device_handle, ret_buffer, &node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_rs_get_prs_method_data(node, ret_buffer);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_possible_resources)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_set_current_resources
+ *
+ * PARAMETERS: device_handle - Handle to the device object for the
+ * device we are setting resources
+ * in_buffer - Pointer to a buffer containing the
+ * resources to be set for the device
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to set the current resources for a
+ * specific device. The caller must first acquire a handle for
+ * the desired device. The resource data is passed to the routine
+ * the buffer pointed to by the in_buffer variable.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_set_current_resources(acpi_handle device_handle,
+ struct acpi_buffer *in_buffer)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ ACPI_FUNCTION_TRACE(acpi_set_current_resources);
+
+ /* Validate the buffer, don't allow zero length */
+
+ if ((!in_buffer) || (!in_buffer->pointer) || (!in_buffer->length)) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Validate parameters then dispatch to internal routine */
+
+ status = acpi_rs_validate_parameters(device_handle, in_buffer, &node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_rs_set_srs_method_data(node, in_buffer);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_set_current_resources)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_event_resources
+ *
+ * PARAMETERS: device_handle - Handle to the device object for the
+ * device we are getting resources
+ * in_buffer - Pointer to a buffer containing the
+ * resources to be set for the device
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get the event resources for a
+ * specific device. The caller must first acquire a handle for
+ * the desired device. The resource data is passed to the routine
+ * the buffer pointed to by the in_buffer variable. Uses the
+ * _AEI method.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_event_resources(acpi_handle device_handle,
+ struct acpi_buffer *ret_buffer)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node;
+
+ ACPI_FUNCTION_TRACE(acpi_get_event_resources);
+
+ /* Validate parameters then dispatch to internal routine */
+
+ status = acpi_rs_validate_parameters(device_handle, ret_buffer, &node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_rs_get_aei_method_data(node, ret_buffer);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_event_resources)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_resource_to_address64
+ *
+ * PARAMETERS: resource - Pointer to a resource
+ * out - Pointer to the users's return buffer
+ * (a struct acpi_resource_address64)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: If the resource is an address16, address32, or address64,
+ * copy it to the address64 return buffer. This saves the
+ * caller from having to duplicate code for different-sized
+ * addresses.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_resource_to_address64(struct acpi_resource *resource,
+ struct acpi_resource_address64 *out)
+{
+ struct acpi_resource_address16 *address16;
+ struct acpi_resource_address32 *address32;
+
+ if (!resource || !out) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Convert 16 or 32 address descriptor to 64 */
+
+ switch (resource->type) {
+ case ACPI_RESOURCE_TYPE_ADDRESS16:
+
+ address16 =
+ ACPI_CAST_PTR(struct acpi_resource_address16,
+ &resource->data);
+ ACPI_COPY_ADDRESS(out, address16);
+ break;
+
+ case ACPI_RESOURCE_TYPE_ADDRESS32:
+
+ address32 =
+ ACPI_CAST_PTR(struct acpi_resource_address32,
+ &resource->data);
+ ACPI_COPY_ADDRESS(out, address32);
+ break;
+
+ case ACPI_RESOURCE_TYPE_ADDRESS64:
+
+ /* Simple copy for 64 bit source */
+
+ memcpy(out, &resource->data,
+ sizeof(struct acpi_resource_address64));
+ break;
+
+ default:
+
+ return (AE_BAD_PARAMETER);
+ }
+
+ return (AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_resource_to_address64)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_vendor_resource
+ *
+ * PARAMETERS: device_handle - Handle for the parent device object
+ * name - Method name for the parent resource
+ * (METHOD_NAME__CRS or METHOD_NAME__PRS)
+ * uuid - Pointer to the UUID to be matched.
+ * includes both subtype and 16-byte UUID
+ * ret_buffer - Where the vendor resource is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Walk a resource template for the specified device to find a
+ * vendor-defined resource that matches the supplied UUID and
+ * UUID subtype. Returns a struct acpi_resource of type Vendor.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_vendor_resource(acpi_handle device_handle,
+ char *name,
+ struct acpi_vendor_uuid *uuid,
+ struct acpi_buffer *ret_buffer)
+{
+ struct acpi_vendor_walk_info info;
+ acpi_status status;
+
+ /* Other parameters are validated by acpi_walk_resources */
+
+ if (!uuid || !ret_buffer) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ info.uuid = uuid;
+ info.buffer = ret_buffer;
+ info.status = AE_NOT_EXIST;
+
+ /* Walk the _CRS or _PRS resource list for this device */
+
+ status =
+ acpi_walk_resources(device_handle, name,
+ acpi_rs_match_vendor_resource, &info);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ return (info.status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_vendor_resource)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_rs_match_vendor_resource
+ *
+ * PARAMETERS: acpi_walk_resource_callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Match a vendor resource via the ACPI 3.0 UUID
+ *
+ ******************************************************************************/
+static acpi_status
+acpi_rs_match_vendor_resource(struct acpi_resource *resource, void *context)
+{
+ struct acpi_vendor_walk_info *info = context;
+ struct acpi_resource_vendor_typed *vendor;
+ struct acpi_buffer *buffer;
+ acpi_status status;
+
+ /* Ignore all descriptors except Vendor */
+
+ if (resource->type != ACPI_RESOURCE_TYPE_VENDOR) {
+ return (AE_OK);
+ }
+
+ vendor = &resource->data.vendor_typed;
+
+ /*
+ * For a valid match, these conditions must hold:
+ *
+ * 1) Length of descriptor data must be at least as long as a UUID struct
+ * 2) The UUID subtypes must match
+ * 3) The UUID data must match
+ */
+ if ((vendor->byte_length < (ACPI_UUID_LENGTH + 1)) ||
+ (vendor->uuid_subtype != info->uuid->subtype) ||
+ (memcmp(vendor->uuid, info->uuid->data, ACPI_UUID_LENGTH))) {
+ return (AE_OK);
+ }
+
+ /* Validate/Allocate/Clear caller buffer */
+
+ buffer = info->buffer;
+ status = acpi_ut_initialize_buffer(buffer, resource->length);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Found the correct resource, copy and return it */
+
+ memcpy(buffer->pointer, resource, resource->length);
+ buffer->length = resource->length;
+
+ /* Found the desired descriptor, terminate resource walk */
+
+ info->status = AE_OK;
+ return (AE_CTRL_TERMINATE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_walk_resource_buffer
+ *
+ * PARAMETERS: buffer - Formatted buffer returned by one of the
+ * various Get*Resource functions
+ * user_function - Called for each resource
+ * context - Passed to user_function
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Walks the input resource template. The user_function is called
+ * once for each resource in the list.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_walk_resource_buffer(struct acpi_buffer *buffer,
+ acpi_walk_resource_callback user_function,
+ void *context)
+{
+ acpi_status status = AE_OK;
+ struct acpi_resource *resource;
+ struct acpi_resource *resource_end;
+
+ ACPI_FUNCTION_TRACE(acpi_walk_resource_buffer);
+
+ /* Parameter validation */
+
+ if (!buffer || !buffer->pointer || !user_function) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Buffer contains the resource list and length */
+
+ resource = ACPI_CAST_PTR(struct acpi_resource, buffer->pointer);
+ resource_end =
+ ACPI_ADD_PTR(struct acpi_resource, buffer->pointer, buffer->length);
+
+ /* Walk the resource list until the end_tag is found (or buffer end) */
+
+ while (resource < resource_end) {
+
+ /* Sanity check the resource type */
+
+ if (resource->type > ACPI_RESOURCE_TYPE_MAX) {
+ status = AE_AML_INVALID_RESOURCE_TYPE;
+ break;
+ }
+
+ /* Sanity check the length. It must not be zero, or we loop forever */
+
+ if (!resource->length) {
+ return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH);
+ }
+
+ /* Invoke the user function, abort on any error returned */
+
+ status = user_function(resource, context);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_CTRL_TERMINATE) {
+
+ /* This is an OK termination by the user function */
+
+ status = AE_OK;
+ }
+ break;
+ }
+
+ /* end_tag indicates end-of-list */
+
+ if (resource->type == ACPI_RESOURCE_TYPE_END_TAG) {
+ break;
+ }
+
+ /* Get the next resource descriptor */
+
+ resource = ACPI_NEXT_RESOURCE(resource);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_walk_resource_buffer)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_walk_resources
+ *
+ * PARAMETERS: device_handle - Handle to the device object for the
+ * device we are querying
+ * name - Method name of the resources we want.
+ * (METHOD_NAME__CRS, METHOD_NAME__PRS, or
+ * METHOD_NAME__AEI or METHOD_NAME__DMA)
+ * user_function - Called for each resource
+ * context - Passed to user_function
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Retrieves the current or possible resource list for the
+ * specified device. The user_function is called once for
+ * each resource in the list.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_walk_resources(acpi_handle device_handle,
+ char *name,
+ acpi_walk_resource_callback user_function, void *context)
+{
+ acpi_status status;
+ struct acpi_buffer buffer;
+
+ ACPI_FUNCTION_TRACE(acpi_walk_resources);
+
+ /* Parameter validation */
+
+ if (!device_handle || !user_function || !name ||
+ (!ACPI_COMPARE_NAMESEG(name, METHOD_NAME__CRS) &&
+ !ACPI_COMPARE_NAMESEG(name, METHOD_NAME__PRS) &&
+ !ACPI_COMPARE_NAMESEG(name, METHOD_NAME__AEI) &&
+ !ACPI_COMPARE_NAMESEG(name, METHOD_NAME__DMA))) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Get the _CRS/_PRS/_AEI/_DMA resource list */
+
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ status = acpi_rs_get_method_data(device_handle, name, &buffer);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Walk the resource list and cleanup */
+
+ status = acpi_walk_resource_buffer(&buffer, user_function, context);
+ ACPI_FREE(buffer.pointer);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_walk_resources)
diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c
new file mode 100644
index 000000000..a7642b34c
--- /dev/null
+++ b/drivers/acpi/acpica/tbdata.c
@@ -0,0 +1,1097 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: tbdata - Table manager data structure functions
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "actables.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_TABLES
+ACPI_MODULE_NAME("tbdata")
+
+/* Local prototypes */
+static acpi_status
+acpi_tb_check_duplication(struct acpi_table_desc *table_desc, u32 *table_index);
+
+static u8
+acpi_tb_compare_tables(struct acpi_table_desc *table_desc, u32 table_index);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_compare_tables
+ *
+ * PARAMETERS: table_desc - Table 1 descriptor to be compared
+ * table_index - Index of table 2 to be compared
+ *
+ * RETURN: TRUE if both tables are identical.
+ *
+ * DESCRIPTION: This function compares a table with another table that has
+ * already been installed in the root table list.
+ *
+ ******************************************************************************/
+
+static u8
+acpi_tb_compare_tables(struct acpi_table_desc *table_desc, u32 table_index)
+{
+ acpi_status status = AE_OK;
+ u8 is_identical;
+ struct acpi_table_header *table;
+ u32 table_length;
+ u8 table_flags;
+
+ status =
+ acpi_tb_acquire_table(&acpi_gbl_root_table_list.tables[table_index],
+ &table, &table_length, &table_flags);
+ if (ACPI_FAILURE(status)) {
+ return (FALSE);
+ }
+
+ /*
+ * Check for a table match on the entire table length,
+ * not just the header.
+ */
+ is_identical = (u8)((table_desc->length != table_length ||
+ memcmp(table_desc->pointer, table, table_length)) ?
+ FALSE : TRUE);
+
+ /* Release the acquired table */
+
+ acpi_tb_release_table(table, table_length, table_flags);
+ return (is_identical);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_init_table_descriptor
+ *
+ * PARAMETERS: table_desc - Table descriptor
+ * address - Physical address of the table
+ * flags - Allocation flags of the table
+ * table - Pointer to the table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Initialize a new table descriptor
+ *
+ ******************************************************************************/
+
+void
+acpi_tb_init_table_descriptor(struct acpi_table_desc *table_desc,
+ acpi_physical_address address,
+ u8 flags, struct acpi_table_header *table)
+{
+
+ /*
+ * Initialize the table descriptor. Set the pointer to NULL for external
+ * tables, since the table is not fully mapped at this time.
+ */
+ memset(table_desc, 0, sizeof(struct acpi_table_desc));
+ table_desc->address = address;
+ table_desc->length = table->length;
+ table_desc->flags = flags;
+ ACPI_MOVE_32_TO_32(table_desc->signature.ascii, table->signature);
+
+ switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) {
+ case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+ case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+
+ table_desc->pointer = table;
+ break;
+
+ case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+ default:
+
+ break;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_acquire_table
+ *
+ * PARAMETERS: table_desc - Table descriptor
+ * table_ptr - Where table is returned
+ * table_length - Where table length is returned
+ * table_flags - Where table allocation flags are returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Acquire an ACPI table. It can be used for tables not
+ * maintained in the acpi_gbl_root_table_list.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_acquire_table(struct acpi_table_desc *table_desc,
+ struct acpi_table_header **table_ptr,
+ u32 *table_length, u8 *table_flags)
+{
+ struct acpi_table_header *table = NULL;
+
+ switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) {
+ case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+ table =
+ acpi_os_map_memory(table_desc->address, table_desc->length);
+ break;
+
+ case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+ case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+
+ table = table_desc->pointer;
+ break;
+
+ default:
+
+ break;
+ }
+
+ /* Table is not valid yet */
+
+ if (!table) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Fill the return values */
+
+ *table_ptr = table;
+ *table_length = table_desc->length;
+ *table_flags = table_desc->flags;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_release_table
+ *
+ * PARAMETERS: table - Pointer for the table
+ * table_length - Length for the table
+ * table_flags - Allocation flags for the table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Release a table. The inverse of acpi_tb_acquire_table().
+ *
+ ******************************************************************************/
+
+void
+acpi_tb_release_table(struct acpi_table_header *table,
+ u32 table_length, u8 table_flags)
+{
+
+ switch (table_flags & ACPI_TABLE_ORIGIN_MASK) {
+ case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+ acpi_os_unmap_memory(table, table_length);
+ break;
+
+ case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+ case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+ default:
+
+ break;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_acquire_temp_table
+ *
+ * PARAMETERS: table_desc - Table descriptor to be acquired
+ * address - Address of the table
+ * flags - Allocation flags of the table
+ * table - Pointer to the table (required for virtual
+ * origins, optional for physical)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function validates the table header to obtain the length
+ * of a table and fills the table descriptor to make its state as
+ * "INSTALLED". Such a table descriptor is only used for verified
+ * installation.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_acquire_temp_table(struct acpi_table_desc *table_desc,
+ acpi_physical_address address,
+ u8 flags, struct acpi_table_header *table)
+{
+ u8 mapped_table = FALSE;
+
+ switch (flags & ACPI_TABLE_ORIGIN_MASK) {
+ case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+ /* Get the length of the full table from the header */
+
+ if (!table) {
+ table =
+ acpi_os_map_memory(address,
+ sizeof(struct
+ acpi_table_header));
+ if (!table) {
+ return (AE_NO_MEMORY);
+ }
+
+ mapped_table = TRUE;
+ }
+
+ break;
+
+ case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+ case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+
+ if (!table) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ break;
+
+ default:
+
+ /* Table is not valid yet */
+
+ return (AE_NO_MEMORY);
+ }
+
+ acpi_tb_init_table_descriptor(table_desc, address, flags, table);
+ if (mapped_table) {
+ acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_release_temp_table
+ *
+ * PARAMETERS: table_desc - Table descriptor to be released
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: The inverse of acpi_tb_acquire_temp_table().
+ *
+ *****************************************************************************/
+
+void acpi_tb_release_temp_table(struct acpi_table_desc *table_desc)
+{
+
+ /*
+ * Note that the .Address is maintained by the callers of
+ * acpi_tb_acquire_temp_table(), thus do not invoke acpi_tb_uninstall_table()
+ * where .Address will be freed.
+ */
+ acpi_tb_invalidate_table(table_desc);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_tb_validate_table
+ *
+ * PARAMETERS: table_desc - Table descriptor
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to validate the table, the returned
+ * table descriptor is in "VALIDATED" state.
+ *
+ *****************************************************************************/
+
+acpi_status acpi_tb_validate_table(struct acpi_table_desc *table_desc)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(tb_validate_table);
+
+ /* Validate the table if necessary */
+
+ if (!table_desc->pointer) {
+ status = acpi_tb_acquire_table(table_desc, &table_desc->pointer,
+ &table_desc->length,
+ &table_desc->flags);
+ if (!table_desc->pointer) {
+ status = AE_NO_MEMORY;
+ }
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_invalidate_table
+ *
+ * PARAMETERS: table_desc - Table descriptor
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Invalidate one internal ACPI table, this is the inverse of
+ * acpi_tb_validate_table().
+ *
+ ******************************************************************************/
+
+void acpi_tb_invalidate_table(struct acpi_table_desc *table_desc)
+{
+
+ ACPI_FUNCTION_TRACE(tb_invalidate_table);
+
+ /* Table must be validated */
+
+ if (!table_desc->pointer) {
+ return_VOID;
+ }
+
+ acpi_tb_release_table(table_desc->pointer, table_desc->length,
+ table_desc->flags);
+
+ switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) {
+ case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+ table_desc->pointer = NULL;
+ break;
+
+ case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+ case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+ default:
+
+ break;
+ }
+
+ return_VOID;
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_tb_validate_temp_table
+ *
+ * PARAMETERS: table_desc - Table descriptor
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to validate the table, the returned
+ * table descriptor is in "VALIDATED" state.
+ *
+ *****************************************************************************/
+
+acpi_status acpi_tb_validate_temp_table(struct acpi_table_desc *table_desc)
+{
+
+ if (!table_desc->pointer && !acpi_gbl_enable_table_validation) {
+ /*
+ * Only validates the header of the table.
+ * Note that Length contains the size of the mapping after invoking
+ * this work around, this value is required by
+ * acpi_tb_release_temp_table().
+ * We can do this because in acpi_init_table_descriptor(), the Length
+ * field of the installed descriptor is filled with the actual
+ * table length obtaining from the table header.
+ */
+ table_desc->length = sizeof(struct acpi_table_header);
+ }
+
+ return (acpi_tb_validate_table(table_desc));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_check_duplication
+ *
+ * PARAMETERS: table_desc - Table descriptor
+ * table_index - Where the table index is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Avoid installing duplicated tables. However table override and
+ * user aided dynamic table load is allowed, thus comparing the
+ * address of the table is not sufficient, and checking the entire
+ * table content is required.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_tb_check_duplication(struct acpi_table_desc *table_desc, u32 *table_index)
+{
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(tb_check_duplication);
+
+ /* Check if table is already registered */
+
+ for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) {
+
+ /* Do not compare with unverified tables */
+
+ if (!
+ (acpi_gbl_root_table_list.tables[i].
+ flags & ACPI_TABLE_IS_VERIFIED)) {
+ continue;
+ }
+
+ /*
+ * Check for a table match on the entire table length,
+ * not just the header.
+ */
+ if (!acpi_tb_compare_tables(table_desc, i)) {
+ continue;
+ }
+
+ /*
+ * Note: the current mechanism does not unregister a table if it is
+ * dynamically unloaded. The related namespace entries are deleted,
+ * but the table remains in the root table list.
+ *
+ * The assumption here is that the number of different tables that
+ * will be loaded is actually small, and there is minimal overhead
+ * in just keeping the table in case it is needed again.
+ *
+ * If this assumption changes in the future (perhaps on large
+ * machines with many table load/unload operations), tables will
+ * need to be unregistered when they are unloaded, and slots in the
+ * root table list should be reused when empty.
+ */
+ if (acpi_gbl_root_table_list.tables[i].flags &
+ ACPI_TABLE_IS_LOADED) {
+
+ /* Table is still loaded, this is an error */
+
+ return_ACPI_STATUS(AE_ALREADY_EXISTS);
+ } else {
+ *table_index = i;
+ return_ACPI_STATUS(AE_CTRL_TERMINATE);
+ }
+ }
+
+ /* Indicate no duplication to the caller */
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_tb_verify_temp_table
+ *
+ * PARAMETERS: table_desc - Table descriptor
+ * signature - Table signature to verify
+ * table_index - Where the table index is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to validate and verify the table, the
+ * returned table descriptor is in "VALIDATED" state.
+ * Note that 'TableIndex' is required to be set to !NULL to
+ * enable duplication check.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_tb_verify_temp_table(struct acpi_table_desc *table_desc,
+ char *signature, u32 *table_index)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(tb_verify_temp_table);
+
+ /* Validate the table */
+
+ status = acpi_tb_validate_temp_table(table_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* If a particular signature is expected (DSDT/FACS), it must match */
+
+ if (signature &&
+ !ACPI_COMPARE_NAMESEG(&table_desc->signature, signature)) {
+ ACPI_BIOS_ERROR((AE_INFO,
+ "Invalid signature 0x%X for ACPI table, expected [%s]",
+ table_desc->signature.integer, signature));
+ status = AE_BAD_SIGNATURE;
+ goto invalidate_and_exit;
+ }
+
+ if (acpi_gbl_enable_table_validation) {
+
+ /* Verify the checksum */
+
+ status =
+ acpi_tb_verify_checksum(table_desc->pointer,
+ table_desc->length);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY,
+ "%4.4s 0x%8.8X%8.8X"
+ " Attempted table install failed",
+ acpi_ut_valid_nameseg(table_desc->
+ signature.
+ ascii) ?
+ table_desc->signature.ascii : "????",
+ ACPI_FORMAT_UINT64(table_desc->
+ address)));
+
+ goto invalidate_and_exit;
+ }
+
+ /* Avoid duplications */
+
+ if (table_index) {
+ status =
+ acpi_tb_check_duplication(table_desc, table_index);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "%4.4s 0x%8.8X%8.8X"
+ " Table is already loaded",
+ acpi_ut_valid_nameseg
+ (table_desc->signature.
+ ascii) ? table_desc->
+ signature.
+ ascii : "????",
+ ACPI_FORMAT_UINT64
+ (table_desc->address)));
+ }
+
+ goto invalidate_and_exit;
+ }
+ }
+
+ table_desc->flags |= ACPI_TABLE_IS_VERIFIED;
+ }
+
+ return_ACPI_STATUS(status);
+
+invalidate_and_exit:
+ acpi_tb_invalidate_table(table_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_resize_root_table_list
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Expand the size of global table array
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_resize_root_table_list(void)
+{
+ struct acpi_table_desc *tables;
+ u32 table_count;
+ u32 current_table_count, max_table_count;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(tb_resize_root_table_list);
+
+ /* allow_resize flag is a parameter to acpi_initialize_tables */
+
+ if (!(acpi_gbl_root_table_list.flags & ACPI_ROOT_ALLOW_RESIZE)) {
+ ACPI_ERROR((AE_INFO,
+ "Resize of Root Table Array is not allowed"));
+ return_ACPI_STATUS(AE_SUPPORT);
+ }
+
+ /* Increase the Table Array size */
+
+ if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) {
+ table_count = acpi_gbl_root_table_list.max_table_count;
+ } else {
+ table_count = acpi_gbl_root_table_list.current_table_count;
+ }
+
+ max_table_count = table_count + ACPI_ROOT_TABLE_SIZE_INCREMENT;
+ tables = ACPI_ALLOCATE_ZEROED(((acpi_size)max_table_count) *
+ sizeof(struct acpi_table_desc));
+ if (!tables) {
+ ACPI_ERROR((AE_INFO,
+ "Could not allocate new root table array"));
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Copy and free the previous table array */
+
+ current_table_count = 0;
+ if (acpi_gbl_root_table_list.tables) {
+ for (i = 0; i < table_count; i++) {
+ if (acpi_gbl_root_table_list.tables[i].address) {
+ memcpy(tables + current_table_count,
+ acpi_gbl_root_table_list.tables + i,
+ sizeof(struct acpi_table_desc));
+ current_table_count++;
+ }
+ }
+
+ if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) {
+ ACPI_FREE(acpi_gbl_root_table_list.tables);
+ }
+ }
+
+ acpi_gbl_root_table_list.tables = tables;
+ acpi_gbl_root_table_list.max_table_count = max_table_count;
+ acpi_gbl_root_table_list.current_table_count = current_table_count;
+ acpi_gbl_root_table_list.flags |= ACPI_ROOT_ORIGIN_ALLOCATED;
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_get_next_table_descriptor
+ *
+ * PARAMETERS: table_index - Where table index is returned
+ * table_desc - Where table descriptor is returned
+ *
+ * RETURN: Status and table index/descriptor.
+ *
+ * DESCRIPTION: Allocate a new ACPI table entry to the global table list
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_get_next_table_descriptor(u32 *table_index,
+ struct acpi_table_desc **table_desc)
+{
+ acpi_status status;
+ u32 i;
+
+ /* Ensure that there is room for the table in the Root Table List */
+
+ if (acpi_gbl_root_table_list.current_table_count >=
+ acpi_gbl_root_table_list.max_table_count) {
+ status = acpi_tb_resize_root_table_list();
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ i = acpi_gbl_root_table_list.current_table_count;
+ acpi_gbl_root_table_list.current_table_count++;
+
+ if (table_index) {
+ *table_index = i;
+ }
+ if (table_desc) {
+ *table_desc = &acpi_gbl_root_table_list.tables[i];
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_terminate
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete all internal ACPI tables
+ *
+ ******************************************************************************/
+
+void acpi_tb_terminate(void)
+{
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(tb_terminate);
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+ /* Delete the individual tables */
+
+ for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) {
+ acpi_tb_uninstall_table(&acpi_gbl_root_table_list.tables[i]);
+ }
+
+ /*
+ * Delete the root table array if allocated locally. Array cannot be
+ * mapped, so we don't need to check for that flag.
+ */
+ if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) {
+ ACPI_FREE(acpi_gbl_root_table_list.tables);
+ }
+
+ acpi_gbl_root_table_list.tables = NULL;
+ acpi_gbl_root_table_list.flags = 0;
+ acpi_gbl_root_table_list.current_table_count = 0;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "ACPI Tables freed\n"));
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_delete_namespace_by_owner
+ *
+ * PARAMETERS: table_index - Table index
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Delete all namespace objects created when this table was loaded.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index)
+{
+ acpi_owner_id owner_id;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(tb_delete_namespace_by_owner);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ if (table_index >= acpi_gbl_root_table_list.current_table_count) {
+
+ /* The table index does not exist */
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /* Get the owner ID for this table, used to delete namespace nodes */
+
+ owner_id = acpi_gbl_root_table_list.tables[table_index].owner_id;
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+
+ /*
+ * Need to acquire the namespace writer lock to prevent interference
+ * with any concurrent namespace walks. The interpreter must be
+ * released during the deletion since the acquisition of the deletion
+ * lock may block, and also since the execution of a namespace walk
+ * must be allowed to use the interpreter.
+ */
+ status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_ns_delete_namespace_by_owner(owner_id);
+ acpi_ut_release_write_lock(&acpi_gbl_namespace_rw_lock);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_allocate_owner_id
+ *
+ * PARAMETERS: table_index - Table index
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Allocates owner_id in table_desc
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_allocate_owner_id(u32 table_index)
+{
+ acpi_status status = AE_BAD_PARAMETER;
+
+ ACPI_FUNCTION_TRACE(tb_allocate_owner_id);
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ if (table_index < acpi_gbl_root_table_list.current_table_count) {
+ status =
+ acpi_ut_allocate_owner_id(&
+ (acpi_gbl_root_table_list.
+ tables[table_index].owner_id));
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_release_owner_id
+ *
+ * PARAMETERS: table_index - Table index
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Releases owner_id in table_desc
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_release_owner_id(u32 table_index)
+{
+ acpi_status status = AE_BAD_PARAMETER;
+
+ ACPI_FUNCTION_TRACE(tb_release_owner_id);
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ if (table_index < acpi_gbl_root_table_list.current_table_count) {
+ acpi_ut_release_owner_id(&
+ (acpi_gbl_root_table_list.
+ tables[table_index].owner_id));
+ status = AE_OK;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_get_owner_id
+ *
+ * PARAMETERS: table_index - Table index
+ * owner_id - Where the table owner_id is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: returns owner_id for the ACPI table
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_get_owner_id(u32 table_index, acpi_owner_id *owner_id)
+{
+ acpi_status status = AE_BAD_PARAMETER;
+
+ ACPI_FUNCTION_TRACE(tb_get_owner_id);
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ if (table_index < acpi_gbl_root_table_list.current_table_count) {
+ *owner_id =
+ acpi_gbl_root_table_list.tables[table_index].owner_id;
+ status = AE_OK;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_is_table_loaded
+ *
+ * PARAMETERS: table_index - Index into the root table
+ *
+ * RETURN: Table Loaded Flag
+ *
+ ******************************************************************************/
+
+u8 acpi_tb_is_table_loaded(u32 table_index)
+{
+ u8 is_loaded = FALSE;
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ if (table_index < acpi_gbl_root_table_list.current_table_count) {
+ is_loaded = (u8)
+ (acpi_gbl_root_table_list.tables[table_index].flags &
+ ACPI_TABLE_IS_LOADED);
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return (is_loaded);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_set_table_loaded_flag
+ *
+ * PARAMETERS: table_index - Table index
+ * is_loaded - TRUE if table is loaded, FALSE otherwise
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Sets the table loaded flag to either TRUE or FALSE.
+ *
+ ******************************************************************************/
+
+void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded)
+{
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ if (table_index < acpi_gbl_root_table_list.current_table_count) {
+ if (is_loaded) {
+ acpi_gbl_root_table_list.tables[table_index].flags |=
+ ACPI_TABLE_IS_LOADED;
+ } else {
+ acpi_gbl_root_table_list.tables[table_index].flags &=
+ ~ACPI_TABLE_IS_LOADED;
+ }
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_load_table
+ *
+ * PARAMETERS: table_index - Table index
+ * parent_node - Where table index is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load an ACPI table
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_load_table(u32 table_index, struct acpi_namespace_node *parent_node)
+{
+ struct acpi_table_header *table;
+ acpi_status status;
+ acpi_owner_id owner_id;
+
+ ACPI_FUNCTION_TRACE(tb_load_table);
+
+ /*
+ * Note: Now table is "INSTALLED", it must be validated before
+ * using.
+ */
+ status = acpi_get_table_by_index(table_index, &table);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ns_load_table(table_index, parent_node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is
+ * responsible for discovering any new wake GPEs by running _PRW methods
+ * that may have been loaded by this table.
+ */
+ status = acpi_tb_get_owner_id(table_index, &owner_id);
+ if (ACPI_SUCCESS(status)) {
+ acpi_ev_update_gpes(owner_id);
+ }
+
+ /* Invoke table handler */
+
+ acpi_tb_notify_table(ACPI_TABLE_EVENT_LOAD, table);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_install_and_load_table
+ *
+ * PARAMETERS: address - Physical address of the table
+ * flags - Allocation flags of the table
+ * table - Pointer to the table (required for
+ * virtual origins, optional for
+ * physical)
+ * override - Whether override should be performed
+ * table_index - Where table index is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install and load an ACPI table
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_install_and_load_table(acpi_physical_address address,
+ u8 flags,
+ struct acpi_table_header *table,
+ u8 override, u32 *table_index)
+{
+ acpi_status status;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(tb_install_and_load_table);
+
+ /* Install the table and load it into the namespace */
+
+ status = acpi_tb_install_standard_table(address, flags, table, TRUE,
+ override, &i);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ status = acpi_tb_load_table(i, acpi_gbl_root_node);
+
+exit:
+ *table_index = i;
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_tb_install_and_load_table)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_unload_table
+ *
+ * PARAMETERS: table_index - Table index
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Unload an ACPI table
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_unload_table(u32 table_index)
+{
+ acpi_status status = AE_OK;
+ struct acpi_table_header *table;
+
+ ACPI_FUNCTION_TRACE(tb_unload_table);
+
+ /* Ensure the table is still loaded */
+
+ if (!acpi_tb_is_table_loaded(table_index)) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /* Invoke table handler */
+
+ status = acpi_get_table_by_index(table_index, &table);
+ if (ACPI_SUCCESS(status)) {
+ acpi_tb_notify_table(ACPI_TABLE_EVENT_UNLOAD, table);
+ }
+
+ /* Delete the portion of the namespace owned by this table */
+
+ status = acpi_tb_delete_namespace_by_owner(table_index);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ (void)acpi_tb_release_owner_id(table_index);
+ acpi_tb_set_table_loaded_flag(table_index, FALSE);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_tb_unload_table)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_notify_table
+ *
+ * PARAMETERS: event - Table event
+ * table - Validated table pointer
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Notify a table event to the users.
+ *
+ ******************************************************************************/
+
+void acpi_tb_notify_table(u32 event, void *table)
+{
+ /* Invoke table handler if present */
+
+ if (acpi_gbl_table_handler) {
+ (void)acpi_gbl_table_handler(event, table,
+ acpi_gbl_table_handler_context);
+ }
+}
diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c
new file mode 100644
index 000000000..31d7ea84a
--- /dev/null
+++ b/drivers/acpi/acpica/tbfadt.c
@@ -0,0 +1,722 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: tbfadt - FADT table utilities
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_TABLES
+ACPI_MODULE_NAME("tbfadt")
+
+/* Local prototypes */
+static void
+acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
+ u8 space_id,
+ u8 byte_width,
+ u64 address, const char *register_name, u8 flags);
+
+static void acpi_tb_convert_fadt(void);
+
+static void acpi_tb_setup_fadt_registers(void);
+
+static u64
+acpi_tb_select_address(char *register_name, u32 address32, u64 address64);
+
+/* Table for conversion of FADT to common internal format and FADT validation */
+
+typedef struct acpi_fadt_info {
+ const char *name;
+ u16 address64;
+ u16 address32;
+ u16 length;
+ u8 default_length;
+ u8 flags;
+
+} acpi_fadt_info;
+
+#define ACPI_FADT_OPTIONAL 0
+#define ACPI_FADT_REQUIRED 1
+#define ACPI_FADT_SEPARATE_LENGTH 2
+#define ACPI_FADT_GPE_REGISTER 4
+
+static struct acpi_fadt_info fadt_info_table[] = {
+ {"Pm1aEventBlock",
+ ACPI_FADT_OFFSET(xpm1a_event_block),
+ ACPI_FADT_OFFSET(pm1a_event_block),
+ ACPI_FADT_OFFSET(pm1_event_length),
+ ACPI_PM1_REGISTER_WIDTH * 2, /* Enable + Status register */
+ ACPI_FADT_REQUIRED},
+
+ {"Pm1bEventBlock",
+ ACPI_FADT_OFFSET(xpm1b_event_block),
+ ACPI_FADT_OFFSET(pm1b_event_block),
+ ACPI_FADT_OFFSET(pm1_event_length),
+ ACPI_PM1_REGISTER_WIDTH * 2, /* Enable + Status register */
+ ACPI_FADT_OPTIONAL},
+
+ {"Pm1aControlBlock",
+ ACPI_FADT_OFFSET(xpm1a_control_block),
+ ACPI_FADT_OFFSET(pm1a_control_block),
+ ACPI_FADT_OFFSET(pm1_control_length),
+ ACPI_PM1_REGISTER_WIDTH,
+ ACPI_FADT_REQUIRED},
+
+ {"Pm1bControlBlock",
+ ACPI_FADT_OFFSET(xpm1b_control_block),
+ ACPI_FADT_OFFSET(pm1b_control_block),
+ ACPI_FADT_OFFSET(pm1_control_length),
+ ACPI_PM1_REGISTER_WIDTH,
+ ACPI_FADT_OPTIONAL},
+
+ {"Pm2ControlBlock",
+ ACPI_FADT_OFFSET(xpm2_control_block),
+ ACPI_FADT_OFFSET(pm2_control_block),
+ ACPI_FADT_OFFSET(pm2_control_length),
+ ACPI_PM2_REGISTER_WIDTH,
+ ACPI_FADT_SEPARATE_LENGTH},
+
+ {"PmTimerBlock",
+ ACPI_FADT_OFFSET(xpm_timer_block),
+ ACPI_FADT_OFFSET(pm_timer_block),
+ ACPI_FADT_OFFSET(pm_timer_length),
+ ACPI_PM_TIMER_WIDTH,
+ ACPI_FADT_SEPARATE_LENGTH}, /* ACPI 5.0A: Timer is optional */
+
+ {"Gpe0Block",
+ ACPI_FADT_OFFSET(xgpe0_block),
+ ACPI_FADT_OFFSET(gpe0_block),
+ ACPI_FADT_OFFSET(gpe0_block_length),
+ 0,
+ ACPI_FADT_SEPARATE_LENGTH | ACPI_FADT_GPE_REGISTER},
+
+ {"Gpe1Block",
+ ACPI_FADT_OFFSET(xgpe1_block),
+ ACPI_FADT_OFFSET(gpe1_block),
+ ACPI_FADT_OFFSET(gpe1_block_length),
+ 0,
+ ACPI_FADT_SEPARATE_LENGTH | ACPI_FADT_GPE_REGISTER}
+};
+
+#define ACPI_FADT_INFO_ENTRIES \
+ (sizeof (fadt_info_table) / sizeof (struct acpi_fadt_info))
+
+/* Table used to split Event Blocks into separate status/enable registers */
+
+typedef struct acpi_fadt_pm_info {
+ struct acpi_generic_address *target;
+ u16 source;
+ u8 register_num;
+
+} acpi_fadt_pm_info;
+
+static struct acpi_fadt_pm_info fadt_pm_info_table[] = {
+ {&acpi_gbl_xpm1a_status,
+ ACPI_FADT_OFFSET(xpm1a_event_block),
+ 0},
+
+ {&acpi_gbl_xpm1a_enable,
+ ACPI_FADT_OFFSET(xpm1a_event_block),
+ 1},
+
+ {&acpi_gbl_xpm1b_status,
+ ACPI_FADT_OFFSET(xpm1b_event_block),
+ 0},
+
+ {&acpi_gbl_xpm1b_enable,
+ ACPI_FADT_OFFSET(xpm1b_event_block),
+ 1}
+};
+
+#define ACPI_FADT_PM_INFO_ENTRIES \
+ (sizeof (fadt_pm_info_table) / sizeof (struct acpi_fadt_pm_info))
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_init_generic_address
+ *
+ * PARAMETERS: generic_address - GAS struct to be initialized
+ * space_id - ACPI Space ID for this register
+ * byte_width - Width of this register
+ * address - Address of the register
+ * register_name - ASCII name of the ACPI register
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Initialize a Generic Address Structure (GAS)
+ * See the ACPI specification for a full description and
+ * definition of this structure.
+ *
+ ******************************************************************************/
+
+static void
+acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
+ u8 space_id,
+ u8 byte_width,
+ u64 address, const char *register_name, u8 flags)
+{
+ u8 bit_width;
+
+ /*
+ * Bit width field in the GAS is only one byte long, 255 max.
+ * Check for bit_width overflow in GAS.
+ */
+ bit_width = (u8)(byte_width * 8);
+ if (byte_width > 31) { /* (31*8)=248, (32*8)=256 */
+ /*
+ * No error for GPE blocks, because we do not use the bit_width
+ * for GPEs, the legacy length (byte_width) is used instead to
+ * allow for a large number of GPEs.
+ */
+ if (!(flags & ACPI_FADT_GPE_REGISTER)) {
+ ACPI_ERROR((AE_INFO,
+ "%s - 32-bit FADT register is too long (%u bytes, %u bits) "
+ "to convert to GAS struct - 255 bits max, truncating",
+ register_name, byte_width,
+ (byte_width * 8)));
+ }
+
+ bit_width = 255;
+ }
+
+ /*
+ * The 64-bit Address field is non-aligned in the byte packed
+ * GAS struct.
+ */
+ ACPI_MOVE_64_TO_64(&generic_address->address, &address);
+
+ /* All other fields are byte-wide */
+
+ generic_address->space_id = space_id;
+ generic_address->bit_width = bit_width;
+ generic_address->bit_offset = 0;
+ generic_address->access_width = 0; /* Access width ANY */
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_select_address
+ *
+ * PARAMETERS: register_name - ASCII name of the ACPI register
+ * address32 - 32-bit address of the register
+ * address64 - 64-bit address of the register
+ *
+ * RETURN: The resolved 64-bit address
+ *
+ * DESCRIPTION: Select between 32-bit and 64-bit versions of addresses within
+ * the FADT. Used for the FACS and DSDT addresses.
+ *
+ * NOTES:
+ *
+ * Check for FACS and DSDT address mismatches. An address mismatch between
+ * the 32-bit and 64-bit address fields (FIRMWARE_CTRL/X_FIRMWARE_CTRL and
+ * DSDT/X_DSDT) could be a corrupted address field or it might indicate
+ * the presence of two FACS or two DSDT tables.
+ *
+ * November 2013:
+ * By default, as per the ACPICA specification, a valid 64-bit address is
+ * used regardless of the value of the 32-bit address. However, this
+ * behavior can be overridden via the acpi_gbl_use32_bit_fadt_addresses flag.
+ *
+ ******************************************************************************/
+
+static u64
+acpi_tb_select_address(char *register_name, u32 address32, u64 address64)
+{
+
+ if (!address64) {
+
+ /* 64-bit address is zero, use 32-bit address */
+
+ return ((u64)address32);
+ }
+
+ if (address32 && (address64 != (u64)address32)) {
+
+ /* Address mismatch between 32-bit and 64-bit versions */
+
+ ACPI_BIOS_WARNING((AE_INFO,
+ "32/64X %s address mismatch in FADT: "
+ "0x%8.8X/0x%8.8X%8.8X, using %u-bit address",
+ register_name, address32,
+ ACPI_FORMAT_UINT64(address64),
+ acpi_gbl_use32_bit_fadt_addresses ? 32 :
+ 64));
+
+ /* 32-bit address override */
+
+ if (acpi_gbl_use32_bit_fadt_addresses) {
+ return ((u64)address32);
+ }
+ }
+
+ /* Default is to use the 64-bit address */
+
+ return (address64);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_parse_fadt
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Initialize the FADT, DSDT and FACS tables
+ * (FADT contains the addresses of the DSDT and FACS)
+ *
+ ******************************************************************************/
+
+void acpi_tb_parse_fadt(void)
+{
+ u32 length;
+ struct acpi_table_header *table;
+ struct acpi_table_desc *fadt_desc;
+ acpi_status status;
+
+ /*
+ * The FADT has multiple versions with different lengths,
+ * and it contains pointers to both the DSDT and FACS tables.
+ *
+ * Get a local copy of the FADT and convert it to a common format
+ * Map entire FADT, assumed to be smaller than one page.
+ */
+ fadt_desc = &acpi_gbl_root_table_list.tables[acpi_gbl_fadt_index];
+ status = acpi_tb_get_table(fadt_desc, &table);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+ length = fadt_desc->length;
+
+ /*
+ * 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);
+
+ /* Create a local copy of the FADT in common ACPI 2.0+ format */
+
+ acpi_tb_create_local_fadt(table, length);
+
+ /* All done with the real FADT, unmap it */
+
+ acpi_tb_put_table(fadt_desc);
+
+ /* Obtain the DSDT and FACS tables via their addresses within the FADT */
+
+ acpi_tb_install_standard_table((acpi_physical_address)acpi_gbl_FADT.
+ Xdsdt,
+ 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);
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_create_local_fadt
+ *
+ * PARAMETERS: table - Pointer to BIOS FADT
+ * length - Length of the table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Get a local copy of the FADT and convert it to a common format.
+ * Performs validation on some important FADT fields.
+ *
+ * NOTE: We create a local copy of the FADT regardless of the version.
+ *
+ ******************************************************************************/
+
+void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length)
+{
+ /*
+ * Check if the FADT is larger than the largest table that we expect
+ * (typically the current ACPI specification version). If so, truncate
+ * the table, and issue a warning.
+ */
+ if (length > sizeof(struct acpi_table_fadt)) {
+ ACPI_BIOS_WARNING((AE_INFO,
+ "FADT (revision %u) is longer than %s length, "
+ "truncating length %u to %u",
+ table->revision, ACPI_FADT_CONFORMANCE,
+ length,
+ (u32)sizeof(struct acpi_table_fadt)));
+ }
+
+ /* Clear the entire local FADT */
+
+ memset(&acpi_gbl_FADT, 0, sizeof(struct acpi_table_fadt));
+
+ /* Copy the original FADT, up to sizeof (struct acpi_table_fadt) */
+
+ memcpy(&acpi_gbl_FADT, table,
+ ACPI_MIN(length, sizeof(struct acpi_table_fadt)));
+
+ /* Take a copy of the Hardware Reduced flag */
+
+ acpi_gbl_reduced_hardware = FALSE;
+ if (acpi_gbl_FADT.flags & ACPI_FADT_HW_REDUCED) {
+ acpi_gbl_reduced_hardware = TRUE;
+ }
+
+ /* Convert the local copy of the FADT to the common internal format */
+
+ acpi_tb_convert_fadt();
+
+ /* Initialize the global ACPI register structures */
+
+ acpi_tb_setup_fadt_registers();
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_convert_fadt
+ *
+ * PARAMETERS: none - acpi_gbl_FADT is used.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Converts all versions of the FADT to a common internal format.
+ * Expand 32-bit addresses to 64-bit as necessary. Also validate
+ * important fields within the FADT.
+ *
+ * NOTE: acpi_gbl_FADT must be of size (struct acpi_table_fadt), and must
+ * contain a copy of the actual BIOS-provided FADT.
+ *
+ * Notes on 64-bit register addresses:
+ *
+ * After this FADT conversion, later ACPICA code will only use the 64-bit "X"
+ * fields of the FADT for all ACPI register addresses.
+ *
+ * The 64-bit X fields are optional extensions to the original 32-bit FADT
+ * V1.0 fields. Even if they are present in the FADT, they are optional and
+ * are unused if the BIOS sets them to zero. Therefore, we must copy/expand
+ * 32-bit V1.0 fields to the 64-bit X fields if the 64-bit X field is originally
+ * zero.
+ *
+ * For ACPI 1.0 FADTs (that contain no 64-bit addresses), all 32-bit address
+ * fields are expanded to the corresponding 64-bit X fields in the internal
+ * common FADT.
+ *
+ * For ACPI 2.0+ FADTs, all valid (non-zero) 32-bit address fields are expanded
+ * to the corresponding 64-bit X fields, if the 64-bit field is originally
+ * zero. Adhering to the ACPI specification, we completely ignore the 32-bit
+ * field if the 64-bit field is valid, regardless of whether the host OS is
+ * 32-bit or 64-bit.
+ *
+ * Possible additional checks:
+ * (acpi_gbl_FADT.pm1_event_length >= 4)
+ * (acpi_gbl_FADT.pm1_control_length >= 2)
+ * (acpi_gbl_FADT.pm_timer_length >= 4)
+ * Gpe block lengths must be multiple of 2
+ *
+ ******************************************************************************/
+
+static void acpi_tb_convert_fadt(void)
+{
+ const char *name;
+ struct acpi_generic_address *address64;
+ u32 address32;
+ u8 length;
+ u8 flags;
+ u32 i;
+
+ /*
+ * For ACPI 1.0 FADTs (revision 1 or 2), ensure that reserved fields which
+ * should be zero are indeed zero. This will workaround BIOSs that
+ * inadvertently place values in these fields.
+ *
+ * The ACPI 1.0 reserved fields that will be zeroed are the bytes located
+ * at offset 45, 55, 95, and the word located at offset 109, 110.
+ *
+ * Note: The FADT revision value is unreliable. Only the length can be
+ * trusted.
+ */
+ if (acpi_gbl_FADT.header.length <= ACPI_FADT_V2_SIZE) {
+ acpi_gbl_FADT.preferred_profile = 0;
+ acpi_gbl_FADT.pstate_control = 0;
+ acpi_gbl_FADT.cst_control = 0;
+ acpi_gbl_FADT.boot_flags = 0;
+ }
+
+ /*
+ * Now we can update the local FADT length to the length of the
+ * current FADT version as defined by the ACPI specification.
+ * Thus, we will have a common FADT internally.
+ */
+ acpi_gbl_FADT.header.length = sizeof(struct acpi_table_fadt);
+
+ /*
+ * Expand the 32-bit DSDT addresses to 64-bit as necessary.
+ * Later ACPICA code will always use the X 64-bit field.
+ */
+ acpi_gbl_FADT.Xdsdt = acpi_tb_select_address("DSDT",
+ acpi_gbl_FADT.dsdt,
+ acpi_gbl_FADT.Xdsdt);
+
+ /* If Hardware Reduced flag is set, we are all done */
+
+ if (acpi_gbl_reduced_hardware) {
+ return;
+ }
+
+ /* Examine all of the 64-bit extended address fields (X fields) */
+
+ for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) {
+ /*
+ * Get the 32-bit and 64-bit addresses, as well as the register
+ * length and register name.
+ */
+ address32 = *ACPI_ADD_PTR(u32,
+ &acpi_gbl_FADT,
+ fadt_info_table[i].address32);
+
+ address64 = ACPI_ADD_PTR(struct acpi_generic_address,
+ &acpi_gbl_FADT,
+ fadt_info_table[i].address64);
+
+ length = *ACPI_ADD_PTR(u8,
+ &acpi_gbl_FADT,
+ fadt_info_table[i].length);
+
+ name = fadt_info_table[i].name;
+ flags = fadt_info_table[i].flags;
+
+ /*
+ * Expand the ACPI 1.0 32-bit addresses to the ACPI 2.0 64-bit "X"
+ * generic address structures as necessary. Later code will always use
+ * the 64-bit address structures.
+ *
+ * November 2013:
+ * Now always use the 64-bit address if it is valid (non-zero), in
+ * accordance with the ACPI specification which states that a 64-bit
+ * address supersedes the 32-bit version. This behavior can be
+ * overridden by the acpi_gbl_use32_bit_fadt_addresses flag.
+ *
+ * During 64-bit address construction and verification,
+ * these cases are handled:
+ *
+ * Address32 zero, Address64 [don't care] - Use Address64
+ *
+ * No override: if acpi_gbl_use32_bit_fadt_addresses is FALSE, and:
+ * Address32 non-zero, Address64 zero - Copy/use Address32
+ * Address32 non-zero == Address64 non-zero - Use Address64
+ * Address32 non-zero != Address64 non-zero - Warning, use Address64
+ *
+ * Override: if acpi_gbl_use32_bit_fadt_addresses is TRUE, and:
+ * Address32 non-zero, Address64 zero - Copy/use Address32
+ * Address32 non-zero == Address64 non-zero - Copy/use Address32
+ * Address32 non-zero != Address64 non-zero - Warning, copy/use Address32
+ *
+ * Note: space_id is always I/O for 32-bit legacy address fields
+ */
+ if (address32) {
+ if (address64->address) {
+ if (address64->address != (u64)address32) {
+
+ /* Address mismatch */
+
+ ACPI_BIOS_WARNING((AE_INFO,
+ "32/64X address mismatch in FADT/%s: "
+ "0x%8.8X/0x%8.8X%8.8X, using %u-bit address",
+ name, address32,
+ ACPI_FORMAT_UINT64
+ (address64->address),
+ acpi_gbl_use32_bit_fadt_addresses
+ ? 32 : 64));
+ }
+
+ /*
+ * For each extended field, check for length mismatch
+ * between the legacy length field and the corresponding
+ * 64-bit X length field.
+ * Note: If the legacy length field is > 0xFF bits, ignore
+ * this check. (GPE registers can be larger than the
+ * 64-bit GAS structure can accommodate, 0xFF bits).
+ */
+ if ((ACPI_MUL_8(length) <= ACPI_UINT8_MAX) &&
+ (address64->bit_width !=
+ ACPI_MUL_8(length))) {
+ ACPI_BIOS_WARNING((AE_INFO,
+ "32/64X length mismatch in FADT/%s: %u/%u",
+ name,
+ ACPI_MUL_8(length),
+ address64->
+ bit_width));
+ }
+ }
+
+ /*
+ * Hardware register access code always uses the 64-bit fields.
+ * So if the 64-bit field is zero or is to be overridden,
+ * initialize it with the 32-bit fields.
+ * Note that when the 32-bit address favor is specified, the
+ * 64-bit fields are always re-initialized so that
+ * access_size/bit_width/bit_offset fields can be correctly
+ * configured to the values to trigger a 32-bit compatible
+ * access mode in the hardware register access code.
+ */
+ if (!address64->address
+ || acpi_gbl_use32_bit_fadt_addresses) {
+ acpi_tb_init_generic_address(address64,
+ ACPI_ADR_SPACE_SYSTEM_IO,
+ length,
+ (u64)address32,
+ name, flags);
+ }
+ }
+
+ if (fadt_info_table[i].flags & ACPI_FADT_REQUIRED) {
+ /*
+ * Field is required (Pm1a_event, Pm1a_control).
+ * Both the address and length must be non-zero.
+ */
+ if (!address64->address || !length) {
+ ACPI_BIOS_ERROR((AE_INFO,
+ "Required FADT field %s has zero address and/or length: "
+ "0x%8.8X%8.8X/0x%X",
+ name,
+ ACPI_FORMAT_UINT64(address64->
+ address),
+ length));
+ }
+ } else if (fadt_info_table[i].flags & ACPI_FADT_SEPARATE_LENGTH) {
+ /*
+ * Field is optional (Pm2_control, GPE0, GPE1) AND has its own
+ * length field. If present, both the address and length must
+ * be valid.
+ */
+ if ((address64->address && !length) ||
+ (!address64->address && length)) {
+ ACPI_BIOS_WARNING((AE_INFO,
+ "Optional FADT field %s has valid %s but zero %s: "
+ "0x%8.8X%8.8X/0x%X", name,
+ (length ? "Length" :
+ "Address"),
+ (length ? "Address" :
+ "Length"),
+ ACPI_FORMAT_UINT64
+ (address64->address),
+ length));
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_setup_fadt_registers
+ *
+ * PARAMETERS: None, uses acpi_gbl_FADT.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Initialize global ACPI PM1 register definitions. Optionally,
+ * force FADT register definitions to their default lengths.
+ *
+ ******************************************************************************/
+
+static void acpi_tb_setup_fadt_registers(void)
+{
+ struct acpi_generic_address *target64;
+ struct acpi_generic_address *source64;
+ u8 pm1_register_byte_width;
+ u32 i;
+
+ /*
+ * Optionally check all register lengths against the default values and
+ * update them if they are incorrect.
+ */
+ if (acpi_gbl_use_default_register_widths) {
+ for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) {
+ target64 =
+ ACPI_ADD_PTR(struct acpi_generic_address,
+ &acpi_gbl_FADT,
+ fadt_info_table[i].address64);
+
+ /*
+ * If a valid register (Address != 0) and the (default_length > 0)
+ * (Not a GPE register), then check the width against the default.
+ */
+ if ((target64->address) &&
+ (fadt_info_table[i].default_length > 0) &&
+ (fadt_info_table[i].default_length !=
+ target64->bit_width)) {
+ ACPI_BIOS_WARNING((AE_INFO,
+ "Invalid length for FADT/%s: %u, using default %u",
+ fadt_info_table[i].name,
+ target64->bit_width,
+ fadt_info_table[i].
+ default_length));
+
+ /* Incorrect size, set width to the default */
+
+ target64->bit_width =
+ fadt_info_table[i].default_length;
+ }
+ }
+ }
+
+ /*
+ * Get the length of the individual PM1 registers (enable and status).
+ * Each register is defined to be (event block length / 2). Extra divide
+ * by 8 converts bits to bytes.
+ */
+ pm1_register_byte_width = (u8)
+ ACPI_DIV_16(acpi_gbl_FADT.xpm1a_event_block.bit_width);
+
+ /*
+ * Calculate separate GAS structs for the PM1x (A/B) Status and Enable
+ * registers. These addresses do not appear (directly) in the FADT, so it
+ * is useful to pre-calculate them from the PM1 Event Block definitions.
+ *
+ * The PM event blocks are split into two register blocks, first is the
+ * PM Status Register block, followed immediately by the PM Enable
+ * Register block. Each is of length (pm1_event_length/2)
+ *
+ * Note: The PM1A event block is required by the ACPI specification.
+ * However, the PM1B event block is optional and is rarely, if ever,
+ * used.
+ */
+
+ for (i = 0; i < ACPI_FADT_PM_INFO_ENTRIES; i++) {
+ source64 =
+ ACPI_ADD_PTR(struct acpi_generic_address, &acpi_gbl_FADT,
+ fadt_pm_info_table[i].source);
+
+ if (source64->address) {
+ acpi_tb_init_generic_address(fadt_pm_info_table[i].
+ target, source64->space_id,
+ pm1_register_byte_width,
+ source64->address +
+ (fadt_pm_info_table[i].
+ register_num *
+ pm1_register_byte_width),
+ "PmRegisters", 0);
+ }
+ }
+}
diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c
new file mode 100644
index 000000000..c31a5ddb0
--- /dev/null
+++ b/drivers/acpi/acpica/tbfind.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: tbfind - find table
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_TABLES
+ACPI_MODULE_NAME("tbfind")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_find_table
+ *
+ * PARAMETERS: signature - String with ACPI table signature
+ * oem_id - String with the table OEM ID
+ * oem_table_id - String with the OEM Table ID
+ * table_index - Where the table index is returned
+ *
+ * RETURN: Status and table index
+ *
+ * DESCRIPTION: Find an ACPI table (in the RSDT/XSDT) that matches the
+ * Signature, OEM ID and OEM Table ID. Returns an index that can
+ * be used to get the table header or entire table.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_tb_find_table(char *signature,
+ char *oem_id, char *oem_table_id, u32 *table_index)
+{
+ acpi_status status = AE_OK;
+ struct acpi_table_header header;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(tb_find_table);
+
+ /* Validate the input table signature */
+
+ if (!acpi_ut_valid_nameseg(signature)) {
+ return_ACPI_STATUS(AE_BAD_SIGNATURE);
+ }
+
+ /* Don't allow the OEM strings to be too long */
+
+ if ((strlen(oem_id) > ACPI_OEM_ID_SIZE) ||
+ (strlen(oem_table_id) > ACPI_OEM_TABLE_ID_SIZE)) {
+ return_ACPI_STATUS(AE_AML_STRING_LIMIT);
+ }
+
+ /* Normalize the input strings */
+
+ 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);
+
+ /* Search for the table */
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) {
+ if (memcmp(&(acpi_gbl_root_table_list.tables[i].signature),
+ header.signature, ACPI_NAMESEG_SIZE)) {
+
+ /* Not the requested table */
+
+ continue;
+ }
+
+ /* Table with matching signature has been found */
+
+ if (!acpi_gbl_root_table_list.tables[i].pointer) {
+
+ /* Table is not currently mapped, map it */
+
+ status =
+ acpi_tb_validate_table(&acpi_gbl_root_table_list.
+ tables[i]);
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+
+ if (!acpi_gbl_root_table_list.tables[i].pointer) {
+ continue;
+ }
+ }
+
+ /* Check for table match on all IDs */
+
+ if (!memcmp
+ (acpi_gbl_root_table_list.tables[i].pointer->signature,
+ header.signature, ACPI_NAMESEG_SIZE) && (!oem_id[0]
+ ||
+ !memcmp
+ (acpi_gbl_root_table_list.
+ tables[i].
+ pointer->oem_id,
+ header.oem_id,
+ ACPI_OEM_ID_SIZE))
+ && (!oem_table_id[0]
+ || !memcmp(acpi_gbl_root_table_list.tables[i].pointer->
+ oem_table_id, header.oem_table_id,
+ ACPI_OEM_TABLE_ID_SIZE))) {
+ *table_index = i;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_TABLES,
+ "Found table [%4.4s]\n",
+ header.signature));
+ goto unlock_and_exit;
+ }
+ }
+ status = AE_NOT_FOUND;
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c
new file mode 100644
index 000000000..499efcaf7
--- /dev/null
+++ b/drivers/acpi/acpica/tbinstal.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: tbinstal - ACPI table installation and removal
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_TABLES
+ACPI_MODULE_NAME("tbinstal")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_install_table_with_override
+ *
+ * PARAMETERS: new_table_desc - New table descriptor to install
+ * override - Whether override should be performed
+ * table_index - Where the table index is returned
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Install an ACPI table into the global data structure. The
+ * table override mechanism is called to allow the host
+ * OS to replace any table before it is installed in the root
+ * table array.
+ *
+ ******************************************************************************/
+void
+acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc,
+ u8 override, u32 *table_index)
+{
+ u32 i;
+ acpi_status status;
+
+ status = acpi_tb_get_next_table_descriptor(&i, NULL);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+
+ /*
+ * ACPI Table Override:
+ *
+ * Before we install the table, let the host OS override it with a new
+ * one if desired. Any table within the RSDT/XSDT can be replaced,
+ * including the DSDT which is pointed to by the FADT.
+ */
+ if (override) {
+ acpi_tb_override_table(new_table_desc);
+ }
+
+ acpi_tb_init_table_descriptor(&acpi_gbl_root_table_list.tables[i],
+ new_table_desc->address,
+ new_table_desc->flags,
+ new_table_desc->pointer);
+
+ acpi_tb_print_table_header(new_table_desc->address,
+ new_table_desc->pointer);
+
+ /* This synchronizes acpi_gbl_dsdt_index */
+
+ *table_index = i;
+
+ /* Set the global integer width (based upon revision of the DSDT) */
+
+ if (i == acpi_gbl_dsdt_index) {
+ acpi_ut_set_integer_width(new_table_desc->pointer->revision);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_install_standard_table
+ *
+ * PARAMETERS: address - Address of the table (might be a virtual
+ * address depending on the table_flags)
+ * flags - Flags for the table
+ * table - Pointer to the table (required for virtual
+ * origins, optional for physical)
+ * reload - Whether reload should be performed
+ * override - Whether override should be performed
+ * table_index - Where the table index is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to verify and install an ACPI table.
+ * When this function is called by "Load" or "LoadTable" opcodes,
+ * or by acpi_load_table() API, the "Reload" parameter is set.
+ * After successfully returning from this function, table is
+ * "INSTALLED" but not "VALIDATED".
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_install_standard_table(acpi_physical_address address,
+ u8 flags,
+ struct acpi_table_header *table,
+ u8 reload, u8 override, u32 *table_index)
+{
+ u32 i;
+ acpi_status status = AE_OK;
+ struct acpi_table_desc new_table_desc;
+
+ ACPI_FUNCTION_TRACE(tb_install_standard_table);
+
+ /* Acquire a temporary table descriptor for validation */
+
+ status =
+ acpi_tb_acquire_temp_table(&new_table_desc, address, flags, table);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO,
+ "Could not acquire table length at %8.8X%8.8X",
+ ACPI_FORMAT_UINT64(address)));
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Optionally do not load any SSDTs from the RSDT/XSDT. This can
+ * be useful for debugging ACPI problems on some machines.
+ */
+ if (!reload &&
+ acpi_gbl_disable_ssdt_table_install &&
+ ACPI_COMPARE_NAMESEG(&new_table_desc.signature, ACPI_SIG_SSDT)) {
+ ACPI_INFO(("Ignoring installation of %4.4s at %8.8X%8.8X",
+ new_table_desc.signature.ascii,
+ ACPI_FORMAT_UINT64(address)));
+ goto release_and_exit;
+ }
+
+ /* Acquire the table lock */
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+ /* Validate and verify a table before installation */
+
+ status = acpi_tb_verify_temp_table(&new_table_desc, NULL, &i);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_CTRL_TERMINATE) {
+ /*
+ * Table was unloaded, allow it to be reloaded.
+ * As we are going to return AE_OK to the caller, we should
+ * take the responsibility of freeing the input descriptor.
+ * Refill the input descriptor to ensure
+ * acpi_tb_install_table_with_override() can be called again to
+ * indicate the re-installation.
+ */
+ acpi_tb_uninstall_table(&new_table_desc);
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ *table_index = i;
+ return_ACPI_STATUS(AE_OK);
+ }
+ goto unlock_and_exit;
+ }
+
+ /* Add the table to the global root table list */
+
+ acpi_tb_install_table_with_override(&new_table_desc, override,
+ table_index);
+
+ /* Invoke table handler */
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ acpi_tb_notify_table(ACPI_TABLE_EVENT_INSTALL, new_table_desc.pointer);
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+unlock_and_exit:
+
+ /* Release the table lock */
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+
+release_and_exit:
+
+ /* Release the temporary table descriptor */
+
+ acpi_tb_release_temp_table(&new_table_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_override_table
+ *
+ * PARAMETERS: old_table_desc - Validated table descriptor to be
+ * overridden
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Attempt table override by calling the OSL override functions.
+ * Note: If the table is overridden, then the entire new table
+ * is acquired and returned by this function.
+ * Before/after invocation, the table descriptor is in a state
+ * that is "VALIDATED".
+ *
+ ******************************************************************************/
+
+void acpi_tb_override_table(struct acpi_table_desc *old_table_desc)
+{
+ acpi_status status;
+ struct acpi_table_desc new_table_desc;
+ struct acpi_table_header *table;
+ acpi_physical_address address;
+ u32 length;
+ ACPI_ERROR_ONLY(char *override_type);
+
+ /* (1) Attempt logical override (returns a logical address) */
+
+ status = acpi_os_table_override(old_table_desc->pointer, &table);
+ if (ACPI_SUCCESS(status) && table) {
+ acpi_tb_acquire_temp_table(&new_table_desc,
+ ACPI_PTR_TO_PHYSADDR(table),
+ ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
+ table);
+ ACPI_ERROR_ONLY(override_type = "Logical");
+ goto finish_override;
+ }
+
+ /* (2) Attempt physical override (returns a physical address) */
+
+ status = acpi_os_physical_table_override(old_table_desc->pointer,
+ &address, &length);
+ if (ACPI_SUCCESS(status) && address && length) {
+ acpi_tb_acquire_temp_table(&new_table_desc, address,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
+ NULL);
+ ACPI_ERROR_ONLY(override_type = "Physical");
+ goto finish_override;
+ }
+
+ return; /* There was no override */
+
+finish_override:
+
+ /*
+ * Validate and verify a table before overriding, no nested table
+ * duplication check as it's too complicated and unnecessary.
+ */
+ status = acpi_tb_verify_temp_table(&new_table_desc, NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+
+ ACPI_INFO(("%4.4s 0x%8.8X%8.8X"
+ " %s table override, new table: 0x%8.8X%8.8X",
+ old_table_desc->signature.ascii,
+ ACPI_FORMAT_UINT64(old_table_desc->address),
+ override_type, ACPI_FORMAT_UINT64(new_table_desc.address)));
+
+ /* We can now uninstall the original table */
+
+ acpi_tb_uninstall_table(old_table_desc);
+
+ /*
+ * Replace the original table descriptor and keep its state as
+ * "VALIDATED".
+ */
+ acpi_tb_init_table_descriptor(old_table_desc, new_table_desc.address,
+ new_table_desc.flags,
+ new_table_desc.pointer);
+ acpi_tb_validate_temp_table(old_table_desc);
+
+ /* Release the temporary table descriptor */
+
+ acpi_tb_release_temp_table(&new_table_desc);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_uninstall_table
+ *
+ * PARAMETERS: table_desc - Table descriptor
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete one internal ACPI table
+ *
+ ******************************************************************************/
+
+void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc)
+{
+
+ ACPI_FUNCTION_TRACE(tb_uninstall_table);
+
+ /* Table must be installed */
+
+ if (!table_desc->address) {
+ return_VOID;
+ }
+
+ acpi_tb_invalidate_table(table_desc);
+
+ if ((table_desc->flags & ACPI_TABLE_ORIGIN_MASK) ==
+ ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL) {
+ ACPI_FREE(table_desc->pointer);
+ table_desc->pointer = NULL;
+ }
+
+ table_desc->address = ACPI_PTR_TO_PHYSADDR(NULL);
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/tbprint.c b/drivers/acpi/acpica/tbprint.c
new file mode 100644
index 000000000..595547db2
--- /dev/null
+++ b/drivers/acpi/acpica/tbprint.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: tbprint - Table output utilities
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_TABLES
+ACPI_MODULE_NAME("tbprint")
+
+/* Local prototypes */
+static void acpi_tb_fix_string(char *string, acpi_size length);
+
+static void
+acpi_tb_cleanup_table_header(struct acpi_table_header *out_header,
+ struct acpi_table_header *header);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_fix_string
+ *
+ * PARAMETERS: string - String to be repaired
+ * length - Maximum length
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Replace every non-printable or non-ascii byte in the string
+ * with a question mark '?'.
+ *
+ ******************************************************************************/
+
+static void acpi_tb_fix_string(char *string, acpi_size length)
+{
+
+ while (length && *string) {
+ if (!isprint((int)*string)) {
+ *string = '?';
+ }
+
+ string++;
+ length--;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_cleanup_table_header
+ *
+ * PARAMETERS: out_header - Where the cleaned header is returned
+ * header - Input ACPI table header
+ *
+ * RETURN: Returns the cleaned header in out_header
+ *
+ * DESCRIPTION: Copy the table header and ensure that all "string" fields in
+ * the header consist of printable characters.
+ *
+ ******************************************************************************/
+
+static void
+acpi_tb_cleanup_table_header(struct acpi_table_header *out_header,
+ struct acpi_table_header *header)
+{
+
+ memcpy(out_header, header, sizeof(struct acpi_table_header));
+
+ acpi_tb_fix_string(out_header->signature, ACPI_NAMESEG_SIZE);
+ acpi_tb_fix_string(out_header->oem_id, ACPI_OEM_ID_SIZE);
+ acpi_tb_fix_string(out_header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE);
+ acpi_tb_fix_string(out_header->asl_compiler_id, ACPI_NAMESEG_SIZE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_print_table_header
+ *
+ * PARAMETERS: address - Table physical address
+ * header - Table header
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print an ACPI table header. Special cases for FACS and RSDP.
+ *
+ ******************************************************************************/
+
+void
+acpi_tb_print_table_header(acpi_physical_address address,
+ struct acpi_table_header *header)
+{
+ struct acpi_table_header local_header;
+
+ if (ACPI_COMPARE_NAMESEG(header->signature, ACPI_SIG_FACS)) {
+
+ /* FACS only has signature and length fields */
+
+ ACPI_INFO(("%-4.4s 0x%8.8X%8.8X %06X",
+ header->signature, ACPI_FORMAT_UINT64(address),
+ header->length));
+ } else if (ACPI_VALIDATE_RSDP_SIG(ACPI_CAST_PTR(struct acpi_table_rsdp,
+ header)->signature)) {
+
+ /* RSDP has no common fields */
+
+ memcpy(local_header.oem_id,
+ ACPI_CAST_PTR(struct acpi_table_rsdp, header)->oem_id,
+ ACPI_OEM_ID_SIZE);
+ acpi_tb_fix_string(local_header.oem_id, ACPI_OEM_ID_SIZE);
+
+ ACPI_INFO(("RSDP 0x%8.8X%8.8X %06X (v%.2d %-6.6s)",
+ ACPI_FORMAT_UINT64(address),
+ (ACPI_CAST_PTR(struct acpi_table_rsdp, header)->
+ revision >
+ 0) ? ACPI_CAST_PTR(struct acpi_table_rsdp,
+ header)->length : 20,
+ ACPI_CAST_PTR(struct acpi_table_rsdp,
+ header)->revision,
+ local_header.oem_id));
+ } else {
+ /* Standard ACPI table with full common header */
+
+ acpi_tb_cleanup_table_header(&local_header, header);
+
+ ACPI_INFO(("%-4.4s 0x%8.8X%8.8X"
+ " %06X (v%.2d %-6.6s %-8.8s %08X %-4.4s %08X)",
+ local_header.signature, ACPI_FORMAT_UINT64(address),
+ local_header.length, local_header.revision,
+ local_header.oem_id, local_header.oem_table_id,
+ local_header.oem_revision,
+ 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);
+}
diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c
new file mode 100644
index 000000000..633a823be
--- /dev/null
+++ b/drivers/acpi/acpica/tbutils.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: tbutils - ACPI Table utilities
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_TABLES
+ACPI_MODULE_NAME("tbutils")
+
+/* Local prototypes */
+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
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a permanent mapping for the FADT and save it in a global
+ * for accessing the Global Lock and Firmware Waking Vector
+ *
+ ******************************************************************************/
+
+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 &&
+ (!acpi_gbl_FADT.facs
+ || !acpi_gbl_use32_bit_facs_addresses)) {
+ (void)acpi_get_table_by_index(acpi_gbl_xfacs_index,
+ ACPI_CAST_INDIRECT_PTR(struct
+ acpi_table_header,
+ &facs));
+ acpi_gbl_FACS = facs;
+ } else if (acpi_gbl_FADT.facs) {
+ (void)acpi_get_table_by_index(acpi_gbl_facs_index,
+ ACPI_CAST_INDIRECT_PTR(struct
+ acpi_table_header,
+ &facs));
+ acpi_gbl_FACS = facs;
+ }
+
+ /* If there is no FACS, just continue. There was already an error msg */
+
+ return (AE_OK);
+}
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_check_dsdt_header
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Quick compare to check validity of the DSDT. This will detect
+ * if the DSDT has been replaced from outside the OS and/or if
+ * the DSDT header has been corrupted.
+ *
+ ******************************************************************************/
+
+void acpi_tb_check_dsdt_header(void)
+{
+
+ /* Compare original length and checksum to current values */
+
+ if (acpi_gbl_original_dsdt_header.length != acpi_gbl_DSDT->length ||
+ acpi_gbl_original_dsdt_header.checksum != acpi_gbl_DSDT->checksum) {
+ ACPI_BIOS_ERROR((AE_INFO,
+ "The DSDT has been corrupted or replaced - "
+ "old, new headers below"));
+
+ acpi_tb_print_table_header(0, &acpi_gbl_original_dsdt_header);
+ acpi_tb_print_table_header(0, acpi_gbl_DSDT);
+
+ ACPI_ERROR((AE_INFO,
+ "Please send DMI info to linux-acpi@vger.kernel.org\n"
+ "If system does not work as expected, please boot with acpi=copy_dsdt"));
+
+ /* Disable further error messages */
+
+ acpi_gbl_original_dsdt_header.length = acpi_gbl_DSDT->length;
+ acpi_gbl_original_dsdt_header.checksum =
+ acpi_gbl_DSDT->checksum;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_copy_dsdt
+ *
+ * PARAMETERS: table_index - Index of installed table to copy
+ *
+ * RETURN: The copied DSDT
+ *
+ * DESCRIPTION: Implements a subsystem option to copy the DSDT to local memory.
+ * Some very bad BIOSs are known to either corrupt the DSDT or
+ * install a new, bad DSDT. This copy works around the problem.
+ *
+ ******************************************************************************/
+
+struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index)
+{
+ struct acpi_table_header *new_table;
+ struct acpi_table_desc *table_desc;
+
+ table_desc = &acpi_gbl_root_table_list.tables[table_index];
+
+ new_table = ACPI_ALLOCATE(table_desc->length);
+ if (!new_table) {
+ ACPI_ERROR((AE_INFO, "Could not copy DSDT of length 0x%X",
+ table_desc->length));
+ return (NULL);
+ }
+
+ memcpy(new_table, table_desc->pointer, table_desc->length);
+ acpi_tb_uninstall_table(table_desc);
+
+ acpi_tb_init_table_descriptor(&acpi_gbl_root_table_list.
+ tables[acpi_gbl_dsdt_index],
+ ACPI_PTR_TO_PHYSADDR(new_table),
+ ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL,
+ new_table);
+
+ ACPI_INFO(("Forced DSDT copy: length 0x%05X copied locally, original unmapped", new_table->length));
+
+ return (new_table);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_get_root_table_entry
+ *
+ * PARAMETERS: table_entry - Pointer to the RSDT/XSDT table entry
+ * table_entry_size - sizeof 32 or 64 (RSDT or XSDT)
+ *
+ * RETURN: Physical address extracted from the root table
+ *
+ * DESCRIPTION: Get one root table entry. Handles 32-bit and 64-bit cases on
+ * both 32-bit and 64-bit platforms
+ *
+ * NOTE: acpi_physical_address is 32-bit on 32-bit platforms, 64-bit on
+ * 64-bit platforms.
+ *
+ ******************************************************************************/
+
+static acpi_physical_address
+acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size)
+{
+ u64 address64;
+
+ /*
+ * Get the table physical address (32-bit for RSDT, 64-bit for XSDT):
+ * Note: Addresses are 32-bit aligned (not 64) in both RSDT and XSDT
+ */
+ if (table_entry_size == ACPI_RSDT_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)));
+ } else {
+ /*
+ * 32-bit platform, XSDT: Truncate 64-bit to 32-bit and return
+ * 64-bit platform, XSDT: Move (unaligned) 64-bit to local,
+ * return 64-bit
+ */
+ ACPI_MOVE_64_TO_64(&address64, table_entry);
+
+#if ACPI_MACHINE_WIDTH == 32
+ if (address64 > ACPI_UINT32_MAX) {
+
+ /* Will truncate 64-bit address to 32 bits, issue warning */
+
+ ACPI_BIOS_WARNING((AE_INFO,
+ "64-bit Physical Address in XSDT is too large (0x%8.8X%8.8X),"
+ " truncating",
+ ACPI_FORMAT_UINT64(address64)));
+ }
+#endif
+ return ((acpi_physical_address)(address64));
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_parse_root_table
+ *
+ * PARAMETERS: rsdp_address - Pointer to the RSDP
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to parse the Root System Description
+ * Table (RSDT or XSDT)
+ *
+ * NOTE: Tables are mapped (not copied) for efficiency. The FACS must
+ * be mapped and cannot be copied because it contains the actual
+ * memory location of the ACPI Global Lock.
+ *
+ ******************************************************************************/
+
+acpi_status ACPI_INIT_FUNCTION
+acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
+{
+ struct acpi_table_rsdp *rsdp;
+ u32 table_entry_size;
+ u32 i;
+ u32 table_count;
+ struct acpi_table_header *table;
+ acpi_physical_address address;
+ u32 length;
+ u8 *table_entry;
+ acpi_status status;
+ u32 table_index;
+
+ ACPI_FUNCTION_TRACE(tb_parse_root_table);
+
+ /* Map the entire RSDP and extract the address of the RSDT or XSDT */
+
+ rsdp = acpi_os_map_memory(rsdp_address, sizeof(struct acpi_table_rsdp));
+ if (!rsdp) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ acpi_tb_print_table_header(rsdp_address,
+ ACPI_CAST_PTR(struct acpi_table_header,
+ rsdp));
+
+ /* Use XSDT if present and not overridden. Otherwise, use RSDT */
+
+ if ((rsdp->revision > 1) &&
+ rsdp->xsdt_physical_address && !acpi_gbl_do_not_use_xsdt) {
+ /*
+ * RSDP contains an XSDT (64-bit physical addresses). We must use
+ * the XSDT if the revision is > 1 and the XSDT pointer is present,
+ * as per the ACPI specification.
+ */
+ address = (acpi_physical_address)rsdp->xsdt_physical_address;
+ table_entry_size = ACPI_XSDT_ENTRY_SIZE;
+ } else {
+ /* Root table is an RSDT (32-bit physical addresses) */
+
+ address = (acpi_physical_address)rsdp->rsdt_physical_address;
+ table_entry_size = ACPI_RSDT_ENTRY_SIZE;
+ }
+
+ /*
+ * It is not possible to map more than one entry in some environments,
+ * so unmap the RSDP here before mapping other tables
+ */
+ acpi_os_unmap_memory(rsdp, sizeof(struct acpi_table_rsdp));
+
+ /* Map the RSDT/XSDT table header to get the full table length */
+
+ table = acpi_os_map_memory(address, sizeof(struct acpi_table_header));
+ if (!table) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ acpi_tb_print_table_header(address, table);
+
+ /*
+ * Validate length of the table, and map entire table.
+ * Minimum length table must contain at least one entry.
+ */
+ length = table->length;
+ acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
+
+ if (length < (sizeof(struct acpi_table_header) + table_entry_size)) {
+ ACPI_BIOS_ERROR((AE_INFO,
+ "Invalid table length 0x%X in RSDT/XSDT",
+ length));
+ return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
+ }
+
+ table = acpi_os_map_memory(address, length);
+ if (!table) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Validate the root table checksum */
+
+ status = acpi_tb_verify_checksum(table, length);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_unmap_memory(table, length);
+ return_ACPI_STATUS(status);
+ }
+
+ /* Get the number of entries and pointer to first entry */
+
+ table_count = (u32)((table->length - sizeof(struct acpi_table_header)) /
+ table_entry_size);
+ table_entry = ACPI_ADD_PTR(u8, table, sizeof(struct acpi_table_header));
+
+ /* Initialize the root table array from the RSDT/XSDT */
+
+ for (i = 0; i < table_count; i++) {
+
+ /* Get the table physical address (32-bit for RSDT, 64-bit for XSDT) */
+
+ address =
+ acpi_tb_get_root_table_entry(table_entry, table_entry_size);
+
+ /* Skip NULL entries in RSDT/XSDT */
+
+ if (!address) {
+ goto next_table;
+ }
+
+ status = acpi_tb_install_standard_table(address,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
+ NULL, FALSE, TRUE,
+ &table_index);
+
+ if (ACPI_SUCCESS(status) &&
+ ACPI_COMPARE_NAMESEG(&acpi_gbl_root_table_list.
+ tables[table_index].signature,
+ ACPI_SIG_FADT)) {
+ acpi_gbl_fadt_index = table_index;
+ acpi_tb_parse_fadt();
+ }
+
+next_table:
+
+ table_entry += table_entry_size;
+ }
+
+ acpi_os_unmap_memory(table, length);
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_get_table
+ *
+ * PARAMETERS: table_desc - Table descriptor
+ * out_table - Where the pointer to the table is returned
+ *
+ * RETURN: Status and pointer to the requested table
+ *
+ * DESCRIPTION: Increase a reference to a table descriptor and return the
+ * validated table pointer.
+ * If the table descriptor is an entry of the root table list,
+ * this API must be invoked with ACPI_MTX_TABLES acquired.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_get_table(struct acpi_table_desc *table_desc,
+ struct acpi_table_header **out_table)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_tb_get_table);
+
+ if (table_desc->validation_count == 0) {
+
+ /* Table need to be "VALIDATED" */
+
+ status = acpi_tb_validate_table(table_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ if (table_desc->validation_count < ACPI_MAX_TABLE_VALIDATIONS) {
+ table_desc->validation_count++;
+
+ /*
+ * Detect validation_count overflows to ensure that the warning
+ * message will only be printed once.
+ */
+ if (table_desc->validation_count >= ACPI_MAX_TABLE_VALIDATIONS) {
+ ACPI_WARNING((AE_INFO,
+ "Table %p, Validation count overflows\n",
+ table_desc));
+ }
+ }
+
+ *out_table = table_desc->pointer;
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_put_table
+ *
+ * PARAMETERS: table_desc - Table descriptor
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decrease a reference to a table descriptor and release the
+ * validated table pointer if no references.
+ * If the table descriptor is an entry of the root table list,
+ * this API must be invoked with ACPI_MTX_TABLES acquired.
+ *
+ ******************************************************************************/
+
+void acpi_tb_put_table(struct acpi_table_desc *table_desc)
+{
+
+ ACPI_FUNCTION_TRACE(acpi_tb_put_table);
+
+ if (table_desc->validation_count < ACPI_MAX_TABLE_VALIDATIONS) {
+ table_desc->validation_count--;
+
+ /*
+ * Detect validation_count underflows to ensure that the warning
+ * message will only be printed once.
+ */
+ if (table_desc->validation_count >= ACPI_MAX_TABLE_VALIDATIONS) {
+ ACPI_WARNING((AE_INFO,
+ "Table %p, Validation count underflows\n",
+ table_desc));
+ return_VOID;
+ }
+ }
+
+ if (table_desc->validation_count == 0) {
+
+ /* Table need to be "INVALIDATED" */
+
+ acpi_tb_invalidate_table(table_desc);
+ }
+
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c
new file mode 100644
index 000000000..37da09dca
--- /dev/null
+++ b/drivers/acpi/acpica/tbxface.c
@@ -0,0 +1,530 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: tbxface - ACPI table-oriented external interfaces
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_TABLES
+ACPI_MODULE_NAME("tbxface")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_allocate_root_table
+ *
+ * PARAMETERS: initial_table_count - Size of initial_table_array, in number of
+ * struct acpi_table_desc structures
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Allocate a root table array. Used by iASL compiler and
+ * acpi_initialize_tables.
+ *
+ ******************************************************************************/
+acpi_status acpi_allocate_root_table(u32 initial_table_count)
+{
+
+ acpi_gbl_root_table_list.max_table_count = initial_table_count;
+ acpi_gbl_root_table_list.flags = ACPI_ROOT_ALLOW_RESIZE;
+
+ return (acpi_tb_resize_root_table_list());
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_initialize_tables
+ *
+ * PARAMETERS: initial_table_array - Pointer to an array of pre-allocated
+ * struct acpi_table_desc structures. If NULL, the
+ * array is dynamically allocated.
+ * initial_table_count - Size of initial_table_array, in number of
+ * struct acpi_table_desc structures
+ * allow_resize - Flag to tell Table Manager if resize of
+ * pre-allocated array is allowed. Ignored
+ * if initial_table_array is NULL.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize the table manager, get the RSDP and RSDT/XSDT.
+ *
+ * NOTE: Allows static allocation of the initial table array in order
+ * to avoid the use of dynamic memory in confined environments
+ * such as the kernel boot sequence where it may not be available.
+ *
+ * If the host OS memory managers are initialized, use NULL for
+ * initial_table_array, and the table will be dynamically allocated.
+ *
+ ******************************************************************************/
+
+acpi_status ACPI_INIT_FUNCTION
+acpi_initialize_tables(struct acpi_table_desc *initial_table_array,
+ u32 initial_table_count, u8 allow_resize)
+{
+ acpi_physical_address rsdp_address;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_initialize_tables);
+
+ /*
+ * Setup the Root Table Array and allocate the table array
+ * if requested
+ */
+ if (!initial_table_array) {
+ status = acpi_allocate_root_table(initial_table_count);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ } else {
+ /* Root Table Array has been statically allocated by the host */
+
+ memset(initial_table_array, 0,
+ (acpi_size)initial_table_count *
+ sizeof(struct acpi_table_desc));
+
+ acpi_gbl_root_table_list.tables = initial_table_array;
+ acpi_gbl_root_table_list.max_table_count = initial_table_count;
+ acpi_gbl_root_table_list.flags = ACPI_ROOT_ORIGIN_UNKNOWN;
+ if (allow_resize) {
+ acpi_gbl_root_table_list.flags |=
+ ACPI_ROOT_ALLOW_RESIZE;
+ }
+ }
+
+ /* Get the address of the RSDP */
+
+ rsdp_address = acpi_os_get_root_pointer();
+ if (!rsdp_address) {
+ return_ACPI_STATUS(AE_NOT_FOUND);
+ }
+
+ /*
+ * Get the root table (RSDT or XSDT) and extract all entries to the local
+ * Root Table Array. This array contains the information of the RSDT/XSDT
+ * in a common, more usable format.
+ */
+ status = acpi_tb_parse_root_table(rsdp_address);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_tables)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_reallocate_root_table
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Reallocate Root Table List into dynamic memory. Copies the
+ * root list from the previously provided scratch area. Should
+ * be called once dynamic memory allocation is available in the
+ * kernel.
+ *
+ ******************************************************************************/
+acpi_status ACPI_INIT_FUNCTION acpi_reallocate_root_table(void)
+{
+ acpi_status status;
+ struct acpi_table_desc *table_desc;
+ u32 i, j;
+
+ ACPI_FUNCTION_TRACE(acpi_reallocate_root_table);
+
+ /*
+ * If there are tables unverified, it is required to reallocate the
+ * root table list to clean up invalid table entries. Otherwise only
+ * reallocate the root table list if the host provided a static buffer
+ * for the table array in the call to acpi_initialize_tables().
+ */
+ if ((acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) &&
+ acpi_gbl_enable_table_validation) {
+ return_ACPI_STATUS(AE_SUPPORT);
+ }
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+ /*
+ * Ensure OS early boot logic, which is required by some hosts. If the
+ * table state is reported to be wrong, developers should fix the
+ * issue by invoking acpi_put_table() for the reported table during the
+ * early stage.
+ */
+ for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) {
+ table_desc = &acpi_gbl_root_table_list.tables[i];
+ if (table_desc->pointer) {
+ ACPI_ERROR((AE_INFO,
+ "Table [%4.4s] is not invalidated during early boot stage",
+ table_desc->signature.ascii));
+ }
+ }
+
+ if (!acpi_gbl_enable_table_validation) {
+ /*
+ * Now it's safe to do full table validation. We can do deferred
+ * table initialization here once the flag is set.
+ */
+ acpi_gbl_enable_table_validation = TRUE;
+ for (i = 0; i < acpi_gbl_root_table_list.current_table_count;
+ ++i) {
+ table_desc = &acpi_gbl_root_table_list.tables[i];
+ if (!(table_desc->flags & ACPI_TABLE_IS_VERIFIED)) {
+ status =
+ acpi_tb_verify_temp_table(table_desc, NULL,
+ &j);
+ if (ACPI_FAILURE(status)) {
+ acpi_tb_uninstall_table(table_desc);
+ }
+ }
+ }
+ }
+
+ acpi_gbl_root_table_list.flags |= ACPI_ROOT_ALLOW_RESIZE;
+ status = acpi_tb_resize_root_table_list();
+ acpi_gbl_root_table_list.flags |= ACPI_ROOT_ORIGIN_ALLOCATED;
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_reallocate_root_table)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_table_header
+ *
+ * PARAMETERS: signature - ACPI signature of needed table
+ * instance - Which instance (for SSDTs)
+ * out_table_header - The pointer to the where the table header
+ * is returned
+ *
+ * RETURN: Status and a copy of the table header
+ *
+ * DESCRIPTION: Finds and returns an ACPI table header. Caller provides the
+ * memory where a copy of the header is to be returned
+ * (fixed length).
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_table_header(char *signature,
+ u32 instance, struct acpi_table_header *out_table_header)
+{
+ u32 i;
+ u32 j;
+ struct acpi_table_header *header;
+
+ /* Parameter validation */
+
+ if (!signature || !out_table_header) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Walk the root table list */
+
+ for (i = 0, j = 0; i < acpi_gbl_root_table_list.current_table_count;
+ i++) {
+ if (!ACPI_COMPARE_NAMESEG
+ (&(acpi_gbl_root_table_list.tables[i].signature),
+ signature)) {
+ continue;
+ }
+
+ if (++j < instance) {
+ continue;
+ }
+
+ if (!acpi_gbl_root_table_list.tables[i].pointer) {
+ if ((acpi_gbl_root_table_list.tables[i].flags &
+ ACPI_TABLE_ORIGIN_MASK) ==
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL) {
+ header =
+ acpi_os_map_memory(acpi_gbl_root_table_list.
+ tables[i].address,
+ sizeof(struct
+ acpi_table_header));
+ if (!header) {
+ return (AE_NO_MEMORY);
+ }
+
+ memcpy(out_table_header, header,
+ sizeof(struct acpi_table_header));
+ acpi_os_unmap_memory(header,
+ sizeof(struct
+ acpi_table_header));
+ } else {
+ return (AE_NOT_FOUND);
+ }
+ } else {
+ memcpy(out_table_header,
+ acpi_gbl_root_table_list.tables[i].pointer,
+ sizeof(struct acpi_table_header));
+ }
+ return (AE_OK);
+ }
+
+ return (AE_NOT_FOUND);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_table_header)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_table
+ *
+ * PARAMETERS: signature - ACPI signature of needed table
+ * instance - Which instance (for SSDTs)
+ * out_table - Where the pointer to the table is returned
+ *
+ * RETURN: Status and pointer to the requested table
+ *
+ * DESCRIPTION: Finds and verifies an ACPI table. Table must be in the
+ * RSDT/XSDT.
+ * Note that an early stage acpi_get_table() call must be paired
+ * with an early stage acpi_put_table() call. otherwise the table
+ * pointer mapped by the early stage mapping implementation may be
+ * erroneously unmapped by the late stage unmapping implementation
+ * in an acpi_put_table() invoked during the late stage.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_table(char *signature,
+ u32 instance, struct acpi_table_header ** out_table)
+{
+ u32 i;
+ u32 j;
+ acpi_status status = AE_NOT_FOUND;
+ struct acpi_table_desc *table_desc;
+
+ /* Parameter validation */
+
+ if (!signature || !out_table) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Note that the following line is required by some OSPMs, they only
+ * check if the returned table is NULL instead of the returned status
+ * to determined if this function is succeeded.
+ */
+ *out_table = NULL;
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+ /* Walk the root table list */
+
+ for (i = 0, j = 0; i < acpi_gbl_root_table_list.current_table_count;
+ i++) {
+ table_desc = &acpi_gbl_root_table_list.tables[i];
+
+ if (!ACPI_COMPARE_NAMESEG(&table_desc->signature, signature)) {
+ continue;
+ }
+
+ if (++j < instance) {
+ continue;
+ }
+
+ status = acpi_tb_get_table(table_desc, out_table);
+ break;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_table)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_put_table
+ *
+ * PARAMETERS: table - The pointer to the table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Release a table returned by acpi_get_table() and its clones.
+ * Note that it is not safe if this function was invoked after an
+ * uninstallation happened to the original table descriptor.
+ * Currently there is no OSPMs' requirement to handle such
+ * situations.
+ *
+ ******************************************************************************/
+void acpi_put_table(struct acpi_table_header *table)
+{
+ u32 i;
+ struct acpi_table_desc *table_desc;
+
+ ACPI_FUNCTION_TRACE(acpi_put_table);
+
+ if (!table) {
+ return_VOID;
+ }
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+ /* Walk the root table list */
+
+ for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) {
+ table_desc = &acpi_gbl_root_table_list.tables[i];
+
+ if (table_desc->pointer != table) {
+ continue;
+ }
+
+ acpi_tb_put_table(table_desc);
+ break;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_VOID;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_put_table)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_table_by_index
+ *
+ * PARAMETERS: table_index - Table index
+ * out_table - Where the pointer to the table is returned
+ *
+ * RETURN: Status and pointer to the requested table
+ *
+ * DESCRIPTION: Obtain a table by an index into the global table list. Used
+ * internally also.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_table_by_index(u32 table_index, struct acpi_table_header **out_table)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_get_table_by_index);
+
+ /* Parameter validation */
+
+ if (!out_table) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Note that the following line is required by some OSPMs, they only
+ * check if the returned table is NULL instead of the returned status
+ * to determined if this function is succeeded.
+ */
+ *out_table = NULL;
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+ /* Validate index */
+
+ if (table_index >= acpi_gbl_root_table_list.current_table_count) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ status =
+ acpi_tb_get_table(&acpi_gbl_root_table_list.tables[table_index],
+ out_table);
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_table_by_index)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_table_handler
+ *
+ * PARAMETERS: handler - Table event handler
+ * context - Value passed to the handler on each event
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a global table event handler.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_install_table_handler(acpi_table_handler handler, void *context)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_table_handler);
+
+ if (!handler) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Don't allow more than one handler */
+
+ if (acpi_gbl_table_handler) {
+ status = AE_ALREADY_EXISTS;
+ goto cleanup;
+ }
+
+ /* Install the handler */
+
+ acpi_gbl_table_handler = handler;
+ acpi_gbl_table_handler_context = context;
+
+cleanup:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_table_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_remove_table_handler
+ *
+ * PARAMETERS: handler - Table event handler that was installed
+ * previously.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove a table event handler
+ *
+ ******************************************************************************/
+acpi_status acpi_remove_table_handler(acpi_table_handler handler)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_remove_table_handler);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Make sure that the installed handler is the same */
+
+ if (!handler || handler != acpi_gbl_table_handler) {
+ status = AE_BAD_PARAMETER;
+ goto cleanup;
+ }
+
+ /* Remove the handler */
+
+ acpi_gbl_table_handler = NULL;
+
+cleanup:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_table_handler)
diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c
new file mode 100644
index 000000000..258796e02
--- /dev/null
+++ b/drivers/acpi/acpica/tbxfload.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: tbxfload - Table load/unload external interfaces
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "actables.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_TABLES
+ACPI_MODULE_NAME("tbxfload")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_load_tables
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load the ACPI tables from the RSDT/XSDT
+ *
+ ******************************************************************************/
+acpi_status ACPI_INIT_FUNCTION acpi_load_tables(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_load_tables);
+
+ /*
+ * Install the default operation region handlers. These are the
+ * handlers that are defined by the ACPI specification to be
+ * "always accessible" -- namely, system_memory, system_IO, and
+ * PCI_Config. This also means that no _REG methods need to be
+ * run for these address spaces. We need to have these handlers
+ * installed before any AML code can be executed, especially any
+ * module-level code (11/2015).
+ * Note that we allow OSPMs to install their own region handlers
+ * between acpi_initialize_subsystem() and acpi_load_tables() to use
+ * their customized default region handlers.
+ */
+ status = acpi_ev_install_region_handlers();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During Region initialization"));
+ return_ACPI_STATUS(status);
+ }
+
+ /* Load the namespace from the tables */
+
+ status = acpi_tb_load_namespace();
+
+ /* Don't let single failures abort the load */
+
+ if (status == AE_CTRL_TERMINATE) {
+ status = AE_OK;
+ }
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While loading namespace from ACPI tables"));
+ }
+
+ /*
+ * Initialize the objects in the namespace that remain uninitialized.
+ * This runs the executable AML that may be part of the declaration of
+ * these name objects:
+ * operation_regions, buffer_fields, Buffers, and Packages.
+ *
+ */
+ status = acpi_ns_initialize_objects();
+ if (ACPI_SUCCESS(status)) {
+ acpi_gbl_namespace_initialized = TRUE;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_load_tables)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_load_namespace
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load the namespace from the DSDT and all SSDTs/PSDTs found in
+ * the RSDT/XSDT.
+ *
+ ******************************************************************************/
+acpi_status acpi_tb_load_namespace(void)
+{
+ acpi_status status;
+ u32 i;
+ struct acpi_table_header *new_dsdt;
+ struct acpi_table_desc *table;
+ u32 tables_loaded = 0;
+ u32 tables_failed = 0;
+
+ ACPI_FUNCTION_TRACE(tb_load_namespace);
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+ /*
+ * Load the namespace. The DSDT is required, but any SSDT and
+ * PSDT tables are optional. Verify the DSDT.
+ */
+ table = &acpi_gbl_root_table_list.tables[acpi_gbl_dsdt_index];
+
+ if (!acpi_gbl_root_table_list.current_table_count ||
+ !ACPI_COMPARE_NAMESEG(table->signature.ascii, ACPI_SIG_DSDT) ||
+ ACPI_FAILURE(acpi_tb_validate_table(table))) {
+ status = AE_NO_ACPI_TABLES;
+ goto unlock_and_exit;
+ }
+
+ /*
+ * Save the DSDT pointer for simple access. This is the mapped memory
+ * address. We must take care here because the address of the .Tables
+ * array can change dynamically as tables are loaded at run-time. Note:
+ * .Pointer field is not validated until after call to acpi_tb_validate_table.
+ */
+ acpi_gbl_DSDT = table->pointer;
+
+ /*
+ * Optionally copy the entire DSDT to local memory (instead of simply
+ * mapping it.) There are some BIOSs that corrupt or replace the original
+ * DSDT, creating the need for this option. Default is FALSE, do not copy
+ * the DSDT.
+ */
+ if (acpi_gbl_copy_dsdt_locally) {
+ new_dsdt = acpi_tb_copy_dsdt(acpi_gbl_dsdt_index);
+ if (new_dsdt) {
+ acpi_gbl_DSDT = new_dsdt;
+ }
+ }
+
+ /*
+ * Save the original DSDT header for detection of table corruption
+ * and/or replacement of the DSDT from outside the OS.
+ */
+ memcpy(&acpi_gbl_original_dsdt_header, acpi_gbl_DSDT,
+ sizeof(struct acpi_table_header));
+
+ /* Load and parse tables */
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ status = acpi_ns_load_table(acpi_gbl_dsdt_index, acpi_gbl_root_node);
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "[DSDT] table load failed"));
+ tables_failed++;
+ } else {
+ tables_loaded++;
+ }
+
+ /* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */
+
+ for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) {
+ table = &acpi_gbl_root_table_list.tables[i];
+
+ if (!table->address ||
+ (!ACPI_COMPARE_NAMESEG
+ (table->signature.ascii, ACPI_SIG_SSDT)
+ && !ACPI_COMPARE_NAMESEG(table->signature.ascii,
+ ACPI_SIG_PSDT)
+ && !ACPI_COMPARE_NAMESEG(table->signature.ascii,
+ ACPI_SIG_OSDT))
+ || ACPI_FAILURE(acpi_tb_validate_table(table))) {
+ continue;
+ }
+
+ /* Ignore errors while loading tables, get as many as possible */
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ status = acpi_ns_load_table(i, acpi_gbl_root_node);
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "(%4.4s:%8.8s) while loading table",
+ table->signature.ascii,
+ table->pointer->oem_table_id));
+
+ tables_failed++;
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
+ "Table [%4.4s:%8.8s] (id FF) - Table namespace load failed\n\n",
+ table->signature.ascii,
+ table->pointer->oem_table_id));
+ } else {
+ tables_loaded++;
+ }
+ }
+
+ if (!tables_failed) {
+ ACPI_INFO(("%u ACPI AML tables successfully acquired and loaded", tables_loaded));
+ } else {
+ ACPI_ERROR((AE_INFO,
+ "%u table load failures, %u successful",
+ tables_failed, tables_loaded));
+
+ /* Indicate at least one failure */
+
+ status = AE_CTRL_TERMINATE;
+ }
+
+#ifdef ACPI_APPLICATION
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT, "\n"));
+#endif
+
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_table
+ *
+ * PARAMETERS: table - Pointer to the ACPI table to be installed.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dynamically install an ACPI table.
+ * Note: This function should only be invoked after
+ * acpi_initialize_tables() and before acpi_load_tables().
+ *
+ ******************************************************************************/
+
+acpi_status ACPI_INIT_FUNCTION
+acpi_install_table(struct acpi_table_header *table)
+{
+ acpi_status status;
+ u32 table_index;
+
+ ACPI_FUNCTION_TRACE(acpi_install_table);
+
+ status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table),
+ ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
+ table, FALSE, FALSE,
+ &table_index);
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_install_table)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_physical_table
+ *
+ * PARAMETERS: address - Address of the ACPI table to be installed.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dynamically install an ACPI table.
+ * Note: This function should only be invoked after
+ * acpi_initialize_tables() and before acpi_load_tables().
+ *
+ ******************************************************************************/
+acpi_status ACPI_INIT_FUNCTION
+acpi_install_physical_table(acpi_physical_address address)
+{
+ acpi_status status;
+ u32 table_index;
+
+ ACPI_FUNCTION_TRACE(acpi_install_physical_table);
+
+ status = acpi_tb_install_standard_table(address,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
+ NULL, FALSE, FALSE,
+ &table_index);
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_install_physical_table)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_load_table
+ *
+ * PARAMETERS: table - Pointer to a buffer containing the ACPI
+ * table to be loaded.
+ * table_idx - Pointer to a u32 for storing the table
+ * index, might be NULL
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dynamically load an ACPI table from the caller's buffer. Must
+ * be a valid ACPI table with a valid ACPI table header.
+ * Note1: Mainly intended to support hotplug addition of SSDTs.
+ * Note2: Does not copy the incoming table. User is responsible
+ * to ensure that the table is not deleted or unmapped.
+ *
+ ******************************************************************************/
+acpi_status acpi_load_table(struct acpi_table_header *table, u32 *table_idx)
+{
+ acpi_status status;
+ u32 table_index;
+
+ ACPI_FUNCTION_TRACE(acpi_load_table);
+
+ /* Parameter validation */
+
+ if (!table) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Install the table and load it into the namespace */
+
+ ACPI_INFO(("Host-directed Dynamic ACPI Table Load:"));
+ status = acpi_tb_install_and_load_table(ACPI_PTR_TO_PHYSADDR(table),
+ ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
+ table, FALSE, &table_index);
+ if (table_idx) {
+ *table_idx = table_index;
+ }
+
+ if (ACPI_SUCCESS(status)) {
+
+ /* Complete the initialization/resolution of new objects */
+
+ acpi_ns_initialize_objects();
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_load_table)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_unload_parent_table
+ *
+ * PARAMETERS: object - Handle to any namespace object owned by
+ * the table to be unloaded
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Via any namespace object within an SSDT or OEMx table, unloads
+ * the table and deletes all namespace objects associated with
+ * that table. Unloading of the DSDT is not allowed.
+ * Note: Mainly intended to support hotplug removal of SSDTs.
+ *
+ ******************************************************************************/
+acpi_status acpi_unload_parent_table(acpi_handle object)
+{
+ struct acpi_namespace_node *node =
+ ACPI_CAST_PTR(struct acpi_namespace_node, object);
+ acpi_status status = AE_NOT_EXIST;
+ acpi_owner_id owner_id;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(acpi_unload_parent_table);
+
+ /* Parameter validation */
+
+ if (!object) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * The node owner_id is currently the same as the parent table ID.
+ * However, this could change in the future.
+ */
+ owner_id = node->owner_id;
+ if (!owner_id) {
+
+ /* owner_id==0 means DSDT is the owner. DSDT cannot be unloaded */
+
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ /* Must acquire the table lock during this operation */
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Find the table in the global table list */
+
+ for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) {
+ if (owner_id != acpi_gbl_root_table_list.tables[i].owner_id) {
+ continue;
+ }
+
+ /*
+ * Allow unload of SSDT and OEMx tables only. Do not allow unload
+ * of the DSDT. No other types of tables should get here, since
+ * only these types can contain AML and thus are the only types
+ * that can create namespace objects.
+ */
+ if (ACPI_COMPARE_NAMESEG
+ (acpi_gbl_root_table_list.tables[i].signature.ascii,
+ ACPI_SIG_DSDT)) {
+ status = AE_TYPE;
+ break;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ status = acpi_tb_unload_table(i);
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+ break;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_unload_parent_table)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_unload_table
+ *
+ * PARAMETERS: table_index - Index as returned by acpi_load_table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Via the table_index representing an SSDT or OEMx table, unloads
+ * the table and deletes all namespace objects associated with
+ * that table. Unloading of the DSDT is not allowed.
+ * Note: Mainly intended to support hotplug removal of SSDTs.
+ *
+ ******************************************************************************/
+acpi_status acpi_unload_table(u32 table_index)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_unload_table);
+
+ if (table_index == 1) {
+
+ /* table_index==1 means DSDT is the owner. DSDT cannot be unloaded */
+
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ status = acpi_tb_unload_table(table_index);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_unload_table)
diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c
new file mode 100644
index 000000000..3d09e3f6b
--- /dev/null
+++ b/drivers/acpi/acpica/tbxfroot.c
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: tbxfroot - Find the root ACPI table (RSDT)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_TABLES
+ACPI_MODULE_NAME("tbxfroot")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_get_rsdp_length
+ *
+ * PARAMETERS: rsdp - Pointer to RSDP
+ *
+ * RETURN: Table length
+ *
+ * DESCRIPTION: Get the length of the RSDP
+ *
+ ******************************************************************************/
+u32 acpi_tb_get_rsdp_length(struct acpi_table_rsdp *rsdp)
+{
+
+ if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature)) {
+
+ /* BAD Signature */
+
+ return (0);
+ }
+
+ /* "Length" field is available if table version >= 2 */
+
+ if (rsdp->revision >= 2) {
+ return (rsdp->length);
+ } else {
+ return (ACPI_RSDP_CHECKSUM_LENGTH);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_validate_rsdp
+ *
+ * PARAMETERS: rsdp - Pointer to unvalidated RSDP
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Validate the RSDP (ptr)
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp)
+{
+
+ /*
+ * The signature and checksum must both be correct
+ *
+ * Note: Sometimes there exists more than one RSDP in memory; the valid
+ * RSDP has a valid checksum, all others have an invalid checksum.
+ */
+ if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature)) {
+
+ /* Nope, BAD Signature */
+
+ return (AE_BAD_SIGNATURE);
+ }
+
+ /* Check the standard checksum */
+
+ if (acpi_tb_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)) {
+ return (AE_BAD_CHECKSUM);
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_find_root_pointer
+ *
+ * PARAMETERS: table_address - Where the table pointer is returned
+ *
+ * RETURN: Status, RSDP physical address
+ *
+ * DESCRIPTION: Search lower 1Mbyte of memory for the root system descriptor
+ * pointer structure. If it is found, set *RSDP to point to it.
+ *
+ * NOTE1: The RSDP must be either in the first 1K of the Extended
+ * BIOS Data Area or between E0000 and FFFFF (From ACPI Spec.)
+ * Only a 32-bit physical address is necessary.
+ *
+ * NOTE2: This function is always available, regardless of the
+ * initialization state of the rest of ACPI.
+ *
+ ******************************************************************************/
+
+acpi_status ACPI_INIT_FUNCTION
+acpi_find_root_pointer(acpi_physical_address *table_address)
+{
+ u8 *table_ptr;
+ u8 *mem_rover;
+ u32 physical_address;
+
+ ACPI_FUNCTION_TRACE(acpi_find_root_pointer);
+
+ /* 1a) Get the location of the Extended BIOS Data Area (EBDA) */
+
+ table_ptr = acpi_os_map_memory((acpi_physical_address)
+ ACPI_EBDA_PTR_LOCATION,
+ ACPI_EBDA_PTR_LENGTH);
+ if (!table_ptr) {
+ ACPI_ERROR((AE_INFO,
+ "Could not map memory at 0x%8.8X for length %u",
+ ACPI_EBDA_PTR_LOCATION, ACPI_EBDA_PTR_LENGTH));
+
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ ACPI_MOVE_16_TO_32(&physical_address, table_ptr);
+
+ /* Convert segment part to physical address */
+
+ physical_address <<= 4;
+ acpi_os_unmap_memory(table_ptr, ACPI_EBDA_PTR_LENGTH);
+
+ /* EBDA present? */
+
+ if (physical_address > 0x400) {
+ /*
+ * 1b) Search EBDA paragraphs (EBDA is required to be a
+ * minimum of 1K length)
+ */
+ table_ptr = acpi_os_map_memory((acpi_physical_address)
+ physical_address,
+ ACPI_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));
+
+ 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);
+
+ if (mem_rover) {
+
+ /* Return the physical address */
+
+ physical_address +=
+ (u32) ACPI_PTR_DIFF(mem_rover, table_ptr);
+
+ *table_address =
+ (acpi_physical_address)physical_address;
+ return_ACPI_STATUS(AE_OK);
+ }
+ }
+
+ /*
+ * 2) Search upper memory: 16-byte boundaries in E0000h-FFFFFh
+ */
+ table_ptr = acpi_os_map_memory((acpi_physical_address)
+ ACPI_HI_RSDP_WINDOW_BASE,
+ ACPI_HI_RSDP_WINDOW_SIZE);
+
+ if (!table_ptr) {
+ ACPI_ERROR((AE_INFO,
+ "Could not map memory at 0x%8.8X for length %u",
+ ACPI_HI_RSDP_WINDOW_BASE,
+ ACPI_HI_RSDP_WINDOW_SIZE));
+
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ mem_rover =
+ acpi_tb_scan_memory_for_rsdp(table_ptr, ACPI_HI_RSDP_WINDOW_SIZE);
+ acpi_os_unmap_memory(table_ptr, ACPI_HI_RSDP_WINDOW_SIZE);
+
+ if (mem_rover) {
+
+ /* Return the physical address */
+
+ physical_address = (u32)
+ (ACPI_HI_RSDP_WINDOW_BASE +
+ ACPI_PTR_DIFF(mem_rover, table_ptr));
+
+ *table_address = (acpi_physical_address)physical_address;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* A valid RSDP was not found */
+
+ ACPI_BIOS_ERROR((AE_INFO, "A valid RSDP was not found"));
+ return_ACPI_STATUS(AE_NOT_FOUND);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_find_root_pointer)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_scan_memory_for_rsdp
+ *
+ * PARAMETERS: start_address - Starting pointer for search
+ * length - Maximum length to search
+ *
+ * RETURN: Pointer to the RSDP if found, otherwise NULL.
+ *
+ * DESCRIPTION: Search a block of memory for the RSDP signature
+ *
+ ******************************************************************************/
+u8 *acpi_tb_scan_memory_for_rsdp(u8 *start_address, u32 length)
+{
+ acpi_status status;
+ u8 *mem_rover;
+ u8 *end_address;
+
+ ACPI_FUNCTION_TRACE(tb_scan_memory_for_rsdp);
+
+ end_address = start_address + length;
+
+ /* Search from given start address for the requested length */
+
+ for (mem_rover = start_address; mem_rover < end_address;
+ mem_rover += ACPI_RSDP_SCAN_STEP) {
+
+ /* The RSDP signature and checksum must both be correct */
+
+ status =
+ acpi_tb_validate_rsdp(ACPI_CAST_PTR
+ (struct acpi_table_rsdp, mem_rover));
+ if (ACPI_SUCCESS(status)) {
+
+ /* Sig and checksum valid, we have found a real RSDP */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "RSDP located at physical address %p\n",
+ mem_rover));
+ return_PTR(mem_rover);
+ }
+
+ /* No sig match or bad checksum, keep searching */
+ }
+
+ /* Searched entire block, no RSDP was found */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Searched entire block from %p, valid RSDP was not found\n",
+ start_address));
+ return_PTR(NULL);
+}
diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c
new file mode 100644
index 000000000..915321806
--- /dev/null
+++ b/drivers/acpi/acpica/utaddress.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utaddress - op_region address range check
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utaddress")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_add_address_range
+ *
+ * PARAMETERS: space_id - Address space ID
+ * address - op_region start address
+ * length - op_region length
+ * region_node - op_region namespace node
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Add the Operation Region address range to the global list.
+ * The only supported Space IDs are Memory and I/O. Called when
+ * the op_region address/length operands are fully evaluated.
+ *
+ * MUTEX: Locks the namespace
+ *
+ * NOTE: Because this interface is only called when an op_region argument
+ * list is evaluated, there cannot be any duplicate region_nodes.
+ * Duplicate Address/Length values are allowed, however, so that multiple
+ * address conflicts can be detected.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ut_add_address_range(acpi_adr_space_type space_id,
+ acpi_physical_address address,
+ u32 length, struct acpi_namespace_node *region_node)
+{
+ struct acpi_address_range *range_info;
+
+ ACPI_FUNCTION_TRACE(ut_add_address_range);
+
+ if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
+ (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Allocate/init a new info block, add it to the appropriate list */
+
+ range_info = ACPI_ALLOCATE(sizeof(struct acpi_address_range));
+ if (!range_info) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ range_info->start_address = address;
+ range_info->end_address = (address + length - 1);
+ range_info->region_node = region_node;
+
+ range_info->next = acpi_gbl_address_range_list[space_id];
+ acpi_gbl_address_range_list[space_id] = range_info;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "\nAdded [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n",
+ acpi_ut_get_node_name(range_info->region_node),
+ ACPI_FORMAT_UINT64(address),
+ ACPI_FORMAT_UINT64(range_info->end_address)));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_remove_address_range
+ *
+ * PARAMETERS: space_id - Address space ID
+ * region_node - op_region namespace node
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Remove the Operation Region from the global list. The only
+ * supported Space IDs are Memory and I/O. Called when an
+ * op_region is deleted.
+ *
+ * MUTEX: Assumes the namespace is locked
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_remove_address_range(acpi_adr_space_type space_id,
+ struct acpi_namespace_node *region_node)
+{
+ struct acpi_address_range *range_info;
+ struct acpi_address_range *prev;
+
+ ACPI_FUNCTION_TRACE(ut_remove_address_range);
+
+ if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
+ (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
+ return_VOID;
+ }
+
+ /* Get the appropriate list head and check the list */
+
+ range_info = prev = acpi_gbl_address_range_list[space_id];
+ while (range_info) {
+ if (range_info->region_node == region_node) {
+ if (range_info == prev) { /* Found at list head */
+ acpi_gbl_address_range_list[space_id] =
+ range_info->next;
+ } else {
+ prev->next = range_info->next;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
+ "\nRemoved [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n",
+ acpi_ut_get_node_name(range_info->
+ region_node),
+ ACPI_FORMAT_UINT64(range_info->
+ start_address),
+ ACPI_FORMAT_UINT64(range_info->
+ end_address)));
+
+ ACPI_FREE(range_info);
+ return_VOID;
+ }
+
+ prev = range_info;
+ range_info = range_info->next;
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_check_address_range
+ *
+ * PARAMETERS: space_id - Address space ID
+ * address - Start address
+ * length - Length of address range
+ * warn - TRUE if warning on overlap desired
+ *
+ * RETURN: Count of the number of conflicts detected. Zero is always
+ * returned for Space IDs other than Memory or I/O.
+ *
+ * DESCRIPTION: Check if the input address range overlaps any of the
+ * ASL operation region address ranges. The only supported
+ * Space IDs are Memory and I/O.
+ *
+ * MUTEX: Assumes the namespace is locked.
+ *
+ ******************************************************************************/
+
+u32
+acpi_ut_check_address_range(acpi_adr_space_type space_id,
+ acpi_physical_address address, u32 length, u8 warn)
+{
+ struct acpi_address_range *range_info;
+ acpi_physical_address end_address;
+ char *pathname;
+ u32 overlap_count = 0;
+
+ ACPI_FUNCTION_TRACE(ut_check_address_range);
+
+ if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
+ (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
+ return_UINT32(0);
+ }
+
+ range_info = acpi_gbl_address_range_list[space_id];
+ end_address = address + length - 1;
+
+ /* Check entire list for all possible conflicts */
+
+ while (range_info) {
+ /*
+ * Check if the requested address/length overlaps this
+ * address range. There are four cases to consider:
+ *
+ * 1) Input address/length is contained completely in the
+ * address range
+ * 2) Input address/length overlaps range at the range start
+ * 3) Input address/length overlaps range at the range end
+ * 4) Input address/length completely encompasses the range
+ */
+ if ((address <= range_info->end_address) &&
+ (end_address >= range_info->start_address)) {
+
+ /* Found an address range overlap */
+
+ overlap_count++;
+ if (warn) { /* Optional warning message */
+ pathname =
+ acpi_ns_get_normalized_pathname(range_info->
+ region_node,
+ TRUE);
+
+ ACPI_WARNING((AE_INFO,
+ "%s range 0x%8.8X%8.8X-0x%8.8X%8.8X conflicts with OpRegion 0x%8.8X%8.8X-0x%8.8X%8.8X (%s)",
+ acpi_ut_get_region_name(space_id),
+ ACPI_FORMAT_UINT64(address),
+ ACPI_FORMAT_UINT64(end_address),
+ ACPI_FORMAT_UINT64(range_info->
+ start_address),
+ ACPI_FORMAT_UINT64(range_info->
+ end_address),
+ pathname));
+ ACPI_FREE(pathname);
+ }
+ }
+
+ range_info = range_info->next;
+ }
+
+ return_UINT32(overlap_count);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_delete_address_lists
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete all global address range lists (called during
+ * subsystem shutdown).
+ *
+ ******************************************************************************/
+
+void acpi_ut_delete_address_lists(void)
+{
+ struct acpi_address_range *next;
+ struct acpi_address_range *range_info;
+ int i;
+
+ /* Delete all elements in all address range lists */
+
+ for (i = 0; i < ACPI_ADDRESS_RANGE_MAX; i++) {
+ next = acpi_gbl_address_range_list[i];
+
+ while (next) {
+ range_info = next;
+ next = range_info->next;
+ ACPI_FREE(range_info);
+ }
+
+ acpi_gbl_address_range_list[i] = NULL;
+ }
+}
diff --git a/drivers/acpi/acpica/utalloc.c b/drivers/acpi/acpica/utalloc.c
new file mode 100644
index 000000000..2bab6017d
--- /dev/null
+++ b/drivers/acpi/acpica/utalloc.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utalloc - local memory allocation routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdebug.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utalloc")
+
+#if !defined (USE_NATIVE_ALLOCATE_ZEROED)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_allocate_zeroed
+ *
+ * PARAMETERS: size - Size of the allocation
+ *
+ * RETURN: Address of the allocated memory on success, NULL on failure.
+ *
+ * DESCRIPTION: Subsystem equivalent of calloc. Allocate and zero memory.
+ * This is the default implementation. Can be overridden via the
+ * USE_NATIVE_ALLOCATE_ZEROED flag.
+ *
+ ******************************************************************************/
+void *acpi_os_allocate_zeroed(acpi_size size)
+{
+ void *allocation;
+
+ ACPI_FUNCTION_ENTRY();
+
+ allocation = acpi_os_allocate(size);
+ if (allocation) {
+
+ /* Clear the memory block */
+
+ memset(allocation, 0, size);
+ }
+
+ return (allocation);
+}
+
+#endif /* !USE_NATIVE_ALLOCATE_ZEROED */
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_caches
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create all local caches
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_create_caches(void)
+{
+ acpi_status status;
+
+ /* Object Caches, for frequently used objects */
+
+ status =
+ acpi_os_create_cache("Acpi-Namespace",
+ sizeof(struct acpi_namespace_node),
+ ACPI_MAX_NAMESPACE_CACHE_DEPTH,
+ &acpi_gbl_namespace_cache);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status =
+ acpi_os_create_cache("Acpi-State", sizeof(union acpi_generic_state),
+ ACPI_MAX_STATE_CACHE_DEPTH,
+ &acpi_gbl_state_cache);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status =
+ acpi_os_create_cache("Acpi-Parse",
+ sizeof(struct acpi_parse_obj_common),
+ ACPI_MAX_PARSE_CACHE_DEPTH,
+ &acpi_gbl_ps_node_cache);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status =
+ acpi_os_create_cache("Acpi-ParseExt",
+ sizeof(struct acpi_parse_obj_named),
+ ACPI_MAX_EXTPARSE_CACHE_DEPTH,
+ &acpi_gbl_ps_node_ext_cache);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status =
+ acpi_os_create_cache("Acpi-Operand",
+ sizeof(union acpi_operand_object),
+ ACPI_MAX_OBJECT_CACHE_DEPTH,
+ &acpi_gbl_operand_cache);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+#ifdef ACPI_ASL_COMPILER
+ /*
+ * For use with the ASL-/ASL+ option. This cache keeps track of regular
+ * 0xA9 0x01 comments.
+ */
+ status =
+ acpi_os_create_cache("Acpi-Comment",
+ sizeof(struct acpi_comment_node),
+ ACPI_MAX_COMMENT_CACHE_DEPTH,
+ &acpi_gbl_reg_comment_cache);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /*
+ * This cache keeps track of the starting addresses of where the comments
+ * lie. This helps prevent duplication of comments.
+ */
+ status =
+ acpi_os_create_cache("Acpi-Comment-Addr",
+ sizeof(struct acpi_comment_addr_node),
+ ACPI_MAX_COMMENT_CACHE_DEPTH,
+ &acpi_gbl_comment_addr_cache);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /*
+ * This cache will be used for nodes that represent files.
+ */
+ status =
+ acpi_os_create_cache("Acpi-File", sizeof(struct acpi_file_node),
+ ACPI_MAX_COMMENT_CACHE_DEPTH,
+ &acpi_gbl_file_cache);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+#endif
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+
+ /* Memory allocation lists */
+
+ status = acpi_ut_create_list("Acpi-Global", 0, &acpi_gbl_global_list);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status =
+ acpi_ut_create_list("Acpi-Namespace",
+ sizeof(struct acpi_namespace_node),
+ &acpi_gbl_ns_node_list);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+#endif
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_delete_caches
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Purge and delete all local caches
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_delete_caches(void)
+{
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ char buffer[7];
+
+ if (acpi_gbl_display_final_mem_stats) {
+ strcpy(buffer, "MEMORY");
+ (void)acpi_db_display_statistics(buffer);
+ }
+#endif
+
+ (void)acpi_os_delete_cache(acpi_gbl_namespace_cache);
+ acpi_gbl_namespace_cache = NULL;
+
+ (void)acpi_os_delete_cache(acpi_gbl_state_cache);
+ acpi_gbl_state_cache = NULL;
+
+ (void)acpi_os_delete_cache(acpi_gbl_operand_cache);
+ acpi_gbl_operand_cache = NULL;
+
+ (void)acpi_os_delete_cache(acpi_gbl_ps_node_cache);
+ acpi_gbl_ps_node_cache = NULL;
+
+ (void)acpi_os_delete_cache(acpi_gbl_ps_node_ext_cache);
+ acpi_gbl_ps_node_ext_cache = NULL;
+
+#ifdef ACPI_ASL_COMPILER
+ (void)acpi_os_delete_cache(acpi_gbl_reg_comment_cache);
+ acpi_gbl_reg_comment_cache = NULL;
+
+ (void)acpi_os_delete_cache(acpi_gbl_comment_addr_cache);
+ acpi_gbl_comment_addr_cache = NULL;
+
+ (void)acpi_os_delete_cache(acpi_gbl_file_cache);
+ acpi_gbl_file_cache = NULL;
+#endif
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+
+ /* Debug only - display leftover memory allocation, if any */
+
+ acpi_ut_dump_allocations(ACPI_UINT32_MAX, NULL);
+
+ /* Free memory lists */
+
+ acpi_os_free(acpi_gbl_global_list);
+ acpi_gbl_global_list = NULL;
+
+ acpi_os_free(acpi_gbl_ns_node_list);
+ acpi_gbl_ns_node_list = NULL;
+#endif
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_validate_buffer
+ *
+ * PARAMETERS: buffer - Buffer descriptor to be validated
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform parameter validation checks on an struct acpi_buffer
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_validate_buffer(struct acpi_buffer *buffer)
+{
+
+ /* Obviously, the structure pointer must be valid */
+
+ if (!buffer) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Special semantics for the length */
+
+ if ((buffer->length == ACPI_NO_BUFFER) ||
+ (buffer->length == ACPI_ALLOCATE_BUFFER) ||
+ (buffer->length == ACPI_ALLOCATE_LOCAL_BUFFER)) {
+ return (AE_OK);
+ }
+
+ /* Length is valid, the buffer pointer must be also */
+
+ if (!buffer->pointer) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_initialize_buffer
+ *
+ * PARAMETERS: buffer - Buffer to be validated
+ * required_length - Length needed
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Validate that the buffer is of the required length or
+ * allocate a new buffer. Returned buffer is always zeroed.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_initialize_buffer(struct acpi_buffer *buffer, acpi_size required_length)
+{
+ acpi_size input_buffer_length;
+
+ /* Parameter validation */
+
+ if (!buffer || !required_length) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Buffer->Length is used as both an input and output parameter. Get the
+ * input actual length and set the output required buffer length.
+ */
+ input_buffer_length = buffer->length;
+ buffer->length = required_length;
+
+ /*
+ * The input buffer length contains the actual buffer length, or the type
+ * of buffer to be allocated by this routine.
+ */
+ switch (input_buffer_length) {
+ case ACPI_NO_BUFFER:
+
+ /* Return the exception (and the required buffer length) */
+
+ return (AE_BUFFER_OVERFLOW);
+
+ case ACPI_ALLOCATE_BUFFER:
+ /*
+ * Allocate a new buffer. We directectly call acpi_os_allocate here to
+ * purposefully bypass the (optionally enabled) internal allocation
+ * tracking mechanism since we only want to track internal
+ * allocations. Note: The caller should use acpi_os_free to free this
+ * buffer created via ACPI_ALLOCATE_BUFFER.
+ */
+ buffer->pointer = acpi_os_allocate(required_length);
+ break;
+
+ case ACPI_ALLOCATE_LOCAL_BUFFER:
+
+ /* Allocate a new buffer with local interface to allow tracking */
+
+ buffer->pointer = ACPI_ALLOCATE(required_length);
+ break;
+
+ default:
+
+ /* Existing buffer: Validate the size of the buffer */
+
+ if (input_buffer_length < required_length) {
+ return (AE_BUFFER_OVERFLOW);
+ }
+ break;
+ }
+
+ /* Validate allocation from above or input buffer pointer */
+
+ if (!buffer->pointer) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Have a valid buffer, clear it */
+
+ memset(buffer->pointer, 0, required_length);
+ return (AE_OK);
+}
diff --git a/drivers/acpi/acpica/utascii.c b/drivers/acpi/acpica/utascii.c
new file mode 100644
index 000000000..72fb7e9ec
--- /dev/null
+++ b/drivers/acpi/acpica/utascii.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utascii - Utility ascii functions
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_valid_nameseg
+ *
+ * PARAMETERS: name - The name or table signature to be examined.
+ * Four characters, does not have to be a
+ * NULL terminated string.
+ *
+ * RETURN: TRUE if signature is has 4 valid ACPI characters
+ *
+ * DESCRIPTION: Validate an ACPI table signature.
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_valid_nameseg(char *name)
+{
+ u32 i;
+
+ /* Validate each character in the signature */
+
+ for (i = 0; i < ACPI_NAMESEG_SIZE; i++) {
+ if (!acpi_ut_valid_name_char(name[i], i)) {
+ return (FALSE);
+ }
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_valid_name_char
+ *
+ * PARAMETERS: char - The character to be examined
+ * position - Byte position (0-3)
+ *
+ * RETURN: TRUE if the character is valid, FALSE otherwise
+ *
+ * DESCRIPTION: Check for a valid ACPI character. Must be one of:
+ * 1) Upper case alpha
+ * 2) numeric
+ * 3) underscore
+ *
+ * We allow a '!' as the last character because of the ASF! table
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_valid_name_char(char character, u32 position)
+{
+
+ if (!((character >= 'A' && character <= 'Z') ||
+ (character >= '0' && character <= '9') || (character == '_'))) {
+
+ /* Allow a '!' in the last position */
+
+ if (character == '!' && position == 3) {
+ return (TRUE);
+ }
+
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_check_and_repair_ascii
+ *
+ * PARAMETERS: name - Ascii string
+ * count - Number of characters to check
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Ensure that the requested number of characters are printable
+ * Ascii characters. Sets non-printable and null chars to <space>.
+ *
+ ******************************************************************************/
+
+void acpi_ut_check_and_repair_ascii(u8 *name, char *repaired_name, u32 count)
+{
+ u32 i;
+
+ for (i = 0; i < count; i++) {
+ repaired_name[i] = (char)name[i];
+
+ if (!name[i]) {
+ return;
+ }
+ if (!isprint(name[i])) {
+ repaired_name[i] = ' ';
+ }
+ }
+}
diff --git a/drivers/acpi/acpica/utbuffer.c b/drivers/acpi/acpica/utbuffer.c
new file mode 100644
index 000000000..59c4050b8
--- /dev/null
+++ b/drivers/acpi/acpica/utbuffer.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utbuffer - Buffer dump routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utbuffer")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_dump_buffer
+ *
+ * PARAMETERS: buffer - Buffer to dump
+ * count - Amount to dump, in bytes
+ * display - BYTE, WORD, DWORD, or QWORD display:
+ * DB_BYTE_DISPLAY
+ * DB_WORD_DISPLAY
+ * DB_DWORD_DISPLAY
+ * DB_QWORD_DISPLAY
+ * base_offset - Beginning buffer offset (display only)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Generic dump buffer in both hex and ascii.
+ *
+ ******************************************************************************/
+void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 base_offset)
+{
+ u32 i = 0;
+ u32 j;
+ u32 temp32;
+ u8 buf_char;
+ u32 display_data_only = display & DB_DISPLAY_DATA_ONLY;
+
+ display &= ~DB_DISPLAY_DATA_ONLY;
+ if (!buffer) {
+ acpi_os_printf("Null Buffer Pointer in DumpBuffer!\n");
+ return;
+ }
+
+ if ((count < 4) || (count & 0x01)) {
+ display = DB_BYTE_DISPLAY;
+ }
+
+ /* Nasty little dump buffer routine! */
+
+ while (i < count) {
+
+ /* Print current offset */
+
+ if (!display_data_only) {
+ acpi_os_printf("%8.4X: ", (base_offset + i));
+ }
+
+ /* Print 16 hex chars */
+
+ for (j = 0; j < 16;) {
+ if (i + j >= count) {
+
+ /* Dump fill spaces */
+
+ acpi_os_printf("%*s", ((display * 2) + 1), " ");
+ j += display;
+ continue;
+ }
+
+ switch (display) {
+ case DB_BYTE_DISPLAY:
+ default: /* Default is BYTE display */
+
+ acpi_os_printf("%02X ",
+ buffer[(acpi_size)i + j]);
+ break;
+
+ case DB_WORD_DISPLAY:
+
+ ACPI_MOVE_16_TO_32(&temp32,
+ &buffer[(acpi_size)i + j]);
+ acpi_os_printf("%04X ", temp32);
+ break;
+
+ case DB_DWORD_DISPLAY:
+
+ ACPI_MOVE_32_TO_32(&temp32,
+ &buffer[(acpi_size)i + j]);
+ acpi_os_printf("%08X ", temp32);
+ break;
+
+ case DB_QWORD_DISPLAY:
+
+ ACPI_MOVE_32_TO_32(&temp32,
+ &buffer[(acpi_size)i + j]);
+ acpi_os_printf("%08X", temp32);
+
+ ACPI_MOVE_32_TO_32(&temp32,
+ &buffer[(acpi_size)i + j +
+ 4]);
+ acpi_os_printf("%08X ", temp32);
+ break;
+ }
+
+ j += display;
+ }
+
+ /*
+ * Print the ASCII equivalent characters but watch out for the bad
+ * unprintable ones (printable chars are 0x20 through 0x7E)
+ */
+ if (!display_data_only) {
+ acpi_os_printf(" ");
+ for (j = 0; j < 16; j++) {
+ if (i + j >= count) {
+ acpi_os_printf("\n");
+ return;
+ }
+
+ /*
+ * Add comment characters so rest of line is ignored when
+ * compiled
+ */
+ if (j == 0) {
+ acpi_os_printf("// ");
+ }
+
+ buf_char = buffer[(acpi_size)i + j];
+ if (isprint(buf_char)) {
+ acpi_os_printf("%c", buf_char);
+ } else {
+ acpi_os_printf(".");
+ }
+ }
+
+ /* Done with that line. */
+
+ acpi_os_printf("\n");
+ }
+ i += 16;
+ }
+
+ return;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_debug_dump_buffer
+ *
+ * PARAMETERS: buffer - Buffer to dump
+ * count - Amount to dump, in bytes
+ * display - BYTE, WORD, DWORD, or QWORD display:
+ * DB_BYTE_DISPLAY
+ * DB_WORD_DISPLAY
+ * DB_DWORD_DISPLAY
+ * DB_QWORD_DISPLAY
+ * component_ID - Caller's component ID
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Generic dump buffer in both hex and ascii.
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_debug_dump_buffer(u8 *buffer, u32 count, u32 display, u32 component_id)
+{
+
+ /* Only dump the buffer if tracing is enabled */
+
+ if (!((ACPI_LV_TABLES & acpi_dbg_level) &&
+ (component_id & acpi_dbg_layer))) {
+ return;
+ }
+
+ acpi_ut_dump_buffer(buffer, count, display, 0);
+}
+
+#ifdef ACPI_APPLICATION
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_dump_buffer_to_file
+ *
+ * PARAMETERS: file - File descriptor
+ * buffer - Buffer to dump
+ * count - Amount to dump, in bytes
+ * display - BYTE, WORD, DWORD, or QWORD display:
+ * DB_BYTE_DISPLAY
+ * DB_WORD_DISPLAY
+ * DB_DWORD_DISPLAY
+ * DB_QWORD_DISPLAY
+ * base_offset - Beginning buffer offset (display only)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Generic dump buffer in both hex and ascii to a file.
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_dump_buffer_to_file(ACPI_FILE file,
+ u8 *buffer, u32 count, u32 display, u32 base_offset)
+{
+ u32 i = 0;
+ u32 j;
+ u32 temp32;
+ u8 buf_char;
+
+ if (!buffer) {
+ fprintf(file, "Null Buffer Pointer in DumpBuffer!\n");
+ return;
+ }
+
+ if ((count < 4) || (count & 0x01)) {
+ display = DB_BYTE_DISPLAY;
+ }
+
+ /* Nasty little dump buffer routine! */
+
+ while (i < count) {
+
+ /* Print current offset */
+
+ fprintf(file, "%8.4X: ", (base_offset + i));
+
+ /* Print 16 hex chars */
+
+ for (j = 0; j < 16;) {
+ if (i + j >= count) {
+
+ /* Dump fill spaces */
+
+ fprintf(file, "%*s", ((display * 2) + 1), " ");
+ j += display;
+ continue;
+ }
+
+ switch (display) {
+ case DB_BYTE_DISPLAY:
+ default: /* Default is BYTE display */
+
+ fprintf(file, "%02X ",
+ buffer[(acpi_size)i + j]);
+ break;
+
+ case DB_WORD_DISPLAY:
+
+ ACPI_MOVE_16_TO_32(&temp32,
+ &buffer[(acpi_size)i + j]);
+ fprintf(file, "%04X ", temp32);
+ break;
+
+ case DB_DWORD_DISPLAY:
+
+ ACPI_MOVE_32_TO_32(&temp32,
+ &buffer[(acpi_size)i + j]);
+ fprintf(file, "%08X ", temp32);
+ break;
+
+ case DB_QWORD_DISPLAY:
+
+ ACPI_MOVE_32_TO_32(&temp32,
+ &buffer[(acpi_size)i + j]);
+ fprintf(file, "%08X", temp32);
+
+ ACPI_MOVE_32_TO_32(&temp32,
+ &buffer[(acpi_size)i + j +
+ 4]);
+ fprintf(file, "%08X ", temp32);
+ break;
+ }
+
+ j += display;
+ }
+
+ /*
+ * Print the ASCII equivalent characters but watch out for the bad
+ * unprintable ones (printable chars are 0x20 through 0x7E)
+ */
+ fprintf(file, " ");
+ for (j = 0; j < 16; j++) {
+ if (i + j >= count) {
+ fprintf(file, "\n");
+ return;
+ }
+
+ buf_char = buffer[(acpi_size)i + j];
+ if (isprint(buf_char)) {
+ fprintf(file, "%c", buf_char);
+ } else {
+ fprintf(file, ".");
+ }
+ }
+
+ /* Done with that line. */
+
+ fprintf(file, "\n");
+ i += 16;
+ }
+
+ return;
+}
+#endif
diff --git a/drivers/acpi/acpica/utcache.c b/drivers/acpi/acpica/utcache.c
new file mode 100644
index 000000000..5425968dd
--- /dev/null
+++ b/drivers/acpi/acpica/utcache.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utcache - local cache allocation routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utcache")
+
+#ifdef ACPI_USE_LOCAL_CACHE
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_create_cache
+ *
+ * PARAMETERS: cache_name - Ascii name for the cache
+ * object_size - Size of each cached object
+ * max_depth - Maximum depth of the cache (in objects)
+ * return_cache - Where the new cache object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a cache object
+ *
+ ******************************************************************************/
+acpi_status
+acpi_os_create_cache(char *cache_name,
+ u16 object_size,
+ u16 max_depth, struct acpi_memory_list **return_cache)
+{
+ struct acpi_memory_list *cache;
+
+ ACPI_FUNCTION_ENTRY();
+
+ if (!cache_name || !return_cache || !object_size) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Create the cache object */
+
+ cache = acpi_os_allocate(sizeof(struct acpi_memory_list));
+ if (!cache) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Populate the cache object and return it */
+
+ memset(cache, 0, sizeof(struct acpi_memory_list));
+ cache->list_name = cache_name;
+ cache->object_size = object_size;
+ cache->max_depth = max_depth;
+
+ *return_cache = cache;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_purge_cache
+ *
+ * PARAMETERS: cache - Handle to cache object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Free all objects within the requested cache.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_os_purge_cache(struct acpi_memory_list *cache)
+{
+ void *next;
+ acpi_status status;
+
+ ACPI_FUNCTION_ENTRY();
+
+ if (!cache) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Walk the list of objects in this cache */
+
+ while (cache->list_head) {
+
+ /* Delete and unlink one cached state object */
+
+ next = ACPI_GET_DESCRIPTOR_PTR(cache->list_head);
+ ACPI_FREE(cache->list_head);
+
+ cache->list_head = next;
+ cache->current_depth--;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_CACHES);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_delete_cache
+ *
+ * PARAMETERS: cache - Handle to cache object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Free all objects within the requested cache and delete the
+ * cache object.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_os_delete_cache(struct acpi_memory_list *cache)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Purge all objects in the cache */
+
+ status = acpi_os_purge_cache(cache);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Now we can delete the cache object */
+
+ acpi_os_free(cache);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_release_object
+ *
+ * PARAMETERS: cache - Handle to cache object
+ * object - The object to be released
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Release an object to the specified cache. If cache is full,
+ * the object is deleted.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_os_release_object(struct acpi_memory_list *cache, void *object)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_ENTRY();
+
+ if (!cache || !object) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* If cache is full, just free this object */
+
+ if (cache->current_depth >= cache->max_depth) {
+ ACPI_FREE(object);
+ ACPI_MEM_TRACKING(cache->total_freed++);
+ }
+
+ /* Otherwise put this object back into the cache */
+
+ else {
+ status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Mark the object as cached */
+
+ memset(object, 0xCA, cache->object_size);
+ ACPI_SET_DESCRIPTOR_TYPE(object, ACPI_DESC_TYPE_CACHED);
+
+ /* Put the object at the head of the cache list */
+
+ ACPI_SET_DESCRIPTOR_PTR(object, cache->list_head);
+ cache->list_head = object;
+ cache->current_depth++;
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_CACHES);
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_acquire_object
+ *
+ * PARAMETERS: cache - Handle to cache object
+ *
+ * RETURN: the acquired object. NULL on error
+ *
+ * DESCRIPTION: Get an object from the specified cache. If cache is empty,
+ * the object is allocated.
+ *
+ ******************************************************************************/
+
+void *acpi_os_acquire_object(struct acpi_memory_list *cache)
+{
+ acpi_status status;
+ void *object;
+
+ ACPI_FUNCTION_TRACE(os_acquire_object);
+
+ if (!cache) {
+ return_PTR(NULL);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES);
+ if (ACPI_FAILURE(status)) {
+ return_PTR(NULL);
+ }
+
+ ACPI_MEM_TRACKING(cache->requests++);
+
+ /* Check the cache first */
+
+ if (cache->list_head) {
+
+ /* There is an object available, use it */
+
+ object = cache->list_head;
+ cache->list_head = ACPI_GET_DESCRIPTOR_PTR(object);
+
+ cache->current_depth--;
+
+ ACPI_MEM_TRACKING(cache->hits++);
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC,
+ "%s: Object %p from %s cache\n",
+ ACPI_GET_FUNCTION_NAME, object,
+ cache->list_name));
+
+ status = acpi_ut_release_mutex(ACPI_MTX_CACHES);
+ if (ACPI_FAILURE(status)) {
+ return_PTR(NULL);
+ }
+
+ /* Clear (zero) the previously used Object */
+
+ memset(object, 0, cache->object_size);
+ } else {
+ /* The cache is empty, create a new object */
+
+ ACPI_MEM_TRACKING(cache->total_allocated++);
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ if ((cache->total_allocated - cache->total_freed) >
+ cache->max_occupied) {
+ cache->max_occupied =
+ cache->total_allocated - cache->total_freed;
+ }
+#endif
+
+ /* Avoid deadlock with ACPI_ALLOCATE_ZEROED */
+
+ status = acpi_ut_release_mutex(ACPI_MTX_CACHES);
+ if (ACPI_FAILURE(status)) {
+ return_PTR(NULL);
+ }
+
+ object = ACPI_ALLOCATE_ZEROED(cache->object_size);
+ if (!object) {
+ return_PTR(NULL);
+ }
+ }
+
+ return_PTR(object);
+}
+#endif /* ACPI_USE_LOCAL_CACHE */
diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c
new file mode 100644
index 000000000..63c17f420
--- /dev/null
+++ b/drivers/acpi/acpica/utcopy.c
@@ -0,0 +1,969 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utcopy - Internal to external object translation utilities
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utcopy")
+
+/* Local prototypes */
+static acpi_status
+acpi_ut_copy_isimple_to_esimple(union acpi_operand_object *internal_object,
+ union acpi_object *external_object,
+ u8 *data_space, acpi_size *buffer_space_used);
+
+static acpi_status
+acpi_ut_copy_ielement_to_ielement(u8 object_type,
+ union acpi_operand_object *source_object,
+ union acpi_generic_state *state,
+ void *context);
+
+static acpi_status
+acpi_ut_copy_ipackage_to_epackage(union acpi_operand_object *internal_object,
+ u8 *buffer, acpi_size *space_used);
+
+static acpi_status
+acpi_ut_copy_esimple_to_isimple(union acpi_object *user_obj,
+ union acpi_operand_object **return_obj);
+
+static acpi_status
+acpi_ut_copy_epackage_to_ipackage(union acpi_object *external_object,
+ union acpi_operand_object **internal_object);
+
+static acpi_status
+acpi_ut_copy_simple_object(union acpi_operand_object *source_desc,
+ union acpi_operand_object *dest_desc);
+
+static acpi_status
+acpi_ut_copy_ielement_to_eelement(u8 object_type,
+ union acpi_operand_object *source_object,
+ union acpi_generic_state *state,
+ void *context);
+
+static acpi_status
+acpi_ut_copy_ipackage_to_ipackage(union acpi_operand_object *source_obj,
+ union acpi_operand_object *dest_obj,
+ struct acpi_walk_state *walk_state);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_isimple_to_esimple
+ *
+ * PARAMETERS: internal_object - Source object to be copied
+ * external_object - Where to return the copied object
+ * data_space - Where object data is returned (such as
+ * buffer and string data)
+ * buffer_space_used - Length of data_space that was used
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to copy a simple internal object to
+ * an external object.
+ *
+ * The data_space buffer is assumed to have sufficient space for
+ * the object.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_copy_isimple_to_esimple(union acpi_operand_object *internal_object,
+ union acpi_object *external_object,
+ u8 *data_space, acpi_size *buffer_space_used)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ut_copy_isimple_to_esimple);
+
+ *buffer_space_used = 0;
+
+ /*
+ * Check for NULL object case (could be an uninitialized
+ * package element)
+ */
+ if (!internal_object) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Always clear the external object */
+
+ memset(external_object, 0, sizeof(union acpi_object));
+
+ /*
+ * In general, the external object will be the same type as
+ * the internal object
+ */
+ external_object->type = internal_object->common.type;
+
+ /* However, only a limited number of external types are supported */
+
+ switch (internal_object->common.type) {
+ case ACPI_TYPE_STRING:
+
+ external_object->string.pointer = (char *)data_space;
+ external_object->string.length = internal_object->string.length;
+ *buffer_space_used = ACPI_ROUND_UP_TO_NATIVE_WORD((acpi_size)
+ internal_object->
+ string.
+ length + 1);
+
+ memcpy((void *)data_space,
+ (void *)internal_object->string.pointer,
+ (acpi_size)internal_object->string.length + 1);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ external_object->buffer.pointer = data_space;
+ external_object->buffer.length = internal_object->buffer.length;
+ *buffer_space_used =
+ ACPI_ROUND_UP_TO_NATIVE_WORD(internal_object->string.
+ length);
+
+ memcpy((void *)data_space,
+ (void *)internal_object->buffer.pointer,
+ internal_object->buffer.length);
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ external_object->integer.value = internal_object->integer.value;
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ /* This is an object reference. */
+
+ switch (internal_object->reference.class) {
+ case ACPI_REFCLASS_NAME:
+ /*
+ * For namepath, return the object handle ("reference")
+ * We are referring to the namespace node
+ */
+ external_object->reference.handle =
+ internal_object->reference.node;
+ external_object->reference.actual_type =
+ acpi_ns_get_type(internal_object->reference.node);
+ break;
+
+ default:
+
+ /* All other reference types are unsupported */
+
+ return_ACPI_STATUS(AE_TYPE);
+ }
+ break;
+
+ case ACPI_TYPE_PROCESSOR:
+
+ external_object->processor.proc_id =
+ internal_object->processor.proc_id;
+ external_object->processor.pblk_address =
+ internal_object->processor.address;
+ external_object->processor.pblk_length =
+ internal_object->processor.length;
+ break;
+
+ case ACPI_TYPE_POWER:
+
+ external_object->power_resource.system_level =
+ internal_object->power_resource.system_level;
+
+ external_object->power_resource.resource_order =
+ internal_object->power_resource.resource_order;
+ break;
+
+ default:
+ /*
+ * There is no corresponding external object type
+ */
+ ACPI_ERROR((AE_INFO,
+ "Unsupported object type, cannot convert to external object: %s",
+ acpi_ut_get_type_name(internal_object->common.
+ type)));
+
+ return_ACPI_STATUS(AE_SUPPORT);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_ielement_to_eelement
+ *
+ * PARAMETERS: acpi_pkg_callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Copy one package element to another package element
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_copy_ielement_to_eelement(u8 object_type,
+ union acpi_operand_object *source_object,
+ union acpi_generic_state *state,
+ void *context)
+{
+ acpi_status status = AE_OK;
+ struct acpi_pkg_info *info = (struct acpi_pkg_info *)context;
+ acpi_size object_space;
+ u32 this_index;
+ union acpi_object *target_object;
+
+ ACPI_FUNCTION_ENTRY();
+
+ this_index = state->pkg.index;
+ target_object = (union acpi_object *)&((union acpi_object *)
+ (state->pkg.dest_object))->
+ package.elements[this_index];
+
+ switch (object_type) {
+ case ACPI_COPY_TYPE_SIMPLE:
+ /*
+ * This is a simple or null object
+ */
+ status = acpi_ut_copy_isimple_to_esimple(source_object,
+ target_object,
+ info->free_space,
+ &object_space);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
+
+ case ACPI_COPY_TYPE_PACKAGE:
+ /*
+ * Build the package object
+ */
+ target_object->type = ACPI_TYPE_PACKAGE;
+ target_object->package.count = source_object->package.count;
+ target_object->package.elements =
+ ACPI_CAST_PTR(union acpi_object, info->free_space);
+
+ /*
+ * Pass the new package object back to the package walk routine
+ */
+ state->pkg.this_target_obj = target_object;
+
+ /*
+ * Save space for the array of objects (Package elements)
+ * update the buffer length counter
+ */
+ object_space = ACPI_ROUND_UP_TO_NATIVE_WORD((acpi_size)
+ target_object->
+ package.count *
+ sizeof(union
+ acpi_object));
+ break;
+
+ default:
+
+ return (AE_BAD_PARAMETER);
+ }
+
+ info->free_space += object_space;
+ info->length += object_space;
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_ipackage_to_epackage
+ *
+ * PARAMETERS: internal_object - Pointer to the object we are returning
+ * buffer - Where the object is returned
+ * space_used - Where the object length is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to place a package object in a user
+ * buffer. A package object by definition contains other objects.
+ *
+ * The buffer is assumed to have sufficient space for the object.
+ * The caller must have verified the buffer length needed using
+ * the acpi_ut_get_object_size function before calling this function.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_copy_ipackage_to_epackage(union acpi_operand_object *internal_object,
+ u8 *buffer, acpi_size *space_used)
+{
+ union acpi_object *external_object;
+ acpi_status status;
+ struct acpi_pkg_info info;
+
+ ACPI_FUNCTION_TRACE(ut_copy_ipackage_to_epackage);
+
+ /*
+ * First package at head of the buffer
+ */
+ external_object = ACPI_CAST_PTR(union acpi_object, buffer);
+
+ /*
+ * Free space begins right after the first package
+ */
+ info.length = ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object));
+ info.free_space = buffer +
+ ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object));
+ info.object_space = 0;
+ info.num_packages = 1;
+
+ external_object->type = internal_object->common.type;
+ external_object->package.count = internal_object->package.count;
+ external_object->package.elements =
+ ACPI_CAST_PTR(union acpi_object, info.free_space);
+
+ /*
+ * Leave room for an array of ACPI_OBJECTS in the buffer
+ * and move the free space past it
+ */
+ info.length += (acpi_size)external_object->package.count *
+ ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object));
+ info.free_space += external_object->package.count *
+ ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object));
+
+ status = acpi_ut_walk_package_tree(internal_object, external_object,
+ acpi_ut_copy_ielement_to_eelement,
+ &info);
+
+ *space_used = info.length;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_iobject_to_eobject
+ *
+ * PARAMETERS: internal_object - The internal object to be converted
+ * ret_buffer - Where the object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to build an API object to be returned
+ * to the caller.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_copy_iobject_to_eobject(union acpi_operand_object *internal_object,
+ struct acpi_buffer *ret_buffer)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ut_copy_iobject_to_eobject);
+
+ if (internal_object->common.type == ACPI_TYPE_PACKAGE) {
+ /*
+ * Package object: Copy all subobjects (including
+ * nested packages)
+ */
+ status = acpi_ut_copy_ipackage_to_epackage(internal_object,
+ ret_buffer->pointer,
+ &ret_buffer->length);
+ } else {
+ /*
+ * Build a simple object (no nested objects)
+ */
+ status = acpi_ut_copy_isimple_to_esimple(internal_object,
+ ACPI_CAST_PTR(union
+ acpi_object,
+ ret_buffer->
+ pointer),
+ ACPI_ADD_PTR(u8,
+ ret_buffer->
+ pointer,
+ ACPI_ROUND_UP_TO_NATIVE_WORD
+ (sizeof
+ (union
+ acpi_object))),
+ &ret_buffer->length);
+ /*
+ * build simple does not include the object size in the length
+ * so we add it in here
+ */
+ ret_buffer->length += sizeof(union acpi_object);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_esimple_to_isimple
+ *
+ * PARAMETERS: external_object - The external object to be converted
+ * ret_internal_object - Where the internal object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function copies an external object to an internal one.
+ * NOTE: Pointers can be copied, we don't need to copy data.
+ * (The pointers have to be valid in our address space no matter
+ * what we do with them!)
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_copy_esimple_to_isimple(union acpi_object *external_object,
+ union acpi_operand_object **ret_internal_object)
+{
+ union acpi_operand_object *internal_object;
+
+ ACPI_FUNCTION_TRACE(ut_copy_esimple_to_isimple);
+
+ /*
+ * Simple types supported are: String, Buffer, Integer
+ */
+ switch (external_object->type) {
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ internal_object = acpi_ut_create_internal_object((u8)
+ external_object->
+ type);
+ if (!internal_object) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+ break;
+
+ case ACPI_TYPE_ANY: /* This is the case for a NULL object */
+
+ *ret_internal_object = NULL;
+ return_ACPI_STATUS(AE_OK);
+
+ default:
+
+ /* All other types are not supported */
+
+ ACPI_ERROR((AE_INFO,
+ "Unsupported object type, cannot convert to internal object: %s",
+ acpi_ut_get_type_name(external_object->type)));
+
+ return_ACPI_STATUS(AE_SUPPORT);
+ }
+
+ /* Must COPY string and buffer contents */
+
+ switch (external_object->type) {
+ case ACPI_TYPE_STRING:
+
+ internal_object->string.pointer =
+ ACPI_ALLOCATE_ZEROED((acpi_size)
+ external_object->string.length + 1);
+
+ if (!internal_object->string.pointer) {
+ goto error_exit;
+ }
+
+ memcpy(internal_object->string.pointer,
+ external_object->string.pointer,
+ external_object->string.length);
+
+ internal_object->string.length = external_object->string.length;
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ internal_object->buffer.pointer =
+ ACPI_ALLOCATE_ZEROED(external_object->buffer.length);
+ if (!internal_object->buffer.pointer) {
+ goto error_exit;
+ }
+
+ memcpy(internal_object->buffer.pointer,
+ external_object->buffer.pointer,
+ external_object->buffer.length);
+
+ internal_object->buffer.length = external_object->buffer.length;
+
+ /* Mark buffer data valid */
+
+ internal_object->buffer.flags |= AOPOBJ_DATA_VALID;
+ break;
+
+ case ACPI_TYPE_INTEGER:
+
+ internal_object->integer.value = external_object->integer.value;
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ /* An incoming reference is defined to be a namespace node */
+
+ internal_object->reference.class = ACPI_REFCLASS_REFOF;
+ internal_object->reference.object =
+ external_object->reference.handle;
+ break;
+
+ default:
+
+ /* Other types can't get here */
+
+ break;
+ }
+
+ *ret_internal_object = internal_object;
+ return_ACPI_STATUS(AE_OK);
+
+error_exit:
+ acpi_ut_remove_reference(internal_object);
+ return_ACPI_STATUS(AE_NO_MEMORY);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_epackage_to_ipackage
+ *
+ * PARAMETERS: external_object - The external object to be converted
+ * internal_object - Where the internal object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Copy an external package object to an internal package.
+ * Handles nested packages.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_copy_epackage_to_ipackage(union acpi_object *external_object,
+ union acpi_operand_object **internal_object)
+{
+ acpi_status status = AE_OK;
+ union acpi_operand_object *package_object;
+ union acpi_operand_object **package_elements;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ut_copy_epackage_to_ipackage);
+
+ /* Create the package object */
+
+ package_object =
+ acpi_ut_create_package_object(external_object->package.count);
+ if (!package_object) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ package_elements = package_object->package.elements;
+
+ /*
+ * Recursive implementation. Probably ok, since nested external
+ * packages as parameters should be very rare.
+ */
+ for (i = 0; i < external_object->package.count; i++) {
+ status =
+ acpi_ut_copy_eobject_to_iobject(&external_object->package.
+ elements[i],
+ &package_elements[i]);
+ if (ACPI_FAILURE(status)) {
+
+ /* Truncate package and delete it */
+
+ package_object->package.count = i;
+ package_elements[i] = NULL;
+ acpi_ut_remove_reference(package_object);
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* Mark package data valid */
+
+ package_object->package.flags |= AOPOBJ_DATA_VALID;
+
+ *internal_object = package_object;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_eobject_to_iobject
+ *
+ * PARAMETERS: external_object - The external object to be converted
+ * internal_object - Where the internal object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Converts an external object to an internal object.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_copy_eobject_to_iobject(union acpi_object *external_object,
+ union acpi_operand_object **internal_object)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ut_copy_eobject_to_iobject);
+
+ if (external_object->type == ACPI_TYPE_PACKAGE) {
+ status =
+ acpi_ut_copy_epackage_to_ipackage(external_object,
+ internal_object);
+ } else {
+ /*
+ * Build a simple object (no nested objects)
+ */
+ status = acpi_ut_copy_esimple_to_isimple(external_object,
+ internal_object);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_simple_object
+ *
+ * PARAMETERS: source_desc - The internal object to be copied
+ * dest_desc - New target object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Simple copy of one internal object to another. Reference count
+ * of the destination object is preserved.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_copy_simple_object(union acpi_operand_object *source_desc,
+ union acpi_operand_object *dest_desc)
+{
+ u16 reference_count;
+ union acpi_operand_object *next_object;
+ acpi_status status;
+ acpi_size copy_size;
+
+ /* Save fields from destination that we don't want to overwrite */
+
+ reference_count = dest_desc->common.reference_count;
+ next_object = dest_desc->common.next_object;
+
+ /*
+ * Copy the entire source object over the destination object.
+ * Note: Source can be either an operand object or namespace node.
+ */
+ copy_size = sizeof(union acpi_operand_object);
+ if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_NAMED) {
+ copy_size = sizeof(struct acpi_namespace_node);
+ }
+
+ memcpy(ACPI_CAST_PTR(char, dest_desc),
+ ACPI_CAST_PTR(char, source_desc), copy_size);
+
+ /* Restore the saved fields */
+
+ dest_desc->common.reference_count = reference_count;
+ dest_desc->common.next_object = next_object;
+
+ /* New object is not static, regardless of source */
+
+ dest_desc->common.flags &= ~AOPOBJ_STATIC_POINTER;
+
+ /* Handle the objects with extra data */
+
+ switch (dest_desc->common.type) {
+ case ACPI_TYPE_BUFFER:
+ /*
+ * Allocate and copy the actual buffer if and only if:
+ * 1) There is a valid buffer pointer
+ * 2) The buffer has a length > 0
+ */
+ if ((source_desc->buffer.pointer) &&
+ (source_desc->buffer.length)) {
+ dest_desc->buffer.pointer =
+ ACPI_ALLOCATE(source_desc->buffer.length);
+ if (!dest_desc->buffer.pointer) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Copy the actual buffer data */
+
+ memcpy(dest_desc->buffer.pointer,
+ source_desc->buffer.pointer,
+ source_desc->buffer.length);
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+ /*
+ * Allocate and copy the actual string if and only if:
+ * 1) There is a valid string pointer
+ * (Pointer to a NULL string is allowed)
+ */
+ if (source_desc->string.pointer) {
+ dest_desc->string.pointer =
+ ACPI_ALLOCATE((acpi_size)source_desc->string.
+ length + 1);
+ if (!dest_desc->string.pointer) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Copy the actual string data */
+
+ memcpy(dest_desc->string.pointer,
+ source_desc->string.pointer,
+ (acpi_size)source_desc->string.length + 1);
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ /*
+ * We copied the reference object, so we now must add a reference
+ * to the object pointed to by the reference
+ *
+ * DDBHandle reference (from Load/load_table) is a special reference,
+ * it does not have a Reference.Object, so does not need to
+ * increase the reference count
+ */
+ if (source_desc->reference.class == ACPI_REFCLASS_TABLE) {
+ break;
+ }
+
+ acpi_ut_add_reference(source_desc->reference.object);
+ break;
+
+ case ACPI_TYPE_REGION:
+ /*
+ * We copied the Region Handler, so we now must add a reference
+ */
+ if (dest_desc->region.handler) {
+ acpi_ut_add_reference(dest_desc->region.handler);
+ }
+ break;
+
+ /*
+ * For Mutex and Event objects, we cannot simply copy the underlying
+ * OS object. We must create a new one.
+ */
+ case ACPI_TYPE_MUTEX:
+
+ status = acpi_os_create_mutex(&dest_desc->mutex.os_mutex);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
+
+ case ACPI_TYPE_EVENT:
+
+ status = acpi_os_create_semaphore(ACPI_NO_UNIT_LIMIT, 0,
+ &dest_desc->event.
+ os_semaphore);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
+
+ default:
+
+ /* Nothing to do for other simple objects */
+
+ break;
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_ielement_to_ielement
+ *
+ * PARAMETERS: acpi_pkg_callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Copy one package element to another package element
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_copy_ielement_to_ielement(u8 object_type,
+ union acpi_operand_object *source_object,
+ union acpi_generic_state *state,
+ void *context)
+{
+ acpi_status status = AE_OK;
+ u32 this_index;
+ union acpi_operand_object **this_target_ptr;
+ union acpi_operand_object *target_object;
+
+ ACPI_FUNCTION_ENTRY();
+
+ this_index = state->pkg.index;
+ this_target_ptr = (union acpi_operand_object **)
+ &state->pkg.dest_object->package.elements[this_index];
+
+ switch (object_type) {
+ case ACPI_COPY_TYPE_SIMPLE:
+
+ /* A null source object indicates a (legal) null package element */
+
+ if (source_object) {
+ /*
+ * This is a simple object, just copy it
+ */
+ target_object =
+ acpi_ut_create_internal_object(source_object->
+ common.type);
+ if (!target_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ status =
+ acpi_ut_copy_simple_object(source_object,
+ target_object);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ *this_target_ptr = target_object;
+ } else {
+ /* Pass through a null element */
+
+ *this_target_ptr = NULL;
+ }
+ break;
+
+ case ACPI_COPY_TYPE_PACKAGE:
+ /*
+ * This object is a package - go down another nesting level
+ * Create and build the package object
+ */
+ target_object =
+ acpi_ut_create_package_object(source_object->package.count);
+ if (!target_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ target_object->common.flags = source_object->common.flags;
+
+ /* Pass the new package object back to the package walk routine */
+
+ state->pkg.this_target_obj = target_object;
+
+ /* Store the object pointer in the parent package object */
+
+ *this_target_ptr = target_object;
+ break;
+
+ default:
+
+ return (AE_BAD_PARAMETER);
+ }
+
+ return (status);
+
+error_exit:
+ acpi_ut_remove_reference(target_object);
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_ipackage_to_ipackage
+ *
+ * PARAMETERS: source_obj - Pointer to the source package object
+ * dest_obj - Where the internal object is returned
+ * walk_state - Current Walk state descriptor
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to copy an internal package object
+ * into another internal package object.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_copy_ipackage_to_ipackage(union acpi_operand_object *source_obj,
+ union acpi_operand_object *dest_obj,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ut_copy_ipackage_to_ipackage);
+
+ dest_obj->common.type = source_obj->common.type;
+ dest_obj->common.flags = source_obj->common.flags;
+ dest_obj->package.count = source_obj->package.count;
+
+ /*
+ * Create the object array and walk the source package tree
+ */
+ dest_obj->package.elements = ACPI_ALLOCATE_ZEROED(((acpi_size)
+ source_obj->package.
+ count +
+ 1) * sizeof(void *));
+ if (!dest_obj->package.elements) {
+ ACPI_ERROR((AE_INFO, "Package allocation failure"));
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /*
+ * Copy the package element-by-element by walking the package "tree".
+ * This handles nested packages of arbitrary depth.
+ */
+ status = acpi_ut_walk_package_tree(source_obj, dest_obj,
+ acpi_ut_copy_ielement_to_ielement,
+ walk_state);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_copy_iobject_to_iobject
+ *
+ * PARAMETERS: source_desc - The internal object to be copied
+ * dest_desc - Where the copied object is returned
+ * walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Copy an internal object to a new internal object
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_copy_iobject_to_iobject(union acpi_operand_object *source_desc,
+ union acpi_operand_object **dest_desc,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(ut_copy_iobject_to_iobject);
+
+ /* Create the top level object */
+
+ *dest_desc = acpi_ut_create_internal_object(source_desc->common.type);
+ if (!*dest_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Copy the object and possible subobjects */
+
+ if (source_desc->common.type == ACPI_TYPE_PACKAGE) {
+ status =
+ acpi_ut_copy_ipackage_to_ipackage(source_desc, *dest_desc,
+ walk_state);
+ } else {
+ status = acpi_ut_copy_simple_object(source_desc, *dest_desc);
+ }
+
+ /* Delete the allocated object if copy failed */
+
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(*dest_desc);
+ }
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c
new file mode 100644
index 000000000..64ed546cf
--- /dev/null
+++ b/drivers/acpi/acpica/utdebug.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utdebug - Debug print/trace routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utdebug")
+
+#ifdef ACPI_DEBUG_OUTPUT
+static acpi_thread_id acpi_gbl_previous_thread_id = (acpi_thread_id) 0xFFFFFFFF;
+static const char *acpi_gbl_function_entry_prefix = "----Entry";
+static const char *acpi_gbl_function_exit_prefix = "----Exit-";
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_init_stack_ptr_trace
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Save the current CPU stack pointer at subsystem startup
+ *
+ ******************************************************************************/
+
+void acpi_ut_init_stack_ptr_trace(void)
+{
+ acpi_size current_sp;
+
+ acpi_gbl_entry_stack_pointer = &current_sp;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_track_stack_ptr
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Save the current CPU stack pointer
+ *
+ ******************************************************************************/
+
+void acpi_ut_track_stack_ptr(void)
+{
+ acpi_size current_sp;
+
+ if (&current_sp < acpi_gbl_lowest_stack_pointer) {
+ acpi_gbl_lowest_stack_pointer = &current_sp;
+ }
+
+ if (acpi_gbl_nesting_level > acpi_gbl_deepest_nesting) {
+ acpi_gbl_deepest_nesting = acpi_gbl_nesting_level;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_trim_function_name
+ *
+ * PARAMETERS: function_name - Ascii string containing a procedure name
+ *
+ * RETURN: Updated pointer to the function name
+ *
+ * DESCRIPTION: Remove the "Acpi" prefix from the function name, if present.
+ * This allows compiler macros such as __func__ to be used
+ * with no change to the debug output.
+ *
+ ******************************************************************************/
+
+static const char *acpi_ut_trim_function_name(const char *function_name)
+{
+
+ /* All Function names are longer than 4 chars, check is safe */
+
+ if (*(ACPI_CAST_PTR(u32, function_name)) == ACPI_PREFIX_MIXED) {
+
+ /* This is the case where the original source has not been modified */
+
+ return (function_name + 4);
+ }
+
+ if (*(ACPI_CAST_PTR(u32, function_name)) == ACPI_PREFIX_LOWER) {
+
+ /* This is the case where the source has been 'linuxized' */
+
+ return (function_name + 5);
+ }
+
+ return (function_name);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_debug_print
+ *
+ * PARAMETERS: requested_debug_level - Requested debug print level
+ * line_number - Caller's line number (for error output)
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ * format - Printf format field
+ * ... - Optional printf arguments
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print error message with prefix consisting of the module name,
+ * line number, and component ID.
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE
+acpi_debug_print(u32 requested_debug_level,
+ u32 line_number,
+ const char *function_name,
+ const char *module_name,
+ u32 component_id, const char *format, ...)
+{
+ acpi_thread_id thread_id;
+ va_list args;
+#ifdef ACPI_APPLICATION
+ int fill_count;
+#endif
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(requested_debug_level, component_id)) {
+ return;
+ }
+
+ /*
+ * Thread tracking and context switch notification
+ */
+ thread_id = acpi_os_get_thread_id();
+ if (thread_id != acpi_gbl_previous_thread_id) {
+ if (ACPI_LV_THREADS & acpi_dbg_level) {
+ acpi_os_printf
+ ("\n**** Context Switch from TID %u to TID %u ****\n\n",
+ (u32)acpi_gbl_previous_thread_id, (u32)thread_id);
+ }
+
+ acpi_gbl_previous_thread_id = thread_id;
+ acpi_gbl_nesting_level = 0;
+ }
+
+ /*
+ * Display the module name, current line number, thread ID (if requested),
+ * current procedure nesting level, and the current procedure name
+ */
+ acpi_os_printf("%9s-%04d ", module_name, line_number);
+
+#ifdef ACPI_APPLICATION
+ /*
+ * For acpi_exec/iASL only, emit the thread ID and nesting level.
+ * Note: nesting level is really only useful during a single-thread
+ * execution. Otherwise, multiple threads will keep resetting the
+ * level.
+ */
+ if (ACPI_LV_THREADS & acpi_dbg_level) {
+ acpi_os_printf("[%u] ", (u32)thread_id);
+ }
+
+ fill_count = 48 - acpi_gbl_nesting_level -
+ strlen(acpi_ut_trim_function_name(function_name));
+ if (fill_count < 0) {
+ fill_count = 0;
+ }
+
+ acpi_os_printf("[%02d] %*s",
+ acpi_gbl_nesting_level, acpi_gbl_nesting_level + 1, " ");
+ acpi_os_printf("%s%*s: ",
+ acpi_ut_trim_function_name(function_name), fill_count,
+ " ");
+
+#else
+ acpi_os_printf("%-22.22s: ", acpi_ut_trim_function_name(function_name));
+#endif
+
+ va_start(args, format);
+ acpi_os_vprintf(format, args);
+ va_end(args);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_debug_print)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_debug_print_raw
+ *
+ * PARAMETERS: requested_debug_level - Requested debug print level
+ * line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ * format - Printf format field
+ * ... - Optional printf arguments
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print message with no headers. Has same interface as
+ * debug_print so that the same macros can be used.
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_debug_print_raw(u32 requested_debug_level,
+ u32 line_number,
+ const char *function_name,
+ const char *module_name,
+ u32 component_id, const char *format, ...)
+{
+ va_list args;
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED(requested_debug_level, component_id)) {
+ return;
+ }
+
+ va_start(args, format);
+ acpi_os_vprintf(format, args);
+ va_end(args);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_debug_print_raw)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_trace
+ *
+ * PARAMETERS: line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in debug_level
+ *
+ ******************************************************************************/
+void
+acpi_ut_trace(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id)
+{
+
+ acpi_gbl_nesting_level++;
+ acpi_ut_track_stack_ptr();
+
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name, module_name,
+ component_id, "%s\n",
+ acpi_gbl_function_entry_prefix);
+ }
+}
+
+ACPI_EXPORT_SYMBOL(acpi_ut_trace)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_trace_ptr
+ *
+ * PARAMETERS: line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ * pointer - Pointer to display
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in debug_level
+ *
+ ******************************************************************************/
+void
+acpi_ut_trace_ptr(u32 line_number,
+ const char *function_name,
+ const char *module_name,
+ u32 component_id, const void *pointer)
+{
+
+ acpi_gbl_nesting_level++;
+ acpi_ut_track_stack_ptr();
+
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name, module_name,
+ component_id, "%s %p\n",
+ acpi_gbl_function_entry_prefix, pointer);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_trace_str
+ *
+ * PARAMETERS: line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ * string - Additional string to display
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in debug_level
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_trace_str(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, const char *string)
+{
+
+ acpi_gbl_nesting_level++;
+ acpi_ut_track_stack_ptr();
+
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name, module_name,
+ component_id, "%s %s\n",
+ acpi_gbl_function_entry_prefix, string);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_trace_u32
+ *
+ * PARAMETERS: line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ * integer - Integer to display
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in debug_level
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_trace_u32(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, u32 integer)
+{
+
+ acpi_gbl_nesting_level++;
+ acpi_ut_track_stack_ptr();
+
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name, module_name,
+ component_id, "%s %08X\n",
+ acpi_gbl_function_entry_prefix, integer);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_exit
+ *
+ * PARAMETERS: line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in debug_level
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id)
+{
+
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name, module_name,
+ component_id, "%s\n",
+ acpi_gbl_function_exit_prefix);
+ }
+
+ if (acpi_gbl_nesting_level) {
+ acpi_gbl_nesting_level--;
+ }
+}
+
+ACPI_EXPORT_SYMBOL(acpi_ut_exit)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_status_exit
+ *
+ * PARAMETERS: line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ * status - Exit status code
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in debug_level. Prints exit status also.
+ *
+ ******************************************************************************/
+void
+acpi_ut_status_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name,
+ u32 component_id, acpi_status status)
+{
+
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
+ if (ACPI_SUCCESS(status)) {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name,
+ module_name, component_id, "%s %s\n",
+ acpi_gbl_function_exit_prefix,
+ acpi_format_exception(status));
+ } else {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name,
+ module_name, component_id,
+ "%s ****Exception****: %s\n",
+ acpi_gbl_function_exit_prefix,
+ acpi_format_exception(status));
+ }
+ }
+
+ if (acpi_gbl_nesting_level) {
+ acpi_gbl_nesting_level--;
+ }
+}
+
+ACPI_EXPORT_SYMBOL(acpi_ut_status_exit)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_value_exit
+ *
+ * PARAMETERS: line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ * value - Value to be printed with exit msg
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in debug_level. Prints exit value also.
+ *
+ ******************************************************************************/
+void
+acpi_ut_value_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, u64 value)
+{
+
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name, module_name,
+ component_id, "%s %8.8X%8.8X\n",
+ acpi_gbl_function_exit_prefix,
+ ACPI_FORMAT_UINT64(value));
+ }
+
+ if (acpi_gbl_nesting_level) {
+ acpi_gbl_nesting_level--;
+ }
+}
+
+ACPI_EXPORT_SYMBOL(acpi_ut_value_exit)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_ptr_exit
+ *
+ * PARAMETERS: line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ * ptr - Pointer to display
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in debug_level. Prints exit value also.
+ *
+ ******************************************************************************/
+void
+acpi_ut_ptr_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, u8 *ptr)
+{
+
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name, module_name,
+ component_id, "%s %p\n",
+ acpi_gbl_function_exit_prefix, ptr);
+ }
+
+ if (acpi_gbl_nesting_level) {
+ acpi_gbl_nesting_level--;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_str_exit
+ *
+ * PARAMETERS: line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ * string - String to display
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in debug_level. Prints exit value also.
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_str_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, const char *string)
+{
+
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name, module_name,
+ component_id, "%s %s\n",
+ acpi_gbl_function_exit_prefix, string);
+ }
+
+ if (acpi_gbl_nesting_level) {
+ acpi_gbl_nesting_level--;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_trace_point
+ *
+ * PARAMETERS: type - Trace event type
+ * begin - TRUE if before execution
+ * aml - Executed AML address
+ * pathname - Object path
+ * pointer - Pointer to the related object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Interpreter execution trace.
+ *
+ ******************************************************************************/
+
+void
+acpi_trace_point(acpi_trace_event_type type, u8 begin, u8 *aml, char *pathname)
+{
+
+ ACPI_FUNCTION_ENTRY();
+
+ acpi_ex_trace_point(type, begin, aml, pathname);
+
+#ifdef ACPI_USE_SYSTEM_TRACER
+ acpi_os_trace_point(type, begin, aml, pathname);
+#endif
+}
+
+ACPI_EXPORT_SYMBOL(acpi_trace_point)
+
+#endif
diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c
new file mode 100644
index 000000000..3176393a7
--- /dev/null
+++ b/drivers/acpi/acpica/utdecode.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utdecode - Utility decoding routines (value-to-string)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utdecode")
+
+/*
+ * Properties of the ACPI Object Types, both internal and external.
+ * The table is indexed by values of acpi_object_type
+ */
+const u8 acpi_gbl_ns_properties[ACPI_NUM_NS_TYPES] = {
+ ACPI_NS_NORMAL, /* 00 Any */
+ ACPI_NS_NORMAL, /* 01 Number */
+ ACPI_NS_NORMAL, /* 02 String */
+ ACPI_NS_NORMAL, /* 03 Buffer */
+ ACPI_NS_NORMAL, /* 04 Package */
+ ACPI_NS_NORMAL, /* 05 field_unit */
+ ACPI_NS_NEWSCOPE, /* 06 Device */
+ ACPI_NS_NORMAL, /* 07 Event */
+ ACPI_NS_NEWSCOPE, /* 08 Method */
+ ACPI_NS_NORMAL, /* 09 Mutex */
+ ACPI_NS_NORMAL, /* 10 Region */
+ ACPI_NS_NEWSCOPE, /* 11 Power */
+ ACPI_NS_NEWSCOPE, /* 12 Processor */
+ ACPI_NS_NEWSCOPE, /* 13 Thermal */
+ ACPI_NS_NORMAL, /* 14 buffer_field */
+ ACPI_NS_NORMAL, /* 15 ddb_handle */
+ ACPI_NS_NORMAL, /* 16 Debug Object */
+ ACPI_NS_NORMAL, /* 17 def_field */
+ ACPI_NS_NORMAL, /* 18 bank_field */
+ ACPI_NS_NORMAL, /* 19 index_field */
+ ACPI_NS_NORMAL, /* 20 Reference */
+ ACPI_NS_NORMAL, /* 21 Alias */
+ ACPI_NS_NORMAL, /* 22 method_alias */
+ ACPI_NS_NORMAL, /* 23 Notify */
+ ACPI_NS_NORMAL, /* 24 Address Handler */
+ ACPI_NS_NEWSCOPE | ACPI_NS_LOCAL, /* 25 Resource Desc */
+ ACPI_NS_NEWSCOPE | ACPI_NS_LOCAL, /* 26 Resource Field */
+ ACPI_NS_NEWSCOPE, /* 27 Scope */
+ ACPI_NS_NORMAL, /* 28 Extra */
+ ACPI_NS_NORMAL, /* 29 Data */
+ ACPI_NS_NORMAL /* 30 Invalid */
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_region_name
+ *
+ * PARAMETERS: Space ID - ID for the region
+ *
+ * RETURN: Decoded region space_id name
+ *
+ * DESCRIPTION: Translate a Space ID into a name string (Debug only)
+ *
+ ******************************************************************************/
+
+/* Region type decoding */
+
+const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS] = {
+ "SystemMemory", /* 0x00 */
+ "SystemIO", /* 0x01 */
+ "PCI_Config", /* 0x02 */
+ "EmbeddedControl", /* 0x03 */
+ "SMBus", /* 0x04 */
+ "SystemCMOS", /* 0x05 */
+ "PCIBARTarget", /* 0x06 */
+ "IPMI", /* 0x07 */
+ "GeneralPurposeIo", /* 0x08 */
+ "GenericSerialBus", /* 0x09 */
+ "PCC", /* 0x0A */
+ "PlatformRtMechanism" /* 0x0B */
+};
+
+const char *acpi_ut_get_region_name(u8 space_id)
+{
+
+ if (space_id >= ACPI_USER_REGION_BEGIN) {
+ return ("UserDefinedRegion");
+ } else if (space_id == ACPI_ADR_SPACE_DATA_TABLE) {
+ return ("DataTable");
+ } else if (space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
+ return ("FunctionalFixedHW");
+ } else if (space_id >= ACPI_NUM_PREDEFINED_REGIONS) {
+ return ("InvalidSpaceId");
+ }
+
+ return (acpi_gbl_region_types[space_id]);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_event_name
+ *
+ * PARAMETERS: event_id - Fixed event ID
+ *
+ * RETURN: Decoded event ID name
+ *
+ * DESCRIPTION: Translate a Event ID into a name string (Debug only)
+ *
+ ******************************************************************************/
+
+/* Event type decoding */
+
+static const char *acpi_gbl_event_types[ACPI_NUM_FIXED_EVENTS] = {
+ "PM_Timer",
+ "GlobalLock",
+ "PowerButton",
+ "SleepButton",
+ "RealTimeClock",
+};
+
+const char *acpi_ut_get_event_name(u32 event_id)
+{
+
+ if (event_id > ACPI_EVENT_MAX) {
+ return ("InvalidEventID");
+ }
+
+ return (acpi_gbl_event_types[event_id]);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_type_name
+ *
+ * PARAMETERS: type - An ACPI object type
+ *
+ * RETURN: Decoded ACPI object type name
+ *
+ * DESCRIPTION: Translate a Type ID into a name string (Debug only)
+ *
+ ******************************************************************************/
+
+/*
+ * Elements of acpi_gbl_ns_type_names below must match
+ * one-to-one with values of acpi_object_type
+ *
+ * The type ACPI_TYPE_ANY (Untyped) is used as a "don't care" when searching;
+ * when stored in a table it really means that we have thus far seen no
+ * evidence to indicate what type is actually going to be stored for this
+ & entry.
+ */
+static const char acpi_gbl_bad_type[] = "UNDEFINED";
+
+/* Printable names of the ACPI object types */
+
+static const char *acpi_gbl_ns_type_names[] = {
+ /* 00 */ "Untyped",
+ /* 01 */ "Integer",
+ /* 02 */ "String",
+ /* 03 */ "Buffer",
+ /* 04 */ "Package",
+ /* 05 */ "FieldUnit",
+ /* 06 */ "Device",
+ /* 07 */ "Event",
+ /* 08 */ "Method",
+ /* 09 */ "Mutex",
+ /* 10 */ "Region",
+ /* 11 */ "Power",
+ /* 12 */ "Processor",
+ /* 13 */ "Thermal",
+ /* 14 */ "BufferField",
+ /* 15 */ "DdbHandle",
+ /* 16 */ "DebugObject",
+ /* 17 */ "RegionField",
+ /* 18 */ "BankField",
+ /* 19 */ "IndexField",
+ /* 20 */ "Reference",
+ /* 21 */ "Alias",
+ /* 22 */ "MethodAlias",
+ /* 23 */ "Notify",
+ /* 24 */ "AddrHandler",
+ /* 25 */ "ResourceDesc",
+ /* 26 */ "ResourceFld",
+ /* 27 */ "Scope",
+ /* 28 */ "Extra",
+ /* 29 */ "Data",
+ /* 30 */ "Invalid"
+};
+
+const char *acpi_ut_get_type_name(acpi_object_type type)
+{
+
+ if (type > ACPI_TYPE_INVALID) {
+ return (acpi_gbl_bad_type);
+ }
+
+ return (acpi_gbl_ns_type_names[type]);
+}
+
+const char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc)
+{
+ ACPI_FUNCTION_TRACE(ut_get_object_type_name);
+
+ if (!obj_desc) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Null Object Descriptor\n"));
+ return_STR("[NULL Object Descriptor]");
+ }
+
+ /* These descriptor types share a common area */
+
+ if ((ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) &&
+ (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_NAMED)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Invalid object descriptor type: 0x%2.2X [%s] (%p)\n",
+ ACPI_GET_DESCRIPTOR_TYPE(obj_desc),
+ acpi_ut_get_descriptor_name(obj_desc),
+ obj_desc));
+
+ return_STR("Invalid object");
+ }
+
+ return_STR(acpi_ut_get_type_name(obj_desc->common.type));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_node_name
+ *
+ * PARAMETERS: object - A namespace node
+ *
+ * RETURN: ASCII name of the node
+ *
+ * DESCRIPTION: Validate the node and return the node's ACPI name.
+ *
+ ******************************************************************************/
+
+const char *acpi_ut_get_node_name(void *object)
+{
+ struct acpi_namespace_node *node = (struct acpi_namespace_node *)object;
+
+ /* Must return a string of exactly 4 characters == ACPI_NAMESEG_SIZE */
+
+ if (!object) {
+ return ("NULL");
+ }
+
+ /* Check for Root node */
+
+ if ((object == ACPI_ROOT_OBJECT) || (object == acpi_gbl_root_node)) {
+ return ("\"\\\" ");
+ }
+
+ /* Descriptor must be a namespace node */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) {
+ return ("####");
+ }
+
+ /*
+ * Ensure name is valid. The name was validated/repaired when the node
+ * was created, but make sure it has not been corrupted.
+ */
+ acpi_ut_repair_name(node->name.ascii);
+
+ /* Return the name */
+
+ return (node->name.ascii);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_descriptor_name
+ *
+ * PARAMETERS: object - An ACPI object
+ *
+ * RETURN: Decoded name of the descriptor type
+ *
+ * DESCRIPTION: Validate object and return the descriptor type
+ *
+ ******************************************************************************/
+
+/* Printable names of object descriptor types */
+
+static const char *acpi_gbl_desc_type_names[] = {
+ /* 00 */ "Not a Descriptor",
+ /* 01 */ "Cached Object",
+ /* 02 */ "State-Generic",
+ /* 03 */ "State-Update",
+ /* 04 */ "State-Package",
+ /* 05 */ "State-Control",
+ /* 06 */ "State-RootParseScope",
+ /* 07 */ "State-ParseScope",
+ /* 08 */ "State-WalkScope",
+ /* 09 */ "State-Result",
+ /* 10 */ "State-Notify",
+ /* 11 */ "State-Thread",
+ /* 12 */ "Tree Walk State",
+ /* 13 */ "Parse Tree Op",
+ /* 14 */ "Operand Object",
+ /* 15 */ "Namespace Node"
+};
+
+const char *acpi_ut_get_descriptor_name(void *object)
+{
+
+ if (!object) {
+ return ("NULL OBJECT");
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(object) > ACPI_DESC_TYPE_MAX) {
+ return ("Not a Descriptor");
+ }
+
+ return (acpi_gbl_desc_type_names[ACPI_GET_DESCRIPTOR_TYPE(object)]);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_reference_name
+ *
+ * PARAMETERS: object - An ACPI reference object
+ *
+ * RETURN: Decoded name of the type of reference
+ *
+ * DESCRIPTION: Decode a reference object sub-type to a string.
+ *
+ ******************************************************************************/
+
+/* Printable names of reference object sub-types */
+
+static const char *acpi_gbl_ref_class_names[] = {
+ /* 00 */ "Local",
+ /* 01 */ "Argument",
+ /* 02 */ "RefOf",
+ /* 03 */ "Index",
+ /* 04 */ "DdbHandle",
+ /* 05 */ "Named Object",
+ /* 06 */ "Debug"
+};
+
+const char *acpi_ut_get_reference_name(union acpi_operand_object *object)
+{
+
+ if (!object) {
+ return ("NULL Object");
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(object) != ACPI_DESC_TYPE_OPERAND) {
+ return ("Not an Operand object");
+ }
+
+ if (object->common.type != ACPI_TYPE_LOCAL_REFERENCE) {
+ return ("Not a Reference object");
+ }
+
+ if (object->reference.class > ACPI_REFCLASS_MAX) {
+ return ("Unknown Reference class");
+ }
+
+ return (acpi_gbl_ref_class_names[object->reference.class]);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_mutex_name
+ *
+ * PARAMETERS: mutex_id - The predefined ID for this mutex.
+ *
+ * RETURN: Decoded name of the internal mutex
+ *
+ * DESCRIPTION: Translate a mutex ID into a name string (Debug only)
+ *
+ ******************************************************************************/
+
+/* Names for internal mutex objects, used for debug output */
+
+static const char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = {
+ "ACPI_MTX_Interpreter",
+ "ACPI_MTX_Namespace",
+ "ACPI_MTX_Tables",
+ "ACPI_MTX_Events",
+ "ACPI_MTX_Caches",
+ "ACPI_MTX_Memory",
+};
+
+const char *acpi_ut_get_mutex_name(u32 mutex_id)
+{
+
+ if (mutex_id > ACPI_MAX_MUTEX) {
+ return ("Invalid Mutex ID");
+ }
+
+ return (acpi_gbl_mutex_names[mutex_id]);
+}
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+
+/*
+ * Strings and procedures used for debug only
+ */
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_notify_name
+ *
+ * PARAMETERS: notify_value - Value from the Notify() request
+ *
+ * RETURN: Decoded name for the notify value
+ *
+ * DESCRIPTION: Translate a Notify Value to a notify namestring.
+ *
+ ******************************************************************************/
+
+/* Names for Notify() values, used for debug output */
+
+static const char *acpi_gbl_generic_notify[ACPI_GENERIC_NOTIFY_MAX + 1] = {
+ /* 00 */ "Bus Check",
+ /* 01 */ "Device Check",
+ /* 02 */ "Device Wake",
+ /* 03 */ "Eject Request",
+ /* 04 */ "Device Check Light",
+ /* 05 */ "Frequency Mismatch",
+ /* 06 */ "Bus Mode Mismatch",
+ /* 07 */ "Power Fault",
+ /* 08 */ "Capabilities Check",
+ /* 09 */ "Device PLD Check",
+ /* 0A */ "Reserved",
+ /* 0B */ "System Locality Update",
+ /* 0C */ "Reserved (was previously Shutdown Request)",
+ /* Reserved in ACPI 6.0 */
+ /* 0D */ "System Resource Affinity Update",
+ /* 0E */ "Heterogeneous Memory Attributes Update",
+ /* ACPI 6.2 */
+ /* 0F */ "Error Disconnect Recover"
+ /* ACPI 6.3 */
+};
+
+static const char *acpi_gbl_device_notify[5] = {
+ /* 80 */ "Status Change",
+ /* 81 */ "Information Change",
+ /* 82 */ "Device-Specific Change",
+ /* 83 */ "Device-Specific Change",
+ /* 84 */ "Reserved"
+};
+
+static const char *acpi_gbl_processor_notify[5] = {
+ /* 80 */ "Performance Capability Change",
+ /* 81 */ "C-State Change",
+ /* 82 */ "Throttling Capability Change",
+ /* 83 */ "Guaranteed Change",
+ /* 84 */ "Minimum Excursion"
+};
+
+static const char *acpi_gbl_thermal_notify[5] = {
+ /* 80 */ "Thermal Status Change",
+ /* 81 */ "Thermal Trip Point Change",
+ /* 82 */ "Thermal Device List Change",
+ /* 83 */ "Thermal Relationship Change",
+ /* 84 */ "Reserved"
+};
+
+const char *acpi_ut_get_notify_name(u32 notify_value, acpi_object_type type)
+{
+
+ /* 00 - 0F are "common to all object types" (from ACPI Spec) */
+
+ if (notify_value <= ACPI_GENERIC_NOTIFY_MAX) {
+ return (acpi_gbl_generic_notify[notify_value]);
+ }
+
+ /* 10 - 7F are reserved */
+
+ if (notify_value <= ACPI_MAX_SYS_NOTIFY) {
+ return ("Reserved");
+ }
+
+ /* 80 - 84 are per-object-type */
+
+ if (notify_value <= ACPI_SPECIFIC_NOTIFY_MAX) {
+ switch (type) {
+ case ACPI_TYPE_ANY:
+ case ACPI_TYPE_DEVICE:
+ return (acpi_gbl_device_notify[notify_value - 0x80]);
+
+ case ACPI_TYPE_PROCESSOR:
+ return (acpi_gbl_processor_notify[notify_value - 0x80]);
+
+ case ACPI_TYPE_THERMAL:
+ return (acpi_gbl_thermal_notify[notify_value - 0x80]);
+
+ default:
+ return ("Target object type does not support notifies");
+ }
+ }
+
+ /* 84 - BF are device-specific */
+
+ if (notify_value <= ACPI_MAX_DEVICE_SPECIFIC_NOTIFY) {
+ return ("Device-Specific");
+ }
+
+ /* C0 and above are hardware-specific */
+
+ return ("Hardware-Specific");
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_argument_type_name
+ *
+ * PARAMETERS: arg_type - an ARGP_* parser argument type
+ *
+ * RETURN: Decoded ARGP_* type
+ *
+ * DESCRIPTION: Decode an ARGP_* parser type, as defined in the amlcode.h file,
+ * and used in the acopcode.h file. For example, ARGP_TERMARG.
+ * Used for debug only.
+ *
+ ******************************************************************************/
+
+static const char *acpi_gbl_argument_type[20] = {
+ /* 00 */ "Unknown ARGP",
+ /* 01 */ "ByteData",
+ /* 02 */ "ByteList",
+ /* 03 */ "CharList",
+ /* 04 */ "DataObject",
+ /* 05 */ "DataObjectList",
+ /* 06 */ "DWordData",
+ /* 07 */ "FieldList",
+ /* 08 */ "Name",
+ /* 09 */ "NameString",
+ /* 0A */ "ObjectList",
+ /* 0B */ "PackageLength",
+ /* 0C */ "SuperName",
+ /* 0D */ "Target",
+ /* 0E */ "TermArg",
+ /* 0F */ "TermList",
+ /* 10 */ "WordData",
+ /* 11 */ "QWordData",
+ /* 12 */ "SimpleName",
+ /* 13 */ "NameOrRef"
+};
+
+const char *acpi_ut_get_argument_type_name(u32 arg_type)
+{
+
+ if (arg_type > ARGP_MAX) {
+ return ("Unknown ARGP");
+ }
+
+ return (acpi_gbl_argument_type[arg_type]);
+}
+
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_valid_object_type
+ *
+ * PARAMETERS: type - Object type to be validated
+ *
+ * RETURN: TRUE if valid object type, FALSE otherwise
+ *
+ * DESCRIPTION: Validate an object type
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_valid_object_type(acpi_object_type type)
+{
+
+ if (type > ACPI_TYPE_LOCAL_MAX) {
+
+ /* Note: Assumes all TYPEs are contiguous (external/local) */
+
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c
new file mode 100644
index 000000000..8d7736d2d
--- /dev/null
+++ b/drivers/acpi/acpica/utdelete.c
@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utdelete - object deletion and reference count utilities
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utdelete")
+
+/* Local prototypes */
+static void acpi_ut_delete_internal_obj(union acpi_operand_object *object);
+
+static void
+acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_delete_internal_obj
+ *
+ * PARAMETERS: object - Object to be deleted
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Low level object deletion, after reference counts have been
+ * updated (All reference counts, including sub-objects!)
+ *
+ ******************************************************************************/
+
+static void acpi_ut_delete_internal_obj(union acpi_operand_object *object)
+{
+ void *obj_pointer = NULL;
+ union acpi_operand_object *handler_desc;
+ union acpi_operand_object *second_desc;
+ union acpi_operand_object *next_desc;
+ union acpi_operand_object *start_desc;
+ union acpi_operand_object **last_obj_ptr;
+
+ ACPI_FUNCTION_TRACE_PTR(ut_delete_internal_obj, object);
+
+ if (!object) {
+ return_VOID;
+ }
+
+ /*
+ * Must delete or free any pointers within the object that are not
+ * actual ACPI objects (for example, a raw buffer pointer).
+ */
+ switch (object->common.type) {
+ case ACPI_TYPE_STRING:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "**** String %p, ptr %p\n", object,
+ object->string.pointer));
+
+ /* Free the actual string buffer */
+
+ if (!(object->common.flags & AOPOBJ_STATIC_POINTER)) {
+
+ /* But only if it is NOT a pointer into an ACPI table */
+
+ obj_pointer = object->string.pointer;
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "**** Buffer %p, ptr %p\n", object,
+ object->buffer.pointer));
+
+ /* Free the actual buffer */
+
+ if (!(object->common.flags & AOPOBJ_STATIC_POINTER)) {
+
+ /* But only if it is NOT a pointer into an ACPI table */
+
+ obj_pointer = object->buffer.pointer;
+ }
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ " **** Package of count %X\n",
+ object->package.count));
+
+ /*
+ * Elements of the package are not handled here, they are deleted
+ * separately
+ */
+
+ /* Free the (variable length) element pointer array */
+
+ obj_pointer = object->package.elements;
+ break;
+
+ /*
+ * These objects have a possible list of notify handlers.
+ * Device object also may have a GPE block.
+ */
+ case ACPI_TYPE_DEVICE:
+
+ if (object->device.gpe_block) {
+ (void)acpi_ev_delete_gpe_block(object->device.
+ gpe_block);
+ }
+
+ ACPI_FALLTHROUGH;
+
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+
+ /* Walk the address handler list for this object */
+
+ handler_desc = object->common_notify.handler;
+ while (handler_desc) {
+ next_desc = handler_desc->address_space.next;
+ acpi_ut_remove_reference(handler_desc);
+ handler_desc = next_desc;
+ }
+ break;
+
+ case ACPI_TYPE_MUTEX:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "***** Mutex %p, OS Mutex %p\n",
+ object, object->mutex.os_mutex));
+
+ if (object == acpi_gbl_global_lock_mutex) {
+
+ /* Global Lock has extra semaphore */
+
+ (void)
+ acpi_os_delete_semaphore
+ (acpi_gbl_global_lock_semaphore);
+ acpi_gbl_global_lock_semaphore = NULL;
+
+ acpi_os_delete_mutex(object->mutex.os_mutex);
+ acpi_gbl_global_lock_mutex = NULL;
+ } else {
+ acpi_ex_unlink_mutex(object);
+ acpi_os_delete_mutex(object->mutex.os_mutex);
+ }
+ break;
+
+ case ACPI_TYPE_EVENT:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "***** Event %p, OS Semaphore %p\n",
+ object, object->event.os_semaphore));
+
+ (void)acpi_os_delete_semaphore(object->event.os_semaphore);
+ object->event.os_semaphore = NULL;
+ break;
+
+ case ACPI_TYPE_METHOD:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "***** Method %p\n", object));
+
+ /* Delete the method mutex if it exists */
+
+ if (object->method.mutex) {
+ acpi_os_delete_mutex(object->method.mutex->mutex.
+ os_mutex);
+ acpi_ut_delete_object_desc(object->method.mutex);
+ object->method.mutex = NULL;
+ }
+
+ if (object->method.node) {
+ object->method.node = NULL;
+ }
+ break;
+
+ case ACPI_TYPE_REGION:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "***** Region %p\n", object));
+
+ /*
+ * Update address_range list. However, only permanent regions
+ * are installed in this list. (Not created within a method)
+ */
+ if (!(object->region.node->flags & ANOBJ_TEMPORARY)) {
+ acpi_ut_remove_address_range(object->region.space_id,
+ object->region.node);
+ }
+
+ second_desc = acpi_ns_get_secondary_object(object);
+ if (second_desc) {
+ /*
+ * Free the region_context if and only if the handler is one of the
+ * default handlers -- and therefore, we created the context object
+ * locally, it was not created by an external caller.
+ */
+ handler_desc = object->region.handler;
+ if (handler_desc) {
+ next_desc =
+ handler_desc->address_space.region_list;
+ start_desc = next_desc;
+ last_obj_ptr =
+ &handler_desc->address_space.region_list;
+
+ /* Remove the region object from the handler list */
+
+ while (next_desc) {
+ if (next_desc == object) {
+ *last_obj_ptr =
+ next_desc->region.next;
+ break;
+ }
+
+ /* Walk the linked list of handlers */
+
+ last_obj_ptr = &next_desc->region.next;
+ next_desc = next_desc->region.next;
+
+ /* Prevent infinite loop if list is corrupted */
+
+ if (next_desc == start_desc) {
+ ACPI_ERROR((AE_INFO,
+ "Circular region list in address handler object %p",
+ handler_desc));
+ return_VOID;
+ }
+ }
+
+ if (handler_desc->address_space.handler_flags &
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) {
+
+ /* Deactivate region and free region context */
+
+ if (handler_desc->address_space.setup) {
+ (void)handler_desc->
+ address_space.setup(object,
+ ACPI_REGION_DEACTIVATE,
+ handler_desc->
+ address_space.
+ context,
+ &second_desc->
+ extra.
+ region_context);
+ }
+ }
+
+ acpi_ut_remove_reference(handler_desc);
+ }
+
+ /* Now we can free the Extra object */
+
+ acpi_ut_delete_object_desc(second_desc);
+ }
+ if (object->field.internal_pcc_buffer) {
+ ACPI_FREE(object->field.internal_pcc_buffer);
+ }
+
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "***** Buffer Field %p\n", object));
+
+ second_desc = acpi_ns_get_secondary_object(object);
+ if (second_desc) {
+ acpi_ut_delete_object_desc(second_desc);
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "***** Bank Field %p\n", object));
+
+ second_desc = acpi_ns_get_secondary_object(object);
+ if (second_desc) {
+ acpi_ut_delete_object_desc(second_desc);
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_ADDRESS_HANDLER:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "***** Address handler %p\n", object));
+
+ acpi_os_delete_mutex(object->address_space.context_mutex);
+ break;
+
+ default:
+
+ break;
+ }
+
+ /* Free any allocated memory (pointer within the object) found above */
+
+ if (obj_pointer) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "Deleting Object Subptr %p\n", obj_pointer));
+ ACPI_FREE(obj_pointer);
+ }
+
+ /* Now the object can be safely deleted */
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_ALLOCATIONS,
+ "%s: Deleting Object %p [%s]\n",
+ ACPI_GET_FUNCTION_NAME, object,
+ acpi_ut_get_object_type_name(object)));
+
+ acpi_ut_delete_object_desc(object);
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_delete_internal_object_list
+ *
+ * PARAMETERS: obj_list - Pointer to the list to be deleted
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: This function deletes an internal object list, including both
+ * simple objects and package objects
+ *
+ ******************************************************************************/
+
+void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list)
+{
+ union acpi_operand_object **internal_obj;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Walk the null-terminated internal list */
+
+ for (internal_obj = obj_list; *internal_obj; internal_obj++) {
+ acpi_ut_remove_reference(*internal_obj);
+ }
+
+ /* Free the combined parameter pointer list and object array */
+
+ ACPI_FREE(obj_list);
+ return;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_update_ref_count
+ *
+ * PARAMETERS: object - Object whose ref count is to be updated
+ * action - What to do (REF_INCREMENT or REF_DECREMENT)
+ *
+ * RETURN: None. Sets new reference count within the object
+ *
+ * DESCRIPTION: Modify the reference count for an internal acpi object
+ *
+ ******************************************************************************/
+
+static void
+acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
+{
+ u16 original_count;
+ u16 new_count = 0;
+ acpi_cpu_flags lock_flags;
+ char *message;
+
+ ACPI_FUNCTION_NAME(ut_update_ref_count);
+
+ if (!object) {
+ return;
+ }
+
+ /*
+ * Always get the reference count lock. Note: Interpreter and/or
+ * Namespace is not always locked when this function is called.
+ */
+ lock_flags = acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
+ original_count = object->common.reference_count;
+
+ /* Perform the reference count action (increment, decrement) */
+
+ switch (action) {
+ case REF_INCREMENT:
+
+ new_count = original_count + 1;
+ object->common.reference_count = new_count;
+ acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
+
+ /* The current reference count should never be zero here */
+
+ if (!original_count) {
+ ACPI_WARNING((AE_INFO,
+ "Obj %p, Reference Count was zero before increment\n",
+ object));
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "Obj %p Type %.2X [%s] Refs %.2X [Incremented]\n",
+ object, object->common.type,
+ acpi_ut_get_object_type_name(object),
+ new_count));
+ message = "Incremement";
+ break;
+
+ case REF_DECREMENT:
+
+ /* The current reference count must be non-zero */
+
+ if (original_count) {
+ new_count = original_count - 1;
+ object->common.reference_count = new_count;
+ }
+
+ acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
+
+ if (!original_count) {
+ ACPI_WARNING((AE_INFO,
+ "Obj %p, Reference Count is already zero, cannot decrement\n",
+ object));
+ return;
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_ALLOCATIONS,
+ "%s: Obj %p Type %.2X Refs %.2X [Decremented]\n",
+ ACPI_GET_FUNCTION_NAME, object,
+ object->common.type, new_count));
+
+ /* Actually delete the object on a reference count of zero */
+
+ if (new_count == 0) {
+ acpi_ut_delete_internal_obj(object);
+ }
+ message = "Decrement";
+ break;
+
+ default:
+
+ acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
+ ACPI_ERROR((AE_INFO, "Unknown Reference Count action (0x%X)",
+ action));
+ return;
+ }
+
+ /*
+ * Sanity check the reference count, for debug purposes only.
+ * (A deleted object will have a huge reference count)
+ */
+ if (new_count > ACPI_MAX_REFERENCE_COUNT) {
+ ACPI_WARNING((AE_INFO,
+ "Large Reference Count (0x%X) in object %p, Type=0x%.2X Operation=%s",
+ new_count, object, object->common.type, message));
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_update_object_reference
+ *
+ * PARAMETERS: object - Increment or decrement the ref count for
+ * this object and all sub-objects
+ * action - Either REF_INCREMENT or REF_DECREMENT
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Increment or decrement the object reference count
+ *
+ * Object references are incremented when:
+ * 1) An object is attached to a Node (namespace object)
+ * 2) An object is copied (all subobjects must be incremented)
+ *
+ * Object references are decremented when:
+ * 1) An object is detached from an Node
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action)
+{
+ acpi_status status = AE_OK;
+ union acpi_generic_state *state_list = NULL;
+ union acpi_operand_object *next_object = NULL;
+ union acpi_operand_object *prev_object;
+ union acpi_generic_state *state;
+ u32 i;
+
+ ACPI_FUNCTION_NAME(ut_update_object_reference);
+
+ while (object) {
+
+ /* Make sure that this isn't a namespace handle */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "Object %p is NS handle\n", object));
+ return (AE_OK);
+ }
+
+ /*
+ * All sub-objects must have their reference count updated
+ * also. Different object types have different subobjects.
+ */
+ switch (object->common.type) {
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_POWER:
+ case ACPI_TYPE_THERMAL:
+ /*
+ * Update the notify objects for these types (if present)
+ * Two lists, system and device notify handlers.
+ */
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
+ prev_object =
+ object->common_notify.notify_list[i];
+ while (prev_object) {
+ next_object =
+ prev_object->notify.next[i];
+ acpi_ut_update_ref_count(prev_object,
+ action);
+ prev_object = next_object;
+ }
+ }
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * We must update all the sub-objects of the package,
+ * each of whom may have their own sub-objects.
+ */
+ for (i = 0; i < object->package.count; i++) {
+ /*
+ * Null package elements are legal and can be simply
+ * ignored.
+ */
+ next_object = object->package.elements[i];
+ if (!next_object) {
+ continue;
+ }
+
+ switch (next_object->common.type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+ /*
+ * For these very simple sub-objects, we can just
+ * update the reference count here and continue.
+ * Greatly increases performance of this operation.
+ */
+ acpi_ut_update_ref_count(next_object,
+ action);
+ break;
+
+ default:
+ /*
+ * For complex sub-objects, push them onto the stack
+ * for later processing (this eliminates recursion.)
+ */
+ status =
+ acpi_ut_create_update_state_and_push
+ (next_object, action, &state_list);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+ break;
+ }
+ }
+
+ next_object = NULL;
+ break;
+
+ case ACPI_TYPE_BUFFER_FIELD:
+
+ next_object = object->buffer_field.buffer_obj;
+ break;
+
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+
+ next_object = object->bank_field.bank_obj;
+ status =
+ acpi_ut_create_update_state_and_push(object->
+ bank_field.
+ region_obj,
+ action,
+ &state_list);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
+ next_object = object->index_field.index_obj;
+ status =
+ acpi_ut_create_update_state_and_push(object->
+ index_field.
+ data_obj,
+ action,
+ &state_list);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ /*
+ * The target of an Index (a package, string, or buffer) or a named
+ * reference must track changes to the ref count of the index or
+ * target object.
+ */
+ if ((object->reference.class == ACPI_REFCLASS_INDEX) ||
+ (object->reference.class == ACPI_REFCLASS_NAME)) {
+ next_object = object->reference.object;
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_REGION:
+ default:
+
+ break; /* No subobjects for all other types */
+ }
+
+ /*
+ * Now we can update the count in the main object. This can only
+ * happen after we update the sub-objects in case this causes the
+ * main object to be deleted.
+ */
+ acpi_ut_update_ref_count(object, action);
+ object = NULL;
+
+ /* Move on to the next object to be updated */
+
+ if (next_object) {
+ object = next_object;
+ next_object = NULL;
+ } else if (state_list) {
+ state = acpi_ut_pop_generic_state(&state_list);
+ object = state->update.object;
+ acpi_ut_delete_generic_state(state);
+ }
+ }
+
+ return (AE_OK);
+
+error_exit:
+
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Could not update object reference count"));
+
+ /* Free any stacked Update State objects */
+
+ while (state_list) {
+ state = acpi_ut_pop_generic_state(&state_list);
+ acpi_ut_delete_generic_state(state);
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_add_reference
+ *
+ * PARAMETERS: object - Object whose reference count is to be
+ * incremented
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Add one reference to an ACPI object
+ *
+ ******************************************************************************/
+
+void acpi_ut_add_reference(union acpi_operand_object *object)
+{
+
+ ACPI_FUNCTION_NAME(ut_add_reference);
+
+ /* Ensure that we have a valid object */
+
+ if (!acpi_ut_valid_internal_object(object)) {
+ return;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "Obj %p Current Refs=%X [To Be Incremented]\n",
+ object, object->common.reference_count));
+
+ /* Increment the reference count */
+
+ (void)acpi_ut_update_object_reference(object, REF_INCREMENT);
+ return;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_remove_reference
+ *
+ * PARAMETERS: object - Object whose ref count will be decremented
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decrement the reference count of an ACPI internal object
+ *
+ ******************************************************************************/
+
+void acpi_ut_remove_reference(union acpi_operand_object *object)
+{
+
+ ACPI_FUNCTION_NAME(ut_remove_reference);
+
+ /*
+ * Allow a NULL pointer to be passed in, just ignore it. This saves
+ * each caller from having to check. Also, ignore NS nodes.
+ */
+ if (!object ||
+ (ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED)) {
+ return;
+ }
+
+ /* Ensure that we have a valid object */
+
+ if (!acpi_ut_valid_internal_object(object)) {
+ return;
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_ALLOCATIONS,
+ "%s: Obj %p Current Refs=%X [To Be Decremented]\n",
+ ACPI_GET_FUNCTION_NAME, object,
+ object->common.reference_count));
+
+ /*
+ * Decrement the reference count, and only actually delete the object
+ * if the reference count becomes 0. (Must also decrement the ref count
+ * of all subobjects!)
+ */
+ (void)acpi_ut_update_object_reference(object, REF_DECREMENT);
+ return;
+}
diff --git a/drivers/acpi/acpica/uterror.c b/drivers/acpi/acpica/uterror.c
new file mode 100644
index 000000000..918aca7c4
--- /dev/null
+++ b/drivers/acpi/acpica/uterror.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: uterror - Various internal error/warning output functions
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("uterror")
+
+/*
+ * This module contains internal error functions that may
+ * be configured out.
+ */
+#if !defined (ACPI_NO_ERROR_MESSAGES)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_predefined_warning
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * pathname - Full pathname to the node
+ * node_flags - From Namespace node for the method/object
+ * format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Warnings for the predefined validation module. Messages are
+ * only emitted the first time a problem with a particular
+ * method/object is detected. This prevents a flood of error
+ * messages for methods that are repeatedly evaluated.
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_warning(const char *module_name,
+ u32 line_number,
+ char *pathname,
+ u16 node_flags, const char *format, ...)
+{
+ va_list arg_list;
+
+ /*
+ * Warning messages for this method/object will be disabled after the
+ * first time a validation fails or an object is successfully repaired.
+ */
+ if (node_flags & ANOBJ_EVALUATED) {
+ return;
+ }
+
+ acpi_os_printf(ACPI_MSG_WARNING "%s: ", pathname);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_predefined_info
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * pathname - Full pathname to the node
+ * node_flags - From Namespace node for the method/object
+ * format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Info messages for the predefined validation module. Messages
+ * are only emitted the first time a problem with a particular
+ * method/object is detected. This prevents a flood of
+ * messages for methods that are repeatedly evaluated.
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_info(const char *module_name,
+ u32 line_number,
+ char *pathname, u16 node_flags, const char *format, ...)
+{
+ va_list arg_list;
+
+ /*
+ * Warning messages for this method/object will be disabled after the
+ * first time a validation fails or an object is successfully repaired.
+ */
+ if (node_flags & ANOBJ_EVALUATED) {
+ return;
+ }
+
+ acpi_os_printf(ACPI_MSG_INFO "%s: ", pathname);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_predefined_bios_error
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * pathname - Full pathname to the node
+ * node_flags - From Namespace node for the method/object
+ * format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: BIOS error message for predefined names. Messages
+ * are only emitted the first time a problem with a particular
+ * method/object is detected. This prevents a flood of
+ * messages for methods that are repeatedly evaluated.
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_bios_error(const char *module_name,
+ u32 line_number,
+ char *pathname,
+ u16 node_flags, const char *format, ...)
+{
+ va_list arg_list;
+
+ /*
+ * Warning messages for this method/object will be disabled after the
+ * first time a validation fails or an object is successfully repaired.
+ */
+ if (node_flags & ANOBJ_EVALUATED) {
+ return;
+ }
+
+ acpi_os_printf(ACPI_MSG_BIOS_ERROR "%s: ", pathname);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_prefixed_namespace_error
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * prefix_scope - Scope/Path that prefixes the internal path
+ * internal_path - Name or path of the namespace node
+ * lookup_status - Exception code from NS lookup
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print error message with the full pathname constructed this way:
+ *
+ * prefix_scope_node_full_path.externalized_internal_path
+ *
+ * NOTE: 10/2017: Treat the major ns_lookup errors as firmware errors
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_prefixed_namespace_error(const char *module_name,
+ u32 line_number,
+ union acpi_generic_state *prefix_scope,
+ const char *internal_path,
+ acpi_status lookup_status)
+{
+ char *full_path;
+ const char *message;
+
+ /*
+ * Main cases:
+ * 1) Object creation, object must not already exist
+ * 2) Object lookup, object must exist
+ */
+ switch (lookup_status) {
+ case AE_ALREADY_EXISTS:
+
+ acpi_os_printf(ACPI_MSG_BIOS_ERROR);
+ message = "Failure creating named object";
+ break;
+
+ case AE_NOT_FOUND:
+
+ acpi_os_printf(ACPI_MSG_BIOS_ERROR);
+ message = "Could not resolve symbol";
+ break;
+
+ default:
+
+ acpi_os_printf(ACPI_MSG_ERROR);
+ message = "Failure resolving symbol";
+ break;
+ }
+
+ /* Concatenate the prefix path and the internal path */
+
+ full_path =
+ acpi_ns_build_prefixed_pathname(prefix_scope, internal_path);
+
+ acpi_os_printf("%s [%s], %s", message,
+ full_path ? full_path : "Could not get pathname",
+ acpi_format_exception(lookup_status));
+
+ if (full_path) {
+ ACPI_FREE(full_path);
+ }
+
+ ACPI_MSG_SUFFIX;
+}
+
+#ifdef __OBSOLETE_FUNCTION
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_namespace_error
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * internal_name - Name or path of the namespace node
+ * lookup_status - Exception code from NS lookup
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print error message with the full pathname for the NS node.
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_namespace_error(const char *module_name,
+ u32 line_number,
+ const char *internal_name, acpi_status lookup_status)
+{
+ acpi_status status;
+ u32 bad_name;
+ char *name = NULL;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_ERROR);
+
+ if (lookup_status == AE_BAD_CHARACTER) {
+
+ /* There is a non-ascii character in the name */
+
+ ACPI_MOVE_32_TO_32(&bad_name,
+ ACPI_CAST_PTR(u32, internal_name));
+ acpi_os_printf("[0x%.8X] (NON-ASCII)", bad_name);
+ } else {
+ /* Convert path to external format */
+
+ status =
+ acpi_ns_externalize_name(ACPI_UINT32_MAX, internal_name,
+ NULL, &name);
+
+ /* Print target name */
+
+ if (ACPI_SUCCESS(status)) {
+ acpi_os_printf("[%s]", name);
+ } else {
+ acpi_os_printf("[COULD NOT EXTERNALIZE NAME]");
+ }
+
+ if (name) {
+ ACPI_FREE(name);
+ }
+ }
+
+ acpi_os_printf(" Namespace lookup failure, %s",
+ acpi_format_exception(lookup_status));
+
+ ACPI_MSG_SUFFIX;
+ ACPI_MSG_REDIRECT_END;
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_method_error
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * message - Error message to use on failure
+ * prefix_node - Prefix relative to the path
+ * path - Path to the node (optional)
+ * method_status - Execution status
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print error message with the full pathname for the method.
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_method_error(const char *module_name,
+ u32 line_number,
+ const char *message,
+ struct acpi_namespace_node *prefix_node,
+ const char *path, acpi_status method_status)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node = prefix_node;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_ERROR);
+
+ if (path) {
+ status = acpi_ns_get_node(prefix_node, path,
+ ACPI_NS_NO_UPSEARCH, &node);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("[Could not get node by pathname]");
+ }
+ }
+
+ acpi_ns_print_node_pathname(node, message);
+ acpi_os_printf(" due to previous error (%s)",
+ acpi_format_exception(method_status));
+
+ ACPI_MSG_SUFFIX;
+ ACPI_MSG_REDIRECT_END;
+}
+
+#endif /* ACPI_NO_ERROR_MESSAGES */
diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c
new file mode 100644
index 000000000..df20d46ed
--- /dev/null
+++ b/drivers/acpi/acpica/uteval.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: uteval - Object evaluation
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("uteval")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_evaluate_object
+ *
+ * PARAMETERS: prefix_node - Starting node
+ * path - Path to object from starting node
+ * expected_return_types - Bitmap of allowed return types
+ * return_desc - Where a return value is stored
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Evaluates a namespace object and verifies the type of the
+ * return object. Common code that simplifies accessing objects
+ * that have required return objects of fixed types.
+ *
+ * NOTE: Internal function, no parameter validation
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node,
+ const char *path,
+ u32 expected_return_btypes,
+ union acpi_operand_object **return_desc)
+{
+ struct acpi_evaluate_info *info;
+ acpi_status status;
+ u32 return_btype;
+
+ ACPI_FUNCTION_TRACE(ut_evaluate_object);
+
+ /* Allocate the evaluation information block */
+
+ info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
+ if (!info) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ info->prefix_node = prefix_node;
+ info->relative_pathname = path;
+
+ /* Evaluate the object/method */
+
+ status = acpi_ns_evaluate(info);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_NOT_FOUND) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[%4.4s.%s] was not found\n",
+ acpi_ut_get_node_name(prefix_node),
+ path));
+ } else {
+ ACPI_ERROR_METHOD("Method execution failed",
+ prefix_node, path, status);
+ }
+
+ goto cleanup;
+ }
+
+ /* Did we get a return object? */
+
+ if (!info->return_object) {
+ if (expected_return_btypes) {
+ ACPI_ERROR_METHOD("No object was returned from",
+ prefix_node, path, AE_NOT_EXIST);
+
+ status = AE_NOT_EXIST;
+ }
+
+ goto cleanup;
+ }
+
+ /* Map the return object type to the bitmapped type */
+
+ switch ((info->return_object)->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ return_btype = ACPI_BTYPE_INTEGER;
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ return_btype = ACPI_BTYPE_BUFFER;
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ return_btype = ACPI_BTYPE_STRING;
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ return_btype = ACPI_BTYPE_PACKAGE;
+ break;
+
+ default:
+
+ return_btype = 0;
+ break;
+ }
+
+ if ((acpi_gbl_enable_interpreter_slack) && (!expected_return_btypes)) {
+ /*
+ * We received a return object, but one was not expected. This can
+ * happen frequently if the "implicit return" feature is enabled.
+ * Just delete the return object and return AE_OK.
+ */
+ acpi_ut_remove_reference(info->return_object);
+ goto cleanup;
+ }
+
+ /* Is the return object one of the expected types? */
+
+ if (!(expected_return_btypes & return_btype)) {
+ ACPI_ERROR_METHOD("Return object type is incorrect",
+ prefix_node, path, AE_TYPE);
+
+ ACPI_ERROR((AE_INFO,
+ "Type returned from %s was incorrect: %s, expected Btypes: 0x%X",
+ path,
+ acpi_ut_get_object_type_name(info->return_object),
+ expected_return_btypes));
+
+ /* On error exit, we must delete the return object */
+
+ acpi_ut_remove_reference(info->return_object);
+ status = AE_TYPE;
+ goto cleanup;
+ }
+
+ /* Object type is OK, return it */
+
+ *return_desc = info->return_object;
+
+cleanup:
+ ACPI_FREE(info);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_evaluate_numeric_object
+ *
+ * PARAMETERS: object_name - Object name to be evaluated
+ * device_node - Node for the device
+ * value - Where the value is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Evaluates a numeric namespace object for a selected device
+ * and stores result in *Value.
+ *
+ * NOTE: Internal function, no parameter validation
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_evaluate_numeric_object(const char *object_name,
+ struct acpi_namespace_node *device_node,
+ u64 *value)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ut_evaluate_numeric_object);
+
+ status = acpi_ut_evaluate_object(device_node, object_name,
+ ACPI_BTYPE_INTEGER, &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Get the returned Integer */
+
+ *value = obj_desc->integer.value;
+
+ /* On exit, we must delete the return object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_execute_STA
+ *
+ * PARAMETERS: device_node - Node for the device
+ * flags - Where the status flags are returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Executes _STA for selected device and stores results in
+ * *Flags. If _STA does not exist, then the device is assumed
+ * to be present/functional/enabled (as per the ACPI spec).
+ *
+ * NOTE: Internal function, no parameter validation
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_execute_STA(struct acpi_namespace_node *device_node, u32 * flags)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ut_execute_STA);
+
+ status = acpi_ut_evaluate_object(device_node, METHOD_NAME__STA,
+ ACPI_BTYPE_INTEGER, &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ if (AE_NOT_FOUND == status) {
+ /*
+ * if _STA does not exist, then (as per the ACPI specification),
+ * the returned flags will indicate that the device is present,
+ * functional, and enabled.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "_STA on %4.4s was not found, assuming device is present\n",
+ acpi_ut_get_node_name(device_node)));
+
+ *flags = ACPI_UINT32_MAX;
+ status = AE_OK;
+ }
+
+ return_ACPI_STATUS(status);
+ }
+
+ /* Extract the status flags */
+
+ *flags = (u32) obj_desc->integer.value;
+
+ /* On exit, we must delete the return object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_execute_power_methods
+ *
+ * PARAMETERS: device_node - Node for the device
+ * method_names - Array of power method names
+ * method_count - Number of methods to execute
+ * out_values - Where the power method values are returned
+ *
+ * RETURN: Status, out_values
+ *
+ * DESCRIPTION: Executes the specified power methods for the device and returns
+ * the result(s).
+ *
+ * NOTE: Internal function, no parameter validation
+ *
+******************************************************************************/
+
+acpi_status
+acpi_ut_execute_power_methods(struct acpi_namespace_node *device_node,
+ const char **method_names,
+ u8 method_count, u8 *out_values)
+{
+ union acpi_operand_object *obj_desc;
+ acpi_status status;
+ acpi_status final_status = AE_NOT_FOUND;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ut_execute_power_methods);
+
+ for (i = 0; i < method_count; i++) {
+ /*
+ * Execute the power method (_sx_d or _sx_w). The only allowable
+ * return type is an Integer.
+ */
+ status = acpi_ut_evaluate_object(device_node,
+ ACPI_CAST_PTR(char,
+ method_names[i]),
+ ACPI_BTYPE_INTEGER, &obj_desc);
+ if (ACPI_SUCCESS(status)) {
+ out_values[i] = (u8)obj_desc->integer.value;
+
+ /* Delete the return object */
+
+ acpi_ut_remove_reference(obj_desc);
+ final_status = AE_OK; /* At least one value is valid */
+ continue;
+ }
+
+ out_values[i] = ACPI_UINT8_MAX;
+ if (status == AE_NOT_FOUND) {
+ continue; /* Ignore if not found */
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Failed %s on Device %4.4s, %s\n",
+ ACPI_CAST_PTR(char, method_names[i]),
+ acpi_ut_get_node_name(device_node),
+ acpi_format_exception(status)));
+ }
+
+ return_ACPI_STATUS(final_status);
+}
diff --git a/drivers/acpi/acpica/utexcep.c b/drivers/acpi/acpica/utexcep.c
new file mode 100644
index 000000000..60fbdcde1
--- /dev/null
+++ b/drivers/acpi/acpica/utexcep.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utexcep - Exception code support
+ *
+ ******************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#define ACPI_DEFINE_EXCEPTION_TABLE
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utexcep")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_format_exception
+ *
+ * PARAMETERS: status - The acpi_status code to be formatted
+ *
+ * RETURN: A string containing the exception text. A valid pointer is
+ * always returned.
+ *
+ * DESCRIPTION: This function translates an ACPI exception into an ASCII
+ * string. Returns "unknown status" string for invalid codes.
+ *
+ ******************************************************************************/
+const char *acpi_format_exception(acpi_status status)
+{
+ const struct acpi_exception_info *exception;
+
+ ACPI_FUNCTION_ENTRY();
+
+ exception = acpi_ut_validate_exception(status);
+ if (!exception) {
+
+ /* Exception code was not recognized */
+
+ ACPI_ERROR((AE_INFO,
+ "Unknown exception code: 0x%8.8X", status));
+
+ return ("UNKNOWN_STATUS_CODE");
+ }
+
+ return (exception->name);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_format_exception)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_validate_exception
+ *
+ * PARAMETERS: status - The acpi_status code to be formatted
+ *
+ * RETURN: A string containing the exception text. NULL if exception is
+ * not valid.
+ *
+ * DESCRIPTION: This function validates and translates an ACPI exception into
+ * an ASCII string.
+ *
+ ******************************************************************************/
+const struct acpi_exception_info *acpi_ut_validate_exception(acpi_status status)
+{
+ u32 sub_status;
+ const struct acpi_exception_info *exception = NULL;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * Status is composed of two parts, a "type" and an actual code
+ */
+ sub_status = (status & ~AE_CODE_MASK);
+
+ switch (status & AE_CODE_MASK) {
+ case AE_CODE_ENVIRONMENTAL:
+
+ if (sub_status <= AE_CODE_ENV_MAX) {
+ exception = &acpi_gbl_exception_names_env[sub_status];
+ }
+ break;
+
+ case AE_CODE_PROGRAMMER:
+
+ if (sub_status <= AE_CODE_PGM_MAX) {
+ exception = &acpi_gbl_exception_names_pgm[sub_status];
+ }
+ break;
+
+ case AE_CODE_ACPI_TABLES:
+
+ if (sub_status <= AE_CODE_TBL_MAX) {
+ exception = &acpi_gbl_exception_names_tbl[sub_status];
+ }
+ break;
+
+ case AE_CODE_AML:
+
+ if (sub_status <= AE_CODE_AML_MAX) {
+ exception = &acpi_gbl_exception_names_aml[sub_status];
+ }
+ break;
+
+ case AE_CODE_CONTROL:
+
+ if (sub_status <= AE_CODE_CTRL_MAX) {
+ exception = &acpi_gbl_exception_names_ctrl[sub_status];
+ }
+ break;
+
+ default:
+
+ break;
+ }
+
+ if (!exception || !exception->name) {
+ return (NULL);
+ }
+
+ return (exception);
+}
diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c
new file mode 100644
index 000000000..cda6e16dd
--- /dev/null
+++ b/drivers/acpi/acpica/utglobal.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utglobal - Global variables for the ACPI subsystem
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+#define DEFINE_ACPI_GLOBALS
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utglobal")
+
+/*******************************************************************************
+ *
+ * Static global variable initialization.
+ *
+ ******************************************************************************/
+/* Various state name strings */
+const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT] = {
+ "\\_S0_",
+ "\\_S1_",
+ "\\_S2_",
+ "\\_S3_",
+ "\\_S4_",
+ "\\_S5_"
+};
+
+const char *acpi_gbl_lowest_dstate_names[ACPI_NUM_sx_w_METHODS] = {
+ "_S0W",
+ "_S1W",
+ "_S2W",
+ "_S3W",
+ "_S4W"
+};
+
+const char *acpi_gbl_highest_dstate_names[ACPI_NUM_sx_d_METHODS] = {
+ "_S1D",
+ "_S2D",
+ "_S3D",
+ "_S4D"
+};
+
+/* Hex-to-ascii */
+
+const char acpi_gbl_lower_hex_digits[] = "0123456789abcdef";
+const char acpi_gbl_upper_hex_digits[] = "0123456789ABCDEF";
+
+/*******************************************************************************
+ *
+ * Namespace globals
+ *
+ ******************************************************************************/
+/*
+ * Predefined ACPI Names (Built-in to the Interpreter)
+ *
+ * NOTES:
+ * 1) _SB_ is defined to be a device to allow \_SB_._INI to be run
+ * during the initialization sequence.
+ * 2) _TZ_ is defined to be a thermal zone in order to allow ASL code to
+ * perform a Notify() operation on it. 09/2010: Changed to type Device.
+ * This still allows notifies, but does not confuse host code that
+ * searches for valid thermal_zone objects.
+ */
+const struct acpi_predefined_names acpi_gbl_pre_defined_names[] = {
+ {"_GPE", ACPI_TYPE_LOCAL_SCOPE, NULL},
+ {"_PR_", ACPI_TYPE_LOCAL_SCOPE, NULL},
+ {"_SB_", ACPI_TYPE_DEVICE, NULL},
+ {"_SI_", ACPI_TYPE_LOCAL_SCOPE, NULL},
+ {"_TZ_", ACPI_TYPE_DEVICE, NULL},
+ /*
+ * March, 2015:
+ * The _REV object is in the process of being deprecated, because
+ * other ACPI implementations permanently return 2. Thus, it
+ * has little or no value. Return 2 for compatibility with
+ * other ACPI implementations.
+ */
+ {"_REV", ACPI_TYPE_INTEGER, ACPI_CAST_PTR(char, 2)},
+ {"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME},
+ {"_GL_", ACPI_TYPE_MUTEX, ACPI_CAST_PTR(char, 1)},
+ {"_OSI", ACPI_TYPE_METHOD, ACPI_CAST_PTR(char, 1)},
+
+ /* Table terminator */
+
+ {NULL, ACPI_TYPE_ANY, NULL}
+};
+
+#if (!ACPI_REDUCED_HARDWARE)
+/******************************************************************************
+ *
+ * Event and Hardware globals
+ *
+ ******************************************************************************/
+
+struct acpi_bit_register_info acpi_gbl_bit_register_info[ACPI_NUM_BITREG] = {
+ /* Name Parent Register Register Bit Position Register Bit Mask */
+
+ /* ACPI_BITREG_TIMER_STATUS */ {ACPI_REGISTER_PM1_STATUS,
+ ACPI_BITPOSITION_TIMER_STATUS,
+ ACPI_BITMASK_TIMER_STATUS},
+ /* ACPI_BITREG_BUS_MASTER_STATUS */ {ACPI_REGISTER_PM1_STATUS,
+ ACPI_BITPOSITION_BUS_MASTER_STATUS,
+ ACPI_BITMASK_BUS_MASTER_STATUS},
+ /* ACPI_BITREG_GLOBAL_LOCK_STATUS */ {ACPI_REGISTER_PM1_STATUS,
+ ACPI_BITPOSITION_GLOBAL_LOCK_STATUS,
+ ACPI_BITMASK_GLOBAL_LOCK_STATUS},
+ /* ACPI_BITREG_POWER_BUTTON_STATUS */ {ACPI_REGISTER_PM1_STATUS,
+ ACPI_BITPOSITION_POWER_BUTTON_STATUS,
+ ACPI_BITMASK_POWER_BUTTON_STATUS},
+ /* ACPI_BITREG_SLEEP_BUTTON_STATUS */ {ACPI_REGISTER_PM1_STATUS,
+ ACPI_BITPOSITION_SLEEP_BUTTON_STATUS,
+ ACPI_BITMASK_SLEEP_BUTTON_STATUS},
+ /* ACPI_BITREG_RT_CLOCK_STATUS */ {ACPI_REGISTER_PM1_STATUS,
+ ACPI_BITPOSITION_RT_CLOCK_STATUS,
+ ACPI_BITMASK_RT_CLOCK_STATUS},
+ /* ACPI_BITREG_WAKE_STATUS */ {ACPI_REGISTER_PM1_STATUS,
+ ACPI_BITPOSITION_WAKE_STATUS,
+ ACPI_BITMASK_WAKE_STATUS},
+ /* ACPI_BITREG_PCIEXP_WAKE_STATUS */ {ACPI_REGISTER_PM1_STATUS,
+ ACPI_BITPOSITION_PCIEXP_WAKE_STATUS,
+ ACPI_BITMASK_PCIEXP_WAKE_STATUS},
+
+ /* ACPI_BITREG_TIMER_ENABLE */ {ACPI_REGISTER_PM1_ENABLE,
+ ACPI_BITPOSITION_TIMER_ENABLE,
+ ACPI_BITMASK_TIMER_ENABLE},
+ /* ACPI_BITREG_GLOBAL_LOCK_ENABLE */ {ACPI_REGISTER_PM1_ENABLE,
+ ACPI_BITPOSITION_GLOBAL_LOCK_ENABLE,
+ ACPI_BITMASK_GLOBAL_LOCK_ENABLE},
+ /* ACPI_BITREG_POWER_BUTTON_ENABLE */ {ACPI_REGISTER_PM1_ENABLE,
+ ACPI_BITPOSITION_POWER_BUTTON_ENABLE,
+ ACPI_BITMASK_POWER_BUTTON_ENABLE},
+ /* ACPI_BITREG_SLEEP_BUTTON_ENABLE */ {ACPI_REGISTER_PM1_ENABLE,
+ ACPI_BITPOSITION_SLEEP_BUTTON_ENABLE,
+ ACPI_BITMASK_SLEEP_BUTTON_ENABLE},
+ /* ACPI_BITREG_RT_CLOCK_ENABLE */ {ACPI_REGISTER_PM1_ENABLE,
+ ACPI_BITPOSITION_RT_CLOCK_ENABLE,
+ ACPI_BITMASK_RT_CLOCK_ENABLE},
+ /* ACPI_BITREG_PCIEXP_WAKE_DISABLE */ {ACPI_REGISTER_PM1_ENABLE,
+ ACPI_BITPOSITION_PCIEXP_WAKE_DISABLE,
+ ACPI_BITMASK_PCIEXP_WAKE_DISABLE},
+
+ /* ACPI_BITREG_SCI_ENABLE */ {ACPI_REGISTER_PM1_CONTROL,
+ ACPI_BITPOSITION_SCI_ENABLE,
+ ACPI_BITMASK_SCI_ENABLE},
+ /* ACPI_BITREG_BUS_MASTER_RLD */ {ACPI_REGISTER_PM1_CONTROL,
+ ACPI_BITPOSITION_BUS_MASTER_RLD,
+ ACPI_BITMASK_BUS_MASTER_RLD},
+ /* ACPI_BITREG_GLOBAL_LOCK_RELEASE */ {ACPI_REGISTER_PM1_CONTROL,
+ ACPI_BITPOSITION_GLOBAL_LOCK_RELEASE,
+ ACPI_BITMASK_GLOBAL_LOCK_RELEASE},
+ /* ACPI_BITREG_SLEEP_TYPE */ {ACPI_REGISTER_PM1_CONTROL,
+ ACPI_BITPOSITION_SLEEP_TYPE,
+ ACPI_BITMASK_SLEEP_TYPE},
+ /* ACPI_BITREG_SLEEP_ENABLE */ {ACPI_REGISTER_PM1_CONTROL,
+ ACPI_BITPOSITION_SLEEP_ENABLE,
+ ACPI_BITMASK_SLEEP_ENABLE},
+
+ /* ACPI_BITREG_ARB_DIS */ {ACPI_REGISTER_PM2_CONTROL,
+ ACPI_BITPOSITION_ARB_DISABLE,
+ ACPI_BITMASK_ARB_DISABLE}
+};
+
+struct acpi_fixed_event_info acpi_gbl_fixed_event_info[ACPI_NUM_FIXED_EVENTS] = {
+ /* ACPI_EVENT_PMTIMER */ {ACPI_BITREG_TIMER_STATUS,
+ ACPI_BITREG_TIMER_ENABLE,
+ ACPI_BITMASK_TIMER_STATUS,
+ ACPI_BITMASK_TIMER_ENABLE},
+ /* ACPI_EVENT_GLOBAL */ {ACPI_BITREG_GLOBAL_LOCK_STATUS,
+ ACPI_BITREG_GLOBAL_LOCK_ENABLE,
+ ACPI_BITMASK_GLOBAL_LOCK_STATUS,
+ ACPI_BITMASK_GLOBAL_LOCK_ENABLE},
+ /* ACPI_EVENT_POWER_BUTTON */ {ACPI_BITREG_POWER_BUTTON_STATUS,
+ ACPI_BITREG_POWER_BUTTON_ENABLE,
+ ACPI_BITMASK_POWER_BUTTON_STATUS,
+ ACPI_BITMASK_POWER_BUTTON_ENABLE},
+ /* ACPI_EVENT_SLEEP_BUTTON */ {ACPI_BITREG_SLEEP_BUTTON_STATUS,
+ ACPI_BITREG_SLEEP_BUTTON_ENABLE,
+ ACPI_BITMASK_SLEEP_BUTTON_STATUS,
+ ACPI_BITMASK_SLEEP_BUTTON_ENABLE},
+ /* ACPI_EVENT_RTC */ {ACPI_BITREG_RT_CLOCK_STATUS,
+ ACPI_BITREG_RT_CLOCK_ENABLE,
+ ACPI_BITMASK_RT_CLOCK_STATUS,
+ ACPI_BITMASK_RT_CLOCK_ENABLE},
+};
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+#if defined (ACPI_DISASSEMBLER) || defined (ACPI_ASL_COMPILER)
+
+/* to_pld macro: compile/disassemble strings */
+
+const char *acpi_gbl_pld_panel_list[] = {
+ "TOP",
+ "BOTTOM",
+ "LEFT",
+ "RIGHT",
+ "FRONT",
+ "BACK",
+ "UNKNOWN",
+ NULL
+};
+
+const char *acpi_gbl_pld_vertical_position_list[] = {
+ "UPPER",
+ "CENTER",
+ "LOWER",
+ NULL
+};
+
+const char *acpi_gbl_pld_horizontal_position_list[] = {
+ "LEFT",
+ "CENTER",
+ "RIGHT",
+ NULL
+};
+
+const char *acpi_gbl_pld_shape_list[] = {
+ "ROUND",
+ "OVAL",
+ "SQUARE",
+ "VERTICALRECTANGLE",
+ "HORIZONTALRECTANGLE",
+ "VERTICALTRAPEZOID",
+ "HORIZONTALTRAPEZOID",
+ "UNKNOWN",
+ "CHAMFERED",
+ NULL
+};
+#endif
+
+/* Public globals */
+
+ACPI_EXPORT_SYMBOL(acpi_gbl_FADT)
+ACPI_EXPORT_SYMBOL(acpi_dbg_level)
+ACPI_EXPORT_SYMBOL(acpi_dbg_layer)
+ACPI_EXPORT_SYMBOL(acpi_gpe_count)
+ACPI_EXPORT_SYMBOL(acpi_current_gpe_count)
diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c
new file mode 100644
index 000000000..c811ee2a8
--- /dev/null
+++ b/drivers/acpi/acpica/uthex.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: uthex -- Hex/ASCII support functions
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_COMPILER
+ACPI_MODULE_NAME("uthex")
+
+/* Hex to ASCII conversion table */
+static const char acpi_gbl_hex_to_ascii[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F'
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_hex_to_ascii_char
+ *
+ * PARAMETERS: integer - Contains the hex digit
+ * position - bit position of the digit within the
+ * integer (multiple of 4)
+ *
+ * RETURN: The converted Ascii character
+ *
+ * DESCRIPTION: Convert a hex digit to an Ascii character
+ *
+ ******************************************************************************/
+
+char acpi_ut_hex_to_ascii_char(u64 integer, u32 position)
+{
+ u64 index;
+
+ acpi_ut_short_shift_right(integer, position, &index);
+ return (acpi_gbl_hex_to_ascii[index & 0xF]);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_ascii_to_hex_byte
+ *
+ * PARAMETERS: two_ascii_chars - Pointer to two ASCII characters
+ * return_byte - Where converted byte is returned
+ *
+ * RETURN: Status and converted hex byte
+ *
+ * DESCRIPTION: Perform ascii-to-hex translation, exactly two ASCII characters
+ * to a single converted byte value.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_ascii_to_hex_byte(char *two_ascii_chars, u8 *return_byte)
+{
+
+ /* Both ASCII characters must be valid hex digits */
+
+ if (!isxdigit((int)two_ascii_chars[0]) ||
+ !isxdigit((int)two_ascii_chars[1])) {
+ return (AE_BAD_HEX_CONSTANT);
+ }
+
+ *return_byte =
+ acpi_ut_ascii_char_to_hex(two_ascii_chars[1]) |
+ (acpi_ut_ascii_char_to_hex(two_ascii_chars[0]) << 4);
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_ascii_char_to_hex
+ *
+ * PARAMETERS: hex_char - Hex character in Ascii. Must be:
+ * 0-9 or A-F or a-f
+ *
+ * RETURN: The binary value of the ascii/hex character
+ *
+ * DESCRIPTION: Perform ascii-to-hex translation
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_ascii_char_to_hex(int hex_char)
+{
+
+ /* Values 0-9 */
+
+ if (hex_char <= '9') {
+ return ((u8)(hex_char - '0'));
+ }
+
+ /* Upper case A-F */
+
+ if (hex_char <= 'F') {
+ return ((u8)(hex_char - 0x37));
+ }
+
+ /* Lower case a-f */
+
+ return ((u8)(hex_char - 0x57));
+}
diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c
new file mode 100644
index 000000000..b6caab49f
--- /dev/null
+++ b/drivers/acpi/acpica/utids.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utids - support for device Ids - HID, UID, CID, SUB, CLS
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utids")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_execute_HID
+ *
+ * PARAMETERS: device_node - Node for the device
+ * return_id - Where the string HID is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Executes the _HID control method that returns the hardware
+ * ID of the device. The HID is either an 32-bit encoded EISAID
+ * Integer or a String. A string is always returned. An EISAID
+ * is converted to a string.
+ *
+ * NOTE: Internal function, no parameter validation
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ut_execute_HID(struct acpi_namespace_node *device_node,
+ struct acpi_pnp_device_id **return_id)
+{
+ union acpi_operand_object *obj_desc;
+ struct acpi_pnp_device_id *hid;
+ u32 length;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ut_execute_HID);
+
+ status = acpi_ut_evaluate_object(device_node, METHOD_NAME__HID,
+ ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Get the size of the String to be returned, includes null terminator */
+
+ if (obj_desc->common.type == ACPI_TYPE_INTEGER) {
+ length = ACPI_EISAID_STRING_SIZE;
+ } else {
+ length = obj_desc->string.length + 1;
+ }
+
+ /* Allocate a buffer for the HID */
+
+ hid =
+ ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pnp_device_id) +
+ (acpi_size)length);
+ if (!hid) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Area for the string starts after PNP_DEVICE_ID struct */
+
+ hid->string =
+ ACPI_ADD_PTR(char, hid, sizeof(struct acpi_pnp_device_id));
+
+ /* Convert EISAID to a string or simply copy existing string */
+
+ if (obj_desc->common.type == ACPI_TYPE_INTEGER) {
+ acpi_ex_eisa_id_to_string(hid->string, obj_desc->integer.value);
+ } else {
+ strcpy(hid->string, obj_desc->string.pointer);
+ }
+
+ hid->length = length;
+ *return_id = hid;
+
+cleanup:
+
+ /* On exit, we must delete the return object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_execute_UID
+ *
+ * PARAMETERS: device_node - Node for the device
+ * return_id - Where the string UID is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Executes the _UID control method that returns the unique
+ * ID of the device. The UID is either a 64-bit Integer (NOT an
+ * EISAID) or a string. Always returns a string. A 64-bit integer
+ * is converted to a decimal string.
+ *
+ * NOTE: Internal function, no parameter validation
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_execute_UID(struct acpi_namespace_node *device_node,
+ struct acpi_pnp_device_id **return_id)
+{
+ union acpi_operand_object *obj_desc;
+ struct acpi_pnp_device_id *uid;
+ u32 length;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ut_execute_UID);
+
+ status = acpi_ut_evaluate_object(device_node, METHOD_NAME__UID,
+ ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING,
+ &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Get the size of the String to be returned, includes null terminator */
+
+ if (obj_desc->common.type == ACPI_TYPE_INTEGER) {
+ length = ACPI_MAX64_DECIMAL_DIGITS + 1;
+ } else {
+ length = obj_desc->string.length + 1;
+ }
+
+ /* Allocate a buffer for the UID */
+
+ uid =
+ ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pnp_device_id) +
+ (acpi_size)length);
+ if (!uid) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Area for the string starts after PNP_DEVICE_ID struct */
+
+ uid->string =
+ ACPI_ADD_PTR(char, uid, sizeof(struct acpi_pnp_device_id));
+
+ /* Convert an Integer to string, or just copy an existing string */
+
+ if (obj_desc->common.type == ACPI_TYPE_INTEGER) {
+ acpi_ex_integer_to_string(uid->string, obj_desc->integer.value);
+ } else {
+ strcpy(uid->string, obj_desc->string.pointer);
+ }
+
+ uid->length = length;
+ *return_id = uid;
+
+cleanup:
+
+ /* On exit, we must delete the return object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_execute_CID
+ *
+ * PARAMETERS: device_node - Node for the device
+ * return_cid_list - Where the CID list is returned
+ *
+ * RETURN: Status, list of CID strings
+ *
+ * DESCRIPTION: Executes the _CID control method that returns one or more
+ * compatible hardware IDs for the device.
+ *
+ * NOTE: Internal function, no parameter validation
+ *
+ * A _CID method can return either a single compatible ID or a package of
+ * compatible IDs. Each compatible ID can be one of the following:
+ * 1) Integer (32 bit compressed EISA ID) or
+ * 2) String (PCI ID format, e.g. "PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss")
+ *
+ * The Integer CIDs are converted to string format by this function.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_execute_CID(struct acpi_namespace_node *device_node,
+ struct acpi_pnp_device_id_list **return_cid_list)
+{
+ union acpi_operand_object **cid_objects;
+ union acpi_operand_object *obj_desc;
+ struct acpi_pnp_device_id_list *cid_list;
+ char *next_id_string;
+ u32 string_area_size;
+ u32 length;
+ u32 cid_list_size;
+ acpi_status status;
+ u32 count;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ut_execute_CID);
+
+ /* Evaluate the _CID method for this device */
+
+ status = acpi_ut_evaluate_object(device_node, METHOD_NAME__CID,
+ ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING
+ | ACPI_BTYPE_PACKAGE, &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Get the count and size of the returned _CIDs. _CID can return either
+ * a Package of Integers/Strings or a single Integer or String.
+ * Note: This section also validates that all CID elements are of the
+ * correct type (Integer or String).
+ */
+ if (obj_desc->common.type == ACPI_TYPE_PACKAGE) {
+ count = obj_desc->package.count;
+ cid_objects = obj_desc->package.elements;
+ } else { /* Single Integer or String CID */
+
+ count = 1;
+ cid_objects = &obj_desc;
+ }
+
+ string_area_size = 0;
+ for (i = 0; i < count; i++) {
+
+ /* String lengths include null terminator */
+
+ switch (cid_objects[i]->common.type) {
+ case ACPI_TYPE_INTEGER:
+
+ string_area_size += ACPI_EISAID_STRING_SIZE;
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ string_area_size += cid_objects[i]->string.length + 1;
+ break;
+
+ default:
+
+ status = AE_TYPE;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Now that we know the length of the CIDs, allocate return buffer:
+ * 1) Size of the base structure +
+ * 2) Size of the CID PNP_DEVICE_ID array +
+ * 3) Size of the actual CID strings
+ */
+ cid_list_size = sizeof(struct acpi_pnp_device_id_list) +
+ (count * sizeof(struct acpi_pnp_device_id)) + string_area_size;
+
+ cid_list = ACPI_ALLOCATE_ZEROED(cid_list_size);
+ if (!cid_list) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Area for CID strings starts after the CID PNP_DEVICE_ID array */
+
+ next_id_string = ACPI_CAST_PTR(char, cid_list->ids) +
+ ((acpi_size)count * sizeof(struct acpi_pnp_device_id));
+
+ /* Copy/convert the CIDs to the return buffer */
+
+ for (i = 0; i < count; i++) {
+ if (cid_objects[i]->common.type == ACPI_TYPE_INTEGER) {
+
+ /* Convert the Integer (EISAID) CID to a string */
+
+ acpi_ex_eisa_id_to_string(next_id_string,
+ cid_objects[i]->integer.
+ value);
+ length = ACPI_EISAID_STRING_SIZE;
+ } else { /* ACPI_TYPE_STRING */
+ /* Copy the String CID from the returned object */
+ strcpy(next_id_string, cid_objects[i]->string.pointer);
+ length = cid_objects[i]->string.length + 1;
+ }
+
+ cid_list->ids[i].string = next_id_string;
+ cid_list->ids[i].length = length;
+ next_id_string += length;
+ }
+
+ /* Finish the CID list */
+
+ cid_list->count = count;
+ cid_list->list_size = cid_list_size;
+ *return_cid_list = cid_list;
+
+cleanup:
+
+ /* On exit, we must delete the _CID return object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_execute_CLS
+ *
+ * PARAMETERS: device_node - Node for the device
+ * return_id - Where the _CLS is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Executes the _CLS control method that returns PCI-defined
+ * class code of the device. The _CLS value is always a package
+ * containing PCI class information as a list of integers.
+ * The returned string has format "BBSSPP", where:
+ * BB = Base-class code
+ * SS = Sub-class code
+ * PP = Programming Interface code
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_execute_CLS(struct acpi_namespace_node *device_node,
+ struct acpi_pnp_device_id **return_id)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object **cls_objects;
+ u32 count;
+ struct acpi_pnp_device_id *cls;
+ u32 length;
+ acpi_status status;
+ u8 class_code[3] = { 0, 0, 0 };
+
+ ACPI_FUNCTION_TRACE(ut_execute_CLS);
+
+ status = acpi_ut_evaluate_object(device_node, METHOD_NAME__CLS,
+ ACPI_BTYPE_PACKAGE, &obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Get the size of the String to be returned, includes null terminator */
+
+ length = ACPI_PCICLS_STRING_SIZE;
+ cls_objects = obj_desc->package.elements;
+ count = obj_desc->package.count;
+
+ if (obj_desc->common.type == ACPI_TYPE_PACKAGE) {
+ if (count > 0
+ && cls_objects[0]->common.type == ACPI_TYPE_INTEGER) {
+ class_code[0] = (u8)cls_objects[0]->integer.value;
+ }
+ if (count > 1
+ && cls_objects[1]->common.type == ACPI_TYPE_INTEGER) {
+ class_code[1] = (u8)cls_objects[1]->integer.value;
+ }
+ if (count > 2
+ && cls_objects[2]->common.type == ACPI_TYPE_INTEGER) {
+ class_code[2] = (u8)cls_objects[2]->integer.value;
+ }
+ }
+
+ /* Allocate a buffer for the CLS */
+
+ cls =
+ ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pnp_device_id) +
+ (acpi_size)length);
+ if (!cls) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Area for the string starts after PNP_DEVICE_ID struct */
+
+ cls->string =
+ ACPI_ADD_PTR(char, cls, sizeof(struct acpi_pnp_device_id));
+
+ /* Simply copy existing string */
+
+ acpi_ex_pci_cls_to_string(cls->string, class_code);
+ cls->length = length;
+ *return_id = cls;
+
+cleanup:
+
+ /* On exit, we must delete the return object */
+
+ acpi_ut_remove_reference(obj_desc);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c
new file mode 100644
index 000000000..18177e4f2
--- /dev/null
+++ b/drivers/acpi/acpica/utinit.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utinit - Common ACPI subsystem initialization
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acevents.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utinit")
+
+/* Local prototypes */
+static void acpi_ut_terminate(void);
+
+#if (!ACPI_REDUCED_HARDWARE)
+
+static void acpi_ut_free_gpe_lists(void);
+
+#else
+
+#define acpi_ut_free_gpe_lists()
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+#if (!ACPI_REDUCED_HARDWARE)
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ut_free_gpe_lists
+ *
+ * PARAMETERS: none
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: Free global GPE lists
+ *
+ ******************************************************************************/
+
+static void acpi_ut_free_gpe_lists(void)
+{
+ struct acpi_gpe_block_info *gpe_block;
+ struct acpi_gpe_block_info *next_gpe_block;
+ struct acpi_gpe_xrupt_info *gpe_xrupt_info;
+ struct acpi_gpe_xrupt_info *next_gpe_xrupt_info;
+
+ /* Free global GPE blocks and related info structures */
+
+ gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head;
+ while (gpe_xrupt_info) {
+ gpe_block = gpe_xrupt_info->gpe_block_list_head;
+ while (gpe_block) {
+ next_gpe_block = gpe_block->next;
+ ACPI_FREE(gpe_block->event_info);
+ ACPI_FREE(gpe_block->register_info);
+ ACPI_FREE(gpe_block);
+
+ gpe_block = next_gpe_block;
+ }
+ next_gpe_xrupt_info = gpe_xrupt_info->next;
+ ACPI_FREE(gpe_xrupt_info);
+ gpe_xrupt_info = next_gpe_xrupt_info;
+ }
+}
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_init_globals
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize ACPICA globals. All globals that require specific
+ * initialization should be initialized here. This allows for
+ * a warm restart.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_init_globals(void)
+{
+ acpi_status status;
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ut_init_globals);
+
+ /* Create all memory caches */
+
+ status = acpi_ut_create_caches();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Address Range lists */
+
+ for (i = 0; i < ACPI_ADDRESS_RANGE_MAX; i++) {
+ acpi_gbl_address_range_list[i] = NULL;
+ }
+
+ /* Mutex locked flags */
+
+ for (i = 0; i < ACPI_NUM_MUTEX; i++) {
+ acpi_gbl_mutex_info[i].mutex = NULL;
+ acpi_gbl_mutex_info[i].thread_id = ACPI_MUTEX_NOT_ACQUIRED;
+ acpi_gbl_mutex_info[i].use_count = 0;
+ }
+
+ for (i = 0; i < ACPI_NUM_OWNERID_MASKS; i++) {
+ acpi_gbl_owner_id_mask[i] = 0;
+ }
+
+ /* Last owner_ID is never valid */
+
+ acpi_gbl_owner_id_mask[ACPI_NUM_OWNERID_MASKS - 1] = 0x80000000;
+
+ /* Event counters */
+
+ acpi_method_count = 0;
+ acpi_sci_count = 0;
+ acpi_gpe_count = 0;
+
+ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) {
+ acpi_fixed_event_count[i] = 0;
+ }
+
+#if (!ACPI_REDUCED_HARDWARE)
+
+ /* GPE/SCI support */
+
+ acpi_gbl_all_gpes_initialized = FALSE;
+ acpi_gbl_gpe_xrupt_list_head = NULL;
+ acpi_gbl_gpe_fadt_blocks[0] = NULL;
+ acpi_gbl_gpe_fadt_blocks[1] = NULL;
+ acpi_current_gpe_count = 0;
+
+ acpi_gbl_global_event_handler = NULL;
+ acpi_gbl_sci_handler_list = NULL;
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+ /* Global handlers */
+
+ acpi_gbl_global_notify[0].handler = NULL;
+ acpi_gbl_global_notify[1].handler = NULL;
+ acpi_gbl_exception_handler = NULL;
+ acpi_gbl_init_handler = NULL;
+ acpi_gbl_table_handler = NULL;
+ acpi_gbl_interface_handler = NULL;
+
+ /* Global Lock support */
+
+ acpi_gbl_global_lock_semaphore = NULL;
+ acpi_gbl_global_lock_mutex = NULL;
+ acpi_gbl_global_lock_acquired = FALSE;
+ acpi_gbl_global_lock_handle = 0;
+ acpi_gbl_global_lock_present = FALSE;
+
+ /* Miscellaneous variables */
+
+ acpi_gbl_DSDT = NULL;
+ acpi_gbl_cm_single_step = FALSE;
+ acpi_gbl_shutdown = FALSE;
+ acpi_gbl_ns_lookup_count = 0;
+ acpi_gbl_ps_find_count = 0;
+ acpi_gbl_acpi_hardware_present = TRUE;
+ acpi_gbl_last_owner_id_index = 0;
+ acpi_gbl_next_owner_id_offset = 0;
+ acpi_gbl_debugger_configuration = DEBUGGER_THREADING;
+ acpi_gbl_osi_mutex = NULL;
+
+ /* Hardware oriented */
+
+ acpi_gbl_events_initialized = FALSE;
+ acpi_gbl_system_awake_and_running = TRUE;
+
+ /* Namespace */
+
+ acpi_gbl_root_node = NULL;
+ acpi_gbl_root_node_struct.name.integer = ACPI_ROOT_NAME;
+ acpi_gbl_root_node_struct.descriptor_type = ACPI_DESC_TYPE_NAMED;
+ acpi_gbl_root_node_struct.type = ACPI_TYPE_DEVICE;
+ acpi_gbl_root_node_struct.parent = NULL;
+ acpi_gbl_root_node_struct.child = NULL;
+ acpi_gbl_root_node_struct.peer = NULL;
+ acpi_gbl_root_node_struct.object = NULL;
+
+#ifdef ACPI_DISASSEMBLER
+ acpi_gbl_external_list = NULL;
+ acpi_gbl_num_external_methods = 0;
+ acpi_gbl_resolved_external_methods = 0;
+#endif
+
+#ifdef ACPI_DEBUG_OUTPUT
+ acpi_gbl_lowest_stack_pointer = ACPI_CAST_PTR(acpi_size, ACPI_SIZE_MAX);
+#endif
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ acpi_gbl_display_final_mem_stats = FALSE;
+ acpi_gbl_disable_mem_tracking = FALSE;
+#endif
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ut_terminate
+ *
+ * PARAMETERS: none
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: Free global memory
+ *
+ ******************************************************************************/
+
+static void acpi_ut_terminate(void)
+{
+ ACPI_FUNCTION_TRACE(ut_terminate);
+
+ acpi_ut_free_gpe_lists();
+ acpi_ut_delete_address_lists();
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_subsystem_shutdown
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Shutdown the various components. Do not delete the mutex
+ * objects here, because the AML debugger may be still running.
+ *
+ ******************************************************************************/
+
+void acpi_ut_subsystem_shutdown(void)
+{
+ ACPI_FUNCTION_TRACE(ut_subsystem_shutdown);
+
+ /* Just exit if subsystem is already shutdown */
+
+ if (acpi_gbl_shutdown) {
+ ACPI_ERROR((AE_INFO, "ACPI Subsystem is already terminated"));
+ return_VOID;
+ }
+
+ /* Subsystem appears active, go ahead and shut it down */
+
+ acpi_gbl_shutdown = TRUE;
+ acpi_gbl_startup_flags = 0;
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n"));
+
+#ifndef ACPI_ASL_COMPILER
+
+ /* Close the acpi_event Handling */
+
+ acpi_ev_terminate();
+
+ /* Delete any dynamic _OSI interfaces */
+
+ acpi_ut_interface_terminate();
+#endif
+
+ /* Close the Namespace */
+
+ acpi_ns_terminate();
+
+ /* Delete the ACPI tables */
+
+ acpi_tb_terminate();
+
+ /* Close the globals */
+
+ acpi_ut_terminate();
+
+ /* Purge the local caches */
+
+ (void)acpi_ut_delete_caches();
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/utlock.c b/drivers/acpi/acpica/utlock.c
new file mode 100644
index 000000000..84abdbf5c
--- /dev/null
+++ b/drivers/acpi/acpica/utlock.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utlock - Reader/Writer lock interfaces
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utlock")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_rw_lock
+ * acpi_ut_delete_rw_lock
+ *
+ * PARAMETERS: lock - Pointer to a valid RW lock
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Reader/writer lock creation and deletion interfaces.
+ *
+ ******************************************************************************/
+acpi_status acpi_ut_create_rw_lock(struct acpi_rw_lock *lock)
+{
+ acpi_status status;
+
+ lock->num_readers = 0;
+ status = acpi_os_create_mutex(&lock->reader_mutex);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status = acpi_os_create_mutex(&lock->writer_mutex);
+ return (status);
+}
+
+void acpi_ut_delete_rw_lock(struct acpi_rw_lock *lock)
+{
+
+ acpi_os_delete_mutex(lock->reader_mutex);
+ acpi_os_delete_mutex(lock->writer_mutex);
+
+ lock->num_readers = 0;
+ lock->reader_mutex = NULL;
+ lock->writer_mutex = NULL;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_acquire_read_lock
+ * acpi_ut_release_read_lock
+ *
+ * PARAMETERS: lock - Pointer to a valid RW lock
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Reader interfaces for reader/writer locks. On acquisition,
+ * only the first reader acquires the write mutex. On release,
+ * only the last reader releases the write mutex. Although this
+ * algorithm can in theory starve writers, this should not be a
+ * problem with ACPICA since the subsystem is infrequently used
+ * in comparison to (for example) an I/O system.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_acquire_read_lock(struct acpi_rw_lock *lock)
+{
+ acpi_status status;
+
+ status = acpi_os_acquire_mutex(lock->reader_mutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Acquire the write lock only for the first reader */
+
+ lock->num_readers++;
+ if (lock->num_readers == 1) {
+ status =
+ acpi_os_acquire_mutex(lock->writer_mutex,
+ ACPI_WAIT_FOREVER);
+ }
+
+ acpi_os_release_mutex(lock->reader_mutex);
+ return (status);
+}
+
+acpi_status acpi_ut_release_read_lock(struct acpi_rw_lock *lock)
+{
+ acpi_status status;
+
+ status = acpi_os_acquire_mutex(lock->reader_mutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Release the write lock only for the very last reader */
+
+ lock->num_readers--;
+ if (lock->num_readers == 0) {
+ acpi_os_release_mutex(lock->writer_mutex);
+ }
+
+ acpi_os_release_mutex(lock->reader_mutex);
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_acquire_write_lock
+ * acpi_ut_release_write_lock
+ *
+ * PARAMETERS: lock - Pointer to a valid RW lock
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Writer interfaces for reader/writer locks. Simply acquire or
+ * release the writer mutex associated with the lock. Acquisition
+ * of the lock is fully exclusive and will block all readers and
+ * writers until it is released.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_acquire_write_lock(struct acpi_rw_lock *lock)
+{
+ acpi_status status;
+
+ status = acpi_os_acquire_mutex(lock->writer_mutex, ACPI_WAIT_FOREVER);
+ return (status);
+}
+
+void acpi_ut_release_write_lock(struct acpi_rw_lock *lock)
+{
+
+ acpi_os_release_mutex(lock->writer_mutex);
+}
diff --git a/drivers/acpi/acpica/utmath.c b/drivers/acpi/acpica/utmath.c
new file mode 100644
index 000000000..2c2c6bc1f
--- /dev/null
+++ b/drivers/acpi/acpica/utmath.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utmath - Integer math support routines
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utmath")
+
+/* Structures used only for 64-bit divide */
+typedef struct uint64_struct {
+ u32 lo;
+ u32 hi;
+
+} uint64_struct;
+
+typedef union uint64_overlay {
+ u64 full;
+ struct uint64_struct part;
+
+} uint64_overlay;
+
+/*
+ * Optional support for 64-bit double-precision integer multiply and shift.
+ * This code is configurable and is implemented in order to support 32-bit
+ * kernel environments where a 64-bit double-precision math library is not
+ * available.
+ */
+#ifndef ACPI_USE_NATIVE_MATH64
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_short_multiply
+ *
+ * PARAMETERS: multiplicand - 64-bit multiplicand
+ * multiplier - 32-bit multiplier
+ * out_product - Pointer to where the product is returned
+ *
+ * DESCRIPTION: Perform a short multiply.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_short_multiply(u64 multiplicand, u32 multiplier, u64 *out_product)
+{
+ union uint64_overlay multiplicand_ovl;
+ union uint64_overlay product;
+ u32 carry32;
+
+ ACPI_FUNCTION_TRACE(ut_short_multiply);
+
+ multiplicand_ovl.full = multiplicand;
+
+ /*
+ * The Product is 64 bits, the carry is always 32 bits,
+ * and is generated by the second multiply.
+ */
+ ACPI_MUL_64_BY_32(0, multiplicand_ovl.part.hi, multiplier,
+ product.part.hi, carry32);
+
+ ACPI_MUL_64_BY_32(0, multiplicand_ovl.part.lo, multiplier,
+ product.part.lo, carry32);
+
+ product.part.hi += carry32;
+
+ /* Return only what was requested */
+
+ if (out_product) {
+ *out_product = product.full;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_short_shift_left
+ *
+ * PARAMETERS: operand - 64-bit shift operand
+ * count - 32-bit shift count
+ * out_result - Pointer to where the result is returned
+ *
+ * DESCRIPTION: Perform a short left shift.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_short_shift_left(u64 operand, u32 count, u64 *out_result)
+{
+ union uint64_overlay operand_ovl;
+
+ ACPI_FUNCTION_TRACE(ut_short_shift_left);
+
+ operand_ovl.full = operand;
+
+ if ((count & 63) >= 32) {
+ operand_ovl.part.hi = operand_ovl.part.lo;
+ operand_ovl.part.lo = 0;
+ count = (count & 63) - 32;
+ }
+ ACPI_SHIFT_LEFT_64_BY_32(operand_ovl.part.hi,
+ operand_ovl.part.lo, count);
+
+ /* Return only what was requested */
+
+ if (out_result) {
+ *out_result = operand_ovl.full;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_short_shift_right
+ *
+ * PARAMETERS: operand - 64-bit shift operand
+ * count - 32-bit shift count
+ * out_result - Pointer to where the result is returned
+ *
+ * DESCRIPTION: Perform a short right shift.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_short_shift_right(u64 operand, u32 count, u64 *out_result)
+{
+ union uint64_overlay operand_ovl;
+
+ ACPI_FUNCTION_TRACE(ut_short_shift_right);
+
+ operand_ovl.full = operand;
+
+ if ((count & 63) >= 32) {
+ operand_ovl.part.lo = operand_ovl.part.hi;
+ operand_ovl.part.hi = 0;
+ count = (count & 63) - 32;
+ }
+ ACPI_SHIFT_RIGHT_64_BY_32(operand_ovl.part.hi,
+ operand_ovl.part.lo, count);
+
+ /* Return only what was requested */
+
+ if (out_result) {
+ *out_result = operand_ovl.full;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+#else
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_short_multiply
+ *
+ * PARAMETERS: See function headers above
+ *
+ * DESCRIPTION: Native version of the ut_short_multiply function.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_short_multiply(u64 multiplicand, u32 multiplier, u64 *out_product)
+{
+
+ ACPI_FUNCTION_TRACE(ut_short_multiply);
+
+ /* Return only what was requested */
+
+ if (out_product) {
+ *out_product = multiplicand * multiplier;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_short_shift_left
+ *
+ * PARAMETERS: See function headers above
+ *
+ * DESCRIPTION: Native version of the ut_short_shift_left function.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_short_shift_left(u64 operand, u32 count, u64 *out_result)
+{
+
+ ACPI_FUNCTION_TRACE(ut_short_shift_left);
+
+ /* Return only what was requested */
+
+ if (out_result) {
+ *out_result = operand << count;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_short_shift_right
+ *
+ * PARAMETERS: See function headers above
+ *
+ * DESCRIPTION: Native version of the ut_short_shift_right function.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_short_shift_right(u64 operand, u32 count, u64 *out_result)
+{
+
+ ACPI_FUNCTION_TRACE(ut_short_shift_right);
+
+ /* Return only what was requested */
+
+ if (out_result) {
+ *out_result = operand >> count;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+#endif
+
+/*
+ * Optional support for 64-bit double-precision integer divide. This code
+ * is configurable and is implemented in order to support 32-bit kernel
+ * environments where a 64-bit double-precision math library is not available.
+ *
+ * Support for a more normal 64-bit divide/modulo (with check for a divide-
+ * by-zero) appears after this optional section of code.
+ */
+#ifndef ACPI_USE_NATIVE_DIVIDE
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_short_divide
+ *
+ * PARAMETERS: dividend - 64-bit dividend
+ * divisor - 32-bit divisor
+ * out_quotient - Pointer to where the quotient is returned
+ * out_remainder - Pointer to where the remainder is returned
+ *
+ * RETURN: Status (Checks for divide-by-zero)
+ *
+ * DESCRIPTION: Perform a short (maximum 64 bits divided by 32 bits)
+ * divide and modulo. The result is a 64-bit quotient and a
+ * 32-bit remainder.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_short_divide(u64 dividend,
+ u32 divisor, u64 *out_quotient, u32 *out_remainder)
+{
+ union uint64_overlay dividend_ovl;
+ union uint64_overlay quotient;
+ u32 remainder32;
+
+ ACPI_FUNCTION_TRACE(ut_short_divide);
+
+ /* Always check for a zero divisor */
+
+ if (divisor == 0) {
+ ACPI_ERROR((AE_INFO, "Divide by zero"));
+ return_ACPI_STATUS(AE_AML_DIVIDE_BY_ZERO);
+ }
+
+ dividend_ovl.full = dividend;
+
+ /*
+ * The quotient is 64 bits, the remainder is always 32 bits,
+ * and is generated by the second divide.
+ */
+ ACPI_DIV_64_BY_32(0, dividend_ovl.part.hi, divisor,
+ quotient.part.hi, remainder32);
+
+ ACPI_DIV_64_BY_32(remainder32, dividend_ovl.part.lo, divisor,
+ quotient.part.lo, remainder32);
+
+ /* Return only what was requested */
+
+ if (out_quotient) {
+ *out_quotient = quotient.full;
+ }
+ if (out_remainder) {
+ *out_remainder = remainder32;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_divide
+ *
+ * PARAMETERS: in_dividend - Dividend
+ * in_divisor - Divisor
+ * out_quotient - Pointer to where the quotient is returned
+ * out_remainder - Pointer to where the remainder is returned
+ *
+ * RETURN: Status (Checks for divide-by-zero)
+ *
+ * DESCRIPTION: Perform a divide and modulo.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_divide(u64 in_dividend,
+ u64 in_divisor, u64 *out_quotient, u64 *out_remainder)
+{
+ union uint64_overlay dividend;
+ union uint64_overlay divisor;
+ union uint64_overlay quotient;
+ union uint64_overlay remainder;
+ union uint64_overlay normalized_dividend;
+ union uint64_overlay normalized_divisor;
+ u32 partial1;
+ union uint64_overlay partial2;
+ union uint64_overlay partial3;
+
+ ACPI_FUNCTION_TRACE(ut_divide);
+
+ /* Always check for a zero divisor */
+
+ if (in_divisor == 0) {
+ ACPI_ERROR((AE_INFO, "Divide by zero"));
+ return_ACPI_STATUS(AE_AML_DIVIDE_BY_ZERO);
+ }
+
+ divisor.full = in_divisor;
+ dividend.full = in_dividend;
+ if (divisor.part.hi == 0) {
+ /*
+ * 1) Simplest case is where the divisor is 32 bits, we can
+ * just do two divides
+ */
+ remainder.part.hi = 0;
+
+ /*
+ * The quotient is 64 bits, the remainder is always 32 bits,
+ * and is generated by the second divide.
+ */
+ ACPI_DIV_64_BY_32(0, dividend.part.hi, divisor.part.lo,
+ quotient.part.hi, partial1);
+
+ ACPI_DIV_64_BY_32(partial1, dividend.part.lo, divisor.part.lo,
+ quotient.part.lo, remainder.part.lo);
+ }
+
+ else {
+ /*
+ * 2) The general case where the divisor is a full 64 bits
+ * is more difficult
+ */
+ quotient.part.hi = 0;
+ normalized_dividend = dividend;
+ normalized_divisor = divisor;
+
+ /* Normalize the operands (shift until the divisor is < 32 bits) */
+
+ do {
+ ACPI_SHIFT_RIGHT_64(normalized_divisor.part.hi,
+ normalized_divisor.part.lo);
+ ACPI_SHIFT_RIGHT_64(normalized_dividend.part.hi,
+ normalized_dividend.part.lo);
+
+ } while (normalized_divisor.part.hi != 0);
+
+ /* Partial divide */
+
+ ACPI_DIV_64_BY_32(normalized_dividend.part.hi,
+ normalized_dividend.part.lo,
+ normalized_divisor.part.lo, quotient.part.lo,
+ partial1);
+
+ /*
+ * The quotient is always 32 bits, and simply requires
+ * adjustment. The 64-bit remainder must be generated.
+ */
+ partial1 = quotient.part.lo * divisor.part.hi;
+ partial2.full = (u64) quotient.part.lo * divisor.part.lo;
+ partial3.full = (u64) partial2.part.hi + partial1;
+
+ remainder.part.hi = partial3.part.lo;
+ remainder.part.lo = partial2.part.lo;
+
+ if (partial3.part.hi == 0) {
+ if (partial3.part.lo >= dividend.part.hi) {
+ if (partial3.part.lo == dividend.part.hi) {
+ if (partial2.part.lo > dividend.part.lo) {
+ quotient.part.lo--;
+ remainder.full -= divisor.full;
+ }
+ } else {
+ quotient.part.lo--;
+ remainder.full -= divisor.full;
+ }
+ }
+
+ remainder.full = remainder.full - dividend.full;
+ remainder.part.hi = (u32)-((s32)remainder.part.hi);
+ remainder.part.lo = (u32)-((s32)remainder.part.lo);
+
+ if (remainder.part.lo) {
+ remainder.part.hi--;
+ }
+ }
+ }
+
+ /* Return only what was requested */
+
+ if (out_quotient) {
+ *out_quotient = quotient.full;
+ }
+ if (out_remainder) {
+ *out_remainder = remainder.full;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+#else
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_short_divide, acpi_ut_divide
+ *
+ * PARAMETERS: See function headers above
+ *
+ * DESCRIPTION: Native versions of the ut_divide functions. Use these if either
+ * 1) The target is a 64-bit platform and therefore 64-bit
+ * integer math is supported directly by the machine.
+ * 2) The target is a 32-bit or 16-bit platform, and the
+ * double-precision integer math library is available to
+ * perform the divide.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_short_divide(u64 in_dividend,
+ u32 divisor, u64 *out_quotient, u32 *out_remainder)
+{
+
+ ACPI_FUNCTION_TRACE(ut_short_divide);
+
+ /* Always check for a zero divisor */
+
+ if (divisor == 0) {
+ ACPI_ERROR((AE_INFO, "Divide by zero"));
+ return_ACPI_STATUS(AE_AML_DIVIDE_BY_ZERO);
+ }
+
+ /* Return only what was requested */
+
+ if (out_quotient) {
+ *out_quotient = in_dividend / divisor;
+ }
+ if (out_remainder) {
+ *out_remainder = (u32) (in_dividend % divisor);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+acpi_status
+acpi_ut_divide(u64 in_dividend,
+ u64 in_divisor, u64 *out_quotient, u64 *out_remainder)
+{
+ ACPI_FUNCTION_TRACE(ut_divide);
+
+ /* Always check for a zero divisor */
+
+ if (in_divisor == 0) {
+ ACPI_ERROR((AE_INFO, "Divide by zero"));
+ return_ACPI_STATUS(AE_AML_DIVIDE_BY_ZERO);
+ }
+
+ /* Return only what was requested */
+
+ if (out_quotient) {
+ *out_quotient = in_dividend / in_divisor;
+ }
+ if (out_remainder) {
+ *out_remainder = in_dividend % in_divisor;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+#endif
diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c
new file mode 100644
index 000000000..8638efacd
--- /dev/null
+++ b/drivers/acpi/acpica/utmisc.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utmisc - common utility procedures
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utmisc")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_is_pci_root_bridge
+ *
+ * PARAMETERS: id - The HID/CID in string format
+ *
+ * RETURN: TRUE if the Id is a match for a PCI/PCI-Express Root Bridge
+ *
+ * DESCRIPTION: Determine if the input ID is a PCI Root Bridge ID.
+ *
+ ******************************************************************************/
+u8 acpi_ut_is_pci_root_bridge(char *id)
+{
+
+ /*
+ * Check if this is a PCI root bridge.
+ * ACPI 3.0+: check for a PCI Express root also.
+ */
+ if (!(strcmp(id,
+ PCI_ROOT_HID_STRING)) ||
+ !(strcmp(id, PCI_EXPRESS_ROOT_HID_STRING))) {
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_NAMES_APP)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_is_aml_table
+ *
+ * PARAMETERS: table - An ACPI table
+ *
+ * RETURN: TRUE if table contains executable AML; FALSE otherwise
+ *
+ * DESCRIPTION: Check ACPI Signature for a table that contains AML code.
+ * Currently, these are DSDT,SSDT,PSDT. All other table types are
+ * data tables that do not contain AML code.
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_is_aml_table(struct acpi_table_header *table)
+{
+
+ /* These are the only tables that contain executable AML */
+
+ if (ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_DSDT) ||
+ ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_PSDT) ||
+ ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_SSDT) ||
+ ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_OSDT) ||
+ ACPI_IS_OEM_SIG(table->signature)) {
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_dword_byte_swap
+ *
+ * PARAMETERS: value - Value to be converted
+ *
+ * RETURN: u32 integer with bytes swapped
+ *
+ * DESCRIPTION: Convert a 32-bit value to big-endian (swap the bytes)
+ *
+ ******************************************************************************/
+
+u32 acpi_ut_dword_byte_swap(u32 value)
+{
+ union {
+ u32 value;
+ u8 bytes[4];
+ } out;
+ union {
+ u32 value;
+ u8 bytes[4];
+ } in;
+
+ ACPI_FUNCTION_ENTRY();
+
+ in.value = value;
+
+ out.bytes[0] = in.bytes[3];
+ out.bytes[1] = in.bytes[2];
+ out.bytes[2] = in.bytes[1];
+ out.bytes[3] = in.bytes[0];
+
+ return (out.value);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_set_integer_width
+ *
+ * PARAMETERS: Revision From DSDT header
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Set the global integer bit width based upon the revision
+ * of the DSDT. For Revision 1 and 0, Integers are 32 bits.
+ * For Revision 2 and above, Integers are 64 bits. Yes, this
+ * makes a difference.
+ *
+ ******************************************************************************/
+
+void acpi_ut_set_integer_width(u8 revision)
+{
+
+ if (revision < 2) {
+
+ /* 32-bit case */
+
+ acpi_gbl_integer_bit_width = 32;
+ acpi_gbl_integer_nybble_width = 8;
+ acpi_gbl_integer_byte_width = 4;
+ } else {
+ /* 64-bit case (ACPI 2.0+) */
+
+ acpi_gbl_integer_bit_width = 64;
+ acpi_gbl_integer_nybble_width = 16;
+ acpi_gbl_integer_byte_width = 8;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_update_state_and_push
+ *
+ * PARAMETERS: object - Object to be added to the new state
+ * action - Increment/Decrement
+ * state_list - List the state will be added to
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a new state and push it
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_create_update_state_and_push(union acpi_operand_object *object,
+ u16 action,
+ union acpi_generic_state **state_list)
+{
+ union acpi_generic_state *state;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Ignore null objects; these are expected */
+
+ if (!object) {
+ return (AE_OK);
+ }
+
+ state = acpi_ut_create_update_state(object, action);
+ if (!state) {
+ return (AE_NO_MEMORY);
+ }
+
+ acpi_ut_push_generic_state(state_list, state);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_walk_package_tree
+ *
+ * PARAMETERS: source_object - The package to walk
+ * target_object - Target object (if package is being copied)
+ * walk_callback - Called once for each package element
+ * context - Passed to the callback function
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Walk through a package, including subpackages
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_walk_package_tree(union acpi_operand_object *source_object,
+ void *target_object,
+ acpi_pkg_callback walk_callback, void *context)
+{
+ acpi_status status = AE_OK;
+ union acpi_generic_state *state_list = NULL;
+ union acpi_generic_state *state;
+ union acpi_operand_object *this_source_obj;
+ u32 this_index;
+
+ ACPI_FUNCTION_TRACE(ut_walk_package_tree);
+
+ state = acpi_ut_create_pkg_state(source_object, target_object, 0);
+ if (!state) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ while (state) {
+
+ /* Get one element of the package */
+
+ this_index = state->pkg.index;
+ this_source_obj =
+ state->pkg.source_object->package.elements[this_index];
+ state->pkg.this_target_obj =
+ &state->pkg.source_object->package.elements[this_index];
+
+ /*
+ * Check for:
+ * 1) An uninitialized package element. It is completely
+ * legal to declare a package and leave it uninitialized
+ * 2) Not an internal object - can be a namespace node instead
+ * 3) Any type other than a package. Packages are handled in else
+ * case below.
+ */
+ if ((!this_source_obj) ||
+ (ACPI_GET_DESCRIPTOR_TYPE(this_source_obj) !=
+ ACPI_DESC_TYPE_OPERAND) ||
+ (this_source_obj->common.type != ACPI_TYPE_PACKAGE)) {
+ status =
+ walk_callback(ACPI_COPY_TYPE_SIMPLE,
+ this_source_obj, state, context);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ state->pkg.index++;
+ while (state->pkg.index >=
+ state->pkg.source_object->package.count) {
+ /*
+ * We've handled all of the objects at this level, This means
+ * that we have just completed a package. That package may
+ * have contained one or more packages itself.
+ *
+ * Delete this state and pop the previous state (package).
+ */
+ acpi_ut_delete_generic_state(state);
+ state = acpi_ut_pop_generic_state(&state_list);
+
+ /* Finished when there are no more states */
+
+ if (!state) {
+ /*
+ * We have handled all of the objects in the top level
+ * package just add the length of the package objects
+ * and exit
+ */
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Go back up a level and move the index past the just
+ * completed package object.
+ */
+ state->pkg.index++;
+ }
+ } else {
+ /* This is a subobject of type package */
+
+ status =
+ walk_callback(ACPI_COPY_TYPE_PACKAGE,
+ this_source_obj, state, context);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Push the current state and create a new one
+ * The callback above returned a new target package object.
+ */
+ acpi_ut_push_generic_state(&state_list, state);
+ state =
+ acpi_ut_create_pkg_state(this_source_obj,
+ state->pkg.this_target_obj,
+ 0);
+ if (!state) {
+
+ /* Free any stacked Update State objects */
+
+ while (state_list) {
+ state =
+ acpi_ut_pop_generic_state
+ (&state_list);
+ acpi_ut_delete_generic_state(state);
+ }
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+ }
+ }
+
+ /* We should never get here */
+
+ ACPI_ERROR((AE_INFO, "State list did not terminate correctly"));
+
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+}
+
+#ifdef ACPI_DEBUG_OUTPUT
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_display_init_pathname
+ *
+ * PARAMETERS: type - Object type of the node
+ * obj_handle - Handle whose pathname will be displayed
+ * path - Additional path string to be appended.
+ * (NULL if no extra path)
+ *
+ * RETURN: acpi_status
+ *
+ * DESCRIPTION: Display full pathname of an object, DEBUG ONLY
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_display_init_pathname(u8 type,
+ struct acpi_namespace_node *obj_handle,
+ const char *path)
+{
+ acpi_status status;
+ struct acpi_buffer buffer;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Only print the path if the appropriate debug level is enabled */
+
+ if (!(acpi_dbg_level & ACPI_LV_INIT_NAMES)) {
+ return;
+ }
+
+ /* Get the full pathname to the node */
+
+ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+
+ /* Print what we're doing */
+
+ switch (type) {
+ case ACPI_TYPE_METHOD:
+
+ acpi_os_printf("Executing ");
+ break;
+
+ default:
+
+ acpi_os_printf("Initializing ");
+ break;
+ }
+
+ /* Print the object type and pathname */
+
+ acpi_os_printf("%-12s %s",
+ acpi_ut_get_type_name(type), (char *)buffer.pointer);
+
+ /* Extra path is used to append names like _STA, _INI, etc. */
+
+ if (path) {
+ acpi_os_printf(".%s", path);
+ }
+ acpi_os_printf("\n");
+
+ ACPI_FREE(buffer.pointer);
+}
+#endif
diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c
new file mode 100644
index 000000000..2e465e6a0
--- /dev/null
+++ b/drivers/acpi/acpica/utmutex.c
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utmutex - local mutex support
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utmutex")
+
+/* Local prototypes */
+static acpi_status acpi_ut_create_mutex(acpi_mutex_handle mutex_id);
+
+static void acpi_ut_delete_mutex(acpi_mutex_handle mutex_id);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_mutex_initialize
+ *
+ * PARAMETERS: None.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create the system mutex objects. This includes mutexes,
+ * spin locks, and reader/writer locks.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_mutex_initialize(void)
+{
+ u32 i;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ut_mutex_initialize);
+
+ /* Create each of the predefined mutex objects */
+
+ for (i = 0; i < ACPI_NUM_MUTEX; i++) {
+ status = acpi_ut_create_mutex(i);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* Create the spinlocks for use at interrupt level or for speed */
+
+ status = acpi_os_create_lock (&acpi_gbl_gpe_lock);
+ if (ACPI_FAILURE (status)) {
+ return_ACPI_STATUS (status);
+ }
+
+ status = acpi_os_create_raw_lock(&acpi_gbl_hardware_lock);
+ if (ACPI_FAILURE (status)) {
+ return_ACPI_STATUS (status);
+ }
+
+ status = acpi_os_create_lock(&acpi_gbl_reference_count_lock);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Mutex for _OSI support */
+
+ status = acpi_os_create_mutex(&acpi_gbl_osi_mutex);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Create the reader/writer lock for namespace access */
+
+ status = acpi_ut_create_rw_lock(&acpi_gbl_namespace_rw_lock);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_mutex_terminate
+ *
+ * PARAMETERS: None.
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Delete all of the system mutex objects. This includes mutexes,
+ * spin locks, and reader/writer locks.
+ *
+ ******************************************************************************/
+
+void acpi_ut_mutex_terminate(void)
+{
+ u32 i;
+
+ ACPI_FUNCTION_TRACE(ut_mutex_terminate);
+
+ /* Delete each predefined mutex object */
+
+ for (i = 0; i < ACPI_NUM_MUTEX; i++) {
+ acpi_ut_delete_mutex(i);
+ }
+
+ acpi_os_delete_mutex(acpi_gbl_osi_mutex);
+
+ /* Delete the spinlocks */
+
+ acpi_os_delete_lock(acpi_gbl_gpe_lock);
+ acpi_os_delete_raw_lock(acpi_gbl_hardware_lock);
+ acpi_os_delete_lock(acpi_gbl_reference_count_lock);
+
+ /* Delete the reader/writer lock */
+
+ acpi_ut_delete_rw_lock(&acpi_gbl_namespace_rw_lock);
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_mutex
+ *
+ * PARAMETERS: mutex_ID - ID of the mutex to be created
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a mutex object.
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_ut_create_mutex(acpi_mutex_handle mutex_id)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_U32(ut_create_mutex, mutex_id);
+
+ if (!acpi_gbl_mutex_info[mutex_id].mutex) {
+ status =
+ acpi_os_create_mutex(&acpi_gbl_mutex_info[mutex_id].mutex);
+ acpi_gbl_mutex_info[mutex_id].thread_id =
+ ACPI_MUTEX_NOT_ACQUIRED;
+ acpi_gbl_mutex_info[mutex_id].use_count = 0;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_delete_mutex
+ *
+ * PARAMETERS: mutex_ID - ID of the mutex to be deleted
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Delete a mutex object.
+ *
+ ******************************************************************************/
+
+static void acpi_ut_delete_mutex(acpi_mutex_handle mutex_id)
+{
+
+ ACPI_FUNCTION_TRACE_U32(ut_delete_mutex, mutex_id);
+
+ acpi_os_delete_mutex(acpi_gbl_mutex_info[mutex_id].mutex);
+
+ acpi_gbl_mutex_info[mutex_id].mutex = NULL;
+ acpi_gbl_mutex_info[mutex_id].thread_id = ACPI_MUTEX_NOT_ACQUIRED;
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_acquire_mutex
+ *
+ * PARAMETERS: mutex_ID - ID of the mutex to be acquired
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Acquire a mutex object.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id)
+{
+ acpi_status status;
+ acpi_thread_id this_thread_id;
+
+ ACPI_FUNCTION_NAME(ut_acquire_mutex);
+
+ if (mutex_id > ACPI_MAX_MUTEX) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ this_thread_id = acpi_os_get_thread_id();
+
+#ifdef ACPI_MUTEX_DEBUG
+ {
+ u32 i;
+ /*
+ * Mutex debug code, for internal debugging only.
+ *
+ * Deadlock prevention. Check if this thread owns any mutexes of value
+ * greater than or equal to this one. If so, the thread has violated
+ * the mutex ordering rule. This indicates a coding error somewhere in
+ * the ACPI subsystem code.
+ */
+ for (i = mutex_id; i < ACPI_NUM_MUTEX; i++) {
+ if (acpi_gbl_mutex_info[i].thread_id == this_thread_id) {
+ if (i == mutex_id) {
+ ACPI_ERROR((AE_INFO,
+ "Mutex [%s] already acquired by this thread [%u]",
+ acpi_ut_get_mutex_name
+ (mutex_id),
+ (u32)this_thread_id));
+
+ return (AE_ALREADY_ACQUIRED);
+ }
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid acquire order: Thread %u owns [%s], wants [%s]",
+ (u32)this_thread_id,
+ acpi_ut_get_mutex_name(i),
+ acpi_ut_get_mutex_name(mutex_id)));
+
+ return (AE_ACQUIRE_DEADLOCK);
+ }
+ }
+ }
+#endif
+
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
+ "Thread %u attempting to acquire Mutex [%s]\n",
+ (u32)this_thread_id,
+ acpi_ut_get_mutex_name(mutex_id)));
+
+ status =
+ acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex,
+ ACPI_WAIT_FOREVER);
+ if (ACPI_SUCCESS(status)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
+ "Thread %u acquired Mutex [%s]\n",
+ (u32)this_thread_id,
+ acpi_ut_get_mutex_name(mutex_id)));
+
+ acpi_gbl_mutex_info[mutex_id].use_count++;
+ acpi_gbl_mutex_info[mutex_id].thread_id = this_thread_id;
+ } else {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Thread %u could not acquire Mutex [%s] (0x%X)",
+ (u32)this_thread_id,
+ acpi_ut_get_mutex_name(mutex_id), mutex_id));
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_release_mutex
+ *
+ * PARAMETERS: mutex_ID - ID of the mutex to be released
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Release a mutex object.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id)
+{
+ ACPI_FUNCTION_NAME(ut_release_mutex);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %u releasing Mutex [%s]\n",
+ (u32)acpi_os_get_thread_id(),
+ acpi_ut_get_mutex_name(mutex_id)));
+
+ if (mutex_id > ACPI_MAX_MUTEX) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /*
+ * Mutex must be acquired in order to release it!
+ */
+ if (acpi_gbl_mutex_info[mutex_id].thread_id == ACPI_MUTEX_NOT_ACQUIRED) {
+ ACPI_ERROR((AE_INFO,
+ "Mutex [%s] (0x%X) is not acquired, cannot release",
+ acpi_ut_get_mutex_name(mutex_id), mutex_id));
+
+ return (AE_NOT_ACQUIRED);
+ }
+#ifdef ACPI_MUTEX_DEBUG
+ {
+ u32 i;
+ /*
+ * Mutex debug code, for internal debugging only.
+ *
+ * Deadlock prevention. Check if this thread owns any mutexes of value
+ * greater than this one. If so, the thread has violated the mutex
+ * ordering rule. This indicates a coding error somewhere in
+ * the ACPI subsystem code.
+ */
+ for (i = mutex_id; i < ACPI_NUM_MUTEX; i++) {
+ if (acpi_gbl_mutex_info[i].thread_id ==
+ acpi_os_get_thread_id()) {
+ if (i == mutex_id) {
+ continue;
+ }
+
+ ACPI_ERROR((AE_INFO,
+ "Invalid release order: owns [%s], releasing [%s]",
+ acpi_ut_get_mutex_name(i),
+ acpi_ut_get_mutex_name(mutex_id)));
+
+ return (AE_RELEASE_DEADLOCK);
+ }
+ }
+ }
+#endif
+
+ /* Mark unlocked FIRST */
+
+ acpi_gbl_mutex_info[mutex_id].thread_id = ACPI_MUTEX_NOT_ACQUIRED;
+
+ acpi_os_release_mutex(acpi_gbl_mutex_info[mutex_id].mutex);
+ return (AE_OK);
+}
diff --git a/drivers/acpi/acpica/utnonansi.c b/drivers/acpi/acpica/utnonansi.c
new file mode 100644
index 000000000..ff0802ace
--- /dev/null
+++ b/drivers/acpi/acpica/utnonansi.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utnonansi - Non-ansi C library functions
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utnonansi")
+
+/*
+ * Non-ANSI C library functions - strlwr, strupr, stricmp, and "safe"
+ * string functions.
+ */
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_strlwr (strlwr)
+ *
+ * PARAMETERS: src_string - The source string to convert
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Convert a string to lowercase
+ *
+ ******************************************************************************/
+void acpi_ut_strlwr(char *src_string)
+{
+ char *string;
+
+ ACPI_FUNCTION_ENTRY();
+
+ if (!src_string) {
+ return;
+ }
+
+ /* Walk entire string, lowercasing the letters */
+
+ for (string = src_string; *string; string++) {
+ *string = (char)tolower((int)*string);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_strupr (strupr)
+ *
+ * PARAMETERS: src_string - The source string to convert
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Convert a string to uppercase
+ *
+ ******************************************************************************/
+
+void acpi_ut_strupr(char *src_string)
+{
+ char *string;
+
+ ACPI_FUNCTION_ENTRY();
+
+ if (!src_string) {
+ return;
+ }
+
+ /* Walk entire string, uppercasing the letters */
+
+ for (string = src_string; *string; string++) {
+ *string = (char)toupper((int)*string);
+ }
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ut_stricmp (stricmp)
+ *
+ * PARAMETERS: string1 - first string to compare
+ * string2 - second string to compare
+ *
+ * RETURN: int that signifies string relationship. Zero means strings
+ * are equal.
+ *
+ * DESCRIPTION: Case-insensitive string compare. Implementation of the
+ * non-ANSI stricmp function.
+ *
+ ******************************************************************************/
+
+int acpi_ut_stricmp(char *string1, char *string2)
+{
+ int c1;
+ int c2;
+
+ do {
+ c1 = tolower((int)*string1);
+ c2 = tolower((int)*string2);
+
+ string1++;
+ string2++;
+ }
+ while ((c1 == c2) && (c1));
+
+ return (c1 - c2);
+}
+
+#if defined (ACPI_DEBUGGER) || defined (ACPI_APPLICATION) || defined (ACPI_DEBUG_OUTPUT)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_safe_strcpy, acpi_ut_safe_strcat, acpi_ut_safe_strncat
+ *
+ * PARAMETERS: Adds a "DestSize" parameter to each of the standard string
+ * functions. This is the size of the Destination buffer.
+ *
+ * RETURN: TRUE if the operation would overflow the destination buffer.
+ *
+ * DESCRIPTION: Safe versions of standard Clib string functions. Ensure that
+ * the result of the operation will not overflow the output string
+ * buffer.
+ *
+ * NOTE: These functions are typically only helpful for processing
+ * user input and command lines. For most ACPICA code, the
+ * required buffer length is precisely calculated before buffer
+ * allocation, so the use of these functions is unnecessary.
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_safe_strcpy(char *dest, acpi_size dest_size, char *source)
+{
+
+ if (strlen(source) >= dest_size) {
+ return (TRUE);
+ }
+
+ strcpy(dest, source);
+ return (FALSE);
+}
+
+u8 acpi_ut_safe_strcat(char *dest, acpi_size dest_size, char *source)
+{
+
+ if ((strlen(dest) + strlen(source)) >= dest_size) {
+ return (TRUE);
+ }
+
+ strcat(dest, source);
+ return (FALSE);
+}
+
+u8
+acpi_ut_safe_strncat(char *dest,
+ acpi_size dest_size,
+ char *source, acpi_size max_transfer_length)
+{
+ acpi_size actual_transfer_length;
+
+ actual_transfer_length = ACPI_MIN(max_transfer_length, strlen(source));
+
+ if ((strlen(dest) + actual_transfer_length) >= dest_size) {
+ return (TRUE);
+ }
+
+ strncat(dest, source, max_transfer_length);
+ return (FALSE);
+}
+
+void acpi_ut_safe_strncpy(char *dest, char *source, acpi_size dest_size)
+{
+ /* Always terminate destination string */
+
+ strncpy(dest, source, dest_size);
+ dest[dest_size - 1] = 0;
+}
+
+#endif
diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c
new file mode 100644
index 000000000..d3667bfff
--- /dev/null
+++ b/drivers/acpi/acpica/utobject.c
@@ -0,0 +1,683 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utobject - ACPI object create/delete/size/cache routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include <linux/kmemleak.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utobject")
+
+/* Local prototypes */
+static acpi_status
+acpi_ut_get_simple_object_size(union acpi_operand_object *obj,
+ acpi_size *obj_length);
+
+static acpi_status
+acpi_ut_get_package_object_size(union acpi_operand_object *obj,
+ acpi_size *obj_length);
+
+static acpi_status
+acpi_ut_get_element_length(u8 object_type,
+ union acpi_operand_object *source_object,
+ union acpi_generic_state *state, void *context);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_internal_object_dbg
+ *
+ * PARAMETERS: module_name - Source file name of caller
+ * line_number - Line number of caller
+ * component_id - Component type of caller
+ * type - ACPI Type of the new object
+ *
+ * RETURN: A new internal object, null on failure
+ *
+ * DESCRIPTION: Create and initialize a new internal object.
+ *
+ * NOTE: We always allocate the worst-case object descriptor because
+ * these objects are cached, and we want them to be
+ * one-size-satisfies-any-request. This in itself may not be
+ * the most memory efficient, but the efficiency of the object
+ * cache should more than make up for this!
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ut_create_internal_object_dbg(const char
+ *module_name,
+ u32 line_number,
+ u32 component_id,
+ acpi_object_type
+ type)
+{
+ union acpi_operand_object *object;
+ union acpi_operand_object *second_object;
+
+ ACPI_FUNCTION_TRACE_STR(ut_create_internal_object_dbg,
+ acpi_ut_get_type_name(type));
+
+ /* Allocate the raw object descriptor */
+
+ object =
+ acpi_ut_allocate_object_desc_dbg(module_name, line_number,
+ component_id);
+ if (!object) {
+ return_PTR(NULL);
+ }
+ kmemleak_not_leak(object);
+
+ switch (type) {
+ case ACPI_TYPE_REGION:
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+
+ /* These types require a secondary object */
+
+ second_object =
+ acpi_ut_allocate_object_desc_dbg(module_name, line_number,
+ component_id);
+ if (!second_object) {
+ acpi_ut_delete_object_desc(object);
+ return_PTR(NULL);
+ }
+
+ second_object->common.type = ACPI_TYPE_LOCAL_EXTRA;
+ second_object->common.reference_count = 1;
+
+ /* Link the second object to the first */
+
+ object->common.next_object = second_object;
+ break;
+
+ default:
+
+ /* All others have no secondary object */
+ break;
+ }
+
+ /* Save the object type in the object descriptor */
+
+ object->common.type = (u8) type;
+
+ /* Init the reference count */
+
+ object->common.reference_count = 1;
+
+ /* Any per-type initialization should go here */
+
+ return_PTR(object);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_package_object
+ *
+ * PARAMETERS: count - Number of package elements
+ *
+ * RETURN: Pointer to a new Package object, null on failure
+ *
+ * DESCRIPTION: Create a fully initialized package object
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ut_create_package_object(u32 count)
+{
+ union acpi_operand_object *package_desc;
+ union acpi_operand_object **package_elements;
+
+ ACPI_FUNCTION_TRACE_U32(ut_create_package_object, count);
+
+ /* Create a new Package object */
+
+ package_desc = acpi_ut_create_internal_object(ACPI_TYPE_PACKAGE);
+ if (!package_desc) {
+ return_PTR(NULL);
+ }
+
+ /*
+ * Create the element array. Count+1 allows the array to be null
+ * terminated.
+ */
+ package_elements = ACPI_ALLOCATE_ZEROED(((acpi_size)count +
+ 1) * sizeof(void *));
+ if (!package_elements) {
+ ACPI_FREE(package_desc);
+ return_PTR(NULL);
+ }
+
+ package_desc->package.count = count;
+ package_desc->package.elements = package_elements;
+ return_PTR(package_desc);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_integer_object
+ *
+ * PARAMETERS: initial_value - Initial value for the integer
+ *
+ * RETURN: Pointer to a new Integer object, null on failure
+ *
+ * DESCRIPTION: Create an initialized integer object
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ut_create_integer_object(u64 initial_value)
+{
+ union acpi_operand_object *integer_desc;
+
+ ACPI_FUNCTION_TRACE(ut_create_integer_object);
+
+ /* Create and initialize a new integer object */
+
+ integer_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!integer_desc) {
+ return_PTR(NULL);
+ }
+
+ integer_desc->integer.value = initial_value;
+ return_PTR(integer_desc);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_buffer_object
+ *
+ * PARAMETERS: buffer_size - Size of buffer to be created
+ *
+ * RETURN: Pointer to a new Buffer object, null on failure
+ *
+ * DESCRIPTION: Create a fully initialized buffer object
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ut_create_buffer_object(acpi_size buffer_size)
+{
+ union acpi_operand_object *buffer_desc;
+ u8 *buffer = NULL;
+
+ ACPI_FUNCTION_TRACE_U32(ut_create_buffer_object, buffer_size);
+
+ /* Create a new Buffer object */
+
+ buffer_desc = acpi_ut_create_internal_object(ACPI_TYPE_BUFFER);
+ if (!buffer_desc) {
+ return_PTR(NULL);
+ }
+
+ /* Create an actual buffer only if size > 0 */
+
+ if (buffer_size > 0) {
+
+ /* Allocate the actual buffer */
+
+ buffer = ACPI_ALLOCATE_ZEROED(buffer_size);
+ if (!buffer) {
+ ACPI_ERROR((AE_INFO, "Could not allocate size %u",
+ (u32)buffer_size));
+
+ acpi_ut_remove_reference(buffer_desc);
+ return_PTR(NULL);
+ }
+ }
+
+ /* Complete buffer object initialization */
+
+ buffer_desc->buffer.flags |= AOPOBJ_DATA_VALID;
+ buffer_desc->buffer.pointer = buffer;
+ buffer_desc->buffer.length = (u32) buffer_size;
+
+ /* Return the new buffer descriptor */
+
+ return_PTR(buffer_desc);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_string_object
+ *
+ * PARAMETERS: string_size - Size of string to be created. Does not
+ * include NULL terminator, this is added
+ * automatically.
+ *
+ * RETURN: Pointer to a new String object
+ *
+ * DESCRIPTION: Create a fully initialized string object
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ut_create_string_object(acpi_size string_size)
+{
+ union acpi_operand_object *string_desc;
+ char *string;
+
+ ACPI_FUNCTION_TRACE_U32(ut_create_string_object, string_size);
+
+ /* Create a new String object */
+
+ string_desc = acpi_ut_create_internal_object(ACPI_TYPE_STRING);
+ if (!string_desc) {
+ return_PTR(NULL);
+ }
+
+ /*
+ * Allocate the actual string buffer -- (Size + 1) for NULL terminator.
+ * NOTE: Zero-length strings are NULL terminated
+ */
+ string = ACPI_ALLOCATE_ZEROED(string_size + 1);
+ if (!string) {
+ ACPI_ERROR((AE_INFO, "Could not allocate size %u",
+ (u32)string_size));
+
+ acpi_ut_remove_reference(string_desc);
+ return_PTR(NULL);
+ }
+
+ /* Complete string object initialization */
+
+ string_desc->string.pointer = string;
+ string_desc->string.length = (u32) string_size;
+
+ /* Return the new string descriptor */
+
+ return_PTR(string_desc);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_valid_internal_object
+ *
+ * PARAMETERS: object - Object to be validated
+ *
+ * RETURN: TRUE if object is valid, FALSE otherwise
+ *
+ * DESCRIPTION: Validate a pointer to be of type union acpi_operand_object
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_valid_internal_object(void *object)
+{
+
+ ACPI_FUNCTION_NAME(ut_valid_internal_object);
+
+ /* Check for a null pointer */
+
+ if (!object) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "**** Null Object Ptr\n"));
+ return (FALSE);
+ }
+
+ /* Check the descriptor type field */
+
+ switch (ACPI_GET_DESCRIPTOR_TYPE(object)) {
+ case ACPI_DESC_TYPE_OPERAND:
+
+ /* The object appears to be a valid union acpi_operand_object */
+
+ return (TRUE);
+
+ default:
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "%p is not an ACPI operand obj [%s]\n",
+ object, acpi_ut_get_descriptor_name(object)));
+ break;
+ }
+
+ return (FALSE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_allocate_object_desc_dbg
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * component_id - Caller's component ID (for error output)
+ *
+ * RETURN: Pointer to newly allocated object descriptor. Null on error
+ *
+ * DESCRIPTION: Allocate a new object descriptor. Gracefully handle
+ * error conditions.
+ *
+ ******************************************************************************/
+
+void *acpi_ut_allocate_object_desc_dbg(const char *module_name,
+ u32 line_number, u32 component_id)
+{
+ union acpi_operand_object *object;
+
+ ACPI_FUNCTION_TRACE(ut_allocate_object_desc_dbg);
+
+ object = acpi_os_acquire_object(acpi_gbl_operand_cache);
+ if (!object) {
+ ACPI_ERROR((module_name, line_number,
+ "Could not allocate an object descriptor"));
+
+ return_PTR(NULL);
+ }
+
+ /* Mark the descriptor type */
+
+ ACPI_SET_DESCRIPTOR_TYPE(object, ACPI_DESC_TYPE_OPERAND);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "%p Size %X\n",
+ object, (u32) sizeof(union acpi_operand_object)));
+
+ return_PTR(object);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_delete_object_desc
+ *
+ * PARAMETERS: object - An Acpi internal object to be deleted
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Free an ACPI object descriptor or add it to the object cache
+ *
+ ******************************************************************************/
+
+void acpi_ut_delete_object_desc(union acpi_operand_object *object)
+{
+ ACPI_FUNCTION_TRACE_PTR(ut_delete_object_desc, object);
+
+ /* Object must be of type union acpi_operand_object */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(object) != ACPI_DESC_TYPE_OPERAND) {
+ ACPI_ERROR((AE_INFO,
+ "%p is not an ACPI Operand object [%s]", object,
+ acpi_ut_get_descriptor_name(object)));
+ return_VOID;
+ }
+
+ (void)acpi_os_release_object(acpi_gbl_operand_cache, object);
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_simple_object_size
+ *
+ * PARAMETERS: internal_object - An ACPI operand object
+ * obj_length - Where the length is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to determine the space required to
+ * contain a simple object for return to an external user.
+ *
+ * The length includes the object structure plus any additional
+ * needed space.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_get_simple_object_size(union acpi_operand_object *internal_object,
+ acpi_size *obj_length)
+{
+ acpi_size length;
+ acpi_size size;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_PTR(ut_get_simple_object_size, internal_object);
+
+ /* Start with the length of the (external) Acpi object */
+
+ length = sizeof(union acpi_object);
+
+ /* A NULL object is allowed, can be a legal uninitialized package element */
+
+ if (!internal_object) {
+ /*
+ * Object is NULL, just return the length of union acpi_object
+ * (A NULL union acpi_object is an object of all zeroes.)
+ */
+ *obj_length = ACPI_ROUND_UP_TO_NATIVE_WORD(length);
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* A Namespace Node should never appear here */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(internal_object) == ACPI_DESC_TYPE_NAMED) {
+
+ /* A namespace node should never get here */
+
+ ACPI_ERROR((AE_INFO,
+ "Received a namespace node [%4.4s] "
+ "where an operand object is required",
+ ACPI_CAST_PTR(struct acpi_namespace_node,
+ internal_object)->name.ascii));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
+ /*
+ * The final length depends on the object type
+ * Strings and Buffers are packed right up against the parent object and
+ * must be accessed bytewise or there may be alignment problems on
+ * certain processors
+ */
+ switch (internal_object->common.type) {
+ case ACPI_TYPE_STRING:
+
+ length += (acpi_size)internal_object->string.length + 1;
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ length += (acpi_size)internal_object->buffer.length;
+ break;
+
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_POWER:
+
+ /* No extra data for these types */
+
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ switch (internal_object->reference.class) {
+ case ACPI_REFCLASS_NAME:
+ /*
+ * Get the actual length of the full pathname to this object.
+ * The reference will be converted to the pathname to the object
+ */
+ size =
+ acpi_ns_get_pathname_length(internal_object->
+ reference.node);
+ if (!size) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ length += ACPI_ROUND_UP_TO_NATIVE_WORD(size);
+ break;
+
+ default:
+ /*
+ * No other reference opcodes are supported.
+ * Notably, Locals and Args are not supported, but this may be
+ * required eventually.
+ */
+ ACPI_ERROR((AE_INFO,
+ "Cannot convert to external object - "
+ "unsupported Reference Class [%s] 0x%X in object %p",
+ acpi_ut_get_reference_name(internal_object),
+ internal_object->reference.class,
+ internal_object));
+ status = AE_TYPE;
+ break;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR((AE_INFO, "Cannot convert to external object - "
+ "unsupported type [%s] 0x%X in object %p",
+ acpi_ut_get_object_type_name(internal_object),
+ internal_object->common.type, internal_object));
+ status = AE_TYPE;
+ break;
+ }
+
+ /*
+ * Account for the space required by the object rounded up to the next
+ * multiple of the machine word size. This keeps each object aligned
+ * on a machine word boundary. (preventing alignment faults on some
+ * machines.)
+ */
+ *obj_length = ACPI_ROUND_UP_TO_NATIVE_WORD(length);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_element_length
+ *
+ * PARAMETERS: acpi_pkg_callback
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get the length of one package element.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_get_element_length(u8 object_type,
+ union acpi_operand_object *source_object,
+ union acpi_generic_state *state, void *context)
+{
+ acpi_status status = AE_OK;
+ struct acpi_pkg_info *info = (struct acpi_pkg_info *)context;
+ acpi_size object_space;
+
+ switch (object_type) {
+ case ACPI_COPY_TYPE_SIMPLE:
+ /*
+ * Simple object - just get the size (Null object/entry is handled
+ * here also) and sum it into the running package length
+ */
+ status =
+ acpi_ut_get_simple_object_size(source_object,
+ &object_space);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ info->length += object_space;
+ break;
+
+ case ACPI_COPY_TYPE_PACKAGE:
+
+ /* Package object - nothing much to do here, let the walk handle it */
+
+ info->num_packages++;
+ state->pkg.this_target_obj = NULL;
+ break;
+
+ default:
+
+ /* No other types allowed */
+
+ return (AE_BAD_PARAMETER);
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_package_object_size
+ *
+ * PARAMETERS: internal_object - An ACPI internal object
+ * obj_length - Where the length is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to determine the space required to
+ * contain a package object for return to an external user.
+ *
+ * This is moderately complex since a package contains other
+ * objects including packages.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_get_package_object_size(union acpi_operand_object *internal_object,
+ acpi_size *obj_length)
+{
+ acpi_status status;
+ struct acpi_pkg_info info;
+
+ ACPI_FUNCTION_TRACE_PTR(ut_get_package_object_size, internal_object);
+
+ info.length = 0;
+ info.object_space = 0;
+ info.num_packages = 1;
+
+ status =
+ acpi_ut_walk_package_tree(internal_object, NULL,
+ acpi_ut_get_element_length, &info);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * We have handled all of the objects in all levels of the package.
+ * just add the length of the package objects themselves.
+ * Round up to the next machine word.
+ */
+ info.length +=
+ ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object)) *
+ (acpi_size)info.num_packages;
+
+ /* Return the total package length */
+
+ *obj_length = info.length;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_object_size
+ *
+ * PARAMETERS: internal_object - An ACPI internal object
+ * obj_length - Where the length will be returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to determine the space required to
+ * contain an object for return to an API user.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_get_object_size(union acpi_operand_object *internal_object,
+ acpi_size *obj_length)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_ENTRY();
+
+ if ((ACPI_GET_DESCRIPTOR_TYPE(internal_object) ==
+ ACPI_DESC_TYPE_OPERAND) &&
+ (internal_object->common.type == ACPI_TYPE_PACKAGE)) {
+ status =
+ acpi_ut_get_package_object_size(internal_object,
+ obj_length);
+ } else {
+ status =
+ acpi_ut_get_simple_object_size(internal_object, obj_length);
+ }
+
+ return (status);
+}
diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c
new file mode 100644
index 000000000..b8ab0a3cb
--- /dev/null
+++ b/drivers/acpi/acpica/utosi.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utosi - Support for the _OSI predefined control method
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utosi")
+
+/******************************************************************************
+ *
+ * ACPICA policy for new _OSI strings:
+ *
+ * It is the stated policy of ACPICA that new _OSI strings will be integrated
+ * into this module as soon as possible after they are defined. It is strongly
+ * recommended that all ACPICA hosts mirror this policy and integrate any
+ * changes to this module as soon as possible. There are several historical
+ * reasons behind this policy:
+ *
+ * 1) New BIOSs tend to test only the case where the host responds TRUE to
+ * the latest version of Windows, which would respond to the latest/newest
+ * _OSI string. Not responding TRUE to the latest version of Windows will
+ * risk executing untested code paths throughout the DSDT and SSDTs.
+ *
+ * 2) If a new _OSI string is recognized only after a significant delay, this
+ * has the potential to cause problems on existing working machines because
+ * of the possibility that a new and different path through the ASL code
+ * will be executed.
+ *
+ * 3) New _OSI strings are tending to come out about once per year. A delay
+ * in recognizing a new string for a significant amount of time risks the
+ * release of another string which only compounds the initial problem.
+ *
+ *****************************************************************************/
+/*
+ * Strings supported by the _OSI predefined control method (which is
+ * implemented internally within this module.)
+ *
+ * March 2009: Removed "Linux" as this host no longer wants to respond true
+ * for this string. Basically, the only safe OS strings are windows-related
+ * and in many or most cases represent the only test path within the
+ * BIOS-provided ASL code.
+ *
+ * The last element of each entry is used to track the newest version of
+ * Windows that the BIOS has requested.
+ */
+static struct acpi_interface_info acpi_default_supported_interfaces[] = {
+ /* Operating System Vendor Strings */
+
+ {"Windows 2000", NULL, 0, ACPI_OSI_WIN_2000}, /* Windows 2000 */
+ {"Windows 2001", NULL, 0, ACPI_OSI_WIN_XP}, /* Windows XP */
+ {"Windows 2001 SP1", NULL, 0, ACPI_OSI_WIN_XP_SP1}, /* Windows XP SP1 */
+ {"Windows 2001.1", NULL, 0, ACPI_OSI_WINSRV_2003}, /* Windows Server 2003 */
+ {"Windows 2001 SP2", NULL, 0, ACPI_OSI_WIN_XP_SP2}, /* Windows XP SP2 */
+ {"Windows 2001.1 SP1", NULL, 0, ACPI_OSI_WINSRV_2003_SP1}, /* Windows Server 2003 SP1 - Added 03/2006 */
+ {"Windows 2006", NULL, 0, ACPI_OSI_WIN_VISTA}, /* Windows vista - Added 03/2006 */
+ {"Windows 2006.1", NULL, 0, ACPI_OSI_WINSRV_2008}, /* Windows Server 2008 - Added 09/2009 */
+ {"Windows 2006 SP1", NULL, 0, ACPI_OSI_WIN_VISTA_SP1}, /* Windows Vista SP1 - Added 09/2009 */
+ {"Windows 2006 SP2", NULL, 0, ACPI_OSI_WIN_VISTA_SP2}, /* Windows Vista SP2 - Added 09/2010 */
+ {"Windows 2009", NULL, 0, ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */
+ {"Windows 2012", NULL, 0, ACPI_OSI_WIN_8}, /* Windows 8 and Server 2012 - Added 08/2012 */
+ {"Windows 2013", NULL, 0, ACPI_OSI_WIN_8_1}, /* Windows 8.1 and Server 2012 R2 - Added 01/2014 */
+ {"Windows 2015", NULL, 0, ACPI_OSI_WIN_10}, /* Windows 10 - Added 03/2015 */
+ {"Windows 2016", NULL, 0, ACPI_OSI_WIN_10_RS1}, /* Windows 10 version 1607 - Added 12/2017 */
+ {"Windows 2017", NULL, 0, ACPI_OSI_WIN_10_RS2}, /* Windows 10 version 1703 - Added 12/2017 */
+ {"Windows 2017.2", NULL, 0, ACPI_OSI_WIN_10_RS3}, /* Windows 10 version 1709 - Added 02/2018 */
+ {"Windows 2018", NULL, 0, ACPI_OSI_WIN_10_RS4}, /* Windows 10 version 1803 - Added 11/2018 */
+ {"Windows 2018.2", NULL, 0, ACPI_OSI_WIN_10_RS5}, /* Windows 10 version 1809 - Added 11/2018 */
+ {"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 */
+
+ /* Feature Group Strings */
+
+ {"Extended Address Space Descriptor", NULL, ACPI_OSI_FEATURE, 0},
+
+ /*
+ * All "optional" feature group strings (features that are implemented
+ * by the host) should be dynamically modified to VALID by the host via
+ * acpi_install_interface or acpi_update_interfaces. Such optional feature
+ * group strings are set as INVALID by default here.
+ */
+
+ {"Module Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
+ {"Processor Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
+ {"3.0 Thermal Model", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
+ {"3.0 _SCP Extensions", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
+ {"Processor Aggregator Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_initialize_interfaces
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize the global _OSI supported interfaces list
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_initialize_interfaces(void)
+{
+ acpi_status status;
+ u32 i;
+
+ status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ acpi_gbl_supported_interfaces = acpi_default_supported_interfaces;
+
+ /* Link the static list of supported interfaces */
+
+ for (i = 0;
+ i < (ACPI_ARRAY_LENGTH(acpi_default_supported_interfaces) - 1);
+ i++) {
+ acpi_default_supported_interfaces[i].next =
+ &acpi_default_supported_interfaces[(acpi_size)i + 1];
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_interface_terminate
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Delete all interfaces in the global list. Sets
+ * acpi_gbl_supported_interfaces to NULL.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_interface_terminate(void)
+{
+ acpi_status status;
+ struct acpi_interface_info *next_interface;
+
+ status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ next_interface = acpi_gbl_supported_interfaces;
+ while (next_interface) {
+ acpi_gbl_supported_interfaces = next_interface->next;
+
+ if (next_interface->flags & ACPI_OSI_DYNAMIC) {
+
+ /* Only interfaces added at runtime can be freed */
+
+ ACPI_FREE(next_interface->name);
+ ACPI_FREE(next_interface);
+ } else {
+ /* Interface is in static list. Reset it to invalid or valid. */
+
+ if (next_interface->flags & ACPI_OSI_DEFAULT_INVALID) {
+ next_interface->flags |= ACPI_OSI_INVALID;
+ } else {
+ next_interface->flags &= ~ACPI_OSI_INVALID;
+ }
+ }
+
+ next_interface = acpi_gbl_supported_interfaces;
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_install_interface
+ *
+ * PARAMETERS: interface_name - The interface to install
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install the interface into the global interface list.
+ * Caller MUST hold acpi_gbl_osi_mutex
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_install_interface(acpi_string interface_name)
+{
+ struct acpi_interface_info *interface_info;
+
+ /* Allocate info block and space for the name string */
+
+ interface_info =
+ ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_interface_info));
+ if (!interface_info) {
+ return (AE_NO_MEMORY);
+ }
+
+ interface_info->name = ACPI_ALLOCATE_ZEROED(strlen(interface_name) + 1);
+ if (!interface_info->name) {
+ ACPI_FREE(interface_info);
+ return (AE_NO_MEMORY);
+ }
+
+ /* Initialize new info and insert at the head of the global list */
+
+ strcpy(interface_info->name, interface_name);
+ interface_info->flags = ACPI_OSI_DYNAMIC;
+ interface_info->next = acpi_gbl_supported_interfaces;
+
+ acpi_gbl_supported_interfaces = interface_info;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_remove_interface
+ *
+ * PARAMETERS: interface_name - The interface to remove
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove the interface from the global interface list.
+ * Caller MUST hold acpi_gbl_osi_mutex
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_remove_interface(acpi_string interface_name)
+{
+ struct acpi_interface_info *previous_interface;
+ struct acpi_interface_info *next_interface;
+
+ previous_interface = next_interface = acpi_gbl_supported_interfaces;
+ while (next_interface) {
+ if (!strcmp(interface_name, next_interface->name)) {
+ /*
+ * Found: name is in either the static list
+ * or was added at runtime
+ */
+ if (next_interface->flags & ACPI_OSI_DYNAMIC) {
+
+ /* Interface was added dynamically, remove and free it */
+
+ if (previous_interface == next_interface) {
+ acpi_gbl_supported_interfaces =
+ next_interface->next;
+ } else {
+ previous_interface->next =
+ next_interface->next;
+ }
+
+ ACPI_FREE(next_interface->name);
+ ACPI_FREE(next_interface);
+ } else {
+ /*
+ * Interface is in static list. If marked invalid, then
+ * it does not actually exist. Else, mark it invalid.
+ */
+ if (next_interface->flags & ACPI_OSI_INVALID) {
+ return (AE_NOT_EXIST);
+ }
+
+ next_interface->flags |= ACPI_OSI_INVALID;
+ }
+
+ return (AE_OK);
+ }
+
+ previous_interface = next_interface;
+ next_interface = next_interface->next;
+ }
+
+ /* Interface was not found */
+
+ return (AE_NOT_EXIST);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_update_interfaces
+ *
+ * PARAMETERS: action - Actions to be performed during the
+ * update
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Update _OSI interface strings, disabling or enabling OS vendor
+ * strings or/and feature group strings.
+ * Caller MUST hold acpi_gbl_osi_mutex
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_update_interfaces(u8 action)
+{
+ struct acpi_interface_info *next_interface;
+
+ next_interface = acpi_gbl_supported_interfaces;
+ while (next_interface) {
+ if (((next_interface->flags & ACPI_OSI_FEATURE) &&
+ (action & ACPI_FEATURE_STRINGS)) ||
+ (!(next_interface->flags & ACPI_OSI_FEATURE) &&
+ (action & ACPI_VENDOR_STRINGS))) {
+ if (action & ACPI_DISABLE_INTERFACES) {
+
+ /* Mark the interfaces as invalid */
+
+ next_interface->flags |= ACPI_OSI_INVALID;
+ } else {
+ /* Mark the interfaces as valid */
+
+ next_interface->flags &= ~ACPI_OSI_INVALID;
+ }
+ }
+
+ next_interface = next_interface->next;
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_interface
+ *
+ * PARAMETERS: interface_name - The interface to find
+ *
+ * RETURN: struct acpi_interface_info if found. NULL if not found.
+ *
+ * DESCRIPTION: Search for the specified interface name in the global list.
+ * Caller MUST hold acpi_gbl_osi_mutex
+ *
+ ******************************************************************************/
+
+struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name)
+{
+ struct acpi_interface_info *next_interface;
+
+ next_interface = acpi_gbl_supported_interfaces;
+ while (next_interface) {
+ if (!strcmp(interface_name, next_interface->name)) {
+ return (next_interface);
+ }
+
+ next_interface = next_interface->next;
+ }
+
+ return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_osi_implementation
+ *
+ * PARAMETERS: walk_state - Current walk state
+ *
+ * RETURN: Status
+ * Integer: TRUE (0) if input string is matched
+ * FALSE (-1) if string is not matched
+ *
+ * DESCRIPTION: Implementation of the _OSI predefined control method. When
+ * an invocation of _OSI is encountered in the system AML,
+ * control is transferred to this function.
+ *
+ * (August 2016)
+ * Note: _OSI is now defined to return "Ones" to indicate a match, for
+ * compatibility with other ACPI implementations. On a 32-bit DSDT, Ones
+ * is 0xFFFFFFFF. On a 64-bit DSDT, Ones is 0xFFFFFFFFFFFFFFFF
+ * (ACPI_UINT64_MAX).
+ *
+ * This function always returns ACPI_UINT64_MAX for TRUE, and later code
+ * will truncate this to 32 bits if necessary.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
+{
+ union acpi_operand_object *string_desc;
+ union acpi_operand_object *return_desc;
+ struct acpi_interface_info *interface_info;
+ acpi_interface_handler interface_handler;
+ acpi_status status;
+ u64 return_value;
+
+ ACPI_FUNCTION_TRACE(ut_osi_implementation);
+
+ /* Validate the string input argument (from the AML caller) */
+
+ string_desc = walk_state->arguments[0].object;
+ if (!string_desc || (string_desc->common.type != ACPI_TYPE_STRING)) {
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ /* Create a return object */
+
+ return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!return_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Default return value is 0, NOT SUPPORTED */
+
+ return_value = 0;
+ status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_remove_reference(return_desc);
+ return_ACPI_STATUS(status);
+ }
+
+ /* Lookup the interface in the global _OSI list */
+
+ interface_info = acpi_ut_get_interface(string_desc->string.pointer);
+ if (interface_info && !(interface_info->flags & ACPI_OSI_INVALID)) {
+ /*
+ * The interface is supported.
+ * Update the osi_data if necessary. We keep track of the latest
+ * version of Windows that has been requested by the BIOS.
+ */
+ if (interface_info->value > acpi_gbl_osi_data) {
+ acpi_gbl_osi_data = interface_info->value;
+ }
+
+ return_value = ACPI_UINT64_MAX;
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+
+ /*
+ * Invoke an optional _OSI interface handler. The host OS may wish
+ * to do some interface-specific handling. For example, warn about
+ * certain interfaces or override the true/false support value.
+ */
+ interface_handler = acpi_gbl_interface_handler;
+ if (interface_handler) {
+ if (interface_handler
+ (string_desc->string.pointer, (u32)return_value)) {
+ return_value = ACPI_UINT64_MAX;
+ }
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO,
+ "ACPI: BIOS _OSI(\"%s\") is %ssupported\n",
+ string_desc->string.pointer,
+ return_value == 0 ? "not " : ""));
+
+ /* Complete the return object */
+
+ return_desc->integer.value = return_value;
+ walk_state->return_desc = return_desc;
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/utownerid.c b/drivers/acpi/acpica/utownerid.c
new file mode 100644
index 000000000..d3525ef8e
--- /dev/null
+++ b/drivers/acpi/acpica/utownerid.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utownerid - Support for Table/Method Owner IDs
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utownerid")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_allocate_owner_id
+ *
+ * PARAMETERS: owner_id - Where the new owner ID is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Allocate a table or method owner ID. The owner ID is used to
+ * track objects created by the table or method, to be deleted
+ * when the method exits or the table is unloaded.
+ *
+ ******************************************************************************/
+acpi_status acpi_ut_allocate_owner_id(acpi_owner_id *owner_id)
+{
+ u32 i;
+ u32 j;
+ u32 k;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ut_allocate_owner_id);
+
+ /* Guard against multiple allocations of ID to the same location */
+
+ if (*owner_id) {
+ ACPI_ERROR((AE_INFO,
+ "Owner ID [0x%3.3X] already exists", *owner_id));
+ return_ACPI_STATUS(AE_ALREADY_EXISTS);
+ }
+
+ /* Mutex for the global ID mask */
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Find a free owner ID, cycle through all possible IDs on repeated
+ * allocations. (ACPI_NUM_OWNERID_MASKS + 1) because first index
+ * may have to be scanned twice.
+ */
+ for (i = 0, j = acpi_gbl_last_owner_id_index;
+ i < (ACPI_NUM_OWNERID_MASKS + 1); i++, j++) {
+ if (j >= ACPI_NUM_OWNERID_MASKS) {
+ j = 0; /* Wraparound to start of mask array */
+ }
+
+ for (k = acpi_gbl_next_owner_id_offset; k < 32; k++) {
+ if (acpi_gbl_owner_id_mask[j] == ACPI_UINT32_MAX) {
+
+ /* There are no free IDs in this mask */
+
+ break;
+ }
+
+ /*
+ * Note: the u32 cast ensures that 1 is stored as a unsigned
+ * integer. Omitting the cast may result in 1 being stored as an
+ * int. Some compilers or runtime error detection may flag this as
+ * an error.
+ */
+ if (!(acpi_gbl_owner_id_mask[j] & ((u32)1 << k))) {
+ /*
+ * Found a free ID. The actual ID is the bit index plus one,
+ * making zero an invalid Owner ID. Save this as the last ID
+ * allocated and update the global ID mask.
+ */
+ acpi_gbl_owner_id_mask[j] |= ((u32)1 << k);
+
+ acpi_gbl_last_owner_id_index = (u8)j;
+ acpi_gbl_next_owner_id_offset = (u8)(k + 1);
+
+ /*
+ * Construct encoded ID from the index and bit position
+ *
+ * Note: Last [j].k (bit 4095) is never used and is marked
+ * permanently allocated (prevents +1 overflow)
+ */
+ *owner_id =
+ (acpi_owner_id)((k + 1) + ACPI_MUL_32(j));
+
+ ACPI_DEBUG_PRINT((ACPI_DB_VALUES,
+ "Allocated OwnerId: 0x%3.3X\n",
+ (unsigned int)*owner_id));
+ goto exit;
+ }
+ }
+
+ acpi_gbl_next_owner_id_offset = 0;
+ }
+
+ /*
+ * All owner_ids have been allocated. This typically should
+ * not happen since the IDs are reused after deallocation. The IDs are
+ * allocated upon table load (one per table) and method execution, and
+ * they are released when a table is unloaded or a method completes
+ * execution.
+ *
+ * If this error happens, there may be very deep nesting of invoked
+ * control methods, or there may be a bug where the IDs are not released.
+ */
+ status = AE_OWNER_ID_LIMIT;
+ ACPI_ERROR((AE_INFO,
+ "Could not allocate new OwnerId (4095 max), AE_OWNER_ID_LIMIT"));
+
+exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_CACHES);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_release_owner_id
+ *
+ * PARAMETERS: owner_id_ptr - Pointer to a previously allocated owner_ID
+ *
+ * RETURN: None. No error is returned because we are either exiting a
+ * control method or unloading a table. Either way, we would
+ * ignore any error anyway.
+ *
+ * DESCRIPTION: Release a table or method owner ID. Valid IDs are 1 - 255
+ *
+ ******************************************************************************/
+
+void acpi_ut_release_owner_id(acpi_owner_id *owner_id_ptr)
+{
+ acpi_owner_id owner_id = *owner_id_ptr;
+ acpi_status status;
+ u32 index;
+ u32 bit;
+
+ ACPI_FUNCTION_TRACE_U32(ut_release_owner_id, owner_id);
+
+ /* Always clear the input owner_id (zero is an invalid ID) */
+
+ *owner_id_ptr = 0;
+
+ /* Zero is not a valid owner_ID */
+
+ if (owner_id == 0) {
+ ACPI_ERROR((AE_INFO, "Invalid OwnerId: 0x%3.3X", owner_id));
+ return_VOID;
+ }
+
+ /* Mutex for the global ID mask */
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES);
+ if (ACPI_FAILURE(status)) {
+ return_VOID;
+ }
+
+ /* Normalize the ID to zero */
+
+ owner_id--;
+
+ /* Decode ID to index/offset pair */
+
+ index = ACPI_DIV_32(owner_id);
+ bit = (u32)1 << ACPI_MOD_32(owner_id);
+
+ /* Free the owner ID only if it is valid */
+
+ if (acpi_gbl_owner_id_mask[index] & bit) {
+ acpi_gbl_owner_id_mask[index] ^= bit;
+ } else {
+ ACPI_ERROR((AE_INFO,
+ "Attempted release of non-allocated OwnerId: 0x%3.3X",
+ owner_id + 1));
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_CACHES);
+ return_VOID;
+}
diff --git a/drivers/acpi/acpica/utpredef.c b/drivers/acpi/acpica/utpredef.c
new file mode 100644
index 000000000..2524f013b
--- /dev/null
+++ b/drivers/acpi/acpica/utpredef.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utpredef - support functions for predefined names
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acpredef.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utpredef")
+
+/*
+ * Names for the types that can be returned by the predefined objects.
+ * Used for warning messages. Must be in the same order as the ACPI_RTYPEs
+ */
+static const char *ut_rtype_names[] = {
+ "/Integer",
+ "/String",
+ "/Buffer",
+ "/Package",
+ "/Reference",
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_next_predefined_method
+ *
+ * PARAMETERS: this_name - Entry in the predefined method/name table
+ *
+ * RETURN: Pointer to next entry in predefined table.
+ *
+ * DESCRIPTION: Get the next entry in the predefine method table. Handles the
+ * cases where a package info entry follows a method name that
+ * returns a package.
+ *
+ ******************************************************************************/
+
+const union acpi_predefined_info *acpi_ut_get_next_predefined_method(const union
+ acpi_predefined_info
+ *this_name)
+{
+
+ /*
+ * Skip next entry in the table if this name returns a Package
+ * (next entry contains the package info)
+ */
+ if ((this_name->info.expected_btypes & ACPI_RTYPE_PACKAGE) &&
+ (this_name->info.expected_btypes != ACPI_RTYPE_ALL)) {
+ this_name++;
+ }
+
+ this_name++;
+ return (this_name);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_match_predefined_method
+ *
+ * PARAMETERS: name - Name to find
+ *
+ * RETURN: Pointer to entry in predefined table. NULL indicates not found.
+ *
+ * DESCRIPTION: Check an object name against the predefined object list.
+ *
+ ******************************************************************************/
+
+const union acpi_predefined_info *acpi_ut_match_predefined_method(char *name)
+{
+ const union acpi_predefined_info *this_name;
+
+ /* Quick check for a predefined name, first character must be underscore */
+
+ if (name[0] != '_') {
+ return (NULL);
+ }
+
+ /* Search info table for a predefined method/object name */
+
+ this_name = acpi_gbl_predefined_methods;
+ while (this_name->info.name[0]) {
+ if (ACPI_COMPARE_NAMESEG(name, this_name->info.name)) {
+ return (this_name);
+ }
+
+ this_name = acpi_ut_get_next_predefined_method(this_name);
+ }
+
+ return (NULL); /* Not found */
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_expected_return_types
+ *
+ * PARAMETERS: buffer - Where the formatted string is returned
+ * expected_Btypes - Bitfield of expected data types
+ *
+ * RETURN: Formatted string in Buffer.
+ *
+ * DESCRIPTION: Format the expected object types into a printable string.
+ *
+ ******************************************************************************/
+
+void acpi_ut_get_expected_return_types(char *buffer, u32 expected_btypes)
+{
+ u32 this_rtype;
+ u32 i;
+ u32 j;
+
+ if (!expected_btypes) {
+ strcpy(buffer, "NONE");
+ return;
+ }
+
+ j = 1;
+ buffer[0] = 0;
+ this_rtype = ACPI_RTYPE_INTEGER;
+
+ for (i = 0; i < ACPI_NUM_RTYPES; i++) {
+
+ /* If one of the expected types, concatenate the name of this type */
+
+ if (expected_btypes & this_rtype) {
+ strcat(buffer, &ut_rtype_names[i][j]);
+ j = 0; /* Use name separator from now on */
+ }
+
+ this_rtype <<= 1; /* Next Rtype */
+ }
+}
+
+/*******************************************************************************
+ *
+ * The remaining functions are used by iASL and acpi_help only
+ *
+ ******************************************************************************/
+
+#if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP)
+
+/* Local prototypes */
+
+static u32 acpi_ut_get_argument_types(char *buffer, u16 argument_types);
+
+/* Types that can be returned externally by a predefined name */
+
+static const char *ut_external_type_names[] = /* Indexed by ACPI_TYPE_* */
+{
+ ", Type_ANY",
+ ", Integer",
+ ", String",
+ ", Buffer",
+ ", Package"
+};
+
+/* Bit widths for resource descriptor predefined names */
+
+static const char *ut_resource_type_names[] = {
+ "/1",
+ "/2",
+ "/3",
+ "/8",
+ "/16",
+ "/32",
+ "/64",
+ "/variable",
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_match_resource_name
+ *
+ * PARAMETERS: name - Name to find
+ *
+ * RETURN: Pointer to entry in the resource table. NULL indicates not
+ * found.
+ *
+ * DESCRIPTION: Check an object name against the predefined resource
+ * descriptor object list.
+ *
+ ******************************************************************************/
+
+const union acpi_predefined_info *acpi_ut_match_resource_name(char *name)
+{
+ const union acpi_predefined_info *this_name;
+
+ /*
+ * Quick check for a predefined name, first character must
+ * be underscore
+ */
+ if (name[0] != '_') {
+ return (NULL);
+ }
+
+ /* Search info table for a predefined method/object name */
+
+ this_name = acpi_gbl_resource_names;
+ while (this_name->info.name[0]) {
+ if (ACPI_COMPARE_NAMESEG(name, this_name->info.name)) {
+ return (this_name);
+ }
+
+ this_name++;
+ }
+
+ return (NULL); /* Not found */
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_display_predefined_method
+ *
+ * PARAMETERS: buffer - Scratch buffer for this function
+ * this_name - Entry in the predefined method/name table
+ * multi_line - TRUE if output should be on >1 line
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display information about a predefined method. Number and
+ * type of the input arguments, and expected type(s) for the
+ * return value, if any.
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_display_predefined_method(char *buffer,
+ const union acpi_predefined_info *this_name,
+ u8 multi_line)
+{
+ u32 arg_count;
+
+ /*
+ * Get the argument count and the string buffer
+ * containing all argument types
+ */
+ arg_count = acpi_ut_get_argument_types(buffer,
+ this_name->info.argument_list);
+
+ if (multi_line) {
+ printf(" ");
+ }
+
+ printf("%4.4s Requires %s%u argument%s",
+ this_name->info.name,
+ (this_name->info.argument_list & ARG_COUNT_IS_MINIMUM) ?
+ "(at least) " : "", arg_count, arg_count != 1 ? "s" : "");
+
+ /* Display the types for any arguments */
+
+ if (arg_count > 0) {
+ printf(" (%s)", buffer);
+ }
+
+ if (multi_line) {
+ printf("\n ");
+ }
+
+ /* Get the return value type(s) allowed */
+
+ if (this_name->info.expected_btypes) {
+ acpi_ut_get_expected_return_types(buffer,
+ this_name->info.
+ expected_btypes);
+ printf(" Return value types: %s\n", buffer);
+ } else {
+ printf(" No return value\n");
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_argument_types
+ *
+ * PARAMETERS: buffer - Where to return the formatted types
+ * argument_types - Types field for this method
+ *
+ * RETURN: count - the number of arguments required for this method
+ *
+ * DESCRIPTION: Format the required data types for this method (Integer,
+ * String, Buffer, or Package) and return the required argument
+ * count.
+ *
+ ******************************************************************************/
+
+static u32 acpi_ut_get_argument_types(char *buffer, u16 argument_types)
+{
+ u16 this_argument_type;
+ u16 sub_index;
+ u16 arg_count;
+ u32 i;
+
+ *buffer = 0;
+ sub_index = 2;
+
+ /* First field in the types list is the count of args to follow */
+
+ arg_count = METHOD_GET_ARG_COUNT(argument_types);
+ if (arg_count > METHOD_PREDEF_ARGS_MAX) {
+ printf("**** Invalid argument count (%u) "
+ "in predefined info structure\n", arg_count);
+ return (arg_count);
+ }
+
+ /* Get each argument from the list, convert to ascii, store to buffer */
+
+ for (i = 0; i < arg_count; i++) {
+ this_argument_type = METHOD_GET_NEXT_TYPE(argument_types);
+
+ if (this_argument_type > METHOD_MAX_ARG_TYPE) {
+ printf("**** Invalid argument type (%u) "
+ "in predefined info structure\n",
+ this_argument_type);
+ return (arg_count);
+ }
+
+ strcat(buffer,
+ ut_external_type_names[this_argument_type] + sub_index);
+ sub_index = 0;
+ }
+
+ return (arg_count);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_resource_bit_width
+ *
+ * PARAMETERS: buffer - Where the formatted string is returned
+ * types - Bitfield of expected data types
+ *
+ * RETURN: Count of return types. Formatted string in Buffer.
+ *
+ * DESCRIPTION: Format the resource bit widths into a printable string.
+ *
+ ******************************************************************************/
+
+u32 acpi_ut_get_resource_bit_width(char *buffer, u16 types)
+{
+ u32 i;
+ u16 sub_index;
+ u32 found;
+
+ *buffer = 0;
+ sub_index = 1;
+ found = 0;
+
+ for (i = 0; i < NUM_RESOURCE_WIDTHS; i++) {
+ if (types & 1) {
+ strcat(buffer, &(ut_resource_type_names[i][sub_index]));
+ sub_index = 0;
+ found++;
+ }
+
+ types >>= 1;
+ }
+
+ return (found);
+}
+#endif
diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c
new file mode 100644
index 000000000..d5aa21098
--- /dev/null
+++ b/drivers/acpi/acpica/utprint.c
@@ -0,0 +1,714 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utprint - Formatted printing routines
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utprint")
+
+#define ACPI_FORMAT_SIGN 0x01
+#define ACPI_FORMAT_SIGN_PLUS 0x02
+#define ACPI_FORMAT_SIGN_PLUS_SPACE 0x04
+#define ACPI_FORMAT_ZERO 0x08
+#define ACPI_FORMAT_LEFT 0x10
+#define ACPI_FORMAT_UPPER 0x20
+#define ACPI_FORMAT_PREFIX 0x40
+/* Local prototypes */
+static acpi_size
+acpi_ut_bound_string_length(const char *string, acpi_size count);
+
+static char *acpi_ut_bound_string_output(char *string, const char *end, char c);
+
+static char *acpi_ut_format_number(char *string,
+ char *end,
+ u64 number,
+ u8 base, s32 width, s32 precision, u8 type);
+
+static char *acpi_ut_put_number(char *string, u64 number, u8 base, u8 upper);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_bound_string_length
+ *
+ * PARAMETERS: string - String with boundary
+ * count - Boundary of the string
+ *
+ * RETURN: Length of the string. Less than or equal to Count.
+ *
+ * DESCRIPTION: Calculate the length of a string with boundary.
+ *
+ ******************************************************************************/
+
+static acpi_size
+acpi_ut_bound_string_length(const char *string, acpi_size count)
+{
+ u32 length = 0;
+
+ while (*string && count) {
+ length++;
+ string++;
+ count--;
+ }
+
+ return (length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_bound_string_output
+ *
+ * PARAMETERS: string - String with boundary
+ * end - Boundary of the string
+ * c - Character to be output to the string
+ *
+ * RETURN: Updated position for next valid character
+ *
+ * DESCRIPTION: Output a character into a string with boundary check.
+ *
+ ******************************************************************************/
+
+static char *acpi_ut_bound_string_output(char *string, const char *end, char c)
+{
+
+ if (string < end) {
+ *string = c;
+ }
+
+ ++string;
+ return (string);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_put_number
+ *
+ * PARAMETERS: string - Buffer to hold reverse-ordered string
+ * number - Integer to be converted
+ * base - Base of the integer
+ * upper - Whether or not using upper cased digits
+ *
+ * RETURN: Updated position for next valid character
+ *
+ * DESCRIPTION: Convert an integer into a string, note that, the string holds a
+ * reversed ordered number without the trailing zero.
+ *
+ ******************************************************************************/
+
+static char *acpi_ut_put_number(char *string, u64 number, u8 base, u8 upper)
+{
+ const char *digits;
+ u64 digit_index;
+ char *pos;
+
+ pos = string;
+ digits = upper ? acpi_gbl_upper_hex_digits : acpi_gbl_lower_hex_digits;
+
+ if (number == 0) {
+ *(pos++) = '0';
+ } else {
+ while (number) {
+ (void)acpi_ut_divide(number, base, &number,
+ &digit_index);
+ *(pos++) = digits[digit_index];
+ }
+ }
+
+ /* *(Pos++) = '0'; */
+ return (pos);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_scan_number
+ *
+ * PARAMETERS: string - String buffer
+ * number_ptr - Where the number is returned
+ *
+ * RETURN: Updated position for next valid character
+ *
+ * DESCRIPTION: Scan a string for a decimal integer.
+ *
+ ******************************************************************************/
+
+const char *acpi_ut_scan_number(const char *string, u64 *number_ptr)
+{
+ u64 number = 0;
+
+ while (isdigit((int)*string)) {
+ acpi_ut_short_multiply(number, 10, &number);
+ number += *(string++) - '0';
+ }
+
+ *number_ptr = number;
+ return (string);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_print_number
+ *
+ * PARAMETERS: string - String buffer
+ * number - The number to be converted
+ *
+ * RETURN: Updated position for next valid character
+ *
+ * DESCRIPTION: Print a decimal integer into a string.
+ *
+ ******************************************************************************/
+
+const char *acpi_ut_print_number(char *string, u64 number)
+{
+ char ascii_string[20];
+ const char *pos1;
+ char *pos2;
+
+ pos1 = acpi_ut_put_number(ascii_string, number, 10, FALSE);
+ pos2 = string;
+
+ while (pos1 != ascii_string) {
+ *(pos2++) = *(--pos1);
+ }
+
+ *pos2 = 0;
+ return (string);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_format_number
+ *
+ * PARAMETERS: string - String buffer with boundary
+ * end - Boundary of the string
+ * number - The number to be converted
+ * base - Base of the integer
+ * width - Field width
+ * precision - Precision of the integer
+ * type - Special printing flags
+ *
+ * RETURN: Updated position for next valid character
+ *
+ * DESCRIPTION: Print an integer into a string with any base and any precision.
+ *
+ ******************************************************************************/
+
+static char *acpi_ut_format_number(char *string,
+ char *end,
+ u64 number,
+ u8 base, s32 width, s32 precision, u8 type)
+{
+ char *pos;
+ char sign;
+ char zero;
+ u8 need_prefix;
+ u8 upper;
+ s32 i;
+ char reversed_string[66];
+
+ /* Parameter validation */
+
+ if (base < 2 || base > 16) {
+ return (NULL);
+ }
+
+ if (type & ACPI_FORMAT_LEFT) {
+ type &= ~ACPI_FORMAT_ZERO;
+ }
+
+ need_prefix = ((type & ACPI_FORMAT_PREFIX)
+ && base != 10) ? TRUE : FALSE;
+ upper = (type & ACPI_FORMAT_UPPER) ? TRUE : FALSE;
+ zero = (type & ACPI_FORMAT_ZERO) ? '0' : ' ';
+
+ /* Calculate size according to sign and prefix */
+
+ sign = '\0';
+ if (type & ACPI_FORMAT_SIGN) {
+ if ((s64)number < 0) {
+ sign = '-';
+ number = -(s64)number;
+ width--;
+ } else if (type & ACPI_FORMAT_SIGN_PLUS) {
+ sign = '+';
+ width--;
+ } else if (type & ACPI_FORMAT_SIGN_PLUS_SPACE) {
+ sign = ' ';
+ width--;
+ }
+ }
+ if (need_prefix) {
+ width--;
+ if (base == 16) {
+ width--;
+ }
+ }
+
+ /* Generate full string in reverse order */
+
+ pos = acpi_ut_put_number(reversed_string, number, base, upper);
+ i = (s32)ACPI_PTR_DIFF(pos, reversed_string);
+
+ /* Printing 100 using %2d gives "100", not "00" */
+
+ if (i > precision) {
+ precision = i;
+ }
+
+ width -= precision;
+
+ /* Output the string */
+
+ if (!(type & (ACPI_FORMAT_ZERO | ACPI_FORMAT_LEFT))) {
+ while (--width >= 0) {
+ string = acpi_ut_bound_string_output(string, end, ' ');
+ }
+ }
+ if (sign) {
+ string = acpi_ut_bound_string_output(string, end, sign);
+ }
+ if (need_prefix) {
+ string = acpi_ut_bound_string_output(string, end, '0');
+ if (base == 16) {
+ string =
+ acpi_ut_bound_string_output(string, end,
+ upper ? 'X' : 'x');
+ }
+ }
+ if (!(type & ACPI_FORMAT_LEFT)) {
+ while (--width >= 0) {
+ string = acpi_ut_bound_string_output(string, end, zero);
+ }
+ }
+
+ while (i <= --precision) {
+ string = acpi_ut_bound_string_output(string, end, '0');
+ }
+ while (--i >= 0) {
+ string = acpi_ut_bound_string_output(string, end,
+ reversed_string[i]);
+ }
+ while (--width >= 0) {
+ string = acpi_ut_bound_string_output(string, end, ' ');
+ }
+
+ return (string);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: vsnprintf
+ *
+ * PARAMETERS: string - String with boundary
+ * size - Boundary of the string
+ * format - Standard printf format
+ * args - Argument list
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to a string using argument list pointer.
+ *
+ ******************************************************************************/
+
+int vsnprintf(char *string, acpi_size size, const char *format, va_list args)
+{
+ u8 base;
+ u8 type;
+ s32 width;
+ s32 precision;
+ char qualifier;
+ u64 number;
+ char *pos;
+ char *end;
+ char c;
+ const char *s;
+ const void *p;
+ s32 length;
+ int i;
+
+ pos = string;
+
+ if (size != ACPI_UINT32_MAX) {
+ end = string + size;
+ } else {
+ end = ACPI_CAST_PTR(char, ACPI_UINT32_MAX);
+ }
+
+ for (; *format; ++format) {
+ if (*format != '%') {
+ pos = acpi_ut_bound_string_output(pos, end, *format);
+ continue;
+ }
+
+ type = 0;
+ base = 10;
+
+ /* Process sign */
+
+ do {
+ ++format;
+ if (*format == '#') {
+ type |= ACPI_FORMAT_PREFIX;
+ } else if (*format == '0') {
+ type |= ACPI_FORMAT_ZERO;
+ } else if (*format == '+') {
+ type |= ACPI_FORMAT_SIGN_PLUS;
+ } else if (*format == ' ') {
+ type |= ACPI_FORMAT_SIGN_PLUS_SPACE;
+ } else if (*format == '-') {
+ type |= ACPI_FORMAT_LEFT;
+ } else {
+ break;
+ }
+
+ } while (1);
+
+ /* Process width */
+
+ width = -1;
+ if (isdigit((int)*format)) {
+ format = acpi_ut_scan_number(format, &number);
+ width = (s32)number;
+ } else if (*format == '*') {
+ ++format;
+ width = va_arg(args, int);
+ if (width < 0) {
+ width = -width;
+ type |= ACPI_FORMAT_LEFT;
+ }
+ }
+
+ /* Process precision */
+
+ precision = -1;
+ if (*format == '.') {
+ ++format;
+ if (isdigit((int)*format)) {
+ format = acpi_ut_scan_number(format, &number);
+ precision = (s32)number;
+ } else if (*format == '*') {
+ ++format;
+ precision = va_arg(args, int);
+ }
+
+ if (precision < 0) {
+ precision = 0;
+ }
+ }
+
+ /* Process qualifier */
+
+ qualifier = -1;
+ if (*format == 'h' || *format == 'l' || *format == 'L') {
+ qualifier = *format;
+ ++format;
+
+ if (qualifier == 'l' && *format == 'l') {
+ qualifier = 'L';
+ ++format;
+ }
+ }
+
+ switch (*format) {
+ case '%':
+
+ pos = acpi_ut_bound_string_output(pos, end, '%');
+ continue;
+
+ case 'c':
+
+ if (!(type & ACPI_FORMAT_LEFT)) {
+ while (--width > 0) {
+ pos =
+ acpi_ut_bound_string_output(pos,
+ end,
+ ' ');
+ }
+ }
+
+ c = (char)va_arg(args, int);
+ pos = acpi_ut_bound_string_output(pos, end, c);
+
+ while (--width > 0) {
+ pos =
+ acpi_ut_bound_string_output(pos, end, ' ');
+ }
+ continue;
+
+ case 's':
+
+ s = va_arg(args, char *);
+ if (!s) {
+ s = "<NULL>";
+ }
+ length = (s32)acpi_ut_bound_string_length(s, precision);
+ if (!(type & ACPI_FORMAT_LEFT)) {
+ while (length < width--) {
+ pos =
+ acpi_ut_bound_string_output(pos,
+ end,
+ ' ');
+ }
+ }
+
+ for (i = 0; i < length; ++i) {
+ pos = acpi_ut_bound_string_output(pos, end, *s);
+ ++s;
+ }
+
+ while (length < width--) {
+ pos =
+ acpi_ut_bound_string_output(pos, end, ' ');
+ }
+ continue;
+
+ case 'o':
+
+ base = 8;
+ break;
+
+ case 'X':
+
+ type |= ACPI_FORMAT_UPPER;
+ ACPI_FALLTHROUGH;
+
+ case 'x':
+
+ base = 16;
+ break;
+
+ case 'd':
+ case 'i':
+
+ type |= ACPI_FORMAT_SIGN;
+
+ case 'u':
+
+ break;
+
+ case 'p':
+
+ if (width == -1) {
+ width = 2 * sizeof(void *);
+ type |= ACPI_FORMAT_ZERO;
+ }
+
+ p = va_arg(args, void *);
+ pos =
+ acpi_ut_format_number(pos, end, ACPI_TO_INTEGER(p),
+ 16, width, precision, type);
+ continue;
+
+ default:
+
+ pos = acpi_ut_bound_string_output(pos, end, '%');
+ if (*format) {
+ pos =
+ acpi_ut_bound_string_output(pos, end,
+ *format);
+ } else {
+ --format;
+ }
+ continue;
+ }
+
+ if (qualifier == 'L') {
+ number = va_arg(args, u64);
+ if (type & ACPI_FORMAT_SIGN) {
+ number = (s64)number;
+ }
+ } else if (qualifier == 'l') {
+ number = va_arg(args, unsigned long);
+ if (type & ACPI_FORMAT_SIGN) {
+ number = (s32)number;
+ }
+ } else if (qualifier == 'h') {
+ number = (u16)va_arg(args, int);
+ if (type & ACPI_FORMAT_SIGN) {
+ number = (s16)number;
+ }
+ } else {
+ number = va_arg(args, unsigned int);
+ if (type & ACPI_FORMAT_SIGN) {
+ number = (signed int)number;
+ }
+ }
+
+ pos = acpi_ut_format_number(pos, end, number, base,
+ width, precision, type);
+ }
+
+ if (size > 0) {
+ if (pos < end) {
+ *pos = '\0';
+ } else {
+ end[-1] = '\0';
+ }
+ }
+
+ return ((int)ACPI_PTR_DIFF(pos, string));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: snprintf
+ *
+ * PARAMETERS: string - String with boundary
+ * size - Boundary of the string
+ * Format, ... - Standard printf format
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to a string.
+ *
+ ******************************************************************************/
+
+int snprintf(char *string, acpi_size size, const char *format, ...)
+{
+ va_list args;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(string, size, format, args);
+ va_end(args);
+
+ return (length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: sprintf
+ *
+ * PARAMETERS: string - String with boundary
+ * Format, ... - Standard printf format
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to a string.
+ *
+ ******************************************************************************/
+
+int sprintf(char *string, const char *format, ...)
+{
+ va_list args;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(string, ACPI_UINT32_MAX, format, args);
+ va_end(args);
+
+ return (length);
+}
+
+#ifdef ACPI_APPLICATION
+/*******************************************************************************
+ *
+ * FUNCTION: vprintf
+ *
+ * PARAMETERS: format - Standard printf format
+ * args - Argument list
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to stdout using argument list pointer.
+ *
+ ******************************************************************************/
+
+int vprintf(const char *format, va_list args)
+{
+ acpi_cpu_flags flags;
+ int length;
+
+ flags = acpi_os_acquire_lock(acpi_gbl_print_lock);
+ length = vsnprintf(acpi_gbl_print_buffer,
+ sizeof(acpi_gbl_print_buffer), format, args);
+
+ (void)fwrite(acpi_gbl_print_buffer, length, 1, ACPI_FILE_OUT);
+ acpi_os_release_lock(acpi_gbl_print_lock, flags);
+
+ return (length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: printf
+ *
+ * PARAMETERS: Format, ... - Standard printf format
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to stdout.
+ *
+ ******************************************************************************/
+
+int printf(const char *format, ...)
+{
+ va_list args;
+ int length;
+
+ va_start(args, format);
+ length = vprintf(format, args);
+ va_end(args);
+
+ return (length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: vfprintf
+ *
+ * PARAMETERS: file - File descriptor
+ * format - Standard printf format
+ * args - Argument list
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to a file using argument list pointer.
+ *
+ ******************************************************************************/
+
+int vfprintf(FILE * file, const char *format, va_list args)
+{
+ acpi_cpu_flags flags;
+ int length;
+
+ flags = acpi_os_acquire_lock(acpi_gbl_print_lock);
+ length = vsnprintf(acpi_gbl_print_buffer,
+ sizeof(acpi_gbl_print_buffer), format, args);
+
+ (void)fwrite(acpi_gbl_print_buffer, length, 1, file);
+ acpi_os_release_lock(acpi_gbl_print_lock, flags);
+
+ return (length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: fprintf
+ *
+ * PARAMETERS: file - File descriptor
+ * Format, ... - Standard printf format
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to a file.
+ *
+ ******************************************************************************/
+
+int fprintf(FILE * file, const char *format, ...)
+{
+ va_list args;
+ int length;
+
+ va_start(args, format);
+ length = vfprintf(file, format, args);
+ va_end(args);
+
+ return (length);
+}
+#endif
diff --git a/drivers/acpi/acpica/utresdecode.c b/drivers/acpi/acpica/utresdecode.c
new file mode 100644
index 000000000..85730fcd7
--- /dev/null
+++ b/drivers/acpi/acpica/utresdecode.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utresdecode - Resource descriptor keyword strings
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utresdecode")
+
+#if defined (ACPI_DEBUG_OUTPUT) || \
+ defined (ACPI_DISASSEMBLER) || \
+ defined (ACPI_DEBUGGER)
+/*
+ * Strings used to decode resource descriptors.
+ * Used by both the disassembler and the debugger resource dump routines
+ */
+const char *acpi_gbl_bm_decode[] = {
+ "NotBusMaster",
+ "BusMaster"
+};
+
+const char *acpi_gbl_config_decode[] = {
+ "0 - Good Configuration",
+ "1 - Acceptable Configuration",
+ "2 - Suboptimal Configuration",
+ "3 - ***Invalid Configuration***",
+};
+
+const char *acpi_gbl_consume_decode[] = {
+ "ResourceProducer",
+ "ResourceConsumer"
+};
+
+const char *acpi_gbl_dec_decode[] = {
+ "PosDecode",
+ "SubDecode"
+};
+
+const char *acpi_gbl_he_decode[] = {
+ "Level",
+ "Edge"
+};
+
+const char *acpi_gbl_io_decode[] = {
+ "Decode10",
+ "Decode16"
+};
+
+const char *acpi_gbl_ll_decode[] = {
+ "ActiveHigh",
+ "ActiveLow",
+ "ActiveBoth",
+ "Reserved"
+};
+
+const char *acpi_gbl_max_decode[] = {
+ "MaxNotFixed",
+ "MaxFixed"
+};
+
+const char *acpi_gbl_mem_decode[] = {
+ "NonCacheable",
+ "Cacheable",
+ "WriteCombining",
+ "Prefetchable"
+};
+
+const char *acpi_gbl_min_decode[] = {
+ "MinNotFixed",
+ "MinFixed"
+};
+
+const char *acpi_gbl_mtp_decode[] = {
+ "AddressRangeMemory",
+ "AddressRangeReserved",
+ "AddressRangeACPI",
+ "AddressRangeNVS"
+};
+
+const char *acpi_gbl_phy_decode[] = {
+ "Type C",
+ "Type D",
+ "Unknown Type",
+ "Unknown Type"
+};
+
+const char *acpi_gbl_rng_decode[] = {
+ "InvalidRanges",
+ "NonISAOnlyRanges",
+ "ISAOnlyRanges",
+ "EntireRange"
+};
+
+const char *acpi_gbl_rw_decode[] = {
+ "ReadOnly",
+ "ReadWrite"
+};
+
+const char *acpi_gbl_shr_decode[] = {
+ "Exclusive",
+ "Shared",
+ "ExclusiveAndWake", /* ACPI 5.0 */
+ "SharedAndWake" /* ACPI 5.0 */
+};
+
+const char *acpi_gbl_siz_decode[] = {
+ "Transfer8",
+ "Transfer8_16",
+ "Transfer16",
+ "InvalidSize"
+};
+
+const char *acpi_gbl_trs_decode[] = {
+ "DenseTranslation",
+ "SparseTranslation"
+};
+
+const char *acpi_gbl_ttp_decode[] = {
+ "TypeStatic",
+ "TypeTranslation"
+};
+
+const char *acpi_gbl_typ_decode[] = {
+ "Compatibility",
+ "TypeA",
+ "TypeB",
+ "TypeF"
+};
+
+const char *acpi_gbl_ppc_decode[] = {
+ "PullDefault",
+ "PullUp",
+ "PullDown",
+ "PullNone"
+};
+
+const char *acpi_gbl_ior_decode[] = {
+ "IoRestrictionNone",
+ "IoRestrictionInputOnly",
+ "IoRestrictionOutputOnly",
+ "IoRestrictionNoneAndPreserve"
+};
+
+const char *acpi_gbl_dts_decode[] = {
+ "Width8bit",
+ "Width16bit",
+ "Width32bit",
+ "Width64bit",
+ "Width128bit",
+ "Width256bit",
+};
+
+/* GPIO connection type */
+
+const char *acpi_gbl_ct_decode[] = {
+ "Interrupt",
+ "I/O"
+};
+
+/* Serial bus type */
+
+const char *acpi_gbl_sbt_decode[] = {
+ "/* UNKNOWN serial bus type */",
+ "I2C",
+ "SPI",
+ "UART",
+ "CSI2"
+};
+
+/* I2C serial bus access mode */
+
+const char *acpi_gbl_am_decode[] = {
+ "AddressingMode7Bit",
+ "AddressingMode10Bit"
+};
+
+/* I2C serial bus slave mode */
+
+const char *acpi_gbl_sm_decode[] = {
+ "ControllerInitiated",
+ "DeviceInitiated"
+};
+
+/* SPI serial bus wire mode */
+
+const char *acpi_gbl_wm_decode[] = {
+ "FourWireMode",
+ "ThreeWireMode"
+};
+
+/* SPI serial clock phase */
+
+const char *acpi_gbl_cph_decode[] = {
+ "ClockPhaseFirst",
+ "ClockPhaseSecond"
+};
+
+/* SPI serial bus clock polarity */
+
+const char *acpi_gbl_cpo_decode[] = {
+ "ClockPolarityLow",
+ "ClockPolarityHigh"
+};
+
+/* SPI serial bus device polarity */
+
+const char *acpi_gbl_dp_decode[] = {
+ "PolarityLow",
+ "PolarityHigh"
+};
+
+/* UART serial bus endian */
+
+const char *acpi_gbl_ed_decode[] = {
+ "LittleEndian",
+ "BigEndian"
+};
+
+/* UART serial bus bits per byte */
+
+const char *acpi_gbl_bpb_decode[] = {
+ "DataBitsFive",
+ "DataBitsSix",
+ "DataBitsSeven",
+ "DataBitsEight",
+ "DataBitsNine",
+ "/* UNKNOWN Bits per byte */",
+ "/* UNKNOWN Bits per byte */",
+ "/* UNKNOWN Bits per byte */"
+};
+
+/* UART serial bus stop bits */
+
+const char *acpi_gbl_sb_decode[] = {
+ "StopBitsZero",
+ "StopBitsOne",
+ "StopBitsOnePlusHalf",
+ "StopBitsTwo"
+};
+
+/* UART serial bus flow control */
+
+const char *acpi_gbl_fc_decode[] = {
+ "FlowControlNone",
+ "FlowControlHardware",
+ "FlowControlXON",
+ "/* UNKNOWN flow control keyword */"
+};
+
+/* UART serial bus parity type */
+
+const char *acpi_gbl_pt_decode[] = {
+ "ParityTypeNone",
+ "ParityTypeEven",
+ "ParityTypeOdd",
+ "ParityTypeMark",
+ "ParityTypeSpace",
+ "/* UNKNOWN parity keyword */",
+ "/* UNKNOWN parity keyword */",
+ "/* UNKNOWN parity keyword */"
+};
+
+/* pin_config type */
+
+const char *acpi_gbl_ptyp_decode[] = {
+ "Default",
+ "Bias Pull-up",
+ "Bias Pull-down",
+ "Bias Default",
+ "Bias Disable",
+ "Bias High Impedance",
+ "Bias Bus Hold",
+ "Drive Open Drain",
+ "Drive Open Source",
+ "Drive Push Pull",
+ "Drive Strength",
+ "Slew Rate",
+ "Input Debounce",
+ "Input Schmitt Trigger",
+};
+
+#endif
diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c
new file mode 100644
index 000000000..16f9a7035
--- /dev/null
+++ b/drivers/acpi/acpica/utresrc.c
@@ -0,0 +1,565 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utresrc - Resource management utilities
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utresrc")
+
+/*
+ * Base sizes of the raw AML resource descriptors, indexed by resource type.
+ * Zero indicates a reserved (and therefore invalid) resource type.
+ */
+const u8 acpi_gbl_resource_aml_sizes[] = {
+ /* Small descriptors */
+
+ 0,
+ 0,
+ 0,
+ 0,
+ ACPI_AML_SIZE_SMALL(struct aml_resource_irq),
+ ACPI_AML_SIZE_SMALL(struct aml_resource_dma),
+ ACPI_AML_SIZE_SMALL(struct aml_resource_start_dependent),
+ ACPI_AML_SIZE_SMALL(struct aml_resource_end_dependent),
+ ACPI_AML_SIZE_SMALL(struct aml_resource_io),
+ ACPI_AML_SIZE_SMALL(struct aml_resource_fixed_io),
+ ACPI_AML_SIZE_SMALL(struct aml_resource_fixed_dma),
+ 0,
+ 0,
+ 0,
+ ACPI_AML_SIZE_SMALL(struct aml_resource_vendor_small),
+ ACPI_AML_SIZE_SMALL(struct aml_resource_end_tag),
+
+ /* Large descriptors */
+
+ 0,
+ ACPI_AML_SIZE_LARGE(struct aml_resource_memory24),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_generic_register),
+ 0,
+ ACPI_AML_SIZE_LARGE(struct aml_resource_vendor_large),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_memory32),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_fixed_memory32),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_address32),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_address16),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_extended_irq),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_address64),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_extended_address64),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_gpio),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_pin_function),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_common_serialbus),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_pin_config),
+ 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),
+};
+
+const u8 acpi_gbl_resource_aml_serial_bus_sizes[] = {
+ 0,
+ ACPI_AML_SIZE_LARGE(struct aml_resource_i2c_serialbus),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_spi_serialbus),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_uart_serialbus),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_csi2_serialbus),
+};
+
+/*
+ * Resource types, used to validate the resource length field.
+ * The length of fixed-length types must match exactly, variable
+ * lengths must meet the minimum required length, etc.
+ * Zero indicates a reserved (and therefore invalid) resource type.
+ */
+static const u8 acpi_gbl_resource_types[] = {
+ /* Small descriptors */
+
+ 0,
+ 0,
+ 0,
+ 0,
+ ACPI_SMALL_VARIABLE_LENGTH, /* 04 IRQ */
+ ACPI_FIXED_LENGTH, /* 05 DMA */
+ ACPI_SMALL_VARIABLE_LENGTH, /* 06 start_dependent_functions */
+ ACPI_FIXED_LENGTH, /* 07 end_dependent_functions */
+ ACPI_FIXED_LENGTH, /* 08 IO */
+ ACPI_FIXED_LENGTH, /* 09 fixed_IO */
+ ACPI_FIXED_LENGTH, /* 0A fixed_DMA */
+ 0,
+ 0,
+ 0,
+ ACPI_VARIABLE_LENGTH, /* 0E vendor_short */
+ ACPI_FIXED_LENGTH, /* 0F end_tag */
+
+ /* Large descriptors */
+
+ 0,
+ ACPI_FIXED_LENGTH, /* 01 Memory24 */
+ ACPI_FIXED_LENGTH, /* 02 generic_register */
+ 0,
+ ACPI_VARIABLE_LENGTH, /* 04 vendor_long */
+ ACPI_FIXED_LENGTH, /* 05 Memory32 */
+ ACPI_FIXED_LENGTH, /* 06 memory32_fixed */
+ ACPI_VARIABLE_LENGTH, /* 07 Dword* address */
+ ACPI_VARIABLE_LENGTH, /* 08 Word* address */
+ ACPI_VARIABLE_LENGTH, /* 09 extended_IRQ */
+ ACPI_VARIABLE_LENGTH, /* 0A Qword* address */
+ ACPI_FIXED_LENGTH, /* 0B Extended* address */
+ ACPI_VARIABLE_LENGTH, /* 0C Gpio* */
+ ACPI_VARIABLE_LENGTH, /* 0D pin_function */
+ ACPI_VARIABLE_LENGTH, /* 0E *serial_bus */
+ ACPI_VARIABLE_LENGTH, /* 0F pin_config */
+ ACPI_VARIABLE_LENGTH, /* 10 pin_group */
+ ACPI_VARIABLE_LENGTH, /* 11 pin_group_function */
+ ACPI_VARIABLE_LENGTH, /* 12 pin_group_config */
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_walk_aml_resources
+ *
+ * PARAMETERS: walk_state - Current walk info
+ * PARAMETERS: aml - Pointer to the raw AML resource template
+ * aml_length - Length of the entire template
+ * user_function - Called once for each descriptor found. If
+ * NULL, a pointer to the end_tag is returned
+ * context - Passed to user_function
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Walk a raw AML resource list(buffer). User function called
+ * once for each resource found.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_walk_aml_resources(struct acpi_walk_state *walk_state,
+ u8 *aml,
+ acpi_size aml_length,
+ acpi_walk_aml_callback user_function, void **context)
+{
+ acpi_status status;
+ u8 *end_aml;
+ u8 resource_index;
+ u32 length;
+ u32 offset = 0;
+ u8 end_tag[2] = { 0x79, 0x00 };
+
+ ACPI_FUNCTION_TRACE(ut_walk_aml_resources);
+
+ /* The absolute minimum resource template is one end_tag descriptor */
+
+ if (aml_length < sizeof(struct aml_resource_end_tag)) {
+ return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG);
+ }
+
+ /* Point to the end of the resource template buffer */
+
+ end_aml = aml + aml_length;
+
+ /* Walk the byte list, abort on any invalid descriptor type or length */
+
+ while (aml < end_aml) {
+
+ /* Validate the Resource Type and Resource Length */
+
+ status =
+ acpi_ut_validate_resource(walk_state, aml, &resource_index);
+ if (ACPI_FAILURE(status)) {
+ /*
+ * Exit on failure. Cannot continue because the descriptor
+ * length may be bogus also.
+ */
+ return_ACPI_STATUS(status);
+ }
+
+ /* Get the length of this descriptor */
+
+ length = acpi_ut_get_descriptor_length(aml);
+
+ /* Invoke the user function */
+
+ if (user_function) {
+ status =
+ user_function(aml, length, offset, resource_index,
+ context);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /* An end_tag descriptor terminates this resource template */
+
+ if (acpi_ut_get_resource_type(aml) ==
+ ACPI_RESOURCE_NAME_END_TAG) {
+ /*
+ * There must be at least one more byte in the buffer for
+ * the 2nd byte of the end_tag
+ */
+ if ((aml + 1) >= end_aml) {
+ return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG);
+ }
+
+ /*
+ * Don't attempt to perform any validation on the 2nd byte.
+ * Although all known ASL compilers insert a zero for the 2nd
+ * byte, it can also be a checksum (as per the ACPI spec),
+ * and this is occasionally seen in the field. July 2017.
+ */
+
+ /* Return the pointer to the end_tag if requested */
+
+ if (!user_function) {
+ *context = aml;
+ }
+
+ /* Normal exit */
+
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ aml += length;
+ offset += length;
+ }
+
+ /* Did not find an end_tag descriptor */
+
+ if (user_function) {
+
+ /* Insert an end_tag anyway. acpi_rs_get_list_length always leaves room */
+
+ (void)acpi_ut_validate_resource(walk_state, end_tag,
+ &resource_index);
+ status =
+ user_function(end_tag, 2, offset, resource_index, context);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_validate_resource
+ *
+ * PARAMETERS: walk_state - Current walk info
+ * aml - Pointer to the raw AML resource descriptor
+ * return_index - Where the resource index is returned. NULL
+ * if the index is not required.
+ *
+ * RETURN: Status, and optionally the Index into the global resource tables
+ *
+ * DESCRIPTION: Validate an AML resource descriptor by checking the Resource
+ * Type and Resource Length. Returns an index into the global
+ * resource information/dispatch tables for later use.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_validate_resource(struct acpi_walk_state *walk_state,
+ void *aml, u8 *return_index)
+{
+ union aml_resource *aml_resource;
+ u8 resource_type;
+ u8 resource_index;
+ acpi_rs_length resource_length;
+ acpi_rs_length minimum_resource_length;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * 1) Validate the resource_type field (Byte 0)
+ */
+ resource_type = ACPI_GET8(aml);
+
+ /*
+ * Byte 0 contains the descriptor name (Resource Type)
+ * Examine the large/small bit in the resource header
+ */
+ if (resource_type & ACPI_RESOURCE_NAME_LARGE) {
+
+ /* Verify the large resource type (name) against the max */
+
+ if (resource_type > ACPI_RESOURCE_NAME_LARGE_MAX) {
+ goto invalid_resource;
+ }
+
+ /*
+ * Large Resource Type -- bits 6:0 contain the name
+ * Translate range 0x80-0x8B to index range 0x10-0x1B
+ */
+ resource_index = (u8) (resource_type - 0x70);
+ } else {
+ /*
+ * Small Resource Type -- bits 6:3 contain the name
+ * Shift range to index range 0x00-0x0F
+ */
+ resource_index = (u8)
+ ((resource_type & ACPI_RESOURCE_NAME_SMALL_MASK) >> 3);
+ }
+
+ /*
+ * Check validity of the resource type, via acpi_gbl_resource_types.
+ * Zero indicates an invalid resource.
+ */
+ if (!acpi_gbl_resource_types[resource_index]) {
+ goto invalid_resource;
+ }
+
+ /*
+ * Validate the resource_length field. This ensures that the length
+ * is at least reasonable, and guarantees that it is non-zero.
+ */
+ resource_length = acpi_ut_get_resource_length(aml);
+ minimum_resource_length = acpi_gbl_resource_aml_sizes[resource_index];
+
+ /* Validate based upon the type of resource - fixed length or variable */
+
+ switch (acpi_gbl_resource_types[resource_index]) {
+ case ACPI_FIXED_LENGTH:
+
+ /* Fixed length resource, length must match exactly */
+
+ if (resource_length != minimum_resource_length) {
+ goto bad_resource_length;
+ }
+ break;
+
+ case ACPI_VARIABLE_LENGTH:
+
+ /* Variable length resource, length must be at least the minimum */
+
+ if (resource_length < minimum_resource_length) {
+ goto bad_resource_length;
+ }
+ break;
+
+ case ACPI_SMALL_VARIABLE_LENGTH:
+
+ /* Small variable length resource, length can be (Min) or (Min-1) */
+
+ if ((resource_length > minimum_resource_length) ||
+ (resource_length < (minimum_resource_length - 1))) {
+ goto bad_resource_length;
+ }
+ break;
+
+ default:
+
+ /* Shouldn't happen (because of validation earlier), but be sure */
+
+ goto invalid_resource;
+ }
+
+ aml_resource = ACPI_CAST_PTR(union aml_resource, aml);
+ if (resource_type == ACPI_RESOURCE_NAME_SERIAL_BUS) {
+
+ /* Validate the bus_type field */
+
+ if ((aml_resource->common_serial_bus.type == 0) ||
+ (aml_resource->common_serial_bus.type >
+ AML_RESOURCE_MAX_SERIALBUSTYPE)) {
+ if (walk_state) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid/unsupported SerialBus resource descriptor: BusType 0x%2.2X",
+ aml_resource->common_serial_bus.
+ type));
+ }
+ return (AE_AML_INVALID_RESOURCE_TYPE);
+ }
+ }
+
+ /* Optionally return the resource table index */
+
+ if (return_index) {
+ *return_index = resource_index;
+ }
+
+ return (AE_OK);
+
+invalid_resource:
+
+ if (walk_state) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid/unsupported resource descriptor: Type 0x%2.2X",
+ resource_type));
+ }
+ return (AE_AML_INVALID_RESOURCE_TYPE);
+
+bad_resource_length:
+
+ if (walk_state) {
+ ACPI_ERROR((AE_INFO,
+ "Invalid resource descriptor length: Type "
+ "0x%2.2X, Length 0x%4.4X, MinLength 0x%4.4X",
+ resource_type, resource_length,
+ minimum_resource_length));
+ }
+ return (AE_AML_BAD_RESOURCE_LENGTH);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_resource_type
+ *
+ * PARAMETERS: aml - Pointer to the raw AML resource descriptor
+ *
+ * RETURN: The Resource Type with no extraneous bits (except the
+ * Large/Small descriptor bit -- this is left alone)
+ *
+ * DESCRIPTION: Extract the Resource Type/Name from the first byte of
+ * a resource descriptor.
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_get_resource_type(void *aml)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * Byte 0 contains the descriptor name (Resource Type)
+ * Examine the large/small bit in the resource header
+ */
+ if (ACPI_GET8(aml) & ACPI_RESOURCE_NAME_LARGE) {
+
+ /* Large Resource Type -- bits 6:0 contain the name */
+
+ return (ACPI_GET8(aml));
+ } else {
+ /* Small Resource Type -- bits 6:3 contain the name */
+
+ return ((u8) (ACPI_GET8(aml) & ACPI_RESOURCE_NAME_SMALL_MASK));
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_resource_length
+ *
+ * PARAMETERS: aml - Pointer to the raw AML resource descriptor
+ *
+ * RETURN: Byte Length
+ *
+ * DESCRIPTION: Get the "Resource Length" of a raw AML descriptor. By
+ * definition, this does not include the size of the descriptor
+ * header or the length field itself.
+ *
+ ******************************************************************************/
+
+u16 acpi_ut_get_resource_length(void *aml)
+{
+ acpi_rs_length resource_length;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * Byte 0 contains the descriptor name (Resource Type)
+ * Examine the large/small bit in the resource header
+ */
+ if (ACPI_GET8(aml) & ACPI_RESOURCE_NAME_LARGE) {
+
+ /* Large Resource type -- bytes 1-2 contain the 16-bit length */
+
+ ACPI_MOVE_16_TO_16(&resource_length, ACPI_ADD_PTR(u8, aml, 1));
+
+ } else {
+ /* Small Resource type -- bits 2:0 of byte 0 contain the length */
+
+ resource_length = (u16) (ACPI_GET8(aml) &
+ ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK);
+ }
+
+ return (resource_length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_resource_header_length
+ *
+ * PARAMETERS: aml - Pointer to the raw AML resource descriptor
+ *
+ * RETURN: Length of the AML header (depends on large/small descriptor)
+ *
+ * DESCRIPTION: Get the length of the header for this resource.
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_get_resource_header_length(void *aml)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /* Examine the large/small bit in the resource header */
+
+ if (ACPI_GET8(aml) & ACPI_RESOURCE_NAME_LARGE) {
+ return (sizeof(struct aml_resource_large_header));
+ } else {
+ return (sizeof(struct aml_resource_small_header));
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_descriptor_length
+ *
+ * PARAMETERS: aml - Pointer to the raw AML resource descriptor
+ *
+ * RETURN: Byte length
+ *
+ * DESCRIPTION: Get the total byte length of a raw AML descriptor, including the
+ * length of the descriptor header and the length field itself.
+ * Used to walk descriptor lists.
+ *
+ ******************************************************************************/
+
+u32 acpi_ut_get_descriptor_length(void *aml)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /*
+ * Get the Resource Length (does not include header length) and add
+ * the header length (depends on if this is a small or large resource)
+ */
+ return (acpi_ut_get_resource_length(aml) +
+ acpi_ut_get_resource_header_length(aml));
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_resource_end_tag
+ *
+ * PARAMETERS: obj_desc - The resource template buffer object
+ * end_tag - Where the pointer to the end_tag is returned
+ *
+ * RETURN: Status, pointer to the end tag
+ *
+ * DESCRIPTION: Find the end_tag resource descriptor in an AML resource template
+ * Note: allows a buffer length of zero.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_get_resource_end_tag(union acpi_operand_object *obj_desc, u8 **end_tag)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ut_get_resource_end_tag);
+
+ /* Allow a buffer length of zero */
+
+ if (!obj_desc->buffer.length) {
+ *end_tag = obj_desc->buffer.pointer;
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Validate the template and get a pointer to the end_tag */
+
+ status = acpi_ut_walk_aml_resources(NULL, obj_desc->buffer.pointer,
+ obj_desc->buffer.length, NULL,
+ (void **)end_tag);
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/utstate.c b/drivers/acpi/acpica/utstate.c
new file mode 100644
index 000000000..a2484556a
--- /dev/null
+++ b/drivers/acpi/acpica/utstate.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utstate - state object support procedures
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utstate")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_push_generic_state
+ *
+ * PARAMETERS: list_head - Head of the state stack
+ * state - State object to push
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Push a state object onto a state stack
+ *
+ ******************************************************************************/
+void
+acpi_ut_push_generic_state(union acpi_generic_state **list_head,
+ union acpi_generic_state *state)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /* Push the state object onto the front of the list (stack) */
+
+ state->common.next = *list_head;
+ *list_head = state;
+ return;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_pop_generic_state
+ *
+ * PARAMETERS: list_head - Head of the state stack
+ *
+ * RETURN: The popped state object
+ *
+ * DESCRIPTION: Pop a state object from a state stack
+ *
+ ******************************************************************************/
+
+union acpi_generic_state *acpi_ut_pop_generic_state(union acpi_generic_state
+ **list_head)
+{
+ union acpi_generic_state *state;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Remove the state object at the head of the list (stack) */
+
+ state = *list_head;
+ if (state) {
+
+ /* Update the list head */
+
+ *list_head = state->common.next;
+ }
+
+ return (state);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_generic_state
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: The new state object. NULL on failure.
+ *
+ * DESCRIPTION: Create a generic state object. Attempt to obtain one from
+ * the global state cache; If none available, create a new one.
+ *
+ ******************************************************************************/
+
+union acpi_generic_state *acpi_ut_create_generic_state(void)
+{
+ union acpi_generic_state *state;
+
+ ACPI_FUNCTION_ENTRY();
+
+ state = acpi_os_acquire_object(acpi_gbl_state_cache);
+ if (state) {
+
+ /* Initialize */
+ state->common.descriptor_type = ACPI_DESC_TYPE_STATE;
+ }
+
+ return (state);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_thread_state
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: New Thread State. NULL on failure
+ *
+ * DESCRIPTION: Create a "Thread State" - a flavor of the generic state used
+ * to track per-thread info during method execution
+ *
+ ******************************************************************************/
+
+struct acpi_thread_state *acpi_ut_create_thread_state(void)
+{
+ union acpi_generic_state *state;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Create the generic state object */
+
+ state = acpi_ut_create_generic_state();
+ if (!state) {
+ return (NULL);
+ }
+
+ /* Init fields specific to the update struct */
+
+ state->common.descriptor_type = ACPI_DESC_TYPE_STATE_THREAD;
+ state->thread.thread_id = acpi_os_get_thread_id();
+
+ /* Check for invalid thread ID - zero is very bad, it will break things */
+
+ if (!state->thread.thread_id) {
+ ACPI_ERROR((AE_INFO, "Invalid zero ID from AcpiOsGetThreadId"));
+ state->thread.thread_id = (acpi_thread_id) 1;
+ }
+
+ return ((struct acpi_thread_state *)state);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_update_state
+ *
+ * PARAMETERS: object - Initial Object to be installed in the state
+ * action - Update action to be performed
+ *
+ * RETURN: New state object, null on failure
+ *
+ * DESCRIPTION: Create an "Update State" - a flavor of the generic state used
+ * to update reference counts and delete complex objects such
+ * as packages.
+ *
+ ******************************************************************************/
+
+union acpi_generic_state *acpi_ut_create_update_state(union acpi_operand_object
+ *object, u16 action)
+{
+ union acpi_generic_state *state;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Create the generic state object */
+
+ state = acpi_ut_create_generic_state();
+ if (!state) {
+ return (NULL);
+ }
+
+ /* Init fields specific to the update struct */
+
+ state->common.descriptor_type = ACPI_DESC_TYPE_STATE_UPDATE;
+ state->update.object = object;
+ state->update.value = action;
+ return (state);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_pkg_state
+ *
+ * PARAMETERS: object - Initial Object to be installed in the state
+ * action - Update action to be performed
+ *
+ * RETURN: New state object, null on failure
+ *
+ * DESCRIPTION: Create a "Package State"
+ *
+ ******************************************************************************/
+
+union acpi_generic_state *acpi_ut_create_pkg_state(void *internal_object,
+ void *external_object,
+ u32 index)
+{
+ union acpi_generic_state *state;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Create the generic state object */
+
+ state = acpi_ut_create_generic_state();
+ if (!state) {
+ return (NULL);
+ }
+
+ /* Init fields specific to the update struct */
+
+ state->common.descriptor_type = ACPI_DESC_TYPE_STATE_PACKAGE;
+ state->pkg.source_object = (union acpi_operand_object *)internal_object;
+ state->pkg.dest_object = external_object;
+ state->pkg.index = index;
+ state->pkg.num_packages = 1;
+
+ return (state);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_control_state
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: New state object, null on failure
+ *
+ * DESCRIPTION: Create a "Control State" - a flavor of the generic state used
+ * to support nested IF/WHILE constructs in the AML.
+ *
+ ******************************************************************************/
+
+union acpi_generic_state *acpi_ut_create_control_state(void)
+{
+ union acpi_generic_state *state;
+
+ ACPI_FUNCTION_ENTRY();
+
+ /* Create the generic state object */
+
+ state = acpi_ut_create_generic_state();
+ if (!state) {
+ return (NULL);
+ }
+
+ /* Init fields specific to the control struct */
+
+ state->common.descriptor_type = ACPI_DESC_TYPE_STATE_CONTROL;
+ state->common.state = ACPI_CONTROL_CONDITIONAL_EXECUTING;
+
+ return (state);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_delete_generic_state
+ *
+ * PARAMETERS: state - The state object to be deleted
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Release a state object to the state cache. NULL state objects
+ * are ignored.
+ *
+ ******************************************************************************/
+
+void acpi_ut_delete_generic_state(union acpi_generic_state *state)
+{
+ ACPI_FUNCTION_ENTRY();
+
+ /* Ignore null state */
+
+ if (state) {
+ (void)acpi_os_release_object(acpi_gbl_state_cache, state);
+ }
+
+ return;
+}
diff --git a/drivers/acpi/acpica/utstring.c b/drivers/acpi/acpica/utstring.c
new file mode 100644
index 000000000..c39b54830
--- /dev/null
+++ b/drivers/acpi/acpica/utstring.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utstring - Common functions for strings and characters
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utstring")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_print_string
+ *
+ * PARAMETERS: string - Null terminated ASCII string
+ * max_length - Maximum output length. Used to constrain the
+ * length of strings during debug output only.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump an ASCII string with support for ACPI-defined escape
+ * sequences.
+ *
+ ******************************************************************************/
+void acpi_ut_print_string(char *string, u16 max_length)
+{
+ u32 i;
+
+ if (!string) {
+ acpi_os_printf("<\"NULL STRING PTR\">");
+ return;
+ }
+
+ acpi_os_printf("\"");
+ for (i = 0; (i < max_length) && string[i]; i++) {
+
+ /* Escape sequences */
+
+ switch (string[i]) {
+ case 0x07:
+
+ acpi_os_printf("\\a"); /* BELL */
+ break;
+
+ case 0x08:
+
+ acpi_os_printf("\\b"); /* BACKSPACE */
+ break;
+
+ case 0x0C:
+
+ acpi_os_printf("\\f"); /* FORMFEED */
+ break;
+
+ case 0x0A:
+
+ acpi_os_printf("\\n"); /* LINEFEED */
+ break;
+
+ case 0x0D:
+
+ acpi_os_printf("\\r"); /* CARRIAGE RETURN */
+ break;
+
+ case 0x09:
+
+ acpi_os_printf("\\t"); /* HORIZONTAL TAB */
+ break;
+
+ case 0x0B:
+
+ acpi_os_printf("\\v"); /* VERTICAL TAB */
+ break;
+
+ case '\'': /* Single Quote */
+ case '\"': /* Double Quote */
+ case '\\': /* Backslash */
+
+ acpi_os_printf("\\%c", (int)string[i]);
+ break;
+
+ default:
+
+ /* Check for printable character or hex escape */
+
+ if (isprint((int)string[i])) {
+ /* This is a normal character */
+
+ acpi_os_printf("%c", (int)string[i]);
+ } else {
+ /* All others will be Hex escapes */
+
+ acpi_os_printf("\\x%2.2X", (s32)string[i]);
+ }
+ break;
+ }
+ }
+
+ acpi_os_printf("\"");
+
+ if (i == max_length && string[i]) {
+ acpi_os_printf("...");
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_repair_name
+ *
+ * PARAMETERS: name - The ACPI name to be repaired
+ *
+ * RETURN: Repaired version of the name
+ *
+ * DESCRIPTION: Repair an ACPI name: Change invalid characters to '*' and
+ * return the new name. NOTE: the Name parameter must reside in
+ * read/write memory, cannot be a const.
+ *
+ * An ACPI Name must consist of valid ACPI characters. We will repair the name
+ * if necessary because we don't want to abort because of this, but we want
+ * all namespace names to be printable. A warning message is appropriate.
+ *
+ * This issue came up because there are in fact machines that exhibit
+ * this problem, and we want to be able to enable ACPI support for them,
+ * even though there are a few bad names.
+ *
+ ******************************************************************************/
+
+void acpi_ut_repair_name(char *name)
+{
+ u32 i;
+ u8 found_bad_char = FALSE;
+ u32 original_name;
+
+ ACPI_FUNCTION_NAME(ut_repair_name);
+
+ /*
+ * Special case for the root node. This can happen if we get an
+ * error during the execution of module-level code.
+ */
+ if (ACPI_COMPARE_NAMESEG(name, ACPI_ROOT_PATHNAME)) {
+ return;
+ }
+
+ ACPI_COPY_NAMESEG(&original_name, name);
+
+ /* Check each character in the name */
+
+ for (i = 0; i < ACPI_NAMESEG_SIZE; i++) {
+ if (acpi_ut_valid_name_char(name[i], i)) {
+ continue;
+ }
+
+ /*
+ * Replace a bad character with something printable, yet technically
+ * still invalid. This prevents any collisions with existing "good"
+ * names in the namespace.
+ */
+ name[i] = '*';
+ found_bad_char = TRUE;
+ }
+
+ if (found_bad_char) {
+
+ /* Report warning only if in strict mode or debug mode */
+
+ if (!acpi_gbl_enable_interpreter_slack) {
+ ACPI_WARNING((AE_INFO,
+ "Invalid character(s) in name (0x%.8X), repaired: [%4.4s]",
+ original_name, name));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Invalid character(s) in name (0x%.8X), repaired: [%4.4s]",
+ original_name, name));
+ }
+ }
+}
+
+#if defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP
+/*******************************************************************************
+ *
+ * FUNCTION: ut_convert_backslashes
+ *
+ * PARAMETERS: pathname - File pathname string to be converted
+ *
+ * RETURN: Modifies the input Pathname
+ *
+ * DESCRIPTION: Convert all backslashes (0x5C) to forward slashes (0x2F) within
+ * the entire input file pathname string.
+ *
+ ******************************************************************************/
+
+void ut_convert_backslashes(char *pathname)
+{
+
+ if (!pathname) {
+ return;
+ }
+
+ while (*pathname) {
+ if (*pathname == '\\') {
+ *pathname = '/';
+ }
+
+ pathname++;
+ }
+}
+#endif
diff --git a/drivers/acpi/acpica/utstrsuppt.c b/drivers/acpi/acpica/utstrsuppt.c
new file mode 100644
index 000000000..199982a6f
--- /dev/null
+++ b/drivers/acpi/acpica/utstrsuppt.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utstrsuppt - Support functions for string-to-integer conversion
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utstrsuppt")
+
+/* Local prototypes */
+static acpi_status
+acpi_ut_insert_digit(u64 *accumulated_value, u32 base, int ascii_digit);
+
+static acpi_status
+acpi_ut_strtoul_multiply64(u64 multiplicand, u32 base, u64 *out_product);
+
+static acpi_status acpi_ut_strtoul_add64(u64 addend1, u32 digit, u64 *out_sum);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_convert_octal_string
+ *
+ * PARAMETERS: string - Null terminated input string
+ * return_value_ptr - Where the converted value is returned
+ *
+ * RETURN: Status and 64-bit converted integer
+ *
+ * DESCRIPTION: Performs a base 8 conversion of the input string to an
+ * integer value, either 32 or 64 bits.
+ *
+ * NOTE: Maximum 64-bit unsigned octal value is 01777777777777777777777
+ * Maximum 32-bit unsigned octal value is 037777777777
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_convert_octal_string(char *string, u64 *return_value_ptr)
+{
+ u64 accumulated_value = 0;
+ acpi_status status = AE_OK;
+
+ /* Convert each ASCII byte in the input string */
+
+ while (*string) {
+ /*
+ * Character must be ASCII 0-7, otherwise:
+ * 1) Runtime: terminate with no error, per the ACPI spec
+ * 2) Compiler: return an error
+ */
+ if (!(ACPI_IS_OCTAL_DIGIT(*string))) {
+#ifdef ACPI_ASL_COMPILER
+ status = AE_BAD_OCTAL_CONSTANT;
+#endif
+ break;
+ }
+
+ /* Convert and insert this octal digit into the accumulator */
+
+ status = acpi_ut_insert_digit(&accumulated_value, 8, *string);
+ if (ACPI_FAILURE(status)) {
+ status = AE_OCTAL_OVERFLOW;
+ break;
+ }
+
+ string++;
+ }
+
+ /* Always return the value that has been accumulated */
+
+ *return_value_ptr = accumulated_value;
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_convert_decimal_string
+ *
+ * PARAMETERS: string - Null terminated input string
+ * return_value_ptr - Where the converted value is returned
+ *
+ * RETURN: Status and 64-bit converted integer
+ *
+ * DESCRIPTION: Performs a base 10 conversion of the input string to an
+ * integer value, either 32 or 64 bits.
+ *
+ * NOTE: Maximum 64-bit unsigned decimal value is 18446744073709551615
+ * Maximum 32-bit unsigned decimal value is 4294967295
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_convert_decimal_string(char *string, u64 *return_value_ptr)
+{
+ u64 accumulated_value = 0;
+ acpi_status status = AE_OK;
+
+ /* Convert each ASCII byte in the input string */
+
+ while (*string) {
+ /*
+ * Character must be ASCII 0-9, otherwise:
+ * 1) Runtime: terminate with no error, per the ACPI spec
+ * 2) Compiler: return an error
+ */
+ if (!isdigit((int)*string)) {
+#ifdef ACPI_ASL_COMPILER
+ status = AE_BAD_DECIMAL_CONSTANT;
+#endif
+ break;
+ }
+
+ /* Convert and insert this decimal digit into the accumulator */
+
+ status = acpi_ut_insert_digit(&accumulated_value, 10, *string);
+ if (ACPI_FAILURE(status)) {
+ status = AE_DECIMAL_OVERFLOW;
+ break;
+ }
+
+ string++;
+ }
+
+ /* Always return the value that has been accumulated */
+
+ *return_value_ptr = accumulated_value;
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_convert_hex_string
+ *
+ * PARAMETERS: string - Null terminated input string
+ * return_value_ptr - Where the converted value is returned
+ *
+ * RETURN: Status and 64-bit converted integer
+ *
+ * DESCRIPTION: Performs a base 16 conversion of the input string to an
+ * integer value, either 32 or 64 bits.
+ *
+ * NOTE: Maximum 64-bit unsigned hex value is 0xFFFFFFFFFFFFFFFF
+ * Maximum 32-bit unsigned hex value is 0xFFFFFFFF
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_convert_hex_string(char *string, u64 *return_value_ptr)
+{
+ u64 accumulated_value = 0;
+ acpi_status status = AE_OK;
+
+ /* Convert each ASCII byte in the input string */
+
+ while (*string) {
+ /*
+ * Character must be ASCII A-F, a-f, or 0-9, otherwise:
+ * 1) Runtime: terminate with no error, per the ACPI spec
+ * 2) Compiler: return an error
+ */
+ if (!isxdigit((int)*string)) {
+#ifdef ACPI_ASL_COMPILER
+ status = AE_BAD_HEX_CONSTANT;
+#endif
+ break;
+ }
+
+ /* Convert and insert this hex digit into the accumulator */
+
+ status = acpi_ut_insert_digit(&accumulated_value, 16, *string);
+ if (ACPI_FAILURE(status)) {
+ status = AE_HEX_OVERFLOW;
+ break;
+ }
+
+ string++;
+ }
+
+ /* Always return the value that has been accumulated */
+
+ *return_value_ptr = accumulated_value;
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_remove_leading_zeros
+ *
+ * PARAMETERS: string - Pointer to input ASCII string
+ *
+ * RETURN: Next character after any leading zeros. This character may be
+ * used by the caller to detect end-of-string.
+ *
+ * DESCRIPTION: Remove any leading zeros in the input string. Return the
+ * next character after the final ASCII zero to enable the caller
+ * to check for the end of the string (NULL terminator).
+ *
+ ******************************************************************************/
+
+char acpi_ut_remove_leading_zeros(char **string)
+{
+
+ while (**string == ACPI_ASCII_ZERO) {
+ *string += 1;
+ }
+
+ return (**string);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_remove_whitespace
+ *
+ * PARAMETERS: string - Pointer to input ASCII string
+ *
+ * RETURN: Next character after any whitespace. This character may be
+ * used by the caller to detect end-of-string.
+ *
+ * DESCRIPTION: Remove any leading whitespace in the input string. Return the
+ * next character after the final ASCII zero to enable the caller
+ * to check for the end of the string (NULL terminator).
+ *
+ ******************************************************************************/
+
+char acpi_ut_remove_whitespace(char **string)
+{
+
+ while (isspace((u8)**string)) {
+ *string += 1;
+ }
+
+ return (**string);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_detect_hex_prefix
+ *
+ * PARAMETERS: string - Pointer to input ASCII string
+ *
+ * RETURN: TRUE if a "0x" prefix was found at the start of the string
+ *
+ * DESCRIPTION: Detect and remove a hex "0x" prefix
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_detect_hex_prefix(char **string)
+{
+ char *initial_position = *string;
+
+ acpi_ut_remove_hex_prefix(string);
+ if (*string != initial_position) {
+ return (TRUE); /* String is past leading 0x */
+ }
+
+ return (FALSE); /* Not a hex string */
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_remove_hex_prefix
+ *
+ * PARAMETERS: string - Pointer to input ASCII string
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: Remove a hex "0x" prefix
+ *
+ ******************************************************************************/
+
+void acpi_ut_remove_hex_prefix(char **string)
+{
+ if ((**string == ACPI_ASCII_ZERO) &&
+ (tolower((int)*(*string + 1)) == 'x')) {
+ *string += 2; /* Go past the leading 0x */
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_detect_octal_prefix
+ *
+ * PARAMETERS: string - Pointer to input ASCII string
+ *
+ * RETURN: True if an octal "0" prefix was found at the start of the
+ * string
+ *
+ * DESCRIPTION: Detect and remove an octal prefix (zero)
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_detect_octal_prefix(char **string)
+{
+
+ if (**string == ACPI_ASCII_ZERO) {
+ *string += 1; /* Go past the leading 0 */
+ return (TRUE);
+ }
+
+ return (FALSE); /* Not an octal string */
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_insert_digit
+ *
+ * PARAMETERS: accumulated_value - Current value of the integer value
+ * accumulator. The new value is
+ * returned here.
+ * base - Radix, either 8/10/16
+ * ascii_digit - ASCII single digit to be inserted
+ *
+ * RETURN: Status and result of the convert/insert operation. The only
+ * possible returned exception code is numeric overflow of
+ * either the multiply or add conversion operations.
+ *
+ * DESCRIPTION: Generic conversion and insertion function for all bases:
+ *
+ * 1) Multiply the current accumulated/converted value by the
+ * base in order to make room for the new character.
+ *
+ * 2) Convert the new character to binary and add it to the
+ * current accumulated value.
+ *
+ * Note: The only possible exception indicates an integer
+ * overflow (AE_NUMERIC_OVERFLOW)
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_insert_digit(u64 *accumulated_value, u32 base, int ascii_digit)
+{
+ acpi_status status;
+ u64 product;
+
+ /* Make room in the accumulated value for the incoming digit */
+
+ status = acpi_ut_strtoul_multiply64(*accumulated_value, base, &product);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Add in the new digit, and store the sum to the accumulated value */
+
+ status =
+ acpi_ut_strtoul_add64(product,
+ acpi_ut_ascii_char_to_hex(ascii_digit),
+ accumulated_value);
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_strtoul_multiply64
+ *
+ * PARAMETERS: multiplicand - Current accumulated converted integer
+ * base - Base/Radix
+ * out_product - Where the product is returned
+ *
+ * RETURN: Status and 64-bit product
+ *
+ * DESCRIPTION: Multiply two 64-bit values, with checking for 64-bit overflow as
+ * well as 32-bit overflow if necessary (if the current global
+ * integer width is 32).
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_strtoul_multiply64(u64 multiplicand, u32 base, u64 *out_product)
+{
+ u64 product;
+ u64 quotient;
+
+ /* Exit if either operand is zero */
+
+ *out_product = 0;
+ if (!multiplicand || !base) {
+ return (AE_OK);
+ }
+
+ /*
+ * Check for 64-bit overflow before the actual multiplication.
+ *
+ * Notes: 64-bit division is often not supported on 32-bit platforms
+ * (it requires a library function), Therefore ACPICA has a local
+ * 64-bit divide function. Also, Multiplier is currently only used
+ * as the radix (8/10/16), to the 64/32 divide will always work.
+ */
+ acpi_ut_short_divide(ACPI_UINT64_MAX, base, &quotient, NULL);
+ if (multiplicand > quotient) {
+ return (AE_NUMERIC_OVERFLOW);
+ }
+
+ product = multiplicand * base;
+
+ /* Check for 32-bit overflow if necessary */
+
+ if ((acpi_gbl_integer_bit_width == 32) && (product > ACPI_UINT32_MAX)) {
+ return (AE_NUMERIC_OVERFLOW);
+ }
+
+ *out_product = product;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_strtoul_add64
+ *
+ * PARAMETERS: addend1 - Current accumulated converted integer
+ * digit - New hex value/char
+ * out_sum - Where sum is returned (Accumulator)
+ *
+ * RETURN: Status and 64-bit sum
+ *
+ * DESCRIPTION: Add two 64-bit values, with checking for 64-bit overflow as
+ * well as 32-bit overflow if necessary (if the current global
+ * integer width is 32).
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_ut_strtoul_add64(u64 addend1, u32 digit, u64 *out_sum)
+{
+ u64 sum;
+
+ /* Check for 64-bit overflow before the actual addition */
+
+ if ((addend1 > 0) && (digit > (ACPI_UINT64_MAX - addend1))) {
+ return (AE_NUMERIC_OVERFLOW);
+ }
+
+ sum = addend1 + digit;
+
+ /* Check for 32-bit overflow if necessary */
+
+ if ((acpi_gbl_integer_bit_width == 32) && (sum > ACPI_UINT32_MAX)) {
+ return (AE_NUMERIC_OVERFLOW);
+ }
+
+ *out_sum = sum;
+ return (AE_OK);
+}
diff --git a/drivers/acpi/acpica/utstrtoul64.c b/drivers/acpi/acpica/utstrtoul64.c
new file mode 100644
index 000000000..5fde619a8
--- /dev/null
+++ b/drivers/acpi/acpica/utstrtoul64.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utstrtoul64 - String-to-integer conversion support for both
+ * 64-bit and 32-bit integers
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utstrtoul64")
+
+/*******************************************************************************
+ *
+ * This module contains the top-level string to 64/32-bit unsigned integer
+ * conversion functions:
+ *
+ * 1) A standard strtoul() function that supports 64-bit integers, base
+ * 8/10/16, with integer overflow support. This is used mainly by the
+ * iASL compiler, which implements tighter constraints on integer
+ * constants than the runtime (interpreter) integer-to-string conversions.
+ * 2) Runtime "Explicit conversion" as defined in the ACPI specification.
+ * 3) Runtime "Implicit conversion" as defined in the ACPI specification.
+ *
+ * Current users of this module:
+ *
+ * iASL - Preprocessor (constants and math expressions)
+ * iASL - Main parser, conversion of constants to integers
+ * iASL - Data Table Compiler parser (constants and math expressions)
+ * interpreter - Implicit and explicit conversions, GPE method names
+ * interpreter - Repair code for return values from predefined names
+ * debugger - Command line input string conversion
+ * acpi_dump - ACPI table physical addresses
+ * acpi_exec - Support for namespace overrides
+ *
+ * Notes concerning users of these interfaces:
+ *
+ * acpi_gbl_integer_byte_width is used to set the 32/64 bit limit for explicit
+ * and implicit conversions. This global must be set to the proper width.
+ * For the core ACPICA code, the width depends on the DSDT version. For the
+ * acpi_ut_strtoul64 interface, all conversions are 64 bits. This interface is
+ * used primarily for iASL, where the default width is 64 bits for all parsers,
+ * but error checking is performed later to flag cases where a 64-bit constant
+ * is wrongly defined in a 32-bit DSDT/SSDT.
+ *
+ * In ACPI, the only place where octal numbers are supported is within
+ * the ASL language itself. This is implemented via the main acpi_ut_strtoul64
+ * interface. According the ACPI specification, there is no ACPI runtime
+ * support (explicit/implicit) for octal string conversions.
+ *
+ ******************************************************************************/
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_strtoul64
+ *
+ * PARAMETERS: string - Null terminated input string,
+ * must be a valid pointer
+ * return_value - Where the converted integer is
+ * returned. Must be a valid pointer
+ *
+ * RETURN: Status and converted integer. Returns an exception on a
+ * 64-bit numeric overflow
+ *
+ * DESCRIPTION: Convert a string into an unsigned integer. Always performs a
+ * full 64-bit conversion, regardless of the current global
+ * integer width. Supports Decimal, Hex, and Octal strings.
+ *
+ * Current users of this function:
+ *
+ * iASL - Preprocessor (constants and math expressions)
+ * iASL - Main ASL parser, conversion of ASL constants to integers
+ * iASL - Data Table Compiler parser (constants and math expressions)
+ * interpreter - Repair code for return values from predefined names
+ * acpi_dump - ACPI table physical addresses
+ * acpi_exec - Support for namespace overrides
+ *
+ ******************************************************************************/
+acpi_status acpi_ut_strtoul64(char *string, u64 *return_value)
+{
+ acpi_status status = AE_OK;
+ u8 original_bit_width;
+ u32 base = 10; /* Default is decimal */
+
+ ACPI_FUNCTION_TRACE_STR(ut_strtoul64, string);
+
+ *return_value = 0;
+
+ /* A NULL return string returns a value of zero */
+
+ if (*string == 0) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ if (!acpi_ut_remove_whitespace(&string)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * 1) Check for a hex constant. A "0x" prefix indicates base 16.
+ */
+ if (acpi_ut_detect_hex_prefix(&string)) {
+ base = 16;
+ }
+
+ /*
+ * 2) Check for an octal constant, defined to be a leading zero
+ * followed by sequence of octal digits (0-7)
+ */
+ else if (acpi_ut_detect_octal_prefix(&string)) {
+ base = 8;
+ }
+
+ if (!acpi_ut_remove_leading_zeros(&string)) {
+ return_ACPI_STATUS(AE_OK); /* Return value 0 */
+ }
+
+ /*
+ * Force a full 64-bit conversion. The caller (usually iASL) must
+ * check for a 32-bit overflow later as necessary (If current mode
+ * is 32-bit, meaning a 32-bit DSDT).
+ */
+ original_bit_width = acpi_gbl_integer_bit_width;
+ acpi_gbl_integer_bit_width = 64;
+
+ /*
+ * Perform the base 8, 10, or 16 conversion. A 64-bit numeric overflow
+ * will return an exception (to allow iASL to flag the statement).
+ */
+ switch (base) {
+ case 8:
+ status = acpi_ut_convert_octal_string(string, return_value);
+ break;
+
+ case 10:
+ status = acpi_ut_convert_decimal_string(string, return_value);
+ break;
+
+ case 16:
+ default:
+ status = acpi_ut_convert_hex_string(string, return_value);
+ break;
+ }
+
+ /* Only possible exception from above is a 64-bit overflow */
+
+ acpi_gbl_integer_bit_width = original_bit_width;
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_implicit_strtoul64
+ *
+ * PARAMETERS: string - Null terminated input string,
+ * must be a valid pointer
+ *
+ * RETURN: Converted integer
+ *
+ * DESCRIPTION: Perform a 64-bit conversion with restrictions placed upon
+ * an "implicit conversion" by the ACPI specification. Used by
+ * many ASL operators that require an integer operand, and support
+ * an automatic (implicit) conversion from a string operand
+ * to the final integer operand. The major restriction is that
+ * only hex strings are supported.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Base is always 16, either with or without the 0x prefix. Decimal and
+ * Octal strings are not supported, as per the ACPI specification.
+ *
+ * Examples (both are hex values):
+ * Add ("BA98", Arg0, Local0)
+ * Subtract ("0x12345678", Arg1, Local1)
+ *
+ * Conversion rules as extracted from the ACPI specification:
+ *
+ * The converted integer is initialized to the value zero.
+ * The ASCII string is always interpreted as a hexadecimal constant.
+ *
+ * 1) According to the ACPI specification, a "0x" prefix is not allowed.
+ * However, ACPICA allows this as an ACPI extension on general
+ * principle. (NO ERROR)
+ *
+ * 2) The conversion terminates when the size of an integer is reached
+ * (32 or 64 bits). There are no numeric overflow conditions. (NO ERROR)
+ *
+ * 3) The first non-hex character terminates the conversion and returns
+ * the current accumulated value of the converted integer (NO ERROR).
+ *
+ * 4) Conversion of a null (zero-length) string to an integer is
+ * technically not allowed. However, ACPICA allows this as an ACPI
+ * extension. The conversion returns the value 0. (NO ERROR)
+ *
+ * NOTE: There are no error conditions returned by this function. At
+ * the minimum, a value of zero is returned.
+ *
+ * Current users of this function:
+ *
+ * interpreter - All runtime implicit conversions, as per ACPI specification
+ * iASL - Data Table Compiler parser (constants and math expressions)
+ *
+ ******************************************************************************/
+
+u64 acpi_ut_implicit_strtoul64(char *string)
+{
+ u64 converted_integer = 0;
+
+ ACPI_FUNCTION_TRACE_STR(ut_implicit_strtoul64, string);
+
+ if (!acpi_ut_remove_whitespace(&string)) {
+ return_VALUE(0);
+ }
+
+ /*
+ * Per the ACPI specification, only hexadecimal is supported for
+ * implicit conversions, and the "0x" prefix is "not allowed".
+ * However, allow a "0x" prefix as an ACPI extension.
+ */
+ acpi_ut_remove_hex_prefix(&string);
+
+ if (!acpi_ut_remove_leading_zeros(&string)) {
+ return_VALUE(0);
+ }
+
+ /*
+ * Ignore overflow as per the ACPI specification. This is implemented by
+ * ignoring the return status from the conversion function called below.
+ * On overflow, the input string is simply truncated.
+ */
+ acpi_ut_convert_hex_string(string, &converted_integer);
+ return_VALUE(converted_integer);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_explicit_strtoul64
+ *
+ * PARAMETERS: string - Null terminated input string,
+ * must be a valid pointer
+ *
+ * RETURN: Converted integer
+ *
+ * DESCRIPTION: Perform a 64-bit conversion with the restrictions placed upon
+ * an "explicit conversion" by the ACPI specification. The
+ * main restriction is that only hex and decimal are supported.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Base is either 10 (default) or 16 (with 0x prefix). Octal (base 8) strings
+ * are not supported, as per the ACPI specification.
+ *
+ * Examples:
+ * to_integer ("1000") Decimal
+ * to_integer ("0xABCD") Hex
+ *
+ * Conversion rules as extracted from the ACPI specification:
+ *
+ * 1) The input string is either a decimal or hexadecimal numeric string.
+ * A hex value must be prefixed by "0x" or it is interpreted as decimal.
+ *
+ * 2) The value must not exceed the maximum of an integer value
+ * (32 or 64 bits). The ACPI specification states the behavior is
+ * "unpredictable", so ACPICA matches the behavior of the implicit
+ * conversion case. There are no numeric overflow conditions. (NO ERROR)
+ *
+ * 3) Behavior on the first non-hex character is not defined by the ACPI
+ * specification (for the to_integer operator), so ACPICA matches the
+ * behavior of the implicit conversion case. It terminates the
+ * conversion and returns the current accumulated value of the converted
+ * integer. (NO ERROR)
+ *
+ * 4) Conversion of a null (zero-length) string to an integer is
+ * technically not allowed. However, ACPICA allows this as an ACPI
+ * extension. The conversion returns the value 0. (NO ERROR)
+ *
+ * NOTE: There are no error conditions returned by this function. At the
+ * minimum, a value of zero is returned.
+ *
+ * Current users of this function:
+ *
+ * interpreter - Runtime ASL to_integer operator, as per the ACPI specification
+ *
+ ******************************************************************************/
+
+u64 acpi_ut_explicit_strtoul64(char *string)
+{
+ u64 converted_integer = 0;
+ u32 base = 10; /* Default is decimal */
+
+ ACPI_FUNCTION_TRACE_STR(ut_explicit_strtoul64, string);
+
+ if (!acpi_ut_remove_whitespace(&string)) {
+ return_VALUE(0);
+ }
+
+ /*
+ * Only Hex and Decimal are supported, as per the ACPI specification.
+ * A "0x" prefix indicates hex; otherwise decimal is assumed.
+ */
+ if (acpi_ut_detect_hex_prefix(&string)) {
+ base = 16;
+ }
+
+ if (!acpi_ut_remove_leading_zeros(&string)) {
+ return_VALUE(0);
+ }
+
+ /*
+ * Ignore overflow as per the ACPI specification. This is implemented by
+ * ignoring the return status from the conversion functions called below.
+ * On overflow, the input string is simply truncated.
+ */
+ switch (base) {
+ case 10:
+ default:
+ acpi_ut_convert_decimal_string(string, &converted_integer);
+ break;
+
+ case 16:
+ acpi_ut_convert_hex_string(string, &converted_integer);
+ break;
+ }
+
+ return_VALUE(converted_integer);
+}
diff --git a/drivers/acpi/acpica/uttrack.c b/drivers/acpi/acpica/uttrack.c
new file mode 100644
index 000000000..a06988ac4
--- /dev/null
+++ b/drivers/acpi/acpica/uttrack.c
@@ -0,0 +1,705 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: uttrack - Memory allocation tracking routines (debug only)
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+/*
+ * These procedures are used for tracking memory leaks in the subsystem, and
+ * they get compiled out when the ACPI_DBG_TRACK_ALLOCATIONS is not set.
+ *
+ * Each memory allocation is tracked via a doubly linked list. Each
+ * element contains the caller's component, module name, function name, and
+ * line number. acpi_ut_allocate and acpi_ut_allocate_zeroed call
+ * acpi_ut_track_allocation to add an element to the list; deletion
+ * occurs in the body of acpi_ut_free.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("uttrack")
+
+/* Local prototypes */
+static struct acpi_debug_mem_block *acpi_ut_find_allocation(struct
+ acpi_debug_mem_block
+ *allocation);
+
+static acpi_status
+acpi_ut_track_allocation(struct acpi_debug_mem_block *address,
+ acpi_size size,
+ u8 alloc_type,
+ u32 component, const char *module, u32 line);
+
+static acpi_status
+acpi_ut_remove_allocation(struct acpi_debug_mem_block *address,
+ u32 component, const char *module, u32 line);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_create_list
+ *
+ * PARAMETERS: cache_name - Ascii name for the cache
+ * object_size - Size of each cached object
+ * return_cache - Where the new cache object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Create a local memory list for tracking purposed
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_create_list(const char *list_name,
+ u16 object_size, struct acpi_memory_list **return_cache)
+{
+ struct acpi_memory_list *cache;
+
+ cache = acpi_os_allocate_zeroed(sizeof(struct acpi_memory_list));
+ if (!cache) {
+ return (AE_NO_MEMORY);
+ }
+
+ cache->list_name = list_name;
+ cache->object_size = object_size;
+
+ *return_cache = cache;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_allocate_and_track
+ *
+ * PARAMETERS: size - Size of the allocation
+ * component - Component type of caller
+ * module - Source file name of caller
+ * line - Line number of caller
+ *
+ * RETURN: Address of the allocated memory on success, NULL on failure.
+ *
+ * DESCRIPTION: The subsystem's equivalent of malloc.
+ *
+ ******************************************************************************/
+
+void *acpi_ut_allocate_and_track(acpi_size size,
+ u32 component, const char *module, u32 line)
+{
+ struct acpi_debug_mem_block *allocation;
+ acpi_status status;
+
+ /* Check for an inadvertent size of zero bytes */
+
+ if (!size) {
+ ACPI_WARNING((module, line,
+ "Attempt to allocate zero bytes, allocating 1 byte"));
+ size = 1;
+ }
+
+ allocation =
+ acpi_os_allocate(size + sizeof(struct acpi_debug_mem_header));
+ if (!allocation) {
+
+ /* Report allocation error */
+
+ ACPI_WARNING((module, line,
+ "Could not allocate size %u", (u32)size));
+
+ return (NULL);
+ }
+
+ status =
+ acpi_ut_track_allocation(allocation, size, ACPI_MEM_MALLOC,
+ component, module, line);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_free(allocation);
+ return (NULL);
+ }
+
+ acpi_gbl_global_list->total_allocated++;
+ acpi_gbl_global_list->total_size += (u32)size;
+ acpi_gbl_global_list->current_total_size += (u32)size;
+
+ if (acpi_gbl_global_list->current_total_size >
+ acpi_gbl_global_list->max_occupied) {
+ acpi_gbl_global_list->max_occupied =
+ acpi_gbl_global_list->current_total_size;
+ }
+
+ return ((void *)&allocation->user_space);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_allocate_zeroed_and_track
+ *
+ * PARAMETERS: size - Size of the allocation
+ * component - Component type of caller
+ * module - Source file name of caller
+ * line - Line number of caller
+ *
+ * RETURN: Address of the allocated memory on success, NULL on failure.
+ *
+ * DESCRIPTION: Subsystem equivalent of calloc.
+ *
+ ******************************************************************************/
+
+void *acpi_ut_allocate_zeroed_and_track(acpi_size size,
+ u32 component,
+ const char *module, u32 line)
+{
+ struct acpi_debug_mem_block *allocation;
+ acpi_status status;
+
+ /* Check for an inadvertent size of zero bytes */
+
+ if (!size) {
+ ACPI_WARNING((module, line,
+ "Attempt to allocate zero bytes, allocating 1 byte"));
+ size = 1;
+ }
+
+ allocation =
+ acpi_os_allocate_zeroed(size +
+ sizeof(struct acpi_debug_mem_header));
+ if (!allocation) {
+
+ /* Report allocation error */
+
+ ACPI_ERROR((module, line,
+ "Could not allocate size %u", (u32)size));
+ return (NULL);
+ }
+
+ status = acpi_ut_track_allocation(allocation, size,
+ ACPI_MEM_CALLOC, component, module,
+ line);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_free(allocation);
+ return (NULL);
+ }
+
+ acpi_gbl_global_list->total_allocated++;
+ acpi_gbl_global_list->total_size += (u32)size;
+ acpi_gbl_global_list->current_total_size += (u32)size;
+
+ if (acpi_gbl_global_list->current_total_size >
+ acpi_gbl_global_list->max_occupied) {
+ acpi_gbl_global_list->max_occupied =
+ acpi_gbl_global_list->current_total_size;
+ }
+
+ return ((void *)&allocation->user_space);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_free_and_track
+ *
+ * PARAMETERS: allocation - Address of the memory to deallocate
+ * component - Component type of caller
+ * module - Source file name of caller
+ * line - Line number of caller
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Frees the memory at Allocation
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_free_and_track(void *allocation,
+ u32 component, const char *module, u32 line)
+{
+ struct acpi_debug_mem_block *debug_block;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ut_free, allocation);
+
+ if (NULL == allocation) {
+ ACPI_ERROR((module, line, "Attempt to delete a NULL address"));
+
+ return_VOID;
+ }
+
+ debug_block = ACPI_CAST_PTR(struct acpi_debug_mem_block,
+ (((char *)allocation) -
+ sizeof(struct acpi_debug_mem_header)));
+
+ acpi_gbl_global_list->total_freed++;
+ acpi_gbl_global_list->current_total_size -= debug_block->size;
+
+ status =
+ acpi_ut_remove_allocation(debug_block, component, module, line);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Could not free memory"));
+ }
+
+ acpi_os_free(debug_block);
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "%p freed (block %p)\n",
+ allocation, debug_block));
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_find_allocation
+ *
+ * PARAMETERS: allocation - Address of allocated memory
+ *
+ * RETURN: Three cases:
+ * 1) List is empty, NULL is returned.
+ * 2) Element was found. Returns Allocation parameter.
+ * 3) Element was not found. Returns position where it should be
+ * inserted into the list.
+ *
+ * DESCRIPTION: Searches for an element in the global allocation tracking list.
+ * If the element is not found, returns the location within the
+ * list where the element should be inserted.
+ *
+ * Note: The list is ordered by larger-to-smaller addresses.
+ *
+ * This global list is used to detect memory leaks in ACPICA as
+ * well as other issues such as an attempt to release the same
+ * internal object more than once. Although expensive as far
+ * as cpu time, this list is much more helpful for finding these
+ * types of issues than using memory leak detectors outside of
+ * the ACPICA code.
+ *
+ ******************************************************************************/
+
+static struct acpi_debug_mem_block *acpi_ut_find_allocation(struct
+ acpi_debug_mem_block
+ *allocation)
+{
+ struct acpi_debug_mem_block *element;
+
+ element = acpi_gbl_global_list->list_head;
+ if (!element) {
+ return (NULL);
+ }
+
+ /*
+ * Search for the address.
+ *
+ * Note: List is ordered by larger-to-smaller addresses, on the
+ * assumption that a new allocation usually has a larger address
+ * than previous allocations.
+ */
+ while (element > allocation) {
+
+ /* Check for end-of-list */
+
+ if (!element->next) {
+ return (element);
+ }
+
+ element = element->next;
+ }
+
+ if (element == allocation) {
+ return (element);
+ }
+
+ return (element->previous);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_track_allocation
+ *
+ * PARAMETERS: allocation - Address of allocated memory
+ * size - Size of the allocation
+ * alloc_type - MEM_MALLOC or MEM_CALLOC
+ * component - Component type of caller
+ * module - Source file name of caller
+ * line - Line number of caller
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Inserts an element into the global allocation tracking list.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_track_allocation(struct acpi_debug_mem_block *allocation,
+ acpi_size size,
+ u8 alloc_type,
+ u32 component, const char *module, u32 line)
+{
+ struct acpi_memory_list *mem_list;
+ struct acpi_debug_mem_block *element;
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE_PTR(ut_track_allocation, allocation);
+
+ if (acpi_gbl_disable_mem_tracking) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ mem_list = acpi_gbl_global_list;
+ status = acpi_ut_acquire_mutex(ACPI_MTX_MEMORY);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Search the global list for this address to make sure it is not
+ * already present. This will catch several kinds of problems.
+ */
+ element = acpi_ut_find_allocation(allocation);
+ if (element == allocation) {
+ ACPI_ERROR((AE_INFO,
+ "UtTrackAllocation: Allocation (%p) already present in global list!",
+ allocation));
+ goto unlock_and_exit;
+ }
+
+ /* Fill in the instance data */
+
+ allocation->size = (u32)size;
+ allocation->alloc_type = alloc_type;
+ allocation->component = component;
+ allocation->line = line;
+
+ acpi_ut_safe_strncpy(allocation->module, (char *)module,
+ ACPI_MAX_MODULE_NAME);
+
+ if (!element) {
+
+ /* Insert at list head */
+
+ if (mem_list->list_head) {
+ ((struct acpi_debug_mem_block *)(mem_list->list_head))->
+ previous = allocation;
+ }
+
+ allocation->next = mem_list->list_head;
+ allocation->previous = NULL;
+
+ mem_list->list_head = allocation;
+ } else {
+ /* Insert after element */
+
+ allocation->next = element->next;
+ allocation->previous = element;
+
+ if (element->next) {
+ (element->next)->previous = allocation;
+ }
+
+ element->next = allocation;
+ }
+
+unlock_and_exit:
+ status = acpi_ut_release_mutex(ACPI_MTX_MEMORY);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_remove_allocation
+ *
+ * PARAMETERS: allocation - Address of allocated memory
+ * component - Component type of caller
+ * module - Source file name of caller
+ * line - Line number of caller
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Deletes an element from the global allocation tracking list.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_remove_allocation(struct acpi_debug_mem_block *allocation,
+ u32 component, const char *module, u32 line)
+{
+ struct acpi_memory_list *mem_list;
+ acpi_status status;
+
+ ACPI_FUNCTION_NAME(ut_remove_allocation);
+
+ if (acpi_gbl_disable_mem_tracking) {
+ return (AE_OK);
+ }
+
+ mem_list = acpi_gbl_global_list;
+ if (NULL == mem_list->list_head) {
+
+ /* No allocations! */
+
+ ACPI_ERROR((module, line,
+ "Empty allocation list, nothing to free!"));
+
+ return (AE_OK);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_MEMORY);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Unlink */
+
+ if (allocation->previous) {
+ (allocation->previous)->next = allocation->next;
+ } else {
+ mem_list->list_head = allocation->next;
+ }
+
+ if (allocation->next) {
+ (allocation->next)->previous = allocation->previous;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Freeing %p, size 0%X\n",
+ &allocation->user_space, allocation->size));
+
+ /* Mark the segment as deleted */
+
+ memset(&allocation->user_space, 0xEA, allocation->size);
+
+ status = acpi_ut_release_mutex(ACPI_MTX_MEMORY);
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_dump_allocation_info
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print some info about the outstanding allocations.
+ *
+ ******************************************************************************/
+
+void acpi_ut_dump_allocation_info(void)
+{
+/*
+ struct acpi_memory_list *mem_list;
+*/
+
+ ACPI_FUNCTION_TRACE(ut_dump_allocation_info);
+
+/*
+ ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
+ ("%30s: %4d (%3d Kb)\n", "Current allocations",
+ mem_list->current_count,
+ ROUND_UP_TO_1K (mem_list->current_size)));
+
+ ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
+ ("%30s: %4d (%3d Kb)\n", "Max concurrent allocations",
+ mem_list->max_concurrent_count,
+ ROUND_UP_TO_1K (mem_list->max_concurrent_size)));
+
+ ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
+ ("%30s: %4d (%3d Kb)\n", "Total (all) internal objects",
+ running_object_count,
+ ROUND_UP_TO_1K (running_object_size)));
+
+ ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
+ ("%30s: %4d (%3d Kb)\n", "Total (all) allocations",
+ running_alloc_count,
+ ROUND_UP_TO_1K (running_alloc_size)));
+
+ ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
+ ("%30s: %4d (%3d Kb)\n", "Current Nodes",
+ acpi_gbl_current_node_count,
+ ROUND_UP_TO_1K (acpi_gbl_current_node_size)));
+
+ ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
+ ("%30s: %4d (%3d Kb)\n", "Max Nodes",
+ acpi_gbl_max_concurrent_node_count,
+ ROUND_UP_TO_1K ((acpi_gbl_max_concurrent_node_count *
+ sizeof (struct acpi_namespace_node)))));
+*/
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_dump_allocations
+ *
+ * PARAMETERS: component - Component(s) to dump info for.
+ * module - Module to dump info for. NULL means all.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print a list of all outstanding allocations.
+ *
+ ******************************************************************************/
+
+void acpi_ut_dump_allocations(u32 component, const char *module)
+{
+ struct acpi_debug_mem_block *element;
+ union acpi_descriptor *descriptor;
+ u32 num_outstanding = 0;
+ u8 descriptor_type;
+
+ ACPI_FUNCTION_TRACE(ut_dump_allocations);
+
+ if (acpi_gbl_disable_mem_tracking) {
+ return_VOID;
+ }
+
+ /*
+ * Walk the allocation list.
+ */
+ if (ACPI_FAILURE(acpi_ut_acquire_mutex(ACPI_MTX_MEMORY))) {
+ return_VOID;
+ }
+
+ if (!acpi_gbl_global_list) {
+ goto exit;
+ }
+
+ element = acpi_gbl_global_list->list_head;
+ while (element) {
+ if ((element->component & component) &&
+ ((module == NULL)
+ || (0 == strcmp(module, element->module)))) {
+ descriptor =
+ ACPI_CAST_PTR(union acpi_descriptor,
+ &element->user_space);
+
+ if (element->size <
+ sizeof(struct acpi_common_descriptor)) {
+ acpi_os_printf("%p Length 0x%04X %9.9s-%4.4u "
+ "[Not a Descriptor - too small]\n",
+ descriptor, element->size,
+ element->module, element->line);
+ } else {
+ /* Ignore allocated objects that are in a cache */
+
+ if (ACPI_GET_DESCRIPTOR_TYPE(descriptor) !=
+ ACPI_DESC_TYPE_CACHED) {
+ acpi_os_printf
+ ("%p Length 0x%04X %9.9s-%4.4u [%s] ",
+ descriptor, element->size,
+ element->module, element->line,
+ acpi_ut_get_descriptor_name
+ (descriptor));
+
+ /* Optional object hex dump */
+
+ if (acpi_gbl_verbose_leak_dump) {
+ acpi_os_printf("\n");
+ acpi_ut_dump_buffer((u8 *)
+ descriptor,
+ element->
+ size,
+ DB_BYTE_DISPLAY,
+ 0);
+ }
+
+ /* Validate the descriptor type using Type field and length */
+
+ descriptor_type = 0; /* Not a valid descriptor type */
+
+ switch (ACPI_GET_DESCRIPTOR_TYPE
+ (descriptor)) {
+ case ACPI_DESC_TYPE_OPERAND:
+
+ if (element->size ==
+ sizeof(union
+ acpi_operand_object))
+ {
+ descriptor_type =
+ ACPI_DESC_TYPE_OPERAND;
+ }
+ break;
+
+ case ACPI_DESC_TYPE_PARSER:
+
+ if (element->size ==
+ sizeof(union
+ acpi_parse_object)) {
+ descriptor_type =
+ ACPI_DESC_TYPE_PARSER;
+ }
+ break;
+
+ case ACPI_DESC_TYPE_NAMED:
+
+ if (element->size ==
+ sizeof(struct
+ acpi_namespace_node))
+ {
+ descriptor_type =
+ ACPI_DESC_TYPE_NAMED;
+ }
+ break;
+
+ default:
+
+ break;
+ }
+
+ /* Display additional info for the major descriptor types */
+
+ switch (descriptor_type) {
+ case ACPI_DESC_TYPE_OPERAND:
+
+ acpi_os_printf
+ ("%12.12s RefCount 0x%04X\n",
+ acpi_ut_get_type_name
+ (descriptor->object.common.
+ type),
+ descriptor->object.common.
+ reference_count);
+ break;
+
+ case ACPI_DESC_TYPE_PARSER:
+
+ acpi_os_printf
+ ("AmlOpcode 0x%04X\n",
+ descriptor->op.asl.
+ aml_opcode);
+ break;
+
+ case ACPI_DESC_TYPE_NAMED:
+
+ acpi_os_printf("%4.4s\n",
+ acpi_ut_get_node_name
+ (&descriptor->
+ node));
+ break;
+
+ default:
+
+ acpi_os_printf("\n");
+ break;
+ }
+ }
+ }
+
+ num_outstanding++;
+ }
+
+ element = element->next;
+ }
+
+exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_MEMORY);
+
+ /* Print summary */
+
+ if (!num_outstanding) {
+ ACPI_INFO(("No outstanding allocations"));
+ } else {
+ ACPI_ERROR((AE_INFO, "%u (0x%X) Outstanding cache allocations",
+ num_outstanding, num_outstanding));
+ }
+
+ return_VOID;
+}
+
+#endif /* ACPI_DBG_TRACK_ALLOCATIONS */
diff --git a/drivers/acpi/acpica/utuuid.c b/drivers/acpi/acpica/utuuid.c
new file mode 100644
index 000000000..e24bc670b
--- /dev/null
+++ b/drivers/acpi/acpica/utuuid.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utuuid -- UUID support functions
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_COMPILER
+ACPI_MODULE_NAME("utuuid")
+
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP)
+/*
+ * UUID support functions.
+ *
+ * This table is used to convert an input UUID ascii string to a 16 byte
+ * buffer and the reverse. The table maps a UUID buffer index 0-15 to
+ * the index within the 36-byte UUID string where the associated 2-byte
+ * hex value can be found.
+ *
+ * 36-byte UUID strings are of the form:
+ * aabbccdd-eeff-gghh-iijj-kkllmmnnoopp
+ * Where aa-pp are one byte hex numbers, made up of two hex digits
+ *
+ * Note: This table is basically the inverse of the string-to-offset table
+ * found in the ACPI spec in the description of the to_UUID macro.
+ */
+const u8 acpi_gbl_map_to_uuid_offset[UUID_BUFFER_LENGTH] = {
+ 6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_convert_string_to_uuid
+ *
+ * PARAMETERS: in_string - 36-byte formatted UUID string
+ * uuid_buffer - Where the 16-byte UUID buffer is returned
+ *
+ * RETURN: None. Output data is returned in the uuid_buffer
+ *
+ * DESCRIPTION: Convert a 36-byte formatted UUID string to 16-byte UUID buffer
+ *
+ ******************************************************************************/
+
+void acpi_ut_convert_string_to_uuid(char *in_string, u8 *uuid_buffer)
+{
+ u32 i;
+
+ for (i = 0; i < UUID_BUFFER_LENGTH; i++) {
+ uuid_buffer[i] =
+ (acpi_ut_ascii_char_to_hex
+ (in_string[acpi_gbl_map_to_uuid_offset[i]]) << 4);
+
+ uuid_buffer[i] |=
+ acpi_ut_ascii_char_to_hex(in_string
+ [acpi_gbl_map_to_uuid_offset[i] +
+ 1]);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_convert_uuid_to_string
+ *
+ * PARAMETERS: uuid_buffer - 16-byte UUID buffer
+ * out_string - 36-byte formatted UUID string
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert 16-byte UUID buffer to 36-byte formatted UUID string
+ * out_string must be 37 bytes to include null terminator.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_convert_uuid_to_string(char *uuid_buffer, char *out_string)
+{
+ u32 i;
+
+ if (!uuid_buffer || !out_string) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ for (i = 0; i < UUID_BUFFER_LENGTH; i++) {
+ out_string[acpi_gbl_map_to_uuid_offset[i]] =
+ acpi_ut_hex_to_ascii_char(uuid_buffer[i], 4);
+
+ out_string[acpi_gbl_map_to_uuid_offset[i] + 1] =
+ acpi_ut_hex_to_ascii_char(uuid_buffer[i], 0);
+ }
+
+ /* Insert required hyphens (dashes) */
+
+ out_string[UUID_HYPHEN1_OFFSET] =
+ out_string[UUID_HYPHEN2_OFFSET] =
+ out_string[UUID_HYPHEN3_OFFSET] =
+ out_string[UUID_HYPHEN4_OFFSET] = '-';
+
+ out_string[UUID_STRING_LENGTH] = 0; /* Null terminate */
+ return (AE_OK);
+}
+#endif
diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c
new file mode 100644
index 000000000..86e76b443
--- /dev/null
+++ b/drivers/acpi/acpica/utxface.c
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utxface - External interfaces, miscellaneous utility functions
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acdebug.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utxface")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_terminate
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Shutdown the ACPICA subsystem and release all resources.
+ *
+ ******************************************************************************/
+acpi_status ACPI_INIT_FUNCTION acpi_terminate(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_terminate);
+
+ /* Shutdown and free all resources */
+
+ acpi_ut_subsystem_shutdown();
+
+ /* Free the mutex objects */
+
+ acpi_ut_mutex_terminate();
+
+ /* Now we can shutdown the OS-dependent layer */
+
+ status = acpi_os_terminate();
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_terminate)
+
+#ifndef ACPI_ASL_COMPILER
+#ifdef ACPI_FUTURE_USAGE
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_subsystem_status
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status of the ACPI subsystem
+ *
+ * DESCRIPTION: Other drivers that use the ACPI subsystem should call this
+ * before making any other calls, to ensure the subsystem
+ * initialized successfully.
+ *
+ ******************************************************************************/
+acpi_status acpi_subsystem_status(void)
+{
+
+ if (acpi_gbl_startup_flags & ACPI_INITIALIZED_OK) {
+ return (AE_OK);
+ } else {
+ return (AE_ERROR);
+ }
+}
+
+ACPI_EXPORT_SYMBOL(acpi_subsystem_status)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_system_info
+ *
+ * PARAMETERS: out_buffer - A buffer to receive the resources for the
+ * device
+ *
+ * RETURN: status - the status of the call
+ *
+ * DESCRIPTION: This function is called to get information about the current
+ * state of the ACPI subsystem. It will return system information
+ * in the out_buffer.
+ *
+ * If the function fails an appropriate status will be returned
+ * and the value of out_buffer is undefined.
+ *
+ ******************************************************************************/
+acpi_status acpi_get_system_info(struct acpi_buffer *out_buffer)
+{
+ struct acpi_system_info *info_ptr;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_get_system_info);
+
+ /* Parameter validation */
+
+ status = acpi_ut_validate_buffer(out_buffer);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Validate/Allocate/Clear caller buffer */
+
+ status =
+ acpi_ut_initialize_buffer(out_buffer,
+ sizeof(struct acpi_system_info));
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Populate the return buffer
+ */
+ info_ptr = (struct acpi_system_info *)out_buffer->pointer;
+ info_ptr->acpi_ca_version = ACPI_CA_VERSION;
+
+ /* System flags (ACPI capabilities) */
+
+ info_ptr->flags = ACPI_SYS_MODE_ACPI;
+
+ /* Timer resolution - 24 or 32 bits */
+
+ if (acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER) {
+ info_ptr->timer_resolution = 24;
+ } else {
+ info_ptr->timer_resolution = 32;
+ }
+
+ /* Clear the reserved fields */
+
+ info_ptr->reserved1 = 0;
+ info_ptr->reserved2 = 0;
+
+ /* Current debug levels */
+
+ info_ptr->debug_layer = acpi_dbg_layer;
+ info_ptr->debug_level = acpi_dbg_level;
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_system_info)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_statistics
+ *
+ * PARAMETERS: stats - Where the statistics are returned
+ *
+ * RETURN: status - the status of the call
+ *
+ * DESCRIPTION: Get the contents of the various system counters
+ *
+ ******************************************************************************/
+acpi_status acpi_get_statistics(struct acpi_statistics *stats)
+{
+ ACPI_FUNCTION_TRACE(acpi_get_statistics);
+
+ /* Parameter validation */
+
+ if (!stats) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Various interrupt-based event counters */
+
+ stats->sci_count = acpi_sci_count;
+ stats->gpe_count = acpi_gpe_count;
+
+ memcpy(stats->fixed_event_count, acpi_fixed_event_count,
+ sizeof(acpi_fixed_event_count));
+
+ /* Other counters */
+
+ stats->method_count = acpi_method_count;
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_statistics)
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_install_initialization_handler
+ *
+ * PARAMETERS: handler - Callback procedure
+ * function - Not (currently) used, see below
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install an initialization handler
+ *
+ * TBD: When a second function is added, must save the Function also.
+ *
+ ****************************************************************************/
+acpi_status
+acpi_install_initialization_handler(acpi_init_handler handler, u32 function)
+{
+
+ if (!handler) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ if (acpi_gbl_init_handler) {
+ return (AE_ALREADY_EXISTS);
+ }
+
+ acpi_gbl_init_handler = handler;
+ return (AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_initialization_handler)
+#endif
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_purge_cached_objects
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Empty all caches (delete the cached objects)
+ *
+ ****************************************************************************/
+acpi_status acpi_purge_cached_objects(void)
+{
+ ACPI_FUNCTION_TRACE(acpi_purge_cached_objects);
+
+ (void)acpi_os_purge_cache(acpi_gbl_state_cache);
+ (void)acpi_os_purge_cache(acpi_gbl_operand_cache);
+ (void)acpi_os_purge_cache(acpi_gbl_ps_node_cache);
+ (void)acpi_os_purge_cache(acpi_gbl_ps_node_ext_cache);
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_purge_cached_objects)
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_install_interface
+ *
+ * PARAMETERS: interface_name - The interface to install
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install an _OSI interface to the global list
+ *
+ ****************************************************************************/
+acpi_status acpi_install_interface(acpi_string interface_name)
+{
+ acpi_status status;
+ struct acpi_interface_info *interface_info;
+
+ /* Parameter validation */
+
+ if (!interface_name || (strlen(interface_name) == 0)) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Check if the interface name is already in the global list */
+
+ interface_info = acpi_ut_get_interface(interface_name);
+ if (interface_info) {
+ /*
+ * The interface already exists in the list. This is OK if the
+ * interface has been marked invalid -- just clear the bit.
+ */
+ if (interface_info->flags & ACPI_OSI_INVALID) {
+ interface_info->flags &= ~ACPI_OSI_INVALID;
+ status = AE_OK;
+ } else {
+ status = AE_ALREADY_EXISTS;
+ }
+ } else {
+ /* New interface name, install into the global list */
+
+ status = acpi_ut_install_interface(interface_name);
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_interface)
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_remove_interface
+ *
+ * PARAMETERS: interface_name - The interface to remove
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove an _OSI interface from the global list
+ *
+ ****************************************************************************/
+acpi_status acpi_remove_interface(acpi_string interface_name)
+{
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (!interface_name || (strlen(interface_name) == 0)) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status = acpi_ut_remove_interface(interface_name);
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_interface)
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_install_interface_handler
+ *
+ * PARAMETERS: handler - The _OSI interface handler to install
+ * NULL means "remove existing handler"
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for the predefined _OSI ACPI method.
+ * invoked during execution of the internal implementation of
+ * _OSI. A NULL handler simply removes any existing handler.
+ *
+ ****************************************************************************/
+acpi_status acpi_install_interface_handler(acpi_interface_handler handler)
+{
+ acpi_status status;
+
+ status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if (handler && acpi_gbl_interface_handler) {
+ status = AE_ALREADY_EXISTS;
+ } else {
+ acpi_gbl_interface_handler = handler;
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_interface_handler)
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_update_interfaces
+ *
+ * PARAMETERS: action - Actions to be performed during the
+ * update
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Update _OSI interface strings, disabling or enabling OS vendor
+ * string or/and feature group strings.
+ *
+ ****************************************************************************/
+acpi_status acpi_update_interfaces(u8 action)
+{
+ acpi_status status;
+
+ status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status = acpi_ut_update_interfaces(action);
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return (status);
+}
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_check_address_range
+ *
+ * PARAMETERS: space_id - Address space ID
+ * address - Start address
+ * length - Length
+ * warn - TRUE if warning on overlap desired
+ *
+ * RETURN: Count of the number of conflicts detected.
+ *
+ * DESCRIPTION: Check if the input address range overlaps any of the
+ * ASL operation region address ranges.
+ *
+ ****************************************************************************/
+
+u32
+acpi_check_address_range(acpi_adr_space_type space_id,
+ acpi_physical_address address,
+ acpi_size length, u8 warn)
+{
+ u32 overlaps;
+ acpi_status status;
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return (0);
+ }
+
+ overlaps = acpi_ut_check_address_range(space_id, address,
+ (u32)length, warn);
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return (overlaps);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_check_address_range)
+#endif /* !ACPI_ASL_COMPILER */
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_decode_pld_buffer
+ *
+ * PARAMETERS: in_buffer - Buffer returned by _PLD method
+ * length - Length of the in_buffer
+ * return_buffer - Where the decode buffer is returned
+ *
+ * RETURN: Status and the decoded _PLD buffer. User must deallocate
+ * the buffer via ACPI_FREE.
+ *
+ * DESCRIPTION: Decode the bit-packed buffer returned by the _PLD method into
+ * a local struct that is much more useful to an ACPI driver.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_decode_pld_buffer(u8 *in_buffer,
+ acpi_size length, struct acpi_pld_info **return_buffer)
+{
+ struct acpi_pld_info *pld_info;
+ u32 *buffer = ACPI_CAST_PTR(u32, in_buffer);
+ u32 dword;
+
+ /* Parameter validation */
+
+ if (!in_buffer || !return_buffer
+ || (length < ACPI_PLD_REV1_BUFFER_SIZE)) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ pld_info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pld_info));
+ if (!pld_info) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* First 32-bit DWord */
+
+ ACPI_MOVE_32_TO_32(&dword, &buffer[0]);
+ pld_info->revision = ACPI_PLD_GET_REVISION(&dword);
+ pld_info->ignore_color = ACPI_PLD_GET_IGNORE_COLOR(&dword);
+ pld_info->red = ACPI_PLD_GET_RED(&dword);
+ pld_info->green = ACPI_PLD_GET_GREEN(&dword);
+ pld_info->blue = ACPI_PLD_GET_BLUE(&dword);
+
+ /* Second 32-bit DWord */
+
+ ACPI_MOVE_32_TO_32(&dword, &buffer[1]);
+ pld_info->width = ACPI_PLD_GET_WIDTH(&dword);
+ pld_info->height = ACPI_PLD_GET_HEIGHT(&dword);
+
+ /* Third 32-bit DWord */
+
+ ACPI_MOVE_32_TO_32(&dword, &buffer[2]);
+ pld_info->user_visible = ACPI_PLD_GET_USER_VISIBLE(&dword);
+ pld_info->dock = ACPI_PLD_GET_DOCK(&dword);
+ pld_info->lid = ACPI_PLD_GET_LID(&dword);
+ pld_info->panel = ACPI_PLD_GET_PANEL(&dword);
+ pld_info->vertical_position = ACPI_PLD_GET_VERTICAL(&dword);
+ pld_info->horizontal_position = ACPI_PLD_GET_HORIZONTAL(&dword);
+ pld_info->shape = ACPI_PLD_GET_SHAPE(&dword);
+ pld_info->group_orientation = ACPI_PLD_GET_ORIENTATION(&dword);
+ pld_info->group_token = ACPI_PLD_GET_TOKEN(&dword);
+ pld_info->group_position = ACPI_PLD_GET_POSITION(&dword);
+ pld_info->bay = ACPI_PLD_GET_BAY(&dword);
+
+ /* Fourth 32-bit DWord */
+
+ ACPI_MOVE_32_TO_32(&dword, &buffer[3]);
+ pld_info->ejectable = ACPI_PLD_GET_EJECTABLE(&dword);
+ pld_info->ospm_eject_required = ACPI_PLD_GET_OSPM_EJECT(&dword);
+ pld_info->cabinet_number = ACPI_PLD_GET_CABINET(&dword);
+ pld_info->card_cage_number = ACPI_PLD_GET_CARD_CAGE(&dword);
+ pld_info->reference = ACPI_PLD_GET_REFERENCE(&dword);
+ pld_info->rotation = ACPI_PLD_GET_ROTATION(&dword);
+ pld_info->order = ACPI_PLD_GET_ORDER(&dword);
+
+ if (length >= ACPI_PLD_REV2_BUFFER_SIZE) {
+
+ /* Fifth 32-bit DWord (Revision 2 of _PLD) */
+
+ ACPI_MOVE_32_TO_32(&dword, &buffer[4]);
+ pld_info->vertical_offset = ACPI_PLD_GET_VERT_OFFSET(&dword);
+ pld_info->horizontal_offset = ACPI_PLD_GET_HORIZ_OFFSET(&dword);
+ }
+
+ *return_buffer = pld_info;
+ return (AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_decode_pld_buffer)
diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c
new file mode 100644
index 000000000..a1ed7fced
--- /dev/null
+++ b/drivers/acpi/acpica/utxferror.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utxferror - Various error/warning output functions
+ *
+ ******************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utxferror")
+
+/*
+ * This module is used for the in-kernel ACPICA as well as the ACPICA
+ * tools/applications.
+ */
+#ifndef ACPI_NO_ERROR_MESSAGES /* Entire module */
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_error
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print "ACPI Error" message with module/line/version info
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_error(const char *module_name, u32 line_number, const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_ERROR);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_error)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_exception
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * status - Status value to be decoded/formatted
+ * format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print an "ACPI Error" message with module/line/version
+ * info as well as decoded acpi_status.
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_exception(const char *module_name,
+ u32 line_number, acpi_status status, const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+
+ /* For AE_OK, just print the message */
+
+ if (ACPI_SUCCESS(status)) {
+ acpi_os_printf(ACPI_MSG_ERROR);
+
+ } else {
+ acpi_os_printf(ACPI_MSG_ERROR "%s, ",
+ acpi_format_exception(status));
+ }
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_exception)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_warning
+ *
+ * PARAMETERS: module_name - Caller's module name (for warning output)
+ * line_number - Caller's line number (for warning output)
+ * format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print "ACPI Warning" message with module/line/version info
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_warning(const char *module_name, u32 line_number, const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_WARNING);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_warning)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_info
+ *
+ * PARAMETERS: format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print generic "ACPI:" information message. There is no
+ * module/line/version info in order to keep the message simple.
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE acpi_info(const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_INFO);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ acpi_os_printf("\n");
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_info)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_bios_error
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print "ACPI Firmware Error" message with module/line/version
+ * info
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_bios_error(const char *module_name,
+ u32 line_number, const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_BIOS_ERROR);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_bios_error)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_bios_exception
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * status - Status value to be decoded/formatted
+ * format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print an "ACPI Firmware Error" message with module/line/version
+ * info as well as decoded acpi_status.
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_bios_exception(const char *module_name,
+ u32 line_number,
+ acpi_status status, const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+
+ /* For AE_OK, just print the message */
+
+ if (ACPI_SUCCESS(status)) {
+ acpi_os_printf(ACPI_MSG_BIOS_ERROR);
+
+ } else {
+ acpi_os_printf(ACPI_MSG_BIOS_ERROR "%s, ",
+ acpi_format_exception(status));
+ }
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_bios_exception)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_bios_warning
+ *
+ * PARAMETERS: module_name - Caller's module name (for warning output)
+ * line_number - Caller's line number (for warning output)
+ * format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print "ACPI Firmware Warning" message with module/line/version
+ * info
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_bios_warning(const char *module_name,
+ u32 line_number, const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_BIOS_WARNING);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_bios_warning)
+#endif /* ACPI_NO_ERROR_MESSAGES */
diff --git a/drivers/acpi/acpica/utxfinit.c b/drivers/acpi/acpica/utxfinit.c
new file mode 100644
index 000000000..f2acec3a0
--- /dev/null
+++ b/drivers/acpi/acpica/utxfinit.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utxfinit - External interfaces for ACPICA initialization
+ *
+ * Copyright (C) 2000 - 2022, Intel Corp.
+ *
+ *****************************************************************************/
+
+#define EXPORT_ACPI_INTERFACES
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+#include "acdebug.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utxfinit")
+
+/* For acpi_exec only */
+void ae_do_object_overrides(void);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_initialize_subsystem
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initializes all global variables. This is the first function
+ * called, so any early initialization belongs here.
+ *
+ ******************************************************************************/
+
+acpi_status ACPI_INIT_FUNCTION acpi_initialize_subsystem(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_initialize_subsystem);
+
+ acpi_gbl_startup_flags = ACPI_SUBSYSTEM_INITIALIZE;
+ ACPI_DEBUG_EXEC(acpi_ut_init_stack_ptr_trace());
+
+ /* Initialize the OS-Dependent layer */
+
+ status = acpi_os_initialize();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "During OSL initialization"));
+ return_ACPI_STATUS(status);
+ }
+
+ /* Initialize all globals used by the subsystem */
+
+ status = acpi_ut_init_globals();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During initialization of globals"));
+ return_ACPI_STATUS(status);
+ }
+
+ /* Create the default mutex objects */
+
+ status = acpi_ut_mutex_initialize();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During Global Mutex creation"));
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Initialize the namespace manager and
+ * the root of the namespace tree
+ */
+ status = acpi_ns_root_initialize();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During Namespace initialization"));
+ return_ACPI_STATUS(status);
+ }
+
+ /* Initialize the global OSI interfaces list with the static names */
+
+ status = acpi_ut_initialize_interfaces();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During OSI interfaces initialization"));
+ return_ACPI_STATUS(status);
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_subsystem)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_enable_subsystem
+ *
+ * PARAMETERS: flags - Init/enable Options
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Completes the subsystem initialization including hardware.
+ * Puts system into ACPI mode if it isn't already.
+ *
+ ******************************************************************************/
+acpi_status ACPI_INIT_FUNCTION acpi_enable_subsystem(u32 flags)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(acpi_enable_subsystem);
+
+ /*
+ * The early initialization phase is complete. The namespace is loaded,
+ * and we can now support address spaces other than Memory, I/O, and
+ * PCI_Config.
+ */
+ acpi_gbl_early_initialization = FALSE;
+
+#if (!ACPI_REDUCED_HARDWARE)
+
+ /* Enable ACPI mode */
+
+ if (!(flags & ACPI_NO_ACPI_ENABLE)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[Init] Going into ACPI mode\n"));
+
+ acpi_gbl_original_mode = acpi_hw_get_mode();
+
+ status = acpi_enable();
+ if (ACPI_FAILURE(status)) {
+ ACPI_WARNING((AE_INFO, "AcpiEnable failed"));
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * 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
+ * execute any control methods safely. Any control method can require
+ * ACPI hardware support, so the hardware must be fully initialized before
+ * any method execution!
+ *
+ * Note2: Fixed events are initialized and enabled here. GPEs are
+ * initialized, but cannot be enabled until after the hardware is
+ * completely initialized (SCI and global_lock activated) and the various
+ * initialization control methods are run (_REG, _STA, _INI) on the
+ * entire namespace.
+ */
+ if (!(flags & ACPI_NO_EVENT_INIT)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[Init] Initializing ACPI events\n"));
+
+ status = acpi_ev_initialize_events();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * Install the SCI handler and Global Lock handler. This completes the
+ * hardware initialization.
+ */
+ if (!(flags & ACPI_NO_HANDLER_INIT)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[Init] Installing SCI/GL handlers\n"));
+
+ status = acpi_ev_install_xrupt_handlers();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_enable_subsystem)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_initialize_objects
+ *
+ * PARAMETERS: flags - Init/enable Options
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Completes namespace initialization by initializing device
+ * objects and executing AML code for Regions, buffers, etc.
+ *
+ ******************************************************************************/
+acpi_status ACPI_INIT_FUNCTION acpi_initialize_objects(u32 flags)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(acpi_initialize_objects);
+
+#ifdef ACPI_OBSOLETE_BEHAVIOR
+ /*
+ * 05/2019: Removed, initialization now happens at both object
+ * creation and table load time
+ */
+
+ /*
+ * Initialize the objects that remain uninitialized. This
+ * runs the executable AML that may be part of the
+ * declaration of these objects: operation_regions, buffer_fields,
+ * bank_fields, Buffers, and Packages.
+ */
+ if (!(flags & ACPI_NO_OBJECT_INIT)) {
+ status = acpi_ns_initialize_objects();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+#endif
+
+ /*
+ * Initialize all device/region objects in the namespace. This runs
+ * the device _STA and _INI methods and region _REG methods.
+ */
+ if (!(flags & (ACPI_NO_DEVICE_INIT | ACPI_NO_ADDRESS_SPACE_INIT))) {
+ status = acpi_ns_initialize_devices(flags);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * Empty the caches (delete the cached objects) on the assumption that
+ * the table load filled them up more than they will be at runtime --
+ * thus wasting non-paged memory.
+ */
+ status = acpi_purge_cached_objects();
+
+ acpi_gbl_startup_flags |= ACPI_INITIALIZED_OK;
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_objects)
diff --git a/drivers/acpi/acpica/utxfmutex.c b/drivers/acpi/acpica/utxfmutex.c
new file mode 100644
index 000000000..be24db254
--- /dev/null
+++ b/drivers/acpi/acpica/utxfmutex.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*******************************************************************************
+ *
+ * Module Name: utxfmutex - external AML mutex access functions
+ *
+ ******************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utxfmutex")
+
+/* Local prototypes */
+static acpi_status
+acpi_ut_get_mutex_object(acpi_handle handle,
+ acpi_string pathname,
+ union acpi_operand_object **ret_obj);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_mutex_object
+ *
+ * PARAMETERS: handle - Mutex or prefix handle (optional)
+ * pathname - Mutex pathname (optional)
+ * ret_obj - Where the mutex object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get an AML mutex object. The mutex node is pointed to by
+ * Handle:Pathname. Either Handle or Pathname can be NULL, but
+ * not both.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ut_get_mutex_object(acpi_handle handle,
+ acpi_string pathname,
+ union acpi_operand_object **ret_obj)
+{
+ struct acpi_namespace_node *mutex_node;
+ union acpi_operand_object *mutex_obj;
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (!ret_obj || (!handle && !pathname)) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Get a the namespace node for the mutex */
+
+ mutex_node = handle;
+ if (pathname != NULL) {
+ status =
+ acpi_get_handle(handle, pathname,
+ ACPI_CAST_PTR(acpi_handle, &mutex_node));
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ /* Ensure that we actually have a Mutex object */
+
+ if (!mutex_node || (mutex_node->type != ACPI_TYPE_MUTEX)) {
+ return (AE_TYPE);
+ }
+
+ /* Get the low-level mutex object */
+
+ mutex_obj = acpi_ns_get_attached_object(mutex_node);
+ if (!mutex_obj) {
+ return (AE_NULL_OBJECT);
+ }
+
+ *ret_obj = mutex_obj;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_acquire_mutex
+ *
+ * PARAMETERS: handle - Mutex or prefix handle (optional)
+ * pathname - Mutex pathname (optional)
+ * timeout - Max time to wait for the lock (millisec)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Acquire an AML mutex. This is a device driver interface to
+ * AML mutex objects, and allows for transaction locking between
+ * drivers and AML code. The mutex node is pointed to by
+ * Handle:Pathname. Either Handle or Pathname can be NULL, but
+ * not both.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_acquire_mutex(acpi_handle handle, acpi_string pathname, u16 timeout)
+{
+ acpi_status status;
+ union acpi_operand_object *mutex_obj;
+
+ /* Get the low-level mutex associated with Handle:Pathname */
+
+ status = acpi_ut_get_mutex_object(handle, pathname, &mutex_obj);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Acquire the OS mutex */
+
+ status = acpi_os_acquire_mutex(mutex_obj->mutex.os_mutex, timeout);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_acquire_mutex)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_release_mutex
+ *
+ * PARAMETERS: handle - Mutex or prefix handle (optional)
+ * pathname - Mutex pathname (optional)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Release an AML mutex. This is a device driver interface to
+ * AML mutex objects, and allows for transaction locking between
+ * drivers and AML code. The mutex node is pointed to by
+ * Handle:Pathname. Either Handle or Pathname can be NULL, but
+ * not both.
+ *
+ ******************************************************************************/
+acpi_status acpi_release_mutex(acpi_handle handle, acpi_string pathname)
+{
+ acpi_status status;
+ union acpi_operand_object *mutex_obj;
+
+ /* Get the low-level mutex associated with Handle:Pathname */
+
+ status = acpi_ut_get_mutex_object(handle, pathname, &mutex_obj);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Release the OS mutex */
+
+ acpi_os_release_mutex(mutex_obj->mutex.os_mutex);
+ return (AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_release_mutex)
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
new file mode 100644
index 000000000..6b18f8bc7
--- /dev/null
+++ b/drivers/acpi/apei/Kconfig
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: GPL-2.0
+config HAVE_ACPI_APEI
+ bool
+
+config HAVE_ACPI_APEI_NMI
+ bool
+
+config ACPI_APEI
+ bool "ACPI Platform Error Interface (APEI)"
+ select MISC_FILESYSTEMS
+ select PSTORE
+ select UEFI_CPER
+ depends on HAVE_ACPI_APEI
+ help
+ APEI allows to report errors (for example from the chipset)
+ to the operating system. This improves NMI handling
+ especially. In addition it supports error serialization and
+ error injection.
+
+config ACPI_APEI_GHES
+ bool "APEI Generic Hardware Error Source"
+ depends on ACPI_APEI
+ select ACPI_HED
+ select IRQ_WORK
+ select GENERIC_ALLOCATOR
+ help
+ Generic Hardware Error Source provides a way to report
+ platform hardware errors (such as that from chipset). It
+ works in so called "Firmware First" mode, that is, hardware
+ errors are reported to firmware firstly, then reported to
+ Linux by firmware. This way, some non-standard hardware
+ error registers or non-standard hardware link can be checked
+ by firmware to produce more valuable hardware error
+ information for Linux.
+
+config ACPI_APEI_PCIEAER
+ bool "APEI PCIe AER logging/recovering support"
+ depends on ACPI_APEI && PCIEAER
+ help
+ PCIe AER errors may be reported via APEI firmware first mode.
+ Turn on this option to enable the corresponding support.
+
+config ACPI_APEI_SEA
+ bool
+ depends on ARM64 && ACPI_APEI_GHES
+ default y
+
+config ACPI_APEI_MEMORY_FAILURE
+ bool "APEI memory error recovering support"
+ depends on ACPI_APEI && MEMORY_FAILURE
+ help
+ Memory errors may be reported via APEI firmware first mode.
+ Turn on this option to enable the memory recovering support.
+
+config ACPI_APEI_EINJ
+ tristate "APEI Error INJection (EINJ)"
+ depends on ACPI_APEI && DEBUG_FS
+ help
+ EINJ provides a hardware error injection mechanism, it is
+ mainly used for debugging and testing the other parts of
+ APEI and some other RAS features.
+
+config ACPI_APEI_ERST_DEBUG
+ tristate "APEI Error Record Serialization Table (ERST) Debug Support"
+ depends on ACPI_APEI
+ help
+ ERST is a way provided by APEI to save and retrieve hardware
+ error information to and from a persistent store. Enable this
+ if you want to debugging and testing the ERST kernel support
+ and firmware implementation.
diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile
new file mode 100644
index 000000000..4dfac2128
--- /dev/null
+++ b/drivers/acpi/apei/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ACPI_APEI) += apei.o
+obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o
+obj-$(CONFIG_ACPI_APEI_EINJ) += einj.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
new file mode 100644
index 000000000..9b52482b4
--- /dev/null
+++ b/drivers/acpi/apei/apei-base.c
@@ -0,0 +1,800 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * apei-base.c - ACPI Platform Error Interface (APEI) supporting
+ * infrastructure
+ *
+ * APEI allows to report errors (for example from the chipset) to
+ * the operating system. This improves NMI handling especially. In
+ * addition it supports error serialization and error injection.
+ *
+ * For more information about APEI, please refer to ACPI Specification
+ * version 4.0, chapter 17.
+ *
+ * This file has Common functions used by more than one APEI table,
+ * including framework of interpreter for ERST and EINJ; resource
+ * management for APEI registers.
+ *
+ * Copyright (C) 2009, Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#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 "apei-internal.h"
+
+#define APEI_PFX "APEI: "
+
+/*
+ * APEI ERST (Error Record Serialization Table) and EINJ (Error
+ * INJection) interpreter framework.
+ */
+
+#define APEI_EXEC_PRESERVE_REGISTER 0x1
+
+void apei_exec_ctx_init(struct apei_exec_context *ctx,
+ struct apei_exec_ins_type *ins_table,
+ u32 instructions,
+ struct acpi_whea_header *action_table,
+ u32 entries)
+{
+ ctx->ins_table = ins_table;
+ ctx->instructions = instructions;
+ ctx->action_table = action_table;
+ ctx->entries = entries;
+}
+EXPORT_SYMBOL_GPL(apei_exec_ctx_init);
+
+int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val)
+{
+ int rc;
+
+ rc = apei_read(val, &entry->register_region);
+ if (rc)
+ return rc;
+ *val >>= entry->register_region.bit_offset;
+ *val &= entry->mask;
+
+ return 0;
+}
+
+int apei_exec_read_register(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 val = 0;
+
+ rc = __apei_exec_read_register(entry, &val);
+ if (rc)
+ return rc;
+ ctx->value = val;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(apei_exec_read_register);
+
+int apei_exec_read_register_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+
+ rc = apei_exec_read_register(ctx, entry);
+ if (rc)
+ return rc;
+ ctx->value = (ctx->value == entry->value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(apei_exec_read_register_value);
+
+int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val)
+{
+ int rc;
+
+ val &= entry->mask;
+ val <<= entry->register_region.bit_offset;
+ if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) {
+ u64 valr = 0;
+ rc = apei_read(&valr, &entry->register_region);
+ if (rc)
+ return rc;
+ valr &= ~(entry->mask << entry->register_region.bit_offset);
+ val |= valr;
+ }
+ rc = apei_write(val, &entry->register_region);
+
+ return rc;
+}
+
+int apei_exec_write_register(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_write_register(entry, ctx->value);
+}
+EXPORT_SYMBOL_GPL(apei_exec_write_register);
+
+int apei_exec_write_register_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ ctx->value = entry->value;
+
+ return apei_exec_write_register(ctx, entry);
+}
+EXPORT_SYMBOL_GPL(apei_exec_write_register_value);
+
+int apei_exec_noop(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(apei_exec_noop);
+
+/*
+ * Interpret the specified action. Go through whole action table,
+ * execute all instructions belong to the action.
+ */
+int __apei_exec_run(struct apei_exec_context *ctx, u8 action,
+ bool optional)
+{
+ int rc = -ENOENT;
+ u32 i, ip;
+ struct acpi_whea_header *entry;
+ apei_exec_ins_func_t run;
+
+ ctx->ip = 0;
+
+ /*
+ * "ip" is the instruction pointer of current instruction,
+ * "ctx->ip" specifies the next instruction to executed,
+ * instruction "run" function may change the "ctx->ip" to
+ * implement "goto" semantics.
+ */
+rewind:
+ ip = 0;
+ for (i = 0; i < ctx->entries; i++) {
+ entry = &ctx->action_table[i];
+ if (entry->action != action)
+ continue;
+ if (ip == ctx->ip) {
+ if (entry->instruction >= ctx->instructions ||
+ !ctx->ins_table[entry->instruction].run) {
+ pr_warn(FW_WARN APEI_PFX
+ "Invalid action table, unknown instruction type: %d\n",
+ entry->instruction);
+ return -EINVAL;
+ }
+ run = ctx->ins_table[entry->instruction].run;
+ rc = run(ctx, entry);
+ if (rc < 0)
+ return rc;
+ else if (rc != APEI_EXEC_SET_IP)
+ ctx->ip++;
+ }
+ ip++;
+ if (ctx->ip < ip)
+ goto rewind;
+ }
+
+ return !optional && rc < 0 ? rc : 0;
+}
+EXPORT_SYMBOL_GPL(__apei_exec_run);
+
+typedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry,
+ void *data);
+
+static int apei_exec_for_each_entry(struct apei_exec_context *ctx,
+ apei_exec_entry_func_t func,
+ void *data,
+ int *end)
+{
+ u8 ins;
+ int i, rc;
+ struct acpi_whea_header *entry;
+ struct apei_exec_ins_type *ins_table = ctx->ins_table;
+
+ for (i = 0; i < ctx->entries; i++) {
+ entry = ctx->action_table + i;
+ ins = entry->instruction;
+ if (end)
+ *end = i;
+ if (ins >= ctx->instructions || !ins_table[ins].run) {
+ pr_warn(FW_WARN APEI_PFX
+ "Invalid action table, unknown instruction type: %d\n",
+ ins);
+ return -EINVAL;
+ }
+ rc = func(ctx, entry, data);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int pre_map_gar_callback(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry,
+ void *data)
+{
+ u8 ins = entry->instruction;
+
+ if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
+ return apei_map_generic_address(&entry->register_region);
+
+ return 0;
+}
+
+/*
+ * Pre-map all GARs in action table to make it possible to access them
+ * in NMI handler.
+ */
+int apei_exec_pre_map_gars(struct apei_exec_context *ctx)
+{
+ int rc, end;
+
+ rc = apei_exec_for_each_entry(ctx, pre_map_gar_callback,
+ NULL, &end);
+ if (rc) {
+ struct apei_exec_context ctx_unmap;
+ memcpy(&ctx_unmap, ctx, sizeof(*ctx));
+ ctx_unmap.entries = end;
+ apei_exec_post_unmap_gars(&ctx_unmap);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(apei_exec_pre_map_gars);
+
+static int post_unmap_gar_callback(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry,
+ void *data)
+{
+ u8 ins = entry->instruction;
+
+ if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
+ apei_unmap_generic_address(&entry->register_region);
+
+ return 0;
+}
+
+/* Post-unmap all GAR in action table. */
+int apei_exec_post_unmap_gars(struct apei_exec_context *ctx)
+{
+ return apei_exec_for_each_entry(ctx, post_unmap_gar_callback,
+ NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(apei_exec_post_unmap_gars);
+
+/*
+ * Resource management for GARs in APEI
+ */
+struct apei_res {
+ struct list_head list;
+ unsigned long start;
+ unsigned long end;
+};
+
+/* Collect all resources requested, to avoid conflict */
+static struct apei_resources apei_resources_all = {
+ .iomem = LIST_HEAD_INIT(apei_resources_all.iomem),
+ .ioport = LIST_HEAD_INIT(apei_resources_all.ioport),
+};
+
+static int apei_res_add(struct list_head *res_list,
+ unsigned long start, unsigned long size)
+{
+ struct apei_res *res, *resn, *res_ins = NULL;
+ unsigned long end = start + size;
+
+ if (end <= start)
+ return 0;
+repeat:
+ list_for_each_entry_safe(res, resn, res_list, list) {
+ if (res->start > end || res->end < start)
+ continue;
+ else if (end <= res->end && start >= res->start) {
+ kfree(res_ins);
+ return 0;
+ }
+ list_del(&res->list);
+ res->start = start = min(res->start, start);
+ res->end = end = max(res->end, end);
+ kfree(res_ins);
+ res_ins = res;
+ goto repeat;
+ }
+
+ if (res_ins)
+ list_add(&res_ins->list, res_list);
+ else {
+ res_ins = kmalloc(sizeof(*res_ins), GFP_KERNEL);
+ if (!res_ins)
+ return -ENOMEM;
+ res_ins->start = start;
+ res_ins->end = end;
+ list_add(&res_ins->list, res_list);
+ }
+
+ return 0;
+}
+
+static int apei_res_sub(struct list_head *res_list1,
+ struct list_head *res_list2)
+{
+ struct apei_res *res1, *resn1, *res2, *res;
+ res1 = list_entry(res_list1->next, struct apei_res, list);
+ resn1 = list_entry(res1->list.next, struct apei_res, list);
+ while (&res1->list != res_list1) {
+ list_for_each_entry(res2, res_list2, list) {
+ if (res1->start >= res2->end ||
+ res1->end <= res2->start)
+ continue;
+ else if (res1->end <= res2->end &&
+ res1->start >= res2->start) {
+ list_del(&res1->list);
+ kfree(res1);
+ break;
+ } else if (res1->end > res2->end &&
+ res1->start < res2->start) {
+ res = kmalloc(sizeof(*res), GFP_KERNEL);
+ if (!res)
+ return -ENOMEM;
+ res->start = res2->end;
+ res->end = res1->end;
+ res1->end = res2->start;
+ list_add(&res->list, &res1->list);
+ resn1 = res;
+ } else {
+ if (res1->start < res2->start)
+ res1->end = res2->start;
+ else
+ res1->start = res2->end;
+ }
+ }
+ res1 = resn1;
+ resn1 = list_entry(resn1->list.next, struct apei_res, list);
+ }
+
+ return 0;
+}
+
+static void apei_res_clean(struct list_head *res_list)
+{
+ struct apei_res *res, *resn;
+
+ list_for_each_entry_safe(res, resn, res_list, list) {
+ list_del(&res->list);
+ kfree(res);
+ }
+}
+
+void apei_resources_fini(struct apei_resources *resources)
+{
+ apei_res_clean(&resources->iomem);
+ apei_res_clean(&resources->ioport);
+}
+EXPORT_SYMBOL_GPL(apei_resources_fini);
+
+static int apei_resources_merge(struct apei_resources *resources1,
+ struct apei_resources *resources2)
+{
+ int rc;
+ struct apei_res *res;
+
+ list_for_each_entry(res, &resources2->iomem, list) {
+ rc = apei_res_add(&resources1->iomem, res->start,
+ res->end - res->start);
+ if (rc)
+ return rc;
+ }
+ list_for_each_entry(res, &resources2->ioport, list) {
+ rc = apei_res_add(&resources1->ioport, res->start,
+ res->end - res->start);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+int apei_resources_add(struct apei_resources *resources,
+ unsigned long start, unsigned long size,
+ bool iomem)
+{
+ if (iomem)
+ return apei_res_add(&resources->iomem, start, size);
+ else
+ return apei_res_add(&resources->ioport, start, size);
+}
+EXPORT_SYMBOL_GPL(apei_resources_add);
+
+/*
+ * EINJ has two groups of GARs (EINJ table entry and trigger table
+ * entry), so common resources are subtracted from the trigger table
+ * resources before the second requesting.
+ */
+int apei_resources_sub(struct apei_resources *resources1,
+ struct apei_resources *resources2)
+{
+ int rc;
+
+ rc = apei_res_sub(&resources1->iomem, &resources2->iomem);
+ if (rc)
+ return rc;
+ return apei_res_sub(&resources1->ioport, &resources2->ioport);
+}
+EXPORT_SYMBOL_GPL(apei_resources_sub);
+
+static int apei_get_res_callback(__u64 start, __u64 size, void *data)
+{
+ struct apei_resources *resources = data;
+ return apei_res_add(&resources->iomem, start, size);
+}
+
+static int apei_get_nvs_resources(struct apei_resources *resources)
+{
+ return acpi_nvs_for_each_region(apei_get_res_callback, resources);
+}
+
+int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
+ void *data), void *data);
+static int apei_get_arch_resources(struct apei_resources *resources)
+
+{
+ return arch_apei_filter_addr(apei_get_res_callback, resources);
+}
+
+/*
+ * IO memory/port resource management mechanism is used to check
+ * whether memory/port area used by GARs conflicts with normal memory
+ * or IO memory/port of devices.
+ */
+int apei_resources_request(struct apei_resources *resources,
+ const char *desc)
+{
+ struct apei_res *res, *res_bak = NULL;
+ struct resource *r;
+ struct apei_resources nvs_resources, arch_res;
+ int rc;
+
+ rc = apei_resources_sub(resources, &apei_resources_all);
+ if (rc)
+ return rc;
+
+ /*
+ * Some firmware uses ACPI NVS region, that has been marked as
+ * busy, so exclude it from APEI resources to avoid false
+ * conflict.
+ */
+ apei_resources_init(&nvs_resources);
+ rc = apei_get_nvs_resources(&nvs_resources);
+ if (rc)
+ goto nvs_res_fini;
+ rc = apei_resources_sub(resources, &nvs_resources);
+ if (rc)
+ goto nvs_res_fini;
+
+ if (arch_apei_filter_addr) {
+ apei_resources_init(&arch_res);
+ rc = apei_get_arch_resources(&arch_res);
+ if (rc)
+ goto arch_res_fini;
+ rc = apei_resources_sub(resources, &arch_res);
+ if (rc)
+ goto arch_res_fini;
+ }
+
+ rc = -EINVAL;
+ list_for_each_entry(res, &resources->iomem, list) {
+ r = request_mem_region(res->start, res->end - res->start,
+ desc);
+ if (!r) {
+ pr_err(APEI_PFX
+ "Can not request [mem %#010llx-%#010llx] for %s registers\n",
+ (unsigned long long)res->start,
+ (unsigned long long)res->end - 1, desc);
+ res_bak = res;
+ goto err_unmap_iomem;
+ }
+ }
+
+ list_for_each_entry(res, &resources->ioport, list) {
+ r = request_region(res->start, res->end - res->start, desc);
+ if (!r) {
+ pr_err(APEI_PFX
+ "Can not request [io %#06llx-%#06llx] for %s registers\n",
+ (unsigned long long)res->start,
+ (unsigned long long)res->end - 1, desc);
+ res_bak = res;
+ goto err_unmap_ioport;
+ }
+ }
+
+ rc = apei_resources_merge(&apei_resources_all, resources);
+ if (rc) {
+ pr_err(APEI_PFX "Fail to merge resources!\n");
+ goto err_unmap_ioport;
+ }
+
+ goto arch_res_fini;
+
+err_unmap_ioport:
+ list_for_each_entry(res, &resources->ioport, list) {
+ if (res == res_bak)
+ break;
+ release_region(res->start, res->end - res->start);
+ }
+ res_bak = NULL;
+err_unmap_iomem:
+ list_for_each_entry(res, &resources->iomem, list) {
+ if (res == res_bak)
+ break;
+ release_mem_region(res->start, res->end - res->start);
+ }
+arch_res_fini:
+ if (arch_apei_filter_addr)
+ apei_resources_fini(&arch_res);
+nvs_res_fini:
+ apei_resources_fini(&nvs_resources);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(apei_resources_request);
+
+void apei_resources_release(struct apei_resources *resources)
+{
+ int rc;
+ struct apei_res *res;
+
+ list_for_each_entry(res, &resources->iomem, list)
+ release_mem_region(res->start, res->end - res->start);
+ list_for_each_entry(res, &resources->ioport, list)
+ release_region(res->start, res->end - res->start);
+
+ rc = apei_resources_sub(&apei_resources_all, resources);
+ if (rc)
+ pr_err(APEI_PFX "Fail to sub resources!\n");
+}
+EXPORT_SYMBOL_GPL(apei_resources_release);
+
+static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr,
+ u32 *access_bit_width)
+{
+ u32 bit_width, bit_offset, access_size_code, space_id;
+
+ bit_width = reg->bit_width;
+ bit_offset = reg->bit_offset;
+ access_size_code = reg->access_width;
+ space_id = reg->space_id;
+ *paddr = get_unaligned(&reg->address);
+ if (!*paddr) {
+ pr_warn(FW_BUG APEI_PFX
+ "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n",
+ *paddr, bit_width, bit_offset, access_size_code,
+ space_id);
+ return -EINVAL;
+ }
+
+ if (access_size_code < 1 || access_size_code > 4) {
+ pr_warn(FW_BUG APEI_PFX
+ "Invalid access size code in GAR [0x%llx/%u/%u/%u/%u]\n",
+ *paddr, bit_width, bit_offset, access_size_code,
+ space_id);
+ return -EINVAL;
+ }
+ *access_bit_width = 1UL << (access_size_code + 2);
+
+ /* Fixup common BIOS bug */
+ if (bit_width == 32 && bit_offset == 0 && (*paddr & 0x03) == 0 &&
+ *access_bit_width < 32)
+ *access_bit_width = 32;
+ else if (bit_width == 64 && bit_offset == 0 && (*paddr & 0x07) == 0 &&
+ *access_bit_width < 64)
+ *access_bit_width = 64;
+
+ if ((bit_width + bit_offset) > *access_bit_width) {
+ pr_warn(FW_BUG APEI_PFX
+ "Invalid bit width + offset in GAR [0x%llx/%u/%u/%u/%u]\n",
+ *paddr, bit_width, bit_offset, access_size_code,
+ space_id);
+ return -EINVAL;
+ }
+
+ if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
+ space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
+ pr_warn(FW_BUG APEI_PFX
+ "Invalid address space type in GAR [0x%llx/%u/%u/%u/%u]\n",
+ *paddr, bit_width, bit_offset, access_size_code,
+ space_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int apei_map_generic_address(struct acpi_generic_address *reg)
+{
+ int rc;
+ u32 access_bit_width;
+ u64 address;
+
+ rc = apei_check_gar(reg, &address, &access_bit_width);
+ if (rc)
+ return rc;
+
+ /* IO space doesn't need mapping */
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO)
+ return 0;
+
+ if (!acpi_os_map_generic_address(reg))
+ return -ENXIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(apei_map_generic_address);
+
+/* read GAR in interrupt (including NMI) or process context */
+int apei_read(u64 *val, struct acpi_generic_address *reg)
+{
+ int rc;
+ u32 access_bit_width;
+ u64 address;
+ acpi_status status;
+
+ rc = apei_check_gar(reg, &address, &access_bit_width);
+ if (rc)
+ return rc;
+
+ *val = 0;
+ switch(reg->space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+ status = acpi_os_read_memory((acpi_physical_address) address,
+ val, access_bit_width);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ break;
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ status = acpi_os_read_port(address, (u32 *)val,
+ access_bit_width);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(apei_read);
+
+/* write GAR in interrupt (including NMI) or process context */
+int apei_write(u64 val, struct acpi_generic_address *reg)
+{
+ int rc;
+ u32 access_bit_width;
+ u64 address;
+ acpi_status status;
+
+ rc = apei_check_gar(reg, &address, &access_bit_width);
+ if (rc)
+ return rc;
+
+ switch (reg->space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+ status = acpi_os_write_memory((acpi_physical_address) address,
+ val, access_bit_width);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ break;
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ status = acpi_os_write_port(address, val, access_bit_width);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(apei_write);
+
+static int collect_res_callback(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry,
+ void *data)
+{
+ struct apei_resources *resources = data;
+ struct acpi_generic_address *reg = &entry->register_region;
+ u8 ins = entry->instruction;
+ u32 access_bit_width;
+ u64 paddr;
+ int rc;
+
+ if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER))
+ return 0;
+
+ rc = apei_check_gar(reg, &paddr, &access_bit_width);
+ if (rc)
+ return rc;
+
+ switch (reg->space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+ return apei_res_add(&resources->iomem, paddr,
+ access_bit_width / 8);
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ return apei_res_add(&resources->ioport, paddr,
+ access_bit_width / 8);
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Same register may be used by multiple instructions in GARs, so
+ * resources are collected before requesting.
+ */
+int apei_exec_collect_resources(struct apei_exec_context *ctx,
+ struct apei_resources *resources)
+{
+ return apei_exec_for_each_entry(ctx, collect_res_callback,
+ resources, NULL);
+}
+EXPORT_SYMBOL_GPL(apei_exec_collect_resources);
+
+struct dentry *apei_get_debugfs_dir(void)
+{
+ static struct dentry *dapei;
+
+ if (!dapei)
+ dapei = debugfs_create_dir("apei", NULL);
+
+ return dapei;
+}
+EXPORT_SYMBOL_GPL(apei_get_debugfs_dir);
+
+int __weak arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr,
+ void *data)
+{
+ return 1;
+}
+EXPORT_SYMBOL_GPL(arch_apei_enable_cmcff);
+
+void __weak arch_apei_report_mem_error(int sev,
+ struct cper_sec_mem_err *mem_err)
+{
+}
+EXPORT_SYMBOL_GPL(arch_apei_report_mem_error);
+
+int apei_osc_setup(void)
+{
+ static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c";
+ acpi_handle handle;
+ u32 capbuf[3];
+ struct acpi_osc_context context = {
+ .uuid_str = whea_uuid_str,
+ .rev = 1,
+ .cap.length = sizeof(capbuf),
+ .cap.pointer = capbuf,
+ };
+
+ capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE;
+ capbuf[OSC_SUPPORT_DWORD] = 1;
+ capbuf[OSC_CONTROL_DWORD] = 0;
+
+ if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))
+ || ACPI_FAILURE(acpi_run_osc(handle, &context)))
+ return -EIO;
+ else {
+ kfree(context.ret.pointer);
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(apei_osc_setup);
diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h
new file mode 100644
index 000000000..1d6ef9654
--- /dev/null
+++ b/drivers/acpi/apei/apei-internal.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * apei-internal.h - ACPI Platform Error Interface internal
+ * definitions.
+ */
+
+#ifndef APEI_INTERNAL_H
+#define APEI_INTERNAL_H
+
+#include <linux/cper.h>
+#include <linux/acpi.h>
+
+struct apei_exec_context;
+
+typedef int (*apei_exec_ins_func_t)(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+
+#define APEI_EXEC_INS_ACCESS_REGISTER 0x0001
+
+struct apei_exec_ins_type {
+ u32 flags;
+ apei_exec_ins_func_t run;
+};
+
+struct apei_exec_context {
+ u32 ip;
+ u64 value;
+ u64 var1;
+ u64 var2;
+ u64 src_base;
+ u64 dst_base;
+ struct apei_exec_ins_type *ins_table;
+ u32 instructions;
+ struct acpi_whea_header *action_table;
+ u32 entries;
+};
+
+void apei_exec_ctx_init(struct apei_exec_context *ctx,
+ struct apei_exec_ins_type *ins_table,
+ u32 instructions,
+ struct acpi_whea_header *action_table,
+ u32 entries);
+
+static inline void apei_exec_ctx_set_input(struct apei_exec_context *ctx,
+ u64 input)
+{
+ ctx->value = input;
+}
+
+static inline u64 apei_exec_ctx_get_output(struct apei_exec_context *ctx)
+{
+ return ctx->value;
+}
+
+int __apei_exec_run(struct apei_exec_context *ctx, u8 action, bool optional);
+
+static inline int apei_exec_run(struct apei_exec_context *ctx, u8 action)
+{
+ return __apei_exec_run(ctx, action, 0);
+}
+
+/* It is optional whether the firmware provides the action */
+static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 action)
+{
+ return __apei_exec_run(ctx, action, 1);
+}
+
+/* Common instruction implementation */
+
+/* IP has been set in instruction function */
+#define APEI_EXEC_SET_IP 1
+
+int apei_map_generic_address(struct acpi_generic_address *reg);
+
+static inline void apei_unmap_generic_address(struct acpi_generic_address *reg)
+{
+ acpi_os_unmap_generic_address(reg);
+}
+
+int apei_read(u64 *val, struct acpi_generic_address *reg);
+int apei_write(u64 val, struct acpi_generic_address *reg);
+
+int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val);
+int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val);
+int apei_exec_read_register(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+int apei_exec_read_register_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+int apei_exec_write_register(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+int apei_exec_write_register_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+int apei_exec_noop(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+int apei_exec_pre_map_gars(struct apei_exec_context *ctx);
+int apei_exec_post_unmap_gars(struct apei_exec_context *ctx);
+
+struct apei_resources {
+ struct list_head iomem;
+ struct list_head ioport;
+};
+
+static inline void apei_resources_init(struct apei_resources *resources)
+{
+ INIT_LIST_HEAD(&resources->iomem);
+ INIT_LIST_HEAD(&resources->ioport);
+}
+
+void apei_resources_fini(struct apei_resources *resources);
+int apei_resources_add(struct apei_resources *resources,
+ unsigned long start, unsigned long size,
+ bool iomem);
+int apei_resources_sub(struct apei_resources *resources1,
+ struct apei_resources *resources2);
+int apei_resources_request(struct apei_resources *resources,
+ const char *desc);
+void apei_resources_release(struct apei_resources *resources);
+int apei_exec_collect_resources(struct apei_exec_context *ctx,
+ struct apei_resources *resources);
+
+struct dentry;
+struct dentry *apei_get_debugfs_dir(void);
+
+static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus)
+{
+ if (estatus->raw_data_length)
+ return estatus->raw_data_offset + \
+ estatus->raw_data_length;
+ else
+ 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);
+#endif
diff --git a/drivers/acpi/apei/bert.c b/drivers/acpi/apei/bert.c
new file mode 100644
index 000000000..c23eb7586
--- /dev/null
+++ b/drivers/acpi/apei/bert.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * APEI Boot Error Record Table (BERT) support
+ *
+ * Copyright 2011 Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.com>
+ *
+ * Under normal circumstances, when a hardware error occurs, the error
+ * handler receives control and processes the error. This gives OSPM a
+ * chance to process the error condition, report it, and optionally attempt
+ * recovery. In some cases, the system is unable to process an error.
+ * For example, system firmware or a management controller may choose to
+ * reset the system or the system might experience an uncontrolled crash
+ * or reset.The boot error source is used to report unhandled errors that
+ * occurred in a previous boot. This mechanism is described in the BERT
+ * table.
+ *
+ * For more information about BERT, please refer to ACPI Specification
+ * version 4.0, section 17.3.1
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+
+#include "apei-internal.h"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "BERT: " fmt
+
+#define ACPI_BERT_PRINT_MAX_RECORDS 5
+#define ACPI_BERT_PRINT_MAX_LEN 1024
+
+static int bert_disable;
+
+/*
+ * Print "all" the error records in the BERT table, but avoid huge spam to
+ * the console if the BIOS included oversize records, or too many records.
+ * Skipping some records here does not lose anything because the full
+ * data is available to user tools in:
+ * /sys/firmware/acpi/tables/data/BERT
+ */
+static void __init bert_print_all(struct acpi_bert_region *region,
+ unsigned int region_len)
+{
+ struct acpi_hest_generic_status *estatus =
+ (struct acpi_hest_generic_status *)region;
+ int remain = region_len;
+ int printed = 0, skipped = 0;
+ u32 estatus_len;
+
+ while (remain >= sizeof(struct acpi_bert_region)) {
+ estatus_len = cper_estatus_len(estatus);
+ if (remain < estatus_len) {
+ pr_err(FW_BUG "Truncated status block (length: %u).\n",
+ estatus_len);
+ break;
+ }
+
+ /* No more error records. */
+ if (!estatus->block_status)
+ break;
+
+ if (cper_estatus_check(estatus)) {
+ pr_err(FW_BUG "Invalid error record.\n");
+ break;
+ }
+
+ if (estatus_len < ACPI_BERT_PRINT_MAX_LEN &&
+ printed < ACPI_BERT_PRINT_MAX_RECORDS) {
+ pr_info_once("Error records from previous boot:\n");
+ cper_estatus_print(KERN_INFO HW_ERR, estatus);
+ printed++;
+ } else {
+ skipped++;
+ }
+
+ /*
+ * Because the boot error source is "one-time polled" type,
+ * clear Block Status of current Generic Error Status Block,
+ * once it's printed.
+ */
+ estatus->block_status = 0;
+
+ estatus = (void *)estatus + estatus_len;
+ remain -= estatus_len;
+ }
+
+ 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)
+{
+ bert_disable = 1;
+
+ return 1;
+}
+__setup("bert_disable", setup_bert_disable);
+
+static int __init bert_check_table(struct acpi_table_bert *bert_tab)
+{
+ if (bert_tab->header.length < sizeof(struct acpi_table_bert) ||
+ bert_tab->region_length < sizeof(struct acpi_bert_region))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __init bert_init(void)
+{
+ struct apei_resources bert_resources;
+ struct acpi_bert_region *boot_error_region;
+ struct acpi_table_bert *bert_tab;
+ unsigned int region_len;
+ acpi_status status;
+ int rc = 0;
+
+ if (acpi_disabled)
+ return 0;
+
+ if (bert_disable) {
+ pr_info("Boot Error Record Table support is disabled.\n");
+ return 0;
+ }
+
+ status = acpi_get_table(ACPI_SIG_BERT, 0, (struct acpi_table_header **)&bert_tab);
+ if (status == AE_NOT_FOUND)
+ return 0;
+
+ if (ACPI_FAILURE(status)) {
+ pr_err("get table failed, %s.\n", acpi_format_exception(status));
+ return -EINVAL;
+ }
+
+ rc = bert_check_table(bert_tab);
+ if (rc) {
+ pr_err(FW_BUG "table invalid.\n");
+ goto out_put_bert_tab;
+ }
+
+ region_len = bert_tab->region_length;
+ apei_resources_init(&bert_resources);
+ rc = apei_resources_add(&bert_resources, bert_tab->address,
+ region_len, true);
+ if (rc)
+ goto out_put_bert_tab;
+ rc = apei_resources_request(&bert_resources, "APEI BERT");
+ if (rc)
+ goto out_fini;
+ boot_error_region = ioremap_cache(bert_tab->address, region_len);
+ if (boot_error_region) {
+ bert_print_all(boot_error_region, region_len);
+ iounmap(boot_error_region);
+ } else {
+ rc = -ENOMEM;
+ }
+
+ apei_resources_release(&bert_resources);
+out_fini:
+ apei_resources_fini(&bert_resources);
+out_put_bert_tab:
+ acpi_put_table((struct acpi_table_header *)bert_tab);
+
+ return rc;
+}
+
+late_initcall(bert_init);
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c
new file mode 100644
index 000000000..6b583373c
--- /dev/null
+++ b/drivers/acpi/apei/einj.c
@@ -0,0 +1,799 @@
+// 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
new file mode 100644
index 000000000..8bc71cdc2
--- /dev/null
+++ b/drivers/acpi/apei/erst-dbg.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * APEI Error Record Serialization Table debug support
+ *
+ * ERST is a way provided by APEI to save and retrieve hardware error
+ * information to and from a persistent store. This file provide the
+ * debugging/testing support for ERST kernel support and firmware
+ * implementation.
+ *
+ * Copyright 2010 Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <acpi/apei.h>
+#include <linux/miscdevice.h>
+
+#include "apei-internal.h"
+
+#define ERST_DBG_PFX "ERST DBG: "
+
+#define ERST_DBG_RECORD_LEN_MAX 0x4000
+
+static void *erst_dbg_buf;
+static unsigned int erst_dbg_buf_len;
+
+/* Prevent erst_dbg_read/write from being invoked concurrently */
+static DEFINE_MUTEX(erst_dbg_mutex);
+
+static int erst_dbg_open(struct inode *inode, struct file *file)
+{
+ int rc, *pos;
+
+ if (erst_disable)
+ return -ENODEV;
+
+ pos = (int *)&file->private_data;
+
+ rc = erst_get_record_id_begin(pos);
+ if (rc)
+ return rc;
+
+ return nonseekable_open(inode, file);
+}
+
+static int erst_dbg_release(struct inode *inode, struct file *file)
+{
+ erst_get_record_id_end();
+
+ return 0;
+}
+
+static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ int rc;
+ u64 record_id;
+ u32 record_count;
+
+ switch (cmd) {
+ case APEI_ERST_CLEAR_RECORD:
+ rc = copy_from_user(&record_id, (void __user *)arg,
+ sizeof(record_id));
+ if (rc)
+ return -EFAULT;
+ return erst_clear(record_id);
+ case APEI_ERST_GET_RECORD_COUNT:
+ rc = erst_get_record_count();
+ if (rc < 0)
+ return rc;
+ record_count = rc;
+ rc = put_user(record_count, (u32 __user *)arg);
+ if (rc)
+ return rc;
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static ssize_t erst_dbg_read(struct file *filp, char __user *ubuf,
+ size_t usize, loff_t *off)
+{
+ int rc, *pos;
+ ssize_t len = 0;
+ u64 id;
+
+ if (*off)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&erst_dbg_mutex) != 0)
+ return -EINTR;
+
+ pos = (int *)&filp->private_data;
+
+retry_next:
+ rc = erst_get_record_id_next(pos, &id);
+ if (rc)
+ goto out;
+ /* no more record */
+ if (id == APEI_ERST_INVALID_RECORD_ID) {
+ /*
+ * If the persistent store is empty initially, the function
+ * 'erst_read' below will return "-ENOENT" value. This causes
+ * 'retry_next' label is entered again. The returned value
+ * should be zero indicating the read operation is EOF.
+ */
+ len = 0;
+
+ goto out;
+ }
+retry:
+ rc = len = erst_read_record(id, erst_dbg_buf, erst_dbg_buf_len,
+ erst_dbg_buf_len, NULL);
+ /* The record may be cleared by others, try read next record */
+ if (rc == -ENOENT)
+ goto retry_next;
+ if (rc < 0)
+ goto out;
+ if (len > ERST_DBG_RECORD_LEN_MAX) {
+ pr_warn(ERST_DBG_PFX
+ "Record (ID: 0x%llx) length is too long: %zd\n", id, len);
+ rc = -EIO;
+ goto out;
+ }
+ if (len > erst_dbg_buf_len) {
+ void *p;
+ rc = -ENOMEM;
+ p = kmalloc(len, GFP_KERNEL);
+ if (!p)
+ goto out;
+ kfree(erst_dbg_buf);
+ erst_dbg_buf = p;
+ erst_dbg_buf_len = len;
+ goto retry;
+ }
+
+ rc = -EINVAL;
+ if (len > usize)
+ goto out;
+
+ rc = -EFAULT;
+ if (copy_to_user(ubuf, erst_dbg_buf, len))
+ goto out;
+ rc = 0;
+out:
+ mutex_unlock(&erst_dbg_mutex);
+ return rc ? rc : len;
+}
+
+static ssize_t erst_dbg_write(struct file *filp, const char __user *ubuf,
+ size_t usize, loff_t *off)
+{
+ int rc;
+ struct cper_record_header *rcd;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (usize > ERST_DBG_RECORD_LEN_MAX) {
+ pr_err(ERST_DBG_PFX "Too long record to be written\n");
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&erst_dbg_mutex))
+ return -EINTR;
+ if (usize > erst_dbg_buf_len) {
+ void *p;
+ rc = -ENOMEM;
+ p = kmalloc(usize, GFP_KERNEL);
+ if (!p)
+ goto out;
+ kfree(erst_dbg_buf);
+ erst_dbg_buf = p;
+ erst_dbg_buf_len = usize;
+ }
+ rc = copy_from_user(erst_dbg_buf, ubuf, usize);
+ if (rc) {
+ rc = -EFAULT;
+ goto out;
+ }
+ rcd = erst_dbg_buf;
+ rc = -EINVAL;
+ if (rcd->record_length != usize)
+ goto out;
+
+ rc = erst_write(erst_dbg_buf);
+
+out:
+ mutex_unlock(&erst_dbg_mutex);
+ return rc < 0 ? rc : usize;
+}
+
+static const struct file_operations erst_dbg_ops = {
+ .owner = THIS_MODULE,
+ .open = erst_dbg_open,
+ .release = erst_dbg_release,
+ .read = erst_dbg_read,
+ .write = erst_dbg_write,
+ .unlocked_ioctl = erst_dbg_ioctl,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice erst_dbg_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "erst_dbg",
+ .fops = &erst_dbg_ops,
+};
+
+static __init int erst_dbg_init(void)
+{
+ if (erst_disable) {
+ pr_info(ERST_DBG_PFX "ERST support is disabled.\n");
+ return -ENODEV;
+ }
+ return misc_register(&erst_dbg_dev);
+}
+
+static __exit void erst_dbg_exit(void)
+{
+ misc_deregister(&erst_dbg_dev);
+ kfree(erst_dbg_buf);
+}
+
+module_init(erst_dbg_init);
+module_exit(erst_dbg_exit);
+
+MODULE_AUTHOR("Huang Ying");
+MODULE_DESCRIPTION("APEI Error Record Serialization Table debug support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
new file mode 100644
index 000000000..247989060
--- /dev/null
+++ b/drivers/acpi/apei/erst.c
@@ -0,0 +1,1267 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * APEI Error Record Serialization Table support
+ *
+ * ERST is a way provided by APEI to save and retrieve hardware error
+ * information to and from a persistent store.
+ *
+ * For more information about ERST, please refer to ACPI Specification
+ * version 4.0, section 17.4.
+ *
+ * Copyright 2010 Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/uaccess.h>
+#include <linux/cper.h>
+#include <linux/nmi.h>
+#include <linux/hardirq.h>
+#include <linux/pstore.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h> /* kvfree() */
+#include <acpi/apei.h>
+
+#include "apei-internal.h"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "ERST: " fmt
+
+/* ERST command status */
+#define ERST_STATUS_SUCCESS 0x0
+#define ERST_STATUS_NOT_ENOUGH_SPACE 0x1
+#define ERST_STATUS_HARDWARE_NOT_AVAILABLE 0x2
+#define ERST_STATUS_FAILED 0x3
+#define ERST_STATUS_RECORD_STORE_EMPTY 0x4
+#define ERST_STATUS_RECORD_NOT_FOUND 0x5
+
+#define ERST_TAB_ENTRY(tab) \
+ ((struct acpi_whea_header *)((char *)(tab) + \
+ sizeof(struct acpi_table_erst)))
+
+#define SPIN_UNIT 100 /* 100ns */
+/* Firmware should respond within 1 milliseconds */
+#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC)
+#define FIRMWARE_MAX_STALL 50 /* 50us */
+
+int erst_disable;
+EXPORT_SYMBOL_GPL(erst_disable);
+
+static struct acpi_table_erst *erst_tab;
+
+/* ERST Error Log Address Range attributes */
+#define ERST_RANGE_RESERVED 0x0001
+#define ERST_RANGE_NVRAM 0x0002
+#define ERST_RANGE_SLOW 0x0004
+
+/*
+ * ERST Error Log Address Range, used as buffer for reading/writing
+ * error records.
+ */
+static struct erst_erange {
+ u64 base;
+ u64 size;
+ void __iomem *vaddr;
+ u32 attr;
+} erst_erange;
+
+/*
+ * Prevent ERST interpreter to run simultaneously, because the
+ * corresponding firmware implementation may not work properly when
+ * invoked simultaneously.
+ *
+ * It is used to provide exclusive accessing for ERST Error Log
+ * Address Range too.
+ */
+static DEFINE_RAW_SPINLOCK(erst_lock);
+
+static inline int erst_errno(int command_status)
+{
+ switch (command_status) {
+ case ERST_STATUS_SUCCESS:
+ return 0;
+ case ERST_STATUS_HARDWARE_NOT_AVAILABLE:
+ return -ENODEV;
+ case ERST_STATUS_NOT_ENOUGH_SPACE:
+ return -ENOSPC;
+ case ERST_STATUS_RECORD_STORE_EMPTY:
+ case ERST_STATUS_RECORD_NOT_FOUND:
+ return -ENOENT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int erst_timedout(u64 *t, u64 spin_unit)
+{
+ if ((s64)*t < spin_unit) {
+ pr_warn(FW_WARN "Firmware does not respond in time.\n");
+ return 1;
+ }
+ *t -= spin_unit;
+ ndelay(spin_unit);
+ touch_nmi_watchdog();
+ return 0;
+}
+
+static int erst_exec_load_var1(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_read_register(entry, &ctx->var1);
+}
+
+static int erst_exec_load_var2(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_read_register(entry, &ctx->var2);
+}
+
+static int erst_exec_store_var1(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_write_register(entry, ctx->var1);
+}
+
+static int erst_exec_add(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ ctx->var1 += ctx->var2;
+ return 0;
+}
+
+static int erst_exec_subtract(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ ctx->var1 -= ctx->var2;
+ return 0;
+}
+
+static int erst_exec_add_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 val;
+
+ rc = __apei_exec_read_register(entry, &val);
+ if (rc)
+ return rc;
+ val += ctx->value;
+ rc = __apei_exec_write_register(entry, val);
+ return rc;
+}
+
+static int erst_exec_subtract_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 val;
+
+ rc = __apei_exec_read_register(entry, &val);
+ if (rc)
+ return rc;
+ val -= ctx->value;
+ rc = __apei_exec_write_register(entry, val);
+ return rc;
+}
+
+static int erst_exec_stall(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ u64 stall_time;
+
+ if (ctx->value > FIRMWARE_MAX_STALL) {
+ if (!in_nmi())
+ pr_warn(FW_WARN
+ "Too long stall time for stall instruction: 0x%llx.\n",
+ ctx->value);
+ stall_time = FIRMWARE_MAX_STALL;
+ } else
+ stall_time = ctx->value;
+ udelay(stall_time);
+ return 0;
+}
+
+static int erst_exec_stall_while_true(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 val;
+ u64 timeout = FIRMWARE_TIMEOUT;
+ u64 stall_time;
+
+ if (ctx->var1 > FIRMWARE_MAX_STALL) {
+ if (!in_nmi())
+ pr_warn(FW_WARN
+ "Too long stall time for stall while true instruction: 0x%llx.\n",
+ ctx->var1);
+ stall_time = FIRMWARE_MAX_STALL;
+ } else
+ stall_time = ctx->var1;
+
+ for (;;) {
+ rc = __apei_exec_read_register(entry, &val);
+ if (rc)
+ return rc;
+ if (val != ctx->value)
+ break;
+ if (erst_timedout(&timeout, stall_time * NSEC_PER_USEC))
+ return -EIO;
+ }
+ return 0;
+}
+
+static int erst_exec_skip_next_instruction_if_true(
+ struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 val;
+
+ rc = __apei_exec_read_register(entry, &val);
+ if (rc)
+ return rc;
+ if (val == ctx->value) {
+ ctx->ip += 2;
+ return APEI_EXEC_SET_IP;
+ }
+
+ return 0;
+}
+
+static int erst_exec_goto(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ ctx->ip = ctx->value;
+ return APEI_EXEC_SET_IP;
+}
+
+static int erst_exec_set_src_address_base(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_read_register(entry, &ctx->src_base);
+}
+
+static int erst_exec_set_dst_address_base(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_read_register(entry, &ctx->dst_base);
+}
+
+static int erst_exec_move_data(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 offset;
+ void *src, *dst;
+
+ /* ioremap does not work in interrupt context */
+ if (in_interrupt()) {
+ pr_warn("MOVE_DATA can not be used in interrupt context.\n");
+ return -EBUSY;
+ }
+
+ rc = __apei_exec_read_register(entry, &offset);
+ if (rc)
+ return rc;
+
+ src = ioremap(ctx->src_base + offset, ctx->var2);
+ if (!src)
+ return -ENOMEM;
+ dst = ioremap(ctx->dst_base + offset, ctx->var2);
+ if (!dst) {
+ iounmap(src);
+ return -ENOMEM;
+ }
+
+ memmove(dst, src, ctx->var2);
+
+ iounmap(src);
+ iounmap(dst);
+
+ return 0;
+}
+
+static struct apei_exec_ins_type erst_ins_type[] = {
+ [ACPI_ERST_READ_REGISTER] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_read_register,
+ },
+ [ACPI_ERST_READ_REGISTER_VALUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_read_register_value,
+ },
+ [ACPI_ERST_WRITE_REGISTER] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_write_register,
+ },
+ [ACPI_ERST_WRITE_REGISTER_VALUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_write_register_value,
+ },
+ [ACPI_ERST_NOOP] = {
+ .flags = 0,
+ .run = apei_exec_noop,
+ },
+ [ACPI_ERST_LOAD_VAR1] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_load_var1,
+ },
+ [ACPI_ERST_LOAD_VAR2] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_load_var2,
+ },
+ [ACPI_ERST_STORE_VAR1] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_store_var1,
+ },
+ [ACPI_ERST_ADD] = {
+ .flags = 0,
+ .run = erst_exec_add,
+ },
+ [ACPI_ERST_SUBTRACT] = {
+ .flags = 0,
+ .run = erst_exec_subtract,
+ },
+ [ACPI_ERST_ADD_VALUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_add_value,
+ },
+ [ACPI_ERST_SUBTRACT_VALUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_subtract_value,
+ },
+ [ACPI_ERST_STALL] = {
+ .flags = 0,
+ .run = erst_exec_stall,
+ },
+ [ACPI_ERST_STALL_WHILE_TRUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_stall_while_true,
+ },
+ [ACPI_ERST_SKIP_NEXT_IF_TRUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_skip_next_instruction_if_true,
+ },
+ [ACPI_ERST_GOTO] = {
+ .flags = 0,
+ .run = erst_exec_goto,
+ },
+ [ACPI_ERST_SET_SRC_ADDRESS_BASE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_set_src_address_base,
+ },
+ [ACPI_ERST_SET_DST_ADDRESS_BASE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_set_dst_address_base,
+ },
+ [ACPI_ERST_MOVE_DATA] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_move_data,
+ },
+};
+
+static inline void erst_exec_ctx_init(struct apei_exec_context *ctx)
+{
+ apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type),
+ ERST_TAB_ENTRY(erst_tab), erst_tab->entries);
+}
+
+static int erst_get_erange(struct erst_erange *range)
+{
+ struct apei_exec_context ctx;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE);
+ if (rc)
+ return rc;
+ range->base = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH);
+ if (rc)
+ return rc;
+ range->size = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES);
+ if (rc)
+ return rc;
+ range->attr = apei_exec_ctx_get_output(&ctx);
+
+ return 0;
+}
+
+static ssize_t __erst_get_record_count(void)
+{
+ struct apei_exec_context ctx;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT);
+ if (rc)
+ return rc;
+ return apei_exec_ctx_get_output(&ctx);
+}
+
+ssize_t erst_get_record_count(void)
+{
+ ssize_t count;
+ unsigned long flags;
+
+ if (erst_disable)
+ return -ENODEV;
+
+ raw_spin_lock_irqsave(&erst_lock, flags);
+ count = __erst_get_record_count();
+ raw_spin_unlock_irqrestore(&erst_lock, flags);
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(erst_get_record_count);
+
+#define ERST_RECORD_ID_CACHE_SIZE_MIN 16
+#define ERST_RECORD_ID_CACHE_SIZE_MAX 1024
+
+struct erst_record_id_cache {
+ struct mutex lock;
+ u64 *entries;
+ int len;
+ int size;
+ int refcount;
+};
+
+static struct erst_record_id_cache erst_record_id_cache = {
+ .lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock),
+ .refcount = 0,
+};
+
+static int __erst_get_next_record_id(u64 *record_id)
+{
+ struct apei_exec_context ctx;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID);
+ if (rc)
+ return rc;
+ *record_id = apei_exec_ctx_get_output(&ctx);
+
+ return 0;
+}
+
+int erst_get_record_id_begin(int *pos)
+{
+ int rc;
+
+ if (erst_disable)
+ return -ENODEV;
+
+ rc = mutex_lock_interruptible(&erst_record_id_cache.lock);
+ if (rc)
+ return rc;
+ erst_record_id_cache.refcount++;
+ mutex_unlock(&erst_record_id_cache.lock);
+
+ *pos = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(erst_get_record_id_begin);
+
+/* erst_record_id_cache.lock must be held by caller */
+static int __erst_record_id_cache_add_one(void)
+{
+ u64 id, prev_id, first_id;
+ int i, rc;
+ u64 *entries;
+ unsigned long flags;
+
+ id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID;
+retry:
+ raw_spin_lock_irqsave(&erst_lock, flags);
+ rc = __erst_get_next_record_id(&id);
+ raw_spin_unlock_irqrestore(&erst_lock, flags);
+ if (rc == -ENOENT)
+ return 0;
+ if (rc)
+ return rc;
+ if (id == APEI_ERST_INVALID_RECORD_ID)
+ return 0;
+ /* can not skip current ID, or loop back to first ID */
+ if (id == prev_id || id == first_id)
+ return 0;
+ if (first_id == APEI_ERST_INVALID_RECORD_ID)
+ first_id = id;
+ prev_id = id;
+
+ entries = erst_record_id_cache.entries;
+ for (i = 0; i < erst_record_id_cache.len; i++) {
+ if (entries[i] == id)
+ break;
+ }
+ /* record id already in cache, try next */
+ if (i < erst_record_id_cache.len)
+ goto retry;
+ if (erst_record_id_cache.len >= erst_record_id_cache.size) {
+ int new_size;
+ u64 *new_entries;
+
+ new_size = erst_record_id_cache.size * 2;
+ new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN,
+ ERST_RECORD_ID_CACHE_SIZE_MAX);
+ if (new_size <= erst_record_id_cache.size) {
+ if (printk_ratelimit())
+ pr_warn(FW_WARN "too many record IDs!\n");
+ return 0;
+ }
+ new_entries = kvmalloc_array(new_size, sizeof(entries[0]),
+ GFP_KERNEL);
+ if (!new_entries)
+ return -ENOMEM;
+ memcpy(new_entries, entries,
+ erst_record_id_cache.len * sizeof(entries[0]));
+ kvfree(entries);
+ erst_record_id_cache.entries = entries = new_entries;
+ erst_record_id_cache.size = new_size;
+ }
+ entries[i] = id;
+ erst_record_id_cache.len++;
+
+ return 1;
+}
+
+/*
+ * Get the record ID of an existing error record on the persistent
+ * storage. If there is no error record on the persistent storage, the
+ * returned record_id is APEI_ERST_INVALID_RECORD_ID.
+ */
+int erst_get_record_id_next(int *pos, u64 *record_id)
+{
+ int rc = 0;
+ u64 *entries;
+
+ if (erst_disable)
+ return -ENODEV;
+
+ /* must be enclosed by erst_get_record_id_begin/end */
+ BUG_ON(!erst_record_id_cache.refcount);
+ BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len);
+
+ mutex_lock(&erst_record_id_cache.lock);
+ entries = erst_record_id_cache.entries;
+ for (; *pos < erst_record_id_cache.len; (*pos)++)
+ if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID)
+ break;
+ /* found next record id in cache */
+ if (*pos < erst_record_id_cache.len) {
+ *record_id = entries[*pos];
+ (*pos)++;
+ goto out_unlock;
+ }
+
+ /* Try to add one more record ID to cache */
+ rc = __erst_record_id_cache_add_one();
+ if (rc < 0)
+ goto out_unlock;
+ /* successfully add one new ID */
+ if (rc == 1) {
+ *record_id = erst_record_id_cache.entries[*pos];
+ (*pos)++;
+ rc = 0;
+ } else {
+ *pos = -1;
+ *record_id = APEI_ERST_INVALID_RECORD_ID;
+ }
+out_unlock:
+ mutex_unlock(&erst_record_id_cache.lock);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(erst_get_record_id_next);
+
+/* erst_record_id_cache.lock must be held by caller */
+static void __erst_record_id_cache_compact(void)
+{
+ int i, wpos = 0;
+ u64 *entries;
+
+ if (erst_record_id_cache.refcount)
+ return;
+
+ entries = erst_record_id_cache.entries;
+ for (i = 0; i < erst_record_id_cache.len; i++) {
+ if (entries[i] == APEI_ERST_INVALID_RECORD_ID)
+ continue;
+ if (wpos != i)
+ entries[wpos] = entries[i];
+ wpos++;
+ }
+ erst_record_id_cache.len = wpos;
+}
+
+void erst_get_record_id_end(void)
+{
+ /*
+ * erst_disable != 0 should be detected by invoker via the
+ * return value of erst_get_record_id_begin/next, so this
+ * function should not be called for erst_disable != 0.
+ */
+ BUG_ON(erst_disable);
+
+ mutex_lock(&erst_record_id_cache.lock);
+ erst_record_id_cache.refcount--;
+ BUG_ON(erst_record_id_cache.refcount < 0);
+ __erst_record_id_cache_compact();
+ mutex_unlock(&erst_record_id_cache.lock);
+}
+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 val;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE);
+ if (rc)
+ return rc;
+ apei_exec_ctx_set_input(&ctx, offset);
+ rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
+ if (rc)
+ return rc;
+ rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
+ if (rc)
+ return rc;
+ for (;;) {
+ rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ if (!val)
+ break;
+ if (erst_timedout(&timeout, SPIN_UNIT))
+ return -EIO;
+ }
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
+ if (rc)
+ return rc;
+
+ return erst_errno(val);
+}
+
+static int __erst_read_from_storage(u64 record_id, u64 offset)
+{
+ struct apei_exec_context ctx;
+ u64 timeout = FIRMWARE_TIMEOUT;
+ u64 val;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ);
+ if (rc)
+ return rc;
+ apei_exec_ctx_set_input(&ctx, offset);
+ rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
+ if (rc)
+ return rc;
+ apei_exec_ctx_set_input(&ctx, record_id);
+ rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
+ if (rc)
+ return rc;
+ rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
+ if (rc)
+ return rc;
+ for (;;) {
+ rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ if (!val)
+ break;
+ if (erst_timedout(&timeout, SPIN_UNIT))
+ return -EIO;
+ }
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
+ if (rc)
+ return rc;
+
+ return erst_errno(val);
+}
+
+static int __erst_clear_from_storage(u64 record_id)
+{
+ struct apei_exec_context ctx;
+ u64 timeout = FIRMWARE_TIMEOUT;
+ u64 val;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR);
+ if (rc)
+ return rc;
+ apei_exec_ctx_set_input(&ctx, record_id);
+ rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
+ if (rc)
+ return rc;
+ rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
+ if (rc)
+ return rc;
+ for (;;) {
+ rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ if (!val)
+ break;
+ if (erst_timedout(&timeout, SPIN_UNIT))
+ return -EIO;
+ }
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
+ if (rc)
+ return rc;
+
+ return erst_errno(val);
+}
+
+/* NVRAM ERST Error Log Address Range is not supported yet */
+static void pr_unimpl_nvram(void)
+{
+ if (printk_ratelimit())
+ pr_warn("NVRAM ERST Log Address Range not implemented yet.\n");
+}
+
+static int __erst_write_to_nvram(const struct cper_record_header *record)
+{
+ /* do not print message, because printk is not safe for NMI */
+ return -ENOSYS;
+}
+
+static int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset)
+{
+ pr_unimpl_nvram();
+ return -ENOSYS;
+}
+
+static int __erst_clear_from_nvram(u64 record_id)
+{
+ pr_unimpl_nvram();
+ return -ENOSYS;
+}
+
+int erst_write(const struct cper_record_header *record)
+{
+ int rc;
+ unsigned long flags;
+ struct cper_record_header *rcd_erange;
+
+ if (erst_disable)
+ return -ENODEV;
+
+ if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE))
+ return -EINVAL;
+
+ if (erst_erange.attr & ERST_RANGE_NVRAM) {
+ if (!raw_spin_trylock_irqsave(&erst_lock, flags))
+ return -EBUSY;
+ rc = __erst_write_to_nvram(record);
+ raw_spin_unlock_irqrestore(&erst_lock, flags);
+ return rc;
+ }
+
+ if (record->record_length > erst_erange.size)
+ return -EINVAL;
+
+ if (!raw_spin_trylock_irqsave(&erst_lock, flags))
+ return -EBUSY;
+ memcpy(erst_erange.vaddr, record, record->record_length);
+ rcd_erange = erst_erange.vaddr;
+ /* signature for serialization system */
+ memcpy(&rcd_erange->persistence_information, "ER", 2);
+
+ rc = __erst_write_to_storage(0);
+ raw_spin_unlock_irqrestore(&erst_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(erst_write);
+
+static int __erst_read_to_erange(u64 record_id, u64 *offset)
+{
+ int rc;
+
+ if (erst_erange.attr & ERST_RANGE_NVRAM)
+ return __erst_read_to_erange_from_nvram(
+ record_id, offset);
+
+ rc = __erst_read_from_storage(record_id, 0);
+ if (rc)
+ return rc;
+ *offset = 0;
+
+ return 0;
+}
+
+static ssize_t __erst_read(u64 record_id, struct cper_record_header *record,
+ size_t buflen)
+{
+ int rc;
+ u64 offset, len = 0;
+ struct cper_record_header *rcd_tmp;
+
+ rc = __erst_read_to_erange(record_id, &offset);
+ if (rc)
+ return rc;
+ rcd_tmp = erst_erange.vaddr + offset;
+ len = rcd_tmp->record_length;
+ if (len <= buflen)
+ memcpy(record, rcd_tmp, len);
+
+ return len;
+}
+
+/*
+ * If return value > buflen, the buffer size is not big enough,
+ * else if return value < 0, something goes wrong,
+ * else everything is OK, and return value is record length
+ */
+ssize_t erst_read(u64 record_id, struct cper_record_header *record,
+ size_t buflen)
+{
+ ssize_t len;
+ unsigned long flags;
+
+ if (erst_disable)
+ return -ENODEV;
+
+ raw_spin_lock_irqsave(&erst_lock, flags);
+ len = __erst_read(record_id, record, buflen);
+ raw_spin_unlock_irqrestore(&erst_lock, flags);
+ return len;
+}
+EXPORT_SYMBOL_GPL(erst_read);
+
+static void erst_clear_cache(u64 record_id)
+{
+ int i;
+ u64 *entries;
+
+ mutex_lock(&erst_record_id_cache.lock);
+
+ entries = erst_record_id_cache.entries;
+ for (i = 0; i < erst_record_id_cache.len; i++) {
+ if (entries[i] == record_id)
+ entries[i] = APEI_ERST_INVALID_RECORD_ID;
+ }
+ __erst_record_id_cache_compact();
+
+ mutex_unlock(&erst_record_id_cache.lock);
+}
+
+ssize_t erst_read_record(u64 record_id, struct cper_record_header *record,
+ size_t buflen, size_t recordlen, const guid_t *creatorid)
+{
+ ssize_t len;
+
+ /*
+ * if creatorid is NULL, read any record for erst-dbg module
+ */
+ if (creatorid == NULL) {
+ len = erst_read(record_id, record, buflen);
+ if (len == -ENOENT)
+ erst_clear_cache(record_id);
+
+ return len;
+ }
+
+ len = erst_read(record_id, record, buflen);
+ /*
+ * if erst_read return value is -ENOENT skip to next record_id,
+ * and clear the record_id cache.
+ */
+ if (len == -ENOENT) {
+ erst_clear_cache(record_id);
+ goto out;
+ }
+
+ if (len < 0)
+ goto out;
+
+ /*
+ * if erst_read return value is less than record head length,
+ * consider it as -EIO, and clear the record_id cache.
+ */
+ if (len < recordlen) {
+ len = -EIO;
+ erst_clear_cache(record_id);
+ goto out;
+ }
+
+ /*
+ * if creatorid is not wanted, consider it as not found,
+ * for skipping to next record_id.
+ */
+ if (!guid_equal(&record->creator_id, creatorid))
+ len = -ENOENT;
+
+out:
+ return len;
+}
+EXPORT_SYMBOL_GPL(erst_read_record);
+
+int erst_clear(u64 record_id)
+{
+ int rc, i;
+ unsigned long flags;
+ u64 *entries;
+
+ if (erst_disable)
+ return -ENODEV;
+
+ rc = mutex_lock_interruptible(&erst_record_id_cache.lock);
+ if (rc)
+ return rc;
+ raw_spin_lock_irqsave(&erst_lock, flags);
+ if (erst_erange.attr & ERST_RANGE_NVRAM)
+ rc = __erst_clear_from_nvram(record_id);
+ else
+ rc = __erst_clear_from_storage(record_id);
+ raw_spin_unlock_irqrestore(&erst_lock, flags);
+ if (rc)
+ goto out;
+ entries = erst_record_id_cache.entries;
+ for (i = 0; i < erst_record_id_cache.len; i++) {
+ if (entries[i] == record_id)
+ entries[i] = APEI_ERST_INVALID_RECORD_ID;
+ }
+ __erst_record_id_cache_compact();
+out:
+ mutex_unlock(&erst_record_id_cache.lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(erst_clear);
+
+static int __init setup_erst_disable(char *str)
+{
+ erst_disable = 1;
+ return 1;
+}
+
+__setup("erst_disable", setup_erst_disable);
+
+static int erst_check_table(struct acpi_table_erst *erst_tab)
+{
+ if ((erst_tab->header_length !=
+ (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header)))
+ && (erst_tab->header_length != sizeof(struct acpi_table_erst)))
+ return -EINVAL;
+ if (erst_tab->header.length < sizeof(struct acpi_table_erst))
+ return -EINVAL;
+ if (erst_tab->entries !=
+ (erst_tab->header.length - sizeof(struct acpi_table_erst)) /
+ sizeof(struct acpi_erst_entry))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int erst_open_pstore(struct pstore_info *psi);
+static int erst_close_pstore(struct pstore_info *psi);
+static ssize_t erst_reader(struct pstore_record *record);
+static int erst_writer(struct pstore_record *record);
+static int erst_clearer(struct pstore_record *record);
+
+static struct pstore_info erst_info = {
+ .owner = THIS_MODULE,
+ .name = "erst",
+ .flags = PSTORE_FLAGS_DMESG,
+ .open = erst_open_pstore,
+ .close = erst_close_pstore,
+ .read = erst_reader,
+ .write = erst_writer,
+ .erase = erst_clearer
+};
+
+#define CPER_CREATOR_PSTORE \
+ GUID_INIT(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \
+ 0x64, 0x90, 0xb8, 0x9d)
+#define CPER_SECTION_TYPE_DMESG \
+ GUID_INIT(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \
+ 0x94, 0x19, 0xeb, 0x12)
+#define CPER_SECTION_TYPE_DMESG_Z \
+ GUID_INIT(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d, \
+ 0x34, 0xdd, 0xfa, 0xc6)
+#define CPER_SECTION_TYPE_MCE \
+ GUID_INIT(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \
+ 0x04, 0x4a, 0x38, 0xfc)
+
+struct cper_pstore_record {
+ struct cper_record_header hdr;
+ struct cper_section_descriptor sec_hdr;
+ char data[];
+} __packed;
+
+static int reader_pos;
+
+static int erst_open_pstore(struct pstore_info *psi)
+{
+ if (erst_disable)
+ return -ENODEV;
+
+ return erst_get_record_id_begin(&reader_pos);
+}
+
+static int erst_close_pstore(struct pstore_info *psi)
+{
+ erst_get_record_id_end();
+
+ return 0;
+}
+
+static ssize_t erst_reader(struct pstore_record *record)
+{
+ int rc;
+ ssize_t len = 0;
+ u64 record_id;
+ struct cper_pstore_record *rcd;
+ size_t rcd_len = sizeof(*rcd) + erst_info.bufsize;
+
+ if (erst_disable)
+ return -ENODEV;
+
+ rcd = kmalloc(rcd_len, GFP_KERNEL);
+ if (!rcd) {
+ rc = -ENOMEM;
+ goto out;
+ }
+skip:
+ rc = erst_get_record_id_next(&reader_pos, &record_id);
+ if (rc)
+ goto out;
+
+ /* no more record */
+ if (record_id == APEI_ERST_INVALID_RECORD_ID) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ len = erst_read_record(record_id, &rcd->hdr, rcd_len, sizeof(*rcd),
+ &CPER_CREATOR_PSTORE);
+ /* The record may be cleared by others, try read next record */
+ if (len == -ENOENT)
+ goto skip;
+ else if (len < 0)
+ goto out;
+
+ record->buf = kmalloc(len, GFP_KERNEL);
+ if (record->buf == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memcpy(record->buf, rcd->data, len - sizeof(*rcd));
+ record->id = record_id;
+ record->compressed = false;
+ record->ecc_notice_size = 0;
+ if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_DMESG_Z)) {
+ record->type = PSTORE_TYPE_DMESG;
+ record->compressed = true;
+ } else if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_DMESG))
+ record->type = PSTORE_TYPE_DMESG;
+ else if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_MCE))
+ record->type = PSTORE_TYPE_MCE;
+ else
+ record->type = PSTORE_TYPE_MAX;
+
+ if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP)
+ record->time.tv_sec = rcd->hdr.timestamp;
+ else
+ record->time.tv_sec = 0;
+ record->time.tv_nsec = 0;
+
+out:
+ kfree(rcd);
+ return (rc < 0) ? rc : (len - sizeof(*rcd));
+}
+
+static int erst_writer(struct pstore_record *record)
+{
+ struct cper_pstore_record *rcd = (struct cper_pstore_record *)
+ (erst_info.buf - sizeof(*rcd));
+ int ret;
+
+ memset(rcd, 0, sizeof(*rcd));
+ memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
+ rcd->hdr.revision = CPER_RECORD_REV;
+ rcd->hdr.signature_end = CPER_SIG_END;
+ rcd->hdr.section_count = 1;
+ rcd->hdr.error_severity = CPER_SEV_FATAL;
+ /* timestamp valid. platform_id, partition_id are invalid */
+ rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP;
+ rcd->hdr.timestamp = ktime_get_real_seconds();
+ rcd->hdr.record_length = sizeof(*rcd) + record->size;
+ rcd->hdr.creator_id = CPER_CREATOR_PSTORE;
+ rcd->hdr.notification_type = CPER_NOTIFY_MCE;
+ rcd->hdr.record_id = cper_next_record_id();
+ rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR;
+
+ rcd->sec_hdr.section_offset = sizeof(*rcd);
+ rcd->sec_hdr.section_length = record->size;
+ rcd->sec_hdr.revision = CPER_SEC_REV;
+ /* fru_id and fru_text is invalid */
+ rcd->sec_hdr.validation_bits = 0;
+ rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
+ switch (record->type) {
+ case PSTORE_TYPE_DMESG:
+ if (record->compressed)
+ rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z;
+ else
+ rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
+ break;
+ case PSTORE_TYPE_MCE:
+ rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
+
+ ret = erst_write(&rcd->hdr);
+ record->id = rcd->hdr.record_id;
+
+ return ret;
+}
+
+static int erst_clearer(struct pstore_record *record)
+{
+ return erst_clear(record->id);
+}
+
+static int __init erst_init(void)
+{
+ int rc = 0;
+ acpi_status status;
+ struct apei_exec_context ctx;
+ struct apei_resources erst_resources;
+ struct resource *r;
+ char *buf;
+
+ if (acpi_disabled)
+ goto err;
+
+ if (erst_disable) {
+ pr_info(
+ "Error Record Serialization Table (ERST) support is disabled.\n");
+ goto err;
+ }
+
+ status = acpi_get_table(ACPI_SIG_ERST, 0,
+ (struct acpi_table_header **)&erst_tab);
+ if (status == AE_NOT_FOUND)
+ goto err;
+ else if (ACPI_FAILURE(status)) {
+ const char *msg = acpi_format_exception(status);
+ pr_err("Failed to get table, %s\n", msg);
+ rc = -EINVAL;
+ goto err;
+ }
+
+ rc = erst_check_table(erst_tab);
+ if (rc) {
+ pr_err(FW_BUG "ERST table is invalid.\n");
+ goto err_put_erst_tab;
+ }
+
+ apei_resources_init(&erst_resources);
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_collect_resources(&ctx, &erst_resources);
+ if (rc)
+ goto err_fini;
+ rc = apei_resources_request(&erst_resources, "APEI ERST");
+ if (rc)
+ goto err_fini;
+ rc = apei_exec_pre_map_gars(&ctx);
+ if (rc)
+ goto err_release;
+ rc = erst_get_erange(&erst_erange);
+ if (rc) {
+ if (rc == -ENODEV)
+ pr_info(
+ "The corresponding hardware device or firmware implementation "
+ "is not available.\n");
+ else
+ pr_err("Failed to get Error Log Address Range.\n");
+ goto err_unmap_reg;
+ }
+
+ r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST");
+ if (!r) {
+ pr_err("Can not request [mem %#010llx-%#010llx] for ERST.\n",
+ (unsigned long long)erst_erange.base,
+ (unsigned long long)erst_erange.base + erst_erange.size - 1);
+ rc = -EIO;
+ goto err_unmap_reg;
+ }
+ rc = -ENOMEM;
+ erst_erange.vaddr = ioremap_cache(erst_erange.base,
+ erst_erange.size);
+ if (!erst_erange.vaddr)
+ goto err_release_erange;
+
+ pr_info(
+ "Error Record Serialization Table (ERST) support is initialized.\n");
+
+ buf = kmalloc(erst_erange.size, GFP_KERNEL);
+ if (buf) {
+ erst_info.buf = buf + sizeof(struct cper_pstore_record);
+ erst_info.bufsize = erst_erange.size -
+ sizeof(struct cper_pstore_record);
+ rc = pstore_register(&erst_info);
+ if (rc) {
+ if (rc != -EPERM)
+ pr_info(
+ "Could not register with persistent store.\n");
+ erst_info.buf = NULL;
+ erst_info.bufsize = 0;
+ kfree(buf);
+ }
+ } else
+ pr_err(
+ "Failed to allocate %lld bytes for persistent store error log.\n",
+ erst_erange.size);
+
+ /* Cleanup ERST Resources */
+ apei_resources_fini(&erst_resources);
+
+ return 0;
+
+err_release_erange:
+ release_mem_region(erst_erange.base, erst_erange.size);
+err_unmap_reg:
+ apei_exec_post_unmap_gars(&ctx);
+err_release:
+ apei_resources_release(&erst_resources);
+err_fini:
+ apei_resources_fini(&erst_resources);
+err_put_erst_tab:
+ acpi_put_table((struct acpi_table_header *)erst_tab);
+err:
+ erst_disable = 1;
+ return rc;
+}
+
+device_initcall(erst_init);
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
new file mode 100644
index 000000000..9952f3a79
--- /dev/null
+++ b/drivers/acpi/apei/ghes.c
@@ -0,0 +1,1499 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * APEI Generic Hardware Error Source support
+ *
+ * Generic Hardware Error Source provides a way to report platform
+ * hardware errors (such as that from chipset). It works in so called
+ * "Firmware First" mode, that is, hardware errors are reported to
+ * firmware firstly, then reported to Linux by firmware. This way,
+ * some non-standard hardware error registers or non-standard hardware
+ * link can be checked by firmware to produce more hardware error
+ * information for Linux.
+ *
+ * For more information about Generic Hardware Error Source, please
+ * refer to ACPI Specification version 4.0, section 17.3.2.6
+ *
+ * Copyright 2010,2011 Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.com>
+ */
+
+#include <linux/arm_sdei.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/cper.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/ratelimit.h>
+#include <linux/vmalloc.h>
+#include <linux/irq_work.h>
+#include <linux/llist.h>
+#include <linux/genalloc.h>
+#include <linux/pci.h>
+#include <linux/pfn.h>
+#include <linux/aer.h>
+#include <linux/nmi.h>
+#include <linux/sched/clock.h>
+#include <linux/uuid.h>
+#include <linux/ras.h>
+#include <linux/task_work.h>
+
+#include <acpi/actbl1.h>
+#include <acpi/ghes.h>
+#include <acpi/apei.h>
+#include <asm/fixmap.h>
+#include <asm/tlbflush.h>
+#include <ras/ras_event.h>
+
+#include "apei-internal.h"
+
+#define GHES_PFX "GHES: "
+
+#define GHES_ESTATUS_MAX_SIZE 65536
+#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536
+
+#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3
+
+/* This is just an estimation for memory pool allocation */
+#define GHES_ESTATUS_CACHE_AVG_SIZE 512
+
+#define GHES_ESTATUS_CACHES_SIZE 4
+
+#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL
+/* Prevent too many caches are allocated because of RCU */
+#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2)
+
+#define GHES_ESTATUS_CACHE_LEN(estatus_len) \
+ (sizeof(struct ghes_estatus_cache) + (estatus_len))
+#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \
+ ((struct acpi_hest_generic_status *) \
+ ((struct ghes_estatus_cache *)(estatus_cache) + 1))
+
+#define GHES_ESTATUS_NODE_LEN(estatus_len) \
+ (sizeof(struct ghes_estatus_node) + (estatus_len))
+#define GHES_ESTATUS_FROM_NODE(estatus_node) \
+ ((struct acpi_hest_generic_status *) \
+ ((struct ghes_estatus_node *)(estatus_node) + 1))
+
+#define GHES_VENDOR_ENTRY_LEN(gdata_len) \
+ (sizeof(struct ghes_vendor_record_entry) + (gdata_len))
+#define GHES_GDATA_FROM_VENDOR_ENTRY(vendor_entry) \
+ ((struct acpi_hest_generic_data *) \
+ ((struct ghes_vendor_record_entry *)(vendor_entry) + 1))
+
+/*
+ * NMI-like notifications vary by architecture, before the compiler can prune
+ * unused static functions it needs a value for these enums.
+ */
+#ifndef CONFIG_ARM_SDE_INTERFACE
+#define FIX_APEI_GHES_SDEI_NORMAL __end_of_fixed_addresses
+#define FIX_APEI_GHES_SDEI_CRITICAL __end_of_fixed_addresses
+#endif
+
+static inline bool is_hest_type_generic_v2(struct ghes *ghes)
+{
+ return ghes->generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR_V2;
+}
+
+/*
+ * 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.
+ */
+bool ghes_disable;
+module_param_named(disable, ghes_disable, 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.
+ *
+ * RCU is used for these lists, so ghes_list_mutex is only used for
+ * list changing, not for traversing.
+ */
+static LIST_HEAD(ghes_hed);
+static DEFINE_MUTEX(ghes_list_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
+ * the fixmap is used instead.
+ *
+ * This spinlock is used to prevent the fixmap entry from being used
+ * simultaneously.
+ */
+static DEFINE_SPINLOCK(ghes_notify_lock_irq);
+
+struct ghes_vendor_record_entry {
+ struct work_struct work;
+ int error_severity;
+ char vendor_record[];
+};
+
+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 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;
+ pgprot_t prot;
+
+ paddr = PFN_PHYS(pfn);
+ prot = arch_apei_get_mem_attribute(paddr);
+ __set_fixmap(fixmap_idx, paddr, prot);
+
+ return (void __iomem *) __fix_to_virt(fixmap_idx);
+}
+
+static void ghes_unmap(void __iomem *vaddr, enum fixed_addresses fixmap_idx)
+{
+ int _idx = virt_to_fix((unsigned long)vaddr);
+
+ WARN_ON_ONCE(fixmap_idx != _idx);
+ clear_fixmap(fixmap_idx);
+}
+
+int ghes_estatus_pool_init(unsigned int num_ghes)
+{
+ unsigned long addr, len;
+ int rc;
+
+ ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1);
+ if (!ghes_estatus_pool)
+ return -ENOMEM;
+
+ 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;
+
+ rc = gen_pool_add(ghes_estatus_pool, addr, PAGE_ALIGN(len), -1);
+ if (rc)
+ goto err_pool_add;
+
+ return 0;
+
+err_pool_add:
+ vfree((void *)addr);
+
+err_pool_alloc:
+ gen_pool_destroy(ghes_estatus_pool);
+
+ return -ENOMEM;
+}
+
+static int map_gen_v2(struct ghes *ghes)
+{
+ return apei_map_generic_address(&ghes->generic_v2->read_ack_register);
+}
+
+static void unmap_gen_v2(struct ghes *ghes)
+{
+ apei_unmap_generic_address(&ghes->generic_v2->read_ack_register);
+}
+
+static void ghes_ack_error(struct acpi_hest_generic_v2 *gv2)
+{
+ int rc;
+ u64 val = 0;
+
+ rc = apei_read(&val, &gv2->read_ack_register);
+ if (rc)
+ return;
+
+ val &= gv2->read_ack_preserve << gv2->read_ack_register.bit_offset;
+ val |= gv2->read_ack_write << gv2->read_ack_register.bit_offset;
+
+ apei_write(val, &gv2->read_ack_register);
+}
+
+static struct ghes *ghes_new(struct acpi_hest_generic *generic)
+{
+ struct ghes *ghes;
+ unsigned int error_block_length;
+ int rc;
+
+ ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
+ if (!ghes)
+ return ERR_PTR(-ENOMEM);
+
+ ghes->generic = generic;
+ if (is_hest_type_generic_v2(ghes)) {
+ rc = map_gen_v2(ghes);
+ if (rc)
+ goto err_free;
+ }
+
+ rc = apei_map_generic_address(&generic->error_status_address);
+ if (rc)
+ goto err_unmap_read_ack_addr;
+ error_block_length = generic->error_block_length;
+ if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
+ pr_warn(FW_WARN GHES_PFX
+ "Error status block length is too long: %u for "
+ "generic hardware error source: %d.\n",
+ error_block_length, generic->header.source_id);
+ error_block_length = GHES_ESTATUS_MAX_SIZE;
+ }
+ ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
+ if (!ghes->estatus) {
+ rc = -ENOMEM;
+ goto err_unmap_status_addr;
+ }
+
+ return ghes;
+
+err_unmap_status_addr:
+ apei_unmap_generic_address(&generic->error_status_address);
+err_unmap_read_ack_addr:
+ if (is_hest_type_generic_v2(ghes))
+ unmap_gen_v2(ghes);
+err_free:
+ kfree(ghes);
+ return ERR_PTR(rc);
+}
+
+static void ghes_fini(struct ghes *ghes)
+{
+ kfree(ghes->estatus);
+ apei_unmap_generic_address(&ghes->generic->error_status_address);
+ if (is_hest_type_generic_v2(ghes))
+ unmap_gen_v2(ghes);
+}
+
+static inline int ghes_severity(int severity)
+{
+ switch (severity) {
+ case CPER_SEV_INFORMATIONAL:
+ return GHES_SEV_NO;
+ case CPER_SEV_CORRECTED:
+ return GHES_SEV_CORRECTED;
+ case CPER_SEV_RECOVERABLE:
+ return GHES_SEV_RECOVERABLE;
+ case CPER_SEV_FATAL:
+ return GHES_SEV_PANIC;
+ default:
+ /* Unknown, go panic */
+ return GHES_SEV_PANIC;
+ }
+}
+
+static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
+ int from_phys,
+ enum fixed_addresses fixmap_idx)
+{
+ void __iomem *vaddr;
+ u64 offset;
+ u32 trunk;
+
+ while (len > 0) {
+ offset = paddr - (paddr & PAGE_MASK);
+ vaddr = ghes_map(PHYS_PFN(paddr), fixmap_idx);
+ trunk = PAGE_SIZE - offset;
+ trunk = min(trunk, len);
+ if (from_phys)
+ memcpy_fromio(buffer, vaddr + offset, trunk);
+ else
+ memcpy_toio(vaddr + offset, buffer, trunk);
+ len -= trunk;
+ paddr += trunk;
+ buffer += trunk;
+ ghes_unmap(vaddr, fixmap_idx);
+ }
+}
+
+/* Check the top-level record header has an appropriate size. */
+static int __ghes_check_estatus(struct ghes *ghes,
+ struct acpi_hest_generic_status *estatus)
+{
+ u32 len = cper_estatus_len(estatus);
+
+ if (len < sizeof(*estatus)) {
+ pr_warn_ratelimited(FW_WARN GHES_PFX "Truncated error status block!\n");
+ return -EIO;
+ }
+
+ if (len > ghes->generic->error_block_length) {
+ pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid error status block length!\n");
+ return -EIO;
+ }
+
+ if (cper_estatus_check_header(estatus)) {
+ pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid CPER header!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* Read the CPER block, returning its address, and header in estatus. */
+static int __ghes_peek_estatus(struct ghes *ghes,
+ struct acpi_hest_generic_status *estatus,
+ u64 *buf_paddr, enum fixed_addresses fixmap_idx)
+{
+ struct acpi_hest_generic *g = ghes->generic;
+ int rc;
+
+ rc = apei_read(buf_paddr, &g->error_status_address);
+ if (rc) {
+ *buf_paddr = 0;
+ pr_warn_ratelimited(FW_WARN GHES_PFX
+"Failed to read error status block address for hardware error source: %d.\n",
+ g->header.source_id);
+ return -EIO;
+ }
+ if (!*buf_paddr)
+ return -ENOENT;
+
+ ghes_copy_tofrom_phys(estatus, *buf_paddr, sizeof(*estatus), 1,
+ fixmap_idx);
+ if (!estatus->block_status) {
+ *buf_paddr = 0;
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int __ghes_read_estatus(struct acpi_hest_generic_status *estatus,
+ u64 buf_paddr, enum fixed_addresses fixmap_idx,
+ size_t buf_len)
+{
+ ghes_copy_tofrom_phys(estatus, buf_paddr, buf_len, 1, fixmap_idx);
+ if (cper_estatus_check(estatus)) {
+ pr_warn_ratelimited(FW_WARN GHES_PFX
+ "Failed to read error status block!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int ghes_read_estatus(struct ghes *ghes,
+ struct acpi_hest_generic_status *estatus,
+ u64 *buf_paddr, enum fixed_addresses fixmap_idx)
+{
+ int rc;
+
+ rc = __ghes_peek_estatus(ghes, estatus, buf_paddr, fixmap_idx);
+ if (rc)
+ return rc;
+
+ rc = __ghes_check_estatus(ghes, estatus);
+ if (rc)
+ return rc;
+
+ return __ghes_read_estatus(estatus, *buf_paddr, fixmap_idx,
+ cper_estatus_len(estatus));
+}
+
+static void ghes_clear_estatus(struct ghes *ghes,
+ struct acpi_hest_generic_status *estatus,
+ u64 buf_paddr, enum fixed_addresses fixmap_idx)
+{
+ estatus->block_status = 0;
+
+ if (!buf_paddr)
+ return;
+
+ ghes_copy_tofrom_phys(estatus, buf_paddr,
+ sizeof(estatus->block_status), 0,
+ fixmap_idx);
+
+ /*
+ * GHESv2 type HEST entries introduce support for error acknowledgment,
+ * so only acknowledge the error if this support is present.
+ */
+ if (is_hest_type_generic_v2(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.
+ */
+static void ghes_kick_task_work(struct callback_head *head)
+{
+ struct acpi_hest_generic_status *estatus;
+ struct ghes_estatus_node *estatus_node;
+ u32 node_len;
+
+ 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);
+
+ 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);
+}
+
+static bool ghes_do_memory_failure(u64 physical_addr, int flags)
+{
+ 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;
+ }
+
+ memory_failure_queue(pfn, flags);
+ return true;
+}
+
+static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
+ int sev)
+{
+ int flags = -1;
+ int sec_sev = ghes_severity(gdata->error_severity);
+ struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
+
+ if (!(mem_err->validation_bits & CPER_MEM_VALID_PA))
+ return false;
+
+ /* iff following two events can be handled properly by now */
+ if (sec_sev == GHES_SEV_CORRECTED &&
+ (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED))
+ flags = MF_SOFT_OFFLINE;
+ if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE)
+ flags = 0;
+
+ if (flags != -1)
+ return ghes_do_memory_failure(mem_err->physical_addr, flags);
+
+ return false;
+}
+
+static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, int sev)
+{
+ struct cper_sec_proc_arm *err = acpi_hest_get_payload(gdata);
+ bool queued = false;
+ int sec_sev, i;
+ char *p;
+
+ log_arm_hw_error(err);
+
+ sec_sev = ghes_severity(gdata->error_severity);
+ 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 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
+ * 1 in some old firmware of HiSilicon Kunpeng920. We assume that
+ * firmware won't mix corrected errors in an uncorrected section,
+ * and don't filter out 'corrected' error here.
+ */
+ if (is_cache && has_pa) {
+ queued = ghes_do_memory_failure(err_info->physical_fault_addr, 0);
+ 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];
+
+ pr_warn_ratelimited(FW_WARN GHES_PFX
+ "Unhandled processor error type: %s\n",
+ error_type);
+ p += err_info->length;
+ }
+
+ return queued;
+}
+
+/*
+ * PCIe AER errors need to be sent to the AER driver for reporting and
+ * recovery. The GHES severities map to the following AER severities and
+ * require the following handling:
+ *
+ * GHES_SEV_CORRECTABLE -> AER_CORRECTABLE
+ * These need to be reported by the AER driver but no recovery is
+ * necessary.
+ * GHES_SEV_RECOVERABLE -> AER_NONFATAL
+ * GHES_SEV_RECOVERABLE && CPER_SEC_RESET -> AER_FATAL
+ * These both need to be reported and recovered from by the AER driver.
+ * GHES_SEV_PANIC does not make it to this handling since the kernel must
+ * panic.
+ */
+static void ghes_handle_aer(struct acpi_hest_generic_data *gdata)
+{
+#ifdef CONFIG_ACPI_APEI_PCIEAER
+ struct cper_sec_pcie *pcie_err = acpi_hest_get_payload(gdata);
+
+ if (pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID &&
+ pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) {
+ unsigned int devfn;
+ int aer_severity;
+
+ devfn = PCI_DEVFN(pcie_err->device_id.device,
+ pcie_err->device_id.function);
+ aer_severity = cper_severity_to_aer(gdata->error_severity);
+
+ /*
+ * If firmware reset the component to contain
+ * the error, we must reinitialize it before
+ * use, so treat it as a fatal AER error.
+ */
+ if (gdata->flags & CPER_SEC_RESET)
+ aer_severity = AER_FATAL;
+
+ aer_recover_queue(pcie_err->device_id.segment,
+ pcie_err->device_id.bus,
+ devfn, aer_severity,
+ (struct aer_capability_regs *)
+ pcie_err->aer_info);
+ }
+#endif
+}
+
+static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list);
+
+int ghes_register_vendor_record_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&vendor_record_notify_list, nb);
+}
+EXPORT_SYMBOL_GPL(ghes_register_vendor_record_notifier);
+
+void ghes_unregister_vendor_record_notifier(struct notifier_block *nb)
+{
+ blocking_notifier_chain_unregister(&vendor_record_notify_list, nb);
+}
+EXPORT_SYMBOL_GPL(ghes_unregister_vendor_record_notifier);
+
+static void ghes_vendor_record_work_func(struct work_struct *work)
+{
+ struct ghes_vendor_record_entry *entry;
+ struct acpi_hest_generic_data *gdata;
+ u32 len;
+
+ entry = container_of(work, struct ghes_vendor_record_entry, work);
+ gdata = GHES_GDATA_FROM_VENDOR_ENTRY(entry);
+
+ blocking_notifier_call_chain(&vendor_record_notify_list,
+ entry->error_severity, gdata);
+
+ len = GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata));
+ gen_pool_free(ghes_estatus_pool, (unsigned long)entry, len);
+}
+
+static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata,
+ int sev)
+{
+ struct acpi_hest_generic_data *copied_gdata;
+ struct ghes_vendor_record_entry *entry;
+ u32 len;
+
+ len = GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata));
+ entry = (void *)gen_pool_alloc(ghes_estatus_pool, len);
+ if (!entry)
+ return;
+
+ copied_gdata = GHES_GDATA_FROM_VENDOR_ENTRY(entry);
+ memcpy(copied_gdata, gdata, acpi_hest_get_record_size(gdata));
+ entry->error_severity = sev;
+
+ INIT_WORK(&entry->work, ghes_vendor_record_work_func);
+ schedule_work(&entry->work);
+}
+
+static bool ghes_do_proc(struct ghes *ghes,
+ const struct acpi_hest_generic_status *estatus)
+{
+ int sev, sec_sev;
+ struct acpi_hest_generic_data *gdata;
+ guid_t *sec_type;
+ const guid_t *fru_id = &guid_null;
+ char *fru_text = "";
+ bool queued = false;
+
+ sev = ghes_severity(estatus->error_severity);
+ apei_estatus_for_each_section(estatus, gdata) {
+ sec_type = (guid_t *)gdata->section_type;
+ sec_sev = ghes_severity(gdata->error_severity);
+ 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;
+
+ 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);
+
+ arch_apei_report_mem_error(sev, mem_err);
+ queued = ghes_handle_memory_failure(gdata, sev);
+ }
+ 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 {
+ void *err = acpi_hest_get_payload(gdata);
+
+ ghes_defer_non_standard_event(gdata, sev);
+ log_non_standard_event(sec_type, fru_id, fru_text,
+ sec_sev, err,
+ gdata->error_data_length);
+ }
+ }
+
+ return queued;
+}
+
+static void __ghes_print_estatus(const char *pfx,
+ const struct acpi_hest_generic *generic,
+ const struct acpi_hest_generic_status *estatus)
+{
+ static atomic_t seqno;
+ unsigned int curr_seqno;
+ char pfx_seq[64];
+
+ if (pfx == NULL) {
+ if (ghes_severity(estatus->error_severity) <=
+ GHES_SEV_CORRECTED)
+ pfx = KERN_WARNING;
+ else
+ pfx = KERN_ERR;
+ }
+ curr_seqno = atomic_inc_return(&seqno);
+ snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno);
+ printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
+ pfx_seq, generic->header.source_id);
+ cper_estatus_print(pfx_seq, estatus);
+}
+
+static int ghes_print_estatus(const char *pfx,
+ const struct acpi_hest_generic *generic,
+ const struct acpi_hest_generic_status *estatus)
+{
+ /* Not more than 2 messages every 5 seconds */
+ static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2);
+ static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2);
+ struct ratelimit_state *ratelimit;
+
+ if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED)
+ ratelimit = &ratelimit_corrected;
+ else
+ ratelimit = &ratelimit_uncorrected;
+ if (__ratelimit(ratelimit)) {
+ __ghes_print_estatus(pfx, generic, estatus);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * GHES error status reporting throttle, to report more kinds of
+ * errors, instead of just most frequently occurred errors.
+ */
+static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus)
+{
+ u32 len;
+ int i, cached = 0;
+ unsigned long long now;
+ struct ghes_estatus_cache *cache;
+ struct acpi_hest_generic_status *cache_estatus;
+
+ len = cper_estatus_len(estatus);
+ rcu_read_lock();
+ for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
+ cache = rcu_dereference(ghes_estatus_caches[i]);
+ if (cache == NULL)
+ continue;
+ if (len != cache->estatus_len)
+ continue;
+ cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
+ if (memcmp(estatus, cache_estatus, len))
+ continue;
+ atomic_inc(&cache->count);
+ now = sched_clock();
+ if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC)
+ cached = 1;
+ break;
+ }
+ rcu_read_unlock();
+ return cached;
+}
+
+static struct ghes_estatus_cache *ghes_estatus_cache_alloc(
+ struct acpi_hest_generic *generic,
+ struct acpi_hest_generic_status *estatus)
+{
+ int alloced;
+ u32 len, cache_len;
+ struct ghes_estatus_cache *cache;
+ struct acpi_hest_generic_status *cache_estatus;
+
+ alloced = atomic_add_return(1, &ghes_estatus_cache_alloced);
+ if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) {
+ atomic_dec(&ghes_estatus_cache_alloced);
+ return NULL;
+ }
+ len = cper_estatus_len(estatus);
+ cache_len = GHES_ESTATUS_CACHE_LEN(len);
+ cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len);
+ if (!cache) {
+ atomic_dec(&ghes_estatus_cache_alloced);
+ return NULL;
+ }
+ cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
+ memcpy(cache_estatus, estatus, len);
+ cache->estatus_len = len;
+ atomic_set(&cache->count, 0);
+ cache->generic = generic;
+ cache->time_in = sched_clock();
+ return cache;
+}
+
+static void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
+{
+ u32 len;
+
+ 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)
+{
+ int i, slot = -1, count;
+ unsigned long long now, duration, period, max_period = 0;
+ struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
+
+ new_cache = ghes_estatus_cache_alloc(generic, estatus);
+ if (new_cache == NULL)
+ 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);
+ period = duration;
+ do_div(period, (count + 1));
+ 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();
+}
+
+static void __ghes_panic(struct ghes *ghes,
+ struct acpi_hest_generic_status *estatus,
+ u64 buf_paddr, enum fixed_addresses fixmap_idx)
+{
+ __ghes_print_estatus(KERN_EMERG, ghes->generic, estatus);
+
+ 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!");
+}
+
+static int ghes_proc(struct ghes *ghes)
+{
+ struct acpi_hest_generic_status *estatus = ghes->estatus;
+ u64 buf_paddr;
+ int rc;
+
+ rc = ghes_read_estatus(ghes, estatus, &buf_paddr, FIX_APEI_GHES_IRQ);
+ if (rc)
+ goto out;
+
+ if (ghes_severity(estatus->error_severity) >= GHES_SEV_PANIC)
+ __ghes_panic(ghes, estatus, buf_paddr, FIX_APEI_GHES_IRQ);
+
+ if (!ghes_estatus_cached(estatus)) {
+ if (ghes_print_estatus(NULL, ghes->generic, estatus))
+ ghes_estatus_cache_add(ghes->generic, estatus);
+ }
+ ghes_do_proc(ghes, estatus);
+
+out:
+ ghes_clear_estatus(ghes, estatus, buf_paddr, FIX_APEI_GHES_IRQ);
+
+ return rc;
+}
+
+static void ghes_add_timer(struct ghes *ghes)
+{
+ struct acpi_hest_generic *g = ghes->generic;
+ unsigned long expire;
+
+ if (!g->notify.poll_interval) {
+ pr_warn(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n",
+ g->header.source_id);
+ return;
+ }
+ expire = jiffies + msecs_to_jiffies(g->notify.poll_interval);
+ ghes->timer.expires = round_jiffies_relative(expire);
+ add_timer(&ghes->timer);
+}
+
+static void ghes_poll_func(struct timer_list *t)
+{
+ struct ghes *ghes = from_timer(ghes, t, timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ghes_notify_lock_irq, flags);
+ ghes_proc(ghes);
+ spin_unlock_irqrestore(&ghes_notify_lock_irq, flags);
+ if (!(ghes->flags & GHES_EXITING))
+ ghes_add_timer(ghes);
+}
+
+static irqreturn_t ghes_irq_func(int irq, void *data)
+{
+ struct ghes *ghes = data;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&ghes_notify_lock_irq, flags);
+ rc = ghes_proc(ghes);
+ spin_unlock_irqrestore(&ghes_notify_lock_irq, flags);
+ if (rc)
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
+static int ghes_notify_hed(struct notifier_block *this, unsigned long event,
+ void *data)
+{
+ struct ghes *ghes;
+ unsigned long flags;
+ 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;
+}
+
+static struct notifier_block ghes_notifier_hed = {
+ .notifier_call = ghes_notify_hed,
+};
+
+/*
+ * Handlers for CPER records may not be NMI safe. For example,
+ * memory_failure_queue() takes spinlocks and calls schedule_work_on().
+ * In any NMI-like handler, memory from ghes_estatus_pool is used to save
+ * estatus, and added to the ghes_estatus_llist. irq_work_queue() causes
+ * ghes_proc_in_irq() to run in IRQ context where each estatus in
+ * ghes_estatus_llist is processed.
+ *
+ * Memory from the ghes_estatus_pool is also used with the ghes_estatus_cache
+ * to suppress frequent messages.
+ */
+static struct llist_head ghes_estatus_llist;
+static struct irq_work ghes_proc_irq_work;
+
+static void ghes_proc_in_irq(struct irq_work *irq_work)
+{
+ struct llist_node *llnode, *next;
+ 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);
+ /*
+ * Because the time order of estatus in list is reversed,
+ * revert it back to proper order.
+ */
+ llnode = llist_reverse_order(llnode);
+ while (llnode) {
+ next = llnode->next;
+ estatus_node = llist_entry(llnode, struct ghes_estatus_node,
+ llnode);
+ 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);
+ 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) {
+ 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);
+
+ llnode = next;
+ }
+}
+
+static void ghes_print_queued_estatus(void)
+{
+ struct llist_node *llnode;
+ struct ghes_estatus_node *estatus_node;
+ struct acpi_hest_generic *generic;
+ struct acpi_hest_generic_status *estatus;
+
+ llnode = llist_del_all(&ghes_estatus_llist);
+ /*
+ * Because the time order of estatus in list is reversed,
+ * revert it back to proper order.
+ */
+ llnode = llist_reverse_order(llnode);
+ while (llnode) {
+ estatus_node = llist_entry(llnode, struct ghes_estatus_node,
+ llnode);
+ estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
+ generic = estatus_node->generic;
+ ghes_print_estatus(NULL, generic, estatus);
+ llnode = llnode->next;
+ }
+}
+
+static int ghes_in_nmi_queue_one_entry(struct ghes *ghes,
+ enum fixed_addresses fixmap_idx)
+{
+ struct acpi_hest_generic_status *estatus, tmp_header;
+ struct ghes_estatus_node *estatus_node;
+ u32 len, node_len;
+ u64 buf_paddr;
+ int sev, rc;
+
+ if (!IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG))
+ return -EOPNOTSUPP;
+
+ rc = __ghes_peek_estatus(ghes, &tmp_header, &buf_paddr, fixmap_idx);
+ if (rc) {
+ ghes_clear_estatus(ghes, &tmp_header, buf_paddr, fixmap_idx);
+ return rc;
+ }
+
+ rc = __ghes_check_estatus(ghes, &tmp_header);
+ if (rc) {
+ ghes_clear_estatus(ghes, &tmp_header, buf_paddr, fixmap_idx);
+ return rc;
+ }
+
+ len = cper_estatus_len(&tmp_header);
+ node_len = GHES_ESTATUS_NODE_LEN(len);
+ estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, node_len);
+ if (!estatus_node)
+ return -ENOMEM;
+
+ 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)) {
+ ghes_clear_estatus(ghes, estatus, buf_paddr, fixmap_idx);
+ rc = -ENOENT;
+ goto no_work;
+ }
+
+ sev = ghes_severity(estatus->error_severity);
+ if (sev >= GHES_SEV_PANIC) {
+ ghes_print_queued_estatus();
+ __ghes_panic(ghes, estatus, buf_paddr, fixmap_idx);
+ }
+
+ ghes_clear_estatus(ghes, &tmp_header, buf_paddr, fixmap_idx);
+
+ /* This error has been reported before, don't process it again. */
+ if (ghes_estatus_cached(estatus))
+ goto no_work;
+
+ llist_add(&estatus_node->llnode, &ghes_estatus_llist);
+
+ return rc;
+
+no_work:
+ gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
+ node_len);
+
+ return rc;
+}
+
+static int ghes_in_nmi_spool_from_list(struct list_head *rcu_list,
+ enum fixed_addresses fixmap_idx)
+{
+ int ret = -ENOENT;
+ struct ghes *ghes;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ghes, rcu_list, list) {
+ if (!ghes_in_nmi_queue_one_entry(ghes, fixmap_idx))
+ ret = 0;
+ }
+ rcu_read_unlock();
+
+ if (IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) && !ret)
+ irq_work_queue(&ghes_proc_irq_work);
+
+ return ret;
+}
+
+#ifdef CONFIG_ACPI_APEI_SEA
+static LIST_HEAD(ghes_sea);
+
+/*
+ * Return 0 only if one of the SEA error sources successfully reported an error
+ * record sent from the firmware.
+ */
+int ghes_notify_sea(void)
+{
+ static DEFINE_RAW_SPINLOCK(ghes_notify_lock_sea);
+ int rv;
+
+ raw_spin_lock(&ghes_notify_lock_sea);
+ rv = ghes_in_nmi_spool_from_list(&ghes_sea, FIX_APEI_GHES_SEA);
+ raw_spin_unlock(&ghes_notify_lock_sea);
+
+ return rv;
+}
+
+static void ghes_sea_add(struct ghes *ghes)
+{
+ mutex_lock(&ghes_list_mutex);
+ list_add_rcu(&ghes->list, &ghes_sea);
+ mutex_unlock(&ghes_list_mutex);
+}
+
+static void ghes_sea_remove(struct ghes *ghes)
+{
+ mutex_lock(&ghes_list_mutex);
+ list_del_rcu(&ghes->list);
+ mutex_unlock(&ghes_list_mutex);
+ synchronize_rcu();
+}
+#else /* CONFIG_ACPI_APEI_SEA */
+static inline void ghes_sea_add(struct ghes *ghes) { }
+static inline void ghes_sea_remove(struct ghes *ghes) { }
+#endif /* CONFIG_ACPI_APEI_SEA */
+
+#ifdef CONFIG_HAVE_ACPI_APEI_NMI
+/*
+ * NMI may be triggered on any CPU, so ghes_in_nmi is used for
+ * having only one concurrent reader.
+ */
+static atomic_t ghes_in_nmi = ATOMIC_INIT(0);
+
+static LIST_HEAD(ghes_nmi);
+
+static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
+{
+ static DEFINE_RAW_SPINLOCK(ghes_notify_lock_nmi);
+ int ret = NMI_DONE;
+
+ if (!atomic_add_unless(&ghes_in_nmi, 1, 1))
+ return ret;
+
+ raw_spin_lock(&ghes_notify_lock_nmi);
+ if (!ghes_in_nmi_spool_from_list(&ghes_nmi, FIX_APEI_GHES_NMI))
+ ret = NMI_HANDLED;
+ raw_spin_unlock(&ghes_notify_lock_nmi);
+
+ atomic_dec(&ghes_in_nmi);
+ return ret;
+}
+
+static void ghes_nmi_add(struct ghes *ghes)
+{
+ mutex_lock(&ghes_list_mutex);
+ if (list_empty(&ghes_nmi))
+ register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes");
+ list_add_rcu(&ghes->list, &ghes_nmi);
+ mutex_unlock(&ghes_list_mutex);
+}
+
+static void ghes_nmi_remove(struct ghes *ghes)
+{
+ mutex_lock(&ghes_list_mutex);
+ list_del_rcu(&ghes->list);
+ if (list_empty(&ghes_nmi))
+ unregister_nmi_handler(NMI_LOCAL, "ghes");
+ mutex_unlock(&ghes_list_mutex);
+ /*
+ * To synchronize with NMI handler, ghes can only be
+ * freed after NMI handler finishes.
+ */
+ synchronize_rcu();
+}
+#else /* CONFIG_HAVE_ACPI_APEI_NMI */
+static inline void ghes_nmi_add(struct ghes *ghes) { }
+static inline void ghes_nmi_remove(struct ghes *ghes) { }
+#endif /* CONFIG_HAVE_ACPI_APEI_NMI */
+
+static void ghes_nmi_init_cxt(void)
+{
+ init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
+}
+
+static int __ghes_sdei_callback(struct ghes *ghes,
+ enum fixed_addresses fixmap_idx)
+{
+ if (!ghes_in_nmi_queue_one_entry(ghes, fixmap_idx)) {
+ irq_work_queue(&ghes_proc_irq_work);
+
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static int ghes_sdei_normal_callback(u32 event_num, struct pt_regs *regs,
+ void *arg)
+{
+ static DEFINE_RAW_SPINLOCK(ghes_notify_lock_sdei_normal);
+ struct ghes *ghes = arg;
+ int err;
+
+ raw_spin_lock(&ghes_notify_lock_sdei_normal);
+ err = __ghes_sdei_callback(ghes, FIX_APEI_GHES_SDEI_NORMAL);
+ raw_spin_unlock(&ghes_notify_lock_sdei_normal);
+
+ return err;
+}
+
+static int ghes_sdei_critical_callback(u32 event_num, struct pt_regs *regs,
+ void *arg)
+{
+ static DEFINE_RAW_SPINLOCK(ghes_notify_lock_sdei_critical);
+ struct ghes *ghes = arg;
+ int err;
+
+ raw_spin_lock(&ghes_notify_lock_sdei_critical);
+ err = __ghes_sdei_callback(ghes, FIX_APEI_GHES_SDEI_CRITICAL);
+ raw_spin_unlock(&ghes_notify_lock_sdei_critical);
+
+ return err;
+}
+
+static int apei_sdei_register_ghes(struct ghes *ghes)
+{
+ if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
+ return -EOPNOTSUPP;
+
+ return sdei_register_ghes(ghes, ghes_sdei_normal_callback,
+ ghes_sdei_critical_callback);
+}
+
+static int apei_sdei_unregister_ghes(struct ghes *ghes)
+{
+ if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
+ return -EOPNOTSUPP;
+
+ return sdei_unregister_ghes(ghes);
+}
+
+static int ghes_probe(struct platform_device *ghes_dev)
+{
+ struct acpi_hest_generic *generic;
+ struct ghes *ghes = NULL;
+ unsigned long flags;
+
+ int rc = -EINVAL;
+
+ generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data;
+ if (!generic->enabled)
+ return -ENODEV;
+
+ switch (generic->notify.type) {
+ case ACPI_HEST_NOTIFY_POLLED:
+ case ACPI_HEST_NOTIFY_EXTERNAL:
+ case ACPI_HEST_NOTIFY_SCI:
+ case ACPI_HEST_NOTIFY_GSIV:
+ case ACPI_HEST_NOTIFY_GPIO:
+ break;
+
+ case ACPI_HEST_NOTIFY_SEA:
+ if (!IS_ENABLED(CONFIG_ACPI_APEI_SEA)) {
+ pr_warn(GHES_PFX "Generic hardware error source: %d notified via SEA is not supported\n",
+ generic->header.source_id);
+ rc = -ENOTSUPP;
+ goto err;
+ }
+ break;
+ case ACPI_HEST_NOTIFY_NMI:
+ if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) {
+ pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n",
+ generic->header.source_id);
+ goto err;
+ }
+ break;
+ case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED:
+ if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE)) {
+ pr_warn(GHES_PFX "Generic hardware error source: %d notified via SDE Interface is not supported!\n",
+ generic->header.source_id);
+ goto err;
+ }
+ break;
+ case ACPI_HEST_NOTIFY_LOCAL:
+ pr_warn(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n",
+ generic->header.source_id);
+ goto err;
+ default:
+ pr_warn(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n",
+ generic->notify.type, generic->header.source_id);
+ goto err;
+ }
+
+ rc = -EIO;
+ if (generic->error_block_length <
+ sizeof(struct acpi_hest_generic_status)) {
+ pr_warn(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n",
+ generic->error_block_length, generic->header.source_id);
+ goto err;
+ }
+ ghes = ghes_new(generic);
+ if (IS_ERR(ghes)) {
+ rc = PTR_ERR(ghes);
+ ghes = NULL;
+ goto err;
+ }
+
+ switch (generic->notify.type) {
+ case ACPI_HEST_NOTIFY_POLLED:
+ timer_setup(&ghes->timer, ghes_poll_func, 0);
+ ghes_add_timer(ghes);
+ break;
+ case ACPI_HEST_NOTIFY_EXTERNAL:
+ /* External interrupt vector is GSI */
+ rc = acpi_gsi_to_irq(generic->notify.vector, &ghes->irq);
+ if (rc) {
+ pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n",
+ generic->header.source_id);
+ goto err;
+ }
+ rc = request_irq(ghes->irq, ghes_irq_func, IRQF_SHARED,
+ "GHES IRQ", ghes);
+ if (rc) {
+ pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n",
+ generic->header.source_id);
+ goto err;
+ }
+ break;
+
+ case ACPI_HEST_NOTIFY_SCI:
+ case ACPI_HEST_NOTIFY_GSIV:
+ case ACPI_HEST_NOTIFY_GPIO:
+ mutex_lock(&ghes_list_mutex);
+ if (list_empty(&ghes_hed))
+ register_acpi_hed_notifier(&ghes_notifier_hed);
+ list_add_rcu(&ghes->list, &ghes_hed);
+ mutex_unlock(&ghes_list_mutex);
+ break;
+
+ case ACPI_HEST_NOTIFY_SEA:
+ ghes_sea_add(ghes);
+ break;
+ case ACPI_HEST_NOTIFY_NMI:
+ ghes_nmi_add(ghes);
+ break;
+ case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED:
+ rc = apei_sdei_register_ghes(ghes);
+ if (rc)
+ goto err;
+ break;
+ default:
+ BUG();
+ }
+
+ platform_set_drvdata(ghes_dev, ghes);
+
+ ghes_edac_register(ghes, &ghes_dev->dev);
+
+ /* Handle any pending errors right away */
+ spin_lock_irqsave(&ghes_notify_lock_irq, flags);
+ ghes_proc(ghes);
+ spin_unlock_irqrestore(&ghes_notify_lock_irq, flags);
+
+ return 0;
+
+err:
+ if (ghes) {
+ ghes_fini(ghes);
+ kfree(ghes);
+ }
+ return rc;
+}
+
+static int ghes_remove(struct platform_device *ghes_dev)
+{
+ int rc;
+ struct ghes *ghes;
+ struct acpi_hest_generic *generic;
+
+ ghes = platform_get_drvdata(ghes_dev);
+ generic = ghes->generic;
+
+ ghes->flags |= GHES_EXITING;
+ switch (generic->notify.type) {
+ case ACPI_HEST_NOTIFY_POLLED:
+ del_timer_sync(&ghes->timer);
+ break;
+ case ACPI_HEST_NOTIFY_EXTERNAL:
+ free_irq(ghes->irq, ghes);
+ break;
+
+ case ACPI_HEST_NOTIFY_SCI:
+ case ACPI_HEST_NOTIFY_GSIV:
+ case ACPI_HEST_NOTIFY_GPIO:
+ mutex_lock(&ghes_list_mutex);
+ list_del_rcu(&ghes->list);
+ if (list_empty(&ghes_hed))
+ unregister_acpi_hed_notifier(&ghes_notifier_hed);
+ mutex_unlock(&ghes_list_mutex);
+ synchronize_rcu();
+ break;
+
+ case ACPI_HEST_NOTIFY_SEA:
+ ghes_sea_remove(ghes);
+ break;
+ case ACPI_HEST_NOTIFY_NMI:
+ ghes_nmi_remove(ghes);
+ break;
+ case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED:
+ rc = apei_sdei_unregister_ghes(ghes);
+ if (rc)
+ return rc;
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ ghes_fini(ghes);
+
+ ghes_edac_unregister(ghes);
+
+ kfree(ghes);
+
+ platform_set_drvdata(ghes_dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ghes_platform_driver = {
+ .driver = {
+ .name = "GHES",
+ },
+ .probe = ghes_probe,
+ .remove = ghes_remove,
+};
+
+void __init acpi_ghes_init(void)
+{
+ int rc;
+
+ sdei_init();
+
+ if (acpi_disabled)
+ return;
+
+ switch (hest_disable) {
+ case HEST_NOT_FOUND:
+ return;
+ case HEST_DISABLED:
+ pr_info(GHES_PFX "HEST is not enabled!\n");
+ return;
+ default:
+ break;
+ }
+
+ if (ghes_disable) {
+ pr_info(GHES_PFX "GHES is not enabled!\n");
+ return;
+ }
+
+ ghes_nmi_init_cxt();
+
+ rc = platform_driver_register(&ghes_platform_driver);
+ if (rc)
+ return;
+
+ rc = apei_osc_setup();
+ if (rc == 0 && osc_sb_apei_support_acked)
+ pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n");
+ else if (rc == 0 && !osc_sb_apei_support_acked)
+ pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n");
+ else if (rc && osc_sb_apei_support_acked)
+ pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n");
+ else
+ pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n");
+}
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
new file mode 100644
index 000000000..6aef1ee5e
--- /dev/null
+++ b/drivers/acpi/apei/hest.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * APEI Hardware Error Source Table support
+ *
+ * HEST describes error sources in detail; communicates operational
+ * parameters (i.e. severity levels, masking bits, and threshold
+ * values) to Linux as necessary. It also allows the BIOS to report
+ * non-standard error sources to Linux (for example, chipset-specific
+ * error registers).
+ *
+ * For more information about HEST, please refer to ACPI Specification
+ * version 4.0, section 17.3.2.
+ *
+ * Copyright 2009 Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/kdebug.h>
+#include <linux/highmem.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <acpi/apei.h>
+#include <acpi/ghes.h>
+
+#include "apei-internal.h"
+
+#define HEST_PFX "HEST: "
+
+int hest_disable;
+EXPORT_SYMBOL_GPL(hest_disable);
+
+/* HEST table parsing */
+
+static struct acpi_table_hest *__read_mostly hest_tab;
+
+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,
+ [ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi),
+ [ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root),
+ [ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer),
+ [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge),
+ [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic),
+ [ACPI_HEST_TYPE_GENERIC_ERROR_V2] = sizeof(struct acpi_hest_generic_v2),
+ [ACPI_HEST_TYPE_IA32_DEFERRED_CHECK] = -1,
+};
+
+static inline bool is_generic_error(struct acpi_hest_header *hest_hdr)
+{
+ return hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR ||
+ hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR_V2;
+}
+
+static int hest_esrc_len(struct acpi_hest_header *hest_hdr)
+{
+ u16 hest_type = hest_hdr->type;
+ int len;
+
+ if (hest_type >= ACPI_HEST_TYPE_RESERVED)
+ return 0;
+
+ len = hest_esrc_len_tab[hest_type];
+
+ if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) {
+ struct acpi_hest_ia_corrected *cmc;
+ cmc = (struct acpi_hest_ia_corrected *)hest_hdr;
+ len = sizeof(*cmc) + cmc->num_hardware_banks *
+ sizeof(struct acpi_hest_ia_error_bank);
+ } 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);
+ } 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);
+ }
+ BUG_ON(len == -1);
+
+ return len;
+};
+
+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)
+{
+ struct acpi_hest_header *hest_hdr;
+ int i, rc, len;
+
+ if (hest_disable || !hest_tab)
+ return -EINVAL;
+
+ hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
+ for (i = 0; i < hest_tab->error_source_count; i++) {
+ len = hest_esrc_len(hest_hdr);
+ if (!len) {
+ pr_warn(FW_WARN HEST_PFX
+ "Unknown or unused hardware error source "
+ "type: %d for hardware error source: %d.\n",
+ hest_hdr->type, hest_hdr->source_id);
+ return -EINVAL;
+ }
+ if ((void *)hest_hdr + len >
+ (void *)hest_tab + hest_tab->header.length) {
+ pr_warn(FW_BUG HEST_PFX
+ "Table contents overflow for hardware error source: %d.\n",
+ hest_hdr->source_id);
+ return -EINVAL;
+ }
+
+ rc = func(hest_hdr, data);
+ if (rc)
+ return rc;
+
+ hest_hdr = (void *)hest_hdr + len;
+ }
+
+ return 0;
+}
+
+/*
+ * Check if firmware advertises firmware first mode. We need FF bit to be set
+ * along with a set of MC banks which work in FF mode.
+ */
+static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data)
+{
+ if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK)
+ return 0;
+
+ if (!acpi_disable_cmcff)
+ return !arch_apei_enable_cmcff(hest_hdr, data);
+
+ return 0;
+}
+
+struct ghes_arr {
+ struct platform_device **ghes_devs;
+ unsigned int count;
+};
+
+static int __init hest_parse_ghes_count(struct acpi_hest_header *hest_hdr, void *data)
+{
+ int *count = data;
+
+ if (is_generic_error(hest_hdr))
+ (*count)++;
+ return 0;
+}
+
+static int __init hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data)
+{
+ struct platform_device *ghes_dev;
+ struct ghes_arr *ghes_arr = data;
+ int rc, i;
+
+ if (!is_generic_error(hest_hdr))
+ return 0;
+
+ if (!((struct acpi_hest_generic *)hest_hdr)->enabled)
+ return 0;
+ for (i = 0; i < ghes_arr->count; i++) {
+ struct acpi_hest_header *hdr;
+ ghes_dev = ghes_arr->ghes_devs[i];
+ hdr = *(struct acpi_hest_header **)ghes_dev->dev.platform_data;
+ if (hdr->source_id == hest_hdr->source_id) {
+ pr_warn(FW_WARN HEST_PFX "Duplicated hardware error source ID: %d.\n",
+ hdr->source_id);
+ return -EIO;
+ }
+ }
+ ghes_dev = platform_device_alloc("GHES", hest_hdr->source_id);
+ if (!ghes_dev)
+ return -ENOMEM;
+
+ rc = platform_device_add_data(ghes_dev, &hest_hdr, sizeof(void *));
+ if (rc)
+ goto err;
+
+ rc = platform_device_add(ghes_dev);
+ if (rc)
+ goto err;
+ ghes_arr->ghes_devs[ghes_arr->count++] = ghes_dev;
+
+ return 0;
+err:
+ platform_device_put(ghes_dev);
+ return rc;
+}
+
+static int __init hest_ghes_dev_register(unsigned int ghes_count)
+{
+ int rc, i;
+ struct ghes_arr ghes_arr;
+
+ ghes_arr.count = 0;
+ ghes_arr.ghes_devs = kmalloc_array(ghes_count, sizeof(void *),
+ GFP_KERNEL);
+ if (!ghes_arr.ghes_devs)
+ return -ENOMEM;
+
+ rc = apei_hest_parse(hest_parse_ghes, &ghes_arr);
+ if (rc)
+ goto err;
+
+ rc = ghes_estatus_pool_init(ghes_count);
+ if (rc)
+ goto err;
+
+out:
+ kfree(ghes_arr.ghes_devs);
+ return rc;
+err:
+ for (i = 0; i < ghes_arr.count; i++)
+ platform_device_unregister(ghes_arr.ghes_devs[i]);
+ goto out;
+}
+
+static int __init setup_hest_disable(char *str)
+{
+ hest_disable = HEST_DISABLED;
+ return 1;
+}
+
+__setup("hest_disable", setup_hest_disable);
+
+void __init acpi_hest_init(void)
+{
+ acpi_status status;
+ int rc;
+ unsigned int ghes_count = 0;
+
+ if (hest_disable) {
+ pr_info(HEST_PFX "Table parsing disabled.\n");
+ return;
+ }
+
+ status = acpi_get_table(ACPI_SIG_HEST, 0,
+ (struct acpi_table_header **)&hest_tab);
+ if (status == AE_NOT_FOUND) {
+ hest_disable = HEST_NOT_FOUND;
+ return;
+ } else if (ACPI_FAILURE(status)) {
+ const char *msg = acpi_format_exception(status);
+ pr_err(HEST_PFX "Failed to get table, %s\n", msg);
+ hest_disable = HEST_DISABLED;
+ return;
+ }
+
+ rc = apei_hest_parse(hest_parse_cmc, NULL);
+ if (rc)
+ goto err;
+
+ if (!ghes_disable) {
+ rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
+ if (rc)
+ goto err;
+
+ if (ghes_count)
+ rc = hest_ghes_dev_register(ghes_count);
+ if (rc)
+ goto err;
+ }
+
+ pr_info(HEST_PFX "Table parsing has been initialized.\n");
+ return;
+err:
+ hest_disable = HEST_DISABLED;
+ acpi_put_table((struct acpi_table_header *)hest_tab);
+}
diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig
new file mode 100644
index 000000000..d4a72835f
--- /dev/null
+++ b/drivers/acpi/arm64/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# ACPI Configuration for ARM64
+#
+
+config ACPI_IORT
+ bool
+
+config ACPI_GTDT
+ bool
+
+config ACPI_AGDI
+ bool "Arm Generic Diagnostic Dump and Reset Device Interface"
+ depends on ARM_SDE_INTERFACE
+ help
+ Arm Generic Diagnostic Dump and Reset Device Interface (AGDI) is
+ a standard that enables issuing a non-maskable diagnostic dump and
+ reset command.
+
+ If set, the kernel parses AGDI table and listens for the command.
diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
new file mode 100644
index 000000000..7b9e40456
--- /dev/null
+++ b/drivers/acpi/arm64/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ACPI_AGDI) += agdi.o
+obj-$(CONFIG_ACPI_IORT) += iort.o
+obj-$(CONFIG_ACPI_GTDT) += gtdt.o
+obj-y += dma.o
diff --git a/drivers/acpi/arm64/agdi.c b/drivers/acpi/arm64/agdi.c
new file mode 100644
index 000000000..cf31abd0e
--- /dev/null
+++ b/drivers/acpi/arm64/agdi.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file implements handling of
+ * Arm Generic Diagnostic Dump and Reset Interface table (AGDI)
+ *
+ * Copyright (c) 2022, Ampere Computing LLC
+ */
+
+#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>
+
+struct agdi_data {
+ int sdei_event;
+};
+
+static int agdi_sdei_handler(u32 sdei_event, struct pt_regs *regs, void *arg)
+{
+ nmi_panic(regs, "Arm Generic Diagnostic Dump and Reset SDEI event issued");
+ return 0;
+}
+
+static int agdi_sdei_probe(struct platform_device *pdev,
+ struct agdi_data *adata)
+{
+ int err;
+
+ err = sdei_event_register(adata->sdei_event, agdi_sdei_handler, pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register for SDEI event %d",
+ adata->sdei_event);
+ return err;
+ }
+
+ err = sdei_event_enable(adata->sdei_event);
+ if (err) {
+ sdei_event_unregister(adata->sdei_event);
+ dev_err(&pdev->dev, "Failed to enable event %d\n",
+ adata->sdei_event);
+ return err;
+ }
+
+ return 0;
+}
+
+static int agdi_probe(struct platform_device *pdev)
+{
+ struct agdi_data *adata = dev_get_platdata(&pdev->dev);
+
+ if (!adata)
+ return -EINVAL;
+
+ return agdi_sdei_probe(pdev, adata);
+}
+
+static int 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;
+
+ for (i = 0; i < 3; i++) {
+ err = sdei_event_unregister(adata->sdei_event);
+ if (err != -EINPROGRESS)
+ break;
+
+ schedule();
+ }
+
+ return err;
+}
+
+static struct platform_driver agdi_driver = {
+ .driver = {
+ .name = "agdi",
+ },
+ .probe = agdi_probe,
+ .remove = agdi_remove,
+};
+
+void __init acpi_agdi_init(void)
+{
+ struct acpi_table_agdi *agdi_table;
+ struct agdi_data pdata;
+ struct platform_device *pdev;
+ acpi_status status;
+
+ status = acpi_get_table(ACPI_SIG_AGDI, 0,
+ (struct acpi_table_header **) &agdi_table);
+ if (ACPI_FAILURE(status))
+ return;
+
+ if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) {
+ pr_warn("Interrupt signaling is not supported");
+ goto err_put_table;
+ }
+
+ pdata.sdei_event = agdi_table->sdei_event;
+
+ pdev = platform_device_register_data(NULL, "agdi", 0, &pdata, sizeof(pdata));
+ if (IS_ERR(pdev))
+ goto err_put_table;
+
+ if (platform_driver_register(&agdi_driver))
+ platform_device_unregister(pdev);
+
+err_put_table:
+ acpi_put_table((struct acpi_table_header *)agdi_table);
+}
diff --git a/drivers/acpi/arm64/dma.c b/drivers/acpi/arm64/dma.c
new file mode 100644
index 000000000..93d796531
--- /dev/null
+++ b/drivers/acpi/arm64/dma.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/acpi.h>
+#include <linux/acpi_iort.h>
+#include <linux/device.h>
+#include <linux/dma-direct.h>
+
+void acpi_arch_dma_setup(struct device *dev)
+{
+ int ret;
+ u64 end, mask;
+ u64 size = 0;
+ const struct bus_dma_region *map = NULL;
+
+ /*
+ * If @dev is expected to be DMA-capable then the bus code that created
+ * it should have initialised its dma_mask pointer by this point. For
+ * now, we'll continue the legacy behaviour of coercing it to the
+ * coherent mask if not, but we'll no longer do so quietly.
+ */
+ if (!dev->dma_mask) {
+ dev_warn(dev, "DMA mask not set\n");
+ dev->dma_mask = &dev->coherent_dma_mask;
+ }
+
+ if (dev->coherent_dma_mask)
+ size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1);
+ else
+ size = 1ULL << 32;
+
+ ret = acpi_dma_get_range(dev, &map);
+ if (!ret && map) {
+ const struct bus_dma_region *r = map;
+
+ for (end = 0; r->size; r++) {
+ if (r->dma_start + r->size - 1 > end)
+ end = r->dma_start + r->size - 1;
+ }
+
+ size = end + 1;
+ dev->dma_range_map = map;
+ }
+
+ if (ret == -ENODEV)
+ ret = iort_dma_get_ranges(dev, &size);
+ if (!ret) {
+ /*
+ * Limit coherent and dma mask based on size retrieved from
+ * firmware.
+ */
+ end = 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);
+ }
+}
diff --git a/drivers/acpi/arm64/gtdt.c b/drivers/acpi/arm64/gtdt.c
new file mode 100644
index 000000000..c0e77c1c8
--- /dev/null
+++ b/drivers/acpi/arm64/gtdt.c
@@ -0,0 +1,418 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ARM Specific GTDT table Support
+ *
+ * Copyright (C) 2016, Linaro Ltd.
+ * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
+ * Fu Wei <fu.wei@linaro.org>
+ * Hanjun Guo <hanjun.guo@linaro.org>
+ */
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <clocksource/arm_arch_timer.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) "ACPI GTDT: " fmt
+
+/**
+ * struct acpi_gtdt_descriptor - Store the key info of GTDT for all functions
+ * @gtdt: The pointer to the struct acpi_table_gtdt of GTDT table.
+ * @gtdt_end: The pointer to the end of GTDT table.
+ * @platform_timer: The pointer to the start of Platform Timer Structure
+ *
+ * The struct store the key info of GTDT table, it should be initialized by
+ * acpi_gtdt_init.
+ */
+struct acpi_gtdt_descriptor {
+ struct acpi_table_gtdt *gtdt;
+ void *gtdt_end;
+ void *platform_timer;
+};
+
+static struct acpi_gtdt_descriptor acpi_gtdt_desc __initdata;
+
+static inline __init void *next_platform_timer(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 NULL;
+}
+
+#define for_each_platform_timer(_g) \
+ for (_g = acpi_gtdt_desc.platform_timer; _g; \
+ _g = next_platform_timer(_g))
+
+static inline bool is_timer_block(void *platform_timer)
+{
+ struct acpi_gtdt_header *gh = platform_timer;
+
+ return gh->type == ACPI_GTDT_TYPE_TIMER_BLOCK;
+}
+
+static inline bool is_non_secure_watchdog(void *platform_timer)
+{
+ struct acpi_gtdt_header *gh = platform_timer;
+ struct acpi_gtdt_watchdog *wd = platform_timer;
+
+ if (gh->type != ACPI_GTDT_TYPE_WATCHDOG)
+ return false;
+
+ return !(wd->timer_flags & ACPI_GTDT_WATCHDOG_SECURE);
+}
+
+static int __init map_gt_gsi(u32 interrupt, u32 flags)
+{
+ int trigger, polarity;
+
+ trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
+ : ACPI_LEVEL_SENSITIVE;
+
+ polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
+ : ACPI_ACTIVE_HIGH;
+
+ return acpi_register_gsi(NULL, interrupt, trigger, polarity);
+}
+
+/**
+ * acpi_gtdt_map_ppi() - Map the PPIs of per-cpu arch_timer.
+ * @type: the type of PPI.
+ *
+ * Note: Secure state is not managed by the kernel on ARM64 systems.
+ * So we only handle the non-secure timer PPIs,
+ * ARCH_TIMER_PHYS_SECURE_PPI is treated as invalid type.
+ *
+ * Return: the mapped PPI value, 0 if error.
+ */
+int __init acpi_gtdt_map_ppi(int type)
+{
+ struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt;
+
+ switch (type) {
+ case ARCH_TIMER_PHYS_NONSECURE_PPI:
+ return map_gt_gsi(gtdt->non_secure_el1_interrupt,
+ gtdt->non_secure_el1_flags);
+ case ARCH_TIMER_VIRT_PPI:
+ return map_gt_gsi(gtdt->virtual_timer_interrupt,
+ gtdt->virtual_timer_flags);
+
+ case ARCH_TIMER_HYP_PPI:
+ return map_gt_gsi(gtdt->non_secure_el2_interrupt,
+ gtdt->non_secure_el2_flags);
+ default:
+ pr_err("Failed to map timer interrupt: invalid type.\n");
+ }
+
+ return 0;
+}
+
+/**
+ * acpi_gtdt_c3stop() - Got c3stop info from GTDT according to the type of PPI.
+ * @type: the type of PPI.
+ *
+ * Return: true if the timer HW state is lost when a CPU enters an idle state,
+ * false otherwise
+ */
+bool __init acpi_gtdt_c3stop(int type)
+{
+ struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt;
+
+ switch (type) {
+ case ARCH_TIMER_PHYS_NONSECURE_PPI:
+ return !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON);
+
+ case ARCH_TIMER_VIRT_PPI:
+ return !(gtdt->virtual_timer_flags & ACPI_GTDT_ALWAYS_ON);
+
+ case ARCH_TIMER_HYP_PPI:
+ return !(gtdt->non_secure_el2_flags & ACPI_GTDT_ALWAYS_ON);
+
+ default:
+ pr_err("Failed to get c3stop info: invalid type.\n");
+ }
+
+ return false;
+}
+
+/**
+ * acpi_gtdt_init() - Get the info of GTDT table to prepare for further init.
+ * @table: The pointer to GTDT table.
+ * @platform_timer_count: It points to a integer variable which is used
+ * for storing the number of platform timers.
+ * This pointer could be NULL, if the caller
+ * doesn't need this info.
+ *
+ * Return: 0 if success, -EINVAL if error.
+ */
+int __init acpi_gtdt_init(struct acpi_table_header *table,
+ int *platform_timer_count)
+{
+ void *platform_timer;
+ struct acpi_table_gtdt *gtdt;
+
+ gtdt = container_of(table, struct acpi_table_gtdt, header);
+ acpi_gtdt_desc.gtdt = gtdt;
+ acpi_gtdt_desc.gtdt_end = (void *)table + table->length;
+ acpi_gtdt_desc.platform_timer = NULL;
+ if (platform_timer_count)
+ *platform_timer_count = 0;
+
+ if (table->revision < 2) {
+ pr_warn("Revision:%d doesn't support Platform Timers.\n",
+ table->revision);
+ return 0;
+ }
+
+ if (!gtdt->platform_timer_count) {
+ pr_debug("No Platform Timer.\n");
+ 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 = platform_timer;
+ if (platform_timer_count)
+ *platform_timer_count = gtdt->platform_timer_count;
+
+ return 0;
+}
+
+static int __init gtdt_parse_timer_block(struct acpi_gtdt_timer_block *block,
+ struct arch_timer_mem *timer_mem)
+{
+ int i;
+ struct arch_timer_mem_frame *frame;
+ struct acpi_gtdt_timer_entry *gtdt_frame;
+
+ if (!block->timer_count) {
+ pr_err(FW_BUG "GT block present, but frame count is zero.\n");
+ return -ENODEV;
+ }
+
+ if (block->timer_count > ARCH_TIMER_MEM_MAX_FRAMES) {
+ pr_err(FW_BUG "GT block lists %d frames, ACPI spec only allows 8\n",
+ block->timer_count);
+ return -EINVAL;
+ }
+
+ timer_mem->cntctlbase = (phys_addr_t)block->block_address;
+ /*
+ * The CNTCTLBase frame is 4KB (register offsets 0x000 - 0xFFC).
+ * See ARM DDI 0487A.k_iss10775, page I1-5129, Table I1-3
+ * "CNTCTLBase memory map".
+ */
+ timer_mem->size = SZ_4K;
+
+ gtdt_frame = (void *)block + block->timer_offset;
+ if (gtdt_frame + block->timer_count != (void *)block + block->header.length)
+ return -EINVAL;
+
+ /*
+ * Get the GT timer Frame data for every GT Block Timer
+ */
+ for (i = 0; i < block->timer_count; i++, gtdt_frame++) {
+ if (gtdt_frame->common_flags & ACPI_GTDT_GT_IS_SECURE_TIMER)
+ continue;
+ if (gtdt_frame->frame_number >= ARCH_TIMER_MEM_MAX_FRAMES ||
+ !gtdt_frame->base_address || !gtdt_frame->timer_interrupt)
+ goto error;
+
+ frame = &timer_mem->frame[gtdt_frame->frame_number];
+
+ /* duplicate frame */
+ if (frame->valid)
+ goto error;
+
+ frame->phys_irq = map_gt_gsi(gtdt_frame->timer_interrupt,
+ gtdt_frame->timer_flags);
+ if (frame->phys_irq <= 0) {
+ pr_warn("failed to map physical timer irq in frame %d.\n",
+ gtdt_frame->frame_number);
+ goto error;
+ }
+
+ if (gtdt_frame->virtual_timer_interrupt) {
+ frame->virt_irq =
+ map_gt_gsi(gtdt_frame->virtual_timer_interrupt,
+ gtdt_frame->virtual_timer_flags);
+ if (frame->virt_irq <= 0) {
+ pr_warn("failed to map virtual timer irq in frame %d.\n",
+ gtdt_frame->frame_number);
+ goto error;
+ }
+ } else {
+ pr_debug("virtual timer in frame %d not implemented.\n",
+ gtdt_frame->frame_number);
+ }
+
+ frame->cntbase = gtdt_frame->base_address;
+ /*
+ * The CNTBaseN frame is 4KB (register offsets 0x000 - 0xFFC).
+ * See ARM DDI 0487A.k_iss10775, page I1-5130, Table I1-4
+ * "CNTBaseN memory map".
+ */
+ frame->size = SZ_4K;
+ frame->valid = true;
+ }
+
+ return 0;
+
+error:
+ do {
+ if (gtdt_frame->common_flags & ACPI_GTDT_GT_IS_SECURE_TIMER ||
+ gtdt_frame->frame_number >= ARCH_TIMER_MEM_MAX_FRAMES)
+ continue;
+
+ frame = &timer_mem->frame[gtdt_frame->frame_number];
+
+ if (frame->phys_irq > 0)
+ acpi_unregister_gsi(gtdt_frame->timer_interrupt);
+ frame->phys_irq = 0;
+
+ if (frame->virt_irq > 0)
+ acpi_unregister_gsi(gtdt_frame->virtual_timer_interrupt);
+ frame->virt_irq = 0;
+ } 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
+ */
+static int __init gtdt_import_sbsa_gwdt(struct acpi_gtdt_watchdog *wd,
+ int index)
+{
+ struct platform_device *pdev;
+ int irq;
+
+ /*
+ * According to SBSA specification the size of refresh and control
+ * frames of SBSA Generic Watchdog is SZ_4K(Offset 0x000 – 0xFFF).
+ */
+ struct resource res[] = {
+ DEFINE_RES_MEM(wd->control_frame_address, SZ_4K),
+ DEFINE_RES_MEM(wd->refresh_frame_address, SZ_4K),
+ {},
+ };
+ int nr_res = ARRAY_SIZE(res);
+
+ pr_debug("found a Watchdog (0x%llx/0x%llx gsi:%u flags:0x%x).\n",
+ wd->refresh_frame_address, wd->control_frame_address,
+ wd->timer_interrupt, wd->timer_flags);
+
+ if (!(wd->refresh_frame_address && wd->control_frame_address)) {
+ pr_err(FW_BUG "failed to get the Watchdog base address.\n");
+ return -EINVAL;
+ }
+
+ irq = map_gt_gsi(wd->timer_interrupt, wd->timer_flags);
+ res[2] = (struct resource)DEFINE_RES_IRQ(irq);
+ if (irq <= 0) {
+ pr_warn("failed to map the Watchdog interrupt.\n");
+ nr_res--;
+ }
+
+ /*
+ * Add a platform device named "sbsa-gwdt" to match the platform driver.
+ * "sbsa-gwdt": SBSA(Server Base System Architecture) Generic Watchdog
+ * The platform driver can get device info below by matching this name.
+ */
+ pdev = platform_device_register_simple("sbsa-gwdt", index, res, nr_res);
+ if (IS_ERR(pdev)) {
+ if (irq > 0)
+ acpi_unregister_gsi(wd->timer_interrupt);
+ return PTR_ERR(pdev);
+ }
+
+ return 0;
+}
+
+static int __init gtdt_sbsa_gwdt_init(void)
+{
+ void *platform_timer;
+ struct acpi_table_header *table;
+ int ret, timer_count, gwdt_count = 0;
+
+ if (acpi_disabled)
+ return 0;
+
+ if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_GTDT, 0, &table)))
+ return -EINVAL;
+
+ /*
+ * Note: Even though the global variable acpi_gtdt_desc has been
+ * initialized by acpi_gtdt_init() while initializing the arch timers,
+ * when we call this function to get SBSA watchdogs info from GTDT, the
+ * pointers stashed in it are stale (since they are early temporary
+ * mappings carried out before acpi_permanent_mmap is set) and we need
+ * to re-initialize them with permanent mapped pointer values to let the
+ * GTDT parsing possible.
+ */
+ ret = acpi_gtdt_init(table, &timer_count);
+ if (ret || !timer_count)
+ goto out_put_gtdt;
+
+ for_each_platform_timer(platform_timer) {
+ if (is_non_secure_watchdog(platform_timer)) {
+ ret = gtdt_import_sbsa_gwdt(platform_timer, gwdt_count);
+ if (ret)
+ break;
+ gwdt_count++;
+ }
+ }
+
+ if (gwdt_count)
+ pr_info("found %d SBSA generic Watchdog(s).\n", gwdt_count);
+
+out_put_gtdt:
+ acpi_put_table(table);
+ return ret;
+}
+
+device_initcall(gtdt_sbsa_gwdt_init);
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
new file mode 100644
index 000000000..3a6cf5675
--- /dev/null
+++ b/drivers/acpi/arm64/iort.c
@@ -0,0 +1,2023 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016, Semihalf
+ * Author: Tomasz Nowicki <tn@semihalf.com>
+ *
+ * This file implements early detection/parsing of I/O mapping
+ * reported to OS through firmware via I/O Remapping Table (IORT)
+ * IORT document number: ARM DEN 0049A
+ */
+
+#define pr_fmt(fmt) "ACPI: IORT: " fmt
+
+#include <linux/acpi_iort.h>
+#include <linux/bitfield.h>
+#include <linux/iommu.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-map-ops.h>
+
+#define IORT_TYPE_MASK(type) (1 << (type))
+#define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP)
+#define IORT_IOMMU_TYPE ((1 << ACPI_IORT_NODE_SMMU) | \
+ (1 << ACPI_IORT_NODE_SMMU_V3))
+
+struct iort_its_msi_chip {
+ struct list_head list;
+ struct fwnode_handle *fw_node;
+ phys_addr_t base_addr;
+ u32 translation_id;
+};
+
+struct iort_fwnode {
+ struct list_head list;
+ struct acpi_iort_node *iort_node;
+ struct fwnode_handle *fwnode;
+};
+static LIST_HEAD(iort_fwnode_list);
+static DEFINE_SPINLOCK(iort_fwnode_lock);
+
+/**
+ * iort_set_fwnode() - Create iort_fwnode and use it to register
+ * iommu data in the iort_fwnode_list
+ *
+ * @iort_node: IORT table node associated with the IOMMU
+ * @fwnode: fwnode associated with the IORT node
+ *
+ * Returns: 0 on success
+ * <0 on failure
+ */
+static inline int iort_set_fwnode(struct acpi_iort_node *iort_node,
+ struct fwnode_handle *fwnode)
+{
+ struct iort_fwnode *np;
+
+ np = kzalloc(sizeof(struct iort_fwnode), GFP_ATOMIC);
+
+ if (WARN_ON(!np))
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&np->list);
+ np->iort_node = iort_node;
+ np->fwnode = fwnode;
+
+ spin_lock(&iort_fwnode_lock);
+ list_add_tail(&np->list, &iort_fwnode_list);
+ spin_unlock(&iort_fwnode_lock);
+
+ return 0;
+}
+
+/**
+ * iort_get_fwnode() - Retrieve fwnode associated with an IORT node
+ *
+ * @node: IORT table node to be looked-up
+ *
+ * Returns: fwnode_handle pointer on success, NULL on failure
+ */
+static inline struct fwnode_handle *iort_get_fwnode(
+ struct acpi_iort_node *node)
+{
+ struct iort_fwnode *curr;
+ struct fwnode_handle *fwnode = NULL;
+
+ spin_lock(&iort_fwnode_lock);
+ list_for_each_entry(curr, &iort_fwnode_list, list) {
+ if (curr->iort_node == node) {
+ fwnode = curr->fwnode;
+ break;
+ }
+ }
+ spin_unlock(&iort_fwnode_lock);
+
+ return fwnode;
+}
+
+/**
+ * iort_delete_fwnode() - Delete fwnode associated with an IORT node
+ *
+ * @node: IORT table node associated with fwnode to delete
+ */
+static inline void iort_delete_fwnode(struct acpi_iort_node *node)
+{
+ struct iort_fwnode *curr, *tmp;
+
+ spin_lock(&iort_fwnode_lock);
+ list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list) {
+ if (curr->iort_node == node) {
+ list_del(&curr->list);
+ kfree(curr);
+ break;
+ }
+ }
+ spin_unlock(&iort_fwnode_lock);
+}
+
+/**
+ * iort_get_iort_node() - Retrieve iort_node associated with an fwnode
+ *
+ * @fwnode: fwnode associated with device to be looked-up
+ *
+ * Returns: iort_node pointer on success, NULL on failure
+ */
+static inline struct acpi_iort_node *iort_get_iort_node(
+ struct fwnode_handle *fwnode)
+{
+ struct iort_fwnode *curr;
+ struct acpi_iort_node *iort_node = NULL;
+
+ spin_lock(&iort_fwnode_lock);
+ list_for_each_entry(curr, &iort_fwnode_list, list) {
+ if (curr->fwnode == fwnode) {
+ iort_node = curr->iort_node;
+ break;
+ }
+ }
+ spin_unlock(&iort_fwnode_lock);
+
+ return iort_node;
+}
+
+typedef acpi_status (*iort_find_node_callback)
+ (struct acpi_iort_node *node, void *context);
+
+/* Root pointer to the mapped IORT table */
+static struct acpi_table_header *iort_table;
+
+static LIST_HEAD(iort_msi_chip_list);
+static DEFINE_SPINLOCK(iort_msi_chip_lock);
+
+/**
+ * iort_register_domain_token() - register domain token along with related
+ * ITS ID and base address to the list from where we can get it back later on.
+ * @trans_id: ITS ID.
+ * @base: ITS base address.
+ * @fw_node: Domain token.
+ *
+ * Returns: 0 on success, -ENOMEM if no memory when allocating list element
+ */
+int iort_register_domain_token(int trans_id, phys_addr_t base,
+ struct fwnode_handle *fw_node)
+{
+ struct iort_its_msi_chip *its_msi_chip;
+
+ its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL);
+ if (!its_msi_chip)
+ return -ENOMEM;
+
+ its_msi_chip->fw_node = fw_node;
+ its_msi_chip->translation_id = trans_id;
+ its_msi_chip->base_addr = base;
+
+ spin_lock(&iort_msi_chip_lock);
+ list_add(&its_msi_chip->list, &iort_msi_chip_list);
+ spin_unlock(&iort_msi_chip_lock);
+
+ return 0;
+}
+
+/**
+ * iort_deregister_domain_token() - Deregister domain token based on ITS ID
+ * @trans_id: ITS ID.
+ *
+ * Returns: none.
+ */
+void iort_deregister_domain_token(int trans_id)
+{
+ struct iort_its_msi_chip *its_msi_chip, *t;
+
+ spin_lock(&iort_msi_chip_lock);
+ list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) {
+ if (its_msi_chip->translation_id == trans_id) {
+ list_del(&its_msi_chip->list);
+ kfree(its_msi_chip);
+ break;
+ }
+ }
+ spin_unlock(&iort_msi_chip_lock);
+}
+
+/**
+ * iort_find_domain_token() - Find domain token based on given ITS ID
+ * @trans_id: ITS ID.
+ *
+ * Returns: domain token when find on the list, NULL otherwise
+ */
+struct fwnode_handle *iort_find_domain_token(int trans_id)
+{
+ struct fwnode_handle *fw_node = NULL;
+ struct iort_its_msi_chip *its_msi_chip;
+
+ spin_lock(&iort_msi_chip_lock);
+ list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
+ if (its_msi_chip->translation_id == trans_id) {
+ fw_node = its_msi_chip->fw_node;
+ break;
+ }
+ }
+ spin_unlock(&iort_msi_chip_lock);
+
+ return fw_node;
+}
+
+static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type,
+ iort_find_node_callback callback,
+ void *context)
+{
+ struct acpi_iort_node *iort_node, *iort_end;
+ struct acpi_table_iort *iort;
+ int i;
+
+ if (!iort_table)
+ return NULL;
+
+ /* Get the first IORT node */
+ iort = (struct acpi_table_iort *)iort_table;
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+ iort->node_offset);
+ iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+ iort_table->length);
+
+ for (i = 0; i < iort->node_count; i++) {
+ if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
+ "IORT node pointer overflows, bad table!\n"))
+ return NULL;
+
+ if (iort_node->type == type &&
+ ACPI_SUCCESS(callback(iort_node, context)))
+ return iort_node;
+
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+ iort_node->length);
+ }
+
+ return NULL;
+}
+
+static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
+ void *context)
+{
+ struct device *dev = context;
+ acpi_status status = AE_NOT_FOUND;
+
+ if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_device *adev;
+ struct acpi_iort_named_component *ncomp;
+ struct device *nc_dev = dev;
+
+ /*
+ * Walk the device tree to find a device with an
+ * ACPI companion; there is no point in scanning
+ * IORT for a device matching a named component if
+ * the device does not have an ACPI companion to
+ * start with.
+ */
+ do {
+ adev = ACPI_COMPANION(nc_dev);
+ if (adev)
+ break;
+
+ nc_dev = nc_dev->parent;
+ } while (nc_dev);
+
+ if (!adev)
+ goto out;
+
+ status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(nc_dev, "Can't get device full path name\n");
+ goto out;
+ }
+
+ ncomp = (struct acpi_iort_named_component *)node->node_data;
+ status = !strcmp(ncomp->device_name, buf.pointer) ?
+ AE_OK : AE_NOT_FOUND;
+ acpi_os_free(buf.pointer);
+ } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
+ struct acpi_iort_root_complex *pci_rc;
+ struct pci_bus *bus;
+
+ bus = to_pci_bus(dev);
+ pci_rc = (struct acpi_iort_root_complex *)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->pci_segment_number == pci_domain_nr(bus) ?
+ AE_OK : AE_NOT_FOUND;
+ }
+out:
+ return status;
+}
+
+static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
+ u32 *rid_out, bool check_overlap)
+{
+ /* Single mapping does not care for input id */
+ if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
+ if (type == ACPI_IORT_NODE_NAMED_COMPONENT ||
+ type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
+ *rid_out = map->output_base;
+ return 0;
+ }
+
+ pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n",
+ map, type);
+ return -ENXIO;
+ }
+
+ if (rid_in < map->input_base ||
+ (rid_in > map->input_base + map->id_count))
+ return -ENXIO;
+
+ if (check_overlap) {
+ /*
+ * We already found a mapping for this input ID at the end of
+ * another region. If it coincides with the start of this
+ * region, we assume the prior match was due to the off-by-1
+ * issue mentioned below, and allow it to be superseded.
+ * Otherwise, things are *really* broken, and we just disregard
+ * duplicate matches entirely to retain compatibility.
+ */
+ pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n",
+ map, rid_in);
+ if (rid_in != map->input_base)
+ return -ENXIO;
+
+ pr_err(FW_BUG "applying workaround.\n");
+ }
+
+ *rid_out = map->output_base + (rid_in - map->input_base);
+
+ /*
+ * Due to confusion regarding the meaning of the id_count field (which
+ * carries the number of IDs *minus 1*), we may have to disregard this
+ * match if it is at the end of the range, and overlaps with the start
+ * of another one.
+ */
+ if (map->id_count > 0 && rid_in == map->input_base + map->id_count)
+ return -EAGAIN;
+ return 0;
+}
+
+static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
+ u32 *id_out, int index)
+{
+ struct acpi_iort_node *parent;
+ struct acpi_iort_id_mapping *map;
+
+ if (!node->mapping_offset || !node->mapping_count ||
+ index >= node->mapping_count)
+ return NULL;
+
+ map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
+ node->mapping_offset + index * sizeof(*map));
+
+ /* Firmware bug! */
+ if (!map->output_reference) {
+ 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_iort_node, iort_table,
+ map->output_reference);
+
+ if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
+ if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT ||
+ node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX ||
+ node->type == ACPI_IORT_NODE_SMMU_V3 ||
+ node->type == ACPI_IORT_NODE_PMCG) {
+ *id_out = map->output_base;
+ return parent;
+ }
+ }
+
+ return NULL;
+}
+
+static int iort_get_id_mapping_index(struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+ struct acpi_iort_pmcg *pmcg;
+
+ switch (node->type) {
+ case ACPI_IORT_NODE_SMMU_V3:
+ /*
+ * SMMUv3 dev ID mapping index was introduced in revision 1
+ * table, not available in revision 0
+ */
+ if (node->revision < 1)
+ return -EINVAL;
+
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+ /*
+ * ID mapping index is only ignored if all interrupts are
+ * GSIV based
+ */
+ if (smmu->event_gsiv && smmu->pri_gsiv && smmu->gerr_gsiv
+ && smmu->sync_gsiv)
+ 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",
+ node, node->type);
+ return -EINVAL;
+ }
+
+ return smmu->id_mapping_index;
+ case ACPI_IORT_NODE_PMCG:
+ pmcg = (struct acpi_iort_pmcg *)node->node_data;
+ if (pmcg->overflow_gsiv || node->mapping_count == 0)
+ return -EINVAL;
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node,
+ u32 id_in, u32 *id_out,
+ u8 type_mask)
+{
+ u32 id = id_in;
+
+ /* Parse the ID mapping tree to find specified node type */
+ while (node) {
+ struct acpi_iort_id_mapping *map;
+ int i, index, rc = 0;
+ u32 out_ref = 0, map_id = id;
+
+ if (IORT_TYPE_MASK(node->type) & type_mask) {
+ if (id_out)
+ *id_out = id;
+ return node;
+ }
+
+ if (!node->mapping_offset || !node->mapping_count)
+ goto fail_map;
+
+ map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
+ node->mapping_offset);
+
+ /* Firmware bug! */
+ if (!map->output_reference) {
+ pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n",
+ node, node->type);
+ goto fail_map;
+ }
+
+ /*
+ * Get the special ID mapping index (if any) and skip its
+ * associated ID map to prevent erroneous multi-stage
+ * IORT ID translations.
+ */
+ index = iort_get_id_mapping_index(node);
+
+ /* Do the ID translation */
+ for (i = 0; i < node->mapping_count; i++, map++) {
+ /* if it is special mapping index, skip it */
+ if (i == index)
+ continue;
+
+ rc = iort_id_map(map, node->type, map_id, &id, out_ref);
+ if (!rc)
+ break;
+ if (rc == -EAGAIN)
+ out_ref = map->output_reference;
+ }
+
+ if (i == node->mapping_count && !out_ref)
+ goto fail_map;
+
+ node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+ rc ? out_ref : map->output_reference);
+ }
+
+fail_map:
+ /* Map input ID to output ID unchanged on mapping failure */
+ if (id_out)
+ *id_out = id_in;
+
+ return NULL;
+}
+
+static struct acpi_iort_node *iort_node_map_platform_id(
+ struct acpi_iort_node *node, u32 *id_out, u8 type_mask,
+ int index)
+{
+ struct acpi_iort_node *parent;
+ u32 id;
+
+ /* step 1: retrieve the initial dev id */
+ parent = iort_node_get_id(node, &id, index);
+ if (!parent)
+ return NULL;
+
+ /*
+ * optional step 2: map the initial dev id if its parent is not
+ * the target type we want, map it again for the use cases such
+ * as NC (named component) -> SMMU -> ITS. If the type is matched,
+ * return the initial dev id and its parent pointer directly.
+ */
+ if (!(IORT_TYPE_MASK(parent->type) & type_mask))
+ parent = iort_node_map_id(parent, id, id_out, type_mask);
+ else
+ if (id_out)
+ *id_out = id;
+
+ return parent;
+}
+
+static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
+{
+ struct pci_bus *pbus;
+
+ if (!dev_is_pci(dev)) {
+ struct acpi_iort_node *node;
+ /*
+ * scan iort_fwnode_list to see if it's an iort platform
+ * device (such as SMMU, PMCG),its iort node already cached
+ * and associated with fwnode when iort platform devices
+ * were initialized.
+ */
+ node = iort_get_iort_node(dev->fwnode);
+ if (node)
+ return node;
+ /*
+ * if not, then it should be a platform device defined in
+ * DSDT/SSDT (with Named Component node in IORT)
+ */
+ return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+ iort_match_node_callback, dev);
+ }
+
+ pbus = to_pci_dev(dev)->bus;
+
+ return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+ iort_match_node_callback, &pbus->dev);
+}
+
+/**
+ * iort_msi_map_id() - Map a MSI input ID for a device
+ * @dev: The device for which the mapping is to be done.
+ * @input_id: The device input ID.
+ *
+ * Returns: mapped MSI ID on success, input ID otherwise
+ */
+u32 iort_msi_map_id(struct device *dev, u32 input_id)
+{
+ struct acpi_iort_node *node;
+ u32 dev_id;
+
+ node = iort_find_dev_node(dev);
+ if (!node)
+ return input_id;
+
+ iort_node_map_id(node, input_id, &dev_id, IORT_MSI_TYPE);
+ return dev_id;
+}
+
+/**
+ * iort_pmsi_get_dev_id() - Get the device id for a device
+ * @dev: The device for which the mapping is to be done.
+ * @dev_id: The device ID found.
+ *
+ * Returns: 0 for successful find a dev id, -ENODEV on error
+ */
+int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
+{
+ int i, index;
+ struct acpi_iort_node *node;
+
+ node = iort_find_dev_node(dev);
+ if (!node)
+ return -ENODEV;
+
+ index = iort_get_id_mapping_index(node);
+ /* if there is a valid index, go get the dev_id directly */
+ if (index >= 0) {
+ if (iort_node_get_id(node, dev_id, index))
+ return 0;
+ } else {
+ for (i = 0; i < node->mapping_count; i++) {
+ if (iort_node_map_platform_id(node, dev_id,
+ IORT_MSI_TYPE, i))
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
+{
+ struct iort_its_msi_chip *its_msi_chip;
+ int ret = -ENODEV;
+
+ spin_lock(&iort_msi_chip_lock);
+ list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
+ if (its_msi_chip->translation_id == its_id) {
+ *base = its_msi_chip->base_addr;
+ ret = 0;
+ break;
+ }
+ }
+ spin_unlock(&iort_msi_chip_lock);
+
+ return ret;
+}
+
+/**
+ * iort_dev_find_its_id() - Find the ITS identifier for a device
+ * @dev: The device.
+ * @id: Device's ID
+ * @idx: Index of the ITS identifier list.
+ * @its_id: ITS identifier.
+ *
+ * Returns: 0 on success, appropriate error value otherwise
+ */
+static int iort_dev_find_its_id(struct device *dev, u32 id,
+ unsigned int idx, int *its_id)
+{
+ struct acpi_iort_its_group *its;
+ struct acpi_iort_node *node;
+
+ node = iort_find_dev_node(dev);
+ if (!node)
+ return -ENXIO;
+
+ node = iort_node_map_id(node, id, NULL, IORT_MSI_TYPE);
+ if (!node)
+ return -ENXIO;
+
+ /* Move to ITS specific data */
+ its = (struct acpi_iort_its_group *)node->node_data;
+ if (idx >= its->its_count) {
+ dev_err(dev, "requested ITS ID index [%d] overruns ITS entries [%d]\n",
+ idx, its->its_count);
+ return -ENXIO;
+ }
+
+ *its_id = its->identifiers[idx];
+ return 0;
+}
+
+/**
+ * iort_get_device_domain() - Find MSI domain related to a device
+ * @dev: The device.
+ * @id: Requester ID for the device.
+ * @bus_token: irq domain bus token.
+ *
+ * Returns: the MSI domain for this device, NULL otherwise
+ */
+struct irq_domain *iort_get_device_domain(struct device *dev, u32 id,
+ enum irq_domain_bus_token bus_token)
+{
+ struct fwnode_handle *handle;
+ int its_id;
+
+ if (iort_dev_find_its_id(dev, id, 0, &its_id))
+ return NULL;
+
+ handle = iort_find_domain_token(its_id);
+ if (!handle)
+ return NULL;
+
+ return irq_find_matching_fwnode(handle, bus_token);
+}
+
+static void iort_set_device_domain(struct device *dev,
+ struct acpi_iort_node *node)
+{
+ struct acpi_iort_its_group *its;
+ struct acpi_iort_node *msi_parent;
+ struct acpi_iort_id_mapping *map;
+ struct fwnode_handle *iort_fwnode;
+ struct irq_domain *domain;
+ int index;
+
+ index = iort_get_id_mapping_index(node);
+ if (index < 0)
+ return;
+
+ map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
+ node->mapping_offset + index * sizeof(*map));
+
+ /* Firmware bug! */
+ if (!map->output_reference ||
+ !(map->flags & ACPI_IORT_ID_SINGLE_MAPPING)) {
+ pr_err(FW_BUG "[node %p type %d] Invalid MSI mapping\n",
+ node, node->type);
+ return;
+ }
+
+ msi_parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+ map->output_reference);
+
+ if (!msi_parent || msi_parent->type != ACPI_IORT_NODE_ITS_GROUP)
+ return;
+
+ /* Move to ITS specific data */
+ its = (struct acpi_iort_its_group *)msi_parent->node_data;
+
+ iort_fwnode = iort_find_domain_token(its->identifiers[0]);
+ if (!iort_fwnode)
+ return;
+
+ domain = irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI);
+ if (domain)
+ dev_set_msi_domain(dev, domain);
+}
+
+/**
+ * iort_get_platform_device_domain() - Find MSI domain related to a
+ * platform device
+ * @dev: the dev pointer associated with the platform device
+ *
+ * Returns: the MSI domain for this device, NULL otherwise
+ */
+static struct irq_domain *iort_get_platform_device_domain(struct device *dev)
+{
+ struct acpi_iort_node *node, *msi_parent = NULL;
+ struct fwnode_handle *iort_fwnode;
+ struct acpi_iort_its_group *its;
+ int i;
+
+ /* find its associated iort node */
+ node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+ iort_match_node_callback, dev);
+ if (!node)
+ return NULL;
+
+ /* then find its msi parent node */
+ for (i = 0; i < node->mapping_count; i++) {
+ msi_parent = iort_node_map_platform_id(node, NULL,
+ IORT_MSI_TYPE, i);
+ if (msi_parent)
+ break;
+ }
+
+ if (!msi_parent)
+ return NULL;
+
+ /* Move to ITS specific data */
+ its = (struct acpi_iort_its_group *)msi_parent->node_data;
+
+ iort_fwnode = iort_find_domain_token(its->identifiers[0]);
+ if (!iort_fwnode)
+ return NULL;
+
+ return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI);
+}
+
+void acpi_configure_pmsi_domain(struct device *dev)
+{
+ struct irq_domain *msi_domain;
+
+ msi_domain = iort_get_platform_device_domain(dev);
+ if (msi_domain)
+ dev_set_msi_domain(dev, msi_domain);
+}
+
+#ifdef CONFIG_IOMMU_API
+static void iort_rmr_free(struct device *dev,
+ struct iommu_resv_region *region)
+{
+ struct iommu_iort_rmr_data *rmr_data;
+
+ rmr_data = container_of(region, struct iommu_iort_rmr_data, rr);
+ kfree(rmr_data->sids);
+ kfree(rmr_data);
+}
+
+static struct iommu_iort_rmr_data *iort_rmr_alloc(
+ struct acpi_iort_rmr_desc *rmr_desc,
+ int prot, enum iommu_resv_type type,
+ u32 *sids, u32 num_sids)
+{
+ struct iommu_iort_rmr_data *rmr_data;
+ struct iommu_resv_region *region;
+ u32 *sids_copy;
+ u64 addr = rmr_desc->base_address, size = rmr_desc->length;
+
+ rmr_data = kmalloc(sizeof(*rmr_data), GFP_KERNEL);
+ if (!rmr_data)
+ return NULL;
+
+ /* Create a copy of SIDs array to associate with this rmr_data */
+ sids_copy = kmemdup(sids, num_sids * sizeof(*sids), GFP_KERNEL);
+ if (!sids_copy) {
+ kfree(rmr_data);
+ return NULL;
+ }
+ rmr_data->sids = sids_copy;
+ rmr_data->num_sids = num_sids;
+
+ if (!IS_ALIGNED(addr, SZ_64K) || !IS_ALIGNED(size, SZ_64K)) {
+ /* PAGE align base addr and size */
+ addr &= PAGE_MASK;
+ size = PAGE_ALIGN(size + offset_in_page(rmr_desc->base_address));
+
+ pr_err(FW_BUG "RMR descriptor[0x%llx - 0x%llx] not aligned to 64K, continue with [0x%llx - 0x%llx]\n",
+ rmr_desc->base_address,
+ rmr_desc->base_address + rmr_desc->length - 1,
+ addr, addr + size - 1);
+ }
+
+ region = &rmr_data->rr;
+ INIT_LIST_HEAD(&region->list);
+ region->start = addr;
+ region->length = size;
+ region->prot = prot;
+ region->type = type;
+ region->free = iort_rmr_free;
+
+ return rmr_data;
+}
+
+static void iort_rmr_desc_check_overlap(struct acpi_iort_rmr_desc *desc,
+ u32 count)
+{
+ int i, j;
+
+ for (i = 0; i < count; i++) {
+ u64 end, start = desc[i].base_address, length = desc[i].length;
+
+ if (!length) {
+ pr_err(FW_BUG "RMR descriptor[0x%llx] with zero length, continue anyway\n",
+ start);
+ continue;
+ }
+
+ end = start + length - 1;
+
+ /* Check for address overlap */
+ for (j = i + 1; j < count; j++) {
+ u64 e_start = desc[j].base_address;
+ u64 e_end = e_start + desc[j].length - 1;
+
+ if (start <= e_end && end >= e_start)
+ pr_err(FW_BUG "RMR descriptor[0x%llx - 0x%llx] overlaps, continue anyway\n",
+ start, end);
+ }
+ }
+}
+
+/*
+ * Please note, we will keep the already allocated RMR reserve
+ * regions in case of a memory allocation failure.
+ */
+static void iort_get_rmrs(struct acpi_iort_node *node,
+ struct acpi_iort_node *smmu,
+ u32 *sids, u32 num_sids,
+ struct list_head *head)
+{
+ struct acpi_iort_rmr *rmr = (struct acpi_iort_rmr *)node->node_data;
+ struct acpi_iort_rmr_desc *rmr_desc;
+ int i;
+
+ rmr_desc = ACPI_ADD_PTR(struct acpi_iort_rmr_desc, node,
+ rmr->rmr_offset);
+
+ iort_rmr_desc_check_overlap(rmr_desc, rmr->rmr_count);
+
+ for (i = 0; i < rmr->rmr_count; i++, rmr_desc++) {
+ struct iommu_iort_rmr_data *rmr_data;
+ enum iommu_resv_type type;
+ int prot = IOMMU_READ | IOMMU_WRITE;
+
+ if (rmr->flags & ACPI_IORT_RMR_REMAP_PERMITTED)
+ type = IOMMU_RESV_DIRECT_RELAXABLE;
+ else
+ type = IOMMU_RESV_DIRECT;
+
+ if (rmr->flags & ACPI_IORT_RMR_ACCESS_PRIVILEGE)
+ prot |= IOMMU_PRIV;
+
+ /* Attributes 0x00 - 0x03 represents device memory */
+ if (ACPI_IORT_RMR_ACCESS_ATTRIBUTES(rmr->flags) <=
+ ACPI_IORT_RMR_ATTR_DEVICE_GRE)
+ prot |= IOMMU_MMIO;
+ else if (ACPI_IORT_RMR_ACCESS_ATTRIBUTES(rmr->flags) ==
+ ACPI_IORT_RMR_ATTR_NORMAL_IWB_OWB)
+ prot |= IOMMU_CACHE;
+
+ rmr_data = iort_rmr_alloc(rmr_desc, prot, type,
+ sids, num_sids);
+ if (!rmr_data)
+ return;
+
+ list_add_tail(&rmr_data->rr.list, head);
+ }
+}
+
+static u32 *iort_rmr_alloc_sids(u32 *sids, u32 count, u32 id_start,
+ u32 new_count)
+{
+ u32 *new_sids;
+ u32 total_count = count + new_count;
+ int i;
+
+ new_sids = krealloc_array(sids, count + new_count,
+ sizeof(*new_sids), GFP_KERNEL);
+ if (!new_sids)
+ return NULL;
+
+ for (i = count; i < total_count; i++)
+ new_sids[i] = id_start++;
+
+ return new_sids;
+}
+
+static bool iort_rmr_has_dev(struct device *dev, u32 id_start,
+ u32 id_count)
+{
+ int i;
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+
+ /*
+ * Make sure the kernel has preserved the boot firmware PCIe
+ * configuration. This is required to ensure that the RMR PCIe
+ * StreamIDs are still valid (Refer: ARM DEN 0049E.d Section 3.1.1.5).
+ */
+ if (dev_is_pci(dev)) {
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_host_bridge *host = pci_find_host_bridge(pdev->bus);
+
+ if (!host->preserve_config)
+ return false;
+ }
+
+ for (i = 0; i < fwspec->num_ids; i++) {
+ if (fwspec->ids[i] >= id_start &&
+ fwspec->ids[i] <= id_start + id_count)
+ return true;
+ }
+
+ return false;
+}
+
+static void iort_node_get_rmr_info(struct acpi_iort_node *node,
+ struct acpi_iort_node *iommu,
+ struct device *dev, struct list_head *head)
+{
+ struct acpi_iort_node *smmu = NULL;
+ struct acpi_iort_rmr *rmr;
+ struct acpi_iort_id_mapping *map;
+ u32 *sids = NULL;
+ u32 num_sids = 0;
+ int i;
+
+ if (!node->mapping_offset || !node->mapping_count) {
+ pr_err(FW_BUG "Invalid ID mapping, skipping RMR node %p\n",
+ node);
+ return;
+ }
+
+ rmr = (struct acpi_iort_rmr *)node->node_data;
+ if (!rmr->rmr_offset || !rmr->rmr_count)
+ return;
+
+ map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
+ node->mapping_offset);
+
+ /*
+ * Go through the ID mappings and see if we have a match for SMMU
+ * and dev(if !NULL). If found, get the sids for the Node.
+ * Please note, id_count is equal to the number of IDs in the
+ * range minus one.
+ */
+ for (i = 0; i < node->mapping_count; i++, map++) {
+ struct acpi_iort_node *parent;
+
+ parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+ map->output_reference);
+ if (parent != iommu)
+ continue;
+
+ /* If dev is valid, check RMR node corresponds to the dev SID */
+ if (dev && !iort_rmr_has_dev(dev, map->output_base,
+ map->id_count))
+ continue;
+
+ /* Retrieve SIDs associated with the Node. */
+ sids = iort_rmr_alloc_sids(sids, num_sids, map->output_base,
+ map->id_count + 1);
+ if (!sids)
+ return;
+
+ num_sids += map->id_count + 1;
+ }
+
+ if (!sids)
+ return;
+
+ iort_get_rmrs(node, smmu, sids, num_sids, head);
+ kfree(sids);
+}
+
+static void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev,
+ struct list_head *head)
+{
+ struct acpi_table_iort *iort;
+ struct acpi_iort_node *iort_node, *iort_end;
+ int i;
+
+ /* Only supports ARM DEN 0049E.d onwards */
+ if (iort_table->revision < 5)
+ return;
+
+ iort = (struct acpi_table_iort *)iort_table;
+
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+ iort->node_offset);
+ iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+ iort_table->length);
+
+ for (i = 0; i < iort->node_count; i++) {
+ if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
+ "IORT node pointer overflows, bad table!\n"))
+ return;
+
+ if (iort_node->type == ACPI_IORT_NODE_RMR)
+ iort_node_get_rmr_info(iort_node, iommu, dev, head);
+
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+ iort_node->length);
+ }
+}
+
+/*
+ * Populate the RMR list associated with a given IOMMU and dev(if provided).
+ * If dev is NULL, the function populates all the RMRs associated with the
+ * given IOMMU.
+ */
+static void iort_iommu_rmr_get_resv_regions(struct fwnode_handle *iommu_fwnode,
+ struct device *dev,
+ struct list_head *head)
+{
+ struct acpi_iort_node *iommu;
+
+ iommu = iort_get_iort_node(iommu_fwnode);
+ if (!iommu)
+ return;
+
+ iort_find_rmrs(iommu, dev, head);
+}
+
+static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev)
+{
+ struct acpi_iort_node *iommu;
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+
+ iommu = iort_get_iort_node(fwspec->iommu_fwnode);
+
+ if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) {
+ struct acpi_iort_smmu_v3 *smmu;
+
+ smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data;
+ if (smmu->model == ACPI_IORT_SMMU_V3_HISILICON_HI161X)
+ return iommu;
+ }
+
+ return NULL;
+}
+
+/*
+ * Retrieve platform specific HW MSI reserve regions.
+ * The ITS interrupt translation spaces (ITS_base + SZ_64K, SZ_64K)
+ * associated with the device are the HW MSI reserved regions.
+ */
+static void iort_iommu_msi_get_resv_regions(struct device *dev,
+ struct list_head *head)
+{
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct acpi_iort_its_group *its;
+ struct acpi_iort_node *iommu_node, *its_node = NULL;
+ int i;
+
+ iommu_node = iort_get_msi_resv_iommu(dev);
+ if (!iommu_node)
+ return;
+
+ /*
+ * Current logic to reserve ITS regions relies on HW topologies
+ * where a given PCI or named component maps its IDs to only one
+ * ITS group; if a PCI or named component can map its IDs to
+ * different ITS groups through IORT mappings this function has
+ * to be reworked to ensure we reserve regions for all ITS groups
+ * a given PCI or named component may map IDs to.
+ */
+
+ for (i = 0; i < fwspec->num_ids; i++) {
+ its_node = iort_node_map_id(iommu_node,
+ fwspec->ids[i],
+ NULL, IORT_MSI_TYPE);
+ if (its_node)
+ break;
+ }
+
+ if (!its_node)
+ return;
+
+ /* Move to ITS specific data */
+ its = (struct acpi_iort_its_group *)its_node->node_data;
+
+ for (i = 0; i < its->its_count; i++) {
+ phys_addr_t base;
+
+ if (!iort_find_its_base(its->identifiers[i], &base)) {
+ int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+ struct iommu_resv_region *region;
+
+ region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K,
+ prot, IOMMU_RESV_MSI,
+ GFP_KERNEL);
+ if (region)
+ list_add_tail(&region->list, head);
+ }
+ }
+}
+
+/**
+ * iort_iommu_get_resv_regions - Generic helper to retrieve reserved regions.
+ * @dev: Device from iommu_get_resv_regions()
+ * @head: Reserved region list from iommu_get_resv_regions()
+ */
+void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head)
+{
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+
+ iort_iommu_msi_get_resv_regions(dev, head);
+ iort_iommu_rmr_get_resv_regions(fwspec->iommu_fwnode, dev, head);
+}
+
+/**
+ * iort_get_rmr_sids - Retrieve IORT RMR node reserved regions with
+ * associated StreamIDs information.
+ * @iommu_fwnode: fwnode associated with IOMMU
+ * @head: Resereved region list
+ */
+void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode,
+ struct list_head *head)
+{
+ iort_iommu_rmr_get_resv_regions(iommu_fwnode, NULL, head);
+}
+EXPORT_SYMBOL_GPL(iort_get_rmr_sids);
+
+/**
+ * iort_put_rmr_sids - Free memory allocated for RMR reserved regions.
+ * @iommu_fwnode: fwnode associated with IOMMU
+ * @head: Resereved region list
+ */
+void iort_put_rmr_sids(struct fwnode_handle *iommu_fwnode,
+ struct list_head *head)
+{
+ struct iommu_resv_region *entry, *next;
+
+ list_for_each_entry_safe(entry, next, head, list)
+ entry->free(NULL, entry);
+}
+EXPORT_SYMBOL_GPL(iort_put_rmr_sids);
+
+static inline bool iort_iommu_driver_enabled(u8 type)
+{
+ switch (type) {
+ case ACPI_IORT_NODE_SMMU_V3:
+ return IS_ENABLED(CONFIG_ARM_SMMU_V3);
+ case ACPI_IORT_NODE_SMMU:
+ return IS_ENABLED(CONFIG_ARM_SMMU);
+ default:
+ pr_warn("IORT node type %u does not describe an SMMU\n", type);
+ return false;
+ }
+}
+
+static bool iort_pci_rc_supports_ats(struct acpi_iort_node *node)
+{
+ struct acpi_iort_root_complex *pci_rc;
+
+ pci_rc = (struct acpi_iort_root_complex *)node->node_data;
+ return pci_rc->ats_attribute & ACPI_IORT_ATS_SUPPORTED;
+}
+
+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)
+ return -ENODEV;
+
+ iort_fwnode = iort_get_fwnode(node);
+ if (!iort_fwnode)
+ 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.
+ */
+ 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);
+}
+
+struct iort_pci_alias_info {
+ struct device *dev;
+ struct acpi_iort_node *node;
+};
+
+static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
+{
+ struct iort_pci_alias_info *info = data;
+ struct acpi_iort_node *parent;
+ u32 streamid;
+
+ parent = iort_node_map_id(info->node, alias, &streamid,
+ IORT_IOMMU_TYPE);
+ return iort_iommu_xlate(info->dev, parent, streamid);
+}
+
+static void iort_named_component_init(struct device *dev,
+ struct acpi_iort_node *node)
+{
+ struct property_entry props[3] = {};
+ struct acpi_iort_named_component *nc;
+
+ nc = (struct acpi_iort_named_component *)node->node_data;
+ props[0] = PROPERTY_ENTRY_U32("pasid-num-bits",
+ FIELD_GET(ACPI_IORT_NC_PASID_BITS,
+ nc->node_flags));
+ if (nc->node_flags & ACPI_IORT_NC_STALL_SUPPORTED)
+ props[1] = PROPERTY_ENTRY_BOOL("dma-can-stall");
+
+ if (device_create_managed_software_node(dev, props, NULL))
+ dev_warn(dev, "Could not add device properties\n");
+}
+
+static int iort_nc_iommu_map(struct device *dev, struct acpi_iort_node *node)
+{
+ struct acpi_iort_node *parent;
+ int err = -ENODEV, i = 0;
+ u32 streamid = 0;
+
+ do {
+
+ parent = iort_node_map_platform_id(node, &streamid,
+ IORT_IOMMU_TYPE,
+ i++);
+
+ if (parent)
+ err = iort_iommu_xlate(dev, parent, streamid);
+ } while (parent && !err);
+
+ return err;
+}
+
+static int iort_nc_iommu_map_id(struct device *dev,
+ struct acpi_iort_node *node,
+ const u32 *in_id)
+{
+ struct acpi_iort_node *parent;
+ u32 streamid;
+
+ parent = iort_node_map_id(node, *in_id, &streamid, IORT_IOMMU_TYPE);
+ if (parent)
+ return iort_iommu_xlate(dev, parent, streamid);
+
+ return -ENODEV;
+}
+
+
+/**
+ * iort_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 iort_iommu_configure_id(struct device *dev, const u32 *id_in)
+{
+ struct acpi_iort_node *node;
+ int err = -ENODEV;
+
+ if (dev_is_pci(dev)) {
+ struct iommu_fwspec *fwspec;
+ struct pci_bus *bus = to_pci_dev(dev)->bus;
+ struct iort_pci_alias_info info = { .dev = dev };
+
+ node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+ iort_match_node_callback, &bus->dev);
+ if (!node)
+ return -ENODEV;
+
+ info.node = node;
+ err = pci_for_each_dma_alias(to_pci_dev(dev),
+ iort_pci_iommu_init, &info);
+
+ fwspec = dev_iommu_fwspec_get(dev);
+ if (fwspec && iort_pci_rc_supports_ats(node))
+ fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS;
+ } else {
+ node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+ iort_match_node_callback, dev);
+ if (!node)
+ return -ENODEV;
+
+ err = id_in ? iort_nc_iommu_map_id(dev, node, id_in) :
+ iort_nc_iommu_map(dev, node);
+
+ if (!err)
+ iort_named_component_init(dev, node);
+ }
+
+ return err;
+}
+
+#else
+void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head)
+{ }
+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)
+{
+ struct acpi_iort_node *node;
+ struct acpi_iort_named_component *ncomp;
+
+ node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+ iort_match_node_callback, dev);
+ if (!node)
+ return -ENODEV;
+
+ ncomp = (struct acpi_iort_named_component *)node->node_data;
+
+ if (!ncomp->memory_address_limit) {
+ pr_warn(FW_BUG "Named component missing memory address limit\n");
+ return -EINVAL;
+ }
+
+ *size = ncomp->memory_address_limit >= 64 ? U64_MAX :
+ 1ULL<<ncomp->memory_address_limit;
+
+ return 0;
+}
+
+static int rc_dma_get_range(struct device *dev, u64 *size)
+{
+ struct acpi_iort_node *node;
+ struct acpi_iort_root_complex *rc;
+ struct pci_bus *pbus = to_pci_dev(dev)->bus;
+
+ node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+ iort_match_node_callback, &pbus->dev);
+ if (!node || node->revision < 1)
+ return -ENODEV;
+
+ rc = (struct acpi_iort_root_complex *)node->node_data;
+
+ if (!rc->memory_address_limit) {
+ pr_warn(FW_BUG "Root complex missing memory address limit\n");
+ return -EINVAL;
+ }
+
+ *size = rc->memory_address_limit >= 64 ? U64_MAX :
+ 1ULL<<rc->memory_address_limit;
+
+ return 0;
+}
+
+/**
+ * iort_dma_get_ranges() - Look up DMA addressing limit for the device
+ * @dev: device to lookup
+ * @size: DMA range size result pointer
+ *
+ * Return: 0 on success, an error otherwise.
+ */
+int iort_dma_get_ranges(struct device *dev, u64 *size)
+{
+ if (dev_is_pci(dev))
+ return rc_dma_get_range(dev, size);
+ else
+ return nc_dma_get_range(dev, size);
+}
+
+static void __init acpi_iort_register_irq(int hwirq, const char *name,
+ int trigger,
+ struct resource *res)
+{
+ int irq = acpi_register_gsi(NULL, hwirq, trigger,
+ ACPI_ACTIVE_HIGH);
+
+ if (irq <= 0) {
+ pr_err("could not register gsi hwirq %d name [%s]\n", hwirq,
+ name);
+ return;
+ }
+
+ res->start = irq;
+ res->end = irq;
+ res->flags = IORESOURCE_IRQ;
+ res->name = name;
+}
+
+static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+ /* Always present mem resource */
+ int num_res = 1;
+
+ /* Retrieve SMMUv3 specific data */
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ if (smmu->event_gsiv)
+ num_res++;
+
+ if (smmu->pri_gsiv)
+ num_res++;
+
+ if (smmu->gerr_gsiv)
+ num_res++;
+
+ if (smmu->sync_gsiv)
+ num_res++;
+
+ return num_res;
+}
+
+static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu)
+{
+ /*
+ * Cavium ThunderX2 implementation doesn't not support unique
+ * irq line. Use single irq line for all the SMMUv3 interrupts.
+ */
+ if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX)
+ return false;
+
+ /*
+ * ThunderX2 doesn't support MSIs from the SMMU, so we're checking
+ * SPI numbers here.
+ */
+ return smmu->event_gsiv == smmu->pri_gsiv &&
+ smmu->event_gsiv == smmu->gerr_gsiv &&
+ smmu->event_gsiv == smmu->sync_gsiv;
+}
+
+static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu)
+{
+ /*
+ * Override the size, for Cavium ThunderX2 implementation
+ * which doesn't support the page 1 SMMU register space.
+ */
+ if (smmu->model == ACPI_IORT_SMMU_V3_CAVIUM_CN99XX)
+ return SZ_64K;
+
+ return SZ_128K;
+}
+
+static void __init arm_smmu_v3_init_resources(struct resource *res,
+ struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+ int num_res = 0;
+
+ /* Retrieve SMMUv3 specific data */
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ res[num_res].start = smmu->base_address;
+ res[num_res].end = smmu->base_address +
+ arm_smmu_v3_resource_size(smmu) - 1;
+ res[num_res].flags = IORESOURCE_MEM;
+
+ num_res++;
+ if (arm_smmu_v3_is_combined_irq(smmu)) {
+ if (smmu->event_gsiv)
+ acpi_iort_register_irq(smmu->event_gsiv, "combined",
+ ACPI_EDGE_SENSITIVE,
+ &res[num_res++]);
+ } else {
+
+ if (smmu->event_gsiv)
+ acpi_iort_register_irq(smmu->event_gsiv, "eventq",
+ ACPI_EDGE_SENSITIVE,
+ &res[num_res++]);
+
+ if (smmu->pri_gsiv)
+ acpi_iort_register_irq(smmu->pri_gsiv, "priq",
+ ACPI_EDGE_SENSITIVE,
+ &res[num_res++]);
+
+ if (smmu->gerr_gsiv)
+ acpi_iort_register_irq(smmu->gerr_gsiv, "gerror",
+ ACPI_EDGE_SENSITIVE,
+ &res[num_res++]);
+
+ if (smmu->sync_gsiv)
+ acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync",
+ ACPI_EDGE_SENSITIVE,
+ &res[num_res++]);
+ }
+}
+
+static void __init arm_smmu_v3_dma_configure(struct device *dev,
+ struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+ enum dev_dma_attr attr;
+
+ /* Retrieve SMMUv3 specific data */
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ attr = (smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE) ?
+ DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
+
+ /* We expect the dma masks to be equivalent for all SMMUv3 set-ups */
+ dev->dma_mask = &dev->coherent_dma_mask;
+
+ /* Configure DMA for the page table walker */
+ acpi_dma_configure(dev, attr);
+}
+
+#if defined(CONFIG_ACPI_NUMA)
+/*
+ * set numa proximity domain for smmuv3 device
+ */
+static int __init arm_smmu_v3_set_proximity(struct device *dev,
+ struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+ if (smmu->flags & ACPI_IORT_SMMU_V3_PXM_VALID) {
+ int dev_node = pxm_to_node(smmu->pxm);
+
+ if (dev_node != NUMA_NO_NODE && !node_online(dev_node))
+ return -EINVAL;
+
+ set_dev_node(dev, dev_node);
+ pr_info("SMMU-v3[%llx] Mapped to Proximity domain %d\n",
+ smmu->base_address,
+ smmu->pxm);
+ }
+ return 0;
+}
+#else
+#define arm_smmu_v3_set_proximity NULL
+#endif
+
+static int __init arm_smmu_count_resources(struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu *smmu;
+
+ /* Retrieve SMMU specific data */
+ smmu = (struct acpi_iort_smmu *)node->node_data;
+
+ /*
+ * Only consider the global fault interrupt and ignore the
+ * configuration access interrupt.
+ *
+ * MMIO address and global fault interrupt resources are always
+ * present so add them to the context interrupt count as a static
+ * value.
+ */
+ return smmu->context_interrupt_count + 2;
+}
+
+static void __init arm_smmu_init_resources(struct resource *res,
+ struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu *smmu;
+ int i, hw_irq, trigger, num_res = 0;
+ u64 *ctx_irq, *glb_irq;
+
+ /* Retrieve SMMU specific data */
+ smmu = (struct acpi_iort_smmu *)node->node_data;
+
+ res[num_res].start = smmu->base_address;
+ res[num_res].end = smmu->base_address + smmu->span - 1;
+ res[num_res].flags = IORESOURCE_MEM;
+ num_res++;
+
+ glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset);
+ /* Global IRQs */
+ hw_irq = IORT_IRQ_MASK(glb_irq[0]);
+ trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]);
+
+ acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger,
+ &res[num_res++]);
+
+ /* Context IRQs */
+ ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset);
+ for (i = 0; i < smmu->context_interrupt_count; i++) {
+ hw_irq = IORT_IRQ_MASK(ctx_irq[i]);
+ trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]);
+
+ acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger,
+ &res[num_res++]);
+ }
+}
+
+static void __init arm_smmu_dma_configure(struct device *dev,
+ struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu *smmu;
+ enum dev_dma_attr attr;
+
+ /* Retrieve SMMU specific data */
+ smmu = (struct acpi_iort_smmu *)node->node_data;
+
+ attr = (smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK) ?
+ DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
+
+ /* We expect the dma masks to be equivalent for SMMU set-ups */
+ dev->dma_mask = &dev->coherent_dma_mask;
+
+ /* Configure DMA for the page table walker */
+ acpi_dma_configure(dev, attr);
+}
+
+static int __init arm_smmu_v3_pmcg_count_resources(struct acpi_iort_node *node)
+{
+ struct acpi_iort_pmcg *pmcg;
+
+ /* Retrieve PMCG specific data */
+ pmcg = (struct acpi_iort_pmcg *)node->node_data;
+
+ /*
+ * There are always 2 memory resources.
+ * If the overflow_gsiv is present then add that for a total of 3.
+ */
+ return pmcg->overflow_gsiv ? 3 : 2;
+}
+
+static void __init arm_smmu_v3_pmcg_init_resources(struct resource *res,
+ struct acpi_iort_node *node)
+{
+ struct acpi_iort_pmcg *pmcg;
+
+ /* Retrieve PMCG specific data */
+ pmcg = (struct acpi_iort_pmcg *)node->node_data;
+
+ res[0].start = pmcg->page0_base_address;
+ res[0].end = pmcg->page0_base_address + SZ_4K - 1;
+ res[0].flags = IORESOURCE_MEM;
+ /*
+ * The initial version in DEN0049C lacked a way to describe register
+ * page 1, which makes it broken for most PMCG implementations; in
+ * that case, just let the driver fail gracefully if it expects to
+ * find a second memory resource.
+ */
+ if (node->revision > 0) {
+ res[1].start = pmcg->page1_base_address;
+ res[1].end = pmcg->page1_base_address + SZ_4K - 1;
+ res[1].flags = IORESOURCE_MEM;
+ }
+
+ if (pmcg->overflow_gsiv)
+ acpi_iort_register_irq(pmcg->overflow_gsiv, "overflow",
+ ACPI_EDGE_SENSITIVE, &res[2]);
+}
+
+static struct acpi_platform_list pmcg_plat_info[] __initdata = {
+ /* HiSilicon Hip08 Platform */
+ {"HISI ", "HIP08 ", 0, ACPI_SIG_IORT, greater_than_or_equal,
+ "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},
+ { }
+};
+
+static int __init arm_smmu_v3_pmcg_add_platdata(struct platform_device *pdev)
+{
+ u32 model;
+ int idx;
+
+ idx = acpi_match_platform_list(pmcg_plat_info);
+ if (idx >= 0)
+ model = pmcg_plat_info[idx].data;
+ else
+ model = IORT_SMMU_V3_PMCG_GENERIC;
+
+ return platform_device_add_data(pdev, &model, sizeof(model));
+}
+
+struct iort_dev_config {
+ const char *name;
+ int (*dev_init)(struct acpi_iort_node *node);
+ void (*dev_dma_configure)(struct device *dev,
+ struct acpi_iort_node *node);
+ int (*dev_count_resources)(struct acpi_iort_node *node);
+ void (*dev_init_resources)(struct resource *res,
+ struct acpi_iort_node *node);
+ int (*dev_set_proximity)(struct device *dev,
+ struct acpi_iort_node *node);
+ int (*dev_add_platdata)(struct platform_device *pdev);
+};
+
+static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = {
+ .name = "arm-smmu-v3",
+ .dev_dma_configure = arm_smmu_v3_dma_configure,
+ .dev_count_resources = arm_smmu_v3_count_resources,
+ .dev_init_resources = arm_smmu_v3_init_resources,
+ .dev_set_proximity = arm_smmu_v3_set_proximity,
+};
+
+static const struct iort_dev_config iort_arm_smmu_cfg __initconst = {
+ .name = "arm-smmu",
+ .dev_dma_configure = arm_smmu_dma_configure,
+ .dev_count_resources = arm_smmu_count_resources,
+ .dev_init_resources = arm_smmu_init_resources,
+};
+
+static const struct iort_dev_config iort_arm_smmu_v3_pmcg_cfg __initconst = {
+ .name = "arm-smmu-v3-pmcg",
+ .dev_count_resources = arm_smmu_v3_pmcg_count_resources,
+ .dev_init_resources = arm_smmu_v3_pmcg_init_resources,
+ .dev_add_platdata = arm_smmu_v3_pmcg_add_platdata,
+};
+
+static __init const struct iort_dev_config *iort_get_dev_cfg(
+ struct acpi_iort_node *node)
+{
+ switch (node->type) {
+ case ACPI_IORT_NODE_SMMU_V3:
+ return &iort_arm_smmu_v3_cfg;
+ case ACPI_IORT_NODE_SMMU:
+ return &iort_arm_smmu_cfg;
+ case ACPI_IORT_NODE_PMCG:
+ return &iort_arm_smmu_v3_pmcg_cfg;
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * iort_add_platform_device() - Allocate a platform device for IORT node
+ * @node: Pointer to device ACPI IORT node
+ * @ops: Pointer to IORT device config struct
+ *
+ * Returns: 0 on success, <0 failure
+ */
+static int __init iort_add_platform_device(struct acpi_iort_node *node,
+ const struct iort_dev_config *ops)
+{
+ struct fwnode_handle *fwnode;
+ struct platform_device *pdev;
+ struct resource *r;
+ int ret, count;
+
+ pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
+ if (!pdev)
+ return -ENOMEM;
+
+ if (ops->dev_set_proximity) {
+ ret = ops->dev_set_proximity(&pdev->dev, node);
+ if (ret)
+ goto dev_put;
+ }
+
+ count = ops->dev_count_resources(node);
+
+ r = kcalloc(count, sizeof(*r), GFP_KERNEL);
+ if (!r) {
+ ret = -ENOMEM;
+ goto dev_put;
+ }
+
+ ops->dev_init_resources(r, node);
+
+ ret = platform_device_add_resources(pdev, r, count);
+ /*
+ * Resources are duplicated in platform_device_add_resources,
+ * free their allocated memory
+ */
+ kfree(r);
+
+ if (ret)
+ goto dev_put;
+
+ /*
+ * Platform devices based on PMCG nodes uses platform_data to
+ * pass the hardware model info to the driver. For others, add
+ * a copy of IORT node pointer to platform_data to be used to
+ * retrieve IORT data information.
+ */
+ if (ops->dev_add_platdata)
+ ret = ops->dev_add_platdata(pdev);
+ else
+ ret = platform_device_add_data(pdev, &node, sizeof(node));
+
+ if (ret)
+ goto dev_put;
+
+ fwnode = iort_get_fwnode(node);
+
+ if (!fwnode) {
+ ret = -ENODEV;
+ goto dev_put;
+ }
+
+ pdev->dev.fwnode = fwnode;
+
+ if (ops->dev_dma_configure)
+ ops->dev_dma_configure(&pdev->dev, node);
+
+ iort_set_device_domain(&pdev->dev, node);
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto dma_deconfigure;
+
+ return 0;
+
+dma_deconfigure:
+ arch_teardown_dma_ops(&pdev->dev);
+dev_put:
+ platform_device_put(pdev);
+
+ return ret;
+}
+
+#ifdef CONFIG_PCI
+static void __init iort_enable_acs(struct acpi_iort_node *iort_node)
+{
+ static bool acs_enabled __initdata;
+
+ if (acs_enabled)
+ return;
+
+ if (iort_node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
+ struct acpi_iort_node *parent;
+ struct acpi_iort_id_mapping *map;
+ int i;
+
+ map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, iort_node,
+ iort_node->mapping_offset);
+
+ for (i = 0; i < iort_node->mapping_count; i++, map++) {
+ if (!map->output_reference)
+ continue;
+
+ parent = ACPI_ADD_PTR(struct acpi_iort_node,
+ iort_table, map->output_reference);
+ /*
+ * If we detect a RC->SMMU mapping, make sure
+ * we enable ACS on the system.
+ */
+ if ((parent->type == ACPI_IORT_NODE_SMMU) ||
+ (parent->type == ACPI_IORT_NODE_SMMU_V3)) {
+ pci_request_acs();
+ acs_enabled = true;
+ return;
+ }
+ }
+ }
+}
+#else
+static inline void iort_enable_acs(struct acpi_iort_node *iort_node) { }
+#endif
+
+static void __init iort_init_platform_devices(void)
+{
+ struct acpi_iort_node *iort_node, *iort_end;
+ struct acpi_table_iort *iort;
+ struct fwnode_handle *fwnode;
+ int i, ret;
+ const struct iort_dev_config *ops;
+
+ /*
+ * iort_table and iort both point to the start of IORT table, but
+ * have different struct types
+ */
+ iort = (struct acpi_table_iort *)iort_table;
+
+ /* Get the first IORT node */
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+ iort->node_offset);
+ iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+ iort_table->length);
+
+ for (i = 0; i < iort->node_count; i++) {
+ if (iort_node >= iort_end) {
+ pr_err("iort node pointer overflows, bad table\n");
+ return;
+ }
+
+ iort_enable_acs(iort_node);
+
+ ops = iort_get_dev_cfg(iort_node);
+ if (ops) {
+ fwnode = acpi_alloc_fwnode_static();
+ if (!fwnode)
+ return;
+
+ iort_set_fwnode(iort_node, fwnode);
+
+ ret = iort_add_platform_device(iort_node, ops);
+ if (ret) {
+ iort_delete_fwnode(iort_node);
+ acpi_free_fwnode_static(fwnode);
+ return;
+ }
+ }
+
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+ iort_node->length);
+ }
+}
+
+void __init acpi_iort_init(void)
+{
+ acpi_status status;
+
+ /* iort_table will be used at runtime after the iort init,
+ * so we don't need to call acpi_put_table() to release
+ * the IORT table mapping.
+ */
+ status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_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;
+ }
+
+ iort_init_platform_devices();
+}
+
+#ifdef CONFIG_ZONE_DMA
+/*
+ * Extract the highest CPU physical address accessible to all DMA masters in
+ * the system. PHYS_ADDR_MAX is returned when no constrained device is found.
+ */
+phys_addr_t __init acpi_iort_dma_get_max_cpu_address(void)
+{
+ phys_addr_t limit = PHYS_ADDR_MAX;
+ struct acpi_iort_node *node, *end;
+ struct acpi_table_iort *iort;
+ acpi_status status;
+ int i;
+
+ if (acpi_disabled)
+ return limit;
+
+ status = acpi_get_table(ACPI_SIG_IORT, 0,
+ (struct acpi_table_header **)&iort);
+ if (ACPI_FAILURE(status))
+ return limit;
+
+ node = ACPI_ADD_PTR(struct acpi_iort_node, iort, iort->node_offset);
+ end = ACPI_ADD_PTR(struct acpi_iort_node, iort, iort->header.length);
+
+ for (i = 0; i < iort->node_count; i++) {
+ if (node >= end)
+ break;
+
+ switch (node->type) {
+ struct acpi_iort_named_component *ncomp;
+ struct acpi_iort_root_complex *rc;
+ phys_addr_t local_limit;
+
+ case ACPI_IORT_NODE_NAMED_COMPONENT:
+ ncomp = (struct acpi_iort_named_component *)node->node_data;
+ local_limit = DMA_BIT_MASK(ncomp->memory_address_limit);
+ limit = min_not_zero(limit, local_limit);
+ break;
+
+ case ACPI_IORT_NODE_PCI_ROOT_COMPLEX:
+ if (node->revision < 1)
+ break;
+
+ rc = (struct acpi_iort_root_complex *)node->node_data;
+ local_limit = DMA_BIT_MASK(rc->memory_address_limit);
+ limit = min_not_zero(limit, local_limit);
+ break;
+ }
+ node = ACPI_ADD_PTR(struct acpi_iort_node, node, node->length);
+ }
+ acpi_put_table(&iort->header);
+ return limit;
+}
+#endif
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
new file mode 100644
index 000000000..084f156bd
--- /dev/null
+++ b/drivers/acpi/battery.c
@@ -0,0 +1,1295 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * battery.c - ACPI Battery Driver (Revision: 2.0)
+ *
+ * Copyright (C) 2007 Alexey Starikovskiy <astarikovskiy@suse.de>
+ * Copyright (C) 2004-2007 Vladimir Lebedev <vladimir.p.lebedev@intel.com>
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: battery: " fmt
+
+#include <linux/async.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/types.h>
+
+#include <asm/unaligned.h>
+
+#include <linux/acpi.h>
+#include <linux/power_supply.h>
+
+#include <acpi/battery.h>
+
+#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF
+#define ACPI_BATTERY_CAPACITY_VALID(capacity) \
+ ((capacity) != 0 && (capacity) != ACPI_BATTERY_VALUE_UNKNOWN)
+
+#define ACPI_BATTERY_DEVICE_NAME "Battery"
+
+/* 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
+
+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;
+static unsigned int cache_time = 1000;
+module_param(cache_time, uint, 0644);
+MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+
+static const struct acpi_device_id battery_device_ids[] = {
+ {"PNP0C0A", 0},
+
+ /* Microsoft Surface Go 3 */
+ {"MSHW0146", 0},
+
+ {"", 0},
+};
+
+MODULE_DEVICE_TABLE(acpi, battery_device_ids);
+
+enum {
+ ACPI_BATTERY_ALARM_PRESENT,
+ ACPI_BATTERY_XINFO_PRESENT,
+ ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
+ /* On Lenovo Thinkpad models from 2010 and 2011, the power unit
+ * switches between mWh and mAh depending on whether the system
+ * is running on battery or not. When mAh is the unit, most
+ * reported values are incorrect and need to be adjusted by
+ * 10000/design_voltage. Verified on x201, t410, t410s, and x220.
+ * Pre-2010 and 2012 models appear to always report in mWh and
+ * are thus unaffected (tested with t42, t61, t500, x200, x300,
+ * and x230). Also, in mid-2012 Lenovo issued a BIOS update for
+ * the 2011 models that fixes the issue (tested on x220 with a
+ * post-1.29 BIOS), but as of Nov. 2012, no such update is
+ * available for the 2010 models.
+ */
+ ACPI_BATTERY_QUIRK_THINKPAD_MAH,
+ /* for batteries reporting current capacity with design capacity
+ * on a full charge, but showing degradation in full charge cap.
+ */
+ ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE,
+};
+
+struct acpi_battery {
+ struct mutex lock;
+ struct mutex sysfs_lock;
+ struct power_supply *bat;
+ struct power_supply_desc bat_desc;
+ struct acpi_device *device;
+ struct notifier_block pm_nb;
+ struct list_head list;
+ unsigned long update_time;
+ int revision;
+ int rate_now;
+ int capacity_now;
+ int voltage_now;
+ int design_capacity;
+ int full_charge_capacity;
+ int technology;
+ int design_voltage;
+ int design_capacity_warning;
+ int design_capacity_low;
+ int cycle_count;
+ int measurement_accuracy;
+ int max_sampling_time;
+ int min_sampling_time;
+ int max_averaging_interval;
+ int min_averaging_interval;
+ 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];
+ int state;
+ int power_unit;
+ unsigned long flags;
+};
+
+#define to_acpi_battery(x) power_supply_get_drvdata(x)
+
+static inline int acpi_battery_present(struct acpi_battery *battery)
+{
+ return battery->device->status.battery_present;
+}
+
+static int acpi_battery_technology(struct acpi_battery *battery)
+{
+ if (!strcasecmp("NiCd", battery->type))
+ return POWER_SUPPLY_TECHNOLOGY_NiCd;
+ if (!strcasecmp("NiMH", battery->type))
+ return POWER_SUPPLY_TECHNOLOGY_NiMH;
+ if (!strcasecmp("LION", battery->type))
+ return POWER_SUPPLY_TECHNOLOGY_LION;
+ if (!strncasecmp("LI-ION", battery->type, 6))
+ return POWER_SUPPLY_TECHNOLOGY_LION;
+ if (!strcasecmp("LiP", battery->type))
+ return POWER_SUPPLY_TECHNOLOGY_LIPO;
+ return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+}
+
+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 */
+ if (battery->state != 0)
+ return 0;
+
+ /* battery not reporting charge */
+ if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN ||
+ battery->capacity_now == 0)
+ return 0;
+
+ /* good batteries update full_charge as the batteries degrade */
+ if (battery->full_charge_capacity == battery->capacity_now)
+ return 1;
+
+ /* fallback to using design values for broken batteries */
+ if (battery->design_capacity <= battery->capacity_now)
+ return 1;
+
+ /* we don't do any sort of metric based on percentages */
+ return 0;
+}
+
+static bool acpi_battery_is_degraded(struct acpi_battery *battery)
+{
+ return ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) &&
+ ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity) &&
+ battery->full_charge_capacity < battery->design_capacity;
+}
+
+static int acpi_battery_handle_discharging(struct acpi_battery *battery)
+{
+ /*
+ * Some devices wrongly report discharging if the battery's charge level
+ * was above the device's start charging threshold atm the AC adapter
+ * was plugged in and the device thus did not start a new charge cycle.
+ */
+ if ((battery_ac_is_broken || power_supply_is_system_supplied()) &&
+ battery->rate_now == 0)
+ return POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ return POWER_SUPPLY_STATUS_DISCHARGING;
+}
+
+static int acpi_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int full_capacity = ACPI_BATTERY_VALUE_UNKNOWN, ret = 0;
+ struct acpi_battery *battery = to_acpi_battery(psy);
+
+ if (acpi_battery_present(battery)) {
+ /* run battery update only if it is present */
+ acpi_battery_get_state(battery);
+ } else if (psp != POWER_SUPPLY_PROP_PRESENT)
+ return -ENODEV;
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (battery->state & ACPI_BATTERY_STATE_DISCHARGING)
+ val->intval = acpi_battery_handle_discharging(battery);
+ else if (battery->state & ACPI_BATTERY_STATE_CHARGING)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (acpi_battery_is_charged(battery))
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = acpi_battery_present(battery);
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = acpi_battery_technology(battery);
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ val->intval = battery->cycle_count;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->design_voltage * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->voltage_now * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ case POWER_SUPPLY_PROP_POWER_NOW:
+ if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->rate_now * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+ if (!ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity))
+ ret = -ENODEV;
+ else
+ val->intval = battery->design_capacity * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ case POWER_SUPPLY_PROP_ENERGY_FULL:
+ if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity))
+ ret = -ENODEV;
+ else
+ val->intval = battery->full_charge_capacity * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ case POWER_SUPPLY_PROP_ENERGY_NOW:
+ if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->capacity_now * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ if (ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity))
+ full_capacity = battery->full_charge_capacity;
+ else if (ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity))
+ full_capacity = battery->design_capacity;
+
+ if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN ||
+ full_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->capacity_now * 100/
+ full_capacity;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+ if (battery->state & ACPI_BATTERY_STATE_CRITICAL)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+ else if (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
+ (battery->capacity_now <= battery->alarm))
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+ else if (acpi_battery_is_charged(battery))
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+ else
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = battery->model_number;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = battery->oem_info;
+ break;
+ case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+ val->strval = battery->serial_number;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static enum power_supply_property charge_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_SERIAL_NUMBER,
+};
+
+static enum power_supply_property charge_battery_full_cap_broken_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_SERIAL_NUMBER,
+};
+
+static enum power_supply_property energy_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_POWER_NOW,
+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_FULL,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_SERIAL_NUMBER,
+};
+
+static enum power_supply_property energy_battery_full_cap_broken_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_POWER_NOW,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_SERIAL_NUMBER,
+};
+
+/* Battery Management */
+struct acpi_offsets {
+ size_t offset; /* offset inside struct acpi_sbs_battery */
+ u8 mode; /* int or string? */
+};
+
+static const struct acpi_offsets state_offsets[] = {
+ {offsetof(struct acpi_battery, state), 0},
+ {offsetof(struct acpi_battery, rate_now), 0},
+ {offsetof(struct acpi_battery, capacity_now), 0},
+ {offsetof(struct acpi_battery, voltage_now), 0},
+};
+
+static const struct acpi_offsets info_offsets[] = {
+ {offsetof(struct acpi_battery, power_unit), 0},
+ {offsetof(struct acpi_battery, design_capacity), 0},
+ {offsetof(struct acpi_battery, full_charge_capacity), 0},
+ {offsetof(struct acpi_battery, technology), 0},
+ {offsetof(struct acpi_battery, design_voltage), 0},
+ {offsetof(struct acpi_battery, design_capacity_warning), 0},
+ {offsetof(struct acpi_battery, design_capacity_low), 0},
+ {offsetof(struct acpi_battery, capacity_granularity_1), 0},
+ {offsetof(struct acpi_battery, capacity_granularity_2), 0},
+ {offsetof(struct acpi_battery, model_number), 1},
+ {offsetof(struct acpi_battery, serial_number), 1},
+ {offsetof(struct acpi_battery, type), 1},
+ {offsetof(struct acpi_battery, oem_info), 1},
+};
+
+static const struct acpi_offsets extended_info_offsets[] = {
+ {offsetof(struct acpi_battery, revision), 0},
+ {offsetof(struct acpi_battery, power_unit), 0},
+ {offsetof(struct acpi_battery, design_capacity), 0},
+ {offsetof(struct acpi_battery, full_charge_capacity), 0},
+ {offsetof(struct acpi_battery, technology), 0},
+ {offsetof(struct acpi_battery, design_voltage), 0},
+ {offsetof(struct acpi_battery, design_capacity_warning), 0},
+ {offsetof(struct acpi_battery, design_capacity_low), 0},
+ {offsetof(struct acpi_battery, cycle_count), 0},
+ {offsetof(struct acpi_battery, measurement_accuracy), 0},
+ {offsetof(struct acpi_battery, max_sampling_time), 0},
+ {offsetof(struct acpi_battery, min_sampling_time), 0},
+ {offsetof(struct acpi_battery, max_averaging_interval), 0},
+ {offsetof(struct acpi_battery, min_averaging_interval), 0},
+ {offsetof(struct acpi_battery, capacity_granularity_1), 0},
+ {offsetof(struct acpi_battery, capacity_granularity_2), 0},
+ {offsetof(struct acpi_battery, model_number), 1},
+ {offsetof(struct acpi_battery, serial_number), 1},
+ {offsetof(struct acpi_battery, type), 1},
+ {offsetof(struct acpi_battery, oem_info), 1},
+};
+
+static int extract_package(struct acpi_battery *battery,
+ union acpi_object *package,
+ const struct acpi_offsets *offsets, int num)
+{
+ int i;
+ union acpi_object *element;
+
+ if (package->type != ACPI_TYPE_PACKAGE)
+ return -EFAULT;
+ for (i = 0; i < num; ++i) {
+ if (package->package.count <= i)
+ return -EFAULT;
+ element = &package->package.elements[i];
+ if (offsets[i].mode) {
+ u8 *ptr = (u8 *)battery + offsets[i].offset;
+
+ if (element->type == ACPI_TYPE_STRING ||
+ element->type == ACPI_TYPE_BUFFER)
+ strscpy(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
+ *ptr = 0; /* don't have value */
+ } else {
+ int *x = (int *)((u8 *)battery + offsets[i].offset);
+ *x = (element->type == ACPI_TYPE_INTEGER) ?
+ element->integer.value : -1;
+ }
+ }
+ return 0;
+}
+
+static int acpi_battery_get_status(struct acpi_battery *battery)
+{
+ if (acpi_bus_get_status(battery->device)) {
+ acpi_handle_info(battery->device->handle,
+ "_STA evaluation failed\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+
+static int extract_battery_info(const int use_bix,
+ struct acpi_battery *battery,
+ const struct acpi_buffer *buffer)
+{
+ int result = -EFAULT;
+
+ if (use_bix && battery_bix_broken_package)
+ result = extract_package(battery, buffer->pointer,
+ extended_info_offsets + 1,
+ ARRAY_SIZE(extended_info_offsets) - 1);
+ else if (use_bix)
+ result = extract_package(battery, buffer->pointer,
+ extended_info_offsets,
+ ARRAY_SIZE(extended_info_offsets));
+ else
+ result = extract_package(battery, buffer->pointer,
+ info_offsets, ARRAY_SIZE(info_offsets));
+ if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
+ battery->full_charge_capacity = battery->design_capacity;
+ if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
+ battery->power_unit && battery->design_voltage) {
+ battery->design_capacity = battery->design_capacity *
+ 10000 / battery->design_voltage;
+ battery->full_charge_capacity = battery->full_charge_capacity *
+ 10000 / battery->design_voltage;
+ battery->design_capacity_warning =
+ battery->design_capacity_warning *
+ 10000 / battery->design_voltage;
+ /* Curiously, design_capacity_low, unlike the rest of them,
+ * is correct.
+ */
+ /* capacity_granularity_* equal 1 on the systems tested, so
+ * it's impossible to tell if they would need an adjustment
+ * or not if their values were higher.
+ */
+ }
+ if (test_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags) &&
+ battery->capacity_now > battery->full_charge_capacity)
+ battery->capacity_now = battery->full_charge_capacity;
+
+ return result;
+}
+
+static int acpi_battery_get_info(struct acpi_battery *battery)
+{
+ const int xinfo = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
+ int use_bix;
+ int result = -ENODEV;
+
+ if (!acpi_battery_present(battery))
+ return 0;
+
+
+ for (use_bix = xinfo ? 1 : 0; use_bix >= 0; use_bix--) {
+ 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,
+ "%s evaluation failed: %s\n",
+ use_bix ? "_BIX":"_BIF",
+ acpi_format_exception(status));
+ } else {
+ result = extract_battery_info(use_bix,
+ battery,
+ &buffer);
+
+ kfree(buffer.pointer);
+ break;
+ }
+ }
+
+ if (!result && !use_bix && xinfo)
+ pr_warn(FW_BUG "The _BIX method is broken, using _BIF.\n");
+
+ return result;
+}
+
+static int acpi_battery_get_state(struct acpi_battery *battery)
+{
+ int result = 0;
+ acpi_status status = 0;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ if (!acpi_battery_present(battery))
+ return 0;
+
+ if (battery->update_time &&
+ time_before(jiffies, battery->update_time +
+ 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",
+ acpi_format_exception(status));
+ return -ENODEV;
+ }
+
+ result = extract_package(battery, buffer.pointer,
+ state_offsets, ARRAY_SIZE(state_offsets));
+ battery->update_time = jiffies;
+ kfree(buffer.pointer);
+
+ /* For buggy DSDTs that report negative 16-bit values for either
+ * charging or discharging current and/or report 0 as 65536
+ * due to bad math.
+ */
+ if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA &&
+ battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
+ (s16)(battery->rate_now) < 0) {
+ battery->rate_now = abs((s16)battery->rate_now);
+ pr_warn_once(FW_BUG "(dis)charge rate invalid.\n");
+ }
+
+ if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
+ && battery->capacity_now >= 0 && battery->capacity_now <= 100)
+ battery->capacity_now = (battery->capacity_now *
+ battery->full_charge_capacity) / 100;
+ if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
+ battery->power_unit && battery->design_voltage) {
+ battery->capacity_now = battery->capacity_now *
+ 10000 / battery->design_voltage;
+ }
+ if (test_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags) &&
+ battery->capacity_now > battery->full_charge_capacity)
+ battery->capacity_now = battery->full_charge_capacity;
+
+ return result;
+}
+
+static int acpi_battery_set_alarm(struct acpi_battery *battery)
+{
+ acpi_status status = 0;
+
+ if (!acpi_battery_present(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;
+
+ acpi_handle_debug(battery->device->handle, "Alarm set to %d\n",
+ battery->alarm);
+
+ return 0;
+}
+
+static int acpi_battery_init_alarm(struct acpi_battery *battery)
+{
+ /* See if alarms are supported, and if so, set default */
+ if (!acpi_has_method(battery->device->handle, "_BTP")) {
+ clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
+ return 0;
+ }
+ set_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
+ if (!battery->alarm)
+ battery->alarm = battery->design_capacity_warning;
+ return acpi_battery_set_alarm(battery);
+}
+
+static ssize_t acpi_battery_alarm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
+
+ return sprintf(buf, "%d\n", battery->alarm * 1000);
+}
+
+static ssize_t acpi_battery_alarm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long x;
+ struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
+
+ if (sscanf(buf, "%lu\n", &x) == 1)
+ battery->alarm = x/1000;
+ if (acpi_battery_present(battery))
+ acpi_battery_set_alarm(battery);
+ return count;
+}
+
+static const struct device_attribute alarm_attr = {
+ .attr = {.name = "alarm", .mode = 0644},
+ .show = acpi_battery_alarm_show,
+ .store = acpi_battery_alarm_store,
+};
+
+/*
+ * The Battery Hooking API
+ *
+ * This API is used inside other drivers that need to expose
+ * platform-specific behaviour within the generic driver in a
+ * generic way.
+ *
+ */
+
+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)
+{
+ 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);
+ }
+ list_del(&hook->list);
+ if (lock)
+ mutex_unlock(&hook_mutex);
+ pr_info("extension unregistered: %s\n", hook->name);
+}
+
+void battery_hook_unregister(struct acpi_battery_hook *hook)
+{
+ __battery_hook_unregister(hook, 1);
+}
+EXPORT_SYMBOL_GPL(battery_hook_unregister);
+
+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
+ * to notify the hook that a battery is available
+ * for each battery, so that the driver may add
+ * its attributes.
+ */
+ list_for_each_entry(battery, &acpi_battery_list, list) {
+ if (hook->add_battery(battery->bat)) {
+ /*
+ * If a add-battery returns non-zero,
+ * the registration of the extension 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);
+ goto end;
+ }
+ }
+ pr_info("new extension: %s\n", hook->name);
+end:
+ mutex_unlock(&hook_mutex);
+}
+EXPORT_SYMBOL_GPL(battery_hook_register);
+
+/*
+ * This function gets called right after the battery sysfs
+ * attributes have been added, so that the drivers that
+ * define custom sysfs attributes can add their own.
+ */
+static void battery_hook_add_battery(struct acpi_battery *battery)
+{
+ struct acpi_battery_hook *hook_node, *tmp;
+
+ mutex_lock(&hook_mutex);
+ INIT_LIST_HEAD(&battery->list);
+ list_add(&battery->list, &acpi_battery_list);
+ /*
+ * Since we added a new battery to the list, we need to
+ * iterate over the hooks and call add_battery for each
+ * hook that was registered. This usually happens
+ * when a battery gets hotplugged or initialized
+ * during the battery module initialization.
+ */
+ list_for_each_entry_safe(hook_node, tmp, &battery_hook_list, list) {
+ if (hook_node->add_battery(battery->bat)) {
+ /*
+ * The notification of the extensions has failed, to
+ * prevent further errors we will unload the extension.
+ */
+ pr_err("error in extension, unloading: %s",
+ hook_node->name);
+ __battery_hook_unregister(hook_node, 0);
+ }
+ }
+ mutex_unlock(&hook_mutex);
+}
+
+static void battery_hook_remove_battery(struct acpi_battery *battery)
+{
+ struct acpi_battery_hook *hook;
+
+ mutex_lock(&hook_mutex);
+ /*
+ * Before removing the hook, we need to remove all
+ * custom attributes from the battery.
+ */
+ list_for_each_entry(hook, &battery_hook_list, list) {
+ hook->remove_battery(battery->bat);
+ }
+ /* Then, just remove the battery from the list */
+ list_del(&battery->list);
+ mutex_unlock(&hook_mutex);
+}
+
+static void __exit battery_hook_exit(void)
+{
+ struct acpi_battery_hook *hook;
+ struct acpi_battery_hook *ptr;
+ /*
+ * At this point, the acpi_bus_unregister_driver()
+ * has called remove for all batteries. We just
+ * need to remove the hooks.
+ */
+ list_for_each_entry_safe(hook, ptr, &battery_hook_list, list) {
+ __battery_hook_unregister(hook, 1);
+ }
+ mutex_destroy(&hook_mutex);
+}
+
+static int sysfs_add_battery(struct acpi_battery *battery)
+{
+ struct power_supply_config psy_cfg = { .drv_data = battery, };
+ bool full_cap_broken = false;
+
+ if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) &&
+ !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity))
+ full_cap_broken = true;
+
+ if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
+ if (full_cap_broken) {
+ battery->bat_desc.properties =
+ charge_battery_full_cap_broken_props;
+ battery->bat_desc.num_properties =
+ ARRAY_SIZE(charge_battery_full_cap_broken_props);
+ } else {
+ battery->bat_desc.properties = charge_battery_props;
+ battery->bat_desc.num_properties =
+ ARRAY_SIZE(charge_battery_props);
+ }
+ } else {
+ if (full_cap_broken) {
+ battery->bat_desc.properties =
+ energy_battery_full_cap_broken_props;
+ battery->bat_desc.num_properties =
+ ARRAY_SIZE(energy_battery_full_cap_broken_props);
+ } else {
+ battery->bat_desc.properties = energy_battery_props;
+ battery->bat_desc.num_properties =
+ ARRAY_SIZE(energy_battery_props);
+ }
+ }
+
+ battery->bat_desc.name = acpi_device_bid(battery->device);
+ 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_desc, &psy_cfg);
+
+ if (IS_ERR(battery->bat)) {
+ int result = PTR_ERR(battery->bat);
+
+ battery->bat = NULL;
+ return result;
+ }
+ battery_hook_add_battery(battery);
+ return device_create_file(&battery->bat->dev, &alarm_attr);
+}
+
+static void sysfs_remove_battery(struct acpi_battery *battery)
+{
+ mutex_lock(&battery->sysfs_lock);
+ if (!battery->bat) {
+ mutex_unlock(&battery->sysfs_lock);
+ 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)
+{
+ struct acpi_battery *battery = (struct acpi_battery *)private;
+ /* Note: the hardcoded offsets below have been extracted from
+ * the source code of dmidecode.
+ */
+ if (dm->type == DMI_ENTRY_PORTABLE_BATTERY && dm->length >= 8) {
+ const u8 *dmi_data = (const u8 *)(dm + 1);
+ int dmi_capacity = get_unaligned((const u16 *)(dmi_data + 6));
+
+ if (dm->length >= 18)
+ dmi_capacity *= dmi_data[17];
+ if (battery->design_capacity * battery->design_voltage / 1000
+ != dmi_capacity &&
+ battery->design_capacity * 10 == dmi_capacity)
+ set_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
+ &battery->flags);
+ }
+}
+
+/*
+ * According to the ACPI spec, some kinds of primary batteries can
+ * report percentage battery remaining capacity directly to OS.
+ * In this case, it reports the Last Full Charged Capacity == 100
+ * and BatteryPresentRate == 0xFFFFFFFF.
+ *
+ * Now we found some battery reports percentage remaining capacity
+ * even if it's rechargeable.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=15979
+ *
+ * Handle this correctly so that they won't break userspace.
+ */
+static void acpi_battery_quirks(struct acpi_battery *battery)
+{
+ if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
+ return;
+
+ if (battery->full_charge_capacity == 100 &&
+ battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN &&
+ battery->capacity_now >= 0 && battery->capacity_now <= 100) {
+ set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags);
+ battery->full_charge_capacity = battery->design_capacity;
+ battery->capacity_now = (battery->capacity_now *
+ battery->full_charge_capacity) / 100;
+ }
+
+ if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags))
+ return;
+
+ if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
+ const char *s;
+
+ s = dmi_get_system_info(DMI_PRODUCT_VERSION);
+ if (s && !strncasecmp(s, "ThinkPad", 8)) {
+ dmi_walk(find_battery, battery);
+ if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
+ &battery->flags) &&
+ battery->design_voltage) {
+ battery->design_capacity =
+ battery->design_capacity *
+ 10000 / battery->design_voltage;
+ battery->full_charge_capacity =
+ battery->full_charge_capacity *
+ 10000 / battery->design_voltage;
+ battery->design_capacity_warning =
+ battery->design_capacity_warning *
+ 10000 / battery->design_voltage;
+ battery->capacity_now = battery->capacity_now *
+ 10000 / battery->design_voltage;
+ }
+ }
+ }
+
+ if (test_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags))
+ return;
+
+ if (acpi_battery_is_degraded(battery) &&
+ battery->capacity_now > battery->full_charge_capacity) {
+ set_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags);
+ battery->capacity_now = battery->full_charge_capacity;
+ }
+}
+
+static int acpi_battery_update(struct acpi_battery *battery, bool resume)
+{
+ int result = acpi_battery_get_status(battery);
+
+ if (result)
+ return result;
+
+ if (!acpi_battery_present(battery)) {
+ sysfs_remove_battery(battery);
+ battery->update_time = 0;
+ return 0;
+ }
+
+ if (resume)
+ return 0;
+
+ if (!battery->update_time) {
+ result = acpi_battery_get_info(battery);
+ if (result)
+ return result;
+ acpi_battery_init_alarm(battery);
+ }
+
+ result = acpi_battery_get_state(battery);
+ if (result)
+ return result;
+ acpi_battery_quirks(battery);
+
+ if (!battery->bat) {
+ result = sysfs_add_battery(battery);
+ if (result)
+ return result;
+ }
+
+ /*
+ * Wakeup the system if battery is critical low
+ * or lower than the alarm level
+ */
+ if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
+ (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
+ (battery->capacity_now <= battery->alarm)))
+ acpi_pm_wakeup_event(&battery->device->dev);
+
+ return result;
+}
+
+static void acpi_battery_refresh(struct acpi_battery *battery)
+{
+ int power_unit;
+
+ if (!battery->bat)
+ return;
+
+ power_unit = battery->power_unit;
+
+ acpi_battery_get_info(battery);
+
+ if (power_unit == battery->power_unit)
+ return;
+
+ /* The battery has changed its reporting units. */
+ sysfs_remove_battery(battery);
+ sysfs_add_battery(battery);
+}
+
+/* Driver Interface */
+static void acpi_battery_notify(struct acpi_device *device, u32 event)
+{
+ struct acpi_battery *battery = acpi_driver_data(device);
+ struct power_supply *old;
+
+ if (!battery)
+ return;
+ old = battery->bat;
+ /*
+ * On Acer Aspire V5-573G notifications are sometimes triggered too
+ * early. For example, when AC is unplugged and notification is
+ * triggered, battery state is still reported as "Full", and changes to
+ * "Discharging" only after short delay, without any notification.
+ */
+ if (battery_notification_delay_ms > 0)
+ msleep(battery_notification_delay_ms);
+ if (event == ACPI_BATTERY_NOTIFY_INFO)
+ acpi_battery_refresh(battery);
+ acpi_battery_update(battery, false);
+ acpi_bus_generate_netlink_event(device->pnp.device_class,
+ dev_name(&device->dev), event,
+ acpi_battery_present(battery));
+ acpi_notifier_call_chain(device, event, acpi_battery_present(battery));
+ /* acpi_battery_update could remove power_supply object */
+ if (old && battery->bat)
+ power_supply_changed(battery->bat);
+}
+
+static int battery_notify(struct notifier_block *nb,
+ 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 (!acpi_battery_present(battery))
+ return 0;
+
+ if (battery->bat) {
+ acpi_battery_refresh(battery);
+ } else {
+ result = acpi_battery_get_info(battery);
+ if (result)
+ return result;
+
+ result = sysfs_add_battery(battery);
+ if (result)
+ return result;
+ }
+
+ acpi_battery_init_alarm(battery);
+ acpi_battery_get_state(battery);
+ break;
+ }
+
+ return 0;
+}
+
+static int __init
+battery_bix_broken_package_quirk(const struct dmi_system_id *d)
+{
+ battery_bix_broken_package = 1;
+ return 0;
+}
+
+static int __init
+battery_notification_delay_quirk(const struct dmi_system_id *d)
+{
+ battery_notification_delay_ms = 1000;
+ return 0;
+}
+
+static int __init
+battery_ac_is_broken_quirk(const struct dmi_system_id *d)
+{
+ battery_ac_is_broken = 1;
+ return 0;
+}
+
+static const struct dmi_system_id bat_dmi_table[] __initconst = {
+ {
+ /* NEC LZ750/LS */
+ .callback = battery_bix_broken_package_quirk,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "NEC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "PC-LZ750LS"),
+ },
+ },
+ {
+ /* Acer Aspire V5-573G */
+ .callback = battery_notification_delay_quirk,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-573G"),
+ },
+ },
+ {
+ /* Point of View mobii wintab p800w */
+ .callback = battery_ac_is_broken_quirk,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ DMI_MATCH(DMI_BIOS_VERSION, "3BAIR1013"),
+ /* Above matches are too generic, add bios-date match */
+ DMI_MATCH(DMI_BIOS_DATE, "08/22/2014"),
+ },
+ },
+ {
+ /* Microsoft Surface Go 3 */
+ .callback = battery_notification_delay_quirk,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"),
+ },
+ },
+ {},
+};
+
+/*
+ * Some machines'(E,G Lenovo Z480) ECs are not stable
+ * during boot up and this causes battery driver fails to be
+ * probed due to failure of getting battery information
+ * from EC sometimes. After several retries, the operation
+ * may work. So add retry code here and 20ms sleep between
+ * every retries.
+ */
+static int acpi_battery_update_retry(struct acpi_battery *battery)
+{
+ int retry, ret;
+
+ for (retry = 5; retry; retry--) {
+ ret = acpi_battery_update(battery, false);
+ if (!ret)
+ break;
+
+ msleep(20);
+ }
+ return ret;
+}
+
+static int acpi_battery_add(struct acpi_device *device)
+{
+ int result = 0;
+ struct acpi_battery *battery = NULL;
+
+ if (!device)
+ return -EINVAL;
+
+ if (device->dep_unmet)
+ return -EPROBE_DEFER;
+
+ battery = kzalloc(sizeof(struct acpi_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);
+ device->driver_data = battery;
+ mutex_init(&battery->lock);
+ mutex_init(&battery->sysfs_lock);
+ if (acpi_has_method(battery->device->handle, "_BIX"))
+ set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
+
+ result = acpi_battery_update_retry(battery);
+ if (result)
+ goto fail;
+
+ pr_info("Slot [%s] (battery %s)\n", acpi_device_bid(device),
+ device->status.battery_present ? "present" : "absent");
+
+ battery->pm_nb.notifier_call = battery_notify;
+ register_pm_notifier(&battery->pm_nb);
+
+ device_init_wakeup(&device->dev, 1);
+
+ return result;
+
+fail:
+ sysfs_remove_battery(battery);
+ mutex_destroy(&battery->lock);
+ mutex_destroy(&battery->sysfs_lock);
+ kfree(battery);
+ return result;
+}
+
+static int acpi_battery_remove(struct acpi_device *device)
+{
+ struct acpi_battery *battery = NULL;
+
+ if (!device || !acpi_driver_data(device))
+ return -EINVAL;
+ device_init_wakeup(&device->dev, 0);
+ battery = acpi_driver_data(device);
+ unregister_pm_notifier(&battery->pm_nb);
+ 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)
+{
+ struct acpi_battery *battery;
+
+ if (!dev)
+ return -EINVAL;
+
+ battery = acpi_driver_data(to_acpi_device(dev));
+ if (!battery)
+ return -EINVAL;
+
+ battery->update_time = 0;
+ 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 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,
+};
+
+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)
+ return -ENODEV;
+
+ async_cookie = async_schedule(acpi_battery_init_async, NULL);
+ return 0;
+}
+
+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();
+ }
+}
+
+module_init(acpi_battery_init);
+module_exit(acpi_battery_exit);
diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c
new file mode 100644
index 000000000..e4fb9e225
--- /dev/null
+++ b/drivers/acpi/bgrt.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * BGRT boot graphic support
+ * Authors: Matthew Garrett, Josh Triplett <josh@joshtriplett.org>
+ * Copyright 2012 Red Hat, Inc <mjg@redhat.com>
+ * Copyright 2012 Intel Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/efi-bgrt.h>
+
+static void *bgrt_image;
+static struct kobject *bgrt_kobj;
+
+#define BGRT_SHOW(_name, _member) \
+ static ssize_t _name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+ { \
+ return sysfs_emit(buf, "%d\n", bgrt_tab._member); \
+ } \
+ static struct kobj_attribute bgrt_attr_##_name = __ATTR_RO(_name)
+
+BGRT_SHOW(version, version);
+BGRT_SHOW(status, status);
+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 struct attribute *bgrt_attributes[] = {
+ &bgrt_attr_version.attr,
+ &bgrt_attr_status.attr,
+ &bgrt_attr_type.attr,
+ &bgrt_attr_xoffset.attr,
+ &bgrt_attr_yoffset.attr,
+ NULL,
+};
+
+static struct bin_attribute *bgrt_bin_attributes[] = {
+ &bin_attr_image,
+ NULL,
+};
+
+static const struct attribute_group bgrt_attribute_group = {
+ .attrs = bgrt_attributes,
+ .bin_attrs = bgrt_bin_attributes,
+};
+
+int __init acpi_parse_bgrt(struct acpi_table_header *table)
+{
+ efi_bgrt_init(table);
+ return 0;
+}
+
+static int __init bgrt_init(void)
+{
+ int ret;
+
+ if (!bgrt_tab.image_address)
+ return -ENODEV;
+
+ bgrt_image = memremap(bgrt_tab.image_address, bgrt_image_size,
+ MEMREMAP_WB);
+ if (!bgrt_image) {
+ pr_notice("Ignoring BGRT: failed to map image memory\n");
+ return -ENOMEM;
+ }
+
+ bin_attr_image.private = bgrt_image;
+ bin_attr_image.size = bgrt_image_size;
+
+ bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj);
+ if (!bgrt_kobj) {
+ ret = -EINVAL;
+ goto out_memmap;
+ }
+
+ ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group);
+ if (ret)
+ goto out_kobject;
+
+ return 0;
+
+out_kobject:
+ kobject_put(bgrt_kobj);
+out_memmap:
+ memunmap(bgrt_image);
+ return ret;
+}
+device_initcall(bgrt_init);
diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c
new file mode 100644
index 000000000..a558d24fb
--- /dev/null
+++ b/drivers/acpi/blacklist.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * blacklist.c
+ *
+ * Check to see if the given machine has a known bad ACPI BIOS
+ * or if the BIOS is too old.
+ * Check given machine against acpi_rev_dmi_table[].
+ *
+ * Copyright (C) 2004 Len Brown <len.brown@intel.com>
+ * Copyright (C) 2002 Andy Grover <andrew.grover@intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+
+#include "internal.h"
+
+#ifdef CONFIG_DMI
+static const struct dmi_system_id acpi_rev_dmi_table[] __initconst;
+#endif
+
+/*
+ * POLICY: If *anything* doesn't work, put it on the blacklist.
+ * If they are critical errors, mark it critical, and abort driver load.
+ */
+static struct acpi_platform_list acpi_blacklist[] __initdata = {
+ /* Compaq Presario 1700 */
+ {"PTLTD ", " DSDT ", 0x06040000, ACPI_SIG_DSDT, less_than_or_equal,
+ "Multiple problems", 1},
+ /* Sony FX120, FX140, FX150? */
+ {"SONY ", "U0 ", 0x20010313, ACPI_SIG_DSDT, less_than_or_equal,
+ "ACPI driver problem", 1},
+ /* Compaq Presario 800, Insyde BIOS */
+ {"INT440", "SYSFexxx", 0x00001001, ACPI_SIG_DSDT, less_than_or_equal,
+ "Does not use _REG to protect EC OpRegions", 1},
+ /* IBM 600E - _ADR should return 7, but it returns 1 */
+ {"IBM ", "TP600E ", 0x00000105, ACPI_SIG_DSDT, less_than_or_equal,
+ "Incorrect _ADR", 1},
+
+ { }
+};
+
+int __init acpi_blacklisted(void)
+{
+ int i;
+ int blacklisted = 0;
+
+ i = acpi_match_platform_list(acpi_blacklist);
+ if (i >= 0) {
+ pr_err("Vendor \"%6.6s\" System \"%8.8s\" Revision 0x%x has a known ACPI BIOS problem.\n",
+ acpi_blacklist[i].oem_id,
+ acpi_blacklist[i].oem_table_id,
+ acpi_blacklist[i].oem_revision);
+
+ pr_err("Reason: %s. This is a %s error\n",
+ acpi_blacklist[i].reason,
+ (acpi_blacklist[i].data ?
+ "non-recoverable" : "recoverable"));
+
+ blacklisted = acpi_blacklist[i].data;
+ }
+
+ (void)early_acpi_osi_init();
+#ifdef CONFIG_DMI
+ dmi_check_system(acpi_rev_dmi_table);
+#endif
+
+ return blacklisted;
+}
+#ifdef CONFIG_DMI
+#ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE
+static int __init dmi_enable_rev_override(const struct dmi_system_id *d)
+{
+ pr_notice("DMI detected: %s (force ACPI _REV to 5)\n", d->ident);
+ acpi_rev_override_setup(NULL);
+ return 0;
+}
+#endif
+
+static const struct dmi_system_id acpi_rev_dmi_table[] __initconst = {
+#ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE
+ /*
+ * DELL XPS 13 (2015) switches sound between HDA and I2S
+ * depending on the ACPI _REV callback. If userspace supports
+ * I2S sufficiently (or if you do not care about sound), you
+ * can safely disable this quirk.
+ */
+ {
+ .callback = dmi_enable_rev_override,
+ .ident = "DELL XPS 13 (2015)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343"),
+ },
+ },
+ {
+ .callback = dmi_enable_rev_override,
+ .ident = "DELL Precision 5520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Precision 5520"),
+ },
+ },
+ {
+ .callback = dmi_enable_rev_override,
+ .ident = "DELL Precision 3520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3520"),
+ },
+ },
+ /*
+ * Resolves a quirk with the Dell Latitude 3350 that
+ * causes the ethernet adapter to not function.
+ */
+ {
+ .callback = dmi_enable_rev_override,
+ .ident = "DELL Latitude 3350",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude 3350"),
+ },
+ },
+ {
+ .callback = dmi_enable_rev_override,
+ .ident = "DELL Inspiron 7537",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"),
+ },
+ },
+#endif
+ {}
+};
+
+#endif /* CONFIG_DMI */
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
new file mode 100644
index 000000000..a16b7de73
--- /dev/null
+++ b/drivers/acpi/bus.c
@@ -0,0 +1,1421 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * acpi_bus.c - ACPI Bus Driver ($Revision: 80 $)
+ *
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/proc_fs.h>
+#include <linux/acpi.h>
+#include <linux/slab.h>
+#include <linux/regulator/machine.h>
+#include <linux/workqueue.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
+#ifdef CONFIG_X86
+#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>
+#include <linux/suspend.h>
+#include <linux/prmt.h>
+
+#include "internal.h"
+
+struct acpi_device *acpi_root;
+struct proc_dir_entry *acpi_root_dir;
+EXPORT_SYMBOL(acpi_root_dir);
+
+#ifdef CONFIG_X86
+#ifdef CONFIG_ACPI_CUSTOM_DSDT
+static inline int set_copy_dsdt(const struct dmi_system_id *id)
+{
+ return 0;
+}
+#else
+static int set_copy_dsdt(const struct dmi_system_id *id)
+{
+ pr_notice("%s detected - force copy of DSDT to local memory\n", id->ident);
+ acpi_gbl_copy_dsdt_locally = 1;
+ return 0;
+}
+#endif
+
+static const struct dmi_system_id dsdt_dmi_table[] __initconst = {
+ /*
+ * Invoke DSDT corruption work-around on all Toshiba Satellite.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=14679
+ */
+ {
+ .callback = set_copy_dsdt,
+ .ident = "TOSHIBA Satellite",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"),
+ },
+ },
+ {}
+};
+#endif
+
+/* --------------------------------------------------------------------------
+ Device Management
+ -------------------------------------------------------------------------- */
+
+acpi_status acpi_bus_get_status_handle(acpi_handle handle,
+ unsigned long long *sta)
+{
+ acpi_status status;
+
+ status = acpi_evaluate_integer(handle, "_STA", NULL, sta);
+ if (ACPI_SUCCESS(status))
+ return AE_OK;
+
+ if (status == AE_NOT_FOUND) {
+ *sta = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
+ ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING;
+ return AE_OK;
+ }
+ return status;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_get_status_handle);
+
+int acpi_bus_get_status(struct acpi_device *device)
+{
+ acpi_status status;
+ unsigned long long sta;
+
+ if (acpi_device_override_status(device, &sta)) {
+ acpi_set_device_status(device, sta);
+ return 0;
+ }
+
+ /* Battery devices must have their deps met before calling _STA */
+ if (acpi_device_is_battery(device) && device->dep_unmet) {
+ acpi_set_device_status(device, 0);
+ return 0;
+ }
+
+ status = acpi_bus_get_status_handle(device->handle, &sta);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ acpi_set_device_status(device, sta);
+
+ if (device->status.functional && !device->status.present) {
+ pr_debug("Device [%s] status [%08x]: functional but not present\n",
+ device->pnp.bus_id, (u32)sta);
+ }
+
+ pr_debug("Device [%s] status [%08x]\n", device->pnp.bus_id, (u32)sta);
+ return 0;
+}
+EXPORT_SYMBOL(acpi_bus_get_status);
+
+void acpi_bus_private_data_handler(acpi_handle handle,
+ void *context)
+{
+ return;
+}
+EXPORT_SYMBOL(acpi_bus_private_data_handler);
+
+int acpi_bus_attach_private_data(acpi_handle handle, void *data)
+{
+ acpi_status status;
+
+ status = acpi_attach_data(handle,
+ acpi_bus_private_data_handler, data);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_debug(handle, "Error attaching device data\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_attach_private_data);
+
+int acpi_bus_get_private_data(acpi_handle handle, void **data)
+{
+ acpi_status status;
+
+ if (!data)
+ return -EINVAL;
+
+ status = acpi_get_data(handle, acpi_bus_private_data_handler, data);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_debug(handle, "No context for object\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_get_private_data);
+
+void acpi_bus_detach_private_data(acpi_handle handle)
+{
+ acpi_detach_data(handle, acpi_bus_private_data_handler);
+}
+EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data);
+
+static void acpi_print_osc_error(acpi_handle handle,
+ struct acpi_osc_context *context, char *error)
+{
+ int i;
+
+ acpi_handle_debug(handle, "(%s): %s\n", context->uuid_str, error);
+
+ pr_debug("_OSC request data:");
+ for (i = 0; i < context->cap.length; i += sizeof(u32))
+ pr_debug(" %x", *((u32 *)(context->cap.pointer + i)));
+
+ pr_debug("\n");
+}
+
+acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context)
+{
+ acpi_status status;
+ struct acpi_object_list input;
+ union acpi_object in_params[4];
+ union acpi_object *out_obj;
+ guid_t guid;
+ u32 errors;
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+
+ if (!context)
+ return AE_ERROR;
+ if (guid_parse(context->uuid_str, &guid))
+ return AE_ERROR;
+ context->ret.length = ACPI_ALLOCATE_BUFFER;
+ context->ret.pointer = NULL;
+
+ /* Setting up input parameters */
+ input.count = 4;
+ input.pointer = in_params;
+ in_params[0].type = ACPI_TYPE_BUFFER;
+ in_params[0].buffer.length = 16;
+ in_params[0].buffer.pointer = (u8 *)&guid;
+ in_params[1].type = ACPI_TYPE_INTEGER;
+ in_params[1].integer.value = context->rev;
+ in_params[2].type = ACPI_TYPE_INTEGER;
+ in_params[2].integer.value = context->cap.length/sizeof(u32);
+ in_params[3].type = ACPI_TYPE_BUFFER;
+ in_params[3].buffer.length = context->cap.length;
+ in_params[3].buffer.pointer = context->cap.pointer;
+
+ status = acpi_evaluate_object(handle, "_OSC", &input, &output);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ if (!output.length)
+ return AE_NULL_OBJECT;
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_BUFFER
+ || out_obj->buffer.length != context->cap.length) {
+ acpi_print_osc_error(handle, context,
+ "_OSC evaluation returned wrong type");
+ status = AE_TYPE;
+ goto out_kfree;
+ }
+ /* Need to ignore the bit0 in result code */
+ errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
+ if (errors) {
+ if (errors & OSC_REQUEST_ERROR)
+ acpi_print_osc_error(handle, context,
+ "_OSC request failed");
+ if (errors & OSC_INVALID_UUID_ERROR)
+ acpi_print_osc_error(handle, context,
+ "_OSC invalid UUID");
+ if (errors & OSC_INVALID_REVISION_ERROR)
+ acpi_print_osc_error(handle, context,
+ "_OSC invalid revision");
+ if (errors & OSC_CAPABILITIES_MASK_ERROR) {
+ if (((u32 *)context->cap.pointer)[OSC_QUERY_DWORD]
+ & OSC_QUERY_ENABLE)
+ goto out_success;
+ status = AE_SUPPORT;
+ goto out_kfree;
+ }
+ status = AE_ERROR;
+ goto out_kfree;
+ }
+out_success:
+ context->ret.length = out_obj->buffer.length;
+ context->ret.pointer = kmemdup(out_obj->buffer.pointer,
+ context->ret.length, GFP_KERNEL);
+ if (!context->ret.pointer) {
+ status = AE_NO_MEMORY;
+ goto out_kfree;
+ }
+ status = AE_OK;
+
+out_kfree:
+ kfree(output.pointer);
+ return status;
+}
+EXPORT_SYMBOL(acpi_run_osc);
+
+bool osc_sb_apei_support_acked;
+
+/*
+ * ACPI 6.0 Section 8.4.4.2 Idle State Coordination
+ * OSPM supports platform coordinated low power idle(LPI) states
+ */
+bool osc_pc_lpi_support_confirmed;
+EXPORT_SYMBOL_GPL(osc_pc_lpi_support_confirmed);
+
+/*
+ * ACPI 6.2 Section 6.2.11.2 'Platform-Wide OSPM Capabilities':
+ * Starting with ACPI Specification 6.2, all _CPC registers can be in
+ * PCC, System Memory, System IO, or Functional Fixed Hardware address
+ * spaces. OSPM support for this more flexible register space scheme is
+ * indicated by the “Flexible Address Space for CPPC Registers” _OSC bit.
+ *
+ * Otherwise (cf ACPI 6.1, s8.4.7.1.1.X), _CPC registers must be in:
+ * - PCC or Functional Fixed Hardware address space if defined
+ * - SystemMemory address space (NULL register) if not defined
+ */
+bool osc_cpc_flexible_adr_space_confirmed;
+EXPORT_SYMBOL_GPL(osc_cpc_flexible_adr_space_confirmed);
+
+/*
+ * ACPI 6.4 Operating System Capabilities for USB.
+ */
+bool osc_sb_native_usb4_support_confirmed;
+EXPORT_SYMBOL_GPL(osc_sb_native_usb4_support_confirmed);
+
+bool osc_sb_cppc2_support_acked;
+
+static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48";
+static void acpi_bus_osc_negotiate_platform_control(void)
+{
+ u32 capbuf[2], *capbuf_ret;
+ struct acpi_osc_context context = {
+ .uuid_str = sb_uuid_str,
+ .rev = 1,
+ .cap.length = 8,
+ .cap.pointer = capbuf,
+ };
+ acpi_handle handle;
+
+ capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE;
+ capbuf[OSC_SUPPORT_DWORD] = OSC_SB_PR3_SUPPORT; /* _PR3 is in use */
+ if (IS_ENABLED(CONFIG_ACPI_PROCESSOR_AGGREGATOR))
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PAD_SUPPORT;
+ if (IS_ENABLED(CONFIG_ACPI_PROCESSOR))
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT;
+
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT;
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT;
+ if (IS_ENABLED(CONFIG_ACPI_PRMT))
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PRM_SUPPORT;
+
+#ifdef CONFIG_ARM64
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GENERIC_INITIATOR_SUPPORT;
+#endif
+#ifdef CONFIG_X86
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GENERIC_INITIATOR_SUPPORT;
+#endif
+
+#ifdef CONFIG_ACPI_CPPC_LIB
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_SUPPORT;
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPCV2_SUPPORT;
+#endif
+
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_FLEXIBLE_ADR_SPACE;
+
+ if (IS_ENABLED(CONFIG_SCHED_MC_PRIO))
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_DIVERSE_HIGH_SUPPORT;
+
+ if (IS_ENABLED(CONFIG_USB4))
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_NATIVE_USB4_SUPPORT;
+
+ if (!ghes_disable)
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT;
+ if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)))
+ return;
+
+ if (ACPI_FAILURE(acpi_run_osc(handle, &context)))
+ return;
+
+ capbuf_ret = context.ret.pointer;
+ if (context.ret.length <= OSC_SUPPORT_DWORD) {
+ kfree(context.ret.pointer);
+ return;
+ }
+
+ /*
+ * Now run _OSC again with query flag clear and with the caps
+ * supported by both the OS and the platform.
+ */
+ capbuf[OSC_QUERY_DWORD] = 0;
+ capbuf[OSC_SUPPORT_DWORD] = capbuf_ret[OSC_SUPPORT_DWORD];
+ kfree(context.ret.pointer);
+
+ if (ACPI_FAILURE(acpi_run_osc(handle, &context)))
+ return;
+
+ capbuf_ret = context.ret.pointer;
+ if (context.ret.length > OSC_SUPPORT_DWORD) {
+#ifdef CONFIG_ACPI_CPPC_LIB
+ osc_sb_cppc2_support_acked = capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_CPCV2_SUPPORT;
+#endif
+
+ osc_sb_apei_support_acked =
+ capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT;
+ osc_pc_lpi_support_confirmed =
+ capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT;
+ osc_sb_native_usb4_support_confirmed =
+ capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT;
+ osc_cpc_flexible_adr_space_confirmed =
+ capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_CPC_FLEXIBLE_ADR_SPACE;
+ }
+
+ kfree(context.ret.pointer);
+}
+
+/*
+ * Native control of USB4 capabilities. If any of the tunneling bits is
+ * set it means OS is in control and we use software based connection
+ * manager.
+ */
+u32 osc_sb_native_usb4_control;
+EXPORT_SYMBOL_GPL(osc_sb_native_usb4_control);
+
+static void acpi_bus_decode_usb_osc(const char *msg, u32 bits)
+{
+ pr_info("%s USB3%c DisplayPort%c PCIe%c XDomain%c\n", msg,
+ (bits & OSC_USB_USB3_TUNNELING) ? '+' : '-',
+ (bits & OSC_USB_DP_TUNNELING) ? '+' : '-',
+ (bits & OSC_USB_PCIE_TUNNELING) ? '+' : '-',
+ (bits & OSC_USB_XDOMAIN) ? '+' : '-');
+}
+
+static u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A";
+static void acpi_bus_osc_negotiate_usb_control(void)
+{
+ u32 capbuf[3];
+ struct acpi_osc_context context = {
+ .uuid_str = sb_usb_uuid_str,
+ .rev = 1,
+ .cap.length = sizeof(capbuf),
+ .cap.pointer = capbuf,
+ };
+ acpi_handle handle;
+ acpi_status status;
+ u32 control;
+
+ if (!osc_sb_native_usb4_support_confirmed)
+ return;
+
+ if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)))
+ return;
+
+ control = OSC_USB_USB3_TUNNELING | OSC_USB_DP_TUNNELING |
+ OSC_USB_PCIE_TUNNELING | OSC_USB_XDOMAIN;
+
+ capbuf[OSC_QUERY_DWORD] = 0;
+ capbuf[OSC_SUPPORT_DWORD] = 0;
+ capbuf[OSC_CONTROL_DWORD] = control;
+
+ 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);
+
+ acpi_bus_decode_usb_osc("USB4 _OSC: OS supports", control);
+ acpi_bus_decode_usb_osc("USB4 _OSC: OS controls",
+ osc_sb_native_usb4_control);
+
+out_free:
+ kfree(context.ret.pointer);
+}
+
+/* --------------------------------------------------------------------------
+ Notification Handling
+ -------------------------------------------------------------------------- */
+
+/**
+ * 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;
+
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
+ break;
+
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
+ break;
+
+ case ACPI_NOTIFY_DEVICE_WAKE:
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_WAKE event\n");
+ return;
+
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
+ break;
+
+ case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK_LIGHT event\n");
+ /* TBD: Exactly what does 'light' mean? */
+ return;
+
+ case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
+ return;
+
+ case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
+ return;
+
+ case ACPI_NOTIFY_POWER_FAULT:
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
+ return;
+
+ default:
+ acpi_handle_debug(handle, "Unknown event type 0x%x\n", type);
+ return;
+ }
+
+ adev = acpi_get_acpi_dev(handle);
+
+ if (adev && ACPI_SUCCESS(acpi_hotplug_schedule(adev, type)))
+ return;
+
+ acpi_put_acpi_dev(adev);
+
+ acpi_evaluate_ost(handle, type, ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
+}
+
+static void acpi_notify_device(acpi_handle handle, u32 event, void *data)
+{
+ struct acpi_device *device = data;
+ struct acpi_driver *acpi_drv = to_acpi_driver(device->dev.driver);
+
+ acpi_drv->ops.notify(device, event);
+}
+
+static void acpi_notify_device_fixed(void *data)
+{
+ struct acpi_device *device = data;
+
+ /* Fixed hardware devices have no handles */
+ acpi_notify_device(NULL, ACPI_FIXED_HARDWARE_EVENT, device);
+}
+
+static u32 acpi_device_fixed_event(void *data)
+{
+ acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_notify_device_fixed, data);
+ return ACPI_INTERRUPT_HANDLED;
+}
+
+static int acpi_device_install_notify_handler(struct acpi_device *device,
+ struct acpi_driver *acpi_drv)
+{
+ 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 {
+ u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ?
+ ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY;
+
+ status = acpi_install_notify_handler(device->handle, type,
+ acpi_notify_device,
+ device);
+ }
+
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ return 0;
+}
+
+static void acpi_device_remove_notify_handler(struct acpi_device *device,
+ struct acpi_driver *acpi_drv)
+{
+ 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 {
+ 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();
+}
+
+/* Handle events targeting \_SB device (at present only graceful shutdown) */
+
+#define ACPI_SB_NOTIFY_SHUTDOWN_REQUEST 0x81
+#define ACPI_SB_INDICATE_INTERVAL 10000
+
+static void sb_notify_work(struct work_struct *dummy)
+{
+ acpi_handle sb_handle;
+
+ orderly_poweroff(true);
+
+ /*
+ * After initiating graceful shutdown, the ACPI spec requires OSPM
+ * to evaluate _OST method once every 10seconds to indicate that
+ * the shutdown is in progress
+ */
+ acpi_get_handle(NULL, "\\_SB", &sb_handle);
+ while (1) {
+ pr_info("Graceful shutdown in progress.\n");
+ acpi_evaluate_ost(sb_handle, ACPI_OST_EC_OSPM_SHUTDOWN,
+ ACPI_OST_SC_OS_SHUTDOWN_IN_PROGRESS, NULL);
+ msleep(ACPI_SB_INDICATE_INTERVAL);
+ }
+}
+
+static void acpi_sb_notify(acpi_handle handle, u32 event, void *data)
+{
+ static DECLARE_WORK(acpi_sb_work, sb_notify_work);
+
+ if (event == ACPI_SB_NOTIFY_SHUTDOWN_REQUEST) {
+ if (!work_busy(&acpi_sb_work))
+ schedule_work(&acpi_sb_work);
+ } else
+ pr_warn("event %x is not supported by \\_SB device\n", event);
+}
+
+static int __init acpi_setup_sb_notify_handler(void)
+{
+ acpi_handle sb_handle;
+
+ if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &sb_handle)))
+ return -ENXIO;
+
+ if (ACPI_FAILURE(acpi_install_notify_handler(sb_handle, ACPI_DEVICE_NOTIFY,
+ acpi_sb_notify, NULL)))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ Device Matching
+ -------------------------------------------------------------------------- */
+
+/**
+ * acpi_get_first_physical_node - Get first physical node of an ACPI device
+ * @adev: ACPI device in question
+ *
+ * Return: First physical node of ACPI device @adev
+ */
+struct device *acpi_get_first_physical_node(struct acpi_device *adev)
+{
+ struct mutex *physical_node_lock = &adev->physical_node_lock;
+ struct device *phys_dev;
+
+ mutex_lock(physical_node_lock);
+ if (list_empty(&adev->physical_node_list)) {
+ phys_dev = NULL;
+ } else {
+ const struct acpi_device_physical_node *node;
+
+ node = list_first_entry(&adev->physical_node_list,
+ struct acpi_device_physical_node, node);
+
+ phys_dev = node->dev;
+ }
+ mutex_unlock(physical_node_lock);
+ return phys_dev;
+}
+EXPORT_SYMBOL_GPL(acpi_get_first_physical_node);
+
+static struct acpi_device *acpi_primary_dev_companion(struct acpi_device *adev,
+ const struct device *dev)
+{
+ const struct device *phys_dev = acpi_get_first_physical_node(adev);
+
+ return phys_dev && phys_dev == dev ? adev : NULL;
+}
+
+/**
+ * acpi_device_is_first_physical_node - Is given dev first physical node
+ * @adev: ACPI companion device
+ * @dev: Physical device to check
+ *
+ * Function checks if given @dev is the first physical devices attached to
+ * the ACPI companion device. This distinction is needed in some cases
+ * where the same companion device is shared between many physical devices.
+ *
+ * Note that the caller have to provide valid @adev pointer.
+ */
+bool acpi_device_is_first_physical_node(struct acpi_device *adev,
+ const struct device *dev)
+{
+ return !!acpi_primary_dev_companion(adev, dev);
+}
+
+/*
+ * acpi_companion_match() - Can we match via ACPI companion device
+ * @dev: Device in question
+ *
+ * Check if the given device has an ACPI companion and if that companion has
+ * a valid list of PNP IDs, and if the device is the first (primary) physical
+ * device associated with it. Return the companion pointer if that's the case
+ * or NULL otherwise.
+ *
+ * If multiple physical devices are attached to a single ACPI companion, we need
+ * to be careful. The usage scenario for this kind of relationship is that all
+ * of the physical devices in question use resources provided by the ACPI
+ * companion. A typical case is an MFD device where all the sub-devices share
+ * the parent's ACPI companion. In such cases we can only allow the primary
+ * (first) physical device to be matched with the help of the companion's PNP
+ * IDs.
+ *
+ * Additional physical devices sharing the ACPI companion can still use
+ * 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)
+{
+ struct acpi_device *adev;
+
+ adev = ACPI_COMPANION(dev);
+ if (!adev)
+ return NULL;
+
+ if (list_empty(&adev->pnp.ids))
+ return NULL;
+
+ return acpi_primary_dev_companion(adev, dev);
+}
+
+/**
+ * acpi_of_match_device - Match device object using the "compatible" property.
+ * @adev: ACPI device object to match.
+ * @of_match_table: List of device IDs to match against.
+ * @of_id: OF ID if matched
+ *
+ * If @dev has an ACPI companion which has ACPI_DT_NAMESPACE_HID in its list of
+ * 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,
+ const struct of_device_id *of_match_table,
+ const struct of_device_id **of_id)
+{
+ const union acpi_object *of_compatible, *obj;
+ int i, nval;
+
+ if (!adev)
+ return false;
+
+ of_compatible = adev->data.of_compatible;
+ if (!of_match_table || !of_compatible)
+ return false;
+
+ if (of_compatible->type == ACPI_TYPE_PACKAGE) {
+ nval = of_compatible->package.count;
+ obj = of_compatible->package.elements;
+ } else { /* Must be ACPI_TYPE_STRING. */
+ nval = 1;
+ obj = of_compatible;
+ }
+ /* Now we can look for the driver DT compatible strings */
+ for (i = 0; i < nval; i++, obj++) {
+ const struct of_device_id *id;
+
+ for (id = of_match_table; id->compatible[0]; id++)
+ if (!strcasecmp(obj->string.pointer, id->compatible)) {
+ if (of_id)
+ *of_id = id;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool acpi_of_modalias(struct acpi_device *adev,
+ char *modalias, size_t len)
+{
+ const union acpi_object *of_compatible;
+ const union acpi_object *obj;
+ const char *str, *chr;
+
+ of_compatible = adev->data.of_compatible;
+ if (!of_compatible)
+ return false;
+
+ if (of_compatible->type == ACPI_TYPE_PACKAGE)
+ obj = of_compatible->package.elements;
+ else /* Must be ACPI_TYPE_STRING. */
+ obj = of_compatible;
+
+ str = obj->string.pointer;
+ chr = strchr(str, ',');
+ strscpy(modalias, chr ? chr + 1 : str, len);
+
+ return true;
+}
+
+/**
+ * acpi_set_modalias - Set modalias using "compatible" property or supplied ID
+ * @adev: ACPI device object to match
+ * @default_id: ID string to use as default if no compatible string found
+ * @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.
+ */
+void acpi_set_modalias(struct acpi_device *adev, const char *default_id,
+ char *modalias, size_t len)
+{
+ if (!acpi_of_modalias(adev, modalias, len))
+ strscpy(modalias, default_id, len);
+}
+EXPORT_SYMBOL_GPL(acpi_set_modalias);
+
+static bool __acpi_match_device_cls(const struct acpi_device_id *id,
+ struct acpi_hardware_id *hwid)
+{
+ int i, msk, byte_shift;
+ char buf[3];
+
+ if (!id->cls)
+ return false;
+
+ /* Apply class-code bitmask, before checking each class-code byte */
+ for (i = 1; i <= 3; i++) {
+ byte_shift = 8 * (3 - i);
+ msk = (id->cls_msk >> byte_shift) & 0xFF;
+ if (!msk)
+ continue;
+
+ sprintf(buf, "%02x", (id->cls >> byte_shift) & msk);
+ if (strncmp(buf, &hwid->id[(i - 1) * 2], 2))
+ return false;
+ }
+ return true;
+}
+
+static bool __acpi_match_device(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,
+ const struct of_device_id **of_id)
+{
+ const struct acpi_device_id *id;
+ struct acpi_hardware_id *hwid;
+
+ /*
+ * If the device is not present, it is unnecessary to load device
+ * driver for it.
+ */
+ if (!device || !device->status.present)
+ return false;
+
+ list_for_each_entry(hwid, &device->pnp.ids, list) {
+ /* First, check the ACPI/PNP IDs provided by the caller. */
+ if (acpi_ids) {
+ for (id = acpi_ids; id->id[0] || id->cls; id++) {
+ if (id->id[0] && !strcmp((char *)id->id, hwid->id))
+ goto out_acpi_match;
+ if (id->cls && __acpi_match_device_cls(id, hwid))
+ goto out_acpi_match;
+ }
+ }
+
+ /*
+ * Next, check ACPI_DT_NAMESPACE_HID and try to match the
+ * "compatible" property if found.
+ */
+ if (!strcmp(ACPI_DT_NAMESPACE_HID, hwid->id))
+ return acpi_of_match_device(device, of_ids, of_id);
+ }
+ return false;
+
+out_acpi_match:
+ if (acpi_id)
+ *acpi_id = id;
+ return true;
+}
+
+/**
+ * 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.
+ *
+ * Check if @dev has a valid ACPI handle and if there is a struct acpi_device
+ * object for that handle and use that object to match against a given list of
+ * device IDs.
+ *
+ * Return a pointer to the first matching ID on success or %NULL on failure.
+ */
+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;
+}
+EXPORT_SYMBOL_GPL(acpi_match_device);
+
+static const void *acpi_of_device_get_match_data(const struct device *dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ const struct of_device_id *match = NULL;
+
+ if (!acpi_of_match_device(adev, dev->driver->of_match_table, &match))
+ return NULL;
+
+ return match->data;
+}
+
+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 (!acpi_ids)
+ return acpi_of_device_get_match_data(dev);
+
+ match = acpi_match_device(acpi_ids, dev);
+ if (!match)
+ return NULL;
+
+ return (const void *)match->driver_data;
+}
+EXPORT_SYMBOL_GPL(acpi_device_get_match_data);
+
+int acpi_match_device_ids(struct acpi_device *device,
+ const struct acpi_device_id *ids)
+{
+ return __acpi_match_device(device, ids, NULL, NULL, NULL) ? 0 : -ENOENT;
+}
+EXPORT_SYMBOL(acpi_match_device_ids);
+
+bool acpi_driver_match_device(struct device *dev,
+ const struct device_driver *drv)
+{
+ 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);
+
+/* --------------------------------------------------------------------------
+ ACPI Driver Management
+ -------------------------------------------------------------------------- */
+
+/**
+ * acpi_bus_register_driver - register a driver with the ACPI bus
+ * @driver: driver being registered
+ *
+ * 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)
+{
+ if (acpi_disabled)
+ return -ENODEV;
+ driver->drv.name = driver->name;
+ driver->drv.bus = &acpi_bus_type;
+ driver->drv.owner = driver->owner;
+
+ return driver_register(&driver->drv);
+}
+
+EXPORT_SYMBOL(acpi_bus_register_driver);
+
+/**
+ * acpi_bus_unregister_driver - unregisters a driver with the ACPI bus
+ * @driver: driver to unregister
+ *
+ * Unregisters a driver with the ACPI bus. Searches the namespace for all
+ * devices that match the driver's criteria and unbinds.
+ */
+void acpi_bus_unregister_driver(struct acpi_driver *driver)
+{
+ driver_unregister(&driver->drv);
+}
+
+EXPORT_SYMBOL(acpi_bus_unregister_driver);
+
+/* --------------------------------------------------------------------------
+ ACPI Bus operations
+ -------------------------------------------------------------------------- */
+
+static int acpi_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ 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)
+{
+ return __acpi_device_uevent_modalias(to_acpi_device(dev), env);
+}
+
+static int acpi_device_probe(struct device *dev)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver);
+ int ret;
+
+ if (acpi_dev->handler && !acpi_is_pnp_device(acpi_dev))
+ return -EINVAL;
+
+ if (!acpi_drv->ops.add)
+ return -ENOSYS;
+
+ ret = acpi_drv->ops.add(acpi_dev);
+ if (ret)
+ 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, acpi_drv);
+ if (ret) {
+ if (acpi_drv->ops.remove)
+ acpi_drv->ops.remove(acpi_dev);
+
+ acpi_dev->driver_data = NULL;
+ return ret;
+ }
+ }
+
+ pr_debug("Found driver [%s] for device [%s]\n", acpi_drv->name,
+ acpi_dev->pnp.bus_id);
+
+ get_device(dev);
+ return 0;
+}
+
+static void acpi_device_remove(struct device *dev)
+{
+ struct acpi_device *acpi_dev = to_acpi_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_drv);
+
+ if (acpi_drv->ops.remove)
+ acpi_drv->ops.remove(acpi_dev);
+
+ acpi_dev->driver_data = NULL;
+
+ put_device(dev);
+}
+
+struct bus_type acpi_bus_type = {
+ .name = "acpi",
+ .match = acpi_bus_match,
+ .probe = acpi_device_probe,
+ .remove = acpi_device_remove,
+ .uevent = acpi_device_uevent,
+};
+
+int acpi_bus_for_each_dev(int (*fn)(struct device *, void *), void *data)
+{
+ return bus_for_each_dev(&acpi_bus_type, NULL, data, fn);
+}
+EXPORT_SYMBOL_GPL(acpi_bus_for_each_dev);
+
+struct acpi_dev_walk_context {
+ int (*fn)(struct acpi_device *, void *);
+ void *data;
+};
+
+static int acpi_dev_for_one_check(struct device *dev, void *context)
+{
+ struct acpi_dev_walk_context *adwc = context;
+
+ if (dev->bus != &acpi_bus_type)
+ return 0;
+
+ return adwc->fn(to_acpi_device(dev), adwc->data);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_for_each_child);
+
+int acpi_dev_for_each_child(struct acpi_device *adev,
+ int (*fn)(struct acpi_device *, void *), void *data)
+{
+ struct acpi_dev_walk_context adwc = {
+ .fn = fn,
+ .data = data,
+ };
+
+ return device_for_each_child(&adev->dev, &adwc, acpi_dev_for_one_check);
+}
+
+int acpi_dev_for_each_child_reverse(struct acpi_device *adev,
+ int (*fn)(struct acpi_device *, void *),
+ void *data)
+{
+ struct acpi_dev_walk_context adwc = {
+ .fn = fn,
+ .data = data,
+ };
+
+ return device_for_each_child_reverse(&adev->dev, &adwc, acpi_dev_for_one_check);
+}
+
+/* --------------------------------------------------------------------------
+ Initialization/Cleanup
+ -------------------------------------------------------------------------- */
+
+static int __init acpi_bus_init_irq(void)
+{
+ acpi_status status;
+ char *message = NULL;
+
+
+ /*
+ * Let the system know what interrupt model we are using by
+ * evaluating the \_PIC object, if exists.
+ */
+
+ switch (acpi_irq_model) {
+ case ACPI_IRQ_MODEL_PIC:
+ message = "PIC";
+ break;
+ case ACPI_IRQ_MODEL_IOAPIC:
+ message = "IOAPIC";
+ break;
+ case ACPI_IRQ_MODEL_IOSAPIC:
+ message = "IOSAPIC";
+ break;
+ case ACPI_IRQ_MODEL_GIC:
+ message = "GIC";
+ break;
+ case ACPI_IRQ_MODEL_PLATFORM:
+ message = "platform specific model";
+ break;
+ case ACPI_IRQ_MODEL_LPIC:
+ message = "LPIC";
+ break;
+ default:
+ pr_info("Unknown interrupt routing model\n");
+ return -ENODEV;
+ }
+
+ pr_info("Using %s for interrupt routing\n", message);
+
+ status = acpi_execute_simple_method(NULL, "\\_PIC", acpi_irq_model);
+ if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
+ pr_info("_PIC evaluation failed: %s\n", acpi_format_exception(status));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * acpi_early_init - Initialize ACPICA and populate the ACPI namespace.
+ *
+ * The ACPI tables are accessible after this, but the handling of events has not
+ * been initialized and the global lock is not available yet, so AML should not
+ * be executed at this point.
+ *
+ * Doing this before switching the EFI runtime services to virtual mode allows
+ * the EfiBootServices memory to be freed slightly earlier on boot.
+ */
+void __init acpi_early_init(void)
+{
+ acpi_status status;
+
+ if (acpi_disabled)
+ return;
+
+ pr_info("Core revision %08x\n", ACPI_CA_VERSION);
+
+ /* enable workarounds, unless strict ACPI spec. compliance */
+ if (!acpi_strict)
+ acpi_gbl_enable_interpreter_slack = TRUE;
+
+ acpi_permanent_mmap = true;
+
+#ifdef CONFIG_X86
+ /*
+ * If the machine falls into the DMI check table,
+ * DSDT will be copied to memory.
+ * Note that calling dmi_check_system() here on other architectures
+ * would not be OK because only x86 initializes dmi early enough.
+ * Thankfully only x86 systems need such quirks for now.
+ */
+ dmi_check_system(dsdt_dmi_table);
+#endif
+
+ status = acpi_reallocate_root_table();
+ if (ACPI_FAILURE(status)) {
+ pr_err("Unable to reallocate ACPI tables\n");
+ goto error0;
+ }
+
+ status = acpi_initialize_subsystem();
+ if (ACPI_FAILURE(status)) {
+ pr_err("Unable to initialize the ACPI Interpreter\n");
+ goto error0;
+ }
+
+#ifdef CONFIG_X86
+ if (!acpi_ioapic) {
+ /* compatible (0) means level (3) */
+ if (!(acpi_sci_flags & ACPI_MADT_TRIGGER_MASK)) {
+ acpi_sci_flags &= ~ACPI_MADT_TRIGGER_MASK;
+ acpi_sci_flags |= ACPI_MADT_TRIGGER_LEVEL;
+ }
+ /* Set PIC-mode SCI trigger type */
+ acpi_pic_sci_set_trigger(acpi_gbl_FADT.sci_interrupt,
+ (acpi_sci_flags & ACPI_MADT_TRIGGER_MASK) >> 2);
+ } else {
+ /*
+ * now that acpi_gbl_FADT is initialized,
+ * update it with result from INT_SRC_OVR parsing
+ */
+ acpi_gbl_FADT.sci_interrupt = acpi_sci_override_gsi;
+ }
+#endif
+ return;
+
+ error0:
+ disable_acpi();
+}
+
+/**
+ * acpi_subsystem_init - Finalize the early initialization of ACPI.
+ *
+ * Switch over the platform to the ACPI mode (if possible).
+ *
+ * Doing this too early is generally unsafe, but at the same time it needs to be
+ * done before all things that really depend on ACPI. The right spot appears to
+ * be before finalizing the EFI initialization.
+ */
+void __init acpi_subsystem_init(void)
+{
+ acpi_status status;
+
+ if (acpi_disabled)
+ return;
+
+ status = acpi_enable_subsystem(~ACPI_NO_ACPI_ENABLE);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Unable to enable ACPI\n");
+ disable_acpi();
+ } else {
+ /*
+ * If the system is using ACPI then we can be reasonably
+ * confident that any regulators are managed by the firmware
+ * so tell the regulator core it has everything it needs to
+ * know.
+ */
+ regulator_has_full_constraints();
+ }
+}
+
+static acpi_status acpi_bus_table_handler(u32 event, void *table, void *context)
+{
+ if (event == ACPI_TABLE_EVENT_LOAD)
+ acpi_scan_table_notify();
+
+ return acpi_sysfs_table_handler(event, table, context);
+}
+
+static int __init acpi_bus_init(void)
+{
+ int result;
+ acpi_status status;
+
+ acpi_os_initialize1();
+
+ status = acpi_load_tables();
+ if (ACPI_FAILURE(status)) {
+ pr_err("Unable to load the System Description Tables\n");
+ goto error1;
+ }
+
+ /*
+ * ACPI 2.0 requires the EC driver to be loaded and work before the EC
+ * device is found in the namespace.
+ *
+ * This is accomplished by looking for the ECDT table and getting the EC
+ * parameters out of that.
+ *
+ * Do that before calling acpi_initialize_objects() which may trigger EC
+ * address space accesses.
+ */
+ acpi_ec_ecdt_probe();
+
+ status = acpi_enable_subsystem(ACPI_NO_ACPI_ENABLE);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Unable to start the ACPI Interpreter\n");
+ goto error1;
+ }
+
+ status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Unable to initialize ACPI objects\n");
+ 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
+ */
+ acpi_bus_osc_negotiate_platform_control();
+ acpi_bus_osc_negotiate_usb_control();
+
+ /*
+ * _PDC control method may load dynamic SSDT tables,
+ * and we need to install the table handler before that.
+ */
+ status = acpi_install_table_handler(acpi_bus_table_handler, NULL);
+
+ acpi_sysfs_init();
+
+ acpi_early_processor_set_pdc();
+
+ /*
+ * Maybe EC region is required at bus_scan/acpi_get_devices. So it
+ * is necessary to enable it as early as possible.
+ */
+ acpi_ec_dsdt_probe();
+
+ pr_info("Interpreter enabled\n");
+
+ /* Initialize sleep structures */
+ acpi_sleep_init();
+
+ /*
+ * Get the system interrupt model and evaluate \_PIC.
+ */
+ result = acpi_bus_init_irq();
+ if (result)
+ goto error1;
+
+ /*
+ * Register the for all standard device notifications.
+ */
+ status =
+ acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY,
+ &acpi_bus_notify, NULL);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Unable to register for system notifications\n");
+ goto error1;
+ }
+
+ /*
+ * Create the top ACPI proc directory
+ */
+ acpi_root_dir = proc_mkdir(ACPI_BUS_FILE_ROOT, NULL);
+
+ result = bus_register(&acpi_bus_type);
+ if (!result)
+ return 0;
+
+ /* Mimic structured exception handling */
+ error1:
+ acpi_terminate();
+ return -ENODEV;
+}
+
+struct kobject *acpi_kobj;
+EXPORT_SYMBOL_GPL(acpi_kobj);
+
+static int __init acpi_init(void)
+{
+ int result;
+
+ if (acpi_disabled) {
+ pr_info("Interpreter disabled.\n");
+ return -ENODEV;
+ }
+
+ acpi_kobj = kobject_create_and_add("acpi", firmware_kobj);
+ if (!acpi_kobj)
+ pr_debug("%s: kset create error\n", __func__);
+
+ init_prmt();
+ acpi_init_pcc();
+ result = acpi_bus_init();
+ if (result) {
+ kobject_put(acpi_kobj);
+ disable_acpi();
+ return result;
+ }
+
+ pci_mmcfg_late_init();
+ acpi_iort_init();
+ acpi_viot_early_init();
+ acpi_hest_init();
+ acpi_ghes_init();
+ acpi_scan_init();
+ acpi_ec_init();
+ acpi_debugfs_init();
+ acpi_sleep_proc_init();
+ acpi_wakeup_device_init();
+ acpi_debugger_init();
+ acpi_setup_sb_notify_handler();
+ acpi_viot_init();
+ acpi_agdi_init();
+ return 0;
+}
+
+subsys_initcall(acpi_init);
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
new file mode 100644
index 000000000..1f9b9a4c3
--- /dev/null
+++ b/drivers/acpi/button.c
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * button.c - ACPI Button Driver
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: button: " fmt
+
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <acpi/button.h>
+
+#define ACPI_BUTTON_CLASS "button"
+#define ACPI_BUTTON_FILE_STATE "state"
+#define ACPI_BUTTON_TYPE_UNKNOWN 0x00
+#define ACPI_BUTTON_NOTIFY_STATUS 0x80
+
+#define ACPI_BUTTON_SUBCLASS_POWER "power"
+#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button"
+#define ACPI_BUTTON_TYPE_POWER 0x01
+
+#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep"
+#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button"
+#define ACPI_BUTTON_TYPE_SLEEP 0x03
+
+#define ACPI_BUTTON_SUBCLASS_LID "lid"
+#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
+#define ACPI_BUTTON_TYPE_LID 0x05
+
+enum {
+ ACPI_BUTTON_LID_INIT_IGNORE,
+ ACPI_BUTTON_LID_INIT_OPEN,
+ ACPI_BUTTON_LID_INIT_METHOD,
+ ACPI_BUTTON_LID_INIT_DISABLED,
+};
+
+static const char * const lid_init_state_str[] = {
+ [ACPI_BUTTON_LID_INIT_IGNORE] = "ignore",
+ [ACPI_BUTTON_LID_INIT_OPEN] = "open",
+ [ACPI_BUTTON_LID_INIT_METHOD] = "method",
+ [ACPI_BUTTON_LID_INIT_DISABLED] = "disabled",
+};
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION("ACPI Button Driver");
+MODULE_LICENSE("GPL");
+
+static const struct acpi_device_id button_device_ids[] = {
+ {ACPI_BUTTON_HID_LID, 0},
+ {ACPI_BUTTON_HID_SLEEP, 0},
+ {ACPI_BUTTON_HID_SLEEPF, 0},
+ {ACPI_BUTTON_HID_POWER, 0},
+ {ACPI_BUTTON_HID_POWERF, 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, button_device_ids);
+
+/* Please keep this list sorted alphabetically by vendor and model */
+static const struct dmi_system_id dmi_lid_quirks[] = {
+ {
+ /* GP-electronic T701, _LID method points to a floating GPIO */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
+ DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
+ },
+ .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
+ },
+ {
+ /*
+ * Lenovo Yoga 9 14ITL5, initial notification of the LID device
+ * never happens.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82BG"),
+ },
+ .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
+ },
+ {
+ /*
+ * Medion Akoya E2215T, notification of the LID device only
+ * happens on close, not on open and _LID always returns closed.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E2215T"),
+ },
+ .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
+ },
+ {
+ /*
+ * Medion Akoya E2228T, notification of the LID device only
+ * happens on close, not on open and _LID always returns closed.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E2228T"),
+ },
+ .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
+ },
+ {
+ /*
+ * Razer Blade Stealth 13 late 2019, notification of the LID device
+ * only happens on close, not on open and _LID always returns closed.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Razer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Razer Blade Stealth 13 Late 2019"),
+ },
+ .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);
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_button_suspend(struct device *dev);
+static int acpi_button_resume(struct device *dev);
+#else
+#define acpi_button_suspend NULL
+#define acpi_button_resume NULL
+#endif
+static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume);
+
+static struct acpi_driver acpi_button_driver = {
+ .name = "button",
+ .class = ACPI_BUTTON_CLASS,
+ .ids = button_device_ids,
+ .ops = {
+ .add = acpi_button_add,
+ .remove = acpi_button_remove,
+ .notify = acpi_button_notify,
+ },
+ .drv.pm = &acpi_button_pm,
+};
+
+struct acpi_button {
+ unsigned int type;
+ struct input_dev *input;
+ char phys[32]; /* for input device */
+ unsigned long pushed;
+ int last_state;
+ ktime_t last_time;
+ bool suspended;
+ bool lid_state_initialized;
+};
+
+static struct acpi_device *lid_device;
+static long lid_init_state = -1;
+
+static unsigned long lid_report_interval __read_mostly = 500;
+module_param(lid_report_interval, ulong, 0644);
+MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
+
+/* FS Interface (/proc) */
+static struct proc_dir_entry *acpi_button_dir;
+static struct proc_dir_entry *acpi_lid_dir;
+
+static int acpi_lid_evaluate_state(struct acpi_device *device)
+{
+ unsigned long long lid_state;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ return lid_state ? 1 : 0;
+}
+
+static int acpi_lid_notify_state(struct acpi_device *device, int state)
+{
+ struct acpi_button *button = acpi_driver_data(device);
+ ktime_t next_report;
+ bool do_update;
+
+ /*
+ * In lid_init_state=ignore mode, if user opens/closes lid
+ * frequently with "open" missing, and "last_time" is also updated
+ * frequently, "close" cannot be delivered to the userspace.
+ * So "last_time" is only updated after a timeout or an actual
+ * switch.
+ */
+ if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
+ button->last_state != !!state)
+ do_update = true;
+ else
+ do_update = false;
+
+ next_report = ktime_add(button->last_time,
+ ms_to_ktime(lid_report_interval));
+ if (button->last_state == !!state &&
+ ktime_after(ktime_get(), next_report)) {
+ /* Complain the buggy firmware */
+ pr_warn_once("The lid device is not compliant to SW_LID.\n");
+
+ /*
+ * Send the unreliable complement switch event:
+ *
+ * On most platforms, the lid device is reliable. However
+ * there are exceptions:
+ * 1. Platforms returning initial lid state as "close" by
+ * default after booting/resuming:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=89211
+ * https://bugzilla.kernel.org/show_bug.cgi?id=106151
+ * 2. Platforms never reporting "open" events:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=106941
+ * On these buggy platforms, the usage model of the ACPI
+ * lid device actually is:
+ * 1. The initial returning value of _LID may not be
+ * reliable.
+ * 2. The open event may not be reliable.
+ * 3. The close event is reliable.
+ *
+ * But SW_LID is typed as input switch event, the input
+ * layer checks if the event is redundant. Hence if the
+ * state is not switched, the userspace cannot see this
+ * platform triggered reliable event. By inserting a
+ * complement switch event, it then is guaranteed that the
+ * platform triggered reliable one can always be seen by
+ * the userspace.
+ */
+ if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
+ do_update = true;
+ /*
+ * Do generate complement switch event for "close"
+ * as "close" is reliable and wrong "open" won't
+ * trigger unexpected behaviors.
+ * Do not generate complement switch event for
+ * "open" as "open" is not reliable and wrong
+ * "close" will trigger unexpected behaviors.
+ */
+ if (!state) {
+ input_report_switch(button->input,
+ SW_LID, state);
+ input_sync(button->input);
+ }
+ }
+ }
+ /* Send the platform triggered reliable event */
+ if (do_update) {
+ acpi_handle_debug(device->handle, "ACPI LID %s\n",
+ state ? "open" : "closed");
+ input_report_switch(button->input, SW_LID, !state);
+ input_sync(button->input);
+ button->last_state = !!state;
+ button->last_time = ktime_get();
+ }
+
+ return 0;
+}
+
+static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq,
+ void *offset)
+{
+ struct acpi_device *device = seq->private;
+ int state;
+
+ state = acpi_lid_evaluate_state(device);
+ seq_printf(seq, "state: %s\n",
+ state < 0 ? "unsupported" : (state ? "open" : "closed"));
+ return 0;
+}
+
+static int acpi_button_add_fs(struct acpi_device *device)
+{
+ struct acpi_button *button = acpi_driver_data(device);
+ struct proc_dir_entry *entry = NULL;
+ int ret = 0;
+
+ /* procfs I/F for ACPI lid device only */
+ if (button->type != ACPI_BUTTON_TYPE_LID)
+ return 0;
+
+ if (acpi_button_dir || acpi_lid_dir) {
+ pr_info("More than one Lid device found!\n");
+ return -EEXIST;
+ }
+
+ /* create /proc/acpi/button */
+ acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
+ if (!acpi_button_dir)
+ return -ENODEV;
+
+ /* create /proc/acpi/button/lid */
+ acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
+ if (!acpi_lid_dir) {
+ ret = -ENODEV;
+ goto remove_button_dir;
+ }
+
+ /* create /proc/acpi/button/lid/LID/ */
+ acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
+ if (!acpi_device_dir(device)) {
+ ret = -ENODEV;
+ goto remove_lid_dir;
+ }
+
+ /* create /proc/acpi/button/lid/LID/state */
+ entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO,
+ acpi_device_dir(device), acpi_button_state_seq_show,
+ device);
+ if (!entry) {
+ ret = -ENODEV;
+ goto remove_dev_dir;
+ }
+
+done:
+ return ret;
+
+remove_dev_dir:
+ remove_proc_entry(acpi_device_bid(device),
+ acpi_lid_dir);
+ acpi_device_dir(device) = NULL;
+remove_lid_dir:
+ remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
+ acpi_lid_dir = NULL;
+remove_button_dir:
+ remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
+ acpi_button_dir = NULL;
+ goto done;
+}
+
+static int acpi_button_remove_fs(struct acpi_device *device)
+{
+ struct acpi_button *button = acpi_driver_data(device);
+
+ if (button->type != ACPI_BUTTON_TYPE_LID)
+ return 0;
+
+ remove_proc_entry(ACPI_BUTTON_FILE_STATE,
+ acpi_device_dir(device));
+ remove_proc_entry(acpi_device_bid(device),
+ acpi_lid_dir);
+ acpi_device_dir(device) = NULL;
+ remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
+ acpi_lid_dir = NULL;
+ remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
+ acpi_button_dir = NULL;
+
+ return 0;
+}
+
+/* Driver Interface */
+int acpi_lid_open(void)
+{
+ if (!lid_device)
+ return -ENODEV;
+
+ return acpi_lid_evaluate_state(lid_device);
+}
+EXPORT_SYMBOL(acpi_lid_open);
+
+static int acpi_lid_update_state(struct acpi_device *device,
+ bool signal_wakeup)
+{
+ int state;
+
+ state = acpi_lid_evaluate_state(device);
+ if (state < 0)
+ return state;
+
+ if (state && signal_wakeup)
+ acpi_pm_wakeup_event(&device->dev);
+
+ return acpi_lid_notify_state(device, state);
+}
+
+static void acpi_lid_initialize_state(struct acpi_device *device)
+{
+ struct acpi_button *button = acpi_driver_data(device);
+
+ switch (lid_init_state) {
+ case ACPI_BUTTON_LID_INIT_OPEN:
+ (void)acpi_lid_notify_state(device, 1);
+ break;
+ case ACPI_BUTTON_LID_INIT_METHOD:
+ (void)acpi_lid_update_state(device, false);
+ break;
+ case ACPI_BUTTON_LID_INIT_IGNORE:
+ default:
+ break;
+ }
+
+ button->lid_state_initialized = true;
+}
+
+static void acpi_button_notify(struct acpi_device *device, u32 event)
+{
+ struct acpi_button *button = acpi_driver_data(device);
+ struct input_dev *input;
+
+ 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;
+ default:
+ acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
+ event);
+ break;
+ }
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_button_suspend(struct device *dev)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct acpi_button *button = acpi_driver_data(device);
+
+ button->suspended = true;
+ return 0;
+}
+
+static int acpi_button_resume(struct device *dev)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct acpi_button *button = acpi_driver_data(device);
+
+ button->suspended = false;
+ if (button->type == ACPI_BUTTON_TYPE_LID) {
+ button->last_state = !!acpi_lid_evaluate_state(device);
+ button->last_time = ktime_get();
+ acpi_lid_initialize_state(device);
+ }
+ return 0;
+}
+#endif
+
+static int acpi_lid_input_open(struct input_dev *input)
+{
+ struct acpi_device *device = input_get_drvdata(input);
+ struct acpi_button *button = acpi_driver_data(device);
+
+ button->last_state = !!acpi_lid_evaluate_state(device);
+ button->last_time = ktime_get();
+ acpi_lid_initialize_state(device);
+
+ return 0;
+}
+
+static int acpi_button_add(struct acpi_device *device)
+{
+ struct acpi_button *button;
+ struct input_dev *input;
+ const char *hid = acpi_device_hid(device);
+ char *name, *class;
+ int error;
+
+ if (!strcmp(hid, ACPI_BUTTON_HID_LID) &&
+ lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED)
+ return -ENODEV;
+
+ button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
+ if (!button)
+ return -ENOMEM;
+
+ device->driver_data = button;
+
+ button->input = input = input_allocate_device();
+ if (!input) {
+ error = -ENOMEM;
+ goto err_free_button;
+ }
+
+ name = acpi_device_name(device);
+ class = acpi_device_class(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);
+ 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);
+ 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);
+ 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;
+
+ snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
+
+ input->name = name;
+ input->phys = button->phys;
+ input->id.bustype = BUS_HOST;
+ input->id.product = button->type;
+ input->dev.parent = &device->dev;
+
+ switch (button->type) {
+ case ACPI_BUTTON_TYPE_POWER:
+ input_set_capability(input, EV_KEY, KEY_POWER);
+ break;
+
+ case ACPI_BUTTON_TYPE_SLEEP:
+ input_set_capability(input, EV_KEY, KEY_SLEEP);
+ break;
+
+ case ACPI_BUTTON_TYPE_LID:
+ input_set_capability(input, EV_SW, SW_LID);
+ break;
+ }
+
+ input_set_drvdata(input, device);
+ error = input_register_device(input);
+ if (error)
+ goto err_remove_fs;
+ if (button->type == ACPI_BUTTON_TYPE_LID) {
+ /*
+ * This assumes there's only one lid device, or if there are
+ * more we only care about the last one...
+ */
+ lid_device = device;
+ }
+
+ device_init_wakeup(&device->dev, true);
+ pr_info("%s [%s]\n", name, acpi_device_bid(device));
+ return 0;
+
+ err_remove_fs:
+ acpi_button_remove_fs(device);
+ err_free_input:
+ input_free_device(input);
+ err_free_button:
+ kfree(button);
+ return error;
+}
+
+static int acpi_button_remove(struct acpi_device *device)
+{
+ struct acpi_button *button = acpi_driver_data(device);
+
+ acpi_button_remove_fs(device);
+ input_unregister_device(button->input);
+ kfree(button);
+ return 0;
+}
+
+static int param_set_lid_init_state(const char *val,
+ const struct kernel_param *kp)
+{
+ int i;
+
+ i = sysfs_match_string(lid_init_state_str, val);
+ if (i < 0)
+ return i;
+
+ lid_init_state = i;
+ pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]);
+ return 0;
+}
+
+static int param_get_lid_init_state(char *buf, const struct kernel_param *kp)
+{
+ int i, c = 0;
+
+ for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++)
+ if (i == lid_init_state)
+ c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]);
+ else
+ c += sprintf(buf + c, "%s ", lid_init_state_str[i]);
+
+ buf[c - 1] = '\n'; /* Replace the final space with a newline */
+
+ return c;
+}
+
+module_param_call(lid_init_state,
+ param_set_lid_init_state, param_get_lid_init_state,
+ NULL, 0644);
+MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");
+
+static int acpi_button_register_driver(struct acpi_driver *driver)
+{
+ const struct dmi_system_id *dmi_id;
+
+ if (lid_init_state == -1) {
+ dmi_id = dmi_first_match(dmi_lid_quirks);
+ if (dmi_id)
+ lid_init_state = (long)dmi_id->driver_data;
+ else
+ lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
+ }
+
+ /*
+ * Modules such as nouveau.ko and i915.ko have a link time dependency
+ * on acpi_lid_open(), and would therefore not be loadable on ACPI
+ * capable kernels booted in non-ACPI mode if the return value of
+ * acpi_bus_register_driver() is returned from here with ACPI disabled
+ * when this driver is built as a module.
+ */
+ if (acpi_disabled)
+ return 0;
+
+ return acpi_bus_register_driver(driver);
+}
+
+static void acpi_button_unregister_driver(struct acpi_driver *driver)
+{
+ if (!acpi_disabled)
+ acpi_bus_unregister_driver(driver);
+}
+
+module_driver(acpi_button_driver, acpi_button_register_driver,
+ acpi_button_unregister_driver);
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c
new file mode 100644
index 000000000..5b7e3b9ae
--- /dev/null
+++ b/drivers/acpi/container.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * container.c - ACPI Generic Container Driver
+ *
+ * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com)
+ * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com)
+ * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com)
+ * Copyright (C) 2004 FUJITSU LIMITED
+ * Copyright (C) 2004, 2013 Intel Corp.
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ */
+#include <linux/acpi.h>
+#include <linux/container.h>
+
+#include "internal.h"
+
+static const struct acpi_device_id container_device_ids[] = {
+ {"ACPI0004", 0},
+ {"PNP0A05", 0},
+ {"PNP0A06", 0},
+ {"", 0},
+};
+
+#ifdef CONFIG_ACPI_CONTAINER
+
+static int check_offline(struct acpi_device *adev, void *not_used)
+{
+ if (acpi_scan_is_offline(adev, false))
+ return 0;
+
+ return -EBUSY;
+}
+
+static int acpi_container_offline(struct container_dev *cdev)
+{
+ /* Check all of the dependent devices' physical companions. */
+ return acpi_dev_for_each_child(ACPI_COMPANION(&cdev->dev), check_offline, NULL);
+}
+
+static void acpi_container_release(struct device *dev)
+{
+ kfree(to_container_dev(dev));
+}
+
+static int container_device_attach(struct acpi_device *adev,
+ const struct acpi_device_id *not_used)
+{
+ struct container_dev *cdev;
+ struct device *dev;
+ int ret;
+
+ if (adev->flags.is_dock_station)
+ return 0;
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ cdev->offline = acpi_container_offline;
+ dev = &cdev->dev;
+ dev->bus = &container_subsys;
+ dev_set_name(dev, "%s", dev_name(&adev->dev));
+ ACPI_COMPANION_SET(dev, adev);
+ dev->release = acpi_container_release;
+ ret = device_register(dev);
+ if (ret) {
+ put_device(dev);
+ return ret;
+ }
+ adev->driver_data = dev;
+ return 1;
+}
+
+static void container_device_detach(struct acpi_device *adev)
+{
+ struct device *dev = acpi_driver_data(adev);
+
+ adev->driver_data = NULL;
+ if (dev)
+ device_unregister(dev);
+}
+
+static void container_device_online(struct acpi_device *adev)
+{
+ struct device *dev = acpi_driver_data(adev);
+
+ kobject_uevent(&dev->kobj, KOBJ_ONLINE);
+}
+
+static struct acpi_scan_handler container_handler = {
+ .ids = container_device_ids,
+ .attach = container_device_attach,
+ .detach = container_device_detach,
+ .hotplug = {
+ .enabled = true,
+ .demand_offline = true,
+ .notify_online = container_device_online,
+ },
+};
+
+void __init acpi_container_init(void)
+{
+ acpi_scan_add_handler(&container_handler);
+}
+
+#else
+
+static struct acpi_scan_handler container_handler = {
+ .ids = container_device_ids,
+};
+
+void __init acpi_container_init(void)
+{
+ acpi_scan_add_handler_with_hotplug(&container_handler, "container");
+}
+
+#endif /* CONFIG_ACPI_CONTAINER */
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
new file mode 100644
index 000000000..093675b1a
--- /dev/null
+++ b/drivers/acpi/cppc_acpi.c
@@ -0,0 +1,1589 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * CPPC (Collaborative Processor Performance Control) methods used by CPUfreq drivers.
+ *
+ * (C) Copyright 2014, 2015 Linaro Ltd.
+ * Author: Ashwin Chaugule <ashwin.chaugule@linaro.org>
+ *
+ * CPPC describes a few methods for controlling CPU performance using
+ * information from a per CPU table called CPC. This table is described in
+ * the ACPI v5.0+ specification. The table consists of a list of
+ * registers which may be memory mapped or hardware registers and also may
+ * include some static integer values.
+ *
+ * CPU performance is on an abstract continuous scale as against a discretized
+ * P-state scale which is tied to CPU frequency only. In brief, the basic
+ * operation involves:
+ *
+ * - OS makes a CPU performance request. (Can provide min and max bounds)
+ *
+ * - Platform (such as BMC) is free to optimize request within requested bounds
+ * depending on power/thermal budgets etc.
+ *
+ * - Platform conveys its decision back to OS
+ *
+ * The communication between OS and platform occurs through another medium
+ * called (PCC) Platform Communication Channel. This is a generic mailbox like
+ * mechanism which includes doorbell semantics to indicate register updates.
+ * See drivers/mailbox/pcc.c for details on PCC.
+ *
+ * Finer details about the PCC and CPPC spec are available in the ACPI v5.1 and
+ * above specifications.
+ */
+
+#define pr_fmt(fmt) "ACPI CPPC: " fmt
+
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/ktime.h>
+#include <linux/rwsem.h>
+#include <linux/wait.h>
+#include <linux/topology.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;
+
+ bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */
+ bool platform_owns_pcc; /* Ownership of PCC subspace */
+ unsigned int pcc_write_cnt; /* Running count of PCC write commands */
+
+ /*
+ * Lock to provide controlled access to the PCC channel.
+ *
+ * For performance critical usecases(currently cppc_set_perf)
+ * We need to take read_lock and check if channel belongs to OSPM
+ * before reading or writing to PCC subspace
+ * We need to take write_lock before transferring the channel
+ * ownership to the platform via a Doorbell
+ * This allows us to batch a number of CPPC requests if they happen
+ * to originate in about the same time
+ *
+ * For non-performance critical usecases(init)
+ * Take write_lock for all purposes which gives exclusive access
+ */
+ struct rw_semaphore pcc_lock;
+
+ /* Wait queue for CPUs whose requests were batched */
+ wait_queue_head_t pcc_write_wait_q;
+ ktime_t last_cmd_cmpl_time;
+ ktime_t last_mpar_reset;
+ int mpar_count;
+ int refcount;
+};
+
+/* Array to represent the PCC channel per subspace ID */
+static struct cppc_pcc_data *pcc_data[MAX_PCC_SUBSPACES];
+/* The cpu_pcc_subspace_idx contains per CPU subspace ID */
+static DEFINE_PER_CPU(int, cpu_pcc_subspace_idx);
+
+/*
+ * The cpc_desc structure contains the ACPI register details
+ * as described in the per CPU _CPC tables. The details
+ * include the type of register (e.g. PCC, System IO, FFH etc.)
+ * and destination addresses which lets us READ/WRITE CPU performance
+ * information using the appropriate I/O methods.
+ */
+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 + \
+ 0x8 + (offs))
+
+/* Check if a CPC register is in PCC */
+#define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \
+ (cpc)->cpc_entry.reg.space_id == \
+ ACPI_ADR_SPACE_PLATFORM_COMM)
+
+/* 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 == \
+ ACPI_ADR_SPACE_SYSTEM_MEMORY)
+
+/* Check if a CPC register is in SystemIo */
+#define CPC_IN_SYSTEM_IO(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \
+ (cpc)->cpc_entry.reg.space_id == \
+ ACPI_ADR_SPACE_SYSTEM_IO)
+
+/* Evaluates to True if reg is a NULL register descriptor */
+#define IS_NULL_REG(reg) ((reg)->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && \
+ (reg)->address == 0 && \
+ (reg)->bit_width == 0 && \
+ (reg)->bit_offset == 0 && \
+ (reg)->access_width == 0)
+
+/* Evaluates to True if an optional cpc field is supported */
+#define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ? \
+ !!(cpc)->cpc_entry.int_value : \
+ !IS_NULL_REG(&(cpc)->cpc_entry.reg))
+/*
+ * Arbitrary Retries in case the remote processor is slow to respond
+ * to PCC commands. Keeping it high enough to cover emulators where
+ * the processors run painfully slow.
+ */
+#define NUM_RETRIES 500ULL
+
+#define OVER_16BTS_MASK ~0xFFFFULL
+
+#define define_one_cppc_ro(_name) \
+static struct kobj_attribute _name = \
+__ATTR(_name, 0444, show_##_name, NULL)
+
+#define to_cpc_desc(a) container_of(a, struct cpc_desc, kobj)
+
+#define show_cppc_data(access_fn, struct_name, member_name) \
+ static ssize_t show_##member_name(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+ { \
+ struct cpc_desc *cpc_ptr = to_cpc_desc(kobj); \
+ struct struct_name st_name = {0}; \
+ int ret; \
+ \
+ ret = access_fn(cpc_ptr->cpu_id, &st_name); \
+ if (ret) \
+ return ret; \
+ \
+ return scnprintf(buf, PAGE_SIZE, "%llu\n", \
+ (u64)st_name.member_name); \
+ } \
+ define_one_cppc_ro(member_name)
+
+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, 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);
+
+static ssize_t show_feedback_ctrs(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
+ struct cppc_perf_fb_ctrs fb_ctrs = {0};
+ int ret;
+
+ ret = cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
+ if (ret)
+ return ret;
+
+ return scnprintf(buf, PAGE_SIZE, "ref:%llu del:%llu\n",
+ fb_ctrs.reference, fb_ctrs.delivered);
+}
+define_one_cppc_ro(feedback_ctrs);
+
+static struct attribute *cppc_attrs[] = {
+ &feedback_ctrs.attr,
+ &reference_perf.attr,
+ &wraparound_time.attr,
+ &highest_perf.attr,
+ &lowest_perf.attr,
+ &lowest_nonlinear_perf.attr,
+ &nominal_perf.attr,
+ &nominal_freq.attr,
+ &lowest_freq.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(cppc);
+
+static struct kobj_type cppc_ktype = {
+ .sysfs_ops = &kobj_sysfs_ops,
+ .default_groups = cppc_groups,
+};
+
+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;
+
+ if (!pcc_ss_data->platform_owns_pcc)
+ return 0;
+
+ /*
+ * Poll PCC status register every 3us(delay_us) for maximum of
+ * deadline_us(timeout_us) until PCC command complete bit is set(cond)
+ */
+ ret = readw_relaxed_poll_timeout(&generic_comm_base->status, status,
+ status & PCC_CMD_COMPLETE_MASK, 3,
+ pcc_ss_data->deadline_us);
+
+ if (likely(!ret)) {
+ pcc_ss_data->platform_owns_pcc = false;
+ if (chk_err_bit && (status & PCC_ERROR_MASK))
+ ret = -EIO;
+ }
+
+ if (unlikely(ret))
+ pr_err("PCC check channel failed for ss: %d. ret=%d\n",
+ pcc_ss_id, ret);
+
+ return ret;
+}
+
+/*
+ * This function transfers the ownership of the PCC to the platform
+ * So it must be called while holding write_lock(pcc_lock)
+ */
+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;
+ unsigned int time_delta;
+
+ /*
+ * For CMD_WRITE we know for a fact the caller should have checked
+ * the channel before writing to PCC space
+ */
+ if (cmd == CMD_READ) {
+ /*
+ * If there are pending cpc_writes, then we stole the channel
+ * before write completion, so first send a WRITE command to
+ * platform
+ */
+ if (pcc_ss_data->pending_pcc_write_cmd)
+ send_pcc_cmd(pcc_ss_id, CMD_WRITE);
+
+ ret = check_pcc_chan(pcc_ss_id, false);
+ if (ret)
+ goto end;
+ } else /* CMD_WRITE */
+ pcc_ss_data->pending_pcc_write_cmd = FALSE;
+
+ /*
+ * Handle the Minimum Request Turnaround Time(MRTT)
+ * "The minimum amount of time that OSPM must wait after the completion
+ * of a command before issuing the next command, in microseconds"
+ */
+ if (pcc_ss_data->pcc_mrtt) {
+ time_delta = ktime_us_delta(ktime_get(),
+ pcc_ss_data->last_cmd_cmpl_time);
+ if (pcc_ss_data->pcc_mrtt > time_delta)
+ udelay(pcc_ss_data->pcc_mrtt - time_delta);
+ }
+
+ /*
+ * Handle the non-zero Maximum Periodic Access Rate(MPAR)
+ * "The maximum number of periodic requests that the subspace channel can
+ * support, reported in commands per minute. 0 indicates no limitation."
+ *
+ * This parameter should be ideally zero or large enough so that it can
+ * handle maximum number of requests that all the cores in the system can
+ * collectively generate. If it is not, we will follow the spec and just
+ * not send the request to the platform after hitting the MPAR limit in
+ * any 60s window
+ */
+ if (pcc_ss_data->pcc_mpar) {
+ if (pcc_ss_data->mpar_count == 0) {
+ time_delta = ktime_ms_delta(ktime_get(),
+ pcc_ss_data->last_mpar_reset);
+ if ((time_delta < 60 * MSEC_PER_SEC) && pcc_ss_data->last_mpar_reset) {
+ pr_debug("PCC cmd for subspace %d not sent due to MPAR limit",
+ pcc_ss_id);
+ ret = -EIO;
+ goto end;
+ }
+ pcc_ss_data->last_mpar_reset = ktime_get();
+ pcc_ss_data->mpar_count = pcc_ss_data->pcc_mpar;
+ }
+ pcc_ss_data->mpar_count--;
+ }
+
+ /* Write to the shared comm region. */
+ writew_relaxed(cmd, &generic_comm_base->command);
+
+ /* Flip CMD COMPLETE bit */
+ writew_relaxed(0, &generic_comm_base->status);
+
+ pcc_ss_data->platform_owns_pcc = true;
+
+ /* Ring doorbell */
+ ret = mbox_send_message(pcc_ss_data->pcc_channel->mchan, &cmd);
+ if (ret < 0) {
+ pr_err("Err sending PCC mbox message. ss: %d cmd:%d, ret:%d\n",
+ pcc_ss_id, cmd, ret);
+ goto end;
+ }
+
+ /* wait for completion and check for PCC error bit */
+ ret = check_pcc_chan(pcc_ss_id, true);
+
+ if (pcc_ss_data->pcc_mrtt)
+ pcc_ss_data->last_cmd_cmpl_time = ktime_get();
+
+ if (pcc_ss_data->pcc_channel->mchan->mbox->txdone_irq)
+ mbox_chan_txdone(pcc_ss_data->pcc_channel->mchan, ret);
+ else
+ mbox_client_txdone(pcc_ss_data->pcc_channel->mchan, ret);
+
+end:
+ if (cmd == CMD_WRITE) {
+ if (unlikely(ret)) {
+ for_each_possible_cpu(i) {
+ struct cpc_desc *desc = per_cpu(cpc_desc_ptr, i);
+
+ if (!desc)
+ continue;
+
+ if (desc->write_cmd_id == pcc_ss_data->pcc_write_cnt)
+ desc->write_cmd_status = ret;
+ }
+ }
+ pcc_ss_data->pcc_write_cnt++;
+ wake_up_all(&pcc_ss_data->pcc_write_wait_q);
+ }
+
+ return ret;
+}
+
+static void cppc_chan_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+ if (ret < 0)
+ pr_debug("TX did not complete: CMD sent:%x, ret:%d\n",
+ *(u16 *)msg, ret);
+ else
+ pr_debug("TX completed. CMD sent:%x, ret:%d\n",
+ *(u16 *)msg, ret);
+}
+
+static struct mbox_client cppc_mbox_cl = {
+ .tx_done = cppc_chan_tx_done,
+ .knows_txdone = true,
+};
+
+static int acpi_get_psd(struct cpc_desc *cpc_ptr, acpi_handle handle)
+{
+ int result = -EFAULT;
+ acpi_status status = AE_OK;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"};
+ struct acpi_buffer state = {0, NULL};
+ union acpi_object *psd = NULL;
+ struct acpi_psd_package *pdomain;
+
+ status = acpi_evaluate_object_typed(handle, "_PSD", NULL,
+ &buffer, ACPI_TYPE_PACKAGE);
+ if (status == AE_NOT_FOUND) /* _PSD is optional */
+ return 0;
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ psd = buffer.pointer;
+ if (!psd || psd->package.count != 1) {
+ pr_debug("Invalid _PSD data\n");
+ goto end;
+ }
+
+ pdomain = &(cpc_ptr->domain_info);
+
+ state.length = sizeof(struct acpi_psd_package);
+ state.pointer = pdomain;
+
+ status = acpi_extract_package(&(psd->package.elements[0]),
+ &format, &state);
+ if (ACPI_FAILURE(status)) {
+ pr_debug("Invalid _PSD data for CPU:%d\n", cpc_ptr->cpu_id);
+ goto end;
+ }
+
+ if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) {
+ pr_debug("Unknown _PSD:num_entries for CPU:%d\n", cpc_ptr->cpu_id);
+ goto end;
+ }
+
+ if (pdomain->revision != ACPI_PSD_REV0_REVISION) {
+ pr_debug("Unknown _PSD:revision for CPU: %d\n", cpc_ptr->cpu_id);
+ goto end;
+ }
+
+ if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
+ pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
+ pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
+ pr_debug("Invalid _PSD:coord_type for CPU:%d\n", cpc_ptr->cpu_id);
+ goto end;
+ }
+
+ result = 0;
+end:
+ kfree(buffer.pointer);
+ return result;
+}
+
+bool acpi_cpc_valid(void)
+{
+ struct cpc_desc *cpc_ptr;
+ int cpu;
+
+ if (acpi_disabled)
+ return false;
+
+ for_each_present_cpu(cpu) {
+ cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
+ if (!cpc_ptr)
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(acpi_cpc_valid);
+
+bool cppc_allow_fast_switch(void)
+{
+ struct cpc_register_resource *desired_reg;
+ struct cpc_desc *cpc_ptr;
+ int cpu;
+
+ for_each_possible_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) &&
+ !CPC_IN_SYSTEM_IO(desired_reg))
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(cppc_allow_fast_switch);
+
+/**
+ * acpi_get_psd_map - Map the CPUs in the freq domain of a given cpu
+ * @cpu: Find all CPUs that share a domain with cpu.
+ * @cpu_data: Pointer to CPU specific CPPC data including PSD info.
+ *
+ * Return: 0 for success or negative value for err.
+ */
+int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data)
+{
+ struct cpc_desc *cpc_ptr, *match_cpc_ptr;
+ struct acpi_psd_package *match_pdomain;
+ struct acpi_psd_package *pdomain;
+ int count_target, i;
+
+ /*
+ * Now that we have _PSD data from all CPUs, let's setup P-state
+ * domain info.
+ */
+ cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
+ if (!cpc_ptr)
+ return -EFAULT;
+
+ pdomain = &(cpc_ptr->domain_info);
+ cpumask_set_cpu(cpu, cpu_data->shared_cpu_map);
+ if (pdomain->num_processors <= 1)
+ return 0;
+
+ /* Validate the Domain info */
+ count_target = pdomain->num_processors;
+ if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
+ cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+ else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL)
+ cpu_data->shared_type = CPUFREQ_SHARED_TYPE_HW;
+ else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)
+ cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ANY;
+
+ for_each_possible_cpu(i) {
+ if (i == cpu)
+ continue;
+
+ match_cpc_ptr = per_cpu(cpc_desc_ptr, i);
+ if (!match_cpc_ptr)
+ goto err_fault;
+
+ match_pdomain = &(match_cpc_ptr->domain_info);
+ if (match_pdomain->domain != pdomain->domain)
+ continue;
+
+ /* Here i and cpu are in the same domain */
+ if (match_pdomain->num_processors != count_target)
+ goto err_fault;
+
+ if (pdomain->coord_type != match_pdomain->coord_type)
+ goto err_fault;
+
+ cpumask_set_cpu(i, cpu_data->shared_cpu_map);
+ }
+
+ return 0;
+
+err_fault:
+ /* Assume no coordination on any error parsing domain info */
+ cpumask_clear(cpu_data->shared_cpu_map);
+ cpumask_set_cpu(cpu, cpu_data->shared_cpu_map);
+ cpu_data->shared_type = CPUFREQ_SHARED_TYPE_NONE;
+
+ return -EFAULT;
+}
+EXPORT_SYMBOL_GPL(acpi_get_psd_map);
+
+static int register_pcc_channel(int pcc_ss_idx)
+{
+ struct pcc_mbox_chan *pcc_chan;
+ u64 usecs_lat;
+
+ if (pcc_ss_idx >= 0) {
+ pcc_chan = pcc_mbox_request_channel(&cppc_mbox_cl, pcc_ss_idx);
+
+ if (IS_ERR(pcc_chan)) {
+ pr_err("Failed to find PCC channel for subspace %d\n",
+ pcc_ss_idx);
+ return -ENODEV;
+ }
+
+ pcc_data[pcc_ss_idx]->pcc_channel = pcc_chan;
+ /*
+ * cppc_ss->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 = NUM_RETRIES * pcc_chan->latency;
+ pcc_data[pcc_ss_idx]->deadline_us = usecs_lat;
+ pcc_data[pcc_ss_idx]->pcc_mrtt = pcc_chan->min_turnaround_time;
+ 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;
+ }
+
+ return 0;
+}
+
+/**
+ * cpc_ffh_supported() - check if FFH reading supported
+ *
+ * Check if the architecture has support for functional fixed hardware
+ * read/write capability.
+ *
+ * Return: true for supported, false for not supported
+ */
+bool __weak cpc_ffh_supported(void)
+{
+ return false;
+}
+
+/**
+ * cpc_supported_by_cpu() - check if CPPC is supported by CPU
+ *
+ * Check if the architectural support for CPPC is present even
+ * if the _OSC hasn't prescribed it
+ *
+ * Return: true for supported, false for not supported
+ */
+bool __weak cpc_supported_by_cpu(void)
+{
+ return false;
+}
+
+/**
+ * pcc_data_alloc() - Allocate the pcc_data memory for pcc subspace
+ *
+ * Check and allocate the cppc_pcc_data memory.
+ * In some processor configurations it is possible that same subspace
+ * is shared between multiple CPUs. This is seen especially in CPUs
+ * with hardware multi-threading support.
+ *
+ * Return: 0 for success, errno for failure
+ */
+static int pcc_data_alloc(int pcc_ss_id)
+{
+ if (pcc_ss_id < 0 || pcc_ss_id >= MAX_PCC_SUBSPACES)
+ return -EINVAL;
+
+ if (pcc_data[pcc_ss_id]) {
+ pcc_data[pcc_ss_id]->refcount++;
+ } else {
+ pcc_data[pcc_ss_id] = kzalloc(sizeof(struct cppc_pcc_data),
+ GFP_KERNEL);
+ if (!pcc_data[pcc_ss_id])
+ return -ENOMEM;
+ pcc_data[pcc_ss_id]->refcount++;
+ }
+
+ return 0;
+}
+
+/*
+ * An example CPC table looks like the following.
+ *
+ * Name (_CPC, Package() {
+ * 17, // NumEntries
+ * 1, // Revision
+ * ResourceTemplate() {Register(PCC, 32, 0, 0x120, 2)}, // Highest Performance
+ * ResourceTemplate() {Register(PCC, 32, 0, 0x124, 2)}, // Nominal Performance
+ * ResourceTemplate() {Register(PCC, 32, 0, 0x128, 2)}, // Lowest Nonlinear Performance
+ * ResourceTemplate() {Register(PCC, 32, 0, 0x12C, 2)}, // Lowest Performance
+ * ResourceTemplate() {Register(PCC, 32, 0, 0x130, 2)}, // Guaranteed Performance Register
+ * ResourceTemplate() {Register(PCC, 32, 0, 0x110, 2)}, // Desired Performance Register
+ * ResourceTemplate() {Register(SystemMemory, 0, 0, 0, 0)},
+ * ...
+ * ...
+ * ...
+ * }
+ * Each Register() encodes how to access that specific register.
+ * e.g. a sample PCC entry has the following encoding:
+ *
+ * Register (
+ * PCC, // AddressSpaceKeyword
+ * 8, // RegisterBitWidth
+ * 8, // RegisterBitOffset
+ * 0x30, // RegisterAddress
+ * 9, // AccessSize (subspace 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.
+ *
+ * Return: 0 for success or negative value for err.
+ */
+int acpi_cppc_processor_probe(struct acpi_processor *pr)
+{
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *out_obj, *cpc_obj;
+ struct cpc_desc *cpc_ptr;
+ struct cpc_reg *gas_t;
+ struct device *cpu_dev;
+ acpi_handle handle = pr->handle;
+ unsigned int num_ent, i, cpc_rev;
+ int pcc_subspace_id = -1;
+ acpi_status status;
+ int ret = -ENODATA;
+
+ if (!osc_sb_cppc2_support_acked) {
+ pr_debug("CPPC v2 _OSC not acked\n");
+ if (!cpc_supported_by_cpu())
+ return -ENODEV;
+ }
+
+ /* Parse the ACPI _CPC table for this CPU. */
+ status = acpi_evaluate_object_typed(handle, "_CPC", NULL, &output,
+ ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status)) {
+ ret = -ENODEV;
+ goto out_buf_free;
+ }
+
+ out_obj = (union acpi_object *) output.pointer;
+
+ cpc_ptr = kzalloc(sizeof(struct cpc_desc), GFP_KERNEL);
+ if (!cpc_ptr) {
+ ret = -ENOMEM;
+ goto out_buf_free;
+ }
+
+ /* First entry is NumEntries. */
+ cpc_obj = &out_obj->package.elements[0];
+ if (cpc_obj->type == ACPI_TYPE_INTEGER) {
+ num_ent = cpc_obj->integer.value;
+ if (num_ent <= 1) {
+ pr_debug("Unexpected _CPC NumEntries value (%d) for CPU:%d\n",
+ num_ent, pr->id);
+ goto out_free;
+ }
+ } else {
+ pr_debug("Unexpected _CPC NumEntries entry type (%d) for CPU:%d\n",
+ cpc_obj->type, pr->id);
+ goto out_free;
+ }
+
+ /* Second entry should be revision. */
+ cpc_obj = &out_obj->package.elements[1];
+ if (cpc_obj->type == ACPI_TYPE_INTEGER) {
+ cpc_rev = cpc_obj->integer.value;
+ } else {
+ pr_debug("Unexpected _CPC Revision entry type (%d) for CPU:%d\n",
+ cpc_obj->type, pr->id);
+ goto out_free;
+ }
+
+ if (cpc_rev < CPPC_V2_REV) {
+ pr_debug("Unsupported _CPC Revision (%d) for CPU:%d\n", cpc_rev,
+ pr->id);
+ goto out_free;
+ }
+
+ /*
+ * Disregard _CPC if the number of entries in the return pachage is not
+ * as expected, but support future revisions being proper supersets of
+ * the v3 and only causing more entries to be returned by _CPC.
+ */
+ if ((cpc_rev == CPPC_V2_REV && num_ent != CPPC_V2_NUM_ENT) ||
+ (cpc_rev == CPPC_V3_REV && num_ent != CPPC_V3_NUM_ENT) ||
+ (cpc_rev > CPPC_V3_REV && num_ent <= CPPC_V3_NUM_ENT)) {
+ pr_debug("Unexpected number of _CPC return package entries (%d) for CPU:%d\n",
+ num_ent, pr->id);
+ goto out_free;
+ }
+ if (cpc_rev > CPPC_V3_REV) {
+ num_ent = CPPC_V3_NUM_ENT;
+ cpc_rev = CPPC_V3_REV;
+ }
+
+ cpc_ptr->num_entries = num_ent;
+ cpc_ptr->version = cpc_rev;
+
+ /* Iterate through remaining entries in _CPC */
+ for (i = 2; i < num_ent; i++) {
+ cpc_obj = &out_obj->package.elements[i];
+
+ if (cpc_obj->type == ACPI_TYPE_INTEGER) {
+ cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER;
+ cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = cpc_obj->integer.value;
+ } else if (cpc_obj->type == ACPI_TYPE_BUFFER) {
+ gas_t = (struct cpc_reg *)
+ cpc_obj->buffer.pointer;
+
+ /*
+ * The PCC Subspace index is encoded inside
+ * the CPC table entries. The same PCC index
+ * will be used for all the PCC entries,
+ * so extract it only once.
+ */
+ if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
+ if (pcc_subspace_id < 0) {
+ pcc_subspace_id = gas_t->access_width;
+ if (pcc_data_alloc(pcc_subspace_id))
+ goto out_free;
+ } else if (pcc_subspace_id != gas_t->access_width) {
+ pr_debug("Mismatched PCC ids in _CPC for CPU:%d\n",
+ pr->id);
+ goto out_free;
+ }
+ } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ if (gas_t->address) {
+ void __iomem *addr;
+
+ if (!osc_cpc_flexible_adr_space_confirmed) {
+ pr_debug("Flexible address space capability not supported\n");
+ if (!cpc_supported_by_cpu())
+ goto out_free;
+ }
+
+ addr = ioremap(gas_t->address, gas_t->bit_width/8);
+ if (!addr)
+ goto out_free;
+ cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr;
+ }
+ } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ if (gas_t->access_width < 1 || gas_t->access_width > 3) {
+ /*
+ * 1 = 8-bit, 2 = 16-bit, and 3 = 32-bit.
+ * SystemIO doesn't implement 64-bit
+ * registers.
+ */
+ pr_debug("Invalid access width %d for SystemIO register in _CPC\n",
+ gas_t->access_width);
+ goto out_free;
+ }
+ if (gas_t->address & OVER_16BTS_MASK) {
+ /* SystemIO registers use 16-bit integer addresses */
+ pr_debug("Invalid IO port %llu for SystemIO register in _CPC\n",
+ gas_t->address);
+ goto out_free;
+ }
+ if (!osc_cpc_flexible_adr_space_confirmed) {
+ pr_debug("Flexible address space capability not supported\n");
+ if (!cpc_supported_by_cpu())
+ goto out_free;
+ }
+ } else {
+ if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) {
+ /* Support only PCC, SystemMemory, SystemIO, and FFH type regs. */
+ pr_debug("Unsupported register type (%d) in _CPC\n",
+ gas_t->space_id);
+ goto out_free;
+ }
+ }
+
+ cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER;
+ memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t));
+ } else {
+ pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n",
+ i, pr->id);
+ goto out_free;
+ }
+ }
+ per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id;
+
+ /*
+ * Initialize the remaining cpc_regs as unsupported.
+ * Example: In case FW exposes CPPC v2, the below loop will initialize
+ * LOWEST_FREQ and NOMINAL_FREQ regs as unsupported
+ */
+ for (i = num_ent - 2; i < MAX_CPC_REG_ENT; i++) {
+ cpc_ptr->cpc_regs[i].type = ACPI_TYPE_INTEGER;
+ cpc_ptr->cpc_regs[i].cpc_entry.int_value = 0;
+ }
+
+
+ /* Store CPU Logical ID */
+ cpc_ptr->cpu_id = pr->id;
+
+ /* Parse PSD data for this CPU */
+ ret = acpi_get_psd(cpc_ptr, handle);
+ if (ret)
+ goto out_free;
+
+ /* Register PCC channel once for all PCC subspace ID. */
+ if (pcc_subspace_id >= 0 && !pcc_data[pcc_subspace_id]->pcc_channel_acquired) {
+ ret = register_pcc_channel(pcc_subspace_id);
+ if (ret)
+ goto out_free;
+
+ init_rwsem(&pcc_data[pcc_subspace_id]->pcc_lock);
+ init_waitqueue_head(&pcc_data[pcc_subspace_id]->pcc_write_wait_q);
+ }
+
+ /* Everything looks okay */
+ pr_debug("Parsed CPC struct for CPU: %d\n", pr->id);
+
+ /* Add per logical CPU nodes for reading its feedback counters. */
+ cpu_dev = get_cpu_device(pr->id);
+ if (!cpu_dev) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ /* Plug PSD data into this CPU's CPC descriptor. */
+ per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr;
+
+ ret = kobject_init_and_add(&cpc_ptr->kobj, &cppc_ktype, &cpu_dev->kobj,
+ "acpi_cppc");
+ if (ret) {
+ per_cpu(cpc_desc_ptr, pr->id) = NULL;
+ kobject_put(&cpc_ptr->kobj);
+ goto out_free;
+ }
+
+ arch_init_invariance_cppc();
+
+ kfree(output.pointer);
+ return 0;
+
+out_free:
+ /* Free all the mapped sys mem areas for this CPU */
+ for (i = 2; i < cpc_ptr->num_entries; i++) {
+ void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
+
+ if (addr)
+ iounmap(addr);
+ }
+ kfree(cpc_ptr);
+
+out_buf_free:
+ kfree(output.pointer);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe);
+
+/**
+ * acpi_cppc_processor_exit - Cleanup CPC structs.
+ * @pr: Ptr to acpi_processor containing this CPU's logical ID.
+ *
+ * Return: Void
+ */
+void acpi_cppc_processor_exit(struct acpi_processor *pr)
+{
+ struct cpc_desc *cpc_ptr;
+ unsigned int i;
+ void __iomem *addr;
+ int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, pr->id);
+
+ if (pcc_ss_id >= 0 && pcc_data[pcc_ss_id]) {
+ if (pcc_data[pcc_ss_id]->pcc_channel_acquired) {
+ pcc_data[pcc_ss_id]->refcount--;
+ if (!pcc_data[pcc_ss_id]->refcount) {
+ pcc_mbox_free_channel(pcc_data[pcc_ss_id]->pcc_channel);
+ kfree(pcc_data[pcc_ss_id]);
+ pcc_data[pcc_ss_id] = NULL;
+ }
+ }
+ }
+
+ cpc_ptr = per_cpu(cpc_desc_ptr, pr->id);
+ if (!cpc_ptr)
+ return;
+
+ /* Free all the mapped sys mem areas for this CPU */
+ for (i = 2; i < cpc_ptr->num_entries; i++) {
+ addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
+ if (addr)
+ iounmap(addr);
+ }
+
+ kobject_put(&cpc_ptr->kobj);
+ kfree(cpc_ptr);
+}
+EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
+
+/**
+ * cpc_read_ffh() - Read FFH register
+ * @cpunum: CPU number to read
+ * @reg: cppc register information
+ * @val: place holder for return value
+ *
+ * Read bit_width bits from a specified address and bit_offset
+ *
+ * Return: 0 for success and error code
+ */
+int __weak cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
+{
+ return -ENOTSUPP;
+}
+
+/**
+ * cpc_write_ffh() - Write FFH register
+ * @cpunum: CPU number to write
+ * @reg: cppc register information
+ * @val: value to write
+ *
+ * Write value of bit_width bits to a specified address and bit_offset
+ *
+ * Return: 0 for success and error code
+ */
+int __weak cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
+{
+ return -ENOTSUPP;
+}
+
+/*
+ * Since cpc_read and cpc_write are called while holding pcc_lock, it should be
+ * as fast as possible. We have already mapped the PCC subspace during init, so
+ * we can directly write to it.
+ */
+
+static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *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;
+
+ if (reg_res->type == ACPI_TYPE_INTEGER) {
+ *val = reg_res->cpc_entry.int_value;
+ return 0;
+ }
+
+ *val = 0;
+
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ u32 width = 8 << (reg->access_width - 1);
+ u32 val_u32;
+ acpi_status status;
+
+ status = acpi_os_read_port((acpi_io_address)reg->address,
+ &val_u32, width);
+ if (ACPI_FAILURE(status)) {
+ pr_debug("Error: Failed to read SystemIO port %llx\n",
+ reg->address);
+ return -EFAULT;
+ }
+
+ *val = val_u32;
+ return 0;
+ } else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0)
+ 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);
+
+ switch (reg->bit_width) {
+ case 8:
+ *val = readb_relaxed(vaddr);
+ break;
+ case 16:
+ *val = readw_relaxed(vaddr);
+ break;
+ case 32:
+ *val = readl_relaxed(vaddr);
+ break;
+ case 64:
+ *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);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
+{
+ int ret_val = 0;
+ void __iomem *vaddr = NULL;
+ int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
+ struct cpc_reg *reg = &reg_res->cpc_entry.reg;
+
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ u32 width = 8 << (reg->access_width - 1);
+ acpi_status status;
+
+ status = acpi_os_write_port((acpi_io_address)reg->address,
+ (u32)val, width);
+ if (ACPI_FAILURE(status)) {
+ pr_debug("Error: Failed to write SystemIO port %llx\n",
+ reg->address);
+ return -EFAULT;
+ }
+
+ return 0;
+ } else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0)
+ 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);
+
+ switch (reg->bit_width) {
+ case 8:
+ writeb_relaxed(val, vaddr);
+ break;
+ case 16:
+ writew_relaxed(val, vaddr);
+ break;
+ case 32:
+ writel_relaxed(val, vaddr);
+ break;
+ case 64:
+ 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);
+ ret_val = -EFAULT;
+ break;
+ }
+
+ return ret_val;
+}
+
+static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf)
+{
+ struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
+ struct cpc_register_resource *reg;
+
+ if (!cpc_desc) {
+ pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
+ 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;
+
+ 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)
+ cpc_read(cpunum, reg, perf);
+ else
+ ret = -EIO;
+
+ up_write(&pcc_ss_data->pcc_lock);
+
+ return ret;
+ }
+
+ cpc_read(cpunum, reg, perf);
+
+ return 0;
+}
+
+/**
+ * cppc_get_desired_perf - Get the desired performance register value.
+ * @cpunum: CPU from which to get desired performance.
+ * @desired_perf: Return address.
+ *
+ * Return: 0 for success, -EIO otherwise.
+ */
+int cppc_get_desired_perf(int cpunum, u64 *desired_perf)
+{
+ return cppc_get_perf(cpunum, DESIRED_PERF, desired_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_get_desired_perf);
+
+/**
+ * cppc_get_nominal_perf - Get the nominal performance register value.
+ * @cpunum: CPU from which to get nominal performance.
+ * @nominal_perf: Return address.
+ *
+ * Return: 0 for success, -EIO otherwise.
+ */
+int cppc_get_nominal_perf(int cpunum, u64 *nominal_perf)
+{
+ return cppc_get_perf(cpunum, NOMINAL_PERF, nominal_perf);
+}
+
+/**
+ * cppc_get_perf_caps - Get a CPU's performance capabilities.
+ * @cpunum: CPU from which to get capabilities info.
+ * @perf_caps: ptr to cppc_perf_caps. See cppc_acpi.h
+ *
+ * Return: 0 for success with perf_caps populated else -ERRNO.
+ */
+int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
+{
+ struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
+ struct cpc_register_resource *highest_reg, *lowest_reg,
+ *lowest_non_linear_reg, *nominal_reg, *guaranteed_reg,
+ *low_freq_reg = NULL, *nom_freq_reg = NULL;
+ u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0;
+ int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
+ struct cppc_pcc_data *pcc_ss_data = NULL;
+ int ret = 0, regs_in_pcc = 0;
+
+ if (!cpc_desc) {
+ pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
+ return -ENODEV;
+ }
+
+ highest_reg = &cpc_desc->cpc_regs[HIGHEST_PERF];
+ lowest_reg = &cpc_desc->cpc_regs[LOWEST_PERF];
+ lowest_non_linear_reg = &cpc_desc->cpc_regs[LOW_NON_LINEAR_PERF];
+ nominal_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
+ low_freq_reg = &cpc_desc->cpc_regs[LOWEST_FREQ];
+ nom_freq_reg = &cpc_desc->cpc_regs[NOMINAL_FREQ];
+ guaranteed_reg = &cpc_desc->cpc_regs[GUARANTEED_PERF];
+
+ /* Are any of the regs PCC ?*/
+ if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
+ CPC_IN_PCC(lowest_non_linear_reg) || CPC_IN_PCC(nominal_reg) ||
+ CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg)) {
+ if (pcc_ss_id < 0) {
+ pr_debug("Invalid pcc_ss_id\n");
+ return -ENODEV;
+ }
+ pcc_ss_data = pcc_data[pcc_ss_id];
+ regs_in_pcc = 1;
+ down_write(&pcc_ss_data->pcc_lock);
+ /* Ring doorbell once to update PCC subspace */
+ if (send_pcc_cmd(pcc_ss_id, CMD_READ) < 0) {
+ ret = -EIO;
+ goto out_err;
+ }
+ }
+
+ cpc_read(cpunum, highest_reg, &high);
+ perf_caps->highest_perf = high;
+
+ cpc_read(cpunum, lowest_reg, &low);
+ perf_caps->lowest_perf = low;
+
+ cpc_read(cpunum, nominal_reg, &nom);
+ perf_caps->nominal_perf = nom;
+
+ if (guaranteed_reg->type != ACPI_TYPE_BUFFER ||
+ IS_NULL_REG(&guaranteed_reg->cpc_entry.reg)) {
+ perf_caps->guaranteed_perf = 0;
+ } else {
+ cpc_read(cpunum, guaranteed_reg, &guaranteed);
+ perf_caps->guaranteed_perf = guaranteed;
+ }
+
+ cpc_read(cpunum, lowest_non_linear_reg, &min_nonlinear);
+ perf_caps->lowest_nonlinear_perf = min_nonlinear;
+
+ if (!high || !low || !nom || !min_nonlinear)
+ ret = -EFAULT;
+
+ /* Read optional lowest and nominal frequencies if present */
+ if (CPC_SUPPORTED(low_freq_reg))
+ cpc_read(cpunum, low_freq_reg, &low_f);
+
+ if (CPC_SUPPORTED(nom_freq_reg))
+ cpc_read(cpunum, nom_freq_reg, &nom_f);
+
+ perf_caps->lowest_freq = low_f;
+ perf_caps->nominal_freq = nom_f;
+
+
+out_err:
+ if (regs_in_pcc)
+ up_write(&pcc_ss_data->pcc_lock);
+ return ret;
+}
+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_present_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
+ *
+ * Return: 0 for success with perf_fb_ctrs populated else -ERRNO.
+ */
+int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
+{
+ struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
+ struct cpc_register_resource *delivered_reg, *reference_reg,
+ *ref_perf_reg, *ctr_wrap_reg;
+ int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
+ struct cppc_pcc_data *pcc_ss_data = NULL;
+ u64 delivered, reference, ref_perf, ctr_wrap_time;
+ int ret = 0, regs_in_pcc = 0;
+
+ if (!cpc_desc) {
+ pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
+ return -ENODEV;
+ }
+
+ delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR];
+ reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR];
+ ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF];
+ ctr_wrap_reg = &cpc_desc->cpc_regs[CTR_WRAP_TIME];
+
+ /*
+ * 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];
+
+ /* Are any of the regs PCC ?*/
+ if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg) ||
+ CPC_IN_PCC(ctr_wrap_reg) || CPC_IN_PCC(ref_perf_reg)) {
+ 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);
+ regs_in_pcc = 1;
+ /* Ring doorbell once to update PCC subspace */
+ if (send_pcc_cmd(pcc_ss_id, CMD_READ) < 0) {
+ ret = -EIO;
+ goto out_err;
+ }
+ }
+
+ cpc_read(cpunum, delivered_reg, &delivered);
+ cpc_read(cpunum, reference_reg, &reference);
+ cpc_read(cpunum, ref_perf_reg, &ref_perf);
+
+ /*
+ * Per spec, if ctr_wrap_time optional register is unsupported, then the
+ * performance counters are assumed to never wrap during the lifetime of
+ * platform
+ */
+ ctr_wrap_time = (u64)(~((u64)0));
+ if (CPC_SUPPORTED(ctr_wrap_reg))
+ cpc_read(cpunum, ctr_wrap_reg, &ctr_wrap_time);
+
+ if (!delivered || !reference || !ref_perf) {
+ ret = -EFAULT;
+ goto out_err;
+ }
+
+ perf_fb_ctrs->delivered = delivered;
+ perf_fb_ctrs->reference = reference;
+ perf_fb_ctrs->reference_perf = ref_perf;
+ perf_fb_ctrs->wraparound_time = ctr_wrap_time;
+out_err:
+ if (regs_in_pcc)
+ up_write(&pcc_ss_data->pcc_lock);
+ return ret;
+}
+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.
+ */
+int cppc_set_enable(int cpu, bool enable)
+{
+ int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
+ struct cpc_register_resource *enable_reg;
+ struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+ struct cppc_pcc_data *pcc_ss_data = NULL;
+ int ret = -EINVAL;
+
+ if (!cpc_desc) {
+ pr_debug("No CPC descriptor for CPU:%d\n", cpu);
+ return -EINVAL;
+ }
+
+ enable_reg = &cpc_desc->cpc_regs[ENABLE];
+
+ if (CPC_IN_PCC(enable_reg)) {
+
+ if (pcc_ss_id < 0)
+ return -EIO;
+
+ ret = cpc_write(cpu, enable_reg, enable);
+ 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 */
+ ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
+ up_write(&pcc_ss_data->pcc_lock);
+ return ret;
+ }
+
+ return cpc_write(cpu, enable_reg, enable);
+}
+EXPORT_SYMBOL_GPL(cppc_set_enable);
+
+/**
+ * cppc_set_perf - Set a CPU's performance controls.
+ * @cpu: CPU for which to set performance controls.
+ * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h
+ *
+ * Return: 0 for success, -ERRNO otherwise.
+ */
+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;
+ int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
+ struct cppc_pcc_data *pcc_ss_data = NULL;
+ int ret = 0;
+
+ if (!cpc_desc) {
+ pr_debug("No CPC descriptor for CPU:%d\n", cpu);
+ return -ENODEV;
+ }
+
+ desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
+
+ /*
+ * This is Phase-I where we want to write to CPC registers
+ * -> We want all CPUs to be able to execute this phase in parallel
+ *
+ * Since read_lock can be acquired by multiple CPUs simultaneously we
+ * achieve that goal here
+ */
+ if (CPC_IN_PCC(desired_reg)) {
+ if (pcc_ss_id < 0) {
+ pr_debug("Invalid pcc_ss_id\n");
+ return -ENODEV;
+ }
+ pcc_ss_data = pcc_data[pcc_ss_id];
+ down_read(&pcc_ss_data->pcc_lock); /* BEGIN Phase-I */
+ if (pcc_ss_data->platform_owns_pcc) {
+ ret = check_pcc_chan(pcc_ss_id, false);
+ if (ret) {
+ up_read(&pcc_ss_data->pcc_lock);
+ return ret;
+ }
+ }
+ /*
+ * Update the pending_write to make sure a PCC CMD_READ will not
+ * arrive and steal the channel during the switch to write lock
+ */
+ pcc_ss_data->pending_pcc_write_cmd = true;
+ cpc_desc->write_cmd_id = pcc_ss_data->pcc_write_cnt;
+ cpc_desc->write_cmd_status = 0;
+ }
+
+ /*
+ * Skip writing MIN/MAX until Linux knows how to come up with
+ * useful values.
+ */
+ cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
+
+ if (CPC_IN_PCC(desired_reg))
+ up_read(&pcc_ss_data->pcc_lock); /* END Phase-I */
+ /*
+ * This is Phase-II where we transfer the ownership of PCC to Platform
+ *
+ * Short Summary: Basically if we think of a group of cppc_set_perf
+ * requests that happened in short overlapping interval. The last CPU to
+ * come out of Phase-I will enter Phase-II and ring the doorbell.
+ *
+ * We have the following requirements for Phase-II:
+ * 1. We want to execute Phase-II only when there are no CPUs
+ * currently executing in Phase-I
+ * 2. Once we start Phase-II we want to avoid all other CPUs from
+ * entering Phase-I.
+ * 3. We want only one CPU among all those who went through Phase-I
+ * to run phase-II
+ *
+ * If write_trylock fails to get the lock and doesn't transfer the
+ * PCC ownership to the platform, then one of the following will be TRUE
+ * 1. There is at-least one CPU in Phase-I which will later execute
+ * write_trylock, so the CPUs in Phase-I will be responsible for
+ * executing the Phase-II.
+ * 2. Some other CPU has beaten this CPU to successfully execute the
+ * write_trylock and has already acquired the write_lock. We know for a
+ * fact it (other CPU acquiring the write_lock) couldn't have happened
+ * before this CPU's Phase-I as we held the read_lock.
+ * 3. Some other CPU executing pcc CMD_READ has stolen the
+ * down_write, in which case, send_pcc_cmd will check for pending
+ * CMD_WRITE commands by checking the pending_pcc_write_cmd.
+ * So this CPU can be certain that its request will be delivered
+ * So in all cases, this CPU knows that its request will be delivered
+ * by another CPU and can return
+ *
+ * After getting the down_write we still need to check for
+ * pending_pcc_write_cmd to take care of the following scenario
+ * The thread running this code could be scheduled out between
+ * Phase-I and Phase-II. Before it is scheduled back on, another CPU
+ * could have delivered the request to Platform by triggering the
+ * doorbell and transferred the ownership of PCC to platform. So this
+ * avoids triggering an unnecessary doorbell and more importantly before
+ * triggering the doorbell it makes sure that the PCC channel ownership
+ * is still with OSPM.
+ * pending_pcc_write_cmd can also be cleared by a different CPU, if
+ * there was a pcc CMD_READ waiting on down_write and it steals the lock
+ * before the pcc CMD_WRITE is completed. send_pcc_cmd checks for this
+ * 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 (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)
+ send_pcc_cmd(pcc_ss_id, CMD_WRITE);
+ up_write(&pcc_ss_data->pcc_lock); /* END Phase-II */
+ } else
+ /* Wait until pcc_write_cnt is updated by send_pcc_cmd */
+ wait_event(pcc_ss_data->pcc_write_wait_q,
+ cpc_desc->write_cmd_id != pcc_ss_data->pcc_write_cnt);
+
+ /* send_pcc_cmd updates the status in case of failure */
+ ret = cpc_desc->write_cmd_status;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cppc_set_perf);
+
+/**
+ * cppc_get_transition_latency - returns frequency transition latency in ns
+ *
+ * ACPI CPPC does not explicitly specify how a platform can specify the
+ * transition latency for performance change requests. The closest we have
+ * is the timing information from the PCCT tables which provides the info
+ * on the number and frequency of PCC commands the platform can handle.
+ *
+ * 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)
+{
+ /*
+ * Expected transition latency is based on the PCCT timing values
+ * Below are definition from ACPI spec:
+ * pcc_nominal- Expected latency to process a command, in microseconds
+ * pcc_mpar - The maximum number of periodic requests that the subspace
+ * channel can support, reported in commands per minute. 0
+ * indicates no limitation.
+ * pcc_mrtt - The minimum amount of time that OSPM must wait after the
+ * 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;
+
+ cpc_desc = per_cpu(cpc_desc_ptr, cpu_num);
+ if (!cpc_desc)
+ return CPUFREQ_ETERNAL;
+
+ 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;
+
+ 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);
+
+ return latency_ns;
+}
+EXPORT_SYMBOL_GPL(cppc_get_transition_latency);
diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c
new file mode 100644
index 000000000..d39a9b474
--- /dev/null
+++ b/drivers/acpi/custom_method.c
@@ -0,0 +1,103 @@
+// 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/debugfs.c b/drivers/acpi/debugfs.c
new file mode 100644
index 000000000..074eb98d2
--- /dev/null
+++ b/drivers/acpi/debugfs.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * debugfs.c - ACPI debugfs interface to userspace.
+ */
+
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/acpi.h>
+
+#include "internal.h"
+
+struct dentry *acpi_debugfs_dir;
+EXPORT_SYMBOL_GPL(acpi_debugfs_dir);
+
+void __init acpi_debugfs_init(void)
+{
+ acpi_debugfs_dir = debugfs_create_dir("acpi", NULL);
+}
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
new file mode 100644
index 000000000..f007116a8
--- /dev/null
+++ b/drivers/acpi/device_pm.c
@@ -0,0 +1,1517 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/acpi/device_pm.c - ACPI device power management routines.
+ *
+ * Copyright (C) 2012, Intel Corp.
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#define pr_fmt(fmt) "PM: " fmt
+
+#include <linux/acpi.h>
+#include <linux/export.h>
+#include <linux/mutex.h>
+#include <linux/pm_qos.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+
+#include "fan.h"
+#include "internal.h"
+
+/**
+ * acpi_power_state_string - String representation of ACPI device power state.
+ * @state: ACPI device power state to return the string representation of.
+ */
+const char *acpi_power_state_string(int state)
+{
+ switch (state) {
+ case ACPI_STATE_D0:
+ return "D0";
+ case ACPI_STATE_D1:
+ return "D1";
+ case ACPI_STATE_D2:
+ return "D2";
+ case ACPI_STATE_D3_HOT:
+ return "D3hot";
+ case ACPI_STATE_D3_COLD:
+ return "D3cold";
+ default:
+ return "(unknown)";
+ }
+}
+
+static int acpi_dev_pm_explicit_get(struct acpi_device *device, int *state)
+{
+ unsigned long long psc;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(device->handle, "_PSC", NULL, &psc);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ *state = psc;
+ return 0;
+}
+
+/**
+ * acpi_device_get_power - Get power state of an ACPI device.
+ * @device: Device to get the power state of.
+ * @state: Place to store the power state of the device.
+ *
+ * This function does not update the device's power.state field, but it may
+ * update its parent's power.state field (when the parent's power state is
+ * unknown and the device's power state turns out to be D0).
+ *
+ * Also, it does not update power resource reference counters to ensure that
+ * the power state returned by it will be persistent and it may return a power
+ * state shallower than previously set by acpi_device_set_power() for @device
+ * (if that power state depends on any power resources).
+ */
+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 = parent ? parent->power.state : ACPI_STATE_D0;
+ goto out;
+ }
+
+ /*
+ * Get the device's power state from power resources settings and _PSC,
+ * if available.
+ */
+ if (device->power.flags.power_resources) {
+ error = acpi_power_get_inferred_state(device, &result);
+ if (error)
+ return error;
+ }
+ if (device->power.flags.explicit_get) {
+ int psc;
+
+ error = acpi_dev_pm_explicit_get(device, &psc);
+ if (error)
+ return error;
+
+ /*
+ * The power resources settings may indicate a power state
+ * shallower than the actual power state of the device, because
+ * the same power resources may be referenced by other devices.
+ *
+ * For systems predating ACPI 4.0 we assume that D3hot is the
+ * deepest state that can be supported.
+ */
+ if (psc > result && psc < ACPI_STATE_D3_COLD)
+ result = psc;
+ else if (result == ACPI_STATE_UNKNOWN)
+ result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_HOT : psc;
+ }
+
+ /*
+ * If we were unsure about the device parent's power state up to this
+ * 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 && parent &&
+ parent->power.state == ACPI_STATE_UNKNOWN &&
+ result == ACPI_STATE_D0)
+ parent->power.state = ACPI_STATE_D0;
+
+ *state = result;
+
+ out:
+ acpi_handle_debug(device->handle, "Power state: %s\n",
+ acpi_power_state_string(*state));
+
+ return 0;
+}
+
+static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state)
+{
+ if (adev->power.states[state].flags.explicit_set) {
+ char method[5] = { '_', 'P', 'S', '0' + state, '\0' };
+ acpi_status status;
+
+ status = acpi_evaluate_object(adev->handle, method, NULL, NULL);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/**
+ * acpi_device_set_power - Set power state of an ACPI device.
+ * @device: Device to set the power state of.
+ * @state: New power state to set.
+ *
+ * Callers must ensure that the device is power manageable before using this
+ * function.
+ */
+int acpi_device_set_power(struct acpi_device *device, int state)
+{
+ int target_state = state;
+ int result = 0;
+
+ if (!device || !device->flags.power_manageable
+ || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
+ return -EINVAL;
+
+ acpi_handle_debug(device->handle, "Power state change: %s -> %s\n",
+ acpi_power_state_string(device->power.state),
+ acpi_power_state_string(state));
+
+ /* Make sure this is a valid target state */
+
+ /* There is a special case for D0 addressed below. */
+ if (state > ACPI_STATE_D0 && state == device->power.state)
+ goto no_change;
+
+ if (state == ACPI_STATE_D3_COLD) {
+ /*
+ * For transitions to D3cold we need to execute _PS3 and then
+ * possibly drop references to the power resources in use.
+ */
+ state = ACPI_STATE_D3_HOT;
+ /* If D3cold is not supported, use D3hot as the target state. */
+ if (!device->power.states[ACPI_STATE_D3_COLD].flags.valid)
+ target_state = state;
+ } else if (!device->power.states[state].flags.valid) {
+ acpi_handle_debug(device->handle, "Power state %s not supported\n",
+ acpi_power_state_string(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;
+ }
+ }
+
+ /*
+ * Transition Power
+ * ----------------
+ * In accordance with ACPI 6, _PSx is executed before manipulating power
+ * resources, unless the target state is D0, in which case _PS0 is
+ * supposed to be executed after turning the power resources on.
+ */
+ if (state > ACPI_STATE_D0) {
+ /*
+ * According to ACPI 6, devices cannot go from lower-power
+ * (deeper) states to higher-power (shallower) states.
+ */
+ if (state < device->power.state) {
+ acpi_handle_debug(device->handle,
+ "Cannot transition from %s to %s\n",
+ acpi_power_state_string(device->power.state),
+ acpi_power_state_string(state));
+ return -ENODEV;
+ }
+
+ /*
+ * If the device goes from D3hot to D3cold, _PS3 has been
+ * evaluated for it already, so skip it in that case.
+ */
+ if (device->power.state < ACPI_STATE_D3_HOT) {
+ result = acpi_dev_pm_explicit_set(device, state);
+ if (result)
+ goto end;
+ }
+
+ if (device->power.flags.power_resources)
+ result = acpi_power_transition(device, target_state);
+ } else {
+ int cur_state = device->power.state;
+
+ if (device->power.flags.power_resources) {
+ result = acpi_power_transition(device, ACPI_STATE_D0);
+ if (result)
+ goto end;
+ }
+
+ if (cur_state == ACPI_STATE_D0) {
+ int psc;
+
+ /* Nothing to do here if _PSC is not present. */
+ if (!device->power.flags.explicit_get)
+ goto no_change;
+
+ /*
+ * The power state of the device was set to D0 last
+ * time, but that might have happened before a
+ * system-wide transition involving the platform
+ * firmware, so it may be necessary to evaluate _PS0
+ * for the device here. However, use extra care here
+ * and evaluate _PSC to check the device's current power
+ * state, and only invoke _PS0 if the evaluation of _PSC
+ * is successful and it returns a power state different
+ * from D0.
+ */
+ result = acpi_dev_pm_explicit_get(device, &psc);
+ if (result || psc == ACPI_STATE_D0)
+ goto no_change;
+ }
+
+ result = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0);
+ }
+
+end:
+ if (result) {
+ acpi_handle_debug(device->handle,
+ "Failed to change power state to %s\n",
+ acpi_power_state_string(target_state));
+ } else {
+ device->power.state = target_state;
+ acpi_handle_debug(device->handle, "Power state changed to %s\n",
+ acpi_power_state_string(target_state));
+ }
+
+ return result;
+
+no_change:
+ acpi_handle_debug(device->handle, "Already in %s\n",
+ acpi_power_state_string(state));
+ return 0;
+}
+EXPORT_SYMBOL(acpi_device_set_power);
+
+int acpi_bus_set_power(acpi_handle handle, int state)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+
+ if (device)
+ return acpi_device_set_power(device, state);
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL(acpi_bus_set_power);
+
+int acpi_bus_init_power(struct acpi_device *device)
+{
+ int state;
+ int result;
+
+ if (!device)
+ return -EINVAL;
+
+ device->power.state = ACPI_STATE_UNKNOWN;
+ if (!acpi_device_is_present(device)) {
+ device->flags.initialized = false;
+ return -ENXIO;
+ }
+
+ result = acpi_device_get_power(device, &state);
+ if (result)
+ return result;
+
+ if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) {
+ /* Reference count the power resources. */
+ result = acpi_power_on_resources(device, state);
+ if (result)
+ return result;
+
+ if (state == ACPI_STATE_D0) {
+ /*
+ * If _PSC is not present and the state inferred from
+ * power resources appears to be D0, it still may be
+ * necessary to execute _PS0 at this point, because
+ * another device using the same power resources may
+ * have been put into D0 previously and that's why we
+ * see D0 here.
+ */
+ result = acpi_dev_pm_explicit_set(device, state);
+ if (result)
+ return result;
+ }
+ } else if (state == ACPI_STATE_UNKNOWN) {
+ /*
+ * No power resources and missing _PSC? Cross fingers and make
+ * it D0 in hope that this is what the BIOS put the device into.
+ * [We tried to force D0 here by executing _PS0, but that broke
+ * Toshiba P870-303 in a nasty way.]
+ */
+ state = ACPI_STATE_D0;
+ }
+ device->power.state = state;
+ return 0;
+}
+
+/**
+ * acpi_device_fix_up_power - Force device with missing _PSC into D0.
+ * @device: Device object whose power state is to be fixed up.
+ *
+ * Devices without power resources and _PSC, but having _PS0 and _PS3 defined,
+ * are assumed to be put into D0 by the BIOS. However, in some cases that may
+ * not be the case and this function should be used then.
+ */
+int acpi_device_fix_up_power(struct acpi_device *device)
+{
+ int ret = 0;
+
+ if (!device->power.flags.power_resources
+ && !device->power.flags.explicit_get
+ && device->power.state == ACPI_STATE_D0)
+ ret = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_device_fix_up_power);
+
+static int fix_up_power_if_applicable(struct acpi_device *adev, void *not_used)
+{
+ if (adev->status.present && adev->status.enabled)
+ acpi_device_fix_up_power(adev);
+
+ return 0;
+}
+
+/**
+ * acpi_device_fix_up_power_extended - Force device and its children into D0.
+ * @adev: Parent device object whose power state is to be fixed up.
+ *
+ * Call acpi_device_fix_up_power() for @adev and its children so long as they
+ * are reported as present and enabled.
+ */
+void acpi_device_fix_up_power_extended(struct acpi_device *adev)
+{
+ acpi_device_fix_up_power(adev);
+ acpi_dev_for_each_child(adev, fix_up_power_if_applicable, NULL);
+}
+EXPORT_SYMBOL_GPL(acpi_device_fix_up_power_extended);
+
+int acpi_device_update_power(struct acpi_device *device, int *state_p)
+{
+ int state;
+ int result;
+
+ if (device->power.state == ACPI_STATE_UNKNOWN) {
+ result = acpi_bus_init_power(device);
+ if (!result && state_p)
+ *state_p = device->power.state;
+
+ return result;
+ }
+
+ result = acpi_device_get_power(device, &state);
+ if (result)
+ return result;
+
+ if (state == ACPI_STATE_UNKNOWN) {
+ state = ACPI_STATE_D0;
+ result = acpi_device_set_power(device, state);
+ if (result)
+ return result;
+ } else {
+ if (device->power.flags.power_resources) {
+ /*
+ * We don't need to really switch the state, bu we need
+ * to update the power resources' reference counters.
+ */
+ result = acpi_power_transition(device, state);
+ if (result)
+ return result;
+ }
+ device->power.state = state;
+ }
+ if (state_p)
+ *state_p = state;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_device_update_power);
+
+int acpi_bus_update_power(acpi_handle handle, int *state_p)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+
+ if (device)
+ return acpi_device_update_power(device, state_p);
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_update_power);
+
+bool acpi_bus_power_manageable(acpi_handle handle)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+
+ return device && device->flags.power_manageable;
+}
+EXPORT_SYMBOL(acpi_bus_power_manageable);
+
+static int acpi_power_up_if_adr_present(struct acpi_device *adev, void *not_used)
+{
+ if (!(adev->flags.power_manageable && adev->pnp.type.bus_address))
+ return 0;
+
+ acpi_handle_debug(adev->handle, "Power state: %s\n",
+ acpi_power_state_string(adev->power.state));
+
+ if (adev->power.state == ACPI_STATE_D3_COLD)
+ return acpi_device_set_power(adev, ACPI_STATE_D0);
+
+ return 0;
+}
+
+/**
+ * acpi_dev_power_up_children_with_adr - Power up childres with valid _ADR
+ * @adev: Parent ACPI device object.
+ *
+ * Change the power states of the direct children of @adev that are in D3cold
+ * and hold valid _ADR objects to D0 in order to allow bus (e.g. PCI)
+ * enumeration code to access them.
+ */
+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);
+
+void acpi_pm_wakeup_event(struct device *dev)
+{
+ pm_wakeup_dev_event(dev, 0, acpi_s2idle_wakeup());
+}
+EXPORT_SYMBOL_GPL(acpi_pm_wakeup_event);
+
+static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
+{
+ struct acpi_device *adev;
+
+ if (val != ACPI_NOTIFY_DEVICE_WAKE)
+ return;
+
+ acpi_handle_debug(handle, "Wake notify\n");
+
+ adev = acpi_get_acpi_dev(handle);
+ if (!adev)
+ return;
+
+ mutex_lock(&acpi_pm_notifier_lock);
+
+ if (adev->wakeup.flags.notifier_present) {
+ pm_wakeup_ws_event(adev->wakeup.ws, 0, acpi_s2idle_wakeup());
+ if (adev->wakeup.context.func) {
+ acpi_handle_debug(handle, "Running %pS for %s\n",
+ adev->wakeup.context.func,
+ dev_name(adev->wakeup.context.dev));
+ adev->wakeup.context.func(&adev->wakeup.context);
+ }
+ }
+
+ mutex_unlock(&acpi_pm_notifier_lock);
+
+ acpi_put_acpi_dev(adev);
+}
+
+/**
+ * acpi_add_pm_notifier - Register PM notify handler for given ACPI device.
+ * @adev: ACPI device to add the notify handler for.
+ * @dev: Device to generate a wakeup event for while handling the notification.
+ * @func: Work function to execute when handling the notification.
+ *
+ * NOTE: @adev need not be a run-wake or wakeup device to be a valid source of
+ * PM wakeup events. For example, wakeup events may be generated for bridges
+ * if one of the devices below the bridge is signaling wakeup, even if the
+ * bridge itself doesn't have a wakeup GPE associated with it.
+ */
+acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
+ void (*func)(struct acpi_device_wakeup_context *context))
+{
+ acpi_status status = AE_ALREADY_EXISTS;
+
+ if (!dev && !func)
+ return AE_BAD_PARAMETER;
+
+ mutex_lock(&acpi_pm_notifier_install_lock);
+
+ if (adev->wakeup.flags.notifier_present)
+ goto out;
+
+ status = acpi_install_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY,
+ acpi_pm_notify_handler, NULL);
+ if (ACPI_FAILURE(status))
+ goto out;
+
+ mutex_lock(&acpi_pm_notifier_lock);
+ adev->wakeup.ws = wakeup_source_register(&adev->dev,
+ dev_name(&adev->dev));
+ adev->wakeup.context.dev = dev;
+ adev->wakeup.context.func = func;
+ adev->wakeup.flags.notifier_present = true;
+ mutex_unlock(&acpi_pm_notifier_lock);
+
+ out:
+ mutex_unlock(&acpi_pm_notifier_install_lock);
+ return status;
+}
+
+/**
+ * acpi_remove_pm_notifier - Unregister PM notifier from given ACPI device.
+ * @adev: ACPI device to remove the notifier from.
+ */
+acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
+{
+ acpi_status status = AE_BAD_PARAMETER;
+
+ mutex_lock(&acpi_pm_notifier_install_lock);
+
+ if (!adev->wakeup.flags.notifier_present)
+ goto out;
+
+ status = acpi_remove_notify_handler(adev->handle,
+ ACPI_SYSTEM_NOTIFY,
+ acpi_pm_notify_handler);
+ if (ACPI_FAILURE(status))
+ goto out;
+
+ mutex_lock(&acpi_pm_notifier_lock);
+ adev->wakeup.context.func = NULL;
+ adev->wakeup.context.dev = NULL;
+ wakeup_source_unregister(adev->wakeup.ws);
+ adev->wakeup.flags.notifier_present = false;
+ mutex_unlock(&acpi_pm_notifier_lock);
+
+ out:
+ mutex_unlock(&acpi_pm_notifier_install_lock);
+ return status;
+}
+
+bool acpi_bus_can_wakeup(acpi_handle handle)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+
+ return device && device->wakeup.flags.valid;
+}
+EXPORT_SYMBOL(acpi_bus_can_wakeup);
+
+bool acpi_pm_device_can_wakeup(struct device *dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+
+ return adev ? acpi_device_can_wakeup(adev) : false;
+}
+
+/**
+ * acpi_dev_pm_get_state - Get preferred power state of ACPI device.
+ * @dev: Device whose preferred target power state to return.
+ * @adev: ACPI device node corresponding to @dev.
+ * @target_state: System state to match the resultant device state.
+ * @d_min_p: Location to store the highest power state available to the device.
+ * @d_max_p: Location to store the lowest power state available to the device.
+ *
+ * Find the lowest power (highest number) and highest power (lowest number) ACPI
+ * device power states that the device can be in while the system is in the
+ * state represented by @target_state. Store the integer numbers representing
+ * those stats in the memory locations pointed to by @d_max_p and @d_min_p,
+ * respectively.
+ *
+ * Callers must ensure that @dev and @adev are valid pointers and that @adev
+ * actually corresponds to @dev before using this function.
+ *
+ * Returns 0 on success or -ENODATA when one of the ACPI methods fails or
+ * returns a value that doesn't make sense. The memory locations pointed to by
+ * @d_max_p and @d_min_p are only modified on success.
+ */
+static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev,
+ u32 target_state, int *d_min_p, int *d_max_p)
+{
+ char method[] = { '_', 'S', '0' + target_state, 'D', '\0' };
+ acpi_handle handle = adev->handle;
+ unsigned long long ret;
+ int d_min, d_max;
+ bool wakeup = false;
+ bool has_sxd = false;
+ acpi_status status;
+
+ /*
+ * If the system state is S0, the lowest power state the device can be
+ * in is D3cold, unless the device has _S0W and is supposed to signal
+ * wakeup, in which case the return value of _S0W has to be used as the
+ * lowest power state available to the device.
+ */
+ d_min = ACPI_STATE_D0;
+ d_max = ACPI_STATE_D3_COLD;
+
+ /*
+ * If present, _SxD methods return the minimum D-state (highest power
+ * state) we can use for the corresponding S-states. Otherwise, the
+ * minimum D-state is D0 (ACPI 3.x).
+ */
+ if (target_state > ACPI_STATE_S0) {
+ /*
+ * We rely on acpi_evaluate_integer() not clobbering the integer
+ * provided if AE_NOT_FOUND is returned.
+ */
+ ret = d_min;
+ status = acpi_evaluate_integer(handle, method, NULL, &ret);
+ if ((ACPI_FAILURE(status) && status != AE_NOT_FOUND)
+ || ret > ACPI_STATE_D3_COLD)
+ return -ENODATA;
+
+ /*
+ * We need to handle legacy systems where D3hot and D3cold are
+ * the same and 3 is returned in both cases, so fall back to
+ * D3cold if D3hot is not a valid state.
+ */
+ if (!adev->power.states[ret].flags.valid) {
+ if (ret == ACPI_STATE_D3_HOT)
+ ret = ACPI_STATE_D3_COLD;
+ else
+ return -ENODATA;
+ }
+
+ if (status == AE_OK)
+ has_sxd = true;
+
+ 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;
+ }
+
+ /*
+ * If _PRW says we can wake up the system from the target sleep state,
+ * the D-state returned by _SxD is sufficient for that (we assume a
+ * wakeup-aware driver if wake is set). Still, if _SxW exists
+ * (ACPI 3.x), it should return the maximum (lowest power) D-state that
+ * can wake the system. _S0W may be valid, too.
+ */
+ if (wakeup) {
+ method[3] = 'W';
+ status = acpi_evaluate_integer(handle, method, NULL, &ret);
+ if (status == AE_NOT_FOUND) {
+ /* No _SxW. In this case, the ACPI spec says that we
+ * must not go into any power state deeper than the
+ * value returned from _SxD.
+ */
+ if (has_sxd && target_state > ACPI_STATE_S0)
+ d_max = d_min;
+ } else if (ACPI_SUCCESS(status) && ret <= ACPI_STATE_D3_COLD) {
+ /* Fall back to D3cold if ret is not a valid state. */
+ if (!adev->power.states[ret].flags.valid)
+ ret = ACPI_STATE_D3_COLD;
+
+ d_max = ret > d_min ? ret : d_min;
+ } else {
+ return -ENODATA;
+ }
+ }
+
+ if (d_min_p)
+ *d_min_p = d_min;
+
+ if (d_max_p)
+ *d_max_p = d_max;
+
+ return 0;
+}
+
+/**
+ * acpi_pm_device_sleep_state - Get preferred power state of ACPI device.
+ * @dev: Device whose preferred target power state to return.
+ * @d_min_p: Location to store the upper limit of the allowed states range.
+ * @d_max_in: Deepest low-power state to take into consideration.
+ * Return value: Preferred power state of the device on success, -ENODEV
+ * if there's no 'struct acpi_device' for @dev, -EINVAL if @d_max_in is
+ * incorrect, or -ENODATA on ACPI method failure.
+ *
+ * The caller must ensure that @dev is valid before using this function.
+ */
+int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
+{
+ struct acpi_device *adev;
+ int ret, d_min, d_max;
+
+ if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD)
+ return -EINVAL;
+
+ if (d_max_in > ACPI_STATE_D2) {
+ enum pm_qos_flags_status stat;
+
+ stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF);
+ if (stat == PM_QOS_FLAGS_ALL)
+ d_max_in = ACPI_STATE_D2;
+ }
+
+ adev = ACPI_COMPANION(dev);
+ if (!adev) {
+ dev_dbg(dev, "ACPI companion missing in %s!\n", __func__);
+ return -ENODEV;
+ }
+
+ ret = acpi_dev_pm_get_state(dev, adev, acpi_target_system_state(),
+ &d_min, &d_max);
+ if (ret)
+ return ret;
+
+ if (d_max_in < d_min)
+ return -EINVAL;
+
+ if (d_max > d_max_in) {
+ for (d_max = d_max_in; d_max > d_min; d_max--) {
+ if (adev->power.states[d_max].flags.valid)
+ break;
+ }
+ }
+
+ if (d_min_p)
+ *d_min_p = d_min;
+
+ return d_max;
+}
+EXPORT_SYMBOL(acpi_pm_device_sleep_state);
+
+/**
+ * acpi_pm_notify_work_func - ACPI devices wakeup notification work function.
+ * @context: Device wakeup context.
+ */
+static void acpi_pm_notify_work_func(struct acpi_device_wakeup_context *context)
+{
+ struct device *dev = context->dev;
+
+ if (dev) {
+ pm_wakeup_event(dev, 0);
+ pm_request_resume(dev);
+ }
+}
+
+static DEFINE_MUTEX(acpi_wakeup_lock);
+
+static int __acpi_device_wakeup_enable(struct acpi_device *adev,
+ u32 target_state)
+{
+ struct acpi_device_wakeup *wakeup = &adev->wakeup;
+ acpi_status status;
+ int error = 0;
+
+ mutex_lock(&acpi_wakeup_lock);
+
+ /*
+ * If the device wakeup power is already enabled, disable it and enable
+ * it again in case it depends on the configuration of subordinate
+ * devices and the conditions have changed since it was enabled last
+ * time.
+ */
+ if (wakeup->enable_count > 0)
+ acpi_disable_wakeup_device_power(adev);
+
+ error = acpi_enable_wakeup_device_power(adev, target_state);
+ if (error) {
+ if (wakeup->enable_count > 0) {
+ acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
+ wakeup->enable_count = 0;
+ }
+ goto out;
+ }
+
+ if (wakeup->enable_count > 0)
+ goto inc;
+
+ status = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
+ if (ACPI_FAILURE(status)) {
+ acpi_disable_wakeup_device_power(adev);
+ error = -EIO;
+ goto out;
+ }
+
+ acpi_handle_debug(adev->handle, "GPE%2X enabled for wakeup\n",
+ (unsigned int)wakeup->gpe_number);
+
+inc:
+ if (wakeup->enable_count < INT_MAX)
+ wakeup->enable_count++;
+ else
+ acpi_handle_info(adev->handle, "Wakeup enable count out of bounds!\n");
+
+out:
+ mutex_unlock(&acpi_wakeup_lock);
+ return error;
+}
+
+/**
+ * acpi_device_wakeup_enable - Enable wakeup functionality for device.
+ * @adev: ACPI device to enable wakeup functionality for.
+ * @target_state: State the system is transitioning into.
+ *
+ * Enable the GPE associated with @adev so that it can generate wakeup signals
+ * for the device in response to external (remote) events and enable wakeup
+ * power for it.
+ *
+ * Callers must ensure that @adev is a valid ACPI device node before executing
+ * this function.
+ */
+static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
+{
+ return __acpi_device_wakeup_enable(adev, target_state);
+}
+
+/**
+ * acpi_device_wakeup_disable - Disable wakeup functionality for device.
+ * @adev: ACPI device to disable wakeup functionality for.
+ *
+ * Disable the GPE associated with @adev and disable wakeup power for it.
+ *
+ * Callers must ensure that @adev is a valid ACPI device node before executing
+ * this function.
+ */
+static void acpi_device_wakeup_disable(struct acpi_device *adev)
+{
+ struct acpi_device_wakeup *wakeup = &adev->wakeup;
+
+ mutex_lock(&acpi_wakeup_lock);
+
+ if (!wakeup->enable_count)
+ goto out;
+
+ acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
+ acpi_disable_wakeup_device_power(adev);
+
+ wakeup->enable_count--;
+
+out:
+ mutex_unlock(&acpi_wakeup_lock);
+}
+
+/**
+ * acpi_pm_set_device_wakeup - Enable/disable remote wakeup for given device.
+ * @dev: Device to enable/disable to generate wakeup events.
+ * @enable: Whether to enable or disable the wakeup functionality.
+ */
+int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
+{
+ struct acpi_device *adev;
+ int error;
+
+ adev = ACPI_COMPANION(dev);
+ if (!adev) {
+ dev_dbg(dev, "ACPI companion missing in %s!\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!acpi_device_can_wakeup(adev))
+ return -EINVAL;
+
+ if (!enable) {
+ acpi_device_wakeup_disable(adev);
+ dev_dbg(dev, "Wakeup disabled by ACPI\n");
+ return 0;
+ }
+
+ error = __acpi_device_wakeup_enable(adev, acpi_target_system_state());
+ if (!error)
+ dev_dbg(dev, "Wakeup enabled by ACPI\n");
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(acpi_pm_set_device_wakeup);
+
+/**
+ * acpi_dev_pm_low_power - Put ACPI device into a low-power state.
+ * @dev: Device to put into a low-power state.
+ * @adev: ACPI device node corresponding to @dev.
+ * @system_state: System state to choose the device state for.
+ */
+static int acpi_dev_pm_low_power(struct device *dev, struct acpi_device *adev,
+ u32 system_state)
+{
+ int ret, state;
+
+ if (!acpi_device_power_manageable(adev))
+ return 0;
+
+ ret = acpi_dev_pm_get_state(dev, adev, system_state, NULL, &state);
+ return ret ? ret : acpi_device_set_power(adev, state);
+}
+
+/**
+ * acpi_dev_pm_full_power - Put ACPI device into the full-power state.
+ * @adev: ACPI device node to put into the full-power state.
+ */
+static int acpi_dev_pm_full_power(struct acpi_device *adev)
+{
+ return acpi_device_power_manageable(adev) ?
+ acpi_device_set_power(adev, ACPI_STATE_D0) : 0;
+}
+
+/**
+ * acpi_dev_suspend - Put device into a low-power state using ACPI.
+ * @dev: Device to put into a low-power state.
+ * @wakeup: Whether or not to enable wakeup for the device.
+ *
+ * Put the given device into a low-power state using the standard ACPI
+ * mechanism. Set up remote wakeup if desired, choose the state to put the
+ * device into (this checks if remote wakeup is expected to work too), and set
+ * the power state of the device.
+ */
+int acpi_dev_suspend(struct device *dev, bool wakeup)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ u32 target_state = acpi_target_system_state();
+ int error;
+
+ if (!adev)
+ return 0;
+
+ if (wakeup && acpi_device_can_wakeup(adev)) {
+ error = acpi_device_wakeup_enable(adev, target_state);
+ if (error)
+ return -EAGAIN;
+ } else {
+ wakeup = false;
+ }
+
+ error = acpi_dev_pm_low_power(dev, adev, target_state);
+ if (error && wakeup)
+ acpi_device_wakeup_disable(adev);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_suspend);
+
+/**
+ * acpi_dev_resume - Put device into the full-power state using ACPI.
+ * @dev: Device to put into the full-power state.
+ *
+ * Put the given device into the full-power state using the standard ACPI
+ * mechanism. Set the power state of the device to ACPI D0 and disable wakeup.
+ */
+int acpi_dev_resume(struct device *dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ int error;
+
+ if (!adev)
+ return 0;
+
+ error = acpi_dev_pm_full_power(adev);
+ acpi_device_wakeup_disable(adev);
+ return error;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_resume);
+
+/**
+ * acpi_subsys_runtime_suspend - Suspend device using ACPI.
+ * @dev: Device to suspend.
+ *
+ * Carry out the generic runtime suspend procedure for @dev and use ACPI to put
+ * it into a runtime low-power state.
+ */
+int acpi_subsys_runtime_suspend(struct device *dev)
+{
+ int ret = pm_generic_runtime_suspend(dev);
+
+ return ret ? ret : acpi_dev_suspend(dev, true);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend);
+
+/**
+ * acpi_subsys_runtime_resume - Resume device using ACPI.
+ * @dev: Device to Resume.
+ *
+ * Use ACPI to put the given device into the full-power state and carry out the
+ * generic runtime resume procedure for it.
+ */
+int acpi_subsys_runtime_resume(struct device *dev)
+{
+ int ret = acpi_dev_resume(dev);
+
+ return ret ? ret : pm_generic_runtime_resume(dev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume);
+
+#ifdef CONFIG_PM_SLEEP
+static bool acpi_dev_needs_resume(struct device *dev, struct acpi_device *adev)
+{
+ u32 sys_target = acpi_target_system_state();
+ int ret, state;
+
+ if (!pm_runtime_suspended(dev) || !adev || (adev->wakeup.flags.valid &&
+ device_may_wakeup(dev) != !!adev->wakeup.prepare_count))
+ return true;
+
+ if (sys_target == ACPI_STATE_S0)
+ return false;
+
+ if (adev->power.flags.dsw_present)
+ return true;
+
+ ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state);
+ if (ret)
+ return true;
+
+ return state != adev->power.state;
+}
+
+/**
+ * acpi_subsys_prepare - Prepare device for system transition to a sleep state.
+ * @dev: Device to prepare.
+ */
+int acpi_subsys_prepare(struct device *dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+
+ if (dev->driver && dev->driver->pm && dev->driver->pm->prepare) {
+ int ret = dev->driver->pm->prepare(dev);
+
+ if (ret < 0)
+ return ret;
+
+ if (!ret && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE))
+ return 0;
+ }
+
+ return !acpi_dev_needs_resume(dev, adev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_prepare);
+
+/**
+ * acpi_subsys_complete - Finalize device's resume during system resume.
+ * @dev: Device to handle.
+ */
+void acpi_subsys_complete(struct device *dev)
+{
+ pm_generic_complete(dev);
+ /*
+ * If the device had been runtime-suspended before the system went into
+ * the sleep state it is going out of and it has never been resumed till
+ * now, resume it in case the firmware powered it up.
+ */
+ if (pm_runtime_suspended(dev) && pm_resume_via_firmware())
+ pm_request_resume(dev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_complete);
+
+/**
+ * acpi_subsys_suspend - Run the device driver's suspend callback.
+ * @dev: Device to handle.
+ *
+ * Follow PCI and resume devices from runtime suspend before running their
+ * system suspend callbacks, unless the driver can cope with runtime-suspended
+ * devices during system suspend and there are no ACPI-specific reasons for
+ * resuming them.
+ */
+int acpi_subsys_suspend(struct device *dev)
+{
+ if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
+ acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
+ pm_runtime_resume(dev);
+
+ return pm_generic_suspend(dev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_suspend);
+
+/**
+ * acpi_subsys_suspend_late - Suspend device using ACPI.
+ * @dev: Device to suspend.
+ *
+ * Carry out the generic late suspend procedure for @dev and use ACPI to put
+ * it into a low-power state during system transition into a sleep state.
+ */
+int acpi_subsys_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_dev_suspend(dev, device_may_wakeup(dev));
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);
+
+/**
+ * acpi_subsys_suspend_noirq - Run the device driver's "noirq" suspend callback.
+ * @dev: Device to suspend.
+ */
+int acpi_subsys_suspend_noirq(struct device *dev)
+{
+ int ret;
+
+ if (dev_pm_skip_suspend(dev))
+ return 0;
+
+ ret = pm_generic_suspend_noirq(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * If the target system sleep state is suspend-to-idle, it is sufficient
+ * to check whether or not the device's wakeup settings are good for
+ * runtime PM. Otherwise, the pm_resume_via_firmware() check will cause
+ * acpi_subsys_complete() to take care of fixing up the device's state
+ * anyway, if need be.
+ */
+ if (device_can_wakeup(dev) && !device_may_wakeup(dev))
+ dev->power.may_skip_resume = false;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq);
+
+/**
+ * acpi_subsys_resume_noirq - Run the device driver's "noirq" resume callback.
+ * @dev: Device to handle.
+ */
+static int acpi_subsys_resume_noirq(struct device *dev)
+{
+ if (dev_pm_skip_resume(dev))
+ return 0;
+
+ return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * acpi_subsys_resume_early - Resume device using ACPI.
+ * @dev: Device to Resume.
+ *
+ * Use ACPI to put the given device into the full-power state and carry out the
+ * generic early resume procedure for it during system transition into the
+ * working state, but only do that if device either defines early resume
+ * handler, or does not define power operations at all. Otherwise powering up
+ * of the device is postponed to the normal resume phase.
+ */
+static int acpi_subsys_resume_early(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ int ret;
+
+ if (dev_pm_skip_resume(dev))
+ return 0;
+
+ if (pm && !pm->resume_early) {
+ dev_dbg(dev, "postponing D0 transition to normal resume stage\n");
+ return 0;
+ }
+
+ ret = acpi_dev_resume(dev);
+ return ret ? ret : pm_generic_resume_early(dev);
+}
+
+/**
+ * acpi_subsys_resume - Resume device using ACPI.
+ * @dev: Device to Resume.
+ *
+ * Use ACPI to put the given device into the full-power state if it has not been
+ * powered up during early resume phase, and carry out the generic resume
+ * procedure for it during system transition into the working state.
+ */
+static int acpi_subsys_resume(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ int ret = 0;
+
+ if (!dev_pm_skip_resume(dev) && pm && !pm->resume_early) {
+ dev_dbg(dev, "executing postponed D0 transition\n");
+ ret = acpi_dev_resume(dev);
+ }
+
+ return ret ? ret : pm_generic_resume(dev);
+}
+
+/**
+ * acpi_subsys_freeze - Run the device driver's freeze callback.
+ * @dev: Device to handle.
+ */
+int acpi_subsys_freeze(struct device *dev)
+{
+ /*
+ * Resume all runtime-suspended devices before creating a snapshot
+ * image of system memory, because the restore kernel generally cannot
+ * be expected to always handle them consistently and they need to be
+ * put into the runtime-active metastate during system resume anyway,
+ * so it is better to ensure that the state saved in the image will be
+ * always consistent with that.
+ */
+ pm_runtime_resume(dev);
+
+ return pm_generic_freeze(dev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_freeze);
+
+/**
+ * acpi_subsys_restore_early - Restore device using ACPI.
+ * @dev: Device to restore.
+ */
+int acpi_subsys_restore_early(struct device *dev)
+{
+ int ret = acpi_dev_resume(dev);
+
+ return ret ? ret : pm_generic_restore_early(dev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_restore_early);
+
+/**
+ * acpi_subsys_poweroff - Run the device driver's poweroff callback.
+ * @dev: Device to handle.
+ *
+ * Follow PCI and resume devices from runtime suspend before running their
+ * system poweroff callbacks, unless the driver can cope with runtime-suspended
+ * devices during system suspend and there are no ACPI-specific reasons for
+ * resuming them.
+ */
+int acpi_subsys_poweroff(struct device *dev)
+{
+ if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
+ acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
+ pm_runtime_resume(dev);
+
+ return pm_generic_poweroff(dev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_poweroff);
+
+/**
+ * acpi_subsys_poweroff_late - Run the device driver's poweroff callback.
+ * @dev: Device to handle.
+ *
+ * Carry out the generic late poweroff procedure for @dev and use ACPI to put
+ * it into a low-power state during system transition into a sleep state.
+ */
+static int acpi_subsys_poweroff_late(struct device *dev)
+{
+ int ret;
+
+ if (dev_pm_skip_suspend(dev))
+ return 0;
+
+ ret = pm_generic_poweroff_late(dev);
+ if (ret)
+ return ret;
+
+ return acpi_dev_suspend(dev, device_may_wakeup(dev));
+}
+
+/**
+ * acpi_subsys_poweroff_noirq - Run the driver's "noirq" poweroff callback.
+ * @dev: Device to suspend.
+ */
+static int acpi_subsys_poweroff_noirq(struct device *dev)
+{
+ if (dev_pm_skip_suspend(dev))
+ return 0;
+
+ return pm_generic_poweroff_noirq(dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static struct dev_pm_domain acpi_general_pm_domain = {
+ .ops = {
+ .runtime_suspend = acpi_subsys_runtime_suspend,
+ .runtime_resume = acpi_subsys_runtime_resume,
+#ifdef CONFIG_PM_SLEEP
+ .prepare = acpi_subsys_prepare,
+ .complete = acpi_subsys_complete,
+ .suspend = acpi_subsys_suspend,
+ .resume = acpi_subsys_resume,
+ .suspend_late = acpi_subsys_suspend_late,
+ .suspend_noirq = acpi_subsys_suspend_noirq,
+ .resume_noirq = acpi_subsys_resume_noirq,
+ .resume_early = acpi_subsys_resume_early,
+ .freeze = acpi_subsys_freeze,
+ .poweroff = acpi_subsys_poweroff,
+ .poweroff_late = acpi_subsys_poweroff_late,
+ .poweroff_noirq = acpi_subsys_poweroff_noirq,
+ .restore_early = acpi_subsys_restore_early,
+#endif
+ },
+};
+
+/**
+ * acpi_dev_pm_detach - Remove ACPI power management from the device.
+ * @dev: Device to take care of.
+ * @power_off: Whether or not to try to remove power from the device.
+ *
+ * Remove the device from the general ACPI PM domain and remove its wakeup
+ * notifier. If @power_off is set, additionally remove power from the device if
+ * possible.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ */
+static void acpi_dev_pm_detach(struct device *dev, bool power_off)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+
+ if (adev && dev->pm_domain == &acpi_general_pm_domain) {
+ dev_pm_domain_set(dev, NULL);
+ acpi_remove_pm_notifier(adev);
+ if (power_off) {
+ /*
+ * If the device's PM QoS resume latency limit or flags
+ * have been exposed to user space, they have to be
+ * hidden at this point, so that they don't affect the
+ * choice of the low-power state to put the device into.
+ */
+ dev_pm_qos_hide_latency_limit(dev);
+ dev_pm_qos_hide_flags(dev);
+ acpi_device_wakeup_disable(adev);
+ acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
+ }
+ }
+}
+
+/**
+ * acpi_dev_pm_attach - Prepare device for ACPI power management.
+ * @dev: Device to prepare.
+ * @power_on: Whether or not to power on the device.
+ *
+ * If @dev has a valid ACPI handle that has a valid struct acpi_device object
+ * attached to it, install a wakeup notification handler for the device and
+ * add it to the general ACPI PM domain. If @power_on is set, the device will
+ * be put into the ACPI D0 state before the function returns.
+ *
+ * This assumes that the @dev's bus type uses generic power management callbacks
+ * (or doesn't use any power management callbacks at all).
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ */
+int acpi_dev_pm_attach(struct device *dev, bool power_on)
+{
+ /*
+ * Skip devices whose ACPI companions match the device IDs below,
+ * because they require special power management handling incompatible
+ * with the generic ACPI PM domain.
+ */
+ static const struct acpi_device_id special_pm_ids[] = {
+ ACPI_FAN_DEVICE_IDS,
+ {}
+ };
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+
+ if (!adev || !acpi_match_device_ids(adev, special_pm_ids))
+ return 0;
+
+ /*
+ * Only attach the power domain to the first device if the
+ * companion is shared by multiple. This is to prevent doing power
+ * management twice.
+ */
+ if (!acpi_device_is_first_physical_node(adev, dev))
+ return 0;
+
+ acpi_add_pm_notifier(adev, dev, acpi_pm_notify_work_func);
+ dev_pm_domain_set(dev, &acpi_general_pm_domain);
+ if (power_on) {
+ acpi_dev_pm_full_power(adev);
+ acpi_device_wakeup_disable(adev);
+ }
+
+ dev->pm_domain->detach = acpi_dev_pm_detach;
+ return 1;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_pm_attach);
+
+/**
+ * acpi_storage_d3 - Check if D3 should be used in the suspend path
+ * @dev: Device to check
+ *
+ * Return %true if the platform firmware wants @dev to be programmed
+ * into D3hot or D3cold (if supported) in the suspend path, or %false
+ * when there is no specific preference. On some platforms, if this
+ * hint is ignored, @dev may remain unresponsive after suspending the
+ * platform as a whole.
+ *
+ * Although the property has storage in the name it actually is
+ * applied to the PCIe slot and plugging in a non-storage device the
+ * same platform restrictions will likely apply.
+ */
+bool acpi_storage_d3(struct device *dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ u8 val;
+
+ if (force_storage_d3())
+ return true;
+
+ if (!adev)
+ return false;
+ if (fwnode_property_read_u8(acpi_fwnode_handle(adev), "StorageD3Enable",
+ &val))
+ return false;
+ return val == 1;
+}
+EXPORT_SYMBOL_GPL(acpi_storage_d3);
+
+/**
+ * acpi_dev_state_d0 - Tell if the device is in D0 power state
+ * @dev: Physical device the ACPI power state of which to check
+ *
+ * On a system without ACPI, return true. On a system with ACPI, return true if
+ * the current ACPI power state of the device is D0, or false otherwise.
+ *
+ * Note that the power state of a device is not well-defined after it has been
+ * passed to acpi_device_set_power() and before that function returns, so it is
+ * 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/non-d0-probe.rst for
+ * more information.
+ */
+bool acpi_dev_state_d0(struct device *dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+
+ if (!adev)
+ return true;
+
+ return adev->power.state == ACPI_STATE_D0;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_state_d0);
+
+#endif /* CONFIG_PM */
diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c
new file mode 100644
index 000000000..c727fb320
--- /dev/null
+++ b/drivers/acpi/device_sysfs.c
@@ -0,0 +1,650 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/acpi/device_sysfs.c - ACPI device sysfs attributes and modalias.
+ *
+ * Copyright (C) 2015, Intel Corp.
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/nls.h>
+
+#include "internal.h"
+
+static ssize_t acpi_object_path(acpi_handle handle, char *buf)
+{
+ struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL};
+ int result;
+
+ result = acpi_get_name(handle, ACPI_FULL_PATHNAME, &path);
+ if (result)
+ return result;
+
+ result = sprintf(buf, "%s\n", (char *)path.pointer);
+ kfree(path.pointer);
+ return result;
+}
+
+struct acpi_data_node_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct acpi_data_node *, char *);
+ ssize_t (*store)(struct acpi_data_node *, const char *, size_t count);
+};
+
+#define DATA_NODE_ATTR(_name) \
+ static struct acpi_data_node_attr data_node_##_name = \
+ __ATTR(_name, 0444, data_node_show_##_name, NULL)
+
+static ssize_t data_node_show_path(struct acpi_data_node *dn, char *buf)
+{
+ return dn->handle ? acpi_object_path(dn->handle, buf) : 0;
+}
+
+DATA_NODE_ATTR(path);
+
+static struct attribute *acpi_data_node_default_attrs[] = {
+ &data_node_path.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(acpi_data_node_default);
+
+#define to_data_node(k) container_of(k, struct acpi_data_node, kobj)
+#define to_attr(a) container_of(a, struct acpi_data_node_attr, attr)
+
+static ssize_t acpi_data_node_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct acpi_data_node *dn = to_data_node(kobj);
+ struct acpi_data_node_attr *dn_attr = to_attr(attr);
+
+ return dn_attr->show ? dn_attr->show(dn, buf) : -ENXIO;
+}
+
+static const struct sysfs_ops acpi_data_node_sysfs_ops = {
+ .show = acpi_data_node_attr_show,
+};
+
+static void acpi_data_node_release(struct kobject *kobj)
+{
+ struct acpi_data_node *dn = to_data_node(kobj);
+
+ complete(&dn->kobj_done);
+}
+
+static 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,
+};
+
+static void acpi_expose_nondev_subnodes(struct kobject *kobj,
+ struct acpi_device_data *data)
+{
+ struct list_head *list = &data->subnodes;
+ struct acpi_data_node *dn;
+
+ if (list_empty(list))
+ return;
+
+ list_for_each_entry(dn, list, sibling) {
+ int ret;
+
+ init_completion(&dn->kobj_done);
+ ret = kobject_init_and_add(&dn->kobj, &acpi_data_node_ktype,
+ kobj, "%s", dn->name);
+ if (!ret)
+ acpi_expose_nondev_subnodes(&dn->kobj, &dn->data);
+ else if (dn->handle)
+ acpi_handle_err(dn->handle, "Failed to expose (%d)\n", ret);
+ }
+}
+
+static void acpi_hide_nondev_subnodes(struct acpi_device_data *data)
+{
+ struct list_head *list = &data->subnodes;
+ struct acpi_data_node *dn;
+
+ if (list_empty(list))
+ return;
+
+ list_for_each_entry_reverse(dn, list, sibling) {
+ acpi_hide_nondev_subnodes(&dn->data);
+ kobject_put(&dn->kobj);
+ }
+}
+
+/**
+ * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent
+ * @acpi_dev: ACPI device object.
+ * @modalias: Buffer to print into.
+ * @size: Size of the buffer.
+ *
+ * Creates hid/cid(s) string needed for modalias and uevent
+ * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
+ * char *modalias: "acpi:IBM0001:ACPI0001"
+ * Return: 0: no _HID and no _CID
+ * -EINVAL: output error
+ * -ENOMEM: output is truncated
+ */
+static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
+ int size)
+{
+ int len;
+ int count;
+ struct acpi_hardware_id *id;
+
+ /* Avoid unnecessarily loading modules for non present devices. */
+ if (!acpi_device_is_present(acpi_dev))
+ return 0;
+
+ /*
+ * Since we skip ACPI_DT_NAMESPACE_HID from the modalias below, 0 should
+ * be returned if ACPI_DT_NAMESPACE_HID is the only ACPI/PNP ID in the
+ * device's list.
+ */
+ count = 0;
+ list_for_each_entry(id, &acpi_dev->pnp.ids, list)
+ if (strcmp(id->id, ACPI_DT_NAMESPACE_HID))
+ count++;
+
+ if (!count)
+ return 0;
+
+ len = snprintf(modalias, size, "acpi:");
+ if (len >= size)
+ return -ENOMEM;
+
+ size -= len;
+
+ list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
+ if (!strcmp(id->id, ACPI_DT_NAMESPACE_HID))
+ continue;
+
+ count = snprintf(&modalias[len], size, "%s:", id->id);
+ if (count < 0)
+ return -EINVAL;
+
+ if (count >= size)
+ return -ENOMEM;
+
+ len += count;
+ size -= count;
+ }
+ modalias[len] = '\0';
+ return len;
+}
+
+/**
+ * create_of_modalias - Creates DT compatible string for modalias and uevent
+ * @acpi_dev: ACPI device object.
+ * @modalias: Buffer to print into.
+ * @size: Size of the buffer.
+ *
+ * Expose DT compatible modalias as of:NnameTCcompatible. This function should
+ * 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,
+ int size)
+{
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+ const union acpi_object *of_compatible, *obj;
+ acpi_status status;
+ int len, count;
+ int i, nval;
+ char *c;
+
+ status = acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ /* DT strings are all in lower case */
+ for (c = buf.pointer; *c != '\0'; c++)
+ *c = tolower(*c);
+
+ len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer);
+ ACPI_FREE(buf.pointer);
+
+ if (len >= size)
+ return -ENOMEM;
+
+ size -= len;
+
+ of_compatible = acpi_dev->data.of_compatible;
+ if (of_compatible->type == ACPI_TYPE_PACKAGE) {
+ nval = of_compatible->package.count;
+ obj = of_compatible->package.elements;
+ } else { /* Must be ACPI_TYPE_STRING. */
+ nval = 1;
+ obj = of_compatible;
+ }
+ 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;
+
+ len += count;
+ size -= count;
+ }
+ modalias[len] = '\0';
+ return len;
+}
+
+int __acpi_device_uevent_modalias(struct acpi_device *adev,
+ struct kobj_uevent_env *env)
+{
+ int len;
+
+ if (!adev)
+ return -ENODEV;
+
+ if (list_empty(&adev->pnp.ids))
+ return 0;
+
+ if (add_uevent_var(env, "MODALIAS="))
+ return -ENOMEM;
+
+ if (adev->data.of_compatible)
+ len = create_of_modalias(adev, &env->buf[env->buflen - 1],
+ sizeof(env->buf) - env->buflen);
+ else
+ len = create_pnp_modalias(adev, &env->buf[env->buflen - 1],
+ sizeof(env->buf) - env->buflen);
+ if (len < 0)
+ return len;
+
+ env->buflen += len;
+
+ return 0;
+}
+
+/**
+ * acpi_device_uevent_modalias - uevent modalias for ACPI-enumerated devices.
+ * @dev: Struct device to get ACPI device node.
+ * @env: Environment variables of the kobject uevent.
+ *
+ * Create the uevent modalias field for ACPI-enumerated devices.
+ *
+ * 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)
+{
+ 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)
+{
+ int len, count;
+
+ if (!adev)
+ return -ENODEV;
+
+ if (list_empty(&adev->pnp.ids))
+ return 0;
+
+ len = create_pnp_modalias(adev, buf, size - 1);
+ if (len < 0) {
+ return len;
+ } else if (len > 0) {
+ buf[len++] = '\n';
+ size -= len;
+ }
+ if (!adev->data.of_compatible)
+ return len;
+
+ count = create_of_modalias(adev, buf + len, size - 1);
+ if (count < 0) {
+ return count;
+ } else if (count > 0) {
+ len += count;
+ buf[len++] = '\n';
+ }
+
+ return len;
+}
+
+/**
+ * acpi_device_modalias - modalias sysfs attribute for ACPI-enumerated devices.
+ * @dev: Struct device to get ACPI device node.
+ * @buf: The buffer to save pnp_modalias and of_modalias.
+ * @size: Size of buffer.
+ *
+ * Create the modalias sysfs attribute for ACPI-enumerated devices.
+ *
+ * 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_modalias(struct device *dev, char *buf, int size)
+{
+ return __acpi_device_modalias(acpi_companion_match(dev), buf, size);
+}
+EXPORT_SYMBOL_GPL(acpi_device_modalias);
+
+static ssize_t
+modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return __acpi_device_modalias(to_acpi_device(dev), buf, 1024);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static ssize_t real_power_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_device *adev = to_acpi_device(dev);
+ int state;
+ int ret;
+
+ ret = acpi_device_get_power(adev, &state);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%s\n", acpi_power_state_string(state));
+}
+
+static DEVICE_ATTR_RO(real_power_state);
+
+static ssize_t power_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_device *adev = to_acpi_device(dev);
+
+ return sprintf(buf, "%s\n", acpi_power_state_string(adev->power.state));
+}
+
+static DEVICE_ATTR_RO(power_state);
+
+static ssize_t
+eject_store(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi_device = to_acpi_device(d);
+ acpi_object_type not_used;
+ acpi_status status;
+
+ if (!count || buf[0] != '1')
+ return -EINVAL;
+
+ if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled)
+ && !d->driver)
+ return -ENODEV;
+
+ status = acpi_get_type(acpi_device->handle, &not_used);
+ if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)
+ return -ENODEV;
+
+ acpi_dev_get(acpi_device);
+ status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT);
+ if (ACPI_SUCCESS(status))
+ return count;
+
+ acpi_dev_put(acpi_device);
+ acpi_evaluate_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
+ ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
+ return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN;
+}
+
+static DEVICE_ATTR_WO(eject);
+
+static ssize_t
+hid_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+
+ return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev));
+}
+static DEVICE_ATTR_RO(hid);
+
+static ssize_t uid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+
+ return sprintf(buf, "%s\n", acpi_dev->pnp.unique_id);
+}
+static DEVICE_ATTR_RO(uid);
+
+static ssize_t adr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+
+ if (acpi_dev->pnp.bus_address > U32_MAX)
+ return sprintf(buf, "0x%016llx\n", acpi_dev->pnp.bus_address);
+ else
+ return sprintf(buf, "0x%08llx\n", acpi_dev->pnp.bus_address);
+}
+static DEVICE_ATTR_RO(adr);
+
+static ssize_t path_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+
+ return acpi_object_path(acpi_dev->handle, buf);
+}
+static DEVICE_ATTR_RO(path);
+
+/* sysfs file that shows description text from the ACPI _STR method */
+static ssize_t description_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ int result;
+
+ if (acpi_dev->pnp.str_obj == NULL)
+ return 0;
+
+ /*
+ * 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,
+ UTF16_LITTLE_ENDIAN, buf,
+ PAGE_SIZE - 1);
+
+ buf[result++] = '\n';
+
+ return result;
+}
+static DEVICE_ATTR_RO(description);
+
+static ssize_t
+sun_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ acpi_status status;
+ unsigned long long sun;
+
+ status = acpi_evaluate_integer(acpi_dev->handle, "_SUN", NULL, &sun);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return sprintf(buf, "%llu\n", sun);
+}
+static DEVICE_ATTR_RO(sun);
+
+static ssize_t
+hrv_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ acpi_status status;
+ unsigned long long hrv;
+
+ status = acpi_evaluate_integer(acpi_dev->handle, "_HRV", NULL, &hrv);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return sprintf(buf, "%llu\n", hrv);
+}
+static DEVICE_ATTR_RO(hrv);
+
+static ssize_t status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ acpi_status status;
+ unsigned long long sta;
+
+ status = acpi_evaluate_integer(acpi_dev->handle, "_STA", NULL, &sta);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return sprintf(buf, "%llu\n", sta);
+}
+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;
+
+ /*
+ * 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 (!list_empty(&dev->pnp.ids)) {
+ result = device_create_file(&dev->dev, &dev_attr_hid);
+ if (result)
+ goto end;
+
+ result = device_create_file(&dev->dev, &dev_attr_modalias);
+ if (result)
+ goto end;
+ }
+
+ /*
+ * 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 (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 (acpi_has_method(dev->handle, "_SUN")) {
+ result = device_create_file(&dev->dev, &dev_attr_sun);
+ if (result)
+ goto end;
+ }
+
+ if (acpi_has_method(dev->handle, "_HRV")) {
+ result = device_create_file(&dev->dev, &dev_attr_hrv);
+ if (result)
+ goto end;
+ }
+
+ if (acpi_has_method(dev->handle, "_STA")) {
+ result = device_create_file(&dev->dev, &dev_attr_status);
+ if (result)
+ goto end;
+ }
+
+ /*
+ * 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 (dev->flags.power_manageable) {
+ result = device_create_file(&dev->dev, &dev_attr_power_state);
+ if (result)
+ return result;
+
+ if (dev->power.flags.power_resources)
+ result = device_create_file(&dev->dev,
+ &dev_attr_real_power_state);
+ }
+
+ acpi_expose_nondev_subnodes(&dev->dev.kobj, &dev->data);
+
+end:
+ return result;
+}
+
+/**
+ * acpi_device_remove_files - Remove sysfs attributes of an ACPI device.
+ * @dev: ACPI device object.
+ */
+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
new file mode 100644
index 000000000..a89bdbe00
--- /dev/null
+++ b/drivers/acpi/dock.c
@@ -0,0 +1,646 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * dock.c - ACPI dock station driver
+ *
+ * Copyright (C) 2006, 2014, Intel Corp.
+ * Author: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
+ * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <linux/stddef.h>
+#include <linux/acpi.h>
+
+#include "internal.h"
+
+static bool immediate_undock = 1;
+module_param(immediate_undock, bool, 0644);
+MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
+ "undock immediately when the undock button is pressed, 0 will cause"
+ " the driver to wait for userspace to write the undock sysfs file "
+ " before undocking");
+
+struct dock_station {
+ acpi_handle handle;
+ unsigned long last_dock_time;
+ u32 flags;
+ struct list_head dependent_devices;
+
+ struct list_head sibling;
+ struct platform_device *dock_device;
+};
+static LIST_HEAD(dock_stations);
+static int dock_station_count;
+
+struct dock_dependent_device {
+ struct list_head list;
+ struct acpi_device *adev;
+};
+
+#define DOCK_DOCKING 0x00000001
+#define DOCK_UNDOCKING 0x00000002
+#define DOCK_IS_DOCK 0x00000010
+#define DOCK_IS_ATA 0x00000020
+#define DOCK_IS_BAT 0x00000040
+#define DOCK_EVENT 3
+#define UNDOCK_EVENT 2
+
+enum dock_callback_type {
+ DOCK_CALL_HANDLER,
+ DOCK_CALL_FIXUP,
+ DOCK_CALL_UEVENT,
+};
+
+/*****************************************************************************
+ * Dock Dependent device functions *
+ *****************************************************************************/
+/**
+ * add_dock_dependent_device - associate a device with the dock station
+ * @ds: Dock station.
+ * @adev: Dependent ACPI device object.
+ *
+ * Add the dependent device to the dock's dependent device list.
+ */
+static int add_dock_dependent_device(struct dock_station *ds,
+ struct acpi_device *adev)
+{
+ struct dock_dependent_device *dd;
+
+ dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ dd->adev = adev;
+ INIT_LIST_HEAD(&dd->list);
+ list_add_tail(&dd->list, &ds->dependent_devices);
+
+ return 0;
+}
+
+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_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;
+ }
+ }
+
+ out:
+ acpi_unlock_hp_context();
+}
+
+static struct dock_station *find_dock_station(acpi_handle handle)
+{
+ struct dock_station *ds;
+
+ list_for_each_entry(ds, &dock_stations, sibling)
+ if (ds->handle == handle)
+ return ds;
+
+ return NULL;
+}
+
+/**
+ * find_dock_dependent_device - get a device dependent on this dock
+ * @ds: the dock station
+ * @adev: ACPI device object to find.
+ *
+ * iterate over the dependent device list for this dock. If the
+ * dependent device matches the handle, return.
+ */
+static struct dock_dependent_device *
+find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev)
+{
+ struct dock_dependent_device *dd;
+
+ list_for_each_entry(dd, &ds->dependent_devices, list)
+ if (adev == dd->adev)
+ return dd;
+
+ return NULL;
+}
+
+void register_dock_dependent_device(struct acpi_device *adev,
+ acpi_handle dshandle)
+{
+ struct dock_station *ds = find_dock_station(dshandle);
+
+ if (ds && !find_dock_dependent_device(ds, adev))
+ add_dock_dependent_device(ds, adev);
+}
+
+/*****************************************************************************
+ * Dock functions *
+ *****************************************************************************/
+
+/**
+ * is_dock_device - see if a device is on a dock station
+ * @adev: ACPI device object to check.
+ *
+ * If this device is either the dock station itself,
+ * or is a device dependent on the dock station, then it
+ * is a dock device
+ */
+int is_dock_device(struct acpi_device *adev)
+{
+ struct dock_station *dock_station;
+
+ if (!dock_station_count)
+ return 0;
+
+ if (acpi_dock_match(adev->handle))
+ return 1;
+
+ list_for_each_entry(dock_station, &dock_stations, sibling)
+ if (find_dock_dependent_device(dock_station, adev))
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(is_dock_device);
+
+/**
+ * dock_present - see if the dock station is present.
+ * @ds: the dock station
+ *
+ * execute the _STA method. note that present does not
+ * imply that we are docked.
+ */
+static int dock_present(struct dock_station *ds)
+{
+ unsigned long long sta;
+ acpi_status status;
+
+ if (ds) {
+ status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
+ if (ACPI_SUCCESS(status) && sta)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * hot_remove_dock_devices - Remove dock station devices.
+ * @ds: Dock station.
+ */
+static void hot_remove_dock_devices(struct dock_station *ds)
+{
+ struct dock_dependent_device *dd;
+
+ /*
+ * Walk the list in reverse order so that devices that have been added
+ * last are removed first (in case there are some indirect dependencies
+ * between them).
+ */
+ list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
+ dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST,
+ DOCK_CALL_HANDLER);
+
+ list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
+ acpi_bus_trim(dd->adev);
+}
+
+/**
+ * hotplug_dock_devices - Insert devices on a dock station.
+ * @ds: the dock station
+ * @event: either bus check or device check request
+ *
+ * Some devices on the dock station need to have drivers called
+ * to perform hotplug operations after a dock event has occurred.
+ * Traverse the list of dock devices that have registered a
+ * hotplug handler, and call the handler.
+ */
+static void hotplug_dock_devices(struct dock_station *ds, u32 event)
+{
+ struct dock_dependent_device *dd;
+
+ /* Call driver specific post-dock fixups. */
+ list_for_each_entry(dd, &ds->dependent_devices, list)
+ dock_hotplug_event(dd, event, DOCK_CALL_FIXUP);
+
+ /* Call driver specific hotplug functions. */
+ list_for_each_entry(dd, &ds->dependent_devices, list)
+ dock_hotplug_event(dd, event, DOCK_CALL_HANDLER);
+
+ /*
+ * Check if all devices have been enumerated already. If not, run
+ * acpi_bus_scan() for them and that will cause scan handlers to be
+ * attached to device objects or acpi_drivers to be stopped/started if
+ * they are present.
+ */
+ list_for_each_entry(dd, &ds->dependent_devices, list) {
+ struct acpi_device *adev = dd->adev;
+
+ if (!acpi_device_enumerated(adev)) {
+ int ret = acpi_bus_scan(adev->handle);
+
+ if (ret)
+ dev_dbg(&adev->dev, "scan error %d\n", -ret);
+ }
+ }
+}
+
+static void dock_event(struct dock_station *ds, u32 event, int num)
+{
+ struct device *dev = &ds->dock_device->dev;
+ char event_string[13];
+ char *envp[] = { event_string, NULL };
+ struct dock_dependent_device *dd;
+
+ if (num == UNDOCK_EVENT)
+ sprintf(event_string, "EVENT=undock");
+ else
+ sprintf(event_string, "EVENT=dock");
+
+ /*
+ * Indicate that the status of the dock station has
+ * changed.
+ */
+ if (num == DOCK_EVENT)
+ kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+
+ list_for_each_entry(dd, &ds->dependent_devices, list)
+ dock_hotplug_event(dd, event, DOCK_CALL_UEVENT);
+
+ if (num != DOCK_EVENT)
+ kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+}
+
+/**
+ * handle_dock - handle a dock event
+ * @ds: the dock station
+ * @dock: to dock, or undock - that is the question
+ *
+ * Execute the _DCK method in response to an acpi event
+ */
+static void handle_dock(struct dock_station *ds, int dock)
+{
+ acpi_status status;
+ struct acpi_object_list arg_list;
+ union acpi_object arg;
+ unsigned long long value;
+
+ acpi_handle_info(ds->handle, "%s\n", dock ? "docking" : "undocking");
+
+ /* _DCK method has one argument */
+ arg_list.count = 1;
+ arg_list.pointer = &arg;
+ arg.type = ACPI_TYPE_INTEGER;
+ arg.integer.value = dock;
+ status = acpi_evaluate_integer(ds->handle, "_DCK", &arg_list, &value);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
+ acpi_handle_err(ds->handle, "Failed to execute _DCK (0x%x)\n",
+ status);
+}
+
+static inline void dock(struct dock_station *ds)
+{
+ handle_dock(ds, 1);
+}
+
+static inline void undock(struct dock_station *ds)
+{
+ handle_dock(ds, 0);
+}
+
+static inline void begin_dock(struct dock_station *ds)
+{
+ ds->flags |= DOCK_DOCKING;
+}
+
+static inline void complete_dock(struct dock_station *ds)
+{
+ ds->flags &= ~(DOCK_DOCKING);
+ ds->last_dock_time = jiffies;
+}
+
+static inline void begin_undock(struct dock_station *ds)
+{
+ ds->flags |= DOCK_UNDOCKING;
+}
+
+static inline void complete_undock(struct dock_station *ds)
+{
+ ds->flags &= ~(DOCK_UNDOCKING);
+}
+
+/**
+ * dock_in_progress - see if we are in the middle of handling a dock event
+ * @ds: the dock station
+ *
+ * Sometimes while docking, false dock events can be sent to the driver
+ * because good connections aren't made or some other reason. Ignore these
+ * if we are in the middle of doing something.
+ */
+static int dock_in_progress(struct dock_station *ds)
+{
+ if ((ds->flags & DOCK_DOCKING) ||
+ time_before(jiffies, (ds->last_dock_time + HZ)))
+ return 1;
+ return 0;
+}
+
+/**
+ * handle_eject_request - handle an undock request checking for error conditions
+ *
+ * Check to make sure the dock device is still present, then undock and
+ * hotremove all the devices that may need removing.
+ */
+static int handle_eject_request(struct dock_station *ds, u32 event)
+{
+ if (dock_in_progress(ds))
+ return -EBUSY;
+
+ /*
+ * here we need to generate the undock
+ * event prior to actually doing the undock
+ * so that the device struct still exists.
+ * Also, even send the dock event if the
+ * device is not present anymore
+ */
+ dock_event(ds, event, UNDOCK_EVENT);
+
+ hot_remove_dock_devices(ds);
+ undock(ds);
+ acpi_evaluate_lck(ds->handle, 0);
+ acpi_evaluate_ej0(ds->handle);
+ if (dock_present(ds)) {
+ acpi_handle_err(ds->handle, "Unable to undock!\n");
+ return -EBUSY;
+ }
+ complete_undock(ds);
+ return 0;
+}
+
+/**
+ * dock_notify - Handle ACPI dock notification.
+ * @adev: Dock station's ACPI device object.
+ * @event: Event code.
+ *
+ * If we are notified to dock, then check to see if the dock is
+ * present and then dock. Notify all drivers of the dock event,
+ * and then hotplug and devices that may need hotplugging.
+ */
+int dock_notify(struct acpi_device *adev, u32 event)
+{
+ acpi_handle handle = adev->handle;
+ struct dock_station *ds = find_dock_station(handle);
+ int surprise_removal = 0;
+
+ if (!ds)
+ return -ENODEV;
+
+ /*
+ * According to acpi spec 3.0a, if a DEVICE_CHECK notification
+ * is sent and _DCK is present, it is assumed to mean an undock
+ * request.
+ */
+ if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK)
+ event = ACPI_NOTIFY_EJECT_REQUEST;
+
+ /*
+ * dock station: BUS_CHECK - docked or surprise removal
+ * DEVICE_CHECK - undocked
+ * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal
+ *
+ * To simplify event handling, dock dependent device handler always
+ * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and
+ * ACPI_NOTIFY_EJECT_REQUEST for removal
+ */
+ switch (event) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) {
+ begin_dock(ds);
+ dock(ds);
+ if (!dock_present(ds)) {
+ acpi_handle_err(handle, "Unable to dock!\n");
+ complete_dock(ds);
+ break;
+ }
+ hotplug_dock_devices(ds, event);
+ complete_dock(ds);
+ dock_event(ds, event, DOCK_EVENT);
+ acpi_evaluate_lck(ds->handle, 1);
+ acpi_update_all_gpes();
+ break;
+ }
+ if (dock_present(ds) || dock_in_progress(ds))
+ break;
+ /* This is a surprise removal */
+ surprise_removal = 1;
+ event = ACPI_NOTIFY_EJECT_REQUEST;
+ /* Fall back */
+ fallthrough;
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ begin_undock(ds);
+ if ((immediate_undock && !(ds->flags & DOCK_IS_ATA))
+ || surprise_removal)
+ handle_eject_request(ds, event);
+ else
+ dock_event(ds, event, UNDOCK_EVENT);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * show_docked - read method for "docked" file in sysfs
+ */
+static ssize_t docked_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dock_station *dock_station = dev->platform_data;
+ struct acpi_device *adev = acpi_fetch_acpi_dev(dock_station->handle);
+
+ return sysfs_emit(buf, "%u\n", acpi_device_enumerated(adev));
+}
+static DEVICE_ATTR_RO(docked);
+
+/*
+ * show_flags - read method for flags file in sysfs
+ */
+static ssize_t flags_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dock_station *dock_station = dev->platform_data;
+
+ return sysfs_emit(buf, "%d\n", dock_station->flags);
+
+}
+static DEVICE_ATTR_RO(flags);
+
+/*
+ * write_undock - write method for "undock" file in sysfs
+ */
+static ssize_t undock_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct dock_station *dock_station = dev->platform_data;
+
+ if (!count)
+ return -EINVAL;
+
+ acpi_scan_lock_acquire();
+ begin_undock(dock_station);
+ ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
+ acpi_scan_lock_release();
+ return ret ? ret : count;
+}
+static DEVICE_ATTR_WO(undock);
+
+/*
+ * show_dock_uid - read method for "uid" file in sysfs
+ */
+static ssize_t uid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long lbuf;
+ struct dock_station *dock_station = dev->platform_data;
+
+ acpi_status status = acpi_evaluate_integer(dock_station->handle,
+ "_UID", NULL, &lbuf);
+ if (ACPI_FAILURE(status))
+ return 0;
+
+ return sysfs_emit(buf, "%llx\n", lbuf);
+}
+static DEVICE_ATTR_RO(uid);
+
+static ssize_t type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dock_station *dock_station = dev->platform_data;
+ char *type;
+
+ if (dock_station->flags & DOCK_IS_DOCK)
+ type = "dock_station";
+ else if (dock_station->flags & DOCK_IS_ATA)
+ type = "ata_bay";
+ else if (dock_station->flags & DOCK_IS_BAT)
+ type = "battery_bay";
+ else
+ type = "unknown";
+
+ return sysfs_emit(buf, "%s\n", type);
+}
+static DEVICE_ATTR_RO(type);
+
+static struct attribute *dock_attributes[] = {
+ &dev_attr_docked.attr,
+ &dev_attr_flags.attr,
+ &dev_attr_undock.attr,
+ &dev_attr_uid.attr,
+ &dev_attr_type.attr,
+ NULL
+};
+
+static const struct attribute_group dock_attribute_group = {
+ .attrs = dock_attributes
+};
+
+/**
+ * acpi_dock_add - Add a new dock station
+ * @adev: Dock station ACPI device object.
+ *
+ * allocated and initialize a new dock station device.
+ */
+void acpi_dock_add(struct acpi_device *adev)
+{
+ struct dock_station *dock_station, ds = { NULL, };
+ struct platform_device_info pdevinfo;
+ acpi_handle handle = adev->handle;
+ struct platform_device *dd;
+ int ret;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo.name = "dock";
+ pdevinfo.id = dock_station_count;
+ pdevinfo.fwnode = acpi_fwnode_handle(adev);
+ pdevinfo.data = &ds;
+ pdevinfo.size_data = sizeof(ds);
+ dd = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(dd))
+ return;
+
+ dock_station = dd->dev.platform_data;
+
+ dock_station->handle = handle;
+ dock_station->dock_device = dd;
+ dock_station->last_dock_time = jiffies - HZ;
+
+ INIT_LIST_HEAD(&dock_station->sibling);
+ INIT_LIST_HEAD(&dock_station->dependent_devices);
+
+ /* we want the dock device to send uevents */
+ dev_set_uevent_suppress(&dd->dev, 0);
+
+ if (acpi_dock_match(handle))
+ dock_station->flags |= DOCK_IS_DOCK;
+ if (acpi_ata_match(handle))
+ dock_station->flags |= DOCK_IS_ATA;
+ if (acpi_device_is_battery(adev))
+ dock_station->flags |= DOCK_IS_BAT;
+
+ ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
+ if (ret)
+ goto err_unregister;
+
+ /* add the dock station as a device dependent on itself */
+ ret = add_dock_dependent_device(dock_station, adev);
+ if (ret)
+ goto err_rmgroup;
+
+ dock_station_count++;
+ list_add(&dock_station->sibling, &dock_stations);
+ adev->flags.is_dock_station = true;
+ dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n",
+ dock_station_count);
+ return;
+
+err_rmgroup:
+ sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);
+
+err_unregister:
+ platform_device_unregister(dd);
+ acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret);
+}
diff --git a/drivers/acpi/dptf/Kconfig b/drivers/acpi/dptf/Kconfig
new file mode 100644
index 000000000..4b3fdc03e
--- /dev/null
+++ b/drivers/acpi/dptf/Kconfig
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menuconfig ACPI_DPTF
+ bool "Intel DPTF (Dynamic Platform and Thermal Framework) Support"
+ depends on X86
+ help
+ Intel Dynamic Platform and Thermal Framework (DPTF) is a platform
+ level hardware/software solution for power and thermal management.
+
+ As a container for multiple power/thermal technologies, DPTF provides
+ a coordinated approach for different policies to effect the hardware
+ state of a system.
+
+if ACPI_DPTF
+
+config DPTF_POWER
+ tristate "Platform Power DPTF Participant"
+ default m
+ help
+ This driver adds support for Dynamic Platform and Thermal Framework
+ (DPTF) Platform Power Participant device (INT3407) support.
+ This participant is responsible for exposing platform telemetry:
+ max_platform_power
+ platform_power_source
+ adapter_rating
+ battery_steady_power
+ charger_type
+
+ To compile this driver as a module, choose M here:
+ the module will be called dptf_power.
+
+config DPTF_PCH_FIVR
+ tristate "PCH FIVR DPTF Participant"
+ default m
+ help
+ This driver adds support for Dynamic Platform and Thermal Framework
+ (DPTF) PCH FIVR Participant device support. This driver allows to
+ switch the PCH FIVR (Fully Integrated Voltage Regulator) frequency.
+ This participant is responsible for exposing:
+ freq_mhz_low_clock
+ freq_mhz_high_clock
+
+ To compile this driver as a module, choose M here:
+ the module will be called dptf_pch_fivr.
+
+endif
diff --git a/drivers/acpi/dptf/Makefile b/drivers/acpi/dptf/Makefile
new file mode 100644
index 000000000..297340682
--- /dev/null
+++ b/drivers/acpi/dptf/Makefile
@@ -0,0 +1,4 @@
+# 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
new file mode 100644
index 000000000..4919e7abe
--- /dev/null
+++ b/drivers/acpi/dptf/dptf_pch_fivr.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dptf_pch_fivr: DPTF PCH FIVR Participant driver
+ * Copyright (c) 2020, Intel Corporation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+struct pch_fivr_resp {
+ u64 status;
+ u64 result;
+};
+
+static int pch_fivr_read(acpi_handle handle, char *method, struct pch_fivr_resp *fivr_resp)
+{
+ struct acpi_buffer resp = { sizeof(struct pch_fivr_resp), fivr_resp};
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer format = { sizeof("NN"), "NN" };
+ union acpi_object *obj;
+ acpi_status status;
+ int ret = -EFAULT;
+
+ status = acpi_evaluate_object(handle, method, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return ret;
+
+ obj = buffer.pointer;
+ if (!obj || obj->type != ACPI_TYPE_PACKAGE)
+ goto release_buffer;
+
+ status = acpi_extract_package(obj, &format, &resp);
+ if (ACPI_FAILURE(status))
+ goto release_buffer;
+
+ if (fivr_resp->status)
+ goto release_buffer;
+
+ ret = 0;
+
+release_buffer:
+ kfree(buffer.pointer);
+ return ret;
+}
+
+/*
+ * Presentation of attributes which are defined for INTC10xx
+ * They are:
+ * freq_mhz_low_clock : Set PCH FIVR switching freq for
+ * FIVR clock 19.2MHz and 24MHz
+ * freq_mhz_high_clock : Set PCH FIVR switching freq for
+ * FIVR clock 38.4MHz
+ */
+#define PCH_FIVR_SHOW(name, method) \
+static ssize_t name##_show(struct device *dev,\
+ struct device_attribute *attr,\
+ char *buf)\
+{\
+ struct acpi_device *acpi_dev = dev_get_drvdata(dev);\
+ struct pch_fivr_resp fivr_resp;\
+ int status;\
+\
+ status = pch_fivr_read(acpi_dev->handle, #method, &fivr_resp);\
+ if (status)\
+ return status;\
+\
+ return sprintf(buf, "%llu\n", fivr_resp.result);\
+}
+
+#define PCH_FIVR_STORE(name, method) \
+static ssize_t name##_store(struct device *dev,\
+ struct device_attribute *attr,\
+ const char *buf, size_t count)\
+{\
+ struct acpi_device *acpi_dev = dev_get_drvdata(dev);\
+ acpi_status status;\
+ u32 val;\
+\
+ if (kstrtouint(buf, 0, &val) < 0)\
+ return -EINVAL;\
+\
+ status = acpi_execute_simple_method(acpi_dev->handle, #method, val);\
+ if (ACPI_SUCCESS(status))\
+ return count;\
+\
+ return -EINVAL;\
+}
+
+PCH_FIVR_SHOW(freq_mhz_low_clock, GFC0)
+PCH_FIVR_SHOW(freq_mhz_high_clock, GFC1)
+PCH_FIVR_SHOW(ssc_clock_info, GEMI)
+PCH_FIVR_SHOW(fivr_switching_freq_mhz, GFCS)
+PCH_FIVR_SHOW(fivr_switching_fault_status, GFFS)
+PCH_FIVR_STORE(freq_mhz_low_clock, RFC0)
+PCH_FIVR_STORE(freq_mhz_high_clock, RFC1)
+
+static DEVICE_ATTR_RW(freq_mhz_low_clock);
+static DEVICE_ATTR_RW(freq_mhz_high_clock);
+static DEVICE_ATTR_RO(ssc_clock_info);
+static DEVICE_ATTR_RO(fivr_switching_freq_mhz);
+static DEVICE_ATTR_RO(fivr_switching_fault_status);
+
+static struct attribute *fivr_attrs[] = {
+ &dev_attr_freq_mhz_low_clock.attr,
+ &dev_attr_freq_mhz_high_clock.attr,
+ &dev_attr_ssc_clock_info.attr,
+ &dev_attr_fivr_switching_freq_mhz.attr,
+ &dev_attr_fivr_switching_fault_status.attr,
+ NULL
+};
+
+static const struct attribute_group pch_fivr_attribute_group = {
+ .attrs = fivr_attrs,
+ .name = "pch_fivr_switch_frequency"
+};
+
+static int pch_fivr_add(struct platform_device *pdev)
+{
+ struct acpi_device *acpi_dev;
+ unsigned long long ptype;
+ acpi_status status;
+ int result;
+
+ acpi_dev = ACPI_COMPANION(&(pdev->dev));
+ if (!acpi_dev)
+ return -ENODEV;
+
+ status = acpi_evaluate_integer(acpi_dev->handle, "PTYP", NULL, &ptype);
+ if (ACPI_FAILURE(status) || ptype != 0x05)
+ return -ENODEV;
+
+ result = sysfs_create_group(&pdev->dev.kobj,
+ &pch_fivr_attribute_group);
+ if (result)
+ return result;
+
+ platform_set_drvdata(pdev, acpi_dev);
+
+ return 0;
+}
+
+static int 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},
+ {"INTC10A3", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, pch_fivr_device_ids);
+
+static struct platform_driver pch_fivr_driver = {
+ .probe = pch_fivr_add,
+ .remove = pch_fivr_remove,
+ .driver = {
+ .name = "dptf_pch_fivr",
+ .acpi_match_table = pch_fivr_device_ids,
+ },
+};
+
+module_platform_driver(pch_fivr_driver);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ACPI DPTF PCH FIVR driver");
diff --git a/drivers/acpi/dptf/dptf_power.c b/drivers/acpi/dptf/dptf_power.c
new file mode 100644
index 000000000..86561eda9
--- /dev/null
+++ b/drivers/acpi/dptf/dptf_power.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dptf_power: DPTF platform power driver
+ * Copyright (c) 2016, Intel Corporation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+
+/*
+ * Presentation of attributes which are defined for INT3407 and INT3532.
+ * They are:
+ * PMAX : Maximum platform power
+ * PSRC : Platform power source
+ * ARTG : Adapter rating
+ * CTYP : Charger type
+ * PROP : Rest of worst case platform Power
+ * PBSS : Power Battery Steady State
+ * RBHF : High Frequency Impedance
+ * VBNL : Instantaneous No-Load Voltage
+ * CMPP : Current Discharge Capability
+ */
+#define DPTF_POWER_SHOW(name, object) \
+static ssize_t name##_show(struct device *dev,\
+ struct device_attribute *attr,\
+ char *buf)\
+{\
+ struct acpi_device *acpi_dev = dev_get_drvdata(dev);\
+ unsigned long long val;\
+ acpi_status status;\
+\
+ status = acpi_evaluate_integer(acpi_dev->handle, #object,\
+ NULL, &val);\
+ if (ACPI_SUCCESS(status))\
+ return sprintf(buf, "%d\n", (int)val);\
+ else \
+ return -EINVAL;\
+}
+
+DPTF_POWER_SHOW(max_platform_power_mw, PMAX)
+DPTF_POWER_SHOW(platform_power_source, PSRC)
+DPTF_POWER_SHOW(adapter_rating_mw, ARTG)
+DPTF_POWER_SHOW(battery_steady_power_mw, PBSS)
+DPTF_POWER_SHOW(charger_type, CTYP)
+DPTF_POWER_SHOW(rest_of_platform_power_mw, PROP)
+DPTF_POWER_SHOW(max_steady_state_power_mw, PBSS)
+DPTF_POWER_SHOW(high_freq_impedance_mohm, RBHF)
+DPTF_POWER_SHOW(no_load_voltage_mv, VBNL)
+DPTF_POWER_SHOW(current_discharge_capbility_ma, CMPP);
+
+static DEVICE_ATTR_RO(max_platform_power_mw);
+static DEVICE_ATTR_RO(platform_power_source);
+static DEVICE_ATTR_RO(adapter_rating_mw);
+static DEVICE_ATTR_RO(battery_steady_power_mw);
+static DEVICE_ATTR_RO(charger_type);
+static DEVICE_ATTR_RO(rest_of_platform_power_mw);
+static DEVICE_ATTR_RO(max_steady_state_power_mw);
+static DEVICE_ATTR_RO(high_freq_impedance_mohm);
+static DEVICE_ATTR_RO(no_load_voltage_mv);
+static DEVICE_ATTR_RO(current_discharge_capbility_ma);
+
+static ssize_t prochot_confirm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi_dev = dev_get_drvdata(dev);
+ acpi_status status;
+ int seq_no;
+
+ if (kstrtouint(buf, 0, &seq_no) < 0)
+ return -EINVAL;
+
+ status = acpi_execute_simple_method(acpi_dev->handle, "PBOK", seq_no);
+ if (ACPI_SUCCESS(status))
+ return count;
+
+ return -EINVAL;
+}
+
+static DEVICE_ATTR_WO(prochot_confirm);
+
+static struct attribute *dptf_power_attrs[] = {
+ &dev_attr_max_platform_power_mw.attr,
+ &dev_attr_platform_power_source.attr,
+ &dev_attr_adapter_rating_mw.attr,
+ &dev_attr_battery_steady_power_mw.attr,
+ &dev_attr_charger_type.attr,
+ &dev_attr_rest_of_platform_power_mw.attr,
+ &dev_attr_prochot_confirm.attr,
+ NULL
+};
+
+static const struct attribute_group dptf_power_attribute_group = {
+ .attrs = dptf_power_attrs,
+ .name = "dptf_power"
+};
+
+static struct attribute *dptf_battery_attrs[] = {
+ &dev_attr_max_platform_power_mw.attr,
+ &dev_attr_max_steady_state_power_mw.attr,
+ &dev_attr_high_freq_impedance_mohm.attr,
+ &dev_attr_no_load_voltage_mv.attr,
+ &dev_attr_current_discharge_capbility_ma.attr,
+ NULL
+};
+
+static const struct attribute_group dptf_battery_attribute_group = {
+ .attrs = dptf_battery_attrs,
+ .name = "dptf_battery"
+};
+
+#define MAX_POWER_CHANGED 0x80
+#define POWER_STATE_CHANGED 0x81
+#define STEADY_STATE_POWER_CHANGED 0x83
+#define POWER_PROP_CHANGE_EVENT 0x84
+#define IMPEDANCE_CHANGED 0x85
+#define VOLTAGE_CURRENT_CHANGED 0x86
+
+static long long dptf_participant_type(acpi_handle handle)
+{
+ unsigned long long ptype;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(handle, "PTYP", NULL, &ptype);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ return ptype;
+}
+
+static void dptf_power_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct platform_device *pdev = data;
+ char *attr;
+
+ switch (event) {
+ case POWER_STATE_CHANGED:
+ attr = "platform_power_source";
+ break;
+ case POWER_PROP_CHANGE_EVENT:
+ attr = "rest_of_platform_power_mw";
+ break;
+ case MAX_POWER_CHANGED:
+ attr = "max_platform_power_mw";
+ break;
+ case STEADY_STATE_POWER_CHANGED:
+ attr = "max_steady_state_power_mw";
+ break;
+ case IMPEDANCE_CHANGED:
+ attr = "high_freq_impedance_mohm";
+ break;
+ case VOLTAGE_CURRENT_CHANGED:
+ attr = "no_load_voltage_mv";
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported event [0x%x]\n", event);
+ return;
+ }
+
+ /*
+ * Notify that an attribute is changed, so that user space can read
+ * again.
+ */
+ if (dptf_participant_type(handle) == 0x0CULL)
+ sysfs_notify(&pdev->dev.kobj, "dptf_battery", attr);
+ else
+ sysfs_notify(&pdev->dev.kobj, "dptf_power", attr);
+}
+
+static int dptf_power_add(struct platform_device *pdev)
+{
+ const struct attribute_group *attr_group;
+ struct acpi_device *acpi_dev;
+ unsigned long long ptype;
+ int result;
+
+ acpi_dev = ACPI_COMPANION(&(pdev->dev));
+ if (!acpi_dev)
+ return -ENODEV;
+
+ ptype = dptf_participant_type(acpi_dev->handle);
+ if (ptype == 0x11)
+ attr_group = &dptf_power_attribute_group;
+ else if (ptype == 0x0C)
+ attr_group = &dptf_battery_attribute_group;
+ else
+ return -ENODEV;
+
+ result = acpi_install_notify_handler(acpi_dev->handle,
+ ACPI_DEVICE_NOTIFY,
+ dptf_power_notify,
+ (void *)pdev);
+ if (result)
+ return result;
+
+ result = sysfs_create_group(&pdev->dev.kobj,
+ attr_group);
+ if (result) {
+ acpi_remove_notify_handler(acpi_dev->handle,
+ ACPI_DEVICE_NOTIFY,
+ dptf_power_notify);
+ return result;
+ }
+
+ platform_set_drvdata(pdev, acpi_dev);
+
+ return 0;
+}
+
+static int dptf_power_remove(struct platform_device *pdev)
+{
+ struct acpi_device *acpi_dev = platform_get_drvdata(pdev);
+
+ acpi_remove_notify_handler(acpi_dev->handle,
+ ACPI_DEVICE_NOTIFY,
+ dptf_power_notify);
+
+ if (dptf_participant_type(acpi_dev->handle) == 0x0CULL)
+ 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[] = {
+ {"INT3407", 0},
+ {"INT3532", 0},
+ {"INTC1047", 0},
+ {"INTC1050", 0},
+ {"INTC1060", 0},
+ {"INTC1061", 0},
+ {"INTC1065", 0},
+ {"INTC1066", 0},
+ {"INTC10A4", 0},
+ {"INTC10A5", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, int3407_device_ids);
+
+static struct platform_driver dptf_power_driver = {
+ .probe = dptf_power_add,
+ .remove = dptf_power_remove,
+ .driver = {
+ .name = "dptf_power",
+ .acpi_match_table = int3407_device_ids,
+ },
+};
+
+module_platform_driver(dptf_power_driver);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ACPI DPTF platform power driver");
diff --git a/drivers/acpi/dptf/int340x_thermal.c b/drivers/acpi/dptf/int340x_thermal.c
new file mode 100644
index 000000000..b7113fa92
--- /dev/null
+++ b/drivers/acpi/dptf/int340x_thermal.c
@@ -0,0 +1,75 @@
+// 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
new file mode 100644
index 000000000..77d1f2cb8
--- /dev/null
+++ b/drivers/acpi/ec.c
@@ -0,0 +1,2249 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ec.c - ACPI Embedded Controller Driver (v3)
+ *
+ * Copyright (C) 2001-2015 Intel Corporation
+ * Author: 2014, 2015 Lv Zheng <lv.zheng@intel.com>
+ * 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
+ * 2006 Denis Sadykov <denis.m.sadykov@intel.com>
+ * 2004 Luming Yu <luming.yu@intel.com>
+ * 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2008 Alexey Starikovskiy <astarikovskiy@suse.de>
+ */
+
+/* Uncomment next line to get verbose printout */
+/* #define DEBUG */
+#define pr_fmt(fmt) "ACPI: EC: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <asm/io.h>
+
+#include "internal.h"
+
+#define ACPI_EC_CLASS "embedded_controller"
+#define ACPI_EC_DEVICE_NAME "Embedded Controller"
+
+/* EC status register */
+#define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */
+#define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */
+#define ACPI_EC_FLAG_CMD 0x08 /* Input buffer contains a command */
+#define ACPI_EC_FLAG_BURST 0x10 /* burst mode */
+#define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */
+
+/*
+ * The SCI_EVT clearing timing is not defined by the ACPI specification.
+ * This leads to lots of practical timing issues for the host EC driver.
+ * The following variations are defined (from the target EC firmware's
+ * perspective):
+ * STATUS: After indicating SCI_EVT edge triggered IRQ to the host, the
+ * target can clear SCI_EVT at any time so long as the host can see
+ * the indication by reading the status register (EC_SC). So the
+ * host should re-check SCI_EVT after the first time the SCI_EVT
+ * indication is seen, which is the same time the query request
+ * (QR_EC) is written to the command register (EC_CMD). SCI_EVT set
+ * at any later time could indicate another event. Normally such
+ * kind of EC firmware has implemented an event queue and will
+ * return 0x00 to indicate "no outstanding event".
+ * QUERY: After seeing the query request (QR_EC) written to the command
+ * register (EC_CMD) by the host and having prepared the responding
+ * event value in the data register (EC_DATA), the target can safely
+ * clear SCI_EVT because the target can confirm that the current
+ * event is being handled by the host. The host then should check
+ * SCI_EVT right after reading the event response from the data
+ * register (EC_DATA).
+ * EVENT: After seeing the event response read from the data register
+ * (EC_DATA) by the host, the target can clear SCI_EVT. As the
+ * target requires time to notice the change in the data register
+ * (EC_DATA), the host may be required to wait additional guarding
+ * time before checking the SCI_EVT again. Such guarding may not be
+ * necessary if the host is notified via another IRQ.
+ */
+#define ACPI_EC_EVT_TIMING_STATUS 0x00
+#define ACPI_EC_EVT_TIMING_QUERY 0x01
+#define ACPI_EC_EVT_TIMING_EVENT 0x02
+
+/* EC commands */
+enum ec_command {
+ ACPI_EC_COMMAND_READ = 0x80,
+ ACPI_EC_COMMAND_WRITE = 0x81,
+ ACPI_EC_BURST_ENABLE = 0x82,
+ ACPI_EC_BURST_DISABLE = 0x83,
+ ACPI_EC_COMMAND_QUERY = 0x84,
+};
+
+#define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */
+#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */
+#define ACPI_EC_UDELAY_POLL 550 /* Wait 1ms for EC transaction polling */
+#define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query
+ * when trying to clear the EC */
+#define ACPI_EC_MAX_QUERIES 16 /* Maximum number of parallel queries */
+
+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_QUERY_METHODS_INSTALLED, /* _Qxx handlers installed */
+ EC_FLAGS_STARTED, /* Driver is started */
+ EC_FLAGS_STOPPED, /* Driver is stopped */
+ EC_FLAGS_EVENTS_MASKED, /* Events masked */
+};
+
+#define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */
+#define ACPI_EC_COMMAND_COMPLETE 0x02 /* Completed last byte */
+
+/* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */
+static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY;
+module_param(ec_delay, uint, 0644);
+MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes");
+
+static unsigned int ec_max_queries __read_mostly = ACPI_EC_MAX_QUERIES;
+module_param(ec_max_queries, uint, 0644);
+MODULE_PARM_DESC(ec_max_queries, "Maximum parallel _Qxx evaluations");
+
+static bool ec_busy_polling __read_mostly;
+module_param(ec_busy_polling, bool, 0644);
+MODULE_PARM_DESC(ec_busy_polling, "Use busy polling to advance EC transaction");
+
+static unsigned int ec_polling_guard __read_mostly = ACPI_EC_UDELAY_POLL;
+module_param(ec_polling_guard, uint, 0644);
+MODULE_PARM_DESC(ec_polling_guard, "Guard time(us) between EC accesses in polling modes");
+
+static unsigned int ec_event_clearing __read_mostly = ACPI_EC_EVT_TIMING_QUERY;
+
+/*
+ * If the number of false interrupts per one transaction exceeds
+ * this threshold, will think there is a GPE storm happened and
+ * will disable the GPE for normal transaction.
+ */
+static unsigned int ec_storm_threshold __read_mostly = 8;
+module_param(ec_storm_threshold, uint, 0644);
+MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm");
+
+static bool ec_freeze_events __read_mostly;
+module_param(ec_freeze_events, bool, 0644);
+MODULE_PARM_DESC(ec_freeze_events, "Disabling event handling during suspend/resume");
+
+static bool ec_no_wakeup __read_mostly;
+module_param(ec_no_wakeup, bool, 0644);
+MODULE_PARM_DESC(ec_no_wakeup, "Do not wake up from suspend-to-idle");
+
+struct acpi_ec_query_handler {
+ struct list_head node;
+ acpi_ec_query_func func;
+ acpi_handle handle;
+ void *data;
+ u8 query_bit;
+ struct kref kref;
+};
+
+struct transaction {
+ const u8 *wdata;
+ u8 *rdata;
+ unsigned short irq_count;
+ u8 command;
+ u8 wi;
+ u8 ri;
+ u8 wlen;
+ u8 rlen;
+ u8 flags;
+};
+
+struct acpi_ec_query {
+ struct transaction transaction;
+ struct work_struct work;
+ struct acpi_ec_query_handler *handler;
+ struct acpi_ec *ec;
+};
+
+static int acpi_ec_submit_query(struct acpi_ec *ec);
+static void advance_transaction(struct acpi_ec *ec, bool interrupt);
+static void acpi_ec_event_handler(struct work_struct *work);
+
+struct acpi_ec *first_ec;
+EXPORT_SYMBOL(first_ec);
+
+static struct acpi_ec *boot_ec;
+static bool boot_ec_is_ecdt;
+static struct workqueue_struct *ec_wq;
+static struct workqueue_struct *ec_query_wq;
+
+static int EC_FLAGS_CORRECT_ECDT; /* Needs ECDT port address correction */
+static int EC_FLAGS_TRUST_DSDT_GPE; /* Needs DSDT GPE as correction setting */
+static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
+
+/* --------------------------------------------------------------------------
+ * Logging/Debugging
+ * -------------------------------------------------------------------------- */
+
+/*
+ * Splitters used by the developers to track the boundary of the EC
+ * handling processes.
+ */
+#ifdef DEBUG
+#define EC_DBG_SEP " "
+#define EC_DBG_DRV "+++++"
+#define EC_DBG_STM "====="
+#define EC_DBG_REQ "*****"
+#define EC_DBG_EVT "#####"
+#else
+#define EC_DBG_SEP ""
+#define EC_DBG_DRV
+#define EC_DBG_STM
+#define EC_DBG_REQ
+#define EC_DBG_EVT
+#endif
+
+#define ec_log_raw(fmt, ...) \
+ pr_info(fmt "\n", ##__VA_ARGS__)
+#define ec_dbg_raw(fmt, ...) \
+ pr_debug(fmt "\n", ##__VA_ARGS__)
+#define ec_log(filter, fmt, ...) \
+ ec_log_raw(filter EC_DBG_SEP fmt EC_DBG_SEP filter, ##__VA_ARGS__)
+#define ec_dbg(filter, fmt, ...) \
+ ec_dbg_raw(filter EC_DBG_SEP fmt EC_DBG_SEP filter, ##__VA_ARGS__)
+
+#define ec_log_drv(fmt, ...) \
+ ec_log(EC_DBG_DRV, fmt, ##__VA_ARGS__)
+#define ec_dbg_drv(fmt, ...) \
+ ec_dbg(EC_DBG_DRV, fmt, ##__VA_ARGS__)
+#define ec_dbg_stm(fmt, ...) \
+ ec_dbg(EC_DBG_STM, fmt, ##__VA_ARGS__)
+#define ec_dbg_req(fmt, ...) \
+ ec_dbg(EC_DBG_REQ, fmt, ##__VA_ARGS__)
+#define ec_dbg_evt(fmt, ...) \
+ ec_dbg(EC_DBG_EVT, fmt, ##__VA_ARGS__)
+#define ec_dbg_ref(ec, fmt, ...) \
+ ec_dbg_raw("%lu: " fmt, ec->reference_count, ## __VA_ARGS__)
+
+/* --------------------------------------------------------------------------
+ * Device Flags
+ * -------------------------------------------------------------------------- */
+
+static bool acpi_ec_started(struct acpi_ec *ec)
+{
+ return test_bit(EC_FLAGS_STARTED, &ec->flags) &&
+ !test_bit(EC_FLAGS_STOPPED, &ec->flags);
+}
+
+static bool acpi_ec_event_enabled(struct acpi_ec *ec)
+{
+ /*
+ * There is an OSPM early stage logic. During the early stages
+ * (boot/resume), OSPMs shouldn't enable the event handling, only
+ * the EC transactions are allowed to be performed.
+ */
+ if (!test_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
+ return false;
+ /*
+ * However, disabling the event handling is experimental for late
+ * stage (suspend), and is controlled by the boot parameter of
+ * "ec_freeze_events":
+ * 1. true: The EC event handling is disabled before entering
+ * the noirq stage.
+ * 2. false: The EC event handling is automatically disabled as
+ * soon as the EC driver is stopped.
+ */
+ if (ec_freeze_events)
+ return acpi_ec_started(ec);
+ else
+ return test_bit(EC_FLAGS_STARTED, &ec->flags);
+}
+
+static bool acpi_ec_flushed(struct acpi_ec *ec)
+{
+ return ec->reference_count == 1;
+}
+
+/* --------------------------------------------------------------------------
+ * EC Registers
+ * -------------------------------------------------------------------------- */
+
+static inline u8 acpi_ec_read_status(struct acpi_ec *ec)
+{
+ u8 x = inb(ec->command_addr);
+
+ ec_dbg_raw("EC_SC(R) = 0x%2.2x "
+ "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d",
+ x,
+ !!(x & ACPI_EC_FLAG_SCI),
+ !!(x & ACPI_EC_FLAG_BURST),
+ !!(x & ACPI_EC_FLAG_CMD),
+ !!(x & ACPI_EC_FLAG_IBF),
+ !!(x & ACPI_EC_FLAG_OBF));
+ return x;
+}
+
+static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
+{
+ u8 x = inb(ec->data_addr);
+
+ ec->timestamp = jiffies;
+ ec_dbg_raw("EC_DATA(R) = 0x%2.2x", x);
+ return x;
+}
+
+static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
+{
+ ec_dbg_raw("EC_SC(W) = 0x%2.2x", command);
+ outb(command, ec->command_addr);
+ ec->timestamp = jiffies;
+}
+
+static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data)
+{
+ ec_dbg_raw("EC_DATA(W) = 0x%2.2x", data);
+ outb(data, ec->data_addr);
+ ec->timestamp = jiffies;
+}
+
+#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
+static const char *acpi_ec_cmd_string(u8 cmd)
+{
+ switch (cmd) {
+ case 0x80:
+ return "RD_EC";
+ case 0x81:
+ return "WR_EC";
+ case 0x82:
+ return "BE_EC";
+ case 0x83:
+ return "BD_EC";
+ case 0x84:
+ return "QR_EC";
+ }
+ return "UNKNOWN";
+}
+#else
+#define acpi_ec_cmd_string(cmd) "UNDEF"
+#endif
+
+/* --------------------------------------------------------------------------
+ * GPE Registers
+ * -------------------------------------------------------------------------- */
+
+static inline bool acpi_ec_gpe_status_set(struct acpi_ec *ec)
+{
+ acpi_event_status gpe_status = 0;
+
+ (void)acpi_get_gpe_status(NULL, ec->gpe, &gpe_status);
+ return !!(gpe_status & ACPI_EVENT_FLAG_STATUS_SET);
+}
+
+static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open)
+{
+ if (open)
+ acpi_enable_gpe(NULL, ec->gpe);
+ else {
+ BUG_ON(ec->reference_count < 1);
+ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE);
+ }
+ if (acpi_ec_gpe_status_set(ec)) {
+ /*
+ * On some platforms, EN=1 writes cannot trigger GPE. So
+ * software need to manually trigger a pseudo GPE event on
+ * EN=1 writes.
+ */
+ ec_dbg_raw("Polling quirk");
+ advance_transaction(ec, false);
+ }
+}
+
+static inline void acpi_ec_disable_gpe(struct acpi_ec *ec, bool close)
+{
+ if (close)
+ acpi_disable_gpe(NULL, ec->gpe);
+ else {
+ BUG_ON(ec->reference_count < 1);
+ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE);
+ }
+}
+
+/* --------------------------------------------------------------------------
+ * Transaction Management
+ * -------------------------------------------------------------------------- */
+
+static void acpi_ec_submit_request(struct acpi_ec *ec)
+{
+ ec->reference_count++;
+ if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags) &&
+ ec->gpe >= 0 && ec->reference_count == 1)
+ acpi_ec_enable_gpe(ec, true);
+}
+
+static void acpi_ec_complete_request(struct acpi_ec *ec)
+{
+ bool flushed = false;
+
+ ec->reference_count--;
+ if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags) &&
+ ec->gpe >= 0 && ec->reference_count == 0)
+ acpi_ec_disable_gpe(ec, true);
+ flushed = acpi_ec_flushed(ec);
+ if (flushed)
+ wake_up(&ec->wait);
+}
+
+static void acpi_ec_mask_events(struct acpi_ec *ec)
+{
+ if (!test_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags)) {
+ if (ec->gpe >= 0)
+ acpi_ec_disable_gpe(ec, false);
+ else
+ disable_irq_nosync(ec->irq);
+
+ ec_dbg_drv("Polling enabled");
+ set_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags);
+ }
+}
+
+static void acpi_ec_unmask_events(struct acpi_ec *ec)
+{
+ if (test_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags)) {
+ clear_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags);
+ if (ec->gpe >= 0)
+ acpi_ec_enable_gpe(ec, false);
+ else
+ enable_irq(ec->irq);
+
+ ec_dbg_drv("Polling disabled");
+ }
+}
+
+/*
+ * acpi_ec_submit_flushable_request() - Increase the reference count unless
+ * the flush operation is not in
+ * progress
+ * @ec: the EC device
+ *
+ * This function must be used before taking a new action that should hold
+ * the reference count. If this function returns false, then the action
+ * must be discarded or it will prevent the flush operation from being
+ * completed.
+ */
+static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec)
+{
+ if (!acpi_ec_started(ec))
+ return false;
+ acpi_ec_submit_request(ec);
+ return true;
+}
+
+static void acpi_ec_submit_event(struct acpi_ec *ec)
+{
+ /*
+ * It is safe to mask the events here, because acpi_ec_close_event()
+ * will run at least once after this.
+ */
+ acpi_ec_mask_events(ec);
+ if (!acpi_ec_event_enabled(ec))
+ return;
+
+ if (ec->event_state != EC_EVENT_READY)
+ return;
+
+ ec_dbg_evt("Command(%s) submitted/blocked",
+ acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
+
+ ec->event_state = EC_EVENT_IN_PROGRESS;
+ /*
+ * If events_to_process is greater than 0 at this point, the while ()
+ * loop in acpi_ec_event_handler() is still running and incrementing
+ * events_to_process will cause it to invoke acpi_ec_submit_query() once
+ * more, so it is not necessary to queue up the event work to start the
+ * same loop again.
+ */
+ if (ec->events_to_process++ > 0)
+ return;
+
+ ec->events_in_progress++;
+ queue_work(ec_wq, &ec->work);
+}
+
+static void acpi_ec_complete_event(struct acpi_ec *ec)
+{
+ if (ec->event_state == EC_EVENT_IN_PROGRESS)
+ ec->event_state = EC_EVENT_COMPLETE;
+}
+
+static void acpi_ec_close_event(struct acpi_ec *ec)
+{
+ if (ec->event_state != EC_EVENT_READY)
+ ec_dbg_evt("Command(%s) unblocked",
+ acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
+
+ ec->event_state = EC_EVENT_READY;
+ acpi_ec_unmask_events(ec);
+}
+
+static inline void __acpi_ec_enable_event(struct acpi_ec *ec)
+{
+ if (!test_and_set_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
+ ec_log_drv("event unblocked");
+ /*
+ * Unconditionally invoke this once after enabling the event
+ * handling mechanism to detect the pending events.
+ */
+ advance_transaction(ec, false);
+}
+
+static inline void __acpi_ec_disable_event(struct acpi_ec *ec)
+{
+ if (test_and_clear_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
+ ec_log_drv("event blocked");
+}
+
+/*
+ * Process _Q events that might have accumulated in the EC.
+ * Run with locked ec mutex.
+ */
+static void acpi_ec_clear(struct acpi_ec *ec)
+{
+ int i;
+
+ for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
+ if (acpi_ec_submit_query(ec))
+ break;
+ }
+ if (unlikely(i == ACPI_EC_CLEAR_MAX))
+ pr_warn("Warning: Maximum of %d stale EC events cleared\n", i);
+ else
+ pr_info("%d stale EC events cleared\n", i);
+}
+
+static void acpi_ec_enable_event(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ if (acpi_ec_started(ec))
+ __acpi_ec_enable_event(ec);
+ spin_unlock_irqrestore(&ec->lock, flags);
+
+ /* Drain additional events if hardware requires that */
+ if (EC_FLAGS_CLEAR_ON_RESUME)
+ acpi_ec_clear(ec);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void __acpi_ec_flush_work(void)
+{
+ flush_workqueue(ec_wq); /* flush ec->work */
+ flush_workqueue(ec_query_wq); /* flush queries */
+}
+
+static void acpi_ec_disable_event(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ __acpi_ec_disable_event(ec);
+ spin_unlock_irqrestore(&ec->lock, flags);
+
+ /*
+ * When ec_freeze_events is true, we need to flush events in
+ * the proper position before entering the noirq stage.
+ */
+ __acpi_ec_flush_work();
+}
+
+void acpi_ec_flush_work(void)
+{
+ /* Without ec_wq there is nothing to flush. */
+ if (!ec_wq)
+ return;
+
+ __acpi_ec_flush_work();
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static bool acpi_ec_guard_event(struct acpi_ec *ec)
+{
+ unsigned long flags;
+ bool guarded;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ /*
+ * If firmware SCI_EVT clearing timing is "event", we actually
+ * don't know when the SCI_EVT will be cleared by firmware after
+ * evaluating _Qxx, so we need to re-check SCI_EVT after waiting an
+ * acceptable period.
+ *
+ * The guarding period is applicable if the event state is not
+ * EC_EVENT_READY, but otherwise if the current transaction is of the
+ * ACPI_EC_COMMAND_QUERY type, the guarding should have elapsed already
+ * and it should not be applied to let the transaction transition into
+ * the ACPI_EC_COMMAND_POLL state immediately.
+ */
+ guarded = ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT &&
+ ec->event_state != EC_EVENT_READY &&
+ (!ec->curr || ec->curr->command != ACPI_EC_COMMAND_QUERY);
+ spin_unlock_irqrestore(&ec->lock, flags);
+ return guarded;
+}
+
+static int ec_transaction_polled(struct acpi_ec *ec)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_POLL))
+ ret = 1;
+ spin_unlock_irqrestore(&ec->lock, flags);
+ return ret;
+}
+
+static int ec_transaction_completed(struct acpi_ec *ec)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE))
+ ret = 1;
+ spin_unlock_irqrestore(&ec->lock, flags);
+ return ret;
+}
+
+static inline void ec_transaction_transition(struct acpi_ec *ec, unsigned long flag)
+{
+ ec->curr->flags |= flag;
+
+ if (ec->curr->command != ACPI_EC_COMMAND_QUERY)
+ return;
+
+ switch (ec_event_clearing) {
+ case ACPI_EC_EVT_TIMING_STATUS:
+ if (flag == ACPI_EC_COMMAND_POLL)
+ acpi_ec_close_event(ec);
+
+ return;
+
+ case ACPI_EC_EVT_TIMING_QUERY:
+ if (flag == ACPI_EC_COMMAND_COMPLETE)
+ acpi_ec_close_event(ec);
+
+ return;
+
+ case ACPI_EC_EVT_TIMING_EVENT:
+ if (flag == ACPI_EC_COMMAND_COMPLETE)
+ acpi_ec_complete_event(ec);
+ }
+}
+
+static void acpi_ec_spurious_interrupt(struct acpi_ec *ec, struct transaction *t)
+{
+ if (t->irq_count < ec_storm_threshold)
+ ++t->irq_count;
+
+ /* Trigger if the threshold is 0 too. */
+ if (t->irq_count == ec_storm_threshold)
+ acpi_ec_mask_events(ec);
+}
+
+static void advance_transaction(struct acpi_ec *ec, bool interrupt)
+{
+ struct transaction *t = ec->curr;
+ bool wakeup = false;
+ u8 status;
+
+ 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);
+
+ /*
+ * Another IRQ or a guarded polling mode advancement is detected,
+ * the next QR_EC submission is then allowed.
+ */
+ if (!t || !(t->flags & ACPI_EC_COMMAND_POLL)) {
+ if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT &&
+ ec->event_state == EC_EVENT_COMPLETE)
+ acpi_ec_close_event(ec);
+
+ if (!t)
+ goto out;
+ }
+
+ if (t->flags & ACPI_EC_COMMAND_POLL) {
+ if (t->wlen > t->wi) {
+ if (!(status & ACPI_EC_FLAG_IBF))
+ acpi_ec_write_data(ec, t->wdata[t->wi++]);
+ else if (interrupt && !(status & ACPI_EC_FLAG_SCI))
+ acpi_ec_spurious_interrupt(ec, t);
+ } else if (t->rlen > t->ri) {
+ if (status & ACPI_EC_FLAG_OBF) {
+ t->rdata[t->ri++] = acpi_ec_read_data(ec);
+ if (t->rlen == t->ri) {
+ ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE);
+ wakeup = true;
+ if (t->command == ACPI_EC_COMMAND_QUERY)
+ ec_dbg_evt("Command(%s) completed by hardware",
+ acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
+ }
+ } else if (interrupt && !(status & ACPI_EC_FLAG_SCI)) {
+ acpi_ec_spurious_interrupt(ec, t);
+ }
+ } else if (t->wlen == t->wi && !(status & ACPI_EC_FLAG_IBF)) {
+ ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE);
+ wakeup = true;
+ }
+ } else if (!(status & ACPI_EC_FLAG_IBF)) {
+ acpi_ec_write_cmd(ec, t->command);
+ ec_transaction_transition(ec, ACPI_EC_COMMAND_POLL);
+ }
+
+out:
+ if (status & ACPI_EC_FLAG_SCI)
+ acpi_ec_submit_event(ec);
+
+ if (wakeup && interrupt)
+ wake_up(&ec->wait);
+}
+
+static void start_transaction(struct acpi_ec *ec)
+{
+ ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0;
+ ec->curr->flags = 0;
+}
+
+static int ec_guard(struct acpi_ec *ec)
+{
+ unsigned long guard = usecs_to_jiffies(ec->polling_guard);
+ unsigned long timeout = ec->timestamp + guard;
+
+ /* Ensure guarding period before polling EC status */
+ do {
+ if (ec->busy_polling) {
+ /* Perform busy polling */
+ if (ec_transaction_completed(ec))
+ return 0;
+ udelay(jiffies_to_usecs(guard));
+ } else {
+ /*
+ * Perform wait polling
+ * 1. Wait the transaction to be completed by the
+ * GPE handler after the transaction enters
+ * ACPI_EC_COMMAND_POLL state.
+ * 2. A special guarding logic is also required
+ * for event clearing mode "event" before the
+ * transaction enters ACPI_EC_COMMAND_POLL
+ * state.
+ */
+ if (!ec_transaction_polled(ec) &&
+ !acpi_ec_guard_event(ec))
+ break;
+ if (wait_event_timeout(ec->wait,
+ ec_transaction_completed(ec),
+ guard))
+ return 0;
+ }
+ } while (time_before(jiffies, timeout));
+ return -ETIME;
+}
+
+static int ec_poll(struct acpi_ec *ec)
+{
+ unsigned long flags;
+ int repeat = 5; /* number of command restarts */
+
+ while (repeat--) {
+ unsigned long delay = jiffies +
+ msecs_to_jiffies(ec_delay);
+ do {
+ if (!ec_guard(ec))
+ return 0;
+ spin_lock_irqsave(&ec->lock, flags);
+ advance_transaction(ec, false);
+ spin_unlock_irqrestore(&ec->lock, flags);
+ } while (time_before(jiffies, delay));
+ pr_debug("controller reset, restart transaction\n");
+ spin_lock_irqsave(&ec->lock, flags);
+ start_transaction(ec);
+ spin_unlock_irqrestore(&ec->lock, flags);
+ }
+ return -ETIME;
+}
+
+static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
+ struct transaction *t)
+{
+ unsigned long tmp;
+ int ret = 0;
+
+ /* start transaction */
+ spin_lock_irqsave(&ec->lock, tmp);
+ /* Enable GPE for command processing (IBF=0/OBF=1) */
+ if (!acpi_ec_submit_flushable_request(ec)) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ ec_dbg_ref(ec, "Increase command");
+ /* following two actions should be kept atomic */
+ ec->curr = t;
+ ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command));
+ start_transaction(ec);
+ spin_unlock_irqrestore(&ec->lock, tmp);
+
+ ret = ec_poll(ec);
+
+ spin_lock_irqsave(&ec->lock, tmp);
+ if (t->irq_count == ec_storm_threshold)
+ acpi_ec_unmask_events(ec);
+ ec_dbg_req("Command(%s) stopped", acpi_ec_cmd_string(t->command));
+ ec->curr = NULL;
+ /* Disable GPE for command processing (IBF=0/OBF=1) */
+ acpi_ec_complete_request(ec);
+ ec_dbg_ref(ec, "Decrease command");
+unlock:
+ spin_unlock_irqrestore(&ec->lock, tmp);
+ return ret;
+}
+
+static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
+{
+ int status;
+ u32 glk;
+
+ 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) {
+ status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
+ if (ACPI_FAILURE(status)) {
+ status = -ENODEV;
+ goto unlock;
+ }
+ }
+
+ status = acpi_ec_transaction_unlocked(ec, t);
+
+ if (ec->global_lock)
+ acpi_release_global_lock(glk);
+unlock:
+ mutex_unlock(&ec->mutex);
+ return status;
+}
+
+static int acpi_ec_burst_enable(struct acpi_ec *ec)
+{
+ u8 d;
+ struct transaction t = {.command = ACPI_EC_BURST_ENABLE,
+ .wdata = NULL, .rdata = &d,
+ .wlen = 0, .rlen = 1};
+
+ return acpi_ec_transaction(ec, &t);
+}
+
+static int acpi_ec_burst_disable(struct acpi_ec *ec)
+{
+ struct transaction t = {.command = ACPI_EC_BURST_DISABLE,
+ .wdata = NULL, .rdata = NULL,
+ .wlen = 0, .rlen = 0};
+
+ return (acpi_ec_read_status(ec) & ACPI_EC_FLAG_BURST) ?
+ acpi_ec_transaction(ec, &t) : 0;
+}
+
+static int acpi_ec_read(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(ec, &t);
+ *data = d;
+ return result;
+}
+
+static int acpi_ec_write(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(ec, &t);
+}
+
+int ec_read(u8 addr, u8 *val)
+{
+ int err;
+ u8 temp_data;
+
+ if (!first_ec)
+ return -ENODEV;
+
+ err = acpi_ec_read(first_ec, addr, &temp_data);
+
+ if (!err) {
+ *val = temp_data;
+ return 0;
+ }
+ return err;
+}
+EXPORT_SYMBOL(ec_read);
+
+int ec_write(u8 addr, u8 val)
+{
+ if (!first_ec)
+ return -ENODEV;
+
+ return acpi_ec_write(first_ec, addr, val);
+}
+EXPORT_SYMBOL(ec_write);
+
+int ec_transaction(u8 command,
+ const u8 *wdata, unsigned wdata_len,
+ u8 *rdata, unsigned rdata_len)
+{
+ struct transaction t = {.command = command,
+ .wdata = wdata, .rdata = rdata,
+ .wlen = wdata_len, .rlen = rdata_len};
+
+ if (!first_ec)
+ return -ENODEV;
+
+ return acpi_ec_transaction(first_ec, &t);
+}
+EXPORT_SYMBOL(ec_transaction);
+
+/* Get the handle to the EC device */
+acpi_handle ec_get_handle(void)
+{
+ if (!first_ec)
+ return NULL;
+ return first_ec->handle;
+}
+EXPORT_SYMBOL(ec_get_handle);
+
+static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) {
+ ec_dbg_drv("Starting EC");
+ /* Enable GPE for event processing (SCI_EVT=1) */
+ if (!resuming) {
+ acpi_ec_submit_request(ec);
+ ec_dbg_ref(ec, "Increase driver");
+ }
+ ec_log_drv("EC started");
+ }
+ spin_unlock_irqrestore(&ec->lock, flags);
+}
+
+static bool acpi_ec_stopped(struct acpi_ec *ec)
+{
+ unsigned long flags;
+ bool flushed;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ flushed = acpi_ec_flushed(ec);
+ spin_unlock_irqrestore(&ec->lock, flags);
+ return flushed;
+}
+
+static void acpi_ec_stop(struct acpi_ec *ec, bool suspending)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ if (acpi_ec_started(ec)) {
+ ec_dbg_drv("Stopping EC");
+ set_bit(EC_FLAGS_STOPPED, &ec->flags);
+ spin_unlock_irqrestore(&ec->lock, flags);
+ wait_event(ec->wait, acpi_ec_stopped(ec));
+ spin_lock_irqsave(&ec->lock, flags);
+ /* Disable GPE for event processing (SCI_EVT=1) */
+ if (!suspending) {
+ acpi_ec_complete_request(ec);
+ ec_dbg_ref(ec, "Decrease driver");
+ } else if (!ec_freeze_events)
+ __acpi_ec_disable_event(ec);
+ clear_bit(EC_FLAGS_STARTED, &ec->flags);
+ clear_bit(EC_FLAGS_STOPPED, &ec->flags);
+ ec_log_drv("EC stopped");
+ }
+ spin_unlock_irqrestore(&ec->lock, flags);
+}
+
+static void acpi_ec_enter_noirq(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ ec->busy_polling = true;
+ ec->polling_guard = 0;
+ ec_log_drv("interrupt blocked");
+ spin_unlock_irqrestore(&ec->lock, flags);
+}
+
+static void acpi_ec_leave_noirq(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ ec->busy_polling = ec_busy_polling;
+ ec->polling_guard = ec_polling_guard;
+ ec_log_drv("interrupt unblocked");
+ spin_unlock_irqrestore(&ec->lock, flags);
+}
+
+void acpi_ec_block_transactions(void)
+{
+ struct acpi_ec *ec = first_ec;
+
+ if (!ec)
+ return;
+
+ mutex_lock(&ec->mutex);
+ /* Prevent transactions from being carried out */
+ acpi_ec_stop(ec, true);
+ mutex_unlock(&ec->mutex);
+}
+
+void acpi_ec_unblock_transactions(void)
+{
+ /*
+ * Allow transactions to happen again (this function is called from
+ * atomic context during wakeup, so we don't need to acquire the mutex).
+ */
+ if (first_ec)
+ acpi_ec_start(first_ec, true);
+}
+
+/* --------------------------------------------------------------------------
+ Event Management
+ -------------------------------------------------------------------------- */
+static struct acpi_ec_query_handler *
+acpi_ec_get_query_handler_by_value(struct acpi_ec *ec, u8 value)
+{
+ struct acpi_ec_query_handler *handler;
+
+ mutex_lock(&ec->mutex);
+ list_for_each_entry(handler, &ec->list, node) {
+ if (value == handler->query_bit) {
+ kref_get(&handler->kref);
+ mutex_unlock(&ec->mutex);
+ return handler;
+ }
+ }
+ mutex_unlock(&ec->mutex);
+ return NULL;
+}
+
+static void acpi_ec_query_handler_release(struct kref *kref)
+{
+ struct acpi_ec_query_handler *handler =
+ container_of(kref, struct acpi_ec_query_handler, kref);
+
+ kfree(handler);
+}
+
+static void acpi_ec_put_query_handler(struct acpi_ec_query_handler *handler)
+{
+ kref_put(&handler->kref, acpi_ec_query_handler_release);
+}
+
+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);
+
+ if (!handler)
+ return -ENOMEM;
+
+ handler->query_bit = query_bit;
+ handler->handle = handle;
+ handler->func = func;
+ handler->data = data;
+ mutex_lock(&ec->mutex);
+ kref_init(&handler->kref);
+ list_add(&handler->node, &ec->list);
+ mutex_unlock(&ec->mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler);
+
+static void acpi_ec_remove_query_handlers(struct acpi_ec *ec,
+ bool remove_all, u8 query_bit)
+{
+ struct acpi_ec_query_handler *handler, *tmp;
+ LIST_HEAD(free_list);
+
+ mutex_lock(&ec->mutex);
+ list_for_each_entry_safe(handler, tmp, &ec->list, node) {
+ if (remove_all || query_bit == handler->query_bit) {
+ list_del_init(&handler->node);
+ list_add(&handler->node, &free_list);
+ }
+ }
+ mutex_unlock(&ec->mutex);
+ list_for_each_entry_safe(handler, tmp, &free_list, node)
+ acpi_ec_put_query_handler(handler);
+}
+
+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);
+
+static void acpi_ec_event_processor(struct work_struct *work)
+{
+ struct acpi_ec_query *q = container_of(work, struct acpi_ec_query, work);
+ struct acpi_ec_query_handler *handler = q->handler;
+ struct acpi_ec *ec = q->ec;
+
+ ec_dbg_evt("Query(0x%02x) started", handler->query_bit);
+
+ if (handler->func)
+ handler->func(handler->data);
+ else if (handler->handle)
+ acpi_evaluate_object(handler->handle, NULL, NULL, NULL);
+
+ ec_dbg_evt("Query(0x%02x) stopped", handler->query_bit);
+
+ spin_lock_irq(&ec->lock);
+ ec->queries_in_progress--;
+ spin_unlock_irq(&ec->lock);
+
+ acpi_ec_put_query_handler(handler);
+ kfree(q);
+}
+
+static struct acpi_ec_query *acpi_ec_create_query(struct acpi_ec *ec, u8 *pval)
+{
+ struct acpi_ec_query *q;
+ struct transaction *t;
+
+ q = kzalloc(sizeof (struct acpi_ec_query), GFP_KERNEL);
+ if (!q)
+ return NULL;
+
+ INIT_WORK(&q->work, acpi_ec_event_processor);
+ t = &q->transaction;
+ t->command = ACPI_EC_COMMAND_QUERY;
+ t->rdata = pval;
+ t->rlen = 1;
+ q->ec = ec;
+ return q;
+}
+
+static int acpi_ec_submit_query(struct acpi_ec *ec)
+{
+ struct acpi_ec_query *q;
+ u8 value = 0;
+ int result;
+
+ q = acpi_ec_create_query(ec, &value);
+ if (!q)
+ return -ENOMEM;
+
+ /*
+ * Query the EC to find out which _Qxx method we need to evaluate.
+ * Note that successful completion of the query causes the ACPI_EC_SCI
+ * bit to be cleared (and thus clearing the interrupt source).
+ */
+ result = acpi_ec_transaction(ec, &q->transaction);
+ if (result)
+ goto err_exit;
+
+ if (!value) {
+ result = -ENODATA;
+ goto err_exit;
+ }
+
+ q->handler = acpi_ec_get_query_handler_by_value(ec, value);
+ if (!q->handler) {
+ result = -ENODATA;
+ goto err_exit;
+ }
+
+ /*
+ * It is reported that _Qxx are evaluated in a parallel way on Windows:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=94411
+ *
+ * Put this log entry before queue_work() to make it appear in the log
+ * before any other messages emitted during workqueue handling.
+ */
+ ec_dbg_evt("Query(0x%02x) scheduled", value);
+
+ spin_lock_irq(&ec->lock);
+
+ ec->queries_in_progress++;
+ queue_work(ec_query_wq, &q->work);
+
+ spin_unlock_irq(&ec->lock);
+
+ return 0;
+
+err_exit:
+ kfree(q);
+
+ return result;
+}
+
+static void acpi_ec_event_handler(struct work_struct *work)
+{
+ struct acpi_ec *ec = container_of(work, struct acpi_ec, work);
+
+ ec_dbg_evt("Event started");
+
+ spin_lock_irq(&ec->lock);
+
+ while (ec->events_to_process) {
+ spin_unlock_irq(&ec->lock);
+
+ acpi_ec_submit_query(ec);
+
+ spin_lock_irq(&ec->lock);
+
+ ec->events_to_process--;
+ }
+
+ /*
+ * Before exit, make sure that the it will be possible to queue up the
+ * event handling work again regardless of whether or not the query
+ * queued up above is processed successfully.
+ */
+ if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) {
+ bool guard_timeout;
+
+ acpi_ec_complete_event(ec);
+
+ ec_dbg_evt("Event stopped");
+
+ spin_unlock_irq(&ec->lock);
+
+ guard_timeout = !!ec_guard(ec);
+
+ spin_lock_irq(&ec->lock);
+
+ /* Take care of SCI_EVT unless someone else is doing that. */
+ if (guard_timeout && !ec->curr)
+ advance_transaction(ec, false);
+ } else {
+ acpi_ec_close_event(ec);
+
+ ec_dbg_evt("Event stopped");
+ }
+
+ ec->events_in_progress--;
+
+ spin_unlock_irq(&ec->lock);
+}
+
+static void acpi_ec_handle_interrupt(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ advance_transaction(ec, true);
+ spin_unlock_irqrestore(&ec->lock, flags);
+}
+
+static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
+ u32 gpe_number, void *data)
+{
+ acpi_ec_handle_interrupt(data);
+ return ACPI_INTERRUPT_HANDLED;
+}
+
+static irqreturn_t acpi_ec_irq_handler(int irq, void *data)
+{
+ acpi_ec_handle_interrupt(data);
+ return IRQ_HANDLED;
+}
+
+/* --------------------------------------------------------------------------
+ * Address Space Management
+ * -------------------------------------------------------------------------- */
+
+static acpi_status
+acpi_ec_space_handler(u32 function, acpi_physical_address address,
+ u32 bits, u64 *value64,
+ void *handler_context, void *region_context)
+{
+ struct acpi_ec *ec = handler_context;
+ int result = 0, i, bytes = bits / 8;
+ u8 *value = (u8 *)value64;
+
+ if ((address > 0xFF) || !value || !handler_context)
+ return AE_BAD_PARAMETER;
+
+ if (function != ACPI_READ && function != ACPI_WRITE)
+ return AE_BAD_PARAMETER;
+
+ if (ec->busy_polling || bits > 8)
+ acpi_ec_burst_enable(ec);
+
+ for (i = 0; i < bytes; ++i, ++address, ++value)
+ result = (function == ACPI_READ) ?
+ acpi_ec_read(ec, address, value) :
+ acpi_ec_write(ec, address, *value);
+
+ if (ec->busy_polling || bits > 8)
+ acpi_ec_burst_disable(ec);
+
+ switch (result) {
+ case -EINVAL:
+ return AE_BAD_PARAMETER;
+ case -ENODEV:
+ return AE_NOT_FOUND;
+ case -ETIME:
+ return AE_TIME;
+ default:
+ return AE_OK;
+ }
+}
+
+/* --------------------------------------------------------------------------
+ * Driver Interface
+ * -------------------------------------------------------------------------- */
+
+static acpi_status
+ec_parse_io_ports(struct acpi_resource *resource, void *context);
+
+static void acpi_ec_free(struct acpi_ec *ec)
+{
+ if (first_ec == ec)
+ first_ec = NULL;
+ if (boot_ec == ec)
+ boot_ec = NULL;
+ kfree(ec);
+}
+
+static struct acpi_ec *acpi_ec_alloc(void)
+{
+ struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
+
+ if (!ec)
+ return NULL;
+ mutex_init(&ec->mutex);
+ init_waitqueue_head(&ec->wait);
+ INIT_LIST_HEAD(&ec->list);
+ spin_lock_init(&ec->lock);
+ INIT_WORK(&ec->work, acpi_ec_event_handler);
+ ec->timestamp = jiffies;
+ ec->busy_polling = true;
+ ec->polling_guard = 0;
+ ec->gpe = -1;
+ ec->irq = -1;
+ return ec;
+}
+
+static acpi_status
+acpi_ec_register_query_methods(acpi_handle handle, u32 level,
+ void *context, void **return_value)
+{
+ char node_name[5];
+ struct acpi_buffer buffer = { sizeof(node_name), node_name };
+ struct acpi_ec *ec = context;
+ int value = 0;
+ acpi_status status;
+
+ status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
+
+ if (ACPI_SUCCESS(status) && sscanf(node_name, "_Q%x", &value) == 1)
+ acpi_ec_add_query_handler(ec, value, handle, NULL, NULL);
+ return AE_OK;
+}
+
+static acpi_status
+ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
+{
+ acpi_status status;
+ unsigned long long tmp = 0;
+ struct acpi_ec *ec = context;
+
+ /* clear addr values, ec_parse_io_ports depend on it */
+ ec->command_addr = ec->data_addr = 0;
+
+ status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+ ec_parse_io_ports, ec);
+ if (ACPI_FAILURE(status))
+ return status;
+ if (ec->data_addr == 0 || ec->command_addr == 0)
+ return AE_OK;
+
+ /* Get GPE bit assignment (EC events). */
+ /* TODO: Add support for _GPE returning a package */
+ status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
+ if (ACPI_SUCCESS(status))
+ ec->gpe = tmp;
+ /*
+ * Errors are non-fatal, allowing for ACPI Reduced Hardware
+ * platforms which use GpioInt instead of GPE.
+ */
+
+ /* Use the global lock for all EC transactions? */
+ tmp = 0;
+ acpi_evaluate_integer(handle, "_GLK", NULL, &tmp);
+ ec->global_lock = tmp;
+ ec->handle = handle;
+ return AE_CTRL_TERMINATE;
+}
+
+static bool install_gpe_event_handler(struct acpi_ec *ec)
+{
+ acpi_status status;
+
+ status = acpi_install_gpe_raw_handler(NULL, ec->gpe,
+ ACPI_GPE_EDGE_TRIGGERED,
+ &acpi_ec_gpe_handler, ec);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ if (test_bit(EC_FLAGS_STARTED, &ec->flags) && ec->reference_count >= 1)
+ acpi_ec_enable_gpe(ec, true);
+
+ return true;
+}
+
+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;
+}
+
+/**
+ * ec_install_handlers - Install service callbacks and register query methods.
+ * @ec: Target EC.
+ * @device: ACPI device object corresponding to @ec.
+ *
+ * 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
+ * namespace and register them, and install an event (either GPE or GPIO IRQ)
+ * handler for the EC, if possible.
+ *
+ * Return:
+ * -ENODEV if the address space handler cannot be installed, which means
+ * "unable to handle transactions",
+ * -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)
+{
+ acpi_status status;
+
+ acpi_ec_start(ec, false);
+
+ if (!test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) {
+ acpi_ec_enter_noirq(ec);
+ status = acpi_install_address_space_handler(ec->handle,
+ ACPI_ADR_SPACE_EC,
+ &acpi_ec_space_handler,
+ NULL, ec);
+ if (ACPI_FAILURE(status)) {
+ acpi_ec_stop(ec, false);
+ return -ENODEV;
+ }
+ set_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
+ }
+
+ if (!device)
+ return 0;
+
+ if (ec->gpe < 0) {
+ /* ACPI reduced hardware platforms use a GpioInt from _CRS. */
+ int irq = acpi_dev_gpio_irq_get(device, 0);
+ /*
+ * Bail out right away for deferred probing or complete the
+ * initialization regardless of any other errors.
+ */
+ if (irq == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ else if (irq >= 0)
+ ec->irq = irq;
+ }
+
+ if (!test_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags)) {
+ /* Find and register all query methods */
+ acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
+ acpi_ec_register_query_methods,
+ NULL, ec, NULL);
+ set_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags);
+ }
+ if (!test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags)) {
+ bool ready = false;
+
+ if (ec->gpe >= 0)
+ ready = install_gpe_event_handler(ec);
+ else if (ec->irq >= 0)
+ ready = install_gpio_irq_event_handler(ec);
+
+ if (ready) {
+ set_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags);
+ acpi_ec_leave_noirq(ec);
+ }
+ /*
+ * Failures to install an event handler are not fatal, because
+ * the EC can be polled for events.
+ */
+ }
+ /* EC is fully operational, allow queries */
+ acpi_ec_enable_event(ec);
+
+ return 0;
+}
+
+static void ec_remove_handlers(struct acpi_ec *ec)
+{
+ 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)))
+ pr_err("failed to remove space handler\n");
+ clear_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
+ }
+
+ /*
+ * Stops handling the EC transactions after removing the operation
+ * region handler. This is required because _REG(DISCONNECT)
+ * invoked during the removal can result in new EC transactions.
+ *
+ * Flushes the EC requests and thus disables the GPE before
+ * removing the GPE handler. This is required by the current ACPICA
+ * GPE core. ACPICA GPE core will automatically disable a GPE when
+ * it is indicated but there is no way to handle it. So the drivers
+ * must disable the GPEs prior to removing the GPE handlers.
+ */
+ acpi_ec_stop(ec, false);
+
+ if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags)) {
+ if (ec->gpe >= 0 &&
+ ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe,
+ &acpi_ec_gpe_handler)))
+ pr_err("failed to remove gpe handler\n");
+
+ if (ec->irq >= 0)
+ free_irq(ec->irq, ec);
+
+ clear_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags);
+ }
+ if (test_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags)) {
+ acpi_ec_remove_query_handlers(ec, true, 0);
+ clear_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags);
+ }
+}
+
+static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device)
+{
+ int ret;
+
+ ret = ec_install_handlers(ec, device);
+ if (ret)
+ return ret;
+
+ /* First EC capable of handling transactions */
+ if (!first_ec)
+ first_ec = ec;
+
+ pr_info("EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n", ec->command_addr,
+ ec->data_addr);
+
+ if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags)) {
+ if (ec->gpe >= 0)
+ pr_info("GPE=0x%x\n", ec->gpe);
+ else
+ pr_info("IRQ=%d\n", ec->irq);
+ }
+
+ return ret;
+}
+
+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);
+
+ if (boot_ec && (boot_ec->handle == device->handle ||
+ !strcmp(acpi_device_hid(device), ACPI_ECDT_HID))) {
+ /* Fast path: this device corresponds to the boot EC. */
+ ec = boot_ec;
+ } else {
+ acpi_status status;
+
+ ec = acpi_ec_alloc();
+ if (!ec)
+ return -ENOMEM;
+
+ status = ec_parse_device(device->handle, 0, ec, NULL);
+ if (status != AE_CTRL_TERMINATE) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (boot_ec && ec->command_addr == boot_ec->command_addr &&
+ ec->data_addr == boot_ec->data_addr) {
+ /*
+ * Trust PNP0C09 namespace location rather than ECDT ID.
+ * But trust ECDT GPE rather than _GPE because of ASUS
+ * quirks. So do not change boot_ec->gpe to ec->gpe,
+ * except when the TRUST_DSDT_GPE quirk is set.
+ */
+ boot_ec->handle = ec->handle;
+
+ if (EC_FLAGS_TRUST_DSDT_GPE)
+ boot_ec->gpe = ec->gpe;
+
+ acpi_handle_debug(ec->handle, "duplicated.\n");
+ acpi_ec_free(ec);
+ ec = boot_ec;
+ }
+ }
+
+ ret = acpi_ec_setup(ec, device);
+ if (ret)
+ goto err;
+
+ if (ec == boot_ec)
+ acpi_handle_info(boot_ec->handle,
+ "Boot %s EC initialization complete\n",
+ boot_ec_is_ecdt ? "ECDT" : "DSDT");
+
+ acpi_handle_info(ec->handle,
+ "EC: Used to handle transactions and events\n");
+
+ device->driver_data = ec;
+
+ ret = !!request_region(ec->data_addr, 1, "EC data");
+ WARN(!ret, "Could not request EC data io port 0x%lx", ec->data_addr);
+ ret = !!request_region(ec->command_addr, 1, "EC cmd");
+ WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr);
+
+ /* Reprobe devices depending on the EC */
+ acpi_dev_clear_dependencies(device);
+
+ acpi_handle_debug(ec->handle, "enumerated.\n");
+ return 0;
+
+err:
+ if (ec != boot_ec)
+ acpi_ec_free(ec);
+
+ return ret;
+}
+
+static int acpi_ec_remove(struct acpi_device *device)
+{
+ struct acpi_ec *ec;
+
+ if (!device)
+ return -EINVAL;
+
+ ec = acpi_driver_data(device);
+ release_region(ec->data_addr, 1);
+ release_region(ec->command_addr, 1);
+ device->driver_data = NULL;
+ if (ec != boot_ec) {
+ ec_remove_handlers(ec);
+ acpi_ec_free(ec);
+ }
+ return 0;
+}
+
+static acpi_status
+ec_parse_io_ports(struct acpi_resource *resource, void *context)
+{
+ struct acpi_ec *ec = context;
+
+ if (resource->type != ACPI_RESOURCE_TYPE_IO)
+ return AE_OK;
+
+ /*
+ * The first address region returned is the data port, and
+ * the second address region returned is the status/command
+ * port.
+ */
+ if (ec->data_addr == 0)
+ ec->data_addr = resource->data.io.minimum;
+ else if (ec->command_addr == 0)
+ ec->command_addr = resource->data.io.minimum;
+ else
+ return AE_CTRL_TERMINATE;
+
+ return AE_OK;
+}
+
+static const struct acpi_device_id ec_device_ids[] = {
+ {"PNP0C09", 0},
+ {ACPI_ECDT_HID, 0},
+ {"", 0},
+};
+
+/*
+ * This function is not Windows-compatible as Windows never enumerates the
+ * namespace EC before the main ACPI device enumeration process. It is
+ * retained for historical reason and will be deprecated in the future.
+ */
+void __init acpi_ec_dsdt_probe(void)
+{
+ struct acpi_ec *ec;
+ acpi_status status;
+ int ret;
+
+ /*
+ * If a platform has ECDT, there is no need to proceed as the
+ * following probe is not a part of the ACPI device enumeration,
+ * executing _STA is not safe, and thus this probe may risk of
+ * picking up an invalid EC device.
+ */
+ if (boot_ec)
+ return;
+
+ ec = acpi_ec_alloc();
+ if (!ec)
+ return;
+
+ /*
+ * At this point, the namespace is initialized, so start to find
+ * the namespace objects.
+ */
+ status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device, ec, NULL);
+ if (ACPI_FAILURE(status) || !ec->handle) {
+ acpi_ec_free(ec);
+ return;
+ }
+
+ /*
+ * When the DSDT EC is available, always re-configure boot EC to
+ * have _REG evaluated. _REG can only be evaluated after the
+ * namespace initialization.
+ * At this point, the GPE is not fully initialized, so do not to
+ * handle the events.
+ */
+ ret = acpi_ec_setup(ec, NULL);
+ if (ret) {
+ acpi_ec_free(ec);
+ return;
+ }
+
+ boot_ec = ec;
+
+ acpi_handle_info(ec->handle,
+ "Boot DSDT EC used to handle transactions\n");
+}
+
+/*
+ * acpi_ec_ecdt_start - Finalize the boot ECDT EC initialization.
+ *
+ * First, look for an ACPI handle for the boot ECDT EC if acpi_ec_add() has not
+ * found a matching object in the namespace.
+ *
+ * Next, in case the DSDT EC is not functioning, it is still necessary to
+ * provide a functional ECDT EC to handle events, so add an extra device object
+ * to represent it (see https://bugzilla.kernel.org/show_bug.cgi?id=115021).
+ *
+ * This is useful on platforms with valid ECDT and invalid DSDT EC settings,
+ * like ASUS X550ZE (see https://bugzilla.kernel.org/show_bug.cgi?id=196847).
+ */
+static void __init acpi_ec_ecdt_start(void)
+{
+ struct acpi_table_ecdt *ecdt_ptr;
+ acpi_handle handle;
+ acpi_status status;
+
+ /* Bail out if a matching EC has been found in the namespace. */
+ if (!boot_ec || boot_ec->handle != ACPI_ROOT_OBJECT)
+ return;
+
+ /* Look up the object pointed to from the ECDT in the namespace. */
+ status = acpi_get_table(ACPI_SIG_ECDT, 1,
+ (struct acpi_table_header **)&ecdt_ptr);
+ if (ACPI_FAILURE(status))
+ return;
+
+ status = acpi_get_handle(NULL, ecdt_ptr->id, &handle);
+ if (ACPI_SUCCESS(status)) {
+ boot_ec->handle = handle;
+
+ /* Add a special ACPI device object to represent the boot EC. */
+ acpi_bus_register_early_device(ACPI_BUS_TYPE_ECDT_EC);
+ }
+
+ acpi_put_table((struct acpi_table_header *)ecdt_ptr);
+}
+
+/*
+ * On some hardware it is necessary to clear events accumulated by the EC during
+ * sleep. These ECs stop reporting GPEs until they are manually polled, if too
+ * many events are accumulated. (e.g. Samsung Series 5/9 notebooks)
+ *
+ * https://bugzilla.kernel.org/show_bug.cgi?id=44161
+ *
+ * Ideally, the EC should also be instructed NOT to accumulate events during
+ * sleep (which Windows seems to do somehow), but the interface to control this
+ * behaviour is not known at this time.
+ *
+ * Models known to be affected are Samsung 530Uxx/535Uxx/540Uxx/550Pxx/900Xxx,
+ * however it is very likely that other Samsung models are affected.
+ *
+ * On systems which don't accumulate _Q events during sleep, this extra check
+ * should be harmless.
+ */
+static int ec_clear_on_resume(const struct dmi_system_id *id)
+{
+ pr_debug("Detected system needing EC poll on resume.\n");
+ EC_FLAGS_CLEAR_ON_RESUME = 1;
+ ec_event_clearing = ACPI_EC_EVT_TIMING_STATUS;
+ return 0;
+}
+
+/*
+ * Some ECDTs contain wrong register addresses.
+ * MSI MS-171F
+ * https://bugzilla.kernel.org/show_bug.cgi?id=12461
+ */
+static int ec_correct_ecdt(const struct dmi_system_id *id)
+{
+ pr_debug("Detected system needing ECDT address correction.\n");
+ EC_FLAGS_CORRECT_ECDT = 1;
+ return 0;
+}
+
+/*
+ * Some ECDTs contain wrong GPE setting, but they share the same port addresses
+ * with DSDT EC, don't duplicate the DSDT EC with ECDT EC in this case.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=209989
+ */
+static int ec_honor_dsdt_gpe(const struct dmi_system_id *id)
+{
+ pr_debug("Detected system needing DSDT GPE setting.\n");
+ EC_FLAGS_TRUST_DSDT_GPE = 1;
+ return 0;
+}
+
+static const struct dmi_system_id ec_dmi_table[] __initconst = {
+ {
+ /*
+ * MSI MS-171F
+ * https://bugzilla.kernel.org/show_bug.cgi?id=12461
+ */
+ .callback = ec_correct_ecdt,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),
+ },
+ },
+ {
+ /*
+ * HP Pavilion Gaming Laptop 15-cx0xxx
+ * https://bugzilla.kernel.org/show_bug.cgi?id=209989
+ */
+ .callback = ec_honor_dsdt_gpe,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Gaming Laptop 15-cx0xxx"),
+ },
+ },
+ {
+ /*
+ * 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
+ */
+ .callback = ec_clear_on_resume,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ },
+ },
+ {}
+};
+
+void __init acpi_ec_ecdt_probe(void)
+{
+ struct acpi_table_ecdt *ecdt_ptr;
+ struct acpi_ec *ec;
+ acpi_status status;
+ int ret;
+
+ /* Generate a boot ec context. */
+ dmi_check_system(ec_dmi_table);
+ status = acpi_get_table(ACPI_SIG_ECDT, 1,
+ (struct acpi_table_header **)&ecdt_ptr);
+ if (ACPI_FAILURE(status))
+ return;
+
+ if (!ecdt_ptr->control.address || !ecdt_ptr->data.address) {
+ /*
+ * Asus X50GL:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=11880
+ */
+ goto out;
+ }
+
+ ec = acpi_ec_alloc();
+ if (!ec)
+ goto out;
+
+ if (EC_FLAGS_CORRECT_ECDT) {
+ ec->command_addr = ecdt_ptr->data.address;
+ ec->data_addr = ecdt_ptr->control.address;
+ } else {
+ ec->command_addr = ecdt_ptr->control.address;
+ ec->data_addr = ecdt_ptr->data.address;
+ }
+
+ /*
+ * Ignore the GPE value on Reduced Hardware platforms.
+ * Some products have this set to an erroneous value.
+ */
+ if (!acpi_gbl_reduced_hardware)
+ ec->gpe = ecdt_ptr->gpe;
+
+ ec->handle = ACPI_ROOT_OBJECT;
+
+ /*
+ * 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);
+ if (ret) {
+ acpi_ec_free(ec);
+ goto out;
+ }
+
+ boot_ec = ec;
+ boot_ec_is_ecdt = true;
+
+ pr_info("Boot ECDT EC used to handle transactions\n");
+
+out:
+ acpi_put_table((struct acpi_table_header *)ecdt_ptr);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_ec_suspend(struct device *dev)
+{
+ struct acpi_ec *ec =
+ acpi_driver_data(to_acpi_device(dev));
+
+ if (!pm_suspend_no_platform() && ec_freeze_events)
+ acpi_ec_disable_event(ec);
+ return 0;
+}
+
+static int acpi_ec_suspend_noirq(struct device *dev)
+{
+ struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev));
+
+ /*
+ * The SCI handler doesn't run at this point, so the GPE can be
+ * masked at the low level without side effects.
+ */
+ if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) &&
+ ec->gpe >= 0 && ec->reference_count >= 1)
+ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE);
+
+ acpi_ec_enter_noirq(ec);
+
+ return 0;
+}
+
+static int acpi_ec_resume_noirq(struct device *dev)
+{
+ struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev));
+
+ acpi_ec_leave_noirq(ec);
+
+ if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) &&
+ ec->gpe >= 0 && ec->reference_count >= 1)
+ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE);
+
+ return 0;
+}
+
+static int acpi_ec_resume(struct device *dev)
+{
+ struct acpi_ec *ec =
+ acpi_driver_data(to_acpi_device(dev));
+
+ acpi_ec_enable_event(ec);
+ return 0;
+}
+
+void acpi_ec_mark_gpe_for_wake(void)
+{
+ if (first_ec && !ec_no_wakeup)
+ acpi_mark_gpe_for_wake(NULL, first_ec->gpe);
+}
+EXPORT_SYMBOL_GPL(acpi_ec_mark_gpe_for_wake);
+
+void acpi_ec_set_gpe_wake_mask(u8 action)
+{
+ if (pm_suspend_no_platform() && first_ec && !ec_no_wakeup)
+ acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action);
+}
+
+static bool acpi_ec_work_in_progress(struct acpi_ec *ec)
+{
+ return ec->events_in_progress + ec->queries_in_progress > 0;
+}
+
+bool acpi_ec_dispatch_gpe(void)
+{
+ bool work_in_progress = false;
+
+ if (!first_ec)
+ return acpi_any_gpe_status_set(U32_MAX);
+
+ /*
+ * Report wakeup if the status bit is set for any enabled GPE other
+ * than the EC one.
+ */
+ if (acpi_any_gpe_status_set(first_ec->gpe))
+ return true;
+
+ /*
+ * Cancel the SCI wakeup and process all pending events in case there
+ * are any wakeup ones in there.
+ *
+ * Note that if any non-EC GPEs are active at this point, the SCI will
+ * retrigger after the rearming in acpi_s2idle_wake(), so no events
+ * should be missed by canceling the wakeup here.
+ */
+ pm_system_cancel_wakeup();
+
+ /*
+ * Dispatch the EC GPE in-band, but do not report wakeup in any case
+ * to allow the caller to process events properly after that.
+ */
+ spin_lock_irq(&first_ec->lock);
+
+ if (acpi_ec_gpe_status_set(first_ec)) {
+ pm_pr_dbg("ACPI EC GPE status set\n");
+
+ advance_transaction(first_ec, false);
+ work_in_progress = acpi_ec_work_in_progress(first_ec);
+ }
+
+ spin_unlock_irq(&first_ec->lock);
+
+ if (!work_in_progress)
+ return false;
+
+ pm_pr_dbg("ACPI EC GPE dispatched\n");
+
+ /* Drain EC work. */
+ do {
+ acpi_ec_flush_work();
+
+ pm_pr_dbg("ACPI EC work flushed\n");
+
+ spin_lock_irq(&first_ec->lock);
+
+ work_in_progress = acpi_ec_work_in_progress(first_ec);
+
+ spin_unlock_irq(&first_ec->lock);
+ } while (work_in_progress && !pm_wakeup_pending());
+
+ return false;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops acpi_ec_pm = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq)
+ SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume)
+};
+
+static int param_set_event_clearing(const char *val,
+ const struct kernel_param *kp)
+{
+ int result = 0;
+
+ if (!strncmp(val, "status", sizeof("status") - 1)) {
+ ec_event_clearing = ACPI_EC_EVT_TIMING_STATUS;
+ pr_info("Assuming SCI_EVT clearing on EC_SC accesses\n");
+ } else if (!strncmp(val, "query", sizeof("query") - 1)) {
+ ec_event_clearing = ACPI_EC_EVT_TIMING_QUERY;
+ pr_info("Assuming SCI_EVT clearing on QR_EC writes\n");
+ } else if (!strncmp(val, "event", sizeof("event") - 1)) {
+ ec_event_clearing = ACPI_EC_EVT_TIMING_EVENT;
+ pr_info("Assuming SCI_EVT clearing on event reads\n");
+ } else
+ result = -EINVAL;
+ return result;
+}
+
+static int param_get_event_clearing(char *buffer,
+ const struct kernel_param *kp)
+{
+ switch (ec_event_clearing) {
+ case ACPI_EC_EVT_TIMING_STATUS:
+ return sprintf(buffer, "status\n");
+ case ACPI_EC_EVT_TIMING_QUERY:
+ return sprintf(buffer, "query\n");
+ case ACPI_EC_EVT_TIMING_EVENT:
+ return sprintf(buffer, "event\n");
+ default:
+ return sprintf(buffer, "invalid\n");
+ }
+ return 0;
+}
+
+module_param_call(ec_event_clearing, param_set_event_clearing, param_get_event_clearing,
+ NULL, 0644);
+MODULE_PARM_DESC(ec_event_clearing, "Assumed SCI_EVT clearing timing");
+
+static struct acpi_driver acpi_ec_driver = {
+ .name = "ec",
+ .class = ACPI_EC_CLASS,
+ .ids = ec_device_ids,
+ .ops = {
+ .add = acpi_ec_add,
+ .remove = acpi_ec_remove,
+ },
+ .drv.pm = &acpi_ec_pm,
+};
+
+static void acpi_ec_destroy_workqueues(void)
+{
+ if (ec_wq) {
+ destroy_workqueue(ec_wq);
+ ec_wq = NULL;
+ }
+ if (ec_query_wq) {
+ destroy_workqueue(ec_query_wq);
+ ec_query_wq = NULL;
+ }
+}
+
+static int acpi_ec_init_workqueues(void)
+{
+ if (!ec_wq)
+ ec_wq = alloc_ordered_workqueue("kec", 0);
+
+ if (!ec_query_wq)
+ ec_query_wq = alloc_workqueue("kec_query", 0, ec_max_queries);
+
+ if (!ec_wq || !ec_query_wq) {
+ acpi_ec_destroy_workqueues();
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static const struct dmi_system_id acpi_ec_no_wakeup[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Thinkpad X1 Carbon 6th"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X1 Yoga 3rd"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "103C_5336AN HP ZHAN 66 Pro"),
+ },
+ },
+ { },
+};
+
+void __init acpi_ec_init(void)
+{
+ int result;
+
+ result = acpi_ec_init_workqueues();
+ if (result)
+ return;
+
+ /*
+ * Disable EC wakeup on following systems to prevent periodic
+ * wakeup from EC GPE.
+ */
+ if (dmi_check_system(acpi_ec_no_wakeup)) {
+ ec_no_wakeup = true;
+ pr_debug("Disabling EC wakeup on suspend-to-idle\n");
+ }
+
+ /* Driver must be registered after acpi_ec_init_workqueues(). */
+ acpi_bus_register_driver(&acpi_ec_driver);
+
+ acpi_ec_ecdt_start();
+}
+
+/* EC driver currently not unloadable */
+#if 0
+static void __exit acpi_ec_exit(void)
+{
+
+ acpi_bus_unregister_driver(&acpi_ec_driver);
+ acpi_ec_destroy_workqueues();
+}
+#endif /* 0 */
diff --git a/drivers/acpi/ec_sys.c b/drivers/acpi/ec_sys.c
new file mode 100644
index 000000000..c074a0fae
--- /dev/null
+++ b/drivers/acpi/ec_sys.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ec_sys.c
+ *
+ * Copyright (C) 2010 SUSE Products GmbH/Novell
+ * Author:
+ * Thomas Renninger <trenn@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include "internal.h"
+
+MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
+MODULE_DESCRIPTION("ACPI EC sysfs access driver");
+MODULE_LICENSE("GPL");
+
+static bool write_support;
+module_param_hw(write_support, bool, other, 0644);
+MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may "
+ "be needed.");
+
+#define EC_SPACE_SIZE 256
+
+static struct dentry *acpi_ec_debugfs_dir;
+
+static ssize_t acpi_ec_read_io(struct file *f, char __user *buf,
+ size_t count, loff_t *off)
+{
+ /* Use this if support reading/writing multiple ECs exists in ec.c:
+ * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private;
+ */
+ unsigned int size = EC_SPACE_SIZE;
+ loff_t init_off = *off;
+ int err = 0;
+
+ if (*off >= size)
+ return 0;
+ if (*off + count >= size) {
+ size -= *off;
+ count = size;
+ } else
+ size = count;
+
+ while (size) {
+ u8 byte_read;
+ err = ec_read(*off, &byte_read);
+ if (err)
+ return err;
+ if (put_user(byte_read, buf + *off - init_off)) {
+ if (*off - init_off)
+ return *off - init_off; /* partial read */
+ return -EFAULT;
+ }
+ *off += 1;
+ size--;
+ }
+ return count;
+}
+
+static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf,
+ size_t count, loff_t *off)
+{
+ /* Use this if support reading/writing multiple ECs exists in ec.c:
+ * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private;
+ */
+
+ unsigned int size = count;
+ loff_t init_off = *off;
+ int err = 0;
+
+ if (!write_support)
+ return -EINVAL;
+
+ if (*off >= EC_SPACE_SIZE)
+ return 0;
+ if (*off + count >= EC_SPACE_SIZE) {
+ size = EC_SPACE_SIZE - *off;
+ count = size;
+ }
+
+ while (size) {
+ u8 byte_write;
+ if (get_user(byte_write, buf + *off - init_off)) {
+ if (*off - init_off)
+ return *off - init_off; /* partial write */
+ return -EFAULT;
+ }
+ err = ec_write(*off, byte_write);
+ if (err)
+ return err;
+
+ *off += 1;
+ size--;
+ }
+ return count;
+}
+
+static const struct file_operations acpi_ec_io_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = acpi_ec_read_io,
+ .write = acpi_ec_write_io,
+ .llseek = default_llseek,
+};
+
+static void acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count)
+{
+ struct dentry *dev_dir;
+ char name[64];
+ umode_t mode = 0400;
+
+ if (ec_device_count == 0)
+ acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL);
+
+ sprintf(name, "ec%u", ec_device_count);
+ dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir);
+
+ debugfs_create_x32("gpe", 0444, dev_dir, &first_ec->gpe);
+ debugfs_create_bool("use_global_lock", 0444, dev_dir,
+ &first_ec->global_lock);
+
+ if (write_support)
+ mode = 0600;
+ debugfs_create_file("io", mode, dev_dir, ec, &acpi_ec_io_ops);
+}
+
+static int __init acpi_ec_sys_init(void)
+{
+ if (first_ec)
+ acpi_ec_add_debugfs(first_ec, 0);
+ return 0;
+}
+
+static void __exit acpi_ec_sys_exit(void)
+{
+ debugfs_remove_recursive(acpi_ec_debugfs_dir);
+}
+
+module_init(acpi_ec_sys_init);
+module_exit(acpi_ec_sys_exit);
diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c
new file mode 100644
index 000000000..d199a19bb
--- /dev/null
+++ b/drivers/acpi/event.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * event.c - exporting ACPI events via procfs
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/spinlock.h>
+#include <linux/export.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/gfp.h>
+#include <linux/acpi.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "internal.h"
+
+/* ACPI notifier chain */
+static BLOCKING_NOTIFIER_HEAD(acpi_chain_head);
+
+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);
+ event.type = type;
+ event.data = data;
+ return (blocking_notifier_call_chain(&acpi_chain_head, 0, (void *)&event)
+ == NOTIFY_BAD) ? -EINVAL : 0;
+}
+EXPORT_SYMBOL(acpi_notifier_call_chain);
+
+int register_acpi_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&acpi_chain_head, nb);
+}
+EXPORT_SYMBOL(register_acpi_notifier);
+
+int unregister_acpi_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&acpi_chain_head, nb);
+}
+EXPORT_SYMBOL(unregister_acpi_notifier);
+
+#ifdef CONFIG_NET
+static unsigned int acpi_event_seqnum;
+struct acpi_genl_event {
+ acpi_device_class device_class;
+ char bus_id[15];
+ u32 type;
+ u32 data;
+};
+
+/* attributes of acpi_genl_family */
+enum {
+ ACPI_GENL_ATTR_UNSPEC,
+ ACPI_GENL_ATTR_EVENT, /* ACPI event info needed by user space */
+ __ACPI_GENL_ATTR_MAX,
+};
+#define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1)
+
+/* commands supported by the acpi_genl_family */
+enum {
+ ACPI_GENL_CMD_UNSPEC,
+ ACPI_GENL_CMD_EVENT, /* kernel->user notifications for ACPI events */
+ __ACPI_GENL_CMD_MAX,
+};
+#define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1)
+
+#define ACPI_GENL_FAMILY_NAME "acpi_event"
+#define ACPI_GENL_VERSION 0x01
+#define ACPI_GENL_MCAST_GROUP_NAME "acpi_mc_group"
+
+static const struct genl_multicast_group acpi_event_mcgrps[] = {
+ { .name = ACPI_GENL_MCAST_GROUP_NAME, },
+};
+
+static struct genl_family acpi_event_genl_family __ro_after_init = {
+ .module = THIS_MODULE,
+ .name = ACPI_GENL_FAMILY_NAME,
+ .version = ACPI_GENL_VERSION,
+ .maxattr = ACPI_GENL_ATTR_MAX,
+ .mcgrps = acpi_event_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(acpi_event_mcgrps),
+};
+
+int acpi_bus_generate_netlink_event(const char *device_class,
+ const char *bus_id,
+ u8 type, int data)
+{
+ struct sk_buff *skb;
+ struct nlattr *attr;
+ struct acpi_genl_event *event;
+ void *msg_header;
+ int size;
+
+ /* allocate memory */
+ size = nla_total_size(sizeof(struct acpi_genl_event)) +
+ nla_total_size(0);
+
+ skb = genlmsg_new(size, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ /* add the genetlink message header */
+ msg_header = genlmsg_put(skb, 0, acpi_event_seqnum++,
+ &acpi_event_genl_family, 0,
+ ACPI_GENL_CMD_EVENT);
+ if (!msg_header) {
+ nlmsg_free(skb);
+ return -ENOMEM;
+ }
+
+ /* fill the data */
+ attr =
+ nla_reserve(skb, ACPI_GENL_ATTR_EVENT,
+ sizeof(struct acpi_genl_event));
+ if (!attr) {
+ nlmsg_free(skb);
+ return -EINVAL;
+ }
+
+ event = nla_data(attr);
+ memset(event, 0, sizeof(struct acpi_genl_event));
+
+ strscpy(event->device_class, device_class, sizeof(event->device_class));
+ strscpy(event->bus_id, bus_id, sizeof(event->bus_id));
+ event->type = type;
+ event->data = data;
+
+ /* send multicast genetlink message */
+ genlmsg_end(skb, msg_header);
+
+ genlmsg_multicast(&acpi_event_genl_family, skb, 0, 0, GFP_ATOMIC);
+ return 0;
+}
+
+EXPORT_SYMBOL(acpi_bus_generate_netlink_event);
+
+static int __init acpi_event_genetlink_init(void)
+{
+ return genl_register_family(&acpi_event_genl_family);
+}
+
+#else
+int acpi_bus_generate_netlink_event(const char *device_class,
+ const char *bus_id,
+ u8 type, int data)
+{
+ return 0;
+}
+
+EXPORT_SYMBOL(acpi_bus_generate_netlink_event);
+
+static int acpi_event_genetlink_init(void)
+{
+ return -ENODEV;
+}
+#endif
+
+static int __init acpi_event_init(void)
+{
+ int error;
+
+ if (acpi_disabled)
+ return 0;
+
+ /* create genetlink for acpi event */
+ error = acpi_event_genetlink_init();
+ if (error)
+ pr_warn("Failed to create genetlink family for ACPI event\n");
+
+ return 0;
+}
+
+fs_initcall(acpi_event_init);
diff --git a/drivers/acpi/evged.c b/drivers/acpi/evged.c
new file mode 100644
index 000000000..fe6b6792c
--- /dev/null
+++ b/drivers/acpi/evged.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Generic Event Device for ACPI.
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Generic Event Device allows platforms to handle interrupts in ACPI
+ * ASL statements. It follows very similar to _EVT method approach
+ * from GPIO events. All interrupts are listed in _CRS and the handler
+ * is written in _EVT method. Here is an example.
+ *
+ * Device (GED0)
+ * {
+ *
+ * Name (_HID, "ACPI0013")
+ * Name (_UID, 0)
+ * Method (_CRS, 0x0, Serialized)
+ * {
+ * Name (RBUF, ResourceTemplate ()
+ * {
+ * Interrupt(ResourceConsumer, Edge, ActiveHigh, Shared, , , )
+ * {123}
+ * }
+ * })
+ *
+ * Method (_EVT, 1) {
+ * if (Lequal(123, Arg0))
+ * {
+ * }
+ * }
+ * }
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+#define MODULE_NAME "acpi-ged"
+
+struct acpi_ged_device {
+ struct device *dev;
+ struct list_head event_list;
+};
+
+struct acpi_ged_event {
+ struct list_head node;
+ struct device *dev;
+ unsigned int gsi;
+ unsigned int irq;
+ acpi_handle handle;
+};
+
+static irqreturn_t acpi_ged_irq_handler(int irq, void *data)
+{
+ struct acpi_ged_event *event = data;
+ acpi_status acpi_ret;
+
+ acpi_ret = acpi_execute_simple_method(event->handle, NULL, event->gsi);
+ if (ACPI_FAILURE(acpi_ret))
+ dev_err_once(event->dev, "IRQ method execution failed\n");
+
+ return IRQ_HANDLED;
+}
+
+static acpi_status acpi_ged_request_interrupt(struct acpi_resource *ares,
+ void *context)
+{
+ struct acpi_ged_event *event;
+ unsigned int irq;
+ unsigned int gsi;
+ unsigned int irqflags = IRQF_ONESHOT;
+ struct acpi_ged_device *geddev = context;
+ struct device *dev = geddev->dev;
+ acpi_handle handle = ACPI_HANDLE(dev);
+ acpi_handle evt_handle;
+ struct resource r;
+ struct acpi_resource_irq *p = &ares->data.irq;
+ struct acpi_resource_extended_irq *pext = &ares->data.extended_irq;
+ char ev_name[5];
+ u8 trigger;
+
+ if (ares->type == ACPI_RESOURCE_TYPE_END_TAG)
+ return AE_OK;
+
+ if (!acpi_dev_resource_interrupt(ares, 0, &r)) {
+ dev_err(dev, "unable to parse IRQ resource\n");
+ return AE_ERROR;
+ }
+ if (ares->type == ACPI_RESOURCE_TYPE_IRQ) {
+ gsi = p->interrupts[0];
+ trigger = p->triggering;
+ } else {
+ gsi = pext->interrupts[0];
+ trigger = pext->triggering;
+ }
+
+ irq = r.start;
+
+ switch (gsi) {
+ case 0 ... 255:
+ sprintf(ev_name, "_%c%02X",
+ trigger == ACPI_EDGE_SENSITIVE ? 'E' : 'L', gsi);
+
+ if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle)))
+ break;
+ fallthrough;
+ default:
+ if (ACPI_SUCCESS(acpi_get_handle(handle, "_EVT", &evt_handle)))
+ break;
+
+ dev_err(dev, "cannot locate _EVT method\n");
+ return AE_ERROR;
+ }
+
+ event = devm_kzalloc(dev, sizeof(*event), GFP_KERNEL);
+ if (!event)
+ return AE_ERROR;
+
+ event->gsi = gsi;
+ event->dev = dev;
+ event->irq = irq;
+ event->handle = evt_handle;
+
+ if (r.flags & IORESOURCE_IRQ_SHAREABLE)
+ irqflags |= IRQF_SHARED;
+
+ if (request_threaded_irq(irq, NULL, acpi_ged_irq_handler,
+ irqflags, "ACPI:Ged", event)) {
+ dev_err(dev, "failed to setup event handler for irq %u\n", irq);
+ return AE_ERROR;
+ }
+
+ dev_dbg(dev, "GED listening GSI %u @ IRQ %u\n", gsi, irq);
+ list_add_tail(&event->node, &geddev->event_list);
+ return AE_OK;
+}
+
+static int ged_probe(struct platform_device *pdev)
+{
+ struct acpi_ged_device *geddev;
+ acpi_status acpi_ret;
+
+ geddev = devm_kzalloc(&pdev->dev, sizeof(*geddev), GFP_KERNEL);
+ if (!geddev)
+ return -ENOMEM;
+
+ geddev->dev = &pdev->dev;
+ INIT_LIST_HEAD(&geddev->event_list);
+ acpi_ret = acpi_walk_resources(ACPI_HANDLE(&pdev->dev), "_CRS",
+ acpi_ged_request_interrupt, geddev);
+ if (ACPI_FAILURE(acpi_ret)) {
+ dev_err(&pdev->dev, "unable to parse the _CRS record\n");
+ return -EINVAL;
+ }
+ platform_set_drvdata(pdev, geddev);
+
+ return 0;
+}
+
+static void ged_shutdown(struct platform_device *pdev)
+{
+ struct acpi_ged_device *geddev = platform_get_drvdata(pdev);
+ struct acpi_ged_event *event, *next;
+
+ list_for_each_entry_safe(event, next, &geddev->event_list, node) {
+ free_irq(event->irq, event);
+ list_del(&event->node);
+ dev_dbg(geddev->dev, "GED releasing GSI %u @ IRQ %u\n",
+ event->gsi, event->irq);
+ }
+}
+
+static int ged_remove(struct platform_device *pdev)
+{
+ ged_shutdown(pdev);
+ return 0;
+}
+
+static const struct acpi_device_id ged_acpi_ids[] = {
+ {"ACPI0013"},
+ {},
+};
+
+static struct platform_driver ged_driver = {
+ .probe = ged_probe,
+ .remove = ged_remove,
+ .shutdown = ged_shutdown,
+ .driver = {
+ .name = MODULE_NAME,
+ .acpi_match_table = ACPI_PTR(ged_acpi_ids),
+ },
+};
+builtin_platform_driver(ged_driver);
diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h
new file mode 100644
index 000000000..e7b4b4e4a
--- /dev/null
+++ b/drivers/acpi/fan.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * ACPI fan device IDs are shared between the fan driver and the device power
+ * management code.
+ *
+ * Add new device IDs before the generic ACPI fan one.
+ */
+
+#ifndef _ACPI_FAN_H_
+#define _ACPI_FAN_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 */ \
+ {"INTC10A2", }, /* Fan for Raptor Lake generation */ \
+ {"PNP0C0B", } /* Generic ACPI fan */
+
+#define ACPI_FPS_NAME_LEN 20
+
+struct acpi_fan_fps {
+ u64 control;
+ u64 trip_point;
+ u64 speed;
+ u64 noise_level;
+ u64 power;
+ char name[ACPI_FPS_NAME_LEN];
+ struct device_attribute dev_attr;
+};
+
+struct acpi_fan_fif {
+ u8 revision;
+ u8 fine_grain_ctrl;
+ u8 step_size;
+ u8 low_speed_notification;
+};
+
+struct acpi_fan_fst {
+ u64 revision;
+ u64 control;
+ u64 speed;
+};
+
+struct acpi_fan {
+ bool acpi4;
+ struct acpi_fan_fif fif;
+ struct acpi_fan_fps *fps;
+ int fps_count;
+ 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);
+int acpi_fan_create_attributes(struct acpi_device *device);
+void acpi_fan_delete_attributes(struct acpi_device *device);
+#endif
diff --git a/drivers/acpi/fan_attr.c b/drivers/acpi/fan_attr.c
new file mode 100644
index 000000000..f15157d40
--- /dev/null
+++ b/drivers/acpi/fan_attr.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * fan_attr.c - Create extra attributes for ACPI Fan driver
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2022 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+
+#include "fan.h"
+
+MODULE_LICENSE("GPL");
+
+static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr);
+ int count;
+
+ if (fps->control == 0xFFFFFFFF || fps->control > 100)
+ count = scnprintf(buf, PAGE_SIZE, "not-defined:");
+ else
+ count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control);
+
+ if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9)
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
+ else
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point);
+
+ if (fps->speed == 0xFFFFFFFF)
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
+ else
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed);
+
+ if (fps->noise_level == 0xFFFFFFFF)
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
+ else
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100);
+
+ if (fps->power == 0xFFFFFFFF)
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n");
+ else
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld\n", fps->power);
+
+ return count;
+}
+
+static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev);
+ struct acpi_fan_fst fst;
+ int status;
+
+ status = acpi_fan_get_fst(acpi_dev, &fst);
+ if (status)
+ return status;
+
+ return sprintf(buf, "%lld\n", fst.speed);
+}
+
+static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ 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);
+}
+
+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;
+ fan->fst_speed.store = NULL;
+ fan->fst_speed.attr.name = "fan_speed_rpm";
+ fan->fst_speed.attr.mode = 0444;
+ status = sysfs_create_file(&device->dev.kobj, &fan->fst_speed.attr);
+ if (status)
+ goto rem_fine_grain_attr;
+
+ for (i = 0; i < fan->fps_count; ++i) {
+ struct acpi_fan_fps *fps = &fan->fps[i];
+
+ snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i);
+ sysfs_attr_init(&fps->dev_attr.attr);
+ fps->dev_attr.show = show_state;
+ fps->dev_attr.store = NULL;
+ fps->dev_attr.attr.name = fps->name;
+ fps->dev_attr.attr.mode = 0444;
+ status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr);
+ if (status) {
+ int j;
+
+ for (j = 0; j < i; ++j)
+ sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr);
+ goto rem_fst_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);
+
+ return status;
+}
+
+void acpi_fan_delete_attributes(struct acpi_device *device)
+{
+ struct acpi_fan *fan = acpi_driver_data(device);
+ int i;
+
+ 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
new file mode 100644
index 000000000..52a0b303b
--- /dev/null
+++ b/drivers/acpi/fan_core.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * fan_core.c - ACPI Fan core Driver
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2022 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/thermal.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/sort.h>
+
+#include "fan.h"
+
+static const struct acpi_device_id fan_device_ids[] = {
+ ACPI_FAN_DEVICE_IDS,
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, fan_device_ids);
+
+/* thermal cooling device callbacks */
+static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
+ *state)
+{
+ struct acpi_device *device = cdev->devdata;
+ struct acpi_fan *fan = acpi_driver_data(device);
+
+ if (fan->acpi4) {
+ if (fan->fif.fine_grain_ctrl)
+ *state = 100 / fan->fif.step_size;
+ else
+ *state = fan->fps_count - 1;
+ } else {
+ *state = 1;
+ }
+
+ return 0;
+}
+
+int acpi_fan_get_fst(struct acpi_device *device, 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;
+ }
+
+ 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;
+ goto err;
+ }
+
+ fst->revision = obj->package.elements[0].integer.value;
+ fst->control = obj->package.elements[1].integer.value;
+ fst->speed = obj->package.elements[2].integer.value;
+
+err:
+ kfree(obj);
+ return ret;
+}
+
+static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
+{
+ struct acpi_fan *fan = acpi_driver_data(device);
+ struct acpi_fan_fst fst;
+ int status, i;
+
+ status = acpi_fan_get_fst(device, &fst);
+ if (status)
+ return status;
+
+ if (fan->fif.fine_grain_ctrl) {
+ /* This control should be same what we set using _FSL by spec */
+ if (fst.control > 100) {
+ dev_dbg(&device->dev, "Invalid control value returned\n");
+ goto match_fps;
+ }
+
+ *state = (int) fst.control / fan->fif.step_size;
+ return 0;
+ }
+
+match_fps:
+ for (i = 0; i < fan->fps_count; i++) {
+ if (fst.control == fan->fps[i].control)
+ break;
+ }
+ if (i == fan->fps_count) {
+ dev_dbg(&device->dev, "Invalid control value returned\n");
+ return -EINVAL;
+ }
+
+ *state = i;
+
+ return status;
+}
+
+static int fan_get_state(struct acpi_device *device, unsigned long *state)
+{
+ int result;
+ int acpi_state = ACPI_STATE_D0;
+
+ result = acpi_device_update_power(device, &acpi_state);
+ if (result)
+ return result;
+
+ *state = acpi_state == ACPI_STATE_D3_COLD
+ || acpi_state == ACPI_STATE_D3_HOT ?
+ 0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1);
+ return 0;
+}
+
+static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
+ *state)
+{
+ struct acpi_device *device = cdev->devdata;
+ struct acpi_fan *fan = acpi_driver_data(device);
+
+ if (fan->acpi4)
+ return fan_get_state_acpi4(device, state);
+ else
+ return fan_get_state(device, state);
+}
+
+static int fan_set_state(struct acpi_device *device, unsigned long state)
+{
+ if (state != 0 && state != 1)
+ return -EINVAL;
+
+ return acpi_device_set_power(device,
+ state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
+}
+
+static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
+{
+ struct acpi_fan *fan = acpi_driver_data(device);
+ acpi_status status;
+ u64 value = state;
+ int max_state;
+
+ if (fan->fif.fine_grain_ctrl)
+ max_state = 100 / fan->fif.step_size;
+ else
+ max_state = fan->fps_count - 1;
+
+ if (state > max_state)
+ return -EINVAL;
+
+ if (fan->fif.fine_grain_ctrl) {
+ value *= fan->fif.step_size;
+ /* Spec allows compensate the last step only */
+ if (value + fan->fif.step_size > 100)
+ value = 100;
+ } else {
+ value = fan->fps[state].control;
+ }
+
+ status = acpi_execute_simple_method(device->handle, "_FSL", value);
+ if (ACPI_FAILURE(status)) {
+ dev_dbg(&device->dev, "Failed to set state by _FSL\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int
+fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+ struct acpi_device *device = cdev->devdata;
+ struct acpi_fan *fan = acpi_driver_data(device);
+
+ if (fan->acpi4)
+ return fan_set_state_acpi4(device, state);
+ else
+ return fan_set_state(device, state);
+}
+
+static const struct thermal_cooling_device_ops fan_cooling_ops = {
+ .get_max_state = fan_get_max_state,
+ .get_cur_state = fan_get_cur_state,
+ .set_cur_state = fan_set_cur_state,
+};
+
+/* --------------------------------------------------------------------------
+ * Driver Interface
+ * --------------------------------------------------------------------------
+*/
+
+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 };
+ struct acpi_fan *fan = acpi_driver_data(device);
+ struct acpi_buffer format = { sizeof("NNNN"), "NNNN" };
+ u64 fields[4];
+ struct acpi_buffer fif = { sizeof(fields), fields };
+ union acpi_object *obj;
+ acpi_status status;
+
+ status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ obj = buffer.pointer;
+ if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
+ dev_err(&device->dev, "Invalid _FIF data\n");
+ status = -EINVAL;
+ goto err;
+ }
+
+ status = acpi_extract_package(obj, &format, &fif);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&device->dev, "Invalid _FIF element\n");
+ status = -EINVAL;
+ }
+
+ fan->fif.revision = fields[0];
+ fan->fif.fine_grain_ctrl = fields[1];
+ fan->fif.step_size = fields[2];
+ fan->fif.low_speed_notification = fields[3];
+
+ /* If there is a bug in step size and set as 0, change to 1 */
+ if (!fan->fif.step_size)
+ fan->fif.step_size = 1;
+ /* If step size > 9, change to 9 (by spec valid values 1-9) */
+ else if (fan->fif.step_size > 9)
+ fan->fif.step_size = 9;
+err:
+ kfree(obj);
+ return status;
+}
+
+static int acpi_fan_speed_cmp(const void *a, const void *b)
+{
+ const struct acpi_fan_fps *fps1 = a;
+ const struct acpi_fan_fps *fps2 = b;
+ return fps1->speed - fps2->speed;
+}
+
+static int acpi_fan_get_fps(struct acpi_device *device)
+{
+ struct acpi_fan *fan = acpi_driver_data(device);
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ acpi_status status;
+ int i;
+
+ status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ obj = buffer.pointer;
+ if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) {
+ dev_err(&device->dev, "Invalid _FPS data\n");
+ status = -EINVAL;
+ goto err;
+ }
+
+ fan->fps_count = obj->package.count - 1; /* minus revision field */
+ fan->fps = devm_kcalloc(&device->dev,
+ fan->fps_count, sizeof(struct acpi_fan_fps),
+ GFP_KERNEL);
+ if (!fan->fps) {
+ dev_err(&device->dev, "Not enough memory\n");
+ status = -ENOMEM;
+ goto err;
+ }
+ for (i = 0; i < fan->fps_count; i++) {
+ struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
+ struct acpi_buffer fps = { offsetof(struct acpi_fan_fps, name),
+ &fan->fps[i] };
+ status = acpi_extract_package(&obj->package.elements[i + 1],
+ &format, &fps);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&device->dev, "Invalid _FPS element\n");
+ goto err;
+ }
+ }
+
+ /* sort the state array according to fan speed in increase order */
+ sort(fan->fps, fan->fps_count, sizeof(*fan->fps),
+ acpi_fan_speed_cmp, NULL);
+
+err:
+ kfree(obj);
+ return status;
+}
+
+static int acpi_fan_probe(struct platform_device *pdev)
+{
+ int result = 0;
+ struct thermal_cooling_device *cdev;
+ struct acpi_fan *fan;
+ struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
+ char *name;
+
+ fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
+ if (!fan) {
+ dev_err(&device->dev, "No memory for fan\n");
+ return -ENOMEM;
+ }
+ device->driver_data = fan;
+ platform_set_drvdata(pdev, fan);
+
+ if (acpi_fan_is_acpi4(device)) {
+ result = acpi_fan_get_fif(device);
+ if (result)
+ return result;
+
+ result = acpi_fan_get_fps(device);
+ if (result)
+ return result;
+
+ result = acpi_fan_create_attributes(device);
+ if (result)
+ return result;
+
+ fan->acpi4 = true;
+ } else {
+ result = acpi_device_update_power(device, NULL);
+ if (result) {
+ dev_err(&device->dev, "Failed to set initial power state\n");
+ goto err_end;
+ }
+ }
+
+ if (!strncmp(pdev->name, "PNP0C0B", strlen("PNP0C0B")))
+ name = "Fan";
+ else
+ name = acpi_device_bid(device);
+
+ cdev = thermal_cooling_device_register(name, device,
+ &fan_cooling_ops);
+ if (IS_ERR(cdev)) {
+ result = PTR_ERR(cdev);
+ goto err_end;
+ }
+
+ dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
+
+ fan->cdev = cdev;
+ result = sysfs_create_link(&pdev->dev.kobj,
+ &cdev->device.kobj,
+ "thermal_cooling");
+ if (result)
+ dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n");
+
+ 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;
+ }
+
+ return 0;
+
+err_end:
+ if (fan->acpi4)
+ acpi_fan_delete_attributes(device);
+
+ return result;
+}
+
+static int acpi_fan_remove(struct platform_device *pdev)
+{
+ struct acpi_fan *fan = platform_get_drvdata(pdev);
+
+ if (fan->acpi4) {
+ struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
+
+ acpi_fan_delete_attributes(device);
+ }
+ 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
+static int acpi_fan_suspend(struct device *dev)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ if (fan->acpi4)
+ return 0;
+
+ acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
+
+ return AE_OK;
+}
+
+static int acpi_fan_resume(struct device *dev)
+{
+ int result;
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+
+ if (fan->acpi4)
+ return 0;
+
+ result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
+ if (result)
+ dev_err(dev, "Error updating fan power state\n");
+
+ 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/glue.c b/drivers/acpi/glue.c
new file mode 100644
index 000000000..a194f3087
--- /dev/null
+++ b/drivers/acpi/glue.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Link physical devices with ACPI devices support
+ *
+ * Copyright (c) 2005 David Shaohua Li <shaohua.li@intel.com>
+ * Copyright (c) 2005 Intel Corp.
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/acpi_iort.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/rwsem.h>
+#include <linux/acpi.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/pci-acpi.h>
+#include <linux/platform_device.h>
+
+#include "internal.h"
+
+static LIST_HEAD(bus_type_list);
+static DECLARE_RWSEM(bus_type_sem);
+
+#define PHYSICAL_NODE_STRING "physical_node"
+#define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10)
+
+int register_acpi_bus_type(struct acpi_bus_type *type)
+{
+ if (acpi_disabled)
+ return -ENODEV;
+ if (type && type->match && type->find_companion) {
+ down_write(&bus_type_sem);
+ list_add_tail(&type->list, &bus_type_list);
+ up_write(&bus_type_sem);
+ pr_info("bus type %s registered\n", type->name);
+ return 0;
+ }
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(register_acpi_bus_type);
+
+int unregister_acpi_bus_type(struct acpi_bus_type *type)
+{
+ if (acpi_disabled)
+ return 0;
+ if (type) {
+ down_write(&bus_type_sem);
+ list_del_init(&type->list);
+ up_write(&bus_type_sem);
+ pr_info("bus type %s unregistered\n", type->name);
+ return 0;
+ }
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);
+
+static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
+{
+ struct acpi_bus_type *tmp, *ret = NULL;
+
+ down_read(&bus_type_sem);
+ list_for_each_entry(tmp, &bus_type_list, list) {
+ if (tmp->match(dev)) {
+ ret = tmp;
+ break;
+ }
+ }
+ up_read(&bus_type_sem);
+ return ret;
+}
+
+#define FIND_CHILD_MIN_SCORE 1
+#define FIND_CHILD_MID_SCORE 2
+#define FIND_CHILD_MAX_SCORE 3
+
+static int match_any(struct acpi_device *adev, void *not_used)
+{
+ return 1;
+}
+
+static bool acpi_dev_has_children(struct acpi_device *adev)
+{
+ return acpi_dev_for_each_child(adev, match_any, NULL) > 0;
+}
+
+static int find_child_checks(struct acpi_device *adev, bool check_children)
+{
+ unsigned long long sta;
+ acpi_status status;
+
+ if (check_children && !acpi_dev_has_children(adev))
+ return -ENODEV;
+
+ status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
+ 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;
+
+ /*
+ * If the device has a _HID returning a valid ACPI/PNP device ID, it is
+ * better to make it look less attractive here, so that the other device
+ * with the same _ADR value (that may not have a valid device ID) can be
+ * matched going forward. [This means a second spec violation in a row,
+ * so whatever we do here is best effort anyway.]
+ */
+ if (adev->pnp.type.platform_id)
+ return FIND_CHILD_MIN_SCORE;
+
+ return FIND_CHILD_MAX_SCORE;
+}
+
+struct find_child_walk_data {
+ struct acpi_device *adev;
+ u64 address;
+ int score;
+ bool check_sta;
+ bool check_children;
+};
+
+static int check_one_child(struct acpi_device *adev, void *data)
+{
+ struct find_child_walk_data *wd = data;
+ int score;
+
+ if (!adev->pnp.type.bus_address || acpi_device_adr(adev) != wd->address)
+ return 0;
+
+ if (!wd->adev) {
+ /*
+ * This is the first matching object, so save it. If it is not
+ * necessary to look for any other matching objects, stop the
+ * search.
+ */
+ wd->adev = adev;
+ return !(wd->check_sta || wd->check_children);
+ }
+
+ /*
+ * There is more than one matching device object with the same _ADR
+ * value. That really is unexpected, so we are kind of beyond the scope
+ * of the spec here. We have to choose which one to return, though.
+ *
+ * First, get the score for the previously found object and terminate
+ * the walk if it is maximum.
+ */
+ if (!wd->score) {
+ score = find_child_checks(wd->adev, wd->check_children);
+ if (score == FIND_CHILD_MAX_SCORE)
+ return 1;
+
+ wd->score = score;
+ }
+ /*
+ * Second, if the object that has just been found has a better score,
+ * replace the previously found one with it and terminate the walk if
+ * the new score is maximum.
+ */
+ score = find_child_checks(adev, wd->check_children);
+ if (score > wd->score) {
+ wd->adev = adev;
+ if (score == FIND_CHILD_MAX_SCORE)
+ return 1;
+
+ wd->score = score;
+ }
+
+ /* Continue, because there may be better matches. */
+ return 0;
+}
+
+static struct acpi_device *acpi_find_child(struct acpi_device *parent,
+ u64 address, bool check_children,
+ bool check_sta)
+{
+ struct find_child_walk_data wd = {
+ .address = address,
+ .check_children = check_children,
+ .check_sta = check_sta,
+ .adev = NULL,
+ .score = 0,
+ };
+
+ if (parent)
+ acpi_dev_for_each_child(parent, check_one_child, &wd);
+
+ return wd.adev;
+}
+
+struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
+ u64 address, bool check_children)
+{
+ return acpi_find_child(parent, address, check_children, true);
+}
+EXPORT_SYMBOL_GPL(acpi_find_child_device);
+
+struct acpi_device *acpi_find_child_by_adr(struct acpi_device *adev,
+ acpi_bus_address adr)
+{
+ return acpi_find_child(adev, adr, false, false);
+}
+EXPORT_SYMBOL_GPL(acpi_find_child_by_adr);
+
+static void acpi_physnode_link_name(char *buf, unsigned int node_id)
+{
+ if (node_id > 0)
+ snprintf(buf, PHYSICAL_NODE_NAME_SIZE,
+ PHYSICAL_NODE_STRING "%u", node_id);
+ else
+ strcpy(buf, PHYSICAL_NODE_STRING);
+}
+
+int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
+{
+ struct acpi_device_physical_node *physical_node, *pn;
+ char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
+ struct list_head *physnode_list;
+ unsigned int node_id;
+ int retval = -EINVAL;
+
+ if (has_acpi_companion(dev)) {
+ if (acpi_dev) {
+ dev_warn(dev, "ACPI companion already set\n");
+ return -EINVAL;
+ } else {
+ acpi_dev = ACPI_COMPANION(dev);
+ }
+ }
+ if (!acpi_dev)
+ return -EINVAL;
+
+ acpi_dev_get(acpi_dev);
+ get_device(dev);
+ physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);
+ if (!physical_node) {
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ mutex_lock(&acpi_dev->physical_node_lock);
+
+ /*
+ * Keep the list sorted by node_id so that the IDs of removed nodes can
+ * be recycled easily.
+ */
+ physnode_list = &acpi_dev->physical_node_list;
+ node_id = 0;
+ list_for_each_entry(pn, &acpi_dev->physical_node_list, node) {
+ /* Sanity check. */
+ if (pn->dev == dev) {
+ mutex_unlock(&acpi_dev->physical_node_lock);
+
+ dev_warn(dev, "Already associated with ACPI node\n");
+ kfree(physical_node);
+ if (ACPI_COMPANION(dev) != acpi_dev)
+ goto err;
+
+ put_device(dev);
+ acpi_dev_put(acpi_dev);
+ return 0;
+ }
+ if (pn->node_id == node_id) {
+ physnode_list = &pn->node;
+ node_id++;
+ }
+ }
+
+ physical_node->node_id = node_id;
+ physical_node->dev = dev;
+ list_add(&physical_node->node, physnode_list);
+ acpi_dev->physical_node_count++;
+
+ if (!has_acpi_companion(dev))
+ ACPI_COMPANION_SET(dev, acpi_dev);
+
+ acpi_physnode_link_name(physical_node_name, node_id);
+ retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
+ physical_node_name);
+ if (retval)
+ dev_err(&acpi_dev->dev, "Failed to create link %s (%d)\n",
+ physical_node_name, retval);
+
+ retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
+ "firmware_node");
+ if (retval)
+ dev_err(dev, "Failed to create link firmware_node (%d)\n",
+ retval);
+
+ mutex_unlock(&acpi_dev->physical_node_lock);
+
+ if (acpi_dev->wakeup.flags.valid)
+ device_set_wakeup_capable(dev, true);
+
+ return 0;
+
+ err:
+ ACPI_COMPANION_SET(dev, NULL);
+ put_device(dev);
+ acpi_dev_put(acpi_dev);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(acpi_bind_one);
+
+int acpi_unbind_one(struct device *dev)
+{
+ struct acpi_device *acpi_dev = ACPI_COMPANION(dev);
+ struct acpi_device_physical_node *entry;
+
+ if (!acpi_dev)
+ return 0;
+
+ mutex_lock(&acpi_dev->physical_node_lock);
+
+ list_for_each_entry(entry, &acpi_dev->physical_node_list, node)
+ if (entry->dev == dev) {
+ char physnode_name[PHYSICAL_NODE_NAME_SIZE];
+
+ list_del(&entry->node);
+ acpi_dev->physical_node_count--;
+
+ acpi_physnode_link_name(physnode_name, entry->node_id);
+ sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name);
+ sysfs_remove_link(&dev->kobj, "firmware_node");
+ ACPI_COMPANION_SET(dev, NULL);
+ /* Drop references taken by acpi_bind_one(). */
+ put_device(dev);
+ acpi_dev_put(acpi_dev);
+ kfree(entry);
+ break;
+ }
+
+ mutex_unlock(&acpi_dev->physical_node_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_unbind_one);
+
+void acpi_device_notify(struct device *dev)
+{
+ struct acpi_device *adev;
+ int ret;
+
+ ret = acpi_bind_one(dev, NULL);
+ if (ret) {
+ struct acpi_bus_type *type = acpi_get_bus_type(dev);
+
+ if (!type)
+ goto err;
+
+ adev = type->find_companion(dev);
+ if (!adev) {
+ dev_dbg(dev, "ACPI companion not found\n");
+ goto err;
+ }
+ ret = acpi_bind_one(dev, adev);
+ if (ret)
+ goto err;
+
+ if (type->setup) {
+ type->setup(dev);
+ goto done;
+ }
+ } else {
+ adev = ACPI_COMPANION(dev);
+
+ if (dev_is_pci(dev)) {
+ pci_acpi_setup(dev, adev);
+ goto done;
+ } else if (dev_is_platform(dev)) {
+ acpi_configure_pmsi_domain(dev);
+ }
+ }
+
+ if (adev->handler && adev->handler->bind)
+ adev->handler->bind(dev);
+
+done:
+ acpi_handle_debug(ACPI_HANDLE(dev), "Bound to device %s\n",
+ dev_name(dev));
+
+ return;
+
+err:
+ dev_dbg(dev, "No ACPI support\n");
+}
+
+void acpi_device_notify_remove(struct device *dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+
+ if (!adev)
+ return;
+
+ if (dev_is_pci(dev))
+ pci_acpi_cleanup(dev, adev);
+ else if (adev->handler && adev->handler->unbind)
+ adev->handler->unbind(dev);
+
+ acpi_unbind_one(dev);
+}
diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c
new file mode 100644
index 000000000..60a2939cd
--- /dev/null
+++ b/drivers/acpi/hed.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI Hardware Error Device (PNP0C33) Driver
+ *
+ * Copyright (C) 2010, Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.com>
+ *
+ * ACPI Hardware Error Device is used to report some hardware errors
+ * notified via SCI, mainly the corrected errors.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <acpi/hed.h>
+
+static const struct acpi_device_id acpi_hed_ids[] = {
+ {"PNP0C33", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, acpi_hed_ids);
+
+static acpi_handle hed_handle;
+
+static BLOCKING_NOTIFIER_HEAD(acpi_hed_notify_list);
+
+int register_acpi_hed_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&acpi_hed_notify_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_acpi_hed_notifier);
+
+void unregister_acpi_hed_notifier(struct notifier_block *nb)
+{
+ blocking_notifier_chain_unregister(&acpi_hed_notify_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_acpi_hed_notifier);
+
+/*
+ * SCI to report hardware error is forwarded to the listeners of HED,
+ * it is used by HEST Generic Hardware Error Source with notify type
+ * SCI.
+ */
+static void acpi_hed_notify(struct acpi_device *device, u32 event)
+{
+ blocking_notifier_call_chain(&acpi_hed_notify_list, 0, NULL);
+}
+
+static int acpi_hed_add(struct acpi_device *device)
+{
+ /* Only one hardware error device */
+ if (hed_handle)
+ return -EINVAL;
+ hed_handle = device->handle;
+ return 0;
+}
+
+static int acpi_hed_remove(struct acpi_device *device)
+{
+ hed_handle = NULL;
+ return 0;
+}
+
+static struct acpi_driver acpi_hed_driver = {
+ .name = "hardware_error_device",
+ .class = "hardware_error",
+ .ids = acpi_hed_ids,
+ .ops = {
+ .add = acpi_hed_add,
+ .remove = acpi_hed_remove,
+ .notify = acpi_hed_notify,
+ },
+};
+module_acpi_driver(acpi_hed_driver);
+
+MODULE_AUTHOR("Huang Ying");
+MODULE_DESCRIPTION("ACPI Hardware Error Device Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
new file mode 100644
index 000000000..219c02df9
--- /dev/null
+++ b/drivers/acpi/internal.h
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * acpi/internal.h
+ * For use by Linux/ACPI infrastructure, not drivers
+ *
+ * Copyright (c) 2009, Intel Corporation.
+ */
+
+#ifndef _ACPI_INTERNAL_H_
+#define _ACPI_INTERNAL_H_
+
+#include <linux/idr.h>
+
+int early_acpi_osi_init(void);
+int acpi_osi_init(void);
+acpi_status acpi_os_initialize1(void);
+void acpi_scan_init(void);
+#ifdef CONFIG_PCI
+void acpi_pci_root_init(void);
+void acpi_pci_link_init(void);
+#else
+static inline void acpi_pci_root_init(void) {}
+static inline void acpi_pci_link_init(void) {}
+#endif
+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);
+void acpi_memory_hotplug_init(void);
+#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
+void pci_ioapic_remove(struct acpi_pci_root *root);
+int acpi_ioapic_remove(struct acpi_pci_root *root);
+#else
+static inline void pci_ioapic_remove(struct acpi_pci_root *root) { return; }
+static inline int acpi_ioapic_remove(struct acpi_pci_root *root) { return 0; }
+#endif
+#ifdef CONFIG_ACPI_DOCK
+void register_dock_dependent_device(struct acpi_device *adev,
+ acpi_handle dshandle);
+int dock_notify(struct acpi_device *adev, u32 event);
+void acpi_dock_add(struct acpi_device *adev);
+#else
+static inline void register_dock_dependent_device(struct acpi_device *adev,
+ acpi_handle dshandle) {}
+static inline int dock_notify(struct acpi_device *adev, u32 event) { return -ENODEV; }
+static inline void acpi_dock_add(struct acpi_device *adev) {}
+#endif
+#ifdef CONFIG_X86
+void acpi_cmos_rtc_init(void);
+#else
+static inline void acpi_cmos_rtc_init(void) {}
+#endif
+int acpi_rev_override_setup(char *str);
+
+void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,
+ const char *name);
+int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
+ const char *hotplug_profile_name);
+void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val);
+
+#ifdef CONFIG_DEBUG_FS
+extern struct dentry *acpi_debugfs_dir;
+void acpi_debugfs_init(void);
+#else
+static inline void acpi_debugfs_init(void) { return; }
+#endif
+#ifdef CONFIG_PCI
+void acpi_lpss_init(void);
+#else
+static inline void acpi_lpss_init(void) {}
+#endif
+
+void acpi_apd_init(void);
+
+acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src);
+bool acpi_queue_hotplug_work(struct work_struct *work);
+void acpi_device_hotplug(struct acpi_device *adev, u32 src);
+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);
+
+/* --------------------------------------------------------------------------
+ Device Node Initialization / Removal
+ -------------------------------------------------------------------------- */
+#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \
+ ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING)
+
+extern struct list_head acpi_bus_id_list;
+
+struct acpi_device_bus_id {
+ const char *bus_id;
+ struct ida instance_ida;
+ struct list_head node;
+};
+
+void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
+ int type, void (*release)(struct device *));
+int acpi_tie_acpi_dev(struct acpi_device *adev);
+int acpi_device_add(struct acpi_device *device);
+int acpi_device_setup_files(struct acpi_device *dev);
+void acpi_device_remove_files(struct acpi_device *dev);
+void acpi_device_add_finalize(struct acpi_device *device);
+void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
+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,
+ const struct device *dev);
+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,
+ struct kobj_uevent_env *env);
+
+/* --------------------------------------------------------------------------
+ Power Resource
+ -------------------------------------------------------------------------- */
+int acpi_power_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);
+struct acpi_device *acpi_add_power_resource(acpi_handle handle);
+void acpi_power_add_remove_device(struct acpi_device *adev, bool add);
+int acpi_power_wakeup_list_init(struct list_head *list, int *system_level);
+int acpi_device_sleep_wake(struct acpi_device *dev,
+ int enable, int sleep_state, int dev_state);
+int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
+int acpi_power_on_resources(struct acpi_device *device, int state);
+int acpi_power_transition(struct acpi_device *device, int state);
+void acpi_turn_off_unused_power_resources(void);
+
+/* --------------------------------------------------------------------------
+ Device Power Management
+ -------------------------------------------------------------------------- */
+int acpi_device_get_power(struct acpi_device *device, int *state);
+int acpi_wakeup_device_init(void);
+
+/* --------------------------------------------------------------------------
+ Processor
+ -------------------------------------------------------------------------- */
+#ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC
+void acpi_early_processor_set_pdc(void);
+#else
+static inline void acpi_early_processor_set_pdc(void) {}
+#endif
+
+#ifdef CONFIG_X86
+void acpi_early_processor_osc(void);
+#else
+static inline void acpi_early_processor_osc(void) {}
+#endif
+
+/* --------------------------------------------------------------------------
+ Embedded Controller
+ -------------------------------------------------------------------------- */
+
+enum acpi_ec_event_state {
+ EC_EVENT_READY = 0, /* Event work can be submitted */
+ EC_EVENT_IN_PROGRESS, /* Event work is pending or being processed */
+ EC_EVENT_COMPLETE, /* Event work processing has completed */
+};
+
+struct acpi_ec {
+ acpi_handle handle;
+ int gpe;
+ int irq;
+ unsigned long command_addr;
+ unsigned long data_addr;
+ bool global_lock;
+ unsigned long flags;
+ unsigned long reference_count;
+ struct mutex mutex;
+ wait_queue_head_t wait;
+ struct list_head list;
+ struct transaction *curr;
+ spinlock_t lock;
+ struct work_struct work;
+ unsigned long timestamp;
+ enum acpi_ec_event_state event_state;
+ unsigned int events_to_process;
+ unsigned int events_in_progress;
+ unsigned int queries_in_progress;
+ bool busy_polling;
+ unsigned int polling_guard;
+};
+
+extern struct acpi_ec *first_ec;
+
+/* If we find an EC via the ECDT, we need to keep a ptr to its context */
+/* External interfaces use first EC only, so remember */
+typedef int (*acpi_ec_query_func) (void *data);
+
+void acpi_ec_init(void);
+void acpi_ec_ecdt_probe(void);
+void acpi_ec_dsdt_probe(void);
+void acpi_ec_block_transactions(void);
+void acpi_ec_unblock_transactions(void);
+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);
+
+#ifdef CONFIG_PM_SLEEP
+void acpi_ec_flush_work(void);
+bool acpi_ec_dispatch_gpe(void);
+#endif
+
+
+/*--------------------------------------------------------------------------
+ Suspend/Resume
+ -------------------------------------------------------------------------- */
+#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT
+extern bool acpi_s2idle_wakeup(void);
+extern int acpi_sleep_init(void);
+#else
+static inline bool acpi_s2idle_wakeup(void) { return false; }
+static inline int acpi_sleep_init(void) { return -ENXIO; }
+#endif
+
+#ifdef CONFIG_ACPI_SLEEP
+void acpi_sleep_proc_init(void);
+int suspend_nvs_alloc(void);
+void suspend_nvs_free(void);
+int suspend_nvs_save(void);
+void suspend_nvs_restore(void);
+#else
+static inline void acpi_sleep_proc_init(void) {}
+static inline int suspend_nvs_alloc(void) { return 0; }
+static inline void suspend_nvs_free(void) {}
+static inline int suspend_nvs_save(void) { return 0; }
+static inline void suspend_nvs_restore(void) {}
+#endif
+
+#ifdef CONFIG_X86
+bool force_storage_d3(void);
+#else
+static inline bool force_storage_d3(void)
+{
+ return false;
+}
+#endif
+
+/*--------------------------------------------------------------------------
+ Device properties
+ -------------------------------------------------------------------------- */
+#define ACPI_DT_NAMESPACE_HID "PRP0001"
+
+void acpi_init_properties(struct acpi_device *adev);
+void acpi_free_properties(struct acpi_device *adev);
+
+#ifdef CONFIG_X86
+void acpi_extract_apple_properties(struct acpi_device *adev);
+#else
+static inline void acpi_extract_apple_properties(struct acpi_device *adev) {}
+#endif
+
+/*--------------------------------------------------------------------------
+ Watchdog
+ -------------------------------------------------------------------------- */
+
+#ifdef CONFIG_ACPI_WATCHDOG
+void acpi_watchdog_init(void);
+#else
+static inline void acpi_watchdog_init(void) {}
+#endif
+
+#ifdef CONFIG_ACPI_LPIT
+void acpi_init_lpit(void);
+#else
+static inline void acpi_init_lpit(void) { }
+#endif
+
+#endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/ioapic.c b/drivers/acpi/ioapic.c
new file mode 100644
index 000000000..a690c7b18
--- /dev/null
+++ b/drivers/acpi/ioapic.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IOAPIC/IOxAPIC/IOSAPIC driver
+ *
+ * Copyright (C) 2009 Fujitsu Limited.
+ * (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Based on original drivers/pci/ioapic.c
+ * Yinghai Lu <yinghai@kernel.org>
+ * Jiang Liu <jiang.liu@intel.com>
+ */
+
+/*
+ * This driver manages I/O APICs added by hotplug after boot.
+ * We try to claim all I/O APIC devices, but those present at boot were
+ * registered when we parsed the ACPI MADT.
+ */
+
+#define pr_fmt(fmt) "ACPI: IOAPIC: " fmt
+
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <acpi/acpi.h>
+
+struct acpi_pci_ioapic {
+ acpi_handle root_handle;
+ acpi_handle handle;
+ u32 gsi_base;
+ struct resource res;
+ struct pci_dev *pdev;
+ struct list_head list;
+};
+
+static LIST_HEAD(ioapic_list);
+static DEFINE_MUTEX(ioapic_list_lock);
+
+static acpi_status setup_res(struct acpi_resource *acpi_res, void *data)
+{
+ struct resource *res = data;
+ struct resource_win win;
+
+ /*
+ * We might assign this to 'res' later, make sure all pointers are
+ * cleared before the resource is added to the global list
+ */
+ memset(&win, 0, sizeof(win));
+
+ res->flags = 0;
+ if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM))
+ return AE_OK;
+
+ if (!acpi_dev_resource_memory(acpi_res, res)) {
+ if (acpi_dev_resource_address_space(acpi_res, &win) ||
+ acpi_dev_resource_ext_address_space(acpi_res, &win))
+ *res = win.res;
+ }
+ if ((res->flags & IORESOURCE_PREFETCH) ||
+ (res->flags & IORESOURCE_DISABLED))
+ res->flags = 0;
+
+ return AE_CTRL_TERMINATE;
+}
+
+static bool acpi_is_ioapic(acpi_handle handle, char **type)
+{
+ acpi_status status;
+ struct acpi_device_info *info;
+ char *hid = NULL;
+ bool match = false;
+
+ if (!acpi_has_method(handle, "_GSB"))
+ return false;
+
+ status = acpi_get_object_info(handle, &info);
+ if (ACPI_SUCCESS(status)) {
+ if (info->valid & ACPI_VALID_HID)
+ hid = info->hardware_id.string;
+ if (hid) {
+ if (strcmp(hid, "ACPI0009") == 0) {
+ *type = "IOxAPIC";
+ match = true;
+ } else if (strcmp(hid, "ACPI000A") == 0) {
+ *type = "IOAPIC";
+ match = true;
+ }
+ }
+ kfree(info);
+ }
+
+ return match;
+}
+
+static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl,
+ void *context, void **rv)
+{
+ acpi_status status;
+ unsigned long long gsi_base;
+ struct acpi_pci_ioapic *ioapic;
+ struct pci_dev *dev = NULL;
+ struct resource *res = NULL, *pci_res = NULL, *crs_res;
+ char *type = NULL;
+
+ if (!acpi_is_ioapic(handle, &type))
+ return AE_OK;
+
+ mutex_lock(&ioapic_list_lock);
+ list_for_each_entry(ioapic, &ioapic_list, list)
+ if (ioapic->handle == handle) {
+ mutex_unlock(&ioapic_list_lock);
+ return AE_OK;
+ }
+
+ status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsi_base);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_warn(handle, "failed to evaluate _GSB method\n");
+ goto exit;
+ }
+
+ ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
+ if (!ioapic) {
+ pr_err("cannot allocate memory for new IOAPIC\n");
+ goto exit;
+ } else {
+ ioapic->root_handle = (acpi_handle)context;
+ ioapic->handle = handle;
+ ioapic->gsi_base = (u32)gsi_base;
+ INIT_LIST_HEAD(&ioapic->list);
+ }
+
+ if (acpi_ioapic_registered(handle, (u32)gsi_base))
+ goto done;
+
+ dev = acpi_get_pci_dev(handle);
+ if (dev && pci_resource_len(dev, 0)) {
+ if (pci_enable_device(dev) < 0)
+ goto exit_put;
+ pci_set_master(dev);
+ if (pci_request_region(dev, 0, type))
+ goto exit_disable;
+ pci_res = &dev->resource[0];
+ ioapic->pdev = dev;
+ } else {
+ pci_dev_put(dev);
+ dev = NULL;
+ }
+
+ crs_res = &ioapic->res;
+ acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, crs_res);
+ crs_res->name = type;
+ crs_res->flags |= IORESOURCE_BUSY;
+ if (crs_res->flags == 0) {
+ acpi_handle_warn(handle, "failed to get resource\n");
+ goto exit_release;
+ } else if (insert_resource(&iomem_resource, crs_res)) {
+ acpi_handle_warn(handle, "failed to insert resource\n");
+ goto exit_release;
+ }
+
+ /* try pci resource first, then "_CRS" resource */
+ res = pci_res;
+ if (!res || !res->flags)
+ res = crs_res;
+
+ if (acpi_register_ioapic(handle, res->start, (u32)gsi_base)) {
+ acpi_handle_warn(handle, "failed to register IOAPIC\n");
+ goto exit_release;
+ }
+done:
+ list_add(&ioapic->list, &ioapic_list);
+ mutex_unlock(&ioapic_list_lock);
+
+ if (dev)
+ dev_info(&dev->dev, "%s at %pR, GSI %u\n",
+ type, res, (u32)gsi_base);
+ else
+ acpi_handle_info(handle, "%s at %pR, GSI %u\n",
+ type, res, (u32)gsi_base);
+
+ return AE_OK;
+
+exit_release:
+ if (dev)
+ pci_release_region(dev, 0);
+ if (ioapic->res.flags && ioapic->res.parent)
+ release_resource(&ioapic->res);
+exit_disable:
+ if (dev)
+ pci_disable_device(dev);
+exit_put:
+ pci_dev_put(dev);
+ kfree(ioapic);
+exit:
+ mutex_unlock(&ioapic_list_lock);
+ *(acpi_status *)rv = AE_ERROR;
+ return AE_OK;
+}
+
+int acpi_ioapic_add(acpi_handle root_handle)
+{
+ acpi_status status, retval = AE_OK;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root_handle,
+ UINT_MAX, handle_ioapic_add, NULL,
+ root_handle, (void **)&retval);
+
+ return ACPI_SUCCESS(status) && ACPI_SUCCESS(retval) ? 0 : -ENODEV;
+}
+
+void pci_ioapic_remove(struct acpi_pci_root *root)
+{
+ struct acpi_pci_ioapic *ioapic, *tmp;
+
+ mutex_lock(&ioapic_list_lock);
+ list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) {
+ if (root->device->handle != ioapic->root_handle)
+ continue;
+ if (ioapic->pdev) {
+ pci_release_region(ioapic->pdev, 0);
+ pci_disable_device(ioapic->pdev);
+ pci_dev_put(ioapic->pdev);
+ }
+ }
+ mutex_unlock(&ioapic_list_lock);
+}
+
+int acpi_ioapic_remove(struct acpi_pci_root *root)
+{
+ int retval = 0;
+ struct acpi_pci_ioapic *ioapic, *tmp;
+
+ mutex_lock(&ioapic_list_lock);
+ list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) {
+ if (root->device->handle != ioapic->root_handle)
+ continue;
+ if (acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base))
+ retval = -EBUSY;
+ if (ioapic->res.flags && ioapic->res.parent)
+ release_resource(&ioapic->res);
+ list_del(&ioapic->list);
+ kfree(ioapic);
+ }
+ mutex_unlock(&ioapic_list_lock);
+
+ return retval;
+}
diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c
new file mode 100644
index 000000000..1687483ff
--- /dev/null
+++ b/drivers/acpi/irq.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI GSI IRQ layer
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+ */
+#include <linux/acpi.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+
+enum acpi_irq_model_id acpi_irq_model;
+
+static struct fwnode_handle *(*acpi_get_gsi_domain_id)(u32 gsi);
+static u32 (*acpi_gsi_to_irq_fallback)(u32 gsi);
+
+/**
+ * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI
+ * @gsi: GSI IRQ number to map
+ * @irq: pointer where linux IRQ number is stored
+ *
+ * irq location updated with irq value [>0 on success, 0 on failure]
+ *
+ * Returns: 0 on success
+ * -EINVAL on failure
+ */
+int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
+{
+ struct irq_domain *d;
+
+ d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
+ DOMAIN_BUS_ANY);
+ *irq = irq_find_mapping(d, gsi);
+ /*
+ * *irq == 0 means no mapping, that should be reported as a
+ * failure, unless there is an arch-specific fallback handler.
+ */
+ if (!*irq && acpi_gsi_to_irq_fallback)
+ *irq = acpi_gsi_to_irq_fallback(gsi);
+
+ return (*irq > 0) ? 0 : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
+
+/**
+ * acpi_register_gsi() - Map a GSI to a linux IRQ number
+ * @dev: device for which IRQ has to be mapped
+ * @gsi: GSI IRQ number
+ * @trigger: trigger type of the GSI number to be mapped
+ * @polarity: polarity of the GSI to be mapped
+ *
+ * Returns: a valid linux IRQ number on success
+ * -EINVAL on failure
+ */
+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)) {
+ pr_warn("GSI: No registered irqchip, giving up\n");
+ return -EINVAL;
+ }
+
+ fwspec.param[0] = gsi;
+ fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
+ fwspec.param_count = 2;
+
+ irq = irq_create_fwspec_mapping(&fwspec);
+ if (!irq)
+ return -EINVAL;
+
+ return irq;
+}
+EXPORT_SYMBOL_GPL(acpi_register_gsi);
+
+/**
+ * acpi_unregister_gsi() - Free a GSI<->linux IRQ number mapping
+ * @gsi: GSI IRQ number
+ */
+void acpi_unregister_gsi(u32 gsi)
+{
+ struct irq_domain *d;
+ int irq;
+
+ if (WARN_ON(acpi_irq_model == ACPI_IRQ_MODEL_GIC && gsi < 16))
+ return;
+
+ d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
+ DOMAIN_BUS_ANY);
+ irq = irq_find_mapping(d, gsi);
+ irq_dispose_mapping(irq);
+}
+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
+ * source.
+ *
+ * Return:
+ * The referenced device fwhandle or NULL on failure
+ */
+static struct fwnode_handle *
+acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source,
+ u32 gsi)
+{
+ struct fwnode_handle *result;
+ struct acpi_device *device;
+ acpi_handle handle;
+ acpi_status status;
+
+ if (!source->string_length)
+ return acpi_get_gsi_domain_id(gsi);
+
+ status = acpi_get_handle(NULL, source->string_ptr, &handle);
+ if (WARN_ON(ACPI_FAILURE(status)))
+ return NULL;
+
+ device = acpi_get_acpi_dev(handle);
+ if (WARN_ON(!device))
+ return NULL;
+
+ result = &device->fwnode;
+ acpi_put_acpi_dev(device);
+ return result;
+}
+
+/*
+ * Context for the resource walk used to lookup IRQ resources.
+ * Contains a return code, the lookup index, and references to the flags
+ * and fwspec where the result is returned.
+ */
+struct acpi_irq_parse_one_ctx {
+ int rc;
+ unsigned int index;
+ unsigned long *res_flags;
+ struct irq_fwspec *fwspec;
+};
+
+/**
+ * acpi_irq_parse_one_match - Handle a matching IRQ resource.
+ * @fwnode: matching fwnode
+ * @hwirq: hardware IRQ number
+ * @triggering: triggering attributes of hwirq
+ * @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:
+ * Handle a matching IRQ resource by populating the given ctx with
+ * the information passed.
+ */
+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, wake_capable);
+ ctx->fwspec->fwnode = fwnode;
+ ctx->fwspec->param[0] = hwirq;
+ ctx->fwspec->param[1] = acpi_dev_get_irq_type(triggering, polarity);
+ ctx->fwspec->param_count = 2;
+}
+
+/**
+ * acpi_irq_parse_one_cb - Handle the given resource.
+ * @ares: resource to handle
+ * @context: context for the walk
+ *
+ * Description:
+ * This is called by acpi_walk_resources passing each resource returned by
+ * the _CRS method. We only inspect IRQ resources. Since IRQ resources
+ * might contain multiple interrupts we check if the index is within this
+ * one's interrupt array, otherwise we subtract the current resource IRQ
+ * count from the lookup index to prepare for the next resource.
+ * Once a match is found we call acpi_irq_parse_one_match to populate
+ * the result and end the walk by returning AE_CTRL_TERMINATE.
+ *
+ * Return:
+ * AE_OK if the walk should continue, AE_CTRL_TERMINATE if a matching
+ * IRQ resource was found.
+ */
+static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
+ void *context)
+{
+ struct acpi_irq_parse_one_ctx *ctx = context;
+ struct acpi_resource_irq *irq;
+ struct acpi_resource_extended_irq *eirq;
+ struct fwnode_handle *fwnode;
+
+ 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;
+ }
+ 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, irq->wake_capable, ctx);
+ 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;
+ }
+ fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source,
+ eirq->interrupts[ctx->index]);
+ acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index],
+ eirq->triggering, eirq->polarity,
+ eirq->shareable, eirq->wake_capable, ctx);
+ return AE_CTRL_TERMINATE;
+ }
+
+ return AE_OK;
+}
+
+/**
+ * acpi_irq_parse_one - Resolve an interrupt for a device
+ * @handle: the device whose interrupt is to be resolved
+ * @index: index of the interrupt to resolve
+ * @fwspec: structure irq_fwspec filled by this function
+ * @flags: resource flags filled by this function
+ *
+ * Description:
+ * Resolves an interrupt for a device by walking its CRS resources to find
+ * the appropriate ACPI IRQ resource and populating the given struct irq_fwspec
+ * and flags.
+ *
+ * Return:
+ * The result stored in ctx.rc by the callback, or the default -EINVAL value
+ * if an error occurs.
+ */
+static int acpi_irq_parse_one(acpi_handle handle, unsigned int index,
+ struct irq_fwspec *fwspec, unsigned long *flags)
+{
+ struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec };
+
+ acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_parse_one_cb, &ctx);
+ return ctx.rc;
+}
+
+/**
+ * acpi_irq_get - Lookup an ACPI IRQ resource and use it to initialize resource.
+ * @handle: ACPI device handle
+ * @index: ACPI IRQ resource index to lookup
+ * @res: Linux IRQ resource to initialize
+ *
+ * Description:
+ * Look for the ACPI IRQ resource with the given index and use it to initialize
+ * the given Linux IRQ resource.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL if an error occurs
+ * -EPROBE_DEFER if the IRQ lookup/conversion failed
+ */
+int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)
+{
+ struct irq_fwspec fwspec;
+ struct irq_domain *domain;
+ unsigned long flags;
+ int rc;
+
+ rc = acpi_irq_parse_one(handle, index, &fwspec, &flags);
+ if (rc)
+ return rc;
+
+ domain = irq_find_matching_fwnode(fwspec.fwnode, DOMAIN_BUS_ANY);
+ if (!domain)
+ return -EPROBE_DEFER;
+
+ rc = irq_create_fwspec_mapping(&fwspec);
+ if (rc <= 0)
+ return -EINVAL;
+
+ res->start = rc;
+ res->end = rc;
+ res->flags = flags;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_irq_get);
+
+/**
+ * acpi_set_irq_model - Setup the GSI irqdomain information
+ * @model: the value assigned to acpi_irq_model
+ * @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_irq_model = model;
+ acpi_get_gsi_domain_id = fn;
+}
+
+/**
+ * acpi_set_gsi_to_irq_fallback - Register a GSI transfer
+ * callback to fallback to arch specified implementation.
+ * @fn: arch-specific fallback handler
+ */
+void __init acpi_set_gsi_to_irq_fallback(u32 (*fn)(u32))
+{
+ acpi_gsi_to_irq_fallback = fn;
+}
+
+/**
+ * acpi_irq_create_hierarchy - Create a hierarchical IRQ domain with the default
+ * GSI domain as its parent.
+ * @flags: Irq domain flags associated with the domain
+ * @size: Size of the domain.
+ * @fwnode: Optional fwnode of the interrupt controller
+ * @ops: Pointer to the interrupt domain callbacks
+ * @host_data: Controller private data pointer
+ */
+struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
+ unsigned int size,
+ struct fwnode_handle *fwnode,
+ const struct irq_domain_ops *ops,
+ void *host_data)
+{
+ struct irq_domain *d;
+
+ /* This only works for the GIC model... */
+ if (acpi_irq_model != ACPI_IRQ_MODEL_GIC)
+ return NULL;
+
+ d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(0),
+ DOMAIN_BUS_ANY);
+
+ if (!d)
+ return NULL;
+
+ return irq_domain_create_hierarchy(d, flags, size, fwnode, ops,
+ host_data);
+}
+EXPORT_SYMBOL_GPL(acpi_irq_create_hierarchy);
diff --git a/drivers/acpi/nfit/Kconfig b/drivers/acpi/nfit/Kconfig
new file mode 100644
index 000000000..52eefd732
--- /dev/null
+++ b/drivers/acpi/nfit/Kconfig
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0
+config ACPI_NFIT
+ tristate "ACPI NVDIMM Firmware Interface Table (NFIT)"
+ depends on PHYS_ADDR_T_64BIT
+ depends on BLK_DEV
+ depends on ARCH_HAS_PMEM_API
+ select LIBNVDIMM
+ help
+ Infrastructure to probe ACPI 6 compliant platforms for
+ NVDIMMs (NFIT) and register a libnvdimm device tree. In
+ addition to storage devices this also enables libnvdimm to pass
+ ACPI._DSM messages for platform/dimm configuration.
+
+ To compile this driver as a module, choose M here:
+ the module will be called nfit.
+
+config NFIT_SECURITY_DEBUG
+ bool "Enable debug for NVDIMM security commands"
+ depends on ACPI_NFIT
+ help
+ Some NVDIMM devices and controllers support encryption and
+ other security features. The payloads for the commands that
+ enable those features may contain sensitive clear-text
+ security material. Disable debug of those command payloads
+ by default. If you are a kernel developer actively working
+ on NVDIMM security enabling say Y, otherwise say N.
diff --git a/drivers/acpi/nfit/Makefile b/drivers/acpi/nfit/Makefile
new file mode 100644
index 000000000..07f53c4e8
--- /dev/null
+++ b/drivers/acpi/nfit/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ACPI_NFIT) := nfit.o
+nfit-y := core.o
+nfit-y += intel.o
+nfit-$(CONFIG_X86_MCE) += mce.o
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
new file mode 100644
index 000000000..6d4ac934c
--- /dev/null
+++ b/drivers/acpi/nfit/core.c
@@ -0,0 +1,3528 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
+ */
+#include <linux/list_sort.h>
+#include <linux/libnvdimm.h>
+#include <linux/module.h>
+#include <linux/nospec.h>
+#include <linux/mutex.h>
+#include <linux/ndctl.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/acpi.h>
+#include <linux/sort.h>
+#include <linux/io.h>
+#include <linux/nd.h>
+#include <asm/cacheflush.h>
+#include <acpi/nfit.h>
+#include "intel.h"
+#include "nfit.h"
+
+/*
+ * For readq() and writeq() on 32-bit builds, the hi-lo, lo-hi order is
+ * irrelevant.
+ */
+#include <linux/io-64-nonatomic-hi-lo.h>
+
+static bool force_enable_dimms;
+module_param(force_enable_dimms, bool, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(force_enable_dimms, "Ignore _STA (ACPI DIMM device) status");
+
+static bool disable_vendor_specific;
+module_param(disable_vendor_specific, bool, S_IRUGO);
+MODULE_PARM_DESC(disable_vendor_specific,
+ "Limit commands to the publicly specified set");
+
+static unsigned long override_dsm_mask;
+module_param(override_dsm_mask, ulong, S_IRUGO);
+MODULE_PARM_DESC(override_dsm_mask, "Bitmask of allowed NVDIMM DSM functions");
+
+static int default_dsm_family = -1;
+module_param(default_dsm_family, int, S_IRUGO);
+MODULE_PARM_DESC(default_dsm_family,
+ "Try this DSM type first when identifying NVDIMM family");
+
+static bool no_init_ars;
+module_param(no_init_ars, bool, 0644);
+MODULE_PARM_DESC(no_init_ars, "Skip ARS run at nfit init time");
+
+static bool force_labels;
+module_param(force_labels, bool, 0444);
+MODULE_PARM_DESC(force_labels, "Opt-in to labels despite missing methods");
+
+LIST_HEAD(acpi_descs);
+DEFINE_MUTEX(acpi_desc_lock);
+
+static struct workqueue_struct *nfit_wq;
+
+struct nfit_table_prev {
+ struct list_head spas;
+ struct list_head memdevs;
+ struct list_head dcrs;
+ struct list_head bdws;
+ struct list_head idts;
+ struct list_head flushes;
+};
+
+static guid_t nfit_uuid[NFIT_UUID_MAX];
+
+const guid_t *to_nfit_uuid(enum nfit_uuids id)
+{
+ return &nfit_uuid[id];
+}
+EXPORT_SYMBOL(to_nfit_uuid);
+
+static const guid_t *to_nfit_bus_uuid(int family)
+{
+ if (WARN_ONCE(family == NVDIMM_BUS_FAMILY_NFIT,
+ "only secondary bus families can be translated\n"))
+ return NULL;
+ /*
+ * The index of bus UUIDs starts immediately following the last
+ * NVDIMM/leaf family.
+ */
+ return to_nfit_uuid(family + NVDIMM_FAMILY_MAX);
+}
+
+static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc)
+{
+ struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
+
+ /*
+ * If provider == 'ACPI.NFIT' we can assume 'dev' is a struct
+ * acpi_device.
+ */
+ if (!nd_desc->provider_name
+ || strcmp(nd_desc->provider_name, "ACPI.NFIT") != 0)
+ return NULL;
+
+ return to_acpi_device(acpi_desc->dev);
+}
+
+static int xlat_bus_status(void *buf, unsigned int cmd, u32 status)
+{
+ struct nd_cmd_clear_error *clear_err;
+ struct nd_cmd_ars_status *ars_status;
+ u16 flags;
+
+ switch (cmd) {
+ case ND_CMD_ARS_CAP:
+ if ((status & 0xffff) == NFIT_ARS_CAP_NONE)
+ return -ENOTTY;
+
+ /* Command failed */
+ if (status & 0xffff)
+ return -EIO;
+
+ /* No supported scan types for this range */
+ flags = ND_ARS_PERSISTENT | ND_ARS_VOLATILE;
+ if ((status >> 16 & flags) == 0)
+ return -ENOTTY;
+ return 0;
+ case ND_CMD_ARS_START:
+ /* ARS is in progress */
+ if ((status & 0xffff) == NFIT_ARS_START_BUSY)
+ return -EBUSY;
+
+ /* Command failed */
+ if (status & 0xffff)
+ return -EIO;
+ return 0;
+ case ND_CMD_ARS_STATUS:
+ ars_status = buf;
+ /* Command failed */
+ if (status & 0xffff)
+ return -EIO;
+ /* Check extended status (Upper two bytes) */
+ if (status == NFIT_ARS_STATUS_DONE)
+ return 0;
+
+ /* ARS is in progress */
+ if (status == NFIT_ARS_STATUS_BUSY)
+ return -EBUSY;
+
+ /* No ARS performed for the current boot */
+ if (status == NFIT_ARS_STATUS_NONE)
+ return -EAGAIN;
+
+ /*
+ * ARS interrupted, either we overflowed or some other
+ * agent wants the scan to stop. If we didn't overflow
+ * then just continue with the returned results.
+ */
+ if (status == NFIT_ARS_STATUS_INTR) {
+ if (ars_status->out_length >= 40 && (ars_status->flags
+ & NFIT_ARS_F_OVERFLOW))
+ return -ENOSPC;
+ return 0;
+ }
+
+ /* Unknown status */
+ if (status >> 16)
+ return -EIO;
+ return 0;
+ case ND_CMD_CLEAR_ERROR:
+ clear_err = buf;
+ if (status & 0xffff)
+ return -EIO;
+ if (!clear_err->cleared)
+ return -EIO;
+ if (clear_err->length > clear_err->cleared)
+ return clear_err->cleared;
+ return 0;
+ default:
+ break;
+ }
+
+ /* all other non-zero status results in an error */
+ if (status)
+ return -EIO;
+ return 0;
+}
+
+#define ACPI_LABELS_LOCKED 3
+
+static int xlat_nvdimm_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd,
+ u32 status)
+{
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+ switch (cmd) {
+ case ND_CMD_GET_CONFIG_SIZE:
+ /*
+ * In the _LSI, _LSR, _LSW case the locked status is
+ * communicated via the read/write commands
+ */
+ if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags))
+ break;
+
+ if (status >> 16 & ND_CONFIG_LOCKED)
+ return -EACCES;
+ break;
+ case ND_CMD_GET_CONFIG_DATA:
+ if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)
+ && status == ACPI_LABELS_LOCKED)
+ return -EACCES;
+ break;
+ case ND_CMD_SET_CONFIG_DATA:
+ if (test_bit(NFIT_MEM_LSW, &nfit_mem->flags)
+ && status == ACPI_LABELS_LOCKED)
+ return -EACCES;
+ break;
+ default:
+ break;
+ }
+
+ /* all other non-zero status results in an error */
+ if (status)
+ return -EIO;
+ return 0;
+}
+
+static int xlat_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd,
+ u32 status)
+{
+ if (!nvdimm)
+ return xlat_bus_status(buf, cmd, status);
+ return xlat_nvdimm_status(nvdimm, buf, cmd, status);
+}
+
+/* convert _LS{I,R} packages to the buffer object acpi_nfit_ctl expects */
+static union acpi_object *pkg_to_buf(union acpi_object *pkg)
+{
+ int i;
+ void *dst;
+ size_t size = 0;
+ union acpi_object *buf = NULL;
+
+ if (pkg->type != ACPI_TYPE_PACKAGE) {
+ WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n",
+ pkg->type);
+ goto err;
+ }
+
+ for (i = 0; i < pkg->package.count; i++) {
+ union acpi_object *obj = &pkg->package.elements[i];
+
+ if (obj->type == ACPI_TYPE_INTEGER)
+ size += 4;
+ else if (obj->type == ACPI_TYPE_BUFFER)
+ size += obj->buffer.length;
+ else {
+ WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n",
+ obj->type);
+ goto err;
+ }
+ }
+
+ buf = ACPI_ALLOCATE(sizeof(*buf) + size);
+ if (!buf)
+ goto err;
+
+ dst = buf + 1;
+ buf->type = ACPI_TYPE_BUFFER;
+ buf->buffer.length = size;
+ buf->buffer.pointer = dst;
+ for (i = 0; i < pkg->package.count; i++) {
+ union acpi_object *obj = &pkg->package.elements[i];
+
+ if (obj->type == ACPI_TYPE_INTEGER) {
+ memcpy(dst, &obj->integer.value, 4);
+ dst += 4;
+ } else if (obj->type == ACPI_TYPE_BUFFER) {
+ memcpy(dst, obj->buffer.pointer, obj->buffer.length);
+ dst += obj->buffer.length;
+ }
+ }
+err:
+ ACPI_FREE(pkg);
+ return buf;
+}
+
+static union acpi_object *int_to_buf(union acpi_object *integer)
+{
+ union acpi_object *buf = NULL;
+ void *dst = NULL;
+
+ if (integer->type != ACPI_TYPE_INTEGER) {
+ WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n",
+ integer->type);
+ goto err;
+ }
+
+ buf = ACPI_ALLOCATE(sizeof(*buf) + 4);
+ if (!buf)
+ goto err;
+
+ dst = buf + 1;
+ buf->type = ACPI_TYPE_BUFFER;
+ buf->buffer.length = 4;
+ buf->buffer.pointer = dst;
+ memcpy(dst, &integer->integer.value, 4);
+err:
+ ACPI_FREE(integer);
+ return buf;
+}
+
+static union acpi_object *acpi_label_write(acpi_handle handle, u32 offset,
+ u32 len, void *data)
+{
+ acpi_status rc;
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_object_list input = {
+ .count = 3,
+ .pointer = (union acpi_object []) {
+ [0] = {
+ .integer.type = ACPI_TYPE_INTEGER,
+ .integer.value = offset,
+ },
+ [1] = {
+ .integer.type = ACPI_TYPE_INTEGER,
+ .integer.value = len,
+ },
+ [2] = {
+ .buffer.type = ACPI_TYPE_BUFFER,
+ .buffer.pointer = data,
+ .buffer.length = len,
+ },
+ },
+ };
+
+ rc = acpi_evaluate_object(handle, "_LSW", &input, &buf);
+ if (ACPI_FAILURE(rc))
+ return NULL;
+ return int_to_buf(buf.pointer);
+}
+
+static union acpi_object *acpi_label_read(acpi_handle handle, u32 offset,
+ u32 len)
+{
+ acpi_status rc;
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_object_list input = {
+ .count = 2,
+ .pointer = (union acpi_object []) {
+ [0] = {
+ .integer.type = ACPI_TYPE_INTEGER,
+ .integer.value = offset,
+ },
+ [1] = {
+ .integer.type = ACPI_TYPE_INTEGER,
+ .integer.value = len,
+ },
+ },
+ };
+
+ rc = acpi_evaluate_object(handle, "_LSR", &input, &buf);
+ if (ACPI_FAILURE(rc))
+ return NULL;
+ return pkg_to_buf(buf.pointer);
+}
+
+static union acpi_object *acpi_label_info(acpi_handle handle)
+{
+ acpi_status rc;
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ rc = acpi_evaluate_object(handle, "_LSI", NULL, &buf);
+ if (ACPI_FAILURE(rc))
+ return NULL;
+ return pkg_to_buf(buf.pointer);
+}
+
+static u8 nfit_dsm_revid(unsigned family, unsigned func)
+{
+ static const u8 revid_table[NVDIMM_FAMILY_MAX+1][NVDIMM_CMD_MAX+1] = {
+ [NVDIMM_FAMILY_INTEL] = {
+ [NVDIMM_INTEL_GET_MODES ...
+ NVDIMM_INTEL_FW_ACTIVATE_ARM] = 2,
+ },
+ };
+ u8 id;
+
+ if (family > NVDIMM_FAMILY_MAX)
+ return 0;
+ if (func > NVDIMM_CMD_MAX)
+ return 0;
+ id = revid_table[family][func];
+ if (id == 0)
+ return 1; /* default */
+ return id;
+}
+
+static bool payload_dumpable(struct nvdimm *nvdimm, unsigned int func)
+{
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+ if (nfit_mem && nfit_mem->family == NVDIMM_FAMILY_INTEL
+ && func >= NVDIMM_INTEL_GET_SECURITY_STATE
+ && func <= NVDIMM_INTEL_MASTER_SECURE_ERASE)
+ return IS_ENABLED(CONFIG_NFIT_SECURITY_DEBUG);
+ return true;
+}
+
+static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd,
+ struct nd_cmd_pkg *call_pkg, int *family)
+{
+ if (call_pkg) {
+ int i;
+
+ if (nfit_mem && nfit_mem->family != call_pkg->nd_family)
+ return -ENOTTY;
+
+ for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++)
+ if (call_pkg->nd_reserved2[i])
+ return -EINVAL;
+ *family = call_pkg->nd_family;
+ return call_pkg->nd_command;
+ }
+
+ /* In the !call_pkg case, bus commands == bus functions */
+ if (!nfit_mem)
+ return cmd;
+
+ /* Linux ND commands == NVDIMM_FAMILY_INTEL function numbers */
+ if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
+ return cmd;
+
+ /*
+ * Force function number validation to fail since 0 is never
+ * published as a valid function in dsm_mask.
+ */
+ return 0;
+}
+
+int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
+ unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
+{
+ struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ union acpi_object in_obj, in_buf, *out_obj;
+ const struct nd_cmd_desc *desc = NULL;
+ struct device *dev = acpi_desc->dev;
+ struct nd_cmd_pkg *call_pkg = NULL;
+ const char *cmd_name, *dimm_name;
+ unsigned long cmd_mask, dsm_mask;
+ u32 offset, fw_status = 0;
+ acpi_handle handle;
+ const guid_t *guid;
+ int func, rc, i;
+ int family = 0;
+
+ if (cmd_rc)
+ *cmd_rc = -EINVAL;
+
+ if (cmd == ND_CMD_CALL)
+ call_pkg = buf;
+ func = cmd_to_func(nfit_mem, cmd, call_pkg, &family);
+ if (func < 0)
+ return func;
+
+ if (nvdimm) {
+ struct acpi_device *adev = nfit_mem->adev;
+
+ if (!adev)
+ return -ENOTTY;
+
+ dimm_name = nvdimm_name(nvdimm);
+ cmd_name = nvdimm_cmd_name(cmd);
+ cmd_mask = nvdimm_cmd_mask(nvdimm);
+ dsm_mask = nfit_mem->dsm_mask;
+ desc = nd_cmd_dimm_desc(cmd);
+ guid = to_nfit_uuid(nfit_mem->family);
+ handle = adev->handle;
+ } else {
+ struct acpi_device *adev = to_acpi_dev(acpi_desc);
+
+ cmd_name = nvdimm_bus_cmd_name(cmd);
+ 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 ||
+ !test_bit(family, &nd_desc->bus_family_mask))
+ return -EINVAL;
+ family = array_index_nospec(family,
+ NVDIMM_BUS_FAMILY_MAX + 1);
+ dsm_mask = acpi_desc->family_dsm_mask[family];
+ guid = to_nfit_bus_uuid(family);
+ } else {
+ dsm_mask = acpi_desc->bus_dsm_mask;
+ guid = to_nfit_uuid(NFIT_DEV_BUS);
+ }
+ desc = nd_cmd_bus_desc(cmd);
+ handle = adev->handle;
+ dimm_name = "bus";
+ }
+
+ if (!desc || (cmd && (desc->out_num + desc->in_num == 0)))
+ return -ENOTTY;
+
+ /*
+ * Check for a valid command. For ND_CMD_CALL, we also have to
+ * make sure that the DSM function is supported.
+ */
+ if (cmd == ND_CMD_CALL &&
+ (func > NVDIMM_CMD_MAX || !test_bit(func, &dsm_mask)))
+ return -ENOTTY;
+ else if (!test_bit(cmd, &cmd_mask))
+ return -ENOTTY;
+
+ in_obj.type = ACPI_TYPE_PACKAGE;
+ in_obj.package.count = 1;
+ in_obj.package.elements = &in_buf;
+ in_buf.type = ACPI_TYPE_BUFFER;
+ in_buf.buffer.pointer = buf;
+ in_buf.buffer.length = 0;
+
+ /* libnvdimm has already validated the input envelope */
+ for (i = 0; i < desc->in_num; i++)
+ in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc,
+ i, buf);
+
+ if (call_pkg) {
+ /* skip over package wrapper */
+ in_buf.buffer.pointer = (void *) &call_pkg->nd_payload;
+ in_buf.buffer.length = call_pkg->nd_size_in;
+ }
+
+ dev_dbg(dev, "%s cmd: %d: family: %d func: %d input length: %d\n",
+ dimm_name, cmd, family, func, in_buf.buffer.length);
+ if (payload_dumpable(nvdimm, func))
+ print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4,
+ in_buf.buffer.pointer,
+ min_t(u32, 256, in_buf.buffer.length), true);
+
+ /* call the BIOS, prefer the named methods over _DSM if available */
+ if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE
+ && test_bit(NFIT_MEM_LSR, &nfit_mem->flags))
+ out_obj = acpi_label_info(handle);
+ else if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA
+ && test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) {
+ struct nd_cmd_get_config_data_hdr *p = buf;
+
+ out_obj = acpi_label_read(handle, p->in_offset, p->in_length);
+ } else if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA
+ && test_bit(NFIT_MEM_LSW, &nfit_mem->flags)) {
+ struct nd_cmd_set_config_hdr *p = buf;
+
+ out_obj = acpi_label_write(handle, p->in_offset, p->in_length,
+ p->in_buf);
+ } else {
+ u8 revid;
+
+ if (nvdimm)
+ revid = nfit_dsm_revid(nfit_mem->family, func);
+ else
+ revid = 1;
+ out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj);
+ }
+
+ if (!out_obj) {
+ dev_dbg(dev, "%s _DSM failed cmd: %s\n", dimm_name, cmd_name);
+ return -EINVAL;
+ }
+
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
+ dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n",
+ dimm_name, cmd_name, out_obj->type);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(dev, "%s cmd: %s output length: %d\n", dimm_name,
+ cmd_name, out_obj->buffer.length);
+ print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, 4,
+ out_obj->buffer.pointer,
+ min_t(u32, 128, out_obj->buffer.length), true);
+
+ if (call_pkg) {
+ call_pkg->nd_fw_size = out_obj->buffer.length;
+ memcpy(call_pkg->nd_payload + call_pkg->nd_size_in,
+ out_obj->buffer.pointer,
+ min(call_pkg->nd_fw_size, call_pkg->nd_size_out));
+
+ ACPI_FREE(out_obj);
+ /*
+ * Need to support FW function w/o known size in advance.
+ * Caller can determine required size based upon nd_fw_size.
+ * If we return an error (like elsewhere) then caller wouldn't
+ * be able to rely upon data returned to make calculation.
+ */
+ if (cmd_rc)
+ *cmd_rc = 0;
+ return 0;
+ }
+
+ for (i = 0, offset = 0; i < desc->out_num; i++) {
+ u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, buf,
+ (u32 *) out_obj->buffer.pointer,
+ out_obj->buffer.length - offset);
+
+ if (offset + out_size > out_obj->buffer.length) {
+ dev_dbg(dev, "%s output object underflow cmd: %s field: %d\n",
+ dimm_name, cmd_name, i);
+ break;
+ }
+
+ if (in_buf.buffer.length + offset + out_size > buf_len) {
+ dev_dbg(dev, "%s output overrun cmd: %s field: %d\n",
+ dimm_name, cmd_name, i);
+ rc = -ENXIO;
+ goto out;
+ }
+ memcpy(buf + in_buf.buffer.length + offset,
+ out_obj->buffer.pointer + offset, out_size);
+ offset += out_size;
+ }
+
+ /*
+ * Set fw_status for all the commands with a known format to be
+ * later interpreted by xlat_status().
+ */
+ if (i >= 1 && ((!nvdimm && cmd >= ND_CMD_ARS_CAP
+ && cmd <= ND_CMD_CLEAR_ERROR)
+ || (nvdimm && cmd >= ND_CMD_SMART
+ && cmd <= ND_CMD_VENDOR)))
+ fw_status = *(u32 *) out_obj->buffer.pointer;
+
+ if (offset + in_buf.buffer.length < buf_len) {
+ if (i >= 1) {
+ /*
+ * status valid, return the number of bytes left
+ * unfilled in the output buffer
+ */
+ rc = buf_len - offset - in_buf.buffer.length;
+ if (cmd_rc)
+ *cmd_rc = xlat_status(nvdimm, buf, cmd,
+ fw_status);
+ } else {
+ dev_err(dev, "%s:%s underrun cmd: %s buf_len: %d out_len: %d\n",
+ __func__, dimm_name, cmd_name, buf_len,
+ offset);
+ rc = -ENXIO;
+ }
+ } else {
+ rc = 0;
+ if (cmd_rc)
+ *cmd_rc = xlat_status(nvdimm, buf, cmd, fw_status);
+ }
+
+ out:
+ ACPI_FREE(out_obj);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(acpi_nfit_ctl);
+
+static const char *spa_type_name(u16 type)
+{
+ static const char *to_name[] = {
+ [NFIT_SPA_VOLATILE] = "volatile",
+ [NFIT_SPA_PM] = "pmem",
+ [NFIT_SPA_DCR] = "dimm-control-region",
+ [NFIT_SPA_BDW] = "block-data-window",
+ [NFIT_SPA_VDISK] = "volatile-disk",
+ [NFIT_SPA_VCD] = "volatile-cd",
+ [NFIT_SPA_PDISK] = "persistent-disk",
+ [NFIT_SPA_PCD] = "persistent-cd",
+
+ };
+
+ if (type > NFIT_SPA_PCD)
+ return "unknown";
+
+ return to_name[type];
+}
+
+int nfit_spa_type(struct acpi_nfit_system_address *spa)
+{
+ guid_t guid;
+ int i;
+
+ import_guid(&guid, spa->range_guid);
+ for (i = 0; i < NFIT_UUID_MAX; i++)
+ if (guid_equal(to_nfit_uuid(i), &guid))
+ return i;
+ return -1;
+}
+
+static size_t sizeof_spa(struct acpi_nfit_system_address *spa)
+{
+ if (spa->flags & ACPI_NFIT_LOCATION_COOKIE_VALID)
+ return sizeof(*spa);
+ return sizeof(*spa) - 8;
+}
+
+static bool add_spa(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_table_prev *prev,
+ struct acpi_nfit_system_address *spa)
+{
+ struct device *dev = acpi_desc->dev;
+ struct nfit_spa *nfit_spa;
+
+ if (spa->header.length != sizeof_spa(spa))
+ return false;
+
+ list_for_each_entry(nfit_spa, &prev->spas, list) {
+ if (memcmp(nfit_spa->spa, spa, sizeof_spa(spa)) == 0) {
+ list_move_tail(&nfit_spa->list, &acpi_desc->spas);
+ return true;
+ }
+ }
+
+ nfit_spa = devm_kzalloc(dev, sizeof(*nfit_spa) + sizeof_spa(spa),
+ GFP_KERNEL);
+ if (!nfit_spa)
+ return false;
+ INIT_LIST_HEAD(&nfit_spa->list);
+ memcpy(nfit_spa->spa, spa, sizeof_spa(spa));
+ list_add_tail(&nfit_spa->list, &acpi_desc->spas);
+ dev_dbg(dev, "spa index: %d type: %s\n",
+ spa->range_index,
+ spa_type_name(nfit_spa_type(spa)));
+ return true;
+}
+
+static bool add_memdev(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_table_prev *prev,
+ struct acpi_nfit_memory_map *memdev)
+{
+ struct device *dev = acpi_desc->dev;
+ struct nfit_memdev *nfit_memdev;
+
+ if (memdev->header.length != sizeof(*memdev))
+ return false;
+
+ list_for_each_entry(nfit_memdev, &prev->memdevs, list)
+ if (memcmp(nfit_memdev->memdev, memdev, sizeof(*memdev)) == 0) {
+ list_move_tail(&nfit_memdev->list, &acpi_desc->memdevs);
+ return true;
+ }
+
+ nfit_memdev = devm_kzalloc(dev, sizeof(*nfit_memdev) + sizeof(*memdev),
+ GFP_KERNEL);
+ if (!nfit_memdev)
+ return false;
+ INIT_LIST_HEAD(&nfit_memdev->list);
+ memcpy(nfit_memdev->memdev, memdev, sizeof(*memdev));
+ list_add_tail(&nfit_memdev->list, &acpi_desc->memdevs);
+ dev_dbg(dev, "memdev handle: %#x spa: %d dcr: %d flags: %#x\n",
+ memdev->device_handle, memdev->range_index,
+ memdev->region_index, memdev->flags);
+ return true;
+}
+
+int nfit_get_smbios_id(u32 device_handle, u16 *flags)
+{
+ struct acpi_nfit_memory_map *memdev;
+ struct acpi_nfit_desc *acpi_desc;
+ struct nfit_mem *nfit_mem;
+ u16 physical_id;
+
+ mutex_lock(&acpi_desc_lock);
+ list_for_each_entry(acpi_desc, &acpi_descs, list) {
+ mutex_lock(&acpi_desc->init_mutex);
+ list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
+ memdev = __to_nfit_memdev(nfit_mem);
+ if (memdev->device_handle == device_handle) {
+ *flags = memdev->flags;
+ physical_id = memdev->physical_id;
+ mutex_unlock(&acpi_desc->init_mutex);
+ mutex_unlock(&acpi_desc_lock);
+ return physical_id;
+ }
+ }
+ mutex_unlock(&acpi_desc->init_mutex);
+ }
+ mutex_unlock(&acpi_desc_lock);
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(nfit_get_smbios_id);
+
+/*
+ * An implementation may provide a truncated control region if no block windows
+ * are defined.
+ */
+static size_t sizeof_dcr(struct acpi_nfit_control_region *dcr)
+{
+ if (dcr->header.length < offsetof(struct acpi_nfit_control_region,
+ window_size))
+ return 0;
+ if (dcr->windows)
+ return sizeof(*dcr);
+ return offsetof(struct acpi_nfit_control_region, window_size);
+}
+
+static bool add_dcr(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_table_prev *prev,
+ struct acpi_nfit_control_region *dcr)
+{
+ struct device *dev = acpi_desc->dev;
+ struct nfit_dcr *nfit_dcr;
+
+ if (!sizeof_dcr(dcr))
+ return false;
+
+ list_for_each_entry(nfit_dcr, &prev->dcrs, list)
+ if (memcmp(nfit_dcr->dcr, dcr, sizeof_dcr(dcr)) == 0) {
+ list_move_tail(&nfit_dcr->list, &acpi_desc->dcrs);
+ return true;
+ }
+
+ nfit_dcr = devm_kzalloc(dev, sizeof(*nfit_dcr) + sizeof(*dcr),
+ GFP_KERNEL);
+ if (!nfit_dcr)
+ return false;
+ INIT_LIST_HEAD(&nfit_dcr->list);
+ memcpy(nfit_dcr->dcr, dcr, sizeof_dcr(dcr));
+ list_add_tail(&nfit_dcr->list, &acpi_desc->dcrs);
+ dev_dbg(dev, "dcr index: %d windows: %d\n",
+ dcr->region_index, dcr->windows);
+ return true;
+}
+
+static bool add_bdw(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_table_prev *prev,
+ struct acpi_nfit_data_region *bdw)
+{
+ struct device *dev = acpi_desc->dev;
+ struct nfit_bdw *nfit_bdw;
+
+ if (bdw->header.length != sizeof(*bdw))
+ return false;
+ list_for_each_entry(nfit_bdw, &prev->bdws, list)
+ if (memcmp(nfit_bdw->bdw, bdw, sizeof(*bdw)) == 0) {
+ list_move_tail(&nfit_bdw->list, &acpi_desc->bdws);
+ return true;
+ }
+
+ nfit_bdw = devm_kzalloc(dev, sizeof(*nfit_bdw) + sizeof(*bdw),
+ GFP_KERNEL);
+ if (!nfit_bdw)
+ return false;
+ INIT_LIST_HEAD(&nfit_bdw->list);
+ memcpy(nfit_bdw->bdw, bdw, sizeof(*bdw));
+ list_add_tail(&nfit_bdw->list, &acpi_desc->bdws);
+ dev_dbg(dev, "bdw dcr: %d windows: %d\n",
+ bdw->region_index, bdw->windows);
+ return true;
+}
+
+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);
+}
+
+static bool add_idt(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_table_prev *prev,
+ struct acpi_nfit_interleave *idt)
+{
+ struct device *dev = acpi_desc->dev;
+ struct nfit_idt *nfit_idt;
+
+ if (!sizeof_idt(idt))
+ return false;
+
+ list_for_each_entry(nfit_idt, &prev->idts, list) {
+ if (sizeof_idt(nfit_idt->idt) != sizeof_idt(idt))
+ continue;
+
+ if (memcmp(nfit_idt->idt, idt, sizeof_idt(idt)) == 0) {
+ list_move_tail(&nfit_idt->list, &acpi_desc->idts);
+ return true;
+ }
+ }
+
+ nfit_idt = devm_kzalloc(dev, sizeof(*nfit_idt) + sizeof_idt(idt),
+ GFP_KERNEL);
+ if (!nfit_idt)
+ return false;
+ INIT_LIST_HEAD(&nfit_idt->list);
+ memcpy(nfit_idt->idt, idt, sizeof_idt(idt));
+ list_add_tail(&nfit_idt->list, &acpi_desc->idts);
+ dev_dbg(dev, "idt index: %d num_lines: %d\n",
+ idt->interleave_index, idt->line_count);
+ return true;
+}
+
+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);
+}
+
+static bool add_flush(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_table_prev *prev,
+ struct acpi_nfit_flush_address *flush)
+{
+ struct device *dev = acpi_desc->dev;
+ struct nfit_flush *nfit_flush;
+
+ if (!sizeof_flush(flush))
+ return false;
+
+ list_for_each_entry(nfit_flush, &prev->flushes, list) {
+ if (sizeof_flush(nfit_flush->flush) != sizeof_flush(flush))
+ continue;
+
+ if (memcmp(nfit_flush->flush, flush,
+ sizeof_flush(flush)) == 0) {
+ list_move_tail(&nfit_flush->list, &acpi_desc->flushes);
+ return true;
+ }
+ }
+
+ nfit_flush = devm_kzalloc(dev, sizeof(*nfit_flush)
+ + sizeof_flush(flush), GFP_KERNEL);
+ if (!nfit_flush)
+ return false;
+ INIT_LIST_HEAD(&nfit_flush->list);
+ memcpy(nfit_flush->flush, flush, sizeof_flush(flush));
+ list_add_tail(&nfit_flush->list, &acpi_desc->flushes);
+ dev_dbg(dev, "nfit_flush handle: %d hint_count: %d\n",
+ flush->device_handle, flush->hint_count);
+ return true;
+}
+
+static bool add_platform_cap(struct acpi_nfit_desc *acpi_desc,
+ struct acpi_nfit_capabilities *pcap)
+{
+ struct device *dev = acpi_desc->dev;
+ u32 mask;
+
+ mask = (1 << (pcap->highest_capability + 1)) - 1;
+ acpi_desc->platform_cap = pcap->capabilities & mask;
+ dev_dbg(dev, "cap: %#x\n", acpi_desc->platform_cap);
+ return true;
+}
+
+static void *add_table(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_table_prev *prev, void *table, const void *end)
+{
+ struct device *dev = acpi_desc->dev;
+ struct acpi_nfit_header *hdr;
+ void *err = ERR_PTR(-ENOMEM);
+
+ if (table >= end)
+ return NULL;
+
+ hdr = table;
+ if (!hdr->length) {
+ dev_warn(dev, "found a zero length table '%d' parsing nfit\n",
+ hdr->type);
+ return NULL;
+ }
+
+ switch (hdr->type) {
+ case ACPI_NFIT_TYPE_SYSTEM_ADDRESS:
+ if (!add_spa(acpi_desc, prev, table))
+ return err;
+ break;
+ case ACPI_NFIT_TYPE_MEMORY_MAP:
+ if (!add_memdev(acpi_desc, prev, table))
+ return err;
+ break;
+ case ACPI_NFIT_TYPE_CONTROL_REGION:
+ if (!add_dcr(acpi_desc, prev, table))
+ return err;
+ break;
+ case ACPI_NFIT_TYPE_DATA_REGION:
+ if (!add_bdw(acpi_desc, prev, table))
+ return err;
+ break;
+ case ACPI_NFIT_TYPE_INTERLEAVE:
+ if (!add_idt(acpi_desc, prev, table))
+ return err;
+ break;
+ case ACPI_NFIT_TYPE_FLUSH_ADDRESS:
+ if (!add_flush(acpi_desc, prev, table))
+ return err;
+ break;
+ case ACPI_NFIT_TYPE_SMBIOS:
+ dev_dbg(dev, "smbios\n");
+ break;
+ case ACPI_NFIT_TYPE_CAPABILITIES:
+ if (!add_platform_cap(acpi_desc, table))
+ return err;
+ break;
+ default:
+ dev_err(dev, "unknown table '%d' parsing nfit\n", hdr->type);
+ break;
+ }
+
+ return table + hdr->length;
+}
+
+static int __nfit_mem_init(struct acpi_nfit_desc *acpi_desc,
+ struct acpi_nfit_system_address *spa)
+{
+ struct nfit_mem *nfit_mem, *found;
+ struct nfit_memdev *nfit_memdev;
+ int type = spa ? nfit_spa_type(spa) : 0;
+
+ switch (type) {
+ case NFIT_SPA_DCR:
+ case NFIT_SPA_PM:
+ break;
+ default:
+ if (spa)
+ return 0;
+ }
+
+ /*
+ * This loop runs in two modes, when a dimm is mapped the loop
+ * adds memdev associations to an existing dimm, or creates a
+ * dimm. In the unmapped dimm case this loop sweeps for memdev
+ * instances with an invalid / zero range_index and adds those
+ * dimms without spa associations.
+ */
+ list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
+ struct nfit_flush *nfit_flush;
+ struct nfit_dcr *nfit_dcr;
+ u32 device_handle;
+ u16 dcr;
+
+ if (spa && nfit_memdev->memdev->range_index != spa->range_index)
+ continue;
+ if (!spa && nfit_memdev->memdev->range_index)
+ continue;
+ found = NULL;
+ dcr = nfit_memdev->memdev->region_index;
+ device_handle = nfit_memdev->memdev->device_handle;
+ list_for_each_entry(nfit_mem, &acpi_desc->dimms, list)
+ if (__to_nfit_memdev(nfit_mem)->device_handle
+ == device_handle) {
+ found = nfit_mem;
+ break;
+ }
+
+ if (found)
+ nfit_mem = found;
+ else {
+ nfit_mem = devm_kzalloc(acpi_desc->dev,
+ sizeof(*nfit_mem), GFP_KERNEL);
+ if (!nfit_mem)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&nfit_mem->list);
+ nfit_mem->acpi_desc = acpi_desc;
+ list_add(&nfit_mem->list, &acpi_desc->dimms);
+ }
+
+ list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) {
+ if (nfit_dcr->dcr->region_index != dcr)
+ continue;
+ /*
+ * Record the control region for the dimm. For
+ * the ACPI 6.1 case, where there are separate
+ * control regions for the pmem vs blk
+ * interfaces, be sure to record the extended
+ * blk details.
+ */
+ if (!nfit_mem->dcr)
+ nfit_mem->dcr = nfit_dcr->dcr;
+ else if (nfit_mem->dcr->windows == 0
+ && nfit_dcr->dcr->windows)
+ nfit_mem->dcr = nfit_dcr->dcr;
+ break;
+ }
+
+ list_for_each_entry(nfit_flush, &acpi_desc->flushes, list) {
+ struct acpi_nfit_flush_address *flush;
+ u16 i;
+
+ if (nfit_flush->flush->device_handle != device_handle)
+ continue;
+ nfit_mem->nfit_flush = nfit_flush;
+ flush = nfit_flush->flush;
+ nfit_mem->flush_wpq = devm_kcalloc(acpi_desc->dev,
+ flush->hint_count,
+ sizeof(struct resource),
+ GFP_KERNEL);
+ if (!nfit_mem->flush_wpq)
+ return -ENOMEM;
+ for (i = 0; i < flush->hint_count; i++) {
+ struct resource *res = &nfit_mem->flush_wpq[i];
+
+ res->start = flush->hint_address[i];
+ res->end = res->start + 8 - 1;
+ }
+ break;
+ }
+
+ if (dcr && !nfit_mem->dcr) {
+ dev_err(acpi_desc->dev, "SPA %d missing DCR %d\n",
+ spa->range_index, dcr);
+ return -ENODEV;
+ }
+
+ if (type == NFIT_SPA_DCR) {
+ struct nfit_idt *nfit_idt;
+ u16 idt_idx;
+
+ /* multiple dimms may share a SPA when interleaved */
+ nfit_mem->spa_dcr = spa;
+ nfit_mem->memdev_dcr = nfit_memdev->memdev;
+ idt_idx = nfit_memdev->memdev->interleave_index;
+ list_for_each_entry(nfit_idt, &acpi_desc->idts, list) {
+ if (nfit_idt->idt->interleave_index != idt_idx)
+ continue;
+ nfit_mem->idt_dcr = nfit_idt->idt;
+ break;
+ }
+ } else if (type == NFIT_SPA_PM) {
+ /*
+ * A single dimm may belong to multiple SPA-PM
+ * ranges, record at least one in addition to
+ * any SPA-DCR range.
+ */
+ nfit_mem->memdev_pmem = nfit_memdev->memdev;
+ } else
+ nfit_mem->memdev_dcr = nfit_memdev->memdev;
+ }
+
+ return 0;
+}
+
+static int nfit_mem_cmp(void *priv, const struct list_head *_a,
+ const struct list_head *_b)
+{
+ struct nfit_mem *a = container_of(_a, typeof(*a), list);
+ struct nfit_mem *b = container_of(_b, typeof(*b), list);
+ u32 handleA, handleB;
+
+ handleA = __to_nfit_memdev(a)->device_handle;
+ handleB = __to_nfit_memdev(b)->device_handle;
+ if (handleA < handleB)
+ return -1;
+ else if (handleA > handleB)
+ return 1;
+ return 0;
+}
+
+static int nfit_mem_init(struct acpi_nfit_desc *acpi_desc)
+{
+ struct nfit_spa *nfit_spa;
+ int rc;
+
+
+ /*
+ * For each SPA-DCR or SPA-PMEM address range find its
+ * corresponding MEMDEV(s). From each MEMDEV find the
+ * corresponding DCR. Then, if we're operating on a SPA-DCR,
+ * try to find a SPA-BDW and a corresponding BDW that references
+ * the DCR. Throw it all into an nfit_mem object. Note, that
+ * BDWs are optional.
+ */
+ list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
+ rc = __nfit_mem_init(acpi_desc, nfit_spa->spa);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * If a DIMM has failed to be mapped into SPA there will be no
+ * SPA entries above. Find and register all the unmapped DIMMs
+ * for reporting and recovery purposes.
+ */
+ rc = __nfit_mem_init(acpi_desc, NULL);
+ if (rc)
+ return rc;
+
+ list_sort(NULL, &acpi_desc->dimms, nfit_mem_cmp);
+
+ return 0;
+}
+
+static ssize_t bus_dsm_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(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);
+}
+static struct device_attribute dev_attr_bus_dsm_mask =
+ __ATTR(dsm_mask, 0444, bus_dsm_mask_show, NULL);
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(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);
+}
+static DEVICE_ATTR_RO(revision);
+
+static ssize_t hw_error_scrub_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(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);
+}
+
+/*
+ * The 'hw_error_scrub' attribute can have the following values written to it:
+ * '0': Switch to the default mode where an exception will only insert
+ * the address of the memory error into the poison and badblocks lists.
+ * '1': Enable a full scrub to happen if an exception for a memory error is
+ * received.
+ */
+static ssize_t hw_error_scrub_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct nvdimm_bus_descriptor *nd_desc;
+ ssize_t rc;
+ long val;
+
+ rc = kstrtol(buf, 0, &val);
+ if (rc)
+ return rc;
+
+ device_lock(dev);
+ nd_desc = dev_get_drvdata(dev);
+ if (nd_desc) {
+ struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+
+ switch (val) {
+ case HW_ERROR_SCRUB_ON:
+ acpi_desc->scrub_mode = HW_ERROR_SCRUB_ON;
+ break;
+ case HW_ERROR_SCRUB_OFF:
+ acpi_desc->scrub_mode = HW_ERROR_SCRUB_OFF;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ }
+ device_unlock(dev);
+ if (rc)
+ return rc;
+ return size;
+}
+static DEVICE_ATTR_RW(hw_error_scrub);
+
+/*
+ * This shows the number of full Address Range Scrubs that have been
+ * completed since driver load time. Userspace can wait on this using
+ * select/poll etc. A '+' at the end indicates an ARS is in progress
+ */
+static ssize_t scrub_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm_bus_descriptor *nd_desc;
+ struct acpi_nfit_desc *acpi_desc;
+ ssize_t rc = -ENXIO;
+ bool busy;
+
+ device_lock(dev);
+ nd_desc = dev_get_drvdata(dev);
+ if (!nd_desc) {
+ device_unlock(dev);
+ return rc;
+ }
+ acpi_desc = to_acpi_desc(nd_desc);
+
+ 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");
+ /* 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)) {
+ acpi_desc->scrub_tmo = 1;
+ mod_delayed_work(nfit_wq, &acpi_desc->dwork, HZ);
+ }
+
+ mutex_unlock(&acpi_desc->init_mutex);
+ device_unlock(dev);
+ return rc;
+}
+
+static ssize_t scrub_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct nvdimm_bus_descriptor *nd_desc;
+ ssize_t rc;
+ long val;
+
+ rc = kstrtol(buf, 0, &val);
+ if (rc)
+ return rc;
+ if (val != 1)
+ return -EINVAL;
+
+ device_lock(dev);
+ nd_desc = dev_get_drvdata(dev);
+ if (nd_desc) {
+ struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+
+ rc = acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG);
+ }
+ device_unlock(dev);
+ if (rc)
+ return rc;
+ return size;
+}
+static DEVICE_ATTR_RW(scrub);
+
+static bool ars_supported(struct nvdimm_bus *nvdimm_bus)
+{
+ struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
+ const unsigned long mask = 1 << ND_CMD_ARS_CAP | 1 << ND_CMD_ARS_START
+ | 1 << ND_CMD_ARS_STATUS;
+
+ return (nd_desc->cmd_mask & mask) == mask;
+}
+
+static umode_t nfit_visible(struct kobject *kobj, struct attribute *a, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
+
+ if (a == &dev_attr_scrub.attr)
+ return ars_supported(nvdimm_bus) ? a->mode : 0;
+
+ if (a == &dev_attr_firmware_activate_noidle.attr)
+ return intel_fwa_supported(nvdimm_bus) ? a->mode : 0;
+
+ return a->mode;
+}
+
+static struct attribute *acpi_nfit_attributes[] = {
+ &dev_attr_revision.attr,
+ &dev_attr_scrub.attr,
+ &dev_attr_hw_error_scrub.attr,
+ &dev_attr_bus_dsm_mask.attr,
+ &dev_attr_firmware_activate_noidle.attr,
+ NULL,
+};
+
+static const struct attribute_group acpi_nfit_attribute_group = {
+ .name = "nfit",
+ .attrs = acpi_nfit_attributes,
+ .is_visible = nfit_visible,
+};
+
+static const struct attribute_group *acpi_nfit_attribute_groups[] = {
+ &acpi_nfit_attribute_group,
+ NULL,
+};
+
+static struct acpi_nfit_memory_map *to_nfit_memdev(struct device *dev)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+ return __to_nfit_memdev(nfit_mem);
+}
+
+static struct acpi_nfit_control_region *to_nfit_dcr(struct device *dev)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+ return nfit_mem->dcr;
+}
+
+static ssize_t handle_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_nfit_memory_map *memdev = to_nfit_memdev(dev);
+
+ return sprintf(buf, "%#x\n", memdev->device_handle);
+}
+static DEVICE_ATTR_RO(handle);
+
+static ssize_t phys_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_nfit_memory_map *memdev = to_nfit_memdev(dev);
+
+ return sprintf(buf, "%#x\n", memdev->physical_id);
+}
+static DEVICE_ATTR_RO(phys_id);
+
+static ssize_t vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
+
+ return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->vendor_id));
+}
+static DEVICE_ATTR_RO(vendor);
+
+static ssize_t rev_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
+
+ return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->revision_id));
+}
+static DEVICE_ATTR_RO(rev_id);
+
+static ssize_t device_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
+
+ return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->device_id));
+}
+static DEVICE_ATTR_RO(device);
+
+static ssize_t subsystem_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
+
+ return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_vendor_id));
+}
+static DEVICE_ATTR_RO(subsystem_vendor);
+
+static ssize_t subsystem_rev_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
+
+ return sprintf(buf, "0x%04x\n",
+ be16_to_cpu(dcr->subsystem_revision_id));
+}
+static DEVICE_ATTR_RO(subsystem_rev_id);
+
+static ssize_t subsystem_device_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
+
+ return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_device_id));
+}
+static DEVICE_ATTR_RO(subsystem_device);
+
+static int num_nvdimm_formats(struct nvdimm *nvdimm)
+{
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ int formats = 0;
+
+ if (nfit_mem->memdev_pmem)
+ formats++;
+ return formats;
+}
+
+static ssize_t format_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
+
+ return sprintf(buf, "0x%04x\n", le16_to_cpu(dcr->code));
+}
+static DEVICE_ATTR_RO(format);
+
+static ssize_t format1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 handle;
+ ssize_t rc = -ENXIO;
+ struct nfit_mem *nfit_mem;
+ struct nfit_memdev *nfit_memdev;
+ struct acpi_nfit_desc *acpi_desc;
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+ struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
+
+ nfit_mem = nvdimm_provider_data(nvdimm);
+ acpi_desc = nfit_mem->acpi_desc;
+ handle = to_nfit_memdev(dev)->device_handle;
+
+ /* assumes DIMMs have at most 2 published interface codes */
+ mutex_lock(&acpi_desc->init_mutex);
+ list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
+ struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev;
+ struct nfit_dcr *nfit_dcr;
+
+ if (memdev->device_handle != handle)
+ continue;
+
+ list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) {
+ if (nfit_dcr->dcr->region_index != memdev->region_index)
+ continue;
+ if (nfit_dcr->dcr->code == dcr->code)
+ continue;
+ rc = sprintf(buf, "0x%04x\n",
+ le16_to_cpu(nfit_dcr->dcr->code));
+ break;
+ }
+ if (rc != -ENXIO)
+ break;
+ }
+ mutex_unlock(&acpi_desc->init_mutex);
+ return rc;
+}
+static DEVICE_ATTR_RO(format1);
+
+static ssize_t formats_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+
+ return sprintf(buf, "%d\n", num_nvdimm_formats(nvdimm));
+}
+static DEVICE_ATTR_RO(formats);
+
+static ssize_t serial_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
+
+ return sprintf(buf, "0x%08x\n", be32_to_cpu(dcr->serial_number));
+}
+static DEVICE_ATTR_RO(serial);
+
+static ssize_t family_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+ if (nfit_mem->family < 0)
+ return -ENXIO;
+ return sprintf(buf, "%d\n", nfit_mem->family);
+}
+static DEVICE_ATTR_RO(family);
+
+static ssize_t dsm_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+ if (nfit_mem->family < 0)
+ return -ENXIO;
+ return sprintf(buf, "%#lx\n", nfit_mem->dsm_mask);
+}
+static DEVICE_ATTR_RO(dsm_mask);
+
+static ssize_t flags_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ u16 flags = __to_nfit_memdev(nfit_mem)->flags;
+
+ 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",
+ flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "",
+ flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore_fail " : "",
+ flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush_fail " : "",
+ flags & ACPI_NFIT_MEM_NOT_ARMED ? "not_armed " : "",
+ flags & ACPI_NFIT_MEM_HEALTH_OBSERVED ? "smart_event " : "",
+ flags & ACPI_NFIT_MEM_MAP_FAILED ? "map_fail " : "",
+ flags & ACPI_NFIT_MEM_HEALTH_ENABLED ? "smart_notify " : "");
+}
+static DEVICE_ATTR_RO(flags);
+
+static ssize_t id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+ return sprintf(buf, "%s\n", nfit_mem->id);
+}
+static DEVICE_ATTR_RO(id);
+
+static ssize_t dirty_shutdown_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+ return sprintf(buf, "%d\n", nfit_mem->dirty_shutdown);
+}
+static DEVICE_ATTR_RO(dirty_shutdown);
+
+static struct attribute *acpi_nfit_dimm_attributes[] = {
+ &dev_attr_handle.attr,
+ &dev_attr_phys_id.attr,
+ &dev_attr_vendor.attr,
+ &dev_attr_device.attr,
+ &dev_attr_rev_id.attr,
+ &dev_attr_subsystem_vendor.attr,
+ &dev_attr_subsystem_device.attr,
+ &dev_attr_subsystem_rev_id.attr,
+ &dev_attr_format.attr,
+ &dev_attr_formats.attr,
+ &dev_attr_format1.attr,
+ &dev_attr_serial.attr,
+ &dev_attr_flags.attr,
+ &dev_attr_id.attr,
+ &dev_attr_family.attr,
+ &dev_attr_dsm_mask.attr,
+ &dev_attr_dirty_shutdown.attr,
+ NULL,
+};
+
+static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct nvdimm *nvdimm = to_nvdimm(dev);
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+ if (!to_nfit_dcr(dev)) {
+ /* Without a dcr only the memdev attributes can be surfaced */
+ if (a == &dev_attr_handle.attr || a == &dev_attr_phys_id.attr
+ || a == &dev_attr_flags.attr
+ || a == &dev_attr_family.attr
+ || a == &dev_attr_dsm_mask.attr)
+ return a->mode;
+ return 0;
+ }
+
+ if (a == &dev_attr_format1.attr && num_nvdimm_formats(nvdimm) <= 1)
+ return 0;
+
+ if (!test_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags)
+ && a == &dev_attr_dirty_shutdown.attr)
+ return 0;
+
+ return a->mode;
+}
+
+static const struct attribute_group acpi_nfit_dimm_attribute_group = {
+ .name = "nfit",
+ .attrs = acpi_nfit_dimm_attributes,
+ .is_visible = acpi_nfit_dimm_attr_visible,
+};
+
+static const struct attribute_group *acpi_nfit_dimm_attribute_groups[] = {
+ &acpi_nfit_dimm_attribute_group,
+ NULL,
+};
+
+static struct nvdimm *acpi_nfit_dimm_by_handle(struct acpi_nfit_desc *acpi_desc,
+ u32 device_handle)
+{
+ struct nfit_mem *nfit_mem;
+
+ list_for_each_entry(nfit_mem, &acpi_desc->dimms, list)
+ if (__to_nfit_memdev(nfit_mem)->device_handle == device_handle)
+ return nfit_mem->nvdimm;
+
+ return NULL;
+}
+
+void __acpi_nvdimm_notify(struct device *dev, u32 event)
+{
+ struct nfit_mem *nfit_mem;
+ struct acpi_nfit_desc *acpi_desc;
+
+ dev_dbg(dev->parent, "%s: event: %d\n", dev_name(dev),
+ event);
+
+ if (event != NFIT_NOTIFY_DIMM_HEALTH) {
+ dev_dbg(dev->parent, "%s: unknown event: %d\n", dev_name(dev),
+ event);
+ return;
+ }
+
+ acpi_desc = dev_get_drvdata(dev->parent);
+ if (!acpi_desc)
+ return;
+
+ /*
+ * If we successfully retrieved acpi_desc, then we know nfit_mem data
+ * is still valid.
+ */
+ nfit_mem = dev_get_drvdata(dev);
+ if (nfit_mem && nfit_mem->flags_attr)
+ sysfs_notify_dirent(nfit_mem->flags_attr);
+}
+EXPORT_SYMBOL_GPL(__acpi_nvdimm_notify);
+
+static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct acpi_device *adev = data;
+ struct device *dev = &adev->dev;
+
+ device_lock(dev->parent);
+ __acpi_nvdimm_notify(dev, event);
+ device_unlock(dev->parent);
+}
+
+static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method)
+{
+ acpi_handle handle;
+ acpi_status status;
+
+ status = acpi_get_handle(adev->handle, method, &handle);
+
+ if (ACPI_SUCCESS(status))
+ return true;
+ return false;
+}
+
+__weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem)
+{
+ struct device *dev = &nfit_mem->adev->dev;
+ struct nd_intel_smart smart = { 0 };
+ union acpi_object in_buf = {
+ .buffer.type = ACPI_TYPE_BUFFER,
+ .buffer.length = 0,
+ };
+ union acpi_object in_obj = {
+ .package.type = ACPI_TYPE_PACKAGE,
+ .package.count = 1,
+ .package.elements = &in_buf,
+ };
+ const u8 func = ND_INTEL_SMART;
+ const guid_t *guid = to_nfit_uuid(nfit_mem->family);
+ u8 revid = nfit_dsm_revid(nfit_mem->family, func);
+ struct acpi_device *adev = nfit_mem->adev;
+ acpi_handle handle = adev->handle;
+ union acpi_object *out_obj;
+
+ 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)) {
+ dev_dbg(dev->parent, "%s: failed to retrieve initial health\n",
+ dev_name(dev));
+ ACPI_FREE(out_obj);
+ return;
+ }
+ memcpy(&smart, out_obj->buffer.pointer, sizeof(smart));
+ ACPI_FREE(out_obj);
+
+ if (smart.flags & ND_INTEL_SMART_SHUTDOWN_VALID) {
+ if (smart.shutdown_state)
+ set_bit(NFIT_MEM_DIRTY, &nfit_mem->flags);
+ }
+
+ if (smart.flags & ND_INTEL_SMART_SHUTDOWN_COUNT_VALID) {
+ set_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags);
+ nfit_mem->dirty_shutdown = smart.shutdown_count;
+ }
+}
+
+static void populate_shutdown_status(struct nfit_mem *nfit_mem)
+{
+ /*
+ * For DIMMs that provide a dynamic facility to retrieve a
+ * dirty-shutdown status and/or a dirty-shutdown count, cache
+ * these values in nfit_mem.
+ */
+ if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
+ nfit_intel_shutdown_status(nfit_mem);
+}
+
+static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_mem *nfit_mem, u32 device_handle)
+{
+ struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
+ struct acpi_device *adev, *adev_dimm;
+ struct device *dev = acpi_desc->dev;
+ unsigned long dsm_mask, label_mask;
+ const guid_t *guid;
+ int i;
+ int family = -1;
+ struct acpi_nfit_control_region *dcr = nfit_mem->dcr;
+
+ /* nfit test assumes 1:1 relationship between commands and dsms */
+ nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en;
+ nfit_mem->family = NVDIMM_FAMILY_INTEL;
+ set_bit(NVDIMM_FAMILY_INTEL, &nd_desc->dimm_family_mask);
+
+ if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID)
+ sprintf(nfit_mem->id, "%04x-%02x-%04x-%08x",
+ be16_to_cpu(dcr->vendor_id),
+ dcr->manufacturing_location,
+ be16_to_cpu(dcr->manufacturing_date),
+ be32_to_cpu(dcr->serial_number));
+ else
+ sprintf(nfit_mem->id, "%04x-%08x",
+ be16_to_cpu(dcr->vendor_id),
+ be32_to_cpu(dcr->serial_number));
+
+ adev = to_acpi_dev(acpi_desc);
+ if (!adev) {
+ /* unit test case */
+ populate_shutdown_status(nfit_mem);
+ return 0;
+ }
+
+ adev_dimm = acpi_find_child_device(adev, device_handle, false);
+ nfit_mem->adev = adev_dimm;
+ if (!adev_dimm) {
+ dev_err(dev, "no ACPI.NFIT device with _ADR %#x, disabling...\n",
+ device_handle);
+ return force_enable_dimms ? 0 : -ENODEV;
+ }
+
+ if (ACPI_FAILURE(acpi_install_notify_handler(adev_dimm->handle,
+ ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify, adev_dimm))) {
+ dev_err(dev, "%s: notification registration failed\n",
+ dev_name(&adev_dimm->dev));
+ return -ENXIO;
+ }
+ /*
+ * Record nfit_mem for the notification path to track back to
+ * the nfit sysfs attributes for this dimm device object.
+ */
+ dev_set_drvdata(&adev_dimm->dev, nfit_mem);
+
+ /*
+ * There are 4 "legacy" NVDIMM command sets
+ * (NVDIMM_FAMILY_{INTEL,MSFT,HPE1,HPE2}) that were created before
+ * an EFI working group was established to constrain this
+ * proliferation. The nfit driver probes for the supported command
+ * set by GUID. Note, if you're a platform developer looking to add
+ * a new command set to this probe, consider using an existing set,
+ * or otherwise seek approval to publish the command set at
+ * http://www.uefi.org/RFIC_LIST.
+ *
+ * Note, that checking for function0 (bit0) tells us if any commands
+ * are reachable through this GUID.
+ */
+ clear_bit(NVDIMM_FAMILY_INTEL, &nd_desc->dimm_family_mask);
+ for (i = 0; i <= NVDIMM_FAMILY_MAX; i++)
+ if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1)) {
+ set_bit(i, &nd_desc->dimm_family_mask);
+ if (family < 0 || i == default_dsm_family)
+ family = i;
+ }
+
+ /* limit the supported commands to those that are publicly documented */
+ nfit_mem->family = family;
+ if (override_dsm_mask && !disable_vendor_specific)
+ dsm_mask = override_dsm_mask;
+ else if (nfit_mem->family == NVDIMM_FAMILY_INTEL) {
+ dsm_mask = NVDIMM_INTEL_CMDMASK;
+ if (disable_vendor_specific)
+ dsm_mask &= ~(1 << ND_CMD_VENDOR);
+ } else if (nfit_mem->family == NVDIMM_FAMILY_HPE1) {
+ dsm_mask = 0x1c3c76;
+ } else if (nfit_mem->family == NVDIMM_FAMILY_HPE2) {
+ dsm_mask = 0x1fe;
+ if (disable_vendor_specific)
+ dsm_mask &= ~(1 << 8);
+ } else if (nfit_mem->family == NVDIMM_FAMILY_MSFT) {
+ dsm_mask = 0xffffffff;
+ } else if (nfit_mem->family == NVDIMM_FAMILY_HYPERV) {
+ dsm_mask = 0x1f;
+ } else {
+ dev_dbg(dev, "unknown dimm command family\n");
+ nfit_mem->family = -1;
+ /* DSMs are optional, continue loading the driver... */
+ return 0;
+ }
+
+ /*
+ * Function 0 is the command interrogation function, don't
+ * export it to potential userspace use, and enable it to be
+ * used as an error value in acpi_nfit_ctl().
+ */
+ dsm_mask &= ~1UL;
+
+ guid = to_nfit_uuid(nfit_mem->family);
+ for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
+ if (acpi_check_dsm(adev_dimm->handle, guid,
+ nfit_dsm_revid(nfit_mem->family, i),
+ 1ULL << i))
+ set_bit(i, &nfit_mem->dsm_mask);
+
+ /*
+ * Prefer the NVDIMM_FAMILY_INTEL label read commands if present
+ * due to their better semantics handling locked capacity.
+ */
+ label_mask = 1 << ND_CMD_GET_CONFIG_SIZE | 1 << ND_CMD_GET_CONFIG_DATA
+ | 1 << ND_CMD_SET_CONFIG_DATA;
+ if (family == NVDIMM_FAMILY_INTEL
+ && (dsm_mask & label_mask) == label_mask)
+ /* skip _LS{I,R,W} enabling */;
+ else {
+ if (acpi_nvdimm_has_method(adev_dimm, "_LSI")
+ && acpi_nvdimm_has_method(adev_dimm, "_LSR")) {
+ dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev));
+ set_bit(NFIT_MEM_LSR, &nfit_mem->flags);
+ }
+
+ if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)
+ && acpi_nvdimm_has_method(adev_dimm, "_LSW")) {
+ dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev));
+ set_bit(NFIT_MEM_LSW, &nfit_mem->flags);
+ }
+
+ /*
+ * Quirk read-only label configurations to preserve
+ * access to label-less namespaces by default.
+ */
+ if (!test_bit(NFIT_MEM_LSW, &nfit_mem->flags)
+ && !force_labels) {
+ dev_dbg(dev, "%s: No _LSW, disable labels\n",
+ dev_name(&adev_dimm->dev));
+ clear_bit(NFIT_MEM_LSR, &nfit_mem->flags);
+ } else
+ dev_dbg(dev, "%s: Force enable labels\n",
+ dev_name(&adev_dimm->dev));
+ }
+
+ populate_shutdown_status(nfit_mem);
+
+ return 0;
+}
+
+static void shutdown_dimm_notify(void *data)
+{
+ struct acpi_nfit_desc *acpi_desc = data;
+ struct nfit_mem *nfit_mem;
+
+ mutex_lock(&acpi_desc->init_mutex);
+ /*
+ * Clear out the nfit_mem->flags_attr and shut down dimm event
+ * notifications.
+ */
+ list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
+ struct acpi_device *adev_dimm = nfit_mem->adev;
+
+ if (nfit_mem->flags_attr) {
+ sysfs_put(nfit_mem->flags_attr);
+ nfit_mem->flags_attr = NULL;
+ }
+ if (adev_dimm) {
+ acpi_remove_notify_handler(adev_dimm->handle,
+ ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify);
+ dev_set_drvdata(&adev_dimm->dev, NULL);
+ }
+ }
+ mutex_unlock(&acpi_desc->init_mutex);
+}
+
+static const struct nvdimm_security_ops *acpi_nfit_get_security_ops(int family)
+{
+ switch (family) {
+ case NVDIMM_FAMILY_INTEL:
+ return intel_security_ops;
+ default:
+ return NULL;
+ }
+}
+
+static const struct nvdimm_fw_ops *acpi_nfit_get_fw_ops(
+ struct nfit_mem *nfit_mem)
+{
+ unsigned long mask;
+ struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
+ struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
+
+ if (!nd_desc->fw_ops)
+ return NULL;
+
+ if (nfit_mem->family != NVDIMM_FAMILY_INTEL)
+ return NULL;
+
+ mask = nfit_mem->dsm_mask & NVDIMM_INTEL_FW_ACTIVATE_CMDMASK;
+ if (mask != NVDIMM_INTEL_FW_ACTIVATE_CMDMASK)
+ return NULL;
+
+ return intel_fw_ops;
+}
+
+static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
+{
+ struct nfit_mem *nfit_mem;
+ int dimm_count = 0, rc;
+ struct nvdimm *nvdimm;
+
+ list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
+ struct acpi_nfit_flush_address *flush;
+ unsigned long flags = 0, cmd_mask;
+ struct nfit_memdev *nfit_memdev;
+ u32 device_handle;
+ u16 mem_flags;
+
+ device_handle = __to_nfit_memdev(nfit_mem)->device_handle;
+ nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle);
+ if (nvdimm) {
+ dimm_count++;
+ continue;
+ }
+
+ /* collate flags across all memdevs for this dimm */
+ list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
+ struct acpi_nfit_memory_map *dimm_memdev;
+
+ dimm_memdev = __to_nfit_memdev(nfit_mem);
+ if (dimm_memdev->device_handle
+ != nfit_memdev->memdev->device_handle)
+ continue;
+ dimm_memdev->flags |= nfit_memdev->memdev->flags;
+ }
+
+ mem_flags = __to_nfit_memdev(nfit_mem)->flags;
+ if (mem_flags & ACPI_NFIT_MEM_NOT_ARMED)
+ set_bit(NDD_UNARMED, &flags);
+
+ rc = acpi_nfit_add_dimm(acpi_desc, nfit_mem, device_handle);
+ if (rc)
+ continue;
+
+ /*
+ * TODO: provide translation for non-NVDIMM_FAMILY_INTEL
+ * devices (i.e. from nd_cmd to acpi_dsm) to standardize the
+ * userspace interface.
+ */
+ cmd_mask = 1UL << ND_CMD_CALL;
+ if (nfit_mem->family == NVDIMM_FAMILY_INTEL) {
+ /*
+ * These commands have a 1:1 correspondence
+ * between DSM payload and libnvdimm ioctl
+ * payload format.
+ */
+ cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK;
+ }
+
+ if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) {
+ set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
+ set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
+ }
+ if (test_bit(NFIT_MEM_LSW, &nfit_mem->flags))
+ set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
+
+ flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush
+ : NULL;
+ nvdimm = __nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
+ acpi_nfit_dimm_attribute_groups,
+ flags, cmd_mask, flush ? flush->hint_count : 0,
+ nfit_mem->flush_wpq, &nfit_mem->id[0],
+ acpi_nfit_get_security_ops(nfit_mem->family),
+ acpi_nfit_get_fw_ops(nfit_mem));
+ if (!nvdimm)
+ return -ENOMEM;
+
+ nfit_mem->nvdimm = nvdimm;
+ dimm_count++;
+
+ if ((mem_flags & ACPI_NFIT_MEM_FAILED_MASK) == 0)
+ continue;
+
+ dev_err(acpi_desc->dev, "Error found in NVDIMM %s flags:%s%s%s%s%s\n",
+ nvdimm_name(nvdimm),
+ mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? " save_fail" : "",
+ mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? " restore_fail":"",
+ mem_flags & ACPI_NFIT_MEM_FLUSH_FAILED ? " flush_fail" : "",
+ mem_flags & ACPI_NFIT_MEM_NOT_ARMED ? " not_armed" : "",
+ mem_flags & ACPI_NFIT_MEM_MAP_FAILED ? " map_fail" : "");
+
+ }
+
+ rc = nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count);
+ if (rc)
+ return rc;
+
+ /*
+ * Now that dimms are successfully registered, and async registration
+ * is flushed, attempt to enable event notification.
+ */
+ list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
+ struct kernfs_node *nfit_kernfs;
+
+ nvdimm = nfit_mem->nvdimm;
+ if (!nvdimm)
+ continue;
+
+ nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit");
+ if (nfit_kernfs)
+ nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs,
+ "flags");
+ sysfs_put(nfit_kernfs);
+ if (!nfit_mem->flags_attr)
+ dev_warn(acpi_desc->dev, "%s: notifications disabled\n",
+ nvdimm_name(nvdimm));
+ }
+
+ return devm_add_action_or_reset(acpi_desc->dev, shutdown_dimm_notify,
+ acpi_desc);
+}
+
+/*
+ * These constants are private because there are no kernel consumers of
+ * these commands.
+ */
+enum nfit_aux_cmds {
+ NFIT_CMD_TRANSLATE_SPA = 5,
+ NFIT_CMD_ARS_INJECT_SET = 7,
+ NFIT_CMD_ARS_INJECT_CLEAR = 8,
+ NFIT_CMD_ARS_INJECT_GET = 9,
+};
+
+static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
+{
+ struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
+ const guid_t *guid = to_nfit_uuid(NFIT_DEV_BUS);
+ unsigned long dsm_mask, *mask;
+ struct acpi_device *adev;
+ int i;
+
+ set_bit(ND_CMD_CALL, &nd_desc->cmd_mask);
+ set_bit(NVDIMM_BUS_FAMILY_NFIT, &nd_desc->bus_family_mask);
+
+ /* enable nfit_test to inject bus command emulation */
+ if (acpi_desc->bus_cmd_force_en) {
+ nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en;
+ mask = &nd_desc->bus_family_mask;
+ if (acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL]) {
+ set_bit(NVDIMM_BUS_FAMILY_INTEL, mask);
+ nd_desc->fw_ops = intel_bus_fw_ops;
+ }
+ }
+
+ adev = to_acpi_dev(acpi_desc);
+ if (!adev)
+ return;
+
+ for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++)
+ if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
+ set_bit(i, &nd_desc->cmd_mask);
+
+ dsm_mask =
+ (1 << ND_CMD_ARS_CAP) |
+ (1 << ND_CMD_ARS_START) |
+ (1 << ND_CMD_ARS_STATUS) |
+ (1 << ND_CMD_CLEAR_ERROR) |
+ (1 << NFIT_CMD_TRANSLATE_SPA) |
+ (1 << NFIT_CMD_ARS_INJECT_SET) |
+ (1 << NFIT_CMD_ARS_INJECT_CLEAR) |
+ (1 << NFIT_CMD_ARS_INJECT_GET);
+ for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
+ if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
+ set_bit(i, &acpi_desc->bus_dsm_mask);
+
+ /* Enumerate allowed NVDIMM_BUS_FAMILY_INTEL commands */
+ dsm_mask = NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK;
+ guid = to_nfit_bus_uuid(NVDIMM_BUS_FAMILY_INTEL);
+ mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL];
+ for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
+ if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
+ set_bit(i, mask);
+
+ if (*mask == dsm_mask) {
+ set_bit(NVDIMM_BUS_FAMILY_INTEL, &nd_desc->bus_family_mask);
+ nd_desc->fw_ops = intel_bus_fw_ops;
+ }
+}
+
+static ssize_t range_index_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ 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);
+}
+static DEVICE_ATTR_RO(range_index);
+
+static struct attribute *acpi_nfit_region_attributes[] = {
+ &dev_attr_range_index.attr,
+ NULL,
+};
+
+static const struct attribute_group acpi_nfit_region_attribute_group = {
+ .name = "nfit",
+ .attrs = acpi_nfit_region_attributes,
+};
+
+static const struct attribute_group *acpi_nfit_region_attribute_groups[] = {
+ &acpi_nfit_region_attribute_group,
+ NULL,
+};
+
+/* enough info to uniquely specify an interleave set */
+struct nfit_set_info {
+ u64 region_offset;
+ u32 serial_number;
+ u32 pad;
+};
+
+struct nfit_set_info2 {
+ u64 region_offset;
+ u32 serial_number;
+ u16 vendor_id;
+ u16 manufacturing_date;
+ u8 manufacturing_location;
+ u8 reserved[31];
+};
+
+static int cmp_map_compat(const void *m0, const void *m1)
+{
+ const struct nfit_set_info *map0 = m0;
+ const struct nfit_set_info *map1 = m1;
+
+ return memcmp(&map0->region_offset, &map1->region_offset,
+ sizeof(u64));
+}
+
+static int cmp_map(const void *m0, const void *m1)
+{
+ const struct nfit_set_info *map0 = m0;
+ const struct nfit_set_info *map1 = m1;
+
+ if (map0->region_offset < map1->region_offset)
+ return -1;
+ else if (map0->region_offset > map1->region_offset)
+ return 1;
+ return 0;
+}
+
+static int cmp_map2(const void *m0, const void *m1)
+{
+ const struct nfit_set_info2 *map0 = m0;
+ const struct nfit_set_info2 *map1 = m1;
+
+ if (map0->region_offset < map1->region_offset)
+ return -1;
+ else if (map0->region_offset > map1->region_offset)
+ return 1;
+ return 0;
+}
+
+/* Retrieve the nth entry referencing this spa */
+static struct acpi_nfit_memory_map *memdev_from_spa(
+ struct acpi_nfit_desc *acpi_desc, u16 range_index, int n)
+{
+ struct nfit_memdev *nfit_memdev;
+
+ list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list)
+ if (nfit_memdev->memdev->range_index == range_index)
+ if (n-- == 0)
+ return nfit_memdev->memdev;
+ return NULL;
+}
+
+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)
+{
+ 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;
+
+ 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;
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ struct nfit_set_info *map = &info[i];
+ struct nfit_set_info2 *map2 = &info2[i];
+ struct acpi_nfit_memory_map *memdev =
+ memdev_from_spa(acpi_desc, spa->range_index, i);
+ struct acpi_nfit_control_region *dcr = nfit_mem->dcr;
+
+ if (!memdev || !nfit_mem->dcr) {
+ dev_err(dev, "%s: failed to find DCR\n", __func__);
+ return -ENODEV;
+ }
+
+ map->region_offset = memdev->region_offset;
+ map->serial_number = dcr->serial_number;
+
+ map2->region_offset = memdev->region_offset;
+ map2->serial_number = dcr->serial_number;
+ map2->vendor_id = dcr->vendor_id;
+ map2->manufacturing_date = dcr->manufacturing_date;
+ map2->manufacturing_location = dcr->manufacturing_location;
+ }
+
+ /* v1.1 namespaces */
+ sort(info, nr, sizeof(*info), cmp_map, NULL);
+ nd_set->cookie1 = nd_fletcher64(info, sizeof(*info) * nr, 0);
+
+ /* v1.2 namespaces */
+ sort(info2, nr, sizeof(*info2), cmp_map2, NULL);
+ nd_set->cookie2 = nd_fletcher64(info2, sizeof(*info2) * nr, 0);
+
+ /* support v1.1 namespaces created with the wrong sort order */
+ sort(info, nr, sizeof(*info), cmp_map_compat, NULL);
+ nd_set->altcookie = nd_fletcher64(info, sizeof(*info) * nr, 0);
+
+ /* record the result of the sort for the mapping position */
+ for (i = 0; i < nr; i++) {
+ struct nfit_set_info2 *map2 = &info2[i];
+ int j;
+
+ for (j = 0; j < nr; j++) {
+ struct nd_mapping_desc *mapping = &ndr_desc->mapping[j];
+ struct nvdimm *nvdimm = mapping->nvdimm;
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ struct acpi_nfit_control_region *dcr = nfit_mem->dcr;
+
+ if (map2->serial_number == dcr->serial_number &&
+ map2->vendor_id == dcr->vendor_id &&
+ map2->manufacturing_date == dcr->manufacturing_date &&
+ map2->manufacturing_location
+ == dcr->manufacturing_location) {
+ mapping->position = i;
+ break;
+ }
+ }
+ }
+
+ ndr_desc->nd_set = nd_set;
+ devm_kfree(dev, info);
+ devm_kfree(dev, info2);
+
+ return 0;
+}
+
+static int ars_get_cap(struct acpi_nfit_desc *acpi_desc,
+ struct nd_cmd_ars_cap *cmd, struct nfit_spa *nfit_spa)
+{
+ struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
+ struct acpi_nfit_system_address *spa = nfit_spa->spa;
+ int cmd_rc, rc;
+
+ cmd->address = spa->address;
+ cmd->length = spa->length;
+ rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, cmd,
+ sizeof(*cmd), &cmd_rc);
+ if (rc < 0)
+ return rc;
+ return cmd_rc;
+}
+
+static int ars_start(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_spa *nfit_spa, enum nfit_ars_state req_type)
+{
+ int rc;
+ int cmd_rc;
+ struct nd_cmd_ars_start ars_start;
+ struct acpi_nfit_system_address *spa = nfit_spa->spa;
+ struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
+
+ memset(&ars_start, 0, sizeof(ars_start));
+ ars_start.address = spa->address;
+ ars_start.length = spa->length;
+ if (req_type == ARS_REQ_SHORT)
+ ars_start.flags = ND_ARS_RETURN_PREV_DATA;
+ if (nfit_spa_type(spa) == NFIT_SPA_PM)
+ ars_start.type = ND_ARS_PERSISTENT;
+ else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE)
+ ars_start.type = ND_ARS_VOLATILE;
+ else
+ return -ENOTTY;
+
+ rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, &ars_start,
+ sizeof(ars_start), &cmd_rc);
+
+ if (rc < 0)
+ return rc;
+ if (cmd_rc < 0)
+ return cmd_rc;
+ set_bit(ARS_VALID, &acpi_desc->scrub_flags);
+ return 0;
+}
+
+static int ars_continue(struct acpi_nfit_desc *acpi_desc)
+{
+ int rc, cmd_rc;
+ struct nd_cmd_ars_start ars_start;
+ struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
+ struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
+
+ ars_start = (struct nd_cmd_ars_start) {
+ .address = ars_status->restart_address,
+ .length = ars_status->restart_length,
+ .type = ars_status->type,
+ };
+ rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, &ars_start,
+ sizeof(ars_start), &cmd_rc);
+ if (rc < 0)
+ return rc;
+ return cmd_rc;
+}
+
+static int ars_get_status(struct acpi_nfit_desc *acpi_desc)
+{
+ struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
+ struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
+ int rc, cmd_rc;
+
+ rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, ars_status,
+ acpi_desc->max_ars, &cmd_rc);
+ if (rc < 0)
+ return rc;
+ return cmd_rc;
+}
+
+static void ars_complete(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_spa *nfit_spa)
+{
+ struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
+ struct acpi_nfit_system_address *spa = nfit_spa->spa;
+ struct nd_region *nd_region = nfit_spa->nd_region;
+ struct device *dev;
+
+ lockdep_assert_held(&acpi_desc->init_mutex);
+ /*
+ * Only advance the ARS state for ARS runs initiated by the
+ * kernel, ignore ARS results from BIOS initiated runs for scrub
+ * completion tracking.
+ */
+ if (acpi_desc->scrub_spa != nfit_spa)
+ return;
+
+ if ((ars_status->address >= spa->address && ars_status->address
+ < spa->address + spa->length)
+ || (ars_status->address < spa->address)) {
+ /*
+ * Assume that if a scrub starts at an offset from the
+ * start of nfit_spa that we are in the continuation
+ * case.
+ *
+ * Otherwise, if the scrub covers the spa range, mark
+ * any pending request complete.
+ */
+ if (ars_status->address + ars_status->length
+ >= spa->address + spa->length)
+ /* complete */;
+ else
+ return;
+ } else
+ return;
+
+ acpi_desc->scrub_spa = NULL;
+ if (nd_region) {
+ dev = nd_region_dev(nd_region);
+ nvdimm_region_notify(nd_region, NVDIMM_REVALIDATE_POISON);
+ } else
+ dev = acpi_desc->dev;
+ dev_dbg(dev, "ARS: range %d complete\n", spa->range_index);
+}
+
+static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc)
+{
+ struct nvdimm_bus *nvdimm_bus = acpi_desc->nvdimm_bus;
+ struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
+ int rc;
+ u32 i;
+
+ /*
+ * First record starts at 44 byte offset from the start of the
+ * payload.
+ */
+ if (ars_status->out_length < 44)
+ return 0;
+
+ /*
+ * Ignore potentially stale results that are only refreshed
+ * after a start-ARS event.
+ */
+ if (!test_and_clear_bit(ARS_VALID, &acpi_desc->scrub_flags)) {
+ dev_dbg(acpi_desc->dev, "skip %d stale records\n",
+ ars_status->num_records);
+ return 0;
+ }
+
+ for (i = 0; i < ars_status->num_records; i++) {
+ /* only process full records */
+ if (ars_status->out_length
+ < 44 + sizeof(struct nd_ars_record) * (i + 1))
+ break;
+ rc = nvdimm_bus_add_badrange(nvdimm_bus,
+ ars_status->records[i].err_address,
+ ars_status->records[i].length);
+ if (rc)
+ return rc;
+ }
+ if (i < ars_status->num_records)
+ dev_warn(acpi_desc->dev, "detected truncated ars results\n");
+
+ return 0;
+}
+
+static void acpi_nfit_remove_resource(void *data)
+{
+ struct resource *res = data;
+
+ remove_resource(res);
+}
+
+static int acpi_nfit_insert_resource(struct acpi_nfit_desc *acpi_desc,
+ struct nd_region_desc *ndr_desc)
+{
+ struct resource *res, *nd_res = ndr_desc->res;
+ int is_pmem, ret;
+
+ /* No operation if the region is already registered as PMEM */
+ is_pmem = region_intersects(nd_res->start, resource_size(nd_res),
+ IORESOURCE_MEM, IORES_DESC_PERSISTENT_MEMORY);
+ if (is_pmem == REGION_INTERSECTS)
+ return 0;
+
+ res = devm_kzalloc(acpi_desc->dev, sizeof(*res), GFP_KERNEL);
+ if (!res)
+ return -ENOMEM;
+
+ res->name = "Persistent Memory";
+ res->start = nd_res->start;
+ res->end = nd_res->end;
+ res->flags = IORESOURCE_MEM;
+ res->desc = IORES_DESC_PERSISTENT_MEMORY;
+
+ ret = insert_resource(&iomem_resource, res);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(acpi_desc->dev,
+ acpi_nfit_remove_resource,
+ res);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc,
+ struct nd_mapping_desc *mapping, struct nd_region_desc *ndr_desc,
+ struct acpi_nfit_memory_map *memdev,
+ struct nfit_spa *nfit_spa)
+{
+ struct nvdimm *nvdimm = acpi_nfit_dimm_by_handle(acpi_desc,
+ memdev->device_handle);
+ struct acpi_nfit_system_address *spa = nfit_spa->spa;
+
+ if (!nvdimm) {
+ dev_err(acpi_desc->dev, "spa%d dimm: %#x not found\n",
+ spa->range_index, memdev->device_handle);
+ return -ENODEV;
+ }
+
+ mapping->nvdimm = nvdimm;
+ switch (nfit_spa_type(spa)) {
+ case NFIT_SPA_PM:
+ case NFIT_SPA_VOLATILE:
+ mapping->start = memdev->address;
+ mapping->size = memdev->region_size;
+ break;
+ }
+
+ return 0;
+}
+
+static bool nfit_spa_is_virtual(struct acpi_nfit_system_address *spa)
+{
+ return (nfit_spa_type(spa) == NFIT_SPA_VDISK ||
+ nfit_spa_type(spa) == NFIT_SPA_VCD ||
+ nfit_spa_type(spa) == NFIT_SPA_PDISK ||
+ nfit_spa_type(spa) == NFIT_SPA_PCD);
+}
+
+static bool nfit_spa_is_volatile(struct acpi_nfit_system_address *spa)
+{
+ return (nfit_spa_type(spa) == NFIT_SPA_VDISK ||
+ nfit_spa_type(spa) == NFIT_SPA_VCD ||
+ nfit_spa_type(spa) == NFIT_SPA_VOLATILE);
+}
+
+static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_spa *nfit_spa)
+{
+ static struct nd_mapping_desc mappings[ND_MAX_MAPPINGS];
+ struct acpi_nfit_system_address *spa = nfit_spa->spa;
+ struct nd_region_desc *ndr_desc, _ndr_desc;
+ struct nfit_memdev *nfit_memdev;
+ struct nvdimm_bus *nvdimm_bus;
+ struct resource res;
+ int count = 0, rc;
+
+ if (nfit_spa->nd_region)
+ return 0;
+
+ if (spa->range_index == 0 && !nfit_spa_is_virtual(spa)) {
+ dev_dbg(acpi_desc->dev, "detected invalid spa index\n");
+ return 0;
+ }
+
+ memset(&res, 0, sizeof(res));
+ memset(&mappings, 0, sizeof(mappings));
+ memset(&_ndr_desc, 0, sizeof(_ndr_desc));
+ res.start = spa->address;
+ res.end = res.start + spa->length - 1;
+ ndr_desc = &_ndr_desc;
+ ndr_desc->res = &res;
+ ndr_desc->provider_data = nfit_spa;
+ ndr_desc->attr_groups = acpi_nfit_region_attribute_groups;
+ if (spa->flags & ACPI_NFIT_PROXIMITY_VALID) {
+ ndr_desc->numa_node = pxm_to_online_node(spa->proximity_domain);
+ ndr_desc->target_node = pxm_to_node(spa->proximity_domain);
+ } else {
+ ndr_desc->numa_node = NUMA_NO_NODE;
+ ndr_desc->target_node = NUMA_NO_NODE;
+ }
+
+ /* Fallback to address based numa information if node lookup failed */
+ if (ndr_desc->numa_node == NUMA_NO_NODE) {
+ ndr_desc->numa_node = memory_add_physaddr_to_nid(spa->address);
+ dev_info(acpi_desc->dev, "changing numa node from %d to %d for nfit region [%pa-%pa]",
+ NUMA_NO_NODE, ndr_desc->numa_node, &res.start, &res.end);
+ }
+ 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);
+ }
+
+ /*
+ * Persistence domain bits are hierarchical, if
+ * ACPI_NFIT_CAPABILITY_CACHE_FLUSH is set then
+ * ACPI_NFIT_CAPABILITY_MEM_FLUSH is implied.
+ */
+ if (acpi_desc->platform_cap & ACPI_NFIT_CAPABILITY_CACHE_FLUSH)
+ set_bit(ND_REGION_PERSIST_CACHE, &ndr_desc->flags);
+ else if (acpi_desc->platform_cap & ACPI_NFIT_CAPABILITY_MEM_FLUSH)
+ set_bit(ND_REGION_PERSIST_MEMCTRL, &ndr_desc->flags);
+
+ list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
+ struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev;
+ struct nd_mapping_desc *mapping;
+
+ /* range index 0 == unmapped in SPA or invalid-SPA */
+ if (memdev->range_index == 0 || spa->range_index == 0)
+ continue;
+ if (memdev->range_index != spa->range_index)
+ continue;
+ if (count >= ND_MAX_MAPPINGS) {
+ dev_err(acpi_desc->dev, "spa%d exceeds max mappings %d\n",
+ spa->range_index, ND_MAX_MAPPINGS);
+ return -ENXIO;
+ }
+ mapping = &mappings[count++];
+ rc = acpi_nfit_init_mapping(acpi_desc, mapping, ndr_desc,
+ memdev, nfit_spa);
+ if (rc)
+ goto out;
+ }
+
+ ndr_desc->mapping = mappings;
+ ndr_desc->num_mappings = count;
+ rc = acpi_nfit_init_interleave_set(acpi_desc, ndr_desc, spa);
+ if (rc)
+ goto out;
+
+ nvdimm_bus = acpi_desc->nvdimm_bus;
+ if (nfit_spa_type(spa) == NFIT_SPA_PM) {
+ rc = acpi_nfit_insert_resource(acpi_desc, ndr_desc);
+ if (rc) {
+ dev_warn(acpi_desc->dev,
+ "failed to insert pmem resource to iomem: %d\n",
+ rc);
+ goto out;
+ }
+
+ nfit_spa->nd_region = nvdimm_pmem_region_create(nvdimm_bus,
+ ndr_desc);
+ if (!nfit_spa->nd_region)
+ rc = -ENOMEM;
+ } else if (nfit_spa_is_volatile(spa)) {
+ nfit_spa->nd_region = nvdimm_volatile_region_create(nvdimm_bus,
+ ndr_desc);
+ if (!nfit_spa->nd_region)
+ rc = -ENOMEM;
+ } else if (nfit_spa_is_virtual(spa)) {
+ nfit_spa->nd_region = nvdimm_pmem_region_create(nvdimm_bus,
+ ndr_desc);
+ if (!nfit_spa->nd_region)
+ rc = -ENOMEM;
+ }
+
+ out:
+ if (rc)
+ dev_err(acpi_desc->dev, "failed to register spa range %d\n",
+ nfit_spa->spa->range_index);
+ return rc;
+}
+
+static int ars_status_alloc(struct acpi_nfit_desc *acpi_desc)
+{
+ struct device *dev = acpi_desc->dev;
+ struct nd_cmd_ars_status *ars_status;
+
+ if (acpi_desc->ars_status) {
+ memset(acpi_desc->ars_status, 0, acpi_desc->max_ars);
+ return 0;
+ }
+
+ ars_status = devm_kzalloc(dev, acpi_desc->max_ars, GFP_KERNEL);
+ if (!ars_status)
+ return -ENOMEM;
+ acpi_desc->ars_status = ars_status;
+ return 0;
+}
+
+static int acpi_nfit_query_poison(struct acpi_nfit_desc *acpi_desc)
+{
+ int rc;
+
+ if (ars_status_alloc(acpi_desc))
+ return -ENOMEM;
+
+ rc = ars_get_status(acpi_desc);
+
+ if (rc < 0 && rc != -ENOSPC)
+ return rc;
+
+ if (ars_status_process_records(acpi_desc))
+ dev_err(acpi_desc->dev, "Failed to process ARS records\n");
+
+ return rc;
+}
+
+static int ars_register(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_spa *nfit_spa)
+{
+ int rc;
+
+ if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
+ return acpi_nfit_register_region(acpi_desc, nfit_spa);
+
+ set_bit(ARS_REQ_SHORT, &nfit_spa->ars_state);
+ if (!no_init_ars)
+ set_bit(ARS_REQ_LONG, &nfit_spa->ars_state);
+
+ switch (acpi_nfit_query_poison(acpi_desc)) {
+ case 0:
+ case -ENOSPC:
+ case -EAGAIN:
+ rc = ars_start(acpi_desc, nfit_spa, ARS_REQ_SHORT);
+ /* shouldn't happen, try again later */
+ if (rc == -EBUSY)
+ break;
+ if (rc) {
+ set_bit(ARS_FAILED, &nfit_spa->ars_state);
+ break;
+ }
+ clear_bit(ARS_REQ_SHORT, &nfit_spa->ars_state);
+ rc = acpi_nfit_query_poison(acpi_desc);
+ if (rc)
+ break;
+ acpi_desc->scrub_spa = nfit_spa;
+ ars_complete(acpi_desc, nfit_spa);
+ /*
+ * If ars_complete() says we didn't complete the
+ * short scrub, we'll try again with a long
+ * request.
+ */
+ acpi_desc->scrub_spa = NULL;
+ break;
+ case -EBUSY:
+ case -ENOMEM:
+ /*
+ * BIOS was using ARS, wait for it to complete (or
+ * resources to become available) and then perform our
+ * own scrubs.
+ */
+ break;
+ default:
+ set_bit(ARS_FAILED, &nfit_spa->ars_state);
+ break;
+ }
+
+ return acpi_nfit_register_region(acpi_desc, nfit_spa);
+}
+
+static void ars_complete_all(struct acpi_nfit_desc *acpi_desc)
+{
+ struct nfit_spa *nfit_spa;
+
+ list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
+ if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
+ continue;
+ ars_complete(acpi_desc, nfit_spa);
+ }
+}
+
+static unsigned int __acpi_nfit_scrub(struct acpi_nfit_desc *acpi_desc,
+ int query_rc)
+{
+ unsigned int tmo = acpi_desc->scrub_tmo;
+ struct device *dev = acpi_desc->dev;
+ struct nfit_spa *nfit_spa;
+
+ lockdep_assert_held(&acpi_desc->init_mutex);
+
+ if (test_bit(ARS_CANCEL, &acpi_desc->scrub_flags))
+ return 0;
+
+ if (query_rc == -EBUSY) {
+ dev_dbg(dev, "ARS: ARS busy\n");
+ return min(30U * 60U, tmo * 2);
+ }
+ if (query_rc == -ENOSPC) {
+ dev_dbg(dev, "ARS: ARS continue\n");
+ ars_continue(acpi_desc);
+ return 1;
+ }
+ if (query_rc && query_rc != -EAGAIN) {
+ unsigned long long addr, end;
+
+ addr = acpi_desc->ars_status->address;
+ end = addr + acpi_desc->ars_status->length;
+ dev_dbg(dev, "ARS: %llx-%llx failed (%d)\n", addr, end,
+ query_rc);
+ }
+
+ ars_complete_all(acpi_desc);
+ list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
+ enum nfit_ars_state req_type;
+ int rc;
+
+ if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
+ continue;
+
+ /* prefer short ARS requests first */
+ if (test_bit(ARS_REQ_SHORT, &nfit_spa->ars_state))
+ req_type = ARS_REQ_SHORT;
+ else if (test_bit(ARS_REQ_LONG, &nfit_spa->ars_state))
+ req_type = ARS_REQ_LONG;
+ else
+ continue;
+ rc = ars_start(acpi_desc, nfit_spa, req_type);
+
+ dev = nd_region_dev(nfit_spa->nd_region);
+ dev_dbg(dev, "ARS: range %d ARS start %s (%d)\n",
+ nfit_spa->spa->range_index,
+ req_type == ARS_REQ_SHORT ? "short" : "long",
+ rc);
+ /*
+ * Hmm, we raced someone else starting ARS? Try again in
+ * a bit.
+ */
+ if (rc == -EBUSY)
+ return 1;
+ if (rc == 0) {
+ dev_WARN_ONCE(dev, acpi_desc->scrub_spa,
+ "scrub start while range %d active\n",
+ acpi_desc->scrub_spa->spa->range_index);
+ clear_bit(req_type, &nfit_spa->ars_state);
+ acpi_desc->scrub_spa = nfit_spa;
+ /*
+ * Consider this spa last for future scrub
+ * requests
+ */
+ list_move_tail(&nfit_spa->list, &acpi_desc->spas);
+ return 1;
+ }
+
+ dev_err(dev, "ARS: range %d ARS failed (%d)\n",
+ nfit_spa->spa->range_index, rc);
+ set_bit(ARS_FAILED, &nfit_spa->ars_state);
+ }
+ return 0;
+}
+
+static void __sched_ars(struct acpi_nfit_desc *acpi_desc, unsigned int tmo)
+{
+ lockdep_assert_held(&acpi_desc->init_mutex);
+
+ set_bit(ARS_BUSY, &acpi_desc->scrub_flags);
+ /* note this should only be set from within the workqueue */
+ if (tmo)
+ acpi_desc->scrub_tmo = tmo;
+ queue_delayed_work(nfit_wq, &acpi_desc->dwork, tmo * HZ);
+}
+
+static void sched_ars(struct acpi_nfit_desc *acpi_desc)
+{
+ __sched_ars(acpi_desc, 0);
+}
+
+static void notify_ars_done(struct acpi_nfit_desc *acpi_desc)
+{
+ lockdep_assert_held(&acpi_desc->init_mutex);
+
+ clear_bit(ARS_BUSY, &acpi_desc->scrub_flags);
+ acpi_desc->scrub_count++;
+ if (acpi_desc->scrub_count_state)
+ sysfs_notify_dirent(acpi_desc->scrub_count_state);
+}
+
+static void acpi_nfit_scrub(struct work_struct *work)
+{
+ struct acpi_nfit_desc *acpi_desc;
+ unsigned int tmo;
+ int query_rc;
+
+ acpi_desc = container_of(work, typeof(*acpi_desc), dwork.work);
+ mutex_lock(&acpi_desc->init_mutex);
+ query_rc = acpi_nfit_query_poison(acpi_desc);
+ tmo = __acpi_nfit_scrub(acpi_desc, query_rc);
+ if (tmo)
+ __sched_ars(acpi_desc, tmo);
+ else
+ notify_ars_done(acpi_desc);
+ memset(acpi_desc->ars_status, 0, acpi_desc->max_ars);
+ clear_bit(ARS_POLL, &acpi_desc->scrub_flags);
+ mutex_unlock(&acpi_desc->init_mutex);
+}
+
+static void acpi_nfit_init_ars(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_spa *nfit_spa)
+{
+ int type = nfit_spa_type(nfit_spa->spa);
+ struct nd_cmd_ars_cap ars_cap;
+ int rc;
+
+ set_bit(ARS_FAILED, &nfit_spa->ars_state);
+ memset(&ars_cap, 0, sizeof(ars_cap));
+ rc = ars_get_cap(acpi_desc, &ars_cap, nfit_spa);
+ if (rc < 0)
+ return;
+ /* check that the supported scrub types match the spa type */
+ if (type == NFIT_SPA_VOLATILE && ((ars_cap.status >> 16)
+ & ND_ARS_VOLATILE) == 0)
+ return;
+ if (type == NFIT_SPA_PM && ((ars_cap.status >> 16)
+ & ND_ARS_PERSISTENT) == 0)
+ return;
+
+ nfit_spa->max_ars = ars_cap.max_ars_out;
+ nfit_spa->clear_err_unit = ars_cap.clear_err_unit;
+ acpi_desc->max_ars = max(nfit_spa->max_ars, acpi_desc->max_ars);
+ clear_bit(ARS_FAILED, &nfit_spa->ars_state);
+}
+
+static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
+{
+ struct nfit_spa *nfit_spa;
+ int rc, do_sched_ars = 0;
+
+ set_bit(ARS_VALID, &acpi_desc->scrub_flags);
+ list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
+ switch (nfit_spa_type(nfit_spa->spa)) {
+ case NFIT_SPA_VOLATILE:
+ case NFIT_SPA_PM:
+ acpi_nfit_init_ars(acpi_desc, nfit_spa);
+ break;
+ }
+ }
+
+ list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
+ switch (nfit_spa_type(nfit_spa->spa)) {
+ case NFIT_SPA_VOLATILE:
+ case NFIT_SPA_PM:
+ /* register regions and kick off initial ARS run */
+ rc = ars_register(acpi_desc, nfit_spa);
+ if (rc)
+ return rc;
+
+ /*
+ * Kick off background ARS if at least one
+ * region successfully registered ARS
+ */
+ if (!test_bit(ARS_FAILED, &nfit_spa->ars_state))
+ do_sched_ars++;
+ break;
+ case NFIT_SPA_BDW:
+ /* nothing to register */
+ break;
+ case NFIT_SPA_DCR:
+ case NFIT_SPA_VDISK:
+ case NFIT_SPA_VCD:
+ case NFIT_SPA_PDISK:
+ case NFIT_SPA_PCD:
+ /* register known regions that don't support ARS */
+ rc = acpi_nfit_register_region(acpi_desc, nfit_spa);
+ if (rc)
+ return rc;
+ break;
+ default:
+ /* don't register unknown regions */
+ break;
+ }
+ }
+
+ if (do_sched_ars)
+ sched_ars(acpi_desc);
+ return 0;
+}
+
+static int acpi_nfit_check_deletions(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_table_prev *prev)
+{
+ struct device *dev = acpi_desc->dev;
+
+ if (!list_empty(&prev->spas) ||
+ !list_empty(&prev->memdevs) ||
+ !list_empty(&prev->dcrs) ||
+ !list_empty(&prev->bdws) ||
+ !list_empty(&prev->idts) ||
+ !list_empty(&prev->flushes)) {
+ dev_err(dev, "new nfit deletes entries (unsupported)\n");
+ return -ENXIO;
+ }
+ return 0;
+}
+
+static int acpi_nfit_desc_init_scrub_attr(struct acpi_nfit_desc *acpi_desc)
+{
+ struct device *dev = acpi_desc->dev;
+ struct kernfs_node *nfit;
+ struct device *bus_dev;
+
+ if (!ars_supported(acpi_desc->nvdimm_bus))
+ return 0;
+
+ bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus);
+ nfit = sysfs_get_dirent(bus_dev->kobj.sd, "nfit");
+ if (!nfit) {
+ dev_err(dev, "sysfs_get_dirent 'nfit' failed\n");
+ return -ENODEV;
+ }
+ acpi_desc->scrub_count_state = sysfs_get_dirent(nfit, "scrub");
+ sysfs_put(nfit);
+ if (!acpi_desc->scrub_count_state) {
+ dev_err(dev, "sysfs_get_dirent 'scrub' failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void acpi_nfit_unregister(void *data)
+{
+ struct acpi_nfit_desc *acpi_desc = data;
+
+ nvdimm_bus_unregister(acpi_desc->nvdimm_bus);
+}
+
+int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz)
+{
+ struct device *dev = acpi_desc->dev;
+ struct nfit_table_prev prev;
+ const void *end;
+ int rc;
+
+ if (!acpi_desc->nvdimm_bus) {
+ acpi_nfit_init_dsms(acpi_desc);
+
+ acpi_desc->nvdimm_bus = nvdimm_bus_register(dev,
+ &acpi_desc->nd_desc);
+ if (!acpi_desc->nvdimm_bus)
+ return -ENOMEM;
+
+ rc = devm_add_action_or_reset(dev, acpi_nfit_unregister,
+ acpi_desc);
+ if (rc)
+ return rc;
+
+ rc = acpi_nfit_desc_init_scrub_attr(acpi_desc);
+ if (rc)
+ return rc;
+
+ /* register this acpi_desc for mce notifications */
+ mutex_lock(&acpi_desc_lock);
+ list_add_tail(&acpi_desc->list, &acpi_descs);
+ mutex_unlock(&acpi_desc_lock);
+ }
+
+ mutex_lock(&acpi_desc->init_mutex);
+
+ INIT_LIST_HEAD(&prev.spas);
+ INIT_LIST_HEAD(&prev.memdevs);
+ INIT_LIST_HEAD(&prev.dcrs);
+ INIT_LIST_HEAD(&prev.bdws);
+ INIT_LIST_HEAD(&prev.idts);
+ INIT_LIST_HEAD(&prev.flushes);
+
+ list_cut_position(&prev.spas, &acpi_desc->spas,
+ acpi_desc->spas.prev);
+ list_cut_position(&prev.memdevs, &acpi_desc->memdevs,
+ acpi_desc->memdevs.prev);
+ list_cut_position(&prev.dcrs, &acpi_desc->dcrs,
+ acpi_desc->dcrs.prev);
+ list_cut_position(&prev.bdws, &acpi_desc->bdws,
+ acpi_desc->bdws.prev);
+ list_cut_position(&prev.idts, &acpi_desc->idts,
+ acpi_desc->idts.prev);
+ list_cut_position(&prev.flushes, &acpi_desc->flushes,
+ acpi_desc->flushes.prev);
+
+ end = data + sz;
+ while (!IS_ERR_OR_NULL(data))
+ data = add_table(acpi_desc, &prev, data, end);
+
+ if (IS_ERR(data)) {
+ dev_dbg(dev, "nfit table parsing error: %ld\n", PTR_ERR(data));
+ rc = PTR_ERR(data);
+ goto out_unlock;
+ }
+
+ rc = acpi_nfit_check_deletions(acpi_desc, &prev);
+ if (rc)
+ goto out_unlock;
+
+ rc = nfit_mem_init(acpi_desc);
+ if (rc)
+ goto out_unlock;
+
+ rc = acpi_nfit_register_dimms(acpi_desc);
+ if (rc)
+ goto out_unlock;
+
+ rc = acpi_nfit_register_regions(acpi_desc);
+
+ out_unlock:
+ mutex_unlock(&acpi_desc->init_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(acpi_nfit_init);
+
+static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc)
+{
+ struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+ struct device *dev = acpi_desc->dev;
+
+ /* Bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */
+ device_lock(dev);
+ device_unlock(dev);
+
+ /* Bounce the init_mutex to complete initial registration */
+ mutex_lock(&acpi_desc->init_mutex);
+ mutex_unlock(&acpi_desc->init_mutex);
+
+ return 0;
+}
+
+static int __acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
+ struct nvdimm *nvdimm, unsigned int cmd)
+{
+ struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+
+ if (nvdimm)
+ return 0;
+ if (cmd != ND_CMD_ARS_START)
+ return 0;
+
+ /*
+ * The kernel and userspace may race to initiate a scrub, but
+ * the scrub thread is prepared to lose that initial race. It
+ * just needs guarantees that any ARS it initiates are not
+ * interrupted by any intervening start requests from userspace.
+ */
+ if (work_busy(&acpi_desc->dwork.work))
+ return -EBUSY;
+
+ return 0;
+}
+
+/*
+ * Prevent security and firmware activate commands from being issued via
+ * ioctl.
+ */
+static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
+ struct nvdimm *nvdimm, unsigned int cmd, void *buf)
+{
+ struct nd_cmd_pkg *call_pkg = buf;
+ unsigned int func;
+
+ if (nvdimm && cmd == ND_CMD_CALL &&
+ call_pkg->nd_family == NVDIMM_FAMILY_INTEL) {
+ func = call_pkg->nd_command;
+ if (func > NVDIMM_CMD_MAX ||
+ (1 << func) & NVDIMM_INTEL_DENY_CMDMASK)
+ return -EOPNOTSUPP;
+ }
+
+ /* block all non-nfit bus commands */
+ if (!nvdimm && cmd == ND_CMD_CALL &&
+ call_pkg->nd_family != NVDIMM_BUS_FAMILY_NFIT)
+ return -EOPNOTSUPP;
+
+ return __acpi_nfit_clear_to_send(nd_desc, nvdimm, cmd);
+}
+
+int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc,
+ enum nfit_ars_state req_type)
+{
+ struct device *dev = acpi_desc->dev;
+ int scheduled = 0, busy = 0;
+ struct nfit_spa *nfit_spa;
+
+ mutex_lock(&acpi_desc->init_mutex);
+ if (test_bit(ARS_CANCEL, &acpi_desc->scrub_flags)) {
+ mutex_unlock(&acpi_desc->init_mutex);
+ return 0;
+ }
+
+ list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
+ int type = nfit_spa_type(nfit_spa->spa);
+
+ if (type != NFIT_SPA_PM && type != NFIT_SPA_VOLATILE)
+ continue;
+ if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
+ continue;
+
+ if (test_and_set_bit(req_type, &nfit_spa->ars_state))
+ busy++;
+ else
+ scheduled++;
+ }
+ if (scheduled) {
+ sched_ars(acpi_desc);
+ dev_dbg(dev, "ars_scan triggered\n");
+ }
+ mutex_unlock(&acpi_desc->init_mutex);
+
+ if (scheduled)
+ return 0;
+ if (busy)
+ return -EBUSY;
+ return -ENOTTY;
+}
+
+void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev)
+{
+ struct nvdimm_bus_descriptor *nd_desc;
+
+ dev_set_drvdata(dev, acpi_desc);
+ acpi_desc->dev = dev;
+ nd_desc = &acpi_desc->nd_desc;
+ nd_desc->provider_name = "ACPI.NFIT";
+ nd_desc->module = THIS_MODULE;
+ nd_desc->ndctl = acpi_nfit_ctl;
+ nd_desc->flush_probe = acpi_nfit_flush_probe;
+ nd_desc->clear_to_send = acpi_nfit_clear_to_send;
+ nd_desc->attr_groups = acpi_nfit_attribute_groups;
+
+ INIT_LIST_HEAD(&acpi_desc->spas);
+ INIT_LIST_HEAD(&acpi_desc->dcrs);
+ INIT_LIST_HEAD(&acpi_desc->bdws);
+ INIT_LIST_HEAD(&acpi_desc->idts);
+ INIT_LIST_HEAD(&acpi_desc->flushes);
+ INIT_LIST_HEAD(&acpi_desc->memdevs);
+ INIT_LIST_HEAD(&acpi_desc->dimms);
+ INIT_LIST_HEAD(&acpi_desc->list);
+ mutex_init(&acpi_desc->init_mutex);
+ acpi_desc->scrub_tmo = 1;
+ INIT_DELAYED_WORK(&acpi_desc->dwork, acpi_nfit_scrub);
+}
+EXPORT_SYMBOL_GPL(acpi_nfit_desc_init);
+
+static void acpi_nfit_put_table(void *table)
+{
+ acpi_put_table(table);
+}
+
+void acpi_nfit_shutdown(void *data)
+{
+ struct acpi_nfit_desc *acpi_desc = data;
+ struct device *bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus);
+
+ /*
+ * Destruct under acpi_desc_lock so that nfit_handle_mce does not
+ * race teardown
+ */
+ mutex_lock(&acpi_desc_lock);
+ list_del(&acpi_desc->list);
+ mutex_unlock(&acpi_desc_lock);
+
+ mutex_lock(&acpi_desc->init_mutex);
+ set_bit(ARS_CANCEL, &acpi_desc->scrub_flags);
+ mutex_unlock(&acpi_desc->init_mutex);
+ cancel_delayed_work_sync(&acpi_desc->dwork);
+
+ /*
+ * Bounce the nvdimm bus lock to make sure any in-flight
+ * acpi_nfit_ars_rescan() submissions have had a chance to
+ * either submit or see ->cancel set.
+ */
+ device_lock(bus_dev);
+ device_unlock(bus_dev);
+
+ flush_workqueue(nfit_wq);
+}
+EXPORT_SYMBOL_GPL(acpi_nfit_shutdown);
+
+static int acpi_nfit_add(struct acpi_device *adev)
+{
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_nfit_desc *acpi_desc;
+ struct device *dev = &adev->dev;
+ struct acpi_table_header *tbl;
+ acpi_status status = AE_OK;
+ acpi_size sz;
+ int rc = 0;
+
+ status = acpi_get_table(ACPI_SIG_NFIT, 0, &tbl);
+ if (ACPI_FAILURE(status)) {
+ /* The NVDIMM root device allows OS to trigger enumeration of
+ * NVDIMMs through NFIT at boot time and re-enumeration at
+ * root level via the _FIT method during runtime.
+ * This is ok to return 0 here, we could have an nvdimm
+ * hotplugged later and evaluate _FIT method which returns
+ * data in the format of a series of NFIT Structures.
+ */
+ dev_dbg(dev, "failed to find NFIT at startup\n");
+ return 0;
+ }
+
+ rc = devm_add_action_or_reset(dev, acpi_nfit_put_table, tbl);
+ if (rc)
+ return rc;
+ sz = tbl->length;
+
+ acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL);
+ if (!acpi_desc)
+ return -ENOMEM;
+ acpi_nfit_desc_init(acpi_desc, &adev->dev);
+
+ /* Save the acpi header for exporting the revision via sysfs */
+ acpi_desc->acpi_header = *tbl;
+
+ /* Evaluate _FIT and override with that if present */
+ status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf);
+ if (ACPI_SUCCESS(status) && buf.length > 0) {
+ union acpi_object *obj = buf.pointer;
+
+ if (obj->type == ACPI_TYPE_BUFFER)
+ rc = acpi_nfit_init(acpi_desc, obj->buffer.pointer,
+ obj->buffer.length);
+ else
+ dev_dbg(dev, "invalid type %d, ignoring _FIT\n",
+ (int) obj->type);
+ kfree(buf.pointer);
+ } else
+ /* skip over the lead-in header table */
+ rc = acpi_nfit_init(acpi_desc, (void *) tbl
+ + sizeof(struct acpi_table_nfit),
+ sz - sizeof(struct acpi_table_nfit));
+
+ 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;
+}
+
+static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle)
+{
+ struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev);
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ acpi_status status;
+ int ret;
+
+ if (!dev->driver) {
+ /* dev->driver may be null if we're being removed */
+ dev_dbg(dev, "no driver found for dev\n");
+ return;
+ }
+
+ if (!acpi_desc) {
+ acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL);
+ if (!acpi_desc)
+ return;
+ acpi_nfit_desc_init(acpi_desc, dev);
+ } else {
+ /*
+ * Finish previous registration before considering new
+ * regions.
+ */
+ flush_workqueue(nfit_wq);
+ }
+
+ /* Evaluate _FIT */
+ status = acpi_evaluate_object(handle, "_FIT", NULL, &buf);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "failed to evaluate _FIT\n");
+ return;
+ }
+
+ obj = buf.pointer;
+ if (obj->type == ACPI_TYPE_BUFFER) {
+ ret = acpi_nfit_init(acpi_desc, obj->buffer.pointer,
+ obj->buffer.length);
+ if (ret)
+ dev_err(dev, "failed to merge updated NFIT\n");
+ } else
+ dev_err(dev, "Invalid _FIT\n");
+ kfree(buf.pointer);
+}
+
+static void acpi_nfit_uc_error_notify(struct device *dev, acpi_handle handle)
+{
+ struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev);
+
+ if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON)
+ acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG);
+ else
+ acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_SHORT);
+}
+
+void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event)
+{
+ dev_dbg(dev, "event: 0x%x\n", event);
+
+ switch (event) {
+ case NFIT_NOTIFY_UPDATE:
+ return acpi_nfit_update_notify(dev, handle);
+ case NFIT_NOTIFY_UC_MEMORY_ERROR:
+ return acpi_nfit_uc_error_notify(dev, handle);
+ default:
+ return;
+ }
+}
+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 },
+};
+MODULE_DEVICE_TABLE(acpi, acpi_nfit_ids);
+
+static struct acpi_driver acpi_nfit_driver = {
+ .name = KBUILD_MODNAME,
+ .ids = acpi_nfit_ids,
+ .ops = {
+ .add = acpi_nfit_add,
+ .remove = acpi_nfit_remove,
+ .notify = acpi_nfit_notify,
+ },
+};
+
+static __init int nfit_init(void)
+{
+ int ret;
+
+ 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_control_region) != 80);
+ BUILD_BUG_ON(sizeof(struct acpi_nfit_data_region) != 40);
+ BUILD_BUG_ON(sizeof(struct acpi_nfit_capabilities) != 16);
+
+ guid_parse(UUID_VOLATILE_MEMORY, &nfit_uuid[NFIT_SPA_VOLATILE]);
+ guid_parse(UUID_PERSISTENT_MEMORY, &nfit_uuid[NFIT_SPA_PM]);
+ guid_parse(UUID_CONTROL_REGION, &nfit_uuid[NFIT_SPA_DCR]);
+ guid_parse(UUID_DATA_REGION, &nfit_uuid[NFIT_SPA_BDW]);
+ guid_parse(UUID_VOLATILE_VIRTUAL_DISK, &nfit_uuid[NFIT_SPA_VDISK]);
+ guid_parse(UUID_VOLATILE_VIRTUAL_CD, &nfit_uuid[NFIT_SPA_VCD]);
+ guid_parse(UUID_PERSISTENT_VIRTUAL_DISK, &nfit_uuid[NFIT_SPA_PDISK]);
+ guid_parse(UUID_PERSISTENT_VIRTUAL_CD, &nfit_uuid[NFIT_SPA_PCD]);
+ guid_parse(UUID_NFIT_BUS, &nfit_uuid[NFIT_DEV_BUS]);
+ guid_parse(UUID_NFIT_DIMM, &nfit_uuid[NFIT_DEV_DIMM]);
+ guid_parse(UUID_NFIT_DIMM_N_HPE1, &nfit_uuid[NFIT_DEV_DIMM_N_HPE1]);
+ guid_parse(UUID_NFIT_DIMM_N_HPE2, &nfit_uuid[NFIT_DEV_DIMM_N_HPE2]);
+ guid_parse(UUID_NFIT_DIMM_N_MSFT, &nfit_uuid[NFIT_DEV_DIMM_N_MSFT]);
+ guid_parse(UUID_NFIT_DIMM_N_HYPERV, &nfit_uuid[NFIT_DEV_DIMM_N_HYPERV]);
+ guid_parse(UUID_INTEL_BUS, &nfit_uuid[NFIT_BUS_INTEL]);
+
+ nfit_wq = create_singlethread_workqueue("nfit");
+ if (!nfit_wq)
+ return -ENOMEM;
+
+ nfit_mce_register();
+ ret = acpi_bus_register_driver(&acpi_nfit_driver);
+ if (ret) {
+ nfit_mce_unregister();
+ destroy_workqueue(nfit_wq);
+ }
+
+ return ret;
+
+}
+
+static __exit void nfit_exit(void)
+{
+ nfit_mce_unregister();
+ acpi_bus_unregister_driver(&acpi_nfit_driver);
+ destroy_workqueue(nfit_wq);
+ WARN_ON(!list_empty(&acpi_descs));
+}
+
+module_init(nfit_init);
+module_exit(nfit_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Intel Corporation");
diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c
new file mode 100644
index 000000000..8dd792a55
--- /dev/null
+++ b/drivers/acpi/nfit/intel.c
@@ -0,0 +1,777 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2018 Intel Corporation. All rights reserved. */
+#include <linux/libnvdimm.h>
+#include <linux/ndctl.h>
+#include <linux/acpi.h>
+#include <asm/smp.h>
+#include "intel.h"
+#include "nfit.h"
+
+static ssize_t firmware_activate_noidle_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(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, "%s\n", acpi_desc->fwa_noidle ? "Y" : "N");
+}
+
+static ssize_t firmware_activate_noidle_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
+ struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
+ struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+ ssize_t rc;
+ bool val;
+
+ rc = kstrtobool(buf, &val);
+ if (rc)
+ return rc;
+ if (val != acpi_desc->fwa_noidle)
+ acpi_desc->fwa_cap = NVDIMM_FWA_CAP_INVALID;
+ acpi_desc->fwa_noidle = val;
+ return size;
+}
+DEVICE_ATTR_RW(firmware_activate_noidle);
+
+bool intel_fwa_supported(struct nvdimm_bus *nvdimm_bus)
+{
+ struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
+ struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+ unsigned long *mask;
+
+ if (!test_bit(NVDIMM_BUS_FAMILY_INTEL, &nd_desc->bus_family_mask))
+ return false;
+
+ mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL];
+ return *mask == NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK;
+}
+
+static unsigned long intel_security_flags(struct nvdimm *nvdimm,
+ enum nvdimm_passphrase_type ptype)
+{
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ unsigned long security_flags = 0;
+ struct {
+ struct nd_cmd_pkg pkg;
+ struct nd_intel_get_security_state cmd;
+ } nd_cmd = {
+ .pkg = {
+ .nd_command = NVDIMM_INTEL_GET_SECURITY_STATE,
+ .nd_family = NVDIMM_FAMILY_INTEL,
+ .nd_size_out =
+ sizeof(struct nd_intel_get_security_state),
+ .nd_fw_size =
+ sizeof(struct nd_intel_get_security_state),
+ },
+ };
+ int rc;
+
+ if (!test_bit(NVDIMM_INTEL_GET_SECURITY_STATE, &nfit_mem->dsm_mask))
+ return 0;
+
+ /*
+ * Short circuit the state retrieval while we are doing overwrite.
+ * The DSM spec states that the security state is indeterminate
+ * until the overwrite DSM completes.
+ */
+ if (nvdimm_in_overwrite(nvdimm) && ptype == NVDIMM_USER)
+ return BIT(NVDIMM_SECURITY_OVERWRITE);
+
+ rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
+ if (rc < 0 || nd_cmd.cmd.status) {
+ pr_err("%s: security state retrieval failed (%d:%#x)\n",
+ nvdimm_name(nvdimm), rc, nd_cmd.cmd.status);
+ return 0;
+ }
+
+ /* check and see if security is enabled and locked */
+ if (ptype == NVDIMM_MASTER) {
+ if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_ENABLED)
+ set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
+ else
+ set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
+ if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_PLIMIT)
+ set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
+ return security_flags;
+ }
+
+ if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED)
+ return 0;
+
+ if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) {
+ if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN ||
+ nd_cmd.cmd.state & ND_INTEL_SEC_STATE_PLIMIT)
+ set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
+
+ if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED)
+ set_bit(NVDIMM_SECURITY_LOCKED, &security_flags);
+ else
+ set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
+ } else
+ set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
+
+ return security_flags;
+}
+
+static int intel_security_freeze(struct nvdimm *nvdimm)
+{
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ struct {
+ struct nd_cmd_pkg pkg;
+ struct nd_intel_freeze_lock cmd;
+ } nd_cmd = {
+ .pkg = {
+ .nd_command = NVDIMM_INTEL_FREEZE_LOCK,
+ .nd_family = NVDIMM_FAMILY_INTEL,
+ .nd_size_out = ND_INTEL_STATUS_SIZE,
+ .nd_fw_size = ND_INTEL_STATUS_SIZE,
+ },
+ };
+ int rc;
+
+ if (!test_bit(NVDIMM_INTEL_FREEZE_LOCK, &nfit_mem->dsm_mask))
+ return -ENOTTY;
+
+ rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
+ if (rc < 0)
+ return rc;
+ if (nd_cmd.cmd.status)
+ return -EIO;
+ return 0;
+}
+
+static int intel_security_change_key(struct nvdimm *nvdimm,
+ const struct nvdimm_key_data *old_data,
+ const struct nvdimm_key_data *new_data,
+ enum nvdimm_passphrase_type ptype)
+{
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ unsigned int cmd = ptype == NVDIMM_MASTER ?
+ NVDIMM_INTEL_SET_MASTER_PASSPHRASE :
+ NVDIMM_INTEL_SET_PASSPHRASE;
+ struct {
+ struct nd_cmd_pkg pkg;
+ struct nd_intel_set_passphrase cmd;
+ } nd_cmd = {
+ .pkg = {
+ .nd_family = NVDIMM_FAMILY_INTEL,
+ .nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2,
+ .nd_size_out = ND_INTEL_STATUS_SIZE,
+ .nd_fw_size = ND_INTEL_STATUS_SIZE,
+ .nd_command = cmd,
+ },
+ };
+ int rc;
+
+ if (!test_bit(cmd, &nfit_mem->dsm_mask))
+ return -ENOTTY;
+
+ memcpy(nd_cmd.cmd.old_pass, old_data->data,
+ sizeof(nd_cmd.cmd.old_pass));
+ memcpy(nd_cmd.cmd.new_pass, new_data->data,
+ sizeof(nd_cmd.cmd.new_pass));
+ rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
+ if (rc < 0)
+ return rc;
+
+ switch (nd_cmd.cmd.status) {
+ case 0:
+ return 0;
+ case ND_INTEL_STATUS_INVALID_PASS:
+ return -EINVAL;
+ case ND_INTEL_STATUS_NOT_SUPPORTED:
+ return -EOPNOTSUPP;
+ case ND_INTEL_STATUS_INVALID_STATE:
+ default:
+ return -EIO;
+ }
+}
+
+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;
+ struct nd_intel_unlock_unit cmd;
+ } nd_cmd = {
+ .pkg = {
+ .nd_command = NVDIMM_INTEL_UNLOCK_UNIT,
+ .nd_family = NVDIMM_FAMILY_INTEL,
+ .nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
+ .nd_size_out = ND_INTEL_STATUS_SIZE,
+ .nd_fw_size = ND_INTEL_STATUS_SIZE,
+ },
+ };
+ int rc;
+
+ if (!test_bit(NVDIMM_INTEL_UNLOCK_UNIT, &nfit_mem->dsm_mask))
+ return -ENOTTY;
+
+ memcpy(nd_cmd.cmd.passphrase, key_data->data,
+ sizeof(nd_cmd.cmd.passphrase));
+ rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
+ if (rc < 0)
+ return rc;
+ switch (nd_cmd.cmd.status) {
+ case 0:
+ break;
+ case ND_INTEL_STATUS_INVALID_PASS:
+ return -EINVAL;
+ default:
+ return -EIO;
+ }
+
+ /* DIMM unlocked, invalidate all CPU caches before we read it */
+ nvdimm_invalidate_cache();
+
+ return 0;
+}
+
+static int intel_security_disable(struct nvdimm *nvdimm,
+ const struct nvdimm_key_data *key_data)
+{
+ int rc;
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ struct {
+ struct nd_cmd_pkg pkg;
+ struct nd_intel_disable_passphrase cmd;
+ } nd_cmd = {
+ .pkg = {
+ .nd_command = NVDIMM_INTEL_DISABLE_PASSPHRASE,
+ .nd_family = NVDIMM_FAMILY_INTEL,
+ .nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
+ .nd_size_out = ND_INTEL_STATUS_SIZE,
+ .nd_fw_size = ND_INTEL_STATUS_SIZE,
+ },
+ };
+
+ if (!test_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE, &nfit_mem->dsm_mask))
+ return -ENOTTY;
+
+ memcpy(nd_cmd.cmd.passphrase, key_data->data,
+ sizeof(nd_cmd.cmd.passphrase));
+ rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
+ if (rc < 0)
+ return rc;
+
+ switch (nd_cmd.cmd.status) {
+ case 0:
+ break;
+ case ND_INTEL_STATUS_INVALID_PASS:
+ return -EINVAL;
+ case ND_INTEL_STATUS_INVALID_STATE:
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused intel_security_erase(struct nvdimm *nvdimm,
+ const struct nvdimm_key_data *key,
+ enum nvdimm_passphrase_type ptype)
+{
+ int rc;
+ 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;
+ struct nd_intel_secure_erase cmd;
+ } nd_cmd = {
+ .pkg = {
+ .nd_family = NVDIMM_FAMILY_INTEL,
+ .nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
+ .nd_size_out = ND_INTEL_STATUS_SIZE,
+ .nd_fw_size = ND_INTEL_STATUS_SIZE,
+ .nd_command = cmd,
+ },
+ };
+
+ 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);
+ if (rc < 0)
+ return rc;
+
+ switch (nd_cmd.cmd.status) {
+ case 0:
+ break;
+ case ND_INTEL_STATUS_NOT_SUPPORTED:
+ return -EOPNOTSUPP;
+ case ND_INTEL_STATUS_INVALID_PASS:
+ return -EINVAL;
+ case ND_INTEL_STATUS_INVALID_STATE:
+ default:
+ return -ENXIO;
+ }
+
+ /* DIMM erased, invalidate all CPU caches before we read it */
+ nvdimm_invalidate_cache();
+ return 0;
+}
+
+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;
+ struct nd_intel_query_overwrite cmd;
+ } nd_cmd = {
+ .pkg = {
+ .nd_command = NVDIMM_INTEL_QUERY_OVERWRITE,
+ .nd_family = NVDIMM_FAMILY_INTEL,
+ .nd_size_out = ND_INTEL_STATUS_SIZE,
+ .nd_fw_size = ND_INTEL_STATUS_SIZE,
+ },
+ };
+
+ if (!test_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &nfit_mem->dsm_mask))
+ return -ENOTTY;
+
+ rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
+ if (rc < 0)
+ return rc;
+
+ switch (nd_cmd.cmd.status) {
+ case 0:
+ break;
+ case ND_INTEL_STATUS_OQUERY_INPROGRESS:
+ return -EBUSY;
+ default:
+ return -ENXIO;
+ }
+
+ /* flush all cache before we make the nvdimms available */
+ nvdimm_invalidate_cache();
+ return 0;
+}
+
+static int __maybe_unused intel_security_overwrite(struct nvdimm *nvdimm,
+ const struct nvdimm_key_data *nkey)
+{
+ int rc;
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ struct {
+ struct nd_cmd_pkg pkg;
+ struct nd_intel_overwrite cmd;
+ } nd_cmd = {
+ .pkg = {
+ .nd_command = NVDIMM_INTEL_OVERWRITE,
+ .nd_family = NVDIMM_FAMILY_INTEL,
+ .nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
+ .nd_size_out = ND_INTEL_STATUS_SIZE,
+ .nd_fw_size = ND_INTEL_STATUS_SIZE,
+ },
+ };
+
+ 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);
+ if (rc < 0)
+ return rc;
+
+ switch (nd_cmd.cmd.status) {
+ case 0:
+ return 0;
+ case ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED:
+ return -ENOTSUPP;
+ case ND_INTEL_STATUS_INVALID_PASS:
+ return -EINVAL;
+ case ND_INTEL_STATUS_INVALID_STATE:
+ default:
+ return -ENXIO;
+ }
+}
+
+/*
+ * 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,
+ .change_key = intel_security_change_key,
+ .disable = intel_security_disable,
+#ifdef CONFIG_X86
+ .unlock = intel_security_unlock,
+ .erase = intel_security_erase,
+ .overwrite = intel_security_overwrite,
+ .query_overwrite = intel_security_query_overwrite,
+#endif
+};
+
+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;
+ struct nd_intel_bus_fw_activate_businfo cmd;
+ } nd_cmd = {
+ .pkg = {
+ .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO,
+ .nd_family = NVDIMM_BUS_FAMILY_INTEL,
+ .nd_size_out =
+ sizeof(struct nd_intel_bus_fw_activate_businfo),
+ .nd_fw_size =
+ sizeof(struct nd_intel_bus_fw_activate_businfo),
+ },
+ };
+ int rc;
+
+ rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd),
+ NULL);
+ *info = nd_cmd.cmd;
+ return rc;
+}
+
+/* The fw_ops expect to be called with the nvdimm_bus_lock() held */
+static enum nvdimm_fwa_state intel_bus_fwa_state(
+ struct nvdimm_bus_descriptor *nd_desc)
+{
+ struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+ struct nd_intel_bus_fw_activate_businfo info;
+ struct device *dev = acpi_desc->dev;
+ enum nvdimm_fwa_state state;
+ int rc;
+
+ /*
+ * It should not be possible for platform firmware to return
+ * busy because activate is a synchronous operation. Treat it
+ * similar to invalid, i.e. always refresh / poll the status.
+ */
+ switch (acpi_desc->fwa_state) {
+ case NVDIMM_FWA_INVALID:
+ case NVDIMM_FWA_BUSY:
+ break;
+ default:
+ /* check if capability needs to be refreshed */
+ if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID)
+ break;
+ return acpi_desc->fwa_state;
+ }
+
+ /* Refresh with platform firmware */
+ rc = intel_bus_fwa_businfo(nd_desc, &info);
+ if (rc)
+ return NVDIMM_FWA_INVALID;
+
+ switch (info.state) {
+ case ND_INTEL_FWA_IDLE:
+ state = NVDIMM_FWA_IDLE;
+ break;
+ case ND_INTEL_FWA_BUSY:
+ state = NVDIMM_FWA_BUSY;
+ break;
+ case ND_INTEL_FWA_ARMED:
+ if (info.activate_tmo > info.max_quiesce_tmo)
+ state = NVDIMM_FWA_ARM_OVERFLOW;
+ else
+ state = NVDIMM_FWA_ARMED;
+ break;
+ default:
+ dev_err_once(dev, "invalid firmware activate state %d\n",
+ info.state);
+ return NVDIMM_FWA_INVALID;
+ }
+
+ /*
+ * Capability data is available in the same payload as state. It
+ * is expected to be static.
+ */
+ if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID) {
+ if (info.capability & ND_INTEL_BUS_FWA_CAP_FWQUIESCE)
+ acpi_desc->fwa_cap = NVDIMM_FWA_CAP_QUIESCE;
+ else if (info.capability & ND_INTEL_BUS_FWA_CAP_OSQUIESCE) {
+ /*
+ * Skip hibernate cycle by default if platform
+ * indicates that it does not need devices to be
+ * quiesced.
+ */
+ acpi_desc->fwa_cap = NVDIMM_FWA_CAP_LIVE;
+ } else
+ acpi_desc->fwa_cap = NVDIMM_FWA_CAP_NONE;
+ }
+
+ acpi_desc->fwa_state = state;
+
+ return state;
+}
+
+static enum nvdimm_fwa_capability intel_bus_fwa_capability(
+ struct nvdimm_bus_descriptor *nd_desc)
+{
+ struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+
+ if (acpi_desc->fwa_cap > NVDIMM_FWA_CAP_INVALID)
+ return acpi_desc->fwa_cap;
+
+ if (intel_bus_fwa_state(nd_desc) > NVDIMM_FWA_INVALID)
+ return acpi_desc->fwa_cap;
+
+ return NVDIMM_FWA_CAP_INVALID;
+}
+
+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;
+ 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),
+ },
+ /*
+ * 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,
+ },
+ };
+ int rc;
+
+ switch (intel_bus_fwa_state(nd_desc)) {
+ case NVDIMM_FWA_ARMED:
+ case NVDIMM_FWA_ARM_OVERFLOW:
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd),
+ NULL);
+
+ /*
+ * Whether the command succeeded, or failed, the agent checking
+ * for the result needs to query the DIMMs individually.
+ * Increment the activation count to invalidate all the DIMM
+ * states at once (it's otherwise not possible to take
+ * acpi_desc->init_mutex in this context)
+ */
+ acpi_desc->fwa_state = NVDIMM_FWA_INVALID;
+ acpi_desc->fwa_count++;
+
+ dev_dbg(acpi_desc->dev, "result: %d\n", rc);
+
+ return rc;
+}
+
+static const struct nvdimm_bus_fw_ops __intel_bus_fw_ops = {
+ .activate_state = intel_bus_fwa_state,
+ .capability = intel_bus_fwa_capability,
+ .activate = intel_bus_fwa_activate,
+};
+
+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;
+ struct nd_intel_fw_activate_dimminfo cmd;
+ } nd_cmd = {
+ .pkg = {
+ .nd_command = NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO,
+ .nd_family = NVDIMM_FAMILY_INTEL,
+ .nd_size_out =
+ sizeof(struct nd_intel_fw_activate_dimminfo),
+ .nd_fw_size =
+ sizeof(struct nd_intel_fw_activate_dimminfo),
+ },
+ };
+ int rc;
+
+ rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
+ *info = nd_cmd.cmd;
+ return rc;
+}
+
+static enum nvdimm_fwa_state intel_fwa_state(struct nvdimm *nvdimm)
+{
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
+ struct nd_intel_fw_activate_dimminfo info;
+ int rc;
+
+ /*
+ * Similar to the bus state, since activate is synchronous the
+ * busy state should resolve within the context of 'activate'.
+ */
+ switch (nfit_mem->fwa_state) {
+ case NVDIMM_FWA_INVALID:
+ case NVDIMM_FWA_BUSY:
+ break;
+ default:
+ /* If no activations occurred the old state is still valid */
+ if (nfit_mem->fwa_count == acpi_desc->fwa_count)
+ return nfit_mem->fwa_state;
+ }
+
+ rc = intel_fwa_dimminfo(nvdimm, &info);
+ if (rc)
+ return NVDIMM_FWA_INVALID;
+
+ switch (info.state) {
+ case ND_INTEL_FWA_IDLE:
+ nfit_mem->fwa_state = NVDIMM_FWA_IDLE;
+ break;
+ case ND_INTEL_FWA_BUSY:
+ nfit_mem->fwa_state = NVDIMM_FWA_BUSY;
+ break;
+ case ND_INTEL_FWA_ARMED:
+ nfit_mem->fwa_state = NVDIMM_FWA_ARMED;
+ break;
+ default:
+ nfit_mem->fwa_state = NVDIMM_FWA_INVALID;
+ break;
+ }
+
+ switch (info.result) {
+ case ND_INTEL_DIMM_FWA_NONE:
+ nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NONE;
+ break;
+ case ND_INTEL_DIMM_FWA_SUCCESS:
+ nfit_mem->fwa_result = NVDIMM_FWA_RESULT_SUCCESS;
+ break;
+ case ND_INTEL_DIMM_FWA_NOTSTAGED:
+ nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NOTSTAGED;
+ break;
+ case ND_INTEL_DIMM_FWA_NEEDRESET:
+ nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NEEDRESET;
+ break;
+ case ND_INTEL_DIMM_FWA_MEDIAFAILED:
+ case ND_INTEL_DIMM_FWA_ABORT:
+ case ND_INTEL_DIMM_FWA_NOTSUPP:
+ case ND_INTEL_DIMM_FWA_ERROR:
+ default:
+ nfit_mem->fwa_result = NVDIMM_FWA_RESULT_FAIL;
+ break;
+ }
+
+ nfit_mem->fwa_count = acpi_desc->fwa_count;
+
+ return nfit_mem->fwa_state;
+}
+
+static enum nvdimm_fwa_result intel_fwa_result(struct nvdimm *nvdimm)
+{
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+ struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
+
+ if (nfit_mem->fwa_count == acpi_desc->fwa_count
+ && nfit_mem->fwa_result > NVDIMM_FWA_RESULT_INVALID)
+ return nfit_mem->fwa_result;
+
+ if (intel_fwa_state(nvdimm) > NVDIMM_FWA_INVALID)
+ return nfit_mem->fwa_result;
+
+ return NVDIMM_FWA_RESULT_INVALID;
+}
+
+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;
+ 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,
+ },
+ };
+ int rc;
+
+ switch (intel_fwa_state(nvdimm)) {
+ case NVDIMM_FWA_INVALID:
+ return -ENXIO;
+ case NVDIMM_FWA_BUSY:
+ return -EBUSY;
+ case NVDIMM_FWA_IDLE:
+ if (arm == NVDIMM_FWA_DISARM)
+ return 0;
+ break;
+ case NVDIMM_FWA_ARMED:
+ if (arm == NVDIMM_FWA_ARM)
+ return 0;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ /*
+ * Invalidate the bus-level state, now that we're committed to
+ * changing the 'arm' state.
+ */
+ acpi_desc->fwa_state = NVDIMM_FWA_INVALID;
+ nfit_mem->fwa_state = NVDIMM_FWA_INVALID;
+
+ rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
+
+ dev_dbg(acpi_desc->dev, "%s result: %d\n", arm == NVDIMM_FWA_ARM
+ ? "arm" : "disarm", rc);
+ return rc;
+}
+
+static const struct nvdimm_fw_ops __intel_fw_ops = {
+ .activate_state = intel_fwa_state,
+ .activate_result = intel_fwa_result,
+ .arm = intel_fwa_arm,
+};
+
+const struct nvdimm_fw_ops *intel_fw_ops = &__intel_fw_ops;
diff --git a/drivers/acpi/nfit/intel.h b/drivers/acpi/nfit/intel.h
new file mode 100644
index 000000000..b768234cc
--- /dev/null
+++ b/drivers/acpi/nfit/intel.h
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ * Intel specific definitions for NVDIMM Firmware Interface Table - NFIT
+ */
+#ifndef _NFIT_INTEL_H_
+#define _NFIT_INTEL_H_
+
+#define ND_INTEL_SMART 1
+
+#define ND_INTEL_SMART_SHUTDOWN_COUNT_VALID (1 << 5)
+#define ND_INTEL_SMART_SHUTDOWN_VALID (1 << 10)
+
+struct nd_intel_smart {
+ u32 status;
+ union {
+ struct {
+ u32 flags;
+ u8 reserved0[4];
+ u8 health;
+ u8 spares;
+ u8 life_used;
+ u8 alarm_flags;
+ u16 media_temperature;
+ u16 ctrl_temperature;
+ u32 shutdown_count;
+ u8 ait_status;
+ u16 pmic_temperature;
+ u8 reserved1[8];
+ u8 shutdown_state;
+ u32 vendor_size;
+ u8 vendor_data[92];
+ } __packed;
+ u8 data[128];
+ };
+} __packed;
+
+extern const struct nvdimm_security_ops *intel_security_ops;
+
+#define ND_INTEL_STATUS_SIZE 4
+#define ND_INTEL_PASSPHRASE_SIZE 32
+
+#define ND_INTEL_STATUS_NOT_SUPPORTED 1
+#define ND_INTEL_STATUS_RETRY 5
+#define ND_INTEL_STATUS_NOT_READY 9
+#define ND_INTEL_STATUS_INVALID_STATE 10
+#define ND_INTEL_STATUS_INVALID_PASS 11
+#define ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED 0x10007
+#define ND_INTEL_STATUS_OQUERY_INPROGRESS 0x10007
+#define ND_INTEL_STATUS_OQUERY_SEQUENCE_ERR 0x20007
+
+#define ND_INTEL_SEC_STATE_ENABLED 0x02
+#define ND_INTEL_SEC_STATE_LOCKED 0x04
+#define ND_INTEL_SEC_STATE_FROZEN 0x08
+#define ND_INTEL_SEC_STATE_PLIMIT 0x10
+#define ND_INTEL_SEC_STATE_UNSUPPORTED 0x20
+#define ND_INTEL_SEC_STATE_OVERWRITE 0x40
+
+#define ND_INTEL_SEC_ESTATE_ENABLED 0x01
+#define ND_INTEL_SEC_ESTATE_PLIMIT 0x02
+
+struct nd_intel_get_security_state {
+ u32 status;
+ u8 extended_state;
+ u8 reserved[3];
+ u8 state;
+ u8 reserved1[3];
+} __packed;
+
+struct nd_intel_set_passphrase {
+ u8 old_pass[ND_INTEL_PASSPHRASE_SIZE];
+ u8 new_pass[ND_INTEL_PASSPHRASE_SIZE];
+ u32 status;
+} __packed;
+
+struct nd_intel_unlock_unit {
+ u8 passphrase[ND_INTEL_PASSPHRASE_SIZE];
+ u32 status;
+} __packed;
+
+struct nd_intel_disable_passphrase {
+ u8 passphrase[ND_INTEL_PASSPHRASE_SIZE];
+ u32 status;
+} __packed;
+
+struct nd_intel_freeze_lock {
+ u32 status;
+} __packed;
+
+struct nd_intel_secure_erase {
+ u8 passphrase[ND_INTEL_PASSPHRASE_SIZE];
+ u32 status;
+} __packed;
+
+struct nd_intel_overwrite {
+ u8 passphrase[ND_INTEL_PASSPHRASE_SIZE];
+ u32 status;
+} __packed;
+
+struct nd_intel_query_overwrite {
+ u32 status;
+} __packed;
+
+struct nd_intel_set_master_passphrase {
+ u8 old_pass[ND_INTEL_PASSPHRASE_SIZE];
+ u8 new_pass[ND_INTEL_PASSPHRASE_SIZE];
+ u32 status;
+} __packed;
+
+struct nd_intel_master_secure_erase {
+ u8 passphrase[ND_INTEL_PASSPHRASE_SIZE];
+ u32 status;
+} __packed;
+
+#define ND_INTEL_FWA_IDLE 0
+#define ND_INTEL_FWA_ARMED 1
+#define ND_INTEL_FWA_BUSY 2
+
+#define ND_INTEL_DIMM_FWA_NONE 0
+#define ND_INTEL_DIMM_FWA_NOTSTAGED 1
+#define ND_INTEL_DIMM_FWA_SUCCESS 2
+#define ND_INTEL_DIMM_FWA_NEEDRESET 3
+#define ND_INTEL_DIMM_FWA_MEDIAFAILED 4
+#define ND_INTEL_DIMM_FWA_ABORT 5
+#define ND_INTEL_DIMM_FWA_NOTSUPP 6
+#define ND_INTEL_DIMM_FWA_ERROR 7
+
+struct nd_intel_fw_activate_dimminfo {
+ u32 status;
+ u16 result;
+ u8 state;
+ u8 reserved[7];
+} __packed;
+
+#define ND_INTEL_DIMM_FWA_ARM 1
+#define ND_INTEL_DIMM_FWA_DISARM 0
+
+struct nd_intel_fw_activate_arm {
+ u8 activate_arm;
+ u32 status;
+} __packed;
+
+/* Root device command payloads */
+#define ND_INTEL_BUS_FWA_CAP_FWQUIESCE (1 << 0)
+#define ND_INTEL_BUS_FWA_CAP_OSQUIESCE (1 << 1)
+#define ND_INTEL_BUS_FWA_CAP_RESET (1 << 2)
+
+struct nd_intel_bus_fw_activate_businfo {
+ u32 status;
+ u16 reserved;
+ u8 state;
+ u8 capability;
+ u64 activate_tmo;
+ u64 cpu_quiesce_tmo;
+ u64 io_quiesce_tmo;
+ u64 max_quiesce_tmo;
+} __packed;
+
+#define ND_INTEL_BUS_FWA_STATUS_NOARM (6 | 1 << 16)
+#define ND_INTEL_BUS_FWA_STATUS_BUSY (6 | 2 << 16)
+#define ND_INTEL_BUS_FWA_STATUS_NOFW (6 | 3 << 16)
+#define ND_INTEL_BUS_FWA_STATUS_TMO (6 | 4 << 16)
+#define ND_INTEL_BUS_FWA_STATUS_NOIDLE (6 | 5 << 16)
+#define ND_INTEL_BUS_FWA_STATUS_ABORT (6 | 6 << 16)
+
+#define ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE (0)
+#define ND_INTEL_BUS_FWA_IODEV_OS_IDLE (1)
+struct nd_intel_bus_fw_activate {
+ u8 iodev_state;
+ u32 status;
+} __packed;
+
+extern const struct nvdimm_fw_ops *intel_fw_ops;
+extern const struct nvdimm_bus_fw_ops *intel_bus_fw_ops;
+#endif
diff --git a/drivers/acpi/nfit/mce.c b/drivers/acpi/nfit/mce.c
new file mode 100644
index 000000000..d48a388b7
--- /dev/null
+++ b/drivers/acpi/nfit/mce.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NFIT - Machine Check Handler
+ *
+ * Copyright(c) 2013-2016 Intel Corporation. All rights reserved.
+ */
+#include <linux/notifier.h>
+#include <linux/acpi.h>
+#include <linux/nd.h>
+#include <asm/mce.h>
+#include "nfit.h"
+
+static int nfit_handle_mce(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct mce *mce = (struct mce *)data;
+ struct acpi_nfit_desc *acpi_desc;
+ struct nfit_spa *nfit_spa;
+
+ /* We only care about uncorrectable memory errors */
+ if (!mce_is_memory_error(mce) || mce_is_correctable(mce))
+ return NOTIFY_DONE;
+
+ /* Verify the address reported in the MCE is valid. */
+ if (!mce_usable_address(mce))
+ return NOTIFY_DONE;
+
+ /*
+ * mce->addr contains the physical addr accessed that caused the
+ * machine check. We need to walk through the list of NFITs, and see
+ * if any of them matches that address, and only then start a scrub.
+ */
+ mutex_lock(&acpi_desc_lock);
+ list_for_each_entry(acpi_desc, &acpi_descs, list) {
+ unsigned int align = 1UL << MCI_MISC_ADDR_LSB(mce->misc);
+ struct device *dev = acpi_desc->dev;
+ int found_match = 0;
+
+ mutex_lock(&acpi_desc->init_mutex);
+ list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
+ struct acpi_nfit_system_address *spa = nfit_spa->spa;
+
+ if (nfit_spa_type(spa) != NFIT_SPA_PM)
+ continue;
+ /* find the spa that covers the mce addr */
+ if (spa->address > mce->addr)
+ continue;
+ if ((spa->address + spa->length - 1) < mce->addr)
+ continue;
+ found_match = 1;
+ dev_dbg(dev, "addr in SPA %d (0x%llx, 0x%llx)\n",
+ spa->range_index, spa->address, spa->length);
+ /*
+ * We can break at the first match because we're going
+ * to rescan all the SPA ranges. There shouldn't be any
+ * aliasing anyway.
+ */
+ break;
+ }
+ mutex_unlock(&acpi_desc->init_mutex);
+
+ if (!found_match)
+ continue;
+
+ /* If this fails due to an -ENOMEM, there is little we can do */
+ nvdimm_bus_add_badrange(acpi_desc->nvdimm_bus,
+ ALIGN_DOWN(mce->addr, align), align);
+ nvdimm_region_notify(nfit_spa->nd_region,
+ NVDIMM_REVALIDATE_POISON);
+
+ if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) {
+ /*
+ * We can ignore an -EBUSY here because if an ARS is
+ * already in progress, just let that be the last
+ * authoritative one
+ */
+ acpi_nfit_ars_rescan(acpi_desc, 0);
+ }
+ mce->kflags |= MCE_HANDLED_NFIT;
+ break;
+ }
+
+ mutex_unlock(&acpi_desc_lock);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block nfit_mce_dec = {
+ .notifier_call = nfit_handle_mce,
+ .priority = MCE_PRIO_NFIT,
+};
+
+void nfit_mce_register(void)
+{
+ mce_register_decode_chain(&nfit_mce_dec);
+}
+
+void nfit_mce_unregister(void)
+{
+ mce_unregister_decode_chain(&nfit_mce_dec);
+}
diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h
new file mode 100644
index 000000000..6023ad618
--- /dev/null
+++ b/drivers/acpi/nfit/nfit.h
@@ -0,0 +1,350 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NVDIMM Firmware Interface Table - NFIT
+ *
+ * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
+ */
+#ifndef __NFIT_H__
+#define __NFIT_H__
+#include <linux/workqueue.h>
+#include <linux/libnvdimm.h>
+#include <linux/ndctl.h>
+#include <linux/types.h>
+#include <linux/acpi.h>
+#include <acpi/acuuid.h>
+
+/* ACPI 6.1 */
+#define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba"
+
+/* https://pmem.io/documents/NVDIMM_DSM_Interface-V1.6.pdf */
+#define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66"
+#define UUID_INTEL_BUS "c7d8acd4-2df8-4b82-9f65-a325335af149"
+
+/* https://github.com/HewlettPackard/hpe-nvm/blob/master/Documentation/ */
+#define UUID_NFIT_DIMM_N_HPE1 "9002c334-acf3-4c0e-9642-a235f0d53bc6"
+#define UUID_NFIT_DIMM_N_HPE2 "5008664b-b758-41a0-a03c-27c2f2d04f7e"
+
+/* https://msdn.microsoft.com/library/windows/hardware/mt604741 */
+#define UUID_NFIT_DIMM_N_MSFT "1ee68b36-d4bd-4a1a-9a16-4f8e53d46e05"
+
+/* http://www.uefi.org/RFIC_LIST (see "Virtual NVDIMM 0x1901") */
+#define UUID_NFIT_DIMM_N_HYPERV "5746c5f2-a9a2-4264-ad0e-e4ddc9e09e80"
+
+#define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \
+ | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \
+ | ACPI_NFIT_MEM_NOT_ARMED | ACPI_NFIT_MEM_MAP_FAILED)
+
+#define NVDIMM_CMD_MAX 31
+
+#define NVDIMM_STANDARD_CMDMASK \
+(1 << ND_CMD_SMART | 1 << ND_CMD_SMART_THRESHOLD | 1 << ND_CMD_DIMM_FLAGS \
+ | 1 << ND_CMD_GET_CONFIG_SIZE | 1 << ND_CMD_GET_CONFIG_DATA \
+ | 1 << ND_CMD_SET_CONFIG_DATA | 1 << ND_CMD_VENDOR_EFFECT_LOG_SIZE \
+ | 1 << ND_CMD_VENDOR_EFFECT_LOG | 1 << ND_CMD_VENDOR)
+
+/*
+ * Command numbers that the kernel needs to know about to handle
+ * non-default DSM revision ids
+ */
+enum nvdimm_family_cmds {
+ NVDIMM_INTEL_LATCH_SHUTDOWN = 10,
+ NVDIMM_INTEL_GET_MODES = 11,
+ NVDIMM_INTEL_GET_FWINFO = 12,
+ NVDIMM_INTEL_START_FWUPDATE = 13,
+ NVDIMM_INTEL_SEND_FWUPDATE = 14,
+ NVDIMM_INTEL_FINISH_FWUPDATE = 15,
+ NVDIMM_INTEL_QUERY_FWUPDATE = 16,
+ NVDIMM_INTEL_SET_THRESHOLD = 17,
+ NVDIMM_INTEL_INJECT_ERROR = 18,
+ NVDIMM_INTEL_GET_SECURITY_STATE = 19,
+ NVDIMM_INTEL_SET_PASSPHRASE = 20,
+ NVDIMM_INTEL_DISABLE_PASSPHRASE = 21,
+ NVDIMM_INTEL_UNLOCK_UNIT = 22,
+ NVDIMM_INTEL_FREEZE_LOCK = 23,
+ NVDIMM_INTEL_SECURE_ERASE = 24,
+ NVDIMM_INTEL_OVERWRITE = 25,
+ NVDIMM_INTEL_QUERY_OVERWRITE = 26,
+ NVDIMM_INTEL_SET_MASTER_PASSPHRASE = 27,
+ NVDIMM_INTEL_MASTER_SECURE_ERASE = 28,
+ NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO = 29,
+ NVDIMM_INTEL_FW_ACTIVATE_ARM = 30,
+};
+
+enum nvdimm_bus_family_cmds {
+ NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO = 1,
+ NVDIMM_BUS_INTEL_FW_ACTIVATE = 2,
+};
+
+#define NVDIMM_INTEL_SECURITY_CMDMASK \
+(1 << NVDIMM_INTEL_GET_SECURITY_STATE | 1 << NVDIMM_INTEL_SET_PASSPHRASE \
+| 1 << NVDIMM_INTEL_DISABLE_PASSPHRASE | 1 << NVDIMM_INTEL_UNLOCK_UNIT \
+| 1 << NVDIMM_INTEL_FREEZE_LOCK | 1 << NVDIMM_INTEL_SECURE_ERASE \
+| 1 << NVDIMM_INTEL_OVERWRITE | 1 << NVDIMM_INTEL_QUERY_OVERWRITE \
+| 1 << NVDIMM_INTEL_SET_MASTER_PASSPHRASE \
+| 1 << NVDIMM_INTEL_MASTER_SECURE_ERASE)
+
+#define NVDIMM_INTEL_FW_ACTIVATE_CMDMASK \
+(1 << NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO | 1 << NVDIMM_INTEL_FW_ACTIVATE_ARM)
+
+#define NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK \
+(1 << NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO | 1 << NVDIMM_BUS_INTEL_FW_ACTIVATE)
+
+#define NVDIMM_INTEL_CMDMASK \
+(NVDIMM_STANDARD_CMDMASK | 1 << NVDIMM_INTEL_GET_MODES \
+ | 1 << NVDIMM_INTEL_GET_FWINFO | 1 << NVDIMM_INTEL_START_FWUPDATE \
+ | 1 << NVDIMM_INTEL_SEND_FWUPDATE | 1 << NVDIMM_INTEL_FINISH_FWUPDATE \
+ | 1 << NVDIMM_INTEL_QUERY_FWUPDATE | 1 << NVDIMM_INTEL_SET_THRESHOLD \
+ | 1 << NVDIMM_INTEL_INJECT_ERROR | 1 << NVDIMM_INTEL_LATCH_SHUTDOWN \
+ | NVDIMM_INTEL_SECURITY_CMDMASK | NVDIMM_INTEL_FW_ACTIVATE_CMDMASK)
+
+#define NVDIMM_INTEL_DENY_CMDMASK \
+(NVDIMM_INTEL_SECURITY_CMDMASK | NVDIMM_INTEL_FW_ACTIVATE_CMDMASK)
+
+enum nfit_uuids {
+ /* for simplicity alias the uuid index with the family id */
+ NFIT_DEV_DIMM = NVDIMM_FAMILY_INTEL,
+ NFIT_DEV_DIMM_N_HPE1 = NVDIMM_FAMILY_HPE1,
+ NFIT_DEV_DIMM_N_HPE2 = NVDIMM_FAMILY_HPE2,
+ NFIT_DEV_DIMM_N_MSFT = NVDIMM_FAMILY_MSFT,
+ NFIT_DEV_DIMM_N_HYPERV = NVDIMM_FAMILY_HYPERV,
+ /*
+ * to_nfit_bus_uuid() expects to translate bus uuid family ids
+ * to a UUID index using NVDIMM_FAMILY_MAX as an offset
+ */
+ NFIT_BUS_INTEL = NVDIMM_FAMILY_MAX + NVDIMM_BUS_FAMILY_INTEL,
+ NFIT_SPA_VOLATILE,
+ NFIT_SPA_PM,
+ NFIT_SPA_DCR,
+ NFIT_SPA_BDW,
+ NFIT_SPA_VDISK,
+ NFIT_SPA_VCD,
+ NFIT_SPA_PDISK,
+ NFIT_SPA_PCD,
+ NFIT_DEV_BUS,
+ NFIT_UUID_MAX,
+};
+
+/*
+ * Region format interface codes are stored with the interface as the
+ * LSB and the function as the MSB.
+ */
+#define NFIT_FIC_BYTE cpu_to_le16(0x101) /* byte-addressable energy backed */
+#define NFIT_FIC_BLK cpu_to_le16(0x201) /* block-addressable non-energy backed */
+#define NFIT_FIC_BYTEN cpu_to_le16(0x301) /* byte-addressable non-energy backed */
+
+enum {
+ NFIT_BLK_READ_FLUSH = 1,
+ NFIT_BLK_DCR_LATCH = 2,
+ NFIT_ARS_STATUS_DONE = 0,
+ NFIT_ARS_STATUS_BUSY = 1 << 16,
+ NFIT_ARS_STATUS_NONE = 2 << 16,
+ NFIT_ARS_STATUS_INTR = 3 << 16,
+ NFIT_ARS_START_BUSY = 6,
+ NFIT_ARS_CAP_NONE = 1,
+ NFIT_ARS_F_OVERFLOW = 1,
+ NFIT_ARS_TIMEOUT = 90,
+};
+
+enum nfit_root_notifiers {
+ NFIT_NOTIFY_UPDATE = 0x80,
+ NFIT_NOTIFY_UC_MEMORY_ERROR = 0x81,
+};
+
+enum nfit_dimm_notifiers {
+ NFIT_NOTIFY_DIMM_HEALTH = 0x81,
+};
+
+enum nfit_ars_state {
+ ARS_REQ_SHORT,
+ ARS_REQ_LONG,
+ ARS_FAILED,
+};
+
+struct nfit_spa {
+ struct list_head list;
+ struct nd_region *nd_region;
+ unsigned long ars_state;
+ u32 clear_err_unit;
+ u32 max_ars;
+ struct acpi_nfit_system_address spa[];
+};
+
+struct nfit_dcr {
+ struct list_head list;
+ struct acpi_nfit_control_region dcr[];
+};
+
+struct nfit_bdw {
+ struct list_head list;
+ struct acpi_nfit_data_region bdw[];
+};
+
+struct nfit_idt {
+ struct list_head list;
+ struct acpi_nfit_interleave idt[];
+};
+
+struct nfit_flush {
+ struct list_head list;
+ struct acpi_nfit_flush_address flush[];
+};
+
+struct nfit_memdev {
+ struct list_head list;
+ struct acpi_nfit_memory_map memdev[];
+};
+
+enum nfit_mem_flags {
+ NFIT_MEM_LSR,
+ NFIT_MEM_LSW,
+ NFIT_MEM_DIRTY,
+ NFIT_MEM_DIRTY_COUNT,
+};
+
+#define NFIT_DIMM_ID_LEN 22
+
+/* assembled tables for a given dimm/memory-device */
+struct nfit_mem {
+ struct nvdimm *nvdimm;
+ struct acpi_nfit_memory_map *memdev_dcr;
+ struct acpi_nfit_memory_map *memdev_pmem;
+ struct acpi_nfit_control_region *dcr;
+ struct acpi_nfit_system_address *spa_dcr;
+ struct acpi_nfit_interleave *idt_dcr;
+ struct kernfs_node *flags_attr;
+ struct nfit_flush *nfit_flush;
+ struct list_head list;
+ struct acpi_device *adev;
+ struct acpi_nfit_desc *acpi_desc;
+ enum nvdimm_fwa_state fwa_state;
+ enum nvdimm_fwa_result fwa_result;
+ int fwa_count;
+ char id[NFIT_DIMM_ID_LEN+1];
+ struct resource *flush_wpq;
+ unsigned long dsm_mask;
+ unsigned long flags;
+ u32 dirty_shutdown;
+ int family;
+};
+
+enum scrub_flags {
+ ARS_BUSY,
+ ARS_CANCEL,
+ ARS_VALID,
+ ARS_POLL,
+};
+
+struct acpi_nfit_desc {
+ struct nvdimm_bus_descriptor nd_desc;
+ struct acpi_table_header acpi_header;
+ struct mutex init_mutex;
+ struct list_head memdevs;
+ struct list_head flushes;
+ struct list_head dimms;
+ struct list_head spas;
+ struct list_head dcrs;
+ struct list_head bdws;
+ struct list_head idts;
+ struct nvdimm_bus *nvdimm_bus;
+ struct device *dev;
+ struct nd_cmd_ars_status *ars_status;
+ struct nfit_spa *scrub_spa;
+ struct delayed_work dwork;
+ struct list_head list;
+ struct kernfs_node *scrub_count_state;
+ unsigned int max_ars;
+ unsigned int scrub_count;
+ unsigned int scrub_mode;
+ unsigned long scrub_flags;
+ unsigned long dimm_cmd_force_en;
+ unsigned long bus_cmd_force_en;
+ unsigned long bus_dsm_mask;
+ unsigned long family_dsm_mask[NVDIMM_BUS_FAMILY_MAX + 1];
+ unsigned int platform_cap;
+ unsigned int scrub_tmo;
+ enum nvdimm_fwa_state fwa_state;
+ enum nvdimm_fwa_capability fwa_cap;
+ int fwa_count;
+ bool fwa_noidle;
+ bool fwa_nosuspend;
+};
+
+enum scrub_mode {
+ HW_ERROR_SCRUB_OFF,
+ HW_ERROR_SCRUB_ON,
+};
+
+enum nd_blk_mmio_selector {
+ BDW,
+ DCR,
+};
+
+struct nd_blk_addr {
+ union {
+ void __iomem *base;
+ void *aperture;
+ };
+};
+
+struct nfit_blk {
+ struct nfit_blk_mmio {
+ struct nd_blk_addr addr;
+ u64 size;
+ u64 base_offset;
+ u32 line_size;
+ u32 num_lines;
+ u32 table_size;
+ struct acpi_nfit_interleave *idt;
+ struct acpi_nfit_system_address *spa;
+ } mmio[2];
+ struct nd_region *nd_region;
+ u64 bdw_offset; /* post interleave offset */
+ u64 stat_offset;
+ u64 cmd_offset;
+ u32 dimm_flags;
+};
+
+extern struct list_head acpi_descs;
+extern struct mutex acpi_desc_lock;
+int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc,
+ enum nfit_ars_state req_type);
+
+#ifdef CONFIG_X86_MCE
+void nfit_mce_register(void);
+void nfit_mce_unregister(void);
+#else
+static inline void nfit_mce_register(void)
+{
+}
+static inline void nfit_mce_unregister(void)
+{
+}
+#endif
+
+int nfit_spa_type(struct acpi_nfit_system_address *spa);
+
+static inline struct acpi_nfit_memory_map *__to_nfit_memdev(
+ struct nfit_mem *nfit_mem)
+{
+ if (nfit_mem->memdev_dcr)
+ return nfit_mem->memdev_dcr;
+ return nfit_mem->memdev_pmem;
+}
+
+static inline struct acpi_nfit_desc *to_acpi_desc(
+ struct nvdimm_bus_descriptor *nd_desc)
+{
+ return container_of(nd_desc, struct acpi_nfit_desc, nd_desc);
+}
+
+const guid_t *to_nfit_uuid(enum nfit_uuids id);
+int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *nfit, acpi_size sz);
+void acpi_nfit_shutdown(void *data);
+void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event);
+void __acpi_nvdimm_notify(struct device *dev, u32 event);
+int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
+ unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc);
+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;
+#endif /* __NFIT_H__ */
diff --git a/drivers/acpi/numa/Kconfig b/drivers/acpi/numa/Kconfig
new file mode 100644
index 000000000..39b1f34c2
--- /dev/null
+++ b/drivers/acpi/numa/Kconfig
@@ -0,0 +1,18 @@
+# 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
+
+config ACPI_HMAT
+ bool "ACPI Heterogeneous Memory Attribute Table Support"
+ depends on ACPI_NUMA
+ select HMEM_REPORTING
+ select MEMREGION
+ help
+ If set, this option has the kernel parse and report the
+ platform's ACPI HMAT (Heterogeneous Memory Attributes Table),
+ register memory initiators with their targets, and export
+ performance attributes through the node's sysfs device if
+ provided.
diff --git a/drivers/acpi/numa/Makefile b/drivers/acpi/numa/Makefile
new file mode 100644
index 000000000..517a6c689
--- /dev/null
+++ b/drivers/acpi/numa/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ACPI_NUMA) += srat.o
+obj-$(CONFIG_ACPI_HMAT) += hmat.o
diff --git a/drivers/acpi/numa/hmat.c b/drivers/acpi/numa/hmat.c
new file mode 100644
index 000000000..6cceca64a
--- /dev/null
+++ b/drivers/acpi/numa/hmat.c
@@ -0,0 +1,877 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019, Intel Corporation.
+ *
+ * Heterogeneous Memory Attributes Table (HMAT) representation
+ *
+ * This program parses and reports the platform's HMAT tables, and registers
+ * the applicable attributes with the node's interfaces.
+ */
+
+#define pr_fmt(fmt) "acpi/hmat: " fmt
+
+#include <linux/acpi.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/list_sort.h>
+#include <linux/memregion.h>
+#include <linux/memory.h>
+#include <linux/mutex.h>
+#include <linux/node.h>
+#include <linux/sysfs.h>
+#include <linux/dax.h>
+
+static u8 hmat_revision;
+static int hmat_disable __initdata;
+
+void __init disable_hmat(void)
+{
+ hmat_disable = 1;
+}
+
+static LIST_HEAD(targets);
+static LIST_HEAD(initiators);
+static LIST_HEAD(localities);
+
+static DEFINE_MUTEX(target_lock);
+
+/*
+ * The defined enum order is used to prioritize attributes to break ties when
+ * selecting the best performing node.
+ */
+enum locality_types {
+ WRITE_LATENCY,
+ READ_LATENCY,
+ WRITE_BANDWIDTH,
+ READ_BANDWIDTH,
+};
+
+static struct memory_locality *localities_types[4];
+
+struct target_cache {
+ struct list_head node;
+ struct node_cache_attrs cache_attrs;
+};
+
+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 list_head caches;
+ struct node_cache_attrs cache_attrs;
+ bool registered;
+};
+
+struct memory_initiator {
+ struct list_head node;
+ unsigned int processor_pxm;
+ bool has_cpu;
+};
+
+struct memory_locality {
+ struct list_head node;
+ struct acpi_hmat_locality *hmat_loc;
+};
+
+static struct memory_initiator *find_mem_initiator(unsigned int cpu_pxm)
+{
+ struct memory_initiator *initiator;
+
+ list_for_each_entry(initiator, &initiators, node)
+ if (initiator->processor_pxm == cpu_pxm)
+ return initiator;
+ return NULL;
+}
+
+static struct memory_target *find_mem_target(unsigned int mem_pxm)
+{
+ struct memory_target *target;
+
+ list_for_each_entry(target, &targets, node)
+ if (target->memory_pxm == mem_pxm)
+ return target;
+ return NULL;
+}
+
+static __init void alloc_memory_initiator(unsigned int cpu_pxm)
+{
+ struct memory_initiator *initiator;
+
+ if (pxm_to_node(cpu_pxm) == NUMA_NO_NODE)
+ return;
+
+ initiator = find_mem_initiator(cpu_pxm);
+ if (initiator)
+ return;
+
+ initiator = kzalloc(sizeof(*initiator), GFP_KERNEL);
+ if (!initiator)
+ return;
+
+ initiator->processor_pxm = cpu_pxm;
+ initiator->has_cpu = node_state(pxm_to_node(cpu_pxm), N_CPU);
+ list_add_tail(&initiator->node, &initiators);
+}
+
+static __init void alloc_memory_target(unsigned int mem_pxm,
+ resource_size_t start, resource_size_t len)
+{
+ struct memory_target *target;
+
+ target = find_mem_target(mem_pxm);
+ if (!target) {
+ target = kzalloc(sizeof(*target), GFP_KERNEL);
+ if (!target)
+ return;
+ target->memory_pxm = mem_pxm;
+ target->processor_pxm = PXM_INVAL;
+ target->memregions = (struct resource) {
+ .name = "ACPI mem",
+ .start = 0,
+ .end = -1,
+ .flags = IORESOURCE_MEM,
+ };
+ list_add_tail(&target->node, &targets);
+ INIT_LIST_HEAD(&target->caches);
+ }
+
+ /*
+ * There are potentially multiple ranges per PXM, so record each
+ * in the per-target memregions resource tree.
+ */
+ if (!__request_region(&target->memregions, start, len, "memory target",
+ IORESOURCE_MEM))
+ pr_warn("failed to reserve %#llx - %#llx in pxm: %d\n",
+ start, start + len, mem_pxm);
+}
+
+static __init const char *hmat_data_type(u8 type)
+{
+ switch (type) {
+ case ACPI_HMAT_ACCESS_LATENCY:
+ return "Access Latency";
+ case ACPI_HMAT_READ_LATENCY:
+ return "Read Latency";
+ case ACPI_HMAT_WRITE_LATENCY:
+ return "Write Latency";
+ case ACPI_HMAT_ACCESS_BANDWIDTH:
+ return "Access Bandwidth";
+ case ACPI_HMAT_READ_BANDWIDTH:
+ return "Read Bandwidth";
+ case ACPI_HMAT_WRITE_BANDWIDTH:
+ return "Write Bandwidth";
+ default:
+ return "Reserved";
+ }
+}
+
+static __init const char *hmat_data_type_suffix(u8 type)
+{
+ switch (type) {
+ case ACPI_HMAT_ACCESS_LATENCY:
+ case ACPI_HMAT_READ_LATENCY:
+ case ACPI_HMAT_WRITE_LATENCY:
+ return " nsec";
+ case ACPI_HMAT_ACCESS_BANDWIDTH:
+ case ACPI_HMAT_READ_BANDWIDTH:
+ case ACPI_HMAT_WRITE_BANDWIDTH:
+ return " MB/s";
+ default:
+ return "";
+ }
+}
+
+static u32 hmat_normalize(u16 entry, u64 base, u8 type)
+{
+ u32 value;
+
+ /*
+ * Check for invalid and overflow values
+ */
+ if (entry == 0xffff || !entry)
+ return 0;
+ else if (base > (UINT_MAX / (entry)))
+ return 0;
+
+ /*
+ * Divide by the base unit for version 1, convert latency from
+ * picosenonds to nanoseconds if revision 2.
+ */
+ value = entry * base;
+ if (hmat_revision == 1) {
+ if (value < 10)
+ return 0;
+ value = DIV_ROUND_UP(value, 10);
+ } else if (hmat_revision == 2) {
+ switch (type) {
+ case ACPI_HMAT_ACCESS_LATENCY:
+ case ACPI_HMAT_READ_LATENCY:
+ case ACPI_HMAT_WRITE_LATENCY:
+ value = DIV_ROUND_UP(value, 1000);
+ break;
+ default:
+ break;
+ }
+ }
+ return value;
+}
+
+static void hmat_update_target_access(struct memory_target *target,
+ u8 type, u32 value, int access)
+{
+ switch (type) {
+ case ACPI_HMAT_ACCESS_LATENCY:
+ target->hmem_attrs[access].read_latency = value;
+ target->hmem_attrs[access].write_latency = value;
+ break;
+ case ACPI_HMAT_READ_LATENCY:
+ target->hmem_attrs[access].read_latency = value;
+ break;
+ case ACPI_HMAT_WRITE_LATENCY:
+ target->hmem_attrs[access].write_latency = value;
+ break;
+ case ACPI_HMAT_ACCESS_BANDWIDTH:
+ target->hmem_attrs[access].read_bandwidth = value;
+ target->hmem_attrs[access].write_bandwidth = value;
+ break;
+ case ACPI_HMAT_READ_BANDWIDTH:
+ target->hmem_attrs[access].read_bandwidth = value;
+ break;
+ case ACPI_HMAT_WRITE_BANDWIDTH:
+ target->hmem_attrs[access].write_bandwidth = value;
+ break;
+ default:
+ break;
+ }
+}
+
+static __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc)
+{
+ struct memory_locality *loc;
+
+ loc = kzalloc(sizeof(*loc), GFP_KERNEL);
+ if (!loc) {
+ pr_notice_once("Failed to allocate HMAT locality\n");
+ return;
+ }
+
+ loc->hmat_loc = hmat_loc;
+ list_add_tail(&loc->node, &localities);
+
+ switch (hmat_loc->data_type) {
+ case ACPI_HMAT_ACCESS_LATENCY:
+ localities_types[READ_LATENCY] = loc;
+ localities_types[WRITE_LATENCY] = loc;
+ break;
+ case ACPI_HMAT_READ_LATENCY:
+ localities_types[READ_LATENCY] = loc;
+ break;
+ case ACPI_HMAT_WRITE_LATENCY:
+ localities_types[WRITE_LATENCY] = loc;
+ break;
+ case ACPI_HMAT_ACCESS_BANDWIDTH:
+ localities_types[READ_BANDWIDTH] = loc;
+ localities_types[WRITE_BANDWIDTH] = loc;
+ break;
+ case ACPI_HMAT_READ_BANDWIDTH:
+ localities_types[READ_BANDWIDTH] = loc;
+ break;
+ case ACPI_HMAT_WRITE_BANDWIDTH:
+ localities_types[WRITE_BANDWIDTH] = loc;
+ break;
+ default:
+ break;
+ }
+}
+
+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("Unexpected locality header length: %u\n",
+ hmat_loc->header.length);
+ return -EINVAL;
+ }
+
+ type = hmat_loc->data_type;
+ mem_hier = hmat_loc->flags & ACPI_HMAT_MEMORY_HIERARCHY;
+ ipds = hmat_loc->number_of_initiator_Pds;
+ tpds = hmat_loc->number_of_target_Pds;
+ total_size = sizeof(*hmat_loc) + sizeof(*entries) * ipds * tpds +
+ sizeof(*inits) * ipds + sizeof(*targs) * tpds;
+ if (hmat_loc->header.length < total_size) {
+ pr_notice("Unexpected locality header length:%u, minimum required:%u\n",
+ hmat_loc->header.length, total_size);
+ return -EINVAL;
+ }
+
+ pr_info("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;
+ entries = (u16 *)(targs + tpds);
+ for (init = 0; init < ipds; init++) {
+ alloc_memory_initiator(inits[init]);
+ for (targ = 0; targ < tpds; targ++) {
+ 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);
+ }
+ }
+ }
+ }
+
+ if (mem_hier == ACPI_HMAT_MEMORY)
+ hmat_add_locality(hmat_loc);
+
+ return 0;
+}
+
+static __init int hmat_parse_cache(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_hmat_cache *cache = (void *)header;
+ struct memory_target *target;
+ struct target_cache *tcache;
+ u32 attrs;
+
+ if (cache->header.length < sizeof(*cache)) {
+ pr_notice("Unexpected cache header length: %u\n",
+ cache->header.length);
+ return -EINVAL;
+ }
+
+ attrs = cache->cache_attributes;
+ pr_info("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)
+ return 0;
+
+ tcache = kzalloc(sizeof(*tcache), GFP_KERNEL);
+ if (!tcache) {
+ pr_notice_once("Failed to allocate HMAT cache info\n");
+ return 0;
+ }
+
+ tcache->cache_attrs.size = cache->cache_size;
+ tcache->cache_attrs.level = (attrs & ACPI_HMAT_CACHE_LEVEL) >> 4;
+ tcache->cache_attrs.line_size = (attrs & ACPI_HMAT_CACHE_LINE_SIZE) >> 16;
+
+ switch ((attrs & ACPI_HMAT_CACHE_ASSOCIATIVITY) >> 8) {
+ case ACPI_HMAT_CA_DIRECT_MAPPED:
+ tcache->cache_attrs.indexing = NODE_CACHE_DIRECT_MAP;
+ break;
+ case ACPI_HMAT_CA_COMPLEX_CACHE_INDEXING:
+ tcache->cache_attrs.indexing = NODE_CACHE_INDEXED;
+ break;
+ case ACPI_HMAT_CA_NONE:
+ default:
+ tcache->cache_attrs.indexing = NODE_CACHE_OTHER;
+ break;
+ }
+
+ switch ((attrs & ACPI_HMAT_WRITE_POLICY) >> 12) {
+ case ACPI_HMAT_CP_WB:
+ tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_BACK;
+ break;
+ case ACPI_HMAT_CP_WT:
+ tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_THROUGH;
+ break;
+ case ACPI_HMAT_CP_NONE:
+ default:
+ tcache->cache_attrs.write_policy = NODE_CACHE_WRITE_OTHER;
+ break;
+ }
+ list_add_tail(&tcache->node, &target->caches);
+
+ return 0;
+}
+
+static int __init hmat_parse_proximity_domain(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_hmat_proximity_domain *p = (void *)header;
+ struct memory_target *target = NULL;
+
+ if (p->header.length != sizeof(*p)) {
+ pr_notice("Unexpected address range header length: %u\n",
+ p->header.length);
+ return -EINVAL;
+ }
+
+ if (hmat_revision == 1)
+ pr_info("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("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("Memory Domain missing from SRAT\n");
+ return -EINVAL;
+ }
+ }
+ if (target && p->flags & ACPI_HMAT_PROCESSOR_PD_VALID) {
+ int p_node = pxm_to_node(p->processor_PD);
+
+ if (p_node == NUMA_NO_NODE) {
+ pr_debug("Invalid Processor Domain\n");
+ return -EINVAL;
+ }
+ target->processor_pxm = p->processor_PD;
+ }
+
+ return 0;
+}
+
+static int __init hmat_parse_subtable(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_hmat_structure *hdr = (void *)header;
+
+ if (!hdr)
+ return -EINVAL;
+
+ switch (hdr->type) {
+ case ACPI_HMAT_TYPE_PROXIMITY:
+ return hmat_parse_proximity_domain(header, end);
+ case ACPI_HMAT_TYPE_LOCALITY:
+ return hmat_parse_locality(header, end);
+ case ACPI_HMAT_TYPE_CACHE:
+ return hmat_parse_cache(header, end);
+ default:
+ return -EINVAL;
+ }
+}
+
+static __init int srat_parse_mem_affinity(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_srat_mem_affinity *ma = (void *)header;
+
+ if (!ma)
+ return -EINVAL;
+ if (!(ma->flags & ACPI_SRAT_MEM_ENABLED))
+ return 0;
+ alloc_memory_target(ma->proximity_domain, ma->base_address, ma->length);
+ return 0;
+}
+
+static u32 hmat_initiator_perf(struct memory_target *target,
+ struct memory_initiator *initiator,
+ struct acpi_hmat_locality *hmat_loc)
+{
+ unsigned int ipds, tpds, i, idx = 0, tdx = 0;
+ u32 *inits, *targs;
+ u16 *entries;
+
+ ipds = hmat_loc->number_of_initiator_Pds;
+ tpds = hmat_loc->number_of_target_Pds;
+ inits = (u32 *)(hmat_loc + 1);
+ targs = inits + ipds;
+ entries = (u16 *)(targs + tpds);
+
+ for (i = 0; i < ipds; i++) {
+ if (inits[i] == initiator->processor_pxm) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (i == ipds)
+ return 0;
+
+ for (i = 0; i < tpds; i++) {
+ if (targs[i] == target->memory_pxm) {
+ tdx = i;
+ break;
+ }
+ }
+ if (i == tpds)
+ return 0;
+
+ return hmat_normalize(entries[idx * tpds + tdx],
+ hmat_loc->entry_base_unit,
+ hmat_loc->data_type);
+}
+
+static bool hmat_update_best(u8 type, u32 value, u32 *best)
+{
+ bool updated = false;
+
+ if (!value)
+ return false;
+
+ switch (type) {
+ case ACPI_HMAT_ACCESS_LATENCY:
+ case ACPI_HMAT_READ_LATENCY:
+ case ACPI_HMAT_WRITE_LATENCY:
+ if (!*best || *best > value) {
+ *best = value;
+ updated = true;
+ }
+ break;
+ case ACPI_HMAT_ACCESS_BANDWIDTH:
+ case ACPI_HMAT_READ_BANDWIDTH:
+ case ACPI_HMAT_WRITE_BANDWIDTH:
+ if (!*best || *best < value) {
+ *best = value;
+ updated = true;
+ }
+ break;
+ }
+
+ return updated;
+}
+
+static int initiator_cmp(void *priv, const struct list_head *a,
+ const struct list_head *b)
+{
+ struct memory_initiator *ia;
+ struct memory_initiator *ib;
+
+ ia = list_entry(a, struct memory_initiator, node);
+ ib = list_entry(b, struct memory_initiator, node);
+
+ return ia->processor_pxm - ib->processor_pxm;
+}
+
+static int initiators_to_nodemask(unsigned long *p_nodes)
+{
+ struct memory_initiator *initiator;
+
+ 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_register_target_initiators(struct memory_target *target)
+{
+ static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
+ struct memory_initiator *initiator;
+ unsigned int mem_nid, cpu_nid;
+ struct memory_locality *loc = NULL;
+ u32 best = 0;
+ bool access0done = false;
+ int i;
+
+ mem_nid = pxm_to_node(target->memory_pxm);
+ /*
+ * If the Address Range Structure provides a local processor pxm, link
+ * only that one. Otherwise, find the best performance attributes and
+ * register 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);
+ return;
+ }
+ }
+
+ if (list_empty(&localities))
+ return;
+
+ /*
+ * We need the initiator list sorted so we can use bitmap_clear for
+ * previously set initiators when we find a better memory accessor.
+ * We'll also use the sorting to prime the candidate nodes with known
+ * initiators.
+ */
+ bitmap_zero(p_nodes, MAX_NUMNODES);
+ list_sort(NULL, &initiators, initiator_cmp);
+ if (initiators_to_nodemask(p_nodes) < 0)
+ return;
+
+ 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);
+ }
+ }
+
+ /* Access 1 ignores Generic Initiators */
+ bitmap_zero(p_nodes, MAX_NUMNODES);
+ if (initiators_to_nodemask(p_nodes) < 0)
+ return;
+
+ 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 (!initiator->has_cpu) {
+ clear_bit(initiator->processor_pxm, p_nodes);
+ continue;
+ }
+ 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, 1);
+ }
+ 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);
+ }
+}
+
+static void hmat_register_target_cache(struct memory_target *target)
+{
+ unsigned mem_nid = pxm_to_node(target->memory_pxm);
+ struct target_cache *tcache;
+
+ list_for_each_entry(tcache, &target->caches, node)
+ node_add_cache(mem_nid, &tcache->cache_attrs);
+}
+
+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);
+}
+
+static void hmat_register_target_devices(struct memory_target *target)
+{
+ struct resource *res;
+
+ /*
+ * Do not bother creating devices if no driver is available to
+ * consume them.
+ */
+ if (!IS_ENABLED(CONFIG_DEV_DAX_HMEM))
+ return;
+
+ for (res = target->memregions.child; res; res = res->sibling) {
+ int target_nid = pxm_to_node(target->memory_pxm);
+
+ hmem_register_device(target_nid, res);
+ }
+}
+
+static void hmat_register_target(struct memory_target *target)
+{
+ int nid = pxm_to_node(target->memory_pxm);
+
+ /*
+ * 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.
+ */
+ 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;
+ }
+ mutex_unlock(&target_lock);
+}
+
+static void hmat_register_targets(void)
+{
+ struct memory_target *target;
+
+ list_for_each_entry(target, &targets, node)
+ hmat_register_target(target);
+}
+
+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;
+
+ if (nid == NUMA_NO_NODE || action != MEM_ONLINE)
+ return NOTIFY_OK;
+
+ pxm = node_to_pxm(nid);
+ target = find_mem_target(pxm);
+ if (!target)
+ return NOTIFY_OK;
+
+ hmat_register_target(target);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block hmat_callback_nb = {
+ .notifier_call = hmat_callback,
+ .priority = 2,
+};
+
+static __init void hmat_free_structures(void)
+{
+ struct memory_target *target, *tnext;
+ struct memory_locality *loc, *lnext;
+ struct memory_initiator *initiator, *inext;
+ struct target_cache *tcache, *cnext;
+
+ list_for_each_entry_safe(target, tnext, &targets, node) {
+ struct resource *res, *res_next;
+
+ list_for_each_entry_safe(tcache, cnext, &target->caches, node) {
+ list_del(&tcache->node);
+ kfree(tcache);
+ }
+
+ list_del(&target->node);
+ res = target->memregions.child;
+ while (res) {
+ res_next = res->sibling;
+ __release_region(&target->memregions, res->start,
+ resource_size(res));
+ res = res_next;
+ }
+ kfree(target);
+ }
+
+ list_for_each_entry_safe(initiator, inext, &initiators, node) {
+ list_del(&initiator->node);
+ kfree(initiator);
+ }
+
+ list_for_each_entry_safe(loc, lnext, &localities, node) {
+ list_del(&loc->node);
+ kfree(loc);
+ }
+}
+
+static __init int hmat_init(void)
+{
+ struct acpi_table_header *tbl;
+ enum acpi_hmat_type i;
+ acpi_status status;
+
+ if (srat_disabled() || hmat_disable)
+ return 0;
+
+ status = acpi_get_table(ACPI_SIG_SRAT, 0, &tbl);
+ if (ACPI_FAILURE(status))
+ return 0;
+
+ if (acpi_table_parse_entries(ACPI_SIG_SRAT,
+ sizeof(struct acpi_table_srat),
+ ACPI_SRAT_TYPE_MEMORY_AFFINITY,
+ srat_parse_mem_affinity, 0) < 0)
+ goto out_put;
+ acpi_put_table(tbl);
+
+ status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl);
+ if (ACPI_FAILURE(status))
+ goto out_put;
+
+ hmat_revision = tbl->revision;
+ switch (hmat_revision) {
+ case 1:
+ case 2:
+ break;
+ default:
+ pr_notice("Ignoring: Unknown revision:%d\n", hmat_revision);
+ goto out_put;
+ }
+
+ for (i = ACPI_HMAT_TYPE_PROXIMITY; i < ACPI_HMAT_TYPE_RESERVED; i++) {
+ if (acpi_table_parse_entries(ACPI_SIG_HMAT,
+ sizeof(struct acpi_table_hmat), i,
+ hmat_parse_subtable, 0) < 0) {
+ 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;
+out_put:
+ hmat_free_structures();
+ acpi_put_table(tbl);
+ return 0;
+}
+device_initcall(hmat_init);
diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c
new file mode 100644
index 000000000..12f330b0e
--- /dev/null
+++ b/drivers/acpi/numa/srat.c
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * acpi_numa.c - ACPI NUMA support
+ *
+ * Copyright (C) 2002 Takayoshi Kochi <t-kochi@bq.jp.nec.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/memblock.h>
+#include <linux/numa.h>
+#include <linux/nodemask.h>
+#include <linux/topology.h>
+
+static nodemask_t nodes_found_map = NODE_MASK_NONE;
+
+/* maps to convert between proximity domain and logical node ID */
+static int pxm_to_node_map[MAX_PXM_DOMAINS]
+ = { [0 ... MAX_PXM_DOMAINS - 1] = NUMA_NO_NODE };
+static int node_to_pxm_map[MAX_NUMNODES]
+ = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
+
+unsigned char acpi_srat_revision __initdata;
+static int acpi_numa __initdata;
+
+void __init disable_srat(void)
+{
+ acpi_numa = -1;
+}
+
+int pxm_to_node(int pxm)
+{
+ if (pxm < 0 || pxm >= MAX_PXM_DOMAINS || numa_off)
+ return NUMA_NO_NODE;
+ return pxm_to_node_map[pxm];
+}
+EXPORT_SYMBOL(pxm_to_node);
+
+int node_to_pxm(int node)
+{
+ if (node < 0)
+ return PXM_INVAL;
+ return node_to_pxm_map[node];
+}
+
+static void __acpi_map_pxm_to_node(int pxm, int node)
+{
+ if (pxm_to_node_map[pxm] == NUMA_NO_NODE || node < pxm_to_node_map[pxm])
+ pxm_to_node_map[pxm] = node;
+ if (node_to_pxm_map[node] == PXM_INVAL || pxm < node_to_pxm_map[node])
+ node_to_pxm_map[node] = pxm;
+}
+
+int acpi_map_pxm_to_node(int pxm)
+{
+ int node;
+
+ if (pxm < 0 || pxm >= MAX_PXM_DOMAINS || numa_off)
+ return NUMA_NO_NODE;
+
+ 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);
+ __acpi_map_pxm_to_node(pxm, node);
+ node_set(node, nodes_found_map);
+ }
+
+ return node;
+}
+EXPORT_SYMBOL(acpi_map_pxm_to_node);
+
+static void __init
+acpi_table_print_srat_entry(struct acpi_subtable_header *header)
+{
+ switch (header->type) {
+ case ACPI_SRAT_TYPE_CPU_AFFINITY:
+ {
+ struct acpi_srat_cpu_affinity *p =
+ (struct acpi_srat_cpu_affinity *)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");
+ }
+ break;
+
+ case ACPI_SRAT_TYPE_MEMORY_AFFINITY:
+ {
+ struct acpi_srat_mem_affinity *p =
+ (struct acpi_srat_mem_affinity *)header;
+ pr_debug("SRAT Memory (0x%llx length 0x%llx) in proximity domain %d %s%s%s\n",
+ (unsigned long long)p->base_address,
+ (unsigned long long)p->length,
+ p->proximity_domain,
+ (p->flags & ACPI_SRAT_MEM_ENABLED) ?
+ "enabled" : "disabled",
+ (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) ?
+ " hot-pluggable" : "",
+ (p->flags & ACPI_SRAT_MEM_NON_VOLATILE) ?
+ " non-volatile" : "");
+ }
+ break;
+
+ case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY:
+ {
+ struct acpi_srat_x2apic_cpu_affinity *p =
+ (struct acpi_srat_x2apic_cpu_affinity *)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");
+ }
+ break;
+
+ case ACPI_SRAT_TYPE_GICC_AFFINITY:
+ {
+ struct acpi_srat_gicc_affinity *p =
+ (struct acpi_srat_gicc_affinity *)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");
+ }
+ break;
+
+ case ACPI_SRAT_TYPE_GENERIC_AFFINITY:
+ {
+ struct acpi_srat_generic_affinity *p =
+ (struct acpi_srat_generic_affinity *)header;
+
+ if (p->device_handle_type == 0) {
+ /*
+ * For pci devices this may be the only place they
+ * are assigned a proximity domain
+ */
+ pr_debug("SRAT Generic Initiator(Seg:%u BDF:%u) in proximity domain %d %s\n",
+ *(u16 *)(&p->device_handle[0]),
+ *(u16 *)(&p->device_handle[2]),
+ p->proximity_domain,
+ (p->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED) ?
+ "enabled" : "disabled");
+ } else {
+ /*
+ * In this case we can rely on the device having a
+ * proximity domain reference
+ */
+ pr_debug("SRAT Generic Initiator(HID=%.8s UID=%.4s) in proximity domain %d %s\n",
+ (char *)(&p->device_handle[0]),
+ (char *)(&p->device_handle[8]),
+ p->proximity_domain,
+ (p->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED) ?
+ "enabled" : "disabled");
+ }
+ }
+ break;
+ default:
+ pr_warn("Found unsupported SRAT entry (type = 0x%x)\n",
+ header->type);
+ break;
+ }
+}
+
+/*
+ * A lot of BIOS fill in 10 (= no distance) everywhere. This messes
+ * up the NUMA heuristics which wants the local node to have a smaller
+ * distance than the others.
+ * Do some quick checks here and only use the SLIT if it passes.
+ */
+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++) {
+ u8 val = slit->entry[d*i + j];
+ if (i == j) {
+ if (val != LOCAL_DISTANCE)
+ return 0;
+ } else if (val <= LOCAL_DISTANCE)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void __init bad_srat(void)
+{
+ pr_err("SRAT: SRAT not used.\n");
+ disable_srat();
+}
+
+int __init srat_disabled(void)
+{
+ return acpi_numa < 0;
+}
+
+#if defined(CONFIG_X86) || defined(CONFIG_ARM64) || defined(CONFIG_LOONGARCH)
+/*
+ * 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)
+{
+ int i, j;
+
+ for (i = 0; i < slit->locality_count; i++) {
+ const int from_node = pxm_to_node(i);
+
+ if (from_node == NUMA_NO_NODE)
+ continue;
+
+ for (j = 0; j < slit->locality_count; j++) {
+ const int to_node = pxm_to_node(j);
+
+ if (to_node == NUMA_NO_NODE)
+ continue;
+
+ numa_set_distance(from_node, to_node,
+ slit->entry[slit->locality_count * i + j]);
+ }
+ }
+}
+
+/*
+ * Default callback for parsing of the Proximity Domain <-> Memory
+ * Area mappings
+ */
+int __init
+acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
+{
+ u64 start, end;
+ u32 hotpluggable;
+ int node, pxm;
+
+ if (srat_disabled())
+ goto out_err;
+ 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;
+ hotpluggable = IS_ENABLED(CONFIG_MEMORY_HOTPLUG) &&
+ (ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE);
+
+ start = ma->base_address;
+ end = start + ma->length;
+ pxm = ma->proximity_domain;
+ if (acpi_srat_revision <= 1)
+ pxm &= 0xff;
+
+ node = acpi_map_pxm_to_node(pxm);
+ if (node == NUMA_NO_NODE) {
+ pr_err("SRAT: Too many proximity domains.\n");
+ goto out_err_bad_srat;
+ }
+
+ if (numa_add_memblk(node, start, end) < 0) {
+ pr_err("SRAT: Failed to add memblk to node %u [mem %#010Lx-%#010Lx]\n",
+ node, (unsigned long long) start,
+ (unsigned long long) end - 1);
+ goto out_err_bad_srat;
+ }
+
+ node_set(node, numa_nodes_parsed);
+
+ pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s\n",
+ node, pxm,
+ (unsigned long long) start, (unsigned long long) end - 1,
+ hotpluggable ? " hotplug" : "",
+ ma->flags & ACPI_SRAT_MEM_NON_VOLATILE ? " non-volatile" : "");
+
+ /* Mark hotplug range in memblock. */
+ if (hotpluggable && memblock_mark_hotplug(start, ma->length))
+ pr_warn("SRAT: Failed to mark hotplug range [mem %#010Lx-%#010Lx] in memblock\n",
+ (unsigned long long)start, (unsigned long long)end - 1);
+
+ max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1));
+
+ return 0;
+out_err_bad_srat:
+ bad_srat();
+out_err:
+ return -EINVAL;
+}
+
+static int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
+ void *arg, const unsigned long table_end)
+{
+ struct acpi_cedt_cfmws *cfmws;
+ int *fake_pxm = arg;
+ u64 start, end;
+ int node;
+
+ cfmws = (struct acpi_cedt_cfmws *)header;
+ start = cfmws->base_hpa;
+ end = cfmws->base_hpa + cfmws->window_size;
+
+ /*
+ * 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) {
+ pr_err("ACPI NUMA: Too many proximity domains while processing CFMWS.\n");
+ return -EINVAL;
+ }
+
+ if (numa_add_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)
+{
+ pr_warn("Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id);
+}
+
+static int __init
+acpi_parse_x2apic_affinity(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_srat_x2apic_cpu_affinity *processor_affinity;
+
+ processor_affinity = (struct acpi_srat_x2apic_cpu_affinity *)header;
+
+ acpi_table_print_srat_entry(&header->common);
+
+ /* let architecture-dependent part to do it */
+ acpi_numa_x2apic_affinity_init(processor_affinity);
+
+ return 0;
+}
+
+static int __init
+acpi_parse_processor_affinity(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_srat_cpu_affinity *processor_affinity;
+
+ processor_affinity = (struct acpi_srat_cpu_affinity *)header;
+
+ acpi_table_print_srat_entry(&header->common);
+
+ /* let architecture-dependent part to do it */
+ acpi_numa_processor_affinity_init(processor_affinity);
+
+ return 0;
+}
+
+static int __init
+acpi_parse_gicc_affinity(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_srat_gicc_affinity *processor_affinity;
+
+ processor_affinity = (struct acpi_srat_gicc_affinity *)header;
+
+ acpi_table_print_srat_entry(&header->common);
+
+ /* let architecture-dependent part to do it */
+ acpi_numa_gicc_affinity_init(processor_affinity);
+
+ return 0;
+}
+
+#if defined(CONFIG_X86) || defined(CONFIG_ARM64)
+static int __init
+acpi_parse_gi_affinity(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_srat_generic_affinity *gi_affinity;
+ int node;
+
+ gi_affinity = (struct acpi_srat_generic_affinity *)header;
+ if (!gi_affinity)
+ return -EINVAL;
+ acpi_table_print_srat_entry(&header->common);
+
+ if (!(gi_affinity->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED))
+ return -EINVAL;
+
+ node = acpi_map_pxm_to_node(gi_affinity->proximity_domain);
+ if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
+ pr_err("SRAT: Too many proximity domains.\n");
+ return -EINVAL;
+ }
+ node_set(node, numa_nodes_parsed);
+ node_set_state(node, N_GENERIC_INITIATOR);
+
+ return 0;
+}
+#else
+static int __init
+acpi_parse_gi_affinity(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ return 0;
+}
+#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)
+{
+ struct acpi_srat_mem_affinity *memory_affinity;
+
+ memory_affinity = (struct acpi_srat_mem_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++;
+ return 0;
+}
+
+static int __init acpi_parse_srat(struct acpi_table_header *table)
+{
+ struct acpi_table_srat *srat = (struct acpi_table_srat *)table;
+
+ acpi_srat_revision = srat->header.revision;
+
+ /* Real work done in acpi_table_parse_srat below. */
+
+ return 0;
+}
+
+static int __init
+acpi_table_parse_srat(enum acpi_srat_type id,
+ acpi_tbl_entry_handler handler, unsigned int max_entries)
+{
+ return acpi_table_parse_entries(ACPI_SIG_SRAT,
+ sizeof(struct acpi_table_srat), id,
+ handler, max_entries);
+}
+
+int __init acpi_numa_init(void)
+{
+ int i, fake_pxm, cnt = 0;
+
+ if (acpi_disabled)
+ return -EINVAL;
+
+ /*
+ * Should not limit number with cpu num that is from NR_CPUS or nr_cpus=
+ * SRAT cpu entries could have different order with that in MADT.
+ * So go over all cpu entries in SRAT to get apicid to node mapping.
+ */
+
+ /* SRAT: System Resource Affinity Table */
+ if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) {
+ struct acpi_subtable_proc srat_proc[4];
+
+ memset(srat_proc, 0, sizeof(srat_proc));
+ srat_proc[0].id = ACPI_SRAT_TYPE_CPU_AFFINITY;
+ srat_proc[0].handler = acpi_parse_processor_affinity;
+ srat_proc[1].id = ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY;
+ srat_proc[1].handler = acpi_parse_x2apic_affinity;
+ srat_proc[2].id = ACPI_SRAT_TYPE_GICC_AFFINITY;
+ 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;
+
+ acpi_table_parse_entries_array(ACPI_SIG_SRAT,
+ sizeof(struct acpi_table_srat),
+ srat_proc, ARRAY_SIZE(srat_proc), 0);
+
+ cnt = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY,
+ acpi_parse_memory_affinity, 0);
+ }
+
+ /* SLIT: System Locality Information Table */
+ acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit);
+
+ /*
+ * CXL Fixed Memory Window Structures (CFMWS) must be parsed
+ * after the SRAT. Create NUMA Nodes for CXL memory ranges that
+ * are defined in the CFMWS and not already defined in the SRAT.
+ * Initialize a fake_pxm as the first available PXM to emulate.
+ */
+
+ /* fake_pxm is the next unused PXM value after SRAT parsing */
+ for (i = 0, fake_pxm = -1; i < MAX_NUMNODES - 1; i++) {
+ if (node_to_pxm_map[i] > fake_pxm)
+ fake_pxm = node_to_pxm_map[i];
+ }
+ fake_pxm++;
+ acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws,
+ &fake_pxm);
+
+ if (cnt < 0)
+ return cnt;
+ else if (!parsed_numa_memblks)
+ return -ENOENT;
+ return 0;
+}
+
+static int acpi_get_pxm(acpi_handle h)
+{
+ unsigned long long pxm;
+ acpi_status status;
+ acpi_handle handle;
+ acpi_handle phandle = h;
+
+ do {
+ handle = phandle;
+ status = acpi_evaluate_integer(handle, "_PXM", NULL, &pxm);
+ if (ACPI_SUCCESS(status))
+ return pxm;
+ status = acpi_get_parent(handle, &phandle);
+ } while (ACPI_SUCCESS(status));
+ return -1;
+}
+
+int acpi_get_node(acpi_handle handle)
+{
+ int pxm;
+
+ pxm = acpi_get_pxm(handle);
+
+ return pxm_to_node(pxm);
+}
+EXPORT_SYMBOL(acpi_get_node);
diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c
new file mode 100644
index 000000000..a2b11069e
--- /dev/null
+++ b/drivers/acpi/nvs.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * nvs.c - Routines for saving and restoring ACPI NVS memory region
+ *
+ * Copyright (C) 2008-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ */
+
+#define pr_fmt(fmt) "ACPI: PM: " fmt
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+
+#include "internal.h"
+
+/* ACPI NVS regions, APEI may use it */
+
+struct nvs_region {
+ __u64 phys_start;
+ __u64 size;
+ struct list_head node;
+};
+
+static LIST_HEAD(nvs_region_list);
+
+#ifdef CONFIG_ACPI_SLEEP
+static int suspend_nvs_register(unsigned long start, unsigned long size);
+#else
+static inline int suspend_nvs_register(unsigned long a, unsigned long b)
+{
+ return 0;
+}
+#endif
+
+int acpi_nvs_register(__u64 start, __u64 size)
+{
+ struct nvs_region *region;
+
+ region = kmalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->phys_start = start;
+ region->size = size;
+ list_add_tail(&region->node, &nvs_region_list);
+
+ return suspend_nvs_register(start, size);
+}
+
+int acpi_nvs_for_each_region(int (*func)(__u64 start, __u64 size, void *data),
+ void *data)
+{
+ int rc;
+ struct nvs_region *region;
+
+ list_for_each_entry(region, &nvs_region_list, node) {
+ rc = func(region->phys_start, region->size, data);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_ACPI_SLEEP
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * suspend and to restore the contents of this memory during the subsequent
+ * resume. The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+ unsigned long phys_start;
+ unsigned int size;
+ void *kaddr;
+ void *data;
+ bool unmap;
+ struct list_head node;
+};
+
+static LIST_HEAD(nvs_list);
+
+/**
+ * suspend_nvs_register - register platform NVS memory region to save
+ * @start: Physical address of the region.
+ * @size: Size of the region.
+ *
+ * The NVS region need not be page-aligned (both ends) and we arrange
+ * things so that the data from page-aligned addresses in this region will
+ * be copied into separate RAM pages.
+ */
+static int suspend_nvs_register(unsigned long start, unsigned long size)
+{
+ struct nvs_page *entry, *next;
+
+ pr_info("Registering ACPI NVS region [mem %#010lx-%#010lx] (%ld bytes)\n",
+ start, start + size - 1, size);
+
+ while (size > 0) {
+ unsigned int nr_bytes;
+
+ entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
+ if (!entry)
+ goto Error;
+
+ list_add_tail(&entry->node, &nvs_list);
+ entry->phys_start = start;
+ nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
+ entry->size = (size < nr_bytes) ? size : nr_bytes;
+
+ start += entry->size;
+ size -= entry->size;
+ }
+ return 0;
+
+ Error:
+ list_for_each_entry_safe(entry, next, &nvs_list, node) {
+ list_del(&entry->node);
+ kfree(entry);
+ }
+ return -ENOMEM;
+}
+
+/**
+ * suspend_nvs_free - free data pages allocated for saving NVS regions
+ */
+void suspend_nvs_free(void)
+{
+ struct nvs_page *entry;
+
+ list_for_each_entry(entry, &nvs_list, node)
+ if (entry->data) {
+ free_page((unsigned long)entry->data);
+ entry->data = NULL;
+ if (entry->kaddr) {
+ if (entry->unmap) {
+ iounmap(entry->kaddr);
+ entry->unmap = false;
+ } else {
+ acpi_os_unmap_iomem(entry->kaddr,
+ entry->size);
+ }
+ entry->kaddr = NULL;
+ }
+ }
+}
+
+/**
+ * suspend_nvs_alloc - allocate memory necessary for saving NVS regions
+ */
+int suspend_nvs_alloc(void)
+{
+ struct nvs_page *entry;
+
+ list_for_each_entry(entry, &nvs_list, node) {
+ entry->data = (void *)__get_free_page(GFP_KERNEL);
+ if (!entry->data) {
+ suspend_nvs_free();
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+/**
+ * suspend_nvs_save - save NVS memory regions
+ */
+int suspend_nvs_save(void)
+{
+ struct nvs_page *entry;
+
+ pr_info("Saving platform NVS memory\n");
+
+ list_for_each_entry(entry, &nvs_list, node)
+ if (entry->data) {
+ unsigned long phys = entry->phys_start;
+ unsigned int size = entry->size;
+
+ entry->kaddr = acpi_os_get_iomem(phys, size);
+ if (!entry->kaddr) {
+ entry->kaddr = acpi_os_ioremap(phys, size);
+ entry->unmap = !!entry->kaddr;
+ }
+ if (!entry->kaddr) {
+ suspend_nvs_free();
+ return -ENOMEM;
+ }
+ memcpy(entry->data, entry->kaddr, entry->size);
+ }
+
+ return 0;
+}
+
+/**
+ * suspend_nvs_restore - restore NVS memory regions
+ *
+ * This function is going to be called with interrupts disabled, so it
+ * cannot iounmap the virtual addresses used to access the NVS region.
+ */
+void suspend_nvs_restore(void)
+{
+ struct nvs_page *entry;
+
+ pr_info("Restoring platform NVS memory\n");
+
+ list_for_each_entry(entry, &nvs_list, node)
+ if (entry->data)
+ memcpy(entry->kaddr, entry->data, entry->size);
+}
+#endif
diff --git a/drivers/acpi/osi.c b/drivers/acpi/osi.c
new file mode 100644
index 000000000..d4405e1ca
--- /dev/null
+++ b/drivers/acpi/osi.c
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * osi.c - _OSI implementation
+ *
+ * Copyright (C) 2016 Intel Corporation
+ * Author: Lv Zheng <lv.zheng@intel.com>
+ */
+
+/* Uncomment next line to get verbose printout */
+/* #define DEBUG */
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/platform_data/x86/apple.h>
+
+#include "internal.h"
+
+
+#define OSI_STRING_LENGTH_MAX 64
+#define OSI_STRING_ENTRIES_MAX 16
+
+struct acpi_osi_entry {
+ char string[OSI_STRING_LENGTH_MAX];
+ bool enable;
+};
+
+static struct acpi_osi_config {
+ u8 default_disabling;
+ unsigned int linux_enable:1;
+ unsigned int linux_dmi:1;
+ unsigned int linux_cmdline:1;
+ unsigned int darwin_enable:1;
+ unsigned int darwin_dmi:1;
+ unsigned int darwin_cmdline:1;
+} osi_config;
+
+static struct acpi_osi_config osi_config;
+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},
+};
+
+static u32 acpi_osi_handler(acpi_string interface, u32 supported)
+{
+ if (!strcmp("Linux", interface)) {
+ pr_notice_once(FW_BUG
+ "BIOS _OSI(Linux) query %s%s\n",
+ osi_config.linux_enable ? "honored" : "ignored",
+ osi_config.linux_cmdline ? " via cmdline" :
+ osi_config.linux_dmi ? " via DMI" : "");
+ }
+ if (!strcmp("Darwin", interface)) {
+ pr_notice_once(
+ "BIOS _OSI(Darwin) query %s%s\n",
+ osi_config.darwin_enable ? "honored" : "ignored",
+ osi_config.darwin_cmdline ? " via cmdline" :
+ osi_config.darwin_dmi ? " via DMI" : "");
+ }
+
+ return supported;
+}
+
+void __init acpi_osi_setup(char *str)
+{
+ struct acpi_osi_entry *osi;
+ bool enable = true;
+ int i;
+
+ if (!acpi_gbl_create_osi_method)
+ return;
+
+ if (str == NULL || *str == '\0') {
+ pr_info("_OSI method disabled\n");
+ acpi_gbl_create_osi_method = FALSE;
+ return;
+ }
+
+ if (*str == '!') {
+ str++;
+ if (*str == '\0') {
+ /* Do not override acpi_osi=!* */
+ if (!osi_config.default_disabling)
+ osi_config.default_disabling =
+ ACPI_DISABLE_ALL_VENDOR_STRINGS;
+ return;
+ } else if (*str == '*') {
+ osi_config.default_disabling = ACPI_DISABLE_ALL_STRINGS;
+ for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
+ osi = &osi_setup_entries[i];
+ osi->enable = false;
+ }
+ return;
+ } else if (*str == '!') {
+ osi_config.default_disabling = 0;
+ return;
+ }
+ enable = false;
+ }
+
+ for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
+ osi = &osi_setup_entries[i];
+ if (!strcmp(osi->string, str)) {
+ osi->enable = enable;
+ break;
+ } else if (osi->string[0] == '\0') {
+ osi->enable = enable;
+ strncpy(osi->string, str, OSI_STRING_LENGTH_MAX);
+ break;
+ }
+ }
+}
+
+static void __init __acpi_osi_setup_darwin(bool enable)
+{
+ osi_config.darwin_enable = !!enable;
+ if (enable) {
+ acpi_osi_setup("!");
+ acpi_osi_setup("Darwin");
+ } else {
+ acpi_osi_setup("!!");
+ acpi_osi_setup("!Darwin");
+ }
+}
+
+static void __init acpi_osi_setup_darwin(bool enable)
+{
+ /* Override acpi_osi_dmi_blacklisted() */
+ osi_config.darwin_dmi = 0;
+ osi_config.darwin_cmdline = 1;
+ __acpi_osi_setup_darwin(enable);
+}
+
+/*
+ * The story of _OSI(Linux)
+ *
+ * From pre-history through Linux-2.6.22, Linux responded TRUE upon a BIOS
+ * OSI(Linux) query.
+ *
+ * Unfortunately, reference BIOS writers got wind of this and put
+ * OSI(Linux) in their example code, quickly exposing this string as
+ * ill-conceived and opening the door to an un-bounded number of BIOS
+ * incompatibilities.
+ *
+ * For example, OSI(Linux) was used on resume to re-POST a video card on
+ * one system, because Linux at that time could not do a speedy restore in
+ * its native driver. But then upon gaining quick native restore
+ * capability, Linux has no way to tell the BIOS to skip the time-consuming
+ * POST -- putting Linux at a permanent performance disadvantage. On
+ * another system, the BIOS writer used OSI(Linux) to infer native OS
+ * support for IPMI! On other systems, OSI(Linux) simply got in the way of
+ * Linux claiming to be compatible with other operating systems, exposing
+ * BIOS issues such as skipped device initialization.
+ *
+ * So "Linux" turned out to be a really poor chose of OSI string, and from
+ * Linux-2.6.23 onward we respond FALSE.
+ *
+ * BIOS writers should NOT query _OSI(Linux) on future systems. Linux will
+ * complain on the console when it sees it, and return FALSE. To get Linux
+ * to return TRUE for your system will require a kernel source update to
+ * add a DMI entry, or boot with "acpi_osi=Linux"
+ */
+static void __init __acpi_osi_setup_linux(bool enable)
+{
+ osi_config.linux_enable = !!enable;
+ if (enable)
+ acpi_osi_setup("Linux");
+ else
+ acpi_osi_setup("!Linux");
+}
+
+static void __init acpi_osi_setup_linux(bool enable)
+{
+ /* Override acpi_osi_dmi_blacklisted() */
+ osi_config.linux_dmi = 0;
+ osi_config.linux_cmdline = 1;
+ __acpi_osi_setup_linux(enable);
+}
+
+/*
+ * Modify the list of "OS Interfaces" reported to BIOS via _OSI
+ *
+ * empty string disables _OSI
+ * string starting with '!' disables that string
+ * otherwise string is added to list, augmenting built-in strings
+ */
+static void __init acpi_osi_setup_late(void)
+{
+ struct acpi_osi_entry *osi;
+ char *str;
+ int i;
+ acpi_status status;
+
+ if (osi_config.default_disabling) {
+ status = acpi_update_interfaces(osi_config.default_disabling);
+ if (ACPI_SUCCESS(status))
+ pr_info("Disabled all _OSI OS vendors%s\n",
+ osi_config.default_disabling ==
+ ACPI_DISABLE_ALL_STRINGS ?
+ " and feature groups" : "");
+ }
+
+ for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
+ osi = &osi_setup_entries[i];
+ str = osi->string;
+ if (*str == '\0')
+ break;
+ if (osi->enable) {
+ status = acpi_install_interface(str);
+ if (ACPI_SUCCESS(status))
+ pr_info("Added _OSI(%s)\n", str);
+ } else {
+ status = acpi_remove_interface(str);
+ if (ACPI_SUCCESS(status))
+ pr_info("Deleted _OSI(%s)\n", str);
+ }
+ }
+}
+
+static int __init osi_setup(char *str)
+{
+ if (str && !strcmp("Linux", str))
+ acpi_osi_setup_linux(true);
+ else if (str && !strcmp("!Linux", str))
+ acpi_osi_setup_linux(false);
+ else if (str && !strcmp("Darwin", str))
+ acpi_osi_setup_darwin(true);
+ else if (str && !strcmp("!Darwin", str))
+ acpi_osi_setup_darwin(false);
+ else
+ acpi_osi_setup(str);
+
+ return 1;
+}
+__setup("acpi_osi=", osi_setup);
+
+bool acpi_osi_is_win8(void)
+{
+ return acpi_gbl_osi_data >= ACPI_OSI_WIN_8;
+}
+EXPORT_SYMBOL(acpi_osi_is_win8);
+
+static void __init acpi_osi_dmi_darwin(void)
+{
+ pr_notice("DMI detected to setup _OSI(\"Darwin\"): Apple hardware\n");
+ osi_config.darwin_dmi = 1;
+ __acpi_osi_setup_darwin(true);
+}
+
+static void __init acpi_osi_dmi_linux(bool enable,
+ const struct dmi_system_id *d)
+{
+ pr_notice("DMI detected to setup _OSI(\"Linux\"): %s\n", d->ident);
+ osi_config.linux_dmi = 1;
+ __acpi_osi_setup_linux(enable);
+}
+
+static int __init dmi_enable_osi_linux(const struct dmi_system_id *d)
+{
+ acpi_osi_dmi_linux(true, d);
+
+ return 0;
+}
+
+static int __init dmi_disable_osi_vista(const struct dmi_system_id *d)
+{
+ pr_notice("DMI detected: %s\n", d->ident);
+ acpi_osi_setup("!Windows 2006");
+ acpi_osi_setup("!Windows 2006 SP1");
+ acpi_osi_setup("!Windows 2006 SP2");
+
+ return 0;
+}
+
+static int __init dmi_disable_osi_win7(const struct dmi_system_id *d)
+{
+ pr_notice("DMI detected: %s\n", d->ident);
+ acpi_osi_setup("!Windows 2009");
+
+ return 0;
+}
+
+static int __init dmi_disable_osi_win8(const struct dmi_system_id *d)
+{
+ pr_notice("DMI detected: %s\n", d->ident);
+ acpi_osi_setup("!Windows 2012");
+
+ return 0;
+}
+
+/*
+ * Linux default _OSI response behavior is determined by this DMI table.
+ *
+ * Note that _OSI("Linux")/_OSI("Darwin") determined here can be overridden
+ * by acpi_osi=!Linux/acpi_osi=!Darwin command line options.
+ */
+static const struct dmi_system_id acpi_osi_dmi_table[] __initconst = {
+ {
+ .callback = dmi_disable_osi_vista,
+ .ident = "Fujitsu Siemens",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
+ },
+ },
+ {
+ /*
+ * There have a NVIF method in MSI GX723 DSDT need call by Nvidia
+ * driver (e.g. nouveau) when user press brightness hotkey.
+ * Currently, nouveau driver didn't do the job and it causes there
+ * have a infinite while loop in DSDT when user press hotkey.
+ * We add MSI GX723's dmi information to this table for workaround
+ * this issue.
+ * Will remove MSI GX723 from the table after nouveau grows support.
+ */
+ .callback = dmi_disable_osi_vista,
+ .ident = "MSI GX723",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX723"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_vista,
+ .ident = "Sony VGN-NS10J_S",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_vista,
+ .ident = "Sony VGN-SR290J",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR290J"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_vista,
+ .ident = "VGN-NS50B_L",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS50B_L"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_vista,
+ .ident = "VGN-SR19XN",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_vista,
+ .ident = "Toshiba Satellite L355",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Satellite L355"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_win7,
+ .ident = "ASUS K50IJ",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "K50IJ"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_vista,
+ .ident = "Toshiba P305D",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P305D"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_vista,
+ .ident = "Toshiba NB100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NB100"),
+ },
+ },
+
+ /*
+ * The wireless hotkey does not work on those machines when
+ * returning true for _OSI("Windows 2012")
+ */
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Inspiron 7737",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Inspiron 7537",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Inspiron 5437",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Inspiron 3437",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Vostro 3446",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
+ },
+ },
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Vostro 3546",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"),
+ },
+ },
+
+ /*
+ * BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
+ * Linux ignores it, except for the machines enumerated below.
+ */
+
+ /*
+ * Without this EEEpc exports a non working WMI interface, with
+ * this it exports a working "good old" eeepc_laptop interface,
+ * fixing both brightness control, and rfkill not working.
+ */
+ {
+ .callback = dmi_enable_osi_linux,
+ .ident = "Asus EEE PC 1015PX",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"),
+ },
+ },
+ {}
+};
+
+static __init void acpi_osi_dmi_blacklisted(void)
+{
+ dmi_check_system(acpi_osi_dmi_table);
+
+ /* Enable _OSI("Darwin") for Apple platforms. */
+ if (x86_apple_machine)
+ acpi_osi_dmi_darwin();
+}
+
+int __init early_acpi_osi_init(void)
+{
+ acpi_osi_dmi_blacklisted();
+
+ return 0;
+}
+
+int __init acpi_osi_init(void)
+{
+ acpi_install_interface_handler(acpi_osi_handler);
+ acpi_osi_setup_late();
+
+ return 0;
+}
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
new file mode 100644
index 000000000..3269a888f
--- /dev/null
+++ b/drivers/acpi/osl.c
@@ -0,0 +1,1770 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * acpi_osl.c - OS-dependent functions ($Revision: 83 $)
+ *
+ * Copyright (C) 2000 Andrew Henroid
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (c) 2008 Intel Corporation
+ * Author: Matthew Wilcox <willy@linux.intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: OSL: " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/lockdep.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/kmod.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/nmi.h>
+#include <linux/acpi.h>
+#include <linux/efi.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/jiffies.h>
+#include <linux/semaphore.h>
+#include <linux/security.h>
+
+#include <asm/io.h>
+#include <linux/uaccess.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+
+#include "acpica/accommon.h"
+#include "internal.h"
+
+/* Definitions for ACPI_DEBUG_PRINT() */
+#define _COMPONENT ACPI_OS_SERVICES
+ACPI_MODULE_NAME("osl");
+
+struct acpi_os_dpc {
+ acpi_osd_exec_callback function;
+ void *context;
+ struct work_struct work;
+};
+
+#ifdef ENABLE_DEBUGGER
+#include <linux/kdb.h>
+
+/* stuff for debugger support */
+int acpi_in_debugger;
+EXPORT_SYMBOL(acpi_in_debugger);
+#endif /*ENABLE_DEBUGGER */
+
+static int (*__acpi_os_prepare_sleep)(u8 sleep_state, u32 pm1a_ctrl,
+ u32 pm1b_ctrl);
+static int (*__acpi_os_prepare_extended_sleep)(u8 sleep_state, u32 val_a,
+ u32 val_b);
+
+static acpi_osd_handler acpi_irq_handler;
+static void *acpi_irq_context;
+static struct workqueue_struct *kacpid_wq;
+static struct workqueue_struct *kacpi_notify_wq;
+static struct workqueue_struct *kacpi_hotplug_wq;
+static bool acpi_os_initialized;
+unsigned int acpi_sci_irq = INVALID_ACPI_IRQ;
+bool acpi_permanent_mmap = false;
+
+/*
+ * This list of permanent mappings is for memory that may be accessed from
+ * interrupt context, where we can't do the ioremap().
+ */
+struct acpi_ioremap {
+ struct list_head list;
+ void __iomem *virt;
+ acpi_physical_address phys;
+ acpi_size size;
+ union {
+ unsigned long refcount;
+ struct rcu_work rwork;
+ } track;
+};
+
+static LIST_HEAD(acpi_ioremaps);
+static DEFINE_MUTEX(acpi_ioremap_lock);
+#define acpi_ioremap_lock_held() lock_is_held(&acpi_ioremap_lock.dep_map)
+
+static void __init acpi_request_region (struct acpi_generic_address *gas,
+ unsigned int length, char *desc)
+{
+ u64 addr;
+
+ /* Handle possible alignment issues */
+ memcpy(&addr, &gas->address, sizeof(addr));
+ if (!addr || !length)
+ return;
+
+ /* Resources are never freed */
+ if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO)
+ request_region(addr, length, desc);
+ else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ request_mem_region(addr, length, desc);
+}
+
+static int __init acpi_reserve_resources(void)
+{
+ acpi_request_region(&acpi_gbl_FADT.xpm1a_event_block, acpi_gbl_FADT.pm1_event_length,
+ "ACPI PM1a_EVT_BLK");
+
+ acpi_request_region(&acpi_gbl_FADT.xpm1b_event_block, acpi_gbl_FADT.pm1_event_length,
+ "ACPI PM1b_EVT_BLK");
+
+ acpi_request_region(&acpi_gbl_FADT.xpm1a_control_block, acpi_gbl_FADT.pm1_control_length,
+ "ACPI PM1a_CNT_BLK");
+
+ acpi_request_region(&acpi_gbl_FADT.xpm1b_control_block, acpi_gbl_FADT.pm1_control_length,
+ "ACPI PM1b_CNT_BLK");
+
+ if (acpi_gbl_FADT.pm_timer_length == 4)
+ acpi_request_region(&acpi_gbl_FADT.xpm_timer_block, 4, "ACPI PM_TMR");
+
+ acpi_request_region(&acpi_gbl_FADT.xpm2_control_block, acpi_gbl_FADT.pm2_control_length,
+ "ACPI PM2_CNT_BLK");
+
+ /* Length of GPE blocks must be a non-negative multiple of 2 */
+
+ if (!(acpi_gbl_FADT.gpe0_block_length & 0x1))
+ acpi_request_region(&acpi_gbl_FADT.xgpe0_block,
+ acpi_gbl_FADT.gpe0_block_length, "ACPI GPE0_BLK");
+
+ if (!(acpi_gbl_FADT.gpe1_block_length & 0x1))
+ acpi_request_region(&acpi_gbl_FADT.xgpe1_block,
+ acpi_gbl_FADT.gpe1_block_length, "ACPI GPE1_BLK");
+
+ return 0;
+}
+fs_initcall_sync(acpi_reserve_resources);
+
+void acpi_os_printf(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ acpi_os_vprintf(fmt, args);
+ va_end(args);
+}
+EXPORT_SYMBOL(acpi_os_printf);
+
+void acpi_os_vprintf(const char *fmt, va_list args)
+{
+ static char buffer[512];
+
+ vsprintf(buffer, fmt, args);
+
+#ifdef ENABLE_DEBUGGER
+ if (acpi_in_debugger) {
+ kdb_printf("%s", buffer);
+ } else {
+ if (printk_get_level(buffer))
+ printk("%s", buffer);
+ else
+ printk(KERN_CONT "%s", buffer);
+ }
+#else
+ if (acpi_debugger_write_log(buffer) < 0) {
+ if (printk_get_level(buffer))
+ printk("%s", buffer);
+ else
+ printk(KERN_CONT "%s", buffer);
+ }
+#endif
+}
+
+#ifdef CONFIG_KEXEC
+static unsigned long acpi_rsdp;
+static int __init setup_acpi_rsdp(char *arg)
+{
+ return kstrtoul(arg, 16, &acpi_rsdp);
+}
+early_param("acpi_rsdp", setup_acpi_rsdp);
+#endif
+
+acpi_physical_address __init acpi_os_get_root_pointer(void)
+{
+ acpi_physical_address pa;
+
+#ifdef CONFIG_KEXEC
+ /*
+ * We may have been provided with an RSDP on the command line,
+ * but if a malicious user has done so they may be pointing us
+ * at modified ACPI tables that could alter kernel behaviour -
+ * so, we check the lockdown status before making use of
+ * it. If we trust it then also stash it in an architecture
+ * specific location (if appropriate) so it can be carried
+ * over further kexec()s.
+ */
+ if (acpi_rsdp && !security_locked_down(LOCKDOWN_ACPI_TABLES)) {
+ acpi_arch_set_root_pointer(acpi_rsdp);
+ return acpi_rsdp;
+ }
+#endif
+ pa = acpi_arch_get_root_pointer();
+ if (pa)
+ return pa;
+
+ if (efi_enabled(EFI_CONFIG_TABLES)) {
+ if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
+ return efi.acpi20;
+ if (efi.acpi != EFI_INVALID_TABLE_ADDR)
+ return efi.acpi;
+ pr_err("System description tables not found\n");
+ } else if (IS_ENABLED(CONFIG_ACPI_LEGACY_TABLES_LOOKUP)) {
+ acpi_find_root_pointer(&pa);
+ }
+
+ return pa;
+}
+
+/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */
+static struct acpi_ioremap *
+acpi_map_lookup(acpi_physical_address phys, acpi_size size)
+{
+ struct acpi_ioremap *map;
+
+ list_for_each_entry_rcu(map, &acpi_ioremaps, list, acpi_ioremap_lock_held())
+ if (map->phys <= phys &&
+ phys + size <= map->phys + map->size)
+ return map;
+
+ return NULL;
+}
+
+/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */
+static void __iomem *
+acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size)
+{
+ struct acpi_ioremap *map;
+
+ map = acpi_map_lookup(phys, size);
+ if (map)
+ return map->virt + (phys - map->phys);
+
+ return NULL;
+}
+
+void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size)
+{
+ struct acpi_ioremap *map;
+ void __iomem *virt = NULL;
+
+ mutex_lock(&acpi_ioremap_lock);
+ map = acpi_map_lookup(phys, size);
+ if (map) {
+ virt = map->virt + (phys - map->phys);
+ map->track.refcount++;
+ }
+ mutex_unlock(&acpi_ioremap_lock);
+ return virt;
+}
+EXPORT_SYMBOL_GPL(acpi_os_get_iomem);
+
+/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */
+static struct acpi_ioremap *
+acpi_map_lookup_virt(void __iomem *virt, acpi_size size)
+{
+ struct acpi_ioremap *map;
+
+ list_for_each_entry_rcu(map, &acpi_ioremaps, list, acpi_ioremap_lock_held())
+ if (map->virt <= virt &&
+ virt + size <= map->virt + map->size)
+ return map;
+
+ return NULL;
+}
+
+#if defined(CONFIG_IA64) || defined(CONFIG_ARM64)
+/* ioremap will take care of cache attributes */
+#define should_use_kmap(pfn) 0
+#else
+#define should_use_kmap(pfn) page_is_ram(pfn)
+#endif
+
+static void __iomem *acpi_map(acpi_physical_address pg_off, unsigned long pg_sz)
+{
+ unsigned long pfn;
+
+ pfn = pg_off >> PAGE_SHIFT;
+ if (should_use_kmap(pfn)) {
+ if (pg_sz > PAGE_SIZE)
+ return NULL;
+ return (void __iomem __force *)kmap(pfn_to_page(pfn));
+ } else
+ return acpi_os_ioremap(pg_off, pg_sz);
+}
+
+static void acpi_unmap(acpi_physical_address pg_off, void __iomem *vaddr)
+{
+ unsigned long pfn;
+
+ pfn = pg_off >> PAGE_SHIFT;
+ if (should_use_kmap(pfn))
+ kunmap(pfn_to_page(pfn));
+ else
+ iounmap(vaddr);
+}
+
+/**
+ * acpi_os_map_iomem - Get a virtual address for a given physical address range.
+ * @phys: Start of the physical address range to map.
+ * @size: Size of the physical address range to map.
+ *
+ * Look up the given physical address range in the list of existing ACPI memory
+ * mappings. If found, get a reference to it and return a pointer to it (its
+ * virtual address). If not found, map it, add it to that list and return a
+ * pointer to it.
+ *
+ * During early init (when acpi_permanent_mmap has not been set yet) this
+ * routine simply calls __acpi_map_table() to get the job done.
+ */
+void __iomem __ref
+*acpi_os_map_iomem(acpi_physical_address phys, acpi_size size)
+{
+ struct acpi_ioremap *map;
+ void __iomem *virt;
+ acpi_physical_address pg_off;
+ acpi_size pg_sz;
+
+ if (phys > ULONG_MAX) {
+ pr_err("Cannot map memory that high: 0x%llx\n", phys);
+ return NULL;
+ }
+
+ if (!acpi_permanent_mmap)
+ return __acpi_map_table((unsigned long)phys, size);
+
+ mutex_lock(&acpi_ioremap_lock);
+ /* Check if there's a suitable mapping already. */
+ map = acpi_map_lookup(phys, size);
+ if (map) {
+ map->track.refcount++;
+ goto out;
+ }
+
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (!map) {
+ mutex_unlock(&acpi_ioremap_lock);
+ return NULL;
+ }
+
+ pg_off = round_down(phys, PAGE_SIZE);
+ pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off;
+ virt = acpi_map(phys, size);
+ if (!virt) {
+ mutex_unlock(&acpi_ioremap_lock);
+ kfree(map);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&map->list);
+ map->virt = (void __iomem __force *)((unsigned long)virt & PAGE_MASK);
+ map->phys = pg_off;
+ map->size = pg_sz;
+ map->track.refcount = 1;
+
+ list_add_tail_rcu(&map->list, &acpi_ioremaps);
+
+out:
+ mutex_unlock(&acpi_ioremap_lock);
+ return map->virt + (phys - map->phys);
+}
+EXPORT_SYMBOL_GPL(acpi_os_map_iomem);
+
+void *__ref acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
+{
+ return (void *)acpi_os_map_iomem(phys, size);
+}
+EXPORT_SYMBOL_GPL(acpi_os_map_memory);
+
+static void acpi_os_map_remove(struct work_struct *work)
+{
+ struct acpi_ioremap *map = container_of(to_rcu_work(work),
+ struct acpi_ioremap,
+ track.rwork);
+
+ acpi_unmap(map->phys, map->virt);
+ kfree(map);
+}
+
+/* Must be called with mutex_lock(&acpi_ioremap_lock) */
+static void acpi_os_drop_map_ref(struct acpi_ioremap *map)
+{
+ if (--map->track.refcount)
+ return;
+
+ list_del_rcu(&map->list);
+
+ INIT_RCU_WORK(&map->track.rwork, acpi_os_map_remove);
+ queue_rcu_work(system_wq, &map->track.rwork);
+}
+
+/**
+ * acpi_os_unmap_iomem - Drop a memory mapping reference.
+ * @virt: Start of the address range to drop a reference to.
+ * @size: Size of the address range to drop a reference to.
+ *
+ * Look up the given virtual address range in the list of existing ACPI memory
+ * mappings, drop a reference to it and if there are no more active references
+ * to it, queue it up for later removal.
+ *
+ * During early init (when acpi_permanent_mmap has not been set yet) this
+ * routine simply calls __acpi_unmap_table() to get the job done. Since
+ * __acpi_unmap_table() is an __init function, the __ref annotation is needed
+ * here.
+ */
+void __ref acpi_os_unmap_iomem(void __iomem *virt, acpi_size size)
+{
+ struct acpi_ioremap *map;
+
+ if (!acpi_permanent_mmap) {
+ __acpi_unmap_table(virt, size);
+ return;
+ }
+
+ mutex_lock(&acpi_ioremap_lock);
+
+ map = acpi_map_lookup_virt(virt, size);
+ if (!map) {
+ mutex_unlock(&acpi_ioremap_lock);
+ WARN(true, "ACPI: %s: bad address %p\n", __func__, virt);
+ return;
+ }
+ acpi_os_drop_map_ref(map);
+
+ mutex_unlock(&acpi_ioremap_lock);
+}
+EXPORT_SYMBOL_GPL(acpi_os_unmap_iomem);
+
+/**
+ * acpi_os_unmap_memory - Drop a memory mapping reference.
+ * @virt: Start of the address range to drop a reference to.
+ * @size: Size of the address range to drop a reference to.
+ */
+void __ref acpi_os_unmap_memory(void *virt, acpi_size size)
+{
+ acpi_os_unmap_iomem((void __iomem *)virt, size);
+}
+EXPORT_SYMBOL_GPL(acpi_os_unmap_memory);
+
+void __iomem *acpi_os_map_generic_address(struct acpi_generic_address *gas)
+{
+ u64 addr;
+
+ if (gas->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ return NULL;
+
+ /* Handle possible alignment issues */
+ memcpy(&addr, &gas->address, sizeof(addr));
+ if (!addr || !gas->bit_width)
+ return NULL;
+
+ return acpi_os_map_iomem(addr, gas->bit_width / 8);
+}
+EXPORT_SYMBOL(acpi_os_map_generic_address);
+
+void acpi_os_unmap_generic_address(struct acpi_generic_address *gas)
+{
+ u64 addr;
+ struct acpi_ioremap *map;
+
+ if (gas->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ return;
+
+ /* Handle possible alignment issues */
+ memcpy(&addr, &gas->address, sizeof(addr));
+ if (!addr || !gas->bit_width)
+ return;
+
+ mutex_lock(&acpi_ioremap_lock);
+
+ map = acpi_map_lookup(addr, gas->bit_width / 8);
+ if (!map) {
+ mutex_unlock(&acpi_ioremap_lock);
+ return;
+ }
+ acpi_os_drop_map_ref(map);
+
+ mutex_unlock(&acpi_ioremap_lock);
+}
+EXPORT_SYMBOL(acpi_os_unmap_generic_address);
+
+#ifdef ACPI_FUTURE_USAGE
+acpi_status
+acpi_os_get_physical_address(void *virt, acpi_physical_address * phys)
+{
+ if (!phys || !virt)
+ return AE_BAD_PARAMETER;
+
+ *phys = virt_to_phys(virt);
+
+ return AE_OK;
+}
+#endif
+
+#ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE
+static bool acpi_rev_override;
+
+int __init acpi_rev_override_setup(char *str)
+{
+ acpi_rev_override = true;
+ return 1;
+}
+__setup("acpi_rev_override", acpi_rev_override_setup);
+#else
+#define acpi_rev_override false
+#endif
+
+#define ACPI_MAX_OVERRIDE_LEN 100
+
+static char acpi_os_name[ACPI_MAX_OVERRIDE_LEN];
+
+acpi_status
+acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
+ acpi_string *new_val)
+{
+ if (!init_val || !new_val)
+ return AE_BAD_PARAMETER;
+
+ *new_val = NULL;
+ if (!memcmp(init_val->name, "_OS_", 4) && strlen(acpi_os_name)) {
+ pr_info("Overriding _OS definition to '%s'\n", acpi_os_name);
+ *new_val = acpi_os_name;
+ }
+
+ if (!memcmp(init_val->name, "_REV", 4) && acpi_rev_override) {
+ pr_info("Overriding _REV return value to 5\n");
+ *new_val = (char *)5;
+ }
+
+ return AE_OK;
+}
+
+static irqreturn_t acpi_irq(int irq, void *dev_id)
+{
+ u32 handled;
+
+ handled = (*acpi_irq_handler) (acpi_irq_context);
+
+ if (handled) {
+ acpi_irq_handled++;
+ return IRQ_HANDLED;
+ } else {
+ acpi_irq_not_handled++;
+ return IRQ_NONE;
+ }
+}
+
+acpi_status
+acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
+ void *context)
+{
+ unsigned int irq;
+
+ acpi_irq_stats_init();
+
+ /*
+ * ACPI interrupts different from the SCI in our copy of the FADT are
+ * not supported.
+ */
+ if (gsi != acpi_gbl_FADT.sci_interrupt)
+ return AE_BAD_PARAMETER;
+
+ if (acpi_irq_handler)
+ return AE_ALREADY_ACQUIRED;
+
+ if (acpi_gsi_to_irq(gsi, &irq) < 0) {
+ pr_err("SCI (ACPI GSI %d) not registered\n", gsi);
+ return AE_OK;
+ }
+
+ acpi_irq_handler = handler;
+ acpi_irq_context = context;
+ if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) {
+ pr_err("SCI (IRQ%d) allocation failed\n", irq);
+ acpi_irq_handler = NULL;
+ return AE_NOT_ACQUIRED;
+ }
+ acpi_sci_irq = irq;
+
+ return AE_OK;
+}
+
+acpi_status acpi_os_remove_interrupt_handler(u32 gsi, acpi_osd_handler handler)
+{
+ if (gsi != acpi_gbl_FADT.sci_interrupt || !acpi_sci_irq_valid())
+ return AE_BAD_PARAMETER;
+
+ free_irq(acpi_sci_irq, acpi_irq);
+ acpi_irq_handler = NULL;
+ acpi_sci_irq = INVALID_ACPI_IRQ;
+
+ return AE_OK;
+}
+
+/*
+ * Running in interpreter thread context, safe to sleep
+ */
+
+void acpi_os_sleep(u64 ms)
+{
+ msleep(ms);
+}
+
+void acpi_os_stall(u32 us)
+{
+ while (us) {
+ u32 delay = 1000;
+
+ if (delay > us)
+ delay = us;
+ udelay(delay);
+ touch_nmi_watchdog();
+ us -= delay;
+ }
+}
+
+/*
+ * Support ACPI 3.0 AML Timer operand. Returns a 64-bit free-running,
+ * monotonically increasing timer with 100ns granularity. Do not use
+ * ktime_get() to implement this function because this function may get
+ * called after timekeeping has been suspended. Note: calling this function
+ * after timekeeping has been suspended may lead to unexpected results
+ * because when timekeeping is suspended the jiffies counter is not
+ * incremented. See also timekeeping_suspend().
+ */
+u64 acpi_os_get_timer(void)
+{
+ return (get_jiffies_64() - INITIAL_JIFFIES) *
+ (ACPI_100NSEC_PER_SEC / HZ);
+}
+
+acpi_status acpi_os_read_port(acpi_io_address port, u32 *value, u32 width)
+{
+ u32 dummy;
+
+ if (value)
+ *value = 0;
+ else
+ value = &dummy;
+
+ if (width <= 8) {
+ *value = inb(port);
+ } else if (width <= 16) {
+ *value = inw(port);
+ } else if (width <= 32) {
+ *value = inl(port);
+ } else {
+ pr_debug("%s: Access width %d not supported\n", __func__, width);
+ return AE_BAD_PARAMETER;
+ }
+
+ return AE_OK;
+}
+
+EXPORT_SYMBOL(acpi_os_read_port);
+
+acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width)
+{
+ if (width <= 8) {
+ outb(value, port);
+ } else if (width <= 16) {
+ outw(value, port);
+ } else if (width <= 32) {
+ outl(value, port);
+ } else {
+ pr_debug("%s: Access width %d not supported\n", __func__, width);
+ return AE_BAD_PARAMETER;
+ }
+
+ return AE_OK;
+}
+
+EXPORT_SYMBOL(acpi_os_write_port);
+
+int acpi_os_read_iomem(void __iomem *virt_addr, u64 *value, u32 width)
+{
+
+ switch (width) {
+ case 8:
+ *(u8 *) value = readb(virt_addr);
+ break;
+ case 16:
+ *(u16 *) value = readw(virt_addr);
+ break;
+ case 32:
+ *(u32 *) value = readl(virt_addr);
+ break;
+ case 64:
+ *(u64 *) value = readq(virt_addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+acpi_status
+acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width)
+{
+ void __iomem *virt_addr;
+ unsigned int size = width / 8;
+ bool unmap = false;
+ u64 dummy;
+ int error;
+
+ rcu_read_lock();
+ virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
+ if (!virt_addr) {
+ rcu_read_unlock();
+ virt_addr = acpi_os_ioremap(phys_addr, size);
+ if (!virt_addr)
+ return AE_BAD_ADDRESS;
+ unmap = true;
+ }
+
+ if (!value)
+ value = &dummy;
+
+ error = acpi_os_read_iomem(virt_addr, value, width);
+ BUG_ON(error);
+
+ if (unmap)
+ iounmap(virt_addr);
+ else
+ rcu_read_unlock();
+
+ return AE_OK;
+}
+
+acpi_status
+acpi_os_write_memory(acpi_physical_address phys_addr, u64 value, u32 width)
+{
+ void __iomem *virt_addr;
+ unsigned int size = width / 8;
+ bool unmap = false;
+
+ rcu_read_lock();
+ virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
+ if (!virt_addr) {
+ rcu_read_unlock();
+ virt_addr = acpi_os_ioremap(phys_addr, size);
+ if (!virt_addr)
+ return AE_BAD_ADDRESS;
+ unmap = true;
+ }
+
+ switch (width) {
+ case 8:
+ writeb(value, virt_addr);
+ break;
+ case 16:
+ writew(value, virt_addr);
+ break;
+ case 32:
+ writel(value, virt_addr);
+ break;
+ case 64:
+ writeq(value, virt_addr);
+ break;
+ default:
+ BUG();
+ }
+
+ if (unmap)
+ iounmap(virt_addr);
+ else
+ rcu_read_unlock();
+
+ return AE_OK;
+}
+
+#ifdef CONFIG_PCI
+acpi_status
+acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
+ u64 *value, u32 width)
+{
+ int result, size;
+ u32 value32;
+
+ if (!value)
+ return AE_BAD_PARAMETER;
+
+ switch (width) {
+ case 8:
+ size = 1;
+ break;
+ case 16:
+ size = 2;
+ break;
+ case 32:
+ size = 4;
+ break;
+ default:
+ return AE_ERROR;
+ }
+
+ result = raw_pci_read(pci_id->segment, pci_id->bus,
+ PCI_DEVFN(pci_id->device, pci_id->function),
+ reg, size, &value32);
+ *value = value32;
+
+ return (result ? AE_ERROR : AE_OK);
+}
+
+acpi_status
+acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
+ u64 value, u32 width)
+{
+ int result, size;
+
+ switch (width) {
+ case 8:
+ size = 1;
+ break;
+ case 16:
+ size = 2;
+ break;
+ case 32:
+ size = 4;
+ break;
+ default:
+ return AE_ERROR;
+ }
+
+ result = raw_pci_write(pci_id->segment, pci_id->bus,
+ PCI_DEVFN(pci_id->device, pci_id->function),
+ reg, size, value);
+
+ return (result ? AE_ERROR : AE_OK);
+}
+#endif
+
+static void acpi_os_execute_deferred(struct work_struct *work)
+{
+ struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
+
+ dpc->function(dpc->context);
+ kfree(dpc);
+}
+
+#ifdef CONFIG_ACPI_DEBUGGER
+static struct acpi_debugger acpi_debugger;
+static bool acpi_debugger_initialized;
+
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_debugger.lock);
+ if (acpi_debugger.ops) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+
+ acpi_debugger.owner = owner;
+ acpi_debugger.ops = ops;
+
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+EXPORT_SYMBOL(acpi_register_debugger);
+
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+ mutex_lock(&acpi_debugger.lock);
+ if (ops == acpi_debugger.ops) {
+ acpi_debugger.ops = NULL;
+ acpi_debugger.owner = NULL;
+ }
+ mutex_unlock(&acpi_debugger.lock);
+}
+EXPORT_SYMBOL(acpi_unregister_debugger);
+
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ int ret;
+ int (*func)(acpi_osd_exec_callback, void *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->create_thread;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(function, context);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_write_log(const char *msg)
+{
+ ssize_t ret;
+ ssize_t (*func)(const char *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->write_log;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(msg);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length)
+{
+ ssize_t ret;
+ ssize_t (*func)(char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->read_cmd;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(buffer, buffer_length);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_wait_command_ready(void)
+{
+ int ret;
+ int (*func)(bool, char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->wait_command_ready;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(acpi_gbl_method_executing,
+ acpi_gbl_db_line_buf, ACPI_DB_LINE_BUFFER_SIZE);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_notify_command_complete(void)
+{
+ int ret;
+ int (*func)(void);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->notify_command_complete;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func();
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int __init acpi_debugger_init(void)
+{
+ mutex_init(&acpi_debugger.lock);
+ acpi_debugger_initialized = true;
+ return 0;
+}
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_execute
+ *
+ * PARAMETERS: Type - Type of the callback
+ * Function - Function to be executed
+ * Context - Function parameters
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Depending on type, either queues function for deferred execution or
+ * immediately executes function on a separate thread.
+ *
+ ******************************************************************************/
+
+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));
+
+ if (type == OSL_DEBUGGER_MAIN_THREAD) {
+ ret = acpi_debugger_create_thread(function, context);
+ if (ret) {
+ pr_err("Kernel thread creation failed\n");
+ status = AE_ERROR;
+ }
+ goto out_thread;
+ }
+
+ /*
+ * Allocate/initialize DPC structure. Note that this memory will be
+ * freed by the callee. The kernel handles the work_struct list in a
+ * way that allows us to also free its memory inside the callee.
+ * Because we may want to schedule several tasks with different
+ * parameters we can't use the approach some kernel code uses of
+ * having a static work_struct.
+ */
+
+ dpc = kzalloc(sizeof(struct acpi_os_dpc), GFP_ATOMIC);
+ if (!dpc)
+ return AE_NO_MEMORY;
+
+ dpc->function = function;
+ dpc->context = context;
+
+ /*
+ * 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 {
+ pr_err("Unsupported os_execute type %d.\n", type);
+ status = AE_ERROR;
+ }
+
+ 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;
+ }
+err_workqueue:
+ if (ACPI_FAILURE(status))
+ kfree(dpc);
+out_thread:
+ return status;
+}
+EXPORT_SYMBOL(acpi_os_execute);
+
+void acpi_os_wait_events_complete(void)
+{
+ /*
+ * Make sure the GPE handler or the fixed event handler is not used
+ * on another CPU after removal.
+ */
+ if (acpi_sci_irq_valid())
+ synchronize_hardirq(acpi_sci_irq);
+ flush_workqueue(kacpid_wq);
+ flush_workqueue(kacpi_notify_wq);
+}
+EXPORT_SYMBOL(acpi_os_wait_events_complete);
+
+struct acpi_hp_work {
+ struct work_struct work;
+ struct acpi_device *adev;
+ u32 src;
+};
+
+static void acpi_hotplug_work_fn(struct work_struct *work)
+{
+ struct acpi_hp_work *hpw = container_of(work, struct acpi_hp_work, work);
+
+ acpi_os_wait_events_complete();
+ acpi_device_hotplug(hpw->adev, hpw->src);
+ kfree(hpw);
+}
+
+acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src)
+{
+ struct acpi_hp_work *hpw;
+
+ acpi_handle_debug(adev->handle,
+ "Scheduling hotplug event %u for deferred handling\n",
+ src);
+
+ hpw = kmalloc(sizeof(*hpw), GFP_KERNEL);
+ if (!hpw)
+ return AE_NO_MEMORY;
+
+ INIT_WORK(&hpw->work, acpi_hotplug_work_fn);
+ hpw->adev = adev;
+ hpw->src = src;
+ /*
+ * We can't run hotplug code in kacpid_wq/kacpid_notify_wq etc., because
+ * the hotplug code may call driver .remove() functions, which may
+ * invoke flush_scheduled_work()/acpi_os_wait_events_complete() to flush
+ * these workqueues.
+ */
+ if (!queue_work(kacpi_hotplug_wq, &hpw->work)) {
+ kfree(hpw);
+ return AE_ERROR;
+ }
+ return AE_OK;
+}
+
+bool acpi_queue_hotplug_work(struct work_struct *work)
+{
+ return queue_work(kacpi_hotplug_wq, work);
+}
+
+acpi_status
+acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle)
+{
+ struct semaphore *sem = NULL;
+
+ sem = acpi_os_allocate_zeroed(sizeof(struct semaphore));
+ if (!sem)
+ return AE_NO_MEMORY;
+
+ sema_init(sem, initial_units);
+
+ *handle = (acpi_handle *) sem;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Creating semaphore[%p|%d].\n",
+ *handle, initial_units));
+
+ return AE_OK;
+}
+
+/*
+ * TODO: A better way to delete semaphores? Linux doesn't have a
+ * 'delete_semaphore()' function -- may result in an invalid
+ * pointer dereference for non-synchronized consumers. Should
+ * we at least check for blocked threads and signal/cancel them?
+ */
+
+acpi_status acpi_os_delete_semaphore(acpi_handle handle)
+{
+ struct semaphore *sem = (struct semaphore *)handle;
+
+ if (!sem)
+ return AE_BAD_PARAMETER;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Deleting semaphore[%p].\n", handle));
+
+ BUG_ON(!list_empty(&sem->wait_list));
+ kfree(sem);
+ sem = NULL;
+
+ return AE_OK;
+}
+
+/*
+ * TODO: Support for units > 1?
+ */
+acpi_status acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 timeout)
+{
+ acpi_status status = AE_OK;
+ struct semaphore *sem = (struct semaphore *)handle;
+ long jiffies;
+ int ret = 0;
+
+ if (!acpi_os_initialized)
+ return AE_OK;
+
+ if (!sem || (units < 1))
+ return AE_BAD_PARAMETER;
+
+ if (units > 1)
+ return AE_SUPPORT;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Waiting for semaphore[%p|%d|%d]\n",
+ handle, units, timeout));
+
+ if (timeout == ACPI_WAIT_FOREVER)
+ jiffies = MAX_SCHEDULE_TIMEOUT;
+ else
+ jiffies = msecs_to_jiffies(timeout);
+
+ ret = down_timeout(sem, jiffies);
+ if (ret)
+ status = AE_TIME;
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
+ "Failed to acquire semaphore[%p|%d|%d], %s",
+ handle, units, timeout,
+ acpi_format_exception(status)));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
+ "Acquired semaphore[%p|%d|%d]", handle,
+ units, timeout));
+ }
+
+ return status;
+}
+
+/*
+ * TODO: Support for units > 1?
+ */
+acpi_status acpi_os_signal_semaphore(acpi_handle handle, u32 units)
+{
+ struct semaphore *sem = (struct semaphore *)handle;
+
+ if (!acpi_os_initialized)
+ return AE_OK;
+
+ if (!sem || (units < 1))
+ return AE_BAD_PARAMETER;
+
+ if (units > 1)
+ return AE_SUPPORT;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Signaling semaphore[%p|%d]\n", handle,
+ units));
+
+ up(sem);
+
+ return AE_OK;
+}
+
+acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
+{
+#ifdef ENABLE_DEBUGGER
+ if (acpi_in_debugger) {
+ u32 chars;
+
+ kdb_read(buffer, buffer_length);
+
+ /* remove the CR kdb includes */
+ chars = strlen(buffer) - 1;
+ buffer[chars] = '\0';
+ }
+#else
+ int ret;
+
+ ret = acpi_debugger_read_cmd(buffer, buffer_length);
+ if (ret < 0)
+ return AE_ERROR;
+ if (bytes_read)
+ *bytes_read = ret;
+#endif
+
+ return AE_OK;
+}
+EXPORT_SYMBOL(acpi_os_get_line);
+
+acpi_status acpi_os_wait_command_ready(void)
+{
+ int ret;
+
+ ret = acpi_debugger_wait_command_ready();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
+acpi_status acpi_os_notify_command_complete(void)
+{
+ int ret;
+
+ ret = acpi_debugger_notify_command_complete();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
+acpi_status acpi_os_signal(u32 function, void *info)
+{
+ switch (function) {
+ case ACPI_SIGNAL_FATAL:
+ pr_err("Fatal opcode executed\n");
+ break;
+ case ACPI_SIGNAL_BREAKPOINT:
+ /*
+ * AML Breakpoint
+ * ACPI spec. says to treat it as a NOP unless
+ * you are debugging. So if/when we integrate
+ * AML debugger into the kernel debugger its
+ * hook will go here. But until then it is
+ * not useful to print anything on breakpoints.
+ */
+ break;
+ default:
+ break;
+ }
+
+ return AE_OK;
+}
+
+static int __init acpi_os_name_setup(char *str)
+{
+ char *p = acpi_os_name;
+ int count = ACPI_MAX_OVERRIDE_LEN - 1;
+
+ if (!str || !*str)
+ return 0;
+
+ for (; count-- && *str; str++) {
+ if (isalnum(*str) || *str == ' ' || *str == ':')
+ *p++ = *str;
+ else if (*str == '\'' || *str == '"')
+ continue;
+ else
+ break;
+ }
+ *p = 0;
+
+ return 1;
+
+}
+
+__setup("acpi_os_name=", acpi_os_name_setup);
+
+/*
+ * Disable the auto-serialization of named objects creation methods.
+ *
+ * This feature is enabled by default. It marks the AML control methods
+ * that contain the opcodes to create named objects as "Serialized".
+ */
+static int __init acpi_no_auto_serialize_setup(char *str)
+{
+ acpi_gbl_auto_serialize_methods = FALSE;
+ pr_info("Auto-serialization disabled\n");
+
+ return 1;
+}
+
+__setup("acpi_no_auto_serialize", acpi_no_auto_serialize_setup);
+
+/* Check of resource interference between native drivers and ACPI
+ * OperationRegions (SystemIO and System Memory only).
+ * IO ports and memory declared in ACPI might be used by the ACPI subsystem
+ * in arbitrary AML code and can interfere with legacy drivers.
+ * acpi_enforce_resources= can be set to:
+ *
+ * - strict (default) (2)
+ * -> further driver trying to access the resources will not load
+ * - lax (1)
+ * -> further driver trying to access the resources will load, but you
+ * get a system message that something might go wrong...
+ *
+ * - no (0)
+ * -> ACPI Operation Region resources will not be registered
+ *
+ */
+#define ENFORCE_RESOURCES_STRICT 2
+#define ENFORCE_RESOURCES_LAX 1
+#define ENFORCE_RESOURCES_NO 0
+
+static unsigned int acpi_enforce_resources = ENFORCE_RESOURCES_STRICT;
+
+static int __init acpi_enforce_resources_setup(char *str)
+{
+ if (str == NULL || *str == '\0')
+ return 0;
+
+ if (!strcmp("strict", str))
+ acpi_enforce_resources = ENFORCE_RESOURCES_STRICT;
+ else if (!strcmp("lax", str))
+ acpi_enforce_resources = ENFORCE_RESOURCES_LAX;
+ else if (!strcmp("no", str))
+ acpi_enforce_resources = ENFORCE_RESOURCES_NO;
+
+ return 1;
+}
+
+__setup("acpi_enforce_resources=", acpi_enforce_resources_setup);
+
+/* Check for resource conflicts between ACPI OperationRegions and native
+ * drivers */
+int acpi_check_resource_conflict(const struct resource *res)
+{
+ acpi_adr_space_type space_id;
+
+ if (acpi_enforce_resources == ENFORCE_RESOURCES_NO)
+ return 0;
+
+ if (res->flags & IORESOURCE_IO)
+ space_id = ACPI_ADR_SPACE_SYSTEM_IO;
+ else if (res->flags & IORESOURCE_MEM)
+ space_id = ACPI_ADR_SPACE_SYSTEM_MEMORY;
+ else
+ return 0;
+
+ if (!acpi_check_address_range(space_id, res->start, resource_size(res), 1))
+ return 0;
+
+ pr_info("Resource conflict; ACPI support missing from driver?\n");
+
+ if (acpi_enforce_resources == ENFORCE_RESOURCES_STRICT)
+ return -EBUSY;
+
+ if (acpi_enforce_resources == ENFORCE_RESOURCES_LAX)
+ pr_notice("Resource conflict: System may be unstable or behave erratically\n");
+
+ return 0;
+}
+EXPORT_SYMBOL(acpi_check_resource_conflict);
+
+int acpi_check_region(resource_size_t start, resource_size_t n,
+ const char *name)
+{
+ struct resource res = DEFINE_RES_IO_NAMED(start, n, name);
+
+ return acpi_check_resource_conflict(&res);
+}
+EXPORT_SYMBOL(acpi_check_region);
+
+/*
+ * Let drivers know whether the resource checks are effective
+ */
+int acpi_resources_are_enforced(void)
+{
+ return acpi_enforce_resources == ENFORCE_RESOURCES_STRICT;
+}
+EXPORT_SYMBOL(acpi_resources_are_enforced);
+
+/*
+ * Deallocate the memory for a spinlock.
+ */
+void acpi_os_delete_lock(acpi_spinlock handle)
+{
+ ACPI_FREE(handle);
+}
+
+/*
+ * Acquire a spinlock.
+ *
+ * handle is a pointer to the spinlock_t.
+ */
+
+acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp)
+ __acquires(lockp)
+{
+ acpi_cpu_flags flags;
+ spin_lock_irqsave(lockp, flags);
+ return flags;
+}
+
+/*
+ * Release a spinlock. See above.
+ */
+
+void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags flags)
+ __releases(lockp)
+{
+ spin_unlock_irqrestore(lockp, flags);
+}
+
+#ifndef ACPI_USE_LOCAL_CACHE
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_create_cache
+ *
+ * PARAMETERS: name - Ascii name for the cache
+ * size - Size of each cached object
+ * depth - Maximum depth of the cache (in objects) <ignored>
+ * cache - Where the new cache object is returned
+ *
+ * RETURN: status
+ *
+ * DESCRIPTION: Create a cache object
+ *
+ ******************************************************************************/
+
+acpi_status
+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)
+ return AE_ERROR;
+ else
+ return AE_OK;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_purge_cache
+ *
+ * PARAMETERS: Cache - Handle to cache object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Free all objects within the requested cache.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_os_purge_cache(acpi_cache_t * cache)
+{
+ kmem_cache_shrink(cache);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_delete_cache
+ *
+ * PARAMETERS: Cache - Handle to cache object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Free all objects within the requested cache and delete the
+ * cache object.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_os_delete_cache(acpi_cache_t * cache)
+{
+ kmem_cache_destroy(cache);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_release_object
+ *
+ * PARAMETERS: Cache - Handle to cache object
+ * Object - The object to be released
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Release an object to the specified cache. If cache is full,
+ * the object is deleted.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object)
+{
+ kmem_cache_free(cache, object);
+ return (AE_OK);
+}
+#endif
+
+static int __init acpi_no_static_ssdt_setup(char *s)
+{
+ acpi_gbl_disable_ssdt_table_install = TRUE;
+ pr_info("Static SSDT installation disabled\n");
+
+ return 0;
+}
+
+early_param("acpi_no_static_ssdt", acpi_no_static_ssdt_setup);
+
+static int __init acpi_disable_return_repair(char *s)
+{
+ pr_notice("Predefined validation mechanism disabled\n");
+ acpi_gbl_disable_auto_repair = TRUE;
+
+ return 1;
+}
+
+__setup("acpica_no_return_repair", acpi_disable_return_repair);
+
+acpi_status __init acpi_os_initialize(void)
+{
+ acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1a_event_block);
+ acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1b_event_block);
+
+ acpi_gbl_xgpe0_block_logical_address =
+ (unsigned long)acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe0_block);
+ acpi_gbl_xgpe1_block_logical_address =
+ (unsigned long)acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe1_block);
+
+ if (acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) {
+ /*
+ * Use acpi_os_map_generic_address to pre-map the reset
+ * register if it's in system memory.
+ */
+ void *rv;
+
+ rv = acpi_os_map_generic_address(&acpi_gbl_FADT.reset_register);
+ pr_debug("%s: Reset register mapping %s\n", __func__,
+ rv ? "successful" : "failed");
+ }
+ acpi_os_initialized = true;
+
+ return AE_OK;
+}
+
+acpi_status __init acpi_os_initialize1(void)
+{
+ kacpid_wq = alloc_workqueue("kacpid", 0, 1);
+ kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
+ kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
+ BUG_ON(!kacpid_wq);
+ BUG_ON(!kacpi_notify_wq);
+ BUG_ON(!kacpi_hotplug_wq);
+ acpi_osi_init();
+ return AE_OK;
+}
+
+acpi_status acpi_os_terminate(void)
+{
+ if (acpi_irq_handler) {
+ acpi_os_remove_interrupt_handler(acpi_gbl_FADT.sci_interrupt,
+ acpi_irq_handler);
+ }
+
+ acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe1_block);
+ acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe0_block);
+ acpi_gbl_xgpe0_block_logical_address = 0UL;
+ acpi_gbl_xgpe1_block_logical_address = 0UL;
+
+ acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1b_event_block);
+ acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1a_event_block);
+
+ if (acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER)
+ acpi_os_unmap_generic_address(&acpi_gbl_FADT.reset_register);
+
+ destroy_workqueue(kacpid_wq);
+ destroy_workqueue(kacpi_notify_wq);
+ destroy_workqueue(kacpi_hotplug_wq);
+
+ return AE_OK;
+}
+
+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);
+ if (rc < 0)
+ return AE_ERROR;
+ else if (rc > 0)
+ return AE_CTRL_TERMINATE;
+
+ return AE_OK;
+}
+
+void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state,
+ u32 pm1a_ctrl, u32 pm1b_ctrl))
+{
+ __acpi_os_prepare_sleep = func;
+}
+
+#if (ACPI_REDUCED_HARDWARE)
+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);
+ if (rc < 0)
+ return AE_ERROR;
+ else if (rc > 0)
+ return AE_CTRL_TERMINATE;
+
+ return AE_OK;
+}
+#else
+acpi_status acpi_os_prepare_extended_sleep(u8 sleep_state, u32 val_a,
+ u32 val_b)
+{
+ return AE_OK;
+}
+#endif
+
+void acpi_os_set_prepare_extended_sleep(int (*func)(u8 sleep_state,
+ u32 val_a, u32 val_b))
+{
+ __acpi_os_prepare_extended_sleep = func;
+}
+
+acpi_status acpi_os_enter_sleep(u8 sleep_state,
+ u32 reg_a_value, u32 reg_b_value)
+{
+ acpi_status status;
+
+ if (acpi_gbl_reduced_hardware)
+ status = acpi_os_prepare_extended_sleep(sleep_state,
+ reg_a_value,
+ reg_b_value);
+ else
+ status = acpi_os_prepare_sleep(sleep_state,
+ reg_a_value, reg_b_value);
+ return status;
+}
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
new file mode 100644
index 000000000..08e15774f
--- /dev/null
+++ b/drivers/acpi/pci_irq.c
@@ -0,0 +1,514 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pci_irq.c - ACPI PCI Interrupt Routing ($Revision: 11 $)
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2002 Dominik Brodowski <devel@brodo.de>
+ * (c) Copyright 2008 Hewlett-Packard Development Company, L.P.
+ * Bjorn Helgaas <bjorn.helgaas@hp.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: PCI: " fmt
+
+#include <linux/dmi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+struct acpi_prt_entry {
+ struct acpi_pci_id id;
+ u8 pin;
+ acpi_handle link;
+ u32 index; /* GSI, or link _CRS index */
+};
+
+static inline char pin_name(int pin)
+{
+ return 'A' + pin - 1;
+}
+
+/* --------------------------------------------------------------------------
+ PCI IRQ Routing Table (PRT) Support
+ -------------------------------------------------------------------------- */
+
+/* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */
+static const struct dmi_system_id medion_md9580[] = {
+ {
+ .ident = "Medion MD9580-F laptop",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A555"),
+ },
+ },
+ { }
+};
+
+/* http://bugzilla.kernel.org/show_bug.cgi?id=5044 */
+static const struct dmi_system_id dell_optiplex[] = {
+ {
+ .ident = "Dell Optiplex GX1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX1 600S+"),
+ },
+ },
+ { }
+};
+
+/* http://bugzilla.kernel.org/show_bug.cgi?id=10138 */
+static const struct dmi_system_id hp_t5710[] = {
+ {
+ .ident = "HP t5710",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "hp t5000 series"),
+ DMI_MATCH(DMI_BOARD_NAME, "098Ch"),
+ },
+ },
+ { }
+};
+
+struct prt_quirk {
+ const struct dmi_system_id *system;
+ unsigned int segment;
+ unsigned int bus;
+ unsigned int device;
+ unsigned char pin;
+ const char *source; /* according to BIOS */
+ const char *actual_source;
+};
+
+#define PCI_INTX_PIN(c) (c - 'A' + 1)
+
+/*
+ * These systems have incorrect _PRT entries. The BIOS claims the PCI
+ * interrupt at the listed segment/bus/device/pin is connected to the first
+ * link device, but it is actually connected to the second.
+ */
+static const struct prt_quirk prt_quirks[] = {
+ { medion_md9580, 0, 0, 9, PCI_INTX_PIN('A'),
+ "\\_SB_.PCI0.ISA_.LNKA",
+ "\\_SB_.PCI0.ISA_.LNKB"},
+ { dell_optiplex, 0, 0, 0xd, PCI_INTX_PIN('A'),
+ "\\_SB_.LNKB",
+ "\\_SB_.LNKA"},
+ { hp_t5710, 0, 0, 1, PCI_INTX_PIN('A'),
+ "\\_SB_.PCI0.LNK1",
+ "\\_SB_.PCI0.LNK3"},
+};
+
+static void do_prt_fixups(struct acpi_prt_entry *entry,
+ struct acpi_pci_routing_table *prt)
+{
+ int i;
+ const struct prt_quirk *quirk;
+
+ for (i = 0; i < ARRAY_SIZE(prt_quirks); i++) {
+ quirk = &prt_quirks[i];
+
+ /* All current quirks involve link devices, not GSIs */
+ if (dmi_check_system(quirk->system) &&
+ entry->id.segment == quirk->segment &&
+ entry->id.bus == quirk->bus &&
+ entry->id.device == quirk->device &&
+ entry->pin == quirk->pin &&
+ !strcmp(prt->source, quirk->source) &&
+ strlen(prt->source) >= strlen(quirk->actual_source)) {
+ pr_warn("Firmware reports "
+ "%04x:%02x:%02x PCI INT %c connected to %s; "
+ "changing to %s\n",
+ entry->id.segment, entry->id.bus,
+ entry->id.device, pin_name(entry->pin),
+ prt->source, quirk->actual_source);
+ strcpy(prt->source, quirk->actual_source);
+ }
+ }
+}
+
+static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev,
+ int pin, struct acpi_pci_routing_table *prt,
+ struct acpi_prt_entry **entry_ptr)
+{
+ int segment = pci_domain_nr(dev->bus);
+ int bus = dev->bus->number;
+ int device = pci_ari_enabled(dev->bus) ? 0 : PCI_SLOT(dev->devfn);
+ struct acpi_prt_entry *entry;
+
+ if (((prt->address >> 16) & 0xffff) != device ||
+ prt->pin + 1 != pin)
+ return -ENODEV;
+
+ entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ /*
+ * Note that the _PRT uses 0=INTA, 1=INTB, etc, while PCI uses
+ * 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert
+ * it here.
+ */
+ entry->id.segment = segment;
+ entry->id.bus = bus;
+ entry->id.device = (prt->address >> 16) & 0xFFFF;
+ entry->pin = prt->pin + 1;
+
+ do_prt_fixups(entry, prt);
+
+ entry->index = prt->source_index;
+
+ /*
+ * Type 1: Dynamic
+ * ---------------
+ * The 'source' field specifies the PCI interrupt link device used to
+ * configure the IRQ assigned to this slot|dev|pin. The 'source_index'
+ * indicates which resource descriptor in the resource template (of
+ * the link device) this interrupt is allocated from.
+ *
+ * NOTE: Don't query the Link Device for IRQ information at this time
+ * because Link Device enumeration may not have occurred yet
+ * (e.g. exists somewhere 'below' this _PRT entry in the ACPI
+ * namespace).
+ */
+ if (prt->source[0])
+ acpi_get_handle(handle, prt->source, &entry->link);
+
+ /*
+ * Type 2: Static
+ * --------------
+ * The 'source' field is NULL, and the 'source_index' field specifies
+ * the IRQ value, which is hardwired to specific interrupt inputs on
+ * the interrupt controller.
+ */
+ pr_debug("%04x:%02x:%02x[%c] -> %s[%d]\n",
+ entry->id.segment, entry->id.bus, entry->id.device,
+ pin_name(entry->pin), prt->source, entry->index);
+
+ *entry_ptr = entry;
+
+ return 0;
+}
+
+static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev,
+ int pin, struct acpi_prt_entry **entry_ptr)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_pci_routing_table *entry;
+ acpi_handle handle = NULL;
+
+ if (dev->bus->bridge)
+ handle = ACPI_HANDLE(dev->bus->bridge);
+
+ if (!handle)
+ return -ENODEV;
+
+ /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */
+ status = acpi_get_irq_routing_table(handle, &buffer);
+ if (ACPI_FAILURE(status)) {
+ kfree(buffer.pointer);
+ return -ENODEV;
+ }
+
+ entry = buffer.pointer;
+ while (entry && (entry->length > 0)) {
+ if (!acpi_pci_irq_check_entry(handle, dev, pin,
+ entry, entry_ptr))
+ break;
+ entry = (struct acpi_pci_routing_table *)
+ ((unsigned long)entry + entry->length);
+ }
+
+ kfree(buffer.pointer);
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ PCI Interrupt Routing Support
+ -------------------------------------------------------------------------- */
+#ifdef CONFIG_X86_IO_APIC
+extern int noioapicquirk;
+extern int noioapicreroute;
+
+static int bridge_has_boot_interrupt_variant(struct pci_bus *bus)
+{
+ struct pci_bus *bus_it;
+
+ for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) {
+ if (!bus_it->self)
+ return 0;
+ if (bus_it->self->irq_reroute_variant)
+ return bus_it->self->irq_reroute_variant;
+ }
+ return 0;
+}
+
+/*
+ * Some chipsets (e.g. Intel 6700PXH) generate a legacy INTx when the IRQ
+ * entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel does
+ * during interrupt handling). When this INTx generation cannot be disabled,
+ * we reroute these interrupts to their legacy equivalent to get rid of
+ * spurious interrupts.
+ */
+static int acpi_reroute_boot_interrupt(struct pci_dev *dev,
+ struct acpi_prt_entry *entry)
+{
+ if (noioapicquirk || noioapicreroute) {
+ return 0;
+ } else {
+ switch (bridge_has_boot_interrupt_variant(dev->bus)) {
+ case 0:
+ /* no rerouting necessary */
+ return 0;
+ case INTEL_IRQ_REROUTE_VARIANT:
+ /*
+ * Remap according to INTx routing table in 6700PXH
+ * specs, intel order number 302628-002, section
+ * 2.15.2. Other chipsets (80332, ...) have the same
+ * mapping and are handled here as well.
+ */
+ dev_info(&dev->dev, "PCI IRQ %d -> rerouted to legacy "
+ "IRQ %d\n", entry->index,
+ (entry->index % 4) + 16);
+ entry->index = (entry->index % 4) + 16;
+ return 1;
+ default:
+ dev_warn(&dev->dev, "Cannot reroute IRQ %d to legacy "
+ "IRQ: unknown mapping\n", entry->index);
+ return -1;
+ }
+ }
+}
+#endif /* CONFIG_X86_IO_APIC */
+
+static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
+{
+ struct acpi_prt_entry *entry = NULL;
+ struct pci_dev *bridge;
+ u8 bridge_pin, orig_pin = pin;
+ int ret;
+
+ ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry);
+ if (!ret && entry) {
+#ifdef CONFIG_X86_IO_APIC
+ acpi_reroute_boot_interrupt(dev, entry);
+#endif /* CONFIG_X86_IO_APIC */
+ dev_dbg(&dev->dev, "Found [%c] _PRT entry\n", pin_name(pin));
+ return entry;
+ }
+
+ /*
+ * Attempt to derive an IRQ for this device from a parent bridge's
+ * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge).
+ */
+ bridge = dev->bus->self;
+ while (bridge) {
+ pin = pci_swizzle_interrupt_pin(dev, pin);
+
+ if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) {
+ /* PC card has the same IRQ as its cardbridge */
+ bridge_pin = bridge->pin;
+ if (!bridge_pin) {
+ dev_dbg(&bridge->dev, "No interrupt pin configured\n");
+ return NULL;
+ }
+ pin = bridge_pin;
+ }
+
+ ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry);
+ if (!ret && entry) {
+ dev_dbg(&dev->dev, "Derived GSI INT %c from %s\n",
+ pin_name(orig_pin), pci_name(bridge));
+ return entry;
+ }
+
+ dev = bridge;
+ bridge = dev->bus->self;
+ }
+
+ dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n",
+ pin_name(orig_pin));
+ return NULL;
+}
+
+#if IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_EISA)
+static int acpi_isa_register_gsi(struct pci_dev *dev)
+{
+ u32 dev_gsi;
+
+ /* Interrupt Line values above 0xF are forbidden */
+ if (dev->irq > 0 && (dev->irq <= 0xF) &&
+ acpi_isa_irq_available(dev->irq) &&
+ (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) {
+ dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n",
+ pin_name(dev->pin), dev->irq);
+ acpi_register_gsi(&dev->dev, dev_gsi,
+ ACPI_LEVEL_SENSITIVE,
+ ACPI_ACTIVE_LOW);
+ return 0;
+ }
+ return -EINVAL;
+}
+#else
+static inline int acpi_isa_register_gsi(struct pci_dev *dev)
+{
+ return -ENODEV;
+}
+#endif
+
+static inline bool acpi_pci_irq_valid(struct pci_dev *dev, u8 pin)
+{
+#ifdef CONFIG_X86
+ /*
+ * On x86 irq line 0xff means "unknown" or "no connection"
+ * (PCI 3.0, Section 6.2.4, footnote on page 223).
+ */
+ if (dev->irq == 0xff) {
+ dev->irq = IRQ_NOTCONNECTED;
+ dev_warn(&dev->dev, "PCI INT %c: not connected\n",
+ pin_name(pin));
+ return false;
+ }
+#endif
+ return true;
+}
+
+int acpi_pci_irq_enable(struct pci_dev *dev)
+{
+ struct acpi_prt_entry *entry;
+ int gsi;
+ u8 pin;
+ int triggering = ACPI_LEVEL_SENSITIVE;
+ /*
+ * On ARM systems with the GIC 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 ?
+ ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW;
+ char *link = NULL;
+ char link_desc[16];
+ int rc;
+
+ pin = dev->pin;
+ if (!pin) {
+ dev_dbg(&dev->dev, "No interrupt pin configured\n");
+ return 0;
+ }
+
+ if (dev->irq_managed && dev->irq > 0)
+ return 0;
+
+ entry = acpi_pci_irq_lookup(dev, pin);
+ if (!entry) {
+ /*
+ * IDE legacy mode controller IRQs are magic. Why do compat
+ * extensions always make such a nasty mess.
+ */
+ if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE &&
+ (dev->class & 0x05) == 0)
+ return 0;
+ }
+
+ if (entry) {
+ if (entry->link)
+ gsi = acpi_pci_link_allocate_irq(entry->link,
+ entry->index,
+ &triggering, &polarity,
+ &link);
+ else
+ gsi = entry->index;
+ } else
+ gsi = -1;
+
+ if (gsi < 0) {
+ /*
+ * No IRQ known to the ACPI subsystem - maybe the BIOS /
+ * driver reported one, then use it. Exit in any case.
+ */
+ if (!acpi_pci_irq_valid(dev, pin)) {
+ kfree(entry);
+ return 0;
+ }
+
+ if (acpi_isa_register_gsi(dev))
+ dev_warn(&dev->dev, "PCI INT %c: no GSI\n",
+ pin_name(pin));
+
+ kfree(entry);
+ return 0;
+ }
+
+ rc = acpi_register_gsi(&dev->dev, gsi, triggering, polarity);
+ if (rc < 0) {
+ dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n",
+ pin_name(pin));
+ kfree(entry);
+ return rc;
+ }
+ dev->irq = rc;
+ dev->irq_managed = 1;
+
+ if (link)
+ snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link);
+ else
+ link_desc[0] = '\0';
+
+ 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);
+
+ kfree(entry);
+ return 0;
+}
+
+void acpi_pci_irq_disable(struct pci_dev *dev)
+{
+ struct acpi_prt_entry *entry;
+ int gsi;
+ u8 pin;
+
+ pin = dev->pin;
+ if (!pin || !dev->irq_managed || dev->irq <= 0)
+ return;
+
+ /* Keep IOAPIC pin configuration when suspending */
+ if (dev->dev.power.is_prepared)
+ return;
+#ifdef CONFIG_PM
+ if (dev->dev.power.runtime_status == RPM_SUSPENDING)
+ return;
+#endif
+
+ entry = acpi_pci_irq_lookup(dev, pin);
+ if (!entry)
+ return;
+
+ if (entry->link)
+ gsi = acpi_pci_link_free_irq(entry->link);
+ else
+ gsi = entry->index;
+
+ kfree(entry);
+
+ /*
+ * TBD: It might be worth clearing dev->irq by magic constant
+ * (e.g. PCI_UNDEFINED_IRQ).
+ */
+
+ dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin));
+ if (gsi >= 0) {
+ acpi_unregister_gsi(gsi);
+ dev->irq_managed = 0;
+ }
+}
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
new file mode 100644
index 000000000..aa1038b8a
--- /dev/null
+++ b/drivers/acpi/pci_link.c
@@ -0,0 +1,907 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pci_link.c - ACPI PCI Interrupt Link Device Driver ($Revision: 34 $)
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2002 Dominik Brodowski <devel@brodo.de>
+ *
+ * TBD:
+ * 1. Support more than one IRQ resource entry per link device (index).
+ * 2. Implement start/stop mechanism and use ACPI Bus Driver facilities
+ * for IRQ management (e.g. start()->_SRS).
+ */
+
+#define pr_fmt(fmt) "ACPI: PCI: " fmt
+
+#include <linux/syscore_ops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/irq.h>
+
+#include "internal.h"
+
+#define ACPI_PCI_LINK_CLASS "pci_irq_routing"
+#define ACPI_PCI_LINK_DEVICE_NAME "PCI Interrupt Link"
+#define ACPI_PCI_LINK_MAX_POSSIBLE 16
+
+static int acpi_pci_link_add(struct acpi_device *device,
+ const struct acpi_device_id *not_used);
+static void acpi_pci_link_remove(struct acpi_device *device);
+
+static const struct acpi_device_id link_device_ids[] = {
+ {"PNP0C0F", 0},
+ {"", 0},
+};
+
+static struct acpi_scan_handler pci_link_handler = {
+ .ids = link_device_ids,
+ .attach = acpi_pci_link_add,
+ .detach = acpi_pci_link_remove,
+};
+
+/*
+ * If a link is initialized, we never change its active and initialized
+ * later even the link is disable. Instead, we just repick the active irq
+ */
+struct acpi_pci_link_irq {
+ u32 active; /* Current IRQ */
+ u8 triggering; /* All IRQs */
+ u8 polarity; /* All IRQs */
+ u8 resource_type;
+ u8 possible_count;
+ u32 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
+ u8 initialized:1;
+ u8 reserved:7;
+};
+
+struct acpi_pci_link {
+ struct list_head list;
+ struct acpi_device *device;
+ struct acpi_pci_link_irq irq;
+ int refcnt;
+};
+
+static LIST_HEAD(acpi_link_list);
+static DEFINE_MUTEX(acpi_link_lock);
+static int sci_irq = -1, sci_penalty;
+
+/* --------------------------------------------------------------------------
+ PCI Link Device Management
+ -------------------------------------------------------------------------- */
+
+/*
+ * set context (link) possible list from resource list
+ */
+static acpi_status acpi_pci_link_check_possible(struct acpi_resource *resource,
+ void *context)
+{
+ struct acpi_pci_link *link = context;
+ acpi_handle handle = link->device->handle;
+ u32 i;
+
+ switch (resource->type) {
+ case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ return AE_OK;
+ case ACPI_RESOURCE_TYPE_IRQ:
+ {
+ struct acpi_resource_irq *p = &resource->data.irq;
+ if (!p->interrupt_count) {
+ acpi_handle_debug(handle,
+ "Blank _PRS IRQ resource\n");
+ return AE_OK;
+ }
+ for (i = 0;
+ (i < p->interrupt_count
+ && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) {
+ if (!p->interrupts[i]) {
+ acpi_handle_debug(handle,
+ "Invalid _PRS IRQ %d\n",
+ p->interrupts[i]);
+ continue;
+ }
+ link->irq.possible[i] = p->interrupts[i];
+ link->irq.possible_count++;
+ }
+ link->irq.triggering = p->triggering;
+ link->irq.polarity = p->polarity;
+ link->irq.resource_type = ACPI_RESOURCE_TYPE_IRQ;
+ break;
+ }
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ {
+ struct acpi_resource_extended_irq *p =
+ &resource->data.extended_irq;
+ if (!p->interrupt_count) {
+ acpi_handle_debug(handle,
+ "Blank _PRS EXT IRQ resource\n");
+ return AE_OK;
+ }
+ for (i = 0;
+ (i < p->interrupt_count
+ && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) {
+ if (!p->interrupts[i]) {
+ acpi_handle_debug(handle,
+ "Invalid _PRS IRQ %d\n",
+ p->interrupts[i]);
+ continue;
+ }
+ link->irq.possible[i] = p->interrupts[i];
+ link->irq.possible_count++;
+ }
+ link->irq.triggering = p->triggering;
+ link->irq.polarity = p->polarity;
+ link->irq.resource_type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ;
+ break;
+ }
+ default:
+ acpi_handle_debug(handle, "_PRS resource type 0x%x is not IRQ\n",
+ resource->type);
+ return AE_OK;
+ }
+
+ return AE_CTRL_TERMINATE;
+}
+
+static int acpi_pci_link_get_possible(struct acpi_pci_link *link)
+{
+ acpi_handle handle = link->device->handle;
+ acpi_status status;
+
+ status = acpi_walk_resources(handle, METHOD_NAME__PRS,
+ acpi_pci_link_check_possible, link);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_debug(handle, "_PRS not present or invalid");
+ return 0;
+ }
+
+ acpi_handle_debug(handle, "Found %d possible IRQs\n",
+ link->irq.possible_count);
+
+ return 0;
+}
+
+static acpi_status acpi_pci_link_check_current(struct acpi_resource *resource,
+ void *context)
+{
+ int *irq = context;
+
+ switch (resource->type) {
+ case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ return AE_OK;
+ case ACPI_RESOURCE_TYPE_IRQ:
+ {
+ struct acpi_resource_irq *p = &resource->data.irq;
+ if (!p->interrupt_count) {
+ /*
+ * IRQ descriptors may have no IRQ# bits set,
+ * particularly those w/ _STA disabled
+ */
+ pr_debug("Blank _CRS IRQ resource\n");
+ return AE_OK;
+ }
+ *irq = p->interrupts[0];
+ break;
+ }
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ {
+ struct acpi_resource_extended_irq *p =
+ &resource->data.extended_irq;
+ if (!p->interrupt_count) {
+ /*
+ * extended IRQ descriptors must
+ * return at least 1 IRQ
+ */
+ pr_debug("Blank _CRS EXT IRQ resource\n");
+ return AE_OK;
+ }
+ *irq = p->interrupts[0];
+ break;
+ }
+ break;
+ default:
+ pr_debug("_CRS resource type 0x%x is not IRQ\n",
+ resource->type);
+ return AE_OK;
+ }
+
+ return AE_CTRL_TERMINATE;
+}
+
+/*
+ * Run _CRS and set link->irq.active
+ *
+ * return value:
+ * 0 - success
+ * !0 - failure
+ */
+static int acpi_pci_link_get_current(struct acpi_pci_link *link)
+{
+ acpi_handle handle = link->device->handle;
+ acpi_status status;
+ int result = 0;
+ int irq = 0;
+
+ link->irq.active = 0;
+
+ /* in practice, status disabled is meaningless, ignore it */
+ if (acpi_strict) {
+ /* Query _STA, set link->device->status */
+ result = acpi_bus_get_status(link->device);
+ if (result) {
+ acpi_handle_err(handle, "Unable to read status\n");
+ goto end;
+ }
+
+ if (!link->device->status.enabled) {
+ acpi_handle_debug(handle, "Link disabled\n");
+ return 0;
+ }
+ }
+
+ /*
+ * Query and parse _CRS to get the current IRQ assignment.
+ */
+
+ status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+ acpi_pci_link_check_current, &irq);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(handle, "_CRS", status);
+ result = -ENODEV;
+ goto end;
+ }
+
+ if (acpi_strict && !irq) {
+ acpi_handle_err(handle, "_CRS returned 0\n");
+ result = -ENODEV;
+ }
+
+ link->irq.active = irq;
+
+ acpi_handle_debug(handle, "Link at IRQ %d \n", link->irq.active);
+
+ end:
+ return result;
+}
+
+static int acpi_pci_link_set(struct acpi_pci_link *link, int irq)
+{
+ struct {
+ struct acpi_resource res;
+ struct acpi_resource end;
+ } *resource;
+ struct acpi_buffer buffer = { 0, NULL };
+ acpi_handle handle = link->device->handle;
+ acpi_status status;
+ int result;
+
+ if (!irq)
+ return -EINVAL;
+
+ resource = kzalloc(sizeof(*resource) + 1, irqs_disabled() ? GFP_ATOMIC: GFP_KERNEL);
+ if (!resource)
+ return -ENOMEM;
+
+ buffer.length = sizeof(*resource) + 1;
+ buffer.pointer = resource;
+
+ switch (link->irq.resource_type) {
+ case ACPI_RESOURCE_TYPE_IRQ:
+ resource->res.type = ACPI_RESOURCE_TYPE_IRQ;
+ resource->res.length = sizeof(struct acpi_resource);
+ resource->res.data.irq.triggering = link->irq.triggering;
+ resource->res.data.irq.polarity =
+ link->irq.polarity;
+ if (link->irq.triggering == ACPI_EDGE_SENSITIVE)
+ resource->res.data.irq.shareable =
+ ACPI_EXCLUSIVE;
+ else
+ resource->res.data.irq.shareable = ACPI_SHARED;
+ resource->res.data.irq.interrupt_count = 1;
+ resource->res.data.irq.interrupts[0] = irq;
+ break;
+
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ resource->res.type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ;
+ resource->res.length = sizeof(struct acpi_resource);
+ resource->res.data.extended_irq.producer_consumer =
+ ACPI_CONSUMER;
+ resource->res.data.extended_irq.triggering =
+ link->irq.triggering;
+ resource->res.data.extended_irq.polarity =
+ link->irq.polarity;
+ if (link->irq.triggering == ACPI_EDGE_SENSITIVE)
+ resource->res.data.extended_irq.shareable =
+ ACPI_EXCLUSIVE;
+ else
+ resource->res.data.extended_irq.shareable = ACPI_SHARED;
+ resource->res.data.extended_irq.interrupt_count = 1;
+ resource->res.data.extended_irq.interrupts[0] = irq;
+ /* ignore resource_source, it's optional */
+ break;
+ default:
+ acpi_handle_err(handle, "Invalid resource type %d\n",
+ link->irq.resource_type);
+ result = -EINVAL;
+ goto end;
+
+ }
+ resource->end.type = ACPI_RESOURCE_TYPE_END_TAG;
+ resource->end.length = sizeof(struct acpi_resource);
+
+ /* Attempt to set the resource */
+ status = acpi_set_current_resources(link->device->handle, &buffer);
+
+ /* check for total failure */
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(handle, "_SRS", status);
+ result = -ENODEV;
+ goto end;
+ }
+
+ /* Query _STA, set device->status */
+ result = acpi_bus_get_status(link->device);
+ if (result) {
+ acpi_handle_err(handle, "Unable to read status\n");
+ goto end;
+ }
+ if (!link->device->status.enabled)
+ acpi_handle_warn(handle, "Disabled and referenced, BIOS bug\n");
+
+ /* Query _CRS, set link->irq.active */
+ result = acpi_pci_link_get_current(link);
+ if (result) {
+ goto end;
+ }
+
+ /*
+ * Is current setting not what we set?
+ * set link->irq.active
+ */
+ if (link->irq.active != irq) {
+ /*
+ * policy: when _CRS doesn't return what we just _SRS
+ * assume _SRS worked and override _CRS value.
+ */
+ acpi_handle_warn(handle, "BIOS reported IRQ %d, using IRQ %d\n",
+ link->irq.active, irq);
+ link->irq.active = irq;
+ }
+
+ acpi_handle_debug(handle, "Set IRQ %d\n", link->irq.active);
+
+ end:
+ kfree(resource);
+ return result;
+}
+
+/* --------------------------------------------------------------------------
+ PCI Link IRQ Management
+ -------------------------------------------------------------------------- */
+
+/*
+ * "acpi_irq_balance" (default in APIC mode) enables ACPI to use PIC Interrupt
+ * Link Devices to move the PIRQs around to minimize sharing.
+ *
+ * "acpi_irq_nobalance" (default in PIC mode) tells ACPI not to move any PIC IRQs
+ * that the BIOS has already set to active. This is necessary because
+ * ACPI has no automatic means of knowing what ISA IRQs are used. Note that
+ * if the BIOS doesn't set a Link Device active, ACPI needs to program it
+ * even if acpi_irq_nobalance is set.
+ *
+ * A tables of penalties avoids directing PCI interrupts to well known
+ * ISA IRQs. Boot params are available to over-ride the default table:
+ *
+ * List interrupts that are free for PCI use.
+ * acpi_irq_pci=n[,m]
+ *
+ * List interrupts that should not be used for PCI:
+ * acpi_irq_isa=n[,m]
+ *
+ * Note that PCI IRQ routers have a list of possible IRQs,
+ * which may not include the IRQs this table says are available.
+ *
+ * Since this heuristic can't tell the difference between a link
+ * that no device will attach to, vs. a link which may be shared
+ * by multiple active devices -- it is not optimal.
+ *
+ * If interrupt performance is that important, get an IO-APIC system
+ * with a pin dedicated to each device. Or for that matter, an MSI
+ * enabled system.
+ */
+
+#define ACPI_MAX_ISA_IRQS 16
+
+#define PIRQ_PENALTY_PCI_POSSIBLE (16*16)
+#define PIRQ_PENALTY_PCI_USING (16*16*16)
+#define PIRQ_PENALTY_ISA_TYPICAL (16*16*16*16)
+#define PIRQ_PENALTY_ISA_USED (16*16*16*16*16)
+#define PIRQ_PENALTY_ISA_ALWAYS (16*16*16*16*16*16)
+
+static int acpi_isa_irq_penalty[ACPI_MAX_ISA_IRQS] = {
+ PIRQ_PENALTY_ISA_ALWAYS, /* IRQ0 timer */
+ PIRQ_PENALTY_ISA_ALWAYS, /* IRQ1 keyboard */
+ PIRQ_PENALTY_ISA_ALWAYS, /* IRQ2 cascade */
+ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ3 serial */
+ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ4 serial */
+ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ5 sometimes SoundBlaster */
+ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ6 */
+ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ7 parallel, spurious */
+ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ8 rtc, sometimes */
+ 0, /* IRQ9 PCI, often acpi */
+ 0, /* IRQ10 PCI */
+ 0, /* IRQ11 PCI */
+ PIRQ_PENALTY_ISA_USED, /* IRQ12 mouse */
+ PIRQ_PENALTY_ISA_USED, /* IRQ13 fpe, sometimes */
+ PIRQ_PENALTY_ISA_USED, /* IRQ14 ide0 */
+ PIRQ_PENALTY_ISA_USED, /* IRQ15 ide1 */
+ /* >IRQ15 */
+};
+
+static int acpi_irq_pci_sharing_penalty(int irq)
+{
+ struct acpi_pci_link *link;
+ int penalty = 0;
+ int i;
+
+ list_for_each_entry(link, &acpi_link_list, list) {
+ /*
+ * If a link is active, penalize its IRQ heavily
+ * so we try to choose a different IRQ.
+ */
+ if (link->irq.active && link->irq.active == irq)
+ penalty += PIRQ_PENALTY_PCI_USING;
+
+ /*
+ * penalize the IRQs PCI might use, but not as severely.
+ */
+ for (i = 0; i < link->irq.possible_count; i++)
+ if (link->irq.possible[i] == irq)
+ penalty += PIRQ_PENALTY_PCI_POSSIBLE /
+ link->irq.possible_count;
+ }
+
+ return penalty;
+}
+
+static int acpi_irq_get_penalty(int irq)
+{
+ int penalty = 0;
+
+ if (irq == sci_irq)
+ penalty += sci_penalty;
+
+ if (irq < ACPI_MAX_ISA_IRQS)
+ return penalty + acpi_isa_irq_penalty[irq];
+
+ return penalty + acpi_irq_pci_sharing_penalty(irq);
+}
+
+int __init acpi_irq_penalty_init(void)
+{
+ struct acpi_pci_link *link;
+ int i;
+
+ /*
+ * Update penalties to facilitate IRQ balancing.
+ */
+ list_for_each_entry(link, &acpi_link_list, list) {
+
+ /*
+ * reflect the possible and active irqs in the penalty table --
+ * useful for breaking ties.
+ */
+ if (link->irq.possible_count) {
+ int penalty =
+ PIRQ_PENALTY_PCI_POSSIBLE /
+ link->irq.possible_count;
+
+ for (i = 0; i < link->irq.possible_count; i++) {
+ if (link->irq.possible[i] < ACPI_MAX_ISA_IRQS)
+ acpi_isa_irq_penalty[link->irq.
+ possible[i]] +=
+ penalty;
+ }
+
+ } else if (link->irq.active &&
+ (link->irq.active < ACPI_MAX_ISA_IRQS)) {
+ acpi_isa_irq_penalty[link->irq.active] +=
+ PIRQ_PENALTY_PCI_POSSIBLE;
+ }
+ }
+
+ return 0;
+}
+
+static int acpi_irq_balance = -1; /* 0: static, 1: balance */
+
+static int acpi_pci_link_allocate(struct acpi_pci_link *link)
+{
+ acpi_handle handle = link->device->handle;
+ int irq;
+ int i;
+
+ if (link->irq.initialized) {
+ if (link->refcnt == 0)
+ /* This means the link is disabled but initialized */
+ acpi_pci_link_set(link, link->irq.active);
+ return 0;
+ }
+
+ /*
+ * search for active IRQ in list of possible IRQs.
+ */
+ for (i = 0; i < link->irq.possible_count; ++i) {
+ if (link->irq.active == link->irq.possible[i])
+ break;
+ }
+ /*
+ * forget active IRQ that is not in possible list
+ */
+ if (i == link->irq.possible_count) {
+ if (acpi_strict)
+ acpi_handle_warn(handle, "_CRS %d not found in _PRS\n",
+ link->irq.active);
+ link->irq.active = 0;
+ }
+
+ /*
+ * if active found, use it; else pick entry from end of possible list.
+ */
+ if (link->irq.active)
+ irq = link->irq.active;
+ else
+ irq = link->irq.possible[link->irq.possible_count - 1];
+
+ if (acpi_irq_balance || !link->irq.active) {
+ /*
+ * Select the best IRQ. This is done in reverse to promote
+ * the use of IRQs 9, 10, 11, and >15.
+ */
+ for (i = (link->irq.possible_count - 1); i >= 0; i--) {
+ if (acpi_irq_get_penalty(irq) >
+ acpi_irq_get_penalty(link->irq.possible[i]))
+ irq = link->irq.possible[i];
+ }
+ }
+ if (acpi_irq_get_penalty(irq) >= PIRQ_PENALTY_ISA_ALWAYS) {
+ acpi_handle_err(handle,
+ "No IRQ available. Try pci=noacpi or acpi=off\n");
+ return -ENODEV;
+ }
+
+ /* Attempt to enable the link device at this IRQ. */
+ if (acpi_pci_link_set(link, irq)) {
+ acpi_handle_err(handle,
+ "Unable to set IRQ. Try pci=noacpi or acpi=off\n");
+ return -ENODEV;
+ } else {
+ if (link->irq.active < ACPI_MAX_ISA_IRQS)
+ acpi_isa_irq_penalty[link->irq.active] +=
+ PIRQ_PENALTY_PCI_USING;
+
+ acpi_handle_info(handle, "Enabled at IRQ %d\n",
+ link->irq.active);
+ }
+
+ link->irq.initialized = 1;
+ return 0;
+}
+
+/*
+ * acpi_pci_link_allocate_irq
+ * success: return IRQ >= 0
+ * failure: return -1
+ */
+int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering,
+ int *polarity, char **name)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+ struct acpi_pci_link *link;
+
+ if (!device) {
+ acpi_handle_err(handle, "Invalid link device\n");
+ return -1;
+ }
+
+ link = acpi_driver_data(device);
+ if (!link) {
+ acpi_handle_err(handle, "Invalid link context\n");
+ return -1;
+ }
+
+ /* TBD: Support multiple index (IRQ) entries per Link Device */
+ if (index) {
+ acpi_handle_err(handle, "Invalid index %d\n", index);
+ return -1;
+ }
+
+ mutex_lock(&acpi_link_lock);
+ if (acpi_pci_link_allocate(link)) {
+ mutex_unlock(&acpi_link_lock);
+ return -1;
+ }
+
+ if (!link->irq.active) {
+ mutex_unlock(&acpi_link_lock);
+ acpi_handle_err(handle, "Link active IRQ is 0!\n");
+ return -1;
+ }
+ link->refcnt++;
+ mutex_unlock(&acpi_link_lock);
+
+ if (triggering)
+ *triggering = link->irq.triggering;
+ if (polarity)
+ *polarity = link->irq.polarity;
+ if (name)
+ *name = acpi_device_bid(link->device);
+ acpi_handle_debug(handle, "Link is referenced\n");
+ return link->irq.active;
+}
+
+/*
+ * We don't change link's irq information here. After it is reenabled, we
+ * continue use the info
+ */
+int acpi_pci_link_free_irq(acpi_handle handle)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+ struct acpi_pci_link *link;
+
+ if (!device) {
+ acpi_handle_err(handle, "Invalid link device\n");
+ return -1;
+ }
+
+ link = acpi_driver_data(device);
+ if (!link) {
+ acpi_handle_err(handle, "Invalid link context\n");
+ return -1;
+ }
+
+ mutex_lock(&acpi_link_lock);
+ if (!link->irq.initialized) {
+ mutex_unlock(&acpi_link_lock);
+ acpi_handle_err(handle, "Link isn't initialized\n");
+ return -1;
+ }
+#ifdef FUTURE_USE
+ /*
+ * The Link reference count allows us to _DISable an unused link
+ * and suspend time, and set it again on resume.
+ * However, 2.6.12 still has irq_router.resume
+ * which blindly restores the link state.
+ * So we disable the reference count method
+ * to prevent duplicate acpi_pci_link_set()
+ * which would harm some systems
+ */
+ link->refcnt--;
+#endif
+ acpi_handle_debug(handle, "Link is dereferenced\n");
+
+ if (link->refcnt == 0)
+ acpi_evaluate_object(link->device->handle, "_DIS", NULL, NULL);
+
+ mutex_unlock(&acpi_link_lock);
+ return link->irq.active;
+}
+
+/* --------------------------------------------------------------------------
+ Driver Interface
+ -------------------------------------------------------------------------- */
+
+static int acpi_pci_link_add(struct acpi_device *device,
+ const struct acpi_device_id *not_used)
+{
+ acpi_handle handle = device->handle;
+ struct acpi_pci_link *link;
+ int result;
+ int i;
+
+ link = kzalloc(sizeof(struct acpi_pci_link), GFP_KERNEL);
+ if (!link)
+ return -ENOMEM;
+
+ link->device = device;
+ strcpy(acpi_device_name(device), ACPI_PCI_LINK_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS);
+ device->driver_data = link;
+
+ mutex_lock(&acpi_link_lock);
+ result = acpi_pci_link_get_possible(link);
+ if (result)
+ goto end;
+
+ /* query and set link->irq.active */
+ acpi_pci_link_get_current(link);
+
+ pr_info("Interrupt link %s configured for IRQ %d\n",
+ acpi_device_bid(device), link->irq.active);
+
+ for (i = 0; i < link->irq.possible_count; i++) {
+ if (link->irq.active != link->irq.possible[i])
+ acpi_handle_debug(handle, "Possible IRQ %d\n",
+ link->irq.possible[i]);
+ }
+
+ if (!link->device->status.enabled)
+ pr_info("Interrupt link %s disabled\n", acpi_device_bid(device));
+
+ list_add_tail(&link->list, &acpi_link_list);
+
+ end:
+ /* disable all links -- to be activated on use */
+ acpi_evaluate_object(handle, "_DIS", NULL, NULL);
+ mutex_unlock(&acpi_link_lock);
+
+ if (result)
+ kfree(link);
+
+ return result < 0 ? result : 1;
+}
+
+static int acpi_pci_link_resume(struct acpi_pci_link *link)
+{
+ if (link->refcnt && link->irq.active && link->irq.initialized)
+ return (acpi_pci_link_set(link, link->irq.active));
+
+ return 0;
+}
+
+static void irqrouter_resume(void)
+{
+ struct acpi_pci_link *link;
+
+ list_for_each_entry(link, &acpi_link_list, list) {
+ acpi_pci_link_resume(link);
+ }
+}
+
+static void acpi_pci_link_remove(struct acpi_device *device)
+{
+ struct acpi_pci_link *link;
+
+ link = acpi_driver_data(device);
+
+ mutex_lock(&acpi_link_lock);
+ list_del(&link->list);
+ mutex_unlock(&acpi_link_lock);
+
+ kfree(link);
+}
+
+/*
+ * modify acpi_isa_irq_penalty[] from cmdline
+ */
+static int __init acpi_irq_penalty_update(char *str, int used)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ int retval;
+ int irq;
+ int new_penalty;
+
+ retval = get_option(&str, &irq);
+
+ if (!retval)
+ break; /* no number found */
+
+ /* see if this is a ISA IRQ */
+ if ((irq < 0) || (irq >= ACPI_MAX_ISA_IRQS))
+ continue;
+
+ if (used)
+ new_penalty = acpi_isa_irq_penalty[irq] +
+ PIRQ_PENALTY_ISA_USED;
+ else
+ new_penalty = 0;
+
+ acpi_isa_irq_penalty[irq] = new_penalty;
+ if (retval != 2) /* no next number */
+ break;
+ }
+ return 1;
+}
+
+/*
+ * We'd like PNP to call this routine for the
+ * single ISA_USED value for each legacy device.
+ * But instead it calls us with each POSSIBLE setting.
+ * There is no ISA_POSSIBLE weight, so we simply use
+ * the (small) PCI_USING penalty.
+ */
+void acpi_penalize_isa_irq(int irq, int active)
+{
+ if ((irq >= 0) && (irq < ARRAY_SIZE(acpi_isa_irq_penalty)))
+ acpi_isa_irq_penalty[irq] +=
+ (active ? PIRQ_PENALTY_ISA_USED : PIRQ_PENALTY_PCI_USING);
+}
+
+bool acpi_isa_irq_available(int irq)
+{
+ return irq >= 0 && (irq >= ARRAY_SIZE(acpi_isa_irq_penalty) ||
+ acpi_irq_get_penalty(irq) < PIRQ_PENALTY_ISA_ALWAYS);
+}
+
+void acpi_penalize_sci_irq(int irq, int trigger, int polarity)
+{
+ sci_irq = irq;
+
+ if (trigger == ACPI_MADT_TRIGGER_LEVEL &&
+ polarity == ACPI_MADT_POLARITY_ACTIVE_LOW)
+ sci_penalty = PIRQ_PENALTY_PCI_USING;
+ else
+ sci_penalty = PIRQ_PENALTY_ISA_ALWAYS;
+}
+
+/*
+ * Over-ride default table to reserve additional IRQs for use by ISA
+ * e.g. acpi_irq_isa=5
+ * Useful for telling ACPI how not to interfere with your ISA sound card.
+ */
+static int __init acpi_irq_isa(char *str)
+{
+ return acpi_irq_penalty_update(str, 1);
+}
+
+__setup("acpi_irq_isa=", acpi_irq_isa);
+
+/*
+ * Over-ride default table to free additional IRQs for use by PCI
+ * e.g. acpi_irq_pci=7,15
+ * Used for acpi_irq_balance to free up IRQs to reduce PCI IRQ sharing.
+ */
+static int __init acpi_irq_pci(char *str)
+{
+ return acpi_irq_penalty_update(str, 0);
+}
+
+__setup("acpi_irq_pci=", acpi_irq_pci);
+
+static int __init acpi_irq_nobalance_set(char *str)
+{
+ acpi_irq_balance = 0;
+ return 1;
+}
+
+__setup("acpi_irq_nobalance", acpi_irq_nobalance_set);
+
+static int __init acpi_irq_balance_set(char *str)
+{
+ acpi_irq_balance = 1;
+ return 1;
+}
+
+__setup("acpi_irq_balance", acpi_irq_balance_set);
+
+static struct syscore_ops irqrouter_syscore_ops = {
+ .resume = irqrouter_resume,
+};
+
+void __init acpi_pci_link_init(void)
+{
+ if (acpi_noirq)
+ return;
+
+ if (acpi_irq_balance == -1) {
+ /* no command line switch: enable balancing in IOAPIC mode */
+ if (acpi_irq_model == ACPI_IRQ_MODEL_IOAPIC)
+ acpi_irq_balance = 1;
+ else
+ acpi_irq_balance = 0;
+ }
+ register_syscore_ops(&irqrouter_syscore_ops);
+ acpi_scan_add_handler(&pci_link_handler);
+}
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
new file mode 100644
index 000000000..860014b89
--- /dev/null
+++ b/drivers/acpi/pci_mcfg.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 Broadcom
+ * Author: Jayachandran C <jchandra@broadcom.com>
+ * Copyright (C) 2016 Semihalf
+ * Author: Tomasz Nowicki <tn@semihalf.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/pci-acpi.h>
+#include <linux/pci-ecam.h>
+
+/* Structure to hold entries from the MCFG table */
+struct mcfg_entry {
+ struct list_head list;
+ phys_addr_t addr;
+ u16 segment;
+ u8 bus_start;
+ u8 bus_end;
+};
+
+#ifdef CONFIG_PCI_QUIRKS
+struct mcfg_fixup {
+ char oem_id[ACPI_OEM_ID_SIZE + 1];
+ char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1];
+ u32 oem_revision;
+ u16 segment;
+ struct resource bus_range;
+ const struct pci_ecam_ops *ops;
+ struct resource cfgres;
+};
+
+#define MCFG_BUS_RANGE(start, end) DEFINE_RES_NAMED((start), \
+ ((end) - (start) + 1), \
+ NULL, IORESOURCE_BUS)
+#define MCFG_BUS_ANY MCFG_BUS_RANGE(0x0, 0xff)
+
+static struct mcfg_fixup mcfg_quirks[] = {
+/* { OEM_ID, OEM_TABLE_ID, REV, SEGMENT, BUS_RANGE, ops, cfgres }, */
+
+#ifdef CONFIG_ARM64
+
+#define AL_ECAM(table_id, rev, seg, ops) \
+ { "AMAZON", table_id, rev, seg, MCFG_BUS_ANY, ops }
+
+ AL_ECAM("GRAVITON", 0, 0, &al_pcie_ops),
+ AL_ECAM("GRAVITON", 0, 1, &al_pcie_ops),
+ AL_ECAM("GRAVITON", 0, 2, &al_pcie_ops),
+ AL_ECAM("GRAVITON", 0, 3, &al_pcie_ops),
+ AL_ECAM("GRAVITON", 0, 4, &al_pcie_ops),
+ AL_ECAM("GRAVITON", 0, 5, &al_pcie_ops),
+ AL_ECAM("GRAVITON", 0, 6, &al_pcie_ops),
+ AL_ECAM("GRAVITON", 0, 7, &al_pcie_ops),
+
+#define QCOM_ECAM32(seg) \
+ { "QCOM ", "QDF2432 ", 1, seg, MCFG_BUS_ANY, &pci_32b_ops }
+
+ QCOM_ECAM32(0),
+ QCOM_ECAM32(1),
+ QCOM_ECAM32(2),
+ QCOM_ECAM32(3),
+ QCOM_ECAM32(4),
+ QCOM_ECAM32(5),
+ QCOM_ECAM32(6),
+ QCOM_ECAM32(7),
+
+#define HISI_QUAD_DOM(table_id, seg, ops) \
+ { "HISI ", table_id, 0, (seg) + 0, MCFG_BUS_ANY, ops }, \
+ { "HISI ", table_id, 0, (seg) + 1, MCFG_BUS_ANY, ops }, \
+ { "HISI ", table_id, 0, (seg) + 2, MCFG_BUS_ANY, ops }, \
+ { "HISI ", table_id, 0, (seg) + 3, MCFG_BUS_ANY, ops }
+
+ HISI_QUAD_DOM("HIP05 ", 0, &hisi_pcie_ops),
+ HISI_QUAD_DOM("HIP06 ", 0, &hisi_pcie_ops),
+ HISI_QUAD_DOM("HIP07 ", 0, &hisi_pcie_ops),
+ HISI_QUAD_DOM("HIP07 ", 4, &hisi_pcie_ops),
+ HISI_QUAD_DOM("HIP07 ", 8, &hisi_pcie_ops),
+ HISI_QUAD_DOM("HIP07 ", 12, &hisi_pcie_ops),
+
+#define THUNDER_PEM_RES(addr, node) \
+ DEFINE_RES_MEM((addr) + ((u64) (node) << 44), 0x39 * SZ_16M)
+
+#define THUNDER_PEM_QUIRK(rev, node) \
+ { "CAVIUM", "THUNDERX", rev, 4 + (10 * (node)), MCFG_BUS_ANY, \
+ &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x88001f000000UL, node) }, \
+ { "CAVIUM", "THUNDERX", rev, 5 + (10 * (node)), MCFG_BUS_ANY, \
+ &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x884057000000UL, node) }, \
+ { "CAVIUM", "THUNDERX", rev, 6 + (10 * (node)), MCFG_BUS_ANY, \
+ &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x88808f000000UL, node) }, \
+ { "CAVIUM", "THUNDERX", rev, 7 + (10 * (node)), MCFG_BUS_ANY, \
+ &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x89001f000000UL, node) }, \
+ { "CAVIUM", "THUNDERX", rev, 8 + (10 * (node)), MCFG_BUS_ANY, \
+ &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x894057000000UL, node) }, \
+ { "CAVIUM", "THUNDERX", rev, 9 + (10 * (node)), MCFG_BUS_ANY, \
+ &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x89808f000000UL, node) }
+
+#define THUNDER_ECAM_QUIRK(rev, seg) \
+ { "CAVIUM", "THUNDERX", rev, seg, MCFG_BUS_ANY, \
+ &pci_thunder_ecam_ops }
+
+ /* SoC pass2.x */
+ THUNDER_PEM_QUIRK(1, 0),
+ THUNDER_PEM_QUIRK(1, 1),
+ THUNDER_ECAM_QUIRK(1, 10),
+
+ /* SoC pass1.x */
+ THUNDER_PEM_QUIRK(2, 0), /* off-chip devices */
+ THUNDER_PEM_QUIRK(2, 1), /* off-chip devices */
+ THUNDER_ECAM_QUIRK(2, 0),
+ THUNDER_ECAM_QUIRK(2, 1),
+ THUNDER_ECAM_QUIRK(2, 2),
+ THUNDER_ECAM_QUIRK(2, 3),
+ THUNDER_ECAM_QUIRK(2, 10),
+ THUNDER_ECAM_QUIRK(2, 11),
+ THUNDER_ECAM_QUIRK(2, 12),
+ THUNDER_ECAM_QUIRK(2, 13),
+
+ { "NVIDIA", "TEGRA194", 1, 0, MCFG_BUS_ANY, &tegra194_pcie_ops},
+ { "NVIDIA", "TEGRA194", 1, 1, MCFG_BUS_ANY, &tegra194_pcie_ops},
+ { "NVIDIA", "TEGRA194", 1, 2, MCFG_BUS_ANY, &tegra194_pcie_ops},
+ { "NVIDIA", "TEGRA194", 1, 3, MCFG_BUS_ANY, &tegra194_pcie_ops},
+ { "NVIDIA", "TEGRA194", 1, 4, MCFG_BUS_ANY, &tegra194_pcie_ops},
+ { "NVIDIA", "TEGRA194", 1, 5, MCFG_BUS_ANY, &tegra194_pcie_ops},
+
+#define XGENE_V1_ECAM_MCFG(rev, seg) \
+ {"APM ", "XGENE ", rev, seg, MCFG_BUS_ANY, \
+ &xgene_v1_pcie_ecam_ops }
+
+#define XGENE_V2_ECAM_MCFG(rev, seg) \
+ {"APM ", "XGENE ", rev, seg, MCFG_BUS_ANY, \
+ &xgene_v2_pcie_ecam_ops }
+
+ /* X-Gene SoC with v1 PCIe controller */
+ XGENE_V1_ECAM_MCFG(1, 0),
+ XGENE_V1_ECAM_MCFG(1, 1),
+ XGENE_V1_ECAM_MCFG(1, 2),
+ XGENE_V1_ECAM_MCFG(1, 3),
+ XGENE_V1_ECAM_MCFG(1, 4),
+ XGENE_V1_ECAM_MCFG(2, 0),
+ XGENE_V1_ECAM_MCFG(2, 1),
+ XGENE_V1_ECAM_MCFG(2, 2),
+ XGENE_V1_ECAM_MCFG(2, 3),
+ XGENE_V1_ECAM_MCFG(2, 4),
+ /* X-Gene SoC with v2.1 PCIe controller */
+ XGENE_V2_ECAM_MCFG(3, 0),
+ XGENE_V2_ECAM_MCFG(3, 1),
+ /* X-Gene SoC with v2.2 PCIe controller */
+ XGENE_V2_ECAM_MCFG(4, 0),
+ XGENE_V2_ECAM_MCFG(4, 1),
+ XGENE_V2_ECAM_MCFG(4, 2),
+
+#define ALTRA_ECAM_QUIRK(rev, seg) \
+ { "Ampere", "Altra ", rev, seg, MCFG_BUS_ANY, &pci_32b_read_ops }
+
+ ALTRA_ECAM_QUIRK(1, 0),
+ ALTRA_ECAM_QUIRK(1, 1),
+ ALTRA_ECAM_QUIRK(1, 2),
+ ALTRA_ECAM_QUIRK(1, 3),
+ ALTRA_ECAM_QUIRK(1, 4),
+ ALTRA_ECAM_QUIRK(1, 5),
+ ALTRA_ECAM_QUIRK(1, 6),
+ ALTRA_ECAM_QUIRK(1, 7),
+ ALTRA_ECAM_QUIRK(1, 8),
+ ALTRA_ECAM_QUIRK(1, 9),
+ ALTRA_ECAM_QUIRK(1, 10),
+ ALTRA_ECAM_QUIRK(1, 11),
+ ALTRA_ECAM_QUIRK(1, 12),
+ ALTRA_ECAM_QUIRK(1, 13),
+ ALTRA_ECAM_QUIRK(1, 14),
+ ALTRA_ECAM_QUIRK(1, 15),
+#endif /* ARM64 */
+
+#ifdef CONFIG_LOONGARCH
+#define LOONGSON_ECAM_MCFG(table_id, seg) \
+ { "LOONGS", table_id, 1, seg, MCFG_BUS_ANY, &loongson_pci_ecam_ops }
+
+ LOONGSON_ECAM_MCFG("\0", 0),
+ LOONGSON_ECAM_MCFG("LOONGSON", 0),
+ LOONGSON_ECAM_MCFG("\0", 1),
+ LOONGSON_ECAM_MCFG("LOONGSON", 1),
+#endif /* LOONGARCH */
+};
+
+static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
+static char mcfg_oem_table_id[ACPI_OEM_TABLE_ID_SIZE];
+static u32 mcfg_oem_revision;
+
+static int pci_mcfg_quirk_matches(struct mcfg_fixup *f, u16 segment,
+ struct resource *bus_range)
+{
+ if (!memcmp(f->oem_id, mcfg_oem_id, ACPI_OEM_ID_SIZE) &&
+ !memcmp(f->oem_table_id, mcfg_oem_table_id,
+ ACPI_OEM_TABLE_ID_SIZE) &&
+ f->oem_revision == mcfg_oem_revision &&
+ f->segment == segment &&
+ resource_contains(&f->bus_range, bus_range))
+ return 1;
+
+ return 0;
+}
+#endif
+
+static void pci_mcfg_apply_quirks(struct acpi_pci_root *root,
+ struct resource *cfgres,
+ const struct pci_ecam_ops **ecam_ops)
+{
+#ifdef CONFIG_PCI_QUIRKS
+ u16 segment = root->segment;
+ struct resource *bus_range = &root->secondary;
+ struct mcfg_fixup *f;
+ int i;
+
+ for (i = 0, f = mcfg_quirks; i < ARRAY_SIZE(mcfg_quirks); i++, f++) {
+ if (pci_mcfg_quirk_matches(f, segment, bus_range)) {
+ if (f->cfgres.start)
+ *cfgres = f->cfgres;
+ if (f->ops)
+ *ecam_ops = f->ops;
+ dev_info(&root->device->dev, "MCFG quirk: ECAM at %pR for %pR with %ps\n",
+ cfgres, bus_range, *ecam_ops);
+ return;
+ }
+ }
+#endif
+}
+
+/* List to save MCFG entries */
+static LIST_HEAD(pci_mcfg_list);
+
+int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres,
+ const struct pci_ecam_ops **ecam_ops)
+{
+ const struct pci_ecam_ops *ops = &pci_generic_ecam_ops;
+ struct resource *bus_res = &root->secondary;
+ u16 seg = root->segment;
+ struct mcfg_entry *e;
+ struct resource res;
+
+ /* Use address from _CBA if present, otherwise lookup MCFG */
+ if (root->mcfg_addr)
+ goto skip_lookup;
+
+ /*
+ * We expect the range in bus_res in the coverage of MCFG bus range.
+ */
+ list_for_each_entry(e, &pci_mcfg_list, list) {
+ if (e->segment == seg && e->bus_start <= bus_res->start &&
+ e->bus_end >= bus_res->end) {
+ root->mcfg_addr = e->addr;
+ }
+
+ }
+
+skip_lookup:
+ memset(&res, 0, sizeof(res));
+ if (root->mcfg_addr) {
+ res.start = root->mcfg_addr + (bus_res->start << 20);
+ res.end = res.start + (resource_size(bus_res) << 20) - 1;
+ res.flags = IORESOURCE_MEM;
+ }
+
+ /*
+ * Allow quirks to override default ECAM ops and CFG resource
+ * range. This may even fabricate a CFG resource range in case
+ * MCFG does not have it. Invalid CFG start address means MCFG
+ * firmware bug or we need another quirk in array.
+ */
+ pci_mcfg_apply_quirks(root, &res, &ops);
+ if (!res.start)
+ return -ENXIO;
+
+ *cfgres = res;
+ *ecam_ops = ops;
+ return 0;
+}
+
+static __init int pci_mcfg_parse(struct acpi_table_header *header)
+{
+ struct acpi_table_mcfg *mcfg;
+ struct acpi_mcfg_allocation *mptr;
+ struct mcfg_entry *e, *arr;
+ int i, n;
+
+ if (header->length < sizeof(struct acpi_table_mcfg))
+ return -EINVAL;
+
+ n = (header->length - sizeof(struct acpi_table_mcfg)) /
+ sizeof(struct acpi_mcfg_allocation);
+ mcfg = (struct acpi_table_mcfg *)header;
+ mptr = (struct acpi_mcfg_allocation *) &mcfg[1];
+
+ arr = kcalloc(n, sizeof(*arr), GFP_KERNEL);
+ if (!arr)
+ return -ENOMEM;
+
+ for (i = 0, e = arr; i < n; i++, mptr++, e++) {
+ e->segment = mptr->pci_segment;
+ e->addr = mptr->address;
+ e->bus_start = mptr->start_bus_number;
+ e->bus_end = mptr->end_bus_number;
+ list_add(&e->list, &pci_mcfg_list);
+ }
+
+#ifdef CONFIG_PCI_QUIRKS
+ /* Save MCFG IDs and revision for quirks matching */
+ memcpy(mcfg_oem_id, header->oem_id, ACPI_OEM_ID_SIZE);
+ memcpy(mcfg_oem_table_id, header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE);
+ mcfg_oem_revision = header->oem_revision;
+#endif
+
+ pr_info("MCFG table detected, %d entries\n", n);
+ return 0;
+}
+
+/* Interface called by ACPI - parse and save MCFG table */
+void __init pci_mmcfg_late_init(void)
+{
+ int err = acpi_table_parse(ACPI_SIG_MCFG, pci_mcfg_parse);
+ if (err)
+ pr_debug("Failed to parse MCFG (%d)\n", err);
+}
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
new file mode 100644
index 000000000..4e3db20e9
--- /dev/null
+++ b/drivers/acpi/pci_root.c
@@ -0,0 +1,1081 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pci_root.c - ACPI PCI Root Bridge Driver ($Revision: 40 $)
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/pci.h>
+#include <linux/pci-acpi.h>
+#include <linux/dmar.h>
+#include <linux/acpi.h>
+#include <linux/slab.h>
+#include <linux/dmi.h>
+#include <linux/platform_data/x86/apple.h>
+#include "internal.h"
+
+#define ACPI_PCI_ROOT_CLASS "pci_bridge"
+#define ACPI_PCI_ROOT_DEVICE_NAME "PCI Root Bridge"
+static int acpi_pci_root_add(struct acpi_device *device,
+ const struct acpi_device_id *not_used);
+static void acpi_pci_root_remove(struct acpi_device *device);
+
+static int acpi_pci_root_scan_dependent(struct acpi_device *adev)
+{
+ acpiphp_check_host_bridge(adev);
+ return 0;
+}
+
+#define ACPI_PCIE_REQ_SUPPORT (OSC_PCI_EXT_CONFIG_SUPPORT \
+ | OSC_PCI_ASPM_SUPPORT \
+ | OSC_PCI_CLOCK_PM_SUPPORT \
+ | OSC_PCI_MSI_SUPPORT)
+
+static const struct acpi_device_id root_device_ids[] = {
+ {"PNP0A03", 0},
+ {"", 0},
+};
+
+static struct acpi_scan_handler pci_root_handler = {
+ .ids = root_device_ids,
+ .attach = acpi_pci_root_add,
+ .detach = acpi_pci_root_remove,
+ .hotplug = {
+ .enabled = true,
+ .scan_dependent = acpi_pci_root_scan_dependent,
+ },
+};
+
+/**
+ * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge
+ * @handle: the ACPI CA node in question.
+ *
+ * Note: we could make this API take a struct acpi_device * instead, but
+ * for now, it's more convenient to operate on an acpi_handle.
+ */
+int acpi_is_root_bridge(acpi_handle handle)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+ int ret;
+
+ if (!device)
+ return 0;
+
+ ret = acpi_match_device_ids(device, root_device_ids);
+ if (ret)
+ return 0;
+ else
+ return 1;
+}
+EXPORT_SYMBOL_GPL(acpi_is_root_bridge);
+
+static acpi_status
+get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
+{
+ struct resource *res = data;
+ struct acpi_resource_address64 address;
+ acpi_status status;
+
+ status = acpi_resource_to_address64(resource, &address);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ if ((address.address.address_length > 0) &&
+ (address.resource_type == ACPI_BUS_NUMBER_RANGE)) {
+ res->start = address.address.minimum;
+ res->end = address.address.minimum + address.address.address_length - 1;
+ }
+
+ return AE_OK;
+}
+
+static acpi_status try_get_root_bridge_busnr(acpi_handle handle,
+ struct resource *res)
+{
+ acpi_status status;
+
+ res->start = -1;
+ status =
+ acpi_walk_resources(handle, METHOD_NAME__CRS,
+ get_root_bridge_busnr_callback, res);
+ if (ACPI_FAILURE(status))
+ return status;
+ if (res->start == -1)
+ return AE_ERROR;
+ return AE_OK;
+}
+
+struct pci_osc_bit_struct {
+ u32 bit;
+ char *desc;
+};
+
+static struct pci_osc_bit_struct pci_osc_support_bit[] = {
+ { OSC_PCI_EXT_CONFIG_SUPPORT, "ExtendedConfig" },
+ { OSC_PCI_ASPM_SUPPORT, "ASPM" },
+ { OSC_PCI_CLOCK_PM_SUPPORT, "ClockPM" },
+ { OSC_PCI_SEGMENT_GROUPS_SUPPORT, "Segments" },
+ { OSC_PCI_MSI_SUPPORT, "MSI" },
+ { OSC_PCI_EDR_SUPPORT, "EDR" },
+ { OSC_PCI_HPX_TYPE_3_SUPPORT, "HPX-Type3" },
+};
+
+static struct pci_osc_bit_struct pci_osc_control_bit[] = {
+ { OSC_PCI_EXPRESS_NATIVE_HP_CONTROL, "PCIeHotplug" },
+ { OSC_PCI_SHPC_NATIVE_HP_CONTROL, "SHPCHotplug" },
+ { OSC_PCI_EXPRESS_PME_CONTROL, "PME" },
+ { OSC_PCI_EXPRESS_AER_CONTROL, "AER" },
+ { OSC_PCI_EXPRESS_CAPABILITY_CONTROL, "PCIeCapability" },
+ { OSC_PCI_EXPRESS_LTR_CONTROL, "LTR" },
+ { OSC_PCI_EXPRESS_DPC_CONTROL, "DPC" },
+};
+
+static struct pci_osc_bit_struct cxl_osc_support_bit[] = {
+ { OSC_CXL_1_1_PORT_REG_ACCESS_SUPPORT, "CXL11PortRegAccess" },
+ { OSC_CXL_2_0_PORT_DEV_REG_ACCESS_SUPPORT, "CXL20PortDevRegAccess" },
+ { OSC_CXL_PROTOCOL_ERR_REPORTING_SUPPORT, "CXLProtocolErrorReporting" },
+ { OSC_CXL_NATIVE_HP_SUPPORT, "CXLNativeHotPlug" },
+};
+
+static struct pci_osc_bit_struct cxl_osc_control_bit[] = {
+ { OSC_CXL_ERROR_REPORTING_CONTROL, "CXLMemErrorReporting" },
+};
+
+static void decode_osc_bits(struct acpi_pci_root *root, char *msg, u32 word,
+ struct pci_osc_bit_struct *table, int size)
+{
+ char buf[80];
+ int i, len = 0;
+ struct pci_osc_bit_struct *entry;
+
+ buf[0] = '\0';
+ for (i = 0, entry = table; i < size; i++, entry++)
+ if (word & entry->bit)
+ len += scnprintf(buf + len, sizeof(buf) - len, "%s%s",
+ len ? " " : "", entry->desc);
+
+ dev_info(&root->device->dev, "_OSC: %s [%s]\n", msg, buf);
+}
+
+static void decode_osc_support(struct acpi_pci_root *root, char *msg, u32 word)
+{
+ decode_osc_bits(root, msg, word, pci_osc_support_bit,
+ ARRAY_SIZE(pci_osc_support_bit));
+}
+
+static void decode_osc_control(struct acpi_pci_root *root, char *msg, u32 word)
+{
+ decode_osc_bits(root, msg, word, pci_osc_control_bit,
+ ARRAY_SIZE(pci_osc_control_bit));
+}
+
+static void decode_cxl_osc_support(struct acpi_pci_root *root, char *msg, u32 word)
+{
+ decode_osc_bits(root, msg, word, cxl_osc_support_bit,
+ ARRAY_SIZE(cxl_osc_support_bit));
+}
+
+static void decode_cxl_osc_control(struct acpi_pci_root *root, char *msg, u32 word)
+{
+ decode_osc_bits(root, msg, word, cxl_osc_control_bit,
+ ARRAY_SIZE(cxl_osc_control_bit));
+}
+
+static inline bool is_pcie(struct acpi_pci_root *root)
+{
+ return root->bridge_type == ACPI_BRIDGE_TYPE_PCIE;
+}
+
+static inline bool is_cxl(struct acpi_pci_root *root)
+{
+ return root->bridge_type == ACPI_BRIDGE_TYPE_CXL;
+}
+
+static u8 pci_osc_uuid_str[] = "33DB4D5B-1FF7-401C-9657-7441C03DD766";
+static u8 cxl_osc_uuid_str[] = "68F2D50B-C469-4d8A-BD3D-941A103FD3FC";
+
+static char *to_uuid(struct acpi_pci_root *root)
+{
+ if (is_cxl(root))
+ return cxl_osc_uuid_str;
+ return pci_osc_uuid_str;
+}
+
+static int cap_length(struct acpi_pci_root *root)
+{
+ if (is_cxl(root))
+ return sizeof(u32) * OSC_CXL_CAPABILITY_DWORDS;
+ return sizeof(u32) * OSC_PCI_CAPABILITY_DWORDS;
+}
+
+static acpi_status acpi_pci_run_osc(struct acpi_pci_root *root,
+ const u32 *capbuf, u32 *pci_control,
+ u32 *cxl_control)
+{
+ struct acpi_osc_context context = {
+ .uuid_str = to_uuid(root),
+ .rev = 1,
+ .cap.length = cap_length(root),
+ .cap.pointer = (void *)capbuf,
+ };
+ acpi_status status;
+
+ status = acpi_run_osc(root->device->handle, &context);
+ if (ACPI_SUCCESS(status)) {
+ *pci_control = acpi_osc_ctx_get_pci_control(&context);
+ if (is_cxl(root))
+ *cxl_control = acpi_osc_ctx_get_cxl_control(&context);
+ kfree(context.ret.pointer);
+ }
+ return status;
+}
+
+static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 support,
+ u32 *control, u32 cxl_support,
+ u32 *cxl_control)
+{
+ acpi_status status;
+ u32 pci_result, cxl_result, capbuf[OSC_CXL_CAPABILITY_DWORDS];
+
+ support |= root->osc_support_set;
+
+ capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE;
+ capbuf[OSC_SUPPORT_DWORD] = support;
+ capbuf[OSC_CONTROL_DWORD] = *control | root->osc_control_set;
+
+ if (is_cxl(root)) {
+ cxl_support |= root->osc_ext_support_set;
+ capbuf[OSC_EXT_SUPPORT_DWORD] = cxl_support;
+ capbuf[OSC_EXT_CONTROL_DWORD] = *cxl_control | root->osc_ext_control_set;
+ }
+
+retry:
+ status = acpi_pci_run_osc(root, capbuf, &pci_result, &cxl_result);
+ if (ACPI_SUCCESS(status)) {
+ root->osc_support_set = support;
+ *control = pci_result;
+ if (is_cxl(root)) {
+ root->osc_ext_support_set = cxl_support;
+ *cxl_control = cxl_result;
+ }
+ } else if (is_cxl(root)) {
+ /*
+ * CXL _OSC is optional on CXL 1.1 hosts. Fall back to PCIe _OSC
+ * upon any failure using CXL _OSC.
+ */
+ root->bridge_type = ACPI_BRIDGE_TYPE_PCIE;
+ goto retry;
+ }
+ return status;
+}
+
+struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+ struct acpi_pci_root *root;
+
+ if (!device || acpi_match_device_ids(device, root_device_ids))
+ return NULL;
+
+ root = acpi_driver_data(device);
+
+ return root;
+}
+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
+ *
+ * Given an ACPI CA handle, the desired PCI device is located in the
+ * list of PCI devices.
+ *
+ * If the device is found, its reference count is increased and this
+ * function returns a pointer to its data structure. The caller must
+ * decrement the reference count by calling pci_dev_put().
+ * If no device is found, %NULL is returned.
+ */
+struct pci_dev *acpi_get_pci_dev(acpi_handle handle)
+{
+ struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
+ struct acpi_device_physical_node *pn;
+ struct pci_dev *pci_dev = NULL;
+
+ if (!adev)
+ return NULL;
+
+ mutex_lock(&adev->physical_node_lock);
+
+ 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;
+ }
+ }
+
+ mutex_unlock(&adev->physical_node_lock);
+
+ return pci_dev;
+}
+EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
+
+/**
+ * acpi_pci_osc_control_set - Request control of PCI root _OSC features.
+ * @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex).
+ * @mask: Mask of _OSC bits to request control of, place to store control mask.
+ * @support: _OSC supported capability.
+ * @cxl_mask: Mask of CXL _OSC control bits, place to store control mask.
+ * @cxl_support: CXL _OSC supported capability.
+ *
+ * Run _OSC query for @mask and if that is successful, compare the returned
+ * mask of control bits with @req. If all of the @req bits are set in the
+ * returned mask, run _OSC request for it.
+ *
+ * The variable at the @mask address may be modified regardless of whether or
+ * not the function returns success. On success it will contain the mask of
+ * _OSC bits the BIOS has granted control of, but its contents are meaningless
+ * on failure.
+ **/
+static acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask,
+ u32 support, u32 *cxl_mask,
+ u32 cxl_support)
+{
+ u32 req = OSC_PCI_EXPRESS_CAPABILITY_CONTROL;
+ struct acpi_pci_root *root;
+ acpi_status status;
+ u32 ctrl, cxl_ctrl = 0, capbuf[OSC_CXL_CAPABILITY_DWORDS];
+
+ if (!mask)
+ return AE_BAD_PARAMETER;
+
+ root = acpi_pci_find_root(handle);
+ if (!root)
+ return AE_NOT_EXIST;
+
+ ctrl = *mask;
+ *mask |= root->osc_control_set;
+
+ if (is_cxl(root)) {
+ cxl_ctrl = *cxl_mask;
+ *cxl_mask |= root->osc_ext_control_set;
+ }
+
+ /* Need to check the available controls bits before requesting them. */
+ do {
+ u32 pci_missing = 0, cxl_missing = 0;
+
+ status = acpi_pci_query_osc(root, support, mask, cxl_support,
+ cxl_mask);
+ if (ACPI_FAILURE(status))
+ return status;
+ if (is_cxl(root)) {
+ if (ctrl == *mask && cxl_ctrl == *cxl_mask)
+ break;
+ pci_missing = ctrl & ~(*mask);
+ cxl_missing = cxl_ctrl & ~(*cxl_mask);
+ } else {
+ if (ctrl == *mask)
+ break;
+ pci_missing = ctrl & ~(*mask);
+ }
+ if (pci_missing)
+ decode_osc_control(root, "platform does not support",
+ pci_missing);
+ if (cxl_missing)
+ decode_cxl_osc_control(root, "CXL platform does not support",
+ cxl_missing);
+ ctrl = *mask;
+ cxl_ctrl = *cxl_mask;
+ } while (*mask || *cxl_mask);
+
+ /* No need to request _OSC if the control was already granted. */
+ if ((root->osc_control_set & ctrl) == ctrl &&
+ (root->osc_ext_control_set & cxl_ctrl) == cxl_ctrl)
+ return AE_OK;
+
+ if ((ctrl & req) != req) {
+ decode_osc_control(root, "not requesting control; platform does not support",
+ req & ~(ctrl));
+ return AE_SUPPORT;
+ }
+
+ capbuf[OSC_QUERY_DWORD] = 0;
+ capbuf[OSC_SUPPORT_DWORD] = root->osc_support_set;
+ capbuf[OSC_CONTROL_DWORD] = ctrl;
+ if (is_cxl(root)) {
+ capbuf[OSC_EXT_SUPPORT_DWORD] = root->osc_ext_support_set;
+ capbuf[OSC_EXT_CONTROL_DWORD] = cxl_ctrl;
+ }
+
+ status = acpi_pci_run_osc(root, capbuf, mask, cxl_mask);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ root->osc_control_set = *mask;
+ root->osc_ext_control_set = *cxl_mask;
+ return AE_OK;
+}
+
+static u32 calculate_support(void)
+{
+ u32 support;
+
+ /*
+ * All supported architectures that use ACPI have support for
+ * PCI domains, so we indicate this in _OSC support capabilities.
+ */
+ support = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
+ support |= OSC_PCI_HPX_TYPE_3_SUPPORT;
+ if (pci_ext_cfg_avail())
+ support |= OSC_PCI_EXT_CONFIG_SUPPORT;
+ if (pcie_aspm_support_enabled())
+ support |= OSC_PCI_ASPM_SUPPORT | OSC_PCI_CLOCK_PM_SUPPORT;
+ if (pci_msi_enabled())
+ support |= OSC_PCI_MSI_SUPPORT;
+ if (IS_ENABLED(CONFIG_PCIE_EDR))
+ support |= OSC_PCI_EDR_SUPPORT;
+
+ return support;
+}
+
+/*
+ * Background on hotplug support, and making it depend on only
+ * CONFIG_HOTPLUG_PCI_PCIE vs. also considering CONFIG_MEMORY_HOTPLUG:
+ *
+ * CONFIG_ACPI_HOTPLUG_MEMORY does depend on CONFIG_MEMORY_HOTPLUG, but
+ * there is no existing _OSC for memory hotplug support. The reason is that
+ * ACPI memory hotplug requires the OS to acknowledge / coordinate with
+ * memory plug events via a scan handler. On the CXL side the equivalent
+ * would be if Linux supported the Mechanical Retention Lock [1], or
+ * otherwise had some coordination for the driver of a PCI device
+ * undergoing hotplug to be consulted on whether the hotplug should
+ * proceed or not.
+ *
+ * The concern is that if Linux says no to supporting CXL hotplug then
+ * the BIOS may say no to giving the OS hotplug control of any other PCIe
+ * device. So the question here is not whether hotplug is enabled, it's
+ * whether it is handled natively by the at all OS, and if
+ * CONFIG_HOTPLUG_PCI_PCIE is enabled then the answer is "yes".
+ *
+ * Otherwise, the plan for CXL coordinated remove, since the kernel does
+ * not support blocking hotplug, is to require the memory device to be
+ * disabled before hotplug is attempted. When CONFIG_MEMORY_HOTPLUG is
+ * disabled that step will fail and the remove attempt cancelled by the
+ * user. If that is not honored and the card is removed anyway then it
+ * does not matter if CONFIG_MEMORY_HOTPLUG is enabled or not, it will
+ * cause a crash and other badness.
+ *
+ * Therefore, just say yes to CXL hotplug and require removal to
+ * be coordinated by userspace unless and until the kernel grows better
+ * mechanisms for doing "managed" removal of devices in consultation with
+ * the driver.
+ *
+ * [1]: https://lore.kernel.org/all/20201122014203.4706-1-ashok.raj@intel.com/
+ */
+static u32 calculate_cxl_support(void)
+{
+ u32 support;
+
+ support = OSC_CXL_2_0_PORT_DEV_REG_ACCESS_SUPPORT;
+ if (pci_aer_available())
+ support |= OSC_CXL_PROTOCOL_ERR_REPORTING_SUPPORT;
+ if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
+ support |= OSC_CXL_NATIVE_HP_SUPPORT;
+
+ return support;
+}
+
+static u32 calculate_control(void)
+{
+ u32 control;
+
+ control = OSC_PCI_EXPRESS_CAPABILITY_CONTROL
+ | OSC_PCI_EXPRESS_PME_CONTROL;
+
+ if (IS_ENABLED(CONFIG_PCIEASPM))
+ control |= OSC_PCI_EXPRESS_LTR_CONTROL;
+
+ if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
+ control |= OSC_PCI_EXPRESS_NATIVE_HP_CONTROL;
+
+ if (IS_ENABLED(CONFIG_HOTPLUG_PCI_SHPC))
+ control |= OSC_PCI_SHPC_NATIVE_HP_CONTROL;
+
+ if (pci_aer_available())
+ control |= OSC_PCI_EXPRESS_AER_CONTROL;
+
+ /*
+ * Per the Downstream Port Containment Related Enhancements ECN to
+ * the PCI Firmware Spec, r3.2, sec 4.5.1, table 4-5,
+ * OSC_PCI_EXPRESS_DPC_CONTROL indicates the OS supports both DPC
+ * and EDR.
+ */
+ if (IS_ENABLED(CONFIG_PCIE_DPC) && IS_ENABLED(CONFIG_PCIE_EDR))
+ control |= OSC_PCI_EXPRESS_DPC_CONTROL;
+
+ return control;
+}
+
+static u32 calculate_cxl_control(void)
+{
+ u32 control = 0;
+
+ if (IS_ENABLED(CONFIG_MEMORY_FAILURE))
+ control |= OSC_CXL_ERROR_REPORTING_CONTROL;
+
+ return control;
+}
+
+static bool os_control_query_checks(struct acpi_pci_root *root, u32 support)
+{
+ struct acpi_device *device = root->device;
+
+ if (pcie_ports_disabled) {
+ dev_info(&device->dev, "PCIe port services disabled; not requesting _OSC control\n");
+ return false;
+ }
+
+ if ((support & ACPI_PCIE_REQ_SUPPORT) != ACPI_PCIE_REQ_SUPPORT) {
+ decode_osc_support(root, "not requesting OS control; OS requires",
+ ACPI_PCIE_REQ_SUPPORT);
+ return false;
+ }
+
+ return true;
+}
+
+static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm)
+{
+ u32 support, control = 0, requested = 0;
+ u32 cxl_support = 0, cxl_control = 0, cxl_requested = 0;
+ acpi_status status;
+ struct acpi_device *device = root->device;
+ acpi_handle handle = device->handle;
+
+ /*
+ * Apple always return failure on _OSC calls when _OSI("Darwin") has
+ * been called successfully. We know the feature set supported by the
+ * platform, so avoid calling _OSC at all
+ */
+ if (x86_apple_machine) {
+ root->osc_control_set = ~OSC_PCI_EXPRESS_PME_CONTROL;
+ decode_osc_control(root, "OS assumes control of",
+ root->osc_control_set);
+ return;
+ }
+
+ support = calculate_support();
+
+ decode_osc_support(root, "OS supports", support);
+
+ if (os_control_query_checks(root, support))
+ requested = control = calculate_control();
+
+ if (is_cxl(root)) {
+ cxl_support = calculate_cxl_support();
+ decode_cxl_osc_support(root, "OS supports", cxl_support);
+ cxl_requested = cxl_control = calculate_cxl_control();
+ }
+
+ status = acpi_pci_osc_control_set(handle, &control, support,
+ &cxl_control, cxl_support);
+ if (ACPI_SUCCESS(status)) {
+ if (control)
+ decode_osc_control(root, "OS now controls", control);
+ if (cxl_control)
+ decode_cxl_osc_control(root, "OS now controls",
+ cxl_control);
+
+ if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) {
+ /*
+ * We have ASPM control, but the FADT indicates that
+ * it's unsupported. Leave existing configuration
+ * intact and prevent the OS from touching it.
+ */
+ dev_info(&device->dev, "FADT indicates ASPM is unsupported, using BIOS configuration\n");
+ *no_aspm = 1;
+ }
+ } else {
+ /*
+ * We want to disable ASPM here, but aspm_disabled
+ * needs to remain in its state from boot so that we
+ * properly handle PCIe 1.1 devices. So we set this
+ * flag here, to defer the action until after the ACPI
+ * root scan.
+ */
+ *no_aspm = 1;
+
+ /* _OSC is optional for PCI host bridges */
+ if (status == AE_NOT_FOUND && !is_pcie(root))
+ return;
+
+ if (control) {
+ decode_osc_control(root, "OS requested", requested);
+ decode_osc_control(root, "platform willing to grant", control);
+ }
+ if (cxl_control) {
+ decode_cxl_osc_control(root, "OS requested", cxl_requested);
+ decode_cxl_osc_control(root, "platform willing to grant",
+ cxl_control);
+ }
+
+ dev_info(&device->dev, "_OSC: platform retains control of PCIe features (%s)\n",
+ acpi_format_exception(status));
+ }
+}
+
+static int acpi_pci_root_add(struct acpi_device *device,
+ const struct acpi_device_id *not_used)
+{
+ unsigned long long segment, bus;
+ acpi_status status;
+ int result;
+ struct acpi_pci_root *root;
+ acpi_handle handle = device->handle;
+ int no_aspm = 0;
+ bool hotadd = system_state == SYSTEM_RUNNING;
+ const char *acpi_hid;
+
+ root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL);
+ if (!root)
+ return -ENOMEM;
+
+ segment = 0;
+ status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL,
+ &segment);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ dev_err(&device->dev, "can't evaluate _SEG\n");
+ result = -ENODEV;
+ goto end;
+ }
+
+ /* Check _CRS first, then _BBN. If no _BBN, default to zero. */
+ root->secondary.flags = IORESOURCE_BUS;
+ status = try_get_root_bridge_busnr(handle, &root->secondary);
+ if (ACPI_FAILURE(status)) {
+ /*
+ * We need both the start and end of the downstream bus range
+ * to interpret _CBA (MMCONFIG base address), so it really is
+ * supposed to be in _CRS. If we don't find it there, all we
+ * can do is assume [_BBN-0xFF] or [0-0xFF].
+ */
+ root->secondary.end = 0xFF;
+ dev_warn(&device->dev,
+ FW_BUG "no secondary bus range in _CRS\n");
+ status = acpi_evaluate_integer(handle, METHOD_NAME__BBN,
+ NULL, &bus);
+ if (ACPI_SUCCESS(status))
+ root->secondary.start = bus;
+ else if (status == AE_NOT_FOUND)
+ root->secondary.start = 0;
+ else {
+ dev_err(&device->dev, "can't evaluate _BBN\n");
+ result = -ENODEV;
+ goto end;
+ }
+ }
+
+ 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);
+ device->driver_data = root;
+
+ if (hotadd && dmar_device_add(handle)) {
+ result = -ENXIO;
+ goto end;
+ }
+
+ pr_info("%s [%s] (domain %04x %pR)\n",
+ acpi_device_name(device), acpi_device_bid(device),
+ root->segment, &root->secondary);
+
+ root->mcfg_addr = acpi_pci_root_get_mcfg_addr(handle);
+
+ acpi_hid = acpi_device_hid(root->device);
+ if (strcmp(acpi_hid, "PNP0A08") == 0)
+ root->bridge_type = ACPI_BRIDGE_TYPE_PCIE;
+ else if (strcmp(acpi_hid, "ACPI0016") == 0)
+ root->bridge_type = ACPI_BRIDGE_TYPE_CXL;
+ else
+ dev_dbg(&device->dev, "Assuming non-PCIe host bridge\n");
+
+ negotiate_os_control(root, &no_aspm);
+
+ /*
+ * TBD: Need PCI interface for enumeration/configuration of roots.
+ */
+
+ /*
+ * Scan the Root Bridge
+ * --------------------
+ * Must do this prior to any attempt to bind the root device, as the
+ * PCI namespace does not get created until this call is made (and
+ * thus the root bridge's pci_dev does not exist).
+ */
+ root->bus = pci_acpi_scan_root(root);
+ if (!root->bus) {
+ dev_err(&device->dev,
+ "Bus %04x:%02x not present in PCI namespace\n",
+ root->segment, (unsigned int)root->secondary.start);
+ device->driver_data = NULL;
+ result = -ENODEV;
+ goto remove_dmar;
+ }
+
+ if (no_aspm)
+ pcie_no_aspm();
+
+ pci_acpi_add_bus_pm_notifier(device);
+ device_set_wakeup_capable(root->bus->bridge, device->wakeup.flags.valid);
+
+ if (hotadd) {
+ pcibios_resource_survey_bus(root->bus);
+ pci_assign_unassigned_root_bus_resources(root->bus);
+ /*
+ * This is only called for the hotadd case. For the boot-time
+ * case, we need to wait until after PCI initialization in
+ * order to deal with IOAPICs mapped in on a PCI BAR.
+ *
+ * This is currently x86-specific, because acpi_ioapic_add()
+ * is an empty function without CONFIG_ACPI_HOTPLUG_IOAPIC.
+ * And CONFIG_ACPI_HOTPLUG_IOAPIC depends on CONFIG_X86_IO_APIC
+ * (see drivers/acpi/Kconfig).
+ */
+ acpi_ioapic_add(root->device->handle);
+ }
+
+ pci_lock_rescan_remove();
+ pci_bus_add_devices(root->bus);
+ pci_unlock_rescan_remove();
+ return 1;
+
+remove_dmar:
+ if (hotadd)
+ dmar_device_remove(handle);
+end:
+ kfree(root);
+ return result;
+}
+
+static void acpi_pci_root_remove(struct acpi_device *device)
+{
+ struct acpi_pci_root *root = acpi_driver_data(device);
+
+ pci_lock_rescan_remove();
+
+ pci_stop_root_bus(root->bus);
+
+ pci_ioapic_remove(root);
+ device_set_wakeup_capable(root->bus->bridge, false);
+ pci_acpi_remove_bus_pm_notifier(device);
+
+ pci_remove_root_bus(root->bus);
+ WARN_ON(acpi_ioapic_remove(root));
+
+ dmar_device_remove(device->handle);
+
+ pci_unlock_rescan_remove();
+
+ kfree(root);
+}
+
+/*
+ * Following code to support acpi_pci_root_create() is copied from
+ * arch/x86/pci/acpi.c and modified so it could be reused by x86, IA64
+ * and ARM64.
+ */
+static void acpi_pci_root_validate_resources(struct device *dev,
+ struct list_head *resources,
+ unsigned long type)
+{
+ LIST_HEAD(list);
+ struct resource *res1, *res2, *root = NULL;
+ struct resource_entry *tmp, *entry, *entry2;
+
+ BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0);
+ root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource;
+
+ list_splice_init(resources, &list);
+ resource_list_for_each_entry_safe(entry, tmp, &list) {
+ bool free = false;
+ resource_size_t end;
+
+ res1 = entry->res;
+ if (!(res1->flags & type))
+ goto next;
+
+ /* Exclude non-addressable range or non-addressable portion */
+ end = min(res1->end, root->end);
+ if (end <= res1->start) {
+ dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n",
+ res1);
+ free = true;
+ goto next;
+ } else if (res1->end != end) {
+ dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n",
+ res1, (unsigned long long)end + 1,
+ (unsigned long long)res1->end);
+ res1->end = end;
+ }
+
+ resource_list_for_each_entry(entry2, resources) {
+ res2 = entry2->res;
+ if (!(res2->flags & type))
+ continue;
+
+ /*
+ * I don't like throwing away windows because then
+ * our resources no longer match the ACPI _CRS, but
+ * the kernel resource tree doesn't allow overlaps.
+ */
+ if (resource_union(res1, res2, res2)) {
+ dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n",
+ res2, res1);
+ free = true;
+ goto next;
+ }
+ }
+
+next:
+ resource_list_del(entry);
+ if (free)
+ resource_list_free_entry(entry);
+ else
+ resource_list_add_tail(entry, resources);
+ }
+}
+
+static void acpi_pci_root_remap_iospace(struct fwnode_handle *fwnode,
+ struct resource_entry *entry)
+{
+#ifdef PCI_IOBASE
+ struct resource *res = entry->res;
+ resource_size_t cpu_addr = res->start;
+ resource_size_t pci_addr = cpu_addr - entry->offset;
+ resource_size_t length = resource_size(res);
+ unsigned long port;
+
+ if (pci_register_io_range(fwnode, cpu_addr, length))
+ goto err;
+
+ port = pci_address_to_pio(cpu_addr);
+ if (port == (unsigned long)-1)
+ goto err;
+
+ res->start = port;
+ res->end = port + length - 1;
+ entry->offset = port - pci_addr;
+
+ if (pci_remap_iospace(res, cpu_addr) < 0)
+ goto err;
+
+ pr_info("Remapped I/O %pa to %pR\n", &cpu_addr, res);
+ return;
+err:
+ res->flags |= IORESOURCE_DISABLED;
+#endif
+}
+
+int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
+{
+ int ret;
+ struct list_head *list = &info->resources;
+ struct acpi_device *device = info->bridge;
+ struct resource_entry *entry, *tmp;
+ unsigned long flags;
+
+ flags = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_MEM_8AND16BIT;
+ ret = acpi_dev_get_resources(device, list,
+ acpi_dev_filter_resource_type_cb,
+ (void *)flags);
+ if (ret < 0)
+ dev_warn(&device->dev,
+ "failed to parse _CRS method, error code %d\n", ret);
+ else if (ret == 0)
+ dev_dbg(&device->dev,
+ "no IO and memory resources present in _CRS\n");
+ else {
+ resource_list_for_each_entry_safe(entry, tmp, list) {
+ if (entry->res->flags & IORESOURCE_IO)
+ acpi_pci_root_remap_iospace(&device->fwnode,
+ entry);
+
+ if (entry->res->flags & IORESOURCE_DISABLED)
+ resource_list_destroy_entry(entry);
+ else
+ entry->res->name = info->name;
+ }
+ acpi_pci_root_validate_resources(&device->dev, list,
+ IORESOURCE_MEM);
+ acpi_pci_root_validate_resources(&device->dev, list,
+ IORESOURCE_IO);
+ }
+
+ return ret;
+}
+
+static void pci_acpi_root_add_resources(struct acpi_pci_root_info *info)
+{
+ struct resource_entry *entry, *tmp;
+ struct resource *res, *conflict, *root = NULL;
+
+ resource_list_for_each_entry_safe(entry, tmp, &info->resources) {
+ res = entry->res;
+ if (res->flags & IORESOURCE_MEM)
+ root = &iomem_resource;
+ else if (res->flags & IORESOURCE_IO)
+ root = &ioport_resource;
+ else
+ continue;
+
+ /*
+ * Some legacy x86 host bridge drivers use iomem_resource and
+ * ioport_resource as default resource pool, skip it.
+ */
+ if (res == root)
+ continue;
+
+ conflict = insert_resource_conflict(root, res);
+ if (conflict) {
+ dev_info(&info->bridge->dev,
+ "ignoring host bridge window %pR (conflicts with %s %pR)\n",
+ res, conflict->name, conflict);
+ resource_list_destroy_entry(entry);
+ }
+ }
+}
+
+static void __acpi_pci_root_release_info(struct acpi_pci_root_info *info)
+{
+ struct resource *res;
+ struct resource_entry *entry, *tmp;
+
+ if (!info)
+ return;
+
+ resource_list_for_each_entry_safe(entry, tmp, &info->resources) {
+ res = entry->res;
+ if (res->parent &&
+ (res->flags & (IORESOURCE_MEM | IORESOURCE_IO)))
+ release_resource(res);
+ resource_list_destroy_entry(entry);
+ }
+
+ info->ops->release_info(info);
+}
+
+static void acpi_pci_root_release_info(struct pci_host_bridge *bridge)
+{
+ struct resource *res;
+ struct resource_entry *entry;
+
+ resource_list_for_each_entry(entry, &bridge->windows) {
+ res = entry->res;
+ if (res->flags & IORESOURCE_IO)
+ pci_unmap_iospace(res);
+ if (res->parent &&
+ (res->flags & (IORESOURCE_MEM | IORESOURCE_IO)))
+ release_resource(res);
+ }
+ __acpi_pci_root_release_info(bridge->release_data);
+}
+
+struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
+ struct acpi_pci_root_ops *ops,
+ struct acpi_pci_root_info *info,
+ void *sysdata)
+{
+ int ret, busnum = root->secondary.start;
+ struct acpi_device *device = root->device;
+ 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;
+ info->ops = ops;
+ INIT_LIST_HEAD(&info->resources);
+ snprintf(info->name, sizeof(info->name), "PCI Bus %04x:%02x",
+ root->segment, busnum);
+
+ if (ops->init_info && ops->init_info(info))
+ goto out_release_info;
+ if (ops->prepare_resources)
+ ret = ops->prepare_resources(info);
+ else
+ ret = acpi_pci_probe_root_resources(info);
+ if (ret < 0)
+ goto out_release_info;
+
+ pci_acpi_root_add_resources(info);
+ pci_add_resource(&info->resources, &root->secondary);
+ bus = pci_create_root_bus(NULL, busnum, ops->pci_ops,
+ sysdata, &info->resources);
+ if (!bus)
+ goto out_release_info;
+
+ host_bridge = to_pci_host_bridge(bus->bridge);
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
+ host_bridge->native_pcie_hotplug = 0;
+ if (!(root->osc_control_set & OSC_PCI_SHPC_NATIVE_HP_CONTROL))
+ host_bridge->native_shpc_hotplug = 0;
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL))
+ host_bridge->native_aer = 0;
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL))
+ host_bridge->native_pme = 0;
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_LTR_CONTROL))
+ host_bridge->native_ltr = 0;
+ 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);
+
+ acpi_dev_power_up_children_with_adr(device);
+
+ pci_scan_child_bus(bus);
+ pci_set_host_bridge_release(host_bridge, acpi_pci_root_release_info,
+ info);
+ if (node != NUMA_NO_NODE)
+ dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node);
+ return bus;
+
+out_release_info:
+ __acpi_pci_root_release_info(info);
+ return NULL;
+}
+
+void __init acpi_pci_root_init(void)
+{
+ if (acpi_pci_disabled)
+ return;
+
+ pci_acpi_crs_quirks();
+ acpi_scan_add_handler_with_hotplug(&pci_root_handler, "pci_root");
+}
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
new file mode 100644
index 000000000..d6cb2c27a
--- /dev/null
+++ b/drivers/acpi/pci_slot.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * pci_slot.c - ACPI PCI Slot Driver
+ *
+ * The code here is heavily leveraged from the acpiphp module.
+ * Thanks to Matthew Wilcox <matthew@wil.cx> for much guidance.
+ * Thanks to Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> for code
+ * review and fixes.
+ *
+ * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P.
+ * Alex Chiang <achiang@hp.com>
+ *
+ * Copyright (C) 2013 Huawei Tech. Co., Ltd.
+ * Jiang Liu <jiang.liu@huawei.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/pci-acpi.h>
+
+static int check_sta_before_sun;
+
+#define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */
+
+struct acpi_pci_slot {
+ struct pci_slot *pci_slot; /* corresponding pci_slot */
+ struct list_head list; /* node in the list of slots */
+};
+
+static LIST_HEAD(slot_list);
+static DEFINE_MUTEX(slot_list_lock);
+
+static int
+check_slot(acpi_handle handle, unsigned long long *sun)
+{
+ int device = -1;
+ unsigned long long adr, sta;
+ acpi_status status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+ pr_debug("Checking slot on path: %s\n", (char *)buffer.pointer);
+
+ if (check_sta_before_sun) {
+ /* If SxFy doesn't have _STA, we just assume it's there */
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT))
+ goto out;
+ }
+
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ pr_debug("_ADR returned %d on %s\n",
+ status, (char *)buffer.pointer);
+ goto out;
+ }
+
+ /* No _SUN == not a slot == bail */
+ status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
+ if (ACPI_FAILURE(status)) {
+ pr_debug("_SUN returned %d on %s\n",
+ status, (char *)buffer.pointer);
+ goto out;
+ }
+
+ device = (adr >> 16) & 0xffff;
+out:
+ kfree(buffer.pointer);
+ return device;
+}
+
+/*
+ * Check whether handle has an associated slot and create PCI slot if it has.
+ */
+static acpi_status
+register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ int device;
+ unsigned long long sun;
+ char name[SLOT_NAME_SIZE];
+ struct acpi_pci_slot *slot;
+ struct pci_slot *pci_slot;
+ struct pci_bus *pci_bus = context;
+
+ device = check_slot(handle, &sun);
+ if (device < 0)
+ return AE_OK;
+
+ /*
+ * There may be multiple PCI functions associated with the same slot.
+ * Check whether PCI slot has already been created for this PCI device.
+ */
+ list_for_each_entry(slot, &slot_list, list) {
+ pci_slot = slot->pci_slot;
+ if (pci_slot->bus == pci_bus && pci_slot->number == device)
+ return AE_OK;
+ }
+
+ slot = kmalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot)
+ return AE_OK;
+
+ 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));
+ kfree(slot);
+ return AE_OK;
+ }
+
+ slot->pci_slot = pci_slot;
+ list_add(&slot->list, &slot_list);
+
+ get_device(&pci_bus->dev);
+
+ pr_debug("%p, pci_bus: %x, device: %d, name: %s\n",
+ pci_slot, pci_bus->number, device, name);
+
+ return AE_OK;
+}
+
+void acpi_pci_slot_enumerate(struct pci_bus *bus)
+{
+ acpi_handle handle = ACPI_HANDLE(bus->bridge);
+
+ if (handle) {
+ mutex_lock(&slot_list_lock);
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
+ register_slot, NULL, bus, NULL);
+ mutex_unlock(&slot_list_lock);
+ }
+}
+
+void acpi_pci_slot_remove(struct pci_bus *bus)
+{
+ struct acpi_pci_slot *slot, *tmp;
+
+ mutex_lock(&slot_list_lock);
+ list_for_each_entry_safe(slot, tmp, &slot_list, list) {
+ if (slot->pci_slot->bus == bus) {
+ list_del(&slot->list);
+ pci_destroy_slot(slot->pci_slot);
+ put_device(&bus->dev);
+ kfree(slot);
+ }
+ }
+ mutex_unlock(&slot_list_lock);
+}
+
+static int do_sta_before_sun(const struct dmi_system_id *d)
+{
+ pr_info("%s detected: will evaluate _STA before calling _SUN\n",
+ d->ident);
+ check_sta_before_sun = 1;
+ return 0;
+}
+
+static const struct dmi_system_id acpi_pci_slot_dmi_table[] __initconst = {
+ /*
+ * Fujitsu Primequest machines will return 1023 to indicate an
+ * error if the _SUN method is evaluated on SxFy objects that
+ * are not present (as indicated by _STA), so for those machines,
+ * we want to check _STA before evaluating _SUN.
+ */
+ {
+ .callback = do_sta_before_sun,
+ .ident = "Fujitsu PRIMEQUEST",
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED"),
+ DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST"),
+ },
+ },
+ {}
+};
+
+void __init acpi_pci_slot_init(void)
+{
+ dmi_check_system(acpi_pci_slot_dmi_table);
+}
diff --git a/drivers/acpi/pfr_telemetry.c b/drivers/acpi/pfr_telemetry.c
new file mode 100644
index 000000000..27fb6cdad
--- /dev/null
+++ b/drivers/acpi/pfr_telemetry.c
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ACPI Platform Firmware Runtime Telemetry driver
+ *
+ * Copyright (C) 2021 Intel Corporation
+ * Author: Chen Yu <yu.c.chen@intel.com>
+ *
+ * This driver allows user space to fetch telemetry data from the
+ * firmware with the help of the Platform Firmware Runtime Telemetry
+ * interface.
+ */
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+#include <linux/uuid.h>
+
+#include <uapi/linux/pfrut.h>
+
+#define PFRT_LOG_EXEC_IDX 0
+#define PFRT_LOG_HISTORY_IDX 1
+
+#define PFRT_LOG_ERR 0
+#define PFRT_LOG_WARN 1
+#define PFRT_LOG_INFO 2
+#define PFRT_LOG_VERB 4
+
+#define PFRT_FUNC_SET_LEV 1
+#define PFRT_FUNC_GET_LEV 2
+#define PFRT_FUNC_GET_DATA 3
+
+#define PFRT_REVID_1 1
+#define PFRT_REVID_2 2
+#define PFRT_DEFAULT_REV_ID PFRT_REVID_1
+
+enum log_index {
+ LOG_STATUS_IDX = 0,
+ LOG_EXT_STATUS_IDX = 1,
+ LOG_MAX_SZ_IDX = 2,
+ LOG_CHUNK1_LO_IDX = 3,
+ LOG_CHUNK1_HI_IDX = 4,
+ LOG_CHUNK1_SZ_IDX = 5,
+ LOG_CHUNK2_LO_IDX = 6,
+ LOG_CHUNK2_HI_IDX = 7,
+ LOG_CHUNK2_SZ_IDX = 8,
+ LOG_ROLLOVER_CNT_IDX = 9,
+ LOG_RESET_CNT_IDX = 10,
+ LOG_NR_IDX
+};
+
+struct pfrt_log_device {
+ int index;
+ struct pfrt_log_info info;
+ struct device *parent_dev;
+ struct miscdevice miscdev;
+};
+
+/* pfrt_guid is the parameter for _DSM method */
+static const guid_t pfrt_log_guid =
+ GUID_INIT(0x75191659, 0x8178, 0x4D9D, 0xB8, 0x8F, 0xAC, 0x5E,
+ 0x5E, 0x93, 0xE8, 0xBF);
+
+static DEFINE_IDA(pfrt_log_ida);
+
+static inline struct pfrt_log_device *to_pfrt_log_dev(struct file *file)
+{
+ return container_of(file->private_data, struct pfrt_log_device, miscdev);
+}
+
+static int get_pfrt_log_data_info(struct pfrt_log_data_info *data_info,
+ struct pfrt_log_device *pfrt_log_dev)
+{
+ acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev);
+ union acpi_object *out_obj, in_obj, in_buf;
+ int ret = -EBUSY;
+
+ memset(data_info, 0, sizeof(*data_info));
+ memset(&in_obj, 0, sizeof(in_obj));
+ memset(&in_buf, 0, sizeof(in_buf));
+ in_obj.type = ACPI_TYPE_PACKAGE;
+ in_obj.package.count = 1;
+ in_obj.package.elements = &in_buf;
+ in_buf.type = ACPI_TYPE_INTEGER;
+ in_buf.integer.value = pfrt_log_dev->info.log_type;
+
+ out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid,
+ pfrt_log_dev->info.log_revid, PFRT_FUNC_GET_DATA,
+ &in_obj, ACPI_TYPE_PACKAGE);
+ if (!out_obj)
+ return -EINVAL;
+
+ if (out_obj->package.count < LOG_NR_IDX ||
+ out_obj->package.elements[LOG_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[LOG_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[LOG_MAX_SZ_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[LOG_CHUNK1_LO_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[LOG_CHUNK1_HI_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[LOG_CHUNK1_SZ_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[LOG_CHUNK2_LO_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[LOG_CHUNK2_HI_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[LOG_CHUNK2_SZ_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[LOG_RESET_CNT_IDX].type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+
+ data_info->status = out_obj->package.elements[LOG_STATUS_IDX].integer.value;
+ data_info->ext_status =
+ out_obj->package.elements[LOG_EXT_STATUS_IDX].integer.value;
+ if (data_info->status != DSM_SUCCEED) {
+ dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", data_info->status);
+ dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n",
+ data_info->ext_status);
+ goto free_acpi_buffer;
+ }
+
+ data_info->max_data_size =
+ out_obj->package.elements[LOG_MAX_SZ_IDX].integer.value;
+ data_info->chunk1_addr_lo =
+ out_obj->package.elements[LOG_CHUNK1_LO_IDX].integer.value;
+ data_info->chunk1_addr_hi =
+ out_obj->package.elements[LOG_CHUNK1_HI_IDX].integer.value;
+ data_info->chunk1_size =
+ out_obj->package.elements[LOG_CHUNK1_SZ_IDX].integer.value;
+ data_info->chunk2_addr_lo =
+ out_obj->package.elements[LOG_CHUNK2_LO_IDX].integer.value;
+ data_info->chunk2_addr_hi =
+ out_obj->package.elements[LOG_CHUNK2_HI_IDX].integer.value;
+ data_info->chunk2_size =
+ out_obj->package.elements[LOG_CHUNK2_SZ_IDX].integer.value;
+ data_info->rollover_cnt =
+ out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].integer.value;
+ data_info->reset_cnt =
+ out_obj->package.elements[LOG_RESET_CNT_IDX].integer.value;
+
+ ret = 0;
+
+free_acpi_buffer:
+ ACPI_FREE(out_obj);
+
+ return ret;
+}
+
+static int set_pfrt_log_level(int level, struct pfrt_log_device *pfrt_log_dev)
+{
+ acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev);
+ union acpi_object *out_obj, *obj, in_obj, in_buf;
+ enum pfru_dsm_status status, ext_status;
+ int ret = 0;
+
+ memset(&in_obj, 0, sizeof(in_obj));
+ memset(&in_buf, 0, sizeof(in_buf));
+ in_obj.type = ACPI_TYPE_PACKAGE;
+ in_obj.package.count = 1;
+ in_obj.package.elements = &in_buf;
+ in_buf.type = ACPI_TYPE_INTEGER;
+ in_buf.integer.value = level;
+
+ out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid,
+ pfrt_log_dev->info.log_revid, PFRT_FUNC_SET_LEV,
+ &in_obj, ACPI_TYPE_PACKAGE);
+ if (!out_obj)
+ return -EINVAL;
+
+ obj = &out_obj->package.elements[0];
+ status = obj->integer.value;
+ if (status != DSM_SUCCEED) {
+ obj = &out_obj->package.elements[1];
+ ext_status = obj->integer.value;
+ dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", status);
+ dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", ext_status);
+ ret = -EBUSY;
+ }
+
+ ACPI_FREE(out_obj);
+
+ return ret;
+}
+
+static int get_pfrt_log_level(struct pfrt_log_device *pfrt_log_dev)
+{
+ acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev);
+ union acpi_object *out_obj, *obj;
+ enum pfru_dsm_status status, ext_status;
+ int ret = -EBUSY;
+
+ out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid,
+ pfrt_log_dev->info.log_revid, PFRT_FUNC_GET_LEV,
+ NULL, ACPI_TYPE_PACKAGE);
+ if (!out_obj)
+ return -EINVAL;
+
+ obj = &out_obj->package.elements[0];
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+
+ status = obj->integer.value;
+ if (status != DSM_SUCCEED) {
+ obj = &out_obj->package.elements[1];
+ ext_status = obj->integer.value;
+ dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", status);
+ dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", ext_status);
+ goto free_acpi_buffer;
+ }
+
+ obj = &out_obj->package.elements[2];
+ if (obj->type != ACPI_TYPE_INTEGER)
+ goto free_acpi_buffer;
+
+ ret = obj->integer.value;
+
+free_acpi_buffer:
+ ACPI_FREE(out_obj);
+
+ return ret;
+}
+
+static int valid_log_level(u32 level)
+{
+ return level == PFRT_LOG_ERR || level == PFRT_LOG_WARN ||
+ level == PFRT_LOG_INFO || level == PFRT_LOG_VERB;
+}
+
+static int valid_log_type(u32 type)
+{
+ return type == PFRT_LOG_EXEC_IDX || type == PFRT_LOG_HISTORY_IDX;
+}
+
+static inline int valid_log_revid(u32 id)
+{
+ return id == PFRT_REVID_1 || id == PFRT_REVID_2;
+}
+
+static long pfrt_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct pfrt_log_device *pfrt_log_dev = to_pfrt_log_dev(file);
+ struct pfrt_log_data_info data_info;
+ struct pfrt_log_info info;
+ void __user *p;
+ int ret = 0;
+
+ p = (void __user *)arg;
+
+ switch (cmd) {
+ case PFRT_LOG_IOC_SET_INFO:
+ if (copy_from_user(&info, p, sizeof(info)))
+ return -EFAULT;
+
+ if (valid_log_revid(info.log_revid))
+ pfrt_log_dev->info.log_revid = info.log_revid;
+
+ if (valid_log_level(info.log_level)) {
+ ret = set_pfrt_log_level(info.log_level, pfrt_log_dev);
+ if (ret < 0)
+ return ret;
+
+ pfrt_log_dev->info.log_level = info.log_level;
+ }
+
+ if (valid_log_type(info.log_type))
+ pfrt_log_dev->info.log_type = info.log_type;
+
+ return 0;
+
+ 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)))
+ return -EFAULT;
+
+ return 0;
+
+ case PFRT_LOG_IOC_GET_DATA_INFO:
+ ret = get_pfrt_log_data_info(&data_info, pfrt_log_dev);
+ if (ret)
+ return ret;
+
+ if (copy_to_user(p, &data_info, sizeof(struct pfrt_log_data_info)))
+ return -EFAULT;
+
+ return 0;
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int
+pfrt_log_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct pfrt_log_device *pfrt_log_dev;
+ struct pfrt_log_data_info info;
+ unsigned long psize, vsize;
+ phys_addr_t base_addr;
+ int ret;
+
+ if (vma->vm_flags & VM_WRITE)
+ return -EROFS;
+
+ /* changing from read to write with mprotect is not allowed */
+ vma->vm_flags &= ~VM_MAYWRITE;
+
+ pfrt_log_dev = to_pfrt_log_dev(file);
+
+ ret = get_pfrt_log_data_info(&info, pfrt_log_dev);
+ if (ret)
+ return ret;
+
+ base_addr = (phys_addr_t)((info.chunk2_addr_hi << 32) | info.chunk2_addr_lo);
+ /* pfrt update has not been launched yet */
+ if (!base_addr)
+ return -ENODEV;
+
+ psize = info.max_data_size;
+ /* base address and total buffer size must be page aligned */
+ if (!PAGE_ALIGNED(base_addr) || !PAGE_ALIGNED(psize))
+ return -ENODEV;
+
+ vsize = vma->vm_end - vma->vm_start;
+ if (vsize > psize)
+ return -EINVAL;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (io_remap_pfn_range(vma, vma->vm_start, PFN_DOWN(base_addr),
+ vsize, vma->vm_page_prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static const struct file_operations acpi_pfrt_log_fops = {
+ .owner = THIS_MODULE,
+ .mmap = pfrt_log_mmap,
+ .unlocked_ioctl = pfrt_log_ioctl,
+ .llseek = noop_llseek,
+};
+
+static int 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)
+{
+ struct pfrt_log_device *pfrt_log_dev = data;
+
+ ida_free(&pfrt_log_ida, pfrt_log_dev->index);
+}
+
+static int acpi_pfrt_log_probe(struct platform_device *pdev)
+{
+ acpi_handle handle = ACPI_HANDLE(&pdev->dev);
+ struct pfrt_log_device *pfrt_log_dev;
+ int ret;
+
+ if (!acpi_has_method(handle, "_DSM")) {
+ dev_dbg(&pdev->dev, "Missing _DSM\n");
+ return -ENODEV;
+ }
+
+ pfrt_log_dev = devm_kzalloc(&pdev->dev, sizeof(*pfrt_log_dev), GFP_KERNEL);
+ if (!pfrt_log_dev)
+ return -ENOMEM;
+
+ ret = ida_alloc(&pfrt_log_ida, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+
+ pfrt_log_dev->index = ret;
+ ret = devm_add_action_or_reset(&pdev->dev, pfrt_log_put_idx, pfrt_log_dev);
+ if (ret)
+ return ret;
+
+ pfrt_log_dev->info.log_revid = PFRT_DEFAULT_REV_ID;
+ pfrt_log_dev->parent_dev = &pdev->dev;
+
+ pfrt_log_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
+ pfrt_log_dev->miscdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "pfrt%d",
+ pfrt_log_dev->index);
+ if (!pfrt_log_dev->miscdev.name)
+ return -ENOMEM;
+
+ pfrt_log_dev->miscdev.nodename = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "acpi_pfr_telemetry%d",
+ pfrt_log_dev->index);
+ if (!pfrt_log_dev->miscdev.nodename)
+ return -ENOMEM;
+
+ pfrt_log_dev->miscdev.fops = &acpi_pfrt_log_fops;
+ pfrt_log_dev->miscdev.parent = &pdev->dev;
+
+ ret = misc_register(&pfrt_log_dev->miscdev);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, pfrt_log_dev);
+
+ return 0;
+}
+
+static const struct acpi_device_id acpi_pfrt_log_ids[] = {
+ {"INTC1081"},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, acpi_pfrt_log_ids);
+
+static struct platform_driver acpi_pfrt_log_driver = {
+ .driver = {
+ .name = "pfr_telemetry",
+ .acpi_match_table = acpi_pfrt_log_ids,
+ },
+ .probe = acpi_pfrt_log_probe,
+ .remove = acpi_pfrt_log_remove,
+};
+module_platform_driver(acpi_pfrt_log_driver);
+
+MODULE_DESCRIPTION("Platform Firmware Runtime Update Telemetry driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c
new file mode 100644
index 000000000..98267f163
--- /dev/null
+++ b/drivers/acpi/pfr_update.c
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ACPI Platform Firmware Runtime Update Device driver
+ *
+ * Copyright (C) 2021 Intel Corporation
+ * Author: Chen Yu <yu.c.chen@intel.com>
+ *
+ * pfr_update driver is used for Platform Firmware Runtime
+ * Update, which includes the code injection and driver update.
+ */
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/efi.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+#include <linux/uuid.h>
+
+#include <uapi/linux/pfrut.h>
+
+#define PFRU_FUNC_STANDARD_QUERY 0
+#define PFRU_FUNC_QUERY_UPDATE_CAP 1
+#define PFRU_FUNC_QUERY_BUF 2
+#define PFRU_FUNC_START 3
+
+#define PFRU_CODE_INJECT_TYPE 1
+#define PFRU_DRIVER_UPDATE_TYPE 2
+
+#define PFRU_REVID_1 1
+#define PFRU_REVID_2 2
+#define PFRU_DEFAULT_REV_ID PFRU_REVID_1
+
+enum cap_index {
+ CAP_STATUS_IDX = 0,
+ CAP_UPDATE_IDX = 1,
+ CAP_CODE_TYPE_IDX = 2,
+ CAP_FW_VER_IDX = 3,
+ CAP_CODE_RT_VER_IDX = 4,
+ CAP_DRV_TYPE_IDX = 5,
+ CAP_DRV_RT_VER_IDX = 6,
+ CAP_DRV_SVN_IDX = 7,
+ CAP_PLAT_ID_IDX = 8,
+ CAP_OEM_ID_IDX = 9,
+ CAP_OEM_INFO_IDX = 10,
+ CAP_NR_IDX
+};
+
+enum buf_index {
+ BUF_STATUS_IDX = 0,
+ BUF_EXT_STATUS_IDX = 1,
+ BUF_ADDR_LOW_IDX = 2,
+ BUF_ADDR_HI_IDX = 3,
+ BUF_SIZE_IDX = 4,
+ BUF_NR_IDX
+};
+
+enum update_index {
+ UPDATE_STATUS_IDX = 0,
+ UPDATE_EXT_STATUS_IDX = 1,
+ UPDATE_AUTH_TIME_LOW_IDX = 2,
+ UPDATE_AUTH_TIME_HI_IDX = 3,
+ UPDATE_EXEC_TIME_LOW_IDX = 4,
+ UPDATE_EXEC_TIME_HI_IDX = 5,
+ UPDATE_NR_IDX
+};
+
+enum pfru_start_action {
+ START_STAGE = 0,
+ START_ACTIVATE = 1,
+ START_STAGE_ACTIVATE = 2,
+};
+
+struct pfru_device {
+ u32 rev_id, index;
+ struct device *parent_dev;
+ struct miscdevice miscdev;
+};
+
+static DEFINE_IDA(pfru_ida);
+
+/*
+ * Manual reference:
+ * https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf
+ *
+ * pfru_guid is the parameter for _DSM method
+ */
+static const guid_t pfru_guid =
+ GUID_INIT(0xECF9533B, 0x4A3C, 0x4E89, 0x93, 0x9E, 0xC7, 0x71,
+ 0x12, 0x60, 0x1C, 0x6D);
+
+/* pfru_code_inj_guid is the UUID to identify code injection EFI capsule file */
+static const guid_t pfru_code_inj_guid =
+ GUID_INIT(0xB2F84B79, 0x7B6E, 0x4E45, 0x88, 0x5F, 0x3F, 0xB9,
+ 0xBB, 0x18, 0x54, 0x02);
+
+/* pfru_drv_update_guid is the UUID to identify driver update EFI capsule file */
+static const guid_t pfru_drv_update_guid =
+ GUID_INIT(0x4569DD8C, 0x75F1, 0x429A, 0xA3, 0xD6, 0x24, 0xDE,
+ 0x80, 0x97, 0xA0, 0xDF);
+
+static inline int pfru_valid_revid(u32 id)
+{
+ return id == PFRU_REVID_1 || id == PFRU_REVID_2;
+}
+
+static inline struct pfru_device *to_pfru_dev(struct file *file)
+{
+ return container_of(file->private_data, struct pfru_device, miscdev);
+}
+
+static int query_capability(struct pfru_update_cap_info *cap_hdr,
+ struct pfru_device *pfru_dev)
+{
+ acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev);
+ union acpi_object *out_obj;
+ int ret = -EINVAL;
+
+ out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid,
+ pfru_dev->rev_id,
+ PFRU_FUNC_QUERY_UPDATE_CAP,
+ NULL, ACPI_TYPE_PACKAGE);
+ if (!out_obj)
+ return ret;
+
+ if (out_obj->package.count < CAP_NR_IDX ||
+ out_obj->package.elements[CAP_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[CAP_UPDATE_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[CAP_CODE_TYPE_IDX].type != ACPI_TYPE_BUFFER ||
+ out_obj->package.elements[CAP_FW_VER_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[CAP_CODE_RT_VER_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[CAP_DRV_TYPE_IDX].type != ACPI_TYPE_BUFFER ||
+ out_obj->package.elements[CAP_DRV_RT_VER_IDX].type != ACPI_TYPE_INTEGER ||
+ 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)
+ 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);
+ goto free_acpi_buffer;
+ }
+
+ cap_hdr->update_cap = out_obj->package.elements[CAP_UPDATE_IDX].integer.value;
+ memcpy(&cap_hdr->code_type,
+ out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.pointer,
+ out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.length);
+ cap_hdr->fw_version =
+ out_obj->package.elements[CAP_FW_VER_IDX].integer.value;
+ cap_hdr->code_rt_version =
+ out_obj->package.elements[CAP_CODE_RT_VER_IDX].integer.value;
+ memcpy(&cap_hdr->drv_type,
+ out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.pointer,
+ out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.length);
+ cap_hdr->drv_rt_version =
+ out_obj->package.elements[CAP_DRV_RT_VER_IDX].integer.value;
+ cap_hdr->drv_svn =
+ out_obj->package.elements[CAP_DRV_SVN_IDX].integer.value;
+ memcpy(&cap_hdr->platform_id,
+ out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.pointer,
+ out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.length);
+ memcpy(&cap_hdr->oem_id,
+ out_obj->package.elements[CAP_OEM_ID_IDX].buffer.pointer,
+ out_obj->package.elements[CAP_OEM_ID_IDX].buffer.length);
+ cap_hdr->oem_info_len =
+ out_obj->package.elements[CAP_OEM_INFO_IDX].buffer.length;
+
+ ret = 0;
+
+free_acpi_buffer:
+ ACPI_FREE(out_obj);
+
+ return ret;
+}
+
+static int query_buffer(struct pfru_com_buf_info *info,
+ struct pfru_device *pfru_dev)
+{
+ acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev);
+ union acpi_object *out_obj;
+ int ret = -EINVAL;
+
+ out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid,
+ pfru_dev->rev_id, PFRU_FUNC_QUERY_BUF,
+ NULL, ACPI_TYPE_PACKAGE);
+ if (!out_obj)
+ 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)
+ 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);
+
+ goto free_acpi_buffer;
+ }
+
+ info->addr_lo =
+ out_obj->package.elements[BUF_ADDR_LOW_IDX].integer.value;
+ info->addr_hi =
+ out_obj->package.elements[BUF_ADDR_HI_IDX].integer.value;
+ info->buf_size = out_obj->package.elements[BUF_SIZE_IDX].integer.value;
+
+ ret = 0;
+
+free_acpi_buffer:
+ ACPI_FREE(out_obj);
+
+ return ret;
+}
+
+static int get_image_type(const struct efi_manage_capsule_image_header *img_hdr,
+ struct pfru_device *pfru_dev)
+{
+ const efi_guid_t *image_type_id = &img_hdr->image_type_id;
+
+ /* check whether this is a code injection or driver update */
+ if (guid_equal(image_type_id, &pfru_code_inj_guid))
+ return PFRU_CODE_INJECT_TYPE;
+
+ if (guid_equal(image_type_id, &pfru_drv_update_guid))
+ return PFRU_DRIVER_UPDATE_TYPE;
+
+ return -EINVAL;
+}
+
+static int adjust_efi_size(const struct efi_manage_capsule_image_header *img_hdr,
+ int size)
+{
+ /*
+ * The (u64 hw_ins) was introduced in UEFI spec version 2,
+ * and (u64 capsule_support) was introduced in version 3.
+ * The size needs to be adjusted accordingly. That is to
+ * say, version 1 should subtract the size of hw_ins+capsule_support,
+ * and version 2 should sbstract the size of capsule_support.
+ */
+ size += sizeof(struct efi_manage_capsule_image_header);
+ switch (img_hdr->ver) {
+ case 1:
+ return size - 2 * sizeof(u64);
+
+ case 2:
+ return size - sizeof(u64);
+
+ default:
+ /* only support version 1 and 2 */
+ return -EINVAL;
+ }
+}
+
+static bool applicable_image(const void *data, struct pfru_update_cap_info *cap,
+ struct pfru_device *pfru_dev)
+{
+ struct pfru_payload_hdr *payload_hdr;
+ const efi_capsule_header_t *cap_hdr = data;
+ const struct efi_manage_capsule_header *m_hdr;
+ const struct efi_manage_capsule_image_header *m_img_hdr;
+ const struct efi_image_auth *auth;
+ int type, size;
+
+ /*
+ * If the code in the capsule is older than the current
+ * firmware code, the update will be rejected by the firmware,
+ * so check the version of it upfront without engaging the
+ * Management Mode update mechanism which may be costly.
+ */
+ size = cap_hdr->headersize;
+ m_hdr = data + size;
+ /*
+ * Current data structure size plus variable array indicated
+ * by number of (emb_drv_cnt + payload_cnt)
+ */
+ size += offsetof(struct efi_manage_capsule_header, offset_list) +
+ (m_hdr->emb_drv_cnt + m_hdr->payload_cnt) * sizeof(u64);
+ m_img_hdr = data + size;
+
+ type = get_image_type(m_img_hdr, pfru_dev);
+ if (type < 0)
+ return false;
+
+ size = adjust_efi_size(m_img_hdr, size);
+ if (size < 0)
+ return false;
+
+ auth = data + size;
+ size += sizeof(u64) + auth->auth_info.hdr.len;
+ payload_hdr = (struct pfru_payload_hdr *)(data + size);
+
+ /* finally compare the version */
+ if (type == PFRU_CODE_INJECT_TYPE)
+ return payload_hdr->rt_ver >= cap->code_rt_version;
+
+ return payload_hdr->rt_ver >= cap->drv_rt_version;
+}
+
+static void print_update_debug_info(struct pfru_updated_result *result,
+ struct pfru_device *pfru_dev)
+{
+ dev_dbg(pfru_dev->parent_dev, "Update result:\n");
+ dev_dbg(pfru_dev->parent_dev, "Authentication Time Low:%lld\n",
+ result->low_auth_time);
+ dev_dbg(pfru_dev->parent_dev, "Authentication Time High:%lld\n",
+ result->high_auth_time);
+ dev_dbg(pfru_dev->parent_dev, "Execution Time Low:%lld\n",
+ result->low_exec_time);
+ dev_dbg(pfru_dev->parent_dev, "Execution Time High:%lld\n",
+ result->high_exec_time);
+}
+
+static int start_update(int action, struct pfru_device *pfru_dev)
+{
+ union acpi_object *out_obj, in_obj, in_buf;
+ struct pfru_updated_result update_result;
+ acpi_handle handle;
+ int ret = -EINVAL;
+
+ memset(&in_obj, 0, sizeof(in_obj));
+ memset(&in_buf, 0, sizeof(in_buf));
+ in_obj.type = ACPI_TYPE_PACKAGE;
+ in_obj.package.count = 1;
+ in_obj.package.elements = &in_buf;
+ in_buf.type = ACPI_TYPE_INTEGER;
+ in_buf.integer.value = action;
+
+ handle = ACPI_HANDLE(pfru_dev->parent_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)
+ return ret;
+
+ if (out_obj->package.count < UPDATE_NR_IDX ||
+ out_obj->package.elements[UPDATE_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
+ out_obj->package.elements[UPDATE_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
+ 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)
+ goto free_acpi_buffer;
+
+ update_result.status =
+ out_obj->package.elements[UPDATE_STATUS_IDX].integer.value;
+ update_result.ext_status =
+ out_obj->package.elements[UPDATE_EXT_STATUS_IDX].integer.value;
+
+ 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",
+ update_result.ext_status);
+
+ goto free_acpi_buffer;
+ }
+
+ update_result.low_auth_time =
+ out_obj->package.elements[UPDATE_AUTH_TIME_LOW_IDX].integer.value;
+ update_result.high_auth_time =
+ out_obj->package.elements[UPDATE_AUTH_TIME_HI_IDX].integer.value;
+ update_result.low_exec_time =
+ out_obj->package.elements[UPDATE_EXEC_TIME_LOW_IDX].integer.value;
+ update_result.high_exec_time =
+ out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].integer.value;
+
+ print_update_debug_info(&update_result, pfru_dev);
+ ret = 0;
+
+free_acpi_buffer:
+ ACPI_FREE(out_obj);
+
+ return ret;
+}
+
+static long pfru_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct pfru_update_cap_info cap_hdr;
+ struct pfru_device *pfru_dev = to_pfru_dev(file);
+ void __user *p = (void __user *)arg;
+ u32 rev;
+ int ret;
+
+ switch (cmd) {
+ case PFRU_IOC_QUERY_CAP:
+ ret = query_capability(&cap_hdr, pfru_dev);
+ if (ret)
+ return ret;
+
+ if (copy_to_user(p, &cap_hdr, sizeof(cap_hdr)))
+ return -EFAULT;
+
+ return 0;
+
+ case PFRU_IOC_SET_REV:
+ if (copy_from_user(&rev, p, sizeof(rev)))
+ return -EFAULT;
+
+ if (!pfru_valid_revid(rev))
+ return -EINVAL;
+
+ pfru_dev->rev_id = rev;
+
+ return 0;
+
+ case PFRU_IOC_STAGE:
+ return start_update(START_STAGE, pfru_dev);
+
+ case PFRU_IOC_ACTIVATE:
+ return start_update(START_ACTIVATE, pfru_dev);
+
+ case PFRU_IOC_STAGE_ACTIVATE:
+ return start_update(START_STAGE_ACTIVATE, pfru_dev);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static ssize_t pfru_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct pfru_device *pfru_dev = to_pfru_dev(file);
+ struct pfru_update_cap_info cap;
+ struct pfru_com_buf_info buf_info;
+ phys_addr_t phy_addr;
+ struct iov_iter iter;
+ struct iovec iov;
+ char *buf_ptr;
+ int ret;
+
+ ret = query_buffer(&buf_info, pfru_dev);
+ if (ret)
+ return ret;
+
+ if (len > buf_info.buf_size)
+ return -EINVAL;
+
+ iov.iov_base = (void __user *)buf;
+ iov.iov_len = 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)
+ return -ENOMEM;
+
+ if (!copy_from_iter_full(buf_ptr, len, &iter)) {
+ ret = -EINVAL;
+ goto unmap;
+ }
+
+ /* check if the capsule header has a valid version number */
+ ret = query_capability(&cap, pfru_dev);
+ if (ret)
+ goto unmap;
+
+ if (!applicable_image(buf_ptr, &cap, pfru_dev))
+ ret = -EINVAL;
+
+unmap:
+ memunmap(buf_ptr);
+
+ return ret ?: len;
+}
+
+static const struct file_operations acpi_pfru_fops = {
+ .owner = THIS_MODULE,
+ .write = pfru_write,
+ .unlocked_ioctl = pfru_ioctl,
+ .llseek = noop_llseek,
+};
+
+static int 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)
+{
+ struct pfru_device *pfru_dev = data;
+
+ ida_free(&pfru_ida, pfru_dev->index);
+}
+
+static int acpi_pfru_probe(struct platform_device *pdev)
+{
+ acpi_handle handle = ACPI_HANDLE(&pdev->dev);
+ struct pfru_device *pfru_dev;
+ int ret;
+
+ if (!acpi_has_method(handle, "_DSM")) {
+ dev_dbg(&pdev->dev, "Missing _DSM\n");
+ return -ENODEV;
+ }
+
+ pfru_dev = devm_kzalloc(&pdev->dev, sizeof(*pfru_dev), GFP_KERNEL);
+ if (!pfru_dev)
+ return -ENOMEM;
+
+ ret = ida_alloc(&pfru_ida, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+
+ pfru_dev->index = ret;
+ ret = devm_add_action_or_reset(&pdev->dev, pfru_put_idx, pfru_dev);
+ if (ret)
+ return ret;
+
+ pfru_dev->rev_id = PFRU_DEFAULT_REV_ID;
+ pfru_dev->parent_dev = &pdev->dev;
+
+ pfru_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
+ pfru_dev->miscdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "pfru%d", pfru_dev->index);
+ if (!pfru_dev->miscdev.name)
+ return -ENOMEM;
+
+ pfru_dev->miscdev.nodename = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "acpi_pfr_update%d", pfru_dev->index);
+ if (!pfru_dev->miscdev.nodename)
+ return -ENOMEM;
+
+ pfru_dev->miscdev.fops = &acpi_pfru_fops;
+ pfru_dev->miscdev.parent = &pdev->dev;
+
+ ret = misc_register(&pfru_dev->miscdev);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, pfru_dev);
+
+ return 0;
+}
+
+static const struct acpi_device_id acpi_pfru_ids[] = {
+ {"INTC1080"},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, acpi_pfru_ids);
+
+static struct platform_driver acpi_pfru_driver = {
+ .driver = {
+ .name = "pfr_update",
+ .acpi_match_table = acpi_pfru_ids,
+ },
+ .probe = acpi_pfru_probe,
+ .remove = acpi_pfru_remove,
+};
+module_platform_driver(acpi_pfru_driver);
+
+MODULE_DESCRIPTION("Platform Firmware Runtime Update device driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
new file mode 100644
index 000000000..d418462ab
--- /dev/null
+++ b/drivers/acpi/platform_profile.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* Platform profile sysfs interface */
+
+#include <linux/acpi.h>
+#include <linux/bits.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;
+static DEFINE_MUTEX(profile_lock);
+
+static const char * const profile_names[] = {
+ [PLATFORM_PROFILE_LOW_POWER] = "low-power",
+ [PLATFORM_PROFILE_COOL] = "cool",
+ [PLATFORM_PROFILE_QUIET] = "quiet",
+ [PLATFORM_PROFILE_BALANCED] = "balanced",
+ [PLATFORM_PROFILE_BALANCED_PERFORMANCE] = "balanced-performance",
+ [PLATFORM_PROFILE_PERFORMANCE] = "performance",
+};
+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;
+
+ if (!cur_profile) {
+ mutex_unlock(&profile_lock);
+ return -ENODEV;
+ }
+
+ for_each_set_bit(i, cur_profile->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)
+{
+ enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;
+ int err;
+
+ err = mutex_lock_interruptible(&profile_lock);
+ if (err)
+ return err;
+
+ if (!cur_profile) {
+ mutex_unlock(&profile_lock);
+ return -ENODEV;
+ }
+
+ err = cur_profile->profile_get(cur_profile, &profile);
+ mutex_unlock(&profile_lock);
+ if (err)
+ return err;
+
+ /* Check that profile is valid index */
+ if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))
+ return -EIO;
+
+ return sysfs_emit(buf, "%s\n", profile_names[profile]);
+}
+
+static ssize_t platform_profile_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int err, i;
+
+ err = mutex_lock_interruptible(&profile_lock);
+ if (err)
+ return err;
+
+ if (!cur_profile) {
+ mutex_unlock(&profile_lock);
+ return -ENODEV;
+ }
+
+ /* Scan for a matching profile */
+ i = sysfs_match_string(profile_names, buf);
+ if (i < 0) {
+ mutex_unlock(&profile_lock);
+ return -EINVAL;
+ }
+
+ /* Check that platform supports this profile choice */
+ if (!test_bit(i, cur_profile->choices)) {
+ mutex_unlock(&profile_lock);
+ return -EOPNOTSUPP;
+ }
+
+ err = cur_profile->profile_set(cur_profile, i);
+ if (!err)
+ 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 attribute *platform_profile_attrs[] = {
+ &dev_attr_platform_profile_choices.attr,
+ &dev_attr_platform_profile.attr,
+ NULL
+};
+
+static const struct attribute_group platform_profile_group = {
+ .attrs = platform_profile_attrs
+};
+
+void platform_profile_notify(void)
+{
+ if (!cur_profile)
+ return;
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
+}
+EXPORT_SYMBOL_GPL(platform_profile_notify);
+
+int platform_profile_register(struct platform_profile_handler *pprof)
+{
+ int err;
+
+ mutex_lock(&profile_lock);
+ /* We can only have one active profile */
+ if (cur_profile) {
+ mutex_unlock(&profile_lock);
+ return -EEXIST;
+ }
+
+ /* 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;
+ }
+
+ err = sysfs_create_group(acpi_kobj, &platform_profile_group);
+ if (err) {
+ mutex_unlock(&profile_lock);
+ return err;
+ }
+
+ cur_profile = pprof;
+ mutex_unlock(&profile_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(platform_profile_register);
+
+int platform_profile_remove(void)
+{
+ sysfs_remove_group(acpi_kobj, &platform_profile_group);
+
+ mutex_lock(&profile_lock);
+ cur_profile = NULL;
+ mutex_unlock(&profile_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(platform_profile_remove);
+
+MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/pmic/Kconfig b/drivers/acpi/pmic/Kconfig
new file mode 100644
index 000000000..f84b8f603
--- /dev/null
+++ b/drivers/acpi/pmic/Kconfig
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menuconfig PMIC_OPREGION
+ bool "PMIC (Power Management Integrated Circuit) operation region support"
+ help
+ Select this option to enable support for ACPI operation
+ region of the PMIC chip. The operation region can be used
+ to control power rails and sensor reading/writing on the
+ PMIC chip.
+
+if PMIC_OPREGION
+
+config BYTCRC_PMIC_OPREGION
+ bool "ACPI operation region support for Bay Trail Crystal Cove PMIC"
+ depends on INTEL_SOC_PMIC
+ help
+ This config adds ACPI operation region support for the Bay Trail
+ version of the Crystal Cove PMIC.
+
+config CHTCRC_PMIC_OPREGION
+ bool "ACPI operation region support for Cherry Trail Crystal Cove PMIC"
+ depends on INTEL_SOC_PMIC
+ help
+ This config adds ACPI operation region support for the Cherry Trail
+ version of the Crystal Cove PMIC.
+
+config XPOWER_PMIC_OPREGION
+ bool "ACPI operation region support for XPower AXP288 PMIC"
+ depends on MFD_AXP20X_I2C && IOSF_MBI=y
+ help
+ This config adds ACPI operation region support for XPower AXP288 PMIC.
+
+config BXT_WC_PMIC_OPREGION
+ bool "ACPI operation region support for BXT WhiskeyCove PMIC"
+ depends on INTEL_SOC_PMIC_BXTWC
+ help
+ This config adds ACPI operation region support for BXT WhiskeyCove PMIC.
+
+config CHT_WC_PMIC_OPREGION
+ bool "ACPI operation region support for CHT Whiskey Cove PMIC"
+ depends on INTEL_SOC_PMIC_CHTWC
+ help
+ This config adds ACPI operation region support for CHT Whiskey Cove PMIC.
+
+config CHT_DC_TI_PMIC_OPREGION
+ bool "ACPI operation region support for Dollar Cove TI PMIC"
+ depends on INTEL_SOC_PMIC_CHTDC_TI
+ help
+ This config adds ACPI operation region support for Dollar Cove TI PMIC.
+
+endif # PMIC_OPREGION
+
+config TPS68470_PMIC_OPREGION
+ bool "ACPI operation region support for TPS68470 PMIC"
+ depends on INTEL_SKL_INT3472
+ help
+ This config adds ACPI operation region support for TI TPS68470 PMIC.
+ TPS68470 device is an advanced power management unit that powers
+ a Compact Camera Module (CCM), generates clocks for image sensors,
+ drives a dual LED for flash and incorporates two LED drivers for
+ general purpose indicators.
+ This driver enables ACPI operation region support control voltage
+ regulators and clocks.
+
+ This option is a bool as it provides an ACPI operation
+ region, which must be available before any of the devices
+ using this, are probed.
diff --git a/drivers/acpi/pmic/Makefile b/drivers/acpi/pmic/Makefile
new file mode 100644
index 000000000..cd072c649
--- /dev/null
+++ b/drivers/acpi/pmic/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_PMIC_OPREGION) += intel_pmic.o
+obj-$(CONFIG_BYTCRC_PMIC_OPREGION) += intel_pmic_bytcrc.o
+obj-$(CONFIG_CHTCRC_PMIC_OPREGION) += intel_pmic_chtcrc.o
+obj-$(CONFIG_XPOWER_PMIC_OPREGION) += intel_pmic_xpower.o
+obj-$(CONFIG_BXT_WC_PMIC_OPREGION) += intel_pmic_bxtwc.o
+obj-$(CONFIG_CHT_WC_PMIC_OPREGION) += intel_pmic_chtwc.o
+obj-$(CONFIG_CHT_DC_TI_PMIC_OPREGION) += intel_pmic_chtdc_ti.o
+obj-$(CONFIG_TPS68470_PMIC_OPREGION) += tps68470_pmic.o
diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c
new file mode 100644
index 000000000..f20dbda1a
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic.c
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * intel_pmic.c - Intel PMIC operation region driver
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/export.h>
+#include <linux/acpi.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/regmap.h>
+#include <acpi/acpi_lpat.h>
+#include "intel_pmic.h"
+
+#define PMIC_POWER_OPREGION_ID 0x8d
+#define PMIC_THERMAL_OPREGION_ID 0x8c
+#define PMIC_REGS_OPREGION_ID 0x8f
+
+struct intel_pmic_regs_handler_ctx {
+ unsigned int val;
+ u16 addr;
+};
+
+struct intel_pmic_opregion {
+ struct mutex lock;
+ struct acpi_lpat_conversion_table *lpat_table;
+ struct regmap *regmap;
+ const struct intel_pmic_opregion_data *data;
+ struct intel_pmic_regs_handler_ctx ctx;
+};
+
+static struct intel_pmic_opregion *intel_pmic_opregion;
+
+static int pmic_get_reg_bit(int address, struct pmic_table *table,
+ int count, int *reg, int *bit)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (table[i].address == address) {
+ *reg = table[i].reg;
+ if (bit)
+ *bit = table[i].bit;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+static acpi_status intel_pmic_power_handler(u32 function,
+ acpi_physical_address address, u32 bits, u64 *value64,
+ void *handler_context, void *region_context)
+{
+ struct intel_pmic_opregion *opregion = region_context;
+ struct regmap *regmap = opregion->regmap;
+ const struct intel_pmic_opregion_data *d = opregion->data;
+ int reg, bit, result;
+
+ if (bits != 32 || !value64)
+ return AE_BAD_PARAMETER;
+
+ if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1))
+ return AE_BAD_PARAMETER;
+
+ result = pmic_get_reg_bit(address, d->power_table,
+ d->power_table_count, &reg, &bit);
+ if (result == -ENOENT)
+ return AE_BAD_PARAMETER;
+
+ mutex_lock(&opregion->lock);
+
+ result = function == ACPI_READ ?
+ d->get_power(regmap, reg, bit, value64) :
+ d->update_power(regmap, reg, bit, *value64 == 1);
+
+ mutex_unlock(&opregion->lock);
+
+ return result ? AE_ERROR : AE_OK;
+}
+
+static int pmic_read_temp(struct intel_pmic_opregion *opregion,
+ int reg, u64 *value)
+{
+ int raw_temp, temp;
+
+ if (!opregion->data->get_raw_temp)
+ return -ENXIO;
+
+ raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg);
+ if (raw_temp < 0)
+ return raw_temp;
+
+ if (!opregion->lpat_table) {
+ *value = raw_temp;
+ return 0;
+ }
+
+ temp = opregion->data->lpat_raw_to_temp(opregion->lpat_table, raw_temp);
+ if (temp < 0)
+ return temp;
+
+ *value = temp;
+ return 0;
+}
+
+static int pmic_thermal_temp(struct intel_pmic_opregion *opregion, int reg,
+ u32 function, u64 *value)
+{
+ return function == ACPI_READ ?
+ pmic_read_temp(opregion, reg, value) : -EINVAL;
+}
+
+static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg,
+ u32 function, u64 *value)
+{
+ int raw_temp;
+
+ if (function == ACPI_READ)
+ return pmic_read_temp(opregion, reg, value);
+
+ if (!opregion->data->update_aux)
+ return -ENXIO;
+
+ if (opregion->lpat_table) {
+ raw_temp = acpi_lpat_temp_to_raw(opregion->lpat_table, *value);
+ if (raw_temp < 0)
+ return raw_temp;
+ } else {
+ raw_temp = *value;
+ }
+
+ return opregion->data->update_aux(opregion->regmap, reg, raw_temp);
+}
+
+static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg,
+ int bit, u32 function, u64 *value)
+{
+ const struct intel_pmic_opregion_data *d = opregion->data;
+ struct regmap *regmap = opregion->regmap;
+
+ if (!d->get_policy || !d->update_policy)
+ return -ENXIO;
+
+ if (function == ACPI_READ)
+ return d->get_policy(regmap, reg, bit, value);
+
+ if (*value != 0 && *value != 1)
+ return -EINVAL;
+
+ return d->update_policy(regmap, reg, bit, *value);
+}
+
+static bool pmic_thermal_is_temp(int address)
+{
+ return (address <= 0x3c) && !(address % 12);
+}
+
+static bool pmic_thermal_is_aux(int address)
+{
+ return (address >= 4 && address <= 0x40 && !((address - 4) % 12)) ||
+ (address >= 8 && address <= 0x44 && !((address - 8) % 12));
+}
+
+static bool pmic_thermal_is_pen(int address)
+{
+ return address >= 0x48 && address <= 0x5c;
+}
+
+static acpi_status intel_pmic_thermal_handler(u32 function,
+ acpi_physical_address address, u32 bits, u64 *value64,
+ void *handler_context, void *region_context)
+{
+ struct intel_pmic_opregion *opregion = region_context;
+ const struct intel_pmic_opregion_data *d = opregion->data;
+ int reg, bit, result;
+
+ if (bits != 32 || !value64)
+ return AE_BAD_PARAMETER;
+
+ result = pmic_get_reg_bit(address, d->thermal_table,
+ d->thermal_table_count, &reg, &bit);
+ if (result == -ENOENT)
+ return AE_BAD_PARAMETER;
+
+ mutex_lock(&opregion->lock);
+
+ if (pmic_thermal_is_temp(address))
+ result = pmic_thermal_temp(opregion, reg, function, value64);
+ else if (pmic_thermal_is_aux(address))
+ result = pmic_thermal_aux(opregion, reg, function, value64);
+ else if (pmic_thermal_is_pen(address))
+ result = pmic_thermal_pen(opregion, reg, bit,
+ function, value64);
+ else
+ result = -EINVAL;
+
+ mutex_unlock(&opregion->lock);
+
+ if (result < 0) {
+ if (result == -EINVAL)
+ return AE_BAD_PARAMETER;
+ else
+ return AE_ERROR;
+ }
+
+ return AE_OK;
+}
+
+static acpi_status intel_pmic_regs_handler(u32 function,
+ acpi_physical_address address, u32 bits, u64 *value64,
+ void *handler_context, void *region_context)
+{
+ struct intel_pmic_opregion *opregion = region_context;
+ int result = -EINVAL;
+
+ if (function == ACPI_WRITE) {
+ switch (address) {
+ case 0:
+ return AE_OK;
+ case 1:
+ opregion->ctx.addr |= (*value64 & 0xff) << 8;
+ return AE_OK;
+ case 2:
+ opregion->ctx.addr |= *value64 & 0xff;
+ return AE_OK;
+ case 3:
+ opregion->ctx.val = *value64 & 0xff;
+ return AE_OK;
+ case 4:
+ if (*value64) {
+ result = regmap_write(opregion->regmap, opregion->ctx.addr,
+ opregion->ctx.val);
+ } else {
+ result = regmap_read(opregion->regmap, opregion->ctx.addr,
+ &opregion->ctx.val);
+ }
+ opregion->ctx.addr = 0;
+ }
+ }
+
+ if (function == ACPI_READ && address == 3) {
+ *value64 = opregion->ctx.val;
+ return AE_OK;
+ }
+
+ if (result < 0) {
+ if (result == -EINVAL)
+ return AE_BAD_PARAMETER;
+ else
+ return AE_ERROR;
+ }
+
+ return AE_OK;
+}
+
+int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,
+ struct regmap *regmap,
+ const struct intel_pmic_opregion_data *d)
+{
+ acpi_status status = AE_OK;
+ struct intel_pmic_opregion *opregion;
+ int ret;
+
+ if (!dev || !regmap || !d)
+ return -EINVAL;
+
+ if (!handle)
+ return -ENODEV;
+
+ opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL);
+ if (!opregion)
+ return -ENOMEM;
+
+ mutex_init(&opregion->lock);
+ opregion->regmap = regmap;
+ opregion->lpat_table = acpi_lpat_get_conversion_table(handle);
+
+ if (d->power_table_count)
+ status = acpi_install_address_space_handler(handle,
+ PMIC_POWER_OPREGION_ID,
+ intel_pmic_power_handler,
+ NULL, opregion);
+ if (ACPI_FAILURE(status)) {
+ ret = -ENODEV;
+ goto out_error;
+ }
+
+ if (d->thermal_table_count)
+ status = acpi_install_address_space_handler(handle,
+ PMIC_THERMAL_OPREGION_ID,
+ intel_pmic_thermal_handler,
+ NULL, opregion);
+ if (ACPI_FAILURE(status)) {
+ ret = -ENODEV;
+ goto out_remove_power_handler;
+ }
+
+ status = acpi_install_address_space_handler(handle,
+ PMIC_REGS_OPREGION_ID, intel_pmic_regs_handler, NULL,
+ opregion);
+ if (ACPI_FAILURE(status)) {
+ ret = -ENODEV;
+ goto out_remove_thermal_handler;
+ }
+
+ opregion->data = d;
+ intel_pmic_opregion = opregion;
+ return 0;
+
+out_remove_thermal_handler:
+ if (d->thermal_table_count)
+ acpi_remove_address_space_handler(handle,
+ PMIC_THERMAL_OPREGION_ID,
+ intel_pmic_thermal_handler);
+
+out_remove_power_handler:
+ if (d->power_table_count)
+ acpi_remove_address_space_handler(handle,
+ PMIC_POWER_OPREGION_ID,
+ intel_pmic_power_handler);
+
+out_error:
+ acpi_lpat_free_conversion_table(opregion->lpat_table);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler);
+
+/**
+ * intel_soc_pmic_exec_mipi_pmic_seq_element - Execute PMIC MIPI sequence
+ * @i2c_address: I2C client address for the PMIC
+ * @reg_address: PMIC register address
+ * @value: New value for the register bits to change
+ * @mask: Mask indicating which register bits to change
+ *
+ * DSI LCD panels describe an initialization sequence in the i915 VBT (Video
+ * BIOS Tables) using so called MIPI sequences. One possible element in these
+ * sequences is a PMIC specific element of 15 bytes.
+ *
+ * This function executes these PMIC specific elements sending the embedded
+ * commands to the PMIC.
+ *
+ * Return 0 on success, < 0 on failure.
+ */
+int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address,
+ u32 value, u32 mask)
+{
+ const struct intel_pmic_opregion_data *d;
+ int ret;
+
+ if (!intel_pmic_opregion) {
+ pr_warn("%s: No PMIC registered\n", __func__);
+ return -ENXIO;
+ }
+
+ d = intel_pmic_opregion->data;
+
+ mutex_lock(&intel_pmic_opregion->lock);
+
+ if (d->exec_mipi_pmic_seq_element) {
+ ret = d->exec_mipi_pmic_seq_element(intel_pmic_opregion->regmap,
+ i2c_address, reg_address,
+ value, mask);
+ } else if (d->pmic_i2c_address) {
+ if (i2c_address == d->pmic_i2c_address) {
+ ret = regmap_update_bits(intel_pmic_opregion->regmap,
+ reg_address, mask, value);
+ } else {
+ 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);
+ ret = -ENXIO;
+ }
+ } else {
+ pr_warn("%s: Not implemented\n", __func__);
+ pr_warn("%s: i2c-addr: 0x%x reg-addr 0x%x value 0x%x mask 0x%x\n",
+ __func__, i2c_address, reg_address, value, mask);
+ ret = -EOPNOTSUPP;
+ }
+
+ mutex_unlock(&intel_pmic_opregion->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(intel_soc_pmic_exec_mipi_pmic_seq_element);
diff --git a/drivers/acpi/pmic/intel_pmic.h b/drivers/acpi/pmic/intel_pmic.h
new file mode 100644
index 000000000..d956b03a6
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __INTEL_PMIC_H
+#define __INTEL_PMIC_H
+
+#include <acpi/acpi_lpat.h>
+
+struct pmic_table {
+ int address; /* operation region address */
+ int reg; /* corresponding thermal register */
+ int bit; /* control bit for power */
+};
+
+struct intel_pmic_opregion_data {
+ int (*get_power)(struct regmap *r, int reg, int bit, u64 *value);
+ int (*update_power)(struct regmap *r, int reg, int bit, bool on);
+ int (*get_raw_temp)(struct regmap *r, int reg);
+ int (*update_aux)(struct regmap *r, int reg, int raw_temp);
+ int (*get_policy)(struct regmap *r, int reg, int bit, u64 *value);
+ int (*update_policy)(struct regmap *r, int reg, int bit, int enable);
+ int (*exec_mipi_pmic_seq_element)(struct regmap *r, u16 i2c_address,
+ 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;
+ int power_table_count;
+ struct pmic_table *thermal_table;
+ int thermal_table_count;
+ /* For generic exec_mipi_pmic_seq_element handling */
+ int pmic_i2c_address;
+};
+
+int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,
+ struct regmap *regmap,
+ const struct intel_pmic_opregion_data *d);
+
+#endif
diff --git a/drivers/acpi/pmic/intel_pmic_bxtwc.c b/drivers/acpi/pmic/intel_pmic_bxtwc.c
new file mode 100644
index 000000000..e24761518
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_bxtwc.c
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel BXT WhiskeyCove PMIC operation region driver
+ *
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include "intel_pmic.h"
+
+#define WHISKEY_COVE_ALRT_HIGH_BIT_MASK 0x0F
+#define WHISKEY_COVE_ADC_HIGH_BIT(x) (((x & 0x0F) << 8))
+#define WHISKEY_COVE_ADC_CURSRC(x) (((x & 0xF0) >> 4))
+#define VR_MODE_DISABLED 0
+#define VR_MODE_AUTO BIT(0)
+#define VR_MODE_NORMAL BIT(1)
+#define VR_MODE_SWITCH BIT(2)
+#define VR_MODE_ECO (BIT(0)|BIT(1))
+#define VSWITCH2_OUTPUT BIT(5)
+#define VSWITCH1_OUTPUT BIT(4)
+#define VUSBPHY_CHARGE BIT(1)
+
+static struct pmic_table power_table[] = {
+ {
+ .address = 0x0,
+ .reg = 0x63,
+ .bit = VR_MODE_AUTO,
+ }, /* VDD1 -> VDD1CNT */
+ {
+ .address = 0x04,
+ .reg = 0x65,
+ .bit = VR_MODE_AUTO,
+ }, /* VDD2 -> VDD2CNT */
+ {
+ .address = 0x08,
+ .reg = 0x67,
+ .bit = VR_MODE_AUTO,
+ }, /* VDD3 -> VDD3CNT */
+ {
+ .address = 0x0c,
+ .reg = 0x6d,
+ .bit = VR_MODE_AUTO,
+ }, /* VLFX -> VFLEXCNT */
+ {
+ .address = 0x10,
+ .reg = 0x6f,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP1A -> VPROG1ACNT */
+ {
+ .address = 0x14,
+ .reg = 0x70,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP1B -> VPROG1BCNT */
+ {
+ .address = 0x18,
+ .reg = 0x71,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP1C -> VPROG1CCNT */
+ {
+ .address = 0x1c,
+ .reg = 0x72,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP1D -> VPROG1DCNT */
+ {
+ .address = 0x20,
+ .reg = 0x73,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP2A -> VPROG2ACNT */
+ {
+ .address = 0x24,
+ .reg = 0x74,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP2B -> VPROG2BCNT */
+ {
+ .address = 0x28,
+ .reg = 0x75,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP2C -> VPROG2CCNT */
+ {
+ .address = 0x2c,
+ .reg = 0x76,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP3A -> VPROG3ACNT */
+ {
+ .address = 0x30,
+ .reg = 0x77,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP3B -> VPROG3BCNT */
+ {
+ .address = 0x34,
+ .reg = 0x78,
+ .bit = VSWITCH2_OUTPUT,
+ }, /* VSW2 -> VLD0CNT Bit 5*/
+ {
+ .address = 0x38,
+ .reg = 0x78,
+ .bit = VSWITCH1_OUTPUT,
+ }, /* VSW1 -> VLD0CNT Bit 4 */
+ {
+ .address = 0x3c,
+ .reg = 0x78,
+ .bit = VUSBPHY_CHARGE,
+ }, /* VUPY -> VLDOCNT Bit 1 */
+ {
+ .address = 0x40,
+ .reg = 0x7b,
+ .bit = VR_MODE_NORMAL,
+ }, /* VRSO -> VREFSOCCNT*/
+ {
+ .address = 0x44,
+ .reg = 0xA0,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP1E -> VPROG1ECNT */
+ {
+ .address = 0x48,
+ .reg = 0xA1,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP1F -> VPROG1FCNT */
+ {
+ .address = 0x4c,
+ .reg = 0xA2,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP2D -> VPROG2DCNT */
+ {
+ .address = 0x50,
+ .reg = 0xA3,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP4A -> VPROG4ACNT */
+ {
+ .address = 0x54,
+ .reg = 0xA4,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP4B -> VPROG4BCNT */
+ {
+ .address = 0x58,
+ .reg = 0xA5,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP4C -> VPROG4CCNT */
+ {
+ .address = 0x5c,
+ .reg = 0xA6,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP4D -> VPROG4DCNT */
+ {
+ .address = 0x60,
+ .reg = 0xA7,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP5A -> VPROG5ACNT */
+ {
+ .address = 0x64,
+ .reg = 0xA8,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP5B -> VPROG5BCNT */
+ {
+ .address = 0x68,
+ .reg = 0xA9,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP6A -> VPROG6ACNT */
+ {
+ .address = 0x6c,
+ .reg = 0xAA,
+ .bit = VR_MODE_NORMAL,
+ }, /* VP6B -> VPROG6BCNT */
+ {
+ .address = 0x70,
+ .reg = 0x36,
+ .bit = BIT(2),
+ }, /* SDWN_N -> MODEMCTRL Bit 2 */
+ {
+ .address = 0x74,
+ .reg = 0x36,
+ .bit = BIT(0),
+ } /* MOFF -> MODEMCTRL Bit 0 */
+};
+
+static struct pmic_table thermal_table[] = {
+ {
+ .address = 0x00,
+ .reg = 0x4F39
+ },
+ {
+ .address = 0x04,
+ .reg = 0x4F24
+ },
+ {
+ .address = 0x08,
+ .reg = 0x4F26
+ },
+ {
+ .address = 0x0c,
+ .reg = 0x4F3B
+ },
+ {
+ .address = 0x10,
+ .reg = 0x4F28
+ },
+ {
+ .address = 0x14,
+ .reg = 0x4F2A
+ },
+ {
+ .address = 0x18,
+ .reg = 0x4F3D
+ },
+ {
+ .address = 0x1c,
+ .reg = 0x4F2C
+ },
+ {
+ .address = 0x20,
+ .reg = 0x4F2E
+ },
+ {
+ .address = 0x24,
+ .reg = 0x4F3F
+ },
+ {
+ .address = 0x28,
+ .reg = 0x4F30
+ },
+ {
+ .address = 0x30,
+ .reg = 0x4F41
+ },
+ {
+ .address = 0x34,
+ .reg = 0x4F32
+ },
+ {
+ .address = 0x3c,
+ .reg = 0x4F43
+ },
+ {
+ .address = 0x40,
+ .reg = 0x4F34
+ },
+ {
+ .address = 0x48,
+ .reg = 0x4F6A,
+ .bit = 0,
+ },
+ {
+ .address = 0x4C,
+ .reg = 0x4F6A,
+ .bit = 1
+ },
+ {
+ .address = 0x50,
+ .reg = 0x4F6A,
+ .bit = 2
+ },
+ {
+ .address = 0x54,
+ .reg = 0x4F6A,
+ .bit = 4
+ },
+ {
+ .address = 0x58,
+ .reg = 0x4F6A,
+ .bit = 5
+ },
+ {
+ .address = 0x5C,
+ .reg = 0x4F6A,
+ .bit = 3
+ },
+};
+
+static int intel_bxtwc_pmic_get_power(struct regmap *regmap, int reg,
+ int bit, u64 *value)
+{
+ int data;
+
+ if (regmap_read(regmap, reg, &data))
+ return -EIO;
+
+ *value = (data & bit) ? 1 : 0;
+ return 0;
+}
+
+static int intel_bxtwc_pmic_update_power(struct regmap *regmap, int reg,
+ int bit, bool on)
+{
+ u8 val, mask = bit;
+
+ if (on)
+ val = 0xFF;
+ else
+ val = 0x0;
+
+ return regmap_update_bits(regmap, reg, mask, val);
+}
+
+static int intel_bxtwc_pmic_get_raw_temp(struct regmap *regmap, int reg)
+{
+ unsigned int val, adc_val, reg_val;
+ u8 temp_l, temp_h, cursrc;
+ unsigned long rlsb;
+ static const unsigned long rlsb_array[] = {
+ 0, 260420, 130210, 65100, 32550, 16280,
+ 8140, 4070, 2030, 0, 260420, 130210 };
+
+ if (regmap_read(regmap, reg, &val))
+ return -EIO;
+ temp_l = (u8) val;
+
+ if (regmap_read(regmap, (reg - 1), &val))
+ return -EIO;
+ temp_h = (u8) val;
+
+ reg_val = temp_l | WHISKEY_COVE_ADC_HIGH_BIT(temp_h);
+ cursrc = WHISKEY_COVE_ADC_CURSRC(temp_h);
+ rlsb = rlsb_array[cursrc];
+ adc_val = reg_val * rlsb / 1000;
+
+ return adc_val;
+}
+
+static int
+intel_bxtwc_pmic_update_aux(struct regmap *regmap, int reg, int raw)
+{
+ u32 bsr_num;
+ u16 resi_val, count = 0, thrsh = 0;
+ u8 alrt_h, alrt_l, cursel = 0;
+
+ bsr_num = raw;
+ bsr_num /= (1 << 5);
+
+ count = fls(bsr_num) - 1;
+
+ cursel = clamp_t(s8, (count - 7), 0, 7);
+ thrsh = raw / (1 << (4 + cursel));
+
+ resi_val = (cursel << 9) | thrsh;
+ alrt_h = (resi_val >> 8) & WHISKEY_COVE_ALRT_HIGH_BIT_MASK;
+ if (regmap_update_bits(regmap,
+ reg - 1,
+ WHISKEY_COVE_ALRT_HIGH_BIT_MASK,
+ alrt_h))
+ return -EIO;
+
+ alrt_l = (u8)resi_val;
+ return regmap_write(regmap, reg, alrt_l);
+}
+
+static int
+intel_bxtwc_pmic_get_policy(struct regmap *regmap, int reg, int bit, u64 *value)
+{
+ u8 mask = BIT(bit);
+ unsigned int val;
+
+ if (regmap_read(regmap, reg, &val))
+ return -EIO;
+
+ *value = (val & mask) >> bit;
+ return 0;
+}
+
+static int
+intel_bxtwc_pmic_update_policy(struct regmap *regmap,
+ int reg, int bit, int enable)
+{
+ u8 mask = BIT(bit), val = enable << bit;
+
+ return regmap_update_bits(regmap, reg, mask, val);
+}
+
+static const struct intel_pmic_opregion_data intel_bxtwc_pmic_opregion_data = {
+ .get_power = intel_bxtwc_pmic_get_power,
+ .update_power = intel_bxtwc_pmic_update_power,
+ .get_raw_temp = intel_bxtwc_pmic_get_raw_temp,
+ .update_aux = intel_bxtwc_pmic_update_aux,
+ .get_policy = intel_bxtwc_pmic_get_policy,
+ .update_policy = intel_bxtwc_pmic_update_policy,
+ .lpat_raw_to_temp = acpi_lpat_raw_to_temp,
+ .power_table = power_table,
+ .power_table_count = ARRAY_SIZE(power_table),
+ .thermal_table = thermal_table,
+ .thermal_table_count = ARRAY_SIZE(thermal_table),
+};
+
+static int intel_bxtwc_pmic_opregion_probe(struct platform_device *pdev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
+
+ return intel_pmic_install_opregion_handler(&pdev->dev,
+ ACPI_HANDLE(pdev->dev.parent),
+ pmic->regmap,
+ &intel_bxtwc_pmic_opregion_data);
+}
+
+static const struct platform_device_id bxt_wc_opregion_id_table[] = {
+ { .name = "bxt_wcove_region" },
+ {},
+};
+
+static struct platform_driver intel_bxtwc_pmic_opregion_driver = {
+ .probe = intel_bxtwc_pmic_opregion_probe,
+ .driver = {
+ .name = "bxt_whiskey_cove_pmic",
+ },
+ .id_table = bxt_wc_opregion_id_table,
+};
+builtin_platform_driver(intel_bxtwc_pmic_opregion_driver);
diff --git a/drivers/acpi/pmic/intel_pmic_bytcrc.c b/drivers/acpi/pmic/intel_pmic_bytcrc.c
new file mode 100644
index 000000000..9ea79f210
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_bytcrc.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Bay Trail Crystal Cove PMIC operation region driver
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include "intel_pmic.h"
+
+#define PWR_SOURCE_SELECT BIT(1)
+
+#define PMIC_A0LOCK_REG 0xc5
+
+static struct pmic_table power_table[] = {
+/* {
+ .address = 0x00,
+ .reg = ??,
+ .bit = ??,
+ }, ** VSYS */
+ {
+ .address = 0x04,
+ .reg = 0x63,
+ .bit = 0x00,
+ }, /* SYSX -> VSYS_SX */
+ {
+ .address = 0x08,
+ .reg = 0x62,
+ .bit = 0x00,
+ }, /* SYSU -> VSYS_U */
+ {
+ .address = 0x0c,
+ .reg = 0x64,
+ .bit = 0x00,
+ }, /* SYSS -> VSYS_S */
+ {
+ .address = 0x10,
+ .reg = 0x6a,
+ .bit = 0x00,
+ }, /* V50S -> V5P0S */
+ {
+ .address = 0x14,
+ .reg = 0x6b,
+ .bit = 0x00,
+ }, /* HOST -> VHOST, USB2/3 host */
+ {
+ .address = 0x18,
+ .reg = 0x6c,
+ .bit = 0x00,
+ }, /* VBUS -> VBUS, USB2/3 OTG */
+ {
+ .address = 0x1c,
+ .reg = 0x6d,
+ .bit = 0x00,
+ }, /* HDMI -> VHDMI */
+/* {
+ .address = 0x20,
+ .reg = ??,
+ .bit = ??,
+ }, ** S285 */
+ {
+ .address = 0x24,
+ .reg = 0x66,
+ .bit = 0x00,
+ }, /* X285 -> V2P85SX, camera */
+/* {
+ .address = 0x28,
+ .reg = ??,
+ .bit = ??,
+ }, ** V33A */
+ {
+ .address = 0x2c,
+ .reg = 0x69,
+ .bit = 0x00,
+ }, /* V33S -> V3P3S, display/ssd/audio */
+ {
+ .address = 0x30,
+ .reg = 0x68,
+ .bit = 0x00,
+ }, /* V33U -> V3P3U, SDIO wifi&bt */
+/* {
+ .address = 0x34 .. 0x40,
+ .reg = ??,
+ .bit = ??,
+ }, ** V33I, V18A, REFQ, V12A */
+ {
+ .address = 0x44,
+ .reg = 0x5c,
+ .bit = 0x00,
+ }, /* V18S -> V1P8S, SOC/USB PHY/SIM */
+ {
+ .address = 0x48,
+ .reg = 0x5d,
+ .bit = 0x00,
+ }, /* V18X -> V1P8SX, eMMC/camara/audio */
+ {
+ .address = 0x4c,
+ .reg = 0x5b,
+ .bit = 0x00,
+ }, /* V18U -> V1P8U, LPDDR */
+ {
+ .address = 0x50,
+ .reg = 0x61,
+ .bit = 0x00,
+ }, /* V12X -> V1P2SX, SOC SFR */
+ {
+ .address = 0x54,
+ .reg = 0x60,
+ .bit = 0x00,
+ }, /* V12S -> V1P2S, MIPI */
+/* {
+ .address = 0x58,
+ .reg = ??,
+ .bit = ??,
+ }, ** V10A */
+ {
+ .address = 0x5c,
+ .reg = 0x56,
+ .bit = 0x00,
+ }, /* V10S -> V1P0S, SOC GFX */
+ {
+ .address = 0x60,
+ .reg = 0x57,
+ .bit = 0x00,
+ }, /* V10X -> V1P0SX, SOC display/DDR IO/PCIe */
+ {
+ .address = 0x64,
+ .reg = 0x59,
+ .bit = 0x00,
+ }, /* V105 -> V1P05S, L2 SRAM */
+};
+
+static struct pmic_table thermal_table[] = {
+ {
+ .address = 0x00,
+ .reg = 0x75
+ },
+ {
+ .address = 0x04,
+ .reg = 0x95
+ },
+ {
+ .address = 0x08,
+ .reg = 0x97
+ },
+ {
+ .address = 0x0c,
+ .reg = 0x77
+ },
+ {
+ .address = 0x10,
+ .reg = 0x9a
+ },
+ {
+ .address = 0x14,
+ .reg = 0x9c
+ },
+ {
+ .address = 0x18,
+ .reg = 0x79
+ },
+ {
+ .address = 0x1c,
+ .reg = 0x9f
+ },
+ {
+ .address = 0x20,
+ .reg = 0xa1
+ },
+ {
+ .address = 0x48,
+ .reg = 0x94
+ },
+ {
+ .address = 0x4c,
+ .reg = 0x99
+ },
+ {
+ .address = 0x50,
+ .reg = 0x9e
+ },
+};
+
+static int intel_crc_pmic_get_power(struct regmap *regmap, int reg,
+ int bit, u64 *value)
+{
+ int data;
+
+ if (regmap_read(regmap, reg, &data))
+ return -EIO;
+
+ *value = (data & PWR_SOURCE_SELECT) && (data & BIT(bit)) ? 1 : 0;
+ return 0;
+}
+
+static int intel_crc_pmic_update_power(struct regmap *regmap, int reg,
+ int bit, bool on)
+{
+ int data;
+
+ if (regmap_read(regmap, reg, &data))
+ return -EIO;
+
+ if (on) {
+ data |= PWR_SOURCE_SELECT | BIT(bit);
+ } else {
+ data &= ~BIT(bit);
+ data |= PWR_SOURCE_SELECT;
+ }
+
+ if (regmap_write(regmap, reg, data))
+ return -EIO;
+ return 0;
+}
+
+static int intel_crc_pmic_get_raw_temp(struct regmap *regmap, int reg)
+{
+ int temp_l, temp_h;
+
+ /*
+ * Raw temperature value is 10bits: 8bits in reg
+ * and 2bits in reg-1: bit0,1
+ */
+ if (regmap_read(regmap, reg, &temp_l) ||
+ regmap_read(regmap, reg - 1, &temp_h))
+ return -EIO;
+
+ return temp_l | (temp_h & 0x3) << 8;
+}
+
+static int intel_crc_pmic_update_aux(struct regmap *regmap, int reg, int raw)
+{
+ return regmap_write(regmap, reg, raw) ||
+ regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8) ? -EIO : 0;
+}
+
+static int intel_crc_pmic_get_policy(struct regmap *regmap,
+ int reg, int bit, u64 *value)
+{
+ int pen;
+
+ if (regmap_read(regmap, reg, &pen))
+ return -EIO;
+ *value = pen >> 7;
+ return 0;
+}
+
+static int intel_crc_pmic_update_policy(struct regmap *regmap,
+ int reg, int bit, int enable)
+{
+ int alert0;
+
+ /* Update to policy enable bit requires unlocking a0lock */
+ if (regmap_read(regmap, PMIC_A0LOCK_REG, &alert0))
+ return -EIO;
+
+ if (regmap_update_bits(regmap, PMIC_A0LOCK_REG, 0x01, 0))
+ return -EIO;
+
+ if (regmap_update_bits(regmap, reg, 0x80, enable << 7))
+ return -EIO;
+
+ /* restore alert0 */
+ if (regmap_write(regmap, PMIC_A0LOCK_REG, alert0))
+ return -EIO;
+
+ return 0;
+}
+
+static const struct intel_pmic_opregion_data intel_crc_pmic_opregion_data = {
+ .get_power = intel_crc_pmic_get_power,
+ .update_power = intel_crc_pmic_update_power,
+ .get_raw_temp = intel_crc_pmic_get_raw_temp,
+ .update_aux = intel_crc_pmic_update_aux,
+ .get_policy = intel_crc_pmic_get_policy,
+ .update_policy = intel_crc_pmic_update_policy,
+ .lpat_raw_to_temp = acpi_lpat_raw_to_temp,
+ .power_table = power_table,
+ .power_table_count= ARRAY_SIZE(power_table),
+ .thermal_table = thermal_table,
+ .thermal_table_count = ARRAY_SIZE(thermal_table),
+};
+
+static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
+ return intel_pmic_install_opregion_handler(&pdev->dev,
+ ACPI_HANDLE(pdev->dev.parent), pmic->regmap,
+ &intel_crc_pmic_opregion_data);
+}
+
+static struct platform_driver intel_crc_pmic_opregion_driver = {
+ .probe = intel_crc_pmic_opregion_probe,
+ .driver = {
+ .name = "byt_crystal_cove_pmic",
+ },
+};
+builtin_platform_driver(intel_crc_pmic_opregion_driver);
diff --git a/drivers/acpi/pmic/intel_pmic_chtcrc.c b/drivers/acpi/pmic/intel_pmic_chtcrc.c
new file mode 100644
index 000000000..f9301c6f0
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_chtcrc.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Cherry Trail Crystal Cove PMIC operation region driver
+ *
+ * Copyright (C) 2019 Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include "intel_pmic.h"
+
+/*
+ * We have no docs for the CHT Crystal Cove PMIC. The Asus Zenfone-2 kernel
+ * code has 2 Crystal Cove regulator drivers, one calls the PMIC a "Crystal
+ * Cove Plus" PMIC and talks about Cherry Trail, so presumably that one
+ * could be used to get register info for the regulators if we need to
+ * implement regulator support in the future.
+ *
+ * For now the sole purpose of this driver is to make
+ * intel_soc_pmic_exec_mipi_pmic_seq_element work on devices with a
+ * CHT Crystal Cove PMIC.
+ */
+static const struct intel_pmic_opregion_data intel_chtcrc_pmic_opregion_data = {
+ .lpat_raw_to_temp = acpi_lpat_raw_to_temp,
+ .pmic_i2c_address = 0x6e,
+};
+
+static int intel_chtcrc_pmic_opregion_probe(struct platform_device *pdev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
+ return intel_pmic_install_opregion_handler(&pdev->dev,
+ ACPI_HANDLE(pdev->dev.parent), pmic->regmap,
+ &intel_chtcrc_pmic_opregion_data);
+}
+
+static struct platform_driver intel_chtcrc_pmic_opregion_driver = {
+ .probe = intel_chtcrc_pmic_opregion_probe,
+ .driver = {
+ .name = "cht_crystal_cove_pmic",
+ },
+};
+builtin_platform_driver(intel_chtcrc_pmic_opregion_driver);
diff --git a/drivers/acpi/pmic/intel_pmic_chtdc_ti.c b/drivers/acpi/pmic/intel_pmic_chtdc_ti.c
new file mode 100644
index 000000000..418eec523
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_chtdc_ti.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Dollar Cove TI PMIC operation region driver
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ * Rewritten and cleaned up
+ * Copyright (C) 2017 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/platform_device.h>
+#include "intel_pmic.h"
+
+/* registers stored in 16bit BE (high:low, total 10bit) */
+#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 struct pmic_table chtdc_ti_thermal_table[] = {
+ {
+ .address = 0x00,
+ .reg = CHTDC_TI_GPADC
+ },
+ {
+ .address = 0x0c,
+ .reg = CHTDC_TI_GPADC
+ },
+ /* TMP2 -> SYSTEMP */
+ {
+ .address = 0x18,
+ .reg = CHTDC_TI_GPADC
+ },
+ /* TMP3 -> BPTHERM */
+ {
+ .address = 0x24,
+ .reg = CHTDC_TI_BPTHERM
+ },
+ {
+ .address = 0x30,
+ .reg = CHTDC_TI_GPADC
+ },
+ /* TMP5 -> DIETEMP */
+ {
+ .address = 0x3c,
+ .reg = CHTDC_TI_DIETEMP
+ },
+};
+
+static int chtdc_ti_pmic_get_power(struct regmap *regmap, int reg, int bit,
+ u64 *value)
+{
+ int data;
+
+ if (regmap_read(regmap, reg, &data))
+ return -EIO;
+
+ *value = data & 1;
+ return 0;
+}
+
+static int chtdc_ti_pmic_update_power(struct regmap *regmap, int reg, int bit,
+ bool on)
+{
+ return regmap_update_bits(regmap, reg, 1, on);
+}
+
+static int chtdc_ti_pmic_get_raw_temp(struct regmap *regmap, int reg)
+{
+ u8 buf[2];
+
+ if (regmap_bulk_read(regmap, reg, buf, 2))
+ return -EIO;
+
+ /* stored in big-endian */
+ return ((buf[0] & 0x03) << 8) | buf[1];
+}
+
+static const struct intel_pmic_opregion_data chtdc_ti_pmic_opregion_data = {
+ .get_power = chtdc_ti_pmic_get_power,
+ .update_power = chtdc_ti_pmic_update_power,
+ .get_raw_temp = chtdc_ti_pmic_get_raw_temp,
+ .lpat_raw_to_temp = acpi_lpat_raw_to_temp,
+ .power_table = chtdc_ti_power_table,
+ .power_table_count = ARRAY_SIZE(chtdc_ti_power_table),
+ .thermal_table = chtdc_ti_thermal_table,
+ .thermal_table_count = ARRAY_SIZE(chtdc_ti_thermal_table),
+ .pmic_i2c_address = 0x5e,
+};
+
+static int chtdc_ti_pmic_opregion_probe(struct platform_device *pdev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
+ int err;
+
+ err = intel_pmic_install_opregion_handler(&pdev->dev,
+ ACPI_HANDLE(pdev->dev.parent), pmic->regmap,
+ &chtdc_ti_pmic_opregion_data);
+ if (err < 0)
+ return err;
+
+ /* Re-enumerate devices depending on PMIC */
+ acpi_dev_clear_dependencies(ACPI_COMPANION(pdev->dev.parent));
+ return 0;
+}
+
+static const struct platform_device_id chtdc_ti_pmic_opregion_id_table[] = {
+ { .name = "chtdc_ti_region" },
+ {},
+};
+
+static struct platform_driver chtdc_ti_pmic_opregion_driver = {
+ .probe = chtdc_ti_pmic_opregion_probe,
+ .driver = {
+ .name = "cht_dollar_cove_ti_pmic",
+ },
+ .id_table = chtdc_ti_pmic_opregion_id_table,
+};
+builtin_platform_driver(chtdc_ti_pmic_opregion_driver);
diff --git a/drivers/acpi/pmic/intel_pmic_chtwc.c b/drivers/acpi/pmic/intel_pmic_chtwc.c
new file mode 100644
index 000000000..f2c42f4c7
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_chtwc.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel CHT Whiskey Cove PMIC operation region driver
+ * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
+ * Copyright (C) 2013-2015 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include "intel_pmic.h"
+
+#define CHT_WC_V1P05A_CTRL 0x6e3b
+#define CHT_WC_V1P15_CTRL 0x6e3c
+#define CHT_WC_V1P05A_VSEL 0x6e3d
+#define CHT_WC_V1P15_VSEL 0x6e3e
+#define CHT_WC_V1P8A_CTRL 0x6e56
+#define CHT_WC_V1P8SX_CTRL 0x6e57
+#define CHT_WC_VDDQ_CTRL 0x6e58
+#define CHT_WC_V1P2A_CTRL 0x6e59
+#define CHT_WC_V1P2SX_CTRL 0x6e5a
+#define CHT_WC_V1P8A_VSEL 0x6e5b
+#define CHT_WC_VDDQ_VSEL 0x6e5c
+#define CHT_WC_V2P8SX_CTRL 0x6e5d
+#define CHT_WC_V3P3A_CTRL 0x6e5e
+#define CHT_WC_V3P3SD_CTRL 0x6e5f
+#define CHT_WC_VSDIO_CTRL 0x6e67
+#define CHT_WC_V3P3A_VSEL 0x6e68
+#define CHT_WC_VPROG1A_CTRL 0x6e90
+#define CHT_WC_VPROG1B_CTRL 0x6e91
+#define CHT_WC_VPROG1F_CTRL 0x6e95
+#define CHT_WC_VPROG2D_CTRL 0x6e99
+#define CHT_WC_VPROG3A_CTRL 0x6e9a
+#define CHT_WC_VPROG3B_CTRL 0x6e9b
+#define CHT_WC_VPROG4A_CTRL 0x6e9c
+#define CHT_WC_VPROG4B_CTRL 0x6e9d
+#define CHT_WC_VPROG4C_CTRL 0x6e9e
+#define CHT_WC_VPROG4D_CTRL 0x6e9f
+#define CHT_WC_VPROG5A_CTRL 0x6ea0
+#define CHT_WC_VPROG5B_CTRL 0x6ea1
+#define CHT_WC_VPROG6A_CTRL 0x6ea2
+#define CHT_WC_VPROG6B_CTRL 0x6ea3
+#define CHT_WC_VPROG1A_VSEL 0x6ec0
+#define CHT_WC_VPROG1B_VSEL 0x6ec1
+#define CHT_WC_V1P8SX_VSEL 0x6ec2
+#define CHT_WC_V1P2SX_VSEL 0x6ec3
+#define CHT_WC_V1P2A_VSEL 0x6ec4
+#define CHT_WC_VPROG1F_VSEL 0x6ec5
+#define CHT_WC_VSDIO_VSEL 0x6ec6
+#define CHT_WC_V2P8SX_VSEL 0x6ec7
+#define CHT_WC_V3P3SD_VSEL 0x6ec8
+#define CHT_WC_VPROG2D_VSEL 0x6ec9
+#define CHT_WC_VPROG3A_VSEL 0x6eca
+#define CHT_WC_VPROG3B_VSEL 0x6ecb
+#define CHT_WC_VPROG4A_VSEL 0x6ecc
+#define CHT_WC_VPROG4B_VSEL 0x6ecd
+#define CHT_WC_VPROG4C_VSEL 0x6ece
+#define CHT_WC_VPROG4D_VSEL 0x6ecf
+#define CHT_WC_VPROG5A_VSEL 0x6ed0
+#define CHT_WC_VPROG5B_VSEL 0x6ed1
+#define CHT_WC_VPROG6A_VSEL 0x6ed2
+#define CHT_WC_VPROG6B_VSEL 0x6ed3
+
+/*
+ * Regulator support is based on the non upstream patch:
+ * "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[] = {
+ {
+ .address = 0x0,
+ .reg = CHT_WC_V1P8A_CTRL,
+ .bit = 0x01,
+ }, /* V18A */
+ {
+ .address = 0x04,
+ .reg = CHT_WC_V1P8SX_CTRL,
+ .bit = 0x07,
+ }, /* V18X */
+ {
+ .address = 0x08,
+ .reg = CHT_WC_VDDQ_CTRL,
+ .bit = 0x01,
+ }, /* VDDQ */
+ {
+ .address = 0x0c,
+ .reg = CHT_WC_V1P2A_CTRL,
+ .bit = 0x07,
+ }, /* V12A */
+ {
+ .address = 0x10,
+ .reg = CHT_WC_V1P2SX_CTRL,
+ .bit = 0x07,
+ }, /* V12X */
+ {
+ .address = 0x14,
+ .reg = CHT_WC_V2P8SX_CTRL,
+ .bit = 0x07,
+ }, /* V28X */
+ {
+ .address = 0x18,
+ .reg = CHT_WC_V3P3A_CTRL,
+ .bit = 0x01,
+ }, /* V33A */
+ {
+ .address = 0x1c,
+ .reg = CHT_WC_V3P3SD_CTRL,
+ .bit = 0x07,
+ }, /* V3SD */
+ {
+ .address = 0x20,
+ .reg = CHT_WC_VSDIO_CTRL,
+ .bit = 0x07,
+ }, /* VSD */
+/* {
+ .address = 0x24,
+ .reg = ??,
+ .bit = ??,
+ }, ** VSW2 */
+/* {
+ .address = 0x28,
+ .reg = ??,
+ .bit = ??,
+ }, ** VSW1 */
+/* {
+ .address = 0x2c,
+ .reg = ??,
+ .bit = ??,
+ }, ** VUPY */
+/* {
+ .address = 0x30,
+ .reg = ??,
+ .bit = ??,
+ }, ** VRSO */
+ {
+ .address = 0x34,
+ .reg = CHT_WC_VPROG1A_CTRL,
+ .bit = 0x07,
+ }, /* VP1A */
+ {
+ .address = 0x38,
+ .reg = CHT_WC_VPROG1B_CTRL,
+ .bit = 0x07,
+ }, /* VP1B */
+ {
+ .address = 0x3c,
+ .reg = CHT_WC_VPROG1F_CTRL,
+ .bit = 0x07,
+ }, /* VP1F */
+ {
+ .address = 0x40,
+ .reg = CHT_WC_VPROG2D_CTRL,
+ .bit = 0x07,
+ }, /* VP2D */
+ {
+ .address = 0x44,
+ .reg = CHT_WC_VPROG3A_CTRL,
+ .bit = 0x07,
+ }, /* VP3A */
+ {
+ .address = 0x48,
+ .reg = CHT_WC_VPROG3B_CTRL,
+ .bit = 0x07,
+ }, /* VP3B */
+ {
+ .address = 0x4c,
+ .reg = CHT_WC_VPROG4A_CTRL,
+ .bit = 0x07,
+ }, /* VP4A */
+ {
+ .address = 0x50,
+ .reg = CHT_WC_VPROG4B_CTRL,
+ .bit = 0x07,
+ }, /* VP4B */
+ {
+ .address = 0x54,
+ .reg = CHT_WC_VPROG4C_CTRL,
+ .bit = 0x07,
+ }, /* VP4C */
+ {
+ .address = 0x58,
+ .reg = CHT_WC_VPROG4D_CTRL,
+ .bit = 0x07,
+ }, /* VP4D */
+ {
+ .address = 0x5c,
+ .reg = CHT_WC_VPROG5A_CTRL,
+ .bit = 0x07,
+ }, /* VP5A */
+ {
+ .address = 0x60,
+ .reg = CHT_WC_VPROG5B_CTRL,
+ .bit = 0x07,
+ }, /* VP5B */
+ {
+ .address = 0x64,
+ .reg = CHT_WC_VPROG6A_CTRL,
+ .bit = 0x07,
+ }, /* VP6A */
+ {
+ .address = 0x68,
+ .reg = CHT_WC_VPROG6B_CTRL,
+ .bit = 0x07,
+ }, /* VP6B */
+/* {
+ .address = 0x6c,
+ .reg = ??,
+ .bit = ??,
+ } ** VP7A */
+};
+
+static int intel_cht_wc_pmic_get_power(struct regmap *regmap, int reg,
+ int bit, u64 *value)
+{
+ int data;
+
+ if (regmap_read(regmap, reg, &data))
+ return -EIO;
+
+ *value = (data & bit) ? 1 : 0;
+ return 0;
+}
+
+static int intel_cht_wc_pmic_update_power(struct regmap *regmap, int reg,
+ int bitmask, bool on)
+{
+ return regmap_update_bits(regmap, reg, bitmask, on ? 1 : 0);
+}
+
+static int intel_cht_wc_exec_mipi_pmic_seq_element(struct regmap *regmap,
+ u16 i2c_client_address,
+ u32 reg_address,
+ u32 value, u32 mask)
+{
+ 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);
+ return -ERANGE;
+ }
+
+ address = (i2c_client_address << 8) | reg_address;
+
+ return regmap_update_bits(regmap, address, mask, value);
+}
+
+/*
+ * The thermal table and ops are empty, we do not support the Thermal opregion
+ * (DPTF) due to lacking documentation.
+ */
+static const struct intel_pmic_opregion_data intel_cht_wc_pmic_opregion_data = {
+ .get_power = intel_cht_wc_pmic_get_power,
+ .update_power = intel_cht_wc_pmic_update_power,
+ .exec_mipi_pmic_seq_element = intel_cht_wc_exec_mipi_pmic_seq_element,
+ .lpat_raw_to_temp = acpi_lpat_raw_to_temp,
+ .power_table = power_table,
+ .power_table_count = ARRAY_SIZE(power_table),
+};
+
+static int intel_cht_wc_pmic_opregion_probe(struct platform_device *pdev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
+
+ return intel_pmic_install_opregion_handler(&pdev->dev,
+ ACPI_HANDLE(pdev->dev.parent),
+ pmic->regmap,
+ &intel_cht_wc_pmic_opregion_data);
+}
+
+static const struct platform_device_id cht_wc_opregion_id_table[] = {
+ { .name = "cht_wcove_region" },
+ {},
+};
+
+static struct platform_driver intel_cht_wc_pmic_opregion_driver = {
+ .probe = intel_cht_wc_pmic_opregion_probe,
+ .driver = {
+ .name = "cht_whiskey_cove_pmic",
+ },
+ .id_table = cht_wc_opregion_id_table,
+};
+builtin_platform_driver(intel_cht_wc_pmic_opregion_driver);
diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
new file mode 100644
index 000000000..61bbe4c24
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * XPower AXP288 PMIC operation region driver
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <asm/iosf_mbi.h>
+#include "intel_pmic.h"
+
+#define XPOWER_GPADC_LOW 0x5b
+#define XPOWER_GPI1_CTRL 0x92
+
+#define GPI1_LDO_MASK GENMASK(2, 0)
+#define GPI1_LDO_ON (3 << 0)
+#define GPI1_LDO_OFF (4 << 0)
+
+#define AXP288_ADC_TS_CURRENT_ON_OFF_MASK GENMASK(1, 0)
+#define AXP288_ADC_TS_CURRENT_OFF (0 << 0)
+#define AXP288_ADC_TS_CURRENT_ON_WHEN_CHARGING (1 << 0)
+#define AXP288_ADC_TS_CURRENT_ON_ONDEMAND (2 << 0)
+#define AXP288_ADC_TS_CURRENT_ON (3 << 0)
+
+static struct pmic_table power_table[] = {
+ {
+ .address = 0x00,
+ .reg = 0x13,
+ .bit = 0x05,
+ }, /* ALD1 */
+ {
+ .address = 0x04,
+ .reg = 0x13,
+ .bit = 0x06,
+ }, /* ALD2 */
+ {
+ .address = 0x08,
+ .reg = 0x13,
+ .bit = 0x07,
+ }, /* ALD3 */
+ {
+ .address = 0x0c,
+ .reg = 0x12,
+ .bit = 0x03,
+ }, /* DLD1 */
+ {
+ .address = 0x10,
+ .reg = 0x12,
+ .bit = 0x04,
+ }, /* DLD2 */
+ {
+ .address = 0x14,
+ .reg = 0x12,
+ .bit = 0x05,
+ }, /* DLD3 */
+ {
+ .address = 0x18,
+ .reg = 0x12,
+ .bit = 0x06,
+ }, /* DLD4 */
+ {
+ .address = 0x1c,
+ .reg = 0x12,
+ .bit = 0x00,
+ }, /* ELD1 */
+ {
+ .address = 0x20,
+ .reg = 0x12,
+ .bit = 0x01,
+ }, /* ELD2 */
+ {
+ .address = 0x24,
+ .reg = 0x12,
+ .bit = 0x02,
+ }, /* ELD3 */
+ {
+ .address = 0x28,
+ .reg = 0x13,
+ .bit = 0x02,
+ }, /* FLD1 */
+ {
+ .address = 0x2c,
+ .reg = 0x13,
+ .bit = 0x03,
+ }, /* FLD2 */
+ {
+ .address = 0x30,
+ .reg = 0x13,
+ .bit = 0x04,
+ }, /* FLD3 */
+ {
+ .address = 0x34,
+ .reg = 0x10,
+ .bit = 0x03,
+ }, /* BUC1 */
+ {
+ .address = 0x38,
+ .reg = 0x10,
+ .bit = 0x06,
+ }, /* BUC2 */
+ {
+ .address = 0x3c,
+ .reg = 0x10,
+ .bit = 0x05,
+ }, /* BUC3 */
+ {
+ .address = 0x40,
+ .reg = 0x10,
+ .bit = 0x04,
+ }, /* BUC4 */
+ {
+ .address = 0x44,
+ .reg = 0x10,
+ .bit = 0x01,
+ }, /* BUC5 */
+ {
+ .address = 0x48,
+ .reg = 0x10,
+ .bit = 0x00
+ }, /* BUC6 */
+ {
+ .address = 0x4c,
+ .reg = 0x92,
+ }, /* GPI1 */
+};
+
+/* TMP0 - TMP5 are the same, all from GPADC */
+static struct pmic_table thermal_table[] = {
+ {
+ .address = 0x00,
+ .reg = XPOWER_GPADC_LOW
+ },
+ {
+ .address = 0x0c,
+ .reg = XPOWER_GPADC_LOW
+ },
+ {
+ .address = 0x18,
+ .reg = XPOWER_GPADC_LOW
+ },
+ {
+ .address = 0x24,
+ .reg = XPOWER_GPADC_LOW
+ },
+ {
+ .address = 0x30,
+ .reg = XPOWER_GPADC_LOW
+ },
+ {
+ .address = 0x3c,
+ .reg = XPOWER_GPADC_LOW
+ },
+};
+
+static int intel_xpower_pmic_get_power(struct regmap *regmap, int reg,
+ int bit, u64 *value)
+{
+ int data;
+
+ if (regmap_read(regmap, reg, &data))
+ return -EIO;
+
+ /* GPIO1 LDO regulator needs special handling */
+ if (reg == XPOWER_GPI1_CTRL)
+ *value = ((data & GPI1_LDO_MASK) == GPI1_LDO_ON);
+ else
+ *value = (data & BIT(bit)) ? 1 : 0;
+
+ return 0;
+}
+
+static int intel_xpower_pmic_update_power(struct regmap *regmap, int reg,
+ int bit, bool on)
+{
+ int data, ret;
+
+ ret = iosf_mbi_block_punit_i2c_access();
+ if (ret)
+ return ret;
+
+ /* GPIO1 LDO regulator needs special handling */
+ if (reg == XPOWER_GPI1_CTRL) {
+ ret = regmap_update_bits(regmap, reg, GPI1_LDO_MASK,
+ on ? GPI1_LDO_ON : GPI1_LDO_OFF);
+ goto out;
+ }
+
+ if (regmap_read(regmap, reg, &data)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (on)
+ data |= BIT(bit);
+ else
+ data &= ~BIT(bit);
+
+ if (regmap_write(regmap, reg, data))
+ ret = -EIO;
+out:
+ iosf_mbi_unblock_punit_i2c_access();
+
+ return ret;
+}
+
+/**
+ * intel_xpower_pmic_get_raw_temp(): Get raw temperature reading from the PMIC
+ *
+ * @regmap: regmap of the PMIC device
+ * @reg: register to get the reading
+ *
+ * Return a positive value on success, errno on failure.
+ */
+static int intel_xpower_pmic_get_raw_temp(struct regmap *regmap, int reg)
+{
+ int ret, adc_ts_pin_ctrl;
+ u8 buf[2];
+
+ /*
+ * The current-source used for the battery temp-sensor (TS) is shared
+ * with the GPADC. For proper fuel-gauge and charger operation the TS
+ * current-source needs to be permanently on. But to read the GPADC we
+ * need to temporary switch the TS current-source to ondemand, so that
+ * the GPADC can use it, otherwise we will always read an all 0 value.
+ *
+ * Note that the switching from on to on-ondemand is not necessary
+ * when the TS current-source is off (this happens on devices which
+ * do not use the TS-pin).
+ */
+ ret = regmap_read(regmap, AXP288_ADC_TS_PIN_CTRL, &adc_ts_pin_ctrl);
+ if (ret)
+ return ret;
+
+ if (adc_ts_pin_ctrl & AXP288_ADC_TS_CURRENT_ON_OFF_MASK) {
+ /*
+ * AXP288_ADC_TS_PIN_CTRL reads are cached by the regmap, so
+ * this does to a single I2C-transfer, and thus there is no
+ * need to explicitly call iosf_mbi_block_punit_i2c_access().
+ */
+ ret = regmap_update_bits(regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
+ AXP288_ADC_TS_CURRENT_ON_ONDEMAND);
+ if (ret)
+ return ret;
+
+ /* Wait a bit after switching the current-source */
+ usleep_range(6000, 10000);
+ }
+
+ ret = iosf_mbi_block_punit_i2c_access();
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(regmap, AXP288_GP_ADC_H, buf, 2);
+ if (ret == 0)
+ ret = (buf[0] << 4) + ((buf[1] >> 4) & 0x0f);
+
+ if (adc_ts_pin_ctrl & AXP288_ADC_TS_CURRENT_ON_OFF_MASK) {
+ regmap_update_bits(regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
+ AXP288_ADC_TS_CURRENT_ON);
+ }
+
+ iosf_mbi_unblock_punit_i2c_access();
+
+ return ret;
+}
+
+static int intel_xpower_exec_mipi_pmic_seq_element(struct regmap *regmap,
+ u16 i2c_address, u32 reg_address,
+ u32 value, u32 mask)
+{
+ 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);
+ return -ENXIO;
+ }
+
+ ret = iosf_mbi_block_punit_i2c_access();
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(regmap, reg_address, mask, value);
+
+ iosf_mbi_unblock_punit_i2c_access();
+
+ return ret;
+}
+
+static int intel_xpower_lpat_raw_to_temp(struct acpi_lpat_conversion_table *lpat_table,
+ int raw)
+{
+ struct acpi_lpat first = lpat_table->lpat[0];
+ struct acpi_lpat last = lpat_table->lpat[lpat_table->lpat_count - 1];
+
+ /*
+ * Some LPAT tables in the ACPI Device for the AXP288 PMIC for some
+ * reason only describe a small temperature range, e.g. 27° - 37°
+ * Celcius. Resulting in errors when the tablet is idle in a cool room.
+ *
+ * To avoid these errors clamp the raw value to be inside the LPAT.
+ */
+ if (first.raw < last.raw)
+ raw = clamp(raw, first.raw, last.raw);
+ else
+ raw = clamp(raw, last.raw, first.raw);
+
+ return acpi_lpat_raw_to_temp(lpat_table, raw);
+}
+
+static const struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
+ .get_power = intel_xpower_pmic_get_power,
+ .update_power = intel_xpower_pmic_update_power,
+ .get_raw_temp = intel_xpower_pmic_get_raw_temp,
+ .exec_mipi_pmic_seq_element = intel_xpower_exec_mipi_pmic_seq_element,
+ .lpat_raw_to_temp = intel_xpower_lpat_raw_to_temp,
+ .power_table = power_table,
+ .power_table_count = ARRAY_SIZE(power_table),
+ .thermal_table = thermal_table,
+ .thermal_table_count = ARRAY_SIZE(thermal_table),
+ .pmic_i2c_address = 0x34,
+};
+
+static acpi_status intel_xpower_pmic_gpio_handler(u32 function,
+ acpi_physical_address address, u32 bit_width, u64 *value,
+ void *handler_context, void *region_context)
+{
+ return AE_OK;
+}
+
+static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
+{
+ struct device *parent = pdev->dev.parent;
+ struct axp20x_dev *axp20x = dev_get_drvdata(parent);
+ acpi_status status;
+ int result;
+
+ status = acpi_install_address_space_handler(ACPI_HANDLE(parent),
+ ACPI_ADR_SPACE_GPIO, intel_xpower_pmic_gpio_handler,
+ NULL, NULL);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ result = intel_pmic_install_opregion_handler(&pdev->dev,
+ ACPI_HANDLE(parent), axp20x->regmap,
+ &intel_xpower_pmic_opregion_data);
+ if (result)
+ acpi_remove_address_space_handler(ACPI_HANDLE(parent),
+ ACPI_ADR_SPACE_GPIO,
+ intel_xpower_pmic_gpio_handler);
+
+ return result;
+}
+
+static struct platform_driver intel_xpower_pmic_opregion_driver = {
+ .probe = intel_xpower_pmic_opregion_probe,
+ .driver = {
+ .name = "axp288_pmic_acpi",
+ },
+};
+builtin_platform_driver(intel_xpower_pmic_opregion_driver);
diff --git a/drivers/acpi/pmic/tps68470_pmic.c b/drivers/acpi/pmic/tps68470_pmic.c
new file mode 100644
index 000000000..ebd03e472
--- /dev/null
+++ b/drivers/acpi/pmic/tps68470_pmic.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI TPS68470 PMIC operation region driver
+ *
+ * Copyright (C) 2017 Intel Corporation. All rights reserved.
+ *
+ * Author: Rajmohan Mani <rajmohan.mani@intel.com>
+ *
+ * Based on drivers/acpi/pmic/intel_pmic* drivers
+ */
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/mfd/tps68470.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct tps68470_pmic_table {
+ u32 address; /* operation region address */
+ u32 reg; /* corresponding register */
+ u32 bitmask; /* bit mask for power, clock */
+};
+
+#define TI_PMIC_POWER_OPREGION_ID 0xB0
+#define TI_PMIC_VR_VAL_OPREGION_ID 0xB1
+#define TI_PMIC_CLOCK_OPREGION_ID 0xB2
+#define TI_PMIC_CLKFREQ_OPREGION_ID 0xB3
+
+struct tps68470_pmic_opregion {
+ struct mutex lock;
+ struct regmap *regmap;
+};
+
+#define S_IO_I2C_EN (BIT(0) | BIT(1))
+
+static const struct tps68470_pmic_table power_table[] = {
+ {
+ .address = 0x00,
+ .reg = TPS68470_REG_S_I2C_CTL,
+ .bitmask = S_IO_I2C_EN,
+ /* S_I2C_CTL */
+ },
+ {
+ .address = 0x04,
+ .reg = TPS68470_REG_VCMCTL,
+ .bitmask = BIT(0),
+ /* VCMCTL */
+ },
+ {
+ .address = 0x08,
+ .reg = TPS68470_REG_VAUX1CTL,
+ .bitmask = BIT(0),
+ /* VAUX1_CTL */
+ },
+ {
+ .address = 0x0C,
+ .reg = TPS68470_REG_VAUX2CTL,
+ .bitmask = BIT(0),
+ /* VAUX2CTL */
+ },
+ {
+ .address = 0x10,
+ .reg = TPS68470_REG_VACTL,
+ .bitmask = BIT(0),
+ /* VACTL */
+ },
+ {
+ .address = 0x14,
+ .reg = TPS68470_REG_VDCTL,
+ .bitmask = BIT(0),
+ /* VDCTL */
+ },
+};
+
+/* Table to set voltage regulator value */
+static const struct tps68470_pmic_table vr_val_table[] = {
+ {
+ .address = 0x00,
+ .reg = TPS68470_REG_VSIOVAL,
+ .bitmask = TPS68470_VSIOVAL_IOVOLT_MASK,
+ /* TPS68470_REG_VSIOVAL */
+ },
+ {
+ .address = 0x04,
+ .reg = TPS68470_REG_VIOVAL,
+ .bitmask = TPS68470_VIOVAL_IOVOLT_MASK,
+ /* TPS68470_REG_VIOVAL */
+ },
+ {
+ .address = 0x08,
+ .reg = TPS68470_REG_VCMVAL,
+ .bitmask = TPS68470_VCMVAL_VCVOLT_MASK,
+ /* TPS68470_REG_VCMVAL */
+ },
+ {
+ .address = 0x0C,
+ .reg = TPS68470_REG_VAUX1VAL,
+ .bitmask = TPS68470_VAUX1VAL_AUX1VOLT_MASK,
+ /* TPS68470_REG_VAUX1VAL */
+ },
+ {
+ .address = 0x10,
+ .reg = TPS68470_REG_VAUX2VAL,
+ .bitmask = TPS68470_VAUX2VAL_AUX2VOLT_MASK,
+ /* TPS68470_REG_VAUX2VAL */
+ },
+ {
+ .address = 0x14,
+ .reg = TPS68470_REG_VAVAL,
+ .bitmask = TPS68470_VAVAL_AVOLT_MASK,
+ /* TPS68470_REG_VAVAL */
+ },
+ {
+ .address = 0x18,
+ .reg = TPS68470_REG_VDVAL,
+ .bitmask = TPS68470_VDVAL_DVOLT_MASK,
+ /* TPS68470_REG_VDVAL */
+ },
+};
+
+/* Table to configure clock frequency */
+static const struct tps68470_pmic_table clk_freq_table[] = {
+ {
+ .address = 0x00,
+ .reg = TPS68470_REG_POSTDIV2,
+ .bitmask = BIT(0) | BIT(1),
+ /* TPS68470_REG_POSTDIV2 */
+ },
+ {
+ .address = 0x04,
+ .reg = TPS68470_REG_BOOSTDIV,
+ .bitmask = 0x1F,
+ /* TPS68470_REG_BOOSTDIV */
+ },
+ {
+ .address = 0x08,
+ .reg = TPS68470_REG_BUCKDIV,
+ .bitmask = 0x0F,
+ /* TPS68470_REG_BUCKDIV */
+ },
+ {
+ .address = 0x0C,
+ .reg = TPS68470_REG_PLLSWR,
+ .bitmask = 0x13,
+ /* TPS68470_REG_PLLSWR */
+ },
+ {
+ .address = 0x10,
+ .reg = TPS68470_REG_XTALDIV,
+ .bitmask = 0xFF,
+ /* TPS68470_REG_XTALDIV */
+ },
+ {
+ .address = 0x14,
+ .reg = TPS68470_REG_PLLDIV,
+ .bitmask = 0xFF,
+ /* TPS68470_REG_PLLDIV */
+ },
+ {
+ .address = 0x18,
+ .reg = TPS68470_REG_POSTDIV,
+ .bitmask = 0x83,
+ /* TPS68470_REG_POSTDIV */
+ },
+};
+
+/* Table to configure and enable clocks */
+static const struct tps68470_pmic_table clk_table[] = {
+ {
+ .address = 0x00,
+ .reg = TPS68470_REG_PLLCTL,
+ .bitmask = 0xF5,
+ /* TPS68470_REG_PLLCTL */
+ },
+ {
+ .address = 0x04,
+ .reg = TPS68470_REG_PLLCTL2,
+ .bitmask = BIT(0),
+ /* TPS68470_REG_PLLCTL2 */
+ },
+ {
+ .address = 0x08,
+ .reg = TPS68470_REG_CLKCFG1,
+ .bitmask = TPS68470_CLKCFG1_MODE_A_MASK |
+ TPS68470_CLKCFG1_MODE_B_MASK,
+ /* TPS68470_REG_CLKCFG1 */
+ },
+ {
+ .address = 0x0C,
+ .reg = TPS68470_REG_CLKCFG2,
+ .bitmask = TPS68470_CLKCFG1_MODE_A_MASK |
+ TPS68470_CLKCFG1_MODE_B_MASK,
+ /* TPS68470_REG_CLKCFG2 */
+ },
+};
+
+static int pmic_get_reg_bit(u64 address,
+ const struct tps68470_pmic_table *table,
+ const unsigned int table_size, int *reg,
+ int *bitmask)
+{
+ u64 i;
+
+ i = address / 4;
+ if (i >= table_size)
+ return -ENOENT;
+
+ if (!reg || !bitmask)
+ return -EINVAL;
+
+ *reg = table[i].reg;
+ *bitmask = table[i].bitmask;
+
+ return 0;
+}
+
+static int tps68470_pmic_get_power(struct regmap *regmap, int reg,
+ int bitmask, u64 *value)
+{
+ unsigned int data;
+
+ if (regmap_read(regmap, reg, &data))
+ return -EIO;
+
+ *value = (data & bitmask) ? 1 : 0;
+ return 0;
+}
+
+static int tps68470_pmic_get_vr_val(struct regmap *regmap, int reg,
+ int bitmask, u64 *value)
+{
+ unsigned int data;
+
+ if (regmap_read(regmap, reg, &data))
+ return -EIO;
+
+ *value = data & bitmask;
+ return 0;
+}
+
+static int tps68470_pmic_get_clk(struct regmap *regmap, int reg,
+ int bitmask, u64 *value)
+{
+ unsigned int data;
+
+ if (regmap_read(regmap, reg, &data))
+ return -EIO;
+
+ *value = (data & bitmask) ? 1 : 0;
+ return 0;
+}
+
+static int tps68470_pmic_get_clk_freq(struct regmap *regmap, int reg,
+ int bitmask, u64 *value)
+{
+ unsigned int data;
+
+ if (regmap_read(regmap, reg, &data))
+ return -EIO;
+
+ *value = data & bitmask;
+ return 0;
+}
+
+static int ti_tps68470_regmap_update_bits(struct regmap *regmap, int reg,
+ int bitmask, u64 value)
+{
+ return regmap_update_bits(regmap, reg, bitmask, value);
+}
+
+static acpi_status tps68470_pmic_common_handler(u32 function,
+ acpi_physical_address address,
+ u32 bits, u64 *value,
+ void *region_context,
+ int (*get)(struct regmap *,
+ int, int, u64 *),
+ int (*update)(struct regmap *,
+ int, int, u64),
+ const struct tps68470_pmic_table *tbl,
+ unsigned int tbl_size)
+{
+ struct tps68470_pmic_opregion *opregion = region_context;
+ struct regmap *regmap = opregion->regmap;
+ int reg, ret, bitmask;
+
+ if (bits != 32)
+ return AE_BAD_PARAMETER;
+
+ ret = pmic_get_reg_bit(address, tbl, tbl_size, &reg, &bitmask);
+ if (ret < 0)
+ return AE_BAD_PARAMETER;
+
+ if (function == ACPI_WRITE && *value > bitmask)
+ return AE_BAD_PARAMETER;
+
+ mutex_lock(&opregion->lock);
+
+ ret = (function == ACPI_READ) ?
+ get(regmap, reg, bitmask, value) :
+ update(regmap, reg, bitmask, *value);
+
+ mutex_unlock(&opregion->lock);
+
+ return ret ? AE_ERROR : AE_OK;
+}
+
+static acpi_status tps68470_pmic_cfreq_handler(u32 function,
+ acpi_physical_address address,
+ u32 bits, u64 *value,
+ void *handler_context,
+ void *region_context)
+{
+ return tps68470_pmic_common_handler(function, address, bits, value,
+ region_context,
+ tps68470_pmic_get_clk_freq,
+ ti_tps68470_regmap_update_bits,
+ clk_freq_table,
+ ARRAY_SIZE(clk_freq_table));
+}
+
+static acpi_status tps68470_pmic_clk_handler(u32 function,
+ acpi_physical_address address, u32 bits,
+ u64 *value, void *handler_context,
+ void *region_context)
+{
+ return tps68470_pmic_common_handler(function, address, bits, value,
+ region_context,
+ tps68470_pmic_get_clk,
+ ti_tps68470_regmap_update_bits,
+ clk_table,
+ ARRAY_SIZE(clk_table));
+}
+
+static acpi_status tps68470_pmic_vrval_handler(u32 function,
+ acpi_physical_address address,
+ u32 bits, u64 *value,
+ void *handler_context,
+ void *region_context)
+{
+ return tps68470_pmic_common_handler(function, address, bits, value,
+ region_context,
+ tps68470_pmic_get_vr_val,
+ ti_tps68470_regmap_update_bits,
+ vr_val_table,
+ ARRAY_SIZE(vr_val_table));
+}
+
+static acpi_status tps68470_pmic_pwr_handler(u32 function,
+ acpi_physical_address address,
+ u32 bits, u64 *value,
+ void *handler_context,
+ void *region_context)
+{
+ if (bits != 32)
+ return AE_BAD_PARAMETER;
+
+ /* set/clear for bit 0, bits 0 and 1 together */
+ if (function == ACPI_WRITE &&
+ !(*value == 0 || *value == 1 || *value == 3)) {
+ return AE_BAD_PARAMETER;
+ }
+
+ return tps68470_pmic_common_handler(function, address, bits, value,
+ region_context,
+ tps68470_pmic_get_power,
+ ti_tps68470_regmap_update_bits,
+ power_table,
+ ARRAY_SIZE(power_table));
+}
+
+static int tps68470_pmic_opregion_probe(struct platform_device *pdev)
+{
+ struct regmap *tps68470_regmap = dev_get_drvdata(pdev->dev.parent);
+ acpi_handle handle = ACPI_HANDLE(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct tps68470_pmic_opregion *opregion;
+ acpi_status status;
+
+ if (!dev || !tps68470_regmap) {
+ dev_warn(dev, "dev or regmap is NULL\n");
+ return -EINVAL;
+ }
+
+ if (!handle) {
+ dev_warn(dev, "acpi handle is NULL\n");
+ return -ENODEV;
+ }
+
+ opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL);
+ if (!opregion)
+ return -ENOMEM;
+
+ mutex_init(&opregion->lock);
+ opregion->regmap = tps68470_regmap;
+
+ status = acpi_install_address_space_handler(handle,
+ TI_PMIC_POWER_OPREGION_ID,
+ tps68470_pmic_pwr_handler,
+ NULL, opregion);
+ if (ACPI_FAILURE(status))
+ goto out_mutex_destroy;
+
+ status = acpi_install_address_space_handler(handle,
+ TI_PMIC_VR_VAL_OPREGION_ID,
+ tps68470_pmic_vrval_handler,
+ NULL, opregion);
+ if (ACPI_FAILURE(status))
+ goto out_remove_power_handler;
+
+ status = acpi_install_address_space_handler(handle,
+ TI_PMIC_CLOCK_OPREGION_ID,
+ tps68470_pmic_clk_handler,
+ NULL, opregion);
+ if (ACPI_FAILURE(status))
+ goto out_remove_vr_val_handler;
+
+ status = acpi_install_address_space_handler(handle,
+ TI_PMIC_CLKFREQ_OPREGION_ID,
+ tps68470_pmic_cfreq_handler,
+ NULL, opregion);
+ if (ACPI_FAILURE(status))
+ goto out_remove_clk_handler;
+
+ return 0;
+
+out_remove_clk_handler:
+ acpi_remove_address_space_handler(handle, TI_PMIC_CLOCK_OPREGION_ID,
+ tps68470_pmic_clk_handler);
+out_remove_vr_val_handler:
+ acpi_remove_address_space_handler(handle, TI_PMIC_VR_VAL_OPREGION_ID,
+ tps68470_pmic_vrval_handler);
+out_remove_power_handler:
+ acpi_remove_address_space_handler(handle, TI_PMIC_POWER_OPREGION_ID,
+ tps68470_pmic_pwr_handler);
+out_mutex_destroy:
+ mutex_destroy(&opregion->lock);
+ return -ENODEV;
+}
+
+static struct platform_driver tps68470_pmic_opregion_driver = {
+ .probe = tps68470_pmic_opregion_probe,
+ .driver = {
+ .name = "tps68470_pmic_opregion",
+ },
+};
+
+builtin_platform_driver(tps68470_pmic_opregion_driver)
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
new file mode 100644
index 000000000..aea8c994c
--- /dev/null
+++ b/drivers/acpi/power.c
@@ -0,0 +1,1066 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * drivers/acpi/power.c - ACPI Power Resources management.
+ *
+ * Copyright (C) 2001 - 2015 Intel Corp.
+ * Author: Andy Grover <andrew.grover@intel.com>
+ * Author: Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ */
+
+/*
+ * ACPI power-managed devices may be controlled in two ways:
+ * 1. via "Device Specific (D-State) Control"
+ * 2. via "Power Resource Control".
+ * The code below deals with ACPI Power Resources control.
+ *
+ * An ACPI "power resource object" represents a software controllable power
+ * plane, clock plane, or other resource depended on by a device.
+ *
+ * A device may rely on multiple power resources, and a power resource
+ * may be shared by multiple devices.
+ */
+
+#define pr_fmt(fmt) "ACPI: PM: " fmt
+
+#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/pm_runtime.h>
+#include <linux/sysfs.h>
+#include <linux/acpi.h>
+#include "sleep.h"
+#include "internal.h"
+
+#define ACPI_POWER_CLASS "power_resource"
+#define ACPI_POWER_DEVICE_NAME "Power Resource"
+#define ACPI_POWER_RESOURCE_STATE_OFF 0x00
+#define ACPI_POWER_RESOURCE_STATE_ON 0x01
+#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
+
+struct acpi_power_dependent_device {
+ struct device *dev;
+ struct list_head node;
+};
+
+struct acpi_power_resource {
+ struct acpi_device device;
+ struct list_head list_node;
+ u32 system_level;
+ u32 order;
+ unsigned int ref_count;
+ u8 state;
+ struct mutex resource_lock;
+ struct list_head dependents;
+};
+
+struct acpi_power_resource_entry {
+ struct list_head node;
+ struct acpi_power_resource *resource;
+};
+
+static LIST_HEAD(acpi_power_resource_list);
+static DEFINE_MUTEX(power_resource_list_lock);
+
+/* --------------------------------------------------------------------------
+ Power Resource Management
+ -------------------------------------------------------------------------- */
+
+static inline const char *resource_dev_name(struct acpi_power_resource *pr)
+{
+ return dev_name(&pr->device.dev);
+}
+
+static inline
+struct acpi_power_resource *to_power_resource(struct acpi_device *device)
+{
+ return container_of(device, struct acpi_power_resource, device);
+}
+
+static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+
+ if (!device)
+ return NULL;
+
+ return to_power_resource(device);
+}
+
+static int acpi_power_resources_list_add(acpi_handle handle,
+ struct list_head *list)
+{
+ struct acpi_power_resource *resource = acpi_power_get_context(handle);
+ struct acpi_power_resource_entry *entry;
+
+ if (!resource || !list)
+ return -EINVAL;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->resource = resource;
+ if (!list_empty(list)) {
+ struct acpi_power_resource_entry *e;
+
+ list_for_each_entry(e, list, node)
+ if (e->resource->order > resource->order) {
+ list_add_tail(&entry->node, &e->node);
+ return 0;
+ }
+ }
+ list_add_tail(&entry->node, list);
+ return 0;
+}
+
+void acpi_power_resources_list_free(struct list_head *list)
+{
+ struct acpi_power_resource_entry *entry, *e;
+
+ list_for_each_entry_safe(entry, e, list, node) {
+ list_del(&entry->node);
+ kfree(entry);
+ }
+}
+
+static bool acpi_power_resource_is_dup(union acpi_object *package,
+ unsigned int start, unsigned int i)
+{
+ acpi_handle rhandle, dup;
+ unsigned int j;
+
+ /* The caller is expected to check the package element types */
+ rhandle = package->package.elements[i].reference.handle;
+ for (j = start; j < i; j++) {
+ dup = package->package.elements[j].reference.handle;
+ if (dup == rhandle)
+ return true;
+ }
+
+ return false;
+}
+
+int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
+ struct list_head *list)
+{
+ unsigned int i;
+ int err = 0;
+
+ for (i = start; i < package->package.count; i++) {
+ union acpi_object *element = &package->package.elements[i];
+ struct acpi_device *rdev;
+ acpi_handle rhandle;
+
+ if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
+ err = -ENODATA;
+ break;
+ }
+ rhandle = element->reference.handle;
+ if (!rhandle) {
+ err = -ENODEV;
+ break;
+ }
+
+ /* Some ACPI tables contain duplicate power resource references */
+ if (acpi_power_resource_is_dup(package, start, i))
+ continue;
+
+ rdev = acpi_add_power_resource(rhandle);
+ if (!rdev) {
+ err = -ENODEV;
+ break;
+ }
+ err = acpi_power_resources_list_add(rhandle, list);
+ if (err)
+ break;
+ }
+ if (err)
+ acpi_power_resources_list_free(list);
+
+ return err;
+}
+
+static int __get_state(acpi_handle handle, u8 *state)
+{
+ acpi_status status = AE_OK;
+ unsigned long long sta = 0;
+ u8 cur_state;
+
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ cur_state = sta & ACPI_POWER_RESOURCE_STATE_ON;
+
+ acpi_handle_debug(handle, "Power resource is %s\n",
+ cur_state ? "on" : "off");
+
+ *state = cur_state;
+ return 0;
+}
+
+static int acpi_power_get_state(struct acpi_power_resource *resource, u8 *state)
+{
+ if (resource->state == ACPI_POWER_RESOURCE_STATE_UNKNOWN) {
+ int ret;
+
+ ret = __get_state(resource->device.handle, &resource->state);
+ if (ret)
+ return ret;
+ }
+
+ *state = resource->state;
+ return 0;
+}
+
+static int acpi_power_get_list_state(struct list_head *list, u8 *state)
+{
+ struct acpi_power_resource_entry *entry;
+ u8 cur_state = ACPI_POWER_RESOURCE_STATE_OFF;
+
+ if (!list || !state)
+ return -EINVAL;
+
+ /* The state of the list is 'on' IFF all resources are 'on'. */
+ list_for_each_entry(entry, list, node) {
+ struct acpi_power_resource *resource = entry->resource;
+ int result;
+
+ mutex_lock(&resource->resource_lock);
+ result = acpi_power_get_state(resource, &cur_state);
+ mutex_unlock(&resource->resource_lock);
+ if (result)
+ return result;
+
+ if (cur_state != ACPI_POWER_RESOURCE_STATE_ON)
+ break;
+ }
+
+ pr_debug("Power resource list is %s\n", cur_state ? "on" : "off");
+
+ *state = cur_state;
+ return 0;
+}
+
+static int
+acpi_power_resource_add_dependent(struct acpi_power_resource *resource,
+ struct device *dev)
+{
+ struct acpi_power_dependent_device *dep;
+ int ret = 0;
+
+ mutex_lock(&resource->resource_lock);
+ list_for_each_entry(dep, &resource->dependents, node) {
+ /* Only add it once */
+ if (dep->dev == dev)
+ goto unlock;
+ }
+
+ dep = kzalloc(sizeof(*dep), GFP_KERNEL);
+ if (!dep) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ dep->dev = dev;
+ list_add_tail(&dep->node, &resource->dependents);
+ dev_dbg(dev, "added power dependency to [%s]\n",
+ resource_dev_name(resource));
+
+unlock:
+ mutex_unlock(&resource->resource_lock);
+ return ret;
+}
+
+static void
+acpi_power_resource_remove_dependent(struct acpi_power_resource *resource,
+ struct device *dev)
+{
+ struct acpi_power_dependent_device *dep;
+
+ mutex_lock(&resource->resource_lock);
+ list_for_each_entry(dep, &resource->dependents, node) {
+ if (dep->dev == dev) {
+ list_del(&dep->node);
+ kfree(dep);
+ dev_dbg(dev, "removed power dependency to [%s]\n",
+ resource_dev_name(resource));
+ break;
+ }
+ }
+ mutex_unlock(&resource->resource_lock);
+}
+
+/**
+ * acpi_device_power_add_dependent - Add dependent device of this ACPI device
+ * @adev: ACPI device pointer
+ * @dev: Dependent device
+ *
+ * If @adev has non-empty _PR0 the @dev is added as dependent device to all
+ * power resources returned by it. This means that whenever these power
+ * resources are turned _ON the dependent devices get runtime resumed. This
+ * is needed for devices such as PCI to allow its driver to re-initialize
+ * it after it went to D0uninitialized.
+ *
+ * If @adev does not have _PR0 this does nothing.
+ *
+ * Returns %0 in case of success and negative errno otherwise.
+ */
+int acpi_device_power_add_dependent(struct acpi_device *adev,
+ struct device *dev)
+{
+ struct acpi_power_resource_entry *entry;
+ struct list_head *resources;
+ int ret;
+
+ if (!adev->flags.power_manageable)
+ return 0;
+
+ resources = &adev->power.states[ACPI_STATE_D0].resources;
+ list_for_each_entry(entry, resources, node) {
+ ret = acpi_power_resource_add_dependent(entry->resource, dev);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ list_for_each_entry(entry, resources, node)
+ acpi_power_resource_remove_dependent(entry->resource, dev);
+
+ return ret;
+}
+
+/**
+ * acpi_device_power_remove_dependent - Remove dependent device
+ * @adev: ACPI device pointer
+ * @dev: Dependent device
+ *
+ * Does the opposite of acpi_device_power_add_dependent() and removes the
+ * dependent device if it is found. Can be called to @adev that does not
+ * have _PR0 as well.
+ */
+void acpi_device_power_remove_dependent(struct acpi_device *adev,
+ struct device *dev)
+{
+ struct acpi_power_resource_entry *entry;
+ struct list_head *resources;
+
+ if (!adev->flags.power_manageable)
+ return;
+
+ resources = &adev->power.states[ACPI_STATE_D0].resources;
+ list_for_each_entry_reverse(entry, resources, node)
+ acpi_power_resource_remove_dependent(entry->resource, dev);
+}
+
+static int __acpi_power_on(struct acpi_power_resource *resource)
+{
+ acpi_handle handle = resource->device.handle;
+ struct acpi_power_dependent_device *dep;
+ acpi_status status = AE_OK;
+
+ status = acpi_evaluate_object(handle, "_ON", NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN;
+ return -ENODEV;
+ }
+
+ resource->state = ACPI_POWER_RESOURCE_STATE_ON;
+
+ acpi_handle_debug(handle, "Power resource turned on\n");
+
+ /*
+ * If there are other dependents on this power resource we need to
+ * resume them now so that their drivers can re-initialize the
+ * hardware properly after it went back to D0.
+ */
+ if (list_empty(&resource->dependents) ||
+ list_is_singular(&resource->dependents))
+ return 0;
+
+ list_for_each_entry(dep, &resource->dependents, node) {
+ dev_dbg(dep->dev, "runtime resuming because [%s] turned on\n",
+ resource_dev_name(resource));
+ pm_request_resume(dep->dev);
+ }
+
+ return 0;
+}
+
+static int acpi_power_on_unlocked(struct acpi_power_resource *resource)
+{
+ int result = 0;
+
+ if (resource->ref_count++) {
+ acpi_handle_debug(resource->device.handle,
+ "Power resource already on\n");
+ } else {
+ result = __acpi_power_on(resource);
+ if (result)
+ resource->ref_count--;
+ }
+ return result;
+}
+
+static int acpi_power_on(struct acpi_power_resource *resource)
+{
+ int result;
+
+ mutex_lock(&resource->resource_lock);
+ result = acpi_power_on_unlocked(resource);
+ mutex_unlock(&resource->resource_lock);
+ return result;
+}
+
+static int __acpi_power_off(struct acpi_power_resource *resource)
+{
+ acpi_handle handle = resource->device.handle;
+ acpi_status status;
+
+ status = acpi_evaluate_object(handle, "_OFF", NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN;
+ return -ENODEV;
+ }
+
+ resource->state = ACPI_POWER_RESOURCE_STATE_OFF;
+
+ acpi_handle_debug(handle, "Power resource turned off\n");
+
+ return 0;
+}
+
+static int acpi_power_off_unlocked(struct acpi_power_resource *resource)
+{
+ int result = 0;
+
+ if (!resource->ref_count) {
+ acpi_handle_debug(resource->device.handle,
+ "Power resource already off\n");
+ return 0;
+ }
+
+ if (--resource->ref_count) {
+ acpi_handle_debug(resource->device.handle,
+ "Power resource still in use\n");
+ } else {
+ result = __acpi_power_off(resource);
+ if (result)
+ resource->ref_count++;
+ }
+ return result;
+}
+
+static int acpi_power_off(struct acpi_power_resource *resource)
+{
+ int result;
+
+ mutex_lock(&resource->resource_lock);
+ result = acpi_power_off_unlocked(resource);
+ mutex_unlock(&resource->resource_lock);
+ return result;
+}
+
+static int acpi_power_off_list(struct list_head *list)
+{
+ struct acpi_power_resource_entry *entry;
+ int result = 0;
+
+ list_for_each_entry_reverse(entry, list, node) {
+ result = acpi_power_off(entry->resource);
+ if (result)
+ goto err;
+ }
+ return 0;
+
+ err:
+ list_for_each_entry_continue(entry, list, node)
+ acpi_power_on(entry->resource);
+
+ return result;
+}
+
+static int acpi_power_on_list(struct list_head *list)
+{
+ struct acpi_power_resource_entry *entry;
+ int result = 0;
+
+ list_for_each_entry(entry, list, node) {
+ result = acpi_power_on(entry->resource);
+ if (result)
+ goto err;
+ }
+ return 0;
+
+ err:
+ list_for_each_entry_continue_reverse(entry, list, node)
+ acpi_power_off(entry->resource);
+
+ return result;
+}
+
+static struct attribute *attrs[] = {
+ NULL,
+};
+
+static const struct attribute_group attr_groups[] = {
+ [ACPI_STATE_D0] = {
+ .name = "power_resources_D0",
+ .attrs = attrs,
+ },
+ [ACPI_STATE_D1] = {
+ .name = "power_resources_D1",
+ .attrs = attrs,
+ },
+ [ACPI_STATE_D2] = {
+ .name = "power_resources_D2",
+ .attrs = attrs,
+ },
+ [ACPI_STATE_D3_HOT] = {
+ .name = "power_resources_D3hot",
+ .attrs = attrs,
+ },
+};
+
+static const struct attribute_group wakeup_attr_group = {
+ .name = "power_resources_wakeup",
+ .attrs = attrs,
+};
+
+static void acpi_power_hide_list(struct acpi_device *adev,
+ struct list_head *resources,
+ const struct attribute_group *attr_group)
+{
+ struct acpi_power_resource_entry *entry;
+
+ if (list_empty(resources))
+ return;
+
+ list_for_each_entry_reverse(entry, resources, node) {
+ struct acpi_device *res_dev = &entry->resource->device;
+
+ sysfs_remove_link_from_group(&adev->dev.kobj,
+ attr_group->name,
+ dev_name(&res_dev->dev));
+ }
+ sysfs_remove_group(&adev->dev.kobj, attr_group);
+}
+
+static void acpi_power_expose_list(struct acpi_device *adev,
+ struct list_head *resources,
+ const struct attribute_group *attr_group)
+{
+ struct acpi_power_resource_entry *entry;
+ int ret;
+
+ if (list_empty(resources))
+ return;
+
+ ret = sysfs_create_group(&adev->dev.kobj, attr_group);
+ if (ret)
+ return;
+
+ list_for_each_entry(entry, resources, node) {
+ struct acpi_device *res_dev = &entry->resource->device;
+
+ ret = sysfs_add_link_to_group(&adev->dev.kobj,
+ attr_group->name,
+ &res_dev->dev.kobj,
+ dev_name(&res_dev->dev));
+ if (ret) {
+ acpi_power_hide_list(adev, resources, attr_group);
+ break;
+ }
+ }
+}
+
+static void acpi_power_expose_hide(struct acpi_device *adev,
+ struct list_head *resources,
+ const struct attribute_group *attr_group,
+ bool expose)
+{
+ if (expose)
+ acpi_power_expose_list(adev, resources, attr_group);
+ else
+ acpi_power_hide_list(adev, resources, attr_group);
+}
+
+void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
+{
+ int state;
+
+ if (adev->wakeup.flags.valid)
+ acpi_power_expose_hide(adev, &adev->wakeup.resources,
+ &wakeup_attr_group, add);
+
+ if (!adev->power.flags.power_resources)
+ return;
+
+ for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++)
+ acpi_power_expose_hide(adev,
+ &adev->power.states[state].resources,
+ &attr_groups[state], add);
+}
+
+int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p)
+{
+ struct acpi_power_resource_entry *entry;
+ int system_level = 5;
+
+ list_for_each_entry(entry, list, node) {
+ struct acpi_power_resource *resource = entry->resource;
+ u8 state;
+
+ mutex_lock(&resource->resource_lock);
+
+ /*
+ * Make sure that the power resource state and its reference
+ * counter value are consistent with each other.
+ */
+ if (!resource->ref_count &&
+ !acpi_power_get_state(resource, &state) &&
+ state == ACPI_POWER_RESOURCE_STATE_ON)
+ __acpi_power_off(resource);
+
+ if (system_level > resource->system_level)
+ system_level = resource->system_level;
+
+ mutex_unlock(&resource->resource_lock);
+ }
+ *system_level_p = system_level;
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ Device Power Management
+ -------------------------------------------------------------------------- */
+
+/**
+ * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in
+ * ACPI 3.0) _PSW (Power State Wake)
+ * @dev: Device to handle.
+ * @enable: 0 - disable, 1 - enable the wake capabilities of the device.
+ * @sleep_state: Target sleep state of the system.
+ * @dev_state: Target power state of the device.
+ *
+ * Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
+ * State Wake) for the device, if present. On failure reset the device's
+ * wakeup.flags.valid flag.
+ *
+ * RETURN VALUE:
+ * 0 if either _DSW or _PSW has been successfully executed
+ * 0 if neither _DSW nor _PSW has been found
+ * -ENODEV if the execution of either _DSW or _PSW has failed
+ */
+int acpi_device_sleep_wake(struct acpi_device *dev,
+ int enable, int sleep_state, int dev_state)
+{
+ union acpi_object in_arg[3];
+ struct acpi_object_list arg_list = { 3, in_arg };
+ acpi_status status = AE_OK;
+
+ /*
+ * Try to execute _DSW first.
+ *
+ * Three arguments are needed for the _DSW object:
+ * Argument 0: enable/disable the wake capabilities
+ * Argument 1: target system state
+ * Argument 2: target device state
+ * When _DSW object is called to disable the wake capabilities, maybe
+ * the first argument is filled. The values of the other two arguments
+ * are meaningless.
+ */
+ in_arg[0].type = ACPI_TYPE_INTEGER;
+ in_arg[0].integer.value = enable;
+ in_arg[1].type = ACPI_TYPE_INTEGER;
+ in_arg[1].integer.value = sleep_state;
+ in_arg[2].type = ACPI_TYPE_INTEGER;
+ in_arg[2].integer.value = dev_state;
+ status = acpi_evaluate_object(dev->handle, "_DSW", &arg_list, NULL);
+ if (ACPI_SUCCESS(status)) {
+ return 0;
+ } else if (status != AE_NOT_FOUND) {
+ acpi_handle_info(dev->handle, "_DSW execution failed\n");
+ dev->wakeup.flags.valid = 0;
+ return -ENODEV;
+ }
+
+ /* Execute _PSW */
+ status = acpi_execute_simple_method(dev->handle, "_PSW", enable);
+ if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
+ acpi_handle_info(dev->handle, "_PSW execution failed\n");
+ dev->wakeup.flags.valid = 0;
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
+ * 1. Power on the power resources required for the wakeup device
+ * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
+ * State Wake) for the device, if present
+ */
+int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
+{
+ int err = 0;
+
+ if (!dev || !dev->wakeup.flags.valid)
+ return -EINVAL;
+
+ mutex_lock(&acpi_device_lock);
+
+ dev_dbg(&dev->dev, "Enabling wakeup power (count %d)\n",
+ dev->wakeup.prepare_count);
+
+ if (dev->wakeup.prepare_count++)
+ goto out;
+
+ err = acpi_power_on_list(&dev->wakeup.resources);
+ if (err) {
+ dev_err(&dev->dev, "Cannot turn on wakeup power resources\n");
+ dev->wakeup.flags.valid = 0;
+ goto out;
+ }
+
+ /*
+ * Passing 3 as the third argument below means the device may be
+ * put into arbitrary power state afterward.
+ */
+ err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
+ if (err) {
+ acpi_power_off_list(&dev->wakeup.resources);
+ dev->wakeup.prepare_count = 0;
+ goto out;
+ }
+
+ dev_dbg(&dev->dev, "Wakeup power enabled\n");
+
+ out:
+ mutex_unlock(&acpi_device_lock);
+ return err;
+}
+
+/*
+ * Shutdown a wakeup device, counterpart of above method
+ * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
+ * State Wake) for the device, if present
+ * 2. Shutdown down the power resources
+ */
+int acpi_disable_wakeup_device_power(struct acpi_device *dev)
+{
+ struct acpi_power_resource_entry *entry;
+ int err = 0;
+
+ if (!dev || !dev->wakeup.flags.valid)
+ return -EINVAL;
+
+ mutex_lock(&acpi_device_lock);
+
+ dev_dbg(&dev->dev, "Disabling wakeup power (count %d)\n",
+ dev->wakeup.prepare_count);
+
+ /* Do nothing if wakeup power has not been enabled for this device. */
+ if (dev->wakeup.prepare_count <= 0)
+ goto out;
+
+ if (--dev->wakeup.prepare_count > 0)
+ goto out;
+
+ err = acpi_device_sleep_wake(dev, 0, 0, 0);
+ if (err)
+ goto out;
+
+ /*
+ * All of the power resources in the list need to be turned off even if
+ * there are errors.
+ */
+ list_for_each_entry(entry, &dev->wakeup.resources, node) {
+ int ret;
+
+ ret = acpi_power_off(entry->resource);
+ if (ret && !err)
+ err = ret;
+ }
+ if (err) {
+ dev_err(&dev->dev, "Cannot turn off wakeup power resources\n");
+ dev->wakeup.flags.valid = 0;
+ goto out;
+ }
+
+ dev_dbg(&dev->dev, "Wakeup power disabled\n");
+
+ out:
+ mutex_unlock(&acpi_device_lock);
+ return err;
+}
+
+int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
+{
+ u8 list_state = ACPI_POWER_RESOURCE_STATE_OFF;
+ int result = 0;
+ int i = 0;
+
+ if (!device || !state)
+ return -EINVAL;
+
+ /*
+ * We know a device's inferred power state when all the resources
+ * required for a given D-state are 'on'.
+ */
+ for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
+ struct list_head *list = &device->power.states[i].resources;
+
+ if (list_empty(list))
+ continue;
+
+ result = acpi_power_get_list_state(list, &list_state);
+ if (result)
+ return result;
+
+ if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
+ *state = i;
+ return 0;
+ }
+ }
+
+ *state = device->power.states[ACPI_STATE_D3_COLD].flags.valid ?
+ ACPI_STATE_D3_COLD : ACPI_STATE_D3_HOT;
+ return 0;
+}
+
+int acpi_power_on_resources(struct acpi_device *device, int state)
+{
+ if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3_HOT)
+ return -EINVAL;
+
+ return acpi_power_on_list(&device->power.states[state].resources);
+}
+
+int acpi_power_transition(struct acpi_device *device, int state)
+{
+ int result = 0;
+
+ if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
+ return -EINVAL;
+
+ if (device->power.state == state || !device->flags.power_manageable)
+ return 0;
+
+ if ((device->power.state < ACPI_STATE_D0)
+ || (device->power.state > ACPI_STATE_D3_COLD))
+ return -ENODEV;
+
+ /*
+ * First we reference all power resources required in the target list
+ * (e.g. so the device doesn't lose power while transitioning). Then,
+ * we dereference all power resources used in the current list.
+ */
+ if (state < ACPI_STATE_D3_COLD)
+ result = acpi_power_on_list(
+ &device->power.states[state].resources);
+
+ if (!result && device->power.state < ACPI_STATE_D3_COLD)
+ acpi_power_off_list(
+ &device->power.states[device->power.state].resources);
+
+ /* We shouldn't change the state unless the above operations succeed. */
+ device->power.state = result ? ACPI_STATE_UNKNOWN : state;
+
+ return result;
+}
+
+static void acpi_release_power_resource(struct device *dev)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct acpi_power_resource *resource;
+
+ resource = container_of(device, struct acpi_power_resource, device);
+
+ mutex_lock(&power_resource_list_lock);
+ list_del(&resource->list_node);
+ mutex_unlock(&power_resource_list_lock);
+
+ acpi_free_pnp_ids(&device->pnp);
+ kfree(resource);
+}
+
+static ssize_t resource_in_use_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_power_resource *resource;
+
+ resource = to_power_resource(to_acpi_device(dev));
+ return sprintf(buf, "%u\n", !!resource->ref_count);
+}
+static DEVICE_ATTR_RO(resource_in_use);
+
+static void acpi_power_sysfs_remove(struct acpi_device *device)
+{
+ device_remove_file(&device->dev, &dev_attr_resource_in_use);
+}
+
+static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource)
+{
+ mutex_lock(&power_resource_list_lock);
+
+ if (!list_empty(&acpi_power_resource_list)) {
+ struct acpi_power_resource *r;
+
+ list_for_each_entry(r, &acpi_power_resource_list, list_node)
+ if (r->order > resource->order) {
+ list_add_tail(&resource->list_node, &r->list_node);
+ goto out;
+ }
+ }
+ list_add_tail(&resource->list_node, &acpi_power_resource_list);
+
+ out:
+ mutex_unlock(&power_resource_list_lock);
+}
+
+struct acpi_device *acpi_add_power_resource(acpi_handle handle)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+ struct acpi_power_resource *resource;
+ union acpi_object acpi_object;
+ struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object };
+ acpi_status status;
+ u8 state_dummy;
+ int result;
+
+ if (device)
+ return device;
+
+ resource = kzalloc(sizeof(*resource), GFP_KERNEL);
+ if (!resource)
+ return NULL;
+
+ device = &resource->device;
+ 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);
+ 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);
+ if (ACPI_FAILURE(status))
+ goto err;
+
+ resource->system_level = acpi_object.power_resource.system_level;
+ resource->order = acpi_object.power_resource.resource_order;
+ resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN;
+
+ /* Get the initial state or just flip it on if that fails. */
+ 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));
+
+ result = acpi_tie_acpi_dev(device);
+ if (result)
+ goto err;
+
+ result = acpi_device_add(device);
+ if (result)
+ goto err;
+
+ if (!device_create_file(&device->dev, &dev_attr_resource_in_use))
+ device->remove = acpi_power_sysfs_remove;
+
+ acpi_power_add_resource_to_list(resource);
+ acpi_device_add_finalize(device);
+ return device;
+
+ err:
+ acpi_release_power_resource(&device->dev);
+ return NULL;
+}
+
+#ifdef CONFIG_ACPI_SLEEP
+void acpi_resume_power_resources(void)
+{
+ struct acpi_power_resource *resource;
+
+ mutex_lock(&power_resource_list_lock);
+
+ list_for_each_entry(resource, &acpi_power_resource_list, list_node) {
+ int result;
+ u8 state;
+
+ mutex_lock(&resource->resource_lock);
+
+ resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN;
+ result = acpi_power_get_state(resource, &state);
+ if (result) {
+ mutex_unlock(&resource->resource_lock);
+ continue;
+ }
+
+ if (state == ACPI_POWER_RESOURCE_STATE_OFF
+ && resource->ref_count) {
+ acpi_handle_debug(resource->device.handle, "Turning ON\n");
+ __acpi_power_on(resource);
+ }
+
+ mutex_unlock(&resource->resource_lock);
+ }
+
+ mutex_unlock(&power_resource_list_lock);
+}
+#endif
+
+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.
+ */
+void acpi_turn_off_unused_power_resources(void)
+{
+ struct acpi_power_resource *resource;
+
+ if (dmi_check_system(dmi_leave_unused_power_resources_on))
+ return;
+
+ mutex_lock(&power_resource_list_lock);
+
+ list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) {
+ mutex_lock(&resource->resource_lock);
+
+ if (!resource->ref_count &&
+ resource->state == ACPI_POWER_RESOURCE_STATE_ON) {
+ acpi_handle_debug(resource->device.handle, "Turning OFF\n");
+ __acpi_power_off(resource);
+ }
+
+ mutex_unlock(&resource->resource_lock);
+ }
+
+ mutex_unlock(&power_resource_list_lock);
+}
diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c
new file mode 100644
index 000000000..ced3eb15b
--- /dev/null
+++ b/drivers/acpi/pptt.c
@@ -0,0 +1,799 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * pptt.c - parsing of Processor Properties Topology Table (PPTT)
+ *
+ * Copyright (C) 2018, ARM
+ *
+ * This file implements parsing of the Processor Properties Topology Table
+ * which is optionally used to describe the processor and cache topology.
+ * Due to the relative pointers used throughout the table, this doesn't
+ * leverage the existing subtable parsing in the kernel.
+ *
+ * The PPTT structure is an inverted tree, with each node potentially
+ * holding one or two inverted tree data structures describing
+ * the caches available at that level. Each cache structure optionally
+ * contains properties describing the cache at a given level which can be
+ * used to override hardware probed values.
+ */
+#define pr_fmt(fmt) "ACPI PPTT: " fmt
+
+#include <linux/acpi.h>
+#include <linux/cacheinfo.h>
+#include <acpi/processor.h>
+
+static struct acpi_subtable_header *fetch_pptt_subtable(struct acpi_table_header *table_hdr,
+ u32 pptt_ref)
+{
+ struct acpi_subtable_header *entry;
+
+ /* there isn't a subtable at reference 0 */
+ if (pptt_ref < sizeof(struct acpi_subtable_header))
+ return NULL;
+
+ if (pptt_ref + sizeof(struct acpi_subtable_header) > table_hdr->length)
+ return NULL;
+
+ entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, pptt_ref);
+
+ if (entry->length == 0)
+ return NULL;
+
+ if (pptt_ref + entry->length > table_hdr->length)
+ return NULL;
+
+ return entry;
+}
+
+static struct acpi_pptt_processor *fetch_pptt_node(struct acpi_table_header *table_hdr,
+ u32 pptt_ref)
+{
+ return (struct acpi_pptt_processor *)fetch_pptt_subtable(table_hdr, pptt_ref);
+}
+
+static struct acpi_pptt_cache *fetch_pptt_cache(struct acpi_table_header *table_hdr,
+ u32 pptt_ref)
+{
+ return (struct acpi_pptt_cache *)fetch_pptt_subtable(table_hdr, pptt_ref);
+}
+
+static struct acpi_subtable_header *acpi_get_pptt_resource(struct acpi_table_header *table_hdr,
+ struct acpi_pptt_processor *node,
+ int resource)
+{
+ u32 *ref;
+
+ if (resource >= node->number_of_priv_resources)
+ return NULL;
+
+ ref = ACPI_ADD_PTR(u32, node, sizeof(struct acpi_pptt_processor));
+ ref += resource;
+
+ return fetch_pptt_subtable(table_hdr, *ref);
+}
+
+static inline bool acpi_pptt_match_type(int table_type, int type)
+{
+ return ((table_type & ACPI_PPTT_MASK_CACHE_TYPE) == type ||
+ table_type & ACPI_PPTT_CACHE_TYPE_UNIFIED & 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
+ * @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
+ * @type: the requested cache type
+ *
+ * Attempt to find a given cache level, while counting the max number
+ * of cache levels for the cache node.
+ *
+ * Given a pptt resource, verify that it is a cache node, then walk
+ * down each level of caches, counting how many levels are found
+ * as well as checking the cache type (icache, dcache, unified). If a
+ * level & type match, then we set found, and continue the search.
+ * Once the entire cache branch has been walked return its max
+ * depth.
+ *
+ * Return: The cache structure and the level we terminated with.
+ */
+static unsigned int acpi_pptt_walk_cache(struct acpi_table_header *table_hdr,
+ unsigned int local_level,
+ struct acpi_subtable_header *res,
+ struct acpi_pptt_cache **found,
+ unsigned int level, int type)
+{
+ struct acpi_pptt_cache *cache;
+
+ if (res->type != ACPI_PPTT_TYPE_CACHE)
+ return 0;
+
+ cache = (struct acpi_pptt_cache *) res;
+ while (cache) {
+ 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");
+
+ pr_debug("Found cache @ level %u\n", level);
+ *found = cache;
+ /*
+ * continue looking at this node's resource list
+ * to verify that we don't find a duplicate
+ * cache node.
+ */
+ }
+ cache = fetch_pptt_cache(table_hdr, cache->next_level_of_cache);
+ }
+ return local_level;
+}
+
+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)
+{
+ struct acpi_subtable_header *res;
+ unsigned int number_of_levels = *starting_level;
+ int resource = 0;
+ struct acpi_pptt_cache *ret = NULL;
+ unsigned int local_level;
+
+ /* walk down from processor node */
+ while ((res = acpi_get_pptt_resource(table_hdr, cpu_node, resource))) {
+ resource++;
+
+ local_level = acpi_pptt_walk_cache(table_hdr, *starting_level,
+ res, &ret, level, type);
+ /*
+ * we are looking for the max depth. Since its potentially
+ * possible for a given node to have resources with differing
+ * depths verify that the depth we have found is the largest.
+ */
+ if (number_of_levels < local_level)
+ number_of_levels = local_level;
+ }
+ if (number_of_levels > *starting_level)
+ *starting_level = number_of_levels;
+
+ return ret;
+}
+
+/**
+ * acpi_count_levels() - Given a PPTT table, and a CPU node, count the caches
+ * @table_hdr: Pointer to the head of the PPTT table
+ * @cpu_node: processor node we wish to count caches for
+ *
+ * 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.
+ */
+static int acpi_count_levels(struct acpi_table_header *table_hdr,
+ struct acpi_pptt_processor *cpu_node)
+{
+ int total_levels = 0;
+
+ do {
+ acpi_find_cache_level(table_hdr, cpu_node, &total_levels, 0, 0);
+ cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent);
+ } while (cpu_node);
+
+ return total_levels;
+}
+
+/**
+ * acpi_pptt_leaf_node() - Given a processor node, determine if its a leaf
+ * @table_hdr: Pointer to the head of the PPTT table
+ * @node: passed node is checked to see if its a leaf
+ *
+ * Determine if the *node parameter is a leaf node by iterating the
+ * PPTT table, looking for nodes which reference it.
+ *
+ * Return: 0 if we find a node referencing the passed node (or table error),
+ * or 1 if we don't.
+ */
+static int acpi_pptt_leaf_node(struct acpi_table_header *table_hdr,
+ struct acpi_pptt_processor *node)
+{
+ struct acpi_subtable_header *entry;
+ unsigned long table_end;
+ u32 node_entry;
+ struct acpi_pptt_processor *cpu_node;
+ u32 proc_sz;
+
+ if (table_hdr->revision > 1)
+ return (node->flags & ACPI_PPTT_ACPI_LEAF_NODE);
+
+ table_end = (unsigned long)table_hdr + table_hdr->length;
+ 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 *);
+
+ 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;
+}
+
+/**
+ * acpi_find_processor_node() - Given a PPTT table find the requested processor
+ * @table_hdr: Pointer to the head of the PPTT table
+ * @acpi_cpu_id: CPU we are searching for
+ *
+ * Find the subtable entry describing the provided processor.
+ * This is done by iterating the PPTT table looking for processor nodes
+ * which have an acpi_processor_id that matches the acpi_cpu_id parameter
+ * passed into the function. If we find a node that matches this criteria
+ * we verify that its a leaf node in the topology rather than depending
+ * on the valid flag, which doesn't need to be set for leaf nodes.
+ *
+ * Return: NULL, or the processors acpi_pptt_processor*
+ */
+static struct acpi_pptt_processor *acpi_find_processor_node(struct acpi_table_header *table_hdr,
+ u32 acpi_cpu_id)
+{
+ struct acpi_subtable_header *entry;
+ unsigned long table_end;
+ struct acpi_pptt_processor *cpu_node;
+ u32 proc_sz;
+
+ 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 *);
+
+ /* find the processor structure associated with this cpuid */
+ 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;
+ }
+ if (entry->type == ACPI_PPTT_TYPE_PROCESSOR &&
+ acpi_cpu_id == cpu_node->acpi_processor_id &&
+ acpi_pptt_leaf_node(table_hdr, cpu_node)) {
+ return (struct acpi_pptt_processor *)entry;
+ }
+
+ entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry,
+ entry->length);
+ }
+
+ 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) {
+ case CACHE_TYPE_DATA:
+ pr_debug("Looking for data cache\n");
+ return ACPI_PPTT_CACHE_TYPE_DATA;
+ case CACHE_TYPE_INST:
+ pr_debug("Looking for instruction cache\n");
+ return ACPI_PPTT_CACHE_TYPE_INSTR;
+ default:
+ case CACHE_TYPE_UNIFIED:
+ pr_debug("Looking for unified cache\n");
+ /*
+ * It is important that ACPI_PPTT_CACHE_TYPE_UNIFIED
+ * contains the bit pattern that will match both
+ * ACPI unified bit patterns because we use it later
+ * to match both cases.
+ */
+ return ACPI_PPTT_CACHE_TYPE_UNIFIED;
+ }
+}
+
+static struct acpi_pptt_cache *acpi_find_cache_node(struct acpi_table_header *table_hdr,
+ u32 acpi_cpu_id,
+ enum cache_type type,
+ unsigned int level,
+ struct acpi_pptt_processor **node)
+{
+ unsigned int total_levels = 0;
+ struct acpi_pptt_cache *found = NULL;
+ struct acpi_pptt_processor *cpu_node;
+ u8 acpi_type = acpi_cache_type(type);
+
+ pr_debug("Looking for CPU %d's level %u cache type %d\n",
+ acpi_cpu_id, level, acpi_type);
+
+ cpu_node = acpi_find_processor_node(table_hdr, acpi_cpu_id);
+
+ while (cpu_node && !found) {
+ found = acpi_find_cache_level(table_hdr, cpu_node,
+ &total_levels, level, acpi_type);
+ *node = cpu_node;
+ cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent);
+ }
+
+ return found;
+}
+
+/**
+ * update_cache_properties() - Update cacheinfo for the given processor
+ * @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
+ * set fields that we determine are VALID.
+ *
+ * Return: nothing. Side effect of updating the global cacheinfo
+ */
+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_cache_v1* found_cache_v1;
+
+ this_leaf->fw_token = cpu_node;
+ if (found_cache->flags & ACPI_PPTT_SIZE_PROPERTY_VALID)
+ this_leaf->size = found_cache->size;
+ if (found_cache->flags & ACPI_PPTT_LINE_SIZE_VALID)
+ this_leaf->coherency_line_size = found_cache->line_size;
+ if (found_cache->flags & ACPI_PPTT_NUMBER_OF_SETS_VALID)
+ this_leaf->number_of_sets = found_cache->number_of_sets;
+ if (found_cache->flags & ACPI_PPTT_ASSOCIATIVITY_VALID)
+ this_leaf->ways_of_associativity = found_cache->associativity;
+ if (found_cache->flags & ACPI_PPTT_WRITE_POLICY_VALID) {
+ switch (found_cache->attributes & ACPI_PPTT_MASK_WRITE_POLICY) {
+ case ACPI_PPTT_CACHE_POLICY_WT:
+ this_leaf->attributes = CACHE_WRITE_THROUGH;
+ break;
+ case ACPI_PPTT_CACHE_POLICY_WB:
+ this_leaf->attributes = CACHE_WRITE_BACK;
+ break;
+ }
+ }
+ if (found_cache->flags & ACPI_PPTT_ALLOCATION_TYPE_VALID) {
+ switch (found_cache->attributes & ACPI_PPTT_MASK_ALLOCATION_TYPE) {
+ case ACPI_PPTT_CACHE_READ_ALLOCATE:
+ this_leaf->attributes |= CACHE_READ_ALLOCATE;
+ break;
+ case ACPI_PPTT_CACHE_WRITE_ALLOCATE:
+ this_leaf->attributes |= CACHE_WRITE_ALLOCATE;
+ break;
+ case ACPI_PPTT_CACHE_RW_ALLOCATE:
+ case ACPI_PPTT_CACHE_RW_ALLOCATE_ALT:
+ this_leaf->attributes |=
+ CACHE_READ_ALLOCATE | CACHE_WRITE_ALLOCATE;
+ break;
+ }
+ }
+ /*
+ * If cache type is NOCACHE, then the cache hasn't been specified
+ * via other mechanisms. Update the type if a cache type has been
+ * provided.
+ *
+ * Note, we assume such caches are unified based on conventional system
+ * design and known examples. Significant work is required elsewhere to
+ * fully support data/instruction only type caches which are only
+ * specified in PPTT.
+ */
+ if (this_leaf->type == CACHE_TYPE_NOCACHE &&
+ 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));
+ this_leaf->id = found_cache_v1->cache_id;
+ this_leaf->attributes |= CACHE_ID;
+ }
+}
+
+static void cache_setup_acpi_cpu(struct acpi_table_header *table,
+ unsigned int cpu)
+{
+ struct acpi_pptt_cache *found_cache;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
+ struct cacheinfo *this_leaf;
+ unsigned int index = 0;
+ struct acpi_pptt_processor *cpu_node = NULL;
+
+ while (index < get_cpu_cacheinfo(cpu)->num_leaves) {
+ this_leaf = this_cpu_ci->info_list + index;
+ found_cache = acpi_find_cache_node(table, acpi_cpu_id,
+ this_leaf->type,
+ this_leaf->level,
+ &cpu_node);
+ 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);
+
+ index++;
+ }
+}
+
+static bool flag_identical(struct acpi_table_header *table_hdr,
+ struct acpi_pptt_processor *cpu)
+{
+ struct acpi_pptt_processor *next;
+
+ /* heterogeneous machines must use PPTT revision > 1 */
+ if (table_hdr->revision < 2)
+ return false;
+
+ /* Locate the last node in the tree with IDENTICAL set */
+ if (cpu->flags & ACPI_PPTT_ACPI_IDENTICAL) {
+ next = fetch_pptt_node(table_hdr, cpu->parent);
+ if (!(next && next->flags & ACPI_PPTT_ACPI_IDENTICAL))
+ return true;
+ }
+
+ return false;
+}
+
+/* Passing level values greater than this will result in search termination */
+#define PPTT_ABORT_PACKAGE 0xFF
+
+static struct acpi_pptt_processor *acpi_find_processor_tag(struct acpi_table_header *table_hdr,
+ struct acpi_pptt_processor *cpu,
+ int level, int flag)
+{
+ struct acpi_pptt_processor *prev_node;
+
+ while (cpu && level) {
+ /* special case the identical flag to find last identical */
+ if (flag == ACPI_PPTT_ACPI_IDENTICAL) {
+ if (flag_identical(table_hdr, cpu))
+ break;
+ } else if (cpu->flags & flag)
+ break;
+ pr_debug("level %d\n", level);
+ prev_node = fetch_pptt_node(table_hdr, cpu->parent);
+ if (prev_node == NULL)
+ break;
+ cpu = prev_node;
+ level--;
+ }
+ return cpu;
+}
+
+static void acpi_pptt_warn_missing(void)
+{
+ pr_warn_once("No PPTT table found, CPU and cache topology may be inaccurate\n");
+}
+
+/**
+ * topology_get_acpi_cpu_tag() - Find a unique topology value for a feature
+ * @table: Pointer to the head of the PPTT table
+ * @cpu: Kernel logical CPU number
+ * @level: A level that terminates the search
+ * @flag: A flag which terminates the search
+ *
+ * Get a unique value given a CPU, and a topology level, that can be
+ * matched to determine which cpus share common topological features
+ * at that level.
+ *
+ * Return: Unique value, or -ENOENT if unable to locate CPU
+ */
+static int topology_get_acpi_cpu_tag(struct acpi_table_header *table,
+ unsigned int cpu, int level, int flag)
+{
+ struct acpi_pptt_processor *cpu_node;
+ u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
+
+ cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
+ if (cpu_node) {
+ cpu_node = acpi_find_processor_tag(table, cpu_node,
+ level, flag);
+ /*
+ * As per specification if the processor structure represents
+ * an actual processor, then ACPI processor ID must be valid.
+ * For processor containers ACPI_PPTT_ACPI_PROCESSOR_ID_VALID
+ * should be set if the UID is valid
+ */
+ if (level == 0 ||
+ cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID)
+ return cpu_node->acpi_processor_id;
+ return ACPI_PTR_DIFF(cpu_node, table);
+ }
+ pr_warn_once("PPTT table found, but unable to locate core %d (%d)\n",
+ cpu, acpi_cpu_id);
+ return -ENOENT;
+}
+
+
+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 && !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;
+}
+
+static int find_acpi_cpu_topology_tag(unsigned int cpu, int level, int flag)
+{
+ struct acpi_table_header *table;
+ int retval;
+
+ table = acpi_get_pptt();
+ if (!table)
+ return -ENOENT;
+
+ retval = topology_get_acpi_cpu_tag(table, cpu, level, flag);
+ pr_debug("Topology Setup ACPI CPU %d, level %d ret = %d\n",
+ cpu, level, retval);
+
+ return retval;
+}
+
+/**
+ * check_acpi_cpu_flag() - Determine if CPU node has a flag set
+ * @cpu: Kernel logical CPU number
+ * @rev: The minimum PPTT revision defining the flag
+ * @flag: The flag itself
+ *
+ * Check the node representing a CPU for a given flag.
+ *
+ * Return: -ENOENT if the PPTT doesn't exist, the CPU cannot be found or
+ * the table revision isn't new enough.
+ * 1, any passed flag set
+ * 0, flag unset
+ */
+static int check_acpi_cpu_flag(unsigned int cpu, int rev, u32 flag)
+{
+ struct acpi_table_header *table;
+ u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
+ struct acpi_pptt_processor *cpu_node = NULL;
+ int ret = -ENOENT;
+
+ table = acpi_get_pptt();
+ if (!table)
+ return -ENOENT;
+
+ if (table->revision >= rev)
+ cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
+
+ if (cpu_node)
+ ret = (cpu_node->flags & flag) != 0;
+
+ return ret;
+}
+
+/**
+ * acpi_find_last_cache_level() - Determines the number of cache levels for a PE
+ * @cpu: Kernel logical CPU number
+ *
+ * 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.
+ */
+int acpi_find_last_cache_level(unsigned int cpu)
+{
+ u32 acpi_cpu_id;
+ struct acpi_table_header *table;
+ int number_of_levels = 0;
+
+ table = acpi_get_pptt();
+ if (!table)
+ return -ENOENT;
+
+ pr_debug("Cache Setup find last level 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);
+
+ return number_of_levels;
+}
+
+/**
+ * cache_setup_acpi() - Override CPU cache topology with data from the PPTT
+ * @cpu: Kernel logical CPU number
+ *
+ * Updates the global cache info provided by cpu_get_cacheinfo()
+ * when there are valid properties in the acpi_pptt_cache nodes. A
+ * successful parse may not result in any updates if none of the
+ * cache levels have any valid flags set. Further, a unique value is
+ * associated with each known CPU cache entry. This unique value
+ * can be used to determine whether caches are shared between CPUs.
+ *
+ * Return: -ENOENT on failure to find table, or 0 on success
+ */
+int cache_setup_acpi(unsigned int cpu)
+{
+ struct acpi_table_header *table;
+
+ table = acpi_get_pptt();
+ if (!table)
+ return -ENOENT;
+
+ pr_debug("Cache Setup ACPI CPU %d\n", cpu);
+
+ cache_setup_acpi_cpu(table, cpu);
+
+ return 0;
+}
+
+/**
+ * acpi_pptt_cpu_is_thread() - Determine if CPU is a thread
+ * @cpu: Kernel logical CPU number
+ *
+ * Return: 1, a thread
+ * 0, not a thread
+ * -ENOENT ,if the PPTT doesn't exist, the CPU cannot be found or
+ * the table revision isn't new enough.
+ */
+int acpi_pptt_cpu_is_thread(unsigned int cpu)
+{
+ return check_acpi_cpu_flag(cpu, 2, ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD);
+}
+
+/**
+ * find_acpi_cpu_topology() - Determine a unique topology value for a given CPU
+ * @cpu: Kernel logical CPU number
+ * @level: The topological level for which we would like a unique ID
+ *
+ * Determine a topology unique ID for each thread/core/cluster/mc_grouping
+ * /socket/etc. This ID can then be used to group peers, which will have
+ * matching ids.
+ *
+ * The search terminates when either the requested level is found or
+ * we reach a root node. Levels beyond the termination point will return the
+ * same unique ID. The unique id for level 0 is the acpi processor id. All
+ * other levels beyond this use a generated value to uniquely identify
+ * a topological feature.
+ *
+ * Return: -ENOENT if the PPTT doesn't exist, or the CPU cannot be found.
+ * Otherwise returns a value which represents a unique topological feature.
+ */
+int find_acpi_cpu_topology(unsigned int cpu, int level)
+{
+ return find_acpi_cpu_topology_tag(cpu, level, 0);
+}
+
+/**
+ * find_acpi_cpu_topology_package() - Determine a unique CPU package value
+ * @cpu: Kernel logical CPU number
+ *
+ * Determine a topology unique package ID for the given CPU.
+ * This ID can then be used to group peers, which will have matching ids.
+ *
+ * The search terminates when either a level is found with the PHYSICAL_PACKAGE
+ * flag set or we reach a root node.
+ *
+ * Return: -ENOENT if the PPTT doesn't exist, or the CPU cannot be found.
+ * Otherwise returns a value which represents the package for this CPU.
+ */
+int find_acpi_cpu_topology_package(unsigned int cpu)
+{
+ return find_acpi_cpu_topology_tag(cpu, PPTT_ABORT_PACKAGE,
+ ACPI_PPTT_PHYSICAL_PACKAGE);
+}
+
+/**
+ * find_acpi_cpu_topology_cluster() - Determine a unique CPU cluster value
+ * @cpu: Kernel logical CPU number
+ *
+ * Determine a topology unique cluster ID for the given CPU/thread.
+ * This ID can then be used to group peers, which will have matching ids.
+ *
+ * The cluster, if present is the level of topology above CPUs. In a
+ * multi-thread CPU, it will be the level above the CPU, not the thread.
+ * It may not exist in single CPU systems. In simple multi-CPU systems,
+ * it may be equal to the package topology level.
+ *
+ * Return: -ENOENT if the PPTT doesn't exist, the CPU cannot be found
+ * or there is no toplogy level above the CPU..
+ * Otherwise returns a value which represents the package for this CPU.
+ */
+
+int find_acpi_cpu_topology_cluster(unsigned int cpu)
+{
+ struct acpi_table_header *table;
+ struct acpi_pptt_processor *cpu_node, *cluster_node;
+ u32 acpi_cpu_id;
+ int retval;
+ int is_thread;
+
+ table = acpi_get_pptt();
+ if (!table)
+ return -ENOENT;
+
+ acpi_cpu_id = get_acpi_id_for_cpu(cpu);
+ cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
+ if (!cpu_node || !cpu_node->parent)
+ return -ENOENT;
+
+ is_thread = cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD;
+ cluster_node = fetch_pptt_node(table, cpu_node->parent);
+ if (!cluster_node)
+ return -ENOENT;
+
+ if (is_thread) {
+ if (!cluster_node->parent)
+ return -ENOENT;
+
+ cluster_node = fetch_pptt_node(table, cluster_node->parent);
+ if (!cluster_node)
+ return -ENOENT;
+ }
+ if (cluster_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID)
+ retval = cluster_node->acpi_processor_id;
+ else
+ retval = ACPI_PTR_DIFF(cluster_node, table);
+
+ return retval;
+}
+
+/**
+ * find_acpi_cpu_topology_hetero_id() - Get a core architecture tag
+ * @cpu: Kernel logical CPU number
+ *
+ * Determine a unique heterogeneous tag for the given CPU. CPUs with the same
+ * implementation should have matching tags.
+ *
+ * The returned tag can be used to group peers with identical implementation.
+ *
+ * The search terminates when a level is found with the identical implementation
+ * flag set or we reach a root node.
+ *
+ * Due to limitations in the PPTT data structure, there may be rare situations
+ * where two cores in a heterogeneous machine may be identical, but won't have
+ * the same tag.
+ *
+ * Return: -ENOENT if the PPTT doesn't exist, or the CPU cannot be found.
+ * Otherwise returns a value which represents a group of identical cores
+ * similar to this CPU.
+ */
+int find_acpi_cpu_topology_hetero_id(unsigned int cpu)
+{
+ return find_acpi_cpu_topology_tag(cpu, PPTT_ABORT_PACKAGE,
+ ACPI_PPTT_ACPI_IDENTICAL);
+}
diff --git a/drivers/acpi/prmt.c b/drivers/acpi/prmt.c
new file mode 100644
index 000000000..3d4c4620f
--- /dev/null
+++ b/drivers/acpi/prmt.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Author: Erik Kaneda <erik.kaneda@intel.com>
+ * Copyright 2020 Intel Corporation
+ *
+ * prmt.c
+ *
+ * Each PRM service is an executable that is run in a restricted environment
+ * that is invoked by writing to the PlatformRtMechanism OperationRegion from
+ * AML bytecode.
+ *
+ * init_prmt initializes the Platform Runtime Mechanism (PRM) services by
+ * processing data in the PRMT as well as registering an ACPI OperationRegion
+ * handler for the PlatformRtMechanism subtype.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/efi.h>
+#include <linux/acpi.h>
+#include <linux/prmt.h>
+#include <asm/efi.h>
+
+#pragma pack(1)
+struct prm_mmio_addr_range {
+ u64 phys_addr;
+ u64 virt_addr;
+ u32 length;
+};
+
+struct prm_mmio_info {
+ u64 mmio_count;
+ struct prm_mmio_addr_range addr_ranges[];
+};
+
+struct prm_buffer {
+ u8 prm_status;
+ u64 efi_status;
+ u8 prm_cmd;
+ guid_t handler_guid;
+};
+
+struct prm_context_buffer {
+ char signature[ACPI_NAMESEG_SIZE];
+ u16 revision;
+ u16 reserved;
+ guid_t identifier;
+ u64 static_data_buffer;
+ struct prm_mmio_info *mmio_ranges;
+};
+#pragma pack()
+
+static LIST_HEAD(prm_module_list);
+
+struct prm_handler_info {
+ guid_t guid;
+ void *handler_addr;
+ u64 static_data_buffer_addr;
+ u64 acpi_param_buffer_addr;
+
+ struct list_head handler_list;
+};
+
+struct prm_module_info {
+ guid_t guid;
+ u16 major_rev;
+ u16 minor_rev;
+ u16 handler_count;
+ struct prm_mmio_info *mmio_info;
+ bool updatable;
+
+ struct list_head module_list;
+ struct prm_handler_info handlers[];
+};
+
+static u64 efi_pa_va_lookup(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)
+ return pa_offset + md->virt_addr + page - md->phys_addr;
+ }
+
+ return 0;
+}
+
+#define get_first_handler(a) ((struct acpi_prmt_handler_info *) ((char *) (a) + a->handler_info_offset))
+#define get_next_handler(a) ((struct acpi_prmt_handler_info *) (sizeof(struct acpi_prmt_handler_info) + (char *) a))
+
+static int __init
+acpi_parse_prmt(union acpi_subtable_headers *header, const unsigned long end)
+{
+ struct acpi_prmt_module_info *module_info;
+ struct acpi_prmt_handler_info *handler_info;
+ struct prm_handler_info *th;
+ struct prm_module_info *tm;
+ u64 *mmio_count;
+ u64 cur_handler = 0;
+ u32 module_info_size = 0;
+ u64 mmio_range_size = 0;
+ void *temp_mmio;
+
+ module_info = (struct acpi_prmt_module_info *) header;
+ module_info_size = struct_size(tm, handlers, module_info->handler_info_count);
+ tm = kmalloc(module_info_size, GFP_KERNEL);
+ if (!tm)
+ goto parse_prmt_out1;
+
+ guid_copy(&tm->guid, (guid_t *) module_info->module_guid);
+ tm->major_rev = module_info->major_rev;
+ tm->minor_rev = module_info->minor_rev;
+ tm->handler_count = module_info->handler_info_count;
+ tm->updatable = true;
+
+ if (module_info->mmio_list_pointer) {
+ /*
+ * Each module is associated with a list of addr
+ * ranges that it can use during the service
+ */
+ mmio_count = (u64 *) memremap(module_info->mmio_list_pointer, 8, MEMREMAP_WB);
+ if (!mmio_count)
+ goto parse_prmt_out2;
+
+ mmio_range_size = struct_size(tm->mmio_info, addr_ranges, *mmio_count);
+ tm->mmio_info = kmalloc(mmio_range_size, GFP_KERNEL);
+ if (!tm->mmio_info)
+ goto parse_prmt_out3;
+
+ temp_mmio = memremap(module_info->mmio_list_pointer, mmio_range_size, MEMREMAP_WB);
+ if (!temp_mmio)
+ goto parse_prmt_out4;
+ memmove(tm->mmio_info, temp_mmio, mmio_range_size);
+ } else {
+ tm->mmio_info = kmalloc(sizeof(*tm->mmio_info), GFP_KERNEL);
+ if (!tm->mmio_info)
+ goto parse_prmt_out2;
+
+ tm->mmio_info->mmio_count = 0;
+ }
+
+ INIT_LIST_HEAD(&tm->module_list);
+ list_add(&tm->module_list, &prm_module_list);
+
+ handler_info = get_first_handler(module_info);
+ do {
+ 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);
+ } while (++cur_handler < tm->handler_count && (handler_info = get_next_handler(handler_info)));
+
+ return 0;
+
+parse_prmt_out4:
+ kfree(tm->mmio_info);
+parse_prmt_out3:
+ memunmap(mmio_count);
+parse_prmt_out2:
+ kfree(tm);
+parse_prmt_out1:
+ return -ENOMEM;
+}
+
+#define GET_MODULE 0
+#define GET_HANDLER 1
+
+static void *find_guid_info(const guid_t *guid, u8 mode)
+{
+ struct prm_handler_info *cur_handler;
+ struct prm_module_info *cur_module;
+ int i = 0;
+
+ list_for_each_entry(cur_module, &prm_module_list, module_list) {
+ for (i = 0; i < cur_module->handler_count; ++i) {
+ cur_handler = &cur_module->handlers[i];
+ if (guid_equal(guid, &cur_handler->guid)) {
+ if (mode == GET_MODULE)
+ return (void *)cur_module;
+ else
+ return (void *)cur_handler;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static struct prm_module_info *find_prm_module(const guid_t *guid)
+{
+ return (struct prm_module_info *)find_guid_info(guid, GET_MODULE);
+}
+
+static struct prm_handler_info *find_prm_handler(const guid_t *guid)
+{
+ return (struct prm_handler_info *) find_guid_info(guid, GET_HANDLER);
+}
+
+/* In-coming PRM commands */
+
+#define PRM_CMD_RUN_SERVICE 0
+#define PRM_CMD_START_TRANSACTION 1
+#define PRM_CMD_END_TRANSACTION 2
+
+/* statuses that can be passed back to ASL */
+
+#define PRM_HANDLER_SUCCESS 0
+#define PRM_HANDLER_ERROR 1
+#define INVALID_PRM_COMMAND 2
+#define PRM_HANDLER_GUID_NOT_FOUND 3
+#define UPDATE_LOCK_ALREADY_HELD 4
+#define UPDATE_UNLOCK_WITHOUT_LOCK 5
+
+/*
+ * This is the PlatformRtMechanism opregion space handler.
+ * @function: indicates the read/write. In fact as the PlatformRtMechanism
+ * message is driven by command, only write is meaningful.
+ *
+ * @addr : not used
+ * @bits : not used.
+ * @value : it is an in/out parameter. It points to the PRM message buffer.
+ * @handler_context: not used
+ */
+static acpi_status acpi_platformrt_space_handler(u32 function,
+ acpi_physical_address addr,
+ u32 bits, acpi_integer *value,
+ void *handler_context,
+ void *region_context)
+{
+ struct prm_buffer *buffer = ACPI_CAST_PTR(struct prm_buffer, value);
+ struct prm_handler_info *handler;
+ struct prm_module_info *module;
+ 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.
+ */
+ switch (buffer->prm_cmd) {
+ case PRM_CMD_RUN_SERVICE:
+
+ handler = find_prm_handler(&buffer->handler_guid);
+ module = find_prm_module(&buffer->handler_guid);
+ if (!handler || !module)
+ goto invalid_guid;
+
+ ACPI_COPY_NAMESEG(context.signature, "PRMC");
+ context.revision = 0x0;
+ context.reserved = 0x0;
+ context.identifier = handler->guid;
+ 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);
+ if (status == EFI_SUCCESS) {
+ buffer->prm_status = PRM_HANDLER_SUCCESS;
+ } else {
+ buffer->prm_status = PRM_HANDLER_ERROR;
+ buffer->efi_status = status;
+ }
+ break;
+
+ case PRM_CMD_START_TRANSACTION:
+
+ module = find_prm_module(&buffer->handler_guid);
+ if (!module)
+ goto invalid_guid;
+
+ if (module->updatable)
+ module->updatable = false;
+ else
+ buffer->prm_status = UPDATE_LOCK_ALREADY_HELD;
+ break;
+
+ case PRM_CMD_END_TRANSACTION:
+
+ module = find_prm_module(&buffer->handler_guid);
+ if (!module)
+ goto invalid_guid;
+
+ if (module->updatable)
+ buffer->prm_status = UPDATE_UNLOCK_WITHOUT_LOCK;
+ else
+ module->updatable = true;
+ break;
+
+ default:
+
+ buffer->prm_status = INVALID_PRM_COMMAND;
+ break;
+ }
+
+ return AE_OK;
+
+invalid_guid:
+ buffer->prm_status = PRM_HANDLER_GUID_NOT_FOUND;
+ return AE_OK;
+}
+
+void __init init_prmt(void)
+{
+ struct acpi_table_header *tbl;
+ acpi_status status;
+ int mc;
+
+ status = acpi_get_table(ACPI_SIG_PRMT, 0, &tbl);
+ if (ACPI_FAILURE(status))
+ return;
+
+ mc = acpi_table_parse_entries(ACPI_SIG_PRMT, sizeof(struct acpi_table_prmt) +
+ sizeof (struct acpi_table_prmt_header),
+ 0, acpi_parse_prmt, 0);
+ acpi_put_table(tbl);
+ /*
+ * Return immediately if PRMT table is not present or no PRM module found.
+ */
+ if (mc <= 0)
+ return;
+
+ 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,
+ NULL, NULL);
+ if (ACPI_FAILURE(status))
+ pr_alert("PRM: OperationRegion handler could not be installed\n");
+}
diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c
new file mode 100644
index 000000000..4322f2da6
--- /dev/null
+++ b/drivers/acpi/proc.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/export.h>
+#include <linux/suspend.h>
+#include <linux/bcd.h>
+#include <linux/acpi.h>
+#include <linux/uaccess.h>
+
+#include "sleep.h"
+#include "internal.h"
+
+/*
+ * this file provides support for:
+ * /proc/acpi/wakeup
+ */
+
+static int
+acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
+{
+ struct acpi_device *dev, *tmp;
+
+ seq_printf(seq, "Device\tS-state\t Status Sysfs node\n");
+
+ mutex_lock(&acpi_device_lock);
+ list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list,
+ wakeup_list) {
+ struct acpi_device_physical_node *entry;
+
+ if (!dev->wakeup.flags.valid)
+ continue;
+
+ seq_printf(seq, "%s\t S%d\t",
+ dev->pnp.bus_id,
+ (u32) 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");
+ } else {
+ struct device *ldev;
+ list_for_each_entry(entry, &dev->physical_node_list,
+ node) {
+ ldev = get_device(entry->dev);
+ if (!ldev)
+ continue;
+
+ if (&entry->node !=
+ dev->physical_node_list.next)
+ seq_printf(seq, "\t\t");
+
+ seq_printf(seq, "%c%-8s %s:%s\n",
+ dev->wakeup.flags.valid ? '*' : ' ',
+ (device_may_wakeup(&dev->dev) ||
+ device_may_wakeup(ldev)) ?
+ "enabled" : "disabled",
+ ldev->bus ? ldev->bus->name :
+ "no-bus", dev_name(ldev));
+ put_device(ldev);
+ }
+ }
+
+ mutex_unlock(&dev->physical_node_lock);
+ }
+ mutex_unlock(&acpi_device_lock);
+ return 0;
+}
+
+static void physical_device_enable_wakeup(struct acpi_device *adev)
+{
+ struct acpi_device_physical_node *entry;
+
+ mutex_lock(&adev->physical_node_lock);
+
+ list_for_each_entry(entry,
+ &adev->physical_node_list, node)
+ if (entry->dev && device_can_wakeup(entry->dev)) {
+ bool enable = !device_may_wakeup(entry->dev);
+ device_set_wakeup_enable(entry->dev, enable);
+ }
+
+ mutex_unlock(&adev->physical_node_lock);
+}
+
+static ssize_t
+acpi_system_write_wakeup_device(struct file *file,
+ const char __user * buffer,
+ size_t count, loff_t * ppos)
+{
+ struct acpi_device *dev, *tmp;
+ char strbuf[5];
+ char str[5] = "";
+
+ if (count > 4)
+ count = 4;
+
+ if (copy_from_user(strbuf, buffer, count))
+ return -EFAULT;
+ strbuf[count] = '\0';
+ sscanf(strbuf, "%s", str);
+
+ mutex_lock(&acpi_device_lock);
+ list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list,
+ wakeup_list) {
+ if (!dev->wakeup.flags.valid)
+ continue;
+
+ if (!strncmp(dev->pnp.bus_id, str, 4)) {
+ if (device_can_wakeup(&dev->dev)) {
+ bool enable = !device_may_wakeup(&dev->dev);
+ device_set_wakeup_enable(&dev->dev, enable);
+ } else {
+ physical_device_enable_wakeup(dev);
+ }
+ break;
+ }
+ }
+ mutex_unlock(&acpi_device_lock);
+ return count;
+}
+
+static int
+acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, acpi_system_wakeup_device_seq_show,
+ pde_data(inode));
+}
+
+static const struct proc_ops acpi_system_wakeup_device_proc_ops = {
+ .proc_open = acpi_system_wakeup_device_open_fs,
+ .proc_read = seq_read,
+ .proc_write = acpi_system_write_wakeup_device,
+ .proc_lseek = seq_lseek,
+ .proc_release = single_release,
+};
+
+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);
+}
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
new file mode 100644
index 000000000..2ac48cda5
--- /dev/null
+++ b/drivers/acpi/processor_core.c
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2005 Intel Corporation
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Alex Chiang <achiang@hp.com>
+ * - Unified x86/ia64 implementations
+ *
+ * I/O APIC hotplug support
+ * Yinghai Lu <yinghai@kernel.org>
+ * Jiang Liu <jiang.liu@intel.com>
+ */
+#include <linux/export.h>
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+
+static struct acpi_table_madt *get_madt_table(void)
+{
+ static struct acpi_table_madt *madt;
+ static int read_madt;
+
+ if (!read_madt) {
+ if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0,
+ (struct acpi_table_header **)&madt)))
+ madt = NULL;
+ read_madt++;
+ }
+
+ return madt;
+}
+
+static int map_lapic_id(struct acpi_subtable_header *entry,
+ u32 acpi_id, phys_cpuid_t *apic_id)
+{
+ struct acpi_madt_local_apic *lapic =
+ container_of(entry, struct acpi_madt_local_apic, header);
+
+ if (!(lapic->lapic_flags & ACPI_MADT_ENABLED))
+ return -ENODEV;
+
+ if (lapic->processor_id != acpi_id)
+ return -EINVAL;
+
+ *apic_id = lapic->id;
+ return 0;
+}
+
+static int map_x2apic_id(struct acpi_subtable_header *entry,
+ int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id)
+{
+ struct acpi_madt_local_x2apic *apic =
+ container_of(entry, struct acpi_madt_local_x2apic, header);
+
+ if (!(apic->lapic_flags & ACPI_MADT_ENABLED))
+ return -ENODEV;
+
+ if (device_declaration && (apic->uid == acpi_id)) {
+ *apic_id = apic->local_apic_id;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int map_lsapic_id(struct acpi_subtable_header *entry,
+ int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id)
+{
+ struct acpi_madt_local_sapic *lsapic =
+ container_of(entry, struct acpi_madt_local_sapic, header);
+
+ if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED))
+ return -ENODEV;
+
+ if (device_declaration) {
+ if ((entry->length < 16) || (lsapic->uid != acpi_id))
+ return -EINVAL;
+ } else if (lsapic->processor_id != acpi_id)
+ return -EINVAL;
+
+ *apic_id = (lsapic->id << 8) | lsapic->eid;
+ return 0;
+}
+
+/*
+ * Retrieve the ARM CPU physical identifier (MPIDR)
+ */
+static int map_gicc_mpidr(struct acpi_subtable_header *entry,
+ int device_declaration, u32 acpi_id, phys_cpuid_t *mpidr)
+{
+ struct acpi_madt_generic_interrupt *gicc =
+ container_of(entry, struct acpi_madt_generic_interrupt, header);
+
+ if (!(gicc->flags & ACPI_MADT_ENABLED))
+ return -ENODEV;
+
+ /* device_declaration means Device object in DSDT, in the
+ * GIC interrupt model, logical processors are required to
+ * have a Processor Device object in the DSDT, so we should
+ * check device_declaration here
+ */
+ if (device_declaration && (gicc->uid == acpi_id)) {
+ *mpidr = gicc->arm_mpidr;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
+ int type, u32 acpi_id)
+{
+ unsigned long madt_end, entry;
+ phys_cpuid_t phys_id = PHYS_CPUID_INVALID; /* CPU hardware ID */
+
+ if (!madt)
+ return phys_id;
+
+ entry = (unsigned long)madt;
+ madt_end = entry + madt->header.length;
+
+ /* Parse all entries looking for a match. */
+
+ entry += sizeof(struct acpi_table_madt);
+ while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
+ struct acpi_subtable_header *header =
+ (struct acpi_subtable_header *)entry;
+ if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) {
+ if (!map_lapic_id(header, acpi_id, &phys_id))
+ break;
+ } else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) {
+ if (!map_x2apic_id(header, type, acpi_id, &phys_id))
+ break;
+ } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) {
+ if (!map_lsapic_id(header, type, acpi_id, &phys_id))
+ break;
+ } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
+ if (!map_gicc_mpidr(header, type, acpi_id, &phys_id))
+ break;
+ }
+ entry += header->length;
+ }
+ return phys_id;
+}
+
+phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id)
+{
+ struct acpi_table_madt *madt = NULL;
+ phys_cpuid_t rv;
+
+ acpi_get_table(ACPI_SIG_MADT, 0,
+ (struct acpi_table_header **)&madt);
+ if (!madt)
+ return PHYS_CPUID_INVALID;
+
+ rv = map_madt_entry(madt, 1, acpi_id);
+
+ acpi_put_table((struct acpi_table_header *)madt);
+
+ return rv;
+}
+
+static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ struct acpi_subtable_header *header;
+ phys_cpuid_t phys_id = PHYS_CPUID_INVALID;
+
+ if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
+ goto exit;
+
+ if (!buffer.length || !buffer.pointer)
+ goto exit;
+
+ obj = buffer.pointer;
+ if (obj->type != ACPI_TYPE_BUFFER ||
+ obj->buffer.length < sizeof(struct acpi_subtable_header)) {
+ goto exit;
+ }
+
+ header = (struct acpi_subtable_header *)obj->buffer.pointer;
+ if (header->type == ACPI_MADT_TYPE_LOCAL_APIC)
+ map_lapic_id(header, acpi_id, &phys_id);
+ else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC)
+ map_lsapic_id(header, type, acpi_id, &phys_id);
+ else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC)
+ 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);
+
+exit:
+ kfree(buffer.pointer);
+ return phys_id;
+}
+
+phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id)
+{
+ phys_cpuid_t phys_id;
+
+ phys_id = map_mat_entry(handle, type, acpi_id);
+ if (invalid_phys_cpuid(phys_id))
+ phys_id = map_madt_entry(get_madt_table(), type, acpi_id);
+
+ return phys_id;
+}
+EXPORT_SYMBOL_GPL(acpi_get_phys_id);
+
+int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
+{
+#ifdef CONFIG_SMP
+ int i;
+#endif
+
+ if (invalid_phys_cpuid(phys_id)) {
+ /*
+ * On UP processor, there is no _MAT or MADT table.
+ * So above phys_id is always set to PHYS_CPUID_INVALID.
+ *
+ * BIOS may define multiple CPU handles even for UP processor.
+ * For example,
+ *
+ * Scope (_PR)
+ * {
+ * Processor (CPU0, 0x00, 0x00000410, 0x06) {}
+ * Processor (CPU1, 0x01, 0x00000410, 0x06) {}
+ * Processor (CPU2, 0x02, 0x00000410, 0x06) {}
+ * Processor (CPU3, 0x03, 0x00000410, 0x06) {}
+ * }
+ *
+ * Ignores phys_id and always returns 0 for the processor
+ * handle with acpi id 0 if nr_cpu_ids is 1.
+ * This should be the case if SMP tables are not found.
+ * Return -EINVAL for other CPU's handle.
+ */
+ if (nr_cpu_ids <= 1 && acpi_id == 0)
+ return acpi_id;
+ else
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_SMP
+ for_each_possible_cpu(i) {
+ if (cpu_physical_id(i) == phys_id)
+ return i;
+ }
+#else
+ /* In UP kernel, only processor 0 is valid */
+ if (phys_id == 0)
+ return phys_id;
+#endif
+ return -ENODEV;
+}
+
+int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
+{
+ phys_cpuid_t phys_id;
+
+ phys_id = acpi_get_phys_id(handle, type, acpi_id);
+
+ return acpi_map_cpuid(phys_id, acpi_id);
+}
+EXPORT_SYMBOL_GPL(acpi_get_cpuid);
+
+#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
+static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base,
+ u64 *phys_addr, int *ioapic_id)
+{
+ struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry;
+
+ if (ioapic->global_irq_base != gsi_base)
+ return 0;
+
+ *phys_addr = ioapic->address;
+ *ioapic_id = ioapic->id;
+ return 1;
+}
+
+static int parse_madt_ioapic_entry(u32 gsi_base, u64 *phys_addr)
+{
+ struct acpi_subtable_header *hdr;
+ unsigned long madt_end, entry;
+ struct acpi_table_madt *madt;
+ int apic_id = -1;
+
+ madt = get_madt_table();
+ if (!madt)
+ return apic_id;
+
+ entry = (unsigned long)madt;
+ madt_end = entry + madt->header.length;
+
+ /* Parse all entries looking for a match. */
+ entry += sizeof(struct acpi_table_madt);
+ while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
+ hdr = (struct acpi_subtable_header *)entry;
+ if (hdr->type == ACPI_MADT_TYPE_IO_APIC &&
+ get_ioapic_id(hdr, gsi_base, phys_addr, &apic_id))
+ break;
+ else
+ entry += hdr->length;
+ }
+
+ return apic_id;
+}
+
+static int parse_mat_ioapic_entry(acpi_handle handle, u32 gsi_base,
+ u64 *phys_addr)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_subtable_header *header;
+ union acpi_object *obj;
+ int apic_id = -1;
+
+ if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
+ goto exit;
+
+ if (!buffer.length || !buffer.pointer)
+ goto exit;
+
+ obj = buffer.pointer;
+ if (obj->type != ACPI_TYPE_BUFFER ||
+ obj->buffer.length < sizeof(struct acpi_subtable_header))
+ goto exit;
+
+ header = (struct acpi_subtable_header *)obj->buffer.pointer;
+ if (header->type == ACPI_MADT_TYPE_IO_APIC)
+ get_ioapic_id(header, gsi_base, phys_addr, &apic_id);
+
+exit:
+ kfree(buffer.pointer);
+ return apic_id;
+}
+
+/**
+ * acpi_get_ioapic_id - Get IOAPIC ID and physical address matching @gsi_base
+ * @handle: ACPI object for IOAPIC device
+ * @gsi_base: GSI base to match with
+ * @phys_addr: Pointer to store physical address of matching IOAPIC record
+ *
+ * Walk resources returned by ACPI_MAT method, then ACPI MADT table, to search
+ * for an ACPI IOAPIC record matching @gsi_base.
+ * Return IOAPIC id and store physical address in @phys_addr if found a match,
+ * otherwise return <0.
+ */
+int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr)
+{
+ int apic_id;
+
+ apic_id = parse_mat_ioapic_entry(handle, gsi_base, phys_addr);
+ if (apic_id == -1)
+ apic_id = parse_madt_ioapic_entry(gsi_base, phys_addr);
+
+ return apic_id;
+}
+#endif /* CONFIG_ACPI_HOTPLUG_IOAPIC */
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
new file mode 100644
index 000000000..1278969ee
--- /dev/null
+++ b/drivers/acpi/processor_driver.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * processor_driver.c - ACPI Processor Driver
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de>
+ * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ * - Added processor hotplug support
+ * Copyright (C) 2013, Intel Corporation
+ * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/cpu.h>
+#include <linux/cpuidle.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+
+#include <acpi/processor.h>
+
+#include "internal.h"
+
+#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
+#define ACPI_PROCESSOR_NOTIFY_POWER 0x81
+#define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82
+
+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[] = {
+ {ACPI_PROCESSOR_OBJECT_HID, 0},
+ {ACPI_PROCESSOR_DEVICE_HID, 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, processor_device_ids);
+
+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,
+};
+
+static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct acpi_device *device = data;
+ struct acpi_processor *pr;
+ int saved;
+
+ if (device->handle != handle)
+ return;
+
+ pr = acpi_driver_data(device);
+ if (!pr)
+ return;
+
+ switch (event) {
+ case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
+ saved = pr->performance_platform_limit;
+ acpi_processor_ppc_has_changed(pr, 1);
+ if (saved == pr->performance_platform_limit)
+ break;
+ acpi_bus_generate_netlink_event(device->pnp.device_class,
+ dev_name(&device->dev), event,
+ pr->performance_platform_limit);
+ break;
+ case ACPI_PROCESSOR_NOTIFY_POWER:
+ acpi_processor_power_state_has_changed(pr);
+ acpi_bus_generate_netlink_event(device->pnp.device_class,
+ dev_name(&device->dev), event, 0);
+ break;
+ case ACPI_PROCESSOR_NOTIFY_THROTTLING:
+ acpi_processor_tstate_has_changed(pr);
+ 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;
+ }
+
+ return;
+}
+
+static int __acpi_processor_start(struct acpi_device *device);
+
+static int acpi_soft_cpu_online(unsigned int cpu)
+{
+ struct acpi_processor *pr = per_cpu(processors, cpu);
+ struct acpi_device *device;
+
+ if (!pr)
+ return 0;
+
+ device = acpi_fetch_acpi_dev(pr->handle);
+ if (!device)
+ return 0;
+
+ /*
+ * CPU got physically hotplugged and onlined for the first time:
+ * Initialize missing things.
+ */
+ if (pr->flags.need_hotplug_init) {
+ 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 {
+ /* Normal CPU soft online event. */
+ acpi_processor_ppc_has_changed(pr, 0);
+ acpi_processor_hotplug(pr);
+ acpi_processor_reevaluate_tstate(pr, false);
+ acpi_processor_tstate_has_changed(pr);
+ }
+ return 0;
+}
+
+static int acpi_soft_cpu_dead(unsigned int cpu)
+{
+ struct acpi_processor *pr = per_cpu(processors, cpu);
+
+ if (!pr || !acpi_fetch_acpi_dev(pr->handle))
+ return 0;
+
+ acpi_processor_reevaluate_tstate(pr, true);
+ return 0;
+}
+
+#ifdef CONFIG_ACPI_CPU_FREQ_PSS
+static void acpi_pss_perf_init(struct acpi_processor *pr)
+{
+ acpi_processor_ppc_has_changed(pr, 0);
+
+ acpi_processor_get_throttling_info(pr);
+
+ if (pr->flags.throttling)
+ pr->flags.limit = 1;
+}
+#else
+static inline void acpi_pss_perf_init(struct acpi_processor *pr) {}
+#endif /* CONFIG_ACPI_CPU_FREQ_PSS */
+
+static int __acpi_processor_start(struct acpi_device *device)
+{
+ struct acpi_processor *pr = acpi_driver_data(device);
+ acpi_status status;
+ int result = 0;
+
+ 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");
+
+ if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
+ acpi_processor_power_init(pr);
+
+ acpi_pss_perf_init(pr);
+
+ result = acpi_processor_thermal_init(pr, device);
+ if (result)
+ goto err_power_exit;
+
+ status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+ acpi_processor_notify, device);
+ if (ACPI_SUCCESS(status))
+ return 0;
+
+ result = -ENODEV;
+ 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);
+ struct acpi_processor *pr;
+
+ if (!device)
+ return 0;
+
+ acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+ acpi_processor_notify);
+
+ pr = acpi_driver_data(device);
+ if (!pr)
+ return 0;
+ acpi_processor_power_exit(pr);
+
+ acpi_cppc_processor_exit(pr);
+
+ acpi_processor_thermal_exit(pr, device);
+
+ return 0;
+}
+
+bool acpi_processor_cpufreq_init;
+
+static int acpi_processor_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct cpufreq_policy *policy = data;
+
+ if (event == CPUFREQ_CREATE_POLICY) {
+ acpi_thermal_cpufreq_init(policy);
+ acpi_processor_ppc_init(policy);
+ } else if (event == CPUFREQ_REMOVE_POLICY) {
+ acpi_processor_ppc_exit(policy);
+ acpi_thermal_cpufreq_exit(policy);
+ }
+
+ return 0;
+}
+
+static struct notifier_block acpi_processor_notifier_block = {
+ .notifier_call = acpi_processor_notifier,
+};
+
+/*
+ * We keep the driver loaded even when ACPI is not running.
+ * This is needed for the powernow-k8 driver, that works even without
+ * ACPI, but needs symbols from this driver
+ */
+static enum cpuhp_state hp_online;
+static int __init acpi_processor_driver_init(void)
+{
+ int result = 0;
+
+ if (acpi_disabled)
+ return 0;
+
+ 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);
+ 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();
+ return 0;
+err:
+ driver_unregister(&acpi_processor_driver);
+ return result;
+}
+
+static void __exit acpi_processor_driver_exit(void)
+{
+ if (acpi_disabled)
+ return;
+
+ if (acpi_processor_cpufreq_init) {
+ cpufreq_unregister_notifier(&acpi_processor_notifier_block,
+ CPUFREQ_POLICY_NOTIFIER);
+ acpi_processor_cpufreq_init = false;
+ }
+
+ cpuhp_remove_state_nocalls(hp_online);
+ cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD);
+ driver_unregister(&acpi_processor_driver);
+}
+
+module_init(acpi_processor_driver_init);
+module_exit(acpi_processor_driver_exit);
+
+MODULE_ALIAS("processor");
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
new file mode 100644
index 000000000..fc5b5b2c9
--- /dev/null
+++ b/drivers/acpi/processor_idle.c
@@ -0,0 +1,1438 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * processor_idle - idle state submodule to the ACPI processor driver
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2004, 2005 Dominik Brodowski <linux@brodo.de>
+ * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ * - Added processor hotplug support
+ * Copyright (C) 2005 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+ * - Added support for C3 on SMP
+ */
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/module.h>
+#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>
+#include <linux/minmax.h>
+#include <linux/perf_event.h>
+#include <acpi/processor.h>
+#include <linux/context_tracking.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).
+ * asm/acpi.h is not an option, as it would require more include magic. Also
+ * creating an empty asm-ia64/apic.h would just trade pest vs. cholera.
+ */
+#ifdef CONFIG_X86
+#include <asm/apic.h>
+#include <asm/cpu.h>
+#endif
+
+#define ACPI_IDLE_STATE_START (IS_ENABLED(CONFIG_ARCH_HAS_CPU_RELAX) ? 1 : 0)
+
+static unsigned int max_cstate __read_mostly = ACPI_PROCESSOR_MAX_POWER;
+module_param(max_cstate, uint, 0400);
+static bool nocst __read_mostly;
+module_param(nocst, bool, 0400);
+static bool bm_check_disable __read_mostly;
+module_param(bm_check_disable, bool, 0400);
+
+static unsigned int latency_factor __read_mostly = 2;
+module_param(latency_factor, uint, 0644);
+
+static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device);
+
+struct cpuidle_driver acpi_idle_driver = {
+ .name = "acpi_idle",
+ .owner = THIS_MODULE,
+};
+
+#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
+static
+DEFINE_PER_CPU(struct acpi_processor_cx * [CPUIDLE_STATE_MAX], acpi_cstate);
+
+static int disabled_by_idle_boot_param(void)
+{
+ return boot_option_idle_override == IDLE_POLL ||
+ boot_option_idle_override == IDLE_HALT;
+}
+
+/*
+ * IBM ThinkPad R40e crashes mysteriously when going into C2 or C3.
+ * For now disable this. Probably a bug somewhere else.
+ *
+ * To skip this limit, boot/load with a large max_cstate limit.
+ */
+static int set_max_cstate(const struct dmi_system_id *id)
+{
+ if (max_cstate > ACPI_PROCESSOR_MAX_POWER)
+ return 0;
+
+ pr_notice("%s detected - limiting to C%ld max_cstate."
+ " Override with \"processor.max_cstate=%d\"\n", id->ident,
+ (long)id->driver_data, ACPI_PROCESSOR_MAX_POWER + 1);
+
+ max_cstate = (long)id->driver_data;
+
+ return 0;
+}
+
+static const struct dmi_system_id processor_power_dmi_table[] = {
+ { set_max_cstate, "Clevo 5600D", {
+ DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
+ DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")},
+ (void *)2},
+ { set_max_cstate, "Pavilion zv5000", {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME,"Pavilion zv5000 (DS502A#ABA)")},
+ (void *)1},
+ { set_max_cstate, "Asus L8400B", {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME,"L8400B series Notebook PC")},
+ (void *)1},
+ {},
+};
+
+
+/*
+ * Callers should disable interrupts before the call and enable
+ * interrupts after return.
+ */
+static void __cpuidle acpi_safe_halt(void)
+{
+ if (!tif_need_resched()) {
+ safe_halt();
+ local_irq_disable();
+ }
+}
+
+#ifdef ARCH_APICTIMER_STOPS_ON_C3
+
+/*
+ * Some BIOS implementations switch to C3 in the published C2 state.
+ * This seems to be a common problem on AMD boxen, but other vendors
+ * are affected too. We pick the most conservative approach: we assume
+ * that the local APIC stops in both C2 and C3.
+ */
+static void lapic_timer_check_state(int state, struct acpi_processor *pr,
+ struct acpi_processor_cx *cx)
+{
+ struct acpi_processor_power *pwr = &pr->power;
+ u8 type = local_apic_timer_c2_ok ? ACPI_STATE_C3 : ACPI_STATE_C2;
+
+ if (cpu_has(&cpu_data(pr->id), X86_FEATURE_ARAT))
+ return;
+
+ if (boot_cpu_has_bug(X86_BUG_AMD_APIC_C1E))
+ type = ACPI_STATE_C1;
+
+ /*
+ * Check, if one of the previous states already marked the lapic
+ * unstable
+ */
+ if (pwr->timer_broadcast_on_state < state)
+ return;
+
+ if (cx->type >= type)
+ pr->power.timer_broadcast_on_state = state;
+}
+
+static void __lapic_timer_propagate_broadcast(void *arg)
+{
+ struct acpi_processor *pr = (struct acpi_processor *) arg;
+
+ if (pr->power.timer_broadcast_on_state < INT_MAX)
+ tick_broadcast_enable();
+ else
+ tick_broadcast_disable();
+}
+
+static void lapic_timer_propagate_broadcast(struct acpi_processor *pr)
+{
+ smp_call_function_single(pr->id, __lapic_timer_propagate_broadcast,
+ (void *)pr, 1);
+}
+
+/* Power(C) State timer broadcast control */
+static bool lapic_timer_needs_broadcast(struct acpi_processor *pr,
+ struct acpi_processor_cx *cx)
+{
+ return cx - pr->power.states >= pr->power.timer_broadcast_on_state;
+}
+
+#else
+
+static void lapic_timer_check_state(int state, struct acpi_processor *pr,
+ struct acpi_processor_cx *cstate) { }
+static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { }
+
+static bool lapic_timer_needs_broadcast(struct acpi_processor *pr,
+ struct acpi_processor_cx *cx)
+{
+ return false;
+}
+
+#endif
+
+#if defined(CONFIG_X86)
+static void tsc_check_state(int state)
+{
+ switch (boot_cpu_data.x86_vendor) {
+ case X86_VENDOR_HYGON:
+ case X86_VENDOR_AMD:
+ case X86_VENDOR_INTEL:
+ case X86_VENDOR_CENTAUR:
+ case X86_VENDOR_ZHAOXIN:
+ /*
+ * AMD Fam10h TSC will tick in all
+ * C/P/S0/S1 states when this bit is set.
+ */
+ if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
+ return;
+ fallthrough;
+ default:
+ /* TSC could halt in idle, so notify users */
+ if (state > ACPI_STATE_C1)
+ mark_tsc_unstable("TSC halts in idle");
+ }
+}
+#else
+static void tsc_check_state(int state) { return; }
+#endif
+
+static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr)
+{
+
+ if (!pr->pblk)
+ return -ENODEV;
+
+ /* if info is obtained from pblk/fadt, type equals state */
+ pr->power.states[ACPI_STATE_C2].type = ACPI_STATE_C2;
+ pr->power.states[ACPI_STATE_C3].type = ACPI_STATE_C3;
+
+#ifndef CONFIG_HOTPLUG_CPU
+ /*
+ * Check for P_LVL2_UP flag before entering C2 and above on
+ * an SMP system.
+ */
+ if ((num_online_cpus() > 1) &&
+ !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED))
+ return -ENODEV;
+#endif
+
+ /* determine C2 and C3 address from pblk */
+ pr->power.states[ACPI_STATE_C2].address = pr->pblk + 4;
+ pr->power.states[ACPI_STATE_C3].address = pr->pblk + 5;
+
+ /* determine latencies from FADT */
+ pr->power.states[ACPI_STATE_C2].latency = acpi_gbl_FADT.c2_latency;
+ pr->power.states[ACPI_STATE_C3].latency = acpi_gbl_FADT.c3_latency;
+
+ /*
+ * FADT specified C2 latency must be less than or equal to
+ * 100 microseconds.
+ */
+ if (acpi_gbl_FADT.c2_latency > ACPI_PROCESSOR_MAX_C2_LATENCY) {
+ acpi_handle_debug(pr->handle, "C2 latency too large [%d]\n",
+ acpi_gbl_FADT.c2_latency);
+ /* invalidate C2 */
+ pr->power.states[ACPI_STATE_C2].address = 0;
+ }
+
+ /*
+ * FADT supplied C3 latency must be less than or equal to
+ * 1000 microseconds.
+ */
+ if (acpi_gbl_FADT.c3_latency > ACPI_PROCESSOR_MAX_C3_LATENCY) {
+ acpi_handle_debug(pr->handle, "C3 latency too large [%d]\n",
+ acpi_gbl_FADT.c3_latency);
+ /* invalidate C3 */
+ pr->power.states[ACPI_STATE_C3].address = 0;
+ }
+
+ acpi_handle_debug(pr->handle, "lvl2[0x%08x] lvl3[0x%08x]\n",
+ pr->power.states[ACPI_STATE_C2].address,
+ pr->power.states[ACPI_STATE_C3].address);
+
+ snprintf(pr->power.states[ACPI_STATE_C2].desc,
+ ACPI_CX_DESC_LEN, "ACPI P_LVL2 IOPORT 0x%x",
+ pr->power.states[ACPI_STATE_C2].address);
+ snprintf(pr->power.states[ACPI_STATE_C3].desc,
+ ACPI_CX_DESC_LEN, "ACPI P_LVL3 IOPORT 0x%x",
+ pr->power.states[ACPI_STATE_C3].address);
+
+ return 0;
+}
+
+static int acpi_processor_get_power_info_default(struct acpi_processor *pr)
+{
+ if (!pr->power.states[ACPI_STATE_C1].valid) {
+ /* set the first C-State to C1 */
+ /* all processors need to support C1 */
+ pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1;
+ pr->power.states[ACPI_STATE_C1].valid = 1;
+ pr->power.states[ACPI_STATE_C1].entry_method = ACPI_CSTATE_HALT;
+
+ snprintf(pr->power.states[ACPI_STATE_C1].desc,
+ ACPI_CX_DESC_LEN, "ACPI HLT");
+ }
+ /* the C0 state only exists as a filler in our array */
+ pr->power.states[ACPI_STATE_C0].valid = 1;
+ return 0;
+}
+
+static int acpi_processor_get_power_info_cst(struct acpi_processor *pr)
+{
+ int ret;
+
+ if (nocst)
+ return -ENODEV;
+
+ ret = acpi_processor_evaluate_cst(pr->handle, pr->id, &pr->power);
+ if (ret)
+ return ret;
+
+ if (!pr->power.count)
+ return -EFAULT;
+
+ pr->flags.has_cst = 1;
+ return 0;
+}
+
+static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
+ struct acpi_processor_cx *cx)
+{
+ static int bm_check_flag = -1;
+ static int bm_control_flag = -1;
+
+
+ if (!cx->address)
+ return;
+
+ /*
+ * PIIX4 Erratum #18: We don't support C3 when Type-F (fast)
+ * DMA transfers are used by any ISA device to avoid livelock.
+ * Note that we could disable Type-F DMA (as recommended by
+ * the erratum), but this is known to disrupt certain ISA
+ * devices thus we take the conservative approach.
+ */
+ else if (errata.piix4.fdma) {
+ acpi_handle_debug(pr->handle,
+ "C3 not supported on PIIX4 with Type-F DMA\n");
+ return;
+ }
+
+ /* All the logic here assumes flags.bm_check is same across all CPUs */
+ if (bm_check_flag == -1) {
+ /* Determine whether bm_check is needed based on CPU */
+ acpi_processor_power_init_bm_check(&(pr->flags), pr->id);
+ bm_check_flag = pr->flags.bm_check;
+ bm_control_flag = pr->flags.bm_control;
+ } else {
+ pr->flags.bm_check = bm_check_flag;
+ pr->flags.bm_control = bm_control_flag;
+ }
+
+ if (pr->flags.bm_check) {
+ if (!pr->flags.bm_control) {
+ if (pr->flags.has_cst != 1) {
+ /* bus mastering control is necessary */
+ acpi_handle_debug(pr->handle,
+ "C3 support requires BM control\n");
+ return;
+ } else {
+ /* Here we enter C3 without bus mastering */
+ acpi_handle_debug(pr->handle,
+ "C3 support without BM control\n");
+ }
+ }
+ } else {
+ /*
+ * WBINVD should be set in fadt, for C3 state to be
+ * supported on when bm_check is not required.
+ */
+ if (!(acpi_gbl_FADT.flags & ACPI_FADT_WBINVD)) {
+ acpi_handle_debug(pr->handle,
+ "Cache invalidation should work properly"
+ " for C3 to be enabled on SMP systems\n");
+ return;
+ }
+ }
+
+ /*
+ * Otherwise we've met all of our C3 requirements.
+ * Normalize the C3 latency to expidite policy. Enable
+ * checking of bus mastering status (bm_check) so we can
+ * use this in our C3 policy
+ */
+ cx->valid = 1;
+
+ /*
+ * On older chipsets, BM_RLD needs to be set
+ * in order for Bus Master activity to wake the
+ * system from C3. Newer chipsets handle DMA
+ * during C3 automatically and BM_RLD is a NOP.
+ * In either case, the proper way to
+ * 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)
+{
+ const struct acpi_processor_cx *x = a, *y = b;
+
+ 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;
+
+ if (!(x->valid && y->valid))
+ return;
+ swap(x->latency, y->latency);
+}
+
+static int acpi_processor_power_verify(struct acpi_processor *pr)
+{
+ unsigned int i;
+ unsigned int working = 0;
+ unsigned int last_latency = 0;
+ unsigned int last_type = 0;
+ bool buggy_latency = false;
+
+ pr->power.timer_broadcast_on_state = INT_MAX;
+
+ for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
+ struct acpi_processor_cx *cx = &pr->power.states[i];
+
+ switch (cx->type) {
+ case ACPI_STATE_C1:
+ cx->valid = 1;
+ break;
+
+ case ACPI_STATE_C2:
+ if (!cx->address)
+ break;
+ cx->valid = 1;
+ break;
+
+ case ACPI_STATE_C3:
+ acpi_processor_power_verify_c3(pr, cx);
+ break;
+ }
+ if (!cx->valid)
+ continue;
+ if (cx->type >= last_type && cx->latency < last_latency)
+ buggy_latency = true;
+ last_latency = cx->latency;
+ last_type = cx->type;
+
+ lapic_timer_check_state(i, pr, cx);
+ tsc_check_state(cx->type);
+ working++;
+ }
+
+ 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);
+ }
+
+ lapic_timer_propagate_broadcast(pr);
+
+ 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 */
+
+ /* Zero initialize all the C-states info. */
+ memset(pr->power.states, 0, sizeof(pr->power.states));
+
+ result = acpi_processor_get_power_info_cst(pr);
+ if (result == -ENODEV)
+ result = acpi_processor_get_power_info_fadt(pr);
+
+ if (result)
+ return result;
+
+ 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;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * acpi_idle_bm_check - checks if bus master activity was detected
+ */
+static int acpi_idle_bm_check(void)
+{
+ u32 bm_status = 0;
+
+ if (bm_check_disable)
+ return 0;
+
+ acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status);
+ if (bm_status)
+ acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, 1);
+ /*
+ * PIIX4 Erratum #18: Note that BM_STS doesn't always reflect
+ * the true state of bus mastering activity; forcing us to
+ * manually check the BMIDEA bit of each IDE channel.
+ */
+ else if (errata.piix4.bmisx) {
+ if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01)
+ || (inb_p(errata.piix4.bmisx + 0x0A) & 0x01))
+ bm_status = 1;
+ }
+ return bm_status;
+}
+
+static void wait_for_freeze(void)
+{
+#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
+ *
+ * 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);
+}
+
+/**
+ * acpi_idle_do_entry - enter idle state using the appropriate method
+ * @cx: cstate data
+ *
+ * Caller disables interrupt before call and enables interrupt after return.
+ */
+static void __cpuidle acpi_idle_do_entry(struct acpi_processor_cx *cx)
+{
+ perf_lopwr_cb(true);
+
+ if (cx->entry_method == ACPI_CSTATE_FFH) {
+ /* Call into architectural FFH based C-state */
+ acpi_processor_ffh_cstate_enter(cx);
+ } else if (cx->entry_method == ACPI_CSTATE_HALT) {
+ acpi_safe_halt();
+ } else {
+ /* IO port based C-state */
+ inb(cx->address);
+ wait_for_freeze();
+ }
+
+ perf_lopwr_cb(false);
+}
+
+/**
+ * acpi_idle_play_dead - enters an ACPI state for long-term idle (i.e. off-lining)
+ * @dev: the target CPU
+ * @index: the index of suggested state
+ */
+static int acpi_idle_play_dead(struct cpuidle_device *dev, int index)
+{
+ struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);
+
+ ACPI_FLUSH_CPU_CACHE();
+
+ while (1) {
+
+ if (cx->entry_method == ACPI_CSTATE_HALT)
+ safe_halt();
+ else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) {
+ inb(cx->address);
+ wait_for_freeze();
+ } else
+ return -ENODEV;
+
+#if defined(CONFIG_X86) && defined(CONFIG_HOTPLUG_CPU)
+ cond_wakeup_cpu0();
+#endif
+ }
+
+ /* Never reached */
+ return 0;
+}
+
+static 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);
+}
+
+static int c3_cpu_count;
+static DEFINE_RAW_SPINLOCK(c3_lock);
+
+/**
+ * acpi_idle_enter_bm - enters C3 with proper BM handling
+ * @drv: cpuidle driver
+ * @pr: Target processor
+ * @cx: Target state context
+ * @index: index of target state
+ */
+static int __cpuidle acpi_idle_enter_bm(struct cpuidle_driver *drv,
+ struct acpi_processor *pr,
+ struct acpi_processor_cx *cx,
+ int index)
+{
+ static struct acpi_processor_cx safe_cx = {
+ .entry_method = ACPI_CSTATE_HALT,
+ };
+
+ /*
+ * disable bus master
+ * bm_check implies we need ARB_DIS
+ * bm_control implies whether we can do ARB_DIS
+ *
+ * That leaves a case where bm_check is set and bm_control is not set.
+ * In that case we cannot do much, we enter C3 without doing anything.
+ */
+ bool dis_bm = pr->flags.bm_control;
+
+ /* If we can skip BM, demote to a safe state. */
+ if (!cx->bm_sts_skip && acpi_idle_bm_check()) {
+ dis_bm = false;
+ index = drv->safe_state_index;
+ if (index >= 0) {
+ cx = this_cpu_read(acpi_cstate[index]);
+ } else {
+ cx = &safe_cx;
+ index = -EBUSY;
+ }
+ }
+
+ if (dis_bm) {
+ raw_spin_lock(&c3_lock);
+ c3_cpu_count++;
+ /* Disable bus master arbitration when all CPUs are in C3 */
+ if (c3_cpu_count == num_online_cpus())
+ acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1);
+ raw_spin_unlock(&c3_lock);
+ }
+
+ ct_idle_enter();
+
+ acpi_idle_do_entry(cx);
+
+ ct_idle_exit();
+
+ /* Re-enable bus master arbitration */
+ if (dis_bm) {
+ raw_spin_lock(&c3_lock);
+ acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0);
+ c3_cpu_count--;
+ raw_spin_unlock(&c3_lock);
+ }
+
+ return index;
+}
+
+static int __cpuidle acpi_idle_enter(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
+{
+ struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);
+ struct acpi_processor *pr;
+
+ pr = __this_cpu_read(processors);
+ if (unlikely(!pr))
+ return -EINVAL;
+
+ if (cx->type != ACPI_STATE_C1) {
+ if (cx->type == ACPI_STATE_C3 && pr->flags.bm_check)
+ return acpi_idle_enter_bm(drv, pr, cx, index);
+
+ /* C2 to C1 demotion. */
+ if (acpi_idle_fallback_to_c1(pr) && num_online_cpus() > 1) {
+ index = ACPI_IDLE_STATE_START;
+ cx = per_cpu(acpi_cstate[index], dev->cpu);
+ }
+ }
+
+ if (cx->type == ACPI_STATE_C3)
+ ACPI_FLUSH_CPU_CACHE();
+
+ acpi_idle_do_entry(cx);
+
+ return index;
+}
+
+static int __cpuidle acpi_idle_enter_s2idle(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
+{
+ struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);
+
+ if (cx->type == ACPI_STATE_C3) {
+ struct acpi_processor *pr = __this_cpu_read(processors);
+
+ if (unlikely(!pr))
+ return 0;
+
+ if (pr->flags.bm_check) {
+ u8 bm_sts_skip = cx->bm_sts_skip;
+
+ /* Don't check BM_STS, do an unconditional ARB_DIS for S2IDLE */
+ cx->bm_sts_skip = 1;
+ acpi_idle_enter_bm(drv, pr, cx, index);
+ cx->bm_sts_skip = bm_sts_skip;
+
+ return 0;
+ } else {
+ ACPI_FLUSH_CPU_CACHE();
+ }
+ }
+ acpi_idle_do_entry(cx);
+
+ return 0;
+}
+
+static int 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)
+ continue;
+
+ 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)
+{
+ int i, count;
+ struct acpi_processor_cx *cx;
+ struct cpuidle_state *state;
+ struct cpuidle_driver *drv = &acpi_idle_driver;
+
+ if (max_cstate == 0)
+ max_cstate = 1;
+
+ if (IS_ENABLED(CONFIG_ARCH_HAS_CPU_RELAX)) {
+ cpuidle_poll_state_init(drv);
+ count = 1;
+ } else {
+ count = 0;
+ }
+
+ for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
+ cx = &pr->power.states[i];
+
+ if (!cx->valid)
+ continue;
+
+ state = &drv->states[count];
+ snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i);
+ 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;
+ }
+ /*
+ * Halt-induced C1 is not good for ->enter_s2idle, because it
+ * re-enables interrupts on exit. Moreover, C1 is generally not
+ * particularly interesting from the suspend-to-idle angle, so
+ * avoid C1 and the situations in which we may need to fall back
+ * to it altogether.
+ */
+ if (cx->type != ACPI_STATE_C1 && !acpi_idle_fallback_to_c1(pr))
+ state->enter_s2idle = acpi_idle_enter_s2idle;
+
+ 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)
+{
+ static int first_run;
+
+ if (first_run)
+ return;
+ dmi_check_system(processor_power_dmi_table);
+ max_cstate = acpi_processor_cstate_check(max_cstate);
+ if (max_cstate < ACPI_C_STATES_MAX)
+ pr_notice("processor limited to max C-state %d\n", max_cstate);
+
+ first_run++;
+
+ if (nocst)
+ return;
+
+ acpi_processor_claim_cst_control();
+}
+#else
+
+static inline int disabled_by_idle_boot_param(void) { return 0; }
+static inline void acpi_processor_cstate_first_run_checks(void) { }
+static int acpi_processor_get_cstate_info(struct acpi_processor *pr)
+{
+ return -ENODEV;
+}
+
+static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
+ struct cpuidle_device *dev)
+{
+ return -EINVAL;
+}
+
+static int acpi_processor_setup_cstates(struct acpi_processor *pr)
+{
+ return -EINVAL;
+}
+
+#endif /* CONFIG_ACPI_PROCESSOR_CSTATE */
+
+struct acpi_lpi_states_array {
+ unsigned int size;
+ unsigned int composite_states_size;
+ struct acpi_lpi_state *entries;
+ struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER];
+};
+
+static int obj_get_integer(union acpi_object *obj, u32 *value)
+{
+ if (obj->type != ACPI_TYPE_INTEGER)
+ return -EINVAL;
+
+ *value = obj->integer.value;
+ return 0;
+}
+
+static int acpi_processor_evaluate_lpi(acpi_handle handle,
+ struct acpi_lpi_states_array *info)
+{
+ acpi_status status;
+ int ret = 0;
+ int pkg_count, state_idx = 1, loop;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *lpi_data;
+ struct acpi_lpi_state *lpi_state;
+
+ status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_debug(handle, "No _LPI, giving up\n");
+ return -ENODEV;
+ }
+
+ lpi_data = buffer.pointer;
+
+ /* There must be at least 4 elements = 3 elements + 1 package */
+ if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE ||
+ lpi_data->package.count < 4) {
+ pr_debug("not enough elements in _LPI\n");
+ ret = -ENODATA;
+ goto end;
+ }
+
+ pkg_count = lpi_data->package.elements[2].integer.value;
+
+ /* Validate number of power states. */
+ if (pkg_count < 1 || pkg_count != lpi_data->package.count - 3) {
+ pr_debug("count given by _LPI is not valid\n");
+ ret = -ENODATA;
+ goto end;
+ }
+
+ lpi_state = kcalloc(pkg_count, sizeof(*lpi_state), GFP_KERNEL);
+ if (!lpi_state) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ info->size = pkg_count;
+ info->entries = lpi_state;
+
+ /* LPI States start at index 3 */
+ for (loop = 3; state_idx <= pkg_count; loop++, state_idx++, lpi_state++) {
+ union acpi_object *element, *pkg_elem, *obj;
+
+ element = &lpi_data->package.elements[loop];
+ if (element->type != ACPI_TYPE_PACKAGE || element->package.count < 7)
+ continue;
+
+ pkg_elem = element->package.elements;
+
+ obj = pkg_elem + 6;
+ if (obj->type == ACPI_TYPE_BUFFER) {
+ struct acpi_power_register *reg;
+
+ reg = (struct acpi_power_register *)obj->buffer.pointer;
+ if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO &&
+ reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)
+ continue;
+
+ lpi_state->address = reg->address;
+ lpi_state->entry_method =
+ reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE ?
+ ACPI_CSTATE_FFH : ACPI_CSTATE_SYSTEMIO;
+ } else if (obj->type == ACPI_TYPE_INTEGER) {
+ lpi_state->entry_method = ACPI_CSTATE_INTEGER;
+ lpi_state->address = obj->integer.value;
+ } else {
+ continue;
+ }
+
+ /* elements[7,8] skipped for now i.e. Residency/Usage counter*/
+
+ obj = pkg_elem + 9;
+ if (obj->type == ACPI_TYPE_STRING)
+ strscpy(lpi_state->desc, obj->string.pointer,
+ ACPI_CX_DESC_LEN);
+
+ lpi_state->index = state_idx;
+ if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) {
+ pr_debug("No min. residency found, assuming 10 us\n");
+ lpi_state->min_residency = 10;
+ }
+
+ if (obj_get_integer(pkg_elem + 1, &lpi_state->wake_latency)) {
+ pr_debug("No wakeup residency found, assuming 10 us\n");
+ lpi_state->wake_latency = 10;
+ }
+
+ if (obj_get_integer(pkg_elem + 2, &lpi_state->flags))
+ lpi_state->flags = 0;
+
+ if (obj_get_integer(pkg_elem + 3, &lpi_state->arch_flags))
+ lpi_state->arch_flags = 0;
+
+ if (obj_get_integer(pkg_elem + 4, &lpi_state->res_cnt_freq))
+ lpi_state->res_cnt_freq = 1;
+
+ if (obj_get_integer(pkg_elem + 5, &lpi_state->enable_parent_state))
+ lpi_state->enable_parent_state = 0;
+ }
+
+ acpi_handle_debug(handle, "Found %d power states\n", state_idx);
+end:
+ kfree(buffer.pointer);
+ 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
+ *
+ * @local: local LPI state
+ * @parent: parent LPI state
+ * @result: composite LPI state
+ */
+static bool combine_lpi_states(struct acpi_lpi_state *local,
+ struct acpi_lpi_state *parent,
+ struct acpi_lpi_state *result)
+{
+ if (parent->entry_method == ACPI_CSTATE_INTEGER) {
+ if (!parent->address) /* 0 means autopromotable */
+ return false;
+ result->address = local->address + parent->address;
+ } else {
+ result->address = parent->address;
+ }
+
+ result->min_residency = max(local->min_residency, parent->min_residency);
+ result->wake_latency = local->wake_latency + parent->wake_latency;
+ result->enable_parent_state = parent->enable_parent_state;
+ result->entry_method = local->entry_method;
+
+ result->flags = parent->flags;
+ result->arch_flags = parent->arch_flags;
+ result->index = parent->index;
+
+ 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;
+}
+
+#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0)
+
+static void stash_composite_state(struct acpi_lpi_states_array *curr_level,
+ struct acpi_lpi_state *t)
+{
+ 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)
+{
+ int i, j, state_count = curr_level->size;
+ struct acpi_lpi_state *p, *t = curr_level->entries;
+
+ curr_level->composite_states_size = 0;
+ for (j = 0; j < state_count; j++, t++) {
+ struct acpi_lpi_state *flpi;
+
+ if (!(t->flags & ACPI_LPI_STATE_FLAGS_ENABLED))
+ continue;
+
+ if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) {
+ pr_warn("Limiting number of LPI states to max (%d)\n",
+ ACPI_PROCESSOR_MAX_POWER);
+ pr_warn("Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n");
+ break;
+ }
+
+ flpi = &pr->power.lpi_states[flat_state_cnt];
+
+ if (!prev_level) { /* leaf/processor node */
+ memcpy(flpi, t, sizeof(*t));
+ stash_composite_state(curr_level, flpi);
+ flat_state_cnt++;
+ continue;
+ }
+
+ for (i = 0; i < prev_level->composite_states_size; i++) {
+ p = prev_level->composite_states[i];
+ if (t->index <= p->enable_parent_state &&
+ combine_lpi_states(p, t, flpi)) {
+ stash_composite_state(curr_level, flpi);
+ flat_state_cnt++;
+ flpi++;
+ }
+ }
+ }
+
+ kfree(curr_level->entries);
+ return 0;
+}
+
+int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu)
+{
+ return -EOPNOTSUPP;
+}
+
+static int acpi_processor_get_lpi_info(struct acpi_processor *pr)
+{
+ int ret, i;
+ acpi_status status;
+ acpi_handle handle = pr->handle, pr_ahandle;
+ struct acpi_device *d = NULL;
+ struct acpi_lpi_states_array info[2], *tmp, *prev, *curr;
+
+ /* make sure our architecture has support */
+ ret = acpi_processor_ffh_lpi_probe(pr->id);
+ if (ret == -EOPNOTSUPP)
+ return ret;
+
+ if (!osc_pc_lpi_support_confirmed)
+ return -EOPNOTSUPP;
+
+ 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);
+
+ 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))
+ break;
+
+ /* can be optional ? */
+ if (!acpi_has_method(handle, "_LPI"))
+ break;
+
+ ret = acpi_processor_evaluate_lpi(handle, curr);
+ if (ret)
+ break;
+
+ /* flatten all the LPI states in this level of hierarchy */
+ flatten_lpi_states(pr, 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++)
+ pr->power.lpi_states[i].index = i;
+
+ /* Tell driver that _LPI is supported. */
+ pr->flags.has_lpi = 1;
+ pr->flags.power = 1;
+
+ return 0;
+}
+
+int __weak acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
+{
+ return -ENODEV;
+}
+
+/**
+ * acpi_idle_lpi_enter - enters an ACPI any LPI state
+ * @dev: the target CPU
+ * @drv: cpuidle driver containing cpuidle state info
+ * @index: index of target state
+ *
+ * Return: 0 for success or negative value for error
+ */
+static int acpi_idle_lpi_enter(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
+{
+ struct acpi_processor *pr;
+ struct acpi_lpi_state *lpi;
+
+ pr = __this_cpu_read(processors);
+
+ if (unlikely(!pr))
+ return -EINVAL;
+
+ lpi = &pr->power.lpi_states[index];
+ if (lpi->entry_method == ACPI_CSTATE_FFH)
+ return acpi_processor_ffh_lpi_enter(lpi);
+
+ return -EINVAL;
+}
+
+static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
+{
+ int i;
+ struct acpi_lpi_state *lpi;
+ struct cpuidle_state *state;
+ struct cpuidle_driver *drv = &acpi_idle_driver;
+
+ if (!pr->flags.has_lpi)
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < pr->power.count && i < CPUIDLE_STATE_MAX; i++) {
+ lpi = &pr->power.lpi_states[i];
+
+ state = &drv->states[i];
+ snprintf(state->name, CPUIDLE_NAME_LEN, "LPI-%d", i);
+ 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->enter = acpi_idle_lpi_enter;
+ drv->safe_state_index = i;
+ }
+
+ drv->state_count = i;
+
+ return 0;
+}
+
+/**
+ * acpi_processor_setup_cpuidle_states- prepares and configures cpuidle
+ * global state data i.e. idle routines
+ *
+ * @pr: the ACPI processor
+ */
+static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
+{
+ int i;
+ struct cpuidle_driver *drv = &acpi_idle_driver;
+
+ if (!pr->flags.power_setup_done || !pr->flags.power)
+ return -EINVAL;
+
+ drv->safe_state_index = -1;
+ for (i = ACPI_IDLE_STATE_START; i < CPUIDLE_STATE_MAX; i++) {
+ drv->states[i].name[0] = '\0';
+ drv->states[i].desc[0] = '\0';
+ }
+
+ if (pr->flags.has_lpi)
+ return acpi_processor_setup_lpi_states(pr);
+
+ return acpi_processor_setup_cstates(pr);
+}
+
+/**
+ * acpi_processor_setup_cpuidle_dev - prepares and configures CPUIDLE
+ * device i.e. per-cpu data
+ *
+ * @pr: the ACPI processor
+ * @dev : the cpuidle device
+ */
+static int acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr,
+ struct cpuidle_device *dev)
+{
+ if (!pr->flags.power_setup_done || !pr->flags.power || !dev)
+ return -EINVAL;
+
+ dev->cpu = pr->id;
+ if (pr->flags.has_lpi)
+ return acpi_processor_ffh_lpi_probe(pr->id);
+
+ return acpi_processor_setup_cpuidle_cx(pr, dev);
+}
+
+static int acpi_processor_get_power_info(struct acpi_processor *pr)
+{
+ int ret;
+
+ ret = acpi_processor_get_lpi_info(pr);
+ if (ret)
+ ret = acpi_processor_get_cstate_info(pr);
+
+ return ret;
+}
+
+int acpi_processor_hotplug(struct acpi_processor *pr)
+{
+ int ret = 0;
+ struct cpuidle_device *dev;
+
+ if (disabled_by_idle_boot_param())
+ return 0;
+
+ if (!pr->flags.power_setup_done)
+ return -ENODEV;
+
+ dev = per_cpu(acpi_cpuidle_device, pr->id);
+ cpuidle_pause_and_lock();
+ cpuidle_disable_device(dev);
+ ret = acpi_processor_get_power_info(pr);
+ if (!ret && pr->flags.power) {
+ acpi_processor_setup_cpuidle_dev(pr, dev);
+ ret = cpuidle_enable_device(dev);
+ }
+ cpuidle_resume_and_unlock();
+
+ return ret;
+}
+
+int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
+{
+ int cpu;
+ struct acpi_processor *_pr;
+ struct cpuidle_device *dev;
+
+ if (disabled_by_idle_boot_param())
+ return 0;
+
+ if (!pr->flags.power_setup_done)
+ return -ENODEV;
+
+ /*
+ * FIXME: Design the ACPI notification to make it once per
+ * system instead of once per-cpu. This condition is a hack
+ * to make the code that updates C-States be called once.
+ */
+
+ if (pr->id == 0 && cpuidle_get_driver() == &acpi_idle_driver) {
+
+ /* Protect against cpu-hotplug */
+ cpus_read_lock();
+ cpuidle_pause_and_lock();
+
+ /* Disable all cpuidle devices */
+ for_each_online_cpu(cpu) {
+ _pr = per_cpu(processors, cpu);
+ if (!_pr || !_pr->flags.power_setup_done)
+ continue;
+ dev = per_cpu(acpi_cpuidle_device, cpu);
+ cpuidle_disable_device(dev);
+ }
+
+ /* Populate Updated C-state information */
+ acpi_processor_get_power_info(pr);
+ acpi_processor_setup_cpuidle_states(pr);
+
+ /* Enable all cpuidle devices */
+ for_each_online_cpu(cpu) {
+ _pr = per_cpu(processors, cpu);
+ if (!_pr || !_pr->flags.power_setup_done)
+ continue;
+ acpi_processor_get_power_info(_pr);
+ if (_pr->flags.power) {
+ dev = per_cpu(acpi_cpuidle_device, cpu);
+ acpi_processor_setup_cpuidle_dev(_pr, dev);
+ cpuidle_enable_device(dev);
+ }
+ }
+ cpuidle_resume_and_unlock();
+ cpus_read_unlock();
+ }
+
+ return 0;
+}
+
+static int acpi_processor_registered;
+
+int acpi_processor_power_init(struct acpi_processor *pr)
+{
+ int retval;
+ struct cpuidle_device *dev;
+
+ if (disabled_by_idle_boot_param())
+ return 0;
+
+ acpi_processor_cstate_first_run_checks();
+
+ if (!acpi_processor_get_power_info(pr))
+ pr->flags.power_setup_done = 1;
+
+ /*
+ * Install the idle handler if processor power management is supported.
+ * Note that we use previously set idle handler will be used on
+ * platforms that only support C1.
+ */
+ if (pr->flags.power) {
+ /* Register acpi_idle_driver if not already registered */
+ if (!acpi_processor_registered) {
+ acpi_processor_setup_cpuidle_states(pr);
+ retval = cpuidle_register_driver(&acpi_idle_driver);
+ if (retval)
+ return retval;
+ pr_debug("%s registered with cpuidle\n",
+ acpi_idle_driver.name);
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ per_cpu(acpi_cpuidle_device, pr->id) = dev;
+
+ acpi_processor_setup_cpuidle_dev(pr, dev);
+
+ /* Register per-cpu cpuidle_device. Cpuidle driver
+ * must already be registered before registering device
+ */
+ retval = cpuidle_register_device(dev);
+ if (retval) {
+ if (acpi_processor_registered == 0)
+ cpuidle_unregister_driver(&acpi_idle_driver);
+ return retval;
+ }
+ acpi_processor_registered++;
+ }
+ return 0;
+}
+
+int acpi_processor_power_exit(struct acpi_processor *pr)
+{
+ struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id);
+
+ if (disabled_by_idle_boot_param())
+ return 0;
+
+ if (pr->flags.power) {
+ cpuidle_unregister_device(dev);
+ acpi_processor_registered--;
+ if (acpi_processor_registered == 0)
+ cpuidle_unregister_driver(&acpi_idle_driver);
+ }
+
+ pr->flags.power_setup_done = 0;
+ return 0;
+}
diff --git a/drivers/acpi/processor_pdc.c b/drivers/acpi/processor_pdc.c
new file mode 100644
index 000000000..18fb04523
--- /dev/null
+++ b/drivers/acpi/processor_pdc.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2005 Intel Corporation
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+ * - Added _PDC for platforms with Intel CPUs
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/dmi.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+
+#include <xen/xen.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;
+ }
+
+ 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 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);
+}
+
+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;
+
+ /* Twiddle arch-specific bits needed for _PDC */
+ arch_acpi_set_pdc_bits(buf);
+}
+
+static struct acpi_object_list *acpi_processor_alloc_pdc(void)
+{
+ struct acpi_object_list *obj_list;
+ union acpi_object *obj;
+ u32 *buf;
+
+ /* allocate and initialize pdc. It will be used later. */
+ obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL);
+ if (!obj_list)
+ goto out;
+
+ obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL);
+ if (!obj) {
+ kfree(obj_list);
+ goto out;
+ }
+
+ buf = kmalloc(12, GFP_KERNEL);
+ if (!buf) {
+ kfree(obj);
+ kfree(obj_list);
+ goto out;
+ }
+
+ acpi_set_pdc_bits(buf);
+
+ obj->type = ACPI_TYPE_BUFFER;
+ obj->buffer.length = 12;
+ obj->buffer.pointer = (u8 *) buf;
+ obj_list->count = 1;
+ obj_list->pointer = obj;
+
+ return obj_list;
+out:
+ pr_err("Memory allocation error\n");
+ return NULL;
+}
+
+/*
+ * _PDC is required for a BIOS-OS handshake for most of the newer
+ * ACPI processor features.
+ */
+static acpi_status
+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))
+ acpi_handle_debug(handle,
+ "Could not evaluate _PDC, using legacy perf control\n");
+
+ return status;
+}
+
+void acpi_processor_set_pdc(acpi_handle handle)
+{
+ struct acpi_object_list *obj_list;
+
+ if (arch_has_acpi_pdc() == false)
+ return;
+
+ obj_list = acpi_processor_alloc_pdc();
+ if (!obj_list)
+ return;
+
+ acpi_processor_eval_pdc(handle, obj_list);
+
+ kfree(obj_list->pointer->buffer.pointer);
+ kfree(obj_list->pointer);
+ kfree(obj_list);
+}
+
+static acpi_status __init
+early_init_pdc(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ if (processor_physically_present(handle) == false)
+ return AE_OK;
+
+ acpi_processor_set_pdc(handle);
+ 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_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ early_init_pdc, NULL, NULL, NULL);
+ acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID, early_init_pdc, NULL, NULL);
+}
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
new file mode 100644
index 000000000..1696700fd
--- /dev/null
+++ b/drivers/acpi/processor_perflib.c
@@ -0,0 +1,800 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * processor_perflib.c - ACPI Processor P-States Library ($Revision: 71 $)
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de>
+ * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ * - Added processor hotplug support
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#ifdef CONFIG_X86
+#include <asm/cpufeature.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
+ * the ACPI core is asked to change the speed policy, the maximum
+ * value is adjusted so that it is within the platform limit.
+ *
+ * Also, when a new platform limit value is detected, the CPUfreq
+ * policy is adjusted accordingly.
+ */
+
+/* ignore_ppc:
+ * -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet
+ * ignore _PPC
+ * 0 -> cpufreq low level drivers initialized -> consider _PPC values
+ * 1 -> ignore _PPC totally -> forced by user through boot param
+ */
+static int ignore_ppc = -1;
+module_param(ignore_ppc, int, 0644);
+MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \
+ "limited by BIOS, this should help");
+
+static bool acpi_processor_ppc_in_use;
+
+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)
+ return -EINVAL;
+
+ /*
+ * _PPC indicates the maximum state currently supported by the platform
+ * (e.g. 0 = states 0..n; 1 = states 1..n; etc.
+ */
+ status = acpi_evaluate_integer(pr->handle, "_PPC", NULL, &ppc);
+ if (status != AE_NOT_FOUND) {
+ acpi_processor_ppc_in_use = true;
+
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(pr->handle, "_PPC", status);
+ return -ENODEV;
+ }
+ }
+
+ 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,
+ index, index ? "is" : "is not");
+
+ pr->performance_platform_limit = index;
+
+ if (unlikely(!freq_qos_request_active(&pr->perflib_req)))
+ return 0;
+
+ /*
+ * 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);
+ }
+
+ return 0;
+}
+
+#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
+/*
+ * acpi_processor_ppc_ost: Notify firmware the _PPC evaluation status
+ * @handle: ACPI processor handle
+ * @status: the status code of _PPC evaluation
+ * 0: success. OSPM is now using the performance state specified.
+ * 1: failure. OSPM has not changed the number of P-states in use
+ */
+static void acpi_processor_ppc_ost(acpi_handle handle, int status)
+{
+ if (acpi_has_method(handle, "_OST"))
+ acpi_evaluate_ost(handle, ACPI_PROCESSOR_NOTIFY_PERFORMANCE,
+ status, NULL);
+}
+
+void acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag)
+{
+ int ret;
+
+ if (ignore_ppc || !pr->performance) {
+ /*
+ * Only when it is notification event, the _OST object
+ * will be evaluated. Otherwise it is skipped.
+ */
+ if (event_flag)
+ acpi_processor_ppc_ost(pr->handle, 1);
+ return;
+ }
+
+ ret = acpi_processor_get_platform_limit(pr);
+ /*
+ * Only when it is notification event, the _OST object
+ * will be evaluated. Otherwise it is skipped.
+ */
+ if (event_flag) {
+ if (ret < 0)
+ acpi_processor_ppc_ost(pr->handle, 1);
+ else
+ acpi_processor_ppc_ost(pr->handle, 0);
+ }
+ if (ret >= 0)
+ cpufreq_update_limits(pr->id);
+}
+
+int acpi_processor_get_bios_limit(int cpu, unsigned int *limit)
+{
+ struct acpi_processor *pr;
+
+ 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;
+}
+EXPORT_SYMBOL(acpi_processor_get_bios_limit);
+
+void acpi_processor_ignore_ppc_init(void)
+{
+ if (ignore_ppc < 0)
+ ignore_ppc = 0;
+}
+
+void acpi_processor_ppc_init(struct cpufreq_policy *policy)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, policy->related_cpus) {
+ struct acpi_processor *pr = per_cpu(processors, cpu);
+ int ret;
+
+ 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,
+ FREQ_QOS_MAX_DEFAULT_VALUE);
+ if (ret < 0)
+ pr_err("Failed to add freq constraint for CPU%d (%d)\n",
+ cpu, ret);
+ }
+}
+
+void acpi_processor_ppc_exit(struct cpufreq_policy *policy)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, policy->related_cpus) {
+ struct acpi_processor *pr = per_cpu(processors, cpu);
+
+ if (pr)
+ freq_qos_remove_request(&pr->perflib_req);
+ }
+}
+
+static int acpi_processor_get_performance_control(struct acpi_processor *pr)
+{
+ int result = 0;
+ acpi_status status = 0;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *pct = NULL;
+ union acpi_object obj = { 0 };
+
+ status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(pr->handle, "_PCT", status);
+ return -ENODEV;
+ }
+
+ pct = (union acpi_object *)buffer.pointer;
+ if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
+ || (pct->package.count != 2)) {
+ pr_err("Invalid _PCT data\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ /*
+ * control_register
+ */
+
+ obj = pct->package.elements[0];
+
+ if ((obj.type != ACPI_TYPE_BUFFER)
+ || (obj.buffer.length < sizeof(struct acpi_pct_register))
+ || (obj.buffer.pointer == NULL)) {
+ pr_err("Invalid _PCT data (control_register)\n");
+ result = -EFAULT;
+ goto end;
+ }
+ memcpy(&pr->performance->control_register, obj.buffer.pointer,
+ sizeof(struct acpi_pct_register));
+
+ /*
+ * status_register
+ */
+
+ obj = pct->package.elements[1];
+
+ if ((obj.type != ACPI_TYPE_BUFFER)
+ || (obj.buffer.length < sizeof(struct acpi_pct_register))
+ || (obj.buffer.pointer == NULL)) {
+ pr_err("Invalid _PCT data (status_register)\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ memcpy(&pr->performance->status_register, obj.buffer.pointer,
+ sizeof(struct acpi_pct_register));
+
+end:
+ kfree(buffer.pointer);
+
+ 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.
+ */
+static void amd_fixup_frequency(struct acpi_processor_px *px, int i)
+{
+ u32 hi, lo, fid, did;
+ int index = px->control & 0x00000007;
+
+ 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) {
+ rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi);
+ /*
+ * MSR C001_0064+:
+ * Bit 63: PstateEn. Read-write. If set, the P-state is valid.
+ */
+ if (!(hi & BIT(31)))
+ return;
+
+ fid = lo & 0x3f;
+ did = (lo >> 6) & 7;
+ if (boot_cpu_data.x86 == 0x10)
+ px->core_frequency = (100 * (fid + 0x10)) >> did;
+ else
+ 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)
+{
+ int result = 0;
+ acpi_status status = AE_OK;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer format = { sizeof("NNNNNN"), "NNNNNN" };
+ struct acpi_buffer state = { 0, NULL };
+ union acpi_object *pss = NULL;
+ int i;
+ int last_invalid = -1;
+
+ status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(pr->handle, "_PSS", status);
+ return -ENODEV;
+ }
+
+ pss = buffer.pointer;
+ if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
+ pr_err("Invalid _PSS data\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ acpi_handle_debug(pr->handle, "Found %d performance states\n",
+ pss->package.count);
+
+ pr->performance->state_count = pss->package.count;
+ pr->performance->states =
+ kmalloc_array(pss->package.count,
+ sizeof(struct acpi_processor_px),
+ GFP_KERNEL);
+ if (!pr->performance->states) {
+ result = -ENOMEM;
+ goto end;
+ }
+
+ for (i = 0; i < pr->performance->state_count; i++) {
+
+ struct acpi_processor_px *px = &(pr->performance->states[i]);
+
+ state.length = sizeof(struct acpi_processor_px);
+ state.pointer = px;
+
+ acpi_handle_debug(pr->handle, "Extracting state %d\n", i);
+
+ status = acpi_extract_package(&(pss->package.elements[i]),
+ &format, &state);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_warn(pr->handle, "Invalid _PSS data: %s\n",
+ acpi_format_exception(status));
+ result = -EFAULT;
+ kfree(pr->performance->states);
+ goto end;
+ }
+
+ amd_fixup_frequency(px, i);
+
+ acpi_handle_debug(pr->handle,
+ "State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n",
+ i,
+ (u32) px->core_frequency,
+ (u32) px->power,
+ (u32) px->transition_latency,
+ (u32) px->bus_master_latency,
+ (u32) px->control, (u32) px->status);
+
+ /*
+ * 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))) {
+ pr_err(FW_BUG
+ "Invalid BIOS _PSS frequency found for processor %d: 0x%llx MHz\n",
+ pr->id, px->core_frequency);
+ if (last_invalid == -1)
+ last_invalid = i;
+ } else {
+ if (last_invalid != -1) {
+ /*
+ * Copy this valid entry over last_invalid entry
+ */
+ memcpy(&(pr->performance->states[last_invalid]),
+ px, sizeof(struct acpi_processor_px));
+ ++last_invalid;
+ }
+ }
+ }
+
+ if (last_invalid == 0) {
+ pr_err(FW_BUG
+ "No valid BIOS _PSS frequency found for processor %d\n", pr->id);
+ result = -EFAULT;
+ kfree(pr->performance->states);
+ pr->performance->states = NULL;
+ }
+
+ if (last_invalid > 0)
+ pr->performance->state_count = last_invalid;
+
+end:
+ kfree(buffer.pointer);
+
+ return result;
+}
+
+int acpi_processor_get_performance_info(struct acpi_processor *pr)
+{
+ int result = 0;
+
+ if (!pr || !pr->performance || !pr->handle)
+ return -EINVAL;
+
+ if (!acpi_has_method(pr->handle, "_PCT")) {
+ acpi_handle_debug(pr->handle,
+ "ACPI-based processor performance control unavailable\n");
+ return -ENODEV;
+ }
+
+ result = acpi_processor_get_performance_control(pr);
+ if (result)
+ goto update_bios;
+
+ result = acpi_processor_get_performance_states(pr);
+ if (result)
+ goto update_bios;
+
+ /* We need to call _PPC once when cpufreq starts */
+ if (ignore_ppc != 1)
+ result = acpi_processor_get_platform_limit(pr);
+
+ return result;
+
+ /*
+ * Having _PPC but missing frequencies (_PSS, _PCT) is a very good hint that
+ * 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);
+
+int acpi_processor_pstate_control(void)
+{
+ acpi_status status;
+
+ if (!acpi_gbl_FADT.smi_command || !acpi_gbl_FADT.pstate_control)
+ return 0;
+
+ pr_debug("Writing pstate_control [0x%x] to smi_command [0x%x]\n",
+ acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command);
+
+ status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
+ (u32)acpi_gbl_FADT.pstate_control, 8);
+ if (ACPI_SUCCESS(status))
+ return 1;
+
+ pr_warn("Failed to write pstate_control [0x%x] to smi_command [0x%x]: %s\n",
+ acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command,
+ acpi_format_exception(status));
+ return -EIO;
+}
+
+int acpi_processor_notify_smm(struct module *calling_module)
+{
+ static int is_done;
+ int result;
+
+ if (!acpi_processor_cpufreq_init)
+ return -EBUSY;
+
+ 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...
+ */
+ if (is_done > 0) {
+ module_put(calling_module);
+ return 0;
+ } else if (is_done < 0) {
+ module_put(calling_module);
+ return is_done;
+ }
+
+ is_done = -EIO;
+
+ 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;
+ }
+
+ /* 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;
+
+ if (!acpi_processor_ppc_in_use)
+ module_put(calling_module);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(acpi_processor_notify_smm);
+
+int acpi_processor_get_psd(acpi_handle handle, struct acpi_psd_package *pdomain)
+{
+ int result = 0;
+ acpi_status status = AE_OK;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"};
+ struct acpi_buffer state = {0, NULL};
+ union acpi_object *psd = NULL;
+
+ status = acpi_evaluate_object(handle, "_PSD", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ return -ENODEV;
+ }
+
+ psd = buffer.pointer;
+ if (!psd || (psd->type != ACPI_TYPE_PACKAGE)) {
+ pr_err("Invalid _PSD data\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ if (psd->package.count != 1) {
+ pr_err("Invalid _PSD data\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ state.length = sizeof(struct acpi_psd_package);
+ state.pointer = pdomain;
+
+ status = acpi_extract_package(&(psd->package.elements[0]),
+ &format, &state);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Invalid _PSD data\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) {
+ pr_err("Unknown _PSD:num_entries\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ if (pdomain->revision != ACPI_PSD_REV0_REVISION) {
+ pr_err("Unknown _PSD:revision\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
+ pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
+ pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
+ pr_err("Invalid _PSD:coord_type\n");
+ result = -EFAULT;
+ goto end;
+ }
+end:
+ kfree(buffer.pointer);
+ return result;
+}
+EXPORT_SYMBOL(acpi_processor_get_psd);
+
+int acpi_processor_preregister_performance(
+ struct acpi_processor_performance __percpu *performance)
+{
+ int count_target;
+ int retval = 0;
+ unsigned int i, j;
+ cpumask_var_t covered_cpus;
+ struct acpi_processor *pr;
+ struct acpi_psd_package *pdomain;
+ struct acpi_processor *match_pr;
+ struct acpi_psd_package *match_pdomain;
+
+ if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL))
+ return -ENOMEM;
+
+ mutex_lock(&performance_mutex);
+
+ /*
+ * Check if another driver has already registered, and abort before
+ * changing pr->performance if it has. Check input data as well.
+ */
+ for_each_possible_cpu(i) {
+ pr = per_cpu(processors, i);
+ if (!pr) {
+ /* Look only at processors in ACPI namespace */
+ continue;
+ }
+
+ if (pr->performance) {
+ retval = -EBUSY;
+ goto err_out;
+ }
+
+ if (!performance || !per_cpu_ptr(performance, i)) {
+ retval = -EINVAL;
+ goto err_out;
+ }
+ }
+
+ /* Call _PSD for all CPUs */
+ for_each_possible_cpu(i) {
+ pr = per_cpu(processors, i);
+ if (!pr)
+ continue;
+
+ pr->performance = per_cpu_ptr(performance, i);
+ pdomain = &(pr->performance->domain_info);
+ if (acpi_processor_get_psd(pr->handle, pdomain)) {
+ retval = -EINVAL;
+ continue;
+ }
+ }
+ if (retval)
+ goto err_ret;
+
+ /*
+ * Now that we have _PSD data from all CPUs, lets setup P-state
+ * domain info.
+ */
+ for_each_possible_cpu(i) {
+ pr = per_cpu(processors, i);
+ if (!pr)
+ continue;
+
+ if (cpumask_test_cpu(i, covered_cpus))
+ continue;
+
+ pdomain = &(pr->performance->domain_info);
+ cpumask_set_cpu(i, pr->performance->shared_cpu_map);
+ cpumask_set_cpu(i, covered_cpus);
+ if (pdomain->num_processors <= 1)
+ continue;
+
+ /* Validate the Domain info */
+ count_target = pdomain->num_processors;
+ if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
+ pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+ else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL)
+ pr->performance->shared_type = CPUFREQ_SHARED_TYPE_HW;
+ else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)
+ pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;
+
+ for_each_possible_cpu(j) {
+ if (i == j)
+ continue;
+
+ match_pr = per_cpu(processors, j);
+ if (!match_pr)
+ continue;
+
+ match_pdomain = &(match_pr->performance->domain_info);
+ if (match_pdomain->domain != pdomain->domain)
+ continue;
+
+ /* Here i and j are in the same domain */
+
+ if (match_pdomain->num_processors != count_target) {
+ retval = -EINVAL;
+ goto err_ret;
+ }
+
+ if (pdomain->coord_type != match_pdomain->coord_type) {
+ retval = -EINVAL;
+ goto err_ret;
+ }
+
+ cpumask_set_cpu(j, covered_cpus);
+ cpumask_set_cpu(j, pr->performance->shared_cpu_map);
+ }
+
+ for_each_possible_cpu(j) {
+ if (i == j)
+ continue;
+
+ match_pr = per_cpu(processors, j);
+ if (!match_pr)
+ continue;
+
+ match_pdomain = &(match_pr->performance->domain_info);
+ if (match_pdomain->domain != pdomain->domain)
+ continue;
+
+ match_pr->performance->shared_type =
+ pr->performance->shared_type;
+ cpumask_copy(match_pr->performance->shared_cpu_map,
+ pr->performance->shared_cpu_map);
+ }
+ }
+
+err_ret:
+ for_each_possible_cpu(i) {
+ pr = per_cpu(processors, i);
+ if (!pr || !pr->performance)
+ continue;
+
+ /* Assume no coordination on any error parsing domain info */
+ if (retval) {
+ cpumask_clear(pr->performance->shared_cpu_map);
+ cpumask_set_cpu(i, pr->performance->shared_cpu_map);
+ pr->performance->shared_type = CPUFREQ_SHARED_TYPE_NONE;
+ }
+ pr->performance = NULL; /* Will be set for real in register */
+ }
+
+err_out:
+ mutex_unlock(&performance_mutex);
+ free_cpumask_var(covered_cpus);
+ return retval;
+}
+EXPORT_SYMBOL(acpi_processor_preregister_performance);
+
+int
+acpi_processor_register_performance(struct acpi_processor_performance
+ *performance, unsigned int cpu)
+{
+ struct acpi_processor *pr;
+
+ if (!acpi_processor_cpufreq_init)
+ return -EINVAL;
+
+ mutex_lock(&performance_mutex);
+
+ pr = per_cpu(processors, cpu);
+ if (!pr) {
+ mutex_unlock(&performance_mutex);
+ return -ENODEV;
+ }
+
+ if (pr->performance) {
+ mutex_unlock(&performance_mutex);
+ return -EBUSY;
+ }
+
+ WARN_ON(!performance);
+
+ pr->performance = performance;
+
+ if (acpi_processor_get_performance_info(pr)) {
+ pr->performance = NULL;
+ mutex_unlock(&performance_mutex);
+ return -EIO;
+ }
+
+ mutex_unlock(&performance_mutex);
+ return 0;
+}
+
+EXPORT_SYMBOL(acpi_processor_register_performance);
+
+void acpi_processor_unregister_performance(unsigned int cpu)
+{
+ struct acpi_processor *pr;
+
+ mutex_lock(&performance_mutex);
+
+ pr = per_cpu(processors, cpu);
+ if (!pr) {
+ mutex_unlock(&performance_mutex);
+ return;
+ }
+
+ if (pr->performance)
+ kfree(pr->performance->states);
+ pr->performance = NULL;
+
+ mutex_unlock(&performance_mutex);
+
+ return;
+}
+
+EXPORT_SYMBOL(acpi_processor_unregister_performance);
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
new file mode 100644
index 000000000..e534fd49a
--- /dev/null
+++ b/drivers/acpi/processor_thermal.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * processor_thermal.c - Passive cooling submodule of the ACPI processor driver
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de>
+ * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ * - Added processor hotplug support
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#include <linux/uaccess.h>
+
+#ifdef CONFIG_CPU_FREQ
+
+/* If a passive cooling situation is detected, primarily CPUfreq is used, as it
+ * offers (in most cases) voltage scaling in addition to frequency scaling, and
+ * thus a cubic (instead of linear) reduction of energy. Also, we allow for
+ * _any_ cpufreq driver and not only the acpi-cpufreq driver.
+ */
+
+#define CPUFREQ_THERMAL_MIN_STEP 0
+#define CPUFREQ_THERMAL_MAX_STEP 3
+
+static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg);
+
+#define reduction_pctg(cpu) \
+ per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu))
+
+/*
+ * Emulate "per package data" using per cpu data (which should really be
+ * provided elsewhere)
+ *
+ * Note we can lose a CPU on cpu hotunplug, in this case we forget the state
+ * temporarily. Fortunately that's not a big issue here (I hope)
+ */
+static int phys_package_first_cpu(int cpu)
+{
+ int i;
+ int id = topology_physical_package_id(cpu);
+
+ for_each_online_cpu(i)
+ if (topology_physical_package_id(i) == id)
+ return i;
+ return 0;
+}
+
+static int 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;
+}
+
+static int cpufreq_get_max_state(unsigned int cpu)
+{
+ if (!cpu_has_cpufreq(cpu))
+ return 0;
+
+ return CPUFREQ_THERMAL_MAX_STEP;
+}
+
+static int cpufreq_get_cur_state(unsigned int cpu)
+{
+ if (!cpu_has_cpufreq(cpu))
+ return 0;
+
+ return reduction_pctg(cpu);
+}
+
+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;
+
+ if (!cpu_has_cpufreq(cpu))
+ return 0;
+
+ reduction_pctg(cpu) = state;
+
+ /*
+ * Update all the CPUs in the same package because they all
+ * contribute to the temperature and often share the same
+ * frequency.
+ */
+ for_each_online_cpu(i) {
+ if (topology_physical_package_id(i) !=
+ topology_physical_package_id(cpu))
+ continue;
+
+ pr = per_cpu(processors, i);
+
+ if (unlikely(!freq_qos_request_active(&pr->thermal_req)))
+ continue;
+
+ policy = cpufreq_cpu_get(i);
+ if (!policy)
+ return -EINVAL;
+
+ max_freq = (policy->cpuinfo.max_freq * (100 - reduction_pctg(i) * 20)) / 100;
+
+ cpufreq_cpu_put(policy);
+
+ 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;
+}
+
+void acpi_thermal_cpufreq_init(struct cpufreq_policy *policy)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, policy->related_cpus) {
+ struct acpi_processor *pr = per_cpu(processors, cpu);
+ int ret;
+
+ if (!pr)
+ continue;
+
+ ret = freq_qos_add_request(&policy->constraints,
+ &pr->thermal_req,
+ FREQ_QOS_MAX, INT_MAX);
+ if (ret < 0)
+ pr_err("Failed to add freq constraint for CPU%d (%d)\n",
+ cpu, ret);
+ }
+}
+
+void acpi_thermal_cpufreq_exit(struct cpufreq_policy *policy)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, policy->related_cpus) {
+ struct acpi_processor *pr = per_cpu(processors, cpu);
+
+ if (pr)
+ freq_qos_remove_request(&pr->thermal_req);
+ }
+}
+#else /* ! CONFIG_CPU_FREQ */
+static int cpufreq_get_max_state(unsigned int cpu)
+{
+ return 0;
+}
+
+static int cpufreq_get_cur_state(unsigned int cpu)
+{
+ return 0;
+}
+
+static int cpufreq_set_cur_state(unsigned int cpu, int state)
+{
+ return 0;
+}
+
+#endif
+
+/* thermal cooling device callbacks */
+static int acpi_processor_max_state(struct acpi_processor *pr)
+{
+ int max_state = 0;
+
+ /*
+ * There exists four states according to
+ * cpufreq_thermal_reduction_pctg. 0, 1, 2, 3
+ */
+ max_state += cpufreq_get_max_state(pr->id);
+ if (pr->flags.throttling)
+ max_state += (pr->throttling.state_count -1);
+
+ return max_state;
+}
+static int
+processor_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct acpi_device *device = cdev->devdata;
+ struct acpi_processor *pr;
+
+ if (!device)
+ return -EINVAL;
+
+ pr = acpi_driver_data(device);
+ if (!pr)
+ return -EINVAL;
+
+ *state = acpi_processor_max_state(pr);
+ return 0;
+}
+
+static int
+processor_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *cur_state)
+{
+ struct acpi_device *device = cdev->devdata;
+ struct acpi_processor *pr;
+
+ if (!device)
+ return -EINVAL;
+
+ pr = acpi_driver_data(device);
+ if (!pr)
+ return -EINVAL;
+
+ *cur_state = cpufreq_get_cur_state(pr->id);
+ if (pr->flags.throttling)
+ *cur_state += pr->throttling.state;
+ return 0;
+}
+
+static int
+processor_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ struct acpi_device *device = cdev->devdata;
+ struct acpi_processor *pr;
+ int result = 0;
+ int max_pstate;
+
+ if (!device)
+ return -EINVAL;
+
+ pr = acpi_driver_data(device);
+ if (!pr)
+ return -EINVAL;
+
+ max_pstate = cpufreq_get_max_state(pr->id);
+
+ if (state > acpi_processor_max_state(pr))
+ return -EINVAL;
+
+ if (state <= max_pstate) {
+ if (pr->flags.throttling && pr->throttling.state)
+ result = acpi_processor_set_throttling(pr, 0, false);
+ cpufreq_set_cur_state(pr->id, state);
+ } else {
+ cpufreq_set_cur_state(pr->id, max_pstate);
+ result = acpi_processor_set_throttling(pr,
+ state - max_pstate, false);
+ }
+ return result;
+}
+
+const struct thermal_cooling_device_ops processor_cooling_ops = {
+ .get_max_state = processor_get_max_state,
+ .get_cur_state = processor_get_cur_state,
+ .set_cur_state = processor_set_cur_state,
+};
+
+int acpi_processor_thermal_init(struct acpi_processor *pr,
+ struct acpi_device *device)
+{
+ int result = 0;
+
+ pr->cdev = thermal_cooling_device_register("Processor", device,
+ &processor_cooling_ops);
+ if (IS_ERR(pr->cdev)) {
+ result = PTR_ERR(pr->cdev);
+ return result;
+ }
+
+ dev_dbg(&device->dev, "registered as cooling_device%d\n",
+ pr->cdev->id);
+
+ result = sysfs_create_link(&device->dev.kobj,
+ &pr->cdev->device.kobj,
+ "thermal_cooling");
+ if (result) {
+ dev_err(&device->dev,
+ "Failed to create sysfs link 'thermal_cooling'\n");
+ goto err_thermal_unregister;
+ }
+
+ result = sysfs_create_link(&pr->cdev->device.kobj,
+ &device->dev.kobj,
+ "device");
+ if (result) {
+ dev_err(&pr->cdev->device,
+ "Failed to create sysfs link 'device'\n");
+ goto err_remove_sysfs_thermal;
+ }
+
+ return 0;
+
+err_remove_sysfs_thermal:
+ sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
+err_thermal_unregister:
+ thermal_cooling_device_unregister(pr->cdev);
+
+ return result;
+}
+
+void acpi_processor_thermal_exit(struct acpi_processor *pr,
+ struct acpi_device *device)
+{
+ if (pr->cdev) {
+ sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
+ sysfs_remove_link(&pr->cdev->device.kobj, "device");
+ thermal_cooling_device_unregister(pr->cdev);
+ pr->cdev = NULL;
+ }
+}
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
new file mode 100644
index 000000000..a822fe410
--- /dev/null
+++ b/drivers/acpi/processor_throttling.c
@@ -0,0 +1,1244 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * processor_throttling.c - Throttling submodule of the ACPI processor driver
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de>
+ * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ * - Added processor hotplug support
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#include <asm/io.h>
+#include <linux/uaccess.h>
+
+/* ignore_tpc:
+ * 0 -> acpi processor driver doesn't ignore _TPC values
+ * 1 -> acpi processor driver ignores _TPC values
+ */
+static int ignore_tpc;
+module_param(ignore_tpc, int, 0644);
+MODULE_PARM_DESC(ignore_tpc, "Disable broken BIOS _TPC throttling support");
+
+struct throttling_tstate {
+ unsigned int cpu; /* cpu nr */
+ int target_state; /* target T-state */
+};
+
+struct acpi_processor_throttling_arg {
+ struct acpi_processor *pr;
+ int target_state;
+ bool force;
+};
+
+#define THROTTLING_PRECHANGE (1)
+#define THROTTLING_POSTCHANGE (2)
+
+static int acpi_processor_get_throttling(struct acpi_processor *pr);
+static int __acpi_processor_set_throttling(struct acpi_processor *pr,
+ int state, bool force, bool direct);
+
+static int acpi_processor_update_tsd_coord(void)
+{
+ int count, count_target;
+ int retval = 0;
+ unsigned int i, j;
+ cpumask_var_t covered_cpus;
+ struct acpi_processor *pr, *match_pr;
+ struct acpi_tsd_package *pdomain, *match_pdomain;
+ struct acpi_processor_throttling *pthrottling, *match_pthrottling;
+
+ if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL))
+ return -ENOMEM;
+
+ /*
+ * Now that we have _TSD data from all CPUs, lets setup T-state
+ * coordination between all CPUs.
+ */
+ for_each_possible_cpu(i) {
+ pr = per_cpu(processors, i);
+ if (!pr)
+ continue;
+
+ /* Basic validity check for domain info */
+ pthrottling = &(pr->throttling);
+
+ /*
+ * If tsd package for one cpu is invalid, the coordination
+ * among all CPUs is thought as invalid.
+ * Maybe it is ugly.
+ */
+ if (!pthrottling->tsd_valid_flag) {
+ retval = -EINVAL;
+ break;
+ }
+ }
+ if (retval)
+ goto err_ret;
+
+ for_each_possible_cpu(i) {
+ pr = per_cpu(processors, i);
+ if (!pr)
+ continue;
+
+ if (cpumask_test_cpu(i, covered_cpus))
+ continue;
+ pthrottling = &pr->throttling;
+
+ pdomain = &(pthrottling->domain_info);
+ cpumask_set_cpu(i, pthrottling->shared_cpu_map);
+ cpumask_set_cpu(i, covered_cpus);
+ /*
+ * If the number of processor in the TSD domain is 1, it is
+ * unnecessary to parse the coordination for this CPU.
+ */
+ if (pdomain->num_processors <= 1)
+ continue;
+
+ /* Validate the Domain info */
+ count_target = pdomain->num_processors;
+ count = 1;
+
+ for_each_possible_cpu(j) {
+ if (i == j)
+ continue;
+
+ match_pr = per_cpu(processors, j);
+ if (!match_pr)
+ continue;
+
+ match_pthrottling = &(match_pr->throttling);
+ match_pdomain = &(match_pthrottling->domain_info);
+ if (match_pdomain->domain != pdomain->domain)
+ continue;
+
+ /* Here i and j are in the same domain.
+ * If two TSD packages have the same domain, they
+ * should have the same num_porcessors and
+ * coordination type. Otherwise it will be regarded
+ * as illegal.
+ */
+ if (match_pdomain->num_processors != count_target) {
+ retval = -EINVAL;
+ goto err_ret;
+ }
+
+ if (pdomain->coord_type != match_pdomain->coord_type) {
+ retval = -EINVAL;
+ goto err_ret;
+ }
+
+ cpumask_set_cpu(j, covered_cpus);
+ cpumask_set_cpu(j, pthrottling->shared_cpu_map);
+ count++;
+ }
+ for_each_possible_cpu(j) {
+ if (i == j)
+ continue;
+
+ match_pr = per_cpu(processors, j);
+ if (!match_pr)
+ continue;
+
+ match_pthrottling = &(match_pr->throttling);
+ match_pdomain = &(match_pthrottling->domain_info);
+ if (match_pdomain->domain != pdomain->domain)
+ continue;
+
+ /*
+ * If some CPUS have the same domain, they
+ * will have the same shared_cpu_map.
+ */
+ cpumask_copy(match_pthrottling->shared_cpu_map,
+ pthrottling->shared_cpu_map);
+ }
+ }
+
+err_ret:
+ free_cpumask_var(covered_cpus);
+
+ for_each_possible_cpu(i) {
+ pr = per_cpu(processors, i);
+ if (!pr)
+ continue;
+
+ /*
+ * Assume no coordination on any error parsing domain info.
+ * The coordination type will be forced as SW_ALL.
+ */
+ if (retval) {
+ pthrottling = &(pr->throttling);
+ cpumask_clear(pthrottling->shared_cpu_map);
+ cpumask_set_cpu(i, pthrottling->shared_cpu_map);
+ pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL;
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Update the T-state coordination after the _TSD
+ * data for all cpus is obtained.
+ */
+void acpi_processor_throttling_init(void)
+{
+ if (acpi_processor_update_tsd_coord())
+ pr_debug("Assume no T-state coordination\n");
+}
+
+static int acpi_processor_throttling_notifier(unsigned long event, void *data)
+{
+ struct throttling_tstate *p_tstate = data;
+ struct acpi_processor *pr;
+ unsigned int cpu;
+ int target_state;
+ struct acpi_processor_limit *p_limit;
+ struct acpi_processor_throttling *p_throttling;
+
+ cpu = p_tstate->cpu;
+ pr = per_cpu(processors, cpu);
+ if (!pr) {
+ pr_debug("Invalid pr pointer\n");
+ return 0;
+ }
+ if (!pr->flags.throttling) {
+ acpi_handle_debug(pr->handle,
+ "Throttling control unsupported on CPU %d\n",
+ cpu);
+ return 0;
+ }
+ target_state = p_tstate->target_state;
+ p_throttling = &(pr->throttling);
+ switch (event) {
+ case THROTTLING_PRECHANGE:
+ /*
+ * Prechange event is used to choose one proper t-state,
+ * which meets the limits of thermal, user and _TPC.
+ */
+ p_limit = &pr->limit;
+ if (p_limit->thermal.tx > target_state)
+ target_state = p_limit->thermal.tx;
+ if (p_limit->user.tx > target_state)
+ target_state = p_limit->user.tx;
+ 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");
+ target_state = p_throttling->state_count - 1;
+ }
+ p_tstate->target_state = target_state;
+ acpi_handle_debug(pr->handle,
+ "PreChange Event: target T-state of CPU %d is T%d\n",
+ cpu, target_state);
+ break;
+ case THROTTLING_POSTCHANGE:
+ /*
+ * Postchange event is only used to update the
+ * T-state flag of acpi_processor_throttling.
+ */
+ p_throttling->state = target_state;
+ acpi_handle_debug(pr->handle,
+ "PostChange Event: CPU %d is switched to T%d\n",
+ cpu, target_state);
+ break;
+ default:
+ pr_warn("Unsupported Throttling notifier event\n");
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * _TPC - Throttling Present Capabilities
+ */
+static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
+{
+ acpi_status status = 0;
+ unsigned long long tpc = 0;
+
+ if (!pr)
+ return -EINVAL;
+
+ if (ignore_tpc)
+ goto end;
+
+ status = acpi_evaluate_integer(pr->handle, "_TPC", NULL, &tpc);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND)
+ acpi_evaluation_failure_warn(pr->handle, "_TPC", status);
+
+ return -ENODEV;
+ }
+
+end:
+ pr->throttling_platform_limit = (int)tpc;
+ return 0;
+}
+
+int acpi_processor_tstate_has_changed(struct acpi_processor *pr)
+{
+ int result = 0;
+ int throttling_limit;
+ int current_state;
+ struct acpi_processor_limit *limit;
+ int target_state;
+
+ if (ignore_tpc)
+ return 0;
+
+ result = acpi_processor_get_platform_limit(pr);
+ if (result) {
+ /* Throttling Limit is unsupported */
+ return result;
+ }
+
+ throttling_limit = pr->throttling_platform_limit;
+ if (throttling_limit >= pr->throttling.state_count) {
+ /* Uncorrect Throttling Limit */
+ return -EINVAL;
+ }
+
+ current_state = pr->throttling.state;
+ if (current_state > throttling_limit) {
+ /*
+ * The current state can meet the requirement of
+ * _TPC limit. But it is reasonable that OSPM changes
+ * t-states from high to low for better performance.
+ * Of course the limit condition of thermal
+ * and user should be considered.
+ */
+ limit = &pr->limit;
+ target_state = throttling_limit;
+ if (limit->thermal.tx > target_state)
+ target_state = limit->thermal.tx;
+ if (limit->user.tx > target_state)
+ target_state = limit->user.tx;
+ } else if (current_state == throttling_limit) {
+ /*
+ * Unnecessary to change the throttling state
+ */
+ return 0;
+ } else {
+ /*
+ * If the current state is lower than the limit of _TPC, it
+ * will be forced to switch to the throttling state defined
+ * by throttling_platfor_limit.
+ * Because the previous state meets with the limit condition
+ * of thermal and user, it is unnecessary to check it again.
+ */
+ target_state = throttling_limit;
+ }
+ return acpi_processor_set_throttling(pr, target_state, false);
+}
+
+/*
+ * This function is used to reevaluate whether the T-state is valid
+ * after one CPU is onlined/offlined.
+ * It is noted that it won't reevaluate the following properties for
+ * the T-state.
+ * 1. Control method.
+ * 2. the number of supported T-state
+ * 3. TSD domain
+ */
+void acpi_processor_reevaluate_tstate(struct acpi_processor *pr,
+ bool is_dead)
+{
+ int result = 0;
+
+ if (is_dead) {
+ /* When one CPU is offline, the T-state throttling
+ * will be invalidated.
+ */
+ pr->flags.throttling = 0;
+ return;
+ }
+ /* the following is to recheck whether the T-state is valid for
+ * the online CPU
+ */
+ if (!pr->throttling.state_count) {
+ /* If the number of T-state is invalid, it is
+ * invalidated.
+ */
+ pr->flags.throttling = 0;
+ return;
+ }
+ pr->flags.throttling = 1;
+
+ /* Disable throttling (if enabled). We'll let subsequent
+ * policy (e.g.thermal) decide to lower performance if it
+ * so chooses, but for now we'll crank up the speed.
+ */
+
+ result = acpi_processor_get_throttling(pr);
+ if (result)
+ goto end;
+
+ if (pr->throttling.state) {
+ result = acpi_processor_set_throttling(pr, 0, false);
+ if (result)
+ goto end;
+ }
+
+end:
+ if (result)
+ pr->flags.throttling = 0;
+}
+/*
+ * _PTC - Processor Throttling Control (and status) register location
+ */
+static int acpi_processor_get_throttling_control(struct acpi_processor *pr)
+{
+ int result = 0;
+ acpi_status status = 0;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *ptc = NULL;
+ union acpi_object obj;
+ struct acpi_processor_throttling *throttling;
+
+ status = acpi_evaluate_object(pr->handle, "_PTC", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND)
+ acpi_evaluation_failure_warn(pr->handle, "_PTC", status);
+
+ return -ENODEV;
+ }
+
+ ptc = (union acpi_object *)buffer.pointer;
+ if (!ptc || (ptc->type != ACPI_TYPE_PACKAGE)
+ || (ptc->package.count != 2)) {
+ pr_err("Invalid _PTC data\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ /*
+ * control_register
+ */
+
+ obj = ptc->package.elements[0];
+
+ if ((obj.type != ACPI_TYPE_BUFFER)
+ || (obj.buffer.length < sizeof(struct acpi_ptc_register))
+ || (obj.buffer.pointer == NULL)) {
+ pr_err("Invalid _PTC data (control_register)\n");
+ result = -EFAULT;
+ goto end;
+ }
+ memcpy(&pr->throttling.control_register, obj.buffer.pointer,
+ sizeof(struct acpi_ptc_register));
+
+ /*
+ * status_register
+ */
+
+ obj = ptc->package.elements[1];
+
+ if ((obj.type != ACPI_TYPE_BUFFER)
+ || (obj.buffer.length < sizeof(struct acpi_ptc_register))
+ || (obj.buffer.pointer == NULL)) {
+ pr_err("Invalid _PTC data (status_register)\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ memcpy(&pr->throttling.status_register, obj.buffer.pointer,
+ sizeof(struct acpi_ptc_register));
+
+ throttling = &pr->throttling;
+
+ if ((throttling->control_register.bit_width +
+ throttling->control_register.bit_offset) > 32) {
+ pr_err("Invalid _PTC control register\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ if ((throttling->status_register.bit_width +
+ throttling->status_register.bit_offset) > 32) {
+ pr_err("Invalid _PTC status register\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+end:
+ kfree(buffer.pointer);
+
+ return result;
+}
+
+/*
+ * _TSS - Throttling Supported States
+ */
+static int acpi_processor_get_throttling_states(struct acpi_processor *pr)
+{
+ int result = 0;
+ acpi_status status = AE_OK;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
+ struct acpi_buffer state = { 0, NULL };
+ union acpi_object *tss = NULL;
+ int i;
+
+ status = acpi_evaluate_object(pr->handle, "_TSS", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND)
+ acpi_evaluation_failure_warn(pr->handle, "_TSS", status);
+
+ return -ENODEV;
+ }
+
+ tss = buffer.pointer;
+ if (!tss || (tss->type != ACPI_TYPE_PACKAGE)) {
+ pr_err("Invalid _TSS data\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ acpi_handle_debug(pr->handle, "Found %d throttling states\n",
+ tss->package.count);
+
+ pr->throttling.state_count = tss->package.count;
+ pr->throttling.states_tss =
+ kmalloc_array(tss->package.count,
+ sizeof(struct acpi_processor_tx_tss),
+ GFP_KERNEL);
+ if (!pr->throttling.states_tss) {
+ result = -ENOMEM;
+ goto end;
+ }
+
+ for (i = 0; i < pr->throttling.state_count; i++) {
+
+ struct acpi_processor_tx_tss *tx =
+ (struct acpi_processor_tx_tss *)&(pr->throttling.
+ states_tss[i]);
+
+ state.length = sizeof(struct acpi_processor_tx_tss);
+ state.pointer = tx;
+
+ acpi_handle_debug(pr->handle, "Extracting state %d\n", i);
+
+ status = acpi_extract_package(&(tss->package.elements[i]),
+ &format, &state);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_warn(pr->handle, "Invalid _TSS data: %s\n",
+ acpi_format_exception(status));
+ result = -EFAULT;
+ kfree(pr->throttling.states_tss);
+ goto end;
+ }
+
+ if (!tx->freqpercentage) {
+ pr_err("Invalid _TSS data: freq is zero\n");
+ result = -EFAULT;
+ kfree(pr->throttling.states_tss);
+ goto end;
+ }
+ }
+
+end:
+ kfree(buffer.pointer);
+
+ return result;
+}
+
+/*
+ * _TSD - T-State Dependencies
+ */
+static int acpi_processor_get_tsd(struct acpi_processor *pr)
+{
+ int result = 0;
+ acpi_status status = AE_OK;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
+ struct acpi_buffer state = { 0, NULL };
+ union acpi_object *tsd = NULL;
+ struct acpi_tsd_package *pdomain;
+ struct acpi_processor_throttling *pthrottling;
+
+ pthrottling = &pr->throttling;
+ pthrottling->tsd_valid_flag = 0;
+
+ status = acpi_evaluate_object(pr->handle, "_TSD", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND)
+ acpi_evaluation_failure_warn(pr->handle, "_TSD", status);
+
+ return -ENODEV;
+ }
+
+ tsd = buffer.pointer;
+ if (!tsd || (tsd->type != ACPI_TYPE_PACKAGE)) {
+ pr_err("Invalid _TSD data\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ if (tsd->package.count != 1) {
+ pr_err("Invalid _TSD data\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ pdomain = &(pr->throttling.domain_info);
+
+ state.length = sizeof(struct acpi_tsd_package);
+ state.pointer = pdomain;
+
+ status = acpi_extract_package(&(tsd->package.elements[0]),
+ &format, &state);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Invalid _TSD data\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ if (pdomain->num_entries != ACPI_TSD_REV0_ENTRIES) {
+ pr_err("Unknown _TSD:num_entries\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ if (pdomain->revision != ACPI_TSD_REV0_REVISION) {
+ pr_err("Unknown _TSD:revision\n");
+ result = -EFAULT;
+ goto end;
+ }
+
+ pthrottling = &pr->throttling;
+ pthrottling->tsd_valid_flag = 1;
+ pthrottling->shared_type = pdomain->coord_type;
+ cpumask_set_cpu(pr->id, pthrottling->shared_cpu_map);
+ /*
+ * If the coordination type is not defined in ACPI spec,
+ * the tsd_valid_flag will be clear and coordination type
+ * will be forecd as DOMAIN_COORD_TYPE_SW_ALL.
+ */
+ if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
+ pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
+ pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
+ pthrottling->tsd_valid_flag = 0;
+ pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL;
+ }
+
+end:
+ kfree(buffer.pointer);
+ return result;
+}
+
+/* --------------------------------------------------------------------------
+ Throttling Control
+ -------------------------------------------------------------------------- */
+static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr)
+{
+ int state = 0;
+ u32 value = 0;
+ u32 duty_mask = 0;
+ u32 duty_value = 0;
+
+ if (!pr)
+ return -EINVAL;
+
+ if (!pr->flags.throttling)
+ return -ENODEV;
+
+ /*
+ * We don't care about error returns - we just try to mark
+ * these reserved so that nobody else is confused into thinking
+ * that this region might be unused..
+ *
+ * (In particular, allocating the IO range for Cardbus)
+ */
+ request_region(pr->throttling.address, 6, "ACPI CPU throttle");
+
+ pr->throttling.state = 0;
+
+ duty_mask = pr->throttling.state_count - 1;
+
+ duty_mask <<= pr->throttling.duty_offset;
+
+ local_irq_disable();
+
+ value = inl(pr->throttling.address);
+
+ /*
+ * Compute the current throttling state when throttling is enabled
+ * (bit 4 is on).
+ */
+ if (value & 0x10) {
+ duty_value = value & duty_mask;
+ duty_value >>= pr->throttling.duty_offset;
+
+ if (duty_value)
+ state = pr->throttling.state_count - duty_value;
+ }
+
+ pr->throttling.state = state;
+
+ local_irq_enable();
+
+ acpi_handle_debug(pr->handle,
+ "Throttling state is T%d (%d%% throttling applied)\n",
+ state, pr->throttling.states[state].performance);
+
+ return 0;
+}
+
+#ifdef CONFIG_X86
+static int acpi_throttling_rdmsr(u64 *value)
+{
+ u64 msr_high, msr_low;
+ u64 msr = 0;
+ int ret = -1;
+
+ if ((this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_INTEL) ||
+ !this_cpu_has(X86_FEATURE_ACPI)) {
+ pr_err("HARDWARE addr space,NOT supported yet\n");
+ } else {
+ msr_low = 0;
+ msr_high = 0;
+ rdmsr_safe(MSR_IA32_THERM_CONTROL,
+ (u32 *)&msr_low, (u32 *) &msr_high);
+ msr = (msr_high << 32) | msr_low;
+ *value = (u64) msr;
+ ret = 0;
+ }
+ return ret;
+}
+
+static int acpi_throttling_wrmsr(u64 value)
+{
+ int ret = -1;
+ u64 msr;
+
+ if ((this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_INTEL) ||
+ !this_cpu_has(X86_FEATURE_ACPI)) {
+ pr_err("HARDWARE addr space,NOT supported yet\n");
+ } else {
+ msr = value;
+ wrmsr_safe(MSR_IA32_THERM_CONTROL,
+ msr & 0xffffffff, msr >> 32);
+ ret = 0;
+ }
+ return ret;
+}
+#else
+static int acpi_throttling_rdmsr(u64 *value)
+{
+ pr_err("HARDWARE addr space,NOT supported yet\n");
+ return -1;
+}
+
+static int acpi_throttling_wrmsr(u64 value)
+{
+ pr_err("HARDWARE addr space,NOT supported yet\n");
+ return -1;
+}
+#endif
+
+static int acpi_read_throttling_status(struct acpi_processor *pr,
+ u64 *value)
+{
+ u32 bit_width, bit_offset;
+ u32 ptc_value;
+ u64 ptc_mask;
+ struct acpi_processor_throttling *throttling;
+ int ret = -1;
+
+ throttling = &pr->throttling;
+ switch (throttling->status_register.space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ bit_width = throttling->status_register.bit_width;
+ bit_offset = throttling->status_register.bit_offset;
+
+ acpi_os_read_port((acpi_io_address) throttling->status_register.
+ address, &ptc_value,
+ (u32) (bit_width + bit_offset));
+ ptc_mask = (1 << bit_width) - 1;
+ *value = (u64) ((ptc_value >> bit_offset) & ptc_mask);
+ ret = 0;
+ break;
+ case ACPI_ADR_SPACE_FIXED_HARDWARE:
+ ret = acpi_throttling_rdmsr(value);
+ break;
+ default:
+ pr_err("Unknown addr space %d\n",
+ (u32) (throttling->status_register.space_id));
+ }
+ return ret;
+}
+
+static int acpi_write_throttling_state(struct acpi_processor *pr,
+ u64 value)
+{
+ u32 bit_width, bit_offset;
+ u64 ptc_value;
+ u64 ptc_mask;
+ struct acpi_processor_throttling *throttling;
+ int ret = -1;
+
+ throttling = &pr->throttling;
+ switch (throttling->control_register.space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ bit_width = throttling->control_register.bit_width;
+ bit_offset = throttling->control_register.bit_offset;
+ ptc_mask = (1 << bit_width) - 1;
+ ptc_value = value & ptc_mask;
+
+ acpi_os_write_port((acpi_io_address) throttling->
+ control_register.address,
+ (u32) (ptc_value << bit_offset),
+ (u32) (bit_width + bit_offset));
+ ret = 0;
+ break;
+ case ACPI_ADR_SPACE_FIXED_HARDWARE:
+ ret = acpi_throttling_wrmsr(value);
+ break;
+ default:
+ pr_err("Unknown addr space %d\n",
+ (u32) (throttling->control_register.space_id));
+ }
+ return ret;
+}
+
+static int acpi_get_throttling_state(struct acpi_processor *pr,
+ u64 value)
+{
+ int i;
+
+ for (i = 0; i < pr->throttling.state_count; i++) {
+ struct acpi_processor_tx_tss *tx =
+ (struct acpi_processor_tx_tss *)&(pr->throttling.
+ states_tss[i]);
+ if (tx->control == value)
+ return i;
+ }
+ return -1;
+}
+
+static int acpi_get_throttling_value(struct acpi_processor *pr,
+ int state, u64 *value)
+{
+ int ret = -1;
+
+ if (state >= 0 && state <= pr->throttling.state_count) {
+ struct acpi_processor_tx_tss *tx =
+ (struct acpi_processor_tx_tss *)&(pr->throttling.
+ states_tss[state]);
+ *value = tx->control;
+ ret = 0;
+ }
+ return ret;
+}
+
+static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr)
+{
+ int state = 0;
+ int ret;
+ u64 value;
+
+ if (!pr)
+ return -EINVAL;
+
+ if (!pr->flags.throttling)
+ return -ENODEV;
+
+ pr->throttling.state = 0;
+
+ value = 0;
+ ret = acpi_read_throttling_status(pr, &value);
+ if (ret >= 0) {
+ state = acpi_get_throttling_state(pr, value);
+ if (state == -1) {
+ acpi_handle_debug(pr->handle,
+ "Invalid throttling state, reset\n");
+ state = 0;
+ ret = __acpi_processor_set_throttling(pr, state, true,
+ true);
+ if (ret)
+ return ret;
+ }
+ pr->throttling.state = state;
+ }
+
+ return 0;
+}
+
+static long __acpi_processor_get_throttling(void *data)
+{
+ struct acpi_processor *pr = data;
+
+ return pr->throttling.acpi_processor_get_throttling(pr);
+}
+
+static int acpi_processor_get_throttling(struct acpi_processor *pr)
+{
+ if (!pr)
+ return -EINVAL;
+
+ if (!pr->flags.throttling)
+ return -ENODEV;
+
+ /*
+ * This is either called from the CPU hotplug callback of
+ * processor_driver or via the ACPI probe function. In the latter
+ * case the CPU is not guaranteed to be online. Both call sites are
+ * protected against CPU hotplug.
+ */
+ if (!cpu_online(pr->id))
+ return -ENODEV;
+
+ return call_on_cpu(pr->id, __acpi_processor_get_throttling, pr, false);
+}
+
+static int acpi_processor_get_fadt_info(struct acpi_processor *pr)
+{
+ int i, step;
+
+ if (!pr->throttling.address) {
+ acpi_handle_debug(pr->handle, "No throttling register\n");
+ return -EINVAL;
+ } else if (!pr->throttling.duty_width) {
+ acpi_handle_debug(pr->handle, "No throttling states\n");
+ return -EINVAL;
+ }
+ /* TBD: Support duty_cycle values that span bit 4. */
+ else if ((pr->throttling.duty_offset + pr->throttling.duty_width) > 4) {
+ pr_warn("duty_cycle spans bit 4\n");
+ return -EINVAL;
+ }
+
+ pr->throttling.state_count = 1 << acpi_gbl_FADT.duty_width;
+
+ /*
+ * Compute state values. Note that throttling displays a linear power
+ * performance relationship (at 50% performance the CPU will consume
+ * 50% power). Values are in 1/10th of a percent to preserve accuracy.
+ */
+
+ step = (1000 / pr->throttling.state_count);
+
+ for (i = 0; i < pr->throttling.state_count; i++) {
+ pr->throttling.states[i].performance = 1000 - step * i;
+ pr->throttling.states[i].power = 1000 - step * i;
+ }
+ return 0;
+}
+
+static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr,
+ int state, bool force)
+{
+ u32 value = 0;
+ u32 duty_mask = 0;
+ u32 duty_value = 0;
+
+ if (!pr)
+ return -EINVAL;
+
+ if ((state < 0) || (state > (pr->throttling.state_count - 1)))
+ return -EINVAL;
+
+ if (!pr->flags.throttling)
+ return -ENODEV;
+
+ if (!force && (state == pr->throttling.state))
+ return 0;
+
+ if (state < pr->throttling_platform_limit)
+ return -EPERM;
+ /*
+ * Calculate the duty_value and duty_mask.
+ */
+ if (state) {
+ duty_value = pr->throttling.state_count - state;
+
+ duty_value <<= pr->throttling.duty_offset;
+
+ /* Used to clear all duty_value bits */
+ duty_mask = pr->throttling.state_count - 1;
+
+ duty_mask <<= acpi_gbl_FADT.duty_offset;
+ duty_mask = ~duty_mask;
+ }
+
+ local_irq_disable();
+
+ /*
+ * Disable throttling by writing a 0 to bit 4. Note that we must
+ * turn it off before you can change the duty_value.
+ */
+ value = inl(pr->throttling.address);
+ if (value & 0x10) {
+ value &= 0xFFFFFFEF;
+ outl(value, pr->throttling.address);
+ }
+
+ /*
+ * Write the new duty_value and then enable throttling. Note
+ * that a state value of 0 leaves throttling disabled.
+ */
+ if (state) {
+ value &= duty_mask;
+ value |= duty_value;
+ outl(value, pr->throttling.address);
+
+ value |= 0x00000010;
+ outl(value, pr->throttling.address);
+ }
+
+ pr->throttling.state = state;
+
+ local_irq_enable();
+
+ acpi_handle_debug(pr->handle,
+ "Throttling state set to T%d (%d%%)\n", state,
+ (pr->throttling.states[state].performance ? pr->
+ throttling.states[state].performance / 10 : 0));
+
+ return 0;
+}
+
+static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr,
+ int state, bool force)
+{
+ int ret;
+ u64 value;
+
+ if (!pr)
+ return -EINVAL;
+
+ if ((state < 0) || (state > (pr->throttling.state_count - 1)))
+ return -EINVAL;
+
+ if (!pr->flags.throttling)
+ return -ENODEV;
+
+ if (!force && (state == pr->throttling.state))
+ return 0;
+
+ if (state < pr->throttling_platform_limit)
+ return -EPERM;
+
+ value = 0;
+ ret = acpi_get_throttling_value(pr, state, &value);
+ if (ret >= 0) {
+ acpi_write_throttling_state(pr, value);
+ pr->throttling.state = state;
+ }
+
+ return 0;
+}
+
+static long acpi_processor_throttling_fn(void *data)
+{
+ struct acpi_processor_throttling_arg *arg = data;
+ struct acpi_processor *pr = arg->pr;
+
+ return pr->throttling.acpi_processor_set_throttling(pr,
+ arg->target_state, arg->force);
+}
+
+static int __acpi_processor_set_throttling(struct acpi_processor *pr,
+ int state, bool force, bool direct)
+{
+ int ret = 0;
+ unsigned int i;
+ struct acpi_processor *match_pr;
+ struct acpi_processor_throttling *p_throttling;
+ struct acpi_processor_throttling_arg arg;
+ struct throttling_tstate t_state;
+
+ if (!pr)
+ return -EINVAL;
+
+ if (!pr->flags.throttling)
+ return -ENODEV;
+
+ if ((state < 0) || (state > (pr->throttling.state_count - 1)))
+ return -EINVAL;
+
+ if (cpu_is_offline(pr->id)) {
+ /*
+ * the cpu pointed by pr->id is offline. Unnecessary to change
+ * the throttling state any more.
+ */
+ return -ENODEV;
+ }
+
+ t_state.target_state = state;
+ p_throttling = &(pr->throttling);
+
+ /*
+ * The throttling notifier will be called for every
+ * affected cpu in order to get one proper T-state.
+ * The notifier event is THROTTLING_PRECHANGE.
+ */
+ for_each_cpu_and(i, cpu_online_mask, p_throttling->shared_cpu_map) {
+ t_state.cpu = i;
+ acpi_processor_throttling_notifier(THROTTLING_PRECHANGE,
+ &t_state);
+ }
+ /*
+ * The function of acpi_processor_set_throttling will be called
+ * to switch T-state. If the coordination type is SW_ALL or HW_ALL,
+ * it is necessary to call it for every affected cpu. Otherwise
+ * it can be called only for the cpu pointed by pr.
+ */
+ if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) {
+ arg.pr = pr;
+ arg.target_state = state;
+ arg.force = force;
+ ret = call_on_cpu(pr->id, acpi_processor_throttling_fn, &arg,
+ direct);
+ } else {
+ /*
+ * When the T-state coordination is SW_ALL or HW_ALL,
+ * it is necessary to set T-state for every affected
+ * cpus.
+ */
+ for_each_cpu_and(i, cpu_online_mask,
+ p_throttling->shared_cpu_map) {
+ match_pr = per_cpu(processors, i);
+ /*
+ * If the pointer is invalid, we will report the
+ * error message and continue.
+ */
+ if (!match_pr) {
+ acpi_handle_debug(pr->handle,
+ "Invalid Pointer for CPU %d\n", i);
+ continue;
+ }
+ /*
+ * If the throttling control is unsupported on CPU i,
+ * we will report the error message and continue.
+ */
+ if (!match_pr->flags.throttling) {
+ acpi_handle_debug(pr->handle,
+ "Throttling Control unsupported on CPU %d\n", i);
+ continue;
+ }
+
+ arg.pr = match_pr;
+ arg.target_state = state;
+ arg.force = force;
+ ret = call_on_cpu(pr->id, acpi_processor_throttling_fn,
+ &arg, direct);
+ }
+ }
+ /*
+ * After the set_throttling is called, the
+ * throttling notifier is called for every
+ * affected cpu to update the T-states.
+ * The notifier event is THROTTLING_POSTCHANGE
+ */
+ for_each_cpu_and(i, cpu_online_mask, p_throttling->shared_cpu_map) {
+ t_state.cpu = i;
+ acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE,
+ &t_state);
+ }
+
+ return ret;
+}
+
+int acpi_processor_set_throttling(struct acpi_processor *pr, int state,
+ bool force)
+{
+ return __acpi_processor_set_throttling(pr, state, force, false);
+}
+
+int acpi_processor_get_throttling_info(struct acpi_processor *pr)
+{
+ int result = 0;
+ struct acpi_processor_throttling *pthrottling;
+
+ acpi_handle_debug(pr->handle,
+ "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n",
+ pr->throttling.address,
+ pr->throttling.duty_offset,
+ pr->throttling.duty_width);
+
+ /*
+ * Evaluate _PTC, _TSS and _TPC
+ * They must all be present or none of them can be used.
+ */
+ if (acpi_processor_get_throttling_control(pr) ||
+ acpi_processor_get_throttling_states(pr) ||
+ acpi_processor_get_platform_limit(pr)) {
+ pr->throttling.acpi_processor_get_throttling =
+ &acpi_processor_get_throttling_fadt;
+ pr->throttling.acpi_processor_set_throttling =
+ &acpi_processor_set_throttling_fadt;
+ if (acpi_processor_get_fadt_info(pr))
+ return 0;
+ } else {
+ pr->throttling.acpi_processor_get_throttling =
+ &acpi_processor_get_throttling_ptc;
+ pr->throttling.acpi_processor_set_throttling =
+ &acpi_processor_set_throttling_ptc;
+ }
+
+ /*
+ * If TSD package for one CPU can't be parsed successfully, it means
+ * that this CPU will have no coordination with other CPUs.
+ */
+ if (acpi_processor_get_tsd(pr)) {
+ pthrottling = &pr->throttling;
+ pthrottling->tsd_valid_flag = 0;
+ cpumask_set_cpu(pr->id, pthrottling->shared_cpu_map);
+ pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL;
+ }
+
+ /*
+ * PIIX4 Errata: We don't support throttling on the original PIIX4.
+ * This shouldn't be an issue as few (if any) mobile systems ever
+ * used this part.
+ */
+ if (errata.piix4.throttle) {
+ acpi_handle_debug(pr->handle,
+ "Throttling not supported on PIIX4 A- or B-step\n");
+ return 0;
+ }
+
+ acpi_handle_debug(pr->handle, "Found %d throttling states\n",
+ pr->throttling.state_count);
+
+ pr->flags.throttling = 1;
+
+ /*
+ * Disable throttling (if enabled). We'll let subsequent policy (e.g.
+ * thermal) decide to lower performance if it so chooses, but for now
+ * we'll crank up the speed.
+ */
+
+ result = acpi_processor_get_throttling(pr);
+ if (result)
+ goto end;
+
+ if (pr->throttling.state) {
+ acpi_handle_debug(pr->handle,
+ "Disabling throttling (was T%d)\n",
+ pr->throttling.state);
+ result = acpi_processor_set_throttling(pr, 0, false);
+ if (result)
+ goto end;
+ }
+
+end:
+ if (result)
+ pr->flags.throttling = 0;
+
+ return result;
+}
+
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
new file mode 100644
index 000000000..62aee900a
--- /dev/null
+++ b/drivers/acpi/property.c
@@ -0,0 +1,1635 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI device specific properties support.
+ *
+ * Copyright (C) 2014, 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>
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/export.h>
+
+#include "internal.h"
+
+static int acpi_data_get_property_array(const struct acpi_device_data *data,
+ const char *name,
+ acpi_object_type type,
+ const union acpi_object **obj);
+
+/*
+ * The GUIDs here are made equivalent to each other in order to avoid extra
+ * complexity in the properties handling code, with the caveat that the
+ * kernel will accept certain combinations of GUID and properties that are
+ * 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.
+ */
+static const guid_t prp_guids[] = {
+ /* ACPI _DSD device properties GUID: 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 */
+ GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3,
+ 0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4),
+ /* External facing port GUID: efcc06cc-73ac-4bc3-bff0-76143807c389 */
+ GUID_INIT(0xefcc06cc, 0x73ac, 0x4bc3,
+ 0xbf, 0xf0, 0x76, 0x14, 0x38, 0x07, 0xc3, 0x89),
+ /* Thunderbolt GUID for IMR_VALID: c44d002f-69f9-4e7d-a904-a7baabdf43f7 */
+ GUID_INIT(0xc44d002f, 0x69f9, 0x4e7d,
+ 0xa9, 0x04, 0xa7, 0xba, 0xab, 0xdf, 0x43, 0xf7),
+ /* Thunderbolt GUID for WAKE_SUPPORTED: 6c501103-c189-4296-ba72-9bf5a26ebe5d */
+ GUID_INIT(0x6c501103, 0xc189, 0x4296,
+ 0xba, 0x72, 0x9b, 0xf5, 0xa2, 0x6e, 0xbe, 0x5d),
+ /* Storage device needs D3 GUID: 5025030f-842f-4ab4-a561-99a5189762d0 */
+ GUID_INIT(0x5025030f, 0x842f, 0x4ab4,
+ 0xa5, 0x61, 0x99, 0xa5, 0x18, 0x97, 0x62, 0xd0),
+};
+
+/* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */
+static const guid_t ads_guid =
+ GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6,
+ 0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b);
+
+static const guid_t buffer_prop_guid =
+ GUID_INIT(0xedb12dd0, 0x363d, 0x4085,
+ 0xa3, 0xd2, 0x49, 0x52, 0x2c, 0xa1, 0x60, 0xc4);
+
+static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
+ union acpi_object *desc,
+ struct acpi_device_data *data,
+ struct fwnode_handle *parent);
+static bool acpi_extract_properties(acpi_handle handle,
+ union acpi_object *desc,
+ struct acpi_device_data *data);
+
+static bool acpi_nondev_subnode_extract(union acpi_object *desc,
+ acpi_handle handle,
+ const union acpi_object *link,
+ struct list_head *list,
+ struct fwnode_handle *parent)
+{
+ struct acpi_data_node *dn;
+ bool result;
+
+ dn = kzalloc(sizeof(*dn), GFP_KERNEL);
+ if (!dn)
+ return false;
+
+ dn->name = link->package.elements[0].string.pointer;
+ fwnode_init(&dn->fwnode, &acpi_data_fwnode_ops);
+ dn->parent = parent;
+ 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 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)) {
+ 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))
+ return false;
+
+ if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
+ parent))
+ return true;
+
+ ACPI_FREE(buf.pointer);
+ return false;
+}
+
+static bool acpi_nondev_subnode_ok(acpi_handle scope,
+ const union acpi_object *link,
+ struct list_head *list,
+ struct fwnode_handle *parent)
+{
+ acpi_handle handle;
+ acpi_status status;
+
+ if (!scope)
+ return false;
+
+ status = acpi_get_handle(scope, link->package.elements[1].string.pointer,
+ &handle);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ return acpi_nondev_subnode_data_ok(handle, link, list, parent);
+}
+
+static bool acpi_add_nondev_subnodes(acpi_handle scope,
+ union acpi_object *links,
+ struct list_head *list,
+ struct fwnode_handle *parent)
+{
+ bool ret = false;
+ int i;
+
+ for (i = 0; i < links->package.count; i++) {
+ union acpi_object *link, *desc;
+ acpi_handle handle;
+ bool result;
+
+ link = &links->package.elements[i];
+ /* Only two elements allowed. */
+ if (link->package.count != 2)
+ continue;
+
+ /* The first one 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. */
+ switch (link->package.elements[1].type) {
+ case ACPI_TYPE_STRING:
+ 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:
+ desc = &link->package.elements[1];
+ result = acpi_nondev_subnode_extract(desc, NULL, link,
+ list, parent);
+ break;
+ default:
+ result = false;
+ break;
+ }
+ ret = ret || result;
+ }
+
+ return ret;
+}
+
+static bool acpi_enumerate_nondev_subnodes(acpi_handle scope,
+ union acpi_object *desc,
+ struct acpi_device_data *data,
+ struct fwnode_handle *parent)
+{
+ int i;
+
+ /* Look for the ACPI data subnodes GUID. */
+ for (i = 0; i < desc->package.count; i += 2) {
+ const union acpi_object *guid;
+ union acpi_object *links;
+
+ guid = &desc->package.elements[i];
+ links = &desc->package.elements[i + 1];
+
+ /*
+ * The first element must be a GUID and the second one must be
+ * a package.
+ */
+ if (guid->type != ACPI_TYPE_BUFFER ||
+ guid->buffer.length != 16 ||
+ links->type != ACPI_TYPE_PACKAGE)
+ break;
+
+ if (!guid_equal((guid_t *)guid->buffer.pointer, &ads_guid))
+ continue;
+
+ return acpi_add_nondev_subnodes(scope, links, &data->subnodes,
+ parent);
+ }
+
+ return false;
+}
+
+static bool acpi_property_value_ok(const union acpi_object *value)
+{
+ int j;
+
+ /*
+ * The value must be an integer, a string, a reference, or a package
+ * whose every element must be an integer, a string, or a reference.
+ */
+ switch (value->type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ return true;
+
+ case ACPI_TYPE_PACKAGE:
+ for (j = 0; j < value->package.count; j++)
+ switch (value->package.elements[j].type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ continue;
+
+ default:
+ return false;
+ }
+
+ return true;
+ }
+ return false;
+}
+
+static bool acpi_properties_format_valid(const union acpi_object *properties)
+{
+ int i;
+
+ for (i = 0; i < properties->package.count; i++) {
+ const union acpi_object *property;
+
+ property = &properties->package.elements[i];
+ /*
+ * Only two elements allowed, the first one must be a string and
+ * the second one has to satisfy certain conditions.
+ */
+ if (property->package.count != 2
+ || property->package.elements[0].type != ACPI_TYPE_STRING
+ || !acpi_property_value_ok(&property->package.elements[1]))
+ return false;
+ }
+ return true;
+}
+
+static void acpi_init_of_compatible(struct acpi_device *adev)
+{
+ const union acpi_object *of_compatible;
+ int ret;
+
+ ret = acpi_data_get_property_array(&adev->data, "compatible",
+ ACPI_TYPE_STRING, &of_compatible);
+ if (ret) {
+ ret = acpi_dev_get_property(adev, "compatible",
+ ACPI_TYPE_STRING, &of_compatible);
+ if (ret) {
+ struct acpi_device *parent;
+
+ parent = acpi_dev_parent(adev);
+ if (parent && parent->flags.of_compatible_ok)
+ goto out;
+
+ return;
+ }
+ }
+ adev->data.of_compatible = of_compatible;
+
+ out:
+ adev->flags.of_compatible_ok = 1;
+}
+
+static bool acpi_is_property_guid(const guid_t *guid)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(prp_guids); i++) {
+ if (guid_equal(guid, &prp_guids[i]))
+ return true;
+ }
+
+ return false;
+}
+
+struct acpi_device_properties *
+acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid,
+ union acpi_object *properties)
+{
+ struct acpi_device_properties *props;
+
+ props = kzalloc(sizeof(*props), GFP_KERNEL);
+ if (props) {
+ INIT_LIST_HEAD(&props->list);
+ props->guid = guid;
+ props->properties = properties;
+ list_add_tail(&props->list, &data->properties);
+ }
+
+ return props;
+}
+
+static void acpi_nondev_subnode_tag(acpi_handle handle, void *context)
+{
+}
+
+static void acpi_untie_nondev_subnodes(struct acpi_device_data *data)
+{
+ struct acpi_data_node *dn;
+
+ list_for_each_entry(dn, &data->subnodes, sibling) {
+ acpi_detach_data(dn->handle, acpi_nondev_subnode_tag);
+
+ acpi_untie_nondev_subnodes(&dn->data);
+ }
+}
+
+static bool acpi_tie_nondev_subnodes(struct acpi_device_data *data)
+{
+ struct acpi_data_node *dn;
+
+ list_for_each_entry(dn, &data->subnodes, sibling) {
+ acpi_status status;
+ bool ret;
+
+ 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");
+ return false;
+ }
+
+ ret = acpi_tie_nondev_subnodes(&dn->data);
+ if (!ret)
+ return ret;
+ }
+
+ return true;
+}
+
+static void acpi_data_add_buffer_props(acpi_handle handle,
+ struct acpi_device_data *data,
+ union acpi_object *properties)
+{
+ struct acpi_device_properties *props;
+ union acpi_object *package;
+ size_t alloc_size;
+ unsigned int i;
+ u32 *count;
+
+ if (check_mul_overflow((size_t)properties->package.count,
+ sizeof(*package) + sizeof(void *),
+ &alloc_size) ||
+ check_add_overflow(sizeof(*props) + sizeof(*package), alloc_size,
+ &alloc_size)) {
+ acpi_handle_warn(handle,
+ "can't allocate memory for %u buffer props",
+ properties->package.count);
+ return;
+ }
+
+ props = kvzalloc(alloc_size, GFP_KERNEL);
+ if (!props)
+ return;
+
+ props->guid = &buffer_prop_guid;
+ props->bufs = (void *)(props + 1);
+ props->properties = (void *)(props->bufs + properties->package.count);
+
+ /* Outer package */
+ package = props->properties;
+ package->type = ACPI_TYPE_PACKAGE;
+ package->package.elements = package + 1;
+ count = &package->package.count;
+ *count = 0;
+
+ /* Inner packages */
+ package++;
+
+ for (i = 0; i < properties->package.count; i++) {
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+ union acpi_object *property = &properties->package.elements[i];
+ union acpi_object *prop, *obj, *buf_obj;
+ acpi_status status;
+
+ if (property->type != ACPI_TYPE_PACKAGE ||
+ property->package.count != 2) {
+ acpi_handle_warn(handle,
+ "buffer property %u has %u entries\n",
+ i, property->package.count);
+ continue;
+ }
+
+ prop = &property->package.elements[0];
+ obj = &property->package.elements[1];
+
+ if (prop->type != ACPI_TYPE_STRING ||
+ obj->type != ACPI_TYPE_STRING) {
+ acpi_handle_warn(handle,
+ "wrong object types %u and %u\n",
+ prop->type, obj->type);
+ continue;
+ }
+
+ status = acpi_evaluate_object_typed(handle, obj->string.pointer,
+ NULL, &buf,
+ ACPI_TYPE_BUFFER);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_warn(handle,
+ "can't evaluate \"%*pE\" as buffer\n",
+ obj->string.length,
+ obj->string.pointer);
+ continue;
+ }
+
+ package->type = ACPI_TYPE_PACKAGE;
+ package->package.elements = prop;
+ package->package.count = 2;
+
+ buf_obj = buf.pointer;
+
+ /* Replace the string object with a buffer object */
+ obj->type = ACPI_TYPE_BUFFER;
+ obj->buffer.length = buf_obj->buffer.length;
+ obj->buffer.pointer = buf_obj->buffer.pointer;
+
+ props->bufs[i] = buf.pointer;
+ package++;
+ (*count)++;
+ }
+
+ if (*count)
+ list_add(&props->list, &data->properties);
+ else
+ kvfree(props);
+}
+
+static bool acpi_extract_properties(acpi_handle scope, union acpi_object *desc,
+ struct acpi_device_data *data)
+{
+ int i;
+
+ if (desc->package.count % 2)
+ return false;
+
+ /* Look for the device properties GUID. */
+ for (i = 0; i < desc->package.count; i += 2) {
+ const union acpi_object *guid;
+ union acpi_object *properties;
+
+ guid = &desc->package.elements[i];
+ properties = &desc->package.elements[i + 1];
+
+ /*
+ * The first element must be a GUID and the second one must be
+ * a package.
+ */
+ if (guid->type != ACPI_TYPE_BUFFER ||
+ guid->buffer.length != 16 ||
+ properties->type != ACPI_TYPE_PACKAGE)
+ break;
+
+ if (guid_equal((guid_t *)guid->buffer.pointer,
+ &buffer_prop_guid)) {
+ acpi_data_add_buffer_props(scope, data, properties);
+ continue;
+ }
+
+ if (!acpi_is_property_guid((guid_t *)guid->buffer.pointer))
+ continue;
+
+ /*
+ * We found the matching GUID. Now validate the format of the
+ * package immediately following it.
+ */
+ if (!acpi_properties_format_valid(properties))
+ continue;
+
+ acpi_data_add_props(data, (const guid_t *)guid->buffer.pointer,
+ properties);
+ }
+
+ return !list_empty(&data->properties);
+}
+
+void acpi_init_properties(struct acpi_device *adev)
+{
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+ struct acpi_hardware_id *hwid;
+ acpi_status status;
+ bool acpi_of = false;
+
+ INIT_LIST_HEAD(&adev->data.properties);
+ INIT_LIST_HEAD(&adev->data.subnodes);
+
+ if (!adev->handle)
+ return;
+
+ /*
+ * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in
+ * Device Tree compatible properties for this device.
+ */
+ list_for_each_entry(hwid, &adev->pnp.ids, list) {
+ if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) {
+ acpi_of = true;
+ break;
+ }
+ }
+
+ status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf,
+ ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status))
+ goto out;
+
+ if (acpi_extract_properties(adev->handle, buf.pointer, &adev->data)) {
+ adev->data.pointer = buf.pointer;
+ if (acpi_of)
+ acpi_init_of_compatible(adev);
+ }
+ if (acpi_enumerate_nondev_subnodes(adev->handle, buf.pointer,
+ &adev->data, acpi_fwnode_handle(adev)))
+ adev->data.pointer = buf.pointer;
+
+ if (!adev->data.pointer) {
+ acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n");
+ ACPI_FREE(buf.pointer);
+ } else {
+ if (!acpi_tie_nondev_subnodes(&adev->data))
+ acpi_untie_nondev_subnodes(&adev->data);
+ }
+
+ out:
+ if (acpi_of && !adev->flags.of_compatible_ok)
+ acpi_handle_info(adev->handle,
+ ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n");
+
+ if (!adev->data.pointer)
+ acpi_extract_apple_properties(adev);
+}
+
+static void acpi_free_device_properties(struct list_head *list)
+{
+ struct acpi_device_properties *props, *tmp;
+
+ list_for_each_entry_safe(props, tmp, list, list) {
+ u32 i;
+
+ list_del(&props->list);
+ /* Buffer data properties were separately allocated */
+ if (props->bufs)
+ for (i = 0; i < props->properties->package.count; i++)
+ ACPI_FREE(props->bufs[i]);
+ kvfree(props);
+ }
+}
+
+static void acpi_destroy_nondev_subnodes(struct list_head *list)
+{
+ struct acpi_data_node *dn, *next;
+
+ if (list_empty(list))
+ return;
+
+ list_for_each_entry_safe_reverse(dn, next, list, sibling) {
+ acpi_destroy_nondev_subnodes(&dn->data.subnodes);
+ wait_for_completion(&dn->kobj_done);
+ list_del(&dn->sibling);
+ ACPI_FREE((void *)dn->data.pointer);
+ acpi_free_device_properties(&dn->data.properties);
+ kfree(dn);
+ }
+}
+
+void acpi_free_properties(struct acpi_device *adev)
+{
+ acpi_untie_nondev_subnodes(&adev->data);
+ acpi_destroy_nondev_subnodes(&adev->data.subnodes);
+ ACPI_FREE((void *)adev->data.pointer);
+ adev->data.of_compatible = NULL;
+ adev->data.pointer = NULL;
+ acpi_free_device_properties(&adev->data.properties);
+}
+
+/**
+ * acpi_data_get_property - return an ACPI property with given name
+ * @data: ACPI device deta object to get the property from
+ * @name: Name of the property
+ * @type: Expected property type
+ * @obj: Location to store the property value (if not %NULL)
+ *
+ * Look up a property with @name and store a pointer to the resulting ACPI
+ * object at the location pointed to by @obj if found.
+ *
+ * Callers must not attempt to free the returned objects. These objects will be
+ * freed by the ACPI core automatically during the removal of @data.
+ *
+ * Return: %0 if property with @name has been found (success),
+ * %-EINVAL if the arguments are invalid,
+ * %-EINVAL if the property doesn't exist,
+ * %-EPROTO if the property value type doesn't match @type.
+ */
+static int acpi_data_get_property(const struct acpi_device_data *data,
+ const char *name, acpi_object_type type,
+ const union acpi_object **obj)
+{
+ const struct acpi_device_properties *props;
+
+ if (!data || !name)
+ return -EINVAL;
+
+ if (!data->pointer || list_empty(&data->properties))
+ return -EINVAL;
+
+ list_for_each_entry(props, &data->properties, list) {
+ const union acpi_object *properties;
+ unsigned int i;
+
+ properties = props->properties;
+ for (i = 0; i < properties->package.count; i++) {
+ const union acpi_object *propname, *propvalue;
+ const union acpi_object *property;
+
+ property = &properties->package.elements[i];
+
+ propname = &property->package.elements[0];
+ propvalue = &property->package.elements[1];
+
+ if (!strcmp(name, propname->string.pointer)) {
+ if (type != ACPI_TYPE_ANY &&
+ propvalue->type != type)
+ return -EPROTO;
+ if (obj)
+ *obj = propvalue;
+
+ return 0;
+ }
+ }
+ }
+ return -EINVAL;
+}
+
+/**
+ * acpi_dev_get_property - return an ACPI property with given name.
+ * @adev: ACPI device to get the property from.
+ * @name: Name of the property.
+ * @type: Expected property type.
+ * @obj: Location to store the property value (if not %NULL).
+ */
+int acpi_dev_get_property(const struct acpi_device *adev, const char *name,
+ acpi_object_type type, const union acpi_object **obj)
+{
+ return adev ? acpi_data_get_property(&adev->data, name, type, obj) : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_property);
+
+static const struct acpi_device_data *
+acpi_device_data_of_node(const struct fwnode_handle *fwnode)
+{
+ if (is_acpi_device_node(fwnode)) {
+ const struct acpi_device *adev = to_acpi_device_node(fwnode);
+ return &adev->data;
+ }
+ if (is_acpi_data_node(fwnode)) {
+ const struct acpi_data_node *dn = to_acpi_data_node(fwnode);
+ return &dn->data;
+ }
+ return NULL;
+}
+
+/**
+ * acpi_node_prop_get - return an ACPI property with given name.
+ * @fwnode: Firmware node to get the property from.
+ * @propname: Name of the property.
+ * @valptr: Location to store a pointer to the property value (if not %NULL).
+ */
+int acpi_node_prop_get(const struct fwnode_handle *fwnode,
+ const char *propname, void **valptr)
+{
+ return acpi_data_get_property(acpi_device_data_of_node(fwnode),
+ propname, ACPI_TYPE_ANY,
+ (const union acpi_object **)valptr);
+}
+
+/**
+ * acpi_data_get_property_array - return an ACPI array property with given name
+ * @data: ACPI data object to get the property from
+ * @name: Name of the property
+ * @type: Expected type of array elements
+ * @obj: Location to store a pointer to the property value (if not NULL)
+ *
+ * Look up an array property with @name and store a pointer to the resulting
+ * ACPI object at the location pointed to by @obj if found.
+ *
+ * Callers must not attempt to free the returned objects. Those objects will be
+ * freed by the ACPI core automatically during the removal of @data.
+ *
+ * Return: %0 if array property (package) with @name has been found (success),
+ * %-EINVAL if the arguments are invalid,
+ * %-EINVAL if the property doesn't exist,
+ * %-EPROTO if the property is not a package or the type of its elements
+ * doesn't match @type.
+ */
+static int acpi_data_get_property_array(const struct acpi_device_data *data,
+ const char *name,
+ acpi_object_type type,
+ const union acpi_object **obj)
+{
+ const union acpi_object *prop;
+ int ret, i;
+
+ ret = acpi_data_get_property(data, name, ACPI_TYPE_PACKAGE, &prop);
+ if (ret)
+ return ret;
+
+ if (type != ACPI_TYPE_ANY) {
+ /* Check that all elements are of correct type. */
+ for (i = 0; i < prop->package.count; i++)
+ if (prop->package.elements[i].type != type)
+ return -EPROTO;
+ }
+ if (obj)
+ *obj = prop;
+
+ return 0;
+}
+
+static struct fwnode_handle *
+acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
+ const char *childname)
+{
+ struct fwnode_handle *child;
+
+ fwnode_for_each_child_node(fwnode, child) {
+ if (is_acpi_data_node(child)) {
+ if (acpi_data_node_match(child, childname))
+ return child;
+ continue;
+ }
+
+ if (!strncmp(acpi_device_bid(to_acpi_device_node(child)),
+ childname, ACPI_NAMESEG_SIZE))
+ return child;
+ }
+
+ return NULL;
+}
+
+static int acpi_get_ref_args(struct fwnode_reference_args *args,
+ struct fwnode_handle *ref_fwnode,
+ 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;
+ }
+
+ /*
+ * 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.
+ */
+ for (i = 0; (*element) + i < end && i < num_args; i++) {
+ acpi_object_type type = (*element)[i].type;
+
+ if (type == ACPI_TYPE_LOCAL_REFERENCE)
+ break;
+
+ if (type == ACPI_TYPE_INTEGER)
+ nargs++;
+ else
+ return -EINVAL;
+ }
+
+ if (nargs > NR_FWNODE_REFERENCE_ARGS)
+ return -EINVAL;
+
+ if (args) {
+ args->fwnode = ref_fwnode;
+ args->nargs = nargs;
+ for (i = 0; i < nargs; i++)
+ args->args[i] = (*element)[i].integer.value;
+ }
+
+ (*element) += nargs;
+
+ 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
+ * (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)
+{
+ const union acpi_object *element, *end;
+ const union acpi_object *obj;
+ const struct acpi_device_data *data;
+ struct acpi_device *device;
+ int ret, idx = 0;
+
+ data = acpi_device_data_of_node(fwnode);
+ if (!data)
+ return -ENOENT;
+
+ ret = acpi_data_get_property(data, propname, ACPI_TYPE_ANY, &obj);
+ if (ret)
+ return ret == -EINVAL ? -ENOENT : -EINVAL;
+
+ switch (obj->type) {
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ /* Plain single reference without arguments. */
+ if (index)
+ return -ENOENT;
+
+ device = acpi_fetch_acpi_dev(obj->reference.handle);
+ if (!device)
+ return -EINVAL;
+
+ if (!args)
+ return 0;
+
+ args->fwnode = acpi_fwnode_handle(device);
+ 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:
+ *
+ * Package () { REF, INT, REF, INT, INT }
+ *
+ * The index argument is then used to determine which reference
+ * the caller wants (along with the arguments).
+ */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (index >= obj->package.count)
+ return -ENOENT;
+
+ element = obj->package.elements;
+ end = element + obj->package.count;
+
+ while (element < end) {
+ switch (element->type) {
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ device = acpi_fetch_acpi_dev(element->reference.handle);
+ if (!device)
+ return -EINVAL;
+
+ element++;
+
+ ret = acpi_get_ref_args(idx == index ? args : NULL,
+ acpi_fwnode_handle(device),
+ &element, end, num_args);
+ if (ret < 0)
+ return ret;
+
+ if (idx == index)
+ return 0;
+
+ break;
+ case ACPI_TYPE_INTEGER:
+ if (idx == index)
+ return -ENOENT;
+ element++;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ idx++;
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(__acpi_node_get_property_reference);
+
+static int acpi_data_prop_read_single(const struct acpi_device_data *data,
+ const char *propname,
+ enum dev_prop_type proptype, void *val)
+{
+ const union acpi_object *obj;
+ int ret;
+
+ 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) {
+ ret = acpi_data_get_property(data, propname, ACPI_TYPE_STRING, &obj);
+ if (ret)
+ return ret;
+
+ if (val)
+ *(char **)val = obj->string.pointer;
+
+ return 1;
+ } else {
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+#define acpi_copy_property_array_uint(items, val, nval) \
+ ({ \
+ typeof(items) __items = items; \
+ typeof(val) __val = val; \
+ typeof(nval) __nval = nval; \
+ size_t i; \
+ int ret = 0; \
+ \
+ for (i = 0; i < __nval; i++) { \
+ if (__items->type == ACPI_TYPE_BUFFER) { \
+ __val[i] = __items->buffer.pointer[i]; \
+ continue; \
+ } \
+ if (__items[i].type != ACPI_TYPE_INTEGER) { \
+ ret = -EPROTO; \
+ break; \
+ } \
+ if (__items[i].integer.value > _Generic(__val, \
+ u8 *: U8_MAX, \
+ u16 *: U16_MAX, \
+ u32 *: U32_MAX, \
+ u64 *: U64_MAX)) { \
+ ret = -EOVERFLOW; \
+ break; \
+ } \
+ \
+ __val[i] = __items[i].integer.value; \
+ } \
+ ret; \
+ })
+
+static int acpi_copy_property_array_string(const union acpi_object *items,
+ char **val, size_t nval)
+{
+ int i;
+
+ for (i = 0; i < nval; i++) {
+ if (items[i].type != ACPI_TYPE_STRING)
+ return -EPROTO;
+
+ val[i] = items[i].string.pointer;
+ }
+ return nval;
+}
+
+static int acpi_data_prop_read(const struct acpi_device_data *data,
+ const char *propname,
+ enum dev_prop_type proptype,
+ void *val, size_t nval)
+{
+ const union acpi_object *obj;
+ const union acpi_object *items;
+ int ret;
+
+ if (nval == 1 || !val) {
+ ret = acpi_data_prop_read_single(data, propname, proptype, val);
+ /*
+ * The overflow error means that the property is there and it is
+ * single-value, but its type does not match, so return.
+ */
+ if (ret >= 0 || ret == -EOVERFLOW)
+ return ret;
+
+ /*
+ * Reading this property as a single-value one failed, but its
+ * value may still be represented as one-element array, so
+ * continue.
+ */
+ }
+
+ ret = acpi_data_get_property_array(data, propname, ACPI_TYPE_ANY, &obj);
+ if (ret && proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64)
+ ret = acpi_data_get_property(data, propname, ACPI_TYPE_BUFFER,
+ &obj);
+ if (ret)
+ return ret;
+
+ if (!val) {
+ if (obj->type == ACPI_TYPE_BUFFER)
+ return obj->buffer.length;
+
+ return obj->package.count;
+ }
+
+ switch (proptype) {
+ case DEV_PROP_STRING:
+ break;
+ default:
+ if (obj->type == ACPI_TYPE_BUFFER) {
+ if (nval > obj->buffer.length)
+ return -EOVERFLOW;
+ } else {
+ if (nval > obj->package.count)
+ return -EOVERFLOW;
+ }
+ break;
+ }
+ if (nval == 0)
+ return -EINVAL;
+
+ 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:
+ ret = acpi_copy_property_array_uint(items, (u8 *)val, nval);
+ break;
+ case DEV_PROP_U16:
+ ret = acpi_copy_property_array_uint(items, (u16 *)val, nval);
+ break;
+ case DEV_PROP_U32:
+ ret = acpi_copy_property_array_uint(items, (u32 *)val, nval);
+ break;
+ case DEV_PROP_U64:
+ 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));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+/**
+ * acpi_node_prop_read - retrieve the value of an ACPI property with given name.
+ * @fwnode: Firmware node to get the property from.
+ * @propname: Name of the property.
+ * @proptype: Expected property type.
+ * @val: Location to store the property value (if not %NULL).
+ * @nval: Size of the array pointed to by @val.
+ *
+ * If @val is %NULL, return the number of array elements comprising the value
+ * of the property. Otherwise, read at most @nval values to the array at the
+ * location pointed to by @val.
+ */
+static int acpi_node_prop_read(const struct fwnode_handle *fwnode,
+ const char *propname, enum dev_prop_type proptype,
+ void *val, size_t nval)
+{
+ return acpi_data_prop_read(acpi_device_data_of_node(fwnode),
+ propname, proptype, val, nval);
+}
+
+static int stop_on_next(struct acpi_device *adev, void *data)
+{
+ struct acpi_device **ret_p = data;
+
+ if (!*ret_p) {
+ *ret_p = adev;
+ return 1;
+ }
+
+ /* Skip until the "previous" object is found. */
+ if (*ret_p == adev)
+ *ret_p = NULL;
+
+ 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)
+{
+ struct acpi_device *adev = to_acpi_device_node(fwnode);
+
+ if ((!child || is_acpi_device_node(child)) && adev) {
+ struct acpi_device *child_adev = to_acpi_device_node(child);
+
+ acpi_dev_for_each_child(adev, stop_on_next, &child_adev);
+ if (child_adev)
+ return acpi_fwnode_handle(child_adev);
+
+ child = NULL;
+ }
+
+ if (!child || is_acpi_data_node(child)) {
+ const struct acpi_data_node *data = to_acpi_data_node(fwnode);
+ const struct list_head *head;
+ struct list_head *next;
+ struct acpi_data_node *dn;
+
+ /*
+ * We can have a combination of device and data nodes, e.g. with
+ * hierarchical _DSD properties. Make sure the adev pointer is
+ * restored before going through data nodes, otherwise we will
+ * be looking for data_nodes below the last device found instead
+ * of the common fwnode shared by device_nodes and data_nodes.
+ */
+ adev = to_acpi_device_node(fwnode);
+ if (adev)
+ head = &adev->data.subnodes;
+ else if (data)
+ head = &data->data.subnodes;
+ else
+ return NULL;
+
+ if (list_empty(head))
+ return NULL;
+
+ if (child) {
+ dn = to_acpi_data_node(child);
+ next = dn->sibling.next;
+ if (next == head)
+ return NULL;
+
+ dn = list_entry(next, struct acpi_data_node, sibling);
+ } else {
+ dn = list_first_entry(head, struct acpi_data_node, sibling);
+ }
+ return &dn->fwnode;
+ }
+ return NULL;
+}
+
+/**
+ * acpi_node_get_parent - Return parent fwnode of this fwnode
+ * @fwnode: Firmware node whose parent to get
+ *
+ * Returns parent node of an ACPI device or data firmware node or %NULL if
+ * not available.
+ */
+static struct fwnode_handle *
+acpi_node_get_parent(const struct fwnode_handle *fwnode)
+{
+ if (is_acpi_data_node(fwnode)) {
+ /* All data nodes have parent pointer so just return that */
+ return to_acpi_data_node(fwnode)->parent;
+ }
+ if (is_acpi_device_node(fwnode)) {
+ struct acpi_device *parent;
+
+ parent = acpi_dev_parent(to_acpi_device_node(fwnode));
+ if (parent)
+ return acpi_fwnode_handle(parent);
+ }
+
+ return NULL;
+}
+
+/*
+ * Return true if the node is an ACPI graph node. Called on either ports
+ * or endpoints.
+ */
+static bool is_acpi_graph_node(struct fwnode_handle *fwnode,
+ const char *str)
+{
+ unsigned int len = strlen(str);
+ const char *name;
+
+ if (!len || !is_acpi_data_node(fwnode))
+ return false;
+
+ name = to_acpi_data_node(fwnode)->name;
+
+ return (fwnode_property_present(fwnode, "reg") &&
+ !strncmp(name, str, len) && name[len] == '@') ||
+ fwnode_property_present(fwnode, str);
+}
+
+/**
+ * acpi_graph_get_next_endpoint - Get next endpoint ACPI firmware node
+ * @fwnode: Pointer to the parent firmware node
+ * @prev: Previous endpoint node or %NULL to get the first
+ *
+ * Looks up next endpoint ACPI firmware node below a given @fwnode. Returns
+ * %NULL if there is no next endpoint or in case of error. In case of success
+ * the next endpoint is returned.
+ */
+static struct fwnode_handle *acpi_graph_get_next_endpoint(
+ const struct fwnode_handle *fwnode, struct fwnode_handle *prev)
+{
+ struct fwnode_handle *port = NULL;
+ struct fwnode_handle *endpoint;
+
+ if (!prev) {
+ do {
+ port = fwnode_get_next_child_node(fwnode, port);
+ /*
+ * The names of the port nodes begin with "port@"
+ * followed by the number of the port node and they also
+ * have a "reg" property that also has the number of the
+ * port node. For compatibility reasons a node is also
+ * recognised as a port node from the "port" property.
+ */
+ if (is_acpi_graph_node(port, "port"))
+ break;
+ } while (port);
+ } else {
+ port = fwnode_get_parent(prev);
+ }
+
+ if (!port)
+ return NULL;
+
+ endpoint = fwnode_get_next_child_node(port, prev);
+ while (!endpoint) {
+ port = fwnode_get_next_child_node(fwnode, port);
+ if (!port)
+ break;
+ if (is_acpi_graph_node(port, "port"))
+ endpoint = fwnode_get_next_child_node(port, NULL);
+ }
+
+ /*
+ * The names of the endpoint nodes begin with "endpoint@" followed by
+ * the number of the endpoint node and they also have a "reg" property
+ * that also has the number of the endpoint node. For compatibility
+ * reasons a node is also recognised as an endpoint node from the
+ * "endpoint" property.
+ */
+ if (!is_acpi_graph_node(endpoint, "endpoint"))
+ return NULL;
+
+ return endpoint;
+}
+
+/**
+ * acpi_graph_get_child_prop_value - Return a child with a given property value
+ * @fwnode: device fwnode
+ * @prop_name: The name of the property to look for
+ * @val: the desired property value
+ *
+ * Return the port node corresponding to a given port number. Returns
+ * the child node on success, NULL otherwise.
+ */
+static struct fwnode_handle *acpi_graph_get_child_prop_value(
+ const struct fwnode_handle *fwnode, const char *prop_name,
+ unsigned int val)
+{
+ struct fwnode_handle *child;
+
+ fwnode_for_each_child_node(fwnode, child) {
+ u32 nr;
+
+ if (fwnode_property_read_u32(child, prop_name, &nr))
+ continue;
+
+ if (val == nr)
+ return child;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * acpi_graph_get_remote_endpoint - Parses and returns remote end of an endpoint
+ * @__fwnode: Endpoint firmware node pointing to a remote device
+ *
+ * Returns the remote endpoint corresponding to @__fwnode. NULL on error.
+ */
+static struct fwnode_handle *
+acpi_graph_get_remote_endpoint(const struct fwnode_handle *__fwnode)
+{
+ struct fwnode_handle *fwnode;
+ unsigned int port_nr, endpoint_nr;
+ struct fwnode_reference_args args;
+ int ret;
+
+ memset(&args, 0, sizeof(args));
+ ret = acpi_node_get_property_reference(__fwnode, "remote-endpoint", 0,
+ &args);
+ if (ret)
+ return NULL;
+
+ /* Direct endpoint reference? */
+ if (!is_acpi_device_node(args.fwnode))
+ return args.nargs ? NULL : args.fwnode;
+
+ /*
+ * Always require two arguments with the reference: port and
+ * endpoint indices.
+ */
+ if (args.nargs != 2)
+ return NULL;
+
+ fwnode = args.fwnode;
+ port_nr = args.args[0];
+ endpoint_nr = args.args[1];
+
+ fwnode = acpi_graph_get_child_prop_value(fwnode, "port", port_nr);
+
+ return acpi_graph_get_child_prop_value(fwnode, "endpoint", endpoint_nr);
+}
+
+static bool acpi_fwnode_device_is_available(const struct fwnode_handle *fwnode)
+{
+ if (!is_acpi_device_node(fwnode))
+ return false;
+
+ return acpi_device_is_present(to_acpi_device_node(fwnode));
+}
+
+static const void *
+acpi_fwnode_device_get_match_data(const struct fwnode_handle *fwnode,
+ const struct device *dev)
+{
+ return acpi_device_get_match_data(dev);
+}
+
+static bool acpi_fwnode_device_dma_supported(const struct fwnode_handle *fwnode)
+{
+ return acpi_dma_supported(to_acpi_device_node(fwnode));
+}
+
+static enum dev_dma_attr
+acpi_fwnode_device_get_dma_attr(const struct fwnode_handle *fwnode)
+{
+ return acpi_get_dma_attr(to_acpi_device_node(fwnode));
+}
+
+static bool acpi_fwnode_property_present(const struct fwnode_handle *fwnode,
+ const char *propname)
+{
+ return !acpi_node_prop_get(fwnode, propname, NULL);
+}
+
+static int
+acpi_fwnode_property_read_int_array(const struct fwnode_handle *fwnode,
+ const char *propname,
+ unsigned int elem_size, void *val,
+ size_t nval)
+{
+ enum dev_prop_type type;
+
+ switch (elem_size) {
+ case sizeof(u8):
+ type = DEV_PROP_U8;
+ break;
+ case sizeof(u16):
+ type = DEV_PROP_U16;
+ break;
+ case sizeof(u32):
+ type = DEV_PROP_U32;
+ break;
+ case sizeof(u64):
+ type = DEV_PROP_U64;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return acpi_node_prop_read(fwnode, propname, type, val, nval);
+}
+
+static int
+acpi_fwnode_property_read_string_array(const struct fwnode_handle *fwnode,
+ const char *propname, const char **val,
+ size_t nval)
+{
+ return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
+ 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;
+ struct fwnode_handle *parent;
+
+ /* Is this the root node? */
+ parent = fwnode_get_parent(fwnode);
+ if (!parent)
+ return "\\";
+
+ fwnode_handle_put(parent);
+
+ if (is_acpi_data_node(fwnode)) {
+ const struct acpi_data_node *dn = to_acpi_data_node(fwnode);
+
+ return dn->name;
+ }
+
+ adev = to_acpi_device_node(fwnode);
+ if (WARN_ON(!adev))
+ return NULL;
+
+ return acpi_device_bid(adev);
+}
+
+static const char *
+acpi_fwnode_get_name_prefix(const struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *parent;
+
+ /* Is this the root node? */
+ parent = fwnode_get_parent(fwnode);
+ if (!parent)
+ return "";
+
+ /* Is this 2nd node from the root? */
+ parent = fwnode_get_next_parent(parent);
+ if (!parent)
+ return "";
+
+ fwnode_handle_put(parent);
+
+ /* ACPI device or data node. */
+ return ".";
+}
+
+static struct fwnode_handle *
+acpi_fwnode_get_parent(struct fwnode_handle *fwnode)
+{
+ return acpi_node_get_parent(fwnode);
+}
+
+static int acpi_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
+ struct fwnode_endpoint *endpoint)
+{
+ struct fwnode_handle *port_fwnode = fwnode_get_parent(fwnode);
+
+ endpoint->local_fwnode = fwnode;
+
+ if (fwnode_property_read_u32(port_fwnode, "reg", &endpoint->port))
+ fwnode_property_read_u32(port_fwnode, "port", &endpoint->port);
+ if (fwnode_property_read_u32(fwnode, "reg", &endpoint->id))
+ fwnode_property_read_u32(fwnode, "endpoint", &endpoint->id);
+
+ return 0;
+}
+
+static int acpi_fwnode_irq_get(const struct fwnode_handle *fwnode,
+ unsigned int index)
+{
+ struct resource res;
+ int ret;
+
+ ret = acpi_irq_get(ACPI_HANDLE_FWNODE(fwnode), index, &res);
+ if (ret)
+ return ret;
+
+ return res.start;
+}
+
+#define DECLARE_ACPI_FWNODE_OPS(ops) \
+ const struct fwnode_operations ops = { \
+ .device_is_available = acpi_fwnode_device_is_available, \
+ .device_get_match_data = acpi_fwnode_device_get_match_data, \
+ .device_dma_supported = \
+ acpi_fwnode_device_dma_supported, \
+ .device_get_dma_attr = acpi_fwnode_device_get_dma_attr, \
+ .property_present = 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_named_child_node = acpi_fwnode_get_named_child_node, \
+ .get_name = acpi_fwnode_get_name, \
+ .get_name_prefix = acpi_fwnode_get_name_prefix, \
+ .get_reference_args = acpi_fwnode_get_reference_args, \
+ .graph_get_next_endpoint = \
+ acpi_graph_get_next_endpoint, \
+ .graph_get_remote_endpoint = \
+ acpi_graph_get_remote_endpoint, \
+ .graph_get_port_parent = acpi_fwnode_get_parent, \
+ .graph_parse_endpoint = acpi_fwnode_graph_parse_endpoint, \
+ .irq_get = acpi_fwnode_irq_get, \
+ }; \
+ EXPORT_SYMBOL_GPL(ops)
+
+DECLARE_ACPI_FWNODE_OPS(acpi_device_fwnode_ops);
+DECLARE_ACPI_FWNODE_OPS(acpi_data_fwnode_ops);
+const struct fwnode_operations acpi_static_fwnode_ops;
+
+bool is_acpi_device_node(const struct fwnode_handle *fwnode)
+{
+ return !IS_ERR_OR_NULL(fwnode) &&
+ fwnode->ops == &acpi_device_fwnode_ops;
+}
+EXPORT_SYMBOL(is_acpi_device_node);
+
+bool is_acpi_data_node(const struct fwnode_handle *fwnode)
+{
+ return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &acpi_data_fwnode_ops;
+}
+EXPORT_SYMBOL(is_acpi_data_node);
diff --git a/drivers/acpi/reboot.c b/drivers/acpi/reboot.c
new file mode 100644
index 000000000..b79b7c99c
--- /dev/null
+++ b/drivers/acpi/reboot.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <acpi/reboot.h>
+#include <linux/delay.h>
+
+#ifdef CONFIG_PCI
+static void acpi_pci_reboot(struct acpi_generic_address *rr, u8 reset_value)
+{
+ unsigned int devfn;
+ struct pci_bus *bus0;
+
+ /* The reset register can only live on bus 0. */
+ bus0 = pci_find_bus(0, 0);
+ if (!bus0)
+ return;
+ /* Form PCI device/function pair. */
+ devfn = PCI_DEVFN((rr->address >> 32) & 0xffff,
+ (rr->address >> 16) & 0xffff);
+ pr_debug("Resetting with ACPI PCI RESET_REG.\n");
+ /* Write the value that resets us. */
+ pci_bus_write_config_byte(bus0, devfn,
+ (rr->address & 0xffff), reset_value);
+}
+#else
+static inline void acpi_pci_reboot(struct acpi_generic_address *rr,
+ u8 reset_value)
+{
+ pr_warn_once("PCI configuration space access is not supported\n");
+}
+#endif
+
+void acpi_reboot(void)
+{
+ struct acpi_generic_address *rr;
+ u8 reset_value;
+
+ if (acpi_disabled)
+ return;
+
+ rr = &acpi_gbl_FADT.reset_register;
+
+ /* ACPI reset register was only introduced with v2 of the FADT */
+
+ if (acpi_gbl_FADT.header.revision < 2)
+ return;
+
+ /* Is the reset register supported? The spec says we should be
+ * checking the bit width and bit offset, but Windows ignores
+ * these fields */
+ if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER))
+ return;
+
+ reset_value = acpi_gbl_FADT.reset_value;
+
+ /* The reset register can only exist in I/O, Memory or PCI config space
+ * on a device on bus 0. */
+ switch (rr->space_id) {
+ case ACPI_ADR_SPACE_PCI_CONFIG:
+ acpi_pci_reboot(rr, reset_value);
+ break;
+
+ case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ pr_debug("ACPI MEMORY or I/O RESET_REG.\n");
+ acpi_reset();
+ break;
+ }
+
+ /*
+ * Some platforms do not shut down immediately after writing to the
+ * ACPI reset register, and this results in racing with the
+ * subsequent reboot mechanism.
+ *
+ * The 15ms delay has been found to be long enough for the system
+ * to reboot on the affected platforms.
+ */
+ mdelay(15);
+}
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
new file mode 100644
index 000000000..5ebeb0d7b
--- /dev/null
+++ b/drivers/acpi/resource.c
@@ -0,0 +1,1010 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/acpi/resource.c - ACPI device resources interpretation.
+ *
+ * Copyright (C) 2012, Intel Corp.
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/dmi.h>
+
+#ifdef CONFIG_X86
+#define valid_IRQ(i) (((i) != 0) && ((i) != 2))
+static inline bool acpi_iospace_resource_valid(struct resource *res)
+{
+ /* On X86 IO space is limited to the [0 - 64K] IO port range */
+ return res->end < 0x10003;
+}
+#else
+#define valid_IRQ(i) (true)
+/*
+ * ACPI IO descriptors on arches other than X86 contain MMIO CPU physical
+ * addresses mapping IO space in CPU physical address space, IO space
+ * resources can be placed anywhere in the 64-bit physical address space.
+ */
+static inline bool
+acpi_iospace_resource_valid(struct resource *res) { return true; }
+#endif
+
+#if IS_ENABLED(CONFIG_ACPI_GENERIC_GSI)
+static inline bool is_gsi(struct acpi_resource_extended_irq *ext_irq)
+{
+ return ext_irq->resource_source.string_length == 0 &&
+ ext_irq->producer_consumer == ACPI_CONSUMER;
+}
+#else
+static inline bool is_gsi(struct acpi_resource_extended_irq *ext_irq)
+{
+ return true;
+}
+#endif
+
+static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io)
+{
+ u64 reslen = end - start + 1;
+
+ /*
+ * CHECKME: len might be required to check versus a minimum
+ * length as well. 1 for io is fine, but for memory it does
+ * not make any sense at all.
+ * Note: some BIOSes report incorrect length for ACPI address space
+ * descriptor, so remove check of 'reslen == len' to avoid regression.
+ */
+ if (len && reslen && start <= end)
+ return true;
+
+ pr_debug("ACPI: invalid or unassigned resource %s [%016llx - %016llx] length [%016llx]\n",
+ io ? "io" : "mem", start, end, len);
+
+ return false;
+}
+
+static void acpi_dev_memresource_flags(struct resource *res, u64 len,
+ u8 write_protect)
+{
+ res->flags = IORESOURCE_MEM;
+
+ if (!acpi_dev_resource_len_valid(res->start, res->end, len, false))
+ res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
+
+ if (write_protect == ACPI_READ_WRITE_MEMORY)
+ res->flags |= IORESOURCE_MEM_WRITEABLE;
+}
+
+static void acpi_dev_get_memresource(struct resource *res, u64 start, u64 len,
+ u8 write_protect)
+{
+ res->start = start;
+ res->end = start + len - 1;
+ acpi_dev_memresource_flags(res, len, write_protect);
+}
+
+/**
+ * acpi_dev_resource_memory - Extract ACPI memory resource information.
+ * @ares: Input ACPI resource object.
+ * @res: Output generic resource object.
+ *
+ * Check if the given ACPI resource object represents a memory resource and
+ * if that's the case, use the information in it to populate the generic
+ * resource object pointed to by @res.
+ *
+ * Return:
+ * 1) false with res->flags setting to zero: not the expected resource type
+ * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource
+ * 3) true: valid assigned resource
+ */
+bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res)
+{
+ struct acpi_resource_memory24 *memory24;
+ struct acpi_resource_memory32 *memory32;
+ struct acpi_resource_fixed_memory32 *fixed_memory32;
+
+ switch (ares->type) {
+ case ACPI_RESOURCE_TYPE_MEMORY24:
+ memory24 = &ares->data.memory24;
+ acpi_dev_get_memresource(res, memory24->minimum << 8,
+ memory24->address_length << 8,
+ memory24->write_protect);
+ break;
+ case ACPI_RESOURCE_TYPE_MEMORY32:
+ memory32 = &ares->data.memory32;
+ acpi_dev_get_memresource(res, memory32->minimum,
+ memory32->address_length,
+ memory32->write_protect);
+ break;
+ case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
+ fixed_memory32 = &ares->data.fixed_memory32;
+ acpi_dev_get_memresource(res, fixed_memory32->address,
+ fixed_memory32->address_length,
+ fixed_memory32->write_protect);
+ break;
+ default:
+ res->flags = 0;
+ return false;
+ }
+
+ return !(res->flags & IORESOURCE_DISABLED);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_resource_memory);
+
+static void acpi_dev_ioresource_flags(struct resource *res, u64 len,
+ u8 io_decode, u8 translation_type)
+{
+ res->flags = IORESOURCE_IO;
+
+ if (!acpi_dev_resource_len_valid(res->start, res->end, len, true))
+ res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
+
+ if (!acpi_iospace_resource_valid(res))
+ res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
+
+ if (io_decode == ACPI_DECODE_16)
+ res->flags |= IORESOURCE_IO_16BIT_ADDR;
+ if (translation_type == ACPI_SPARSE_TRANSLATION)
+ res->flags |= IORESOURCE_IO_SPARSE;
+}
+
+static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len,
+ u8 io_decode)
+{
+ res->start = start;
+ res->end = start + len - 1;
+ acpi_dev_ioresource_flags(res, len, io_decode, 0);
+}
+
+/**
+ * acpi_dev_resource_io - Extract ACPI I/O resource information.
+ * @ares: Input ACPI resource object.
+ * @res: Output generic resource object.
+ *
+ * Check if the given ACPI resource object represents an I/O resource and
+ * if that's the case, use the information in it to populate the generic
+ * resource object pointed to by @res.
+ *
+ * Return:
+ * 1) false with res->flags setting to zero: not the expected resource type
+ * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource
+ * 3) true: valid assigned resource
+ */
+bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res)
+{
+ struct acpi_resource_io *io;
+ struct acpi_resource_fixed_io *fixed_io;
+
+ switch (ares->type) {
+ case ACPI_RESOURCE_TYPE_IO:
+ io = &ares->data.io;
+ acpi_dev_get_ioresource(res, io->minimum,
+ io->address_length,
+ io->io_decode);
+ break;
+ case ACPI_RESOURCE_TYPE_FIXED_IO:
+ fixed_io = &ares->data.fixed_io;
+ acpi_dev_get_ioresource(res, fixed_io->address,
+ fixed_io->address_length,
+ ACPI_DECODE_10);
+ break;
+ default:
+ res->flags = 0;
+ return false;
+ }
+
+ return !(res->flags & IORESOURCE_DISABLED);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_resource_io);
+
+static bool acpi_decode_space(struct resource_win *win,
+ struct acpi_resource_address *addr,
+ struct acpi_address64_attribute *attr)
+{
+ u8 iodec = attr->granularity == 0xfff ? ACPI_DECODE_10 : ACPI_DECODE_16;
+ bool wp = addr->info.mem.write_protect;
+ u64 len = attr->address_length;
+ u64 start, end, offset = 0;
+ struct resource *res = &win->res;
+
+ /*
+ * Filter out invalid descriptor according to ACPI Spec 5.0, section
+ * 6.4.3.5 Address Space Resource Descriptors.
+ */
+ if ((addr->min_address_fixed != addr->max_address_fixed && len) ||
+ (addr->min_address_fixed && addr->max_address_fixed && !len))
+ pr_debug("ACPI: Invalid address space min_addr_fix %d, max_addr_fix %d, len %llx\n",
+ addr->min_address_fixed, addr->max_address_fixed, len);
+
+ /*
+ * For bridges that translate addresses across the bridge,
+ * translation_offset is the offset that must be added to the
+ * address on the secondary side to obtain the address on the
+ * primary side. Non-bridge devices must list 0 for all Address
+ * Translation offset bits.
+ */
+ if (addr->producer_consumer == ACPI_PRODUCER)
+ offset = attr->translation_offset;
+ else if (attr->translation_offset)
+ pr_debug("ACPI: translation_offset(%lld) is invalid for non-bridge device.\n",
+ attr->translation_offset);
+ start = attr->minimum + offset;
+ end = attr->maximum + offset;
+
+ win->offset = offset;
+ res->start = start;
+ res->end = end;
+ if (sizeof(resource_size_t) < sizeof(u64) &&
+ (offset != win->offset || start != res->start || end != res->end)) {
+ pr_warn("acpi resource window ([%#llx-%#llx] ignored, not CPU addressable)\n",
+ attr->minimum, attr->maximum);
+ return false;
+ }
+
+ switch (addr->resource_type) {
+ case ACPI_MEMORY_RANGE:
+ acpi_dev_memresource_flags(res, len, wp);
+ break;
+ case ACPI_IO_RANGE:
+ acpi_dev_ioresource_flags(res, len, iodec,
+ addr->info.io.translation_type);
+ break;
+ case ACPI_BUS_NUMBER_RANGE:
+ res->flags = IORESOURCE_BUS;
+ break;
+ default:
+ return false;
+ }
+
+ 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);
+}
+
+/**
+ * acpi_dev_resource_address_space - Extract ACPI address space information.
+ * @ares: Input ACPI resource object.
+ * @win: Output generic resource object.
+ *
+ * Check if the given ACPI resource object represents an address space resource
+ * and if that's the case, use the information in it to populate the generic
+ * resource object pointed to by @win.
+ *
+ * Return:
+ * 1) false with win->res.flags setting to zero: not the expected resource type
+ * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned
+ * resource
+ * 3) true: valid assigned resource
+ */
+bool acpi_dev_resource_address_space(struct acpi_resource *ares,
+ struct resource_win *win)
+{
+ struct acpi_resource_address64 addr;
+
+ win->res.flags = 0;
+ if (ACPI_FAILURE(acpi_resource_to_address64(ares, &addr)))
+ return false;
+
+ return acpi_decode_space(win, (struct acpi_resource_address *)&addr,
+ &addr.address);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_resource_address_space);
+
+/**
+ * acpi_dev_resource_ext_address_space - Extract ACPI address space information.
+ * @ares: Input ACPI resource object.
+ * @win: Output generic resource object.
+ *
+ * Check if the given ACPI resource object represents an extended address space
+ * resource and if that's the case, use the information in it to populate the
+ * generic resource object pointed to by @win.
+ *
+ * Return:
+ * 1) false with win->res.flags setting to zero: not the expected resource type
+ * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned
+ * resource
+ * 3) true: valid assigned resource
+ */
+bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares,
+ struct resource_win *win)
+{
+ struct acpi_resource_extended_address64 *ext_addr;
+
+ win->res.flags = 0;
+ if (ares->type != ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64)
+ return false;
+
+ ext_addr = &ares->data.ext_address64;
+
+ return acpi_decode_space(win, (struct acpi_resource_address *)ext_addr,
+ &ext_addr->address);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_resource_ext_address_space);
+
+/**
+ * acpi_dev_irq_flags - Determine IRQ resource flags.
+ * @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, u8 wake_capable)
+{
+ unsigned long flags;
+
+ if (triggering == ACPI_LEVEL_SENSITIVE)
+ flags = polarity == ACPI_ACTIVE_LOW ?
+ IORESOURCE_IRQ_LOWLEVEL : IORESOURCE_IRQ_HIGHLEVEL;
+ else
+ flags = polarity == ACPI_ACTIVE_LOW ?
+ IORESOURCE_IRQ_LOWEDGE : IORESOURCE_IRQ_HIGHEDGE;
+
+ 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);
+
+/**
+ * acpi_dev_get_irq_type - Determine irq type.
+ * @triggering: Triggering type as provided by ACPI.
+ * @polarity: Interrupt polarity as provided by ACPI.
+ */
+unsigned int acpi_dev_get_irq_type(int triggering, int polarity)
+{
+ switch (polarity) {
+ case ACPI_ACTIVE_LOW:
+ return triggering == ACPI_EDGE_SENSITIVE ?
+ IRQ_TYPE_EDGE_FALLING :
+ IRQ_TYPE_LEVEL_LOW;
+ case ACPI_ACTIVE_HIGH:
+ return triggering == ACPI_EDGE_SENSITIVE ?
+ IRQ_TYPE_EDGE_RISING :
+ IRQ_TYPE_LEVEL_HIGH;
+ case ACPI_ACTIVE_BOTH:
+ if (triggering == ACPI_EDGE_SENSITIVE)
+ return IRQ_TYPE_EDGE_BOTH;
+ fallthrough;
+ default:
+ return IRQ_TYPE_NONE;
+ }
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_irq_type);
+
+static const struct dmi_system_id medion_laptop[] = {
+ {
+ .ident = "MEDION P15651",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_BOARD_NAME, "M15T"),
+ },
+ },
+ {
+ .ident = "MEDION S17405",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_BOARD_NAME, "M17T"),
+ },
+ },
+ {
+ .ident = "MEDION S17413",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_BOARD_NAME, "M1xA"),
+ },
+ },
+ { }
+};
+
+static const struct dmi_system_id asus_laptop[] = {
+ {
+ .ident = "Asus Vivobook K3402ZA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "K3402ZA"),
+ },
+ },
+ {
+ .ident = "Asus Vivobook K3502ZA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "K3502ZA"),
+ },
+ },
+ {
+ .ident = "Asus Vivobook S5402ZA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "S5402ZA"),
+ },
+ },
+ {
+ .ident = "Asus Vivobook S5602ZA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "S5602ZA"),
+ },
+ },
+ {
+ .ident = "Asus ExpertBook B1402CBA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B1402CBA"),
+ },
+ },
+ {
+ /* Asus ExpertBook B1402CVA */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B1402CVA"),
+ },
+ },
+ {
+ .ident = "Asus ExpertBook B2402CBA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B2402CBA"),
+ },
+ },
+ {
+ .ident = "Asus ExpertBook B2502",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B2502CBA"),
+ },
+ },
+ { }
+};
+
+static const struct dmi_system_id lenovo_laptop[] = {
+ {
+ .ident = "LENOVO IdeaPad Flex 5 14ALC7",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82R9"),
+ },
+ },
+ {
+ .ident = "LENOVO IdeaPad Flex 5 16ALC7",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82RA"),
+ },
+ },
+ { }
+};
+
+static const struct dmi_system_id tongfang_gm_rg[] = {
+ {
+ .ident = "TongFang GMxRGxx/XMG CORE 15 (M22)/TUXEDO Stellaris 15 Gen4 AMD",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GMxRGxx"),
+ },
+ },
+ { }
+};
+
+static const struct dmi_system_id maingear_laptop[] = {
+ {
+ .ident = "MAINGEAR Vector Pro 2 15",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-15A3070T"),
+ }
+ },
+ {
+ /* 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"),
+ },
+ },
+ {
+ /* TongFang GM6XGxX/TUXEDO Stellaris 16 Gen5 AMD */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GM6XGxX"),
+ },
+ },
+ {
+ .ident = "MAINGEAR Vector Pro 2 17",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-17A3070T"),
+ },
+ },
+ { }
+};
+
+static const struct dmi_system_id lg_laptop[] = {
+ {
+ .ident = "LG Electronics 17U70P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
+ DMI_MATCH(DMI_BOARD_NAME, "17U70P"),
+ },
+ },
+ { }
+};
+
+struct irq_override_cmp {
+ const struct dmi_system_id *system;
+ unsigned char irq;
+ unsigned char triggering;
+ unsigned char polarity;
+ unsigned char shareable;
+ bool override;
+};
+
+static const struct irq_override_cmp override_table[] = {
+ { medion_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false },
+ { asus_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false },
+ { lenovo_laptop, 6, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true },
+ { lenovo_laptop, 10, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true },
+ { tongfang_gm_rg, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true },
+ { maingear_laptop, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true },
+ { lg_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false },
+};
+
+static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity,
+ u8 shareable)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(override_table); i++) {
+ const struct irq_override_cmp *entry = &override_table[i];
+
+ if (dmi_check_system(entry->system) &&
+ entry->irq == gsi &&
+ entry->triggering == triggering &&
+ entry->polarity == polarity &&
+ entry->shareable == shareable)
+ return entry->override;
+ }
+
+#ifdef CONFIG_X86
+ /*
+ * 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.
+ */
+ if (boot_cpu_has(X86_FEATURE_ZEN))
+ return false;
+#endif
+
+ return true;
+}
+
+static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
+ u8 triggering, u8 polarity, u8 shareable,
+ u8 wake_capable, bool check_override)
+{
+ int irq, p, t;
+
+ if (!valid_IRQ(gsi)) {
+ irqresource_disabled(res, gsi);
+ return;
+ }
+
+ /*
+ * In IO-APIC mode, use overridden attribute. Two reasons:
+ * 1. BIOS bug in DSDT
+ * 2. BIOS uses IO-APIC mode Interrupt Source Override
+ *
+ * We do this only if we are dealing with IRQ() or IRQNoFlags()
+ * resource (the legacy ISA resources). With modern ACPI 5 devices
+ * using extended IRQ descriptors we take the IRQ configuration
+ * from _CRS directly.
+ */
+ if (check_override &&
+ acpi_dev_irq_override(gsi, triggering, polarity, shareable) &&
+ !acpi_get_override_irq(gsi, &t, &p)) {
+ u8 trig = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
+ u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH;
+
+ if (triggering != trig || polarity != pol) {
+ pr_warn("ACPI: IRQ %d override to %s%s, %s%s\n", gsi,
+ t ? "level" : "edge",
+ trig == triggering ? "" : "(!)",
+ p ? "low" : "high",
+ pol == polarity ? "" : "(!)");
+ triggering = trig;
+ polarity = pol;
+ }
+ }
+
+ 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;
+ res->end = irq;
+ } else {
+ irqresource_disabled(res, gsi);
+ }
+}
+
+/**
+ * acpi_dev_resource_interrupt - Extract ACPI interrupt resource information.
+ * @ares: Input ACPI resource object.
+ * @index: Index into the array of GSIs represented by the resource.
+ * @res: Output generic resource object.
+ *
+ * Check if the given ACPI resource object represents an interrupt resource
+ * and @index does not exceed the resource's interrupt count (true is returned
+ * in that case regardless of the results of the other checks)). If that's the
+ * case, register the GSI corresponding to @index from the array of interrupts
+ * represented by the resource and populate the generic resource object pointed
+ * to by @res accordingly. If the registration of the GSI is not successful,
+ * IORESOURCE_DISABLED will be set it that object's flags.
+ *
+ * Return:
+ * 1) false with res->flags setting to zero: not the expected resource type
+ * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource
+ * 3) true: valid assigned resource
+ */
+bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
+ struct resource *res)
+{
+ struct acpi_resource_irq *irq;
+ struct acpi_resource_extended_irq *ext_irq;
+
+ switch (ares->type) {
+ case ACPI_RESOURCE_TYPE_IRQ:
+ /*
+ * Per spec, only one interrupt per descriptor is allowed in
+ * _CRS, but some firmware violates this, so parse them all.
+ */
+ irq = &ares->data.irq;
+ if (index >= irq->interrupt_count) {
+ irqresource_disabled(res, 0);
+ return false;
+ }
+ acpi_dev_get_irqresource(res, irq->interrupts[index],
+ irq->triggering, irq->polarity,
+ irq->shareable, irq->wake_capable,
+ true);
+ break;
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ ext_irq = &ares->data.extended_irq;
+ if (index >= ext_irq->interrupt_count) {
+ irqresource_disabled(res, 0);
+ return false;
+ }
+ if (is_gsi(ext_irq))
+ acpi_dev_get_irqresource(res, ext_irq->interrupts[index],
+ ext_irq->triggering, ext_irq->polarity,
+ ext_irq->shareable, ext_irq->wake_capable,
+ false);
+ else
+ irqresource_disabled(res, 0);
+ break;
+ default:
+ res->flags = 0;
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt);
+
+/**
+ * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources().
+ * @list: The head of the resource list to free.
+ */
+void acpi_dev_free_resource_list(struct list_head *list)
+{
+ resource_list_free(list);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list);
+
+struct res_proc_context {
+ struct list_head *list;
+ int (*preproc)(struct acpi_resource *, void *);
+ void *preproc_data;
+ int count;
+ int error;
+};
+
+static acpi_status acpi_dev_new_resource_entry(struct resource_win *win,
+ struct res_proc_context *c)
+{
+ struct resource_entry *rentry;
+
+ rentry = resource_list_create_entry(NULL, 0);
+ if (!rentry) {
+ c->error = -ENOMEM;
+ return AE_NO_MEMORY;
+ }
+ *rentry->res = win->res;
+ rentry->offset = win->offset;
+ resource_list_add_tail(rentry, c->list);
+ c->count++;
+ return AE_OK;
+}
+
+static acpi_status acpi_dev_process_resource(struct acpi_resource *ares,
+ void *context)
+{
+ struct res_proc_context *c = context;
+ struct resource_win win;
+ struct resource *res = &win.res;
+ int i;
+
+ if (c->preproc) {
+ int ret;
+
+ ret = c->preproc(ares, c->preproc_data);
+ if (ret < 0) {
+ c->error = ret;
+ return AE_ABORT_METHOD;
+ } else if (ret > 0) {
+ return AE_OK;
+ }
+ }
+
+ memset(&win, 0, sizeof(win));
+
+ if (acpi_dev_resource_memory(ares, res)
+ || acpi_dev_resource_io(ares, res)
+ || acpi_dev_resource_address_space(ares, &win)
+ || acpi_dev_resource_ext_address_space(ares, &win))
+ return acpi_dev_new_resource_entry(&win, c);
+
+ for (i = 0; acpi_dev_resource_interrupt(ares, i, res); i++) {
+ acpi_status status;
+
+ status = acpi_dev_new_resource_entry(&win, c);
+ if (ACPI_FAILURE(status))
+ return status;
+ }
+
+ return AE_OK;
+}
+
+static int __acpi_dev_get_resources(struct acpi_device *adev,
+ struct list_head *list,
+ int (*preproc)(struct acpi_resource *, void *),
+ void *preproc_data, char *method)
+{
+ struct res_proc_context c;
+ acpi_status status;
+
+ if (!adev || !adev->handle || !list_empty(list))
+ return -EINVAL;
+
+ if (!acpi_has_method(adev->handle, method))
+ return 0;
+
+ c.list = list;
+ c.preproc = preproc;
+ c.preproc_data = preproc_data;
+ c.count = 0;
+ c.error = 0;
+ status = acpi_walk_resources(adev->handle, method,
+ acpi_dev_process_resource, &c);
+ if (ACPI_FAILURE(status)) {
+ acpi_dev_free_resource_list(list);
+ return c.error ? c.error : -EIO;
+ }
+
+ return c.count;
+}
+
+/**
+ * acpi_dev_get_resources - Get current resources of a device.
+ * @adev: ACPI device node to get the resources for.
+ * @list: Head of the resultant list of resources (must be empty).
+ * @preproc: The caller's preprocessing routine.
+ * @preproc_data: Pointer passed to the caller's preprocessing routine.
+ *
+ * Evaluate the _CRS method for the given device node and process its output by
+ * (1) executing the @preproc() routine provided by the caller, passing the
+ * resource pointer and @preproc_data to it as arguments, for each ACPI resource
+ * returned and (2) converting all of the returned ACPI resources into struct
+ * resource objects if possible. If the return value of @preproc() in step (1)
+ * is different from 0, step (2) is not applied to the given ACPI resource and
+ * if that value is negative, the whole processing is aborted and that value is
+ * returned as the final error code.
+ *
+ * The resultant struct resource objects are put on the list pointed to by
+ * @list, that must be empty initially, as members of struct resource_entry
+ * objects. Callers of this routine should use %acpi_dev_free_resource_list() to
+ * free that list.
+ *
+ * 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_resources(struct acpi_device *adev, struct list_head *list,
+ int (*preproc)(struct acpi_resource *, void *),
+ void *preproc_data)
+{
+ return __acpi_dev_get_resources(adev, list, preproc, preproc_data,
+ METHOD_NAME__CRS);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_resources);
+
+static int is_memory(struct acpi_resource *ares, void *not_used)
+{
+ struct resource_win win;
+ struct resource *res = &win.res;
+
+ 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));
+}
+
+/**
+ * acpi_dev_get_dma_resources - Get current DMA resources of a device.
+ * @adev: ACPI device node to get the resources for.
+ * @list: Head of the resultant list of resources (must be empty).
+ *
+ * Evaluate the _DMA method for the given device node and process its
+ * output.
+ *
+ * The resultant struct resource objects are put on the list pointed to
+ * by @list, that must be empty initially, as members of struct
+ * resource_entry objects. Callers of this routine should use
+ * %acpi_dev_free_resource_list() to free that list.
+ *
+ * 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_dma_resources(struct acpi_device *adev, struct list_head *list)
+{
+ return __acpi_dev_get_resources(adev, list, is_memory, NULL,
+ METHOD_NAME__DMA);
+}
+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.
+ * @types: Valid resource types of IORESOURCE_XXX
+ *
+ * This is a helper function to support acpi_dev_get_resources(), which filters
+ * ACPI resource objects according to resource types.
+ */
+int acpi_dev_filter_resource_type(struct acpi_resource *ares,
+ unsigned long types)
+{
+ unsigned long type = 0;
+
+ switch (ares->type) {
+ case ACPI_RESOURCE_TYPE_MEMORY24:
+ case ACPI_RESOURCE_TYPE_MEMORY32:
+ case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
+ type = IORESOURCE_MEM;
+ break;
+ case ACPI_RESOURCE_TYPE_IO:
+ case ACPI_RESOURCE_TYPE_FIXED_IO:
+ type = IORESOURCE_IO;
+ break;
+ case ACPI_RESOURCE_TYPE_IRQ:
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ type = IORESOURCE_IRQ;
+ break;
+ case ACPI_RESOURCE_TYPE_DMA:
+ case ACPI_RESOURCE_TYPE_FIXED_DMA:
+ type = IORESOURCE_DMA;
+ break;
+ case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
+ type = IORESOURCE_REG;
+ break;
+ case ACPI_RESOURCE_TYPE_ADDRESS16:
+ case ACPI_RESOURCE_TYPE_ADDRESS32:
+ case ACPI_RESOURCE_TYPE_ADDRESS64:
+ case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
+ if (ares->data.address.resource_type == ACPI_MEMORY_RANGE)
+ type = IORESOURCE_MEM;
+ else if (ares->data.address.resource_type == ACPI_IO_RANGE)
+ type = IORESOURCE_IO;
+ else if (ares->data.address.resource_type ==
+ ACPI_BUS_NUMBER_RANGE)
+ type = IORESOURCE_BUS;
+ break;
+ default:
+ break;
+ }
+
+ return (type & types) ? 0 : 1;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type);
+
+static int acpi_dev_consumes_res(struct acpi_device *adev, struct resource *res)
+{
+ struct list_head resource_list;
+ struct resource_entry *rentry;
+ int ret, found = 0;
+
+ INIT_LIST_HEAD(&resource_list);
+ ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
+ if (ret < 0)
+ return 0;
+
+ list_for_each_entry(rentry, &resource_list, node) {
+ if (resource_contains(rentry->res, res)) {
+ found = 1;
+ break;
+ }
+
+ }
+
+ acpi_dev_free_resource_list(&resource_list);
+ return found;
+}
+
+static acpi_status acpi_res_consumer_cb(acpi_handle handle, u32 depth,
+ void *context, void **ret)
+{
+ struct resource *res = context;
+ struct acpi_device **consumer = (struct acpi_device **) ret;
+ struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
+
+ if (!adev)
+ return AE_OK;
+
+ if (acpi_dev_consumes_res(adev, res)) {
+ *consumer = adev;
+ return AE_CTRL_TERMINATE;
+ }
+
+ return AE_OK;
+}
+
+/**
+ * acpi_resource_consumer - Find the ACPI device that consumes @res.
+ * @res: Resource to search for.
+ *
+ * Search the current resource settings (_CRS) of every ACPI device node
+ * for @res. If we find an ACPI device whose _CRS includes @res, return
+ * it. Otherwise, return NULL.
+ */
+struct acpi_device *acpi_resource_consumer(struct resource *res)
+{
+ struct acpi_device *consumer = NULL;
+
+ acpi_get_devices(NULL, acpi_res_consumer_cb, res, (void **) &consumer);
+ return consumer;
+}
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
new file mode 100644
index 000000000..e6a01a8df
--- /dev/null
+++ b/drivers/acpi/sbs.c
@@ -0,0 +1,714 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * sbs.c - ACPI Smart Battery System Driver ($Revision: 2.0 $)
+ *
+ * Copyright (c) 2007 Alexey Starikovskiy <astarikovskiy@suse.de>
+ * Copyright (c) 2005-2007 Vladimir Lebedev <vladimir.p.lebedev@intel.com>
+ * Copyright (c) 2005 Rich Townsend <rhdt@bartol.udel.edu>
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+
+#include <linux/acpi.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/power_supply.h>
+#include <linux/platform_data/x86/apple.h>
+#include <acpi/battery.h>
+
+#include "sbshc.h"
+
+#define ACPI_SBS_CLASS "sbs"
+#define ACPI_AC_CLASS "ac_adapter"
+#define ACPI_SBS_DEVICE_NAME "Smart Battery System"
+#define ACPI_BATTERY_DIR_NAME "BAT%i"
+#define ACPI_AC_DIR_NAME "AC0"
+
+#define ACPI_SBS_NOTIFY_STATUS 0x80
+#define ACPI_SBS_NOTIFY_INFO 0x81
+
+MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
+MODULE_DESCRIPTION("Smart Battery System ACPI interface driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int cache_time = 1000;
+module_param(cache_time, uint, 0644);
+MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+
+#define MAX_SBS_BAT 4
+#define ACPI_SBS_BLOCK_MAX 32
+
+static const struct acpi_device_id sbs_device_ids[] = {
+ {"ACPI0002", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, sbs_device_ids);
+
+struct acpi_battery {
+ struct power_supply *bat;
+ struct power_supply_desc bat_desc;
+ struct acpi_sbs *sbs;
+ unsigned long update_time;
+ char name[8];
+ char manufacturer_name[ACPI_SBS_BLOCK_MAX];
+ char device_name[ACPI_SBS_BLOCK_MAX];
+ char device_chemistry[ACPI_SBS_BLOCK_MAX];
+ u16 alarm_capacity;
+ u16 full_charge_capacity;
+ u16 design_capacity;
+ u16 design_voltage;
+ u16 serial_number;
+ u16 cycle_count;
+ u16 temp_now;
+ u16 voltage_now;
+ s16 rate_now;
+ s16 rate_avg;
+ u16 capacity_now;
+ u16 state_of_charge;
+ u16 state;
+ u16 mode;
+ u16 spec;
+ u8 id;
+ u8 present:1;
+ u8 have_sysfs_alarm:1;
+};
+
+#define to_acpi_battery(x) power_supply_get_drvdata(x)
+
+struct acpi_sbs {
+ struct power_supply *charger;
+ struct acpi_device *device;
+ struct acpi_smb_hc *hc;
+ struct mutex lock;
+ struct acpi_battery battery[MAX_SBS_BAT];
+ u8 batteries_supported:4;
+ u8 manager_present:1;
+ u8 charger_present:1;
+ u8 charger_exists:1;
+};
+
+#define to_acpi_sbs(x) power_supply_get_drvdata(x)
+
+static int acpi_sbs_remove(struct acpi_device *device);
+static int acpi_battery_get_state(struct acpi_battery *battery);
+
+static inline int battery_scale(int log)
+{
+ int scale = 1;
+ while (log--)
+ scale *= 10;
+ return scale;
+}
+
+static inline int acpi_battery_vscale(struct acpi_battery *battery)
+{
+ return battery_scale((battery->spec & 0x0f00) >> 8);
+}
+
+static inline int acpi_battery_ipscale(struct acpi_battery *battery)
+{
+ return battery_scale((battery->spec & 0xf000) >> 12);
+}
+
+static inline int acpi_battery_mode(struct acpi_battery *battery)
+{
+ return (battery->mode & 0x8000);
+}
+
+static inline int acpi_battery_scale(struct acpi_battery *battery)
+{
+ return (acpi_battery_mode(battery) ? 10 : 1) *
+ acpi_battery_ipscale(battery);
+}
+
+static int sbs_get_ac_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct acpi_sbs *sbs = to_acpi_sbs(psy);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = sbs->charger_present;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int acpi_battery_technology(struct acpi_battery *battery)
+{
+ if (!strcasecmp("NiCd", battery->device_chemistry))
+ return POWER_SUPPLY_TECHNOLOGY_NiCd;
+ if (!strcasecmp("NiMH", battery->device_chemistry))
+ return POWER_SUPPLY_TECHNOLOGY_NiMH;
+ if (!strcasecmp("LION", battery->device_chemistry))
+ return POWER_SUPPLY_TECHNOLOGY_LION;
+ if (!strcasecmp("LiP", battery->device_chemistry))
+ return POWER_SUPPLY_TECHNOLOGY_LIPO;
+ return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+}
+
+static int acpi_sbs_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct acpi_battery *battery = to_acpi_battery(psy);
+
+ if ((!battery->present) && psp != POWER_SUPPLY_PROP_PRESENT)
+ return -ENODEV;
+
+ acpi_battery_get_state(battery);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (battery->rate_now < 0)
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (battery->rate_now > 0)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = battery->present;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = acpi_battery_technology(battery);
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ val->intval = battery->cycle_count;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = battery->design_voltage *
+ acpi_battery_vscale(battery) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = battery->voltage_now *
+ acpi_battery_vscale(battery) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ case POWER_SUPPLY_PROP_POWER_NOW:
+ val->intval = abs(battery->rate_now) *
+ acpi_battery_ipscale(battery) * 1000;
+ val->intval *= (acpi_battery_mode(battery)) ?
+ (battery->voltage_now *
+ acpi_battery_vscale(battery) / 1000) : 1;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ case POWER_SUPPLY_PROP_POWER_AVG:
+ val->intval = abs(battery->rate_avg) *
+ acpi_battery_ipscale(battery) * 1000;
+ val->intval *= (acpi_battery_mode(battery)) ?
+ (battery->voltage_now *
+ acpi_battery_vscale(battery) / 1000) : 1;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = battery->state_of_charge;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+ val->intval = battery->design_capacity *
+ acpi_battery_scale(battery) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ case POWER_SUPPLY_PROP_ENERGY_FULL:
+ val->intval = battery->full_charge_capacity *
+ acpi_battery_scale(battery) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ case POWER_SUPPLY_PROP_ENERGY_NOW:
+ val->intval = battery->capacity_now *
+ acpi_battery_scale(battery) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = battery->temp_now - 2730; // dK -> dC
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = battery->device_name;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = battery->manufacturer_name;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static enum power_supply_property sbs_ac_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property sbs_charge_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property sbs_energy_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_POWER_NOW,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_FULL,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static const struct power_supply_desc acpi_sbs_charger_desc = {
+ .name = "sbs-charger",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .properties = sbs_ac_props,
+ .num_properties = ARRAY_SIZE(sbs_ac_props),
+ .get_property = sbs_get_ac_property,
+};
+
+/* --------------------------------------------------------------------------
+ Smart Battery System Management
+ -------------------------------------------------------------------------- */
+
+struct acpi_battery_reader {
+ u8 command; /* command for battery */
+ u8 mode; /* word or block? */
+ size_t offset; /* offset inside struct acpi_sbs_battery */
+};
+
+static struct acpi_battery_reader info_readers[] = {
+ {0x01, SMBUS_READ_WORD, offsetof(struct acpi_battery, alarm_capacity)},
+ {0x03, SMBUS_READ_WORD, offsetof(struct acpi_battery, mode)},
+ {0x10, SMBUS_READ_WORD, offsetof(struct acpi_battery, full_charge_capacity)},
+ {0x17, SMBUS_READ_WORD, offsetof(struct acpi_battery, cycle_count)},
+ {0x18, SMBUS_READ_WORD, offsetof(struct acpi_battery, design_capacity)},
+ {0x19, SMBUS_READ_WORD, offsetof(struct acpi_battery, design_voltage)},
+ {0x1a, SMBUS_READ_WORD, offsetof(struct acpi_battery, spec)},
+ {0x1c, SMBUS_READ_WORD, offsetof(struct acpi_battery, serial_number)},
+ {0x20, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, manufacturer_name)},
+ {0x21, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, device_name)},
+ {0x22, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, device_chemistry)},
+};
+
+static struct acpi_battery_reader state_readers[] = {
+ {0x08, SMBUS_READ_WORD, offsetof(struct acpi_battery, temp_now)},
+ {0x09, SMBUS_READ_WORD, offsetof(struct acpi_battery, voltage_now)},
+ {0x0a, SMBUS_READ_WORD, offsetof(struct acpi_battery, rate_now)},
+ {0x0b, SMBUS_READ_WORD, offsetof(struct acpi_battery, rate_avg)},
+ {0x0f, SMBUS_READ_WORD, offsetof(struct acpi_battery, capacity_now)},
+ {0x0e, SMBUS_READ_WORD, offsetof(struct acpi_battery, state_of_charge)},
+ {0x16, SMBUS_READ_WORD, offsetof(struct acpi_battery, state)},
+};
+
+static int acpi_manager_get_info(struct acpi_sbs *sbs)
+{
+ int result = 0;
+ u16 battery_system_info;
+
+ result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_MANAGER,
+ 0x04, (u8 *)&battery_system_info);
+ if (!result)
+ sbs->batteries_supported = battery_system_info & 0x000f;
+ return result;
+}
+
+static int acpi_battery_get_info(struct acpi_battery *battery)
+{
+ int i, result = 0;
+
+ for (i = 0; i < ARRAY_SIZE(info_readers); ++i) {
+ result = acpi_smbus_read(battery->sbs->hc,
+ info_readers[i].mode,
+ ACPI_SBS_BATTERY,
+ info_readers[i].command,
+ (u8 *) battery +
+ info_readers[i].offset);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+static int acpi_battery_get_state(struct acpi_battery *battery)
+{
+ int i, result = 0;
+
+ if (battery->update_time &&
+ time_before(jiffies, battery->update_time +
+ msecs_to_jiffies(cache_time)))
+ return 0;
+ for (i = 0; i < ARRAY_SIZE(state_readers); ++i) {
+ result = acpi_smbus_read(battery->sbs->hc,
+ state_readers[i].mode,
+ ACPI_SBS_BATTERY,
+ state_readers[i].command,
+ (u8 *)battery +
+ state_readers[i].offset);
+ if (result)
+ goto end;
+ }
+ end:
+ battery->update_time = jiffies;
+ return result;
+}
+
+static int acpi_battery_get_alarm(struct acpi_battery *battery)
+{
+ return acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
+ ACPI_SBS_BATTERY, 0x01,
+ (u8 *)&battery->alarm_capacity);
+}
+
+static int acpi_battery_set_alarm(struct acpi_battery *battery)
+{
+ struct acpi_sbs *sbs = battery->sbs;
+ u16 value, sel = 1 << (battery->id + 12);
+
+ int ret;
+
+
+ if (sbs->manager_present) {
+ ret = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_MANAGER,
+ 0x01, (u8 *)&value);
+ if (ret)
+ goto end;
+ if ((value & 0xf000) != sel) {
+ value &= 0x0fff;
+ value |= sel;
+ ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD,
+ ACPI_SBS_MANAGER,
+ 0x01, (u8 *)&value, 2);
+ if (ret)
+ goto end;
+ }
+ }
+ ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY,
+ 0x01, (u8 *)&battery->alarm_capacity, 2);
+ end:
+ return ret;
+}
+
+static int acpi_ac_get_present(struct acpi_sbs *sbs)
+{
+ int result;
+ u16 status;
+
+ result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_CHARGER,
+ 0x13, (u8 *) & status);
+
+ if (result)
+ return result;
+
+ /*
+ * The spec requires that bit 4 always be 1. If it's not set, assume
+ * that the implementation doesn't support an SBS charger.
+ *
+ * And on some MacBooks a status of 0xffff is always returned, no
+ * matter whether the charger is plugged in or not, which is also
+ * wrong, so ignore the SBS charger for those too.
+ */
+ if (!((status >> 4) & 0x1) || status == 0xffff)
+ return -ENODEV;
+
+ sbs->charger_present = (status >> 15) & 0x1;
+ return 0;
+}
+
+static ssize_t acpi_battery_alarm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
+ acpi_battery_get_alarm(battery);
+ return sprintf(buf, "%d\n", battery->alarm_capacity *
+ acpi_battery_scale(battery) * 1000);
+}
+
+static ssize_t acpi_battery_alarm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long x;
+ struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
+ if (sscanf(buf, "%lu\n", &x) == 1)
+ battery->alarm_capacity = x /
+ (1000 * acpi_battery_scale(battery));
+ if (battery->present)
+ acpi_battery_set_alarm(battery);
+ return count;
+}
+
+static const struct device_attribute alarm_attr = {
+ .attr = {.name = "alarm", .mode = 0644},
+ .show = acpi_battery_alarm_show,
+ .store = acpi_battery_alarm_store,
+};
+
+/* --------------------------------------------------------------------------
+ Driver Interface
+ -------------------------------------------------------------------------- */
+static int acpi_battery_read(struct acpi_battery *battery)
+{
+ int result = 0, 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;
+ 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;
+
+ if (saved_present != battery->present) {
+ battery->update_time = 0;
+ result = acpi_battery_get_info(battery);
+ if (result) {
+ battery->present = 0;
+ return result;
+ }
+ }
+ result = acpi_battery_get_state(battery);
+ if (result)
+ battery->present = 0;
+ return result;
+}
+
+/* Smart 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, };
+ int result;
+
+ battery->id = id;
+ battery->sbs = sbs;
+ result = acpi_battery_read(battery);
+ if (result)
+ return result;
+
+ sprintf(battery->name, ACPI_BATTERY_DIR_NAME, id);
+ battery->bat_desc.name = battery->name;
+ battery->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+ if (!acpi_battery_mode(battery)) {
+ battery->bat_desc.properties = sbs_charge_battery_props;
+ battery->bat_desc.num_properties =
+ ARRAY_SIZE(sbs_charge_battery_props);
+ } else {
+ battery->bat_desc.properties = sbs_energy_battery_props;
+ battery->bat_desc.num_properties =
+ ARRAY_SIZE(sbs_energy_battery_props);
+ }
+ battery->bat_desc.get_property = acpi_sbs_battery_get_property;
+ battery->bat = power_supply_register(&sbs->device->dev,
+ &battery->bat_desc, &psy_cfg);
+ if (IS_ERR(battery->bat)) {
+ result = PTR_ERR(battery->bat);
+ battery->bat = NULL;
+ 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),
+ battery->name, battery->present ? "present" : "absent");
+ return result;
+}
+
+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);
+ power_supply_unregister(battery->bat);
+ }
+}
+
+static int acpi_charger_add(struct acpi_sbs *sbs)
+{
+ int result;
+ struct power_supply_config psy_cfg = { .drv_data = sbs, };
+
+ result = acpi_ac_get_present(sbs);
+ if (result)
+ goto end;
+
+ sbs->charger_exists = 1;
+ sbs->charger = power_supply_register(&sbs->device->dev,
+ &acpi_sbs_charger_desc, &psy_cfg);
+ if (IS_ERR(sbs->charger)) {
+ result = PTR_ERR(sbs->charger);
+ sbs->charger = NULL;
+ }
+ pr_info("%s [%s]: AC Adapter [%s] (%s)\n",
+ ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
+ ACPI_AC_DIR_NAME, sbs->charger_present ? "on-line" : "off-line");
+end:
+ return result;
+}
+
+static void acpi_charger_remove(struct acpi_sbs *sbs)
+{
+ if (sbs->charger)
+ power_supply_unregister(sbs->charger);
+}
+
+static void acpi_sbs_callback(void *context)
+{
+ int id;
+ struct acpi_sbs *sbs = context;
+ struct acpi_battery *bat;
+ u8 saved_charger_state = sbs->charger_present;
+ u8 saved_battery_state;
+
+ if (sbs->charger_exists) {
+ acpi_ac_get_present(sbs);
+ if (sbs->charger_present != saved_charger_state)
+ kobject_uevent(&sbs->charger->dev.kobj, KOBJ_CHANGE);
+ }
+
+ if (sbs->manager_present) {
+ for (id = 0; id < MAX_SBS_BAT; ++id) {
+ if (!(sbs->batteries_supported & (1 << id)))
+ continue;
+ bat = &sbs->battery[id];
+ saved_battery_state = bat->present;
+ acpi_battery_read(bat);
+ if (saved_battery_state == bat->present)
+ continue;
+ kobject_uevent(&bat->bat->dev.kobj, KOBJ_CHANGE);
+ }
+ }
+}
+
+static int acpi_sbs_add(struct acpi_device *device)
+{
+ struct acpi_sbs *sbs;
+ int result = 0;
+ int id;
+
+ sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
+ if (!sbs) {
+ result = -ENOMEM;
+ goto end;
+ }
+
+ mutex_init(&sbs->lock);
+
+ 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);
+ device->driver_data = sbs;
+
+ result = acpi_charger_add(sbs);
+ if (result && result != -ENODEV)
+ goto end;
+
+ result = 0;
+
+ if (!x86_apple_machine) {
+ result = acpi_manager_get_info(sbs);
+ if (!result) {
+ sbs->manager_present = 1;
+ for (id = 0; id < MAX_SBS_BAT; ++id)
+ if ((sbs->batteries_supported & (1 << id)))
+ acpi_battery_add(sbs, id);
+ }
+ }
+
+ if (!sbs->manager_present)
+ acpi_battery_add(sbs, 0);
+
+ acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs);
+end:
+ if (result)
+ acpi_sbs_remove(device);
+ return result;
+}
+
+static int acpi_sbs_remove(struct acpi_device *device)
+{
+ struct acpi_sbs *sbs;
+ int id;
+
+ if (!device)
+ return -EINVAL;
+ sbs = acpi_driver_data(device);
+ if (!sbs)
+ return -EINVAL;
+ mutex_lock(&sbs->lock);
+ acpi_smbus_unregister_callback(sbs->hc);
+ for (id = 0; id < MAX_SBS_BAT; ++id)
+ acpi_battery_remove(sbs, id);
+ acpi_charger_remove(sbs);
+ mutex_unlock(&sbs->lock);
+ mutex_destroy(&sbs->lock);
+ kfree(sbs);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_sbs_resume(struct device *dev)
+{
+ struct acpi_sbs *sbs;
+ if (!dev)
+ return -EINVAL;
+ sbs = to_acpi_device(dev)->driver_data;
+ acpi_sbs_callback(sbs);
+ return 0;
+}
+#else
+#define acpi_sbs_resume NULL
+#endif
+
+static SIMPLE_DEV_PM_OPS(acpi_sbs_pm, NULL, acpi_sbs_resume);
+
+static struct acpi_driver acpi_sbs_driver = {
+ .name = "sbs",
+ .class = ACPI_SBS_CLASS,
+ .ids = sbs_device_ids,
+ .ops = {
+ .add = acpi_sbs_add,
+ .remove = acpi_sbs_remove,
+ },
+ .drv.pm = &acpi_sbs_pm,
+};
+module_acpi_driver(acpi_sbs_driver);
diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c
new file mode 100644
index 000000000..340e0b615
--- /dev/null
+++ b/drivers/acpi/sbshc.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SMBus driver for ACPI Embedded Controller (v0.1)
+ *
+ * Copyright (c) 2007 Alexey Starikovskiy
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/acpi.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include "sbshc.h"
+
+#define ACPI_SMB_HC_CLASS "smbus_host_ctl"
+#define ACPI_SMB_HC_DEVICE_NAME "ACPI SMBus HC"
+
+struct acpi_smb_hc {
+ struct acpi_ec *ec;
+ struct mutex lock;
+ wait_queue_head_t wait;
+ u8 offset;
+ u8 query_bit;
+ smbus_alarm_callback callback;
+ void *context;
+ bool done;
+};
+
+static int acpi_smbus_hc_add(struct acpi_device *device);
+static int acpi_smbus_hc_remove(struct acpi_device *device);
+
+static const struct acpi_device_id sbs_device_ids[] = {
+ {"ACPI0001", 0},
+ {"ACPI0005", 0},
+ {"", 0},
+};
+
+MODULE_DEVICE_TABLE(acpi, sbs_device_ids);
+
+static struct acpi_driver acpi_smb_hc_driver = {
+ .name = "smbus_hc",
+ .class = ACPI_SMB_HC_CLASS,
+ .ids = sbs_device_ids,
+ .ops = {
+ .add = acpi_smbus_hc_add,
+ .remove = acpi_smbus_hc_remove,
+ },
+};
+
+union acpi_smb_status {
+ u8 raw;
+ struct {
+ u8 status:5;
+ u8 reserved:1;
+ u8 alarm:1;
+ u8 done:1;
+ } fields;
+};
+
+enum acpi_smb_status_codes {
+ SMBUS_OK = 0,
+ SMBUS_UNKNOWN_FAILURE = 0x07,
+ SMBUS_DEVICE_ADDRESS_NACK = 0x10,
+ SMBUS_DEVICE_ERROR = 0x11,
+ SMBUS_DEVICE_COMMAND_ACCESS_DENIED = 0x12,
+ SMBUS_UNKNOWN_ERROR = 0x13,
+ SMBUS_DEVICE_ACCESS_DENIED = 0x17,
+ SMBUS_TIMEOUT = 0x18,
+ SMBUS_HOST_UNSUPPORTED_PROTOCOL = 0x19,
+ SMBUS_BUSY = 0x1a,
+ SMBUS_PEC_ERROR = 0x1f,
+};
+
+enum acpi_smb_offset {
+ ACPI_SMB_PROTOCOL = 0, /* protocol, PEC */
+ ACPI_SMB_STATUS = 1, /* status */
+ ACPI_SMB_ADDRESS = 2, /* address */
+ ACPI_SMB_COMMAND = 3, /* command */
+ ACPI_SMB_DATA = 4, /* 32 data registers */
+ ACPI_SMB_BLOCK_COUNT = 0x24, /* number of data bytes */
+ ACPI_SMB_ALARM_ADDRESS = 0x25, /* alarm address */
+ ACPI_SMB_ALARM_DATA = 0x26, /* 2 bytes alarm data */
+};
+
+static inline int smb_hc_read(struct acpi_smb_hc *hc, u8 address, u8 *data)
+{
+ return ec_read(hc->offset + address, data);
+}
+
+static inline int smb_hc_write(struct acpi_smb_hc *hc, u8 address, u8 data)
+{
+ return ec_write(hc->offset + address, data);
+}
+
+static int wait_transaction_complete(struct acpi_smb_hc *hc, int timeout)
+{
+ if (wait_event_timeout(hc->wait, hc->done, msecs_to_jiffies(timeout)))
+ return 0;
+ return -ETIME;
+}
+
+static int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol,
+ u8 address, u8 command, u8 *data, u8 length)
+{
+ int ret = -EFAULT, i;
+ u8 temp, sz = 0;
+
+ if (!hc) {
+ pr_err("host controller is not configured\n");
+ return ret;
+ }
+
+ mutex_lock(&hc->lock);
+ hc->done = false;
+ if (smb_hc_read(hc, ACPI_SMB_PROTOCOL, &temp))
+ goto end;
+ if (temp) {
+ ret = -EBUSY;
+ goto end;
+ }
+ smb_hc_write(hc, ACPI_SMB_COMMAND, command);
+ if (!(protocol & 0x01)) {
+ smb_hc_write(hc, ACPI_SMB_BLOCK_COUNT, length);
+ for (i = 0; i < length; ++i)
+ smb_hc_write(hc, ACPI_SMB_DATA + i, data[i]);
+ }
+ smb_hc_write(hc, ACPI_SMB_ADDRESS, address << 1);
+ smb_hc_write(hc, ACPI_SMB_PROTOCOL, protocol);
+ /*
+ * Wait for completion. Save the status code, data size,
+ * and data into the return package (if required by the protocol).
+ */
+ ret = wait_transaction_complete(hc, 1000);
+ if (ret || !(protocol & 0x01))
+ goto end;
+ switch (protocol) {
+ case SMBUS_RECEIVE_BYTE:
+ case SMBUS_READ_BYTE:
+ sz = 1;
+ break;
+ case SMBUS_READ_WORD:
+ sz = 2;
+ break;
+ case SMBUS_READ_BLOCK:
+ if (smb_hc_read(hc, ACPI_SMB_BLOCK_COUNT, &sz)) {
+ ret = -EFAULT;
+ goto end;
+ }
+ sz &= 0x1f;
+ break;
+ }
+ for (i = 0; i < sz; ++i)
+ smb_hc_read(hc, ACPI_SMB_DATA + i, &data[i]);
+ end:
+ mutex_unlock(&hc->lock);
+ return ret;
+}
+
+int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address,
+ u8 command, u8 *data)
+{
+ return acpi_smbus_transaction(hc, protocol, address, command, data, 0);
+}
+
+EXPORT_SYMBOL_GPL(acpi_smbus_read);
+
+int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 address,
+ u8 command, u8 *data, u8 length)
+{
+ return acpi_smbus_transaction(hc, protocol, address, command, data, length);
+}
+
+EXPORT_SYMBOL_GPL(acpi_smbus_write);
+
+int acpi_smbus_register_callback(struct acpi_smb_hc *hc,
+ smbus_alarm_callback callback, void *context)
+{
+ mutex_lock(&hc->lock);
+ hc->callback = callback;
+ hc->context = context;
+ mutex_unlock(&hc->lock);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(acpi_smbus_register_callback);
+
+int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc)
+{
+ mutex_lock(&hc->lock);
+ hc->callback = NULL;
+ hc->context = NULL;
+ mutex_unlock(&hc->lock);
+ acpi_os_wait_events_complete();
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(acpi_smbus_unregister_callback);
+
+static inline void acpi_smbus_callback(void *context)
+{
+ struct acpi_smb_hc *hc = context;
+ if (hc->callback)
+ hc->callback(hc->context);
+}
+
+static int smbus_alarm(void *context)
+{
+ struct acpi_smb_hc *hc = context;
+ union acpi_smb_status status;
+ u8 address;
+ if (smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw))
+ return 0;
+ /* Check if it is only a completion notify */
+ if (status.fields.done && status.fields.status == SMBUS_OK) {
+ hc->done = true;
+ wake_up(&hc->wait);
+ }
+ if (!status.fields.alarm)
+ return 0;
+ mutex_lock(&hc->lock);
+ smb_hc_read(hc, ACPI_SMB_ALARM_ADDRESS, &address);
+ status.fields.alarm = 0;
+ smb_hc_write(hc, ACPI_SMB_STATUS, status.raw);
+ /* We are only interested in events coming from known devices */
+ switch (address >> 1) {
+ case ACPI_SBS_CHARGER:
+ case ACPI_SBS_MANAGER:
+ case ACPI_SBS_BATTERY:
+ acpi_os_execute(OSL_NOTIFY_HANDLER,
+ acpi_smbus_callback, hc);
+ }
+ mutex_unlock(&hc->lock);
+ 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;
+ unsigned long long val;
+ struct acpi_smb_hc *hc;
+
+ if (!device)
+ return -EINVAL;
+
+ status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val);
+ if (ACPI_FAILURE(status)) {
+ pr_err("error obtaining _EC.\n");
+ return -EIO;
+ }
+
+ strcpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_SMB_HC_CLASS);
+
+ hc = kzalloc(sizeof(struct acpi_smb_hc), GFP_KERNEL);
+ if (!hc)
+ return -ENOMEM;
+ mutex_init(&hc->lock);
+ init_waitqueue_head(&hc->wait);
+
+ hc->ec = acpi_driver_data(acpi_dev_parent(device));
+ hc->offset = (val >> 8) & 0xff;
+ hc->query_bit = val & 0xff;
+ device->driver_data = hc;
+
+ acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc);
+ dev_info(&device->dev, "SBS HC: offset = 0x%0x, query_bit = 0x%0x\n",
+ hc->offset, hc->query_bit);
+
+ 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)
+{
+ struct acpi_smb_hc *hc;
+
+ if (!device)
+ return -EINVAL;
+
+ 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);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexey Starikovskiy");
+MODULE_DESCRIPTION("ACPI SMBus HC driver");
diff --git a/drivers/acpi/sbshc.h b/drivers/acpi/sbshc.h
new file mode 100644
index 000000000..695c390e2
--- /dev/null
+++ b/drivers/acpi/sbshc.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+struct acpi_smb_hc;
+enum acpi_smb_protocol {
+ SMBUS_WRITE_QUICK = 2,
+ SMBUS_READ_QUICK = 3,
+ SMBUS_SEND_BYTE = 4,
+ SMBUS_RECEIVE_BYTE = 5,
+ SMBUS_WRITE_BYTE = 6,
+ SMBUS_READ_BYTE = 7,
+ SMBUS_WRITE_WORD = 8,
+ SMBUS_READ_WORD = 9,
+ SMBUS_WRITE_BLOCK = 0xa,
+ SMBUS_READ_BLOCK = 0xb,
+ SMBUS_PROCESS_CALL = 0xc,
+ SMBUS_BLOCK_PROCESS_CALL = 0xd,
+};
+
+enum acpi_sbs_device_addr {
+ ACPI_SBS_CHARGER = 0x9,
+ ACPI_SBS_MANAGER = 0xa,
+ ACPI_SBS_BATTERY = 0xb,
+};
+
+typedef void (*smbus_alarm_callback)(void *context);
+
+extern int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address,
+ u8 command, u8 *data);
+extern int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 slave_address,
+ u8 command, u8 *data, u8 length);
+extern int acpi_smbus_register_callback(struct acpi_smb_hc *hc,
+ smbus_alarm_callback callback, void *context);
+extern int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc);
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
new file mode 100644
index 000000000..94154a849
--- /dev/null
+++ b/drivers/acpi/scan.c
@@ -0,0 +1,2707 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * scan.c - support for transforming the ACPI namespace into individual objects
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/acpi_iort.h>
+#include <linux/acpi_viot.h>
+#include <linux/iommu.h>
+#include <linux/signal.h>
+#include <linux/kthread.h>
+#include <linux/dmi.h>
+#include <linux/dma-map-ops.h>
+#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;
+
+#define ACPI_BUS_CLASS "system_bus"
+#define ACPI_BUS_HID "LNXSYBUS"
+#define ACPI_BUS_DEVICE_NAME "System Bus"
+
+#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page)
+
+static const char *dummy_hid = "device";
+
+static LIST_HEAD(acpi_dep_list);
+static DEFINE_MUTEX(acpi_dep_list_lock);
+LIST_HEAD(acpi_bus_id_list);
+static DEFINE_MUTEX(acpi_scan_lock);
+static LIST_HEAD(acpi_scan_handlers_list);
+DEFINE_MUTEX(acpi_device_lock);
+LIST_HEAD(acpi_wakeup_device_list);
+static DEFINE_MUTEX(acpi_hp_context_lock);
+
+/*
+ * The UART device described by the SPCR table is the only object which needs
+ * special-casing. Everything else is covered by ACPI namespace paths in STAO
+ * table.
+ */
+static u64 spcr_uart_addr;
+
+void acpi_scan_lock_acquire(void)
+{
+ mutex_lock(&acpi_scan_lock);
+}
+EXPORT_SYMBOL_GPL(acpi_scan_lock_acquire);
+
+void acpi_scan_lock_release(void)
+{
+ mutex_unlock(&acpi_scan_lock);
+}
+EXPORT_SYMBOL_GPL(acpi_scan_lock_release);
+
+void acpi_lock_hp_context(void)
+{
+ mutex_lock(&acpi_hp_context_lock);
+}
+
+void acpi_unlock_hp_context(void)
+{
+ mutex_unlock(&acpi_hp_context_lock);
+}
+
+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_lock_hp_context();
+ hp->notify = notify;
+ hp->uevent = uevent;
+ acpi_set_hp_context(adev, hp);
+ acpi_unlock_hp_context();
+}
+EXPORT_SYMBOL_GPL(acpi_initialize_hp_context);
+
+int acpi_scan_add_handler(struct acpi_scan_handler *handler)
+{
+ if (!handler)
+ return -EINVAL;
+
+ list_add_tail(&handler->list_node, &acpi_scan_handlers_list);
+ return 0;
+}
+
+int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
+ const char *hotplug_profile_name)
+{
+ int error;
+
+ error = acpi_scan_add_handler(handler);
+ if (error)
+ return error;
+
+ acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name);
+ return 0;
+}
+
+bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
+{
+ struct acpi_device_physical_node *pn;
+ bool offline = true;
+ char *envp[] = { "EVENT=offline", NULL };
+
+ /*
+ * acpi_container_offline() calls this for all of the container's
+ * children under the container's physical_node_lock lock.
+ */
+ mutex_lock_nested(&adev->physical_node_lock, SINGLE_DEPTH_NESTING);
+
+ list_for_each_entry(pn, &adev->physical_node_list, node)
+ if (device_supports_offline(pn->dev) && !pn->dev->offline) {
+ if (uevent)
+ kobject_uevent_env(&pn->dev->kobj, KOBJ_CHANGE, envp);
+
+ offline = false;
+ break;
+ }
+
+ mutex_unlock(&adev->physical_node_lock);
+ return offline;
+}
+
+static acpi_status acpi_bus_offline(acpi_handle handle, u32 lvl, void *data,
+ void **ret_p)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+ struct acpi_device_physical_node *pn;
+ bool second_pass = (bool)data;
+ acpi_status status = AE_OK;
+
+ if (!device)
+ return AE_OK;
+
+ if (device->handler && !device->handler->hotplug.enabled) {
+ *ret_p = &device->dev;
+ return AE_SUPPORT;
+ }
+
+ mutex_lock(&device->physical_node_lock);
+
+ list_for_each_entry(pn, &device->physical_node_list, node) {
+ int ret;
+
+ if (second_pass) {
+ /* Skip devices offlined by the first pass. */
+ if (pn->put_online)
+ continue;
+ } else {
+ pn->put_online = false;
+ }
+ ret = device_offline(pn->dev);
+ if (ret >= 0) {
+ pn->put_online = !ret;
+ } else {
+ *ret_p = pn->dev;
+ if (second_pass) {
+ status = AE_ERROR;
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&device->physical_node_lock);
+
+ return status;
+}
+
+static acpi_status acpi_bus_online(acpi_handle handle, u32 lvl, void *data,
+ void **ret_p)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+ struct acpi_device_physical_node *pn;
+
+ if (!device)
+ return AE_OK;
+
+ mutex_lock(&device->physical_node_lock);
+
+ list_for_each_entry(pn, &device->physical_node_list, node)
+ if (pn->put_online) {
+ device_online(pn->dev);
+ pn->put_online = false;
+ }
+
+ mutex_unlock(&device->physical_node_lock);
+
+ return AE_OK;
+}
+
+static int acpi_scan_try_to_offline(struct acpi_device *device)
+{
+ acpi_handle handle = device->handle;
+ struct device *errdev = NULL;
+ acpi_status status;
+
+ /*
+ * Carry out two passes here and ignore errors in the first pass,
+ * because if the devices in question are memory blocks and
+ * CONFIG_MEMCG is set, one of the blocks may hold data structures
+ * that the other blocks depend on, but it is not known in advance which
+ * block holds them.
+ *
+ * If the first pass is successful, the second one isn't needed, though.
+ */
+ status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
+ NULL, acpi_bus_offline, (void *)false,
+ (void **)&errdev);
+ if (status == AE_SUPPORT) {
+ dev_warn(errdev, "Offline disabled.\n");
+ acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
+ acpi_bus_online, NULL, NULL, NULL);
+ return -EPERM;
+ }
+ acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev);
+ if (errdev) {
+ errdev = NULL;
+ acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
+ NULL, acpi_bus_offline, (void *)true,
+ (void **)&errdev);
+ if (!errdev)
+ acpi_bus_offline(handle, 0, (void *)true,
+ (void **)&errdev);
+
+ if (errdev) {
+ dev_warn(errdev, "Offline failed.\n");
+ acpi_bus_online(handle, 0, NULL, NULL);
+ acpi_walk_namespace(ACPI_TYPE_ANY, handle,
+ ACPI_UINT32_MAX, acpi_bus_online,
+ NULL, NULL, NULL);
+ return -EBUSY;
+ }
+ }
+ return 0;
+}
+
+static int acpi_scan_hot_remove(struct acpi_device *device)
+{
+ acpi_handle handle = device->handle;
+ unsigned long long sta;
+ acpi_status status;
+
+ if (device->handler && device->handler->hotplug.demand_offline) {
+ if (!acpi_scan_is_offline(device, true))
+ return -EBUSY;
+ } else {
+ int error = acpi_scan_try_to_offline(device);
+ if (error)
+ return error;
+ }
+
+ acpi_handle_debug(handle, "Ejecting\n");
+
+ acpi_bus_trim(device);
+
+ acpi_evaluate_lck(handle, 0);
+ /*
+ * TBD: _EJD support.
+ */
+ status = acpi_evaluate_ej0(handle);
+ if (status == AE_NOT_FOUND)
+ return -ENODEV;
+ else if (ACPI_FAILURE(status))
+ return -EIO;
+
+ /*
+ * Verify if eject was indeed successful. If not, log an error
+ * message. No need to call _OST since _EJ0 call was made OK.
+ */
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_warn(handle,
+ "Status check after eject failed (0x%x)\n", status);
+ } else if (sta & ACPI_STA_DEVICE_ENABLED) {
+ acpi_handle_warn(handle,
+ "Eject incomplete - status 0x%llx\n", sta);
+ }
+
+ return 0;
+}
+
+static int acpi_scan_device_not_present(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;
+}
+
+static int acpi_scan_device_check(struct acpi_device *adev)
+{
+ int error;
+
+ 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;
+}
+
+static int acpi_scan_bus_check(struct acpi_device *adev, void *not_used)
+{
+ struct acpi_scan_handler *handler = adev->handler;
+ int error;
+
+ acpi_bus_get_status(adev);
+ if (!(adev->status.present || adev->status.functional)) {
+ acpi_scan_device_not_present(adev);
+ 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);
+}
+
+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);
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ return acpi_scan_device_check(adev);
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ case ACPI_OST_EC_OSPM_EJECT:
+ if (adev->handler && !adev->handler->hotplug.enabled) {
+ dev_info(&adev->dev, "Eject disabled\n");
+ return -EPERM;
+ }
+ acpi_evaluate_ost(adev->handle, ACPI_NOTIFY_EJECT_REQUEST,
+ ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
+ return acpi_scan_hot_remove(adev);
+ }
+ return -EINVAL;
+}
+
+void acpi_device_hotplug(struct acpi_device *adev, u32 src)
+{
+ u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+ int error = -ENODEV;
+
+ lock_device_hotplug();
+ mutex_lock(&acpi_scan_lock);
+
+ /*
+ * The device object's ACPI handle cannot become invalid as long as we
+ * are holding acpi_scan_lock, but it might have become invalid before
+ * that lock was acquired.
+ */
+ if (adev->handle == INVALID_ACPI_HANDLE)
+ goto err_out;
+
+ if (adev->flags.is_dock_station) {
+ error = dock_notify(adev, src);
+ } else if (adev->flags.hotplug_notify) {
+ error = acpi_generic_hotplug_event(adev, src);
+ } else {
+ int (*notify)(struct acpi_device *, u32);
+
+ acpi_lock_hp_context();
+ notify = adev->hp ? adev->hp->notify : NULL;
+ acpi_unlock_hp_context();
+ /*
+ * There may be additional notify handlers for device objects
+ * without the .event() callback, so ignore them here.
+ */
+ if (notify)
+ error = notify(adev, src);
+ else
+ goto out;
+ }
+ switch (error) {
+ case 0:
+ ost_code = ACPI_OST_SC_SUCCESS;
+ break;
+ case -EPERM:
+ ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
+ break;
+ case -EBUSY:
+ ost_code = ACPI_OST_SC_DEVICE_BUSY;
+ break;
+ default:
+ ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+ break;
+ }
+
+ err_out:
+ acpi_evaluate_ost(adev->handle, src, ost_code, NULL);
+
+ out:
+ acpi_put_acpi_dev(adev);
+ mutex_unlock(&acpi_scan_lock);
+ unlock_device_hotplug();
+}
+
+static void acpi_free_power_resources_lists(struct acpi_device *device)
+{
+ int i;
+
+ if (device->wakeup.flags.valid)
+ acpi_power_resources_list_free(&device->wakeup.resources);
+
+ if (!device->power.flags.power_resources)
+ return;
+
+ for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
+ struct acpi_device_power_state *ps = &device->power.states[i];
+ acpi_power_resources_list_free(&ps->resources);
+ }
+}
+
+static void acpi_device_release(struct device *dev)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+
+ acpi_free_properties(acpi_dev);
+ acpi_free_pnp_ids(&acpi_dev->pnp);
+ acpi_free_power_resources_lists(acpi_dev);
+ kfree(acpi_dev);
+}
+
+static void acpi_device_del(struct acpi_device *device)
+{
+ struct acpi_device_bus_id *acpi_device_bus_id;
+
+ mutex_lock(&acpi_device_lock);
+
+ list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node)
+ if (!strcmp(acpi_device_bus_id->bus_id,
+ acpi_device_hid(device))) {
+ ida_free(&acpi_device_bus_id->instance_ida,
+ device->pnp.instance_no);
+ if (ida_is_empty(&acpi_device_bus_id->instance_ida)) {
+ list_del(&acpi_device_bus_id->node);
+ kfree_const(acpi_device_bus_id->bus_id);
+ kfree(acpi_device_bus_id);
+ }
+ break;
+ }
+
+ list_del(&device->wakeup_list);
+
+ mutex_unlock(&acpi_device_lock);
+
+ acpi_power_add_remove_device(device, false);
+ acpi_device_remove_files(device);
+ if (device->remove)
+ device->remove(device);
+
+ device_del(&device->dev);
+}
+
+static BLOCKING_NOTIFIER_HEAD(acpi_reconfig_chain);
+
+static LIST_HEAD(acpi_device_del_list);
+static DEFINE_MUTEX(acpi_device_del_lock);
+
+static void acpi_device_del_work_fn(struct work_struct *work_not_used)
+{
+ for (;;) {
+ struct acpi_device *adev;
+
+ mutex_lock(&acpi_device_del_lock);
+
+ if (list_empty(&acpi_device_del_list)) {
+ mutex_unlock(&acpi_device_del_lock);
+ break;
+ }
+ adev = list_first_entry(&acpi_device_del_list,
+ struct acpi_device, del_list);
+ list_del(&adev->del_list);
+
+ mutex_unlock(&acpi_device_del_lock);
+
+ blocking_notifier_call_chain(&acpi_reconfig_chain,
+ ACPI_RECONFIG_DEVICE_REMOVE, adev);
+
+ acpi_device_del(adev);
+ /*
+ * Drop references to all power resources that might have been
+ * used by the device.
+ */
+ acpi_power_transition(adev, ACPI_STATE_D3_COLD);
+ acpi_dev_put(adev);
+ }
+}
+
+/**
+ * acpi_scan_drop_device - Drop an ACPI device object.
+ * @handle: Handle of an ACPI namespace node, not used.
+ * @context: Address of the ACPI device object to drop.
+ *
+ * This is invoked by acpi_ns_delete_node() during the removal of the ACPI
+ * namespace node the device object pointed to by @context is attached to.
+ *
+ * The unregistration is carried out asynchronously to avoid running
+ * acpi_device_del() under the ACPICA's namespace mutex and the list is used to
+ * ensure the correct ordering (the device objects must be unregistered in the
+ * same order in which the corresponding namespace nodes are deleted).
+ */
+static void acpi_scan_drop_device(acpi_handle handle, void *context)
+{
+ static DECLARE_WORK(work, acpi_device_del_work_fn);
+ struct acpi_device *adev = context;
+
+ mutex_lock(&acpi_device_del_lock);
+
+ /*
+ * Use the ACPI hotplug workqueue which is ordered, so this work item
+ * won't run after any hotplug work items submitted subsequently. That
+ * prevents attempts to register device objects identical to those being
+ * deleted from happening concurrently (such attempts result from
+ * hotplug events handled via the ACPI hotplug workqueue). It also will
+ * run after all of the work items submitted previously, which helps
+ * those work items to ensure that they are not accessing stale device
+ * objects.
+ */
+ if (list_empty(&acpi_device_del_list))
+ acpi_queue_hotplug_work(&work);
+
+ list_add_tail(&adev->del_list, &acpi_device_del_list);
+ /* Make acpi_ns_validate_handle() return NULL for this handle. */
+ adev->handle = INVALID_ACPI_HANDLE;
+
+ mutex_unlock(&acpi_device_del_lock);
+}
+
+static struct acpi_device *handle_to_device(acpi_handle handle,
+ void (*callback)(void *))
+{
+ struct acpi_device *adev = NULL;
+ acpi_status status;
+
+ status = acpi_get_data_full(handle, acpi_scan_drop_device,
+ (void **)&adev, callback);
+ if (ACPI_FAILURE(status) || !adev) {
+ acpi_handle_debug(handle, "No context!\n");
+ return NULL;
+ }
+ return adev;
+}
+
+/**
+ * acpi_fetch_acpi_dev - Retrieve ACPI device object.
+ * @handle: ACPI handle associated with the requested ACPI device object.
+ *
+ * Return a pointer to the ACPI device object associated with @handle, if
+ * present, or NULL otherwise.
+ */
+struct acpi_device *acpi_fetch_acpi_dev(acpi_handle handle)
+{
+ return handle_to_device(handle, NULL);
+}
+EXPORT_SYMBOL_GPL(acpi_fetch_acpi_dev);
+
+static void get_acpi_device(void *dev)
+{
+ acpi_dev_get(dev);
+}
+
+/**
+ * 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_get_acpi_dev);
+
+static struct acpi_device_bus_id *acpi_device_bus_id_match(const char *dev_id)
+{
+ struct acpi_device_bus_id *acpi_device_bus_id;
+
+ /* Find suitable bus_id and instance number in acpi_bus_id_list. */
+ list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) {
+ if (!strcmp(acpi_device_bus_id->bus_id, dev_id))
+ return acpi_device_bus_id;
+ }
+ return NULL;
+}
+
+static int acpi_device_set_name(struct acpi_device *device,
+ struct acpi_device_bus_id *acpi_device_bus_id)
+{
+ struct ida *instance_ida = &acpi_device_bus_id->instance_ida;
+ int result;
+
+ result = ida_alloc(instance_ida, GFP_KERNEL);
+ if (result < 0)
+ return result;
+
+ device->pnp.instance_no = result;
+ dev_set_name(&device->dev, "%s:%02x", acpi_device_bus_id->bus_id, result);
+ return 0;
+}
+
+int acpi_tie_acpi_dev(struct acpi_device *adev)
+{
+ acpi_handle handle = adev->handle;
+ acpi_status status;
+
+ if (!handle)
+ return 0;
+
+ status = acpi_attach_data(handle, acpi_scan_drop_device, adev);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_err(handle, "Unable to attach device data\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+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))
+ return;
+
+ adev->pld_crc = crc32(~0, pld, sizeof(*pld));
+ ACPI_FREE(pld);
+}
+
+int acpi_device_add(struct acpi_device *device)
+{
+ struct acpi_device_bus_id *acpi_device_bus_id;
+ int result;
+
+ /*
+ * Linkage
+ * -------
+ * Link this device to its parent and siblings.
+ */
+ INIT_LIST_HEAD(&device->wakeup_list);
+ INIT_LIST_HEAD(&device->physical_node_list);
+ INIT_LIST_HEAD(&device->del_list);
+ mutex_init(&device->physical_node_lock);
+
+ mutex_lock(&acpi_device_lock);
+
+ acpi_device_bus_id = acpi_device_bus_id_match(acpi_device_hid(device));
+ if (acpi_device_bus_id) {
+ result = acpi_device_set_name(device, acpi_device_bus_id);
+ if (result)
+ goto err_unlock;
+ } else {
+ acpi_device_bus_id = kzalloc(sizeof(*acpi_device_bus_id),
+ GFP_KERNEL);
+ if (!acpi_device_bus_id) {
+ result = -ENOMEM;
+ goto err_unlock;
+ }
+ acpi_device_bus_id->bus_id =
+ kstrdup_const(acpi_device_hid(device), GFP_KERNEL);
+ if (!acpi_device_bus_id->bus_id) {
+ kfree(acpi_device_bus_id);
+ result = -ENOMEM;
+ goto err_unlock;
+ }
+
+ ida_init(&acpi_device_bus_id->instance_ida);
+
+ result = acpi_device_set_name(device, acpi_device_bus_id);
+ if (result) {
+ kfree_const(acpi_device_bus_id->bus_id);
+ kfree(acpi_device_bus_id);
+ goto err_unlock;
+ }
+
+ list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list);
+ }
+
+ if (device->wakeup.flags.valid)
+ list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list);
+
+ acpi_store_pld_crc(device);
+
+ mutex_unlock(&acpi_device_lock);
+
+ 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));
+
+ return 0;
+
+err:
+ mutex_lock(&acpi_device_lock);
+
+ list_del(&device->wakeup_list);
+
+err_unlock:
+ mutex_unlock(&acpi_device_lock);
+
+ acpi_detach_data(device->handle, acpi_scan_drop_device);
+
+ return result;
+}
+
+/* --------------------------------------------------------------------------
+ Device Enumeration
+ -------------------------------------------------------------------------- */
+static bool acpi_info_matches_ids(struct acpi_device_info *info,
+ const char * const ids[])
+{
+ struct acpi_pnp_device_id_list *cid_list = NULL;
+ int i, index;
+
+ if (!(info->valid & ACPI_VALID_HID))
+ return false;
+
+ index = match_string(ids, -1, info->hardware_id.string);
+ if (index >= 0)
+ return true;
+
+ if (info->valid & ACPI_VALID_CID)
+ cid_list = &info->compatible_id_list;
+
+ if (!cid_list)
+ return false;
+
+ for (i = 0; i < cid_list->count; i++) {
+ index = match_string(ids, -1, cid_list->ids[i].string);
+ if (index >= 0)
+ return true;
+ }
+
+ return false;
+}
+
+/* List of HIDs for which we ignore matching ACPI devices, when checking _DEP lists. */
+static const char * const acpi_ignore_dep_ids[] = {
+ "PNP0D80", /* Windows-compatible System Power Management Controller */
+ "INT33BD", /* Intel Baytrail Mailbox Device */
+ "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 */
+ NULL
+};
+
+static struct acpi_device *acpi_find_parent_acpi_dev(acpi_handle handle)
+{
+ struct acpi_device *adev;
+
+ /*
+ * Fixed hardware devices do not appear in the namespace and do not
+ * have handles, but we fabricate acpi_devices for them, so we have
+ * to deal with them specially.
+ */
+ if (!handle)
+ return acpi_root;
+
+ do {
+ acpi_status status;
+
+ status = acpi_get_parent(handle, &handle);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NULL_ENTRY)
+ return acpi_root;
+
+ return NULL;
+ }
+ adev = acpi_fetch_acpi_dev(handle);
+ } while (!adev);
+ return adev;
+}
+
+acpi_status
+acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd)
+{
+ acpi_status status;
+ acpi_handle tmp;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *obj;
+
+ status = acpi_get_handle(handle, "_EJD", &tmp);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer);
+ if (ACPI_SUCCESS(status)) {
+ obj = buffer.pointer;
+ status = acpi_get_handle(ACPI_ROOT_OBJECT, obj->string.pointer,
+ ejd);
+ kfree(buffer.pointer);
+ }
+ return status;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_get_ejd);
+
+static int acpi_bus_extract_wakeup_device_power_package(struct acpi_device *dev)
+{
+ acpi_handle handle = dev->handle;
+ struct acpi_device_wakeup *wakeup = &dev->wakeup;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *package = NULL;
+ union acpi_object *element = NULL;
+ acpi_status status;
+ int err = -ENODATA;
+
+ INIT_LIST_HEAD(&wakeup->resources);
+
+ /* _PRW */
+ status = acpi_evaluate_object(handle, "_PRW", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_info(handle, "_PRW evaluation failed: %s\n",
+ acpi_format_exception(status));
+ return err;
+ }
+
+ package = (union acpi_object *)buffer.pointer;
+
+ if (!package || package->package.count < 2)
+ goto out;
+
+ element = &(package->package.elements[0]);
+ if (!element)
+ goto out;
+
+ if (element->type == ACPI_TYPE_PACKAGE) {
+ if ((element->package.count < 2) ||
+ (element->package.elements[0].type !=
+ ACPI_TYPE_LOCAL_REFERENCE)
+ || (element->package.elements[1].type != ACPI_TYPE_INTEGER))
+ goto out;
+
+ wakeup->gpe_device =
+ element->package.elements[0].reference.handle;
+ wakeup->gpe_number =
+ (u32) element->package.elements[1].integer.value;
+ } else if (element->type == ACPI_TYPE_INTEGER) {
+ wakeup->gpe_device = NULL;
+ wakeup->gpe_number = element->integer.value;
+ } else {
+ goto out;
+ }
+
+ element = &(package->package.elements[1]);
+ if (element->type != ACPI_TYPE_INTEGER)
+ goto out;
+
+ wakeup->sleep_state = element->integer.value;
+
+ err = acpi_extract_power_resources(package, 2, &wakeup->resources);
+ if (err)
+ goto out;
+
+ if (!list_empty(&wakeup->resources)) {
+ int sleep_state;
+
+ err = acpi_power_wakeup_list_init(&wakeup->resources,
+ &sleep_state);
+ if (err) {
+ acpi_handle_warn(handle, "Retrieving current states "
+ "of wakeup power resources failed\n");
+ acpi_power_resources_list_free(&wakeup->resources);
+ goto out;
+ }
+ if (sleep_state < wakeup->sleep_state) {
+ acpi_handle_warn(handle, "Overriding _PRW sleep state "
+ "(S%d) by S%d from power resources\n",
+ (int)wakeup->sleep_state, sleep_state);
+ wakeup->sleep_state = sleep_state;
+ }
+ }
+
+ out:
+ kfree(buffer.pointer);
+ return err;
+}
+
+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 */
+ {"", 0},
+ };
+ struct acpi_device_wakeup *wakeup = &device->wakeup;
+ 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;
+ }
+ acpi_mark_gpe_for_wake(wakeup->gpe_device, wakeup->gpe_number);
+ device_set_wakeup_capable(&device->dev, true);
+ return true;
+ }
+
+ status = acpi_setup_gpe_for_wake(device->handle, wakeup->gpe_device,
+ wakeup->gpe_number);
+ return ACPI_SUCCESS(status);
+}
+
+static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
+{
+ int err;
+
+ /* Presence of _PRW indicates wake capable */
+ if (!acpi_has_method(device->handle, "_PRW"))
+ return;
+
+ err = acpi_bus_extract_wakeup_device_power_package(device);
+ if (err) {
+ dev_err(&device->dev, "Unable to extract wakeup power resources");
+ return;
+ }
+
+ device->wakeup.flags.valid = acpi_wakeup_gpe_init(device);
+ device->wakeup.prepare_count = 0;
+ /*
+ * Call _PSW/_DSW object to disable its ability to wake the sleeping
+ * system for the ACPI device with the _PRW object.
+ * The _PSW object is deprecated in ACPI 3.0 and is replaced by _DSW.
+ * So it is necessary to call _DSW object first. Only when it is not
+ * present will the _PSW object used.
+ */
+ err = acpi_device_sleep_wake(device, 0, 0, 0);
+ if (err)
+ pr_debug("error in _DSW or _PSW evaluation\n");
+}
+
+static void acpi_bus_init_power_state(struct acpi_device *device, int state)
+{
+ struct acpi_device_power_state *ps = &device->power.states[state];
+ char pathname[5] = { '_', 'P', 'R', '0' + state, '\0' };
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+
+ INIT_LIST_HEAD(&ps->resources);
+
+ /* Evaluate "_PRx" to get referenced power resources */
+ status = acpi_evaluate_object(device->handle, pathname, NULL, &buffer);
+ if (ACPI_SUCCESS(status)) {
+ union acpi_object *package = buffer.pointer;
+
+ if (buffer.length && package
+ && package->type == ACPI_TYPE_PACKAGE
+ && package->package.count)
+ acpi_extract_power_resources(package, 0, &ps->resources);
+
+ ACPI_FREE(buffer.pointer);
+ }
+
+ /* Evaluate "_PSx" to see if we can do explicit sets */
+ pathname[2] = 'S';
+ if (acpi_has_method(device->handle, pathname))
+ ps->flags.explicit_set = 1;
+
+ /* State is valid if there are means to put the device into it. */
+ if (!list_empty(&ps->resources) || ps->flags.explicit_set)
+ ps->flags.valid = 1;
+
+ ps->power = -1; /* Unknown - driver assigned */
+ ps->latency = -1; /* Unknown - driver assigned */
+}
+
+static void acpi_bus_get_power_flags(struct acpi_device *device)
+{
+ unsigned long long dsc = ACPI_STATE_D0;
+ u32 i;
+
+ /* Presence of _PS0|_PR0 indicates 'power manageable' */
+ if (!acpi_has_method(device->handle, "_PS0") &&
+ !acpi_has_method(device->handle, "_PR0"))
+ return;
+
+ device->flags.power_manageable = 1;
+
+ /*
+ * Power Management Flags
+ */
+ if (acpi_has_method(device->handle, "_PSC"))
+ device->power.flags.explicit_get = 1;
+
+ if (acpi_has_method(device->handle, "_IRC"))
+ device->power.flags.inrush_current = 1;
+
+ if (acpi_has_method(device->handle, "_DSW"))
+ device->power.flags.dsw_present = 1;
+
+ acpi_evaluate_integer(device->handle, "_DSC", NULL, &dsc);
+ device->power.state_for_enumeration = dsc;
+
+ /*
+ * Enumerate supported power management states
+ */
+ for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++)
+ acpi_bus_init_power_state(device, i);
+
+ INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources);
+
+ /* Set the defaults for D0 and D3hot (always supported). */
+ device->power.states[ACPI_STATE_D0].flags.valid = 1;
+ device->power.states[ACPI_STATE_D0].power = 100;
+ device->power.states[ACPI_STATE_D3_HOT].flags.valid = 1;
+
+ /*
+ * Use power resources only if the D0 list of them is populated, because
+ * some platforms may provide _PR3 only to indicate D3cold support and
+ * in those cases the power resources list returned by it may be bogus.
+ */
+ if (!list_empty(&device->power.states[ACPI_STATE_D0].resources)) {
+ device->power.flags.power_resources = 1;
+ /*
+ * D3cold is supported if the D3hot list of power resources is
+ * not empty.
+ */
+ if (!list_empty(&device->power.states[ACPI_STATE_D3_HOT].resources))
+ device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
+ }
+
+ if (acpi_bus_init_power(device))
+ device->flags.power_manageable = 0;
+}
+
+static void acpi_bus_get_flags(struct acpi_device *device)
+{
+ /* Presence of _STA indicates 'dynamic_status' */
+ if (acpi_has_method(device->handle, "_STA"))
+ device->flags.dynamic_status = 1;
+
+ /* Presence of _RMV indicates 'removable' */
+ if (acpi_has_method(device->handle, "_RMV"))
+ device->flags.removable = 1;
+
+ /* Presence of _EJD|_EJ0 indicates 'ejectable' */
+ if (acpi_has_method(device->handle, "_EJD") ||
+ acpi_has_method(device->handle, "_EJ0"))
+ device->flags.ejectable = 1;
+}
+
+static void acpi_device_get_busid(struct acpi_device *device)
+{
+ char bus_id[5] = { '?', 0 };
+ struct acpi_buffer buffer = { sizeof(bus_id), bus_id };
+ int i = 0;
+
+ /*
+ * Bus ID
+ * ------
+ * The device's Bus ID is simply the object name.
+ * TBD: Shouldn't this value be unique (within the ACPI namespace)?
+ */
+ if (!acpi_dev_parent(device)) {
+ strcpy(device->pnp.bus_id, "ACPI");
+ return;
+ }
+
+ switch (device->device_type) {
+ case ACPI_BUS_TYPE_POWER_BUTTON:
+ strcpy(device->pnp.bus_id, "PWRF");
+ break;
+ case ACPI_BUS_TYPE_SLEEP_BUTTON:
+ strcpy(device->pnp.bus_id, "SLPF");
+ break;
+ case ACPI_BUS_TYPE_ECDT_EC:
+ strcpy(device->pnp.bus_id, "ECDT");
+ break;
+ default:
+ acpi_get_name(device->handle, ACPI_SINGLE_NAME, &buffer);
+ /* Clean up trailing underscores (if any) */
+ for (i = 3; i > 1; i--) {
+ if (bus_id[i] == '_')
+ bus_id[i] = '\0';
+ else
+ break;
+ }
+ strcpy(device->pnp.bus_id, bus_id);
+ break;
+ }
+}
+
+/*
+ * acpi_ata_match - see if an acpi object is an ATA device
+ *
+ * If an acpi object has one of the ACPI ATA methods defined,
+ * then we can safely call it an ATA device.
+ */
+bool acpi_ata_match(acpi_handle handle)
+{
+ return acpi_has_method(handle, "_GTF") ||
+ acpi_has_method(handle, "_GTM") ||
+ acpi_has_method(handle, "_STM") ||
+ acpi_has_method(handle, "_SDD");
+}
+
+/*
+ * acpi_bay_match - see if an acpi object is an ejectable driver bay
+ *
+ * If an acpi object is ejectable and has one of the ACPI ATA methods defined,
+ * then we can safely call it an ejectable drive bay
+ */
+bool acpi_bay_match(acpi_handle handle)
+{
+ acpi_handle phandle;
+
+ if (!acpi_has_method(handle, "_EJ0"))
+ return false;
+ if (acpi_ata_match(handle))
+ return true;
+ if (ACPI_FAILURE(acpi_get_parent(handle, &phandle)))
+ return false;
+
+ return acpi_ata_match(phandle);
+}
+
+bool acpi_device_is_battery(struct acpi_device *adev)
+{
+ struct acpi_hardware_id *hwid;
+
+ list_for_each_entry(hwid, &adev->pnp.ids, list)
+ if (!strcmp("PNP0C0A", hwid->id))
+ return true;
+
+ return false;
+}
+
+static bool is_ejectable_bay(struct acpi_device *adev)
+{
+ acpi_handle handle = adev->handle;
+
+ if (acpi_has_method(handle, "_EJ0") && acpi_device_is_battery(adev))
+ return true;
+
+ return acpi_bay_match(handle);
+}
+
+/*
+ * acpi_dock_match - see if an acpi object has a _DCK method
+ */
+bool acpi_dock_match(acpi_handle handle)
+{
+ return acpi_has_method(handle, "_DCK");
+}
+
+static acpi_status
+acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context,
+ void **return_value)
+{
+ long *cap = context;
+
+ if (acpi_has_method(handle, "_BCM") &&
+ acpi_has_method(handle, "_BCL")) {
+ acpi_handle_debug(handle, "Found generic backlight support\n");
+ *cap |= ACPI_VIDEO_BACKLIGHT;
+ /* We have backlight support, no need to scan further */
+ return AE_CTRL_TERMINATE;
+ }
+ return 0;
+}
+
+/* Returns true if the ACPI object is a video device which can be
+ * handled by video.ko.
+ * The device will get a Linux specific CID added in scan.c to
+ * identify the device as an ACPI graphics device
+ * Be aware that the graphics device may not be physically present
+ * Use acpi_video_get_capabilities() to detect general ACPI video
+ * capabilities of present cards
+ */
+long acpi_is_video_device(acpi_handle handle)
+{
+ long video_caps = 0;
+
+ /* Is this device able to support video switching ? */
+ if (acpi_has_method(handle, "_DOD") || acpi_has_method(handle, "_DOS"))
+ video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING;
+
+ /* Is this device able to retrieve a video ROM ? */
+ if (acpi_has_method(handle, "_ROM"))
+ video_caps |= ACPI_VIDEO_ROM_AVAILABLE;
+
+ /* Is this device able to configure which video head to be POSTed ? */
+ if (acpi_has_method(handle, "_VPO") &&
+ acpi_has_method(handle, "_GPD") &&
+ acpi_has_method(handle, "_SPD"))
+ video_caps |= ACPI_VIDEO_DEVICE_POSTING;
+
+ /* Only check for backlight functionality if one of the above hit. */
+ if (video_caps)
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
+ ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL,
+ &video_caps, NULL);
+
+ return video_caps;
+}
+EXPORT_SYMBOL(acpi_is_video_device);
+
+const char *acpi_device_hid(struct acpi_device *device)
+{
+ struct acpi_hardware_id *hid;
+
+ if (list_empty(&device->pnp.ids))
+ return dummy_hid;
+
+ hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list);
+ return hid->id;
+}
+EXPORT_SYMBOL(acpi_device_hid);
+
+static void acpi_add_id(struct acpi_device_pnp *pnp, const char *dev_id)
+{
+ struct acpi_hardware_id *id;
+
+ id = kmalloc(sizeof(*id), GFP_KERNEL);
+ if (!id)
+ return;
+
+ id->id = kstrdup_const(dev_id, GFP_KERNEL);
+ if (!id->id) {
+ kfree(id);
+ return;
+ }
+
+ list_add_tail(&id->list, &pnp->ids);
+ pnp->type.hardware_id = 1;
+}
+
+/*
+ * Old IBM workstations have a DSDT bug wherein the SMBus object
+ * lacks the SMBUS01 HID and the methods do not have the necessary "_"
+ * prefix. Work around this.
+ */
+static bool acpi_ibm_smbus_match(acpi_handle handle)
+{
+ char node_name[ACPI_PATH_SEGMENT_LENGTH];
+ struct acpi_buffer path = { sizeof(node_name), node_name };
+
+ if (!dmi_name_in_vendors("IBM"))
+ return false;
+
+ /* Look for SMBS object */
+ if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &path)) ||
+ strcmp("SMBS", path.pointer))
+ return false;
+
+ /* Does it have the necessary (but misnamed) methods? */
+ if (acpi_has_method(handle, "SBI") &&
+ acpi_has_method(handle, "SBR") &&
+ acpi_has_method(handle, "SBW"))
+ return true;
+
+ return false;
+}
+
+static bool acpi_object_is_system_bus(acpi_handle handle)
+{
+ acpi_handle tmp;
+
+ if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_SB", &tmp)) &&
+ tmp == handle)
+ return true;
+ if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_TZ", &tmp)) &&
+ tmp == handle)
+ return true;
+
+ return false;
+}
+
+static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
+ int device_type)
+{
+ struct acpi_device_info *info = NULL;
+ struct acpi_pnp_device_id_list *cid_list;
+ int i;
+
+ switch (device_type) {
+ case ACPI_BUS_TYPE_DEVICE:
+ if (handle == ACPI_ROOT_OBJECT) {
+ acpi_add_id(pnp, ACPI_SYSTEM_HID);
+ break;
+ }
+
+ acpi_get_object_info(handle, &info);
+ if (!info) {
+ pr_err("%s: Error reading device info\n", __func__);
+ return;
+ }
+
+ if (info->valid & ACPI_VALID_HID) {
+ acpi_add_id(pnp, info->hardware_id.string);
+ pnp->type.platform_id = 1;
+ }
+ if (info->valid & ACPI_VALID_CID) {
+ cid_list = &info->compatible_id_list;
+ for (i = 0; i < cid_list->count; i++)
+ acpi_add_id(pnp, cid_list->ids[i].string);
+ }
+ if (info->valid & ACPI_VALID_ADR) {
+ pnp->bus_address = info->address;
+ pnp->type.bus_address = 1;
+ }
+ if (info->valid & ACPI_VALID_UID)
+ pnp->unique_id = kstrdup(info->unique_id.string,
+ GFP_KERNEL);
+ if (info->valid & ACPI_VALID_CLS)
+ acpi_add_id(pnp, info->class_code.string);
+
+ kfree(info);
+
+ /*
+ * 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)) {
+ acpi_add_id(pnp, ACPI_VIDEO_HID);
+ 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);
+ else if (acpi_ibm_smbus_match(handle))
+ acpi_add_id(pnp, ACPI_SMBUS_IBM_HID);
+ else if (list_empty(&pnp->ids) &&
+ 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);
+ }
+
+ break;
+ case ACPI_BUS_TYPE_POWER:
+ acpi_add_id(pnp, ACPI_POWER_HID);
+ break;
+ case ACPI_BUS_TYPE_PROCESSOR:
+ acpi_add_id(pnp, ACPI_PROCESSOR_OBJECT_HID);
+ break;
+ case ACPI_BUS_TYPE_THERMAL:
+ acpi_add_id(pnp, ACPI_THERMAL_HID);
+ break;
+ case ACPI_BUS_TYPE_POWER_BUTTON:
+ acpi_add_id(pnp, ACPI_BUTTON_HID_POWERF);
+ break;
+ case ACPI_BUS_TYPE_SLEEP_BUTTON:
+ acpi_add_id(pnp, ACPI_BUTTON_HID_SLEEPF);
+ break;
+ case ACPI_BUS_TYPE_ECDT_EC:
+ acpi_add_id(pnp, ACPI_ECDT_HID);
+ break;
+ }
+}
+
+void acpi_free_pnp_ids(struct acpi_device_pnp *pnp)
+{
+ struct acpi_hardware_id *id, *tmp;
+
+ list_for_each_entry_safe(id, tmp, &pnp->ids, list) {
+ kfree_const(id->id);
+ kfree(id);
+ }
+ kfree(pnp->unique_id);
+}
+
+/**
+ * acpi_dma_supported - Check DMA support for the specified device.
+ * @adev: The pointer to acpi device
+ *
+ * Return false if DMA is not supported. Otherwise, return true
+ */
+bool acpi_dma_supported(const struct acpi_device *adev)
+{
+ if (!adev)
+ return false;
+
+ if (adev->flags.cca_seen)
+ return true;
+
+ /*
+ * Per ACPI 6.0 sec 6.2.17, assume devices can do cache-coherent
+ * DMA on "Intel platforms". Presumably that includes all x86 and
+ * ia64, and other arches will set CONFIG_ACPI_CCA_REQUIRED=y.
+ */
+ if (!IS_ENABLED(CONFIG_ACPI_CCA_REQUIRED))
+ return true;
+
+ return false;
+}
+
+/**
+ * acpi_get_dma_attr - Check the supported DMA attr for the specified device.
+ * @adev: The pointer to acpi device
+ *
+ * Return enum dev_dma_attr.
+ */
+enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
+{
+ if (!acpi_dma_supported(adev))
+ return DEV_DMA_NOT_SUPPORTED;
+
+ if (adev->flags.coherent_dma)
+ return DEV_DMA_COHERENT;
+ else
+ return DEV_DMA_NON_COHERENT;
+}
+
+/**
+ * acpi_dma_get_range() - Get device DMA parameters.
+ *
+ * @dev: device to configure
+ * @map: pointer to DMA ranges result
+ *
+ * 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, const struct bus_dma_region **map)
+{
+ struct acpi_device *adev;
+ LIST_HEAD(list);
+ struct resource_entry *rentry;
+ int ret;
+ struct device *dma_dev = dev;
+ struct bus_dma_region *r;
+
+ /*
+ * Walk the device tree chasing an ACPI companion with a _DMA
+ * object while we go. Stop if we find a device with an ACPI
+ * companion containing a _DMA method.
+ */
+ do {
+ adev = ACPI_COMPANION(dma_dev);
+ if (adev && acpi_has_method(adev->handle, METHOD_NAME__DMA))
+ break;
+
+ dma_dev = dma_dev->parent;
+ } while (dma_dev);
+
+ if (!dma_dev)
+ return -ENODEV;
+
+ if (!acpi_has_method(adev->handle, METHOD_NAME__CRS)) {
+ acpi_handle_warn(adev->handle, "_DMA is valid only if _CRS is present\n");
+ return -EINVAL;
+ }
+
+ 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 (rentry->res->start >= rentry->res->end) {
+ kfree(*map);
+ *map = NULL;
+ ret = -EINVAL;
+ dev_dbg(dma_dev, "Invalid DMA regions configuration\n");
+ goto out;
+ }
+
+ r->cpu_start = rentry->res->start;
+ r->dma_start = rentry->res->start - rentry->offset;
+ r->size = resource_size(rentry->res);
+ r->offset = rentry->offset;
+ r++;
+ }
+ }
+ out:
+ acpi_dev_free_resource_list(&list);
+
+ return ret >= 0 ? 0 : ret;
+}
+
+#ifdef CONFIG_IOMMU_API
+int acpi_iommu_fwspec_init(struct device *dev, u32 id,
+ struct fwnode_handle *fwnode,
+ const struct iommu_ops *ops)
+{
+ int ret = iommu_fwspec_init(dev, fwnode, ops);
+
+ if (!ret)
+ ret = iommu_fwspec_add_ids(dev, &id, 1);
+
+ return ret;
+}
+
+static inline const struct iommu_ops *acpi_iommu_fwspec_ops(struct device *dev)
+{
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+
+ return fwspec ? fwspec->ops : NULL;
+}
+
+static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev,
+ const u32 *id_in)
+{
+ int err;
+ const struct iommu_ops *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,
+ * return the iommu_ops.
+ */
+ ops = acpi_iommu_fwspec_ops(dev);
+ if (ops) {
+ mutex_unlock(&iommu_probe_device_lock);
+ return ops;
+ }
+
+ err = iort_iommu_configure_id(dev, id_in);
+ if (err && err != -EPROBE_DEFER)
+ err = viot_iommu_configure(dev);
+ mutex_unlock(&iommu_probe_device_lock);
+
+ /*
+ * 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);
+
+ /* 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);
+}
+
+#else /* !CONFIG_IOMMU_API */
+
+int acpi_iommu_fwspec_init(struct device *dev, u32 id,
+ struct fwnode_handle *fwnode,
+ const struct iommu_ops *ops)
+{
+ return -ENODEV;
+}
+
+static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev,
+ const u32 *id_in)
+{
+ return NULL;
+}
+
+#endif /* !CONFIG_IOMMU_API */
+
+/**
+ * acpi_dma_configure_id - Set-up DMA configuration for the device.
+ * @dev: The pointer to the device
+ * @attr: device dma attributes
+ * @input_id: input device id const value pointer
+ */
+int acpi_dma_configure_id(struct device *dev, enum dev_dma_attr attr,
+ const u32 *input_id)
+{
+ const struct iommu_ops *iommu;
+
+ if (attr == DEV_DMA_NOT_SUPPORTED) {
+ set_dma_ops(dev, &dma_dummy_ops);
+ return 0;
+ }
+
+ acpi_arch_dma_setup(dev);
+
+ iommu = acpi_iommu_configure_id(dev, input_id);
+ if (PTR_ERR(iommu) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ arch_setup_dma_ops(dev, 0, U64_MAX,
+ iommu, attr == DEV_DMA_COHERENT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_dma_configure_id);
+
+static void acpi_init_coherency(struct acpi_device *adev)
+{
+ unsigned long long cca = 0;
+ acpi_status status;
+ struct acpi_device *parent = acpi_dev_parent(adev);
+
+ if (parent && parent->flags.cca_seen) {
+ /*
+ * From ACPI spec, OSPM will ignore _CCA if an ancestor
+ * already saw one.
+ */
+ adev->flags.cca_seen = 1;
+ cca = parent->flags.coherent_dma;
+ } else {
+ status = acpi_evaluate_integer(adev->handle, "_CCA",
+ NULL, &cca);
+ if (ACPI_SUCCESS(status))
+ adev->flags.cca_seen = 1;
+ else if (!IS_ENABLED(CONFIG_ACPI_CCA_REQUIRED))
+ /*
+ * If architecture does not specify that _CCA is
+ * required for DMA-able devices (e.g. x86),
+ * we default to _CCA=1.
+ */
+ cca = 1;
+ else
+ acpi_handle_debug(adev->handle,
+ "ACPI device is missing _CCA.\n");
+ }
+
+ adev->flags.coherent_dma = cca;
+}
+
+static int acpi_check_serial_bus_slave(struct acpi_resource *ares, void *data)
+{
+ bool *is_serial_bus_slave_p = data;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
+ return 1;
+
+ *is_serial_bus_slave_p = true;
+
+ /* no need to do more checking */
+ return -1;
+}
+
+static bool acpi_is_indirect_io_slave(struct acpi_device *device)
+{
+ struct acpi_device *parent = acpi_dev_parent(device);
+ static const struct acpi_device_id indirect_io_hosts[] = {
+ {"HISI0191", 0},
+ {}
+ };
+
+ return parent && !acpi_match_device_ids(parent, indirect_io_hosts);
+}
+
+static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
+{
+ struct list_head resource_list;
+ bool is_serial_bus_slave = false;
+ static const struct acpi_device_id ignore_serial_bus_ids[] = {
+ /*
+ * These devices have multiple SerialBus resources and a client
+ * device must be instantiated for each of them, each with
+ * its own device id.
+ * Normally we only instantiate one client device for the first
+ * resource, using the ACPI HID as id. These special cases are handled
+ * by the drivers/platform/x86/serial-multi-instantiate.c driver, which
+ * knows which client device id to use for each resource.
+ */
+ {"BSG1160", },
+ {"BSG2150", },
+ {"CSC3551", },
+ {"CSC3556", },
+ {"INT33FE", },
+ {"INT3515", },
+ /* Non-conforming _HID for Cirrus Logic already released */
+ {"CLSA0100", },
+ {"CLSA0101", },
+ /*
+ * Some ACPI devs contain SerialBus resources even though they are not
+ * attached to a serial bus at all.
+ */
+ {"MSHW0028", },
+ /*
+ * HIDs of device with an UartSerialBusV2 resource for which userspace
+ * expects a regular tty cdev to be created (instead of the in kernel
+ * serdev) and which have a kernel driver which expects a platform_dev
+ * such as the rfkill-gpio driver.
+ */
+ {"BCM4752", },
+ {"LNV4752", },
+ {}
+ };
+
+ if (acpi_is_indirect_io_slave(device))
+ return true;
+
+ /* Macs use device properties in lieu of _CRS resources */
+ if (x86_apple_machine &&
+ (fwnode_property_present(&device->fwnode, "spiSclkPeriod") ||
+ fwnode_property_present(&device->fwnode, "i2cAddress") ||
+ fwnode_property_present(&device->fwnode, "baud")))
+ return true;
+
+ if (!acpi_match_device_ids(device, ignore_serial_bus_ids))
+ return false;
+
+ INIT_LIST_HEAD(&resource_list);
+ acpi_dev_get_resources(device, &resource_list,
+ acpi_check_serial_bus_slave,
+ &is_serial_bus_slave);
+ acpi_dev_free_resource_list(&resource_list);
+
+ return is_serial_bus_slave;
+}
+
+void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
+ 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->dev.parent = parent ? &parent->dev : NULL;
+ device->dev.release = release;
+ device->dev.bus = &acpi_bus_type;
+ fwnode_init(&device->fwnode, &acpi_device_fwnode_ops);
+ acpi_set_device_status(device, ACPI_STA_DEFAULT);
+ acpi_device_get_busid(device);
+ acpi_set_pnp_ids(handle, &device->pnp, type);
+ acpi_init_properties(device);
+ acpi_bus_get_flags(device);
+ device->flags.match_driver = false;
+ device->flags.initialized = true;
+ device->flags.enumeration_by_parent =
+ acpi_device_enumeration_by_parent(device);
+ acpi_device_clear_enumerated(device);
+ device_initialize(&device->dev);
+ dev_set_uevent_suppress(&device->dev, true);
+ acpi_init_coherency(device);
+}
+
+static void acpi_scan_dep_init(struct acpi_device *adev)
+{
+ struct acpi_dep_data *dep;
+
+ list_for_each_entry(dep, &acpi_dep_list, node) {
+ if (dep->consumer == adev->handle) {
+ if (dep->honor_dep)
+ adev->flags.honor_deps = 1;
+
+ adev->dep_unmet++;
+ }
+ }
+}
+
+void acpi_device_add_finalize(struct acpi_device *device)
+{
+ dev_set_uevent_suppress(&device->dev, false);
+ kobject_uevent(&device->dev.kobj, KOBJ_ADD);
+}
+
+static void acpi_scan_init_status(struct acpi_device *adev)
+{
+ if (acpi_bus_get_status(adev))
+ acpi_set_device_status(adev, 0);
+}
+
+static int acpi_add_single_object(struct acpi_device **child,
+ acpi_handle handle, int type, bool dep_init)
+{
+ struct acpi_device *device;
+ bool release_dep_lock = false;
+ int result;
+
+ device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
+ if (!device)
+ return -ENOMEM;
+
+ 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
+ * this must be done before the get power-/wakeup_dev-flags calls.
+ */
+ if (type == ACPI_BUS_TYPE_DEVICE || type == ACPI_BUS_TYPE_PROCESSOR) {
+ if (dep_init) {
+ mutex_lock(&acpi_dep_list_lock);
+ /*
+ * Hold the lock until the acpi_tie_acpi_dev() call
+ * below to prevent concurrent acpi_scan_clear_dep()
+ * from deleting a dependency list entry without
+ * updating dep_unmet for the device.
+ */
+ release_dep_lock = true;
+ acpi_scan_dep_init(device);
+ }
+ acpi_scan_init_status(device);
+ }
+
+ acpi_bus_get_power_flags(device);
+ acpi_bus_get_wakeup_device_flags(device);
+
+ result = acpi_tie_acpi_dev(device);
+
+ if (release_dep_lock)
+ mutex_unlock(&acpi_dep_list_lock);
+
+ if (!result)
+ result = acpi_device_add(device);
+
+ if (result) {
+ acpi_device_release(&device->dev);
+ return result;
+ }
+
+ acpi_power_add_remove_device(device, true);
+ acpi_device_add_finalize(device);
+
+ acpi_handle_debug(handle, "Added as %s, parent %s\n",
+ dev_name(&device->dev), device->dev.parent ?
+ dev_name(device->dev.parent) : "(null)");
+
+ *child = device;
+ return 0;
+}
+
+static acpi_status acpi_get_resource_memory(struct acpi_resource *ares,
+ void *context)
+{
+ struct resource *res = context;
+
+ if (acpi_dev_resource_memory(ares, res))
+ return AE_CTRL_TERMINATE;
+
+ return AE_OK;
+}
+
+static bool acpi_device_should_be_hidden(acpi_handle handle)
+{
+ acpi_status status;
+ struct resource res;
+
+ /* Check if it should ignore the UART device */
+ if (!(spcr_uart_addr && acpi_has_method(handle, METHOD_NAME__CRS)))
+ return false;
+
+ /*
+ * The UART device described in SPCR table is assumed to have only one
+ * memory resource present. So we only look for the first one here.
+ */
+ status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+ acpi_get_resource_memory, &res);
+ if (ACPI_FAILURE(status) || res.start != spcr_uart_addr)
+ return false;
+
+ acpi_handle_info(handle, "The UART device @%pa in SPCR table will be hidden\n",
+ &res.start);
+
+ return true;
+}
+
+bool acpi_device_is_present(const struct acpi_device *adev)
+{
+ return adev->status.present || adev->status.functional;
+}
+
+static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,
+ const char *idstr,
+ const struct acpi_device_id **matchid)
+{
+ const struct acpi_device_id *devid;
+
+ if (handler->match)
+ return handler->match(idstr, matchid);
+
+ for (devid = handler->ids; devid->id[0]; devid++)
+ if (!strcmp((char *)devid->id, idstr)) {
+ if (matchid)
+ *matchid = devid;
+
+ return true;
+ }
+
+ return false;
+}
+
+static struct acpi_scan_handler *acpi_scan_match_handler(const char *idstr,
+ const struct acpi_device_id **matchid)
+{
+ struct acpi_scan_handler *handler;
+
+ list_for_each_entry(handler, &acpi_scan_handlers_list, list_node)
+ if (acpi_scan_handler_matching(handler, idstr, matchid))
+ return handler;
+
+ return NULL;
+}
+
+void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val)
+{
+ if (!!hotplug->enabled == !!val)
+ return;
+
+ mutex_lock(&acpi_scan_lock);
+
+ hotplug->enabled = 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)
+{
+ 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++) {
+ struct acpi_device_info *info;
+ struct acpi_dep_data *dep;
+ bool skip, honor_dep;
+
+ 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;
+ }
+
+ skip = acpi_info_matches_ids(info, acpi_ignore_dep_ids);
+ honor_dep = acpi_info_matches_ids(info, acpi_honor_dep_ids);
+ kfree(info);
+
+ if (skip)
+ continue;
+
+ dep = kzalloc(sizeof(*dep), GFP_KERNEL);
+ if (!dep)
+ continue;
+
+ count++;
+
+ 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);
+ mutex_unlock(&acpi_dep_list_lock);
+ }
+
+ return count;
+}
+
+static bool acpi_bus_scan_second_pass;
+
+static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
+ struct acpi_device **adev_p)
+{
+ struct acpi_device *device = acpi_fetch_acpi_dev(handle);
+ acpi_object_type acpi_type;
+ int type;
+
+ if (device)
+ goto out;
+
+ if (ACPI_FAILURE(acpi_get_type(handle, &acpi_type)))
+ return AE_OK;
+
+ switch (acpi_type) {
+ case ACPI_TYPE_DEVICE:
+ 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;
+ }
+
+ fallthrough;
+ case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */
+ type = ACPI_BUS_TYPE_DEVICE;
+ break;
+
+ case ACPI_TYPE_PROCESSOR:
+ type = ACPI_BUS_TYPE_PROCESSOR;
+ break;
+
+ case ACPI_TYPE_THERMAL:
+ type = ACPI_BUS_TYPE_THERMAL;
+ break;
+
+ case ACPI_TYPE_POWER:
+ acpi_add_power_resource(handle);
+ fallthrough;
+ default:
+ return AE_OK;
+ }
+
+ /*
+ * If check_dep 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);
+ if (!device)
+ return AE_CTRL_DEPTH;
+
+ acpi_scan_init_hotplug(device);
+
+out:
+ if (!*adev_p)
+ *adev_p = device;
+
+ return AE_OK;
+}
+
+static acpi_status acpi_bus_check_add_1(acpi_handle handle, u32 lvl_not_used,
+ void *not_used, void **ret_p)
+{
+ return acpi_bus_check_add(handle, true, (struct acpi_device **)ret_p);
+}
+
+static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used,
+ void *not_used, void **ret_p)
+{
+ return acpi_bus_check_add(handle, false, (struct acpi_device **)ret_p);
+}
+
+static void acpi_default_enumeration(struct acpi_device *device)
+{
+ /*
+ * Do not enumerate devices with enumeration_by_parent flag set as
+ * they will be enumerated by their respective parents.
+ */
+ if (!device->flags.enumeration_by_parent) {
+ acpi_create_platform_device(device, NULL);
+ acpi_device_set_enumerated(device);
+ } else {
+ blocking_notifier_call_chain(&acpi_reconfig_chain,
+ ACPI_RECONFIG_DEVICE_ADD, device);
+ }
+}
+
+static const struct acpi_device_id generic_device_ids[] = {
+ {ACPI_DT_NAMESPACE_HID, },
+ {"", },
+};
+
+static int acpi_generic_device_attach(struct acpi_device *adev,
+ const struct acpi_device_id *not_used)
+{
+ /*
+ * Since ACPI_DT_NAMESPACE_HID is the only ID handled here, the test
+ * below can be unconditional.
+ */
+ if (adev->data.of_compatible)
+ acpi_default_enumeration(adev);
+
+ return 1;
+}
+
+static struct acpi_scan_handler generic_device_handler = {
+ .ids = generic_device_ids,
+ .attach = acpi_generic_device_attach,
+};
+
+static int acpi_scan_attach_handler(struct acpi_device *device)
+{
+ struct acpi_hardware_id *hwid;
+ int ret = 0;
+
+ list_for_each_entry(hwid, &device->pnp.ids, list) {
+ const struct acpi_device_id *devid;
+ struct acpi_scan_handler *handler;
+
+ handler = acpi_scan_match_handler(hwid->id, &devid);
+ if (handler) {
+ if (!handler->attach) {
+ device->pnp.type.platform_id = 0;
+ continue;
+ }
+ device->handler = handler;
+ ret = handler->attach(device, devid);
+ if (ret > 0)
+ break;
+
+ device->handler = NULL;
+ if (ret < 0)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int acpi_bus_attach(struct acpi_device *device, void *first_pass)
+{
+ bool skip = !first_pass && device->flags.visited;
+ acpi_handle ejd;
+ int ret;
+
+ if (skip)
+ goto ok;
+
+ if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd)))
+ register_dock_dependent_device(device, ejd);
+
+ acpi_bus_get_status(device);
+ /* Skip devices that are not ready for enumeration (e.g. not present) */
+ if (!acpi_dev_ready_for_enumeration(device)) {
+ device->flags.initialized = false;
+ acpi_device_clear_enumerated(device);
+ device->flags.power_manageable = 0;
+ return 0;
+ }
+ if (device->handler)
+ goto ok;
+
+ if (!device->flags.initialized) {
+ device->flags.power_manageable =
+ device->power.states[ACPI_STATE_D0].flags.valid;
+ if (acpi_bus_init_power(device))
+ device->flags.power_manageable = 0;
+
+ device->flags.initialized = true;
+ } else if (device->flags.visited) {
+ goto ok;
+ }
+
+ ret = acpi_scan_attach_handler(device);
+ if (ret < 0)
+ return 0;
+
+ device->flags.match_driver = true;
+ if (ret > 0 && !device->flags.enumeration_by_parent) {
+ acpi_device_set_enumerated(device);
+ goto ok;
+ }
+
+ ret = device_attach(&device->dev);
+ if (ret < 0)
+ return 0;
+
+ if (device->pnp.type.platform_id || device->flags.enumeration_by_parent)
+ acpi_default_enumeration(device);
+ else
+ acpi_device_set_enumerated(device);
+
+ok:
+ acpi_dev_for_each_child(device, acpi_bus_attach, first_pass);
+
+ if (!skip && device->handler && device->handler->hotplug.notify_online)
+ device->handler->hotplug.notify_online(device);
+
+ return 0;
+}
+
+static int acpi_dev_get_next_consumer_dev_cb(struct acpi_dep_data *dep, void *data)
+{
+ 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_get_acpi_dev(dep->consumer);
+ if (adev) {
+ *(struct acpi_device **)data = adev;
+ return 1;
+ }
+ /* Continue parsing if the device object is not present. */
+ return 0;
+}
+
+struct acpi_scan_clear_dep_work {
+ struct work_struct work;
+ struct acpi_device *adev;
+};
+
+static void acpi_scan_clear_dep_fn(struct work_struct *work)
+{
+ struct acpi_scan_clear_dep_work *cdw;
+
+ cdw = container_of(work, struct acpi_scan_clear_dep_work, work);
+
+ acpi_scan_lock_acquire();
+ acpi_bus_attach(cdw->adev, (void *)true);
+ acpi_scan_lock_release();
+
+ acpi_dev_put(cdw->adev);
+ kfree(cdw);
+}
+
+static bool acpi_scan_clear_dep_queue(struct acpi_device *adev)
+{
+ struct acpi_scan_clear_dep_work *cdw;
+
+ if (adev->dep_unmet)
+ return false;
+
+ cdw = kmalloc(sizeof(*cdw), GFP_KERNEL);
+ if (!cdw)
+ return false;
+
+ cdw->adev = adev;
+ INIT_WORK(&cdw->work, acpi_scan_clear_dep_fn);
+ /*
+ * Since the work function may block on the lock until the entire
+ * initial enumeration of devices is complete, put it into the unbound
+ * workqueue.
+ */
+ queue_work(system_unbound_wq, &cdw->work);
+
+ return true;
+}
+
+static int acpi_scan_clear_dep(struct acpi_dep_data *dep, void *data)
+{
+ struct acpi_device *adev = acpi_get_acpi_dev(dep->consumer);
+
+ if (adev) {
+ adev->dep_unmet--;
+ if (!acpi_scan_clear_dep_queue(adev))
+ acpi_dev_put(adev);
+ }
+
+ list_del(&dep->node);
+ kfree(dep);
+
+ return 0;
+}
+
+/**
+ * acpi_walk_dep_device_list - Apply a callback to every entry in acpi_dep_list
+ * @handle: The ACPI handle of the supplier device
+ * @callback: Pointer to the callback function to apply
+ * @data: Pointer to some data to pass to the callback
+ *
+ * The return value of the callback determines this function's behaviour. If 0
+ * is returned we continue to iterate over acpi_dep_list. If a positive value
+ * is returned then the loop is broken but this function returns 0. If a
+ * negative value is returned by the callback then the loop is broken and that
+ * value is returned as the final error.
+ */
+static int acpi_walk_dep_device_list(acpi_handle handle,
+ int (*callback)(struct acpi_dep_data *, void *),
+ void *data)
+{
+ struct acpi_dep_data *dep, *tmp;
+ int ret = 0;
+
+ mutex_lock(&acpi_dep_list_lock);
+ list_for_each_entry_safe(dep, tmp, &acpi_dep_list, node) {
+ if (dep->supplier == handle) {
+ ret = callback(dep, data);
+ if (ret)
+ break;
+ }
+ }
+ mutex_unlock(&acpi_dep_list_lock);
+
+ return ret > 0 ? 0 : ret;
+}
+
+/**
+ * acpi_dev_clear_dependencies - Inform consumers that the device is now active
+ * @supplier: Pointer to the supplier &struct acpi_device
+ *
+ * Clear dependencies on the given device.
+ */
+void acpi_dev_clear_dependencies(struct acpi_device *supplier)
+{
+ acpi_walk_dep_device_list(supplier->handle, acpi_scan_clear_dep, NULL);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_clear_dependencies);
+
+/**
+ * acpi_dev_ready_for_enumeration - Check if the ACPI device is ready for enumeration
+ * @device: Pointer to the &struct acpi_device to check
+ *
+ * Check if the device is present and has no unmet dependencies.
+ *
+ * Return true if the device is ready for enumeratino. Otherwise, return false.
+ */
+bool acpi_dev_ready_for_enumeration(const struct acpi_device *device)
+{
+ if (device->flags.honor_deps && device->dep_unmet)
+ return false;
+
+ return acpi_device_is_present(device);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_ready_for_enumeration);
+
+/**
+ * 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 next &struct acpi_device which declares itself dependent on
+ * @supplier via the _DEP buffer, parsed from the acpi_dep_list.
+ *
+ * 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_next_consumer_dev(struct acpi_device *supplier,
+ struct acpi_device *start)
+{
+ struct acpi_device *adev = start;
+
+ acpi_walk_dep_device_list(supplier->handle,
+ 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_next_consumer_dev);
+
+/**
+ * acpi_bus_scan - Add ACPI device node objects in a given namespace scope.
+ * @handle: Root of the namespace scope to scan.
+ *
+ * Scan a given ACPI tree (probably recently hot-plugged) and create and add
+ * found devices.
+ *
+ * If no devices were found, -ENODEV is returned, but it does not mean that
+ * there has been a real error. There just have been no suitable ACPI objects
+ * in the table trunk from which the kernel could create a device and add an
+ * appropriate driver.
+ *
+ * Must be called under acpi_scan_lock.
+ */
+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)))
+ acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
+ acpi_bus_check_add_1, NULL, NULL,
+ (void **)&device);
+
+ if (!device)
+ return -ENODEV;
+
+ acpi_bus_attach(device, (void *)true);
+
+ if (!acpi_bus_scan_second_pass)
+ return 0;
+
+ /* Pass 2: Enumerate all of the remaining devices. */
+
+ device = NULL;
+
+ 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);
+
+ 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.
+ *
+ * Must be called under acpi_scan_lock.
+ */
+void acpi_bus_trim(struct acpi_device *adev)
+{
+ acpi_bus_trim_one(adev, NULL);
+}
+EXPORT_SYMBOL_GPL(acpi_bus_trim);
+
+int acpi_bus_register_early_device(int type)
+{
+ struct acpi_device *device = NULL;
+ int result;
+
+ result = acpi_add_single_object(&device, NULL, type, false);
+ if (result)
+ return result;
+
+ device->flags.match_driver = true;
+ return device_attach(&device->dev);
+}
+EXPORT_SYMBOL_GPL(acpi_bus_register_early_device);
+
+static void acpi_bus_scan_fixed(void)
+{
+ if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) {
+ struct acpi_device *adev = NULL;
+
+ acpi_add_single_object(&adev, NULL, ACPI_BUS_TYPE_POWER_BUTTON,
+ false);
+ if (adev) {
+ adev->flags.match_driver = true;
+ if (device_attach(&adev->dev) >= 0)
+ device_init_wakeup(&adev->dev, true);
+ else
+ dev_dbg(&adev->dev, "No driver\n");
+ }
+ }
+
+ if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) {
+ struct acpi_device *adev = NULL;
+
+ acpi_add_single_object(&adev, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON,
+ false);
+ if (adev) {
+ adev->flags.match_driver = true;
+ if (device_attach(&adev->dev) < 0)
+ dev_dbg(&adev->dev, "No driver\n");
+ }
+ }
+}
+
+static void __init acpi_get_spcr_uart_addr(void)
+{
+ acpi_status status;
+ struct acpi_table_spcr *spcr_ptr;
+
+ status = acpi_get_table(ACPI_SIG_SPCR, 0,
+ (struct acpi_table_header **)&spcr_ptr);
+ if (ACPI_FAILURE(status)) {
+ pr_warn("STAO table present, but SPCR is missing\n");
+ return;
+ }
+
+ spcr_uart_addr = spcr_ptr->serial_port.address;
+ acpi_put_table((struct acpi_table_header *)spcr_ptr);
+}
+
+static bool acpi_scan_initialized;
+
+void __init acpi_scan_init(void)
+{
+ acpi_status status;
+ struct acpi_table_stao *stao_ptr;
+
+ acpi_pci_root_init();
+ acpi_pci_link_init();
+ acpi_processor_init();
+ acpi_platform_init();
+ acpi_lpss_init();
+ acpi_apd_init();
+ acpi_cmos_rtc_init();
+ acpi_container_init();
+ acpi_memory_hotplug_init();
+ acpi_watchdog_init();
+ acpi_pnp_init();
+ acpi_int340x_thermal_init();
+ acpi_amba_init();
+ acpi_init_lpit();
+
+ acpi_scan_add_handler(&generic_device_handler);
+
+ /*
+ * If there is STAO table, check whether it needs to ignore the UART
+ * device in SPCR table.
+ */
+ status = acpi_get_table(ACPI_SIG_STAO, 0,
+ (struct acpi_table_header **)&stao_ptr);
+ if (ACPI_SUCCESS(status)) {
+ if (stao_ptr->header.length > sizeof(struct acpi_table_stao))
+ pr_info("STAO Name List not yet supported.\n");
+
+ if (stao_ptr->ignore_uart)
+ acpi_get_spcr_uart_addr();
+
+ acpi_put_table((struct acpi_table_header *)stao_ptr);
+ }
+
+ acpi_gpe_apply_masked_gpes();
+ acpi_update_all_gpes();
+
+ /*
+ * Although we call __add_memory() that is documented to require the
+ * device_hotplug_lock, it is not necessary here because this is an
+ * early code when userspace or any other code path cannot trigger
+ * hotplug/hotunplug operations.
+ */
+ mutex_lock(&acpi_scan_lock);
+ /*
+ * Enumerate devices in the ACPI namespace.
+ */
+ if (acpi_bus_scan(ACPI_ROOT_OBJECT))
+ goto unlock;
+
+ acpi_root = acpi_fetch_acpi_dev(ACPI_ROOT_OBJECT);
+ if (!acpi_root)
+ goto unlock;
+
+ /* Fixed feature devices do not exist on HW-reduced platform */
+ if (!acpi_gbl_reduced_hardware)
+ acpi_bus_scan_fixed();
+
+ acpi_turn_off_unused_power_resources();
+
+ acpi_scan_initialized = true;
+
+unlock:
+ mutex_unlock(&acpi_scan_lock);
+}
+
+static struct acpi_probe_entry *ape;
+static int acpi_probe_count;
+static DEFINE_MUTEX(acpi_probe_mutex);
+
+static int __init acpi_match_madt(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ if (!ape->subtable_valid || ape->subtable_valid(&header->common, ape))
+ if (!ape->probe_subtbl(header, end))
+ acpi_probe_count++;
+
+ return 0;
+}
+
+int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr)
+{
+ int count = 0;
+
+ if (acpi_disabled)
+ return 0;
+
+ mutex_lock(&acpi_probe_mutex);
+ for (ape = ap_head; nr; ape++, nr--) {
+ if (ACPI_COMPARE_NAMESEG(ACPI_SIG_MADT, ape->id)) {
+ acpi_probe_count = 0;
+ acpi_table_parse_madt(ape->type, acpi_match_madt, 0);
+ count += acpi_probe_count;
+ } else {
+ int res;
+ res = acpi_table_parse(ape->id, ape->probe_table);
+ if (!res)
+ count++;
+ }
+ }
+ mutex_unlock(&acpi_probe_mutex);
+
+ return count;
+}
+
+static void acpi_table_events_fn(struct work_struct *work)
+{
+ acpi_scan_lock_acquire();
+ acpi_bus_scan(ACPI_ROOT_OBJECT);
+ acpi_scan_lock_release();
+
+ kfree(work);
+}
+
+void acpi_scan_table_notify(void)
+{
+ struct work_struct *work;
+
+ if (!acpi_scan_initialized)
+ return;
+
+ work = kmalloc(sizeof(*work), GFP_KERNEL);
+ if (!work)
+ return;
+
+ INIT_WORK(work, acpi_table_events_fn);
+ schedule_work(work);
+}
+
+int acpi_reconfig_notifier_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&acpi_reconfig_chain, nb);
+}
+EXPORT_SYMBOL(acpi_reconfig_notifier_register);
+
+int acpi_reconfig_notifier_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&acpi_reconfig_chain, nb);
+}
+EXPORT_SYMBOL(acpi_reconfig_notifier_unregister);
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
new file mode 100644
index 000000000..539c12fbd
--- /dev/null
+++ b/drivers/acpi/sleep.c
@@ -0,0 +1,1128 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sleep.c - ACPI sleep support.
+ *
+ * Copyright (c) 2005 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
+ * Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com>
+ * Copyright (c) 2000-2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ */
+
+#define pr_fmt(fmt) "ACPI: PM: " fmt
+
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/dmi.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/reboot.h>
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/syscore_ops.h>
+#include <asm/io.h>
+#include <trace/events/power.h>
+
+#include "internal.h"
+#include "sleep.h"
+
+/*
+ * Some HW-full platforms do not have _S5, so they may need
+ * to leverage efi power off for a shutdown.
+ */
+bool acpi_no_s5;
+static u8 sleep_states[ACPI_S_STATE_COUNT];
+
+static void acpi_sleep_tts_switch(u32 acpi_state)
+{
+ acpi_status status;
+
+ status = acpi_execute_simple_method(NULL, "\\_TTS", acpi_state);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ /*
+ * OS can't evaluate the _TTS object correctly. Some warning
+ * message will be printed. But it won't break anything.
+ */
+ pr_notice("Failure in evaluating _TTS object\n");
+ }
+}
+
+static int tts_notify_reboot(struct notifier_block *this,
+ unsigned long code, void *x)
+{
+ acpi_sleep_tts_switch(ACPI_STATE_S5);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block tts_notifier = {
+ .notifier_call = tts_notify_reboot,
+ .next = NULL,
+ .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 && !acpi_skip_set_wakeup_address()) {
+ acpi_wakeup_address = acpi_get_wakeup_address();
+ if (!acpi_wakeup_address)
+ return -EFAULT;
+ acpi_set_waking_vector(acpi_wakeup_address);
+
+ }
+#endif
+ pr_info("Preparing to enter system sleep state S%d\n", acpi_state);
+ acpi_enable_wakeup_devices(acpi_state);
+ acpi_enter_sleep_state_prep(acpi_state);
+ return 0;
+}
+
+bool acpi_sleep_state_supported(u8 sleep_state)
+{
+ acpi_status status;
+ u8 type_a, type_b;
+
+ status = acpi_get_sleep_type_data(sleep_state, &type_a, &type_b);
+ return ACPI_SUCCESS(status) && (!acpi_gbl_reduced_hardware
+ || (acpi_gbl_FADT.sleep_control.address
+ && acpi_gbl_FADT.sleep_status.address));
+}
+
+#ifdef CONFIG_ACPI_SLEEP
+static u32 acpi_target_sleep_state = ACPI_STATE_S0;
+
+u32 acpi_target_system_state(void)
+{
+ return acpi_target_sleep_state;
+}
+EXPORT_SYMBOL_GPL(acpi_target_system_state);
+
+static bool pwr_btn_event_pending;
+
+/*
+ * The ACPI specification wants us to save NVS memory regions during hibernation
+ * and to restore them during the subsequent resume. Windows does that also for
+ * suspend to RAM. However, it is known that this mechanism does not work on
+ * all machines, so we allow the user to disable it with the help of the
+ * 'acpi_sleep=nonvs' kernel command line option.
+ */
+static bool nvs_nosave;
+
+void __init acpi_nvs_nosave(void)
+{
+ nvs_nosave = true;
+}
+
+/*
+ * The ACPI specification wants us to save NVS memory regions during hibernation
+ * but says nothing about saving NVS during S3. Not all versions of Windows
+ * save NVS on S3 suspend either, and it is clear that not all systems need
+ * NVS to be saved at S3 time. To improve suspend/resume time, allow the
+ * user to disable saving NVS on S3 if their system does not require it, but
+ * continue to save/restore NVS for S4 as specified.
+ */
+static bool nvs_nosave_s3;
+
+void __init acpi_nvs_nosave_s3(void)
+{
+ nvs_nosave_s3 = true;
+}
+
+static int __init init_nvs_save_s3(const struct dmi_system_id *d)
+{
+ nvs_nosave_s3 = false;
+ return 0;
+}
+
+/*
+ * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the
+ * user to request that behavior by using the 'acpi_old_suspend_ordering'
+ * kernel command line option that causes the following variable to be set.
+ */
+static bool old_suspend_ordering;
+
+void __init acpi_old_suspend_ordering(void)
+{
+ old_suspend_ordering = true;
+}
+
+static int __init init_old_suspend_ordering(const struct dmi_system_id *d)
+{
+ acpi_old_suspend_ordering();
+ return 0;
+}
+
+static int __init init_nvs_nosave(const struct dmi_system_id *d)
+{
+ acpi_nvs_nosave();
+ return 0;
+}
+
+bool acpi_sleep_default_s3;
+
+static int __init init_default_s3(const struct dmi_system_id *d)
+{
+ acpi_sleep_default_s3 = true;
+ return 0;
+}
+
+static const struct dmi_system_id acpisleep_dmi_table[] __initconst = {
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "Abit KN9 (nForce4 variant)",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "http://www.abit.com.tw/"),
+ DMI_MATCH(DMI_BOARD_NAME, "KN9 Series(NF-CK804)"),
+ },
+ },
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "HP xw4600 Workstation",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP xw4600 Workstation"),
+ },
+ },
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "Asus Pundit P1-AH2 (M2N8L motherboard)",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "M2N8L"),
+ },
+ },
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "Panasonic CF51-2L",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR,
+ "Matsushita Electric Industrial Co.,Ltd."),
+ DMI_MATCH(DMI_BOARD_NAME, "CF51-2L"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VGN-FW41E_H",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW41E_H"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VGN-FW21E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21E"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VGN-FW21M",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21M"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VPCEB17FX",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB17FX"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VGN-SR11M",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR11M"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Everex StepNote Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Everex Systems, Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Everex StepNote Series"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VPCEB1Z1E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1Z1E"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VGN-NW130D",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NW130D"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VPCCW29FX",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VPCCW29FX"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Averatec AV1020-ED2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AVERATEC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1000 Series"),
+ },
+ },
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "Asus A8N-SLI DELUXE",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI DELUXE"),
+ },
+ },
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "Asus A8N-SLI Premium",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI Premium"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VGN-SR26GN_P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR26GN_P"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VPCEB1S1E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1S1E"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VGN-FW520F",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW520F"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Asus K54C",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "K54C"),
+ },
+ },
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Asus K54HR",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "K54HR"),
+ },
+ },
+ {
+ .callback = init_nvs_save_s3,
+ .ident = "Asus 1025C",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1025C"),
+ },
+ },
+ /*
+ * 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.
+ */
+ {
+ .callback = init_nvs_save_s3,
+ .ident = "Lenovo G50-45",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "80E3"),
+ },
+ },
+ {
+ .callback = init_nvs_save_s3,
+ .ident = "Lenovo G40-45",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "80E1"),
+ },
+ },
+ /*
+ * ThinkPad X1 Tablet(2016) cannot do suspend-to-idle using
+ * the Low Power S0 Idle firmware interface (see
+ * https://bugzilla.kernel.org/show_bug.cgi?id=199057).
+ */
+ {
+ .callback = init_default_s3,
+ .ident = "ThinkPad X1 Tablet(2016)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ 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"),
+ },
+ },
+ {},
+};
+
+static bool ignore_blacklist;
+
+void __init acpi_sleep_no_blacklist(void)
+{
+ ignore_blacklist = true;
+}
+
+static void __init acpi_sleep_dmi_check(void)
+{
+ if (ignore_blacklist)
+ return;
+
+ if (dmi_get_bios_year() >= 2012)
+ acpi_nvs_nosave_s3();
+
+ dmi_check_system(acpisleep_dmi_table);
+}
+
+/**
+ * acpi_pm_freeze - Disable the GPEs and suspend EC transactions.
+ */
+static int acpi_pm_freeze(void)
+{
+ acpi_disable_all_gpes();
+ acpi_os_wait_events_complete();
+ acpi_ec_block_transactions();
+ return 0;
+}
+
+/**
+ * acpi_pm_pre_suspend - Enable wakeup devices, "freeze" EC and save NVS.
+ */
+static int acpi_pm_pre_suspend(void)
+{
+ acpi_pm_freeze();
+ return suspend_nvs_save();
+}
+
+/**
+ * __acpi_pm_prepare - Prepare the platform to enter the target state.
+ *
+ * If necessary, set the firmware waking vector and do arch-specific
+ * nastiness to get the wakeup code to the waking vector.
+ */
+static int __acpi_pm_prepare(void)
+{
+ int error = acpi_sleep_prepare(acpi_target_sleep_state);
+ if (error)
+ acpi_target_sleep_state = ACPI_STATE_S0;
+
+ return error;
+}
+
+/**
+ * acpi_pm_prepare - Prepare the platform to enter the target sleep
+ * state and disable the GPEs.
+ */
+static int acpi_pm_prepare(void)
+{
+ int error = __acpi_pm_prepare();
+ if (!error)
+ error = acpi_pm_pre_suspend();
+
+ return error;
+}
+
+/**
+ * acpi_pm_finish - Instruct the platform to leave a sleep state.
+ *
+ * This is called after we wake back up (or if entering the sleep state
+ * failed).
+ */
+static void acpi_pm_finish(void)
+{
+ struct acpi_device *pwr_btn_adev;
+ u32 acpi_state = acpi_target_sleep_state;
+
+ acpi_ec_unblock_transactions();
+ suspend_nvs_free();
+
+ if (acpi_state == ACPI_STATE_S0)
+ return;
+
+ pr_info("Waking up from system sleep state S%d\n", acpi_state);
+ acpi_disable_wakeup_devices(acpi_state);
+ acpi_leave_sleep_state(acpi_state);
+
+ /* reset firmware waking vector */
+ acpi_set_waking_vector(0);
+
+ acpi_target_sleep_state = ACPI_STATE_S0;
+
+ acpi_resume_power_resources();
+
+ /* If we were woken with the fixed power button, provide a small
+ * hint to userspace in the form of a wakeup event on the fixed power
+ * button device (if it can be found).
+ *
+ * We delay the event generation til now, as the PM layer requires
+ * timekeeping to be running before we generate events. */
+ if (!pwr_btn_event_pending)
+ return;
+
+ pwr_btn_event_pending = false;
+ pwr_btn_adev = acpi_dev_get_first_match_dev(ACPI_BUTTON_HID_POWERF,
+ NULL, -1);
+ if (pwr_btn_adev) {
+ pm_wakeup_event(&pwr_btn_adev->dev, 0);
+ acpi_dev_put(pwr_btn_adev);
+ }
+}
+
+/**
+ * acpi_pm_start - Start system PM transition.
+ */
+static void acpi_pm_start(u32 acpi_state)
+{
+ acpi_target_sleep_state = acpi_state;
+ acpi_sleep_tts_switch(acpi_target_sleep_state);
+ acpi_scan_lock_acquire();
+}
+
+/**
+ * acpi_pm_end - Finish up system PM transition.
+ */
+static void acpi_pm_end(void)
+{
+ acpi_turn_off_unused_power_resources();
+ acpi_scan_lock_release();
+ /*
+ * This is necessary in case acpi_pm_finish() is not called during a
+ * failing transition to a sleep state.
+ */
+ acpi_target_sleep_state = ACPI_STATE_S0;
+ acpi_sleep_tts_switch(acpi_target_sleep_state);
+}
+#else /* !CONFIG_ACPI_SLEEP */
+#define sleep_no_lps0 (1)
+#define acpi_target_sleep_state ACPI_STATE_S0
+#define acpi_sleep_default_s3 (1)
+static inline void acpi_sleep_dmi_check(void) {}
+#endif /* CONFIG_ACPI_SLEEP */
+
+#ifdef CONFIG_SUSPEND
+static u32 acpi_suspend_states[] = {
+ [PM_SUSPEND_ON] = ACPI_STATE_S0,
+ [PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
+ [PM_SUSPEND_MEM] = ACPI_STATE_S3,
+ [PM_SUSPEND_MAX] = ACPI_STATE_S5
+};
+
+/**
+ * acpi_suspend_begin - Set the target system sleep state to the state
+ * associated with given @pm_state, if supported.
+ */
+static int acpi_suspend_begin(suspend_state_t pm_state)
+{
+ u32 acpi_state = acpi_suspend_states[pm_state];
+ int error;
+
+ error = (nvs_nosave || nvs_nosave_s3) ? 0 : suspend_nvs_alloc();
+ if (error)
+ return error;
+
+ if (!sleep_states[acpi_state]) {
+ pr_err("ACPI does not support sleep state S%u\n", acpi_state);
+ return -ENOSYS;
+ }
+ if (acpi_state > ACPI_STATE_S1)
+ pm_set_suspend_via_firmware();
+
+ acpi_pm_start(acpi_state);
+ return 0;
+}
+
+/**
+ * acpi_suspend_enter - Actually enter a sleep state.
+ * @pm_state: ignored
+ *
+ * Flush caches and go to sleep. For STR we have to call arch-specific
+ * assembly, which in turn call acpi_enter_sleep_state().
+ * It's unfortunate, but it works. Please fix if you're feeling frisky.
+ */
+static int acpi_suspend_enter(suspend_state_t pm_state)
+{
+ acpi_status status = AE_OK;
+ u32 acpi_state = acpi_target_sleep_state;
+ int error;
+
+ trace_suspend_resume(TPS("acpi_suspend"), acpi_state, true);
+ switch (acpi_state) {
+ case ACPI_STATE_S1:
+ barrier();
+ status = acpi_enter_sleep_state(acpi_state);
+ break;
+
+ case ACPI_STATE_S3:
+ if (!acpi_suspend_lowlevel)
+ return -ENOSYS;
+ error = acpi_suspend_lowlevel();
+ if (error)
+ return error;
+ pr_info("Low-level resume complete\n");
+ pm_set_resume_via_firmware();
+ break;
+ }
+ trace_suspend_resume(TPS("acpi_suspend"), acpi_state, false);
+
+ /* This violates the spec but is required for bug compatibility. */
+ acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
+
+ /* Reprogram control registers */
+ acpi_leave_sleep_state_prep(acpi_state);
+
+ /* ACPI 3.0 specs (P62) says that it's the responsibility
+ * of the OSPM to clear the status bit [ implying that the
+ * POWER_BUTTON event should not reach userspace ]
+ *
+ * However, we do generate a small hint for userspace in the form of
+ * a wakeup event. We flag this condition for now and generate the
+ * event later, as we're currently too early in resume to be able to
+ * generate wakeup events.
+ */
+ if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) {
+ acpi_event_status pwr_btn_status = ACPI_EVENT_FLAG_DISABLED;
+
+ acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);
+
+ if (pwr_btn_status & ACPI_EVENT_FLAG_STATUS_SET) {
+ acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
+ /* Flag for later */
+ pwr_btn_event_pending = true;
+ }
+ }
+
+ /*
+ * 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 interrups.
+ *
+ * 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_hw_disable_all_gpes();
+ /* Allow EC transactions to happen. */
+ acpi_ec_unblock_transactions();
+
+ suspend_nvs_restore();
+
+ return ACPI_SUCCESS(status) ? 0 : -EFAULT;
+}
+
+static int acpi_suspend_state_valid(suspend_state_t pm_state)
+{
+ u32 acpi_state;
+
+ switch (pm_state) {
+ case PM_SUSPEND_ON:
+ case PM_SUSPEND_STANDBY:
+ case PM_SUSPEND_MEM:
+ acpi_state = acpi_suspend_states[pm_state];
+
+ return sleep_states[acpi_state];
+ default:
+ return 0;
+ }
+}
+
+static const struct platform_suspend_ops acpi_suspend_ops = {
+ .valid = acpi_suspend_state_valid,
+ .begin = acpi_suspend_begin,
+ .prepare_late = acpi_pm_prepare,
+ .enter = acpi_suspend_enter,
+ .wake = acpi_pm_finish,
+ .end = acpi_pm_end,
+};
+
+/**
+ * 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.
+ */
+static int acpi_suspend_begin_old(suspend_state_t pm_state)
+{
+ int error = acpi_suspend_begin(pm_state);
+ if (!error)
+ error = __acpi_pm_prepare();
+
+ return error;
+}
+
+/*
+ * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
+ * been requested.
+ */
+static const struct platform_suspend_ops acpi_suspend_ops_old = {
+ .valid = acpi_suspend_state_valid,
+ .begin = acpi_suspend_begin_old,
+ .prepare_late = acpi_pm_pre_suspend,
+ .enter = acpi_suspend_enter,
+ .wake = acpi_pm_finish,
+ .end = acpi_pm_end,
+ .recover = acpi_pm_finish,
+};
+
+static bool s2idle_wakeup;
+
+int acpi_s2idle_begin(void)
+{
+ acpi_scan_lock_acquire();
+ return 0;
+}
+
+int acpi_s2idle_prepare(void)
+{
+ if (acpi_sci_irq_valid()) {
+ enable_irq_wake(acpi_sci_irq);
+ acpi_ec_set_gpe_wake_mask(ACPI_GPE_ENABLE);
+ }
+
+ acpi_enable_wakeup_devices(ACPI_STATE_S0);
+
+ /* Change the configuration of GPEs to avoid spurious wakeup. */
+ acpi_enable_all_wakeup_gpes();
+ acpi_os_wait_events_complete();
+
+ s2idle_wakeup = true;
+ return 0;
+}
+
+bool acpi_s2idle_wake(void)
+{
+ if (!acpi_sci_irq_valid())
+ return pm_wakeup_pending();
+
+ while (pm_wakeup_pending()) {
+ /*
+ * If IRQD_WAKEUP_ARMED is set for the SCI at this point, the
+ * SCI has not triggered while suspended, so bail out (the
+ * wakeup is pending anyway and the SCI is not the source of
+ * it).
+ */
+ if (irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
+ pm_pr_dbg("Wakeup unrelated to ACPI SCI\n");
+ return true;
+ }
+
+ /*
+ * If the status bit of any enabled fixed event is set, the
+ * wakeup is regarded as valid.
+ */
+ if (acpi_any_fixed_event_status_set()) {
+ pm_pr_dbg("ACPI fixed event wakeup\n");
+ return true;
+ }
+
+ /* Check wakeups from drivers sharing the SCI. */
+ if (acpi_check_wakeup_handlers()) {
+ pm_pr_dbg("ACPI custom handler wakeup\n");
+ return true;
+ }
+
+ /*
+ * Check non-EC GPE wakeups and if there are none, cancel the
+ * SCI-related wakeup and dispatch the EC GPE.
+ */
+ if (acpi_ec_dispatch_gpe()) {
+ pm_pr_dbg("ACPI non-EC GPE wakeup\n");
+ return true;
+ }
+
+ acpi_os_wait_events_complete();
+
+ /*
+ * The SCI is in the "suspended" state now and it cannot produce
+ * new wakeup events till the rearming below, so if any of them
+ * are pending here, they must be resulting from the processing
+ * of EC events above or coming from somewhere else.
+ */
+ if (pm_wakeup_pending()) {
+ pm_pr_dbg("Wakeup after ACPI Notify sync\n");
+ return true;
+ }
+
+ pm_pr_dbg("Rearming ACPI SCI for wakeup\n");
+
+ pm_wakeup_clear(acpi_sci_irq);
+ rearm_wake_irq(acpi_sci_irq);
+ }
+
+ return false;
+}
+
+void acpi_s2idle_restore(void)
+{
+ /*
+ * Drain pending events before restoring the working-state configuration
+ * of GPEs.
+ */
+ acpi_os_wait_events_complete(); /* synchronize GPE processing */
+ acpi_ec_flush_work(); /* flush the EC driver's workqueues */
+ acpi_os_wait_events_complete(); /* synchronize Notify handling */
+
+ s2idle_wakeup = false;
+
+ acpi_enable_all_runtime_gpes();
+
+ acpi_disable_wakeup_devices(ACPI_STATE_S0);
+
+ if (acpi_sci_irq_valid()) {
+ acpi_ec_set_gpe_wake_mask(ACPI_GPE_DISABLE);
+ disable_irq_wake(acpi_sci_irq);
+ }
+}
+
+void acpi_s2idle_end(void)
+{
+ acpi_scan_lock_release();
+}
+
+static const struct platform_s2idle_ops acpi_s2idle_ops = {
+ .begin = acpi_s2idle_begin,
+ .prepare = acpi_s2idle_prepare,
+ .wake = acpi_s2idle_wake,
+ .restore = acpi_s2idle_restore,
+ .end = acpi_s2idle_end,
+};
+
+void __weak acpi_s2idle_setup(void)
+{
+ if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)
+ pr_info("Efficient low-power S0 idle declared\n");
+
+ s2idle_set_ops(&acpi_s2idle_ops);
+}
+
+static void acpi_sleep_suspend_setup(void)
+{
+ bool suspend_ops_needed = false;
+ int i;
+
+ for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++)
+ if (acpi_sleep_state_supported(i)) {
+ sleep_states[i] = 1;
+ suspend_ops_needed = true;
+ }
+
+ if (suspend_ops_needed)
+ suspend_set_ops(old_suspend_ordering ?
+ &acpi_suspend_ops_old : &acpi_suspend_ops);
+
+ acpi_s2idle_setup();
+}
+
+#else /* !CONFIG_SUSPEND */
+#define s2idle_wakeup (false)
+static inline void acpi_sleep_suspend_setup(void) {}
+#endif /* !CONFIG_SUSPEND */
+
+bool acpi_s2idle_wakeup(void)
+{
+ return s2idle_wakeup;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static u32 saved_bm_rld;
+
+static int acpi_save_bm_rld(void)
+{
+ acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &saved_bm_rld);
+ return 0;
+}
+
+static void acpi_restore_bm_rld(void)
+{
+ u32 resumed_bm_rld = 0;
+
+ acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &resumed_bm_rld);
+ if (resumed_bm_rld == saved_bm_rld)
+ return;
+
+ acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld);
+}
+
+static struct syscore_ops acpi_sleep_syscore_ops = {
+ .suspend = acpi_save_bm_rld,
+ .resume = acpi_restore_bm_rld,
+};
+
+static void acpi_sleep_syscore_init(void)
+{
+ register_syscore_ops(&acpi_sleep_syscore_ops);
+}
+#else
+static inline void acpi_sleep_syscore_init(void) {}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_HIBERNATION
+static unsigned long s4_hardware_signature;
+static struct acpi_table_facs *facs;
+int acpi_check_s4_hw_signature = -1; /* Default behaviour is just to warn */
+
+static int acpi_hibernation_begin(pm_message_t stage)
+{
+ if (!nvs_nosave) {
+ int error = suspend_nvs_alloc();
+ if (error)
+ return error;
+ }
+
+ if (stage.event == PM_EVENT_HIBERNATE)
+ pm_set_suspend_via_firmware();
+
+ acpi_pm_start(ACPI_STATE_S4);
+ return 0;
+}
+
+static int acpi_hibernation_enter(void)
+{
+ acpi_status status = AE_OK;
+
+ /* This shouldn't return. If it returns, we have a problem */
+ status = acpi_enter_sleep_state(ACPI_STATE_S4);
+ /* Reprogram control registers */
+ acpi_leave_sleep_state_prep(ACPI_STATE_S4);
+
+ return ACPI_SUCCESS(status) ? 0 : -EFAULT;
+}
+
+static void acpi_hibernation_leave(void)
+{
+ pm_set_resume_via_firmware();
+ /*
+ * If ACPI is not enabled by the BIOS and the boot kernel, we need to
+ * enable it here.
+ */
+ acpi_enable();
+ /* Reprogram control registers */
+ acpi_leave_sleep_state_prep(ACPI_STATE_S4);
+ /* Check the hardware signature */
+ if (facs && s4_hardware_signature != facs->hardware_signature)
+ pr_crit("Hardware changed while hibernated, success doubtful!\n");
+ /* Restore the NVS memory area */
+ suspend_nvs_restore();
+ /* Allow EC transactions to happen. */
+ acpi_ec_unblock_transactions();
+}
+
+static void acpi_pm_thaw(void)
+{
+ acpi_ec_unblock_transactions();
+ acpi_enable_all_runtime_gpes();
+}
+
+static const struct platform_hibernation_ops acpi_hibernation_ops = {
+ .begin = acpi_hibernation_begin,
+ .end = acpi_pm_end,
+ .pre_snapshot = acpi_pm_prepare,
+ .finish = acpi_pm_finish,
+ .prepare = acpi_pm_prepare,
+ .enter = acpi_hibernation_enter,
+ .leave = acpi_hibernation_leave,
+ .pre_restore = acpi_pm_freeze,
+ .restore_cleanup = acpi_pm_thaw,
+};
+
+/**
+ * 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.
+ */
+static int acpi_hibernation_begin_old(pm_message_t stage)
+{
+ int error;
+ /*
+ * The _TTS object should always be evaluated before the _PTS object.
+ * When the old_suspended_ordering is true, the _PTS object is
+ * evaluated in the acpi_sleep_prepare.
+ */
+ acpi_sleep_tts_switch(ACPI_STATE_S4);
+
+ error = acpi_sleep_prepare(ACPI_STATE_S4);
+ if (error)
+ return error;
+
+ if (!nvs_nosave) {
+ error = suspend_nvs_alloc();
+ if (error)
+ return error;
+ }
+
+ if (stage.event == PM_EVENT_HIBERNATE)
+ pm_set_suspend_via_firmware();
+
+ acpi_target_sleep_state = ACPI_STATE_S4;
+ acpi_scan_lock_acquire();
+ return 0;
+}
+
+/*
+ * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
+ * been requested.
+ */
+static const struct platform_hibernation_ops acpi_hibernation_ops_old = {
+ .begin = acpi_hibernation_begin_old,
+ .end = acpi_pm_end,
+ .pre_snapshot = acpi_pm_pre_suspend,
+ .prepare = acpi_pm_freeze,
+ .finish = acpi_pm_finish,
+ .enter = acpi_hibernation_enter,
+ .leave = acpi_hibernation_leave,
+ .pre_restore = acpi_pm_freeze,
+ .restore_cleanup = acpi_pm_thaw,
+ .recover = acpi_pm_finish,
+};
+
+static void acpi_sleep_hibernate_setup(void)
+{
+ if (!acpi_sleep_state_supported(ACPI_STATE_S4))
+ return;
+
+ hibernation_set_ops(old_suspend_ordering ?
+ &acpi_hibernation_ops_old : &acpi_hibernation_ops);
+ sleep_states[ACPI_STATE_S4] = 1;
+ if (!acpi_check_s4_hw_signature)
+ return;
+
+ acpi_get_table(ACPI_SIG_FACS, 1, (struct acpi_table_header **)&facs);
+ if (facs) {
+ /*
+ * s4_hardware_signature is the local variable which is just
+ * used to warn about mismatch after we're attempting to
+ * resume (in violation of the ACPI specification.)
+ */
+ s4_hardware_signature = facs->hardware_signature;
+
+ if (acpi_check_s4_hw_signature > 0) {
+ /*
+ * If we're actually obeying the ACPI specification
+ * then the signature is written out as part of the
+ * swsusp header, in order to allow the boot kernel
+ * to gracefully decline to resume.
+ */
+ swsusp_hardware_signature = facs->hardware_signature;
+ }
+ }
+}
+#else /* !CONFIG_HIBERNATION */
+static inline void acpi_sleep_hibernate_setup(void) {}
+#endif /* !CONFIG_HIBERNATION */
+
+static int acpi_power_off_prepare(struct sys_off_data *data)
+{
+ /* Prepare to power off the system */
+ acpi_sleep_prepare(ACPI_STATE_S5);
+ acpi_disable_all_gpes();
+ acpi_os_wait_events_complete();
+ return NOTIFY_DONE;
+}
+
+static int acpi_power_off(struct sys_off_data *data)
+{
+ /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */
+ pr_debug("%s called\n", __func__);
+ local_irq_disable();
+ acpi_enter_sleep_state(ACPI_STATE_S5);
+ return NOTIFY_DONE;
+}
+
+int __init acpi_sleep_init(void)
+{
+ char supported[ACPI_S_STATE_COUNT * 3 + 1];
+ char *pos = supported;
+ int i;
+
+ acpi_sleep_dmi_check();
+
+ sleep_states[ACPI_STATE_S0] = 1;
+
+ acpi_sleep_syscore_init();
+ acpi_sleep_suspend_setup();
+ acpi_sleep_hibernate_setup();
+
+ if (acpi_sleep_state_supported(ACPI_STATE_S5)) {
+ sleep_states[ACPI_STATE_S5] = 1;
+
+ register_sys_off_handler(SYS_OFF_MODE_POWER_OFF_PREPARE,
+ SYS_OFF_PRIO_FIRMWARE,
+ acpi_power_off_prepare, NULL);
+
+ 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;
+ }
+
+ supported[0] = 0;
+ for (i = 0; i < ACPI_S_STATE_COUNT; i++) {
+ if (sleep_states[i])
+ pos += sprintf(pos, " S%d", i);
+ }
+ pr_info("(supports%s)\n", supported);
+
+ /*
+ * Register the tts_notifier to reboot notifier list so that the _TTS
+ * object can also be evaluated when the system enters S5.
+ */
+ register_reboot_notifier(&tts_notifier);
+ return 0;
+}
diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h
new file mode 100644
index 000000000..d960a238b
--- /dev/null
+++ b/drivers/acpi/sleep.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+extern void acpi_enable_wakeup_devices(u8 sleep_state);
+extern void acpi_disable_wakeup_devices(u8 sleep_state);
+extern bool acpi_check_wakeup_handlers(void);
+
+extern struct list_head acpi_wakeup_device_list;
+extern struct mutex acpi_device_lock;
+
+extern void acpi_resume_power_resources(void);
+
+static inline acpi_status acpi_set_waking_vector(u32 wakeup_address)
+{
+ return acpi_set_firmware_waking_vector(
+ (acpi_physical_address)wakeup_address, 0);
+}
+
+extern int acpi_s2idle_begin(void);
+extern int acpi_s2idle_prepare(void);
+extern int acpi_s2idle_prepare_late(void);
+extern void acpi_s2idle_check(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);
+
+extern void acpi_s2idle_setup(void);
+
+#ifdef CONFIG_ACPI_SLEEP
+extern bool acpi_sleep_default_s3;
+#else
+#define acpi_sleep_default_s3 (1)
+#endif
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c
new file mode 100644
index 000000000..1eabfcd12
--- /dev/null
+++ b/drivers/acpi/spcr.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, Intel Corporation
+ * Copyright (c) 2015, Red Hat, Inc.
+ * Copyright (c) 2015, 2016 Linaro Ltd.
+ */
+
+#define pr_fmt(fmt) "ACPI: SPCR: " fmt
+
+#include <linux/acpi.h>
+#include <linux/console.h>
+#include <linux/kernel.h>
+#include <linux/serial_core.h>
+
+/*
+ * Erratum 44 for QDF2432v1 and QDF2400v1 SoCs describes the BUSY bit as
+ * occasionally getting stuck as 1. To avoid the potential for a hang, check
+ * TXFE == 0 instead of BUSY == 1. This may not be suitable for all UART
+ * implementations, so only do so if an affected platform is detected in
+ * acpi_parse_spcr().
+ */
+bool qdf2400_e44_present;
+EXPORT_SYMBOL(qdf2400_e44_present);
+
+/*
+ * Some Qualcomm Datacenter Technologies SoCs have a defective UART BUSY bit.
+ * Detect them by examining the OEM fields in the SPCR header, similar to PCI
+ * quirk detection in pci_mcfg.c.
+ */
+static bool qdf2400_erratum_44_present(struct acpi_table_header *h)
+{
+ if (memcmp(h->oem_id, "QCOM ", ACPI_OEM_ID_SIZE))
+ return false;
+
+ if (!memcmp(h->oem_table_id, "QDF2432 ", ACPI_OEM_TABLE_ID_SIZE))
+ return true;
+
+ if (!memcmp(h->oem_table_id, "QDF2400 ", ACPI_OEM_TABLE_ID_SIZE) &&
+ h->oem_revision == 1)
+ return true;
+
+ return false;
+}
+
+/*
+ * APM X-Gene v1 and v2 UART hardware is an 16550 like device but has its
+ * register aligned to 32-bit. In addition, the BIOS also encoded the
+ * access width to be 8 bits. This function detects this errata condition.
+ */
+static bool xgene_8250_erratum_present(struct acpi_table_spcr *tb)
+{
+ bool xgene_8250 = false;
+
+ if (tb->interface_type != ACPI_DBG2_16550_COMPATIBLE)
+ return false;
+
+ if (memcmp(tb->header.oem_id, "APMC0D", ACPI_OEM_ID_SIZE) &&
+ memcmp(tb->header.oem_id, "HPE ", ACPI_OEM_ID_SIZE))
+ return false;
+
+ if (!memcmp(tb->header.oem_table_id, "XGENESPC",
+ ACPI_OEM_TABLE_ID_SIZE) && tb->header.oem_revision == 0)
+ xgene_8250 = true;
+
+ if (!memcmp(tb->header.oem_table_id, "ProLiant",
+ ACPI_OEM_TABLE_ID_SIZE) && tb->header.oem_revision == 1)
+ xgene_8250 = true;
+
+ return xgene_8250;
+}
+
+/**
+ * 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.
+ *
+ * For the architectures with support for ACPI, CONFIG_ACPI_SPCR_TABLE may be
+ * defined to parse ACPI SPCR table. As a result of the parsing preferred
+ * console is registered and if @enable_earlycon is true, earlycon is set up.
+ * If @enable_console is true the system console is also configured.
+ *
+ * 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)
+{
+ static char opts[64];
+ struct acpi_table_spcr *table;
+ acpi_status status;
+ char *uart;
+ char *iotype;
+ int baud_rate;
+ int err;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ status = acpi_get_table(ACPI_SIG_SPCR, 0,
+ (struct acpi_table_header **)&table);
+
+ if (ACPI_FAILURE(status))
+ return -ENOENT;
+
+ if (table->header.revision < 2)
+ pr_info("SPCR table version %d\n", table->header.revision);
+
+ if (table->serial_port.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ 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");
+ 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");
+ fallthrough;
+ case 8:
+ iotype = "mmio";
+ break;
+ case 16:
+ iotype = "mmio16";
+ break;
+ case 32:
+ iotype = "mmio32";
+ break;
+ }
+ } else
+ iotype = "io";
+
+ switch (table->interface_type) {
+ case ACPI_DBG2_ARM_SBSA_32BIT:
+ iotype = "mmio32";
+ fallthrough;
+ case ACPI_DBG2_ARM_PL011:
+ case ACPI_DBG2_ARM_SBSA_GENERIC:
+ case ACPI_DBG2_BCM2835:
+ uart = "pl011";
+ break;
+ case ACPI_DBG2_16550_COMPATIBLE:
+ case ACPI_DBG2_16550_SUBSET:
+ case ACPI_DBG2_16550_WITH_GAS:
+ case ACPI_DBG2_16550_NVIDIA:
+ uart = "uart";
+ break;
+ default:
+ err = -ENOENT;
+ goto done;
+ }
+
+ switch (table->baud_rate) {
+ case 0:
+ /*
+ * SPCR 1.04 defines 0 as a preconfigured state of UART.
+ * Assume firmware or bootloader configures console correctly.
+ */
+ baud_rate = 0;
+ break;
+ case 3:
+ baud_rate = 9600;
+ break;
+ case 4:
+ baud_rate = 19200;
+ break;
+ case 6:
+ baud_rate = 57600;
+ break;
+ case 7:
+ baud_rate = 115200;
+ break;
+ default:
+ err = -ENOENT;
+ goto done;
+ }
+
+ /*
+ * If the E44 erratum is required, then we need to tell the pl011
+ * driver to implement the work-around.
+ *
+ * The global variable is used by the probe function when it
+ * creates the UARTs, whether or not they're used as a console.
+ *
+ * If the user specifies "traditional" earlycon, the qdf2400_e44
+ * console name matches the EARLYCON_DECLARE() statement, and
+ * SPCR is not used. Parameter "earlycon" is false.
+ *
+ * If the user specifies "SPCR" earlycon, then we need to update
+ * the console name so that it also says "qdf2400_e44". Parameter
+ * "earlycon" is true.
+ *
+ * For consistency, if we change the console name, then we do it
+ * for everyone, not just earlycon.
+ */
+ if (qdf2400_erratum_44_present(&table->header)) {
+ qdf2400_e44_present = true;
+ if (enable_earlycon)
+ uart = "qdf2400_e44";
+ }
+
+ if (xgene_8250_erratum_present(table)) {
+ iotype = "mmio32";
+
+ /* 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
+ */
+ baud_rate = 0;
+ }
+
+ if (!baud_rate) {
+ snprintf(opts, sizeof(opts), "%s,%s,0x%llx", uart, iotype,
+ table->serial_port.address);
+ } else {
+ snprintf(opts, sizeof(opts), "%s,%s,0x%llx,%d", uart, iotype,
+ table->serial_port.address, baud_rate);
+ }
+
+ pr_info("console: %s\n", opts);
+
+ if (enable_earlycon)
+ setup_earlycon(opts);
+
+ if (enable_console)
+ err = add_preferred_console(uart, 0, opts + strlen(uart) + 1);
+ else
+ err = 0;
+done:
+ acpi_put_table((struct acpi_table_header *)table);
+ return err;
+}
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
new file mode 100644
index 000000000..cc2fe0618
--- /dev/null
+++ b/drivers/acpi/sysfs.c
@@ -0,0 +1,1026 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sysfs.c - ACPI sysfs interface to userspace.
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/acpi.h>
+#include <linux/bitmap.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+
+#include "internal.h"
+
+#ifdef CONFIG_ACPI_DEBUG
+/*
+ * ACPI debug sysfs I/F, including:
+ * /sys/modules/acpi/parameters/debug_layer
+ * /sys/modules/acpi/parameters/debug_level
+ * /sys/modules/acpi/parameters/trace_method_name
+ * /sys/modules/acpi/parameters/trace_state
+ * /sys/modules/acpi/parameters/trace_debug_layer
+ * /sys/modules/acpi/parameters/trace_debug_level
+ */
+
+struct acpi_dlayer {
+ const char *name;
+ unsigned long value;
+};
+struct acpi_dlevel {
+ const char *name;
+ unsigned long value;
+};
+#define ACPI_DEBUG_INIT(v) { .name = #v, .value = v }
+
+static const struct acpi_dlayer acpi_debug_layers[] = {
+ ACPI_DEBUG_INIT(ACPI_UTILITIES),
+ ACPI_DEBUG_INIT(ACPI_HARDWARE),
+ ACPI_DEBUG_INIT(ACPI_EVENTS),
+ ACPI_DEBUG_INIT(ACPI_TABLES),
+ ACPI_DEBUG_INIT(ACPI_NAMESPACE),
+ ACPI_DEBUG_INIT(ACPI_PARSER),
+ ACPI_DEBUG_INIT(ACPI_DISPATCHER),
+ ACPI_DEBUG_INIT(ACPI_EXECUTER),
+ ACPI_DEBUG_INIT(ACPI_RESOURCES),
+ ACPI_DEBUG_INIT(ACPI_CA_DEBUGGER),
+ ACPI_DEBUG_INIT(ACPI_OS_SERVICES),
+ ACPI_DEBUG_INIT(ACPI_CA_DISASSEMBLER),
+ ACPI_DEBUG_INIT(ACPI_COMPILER),
+ ACPI_DEBUG_INIT(ACPI_TOOLS),
+};
+
+static const struct acpi_dlevel acpi_debug_levels[] = {
+ ACPI_DEBUG_INIT(ACPI_LV_INIT),
+ ACPI_DEBUG_INIT(ACPI_LV_DEBUG_OBJECT),
+ ACPI_DEBUG_INIT(ACPI_LV_INFO),
+ ACPI_DEBUG_INIT(ACPI_LV_REPAIR),
+ ACPI_DEBUG_INIT(ACPI_LV_TRACE_POINT),
+
+ ACPI_DEBUG_INIT(ACPI_LV_INIT_NAMES),
+ ACPI_DEBUG_INIT(ACPI_LV_PARSE),
+ ACPI_DEBUG_INIT(ACPI_LV_LOAD),
+ ACPI_DEBUG_INIT(ACPI_LV_DISPATCH),
+ ACPI_DEBUG_INIT(ACPI_LV_EXEC),
+ ACPI_DEBUG_INIT(ACPI_LV_NAMES),
+ ACPI_DEBUG_INIT(ACPI_LV_OPREGION),
+ ACPI_DEBUG_INIT(ACPI_LV_BFIELD),
+ ACPI_DEBUG_INIT(ACPI_LV_TABLES),
+ ACPI_DEBUG_INIT(ACPI_LV_VALUES),
+ ACPI_DEBUG_INIT(ACPI_LV_OBJECTS),
+ ACPI_DEBUG_INIT(ACPI_LV_RESOURCES),
+ ACPI_DEBUG_INIT(ACPI_LV_USER_REQUESTS),
+ ACPI_DEBUG_INIT(ACPI_LV_PACKAGE),
+
+ ACPI_DEBUG_INIT(ACPI_LV_ALLOCATIONS),
+ ACPI_DEBUG_INIT(ACPI_LV_FUNCTIONS),
+ ACPI_DEBUG_INIT(ACPI_LV_OPTIMIZATIONS),
+
+ ACPI_DEBUG_INIT(ACPI_LV_MUTEX),
+ ACPI_DEBUG_INIT(ACPI_LV_THREADS),
+ ACPI_DEBUG_INIT(ACPI_LV_IO),
+ ACPI_DEBUG_INIT(ACPI_LV_INTERRUPTS),
+
+ ACPI_DEBUG_INIT(ACPI_LV_AML_DISASSEMBLE),
+ ACPI_DEBUG_INIT(ACPI_LV_VERBOSE_INFO),
+ ACPI_DEBUG_INIT(ACPI_LV_FULL_TABLES),
+ ACPI_DEBUG_INIT(ACPI_LV_EVENTS),
+};
+
+static int param_get_debug_layer(char *buffer, const struct kernel_param *kp)
+{
+ int result = 0;
+ int i;
+
+ result = sprintf(buffer, "%-25s\tHex SET\n", "Description");
+
+ for (i = 0; i < ARRAY_SIZE(acpi_debug_layers); i++) {
+ result += sprintf(buffer + result, "%-25s\t0x%08lX [%c]\n",
+ acpi_debug_layers[i].name,
+ acpi_debug_layers[i].value,
+ (acpi_dbg_layer & acpi_debug_layers[i].value)
+ ? '*' : ' ');
+ }
+ result +=
+ sprintf(buffer + result, "%-25s\t0x%08X [%c]\n", "ACPI_ALL_DRIVERS",
+ ACPI_ALL_DRIVERS,
+ (acpi_dbg_layer & ACPI_ALL_DRIVERS) ==
+ ACPI_ALL_DRIVERS ? '*' : (acpi_dbg_layer & ACPI_ALL_DRIVERS)
+ == 0 ? ' ' : '-');
+ result +=
+ sprintf(buffer + result,
+ "--\ndebug_layer = 0x%08X ( * = enabled)\n",
+ acpi_dbg_layer);
+
+ return result;
+}
+
+static int param_get_debug_level(char *buffer, const struct kernel_param *kp)
+{
+ int result = 0;
+ int i;
+
+ result = sprintf(buffer, "%-25s\tHex SET\n", "Description");
+
+ for (i = 0; i < ARRAY_SIZE(acpi_debug_levels); i++) {
+ result += sprintf(buffer + result, "%-25s\t0x%08lX [%c]\n",
+ acpi_debug_levels[i].name,
+ acpi_debug_levels[i].value,
+ (acpi_dbg_level & acpi_debug_levels[i].value)
+ ? '*' : ' ');
+ }
+ result +=
+ sprintf(buffer + result, "--\ndebug_level = 0x%08X (* = enabled)\n",
+ acpi_dbg_level);
+
+ return result;
+}
+
+static const struct kernel_param_ops param_ops_debug_layer = {
+ .set = param_set_uint,
+ .get = param_get_debug_layer,
+};
+
+static const struct kernel_param_ops param_ops_debug_level = {
+ .set = param_set_uint,
+ .get = param_get_debug_level,
+};
+
+module_param_cb(debug_layer, &param_ops_debug_layer, &acpi_dbg_layer, 0644);
+module_param_cb(debug_level, &param_ops_debug_level, &acpi_dbg_level, 0644);
+
+static char trace_method_name[1024];
+
+static int param_set_trace_method_name(const char *val,
+ const struct kernel_param *kp)
+{
+ u32 saved_flags = 0;
+ bool is_abs_path = true;
+
+ if (*val != '\\')
+ is_abs_path = false;
+
+ if ((is_abs_path && strlen(val) > 1023) ||
+ (!is_abs_path && strlen(val) > 1022)) {
+ pr_err("%s: string parameter too long\n", kp->name);
+ return -ENOSPC;
+ }
+
+ /*
+ * It's not safe to update acpi_gbl_trace_method_name without
+ * having the tracer stopped, so we save the original tracer
+ * state and disable it.
+ */
+ saved_flags = acpi_gbl_trace_flags;
+ (void)acpi_debug_trace(NULL,
+ acpi_gbl_trace_dbg_level,
+ acpi_gbl_trace_dbg_layer,
+ 0);
+
+ /* This is a hack. We can't kmalloc in early boot. */
+ if (is_abs_path)
+ strcpy(trace_method_name, val);
+ else {
+ trace_method_name[0] = '\\';
+ strcpy(trace_method_name+1, val);
+ }
+
+ /* Restore the original tracer state */
+ (void)acpi_debug_trace(trace_method_name,
+ acpi_gbl_trace_dbg_level,
+ acpi_gbl_trace_dbg_layer,
+ saved_flags);
+
+ return 0;
+}
+
+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);
+}
+
+static const struct kernel_param_ops param_ops_trace_method = {
+ .set = param_set_trace_method_name,
+ .get = param_get_trace_method_name,
+};
+
+static const struct kernel_param_ops param_ops_trace_attrib = {
+ .set = param_set_uint,
+ .get = param_get_uint,
+};
+
+module_param_cb(trace_method_name, &param_ops_trace_method, &trace_method_name, 0644);
+module_param_cb(trace_debug_layer, &param_ops_trace_attrib, &acpi_gbl_trace_dbg_layer, 0644);
+module_param_cb(trace_debug_level, &param_ops_trace_attrib, &acpi_gbl_trace_dbg_level, 0644);
+
+static int param_set_trace_state(const char *val,
+ const struct kernel_param *kp)
+{
+ acpi_status status;
+ const char *method = trace_method_name;
+ u32 flags = 0;
+
+/* So "xxx-once" comparison should go prior than "xxx" comparison */
+#define acpi_compare_param(val, key) \
+ strncmp((val), (key), sizeof(key) - 1)
+
+ if (!acpi_compare_param(val, "enable")) {
+ method = NULL;
+ flags = ACPI_TRACE_ENABLED;
+ } else if (!acpi_compare_param(val, "disable"))
+ method = NULL;
+ else if (!acpi_compare_param(val, "method-once"))
+ flags = ACPI_TRACE_ENABLED | ACPI_TRACE_ONESHOT;
+ else if (!acpi_compare_param(val, "method"))
+ flags = ACPI_TRACE_ENABLED;
+ else if (!acpi_compare_param(val, "opcode-once"))
+ flags = ACPI_TRACE_ENABLED | ACPI_TRACE_ONESHOT | ACPI_TRACE_OPCODE;
+ else if (!acpi_compare_param(val, "opcode"))
+ flags = ACPI_TRACE_ENABLED | ACPI_TRACE_OPCODE;
+ else
+ return -EINVAL;
+
+ status = acpi_debug_trace(method,
+ acpi_gbl_trace_dbg_level,
+ acpi_gbl_trace_dbg_layer,
+ flags);
+ if (ACPI_FAILURE(status))
+ return -EBUSY;
+
+ return 0;
+}
+
+static int param_get_trace_state(char *buffer, const struct kernel_param *kp)
+{
+ if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED))
+ return sprintf(buffer, "disable\n");
+ if (!acpi_gbl_trace_method_name)
+ return sprintf(buffer, "enable\n");
+ if (acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT)
+ return sprintf(buffer, "method-once\n");
+ else
+ return sprintf(buffer, "method\n");
+}
+
+module_param_call(trace_state, param_set_trace_state, param_get_trace_state,
+ NULL, 0644);
+#endif /* CONFIG_ACPI_DEBUG */
+
+
+/* /sys/modules/acpi/parameters/aml_debug_output */
+
+module_param_named(aml_debug_output, acpi_gbl_enable_aml_debug_object,
+ byte, 0644);
+MODULE_PARM_DESC(aml_debug_output,
+ "To enable/disable the ACPI Debug Object output.");
+
+/* /sys/module/acpi/parameters/acpica_version */
+static int param_get_acpica_version(char *buffer,
+ const struct kernel_param *kp)
+{
+ int result;
+
+ result = sprintf(buffer, "%x\n", ACPI_CA_VERSION);
+
+ return result;
+}
+
+module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444);
+
+/*
+ * ACPI table sysfs I/F:
+ * /sys/firmware/acpi/tables/
+ * /sys/firmware/acpi/tables/data/
+ * /sys/firmware/acpi/tables/dynamic/
+ */
+
+static LIST_HEAD(acpi_table_attr_list);
+static struct kobject *tables_kobj;
+static struct kobject *tables_data_kobj;
+static struct kobject *dynamic_tables_kobj;
+static struct kobject *hotplug_kobj;
+
+#define ACPI_MAX_TABLE_INSTANCES 999
+#define ACPI_INST_SIZE 4 /* including trailing 0 */
+
+struct acpi_table_attr {
+ struct bin_attribute attr;
+ char name[ACPI_NAMESEG_SIZE];
+ int instance;
+ char filename[ACPI_NAMESEG_SIZE+ACPI_INST_SIZE];
+ struct list_head node;
+};
+
+struct acpi_data_attr {
+ struct bin_attribute attr;
+ u64 addr;
+};
+
+static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t offset, size_t count)
+{
+ struct acpi_table_attr *table_attr =
+ container_of(bin_attr, struct acpi_table_attr, attr);
+ struct acpi_table_header *table_header = NULL;
+ acpi_status status;
+ ssize_t rc;
+
+ status = acpi_get_table(table_attr->name, table_attr->instance,
+ &table_header);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ rc = memory_read_from_buffer(buf, count, &offset, table_header,
+ table_header->length);
+ acpi_put_table(table_header);
+ return rc;
+}
+
+static int acpi_table_attr_init(struct kobject *tables_obj,
+ struct acpi_table_attr *table_attr,
+ struct acpi_table_header *table_header)
+{
+ struct acpi_table_header *header = NULL;
+ struct acpi_table_attr *attr = NULL;
+ char instance_str[ACPI_INST_SIZE];
+
+ sysfs_attr_init(&table_attr->attr.attr);
+ ACPI_COPY_NAMESEG(table_attr->name, table_header->signature);
+
+ list_for_each_entry(attr, &acpi_table_attr_list, node) {
+ if (ACPI_COMPARE_NAMESEG(table_attr->name, attr->name))
+ if (table_attr->instance < attr->instance)
+ table_attr->instance = attr->instance;
+ }
+ table_attr->instance++;
+ if (table_attr->instance > ACPI_MAX_TABLE_INSTANCES) {
+ pr_warn("%4.4s: too many table instances\n", table_attr->name);
+ return -ERANGE;
+ }
+
+ ACPI_COPY_NAMESEG(table_attr->filename, table_header->signature);
+ table_attr->filename[ACPI_NAMESEG_SIZE] = '\0';
+ if (table_attr->instance > 1 || (table_attr->instance == 1 &&
+ !acpi_get_table
+ (table_header->signature, 2, &header))) {
+ snprintf(instance_str, sizeof(instance_str), "%u",
+ table_attr->instance);
+ strcat(table_attr->filename, instance_str);
+ }
+
+ table_attr->attr.size = table_header->length;
+ table_attr->attr.read = acpi_table_show;
+ table_attr->attr.attr.name = table_attr->filename;
+ table_attr->attr.attr.mode = 0400;
+
+ return sysfs_create_bin_file(tables_obj, &table_attr->attr);
+}
+
+acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context)
+{
+ struct acpi_table_attr *table_attr;
+
+ switch (event) {
+ case ACPI_TABLE_EVENT_INSTALL:
+ table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL);
+ if (!table_attr)
+ return AE_NO_MEMORY;
+
+ if (acpi_table_attr_init(dynamic_tables_kobj,
+ table_attr, table)) {
+ kfree(table_attr);
+ return AE_ERROR;
+ }
+ list_add_tail(&table_attr->node, &acpi_table_attr_list);
+ break;
+ case ACPI_TABLE_EVENT_LOAD:
+ case ACPI_TABLE_EVENT_UNLOAD:
+ case ACPI_TABLE_EVENT_UNINSTALL:
+ /*
+ * we do not need to do anything right now
+ * because the table is not deleted from the
+ * global table list when unloading it.
+ */
+ break;
+ default:
+ return AE_BAD_PARAMETER;
+ }
+ return AE_OK;
+}
+
+static ssize_t acpi_data_show(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t offset, size_t count)
+{
+ struct acpi_data_attr *data_attr;
+ void __iomem *base;
+ ssize_t size;
+
+ data_attr = container_of(bin_attr, struct acpi_data_attr, attr);
+ size = data_attr->attr.size;
+
+ if (offset < 0)
+ return -EINVAL;
+
+ if (offset >= size)
+ return 0;
+
+ if (count > size - offset)
+ count = size - offset;
+
+ base = acpi_os_map_iomem(data_attr->addr, size);
+ if (!base)
+ return -ENOMEM;
+
+ memcpy_fromio(buf, base + offset, count);
+
+ acpi_os_unmap_iomem(base, size);
+
+ return count;
+}
+
+static int acpi_bert_data_init(void *th, struct acpi_data_attr *data_attr)
+{
+ struct acpi_table_bert *bert = th;
+
+ if (bert->header.length < sizeof(struct acpi_table_bert) ||
+ bert->region_length < sizeof(struct acpi_hest_generic_status)) {
+ kfree(data_attr);
+ return -EINVAL;
+ }
+ data_attr->addr = bert->address;
+ data_attr->attr.size = bert->region_length;
+ data_attr->attr.attr.name = "BERT";
+
+ 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 },
+};
+
+#define NUM_ACPI_DATA_OBJS ARRAY_SIZE(acpi_data_objs)
+
+static int acpi_table_data_init(struct acpi_table_header *th)
+{
+ struct acpi_data_attr *data_attr;
+ int i;
+
+ for (i = 0; i < NUM_ACPI_DATA_OBJS; i++) {
+ if (ACPI_COMPARE_NAMESEG(th->signature, acpi_data_objs[i].name)) {
+ data_attr = kzalloc(sizeof(*data_attr), GFP_KERNEL);
+ if (!data_attr)
+ return -ENOMEM;
+ sysfs_attr_init(&data_attr->attr.attr);
+ data_attr->attr.read = acpi_data_show;
+ data_attr->attr.attr.mode = 0400;
+ return acpi_data_objs[i].fn(th, data_attr);
+ }
+ }
+ return 0;
+}
+
+static int acpi_tables_sysfs_init(void)
+{
+ struct acpi_table_attr *table_attr;
+ struct acpi_table_header *table_header = NULL;
+ int table_index;
+ acpi_status status;
+ int ret;
+
+ tables_kobj = kobject_create_and_add("tables", acpi_kobj);
+ if (!tables_kobj)
+ goto err;
+
+ tables_data_kobj = kobject_create_and_add("data", tables_kobj);
+ if (!tables_data_kobj)
+ goto err_tables_data;
+
+ dynamic_tables_kobj = kobject_create_and_add("dynamic", tables_kobj);
+ if (!dynamic_tables_kobj)
+ goto err_dynamic_tables;
+
+ for (table_index = 0;; table_index++) {
+ status = acpi_get_table_by_index(table_index, &table_header);
+
+ if (status == AE_BAD_PARAMETER)
+ break;
+
+ if (ACPI_FAILURE(status))
+ continue;
+
+ table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL);
+ if (!table_attr)
+ return -ENOMEM;
+
+ ret = acpi_table_attr_init(tables_kobj,
+ table_attr, table_header);
+ if (ret) {
+ kfree(table_attr);
+ return ret;
+ }
+ list_add_tail(&table_attr->node, &acpi_table_attr_list);
+ acpi_table_data_init(table_header);
+ }
+
+ kobject_uevent(tables_kobj, KOBJ_ADD);
+ kobject_uevent(tables_data_kobj, KOBJ_ADD);
+ kobject_uevent(dynamic_tables_kobj, KOBJ_ADD);
+
+ return 0;
+err_dynamic_tables:
+ kobject_put(tables_data_kobj);
+err_tables_data:
+ kobject_put(tables_kobj);
+err:
+ return -ENOMEM;
+}
+
+/*
+ * Detailed ACPI IRQ counters:
+ * /sys/firmware/acpi/interrupts/
+ */
+
+u32 acpi_irq_handled;
+u32 acpi_irq_not_handled;
+
+#define COUNT_GPE 0
+#define COUNT_SCI 1 /* acpi_irq_handled */
+#define COUNT_SCI_NOT 2 /* acpi_irq_not_handled */
+#define COUNT_ERROR 3 /* other */
+#define NUM_COUNTERS_EXTRA 4
+
+struct event_counter {
+ u32 count;
+ u32 flags;
+};
+
+static struct event_counter *all_counters;
+static u32 num_gpes;
+static u32 num_counters;
+static struct attribute **all_attrs;
+static u32 acpi_gpe_count;
+
+static struct attribute_group interrupt_stats_attr_group = {
+ .name = "interrupts",
+};
+
+static struct kobj_attribute *counter_attrs;
+
+static void delete_gpe_attr_array(void)
+{
+ struct event_counter *tmp = all_counters;
+
+ all_counters = NULL;
+ kfree(tmp);
+
+ if (counter_attrs) {
+ int i;
+
+ for (i = 0; i < num_gpes; i++)
+ kfree(counter_attrs[i].attr.name);
+
+ kfree(counter_attrs);
+ }
+ kfree(all_attrs);
+}
+
+static void gpe_count(u32 gpe_number)
+{
+ acpi_gpe_count++;
+
+ if (!all_counters)
+ return;
+
+ if (gpe_number < num_gpes)
+ all_counters[gpe_number].count++;
+ else
+ all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS +
+ COUNT_ERROR].count++;
+}
+
+static void fixed_event_count(u32 event_number)
+{
+ if (!all_counters)
+ return;
+
+ if (event_number < ACPI_NUM_FIXED_EVENTS)
+ all_counters[num_gpes + event_number].count++;
+ else
+ all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS +
+ COUNT_ERROR].count++;
+}
+
+static void acpi_global_event_handler(u32 event_type, acpi_handle device,
+ u32 event_number, void *context)
+{
+ if (event_type == ACPI_EVENT_TYPE_GPE) {
+ gpe_count(event_number);
+ pr_debug("GPE event 0x%02x\n", event_number);
+ } else if (event_type == ACPI_EVENT_TYPE_FIXED) {
+ fixed_event_count(event_number);
+ pr_debug("Fixed event 0x%02x\n", event_number);
+ } else {
+ pr_debug("Other event 0x%02x\n", event_number);
+ }
+}
+
+static int get_status(u32 index, acpi_event_status *ret,
+ acpi_handle *handle)
+{
+ acpi_status status;
+
+ if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS)
+ return -EINVAL;
+
+ if (index < num_gpes) {
+ status = acpi_get_gpe_device(index, handle);
+ if (ACPI_FAILURE(status)) {
+ pr_warn("Invalid GPE 0x%x", index);
+ return -ENXIO;
+ }
+ status = acpi_get_gpe_status(*handle, index, ret);
+ } else {
+ status = acpi_get_event_status(index - num_gpes, ret);
+ }
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return 0;
+}
+
+static ssize_t counter_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int index = attr - counter_attrs;
+ int size;
+ acpi_handle handle;
+ acpi_event_status status;
+ int result = 0;
+
+ all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI].count =
+ acpi_irq_handled;
+ all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI_NOT].count =
+ acpi_irq_not_handled;
+ all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count =
+ acpi_gpe_count;
+ size = sprintf(buf, "%8u", all_counters[index].count);
+
+ /* "gpe_all" or "sci" */
+ if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS)
+ goto end;
+
+ result = get_status(index, &status, &handle);
+ if (result)
+ goto end;
+
+ if (status & ACPI_EVENT_FLAG_ENABLE_SET)
+ size += sprintf(buf + size, " EN");
+ else
+ size += sprintf(buf + size, " ");
+ if (status & ACPI_EVENT_FLAG_STATUS_SET)
+ size += sprintf(buf + size, " STS");
+ else
+ size += sprintf(buf + size, " ");
+
+ if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER))
+ size += sprintf(buf + size, " invalid ");
+ else if (status & ACPI_EVENT_FLAG_ENABLED)
+ size += sprintf(buf + size, " enabled ");
+ else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED)
+ size += sprintf(buf + size, " wake_enabled");
+ else
+ size += sprintf(buf + size, " disabled ");
+ if (status & ACPI_EVENT_FLAG_MASKED)
+ size += sprintf(buf + size, " masked ");
+ else
+ size += sprintf(buf + size, " unmasked");
+
+end:
+ size += sprintf(buf + size, "\n");
+ return result ? result : size;
+}
+
+/*
+ * counter_set() sets the specified counter.
+ * setting the total "sci" file to any value clears all counters.
+ * enable/disable/clear a gpe/fixed event in user space.
+ */
+static ssize_t counter_set(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf,
+ size_t size)
+{
+ int index = attr - counter_attrs;
+ acpi_event_status status;
+ acpi_handle handle;
+ int result = 0;
+ unsigned long tmp;
+
+ if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) {
+ int i;
+ for (i = 0; i < num_counters; ++i)
+ all_counters[i].count = 0;
+ acpi_gpe_count = 0;
+ acpi_irq_handled = 0;
+ acpi_irq_not_handled = 0;
+ goto end;
+ }
+
+ /* show the event status for both GPEs and Fixed Events */
+ result = get_status(index, &status, &handle);
+ if (result)
+ goto end;
+
+ if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) {
+ pr_warn("Can not change Invalid GPE/Fixed Event status\n");
+ return -EINVAL;
+ }
+
+ if (index < num_gpes) {
+ if (!strcmp(buf, "disable\n") &&
+ (status & ACPI_EVENT_FLAG_ENABLED))
+ result = acpi_disable_gpe(handle, index);
+ else if (!strcmp(buf, "enable\n") &&
+ !(status & ACPI_EVENT_FLAG_ENABLED))
+ result = acpi_enable_gpe(handle, index);
+ else if (!strcmp(buf, "clear\n") &&
+ (status & ACPI_EVENT_FLAG_STATUS_SET))
+ result = acpi_clear_gpe(handle, index);
+ else if (!strcmp(buf, "mask\n"))
+ result = acpi_mask_gpe(handle, index, TRUE);
+ else if (!strcmp(buf, "unmask\n"))
+ result = acpi_mask_gpe(handle, index, FALSE);
+ else if (!kstrtoul(buf, 0, &tmp))
+ all_counters[index].count = tmp;
+ else
+ result = -EINVAL;
+ } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) {
+ int event = index - num_gpes;
+ if (!strcmp(buf, "disable\n") &&
+ (status & ACPI_EVENT_FLAG_ENABLE_SET))
+ result = acpi_disable_event(event, ACPI_NOT_ISR);
+ else if (!strcmp(buf, "enable\n") &&
+ !(status & ACPI_EVENT_FLAG_ENABLE_SET))
+ result = acpi_enable_event(event, ACPI_NOT_ISR);
+ else if (!strcmp(buf, "clear\n") &&
+ (status & ACPI_EVENT_FLAG_STATUS_SET))
+ result = acpi_clear_event(event);
+ else if (!kstrtoul(buf, 0, &tmp))
+ all_counters[index].count = tmp;
+ else
+ result = -EINVAL;
+ } else
+ all_counters[index].count = strtoul(buf, NULL, 0);
+
+ if (ACPI_FAILURE(result))
+ result = -EINVAL;
+end:
+ return result ? result : size;
+}
+
+/*
+ * A Quirk Mechanism for GPE Flooding Prevention:
+ *
+ * Quirks may be needed to prevent GPE flooding on a specific GPE. The
+ * flooding typically cannot be detected and automatically prevented by
+ * ACPI_GPE_DISPATCH_NONE check because there is a _Lxx/_Exx prepared in
+ * the AML tables. This normally indicates a feature gap in Linux, thus
+ * instead of providing endless quirk tables, we provide a boot parameter
+ * for those who want this quirk. For example, if the users want to prevent
+ * the GPE flooding for GPE 00, they need to specify the following boot
+ * parameter:
+ * acpi_mask_gpe=0x00
+ * Note, the parameter can be a list (see bitmap_parselist() for the details).
+ * The masking status can be modified by the following runtime controlling
+ * interface:
+ * echo unmask > /sys/firmware/acpi/interrupts/gpe00
+ */
+#define ACPI_MASKABLE_GPE_MAX 0x100
+static DECLARE_BITMAP(acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX) __initdata;
+
+static int __init acpi_gpe_set_masked_gpes(char *val)
+{
+ int ret;
+ u8 gpe;
+
+ ret = kstrtou8(val, 0, &gpe);
+ if (ret) {
+ ret = bitmap_parselist(val, acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX);
+ if (ret)
+ return ret;
+ } else
+ set_bit(gpe, acpi_masked_gpes_map);
+
+ return 1;
+}
+__setup("acpi_mask_gpe=", acpi_gpe_set_masked_gpes);
+
+void __init acpi_gpe_apply_masked_gpes(void)
+{
+ acpi_handle handle;
+ acpi_status status;
+ u16 gpe;
+
+ for_each_set_bit(gpe, acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX) {
+ status = acpi_get_gpe_device(gpe, &handle);
+ if (ACPI_SUCCESS(status)) {
+ pr_info("Masking GPE 0x%x.\n", gpe);
+ (void)acpi_mask_gpe(handle, gpe, TRUE);
+ }
+ }
+}
+
+void acpi_irq_stats_init(void)
+{
+ acpi_status status;
+ int i;
+
+ if (all_counters)
+ return;
+
+ num_gpes = acpi_current_gpe_count;
+ num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA;
+
+ all_attrs = kcalloc(num_counters + 1, sizeof(*all_attrs), GFP_KERNEL);
+ if (all_attrs == NULL)
+ return;
+
+ all_counters = kcalloc(num_counters, sizeof(*all_counters), GFP_KERNEL);
+ if (all_counters == NULL)
+ goto fail;
+
+ status = acpi_install_global_event_handler(acpi_global_event_handler, NULL);
+ if (ACPI_FAILURE(status))
+ goto fail;
+
+ counter_attrs = kcalloc(num_counters, sizeof(*counter_attrs), GFP_KERNEL);
+ if (counter_attrs == NULL)
+ goto fail;
+
+ for (i = 0; i < num_counters; ++i) {
+ char buffer[12];
+ char *name;
+
+ if (i < num_gpes)
+ sprintf(buffer, "gpe%02X", i);
+ else if (i == num_gpes + ACPI_EVENT_PMTIMER)
+ sprintf(buffer, "ff_pmtimer");
+ else if (i == num_gpes + ACPI_EVENT_GLOBAL)
+ sprintf(buffer, "ff_gbl_lock");
+ else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON)
+ sprintf(buffer, "ff_pwr_btn");
+ else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON)
+ sprintf(buffer, "ff_slp_btn");
+ else if (i == num_gpes + ACPI_EVENT_RTC)
+ sprintf(buffer, "ff_rt_clk");
+ else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE)
+ sprintf(buffer, "gpe_all");
+ else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI)
+ sprintf(buffer, "sci");
+ else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI_NOT)
+ sprintf(buffer, "sci_not");
+ else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR)
+ sprintf(buffer, "error");
+ else
+ sprintf(buffer, "bug%02X", i);
+
+ name = kstrdup(buffer, GFP_KERNEL);
+ if (name == NULL)
+ goto fail;
+
+ sysfs_attr_init(&counter_attrs[i].attr);
+ counter_attrs[i].attr.name = name;
+ counter_attrs[i].attr.mode = 0644;
+ counter_attrs[i].show = counter_show;
+ counter_attrs[i].store = counter_set;
+
+ all_attrs[i] = &counter_attrs[i].attr;
+ }
+
+ interrupt_stats_attr_group.attrs = all_attrs;
+ if (!sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group))
+ return;
+
+fail:
+ delete_gpe_attr_array();
+}
+
+static void __exit interrupt_stats_exit(void)
+{
+ sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group);
+
+ delete_gpe_attr_array();
+}
+
+static ssize_t pm_profile_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", acpi_gbl_FADT.preferred_profile);
+}
+
+static const struct kobj_attribute pm_profile_attr = __ATTR_RO(pm_profile);
+
+static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj);
+
+ return sprintf(buf, "%d\n", hotplug->enabled);
+}
+
+static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj);
+ unsigned int val;
+
+ if (kstrtouint(buf, 10, &val) || val > 1)
+ return -EINVAL;
+
+ acpi_scan_hotplug_enabled(hotplug, val);
+ return size;
+}
+
+static struct kobj_attribute hotplug_enabled_attr = __ATTR_RW(enabled);
+
+static struct attribute *hotplug_profile_attrs[] = {
+ &hotplug_enabled_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(hotplug_profile);
+
+static struct kobj_type acpi_hotplug_profile_ktype = {
+ .sysfs_ops = &kobj_sysfs_ops,
+ .default_groups = hotplug_profile_groups,
+};
+
+void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,
+ const char *name)
+{
+ int error;
+
+ if (!hotplug_kobj)
+ goto err_out;
+
+ error = kobject_init_and_add(&hotplug->kobj,
+ &acpi_hotplug_profile_ktype, hotplug_kobj, "%s", name);
+ if (error) {
+ kobject_put(&hotplug->kobj);
+ goto err_out;
+ }
+
+ kobject_uevent(&hotplug->kobj, KOBJ_ADD);
+ return;
+
+ err_out:
+ pr_err("Unable to add hotplug profile '%s'\n", name);
+}
+
+static ssize_t force_remove_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", 0);
+}
+
+static ssize_t force_remove_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t size)
+{
+ bool val;
+ int ret;
+
+ ret = strtobool(buf, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val) {
+ pr_err("Enabling force_remove is not supported anymore. Please report to linux-acpi@vger.kernel.org if you depend on this functionality\n");
+ return -EINVAL;
+ }
+ return size;
+}
+
+static const struct kobj_attribute force_remove_attr = __ATTR_RW(force_remove);
+
+int __init acpi_sysfs_init(void)
+{
+ int result;
+
+ result = acpi_tables_sysfs_init();
+ if (result)
+ return result;
+
+ hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj);
+ if (!hotplug_kobj)
+ return -ENOMEM;
+
+ result = sysfs_create_file(hotplug_kobj, &force_remove_attr.attr);
+ if (result)
+ return result;
+
+ result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr);
+ return result;
+}
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
new file mode 100644
index 000000000..47ec11d4c
--- /dev/null
+++ b/drivers/acpi/tables.c
@@ -0,0 +1,933 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * acpi_tables.c - ACPI Boot-Time Table Parsing
+ *
+ * Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ */
+
+/* Uncomment next line to get verbose printout */
+/* #define DEBUG */
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/memblock.h>
+#include <linux/earlycpio.h>
+#include <linux/initrd.h>
+#include <linux/security.h>
+#include <linux/kmemleak.h>
+#include "internal.h"
+
+#ifdef CONFIG_ACPI_CUSTOM_DSDT
+#include CONFIG_ACPI_CUSTOM_DSDT_FILE
+#endif
+
+#define ACPI_MAX_TABLES 128
+
+static char *mps_inti_flags_polarity[] = { "dfl", "high", "res", "low" };
+static char *mps_inti_flags_trigger[] = { "dfl", "edge", "res", "level" };
+
+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.
+ */
+static bool acpi_verify_table_checksum __initdata_or_acpilib = false;
+
+void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
+{
+ if (!header)
+ return;
+
+ switch (header->type) {
+
+ case ACPI_MADT_TYPE_LOCAL_APIC:
+ {
+ struct acpi_madt_local_apic *p =
+ (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");
+ }
+ break;
+
+ case ACPI_MADT_TYPE_LOCAL_X2APIC:
+ {
+ struct acpi_madt_local_x2apic *p =
+ (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");
+ }
+ break;
+
+ case ACPI_MADT_TYPE_IO_APIC:
+ {
+ struct acpi_madt_io_apic *p =
+ (struct acpi_madt_io_apic *)header;
+ pr_debug("IOAPIC (id[0x%02x] address[0x%08x] gsi_base[%d])\n",
+ p->id, p->address, p->global_irq_base);
+ }
+ break;
+
+ case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:
+ {
+ struct acpi_madt_interrupt_override *p =
+ (struct acpi_madt_interrupt_override *)header;
+ pr_info("INT_SRC_OVR (bus %d bus_irq %d global_irq %d %s %s)\n",
+ p->bus, p->source_irq, p->global_irq,
+ mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK],
+ mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2]);
+ if (p->inti_flags &
+ ~(ACPI_MADT_POLARITY_MASK | ACPI_MADT_TRIGGER_MASK))
+ pr_info("INT_SRC_OVR unexpected reserved flags: 0x%x\n",
+ p->inti_flags &
+ ~(ACPI_MADT_POLARITY_MASK | ACPI_MADT_TRIGGER_MASK));
+ }
+ break;
+
+ case ACPI_MADT_TYPE_NMI_SOURCE:
+ {
+ struct acpi_madt_nmi_source *p =
+ (struct acpi_madt_nmi_source *)header;
+ pr_info("NMI_SRC (%s %s global_irq %d)\n",
+ mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK],
+ mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2],
+ p->global_irq);
+ }
+ break;
+
+ case ACPI_MADT_TYPE_LOCAL_APIC_NMI:
+ {
+ struct acpi_madt_local_apic_nmi *p =
+ (struct acpi_madt_local_apic_nmi *)header;
+ pr_info("LAPIC_NMI (acpi_id[0x%02x] %s %s lint[0x%x])\n",
+ p->processor_id,
+ mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK ],
+ mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2],
+ p->lint);
+ }
+ break;
+
+ case ACPI_MADT_TYPE_LOCAL_X2APIC_NMI:
+ {
+ u16 polarity, trigger;
+ struct acpi_madt_local_x2apic_nmi *p =
+ (struct acpi_madt_local_x2apic_nmi *)header;
+
+ polarity = p->inti_flags & ACPI_MADT_POLARITY_MASK;
+ trigger = (p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2;
+
+ pr_info("X2APIC_NMI (uid[0x%02x] %s %s lint[0x%x])\n",
+ p->uid,
+ mps_inti_flags_polarity[polarity],
+ mps_inti_flags_trigger[trigger],
+ p->lint);
+ }
+ break;
+
+ case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE:
+ {
+ struct acpi_madt_local_apic_override *p =
+ (struct acpi_madt_local_apic_override *)header;
+ pr_info("LAPIC_ADDR_OVR (address[0x%llx])\n",
+ p->address);
+ }
+ break;
+
+ case ACPI_MADT_TYPE_IO_SAPIC:
+ {
+ struct acpi_madt_io_sapic *p =
+ (struct acpi_madt_io_sapic *)header;
+ pr_debug("IOSAPIC (id[0x%x] address[%p] gsi_base[%d])\n",
+ p->id, (void *)(unsigned long)p->address,
+ p->global_irq_base);
+ }
+ break;
+
+ case ACPI_MADT_TYPE_LOCAL_SAPIC:
+ {
+ struct acpi_madt_local_sapic *p =
+ (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");
+ }
+ break;
+
+ case ACPI_MADT_TYPE_INTERRUPT_SOURCE:
+ {
+ struct acpi_madt_interrupt_source *p =
+ (struct acpi_madt_interrupt_source *)header;
+ pr_info("PLAT_INT_SRC (%s %s type[0x%x] id[0x%04x] eid[0x%x] iosapic_vector[0x%x] global_irq[0x%x]\n",
+ mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK],
+ mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2],
+ p->type, p->id, p->eid, p->io_sapic_vector,
+ p->global_irq);
+ }
+ break;
+
+ case ACPI_MADT_TYPE_GENERIC_INTERRUPT:
+ {
+ struct acpi_madt_generic_interrupt *p =
+ (struct acpi_madt_generic_interrupt *)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");
+
+ }
+ break;
+
+ case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
+ {
+ struct acpi_madt_generic_distributor *p =
+ (struct acpi_madt_generic_distributor *)header;
+ pr_debug("GIC Distributor (gic_id[0x%04x] address[%llx] gsi_base[%d])\n",
+ p->gic_id, p->base_address,
+ p->global_irq_base);
+ }
+ 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;
+
+ /* Parse all entries looking for a match. */
+
+ 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);
+
+ while (((unsigned long)entry.hdr) + subtable_len < table_end) {
+ if (max_entries && count >= max_entries)
+ break;
+
+ 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;
+ }
+ if (i != proc_num)
+ count++;
+
+ /*
+ * 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;
+ }
+
+ entry.hdr = (union acpi_subtable_headers *)
+ ((unsigned long)entry.hdr + entry_len);
+ }
+
+ if (max_entries && count > max_entries) {
+ pr_warn("[%4.4s:0x%02x] found the maximum %i entries\n",
+ id, proc->id, count);
+ }
+
+ return errs ? -EINVAL : count;
+}
+
+int __init_or_acpilib acpi_table_parse_entries_array(
+ char *id, unsigned long table_size, struct acpi_subtable_proc *proc,
+ int proc_num, unsigned int max_entries)
+{
+ struct acpi_table_header *table_header = NULL;
+ int count;
+ u32 instance = 0;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ if (!id)
+ return -EINVAL;
+
+ if (!table_size)
+ return -EINVAL;
+
+ if (!strncmp(id, ACPI_SIG_MADT, 4))
+ instance = acpi_apic_instance;
+
+ acpi_get_table(id, instance, &table_header);
+ if (!table_header) {
+ pr_debug("%4.4s not present\n", id);
+ return -ENODEV;
+ }
+
+ count = acpi_parse_entries_array(id, table_size, table_header,
+ proc, proc_num, max_entries);
+
+ acpi_put_table(table_header);
+ return count;
+}
+
+static int __init_or_acpilib __acpi_table_parse_entries(
+ char *id, unsigned long table_size, int entry_id,
+ acpi_tbl_entry_handler handler, acpi_tbl_entry_handler_arg handler_arg,
+ void *arg, unsigned int max_entries)
+{
+ struct acpi_subtable_proc proc = {
+ .id = entry_id,
+ .handler = handler,
+ .handler_arg = handler_arg,
+ .arg = arg,
+ };
+
+ return acpi_table_parse_entries_array(id, table_size, &proc, 1,
+ max_entries);
+}
+
+int __init_or_acpilib
+acpi_table_parse_cedt(enum acpi_cedt_type id,
+ acpi_tbl_entry_handler_arg handler_arg, void *arg)
+{
+ return __acpi_table_parse_entries(ACPI_SIG_CEDT,
+ sizeof(struct acpi_table_cedt), id,
+ NULL, handler_arg, arg, 0);
+}
+EXPORT_SYMBOL_ACPI_LIB(acpi_table_parse_cedt);
+
+int __init acpi_table_parse_entries(char *id, unsigned long table_size,
+ int entry_id,
+ acpi_tbl_entry_handler handler,
+ unsigned int max_entries)
+{
+ return __acpi_table_parse_entries(id, table_size, entry_id, handler,
+ NULL, NULL, max_entries);
+}
+
+int __init acpi_table_parse_madt(enum acpi_madt_type id,
+ acpi_tbl_entry_handler handler, unsigned int max_entries)
+{
+ return acpi_table_parse_entries(ACPI_SIG_MADT,
+ sizeof(struct acpi_table_madt), id,
+ handler, max_entries);
+}
+
+/**
+ * acpi_table_parse - find table with @id, run @handler on it
+ * @id: table id to find
+ * @handler: handler to run
+ *
+ * Scan the ACPI System Descriptor Table (STD) for a table matching @id,
+ * run @handler on it.
+ *
+ * Return 0 if table found, -errno if not.
+ */
+int __init acpi_table_parse(char *id, acpi_tbl_table_handler handler)
+{
+ struct acpi_table_header *table = NULL;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ if (!id || !handler)
+ return -EINVAL;
+
+ if (strncmp(id, ACPI_SIG_MADT, 4) == 0)
+ acpi_get_table(id, acpi_apic_instance, &table);
+ else
+ acpi_get_table(id, 0, &table);
+
+ if (table) {
+ handler(table);
+ acpi_put_table(table);
+ return 0;
+ } else
+ return -ENODEV;
+}
+
+/*
+ * The BIOS is supposed to supply a single APIC/MADT,
+ * but some report two. Provide a knob to use either.
+ * (don't you wish instance 0 and 1 were not the same?)
+ */
+static void __init check_multiple_madt(void)
+{
+ struct acpi_table_header *table = NULL;
+
+ acpi_get_table(ACPI_SIG_MADT, 2, &table);
+ if (table) {
+ pr_warn("BIOS bug: multiple APIC/MADT found, using %d\n",
+ acpi_apic_instance);
+ pr_warn("If \"acpi_apic_instance=%d\" works better, "
+ "notify linux-acpi@vger.kernel.org\n",
+ acpi_apic_instance ? 0 : 2);
+ acpi_put_table(table);
+
+ } else
+ acpi_apic_instance = 0;
+
+ return;
+}
+
+static void acpi_table_taint(struct acpi_table_header *table)
+{
+ pr_warn("Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n",
+ table->signature, table->oem_table_id);
+ add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE);
+}
+
+#ifdef CONFIG_ACPI_TABLE_UPGRADE
+static u64 acpi_tables_addr;
+static int all_tables_size;
+
+/* Copied from acpica/tbutils.c:acpi_tb_checksum() */
+static u8 __init acpi_table_checksum(u8 *buffer, u32 length)
+{
+ u8 sum = 0;
+ u8 *end = buffer + length;
+
+ while (buffer < end)
+ sum = (u8) (sum + *(buffer++));
+ return sum;
+}
+
+/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */
+static const char table_sigs[][ACPI_NAMESEG_SIZE] __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,
+ ACPI_SIG_ASF, ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR,
+ ACPI_SIG_HPET, ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG,
+ ACPI_SIG_MCHI, ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI,
+ ACPI_SIG_TCPA, ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT,
+ 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 };
+
+#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
+
+#define NR_ACPI_INITRD_TABLES 64
+static struct cpio_data __initdata acpi_initrd_files[NR_ACPI_INITRD_TABLES];
+static DECLARE_BITMAP(acpi_initrd_installed, NR_ACPI_INITRD_TABLES);
+
+#define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT)
+
+void __init acpi_table_upgrade(void)
+{
+ void *data;
+ size_t size;
+ int sig, no, table_nr = 0, total_offset = 0;
+ long offset = 0;
+ struct acpi_table_header *table;
+ char cpio_path[32] = "kernel/firmware/acpi/";
+ struct cpio_data file;
+
+ if (IS_ENABLED(CONFIG_ACPI_TABLE_OVERRIDE_VIA_BUILTIN_INITRD)) {
+ data = __initramfs_start;
+ size = __initramfs_size;
+ } else {
+ data = (void *)initrd_start;
+ size = initrd_end - initrd_start;
+ }
+
+ if (data == NULL || size == 0)
+ return;
+
+ for (no = 0; no < NR_ACPI_INITRD_TABLES; no++) {
+ file = find_cpio_data(cpio_path, data, size, &offset);
+ if (!file.data)
+ break;
+
+ data += offset;
+ size -= offset;
+
+ if (file.size < sizeof(struct acpi_table_header)) {
+ pr_err("ACPI OVERRIDE: Table smaller than ACPI header [%s%s]\n",
+ cpio_path, file.name);
+ continue;
+ }
+
+ table = file.data;
+
+ for (sig = 0; sig < ARRAY_SIZE(table_sigs); sig++)
+ if (!memcmp(table->signature, table_sigs[sig], 4))
+ break;
+
+ if (sig >= ARRAY_SIZE(table_sigs)) {
+ pr_err("ACPI OVERRIDE: Unknown signature [%s%s]\n",
+ cpio_path, file.name);
+ continue;
+ }
+ if (file.size != table->length) {
+ pr_err("ACPI OVERRIDE: File length does not match table length [%s%s]\n",
+ cpio_path, file.name);
+ continue;
+ }
+ if (acpi_table_checksum(file.data, table->length)) {
+ pr_err("ACPI OVERRIDE: Bad table checksum [%s%s]\n",
+ cpio_path, file.name);
+ continue;
+ }
+
+ pr_info("%4.4s ACPI table found in initrd [%s%s][0x%x]\n",
+ table->signature, cpio_path, file.name, table->length);
+
+ all_tables_size += table->length;
+ acpi_initrd_files[table_nr].data = file.data;
+ acpi_initrd_files[table_nr].size = file.size;
+ table_nr++;
+ }
+ if (table_nr == 0)
+ return;
+
+ if (security_locked_down(LOCKDOWN_ACPI_TABLES)) {
+ pr_notice("kernel is locked down, ignoring table override\n");
+ return;
+ }
+
+ acpi_tables_addr =
+ memblock_phys_alloc_range(all_tables_size, PAGE_SIZE,
+ 0, ACPI_TABLE_UPGRADE_MAX_PHYS);
+ if (!acpi_tables_addr) {
+ WARN_ON(1);
+ return;
+ }
+ /*
+ * Only calling e820_add_reserve does not work and the
+ * tables are invalid (memory got used) later.
+ * memblock_reserve works as expected and the tables won't get modified.
+ * But it's not enough on X86 because ioremap will
+ * complain later (used by acpi_os_map_memory) that the pages
+ * that should get mapped are not marked "reserved".
+ * Both memblock_reserve and e820__range_add (via arch_reserve_mem_area)
+ * works fine.
+ */
+ arch_reserve_mem_area(acpi_tables_addr, all_tables_size);
+
+ kmemleak_ignore_phys(acpi_tables_addr);
+
+ /*
+ * early_ioremap only can remap 256k one time. If we map all
+ * tables one time, we will hit the limit. Need to map chunks
+ * one by one during copying the same as that in relocate_initrd().
+ */
+ for (no = 0; no < table_nr; no++) {
+ unsigned char *src_p = acpi_initrd_files[no].data;
+ phys_addr_t size = acpi_initrd_files[no].size;
+ phys_addr_t dest_addr = acpi_tables_addr + total_offset;
+ phys_addr_t slop, clen;
+ char *dest_p;
+
+ total_offset += size;
+
+ while (size) {
+ slop = dest_addr & ~PAGE_MASK;
+ clen = size;
+ if (clen > MAP_CHUNK_SIZE - slop)
+ clen = MAP_CHUNK_SIZE - slop;
+ dest_p = early_memremap(dest_addr & PAGE_MASK,
+ clen + slop);
+ memcpy(dest_p + slop, src_p, clen);
+ early_memunmap(dest_p, clen + slop);
+ src_p += clen;
+ dest_addr += clen;
+ size -= clen;
+ }
+ }
+}
+
+static acpi_status
+acpi_table_initrd_override(struct acpi_table_header *existing_table,
+ acpi_physical_address *address, u32 *length)
+{
+ int table_offset = 0;
+ int table_index = 0;
+ struct acpi_table_header *table;
+ u32 table_length;
+
+ *length = 0;
+ *address = 0;
+ if (!acpi_tables_addr)
+ return AE_OK;
+
+ while (table_offset + ACPI_HEADER_SIZE <= all_tables_size) {
+ table = acpi_os_map_memory(acpi_tables_addr + table_offset,
+ ACPI_HEADER_SIZE);
+ if (table_offset + table->length > all_tables_size) {
+ acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+ WARN_ON(1);
+ return AE_OK;
+ }
+
+ table_length = table->length;
+
+ /* Only override tables matched */
+ if (memcmp(existing_table->signature, table->signature, 4) ||
+ memcmp(table->oem_id, existing_table->oem_id,
+ ACPI_OEM_ID_SIZE) ||
+ memcmp(table->oem_table_id, existing_table->oem_table_id,
+ ACPI_OEM_TABLE_ID_SIZE)) {
+ acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+ goto next_table;
+ }
+ /*
+ * Mark the table to avoid being used in
+ * acpi_table_initrd_scan() and check the revision.
+ */
+ if (test_and_set_bit(table_index, acpi_initrd_installed) ||
+ existing_table->oem_revision >= table->oem_revision) {
+ acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+ goto next_table;
+ }
+
+ *length = table_length;
+ *address = acpi_tables_addr + table_offset;
+ pr_info("Table Upgrade: override [%4.4s-%6.6s-%8.8s]\n",
+ table->signature, table->oem_id,
+ table->oem_table_id);
+ acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+ break;
+
+next_table:
+ table_offset += table_length;
+ table_index++;
+ }
+ return AE_OK;
+}
+
+static void __init acpi_table_initrd_scan(void)
+{
+ int table_offset = 0;
+ int table_index = 0;
+ u32 table_length;
+ struct acpi_table_header *table;
+
+ if (!acpi_tables_addr)
+ return;
+
+ while (table_offset + ACPI_HEADER_SIZE <= all_tables_size) {
+ table = acpi_os_map_memory(acpi_tables_addr + table_offset,
+ ACPI_HEADER_SIZE);
+ if (table_offset + table->length > all_tables_size) {
+ acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+ WARN_ON(1);
+ return;
+ }
+
+ table_length = table->length;
+
+ /* Skip RSDT/XSDT which should only be used for override */
+ if (ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_RSDT) ||
+ ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_XSDT)) {
+ acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+ goto next_table;
+ }
+ /*
+ * Mark the table to avoid being used in
+ * acpi_table_initrd_override(). Though this is not possible
+ * because override is disabled in acpi_install_physical_table().
+ */
+ if (test_and_set_bit(table_index, acpi_initrd_installed)) {
+ acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+ goto next_table;
+ }
+
+ pr_info("Table Upgrade: install [%4.4s-%6.6s-%8.8s]\n",
+ table->signature, table->oem_id,
+ table->oem_table_id);
+ acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+ acpi_install_physical_table(acpi_tables_addr + table_offset);
+next_table:
+ table_offset += table_length;
+ table_index++;
+ }
+}
+#else
+static acpi_status
+acpi_table_initrd_override(struct acpi_table_header *existing_table,
+ acpi_physical_address *address,
+ u32 *table_length)
+{
+ *table_length = 0;
+ *address = 0;
+ return AE_OK;
+}
+
+static void __init acpi_table_initrd_scan(void)
+{
+}
+#endif /* CONFIG_ACPI_TABLE_UPGRADE */
+
+acpi_status
+acpi_os_physical_table_override(struct acpi_table_header *existing_table,
+ acpi_physical_address *address,
+ u32 *table_length)
+{
+ return acpi_table_initrd_override(existing_table, address,
+ table_length);
+}
+
+#ifdef CONFIG_ACPI_CUSTOM_DSDT
+static void *amlcode __attribute__ ((weakref("AmlCode")));
+static void *dsdt_amlcode __attribute__ ((weakref("dsdt_aml_code")));
+#endif
+
+acpi_status acpi_os_table_override(struct acpi_table_header *existing_table,
+ struct acpi_table_header **new_table)
+{
+ if (!existing_table || !new_table)
+ return AE_BAD_PARAMETER;
+
+ *new_table = NULL;
+
+#ifdef CONFIG_ACPI_CUSTOM_DSDT
+ if (!strncmp(existing_table->signature, "DSDT", 4)) {
+ *new_table = (struct acpi_table_header *)&amlcode;
+ if (!(*new_table))
+ *new_table = (struct acpi_table_header *)&dsdt_amlcode;
+ }
+#endif
+ if (*new_table != NULL)
+ acpi_table_taint(existing_table);
+ return AE_OK;
+}
+
+/*
+ * acpi_locate_initial_tables()
+ *
+ * find RSDP, find and checksum SDT/XSDT.
+ * checksum all tables, print SDT/XSDT
+ *
+ * result: sdt_entry[] is initialized
+ */
+
+int __init acpi_locate_initial_tables(void)
+{
+ acpi_status status;
+
+ if (acpi_verify_table_checksum) {
+ pr_info("Early table checksum verification enabled\n");
+ acpi_gbl_enable_table_validation = TRUE;
+ } else {
+ pr_info("Early table checksum verification disabled\n");
+ acpi_gbl_enable_table_validation = FALSE;
+ }
+
+ status = acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ return 0;
+}
+
+void __init acpi_reserve_initial_tables(void)
+{
+ int i;
+
+ for (i = 0; i < ACPI_MAX_TABLES; i++) {
+ struct acpi_table_desc *table_desc = &initial_tables[i];
+ u64 start = table_desc->address;
+ u64 size = table_desc->length;
+
+ if (!start || !size)
+ break;
+
+ pr_info("Reserving %4s table memory at [mem 0x%llx-0x%llx]\n",
+ table_desc->signature.ascii, start, start + size - 1);
+
+ memblock_reserve(start, size);
+ }
+}
+
+void __init acpi_table_init_complete(void)
+{
+ acpi_table_initrd_scan();
+ check_multiple_madt();
+}
+
+int __init acpi_table_init(void)
+{
+ int ret;
+
+ ret = acpi_locate_initial_tables();
+ if (ret)
+ return ret;
+
+ acpi_table_init_complete();
+
+ return 0;
+}
+
+static int __init acpi_parse_apic_instance(char *str)
+{
+ if (!str)
+ return -EINVAL;
+
+ if (kstrtoint(str, 0, &acpi_apic_instance))
+ return -EINVAL;
+
+ pr_notice("Shall use APIC/MADT table %d\n", acpi_apic_instance);
+
+ return 0;
+}
+early_param("acpi_apic_instance", acpi_parse_apic_instance);
+
+static int __init acpi_force_table_verification_setup(char *s)
+{
+ acpi_verify_table_checksum = true;
+
+ return 0;
+}
+early_param("acpi_force_table_verification", acpi_force_table_verification_setup);
+
+static int __init acpi_force_32bit_fadt_addr(char *s)
+{
+ pr_info("Forcing 32 Bit FADT addresses\n");
+ acpi_gbl_use32_bit_fadt_addresses = TRUE;
+
+ return 0;
+}
+early_param("acpi_force_32bit_fadt_addr", acpi_force_32bit_fadt_addr);
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
new file mode 100644
index 000000000..40ecb55b5
--- /dev/null
+++ b/drivers/acpi/thermal.c
@@ -0,0 +1,1219 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * acpi_thermal.c - ACPI Thermal Zone Driver ($Revision: 41 $)
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * This driver fully implements the ACPI thermal policy as described in the
+ * ACPI 2.0 Specification.
+ *
+ * TBD: 1. Implement passive cooling hysteresis.
+ * 2. Enhance passive cooling (CPU) states/limit interface to support
+ * concepts of 'multiple limiters', upper/lower limits, etc.
+ */
+
+#define pr_fmt(fmt) "ACPI: thermal: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/jiffies.h>
+#include <linux/kmod.h>
+#include <linux/reboot.h>
+#include <linux/device.h>
+#include <linux/thermal.h>
+#include <linux/acpi.h>
+#include <linux/workqueue.h>
+#include <linux/uaccess.h>
+#include <linux/units.h>
+
+#define ACPI_THERMAL_CLASS "thermal_zone"
+#define ACPI_THERMAL_DEVICE_NAME "Thermal Zone"
+#define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80
+#define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81
+#define ACPI_THERMAL_NOTIFY_DEVICES 0x82
+#define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0
+#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
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
+MODULE_LICENSE("GPL");
+
+static int act;
+module_param(act, int, 0644);
+MODULE_PARM_DESC(act, "Disable or override all lowest active trip points.");
+
+static int crt;
+module_param(crt, int, 0644);
+MODULE_PARM_DESC(crt, "Disable or lower all critical trip points.");
+
+static int tzp;
+module_param(tzp, int, 0444);
+MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.");
+
+static int off;
+module_param(off, int, 0);
+MODULE_PARM_DESC(off, "Set to disable ACPI thermal support.");
+
+static int psv;
+module_param(psv, int, 0644);
+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_passive {
+ struct acpi_thermal_state_flags flags;
+ unsigned long temperature;
+ unsigned long tc1;
+ unsigned long tc2;
+ unsigned long tsp;
+ struct acpi_handle_list devices;
+};
+
+struct acpi_thermal_active {
+ struct acpi_thermal_state_flags flags;
+ unsigned long temperature;
+ struct acpi_handle_list devices;
+};
+
+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;
+ acpi_bus_id name;
+ unsigned long temperature;
+ unsigned long last_temperature;
+ 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;
+ struct mutex thermal_check_lock;
+ refcount_t thermal_check_count;
+};
+
+/* --------------------------------------------------------------------------
+ Thermal Zone Management
+ -------------------------------------------------------------------------- */
+
+static int acpi_thermal_get_temperature(struct acpi_thermal *tz)
+{
+ acpi_status status = AE_OK;
+ unsigned long long tmp;
+
+ if (!tz)
+ return -EINVAL;
+
+ tz->last_temperature = tz->temperature;
+
+ status = acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ tz->temperature = tmp;
+
+ acpi_handle_debug(tz->device->handle, "Temperature is %lu dK\n",
+ tz->temperature);
+
+ return 0;
+}
+
+static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz)
+{
+ acpi_status status = AE_OK;
+ unsigned long long tmp;
+
+ if (!tz)
+ return -EINVAL;
+
+ status = acpi_evaluate_integer(tz->device->handle, "_TZP", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ tz->polling_frequency = tmp;
+ acpi_handle_debug(tz->device->handle, "Polling frequency is %lu dS\n",
+ tz->polling_frequency);
+
+ return 0;
+}
+
+static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
+{
+ if (!tz)
+ return -EINVAL;
+
+ if (ACPI_FAILURE(acpi_execute_simple_method(tz->device->handle,
+ "_SCP", mode)))
+ return -ENODEV;
+
+ return 0;
+}
+
+#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
+
+#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
+#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES
+
+#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \
+ ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \
+ ACPI_TRIPS_DEVICES)
+
+/*
+ * 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 int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
+{
+ acpi_status status;
+ unsigned long long tmp;
+ struct acpi_handle_list devices;
+ int valid = 0;
+ int i;
+
+ /* 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;
+ }
+ }
+ }
+
+ /* 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);
+ }
+ }
+
+ /* 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");
+ }
+
+ /* 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;
+
+ 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 (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");
+ }
+ }
+
+ return 0;
+}
+
+static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
+{
+ int i, valid, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
+
+ if (ret)
+ return ret;
+
+ valid = tz->trips.critical.flags.valid |
+ tz->trips.hot.flags.valid |
+ tz->trips.passive.flags.valid;
+
+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
+ valid |= tz->trips.active[i].flags.valid;
+
+ if (!valid) {
+ pr_warn(FW_BUG "No valid trip found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* 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;
+ int result;
+
+ if (!tz)
+ return -EINVAL;
+
+ result = acpi_thermal_get_temperature(tz);
+ if (result)
+ return result;
+
+ *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)
+{
+ struct acpi_thermal *tz = thermal->devdata;
+ int i;
+
+ if (!tz || trip < 0)
+ return -EINVAL;
+
+ if (tz->trips.critical.flags.valid) {
+ if (!trip) {
+ *type = THERMAL_TRIP_CRITICAL;
+ return 0;
+ }
+ trip--;
+ }
+
+ if (tz->trips.hot.flags.valid) {
+ if (!trip) {
+ *type = THERMAL_TRIP_HOT;
+ return 0;
+ }
+ trip--;
+ }
+
+ if (tz->trips.passive.flags.valid) {
+ if (!trip) {
+ *type = THERMAL_TRIP_PASSIVE;
+ return 0;
+ }
+ trip--;
+ }
+
+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].flags.valid; i++) {
+ if (!trip) {
+ *type = THERMAL_TRIP_ACTIVE;
+ return 0;
+ }
+ trip--;
+ }
+
+ return -EINVAL;
+}
+
+static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
+ int trip, int *temp)
+{
+ struct acpi_thermal *tz = thermal->devdata;
+ int i;
+
+ if (!tz || trip < 0)
+ return -EINVAL;
+
+ 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--;
+ }
+
+ 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--;
+ }
+
+ 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--;
+ }
+
+ 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--;
+ }
+
+ return -EINVAL;
+}
+
+static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
+ int *temperature)
+{
+ struct acpi_thermal *tz = thermal->devdata;
+
+ if (tz->trips.critical.flags.valid) {
+ *temperature = deci_kelvin_to_millicelsius_with_offset(
+ tz->trips.critical.temperature,
+ tz->kelvin_offset);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int thermal_get_trend(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trend *trend)
+{
+ struct acpi_thermal *tz = thermal->devdata;
+ enum thermal_trip_type type;
+ int i;
+
+ if (thermal_get_trip_type(thermal, trip, &type))
+ 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;
+
+ if (temp > trip_temp) {
+ *trend = THERMAL_TREND_RAISING;
+ return 0;
+ } else {
+ /* Fall back on default trend */
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * 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);
+
+ if (i > 0)
+ *trend = THERMAL_TREND_RAISING;
+ else if (i < 0)
+ *trend = THERMAL_TREND_DROPPING;
+ else
+ *trend = THERMAL_TREND_STABLE;
+
+ return 0;
+}
+
+static void acpi_thermal_zone_device_hot(struct thermal_zone_device *thermal)
+{
+ struct acpi_thermal *tz = thermal->devdata;
+
+ acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
+ dev_name(&tz->device->dev),
+ ACPI_THERMAL_NOTIFY_HOT, 1);
+}
+
+static void acpi_thermal_zone_device_critical(struct thermal_zone_device *thermal)
+{
+ struct acpi_thermal *tz = thermal->devdata;
+
+ acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
+ dev_name(&tz->device->dev),
+ ACPI_THERMAL_NOTIFY_CRITICAL, 1);
+
+ thermal_zone_device_critical(thermal);
+}
+
+static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdev,
+ bool bind)
+{
+ struct acpi_device *device = cdev->devdata;
+ struct acpi_thermal *tz = thermal->devdata;
+ struct acpi_device *dev;
+ acpi_handle handle;
+ int i;
+ int j;
+ int trip = -1;
+ int result = 0;
+
+ if (tz->trips.critical.flags.valid)
+ trip++;
+
+ if (tz->trips.hot.flags.valid)
+ trip++;
+
+ 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_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;
+ }
+ }
+
+failed:
+ return result;
+}
+
+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,
+ .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)
+{
+ int trips = 0;
+ int result;
+ acpi_status status;
+ int i;
+
+ if (tz->trips.critical.flags.valid)
+ trips++;
+
+ if (tz->trips.hot.flags.valid)
+ trips++;
+
+ if (tz->trips.passive.flags.valid)
+ trips++;
+
+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].flags.valid;
+ i++, trips++);
+
+ 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);
+ else
+ tz->thermal_zone =
+ thermal_zone_device_register("acpitz", trips, 0, tz,
+ &acpi_thermal_zone_ops, NULL,
+ 0, tz->polling_frequency * 100);
+
+ if (IS_ERR(tz->thermal_zone))
+ return -ENODEV;
+
+ result = sysfs_create_link(&tz->device->dev.kobj,
+ &tz->thermal_zone->device.kobj, "thermal_zone");
+ 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;
+
+ dev_info(&tz->device->dev, "registered as thermal_zone%d\n",
+ tz->thermal_zone->id);
+
+ 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");
+unregister_tzd:
+ thermal_zone_device_unregister(tz->thermal_zone);
+
+ return result;
+}
+
+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_unregister(tz->thermal_zone);
+ tz->thermal_zone = NULL;
+ acpi_bus_detach_private_data(tz->device->handle);
+}
+
+
+/* --------------------------------------------------------------------------
+ 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)
+{
+ struct acpi_thermal *tz = acpi_driver_data(device);
+
+ if (!tz)
+ return;
+
+ switch (event) {
+ case ACPI_THERMAL_NOTIFY_TEMPERATURE:
+ 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);
+ break;
+ default:
+ acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
+ event);
+ break;
+ }
+}
+
+/*
+ * On some platforms, the AML code has dependency about
+ * the evaluating order of _TMP and _CRT/_HOT/_PSV/_ACx.
+ * 1. On HP Pavilion G4-1016tx, _TMP must be invoked after
+ * /_CRT/_HOT/_PSV/_ACx, or else system will be power off.
+ * 2. On HP Compaq 6715b/6715s, the return value of _PSV is 0
+ * if _TMP has never been evaluated.
+ *
+ * As this dependency is totally transparent to OS, evaluate
+ * all of them once, in the order of _CRT/_HOT/_PSV/_ACx,
+ * _TMP, before they are actually used.
+ */
+static void acpi_thermal_aml_dependency_fix(struct acpi_thermal *tz)
+{
+ acpi_handle handle = tz->device->handle;
+ unsigned long long value;
+ int i;
+
+ acpi_evaluate_integer(handle, "_CRT", NULL, &value);
+ acpi_evaluate_integer(handle, "_HOT", NULL, &value);
+ acpi_evaluate_integer(handle, "_PSV", NULL, &value);
+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
+ char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
+ acpi_status status;
+
+ status = acpi_evaluate_integer(handle, name, NULL, &value);
+ if (status == AE_NOT_FOUND)
+ break;
+ }
+ acpi_evaluate_integer(handle, "_TMP", NULL, &value);
+}
+
+static int acpi_thermal_get_info(struct acpi_thermal *tz)
+{
+ int result;
+
+ 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,
+ * some implementations use an offset of 273.1 and others use an offset of
+ * 273.2. Try to find out which one is being used, to present the most
+ * accurate and visually appealing number.
+ *
+ * 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)
+{
+ if (tz->trips.critical.flags.valid &&
+ (tz->trips.critical.temperature % 5) == 1)
+ tz->kelvin_offset = 273100;
+ else
+ tz->kelvin_offset = 273200;
+}
+
+static void acpi_thermal_check_fn(struct work_struct *work)
+{
+ struct acpi_thermal *tz = container_of(work, struct acpi_thermal,
+ thermal_check_work);
+
+ /*
+ * In general, it is not sufficient to check the pending bit, because
+ * subsequent instances of this function may be queued after one of them
+ * has started running (e.g. if _TMP sleeps). Avoid bailing out if just
+ * one of them is running, though, because it may have done the actual
+ * check some time ago, so allow at least one of them to block on the
+ * mutex while another one is running the update.
+ */
+ if (!refcount_dec_not_one(&tz->thermal_check_count))
+ return;
+
+ mutex_lock(&tz->thermal_check_lock);
+
+ thermal_zone_device_update(tz->thermal_zone, THERMAL_EVENT_UNSPECIFIED);
+
+ refcount_inc(&tz->thermal_check_count);
+
+ mutex_unlock(&tz->thermal_check_lock);
+}
+
+static int acpi_thermal_add(struct acpi_device *device)
+{
+ struct acpi_thermal *tz;
+ int result;
+
+ if (!device)
+ return -EINVAL;
+
+ tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL);
+ if (!tz)
+ 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);
+ device->driver_data = tz;
+
+ result = acpi_thermal_get_info(tz);
+ if (result)
+ goto free_memory;
+
+ acpi_thermal_guess_offset(tz);
+
+ result = acpi_thermal_register_thermal_zone(tz);
+ if (result)
+ goto free_memory;
+
+ refcount_set(&tz->thermal_check_count, 3);
+ mutex_init(&tz->thermal_check_lock);
+ 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;
+
+free_memory:
+ kfree(tz);
+end:
+ return result;
+}
+
+static int acpi_thermal_remove(struct acpi_device *device)
+{
+ struct acpi_thermal *tz;
+
+ if (!device || !acpi_driver_data(device))
+ return -EINVAL;
+
+ flush_workqueue(acpi_thermal_pm_queue);
+ tz = acpi_driver_data(device);
+
+ acpi_thermal_unregister_thermal_zone(tz);
+ kfree(tz);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_thermal_suspend(struct device *dev)
+{
+ /* Make sure the previously queued thermal check work has been done */
+ flush_workqueue(acpi_thermal_pm_queue);
+ return 0;
+}
+
+static int acpi_thermal_resume(struct device *dev)
+{
+ struct acpi_thermal *tz;
+ int i, j, power_state, result;
+
+ if (!dev)
+ return -EINVAL;
+
+ tz = acpi_driver_data(to_acpi_device(dev));
+ if (!tz)
+ return -EINVAL;
+
+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
+ if (!tz->trips.active[i].flags.valid)
+ 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;
+ }
+
+ acpi_queue_thermal_check(tz);
+
+ return AE_OK;
+}
+#endif
+
+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);
+ act = -1;
+ }
+ return 0;
+}
+static int thermal_nocrt(const struct dmi_system_id *d) {
+ pr_notice("%s detected: disabling all critical thermal trip point actions.\n",
+ d->ident);
+ crt = -1;
+ return 0;
+}
+static int thermal_tzp(const struct dmi_system_id *d) {
+ if (tzp == 0) {
+ pr_notice("%s detected: enabling thermal zone polling\n",
+ d->ident);
+ tzp = 300; /* 300 dS = 30 Seconds */
+ }
+ return 0;
+}
+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);
+ psv = -1;
+ }
+ return 0;
+}
+
+static const struct dmi_system_id thermal_dmi_table[] __initconst = {
+ /*
+ * Award BIOS on this AOpen makes thermal control almost worthless.
+ * http://bugzilla.kernel.org/show_bug.cgi?id=8842
+ */
+ {
+ .callback = thermal_act,
+ .ident = "AOpen i915GMm-HFS",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
+ DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
+ },
+ },
+ {
+ .callback = thermal_psv,
+ .ident = "AOpen i915GMm-HFS",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
+ DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
+ },
+ },
+ {
+ .callback = thermal_tzp,
+ .ident = "AOpen i915GMm-HFS",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
+ DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
+ },
+ },
+ {
+ .callback = thermal_nocrt,
+ .ident = "Gigabyte GA-7ZX",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
+ DMI_MATCH(DMI_BOARD_NAME, "7ZX"),
+ },
+ },
+ {}
+};
+
+static int __init acpi_thermal_init(void)
+{
+ int result;
+
+ dmi_check_system(thermal_dmi_table);
+
+ if (off) {
+ pr_notice("thermal control disabled\n");
+ return -ENODEV;
+ }
+
+ acpi_thermal_pm_queue = alloc_workqueue("acpi_thermal_pm",
+ WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
+ if (!acpi_thermal_pm_queue)
+ return -ENODEV;
+
+ result = acpi_bus_register_driver(&acpi_thermal_driver);
+ if (result < 0) {
+ destroy_workqueue(acpi_thermal_pm_queue);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit acpi_thermal_exit(void)
+{
+ acpi_bus_unregister_driver(&acpi_thermal_driver);
+ destroy_workqueue(acpi_thermal_pm_queue);
+}
+
+module_init(acpi_thermal_init);
+module_exit(acpi_thermal_exit);
diff --git a/drivers/acpi/tiny-power-button.c b/drivers/acpi/tiny-power-button.c
new file mode 100644
index 000000000..a19f0e4e6
--- /dev/null
+++ b/drivers/acpi/tiny-power-button.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/module.h>
+#include <linux/sched/signal.h>
+#include <linux/acpi.h>
+#include <acpi/button.h>
+
+MODULE_AUTHOR("Josh Triplett");
+MODULE_DESCRIPTION("ACPI Tiny Power Button Driver");
+MODULE_LICENSE("GPL");
+
+static int power_signal __read_mostly = CONFIG_ACPI_TINY_POWER_BUTTON_SIGNAL;
+module_param(power_signal, int, 0644);
+MODULE_PARM_DESC(power_signal, "Power button sends this signal to init");
+
+static const struct acpi_device_id tiny_power_button_device_ids[] = {
+ { ACPI_BUTTON_HID_POWER, 0 },
+ { ACPI_BUTTON_HID_POWERF, 0 },
+ { "", 0 },
+};
+MODULE_DEVICE_TABLE(acpi, tiny_power_button_device_ids);
+
+static int acpi_noop_add_remove(struct acpi_device *device)
+{
+ return 0;
+}
+
+static void acpi_tiny_power_button_notify(struct acpi_device *device, u32 event)
+{
+ kill_cad_pid(power_signal, 1);
+}
+
+static struct acpi_driver acpi_tiny_power_button_driver = {
+ .name = "tiny-power-button",
+ .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,
+ },
+};
+
+module_acpi_driver(acpi_tiny_power_button_driver);
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
new file mode 100644
index 000000000..2ea14648a
--- /dev/null
+++ b/drivers/acpi/utils.c
@@ -0,0 +1,1028 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * acpi_utils.c - ACPI Utility Functions ($Revision: 10 $)
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: utils: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/hardirq.h>
+#include <linux/acpi.h>
+#include <linux/dynamic_debug.h>
+
+#include "internal.h"
+#include "sleep.h"
+
+/* --------------------------------------------------------------------------
+ Object Evaluation Helpers
+ -------------------------------------------------------------------------- */
+static void acpi_util_eval_error(acpi_handle h, acpi_string p, acpi_status s)
+{
+ acpi_handle_debug(h, "Evaluate [%s]: %s\n", p, acpi_format_exception(s));
+}
+
+acpi_status
+acpi_extract_package(union acpi_object *package,
+ struct acpi_buffer *format, struct acpi_buffer *buffer)
+{
+ u32 size_required = 0;
+ u32 tail_offset = 0;
+ char *format_string = NULL;
+ u32 format_count = 0;
+ u32 i = 0;
+ u8 *head = NULL;
+ u8 *tail = NULL;
+
+
+ if (!package || (package->type != ACPI_TYPE_PACKAGE)
+ || (package->package.count < 1)) {
+ pr_debug("Invalid package argument\n");
+ return AE_BAD_PARAMETER;
+ }
+
+ if (!format || !format->pointer || (format->length < 1)) {
+ pr_debug("Invalid format argument\n");
+ return AE_BAD_PARAMETER;
+ }
+
+ if (!buffer) {
+ pr_debug("Invalid buffer argument\n");
+ return AE_BAD_PARAMETER;
+ }
+
+ format_count = (format->length / sizeof(char)) - 1;
+ if (format_count > package->package.count) {
+ pr_debug("Format specifies more objects [%d] than present [%d]\n",
+ format_count, package->package.count);
+ return AE_BAD_DATA;
+ }
+
+ format_string = format->pointer;
+
+ /*
+ * Calculate size_required.
+ */
+ for (i = 0; i < format_count; i++) {
+
+ union acpi_object *element = &(package->package.elements[i]);
+
+ switch (element->type) {
+
+ case ACPI_TYPE_INTEGER:
+ switch (format_string[i]) {
+ case 'N':
+ size_required += sizeof(u64);
+ tail_offset += sizeof(u64);
+ break;
+ case 'S':
+ size_required +=
+ sizeof(char *) + sizeof(u64) +
+ sizeof(char);
+ tail_offset += sizeof(char *);
+ break;
+ default:
+ pr_debug("Invalid package element [%d]: got number, expected [%c]\n",
+ i, format_string[i]);
+ return AE_BAD_DATA;
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+ switch (format_string[i]) {
+ case 'S':
+ size_required +=
+ sizeof(char *) +
+ (element->string.length * sizeof(char)) +
+ sizeof(char);
+ tail_offset += sizeof(char *);
+ break;
+ case 'B':
+ size_required +=
+ sizeof(u8 *) + element->buffer.length;
+ tail_offset += sizeof(u8 *);
+ break;
+ default:
+ pr_debug("Invalid package element [%d] got string/buffer, expected [%c]\n",
+ i, format_string[i]);
+ return AE_BAD_DATA;
+ }
+ break;
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ switch (format_string[i]) {
+ case 'R':
+ size_required += sizeof(void *);
+ tail_offset += sizeof(void *);
+ break;
+ default:
+ pr_debug("Invalid package element [%d] got reference, expected [%c]\n",
+ i, format_string[i]);
+ return AE_BAD_DATA;
+ }
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+ default:
+ pr_debug("Unsupported element at index=%d\n", i);
+ /* TBD: handle nested packages... */
+ return AE_SUPPORT;
+ }
+ }
+
+ /*
+ * Validate output buffer.
+ */
+ if (buffer->length == ACPI_ALLOCATE_BUFFER) {
+ buffer->pointer = ACPI_ALLOCATE_ZEROED(size_required);
+ if (!buffer->pointer)
+ return AE_NO_MEMORY;
+ buffer->length = size_required;
+ } else {
+ if (buffer->length < size_required) {
+ buffer->length = size_required;
+ return AE_BUFFER_OVERFLOW;
+ } else if (buffer->length != size_required ||
+ !buffer->pointer) {
+ return AE_BAD_PARAMETER;
+ }
+ }
+
+ head = buffer->pointer;
+ tail = buffer->pointer + tail_offset;
+
+ /*
+ * Extract package data.
+ */
+ for (i = 0; i < format_count; i++) {
+
+ u8 **pointer = NULL;
+ union acpi_object *element = &(package->package.elements[i]);
+
+ switch (element->type) {
+
+ case ACPI_TYPE_INTEGER:
+ switch (format_string[i]) {
+ case 'N':
+ *((u64 *) head) =
+ element->integer.value;
+ head += sizeof(u64);
+ break;
+ case 'S':
+ pointer = (u8 **) head;
+ *pointer = tail;
+ *((u64 *) tail) =
+ element->integer.value;
+ head += sizeof(u64 *);
+ tail += sizeof(u64);
+ /* NULL terminate string */
+ *tail = (char)0;
+ tail += sizeof(char);
+ break;
+ default:
+ /* Should never get here */
+ break;
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+ switch (format_string[i]) {
+ case 'S':
+ pointer = (u8 **) head;
+ *pointer = tail;
+ memcpy(tail, element->string.pointer,
+ element->string.length);
+ head += sizeof(char *);
+ tail += element->string.length * sizeof(char);
+ /* NULL terminate string */
+ *tail = (char)0;
+ tail += sizeof(char);
+ break;
+ case 'B':
+ pointer = (u8 **) head;
+ *pointer = tail;
+ memcpy(tail, element->buffer.pointer,
+ element->buffer.length);
+ head += sizeof(u8 *);
+ tail += element->buffer.length;
+ break;
+ default:
+ /* Should never get here */
+ break;
+ }
+ break;
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ switch (format_string[i]) {
+ case 'R':
+ *(void **)head =
+ (void *)element->reference.handle;
+ head += sizeof(void *);
+ break;
+ default:
+ /* Should never get here */
+ break;
+ }
+ break;
+ case ACPI_TYPE_PACKAGE:
+ /* TBD: handle nested packages... */
+ default:
+ /* Should never get here */
+ break;
+ }
+ }
+
+ return AE_OK;
+}
+
+EXPORT_SYMBOL(acpi_extract_package);
+
+acpi_status
+acpi_evaluate_integer(acpi_handle handle,
+ acpi_string pathname,
+ struct acpi_object_list *arguments, unsigned long long *data)
+{
+ acpi_status status = AE_OK;
+ union acpi_object element;
+ struct acpi_buffer buffer = { 0, NULL };
+
+ if (!data)
+ return AE_BAD_PARAMETER;
+
+ buffer.length = sizeof(union acpi_object);
+ buffer.pointer = &element;
+ status = acpi_evaluate_object(handle, pathname, arguments, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_util_eval_error(handle, pathname, status);
+ return status;
+ }
+
+ if (element.type != ACPI_TYPE_INTEGER) {
+ acpi_util_eval_error(handle, pathname, AE_BAD_DATA);
+ return AE_BAD_DATA;
+ }
+
+ *data = element.integer.value;
+
+ acpi_handle_debug(handle, "Return value [%llu]\n", *data);
+
+ return AE_OK;
+}
+
+EXPORT_SYMBOL(acpi_evaluate_integer);
+
+int acpi_get_local_address(acpi_handle handle, u32 *addr)
+{
+ unsigned long long adr;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
+ if (ACPI_FAILURE(status))
+ return -ENODATA;
+
+ *addr = (u32)adr;
+ return 0;
+}
+EXPORT_SYMBOL(acpi_get_local_address);
+
+#define ACPI_MAX_SUB_BUF_SIZE 9
+
+const char *acpi_get_subsystem_id(acpi_handle handle)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ acpi_status status;
+ const char *sub;
+ size_t len;
+
+ status = acpi_evaluate_object(handle, METHOD_NAME__SUB, NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_debug(handle, "Reading ACPI _SUB failed: %#x\n", status);
+ return ERR_PTR(-ENODATA);
+ }
+
+ obj = buffer.pointer;
+ if (obj->type == ACPI_TYPE_STRING) {
+ len = strlen(obj->string.pointer);
+ if (len < ACPI_MAX_SUB_BUF_SIZE && len > 0) {
+ sub = kstrdup(obj->string.pointer, GFP_KERNEL);
+ if (!sub)
+ sub = ERR_PTR(-ENOMEM);
+ } else {
+ acpi_handle_err(handle, "ACPI _SUB Length %zu is Invalid\n", len);
+ sub = ERR_PTR(-ENODATA);
+ }
+ } else {
+ acpi_handle_warn(handle, "Warning ACPI _SUB did not return a string\n");
+ sub = ERR_PTR(-ENODATA);
+ }
+
+ acpi_os_free(buffer.pointer);
+
+ return sub;
+}
+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)
+{
+ 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;
+
+
+ if (!list) {
+ return AE_BAD_PARAMETER;
+ }
+
+ /* Evaluate object. */
+
+ status = acpi_evaluate_object(handle, pathname, arguments, &buffer);
+ if (ACPI_FAILURE(status))
+ goto end;
+
+ 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 (package->package.count > ACPI_MAX_HANDLES) {
+ kfree(package);
+ return AE_NO_MEMORY;
+ }
+ list->count = package->package.count;
+
+ /* Extract package data. */
+
+ for (i = 0; i < list->count; 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->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);
+ }
+
+ kfree(buffer.pointer);
+
+ return status;
+}
+
+EXPORT_SYMBOL(acpi_evaluate_reference);
+
+acpi_status
+acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *output;
+
+ status = acpi_evaluate_object(handle, "_PLD", NULL, &buffer);
+
+ if (ACPI_FAILURE(status))
+ return status;
+
+ output = buffer.pointer;
+
+ if (!output || output->type != ACPI_TYPE_PACKAGE
+ || !output->package.count
+ || output->package.elements[0].type != ACPI_TYPE_BUFFER
+ || output->package.elements[0].buffer.length < ACPI_PLD_REV1_BUFFER_SIZE) {
+ status = AE_TYPE;
+ goto out;
+ }
+
+ status = acpi_decode_pld_buffer(
+ output->package.elements[0].buffer.pointer,
+ output->package.elements[0].buffer.length,
+ pld);
+
+out:
+ kfree(buffer.pointer);
+ return status;
+}
+EXPORT_SYMBOL(acpi_get_physical_device_location);
+
+/**
+ * acpi_evaluate_ost: Evaluate _OST for hotplug operations
+ * @handle: ACPI device handle
+ * @source_event: source event code
+ * @status_code: status code
+ * @status_buf: optional detailed information (NULL if none)
+ *
+ * Evaluate _OST for hotplug operations. All ACPI hotplug handlers
+ * must call this function when evaluating _OST for hotplug operations.
+ * When the platform does not support _OST, this function has no effect.
+ */
+acpi_status
+acpi_evaluate_ost(acpi_handle handle, u32 source_event, u32 status_code,
+ struct acpi_buffer *status_buf)
+{
+ union acpi_object params[3] = {
+ {.type = ACPI_TYPE_INTEGER,},
+ {.type = ACPI_TYPE_INTEGER,},
+ {.type = ACPI_TYPE_BUFFER,}
+ };
+ struct acpi_object_list arg_list = {3, params};
+
+ params[0].integer.value = source_event;
+ params[1].integer.value = status_code;
+ if (status_buf != NULL) {
+ params[2].buffer.pointer = status_buf->pointer;
+ params[2].buffer.length = status_buf->length;
+ } else {
+ params[2].buffer.pointer = NULL;
+ params[2].buffer.length = 0;
+ }
+
+ return acpi_evaluate_object(handle, "_OST", &arg_list, NULL);
+}
+EXPORT_SYMBOL(acpi_evaluate_ost);
+
+/**
+ * acpi_handle_path: Return the object path of handle
+ * @handle: ACPI device handle
+ *
+ * Caller must free the returned buffer
+ */
+static char *acpi_handle_path(acpi_handle handle)
+{
+ struct acpi_buffer buffer = {
+ .length = ACPI_ALLOCATE_BUFFER,
+ .pointer = NULL
+ };
+
+ if (in_interrupt() ||
+ acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer) != AE_OK)
+ return NULL;
+ return buffer.pointer;
+}
+
+/**
+ * acpi_handle_printk: Print message with ACPI prefix and object path
+ * @level: log level
+ * @handle: ACPI device handle
+ * @fmt: format string
+ *
+ * This function is called through acpi_handle_<level> macros and prints
+ * a message with ACPI prefix and object path. This function acquires
+ * the global namespace mutex to obtain an object path. In interrupt
+ * context, it shows the object path as <n/a>.
+ */
+void
+acpi_handle_printk(const char *level, acpi_handle handle, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ const char *path;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ path = acpi_handle_path(handle);
+ printk("%sACPI: %s: %pV", level, path ? path : "<n/a>" , &vaf);
+
+ va_end(args);
+ kfree(path);
+}
+EXPORT_SYMBOL(acpi_handle_printk);
+
+#if defined(CONFIG_DYNAMIC_DEBUG)
+/**
+ * __acpi_handle_debug: pr_debug with ACPI prefix and object path
+ * @descriptor: Dynamic Debug descriptor
+ * @handle: ACPI device handle
+ * @fmt: format string
+ *
+ * This function is called through acpi_handle_debug macro and debug
+ * prints a message with ACPI prefix and object path. This function
+ * acquires the global namespace mutex to obtain an object path. In
+ * interrupt context, it shows the object path as <n/a>.
+ */
+void
+__acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle,
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ const char *path;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ path = acpi_handle_path(handle);
+ __dynamic_pr_debug(descriptor, "ACPI: %s: %pV", path ? path : "<n/a>", &vaf);
+
+ va_end(args);
+ kfree(path);
+}
+EXPORT_SYMBOL(__acpi_handle_debug);
+#endif
+
+/**
+ * acpi_evaluation_failure_warn - Log evaluation failure warning.
+ * @handle: Parent object handle.
+ * @name: Name of the object whose evaluation has failed.
+ * @status: Status value returned by the failing object evaluation.
+ */
+void acpi_evaluation_failure_warn(acpi_handle handle, const char *name,
+ acpi_status status)
+{
+ acpi_handle_warn(handle, "%s evaluation failed: %s\n", name,
+ acpi_format_exception(status));
+}
+EXPORT_SYMBOL_GPL(acpi_evaluation_failure_warn);
+
+/**
+ * acpi_has_method: Check whether @handle has a method named @name
+ * @handle: ACPI device handle
+ * @name: name of object or method
+ *
+ * Check whether @handle has a method named @name.
+ */
+bool acpi_has_method(acpi_handle handle, char *name)
+{
+ acpi_handle tmp;
+
+ return ACPI_SUCCESS(acpi_get_handle(handle, name, &tmp));
+}
+EXPORT_SYMBOL(acpi_has_method);
+
+acpi_status acpi_execute_simple_method(acpi_handle handle, char *method,
+ u64 arg)
+{
+ union acpi_object obj = { .type = ACPI_TYPE_INTEGER };
+ struct acpi_object_list arg_list = { .count = 1, .pointer = &obj, };
+
+ obj.integer.value = arg;
+
+ return acpi_evaluate_object(handle, method, &arg_list, NULL);
+}
+EXPORT_SYMBOL(acpi_execute_simple_method);
+
+/**
+ * acpi_evaluate_ej0: Evaluate _EJ0 method for hotplug operations
+ * @handle: ACPI device handle
+ *
+ * Evaluate device's _EJ0 method for hotplug operations.
+ */
+acpi_status acpi_evaluate_ej0(acpi_handle handle)
+{
+ acpi_status status;
+
+ status = acpi_execute_simple_method(handle, "_EJ0", 1);
+ if (status == AE_NOT_FOUND)
+ acpi_handle_warn(handle, "No _EJ0 support for device\n");
+ else if (ACPI_FAILURE(status))
+ acpi_handle_warn(handle, "Eject failed (0x%x)\n", status);
+
+ return status;
+}
+
+/**
+ * acpi_evaluate_lck: Evaluate _LCK method to lock/unlock device
+ * @handle: ACPI device handle
+ * @lock: lock device if non-zero, otherwise unlock device
+ *
+ * Evaluate device's _LCK method if present to lock/unlock device
+ */
+acpi_status acpi_evaluate_lck(acpi_handle handle, int lock)
+{
+ acpi_status status;
+
+ status = acpi_execute_simple_method(handle, "_LCK", !!lock);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ if (lock)
+ acpi_handle_warn(handle,
+ "Locking device failed (0x%x)\n", status);
+ else
+ acpi_handle_warn(handle,
+ "Unlocking device failed (0x%x)\n", status);
+ }
+
+ return status;
+}
+
+/**
+ * acpi_evaluate_reg: Evaluate _REG method to register OpRegion presence
+ * @handle: ACPI device handle
+ * @space_id: ACPI address space id to register OpRegion presence for
+ * @function: Parameter to pass to _REG one of ACPI_REG_CONNECT or
+ * ACPI_REG_DISCONNECT
+ *
+ * Evaluate device's _REG method to register OpRegion presence.
+ */
+acpi_status acpi_evaluate_reg(acpi_handle handle, u8 space_id, u32 function)
+{
+ struct acpi_object_list arg_list;
+ union acpi_object params[2];
+
+ params[0].type = ACPI_TYPE_INTEGER;
+ params[0].integer.value = space_id;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = function;
+ arg_list.count = 2;
+ arg_list.pointer = params;
+
+ return acpi_evaluate_object(handle, "_REG", &arg_list, NULL);
+}
+EXPORT_SYMBOL(acpi_evaluate_reg);
+
+/**
+ * acpi_evaluate_dsm - evaluate device's _DSM method
+ * @handle: ACPI device handle
+ * @guid: GUID of requested functions, should be 16 bytes
+ * @rev: revision number of requested function
+ * @func: requested function number
+ * @argv4: the function specific parameter
+ *
+ * Evaluate device's _DSM method with specified GUID, revision id and
+ * function number. Caller needs to free the returned object.
+ *
+ * Though ACPI defines the fourth parameter for _DSM should be a package,
+ * some old BIOSes do expect a buffer or an integer etc.
+ */
+union acpi_object *
+acpi_evaluate_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 func,
+ union acpi_object *argv4)
+{
+ acpi_status ret;
+ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object params[4];
+ struct acpi_object_list input = {
+ .count = 4,
+ .pointer = params,
+ };
+
+ params[0].type = ACPI_TYPE_BUFFER;
+ params[0].buffer.length = 16;
+ params[0].buffer.pointer = (u8 *)guid;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = rev;
+ params[2].type = ACPI_TYPE_INTEGER;
+ params[2].integer.value = func;
+ if (argv4) {
+ params[3] = *argv4;
+ } else {
+ params[3].type = ACPI_TYPE_PACKAGE;
+ params[3].package.count = 0;
+ params[3].package.elements = NULL;
+ }
+
+ ret = acpi_evaluate_object(handle, "_DSM", &input, &buf);
+ if (ACPI_SUCCESS(ret))
+ return (union acpi_object *)buf.pointer;
+
+ if (ret != AE_NOT_FOUND)
+ acpi_handle_warn(handle,
+ "failed to evaluate _DSM %pUb (0x%x)\n", guid, ret);
+
+ return NULL;
+}
+EXPORT_SYMBOL(acpi_evaluate_dsm);
+
+/**
+ * acpi_check_dsm - check if _DSM method supports requested functions.
+ * @handle: ACPI device handle
+ * @guid: GUID of requested functions, should be 16 bytes at least
+ * @rev: revision number of requested functions
+ * @funcs: bitmap of requested functions
+ *
+ * Evaluate device's _DSM method to check whether it supports requested
+ * functions. Currently only support 64 functions at maximum, should be
+ * enough for now.
+ */
+bool acpi_check_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 funcs)
+{
+ int i;
+ u64 mask = 0;
+ union acpi_object *obj;
+
+ if (funcs == 0)
+ return false;
+
+ obj = acpi_evaluate_dsm(handle, guid, rev, 0, NULL);
+ if (!obj)
+ return false;
+
+ /* For compatibility, old BIOSes may return an integer */
+ if (obj->type == ACPI_TYPE_INTEGER)
+ mask = obj->integer.value;
+ else if (obj->type == ACPI_TYPE_BUFFER)
+ for (i = 0; i < obj->buffer.length && i < 8; i++)
+ mask |= (((u64)obj->buffer.pointer[i]) << (i * 8));
+ ACPI_FREE(obj);
+
+ /*
+ * Bit 0 indicates whether there's support for any functions other than
+ * function 0 for the specified GUID and revision.
+ */
+ if ((mask & 0x1) && (mask & funcs) == funcs)
+ return true;
+
+ return false;
+}
+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.
+ *
+ * Matches HID and UID in @adev with given @hid2 and @uid2.
+ * Returns true if matches.
+ */
+bool acpi_dev_hid_uid_match(struct acpi_device *adev,
+ const char *hid2, const char *uid2)
+{
+ const char *hid1 = acpi_device_hid(adev);
+ const char *uid1 = acpi_device_uid(adev);
+
+ if (strcmp(hid1, hid2))
+ return false;
+
+ if (!uid2)
+ return true;
+
+ return uid1 && !strcmp(uid1, uid2);
+}
+EXPORT_SYMBOL(acpi_dev_hid_uid_match);
+
+/**
+ * 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.
+ *
+ * Returns 0 on success, or negative error code otherwise.
+ */
+int acpi_dev_uid_to_integer(struct acpi_device *adev, u64 *integer)
+{
+ const char *uid;
+
+ if (!adev)
+ return -ENODEV;
+
+ uid = acpi_device_uid(adev);
+ if (!uid)
+ return -ENODATA;
+
+ return kstrtou64(uid, 0, integer);
+}
+EXPORT_SYMBOL(acpi_dev_uid_to_integer);
+
+/**
+ * acpi_dev_found - Detect presence of a given ACPI device in the namespace.
+ * @hid: Hardware ID of the device.
+ *
+ * Return %true if the device was present at the moment of invocation.
+ * Note that if the device is pluggable, it may since have disappeared.
+ *
+ * For this function to work, acpi_bus_scan() must have been executed
+ * which happens in the subsys_initcall() subsection. Hence, do not
+ * call from a subsys_initcall() or earlier (use acpi_get_devices()
+ * instead). Calling from module_init() is fine (which is synonymous
+ * with device_initcall()).
+ */
+bool acpi_dev_found(const char *hid)
+{
+ struct acpi_device_bus_id *acpi_device_bus_id;
+ bool found = false;
+
+ mutex_lock(&acpi_device_lock);
+ list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node)
+ if (!strcmp(acpi_device_bus_id->bus_id, hid)) {
+ found = true;
+ break;
+ }
+ mutex_unlock(&acpi_device_lock);
+
+ return found;
+}
+EXPORT_SYMBOL(acpi_dev_found);
+
+struct acpi_dev_match_info {
+ struct acpi_device_id hid[2];
+ const char *uid;
+ s64 hrv;
+};
+
+static int acpi_dev_match_cb(struct device *dev, const void *data)
+{
+ struct acpi_device *adev = to_acpi_device(dev);
+ const struct acpi_dev_match_info *match = data;
+ unsigned long long hrv;
+ acpi_status status;
+
+ if (acpi_match_device_ids(adev, match->hid))
+ return 0;
+
+ if (match->uid && (!adev->pnp.unique_id ||
+ strcmp(adev->pnp.unique_id, match->uid)))
+ return 0;
+
+ if (match->hrv == -1)
+ return 1;
+
+ status = acpi_evaluate_integer(adev->handle, "_HRV", NULL, &hrv);
+ if (ACPI_FAILURE(status))
+ return 0;
+
+ return hrv == match->hrv;
+}
+
+/**
+ * acpi_dev_present - Detect that a given ACPI device is present
+ * @hid: Hardware ID of the device.
+ * @uid: Unique ID of the device, pass NULL to not check _UID
+ * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
+ *
+ * Return %true if a matching device was present at the moment of invocation.
+ * Note that if the device is pluggable, it may since have disappeared.
+ *
+ * Note that unlike acpi_dev_found() this function checks the status
+ * of the device. So for devices which are present in the DSDT, but
+ * which are disabled (their _STA callback returns 0) this function
+ * will return false.
+ *
+ * For this function to work, acpi_bus_scan() must have been executed
+ * which happens in the subsys_initcall() subsection. Hence, do not
+ * call from a subsys_initcall() or earlier (use acpi_get_devices()
+ * instead). Calling from module_init() is fine (which is synonymous
+ * with device_initcall()).
+ */
+bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
+{
+ struct acpi_dev_match_info match = {};
+ struct device *dev;
+
+ strscpy(match.hid[0].id, hid, sizeof(match.hid[0].id));
+ match.uid = uid;
+ match.hrv = hrv;
+
+ dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
+ put_device(dev);
+ return !!dev;
+}
+EXPORT_SYMBOL(acpi_dev_present);
+
+/**
+ * acpi_dev_get_next_match_dev - Return the next match of ACPI device
+ * @adev: Pointer to the previous ACPI device matching this @hid, @uid and @hrv
+ * @hid: Hardware ID of the device.
+ * @uid: Unique ID of the device, pass NULL to not check _UID
+ * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
+ *
+ * Return the next match of ACPI device if another matching device was present
+ * at the moment of invocation, or NULL otherwise.
+ *
+ * The caller is responsible for invoking acpi_dev_put() on the returned device.
+ * On the other hand the function invokes acpi_dev_put() on the given @adev
+ * assuming that its reference counter had been increased beforehand.
+ *
+ * See additional information in acpi_dev_present() as well.
+ */
+struct acpi_device *
+acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv)
+{
+ struct device *start = adev ? &adev->dev : NULL;
+ struct acpi_dev_match_info match = {};
+ struct device *dev;
+
+ strscpy(match.hid[0].id, hid, sizeof(match.hid[0].id));
+ match.uid = uid;
+ match.hrv = hrv;
+
+ dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
+ acpi_dev_put(adev);
+ return dev ? to_acpi_device(dev) : NULL;
+}
+EXPORT_SYMBOL(acpi_dev_get_next_match_dev);
+
+/**
+ * acpi_dev_get_first_match_dev - Return the first match of ACPI device
+ * @hid: Hardware ID of the device.
+ * @uid: Unique ID of the device, pass NULL to not check _UID
+ * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
+ *
+ * Return the first match of ACPI device if a matching device was present
+ * at the moment of invocation, or NULL otherwise.
+ *
+ * The caller is responsible for invoking acpi_dev_put() on the returned device.
+ *
+ * See additional information in acpi_dev_present() as well.
+ */
+struct acpi_device *
+acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
+{
+ return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
+}
+EXPORT_SYMBOL(acpi_dev_get_first_match_dev);
+
+/**
+ * acpi_reduced_hardware - Return if this is an ACPI-reduced-hw machine
+ *
+ * Return true when running on an ACPI-reduced-hw machine, false otherwise.
+ */
+bool acpi_reduced_hardware(void)
+{
+ return acpi_gbl_reduced_hardware;
+}
+EXPORT_SYMBOL_GPL(acpi_reduced_hardware);
+
+/*
+ * acpi_backlight= handling, this is done here rather then in video_detect.c
+ * because __setup cannot be used in modules.
+ */
+char acpi_video_backlight_string[16];
+EXPORT_SYMBOL(acpi_video_backlight_string);
+
+static int __init acpi_backlight(char *str)
+{
+ strscpy(acpi_video_backlight_string, str,
+ sizeof(acpi_video_backlight_string));
+ return 1;
+}
+__setup("acpi_backlight=", acpi_backlight);
+
+/**
+ * acpi_match_platform_list - Check if the system matches with a given list
+ * @plat: pointer to acpi_platform_list table terminated by a NULL entry
+ *
+ * Return the matched index if the system is found in the platform list.
+ * Otherwise, return a negative error code.
+ */
+int acpi_match_platform_list(const struct acpi_platform_list *plat)
+{
+ struct acpi_table_header hdr;
+ int idx = 0;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ for (; plat->oem_id[0]; plat++, idx++) {
+ if (ACPI_FAILURE(acpi_get_table_header(plat->table, 0, &hdr)))
+ continue;
+
+ if (strncmp(plat->oem_id, hdr.oem_id, ACPI_OEM_ID_SIZE))
+ continue;
+
+ if (strncmp(plat->oem_table_id, hdr.oem_table_id, ACPI_OEM_TABLE_ID_SIZE))
+ continue;
+
+ if ((plat->pred == all_versions) ||
+ (plat->pred == less_than_or_equal && hdr.oem_revision <= plat->oem_revision) ||
+ (plat->pred == greater_than_or_equal && hdr.oem_revision >= plat->oem_revision) ||
+ (plat->pred == equal && hdr.oem_revision == plat->oem_revision))
+ return idx;
+ }
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL(acpi_match_platform_list);
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
new file mode 100644
index 000000000..60b0128a1
--- /dev/null
+++ b/drivers/acpi/video_detect.c
@@ -0,0 +1,875 @@
+/*
+ * Copyright (C) 2015 Red Hat Inc.
+ * Hans de Goede <hdegoede@redhat.com>
+ * Copyright (C) 2008 SuSE Linux Products GmbH
+ * Thomas Renninger <trenn@suse.de>
+ *
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * video_detect.c:
+ * After PCI devices are glued with ACPI devices
+ * acpi_get_pci_dev() can be called to identify ACPI graphics
+ * devices for which a real graphics card is plugged in
+ *
+ * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B)
+ * are available, video.ko should be used to handle the device.
+ *
+ * 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. 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
+ * always return acpi_backlight_vendor.
+ */
+
+#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>
+
+static enum acpi_backlight_type acpi_backlight_cmdline = acpi_backlight_undef;
+static enum acpi_backlight_type acpi_backlight_dmi = acpi_backlight_undef;
+
+static void acpi_video_parse_cmdline(void)
+{
+ if (!strcmp("vendor", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_vendor;
+ if (!strcmp("video", acpi_video_backlight_string))
+ 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("none", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_none;
+}
+
+static acpi_status
+find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ struct acpi_device *acpi_dev = acpi_fetch_acpi_dev(handle);
+ long *cap = context;
+ struct pci_dev *dev;
+
+ static const struct acpi_device_id video_ids[] = {
+ {ACPI_VIDEO_HID, 0},
+ {"", 0},
+ };
+
+ if (acpi_dev && !acpi_match_device_ids(acpi_dev, video_ids)) {
+ dev = acpi_get_pci_dev(handle);
+ if (!dev)
+ return AE_OK;
+ pci_dev_put(dev);
+ *cap |= acpi_is_video_device(handle);
+ }
+ 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)
+{
+ acpi_backlight_dmi = acpi_backlight_vendor;
+ return 0;
+}
+
+static int video_detect_force_video(const struct dmi_system_id *d)
+{
+ acpi_backlight_dmi = acpi_backlight_video;
+ return 0;
+}
+
+static int video_detect_force_native(const struct dmi_system_id *d)
+{
+ acpi_backlight_dmi = acpi_backlight_native;
+ return 0;
+}
+
+static int video_detect_portege_r100(const struct dmi_system_id *d)
+{
+ 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[] = {
+ /*
+ * 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,
+ /* Acer KAV80 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "KAV80"),
+ },
+ },
+ {
+ .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 = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UL30A"),
+ },
+ },
+ {
+ .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,
+ /* 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"),
+ },
+ },
+ {
+ .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"),
+ },
+ },
+ {
+ .callback = video_detect_force_vendor,
+ /* Xiaomi Mi Pad 2 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"),
+ },
+ },
+
+ /*
+ * 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, "PCG-FRV35"),
+ },
+ },
+
+ /*
+ * 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"),
+ },
+ },
+
+ /*
+ * These models have a working acpi_video backlight control, and using
+ * native backlight causes a regression where backlight does not work
+ * when userspace is not handling brightness key events. Disable
+ * native_backlight on these to fix this:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=81691
+ */
+ {
+ .callback = video_detect_force_video,
+ /* ThinkPad T420 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"),
+ },
+ },
+ {
+ .callback = video_detect_force_video,
+ /* ThinkPad T520 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"),
+ },
+ },
+ {
+ .callback = video_detect_force_video,
+ /* ThinkPad X201s */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"),
+ },
+ },
+ {
+ .callback = video_detect_force_video,
+ /* ThinkPad X201T */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201T"),
+ },
+ },
+
+ /* The native backlight controls do not work on some older machines */
+ {
+ /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */
+ .callback = video_detect_force_video,
+ /* HP ENVY 15 Notebook */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"),
+ },
+ },
+ {
+ .callback = video_detect_force_video,
+ /* SAMSUNG 870Z5E/880Z5E/680Z5E */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"),
+ },
+ },
+ {
+ .callback = video_detect_force_video,
+ /* SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "370R4E/370R4V/370R5E/3570RE/370R5V"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */
+ .callback = video_detect_force_video,
+ /* SAMSUNG 3570R/370R/470R/450R/510R/4450RV */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "3570R/370R/470R/450R/510R/4450RV"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1557060 */
+ .callback = video_detect_force_video,
+ /* SAMSUNG 670Z5E */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "670Z5E"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */
+ .callback = video_detect_force_video,
+ /* SAMSUNG 730U3E/740U3E */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"),
+ },
+ },
+ {
+ /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */
+ .callback = video_detect_force_video,
+ /* SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "900X3C/900X3D/900X3E/900X4C/900X4D"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1272633 */
+ .callback = video_detect_force_video,
+ /* Dell XPS14 L421X */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */
+ .callback = video_detect_force_video,
+ /* Dell XPS15 L521X */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"),
+ },
+ },
+ {
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=108971 */
+ .callback = video_detect_force_video,
+ /* SAMSUNG 530U4E/540U4E */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "530U4E/540U4E"),
+ },
+ },
+ /* https://bugs.launchpad.net/bugs/1894667 */
+ {
+ .callback = video_detect_force_video,
+ /* HP 635 Notebook */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP 635 Notebook PC"),
+ },
+ },
+
+ /* Non win8 machines which need native backlight nevertheless */
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1201530 */
+ .callback = video_detect_force_native,
+ /* Lenovo Ideapad S405 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_BOARD_NAME, "Lenovo IdeaPad S405"),
+ },
+ },
+ {
+ /* 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_VERSION, "Ideapad Z570"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Lenovo E41-25 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "81FS"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Lenovo E41-45 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82BK"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */
+ .callback = video_detect_force_native,
+ /* Apple MacBook Pro 12,1 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Dell Inspiron N4010 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N4010"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Dell Vostro V131 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1123661 */
+ .callback = video_detect_force_native,
+ /* Dell XPS 17 L702X */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L702X"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Dell Precision 7510 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Precision 7510"),
+ },
+ },
+ {
+ .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"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"),
+ DMI_MATCH(DMI_BOARD_NAME, "JV50"),
+ },
+ },
+ {
+ /* 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 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5735Z"),
+ DMI_MATCH(DMI_BOARD_NAME, "BA51_MV"),
+ },
+ },
+ {
+ /* 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 = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GA502"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* ASUSTeK COMPUTER INC. GA503 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GA503"),
+ },
+ },
+ {
+ .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,
+ /* 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,
+ /* 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,
+ /* 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"),
+ },
+ },
+
+ /*
+ * 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.
+ */
+ {
+ /* 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,
+ /* Toshiba Satellite/Portégé Z830 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Z830"),
+ },
+ },
+
+ /*
+ * 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,
+ /* Dell G15 5515 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 15 3535"),
+ },
+ },
+ { },
+};
+
+static bool google_cros_ec_present(void)
+{
+ return acpi_dev_found("GOOG0004") || acpi_dev_found("GOOG000C");
+}
+
+/*
+ * 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)
+{
+ 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.
+ */
+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 native_available;
+ static bool init_done;
+ static long video_caps;
+
+ /* Parse cmdline, dmi and acpi only once */
+ mutex_lock(&init_mutex);
+ if (!init_done) {
+ acpi_video_parse_cmdline();
+ dmi_check_system(video_detect_dmi_table);
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, find_video, NULL,
+ &video_caps, NULL);
+ nvidia_wmi_ec_present = nvidia_wmi_ec_supported();
+ apple_gmux_present = apple_gmux_detect(NULL, NULL);
+ 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 (auto_detect)
+ *auto_detect = true;
+
+ /* Special cases such as nvidia_wmi_ec and apple gmux. */
+ if (nvidia_wmi_ec_present)
+ return acpi_backlight_nvidia_wmi_ec;
+
+ if (apple_gmux_present)
+ return acpi_backlight_apple_gmux;
+
+ /* 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;
+
+ /* 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
new file mode 100644
index 000000000..c8025921c
--- /dev/null
+++ b/drivers/acpi/viot.c
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Virtual I/O topology
+ *
+ * The Virtual I/O Translation Table (VIOT) describes the topology of
+ * para-virtual IOMMUs and the endpoints they manage. The OS uses it to
+ * initialize devices in the right order, preventing endpoints from issuing DMA
+ * before their IOMMU is ready.
+ *
+ * When binding a driver to a device, before calling the device driver's probe()
+ * method, the driver infrastructure calls dma_configure(). At that point the
+ * VIOT driver looks for an IOMMU associated to the device in the VIOT table.
+ * If an IOMMU exists and has been initialized, the VIOT driver initializes the
+ * device's IOMMU fwspec, allowing the DMA infrastructure to invoke the IOMMU
+ * ops when the device driver configures DMA mappings. If an IOMMU exists and
+ * hasn't yet been initialized, VIOT returns -EPROBE_DEFER to postpone probing
+ * the device until the IOMMU is available.
+ */
+#define pr_fmt(fmt) "ACPI: VIOT: " fmt
+
+#include <linux/acpi_viot.h>
+#include <linux/fwnode.h>
+#include <linux/iommu.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+struct viot_iommu {
+ /* Node offset within the table */
+ unsigned int offset;
+ struct fwnode_handle *fwnode;
+ struct list_head list;
+};
+
+struct viot_endpoint {
+ union {
+ /* PCI range */
+ struct {
+ u16 segment_start;
+ u16 segment_end;
+ u16 bdf_start;
+ u16 bdf_end;
+ };
+ /* MMIO */
+ u64 address;
+ };
+ u32 endpoint_id;
+ struct viot_iommu *viommu;
+ struct list_head list;
+};
+
+static struct acpi_table_viot *viot;
+static LIST_HEAD(viot_iommus);
+static LIST_HEAD(viot_pci_ranges);
+static LIST_HEAD(viot_mmio_endpoints);
+
+static int __init viot_check_bounds(const struct acpi_viot_header *hdr)
+{
+ struct acpi_viot_header *start, *end, *hdr_end;
+
+ start = ACPI_ADD_PTR(struct acpi_viot_header, viot,
+ max_t(size_t, sizeof(*viot), viot->node_offset));
+ end = ACPI_ADD_PTR(struct acpi_viot_header, viot, viot->header.length);
+ hdr_end = ACPI_ADD_PTR(struct acpi_viot_header, hdr, sizeof(*hdr));
+
+ if (hdr < start || hdr_end > end) {
+ pr_err(FW_BUG "Node pointer overflows\n");
+ return -EOVERFLOW;
+ }
+ if (hdr->length < sizeof(*hdr)) {
+ pr_err(FW_BUG "Empty node\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int __init viot_get_pci_iommu_fwnode(struct viot_iommu *viommu,
+ u16 segment, u16 bdf)
+{
+ struct pci_dev *pdev;
+ struct fwnode_handle *fwnode;
+
+ pdev = pci_get_domain_bus_and_slot(segment, PCI_BUS_NUM(bdf),
+ bdf & 0xff);
+ if (!pdev) {
+ pr_err("Could not find PCI IOMMU\n");
+ return -ENODEV;
+ }
+
+ fwnode = dev_fwnode(&pdev->dev);
+ if (!fwnode) {
+ /*
+ * PCI devices aren't necessarily described by ACPI. Create a
+ * fwnode so the IOMMU subsystem can identify this device.
+ */
+ fwnode = acpi_alloc_fwnode_static();
+ if (!fwnode) {
+ pci_dev_put(pdev);
+ return -ENOMEM;
+ }
+ set_primary_fwnode(&pdev->dev, fwnode);
+ }
+ viommu->fwnode = dev_fwnode(&pdev->dev);
+ pci_dev_put(pdev);
+ return 0;
+}
+
+static int __init viot_get_mmio_iommu_fwnode(struct viot_iommu *viommu,
+ u64 address)
+{
+ struct acpi_device *adev;
+ struct resource res = {
+ .start = address,
+ .end = address,
+ .flags = IORESOURCE_MEM,
+ };
+
+ adev = acpi_resource_consumer(&res);
+ if (!adev) {
+ pr_err("Could not find MMIO IOMMU\n");
+ return -EINVAL;
+ }
+ viommu->fwnode = &adev->fwnode;
+ return 0;
+}
+
+static struct viot_iommu * __init viot_get_iommu(unsigned int offset)
+{
+ int ret;
+ struct viot_iommu *viommu;
+ struct acpi_viot_header *hdr = ACPI_ADD_PTR(struct acpi_viot_header,
+ viot, offset);
+ union {
+ struct acpi_viot_virtio_iommu_pci pci;
+ struct acpi_viot_virtio_iommu_mmio mmio;
+ } *node = (void *)hdr;
+
+ list_for_each_entry(viommu, &viot_iommus, list)
+ if (viommu->offset == offset)
+ return viommu;
+
+ if (viot_check_bounds(hdr))
+ return NULL;
+
+ viommu = kzalloc(sizeof(*viommu), GFP_KERNEL);
+ if (!viommu)
+ return NULL;
+
+ viommu->offset = offset;
+ switch (hdr->type) {
+ case ACPI_VIOT_NODE_VIRTIO_IOMMU_PCI:
+ if (hdr->length < sizeof(node->pci))
+ goto err_free;
+
+ ret = viot_get_pci_iommu_fwnode(viommu, node->pci.segment,
+ node->pci.bdf);
+ break;
+ case ACPI_VIOT_NODE_VIRTIO_IOMMU_MMIO:
+ if (hdr->length < sizeof(node->mmio))
+ goto err_free;
+
+ ret = viot_get_mmio_iommu_fwnode(viommu,
+ node->mmio.base_address);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret)
+ goto err_free;
+
+ list_add(&viommu->list, &viot_iommus);
+ return viommu;
+
+err_free:
+ kfree(viommu);
+ return NULL;
+}
+
+static int __init viot_parse_node(const struct acpi_viot_header *hdr)
+{
+ int ret = -EINVAL;
+ struct list_head *list;
+ struct viot_endpoint *ep;
+ union {
+ struct acpi_viot_mmio mmio;
+ struct acpi_viot_pci_range pci;
+ } *node = (void *)hdr;
+
+ if (viot_check_bounds(hdr))
+ return -EINVAL;
+
+ if (hdr->type == ACPI_VIOT_NODE_VIRTIO_IOMMU_PCI ||
+ hdr->type == ACPI_VIOT_NODE_VIRTIO_IOMMU_MMIO)
+ return 0;
+
+ ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ if (!ep)
+ return -ENOMEM;
+
+ switch (hdr->type) {
+ case ACPI_VIOT_NODE_PCI_RANGE:
+ if (hdr->length < sizeof(node->pci)) {
+ pr_err(FW_BUG "Invalid PCI node size\n");
+ goto err_free;
+ }
+
+ ep->segment_start = node->pci.segment_start;
+ ep->segment_end = node->pci.segment_end;
+ ep->bdf_start = node->pci.bdf_start;
+ ep->bdf_end = node->pci.bdf_end;
+ ep->endpoint_id = node->pci.endpoint_start;
+ ep->viommu = viot_get_iommu(node->pci.output_node);
+ list = &viot_pci_ranges;
+ break;
+ case ACPI_VIOT_NODE_MMIO:
+ if (hdr->length < sizeof(node->mmio)) {
+ pr_err(FW_BUG "Invalid MMIO node size\n");
+ goto err_free;
+ }
+
+ ep->address = node->mmio.base_address;
+ ep->endpoint_id = node->mmio.endpoint;
+ ep->viommu = viot_get_iommu(node->mmio.output_node);
+ list = &viot_mmio_endpoints;
+ break;
+ default:
+ pr_warn("Unsupported node %x\n", hdr->type);
+ ret = 0;
+ goto err_free;
+ }
+
+ if (!ep->viommu) {
+ pr_warn("No IOMMU node found\n");
+ /*
+ * A future version of the table may use the node for other
+ * purposes. Keep parsing.
+ */
+ ret = 0;
+ goto err_free;
+ }
+
+ list_add(&ep->list, list);
+ return 0;
+
+err_free:
+ kfree(ep);
+ return ret;
+}
+
+/**
+ * acpi_viot_early_init - Test the presence of VIOT and enable ACS
+ *
+ * If the VIOT does exist, ACS must be enabled. This cannot be
+ * done in acpi_viot_init() which is called after the bus scan
+ */
+void __init acpi_viot_early_init(void)
+{
+#ifdef CONFIG_PCI
+ acpi_status status;
+ struct acpi_table_header *hdr;
+
+ status = acpi_get_table(ACPI_SIG_VIOT, 0, &hdr);
+ if (ACPI_FAILURE(status))
+ return;
+ pci_request_acs();
+ acpi_put_table(hdr);
+#endif
+}
+
+/**
+ * acpi_viot_init - Parse the VIOT table
+ *
+ * Parse the VIOT table, prepare the list of endpoints to be used during DMA
+ * setup of devices.
+ */
+void __init acpi_viot_init(void)
+{
+ int i;
+ acpi_status status;
+ struct acpi_table_header *hdr;
+ struct acpi_viot_header *node;
+
+ status = acpi_get_table(ACPI_SIG_VIOT, 0, &hdr);
+ 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;
+ }
+
+ viot = (void *)hdr;
+
+ node = ACPI_ADD_PTR(struct acpi_viot_header, viot, viot->node_offset);
+ for (i = 0; i < viot->node_count; i++) {
+ if (viot_parse_node(node))
+ return;
+
+ node = ACPI_ADD_PTR(struct acpi_viot_header, node,
+ node->length);
+ }
+
+ acpi_put_table(hdr);
+}
+
+static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu,
+ u32 epid)
+{
+ const struct iommu_ops *ops;
+
+ if (!viommu)
+ 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);
+}
+
+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) {
+ if (domain_nr >= ep->segment_start &&
+ domain_nr <= ep->segment_end &&
+ dev_id >= ep->bdf_start &&
+ dev_id <= ep->bdf_end) {
+ epid = ((domain_nr - ep->segment_start) << 16) +
+ dev_id - ep->bdf_start + ep->endpoint_id;
+
+ return viot_dev_iommu_init(aliased_dev, ep->viommu,
+ epid);
+ }
+ }
+ return -ENODEV;
+}
+
+static int viot_mmio_dev_iommu_init(struct platform_device *pdev)
+{
+ struct resource *mem;
+ struct viot_endpoint *ep;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -ENODEV;
+
+ list_for_each_entry(ep, &viot_mmio_endpoints, list) {
+ if (ep->address == mem->start)
+ return viot_dev_iommu_init(&pdev->dev, ep->viommu,
+ ep->endpoint_id);
+ }
+ return -ENODEV;
+}
+
+/**
+ * viot_iommu_configure - Setup IOMMU ops for an endpoint described by VIOT
+ * @dev: the endpoint
+ *
+ * Return: 0 on success, <0 on failure
+ */
+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, 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
new file mode 100644
index 000000000..b02bf770a
--- /dev/null
+++ b/drivers/acpi/wakeup.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * wakeup.c - support wakeup devices
+ * Copyright (C) 2004 Li Shaohua <shaohua.li@intel.com>
+ */
+
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "internal.h"
+#include "sleep.h"
+
+struct acpi_wakeup_handler {
+ struct list_head list_node;
+ bool (*wakeup)(void *context);
+ void *context;
+};
+
+static LIST_HEAD(acpi_wakeup_handler_head);
+static DEFINE_MUTEX(acpi_wakeup_handler_mutex);
+
+/*
+ * We didn't lock acpi_device_lock in the file, because it invokes oops in
+ * suspend/resume and isn't really required as this is called in S-state. At
+ * that time, there is no device hotplug
+ **/
+
+/**
+ * acpi_enable_wakeup_devices - Enable wake-up device GPEs.
+ * @sleep_state: ACPI system sleep state.
+ *
+ * Enable wakeup device power of devices with the state.enable flag set and set
+ * the wakeup enable mask bits in the GPE registers that correspond to wakeup
+ * devices.
+ */
+void acpi_enable_wakeup_devices(u8 sleep_state)
+{
+ struct acpi_device *dev, *tmp;
+
+ 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
+ || !(device_may_wakeup(&dev->dev)
+ || dev->wakeup.prepare_count))
+ continue;
+
+ if (device_may_wakeup(&dev->dev))
+ acpi_enable_wakeup_device_power(dev, sleep_state);
+
+ /* The wake-up power should have been enabled already. */
+ acpi_set_gpe_wake_mask(dev->wakeup.gpe_device, dev->wakeup.gpe_number,
+ ACPI_GPE_ENABLE);
+ }
+}
+
+/**
+ * acpi_disable_wakeup_devices - Disable devices' wakeup capability.
+ * @sleep_state: ACPI system sleep state.
+ */
+void acpi_disable_wakeup_devices(u8 sleep_state)
+{
+ struct acpi_device *dev, *tmp;
+
+ 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
+ || !(device_may_wakeup(&dev->dev)
+ || dev->wakeup.prepare_count))
+ continue;
+
+ acpi_set_gpe_wake_mask(dev->wakeup.gpe_device, dev->wakeup.gpe_number,
+ ACPI_GPE_DISABLE);
+
+ if (device_may_wakeup(&dev->dev))
+ acpi_disable_wakeup_device_power(dev);
+ }
+}
+
+int __init acpi_wakeup_device_init(void)
+{
+ struct acpi_device *dev, *tmp;
+
+ mutex_lock(&acpi_device_lock);
+ list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list,
+ wakeup_list) {
+ if (device_can_wakeup(&dev->dev)) {
+ /* Button GPEs are supposed to be always enabled. */
+ acpi_enable_gpe(dev->wakeup.gpe_device,
+ dev->wakeup.gpe_number);
+ device_set_wakeup_enable(&dev->dev, true);
+ }
+ }
+ mutex_unlock(&acpi_device_lock);
+ return 0;
+}
+
+/**
+ * acpi_register_wakeup_handler - Register wakeup handler
+ * @wake_irq: The IRQ through which the device may receive wakeups
+ * @wakeup: Wakeup-handler to call when the SCI has triggered a wakeup
+ * @context: Context to pass to the handler when calling it
+ *
+ * Drivers which may share an IRQ with the SCI can use this to register
+ * a handler which returns true when the device they are managing wants
+ * to trigger a wakeup.
+ */
+int acpi_register_wakeup_handler(int wake_irq, bool (*wakeup)(void *context),
+ void *context)
+{
+ struct acpi_wakeup_handler *handler;
+
+ /*
+ * If the device is not sharing its IRQ with the SCI, there is no
+ * need to register the handler.
+ */
+ if (!acpi_sci_irq_valid() || wake_irq != acpi_sci_irq)
+ return 0;
+
+ handler = kmalloc(sizeof(*handler), GFP_KERNEL);
+ if (!handler)
+ return -ENOMEM;
+
+ handler->wakeup = wakeup;
+ handler->context = context;
+
+ mutex_lock(&acpi_wakeup_handler_mutex);
+ list_add(&handler->list_node, &acpi_wakeup_handler_head);
+ mutex_unlock(&acpi_wakeup_handler_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_register_wakeup_handler);
+
+/**
+ * acpi_unregister_wakeup_handler - Unregister wakeup handler
+ * @wakeup: Wakeup-handler passed to acpi_register_wakeup_handler()
+ * @context: Context passed to acpi_register_wakeup_handler()
+ */
+void acpi_unregister_wakeup_handler(bool (*wakeup)(void *context),
+ void *context)
+{
+ struct acpi_wakeup_handler *handler;
+
+ mutex_lock(&acpi_wakeup_handler_mutex);
+ list_for_each_entry(handler, &acpi_wakeup_handler_head, list_node) {
+ if (handler->wakeup == wakeup && handler->context == context) {
+ list_del(&handler->list_node);
+ kfree(handler);
+ break;
+ }
+ }
+ mutex_unlock(&acpi_wakeup_handler_mutex);
+}
+EXPORT_SYMBOL_GPL(acpi_unregister_wakeup_handler);
+
+bool acpi_check_wakeup_handlers(void)
+{
+ struct acpi_wakeup_handler *handler;
+
+ /* No need to lock, nothing else is running when we're called. */
+ list_for_each_entry(handler, &acpi_wakeup_handler_head, list_node) {
+ if (handler->wakeup(handler->context))
+ return true;
+ }
+
+ return false;
+}
diff --git a/drivers/acpi/x86/apple.c b/drivers/acpi/x86/apple.c
new file mode 100644
index 000000000..8812ecd03
--- /dev/null
+++ b/drivers/acpi/x86/apple.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * apple.c - Apple ACPI quirks
+ * Copyright (C) 2017 Lukas Wunner <lukas@wunner.de>
+ */
+
+#include <linux/acpi.h>
+#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 =
+ GUID_INIT(0xa0b5b7c6, 0x1318, 0x441c,
+ 0xb0, 0xc9, 0xfe, 0x69, 0x5e, 0xaf, 0x94, 0x9b);
+
+/**
+ * acpi_extract_apple_properties - retrieve and convert Apple _DSM properties
+ * @adev: ACPI device for which to retrieve the properties
+ *
+ * Invoke Apple's custom _DSM once to check the protocol version and once more
+ * to retrieve the properties. They are marshalled up in a single package as
+ * alternating key/value elements, unlike _DSD which stores them as a package
+ * of 2-element packages. Convert to _DSD format and make them available under
+ * the primary fwnode.
+ */
+void acpi_extract_apple_properties(struct acpi_device *adev)
+{
+ unsigned int i, j = 0, newsize = 0, numprops, numvalid;
+ union acpi_object *props, *newprops;
+ unsigned long *valid = NULL;
+ void *free_space;
+
+ if (!x86_apple_machine)
+ return;
+
+ props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 0,
+ NULL, ACPI_TYPE_BUFFER);
+ if (!props)
+ return;
+
+ if (!props->buffer.length)
+ goto out_free;
+
+ if (props->buffer.pointer[0] != 3) {
+ acpi_handle_info(adev->handle, FW_INFO
+ "unsupported properties version %*ph\n",
+ props->buffer.length, props->buffer.pointer);
+ goto out_free;
+ }
+
+ ACPI_FREE(props);
+ props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 1,
+ NULL, ACPI_TYPE_PACKAGE);
+ if (!props)
+ return;
+
+ numprops = props->package.count / 2;
+ if (!numprops)
+ goto out_free;
+
+ valid = bitmap_zalloc(numprops, GFP_KERNEL);
+ if (!valid)
+ goto out_free;
+
+ /* newsize = key length + value length of each tuple */
+ for (i = 0; i < numprops; i++) {
+ union acpi_object *key = &props->package.elements[i * 2];
+ union acpi_object *val = &props->package.elements[i * 2 + 1];
+
+ if ( key->type != ACPI_TYPE_STRING ||
+ (val->type != ACPI_TYPE_INTEGER &&
+ val->type != ACPI_TYPE_BUFFER))
+ continue; /* skip invalid properties */
+
+ __set_bit(i, valid);
+ newsize += key->string.length + 1;
+ if ( val->type == ACPI_TYPE_BUFFER)
+ newsize += val->buffer.length;
+ }
+
+ numvalid = bitmap_weight(valid, numprops);
+ if (numprops > numvalid)
+ acpi_handle_info(adev->handle, FW_INFO
+ "skipped %u properties: wrong type\n",
+ numprops - numvalid);
+ if (numvalid == 0)
+ goto out_free;
+
+ /* newsize += top-level package + 3 objects for each key/value tuple */
+ newsize += (1 + 3 * numvalid) * sizeof(union acpi_object);
+ newprops = ACPI_ALLOCATE_ZEROED(newsize);
+ if (!newprops)
+ goto out_free;
+
+ /* layout: top-level package | packages | key/value tuples | strings */
+ newprops->type = ACPI_TYPE_PACKAGE;
+ newprops->package.count = numvalid;
+ newprops->package.elements = &newprops[1];
+ free_space = &newprops[1 + 3 * numvalid];
+
+ for_each_set_bit(i, valid, numprops) {
+ union acpi_object *key = &props->package.elements[i * 2];
+ union acpi_object *val = &props->package.elements[i * 2 + 1];
+ unsigned int k = 1 + numvalid + j * 2; /* index into newprops */
+ unsigned int v = k + 1;
+
+ newprops[1 + j].type = ACPI_TYPE_PACKAGE;
+ newprops[1 + j].package.count = 2;
+ newprops[1 + j].package.elements = &newprops[k];
+
+ newprops[k].type = ACPI_TYPE_STRING;
+ newprops[k].string.length = key->string.length;
+ newprops[k].string.pointer = free_space;
+ memcpy(free_space, key->string.pointer, key->string.length);
+ free_space += key->string.length + 1;
+
+ newprops[v].type = val->type;
+ if (val->type == ACPI_TYPE_INTEGER) {
+ newprops[v].integer.value = val->integer.value;
+ } else {
+ newprops[v].buffer.length = val->buffer.length;
+ newprops[v].buffer.pointer = free_space;
+ memcpy(free_space, val->buffer.pointer,
+ val->buffer.length);
+ free_space += val->buffer.length;
+ }
+ j++; /* count valid properties */
+ }
+ WARN_ON(free_space != (void *)newprops + newsize);
+
+ adev->data.pointer = newprops;
+ acpi_data_add_props(&adev->data, &apple_prp_guid, newprops);
+
+out_free:
+ ACPI_FREE(props);
+ bitmap_free(valid);
+}
diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c
new file mode 100644
index 000000000..5510657d4
--- /dev/null
+++ b/drivers/acpi/x86/s2idle.c
@@ -0,0 +1,597 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Architecture-specific ACPI-based support for suspend-to-idle.
+ *
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ *
+ * On platforms supporting the Low Power S0 Idle interface there is an ACPI
+ * device object with the PNP0D80 compatible device ID (System Power Management
+ * Controller) and a specific _DSM method under it. That method, if present,
+ * can be used to indicate to the platform that the OS is transitioning into a
+ * low-power state in which certain types of activity are not desirable or that
+ * it is leaving such a state, which allows the platform to adjust its operation
+ * mode accordingly.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/suspend.h>
+
+#include "../sleep.h"
+
+#ifdef CONFIG_SUSPEND
+
+static bool sleep_no_lps0 __read_mostly;
+module_param(sleep_no_lps0, bool, 0644);
+MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface");
+
+static const struct acpi_device_id lps0_device_ids[] = {
+ {"PNP0D80", },
+ {"", },
+};
+
+/* Microsoft platform agnostic UUID */
+#define ACPI_LPS0_DSM_UUID_MICROSOFT "11e00d56-ce64-47ce-837b-1f898f9aa461"
+
+#define ACPI_LPS0_DSM_UUID "c4eb40a0-6cd2-11e2-bcfd-0800200c9a66"
+
+#define ACPI_LPS0_GET_DEVICE_CONSTRAINTS 1
+#define ACPI_LPS0_SCREEN_OFF 3
+#define ACPI_LPS0_SCREEN_ON 4
+#define ACPI_LPS0_ENTRY 5
+#define ACPI_LPS0_EXIT 6
+#define ACPI_LPS0_MS_ENTRY 7
+#define ACPI_LPS0_MS_EXIT 8
+
+/* AMD */
+#define ACPI_LPS0_DSM_UUID_AMD "e3f32452-febc-43ce-9039-932122d37721"
+#define ACPI_LPS0_ENTRY_AMD 2
+#define ACPI_LPS0_EXIT_AMD 3
+#define ACPI_LPS0_SCREEN_OFF_AMD 4
+#define ACPI_LPS0_SCREEN_ON_AMD 5
+
+static acpi_handle lps0_device_handle;
+static guid_t lps0_dsm_guid;
+static int lps0_dsm_func_mask;
+
+static guid_t lps0_dsm_guid_microsoft;
+static int lps0_dsm_func_mask_microsoft;
+
+/* Device constraint entry structure */
+struct lpi_device_info {
+ char *name;
+ int enabled;
+ union acpi_object *package;
+};
+
+/* Constraint package structure */
+struct lpi_device_constraint {
+ int uid;
+ int min_dstate;
+ int function_states;
+};
+
+struct lpi_constraints {
+ acpi_handle handle;
+ int min_dstate;
+};
+
+/* AMD Constraint package structure */
+struct lpi_device_constraint_amd {
+ char *name;
+ int enabled;
+ int function_states;
+ int min_dstate;
+};
+
+static LIST_HEAD(lps0_s2idle_devops_head);
+
+static struct lpi_constraints *lpi_constraints_table;
+static int lpi_constraints_table_size;
+static int rev_id;
+
+static void lpi_device_get_constraints_amd(void)
+{
+ union acpi_object *out_obj;
+ int i, j, k;
+
+ out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid,
+ rev_id, ACPI_LPS0_GET_DEVICE_CONSTRAINTS,
+ NULL, ACPI_TYPE_PACKAGE);
+
+ acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n",
+ out_obj ? "successful" : "failed");
+
+ if (!out_obj)
+ return;
+
+ for (i = 0; i < out_obj->package.count; i++) {
+ 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);
+
+ if (!lpi_constraints_table)
+ goto free_acpi_buffer;
+
+ acpi_handle_debug(lps0_device_handle,
+ "LPI: constraints list begin:\n");
+
+ 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;
+
+ list = &lpi_constraints_table[lpi_constraints_table_size];
+
+ for (k = 0; k < info_obj->package.count; k++) {
+ union acpi_object *obj = &info_obj->package.elements[k];
+
+ switch (k) {
+ case 0:
+ dev_info.enabled = obj->integer.value;
+ break;
+ case 1:
+ dev_info.name = obj->string.pointer;
+ break;
+ case 2:
+ dev_info.function_states = obj->integer.value;
+ break;
+ case 3:
+ dev_info.min_dstate = obj->integer.value;
+ break;
+ }
+ }
+
+ if (!dev_info.enabled || !dev_info.name ||
+ !dev_info.min_dstate)
+ continue;
+
+ status = acpi_get_handle(NULL, dev_info.name, &list->handle);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ acpi_handle_debug(lps0_device_handle,
+ "Name:%s\n", dev_info.name);
+
+ list->min_dstate = dev_info.min_dstate;
+
+ lpi_constraints_table_size++;
+ }
+ }
+ }
+
+ acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n");
+
+free_acpi_buffer:
+ ACPI_FREE(out_obj);
+}
+
+static void lpi_device_get_constraints(void)
+{
+ union acpi_object *out_obj;
+ int i;
+
+ out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid,
+ 1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS,
+ NULL, ACPI_TYPE_PACKAGE);
+
+ acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n",
+ out_obj ? "successful" : "failed");
+
+ if (!out_obj)
+ return;
+
+ lpi_constraints_table = kcalloc(out_obj->package.count,
+ sizeof(*lpi_constraints_table),
+ GFP_KERNEL);
+ if (!lpi_constraints_table)
+ goto free_acpi_buffer;
+
+ acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n");
+
+ for (i = 0; i < out_obj->package.count; i++) {
+ struct lpi_constraints *constraint;
+ acpi_status status;
+ union acpi_object *package = &out_obj->package.elements[i];
+ struct lpi_device_info info = { };
+ int package_count = 0, j;
+
+ if (!package)
+ continue;
+
+ for (j = 0; j < package->package.count; j++) {
+ union acpi_object *element =
+ &(package->package.elements[j]);
+
+ switch (element->type) {
+ case ACPI_TYPE_INTEGER:
+ info.enabled = element->integer.value;
+ break;
+ case ACPI_TYPE_STRING:
+ info.name = element->string.pointer;
+ break;
+ case ACPI_TYPE_PACKAGE:
+ package_count = element->package.count;
+ info.package = element->package.elements;
+ break;
+ }
+ }
+
+ if (!info.enabled || !info.package || !info.name)
+ continue;
+
+ constraint = &lpi_constraints_table[lpi_constraints_table_size];
+
+ status = acpi_get_handle(NULL, info.name, &constraint->handle);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ acpi_handle_debug(lps0_device_handle,
+ "index:%d Name:%s\n", i, info.name);
+
+ constraint->min_dstate = -1;
+
+ for (j = 0; j < package_count; j++) {
+ union acpi_object *info_obj = &info.package[j];
+ union acpi_object *cnstr_pkg;
+ union acpi_object *obj;
+ struct lpi_device_constraint dev_info;
+
+ switch (info_obj->type) {
+ case ACPI_TYPE_INTEGER:
+ /* version */
+ break;
+ case ACPI_TYPE_PACKAGE:
+ if (info_obj->package.count < 2)
+ break;
+
+ cnstr_pkg = info_obj->package.elements;
+ obj = &cnstr_pkg[0];
+ dev_info.uid = obj->integer.value;
+ obj = &cnstr_pkg[1];
+ dev_info.min_dstate = obj->integer.value;
+
+ acpi_handle_debug(lps0_device_handle,
+ "uid:%d min_dstate:%s\n",
+ dev_info.uid,
+ acpi_power_state_string(dev_info.min_dstate));
+
+ constraint->min_dstate = dev_info.min_dstate;
+ break;
+ }
+ }
+
+ if (constraint->min_dstate < 0) {
+ acpi_handle_debug(lps0_device_handle,
+ "Incomplete constraint defined\n");
+ continue;
+ }
+
+ lpi_constraints_table_size++;
+ }
+
+ acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n");
+
+free_acpi_buffer:
+ ACPI_FREE(out_obj);
+}
+
+static void lpi_check_constraints(void)
+{
+ int i;
+
+ 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 (!adev)
+ continue;
+
+ acpi_handle_debug(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(adev->power.state));
+
+ if (!adev->flags.power_manageable) {
+ acpi_handle_info(handle, "LPI: Device not power manageable\n");
+ lpi_constraints_table[i].handle = NULL;
+ continue;
+ }
+
+ if (adev->power.state < lpi_constraints_table[i].min_dstate)
+ acpi_handle_info(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(adev->power.state));
+ }
+}
+
+static void acpi_sleep_run_lps0_dsm(unsigned int func, unsigned int func_mask, guid_t dsm_guid)
+{
+ union acpi_object *out_obj;
+
+ if (!(func_mask & (1 << func)))
+ return;
+
+ out_obj = acpi_evaluate_dsm(lps0_device_handle, &dsm_guid,
+ 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");
+}
+
+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)
+{
+ union acpi_object *obj;
+ 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)) {
+ acpi_handle_debug(handle,
+ "_DSM UUID %s rev %d function 0 evaluation failed\n", uuid, rev);
+ goto out;
+ }
+
+ ret = *(int *)obj->buffer.pointer;
+ acpi_handle_debug(handle, "_DSM UUID %s rev %d function mask: 0x%x\n", uuid, rev, ret);
+
+out:
+ ACPI_FREE(obj);
+ 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()) {
+ 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);
+ 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 && rev_id) {
+ lps0_dsm_func_mask_microsoft = -EINVAL;
+ acpi_handle_debug(adev->handle, "_DSM Using AMD method\n");
+ }
+ } else {
+ 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)
+ return 0; //function evaluation failed
+
+ 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
+ * line.
+ */
+ if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) &&
+ mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) {
+ mem_sleep_current = PM_SUSPEND_TO_IDLE;
+ pr_info("Low-power S0 idle used by default for system suspend\n");
+ }
+
+ /*
+ * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the
+ * EC GPE to be enabled while suspended for certain wakeup devices to
+ * work, so mark it as wakeup-capable.
+ */
+ acpi_ec_mark_gpe_for_wake();
+
+ return 0;
+}
+
+static struct acpi_scan_handler lps0_handler = {
+ .ids = lps0_device_ids,
+ .attach = lps0_device_attach,
+};
+
+int acpi_s2idle_prepare_late(void)
+{
+ struct acpi_s2idle_dev_ops *handler;
+
+ if (!lps0_device_handle || sleep_no_lps0)
+ return 0;
+
+ if (pm_debug_messages_on)
+ lpi_check_constraints();
+
+ /* Screen off */
+ if (lps0_dsm_func_mask > 0)
+ acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
+ ACPI_LPS0_SCREEN_OFF_AMD :
+ ACPI_LPS0_SCREEN_OFF,
+ lps0_dsm_func_mask, lps0_dsm_guid);
+
+ if (lps0_dsm_func_mask_microsoft > 0)
+ acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF,
+ 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,
+ 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 */
+ acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY,
+ lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
+ }
+
+ list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
+ if (handler->prepare)
+ handler->prepare();
+ }
+
+ return 0;
+}
+
+void acpi_s2idle_check(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();
+ }
+}
+
+void acpi_s2idle_restore_early(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->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)
+ acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT,
+ lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
+
+ /* Screen on */
+ if (lps0_dsm_func_mask_microsoft > 0)
+ acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON,
+ lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
+ if (lps0_dsm_func_mask > 0)
+ acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
+ ACPI_LPS0_SCREEN_ON_AMD :
+ ACPI_LPS0_SCREEN_ON,
+ lps0_dsm_func_mask, lps0_dsm_guid);
+}
+
+static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = {
+ .begin = acpi_s2idle_begin,
+ .prepare = acpi_s2idle_prepare,
+ .prepare_late = acpi_s2idle_prepare_late,
+ .check = acpi_s2idle_check,
+ .wake = acpi_s2idle_wake,
+ .restore_early = acpi_s2idle_restore_early,
+ .restore = acpi_s2idle_restore,
+ .end = acpi_s2idle_end,
+};
+
+void __init acpi_s2idle_setup(void)
+{
+ acpi_scan_add_handler(&lps0_handler);
+ s2idle_set_ops(&acpi_s2idle_ops_lps0);
+}
+
+int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg)
+{
+ unsigned int sleep_flags;
+
+ if (!lps0_device_handle || sleep_no_lps0)
+ return -ENODEV;
+
+ sleep_flags = lock_system_sleep();
+ list_add(&arg->list_node, &lps0_s2idle_devops_head);
+ unlock_system_sleep(sleep_flags);
+
+ return 0;
+}
+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;
+
+ sleep_flags = lock_system_sleep();
+ list_del(&arg->list_node);
+ unlock_system_sleep(sleep_flags);
+}
+EXPORT_SYMBOL_GPL(acpi_unregister_lps0_dev);
+
+#endif /* CONFIG_SUSPEND */
diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c
new file mode 100644
index 000000000..e45285d4e
--- /dev/null
+++ b/drivers/acpi/x86/utils.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * X86 ACPI Utility Functions
+ *
+ * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
+ * Copyright (C) 2013-2015 Intel Corporation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/platform_device.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+#include "../internal.h"
+
+/*
+ * Some ACPI devices are hidden (status == 0x0) in recent BIOS-es because
+ * some recent Windows drivers bind to one device but poke at multiple
+ * devices at the same time, so the others get hidden.
+ *
+ * Some BIOS-es (temporarily) hide specific APCI devices to work around Windows
+ * driver bugs. We use DMI matching to match known cases of this.
+ *
+ * Likewise sometimes some not-actually present devices are sometimes
+ * reported as present, which may cause issues.
+ *
+ * We work around this by using the below quirk list to override the status
+ * reported by the _STA method with a fixed value (ACPI_STA_DEFAULT or 0).
+ * Note this MUST only be done for devices where this is safe.
+ *
+ * This status overriding is limited to specific CPU (SoC) models both to
+ * avoid potentially causing trouble on other models and because some HIDs
+ * are re-used on different SoCs for completely different devices.
+ */
+struct override_status_id {
+ struct acpi_device_id hid[2];
+ struct x86_cpu_id cpu_ids[2];
+ struct dmi_system_id dmi_ids[2]; /* Optional */
+ const char *uid;
+ const char *path;
+ unsigned long long status;
+};
+
+#define ENTRY(status, hid, uid, path, cpu_model, dmi...) { \
+ { { hid, }, {} }, \
+ { X86_MATCH_INTEL_FAM6_MODEL(cpu_model, 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 NOT_PRESENT_ENTRY_HID(hid, uid, cpu_model, dmi...) \
+ ENTRY(0, hid, uid, NULL, cpu_model, dmi)
+
+#define PRESENT_ENTRY_PATH(path, cpu_model, dmi...) \
+ ENTRY(ACPI_STA_DEFAULT, "", NULL, path, cpu_model, dmi)
+
+#define NOT_PRESENT_ENTRY_PATH(path, cpu_model, dmi...) \
+ ENTRY(0, "", NULL, path, cpu_model, 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, {}),
+
+ /* The Xiaomi Mi Pad 2 uses PWM2 for touchkeys backlight control */
+ PRESENT_ENTRY_HID("80862289", "2", ATOM_AIRMONT, {
+ DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"),
+ }),
+
+ /*
+ * 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, {}),
+ /*
+ * 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, {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Venue 11 Pro 7130"),
+ }),
+ PRESENT_ENTRY_HID("SYNA7500", "1", HASWELL_L, {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Venue 11 Pro 7139"),
+ }),
+
+ /*
+ * 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,
+ * but it is. In the 67000+ DMI decode dumps from linux-hardware.org
+ * only 116 have board_vendor set to "AMI Corporation" and of those 116
+ * only the GPD win and pocket entries' board_name is "Default string".
+ *
+ * Unfortunately the GPD pocket also uses these strings and its BIOS
+ * 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, {
+ 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, {
+ 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, {
+ 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, "05/25/2017")
+ }),
+
+ /*
+ * The GPD win/pocket have a PCI wifi card, but its DSDT has the SDIO
+ * mmc controller enabled and that has a child-device which _PS3
+ * 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, {
+ 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"),
+ }),
+};
+
+bool acpi_device_override_status(struct acpi_device *adev, unsigned long long *status)
+{
+ bool ret = false;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(override_status_ids); i++) {
+ if (!x86_match_cpu(override_status_ids[i].cpu_ids))
+ continue;
+
+ if (override_status_ids[i].dmi_ids[0].matches[0].slot &&
+ !dmi_check_system(override_status_ids[i].dmi_ids))
+ continue;
+
+ if (override_status_ids[i].path) {
+ struct acpi_buffer path = { ACPI_ALLOCATE_BUFFER, NULL };
+ bool match;
+
+ if (acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &path))
+ continue;
+
+ match = strcmp((char *)path.pointer, override_status_ids[i].path) == 0;
+ kfree(path.pointer);
+
+ if (!match)
+ continue;
+ } else {
+ 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))
+ continue;
+ }
+
+ *status = override_status_ids[i].status;
+ ret = true;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * AMD systems from Renoir and Lucienne *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.
+ *
+ * The Microsoft documentation for StorageD3Enable mentioned that Windows has
+ * a hardcoded allowlist for D3 support, which was used for these platforms.
+ *
+ * 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.
+ */
+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 */
+ X86_MATCH_VENDOR_FAM_MODEL(AMD, 25, 80, NULL), /* Cezanne */
+ {}
+};
+
+bool force_storage_d3(void)
+{
+ return x86_match_cpu(storage_d3_cpu_ids);
+}
+
+/*
+ * x86 ACPI boards which ship with only Android as their factory image usually
+ * declare a whole bunch of bogus I2C devices in their ACPI tables and sometimes
+ * there are issues with serdev devices on these boards too, e.g. the resource
+ * points to the wrong serdev_controller.
+ *
+ * Instantiating I2C / serdev devs for these bogus devs causes various issues,
+ * e.g. GPIO/IRQ resource conflicts because sometimes drivers do bind to them.
+ * The Android x86 kernel fork shipped on these devices has some special code
+ * to remove the bogus I2C clients (and AFAICT serdevs are ignored completely).
+ *
+ * The acpi_quirk_skip_*_enumeration() functions below are used by the I2C or
+ * serdev code to skip instantiating any I2C or serdev devs on broken boards.
+ *
+ * In case of I2C an exception is made for HIDs on the i2c_acpi_known_good_ids
+ * list. These are known to always be correct (and in case of the audio-codecs
+ * the drivers heavily rely on the codec being enumerated through ACPI).
+ *
+ * Note these boards typically do actually have I2C and serdev devices,
+ * just different ones then the ones described in their DSDT. The devices
+ * which are actually present are manually instantiated by the
+ * 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)
+
+static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
+ /*
+ * 1. Devices with only the skip / don't-skip AC and battery quirks,
+ * sorted alphabetically.
+ */
+ {
+ /* ECS EF20EA, AXP288 PMIC but uses separate fuel-gauge */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
+ },
+ .driver_data = (void *)ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY
+ },
+ {
+ /* Lenovo Ideapad Miix 320, AXP288 PMIC, separate fuel-gauge */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "80XF"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
+ },
+ .driver_data = (void *)ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY
+ },
+
+ /*
+ * 2. Devices which also have the skip i2c/serdev quirks and which
+ * need the x86-android-tablets module to properly work.
+ */
+#if IS_ENABLED(CONFIG_X86_ANDROID_TABLETS)
+ {
+ .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),
+ },
+ {
+ .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),
+ },
+ {
+ /* Lenovo Yoga Tablet 2 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 = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ 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_NAME, "CHERRYVIEW D1 PLATFORM"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
+ },
+ {
+ /* 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 */
+ .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),
+ },
+ {
+ /* Whitelabel (sold as various brands) TM800A550L */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* Above strings are too generic, also match on BIOS version */
+ DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
+ },
+#endif
+ {}
+};
+
+#if IS_ENABLED(CONFIG_X86_ANDROID_TABLETS)
+static const struct acpi_device_id i2c_acpi_known_good_ids[] = {
+ { "10EC5640", 0 }, /* RealTek ALC5640 audio codec */
+ { "INT33F4", 0 }, /* X-Powers AXP288 PMIC */
+ { "INT33FD", 0 }, /* Intel Crystal Cove PMIC */
+ { "INT34D3", 0 }, /* Intel Whiskey Cove PMIC */
+ { "NPCE69A", 0 }, /* Asus Transformer keyboard dock */
+ {}
+};
+
+bool acpi_quirk_skip_i2c_client_enumeration(struct acpi_device *adev)
+{
+ 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;
+ if (!(quirks & ACPI_QUIRK_SKIP_I2C_CLIENTS))
+ return false;
+
+ return acpi_match_device_ids(adev, i2c_acpi_known_good_ids);
+}
+EXPORT_SYMBOL_GPL(acpi_quirk_skip_i2c_client_enumeration);
+
+int acpi_quirk_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;
+ int ret;
+
+ *skip = false;
+
+ ret = acpi_dev_uid_to_integer(adev, &uid);
+ if (ret)
+ return 0;
+
+ /* to not match on PNP enumerated debug UARTs */
+ if (!dev_is_platform(controller_parent))
+ return 0;
+
+ dmi_id = dmi_first_match(acpi_quirk_skip_dmi_ids);
+ if (dmi_id)
+ quirks = (unsigned long)dmi_id->driver_data;
+
+ if (quirks & ACPI_QUIRK_UART1_TTY_UART2_SKIP) {
+ if (uid == 1)
+ return -ENODEV; /* Create tty cdev instead of serdev */
+
+ if (uid == 2)
+ *skip = true;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_quirk_skip_serdev_enumeration);
+#endif
+
+/* Lists of PMIC ACPI HIDs with an (often better) native charger driver */
+static const struct {
+ const char *hid;
+ int hrv;
+} acpi_skip_ac_and_battery_pmic_ids[] = {
+ { "INT33F4", -1 }, /* X-Powers AXP288 PMIC */
+ { "INT34D3", 3 }, /* Intel Cherrytrail Whiskey Cove PMIC */
+};
+
+bool acpi_quirk_skip_acpi_ac_and_battery(void)
+{
+ const struct dmi_system_id *dmi_id;
+ long quirks = 0;
+ int i;
+
+ dmi_id = dmi_first_match(acpi_quirk_skip_dmi_ids);
+ if (dmi_id)
+ quirks = (unsigned long)dmi_id->driver_data;
+
+ if (quirks & ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY)
+ return true;
+
+ if (quirks & ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(acpi_skip_ac_and_battery_pmic_ids); i++) {
+ if (acpi_dev_present(acpi_skip_ac_and_battery_pmic_ids[i].hid, "1",
+ acpi_skip_ac_and_battery_pmic_ids[i].hrv)) {
+ pr_info_once("found native %s PMIC, skipping ACPI AC and battery devices\n",
+ acpi_skip_ac_and_battery_pmic_ids[i].hid);
+ return true;
+ }
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(acpi_quirk_skip_acpi_ac_and_battery);