/* * netip_subsystem_pm.c * * GPL LICENSE SUMMARY * * Copyright(c) 2015-2018 Intel Corporation. All rights reserved. * * 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 "netip_subsystem_defs.h" #include "netip_subsystem_pm.h" #include "netip_subsystem_sysfs.h" #include #include #define NETIP_CRU0_MOD_STATE_OFFSET (0) #define NETIP_CRU0_MOD_STATUS_OFFSET (4) #define NETIP_CRU0_ENABLE (3) #define NETIP_DEV_CLASS "NETIP_SUBSYSTEM" #define NETIP_DEV_NAME "80862be9" #define PUMA_ACPI_NOTIFY_NETIP_HANDSHAKE_DONE 0xA4 extern int docsis_chan_cfg; extern int cm_mode; extern int RCE_Floor; extern int RCE_Filter; #ifdef CONFIG_NETIP_SERVICES_ON_ATOM extern bool sgmii0_on_atom; extern bool sgmii1_on_atom; extern bool rgmii2_on_atom; extern bool rgmii3_on_atom; #endif void netss_sysfs_clean_up(uint32_t available_services); int __netss_set_service_level(uint32_t level); int netss_notify_netip_power_state_to_child_drivers(netss_power_state_t state); /*External variables */ extern struct net_subsystem_drv_data *pnetss_drv_data; extern struct net_device * __maybe_unused g_netss_netdev; #define NETSS_PM_PRINT(fmt, args...) \ do { \ if(g_netss_sysfs_attributes_info.enable_debug) \ printk(KERN_INFO fmt, ##args); \ } while(0) #define IS_SERVICE_RESOURCE_TO_BE_CONTROLLED(id) ( ( (id == NETSS_SERVICE_ID_EXSW) || \ (id == NETSS_SERVICE_ID_SFP) || \ (id == NETSS_SERVICE_ID_SGMII1_BARE) || \ (id == NETSS_SERVICE_ID_ETHPHY) || \ (id == NETSS_SERVICE_ID_RGMII3_BARE) || \ (id == NETSS_SERVICE_ID_SGMII0_BARE) || \ (id == NETSS_SERVICE_ID_RGMII2_BARE) ) ? 1 : 0) /** Global variables */ netss_sysfs_attributes_info_t g_netss_sysfs_attributes_info; /*The variable when set indicates that a message is received from NetIP *NetIP subsustem msg processing thread sleeps in the work queue *untill this variable is not zero*/ static int g_netip_msg = 0; /*Bit mask representing available services * set bit indicates the availablity of the corresponding service. * bit 0 represents the availability of service id 1 * bit 1 represents the availability of service id 2 and so on * bit 11 represents the availability of service id 12 * As of now the maximum number of services is 12 */ static uint16_t g_netip_services_available = 0; /*The variable indicates the services whose service status message is received. * if bit n is set, service id n-1's service status message is received */ static uint16_t g_netip_services_status_received = 0; /* Netip services that are ready to be moved to desired power level */ static uint16_t g_netip_services_status_ready = 0; /* Netip services that completed the very first power level move */ static uint16_t g_netip_services_init_complete = 0; /* Netip services that are requested to change power level by user */ static uint16_t g_netip_services_change_requested = 0; /* Netip services that got service request reply */ static uint16_t g_netip_services_change_complete = 0; /* Number of Netip services that are ready */ static uint16_t g_num_netip_services_status_ready = 0; /*Netip services whose services request reply is pending *We should servie request one after the other only */ static bool g_netip_service_request_reply_pending = false; /*Indicates that user space updated sysfs attributes while reply is pending */ static bool g_netip_service_level_request_pending = false; /**Indicates whether PMP or power manager informed that we are on battery */ static bool g_onBattery = false; /*Current NetIP power state */ static netss_power_level_t g_current_netip_power_state = NETSS_POWER_LEVEL_INVALID; /**Netip is in soft off state, NetIP is in off state but not clock gated */ static bool g_is_netip_in_soft_off_state = false; /*Indicated if the bootime hand shake with NetIP is done or not */ static bool g_netss_boot_handshake_done = false; /**timer to indicate timeout,if at all happens during dualboot handshake or * while waiting for the service request replies */ static struct timer_list netss_pm_timer; static bool g_timer_created = false; static struct netss_service_request_stats srv_rqst_stats; /**Need to notify once whether internal BBU is on or off */ static bool g_acpi_notified_bbu_status = false; /* Forces not to send service request untill power manger * has set the service level */ static bool g_service_level_initialized = false; #ifdef CONFIG_NETIP_SERVICES_ON_ATOM /* Number of NetIP services on ATtom * Currently hard coding it for xGMII 4 interfaces */ static int g_netip_services_on_atom = 4; #endif /** * puma_netip_notify_state() - invoke to update the state of NETIP * @state: state of NETIP, 0 - Active, 1 - BBU, 2 - Standby, 3 - Deep standby */ extern int puma_netip_notify_state(int state); static DECLARE_WAIT_QUEUE_HEAD(waitForNetIPMsgWQ); /** Initialize all PM global data */ static void netss_pm_init_data(void) { mutex_init(&g_netss_sysfs_attributes_info.lock); init_timer(&netss_pm_timer); g_timer_created = true; /** Initialize services status and level. Notes: Bits 28-31 do not represent a valid service. Bit 2 is unused. Bit 3 indicates on-battery status. */ g_netss_sysfs_attributes_info.service_status = 0xFFFFFFF3; g_netss_sysfs_attributes_info.service_level = 0xFFFFFFF0; g_netss_sysfs_attributes_info.service_ready = 0x0; g_netss_sysfs_attributes_info.port_mapping = 0xFFFFFFFF; } static uint32_t mapid_to_interfaceid(uint8_t map_id) { int i = 0; uint32_t interface_ids = 0; uint8_t interface_id = 0; uint32_t num_interfaces = 0; netss_sysfs_attributes_info_t *sysfs = &g_netss_sysfs_attributes_info; for (i = 0; i < MAX_BOARD_INTERFACES; i++) { if ( map_id == ((sysfs->port_mapping >> (i*8)) & 0xFF)) { NETSS_PM_PRINT("Match found at %d, map id %d\n", i, map_id); if (i == 0) { interface_id = NETSS_BRD_INTRFC_SGMII0; } else if (i == 1) { interface_id = NETSS_BRD_INTRFC_SGMII1; } else if (i == 2) { interface_id = NETSS_BRD_INTRFC_RGMII2; } else if (i == 3) { interface_id = NETSS_BRD_INTRFC_RGMII3; } interface_ids |= (interface_id << (num_interfaces*8)); num_interfaces++; } } NETSS_PM_PRINT("map id %d No.of.interfaces %d ids %08X\n", map_id, num_interfaces, interface_ids); return interface_ids; } /* port map ids shared between Arm's PM SRV and Atom's NetSS driver NETSS_PORTMAP_BARE = 0, NETSS_PORTMAP_EXSW = 1, NETSS_PORTMAP_ETHPHY = 2, NETSS_PORTMAP_SFP = 3 */ static uint32_t get_interface_id(uint8_t service_id) { uint32_t interface_ids = 0; switch (service_id) { case NETSS_SERVICE_ID_EXSW: interface_ids = mapid_to_interfaceid(NETSS_PORTMAP_EXSW); break; case NETSS_SERVICE_ID_SFP: interface_ids = mapid_to_interfaceid(NETSS_PORTMAP_SFP); break; case NETSS_SERVICE_ID_SGMII1_BARE: interface_ids = NETSS_BRD_INTRFC_SGMII1; break; case NETSS_SERVICE_ID_ETHPHY: interface_ids = mapid_to_interfaceid(NETSS_PORTMAP_ETHPHY); break; case NETSS_SERVICE_ID_RGMII3_BARE: interface_ids = NETSS_BRD_INTRFC_RGMII3; break; case NETSS_SERVICE_ID_SGMII0_BARE: interface_ids = NETSS_BRD_INTRFC_SGMII0; break; case NETSS_SERVICE_ID_RGMII2_BARE: interface_ids = NETSS_BRD_INTRFC_RGMII2; break; default: break; }; return interface_ids; } #ifdef CONFIG_NETIP_SERVICES_ON_ATOM static uint8_t map_id_to_service_id(uint8_t idx, uint8_t map_id) { uint8_t service_id = 0; if (map_id == NETSS_PORTMAP_BARE) { if (idx == 0) service_id = NETSS_SERVICE_ID_SGMII0_BARE; else if (idx == 1) service_id = NETSS_SERVICE_ID_SGMII1_BARE; else if (idx == 2) service_id = NETSS_SERVICE_ID_RGMII2_BARE; else if (idx == 3) service_id = NETSS_SERVICE_ID_RGMII3_BARE; } else if (map_id == NETSS_PORTMAP_EXSW) { service_id = NETSS_SERVICE_ID_EXSW; } else if (map_id == NETSS_PORTMAP_ETHPHY) { service_id = NETSS_SERVICE_ID_ETHPHY; } else if (map_id == NETSS_PORTMAP_SFP) { service_id = NETSS_SERVICE_ID_SFP; } return service_id; } /* port map ids shared between Arm's PM SRV and Atom's NetSS driver NETSS_PORTMAP_BARE = 0, NETSS_PORTMAP_EXSW = 1, NETSS_PORTMAP_ETHPHY = 2, NETSS_PORTMAP_SFP = 3 */ static uint8_t __maybe_unused get_service_id(uint8_t interface_id) { uint8_t service_id = 0; uint8_t map_id = 0; netss_sysfs_attributes_info_t *sysfs = &g_netss_sysfs_attributes_info; switch (interface_id) { case NETSS_BRD_INTRFC_SGMII0: map_id = sysfs->port_mapping & 0xFF; service_id = map_id_to_service_id(0, map_id); break; case NETSS_BRD_INTRFC_SGMII1: map_id = (sysfs->port_mapping >> 8) & 0xFF; service_id = map_id_to_service_id(1, map_id); break; case NETSS_BRD_INTRFC_RGMII2: map_id = (sysfs->port_mapping >> 16) & 0xFF; service_id = map_id_to_service_id(2, map_id); break; case NETSS_BRD_INTRFC_RGMII3: map_id = (sysfs->port_mapping >> 24) & 0xFF; service_id = map_id_to_service_id(3, map_id); break; default: break; } NETSS_PM_PRINT("Interface id=%d Service ID=%d\n", interface_id, service_id); return service_id; } #endif static void netss_handle_resource_power_state(uint8_t service_id, uint8_t state) { struct acpi_object_list input; union acpi_object in_params[2]; int i = 0; uint32_t interface_ids = get_interface_id(service_id); if(pnetss_drv_data->acpi_h == NULL) { printk("NetIP acpi handle is NULL \n"); return; } for (i = 0; i < MAX_BOARD_INTERFACES; i++) { uint8_t interface_id = (interface_ids >> (i*8)); if (interface_id != 0) { input.count = 2; input.pointer = in_params; in_params[0].type = ACPI_TYPE_INTEGER; in_params[0].integer.value = interface_id; in_params[1].type = ACPI_TYPE_INTEGER; in_params[1].integer.value = state; /** call ACPI function from here to power on/off of the resource associated with the service */ NETSS_PM_PRINT("Changing the resource power state of interface id %d service id %d state %d\n", interface_id, service_id, state); acpi_evaluate_object(pnetss_drv_data->acpi_h, "SRPS", &input, NULL); } } return; } static void netss_indicate_bbu_service_level(uint8_t level) { struct acpi_object_list input; union acpi_object in_param; input.count = 1; input.pointer = &in_param; in_param.type = ACPI_TYPE_INTEGER; in_param.integer.value = level; if(pnetss_drv_data->acpi_h == NULL) { printk("NetIP acpi handle is NULL \n"); } else { /** call ACPI function from here to inform whether internal BBU is being used */ if(!g_acpi_notified_bbu_status) { NETSS_PM_PRINT("Internal BBU being used? %s \n", (level? "YES" : "NO")); acpi_evaluate_object(pnetss_drv_data->acpi_h, "NBED", &input, NULL); g_acpi_notified_bbu_status= true; } } } static void netss_send_port_mapping(uint32_t port_mapping) { struct acpi_object_list input; union acpi_object in_params[4]; input.count = 4; input.pointer = in_params; in_params[0].type = ACPI_TYPE_INTEGER; in_params[0].integer.value = port_mapping & 0xFF; in_params[1].type = ACPI_TYPE_INTEGER; in_params[1].integer.value = (port_mapping >> 8) & 0xFF; in_params[2].type = ACPI_TYPE_INTEGER; in_params[2].integer.value = (port_mapping >> 16) & 0xFF; in_params[3].type = ACPI_TYPE_INTEGER; in_params[3].integer.value = (port_mapping >> 24) & 0xFF; if(pnetss_drv_data->acpi_h == NULL) { printk("NetIP acpi handle is NULL \n"); } else { /** Pass the port mapping to ACPI */ NETSS_PM_PRINT("Passing the port mapping to ACPI %08X\n", port_mapping); acpi_evaluate_object(pnetss_drv_data->acpi_h, "gmap", &input, NULL); } return; } static void netss_pm_timeout_handler (unsigned long data) { printk("IT APPEARS THAT ATOM NOT RECEIVING MSGS FROM NETIP \n"); printk("Keep Checking #cat /sys/class/net/netip_subsystem/service_status \n"); if(g_netss_boot_handshake_done) { /**We dint get replies for all service request made */ printk("Timeout happened while waiting for service request replies\n"); printk("services requested to change power level %08X\n", g_netip_services_change_requested); printk("Received service request replies for %08X\n", g_netip_services_change_complete); } else { printk("Dualboot handshake dint happen \n"); printk("Available services = 0x%08X\n", g_netip_services_available); printk("services whose status recieved = 0x%08X\n", g_netip_services_status_received); printk("services ready = 0x%08X\n", g_netip_services_status_ready); printk("services that completed initial power level change = 0x%08X\n", g_netip_services_init_complete); } } static int netss_receive_netip_msg(hw_mbox_Masters_e commander, //Uint32 code, Uint8 *dataBuf, Uint32 dataLen, Uint32 *token) { int i = 0; int ret = 0; char *msg_name = "unknown"; mutex_lock(&pnetss_drv_data->netip_msg.lock); memcpy(pnetss_drv_data->netip_msg.payload, dataBuf, dataLen); pnetss_drv_data->netip_msg.len = dataLen; pnetss_drv_data->netip_msg.type = dataBuf[0]; /*Wake up thread, cleanup sysfs attributes */ if(pnetss_drv_data->netip_msg.type == NETSS_OPCODE_PORT_MAPPING) { g_netip_msg = 4; msg_name = "Port Mapping"; } else if(pnetss_drv_data->netip_msg.type == NETSS_OPCODE_NETIP_PROPERTIES) { g_netip_msg = 1; msg_name = "Properties"; } else if (pnetss_drv_data->netip_msg.type == NETSS_OPCODE_SERVICE_STATUS) { g_netip_msg = 2; msg_name = "Service Status"; } else if (pnetss_drv_data->netip_msg.type == NETSS_OPCODE_SERVICE_REQUEST_REPLY) { g_netip_msg = 3; msg_name = "Service Request Reply"; } else { ret = -1; } NETSS_PM_PRINT("NetIP Msg recvd: %s\n", msg_name); for(i=0; inetip_msg.lock); wake_up_all(&waitForNetIPMsgWQ); return ret; } static void netss_send_reply_ack(void) { int ret = 0; ret = hwMbox_sendAckOpcode(HW_MBOX_MASTER_NP_CPU); if(ret) { printk(KERN_ERR "Failed to send Ack to msg ret %d\n", ret); } return; } #if 0 static void netss_send_reply_msg(void) { uint8_t reply_msg[64]; memcpy(reply_msg, pnetss_drv_data->netip_msg.payload, pnetss_drv_data->netip_msg.len); /* Sending what recieved */ hwMbox_sendReplyOpcode(HW_MBOX_MASTER_NP_CPU, reply_msg, pnetss_drv_data->netip_msg.len); return; } #endif /** Port Mapping message format * --------------------------- * | Opcode - 4bytes | * |-------------------------| * |-------------------------| * |port mapping indices | * |IDX0 |IDX1 |IDX2 |IDX3 | * |-------------------------| * |-------------------------| * | 0 | 1, 2, 3 or 4 terminating bytes * |-------------------------| */ static void ness_process_port_mapping_msg(void) { uint8_t *data = (uint8_t *)(&pnetss_drv_data->netip_msg.payload[4]); netss_sysfs_attributes_info_t *sysfs = &g_netss_sysfs_attributes_info; int i = 0; sysfs->port_mapping = 0; for (i = 0; i < MAX_BOARD_INTERFACES; i++) sysfs->port_mapping |= data[i] << (i*8); NETSS_PM_PRINT("Netss port mapping = %08X\n", sysfs->port_mapping); netss_send_port_mapping(sysfs->port_mapping); return; } /** NetIP properties message format * --------------------------- * | Opcode - 4bytes | * |-------------------------| * |-------------------------| * | service ids of 1byte ech| * | ID | ID | ID | ID | * |-------------------------| * | ID | ID | ID | ID | * |-------------------------| * | And so on......... | * |-------------------------| * |-------------------------| * | 0 | 1, 2, 3 or 4 terminating bytes * |-------------------------| */ static void ness_process_netip_props_msg(void) { uint8_t *data = (uint8_t *)(&pnetss_drv_data->netip_msg.payload[4]); while (*data != 0) { /*netip properties message is null terminated *set the bit n-1, for the service id n *service id starts from 1 */ if (*data < NETSS_SERVICE_ID_MAX) { g_netip_services_available |= 1 << (*data-1); /** Update sysfs service_available attribute */ g_netss_sysfs_attributes_info.service_available |= NETSS_SET_SERVICE_POWER_LEVEL(*data, 1); } else { pr_err("Invalid service id in properties msg %d\n", *data); } /*Check next two bytes */ data++; } /**Internal BBU service does not exist */ if (!(g_netip_services_available & (1 << (NETSS_SERVICE_ID_BBU-1)))) { /**Inform ACPI internal BBU service is off */ netss_indicate_bbu_service_level(0); } NETSS_PM_PRINT("services that are available = %x\n", g_netip_services_available); return; } /** NetIP configure message format * ------------------------------------ * |header including Opcode -4bytes | * |----------------------------------| * | chan id | chan id|chan id|chan id| 32 bytes are reserved for 32 channels [1...32] * | 1byte | 1byte |1byte |1byte | to be specified as docsis channels. * |----------------------------------| terminate list with 0 * |And so on.... * |----------------------------------| * | chan id | chan id|chan id|chan id| * | 1bytes | 1byte |1byte |1byte | * |----------------------------------| * | cable modem mode 0-docsis, 1-GW | * | 4bytes | * |----------------------------------| * | RCE Floor | * | 4bytes | * |----------------------------------| * | RCE Filter | * | 4bytes | * |----------------------------------| */ static void netss_send_configure_msg (void) { int i, j=0, ret; //uint32_t docsis_channels = 0xF0F00000; /*Mask indicating the channels numbers configured as docsis channels */ netss_configure_msg_t *cfg_msg; uint8_t *msg; uint32_t reply_len; int msg_len, bytes_consumed=0; #ifdef CONFIG_NETIP_SERVICES_ON_ATOM uint8_t service_id; #endif #ifdef CONFIG_NETIP_SERVICES_ON_ATOM int services_offset = sizeof(netss_configure_msg_t); #endif msg_len = sizeof(netss_configure_msg_t); #ifdef CONFIG_NETIP_SERVICES_ON_ATOM msg_len += g_netip_services_on_atom; #endif /*Make 4 byte aligned, if it is already 4 byte aligned increment by 4 bytes for null termination */ msg_len = ((msg_len + 4) & ~3); cfg_msg = kzalloc(msg_len, GFP_KERNEL); if(!cfg_msg) { printk(KERN_ERR "Failed to alloc memory for configure msg!\n"); return; } msg = (uint8_t *)cfg_msg; cfg_msg->hdr.opcode = NETSS_OPCODE_CONFIGURE; /*These values should be read from platfom config files *for now writing some default values */ for(i=0; i < 32; i++) { if(docsis_chan_cfg & (1 << i)) { cfg_msg->docsis_chan[j] = i+1; j++; } } /*Just write 0 to terminate the list */ if(j < 32) { cfg_msg->docsis_chan[j] = 0; } cfg_msg->cm_mode = cm_mode; cfg_msg->RCE_floor = RCE_Floor; cfg_msg->RCE_filter = RCE_Filter; bytes_consumed = sizeof(netss_configure_msg_t); #ifdef CONFIG_NETIP_SERVICES_ON_ATOM msg += services_offset; /**Get the service ID connected at sgmii0 */ service_id = get_service_id(NETSS_BRD_INTRFC_SGMII0); if ((pnetss_drv_data->bios_enabled_xgmiis & (1 << NETSS_SGMII0_UID)) && sgmii0_on_atom && service_id) *msg++ = service_id; /**Get the service ID connected at sgmii1 */ service_id = get_service_id(NETSS_BRD_INTRFC_SGMII1); if ((pnetss_drv_data->bios_enabled_xgmiis & (1 << NETSS_SGMII1_UID)) && sgmii1_on_atom && service_id) *msg++ = service_id; /**Get the service ID connected at rgmii2 */ service_id = get_service_id(NETSS_BRD_INTRFC_RGMII2); if (rgmii2_on_atom && service_id) *msg++ = service_id; /**Get the service ID connected at rgmii3 */ service_id = get_service_id(NETSS_BRD_INTRFC_RGMII3); if (rgmii3_on_atom && service_id) *msg++ = service_id; bytes_consumed = msg - (uint8_t *)cfg_msg; msg = (uint8_t *)cfg_msg; #endif /*Make 4 byte aligned, if it is already 4 byte aligned increment by 4 bytes for null termination */ bytes_consumed = ((bytes_consumed + 4) & ~3); NETSS_PM_PRINT("configure Msg: msg_len=[%d] bytes_consumed=[%d]\n", msg_len, bytes_consumed); for(i=0; inetip_msg.payload[0]; netss_service_status_t *service_status = (netss_service_status_t *)&pnetss_drv_data->netip_msg.payload[SERVICE_OFFSET]; NETSS_PM_PRINT("processing service status message opcode %d\n", service_msg->hdr.opcode); while(service_status[i].id != 0) { uint16_t id_mask = 1 << (service_status[i].id - 1); /** Check if the service id is available i.e mentioned in netip properties msg and valid */ if(g_netip_services_available & id_mask) { if(!(g_netip_services_status_received & id_mask)) { g_netip_services_status_received |= id_mask; /* Services that are ready to request */ if(service_status[i].state == NETSS_SERVICE_STATUS_INITIALIZED) { g_netip_services_status_ready |= id_mask; g_num_netip_services_status_ready++; /** Update sysfs service_ready attribute */ g_netss_sysfs_attributes_info.service_ready |= NETSS_SET_SERVICE_POWER_LEVEL(service_status[i].id, NETSS_SERVICE_STATUS_INITIALIZED); } } } else { printk("Error:Invalid service id in the status message %d\n", service_status[i].id); } i++; } return; } static char * netss_stringfy_netip_state(void) { char *state_string = "Invalid"; switch (g_current_netip_power_state) { case NETSS_POWER_LEVEL_ENABLE: if (g_onBattery) state_string = "Active on Battery"; else state_string = "Active on AC"; break; case NETSS_POWER_LEVEL_DISABLE: state_string = "Fast Standby"; break; case NETSS_POWER_LEVEL_DEEP_STANDBY: state_string = "Deep Standby"; break; default: state_string = "Invalid"; break; } return state_string; } static void netss_update_sysfs_attributes(netss_sysfs_attributes_info_t *sysfs_info, netss_service_request_msg_t *service_msg) { int i=0; /*Upon successful reply process the reply and update sysfs attributes */ //sysfs_info->runtime_state[0] = service_msg->netip_level; NETSS_PM_PRINT("--> Service Status(0x%08x)\n", sysfs_info->service_status); while(service_msg->service[i].id != 0) { NETSS_PM_PRINT("Service ID(%d) Status(%d)\n", service_msg->service[i].id, service_msg->service[i].state); /**Clear and update the individual service level */ NETSS_CLEAR_SERVICE_POWER_LEVEL(service_msg->service[i].id, sysfs_info->service_status); sysfs_info->service_status |= NETSS_SET_SERVICE_POWER_LEVEL(service_msg->service[i].id, service_msg->service[i].state); if( IS_SERVICE_RESOURCE_TO_BE_CONTROLLED(service_msg->service[i].id) && (service_msg->service[i].state == NETSS_SERVICE_POWER_LEVEL_OFF)) { netss_handle_resource_power_state(service_msg->service[i].id, service_msg->service[i].state); } /**Internal BBU service is available, inform acip whether user turning it on or off */ if(service_msg->service[i].id == NETSS_SERVICE_ID_BBU) { netss_indicate_bbu_service_level((service_msg->service[i].state == NETSS_SERVICE_POWER_LEVEL_ON)? 1:0); } if(!g_netss_boot_handshake_done) { /*Service completed initalized by moving to the requested power level*/ g_netip_services_init_complete |= (1<< (service_msg->service[i].id-1)); } else { g_netip_services_change_complete |= (1<< (service_msg->service[i].id-1)); } i++; } if(!g_netss_boot_handshake_done) { NETSS_PM_PRINT("services_ready = %X init complete %X service status %X\n", g_netip_services_status_ready, g_netip_services_init_complete, sysfs_info->service_status); } else { NETSS_PM_PRINT("services_change requested = %X change complete %X service status = %X\n", g_netip_services_change_requested, g_netip_services_change_complete, sysfs_info->service_status); } /*This part of the code updates the overall system state and should be exceuted on two condtions *1. During boot, i.e before dual boot hand shake, after all the available service status are recieved * and the service request replies received for all the ready services. *2. After boot, i.e dual boot handshake, power manager might have requested state change in that *case check if all change requests are completed with replies. **/ if( (!g_netss_boot_handshake_done && (g_netip_services_status_received == g_netip_services_available) && (g_netip_services_status_ready == g_netip_services_init_complete)) || (g_netss_boot_handshake_done && (g_netip_services_change_complete == g_netip_services_change_requested)) ) { uint32_t temp = NETSS_POWER_LEVEL_ENABLE; /** Clear and update netip level after receiving replies for all services requested. */ sysfs_info->service_status &= ~NETSS_BIT_MASK_POWER_LEVEL; sysfs_info->service_status |= service_msg->netip_level & NETSS_BIT_MASK_POWER_LEVEL; /**This is before dualboot handshake is done, system boot*/ if (g_current_netip_power_state != NETSS_POWER_LEVEL_INVALID) { temp = g_current_netip_power_state; } g_current_netip_power_state = service_msg->netip_level; if(g_timer_created) { /**delete the timer as we completed either dualboot handshake or * a service_request change initiated by user after dualboot handshake */ del_timer_sync(&netss_pm_timer); } /**update the status vector with battery status */ if(g_onBattery) { sysfs_info->service_status |= NETSS_BIT_MASK_ON_BATTERY; } else { sysfs_info->service_status &= ~NETSS_BIT_MASK_ON_BATTERY; } if(service_msg->netip_level == NETSS_POWER_LEVEL_RESERVED) { NETSS_PM_PRINT("ERROR NetIP level should not be reserved level = %d\n", NETSS_POWER_LEVEL_RESERVED); } if(g_current_netip_power_state == NETSS_POWER_LEVEL_DEEP_STANDBY) { /**We are not suspending meaning not clocking gating etc, but doing a netip soft off */ if(sysfs_info->netip_soft_off) { /**Notify the NetIP power state to all child drivers */ netss_notify_netip_power_state_to_child_drivers(NETSS_NETIP_POWER_STATE_OFF); /**Indicate that netip_soft_off exectution is complete, as we got reply for all service requests */ sysfs_info->netip_soft_off = 0; #if IS_ENABLED(CONFIG_NET_SUBSYSTEM_SYSFS) sysfs_notify(&g_netss_netdev->dev.kobj, NULL, "netip_soft_off"); #endif /**Indicate that netip is in soft off state */ g_is_netip_in_soft_off_state = true; } else { /**Let the usage counter go to zero */ pm_runtime_put(&pnetss_drv_data->dev->dev); } } else if (g_current_netip_power_state == NETSS_POWER_LEVEL_ENABLE && temp == NETSS_POWER_LEVEL_DEEP_STANDBY) { /**NetIP came out of soft off state to active */ if(g_is_netip_in_soft_off_state) { /**Notify the NetIP power state to all child drivers */ netss_notify_netip_power_state_to_child_drivers(NETSS_NETIP_POWER_STATE_ACTIVE); g_is_netip_in_soft_off_state = false; } } if(temp < g_current_netip_power_state && g_current_netip_power_state != NETSS_POWER_LEVEL_DEEP_STANDBY) { /* No need to call if going to deepstandby, because netss_runtime_suspend will call. * 1. Call the platform driver api to inform the new power state * we are going to lower power level like active to bbu, active to fast standby/disable */ if(g_current_netip_power_state == NETSS_POWER_LEVEL_DISABLE) { puma_netip_notify_state(2); } else if(g_onBattery) { /**inform that we are on BBU */ puma_netip_notify_state(1); } } /** g_current_netip_power_state will never be NETSS_POWER_LEVEL_RESERVED, just a safety check */ else if(temp == g_current_netip_power_state && g_current_netip_power_state != NETSS_POWER_LEVEL_RESERVED) { /**There is no change in the netip level */ if(g_onBattery && (g_current_netip_power_state == NETSS_POWER_LEVEL_ENABLE)) { puma_netip_notify_state(1); } else { puma_netip_notify_state(g_current_netip_power_state); } } /*We are good to make another cyle of service requests */ if(g_netip_services_change_complete == g_netip_services_change_requested) { unsigned time_taken = 0; srv_rqst_stats.end = jiffies; time_taken = jiffies_to_msecs(srv_rqst_stats.end - srv_rqst_stats.start); NETSS_PM_PRINT("Time taken to transit from %s to %s (%u) msec\n", srv_rqst_stats.old_state, netss_stringfy_netip_state(), time_taken); g_netip_services_change_complete = 0; g_netip_services_change_requested = 0; if (g_netip_service_level_request_pending) { g_netip_service_level_request_pending = false; /*sysfs attribute was changed from user space, process it */ __netss_set_service_level(g_netss_sysfs_attributes_info.service_level); } } } NETSS_PM_PRINT("<-- Service Status(0x%08x)\n", sysfs_info->service_status); return; } static bool netss_request_for_service_level_change(int index, uint32_t level) { bool add_service_to_msg = false; uint32_t service_mask = (1 << (index - 1)); uint32_t status = g_netss_sysfs_attributes_info.service_status; if(g_netip_services_status_ready & service_mask) { if(g_netss_boot_handshake_done) add_service_to_msg = !(g_netip_services_change_requested & service_mask); else add_service_to_msg = !(g_netip_services_init_complete & service_mask) || (NETSS_GET_SERVICE_POWER_LEVEL(index, status) != NETSS_GET_SERVICE_POWER_LEVEL(index, level)); } return add_service_to_msg; } /*Service request message format is same as service status message *sys/class/net/netip_subsystem/service_level is set by PMP (power management policy) *and it is interpreted as follows * --------------------------------------------- * |BITS |Service | | | | ID | Interpretation | * |------------------------------------------ | * |1:0 | _ | NetIP 00 - Enable | * | | | 01 - Reserved | * | | | 10 - Disable/Fast Standby| * | | | 11 - Deep stanby | * |2:2 | | Reserved | * |3:3 | | On Battery | * |-------------------------------------------| * |5:4 | 1 | Docsis | * |7:6 | 2 | Spectral Analysis | * |9:8 | 3 | Video | * |11:10| 4 | Voice 00 - On | * |13:12| 5 | Moca 01 - Low | * |15:14| 6 | BBU 10 - Reserved | * |17:16| 7 | Wifi 11 - Off | * |19:18| 8 | ExSw | * |21:20| 9 | SFP | * |23:22| 10 | SGMII1_BARE | * |25:24| 11 | ETHPHY | * |27:26| 12 | RGMII3_BARE | * |29:28| 13 | SGMII0_BARE | * |31:30| 14 | RGMII2_BARE | * --------------------------------------------- */ /** service request/reply message format * --------------------------- * | Opcode - 4bytes | * |-------------------------| * | On Battery - 4bytes | * |-------------------------| * | netip level - 4bytes | * |-------------------------| * | serv | | serv | | * | id |level| id |level| * | 1byt | 1byt| 1byt | 1byt| * |-------------------------| * |-------------------------| * | And so on......... | * |-------------------------| * | serv | | serv | | * | id |level| id |level| * | 1byt | 1byt| 1byt | 1byt| * |-------------------------| * | 0 | terminating bytes * |-------------------------| */ static int netss_send_service_request_msg(uint32_t service_level) { int i=0, j=0; int ret = -1; uint32_t reply_len; uint32_t msg_len=12; netss_service_request_msg_t *service_msg = NULL; uint8_t *msg; uint32_t service_msg_len; uint16_t service_count = 0; /*Need one service for null termination */ service_msg_len = sizeof(netss_service_request_msg_t)+((g_num_netip_services_status_ready+1)*sizeof(netss_service_status_t)); /*Align the size to 4 byte boundary */ service_msg_len = (service_msg_len + 3) & ~3; /* Alloc memory for service request message */ service_msg = kzalloc(service_msg_len, GFP_KERNEL); msg = (uint8_t *)service_msg; if(!service_msg) { printk(KERN_ERR "Could not allocate service request message\n"); } else { //mutex_lock(&syfs_info->lock); srv_rqst_stats.old_state = netss_stringfy_netip_state(); service_msg->hdr.opcode = NETSS_OPCODE_SERVICE_REQUEST; service_msg->onBattery = g_onBattery = (service_level & NETSS_BIT_MASK_ON_BATTERY)? 1 : 0; service_msg->netip_level = (service_level & NETSS_BIT_MASK_POWER_LEVEL); if(service_msg->netip_level == NETSS_POWER_LEVEL_RESERVED) { NETSS_PM_PRINT("ERROR NetIP level should not be reserved\n"); } for(i = NETSS_SERVICE_ID_DOCSIS; i < NETSS_SERVICE_ID_MAX; i++) { if(netss_request_for_service_level_change(i, service_level)) { uint32_t level = NETSS_GET_SERVICE_POWER_LEVEL(i, service_level); service_count++; if (service_count > g_num_netip_services_status_ready) { printk(KERN_ERR "Allocated memory for %d services only\n", g_num_netip_services_status_ready); break; } if(level == NETSS_SERVICE_POWER_LEVEL_RESERVED) { NETSS_PM_PRINT("ERROR Cannot set service id %d to level %d\n", i, NETSS_SERVICE_POWER_LEVEL_RESERVED); } else { if( IS_SERVICE_RESOURCE_TO_BE_CONTROLLED(i) && (level == NETSS_SERVICE_POWER_LEVEL_ON)) { netss_handle_resource_power_state(i, level); } service_msg->service[j].id = i; service_msg->service[j].state = level; j++; msg_len += 2; } } } /*Null termination of the list */ if(msg_len % 4) { service_msg->service[j].id = 0; service_msg->service[j].state = 0; msg_len += 2; } else { service_msg->service[j].id = 0; service_msg->service[j].state = 0; j++; service_msg->service[j].id = 0; service_msg->service[j].state = 0; msg_len += 4; } NETSS_PM_PRINT("service request:"); for(i=0; iservice[i].id != 0) { g_netip_services_change_requested |= (1 << (service_msg->service[i].id-1)); } } } if(g_timer_created) { /**Set the timeout value two minutes before getting replies for all the services */ mod_timer(&netss_pm_timer, jiffies + msecs_to_jiffies(120000)); } } // mutex_unlock(&syfs_info->lock); /*Can free the message memory now */ kfree(service_msg); } /**Add some debug message on success */ return ret; } static int netss_configure_and_setup_netip_services(void *data) { int count=0; int sleep_count=0; netss_pm_init_data(); while(1) { if(0 == hwMbox_isReady()) { /*HW Mailbox driver is ready register callbacks for netip pm messages */ /*Register netip properties message, update tag and token later */ hwMbox_registerRecvOpcode(HW_MBOX_MASTER_NP_CPU, netss_receive_netip_msg, NETSS_MSG_TAG_PROPS_CNFGR_STAT, 0x0); hwMbox_registerRecvOpcode(HW_MBOX_MASTER_NP_CPU, netss_receive_netip_msg, NETSS_MSG_TAG_SERVICE_REQ_RPLY, 0x1); NETSS_PM_PRINT("Slept for iterations %d msec %d\n", sleep_count, sleep_count*10); break; } msleep(10); sleep_count++; } /** Within a minute atleast we should receive netip propertis msg */ netss_pm_timer.expires = jiffies + msecs_to_jiffies(60000); netss_pm_timer.function = netss_pm_timeout_handler; add_timer(&netss_pm_timer); while(1) { /*Wait for netip messages */ wait_event_interruptible(waitForNetIPMsgWQ, ((g_netip_msg != NETIP_MSG_NONE) || ((kthread_should_stop())))); mutex_lock(&pnetss_drv_data->netip_msg.lock); if(kthread_should_stop()) { mutex_unlock(&pnetss_drv_data->netip_msg.lock); break; }; NETSS_PM_PRINT("Message from NetIP %d opcode %d \n", g_netip_msg, pnetss_drv_data->netip_msg.type); /*Acquire sysfs lock */ mutex_lock(&g_netss_sysfs_attributes_info.lock); if(pnetss_drv_data->netip_msg.type == NETSS_OPCODE_PORT_MAPPING) { /*send reply*/ netss_send_reply_ack(); /*process netip properties message */ ness_process_port_mapping_msg(); } else if(pnetss_drv_data->netip_msg.type == NETSS_OPCODE_NETIP_PROPERTIES) { /*send reply*/ netss_send_reply_ack(); /*process netip properties message */ ness_process_netip_props_msg(); /* send configure message */ netss_send_configure_msg(); if(g_timer_created) { /**Within 120 seconds we should receive status message */ mod_timer(&netss_pm_timer, jiffies + msecs_to_jiffies(120000)); } } else if(pnetss_drv_data->netip_msg.type == NETSS_OPCODE_SERVICE_STATUS) { /*send reply*/ netss_send_reply_ack(); /*process service status message */ ness_process_service_status_msg(); if(!g_netip_service_request_reply_pending && g_service_level_initialized) { /* send service request message */ netss_send_service_request_msg(g_netss_sysfs_attributes_info.service_level); } } else if(pnetss_drv_data->netip_msg.type == NETSS_OPCODE_SERVICE_REQUEST_REPLY) { /*send reply*/ netss_send_reply_ack(); /*We got a reply */ g_netip_service_request_reply_pending = false; netss_update_sysfs_attributes(&g_netss_sysfs_attributes_info, (netss_service_request_msg_t*) &pnetss_drv_data->netip_msg.payload[0]); /*Check if all the ready services are moved to initial power state */ if(g_netip_services_status_ready == g_netip_services_init_complete) { if(g_netss_boot_handshake_done == false) { /*Check if we have received status messages for all services */ if(g_netip_services_status_received == g_netip_services_available) { g_netss_boot_handshake_done = true; acpi_bus_generate_netlink_event(NETIP_DEV_CLASS, NETIP_DEV_NAME, PUMA_ACPI_NOTIFY_NETIP_HANDSHAKE_DONE, 0); printk("Dual boot handshake is done services available %08X init complete %08X\n", g_netip_services_available, g_netip_services_init_complete); /**Increament the usage counter */ /**Count is already one so dont increament */ //pm_runtime_get_noresume(&pnetss_drv_data->dev->dev); } else { NETSS_PM_PRINT("Status of all services not recvd Available %X status recvd %X\n", g_netip_services_available, g_netip_services_status_received); } } } else { /* send service request message */ netss_send_service_request_msg(g_netss_sysfs_attributes_info.service_level); } } else if (g_netip_msg == 5) { NETSS_PM_PRINT("test wake up received %d\n", count++); } else { NETSS_PM_PRINT("Unknown message received Opcode type %d\n", pnetss_drv_data->netip_msg.type); /*send reply*/ netss_send_reply_ack(); } /*Done processing the message */ g_netip_msg = NETIP_MSG_NONE; /*Release sysfs lock */ mutex_unlock(&g_netss_sysfs_attributes_info.lock); /*Release NetIP message lock */ mutex_unlock(&pnetss_drv_data->netip_msg.lock); /*send service request message */ } mutex_destroy(&g_netss_sysfs_attributes_info.lock); return 0; } void netss_manage_netip_services(void) { pnetss_drv_data->handshake_thread = kthread_run(netss_configure_and_setup_netip_services, NULL, "netip_service_manager"); if(!pnetss_drv_data->handshake_thread) { printk(KERN_ERR "Failed to create netss boot hand shake thread\n"); } return; } void netss_stop_handshake_thread(void) { g_netip_msg = 3; //wake_up_all(&waitForNetIPMsgWQ); kthread_stop(pnetss_drv_data->handshake_thread); return; } uint32_t netss_get_attribute(uint32_t attr_id, uint32_t service_id) { uint32_t value = 0, bit_offset = 0, service_id_mask = 0xFFFFFFFF; if (service_id == 0) { service_id_mask = 0xF; } else if (service_id < NETSS_SERVICE_ID_MAX) { bit_offset = (2 * (service_id + 1)); service_id_mask = NETSS_BIT_MASK_POWER_LEVEL << bit_offset; } mutex_lock(&g_netss_sysfs_attributes_info.lock); switch (attr_id) { case NETSS_SERVICE_LEVEL_ATTR: value = (g_netss_sysfs_attributes_info.service_level & service_id_mask) >> bit_offset; break; case NETSS_SERVICE_STATE_ATTR: value = (g_netss_sysfs_attributes_info.service_status & service_id_mask) >> bit_offset; break; case NETSS_SERVICE_AVAILABLE_ATTR: value = (g_netss_sysfs_attributes_info.service_available & service_id_mask) >> bit_offset; break; case NETSS_NETIP_SOFT_OFF_ATTR: value = g_netss_sysfs_attributes_info.netip_soft_off; break; case NETSS_ENABLE_DEBUG_ATTR: value = g_netss_sysfs_attributes_info.enable_debug; break; case NETSS_SERVICE_READY_ATTR: value = (g_netss_sysfs_attributes_info.service_ready & service_id_mask) >> bit_offset; break; case NETSS_BOOT_HANDSHAKE_DONE_ATTR: value = g_netss_boot_handshake_done; break; case NETSS_PORT_MAPPING_ATTR: value = g_netss_sysfs_attributes_info.port_mapping; break; default: pr_err("Attempting to get an invalid attribute ID!\n"); } mutex_unlock(&g_netss_sysfs_attributes_info.lock); return value; } int netss_set_attribute(uint32_t attr_id, uint32_t value, uint32_t service_id) { int ret = -EINVAL; mutex_lock(&g_netss_sysfs_attributes_info.lock); // TODO: Implement logic to set the level to an individual service, // it may require to add another global variable (level_mask) switch (attr_id) { case NETSS_SERVICE_LEVEL_ATTR: ret = __netss_set_service_level(value); break; case NETSS_NETIP_SOFT_OFF_ATTR: g_netss_sysfs_attributes_info.netip_soft_off = (value != 0); ret = __netss_set_service_level(0xAFFFFFF3); break; case NETSS_ENABLE_DEBUG_ATTR: g_netss_sysfs_attributes_info.enable_debug = (value != 0); ret = 0; break; default: printk(KERN_ERR "Attempting to set an invalid attribute ID!\n"); } mutex_unlock(&g_netss_sysfs_attributes_info.lock); return ret; } int __netss_set_service_level(uint32_t level) { int ret = 0; netss_power_level_t new_state = level & NETSS_BIT_MASK_POWER_LEVEL; /** Updated the global service level */ g_netss_sysfs_attributes_info.service_level = level; /* Service request reply is pending mark service request is pending * so this request will be sent after receiving all replies to * the earlier request */ if (g_netip_service_request_reply_pending) { NETSS_PM_PRINT("service level Request pending %X\n", level); g_netip_service_level_request_pending = true; } else if (g_netss_boot_handshake_done) { /* NOTE: WHEN TO CALL PLATFORM API * If new power state is greater old power state, * call before making service request * Else if new power state old power state, * call after making service request * ORDER OF THE POWER states FROM HIGHEST TO LOWEST * |======================= ===================| * |POWER LEVELS | ENUMERATION| * |===========================================| * |NETSS_POWER_LEVEL_ACTIVE | 0 | * |NETSS_POWER_LEVEL_RESERVED | 1 | * |NETSS_POWER_LEVEL_DISABLE | 2 | * |NETSS_POWER_LEVEL_DEEP_STANDBY| 3 | * |===========================================| */ if (new_state == NETSS_POWER_LEVEL_DEEP_STANDBY && g_current_netip_power_state != NETSS_POWER_LEVEL_DEEP_STANDBY) { /**We are going to deep standby lowest power level */ ret = netss_send_service_request_msg(level); /* This has to be done in the * netss_update_sysfs_attributes after * receiving the reply for all the service requests * 1. Call the platform driver api */ } else if (new_state == NETSS_POWER_LEVEL_ENABLE && g_current_netip_power_state == NETSS_POWER_LEVEL_DEEP_STANDBY) { /* * 1. Call the platform driver api */ puma_netip_notify_state(((level & NETSS_BIT_MASK_ON_BATTERY) ? 1 : 0)); if (g_is_netip_in_soft_off_state) { /* We are coming out of netip soft off * state to active */ ret = netss_send_service_request_msg(level); } else { pm_runtime_get(&pnetss_drv_data->dev->dev); } } else if (new_state > g_current_netip_power_state) { /* Going to lower power state Ex: Active to BBU, * Active to Standby BBU to standby */ ret = netss_send_service_request_msg(level); /* This has to be done in the * netss_update_sysfs_attributes after * receiving the reply for all the service requests * 1. Call the platform driver api */ } else if (new_state < g_current_netip_power_state) { /* Going to higher power state * Ex: BBU to Active or Standby to Active * 1. Call the platform driver api */ puma_netip_notify_state(((level & NETSS_BIT_MASK_ON_BATTERY) ? 1 : 0)); ret = netss_send_service_request_msg(level); } else { /* This is 'probably' changing the individual services * without changing netip level */ ret = netss_send_service_request_msg(level); } } else { g_service_level_initialized = true; /* If not even one service is ready, * netss_configure_and_setup_netip_services * will send the service request */ if (g_netip_services_status_ready) ret = netss_send_service_request_msg(level); } return ret; } /** Enable NetIP when on == true, otherwisedisable */ static int netss_modify_netip_cru_mod_state(bool on) { netss_dev_info_t netip_cru_mmio; int ret = 0; int count = 0; if(netss_device_get_info(NETSS_DEV_CLK_CTRL, &netip_cru_mmio)) { printk(KERN_ALERT "Couldn't get NetIP CRU MMIO info!\n"); ret = -ENOMEM; } else { void __iomem *netip_cru_base = ioremap_nocache(netip_cru_mmio.base, netip_cru_mmio.size); uint32_t reg_val; if (NULL != netip_cru_base) { reg_val = ioread32be(netip_cru_base + NETIP_CRU0_MOD_STATE_OFFSET); if (on) { reg_val |= NETIP_CRU0_ENABLE; } else { reg_val &= ~(NETIP_CRU0_ENABLE); } iowrite32be(reg_val, netip_cru_base + NETIP_CRU0_MOD_STATE_OFFSET); if (on) { reg_val = ioread32be(netip_cru_base + NETIP_CRU0_MOD_STATUS_OFFSET); while ((reg_val & NETIP_CRU0_ENABLE) != NETIP_CRU0_ENABLE) { msleep(5); reg_val = ioread32be(netip_cru_base + NETIP_CRU0_MOD_STATUS_OFFSET); if (count > 1000) { ret = -EBUSY; printk(KERN_WARNING "NetIP clock not enabled in 5 sec cru0 stat= %08X\n", reg_val); break; } count++; } } iounmap(netip_cru_base); } else { ret = -EFAULT; } } return ret; } /**Notify power state to all NetSS child drivers, return 0 on success, bitmask of * failure sub device ids on failure */ int netss_notify_netip_power_state_to_child_drivers(netss_power_state_t state) { int ret = 0; int i = 0, j = 0; for(i=0; ipower_state_change_cbinfo[i].func; void * args = pnetss_drv_data->power_state_change_cbinfo[i].args; if(func != NULL) { if(state == NETSS_NETIP_POWER_STATE_OFF) { j=0; /**While suspending notify the power state, if it fails * repeat 10 times sleeping 10msec before trying. */ while(j < 10) { /**Callback returns 0 on success, -1 on failure */ if( 0 != func(args, state)) { j++; ret |= (1<netip_msg.lock); /*Wake up thread, cleanup sysfs attributes */ g_netip_msg = 5; mutex_unlock(&pnetss_drv_data->netip_msg.lock); wake_up(&waitForNetIPMsgWQ); } EXPORT_SYMBOL(netss_wakeup_netip_manager_thread); /**Registers power state change handling callback * Return 0 on sucess, -1 on failing to register */ int netss_power_state_change_callback_register(netss_dev_t subdevice, netss_power_state_callback_info_t *cbinfo) { int ret = -1; /**Validate the parametrs */ if( (cbinfo == NULL) || (subdevice < 0) || (subdevice >= NETSS_DEV_MAX) ) { return ret; } /**Regiser NetIP power state change handler callback for the subdevice driver*/ if(cbinfo->func != NULL) { if(pnetss_drv_data->power_state_change_cbinfo[subdevice].func == NULL) { pnetss_drv_data->power_state_change_cbinfo[subdevice].func = cbinfo->func; pnetss_drv_data->power_state_change_cbinfo[subdevice].args = cbinfo->args; ret = 0; } else { printk(KERN_ERR "NetSS power state callback already registered for subdevice %d \n", subdevice); } } else { /**Unregistering the callback */ pnetss_drv_data->power_state_change_cbinfo[subdevice].func = cbinfo->func; pnetss_drv_data->power_state_change_cbinfo[subdevice].args = cbinfo->args; ret = 0; } return ret; } EXPORT_SYMBOL(netss_power_state_change_callback_register);