coreboot-kgpe-d16/src/soc/intel/baytrail/gfx.c
Jacob Garber 767c4b2899 soc/intel/baytrail: Prevent unintended sign extensions
Consider the following assignment:

    u64 = s32

For positive values this is fine, but if the s32 is negative, it will be
sign-extended in the conversion to a very large unsigned integer. This
manifests itself in two ways in the following code:

First, gpu_pipe{a,b}_port_select are defined as int, and can have the
values 1 or 2. In the case when they have the value 2, the shift 2 << 30
will be a negative number, making it susceptible to the sign-extension
problem above. Change these variables to something more reasonable like
a uint8_t, which is unsigned.

Second, in any bit shift, any variable with width less than an int will
be implicitly promoted to an int before performing the bit shift.
For example, the variable gpu_pipea_power_on_delay is a uint16_t, and if its
highest bit is set, the shift gpu_pipea_power_on_delay << 16 will become
negative, again introducing the above problem. To prevent this, cast all
smaller variables to a u32 before the shift, which will prevent the
implicit promotions and sign extensions.

Change-Id: Ic5db6001504cefb501dee199590a0e961a15771b
Signed-off-by: Jacob Garber <jgarber1@ualberta.ca>
Found-by: Coverity CID 1229699, 1229700, 1229701, 1229702
Reviewed-on: https://review.coreboot.org/c/coreboot/+/34487
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-by: Alexander Couzens <lynxis@fe80.eu>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2019-07-29 06:08:21 +00:00

441 lines
14 KiB
C

