162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci * Copyright (C) 2021-2022 Linaro Ltd 562306a36Sopenharmony_ci * Author: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>, based on 662306a36Sopenharmony_ci * previous work of Thara Gopinath and msm-4.9 downstream sources. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/interconnect.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/pm_opp.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci#include <linux/sizes.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * The BWMON samples data throughput within 'sample_ms' time. With three 2362306a36Sopenharmony_ci * configurable thresholds (Low, Medium and High) gives four windows (called 2462306a36Sopenharmony_ci * zones) of current bandwidth: 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Zone 0: byte count < THRES_LO 2762306a36Sopenharmony_ci * Zone 1: THRES_LO < byte count < THRES_MED 2862306a36Sopenharmony_ci * Zone 2: THRES_MED < byte count < THRES_HIGH 2962306a36Sopenharmony_ci * Zone 3: THRES_HIGH < byte count 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Zones 0 and 2 are not used by this driver. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Internal sampling clock frequency */ 3562306a36Sopenharmony_ci#define HW_TIMER_HZ 19200000 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define BWMON_V4_GLOBAL_IRQ_CLEAR 0x108 3862306a36Sopenharmony_ci#define BWMON_V4_GLOBAL_IRQ_ENABLE 0x10c 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * All values here and further are matching regmap fields, so without absolute 4162306a36Sopenharmony_ci * register offsets. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci#define BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE BIT(0) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Starting with SDM845, the BWMON4 register space has changed a bit: 4762306a36Sopenharmony_ci * the global registers were jammed into the beginning of the monitor region. 4862306a36Sopenharmony_ci * To keep the proper offsets, one would have to map <GLOBAL_BASE 0x200> and 4962306a36Sopenharmony_ci * <GLOBAL_BASE+0x100 0x300>, which is straight up wrong. 5062306a36Sopenharmony_ci * To facilitate for that, while allowing the older, arguably more proper 5162306a36Sopenharmony_ci * implementations to work, offset the global registers by -0x100 to avoid 5262306a36Sopenharmony_ci * having to map half of the global registers twice. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci#define BWMON_V4_845_OFFSET 0x100 5562306a36Sopenharmony_ci#define BWMON_V4_GLOBAL_IRQ_CLEAR_845 (BWMON_V4_GLOBAL_IRQ_CLEAR - BWMON_V4_845_OFFSET) 5662306a36Sopenharmony_ci#define BWMON_V4_GLOBAL_IRQ_ENABLE_845 (BWMON_V4_GLOBAL_IRQ_ENABLE - BWMON_V4_845_OFFSET) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define BWMON_V4_IRQ_STATUS 0x100 5962306a36Sopenharmony_ci#define BWMON_V4_IRQ_CLEAR 0x108 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define BWMON_V4_IRQ_ENABLE 0x10c 6262306a36Sopenharmony_ci#define BWMON_IRQ_ENABLE_MASK (BIT(1) | BIT(3)) 6362306a36Sopenharmony_ci#define BWMON_V5_IRQ_STATUS 0x000 6462306a36Sopenharmony_ci#define BWMON_V5_IRQ_CLEAR 0x008 6562306a36Sopenharmony_ci#define BWMON_V5_IRQ_ENABLE 0x00c 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define BWMON_V4_ENABLE 0x2a0 6862306a36Sopenharmony_ci#define BWMON_V5_ENABLE 0x010 6962306a36Sopenharmony_ci#define BWMON_ENABLE_ENABLE BIT(0) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define BWMON_V4_CLEAR 0x2a4 7262306a36Sopenharmony_ci#define BWMON_V5_CLEAR 0x014 7362306a36Sopenharmony_ci#define BWMON_CLEAR_CLEAR BIT(0) 7462306a36Sopenharmony_ci#define BWMON_CLEAR_CLEAR_ALL BIT(1) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define BWMON_V4_SAMPLE_WINDOW 0x2a8 7762306a36Sopenharmony_ci#define BWMON_V5_SAMPLE_WINDOW 0x020 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define BWMON_V4_THRESHOLD_HIGH 0x2ac 8062306a36Sopenharmony_ci#define BWMON_V4_THRESHOLD_MED 0x2b0 8162306a36Sopenharmony_ci#define BWMON_V4_THRESHOLD_LOW 0x2b4 8262306a36Sopenharmony_ci#define BWMON_V5_THRESHOLD_HIGH 0x024 8362306a36Sopenharmony_ci#define BWMON_V5_THRESHOLD_MED 0x028 8462306a36Sopenharmony_ci#define BWMON_V5_THRESHOLD_LOW 0x02c 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define BWMON_V4_ZONE_ACTIONS 0x2b8 8762306a36Sopenharmony_ci#define BWMON_V5_ZONE_ACTIONS 0x030 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * Actions to perform on some zone 'z' when current zone hits the threshold: 9062306a36Sopenharmony_ci * Increment counter of zone 'z' 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci#define BWMON_ZONE_ACTIONS_INCREMENT(z) (0x2 << ((z) * 2)) 9362306a36Sopenharmony_ci/* Clear counter of zone 'z' */ 9462306a36Sopenharmony_ci#define BWMON_ZONE_ACTIONS_CLEAR(z) (0x1 << ((z) * 2)) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* Zone 0 threshold hit: Clear zone count */ 9762306a36Sopenharmony_ci#define BWMON_ZONE_ACTIONS_ZONE0 (BWMON_ZONE_ACTIONS_CLEAR(0)) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* Zone 1 threshold hit: Increment zone count & clear lower zones */ 10062306a36Sopenharmony_ci#define BWMON_ZONE_ACTIONS_ZONE1 (BWMON_ZONE_ACTIONS_INCREMENT(1) | \ 10162306a36Sopenharmony_ci BWMON_ZONE_ACTIONS_CLEAR(0)) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* Zone 2 threshold hit: Increment zone count & clear lower zones */ 10462306a36Sopenharmony_ci#define BWMON_ZONE_ACTIONS_ZONE2 (BWMON_ZONE_ACTIONS_INCREMENT(2) | \ 10562306a36Sopenharmony_ci BWMON_ZONE_ACTIONS_CLEAR(1) | \ 10662306a36Sopenharmony_ci BWMON_ZONE_ACTIONS_CLEAR(0)) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* Zone 3 threshold hit: Increment zone count & clear lower zones */ 10962306a36Sopenharmony_ci#define BWMON_ZONE_ACTIONS_ZONE3 (BWMON_ZONE_ACTIONS_INCREMENT(3) | \ 11062306a36Sopenharmony_ci BWMON_ZONE_ACTIONS_CLEAR(2) | \ 11162306a36Sopenharmony_ci BWMON_ZONE_ACTIONS_CLEAR(1) | \ 11262306a36Sopenharmony_ci BWMON_ZONE_ACTIONS_CLEAR(0)) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * There is no clear documentation/explanation of BWMON_V4_THRESHOLD_COUNT 11662306a36Sopenharmony_ci * register. Based on observations, this is number of times one threshold has to 11762306a36Sopenharmony_ci * be reached, to trigger interrupt in given zone. 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * 0xff are maximum values meant to ignore the zones 0 and 2. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci#define BWMON_V4_THRESHOLD_COUNT 0x2bc 12262306a36Sopenharmony_ci#define BWMON_V5_THRESHOLD_COUNT 0x034 12362306a36Sopenharmony_ci#define BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT 0xff 12462306a36Sopenharmony_ci#define BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT 0xff 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define BWMON_V4_ZONE_MAX(zone) (0x2e0 + 4 * (zone)) 12762306a36Sopenharmony_ci#define BWMON_V5_ZONE_MAX(zone) (0x044 + 4 * (zone)) 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* Quirks for specific BWMON types */ 13062306a36Sopenharmony_ci#define BWMON_HAS_GLOBAL_IRQ BIT(0) 13162306a36Sopenharmony_ci#define BWMON_NEEDS_FORCE_CLEAR BIT(1) 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cienum bwmon_fields { 13462306a36Sopenharmony_ci /* Global region fields, keep them at the top */ 13562306a36Sopenharmony_ci F_GLOBAL_IRQ_CLEAR, 13662306a36Sopenharmony_ci F_GLOBAL_IRQ_ENABLE, 13762306a36Sopenharmony_ci F_NUM_GLOBAL_FIELDS, 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Monitor region fields */ 14062306a36Sopenharmony_ci F_IRQ_STATUS = F_NUM_GLOBAL_FIELDS, 14162306a36Sopenharmony_ci F_IRQ_CLEAR, 14262306a36Sopenharmony_ci F_IRQ_ENABLE, 14362306a36Sopenharmony_ci F_ENABLE, 14462306a36Sopenharmony_ci F_CLEAR, 14562306a36Sopenharmony_ci F_SAMPLE_WINDOW, 14662306a36Sopenharmony_ci F_THRESHOLD_HIGH, 14762306a36Sopenharmony_ci F_THRESHOLD_MED, 14862306a36Sopenharmony_ci F_THRESHOLD_LOW, 14962306a36Sopenharmony_ci F_ZONE_ACTIONS_ZONE0, 15062306a36Sopenharmony_ci F_ZONE_ACTIONS_ZONE1, 15162306a36Sopenharmony_ci F_ZONE_ACTIONS_ZONE2, 15262306a36Sopenharmony_ci F_ZONE_ACTIONS_ZONE3, 15362306a36Sopenharmony_ci F_THRESHOLD_COUNT_ZONE0, 15462306a36Sopenharmony_ci F_THRESHOLD_COUNT_ZONE1, 15562306a36Sopenharmony_ci F_THRESHOLD_COUNT_ZONE2, 15662306a36Sopenharmony_ci F_THRESHOLD_COUNT_ZONE3, 15762306a36Sopenharmony_ci F_ZONE0_MAX, 15862306a36Sopenharmony_ci F_ZONE1_MAX, 15962306a36Sopenharmony_ci F_ZONE2_MAX, 16062306a36Sopenharmony_ci F_ZONE3_MAX, 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci F_NUM_FIELDS 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistruct icc_bwmon_data { 16662306a36Sopenharmony_ci unsigned int sample_ms; 16762306a36Sopenharmony_ci unsigned int count_unit_kb; /* kbytes */ 16862306a36Sopenharmony_ci u8 zone1_thres_count; 16962306a36Sopenharmony_ci u8 zone3_thres_count; 17062306a36Sopenharmony_ci unsigned int quirks; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci const struct regmap_config *regmap_cfg; 17362306a36Sopenharmony_ci const struct reg_field *regmap_fields; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci const struct regmap_config *global_regmap_cfg; 17662306a36Sopenharmony_ci const struct reg_field *global_regmap_fields; 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistruct icc_bwmon { 18062306a36Sopenharmony_ci struct device *dev; 18162306a36Sopenharmony_ci const struct icc_bwmon_data *data; 18262306a36Sopenharmony_ci int irq; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci struct regmap_field *regs[F_NUM_FIELDS]; 18562306a36Sopenharmony_ci struct regmap_field *global_regs[F_NUM_GLOBAL_FIELDS]; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci unsigned int max_bw_kbps; 18862306a36Sopenharmony_ci unsigned int min_bw_kbps; 18962306a36Sopenharmony_ci unsigned int target_kbps; 19062306a36Sopenharmony_ci unsigned int current_kbps; 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* BWMON v4 */ 19462306a36Sopenharmony_cistatic const struct reg_field msm8998_bwmon_reg_fields[] = { 19562306a36Sopenharmony_ci [F_GLOBAL_IRQ_CLEAR] = {}, 19662306a36Sopenharmony_ci [F_GLOBAL_IRQ_ENABLE] = {}, 19762306a36Sopenharmony_ci [F_IRQ_STATUS] = REG_FIELD(BWMON_V4_IRQ_STATUS, 4, 7), 19862306a36Sopenharmony_ci [F_IRQ_CLEAR] = REG_FIELD(BWMON_V4_IRQ_CLEAR, 4, 7), 19962306a36Sopenharmony_ci [F_IRQ_ENABLE] = REG_FIELD(BWMON_V4_IRQ_ENABLE, 4, 7), 20062306a36Sopenharmony_ci /* F_ENABLE covers entire register to disable other features */ 20162306a36Sopenharmony_ci [F_ENABLE] = REG_FIELD(BWMON_V4_ENABLE, 0, 31), 20262306a36Sopenharmony_ci [F_CLEAR] = REG_FIELD(BWMON_V4_CLEAR, 0, 1), 20362306a36Sopenharmony_ci [F_SAMPLE_WINDOW] = REG_FIELD(BWMON_V4_SAMPLE_WINDOW, 0, 23), 20462306a36Sopenharmony_ci [F_THRESHOLD_HIGH] = REG_FIELD(BWMON_V4_THRESHOLD_HIGH, 0, 11), 20562306a36Sopenharmony_ci [F_THRESHOLD_MED] = REG_FIELD(BWMON_V4_THRESHOLD_MED, 0, 11), 20662306a36Sopenharmony_ci [F_THRESHOLD_LOW] = REG_FIELD(BWMON_V4_THRESHOLD_LOW, 0, 11), 20762306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE0] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 0, 7), 20862306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE1] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 8, 15), 20962306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE2] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 16, 23), 21062306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE3] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 24, 31), 21162306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE0] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 0, 7), 21262306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE1] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 8, 15), 21362306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE2] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 16, 23), 21462306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE3] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 24, 31), 21562306a36Sopenharmony_ci [F_ZONE0_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(0), 0, 11), 21662306a36Sopenharmony_ci [F_ZONE1_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(1), 0, 11), 21762306a36Sopenharmony_ci [F_ZONE2_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(2), 0, 11), 21862306a36Sopenharmony_ci [F_ZONE3_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(3), 0, 11), 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic const struct regmap_range msm8998_bwmon_reg_noread_ranges[] = { 22262306a36Sopenharmony_ci regmap_reg_range(BWMON_V4_IRQ_CLEAR, BWMON_V4_IRQ_CLEAR), 22362306a36Sopenharmony_ci regmap_reg_range(BWMON_V4_CLEAR, BWMON_V4_CLEAR), 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic const struct regmap_access_table msm8998_bwmon_reg_read_table = { 22762306a36Sopenharmony_ci .no_ranges = msm8998_bwmon_reg_noread_ranges, 22862306a36Sopenharmony_ci .n_no_ranges = ARRAY_SIZE(msm8998_bwmon_reg_noread_ranges), 22962306a36Sopenharmony_ci}; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic const struct regmap_range msm8998_bwmon_reg_volatile_ranges[] = { 23262306a36Sopenharmony_ci regmap_reg_range(BWMON_V4_IRQ_STATUS, BWMON_V4_IRQ_STATUS), 23362306a36Sopenharmony_ci regmap_reg_range(BWMON_V4_ZONE_MAX(0), BWMON_V4_ZONE_MAX(3)), 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic const struct regmap_access_table msm8998_bwmon_reg_volatile_table = { 23762306a36Sopenharmony_ci .yes_ranges = msm8998_bwmon_reg_volatile_ranges, 23862306a36Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(msm8998_bwmon_reg_volatile_ranges), 23962306a36Sopenharmony_ci}; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic const struct reg_field msm8998_bwmon_global_reg_fields[] = { 24262306a36Sopenharmony_ci [F_GLOBAL_IRQ_CLEAR] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_CLEAR, 0, 0), 24362306a36Sopenharmony_ci [F_GLOBAL_IRQ_ENABLE] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_ENABLE, 0, 0), 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const struct regmap_range msm8998_bwmon_global_reg_noread_ranges[] = { 24762306a36Sopenharmony_ci regmap_reg_range(BWMON_V4_GLOBAL_IRQ_CLEAR, BWMON_V4_GLOBAL_IRQ_CLEAR), 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic const struct regmap_access_table msm8998_bwmon_global_reg_read_table = { 25162306a36Sopenharmony_ci .no_ranges = msm8998_bwmon_global_reg_noread_ranges, 25262306a36Sopenharmony_ci .n_no_ranges = ARRAY_SIZE(msm8998_bwmon_global_reg_noread_ranges), 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* 25662306a36Sopenharmony_ci * Fill the cache for non-readable registers only as rest does not really 25762306a36Sopenharmony_ci * matter and can be read from the device. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_cistatic const struct reg_default msm8998_bwmon_reg_defaults[] = { 26062306a36Sopenharmony_ci { BWMON_V4_IRQ_CLEAR, 0x0 }, 26162306a36Sopenharmony_ci { BWMON_V4_CLEAR, 0x0 }, 26262306a36Sopenharmony_ci}; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic const struct reg_default msm8998_bwmon_global_reg_defaults[] = { 26562306a36Sopenharmony_ci { BWMON_V4_GLOBAL_IRQ_CLEAR, 0x0 }, 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic const struct regmap_config msm8998_bwmon_regmap_cfg = { 26962306a36Sopenharmony_ci .reg_bits = 32, 27062306a36Sopenharmony_ci .reg_stride = 4, 27162306a36Sopenharmony_ci .val_bits = 32, 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * No concurrent access expected - driver has one interrupt handler, 27462306a36Sopenharmony_ci * regmap is not shared, no driver or user-space API. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci .disable_locking = true, 27762306a36Sopenharmony_ci .rd_table = &msm8998_bwmon_reg_read_table, 27862306a36Sopenharmony_ci .volatile_table = &msm8998_bwmon_reg_volatile_table, 27962306a36Sopenharmony_ci .reg_defaults = msm8998_bwmon_reg_defaults, 28062306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(msm8998_bwmon_reg_defaults), 28162306a36Sopenharmony_ci /* 28262306a36Sopenharmony_ci * Cache is necessary for using regmap fields with non-readable 28362306a36Sopenharmony_ci * registers. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic const struct regmap_config msm8998_bwmon_global_regmap_cfg = { 28962306a36Sopenharmony_ci .reg_bits = 32, 29062306a36Sopenharmony_ci .reg_stride = 4, 29162306a36Sopenharmony_ci .val_bits = 32, 29262306a36Sopenharmony_ci /* 29362306a36Sopenharmony_ci * No concurrent access expected - driver has one interrupt handler, 29462306a36Sopenharmony_ci * regmap is not shared, no driver or user-space API. 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_ci .disable_locking = true, 29762306a36Sopenharmony_ci .rd_table = &msm8998_bwmon_global_reg_read_table, 29862306a36Sopenharmony_ci .reg_defaults = msm8998_bwmon_global_reg_defaults, 29962306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(msm8998_bwmon_global_reg_defaults), 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * Cache is necessary for using regmap fields with non-readable 30262306a36Sopenharmony_ci * registers. 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic const struct reg_field sdm845_cpu_bwmon_reg_fields[] = { 30862306a36Sopenharmony_ci [F_GLOBAL_IRQ_CLEAR] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_CLEAR_845, 0, 0), 30962306a36Sopenharmony_ci [F_GLOBAL_IRQ_ENABLE] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_ENABLE_845, 0, 0), 31062306a36Sopenharmony_ci [F_IRQ_STATUS] = REG_FIELD(BWMON_V4_IRQ_STATUS, 4, 7), 31162306a36Sopenharmony_ci [F_IRQ_CLEAR] = REG_FIELD(BWMON_V4_IRQ_CLEAR, 4, 7), 31262306a36Sopenharmony_ci [F_IRQ_ENABLE] = REG_FIELD(BWMON_V4_IRQ_ENABLE, 4, 7), 31362306a36Sopenharmony_ci /* F_ENABLE covers entire register to disable other features */ 31462306a36Sopenharmony_ci [F_ENABLE] = REG_FIELD(BWMON_V4_ENABLE, 0, 31), 31562306a36Sopenharmony_ci [F_CLEAR] = REG_FIELD(BWMON_V4_CLEAR, 0, 1), 31662306a36Sopenharmony_ci [F_SAMPLE_WINDOW] = REG_FIELD(BWMON_V4_SAMPLE_WINDOW, 0, 23), 31762306a36Sopenharmony_ci [F_THRESHOLD_HIGH] = REG_FIELD(BWMON_V4_THRESHOLD_HIGH, 0, 11), 31862306a36Sopenharmony_ci [F_THRESHOLD_MED] = REG_FIELD(BWMON_V4_THRESHOLD_MED, 0, 11), 31962306a36Sopenharmony_ci [F_THRESHOLD_LOW] = REG_FIELD(BWMON_V4_THRESHOLD_LOW, 0, 11), 32062306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE0] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 0, 7), 32162306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE1] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 8, 15), 32262306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE2] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 16, 23), 32362306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE3] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 24, 31), 32462306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE0] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 0, 7), 32562306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE1] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 8, 15), 32662306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE2] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 16, 23), 32762306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE3] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 24, 31), 32862306a36Sopenharmony_ci [F_ZONE0_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(0), 0, 11), 32962306a36Sopenharmony_ci [F_ZONE1_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(1), 0, 11), 33062306a36Sopenharmony_ci [F_ZONE2_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(2), 0, 11), 33162306a36Sopenharmony_ci [F_ZONE3_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(3), 0, 11), 33262306a36Sopenharmony_ci}; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic const struct regmap_range sdm845_cpu_bwmon_reg_noread_ranges[] = { 33562306a36Sopenharmony_ci regmap_reg_range(BWMON_V4_GLOBAL_IRQ_CLEAR_845, BWMON_V4_GLOBAL_IRQ_CLEAR_845), 33662306a36Sopenharmony_ci regmap_reg_range(BWMON_V4_IRQ_CLEAR, BWMON_V4_IRQ_CLEAR), 33762306a36Sopenharmony_ci regmap_reg_range(BWMON_V4_CLEAR, BWMON_V4_CLEAR), 33862306a36Sopenharmony_ci}; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic const struct regmap_access_table sdm845_cpu_bwmon_reg_read_table = { 34162306a36Sopenharmony_ci .no_ranges = sdm845_cpu_bwmon_reg_noread_ranges, 34262306a36Sopenharmony_ci .n_no_ranges = ARRAY_SIZE(sdm845_cpu_bwmon_reg_noread_ranges), 34362306a36Sopenharmony_ci}; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * Fill the cache for non-readable registers only as rest does not really 34762306a36Sopenharmony_ci * matter and can be read from the device. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_cistatic const struct reg_default sdm845_cpu_bwmon_reg_defaults[] = { 35062306a36Sopenharmony_ci { BWMON_V4_GLOBAL_IRQ_CLEAR_845, 0x0 }, 35162306a36Sopenharmony_ci { BWMON_V4_IRQ_CLEAR, 0x0 }, 35262306a36Sopenharmony_ci { BWMON_V4_CLEAR, 0x0 }, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic const struct regmap_config sdm845_cpu_bwmon_regmap_cfg = { 35662306a36Sopenharmony_ci .reg_bits = 32, 35762306a36Sopenharmony_ci .reg_stride = 4, 35862306a36Sopenharmony_ci .val_bits = 32, 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * No concurrent access expected - driver has one interrupt handler, 36162306a36Sopenharmony_ci * regmap is not shared, no driver or user-space API. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ci .disable_locking = true, 36462306a36Sopenharmony_ci .rd_table = &sdm845_cpu_bwmon_reg_read_table, 36562306a36Sopenharmony_ci .volatile_table = &msm8998_bwmon_reg_volatile_table, 36662306a36Sopenharmony_ci .reg_defaults = sdm845_cpu_bwmon_reg_defaults, 36762306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(sdm845_cpu_bwmon_reg_defaults), 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * Cache is necessary for using regmap fields with non-readable 37062306a36Sopenharmony_ci * registers. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 37362306a36Sopenharmony_ci}; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* BWMON v5 */ 37662306a36Sopenharmony_cistatic const struct reg_field sdm845_llcc_bwmon_reg_fields[] = { 37762306a36Sopenharmony_ci [F_GLOBAL_IRQ_CLEAR] = {}, 37862306a36Sopenharmony_ci [F_GLOBAL_IRQ_ENABLE] = {}, 37962306a36Sopenharmony_ci [F_IRQ_STATUS] = REG_FIELD(BWMON_V5_IRQ_STATUS, 0, 3), 38062306a36Sopenharmony_ci [F_IRQ_CLEAR] = REG_FIELD(BWMON_V5_IRQ_CLEAR, 0, 3), 38162306a36Sopenharmony_ci [F_IRQ_ENABLE] = REG_FIELD(BWMON_V5_IRQ_ENABLE, 0, 3), 38262306a36Sopenharmony_ci /* F_ENABLE covers entire register to disable other features */ 38362306a36Sopenharmony_ci [F_ENABLE] = REG_FIELD(BWMON_V5_ENABLE, 0, 31), 38462306a36Sopenharmony_ci [F_CLEAR] = REG_FIELD(BWMON_V5_CLEAR, 0, 1), 38562306a36Sopenharmony_ci [F_SAMPLE_WINDOW] = REG_FIELD(BWMON_V5_SAMPLE_WINDOW, 0, 19), 38662306a36Sopenharmony_ci [F_THRESHOLD_HIGH] = REG_FIELD(BWMON_V5_THRESHOLD_HIGH, 0, 11), 38762306a36Sopenharmony_ci [F_THRESHOLD_MED] = REG_FIELD(BWMON_V5_THRESHOLD_MED, 0, 11), 38862306a36Sopenharmony_ci [F_THRESHOLD_LOW] = REG_FIELD(BWMON_V5_THRESHOLD_LOW, 0, 11), 38962306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE0] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 0, 7), 39062306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE1] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 8, 15), 39162306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE2] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 16, 23), 39262306a36Sopenharmony_ci [F_ZONE_ACTIONS_ZONE3] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 24, 31), 39362306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE0] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 0, 7), 39462306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE1] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 8, 15), 39562306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE2] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 16, 23), 39662306a36Sopenharmony_ci [F_THRESHOLD_COUNT_ZONE3] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 24, 31), 39762306a36Sopenharmony_ci [F_ZONE0_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(0), 0, 11), 39862306a36Sopenharmony_ci [F_ZONE1_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(1), 0, 11), 39962306a36Sopenharmony_ci [F_ZONE2_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(2), 0, 11), 40062306a36Sopenharmony_ci [F_ZONE3_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(3), 0, 11), 40162306a36Sopenharmony_ci}; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic const struct regmap_range sdm845_llcc_bwmon_reg_noread_ranges[] = { 40462306a36Sopenharmony_ci regmap_reg_range(BWMON_V5_IRQ_CLEAR, BWMON_V5_IRQ_CLEAR), 40562306a36Sopenharmony_ci regmap_reg_range(BWMON_V5_CLEAR, BWMON_V5_CLEAR), 40662306a36Sopenharmony_ci}; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic const struct regmap_access_table sdm845_llcc_bwmon_reg_read_table = { 40962306a36Sopenharmony_ci .no_ranges = sdm845_llcc_bwmon_reg_noread_ranges, 41062306a36Sopenharmony_ci .n_no_ranges = ARRAY_SIZE(sdm845_llcc_bwmon_reg_noread_ranges), 41162306a36Sopenharmony_ci}; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic const struct regmap_range sdm845_llcc_bwmon_reg_volatile_ranges[] = { 41462306a36Sopenharmony_ci regmap_reg_range(BWMON_V5_IRQ_STATUS, BWMON_V5_IRQ_STATUS), 41562306a36Sopenharmony_ci regmap_reg_range(BWMON_V5_ZONE_MAX(0), BWMON_V5_ZONE_MAX(3)), 41662306a36Sopenharmony_ci}; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic const struct regmap_access_table sdm845_llcc_bwmon_reg_volatile_table = { 41962306a36Sopenharmony_ci .yes_ranges = sdm845_llcc_bwmon_reg_volatile_ranges, 42062306a36Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(sdm845_llcc_bwmon_reg_volatile_ranges), 42162306a36Sopenharmony_ci}; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci/* 42462306a36Sopenharmony_ci * Fill the cache for non-readable registers only as rest does not really 42562306a36Sopenharmony_ci * matter and can be read from the device. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_cistatic const struct reg_default sdm845_llcc_bwmon_reg_defaults[] = { 42862306a36Sopenharmony_ci { BWMON_V5_IRQ_CLEAR, 0x0 }, 42962306a36Sopenharmony_ci { BWMON_V5_CLEAR, 0x0 }, 43062306a36Sopenharmony_ci}; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic const struct regmap_config sdm845_llcc_bwmon_regmap_cfg = { 43362306a36Sopenharmony_ci .reg_bits = 32, 43462306a36Sopenharmony_ci .reg_stride = 4, 43562306a36Sopenharmony_ci .val_bits = 32, 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * No concurrent access expected - driver has one interrupt handler, 43862306a36Sopenharmony_ci * regmap is not shared, no driver or user-space API. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci .disable_locking = true, 44162306a36Sopenharmony_ci .rd_table = &sdm845_llcc_bwmon_reg_read_table, 44262306a36Sopenharmony_ci .volatile_table = &sdm845_llcc_bwmon_reg_volatile_table, 44362306a36Sopenharmony_ci .reg_defaults = sdm845_llcc_bwmon_reg_defaults, 44462306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(sdm845_llcc_bwmon_reg_defaults), 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * Cache is necessary for using regmap fields with non-readable 44762306a36Sopenharmony_ci * registers. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 45062306a36Sopenharmony_ci}; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic void bwmon_clear_counters(struct icc_bwmon *bwmon, bool clear_all) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci unsigned int val = BWMON_CLEAR_CLEAR; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (clear_all) 45762306a36Sopenharmony_ci val |= BWMON_CLEAR_CLEAR_ALL; 45862306a36Sopenharmony_ci /* 45962306a36Sopenharmony_ci * Clear counters. The order and barriers are 46062306a36Sopenharmony_ci * important. Quoting downstream Qualcomm msm-4.9 tree: 46162306a36Sopenharmony_ci * 46262306a36Sopenharmony_ci * The counter clear and IRQ clear bits are not in the same 4KB 46362306a36Sopenharmony_ci * region. So, we need to make sure the counter clear is completed 46462306a36Sopenharmony_ci * before we try to clear the IRQ or do any other counter operations. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci regmap_field_force_write(bwmon->regs[F_CLEAR], val); 46762306a36Sopenharmony_ci if (bwmon->data->quirks & BWMON_NEEDS_FORCE_CLEAR) 46862306a36Sopenharmony_ci regmap_field_force_write(bwmon->regs[F_CLEAR], 0); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic void bwmon_clear_irq(struct icc_bwmon *bwmon) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct regmap_field *global_irq_clr; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (bwmon->data->global_regmap_fields) 47662306a36Sopenharmony_ci global_irq_clr = bwmon->global_regs[F_GLOBAL_IRQ_CLEAR]; 47762306a36Sopenharmony_ci else 47862306a36Sopenharmony_ci global_irq_clr = bwmon->regs[F_GLOBAL_IRQ_CLEAR]; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * Clear zone and global interrupts. The order and barriers are 48262306a36Sopenharmony_ci * important. Quoting downstream Qualcomm msm-4.9 tree: 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci * Synchronize the local interrupt clear in mon_irq_clear() 48562306a36Sopenharmony_ci * with the global interrupt clear here. Otherwise, the CPU 48662306a36Sopenharmony_ci * may reorder the two writes and clear the global interrupt 48762306a36Sopenharmony_ci * before the local interrupt, causing the global interrupt 48862306a36Sopenharmony_ci * to be retriggered by the local interrupt still being high. 48962306a36Sopenharmony_ci * 49062306a36Sopenharmony_ci * Similarly, because the global registers are in a different 49162306a36Sopenharmony_ci * region than the local registers, we need to ensure any register 49262306a36Sopenharmony_ci * writes to enable the monitor after this call are ordered with the 49362306a36Sopenharmony_ci * clearing here so that local writes don't happen before the 49462306a36Sopenharmony_ci * interrupt is cleared. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci regmap_field_force_write(bwmon->regs[F_IRQ_CLEAR], BWMON_IRQ_ENABLE_MASK); 49762306a36Sopenharmony_ci if (bwmon->data->quirks & BWMON_NEEDS_FORCE_CLEAR) 49862306a36Sopenharmony_ci regmap_field_force_write(bwmon->regs[F_IRQ_CLEAR], 0); 49962306a36Sopenharmony_ci if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ) 50062306a36Sopenharmony_ci regmap_field_force_write(global_irq_clr, 50162306a36Sopenharmony_ci BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic void bwmon_disable(struct icc_bwmon *bwmon) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct regmap_field *global_irq_en; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (bwmon->data->global_regmap_fields) 50962306a36Sopenharmony_ci global_irq_en = bwmon->global_regs[F_GLOBAL_IRQ_ENABLE]; 51062306a36Sopenharmony_ci else 51162306a36Sopenharmony_ci global_irq_en = bwmon->regs[F_GLOBAL_IRQ_ENABLE]; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* Disable interrupts. Strict ordering, see bwmon_clear_irq(). */ 51462306a36Sopenharmony_ci if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ) 51562306a36Sopenharmony_ci regmap_field_write(global_irq_en, 0x0); 51662306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_IRQ_ENABLE], 0x0); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* 51962306a36Sopenharmony_ci * Disable bwmon. Must happen before bwmon_clear_irq() to avoid spurious 52062306a36Sopenharmony_ci * IRQ. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_ENABLE], 0x0); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic void bwmon_enable(struct icc_bwmon *bwmon, unsigned int irq_enable) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct regmap_field *global_irq_en; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (bwmon->data->global_regmap_fields) 53062306a36Sopenharmony_ci global_irq_en = bwmon->global_regs[F_GLOBAL_IRQ_ENABLE]; 53162306a36Sopenharmony_ci else 53262306a36Sopenharmony_ci global_irq_en = bwmon->regs[F_GLOBAL_IRQ_ENABLE]; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Enable interrupts */ 53562306a36Sopenharmony_ci if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ) 53662306a36Sopenharmony_ci regmap_field_write(global_irq_en, 53762306a36Sopenharmony_ci BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_IRQ_ENABLE], irq_enable); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Enable bwmon */ 54262306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_ENABLE], BWMON_ENABLE_ENABLE); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic unsigned int bwmon_kbps_to_count(struct icc_bwmon *bwmon, 54662306a36Sopenharmony_ci unsigned int kbps) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci return kbps / bwmon->data->count_unit_kb; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic void bwmon_set_threshold(struct icc_bwmon *bwmon, 55262306a36Sopenharmony_ci struct regmap_field *reg, unsigned int kbps) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci unsigned int thres; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci thres = mult_frac(bwmon_kbps_to_count(bwmon, kbps), 55762306a36Sopenharmony_ci bwmon->data->sample_ms, MSEC_PER_SEC); 55862306a36Sopenharmony_ci regmap_field_write(reg, thres); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic void bwmon_start(struct icc_bwmon *bwmon) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci const struct icc_bwmon_data *data = bwmon->data; 56462306a36Sopenharmony_ci u32 bw_low = 0; 56562306a36Sopenharmony_ci int window; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* No need to check for errors, as this must have succeeded before. */ 56862306a36Sopenharmony_ci dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_low, 0); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci bwmon_clear_counters(bwmon, true); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci window = mult_frac(bwmon->data->sample_ms, HW_TIMER_HZ, MSEC_PER_SEC); 57362306a36Sopenharmony_ci /* Maximum sampling window: 0xffffff for v4 and 0xfffff for v5 */ 57462306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_SAMPLE_WINDOW], window); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_HIGH], bw_low); 57762306a36Sopenharmony_ci bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_MED], bw_low); 57862306a36Sopenharmony_ci bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_LOW], 0); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE0], 58162306a36Sopenharmony_ci BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT); 58262306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE1], 58362306a36Sopenharmony_ci data->zone1_thres_count); 58462306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE2], 58562306a36Sopenharmony_ci BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT); 58662306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE3], 58762306a36Sopenharmony_ci data->zone3_thres_count); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE0], 59062306a36Sopenharmony_ci BWMON_ZONE_ACTIONS_ZONE0); 59162306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE1], 59262306a36Sopenharmony_ci BWMON_ZONE_ACTIONS_ZONE1); 59362306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE2], 59462306a36Sopenharmony_ci BWMON_ZONE_ACTIONS_ZONE2); 59562306a36Sopenharmony_ci regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE3], 59662306a36Sopenharmony_ci BWMON_ZONE_ACTIONS_ZONE3); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci bwmon_clear_irq(bwmon); 59962306a36Sopenharmony_ci bwmon_enable(bwmon, BWMON_IRQ_ENABLE_MASK); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic irqreturn_t bwmon_intr(int irq, void *dev_id) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct icc_bwmon *bwmon = dev_id; 60562306a36Sopenharmony_ci unsigned int status, max; 60662306a36Sopenharmony_ci int zone; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (regmap_field_read(bwmon->regs[F_IRQ_STATUS], &status)) 60962306a36Sopenharmony_ci return IRQ_NONE; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci status &= BWMON_IRQ_ENABLE_MASK; 61262306a36Sopenharmony_ci if (!status) { 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * Only zone 1 and zone 3 interrupts are enabled but zone 2 61562306a36Sopenharmony_ci * threshold could be hit and trigger interrupt even if not 61662306a36Sopenharmony_ci * enabled. 61762306a36Sopenharmony_ci * Such spurious interrupt might come with valuable max count or 61862306a36Sopenharmony_ci * not, so solution would be to always check all 61962306a36Sopenharmony_ci * BWMON_ZONE_MAX() registers to find the highest value. 62062306a36Sopenharmony_ci * Such case is currently ignored. 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_ci return IRQ_NONE; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci bwmon_disable(bwmon); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci zone = get_bitmask_order(status) - 1; 62862306a36Sopenharmony_ci /* 62962306a36Sopenharmony_ci * Zone max bytes count register returns count units within sampling 63062306a36Sopenharmony_ci * window. Downstream kernel for BWMONv4 (called BWMON type 2 in 63162306a36Sopenharmony_ci * downstream) always increments the max bytes count by one. 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_ci if (regmap_field_read(bwmon->regs[F_ZONE0_MAX + zone], &max)) 63462306a36Sopenharmony_ci return IRQ_NONE; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci max += 1; 63762306a36Sopenharmony_ci max *= bwmon->data->count_unit_kb; 63862306a36Sopenharmony_ci bwmon->target_kbps = mult_frac(max, MSEC_PER_SEC, bwmon->data->sample_ms); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic irqreturn_t bwmon_intr_thread(int irq, void *dev_id) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct icc_bwmon *bwmon = dev_id; 64662306a36Sopenharmony_ci unsigned int irq_enable = 0; 64762306a36Sopenharmony_ci struct dev_pm_opp *opp, *target_opp; 64862306a36Sopenharmony_ci unsigned int bw_kbps, up_kbps, down_kbps; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci bw_kbps = bwmon->target_kbps; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci target_opp = dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_kbps, 0); 65362306a36Sopenharmony_ci if (IS_ERR(target_opp) && PTR_ERR(target_opp) == -ERANGE) 65462306a36Sopenharmony_ci target_opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci bwmon->target_kbps = bw_kbps; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci bw_kbps--; 65962306a36Sopenharmony_ci opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0); 66062306a36Sopenharmony_ci if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE) 66162306a36Sopenharmony_ci down_kbps = bwmon->target_kbps; 66262306a36Sopenharmony_ci else 66362306a36Sopenharmony_ci down_kbps = bw_kbps; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci up_kbps = bwmon->target_kbps + 1; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (bwmon->target_kbps >= bwmon->max_bw_kbps) 66862306a36Sopenharmony_ci irq_enable = BIT(1); 66962306a36Sopenharmony_ci else if (bwmon->target_kbps <= bwmon->min_bw_kbps) 67062306a36Sopenharmony_ci irq_enable = BIT(3); 67162306a36Sopenharmony_ci else 67262306a36Sopenharmony_ci irq_enable = BWMON_IRQ_ENABLE_MASK; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_HIGH], 67562306a36Sopenharmony_ci up_kbps); 67662306a36Sopenharmony_ci bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_MED], 67762306a36Sopenharmony_ci down_kbps); 67862306a36Sopenharmony_ci bwmon_clear_counters(bwmon, false); 67962306a36Sopenharmony_ci bwmon_clear_irq(bwmon); 68062306a36Sopenharmony_ci bwmon_enable(bwmon, irq_enable); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (bwmon->target_kbps == bwmon->current_kbps) 68362306a36Sopenharmony_ci goto out; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci dev_pm_opp_set_opp(bwmon->dev, target_opp); 68662306a36Sopenharmony_ci bwmon->current_kbps = bwmon->target_kbps; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ciout: 68962306a36Sopenharmony_ci dev_pm_opp_put(target_opp); 69062306a36Sopenharmony_ci if (!IS_ERR(opp)) 69162306a36Sopenharmony_ci dev_pm_opp_put(opp); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci return IRQ_HANDLED; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic int bwmon_init_regmap(struct platform_device *pdev, 69762306a36Sopenharmony_ci struct icc_bwmon *bwmon) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 70062306a36Sopenharmony_ci void __iomem *base; 70162306a36Sopenharmony_ci struct regmap *map; 70262306a36Sopenharmony_ci int ret; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* Map the monitor base */ 70562306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 70662306a36Sopenharmony_ci if (IS_ERR(base)) 70762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(base), 70862306a36Sopenharmony_ci "failed to map bwmon registers\n"); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci map = devm_regmap_init_mmio(dev, base, bwmon->data->regmap_cfg); 71162306a36Sopenharmony_ci if (IS_ERR(map)) 71262306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(map), 71362306a36Sopenharmony_ci "failed to initialize regmap\n"); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(msm8998_bwmon_global_reg_fields) != F_NUM_GLOBAL_FIELDS); 71662306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(msm8998_bwmon_reg_fields) != F_NUM_FIELDS); 71762306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(sdm845_cpu_bwmon_reg_fields) != F_NUM_FIELDS); 71862306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(sdm845_llcc_bwmon_reg_fields) != F_NUM_FIELDS); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci ret = devm_regmap_field_bulk_alloc(dev, map, bwmon->regs, 72162306a36Sopenharmony_ci bwmon->data->regmap_fields, 72262306a36Sopenharmony_ci F_NUM_FIELDS); 72362306a36Sopenharmony_ci if (ret) 72462306a36Sopenharmony_ci return ret; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (bwmon->data->global_regmap_cfg) { 72762306a36Sopenharmony_ci /* Map the global base, if separate */ 72862306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 1); 72962306a36Sopenharmony_ci if (IS_ERR(base)) 73062306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(base), 73162306a36Sopenharmony_ci "failed to map bwmon global registers\n"); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci map = devm_regmap_init_mmio(dev, base, bwmon->data->global_regmap_cfg); 73462306a36Sopenharmony_ci if (IS_ERR(map)) 73562306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(map), 73662306a36Sopenharmony_ci "failed to initialize global regmap\n"); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci ret = devm_regmap_field_bulk_alloc(dev, map, bwmon->global_regs, 73962306a36Sopenharmony_ci bwmon->data->global_regmap_fields, 74062306a36Sopenharmony_ci F_NUM_GLOBAL_FIELDS); 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return ret; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int bwmon_probe(struct platform_device *pdev) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 74962306a36Sopenharmony_ci struct dev_pm_opp *opp; 75062306a36Sopenharmony_ci struct icc_bwmon *bwmon; 75162306a36Sopenharmony_ci int ret; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci bwmon = devm_kzalloc(dev, sizeof(*bwmon), GFP_KERNEL); 75462306a36Sopenharmony_ci if (!bwmon) 75562306a36Sopenharmony_ci return -ENOMEM; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci bwmon->data = of_device_get_match_data(dev); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci ret = bwmon_init_regmap(pdev, bwmon); 76062306a36Sopenharmony_ci if (ret) 76162306a36Sopenharmony_ci return ret; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci bwmon->irq = platform_get_irq(pdev, 0); 76462306a36Sopenharmony_ci if (bwmon->irq < 0) 76562306a36Sopenharmony_ci return bwmon->irq; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci ret = devm_pm_opp_of_add_table(dev); 76862306a36Sopenharmony_ci if (ret) 76962306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to add OPP table\n"); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci bwmon->max_bw_kbps = UINT_MAX; 77262306a36Sopenharmony_ci opp = dev_pm_opp_find_bw_floor(dev, &bwmon->max_bw_kbps, 0); 77362306a36Sopenharmony_ci if (IS_ERR(opp)) 77462306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(opp), "failed to find max peak bandwidth\n"); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci bwmon->min_bw_kbps = 0; 77762306a36Sopenharmony_ci opp = dev_pm_opp_find_bw_ceil(dev, &bwmon->min_bw_kbps, 0); 77862306a36Sopenharmony_ci if (IS_ERR(opp)) 77962306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(opp), "failed to find min peak bandwidth\n"); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci bwmon->dev = dev; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci bwmon_disable(bwmon); 78462306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, bwmon->irq, bwmon_intr, 78562306a36Sopenharmony_ci bwmon_intr_thread, 78662306a36Sopenharmony_ci IRQF_ONESHOT, dev_name(dev), bwmon); 78762306a36Sopenharmony_ci if (ret) 78862306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to request IRQ\n"); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci platform_set_drvdata(pdev, bwmon); 79162306a36Sopenharmony_ci bwmon_start(bwmon); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic int bwmon_remove(struct platform_device *pdev) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct icc_bwmon *bwmon = platform_get_drvdata(pdev); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci bwmon_disable(bwmon); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return 0; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic const struct icc_bwmon_data msm8998_bwmon_data = { 80662306a36Sopenharmony_ci .sample_ms = 4, 80762306a36Sopenharmony_ci .count_unit_kb = 1024, 80862306a36Sopenharmony_ci .zone1_thres_count = 16, 80962306a36Sopenharmony_ci .zone3_thres_count = 1, 81062306a36Sopenharmony_ci .quirks = BWMON_HAS_GLOBAL_IRQ, 81162306a36Sopenharmony_ci .regmap_fields = msm8998_bwmon_reg_fields, 81262306a36Sopenharmony_ci .regmap_cfg = &msm8998_bwmon_regmap_cfg, 81362306a36Sopenharmony_ci .global_regmap_fields = msm8998_bwmon_global_reg_fields, 81462306a36Sopenharmony_ci .global_regmap_cfg = &msm8998_bwmon_global_regmap_cfg, 81562306a36Sopenharmony_ci}; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic const struct icc_bwmon_data sdm845_cpu_bwmon_data = { 81862306a36Sopenharmony_ci .sample_ms = 4, 81962306a36Sopenharmony_ci .count_unit_kb = 64, 82062306a36Sopenharmony_ci .zone1_thres_count = 16, 82162306a36Sopenharmony_ci .zone3_thres_count = 1, 82262306a36Sopenharmony_ci .quirks = BWMON_HAS_GLOBAL_IRQ, 82362306a36Sopenharmony_ci .regmap_fields = sdm845_cpu_bwmon_reg_fields, 82462306a36Sopenharmony_ci .regmap_cfg = &sdm845_cpu_bwmon_regmap_cfg, 82562306a36Sopenharmony_ci}; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic const struct icc_bwmon_data sdm845_llcc_bwmon_data = { 82862306a36Sopenharmony_ci .sample_ms = 4, 82962306a36Sopenharmony_ci .count_unit_kb = 1024, 83062306a36Sopenharmony_ci .zone1_thres_count = 16, 83162306a36Sopenharmony_ci .zone3_thres_count = 1, 83262306a36Sopenharmony_ci .regmap_fields = sdm845_llcc_bwmon_reg_fields, 83362306a36Sopenharmony_ci .regmap_cfg = &sdm845_llcc_bwmon_regmap_cfg, 83462306a36Sopenharmony_ci}; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic const struct icc_bwmon_data sc7280_llcc_bwmon_data = { 83762306a36Sopenharmony_ci .sample_ms = 4, 83862306a36Sopenharmony_ci .count_unit_kb = 64, 83962306a36Sopenharmony_ci .zone1_thres_count = 16, 84062306a36Sopenharmony_ci .zone3_thres_count = 1, 84162306a36Sopenharmony_ci .quirks = BWMON_NEEDS_FORCE_CLEAR, 84262306a36Sopenharmony_ci .regmap_fields = sdm845_llcc_bwmon_reg_fields, 84362306a36Sopenharmony_ci .regmap_cfg = &sdm845_llcc_bwmon_regmap_cfg, 84462306a36Sopenharmony_ci}; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic const struct of_device_id bwmon_of_match[] = { 84762306a36Sopenharmony_ci /* BWMONv4, separate monitor and global register spaces */ 84862306a36Sopenharmony_ci { .compatible = "qcom,msm8998-bwmon", .data = &msm8998_bwmon_data }, 84962306a36Sopenharmony_ci /* BWMONv4, unified register space */ 85062306a36Sopenharmony_ci { .compatible = "qcom,sdm845-bwmon", .data = &sdm845_cpu_bwmon_data }, 85162306a36Sopenharmony_ci /* BWMONv5 */ 85262306a36Sopenharmony_ci { .compatible = "qcom,sdm845-llcc-bwmon", .data = &sdm845_llcc_bwmon_data }, 85362306a36Sopenharmony_ci { .compatible = "qcom,sc7280-llcc-bwmon", .data = &sc7280_llcc_bwmon_data }, 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* Compatibles kept for legacy reasons */ 85662306a36Sopenharmony_ci { .compatible = "qcom,sc7280-cpu-bwmon", .data = &sdm845_cpu_bwmon_data }, 85762306a36Sopenharmony_ci { .compatible = "qcom,sc8280xp-cpu-bwmon", .data = &sdm845_cpu_bwmon_data }, 85862306a36Sopenharmony_ci { .compatible = "qcom,sm8550-cpu-bwmon", .data = &sdm845_cpu_bwmon_data }, 85962306a36Sopenharmony_ci {} 86062306a36Sopenharmony_ci}; 86162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bwmon_of_match); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic struct platform_driver bwmon_driver = { 86462306a36Sopenharmony_ci .probe = bwmon_probe, 86562306a36Sopenharmony_ci .remove = bwmon_remove, 86662306a36Sopenharmony_ci .driver = { 86762306a36Sopenharmony_ci .name = "qcom-bwmon", 86862306a36Sopenharmony_ci .of_match_table = bwmon_of_match, 86962306a36Sopenharmony_ci }, 87062306a36Sopenharmony_ci}; 87162306a36Sopenharmony_cimodule_platform_driver(bwmon_driver); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ciMODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>"); 87462306a36Sopenharmony_ciMODULE_DESCRIPTION("QCOM BWMON driver"); 87562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 876