/*! * @brief Validates that the PPD is a supported device * * The event handler for this state will be called after a delay to ensure that * the output of the USB transceiver has stabilized. If the D+ line is low, the * accessory is a valid phone-powered accessory, so the state is changed to the * Connecting State: PPD validate state to identify the accessory. If the D+ line * is high, the accessory is invalid, so the state is changed to the Connected * State indicating the invalid accessory. As a separate verification step, the * event handler will also verify that the accessory still appears to be a * phone-powered accessory (as opposed to a self-powered accessory). If the * accessory does not appear to be a phone-powered accessory, the state is reset * to the Connecting: debounce_dev_type state to attempt to re-debounce the * insertion of the accessory. * * @param polling_interval a return parameter that holds the "polling rate" * * @return next_state tells the state machine what the next EMU state is */ EMU_STATE_T ppd_validate_handler(int *polling_interval) { if (get_device_type() != EMU_DEV_TYPE_PPD) { tracemsg(_k_d("EMU: ppd_validate_handler: device_type changed! next_state = EMU_STATE_CONNECTING__DEBOUNCE_DEV_TYPE")); /* Something changed, debounce the device type again */ *polling_interval = EMU_POLL_CONTINUE; return EMU_STATE_CONNECTING__DEBOUNCE_DEV_TYPE; } if (!get_bus_state(EMU_BUS_SIGNAL_DPLUS)) { tracemsg(_k_d("EMU: ppd_validate_handler: D+ is low, next_state = EMU_STATE_CONNECTING__PPD_IDENTIFY")); /* If D+ is low and the device type has not changed go on to the next layer of PPD detection */ *polling_interval = EMU_POLL_CONTINUE; return EMU_STATE_CONNECTING__PRE_PPD_IDENTIFY; } tracemsg(_k_d("EMU: ppd_validate_handler : D+ is high, current_device = MOTO_ACCY_TYPE_INVALID, next_state = EMU_STATE_CONNECTED__INVALID")); /* D+ is not low at this point so the device is invalid */ emu_current_device = MOTO_ACCY_TYPE_INVALID; *polling_interval = EMU_POLL_CONTINUE; return EMU_STATE_CONNECTED__INVALID; }
/*! * @brief ioctl() handler for the charger control interface. * * This function is the ioctl() interface handler for all charger control operations. It is * not called directly through an ioctl() call on the power IC device, but is executed * from the core ioctl handler for all ioctl requests in the range for charger operations. * * @param cmd ioctl() request received. * @param arg additional information about request, specific to each request. * * @return 0 if successful. */ int charger_ioctl(unsigned int cmd, unsigned long arg) { int error; int temp; tracemsg(_k_d("Charger control ioctl(), request 0x%X (cmd 0x%X)"),(int) cmd, _IOC_NR(cmd)); /* Handle the request. Note that no charger control operations require a structure to be * passed - all operations take a single parameter passed by value. */ switch(cmd) { case POWER_IC_IOCTL_CHARGER_SET_CHARGE_VOLTAGE: return(power_ic_charger_set_charge_voltage((int)arg)); break; case POWER_IC_IOCTL_CHARGER_SET_CHARGE_CURRENT: return(power_ic_charger_set_charge_current((int)arg)); break; case POWER_IC_IOCTL_CHARGER_SET_TRICKLE_CURRENT: return(power_ic_charger_set_trickle_current((int)arg)); break; case POWER_IC_IOCTL_CHARGER_SET_POWER_PATH: return(power_ic_charger_set_power_path((int)arg)); break; case POWER_IC_IOCTL_CHARGER_GET_OVERVOLTAGE: /* Read the state of overvoltage from the hardware. */ if((error = power_ic_charger_get_overvoltage(&temp))) { return error; } /* Return the read state back to the caller. */ if(put_user(temp, (int *)arg)) { return -EFAULT; } break; case POWER_IC_IOCTL_CHARGER_RESET_OVERVOLTAGE: return(power_ic_charger_reset_overvoltage()); break; default: /* This shouldn't be able to happen, but just in case... */ tracemsg(_k_d("=> 0x%X unsupported charger ioctl command"), (int) cmd); return -ENOTTY; break; } return 0; }
/*! * This function is the ioctl() interface handler for all peripheral operations. It is not called * directly through an ioctl() call on the power IC device, but is executed from the core ioctl * handler for all ioctl requests in the range for peripherals. * * @param cmd the ioctl() command * @param arg the ioctl() argument * * @return This function returns 0 if successful. */ int periph_ioctl(unsigned int cmd, unsigned long arg) { int retval = 0; int data = (int) arg; /* Get the actual command from the ioctl request. */ unsigned int cmd_num = _IOC_NR(cmd); tracemsg(_k_d("peripheral ioctl(), request 0x%X (cmd %d)"),(int) cmd, (int)cmd_num); /* Handle the request. */ switch(cmd) { case POWER_IC_IOCTL_PERIPH_SET_BLUETOOTH_ON: retval = power_ic_periph_set_bluetooth_on(data); break; case POWER_IC_IOCTL_PERIPH_SET_FLASH_CARD_ON: retval = power_ic_periph_set_flash_card_on(data); break; case POWER_IC_IOCTL_PERIPH_SET_VIBRATOR_LEVEL: retval = power_ic_periph_set_vibrator_level(data); break; case POWER_IC_IOCTL_PERIPH_SET_VIBRATOR_ON: retval = power_ic_periph_set_vibrator_on(data); break; case POWER_IC_IOCTL_PERIPH_SET_WLAN_ON: retval = power_ic_periph_set_wlan_on(data); break; case POWER_IC_IOCTL_PERIPH_SET_WLAN_LOW_POWER_STATE_ON: retval = power_ic_periph_set_wlan_low_power_state_on(); break; case POWER_IC_IOCTL_PERIPH_SET_CAMERA_ON: retval = power_ic_periph_set_camera_on(data); break; default: /* This shouldn't be able to happen, but just in case... */ tracemsg(_k_d("=> 0x%X unsupported peripheral ioctl command"), (int) cmd); retval = -EINVAL; break; } return retval; }
/*! * @brief Used to determine what type of PPD this is. * * The event handler function reads the state of the D+ and D- lines to determine * what type of accessory is connected. The state is then changed to the * Connected state along with an indication of the identification of the accessory. * * @param polling_interval a return parameter that holds the "polling rate" * * @return next_state tells the state machine what the next EMU state is */ EMU_STATE_T ppd_identify_handler(int *polling_interval) { *polling_interval = EMU_POLL_CONTINUE; switch (get_bus_state(EMU_BUS_SIGNAL_DP_DM)) { case EMU_BUS_SIGNAL_STATE_DP_DM_10: emu_current_device = MOTO_ACCY_TYPE_HEADSET_EMU_MONO; return EMU_STATE_CONNECTED__HEADSET; break; case EMU_BUS_SIGNAL_STATE_DP_DM_00: tracemsg(_k_d("The headset is detected as EMU ST headset")); emu_current_device = MOTO_ACCY_TYPE_HEADSET_EMU_STEREO; return EMU_STATE_CONNECTED__HEADSET; break; case EMU_BUS_SIGNAL_STATE_DP_DM_01: case EMU_BUS_SIGNAL_STATE_DP_DM_11: default: emu_current_device = MOTO_ACCY_TYPE_NOT_SUPPORTED; return EMU_STATE_CONNECTED__NOT_SUPPORTED; break; } }
/*! * @brief Event handler for the power IC VBUS, SE1, and ID interrupts on SCMA11-Bute platforms * * This function is the event handler for the power IC VBUS,SE1 and ID interrupts. * When the events occur, the job of the interrupt handler is to ensure that * the event is reported to the User Space using the poll system call. * This is accomplished through the following mechanism: * * - Update the power_ic_event_int variable containing the type of events that * occur before the User Space read it. * * - Wake up the power_ic_poll function by updating the power_ic_event_int and * waking up the power_ic_event_int_wait_queue wait queue. * * @param event parameter indicating which even occurred * * @return 1, indicating that the event has been handled */ static int emu_glue_int_handler(POWER_IC_EVENT_T event) { tracemsg(_k_d("EMU: emu_glue_int_handler: interrupt received for 0x%x event"),event); /* take mutex */ down(&emu_proc_event_int_mutex); /* Update power_ic_event_int variable */ switch (event) { case POWER_IC_EVENT_ATLAS_USBI: emu_proc_event_int |= POWER_IC_EVENT_INT_VBUS; break; case POWER_IC_EVENT_ATLAS_IDI: emu_proc_event_int |= POWER_IC_EVENT_INT_ID; break; case POWER_IC_EVENT_ATLAS_SE1I: emu_proc_event_int |= POWER_IC_EVENT_INT_SE1; break; default: break; } /* Release mutex and wake up poll function */ emu_proc_event_int_flag = true; up(&emu_proc_event_int_mutex); wake_up_interruptible(&emu_proc_event_int_wait_queue); return 1; }
/*! * @brief Enables power to the PPD accessory, then wait for the VBUS line to settle * * The purpose of this state is to identify the type of phone-powered accessory * attached to the phone. This identification is performed using the following * steps: * 1.Enable power to the accessory * 2.Wait 200 milliseconds. * 3.Read the state of the D+ and D- lines. * The state of the D+ and D- lines indicates the type of accessory that is * connected based on the following table: * * D+ D- Accessory * 0 0 Unknown * 0 1 Unknown * 1 0 EMU mono headset * 1 1 Unknown * Table 4: Phone-powered accessory identification * * The entry function for the state will enable the power to the accessory and * configure the timer to expire after 200 milliseconds. This will cause the * event handler function to be called after this delay to check the state of the * D+ and D- lines. * * @param prev_state the previous EMU state * @param polling_interval a return parameter that holds the "polling rate" */ void pre_ppd_identify_enter(EMU_STATE_T prev_state, int *polling_interval) { tracemsg(_k_d("EMU: pre_ppd_identify_enter: EMU_DEV_TYPE_PPD")); /* Power VBUS */ EMU_SET_REVERSE_MODE(ENABLE); /* Set timer to wait for D+ and D- lines to stabilize */ *polling_interval = EMU_POLL_REV_MODE_DELAY; }
/*! * @brief This utility function sets the audio mode in Atlas and stereo emu headset * pull up. * * @param mode - The headset mode * */ void emu_util_set_emu_headset_mode(MOTO_ACCY_HEADSET_MODE_T mode) { switch(mode) { case MOTO_ACCY_HEADSET_MODE_NONE: EMU_SET_REVERSE_MODE(false); /* Don't allow power management to suspend the phone while waiting for VBUS to settle. */ power_ic_pm_suspend_mask_tbl[POWER_IC_PM_MISC_SLEEPS] |= POWER_IC_EMU_REV_MODE_SLEEP; /* mdelay must be used here because the delay cannot be allowed to be longer than 20 ms. */ mdelay(12); power_ic_pm_suspend_mask_tbl[POWER_IC_PM_MISC_SLEEPS] &= ~(POWER_IC_EMU_REV_MODE_SLEEP); EMU_SET_EMU_CONN_MODE(POWER_IC_EMU_CONN_MODE_USB); EMU_SET_HEADSET_PULL_UP(0); /*Following is the work around for the emu headset send/end key*/ EMU_SET_HS_SEND_END_REGS(1); EMU_SET_VUSB_INPUT_SOURCE(EMU_VREG_IN_VINBUS); break; case MOTO_ACCY_HEADSET_MODE_MONO: EMU_SET_VUSB_INPUT_SOURCE(EMU_VREG_IN_BPLUS); EMU_SET_HS_SEND_END_REGS(0); EMU_SET_EMU_CONN_MODE(POWER_IC_EMU_CONN_MODE_MONO_AUDIO); EMU_SET_HEADSET_PULL_UP(0); /* Don't allow power management to suspend the phone while waiting for D- to settle */ power_ic_pm_suspend_mask_tbl[POWER_IC_PM_MISC_SLEEPS] |= POWER_IC_EMU_DMINUS_MONO_SLEEP; msleep(10); power_ic_pm_suspend_mask_tbl[POWER_IC_PM_MISC_SLEEPS] &= ~(POWER_IC_EMU_DMINUS_MONO_SLEEP); EMU_SET_REVERSE_MODE(true); break; case MOTO_ACCY_HEADSET_MODE_STEREO: EMU_SET_VUSB_INPUT_SOURCE(EMU_VREG_IN_BPLUS); EMU_SET_HS_SEND_END_REGS(0); EMU_SET_EMU_CONN_MODE(POWER_IC_EMU_CONN_MODE_STEREO_AUDIO); EMU_SET_HEADSET_PULL_UP(1); /* Don't allow power management to suspend the phone while waiting for D- to settle */ power_ic_pm_suspend_mask_tbl[POWER_IC_PM_MISC_SLEEPS] |= POWER_IC_EMU_DMINUS_STEREO_SLEEP; msleep(10); power_ic_pm_suspend_mask_tbl[POWER_IC_PM_MISC_SLEEPS] &= ~(POWER_IC_EMU_DMINUS_STEREO_SLEEP); EMU_SET_REVERSE_MODE(true); break; default: tracemsg(_k_d("EMU: Audio Mode %d not supported"), mode); } }
/*! * @brief Initializes the EMU Glue utils * * The function performs the initialization of the EMU glue utils variables and register handler * to the Power IC EMU related interrupts */ int __init emu_glue_utils_init(void) { struct proc_dir_entry * emu_proc; tracemsg(_k_d("EMU: emu_glue_utils_init: initializing kernel EMU glue utils")); /* configure D+/D- lines and cradle detect line */ power_ic_gpio_emu_config(); /* Create emu proc entry */ emu_proc = create_proc_entry("emu", S_IRUGO | S_IWUGO, NULL); if (emu_proc == NULL) { tracemsg(_k_d(KERN_ERR "Unable to create EMU proc entry in /proc.\n")); return -ENOMEM; } /* Set the proc fops */ emu_proc->proc_fops = (struct file_operations *)&emu_proc_fops; /* Initialize variables */ emu_proc_event_int = 0; emu_proc_event_int_flag = false; power_ic_emu_hw_locked = false; emu_proc_opened = false; audio_config.headset_mode = MOTO_ACCY_HEADSET_MODE_NONE; audio_config.conn_mode = POWER_IC_EMU_CONN_MODE_USB; audio_config.id_pull_down = 0; /* register the light usb driver int */ power_ic_event_subscribe(EMU_INT_VBUS,usb_detection_int_handler); tracemsg(_k_d("USB DET: Start light driver thread")); /* Create USB detection thread */ kernel_thread(usb_detection_state_machine_thread, NULL, 0); /* Init ok */ return 0; }
int rtc_ioctl(unsigned int cmd, unsigned long arg) { struct timeval power_ic_time; struct timeval * usr_spc_time_val = (struct timeval *)arg; int err = 0; /* Handle the request. */ switch(cmd) { case POWER_IC_IOCTL_GET_TIME: /* Read the TOD and DAY registers and set the data in the timeval structure to the format the Linux uses.*/ err = power_ic_rtc_get_time(&power_ic_time); if(copy_to_user(usr_spc_time_val, &power_ic_time, sizeof(power_ic_time))) { err = -EFAULT; } break; case POWER_IC_IOCTL_GET_ALARM_TIME: /* Read the TODA and DAYA registers and set the data in the timeval structure to the format the Linux uses.*/ err = power_ic_rtc_get_time_alarm(&power_ic_time); if(copy_to_user(usr_spc_time_val, &power_ic_time, sizeof(power_ic_time))) { err = -EFAULT; } break; case POWER_IC_IOCTL_SET_TIME: /* Write to the TOD and DAY registers based on the data in the timeval struct */ if(copy_from_user(&power_ic_time,usr_spc_time_val, sizeof(power_ic_time))) { err = -EFAULT; } err = power_ic_rtc_set_time(&power_ic_time); break; case POWER_IC_IOCTL_SET_ALARM_TIME: /* Write to the TODA and DAYA registers based on the data in the timeval struct */ if (copy_from_user(&power_ic_time,usr_spc_time_val,sizeof(power_ic_time))) { err = -EFAULT; } err = power_ic_rtc_set_time_alarm(&power_ic_time); break; default: /* This shouldn't be able to happen, but just in case... */ tracemsg(_k_d("0x%X unsupported ioctl command"), (int) cmd); err = -ENOTTY; break; } return err; }
static int emu_proc_open(struct inode *inode, struct file *file) { /* take mutex */ if(down_interruptible(&emu_proc_opened_mutex)) { tracemsg(_k_d("process received signal while waiting for power ic opened mutex. Exiting.")); return -EINTR; } /* Check if the proc is not already open */ if (emu_proc_opened == true) { tracemsg(_k_d("/proc/emu control already opened.\n")); /* Release mutex */ up(&emu_proc_opened_mutex); return -EBUSY; } /* Set the opened state */ emu_proc_opened = true; /* MASK VBUS INT */ power_ic_event_mask(EMU_INT_VBUS); /* Unsubsribe usb detection light driver handler on VBUS */ power_ic_event_unsubscribe(EMU_INT_VBUS,usb_detection_int_handler); /* Subscribe to the EMU interrupt events */ power_ic_event_subscribe(EMU_INT_VBUS, emu_glue_int_handler); power_ic_event_subscribe(EMU_INT_ID, emu_glue_int_handler); power_ic_event_subscribe(EMU_INT_SE1, emu_glue_int_handler); /* Release mutex */ up(&emu_proc_opened_mutex); return 0; }
int power_ic_rtc_set_time(struct timeval *power_ic_time) { int err = 0; if (power_ic_time->tv_usec > 500000) { power_ic_time->tv_sec += 1; } err = power_ic_set_reg_value(RTC_TOD_REG,POWER_IC_TIME_REG_BIT, power_ic_time->tv_sec % POWER_IC_NUM_SEC_PER_DAY, POWER_IC_TOD_NUM_BITS ); err = power_ic_set_reg_value(RTC_DAY_REG,POWER_IC_TIME_REG_BIT, power_ic_time->tv_sec / POWER_IC_NUM_SEC_PER_DAY, POWER_IC_DAY_NUM_BITS); tracemsg(_k_d("Set RTC Time \n RTC_TOD = %d \n RTC_DAY = %d \n tv_sec = %d Error = %d \n"),((int)power_ic_time->tv_sec % POWER_IC_NUM_SEC_PER_DAY) , ((int)power_ic_time->tv_sec / POWER_IC_NUM_SEC_PER_DAY),((int)power_ic_time->tv_sec), err ); return err; }
static int emu_proc_release(struct inode *inode, struct file *file) { /* take mutex */ if(down_interruptible(&emu_proc_opened_mutex)) { tracemsg(_k_d("process received signal while waiting for power ic opened mutex. Exiting.")); return -EINTR; } /* Reset the opened state */ emu_proc_opened = false; /* Release mutex */ up(&emu_proc_opened_mutex); return 0; }
int power_ic_rtc_get_time_alarm(struct timeval *power_ic_time) { int toda_reg_val = 0; int daya_reg_val = 0; int err = 0; err = power_ic_get_reg_value(RTC_TODA_REG, POWER_IC_TIME_REG_BIT, &toda_reg_val, POWER_IC_TOD_NUM_BITS); err = power_ic_get_reg_value(RTC_DAYA_REG, POWER_IC_TIME_REG_BIT, &daya_reg_val, POWER_IC_DAY_NUM_BITS); power_ic_time->tv_sec = toda_reg_val + daya_reg_val * POWER_IC_NUM_SEC_PER_DAY; power_ic_time->tv_usec = 0; tracemsg(_k_d("Get RTC Alarm Time \n RTC_TODA = %d \n RTC_DAYA = %d \n tv_sec = %d Error = %d \n"), toda_reg_val, daya_reg_val,(int)power_ic_time->tv_sec, err ); return err; }
int power_ic_rtc_set_time_alarm(struct timeval *power_ic_time) { int err = 0; if (power_ic_time->tv_usec > 500000) { power_ic_time->tv_sec += 1; } err = power_ic_set_reg_value(RTC_TODA_REG,POWER_IC_TIME_REG_BIT, power_ic_time->tv_sec % POWER_IC_NUM_SEC_PER_DAY, POWER_IC_TOD_NUM_BITS ); err = power_ic_set_reg_value(RTC_DAYA_REG,POWER_IC_TIME_REG_BIT, power_ic_time->tv_sec / POWER_IC_NUM_SEC_PER_DAY, POWER_IC_DAY_NUM_BITS); err |= power_ic_event_unmask(RTC_TODA_EVENT); tracemsg(_k_d("Set RTC Alarm Time \nRTC_TODA = %d \n RTC_DAYA = %d \n tv_sec = %d Error = %d \n"),(int)power_ic_time->tv_sec % POWER_IC_NUM_SEC_PER_DAY , (int)power_ic_time->tv_sec / POWER_IC_NUM_SEC_PER_DAY,(int)power_ic_time->tv_sec, err ); return err; }
/*! * @brief Used to determine what type of PPD this is initially. * * The event handler function reads the state of the D+ and D- lines to determine * what type of accessory is connected. The state is then changed to the * Connected state along with an indication of the identification of the accessory. * * @param polling_interval a return parameter that holds the "polling rate" * * @return next_state tells the state machine what the next EMU state is */ EMU_STATE_T pre_ppd_identify_handler(int *polling_interval) { *polling_interval = EMU_POLL_CONTINUE; switch (get_bus_state(EMU_BUS_SIGNAL_DP_DM)) { case EMU_BUS_SIGNAL_STATE_DP_DM_10: tracemsg(_k_d("Set the GPIO for ST headset")); EMU_SET_HEADSET_PULL_UP(ENABLE); return EMU_STATE_CONNECTING__PPD_IDENTIFY; break; case EMU_BUS_SIGNAL_STATE_DP_DM_00: case EMU_BUS_SIGNAL_STATE_DP_DM_01: case EMU_BUS_SIGNAL_STATE_DP_DM_11: default: emu_current_device = MOTO_ACCY_TYPE_NOT_SUPPORTED; return EMU_STATE_CONNECTED__NOT_SUPPORTED; break; } }
/*! * @brief the read() handler for the EMU power IC proc device node - for SCMA11 and BUTE platform only. * * This function implements the read() system call on the power IC device. * * @param file file pointer * @param buf data * @param count data size * @param ppos position unused. * * * @return 0 if succesful */ ssize_t emu_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos) { int retval = 0; /* If there isn't space in the buffer for the results, return a failure. Do not * consider the process complete if this happens as the results are still available. */ if(count < sizeof(emu_proc_event_int)) { return -EFBIG; } /* take mutex */ if(down_interruptible(&emu_proc_event_int_mutex)) { tracemsg(_k_d("process received signal while waiting for power ic int event mutex. Exiting.")); return -EINTR; } /* Copy the results back to user-space. */ if( !(copy_to_user((int *)buf, (void *)&emu_proc_event_int, sizeof(emu_proc_event_int))) ) { /* Data was copied successfully. */ retval = sizeof(emu_proc_event_int); } else { /* release mutex */ up(&emu_proc_event_int_mutex); return -EFAULT; } /* Clear variable and release mutex */ emu_proc_event_int_flag = false; emu_proc_event_int = 0; up(&emu_proc_event_int_mutex); return retval; }
/*! * @brief Sets the power path. * * This function configures the path used to supply current from the charger. In * dual-path mode, the charger is connected to the phone's supply and can operate * the phone without a battery so long as the current drawn isn't too high. In * current-share mode, a battery must be present for the phone to operate. * * @param path New setting for power path. * * @pre The power path must be set to dual-path prior to enabling the trickle * charger. * * @return returns 0 if successful. */ int power_ic_charger_set_power_path(POWER_IC_CHARGER_POWER_PATH_T path) { /* Both FET override and FET control are used to control path. */ int mask = EMU_FET_OVRD_MASK | EMU_FET_CTRL_MASK; int setup; switch(path) { /* Hardware-controlled dual-path mode. */ case POWER_IC_CHARGER_POWER_DUAL_PATH: setup = 0; break; /* Hardware-controlled current-share mode. */ case POWER_IC_CHARGER_POWER_CURRENT_SHARE: setup = EMU_FET_CTRL_MASK; break; /* Software-overridden dual-path mode. */ case POWER_IC_CHARGER_POWER_DUAL_PATH_SW_OVERRIDE: setup = EMU_FET_OVRD_MASK; break; /* Software-overridden current-share mode. */ case POWER_IC_CHARGER_POWER_CURRENT_SHARE_SW_OVERRIDE: setup = EMU_FET_OVRD_MASK | EMU_FET_CTRL_MASK; break; default: tracemsg(_k_d(" power path %d is invalid."), path); return -EINVAL; } tracemsg(_k_a("########################## Charger: setting power path 0x%X (masked with 0x%X)"), setup, mask); return(power_ic_set_reg_mask(POWER_IC_REG_EOC_POWER_CONTROL_0, mask, setup)); }
/*! * @brief Sets the charge voltage. * * This function sets the maximum voltage that the battery will charged up to. * * @param charge_voltage Maximum charge voltage to be set. * * @return returns 0 if successful. */ int power_ic_charger_set_charge_voltage(int charge_voltage) { tracemsg(_k_d("Charger: setting VCHRG 0x%X"), charge_voltage); return(power_ic_set_reg_mask(POWER_IC_REG_EOC_POWER_CONTROL_0, EMU_VCHRG_MASK, charge_voltage)); }
static int emu_proc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned int ret_val = 0; POWER_IC_EMU_GLUE_READ_SENSE_T read_sense; POWER_IC_EMU_GLUE_TRANSCEIVER_PARAMS_T trans_params; POWER_IC_FET_CONTROL_T fet_ctrl; /* prev lockout status */ static bool prev_emu_hw_lockout_state = false; /* what devices were connected before lockout */ static MOTO_ACCY_MASK_T connected_device_before_lock = 0; MOTO_ACCY_MASK_T connected_device; /* Get the actual command from the ioctl request. */ unsigned int cmd_num = _IOC_NR(cmd); if ((cmd_num >= POWER_IC_IOC_CMD_EMU_GLUE_BASE) && (cmd_num <= POWER_IC_IOC_CMD_EMU_GLUE_LAST_CMD)) { tracemsg(_k_d("EMU GLUE control ioctl(), request 0x%X (cmd 0x%X)"),(int) cmd, _IOC_NR(cmd)); /* Handle the request. */ switch(cmd) { case POWER_IC_IOCTL_CMD_EMU_GLUE_READ_SENSE: /* Fetch the data passed from user space. */ if(copy_from_user((void *)&read_sense, (void *)arg, sizeof(read_sense)) != 0) { tracemsg(_k_d("error copying data from user space.")); ret_val = -EFAULT; } else { /* Read the sense and clear the interrupt if requested */ read_sense.sense = emu_glue_read_sense(read_sense.clear_int_flags); /* Only the sense value read needs to be sent back to the caller. */ if(put_user(read_sense.sense,&(((POWER_IC_EMU_GLUE_READ_SENSE_T *)arg)->sense)) != 0) { tracemsg(_k_d("error copying read bits to user space.")); ret_val = -EFAULT; } } break; case POWER_IC_IOCTL_CMD_EMU_GLUE_LOCKOUT_CHANGES: power_ic_emu_hw_locked = (bool)(arg == 0 ? 0 : 1); if (power_ic_emu_hw_locked) { /* Make sure the hardware is not already locked */ if (prev_emu_hw_lockout_state == false) { /* If the EMU hardware needs to be locked, keep track of the currently connected accessory */ connected_device_before_lock = moto_accy_get_all_devices(); } } else { /* If the EMU hardware was previously locked but is now unlocked the accessory may need to be reconfigured. */ if (prev_emu_hw_lockout_state == true) { connected_device = moto_accy_get_all_devices(); if (connected_device_before_lock == connected_device) { if ((ACCY_BITMASK_ISSET(connected_device, MOTO_ACCY_TYPE_HEADSET_EMU_MONO)) || (ACCY_BITMASK_ISSET(connected_device, MOTO_ACCY_TYPE_HEADSET_EMU_STEREO))) { audio_config.id_pull_down = 0; audio_config.conn_mode = 0; emu_util_set_emu_headset_mode(audio_config.headset_mode); } else if ((ACCY_BITMASK_ISSET(connected_device, MOTO_ACCY_TYPE_CARKIT_MID)) || (ACCY_BITMASK_ISSET(connected_device, MOTO_ACCY_TYPE_CARKIT_FAST))) { audio_config.headset_mode = MOTO_ACCY_HEADSET_MODE_NONE; EMU_SET_EMU_CONN_MODE(audio_config.conn_mode); EMU_SET_ID_PULL_DOWN(audio_config.id_pull_down); } else { audio_config.headset_mode = MOTO_ACCY_HEADSET_MODE_NONE; audio_config.conn_mode = POWER_IC_EMU_CONN_MODE_USB; audio_config.id_pull_down = 0; } } else { audio_config.headset_mode = MOTO_ACCY_HEADSET_MODE_NONE; audio_config.conn_mode = POWER_IC_EMU_CONN_MODE_USB; audio_config.id_pull_down = 0; } } } prev_emu_hw_lockout_state = power_ic_emu_hw_locked; break; case POWER_IC_IOCTL_CMD_EMU_GLUE_GET_FET_CONTROL: if ((power_ic_get_reg_value(POWER_IC_REG_ATLAS_CHARGER_0, 10, (int *)&(fet_ctrl), 2)) != 0) { ret_val = -EIO; } /* Only the sense value read needs to be sent back to the caller. */ else if(put_user(fet_ctrl, (((int *)arg))) != 0) { tracemsg(_k_d("error copying read bits to user space.")); ret_val = -EFAULT; } break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_FET_CONTROL: power_ic_set_reg_value(POWER_IC_REG_ATLAS_CHARGER_0, 10, arg, 2); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_VBUS_5K_PD: power_ic_set_reg_value(POWER_IC_REG_ATLAS_CHARGER_0, 19, (arg == 0 ? 0 : 1), 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_VBUS_70K_PD: power_ic_set_reg_value(POWER_IC_REG_ATLAS_USB_0, 6, (arg == 0 ? 0 : 1), 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_REVERSE_MODE: power_ic_set_reg_value(POWER_IC_REG_ATLAS_CHARGE_USB_1, 5, (arg == 0 ? 0 : 1), 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_ID_PU: power_ic_set_reg_value(POWER_IC_REG_ATLAS_USB_0, 22, (arg == 0 ? 0 : 1), 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_ID_PD: power_ic_set_reg_value(POWER_IC_REG_ATLAS_USB_0, 20, (arg == 0 ? 0 : 1), 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_ID_STEREO_PU: power_ic_set_reg_value(POWER_IC_REG_ATLAS_CHARGE_USB_1, 8, (arg == 0 ? 0 : 1), 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_CONN_MODE: power_ic_set_reg_value(POWER_IC_REG_ATLAS_USB_0, 14, arg, 3); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_DPLUS_150K_PU: power_ic_set_reg_value(POWER_IC_REG_ATLAS_USB_0, 5, (arg == 0 ? 0 : 1), 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_DPLUS_1_5K_PU: power_ic_set_reg_value(POWER_IC_REG_ATLAS_USB_0, 2, (arg == 0 ? 0 : 1), 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_LOW_SPEED_MODE: power_ic_set_reg_value(POWER_IC_REG_ATLAS_USB_0, 0, (arg == 0 ? 0 : 1), 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_USB_SUSPEND: power_ic_set_reg_value(POWER_IC_REG_ATLAS_USB_0, 1, (arg == 0 ? 0 : 1), 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_TRANSCEIVER_PARAMS: /* Fetch the data passed from user space. */ if(copy_from_user((void *)&trans_params, (void *)arg, sizeof(trans_params)) != 0) { tracemsg(_k_d("error copying data from user space.")); ret_val = -EFAULT; } else { /* Call local function */ emu_glue_set_transceiver_params(trans_params); } break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_ID_INT_MASK: power_ic_set_reg_value(POWER_IC_REG_ATLAS_INT_MASK_0, 19, arg, 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_VBUS_INT_MASK: power_ic_set_reg_value(POWER_IC_REG_ATLAS_INT_MASK_0, 16, arg, 1); break; case POWER_IC_IOCTL_CMD_EMU_GLUE_SET_SE1_INT_MASK: power_ic_set_reg_value(POWER_IC_REG_ATLAS_INT_MASK_0, 21, arg, 1); break; default: /* This shouldn't be able to happen, but just in case... */ tracemsg(_k_d("=> 0x%X unsupported emu proc ioctl command"), (int) cmd); ret_val = -ENOTTY; break; } } else /* The driver doesn't support this request. */ { tracemsg(_k_d("0x%X unsupported ioctl command"), (int) cmd); ret_val = -ENOTTY; } return ret_val; }
/*! * @brief Used to debounce the device type * * The debouncing of the accessory is done using the base accessory type * (self-powered versus phone-powered). The purpose of the event handler is to * wait until the base accessory type has stabilized before continuing with * accessory identification. This is accomplished by having the event handler * execute every 100 milliseconds until the base accessory type has not changed * for 300 milliseconds. * Once the debounce is complete, the event handler will perform the initial * accessory identification and transition to the appropriate next state. * * @param polling_interval a return parameter that points to the "polling rate" * * @return next_state tells the state machine what the next EMU state is */ EMU_STATE_T debounce_dev_type_handler(int *polling_interval) { EMU_ID_RESISTOR_T id_res; EMU_DEV_TYPE_T device_type; EMU_STATE_T next_state; /* Debounce the device type */ if ((is_device_type_debounced(&device_type)) == FALSE) { /* Come back to this state after polling interval expires */ *polling_interval = EMU_POLL_DFLT_DEB_DELAY; return EMU_STATE_CONNECTING__DEBOUNCE_DEV_TYPE; } /* Finished debouncing, reset the counter */ emu_debounce_counter = 0; /* Store the device type */ emu_current_device_type = device_type; /* Now that the device type is debounced... */ switch (device_type) { case EMU_DEV_TYPE_PPD: tracemsg(_k_d("EMU: debounce_dev_type_handler: EMU_DEV_TYPE_PPD")); /* Verify that ID res is 100k */ if (get_id_res_value() != EMU_ID_RESISTOR_100K) { /*if ID is not 100k, the accy is invalid */ emu_current_device = MOTO_ACCY_TYPE_INVALID; next_state = EMU_STATE_CONNECTED__INVALID; tracemsg(_k_d("EMU: debounce_dev_type_handler: ID != 100k")); } else { /* Go to the first PPD polling state */ next_state = EMU_STATE_CONNECTING__PPD_VALIDATE; } break; case EMU_DEV_TYPE_SPD: tracemsg(_k_d("EMU: debounce_dev_type_handler: EMU_DEV_TYPE_SPD")); /* Get the resistance on ID */ id_res = get_id_res_value(); /* Is the device single ended one */ if (get_bus_state(EMU_BUS_SIGNAL_SE1)) { id_res += EMU_ID_RESISTOR_SE1; } emu_current_device = spd_id_translation_table[id_res].device; next_state = spd_id_translation_table[id_res].next_state; /* If we were previously in the unpowered SIHF state, and the device is still not behaving (looks like an unpowered sihf), set the device as invalid and transition to the connected state */ if ((emu_prev_state == EMU_STATE_CONNECTING__UNPOWERED_SIHF) && (next_state == EMU_STATE_CONNECTING__UNPOWERED_SIHF)) { emu_current_device = MOTO_ACCY_TYPE_INVALID; next_state = EMU_STATE_CONNECTED__INVALID; } break; case EMU_DEV_TYPE_NOT_SUPPORTED: tracemsg(_k_d("EMU: debounce_dev_type_handler: EMU_DEV_TYPE_NOT_SUPPORTED")); emu_current_device = MOTO_ACCY_TYPE_NOT_SUPPORTED; next_state = EMU_STATE_CONNECTED__NOT_SUPPORTED; break; case EMU_DEV_TYPE_INVALID: tracemsg(_k_d("EMU: debounce_dev_type_handler: EMU_DEV_TYPE_INVALID")); emu_current_device = MOTO_ACCY_TYPE_INVALID; next_state = EMU_STATE_CONNECTED__INVALID; break; case EMU_DEV_TYPE_NONE: tracemsg(_k_d("EMU: debounce_dev_type_handler: EMU_DEV_TYPE_NONE")); default: emu_current_device = MOTO_ACCY_TYPE_NONE; next_state = EMU_STATE_DISCONNECTED; break; } tracemsg(_k_d("EMU: debounce_dev_type_handler: current_device = %d, next_state = %d"), emu_current_device, next_state); *polling_interval = EMU_POLL_CONTINUE; return next_state; }
/*! * @brief Connecting State: spd_delay_handler function * * The event handler for this state will only be called after the 5 millisecond * delay and is responsible for checking the state of the D- line. * * @param polling_interval a return parameter that holds the "polling rate" * * @return next_state tells the state machine what the next EMU state is */ EMU_STATE_T spd_delay_handler(int *polling_interval) { int vbus; EMU_STATE_T next_state = EMU_STATE_CONNECTED__POLL_SPD_REMOVAL; /* Verify that the device type didn't change while polling */ if (get_device_type() != EMU_DEV_TYPE_SPD) { tracemsg(_k_d("EMU: spd_delay_handler:device type changed while polling!")); *polling_interval = EMU_POLL_CONTINUE; /* Something changed debounce the accy again */ return EMU_STATE_CONNECTING__DEBOUNCE_DEV_TYPE; } /* If dminus is high then this charger is a SIHF */ if (get_bus_state(EMU_BUS_SIGNAL_DMINUS)) { if (emu_current_device == MOTO_ACCY_TYPE_CHARGER_FAST) { emu_current_device = MOTO_ACCY_TYPE_CARKIT_FAST; } else { emu_current_device = MOTO_ACCY_TYPE_CARKIT_MID; } } else { if (emu_current_device == MOTO_ACCY_TYPE_CHARGER_FAST) { /* Convert the Batt+ channel to get the VBUS voltage */ if (power_ic_atod_single_channel(EMU_A2D_VBUS, &vbus) != 0) { vbus = 0; } if (vbus >= EMU_VBUS_5VOLTS) { /*current_device is already set correctly */ /*current_device = MOTO_ACCY_TYPE_CHARGER_FAST;*/ } else if (vbus >= EMU_VBUS_4_5VOLTS) { emu_current_device = MOTO_ACCY_TYPE_CHARGER_FAST_3G; } else /*if (vbus < VBUS_4_5VOLTS)*/ { emu_current_device = MOTO_ACCY_TYPE_INVALID; next_state = EMU_STATE_CONNECTED__INVALID; } } /*else if (emu_current_device == MOTO_ACCY_TYPE_CHARGER_MID) */ /* Nothing to do here for Mid rate chargers */ } tracemsg(_k_d("EMU: current_device = %d"), emu_current_device); *polling_interval = EMU_POLL_CONTINUE; /* Go to the determined connected substate */ return next_state; }