/*
* This file is part of the coreboot project.
*
* Copyright 2013 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <device/mmio.h>
#include <device/pci_ops.h>
#include <console/console.h>
#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ids.h>
#include <drivers/intel/gma/opregion.h>
#include <reg_script.h>
#include <stdlib.h>
#include <soc/gfx.h>
#include <soc/iosf.h>
#include <soc/nvs.h>
#include <soc/pci_devs.h>
#include <soc/ramstage.h>
#include <cbmem.h>
#include <types.h>
#include "chip.h"
#define GFX_TIMEOUT 100000 /* 100ms */
/*
* Lock Power Context Base Register to point to a 24KB block
* of memory in GSM. Power context save data is stored here.
*/
static void gfx_lock_pcbase(struct device *dev)
{
struct resource *res = find_resource(dev, PCI_BASE_ADDRESS_0);
const u16 gms_size_map[17] = { 0,32,64,96,128,160,192,224,256,
288,320,352,384,416,448,480,512 };
u32 pcsize = 24 << 10; /* 24KB */
u32 wopcmsz = 0x100000; /* PAVP offset */
u32 gms, gmsize, pcbase;
gms = pci_read_config32(dev, GGC) & GGC_GSM_SIZE_MASK;
gms >>= 3;
if (gms >= ARRAY_SIZE(gms_size_map))
return;
gmsize = gms_size_map[gms];
/* PcBase = BDSM + GMS Size - WOPCMSZ - PowerContextSize */
pcbase = pci_read_config32(dev, GSM_BASE) & 0xfff00000;
pcbase += (gmsize-1) * wopcmsz - pcsize;
pcbase |= 1; /* Lock */
write32((u32 *)(uintptr_t)(res->base + 0x182120), pcbase);
}
static const struct reg_script gfx_init_script[] = {
/* Allow-Wake render/media wells */
REG_RES_RMW32(PCI_BASE_ADDRESS_0, 0x130090, ~1, 1),
REG_RES_POLL32(PCI_BASE_ADDRESS_0, 0x130094, 1, 1, GFX_TIMEOUT),
/* Render Force-Wake */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x1300b0, 0x80008000),
REG_RES_POLL32(PCI_BASE_ADDRESS_0, 0x1300b4, 0x8000, 0x8000,
GFX_TIMEOUT),
/* Media Force-Wake */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x1300b8, 0x80008000),
REG_RES_POLL32(PCI_BASE_ADDRESS_0, 0x1300bc, 0x8000, 0x8000,
GFX_TIMEOUT),
/* Workaround - X0:261954/A0:261955 */
REG_RES_RMW32(PCI_BASE_ADDRESS_0, 0x182060, ~0xf, 1),
/*
* PowerMeter Weights
*/
/* SET1 */
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA800, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA804, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA808, 0x0000ff0A),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA80C, 0x1D000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA810, 0xAC004900),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA814, 0x000F0000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA818, 0x5A000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA81C, 0x2600001F),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA820, 0x00090000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA824, 0x2000ff00),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA828, 0xff090016),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA82C, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA830, 0x00000100),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA834, 0x00A00F51),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA838, 0x000B0000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA83C, 0xcb7D3307),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA840, 0x003C0000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA844, 0xFFFF0000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA848, 0x00220000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA84c, 0x43000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA850, 0x00000800),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA854, 0x00000F00),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA858, 0x00000021),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA85c, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA860, 0x00190000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xAA80, 0x00FF00FF),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xAA84, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0x1300A4, 0x00000000),
/* SET2 */
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA900, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA904, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA908, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa90c, 0x1D000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa910, 0xAC005000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa914, 0x000F0000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa918, 0x5A000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa91c, 0x2600001F),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa920, 0x00090000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa924, 0x2000ff00),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa928, 0xff090016),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa92c, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa930, 0x00000100),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa934, 0x00A00F51),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xa938, 0x000B0000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA93C, 0xcb7D3307),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA940, 0x003C0000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA944, 0xFFFF0000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA948, 0x00220000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA94C, 0x43000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA950, 0x00000800),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA954, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA960, 0x00000000),
/* SET3 */
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xaa3c, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xaa54, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xaa60, 0x00000000),
/* Enable PowerMeter Counters */
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA248, 0x00000058),
/* Program PUNIT_GPU_EC_VIRUS based on DPTF SDP */
/* SDP Profile 4 == 0x11940, others 0xcf08 */
REG_IOSF_WRITE(IOSF_PORT_PMC, PUNIT_GPU_EC_VIRUS, 0xcf08),
/* GfxPause */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0xa000, 0x00071388),
/* Dynamic EU Control Settings */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0xa080, 0x00000004),
/* Lock ECO Bit Settings */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0xa180, 0x80000000),
/* DOP Clock Gating */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x9424, 0x00000001),
/* MBCunit will send the VCR (Fuse) writes as NP-W */
REG_RES_RMW32(PCI_BASE_ADDRESS_0, 0x907c, 0xfffeffff, 0x00010000),
/*
* RC6 Settings
*/
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA090, 0x00000000),
/* RC1e - RC6/6p - RC6pp Wake Rate Limits */
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA09C, 0x00280000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA0A8, 0x0001E848),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA0AC, 0x00000019),
/* RC Sleep / RCx Thresholds */
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA0B0, 0x00000000),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA0B8, 0x00000557),
/*
* Turbo Settings
*/
/* Render/Video/Blitter Idle Max Count */
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0x2054, 0x0000000A),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0x12054, 0x0000000A),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0x22054, 0x0000000A),
/* RP Down Timeout */
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA010, 0x000F4240),
/*
* Turbo Control Settings
*/
/* RP Up/Down Threshold */
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA02C, 0x0000E8E8),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA030, 0x0003BD08),
/* RP Up/Down EI */
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA068, 0x000101D0),
REG_RES_OR32(PCI_BASE_ADDRESS_0, 0xA06C, 0x00055730),
/* RP Idle Hysteresis */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0xa070, 0x0000000a),
/* HW RC6 Control Settings */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0xa090, 0x11000000),
/* RP Control */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0xa024, 0x00000592),
/* Enable PM Interrupts */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x44024, 0x03000000),
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x4402c, 0x03000076),
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0xa168, 0x0000007e),
/* Aggressive Clock Gating */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x9400, 0),
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x9404, 0),
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x9408, 0),
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x940c, 0),
/* Enable Gfx Turbo. */
REG_IOSF_RMW(IOSF_PORT_PMC, SB_BIOS_CONFIG,
~SB_BIOS_CONFIG_GFX_TURBO_DIS, 0),
REG_SCRIPT_END
};
static const struct reg_script gpu_pre_vbios_script[] = {
/* Make sure GFX is bus master with MMIO access */
REG_PCI_OR32(PCI_COMMAND, PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY),
/* Display */
REG_IOSF_WRITE(IOSF_PORT_PMC, PUNIT_PWRGT_CONTROL, 0xc0),
REG_IOSF_POLL(IOSF_PORT_PMC, PUNIT_PWRGT_STATUS, 0xc0, 0xc0,
GFX_TIMEOUT),
/* Tx/Rx Lanes */
REG_IOSF_WRITE(IOSF_PORT_PMC, PUNIT_PWRGT_CONTROL, 0xfff0c0),
REG_IOSF_POLL(IOSF_PORT_PMC, PUNIT_PWRGT_STATUS, 0xfff0c0, 0xfff0c0,
GFX_TIMEOUT),
/* Common Lane */
REG_IOSF_WRITE(IOSF_PORT_PMC, PUNIT_PWRGT_CONTROL, 0xfffcc0),
REG_IOSF_POLL(IOSF_PORT_PMC, PUNIT_PWRGT_STATUS, 0xfffcc0, 0xfffcc0,
GFX_TIMEOUT),
/* Ungating Tx only */
REG_IOSF_WRITE(IOSF_PORT_PMC, PUNIT_PWRGT_CONTROL, 0xf00cc0),
REG_IOSF_POLL(IOSF_PORT_PMC, PUNIT_PWRGT_STATUS, 0xfffcc0, 0xf00cc0,
GFX_TIMEOUT),
/* Ungating Common Lane only */
REG_IOSF_WRITE(IOSF_PORT_PMC, PUNIT_PWRGT_CONTROL, 0xf000c0),
REG_IOSF_POLL(IOSF_PORT_PMC, PUNIT_PWRGT_STATUS, 0xffffc0, 0xf000c0,
GFX_TIMEOUT),
/* Ungating Display */
REG_IOSF_WRITE(IOSF_PORT_PMC, PUNIT_PWRGT_CONTROL, 0xf00000),
REG_IOSF_POLL(IOSF_PORT_PMC, PUNIT_PWRGT_STATUS, 0xfffff0, 0xf00000,
GFX_TIMEOUT),
REG_SCRIPT_END
};
static const struct reg_script gfx_post_vbios_script[] = {
/* Deassert Render Force-Wake */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x1300b0, 0x80000000),
REG_RES_POLL32(PCI_BASE_ADDRESS_0, 0x1300b4, 0x8000, 0, GFX_TIMEOUT),
/* Deassert Media Force-Wake */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0x1300b8, 0x80000000),
REG_RES_POLL32(PCI_BASE_ADDRESS_0, 0x1300bc, 0x8000, 0, GFX_TIMEOUT),
/* Set Lock bits */
REG_PCI_RMW32(GGC, 0xffffffff, 1),
REG_PCI_RMW32(GSM_BASE, 0xffffffff, 1),
REG_PCI_RMW32(GTT_BASE, 0xffffffff, 1),
REG_SCRIPT_END
};
static inline void gfx_run_script(struct device *dev,
const struct reg_script *ops)
{
reg_script_run_on_dev(dev, ops);
}
static void gfx_pre_vbios_init(struct device *dev)
{
printk(BIOS_INFO, "GFX: Pre VBIOS Init\n");
gfx_run_script(dev, gpu_pre_vbios_script);
}
static void gfx_pm_init(struct device *dev)
{
printk(BIOS_INFO, "GFX: Power Management Init\n");
gfx_run_script(dev, gfx_init_script);
/* Lock power context base */
gfx_lock_pcbase(dev);
}
static void gfx_post_vbios_init(struct device *dev)
{
printk(BIOS_INFO, "GFX: Post VBIOS Init\n");
gfx_run_script(dev, gfx_post_vbios_script);
}
static void set_backlight_pwm(struct device *dev, uint32_t bklt_reg, int req_hz)
{
int divider;
struct resource *res;
res = find_resource(dev, PCI_BASE_ADDRESS_0);
if (res == NULL)
return;
/* Default to 200 Hz if nothing is set. */
if (req_hz == 0)
req_hz = 200;
/* Base clock is 25MHz */
divider = 25 * 1000 * 1000 / (16 * req_hz);
/* Do not set duty cycle (lower 16 bits). Just set the divider. */
write32((u32 *)(uintptr_t)(res->base + bklt_reg), divider << 16);
}
static void gfx_panel_setup(struct device *dev)
{
struct soc_intel_baytrail_config *config = config_of(dev);
struct reg_script gfx_pipea_init[] = {
/* CONTROL */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, PIPEA_REG(PP_CONTROL),
PP_CONTROL_UNLOCK | PP_CONTROL_EDP_FORCE_VDD),
/* POWER ON */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, PIPEA_REG(PP_ON_DELAYS),
((u32)config->gpu_pipea_port_select << 30 |
(u32)config->gpu_pipea_power_on_delay << 16 |
(u32)config->gpu_pipea_light_on_delay)),
/* POWER OFF */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, PIPEA_REG(PP_OFF_DELAYS),
((u32)config->gpu_pipea_power_off_delay << 16 |
(u32)config->gpu_pipea_light_off_delay)),
/* DIVISOR */
REG_RES_RMW32(PCI_BASE_ADDRESS_0, PIPEA_REG(PP_DIVISOR),
~0x1f, config->gpu_pipea_power_cycle_delay),
REG_SCRIPT_END
};
struct reg_script gfx_pipeb_init[] = {
/* CONTROL */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, PIPEB_REG(PP_CONTROL),
PP_CONTROL_UNLOCK | PP_CONTROL_EDP_FORCE_VDD),
/* POWER ON */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, PIPEB_REG(PP_ON_DELAYS),
((u32)config->gpu_pipeb_port_select << 30 |
(u32)config->gpu_pipeb_power_on_delay << 16 |
(u32)config->gpu_pipeb_light_on_delay)),
/* POWER OFF */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, PIPEB_REG(PP_OFF_DELAYS),
((u32)config->gpu_pipeb_power_off_delay << 16 |
(u32)config->gpu_pipeb_light_off_delay)),
/* DIVISOR */
REG_RES_RMW32(PCI_BASE_ADDRESS_0, PIPEB_REG(PP_DIVISOR),
~0x1f, config->gpu_pipeb_power_cycle_delay),
REG_SCRIPT_END
};
if (config->gpu_pipea_port_select) {
printk(BIOS_INFO, "GFX: Initialize PIPEA\n");
reg_script_run_on_dev(dev, gfx_pipea_init);
set_backlight_pwm(dev, PIPEA_REG(BACKLIGHT_CTL),
config->gpu_pipea_pwm_freq_hz);
}
if (config->gpu_pipeb_port_select) {
printk(BIOS_INFO, "GFX: Initialize PIPEB\n");
reg_script_run_on_dev(dev, gfx_pipeb_init);
set_backlight_pwm(dev, PIPEB_REG(BACKLIGHT_CTL),
config->gpu_pipeb_pwm_freq_hz);
}
}
uintptr_t gma_get_gnvs_aslb(const void *gnvs)
{
const global_nvs_t *gnvs_ptr = gnvs;
return (uintptr_t)(gnvs_ptr ? gnvs_ptr->aslb : 0);
}
void gma_set_gnvs_aslb(void *gnvs, uintptr_t aslb)
{
global_nvs_t *gnvs_ptr = gnvs;
if (gnvs_ptr)
gnvs_ptr->aslb = aslb;
}
static void gfx_init(struct device *dev)
{
/* Pre VBIOS Init */
gfx_pre_vbios_init(dev);
/* Power Management Init */
gfx_pm_init(dev);
gfx_panel_setup(dev);
/* Run VBIOS */
pci_dev_init(dev);
/* Post VBIOS Init */
gfx_post_vbios_init(dev);
/* Restore opregion on S3 resume */
intel_gma_restore_opregion();
}
static unsigned long
gma_write_acpi_tables(struct device *const dev,
unsigned long current,
struct acpi_rsdp *const rsdp)
{
igd_opregion_t *opregion = (igd_opregion_t *)current;
global_nvs_t *gnvs;
if (intel_gma_init_igd_opregion(opregion) != CB_SUCCESS)
return current;
current += sizeof(igd_opregion_t);
/* GNVS has been already set up */
gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS);
if (gnvs) {
/* IGD OpRegion Base Address */
gma_set_gnvs_aslb(gnvs, (uintptr_t)opregion);
} else {
printk(BIOS_ERR, "Error: GNVS table not found.\n");
}
current = acpi_align_current(current);
return current;
}
static struct device_operations gfx_device_ops = {
.read_resources = pci_dev_read_resources,
.set_resources = pci_dev_set_resources,
.enable_resources = pci_dev_enable_resources,
.init = gfx_init,
.ops_pci = &soc_pci_ops,
.write_acpi_tables = gma_write_acpi_tables,
};
static const struct pci_driver gfx_driver __pci_driver = {
.ops = &gfx_device_ops,
.vendor = PCI_VENDOR_ID_INTEL,
.device = GFX_DEVID,
};