static void palmas_enable_irq(struct palmas_usb *palmas_usb) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_VBUS_CTRL_SET, PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP); if (palmas_usb->enable_id_detection) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_CTRL_SET, PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP); palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_EN_HI_SET, PALMAS_USB_ID_INT_EN_HI_SET_ID_GND | PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); } if (palmas_usb->enable_vbus_detection) palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); /* cold plug for host mode needs this delay */ if (palmas_usb->enable_id_detection) { msleep(30); palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb); } }
static void palmas_enable_irq(struct palmas_usb *palmas_usb) { int ret; palmas_usb->vbus_linkstat = PALMAS_USB_STATE_INIT; palmas_usb->id_linkstat = PALMAS_USB_STATE_INIT; palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_VBUS_CTRL_SET, PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP); palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_CTRL_CLEAR, PALMAS_USB_ID_CTRL_SET_ID_SRC_5U); palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_CTRL_SET, PALMAS_USB_ID_CTRL_SET_ID_SRC_16U | PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP); if (palmas_usb->enable_vbus_detection) palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); if (palmas_usb->enable_id_detection) { /* Wait for the comparator to update status */ msleep(palmas_usb->cable_debounce_time); ret = palmas_usb_id_state_update(palmas_usb); if (ret == -EAGAIN) schedule_delayed_work(&palmas_usb->cable_update_wq, msecs_to_jiffies(palmas_usb->cable_debounce_time)); } }
static void palmas_usb_wakeup(struct palmas *palmas, int enable) { if (enable) palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, PALMAS_USB_WAKEUP_ID_WK_UP_COMP); else palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, 0); }
/* * GPADC lock issue in AUTO mode. * Impact: In AUTO mode, GPADC conversion can be locked after disabling AUTO * mode feature. * Details: * When the AUTO mode is the only conversion mode enabled, if the AUTO * mode feature is disabled with bit GPADC_AUTO_CTRL.AUTO_CONV1_EN = 0 * or bit GPADC_AUTO_CTRL.AUTO_CONV0_EN = 0 during a conversion, the * conversion mechanism can be seen as locked meaning that all following * conversion will give 0 as a result. Bit GPADC_STATUS.GPADC_AVAILABLE * will stay at 0 meaning that GPADC is busy. An RT conversion can unlock * the GPADC. * * Workaround(s): * To avoid the lock mechanism, the workaround to follow before any stop * conversion request is: * Force the GPADC state machine to be ON by using the * GPADC_CTRL1.GPADC_FORCE bit = 1 * Shutdown the GPADC AUTO conversion using * GPADC_AUTO_CTRL.SHUTDOWN_CONV[01] = 0. * After 100us, force the GPADC state machine to be OFF by using the * GPADC_CTRL1.GPADC_FORCE bit = 0 */ static int palmas_disable_auto_conversion(struct palmas_gpadc *adc) { int ret; ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_CTRL1, PALMAS_GPADC_CTRL1_GPADC_FORCE, PALMAS_GPADC_CTRL1_GPADC_FORCE); if (ret < 0) { dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret); return ret; } ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_AUTO_CTRL, 0); if (ret < 0) { dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret); return ret; } udelay(100); ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_CTRL1, PALMAS_GPADC_CTRL1_GPADC_FORCE, 0); if (ret < 0) { dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret); return ret; } return 0; }
static int palmas_gpadc_enable(struct palmas_gpadc *adc, int adc_chan, int enable) { unsigned int mask, val; int ret; if (enable) { val = (adc->extended_delay << PALMAS_GPADC_RT_CTRL_EXTEND_DELAY_SHIFT); ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_RT_CTRL, PALMAS_GPADC_RT_CTRL_EXTEND_DELAY, val); if (ret < 0) { dev_err(adc->dev, "RT_CTRL update failed: %d\n", ret); return ret; } mask = (PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_MASK | PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_MASK | PALMAS_GPADC_CTRL1_GPADC_FORCE); val = (adc->ch0_current << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_SHIFT); val |= (adc->ch3_current << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_SHIFT); val |= PALMAS_GPADC_CTRL1_GPADC_FORCE; ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_CTRL1, mask, val); if (ret < 0) { dev_err(adc->dev, "Failed to update current setting: %d\n", ret); return ret; } mask = (PALMAS_GPADC_SW_SELECT_SW_CONV0_SEL_MASK | PALMAS_GPADC_SW_SELECT_SW_CONV_EN); val = (adc_chan | PALMAS_GPADC_SW_SELECT_SW_CONV_EN); ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_SW_SELECT, mask, val); if (ret < 0) { dev_err(adc->dev, "SW_SELECT update failed: %d\n", ret); return ret; } } else { ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_SW_SELECT, 0); if (ret < 0) dev_err(adc->dev, "SW_SELECT write failed: %d\n", ret); ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_CTRL1, PALMAS_GPADC_CTRL1_GPADC_FORCE, 0); if (ret < 0) { dev_err(adc->dev, "CTRL1 update failed: %d\n", ret); return ret; } } return ret; }
static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) { struct palmas *palmas = dev_get_drvdata(dev->parent); u8 val; val = enabled ? PALMAS_RTC_INTERRUPTS_REG_IT_ALARM : 0; return palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG, val); }
static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) { int adc_period, conv; int i; int ch0 = 0, ch1 = 0; int thres; int ret; adc_period = adc->auto_conversion_period; for (i = 0; i < 16; ++i) { if (((1000 * (1 << i))/32) < adc_period) continue; } if (i > 0) i--; adc_period = i; ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_AUTO_CTRL, PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK, adc_period); if (ret < 0) { dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret); return ret; } conv = 0; if (adc->wakeup1_enable) { int is_high; ch0 = adc->wakeup1_data.adc_channel_number; conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN; if (adc->wakeup1_data.adc_high_threshold > 0) { thres = adc->wakeup1_data.adc_high_threshold; is_high = 0; } else { thres = adc->wakeup1_data.adc_low_threshold; is_high = BIT(7); } ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_THRES_CONV0_LSB, thres & 0xFF); if (ret < 0) { dev_err(adc->dev, "THRES_CONV0_LSB write failed: %d\n", ret); return ret; } ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_THRES_CONV0_MSB, ((thres >> 8) & 0xF) | is_high); if (ret < 0) { dev_err(adc->dev, "THRES_CONV0_MSB write failed: %d\n", ret); return ret; } }
static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) { unsigned int set, id_src; struct palmas_usb *palmas_usb = _palmas_usb; struct extcon_dev *edev = palmas_usb->edev; palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_LATCH_SET, &set); palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_SRC, &id_src); if ((set & PALMAS_USB_ID_INT_SRC_ID_GND) && (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); palmas_usb->linkstat = PALMAS_USB_STATE_ID; extcon_set_state_sync(edev, EXTCON_USB_HOST, true); dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_state_sync(edev, EXTCON_USB_HOST, false); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) && (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_state_sync(edev, EXTCON_USB_HOST, false); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { palmas_usb->linkstat = PALMAS_USB_STATE_ID; extcon_set_state_sync(edev, EXTCON_USB_HOST, true); dev_info(palmas_usb->dev, " USB-HOST cable is attached\n"); } return IRQ_HANDLED; }
static void palmas_usb_id_int_set(struct palmas_usb *palmas_usb) { unsigned int all_int; all_int = PALMAS_USB_ID_INT_SRC_ID_GND | PALMAS_USB_ID_INT_SRC_ID_A | PALMAS_USB_ID_INT_SRC_ID_B | PALMAS_USB_ID_INT_SRC_ID_C | PALMAS_USB_ID_INT_SRC_ID_FLOAT; if (palmas_usb->id_linkstat == PALMAS_USB_STATE_ID_FLOAT) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_EN_HI_SET, all_int); palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_EN_LO_CLR, all_int); } else { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_EN_HI_CLR, all_int); palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_EN_HI_SET, PALMAS_USB_ID_INT_SRC_ID_FLOAT); palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_EN_LO_CLR, all_int); palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_EN_LO_SET, all_int ^ PALMAS_USB_ID_INT_SRC_ID_FLOAT); } }
static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) { unsigned int set; struct palmas_usb *palmas_usb = _palmas_usb; palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_LATCH_SET, &set); palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_LATCH_CLR, set); schedule_delayed_work(&palmas_usb->cable_update_wq, msecs_to_jiffies(palmas_usb->cable_debounce_time)); return IRQ_HANDLED; }
static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { struct palmas_gpio *pg = gpiochip_get_data(gc); struct palmas *palmas = pg->palmas; int ret; unsigned int reg; int gpio16 = (offset/8); offset %= 8; if (gpio16) reg = (value) ? PALMAS_GPIO_SET_DATA_OUT2 : PALMAS_GPIO_CLEAR_DATA_OUT2; else reg = (value) ? PALMAS_GPIO_SET_DATA_OUT : PALMAS_GPIO_CLEAR_DATA_OUT; ret = palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset)); if (ret < 0) dev_err(gc->parent, "Reg 0x%02x write failed, %d\n", reg, ret); }
static int palmas_clear_interrupts(struct device *dev) { struct palmas *palmas = dev_get_drvdata(dev->parent); unsigned int rtc_reg; int ret; ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, &rtc_reg); if (ret < 0) { dev_err(dev, "RTC_STATUS read failed, err = %d\n", ret); return ret; } ret = palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, rtc_reg); if (ret < 0) { dev_err(dev, "RTC_STATUS write failed, err = %d\n", ret); return ret; } return 0; }
static void palmas_power_off(void *drv_data) { struct palmas_pm *palmas_pm = drv_data; struct palmas *palmas = palmas_pm->palmas; unsigned int val; int i; int ret; unsigned int vbus_line_state, ldo_short_status2; palmas_allow_atomic_xfer(palmas); if (palmas_pm->need_rtc_power_on) palmas_auto_power_on(palmas_pm); for (i = 0; i < palmas_pm->num_int_mask_regs; ++i) { ret = palmas_write(palmas, PALMAS_INTERRUPT_BASE, palmas_pm->int_mask_reg_add[i], palmas_pm->int_mask_val[i]); if (ret < 0) dev_err(palmas_pm->dev, "register 0x%02x write failed: %d\n", palmas_pm->int_mask_reg_add[i], ret); ret = palmas_read(palmas, PALMAS_INTERRUPT_BASE, palmas_pm->int_status_reg_add[i], &val); if (ret < 0) dev_err(palmas_pm->dev, "register 0x%02x read failed: %d\n", palmas_pm->int_status_reg_add[i], ret); } if (palmas_pm->enable_boot_up_at_vbus || palmas_pm->need_usb_event_power_on) { ret = palmas_update_bits(palmas, PALMAS_INTERRUPT_BASE, PALMAS_INT3_MASK, PALMAS_INT3_MASK_VBUS | PALMAS_INT3_MASK_VBUS_OTG, 0); if (ret < 0) dev_err(palmas_pm->dev, "INT3_MASK update failed: %d\n", ret); } /* Mask all COLD RST condition */ palmas_write(palmas, PALMAS_PMU_CONTROL_BASE, PALMAS_SWOFF_COLDRST, 0x0); dev_info(palmas_pm->dev, "Powering off the device\n"); palmas_read(palmas, PALMAS_INTERRUPT_BASE, PALMAS_INT3_LINE_STATE, &vbus_line_state); if (palmas_pm->enable_boot_up_at_vbus && (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) { dev_info(palmas_pm->dev, "VBUS found, boot on system by timer interrupt\n"); ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG, PALMAS_RTC_INTERRUPTS_REG_IT_TIMER, PALMAS_RTC_INTERRUPTS_REG_IT_TIMER); if (ret < 0) { dev_err(palmas_pm->dev, "RTC_INTERRUPTS update failed: %d\n", ret); goto poweroff_direct; } ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG, PALMAS_RTC_INTERRUPTS_REG_EVERY_MASK, 0); if (ret < 0) { dev_err(palmas_pm->dev, "RTC_INTERRUPTS update failed: %d\n", ret); goto poweroff_direct; } ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC); if (ret < 0) { dev_err(palmas_pm->dev, "RTC_CTRL_REG update failed: %d\n", ret); goto poweroff_direct; } ret = palmas_update_bits(palmas, PALMAS_INTERRUPT_BASE, PALMAS_INT2_MASK, PALMAS_INT2_MASK_RTC_TIMER, 0); if (ret < 0) { dev_err(palmas_pm->dev, "INT2_MASK update failed: %d\n", ret); goto poweroff_direct; } } poweroff_direct: /* Errata * clear VANA short status before switch-off */ palmas_read(palmas, PALMAS_LDO_BASE, PALMAS_LDO_SHORT_STATUS2, &ldo_short_status2); /* Power off the device */ palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, PALMAS_DEV_CTRL, 1, 0); }
static void palmas_power_reset(void *drv_data) { struct palmas_pm *palmas_pm = drv_data; struct palmas *palmas = palmas_pm->palmas; unsigned int val; int i; int ret; palmas_allow_atomic_xfer(palmas); for (i = 0; i < palmas_pm->num_int_mask_regs; ++i) { ret = palmas_write(palmas, PALMAS_INTERRUPT_BASE, palmas_pm->int_mask_reg_add[i], palmas_pm->int_mask_val[i]); if (ret < 0) dev_err(palmas_pm->dev, "register 0x%02x write failed: %d\n", palmas_pm->int_mask_reg_add[i], ret); ret = palmas_read(palmas, PALMAS_INTERRUPT_BASE, palmas_pm->int_status_reg_add[i], &val); if (ret < 0) dev_err(palmas_pm->dev, "register 0x%02x read failed: %d\n", palmas_pm->int_status_reg_add[i], ret); } /* SW-WAR for ES Version 2.1, 2.0 and 1.0 */ if (palmas_is_es_version_or_less(palmas, 2, 1)) { dev_info(palmas_pm->dev, "Resetting Palmas through RTC\n"); ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, PALMAS_DEV_CTRL, PALMAS_DEV_CTRL_SW_RST, 0); if (ret < 0) { dev_err(palmas_pm->dev, "DEV_CTRL update failed: %d\n", ret); goto reset_direct; } ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG, PALMAS_RTC_INTERRUPTS_REG_IT_TIMER, PALMAS_RTC_INTERRUPTS_REG_IT_TIMER); if (ret < 0) { dev_err(palmas_pm->dev, "RTC_INTERRUPTS update failed: %d\n", ret); goto reset_direct; } ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC); if (ret < 0) { dev_err(palmas_pm->dev, "RTC_CTRL_REG update failed: %d\n", ret); goto reset_direct; } ret = palmas_update_bits(palmas, PALMAS_INTERRUPT_BASE, PALMAS_INT2_MASK, PALMAS_INT2_MASK_RTC_TIMER, 0); if (ret < 0) { dev_err(palmas_pm->dev, "INT2_MASK update failed: %d\n", ret); goto reset_direct; } ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, PALMAS_SWOFF_COLDRST, PALMAS_SWOFF_COLDRST_SW_RST, PALMAS_SWOFF_COLDRST_SW_RST); if (ret < 0) { dev_err(palmas_pm->dev, "SWOFF_COLDRST update failed: %d\n", ret); goto reset_direct; } ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, PALMAS_DEV_CTRL, PALMAS_DEV_CTRL_SW_RST, PALMAS_DEV_CTRL_SW_RST); if (ret < 0) { dev_err(palmas_pm->dev, "DEV_CTRL update failed: %d\n", ret); goto reset_direct; } return; } reset_direct: dev_info(palmas_pm->dev, "Power reset the device\n"); palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, PALMAS_SWOFF_COLDRST, PALMAS_SWOFF_COLDRST_SW_RST, PALMAS_SWOFF_COLDRST_SW_RST); palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, PALMAS_DEV_CTRL, 0x2, 0x2); }