[Nouveau] Delays in DRM nouveau_bios.c
Pekka Paalanen
pq at iki.fi
Thu Aug 20 04:57:55 PDT 2009
On Thu, 20 Aug 2009 14:40:24 +0300
Pekka Paalanen <pq at iki.fi> wrote:
> Hi,
>
> questions will follow.
This patch builds, but I have not tried to run it.
I'm hoping Stuart or someone could comment on it.
> ---
>
> diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
> index 99f7bd4..13b3fb1 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_bios.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
> @@ -40,8 +40,6 @@
> #define BIOSLOG(sip, fmt, arg...) NV_DEBUG(sip->dev, fmt, ##arg)
> #define LOG_OLD_VALUE(x) //x
>
> -#define BIOS_USLEEP(n) mdelay((n)/1000)
> -
> #define ROM16(x) le16_to_cpu(*(uint16_t *)&(x))
> #define ROM32(x) le32_to_cpu(*(uint32_t *)&(x))
>
> @@ -50,6 +48,15 @@ struct init_exec {
> bool repeat;
> };
>
> +static inline void bios_usleep(unsigned usecs)
> +{
> + might_sleep();
> + if (usecs < 1000 * MAX_UDELAY_MS)
> + udelay(usecs);
> + else
> + msleep(usecs / 1000 + 1);
> +}
This function is to replace the BIOS_USLEEP() macro with something,
that works both for short and long delays. BIOS_USLEEP would truncate
all sleeps to milliseconds, meaning delays less than 1000 us would
be zero. Also, mdelay() is a busyloop delay burning CPU cycles for
nothing. msleep() is not, it will reschedule until the delay has
passed. The change raises two questions:
- Is the bios code ever called in a situation, where scheduling
is not allowed (interrupt context, pre-emption disabled, inside
spinlocks, etc.)?
- Is the delay accuracy on the msleep path critical?
> static bool nv_cksum(const uint8_t *data, unsigned int length)
> {
> /* there's a few checksums in the BIOS, so here's a generic checking function */
> @@ -262,7 +269,7 @@ static int parse_init_table(struct nvbios *, unsigned int, struct init_exec *);
> static void still_alive(void)
> {
> // sync();
> -// BIOS_USLEEP(2000);
> +// bios_usleep(2000);
> }
>
> static uint32_t
> @@ -1608,17 +1615,18 @@ init_condition_time(struct nvbios *bios, uint16_t offset,
>
> for (; retries > 0; retries--)
> if (bios_condition_met(bios, offset, cond)) {
> - BIOSLOG(bios, "0x%04X: Condition met, continuing\n", offset);
> + BIOSLOG(bios, "0x%04X: Condition met, continuing\n",
> + offset);
> break;
> } else {
> BIOSLOG(bios, "0x%04X: Condition not met, sleeping for 20ms\n", offset);
> - BIOS_USLEEP(20000);
> + bios_usleep(20000);
It so happens, that 20000 was the maximum allowed udelay() constant time
for x86. Did the number come from that?
> }
>
> if (!bios_condition_met(bios, offset, cond)) {
> NV_WARN(bios->dev,
> "0x%04X: Condition still not met after %dms, "
> - "skiping following opcodes\n", offset, 20 * retries);
> + "skipping following opcodes\n", offset, 20 * retries);
> iexec->execute = false;
> }
>
> @@ -1851,7 +1859,7 @@ init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
> bios_wr32(bios, NV_PBUS_PCI_NV_19, 0);
> bios_wr32(bios, reg, value1);
>
> - BIOS_USLEEP(10);
> + bios_usleep(10);
With the old BIOS_USLEEP, this delay would vanish.
>
> bios_wr32(bios, reg, value2);
> bios_wr32(bios, NV_PBUS_PCI_NV_19, pci_nv_19);
> @@ -2233,8 +2241,7 @@ init_time(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
> BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X microseconds\n",
> offset, time);
>
> - BIOS_USLEEP(time);
> -
> + bios_usleep(time);
Can here be delay accuracy issues? How much are we allowed to exceed the
specified delay?
> return true;
> }
>
> @@ -2872,9 +2879,11 @@ static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_entr
>
> run_digital_op_script(dev, scriptofs, dcbent, head, bios->fp.dual_link);
>
> - if (script == LVDS_PANEL_OFF)
> + if (script == LVDS_PANEL_OFF) {
> /* off-on delay in ms */
> - BIOS_USLEEP(ROM16(bios->data[bios->fp.xlated_entry + 7]));
> + bios_usleep(ROM16(bios->data[bios->fp.xlated_entry + 7]));
> + }
The comment and code disagree here. Is it microseconds or milliseconds?
--
Pekka Paalanen
http://www.iki.fi/pq/
More information about the Nouveau
mailing list