/* * puma_thermal.c - Puma Thermal Driver * * Copyright (C) 2016-2017 Intel Corporation * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This driver registers to Thermal framework as SoC zones. It exposes * SoC DTS temperature with four writeable trip points. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "intel/thermal_interrupt.h" /* SOC DTS Registers */ #define PUMA_THERMAL_SENSORS 3 #define PUMA_CORE0_DTS_INDEX 0 #define PUMA_CORE1_DTS_INDEX 1 #define PUMA_SOC_DTS_INDEX 2 #define CPU_THERMAL_TRPIS 1 #define SOC_THERMAL_TRIPS 4 #define SOC_TRIP_MASK 0xF #define CPU0_TRIP_MASK 0 #define CPU1_TRIP_MASK 1 #define DTS_ENABLE 0x01 #define DTS_DISABLE 0x0 #if IS_ENABLED(CONFIG_PUMA_THERMAL_USE_IOSF_PRIMARY) #define SOC_DTS_CONTROL 0x0200 #define DTS_ENABLE_REG 0x02C0 #define PUNIT_TEMP_REG 0x02C4 #define PUNIT_AUX_REG 0x02C8 #define PUMA_PUNIT_PTTS 0x02CC #define PUMA_PUNIT_PTTSS 0x02D0 #define TE_AUX0 0x02D4 #define TE_AUX1 0x02D8 #define TE_AUX2 0x02DC #define TE_AUX3 0x02E0 #define TTE_SLM0 0x02F0 #define INT_ENABLE (1 << 11) #else #define SOC_DTS_CONTROL 0x80 #define DTS_ENABLE_REG 0xB0 #define PUNIT_TEMP_REG 0xB1 #define PUNIT_AUX_REG 0xB2 #define PUMA_PUNIT_PTTS 0xB3 #define PUMA_PUNIT_PTTSS 0xB4 #define TE_AUX0 0xB5 #define TE_AUX1 0xB6 #define TE_AUX2 0xB7 #define TE_AUX3 0xB8 #define TTE_SLM0 0xBC #define INT_ENABLE (1 << 9) #endif #define PUNIT_PORT 0x04 #define PUNIT_WRITE_OPCODE 0x07 #define PUNIT_READ_OPCODE 0x06 #define TJMAX_CODE 0x7F /* Max hysteresis values in C */ #define MAX_HYST 7 #define PUMA_MSR_THERM_INT 0x19B #define HI_TEMP_INT_EN 0x1 #define LO_TEMP_INT_EN (1 << 1) #define TEMP_THRESHOLD1_INT_EN (1 << 15) #define TEMP_THRESHOLD2_INT_EN (1 << 23) #define CPU_INT_EN (HI_TEMP_INT_EN | LO_TEMP_INT_EN | TEMP_THRESHOLD1_INT_EN | TEMP_THRESHOLD2_INT_EN) #define PUMA_MSR_THERM_STATUS 0x19C #define PUMA_THERMAL_MONITOR_STS 0x1 #define TEMP_THRESHOLD1_INT_STS (1 << 6) #define TEMP_THRESHOLD1_INT_LOG (1 << 7) #define TEMP_THRESHOLD2_INT_STS (1 << 8) #define TEMP_THRESHOLD2_INT_LOG (1 << 9) #define PUMA_CPU_CORES 2 #define PUMA_CORE0_TT 1 #define PUMA_CORE0_NOT_TT (1 << 1) #define PUMA_CORE1_TT (1 << 2) #define PUMA_CORE1_NOT_TT (1 << 3) #define PUMA_CORE0_ABOVE_TH1 (1 << 4) #define PUMA_CORE0_BELOW_TH1 (1 << 5) #define PUMA_CORE1_ABOVE_TH1 (1 << 6) #define PUMA_CORE1_BELOW_TH1 (1 << 7) #define PUMA_CORE0_ABOVE_TH2 (1 << 8) #define PUMA_CORE0_BELOW_TH2 (1 << 9) #define PUMA_CORE1_ABOVE_TH2 (1 << 10) #define PUMA_CORE1_BELOW_TH2 (1 << 11) #define MSR_THERM_CFG1 0x673 #define MSR_THERM_CFG2 0x674 #define ENABLE_CPU_TT (1 << 16) #define PUMA_ACPI_NOTIFY_CPU_DTS_TRIP 0x93 #define PUMA_ACPI_NOTIFY_SOC_DTS_TRIP 0x94 #define PUMA_SOC_THERMAL_TRIP0 1 #define PUMA_THERMAL_CLASS "thermal" #define PUMA_DEVICE_NAME "puma_dts" #define MAX_NAME_LEN 20 struct thermal_device_info { struct mutex lock; int sensor_index; int trips; int mask; enum thermal_device_mode mode; char name[MAX_NAME_LEN]; }; struct thermal_soc_data { struct thermal_zone_device *tzd[PUMA_THERMAL_SENSORS]; struct thermal_device_info tdi[PUMA_THERMAL_SENSORS]; struct work_struct twq; struct mutex event_lock; atomic_t cpu_therm_event; atomic_t soc_therm_event; unsigned int cpu_therm_status; unsigned int soc_therm_status; }; struct puma_thermal_mmio { unsigned long region1_base; unsigned long region1_size; volatile void __iomem *reg_base; }; static const struct pci_device_id puma_thermal_pci_tbl[] = { { PCI_DEVICE( 0x8086, 0x2BDC), .driver_data = 0 }, {0}, }; static struct puma_thermal_mmio puma_therm_mmio; MODULE_DEVICE_TABLE(pci, puma_thermal_pci_tbl); /* TJ MAX temperature */ static int puma_tjmax_temp; static struct thermal_soc_data *pdata_therm; static inline int puma_read_punit_reg(unsigned int addr, u32 *read_val) { #if IS_ENABLED(CONFIG_PUMA_THERMAL_USE_IOSF_PRIMARY) *read_val = __raw_readl(puma_therm_mmio.reg_base + addr); return 0; #else return (iosf_mbi_read(PUNIT_PORT, PUNIT_READ_OPCODE, addr, read_val)); #endif } static inline int puma_write_punit_reg(unsigned int addr, u32 val) { #if IS_ENABLED(CONFIG_PUMA_THERMAL_USE_IOSF_PRIMARY) __raw_writel(val, puma_therm_mmio.reg_base + addr); return 0; #else return (iosf_mbi_write(PUNIT_PORT, PUNIT_WRITE_OPCODE, addr, val)); #endif } /* Get the maximum junction temperature/throttle temperature */ static int puma_get_tj_max(int *tj_max) { u32 eax, edx; u32 val; int err = 0; err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); if (err == 0) { val = (eax >> 16) & 0xff; if (val) *tj_max = val * 1000; else { err = -EINVAL; } } return err; } static int puma_configure_dts(void) { int ret; /* * Enable the SOC DTS */ if((ret = puma_write_punit_reg(DTS_ENABLE_REG, DTS_ENABLE)) < 0) { pr_err("PUMA Failed to enable SOC DTS: %d\n", ret); } return ret; } static int puma_show_trip_hyst(struct thermal_zone_device *tzd, int trip, int *hyst) { u32 eax, edx; int ret; struct thermal_device_info *td_info = tzd->devdata; /* Hysteresis is only supported for trip point 0 */ if (trip != 0) { return -EINVAL; } mutex_lock(&td_info->lock); ret = rdmsr_safe_on_cpu(0, MSR_THERM_CFG1, &eax, &edx); if(ret == 0) { /* B[8:10] H2C Hyst, for trip 0. Report hysteresis in mC */ *hyst = ((eax >> 8) & 0x7) * 1000; } else { pr_err("PUMA read MSR_THERM_CFG1 failed: %d\n", ret); } mutex_unlock(&td_info->lock); return ret; } static int puma_store_trip_hyst(struct thermal_zone_device *tzd, int trip, int hyst) { u32 eax, edx; int ret; struct thermal_device_info *td_info = tzd->devdata; /* Convert from mC to C */ hyst /= 1000; if (trip != 0 || hyst < 0 || hyst > MAX_HYST) return -EINVAL; mutex_lock(&td_info->lock); ret = rdmsr_safe_on_cpu(0, MSR_THERM_CFG1, &eax, &edx); if(ret == 0) { /* B[8:10] H2C Hyst */ eax = (eax & ~(0x7 << 8)) | (hyst << 8); if((ret = wrmsr_safe_on_cpu(0, MSR_THERM_CFG1, eax, edx)) < 0) { pr_err("PUMA write MSR_THERM_CFG1 failed: %d\n", ret); } } else { pr_err("PUMA read MSR_THERM_CFG1 failed: %d\n", ret); } mutex_unlock(&td_info->lock); return ret; } static int puma_read_cpu_temp(unsigned int cpu, int *temp) { int ret = 0; u32 eax, edx; int term_valid_range; /* get valid range */ if((ret = rdmsr_safe_on_cpu(0, MSR_THERM_CFG2, &eax, &edx)) < 0) { pr_err("PUMA read MSR_THERM_CFG2 failed: %d\n", ret); } else { term_valid_range = (eax >> 8) & 0xF; if((ret = rdmsr_safe_on_cpu(cpu, PUMA_MSR_THERM_STATUS, &eax, &edx)) < 0) { pr_err("PUMA read PUMA_MSR_THERM_STATUS failed: %d\n", ret); } else { if(term_valid_range) { /* valid range below TJMAX */ if(eax & 0x80000000) { *temp = puma_tjmax_temp - ((eax >> 16) & 0x7F) * 1000; } else { ret = -EAGAIN; } } else { /* valid range in both directions of TJMAX */ if(eax & 0x80000000) { *temp = puma_tjmax_temp - ((eax >> 16) & 0x7F) * 1000; } else { *temp = puma_tjmax_temp + ((eax >> 16) & 0x7F) * 1000; } } } } return ret; } static int puma_show_temp(struct thermal_zone_device *tzd, int *temp) { struct thermal_device_info *td_info = tzd->devdata; int ret = 0; u32 val; mutex_lock(&td_info->lock); switch(td_info->sensor_index) { case PUMA_CORE0_DTS_INDEX: ret = puma_read_cpu_temp(0, temp); break; case PUMA_CORE1_DTS_INDEX: ret = puma_read_cpu_temp(1, temp); break; case PUMA_SOC_DTS_INDEX: if((ret = puma_read_punit_reg(PUNIT_TEMP_REG, &val)) < 0) { pr_err("PUMA PUNIT_TEMP_REG read failed: %d\n", ret); } else { val = val & 0xFF; if(val >= TJMAX_CODE) { /* Calibrate the temperature */ val = val - TJMAX_CODE; *temp = puma_tjmax_temp - val * 1000; } else { val = TJMAX_CODE - val; *temp = puma_tjmax_temp + val * 1000; } } break; default: ret = -EINVAL; pr_err("PUMA show temp failed: %d\n", ret); } mutex_unlock(&td_info->lock); return ret; } static int puma_change_mode(struct thermal_zone_device *tzd, enum thermal_device_mode mode) { struct thermal_device_info *td_info = tzd->devdata; int ret = 0; u32 val, eax, edx; mutex_lock(&td_info->lock); if(td_info->mode != mode) { td_info->mode = mode; switch(td_info->sensor_index) { case PUMA_CORE0_DTS_INDEX: case PUMA_CORE1_DTS_INDEX: /* CPU module 0 can trigger thermal event */ if((ret = puma_read_punit_reg(SOC_DTS_CONTROL, &val)) < 0) { pr_err("PUMA SOC_DTS_CONTROL read failed: %d\n", ret); } else if((ret = puma_write_punit_reg(SOC_DTS_CONTROL, ((mode == THERMAL_DEVICE_ENABLED) ? (val | ENABLE_CPU_TT) : (val & ~ENABLE_CPU_TT)))) < 0) { pr_err("PUMA SOC_DTS_CONTROL write failed: %d\n", ret); } else if((ret = puma_read_punit_reg(TTE_SLM0, &val)) < 0) { pr_err("PUMA TTE_SLM0 read failed: %d\n", ret); } else if((ret = puma_write_punit_reg(TTE_SLM0, ((mode == THERMAL_DEVICE_ENABLED) ? (val | INT_ENABLE) : (val & ~INT_ENABLE)))) < 0) { pr_err("PUMA TTE_SLM0 write failed: %d\n", ret); } else if((ret = rdmsr_safe_on_cpu(0, PUMA_MSR_THERM_INT, &eax, &edx)) < 0) { pr_err("PUMA read PUMA_MSR_THERM_INT failed for CPU 0: %d\n", ret); } else if((ret = wrmsr_safe_on_cpu(0, PUMA_MSR_THERM_INT, ((mode == THERMAL_DEVICE_ENABLED) ? (eax | CPU_INT_EN) : (eax & ~CPU_INT_EN)), edx)) < 0) { pr_err("PUMA write PUMA_MSR_THERM_INT failed for CPU 0: %d\n", ret); } else if((ret = rdmsr_safe_on_cpu(1, PUMA_MSR_THERM_INT, &eax, &edx)) < 0) { pr_err("PUMA read PUMA_MSR_THERM_INT failed for CPU 1: %d\n", ret); } else if((ret = wrmsr_safe_on_cpu(1, PUMA_MSR_THERM_INT, ((mode == THERMAL_DEVICE_ENABLED) ? (eax | CPU_INT_EN) : (eax & ~CPU_INT_EN)), edx)) < 0) { pr_err("PUMA write PUMA_MSR_THERM_INT failed for CPU 1: %d\n", ret); } break; default: ret = -EINVAL; pr_err("PUMA set mode failed: %d\n", ret); } } mutex_unlock(&td_info->lock); return ret; } static int puma_show_trip_type(struct thermal_zone_device *tzd, int trip, enum thermal_trip_type *trip_type) { /* All are passive trip points */ *trip_type = THERMAL_TRIP_PASSIVE; return 0; } static int puma_show_trip_temp(struct thermal_zone_device *tzd, int trip, int *trip_temp) { struct thermal_device_info *td_info = tzd->devdata; u32 aux_value, eax, edx; int ret = 0; mutex_lock(&td_info->lock); switch(td_info->sensor_index) { case PUMA_CORE0_DTS_INDEX: if((ret = puma_get_tj_max(trip_temp)) < 0) { pr_err("PUMA get TJMax failed: %d\n", ret); } break; case PUMA_CORE1_DTS_INDEX: if((ret = rdmsr_safe_on_cpu(1, PUMA_MSR_THERM_INT, &eax, &edx)) < 0) { pr_err("PUMA read PUMA_MSR_THERM_INT failed: %d\n", ret); } else { *trip_temp = puma_tjmax_temp - ((eax >> 16) & 0x7F) * 1000; } break; case PUMA_SOC_DTS_INDEX: if((ret = puma_read_punit_reg(PUNIT_AUX_REG, &aux_value)) < 0) { pr_err("PUMA PUNIT_AUX_REG read failed: %d\n", ret); } else { /* aux0 b[0:7], aux1 b[8:15], aux2 b[16:23], aux3 b[24:31] */ *trip_temp = (aux_value >> (8 * trip)) & 0xFF; /* Calibrate the trip point temperature */ *trip_temp = puma_tjmax_temp - *trip_temp * 1000; } break; default: ret = -EINVAL; pr_err("PUMA show trip temp failed: %d\n", ret); } mutex_unlock(&td_info->lock); return ret; } static int puma_store_trip_temp(struct thermal_zone_device *tzd, int trip, int trip_temp) { u32 aux_trip, thermal_trip_trig, thermal_event_int, aux = 0; u32 eax, edx; int ret = 0, i; unsigned int TE_AUX[] = {TE_AUX0, TE_AUX1, TE_AUX2, TE_AUX3}; struct thermal_device_info *td_info = tzd->devdata; mutex_lock(&td_info->lock); switch(td_info->sensor_index) { case PUMA_CORE1_DTS_INDEX: if(trip_temp < puma_tjmax_temp) { trip_temp = (puma_tjmax_temp - trip_temp)/1000; trip_temp = trip_temp & 0x7F; aux_trip = trip_temp << 16; for(i = 0; i < PUMA_CPU_CORES; i++) { if((ret = rdmsr_safe_on_cpu(i, PUMA_MSR_THERM_INT, &eax, &edx)) < 0) { pr_err("PUMA read PUMA_MSR_THERM_INT failed: %d\n", ret); } else { eax = eax & ~(0x7F << 16); if((ret = wrmsr_safe_on_cpu(i, PUMA_MSR_THERM_INT, (eax | aux_trip), edx)) < 0) { pr_err("PUMA read PUMA_MSR_THERM_INT failed: %d\n", ret); } } } } else { pr_err("PUMA store trip temp failed: invalid trip temp\n"); ret = -EINVAL; } break; case PUMA_SOC_DTS_INDEX: /* Convert from mC to C */ trip_temp /= 1000; /* The trip temp is 8 bits wide (unsigned) */ if (trip_temp > 255) { pr_err("PUMA store trip temp failed: invalid trip temp\n"); ret = -EINVAL; } else { /* Assign last byte to unsigned 32 */ aux_trip = trip_temp & 0xFF; /* Calibrate w.r.t TJMAX_TEMP */ aux_trip = puma_tjmax_temp/1000 - aux_trip; if((ret = puma_read_punit_reg(PUNIT_AUX_REG, &aux)) < 0) { pr_err("PUMA PUNIT_AUX_REG read failed %d\n", ret); } else { aux = (aux & ~(0xFF << (8 * trip))) | (aux_trip << (8 * trip)); if((ret = puma_write_punit_reg(PUNIT_AUX_REG, aux)) < 0) { pr_err("PUMA PUNIT_AUX_REG write failed %d\n", ret); } else { /* Enable the thermal trip throttling and interrupt */ if((ret = puma_read_punit_reg(SOC_DTS_CONTROL, &thermal_trip_trig)) < 0) { pr_err("PUMA SOC_DTS_CONTROL read failed %d\n", ret); } else if ((ret = puma_write_punit_reg(SOC_DTS_CONTROL, ((trip_temp) ? (thermal_trip_trig |= (1 << trip)) : (thermal_trip_trig &= ~(1 << trip))))) < 0) { pr_err("PUMA SOC_DTS_CONTROL write failed %d\n", ret); } else if((ret = puma_read_punit_reg(TE_AUX[trip], &thermal_event_int)) < 0) { pr_err("PUMA TE_AUXx read failed %d\n", ret); } else if ((ret = puma_write_punit_reg(TE_AUX[trip], ((trip_temp) ? (thermal_event_int |= INT_ENABLE) : (thermal_event_int &= ~INT_ENABLE)))) < 0) { pr_err("PUMA TE_AUXx write failed %d\n", ret); } } } } break; case PUMA_CORE0_DTS_INDEX: default: ret = -EINVAL; pr_err("PUMA show trip temp failed: %d\n", ret); } mutex_unlock(&td_info->lock); return ret; } static struct thermal_zone_device_ops tzd_soc_ops[] = { { .get_temp = puma_show_temp, .change_mode = puma_change_mode, .get_trip_type = puma_show_trip_type, .get_trip_temp = puma_show_trip_temp, .get_trip_hyst = puma_show_trip_hyst, .set_trip_hyst = puma_store_trip_hyst, }, { .get_temp = puma_show_temp, .get_trip_type = puma_show_trip_type, .get_trip_temp = puma_show_trip_temp, .set_trip_temp = puma_store_trip_temp, }, { .get_temp = puma_show_temp, .get_trip_type = puma_show_trip_type, .get_trip_temp = puma_show_trip_temp, .set_trip_temp = puma_store_trip_temp, }, }; static const struct x86_cpu_id puma_ids[] = { { X86_VENDOR_INTEL, X86_FAMILY_ANY, 0x6E }, {} }; MODULE_DEVICE_TABLE(x86cpu, puma_ids); static int puma_thermal_callback(__u64 msr_val) { atomic_set(&pdata_therm->cpu_therm_event, 1); schedule_work(&pdata_therm->twq); return 0; } static int puma_thermal_init(void) { int i, ret = 0; char *name[PUMA_THERMAL_SENSORS] = {"Core0_DTS", "Core1_DTS", "SoC_DTS"}; int trips[PUMA_THERMAL_SENSORS] = {CPU_THERMAL_TRPIS, CPU_THERMAL_TRPIS, SOC_THERMAL_TRIPS}; int mask[PUMA_THERMAL_SENSORS] = {CPU0_TRIP_MASK, CPU1_TRIP_MASK, SOC_TRIP_MASK}; int index[PUMA_THERMAL_SENSORS] = {PUMA_CORE0_DTS_INDEX, PUMA_CORE1_DTS_INDEX, PUMA_SOC_DTS_INDEX}; struct thermal_device_info *tdi_ptr; if (!x86_match_cpu(puma_ids)) return -ENODEV; if (puma_get_tj_max(&puma_tjmax_temp)) return -EINVAL; pdata_therm = (struct thermal_soc_data *)kzalloc(sizeof(struct thermal_soc_data), GFP_KERNEL); if (!pdata_therm) return -ENOMEM; tdi_ptr = &pdata_therm->tdi[0]; /* Register each sensor with the generic thermal framework */ for (i = 0; i < PUMA_THERMAL_SENSORS; i++) { tdi_ptr->sensor_index = index[i]; mutex_init(&tdi_ptr->lock); strlcpy(tdi_ptr->name, name[i], MAX_NAME_LEN); tdi_ptr->trips = trips[i]; tdi_ptr->mask = mask[i]; tdi_ptr->mode = THERMAL_DEVICE_DISABLED; pdata_therm->tzd[i] = thermal_zone_device_register(tdi_ptr->name, tdi_ptr->trips, tdi_ptr->mask, tdi_ptr, &tzd_soc_ops[i], NULL, 0, 0); if (IS_ERR(pdata_therm->tzd[i])) { ret = PTR_ERR(pdata_therm->tzd[i]); pr_err("PUMA tzd register failed: %d\n", ret); break; } tdi_ptr++; } if(ret == 0) { /* Configure DTS */ if((ret = puma_configure_dts()) != 0) { pr_err("PUMA configure DTS failed: %d\n", ret); } else { mutex_init(&pdata_therm->event_lock); atomic_set(&pdata_therm->cpu_therm_event, 0); atomic_set(&pdata_therm->soc_therm_event, 0); platform_thermal_notify = &puma_thermal_callback; } } if(ret != 0) { while (--i >= 0) { thermal_zone_device_unregister(pdata_therm->tzd[i]); } kfree(pdata_therm); } else { printk(KERN_INFO "Initializing Puma thermal driver"); } return ret; } static void puma_thermal_exit(void) { int i; struct thermal_device_info *tdi_ptr; mutex_destroy(&pdata_therm->event_lock); tdi_ptr = &pdata_therm->tdi[0]; /* Destroy mutex and unregister each sensor with the generic thermal framework */ for (i = 0; i < PUMA_THERMAL_SENSORS; i++) { mutex_destroy(&tdi_ptr->lock); thermal_zone_device_unregister(pdata_therm->tzd[i]); tdi_ptr++; } platform_thermal_notify = NULL; kfree(pdata_therm); } static void puma_thermal_do_wq(struct work_struct *twq) { struct thermal_soc_data *data = container_of(twq, struct thermal_soc_data, twq); unsigned int i; u32 eax, edx; int cpu_thermal_event = 0, soc_thermal_event = 0; mutex_lock(&data->event_lock); if(atomic_read(&data->cpu_therm_event)) { atomic_set(&data->cpu_therm_event, 0); cpu_thermal_event = 1; } if(atomic_read(&data->soc_therm_event)) { atomic_set(&data->soc_therm_event, 0); soc_thermal_event = 1; } data->cpu_therm_status = 0; for(i = 0; i < PUMA_CPU_CORES; i++) { if((rdmsr_safe_on_cpu(i, PUMA_MSR_THERM_STATUS, &eax, &edx)) < 0) { pr_err("Puma ACPI ERROR: rdmsr_safe_on_cpu %d failed\n", i); } else { if(eax & PUMA_THERMAL_MONITOR_STS) { data->cpu_therm_status |= ((i == 0) ? PUMA_CORE0_TT : PUMA_CORE1_TT); } else { data->cpu_therm_status |= ((i == 0) ? PUMA_CORE0_NOT_TT : PUMA_CORE1_NOT_TT); } if((eax & TEMP_THRESHOLD1_INT_STS) && (eax & TEMP_THRESHOLD1_INT_LOG)) { data->cpu_therm_status |= ((i == 0) ? PUMA_CORE0_ABOVE_TH1 : PUMA_CORE1_ABOVE_TH1); } else if (((eax & TEMP_THRESHOLD1_INT_STS) == 0) && (eax & TEMP_THRESHOLD1_INT_LOG)) { data->cpu_therm_status |= ((i == 0) ? PUMA_CORE0_BELOW_TH1 : PUMA_CORE1_BELOW_TH1); } if ((eax & TEMP_THRESHOLD2_INT_STS) && (eax & TEMP_THRESHOLD2_INT_LOG)) { data->cpu_therm_status |= ((i == 0) ? PUMA_CORE0_ABOVE_TH2 : PUMA_CORE1_ABOVE_TH2); } else if (((eax & TEMP_THRESHOLD2_INT_STS) == 0) && (eax & TEMP_THRESHOLD2_INT_LOG)) { data->cpu_therm_status |= ((i == 0) ? PUMA_CORE0_BELOW_TH2 : PUMA_CORE1_BELOW_TH2); } } } if((puma_read_punit_reg(PUMA_PUNIT_PTTS, &eax)) < 0) { pr_err("puma_read_punit_reg PTTS failed\n"); } else if((puma_read_punit_reg(PUMA_PUNIT_PTTSS, &edx)) < 0) { pr_err("puma_read_punit_reg PTTSS failed\n"); } else if((puma_write_punit_reg(PUMA_PUNIT_PTTSS, edx)) < 0) { pr_err("puma_write_punit_reg PTTSS failed\n"); } else { data->soc_therm_status = eax & 0xF; } if(cpu_thermal_event){ acpi_bus_generate_netlink_event(PUMA_THERMAL_CLASS, PUMA_DEVICE_NAME, PUMA_ACPI_NOTIFY_CPU_DTS_TRIP, ((data->soc_therm_status << 16) | data->cpu_therm_status)); } if(soc_thermal_event){ acpi_bus_generate_netlink_event(PUMA_THERMAL_CLASS, PUMA_DEVICE_NAME, PUMA_ACPI_NOTIFY_SOC_DTS_TRIP, ((data->soc_therm_status << 16) | data->cpu_therm_status)); } mutex_unlock(&data->event_lock); } static irqreturn_t puma_thermal_isr(int irq, void *data) { struct thermal_soc_data *td = data; atomic_set(&td->soc_therm_event, 1); schedule_work(&td->twq); return IRQ_HANDLED; } static int puma_thermal_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret = 0; ret = pci_enable_device(pdev); if (ret) { dev_err(&pdev->dev, "Puma PUNIT pci_enable_device failed.\n"); return ret; } ret = pci_enable_msi(pdev); if (ret) { if(-EINVAL == ret) { dev_err(&pdev->dev, "PUMA pci_enable_msi returned invalid param. %x", (unsigned int) ret); } dev_err(&pdev->dev, "PUMA pci_enable_msi failed. %x", (unsigned int) ret); goto free_dev; } puma_therm_mmio.region1_base = pci_resource_start(pdev,0); puma_therm_mmio.region1_size = pci_resource_len(pdev,0); if ((ret = (pci_request_regions(pdev, "Puma_thermal")))){ dev_err(&pdev->dev, "Puma cannot obtain PCI resources\n"); goto free_dev; } puma_therm_mmio.reg_base = (void __iomem *)ioremap(puma_therm_mmio.region1_base, puma_therm_mmio.region1_size); if (!puma_therm_mmio.reg_base) { dev_err( &pdev->dev, "Puma failed to ioremap thermal base\n"); ret = -ENOMEM; goto free_resource; } if((ret = puma_thermal_init()) != 0) { dev_err( &pdev->dev, "Puma thermal init failed\n"); goto free_iomem; } INIT_WORK(&pdata_therm->twq, puma_thermal_do_wq); if((ret = request_irq(pdev->irq, puma_thermal_isr, 0, "Puma Thermal", (void *)pdata_therm)) != 0) { dev_err( &pdev->dev, "Puma thermal request IRQ failed\n"); goto free_mem; } pci_set_drvdata(pdev, pdata_therm); return 0; free_mem: puma_thermal_exit(); free_iomem: iounmap(puma_therm_mmio.reg_base); free_resource: pci_release_regions(pdev); free_dev: pci_disable_device(pdev); return ret; } static void puma_thermal_remove(struct pci_dev *pdev) { iounmap(puma_therm_mmio.reg_base); puma_thermal_exit(); pci_release_regions(pdev); pci_disable_device(pdev); } static struct pci_driver puma_thermal_driver = { .name = "puma_thermal_driver", .id_table = puma_thermal_pci_tbl, .probe = puma_thermal_probe, .remove = puma_thermal_remove, }; static int __init puma_thermal_module_init(void) { #if IS_ENABLED(CONFIG_PUMA_THERMAL_USE_IOSF_PRIMARY) return pci_register_driver(&puma_thermal_driver); #else return puma_thermal_init(); #endif } static void __exit puma_thermal_module_exit(void) { #if IS_ENABLED(CONFIG_PUMA_THERMAL_USE_IOSF_PRIMARY) pci_unregister_driver(&puma_thermal_driver); #else puma_thermal_exit(); #endif } module_init(puma_thermal_module_init); module_exit(puma_thermal_module_exit); MODULE_AUTHOR("Vinay Patel "); MODULE_DESCRIPTION("PUMA Thermal Driver"); MODULE_LICENSE("GPL");