10ab88e23SCryolitia PukNgae // SPDX-License-Identifier: GPL-2.0+ 20ab88e23SCryolitia PukNgae 30ab88e23SCryolitia PukNgae /* Platform driver for GPD devices that expose fan control via hwmon sysfs. 40ab88e23SCryolitia PukNgae * 50ab88e23SCryolitia PukNgae * Fan control is provided via pwm interface in the range [0-255]. 60ab88e23SCryolitia PukNgae * Each model has a different range in the EC, the written value is scaled to 70ab88e23SCryolitia PukNgae * accommodate for that. 80ab88e23SCryolitia PukNgae * 90ab88e23SCryolitia PukNgae * Based on this repo: 100ab88e23SCryolitia PukNgae * https://github.com/Cryolitia/gpd-fan-driver 110ab88e23SCryolitia PukNgae * 120ab88e23SCryolitia PukNgae * Copyright (c) 2024 Cryolitia PukNgae 130ab88e23SCryolitia PukNgae */ 140ab88e23SCryolitia PukNgae 150ab88e23SCryolitia PukNgae #include <linux/acpi.h> 160ab88e23SCryolitia PukNgae #include <linux/dmi.h> 170ab88e23SCryolitia PukNgae #include <linux/hwmon.h> 180ab88e23SCryolitia PukNgae #include <linux/ioport.h> 190ab88e23SCryolitia PukNgae #include <linux/kernel.h> 200ab88e23SCryolitia PukNgae #include <linux/module.h> 210ab88e23SCryolitia PukNgae #include <linux/platform_device.h> 220ab88e23SCryolitia PukNgae 230ab88e23SCryolitia PukNgae #define DRIVER_NAME "gpdfan" 240ab88e23SCryolitia PukNgae #define GPD_PWM_CTR_OFFSET 0x1841 250ab88e23SCryolitia PukNgae 260ab88e23SCryolitia PukNgae static char *gpd_fan_board = ""; 270ab88e23SCryolitia PukNgae module_param(gpd_fan_board, charp, 0444); 280ab88e23SCryolitia PukNgae 290ab88e23SCryolitia PukNgae // EC read/write locker, protecting a sequence of EC operations 300ab88e23SCryolitia PukNgae static DEFINE_MUTEX(gpd_fan_sequence_lock); 310ab88e23SCryolitia PukNgae 320ab88e23SCryolitia PukNgae enum gpd_board { 330ab88e23SCryolitia PukNgae win_mini, 340ab88e23SCryolitia PukNgae win4_6800u, 350ab88e23SCryolitia PukNgae win_max_2, 360ab88e23SCryolitia PukNgae duo, 370ab88e23SCryolitia PukNgae }; 380ab88e23SCryolitia PukNgae 390ab88e23SCryolitia PukNgae enum FAN_PWM_ENABLE { 400ab88e23SCryolitia PukNgae DISABLE = 0, 410ab88e23SCryolitia PukNgae MANUAL = 1, 420ab88e23SCryolitia PukNgae AUTOMATIC = 2, 430ab88e23SCryolitia PukNgae }; 440ab88e23SCryolitia PukNgae 450ab88e23SCryolitia PukNgae static struct { 460ab88e23SCryolitia PukNgae enum FAN_PWM_ENABLE pwm_enable; 470ab88e23SCryolitia PukNgae u8 pwm_value; 480ab88e23SCryolitia PukNgae 490ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *drvdata; 500ab88e23SCryolitia PukNgae } gpd_driver_priv; 510ab88e23SCryolitia PukNgae 520ab88e23SCryolitia PukNgae struct gpd_fan_drvdata { 530ab88e23SCryolitia PukNgae const char *board_name; // Board name for module param comparison 540ab88e23SCryolitia PukNgae const enum gpd_board board; 550ab88e23SCryolitia PukNgae 560ab88e23SCryolitia PukNgae const u8 addr_port; 570ab88e23SCryolitia PukNgae const u8 data_port; 580ab88e23SCryolitia PukNgae const u16 manual_control_enable; 590ab88e23SCryolitia PukNgae const u16 rpm_read; 600ab88e23SCryolitia PukNgae const u16 pwm_write; 610ab88e23SCryolitia PukNgae const u16 pwm_max; 620ab88e23SCryolitia PukNgae }; 630ab88e23SCryolitia PukNgae 640ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_win_mini_drvdata = { 650ab88e23SCryolitia PukNgae .board_name = "win_mini", 660ab88e23SCryolitia PukNgae .board = win_mini, 670ab88e23SCryolitia PukNgae 680ab88e23SCryolitia PukNgae .addr_port = 0x4E, 690ab88e23SCryolitia PukNgae .data_port = 0x4F, 700ab88e23SCryolitia PukNgae .manual_control_enable = 0x047A, 710ab88e23SCryolitia PukNgae .rpm_read = 0x0478, 720ab88e23SCryolitia PukNgae .pwm_write = 0x047A, 730ab88e23SCryolitia PukNgae .pwm_max = 244, 740ab88e23SCryolitia PukNgae }; 750ab88e23SCryolitia PukNgae 760ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_duo_drvdata = { 770ab88e23SCryolitia PukNgae .board_name = "duo", 780ab88e23SCryolitia PukNgae .board = duo, 790ab88e23SCryolitia PukNgae 800ab88e23SCryolitia PukNgae .addr_port = 0x4E, 810ab88e23SCryolitia PukNgae .data_port = 0x4F, 820ab88e23SCryolitia PukNgae .manual_control_enable = 0x047A, 830ab88e23SCryolitia PukNgae .rpm_read = 0x0478, 840ab88e23SCryolitia PukNgae .pwm_write = 0x047A, 850ab88e23SCryolitia PukNgae .pwm_max = 244, 860ab88e23SCryolitia PukNgae }; 870ab88e23SCryolitia PukNgae 880ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_win4_drvdata = { 890ab88e23SCryolitia PukNgae .board_name = "win4", 900ab88e23SCryolitia PukNgae .board = win4_6800u, 910ab88e23SCryolitia PukNgae 920ab88e23SCryolitia PukNgae .addr_port = 0x2E, 930ab88e23SCryolitia PukNgae .data_port = 0x2F, 940ab88e23SCryolitia PukNgae .manual_control_enable = 0xC311, 950ab88e23SCryolitia PukNgae .rpm_read = 0xC880, 960ab88e23SCryolitia PukNgae .pwm_write = 0xC311, 970ab88e23SCryolitia PukNgae .pwm_max = 127, 980ab88e23SCryolitia PukNgae }; 990ab88e23SCryolitia PukNgae 1000ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_wm2_drvdata = { 1010ab88e23SCryolitia PukNgae .board_name = "wm2", 1020ab88e23SCryolitia PukNgae .board = win_max_2, 1030ab88e23SCryolitia PukNgae 1040ab88e23SCryolitia PukNgae .addr_port = 0x4E, 1050ab88e23SCryolitia PukNgae .data_port = 0x4F, 1060ab88e23SCryolitia PukNgae .manual_control_enable = 0x0275, 1070ab88e23SCryolitia PukNgae .rpm_read = 0x0218, 1080ab88e23SCryolitia PukNgae .pwm_write = 0x1809, 1090ab88e23SCryolitia PukNgae .pwm_max = 184, 1100ab88e23SCryolitia PukNgae }; 1110ab88e23SCryolitia PukNgae 1120ab88e23SCryolitia PukNgae static const struct dmi_system_id dmi_table[] = { 1130ab88e23SCryolitia PukNgae { 1140ab88e23SCryolitia PukNgae // GPD Win Mini 1150ab88e23SCryolitia PukNgae // GPD Win Mini with AMD Ryzen 8840U 1160ab88e23SCryolitia PukNgae .matches = { 1170ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 1180ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1617-01") 1190ab88e23SCryolitia PukNgae }, 1200ab88e23SCryolitia PukNgae .driver_data = &gpd_win_mini_drvdata, 1210ab88e23SCryolitia PukNgae }, 1220ab88e23SCryolitia PukNgae { 1230ab88e23SCryolitia PukNgae // GPD Win Mini 1240ab88e23SCryolitia PukNgae // GPD Win Mini with AMD Ryzen HX370 1250ab88e23SCryolitia PukNgae .matches = { 1260ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 1270ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1617-02") 1280ab88e23SCryolitia PukNgae }, 1290ab88e23SCryolitia PukNgae .driver_data = &gpd_win_mini_drvdata, 1300ab88e23SCryolitia PukNgae }, 1310ab88e23SCryolitia PukNgae { 1320ab88e23SCryolitia PukNgae // GPD Win Mini 1330ab88e23SCryolitia PukNgae // GPD Win Mini with AMD Ryzen HX370 1340ab88e23SCryolitia PukNgae .matches = { 1350ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 1360ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1617-02-L") 1370ab88e23SCryolitia PukNgae }, 1380ab88e23SCryolitia PukNgae .driver_data = &gpd_win_mini_drvdata, 1390ab88e23SCryolitia PukNgae }, 1400ab88e23SCryolitia PukNgae { 1410ab88e23SCryolitia PukNgae // GPD Win 4 with AMD Ryzen 6800U 1420ab88e23SCryolitia PukNgae .matches = { 1430ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 1440ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"), 1450ab88e23SCryolitia PukNgae DMI_MATCH(DMI_BOARD_VERSION, "Default string"), 1460ab88e23SCryolitia PukNgae }, 1470ab88e23SCryolitia PukNgae .driver_data = &gpd_win4_drvdata, 1480ab88e23SCryolitia PukNgae }, 1490ab88e23SCryolitia PukNgae { 1500ab88e23SCryolitia PukNgae // GPD Win 4 with Ryzen 7840U 1510ab88e23SCryolitia PukNgae .matches = { 1520ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 1530ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"), 1540ab88e23SCryolitia PukNgae DMI_MATCH(DMI_BOARD_VERSION, "Ver. 1.0"), 1550ab88e23SCryolitia PukNgae }, 1560ab88e23SCryolitia PukNgae // Since 7840U, win4 uses the same drvdata as wm2 1570ab88e23SCryolitia PukNgae .driver_data = &gpd_wm2_drvdata, 1580ab88e23SCryolitia PukNgae }, 1590ab88e23SCryolitia PukNgae { 1600ab88e23SCryolitia PukNgae // GPD Win 4 with Ryzen 7840U (another) 1610ab88e23SCryolitia PukNgae .matches = { 1620ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 1630ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"), 1640ab88e23SCryolitia PukNgae DMI_MATCH(DMI_BOARD_VERSION, "Ver.1.0"), 1650ab88e23SCryolitia PukNgae }, 1660ab88e23SCryolitia PukNgae .driver_data = &gpd_wm2_drvdata, 1670ab88e23SCryolitia PukNgae }, 1680ab88e23SCryolitia PukNgae { 1690ab88e23SCryolitia PukNgae // GPD Win Max 2 with Ryzen 6800U 1700ab88e23SCryolitia PukNgae // GPD Win Max 2 2023 with Ryzen 7840U 1710ab88e23SCryolitia PukNgae // GPD Win Max 2 2024 with Ryzen 8840U 1720ab88e23SCryolitia PukNgae .matches = { 1730ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 1740ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), 1750ab88e23SCryolitia PukNgae }, 1760ab88e23SCryolitia PukNgae .driver_data = &gpd_wm2_drvdata, 1770ab88e23SCryolitia PukNgae }, 1780ab88e23SCryolitia PukNgae { 1790ab88e23SCryolitia PukNgae // GPD Win Max 2 with AMD Ryzen HX370 1800ab88e23SCryolitia PukNgae .matches = { 1810ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 1820ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1619-05"), 1830ab88e23SCryolitia PukNgae }, 1840ab88e23SCryolitia PukNgae .driver_data = &gpd_wm2_drvdata, 1850ab88e23SCryolitia PukNgae }, 1860ab88e23SCryolitia PukNgae { 1870ab88e23SCryolitia PukNgae // GPD Duo 1880ab88e23SCryolitia PukNgae .matches = { 1890ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 1900ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1622-01"), 1910ab88e23SCryolitia PukNgae }, 1920ab88e23SCryolitia PukNgae .driver_data = &gpd_duo_drvdata, 1930ab88e23SCryolitia PukNgae }, 1940ab88e23SCryolitia PukNgae { 1950ab88e23SCryolitia PukNgae // GPD Duo (another) 1960ab88e23SCryolitia PukNgae .matches = { 1970ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 1980ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1622-01-L"), 1990ab88e23SCryolitia PukNgae }, 2000ab88e23SCryolitia PukNgae .driver_data = &gpd_duo_drvdata, 2010ab88e23SCryolitia PukNgae }, 2020ab88e23SCryolitia PukNgae { 2030ab88e23SCryolitia PukNgae // GPD Pocket 4 2040ab88e23SCryolitia PukNgae .matches = { 2050ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 2060ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1628-04"), 2070ab88e23SCryolitia PukNgae }, 2080ab88e23SCryolitia PukNgae .driver_data = &gpd_win_mini_drvdata, 2090ab88e23SCryolitia PukNgae }, 2100ab88e23SCryolitia PukNgae { 2110ab88e23SCryolitia PukNgae // GPD Pocket 4 (another) 2120ab88e23SCryolitia PukNgae .matches = { 2130ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 2140ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1628-04-L"), 2150ab88e23SCryolitia PukNgae }, 2160ab88e23SCryolitia PukNgae .driver_data = &gpd_win_mini_drvdata, 2170ab88e23SCryolitia PukNgae }, 2180ab88e23SCryolitia PukNgae {} 2190ab88e23SCryolitia PukNgae }; 2200ab88e23SCryolitia PukNgae 2210ab88e23SCryolitia PukNgae static const struct gpd_fan_drvdata *gpd_module_drvdata[] = { 2220ab88e23SCryolitia PukNgae &gpd_win_mini_drvdata, &gpd_win4_drvdata, &gpd_wm2_drvdata, NULL 2230ab88e23SCryolitia PukNgae }; 2240ab88e23SCryolitia PukNgae 2250ab88e23SCryolitia PukNgae // Helper functions to handle EC read/write 2260ab88e23SCryolitia PukNgae static void gpd_ecram_read(u16 offset, u8 *val) 2270ab88e23SCryolitia PukNgae { 2280ab88e23SCryolitia PukNgae u16 addr_port = gpd_driver_priv.drvdata->addr_port; 2290ab88e23SCryolitia PukNgae u16 data_port = gpd_driver_priv.drvdata->data_port; 2300ab88e23SCryolitia PukNgae 2310ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 2320ab88e23SCryolitia PukNgae outb(0x11, data_port); 2330ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 2340ab88e23SCryolitia PukNgae outb((u8)((offset >> 8) & 0xFF), data_port); 2350ab88e23SCryolitia PukNgae 2360ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 2370ab88e23SCryolitia PukNgae outb(0x10, data_port); 2380ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 2390ab88e23SCryolitia PukNgae outb((u8)(offset & 0xFF), data_port); 2400ab88e23SCryolitia PukNgae 2410ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 2420ab88e23SCryolitia PukNgae outb(0x12, data_port); 2430ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 2440ab88e23SCryolitia PukNgae *val = inb(data_port); 2450ab88e23SCryolitia PukNgae } 2460ab88e23SCryolitia PukNgae 2470ab88e23SCryolitia PukNgae static void gpd_ecram_write(u16 offset, u8 value) 2480ab88e23SCryolitia PukNgae { 2490ab88e23SCryolitia PukNgae u16 addr_port = gpd_driver_priv.drvdata->addr_port; 2500ab88e23SCryolitia PukNgae u16 data_port = gpd_driver_priv.drvdata->data_port; 2510ab88e23SCryolitia PukNgae 2520ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 2530ab88e23SCryolitia PukNgae outb(0x11, data_port); 2540ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 2550ab88e23SCryolitia PukNgae outb((u8)((offset >> 8) & 0xFF), data_port); 2560ab88e23SCryolitia PukNgae 2570ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 2580ab88e23SCryolitia PukNgae outb(0x10, data_port); 2590ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 2600ab88e23SCryolitia PukNgae outb((u8)(offset & 0xFF), data_port); 2610ab88e23SCryolitia PukNgae 2620ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 2630ab88e23SCryolitia PukNgae outb(0x12, data_port); 2640ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 2650ab88e23SCryolitia PukNgae outb(value, data_port); 2660ab88e23SCryolitia PukNgae } 2670ab88e23SCryolitia PukNgae 2680ab88e23SCryolitia PukNgae static int gpd_generic_read_rpm(void) 2690ab88e23SCryolitia PukNgae { 2700ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 2710ab88e23SCryolitia PukNgae u8 high, low; 2720ab88e23SCryolitia PukNgae 2730ab88e23SCryolitia PukNgae gpd_ecram_read(drvdata->rpm_read, &high); 2740ab88e23SCryolitia PukNgae gpd_ecram_read(drvdata->rpm_read + 1, &low); 2750ab88e23SCryolitia PukNgae 2760ab88e23SCryolitia PukNgae return (u16)high << 8 | low; 2770ab88e23SCryolitia PukNgae } 2780ab88e23SCryolitia PukNgae 2790ab88e23SCryolitia PukNgae static void gpd_win4_init_ec(void) 2800ab88e23SCryolitia PukNgae { 2810ab88e23SCryolitia PukNgae u8 chip_id, chip_ver; 2820ab88e23SCryolitia PukNgae 2830ab88e23SCryolitia PukNgae gpd_ecram_read(0x2000, &chip_id); 2840ab88e23SCryolitia PukNgae 2850ab88e23SCryolitia PukNgae if (chip_id == 0x55) { 2860ab88e23SCryolitia PukNgae gpd_ecram_read(0x1060, &chip_ver); 2870ab88e23SCryolitia PukNgae gpd_ecram_write(0x1060, chip_ver | 0x80); 2880ab88e23SCryolitia PukNgae } 2890ab88e23SCryolitia PukNgae } 2900ab88e23SCryolitia PukNgae 2910ab88e23SCryolitia PukNgae static int gpd_win4_read_rpm(void) 2920ab88e23SCryolitia PukNgae { 2930ab88e23SCryolitia PukNgae int ret; 2940ab88e23SCryolitia PukNgae 2950ab88e23SCryolitia PukNgae ret = gpd_generic_read_rpm(); 2960ab88e23SCryolitia PukNgae 2970ab88e23SCryolitia PukNgae if (ret == 0) 2980ab88e23SCryolitia PukNgae // Re-init EC when speed is 0 2990ab88e23SCryolitia PukNgae gpd_win4_init_ec(); 3000ab88e23SCryolitia PukNgae 3010ab88e23SCryolitia PukNgae return ret; 3020ab88e23SCryolitia PukNgae } 3030ab88e23SCryolitia PukNgae 3040ab88e23SCryolitia PukNgae static int gpd_wm2_read_rpm(void) 3050ab88e23SCryolitia PukNgae { 3060ab88e23SCryolitia PukNgae for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET; 3070ab88e23SCryolitia PukNgae pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2; pwm_ctr_offset++) { 3080ab88e23SCryolitia PukNgae u8 PWMCTR; 3090ab88e23SCryolitia PukNgae 3100ab88e23SCryolitia PukNgae gpd_ecram_read(pwm_ctr_offset, &PWMCTR); 3110ab88e23SCryolitia PukNgae 3120ab88e23SCryolitia PukNgae if (PWMCTR != 0xB8) 3130ab88e23SCryolitia PukNgae gpd_ecram_write(pwm_ctr_offset, 0xB8); 3140ab88e23SCryolitia PukNgae } 3150ab88e23SCryolitia PukNgae 3160ab88e23SCryolitia PukNgae return gpd_generic_read_rpm(); 3170ab88e23SCryolitia PukNgae } 3180ab88e23SCryolitia PukNgae 3190ab88e23SCryolitia PukNgae // Read value for fan1_input 3200ab88e23SCryolitia PukNgae static int gpd_read_rpm(void) 3210ab88e23SCryolitia PukNgae { 3220ab88e23SCryolitia PukNgae switch (gpd_driver_priv.drvdata->board) { 3230ab88e23SCryolitia PukNgae case win_mini: 3240ab88e23SCryolitia PukNgae case duo: 3250ab88e23SCryolitia PukNgae return gpd_generic_read_rpm(); 3260ab88e23SCryolitia PukNgae case win4_6800u: 3270ab88e23SCryolitia PukNgae return gpd_win4_read_rpm(); 3280ab88e23SCryolitia PukNgae case win_max_2: 3290ab88e23SCryolitia PukNgae return gpd_wm2_read_rpm(); 3300ab88e23SCryolitia PukNgae } 3310ab88e23SCryolitia PukNgae 3320ab88e23SCryolitia PukNgae return 0; 3330ab88e23SCryolitia PukNgae } 3340ab88e23SCryolitia PukNgae 3350ab88e23SCryolitia PukNgae static int gpd_wm2_read_pwm(void) 3360ab88e23SCryolitia PukNgae { 3370ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 3380ab88e23SCryolitia PukNgae u8 var; 3390ab88e23SCryolitia PukNgae 3400ab88e23SCryolitia PukNgae gpd_ecram_read(drvdata->pwm_write, &var); 3410ab88e23SCryolitia PukNgae 3420ab88e23SCryolitia PukNgae // Match gpd_generic_write_pwm(u8) below 3430ab88e23SCryolitia PukNgae return DIV_ROUND_CLOSEST((var - 1) * 255, (drvdata->pwm_max - 1)); 3440ab88e23SCryolitia PukNgae } 3450ab88e23SCryolitia PukNgae 3460ab88e23SCryolitia PukNgae // Read value for pwm1 3470ab88e23SCryolitia PukNgae static int gpd_read_pwm(void) 3480ab88e23SCryolitia PukNgae { 3490ab88e23SCryolitia PukNgae switch (gpd_driver_priv.drvdata->board) { 3500ab88e23SCryolitia PukNgae case win_mini: 3510ab88e23SCryolitia PukNgae case duo: 3520ab88e23SCryolitia PukNgae case win4_6800u: 3530ab88e23SCryolitia PukNgae switch (gpd_driver_priv.pwm_enable) { 3540ab88e23SCryolitia PukNgae case DISABLE: 3550ab88e23SCryolitia PukNgae return 255; 3560ab88e23SCryolitia PukNgae case MANUAL: 3570ab88e23SCryolitia PukNgae return gpd_driver_priv.pwm_value; 3580ab88e23SCryolitia PukNgae case AUTOMATIC: 3590ab88e23SCryolitia PukNgae return -EOPNOTSUPP; 3600ab88e23SCryolitia PukNgae } 3610ab88e23SCryolitia PukNgae break; 3620ab88e23SCryolitia PukNgae case win_max_2: 3630ab88e23SCryolitia PukNgae return gpd_wm2_read_pwm(); 3640ab88e23SCryolitia PukNgae } 3650ab88e23SCryolitia PukNgae return 0; 3660ab88e23SCryolitia PukNgae } 3670ab88e23SCryolitia PukNgae 3680ab88e23SCryolitia PukNgae // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it. 3690ab88e23SCryolitia PukNgae static inline u8 gpd_cast_pwm_range(u8 val) 3700ab88e23SCryolitia PukNgae { 3710ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 3720ab88e23SCryolitia PukNgae 3730ab88e23SCryolitia PukNgae return DIV_ROUND_CLOSEST(val * (drvdata->pwm_max - 1), 255) + 1; 3740ab88e23SCryolitia PukNgae } 3750ab88e23SCryolitia PukNgae 3760ab88e23SCryolitia PukNgae static void gpd_generic_write_pwm(u8 val) 3770ab88e23SCryolitia PukNgae { 3780ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 3790ab88e23SCryolitia PukNgae u8 pwm_reg; 3800ab88e23SCryolitia PukNgae 3810ab88e23SCryolitia PukNgae pwm_reg = gpd_cast_pwm_range(val); 3820ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->pwm_write, pwm_reg); 3830ab88e23SCryolitia PukNgae } 3840ab88e23SCryolitia PukNgae 3850ab88e23SCryolitia PukNgae static void gpd_duo_write_pwm(u8 val) 3860ab88e23SCryolitia PukNgae { 3870ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 3880ab88e23SCryolitia PukNgae u8 pwm_reg; 3890ab88e23SCryolitia PukNgae 3900ab88e23SCryolitia PukNgae pwm_reg = gpd_cast_pwm_range(val); 3910ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->pwm_write, pwm_reg); 3920ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->pwm_write + 1, pwm_reg); 3930ab88e23SCryolitia PukNgae } 3940ab88e23SCryolitia PukNgae 3950ab88e23SCryolitia PukNgae // Write value for pwm1 3960ab88e23SCryolitia PukNgae static int gpd_write_pwm(u8 val) 3970ab88e23SCryolitia PukNgae { 3980ab88e23SCryolitia PukNgae if (gpd_driver_priv.pwm_enable != MANUAL) 3990ab88e23SCryolitia PukNgae return -EPERM; 4000ab88e23SCryolitia PukNgae 4010ab88e23SCryolitia PukNgae switch (gpd_driver_priv.drvdata->board) { 4020ab88e23SCryolitia PukNgae case duo: 4030ab88e23SCryolitia PukNgae gpd_duo_write_pwm(val); 4040ab88e23SCryolitia PukNgae break; 4050ab88e23SCryolitia PukNgae case win_mini: 4060ab88e23SCryolitia PukNgae case win4_6800u: 4070ab88e23SCryolitia PukNgae case win_max_2: 4080ab88e23SCryolitia PukNgae gpd_generic_write_pwm(val); 4090ab88e23SCryolitia PukNgae break; 4100ab88e23SCryolitia PukNgae } 4110ab88e23SCryolitia PukNgae 4120ab88e23SCryolitia PukNgae return 0; 4130ab88e23SCryolitia PukNgae } 4140ab88e23SCryolitia PukNgae 4150ab88e23SCryolitia PukNgae static void gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) 4160ab88e23SCryolitia PukNgae { 4170ab88e23SCryolitia PukNgae switch (pwm_enable) { 4180ab88e23SCryolitia PukNgae case DISABLE: 4190ab88e23SCryolitia PukNgae gpd_generic_write_pwm(255); 4200ab88e23SCryolitia PukNgae break; 4210ab88e23SCryolitia PukNgae case MANUAL: 4220ab88e23SCryolitia PukNgae gpd_generic_write_pwm(gpd_driver_priv.pwm_value); 4230ab88e23SCryolitia PukNgae break; 4240ab88e23SCryolitia PukNgae case AUTOMATIC: 4250ab88e23SCryolitia PukNgae gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); 4260ab88e23SCryolitia PukNgae break; 4270ab88e23SCryolitia PukNgae } 4280ab88e23SCryolitia PukNgae } 4290ab88e23SCryolitia PukNgae 4300ab88e23SCryolitia PukNgae static void gpd_duo_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) 4310ab88e23SCryolitia PukNgae { 4320ab88e23SCryolitia PukNgae switch (pwm_enable) { 4330ab88e23SCryolitia PukNgae case DISABLE: 4340ab88e23SCryolitia PukNgae gpd_duo_write_pwm(255); 4350ab88e23SCryolitia PukNgae break; 4360ab88e23SCryolitia PukNgae case MANUAL: 4370ab88e23SCryolitia PukNgae gpd_duo_write_pwm(gpd_driver_priv.pwm_value); 4380ab88e23SCryolitia PukNgae break; 4390ab88e23SCryolitia PukNgae case AUTOMATIC: 4400ab88e23SCryolitia PukNgae gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); 4410ab88e23SCryolitia PukNgae break; 4420ab88e23SCryolitia PukNgae } 4430ab88e23SCryolitia PukNgae } 4440ab88e23SCryolitia PukNgae 4450ab88e23SCryolitia PukNgae static void gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable) 4460ab88e23SCryolitia PukNgae { 4470ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 4480ab88e23SCryolitia PukNgae 4490ab88e23SCryolitia PukNgae switch (enable) { 4500ab88e23SCryolitia PukNgae case DISABLE: 4510ab88e23SCryolitia PukNgae gpd_generic_write_pwm(255); 4520ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->manual_control_enable, 1); 4530ab88e23SCryolitia PukNgae break; 4540ab88e23SCryolitia PukNgae case MANUAL: 4550ab88e23SCryolitia PukNgae gpd_generic_write_pwm(gpd_driver_priv.pwm_value); 4560ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->manual_control_enable, 1); 4570ab88e23SCryolitia PukNgae break; 4580ab88e23SCryolitia PukNgae case AUTOMATIC: 4590ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->manual_control_enable, 0); 4600ab88e23SCryolitia PukNgae break; 4610ab88e23SCryolitia PukNgae } 4620ab88e23SCryolitia PukNgae } 4630ab88e23SCryolitia PukNgae 4640ab88e23SCryolitia PukNgae // Write value for pwm1_enable 4650ab88e23SCryolitia PukNgae static void gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable) 4660ab88e23SCryolitia PukNgae { 4670ab88e23SCryolitia PukNgae if (enable == MANUAL) 4680ab88e23SCryolitia PukNgae // Set pwm_value to max firstly when switching to manual mode, in 4690ab88e23SCryolitia PukNgae // consideration of device safety. 4700ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_value = 255; 4710ab88e23SCryolitia PukNgae 4720ab88e23SCryolitia PukNgae switch (gpd_driver_priv.drvdata->board) { 4730ab88e23SCryolitia PukNgae case win_mini: 4740ab88e23SCryolitia PukNgae case win4_6800u: 4750ab88e23SCryolitia PukNgae gpd_win_mini_set_pwm_enable(enable); 4760ab88e23SCryolitia PukNgae break; 4770ab88e23SCryolitia PukNgae case duo: 4780ab88e23SCryolitia PukNgae gpd_duo_set_pwm_enable(enable); 4790ab88e23SCryolitia PukNgae break; 4800ab88e23SCryolitia PukNgae case win_max_2: 4810ab88e23SCryolitia PukNgae gpd_wm2_set_pwm_enable(enable); 4820ab88e23SCryolitia PukNgae break; 4830ab88e23SCryolitia PukNgae } 4840ab88e23SCryolitia PukNgae } 4850ab88e23SCryolitia PukNgae 4860ab88e23SCryolitia PukNgae static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata, 4870ab88e23SCryolitia PukNgae enum hwmon_sensor_types type, u32 attr, 4880ab88e23SCryolitia PukNgae __always_unused int channel) 4890ab88e23SCryolitia PukNgae { 4900ab88e23SCryolitia PukNgae if (type == hwmon_fan && attr == hwmon_fan_input) { 4910ab88e23SCryolitia PukNgae return 0444; 4920ab88e23SCryolitia PukNgae } else if (type == hwmon_pwm) { 4930ab88e23SCryolitia PukNgae switch (attr) { 4940ab88e23SCryolitia PukNgae case hwmon_pwm_enable: 4950ab88e23SCryolitia PukNgae case hwmon_pwm_input: 4960ab88e23SCryolitia PukNgae return 0644; 4970ab88e23SCryolitia PukNgae default: 4980ab88e23SCryolitia PukNgae return 0; 4990ab88e23SCryolitia PukNgae } 5000ab88e23SCryolitia PukNgae } 5010ab88e23SCryolitia PukNgae return 0; 5020ab88e23SCryolitia PukNgae } 5030ab88e23SCryolitia PukNgae 5040ab88e23SCryolitia PukNgae static int gpd_fan_hwmon_read(__always_unused struct device *dev, 5050ab88e23SCryolitia PukNgae enum hwmon_sensor_types type, u32 attr, 5060ab88e23SCryolitia PukNgae __always_unused int channel, long *val) 5070ab88e23SCryolitia PukNgae { 5080ab88e23SCryolitia PukNgae int ret; 5090ab88e23SCryolitia PukNgae 5100ab88e23SCryolitia PukNgae ret = mutex_lock_interruptible(&gpd_fan_sequence_lock); 5110ab88e23SCryolitia PukNgae if (ret) 5120ab88e23SCryolitia PukNgae return ret; 5130ab88e23SCryolitia PukNgae 5140ab88e23SCryolitia PukNgae if (type == hwmon_fan) { 5150ab88e23SCryolitia PukNgae if (attr == hwmon_fan_input) { 5160ab88e23SCryolitia PukNgae ret = gpd_read_rpm(); 5170ab88e23SCryolitia PukNgae 5180ab88e23SCryolitia PukNgae if (ret < 0) 5190ab88e23SCryolitia PukNgae goto OUT; 5200ab88e23SCryolitia PukNgae 5210ab88e23SCryolitia PukNgae *val = ret; 5220ab88e23SCryolitia PukNgae ret = 0; 5230ab88e23SCryolitia PukNgae goto OUT; 5240ab88e23SCryolitia PukNgae } 5250ab88e23SCryolitia PukNgae } else if (type == hwmon_pwm) { 5260ab88e23SCryolitia PukNgae switch (attr) { 5270ab88e23SCryolitia PukNgae case hwmon_pwm_enable: 5280ab88e23SCryolitia PukNgae *val = gpd_driver_priv.pwm_enable; 5290ab88e23SCryolitia PukNgae ret = 0; 5300ab88e23SCryolitia PukNgae goto OUT; 5310ab88e23SCryolitia PukNgae case hwmon_pwm_input: 5320ab88e23SCryolitia PukNgae ret = gpd_read_pwm(); 5330ab88e23SCryolitia PukNgae 5340ab88e23SCryolitia PukNgae if (ret < 0) 5350ab88e23SCryolitia PukNgae goto OUT; 5360ab88e23SCryolitia PukNgae 5370ab88e23SCryolitia PukNgae *val = ret; 5380ab88e23SCryolitia PukNgae ret = 0; 5390ab88e23SCryolitia PukNgae goto OUT; 5400ab88e23SCryolitia PukNgae } 5410ab88e23SCryolitia PukNgae } 5420ab88e23SCryolitia PukNgae 5430ab88e23SCryolitia PukNgae ret = -EOPNOTSUPP; 5440ab88e23SCryolitia PukNgae 5450ab88e23SCryolitia PukNgae OUT: 5460ab88e23SCryolitia PukNgae mutex_unlock(&gpd_fan_sequence_lock); 5470ab88e23SCryolitia PukNgae return ret; 5480ab88e23SCryolitia PukNgae } 5490ab88e23SCryolitia PukNgae 5500ab88e23SCryolitia PukNgae static int gpd_fan_hwmon_write(__always_unused struct device *dev, 5510ab88e23SCryolitia PukNgae enum hwmon_sensor_types type, u32 attr, 5520ab88e23SCryolitia PukNgae __always_unused int channel, long val) 5530ab88e23SCryolitia PukNgae { 5540ab88e23SCryolitia PukNgae int ret; 5550ab88e23SCryolitia PukNgae 5560ab88e23SCryolitia PukNgae ret = mutex_lock_interruptible(&gpd_fan_sequence_lock); 5570ab88e23SCryolitia PukNgae if (ret) 5580ab88e23SCryolitia PukNgae return ret; 5590ab88e23SCryolitia PukNgae 5600ab88e23SCryolitia PukNgae if (type == hwmon_pwm) { 5610ab88e23SCryolitia PukNgae switch (attr) { 5620ab88e23SCryolitia PukNgae case hwmon_pwm_enable: 5630ab88e23SCryolitia PukNgae if (!in_range(val, 0, 3)) { 5640ab88e23SCryolitia PukNgae ret = -EINVAL; 5650ab88e23SCryolitia PukNgae goto OUT; 5660ab88e23SCryolitia PukNgae } 5670ab88e23SCryolitia PukNgae 5680ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_enable = val; 5690ab88e23SCryolitia PukNgae 5700ab88e23SCryolitia PukNgae gpd_set_pwm_enable(gpd_driver_priv.pwm_enable); 5710ab88e23SCryolitia PukNgae ret = 0; 5720ab88e23SCryolitia PukNgae goto OUT; 5730ab88e23SCryolitia PukNgae case hwmon_pwm_input: 574*1fac317bSCryolitia PukNgae if (!in_range(val, 0, 256)) { 5750ab88e23SCryolitia PukNgae ret = -ERANGE; 5760ab88e23SCryolitia PukNgae goto OUT; 5770ab88e23SCryolitia PukNgae } 5780ab88e23SCryolitia PukNgae 5790ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_value = val; 5800ab88e23SCryolitia PukNgae 5810ab88e23SCryolitia PukNgae ret = gpd_write_pwm(val); 5820ab88e23SCryolitia PukNgae goto OUT; 5830ab88e23SCryolitia PukNgae } 5840ab88e23SCryolitia PukNgae } 5850ab88e23SCryolitia PukNgae 5860ab88e23SCryolitia PukNgae ret = -EOPNOTSUPP; 5870ab88e23SCryolitia PukNgae 5880ab88e23SCryolitia PukNgae OUT: 5890ab88e23SCryolitia PukNgae mutex_unlock(&gpd_fan_sequence_lock); 5900ab88e23SCryolitia PukNgae return ret; 5910ab88e23SCryolitia PukNgae } 5920ab88e23SCryolitia PukNgae 5930ab88e23SCryolitia PukNgae static const struct hwmon_ops gpd_fan_ops = { 5940ab88e23SCryolitia PukNgae .is_visible = gpd_fan_hwmon_is_visible, 5950ab88e23SCryolitia PukNgae .read = gpd_fan_hwmon_read, 5960ab88e23SCryolitia PukNgae .write = gpd_fan_hwmon_write, 5970ab88e23SCryolitia PukNgae }; 5980ab88e23SCryolitia PukNgae 5990ab88e23SCryolitia PukNgae static const struct hwmon_channel_info *gpd_fan_hwmon_channel_info[] = { 6000ab88e23SCryolitia PukNgae HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), 6010ab88e23SCryolitia PukNgae HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), 6020ab88e23SCryolitia PukNgae NULL 6030ab88e23SCryolitia PukNgae }; 6040ab88e23SCryolitia PukNgae 6050ab88e23SCryolitia PukNgae static struct hwmon_chip_info gpd_fan_chip_info = { 6060ab88e23SCryolitia PukNgae .ops = &gpd_fan_ops, 6070ab88e23SCryolitia PukNgae .info = gpd_fan_hwmon_channel_info 6080ab88e23SCryolitia PukNgae }; 6090ab88e23SCryolitia PukNgae 6100ab88e23SCryolitia PukNgae static int gpd_fan_probe(struct platform_device *pdev) 6110ab88e23SCryolitia PukNgae { 6120ab88e23SCryolitia PukNgae struct device *dev = &pdev->dev; 6130ab88e23SCryolitia PukNgae const struct resource *region; 6140ab88e23SCryolitia PukNgae const struct resource *res; 6150ab88e23SCryolitia PukNgae const struct device *hwdev; 6160ab88e23SCryolitia PukNgae 6170ab88e23SCryolitia PukNgae res = platform_get_resource(pdev, IORESOURCE_IO, 0); 6180ab88e23SCryolitia PukNgae if (IS_ERR(res)) 6190ab88e23SCryolitia PukNgae return dev_err_probe(dev, PTR_ERR(res), 6200ab88e23SCryolitia PukNgae "Failed to get platform resource\n"); 6210ab88e23SCryolitia PukNgae 6220ab88e23SCryolitia PukNgae region = devm_request_region(dev, res->start, 6230ab88e23SCryolitia PukNgae resource_size(res), DRIVER_NAME); 6240ab88e23SCryolitia PukNgae if (IS_ERR(region)) 6250ab88e23SCryolitia PukNgae return dev_err_probe(dev, PTR_ERR(region), 6260ab88e23SCryolitia PukNgae "Failed to request region\n"); 6270ab88e23SCryolitia PukNgae 6280ab88e23SCryolitia PukNgae hwdev = devm_hwmon_device_register_with_info(dev, 6290ab88e23SCryolitia PukNgae DRIVER_NAME, 6300ab88e23SCryolitia PukNgae NULL, 6310ab88e23SCryolitia PukNgae &gpd_fan_chip_info, 6320ab88e23SCryolitia PukNgae NULL); 6330ab88e23SCryolitia PukNgae if (IS_ERR(hwdev)) 6340ab88e23SCryolitia PukNgae return dev_err_probe(dev, PTR_ERR(region), 6350ab88e23SCryolitia PukNgae "Failed to register hwmon device\n"); 6360ab88e23SCryolitia PukNgae 6370ab88e23SCryolitia PukNgae return 0; 6380ab88e23SCryolitia PukNgae } 6390ab88e23SCryolitia PukNgae 6400ab88e23SCryolitia PukNgae static void gpd_fan_remove(__always_unused struct platform_device *pdev) 6410ab88e23SCryolitia PukNgae { 6420ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_enable = AUTOMATIC; 6430ab88e23SCryolitia PukNgae gpd_set_pwm_enable(AUTOMATIC); 6440ab88e23SCryolitia PukNgae } 6450ab88e23SCryolitia PukNgae 6460ab88e23SCryolitia PukNgae static struct platform_driver gpd_fan_driver = { 6470ab88e23SCryolitia PukNgae .probe = gpd_fan_probe, 6480ab88e23SCryolitia PukNgae .remove = gpd_fan_remove, 6490ab88e23SCryolitia PukNgae .driver = { 6500ab88e23SCryolitia PukNgae .name = KBUILD_MODNAME, 6510ab88e23SCryolitia PukNgae }, 6520ab88e23SCryolitia PukNgae }; 6530ab88e23SCryolitia PukNgae 6540ab88e23SCryolitia PukNgae static struct platform_device *gpd_fan_platform_device; 6550ab88e23SCryolitia PukNgae 6560ab88e23SCryolitia PukNgae static int __init gpd_fan_init(void) 6570ab88e23SCryolitia PukNgae { 6580ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *match = NULL; 6590ab88e23SCryolitia PukNgae 6600ab88e23SCryolitia PukNgae for (const struct gpd_fan_drvdata **p = gpd_module_drvdata; *p; p++) { 6610ab88e23SCryolitia PukNgae if (strcmp(gpd_fan_board, (*p)->board_name) == 0) { 6620ab88e23SCryolitia PukNgae match = *p; 6630ab88e23SCryolitia PukNgae break; 6640ab88e23SCryolitia PukNgae } 6650ab88e23SCryolitia PukNgae } 6660ab88e23SCryolitia PukNgae 6670ab88e23SCryolitia PukNgae if (!match) { 6680ab88e23SCryolitia PukNgae const struct dmi_system_id *dmi_match = 6690ab88e23SCryolitia PukNgae dmi_first_match(dmi_table); 6700ab88e23SCryolitia PukNgae if (dmi_match) 6710ab88e23SCryolitia PukNgae match = dmi_match->driver_data; 6720ab88e23SCryolitia PukNgae } 6730ab88e23SCryolitia PukNgae 6740ab88e23SCryolitia PukNgae if (!match) 6750ab88e23SCryolitia PukNgae return -ENODEV; 6760ab88e23SCryolitia PukNgae 6770ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_enable = AUTOMATIC; 6780ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_value = 255; 6790ab88e23SCryolitia PukNgae gpd_driver_priv.drvdata = match; 6800ab88e23SCryolitia PukNgae 6810ab88e23SCryolitia PukNgae struct resource gpd_fan_resources[] = { 6820ab88e23SCryolitia PukNgae { 6830ab88e23SCryolitia PukNgae .start = match->addr_port, 6840ab88e23SCryolitia PukNgae .end = match->data_port, 6850ab88e23SCryolitia PukNgae .flags = IORESOURCE_IO, 6860ab88e23SCryolitia PukNgae }, 6870ab88e23SCryolitia PukNgae }; 6880ab88e23SCryolitia PukNgae 6890ab88e23SCryolitia PukNgae gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver, 6900ab88e23SCryolitia PukNgae gpd_fan_probe, 6910ab88e23SCryolitia PukNgae gpd_fan_resources, 6920ab88e23SCryolitia PukNgae 1, NULL, 0); 6930ab88e23SCryolitia PukNgae 6940ab88e23SCryolitia PukNgae if (IS_ERR(gpd_fan_platform_device)) { 6950ab88e23SCryolitia PukNgae pr_warn("Failed to create platform device\n"); 6960ab88e23SCryolitia PukNgae return PTR_ERR(gpd_fan_platform_device); 6970ab88e23SCryolitia PukNgae } 6980ab88e23SCryolitia PukNgae 6990ab88e23SCryolitia PukNgae return 0; 7000ab88e23SCryolitia PukNgae } 7010ab88e23SCryolitia PukNgae 7020ab88e23SCryolitia PukNgae static void __exit gpd_fan_exit(void) 7030ab88e23SCryolitia PukNgae { 7040ab88e23SCryolitia PukNgae platform_device_unregister(gpd_fan_platform_device); 7050ab88e23SCryolitia PukNgae platform_driver_unregister(&gpd_fan_driver); 7060ab88e23SCryolitia PukNgae } 7070ab88e23SCryolitia PukNgae 7080ab88e23SCryolitia PukNgae MODULE_DEVICE_TABLE(dmi, dmi_table); 7090ab88e23SCryolitia PukNgae 7100ab88e23SCryolitia PukNgae module_init(gpd_fan_init); 7110ab88e23SCryolitia PukNgae module_exit(gpd_fan_exit); 7120ab88e23SCryolitia PukNgae 7130ab88e23SCryolitia PukNgae MODULE_LICENSE("GPL"); 7140ab88e23SCryolitia PukNgae MODULE_AUTHOR("Cryolitia PukNgae <cryolitia@uniontech.com>"); 7150ab88e23SCryolitia PukNgae MODULE_DESCRIPTION("GPD Devices fan control driver"); 716