/* * PUMA platform ACPI driver * * Copyright(c) 2015-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Corporation * 2200 Mission College Blvd. * Santa Clara, CA 97052 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../pci/pci.h" /* fifo size in elements (ints) */ #define PUMA_ACPI_FIFO_SIZE 32 /* Event bits the ACPI thread waits on */ #define PUMA_ACPI_EVENT 0 #define PUMA_NETIP_EVENT 1 #define PUMA_TIMER_EVENT 2 /* ACPI power status register bits */ #define PUMA_ACPI_POWER_LIVE_MASK 0x1 #define PUMA_ACPI_POWER_LATCH_MASK 0x2 /* PUMA ACPI notifications */ #define PUMA_ACPI_NOTIFY_ACTIVE_ON_AC 0x80 #define PUMA_ACPI_NOTIFY_ACTIVE_ON_BATTERY 0x81 #define PUMA_ACPI_NOTIFY_AC_SPIKE 0x83 #define PUMA_ACPI_NOTIFY_BATTERY_SPIKE 0x84 #define PUMA_ACPI_NOTIFY_PWR_BUTTON_PRS 0x85 #define PUMA_ACPI_NOTIFY_PWR_BUTTON_RLS 0x86 #define PUMA_ACPI_NOTIFY_FAST_STANBY 0x87 #define PUMA_ACPI_NOTIFY_DEEP_STANDBY 0x88 #define PUMA_ACPI_NOTIFY_BATTERY_LOW 0x89 #define PUMA_ACPI_NOTIFY_PMC2ATOM_IPC 0x8A #define PUMA_ACPI_NOTIFY_NETIP_CONNECTED_IDLE 0x8B #define PUMA_ACPI_NOTIFY_NETIP_REQUEST_RESET 0x8C #define PUMA_ACPI_NOTIFY_PLATFORM_RESOURCES_ON 0x8D #define PUMA_ACPI_NOTIFY_PLATFORM_RESOURCES_OFF 0x8E #define PUMA_ACPI_NOTIFY_PWR_STAT_CNG_DURING_SB 0x8F #define PUMA_ACPI_NOTIFY_CPU_DTS_TRIP 0x93 #define PUMA_ACPI_NOTIFY_SOC_DTS_TRIP 0x94 #define PUMA_ACPI_NOTIFY_PCIE_PORT0_ON 0x95 #define PUMA_ACPI_NOTIFY_PCIE_PORT0_OFF 0x96 #define PUMA_ACPI_NOTIFY_PCIE_PORT1_ON 0x97 #define PUMA_ACPI_NOTIFY_PCIE_PORT1_OFF 0x98 #define PUMA_ACPI_NOTIFY_PCIE_PORT2_ON 0x99 #define PUMA_ACPI_NOTIFY_PCIE_PORT2_OFF 0x9A #define PUMA_ACPI_NOTIFY_PCIE_PORT3_ON 0x9B #define PUMA_ACPI_NOTIFY_PCIE_PORT3_OFF 0x9C #define PUMA_ACPI_NOTIFY_USB_ON 0x9D #define PUMA_ACPI_NOTIFY_USB_OFF 0x9E #define PUMA_ACPI_NOTIFY_SD_ON 0x9F #define PUMA_ACPI_NOTIFY_SD_OFF 0xA0 #define PUMA_ACPI_NOTIFY_SATA_ON 0xA1 #define PUMA_ACPI_NOTIFY_SATA_OFF 0xA2 #define PUMA_ACPI_NOTIFY_SFP_ACTIVE 0xA3 #define PUMA_ACPI_NOTIFY_NETIP_HADNDSHAKE_DONE 0xA4 #define PUMA_ACPI_NOTIFY_ERP_ACTIVE 0xA5 #define PUMA_ACPI_NOTIFY_ERP_INACTIVE 0xA6 //Power test automation support control method #define PUMA_ACPI_PTAS "PTAS" /* PUMA initial devices state and mask in lower power mode */ #define PUMA_BBU_DEVICE_MASK 0 #define PUMA_SB_DEVICE_MASK 0 #define PUMA_DSB_DEVICE_MASK 0 #define PUMA_DEV_MASK 0x7F #define PUMA_DEV_PCIE_PORT0 (1) #define PUMA_DEV_PCIE_PORT1 (1 << 1) #define PUMA_DEV_PCIE_PORT2 (1 << 2) #define PUMA_DEV_PCIE_PORT3 (1 << 3) #define PUMA_DEV_USB (1 << 4) #define PUMA_DEV_SD (1 << 5) #define PUMA_DEV_SATA (1 << 6) /* CPU thermal status */ #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) /* SOC thermal status */ #define PUMA_PUNIT_PORT 0x04 #define PUMA_PUNIT_WRITE_OPCODE 0x07 #define PUMA_PUNIT_READ_OPCODE 0x06 #define PUMA_PUNIT_PTTS 0xB3 #define PUMA_PUNIT_PTTSS 0xB4 #define PUMA_PUNIT_TE_AUX0 0xB5 #define PUMA_PUNIT_TE_AUX1 0xB6 #define PUMA_PUNIT_TE_AUX2 0xB7 #define PUMA_PUNIT_TE_AUX3 0xB8 #define PUMA_SOC_THERMAL_TRIPS 4 #define PUMA_SOC_THERMAL_TRIP0 1 #define PUMA_SOC_TE_DDR_2X_REFRESH 1 #define PUMA_SOC_TE_BW_THROT (1 << 1) #define PUMA_SOC_TE_DDR_FREQ_THROT (1 << 2) #define PUMA_SOC_TE_CPU_THROT (1 << 3) #define PUMA_SOC_TE_CPU_THROT_MASK 0x20000 #define PUMA_SOC_TE_MASK (PUMA_SOC_TE_CPU_THROT_MASK) /* PUMA ACPI TIMER durations in ms */ #define PUMA_ACPI_TIMER_THRS_SB 5000 #define PUMA_ACPI_TIMER_LED_SB 1000 #define PUMA_ACPI_TIMER_LED_DSB 500 /* PUMA PMC IPC */ #define PUMA_PMC_PORT_ID 0x52 #define PUMA_PMC_READ_OPCODE 0x06 #define PUMA_PMC_WRITE_OPCODE 0x07 #define PUMA_ATOM2PMC_CSR 0x8140 #define PUMA_ATOM2PMC_DB 0x8144 #define PUMA_ATOM2PMC_DBM 0x815C #define PUMA_PMC2ATOM_CSR 0x8150 #define PUMA_PMC2ATOM_DB 0x8154 #define PUMA_PMC2ATOM_DBM 0x814C #define PUMA_PMC_D3_STS_0 0x0A0 #define PUMA_PMC_D3_STS_1 0x0A4 #define PUMA_PMC_FUNC_DIS_0 0x34 #define PUMA_PMC_FUNC_DIS_1 0x38 #define PUMA_PMC_DISABLE_IP_0 0xD8 #define PUMA_PMC_DISABLE_IP_1 0xDC #define PUMA_PMC_MEM_READ_OPCODE 0x00 #define PUMA_PMC_MEM_WRITE_OPCODE 0x01 #define PUMA_PMC_VUART4 0x0FC #define PUMA_PMC_RESET_INFO 0x0E8 #define PUMA_PMC_RESET_CAUSE_MASK 0x7F000000 #define PUMA_PMC_RESET_CAUSE_SHIFT 24 #define PUMA_PMC_RESET_TYPE_MASK 0xFF0000 #define PUMA_PMC_RESET_TYPE_SHIFT 16 #define PUMA_PMC_RESET_BTN_DUR_MASK 0xF #define PUMA_PMC_FUNC_DIS0_SD (1 << 10) #define PUMA_PMC_FUNC_DIS0_SATA (1 << 17) #define PUMA_PMC_FUNC_DIS0_XHCI (1 << 18) #define PUMA_PMC_FUNC_DIS0_PCIE0 (1 << 20) #define PUMA_PMC_FUNC_DIS0_PCIE1 (1 << 21) #define PUMA_PMC_FUNC_DIS0_PCIE2 (1 << 22) #define PUMA_PMC_FUNC_DIS0_PCIE3 (1 << 23) #define PUMA_ATOM2PMC_DB_VALUE(busy,ipc_transaction_type,format,ipc_cmd_type,ipc_ext_payload,ipc_cmd,ipc_payload) \ ((busy << 31) | (ipc_transaction_type << 29) | (format << 28) | (ipc_cmd_type << 24) | (ipc_ext_payload << 23) | \ (ipc_cmd << 16) | (ipc_payload)) #define PUMA_SETPS_ACTIVE PUMA_ATOM2PMC_DB_VALUE(1, 3, 1, 1, 0, 7, 2) #define PUMA_SETPS_LPM PUMA_ATOM2PMC_DB_VALUE(1, 3, 1, 1, 0, 7, 1) #define PUMA_SETPS_NETIP_ON PUMA_ATOM2PMC_DB_VALUE(1, 3, 1, 1, 0, 7, 4) #define PUMA_SETPS_NETIP_OFF PUMA_ATOM2PMC_DB_VALUE(1, 3, 1, 1, 0, 7, 8) #define PUMA_CLEAR_RESET_INFO PUMA_ATOM2PMC_DB_VALUE(1, 3, 1, 3, 0, 7, 0) #define PUMA_ATOM_OVERRIDE_LTR PUMA_ATOM2PMC_DB_VALUE(1, 3, 1, 1, 0, 5, 7) #define PUMA_ATOM_USE_DEVICE_LTR PUMA_ATOM2PMC_DB_VALUE(1, 3, 1, 1, 0, 5, 0) #define PUMA_READ_PMC_IPC 0 #define PUMA_WRITE_PMC_IPC 1 #define PUMA_PMC_IPC_BUSY_MASK 0x80000000 #define PUMA_PMC_IPC_OPERATIONAL_MASK 0x80000000 #define PUMA_PMC_IPC_PAYLOAD_MASK 0xFFFF #define PUMA_PMC_IPC_GET_CMD(data) ((data >> 16) & 0x7F) #define PUMA_PMC_ABORT_MSG 0x1 #define PUMA_PMC_ABORT_CONDITION1 0x1 #define PUMA_PMC_ABORT_CONDITION2 0x2 #define PUMA_PMC_ABORT_CONDITION3 0x4 #define PUMA_MAX_PMC_IPC_RETRY 1000 #define PUMA_PRINT_STATE(data) \ (pr_err("Puma ACPI state %d Netip state %d PMC state %d Platform resource %d notify type %d\n", \ data->current_state, data->netip_state, data->pmc_state, data->acpi_resource_indication, data->notify_state_type)) enum puma_acpi_system_state { PUMA_ACPI_STATE_ACTIVE, PUMA_ACPI_STATE_BBU, PUMA_ACPI_STATE_STANDBY, PUMA_ACPI_STATE_DEEP_STANDBY }; enum puma_acpi_test_mode { PUMA_ACPI_ENTER_ACTIVE_AC, PUMA_ACPI_ENTER_ACTIVE_BBU, PUMA_ACPI_ENTER_STANDBY, PUMA_ACPI_ENTER_DEEP_STANDBY, PUMA_ACPI_EXIT_STANDBY, PUMA_ACPI_ERP_ACTIVE, PUMA_ACPI_ERP_INACTIVE }; enum puma_acpi_timer_state { PUMA_ACPI_TIMER_NOT_STARTED, PUMA_ACPI_TIMER_STARTED, PUMA_ACPI_TIMER_SB_THRS, PUMA_ACPI_TIMER_SB_LED, PUMA_ACPI_TIMER_DSB_THRS, PUMA_ACPI_TIMER_DSB_LED, PUMA_ACPI_TIMER_STOPPED, }; enum puma_acpi_led_state { LED_OFF, LED_ON, }; /* New memeber to be added after looking at puma_acpi_free_resources() function. * The index order is important as it is used to free the specific resources. */ enum puma_resource_index { PUMA_ACPI_DATA, PUMA_ACPI_NOTIFY_FIFO, PUMA_ACPI_SYSFS, PUMA_ACPI_TASK, }; enum puma_acpi_resource_indication { PUMA_ACPI_RESOURCE_ON, PUMA_ACPI_RESOURCE_OFF, }; enum puma_pcie_port_state { PUMA_PCIE_PORT_ENABLE, PUMA_PCIE_PORT_DISABLE, }; enum puma_acpi_netip_request_reset { PUMA_ACPI_NETIP_REQUEST_RESET_WARM = 1, PUMA_ACPI_NETIP_REQUEST_RESET_COLD = 2, }; enum puma_acpi_pmic_type { PUMA7_ACPI_PMIC, }; enum puma_lpm_interface { PUMA_LPM_USB, PUMA_LPM_SATA, }; enum puma_lpm_operation { PUMA_LPM_GET, PUMA_LPM_SET, }; struct puma_acpi_data { char name[20]; //name of the task unsigned char notify_state_type; int current_state; int netip_state; int timer_state; int pmc_state; int led_state; int led_blink_count; int acpi_resource_indication; unsigned int acpi_resource_state_vector; bool deepstandby2active; bool deepstandby2bbu; bool on_bat_during_standby; unsigned long button_press_time; unsigned long button_release_time; struct acpi_device *acpi_dev; struct task_struct *task; struct timer_list timer; DECLARE_KFIFO_PTR(notify_fifo, unsigned int); struct regulator *regulator; unsigned int pmic_supported:1; int pmic_type; int reset_cause; int reset_type; int reset_btn_dur; unsigned int bbu_device_mask; unsigned int sb_device_mask; unsigned int dsb_device_mask; unsigned int device_state; uint32_t pmc_dev_disabled_config0; uint32_t pmc_dev_disabled_config1; unsigned int enabled_devices_mask; unsigned int power_test_mode; unsigned int pcie_port_dev_info[4]; }; static unsigned long puma_events; static wait_queue_head_t puma_wait; static atomic_t puma_netip_state = ATOMIC_INIT(0); /* invoke the ACPI control method specified by the pathname */ static int puma_acpi_get_data(struct puma_acpi_data *data, acpi_string pathname, unsigned int *out_data) { int ret = 0; struct acpi_buffer buf; union acpi_object out_obj; acpi_status status; buf.pointer = &out_obj; buf.length = sizeof(out_obj); status = acpi_evaluate_object(data->acpi_dev->handle, pathname, NULL, &buf); if (ACPI_FAILURE(status) || out_obj.type != ACPI_TYPE_INTEGER) { pr_err("PUMA ACPI %s method execution failed\n", pathname); ret = -EIO; } else { *out_data = out_obj.integer.value; } return ret; } /* Read operation for the sysfs attributes - system_state, netip_state, pmc_state and platform_resource */ static ssize_t show_pcie_port_dev_info(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t show_system_state(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || NULL == buf || NULL == attr) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "%d\n", data->current_state); } static ssize_t show_netip_state(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || NULL == buf || NULL == attr) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "%d\n", data->netip_state); } static ssize_t show_pmc_state(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || NULL == buf || NULL == attr) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "%d\n", data->pmc_state); } static ssize_t show_platform_resource(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || NULL == buf || NULL == attr) { return -ENXIO; } if(puma_acpi_get_data(data, "PPRS", &data->acpi_resource_state_vector)) { pr_err("puma_acpi_get_data PPRS failed!\n"); } return scnprintf(buf, PAGE_SIZE, "%d :: 0x%x\n", ((data->acpi_resource_indication) ? 0:1), data->acpi_resource_state_vector); } static ssize_t show_device_power_state(struct device *dev, struct device_attribute *attr, char *buf) { unsigned int reg_value0, reg_value1; int ret_value; if (NULL == dev || NULL == buf || NULL == attr) return -EIO; if((ret_value = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_MEM_READ_OPCODE, PUMA_PMC_D3_STS_0, ®_value0)) < 0) { pr_err("iosf_mbi_read failed %d at line %d!\n",ret_value, __LINE__); } else if((ret_value = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_MEM_READ_OPCODE, PUMA_PMC_D3_STS_1, ®_value1)) < 0) { pr_err("iosf_mbi_read failed %d at line %d!\n",ret_value, __LINE__); } if(ret_value < 0) { return -EIO; } else { return scnprintf(buf, PAGE_SIZE, "D3_STS_0 0x%x D3_STS_1 0x%x\n", reg_value0, reg_value1); } } static ssize_t show_function_disable_state(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || NULL == buf || NULL == attr) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "FUNC_DIS_0 0x%08x FUNC_DIS_1 0x%08x\n", data->pmc_dev_disabled_config0, data->pmc_dev_disabled_config1); } static acpi_status update_lpm(acpi_handle handle, int xface, int operation, int *lpm_bbu) { union acpi_object param[3]; struct acpi_object_list input; struct acpi_buffer buf; union acpi_object out_obj; acpi_status status; param[0].type = ACPI_TYPE_INTEGER; param[0].integer.value = xface; param[1].type = ACPI_TYPE_INTEGER; param[1].integer.value = operation; param[2].type = ACPI_TYPE_INTEGER; param[2].integer.value = *lpm_bbu; input.count = 3; input.pointer = param; buf.pointer = &out_obj; buf.length = sizeof(out_obj); status = acpi_evaluate_object(handle, "LPMC", &input, &buf); if (ACPI_FAILURE(status)) { return status; } else if (operation == PUMA_LPM_GET){ *lpm_bbu = out_obj.integer.value; } return 0; } static ssize_t show_usb_lpm(struct device *dev, struct device_attribute *attr, char *buf) { int usb_lpm_bbu = -1; struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || buf == NULL || attr == NULL) { return -ENXIO; } //Get the data from ACPI if(update_lpm(data->acpi_dev->handle, PUMA_LPM_USB, PUMA_LPM_GET, &usb_lpm_bbu) != 0) { return -EIO; } return scnprintf(buf, PAGE_SIZE, "%d\n", usb_lpm_bbu); } static ssize_t store_usb_lpm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int usb_lpm_bbu; struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || buf == NULL || attr == NULL) { return -ENXIO; } if(sscanf(buf, "%d", &usb_lpm_bbu) != 1) { return -EINVAL; } else if(usb_lpm_bbu < 0 || usb_lpm_bbu > 1){ return -EINVAL; } //Update the ACPI data if(update_lpm(data->acpi_dev->handle, PUMA_LPM_USB, PUMA_LPM_SET, &usb_lpm_bbu) != 0) { return -EIO; } return count; } static ssize_t show_sata_lpm(struct device *dev, struct device_attribute *attr, char *buf) { int sata_lpm_bbu = -1; struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || buf == NULL || attr == NULL) { return -ENXIO; } //Get the data from ACPI if(update_lpm(data->acpi_dev->handle, PUMA_LPM_SATA, PUMA_LPM_GET, &sata_lpm_bbu) != 0) { return -EIO; } return scnprintf(buf, PAGE_SIZE, "%d\n", sata_lpm_bbu); } static ssize_t store_sata_lpm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int sata_lpm_bbu; struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || buf == NULL || attr == NULL) { return -ENXIO; } if(sscanf(buf, "%d", &sata_lpm_bbu) != 1) { return -EINVAL; } else if(sata_lpm_bbu < 0 || sata_lpm_bbu > 1){ return -EINVAL; } //Update the ACPI data if(update_lpm(data->acpi_dev->handle, PUMA_LPM_SATA, PUMA_LPM_SET, &sata_lpm_bbu) != 0) { return -EIO; } return count; } static int puma_get_soc_dts_te(unsigned int offset, unsigned int *data) { unsigned int reg_value; int ret_value; if (data == NULL) return -EINVAL; if((ret_value = iosf_mbi_read(PUMA_PUNIT_PORT, PUMA_PUNIT_READ_OPCODE, offset, ®_value)) < 0) { pr_err("iosf_mbi_read failed %d at line %d!\n",ret_value, __LINE__); } else { *data |= ((reg_value & PUMA_SOC_TE_CPU_THROT_MASK) ? PUMA_SOC_TE_CPU_THROT : 0); } return ret_value; } static int puma_set_soc_dts_te(unsigned int offset, unsigned int data) { unsigned int reg_value; int ret_value; if((ret_value = iosf_mbi_read(PUMA_PUNIT_PORT, PUMA_PUNIT_READ_OPCODE, offset, ®_value)) < 0) { pr_err("iosf_mbi_read failed %d at line %d!\n",ret_value, __LINE__); } if((ret_value = iosf_mbi_write(PUMA_PUNIT_PORT, PUMA_PUNIT_WRITE_OPCODE, offset, (reg_value = reg_value & ~(PUMA_SOC_TE_MASK)))) < 0) { pr_err("iosf_mbi_write failed %d at line %d!\n",ret_value, __LINE__); } else { reg_value |= ((data & PUMA_SOC_TE_CPU_THROT) ? PUMA_SOC_TE_CPU_THROT_MASK : 0); if((ret_value = iosf_mbi_write(PUMA_PUNIT_PORT, PUMA_PUNIT_WRITE_OPCODE, offset, reg_value)) < 0) { pr_err("iosf_mbi_write failed %d at line %d!\n",ret_value, __LINE__); } } return ret_value; } static ssize_t show_soc_dts_te0(struct device *dev, struct device_attribute *attr, char *buf) { int ret_value; unsigned int data = 0; if ( dev == NULL || attr == NULL || buf == NULL) return -EINVAL; ret_value = puma_get_soc_dts_te(PUMA_PUNIT_TE_AUX0, &data); if(ret_value < 0) { return -EIO; } else { return scnprintf(buf, PAGE_SIZE, "0x%x\n", data); } } static ssize_t show_soc_dts_te1(struct device *dev, struct device_attribute *attr, char *buf) { int ret_value; unsigned int data = 0; if ( dev == NULL || attr == NULL || buf == NULL) return -EINVAL; ret_value = puma_get_soc_dts_te(PUMA_PUNIT_TE_AUX1, &data); if(ret_value < 0) { return -EIO; } else { return scnprintf(buf, PAGE_SIZE, "0x%x\n", data); } } static ssize_t show_soc_dts_te2(struct device *dev, struct device_attribute *attr, char *buf) { int ret_value; unsigned int data = 0; ret_value = puma_get_soc_dts_te(PUMA_PUNIT_TE_AUX2, &data); if ( dev == NULL || attr == NULL || buf == NULL) return -EINVAL; if(ret_value < 0) { return -EIO; } else { return scnprintf(buf, PAGE_SIZE, "0x%x\n", data); } } static ssize_t show_soc_dts_te3(struct device *dev, struct device_attribute *attr, char *buf) { int ret_value; unsigned int data = 0; if ( dev == NULL || attr == NULL || buf == NULL) return -EINVAL; ret_value = puma_get_soc_dts_te(PUMA_PUNIT_TE_AUX3, &data); if(ret_value < 0) { return -EIO; } else { return scnprintf(buf, PAGE_SIZE, "0x%x\n", data); } } static ssize_t store_soc_dts_te0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int data; if ( dev == NULL || attr == NULL || buf == NULL) return -EINVAL; if(sscanf(buf, "%x", &data) != 1) { return -EINVAL; } if(puma_set_soc_dts_te(PUMA_PUNIT_TE_AUX0, data) < 0) { return -EIO; } return count; } static ssize_t store_soc_dts_te1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int data; if(sscanf(buf, "%x", &data) != 1) { return -EINVAL; } if(puma_set_soc_dts_te(PUMA_PUNIT_TE_AUX1, data) < 0) { return -EIO; } return count; } static ssize_t store_soc_dts_te2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int data; if(sscanf(buf, "%x", &data) != 1) { return -EINVAL; } if(puma_set_soc_dts_te(PUMA_PUNIT_TE_AUX2, data) < 0) { return -EIO; } return count; } static ssize_t store_soc_dts_te3(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int data; if ( dev == NULL || attr == NULL || buf == NULL) return -EINVAL; if(sscanf(buf, "%x", &data) != 1) { return -EINVAL; } if(puma_set_soc_dts_te(PUMA_PUNIT_TE_AUX3, data) < 0) { return -EIO; } return count; } static ssize_t show_reset_cause(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "%d\n", data->reset_cause); } static ssize_t show_reset_type(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "%d\n", data->reset_type); } static ssize_t show_reset_btn_dur(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "%d\n", data->reset_btn_dur); } static ssize_t show_bbu_device_mask(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "%08x\n", data->bbu_device_mask); } static ssize_t store_bbu_device_mask(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int bbu_device_mask; struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } if(sscanf(buf, "%x", &bbu_device_mask) != 1) { return -EINVAL; } else if(bbu_device_mask & ~PUMA_DEV_MASK){ return -EINVAL; } // disabled devices must be off data->bbu_device_mask = bbu_device_mask & data->enabled_devices_mask; return count; } static ssize_t show_sb_device_mask(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "%08x\n", data->sb_device_mask); } static ssize_t store_sb_device_mask(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int sb_device_mask; struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } if(sscanf(buf, "%x", &sb_device_mask) != 1) { return -EINVAL; } else if(sb_device_mask & ~PUMA_DEV_MASK){ return -EINVAL; } // disabled devices must be off data->sb_device_mask = sb_device_mask & data->enabled_devices_mask; return count; } static ssize_t show_dsb_device_mask(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "%08x\n", data->dsb_device_mask); } static ssize_t show_power_test_mode(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } return scnprintf(buf, PAGE_SIZE, "%d\n", data->power_test_mode); } static ssize_t store_dsb_device_mask(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int dsb_device_mask; struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } if(sscanf(buf, "%x", &dsb_device_mask) != 1) { return -EINVAL; } else if(dsb_device_mask & ~PUMA_DEV_MASK){ return -EINVAL; } // disabled devices must be off data->dsb_device_mask = dsb_device_mask & data->enabled_devices_mask; return count; } int puma_acpi_set_data(struct puma_acpi_data *data, acpi_string pathname, unsigned int in_data){ int ret_value; struct acpi_object_list input; union acpi_object in_params; in_params.type = ACPI_TYPE_INTEGER; in_params.integer.value = in_data; input.count = 1; input.pointer = &in_params; ret_value = acpi_evaluate_object(data->acpi_dev->handle, pathname, &input, NULL); if(ret_value != 0){ pr_err("acpi_evaluate_object failed.\n"); } return ret_value; } static ssize_t store_power_test_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int power_test_mode; struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL==acpi)?NULL:acpi_driver_data(acpi); if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } if(sscanf(buf, "%x", &power_test_mode) != 1) { return -EINVAL; } switch(power_test_mode){ case PUMA_ACPI_ENTER_ACTIVE_AC: puma_acpi_set_data(data, PUMA_ACPI_PTAS, PUMA_ACPI_NOTIFY_ACTIVE_ON_AC); break; case PUMA_ACPI_ENTER_ACTIVE_BBU: puma_acpi_set_data(data, PUMA_ACPI_PTAS, PUMA_ACPI_NOTIFY_ACTIVE_ON_BATTERY); break; case PUMA_ACPI_ENTER_STANDBY: puma_acpi_set_data(data, PUMA_ACPI_PTAS, PUMA_ACPI_NOTIFY_PWR_BUTTON_PRS); msleep(7000); puma_acpi_set_data(data, PUMA_ACPI_PTAS, PUMA_ACPI_NOTIFY_PWR_BUTTON_RLS); break; case PUMA_ACPI_ENTER_DEEP_STANDBY: puma_acpi_set_data(data, PUMA_ACPI_PTAS, PUMA_ACPI_NOTIFY_PWR_BUTTON_PRS); msleep(12000); puma_acpi_set_data(data, PUMA_ACPI_PTAS, PUMA_ACPI_NOTIFY_PWR_BUTTON_RLS); break; case PUMA_ACPI_EXIT_STANDBY: puma_acpi_set_data(data, PUMA_ACPI_PTAS, PUMA_ACPI_NOTIFY_PWR_BUTTON_PRS); msleep(500); puma_acpi_set_data(data, PUMA_ACPI_PTAS, PUMA_ACPI_NOTIFY_PWR_BUTTON_RLS); break; case PUMA_ACPI_ERP_ACTIVE: puma_acpi_set_data(data, PUMA_ACPI_PTAS, PUMA_ACPI_NOTIFY_ERP_ACTIVE); break; case PUMA_ACPI_ERP_INACTIVE: puma_acpi_set_data(data, PUMA_ACPI_PTAS, PUMA_ACPI_NOTIFY_ERP_INACTIVE); break; } data->power_test_mode = power_test_mode; return count; } static DEVICE_ATTR(system_state, S_IRUGO, show_system_state, NULL); static DEVICE_ATTR(netip_state, S_IRUGO, show_netip_state, NULL); static DEVICE_ATTR(pmc_state, S_IRUGO, show_pmc_state, NULL); static DEVICE_ATTR(platform_resource, S_IRUGO, show_platform_resource, NULL); static DEVICE_ATTR(device_power_state, S_IRUGO, show_device_power_state, NULL); static DEVICE_ATTR(function_disable_state, S_IRUGO, show_function_disable_state, NULL); static DEVICE_ATTR(usb_lpm, S_IRUGO | S_IWUSR, show_usb_lpm, store_usb_lpm); static DEVICE_ATTR(sata_lpm, S_IRUGO | S_IWUSR, show_sata_lpm, store_sata_lpm); static DEVICE_ATTR(soc_dts_te0, S_IRUGO | S_IWUSR, show_soc_dts_te0, store_soc_dts_te0); static DEVICE_ATTR(soc_dts_te1, S_IRUGO | S_IWUSR, show_soc_dts_te1, store_soc_dts_te1); static DEVICE_ATTR(soc_dts_te2, S_IRUGO | S_IWUSR, show_soc_dts_te2, store_soc_dts_te2); static DEVICE_ATTR(soc_dts_te3, S_IRUGO | S_IWUSR, show_soc_dts_te3, store_soc_dts_te3); static DEVICE_ATTR(reset_cause, S_IRUGO, show_reset_cause, NULL); static DEVICE_ATTR(reset_type, S_IRUGO, show_reset_type, NULL); static DEVICE_ATTR(reset_btn_dur, S_IRUGO, show_reset_btn_dur, NULL); static DEVICE_ATTR(bbu_device_mask, S_IRUGO | S_IWUSR, show_bbu_device_mask, store_bbu_device_mask); static DEVICE_ATTR(sb_device_mask, S_IRUGO | S_IWUSR, show_sb_device_mask, store_sb_device_mask); static DEVICE_ATTR(dsb_device_mask, S_IRUGO | S_IWUSR, show_dsb_device_mask, store_dsb_device_mask); static DEVICE_ATTR(power_test_mode, S_IRUGO | S_IWUSR, show_power_test_mode, store_power_test_mode); static DEVICE_ATTR(pcie_port0_dev_info, S_IRUGO, show_pcie_port_dev_info, NULL); static DEVICE_ATTR(pcie_port1_dev_info, S_IRUGO, show_pcie_port_dev_info, NULL); static DEVICE_ATTR(pcie_port2_dev_info, S_IRUGO, show_pcie_port_dev_info, NULL); static DEVICE_ATTR(pcie_port3_dev_info, S_IRUGO, show_pcie_port_dev_info, NULL); static struct attribute *puma_acpi_sysfs_entries[] = { &dev_attr_system_state.attr, &dev_attr_netip_state.attr, &dev_attr_pmc_state.attr, &dev_attr_platform_resource.attr, &dev_attr_device_power_state.attr, &dev_attr_function_disable_state.attr, &dev_attr_usb_lpm.attr, &dev_attr_sata_lpm.attr, &dev_attr_soc_dts_te0.attr, &dev_attr_soc_dts_te1.attr, &dev_attr_soc_dts_te2.attr, &dev_attr_soc_dts_te3.attr, &dev_attr_reset_cause.attr, &dev_attr_reset_type.attr, &dev_attr_reset_btn_dur.attr, &dev_attr_bbu_device_mask.attr, &dev_attr_sb_device_mask.attr, &dev_attr_dsb_device_mask.attr, &dev_attr_power_test_mode.attr, &dev_attr_pcie_port0_dev_info.attr, &dev_attr_pcie_port1_dev_info.attr, &dev_attr_pcie_port2_dev_info.attr, &dev_attr_pcie_port3_dev_info.attr, NULL, }; static struct attribute_group puma_acpi_attr_group = { .name = NULL, /* put in device directory */ .attrs = puma_acpi_sysfs_entries, }; static ssize_t show_pcie_port_dev_info(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi = to_acpi_device(dev); struct puma_acpi_data *data = (NULL == acpi) ? NULL : acpi_driver_data(acpi); unsigned int index = 0; if (NULL == data || attr == NULL || buf == NULL) { return -ENXIO; } if (attr == &dev_attr_pcie_port0_dev_info){ index = 0; } else if (attr == &dev_attr_pcie_port1_dev_info){ index = 1; } else if (attr == &dev_attr_pcie_port2_dev_info){ index = 2; } else if (attr == &dev_attr_pcie_port3_dev_info){ index = 3; } return scnprintf(buf, PAGE_SIZE, "0x%08x\n", data->pcie_port_dev_info[index]); } static inline int puma_check_condition(int *acpi_event, int *netip_event, int *timer_event) { return ((*acpi_event = test_and_clear_bit(PUMA_ACPI_EVENT, &puma_events)) || (*netip_event = test_and_clear_bit(PUMA_NETIP_EVENT, &puma_events)) || (*timer_event = test_and_clear_bit(PUMA_TIMER_EVENT, &puma_events))); } static void puma_acpi_notify(struct acpi_device *acpi_dev, unsigned int event) { struct puma_acpi_data *data = acpi_driver_data(acpi_dev); if(!(kfifo_put(&data->notify_fifo, event))) { pr_err("Puma ACPI notify fifo full!\n"); } else { set_bit(PUMA_ACPI_EVENT, &puma_events); wake_up_interruptible(&puma_wait); } } static void puma_timer_notify(unsigned long data) { set_bit(PUMA_TIMER_EVENT, &puma_events); wake_up_interruptible(&puma_wait); } /** * puma_netip_notify_state() - invoke to update the state of NETIP * @state: state of NETIP, 0 - Active, 1 - BBU, 2 - Standby, 3 - Deep standby */ int puma_netip_notify_state(int state) { int ret = 0; if((state < PUMA_ACPI_STATE_ACTIVE) || (state > PUMA_ACPI_STATE_DEEP_STANDBY)) { pr_err("puma_netip_notify_state %d invalid state\n", state); ret = -EINVAL; } else { atomic_set(&puma_netip_state, state); set_bit(PUMA_NETIP_EVENT, &puma_events); wake_up_interruptible(&puma_wait); } return ret; } EXPORT_SYMBOL(puma_netip_notify_state); void puma_acpi_free_resources(struct puma_acpi_data *data, int resource_index) { switch(resource_index) { case PUMA_ACPI_SYSFS: sysfs_remove_group(&data->acpi_dev->dev.kobj, &puma_acpi_attr_group); case PUMA_ACPI_NOTIFY_FIFO: kfifo_free(&data->notify_fifo); case PUMA_ACPI_DATA: kfree(data); break; default: pr_err("Puma ACPI invalid resource index %d\n", resource_index); } } static int puma_acpi_power_led(struct puma_acpi_data *data) { int ret = 0; struct acpi_object_list input; union acpi_object param; acpi_status status; param.type = ACPI_TYPE_INTEGER; param.integer.value = data->led_state; input.count = 1; input.pointer = ¶m; status = acpi_evaluate_object(data->acpi_dev->handle, "PLED", &input, NULL); if (!ACPI_SUCCESS(status)) { pr_err("Puma ACPI PLED method execution failed!\n"); ret = -EINVAL; } return ret; } int puma_acpi_make_pmc_ipc_channel_operational(void) { unsigned int reg_value; int ret_value; if(iosf_mbi_available()) { /* check if the IPC channel is operational */ if((ret_value = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_READ_OPCODE, PUMA_PMC2ATOM_CSR, ®_value)) < 0) { pr_err("iosf_mbi_read failed %d at line %d!\n",ret_value, __LINE__); } else if(!(reg_value & PUMA_PMC_IPC_OPERATIONAL_MASK)) { pr_err("Puma PMC IPC not ready\n"); ret_value = -EIO; } else if((ret_value = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_READ_OPCODE, PUMA_ATOM2PMC_CSR, ®_value)) < 0) { pr_err("iosf_mbi_read failed %d at line %d!\n",ret_value, __LINE__); } else if((ret_value = iosf_mbi_write(PUMA_PMC_PORT_ID, PUMA_PMC_WRITE_OPCODE, PUMA_ATOM2PMC_CSR, (reg_value | PUMA_PMC_IPC_OPERATIONAL_MASK))) < 0) { pr_err("iosf_mbi_write failed %d at line %d!\n",ret_value, __LINE__); } } else { pr_err("Puma ACPI iosf mbi not available\n"); ret_value = -ENODEV; } return ret_value; } int puma_acpi_pmc_ipc(int operation, unsigned int cmd, unsigned int *data) { unsigned int reg_value; int ret_value = 0; unsigned int condition = 1; unsigned int timeout_count = PUMA_MAX_PMC_IPC_RETRY; if(operation == PUMA_WRITE_PMC_IPC) { if((ret_value = iosf_mbi_write(PUMA_PMC_PORT_ID, PUMA_PMC_WRITE_OPCODE, PUMA_ATOM2PMC_DB, cmd)) < 0) { pr_err("iosf_mbi_write failed %d for cmd %u at line %d!\n", ret_value, cmd, __LINE__); } else { do { if((ret_value = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_READ_OPCODE, PUMA_ATOM2PMC_DBM, ®_value)) < 0) { pr_err("iosf_mbi_read failed %d at line %d!\n", ret_value, __LINE__); } else { condition = reg_value & PUMA_PMC_IPC_BUSY_MASK; } if(condition) { timeout_count--; if(timeout_count == 0) { pr_err("PMC IPC not responding PUMA_ATOM2PMC_DBM %d!\n", reg_value); ret_value = -EIO; } msleep(5); } } while(condition && timeout_count); } } else if(operation == PUMA_READ_PMC_IPC) { if((ret_value = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_READ_OPCODE, PUMA_PMC2ATOM_DB, data)) < 0) { pr_err("iosf_mbi_read failed %d at line %d!\n", ret_value, __LINE__); } else if((ret_value = iosf_mbi_write(PUMA_PMC_PORT_ID, PUMA_PMC_WRITE_OPCODE, PUMA_PMC2ATOM_DB, (*data & ~PUMA_PMC_IPC_BUSY_MASK))) < 0) { pr_err("iosf_mbi_write failed %d at line %d!\n", ret_value, __LINE__); } else if((ret_value = iosf_mbi_write(PUMA_PMC_PORT_ID, PUMA_PMC_WRITE_OPCODE, PUMA_PMC2ATOM_DBM, (*data & ~PUMA_PMC_IPC_BUSY_MASK))) < 0) { pr_err("iosf_mbi_write failed %d at line %d!\n", ret_value, __LINE__); } } else { pr_err("Invalid operation\n"); } return ret_value; } /* puma_acpi_button_release() - Handle the button release event based on the current state * and the state of the timer */ void puma_acpi_button_release(struct puma_acpi_data *data, unsigned int event) { unsigned long button_press_duration; data->button_release_time = jiffies; button_press_duration = data->button_release_time - data->button_press_time; pr_debug("Puma ACPI button press duration %u ms\n", jiffies_to_msecs(button_press_duration)); if((data->timer_state != PUMA_ACPI_TIMER_STOPPED) && (data->timer_state != PUMA_ACPI_TIMER_NOT_STARTED)) { del_timer_sync(&data->timer); } switch(data->current_state) { case PUMA_ACPI_STATE_ACTIVE: if((data->timer_state >= PUMA_ACPI_TIMER_SB_THRS) && (data->timer_state <= PUMA_ACPI_TIMER_SB_LED)) { data->current_state = PUMA_ACPI_STATE_STANDBY; data->notify_state_type = PUMA_ACPI_NOTIFY_FAST_STANBY; data->on_bat_during_standby = false; } else if(data->timer_state >= PUMA_ACPI_TIMER_DSB_THRS) { data->current_state = PUMA_ACPI_STATE_DEEP_STANDBY; data->notify_state_type = PUMA_ACPI_NOTIFY_DEEP_STANDBY; data->on_bat_during_standby = false; } break; case PUMA_ACPI_STATE_BBU: if((data->timer_state >= PUMA_ACPI_TIMER_SB_THRS) && (data->timer_state <= PUMA_ACPI_TIMER_SB_LED)) { data->current_state = PUMA_ACPI_STATE_STANDBY; data->notify_state_type = PUMA_ACPI_NOTIFY_FAST_STANBY; data->on_bat_during_standby = true; } else if(data->timer_state >= PUMA_ACPI_TIMER_DSB_THRS) { data->current_state = PUMA_ACPI_STATE_DEEP_STANDBY; data->notify_state_type = PUMA_ACPI_NOTIFY_DEEP_STANDBY; data->on_bat_during_standby = true; } break; case PUMA_ACPI_STATE_STANDBY: if(data->timer_state >= PUMA_ACPI_TIMER_DSB_THRS) { data->current_state = PUMA_ACPI_STATE_DEEP_STANDBY; data->notify_state_type = PUMA_ACPI_NOTIFY_DEEP_STANDBY; } else { if(data->on_bat_during_standby) { data->current_state = PUMA_ACPI_STATE_BBU; data->notify_state_type = PUMA_ACPI_NOTIFY_ACTIVE_ON_BATTERY; } else { data->current_state = PUMA_ACPI_STATE_ACTIVE; data->notify_state_type = PUMA_ACPI_NOTIFY_ACTIVE_ON_AC; } } break; case PUMA_ACPI_STATE_DEEP_STANDBY: if(data->on_bat_during_standby) { data->current_state = PUMA_ACPI_STATE_BBU; data->notify_state_type = PUMA_ACPI_NOTIFY_ACTIVE_ON_BATTERY; data->deepstandby2bbu = true; } else { data->current_state = PUMA_ACPI_STATE_ACTIVE; data->notify_state_type = PUMA_ACPI_NOTIFY_ACTIVE_ON_AC; data->deepstandby2active = true; } break; default: pr_err("Puma ACPI ERROR invalid state %d for event %d\n", data->current_state, event); } data->timer_state = PUMA_ACPI_TIMER_NOT_STARTED; } /* puma_acpi_pmc2atom_ipc() - PMC has hit the abort condition, print the indicated error message */ void puma_acpi_pmc2atom_ipc(struct puma_acpi_data *data, unsigned int event) { unsigned int out_value; puma_acpi_pmc_ipc(PUMA_READ_PMC_IPC, 0, &out_value); if(PUMA_PMC_IPC_GET_CMD(out_value) == PUMA_PMC_ABORT_MSG) { switch(out_value & PUMA_PMC_IPC_PAYLOAD_MASK) { case PUMA_PMC_ABORT_CONDITION1: pr_err("Puma ACPI ERROR: PMC exited because ILB interrupt is present and CPU is going to exit C7\n"); break; case PUMA_PMC_ABORT_CONDITION2: pr_err("Puma ACPI ERROR: PMC exited because current Cstate < 6\n"); break; case PUMA_PMC_ABORT_CONDITION3: pr_err("Puma ACPI ERROR: PMC exited because PLLs are still locked\n"); break; default: pr_err("Puma ACPI ERROR invalid event %d msg\n", event); } } else { pr_err("Puma ACPI ERROR invalid event %d\n", event); } } /* puma_acpi_event_handler() - Handle the ACPI events and change the state of the system if needed */ void puma_acpi_event_handler(struct puma_acpi_data *data) { unsigned int event; unsigned int out_value; u32 eax, edx, i; while(kfifo_get(&data->notify_fifo, &event)) { pr_debug("Puma ACPI state %d notify type %d before event %d handling\n", data->current_state, data->notify_state_type, event); switch(event) { case PUMA_ACPI_NOTIFY_ACTIVE_ON_AC: if(data->current_state == PUMA_ACPI_STATE_BBU) { data->current_state = PUMA_ACPI_STATE_ACTIVE; data->notify_state_type = PUMA_ACPI_NOTIFY_ACTIVE_ON_AC; } else if ((data->current_state == PUMA_ACPI_STATE_STANDBY) || (data->current_state == PUMA_ACPI_STATE_DEEP_STANDBY)) { data->on_bat_during_standby = false; acpi_bus_generate_netlink_event(data->acpi_dev->pnp.device_class, dev_name(&data->acpi_dev->dev), PUMA_ACPI_NOTIFY_PWR_STAT_CNG_DURING_SB, false); } break; case PUMA_ACPI_NOTIFY_ACTIVE_ON_BATTERY: if(data->current_state == PUMA_ACPI_STATE_ACTIVE) { data->current_state = PUMA_ACPI_STATE_BBU; data->notify_state_type = PUMA_ACPI_NOTIFY_ACTIVE_ON_BATTERY; } else if ((data->current_state == PUMA_ACPI_STATE_STANDBY) || (data->current_state == PUMA_ACPI_STATE_DEEP_STANDBY)) { data->on_bat_during_standby = true; acpi_bus_generate_netlink_event(data->acpi_dev->pnp.device_class, dev_name(&data->acpi_dev->dev), PUMA_ACPI_NOTIFY_PWR_STAT_CNG_DURING_SB, true); } break; case PUMA_ACPI_NOTIFY_AC_SPIKE: // check the current status of power if(data->current_state != PUMA_ACPI_STATE_ACTIVE) { pr_err("Puma ACPI invalid state %d for event %d\n", data->current_state, event); } break; case PUMA_ACPI_NOTIFY_BATTERY_SPIKE: // check the current status of power if(data->current_state != PUMA_ACPI_STATE_BBU) { pr_err("Puma ACPI invalid state %d for event %d\n", data->current_state, event); } break; case PUMA_ACPI_NOTIFY_PWR_BUTTON_PRS: data->button_press_time = jiffies; if((data->current_state != PUMA_ACPI_STATE_DEEP_STANDBY) && (data->timer_state == PUMA_ACPI_TIMER_NOT_STARTED)) { if(data->current_state == PUMA_ACPI_STATE_STANDBY) { data->timer_state = PUMA_ACPI_TIMER_SB_THRS; data->led_blink_count = 0; } else { data->timer_state = PUMA_ACPI_TIMER_STARTED; } data->timer.expires = data->button_press_time + msecs_to_jiffies(PUMA_ACPI_TIMER_THRS_SB); data->timer.data = 0; data->timer.function = puma_timer_notify; add_timer(&data->timer); } break; case PUMA_ACPI_NOTIFY_PWR_BUTTON_RLS: puma_acpi_button_release(data, event); break; case PUMA_ACPI_NOTIFY_NETIP_REQUEST_RESET: puma_acpi_get_data(data, "GRST", &out_value); if(out_value == PUMA_ACPI_NETIP_REQUEST_RESET_WARM) { reboot_type = BOOT_CF9_FORCE; reboot_mode = REBOOT_WARM; } else if (out_value == PUMA_ACPI_NETIP_REQUEST_RESET_COLD) { reboot_type = BOOT_CF9_FORCE; reboot_mode = REBOOT_COLD; } acpi_bus_generate_netlink_event(data->acpi_dev->pnp.device_class, dev_name(&data->acpi_dev->dev), event, out_value); break; case PUMA_ACPI_NOTIFY_PLATFORM_RESOURCES_OFF: data->acpi_resource_indication = PUMA_ACPI_RESOURCE_OFF; break; case PUMA_ACPI_NOTIFY_PLATFORM_RESOURCES_ON: data->acpi_resource_indication = PUMA_ACPI_RESOURCE_ON; break; case PUMA_ACPI_NOTIFY_BATTERY_LOW: acpi_bus_generate_netlink_event(data->acpi_dev->pnp.device_class, dev_name(&data->acpi_dev->dev), event, 0); break; case PUMA_ACPI_NOTIFY_PMC2ATOM_IPC: puma_acpi_pmc2atom_ipc(data, event); break; case PUMA_ACPI_NOTIFY_NETIP_CONNECTED_IDLE: puma_acpi_get_data(data, "GRCE", &out_value); acpi_bus_generate_netlink_event(data->acpi_dev->pnp.device_class, dev_name(&data->acpi_dev->dev), event, out_value); break; case PUMA_ACPI_NOTIFY_CPU_DTS_TRIP: case PUMA_ACPI_NOTIFY_SOC_DTS_TRIP: out_value = 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) { out_value |= ((i == 0) ? PUMA_CORE0_TT : PUMA_CORE1_TT); } else { out_value |= ((i == 0) ? PUMA_CORE0_NOT_TT : PUMA_CORE1_NOT_TT); } if((eax & TEMP_THRESHOLD1_INT_STS) && (eax & TEMP_THRESHOLD1_INT_LOG)) { out_value |= ((i == 0) ? PUMA_CORE0_ABOVE_TH1 : PUMA_CORE1_ABOVE_TH1); } else if (((eax & TEMP_THRESHOLD1_INT_STS) == 0) && (eax & TEMP_THRESHOLD1_INT_LOG)) { out_value |= ((i == 0) ? PUMA_CORE0_BELOW_TH1 : PUMA_CORE1_BELOW_TH1); } if ((eax & TEMP_THRESHOLD2_INT_STS) && (eax & TEMP_THRESHOLD2_INT_LOG)) { out_value |= ((i == 0) ? PUMA_CORE0_ABOVE_TH2 : PUMA_CORE1_ABOVE_TH2); } else if (((eax & TEMP_THRESHOLD2_INT_STS) == 0) && (eax & TEMP_THRESHOLD2_INT_LOG)) { out_value |= ((i == 0) ? PUMA_CORE0_BELOW_TH2 : PUMA_CORE1_BELOW_TH2); } } if((wrmsr_safe_on_cpu(i, PUMA_MSR_THERM_STATUS, 0, 0)) < 0) { pr_err("Puma ACPI ERROR: wrmsr_safe_on_cpu %d failed\n", i); } } if((iosf_mbi_read(PUMA_PUNIT_PORT, PUMA_PUNIT_READ_OPCODE, PUMA_PUNIT_PTTS, &eax)) < 0) { pr_err("Puma ACPI ERROR: iosf_mbi_read PTTS failed\n"); } else if((iosf_mbi_read(PUMA_PUNIT_PORT, PUMA_PUNIT_READ_OPCODE, PUMA_PUNIT_PTTSS, &edx)) < 0) { pr_err("Puma ACPI ERROR: iosf_mbi_read PTTSS failed\n"); } else if((iosf_mbi_write(PUMA_PUNIT_PORT, PUMA_PUNIT_WRITE_OPCODE, PUMA_PUNIT_PTTSS, edx)) < 0) { pr_err("Puma ACPI ERROR: iosf_mbi_write PTTSS failed\n"); } else { out_value |= ((eax & 0xF) << 16); acpi_bus_generate_netlink_event(data->acpi_dev->pnp.device_class, dev_name(&data->acpi_dev->dev), event, out_value); } break; case PUMA_ACPI_NOTIFY_PCIE_PORT0_ON: data->device_state |= PUMA_DEV_PCIE_PORT0; break; case PUMA_ACPI_NOTIFY_PCIE_PORT0_OFF: data->device_state &= ~PUMA_DEV_PCIE_PORT0; break; case PUMA_ACPI_NOTIFY_PCIE_PORT1_ON: data->device_state |= PUMA_DEV_PCIE_PORT1; break; case PUMA_ACPI_NOTIFY_PCIE_PORT1_OFF: data->device_state &= ~PUMA_DEV_PCIE_PORT1; break; case PUMA_ACPI_NOTIFY_PCIE_PORT2_ON: data->device_state |= PUMA_DEV_PCIE_PORT2; break; case PUMA_ACPI_NOTIFY_PCIE_PORT2_OFF: data->device_state &= ~PUMA_DEV_PCIE_PORT2; break; case PUMA_ACPI_NOTIFY_PCIE_PORT3_ON: data->device_state |= PUMA_DEV_PCIE_PORT3; break; case PUMA_ACPI_NOTIFY_PCIE_PORT3_OFF: data->device_state &= ~PUMA_DEV_PCIE_PORT3; break; case PUMA_ACPI_NOTIFY_USB_ON: data->device_state |= PUMA_DEV_USB; break; case PUMA_ACPI_NOTIFY_USB_OFF: data->device_state &= ~PUMA_DEV_USB; break; case PUMA_ACPI_NOTIFY_SD_ON: data->device_state |= PUMA_DEV_SD; break; case PUMA_ACPI_NOTIFY_SD_OFF: data->device_state &= ~PUMA_DEV_SD; break; case PUMA_ACPI_NOTIFY_SATA_ON: data->device_state |= PUMA_DEV_SATA; break; case PUMA_ACPI_NOTIFY_SATA_OFF: data->device_state &= ~PUMA_DEV_SATA; break; default: pr_err("Puma ACPI invalid event %d\n", event); } pr_debug("Puma ACPI state %d (0x%x) notify type %d after event %d handling\n", data->current_state, data->device_state, data->notify_state_type, event); } } inline void puma_netip_event_handler(struct puma_acpi_data *data) { data->netip_state = atomic_read(&puma_netip_state); } /* puma_timer_event_handler() - Manage the timer state based on the time lapsed */ void puma_timer_event_handler(struct puma_acpi_data *data) { pr_debug("Puma ACPI timer state %d before timer event handling, time lapsed from button press %u ms\n", data->timer_state, jiffies_to_msecs(jiffies - data->button_press_time)); switch(data->timer_state) { case PUMA_ACPI_TIMER_NOT_STARTED: break; case PUMA_ACPI_TIMER_STARTED: data->timer.expires = jiffies + msecs_to_jiffies(PUMA_ACPI_TIMER_LED_SB); data->timer_state = PUMA_ACPI_TIMER_SB_THRS; data->led_state = LED_OFF; data->led_blink_count = 0; puma_acpi_power_led(data); mod_timer(&data->timer, data->timer.expires); break; case PUMA_ACPI_TIMER_SB_THRS: data->timer.expires = jiffies + msecs_to_jiffies(PUMA_ACPI_TIMER_LED_SB); data->timer_state = PUMA_ACPI_TIMER_SB_LED; data->led_state ^= 1; data->led_blink_count++; puma_acpi_power_led(data); mod_timer(&data->timer, data->timer.expires); break; case PUMA_ACPI_TIMER_SB_LED: if (data->led_blink_count < 4) { data->led_blink_count++; } else { data->timer_state = PUMA_ACPI_TIMER_DSB_THRS; data->led_blink_count = 0; } data->timer.expires = jiffies + msecs_to_jiffies(PUMA_ACPI_TIMER_LED_SB); data->led_state ^= 1; puma_acpi_power_led(data); mod_timer(&data->timer, data->timer.expires); break; case PUMA_ACPI_TIMER_DSB_THRS: data->timer.expires = jiffies + msecs_to_jiffies(PUMA_ACPI_TIMER_LED_DSB); data->timer_state = PUMA_ACPI_TIMER_DSB_LED; data->led_state ^= 1; data->led_blink_count++; puma_acpi_power_led(data); mod_timer(&data->timer, data->timer.expires); break; case PUMA_ACPI_TIMER_DSB_LED: if (data->led_blink_count <= 10) { data->led_blink_count++; data->timer.expires = jiffies + msecs_to_jiffies(PUMA_ACPI_TIMER_LED_DSB); data->led_state ^= 1; puma_acpi_power_led(data); mod_timer(&data->timer, data->timer.expires); } else { data->timer_state = PUMA_ACPI_TIMER_STOPPED; } break; case PUMA_ACPI_TIMER_STOPPED: data->led_blink_count = 0; break; default: pr_err("Puma ACPI invalid timer state %d\n", data->timer_state); } pr_debug("Puma ACPI timer state %d LED state %d after timer event handling\n", data->timer_state, data->led_state); } /* puma_acpi_configure_pmic() - Configure the PMIC LPM mode based on the system state */ int puma_acpi_configure_pmic(struct puma_acpi_data *data, int state) { int ret = 0; if(data->pmic_supported) { if(data->pmic_type == PUMA7_ACPI_PMIC) { if((state < PUMA_ACPI_STATE_ACTIVE) || (state > PUMA_ACPI_STATE_DEEP_STANDBY)) { pr_err("puma_acpi_configure_pmic %d invalid state\n", state); ret = -EINVAL; } else if((ret = regulator_set_mode(data->regulator, (1 << state))) != 0) { pr_err("puma regulator_set_mode failed %d in state %d\n", ret, state); } } } return ret; } static inline int puma_device_state_changed(struct puma_acpi_data *data) { int ret = 0; switch(data->current_state) { case PUMA_ACPI_STATE_BBU: if(data->device_state == data->bbu_device_mask) { ret = 1; } break; case PUMA_ACPI_STATE_STANDBY: if(data->device_state == data->sb_device_mask) { ret = 1; } break; case PUMA_ACPI_STATE_DEEP_STANDBY: if(data->device_state == data->dsb_device_mask) { ret = 1; } break; default: pr_err("puma_device_state_changed invalid state %d\n", data->current_state); } pr_debug("puma_device_state_changed current_state=%d device_state=0x%x ret=%d\n", data->current_state, data->device_state, ret); return ret; } static inline void puma_acpi_update(struct puma_acpi_data *data, int led_state) { if(data->notify_state_type) { acpi_bus_generate_netlink_event(data->acpi_dev->pnp.device_class, dev_name(&data->acpi_dev->dev), data->notify_state_type, 0); if((puma_acpi_configure_pmic(data, data->current_state)) != 0) { pr_err("puma_acpi_configure_pmic failed\n"); PUMA_PRINT_STATE(data); } data->notify_state_type = 0; data->led_state = led_state; puma_acpi_power_led(data); } } /* puma_acpi_update_system() - Change the PMC state based on system, netip and resource state. * Also indicate the ACPID about the current state */ void puma_acpi_update_system(struct puma_acpi_data *data) { unsigned int ipc_data; int ret = 0; pr_debug("Puma ACPI state %d Netip state %d PMC state %d Platform resource %d notify type %d before system update\n", data->current_state, data->netip_state, data->pmc_state, data->acpi_resource_indication, data->notify_state_type); switch(data->current_state) { case PUMA_ACPI_STATE_ACTIVE: if(data->notify_state_type) { if((puma_acpi_configure_pmic(data, data->current_state)) != 0) { pr_err("puma_acpi_configure_pmic failed\n"); PUMA_PRINT_STATE(data); } else if ((puma_acpi_pmc_ipc(PUMA_WRITE_PMC_IPC, PUMA_ATOM_USE_DEVICE_LTR, &ipc_data)) != 0) { pr_err("puma_acpi_pmc_ipc PUMA_ATOM_USE_DEVICE_LTR failed\n"); PUMA_PRINT_STATE(data); } else { if(data->deepstandby2active) { ret = puma_acpi_pmc_ipc(PUMA_WRITE_PMC_IPC, PUMA_SETPS_NETIP_ON, &ipc_data); } else { ret = puma_acpi_pmc_ipc(PUMA_WRITE_PMC_IPC, PUMA_SETPS_ACTIVE, &ipc_data); } if(ret == 0) { data->deepstandby2active = false; acpi_bus_generate_netlink_event(data->acpi_dev->pnp.device_class, dev_name(&data->acpi_dev->dev), data->notify_state_type, 0); data->notify_state_type = 0; data->pmc_state = PUMA_ACPI_STATE_ACTIVE; data->led_state = LED_ON; puma_acpi_power_led(data); } else { pr_err("puma_acpi_pmc_ipc SETPS ACTIVE failed\n"); PUMA_PRINT_STATE(data); } } } break; case PUMA_ACPI_STATE_BBU: case PUMA_ACPI_STATE_STANDBY: puma_acpi_update(data, LED_ON); if((data->netip_state == data->current_state) && (puma_device_state_changed(data)) && (data->pmc_state != data->current_state)) { if(data->deepstandby2bbu) { /* First move the PMC to NETIP ON state */ if ((ret = puma_acpi_pmc_ipc(PUMA_WRITE_PMC_IPC, PUMA_ATOM_USE_DEVICE_LTR, &ipc_data)) != 0) { pr_err("puma_acpi_pmc_ipc PUMA_ATOM_USE_DEVICE_LTR failed\n"); PUMA_PRINT_STATE(data); } else if((ret = puma_acpi_pmc_ipc(PUMA_WRITE_PMC_IPC, PUMA_SETPS_NETIP_ON, &ipc_data)) != 0) { pr_err("puma_acpi_pmc_ipc PUMA_ATOM_USE_DEVICE_LTR failed\n"); PUMA_PRINT_STATE(data); } } if(ret == 0) { if((puma_acpi_pmc_ipc(PUMA_WRITE_PMC_IPC, PUMA_ATOM_OVERRIDE_LTR, &ipc_data)) != 0) { pr_err("puma_acpi_pmc_ipc PUMA_ATOM_OVERRIDE_LTR failed\n"); PUMA_PRINT_STATE(data); } else if ((puma_acpi_pmc_ipc(PUMA_WRITE_PMC_IPC, PUMA_SETPS_LPM, &ipc_data)) != 0) { pr_err("puma_acpi_pmc_ipc PUMA_SETPS_LPM failed\n"); PUMA_PRINT_STATE(data); } else { data->pmc_state = data->current_state; data->deepstandby2bbu = false; } } } break; case PUMA_ACPI_STATE_DEEP_STANDBY: puma_acpi_update(data, LED_OFF); if((data->netip_state == PUMA_ACPI_STATE_DEEP_STANDBY) && (puma_device_state_changed(data)) && (data->pmc_state != data->current_state)) { if((puma_acpi_pmc_ipc(PUMA_WRITE_PMC_IPC, PUMA_ATOM_OVERRIDE_LTR, &ipc_data)) != 0) { pr_err("puma_acpi_pmc_ipc PUMA_ATOM_OVERRIDE_LTR failed\n"); PUMA_PRINT_STATE(data); } else if((puma_acpi_pmc_ipc(PUMA_WRITE_PMC_IPC, PUMA_SETPS_NETIP_OFF, &ipc_data)) != 0) { pr_err("puma_acpi_pmc_ipc PUMA_SETPS_NETIP_OFF failed\n"); PUMA_PRINT_STATE(data); } else { data->pmc_state = data->current_state; } } break; default: pr_err("Puma ACPI Invalid state %d\n", data->current_state); } pr_debug("Puma ACPI state %d Netip state %d PMC state %d Platform resource %d notify type %d after system update\n", data->current_state, data->netip_state, data->pmc_state, data->acpi_resource_indication, data->notify_state_type); } static int puma_acpi_task(void *data) { struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; struct puma_acpi_data *acpi_data = data; int acpi_event = 0; int netip_event = 0; int timer_event = 0; int ret; sched_setscheduler(current, SCHED_RR, ¶m); allow_signal(SIGKILL); // update the current state if(acpi_data->current_state == PUMA_ACPI_STATE_BBU) { acpi_bus_generate_netlink_event(acpi_data->acpi_dev->pnp.device_class, dev_name(&acpi_data->acpi_dev->dev), PUMA_ACPI_NOTIFY_ACTIVE_ON_BATTERY, 0); } else { acpi_bus_generate_netlink_event(acpi_data->acpi_dev->pnp.device_class, dev_name(&acpi_data->acpi_dev->dev), PUMA_ACPI_NOTIFY_ACTIVE_ON_AC, 0); } pr_debug("Puma ACPI thread started (%d)\n", current->pid); for(;;) { ret = wait_event_interruptible(puma_wait, puma_check_condition(&acpi_event, &netip_event, &timer_event)); if (ret < 0) { pr_err("Wait returned %d, exiting Puma ACPI thread!\n", ret); break; } if(timer_event) { puma_timer_event_handler(acpi_data); } if(acpi_event) { puma_acpi_event_handler(acpi_data); } if(netip_event) { puma_netip_event_handler(acpi_data); } puma_acpi_update_system(acpi_data); } pr_debug("Puma ACPI thread exiting (%d)\n", current->pid); module_put_and_exit(0); } static int puma_acpi_dev_disabled_status(struct puma_acpi_data *data) { uint32_t func_dis, disabled_ip; int ret_value; ret_value = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_MEM_READ_OPCODE, PUMA_PMC_FUNC_DIS_0, &func_dis); if(ret_value < 0) { pr_err("iosf_mbi_read error %d while reading 0x%x at line %d!\n", ret_value, PUMA_PMC_FUNC_DIS_0, __LINE__); return ret_value; } ret_value = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_MEM_READ_OPCODE, PUMA_PMC_DISABLE_IP_0, &disabled_ip); if(ret_value < 0) { pr_err("iosf_mbi_read error %d while reading 0x%x at line %d!\n", ret_value, PUMA_PMC_DISABLE_IP_0, __LINE__); return ret_value; } data->pmc_dev_disabled_config0 = func_dis | disabled_ip; ret_value = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_MEM_READ_OPCODE, PUMA_PMC_FUNC_DIS_1, &func_dis); if(ret_value < 0) { pr_err("iosf_mbi_read error %d while reading 0x%x at line %d!\n", ret_value, PUMA_PMC_FUNC_DIS_1, __LINE__); return ret_value; } ret_value = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_MEM_READ_OPCODE, PUMA_PMC_DISABLE_IP_1, &disabled_ip); if(ret_value < 0) { pr_err("iosf_mbi_read error %d while reading 0x%x at line %d!\n", ret_value, PUMA_PMC_DISABLE_IP_1, __LINE__); return ret_value; } data->pmc_dev_disabled_config1 = func_dis | disabled_ip; data->enabled_devices_mask = PUMA_DEV_MASK; data->enabled_devices_mask &= (data->pmc_dev_disabled_config0 & PUMA_PMC_FUNC_DIS0_PCIE0)?~PUMA_DEV_PCIE_PORT0:PUMA_DEV_MASK; data->enabled_devices_mask &= (data->pmc_dev_disabled_config0 & PUMA_PMC_FUNC_DIS0_PCIE1)?~PUMA_DEV_PCIE_PORT1:PUMA_DEV_MASK; data->enabled_devices_mask &= (data->pmc_dev_disabled_config0 & PUMA_PMC_FUNC_DIS0_PCIE2)?~PUMA_DEV_PCIE_PORT2:PUMA_DEV_MASK; data->enabled_devices_mask &= (data->pmc_dev_disabled_config0 & PUMA_PMC_FUNC_DIS0_PCIE3)?~PUMA_DEV_PCIE_PORT3:PUMA_DEV_MASK; data->enabled_devices_mask &= (data->pmc_dev_disabled_config0 & PUMA_PMC_FUNC_DIS0_XHCI)?~PUMA_DEV_USB:PUMA_DEV_MASK; data->enabled_devices_mask &= (data->pmc_dev_disabled_config0 & PUMA_PMC_FUNC_DIS0_SD)?~PUMA_DEV_SD:PUMA_DEV_MASK; data->enabled_devices_mask &= (data->pmc_dev_disabled_config0 & PUMA_PMC_FUNC_DIS0_SATA)?~PUMA_DEV_SATA:PUMA_DEV_MASK; return ret_value; } static int get_pcie_port_dev_info(unsigned int *pcie_port_dev_info){ int ret = 0; int i; int device_count = 0; unsigned int l; struct pci_bus *port_bus = NULL; struct pci_bus *device_bus = NULL; //All PCIe port are under bus 0 port_bus = pci_find_bus(0, 0); if(port_bus == NULL){ pr_err("get_pcie_port_dev_info: cannot find bus 0!\n"); return -ENODEV; } //There are 4 PCIe ports. First, we need to find out which port is enumerated. //Only the port with device connected will be enumerated. for(i = 0; i < 4; i++){ //Set the default mapping to 0xffffffff pcie_port_dev_info[i] = 0xffffffff; //PCIe port's device is "1c", function will be 0~3. If we can read verdor //id, it means the port is enumerated. if(pci_bus_read_dev_vendor_id(port_bus, PCI_DEVFN(0x1c, i), &l, 60 * 1000)){ //Increase the device count. device_count++; //The first PCIe device's Bus:Device:Function will be 01:00:00 //The second PCIe device's Bus:Device:Function will be 02:00:00 device_bus = pci_find_bus(0, device_count); if(device_bus == NULL){ pr_err("get_pcie_port_dev_info: cannot find bus %d!\n", device_count); continue; } if(pci_bus_read_dev_vendor_id(device_bus, PCI_DEVFN(0, 0), &l, 60 * 1000)){ pcie_port_dev_info[i] = l; } } } return ret; } static int puma_acpi_add(struct acpi_device *acpi_dev) { struct puma_acpi_data *data; struct task_struct *task; int resource_index; //points to the last resource successfully allocated int ret; unsigned int power_status, reset_info1, reset_info2; char *str; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { return -ENOMEM; } else { resource_index = PUMA_ACPI_DATA; } data->acpi_dev = acpi_dev; ret = kfifo_alloc(&data->notify_fifo, PUMA_ACPI_FIFO_SIZE, GFP_KERNEL); if (ret) { pr_err("error kfifo_alloc %d\n", ret); goto puma_acpi_add_error; } else { resource_index = PUMA_ACPI_NOTIFY_FIFO; } str = acpi_device_name(acpi_dev); strcpy(str, "PUMA_ACPI_DEVICE"); str = acpi_device_class(acpi_dev); strcpy(str, "Platform"); ret = puma_acpi_get_data(data, "GCSR", &power_status); if(ret) { pr_err("puma_acpi_get_data failed!\n"); goto puma_acpi_add_error; } if(power_status & PUMA_ACPI_POWER_LIVE_MASK) { data->current_state = PUMA_ACPI_STATE_BBU; data->netip_state = PUMA_ACPI_STATE_BBU; } else { data->current_state = PUMA_ACPI_STATE_ACTIVE; data->netip_state = PUMA_ACPI_STATE_ACTIVE; } if(puma_acpi_get_data(data, "PPRS", &data->acpi_resource_state_vector)) { pr_err("puma_acpi_get_data PPRS failed!\n"); } else if(data->acpi_resource_state_vector != 0) { data->acpi_resource_indication = PUMA_ACPI_RESOURCE_ON; } else if(data->acpi_resource_state_vector == 0) { data->acpi_resource_indication = PUMA_ACPI_RESOURCE_OFF; } /* create debug attributes */ if((ret = sysfs_create_group(&acpi_dev->dev.kobj, &puma_acpi_attr_group))) { pr_err("puma sysfs_create_group failed!\n"); goto puma_acpi_add_error; } else { resource_index = PUMA_ACPI_SYSFS; } if((ret = puma_acpi_make_pmc_ipc_channel_operational()) < 0) { pr_err("puma_acpi_make_pmc_ipc_channel_operational failed!\n"); goto puma_acpi_add_error; } /* get the reset information */ if((ret = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_MEM_READ_OPCODE, PUMA_PMC_RESET_INFO, &reset_info1)) < 0) { pr_err("iosf_mbi_read failed %d at line %d!\n", ret, __LINE__); goto puma_acpi_add_error; } else if((ret = iosf_mbi_read(PUMA_PMC_PORT_ID, PUMA_PMC_MEM_READ_OPCODE, PUMA_PMC_VUART4, &reset_info2)) < 0) { pr_err("iosf_mbi_read failed %d at line %d!\n", ret, __LINE__); goto puma_acpi_add_error; } else { data->reset_cause = (reset_info2 & PUMA_PMC_RESET_CAUSE_MASK) >> PUMA_PMC_RESET_CAUSE_SHIFT; data->reset_type = (reset_info2 & PUMA_PMC_RESET_TYPE_MASK) >> PUMA_PMC_RESET_TYPE_SHIFT; data->reset_btn_dur = reset_info2 & PUMA_PMC_RESET_BTN_DUR_MASK; /* send IPC to clear the reset info */ if ((ret = puma_acpi_pmc_ipc(PUMA_WRITE_PMC_IPC, PUMA_CLEAR_RESET_INFO, &reset_info1)) != 0) { pr_err("puma_acpi_pmc_ipc PUMA_CLEAR_RESET_INFO failed\n"); goto puma_acpi_add_error; } /* clear the reset duration in VUART4 */ reset_info2 &= ~PUMA_PMC_RESET_BTN_DUR_MASK; ret = iosf_mbi_write(PUMA_PMC_PORT_ID, PUMA_PMC_MEM_WRITE_OPCODE, PUMA_PMC_VUART4, reset_info2); if (ret < 0) { pr_err("iosf mbi wr failed %d at %d!\n", ret, __LINE__); goto puma_acpi_add_error; } } if((ret = puma_acpi_dev_disabled_status(data))) { pr_err("Get devices disabled status failed\n"); goto puma_acpi_add_error; } if((ret = get_pcie_port_dev_info(data->pcie_port_dev_info))) { pr_err("Get pcie port device mapping failed\n"); goto puma_acpi_add_error; } init_waitqueue_head(&puma_wait); init_timer(&data->timer); data->timer_state = PUMA_ACPI_TIMER_NOT_STARTED; data->pmc_state = PUMA_ACPI_STATE_ACTIVE; data->regulator = regulator_get(&data->acpi_dev->dev, "PUMA7_PMIC"); if (!IS_ERR(data->regulator)) { data->pmic_supported = 1; data->pmic_type = PUMA7_ACPI_PMIC; } data->notify_state_type = 0; data->deepstandby2active = false; data->deepstandby2bbu = false; data->on_bat_during_standby = false; data->bbu_device_mask = PUMA_BBU_DEVICE_MASK; data->sb_device_mask = PUMA_SB_DEVICE_MASK; data->dsb_device_mask = PUMA_DSB_DEVICE_MASK; data->device_state = data->enabled_devices_mask; acpi_dev->driver_data = data; strcpy(data->name, "Puma_ACPI_Task"); task = kthread_run(puma_acpi_task, data, "%s", data->name); if (task == NULL || IS_ERR(task)) { pr_err("error kthread_run\n"); ret = -ENOMEM; goto puma_acpi_add_error; } data->task = task; pr_info("PUMA ACPI platform driver loaded!\n"); return 0; puma_acpi_add_error: puma_acpi_free_resources(data, resource_index); pr_err("Failed to load Puma ACPI platform driver!\n"); return ret; } static int puma_acpi_remove(struct acpi_device *acpi_dev) { struct puma_acpi_data *data = acpi_driver_data(acpi_dev); if(&data->timer) { del_timer_sync(&data->timer); } if(data->task) { kthread_stop(data->task); } sysfs_remove_group(&acpi_dev->dev.kobj, &puma_acpi_attr_group); kfifo_free(&data->notify_fifo); kfree(data); return 0; } static const struct acpi_device_id puma_device_ids[] = { {"INT34DB", 0}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, puma_device_ids); static struct acpi_driver puma_acpi_driver = { .name = "Puma ACPI driver", .owner = THIS_MODULE, .ids = puma_device_ids, .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { .add = puma_acpi_add, .remove = puma_acpi_remove, .notify = puma_acpi_notify, }, }; static int __init puma_acpi_init(void) { int ret; ret = acpi_bus_register_driver(&puma_acpi_driver); if (ret) { pr_err("Failed to register ACPI driver: %d\n", ret); } return ret; } static void __exit puma_acpi_exit(void) { acpi_bus_unregister_driver(&puma_acpi_driver); } module_init(puma_acpi_init); module_exit(puma_acpi_exit); MODULE_AUTHOR("Vinay Patel"); MODULE_DESCRIPTION("Puma Platform ACPI Driver"); MODULE_LICENSE("GPL");