2022-07-04 15:17:28 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( c ) 2014 - 2018 , The Linux Foundation . All rights reserved .
* Copyright ( C ) 2021 - 2022 Linaro Ltd
* Author : Krzysztof Kozlowski < krzysztof . kozlowski @ linaro . org > , based on
* previous work of Thara Gopinath and msm - 4.9 downstream sources .
*/
# include <linux/interconnect.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/pm_opp.h>
# include <linux/sizes.h>
/*
* The BWMON samples data throughput within ' sample_ms ' time . With three
* configurable thresholds ( Low , Medium and High ) gives four windows ( called
* zones ) of current bandwidth :
*
* Zone 0 : byte count < THRES_LO
* Zone 1 : THRES_LO < byte count < THRES_MED
* Zone 2 : THRES_MED < byte count < THRES_HIGH
* Zone 3 : THRES_HIGH < byte count
*
* Zones 0 and 2 are not used by this driver .
*/
/* Internal sampling clock frequency */
# define HW_TIMER_HZ 19200000
# define BWMON_GLOBAL_IRQ_CLEAR 0x8
# define BWMON_GLOBAL_IRQ_ENABLE 0xc
# define BWMON_GLOBAL_IRQ_ENABLE_ENABLE BIT(0)
# define BWMON_IRQ_STATUS 0x100
# define BWMON_IRQ_STATUS_ZONE_SHIFT 4
# define BWMON_IRQ_CLEAR 0x108
# define BWMON_IRQ_ENABLE 0x10c
# define BWMON_IRQ_ENABLE_ZONE1_SHIFT 5
# define BWMON_IRQ_ENABLE_ZONE2_SHIFT 6
# define BWMON_IRQ_ENABLE_ZONE3_SHIFT 7
# define BWMON_IRQ_ENABLE_MASK (BIT(BWMON_IRQ_ENABLE_ZONE1_SHIFT) | \
BIT ( BWMON_IRQ_ENABLE_ZONE3_SHIFT ) )
# define BWMON_ENABLE 0x2a0
# define BWMON_ENABLE_ENABLE BIT(0)
# define BWMON_CLEAR 0x2a4
# define BWMON_CLEAR_CLEAR BIT(0)
2022-07-28 14:37:42 +03:00
# define BWMON_CLEAR_CLEAR_ALL BIT(1)
2022-07-04 15:17:28 +03:00
# define BWMON_SAMPLE_WINDOW 0x2a8
# define BWMON_THRESHOLD_HIGH 0x2ac
# define BWMON_THRESHOLD_MED 0x2b0
# define BWMON_THRESHOLD_LOW 0x2b4
# define BWMON_ZONE_ACTIONS 0x2b8
/*
* Actions to perform on some zone ' z ' when current zone hits the threshold :
* Increment counter of zone ' z '
*/
# define BWMON_ZONE_ACTIONS_INCREMENT(z) (0x2 << ((z) * 2))
/* Clear counter of zone 'z' */
# define BWMON_ZONE_ACTIONS_CLEAR(z) (0x1 << ((z) * 2))
/* Zone 0 threshold hit: Clear zone count */
# define BWMON_ZONE_ACTIONS_ZONE0 (BWMON_ZONE_ACTIONS_CLEAR(0))
/* Zone 1 threshold hit: Increment zone count & clear lower zones */
# define BWMON_ZONE_ACTIONS_ZONE1 (BWMON_ZONE_ACTIONS_INCREMENT(1) | \
BWMON_ZONE_ACTIONS_CLEAR ( 0 ) )
/* Zone 2 threshold hit: Increment zone count & clear lower zones */
# define BWMON_ZONE_ACTIONS_ZONE2 (BWMON_ZONE_ACTIONS_INCREMENT(2) | \
BWMON_ZONE_ACTIONS_CLEAR ( 1 ) | \
BWMON_ZONE_ACTIONS_CLEAR ( 0 ) )
/* Zone 3 threshold hit: Increment zone count & clear lower zones */
# define BWMON_ZONE_ACTIONS_ZONE3 (BWMON_ZONE_ACTIONS_INCREMENT(3) | \
BWMON_ZONE_ACTIONS_CLEAR ( 2 ) | \
BWMON_ZONE_ACTIONS_CLEAR ( 1 ) | \
BWMON_ZONE_ACTIONS_CLEAR ( 0 ) )
/* Value for BWMON_ZONE_ACTIONS */
# define BWMON_ZONE_ACTIONS_DEFAULT (BWMON_ZONE_ACTIONS_ZONE0 | \
BWMON_ZONE_ACTIONS_ZONE1 < < 8 | \
BWMON_ZONE_ACTIONS_ZONE2 < < 16 | \
BWMON_ZONE_ACTIONS_ZONE3 < < 24 )
/*
* There is no clear documentation / explanation of BWMON_THRESHOLD_COUNT
* register . Based on observations , this is number of times one threshold has to
* be reached , to trigger interrupt in given zone .
*
* 0xff are maximum values meant to ignore the zones 0 and 2.
*/
# define BWMON_THRESHOLD_COUNT 0x2bc
# define BWMON_THRESHOLD_COUNT_ZONE1_SHIFT 8
# define BWMON_THRESHOLD_COUNT_ZONE2_SHIFT 16
# define BWMON_THRESHOLD_COUNT_ZONE3_SHIFT 24
# define BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT 0xff
# define BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT 0xff
/* BWMONv4 count registers use count unit of 64 kB */
# define BWMON_COUNT_UNIT_KB 64
# define BWMON_ZONE_MAX(zone) (0x2e0 + 4 * (zone))
struct icc_bwmon_data {
unsigned int sample_ms ;
unsigned int default_highbw_kbps ;
unsigned int default_medbw_kbps ;
unsigned int default_lowbw_kbps ;
u8 zone1_thres_count ;
u8 zone3_thres_count ;
} ;
struct icc_bwmon {
struct device * dev ;
2022-07-28 14:37:41 +03:00
const struct icc_bwmon_data * data ;
2022-07-04 15:17:28 +03:00
void __iomem * base ;
int irq ;
unsigned int max_bw_kbps ;
unsigned int min_bw_kbps ;
unsigned int target_kbps ;
unsigned int current_kbps ;
} ;
2022-07-28 14:37:42 +03:00
static void bwmon_clear_counters ( struct icc_bwmon * bwmon , bool clear_all )
2022-07-04 15:17:28 +03:00
{
2022-07-28 14:37:42 +03:00
unsigned int val = BWMON_CLEAR_CLEAR ;
if ( clear_all )
val | = BWMON_CLEAR_CLEAR_ALL ;
2022-07-04 15:17:28 +03:00
/*
* Clear counters . The order and barriers are
* important . Quoting downstream Qualcomm msm - 4.9 tree :
*
* The counter clear and IRQ clear bits are not in the same 4 KB
* region . So , we need to make sure the counter clear is completed
* before we try to clear the IRQ or do any other counter operations .
*/
2022-07-28 14:37:42 +03:00
writel ( val , bwmon - > base + BWMON_CLEAR ) ;
2022-07-04 15:17:28 +03:00
}
static void bwmon_clear_irq ( struct icc_bwmon * bwmon )
{
/*
* Clear zone and global interrupts . The order and barriers are
* important . Quoting downstream Qualcomm msm - 4.9 tree :
*
* Synchronize the local interrupt clear in mon_irq_clear ( )
* with the global interrupt clear here . Otherwise , the CPU
* may reorder the two writes and clear the global interrupt
* before the local interrupt , causing the global interrupt
* to be retriggered by the local interrupt still being high .
*
* Similarly , because the global registers are in a different
* region than the local registers , we need to ensure any register
* writes to enable the monitor after this call are ordered with the
* clearing here so that local writes don ' t happen before the
* interrupt is cleared .
*/
writel ( BWMON_IRQ_ENABLE_MASK , bwmon - > base + BWMON_IRQ_CLEAR ) ;
2022-07-28 14:37:39 +03:00
writel ( BWMON_GLOBAL_IRQ_ENABLE_ENABLE ,
bwmon - > base + BWMON_GLOBAL_IRQ_CLEAR ) ;
2022-07-04 15:17:28 +03:00
}
static void bwmon_disable ( struct icc_bwmon * bwmon )
{
/* Disable interrupts. Strict ordering, see bwmon_clear_irq(). */
writel ( 0x0 , bwmon - > base + BWMON_GLOBAL_IRQ_ENABLE ) ;
writel ( 0x0 , bwmon - > base + BWMON_IRQ_ENABLE ) ;
/*
* Disable bwmon . Must happen before bwmon_clear_irq ( ) to avoid spurious
* IRQ .
*/
writel ( 0x0 , bwmon - > base + BWMON_ENABLE ) ;
}
static void bwmon_enable ( struct icc_bwmon * bwmon , unsigned int irq_enable )
{
/* Enable interrupts */
writel ( BWMON_GLOBAL_IRQ_ENABLE_ENABLE ,
bwmon - > base + BWMON_GLOBAL_IRQ_ENABLE ) ;
writel ( irq_enable , bwmon - > base + BWMON_IRQ_ENABLE ) ;
/* Enable bwmon */
writel ( BWMON_ENABLE_ENABLE , bwmon - > base + BWMON_ENABLE ) ;
}
static unsigned int bwmon_kbps_to_count ( unsigned int kbps )
{
return kbps / BWMON_COUNT_UNIT_KB ;
}
static void bwmon_set_threshold ( struct icc_bwmon * bwmon , unsigned int reg ,
unsigned int kbps )
{
unsigned int thres ;
2022-07-28 14:37:41 +03:00
thres = mult_frac ( bwmon_kbps_to_count ( kbps ) , bwmon - > data - > sample_ms ,
2022-07-04 15:17:28 +03:00
MSEC_PER_SEC ) ;
writel_relaxed ( thres , bwmon - > base + reg ) ;
}
2022-07-28 14:37:41 +03:00
static void bwmon_start ( struct icc_bwmon * bwmon )
2022-07-04 15:17:28 +03:00
{
2022-07-28 14:37:41 +03:00
const struct icc_bwmon_data * data = bwmon - > data ;
2022-07-04 15:17:28 +03:00
unsigned int thres_count ;
int window ;
2022-07-28 14:37:42 +03:00
bwmon_clear_counters ( bwmon , true ) ;
2022-07-04 15:17:28 +03:00
2022-07-28 14:37:41 +03:00
window = mult_frac ( bwmon - > data - > sample_ms , HW_TIMER_HZ , MSEC_PER_SEC ) ;
2022-07-04 15:17:28 +03:00
/* Maximum sampling window: 0xfffff */
writel_relaxed ( window , bwmon - > base + BWMON_SAMPLE_WINDOW ) ;
bwmon_set_threshold ( bwmon , BWMON_THRESHOLD_HIGH ,
data - > default_highbw_kbps ) ;
bwmon_set_threshold ( bwmon , BWMON_THRESHOLD_MED ,
data - > default_medbw_kbps ) ;
bwmon_set_threshold ( bwmon , BWMON_THRESHOLD_LOW ,
data - > default_lowbw_kbps ) ;
thres_count = data - > zone3_thres_count < < BWMON_THRESHOLD_COUNT_ZONE3_SHIFT |
BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT < < BWMON_THRESHOLD_COUNT_ZONE2_SHIFT |
data - > zone1_thres_count < < BWMON_THRESHOLD_COUNT_ZONE1_SHIFT |
BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT ;
writel_relaxed ( thres_count , bwmon - > base + BWMON_THRESHOLD_COUNT ) ;
writel_relaxed ( BWMON_ZONE_ACTIONS_DEFAULT ,
bwmon - > base + BWMON_ZONE_ACTIONS ) ;
/* Write barriers in bwmon_clear_irq() */
bwmon_clear_irq ( bwmon ) ;
bwmon_enable ( bwmon , BWMON_IRQ_ENABLE_MASK ) ;
}
static irqreturn_t bwmon_intr ( int irq , void * dev_id )
{
struct icc_bwmon * bwmon = dev_id ;
unsigned int status , max ;
int zone ;
status = readl ( bwmon - > base + BWMON_IRQ_STATUS ) ;
status & = BWMON_IRQ_ENABLE_MASK ;
if ( ! status ) {
/*
* Only zone 1 and zone 3 interrupts are enabled but zone 2
* threshold could be hit and trigger interrupt even if not
* enabled .
* Such spurious interrupt might come with valuable max count or
* not , so solution would be to always check all
* BWMON_ZONE_MAX ( ) registers to find the highest value .
* Such case is currently ignored .
*/
return IRQ_NONE ;
}
bwmon_disable ( bwmon ) ;
zone = get_bitmask_order ( status > > BWMON_IRQ_STATUS_ZONE_SHIFT ) - 1 ;
/*
* Zone max bytes count register returns count units within sampling
* window . Downstream kernel for BWMONv4 ( called BWMON type 2 in
* downstream ) always increments the max bytes count by one .
*/
max = readl ( bwmon - > base + BWMON_ZONE_MAX ( zone ) ) + 1 ;
max * = BWMON_COUNT_UNIT_KB ;
2022-07-28 14:37:41 +03:00
bwmon - > target_kbps = mult_frac ( max , MSEC_PER_SEC , bwmon - > data - > sample_ms ) ;
2022-07-04 15:17:28 +03:00
return IRQ_WAKE_THREAD ;
}
static irqreturn_t bwmon_intr_thread ( int irq , void * dev_id )
{
struct icc_bwmon * bwmon = dev_id ;
unsigned int irq_enable = 0 ;
struct dev_pm_opp * opp , * target_opp ;
unsigned int bw_kbps , up_kbps , down_kbps ;
bw_kbps = bwmon - > target_kbps ;
target_opp = dev_pm_opp_find_bw_ceil ( bwmon - > dev , & bw_kbps , 0 ) ;
if ( IS_ERR ( target_opp ) & & PTR_ERR ( target_opp ) = = - ERANGE )
target_opp = dev_pm_opp_find_bw_floor ( bwmon - > dev , & bw_kbps , 0 ) ;
bwmon - > target_kbps = bw_kbps ;
bw_kbps - - ;
opp = dev_pm_opp_find_bw_floor ( bwmon - > dev , & bw_kbps , 0 ) ;
if ( IS_ERR ( opp ) & & PTR_ERR ( opp ) = = - ERANGE )
down_kbps = bwmon - > target_kbps ;
else
down_kbps = bw_kbps ;
up_kbps = bwmon - > target_kbps + 1 ;
if ( bwmon - > target_kbps > = bwmon - > max_bw_kbps )
irq_enable = BIT ( BWMON_IRQ_ENABLE_ZONE1_SHIFT ) ;
else if ( bwmon - > target_kbps < = bwmon - > min_bw_kbps )
irq_enable = BIT ( BWMON_IRQ_ENABLE_ZONE3_SHIFT ) ;
else
irq_enable = BWMON_IRQ_ENABLE_MASK ;
bwmon_set_threshold ( bwmon , BWMON_THRESHOLD_HIGH , up_kbps ) ;
bwmon_set_threshold ( bwmon , BWMON_THRESHOLD_MED , down_kbps ) ;
/* Write barriers in bwmon_clear_counters() */
2022-07-28 14:37:42 +03:00
bwmon_clear_counters ( bwmon , false ) ;
2022-07-04 15:17:28 +03:00
bwmon_clear_irq ( bwmon ) ;
bwmon_enable ( bwmon , irq_enable ) ;
if ( bwmon - > target_kbps = = bwmon - > current_kbps )
goto out ;
dev_pm_opp_set_opp ( bwmon - > dev , target_opp ) ;
bwmon - > current_kbps = bwmon - > target_kbps ;
out :
dev_pm_opp_put ( target_opp ) ;
if ( ! IS_ERR ( opp ) )
dev_pm_opp_put ( opp ) ;
return IRQ_HANDLED ;
}
static int bwmon_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct dev_pm_opp * opp ;
struct icc_bwmon * bwmon ;
int ret ;
bwmon = devm_kzalloc ( dev , sizeof ( * bwmon ) , GFP_KERNEL ) ;
if ( ! bwmon )
return - ENOMEM ;
2022-07-28 14:37:41 +03:00
bwmon - > data = of_device_get_match_data ( dev ) ;
2022-07-04 15:17:28 +03:00
bwmon - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( bwmon - > base ) ) {
dev_err ( dev , " failed to map bwmon registers \n " ) ;
return PTR_ERR ( bwmon - > base ) ;
}
bwmon - > irq = platform_get_irq ( pdev , 0 ) ;
2022-07-14 10:55:32 +03:00
if ( bwmon - > irq < 0 )
2022-07-04 15:17:28 +03:00
return bwmon - > irq ;
ret = devm_pm_opp_of_add_table ( dev ) ;
if ( ret )
return dev_err_probe ( dev , ret , " failed to add OPP table \n " ) ;
bwmon - > max_bw_kbps = UINT_MAX ;
opp = dev_pm_opp_find_bw_floor ( dev , & bwmon - > max_bw_kbps , 0 ) ;
if ( IS_ERR ( opp ) )
return dev_err_probe ( dev , ret , " failed to find max peak bandwidth \n " ) ;
bwmon - > min_bw_kbps = 0 ;
opp = dev_pm_opp_find_bw_ceil ( dev , & bwmon - > min_bw_kbps , 0 ) ;
if ( IS_ERR ( opp ) )
return dev_err_probe ( dev , ret , " failed to find min peak bandwidth \n " ) ;
bwmon - > dev = dev ;
bwmon_disable ( bwmon ) ;
ret = devm_request_threaded_irq ( dev , bwmon - > irq , bwmon_intr ,
bwmon_intr_thread ,
IRQF_ONESHOT , dev_name ( dev ) , bwmon ) ;
if ( ret )
return dev_err_probe ( dev , ret , " failed to request IRQ \n " ) ;
platform_set_drvdata ( pdev , bwmon ) ;
2022-07-28 14:37:41 +03:00
bwmon_start ( bwmon ) ;
2022-07-04 15:17:28 +03:00
return 0 ;
}
static int bwmon_remove ( struct platform_device * pdev )
{
struct icc_bwmon * bwmon = platform_get_drvdata ( pdev ) ;
bwmon_disable ( bwmon ) ;
return 0 ;
}
/* BWMON v4 */
static const struct icc_bwmon_data msm8998_bwmon_data = {
. sample_ms = 4 ,
. default_highbw_kbps = 4800 * 1024 , /* 4.8 GBps */
. default_medbw_kbps = 512 * 1024 , /* 512 MBps */
. default_lowbw_kbps = 0 ,
. zone1_thres_count = 16 ,
. zone3_thres_count = 1 ,
} ;
static const struct of_device_id bwmon_of_match [ ] = {
{ . compatible = " qcom,msm8998-bwmon " , . data = & msm8998_bwmon_data } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , bwmon_of_match ) ;
static struct platform_driver bwmon_driver = {
. probe = bwmon_probe ,
. remove = bwmon_remove ,
. driver = {
. name = " qcom-bwmon " ,
. of_match_table = bwmon_of_match ,
} ,
} ;
module_platform_driver ( bwmon_driver ) ;
MODULE_AUTHOR ( " Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> " ) ;
MODULE_DESCRIPTION ( " QCOM BWMON driver " ) ;
MODULE_LICENSE ( " GPL " ) ;