static int __devinit pcf50633_bl_probe(struct platform_device *pdev) { struct pcf50633_bl *pcf_bl; struct device *parent = pdev->dev.parent; struct pcf50633_platform_data *pcf50633_data = parent->platform_data; struct pcf50633_bl_platform_data *pdata = pcf50633_data->backlight_data; struct backlight_properties bl_props; pcf_bl = devm_kzalloc(&pdev->dev, sizeof(*pcf_bl), GFP_KERNEL); if (!pcf_bl) return -ENOMEM; memset(&bl_props, 0, sizeof(bl_props)); bl_props.type = BACKLIGHT_RAW; bl_props.max_brightness = 0x3f; bl_props.power = FB_BLANK_UNBLANK; if (pdata) { bl_props.brightness = pdata->default_brightness; pcf_bl->brightness_limit = pdata->default_brightness_limit; } else { bl_props.brightness = 0x3f; pcf_bl->brightness_limit = 0x3f; } pcf_bl->pcf = dev_to_pcf50633(pdev->dev.parent); pcf_bl->bl = backlight_device_register(pdev->name, &pdev->dev, pcf_bl, &pcf50633_bl_ops, &bl_props); if (IS_ERR(pcf_bl->bl)) return PTR_ERR(pcf_bl->bl); platform_set_drvdata(pdev, pcf_bl); pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDDIM, pdata->ramp_time); /* Should be different from bl_props.brightness, so we do not exit * update_status early the first time it's called */ pcf_bl->brightness = pcf_bl->bl->props.brightness + 1; backlight_update_status(pcf_bl->bl); return 0; }
static int pcf50633_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { struct pcf50633 *pcf; int regulator_id, millivolts; u8 volt_bits, regnr; pcf = rdev_get_drvdata(rdev); regulator_id = rdev_get_id(rdev); if (regulator_id >= PCF50633_NUM_REGULATORS) return -EINVAL; millivolts = min_uV / 1000; regnr = pcf50633_regulator_registers[regulator_id]; switch (regulator_id) { case PCF50633_REGULATOR_AUTO: volt_bits = auto_voltage_bits(millivolts); break; case PCF50633_REGULATOR_DOWN1: volt_bits = down_voltage_bits(millivolts); break; case PCF50633_REGULATOR_DOWN2: volt_bits = down_voltage_bits(millivolts); break; case PCF50633_REGULATOR_LDO1: case PCF50633_REGULATOR_LDO2: case PCF50633_REGULATOR_LDO3: case PCF50633_REGULATOR_LDO4: case PCF50633_REGULATOR_LDO5: case PCF50633_REGULATOR_LDO6: case PCF50633_REGULATOR_HCLDO: volt_bits = ldo_voltage_bits(millivolts); break; default: return -EINVAL; } return pcf50633_reg_write(pcf, regnr, volt_bits); }
static ssize_t set_chglim(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct pcf50633_mbc *mbc = dev_get_drvdata(dev); unsigned long ma; unsigned int mbcc5; int ret; if (!mbc->pcf->pdata->charger_reference_current_ma) return -ENODEV; ret = kstrtoul(buf, 10, &ma); if (ret) return ret; mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma; if (mbcc5 > 255) mbcc5 = 255; pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5); return count; }
static irqreturn_t pcf50633_irq(int irq, void *data) { struct pcf50633 *pcf = data; int ret, i, j; u8 pcf_int[5], chgstat; /* Read the 5 INT regs in one transaction */ ret = pcf50633_read_block(pcf, PCF50633_REG_INT1, ARRAY_SIZE(pcf_int), pcf_int); if (ret != ARRAY_SIZE(pcf_int)) { dev_err(pcf->dev, "Error reading INT registers\n"); /* * If this doesn't ACK the interrupt to the chip, we'll be * called once again as we're level triggered. */ goto out; } /* defeat 8s death from lowsys on A5 */ pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN, 0x04); /* We immediately read the usb and adapter status. We thus make sure * only of USBINS/USBREM IRQ handlers are called */ if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) { chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); if (chgstat & (0x3 << 4)) pcf_int[0] &= ~PCF50633_INT1_USBREM; else pcf_int[0] &= ~PCF50633_INT1_USBINS; } /* Make sure only one of ADPINS or ADPREM is set */ if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) { chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); if (chgstat & (0x3 << 4)) pcf_int[0] &= ~PCF50633_INT1_ADPREM; else pcf_int[0] &= ~PCF50633_INT1_ADPINS; } dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x " "INT4=0x%02x INT5=0x%02x\n", pcf_int[0], pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]); /* Some revisions of the chip don't have a 8s standby mode on * ONKEY1S press. We try to manually do it in such cases. */ if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) { dev_info(pcf->dev, "ONKEY1S held for %d secs\n", pcf->onkey1s_held); if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT) if (pcf->pdata->force_shutdown) pcf->pdata->force_shutdown(pcf); } if (pcf_int[2] & PCF50633_INT3_ONKEY1S) { dev_info(pcf->dev, "ONKEY1S held\n"); pcf->onkey1s_held = 1 ; /* Unmask IRQ_SECOND */ pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M, PCF50633_INT1_SECOND); /* Unmask IRQ_ONKEYR */ pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M, PCF50633_INT2_ONKEYR); } if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) { pcf->onkey1s_held = 0; /* Mask SECOND and ONKEYR interrupts */ if (pcf->mask_regs[0] & PCF50633_INT1_SECOND) pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_INT1M, PCF50633_INT1_SECOND, PCF50633_INT1_SECOND); if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR) pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_INT2M, PCF50633_INT2_ONKEYR, PCF50633_INT2_ONKEYR); } /* Have we just resumed ? */ if (pcf->is_suspended) { pcf->is_suspended = 0; /* Set the resume reason filtering out non resumers */ for (i = 0; i < ARRAY_SIZE(pcf_int); i++) pcf->resume_reason[i] = pcf_int[i] & pcf->pdata->resumers[i]; /* Make sure we don't pass on any ONKEY events to * userspace now */ pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF); } for (i = 0; i < ARRAY_SIZE(pcf_int); i++) { /* Unset masked interrupts */ pcf_int[i] &= ~pcf->mask_regs[i]; for (j = 0; j < 8 ; j++) if (pcf_int[i] & (1 << j)) pcf50633_irq_call_handler(pcf, (i * 8) + j); } out: return IRQ_HANDLED; }
static int __devinit pcf50633_probe(struct i2c_client *client, const struct i2c_device_id *ids) { struct pcf50633 *pcf; struct pcf50633_platform_data *pdata = client->dev.platform_data; int i, ret = 0; int version, variant; pcf = kzalloc(sizeof(*pcf), GFP_KERNEL); if (!pcf) return -ENOMEM; pcf->pdata = pdata; mutex_init(&pcf->lock); i2c_set_clientdata(client, pcf); pcf->dev = &client->dev; pcf->i2c_client = client; pcf->irq = client->irq; pcf->work_queue = create_singlethread_workqueue("pcf50633"); INIT_WORK(&pcf->irq_work, pcf50633_irq_worker); version = pcf50633_reg_read(pcf, 0); variant = pcf50633_reg_read(pcf, 1); if (version < 0 || variant < 0) { dev_err(pcf->dev, "Unable to probe pcf50633\n"); ret = -ENODEV; goto err; } dev_info(pcf->dev, "Probed device version %d variant %d\n", version, variant); /* Enable all interrupts except RTC SECOND */ pcf->mask_regs[0] = 0x80; pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]); pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00); pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00); pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00); pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00); /* Create sub devices */ pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev); pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev); pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev); pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc_pdev); for (i = 0; i < PCF50633_NUM_REGULATORS; i++) { struct platform_device *pdev; pdev = platform_device_alloc("pcf50633-regltr", i); if (!pdev) { dev_err(pcf->dev, "Cannot create regulator\n"); continue; } pdev->dev.parent = pcf->dev; pdev->dev.platform_data = &pdata->reg_init_data[i]; dev_set_drvdata(&pdev->dev, pcf); pcf->regulator_pdev[i] = pdev; platform_device_add(pdev); } if (client->irq) { ret = request_irq(client->irq, pcf50633_irq, IRQF_TRIGGER_LOW, "pcf50633", pcf); if (ret) { dev_err(pcf->dev, "Failed to request IRQ %d\n", ret); goto err; } } else { dev_err(pcf->dev, "No IRQ configured\n"); goto err; } if (enable_irq_wake(client->irq) < 0) dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source" "in this hardware revision", client->irq); ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group); if (ret) dev_err(pcf->dev, "error creating sysfs entries\n"); if (pdata->probe_done) pdata->probe_done(pcf); return 0; err: destroy_workqueue(pcf->work_queue); kfree(pcf); return ret; }
int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) { struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); int ret = 0; u8 bits; u8 mbcs2, chgmod; unsigned int mbcc5; if (ma >= 1000) { bits = PCF50633_MBCC7_USB_1000mA; ma = 1000; } else if (ma >= 500) { bits = PCF50633_MBCC7_USB_500mA; ma = 500; } else if (ma >= 100) { bits = PCF50633_MBCC7_USB_100mA; ma = 100; } else { bits = PCF50633_MBCC7_USB_SUSPEND; ma = 0; } ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7, PCF50633_MBCC7_USB_MASK, bits); if (ret) dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma); else dev_info(pcf->dev, "usb curlim to %d mA\n", ma); /* * We limit the charging current to be the USB current limit. * The reason is that on pcf50633, when it enters PMU Standby mode, * which it does when the device goes "off", the USB current limit * reverts to the variant default. In at least one common case, that * default is 500mA. By setting the charging current to be the same * as the USB limit we set here before PMU standby, we enforce it only * using the correct amount of current even when the USB current limit * gets reset to the wrong thing */ if (mbc->pcf->pdata->charger_reference_current_ma) { mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma; if (mbcc5 > 255) mbcc5 = 255; pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5); } mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2); chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); /* If chgmod == BATFULL, setting chgena has no effect. * Datasheet says we need to set resume instead but when autoresume is * used resume doesn't work. Clear and set chgena instead. */ if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL) pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA); else { pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1, PCF50633_MBCC1_CHGENA); pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA); } power_supply_changed(mbc->usb); return ret; }