/* * Fan driver for Nintendo Switch * * Copyright (c) 2018 CTCaer * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "fan.h" #include "../gfx/gfx.h" #include "../soc/gpio.h" #include "../soc/pinmux.h" #include "../soc/t210.h" #include "../utils/util.h" bool fan_init = false; void set_fan_duty(u32 duty) { if (!fan_init) { // Fan power from internal 5V regulator (battery). PINMUX_AUX(PINMUX_AUX_SATA_LED_ACTIVE) = 3; gpio_config(GPIO_PORT_A, GPIO_PIN_5, GPIO_MODE_GPIO); gpio_output_enable(GPIO_PORT_A, GPIO_PIN_5, GPIO_OUTPUT_ENABLE); gpio_write(GPIO_PORT_A, GPIO_PIN_5, GPIO_HIGH); // Fan power from USB 5V vdd. PINMUX_AUX(PINMUX_AUX_USB_VBUS_EN0) = 3; gpio_config(GPIO_PORT_CC, GPIO_PIN_4, GPIO_MODE_GPIO); gpio_output_enable(GPIO_PORT_CC, GPIO_PIN_4, GPIO_OUTPUT_ENABLE); gpio_write(GPIO_PORT_CC, GPIO_PIN_4, GPIO_HIGH); // Fan tachometer. PINMUX_AUX(PINMUX_AUX_CAM1_PWDN) = PINMUX_PULL_UP | PINMUX_TRISTATE | PINMUX_INPUT_ENABLE | 3; gpio_output_enable(GPIO_PORT_S, GPIO_PIN_7, GPIO_OUTPUT_DISABLE); gpio_config(GPIO_PORT_S, GPIO_PIN_7, GPIO_MODE_GPIO); gpio_write(GPIO_PORT_S, GPIO_PIN_7, GPIO_LOW); PWM(PWM_CONTROLLER_PWM_CSR_1) = (1 << 31) | (255 << 16); // Max PWM to disable fan. PINMUX_AUX(PINMUX_AUX_LCD_GPIO2) = 1; // Set source to PWM1. gpio_config(GPIO_PORT_V, GPIO_PIN_4, GPIO_MODE_SPIO); // Fan power mode. fan_init = true; } if (duty > 236) duty = 236; // Inverted polarity. u32 inv_duty = 236 - duty; // If disabled send a 0 duty. if (inv_duty == 236) PWM(PWM_CONTROLLER_PWM_CSR_1) = PWM_CSR_EN | (1 << 24); else // Set PWM duty. PWM(PWM_CONTROLLER_PWM_CSR_1) = PWM_CSR_EN | (inv_duty << 16); } void get_fan_speed(u32 *duty, u32 *rpm) { if (rpm) { u32 irq_count = 0; bool should_read = true; bool irq_val = 0; // Poll irqs for 2 seconds. int timer = get_tmr_us() + 1000000; while (timer - get_tmr_us()) { irq_val = gpio_read(GPIO_PORT_S, GPIO_PIN_7); if (irq_val && should_read) { irq_count++; should_read = false; } else if (!irq_val) should_read = true; } // Calculate rpm based on triggered interrupts. *rpm = 60000000 / ((1000000 * 2) / irq_count); } if (duty) *duty = 236 - ((PWM(PWM_CONTROLLER_PWM_CSR_1) >> 16) & 0xFF); }