123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702 |
- // 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 <[email protected]>, based on
- * previous work of Thara Gopinath and msm-4.9 downstream sources.
- */
- #include <linux/err.h>
- #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/regmap.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_V4_GLOBAL_IRQ_CLEAR 0x008
- #define BWMON_V4_GLOBAL_IRQ_ENABLE 0x00c
- /*
- * All values here and further are matching regmap fields, so without absolute
- * register offsets.
- */
- #define BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE BIT(0)
- #define BWMON_V4_IRQ_STATUS 0x100
- #define BWMON_V4_IRQ_CLEAR 0x108
- #define BWMON_V4_IRQ_ENABLE 0x10c
- #define BWMON_IRQ_ENABLE_MASK (BIT(1) | BIT(3))
- #define BWMON_V5_IRQ_STATUS 0x000
- #define BWMON_V5_IRQ_CLEAR 0x008
- #define BWMON_V5_IRQ_ENABLE 0x00c
- #define BWMON_V4_ENABLE 0x2a0
- #define BWMON_V5_ENABLE 0x010
- #define BWMON_ENABLE_ENABLE BIT(0)
- #define BWMON_V4_CLEAR 0x2a4
- #define BWMON_V5_CLEAR 0x014
- #define BWMON_CLEAR_CLEAR BIT(0)
- #define BWMON_CLEAR_CLEAR_ALL BIT(1)
- #define BWMON_V4_SAMPLE_WINDOW 0x2a8
- #define BWMON_V5_SAMPLE_WINDOW 0x020
- #define BWMON_V4_THRESHOLD_HIGH 0x2ac
- #define BWMON_V4_THRESHOLD_MED 0x2b0
- #define BWMON_V4_THRESHOLD_LOW 0x2b4
- #define BWMON_V5_THRESHOLD_HIGH 0x024
- #define BWMON_V5_THRESHOLD_MED 0x028
- #define BWMON_V5_THRESHOLD_LOW 0x02c
- #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
- #define BWMON_V4_ZONE_MAX(zone) (0x2e0 + 4 * (zone))
- #define BWMON_V5_ZONE_MAX(zone) (0x044 + 4 * (zone))
- /* Quirks for specific BWMON types */
- #define BWMON_HAS_GLOBAL_IRQ BIT(0)
- #define BWMON_NEEDS_FORCE_CLEAR BIT(1)
- enum bwmon_fields {
- F_GLOBAL_IRQ_CLEAR,
- F_GLOBAL_IRQ_ENABLE,
- F_IRQ_STATUS,
- F_IRQ_CLEAR,
- F_IRQ_ENABLE,
- F_ENABLE,
- F_CLEAR,
- F_SAMPLE_WINDOW,
- F_THRESHOLD_HIGH,
- F_THRESHOLD_MED,
- F_THRESHOLD_LOW,
- F_ZONE_ACTIONS_ZONE0,
- F_ZONE_ACTIONS_ZONE1,
- F_ZONE_ACTIONS_ZONE2,
- F_ZONE_ACTIONS_ZONE3,
- F_THRESHOLD_COUNT_ZONE0,
- F_THRESHOLD_COUNT_ZONE1,
- F_THRESHOLD_COUNT_ZONE2,
- F_THRESHOLD_COUNT_ZONE3,
- F_ZONE0_MAX,
- F_ZONE1_MAX,
- F_ZONE2_MAX,
- F_ZONE3_MAX,
- F_NUM_FIELDS
- };
- struct icc_bwmon_data {
- unsigned int sample_ms;
- unsigned int count_unit_kb; /* kbytes */
- unsigned int default_highbw_kbps;
- unsigned int default_medbw_kbps;
- unsigned int default_lowbw_kbps;
- u8 zone1_thres_count;
- u8 zone3_thres_count;
- unsigned int quirks;
- const struct regmap_config *regmap_cfg;
- const struct reg_field *regmap_fields;
- };
- struct icc_bwmon {
- struct device *dev;
- const struct icc_bwmon_data *data;
- int irq;
- struct regmap *regmap;
- struct regmap_field *regs[F_NUM_FIELDS];
- unsigned int max_bw_kbps;
- unsigned int min_bw_kbps;
- unsigned int target_kbps;
- unsigned int current_kbps;
- };
- /* BWMON v4 */
- static const struct reg_field msm8998_bwmon_reg_fields[] = {
- [F_GLOBAL_IRQ_CLEAR] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_CLEAR, 0, 0),
- [F_GLOBAL_IRQ_ENABLE] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_ENABLE, 0, 0),
- [F_IRQ_STATUS] = REG_FIELD(BWMON_V4_IRQ_STATUS, 4, 7),
- [F_IRQ_CLEAR] = REG_FIELD(BWMON_V4_IRQ_CLEAR, 4, 7),
- [F_IRQ_ENABLE] = REG_FIELD(BWMON_V4_IRQ_ENABLE, 4, 7),
- /* F_ENABLE covers entire register to disable other features */
- [F_ENABLE] = REG_FIELD(BWMON_V4_ENABLE, 0, 31),
- [F_CLEAR] = REG_FIELD(BWMON_V4_CLEAR, 0, 1),
- [F_SAMPLE_WINDOW] = REG_FIELD(BWMON_V4_SAMPLE_WINDOW, 0, 23),
- [F_THRESHOLD_HIGH] = REG_FIELD(BWMON_V4_THRESHOLD_HIGH, 0, 11),
- [F_THRESHOLD_MED] = REG_FIELD(BWMON_V4_THRESHOLD_MED, 0, 11),
- [F_THRESHOLD_LOW] = REG_FIELD(BWMON_V4_THRESHOLD_LOW, 0, 11),
- [F_ZONE_ACTIONS_ZONE0] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 0, 7),
- [F_ZONE_ACTIONS_ZONE1] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 8, 15),
- [F_ZONE_ACTIONS_ZONE2] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 16, 23),
- [F_ZONE_ACTIONS_ZONE3] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 24, 31),
- [F_THRESHOLD_COUNT_ZONE0] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 0, 7),
- [F_THRESHOLD_COUNT_ZONE1] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 8, 15),
- [F_THRESHOLD_COUNT_ZONE2] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 16, 23),
- [F_THRESHOLD_COUNT_ZONE3] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 24, 31),
- [F_ZONE0_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(0), 0, 11),
- [F_ZONE1_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(1), 0, 11),
- [F_ZONE2_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(2), 0, 11),
- [F_ZONE3_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(3), 0, 11),
- };
- static const struct regmap_range msm8998_bwmon_reg_noread_ranges[] = {
- regmap_reg_range(BWMON_V4_GLOBAL_IRQ_CLEAR, BWMON_V4_GLOBAL_IRQ_CLEAR),
- regmap_reg_range(BWMON_V4_IRQ_CLEAR, BWMON_V4_IRQ_CLEAR),
- regmap_reg_range(BWMON_V4_CLEAR, BWMON_V4_CLEAR),
- };
- static const struct regmap_access_table msm8998_bwmon_reg_read_table = {
- .no_ranges = msm8998_bwmon_reg_noread_ranges,
- .n_no_ranges = ARRAY_SIZE(msm8998_bwmon_reg_noread_ranges),
- };
- static const struct regmap_range msm8998_bwmon_reg_volatile_ranges[] = {
- regmap_reg_range(BWMON_V4_IRQ_STATUS, BWMON_V4_IRQ_STATUS),
- regmap_reg_range(BWMON_V4_ZONE_MAX(0), BWMON_V4_ZONE_MAX(3)),
- };
- static const struct regmap_access_table msm8998_bwmon_reg_volatile_table = {
- .yes_ranges = msm8998_bwmon_reg_volatile_ranges,
- .n_yes_ranges = ARRAY_SIZE(msm8998_bwmon_reg_volatile_ranges),
- };
- /*
- * Fill the cache for non-readable registers only as rest does not really
- * matter and can be read from the device.
- */
- static const struct reg_default msm8998_bwmon_reg_defaults[] = {
- { BWMON_V4_GLOBAL_IRQ_CLEAR, 0x0 },
- { BWMON_V4_IRQ_CLEAR, 0x0 },
- { BWMON_V4_CLEAR, 0x0 },
- };
- static const struct regmap_config msm8998_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 = &msm8998_bwmon_reg_read_table,
- .volatile_table = &msm8998_bwmon_reg_volatile_table,
- .reg_defaults = msm8998_bwmon_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(msm8998_bwmon_reg_defaults),
- /*
- * Cache is necessary for using regmap fields with non-readable
- * registers.
- */
- .cache_type = REGCACHE_RBTREE,
- };
- /* BWMON v5 */
- static const struct reg_field sdm845_llcc_bwmon_reg_fields[] = {
- [F_GLOBAL_IRQ_CLEAR] = {},
- [F_GLOBAL_IRQ_ENABLE] = {},
- [F_IRQ_STATUS] = REG_FIELD(BWMON_V5_IRQ_STATUS, 0, 3),
- [F_IRQ_CLEAR] = REG_FIELD(BWMON_V5_IRQ_CLEAR, 0, 3),
- [F_IRQ_ENABLE] = REG_FIELD(BWMON_V5_IRQ_ENABLE, 0, 3),
- /* F_ENABLE covers entire register to disable other features */
- [F_ENABLE] = REG_FIELD(BWMON_V5_ENABLE, 0, 31),
- [F_CLEAR] = REG_FIELD(BWMON_V5_CLEAR, 0, 1),
- [F_SAMPLE_WINDOW] = REG_FIELD(BWMON_V5_SAMPLE_WINDOW, 0, 19),
- [F_THRESHOLD_HIGH] = REG_FIELD(BWMON_V5_THRESHOLD_HIGH, 0, 11),
- [F_THRESHOLD_MED] = REG_FIELD(BWMON_V5_THRESHOLD_MED, 0, 11),
- [F_THRESHOLD_LOW] = REG_FIELD(BWMON_V5_THRESHOLD_LOW, 0, 11),
- [F_ZONE_ACTIONS_ZONE0] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 0, 7),
- [F_ZONE_ACTIONS_ZONE1] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 8, 15),
- [F_ZONE_ACTIONS_ZONE2] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 16, 23),
- [F_ZONE_ACTIONS_ZONE3] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 24, 31),
- [F_THRESHOLD_COUNT_ZONE0] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 0, 7),
- [F_THRESHOLD_COUNT_ZONE1] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 8, 15),
- [F_THRESHOLD_COUNT_ZONE2] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 16, 23),
- [F_THRESHOLD_COUNT_ZONE3] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 24, 31),
- [F_ZONE0_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(0), 0, 11),
- [F_ZONE1_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(1), 0, 11),
- [F_ZONE2_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(2), 0, 11),
- [F_ZONE3_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(3), 0, 11),
- };
- static const struct regmap_range sdm845_llcc_bwmon_reg_noread_ranges[] = {
- regmap_reg_range(BWMON_V5_IRQ_CLEAR, BWMON_V5_IRQ_CLEAR),
- regmap_reg_range(BWMON_V5_CLEAR, BWMON_V5_CLEAR),
- };
- static const struct regmap_access_table sdm845_llcc_bwmon_reg_read_table = {
- .no_ranges = sdm845_llcc_bwmon_reg_noread_ranges,
- .n_no_ranges = ARRAY_SIZE(sdm845_llcc_bwmon_reg_noread_ranges),
- };
- static const struct regmap_range sdm845_llcc_bwmon_reg_volatile_ranges[] = {
- regmap_reg_range(BWMON_V5_IRQ_STATUS, BWMON_V5_IRQ_STATUS),
- regmap_reg_range(BWMON_V5_ZONE_MAX(0), BWMON_V5_ZONE_MAX(3)),
- };
- static const struct regmap_access_table sdm845_llcc_bwmon_reg_volatile_table = {
- .yes_ranges = sdm845_llcc_bwmon_reg_volatile_ranges,
- .n_yes_ranges = ARRAY_SIZE(sdm845_llcc_bwmon_reg_volatile_ranges),
- };
- /*
- * Fill the cache for non-readable registers only as rest does not really
- * matter and can be read from the device.
- */
- static const struct reg_default sdm845_llcc_bwmon_reg_defaults[] = {
- { BWMON_V5_IRQ_CLEAR, 0x0 },
- { BWMON_V5_CLEAR, 0x0 },
- };
- static const struct 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_RBTREE,
- };
- static void bwmon_clear_counters(struct icc_bwmon *bwmon, bool clear_all)
- {
- unsigned int val = BWMON_CLEAR_CLEAR;
- 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);
- }
- 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.
- */
- 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(bwmon->regs[F_GLOBAL_IRQ_CLEAR],
- BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE);
- }
- static void bwmon_disable(struct icc_bwmon *bwmon)
- {
- /* Disable interrupts. Strict ordering, see bwmon_clear_irq(). */
- if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ)
- regmap_field_write(bwmon->regs[F_GLOBAL_IRQ_ENABLE], 0x0);
- regmap_field_write(bwmon->regs[F_IRQ_ENABLE], 0x0);
- /*
- * Disable bwmon. Must happen before bwmon_clear_irq() to avoid spurious
- * IRQ.
- */
- regmap_field_write(bwmon->regs[F_ENABLE], 0x0);
- }
- static void bwmon_enable(struct icc_bwmon *bwmon, unsigned int irq_enable)
- {
- /* Enable interrupts */
- if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ)
- regmap_field_write(bwmon->regs[F_GLOBAL_IRQ_ENABLE],
- BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE);
- regmap_field_write(bwmon->regs[F_IRQ_ENABLE], irq_enable);
- /* Enable bwmon */
- regmap_field_write(bwmon->regs[F_ENABLE], BWMON_ENABLE_ENABLE);
- }
- static unsigned int bwmon_kbps_to_count(struct icc_bwmon *bwmon,
- unsigned int kbps)
- {
- return kbps / bwmon->data->count_unit_kb;
- }
- static void bwmon_set_threshold(struct icc_bwmon *bwmon,
- struct regmap_field *reg, unsigned int kbps)
- {
- unsigned int thres;
- thres = mult_frac(bwmon_kbps_to_count(bwmon, kbps),
- bwmon->data->sample_ms, MSEC_PER_SEC);
- regmap_field_write(reg, thres);
- }
- static void bwmon_start(struct icc_bwmon *bwmon)
- {
- const struct icc_bwmon_data *data = bwmon->data;
- int window;
- 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);
- bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_HIGH],
- data->default_highbw_kbps);
- bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_MED],
- data->default_medbw_kbps);
- bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_LOW],
- data->default_lowbw_kbps);
- regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE0],
- BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT);
- regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE1],
- data->zone1_thres_count);
- regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE2],
- BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT);
- regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE3],
- data->zone3_thres_count);
- regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE0],
- BWMON_ZONE_ACTIONS_ZONE0);
- regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE1],
- BWMON_ZONE_ACTIONS_ZONE1);
- regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE2],
- BWMON_ZONE_ACTIONS_ZONE2);
- regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE3],
- BWMON_ZONE_ACTIONS_ZONE3);
- 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;
- 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);
- 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(1);
- else if (bwmon->target_kbps <= bwmon->min_bw_kbps)
- irq_enable = BIT(3);
- else
- irq_enable = BWMON_IRQ_ENABLE_MASK;
- bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_HIGH],
- up_kbps);
- bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_MED],
- down_kbps);
- bwmon_clear_counters(bwmon, false);
- 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_init_regmap(struct platform_device *pdev,
- struct icc_bwmon *bwmon)
- {
- struct device *dev = &pdev->dev;
- void __iomem *base;
- struct regmap *map;
- 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");
- BUILD_BUG_ON(ARRAY_SIZE(msm8998_bwmon_reg_fields) != F_NUM_FIELDS);
- BUILD_BUG_ON(ARRAY_SIZE(sdm845_llcc_bwmon_reg_fields) != F_NUM_FIELDS);
- return devm_regmap_field_bulk_alloc(dev, map, bwmon->regs,
- bwmon->data->regmap_fields,
- F_NUM_FIELDS);
- }
- 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;
- 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");
- 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");
- 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);
- return 0;
- }
- static int bwmon_remove(struct platform_device *pdev)
- {
- struct icc_bwmon *bwmon = platform_get_drvdata(pdev);
- bwmon_disable(bwmon);
- return 0;
- }
- static const struct icc_bwmon_data msm8998_bwmon_data = {
- .sample_ms = 4,
- .count_unit_kb = 64,
- .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,
- .quirks = BWMON_HAS_GLOBAL_IRQ,
- .regmap_fields = msm8998_bwmon_reg_fields,
- .regmap_cfg = &msm8998_bwmon_regmap_cfg,
- };
- static const struct icc_bwmon_data sdm845_llcc_bwmon_data = {
- .sample_ms = 4,
- .count_unit_kb = 1024,
- .default_highbw_kbps = 800 * 1024, /* 800 MBps */
- .default_medbw_kbps = 256 * 1024, /* 256 MBps */
- .default_lowbw_kbps = 0,
- .zone1_thres_count = 16,
- .zone3_thres_count = 1,
- .regmap_fields = sdm845_llcc_bwmon_reg_fields,
- .regmap_cfg = &sdm845_llcc_bwmon_regmap_cfg,
- };
- static const struct icc_bwmon_data sc7280_llcc_bwmon_data = {
- .sample_ms = 4,
- .count_unit_kb = 64,
- .default_highbw_kbps = 800 * 1024, /* 800 MBps */
- .default_medbw_kbps = 256 * 1024, /* 256 MBps */
- .default_lowbw_kbps = 0,
- .zone1_thres_count = 16,
- .zone3_thres_count = 1,
- .quirks = BWMON_NEEDS_FORCE_CLEAR,
- .regmap_fields = sdm845_llcc_bwmon_reg_fields,
- .regmap_cfg = &sdm845_llcc_bwmon_regmap_cfg,
- };
- static const struct of_device_id bwmon_of_match[] = {
- {
- .compatible = "qcom,msm8998-bwmon",
- .data = &msm8998_bwmon_data
- }, {
- .compatible = "qcom,sdm845-llcc-bwmon",
- .data = &sdm845_llcc_bwmon_data
- }, {
- .compatible = "qcom,sc7280-llcc-bwmon",
- .data = &sc7280_llcc_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 <[email protected]>");
- MODULE_DESCRIPTION("QCOM BWMON driver");
- MODULE_LICENSE("GPL");
|