167754Smsmith/******************************************************************************
267754Smsmith *
377424Smsmith * Module Name: exfldio - Aml Field I/O
467754Smsmith *
567754Smsmith *****************************************************************************/
667754Smsmith
7217365Sjkim/*
8245582Sjkim * Copyright (C) 2000 - 2013, Intel Corp.
970243Smsmith * All rights reserved.
1067754Smsmith *
11217365Sjkim * Redistribution and use in source and binary forms, with or without
12217365Sjkim * modification, are permitted provided that the following conditions
13217365Sjkim * are met:
14217365Sjkim * 1. Redistributions of source code must retain the above copyright
15217365Sjkim *    notice, this list of conditions, and the following disclaimer,
16217365Sjkim *    without modification.
17217365Sjkim * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18217365Sjkim *    substantially similar to the "NO WARRANTY" disclaimer below
19217365Sjkim *    ("Disclaimer") and any redistribution must be conditioned upon
20217365Sjkim *    including a substantially similar Disclaimer requirement for further
21217365Sjkim *    binary redistribution.
22217365Sjkim * 3. Neither the names of the above-listed copyright holders nor the names
23217365Sjkim *    of any contributors may be used to endorse or promote products derived
24217365Sjkim *    from this software without specific prior written permission.
2567754Smsmith *
26217365Sjkim * Alternatively, this software may be distributed under the terms of the
27217365Sjkim * GNU General Public License ("GPL") version 2 as published by the Free
28217365Sjkim * Software Foundation.
2967754Smsmith *
30217365Sjkim * NO WARRANTY
31217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32217365Sjkim * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33217365Sjkim * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34217365Sjkim * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35217365Sjkim * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36217365Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37217365Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39217365Sjkim * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40217365Sjkim * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41217365Sjkim * POSSIBILITY OF SUCH DAMAGES.
42217365Sjkim */
4367754Smsmith
4467754Smsmith
4577424Smsmith#define __EXFLDIO_C__
4667754Smsmith
47193341Sjkim#include <contrib/dev/acpica/include/acpi.h>
48193341Sjkim#include <contrib/dev/acpica/include/accommon.h>
49193341Sjkim#include <contrib/dev/acpica/include/acinterp.h>
50193341Sjkim#include <contrib/dev/acpica/include/amlcode.h>
51193341Sjkim#include <contrib/dev/acpica/include/acevents.h>
52193341Sjkim#include <contrib/dev/acpica/include/acdispat.h>
5367754Smsmith
5467754Smsmith
5577424Smsmith#define _COMPONENT          ACPI_EXECUTER
5691116Smsmith        ACPI_MODULE_NAME    ("exfldio")
5767754Smsmith
58151937Sjkim/* Local prototypes */
5967754Smsmith
60151937Sjkimstatic ACPI_STATUS
61151937SjkimAcpiExFieldDatumIo (
62151937Sjkim    ACPI_OPERAND_OBJECT     *ObjDesc,
63151937Sjkim    UINT32                  FieldDatumByteOffset,
64202771Sjkim    UINT64                  *Value,
65151937Sjkim    UINT32                  ReadWrite);
66151937Sjkim
67151937Sjkimstatic BOOLEAN
68151937SjkimAcpiExRegisterOverflow (
69151937Sjkim    ACPI_OPERAND_OBJECT     *ObjDesc,
70202771Sjkim    UINT64                  Value);
71151937Sjkim
72151937Sjkimstatic ACPI_STATUS
73151937SjkimAcpiExSetupRegion (
74151937Sjkim    ACPI_OPERAND_OBJECT     *ObjDesc,
75151937Sjkim    UINT32                  FieldDatumByteOffset);
76151937Sjkim
77151937Sjkim
7867754Smsmith/*******************************************************************************
7967754Smsmith *
8087031Smsmith * FUNCTION:    AcpiExSetupRegion
8167754Smsmith *
82151937Sjkim * PARAMETERS:  ObjDesc                 - Field to be read or written
8387031Smsmith *              FieldDatumByteOffset    - Byte offset of this datum within the
8487031Smsmith *                                        parent field
8567754Smsmith *
8667754Smsmith * RETURN:      Status
8767754Smsmith *
8877424Smsmith * DESCRIPTION: Common processing for AcpiExExtractFromField and
89241973Sjkim *              AcpiExInsertIntoField. Initialize the Region if necessary and
90123315Snjl *              validate the request.
9167754Smsmith *
9267754Smsmith ******************************************************************************/
9367754Smsmith
94151937Sjkimstatic ACPI_STATUS
9587031SmsmithAcpiExSetupRegion (
9667754Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
9777424Smsmith    UINT32                  FieldDatumByteOffset)
9867754Smsmith{
9977424Smsmith    ACPI_STATUS             Status = AE_OK;
10077424Smsmith    ACPI_OPERAND_OBJECT     *RgnDesc;
101228110Sjkim    UINT8                   SpaceId;
10267754Smsmith
10367754Smsmith
104167802Sjkim    ACPI_FUNCTION_TRACE_U32 (ExSetupRegion, FieldDatumByteOffset);
10567754Smsmith
10683174Smsmith
10777424Smsmith    RgnDesc = ObjDesc->CommonField.RegionObj;
10867754Smsmith
109107325Siwasaki    /* We must have a valid region */
110107325Siwasaki
111193267Sjkim    if (RgnDesc->Common.Type != ACPI_TYPE_REGION)
11277424Smsmith    {
113204773Sjkim        ACPI_ERROR ((AE_INFO, "Needed Region, found type 0x%X (%s)",
114193267Sjkim            RgnDesc->Common.Type,
11599679Siwasaki            AcpiUtGetObjectTypeName (RgnDesc)));
11699679Siwasaki
11777424Smsmith        return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
11877424Smsmith    }
11967754Smsmith
120228110Sjkim    SpaceId = RgnDesc->Region.SpaceId;
121228110Sjkim
122228110Sjkim    /* Validate the Space ID */
123228110Sjkim
124228110Sjkim    if (!AcpiIsValidSpaceId (SpaceId))
125228110Sjkim    {
126228110Sjkim        ACPI_ERROR ((AE_INFO, "Invalid/unknown Address Space ID: 0x%2.2X", SpaceId));
127228110Sjkim        return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID);
128228110Sjkim    }
129228110Sjkim
13077424Smsmith    /*
13177424Smsmith     * If the Region Address and Length have not been previously evaluated,
13277424Smsmith     * evaluate them now and save the results.
13377424Smsmith     */
134123315Snjl    if (!(RgnDesc->Common.Flags & AOPOBJ_DATA_VALID))
13567754Smsmith    {
13677424Smsmith        Status = AcpiDsGetRegionArguments (RgnDesc);
13777424Smsmith        if (ACPI_FAILURE (Status))
13877424Smsmith        {
13977424Smsmith            return_ACPI_STATUS (Status);
14077424Smsmith        }
14167754Smsmith    }
14267754Smsmith
143167802Sjkim    /*
144228110Sjkim     * Exit now for SMBus, GSBus or IPMI address space, it has a non-linear
145210976Sjkim     * address space and the request cannot be directly validated
146167802Sjkim     */
147228110Sjkim    if (SpaceId == ACPI_ADR_SPACE_SMBUS ||
148228110Sjkim        SpaceId == ACPI_ADR_SPACE_GSBUS ||
149228110Sjkim        SpaceId == ACPI_ADR_SPACE_IPMI)
150107325Siwasaki    {
151197104Sjkim        /* SMBus or IPMI has a non-linear address space */
152107325Siwasaki
153107325Siwasaki        return_ACPI_STATUS (AE_OK);
154107325Siwasaki    }
155107325Siwasaki
156123315Snjl#ifdef ACPI_UNDER_DEVELOPMENT
15777424Smsmith    /*
158123315Snjl     * If the Field access is AnyAcc, we can now compute the optimal
159123315Snjl     * access (because we know know the length of the parent region)
160123315Snjl     */
161123315Snjl    if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
162123315Snjl    {
163123315Snjl        if (ACPI_FAILURE (Status))
164123315Snjl        {
165123315Snjl            return_ACPI_STATUS (Status);
166123315Snjl        }
167123315Snjl    }
168123315Snjl#endif
169123315Snjl
170123315Snjl    /*
171241973Sjkim     * Validate the request. The entire request from the byte offset for a
17277424Smsmith     * length of one field datum (access width) must fit within the region.
17377424Smsmith     * (Region length is specified in bytes)
17477424Smsmith     */
175167802Sjkim    if (RgnDesc->Region.Length <
176210976Sjkim            (ObjDesc->CommonField.BaseByteOffset + FieldDatumByteOffset +
177167802Sjkim            ObjDesc->CommonField.AccessByteWidth))
17877424Smsmith    {
179138287Smarks        if (AcpiGbl_EnableInterpreterSlack)
180138287Smarks        {
181138287Smarks            /*
182138287Smarks             * Slack mode only:  We will go ahead and allow access to this
183138287Smarks             * field if it is within the region length rounded up to the next
184193267Sjkim             * access width boundary. ACPI_SIZE cast for 64-bit compile.
185138287Smarks             */
186138287Smarks            if (ACPI_ROUND_UP (RgnDesc->Region.Length,
187167802Sjkim                    ObjDesc->CommonField.AccessByteWidth) >=
188193267Sjkim                ((ACPI_SIZE) ObjDesc->CommonField.BaseByteOffset +
189193267Sjkim                    ObjDesc->CommonField.AccessByteWidth +
190193267Sjkim                    FieldDatumByteOffset))
191138287Smarks            {
192138287Smarks                return_ACPI_STATUS (AE_OK);
193138287Smarks            }
194138287Smarks        }
195138287Smarks
19677424Smsmith        if (RgnDesc->Region.Length < ObjDesc->CommonField.AccessByteWidth)
19777424Smsmith        {
19883174Smsmith            /*
19977424Smsmith             * This is the case where the AccessType (AccWord, etc.) is wider
200241973Sjkim             * than the region itself. For example, a region of length one
20177424Smsmith             * byte, and a field with Dword access specified.
20277424Smsmith             */
203167802Sjkim            ACPI_ERROR ((AE_INFO,
204204773Sjkim                "Field [%4.4s] access width (%u bytes) too large for region [%4.4s] (length %u)",
205123315Snjl                AcpiUtGetNodeName (ObjDesc->CommonField.Node),
206123315Snjl                ObjDesc->CommonField.AccessByteWidth,
207151937Sjkim                AcpiUtGetNodeName (RgnDesc->Region.Node),
208151937Sjkim                RgnDesc->Region.Length));
20977424Smsmith        }
21077424Smsmith
21177424Smsmith        /*
21277424Smsmith         * Offset rounded up to next multiple of field width
21377424Smsmith         * exceeds region length, indicate an error
21477424Smsmith         */
215167802Sjkim        ACPI_ERROR ((AE_INFO,
216204773Sjkim            "Field [%4.4s] Base+Offset+Width %u+%u+%u is beyond end of region [%4.4s] (length %u)",
217123315Snjl            AcpiUtGetNodeName (ObjDesc->CommonField.Node),
218123315Snjl            ObjDesc->CommonField.BaseByteOffset,
21991116Smsmith            FieldDatumByteOffset, ObjDesc->CommonField.AccessByteWidth,
220151937Sjkim            AcpiUtGetNodeName (RgnDesc->Region.Node),
221151937Sjkim            RgnDesc->Region.Length));
22277424Smsmith
22377424Smsmith        return_ACPI_STATUS (AE_AML_REGION_LIMIT);
22477424Smsmith    }
22577424Smsmith
22677424Smsmith    return_ACPI_STATUS (AE_OK);
22777424Smsmith}
22877424Smsmith
22977424Smsmith
23077424Smsmith/*******************************************************************************
23177424Smsmith *
23287031Smsmith * FUNCTION:    AcpiExAccessRegion
23377424Smsmith *
234151937Sjkim * PARAMETERS:  ObjDesc                 - Field to be read
23587031Smsmith *              FieldDatumByteOffset    - Byte offset of this datum within the
23687031Smsmith *                                        parent field
237151937Sjkim *              Value                   - Where to store value (must at least
238202771Sjkim *                                        64 bits)
239107325Siwasaki *              Function                - Read or Write flag plus other region-
240107325Siwasaki *                                        dependent flags
24177424Smsmith *
24277424Smsmith * RETURN:      Status
24377424Smsmith *
24487031Smsmith * DESCRIPTION: Read or Write a single field datum to an Operation Region.
24577424Smsmith *
24677424Smsmith ******************************************************************************/
24777424Smsmith
24877424SmsmithACPI_STATUS
24987031SmsmithAcpiExAccessRegion (
25077424Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
25177424Smsmith    UINT32                  FieldDatumByteOffset,
252202771Sjkim    UINT64                  *Value,
253107325Siwasaki    UINT32                  Function)
25477424Smsmith{
25577424Smsmith    ACPI_STATUS             Status;
25677424Smsmith    ACPI_OPERAND_OBJECT     *RgnDesc;
257193267Sjkim    UINT32                  RegionOffset;
25877424Smsmith
25977424Smsmith
260167802Sjkim    ACPI_FUNCTION_TRACE (ExAccessRegion);
26177424Smsmith
26277424Smsmith
263114237Snjl    /*
264107325Siwasaki     * Ensure that the region operands are fully evaluated and verify
265107325Siwasaki     * the validity of the request
266107325Siwasaki     */
267107325Siwasaki    Status = AcpiExSetupRegion (ObjDesc, FieldDatumByteOffset);
268107325Siwasaki    if (ACPI_FAILURE (Status))
269107325Siwasaki    {
270107325Siwasaki        return_ACPI_STATUS (Status);
271107325Siwasaki    }
272107325Siwasaki
27387031Smsmith    /*
27487031Smsmith     * The physical address of this field datum is:
27587031Smsmith     *
27687031Smsmith     * 1) The base of the region, plus
27787031Smsmith     * 2) The base offset of the field, plus
27887031Smsmith     * 3) The current offset into the field
27987031Smsmith     */
28087031Smsmith    RgnDesc = ObjDesc->CommonField.RegionObj;
281193267Sjkim    RegionOffset =
282193267Sjkim        ObjDesc->CommonField.BaseByteOffset +
283193267Sjkim        FieldDatumByteOffset;
28487031Smsmith
285107325Siwasaki    if ((Function & ACPI_IO_MASK) == ACPI_READ)
28667754Smsmith    {
28787031Smsmith        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[READ]"));
28867754Smsmith    }
28987031Smsmith    else
29087031Smsmith    {
29187031Smsmith        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[WRITE]"));
29287031Smsmith    }
29367754Smsmith
29487031Smsmith    ACPI_DEBUG_PRINT_RAW ((ACPI_DB_BFIELD,
295167802Sjkim        " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %p\n",
29687031Smsmith        AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
29787031Smsmith        RgnDesc->Region.SpaceId,
29891116Smsmith        ObjDesc->CommonField.AccessByteWidth,
29987031Smsmith        ObjDesc->CommonField.BaseByteOffset,
30087031Smsmith        FieldDatumByteOffset,
301193267Sjkim        ACPI_CAST_PTR (void, (RgnDesc->Region.Address + RegionOffset))));
30267754Smsmith
30387031Smsmith    /* Invoke the appropriate AddressSpace/OpRegion handler */
30477424Smsmith
305228110Sjkim    Status = AcpiEvAddressSpaceDispatch (RgnDesc, ObjDesc,
306228110Sjkim                Function, RegionOffset,
307151937Sjkim                ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth), Value);
30887031Smsmith
30987031Smsmith    if (ACPI_FAILURE (Status))
31087031Smsmith    {
31187031Smsmith        if (Status == AE_NOT_IMPLEMENTED)
31287031Smsmith        {
313167802Sjkim            ACPI_ERROR ((AE_INFO,
314218590Sjkim                "Region %s (ID=%u) not implemented",
31587031Smsmith                AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
31687031Smsmith                RgnDesc->Region.SpaceId));
31787031Smsmith        }
31887031Smsmith        else if (Status == AE_NOT_EXIST)
31987031Smsmith        {
320167802Sjkim            ACPI_ERROR ((AE_INFO,
321218590Sjkim                "Region %s (ID=%u) has no handler",
32287031Smsmith                AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
32387031Smsmith                RgnDesc->Region.SpaceId));
32487031Smsmith        }
32587031Smsmith    }
32687031Smsmith
32787031Smsmith    return_ACPI_STATUS (Status);
32887031Smsmith}
32987031Smsmith
33087031Smsmith
33187031Smsmith/*******************************************************************************
33287031Smsmith *
33387031Smsmith * FUNCTION:    AcpiExRegisterOverflow
33487031Smsmith *
335151937Sjkim * PARAMETERS:  ObjDesc                 - Register(Field) to be written
33687031Smsmith *              Value                   - Value to be stored
33787031Smsmith *
33887031Smsmith * RETURN:      TRUE if value overflows the field, FALSE otherwise
33987031Smsmith *
34087031Smsmith * DESCRIPTION: Check if a value is out of range of the field being written.
34187031Smsmith *              Used to check if the values written to Index and Bank registers
342241973Sjkim *              are out of range. Normally, the value is simply truncated
34387031Smsmith *              to fit the field, but this case is most likely a serious
34487031Smsmith *              coding error in the ASL.
34587031Smsmith *
34687031Smsmith ******************************************************************************/
34787031Smsmith
348151937Sjkimstatic BOOLEAN
34987031SmsmithAcpiExRegisterOverflow (
35087031Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
351202771Sjkim    UINT64                  Value)
35287031Smsmith{
35387031Smsmith
35491116Smsmith    if (ObjDesc->CommonField.BitLength >= ACPI_INTEGER_BIT_SIZE)
35591116Smsmith    {
35691116Smsmith        /*
35791116Smsmith         * The field is large enough to hold the maximum integer, so we can
35891116Smsmith         * never overflow it.
35991116Smsmith         */
36091116Smsmith        return (FALSE);
36191116Smsmith    }
36291116Smsmith
363202771Sjkim    if (Value >= ((UINT64) 1 << ObjDesc->CommonField.BitLength))
36487031Smsmith    {
36591116Smsmith        /*
36691116Smsmith         * The Value is larger than the maximum value that can fit into
36791116Smsmith         * the register.
36891116Smsmith         */
369228110Sjkim        ACPI_ERROR ((AE_INFO,
370228110Sjkim            "Index value 0x%8.8X%8.8X overflows field width 0x%X",
371228110Sjkim            ACPI_FORMAT_UINT64 (Value),
372228110Sjkim            ObjDesc->CommonField.BitLength));
373228110Sjkim
37487031Smsmith        return (TRUE);
37587031Smsmith    }
37687031Smsmith
37791116Smsmith    /* The Value will fit into the field with no truncation */
37891116Smsmith
37987031Smsmith    return (FALSE);
38087031Smsmith}
38187031Smsmith
38287031Smsmith
38387031Smsmith/*******************************************************************************
38487031Smsmith *
38587031Smsmith * FUNCTION:    AcpiExFieldDatumIo
38687031Smsmith *
387151937Sjkim * PARAMETERS:  ObjDesc                 - Field to be read
38887031Smsmith *              FieldDatumByteOffset    - Byte offset of this datum within the
38987031Smsmith *                                        parent field
390151937Sjkim *              Value                   - Where to store value (must be 64 bits)
39187031Smsmith *              ReadWrite               - Read or Write flag
39287031Smsmith *
39387031Smsmith * RETURN:      Status
39487031Smsmith *
395241973Sjkim * DESCRIPTION: Read or Write a single datum of a field. The FieldType is
39687031Smsmith *              demultiplexed here to handle the different types of fields
39787031Smsmith *              (BufferField, RegionField, IndexField, BankField)
39887031Smsmith *
39987031Smsmith ******************************************************************************/
40087031Smsmith
401151937Sjkimstatic ACPI_STATUS
40287031SmsmithAcpiExFieldDatumIo (
40387031Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
40487031Smsmith    UINT32                  FieldDatumByteOffset,
405202771Sjkim    UINT64                  *Value,
40687031Smsmith    UINT32                  ReadWrite)
40787031Smsmith{
40887031Smsmith    ACPI_STATUS             Status;
409202771Sjkim    UINT64                  LocalValue;
41087031Smsmith
41187031Smsmith
412167802Sjkim    ACPI_FUNCTION_TRACE_U32 (ExFieldDatumIo, FieldDatumByteOffset);
41387031Smsmith
41487031Smsmith
41587031Smsmith    if (ReadWrite == ACPI_READ)
41687031Smsmith    {
41787031Smsmith        if (!Value)
41887031Smsmith        {
41987031Smsmith            LocalValue = 0;
420151937Sjkim
421151937Sjkim            /* To support reads without saving return value */
422151937Sjkim            Value = &LocalValue;
42387031Smsmith        }
42487031Smsmith
42587031Smsmith        /* Clear the entire return buffer first, [Very Important!] */
42687031Smsmith
42787031Smsmith        *Value = 0;
42887031Smsmith    }
42987031Smsmith
43087031Smsmith    /*
43187031Smsmith     * The four types of fields are:
43287031Smsmith     *
433122944Snjl     * BufferField - Read/write from/to a Buffer
434122944Snjl     * RegionField - Read/write from/to a Operation Region.
435151937Sjkim     * BankField   - Write to a Bank Register, then read/write from/to an
436151937Sjkim     *               OperationRegion
437151937Sjkim     * IndexField  - Write to an Index Register, then read/write from/to a
438151937Sjkim     *               Data Register
43987031Smsmith     */
440193267Sjkim    switch (ObjDesc->Common.Type)
44177424Smsmith    {
44277424Smsmith    case ACPI_TYPE_BUFFER_FIELD:
44377424Smsmith        /*
44487031Smsmith         * If the BufferField arguments have not been previously evaluated,
44587031Smsmith         * evaluate them now and save the results.
44677424Smsmith         */
44787031Smsmith        if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
44887031Smsmith        {
44987031Smsmith            Status = AcpiDsGetBufferFieldArguments (ObjDesc);
45087031Smsmith            if (ACPI_FAILURE (Status))
45187031Smsmith            {
45287031Smsmith                return_ACPI_STATUS (Status);
45387031Smsmith            }
45487031Smsmith        }
45587031Smsmith
45687031Smsmith        if (ReadWrite == ACPI_READ)
45787031Smsmith        {
45887031Smsmith            /*
45987031Smsmith             * Copy the data from the source buffer.
46087031Smsmith             * Length is the field width in bytes.
46187031Smsmith             */
462151937Sjkim            ACPI_MEMCPY (Value,
463151937Sjkim                (ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
464151937Sjkim                    ObjDesc->BufferField.BaseByteOffset +
465151937Sjkim                    FieldDatumByteOffset,
466151937Sjkim                ObjDesc->CommonField.AccessByteWidth);
46787031Smsmith        }
46887031Smsmith        else
46987031Smsmith        {
47087031Smsmith            /*
47187031Smsmith             * Copy the data to the target buffer.
47287031Smsmith             * Length is the field width in bytes.
47387031Smsmith             */
474151937Sjkim            ACPI_MEMCPY ((ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
475167802Sjkim                ObjDesc->BufferField.BaseByteOffset +
476167802Sjkim                FieldDatumByteOffset,
477167802Sjkim                Value, ObjDesc->CommonField.AccessByteWidth);
47887031Smsmith        }
47987031Smsmith
48077424Smsmith        Status = AE_OK;
48177424Smsmith        break;
48267754Smsmith
483107325Siwasaki    case ACPI_TYPE_LOCAL_BANK_FIELD:
484151937Sjkim        /*
485151937Sjkim         * Ensure that the BankValue is not beyond the capacity of
486151937Sjkim         * the register
487151937Sjkim         */
48887031Smsmith        if (AcpiExRegisterOverflow (ObjDesc->BankField.BankObj,
489202771Sjkim                (UINT64) ObjDesc->BankField.Value))
49087031Smsmith        {
49187031Smsmith            return_ACPI_STATUS (AE_AML_REGISTER_LIMIT);
49287031Smsmith        }
49387031Smsmith
49477424Smsmith        /*
49587031Smsmith         * For BankFields, we must write the BankValue to the BankRegister
49687031Smsmith         * (itself a RegionField) before we can access the data.
49777424Smsmith         */
49887031Smsmith        Status = AcpiExInsertIntoField (ObjDesc->BankField.BankObj,
499167802Sjkim                    &ObjDesc->BankField.Value,
500167802Sjkim                    sizeof (ObjDesc->BankField.Value));
50177424Smsmith        if (ACPI_FAILURE (Status))
50277424Smsmith        {
50377424Smsmith            return_ACPI_STATUS (Status);
50477424Smsmith        }
50567754Smsmith
50677424Smsmith        /*
50787031Smsmith         * Now that the Bank has been selected, fall through to the
50887031Smsmith         * RegionField case and write the datum to the Operation Region
50977424Smsmith         */
51077424Smsmith
51199679Siwasaki        /*lint -fallthrough */
51277424Smsmith
513107325Siwasaki    case ACPI_TYPE_LOCAL_REGION_FIELD:
51487031Smsmith        /*
51587031Smsmith         * For simple RegionFields, we just directly access the owning
51687031Smsmith         * Operation Region.
51787031Smsmith         */
51887031Smsmith        Status = AcpiExAccessRegion (ObjDesc, FieldDatumByteOffset, Value,
519167802Sjkim                    ReadWrite);
52087031Smsmith        break;
52187031Smsmith
522107325Siwasaki    case ACPI_TYPE_LOCAL_INDEX_FIELD:
523151937Sjkim        /*
524151937Sjkim         * Ensure that the IndexValue is not beyond the capacity of
525151937Sjkim         * the register
526151937Sjkim         */
52787031Smsmith        if (AcpiExRegisterOverflow (ObjDesc->IndexField.IndexObj,
528202771Sjkim                (UINT64) ObjDesc->IndexField.Value))
52977424Smsmith        {
53087031Smsmith            return_ACPI_STATUS (AE_AML_REGISTER_LIMIT);
53177424Smsmith        }
53287031Smsmith
53387031Smsmith        /* Write the index value to the IndexRegister (itself a RegionField) */
53487031Smsmith
535122944Snjl        FieldDatumByteOffset += ObjDesc->IndexField.Value;
536122944Snjl
537122944Snjl        ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
538167802Sjkim            "Write to Index Register: Value %8.8X\n",
539167802Sjkim            FieldDatumByteOffset));
540122944Snjl
54187031Smsmith        Status = AcpiExInsertIntoField (ObjDesc->IndexField.IndexObj,
542167802Sjkim                    &FieldDatumByteOffset,
543167802Sjkim                    sizeof (FieldDatumByteOffset));
54487031Smsmith        if (ACPI_FAILURE (Status))
54587031Smsmith        {
54687031Smsmith            return_ACPI_STATUS (Status);
54787031Smsmith        }
54887031Smsmith
54987031Smsmith        if (ReadWrite == ACPI_READ)
55087031Smsmith        {
55187031Smsmith            /* Read the datum from the DataRegister */
55287031Smsmith
553193267Sjkim            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
554193267Sjkim                "Read from Data Register\n"));
555193267Sjkim
55687031Smsmith            Status = AcpiExExtractFromField (ObjDesc->IndexField.DataObj,
557202771Sjkim                        Value, sizeof (UINT64));
55887031Smsmith        }
55987031Smsmith        else
56087031Smsmith        {
561122944Snjl            /* Write the datum to the DataRegister */
56287031Smsmith
563193267Sjkim            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
564193267Sjkim                "Write to Data Register: Value %8.8X%8.8X\n",
565193267Sjkim                ACPI_FORMAT_UINT64 (*Value)));
566193267Sjkim
56787031Smsmith            Status = AcpiExInsertIntoField (ObjDesc->IndexField.DataObj,
568202771Sjkim                        Value, sizeof (UINT64));
56987031Smsmith        }
57077424Smsmith        break;
57177424Smsmith
57277424Smsmith    default:
57377424Smsmith
574204773Sjkim        ACPI_ERROR ((AE_INFO, "Wrong object type in field I/O %u",
575193267Sjkim            ObjDesc->Common.Type));
57677424Smsmith        Status = AE_AML_INTERNAL;
57777424Smsmith        break;
57867754Smsmith    }
57967754Smsmith
58087031Smsmith    if (ACPI_SUCCESS (Status))
58187031Smsmith    {
58287031Smsmith        if (ReadWrite == ACPI_READ)
58387031Smsmith        {
584151937Sjkim            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
585209746Sjkim                "Value Read %8.8X%8.8X, Width %u\n",
586151937Sjkim                ACPI_FORMAT_UINT64 (*Value),
587151937Sjkim                ObjDesc->CommonField.AccessByteWidth));
58887031Smsmith        }
58987031Smsmith        else
59087031Smsmith        {
591151937Sjkim            ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
592209746Sjkim                "Value Written %8.8X%8.8X, Width %u\n",
593151937Sjkim                ACPI_FORMAT_UINT64 (*Value),
594151937Sjkim                ObjDesc->CommonField.AccessByteWidth));
59587031Smsmith        }
59687031Smsmith    }
59777424Smsmith
59887031Smsmith    return_ACPI_STATUS (Status);
59987031Smsmith}
60077424Smsmith
60187031Smsmith
60287031Smsmith/*******************************************************************************
60387031Smsmith *
60487031Smsmith * FUNCTION:    AcpiExWriteWithUpdateRule
60587031Smsmith *
606151937Sjkim * PARAMETERS:  ObjDesc                 - Field to be written
607151937Sjkim *              Mask                    - bitmask within field datum
608151937Sjkim *              FieldValue              - Value to write
609151937Sjkim *              FieldDatumByteOffset    - Offset of datum within field
61087031Smsmith *
61187031Smsmith * RETURN:      Status
61287031Smsmith *
61387031Smsmith * DESCRIPTION: Apply the field update rule to a field write
61487031Smsmith *
61587031Smsmith ******************************************************************************/
61687031Smsmith
61787031SmsmithACPI_STATUS
61887031SmsmithAcpiExWriteWithUpdateRule (
61987031Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
620202771Sjkim    UINT64                  Mask,
621202771Sjkim    UINT64                  FieldValue,
62287031Smsmith    UINT32                  FieldDatumByteOffset)
62387031Smsmith{
62487031Smsmith    ACPI_STATUS             Status = AE_OK;
625202771Sjkim    UINT64                  MergedValue;
626202771Sjkim    UINT64                  CurrentValue;
62787031Smsmith
62887031Smsmith
629167802Sjkim    ACPI_FUNCTION_TRACE_U32 (ExWriteWithUpdateRule, Mask);
63087031Smsmith
63187031Smsmith
63287031Smsmith    /* Start with the new bits  */
63387031Smsmith
63487031Smsmith    MergedValue = FieldValue;
63587031Smsmith
63687031Smsmith    /* If the mask is all ones, we don't need to worry about the update rule */
63787031Smsmith
638202771Sjkim    if (Mask != ACPI_UINT64_MAX)
63987031Smsmith    {
64087031Smsmith        /* Decode the update rule */
64187031Smsmith
64287031Smsmith        switch (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)
64387031Smsmith        {
64487031Smsmith        case AML_FIELD_UPDATE_PRESERVE:
64587031Smsmith            /*
64687031Smsmith             * Check if update rule needs to be applied (not if mask is all
64787031Smsmith             * ones)  The left shift drops the bits we want to ignore.
64887031Smsmith             */
64991116Smsmith            if ((~Mask << (ACPI_MUL_8 (sizeof (Mask)) -
65091116Smsmith                           ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth))) != 0)
65187031Smsmith            {
65287031Smsmith                /*
65387031Smsmith                 * Read the current contents of the byte/word/dword containing
65487031Smsmith                 * the field, and merge with the new field value.
65587031Smsmith                 */
65687031Smsmith                Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
657167802Sjkim                            &CurrentValue, ACPI_READ);
658123315Snjl                if (ACPI_FAILURE (Status))
659123315Snjl                {
660123315Snjl                    return_ACPI_STATUS (Status);
661123315Snjl                }
662123315Snjl
66387031Smsmith                MergedValue |= (CurrentValue & ~Mask);
66487031Smsmith            }
66587031Smsmith            break;
66687031Smsmith
66787031Smsmith        case AML_FIELD_UPDATE_WRITE_AS_ONES:
66887031Smsmith
66987031Smsmith            /* Set positions outside the field to all ones */
67087031Smsmith
67187031Smsmith            MergedValue |= ~Mask;
67287031Smsmith            break;
67387031Smsmith
67487031Smsmith        case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
67587031Smsmith
67687031Smsmith            /* Set positions outside the field to all zeros */
67787031Smsmith
67887031Smsmith            MergedValue &= Mask;
67987031Smsmith            break;
68087031Smsmith
68187031Smsmith        default:
682123315Snjl
683167802Sjkim            ACPI_ERROR ((AE_INFO,
684204773Sjkim                "Unknown UpdateRule value: 0x%X",
68587031Smsmith                (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)));
68687031Smsmith            return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
68787031Smsmith        }
68887031Smsmith    }
68987031Smsmith
690123315Snjl    ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
691123315Snjl        "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
692123315Snjl        ACPI_FORMAT_UINT64 (Mask),
693123315Snjl        FieldDatumByteOffset,
694123315Snjl        ObjDesc->CommonField.AccessByteWidth,
695123315Snjl        ACPI_FORMAT_UINT64 (FieldValue),
696123315Snjl        ACPI_FORMAT_UINT64 (MergedValue)));
697123315Snjl
69887031Smsmith    /* Write the merged value */
69987031Smsmith
70087031Smsmith    Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
701167802Sjkim                &MergedValue, ACPI_WRITE);
70287031Smsmith
70377424Smsmith    return_ACPI_STATUS (Status);
70477424Smsmith}
70577424Smsmith
70677424Smsmith
70777424Smsmith/*******************************************************************************
70877424Smsmith *
70977424Smsmith * FUNCTION:    AcpiExExtractFromField
71067754Smsmith *
711131440Smarks * PARAMETERS:  ObjDesc             - Field to be read
712131440Smarks *              Buffer              - Where to store the field data
713131440Smarks *              BufferLength        - Length of Buffer
71467754Smsmith *
71567754Smsmith * RETURN:      Status
71667754Smsmith *
717131440Smarks * DESCRIPTION: Retrieve the current value of the given field
71867754Smsmith *
71967754Smsmith ******************************************************************************/
72067754Smsmith
72167754SmsmithACPI_STATUS
72277424SmsmithAcpiExExtractFromField (
72367754Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
72467754Smsmith    void                    *Buffer,
72577424Smsmith    UINT32                  BufferLength)
72667754Smsmith{
72767754Smsmith    ACPI_STATUS             Status;
728202771Sjkim    UINT64                  RawDatum;
729202771Sjkim    UINT64                  MergedDatum;
730151937Sjkim    UINT32                  FieldOffset = 0;
731151937Sjkim    UINT32                  BufferOffset = 0;
732151937Sjkim    UINT32                  BufferTailBits;
73377424Smsmith    UINT32                  DatumCount;
734151937Sjkim    UINT32                  FieldDatumCount;
735210976Sjkim    UINT32                  AccessBitWidth;
736123315Snjl    UINT32                  i;
73767754Smsmith
73867754Smsmith
739167802Sjkim    ACPI_FUNCTION_TRACE (ExExtractFromField);
74067754Smsmith
74177424Smsmith
742151937Sjkim    /* Validate target buffer and clear it */
74377424Smsmith
744167802Sjkim    if (BufferLength <
745210976Sjkim        ACPI_ROUND_BITS_UP_TO_BYTES (ObjDesc->CommonField.BitLength))
746151937Sjkim    {
747167802Sjkim        ACPI_ERROR ((AE_INFO,
748204773Sjkim            "Field size %u (bits) is too large for buffer (%u)",
749151937Sjkim            ObjDesc->CommonField.BitLength, BufferLength));
750151937Sjkim
751151937Sjkim        return_ACPI_STATUS (AE_BUFFER_OVERFLOW);
752151937Sjkim    }
753210976Sjkim
754151937Sjkim    ACPI_MEMSET (Buffer, 0, BufferLength);
755210976Sjkim    AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
756151937Sjkim
757210976Sjkim    /* Handle the simple case here */
758210976Sjkim
759210976Sjkim    if ((ObjDesc->CommonField.StartFieldBitOffset == 0) &&
760210976Sjkim        (ObjDesc->CommonField.BitLength == AccessBitWidth))
761210976Sjkim    {
762249663Sjkim        if (BufferLength >= sizeof (UINT64))
763249663Sjkim        {
764249663Sjkim            Status = AcpiExFieldDatumIo (ObjDesc, 0, Buffer, ACPI_READ);
765249663Sjkim        }
766249663Sjkim        else
767249663Sjkim        {
768249663Sjkim            /* Use RawDatum (UINT64) to handle buffers < 64 bits */
769249663Sjkim
770249663Sjkim            Status = AcpiExFieldDatumIo (ObjDesc, 0, &RawDatum, ACPI_READ);
771249663Sjkim            ACPI_MEMCPY (Buffer, &RawDatum, BufferLength);
772249663Sjkim        }
773249663Sjkim
774210976Sjkim        return_ACPI_STATUS (Status);
775210976Sjkim    }
776210976Sjkim
777210976Sjkim/* TBD: Move to common setup code */
778210976Sjkim
779210976Sjkim    /* Field algorithm is limited to sizeof(UINT64), truncate if needed */
780210976Sjkim
781210976Sjkim    if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
782210976Sjkim    {
783210976Sjkim        ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
784210976Sjkim        AccessBitWidth = sizeof (UINT64) * 8;
785210976Sjkim    }
786210976Sjkim
787151937Sjkim    /* Compute the number of datums (access width data items) */
788151937Sjkim
789151937Sjkim    DatumCount = ACPI_ROUND_UP_TO (
790210976Sjkim        ObjDesc->CommonField.BitLength, AccessBitWidth);
791210976Sjkim
792151937Sjkim    FieldDatumCount = ACPI_ROUND_UP_TO (
793210976Sjkim        ObjDesc->CommonField.BitLength +
794210976Sjkim        ObjDesc->CommonField.StartFieldBitOffset, AccessBitWidth);
795151937Sjkim
796151937Sjkim    /* Priming read from the field */
797151937Sjkim
798151937Sjkim    Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset, &RawDatum, ACPI_READ);
799131440Smarks    if (ACPI_FAILURE (Status))
800123315Snjl    {
801131440Smarks        return_ACPI_STATUS (Status);
802123315Snjl    }
803151937Sjkim    MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
804123315Snjl
805151937Sjkim    /* Read the rest of the field */
80667754Smsmith
807151937Sjkim    for (i = 1; i < FieldDatumCount; i++)
808151937Sjkim    {
809151937Sjkim        /* Get next input datum from the field */
81067754Smsmith
811151937Sjkim        FieldOffset += ObjDesc->CommonField.AccessByteWidth;
812151937Sjkim        Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset,
813167802Sjkim                    &RawDatum, ACPI_READ);
814123315Snjl        if (ACPI_FAILURE (Status))
815123315Snjl        {
816123315Snjl            return_ACPI_STATUS (Status);
817123315Snjl        }
81867754Smsmith
819167802Sjkim        /*
820167802Sjkim         * Merge with previous datum if necessary.
821167802Sjkim         *
822167802Sjkim         * Note: Before the shift, check if the shift value will be larger than
823167802Sjkim         * the integer size. If so, there is no need to perform the operation.
824167802Sjkim         * This avoids the differences in behavior between different compilers
825167802Sjkim         * concerning shift values larger than the target data width.
826167802Sjkim         */
827210976Sjkim        if (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset <
828210976Sjkim            ACPI_INTEGER_BIT_SIZE)
829167802Sjkim        {
830167802Sjkim            MergedDatum |= RawDatum <<
831210976Sjkim                (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
832167802Sjkim        }
83367754Smsmith
834151937Sjkim        if (i == DatumCount)
83567754Smsmith        {
836151937Sjkim            break;
83777424Smsmith        }
83867754Smsmith
839151937Sjkim        /* Write merged datum to target buffer */
84071867Smsmith
841151937Sjkim        ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
842151937Sjkim            ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
843167802Sjkim                BufferLength - BufferOffset));
84467754Smsmith
845151937Sjkim        BufferOffset += ObjDesc->CommonField.AccessByteWidth;
846151937Sjkim        MergedDatum = RawDatum >> ObjDesc->CommonField.StartFieldBitOffset;
847151937Sjkim    }
848123315Snjl
849151937Sjkim    /* Mask off any extra bits in the last datum */
850123315Snjl
851210976Sjkim    BufferTailBits = ObjDesc->CommonField.BitLength % AccessBitWidth;
852151937Sjkim    if (BufferTailBits)
853151937Sjkim    {
854151937Sjkim        MergedDatum &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
855123315Snjl    }
856123315Snjl
857151937Sjkim    /* Write the last datum to the buffer */
858123315Snjl
859151937Sjkim    ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
860151937Sjkim        ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
861167802Sjkim            BufferLength - BufferOffset));
862123315Snjl
86377424Smsmith    return_ACPI_STATUS (AE_OK);
86467754Smsmith}
86567754Smsmith
86667754Smsmith
86767754Smsmith/*******************************************************************************
86867754Smsmith *
86977424Smsmith * FUNCTION:    AcpiExInsertIntoField
87067754Smsmith *
871131440Smarks * PARAMETERS:  ObjDesc             - Field to be written
872131440Smarks *              Buffer              - Data to be written
873131440Smarks *              BufferLength        - Length of Buffer
87467754Smsmith *
87567754Smsmith * RETURN:      Status
87667754Smsmith *
877131440Smarks * DESCRIPTION: Store the Buffer contents into the given field
87867754Smsmith *
87977424Smsmith ******************************************************************************/
88067754Smsmith
88167754SmsmithACPI_STATUS
88277424SmsmithAcpiExInsertIntoField (
88367754Smsmith    ACPI_OPERAND_OBJECT     *ObjDesc,
88467754Smsmith    void                    *Buffer,
88577424Smsmith    UINT32                  BufferLength)
88667754Smsmith{
887210976Sjkim    void                    *NewBuffer;
88867754Smsmith    ACPI_STATUS             Status;
889202771Sjkim    UINT64                  Mask;
890202771Sjkim    UINT64                  WidthMask;
891202771Sjkim    UINT64                  MergedDatum;
892202771Sjkim    UINT64                  RawDatum = 0;
893151937Sjkim    UINT32                  FieldOffset = 0;
894151937Sjkim    UINT32                  BufferOffset = 0;
895151937Sjkim    UINT32                  BufferTailBits;
89677424Smsmith    UINT32                  DatumCount;
897151937Sjkim    UINT32                  FieldDatumCount;
898210976Sjkim    UINT32                  AccessBitWidth;
899210976Sjkim    UINT32                  RequiredLength;
900151937Sjkim    UINT32                  i;
90167754Smsmith
90267754Smsmith
903167802Sjkim    ACPI_FUNCTION_TRACE (ExInsertIntoField);
90467754Smsmith
90567754Smsmith
906151937Sjkim    /* Validate input buffer */
907131440Smarks
908193267Sjkim    NewBuffer = NULL;
909193267Sjkim    RequiredLength = ACPI_ROUND_BITS_UP_TO_BYTES (
910193267Sjkim                        ObjDesc->CommonField.BitLength);
911193267Sjkim    /*
912193267Sjkim     * We must have a buffer that is at least as long as the field
913241973Sjkim     * we are writing to. This is because individual fields are
914193267Sjkim     * indivisible and partial writes are not supported -- as per
915193267Sjkim     * the ACPI specification.
916193267Sjkim     */
917193267Sjkim    if (BufferLength < RequiredLength)
91877424Smsmith    {
919193267Sjkim        /* We need to create a new buffer */
920151937Sjkim
921193267Sjkim        NewBuffer = ACPI_ALLOCATE_ZEROED (RequiredLength);
922193267Sjkim        if (!NewBuffer)
923193267Sjkim        {
924193267Sjkim            return_ACPI_STATUS (AE_NO_MEMORY);
925193267Sjkim        }
926193267Sjkim
927193267Sjkim        /*
928193267Sjkim         * Copy the original data to the new buffer, starting
929241973Sjkim         * at Byte zero. All unused (upper) bytes of the
930193267Sjkim         * buffer will be 0.
931193267Sjkim         */
932193267Sjkim        ACPI_MEMCPY ((char *) NewBuffer, (char *) Buffer, BufferLength);
933193267Sjkim        Buffer = NewBuffer;
934193267Sjkim        BufferLength = RequiredLength;
93577424Smsmith    }
93667754Smsmith
937210976Sjkim/* TBD: Move to common setup code */
938210976Sjkim
939210976Sjkim    /* Algo is limited to sizeof(UINT64), so cut the AccessByteWidth */
940210976Sjkim    if (ObjDesc->CommonField.AccessByteWidth > sizeof (UINT64))
941210976Sjkim    {
942210976Sjkim        ObjDesc->CommonField.AccessByteWidth = sizeof (UINT64);
943210976Sjkim    }
944210976Sjkim
945210976Sjkim    AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
946210976Sjkim
947167802Sjkim    /*
948167802Sjkim     * Create the bitmasks used for bit insertion.
949167802Sjkim     * Note: This if/else is used to bypass compiler differences with the
950167802Sjkim     * shift operator
951167802Sjkim     */
952210976Sjkim    if (AccessBitWidth == ACPI_INTEGER_BIT_SIZE)
953167802Sjkim    {
954202771Sjkim        WidthMask = ACPI_UINT64_MAX;
955167802Sjkim    }
956167802Sjkim    else
957167802Sjkim    {
958210976Sjkim        WidthMask = ACPI_MASK_BITS_ABOVE (AccessBitWidth);
959167802Sjkim    }
960167802Sjkim
961167802Sjkim    Mask = WidthMask &
962210976Sjkim        ACPI_MASK_BITS_BELOW (ObjDesc->CommonField.StartFieldBitOffset);
963167802Sjkim
964151937Sjkim    /* Compute the number of datums (access width data items) */
96567754Smsmith
966151937Sjkim    DatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength,
967210976Sjkim        AccessBitWidth);
968167802Sjkim
969151937Sjkim    FieldDatumCount = ACPI_ROUND_UP_TO (ObjDesc->CommonField.BitLength +
970210976Sjkim        ObjDesc->CommonField.StartFieldBitOffset,
971210976Sjkim        AccessBitWidth);
97267754Smsmith
973151937Sjkim    /* Get initial Datum from the input buffer */
97467754Smsmith
975151937Sjkim    ACPI_MEMCPY (&RawDatum, Buffer,
976151937Sjkim        ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
977167802Sjkim            BufferLength - BufferOffset));
97867754Smsmith
979151937Sjkim    MergedDatum = RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
98067754Smsmith
981151937Sjkim    /* Write the entire field */
98267754Smsmith
983151937Sjkim    for (i = 1; i < FieldDatumCount; i++)
98467754Smsmith    {
985151937Sjkim        /* Write merged datum to the target field */
98667754Smsmith
987151937Sjkim        MergedDatum &= Mask;
988151937Sjkim        Status = AcpiExWriteWithUpdateRule (ObjDesc, Mask,
989151937Sjkim                    MergedDatum, FieldOffset);
990151937Sjkim        if (ACPI_FAILURE (Status))
991151937Sjkim        {
992193267Sjkim            goto Exit;
993151937Sjkim        }
994123315Snjl
995151937Sjkim        FieldOffset += ObjDesc->CommonField.AccessByteWidth;
99667754Smsmith
997167802Sjkim        /*
998167802Sjkim         * Start new output datum by merging with previous input datum
999167802Sjkim         * if necessary.
1000167802Sjkim         *
1001167802Sjkim         * Note: Before the shift, check if the shift value will be larger than
1002167802Sjkim         * the integer size. If so, there is no need to perform the operation.
1003167802Sjkim         * This avoids the differences in behavior between different compilers
1004167802Sjkim         * concerning shift values larger than the target data width.
1005167802Sjkim         */
1006210976Sjkim        if ((AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset) <
1007210976Sjkim            ACPI_INTEGER_BIT_SIZE)
1008167802Sjkim        {
1009167802Sjkim            MergedDatum = RawDatum >>
1010210976Sjkim                (AccessBitWidth - ObjDesc->CommonField.StartFieldBitOffset);
1011167802Sjkim        }
1012167802Sjkim        else
1013167802Sjkim        {
1014167802Sjkim            MergedDatum = 0;
1015167802Sjkim        }
1016167802Sjkim
1017167802Sjkim        Mask = WidthMask;
1018167802Sjkim
1019151937Sjkim        if (i == DatumCount)
102067754Smsmith        {
1021151937Sjkim            break;
102267754Smsmith        }
102377424Smsmith
1024151937Sjkim        /* Get the next input datum from the buffer */
102567754Smsmith
1026151937Sjkim        BufferOffset += ObjDesc->CommonField.AccessByteWidth;
1027151937Sjkim        ACPI_MEMCPY (&RawDatum, ((char *) Buffer) + BufferOffset,
1028151937Sjkim            ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
1029210976Sjkim                 BufferLength - BufferOffset));
1030210976Sjkim
1031151937Sjkim        MergedDatum |= RawDatum << ObjDesc->CommonField.StartFieldBitOffset;
1032151937Sjkim    }
103367754Smsmith
1034151937Sjkim    /* Mask off any extra bits in the last datum */
103567754Smsmith
1036151937Sjkim    BufferTailBits = (ObjDesc->CommonField.BitLength +
1037210976Sjkim        ObjDesc->CommonField.StartFieldBitOffset) % AccessBitWidth;
1038151937Sjkim    if (BufferTailBits)
1039151937Sjkim    {
1040151937Sjkim        Mask &= ACPI_MASK_BITS_ABOVE (BufferTailBits);
1041151937Sjkim    }
1042123315Snjl
1043151937Sjkim    /* Write the last datum to the field */
104467754Smsmith
1045151937Sjkim    MergedDatum &= Mask;
1046151937Sjkim    Status = AcpiExWriteWithUpdateRule (ObjDesc,
1047151937Sjkim                Mask, MergedDatum, FieldOffset);
104867754Smsmith
1049193267SjkimExit:
1050193267Sjkim    /* Free temporary buffer if we used one */
1051193267Sjkim
1052193267Sjkim    if (NewBuffer)
1053193267Sjkim    {
1054193267Sjkim        ACPI_FREE (NewBuffer);
1055193267Sjkim    }
105667754Smsmith    return_ACPI_STATUS (Status);
105767754Smsmith}
1058