// 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 , based on * previous work of Thara Gopinath and msm-4.9 downstream sources. */ #include #include #include #include #include #include #include #include #include /* * 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_STATUS 0x0 #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) #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_COUNT 0x2d8 #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; void __iomem *base; int irq; unsigned int default_lowbw_kbps; unsigned int sample_ms; unsigned int max_bw_kbps; unsigned int min_bw_kbps; unsigned int target_kbps; unsigned int current_kbps; }; static void bwmon_clear_counters(struct icc_bwmon *bwmon) { /* * 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. */ writel(BWMON_CLEAR_CLEAR, bwmon->base + BWMON_CLEAR); } 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); writel(BIT(0), bwmon->base + BWMON_GLOBAL_IRQ_CLEAR); } 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; thres = mult_frac(bwmon_kbps_to_count(kbps), bwmon->sample_ms, MSEC_PER_SEC); writel_relaxed(thres, bwmon->base + reg); } static void bwmon_start(struct icc_bwmon *bwmon, const struct icc_bwmon_data *data) { unsigned int thres_count; int window; bwmon_clear_counters(bwmon); window = mult_frac(bwmon->sample_ms, HW_TIMER_HZ, MSEC_PER_SEC); /* 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; bwmon->target_kbps = mult_frac(max, MSEC_PER_SEC, bwmon->sample_ms); 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() */ bwmon_clear_counters(bwmon); 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; const struct icc_bwmon_data *data; int ret; bwmon = devm_kzalloc(dev, sizeof(*bwmon), GFP_KERNEL); if (!bwmon) return -ENOMEM; data = of_device_get_match_data(dev); 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); 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, 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->sample_ms = data->sample_ms; bwmon->default_lowbw_kbps = data->default_lowbw_kbps; 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); bwmon_start(bwmon, data); 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 "); MODULE_DESCRIPTION("QCOM BWMON driver"); MODULE_LICENSE("GPL");