[Intel-gfx] [PATCH] drm/i915: add plumbing for SWSCI

Paulo Zanoni przanoni at gmail.com
Thu Aug 29 19:15:13 CEST 2013


2013/8/29 Jani Nikula <jani.nikula at intel.com>:
> On Thu, 29 Aug 2013, Paulo Zanoni <przanoni at gmail.com> wrote:
>> Hi
>>
>> 2013/8/28 Jani Nikula <jani.nikula at intel.com>:
>>> SWSCI is a driver to bios call interface.
>>>
>>> This checks for SWSCI availability and bios requested callbacks, and
>>> filters out any calls that shouldn't happen. This way the callers don't
>>> need to do the checks all over the place.
>>>
>>> v2: silence some checkpatch nagging
>>>
>>> v3: set PCI_SWSCI bit 0 to trigger interrupt (Mengdong Lin)
>>>
>>> v4: remove an extra #define (Jesse)
>>>
>>> v5: spec says s/w is responsible for clearing PCI_SWSCI bit 0 too
>>>
>>> Signed-off-by: Jani Nikula <jani.nikula at intel.com>
>>> ---
>>>  drivers/gpu/drm/i915/i915_drv.h       |    1 +
>>>  drivers/gpu/drm/i915/intel_opregion.c |  133 ++++++++++++++++++++++++++++++++-
>>>  2 files changed, 131 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
>>> index 84b95b1..adc2f46 100644
>>> --- a/drivers/gpu/drm/i915/i915_drv.h
>>> +++ b/drivers/gpu/drm/i915/i915_drv.h
>>> @@ -225,6 +225,7 @@ struct intel_opregion {
>>>         struct opregion_header __iomem *header;
>>>         struct opregion_acpi __iomem *acpi;
>>>         struct opregion_swsci __iomem *swsci;
>>> +       u32 swsci_requested_callbacks;
>>>         struct opregion_asle __iomem *asle;
>>>         void __iomem *vbt;
>>>         u32 __iomem *lid_state;
>>> diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
>>> index cfb8fb6..233cc7f 100644
>>> --- a/drivers/gpu/drm/i915/intel_opregion.c
>>> +++ b/drivers/gpu/drm/i915/intel_opregion.c
>>> @@ -36,8 +36,11 @@
>>>  #include "i915_drv.h"
>>>  #include "intel_drv.h"
>>>
>>> -#define PCI_ASLE 0xe4
>>> -#define PCI_ASLS 0xfc
>>> +#define PCI_ASLE               0xe4
>>> +#define PCI_ASLS               0xfc
>>> +#define PCI_SWSCI              0xe8
>>> +#define PCI_SWSCI_SCISEL       (1 << 15)
>>> +#define PCI_SWSCI_GSSCIE       (1 << 0)
>>>
>>>  #define OPREGION_HEADER_OFFSET 0
>>>  #define OPREGION_ACPI_OFFSET   0x100
>>> @@ -151,6 +154,48 @@ struct opregion_asle {
>>>
>>>  #define ASLE_CBLV_VALID         (1<<31)
>>>
>>> +/* Software System Control Interrupt (SWSCI) */
>>> +#define SWSCI_SCIC_INDICATOR           (1 << 0)
>>> +#define SWSCI_SCIC_MAIN_FUNCTION_SHIFT 1
>>> +#define SWSCI_SCIC_MAIN_FUNCTION_MASK  (0xf << 1)
>>> +#define SWSCI_SCIC_SUB_FUNCTION_SHIFT  8
>>> +#define SWSCI_SCIC_SUB_FUNCTION_MASK   (0x7f << 8)
>>
>> Shouldn't this be (0xff << 8) since it's bits 15:8 ?
>
> In my spec, section 4.1.1: "Function codes are broken down into two
> parts the main function code (SCIC bits [4:1]) and the sub-function code
> (SCIC bits [14:8])."

In the same spec, section 3.3.1, table 3-35 says it's 15:8 :(
We're both right and wrong...


>
>>> +#define SWSCI_SCIC_EXIT_STATUS_SHIFT   5
>>> +#define SWSCI_SCIC_EXIT_STATUS_MASK    (7 << 5)
>>> +#define SWSCI_SCIC_EXIT_STATUS_SUCCESS 1
>>> +
>>> +#define SWSCI_FUNCTION_CODE(main, sub) \
>>> +       ((main) << SWSCI_SCIC_MAIN_FUNCTION_SHIFT | \
>>> +        (sub) << SWSCI_SCIC_SUB_FUNCTION_SHIFT)
>>> +
>>> +/* SWSCI: Get BIOS Data (GBDA) */
>>> +#define SWSCI_GBDA                     4
>>> +#define SWSCI_GBDA_SUPPORTED_CALLS     SWSCI_FUNCTION_CODE(SWSCI_GBDA, 0)
>>> +#define SWSCI_GBDA_REQUESTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 1)
>>> +#define SWSCI_GBDA_BOOT_DISPLAY_PREF   SWSCI_FUNCTION_CODE(SWSCI_GBDA, 4)
>>> +#define SWSCI_GBDA_PANEL_DETAILS       SWSCI_FUNCTION_CODE(SWSCI_GBDA, 5)
>>> +#define SWSCI_GBDA_TV_STANDARD         SWSCI_FUNCTION_CODE(SWSCI_GBDA, 6)
>>
>> The newer spec says "reserved" here. But let's leave your definition
>> until they assign it again to something else :) (same thing for the
>> other TV bit below)
>
> Okay. :)
>
>>> +#define SWSCI_GBDA_INTERNAL_GRAPHICS   SWSCI_FUNCTION_CODE(SWSCI_GBDA, 7)
>>> +#define SWSCI_GBDA_SPREAD_SPECTRUM     SWSCI_FUNCTION_CODE(SWSCI_GBDA, 10)
>>> +
>>> +/* SWSCI: System BIOS Callbacks (SBCB) */
>>> +#define SWSCI_SBCB                     6
>>> +#define SWSCI_SBCB_SUPPORTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 0)
>>> +#define SWSCI_SBCB_INIT_COMPLETION     SWSCI_FUNCTION_CODE(SWSCI_SBCB, 1)
>>> +#define SWSCI_SBCB_PRE_HIRES_SET_MODE  SWSCI_FUNCTION_CODE(SWSCI_SBCB, 3)
>>> +#define SWSCI_SBCB_POST_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 4)
>>> +#define SWSCI_SBCB_DISPLAY_SWITCH      SWSCI_FUNCTION_CODE(SWSCI_SBCB, 5)
>>> +#define SWSCI_SBCB_SET_TV_FORMAT       SWSCI_FUNCTION_CODE(SWSCI_SBCB, 6)
>>> +#define SWSCI_SBCB_ADAPTER_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 7)
>>> +#define SWSCI_SBCB_DISPLAY_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 8)
>>> +#define SWSCI_SBCB_SET_BOOT_DISPLAY    SWSCI_FUNCTION_CODE(SWSCI_SBCB, 9)
>>> +#define SWSCI_SBCB_SET_INTERNAL_GFX    SWSCI_FUNCTION_CODE(SWSCI_SBCB, 11)
>>> +#define SWSCI_SBCB_POST_HIRES_TO_DOS_FS        SWSCI_FUNCTION_CODE(SWSCI_SBCB, 16)
>>> +#define SWSCI_SBCB_SUSPEND_RESUME      SWSCI_FUNCTION_CODE(SWSCI_SBCB, 17)
>>> +#define SWSCI_SBCB_SET_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 18)
>>> +#define SWSCI_SBCB_POST_VBE_PM         SWSCI_FUNCTION_CODE(SWSCI_SBCB, 19)
>>> +#define SWSCI_SBCB_ENABLE_DISABLE_AUDIO        SWSCI_FUNCTION_CODE(SWSCI_SBCB, 21)
>>> +
>>>  #define ACPI_OTHER_OUTPUT (0<<8)
>>>  #define ACPI_VGA_OUTPUT (1<<8)
>>>  #define ACPI_TV_OUTPUT (2<<8)
>>> @@ -158,6 +203,84 @@ struct opregion_asle {
>>>  #define ACPI_LVDS_OUTPUT (4<<8)
>>>
>>>  #ifdef CONFIG_ACPI
>>> +static int swsci(struct drm_device *dev, u32 function, u32 parm, u32 *parm_out)
>>> +{
>>> +       struct drm_i915_private *dev_priv = dev->dev_private;
>>> +       struct opregion_swsci __iomem *swsci = dev_priv->opregion.swsci;
>>> +       u32 main_function, sub_function, scic;
>>> +       u16 pci_swsci;
>>> +
>>> +       if (!swsci)
>>> +               return 0;
>>
>> You also use "return 0" for success below. I'd return -ESOMETHING
>> here. Perhaps also print an error message.
>
> My reasoning was, if the platform doesn't support SWSCI (this one here),
> or does not request a specific sub-function (the one below), we just
> carry on. I can add a -ESOMETHING if you like, but I think an error
> message would pollute dmesg too much. Or else we'd have to burden the
> call sites with checks if we can call the functions.

Makes sense.


>
>>> +
>>> +       main_function = (function & SWSCI_SCIC_MAIN_FUNCTION_MASK) >>
>>> +               SWSCI_SCIC_MAIN_FUNCTION_SHIFT;
>>> +       sub_function = (function & SWSCI_SCIC_SUB_FUNCTION_MASK) >>
>>> +               SWSCI_SCIC_SUB_FUNCTION_SHIFT;
>>> +
>>> +       if (main_function == SWSCI_SBCB && sub_function != 0) {
>>> +               /*
>>> +                * SBCB sub-function codes correspond to the bits in requested
>>> +                * callbacks, except for bit 0 which is reserved. The driver
>>> +                * must not call interfaces that are not specifically requested
>>> +                * by the bios.
>>> +                */
>>> +               if ((dev_priv->opregion.swsci_requested_callbacks &
>>> +                    (1 << sub_function)) == 0)
>>
>> I think you have to do (1 << sub_funcition -1) to make it correct.
>
> I think you're off by one, please double check.

Ok, it seems you were using spec version 0.8 from December 2012,
right? Mine is version 1.0 from June 2013. The sub-function codes are
still the same, but the callbacks reply changed. Now I see some bits
moved even 5 positions (e.g, enable/disable audio) while the ones I
checked only moved 1 position. Trolls!!! Where's my sword?


>
>>> +                       return 0;
>>
>> I'd return -ESOMETHINGELSE here too, possibly printing an error
>> message.
>
> See above.
>
>>> +       }
>>> +
>>> +       /* XXX: The spec tells us to do this, but we are the only user... */
>>
>> I'd remove the "XXX: " substring from the comment. The spec suggests
>> only graphics uses this specific interface, but it doesn't say that
>> other events happening on the system won't cause this bit to be
>> flipped. I don't know... the code seems correct, so the "XXX" code may
>> be misleading.
>
> I'll drop it.
>
>> The sad thing is that there's also no guarantees that someone won't
>> start using the interface just after you read this bit and before
>> writing it to 1... We may need dev_priv->swsci_lock depending on how
>> we use this function (I haven't checked the next patches yet).
>
> I think I thought we'd be covered by struct_mutex here, but perhaps that
> was before the adapter notification... how's the pc8 call paths?

If we only call this while modesetting and entering/leaving PC8, we
should be fine. But we never know what's going to be needed in the
future. For now, we can leave as it is.


>
>>> +       scic = ioread32(&swsci->scic);
>>> +       if (scic & SWSCI_SCIC_INDICATOR) {
>>> +               DRM_DEBUG_DRIVER("SWSCI request already in progress\n");
>>> +               return -EBUSY;
>>> +       }
>>> +
>>> +       scic = function | SWSCI_SCIC_INDICATOR;
>>> +
>>> +       iowrite32(parm, &swsci->parm);
>>> +       iowrite32(scic, &swsci->scic);
>>> +
>>
>>
>> From what I understood of the spec, from the line below...
>>
>>> +       /* Ensure SCI event is selected and event trigger is cleared. */
>>> +       pci_read_config_word(dev->pdev, PCI_SWSCI, &pci_swsci);
>>> +       if (!(pci_swsci & PCI_SWSCI_SCISEL) || (pci_swsci & PCI_SWSCI_GSSCIE)) {
>>> +               pci_swsci |= PCI_SWSCI_SCISEL;
>>> +               pci_swsci &= ~PCI_SWSCI_GSSCIE;
>>> +               pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci);
>>> +       }
>>> +
>>> +       /* Use event trigger to tell bios to check the mail. */
>>> +       pci_swsci |= PCI_SWSCI_GSSCIE;
>>> +       pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci);
>>
>> ... to the line above, we don't need this code. My understanding is
>> that the BIOS will do all these things automagically for us, so if we
>> write these regs we may even be racing with it, especially since we
>> already wrote swsci->scic. Do we really need this code?
>
> We need it. It gives me some consolation that I made the same
> interpretation as you did, before Mengdong Lin corrected me... The
> opregion spec is really unclear about this, and it's easy to confuse
> between the SCIC register and the PCI SWSCI register. Have a look at
> SWSCI in the bspec PCI section. Only that triggers the event.

I'll take a look and do some experiments.


>
>>> +
>>> +       /*
>>> +        * Poll for the result. The spec says nothing about timing out; add an
>>> +        * arbitrary timeout to not hang if the bios fails.
>>> +        */
>>
>> The spec does mention a timeout, check the description of DSLP: Driver
>> Sleep Timeout.
>
> Ah yes, thanks. Any idea what the unit for DSLP might be? Not in my
> spec...

On the 1.0 spec, the second paragraph of section 3.3.3 is just
"Timeout Unit will be in milliseconds". I couldn't find anything in
the 0.8 spec... Section 3.3.3 also says that if the timeout value is
0, we should consider 2ms.


>
>>> +#define C (((scic = ioread32(&swsci->scic)) & SWSCI_SCIC_INDICATOR) == 0)
>>> +       if (wait_for(C, 500)) {
>>> +               DRM_DEBUG_DRIVER("SWSCI request timed out\n");
>>> +               return -ETIMEDOUT;
>>> +       }
>>> +
>>> +       scic = (scic & SWSCI_SCIC_EXIT_STATUS_MASK) >>
>>> +               SWSCI_SCIC_EXIT_STATUS_SHIFT;
>>> +
>>> +       /* Note: scic == 0 is an error! */
>>> +       if (scic != SWSCI_SCIC_EXIT_STATUS_SUCCESS) {
>>> +               DRM_DEBUG_DRIVER("SWSCI request error %u\n", scic);
>>> +               return -EINVAL; /* XXX */
>>
>> Why XXX above? To me, a XXX means there's something on the code that
>> must be fixed.
>
> s/XXX/check if there's an appropriate error code we could use/ :)

Ok. I'll leave the decision to you :)


>
>>> +       }
>>> +
>>> +       if (parm_out)
>>> +               *parm_out = ioread32(&swsci->parm);
>>> +
>>> +       return 0;
>>> +
>>> +#undef C
>>> +}
>>> +
>>>  static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
>>>  {
>>>         struct drm_i915_private *dev_priv = dev->dev_private;
>>> @@ -488,8 +611,12 @@ int intel_opregion_setup(struct drm_device *dev)
>>>         }
>>>
>>>         if (mboxes & MBOX_SWSCI) {
>>> -               DRM_DEBUG_DRIVER("SWSCI supported\n");
>>>                 opregion->swsci = base + OPREGION_SWSCI_OFFSET;
>>> +
>>> +               swsci(dev, SWSCI_GBDA_REQUESTED_CALLBACKS, 0,
>>> +                     &opregion->swsci_requested_callbacks);
>>
>> No error checking?
>
> Will fix.
>
> Thanks for the review,
> Jani.
>
>>
>>
>>> +               DRM_DEBUG_DRIVER("SWSCI supported, requested callbacks %08x\n",
>>> +                                opregion->swsci_requested_callbacks);
>>>         }
>>>         if (mboxes & MBOX_ASLE) {
>>>                 DRM_DEBUG_DRIVER("ASLE supported\n");
>>> --
>>> 1.7.10.4
>>>
>>
>>
>>
>> --
>> Paulo Zanoni
>> _______________________________________________
>> Intel-gfx mailing list
>> Intel-gfx at lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/intel-gfx



-- 
Paulo Zanoni



More information about the Intel-gfx mailing list