1*0ab88e23SCryolitia PukNgae // SPDX-License-Identifier: GPL-2.0+ 2*0ab88e23SCryolitia PukNgae 3*0ab88e23SCryolitia PukNgae /* Platform driver for GPD devices that expose fan control via hwmon sysfs. 4*0ab88e23SCryolitia PukNgae * 5*0ab88e23SCryolitia PukNgae * Fan control is provided via pwm interface in the range [0-255]. 6*0ab88e23SCryolitia PukNgae * Each model has a different range in the EC, the written value is scaled to 7*0ab88e23SCryolitia PukNgae * accommodate for that. 8*0ab88e23SCryolitia PukNgae * 9*0ab88e23SCryolitia PukNgae * Based on this repo: 10*0ab88e23SCryolitia PukNgae * https://github.com/Cryolitia/gpd-fan-driver 11*0ab88e23SCryolitia PukNgae * 12*0ab88e23SCryolitia PukNgae * Copyright (c) 2024 Cryolitia PukNgae 13*0ab88e23SCryolitia PukNgae */ 14*0ab88e23SCryolitia PukNgae 15*0ab88e23SCryolitia PukNgae #include <linux/acpi.h> 16*0ab88e23SCryolitia PukNgae #include <linux/dmi.h> 17*0ab88e23SCryolitia PukNgae #include <linux/hwmon.h> 18*0ab88e23SCryolitia PukNgae #include <linux/ioport.h> 19*0ab88e23SCryolitia PukNgae #include <linux/kernel.h> 20*0ab88e23SCryolitia PukNgae #include <linux/module.h> 21*0ab88e23SCryolitia PukNgae #include <linux/platform_device.h> 22*0ab88e23SCryolitia PukNgae 23*0ab88e23SCryolitia PukNgae #define DRIVER_NAME "gpdfan" 24*0ab88e23SCryolitia PukNgae #define GPD_PWM_CTR_OFFSET 0x1841 25*0ab88e23SCryolitia PukNgae 26*0ab88e23SCryolitia PukNgae static char *gpd_fan_board = ""; 27*0ab88e23SCryolitia PukNgae module_param(gpd_fan_board, charp, 0444); 28*0ab88e23SCryolitia PukNgae 29*0ab88e23SCryolitia PukNgae // EC read/write locker, protecting a sequence of EC operations 30*0ab88e23SCryolitia PukNgae static DEFINE_MUTEX(gpd_fan_sequence_lock); 31*0ab88e23SCryolitia PukNgae 32*0ab88e23SCryolitia PukNgae enum gpd_board { 33*0ab88e23SCryolitia PukNgae win_mini, 34*0ab88e23SCryolitia PukNgae win4_6800u, 35*0ab88e23SCryolitia PukNgae win_max_2, 36*0ab88e23SCryolitia PukNgae duo, 37*0ab88e23SCryolitia PukNgae }; 38*0ab88e23SCryolitia PukNgae 39*0ab88e23SCryolitia PukNgae enum FAN_PWM_ENABLE { 40*0ab88e23SCryolitia PukNgae DISABLE = 0, 41*0ab88e23SCryolitia PukNgae MANUAL = 1, 42*0ab88e23SCryolitia PukNgae AUTOMATIC = 2, 43*0ab88e23SCryolitia PukNgae }; 44*0ab88e23SCryolitia PukNgae 45*0ab88e23SCryolitia PukNgae static struct { 46*0ab88e23SCryolitia PukNgae enum FAN_PWM_ENABLE pwm_enable; 47*0ab88e23SCryolitia PukNgae u8 pwm_value; 48*0ab88e23SCryolitia PukNgae 49*0ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *drvdata; 50*0ab88e23SCryolitia PukNgae } gpd_driver_priv; 51*0ab88e23SCryolitia PukNgae 52*0ab88e23SCryolitia PukNgae struct gpd_fan_drvdata { 53*0ab88e23SCryolitia PukNgae const char *board_name; // Board name for module param comparison 54*0ab88e23SCryolitia PukNgae const enum gpd_board board; 55*0ab88e23SCryolitia PukNgae 56*0ab88e23SCryolitia PukNgae const u8 addr_port; 57*0ab88e23SCryolitia PukNgae const u8 data_port; 58*0ab88e23SCryolitia PukNgae const u16 manual_control_enable; 59*0ab88e23SCryolitia PukNgae const u16 rpm_read; 60*0ab88e23SCryolitia PukNgae const u16 pwm_write; 61*0ab88e23SCryolitia PukNgae const u16 pwm_max; 62*0ab88e23SCryolitia PukNgae }; 63*0ab88e23SCryolitia PukNgae 64*0ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_win_mini_drvdata = { 65*0ab88e23SCryolitia PukNgae .board_name = "win_mini", 66*0ab88e23SCryolitia PukNgae .board = win_mini, 67*0ab88e23SCryolitia PukNgae 68*0ab88e23SCryolitia PukNgae .addr_port = 0x4E, 69*0ab88e23SCryolitia PukNgae .data_port = 0x4F, 70*0ab88e23SCryolitia PukNgae .manual_control_enable = 0x047A, 71*0ab88e23SCryolitia PukNgae .rpm_read = 0x0478, 72*0ab88e23SCryolitia PukNgae .pwm_write = 0x047A, 73*0ab88e23SCryolitia PukNgae .pwm_max = 244, 74*0ab88e23SCryolitia PukNgae }; 75*0ab88e23SCryolitia PukNgae 76*0ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_duo_drvdata = { 77*0ab88e23SCryolitia PukNgae .board_name = "duo", 78*0ab88e23SCryolitia PukNgae .board = duo, 79*0ab88e23SCryolitia PukNgae 80*0ab88e23SCryolitia PukNgae .addr_port = 0x4E, 81*0ab88e23SCryolitia PukNgae .data_port = 0x4F, 82*0ab88e23SCryolitia PukNgae .manual_control_enable = 0x047A, 83*0ab88e23SCryolitia PukNgae .rpm_read = 0x0478, 84*0ab88e23SCryolitia PukNgae .pwm_write = 0x047A, 85*0ab88e23SCryolitia PukNgae .pwm_max = 244, 86*0ab88e23SCryolitia PukNgae }; 87*0ab88e23SCryolitia PukNgae 88*0ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_win4_drvdata = { 89*0ab88e23SCryolitia PukNgae .board_name = "win4", 90*0ab88e23SCryolitia PukNgae .board = win4_6800u, 91*0ab88e23SCryolitia PukNgae 92*0ab88e23SCryolitia PukNgae .addr_port = 0x2E, 93*0ab88e23SCryolitia PukNgae .data_port = 0x2F, 94*0ab88e23SCryolitia PukNgae .manual_control_enable = 0xC311, 95*0ab88e23SCryolitia PukNgae .rpm_read = 0xC880, 96*0ab88e23SCryolitia PukNgae .pwm_write = 0xC311, 97*0ab88e23SCryolitia PukNgae .pwm_max = 127, 98*0ab88e23SCryolitia PukNgae }; 99*0ab88e23SCryolitia PukNgae 100*0ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_wm2_drvdata = { 101*0ab88e23SCryolitia PukNgae .board_name = "wm2", 102*0ab88e23SCryolitia PukNgae .board = win_max_2, 103*0ab88e23SCryolitia PukNgae 104*0ab88e23SCryolitia PukNgae .addr_port = 0x4E, 105*0ab88e23SCryolitia PukNgae .data_port = 0x4F, 106*0ab88e23SCryolitia PukNgae .manual_control_enable = 0x0275, 107*0ab88e23SCryolitia PukNgae .rpm_read = 0x0218, 108*0ab88e23SCryolitia PukNgae .pwm_write = 0x1809, 109*0ab88e23SCryolitia PukNgae .pwm_max = 184, 110*0ab88e23SCryolitia PukNgae }; 111*0ab88e23SCryolitia PukNgae 112*0ab88e23SCryolitia PukNgae static const struct dmi_system_id dmi_table[] = { 113*0ab88e23SCryolitia PukNgae { 114*0ab88e23SCryolitia PukNgae // GPD Win Mini 115*0ab88e23SCryolitia PukNgae // GPD Win Mini with AMD Ryzen 8840U 116*0ab88e23SCryolitia PukNgae .matches = { 117*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 118*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1617-01") 119*0ab88e23SCryolitia PukNgae }, 120*0ab88e23SCryolitia PukNgae .driver_data = &gpd_win_mini_drvdata, 121*0ab88e23SCryolitia PukNgae }, 122*0ab88e23SCryolitia PukNgae { 123*0ab88e23SCryolitia PukNgae // GPD Win Mini 124*0ab88e23SCryolitia PukNgae // GPD Win Mini with AMD Ryzen HX370 125*0ab88e23SCryolitia PukNgae .matches = { 126*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 127*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1617-02") 128*0ab88e23SCryolitia PukNgae }, 129*0ab88e23SCryolitia PukNgae .driver_data = &gpd_win_mini_drvdata, 130*0ab88e23SCryolitia PukNgae }, 131*0ab88e23SCryolitia PukNgae { 132*0ab88e23SCryolitia PukNgae // GPD Win Mini 133*0ab88e23SCryolitia PukNgae // GPD Win Mini with AMD Ryzen HX370 134*0ab88e23SCryolitia PukNgae .matches = { 135*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 136*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1617-02-L") 137*0ab88e23SCryolitia PukNgae }, 138*0ab88e23SCryolitia PukNgae .driver_data = &gpd_win_mini_drvdata, 139*0ab88e23SCryolitia PukNgae }, 140*0ab88e23SCryolitia PukNgae { 141*0ab88e23SCryolitia PukNgae // GPD Win 4 with AMD Ryzen 6800U 142*0ab88e23SCryolitia PukNgae .matches = { 143*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 144*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"), 145*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_BOARD_VERSION, "Default string"), 146*0ab88e23SCryolitia PukNgae }, 147*0ab88e23SCryolitia PukNgae .driver_data = &gpd_win4_drvdata, 148*0ab88e23SCryolitia PukNgae }, 149*0ab88e23SCryolitia PukNgae { 150*0ab88e23SCryolitia PukNgae // GPD Win 4 with Ryzen 7840U 151*0ab88e23SCryolitia PukNgae .matches = { 152*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 153*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"), 154*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_BOARD_VERSION, "Ver. 1.0"), 155*0ab88e23SCryolitia PukNgae }, 156*0ab88e23SCryolitia PukNgae // Since 7840U, win4 uses the same drvdata as wm2 157*0ab88e23SCryolitia PukNgae .driver_data = &gpd_wm2_drvdata, 158*0ab88e23SCryolitia PukNgae }, 159*0ab88e23SCryolitia PukNgae { 160*0ab88e23SCryolitia PukNgae // GPD Win 4 with Ryzen 7840U (another) 161*0ab88e23SCryolitia PukNgae .matches = { 162*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 163*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"), 164*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_BOARD_VERSION, "Ver.1.0"), 165*0ab88e23SCryolitia PukNgae }, 166*0ab88e23SCryolitia PukNgae .driver_data = &gpd_wm2_drvdata, 167*0ab88e23SCryolitia PukNgae }, 168*0ab88e23SCryolitia PukNgae { 169*0ab88e23SCryolitia PukNgae // GPD Win Max 2 with Ryzen 6800U 170*0ab88e23SCryolitia PukNgae // GPD Win Max 2 2023 with Ryzen 7840U 171*0ab88e23SCryolitia PukNgae // GPD Win Max 2 2024 with Ryzen 8840U 172*0ab88e23SCryolitia PukNgae .matches = { 173*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 174*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), 175*0ab88e23SCryolitia PukNgae }, 176*0ab88e23SCryolitia PukNgae .driver_data = &gpd_wm2_drvdata, 177*0ab88e23SCryolitia PukNgae }, 178*0ab88e23SCryolitia PukNgae { 179*0ab88e23SCryolitia PukNgae // GPD Win Max 2 with AMD Ryzen HX370 180*0ab88e23SCryolitia PukNgae .matches = { 181*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 182*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1619-05"), 183*0ab88e23SCryolitia PukNgae }, 184*0ab88e23SCryolitia PukNgae .driver_data = &gpd_wm2_drvdata, 185*0ab88e23SCryolitia PukNgae }, 186*0ab88e23SCryolitia PukNgae { 187*0ab88e23SCryolitia PukNgae // GPD Duo 188*0ab88e23SCryolitia PukNgae .matches = { 189*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 190*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1622-01"), 191*0ab88e23SCryolitia PukNgae }, 192*0ab88e23SCryolitia PukNgae .driver_data = &gpd_duo_drvdata, 193*0ab88e23SCryolitia PukNgae }, 194*0ab88e23SCryolitia PukNgae { 195*0ab88e23SCryolitia PukNgae // GPD Duo (another) 196*0ab88e23SCryolitia PukNgae .matches = { 197*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 198*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1622-01-L"), 199*0ab88e23SCryolitia PukNgae }, 200*0ab88e23SCryolitia PukNgae .driver_data = &gpd_duo_drvdata, 201*0ab88e23SCryolitia PukNgae }, 202*0ab88e23SCryolitia PukNgae { 203*0ab88e23SCryolitia PukNgae // GPD Pocket 4 204*0ab88e23SCryolitia PukNgae .matches = { 205*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 206*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1628-04"), 207*0ab88e23SCryolitia PukNgae }, 208*0ab88e23SCryolitia PukNgae .driver_data = &gpd_win_mini_drvdata, 209*0ab88e23SCryolitia PukNgae }, 210*0ab88e23SCryolitia PukNgae { 211*0ab88e23SCryolitia PukNgae // GPD Pocket 4 (another) 212*0ab88e23SCryolitia PukNgae .matches = { 213*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 214*0ab88e23SCryolitia PukNgae DMI_MATCH(DMI_PRODUCT_NAME, "G1628-04-L"), 215*0ab88e23SCryolitia PukNgae }, 216*0ab88e23SCryolitia PukNgae .driver_data = &gpd_win_mini_drvdata, 217*0ab88e23SCryolitia PukNgae }, 218*0ab88e23SCryolitia PukNgae {} 219*0ab88e23SCryolitia PukNgae }; 220*0ab88e23SCryolitia PukNgae 221*0ab88e23SCryolitia PukNgae static const struct gpd_fan_drvdata *gpd_module_drvdata[] = { 222*0ab88e23SCryolitia PukNgae &gpd_win_mini_drvdata, &gpd_win4_drvdata, &gpd_wm2_drvdata, NULL 223*0ab88e23SCryolitia PukNgae }; 224*0ab88e23SCryolitia PukNgae 225*0ab88e23SCryolitia PukNgae // Helper functions to handle EC read/write 226*0ab88e23SCryolitia PukNgae static void gpd_ecram_read(u16 offset, u8 *val) 227*0ab88e23SCryolitia PukNgae { 228*0ab88e23SCryolitia PukNgae u16 addr_port = gpd_driver_priv.drvdata->addr_port; 229*0ab88e23SCryolitia PukNgae u16 data_port = gpd_driver_priv.drvdata->data_port; 230*0ab88e23SCryolitia PukNgae 231*0ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 232*0ab88e23SCryolitia PukNgae outb(0x11, data_port); 233*0ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 234*0ab88e23SCryolitia PukNgae outb((u8)((offset >> 8) & 0xFF), data_port); 235*0ab88e23SCryolitia PukNgae 236*0ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 237*0ab88e23SCryolitia PukNgae outb(0x10, data_port); 238*0ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 239*0ab88e23SCryolitia PukNgae outb((u8)(offset & 0xFF), data_port); 240*0ab88e23SCryolitia PukNgae 241*0ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 242*0ab88e23SCryolitia PukNgae outb(0x12, data_port); 243*0ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 244*0ab88e23SCryolitia PukNgae *val = inb(data_port); 245*0ab88e23SCryolitia PukNgae } 246*0ab88e23SCryolitia PukNgae 247*0ab88e23SCryolitia PukNgae static void gpd_ecram_write(u16 offset, u8 value) 248*0ab88e23SCryolitia PukNgae { 249*0ab88e23SCryolitia PukNgae u16 addr_port = gpd_driver_priv.drvdata->addr_port; 250*0ab88e23SCryolitia PukNgae u16 data_port = gpd_driver_priv.drvdata->data_port; 251*0ab88e23SCryolitia PukNgae 252*0ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 253*0ab88e23SCryolitia PukNgae outb(0x11, data_port); 254*0ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 255*0ab88e23SCryolitia PukNgae outb((u8)((offset >> 8) & 0xFF), data_port); 256*0ab88e23SCryolitia PukNgae 257*0ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 258*0ab88e23SCryolitia PukNgae outb(0x10, data_port); 259*0ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 260*0ab88e23SCryolitia PukNgae outb((u8)(offset & 0xFF), data_port); 261*0ab88e23SCryolitia PukNgae 262*0ab88e23SCryolitia PukNgae outb(0x2E, addr_port); 263*0ab88e23SCryolitia PukNgae outb(0x12, data_port); 264*0ab88e23SCryolitia PukNgae outb(0x2F, addr_port); 265*0ab88e23SCryolitia PukNgae outb(value, data_port); 266*0ab88e23SCryolitia PukNgae } 267*0ab88e23SCryolitia PukNgae 268*0ab88e23SCryolitia PukNgae static int gpd_generic_read_rpm(void) 269*0ab88e23SCryolitia PukNgae { 270*0ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 271*0ab88e23SCryolitia PukNgae u8 high, low; 272*0ab88e23SCryolitia PukNgae 273*0ab88e23SCryolitia PukNgae gpd_ecram_read(drvdata->rpm_read, &high); 274*0ab88e23SCryolitia PukNgae gpd_ecram_read(drvdata->rpm_read + 1, &low); 275*0ab88e23SCryolitia PukNgae 276*0ab88e23SCryolitia PukNgae return (u16)high << 8 | low; 277*0ab88e23SCryolitia PukNgae } 278*0ab88e23SCryolitia PukNgae 279*0ab88e23SCryolitia PukNgae static void gpd_win4_init_ec(void) 280*0ab88e23SCryolitia PukNgae { 281*0ab88e23SCryolitia PukNgae u8 chip_id, chip_ver; 282*0ab88e23SCryolitia PukNgae 283*0ab88e23SCryolitia PukNgae gpd_ecram_read(0x2000, &chip_id); 284*0ab88e23SCryolitia PukNgae 285*0ab88e23SCryolitia PukNgae if (chip_id == 0x55) { 286*0ab88e23SCryolitia PukNgae gpd_ecram_read(0x1060, &chip_ver); 287*0ab88e23SCryolitia PukNgae gpd_ecram_write(0x1060, chip_ver | 0x80); 288*0ab88e23SCryolitia PukNgae } 289*0ab88e23SCryolitia PukNgae } 290*0ab88e23SCryolitia PukNgae 291*0ab88e23SCryolitia PukNgae static int gpd_win4_read_rpm(void) 292*0ab88e23SCryolitia PukNgae { 293*0ab88e23SCryolitia PukNgae int ret; 294*0ab88e23SCryolitia PukNgae 295*0ab88e23SCryolitia PukNgae ret = gpd_generic_read_rpm(); 296*0ab88e23SCryolitia PukNgae 297*0ab88e23SCryolitia PukNgae if (ret == 0) 298*0ab88e23SCryolitia PukNgae // Re-init EC when speed is 0 299*0ab88e23SCryolitia PukNgae gpd_win4_init_ec(); 300*0ab88e23SCryolitia PukNgae 301*0ab88e23SCryolitia PukNgae return ret; 302*0ab88e23SCryolitia PukNgae } 303*0ab88e23SCryolitia PukNgae 304*0ab88e23SCryolitia PukNgae static int gpd_wm2_read_rpm(void) 305*0ab88e23SCryolitia PukNgae { 306*0ab88e23SCryolitia PukNgae for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET; 307*0ab88e23SCryolitia PukNgae pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2; pwm_ctr_offset++) { 308*0ab88e23SCryolitia PukNgae u8 PWMCTR; 309*0ab88e23SCryolitia PukNgae 310*0ab88e23SCryolitia PukNgae gpd_ecram_read(pwm_ctr_offset, &PWMCTR); 311*0ab88e23SCryolitia PukNgae 312*0ab88e23SCryolitia PukNgae if (PWMCTR != 0xB8) 313*0ab88e23SCryolitia PukNgae gpd_ecram_write(pwm_ctr_offset, 0xB8); 314*0ab88e23SCryolitia PukNgae } 315*0ab88e23SCryolitia PukNgae 316*0ab88e23SCryolitia PukNgae return gpd_generic_read_rpm(); 317*0ab88e23SCryolitia PukNgae } 318*0ab88e23SCryolitia PukNgae 319*0ab88e23SCryolitia PukNgae // Read value for fan1_input 320*0ab88e23SCryolitia PukNgae static int gpd_read_rpm(void) 321*0ab88e23SCryolitia PukNgae { 322*0ab88e23SCryolitia PukNgae switch (gpd_driver_priv.drvdata->board) { 323*0ab88e23SCryolitia PukNgae case win_mini: 324*0ab88e23SCryolitia PukNgae case duo: 325*0ab88e23SCryolitia PukNgae return gpd_generic_read_rpm(); 326*0ab88e23SCryolitia PukNgae case win4_6800u: 327*0ab88e23SCryolitia PukNgae return gpd_win4_read_rpm(); 328*0ab88e23SCryolitia PukNgae case win_max_2: 329*0ab88e23SCryolitia PukNgae return gpd_wm2_read_rpm(); 330*0ab88e23SCryolitia PukNgae } 331*0ab88e23SCryolitia PukNgae 332*0ab88e23SCryolitia PukNgae return 0; 333*0ab88e23SCryolitia PukNgae } 334*0ab88e23SCryolitia PukNgae 335*0ab88e23SCryolitia PukNgae static int gpd_wm2_read_pwm(void) 336*0ab88e23SCryolitia PukNgae { 337*0ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 338*0ab88e23SCryolitia PukNgae u8 var; 339*0ab88e23SCryolitia PukNgae 340*0ab88e23SCryolitia PukNgae gpd_ecram_read(drvdata->pwm_write, &var); 341*0ab88e23SCryolitia PukNgae 342*0ab88e23SCryolitia PukNgae // Match gpd_generic_write_pwm(u8) below 343*0ab88e23SCryolitia PukNgae return DIV_ROUND_CLOSEST((var - 1) * 255, (drvdata->pwm_max - 1)); 344*0ab88e23SCryolitia PukNgae } 345*0ab88e23SCryolitia PukNgae 346*0ab88e23SCryolitia PukNgae // Read value for pwm1 347*0ab88e23SCryolitia PukNgae static int gpd_read_pwm(void) 348*0ab88e23SCryolitia PukNgae { 349*0ab88e23SCryolitia PukNgae switch (gpd_driver_priv.drvdata->board) { 350*0ab88e23SCryolitia PukNgae case win_mini: 351*0ab88e23SCryolitia PukNgae case duo: 352*0ab88e23SCryolitia PukNgae case win4_6800u: 353*0ab88e23SCryolitia PukNgae switch (gpd_driver_priv.pwm_enable) { 354*0ab88e23SCryolitia PukNgae case DISABLE: 355*0ab88e23SCryolitia PukNgae return 255; 356*0ab88e23SCryolitia PukNgae case MANUAL: 357*0ab88e23SCryolitia PukNgae return gpd_driver_priv.pwm_value; 358*0ab88e23SCryolitia PukNgae case AUTOMATIC: 359*0ab88e23SCryolitia PukNgae return -EOPNOTSUPP; 360*0ab88e23SCryolitia PukNgae } 361*0ab88e23SCryolitia PukNgae break; 362*0ab88e23SCryolitia PukNgae case win_max_2: 363*0ab88e23SCryolitia PukNgae return gpd_wm2_read_pwm(); 364*0ab88e23SCryolitia PukNgae } 365*0ab88e23SCryolitia PukNgae return 0; 366*0ab88e23SCryolitia PukNgae } 367*0ab88e23SCryolitia PukNgae 368*0ab88e23SCryolitia PukNgae // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it. 369*0ab88e23SCryolitia PukNgae static inline u8 gpd_cast_pwm_range(u8 val) 370*0ab88e23SCryolitia PukNgae { 371*0ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 372*0ab88e23SCryolitia PukNgae 373*0ab88e23SCryolitia PukNgae return DIV_ROUND_CLOSEST(val * (drvdata->pwm_max - 1), 255) + 1; 374*0ab88e23SCryolitia PukNgae } 375*0ab88e23SCryolitia PukNgae 376*0ab88e23SCryolitia PukNgae static void gpd_generic_write_pwm(u8 val) 377*0ab88e23SCryolitia PukNgae { 378*0ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 379*0ab88e23SCryolitia PukNgae u8 pwm_reg; 380*0ab88e23SCryolitia PukNgae 381*0ab88e23SCryolitia PukNgae pwm_reg = gpd_cast_pwm_range(val); 382*0ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->pwm_write, pwm_reg); 383*0ab88e23SCryolitia PukNgae } 384*0ab88e23SCryolitia PukNgae 385*0ab88e23SCryolitia PukNgae static void gpd_duo_write_pwm(u8 val) 386*0ab88e23SCryolitia PukNgae { 387*0ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 388*0ab88e23SCryolitia PukNgae u8 pwm_reg; 389*0ab88e23SCryolitia PukNgae 390*0ab88e23SCryolitia PukNgae pwm_reg = gpd_cast_pwm_range(val); 391*0ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->pwm_write, pwm_reg); 392*0ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->pwm_write + 1, pwm_reg); 393*0ab88e23SCryolitia PukNgae } 394*0ab88e23SCryolitia PukNgae 395*0ab88e23SCryolitia PukNgae // Write value for pwm1 396*0ab88e23SCryolitia PukNgae static int gpd_write_pwm(u8 val) 397*0ab88e23SCryolitia PukNgae { 398*0ab88e23SCryolitia PukNgae if (gpd_driver_priv.pwm_enable != MANUAL) 399*0ab88e23SCryolitia PukNgae return -EPERM; 400*0ab88e23SCryolitia PukNgae 401*0ab88e23SCryolitia PukNgae switch (gpd_driver_priv.drvdata->board) { 402*0ab88e23SCryolitia PukNgae case duo: 403*0ab88e23SCryolitia PukNgae gpd_duo_write_pwm(val); 404*0ab88e23SCryolitia PukNgae break; 405*0ab88e23SCryolitia PukNgae case win_mini: 406*0ab88e23SCryolitia PukNgae case win4_6800u: 407*0ab88e23SCryolitia PukNgae case win_max_2: 408*0ab88e23SCryolitia PukNgae gpd_generic_write_pwm(val); 409*0ab88e23SCryolitia PukNgae break; 410*0ab88e23SCryolitia PukNgae } 411*0ab88e23SCryolitia PukNgae 412*0ab88e23SCryolitia PukNgae return 0; 413*0ab88e23SCryolitia PukNgae } 414*0ab88e23SCryolitia PukNgae 415*0ab88e23SCryolitia PukNgae static void gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) 416*0ab88e23SCryolitia PukNgae { 417*0ab88e23SCryolitia PukNgae switch (pwm_enable) { 418*0ab88e23SCryolitia PukNgae case DISABLE: 419*0ab88e23SCryolitia PukNgae gpd_generic_write_pwm(255); 420*0ab88e23SCryolitia PukNgae break; 421*0ab88e23SCryolitia PukNgae case MANUAL: 422*0ab88e23SCryolitia PukNgae gpd_generic_write_pwm(gpd_driver_priv.pwm_value); 423*0ab88e23SCryolitia PukNgae break; 424*0ab88e23SCryolitia PukNgae case AUTOMATIC: 425*0ab88e23SCryolitia PukNgae gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); 426*0ab88e23SCryolitia PukNgae break; 427*0ab88e23SCryolitia PukNgae } 428*0ab88e23SCryolitia PukNgae } 429*0ab88e23SCryolitia PukNgae 430*0ab88e23SCryolitia PukNgae static void gpd_duo_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) 431*0ab88e23SCryolitia PukNgae { 432*0ab88e23SCryolitia PukNgae switch (pwm_enable) { 433*0ab88e23SCryolitia PukNgae case DISABLE: 434*0ab88e23SCryolitia PukNgae gpd_duo_write_pwm(255); 435*0ab88e23SCryolitia PukNgae break; 436*0ab88e23SCryolitia PukNgae case MANUAL: 437*0ab88e23SCryolitia PukNgae gpd_duo_write_pwm(gpd_driver_priv.pwm_value); 438*0ab88e23SCryolitia PukNgae break; 439*0ab88e23SCryolitia PukNgae case AUTOMATIC: 440*0ab88e23SCryolitia PukNgae gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); 441*0ab88e23SCryolitia PukNgae break; 442*0ab88e23SCryolitia PukNgae } 443*0ab88e23SCryolitia PukNgae } 444*0ab88e23SCryolitia PukNgae 445*0ab88e23SCryolitia PukNgae static void gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable) 446*0ab88e23SCryolitia PukNgae { 447*0ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; 448*0ab88e23SCryolitia PukNgae 449*0ab88e23SCryolitia PukNgae switch (enable) { 450*0ab88e23SCryolitia PukNgae case DISABLE: 451*0ab88e23SCryolitia PukNgae gpd_generic_write_pwm(255); 452*0ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->manual_control_enable, 1); 453*0ab88e23SCryolitia PukNgae break; 454*0ab88e23SCryolitia PukNgae case MANUAL: 455*0ab88e23SCryolitia PukNgae gpd_generic_write_pwm(gpd_driver_priv.pwm_value); 456*0ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->manual_control_enable, 1); 457*0ab88e23SCryolitia PukNgae break; 458*0ab88e23SCryolitia PukNgae case AUTOMATIC: 459*0ab88e23SCryolitia PukNgae gpd_ecram_write(drvdata->manual_control_enable, 0); 460*0ab88e23SCryolitia PukNgae break; 461*0ab88e23SCryolitia PukNgae } 462*0ab88e23SCryolitia PukNgae } 463*0ab88e23SCryolitia PukNgae 464*0ab88e23SCryolitia PukNgae // Write value for pwm1_enable 465*0ab88e23SCryolitia PukNgae static void gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable) 466*0ab88e23SCryolitia PukNgae { 467*0ab88e23SCryolitia PukNgae if (enable == MANUAL) 468*0ab88e23SCryolitia PukNgae // Set pwm_value to max firstly when switching to manual mode, in 469*0ab88e23SCryolitia PukNgae // consideration of device safety. 470*0ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_value = 255; 471*0ab88e23SCryolitia PukNgae 472*0ab88e23SCryolitia PukNgae switch (gpd_driver_priv.drvdata->board) { 473*0ab88e23SCryolitia PukNgae case win_mini: 474*0ab88e23SCryolitia PukNgae case win4_6800u: 475*0ab88e23SCryolitia PukNgae gpd_win_mini_set_pwm_enable(enable); 476*0ab88e23SCryolitia PukNgae break; 477*0ab88e23SCryolitia PukNgae case duo: 478*0ab88e23SCryolitia PukNgae gpd_duo_set_pwm_enable(enable); 479*0ab88e23SCryolitia PukNgae break; 480*0ab88e23SCryolitia PukNgae case win_max_2: 481*0ab88e23SCryolitia PukNgae gpd_wm2_set_pwm_enable(enable); 482*0ab88e23SCryolitia PukNgae break; 483*0ab88e23SCryolitia PukNgae } 484*0ab88e23SCryolitia PukNgae } 485*0ab88e23SCryolitia PukNgae 486*0ab88e23SCryolitia PukNgae static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata, 487*0ab88e23SCryolitia PukNgae enum hwmon_sensor_types type, u32 attr, 488*0ab88e23SCryolitia PukNgae __always_unused int channel) 489*0ab88e23SCryolitia PukNgae { 490*0ab88e23SCryolitia PukNgae if (type == hwmon_fan && attr == hwmon_fan_input) { 491*0ab88e23SCryolitia PukNgae return 0444; 492*0ab88e23SCryolitia PukNgae } else if (type == hwmon_pwm) { 493*0ab88e23SCryolitia PukNgae switch (attr) { 494*0ab88e23SCryolitia PukNgae case hwmon_pwm_enable: 495*0ab88e23SCryolitia PukNgae case hwmon_pwm_input: 496*0ab88e23SCryolitia PukNgae return 0644; 497*0ab88e23SCryolitia PukNgae default: 498*0ab88e23SCryolitia PukNgae return 0; 499*0ab88e23SCryolitia PukNgae } 500*0ab88e23SCryolitia PukNgae } 501*0ab88e23SCryolitia PukNgae return 0; 502*0ab88e23SCryolitia PukNgae } 503*0ab88e23SCryolitia PukNgae 504*0ab88e23SCryolitia PukNgae static int gpd_fan_hwmon_read(__always_unused struct device *dev, 505*0ab88e23SCryolitia PukNgae enum hwmon_sensor_types type, u32 attr, 506*0ab88e23SCryolitia PukNgae __always_unused int channel, long *val) 507*0ab88e23SCryolitia PukNgae { 508*0ab88e23SCryolitia PukNgae int ret; 509*0ab88e23SCryolitia PukNgae 510*0ab88e23SCryolitia PukNgae ret = mutex_lock_interruptible(&gpd_fan_sequence_lock); 511*0ab88e23SCryolitia PukNgae if (ret) 512*0ab88e23SCryolitia PukNgae return ret; 513*0ab88e23SCryolitia PukNgae 514*0ab88e23SCryolitia PukNgae if (type == hwmon_fan) { 515*0ab88e23SCryolitia PukNgae if (attr == hwmon_fan_input) { 516*0ab88e23SCryolitia PukNgae ret = gpd_read_rpm(); 517*0ab88e23SCryolitia PukNgae 518*0ab88e23SCryolitia PukNgae if (ret < 0) 519*0ab88e23SCryolitia PukNgae goto OUT; 520*0ab88e23SCryolitia PukNgae 521*0ab88e23SCryolitia PukNgae *val = ret; 522*0ab88e23SCryolitia PukNgae ret = 0; 523*0ab88e23SCryolitia PukNgae goto OUT; 524*0ab88e23SCryolitia PukNgae } 525*0ab88e23SCryolitia PukNgae } else if (type == hwmon_pwm) { 526*0ab88e23SCryolitia PukNgae switch (attr) { 527*0ab88e23SCryolitia PukNgae case hwmon_pwm_enable: 528*0ab88e23SCryolitia PukNgae *val = gpd_driver_priv.pwm_enable; 529*0ab88e23SCryolitia PukNgae ret = 0; 530*0ab88e23SCryolitia PukNgae goto OUT; 531*0ab88e23SCryolitia PukNgae case hwmon_pwm_input: 532*0ab88e23SCryolitia PukNgae ret = gpd_read_pwm(); 533*0ab88e23SCryolitia PukNgae 534*0ab88e23SCryolitia PukNgae if (ret < 0) 535*0ab88e23SCryolitia PukNgae goto OUT; 536*0ab88e23SCryolitia PukNgae 537*0ab88e23SCryolitia PukNgae *val = ret; 538*0ab88e23SCryolitia PukNgae ret = 0; 539*0ab88e23SCryolitia PukNgae goto OUT; 540*0ab88e23SCryolitia PukNgae } 541*0ab88e23SCryolitia PukNgae } 542*0ab88e23SCryolitia PukNgae 543*0ab88e23SCryolitia PukNgae ret = -EOPNOTSUPP; 544*0ab88e23SCryolitia PukNgae 545*0ab88e23SCryolitia PukNgae OUT: 546*0ab88e23SCryolitia PukNgae mutex_unlock(&gpd_fan_sequence_lock); 547*0ab88e23SCryolitia PukNgae return ret; 548*0ab88e23SCryolitia PukNgae } 549*0ab88e23SCryolitia PukNgae 550*0ab88e23SCryolitia PukNgae static int gpd_fan_hwmon_write(__always_unused struct device *dev, 551*0ab88e23SCryolitia PukNgae enum hwmon_sensor_types type, u32 attr, 552*0ab88e23SCryolitia PukNgae __always_unused int channel, long val) 553*0ab88e23SCryolitia PukNgae { 554*0ab88e23SCryolitia PukNgae int ret; 555*0ab88e23SCryolitia PukNgae 556*0ab88e23SCryolitia PukNgae ret = mutex_lock_interruptible(&gpd_fan_sequence_lock); 557*0ab88e23SCryolitia PukNgae if (ret) 558*0ab88e23SCryolitia PukNgae return ret; 559*0ab88e23SCryolitia PukNgae 560*0ab88e23SCryolitia PukNgae if (type == hwmon_pwm) { 561*0ab88e23SCryolitia PukNgae switch (attr) { 562*0ab88e23SCryolitia PukNgae case hwmon_pwm_enable: 563*0ab88e23SCryolitia PukNgae if (!in_range(val, 0, 3)) { 564*0ab88e23SCryolitia PukNgae ret = -EINVAL; 565*0ab88e23SCryolitia PukNgae goto OUT; 566*0ab88e23SCryolitia PukNgae } 567*0ab88e23SCryolitia PukNgae 568*0ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_enable = val; 569*0ab88e23SCryolitia PukNgae 570*0ab88e23SCryolitia PukNgae gpd_set_pwm_enable(gpd_driver_priv.pwm_enable); 571*0ab88e23SCryolitia PukNgae ret = 0; 572*0ab88e23SCryolitia PukNgae goto OUT; 573*0ab88e23SCryolitia PukNgae case hwmon_pwm_input: 574*0ab88e23SCryolitia PukNgae if (!in_range(val, 0, 255)) { 575*0ab88e23SCryolitia PukNgae ret = -ERANGE; 576*0ab88e23SCryolitia PukNgae goto OUT; 577*0ab88e23SCryolitia PukNgae } 578*0ab88e23SCryolitia PukNgae 579*0ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_value = val; 580*0ab88e23SCryolitia PukNgae 581*0ab88e23SCryolitia PukNgae ret = gpd_write_pwm(val); 582*0ab88e23SCryolitia PukNgae goto OUT; 583*0ab88e23SCryolitia PukNgae } 584*0ab88e23SCryolitia PukNgae } 585*0ab88e23SCryolitia PukNgae 586*0ab88e23SCryolitia PukNgae ret = -EOPNOTSUPP; 587*0ab88e23SCryolitia PukNgae 588*0ab88e23SCryolitia PukNgae OUT: 589*0ab88e23SCryolitia PukNgae mutex_unlock(&gpd_fan_sequence_lock); 590*0ab88e23SCryolitia PukNgae return ret; 591*0ab88e23SCryolitia PukNgae } 592*0ab88e23SCryolitia PukNgae 593*0ab88e23SCryolitia PukNgae static const struct hwmon_ops gpd_fan_ops = { 594*0ab88e23SCryolitia PukNgae .is_visible = gpd_fan_hwmon_is_visible, 595*0ab88e23SCryolitia PukNgae .read = gpd_fan_hwmon_read, 596*0ab88e23SCryolitia PukNgae .write = gpd_fan_hwmon_write, 597*0ab88e23SCryolitia PukNgae }; 598*0ab88e23SCryolitia PukNgae 599*0ab88e23SCryolitia PukNgae static const struct hwmon_channel_info *gpd_fan_hwmon_channel_info[] = { 600*0ab88e23SCryolitia PukNgae HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), 601*0ab88e23SCryolitia PukNgae HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), 602*0ab88e23SCryolitia PukNgae NULL 603*0ab88e23SCryolitia PukNgae }; 604*0ab88e23SCryolitia PukNgae 605*0ab88e23SCryolitia PukNgae static struct hwmon_chip_info gpd_fan_chip_info = { 606*0ab88e23SCryolitia PukNgae .ops = &gpd_fan_ops, 607*0ab88e23SCryolitia PukNgae .info = gpd_fan_hwmon_channel_info 608*0ab88e23SCryolitia PukNgae }; 609*0ab88e23SCryolitia PukNgae 610*0ab88e23SCryolitia PukNgae static int gpd_fan_probe(struct platform_device *pdev) 611*0ab88e23SCryolitia PukNgae { 612*0ab88e23SCryolitia PukNgae struct device *dev = &pdev->dev; 613*0ab88e23SCryolitia PukNgae const struct resource *region; 614*0ab88e23SCryolitia PukNgae const struct resource *res; 615*0ab88e23SCryolitia PukNgae const struct device *hwdev; 616*0ab88e23SCryolitia PukNgae 617*0ab88e23SCryolitia PukNgae res = platform_get_resource(pdev, IORESOURCE_IO, 0); 618*0ab88e23SCryolitia PukNgae if (IS_ERR(res)) 619*0ab88e23SCryolitia PukNgae return dev_err_probe(dev, PTR_ERR(res), 620*0ab88e23SCryolitia PukNgae "Failed to get platform resource\n"); 621*0ab88e23SCryolitia PukNgae 622*0ab88e23SCryolitia PukNgae region = devm_request_region(dev, res->start, 623*0ab88e23SCryolitia PukNgae resource_size(res), DRIVER_NAME); 624*0ab88e23SCryolitia PukNgae if (IS_ERR(region)) 625*0ab88e23SCryolitia PukNgae return dev_err_probe(dev, PTR_ERR(region), 626*0ab88e23SCryolitia PukNgae "Failed to request region\n"); 627*0ab88e23SCryolitia PukNgae 628*0ab88e23SCryolitia PukNgae hwdev = devm_hwmon_device_register_with_info(dev, 629*0ab88e23SCryolitia PukNgae DRIVER_NAME, 630*0ab88e23SCryolitia PukNgae NULL, 631*0ab88e23SCryolitia PukNgae &gpd_fan_chip_info, 632*0ab88e23SCryolitia PukNgae NULL); 633*0ab88e23SCryolitia PukNgae if (IS_ERR(hwdev)) 634*0ab88e23SCryolitia PukNgae return dev_err_probe(dev, PTR_ERR(region), 635*0ab88e23SCryolitia PukNgae "Failed to register hwmon device\n"); 636*0ab88e23SCryolitia PukNgae 637*0ab88e23SCryolitia PukNgae return 0; 638*0ab88e23SCryolitia PukNgae } 639*0ab88e23SCryolitia PukNgae 640*0ab88e23SCryolitia PukNgae static void gpd_fan_remove(__always_unused struct platform_device *pdev) 641*0ab88e23SCryolitia PukNgae { 642*0ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_enable = AUTOMATIC; 643*0ab88e23SCryolitia PukNgae gpd_set_pwm_enable(AUTOMATIC); 644*0ab88e23SCryolitia PukNgae } 645*0ab88e23SCryolitia PukNgae 646*0ab88e23SCryolitia PukNgae static struct platform_driver gpd_fan_driver = { 647*0ab88e23SCryolitia PukNgae .probe = gpd_fan_probe, 648*0ab88e23SCryolitia PukNgae .remove = gpd_fan_remove, 649*0ab88e23SCryolitia PukNgae .driver = { 650*0ab88e23SCryolitia PukNgae .name = KBUILD_MODNAME, 651*0ab88e23SCryolitia PukNgae }, 652*0ab88e23SCryolitia PukNgae }; 653*0ab88e23SCryolitia PukNgae 654*0ab88e23SCryolitia PukNgae static struct platform_device *gpd_fan_platform_device; 655*0ab88e23SCryolitia PukNgae 656*0ab88e23SCryolitia PukNgae static int __init gpd_fan_init(void) 657*0ab88e23SCryolitia PukNgae { 658*0ab88e23SCryolitia PukNgae const struct gpd_fan_drvdata *match = NULL; 659*0ab88e23SCryolitia PukNgae 660*0ab88e23SCryolitia PukNgae for (const struct gpd_fan_drvdata **p = gpd_module_drvdata; *p; p++) { 661*0ab88e23SCryolitia PukNgae if (strcmp(gpd_fan_board, (*p)->board_name) == 0) { 662*0ab88e23SCryolitia PukNgae match = *p; 663*0ab88e23SCryolitia PukNgae break; 664*0ab88e23SCryolitia PukNgae } 665*0ab88e23SCryolitia PukNgae } 666*0ab88e23SCryolitia PukNgae 667*0ab88e23SCryolitia PukNgae if (!match) { 668*0ab88e23SCryolitia PukNgae const struct dmi_system_id *dmi_match = 669*0ab88e23SCryolitia PukNgae dmi_first_match(dmi_table); 670*0ab88e23SCryolitia PukNgae if (dmi_match) 671*0ab88e23SCryolitia PukNgae match = dmi_match->driver_data; 672*0ab88e23SCryolitia PukNgae } 673*0ab88e23SCryolitia PukNgae 674*0ab88e23SCryolitia PukNgae if (!match) 675*0ab88e23SCryolitia PukNgae return -ENODEV; 676*0ab88e23SCryolitia PukNgae 677*0ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_enable = AUTOMATIC; 678*0ab88e23SCryolitia PukNgae gpd_driver_priv.pwm_value = 255; 679*0ab88e23SCryolitia PukNgae gpd_driver_priv.drvdata = match; 680*0ab88e23SCryolitia PukNgae 681*0ab88e23SCryolitia PukNgae struct resource gpd_fan_resources[] = { 682*0ab88e23SCryolitia PukNgae { 683*0ab88e23SCryolitia PukNgae .start = match->addr_port, 684*0ab88e23SCryolitia PukNgae .end = match->data_port, 685*0ab88e23SCryolitia PukNgae .flags = IORESOURCE_IO, 686*0ab88e23SCryolitia PukNgae }, 687*0ab88e23SCryolitia PukNgae }; 688*0ab88e23SCryolitia PukNgae 689*0ab88e23SCryolitia PukNgae gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver, 690*0ab88e23SCryolitia PukNgae gpd_fan_probe, 691*0ab88e23SCryolitia PukNgae gpd_fan_resources, 692*0ab88e23SCryolitia PukNgae 1, NULL, 0); 693*0ab88e23SCryolitia PukNgae 694*0ab88e23SCryolitia PukNgae if (IS_ERR(gpd_fan_platform_device)) { 695*0ab88e23SCryolitia PukNgae pr_warn("Failed to create platform device\n"); 696*0ab88e23SCryolitia PukNgae return PTR_ERR(gpd_fan_platform_device); 697*0ab88e23SCryolitia PukNgae } 698*0ab88e23SCryolitia PukNgae 699*0ab88e23SCryolitia PukNgae return 0; 700*0ab88e23SCryolitia PukNgae } 701*0ab88e23SCryolitia PukNgae 702*0ab88e23SCryolitia PukNgae static void __exit gpd_fan_exit(void) 703*0ab88e23SCryolitia PukNgae { 704*0ab88e23SCryolitia PukNgae platform_device_unregister(gpd_fan_platform_device); 705*0ab88e23SCryolitia PukNgae platform_driver_unregister(&gpd_fan_driver); 706*0ab88e23SCryolitia PukNgae } 707*0ab88e23SCryolitia PukNgae 708*0ab88e23SCryolitia PukNgae MODULE_DEVICE_TABLE(dmi, dmi_table); 709*0ab88e23SCryolitia PukNgae 710*0ab88e23SCryolitia PukNgae module_init(gpd_fan_init); 711*0ab88e23SCryolitia PukNgae module_exit(gpd_fan_exit); 712*0ab88e23SCryolitia PukNgae 713*0ab88e23SCryolitia PukNgae MODULE_LICENSE("GPL"); 714*0ab88e23SCryolitia PukNgae MODULE_AUTHOR("Cryolitia PukNgae <cryolitia@uniontech.com>"); 715*0ab88e23SCryolitia PukNgae MODULE_DESCRIPTION("GPD Devices fan control driver"); 716