/* * setup_pm_dependency: * * Function sets up the dependency matrix by populating * the matrix with node depency information. * * Returns 0 on success. Appropriate error on failure. */ int setup_pm_dependency(void){ int err = 0; uint16_t i; uint16_t j; mic_ctx_t *mic_ctx; for (i = 0; i < mic_data.dd_numdevs; i++) { mic_ctx = get_per_dev_ctx(i); if (!mic_ctx) { PM_DEBUG("Failed to retrieve driver context\n"); return -EFAULT; } if (mic_ctx->micpm_ctx.idle_state == PM_IDLE_STATE_PC3_READY) { for (j = 0; j < mic_data.dd_numdevs; j++) { if (micscif_get_nodedep(mic_get_scifnode_id(mic_ctx),j+1) == DEP_STATE_DEPENDENT) { micscif_set_nodedep(mic_get_scifnode_id(mic_ctx),j+1, DEP_STATE_DISCONNECT_READY); } } } } return err; }
static int micvcons_write(struct tty_struct * tty, const unsigned char *buf, int count) { micvcons_port_t *port = (micvcons_port_t *)tty->driver_data; mic_ctx_t *mic_ctx = get_per_dev_ctx(tty->index); int bytes=0, status; struct vcons_buf *vcons_host_header; u8 card_alive = 1; spin_lock_bh(&port->dp_lock); vcons_host_header = (struct vcons_buf *)port->dp_vcons->dc_hdr_virt; if (vcons_host_header->mic_magic == MIC_VCONS_SLEEPING) { status = micvcons_resume(mic_ctx); if (status != 0) { /* If card can not wakeup, it is dead. */ card_alive = 0; goto exit; } } if (vcons_host_header->mic_magic != MIC_VCONS_READY) goto exit; bytes = micvcons_port_write(port, buf, count); if (bytes) { mic_send_hvc_intr(mic_ctx); extra_timeout = 0; } exit: spin_unlock_bh(&port->dp_lock); if (!card_alive) micvcons_del_timer_entry(port); return bytes; }
static int micvcons_open(struct tty_struct * tty, struct file * filp) { micvcons_port_t *port = &mic_data.dd_ports[tty->index]; int ret = 0; mic_ctx_t *mic_ctx = get_per_dev_ctx(tty->index); tty->driver_data = port; mutex_lock(&port->dp_mutex); spin_lock_bh(&port->dp_lock); if ((filp->f_flags & O_ACCMODE) != O_RDONLY) { if (port->dp_writer) { ret = -EBUSY; goto exit_locked; } port->dp_writer = filp; port->dp_bytes = 0; } if ((filp->f_flags & O_ACCMODE) != O_WRONLY) { if (port->dp_reader) { ret = -EBUSY; goto exit_locked; } port->dp_reader = filp; port->dp_canread = 1; } #if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) tty->low_latency = 0; #else tty->port->low_latency = 0; #endif if (!port->dp_tty) port->dp_tty = tty; if (!port->dp_vcons) port->dp_vcons = &mic_ctx->bi_vcons; if (tty->count == 1) { ret = micvcons_start(mic_ctx); if (ret != 0) goto exit_locked; spin_lock(&timer_list_lock); list_add_tail_rcu(&port->list_member, &timer_list_head); if (list_is_singular(&timer_list_head)) { restart_timer_flag = MICVCONS_TIMER_RESTART; mod_timer(&vcons_timer, jiffies + msecs_to_jiffies(MICVCONS_SHORT_TIMEOUT)); } spin_unlock(&timer_list_lock); } exit_locked: spin_unlock_bh(&port->dp_lock); mutex_unlock(&port->dp_mutex); return ret; }
void acptboot_getconn(struct work_struct *work) { mic_ctx_t *node_ctx; struct scif_portID data; scif_epd_t conn_epd; struct timespec tod; int proto; int version; int err; if ((err = scif_accept(acptboot_data->listen_epd, &data, &conn_epd, SCIF_ACCEPT_SYNC))) { pr_debug("ACPTBOOT: scif_accept_failed %d\n", err); return; //goto requeue_accept; } if (!data.node) { printk(KERN_ERR "ACPTBOOT: connect received from invalid dev %d\n", -EINVAL); goto close_epd; } if ((err = scif_recv(conn_epd, &version, sizeof(version), SCIF_RECV_BLOCK)) != sizeof(version)) { printk(KERN_ERR "ACPTBOOT: failed to recieve version number err %d\n", err); goto close_epd; } if ((err = scif_recv(conn_epd, &proto, sizeof(proto), SCIF_RECV_BLOCK)) != sizeof(proto)) { printk(KERN_ERR "ACPTBOOT: failed to recieve proto id %d\n", err); goto close_epd; } switch (proto) { case ACPT_BOOTED: node_ctx = get_per_dev_ctx(data.node - 1); mic_setstate(node_ctx, MIC_ONLINE); node_ctx->boot_count++; proto = ACPT_BOOT_ACK; scif_send(conn_epd, &proto, sizeof(proto), SCIF_SEND_BLOCK); break; case ACPT_REQUEST_TIME: getnstimeofday(&tod); proto = ACPT_TIME_DATA; scif_send(conn_epd, &proto, sizeof(proto), SCIF_SEND_BLOCK); scif_send(conn_epd, &tod, sizeof(tod), SCIF_SEND_BLOCK); break; } close_epd: if ((err = scif_close(conn_epd))) printk(KERN_ERR "ACPTBOOT: scif_close failed %d\n", err); //requeue_accept: queue_work(acptboot_data->acptbootwq, &acptboot_data->acptbootwork); }
/* * revert_idle_entry_trasaction: * * @node_disc_bitmask: Bitmask of nodes which were involved in the * transaction * * Function Reverts idle state changes made to nodes when an idle * state trasaction fails. */ int revert_idle_entry_trasaction(uint8_t *node_disc_bitmask) { int err = 0; mic_ctx_t *node_ctx; uint32_t node_id = 0; for(node_id = 0; node_id <= ms_info.mi_maxid; node_id++) { if (node_id == SCIF_HOST_NODE) continue; if (!get_nodemask_bit(node_disc_bitmask, node_id)) continue; node_ctx = get_per_dev_ctx(node_id - 1); if (!node_ctx) { PM_DEBUG("Failed to retrieve node context."); err = -EINVAL; goto exit; } if (node_ctx->micpm_ctx.idle_state == PM_IDLE_STATE_PC3) { err = pm_pc3_exit(node_ctx); if (err) { PM_DEBUG("Wakeup of Node %d failed. Node is lost" " and is to be disconnected",node_id); node_ctx->micpm_ctx.idle_state = PM_IDLE_STATE_LOST; /* Since node is lost, ref_cnt increment(decement) through the * pm_get(put)_reference interface is prevented by idle_state. * We still need to ensure the ref_cnt iself is reset * back to 0 so that pm_get(put)_reference will work after the * lost node interface recovers the node. */ atomic_set(&node_ctx->micpm_ctx.pm_ref_cnt, 0); } } } exit: return err; }
/* pm_node_disconnect * * Called during idlestate entry. * * Function checks the pm_ref_cnt and returns ACK * or NACK depending on the pm_ref_cnt value. */ int pm_node_disconnect(uint8_t *nodemask) { uint32_t node_id; mic_ctx_t *mic_ctx; int ret = 0; int err = 0; for (node_id = 0; node_id <= ms_info.mi_maxid; node_id++) { if (node_id == SCIF_HOST_NODE) continue; if (!get_nodemask_bit(nodemask, node_id)) continue; mic_ctx = get_per_dev_ctx(node_id - 1); if (!mic_ctx) { set_nodemask_bit(nodemask, node_id, 0); return -EAGAIN; } if (mic_ctx->state != MIC_ONLINE) { set_nodemask_bit(nodemask, node_id, 0); return -EAGAIN; } ret = atomic_cmpxchg(&mic_ctx->micpm_ctx.pm_ref_cnt, 0, PM_NODE_IDLE); if (((ret != 0) && (ret != PM_NODE_IDLE)) || atomic_read(&mic_data.dd_pm.wakeup_in_progress)) { set_nodemask_bit(nodemask, node_id, 0); return -EAGAIN; } } return err; }
static PM_MMIO_REGVALUE_GET(svidctrl, SBOX_SVID_CONTROL); static PM_MMIO_REGVALUE_GET(pcuctrl, SBOX_PCU_CONTROL); static PM_MMIO_REGVALUE_GET(hoststate,SBOX_HOST_PMSTATE); static PM_MMIO_REGVALUE_GET(cardstate, SBOX_UOS_PMSTATE); static PM_MMIO_REGVALUE_GET(wtimer, SBOX_C3WAKEUP_TIMER); static PM_MMIO_REGVALUE_GET(gpmctrl, GBOX_PM_CTRL); static PM_MMIO_REGVALUE_GET(core_volt, SBOX_COREVOLT); static PM_MMIO_REGVALUE_GET(uos_pcuctrl, SBOX_UOS_PCUCONTROL); static int depgraph_j2i_show(struct seq_file *s, void *pos) { uint64_t bid = (uint64_t)s->private; mic_ctx_t *mic_ctx; int i, j; mic_ctx = get_per_dev_ctx(bid); if (!mic_ctx) { return -EINVAL; } seq_printf(s,"=================================================================\n"); seq_printf(s,"%-10s |%-25s\n", "Scif Node" , "dependent nodes"); seq_printf(s,"=================================================================\n"); for ( i = 0; i <= ms_info.mi_maxid; i++) { seq_printf(s, "%-10d |", i); for (j = 0; j <= ms_info.mi_maxid; j++) { switch(ms_info.mi_depmtrx[j][i]) { case DEP_STATE_DEPENDENT: { /* (A) - active dependency on node i */
/* do_idlestate_exit: * * Initiate idle state exits for nodes specified * by the bitmask. * * mic_ctx: The device context. * get_ref: Set to true if the entity that wants to wake * a node up also wantes to get a reference to the node. * * Returs 0 on success. Appropriate error on failure. * */ int do_idlestate_exit(mic_ctx_t *mic_ctx, bool get_ref) { int err = 0; uint32_t node_id = 0; mic_ctx_t *node_ctx; uint8_t *nodemask_buf; if(!mic_ctx) return -EINVAL; might_sleep(); /* If the idle_state_mutex is already obtained by another thread * try to wakeup the thread which MAY be waiting for REMOVE_NODE * responses. This way, we give priority to idle state exits than * idle state entries. */ if (!mutex_trylock(&mic_data.dd_pm.pm_idle_mutex)) { atomic_inc(&mic_data.dd_pm.wakeup_in_progress); wake_up(&ms_info.mi_disconn_wq); mutex_lock(&mic_data.dd_pm.pm_idle_mutex); atomic_dec(&mic_data.dd_pm.wakeup_in_progress); } nodemask_buf = (uint8_t *)kzalloc(mic_ctx->micpm_ctx.nodemask.len, GFP_KERNEL); if(!nodemask_buf) { PM_DEBUG("Error allocating nodemask buffer\n"); mutex_unlock(&mic_data.dd_pm.pm_idle_mutex); err = ENOMEM; goto abort_node_wake; } if ((err = micscif_get_activeset(mic_get_scifnode_id(mic_ctx), nodemask_buf))) { PM_DEBUG("Node connect failed during Activation set calculation for node\n"); mutex_unlock(&mic_data.dd_pm.pm_idle_mutex); err = -EINVAL; goto free_buf; } print_nodemaskbuf(nodemask_buf); for(node_id = 0; node_id <= ms_info.mi_maxid; node_id++) { if (node_id == SCIF_HOST_NODE) continue; if (!get_nodemask_bit(nodemask_buf, node_id)) continue; node_ctx = get_per_dev_ctx(node_id - 1); if (!node_ctx) { mutex_unlock(&mic_data.dd_pm.pm_idle_mutex); goto free_buf; } switch (node_ctx->micpm_ctx.idle_state) { case PM_IDLE_STATE_PC3: case PM_IDLE_STATE_PC3_READY: if ((err = pm_pc3_exit(node_ctx))) { PM_DEBUG("Wakeup of Node %d failed." "Node to be disconnected",node_id); set_nodemask_bit(nodemask_buf, node_id, 0); node_ctx->micpm_ctx.idle_state = PM_IDLE_STATE_LOST; /* Since node is lost, ref_cnt increment(decement) through the * pm_get(put)_reference interface is prevented by idle_state. * We still need to ensure the ref_cnt iself is reset * back to 0 so that pm_get(put)_reference will work after the * lost node interface recovers the node. */ atomic_set(&node_ctx->micpm_ctx.pm_ref_cnt, 0); } else { if ((mic_ctx == node_ctx) && get_ref) if (atomic_cmpxchg(&mic_ctx->micpm_ctx.pm_ref_cnt, PM_NODE_IDLE, 1) != PM_NODE_IDLE) atomic_inc(&mic_ctx->micpm_ctx.pm_ref_cnt); } break; case PM_IDLE_STATE_PC6: if ((err = pm_pc6_exit(node_ctx))) { PM_DEBUG("Wakeup of Node %d failed." "Node to be disconnected",node_id); set_nodemask_bit(nodemask_buf, node_id, 0); node_ctx->micpm_ctx.idle_state = PM_IDLE_STATE_LOST; /* Since node is lost, ref_cnt increment(decement) through the * pm_get(put)_reference interface is prevented by idle_state. * We still need to ensure the ref_cnt iself is reset * back to 0 so that pm_get(put)_reference will work after the * lost node interface recovers the node. */ atomic_set(&node_ctx->micpm_ctx.pm_ref_cnt, 0); } else { if ((mic_ctx == node_ctx) && get_ref) if (atomic_cmpxchg(&mic_ctx->micpm_ctx.pm_ref_cnt, PM_NODE_IDLE, 1) != PM_NODE_IDLE) atomic_inc(&mic_ctx->micpm_ctx.pm_ref_cnt); } break; case PM_IDLE_STATE_PC0: PM_DEBUG("Node %d is in state %d " "and already out of package state.\n",node_id, node_ctx->micpm_ctx.idle_state); if ((mic_ctx == node_ctx) && get_ref) if (atomic_cmpxchg(&mic_ctx->micpm_ctx.pm_ref_cnt, PM_NODE_IDLE, 1) != PM_NODE_IDLE) atomic_inc(&mic_ctx->micpm_ctx.pm_ref_cnt); break; default: PM_DEBUG("Invalid idle state of node %d." " State = %d \n", node_id, node_ctx->micpm_ctx.idle_state); mutex_unlock(&mic_data.dd_pm.pm_idle_mutex); err = -ENODEV; goto free_buf; } } /* Idle state exit of nodes are complete. * Set the register state now for those nodes * that are successfully up. */ for(node_id = 0; node_id <= ms_info.mi_maxid; node_id++) { if (node_id == SCIF_HOST_NODE) continue; if (!get_nodemask_bit(nodemask_buf, node_id)) continue; node_ctx = get_per_dev_ctx(node_id - 1); if (!node_ctx) { PM_DEBUG("Failed to retrieve node context."); continue; } if (node_ctx->micpm_ctx.idle_state == PM_IDLE_STATE_PC0) set_host_state(node_ctx, PM_IDLE_STATE_PC0); } mutex_unlock(&mic_data.dd_pm.pm_idle_mutex); free_buf: kfree(nodemask_buf); abort_node_wake: return err; }
/* * do_idlestate_entry: * * Function to start the idle state entry transaction for a node. Puts a node * and all the nodes that are dependent on this node to idle state if * it is possible. * * mic_ctx: The device context of node that needs to be put in idle state * Returs 0 in success. Appropriate error code on failure */ int do_idlestate_entry(mic_ctx_t *mic_ctx) { int err = 0; uint32_t node_id = 0; mic_ctx_t *node_ctx; uint8_t *nodemask_buf; if(!mic_ctx) return -EINVAL; mutex_lock(&mic_data.dd_pm.pm_idle_mutex); if ((err = setup_pm_dependency())) { PM_DEBUG("Failed to set up PM specific dependencies"); goto unlock; } nodemask_buf = (uint8_t *) kzalloc(mic_ctx->micpm_ctx.nodemask.len, GFP_KERNEL); if(!nodemask_buf) { PM_DEBUG("Error allocating nodemask buffer\n"); err = ENOMEM; goto dep_teardown; } err = micscif_get_deactiveset(mic_get_scifnode_id(mic_ctx), nodemask_buf, 1); if (err) { PM_DEBUG("Node disconnection failed " "during deactivation set calculation"); goto free_buf; } print_nodemaskbuf(nodemask_buf); if ((err = micscif_disconnect_node(mic_get_scifnode_id(mic_ctx), nodemask_buf, DISCONN_TYPE_POWER_MGMT))) { PM_DEBUG("SCIF Node disconnect failed. err: %d", err); goto free_buf; } if ((err = pm_node_disconnect(nodemask_buf))) { PM_DEBUG("PM Node disconnect failed. err = %d\n", err); goto free_buf; } if ((err = micvcons_pm_disconnect_node(nodemask_buf, DISCONN_TYPE_POWER_MGMT))) { PM_DEBUG("VCONS Node disconnect failed. err = %d\n", err); goto free_buf; } for(node_id = 0; node_id <= ms_info.mi_maxid; node_id++) { if (node_id == SCIF_HOST_NODE) continue; if (!get_nodemask_bit(nodemask_buf, node_id)) continue; node_ctx = get_per_dev_ctx(node_id - 1); if (!node_ctx) { PM_DEBUG("Failed to retrieve node context."); err = -EINVAL; goto revert; } if (node_ctx->micpm_ctx.idle_state == PM_IDLE_STATE_PC3_READY) { set_host_state(node_ctx, PM_IDLE_STATE_PC3); node_ctx->micpm_ctx.idle_state = PM_IDLE_STATE_PC3; PM_DEBUG("Node %d entered PC3\n", mic_get_scifnode_id(node_ctx)); } else { PM_DEBUG("Invalid idle state \n"); err = -EINVAL; goto revert; } } revert: if (err) revert_idle_entry_trasaction(nodemask_buf); free_buf: kfree(nodemask_buf); dep_teardown: teardown_pm_dependency(); unlock: if (err && (mic_ctx->micpm_ctx.idle_state != PM_IDLE_STATE_PC0)) pm_pc3_exit(mic_ctx); mutex_unlock(&mic_data.dd_pm.pm_idle_mutex); return err; }
/* DESCRIPTION:: Gets the opcode from the input buffer and call appropriate method PARAMETERS:: [in]mic_ctx_t *mic_ctx - pointer to the mic private context [in]void *in_buffer - input buffer containing opcode + ioctl arguments, RETURN_VALUE:: 0 if successful, non-zero if failure */ int adapter_do_ioctl(struct file *f, uint32_t cmd, uint64_t arg) { int status = 0; mic_ctx_t *mic_ctx = NULL; void __user *argp = (void __user *)arg; switch (cmd) { case IOCTL_FLASHCMD: { struct ctrlioctl_flashcmd args = {0}; if (copy_from_user(&args, argp, sizeof(struct ctrlioctl_flashcmd))) { return -EFAULT; } if (args.brdnum >= (uint32_t)mic_data.dd_numdevs) { printk(KERN_ERR "IOCTL error: given board num is invalid\n"); return -EINVAL; } mic_ctx = get_per_dev_ctx(args.brdnum); if (!mic_ctx) { printk(KERN_ERR "IOCTL error: null mic context\n"); return -ENODEV; } /* Make sure we are running in flash mode */ if (mic_ctx->mode != MODE_FLASH || mic_ctx->state != MIC_ONLINE) { printk(KERN_ERR "%s Card is not online in flash mode or online state\n", __func__); return -EPERM; } if (mic_ctx->bi_family != FAMILY_KNC) { printk(KERN_ERR "%s IOCTL_FLASHCMD not supported for non KNC family cards\n", __func__); return -EPERM; } status = do_send_flash_cmd(mic_ctx, &args); if (status) { printk(KERN_ERR "IOCTL error: failed to complete IOCTL for bdnum %d\n", args.brdnum); return status; } if (copy_to_user(argp, &args, sizeof(struct ctrlioctl_flashcmd))) { return -EFAULT; } break; } case IOCTL_CARDMEMCPY: { struct ctrlioctl_cardmemcpy args = {0}; if (copy_from_user(&args, argp, sizeof(struct ctrlioctl_cardmemcpy))) { return -EFAULT; } if (args.brdnum >= (uint32_t)mic_data.dd_numdevs) { printk(KERN_ERR "IOCTL error: given board num is invalid\n"); return -EINVAL; } mic_ctx = get_per_dev_ctx(args.brdnum); if (!mic_ctx) { printk(KERN_ERR "IOCTL error: null mic context\n"); return -EFAULT; } if(mic_ctx->state != MIC_ONLINE || mic_ctx->mode != MODE_LINUX) { status = -EFAULT; printk("Error ! Card not in linux mode or online state!\n"); return status; } status = get_card_mem(mic_ctx, &args); if (status) { printk(KERN_ERR "IOCTL error: failed to complete IOCTL for bdnum %d\n", args.brdnum); return status; } break; } default: return micmem_ioctl(f, cmd, arg); } return status; }
static int __init mic_init(void) { int ret, i; adapter_init(); unaligned_cache = micscif_kmem_cache_create(); if (!unaligned_cache) { ret = -ENOMEM; goto init_free_ports; } mic_lindata.dd_pcidriver.name = "mic"; mic_lindata.dd_pcidriver.id_table = mic_pci_tbl; mic_lindata.dd_pcidriver.probe = mic_probe; mic_lindata.dd_pcidriver.remove = mic_remove; mic_lindata.dd_pcidriver.driver.pm = &pci_dev_pm_ops; mic_lindata.dd_pcidriver.shutdown = mic_shutdown; if ((ret = alloc_chrdev_region(&mic_lindata.dd_dev, 0, MAX_DLDR_MINORS, "mic") != 0)) { printk("Error allocating device nodes: %d\n", ret); goto init_free_ports; } cdev_init(&mic_lindata.dd_cdev, &mic_fops); mic_lindata.dd_cdev.owner = THIS_MODULE; mic_lindata.dd_cdev.ops = &mic_fops; if ((ret = cdev_add(&mic_lindata.dd_cdev, mic_lindata.dd_dev, MAX_DLDR_MINORS) != 0)) { kobject_put(&mic_lindata.dd_cdev.kobj); goto init_free_region; } mic_lindata.dd_class = class_create(THIS_MODULE, "mic"); if (IS_ERR(mic_lindata.dd_class)) { printk("MICDLDR: Error createing mic class\n"); cdev_del(&mic_lindata.dd_cdev); ret = PTR_ERR(mic_lindata.dd_class); goto init_free_region; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31) mic_lindata.dd_class->devnode = mic_devnode; #endif mic_lindata.dd_hostdev = device_create(mic_lindata.dd_class, NULL, mic_lindata.dd_dev, NULL, "ctrl"); mic_lindata.dd_scifdev = device_create(mic_lindata.dd_class, NULL, mic_lindata.dd_dev + 1, NULL, "scif"); ret = sysfs_create_group(&mic_lindata.dd_hostdev->kobj, &host_attr_group); ret = sysfs_create_group(&mic_lindata.dd_scifdev->kobj, &scif_attr_group); #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,31) mic_lindata.dd_class->devnode = NULL; #endif if (micveth_init(mic_lindata.dd_hostdev)) printk(KERN_ERR "%s: micveth_init failed\n", __func__); ret = pci_register_driver(&mic_lindata.dd_pcidriver); if (ret) { micscif_destroy(); printk("mic: failed to register pci driver %d\n", ret); goto clean_unregister; } if (!mic_data.dd_numdevs) { printk("mic: No MIC boards present. SCIF available in loopback mode\n"); } else { printk("mic: number of devices detected %d \n", mic_data.dd_numdevs); } for (i = 0; i < mic_data.dd_numdevs; i++) { mic_ctx_t *mic_ctx = get_per_dev_ctx(i); wait_event(mic_ctx->ioremapwq, mic_ctx->aper.va || mic_ctx->state == MIC_RESETFAIL); destroy_workqueue(mic_ctx->ioremapworkq); } micveth_init_legacy(mic_data.dd_numdevs, mic_lindata.dd_hostdev); ret = acptboot_init(); #ifdef USE_VCONSOLE micvcons_create(mic_data.dd_numdevs); #endif /* Initialize Data structures for PM Disconnect */ ret = micpm_disconn_init(mic_data.dd_numdevs + 1); if (ret) printk(KERN_ERR "%s: Failed to initialize PM disconnect" " data structures. PM may not work as expected." " ret = %d\n", __func__, ret); register_pm_notifier(&mic_pm_notifer); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,34)) ret = pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, "mic", mic_pm_qos_cpu_dma_lat); if (ret) { printk(KERN_ERR "%s %d mic_pm_qos_cpu_dma_lat %d ret %d\n", __func__, __LINE__, mic_pm_qos_cpu_dma_lat, ret); ret = 0; /* Dont fail driver load due to PM QoS API. Fall through */ } #endif return 0; clean_unregister: device_destroy(mic_lindata.dd_class, mic_lindata.dd_dev + 1); device_destroy(mic_lindata.dd_class, mic_lindata.dd_dev); class_destroy(mic_lindata.dd_class); cdev_del(&mic_lindata.dd_cdev); unregister_pm_notifier(&mic_pm_notifer); init_free_region: unregister_chrdev_region(mic_lindata.dd_dev, MAX_DLDR_MINORS); init_free_ports: micpm_uninit(); return ret; }