static int palmas_gpadc_calibrate(struct palmas_gpadc *adc, int adc_chan) { s64 k; int d1; int d2; int ret; int x1 = adc->adc_info[adc_chan].x1; int x2 = adc->adc_info[adc_chan].x2; ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE, adc->adc_info[adc_chan].trim1_reg, &d1); if (ret < 0) { dev_err(adc->dev, "TRIM read failed: %d\n", ret); goto scrub; } ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE, adc->adc_info[adc_chan].trim2_reg, &d2); if (ret < 0) { dev_err(adc->dev, "TRIM read failed: %d\n", ret); goto scrub; } /* Gain Calculation */ k = PRECISION_MULTIPLIER; k += div64_s64(PRECISION_MULTIPLIER * (d2 - d1), x2 - x1); adc->adc_info[adc_chan].gain = k; /* Offset Calculation */ adc->adc_info[adc_chan].offset = (d1 * PRECISION_MULTIPLIER); adc->adc_info[adc_chan].offset -= ((k - PRECISION_MULTIPLIER) * x1); scrub: return ret; }
static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset) { struct palmas_gpio *pg = gpiochip_get_data(gc); struct palmas *palmas = pg->palmas; unsigned int val; int ret; unsigned int reg; int gpio16 = (offset/8); offset %= 8; reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR; ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val); if (ret < 0) { dev_err(gc->parent, "Reg 0x%02x read failed, %d\n", reg, ret); return ret; } if (val & BIT(offset)) reg = (gpio16) ? PALMAS_GPIO_DATA_OUT2 : PALMAS_GPIO_DATA_OUT; else reg = (gpio16) ? PALMAS_GPIO_DATA_IN2 : PALMAS_GPIO_DATA_IN; ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val); if (ret < 0) { dev_err(gc->parent, "Reg 0x%02x read failed, %d\n", reg, ret); return ret; } return !!(val & BIT(offset)); }
static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) { struct palmas_usb *palmas_usb = _palmas_usb; unsigned int vbus_line_state; palmas_read(palmas_usb->palmas, PALMAS_INTERRUPT_BASE, PALMAS_INT3_LINE_STATE, &vbus_line_state); dev_info(palmas_usb->dev, "vbus-irq() INT3_LINE_STATE 0x%02x\n", vbus_line_state); if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) { if (palmas_usb->vbus_linkstat != PALMAS_USB_STATE_VBUS) { palmas_usb->vbus_linkstat = PALMAS_USB_STATE_VBUS; extcon_set_cable_state(&palmas_usb->edev, "USB", true); dev_info(palmas_usb->dev, "USB cable is attached\n"); } else { dev_info(palmas_usb->dev, "Spurious connect event detected\n"); } } else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) { if (palmas_usb->vbus_linkstat == PALMAS_USB_STATE_VBUS) { palmas_usb->vbus_linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_cable_state(&palmas_usb->edev, "USB", false); dev_info(palmas_usb->dev, "USB cable is detached\n"); } else { dev_info(palmas_usb->dev, "Spurious disconnect event detected\n"); } } return IRQ_HANDLED; }
static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; u32 int_val; struct palmas *palmas = dev_get_drvdata(dev->parent); int ret; ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, PALMAS_ALARM_SECONDS_REG, alarm_data, PALMAS_NUM_TIME_REGS); if (ret < 0) { dev_err(dev, "RTC_ALARM_SECONDS read failed, err = %d\n", ret); return ret; } alm->time.tm_sec = bcd2bin(alarm_data[0]); alm->time.tm_min = bcd2bin(alarm_data[1]); alm->time.tm_hour = bcd2bin(alarm_data[2]); alm->time.tm_mday = bcd2bin(alarm_data[3]); alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG, &int_val); if (ret < 0) { dev_err(dev, "RTC_INTERRUPTS reg read failed, err = %d\n", ret); return ret; } if (int_val & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM) alm->enabled = 1; 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 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 irqreturn_t palmas_gpadc_irq_auto(int irq, void *data) { struct palmas_gpadc *adc = data; unsigned int val = 0; int ret; ret = palmas_read(adc->palmas, PALMAS_INTERRUPT_BASE, PALMAS_INT3_LINE_STATE, &val); if (ret < 0) dev_err(adc->dev, "%s: Failed to read INT3_LINE_STATE, %d\n", __func__, ret); if (val & PALMAS_INT3_LINE_STATE_GPADC_AUTO_0) dev_info(adc->dev, "Auto0 threshold interrupt occurred\n"); if (val & PALMAS_INT3_LINE_STATE_GPADC_AUTO_1) dev_info(adc->dev, "Auto1 threshold interrupt occurred\n"); return IRQ_HANDLED; }
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 int palmas_usb_id_state_update(struct palmas_usb *palmas_usb) { unsigned int id_src; int ret; int new_state; int new_cable_index; int retry = 5; src_again: ret = palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_SRC, &id_src); if (ret < 0) { dev_err(palmas_usb->dev, "ID_INT_SRC read failed: %d\n", ret); return ret; } dev_info(palmas_usb->dev, "id-state: 0x%02x\n", id_src); if (!id_src) { dev_err(palmas_usb->dev, "Improper ID state found\n"); if (retry--) { msleep(200); goto src_again; } return -EAGAIN; } /* If two ID states show sign then allow debouncing to settle */ if (id_src & (id_src - 1)) { dev_info(palmas_usb->dev, "ID states are not settled, try later\n"); return -EAGAIN; } if (id_src & PALMAS_USB_ID_INT_SRC_ID_GND) { new_state = PALMAS_USB_STATE_ID_GND; new_cable_index = 1; } else if (id_src & PALMAS_USB_ID_INT_SRC_ID_A) { new_state = PALMAS_USB_STATE_ID_A; new_cable_index = 2; } else if (id_src & PALMAS_USB_ID_INT_SRC_ID_B) { new_state = PALMAS_USB_STATE_ID_B; new_cable_index = 3; } else if (id_src & PALMAS_USB_ID_INT_SRC_ID_C) { new_state = PALMAS_USB_STATE_ID_C; new_cable_index = 4; } else if (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT) { new_state = PALMAS_USB_STATE_ID_FLOAT; new_cable_index = -1; } else { dev_info(palmas_usb->dev, "ID_SRC is not valid\n"); new_state = PALMAS_USB_STATE_ID_FLOAT; new_cable_index = 0; } if (palmas_usb->id_linkstat == new_state) { dev_info(palmas_usb->dev, "No change in ID state: Old %d and New %d\n", palmas_usb->id_linkstat, new_state); palmas_usb_id_int_set(palmas_usb); return 0; } if (palmas_usb->cur_cable_index > 0) { dev_info(palmas_usb->dev, "Cable %s detached\n", palmas_extcon_cable[palmas_usb->cur_cable_index]); extcon_set_cable_state(&palmas_usb->edev, palmas_extcon_cable[palmas_usb->cur_cable_index], false); } if ((new_cable_index < 0) && (!palmas_usb->cur_cable_index)) { dev_info(palmas_usb->dev, "All cable detached\n"); extcon_set_cable_state(&palmas_usb->edev, palmas_extcon_cable[1], false); extcon_set_cable_state(&palmas_usb->edev, palmas_extcon_cable[2], false); extcon_set_cable_state(&palmas_usb->edev, palmas_extcon_cable[3], false); extcon_set_cable_state(&palmas_usb->edev, palmas_extcon_cable[4], false); } palmas_usb->cur_cable_index = new_cable_index; palmas_usb->id_linkstat = new_state; if (palmas_usb->cur_cable_index <= 0) goto end; dev_info(palmas_usb->dev, "Cable %s attached\n", palmas_extcon_cable[palmas_usb->cur_cable_index]); extcon_set_cable_state(&palmas_usb->edev, palmas_extcon_cable[palmas_usb->cur_cable_index], true); end: palmas_usb_id_int_set(palmas_usb); 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); }