cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cvb.c (4002B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Utility functions for parsing Tegra CVB voltage tables
      4 *
      5 * Copyright (C) 2012-2019 NVIDIA Corporation.  All rights reserved.
      6 */
      7#include <linux/err.h>
      8#include <linux/kernel.h>
      9#include <linux/pm_opp.h>
     10
     11#include "cvb.h"
     12
     13/* cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) */
     14static inline int get_cvb_voltage(int speedo, int s_scale,
     15				  const struct cvb_coefficients *cvb)
     16{
     17	int mv;
     18
     19	/* apply only speedo scale: output mv = cvb_mv * v_scale */
     20	mv = DIV_ROUND_CLOSEST(cvb->c2 * speedo, s_scale);
     21	mv = DIV_ROUND_CLOSEST((mv + cvb->c1) * speedo, s_scale) + cvb->c0;
     22	return mv;
     23}
     24
     25static int round_cvb_voltage(int mv, int v_scale,
     26			     const struct rail_alignment *align)
     27{
     28	/* combined: apply voltage scale and round to cvb alignment step */
     29	int uv;
     30	int step = (align->step_uv ? : 1000) * v_scale;
     31	int offset = align->offset_uv * v_scale;
     32
     33	uv = max(mv * 1000, offset) - offset;
     34	uv = DIV_ROUND_UP(uv, step) * align->step_uv + align->offset_uv;
     35	return uv / 1000;
     36}
     37
     38enum {
     39	DOWN,
     40	UP
     41};
     42
     43static int round_voltage(int mv, const struct rail_alignment *align, int up)
     44{
     45	if (align->step_uv) {
     46		int uv;
     47
     48		uv = max(mv * 1000, align->offset_uv) - align->offset_uv;
     49		uv = (uv + (up ? align->step_uv - 1 : 0)) / align->step_uv;
     50		return (uv * align->step_uv + align->offset_uv) / 1000;
     51	}
     52	return mv;
     53}
     54
     55static int build_opp_table(struct device *dev, const struct cvb_table *table,
     56			   struct rail_alignment *align,
     57			   int speedo_value, unsigned long max_freq)
     58{
     59	int i, ret, dfll_mv, min_mv, max_mv;
     60
     61	min_mv = round_voltage(table->min_millivolts, align, UP);
     62	max_mv = round_voltage(table->max_millivolts, align, DOWN);
     63
     64	for (i = 0; i < MAX_DVFS_FREQS; i++) {
     65		const struct cvb_table_freq_entry *entry = &table->entries[i];
     66
     67		if (!entry->freq || (entry->freq > max_freq))
     68			break;
     69
     70		dfll_mv = get_cvb_voltage(speedo_value, table->speedo_scale,
     71					  &entry->coefficients);
     72		dfll_mv = round_cvb_voltage(dfll_mv, table->voltage_scale,
     73					    align);
     74		dfll_mv = clamp(dfll_mv, min_mv, max_mv);
     75
     76		ret = dev_pm_opp_add(dev, entry->freq, dfll_mv * 1000);
     77		if (ret)
     78			return ret;
     79	}
     80
     81	return 0;
     82}
     83
     84/**
     85 * tegra_cvb_add_opp_table - build OPP table from Tegra CVB tables
     86 * @dev: the struct device * for which the OPP table is built
     87 * @tables: array of CVB tables
     88 * @count: size of the previously mentioned array
     89 * @align: parameters of the regulator step and offset
     90 * @process_id: process id of the HW module
     91 * @speedo_id: speedo id of the HW module
     92 * @speedo_value: speedo value of the HW module
     93 * @max_freq: highest safe clock rate
     94 *
     95 * On Tegra, a CVB table encodes the relationship between operating voltage
     96 * and safe maximal frequency for a given module (e.g. GPU or CPU). This
     97 * function calculates the optimal voltage-frequency operating points
     98 * for the given arguments and exports them via the OPP library for the
     99 * given @dev. Returns a pointer to the struct cvb_table that matched
    100 * or an ERR_PTR on failure.
    101 */
    102const struct cvb_table *
    103tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables,
    104			size_t count, struct rail_alignment *align,
    105			int process_id, int speedo_id, int speedo_value,
    106			unsigned long max_freq)
    107{
    108	size_t i;
    109	int ret;
    110
    111	for (i = 0; i < count; i++) {
    112		const struct cvb_table *table = &tables[i];
    113
    114		if (table->speedo_id != -1 && table->speedo_id != speedo_id)
    115			continue;
    116
    117		if (table->process_id != -1 && table->process_id != process_id)
    118			continue;
    119
    120		ret = build_opp_table(dev, table, align, speedo_value,
    121				      max_freq);
    122		return ret ? ERR_PTR(ret) : table;
    123	}
    124
    125	return ERR_PTR(-EINVAL);
    126}
    127
    128void tegra_cvb_remove_opp_table(struct device *dev,
    129				const struct cvb_table *table,
    130				unsigned long max_freq)
    131{
    132	unsigned int i;
    133
    134	for (i = 0; i < MAX_DVFS_FREQS; i++) {
    135		const struct cvb_table_freq_entry *entry = &table->entries[i];
    136
    137		if (!entry->freq || (entry->freq > max_freq))
    138			break;
    139
    140		dev_pm_opp_remove(dev, entry->freq);
    141	}
    142}