Commit 7648fa99 authored by Jesse Barnes's avatar Jesse Barnes Committed by Eric Anholt

drm/i915: add power monitoring support

Add power monitoring support to the i915 driver for use by the IPS
driver.  Export the available power info to the IPS driver through a few
new inter-driver hooks.  When used together, the IPS driver and this
patch can significantly increase graphics performance on Ironlake class
chips.
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
[anholt: Fixed 32-bit compile.  stupid obfuscating div_u64()]
Signed-off-by: default avatarEric Anholt <eric@anholt.net>
parent 7a772c49
......@@ -491,11 +491,14 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
u16 rgvswctl = I915_READ16(MEMSWCTL);
u16 rgvstat = I915_READ16(MEMSTAT_ILK);
seq_printf(m, "Last command: 0x%01x\n", (rgvswctl >> 13) & 0x3);
seq_printf(m, "Command status: %d\n", (rgvswctl >> 12) & 1);
seq_printf(m, "P%d DELAY 0x%02x\n", (rgvswctl >> 8) & 0xf,
rgvswctl & 0x3f);
seq_printf(m, "Requested P-state: %d\n", (rgvswctl >> 8) & 0xf);
seq_printf(m, "Requested VID: %d\n", rgvswctl & 0x3f);
seq_printf(m, "Current VID: %d\n", (rgvstat & MEMSTAT_VID_MASK) >>
MEMSTAT_VID_SHIFT);
seq_printf(m, "Current P-state: %d\n",
(rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
return 0;
}
......@@ -510,7 +513,8 @@ static int i915_delayfreq_table(struct seq_file *m, void *unused)
for (i = 0; i < 16; i++) {
delayfreq = I915_READ(PXVFREQ_BASE + i * 4);
seq_printf(m, "P%02dVIDFREQ: 0x%08x\n", i, delayfreq);
seq_printf(m, "P%02dVIDFREQ: 0x%08x (VID: %d)\n", i, delayfreq,
(delayfreq & PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT);
}
return 0;
......@@ -543,6 +547,8 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
u32 rgvmodectl = I915_READ(MEMMODECTL);
u32 rstdbyctl = I915_READ(MCHBAR_RENDER_STANDBY);
u16 crstandvid = I915_READ16(CRSTANDVID);
seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ?
"yes" : "no");
......@@ -557,9 +563,13 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
rgvmodectl & MEMMODE_RCLK_GATE ? "yes" : "no");
seq_printf(m, "Starting frequency: P%d\n",
(rgvmodectl & MEMMODE_FSTART_MASK) >> MEMMODE_FSTART_SHIFT);
seq_printf(m, "Max frequency: P%d\n",
seq_printf(m, "Max P-state: P%d\n",
(rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT);
seq_printf(m, "Min frequency: P%d\n", (rgvmodectl & MEMMODE_FMIN_MASK));
seq_printf(m, "Min P-state: P%d\n", (rgvmodectl & MEMMODE_FMIN_MASK));
seq_printf(m, "RS1 VID: %d\n", (crstandvid & 0x3f));
seq_printf(m, "RS2 VID: %d\n", ((crstandvid >> 8) & 0x3f));
seq_printf(m, "Render standby enabled: %s\n",
(rstdbyctl & RCX_SW_EXIT) ? "no" : "yes");
return 0;
}
......@@ -623,6 +633,36 @@ static int i915_sr_status(struct seq_file *m, void *unused)
return 0;
}
static int i915_emon_status(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
unsigned long temp, chipset, gfx;
temp = i915_mch_val(dev_priv);
chipset = i915_chipset_val(dev_priv);
gfx = i915_gfx_val(dev_priv);
seq_printf(m, "GMCH temp: %ld\n", temp);
seq_printf(m, "Chipset power: %ld\n", chipset);
seq_printf(m, "GFX power: %ld\n", gfx);
seq_printf(m, "Total power: %ld\n", chipset + gfx);
return 0;
}
static int i915_gfxec(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
seq_printf(m, "GFXEC: %ld\n", (unsigned long)I915_READ(0x112f4));
return 0;
}
static int
i915_wedged_open(struct inode *inode,
struct file *filp)
......@@ -745,6 +785,8 @@ static struct drm_info_list i915_debugfs_list[] = {
{"i915_delayfreq_table", i915_delayfreq_table, 0},
{"i915_inttoext_table", i915_inttoext_table, 0},
{"i915_drpc_info", i915_drpc_info, 0},
{"i915_emon_status", i915_emon_status, 0},
{"i915_gfxec", i915_gfxec, 0},
{"i915_fbc_status", i915_fbc_status, 0},
{"i915_sr_status", i915_sr_status, 0},
};
......
This diff is collapsed.
......@@ -619,6 +619,18 @@ typedef struct drm_i915_private {
u8 cur_delay;
u8 min_delay;
u8 max_delay;
u8 fmax;
u8 fstart;
u64 last_count1;
unsigned long last_time1;
u64 last_count2;
struct timespec last_time2;
unsigned long gfx_power;
int c_m;
int r_t;
u8 corr;
spinlock_t *mchdev_lock;
enum no_fbc_reason no_fbc_reason;
......@@ -802,6 +814,11 @@ extern int i915_emit_box(struct drm_device *dev,
struct drm_clip_rect *boxes,
int i, int DR1, int DR4);
extern int i965_reset(struct drm_device *dev, u8 flags);
extern unsigned long i915_chipset_val(struct drm_i915_private *dev_priv);
extern unsigned long i915_mch_val(struct drm_i915_private *dev_priv);
extern unsigned long i915_gfx_val(struct drm_i915_private *dev_priv);
extern void i915_update_gfx_val(struct drm_i915_private *dev_priv);
/* i915_irq.c */
void i915_hangcheck_elapsed(unsigned long data);
......@@ -1005,7 +1022,7 @@ extern void g4x_disable_fbc(struct drm_device *dev);
extern void intel_disable_fbc(struct drm_device *dev);
extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval);
extern bool intel_fbc_enabled(struct drm_device *dev);
extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
extern void intel_detect_pch (struct drm_device *dev);
extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
......@@ -1030,6 +1047,7 @@ extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
#define I915_WRITE64(reg, val) writeq(val, dev_priv->regs + (reg))
#define I915_READ64(reg) readq(dev_priv->regs + (reg))
#define POSTING_READ(reg) (void)I915_READ(reg)
#define POSTING_READ16(reg) (void)I915_READ16(reg)
#define I915_VERBOSE 0
......
......@@ -278,10 +278,9 @@ static void i915_handle_rps_change(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
u32 busy_up, busy_down, max_avg, min_avg;
u16 rgvswctl;
u8 new_delay = dev_priv->cur_delay;
I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS) & ~MEMINT_EVAL_CHG);
I915_WRITE16(MEMINTRSTS, MEMINT_EVAL_CHG);
busy_up = I915_READ(RCPREVBSYTUPAVG);
busy_down = I915_READ(RCPREVBSYTDNAVG);
max_avg = I915_READ(RCBMAXAVG);
......@@ -300,27 +299,8 @@ static void i915_handle_rps_change(struct drm_device *dev)
new_delay = dev_priv->min_delay;
}
DRM_DEBUG("rps change requested: %d -> %d\n",
dev_priv->cur_delay, new_delay);
rgvswctl = I915_READ(MEMSWCTL);
if (rgvswctl & MEMCTL_CMD_STS) {
DRM_ERROR("gpu busy, RCS change rejected\n");
return; /* still busy with another command */
}
/* Program the new state */
rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
(new_delay << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
I915_WRITE(MEMSWCTL, rgvswctl);
POSTING_READ(MEMSWCTL);
rgvswctl |= MEMCTL_CMD_STS;
I915_WRITE(MEMSWCTL, rgvswctl);
dev_priv->cur_delay = new_delay;
DRM_DEBUG("rps changed\n");
if (ironlake_set_drps(dev, new_delay))
dev_priv->cur_delay = new_delay;
return;
}
......@@ -392,7 +372,7 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev)
}
if (de_iir & DE_PCU_EVENT) {
I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS));
I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS));
i915_handle_rps_change(dev);
}
......
......@@ -838,6 +838,12 @@
#define CLKCFG_MEM_800 (3 << 4)
#define CLKCFG_MEM_MASK (7 << 4)
#define TR1 0x11006
#define TSFS 0x11020
#define TSFS_SLOPE_MASK 0x0000ff00
#define TSFS_SLOPE_SHIFT 8
#define TSFS_INTR_MASK 0x000000ff
#define CRSTANDVID 0x11100
#define PXVFREQ_BASE 0x11110 /* P[0-15]VIDFREQ (0x1114c) (Ironlake) */
#define PXVFREQ_PX_MASK 0x7f000000
......@@ -976,6 +982,41 @@
#define MEMSTAT_SRC_CTL_STDBY 3
#define RCPREVBSYTUPAVG 0x113b8
#define RCPREVBSYTDNAVG 0x113bc
#define SDEW 0x1124c
#define CSIEW0 0x11250
#define CSIEW1 0x11254
#define CSIEW2 0x11258
#define PEW 0x1125c
#define DEW 0x11270
#define MCHAFE 0x112c0
#define CSIEC 0x112e0
#define DMIEC 0x112e4
#define DDREC 0x112e8
#define PEG0EC 0x112ec
#define PEG1EC 0x112f0
#define GFXEC 0x112f4
#define RPPREVBSYTUPAVG 0x113b8
#define RPPREVBSYTDNAVG 0x113bc
#define ECR 0x11600
#define ECR_GPFE (1<<31)
#define ECR_IMONE (1<<30)
#define ECR_CAP_MASK 0x0000001f /* Event range, 0-31 */
#define OGW0 0x11608
#define OGW1 0x1160c
#define EG0 0x11610
#define EG1 0x11614
#define EG2 0x11618
#define EG3 0x1161c
#define EG4 0x11620
#define EG5 0x11624
#define EG6 0x11628
#define EG7 0x1162c
#define PXW 0x11664
#define PXWL 0x11680
#define LCFUSE02 0x116c0
#define LCFUSE_HIV_MASK 0x000000ff
#define CSIPLL0 0x12c10
#define DDRMPLL1 0X12c20
#define PEG_BAND_GAP_DATA 0x14d68
/*
......
......@@ -4459,6 +4459,8 @@ static void intel_idle_update(struct work_struct *work)
mutex_lock(&dev->struct_mutex);
i915_update_gfx_val(dev_priv);
if (IS_I945G(dev) || IS_I945GM(dev)) {
DRM_DEBUG_DRIVER("enable memory self refresh on 945\n");
I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN_MASK | FW_BLC_SELF_EN);
......@@ -5045,10 +5047,32 @@ intel_alloc_power_context(struct drm_device *dev)
return NULL;
}
bool ironlake_set_drps(struct drm_device *dev, u8 val)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u16 rgvswctl;
rgvswctl = I915_READ16(MEMSWCTL);
if (rgvswctl & MEMCTL_CMD_STS) {
DRM_DEBUG("gpu busy, RCS change rejected\n");
return false; /* still busy with another command */
}
rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
(val << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
I915_WRITE16(MEMSWCTL, rgvswctl);
POSTING_READ16(MEMSWCTL);
rgvswctl |= MEMCTL_CMD_STS;
I915_WRITE16(MEMSWCTL, rgvswctl);
return true;
}
void ironlake_enable_drps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 rgvmodectl = I915_READ(MEMMODECTL), rgvswctl;
u32 rgvmodectl = I915_READ(MEMMODECTL);
u8 fmax, fmin, fstart, vstart;
int i = 0;
......@@ -5067,13 +5091,21 @@ void ironlake_enable_drps(struct drm_device *dev)
fmin = (rgvmodectl & MEMMODE_FMIN_MASK);
fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >>
MEMMODE_FSTART_SHIFT;
fstart = fmax;
vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >>
PXVFREQ_PX_SHIFT;
dev_priv->max_delay = fstart; /* can't go to fmax w/o IPS */
dev_priv->fmax = fstart; /* IPS callback will increase this */
dev_priv->fstart = fstart;
dev_priv->max_delay = fmax;
dev_priv->min_delay = fmin;
dev_priv->cur_delay = fstart;
DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n", fmax, fmin,
fstart);
I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN);
/*
......@@ -5095,20 +5127,19 @@ void ironlake_enable_drps(struct drm_device *dev)
}
msleep(1);
rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
(fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
I915_WRITE(MEMSWCTL, rgvswctl);
POSTING_READ(MEMSWCTL);
ironlake_set_drps(dev, fstart);
rgvswctl |= MEMCTL_CMD_STS;
I915_WRITE(MEMSWCTL, rgvswctl);
dev_priv->last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) +
I915_READ(0x112e0);
dev_priv->last_time1 = jiffies_to_msecs(jiffies);
dev_priv->last_count2 = I915_READ(0x112f4);
getrawmonotonic(&dev_priv->last_time2);
}
void ironlake_disable_drps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 rgvswctl;
u8 fstart;
u16 rgvswctl = I915_READ16(MEMSWCTL);
/* Ack interrupts, disable EFC interrupt */
I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN);
......@@ -5118,11 +5149,7 @@ void ironlake_disable_drps(struct drm_device *dev)
I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT);
/* Go back to the starting frequency */
fstart = (I915_READ(MEMMODECTL) & MEMMODE_FSTART_MASK) >>
MEMMODE_FSTART_SHIFT;
rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
(fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
I915_WRITE(MEMSWCTL, rgvswctl);
ironlake_set_drps(dev, dev_priv->fstart);
msleep(1);
rgvswctl |= MEMCTL_CMD_STS;
I915_WRITE(MEMSWCTL, rgvswctl);
......@@ -5130,6 +5157,92 @@ void ironlake_disable_drps(struct drm_device *dev)
}
static unsigned long intel_pxfreq(u32 vidfreq)
{
unsigned long freq;
int div = (vidfreq & 0x3f0000) >> 16;
int post = (vidfreq & 0x3000) >> 12;
int pre = (vidfreq & 0x7);
if (!pre)
return 0;
freq = ((div * 133333) / ((1<<post) * pre));
return freq;
}
void intel_init_emon(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 lcfuse;
u8 pxw[16];
int i;
/* Disable to program */
I915_WRITE(ECR, 0);
POSTING_READ(ECR);
/* Program energy weights for various events */
I915_WRITE(SDEW, 0x15040d00);
I915_WRITE(CSIEW0, 0x007f0000);
I915_WRITE(CSIEW1, 0x1e220004);
I915_WRITE(CSIEW2, 0x04000004);
for (i = 0; i < 5; i++)
I915_WRITE(PEW + (i * 4), 0);
for (i = 0; i < 3; i++)
I915_WRITE(DEW + (i * 4), 0);
/* Program P-state weights to account for frequency power adjustment */
for (i = 0; i < 16; i++) {
u32 pxvidfreq = I915_READ(PXVFREQ_BASE + (i * 4));
unsigned long freq = intel_pxfreq(pxvidfreq);
unsigned long vid = (pxvidfreq & PXVFREQ_PX_MASK) >>
PXVFREQ_PX_SHIFT;
unsigned long val;
val = vid * vid;
val *= (freq / 1000);
val *= 255;
val /= (127*127*900);
if (val > 0xff)
DRM_ERROR("bad pxval: %ld\n", val);
pxw[i] = val;
}
/* Render standby states get 0 weight */
pxw[14] = 0;
pxw[15] = 0;
for (i = 0; i < 4; i++) {
u32 val = (pxw[i*4] << 24) | (pxw[(i*4)+1] << 16) |
(pxw[(i*4)+2] << 8) | (pxw[(i*4)+3]);
I915_WRITE(PXW + (i * 4), val);
}
/* Adjust magic regs to magic values (more experimental results) */
I915_WRITE(OGW0, 0);
I915_WRITE(OGW1, 0);
I915_WRITE(EG0, 0x00007f00);
I915_WRITE(EG1, 0x0000000e);
I915_WRITE(EG2, 0x000e0000);
I915_WRITE(EG3, 0x68000300);
I915_WRITE(EG4, 0x42000000);
I915_WRITE(EG5, 0x00140031);
I915_WRITE(EG6, 0);
I915_WRITE(EG7, 0);
for (i = 0; i < 8; i++)
I915_WRITE(PXWL + (i * 4), 0);
/* Enable PMON + select events */
I915_WRITE(ECR, 0x80000019);
lcfuse = I915_READ(LCFUSE02);
dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK);
}
void intel_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
......@@ -5376,8 +5489,10 @@ void intel_modeset_init(struct drm_device *dev)
intel_init_clock_gating(dev);
if (IS_IRONLAKE_M(dev))
if (IS_IRONLAKE_M(dev)) {
ironlake_enable_drps(dev);
intel_init_emon(dev);
}
INIT_WORK(&dev_priv->idle_work, intel_idle_update);
setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment