2012-04-10 21:17:01 -07:00
/*
* Copyright © 2012 Intel Corporation
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE .
*
* Authors :
* Ben Widawsky < ben @ bwidawsk . net >
*
*/
# include <linux/device.h>
# include <linux/module.h>
# include <linux/stat.h>
# include <linux/sysfs.h>
2012-05-25 16:56:25 -07:00
# include "intel_drv.h"
2012-04-10 21:17:01 -07:00
# include "i915_drv.h"
2012-07-01 03:45:07 +00:00
# ifdef CONFIG_PM
2012-04-10 21:17:01 -07:00
static u32 calc_residency ( struct drm_device * dev , const u32 reg )
{
struct drm_i915_private * dev_priv = dev - > dev_private ;
u64 raw_time ; /* 32b value may overflow during fixed point math */
if ( ! intel_enable_rc6 ( dev ) )
return 0 ;
2012-04-20 11:50:01 -07:00
raw_time = I915_READ ( reg ) * 128ULL ;
return DIV_ROUND_UP_ULL ( raw_time , 100000 ) ;
2012-04-10 21:17:01 -07:00
}
static ssize_t
2012-09-07 19:43:38 -07:00
show_rc6_mask ( struct device * kdev , struct device_attribute * attr , char * buf )
2012-04-10 21:17:01 -07:00
{
2012-09-07 19:43:38 -07:00
struct drm_minor * dminor = container_of ( kdev , struct drm_minor , kdev ) ;
2013-02-14 10:42:11 +02:00
return snprintf ( buf , PAGE_SIZE , " %x \n " , intel_enable_rc6 ( dminor - > dev ) ) ;
2012-04-10 21:17:01 -07:00
}
static ssize_t
2012-09-07 19:43:38 -07:00
show_rc6_ms ( struct device * kdev , struct device_attribute * attr , char * buf )
2012-04-10 21:17:01 -07:00
{
2012-09-07 19:43:38 -07:00
struct drm_minor * dminor = container_of ( kdev , struct drm_minor , kdev ) ;
2012-04-10 21:17:01 -07:00
u32 rc6_residency = calc_residency ( dminor - > dev , GEN6_GT_GFX_RC6 ) ;
2013-02-14 10:42:11 +02:00
return snprintf ( buf , PAGE_SIZE , " %u \n " , rc6_residency ) ;
2012-04-10 21:17:01 -07:00
}
static ssize_t
2012-09-07 19:43:38 -07:00
show_rc6p_ms ( struct device * kdev , struct device_attribute * attr , char * buf )
2012-04-10 21:17:01 -07:00
{
2012-09-07 19:43:38 -07:00
struct drm_minor * dminor = container_of ( kdev , struct drm_minor , kdev ) ;
2012-04-10 21:17:01 -07:00
u32 rc6p_residency = calc_residency ( dminor - > dev , GEN6_GT_GFX_RC6p ) ;
2013-02-14 10:42:11 +02:00
return snprintf ( buf , PAGE_SIZE , " %u \n " , rc6p_residency ) ;
2012-04-10 21:17:01 -07:00
}
static ssize_t
2012-09-07 19:43:38 -07:00
show_rc6pp_ms ( struct device * kdev , struct device_attribute * attr , char * buf )
2012-04-10 21:17:01 -07:00
{
2012-09-07 19:43:38 -07:00
struct drm_minor * dminor = container_of ( kdev , struct drm_minor , kdev ) ;
2012-04-10 21:17:01 -07:00
u32 rc6pp_residency = calc_residency ( dminor - > dev , GEN6_GT_GFX_RC6pp ) ;
2013-02-14 10:42:11 +02:00
return snprintf ( buf , PAGE_SIZE , " %u \n " , rc6pp_residency ) ;
2012-04-10 21:17:01 -07:00
}
static DEVICE_ATTR ( rc6_enable , S_IRUGO , show_rc6_mask , NULL ) ;
static DEVICE_ATTR ( rc6_residency_ms , S_IRUGO , show_rc6_ms , NULL ) ;
static DEVICE_ATTR ( rc6p_residency_ms , S_IRUGO , show_rc6p_ms , NULL ) ;
static DEVICE_ATTR ( rc6pp_residency_ms , S_IRUGO , show_rc6pp_ms , NULL ) ;
static struct attribute * rc6_attrs [ ] = {
& dev_attr_rc6_enable . attr ,
& dev_attr_rc6_residency_ms . attr ,
& dev_attr_rc6p_residency_ms . attr ,
& dev_attr_rc6pp_residency_ms . attr ,
NULL
} ;
static struct attribute_group rc6_attr_group = {
. name = power_group_name ,
. attrs = rc6_attrs
} ;
2012-09-02 00:24:40 -07:00
# endif
2012-04-10 21:17:01 -07:00
2012-05-25 16:56:25 -07:00
static int l3_access_valid ( struct drm_device * dev , loff_t offset )
{
2012-12-05 09:52:14 +01:00
if ( ! HAS_L3_GPU_CACHE ( dev ) )
2012-05-25 16:56:25 -07:00
return - EPERM ;
if ( offset % 4 ! = 0 )
return - EINVAL ;
if ( offset > = GEN7_L3LOG_SIZE )
return - ENXIO ;
return 0 ;
}
static ssize_t
i915_l3_read ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * attr , char * buf ,
loff_t offset , size_t count )
{
struct device * dev = container_of ( kobj , struct device , kobj ) ;
struct drm_minor * dminor = container_of ( dev , struct drm_minor , kdev ) ;
struct drm_device * drm_dev = dminor - > dev ;
struct drm_i915_private * dev_priv = drm_dev - > dev_private ;
uint32_t misccpctl ;
int i , ret ;
ret = l3_access_valid ( drm_dev , offset ) ;
if ( ret )
return ret ;
ret = i915_mutex_lock_interruptible ( drm_dev ) ;
if ( ret )
return ret ;
misccpctl = I915_READ ( GEN7_MISCCPCTL ) ;
I915_WRITE ( GEN7_MISCCPCTL , misccpctl & ~ GEN7_DOP_CLOCK_GATE_ENABLE ) ;
for ( i = offset ; count > = 4 & & i < GEN7_L3LOG_SIZE ; i + = 4 , count - = 4 )
* ( ( uint32_t * ) ( & buf [ i ] ) ) = I915_READ ( GEN7_L3LOG_BASE + i ) ;
I915_WRITE ( GEN7_MISCCPCTL , misccpctl ) ;
mutex_unlock ( & drm_dev - > struct_mutex ) ;
return i - offset ;
}
static ssize_t
i915_l3_write ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * attr , char * buf ,
loff_t offset , size_t count )
{
struct device * dev = container_of ( kobj , struct device , kobj ) ;
struct drm_minor * dminor = container_of ( dev , struct drm_minor , kdev ) ;
struct drm_device * drm_dev = dminor - > dev ;
struct drm_i915_private * dev_priv = drm_dev - > dev_private ;
u32 * temp = NULL ; /* Just here to make handling failures easy */
int ret ;
ret = l3_access_valid ( drm_dev , offset ) ;
if ( ret )
return ret ;
ret = i915_mutex_lock_interruptible ( drm_dev ) ;
if ( ret )
return ret ;
2012-11-02 19:55:07 +01:00
if ( ! dev_priv - > l3_parity . remap_info ) {
2012-05-25 16:56:25 -07:00
temp = kzalloc ( GEN7_L3LOG_SIZE , GFP_KERNEL ) ;
if ( ! temp ) {
mutex_unlock ( & drm_dev - > struct_mutex ) ;
return - ENOMEM ;
}
}
ret = i915_gpu_idle ( drm_dev ) ;
if ( ret ) {
kfree ( temp ) ;
mutex_unlock ( & drm_dev - > struct_mutex ) ;
return ret ;
}
/* TODO: Ideally we really want a GPU reset here to make sure errors
* aren ' t propagated . Since I cannot find a stable way to reset the GPU
* at this point it is left as a TODO .
*/
if ( temp )
2012-11-02 19:55:07 +01:00
dev_priv - > l3_parity . remap_info = temp ;
2012-05-25 16:56:25 -07:00
2012-11-02 19:55:07 +01:00
memcpy ( dev_priv - > l3_parity . remap_info + ( offset / 4 ) ,
2012-05-25 16:56:25 -07:00
buf + ( offset / 4 ) ,
count ) ;
i915_gem_l3_remap ( drm_dev ) ;
mutex_unlock ( & drm_dev - > struct_mutex ) ;
return count ;
}
static struct bin_attribute dpf_attrs = {
. attr = { . name = " l3_parity " , . mode = ( S_IRUSR | S_IWUSR ) } ,
. size = GEN7_L3LOG_SIZE ,
. read = i915_l3_read ,
. write = i915_l3_write ,
. mmap = NULL
} ;
2012-09-07 19:43:40 -07:00
static ssize_t gt_cur_freq_mhz_show ( struct device * kdev ,
struct device_attribute * attr , char * buf )
{
struct drm_minor * minor = container_of ( kdev , struct drm_minor , kdev ) ;
struct drm_device * dev = minor - > dev ;
struct drm_i915_private * dev_priv = dev - > dev_private ;
int ret ;
2012-11-02 11:14:01 -07:00
mutex_lock ( & dev_priv - > rps . hw_lock ) ;
2012-09-07 19:43:40 -07:00
ret = dev_priv - > rps . cur_delay * GT_FREQUENCY_MULTIPLIER ;
2012-11-02 11:14:01 -07:00
mutex_unlock ( & dev_priv - > rps . hw_lock ) ;
2012-09-07 19:43:40 -07:00
2013-02-14 10:42:11 +02:00
return snprintf ( buf , PAGE_SIZE , " %d \n " , ret ) ;
2012-09-07 19:43:40 -07:00
}
static ssize_t gt_max_freq_mhz_show ( struct device * kdev , struct device_attribute * attr , char * buf )
{
struct drm_minor * minor = container_of ( kdev , struct drm_minor , kdev ) ;
struct drm_device * dev = minor - > dev ;
struct drm_i915_private * dev_priv = dev - > dev_private ;
int ret ;
2012-11-02 11:14:01 -07:00
mutex_lock ( & dev_priv - > rps . hw_lock ) ;
2012-09-07 19:43:40 -07:00
ret = dev_priv - > rps . max_delay * GT_FREQUENCY_MULTIPLIER ;
2012-11-02 11:14:01 -07:00
mutex_unlock ( & dev_priv - > rps . hw_lock ) ;
2012-09-07 19:43:40 -07:00
2013-02-14 10:42:11 +02:00
return snprintf ( buf , PAGE_SIZE , " %d \n " , ret ) ;
2012-09-07 19:43:40 -07:00
}
2012-09-12 18:12:07 -07:00
static ssize_t gt_max_freq_mhz_store ( struct device * kdev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct drm_minor * minor = container_of ( kdev , struct drm_minor , kdev ) ;
struct drm_device * dev = minor - > dev ;
struct drm_i915_private * dev_priv = dev - > dev_private ;
u32 val , rp_state_cap , hw_max , hw_min ;
ssize_t ret ;
ret = kstrtou32 ( buf , 0 , & val ) ;
if ( ret )
return ret ;
val / = GT_FREQUENCY_MULTIPLIER ;
2012-11-02 11:14:01 -07:00
mutex_lock ( & dev_priv - > rps . hw_lock ) ;
2012-09-12 18:12:07 -07:00
rp_state_cap = I915_READ ( GEN6_RP_STATE_CAP ) ;
hw_max = ( rp_state_cap & 0xff ) ;
hw_min = ( ( rp_state_cap & 0xff0000 ) > > 16 ) ;
if ( val < hw_min | | val > hw_max | | val < dev_priv - > rps . min_delay ) {
2012-11-02 11:14:01 -07:00
mutex_unlock ( & dev_priv - > rps . hw_lock ) ;
2012-09-12 18:12:07 -07:00
return - EINVAL ;
}
if ( dev_priv - > rps . cur_delay > val )
gen6_set_rps ( dev_priv - > dev , val ) ;
dev_priv - > rps . max_delay = val ;
2012-11-02 11:14:01 -07:00
mutex_unlock ( & dev_priv - > rps . hw_lock ) ;
2012-09-12 18:12:07 -07:00
return count ;
}
2012-09-07 19:43:40 -07:00
static ssize_t gt_min_freq_mhz_show ( struct device * kdev , struct device_attribute * attr , char * buf )
{
struct drm_minor * minor = container_of ( kdev , struct drm_minor , kdev ) ;
struct drm_device * dev = minor - > dev ;
struct drm_i915_private * dev_priv = dev - > dev_private ;
int ret ;
2012-11-02 11:14:01 -07:00
mutex_lock ( & dev_priv - > rps . hw_lock ) ;
2012-09-07 19:43:40 -07:00
ret = dev_priv - > rps . min_delay * GT_FREQUENCY_MULTIPLIER ;
2012-11-02 11:14:01 -07:00
mutex_unlock ( & dev_priv - > rps . hw_lock ) ;
2012-09-07 19:43:40 -07:00
2013-02-14 10:42:11 +02:00
return snprintf ( buf , PAGE_SIZE , " %d \n " , ret ) ;
2012-09-07 19:43:40 -07:00
}
2012-09-12 18:12:07 -07:00
static ssize_t gt_min_freq_mhz_store ( struct device * kdev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct drm_minor * minor = container_of ( kdev , struct drm_minor , kdev ) ;
struct drm_device * dev = minor - > dev ;
struct drm_i915_private * dev_priv = dev - > dev_private ;
u32 val , rp_state_cap , hw_max , hw_min ;
ssize_t ret ;
ret = kstrtou32 ( buf , 0 , & val ) ;
if ( ret )
return ret ;
val / = GT_FREQUENCY_MULTIPLIER ;
2012-11-02 11:14:01 -07:00
mutex_lock ( & dev_priv - > rps . hw_lock ) ;
2012-09-12 18:12:07 -07:00
rp_state_cap = I915_READ ( GEN6_RP_STATE_CAP ) ;
hw_max = ( rp_state_cap & 0xff ) ;
hw_min = ( ( rp_state_cap & 0xff0000 ) > > 16 ) ;
if ( val < hw_min | | val > hw_max | | val > dev_priv - > rps . max_delay ) {
2012-11-02 11:14:01 -07:00
mutex_unlock ( & dev_priv - > rps . hw_lock ) ;
2012-09-12 18:12:07 -07:00
return - EINVAL ;
}
if ( dev_priv - > rps . cur_delay < val )
gen6_set_rps ( dev_priv - > dev , val ) ;
dev_priv - > rps . min_delay = val ;
2012-11-02 11:14:01 -07:00
mutex_unlock ( & dev_priv - > rps . hw_lock ) ;
2012-09-12 18:12:07 -07:00
return count ;
}
2012-09-07 19:43:40 -07:00
static DEVICE_ATTR ( gt_cur_freq_mhz , S_IRUGO , gt_cur_freq_mhz_show , NULL ) ;
2012-09-12 18:12:07 -07:00
static DEVICE_ATTR ( gt_max_freq_mhz , S_IRUGO | S_IWUSR , gt_max_freq_mhz_show , gt_max_freq_mhz_store ) ;
static DEVICE_ATTR ( gt_min_freq_mhz , S_IRUGO | S_IWUSR , gt_min_freq_mhz_show , gt_min_freq_mhz_store ) ;
2012-09-07 19:43:40 -07:00
2012-09-07 19:43:44 -07:00
static ssize_t gt_rp_mhz_show ( struct device * kdev , struct device_attribute * attr , char * buf ) ;
static DEVICE_ATTR ( gt_RP0_freq_mhz , S_IRUGO , gt_rp_mhz_show , NULL ) ;
static DEVICE_ATTR ( gt_RP1_freq_mhz , S_IRUGO , gt_rp_mhz_show , NULL ) ;
static DEVICE_ATTR ( gt_RPn_freq_mhz , S_IRUGO , gt_rp_mhz_show , NULL ) ;
/* For now we have a static number of RP states */
static ssize_t gt_rp_mhz_show ( struct device * kdev , struct device_attribute * attr , char * buf )
{
struct drm_minor * minor = container_of ( kdev , struct drm_minor , kdev ) ;
struct drm_device * dev = minor - > dev ;
struct drm_i915_private * dev_priv = dev - > dev_private ;
u32 val , rp_state_cap ;
ssize_t ret ;
ret = mutex_lock_interruptible ( & dev - > struct_mutex ) ;
if ( ret )
return ret ;
rp_state_cap = I915_READ ( GEN6_RP_STATE_CAP ) ;
mutex_unlock ( & dev - > struct_mutex ) ;
if ( attr = = & dev_attr_gt_RP0_freq_mhz ) {
val = ( ( rp_state_cap & 0x0000ff ) > > 0 ) * GT_FREQUENCY_MULTIPLIER ;
} else if ( attr = = & dev_attr_gt_RP1_freq_mhz ) {
val = ( ( rp_state_cap & 0x00ff00 ) > > 8 ) * GT_FREQUENCY_MULTIPLIER ;
} else if ( attr = = & dev_attr_gt_RPn_freq_mhz ) {
val = ( ( rp_state_cap & 0xff0000 ) > > 16 ) * GT_FREQUENCY_MULTIPLIER ;
} else {
BUG ( ) ;
}
2013-02-14 10:42:11 +02:00
return snprintf ( buf , PAGE_SIZE , " %d \n " , val ) ;
2012-09-07 19:43:44 -07:00
}
2012-09-07 19:43:40 -07:00
static const struct attribute * gen6_attrs [ ] = {
& dev_attr_gt_cur_freq_mhz . attr ,
& dev_attr_gt_max_freq_mhz . attr ,
& dev_attr_gt_min_freq_mhz . attr ,
2012-09-07 19:43:44 -07:00
& dev_attr_gt_RP0_freq_mhz . attr ,
& dev_attr_gt_RP1_freq_mhz . attr ,
& dev_attr_gt_RPn_freq_mhz . attr ,
2012-09-07 19:43:40 -07:00
NULL ,
} ;
2012-04-10 21:17:01 -07:00
void i915_setup_sysfs ( struct drm_device * dev )
{
int ret ;
2012-09-02 00:24:40 -07:00
# ifdef CONFIG_PM
2012-05-31 14:57:43 +02:00
if ( INTEL_INFO ( dev ) - > gen > = 6 ) {
ret = sysfs_merge_group ( & dev - > primary - > kdev . kobj ,
& rc6_attr_group ) ;
if ( ret )
DRM_ERROR ( " RC6 residency sysfs setup failed \n " ) ;
}
2012-09-02 00:24:40 -07:00
# endif
2012-07-24 20:47:31 -07:00
if ( HAS_L3_GPU_CACHE ( dev ) ) {
2012-05-31 14:57:43 +02:00
ret = device_create_bin_file ( & dev - > primary - > kdev , & dpf_attrs ) ;
if ( ret )
DRM_ERROR ( " l3 parity sysfs setup failed \n " ) ;
}
2012-09-07 19:43:40 -07:00
if ( INTEL_INFO ( dev ) - > gen > = 6 ) {
ret = sysfs_create_files ( & dev - > primary - > kdev . kobj , gen6_attrs ) ;
if ( ret )
DRM_ERROR ( " gen6 sysfs setup failed \n " ) ;
}
2012-04-10 21:17:01 -07:00
}
void i915_teardown_sysfs ( struct drm_device * dev )
{
2012-09-07 19:43:40 -07:00
sysfs_remove_files ( & dev - > primary - > kdev . kobj , gen6_attrs ) ;
2012-05-25 16:56:25 -07:00
device_remove_bin_file ( & dev - > primary - > kdev , & dpf_attrs ) ;
2012-09-19 10:50:19 -07:00
# ifdef CONFIG_PM
2012-04-10 21:17:01 -07:00
sysfs_unmerge_group ( & dev - > primary - > kdev . kobj , & rc6_attr_group ) ;
2012-09-19 10:50:19 -07:00
# endif
2012-04-10 21:17:01 -07:00
}