/* * * Copyright (C) 2010 Google, Inc. * * Author: * Colin Cross * * Copyright (c) 2010-2016 NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #ifndef _TEGRA_DVFS_H_ #define _TEGRA_DVFS_H_ #include #include #define MAX_DVFS_FREQS 40 #define MAX_DVFS_TABLES 80 #define DVFS_RAIL_STATS_TOP_BIN 100 #define MAX_THERMAL_LIMITS 8 #define MAX_THERMAL_RANGES (MAX_THERMAL_LIMITS + 1) struct clk; struct dvfs_rail; /* * dvfs_relationship between to rails, "from" and "to" * when the rail changes, it will call dvfs_rail_update on the rails * in the relationship_to list. * when determining the voltage to set a rail to, it will consider each * rail in the relationship_from list. */ struct dvfs_relationship { struct dvfs_rail *to; struct dvfs_rail *from; int (*solve)(struct dvfs_rail *, struct dvfs_rail *); struct list_head to_node; /* node in relationship_to list */ struct list_head from_node; /* node in relationship_from list */ bool solved_at_nominal; bool solved_at_suspend; }; struct rail_stats { ktime_t time_at_mv[DVFS_RAIL_STATS_TOP_BIN + 1]; ktime_t last_update; int last_index; bool off; int bin_uV; }; struct rail_alignment { int offset_uv; int step_uv; /* Step voltage */ }; struct dvfs_rail { const char *reg_id; int min_millivolts; int max_millivolts; int reg_max_millivolts; int nominal_millivolts; /* Max DVFS voltage */ int fixed_millivolts; int override_millivolts; int min_override_millivolts; int override_unresolved; int (*resolve_override)(int mv); const int *therm_mv_floors; const int *therm_mv_dfll_floors; int therm_mv_floors_num; const int *therm_mv_caps; int therm_mv_caps_num; const int *simon_vmin_offsets; int simon_vmin_offs_num; int simon_domain; int step; int step_up; bool jmp_to_zero; bool in_band_pm; bool disabled; bool updating; bool resolving_to; bool rate_set; bool dt_reg_fixed; bool dt_reg_pwm; struct device_node *dt_node; /* device tree rail node */ struct list_head node; /* node in dvfs_rail_list */ struct list_head dvfs; /* list head of attached dvfs clocks */ struct list_head relationships_to; struct list_head relationships_from; struct regulator *reg; int millivolts; int new_millivolts; int dbg_mv_offs; int boot_millivolts; int disable_millivolts; int suspend_millivolts; /* voltage setting set during suspend */ bool suspended; bool dfll_mode; /* DFLL mode ON/OFF */ bool dfll_mode_updating; int therm_floor_idx; /* index of vmin thermal range */ int therm_cap_idx; /* index of vmax thermal range */ int therm_scale_idx; /* index of thermal DVFS or clk source range */ /* Trips for vmin cooling device */ struct tegra_cooling_device *vmin_cdev; /* Trips for vmax cooling device */ struct tegra_cooling_device *vmax_cdev; /* Trips for thermal DVFS cooling device */ struct tegra_cooling_device *vts_cdev; /* Trips for clock source switch cooling device */ struct tegra_cooling_device *clk_switch_cdev; /* Vmax capping method */ int (*apply_vmax_cap)(int *cap_idx, int new_idx, int cap_mv); struct rail_alignment alignment; struct rail_stats stats; const char *version; }; /* * dfll_range - * DFLL_RANGE_NONE : DFLL is not used * DFLL_RANGE_ALL_RATES : DFLL is is used for all CPU rates * DFLL_RANGE_HIGH_RATES : DFLL is used only for high rates * above crossover with PLL dvfs curve */ enum dfll_range { DFLL_RANGE_NONE = 0, DFLL_RANGE_ALL_RATES, DFLL_RANGE_HIGH_RATES, }; /* DFLL usage is under thermal cooling device control */ #define TEGRA_USE_DFLL_CDEV_CNTRL 3 extern int tegra_override_dfll_range; /* DVFS settings specific for DFLL clock source */ struct dvfs_dfll_data { u32 tune0; u32 tune0_high_mv; u32 tune0_simon_mask; u32 tune1; bool tune0_low_at_cold; unsigned long droop_rate_min; unsigned long use_dfll_rate_min; unsigned long out_rate_min; unsigned long max_rate_boost; /* Boot frequency if DFLL is enabled by boot-loader; zero otherwise */ unsigned long dfll_boot_khz; int tune_high_min_millivolts; int tune_high_margin_mv; int min_millivolts; enum dfll_range range; void (*tune_trimmers)(bool trim_high); unsigned int (*is_bypass_down)(void); }; /* DVFS settings specific for PLL clock source */ struct dvfs_pll_data { int min_millivolts; }; struct dvfs { const char *clk_name; struct clk *clk; int speedo_id; int process_id; /* Must be initialized before tegra_dvfs_init */ int freqs_mult; unsigned long freqs[MAX_DVFS_FREQS]; const int *millivolts; const int *peak_millivolts; /* voltage settings as per DFLL clock source */ const int *dfll_millivolts; struct dvfs_rail *dvfs_rail; bool auto_dvfs; bool can_override; bool defer_override; bool boost_table; /* Filled in by tegra_dvfs_init */ int max_millivolts; int num_freqs; struct dvfs_dfll_data dfll_data; /* Set if clock rate is adapted by h/w to voltage noise (noise-aware) */ bool na_dvfs; /* Indicates thermal DVFS on/off */ bool therm_dvfs; /* Maximum rate safe at minimum voltage across all thermal ranges */ unsigned long fmax_at_vmin_safe_t; /* * Current dvfs point = { index into V/F arrays, voltage, rate request } * cur_index is invalid - set to MAX_DVFS_FREQS - if cur_rate and * cur_millivolts are set to zero (may happen when dvfs rate is not set * initially, or clock is disabled). */ int cur_index; int cur_millivolts; unsigned long cur_rate; unsigned long *alt_freqs; bool use_alt_freqs; long dbg_hz_offs; struct list_head node; struct list_head debug_node; struct list_head reg_node; struct mutex *lock; }; /* CVB coefficients */ struct cvb_dvfs_parameters { int c0; int c1; int c2; int c3; int c4; int c5; }; struct cvb_dvfs_table { unsigned long freq; /* Coeffs for voltage calculation, when dfll clock source is selected */ struct cvb_dvfs_parameters cvb_dfll_param; /* Coeffs for voltage calculation, when pll clock source is selected */ struct cvb_dvfs_parameters cvb_pll_param; }; struct cvb_dvfs { int speedo_id; int process_id; /* Tuning parameters for dfll */ struct dvfs_dfll_data dfll_tune_data; /* tuning parameters for pll clock */ struct dvfs_pll_data pll_tune_data; /* dvfs Max voltage */ int max_mv; /* dvfs Max frequency */ unsigned long max_freq; int freqs_mult; /* scaling values for voltage calculation */ int speedo_scale; int voltage_scale; int thermal_scale; struct cvb_dvfs_table cvb_vmin; /* CVB table for various frequencies */ struct cvb_dvfs_table cvb_table[MAX_DVFS_FREQS]; /* Trips for minimal voltage settings per thermal ranges */ int vmin_trips_table[MAX_THERMAL_LIMITS]; int therm_floors_table[MAX_THERMAL_LIMITS]; /* Trips for thermal DVFS per thermal ranges */ int vts_trips_table[MAX_THERMAL_LIMITS]; /* Trips for clock source change per thermal ranges */ int clk_switch_trips[MAX_THERMAL_LIMITS]; }; #define cpu_cvb_dvfs cvb_dvfs #define gpu_cvb_dvfs cvb_dvfs #define core_cvb_dvfs cvb_dvfs extern struct dvfs_rail *tegra_cpu_rail; extern struct dvfs_rail *tegra_gpu_rail; extern struct dvfs_rail *tegra_core_rail; struct dvfs_data { struct dvfs_rail *rail; struct dvfs *tables; int *millivolts; unsigned int num_tables; unsigned int num_voltages; }; #ifdef CONFIG_OF typedef int (*of_tegra_dvfs_init_cb_t)(struct device_node *); int of_tegra_dvfs_init(const struct of_device_id *matches); int of_tegra_dvfs_rail_node_parse(struct device_node *rail_dn, struct dvfs_rail *rail); int of_tegra_dvfs_rail_get_cdev_trips( struct tegra_cooling_device *tegra_cdev, int *therm_trips_table, int *therm_limits_table, struct rail_alignment *align, bool up); #else static inline int of_tegra_dvfs_init(const struct of_device_id *matches) { return -ENODATA; } static inline int of_tegra_dvfs_rail_node_parse(struct device_node *rail_dn, struct dvfs_rail *rail) { return -ENODEV; } static inline int of_tegra_dvfs_rail_get_cdev_trips( struct tegra_cooling_device *tegra_cdev, int *therm_trips_table, int *therm_limits_table, struct rail_alignment *align, bool up) { return -ENODEV; } #endif void tegra21x_init_dvfs(void); int dvfs_debugfs_init(struct dentry *clk_debugfs_root); int tegra_dvfs_rail_connect_regulators(void); int tegra_dvfs_rail_register_notifiers(void); int tegra_dvfs_init_rails(struct dvfs_rail *dvfs_rails[], int n); void tegra_dvfs_add_relationships(struct dvfs_relationship *rels, int n); void tegra_dvfs_rail_enable(struct dvfs_rail *rail); void tegra_dvfs_rail_disable(struct dvfs_rail *rail); int tegra_dvfs_rail_power_up(struct dvfs_rail *rail); int tegra_dvfs_rail_power_down(struct dvfs_rail *rail); bool tegra_dvfs_is_rail_up(struct dvfs_rail *rail); bool tegra_dvfs_rail_updating(struct clk *clk); void tegra_dvfs_rail_off(struct dvfs_rail *rail, ktime_t now); void tegra_dvfs_rail_on(struct dvfs_rail *rail, ktime_t now); void tegra_dvfs_rail_pause(struct dvfs_rail *rail, ktime_t delta, bool on); int tegra_dvfs_rail_set_mode(struct dvfs_rail *rail, unsigned int mode); int tegra_dvfs_rail_register_notifier(struct dvfs_rail *rail, struct notifier_block *nb); int tegra_dvfs_rail_unregister_notifier(struct dvfs_rail *rail, struct notifier_block *nb); struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id); int tegra_dvfs_rail_get_current_millivolts(struct dvfs_rail *rail); int tegra_dvfs_override_core_cap_apply(int level); int tegra_dvfs_therm_vmax_core_cap_apply(int *cap_idx, int new_idx, int level); int tegra_dvfs_alt_freqs_install(struct dvfs *d, unsigned long *alt_freqs); void tegra_dvfs_alt_freqs_install_always( struct dvfs *d, unsigned long *alt_freqs); int tegra_dvfs_replace_voltage_table(struct dvfs *d, const int *new_millivolts); int tegra_dvfs_butterfly_throttle(struct clk *c1, unsigned long *rate1, struct clk *c2, unsigned long *rate2); int tegra_dvfs_dfll_mode_set(struct dvfs *d, unsigned long rate); int tegra_dvfs_dfll_mode_clear(struct dvfs *d, unsigned long rate); int tegra_clk_dfll_range_control(enum dfll_range use_dfll); bool tegra_dvfs_is_dfll_scale(struct dvfs *d, unsigned long rate); bool tegra_dvfs_is_dfll_range(struct dvfs *d, unsigned long rate); int tegra_dvfs_swap_dfll_range(struct dvfs *d, int range, int *old_range); int tegra_dvfs_set_dfll_range(struct dvfs *d, int range); int tegra_dvfs_rail_set_reg_volatile(struct dvfs_rail *rail, bool set); void tegra_clip_freqs(u32 *freqs, int *num_freqs, int freqs_mult, const unsigned long *rates_ladder, int num_rates, bool up); struct tegra_cooling_device *tegra_dvfs_get_cpu_vmax_cdev(void); struct tegra_cooling_device *tegra_dvfs_get_cpu_vmin_cdev(void); struct tegra_cooling_device *tegra_dvfs_get_core_vmax_cdev(void); struct tegra_cooling_device *tegra_dvfs_get_core_vmin_cdev(void); struct tegra_cooling_device *tegra_dvfs_get_gpu_vmin_cdev(void); struct tegra_cooling_device *tegra_dvfs_get_gpu_vts_cdev(void); struct tegra_cooling_device *tegra_dvfs_get_cpu_clk_switch_cdev(void); void tegra_dvfs_rail_init_vmin_thermal_profile( int *therm_trips_table, int *therm_floors_table, struct dvfs_rail *rail, struct dvfs_dfll_data *d); void tegra_dvfs_rail_init_vmax_thermal_profile( int *therm_trips_table, int *therm_caps_table, struct dvfs_rail *rail, struct dvfs_dfll_data *d); int tegra_dvfs_rail_of_init_vmin_thermal_profile( int *therm_trips_table, int *therm_floors_table, struct dvfs_rail *rail, struct dvfs_dfll_data *d); int tegra_dvfs_rail_of_init_vmax_thermal_profile( int *therm_trips_table, int *therm_caps_table, struct dvfs_rail *rail, struct dvfs_dfll_data *d); int tegra_dvfs_rail_init_clk_switch_thermal_profile( int *clk_switch_trips, struct dvfs_rail *rail); int tegra_dvfs_rail_init_thermal_dvfs_trips( int *therm_trips_table, struct dvfs_rail *rail); int tegra_dvfs_init_thermal_dvfs_voltages(int *millivolts, int *peak_millivolts, int freqs_num, int ranges_num, struct dvfs *d); int tegra_dvfs_rail_dfll_mode_set_cold(struct dvfs_rail *rail, struct clk *dfll_clk); int tegra_dvfs_rail_get_thermal_floor(struct dvfs_rail *rail); void tegra_dvfs_rail_register_vmax_cdev(struct dvfs_rail *rail); #ifdef CONFIG_TEGRA_VDD_CORE_OVERRIDE int tegra_dvfs_resolve_override(struct clk *c, unsigned long max_rate); int tegra_dvfs_rail_get_override_floor(struct dvfs_rail *rail); #else static inline int tegra_dvfs_resolve_override(struct clk *c, unsigned long rate) { return 0; } static inline int tegra_dvfs_rail_get_override_floor(struct dvfs_rail *rail) { return 0; } #endif static inline int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail) { return 0; } static inline int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail) { return 0; } bool tegra_dvfs_is_dfll_bypass(void); static inline bool tegra_dvfs_rail_is_dfll_mode(struct dvfs_rail *rail) { return rail ? rail->dfll_mode : false; } static inline void tegra_dvfs_rail_mode_updating(struct dvfs_rail *rail, bool updating) { if (rail) rail->dfll_mode_updating = updating; } static inline void tegra_dvfs_set_dfll_tune_trimmers( struct dvfs *d, void (*tune_trimmers)(bool trim_high)) { if (d->dfll_data.tune_high_min_millivolts) d->dfll_data.tune_trimmers = tune_trimmers; } #endif