// 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.
*/
/* * 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_V4_GLOBAL_IRQ_CLEAR 0x108 #define BWMON_V4_GLOBAL_IRQ_ENABLE 0x10c /* * All values here and further are matching regmap fields, so without absolute * register offsets.
*/ #define BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE BIT(0)
/* * Starting with SDM845, the BWMON4 register space has changed a bit: * the global registers were jammed into the beginning of the monitor region. * To keep the proper offsets, one would have to map <GLOBAL_BASE 0x200> and * <GLOBAL_BASE+0x100 0x300>, which is straight up wrong. * To facilitate for that, while allowing the older, arguably more proper * implementations to work, offset the global registers by -0x100 to avoid * having to map half of the global registers twice.
*/ #define BWMON_V4_845_OFFSET 0x100 #define BWMON_V4_GLOBAL_IRQ_CLEAR_845 (BWMON_V4_GLOBAL_IRQ_CLEAR - BWMON_V4_845_OFFSET) #define BWMON_V4_GLOBAL_IRQ_ENABLE_845 (BWMON_V4_GLOBAL_IRQ_ENABLE - BWMON_V4_845_OFFSET)
#define BWMON_V4_ZONE_ACTIONS 0x2b8 #define BWMON_V5_ZONE_ACTIONS 0x030 /* * 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))
/* * There is no clear documentation/explanation of BWMON_V4_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_V4_THRESHOLD_COUNT 0x2bc #define BWMON_V5_THRESHOLD_COUNT 0x034 #define BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT 0xff #define BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT 0xff
/* * Fill the cache for non-readable registers only as rest does not really * matter and can be read from the device.
*/ staticconststruct reg_default msm8998_bwmon_reg_defaults[] = {
{ BWMON_V4_IRQ_CLEAR, 0x0 },
{ BWMON_V4_CLEAR, 0x0 },
};
/* * Fill the cache for non-readable registers only as rest does not really * matter and can be read from the device.
*/ staticconststruct reg_default sdm845_cpu_bwmon_reg_defaults[] = {
{ BWMON_V4_GLOBAL_IRQ_CLEAR_845, 0x0 },
{ BWMON_V4_IRQ_CLEAR, 0x0 },
{ BWMON_V4_CLEAR, 0x0 },
};
staticconststruct regmap_config sdm845_cpu_bwmon_regmap_cfg = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32, /* * No concurrent access expected - driver has one interrupt handler, * regmap is not shared, no driver or user-space API.
*/
.disable_locking = true,
.rd_table = &sdm845_cpu_bwmon_reg_read_table,
.volatile_table = &msm8998_bwmon_reg_volatile_table,
.reg_defaults = sdm845_cpu_bwmon_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(sdm845_cpu_bwmon_reg_defaults), /* * Cache is necessary for using regmap fields with non-readable * registers.
*/
.cache_type = REGCACHE_MAPLE,
};
/* * Fill the cache for non-readable registers only as rest does not really * matter and can be read from the device.
*/ staticconststruct reg_default sdm845_llcc_bwmon_reg_defaults[] = {
{ BWMON_V5_IRQ_CLEAR, 0x0 },
{ BWMON_V5_CLEAR, 0x0 },
};
staticconststruct regmap_config sdm845_llcc_bwmon_regmap_cfg = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32, /* * No concurrent access expected - driver has one interrupt handler, * regmap is not shared, no driver or user-space API.
*/
.disable_locking = true,
.rd_table = &sdm845_llcc_bwmon_reg_read_table,
.volatile_table = &sdm845_llcc_bwmon_reg_volatile_table,
.reg_defaults = sdm845_llcc_bwmon_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(sdm845_llcc_bwmon_reg_defaults), /* * Cache is necessary for using regmap fields with non-readable * registers.
*/
.cache_type = REGCACHE_MAPLE,
};
if (clear_all)
val |= BWMON_CLEAR_CLEAR_ALL; /* * 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 4KB * 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.
*/
regmap_field_force_write(bwmon->regs[F_CLEAR], val); if (bwmon->data->quirks & BWMON_NEEDS_FORCE_CLEAR)
regmap_field_force_write(bwmon->regs[F_CLEAR], 0);
}
if (bwmon->data->global_regmap_fields)
global_irq_clr = bwmon->global_regs[F_GLOBAL_IRQ_CLEAR]; else
global_irq_clr = bwmon->regs[F_GLOBAL_IRQ_CLEAR];
/* * 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.
*/
regmap_field_force_write(bwmon->regs[F_IRQ_CLEAR], BWMON_IRQ_ENABLE_MASK); if (bwmon->data->quirks & BWMON_NEEDS_FORCE_CLEAR)
regmap_field_force_write(bwmon->regs[F_IRQ_CLEAR], 0); if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ)
regmap_field_force_write(global_irq_clr,
BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE);
}
/* No need to check for errors, as this must have succeeded before. */
dev_pm_opp_put(dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_low, 0));
bwmon_clear_counters(bwmon, true);
window = mult_frac(bwmon->data->sample_ms, HW_TIMER_HZ, MSEC_PER_SEC); /* Maximum sampling window: 0xffffff for v4 and 0xfffff for v5 */
regmap_field_write(bwmon->regs[F_SAMPLE_WINDOW], window);
if (regmap_field_read(bwmon->regs[F_IRQ_STATUS], &status)) return IRQ_NONE;
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) - 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.
*/ if (regmap_field_read(bwmon->regs[F_ZONE0_MAX + zone], &max)) return IRQ_NONE;
max += 1;
max *= bwmon->data->count_unit_kb;
bwmon->target_kbps = mult_frac(max, MSEC_PER_SEC, bwmon->data->sample_ms);
/* Map the monitor base */
base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return dev_err_probe(dev, PTR_ERR(base), "failed to map bwmon registers\n");
map = devm_regmap_init_mmio(dev, base, bwmon->data->regmap_cfg); if (IS_ERR(map)) return dev_err_probe(dev, PTR_ERR(map), "failed to initialize regmap\n");
ret = devm_regmap_field_bulk_alloc(dev, map, bwmon->regs,
bwmon->data->regmap_fields,
F_NUM_FIELDS); if (ret) return ret;
if (bwmon->data->global_regmap_cfg) { /* Map the global base, if separate */
base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(base)) return dev_err_probe(dev, PTR_ERR(base), "failed to map bwmon global registers\n");
map = devm_regmap_init_mmio(dev, base, bwmon->data->global_regmap_cfg); if (IS_ERR(map)) return dev_err_probe(dev, PTR_ERR(map), "failed to initialize global regmap\n");
ret = devm_regmap_field_bulk_alloc(dev, map, bwmon->global_regs,
bwmon->data->global_regmap_fields,
F_NUM_GLOBAL_FIELDS);
}
bwmon = devm_kzalloc(dev, sizeof(*bwmon), GFP_KERNEL); if (!bwmon) return -ENOMEM;
bwmon->data = of_device_get_match_data(dev);
ret = bwmon_init_regmap(pdev, bwmon); if (ret) return ret;
bwmon->irq = platform_get_irq(pdev, 0); if (bwmon->irq < 0) 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, PTR_ERR(opp), "failed to find max peak bandwidth\n");
dev_pm_opp_put(opp);
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, PTR_ERR(opp), "failed to find min peak bandwidth\n");
dev_pm_opp_put(opp);
bwmon->dev = dev;
bwmon_disable(bwmon);
/* * SoCs with multiple cpu-bwmon instances can end up using a shared interrupt * line. Using the devm_ variant might result in the IRQ handler being executed * after bwmon_disable in bwmon_remove()
*/
ret = request_threaded_irq(bwmon->irq, bwmon_intr, bwmon_intr_thread,
IRQF_ONESHOT | IRQF_SHARED, dev_name(dev), bwmon); if (ret) return dev_err_probe(dev, ret, "failed to request IRQ\n");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.