xref: /kernel/linux/linux-6.6/tools/thermal/tmon/pid.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * pid.c PID controller for testing cooling devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Intel Corporation. All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author Name Jacob Pan <jacob.jun.pan@linux.intel.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <unistd.h>
1162306a36Sopenharmony_ci#include <stdio.h>
1262306a36Sopenharmony_ci#include <stdlib.h>
1362306a36Sopenharmony_ci#include <string.h>
1462306a36Sopenharmony_ci#include <stdint.h>
1562306a36Sopenharmony_ci#include <sys/types.h>
1662306a36Sopenharmony_ci#include <dirent.h>
1762306a36Sopenharmony_ci#include <libintl.h>
1862306a36Sopenharmony_ci#include <ctype.h>
1962306a36Sopenharmony_ci#include <assert.h>
2062306a36Sopenharmony_ci#include <time.h>
2162306a36Sopenharmony_ci#include <limits.h>
2262306a36Sopenharmony_ci#include <math.h>
2362306a36Sopenharmony_ci#include <sys/stat.h>
2462306a36Sopenharmony_ci#include <syslog.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "tmon.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/**************************************************************************
2962306a36Sopenharmony_ci * PID (Proportional-Integral-Derivative) controller is commonly used in
3062306a36Sopenharmony_ci * linear control system, consider the process.
3162306a36Sopenharmony_ci * G(s) = U(s)/E(s)
3262306a36Sopenharmony_ci * kp = proportional gain
3362306a36Sopenharmony_ci * ki = integral gain
3462306a36Sopenharmony_ci * kd = derivative gain
3562306a36Sopenharmony_ci * Ts
3662306a36Sopenharmony_ci * We use type C Alan Bradley equation which takes set point off the
3762306a36Sopenharmony_ci * output dependency in P and D term.
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci *   y[k] = y[k-1] - kp*(x[k] - x[k-1]) + Ki*Ts*e[k] - Kd*(x[k]
4062306a36Sopenharmony_ci *          - 2*x[k-1]+x[k-2])/Ts
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci ***********************************************************************/
4462306a36Sopenharmony_cistruct pid_params p_param;
4562306a36Sopenharmony_ci/* cached data from previous loop */
4662306a36Sopenharmony_cistatic double xk_1, xk_2; /* input temperature x[k-#] */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * TODO: make PID parameters tuned automatically,
5062306a36Sopenharmony_ci * 1. use CPU burn to produce open loop unit step response
5162306a36Sopenharmony_ci * 2. calculate PID based on Ziegler-Nichols rule
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * add a flag for tuning PID
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_ciint init_thermal_controller(void)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* init pid params */
5962306a36Sopenharmony_ci	p_param.ts = ticktime;
6062306a36Sopenharmony_ci	/* TODO: get it from TUI tuning tab */
6162306a36Sopenharmony_ci	p_param.kp = .36;
6262306a36Sopenharmony_ci	p_param.ki = 5.0;
6362306a36Sopenharmony_ci	p_param.kd = 0.19;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	p_param.t_target = target_temp_user;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_civoid controller_reset(void)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	/* TODO: relax control data when not over thermal limit */
7362306a36Sopenharmony_ci	syslog(LOG_DEBUG, "TC inactive, relax p-state\n");
7462306a36Sopenharmony_ci	p_param.y_k = 0.0;
7562306a36Sopenharmony_ci	xk_1 = 0.0;
7662306a36Sopenharmony_ci	xk_2 = 0.0;
7762306a36Sopenharmony_ci	set_ctrl_state(0);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* To be called at time interval Ts. Type C PID controller.
8162306a36Sopenharmony_ci *    y[k] = y[k-1] - kp*(x[k] - x[k-1]) + Ki*Ts*e[k] - Kd*(x[k]
8262306a36Sopenharmony_ci *          - 2*x[k-1]+x[k-2])/Ts
8362306a36Sopenharmony_ci * TODO: add low pass filter for D term
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_ci#define GUARD_BAND (2)
8662306a36Sopenharmony_civoid controller_handler(const double xk, double *yk)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	double ek;
8962306a36Sopenharmony_ci	double p_term, i_term, d_term;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	ek = p_param.t_target - xk; /* error */
9262306a36Sopenharmony_ci	if (ek >= 3.0) {
9362306a36Sopenharmony_ci		syslog(LOG_DEBUG, "PID: %3.1f Below set point %3.1f, stop\n",
9462306a36Sopenharmony_ci			xk, p_param.t_target);
9562306a36Sopenharmony_ci		controller_reset();
9662306a36Sopenharmony_ci		*yk = 0.0;
9762306a36Sopenharmony_ci		return;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci	/* compute intermediate PID terms */
10062306a36Sopenharmony_ci	p_term = -p_param.kp * (xk - xk_1);
10162306a36Sopenharmony_ci	i_term = p_param.kp * p_param.ki * p_param.ts * ek;
10262306a36Sopenharmony_ci	d_term = -p_param.kp * p_param.kd * (xk - 2 * xk_1 + xk_2) / p_param.ts;
10362306a36Sopenharmony_ci	/* compute output */
10462306a36Sopenharmony_ci	*yk += p_term + i_term + d_term;
10562306a36Sopenharmony_ci	/* update sample data */
10662306a36Sopenharmony_ci	xk_1 = xk;
10762306a36Sopenharmony_ci	xk_2 = xk_1;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* clamp output adjustment range */
11062306a36Sopenharmony_ci	if (*yk < -LIMIT_HIGH)
11162306a36Sopenharmony_ci		*yk = -LIMIT_HIGH;
11262306a36Sopenharmony_ci	else if (*yk > -LIMIT_LOW)
11362306a36Sopenharmony_ci		*yk = -LIMIT_LOW;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	p_param.y_k = *yk;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	set_ctrl_state(lround(fabs(p_param.y_k)));
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci}
120