Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/thermal/fan.c
1476 views
1
/*
2
* Fan driver for Nintendo Switch
3
*
4
* Copyright (c) 2018-2024 CTCaer
5
*
6
* This program is free software; you can redistribute it and/or modify it
7
* under the terms and conditions of the GNU General Public License,
8
* version 2, as published by the Free Software Foundation.
9
*
10
* This program is distributed in the hope it will be useful, but WITHOUT
11
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13
* more details.
14
*
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#include <thermal/fan.h>
20
#include <power/regulator_5v.h>
21
#include <soc/bpmp.h>
22
#include <soc/clock.h>
23
#include <soc/fuse.h>
24
#include <soc/gpio.h>
25
#include <soc/hw_init.h>
26
#include <soc/pinmux.h>
27
#include <soc/timer.h>
28
#include <soc/t210.h>
29
30
void fan_set_duty(u32 duty)
31
{
32
static bool fan_init = false;
33
static u16 curr_duty = -1;
34
35
if (duty > 236)
36
duty = 236;
37
38
if (curr_duty == duty)
39
return;
40
41
curr_duty = duty;
42
43
if (!fan_init)
44
{
45
// Fan tachometer.
46
u32 pull_resistor = hw_get_chip_id() == GP_HIDREV_MAJOR_T210 ? PINMUX_PULL_UP : 0;
47
PINMUX_AUX(PINMUX_AUX_CAM1_PWDN) = PINMUX_TRISTATE | PINMUX_INPUT_ENABLE | pull_resistor | 1;
48
gpio_direction_input(GPIO_PORT_S, GPIO_PIN_7);
49
50
// Enable PWM if disabled.
51
if (fuse_read_hw_type() == FUSE_NX_HW_TYPE_AULA)
52
clock_enable_pwm();
53
54
PWM(PWM_CONTROLLER_PWM_CSR_1) = PWM_CSR_EN | (0x100 << 16); // Max PWM to disable fan.
55
56
PINMUX_AUX(PINMUX_AUX_LCD_GPIO2) = 1; // Set source to PWM1.
57
gpio_config(GPIO_PORT_V, GPIO_PIN_4, GPIO_MODE_SPIO); // Fan power mode.
58
59
fan_init = true;
60
}
61
62
// Inverted polarity.
63
u32 inv_duty = 236 - duty;
64
65
// If disabled send a 0 duty.
66
if (inv_duty == 236)
67
{
68
PWM(PWM_CONTROLLER_PWM_CSR_1) = PWM_CSR_EN | (0x100 << 16); // Bit 24 is absolute 0%.
69
regulator_5v_disable(REGULATOR_5V_FAN);
70
71
// Disable fan.
72
PINMUX_AUX(PINMUX_AUX_LCD_GPIO2) = PINMUX_INPUT_ENABLE | PINMUX_PARKED |
73
PINMUX_TRISTATE | PINMUX_PULL_DOWN; // Set source to PWM1.
74
}
75
else // Set PWM duty.
76
{
77
// Fan power supply.
78
regulator_5v_enable(REGULATOR_5V_FAN);
79
PWM(PWM_CONTROLLER_PWM_CSR_1) = PWM_CSR_EN | (inv_duty << 16);
80
81
// Enable fan.
82
PINMUX_AUX(PINMUX_AUX_LCD_GPIO2) = 1; // Set source to PWM1.
83
}
84
}
85
86
void fan_get_speed(u32 *duty, u32 *rpm)
87
{
88
if (rpm)
89
{
90
u32 irq_count = 0;
91
bool should_read = true;
92
93
// Poll irqs for 2 seconds. (5 seconds for accurate count).
94
int timer = get_tmr_us() + 2000000;
95
while ((timer - get_tmr_us()) > 0)
96
{
97
bool irq_val = gpio_read(GPIO_PORT_S, GPIO_PIN_7);
98
if (irq_val && should_read)
99
{
100
irq_count++;
101
should_read = false;
102
}
103
else if (!irq_val)
104
should_read = true;
105
}
106
107
// Halve the irq count.
108
irq_count /= 2;
109
110
// Calculate rpm based on triggered interrupts.
111
*rpm = irq_count * (60 / 2);
112
}
113
114
if (duty)
115
*duty = 236 - ((PWM(PWM_CONTROLLER_PWM_CSR_1) >> 16) & 0xFF);
116
}
117
118
void fan_set_from_temp(u32 temp)
119
{
120
if (temp >= 52)
121
fan_set_duty(102);
122
else if (temp >= 47)
123
fan_set_duty(76);
124
else if (temp >= 42)
125
fan_set_duty(51);
126
else if (temp <= 39)
127
fan_set_duty(0);
128
}
129
130