exfldio.c revision 210976
167754Smsmith/******************************************************************************
267754Smsmith *
377424Smsmith * Module Name: exfldio - Aml Field I/O
467754Smsmith *
567754Smsmith *****************************************************************************/
667754Smsmith
767754Smsmith/******************************************************************************
867754Smsmith *
967754Smsmith * 1. Copyright Notice
1067754Smsmith *
11202771Sjkim * Some or all of this work - Copyright (c) 1999 - 2010, Intel Corp.
1270243Smsmith * All rights reserved.
1367754Smsmith *
1467754Smsmith * 2. License
1567754Smsmith *
1667754Smsmith * 2.1. This is your license from Intel Corp. under its intellectual property
1767754Smsmith * rights.  You may have additional license terms from the party that provided
1867754Smsmith * you this software, covering your right to use that party's intellectual
1967754Smsmith * property rights.
2067754Smsmith *
2167754Smsmith * 2.2. Intel grants, free of charge, to any person ("Licensee") obtaining a
2267754Smsmith * copy of the source code appearing in this file ("Covered Code") an
2367754Smsmith * irrevocable, perpetual, worldwide license under Intel's copyrights in the
2467754Smsmith * base code distributed originally by Intel ("Original Intel Code") to copy,
2567754Smsmith * make derivatives, distribute, use and display any portion of the Covered
2667754Smsmith * Code in any form, with the right to sublicense such rights; and
2767754Smsmith *
2867754Smsmith * 2.3. Intel grants Licensee a non-exclusive and non-transferable patent
2967754Smsmith * license (with the right to sublicense), under only those claims of Intel
3067754Smsmith * patents that are infringed by the Original Intel Code, to make, use, sell,
3167754Smsmith * offer to sell, and import the Covered Code and derivative works thereof
3267754Smsmith * solely to the minimum extent necessary to exercise the above copyright
3367754Smsmith * license, and in no event shall the patent license extend to any additions
3467754Smsmith * to or modifications of the Original Intel Code.  No other license or right
3567754Smsmith * is granted directly or by implication, estoppel or otherwise;
3667754Smsmith *
3767754Smsmith * The above copyright and patent license is granted only if the following
3867754Smsmith * conditions are met:
3967754Smsmith *
4067754Smsmith * 3. Conditions
4167754Smsmith *
4267754Smsmith * 3.1. Redistribution of Source with Rights to Further Distribute Source.
4367754Smsmith * Redistribution of source code of any substantial portion of the Covered
4467754Smsmith * Code or modification with rights to further distribute source must include
4567754Smsmith * the above Copyright Notice, the above License, this list of Conditions,
4667754Smsmith * and the following Disclaimer and Export Compliance provision.  In addition,
4767754Smsmith * Licensee must cause all Covered Code to which Licensee contributes to
4867754Smsmith * contain a file documenting the changes Licensee made to create that Covered
4967754Smsmith * Code and the date of any change.  Licensee must include in that file the
5067754Smsmith * documentation of any changes made by any predecessor Licensee.  Licensee
5167754Smsmith * must include a prominent statement that the modification is derived,
5267754Smsmith * directly or indirectly, from Original Intel Code.
5367754Smsmith *
5467754Smsmith * 3.2. Redistribution of Source with no Rights to Further Distribute Source.
5567754Smsmith * Redistribution of source code of any substantial portion of the Covered
5667754Smsmith * Code or modification without rights to further distribute source must
5767754Smsmith * include the following Disclaimer and Export Compliance provision in the
5867754Smsmith * documentation and/or other materials provided with distribution.  In
5967754Smsmith * addition, Licensee may not authorize further sublicense of source of any
6067754Smsmith * portion of the Covered Code, and must include terms to the effect that the
6167754Smsmith * license from Licensee to its licensee is limited to the intellectual
6267754Smsmith * property embodied in the software Licensee provides to its licensee, and
6367754Smsmith * not to intellectual property embodied in modifications its licensee may
6467754Smsmith * make.
6567754Smsmith *
6667754Smsmith * 3.3. Redistribution of Executable. Redistribution in executable form of any
6767754Smsmith * substantial portion of the Covered Code or modification must reproduce the
6867754Smsmith * above Copyright Notice, and the following Disclaimer and Export Compliance
6967754Smsmith * provision in the documentation and/or other materials provided with the
7067754Smsmith * distribution.
7167754Smsmith *
7267754Smsmith * 3.4. Intel retains all right, title, and interest in and to the Original
7367754Smsmith * Intel Code.
7467754Smsmith *
7567754Smsmith * 3.5. Neither the name Intel nor any other trademark owned or controlled by
7667754Smsmith * Intel shall be used in advertising or otherwise to promote the sale, use or
7767754Smsmith * other dealings in products derived from or relating to the Covered Code
7867754Smsmith * without prior written authorization from Intel.
7967754Smsmith *
8067754Smsmith * 4. Disclaimer and Export Compliance
8167754Smsmith *
8267754Smsmith * 4.1. INTEL MAKES NO WARRANTY OF ANY KIND REGARDING ANY SOFTWARE PROVIDED
8367754Smsmith * HERE.  ANY SOFTWARE ORIGINATING FROM INTEL OR DERIVED FROM INTEL SOFTWARE
8467754Smsmith * IS PROVIDED "AS IS," AND INTEL WILL NOT PROVIDE ANY SUPPORT,  ASSISTANCE,
8567754Smsmith * INSTALLATION, TRAINING OR OTHER SERVICES.  INTEL WILL NOT PROVIDE ANY
8667754Smsmith * UPDATES, ENHANCEMENTS OR EXTENSIONS.  INTEL SPECIFICALLY DISCLAIMS ANY
8767754Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A
8867754Smsmith * PARTICULAR PURPOSE.
8967754Smsmith *
9067754Smsmith * 4.2. IN NO EVENT SHALL INTEL HAVE ANY LIABILITY TO LICENSEE, ITS LICENSEES
9167754Smsmith * OR ANY OTHER THIRD PARTY, FOR ANY LOST PROFITS, LOST DATA, LOSS OF USE OR
9267754Smsmith * COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT,
9367754Smsmith * SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, UNDER ANY
9467754Smsmith * CAUSE OF ACTION OR THEORY OF LIABILITY, AND IRRESPECTIVE OF WHETHER INTEL
9567754Smsmith * HAS ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES.  THESE LIMITATIONS
9667754Smsmith * SHALL APPLY NOTWITHSTANDING THE FAILURE OF THE ESSENTIAL PURPOSE OF ANY
9767754Smsmith * LIMITED REMEDY.
9867754Smsmith *
9967754Smsmith * 4.3. Licensee shall not export, either directly or indirectly, any of this
10067754Smsmith * software or system incorporating such software without first obtaining any
10167754Smsmith * required license or other approval from the U. S. Department of Commerce or
10267754Smsmith * any other agency or department of the United States Government.  In the
10367754Smsmith * event Licensee exports any such software from the United States or
10467754Smsmith * re-exports any such software from a foreign destination, Licensee shall
10567754Smsmith * ensure that the distribution and export/re-export of the software is in
10667754Smsmith * compliance with all laws, regulations, orders, or other restrictions of the
10767754Smsmith * U.S. Export Administration Regulations. Licensee agrees that neither it nor
10867754Smsmith * any of its subsidiaries will export/re-export any technical data, process,
10967754Smsmith * software, or service, directly or indirectly, to any country for which the
11067754Smsmith * United States government or any agency thereof requires an export license,
11167754Smsmith * other governmental approval, or letter of assurance, without first obtaining
11267754Smsmith * such license, approval or letter.
11367754Smsmith *
11467754Smsmith *****************************************************************************/
11567754Smsmith
11667754Smsmith
11777424Smsmith#define __EXFLDIO_C__
11867754Smsmith
119193341Sjkim#include <contrib/dev/acpica/include/acpi.h>
120193341Sjkim#include <contrib/dev/acpica/include/accommon.h>
121193341Sjkim#include <contrib/dev/acpica/include/acinterp.h>
122193341Sjkim#include <contrib/dev/acpica/include/amlcode.h>
123193341Sjkim#include <contrib/dev/acpica/include/acevents.h>
124193341Sjkim#include <contrib/dev/acpica/include/acdispat.h>
12567754Smsmith
12667754Smsmith
12777424Smsmith#define _COMPONENT          ACPI_EXECUTER
12891116Smsmith        ACPI_MODULE_NAME    ("exfldio")
12967754Smsmith
130151937Sjkim/* Local prototypes */
13167754Smsmith
132151937Sjkimstatic ACPI_STATUS
133151937SjkimAcpiExFieldDatumIo (
134151937Sjkim    ACPI_OPERAND_OBJECT     *ObjDesc,
135151937Sjkim    UINT32                  FieldDatumByteOffset,
136202771Sjkim    UINT64                  *Value,
137151937Sjkim    UINT32                  ReadWrite);
138151937Sjkim
139151937Sjkimstatic BOOLEAN
140151937SjkimAcpiExRegisterOverflow (
141151937Sjkim    ACPI_OPERAND_OBJECT     *ObjDesc,
142202771Sjkim    UINT64                  Value);
143151937Sjkim
144151937Sjkimstatic ACPI_STATUS
145151937SjkimAcpiExSetupRegion (
146151937Sjkim    ACPI_OPERAND_OBJECT     *ObjDesc,
147151937Sjkim    UINT32                  FieldDatumByteOffset);
148151937Sjkim
149151937Sjkim
15067754Smsmith/*******************************************************************************
15167754Smsmith *
15287031Smsmith * FUNCTION:    AcpiExSetupRegion
15367754Smsmith *
154151937Sjkim * PARAMETERS:  ObjDesc                 - Field to be read or written
15587031Smsmith *              FieldDatumByteOffset    - Byte offset of this datum within the
15687031Smsmith *                                        parent field
15767754Smsmith *
15867754Smsmith * RETURN:      Status
15967754Smsmith *
16077424Smsmith * DESCRIPTION: Common processing for AcpiExExtractFromField and
161123315Snjl *              AcpiExInsertIntoField.  Initialize the Region if necessary and
162123315Snjl *              validate the request.
16367754Smsmith *
16467754Smsmith ******************************************************************************/
16567754Smsmith
166151937Sjkimstatic ACPI_STATUS
16787031SmsmithAcpiExSetupRegion (
16867754Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
16977424Smsmith    UINT32                  FieldDatumByteOffset)
17067754Smsmith{
17177424Smsmith    ACPI_STATUS             Status = AE_OK;
17277424Smsmith    ACPI_OPERAND_OBJECT     *RgnDesc;
17367754Smsmith
17467754Smsmith
175167802Sjkim    ACPI_FUNCTION_TRACE_U32 (ExSetupRegion, FieldDatumByteOffset);
17667754Smsmith
17783174Smsmith
17877424Smsmith    RgnDesc = ObjDesc->CommonField.RegionObj;
17967754Smsmith
180107325Siwasaki    /* We must have a valid region */
181107325Siwasaki
182193267Sjkim    if (RgnDesc->Common.Type != ACPI_TYPE_REGION)
18377424Smsmith    {
184204773Sjkim        ACPI_ERROR ((AE_INFO, "Needed Region, found type 0x%X (%s)",
185193267Sjkim            RgnDesc->Common.Type,
18699679Siwasaki            AcpiUtGetObjectTypeName (RgnDesc)));
18799679Siwasaki
18877424Smsmith        return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
18977424Smsmith    }
19067754Smsmith
19177424Smsmith    /*
19277424Smsmith     * If the Region Address and Length have not been previously evaluated,
19377424Smsmith     * evaluate them now and save the results.
19477424Smsmith     */
195123315Snjl    if (!(RgnDesc->Common.Flags & AOPOBJ_DATA_VALID))
19667754Smsmith    {
19777424Smsmith        Status = AcpiDsGetRegionArguments (RgnDesc);
19877424Smsmith        if (ACPI_FAILURE (Status))
19977424Smsmith        {
20077424Smsmith            return_ACPI_STATUS (Status);
20177424Smsmith        }
20267754Smsmith    }
20367754Smsmith
204167802Sjkim    /*
205210976Sjkim     * Exit now for SMBus or IPMI address space, it has a non-linear
206210976Sjkim     * address space and the request cannot be directly validated
207167802Sjkim     */
208197104Sjkim    if (RgnDesc->Region.SpaceId == ACPI_ADR_SPACE_SMBUS ||
209197104Sjkim        RgnDesc->Region.SpaceId == ACPI_ADR_SPACE_IPMI)
210107325Siwasaki    {
211197104Sjkim        /* SMBus or IPMI has a non-linear address space */
212107325Siwasaki
213107325Siwasaki        return_ACPI_STATUS (AE_OK);
214107325Siwasaki    }
215107325Siwasaki
216123315Snjl#ifdef ACPI_UNDER_DEVELOPMENT
21777424Smsmith    /*
218123315Snjl     * If the Field access is AnyAcc, we can now compute the optimal
219123315Snjl     * access (because we know know the length of the parent region)
220123315Snjl     */
221123315Snjl    if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
222123315Snjl    {
223123315Snjl        if (ACPI_FAILURE (Status))
224123315Snjl        {
225123315Snjl            return_ACPI_STATUS (Status);
226123315Snjl        }
227123315Snjl    }
228123315Snjl#endif
229123315Snjl
230123315Snjl    /*
23177424Smsmith     * Validate the request.  The entire request from the byte offset for a
23277424Smsmith     * length of one field datum (access width) must fit within the region.
23377424Smsmith     * (Region length is specified in bytes)
23477424Smsmith     */
235167802Sjkim    if (RgnDesc->Region.Length <
236210976Sjkim            (ObjDesc->CommonField.BaseByteOffset + FieldDatumByteOffset +
237167802Sjkim            ObjDesc->CommonField.AccessByteWidth))
23877424Smsmith    {
239138287Smarks        if (AcpiGbl_EnableInterpreterSlack)
240138287Smarks        {
241138287Smarks            /*
242138287Smarks             * Slack mode only:  We will go ahead and allow access to this
243138287Smarks             * field if it is within the region length rounded up to the next
244193267Sjkim             * access width boundary. ACPI_SIZE cast for 64-bit compile.
245138287Smarks             */
246138287Smarks            if (ACPI_ROUND_UP (RgnDesc->Region.Length,
247167802Sjkim                    ObjDesc->CommonField.AccessByteWidth) >=
248193267Sjkim                ((ACPI_SIZE) ObjDesc->CommonField.BaseByteOffset +
249193267Sjkim                    ObjDesc->CommonField.AccessByteWidth +
250193267Sjkim                    FieldDatumByteOffset))
251138287Smarks            {
252138287Smarks                return_ACPI_STATUS (AE_OK);
253138287Smarks            }
254138287Smarks        }
255138287Smarks
25677424Smsmith        if (RgnDesc->Region.Length < ObjDesc->CommonField.AccessByteWidth)
25777424Smsmith        {
25883174Smsmith            /*
25977424Smsmith             * This is the case where the AccessType (AccWord, etc.) is wider
26077424Smsmith             * than the region itself.  For example, a region of length one
26177424Smsmith             * byte, and a field with Dword access specified.
26277424Smsmith             */
263167802Sjkim            ACPI_ERROR ((AE_INFO,
264204773Sjkim                "Field [%4.4s] access width (%u bytes) too large for region [%4.4s] (length %u)",
265123315Snjl                AcpiUtGetNodeName (ObjDesc->CommonField.Node),
266123315Snjl                ObjDesc->CommonField.AccessByteWidth,
267151937Sjkim                AcpiUtGetNodeName (RgnDesc->Region.Node),
268151937Sjkim                RgnDesc->Region.Length));
26977424Smsmith        }
27077424Smsmith
27177424Smsmith        /*
27277424Smsmith         * Offset rounded up to next multiple of field width
27377424Smsmith         * exceeds region length, indicate an error
27477424Smsmith         */
275167802Sjkim        ACPI_ERROR ((AE_INFO,
276204773Sjkim            "Field [%4.4s] Base+Offset+Width %u+%u+%u is beyond end of region [%4.4s] (length %u)",
277123315Snjl            AcpiUtGetNodeName (ObjDesc->CommonField.Node),
278123315Snjl            ObjDesc->CommonField.BaseByteOffset,
27991116Smsmith            FieldDatumByteOffset, ObjDesc->CommonField.AccessByteWidth,
280151937Sjkim            AcpiUtGetNodeName (RgnDesc->Region.Node),
281151937Sjkim            RgnDesc->Region.Length));
28277424Smsmith
28377424Smsmith        return_ACPI_STATUS (AE_AML_REGION_LIMIT);
28477424Smsmith    }
28577424Smsmith
28677424Smsmith    return_ACPI_STATUS (AE_OK);
28777424Smsmith}
28877424Smsmith
28977424Smsmith
29077424Smsmith/*******************************************************************************
29177424Smsmith *
29287031Smsmith * FUNCTION:    AcpiExAccessRegion
29377424Smsmith *
294151937Sjkim * PARAMETERS:  ObjDesc                 - Field to be read
29587031Smsmith *              FieldDatumByteOffset    - Byte offset of this datum within the
29687031Smsmith *                                        parent field
297151937Sjkim *              Value                   - Where to store value (must at least
298202771Sjkim *                                        64 bits)
299107325Siwasaki *              Function                - Read or Write flag plus other region-
300107325Siwasaki *                                        dependent flags
30177424Smsmith *
30277424Smsmith * RETURN:      Status
30377424Smsmith *
30487031Smsmith * DESCRIPTION: Read or Write a single field datum to an Operation Region.
30577424Smsmith *
30677424Smsmith ******************************************************************************/
30777424Smsmith
30877424SmsmithACPI_STATUS
30987031SmsmithAcpiExAccessRegion (
31077424Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
31177424Smsmith    UINT32                  FieldDatumByteOffset,
312202771Sjkim    UINT64                  *Value,
313107325Siwasaki    UINT32                  Function)
31477424Smsmith{
31577424Smsmith    ACPI_STATUS             Status;
31677424Smsmith    ACPI_OPERAND_OBJECT     *RgnDesc;
317193267Sjkim    UINT32                  RegionOffset;
31877424Smsmith
31977424Smsmith
320167802Sjkim    ACPI_FUNCTION_TRACE (ExAccessRegion);
32177424Smsmith
32277424Smsmith
323114237Snjl    /*
324107325Siwasaki     * Ensure that the region operands are fully evaluated and verify
325107325Siwasaki     * the validity of the request
326107325Siwasaki     */
327107325Siwasaki    Status = AcpiExSetupRegion (ObjDesc, FieldDatumByteOffset);
328107325Siwasaki    if (ACPI_FAILURE (Status))
329107325Siwasaki    {
330107325Siwasaki        return_ACPI_STATUS (Status);
331107325Siwasaki    }
332107325Siwasaki
33387031Smsmith    /*
33487031Smsmith     * The physical address of this field datum is:
33587031Smsmith     *
33687031Smsmith     * 1) The base of the region, plus
33787031Smsmith     * 2) The base offset of the field, plus
33887031Smsmith     * 3) The current offset into the field
33987031Smsmith     */
34087031Smsmith    RgnDesc = ObjDesc->CommonField.RegionObj;
341193267Sjkim    RegionOffset =
342193267Sjkim        ObjDesc->CommonField.BaseByteOffset +
343193267Sjkim        FieldDatumByteOffset;
34487031Smsmith
345107325Siwasaki    if ((Function & ACPI_IO_MASK) == ACPI_READ)
34667754Smsmith    {
34787031Smsmith        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[READ]"));
34867754Smsmith    }
34987031Smsmith    else
35087031Smsmith    {
35187031Smsmith        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[WRITE]"));
35287031Smsmith    }
35367754Smsmith
35487031Smsmith    ACPI_DEBUG_PRINT_RAW ((ACPI_DB_BFIELD,
355167802Sjkim        " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %p\n",
35687031Smsmith        AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
35787031Smsmith        RgnDesc->Region.SpaceId,
35891116Smsmith        ObjDesc->CommonField.AccessByteWidth,
35987031Smsmith        ObjDesc->CommonField.BaseByteOffset,
36087031Smsmith        FieldDatumByteOffset,
361193267Sjkim        ACPI_CAST_PTR (void, (RgnDesc->Region.Address + RegionOffset))));
36267754Smsmith
36387031Smsmith    /* Invoke the appropriate AddressSpace/OpRegion handler */
36477424Smsmith
365193267Sjkim    Status = AcpiEvAddressSpaceDispatch (RgnDesc, Function, RegionOffset,
366151937Sjkim                ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth), Value);
36787031Smsmith
36887031Smsmith    if (ACPI_FAILURE (Status))
36987031Smsmith    {
37087031Smsmith        if (Status == AE_NOT_IMPLEMENTED)
37187031Smsmith        {
372167802Sjkim            ACPI_ERROR ((AE_INFO,
373204773Sjkim                "Region %s(0x%X) not implemented",
37487031Smsmith                AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
37587031Smsmith                RgnDesc->Region.SpaceId));
37687031Smsmith        }
37787031Smsmith        else if (Status == AE_NOT_EXIST)
37887031Smsmith        {
379167802Sjkim            ACPI_ERROR ((AE_INFO,
380204773Sjkim                "Region %s(0x%X) has no handler",
38187031Smsmith                AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
38287031Smsmith                RgnDesc->Region.SpaceId));
38387031Smsmith        }
38487031Smsmith    }
38587031Smsmith
38687031Smsmith    return_ACPI_STATUS (Status);
38787031Smsmith}
38887031Smsmith
38987031Smsmith
39087031Smsmith/*******************************************************************************
39187031Smsmith *
39287031Smsmith * FUNCTION:    AcpiExRegisterOverflow
39387031Smsmith *
394151937Sjkim * PARAMETERS:  ObjDesc                 - Register(Field) to be written
39587031Smsmith *              Value                   - Value to be stored
39687031Smsmith *
39787031Smsmith * RETURN:      TRUE if value overflows the field, FALSE otherwise
39887031Smsmith *
39987031Smsmith * DESCRIPTION: Check if a value is out of range of the field being written.
40087031Smsmith *              Used to check if the values written to Index and Bank registers
40187031Smsmith *              are out of range.  Normally, the value is simply truncated
40287031Smsmith *              to fit the field, but this case is most likely a serious
40387031Smsmith *              coding error in the ASL.
40487031Smsmith *
40587031Smsmith ******************************************************************************/
40687031Smsmith
407151937Sjkimstatic BOOLEAN
40887031SmsmithAcpiExRegisterOverflow (
40987031Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
410202771Sjkim    UINT64                  Value)
41187031Smsmith{
41287031Smsmith
41391116Smsmith    if (ObjDesc->CommonField.BitLength >= ACPI_INTEGER_BIT_SIZE)
41491116Smsmith    {
41591116Smsmith        /*
41691116Smsmith         * The field is large enough to hold the maximum integer, so we can
41791116Smsmith         * never overflow it.
41891116Smsmith         */
41991116Smsmith        return (FALSE);
42091116Smsmith    }
42191116Smsmith
422202771Sjkim    if (Value >= ((UINT64) 1 << ObjDesc->CommonField.BitLength))
42387031Smsmith    {
42491116Smsmith        /*
42591116Smsmith         * The Value is larger than the maximum value that can fit into
42691116Smsmith         * the register.
42791116Smsmith         */
42887031Smsmith        return (TRUE);
42987031Smsmith    }
43087031Smsmith
43191116Smsmith    /* The Value will fit into the field with no truncation */
43291116Smsmith
43387031Smsmith    return (FALSE);
43487031Smsmith}
43587031Smsmith
43687031Smsmith
43787031Smsmith/*******************************************************************************
43887031Smsmith *
43987031Smsmith * FUNCTION:    AcpiExFieldDatumIo
44087031Smsmith *
441151937Sjkim * PARAMETERS:  ObjDesc                 - Field to be read
44287031Smsmith *              FieldDatumByteOffset    - Byte offset of this datum within the
44387031Smsmith *                                        parent field
444151937Sjkim *              Value                   - Where to store value (must be 64 bits)
44587031Smsmith *              ReadWrite               - Read or Write flag
44687031Smsmith *
44787031Smsmith * RETURN:      Status
44887031Smsmith *
44987031Smsmith * DESCRIPTION: Read or Write a single datum of a field.  The FieldType is
45087031Smsmith *              demultiplexed here to handle the different types of fields
45187031Smsmith *              (BufferField, RegionField, IndexField, BankField)
45287031Smsmith *
45387031Smsmith ******************************************************************************/
45487031Smsmith
455151937Sjkimstatic ACPI_STATUS
45687031SmsmithAcpiExFieldDatumIo (
45787031Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
45887031Smsmith    UINT32                  FieldDatumByteOffset,
459202771Sjkim    UINT64                  *Value,
46087031Smsmith    UINT32                  ReadWrite)
46187031Smsmith{
46287031Smsmith    ACPI_STATUS             Status;
463202771Sjkim    UINT64                  LocalValue;
46487031Smsmith
46587031Smsmith
466167802Sjkim    ACPI_FUNCTION_TRACE_U32 (ExFieldDatumIo, FieldDatumByteOffset);
46787031Smsmith
46887031Smsmith
46987031Smsmith    if (ReadWrite == ACPI_READ)
47087031Smsmith    {
47187031Smsmith        if (!Value)
47287031Smsmith        {
47387031Smsmith            LocalValue = 0;
474151937Sjkim
475151937Sjkim            /* To support reads without saving return value */
476151937Sjkim            Value = &LocalValue;
47787031Smsmith        }
47887031Smsmith
47987031Smsmith        /* Clear the entire return buffer first, [Very Important!] */
48087031Smsmith
48187031Smsmith        *Value = 0;
48287031Smsmith    }
48387031Smsmith
48487031Smsmith    /*
48587031Smsmith     * The four types of fields are:
48687031Smsmith     *
487122944Snjl     * BufferField - Read/write from/to a Buffer
488122944Snjl     * RegionField - Read/write from/to a Operation Region.
489151937Sjkim     * BankField   - Write to a Bank Register, then read/write from/to an
490151937Sjkim     *               OperationRegion
491151937Sjkim     * IndexField  - Write to an Index Register, then read/write from/to a
492151937Sjkim     *               Data Register
49387031Smsmith     */
494193267Sjkim    switch (ObjDesc->Common.Type)
49577424Smsmith    {
49677424Smsmith    case ACPI_TYPE_BUFFER_FIELD:
49777424Smsmith        /*
49887031Smsmith         * If the BufferField arguments have not been previously evaluated,
49987031Smsmith         * evaluate them now and save the results.
50077424Smsmith         */
50187031Smsmith        if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
50287031Smsmith        {
50387031Smsmith            Status = AcpiDsGetBufferFieldArguments (ObjDesc);
50487031Smsmith            if (ACPI_FAILURE (Status))
50587031Smsmith            {
50687031Smsmith                return_ACPI_STATUS (Status);
50787031Smsmith            }
50887031Smsmith        }
50987031Smsmith
51087031Smsmith        if (ReadWrite == ACPI_READ)
51187031Smsmith        {
51287031Smsmith            /*
51387031Smsmith             * Copy the data from the source buffer.
51487031Smsmith             * Length is the field width in bytes.
51587031Smsmith             */
516151937Sjkim            ACPI_MEMCPY (Value,
517151937Sjkim                (ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
518151937Sjkim                    ObjDesc->BufferField.BaseByteOffset +
519151937Sjkim                    FieldDatumByteOffset,
520151937Sjkim                ObjDesc->CommonField.AccessByteWidth);
52187031Smsmith        }
52287031Smsmith        else
52387031Smsmith        {
52487031Smsmith            /*
52587031Smsmith             * Copy the data to the target buffer.
52687031Smsmith             * Length is the field width in bytes.
52787031Smsmith             */
528151937Sjkim            ACPI_MEMCPY ((ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
529167802Sjkim                ObjDesc->BufferField.BaseByteOffset +
530167802Sjkim                FieldDatumByteOffset,
531167802Sjkim                Value, ObjDesc->CommonField.AccessByteWidth);
53287031Smsmith        }
53387031Smsmith
53477424Smsmith        Status = AE_OK;
53577424Smsmith        break;
53667754Smsmith
53767754Smsmith
538107325Siwasaki    case ACPI_TYPE_LOCAL_BANK_FIELD:
53967754Smsmith
540151937Sjkim        /*
541151937Sjkim         * Ensure that the BankValue is not beyond the capacity of
542151937Sjkim         * the register
543151937Sjkim         */
54487031Smsmith        if (AcpiExRegisterOverflow (ObjDesc->BankField.BankObj,
545202771Sjkim                (UINT64) ObjDesc->BankField.Value))
54687031Smsmith        {
54787031Smsmith            return_ACPI_STATUS (AE_AML_REGISTER_LIMIT);
54887031Smsmith        }
54987031Smsmith
55077424Smsmith        /*
55187031Smsmith         * For BankFields, we must write the BankValue to the BankRegister
55287031Smsmith         * (itself a RegionField) before we can access the data.
55377424Smsmith         */
55487031Smsmith        Status = AcpiExInsertIntoField (ObjDesc->BankField.BankObj,
555167802Sjkim                    &ObjDesc->BankField.Value,
556167802Sjkim                    sizeof (ObjDesc->BankField.Value));
55777424Smsmith        if (ACPI_FAILURE (Status))
55877424Smsmith        {
55977424Smsmith            return_ACPI_STATUS (Status);
56077424Smsmith        }
56167754Smsmith
56277424Smsmith        /*
56387031Smsmith         * Now that the Bank has been selected, fall through to the
56487031Smsmith         * RegionField case and write the datum to the Operation Region
56577424Smsmith         */
56677424Smsmith
56799679Siwasaki        /*lint -fallthrough */
56877424Smsmith
56977424Smsmith
570107325Siwasaki    case ACPI_TYPE_LOCAL_REGION_FIELD:
57187031Smsmith        /*
57287031Smsmith         * For simple RegionFields, we just directly access the owning
57387031Smsmith         * Operation Region.
57487031Smsmith         */
57587031Smsmith        Status = AcpiExAccessRegion (ObjDesc, FieldDatumByteOffset, Value,
576167802Sjkim                    ReadWrite);
57787031Smsmith        break;
57887031Smsmith
57987031Smsmith
580107325Siwasaki    case ACPI_TYPE_LOCAL_INDEX_FIELD:
58187031Smsmith
58287031Smsmith
583151937Sjkim        /*
584151937Sjkim         * Ensure that the IndexValue is not beyond the capacity of
585151937Sjkim         * the register
586151937Sjkim         */
58787031Smsmith        if (AcpiExRegisterOverflow (ObjDesc->IndexField.IndexObj,
588202771Sjkim                (UINT64) ObjDesc->IndexField.Value))
58977424Smsmith        {
59087031Smsmith            return_ACPI_STATUS (AE_AML_REGISTER_LIMIT);
59177424Smsmith        }
59287031Smsmith
59387031Smsmith        /* Write the index value to the IndexRegister (itself a RegionField) */
59487031Smsmith
595122944Snjl        FieldDatumByteOffset += ObjDesc->IndexField.Value;
596122944Snjl
597122944Snjl        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
598167802Sjkim            "Write to Index Register: Value %8.8X\n",
599167802Sjkim            FieldDatumByteOffset));
600122944Snjl
60187031Smsmith        Status = AcpiExInsertIntoField (ObjDesc->IndexField.IndexObj,
602167802Sjkim                    &FieldDatumByteOffset,
603167802Sjkim                    sizeof (FieldDatumByteOffset));
60487031Smsmith        if (ACPI_FAILURE (Status))
60587031Smsmith        {
60687031Smsmith            return_ACPI_STATUS (Status);
60787031Smsmith        }
60887031Smsmith
60987031Smsmith        if (ReadWrite == ACPI_READ)
61087031Smsmith        {
61187031Smsmith            /* Read the datum from the DataRegister */
61287031Smsmith
613193267Sjkim            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
614193267Sjkim                "Read from Data Register\n"));
615193267Sjkim
61687031Smsmith            Status = AcpiExExtractFromField (ObjDesc->IndexField.DataObj,
617202771Sjkim                        Value, sizeof (UINT64));
61887031Smsmith        }
61987031Smsmith        else
62087031Smsmith        {
621122944Snjl            /* Write the datum to the DataRegister */
62287031Smsmith
623193267Sjkim            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
624193267Sjkim                "Write to Data Register: Value %8.8X%8.8X\n",
625193267Sjkim                ACPI_FORMAT_UINT64 (*Value)));
626193267Sjkim
62787031Smsmith            Status = AcpiExInsertIntoField (ObjDesc->IndexField.DataObj,
628202771Sjkim                        Value, sizeof (UINT64));
62987031Smsmith        }
63077424Smsmith        break;
63177424Smsmith
63277424Smsmith
63377424Smsmith    default:
63477424Smsmith
635204773Sjkim        ACPI_ERROR ((AE_INFO, "Wrong object type in field I/O %u",
636193267Sjkim            ObjDesc->Common.Type));
63777424Smsmith        Status = AE_AML_INTERNAL;
63877424Smsmith        break;
63967754Smsmith    }
64067754Smsmith
64187031Smsmith    if (ACPI_SUCCESS (Status))
64287031Smsmith    {
64387031Smsmith        if (ReadWrite == ACPI_READ)
64487031Smsmith        {
645151937Sjkim            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
646209746Sjkim                "Value Read %8.8X%8.8X, Width %u\n",
647151937Sjkim                ACPI_FORMAT_UINT64 (*Value),
648151937Sjkim                ObjDesc->CommonField.AccessByteWidth));
64987031Smsmith        }
65087031Smsmith        else
65187031Smsmith        {
652151937Sjkim            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
653209746Sjkim                "Value Written %8.8X%8.8X, Width %u\n",
654151937Sjkim                ACPI_FORMAT_UINT64 (*Value),
655151937Sjkim                ObjDesc->CommonField.AccessByteWidth));
65687031Smsmith        }
65787031Smsmith    }
65877424Smsmith
65987031Smsmith    return_ACPI_STATUS (Status);
66087031Smsmith}
66177424Smsmith
66287031Smsmith
66387031Smsmith/*******************************************************************************
66487031Smsmith *
66587031Smsmith * FUNCTION:    AcpiExWriteWithUpdateRule
66687031Smsmith *
667151937Sjkim * PARAMETERS:  ObjDesc                 - Field to be written
668151937Sjkim *              Mask                    - bitmask within field datum
669151937Sjkim *              FieldValue              - Value to write
670151937Sjkim *              FieldDatumByteOffset    - Offset of datum within field
67187031Smsmith *
67287031Smsmith * RETURN:      Status
67387031Smsmith *
67487031Smsmith * DESCRIPTION: Apply the field update rule to a field write
67587031Smsmith *
67687031Smsmith ******************************************************************************/
67787031Smsmith
67887031SmsmithACPI_STATUS
67987031SmsmithAcpiExWriteWithUpdateRule (
68087031Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
681202771Sjkim    UINT64                  Mask,
682202771Sjkim    UINT64                  FieldValue,
68387031Smsmith    UINT32                  FieldDatumByteOffset)
68487031Smsmith{
68587031Smsmith    ACPI_STATUS             Status = AE_OK;
686202771Sjkim    UINT64                  MergedValue;
687202771Sjkim    UINT64                  CurrentValue;
68887031Smsmith
68987031Smsmith
690167802Sjkim    ACPI_FUNCTION_TRACE_U32 (ExWriteWithUpdateRule, Mask);
69187031Smsmith
69287031Smsmith
69387031Smsmith    /* Start with the new bits  */
69487031Smsmith
69587031Smsmith    MergedValue = FieldValue;
69687031Smsmith
69787031Smsmith    /* If the mask is all ones, we don't need to worry about the update rule */
69887031Smsmith
699202771Sjkim    if (Mask != ACPI_UINT64_MAX)
70087031Smsmith    {
70187031Smsmith        /* Decode the update rule */
70287031Smsmith
70387031Smsmith        switch (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)
70487031Smsmith        {
70587031Smsmith        case AML_FIELD_UPDATE_PRESERVE:
70687031Smsmith            /*
70787031Smsmith             * Check if update rule needs to be applied (not if mask is all
70887031Smsmith             * ones)  The left shift drops the bits we want to ignore.
70987031Smsmith             */
71091116Smsmith            if ((~Mask << (ACPI_MUL_8 (sizeof (Mask)) -
71191116Smsmith                           ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth))) != 0)
71287031Smsmith            {
71387031Smsmith                /*
71487031Smsmith                 * Read the current contents of the byte/word/dword containing
71587031Smsmith                 * the field, and merge with the new field value.
71687031Smsmith                 */
71787031Smsmith                Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
718167802Sjkim                            &CurrentValue, ACPI_READ);
719123315Snjl                if (ACPI_FAILURE (Status))
720123315Snjl                {
721123315Snjl                    return_ACPI_STATUS (Status);
722123315Snjl                }
723123315Snjl
72487031Smsmith                MergedValue |= (CurrentValue & ~Mask);
72587031Smsmith            }
72687031Smsmith            break;
72787031Smsmith
72887031Smsmith        case AML_FIELD_UPDATE_WRITE_AS_ONES:
72987031Smsmith
73087031Smsmith            /* Set positions outside the field to all ones */
73187031Smsmith
73287031Smsmith            MergedValue |= ~Mask;
73387031Smsmith            break;
73487031Smsmith
73587031Smsmith        case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
73687031Smsmith
73787031Smsmith            /* Set positions outside the field to all zeros */
73887031Smsmith
73987031Smsmith            MergedValue &= Mask;
74087031Smsmith            break;
74187031Smsmith
74287031Smsmith        default:
743123315Snjl
744167802Sjkim            ACPI_ERROR ((AE_INFO,
745204773Sjkim                "Unknown UpdateRule value: 0x%X",
74687031Smsmith                (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)));
74787031Smsmith            return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
74887031Smsmith        }
74987031Smsmith    }
75087031Smsmith
751123315Snjl    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
752123315Snjl        "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
753123315Snjl        ACPI_FORMAT_UINT64 (Mask),
754123315Snjl        FieldDatumByteOffset,
755123315Snjl        ObjDesc->CommonField.AccessByteWidth,
756123315Snjl        ACPI_FORMAT_UINT64 (FieldValue),
757123315Snjl        ACPI_FORMAT_UINT64 (MergedValue)));
758123315Snjl
75987031Smsmith    /* Write the merged value */
76087031Smsmith
76187031Smsmith    Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
762167802Sjkim                &MergedValue, ACPI_WRITE);
76387031Smsmith
76477424Smsmith    return_ACPI_STATUS (Status);
76577424Smsmith}
76677424Smsmith
76777424Smsmith
76877424Smsmith/*******************************************************************************
76977424Smsmith *
77077424Smsmith * FUNCTION:    AcpiExExtractFromField
77167754Smsmith *
772131440Smarks * PARAMETERS:  ObjDesc             - Field to be read
773131440Smarks *              Buffer              - Where to store the field data
774131440Smarks *              BufferLength        - Length of Buffer
77567754Smsmith *
77667754Smsmith * RETURN:      Status
77767754Smsmith *
778131440Smarks * DESCRIPTION: Retrieve the current value of the given field
77967754Smsmith *
78067754Smsmith ******************************************************************************/
78167754Smsmith
78267754SmsmithACPI_STATUS
78377424SmsmithAcpiExExtractFromField (
78467754Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
78567754Smsmith    void                    *Buffer,
78677424Smsmith    UINT32                  BufferLength)
78767754Smsmith{
78867754Smsmith    ACPI_STATUS             Status;
789202771Sjkim    UINT64                  RawDatum;
790202771Sjkim    UINT64                  MergedDatum;
791151937Sjkim    UINT32                  FieldOffset = 0;
792151937Sjkim    UINT32                  BufferOffset = 0;
793151937Sjkim    UINT32                  BufferTailBits;
79477424Smsmith    UINT32                  DatumCount;
795151937Sjkim    UINT32                  FieldDatumCount;
796210976Sjkim    UINT32                  AccessBitWidth;
797123315Snjl    UINT32                  i;
79867754Smsmith
79967754Smsmith
800167802Sjkim    ACPI_FUNCTION_TRACE (ExExtractFromField);
80167754Smsmith
80277424Smsmith
803151937Sjkim    /* Validate target buffer and clear it */
80477424Smsmith
805167802Sjkim    if (BufferLength <
806210976Sjkim        ACPI_ROUND_BITS_UP_TO_BYTES (ObjDesc->CommonField.BitLength))
807151937Sjkim    {
808167802Sjkim        ACPI_ERROR ((AE_INFO,
809204773Sjkim            "Field size %u (bits) is too large for buffer (%u)",
810151937Sjkim            ObjDesc->CommonField.BitLength, BufferLength));
811151937Sjkim
812151937Sjkim        return_ACPI_STATUS (AE_BUFFER_OVERFLOW);
813151937Sjkim    }
814210976Sjkim
815151937Sjkim    ACPI_MEMSET (Buffer, 0, BufferLength);
816210976Sjkim    AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
817151937Sjkim
818210976Sjkim    /* Handle the simple case here */
819210976Sjkim
820210976Sjkim    if ((ObjDesc->CommonField.StartFieldBitOffset == 0) &&
821210976Sjkim        (ObjDesc->CommonField.BitLength == AccessBitWidth))
822210976Sjkim    {
823210976Sjkim        Status = AcpiExFieldDatumIo (ObjDesc, 0, Buffer, ACPI_READ);
824210976Sjkim        return_ACPI_STATUS (Status);
825210976Sjkim    }
826210976Sjkim
827210976Sjkim/* TBD: Move to common setup code */
828210976Sjkim
829210976Sjkim    /* Field algorithm is limited to sizeof(UINT64), truncate if needed */
830210976Sjkim
831210976Sjkim    if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
832210976Sjkim    {
833210976Sjkim        ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
834210976Sjkim        AccessBitWidth = sizeof (UINT64) * 8;
835210976Sjkim    }
836210976Sjkim
837151937Sjkim    /* Compute the number of datums (access width data items) */
838151937Sjkim
839151937Sjkim    DatumCount = ACPI_ROUND_UP_TO (
840210976Sjkim        ObjDesc->CommonField.BitLength, AccessBitWidth);
841210976Sjkim
842151937Sjkim    FieldDatumCount = ACPI_ROUND_UP_TO (
843210976Sjkim        ObjDesc->CommonField.BitLength +
844210976Sjkim        ObjDesc->CommonField.StartFieldBitOffset, AccessBitWidth);
845151937Sjkim
846151937Sjkim    /* Priming read from the field */
847151937Sjkim
848151937Sjkim    Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset, &RawDatum, ACPI_READ);
849131440Smarks    if (ACPI_FAILURE (Status))
850123315Snjl    {
851131440Smarks        return_ACPI_STATUS (Status);
852123315Snjl    }
853151937Sjkim    MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
854123315Snjl
855151937Sjkim    /* Read the rest of the field */
85667754Smsmith
857151937Sjkim    for (i = 1; i < FieldDatumCount; i++)
858151937Sjkim    {
859151937Sjkim        /* Get next input datum from the field */
86067754Smsmith
861151937Sjkim        FieldOffset += ObjDesc->CommonField.AccessByteWidth;
862151937Sjkim        Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset,
863167802Sjkim                    &RawDatum, ACPI_READ);
864123315Snjl        if (ACPI_FAILURE (Status))
865123315Snjl        {
866123315Snjl            return_ACPI_STATUS (Status);
867123315Snjl        }
86867754Smsmith
869167802Sjkim        /*
870167802Sjkim         * Merge with previous datum if necessary.
871167802Sjkim         *
872167802Sjkim         * Note: Before the shift, check if the shift value will be larger than
873167802Sjkim         * the integer size. If so, there is no need to perform the operation.
874167802Sjkim         * This avoids the differences in behavior between different compilers
875167802Sjkim         * concerning shift values larger than the target data width.
876167802Sjkim         */
877210976Sjkim        if (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset <
878210976Sjkim            ACPI_INTEGER_BIT_SIZE)
879167802Sjkim        {
880167802Sjkim            MergedDatum |= RawDatum <<
881210976Sjkim                (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
882167802Sjkim        }
88367754Smsmith
884151937Sjkim        if (i == DatumCount)
88567754Smsmith        {
886151937Sjkim            break;
88777424Smsmith        }
88867754Smsmith
889151937Sjkim        /* Write merged datum to target buffer */
89071867Smsmith
891151937Sjkim        ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
892151937Sjkim            ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
893167802Sjkim                BufferLength - BufferOffset));
89467754Smsmith
895151937Sjkim        BufferOffset += ObjDesc->CommonField.AccessByteWidth;
896151937Sjkim        MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
897151937Sjkim    }
898123315Snjl
899151937Sjkim    /* Mask off any extra bits in the last datum */
900123315Snjl
901210976Sjkim    BufferTailBits = ObjDesc->CommonField.BitLength % AccessBitWidth;
902151937Sjkim    if (BufferTailBits)
903151937Sjkim    {
904151937Sjkim        MergedDatum &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
905123315Snjl    }
906123315Snjl
907151937Sjkim    /* Write the last datum to the buffer */
908123315Snjl
909151937Sjkim    ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
910151937Sjkim        ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
911167802Sjkim            BufferLength - BufferOffset));
912123315Snjl
91377424Smsmith    return_ACPI_STATUS (AE_OK);
91467754Smsmith}
91567754Smsmith
91667754Smsmith
91767754Smsmith/*******************************************************************************
91867754Smsmith *
91977424Smsmith * FUNCTION:    AcpiExInsertIntoField
92067754Smsmith *
921131440Smarks * PARAMETERS:  ObjDesc             - Field to be written
922131440Smarks *              Buffer              - Data to be written
923131440Smarks *              BufferLength        - Length of Buffer
92467754Smsmith *
92567754Smsmith * RETURN:      Status
92667754Smsmith *
927131440Smarks * DESCRIPTION: Store the Buffer contents into the given field
92867754Smsmith *
92977424Smsmith ******************************************************************************/
93067754Smsmith
93167754SmsmithACPI_STATUS
93277424SmsmithAcpiExInsertIntoField (
93367754Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
93467754Smsmith    void                    *Buffer,
93577424Smsmith    UINT32                  BufferLength)
93667754Smsmith{
937210976Sjkim    void                    *NewBuffer;
93867754Smsmith    ACPI_STATUS             Status;
939202771Sjkim    UINT64                  Mask;
940202771Sjkim    UINT64                  WidthMask;
941202771Sjkim    UINT64                  MergedDatum;
942202771Sjkim    UINT64                  RawDatum = 0;
943151937Sjkim    UINT32                  FieldOffset = 0;
944151937Sjkim    UINT32                  BufferOffset = 0;
945151937Sjkim    UINT32                  BufferTailBits;
94677424Smsmith    UINT32                  DatumCount;
947151937Sjkim    UINT32                  FieldDatumCount;
948210976Sjkim    UINT32                  AccessBitWidth;
949210976Sjkim    UINT32                  RequiredLength;
950151937Sjkim    UINT32                  i;
95167754Smsmith
95267754Smsmith
953167802Sjkim    ACPI_FUNCTION_TRACE (ExInsertIntoField);
95467754Smsmith
95567754Smsmith
956151937Sjkim    /* Validate input buffer */
957131440Smarks
958193267Sjkim    NewBuffer = NULL;
959193267Sjkim    RequiredLength = ACPI_ROUND_BITS_UP_TO_BYTES (
960193267Sjkim                        ObjDesc->CommonField.BitLength);
961193267Sjkim    /*
962193267Sjkim     * We must have a buffer that is at least as long as the field
963193267Sjkim     * we are writing to.  This is because individual fields are
964193267Sjkim     * indivisible and partial writes are not supported -- as per
965193267Sjkim     * the ACPI specification.
966193267Sjkim     */
967193267Sjkim    if (BufferLength < RequiredLength)
96877424Smsmith    {
969193267Sjkim        /* We need to create a new buffer */
970151937Sjkim
971193267Sjkim        NewBuffer = ACPI_ALLOCATE_ZEROED (RequiredLength);
972193267Sjkim        if (!NewBuffer)
973193267Sjkim        {
974193267Sjkim            return_ACPI_STATUS (AE_NO_MEMORY);
975193267Sjkim        }
976193267Sjkim
977193267Sjkim        /*
978193267Sjkim         * Copy the original data to the new buffer, starting
979193267Sjkim         * at Byte zero.  All unused (upper) bytes of the
980193267Sjkim         * buffer will be 0.
981193267Sjkim         */
982193267Sjkim        ACPI_MEMCPY ((char *) NewBuffer, (char *) Buffer, BufferLength);
983193267Sjkim        Buffer = NewBuffer;
984193267Sjkim        BufferLength = RequiredLength;
98577424Smsmith    }
98667754Smsmith
987210976Sjkim/* TBD: Move to common setup code */
988210976Sjkim
989210976Sjkim    /* Algo is limited to sizeof(UINT64), so cut the AccessByteWidth */
990210976Sjkim    if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
991210976Sjkim    {
992210976Sjkim        ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
993210976Sjkim    }
994210976Sjkim
995210976Sjkim    AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
996210976Sjkim
997167802Sjkim    /*
998167802Sjkim     * Create the bitmasks used for bit insertion.
999167802Sjkim     * Note: This if/else is used to bypass compiler differences with the
1000167802Sjkim     * shift operator
1001167802Sjkim     */
1002210976Sjkim    if (AccessBitWidth == ACPI_INTEGER_BIT_SIZE)
1003167802Sjkim    {
1004202771Sjkim        WidthMask = ACPI_UINT64_MAX;
1005167802Sjkim    }
1006167802Sjkim    else
1007167802Sjkim    {
1008210976Sjkim        WidthMask = ACPI_MASK_BITS_ABOVE (AccessBitWidth);
1009167802Sjkim    }
1010167802Sjkim
1011167802Sjkim    Mask = WidthMask &
1012210976Sjkim        ACPI_MASK_BITS_BELOW (ObjDesc->CommonField.StartFieldBitOffset);
1013167802Sjkim
1014151937Sjkim    /* Compute the number of datums (access width data items) */
101567754Smsmith
1016151937Sjkim    DatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength,
1017210976Sjkim        AccessBitWidth);
1018167802Sjkim
1019151937Sjkim    FieldDatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength +
1020210976Sjkim        ObjDesc->CommonField.StartFieldBitOffset,
1021210976Sjkim        AccessBitWidth);
102267754Smsmith
1023151937Sjkim    /* Get initial Datum from the input buffer */
102467754Smsmith
1025151937Sjkim    ACPI_MEMCPY (&RawDatum, Buffer,
1026151937Sjkim        ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
1027167802Sjkim            BufferLength - BufferOffset));
102867754Smsmith
1029151937Sjkim    MergedDatum = RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
103067754Smsmith
1031151937Sjkim    /* Write the entire field */
103267754Smsmith
1033151937Sjkim    for (i = 1; i < FieldDatumCount; i++)
103467754Smsmith    {
1035151937Sjkim        /* Write merged datum to the target field */
103667754Smsmith
1037151937Sjkim        MergedDatum &= Mask;
1038151937Sjkim        Status = AcpiExWriteWithUpdateRule (ObjDesc, Mask,
1039151937Sjkim                    MergedDatum, FieldOffset);
1040151937Sjkim        if (ACPI_FAILURE (Status))
1041151937Sjkim        {
1042193267Sjkim            goto Exit;
1043151937Sjkim        }
1044123315Snjl
1045151937Sjkim        FieldOffset += ObjDesc->CommonField.AccessByteWidth;
104667754Smsmith
1047167802Sjkim        /*
1048167802Sjkim         * Start new output datum by merging with previous input datum
1049167802Sjkim         * if necessary.
1050167802Sjkim         *
1051167802Sjkim         * Note: Before the shift, check if the shift value will be larger than
1052167802Sjkim         * the integer size. If so, there is no need to perform the operation.
1053167802Sjkim         * This avoids the differences in behavior between different compilers
1054167802Sjkim         * concerning shift values larger than the target data width.
1055167802Sjkim         */
1056210976Sjkim        if ((AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset) <
1057210976Sjkim            ACPI_INTEGER_BIT_SIZE)
1058167802Sjkim        {
1059167802Sjkim            MergedDatum = RawDatum >>
1060210976Sjkim                (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
1061167802Sjkim        }
1062167802Sjkim        else
1063167802Sjkim        {
1064167802Sjkim            MergedDatum = 0;
1065167802Sjkim        }
1066167802Sjkim
1067167802Sjkim        Mask = WidthMask;
1068167802Sjkim
1069151937Sjkim        if (i == DatumCount)
107067754Smsmith        {
1071151937Sjkim            break;
107267754Smsmith        }
107377424Smsmith
1074151937Sjkim        /* Get the next input datum from the buffer */
107567754Smsmith
1076151937Sjkim        BufferOffset += ObjDesc->CommonField.AccessByteWidth;
1077151937Sjkim        ACPI_MEMCPY (&RawDatum, ((char *) Buffer) + BufferOffset,
1078151937Sjkim            ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
1079210976Sjkim                 BufferLength - BufferOffset));
1080210976Sjkim
1081151937Sjkim        MergedDatum |= RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
1082151937Sjkim    }
108367754Smsmith
1084151937Sjkim    /* Mask off any extra bits in the last datum */
108567754Smsmith
1086151937Sjkim    BufferTailBits = (ObjDesc->CommonField.BitLength +
1087210976Sjkim        ObjDesc->CommonField.StartFieldBitOffset) % AccessBitWidth;
1088151937Sjkim    if (BufferTailBits)
1089151937Sjkim    {
1090151937Sjkim        Mask &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
1091151937Sjkim    }
1092123315Snjl
1093151937Sjkim    /* Write the last datum to the field */
109467754Smsmith
1095151937Sjkim    MergedDatum &= Mask;
1096151937Sjkim    Status = AcpiExWriteWithUpdateRule (ObjDesc,
1097151937Sjkim                Mask, MergedDatum, FieldOffset);
109867754Smsmith
1099193267SjkimExit:
1100193267Sjkim    /* Free temporary buffer if we used one */
1101193267Sjkim
1102193267Sjkim    if (NewBuffer)
1103193267Sjkim    {
1104193267Sjkim        ACPI_FREE (NewBuffer);
1105193267Sjkim    }
110667754Smsmith    return_ACPI_STATUS (Status);
110767754Smsmith}
110867754Smsmith
110967754Smsmith
1110