コード例 #1
0
/*
 * The pm8058_nc_ir detects insert / remove of the headset (for NO),
 * The current state of the headset is maintained in othc_ir_state variable.
 * Due to a hardware bug, false switch interrupts are seen during headset
 * insert. This is handled in the software by rejecting the switch interrupts
 * for a small period of time after the headset has been inserted.
 */
static irqreturn_t pm8058_nc_ir(int irq, void *dev_id)
{
	unsigned long flags, rc;
	struct pm8058_othc *dd = dev_id;

	spin_lock_irqsave(&dd->lock, flags);
	/* Enable the switch reject flag */
	dd->switch_reject = true;
	spin_unlock_irqrestore(&dd->lock, flags);

	/* Start the HR timer if one is not active */
	if (hrtimer_active(&dd->timer))
		hrtimer_cancel(&dd->timer);

	hrtimer_start(&dd->timer,
		ktime_set((dd->switch_debounce_ms / 1000),
		(dd->switch_debounce_ms % 1000) * 1000000), HRTIMER_MODE_REL);

	/* disable irq, this gets enabled in the workqueue */
	disable_irq_nosync(dd->othc_irq_ir);

	/* Check the MIC_BIAS status, to check if inserted or removed */
	rc = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_ir);
	if (rc < 0) {
		pr_err("Unable to read IR status\n");
		goto fail_ir;
	}

	dd->othc_ir_state = rc;
	schedule_delayed_work(&dd->detect_work,
				msecs_to_jiffies(dd->detection_delay_ms));

fail_ir:
	return IRQ_HANDLED;
}
コード例 #2
0
/*
 * The pm8058_no_sw detects the switch press and release operation.
 * The odd number call is press and even number call is release.
 * The current state of the button is maintained in othc_sw_state variable.
 * This isr gets called only for NO type headsets.
 */
static irqreturn_t pm8058_no_sw(int irq, void *dev_id)
{
	int level;
	struct pm8058_othc *dd = dev_id;
	unsigned long flags;

#if defined(CONFIG_HTC_HEADSET_8X60)
	hs_8x60_notify_key_event();
#endif /* CONFIG_HTC_HEADSET_8X60 */

	spin_lock_irqsave(&dd->lock, flags);
	if (dd->switch_reject == true) {
		spin_unlock_irqrestore(&dd->lock, flags);
		return IRQ_HANDLED;
	}
	spin_unlock_irqrestore(&dd->lock, flags);

	level = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_sw);
	if (level < 0) {
		pr_err("Unable to read IRQ status register\n");
		return IRQ_HANDLED;
	}

	if (dd->othc_support_n_switch == true) {
#if !defined(CONFIG_HTC_HEADSET_8X60)
		if (level == 0) {
			dd->othc_sw_state = false;
			input_report_key(dd->othc_ipd, dd->sw_key_code, 0);
			input_sync(dd->othc_ipd);
		} else {
			disable_irq_nosync(dd->othc_irq_sw);
			schedule_work(&dd->switch_work);
		}
#endif /* CONFIG_HTC_HEADSET_8X60 */
		return IRQ_HANDLED;
	}
	/*
	 * It is necessary to check the software state and the hardware state
	 * to make sure that the residual interrupt after the debounce time does
	 * not disturb the software state machine.
	 */
	if (level == 1 && dd->othc_sw_state == false) {
		/*  Switch has been pressed */
		dd->othc_sw_state = true;
		input_report_key(dd->othc_ipd, KEY_MEDIA, 1);
	} else if (level == 0 && dd->othc_sw_state == true) {
		/* Switch has been released */
		dd->othc_sw_state = false;
		input_report_key(dd->othc_ipd, KEY_MEDIA, 0);
	}
	input_sync(dd->othc_ipd);

	return IRQ_HANDLED;
}
コード例 #3
0
static int pm8058_mpp_get(struct gpio_chip *chip, unsigned mpp)
{
	struct pm8058_gpio_platform_data *pdata;
	struct pm8058_chip *pm_chip;

	if (mpp >= PM8058_MPPS || chip == NULL)
		return -EINVAL;

	pdata = chip->dev->platform_data;
	pm_chip = dev_get_drvdata(chip->dev);

	return pm8058_irq_get_rt_status(pm_chip,
		pdata->irq_base + mpp);
}
コード例 #4
0
static irqreturn_t max_valid_handler(int irq, void *dev_id)
{
	struct max8903_struct *max_chg;
    struct platform_device *pdev;
    struct pm8058_chip *chip;
    int state;

    pdev = (struct platform_device *)dev_id;
    max_chg = platform_get_drvdata(pdev);
    chip = get_irq_data(irq);
	
#if 0
	/* Dock insert, think it as AC charger */
	if (gpio_get_value_cansleep(max_chg->dock_det) && (BOARD_NUM(hw_ver) != BOARD_NUM_V11))
		max_chg->adapter_hw_chg.type = CHG_TYPE_USB;
	else
		max_chg->adapter_hw_chg.type = CHG_TYPE_AC;
#endif
	/* reinitialize charger type */
	if ((BOARD_NUM(hw_ver) == BOARD_NUM_V11))
		max_chg->adapter_hw_chg.type = CHG_TYPE_AC;
	else
		max_chg->adapter_hw_chg.type = CHG_TYPE_USB;

    state = pm8058_irq_get_rt_status(chip, irq);
    pr_info("%s:charge state=%d, hw_chg_type=%d\n", __func__, state, max_chg->adapter_hw_chg.type);
    if(state){
		/* delay to queue charge insert envent when charger inserted,
		 * need to detect if it is an ac charger
		 */
        //msm_charger_notify_event(&max_chg->adapter_hw_chg,
        //			 CHG_INSERTED_EVENT);
		delay_ac_charger_detect = 1;
		pr_info("%s:delay_ac_charger_detect=%d start ac charger delay work\n", __func__, delay_ac_charger_detect);
		schedule_delayed_work(&max_chg->ac_charger,
					AC_CHARGER_DETECT_DELAY);
        max_chg->present = 1;
		wake_lock(&max_chg->wl);
    }else{
    	delay_ac_charger_detect = 0;
		pr_info("%s:delay_ac_charger_detect=%d cancel ac charger delay work\n", __func__, delay_ac_charger_detect);
		cancel_delayed_work(&saved_msm_chg->ac_charger);
		
        msm_charger_notify_event(&max_chg->adapter_hw_chg,
        			 CHG_REMOVED_EVENT);
        max_chg->present = 0;
		wake_unlock(&max_chg->wl);
    }
	return IRQ_HANDLED;
}
コード例 #5
0
static int pm_chg_get_rt_status(int irq)
{
    int count = 3;
    int ret;

    while ((ret =
                pm8058_irq_get_rt_status(pm8058_chg.pm_chip, irq)) == -EAGAIN
            && count--) {
        dev_info(pm8058_chg.dev, "%s trycount=%d\n", __func__, count);
        cpu_relax();
    }
    if (ret == -EAGAIN)
        return 0;
    else
        return ret;
}
コード例 #6
0
/*
 * The pm8058_no_sw detects the switch press and release operation.
 * The odd number call is press and even number call is release.
 * The current state of the button is maintained in othc_sw_state variable.
 * This isr gets called only for NO type headsets.
 */
static irqreturn_t pm8058_no_sw(int irq, void *dev_id)
{
	int level;
	struct pm8058_othc *dd = dev_id;
	unsigned long flags;

	spin_lock_irqsave(&dd->lock, flags);
	if (dd->switch_reject == true) {
		spin_unlock_irqrestore(&dd->lock, flags);
		return IRQ_HANDLED;
	}
	spin_unlock_irqrestore(&dd->lock, flags);

	level = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_sw);
	if (level < 0) {
		pr_err("%s: Unable to read IRQ status register\n", __func__);
		return IRQ_HANDLED;
	}

	/*
	 * It is necessary to check the software state and the hardware state
	 * to make sure that the residual interrupt after the debounce time does
	 * not disturb the software state machine.
	 */

	if (level == 1 && dd->othc_sw_state == false) {
		/*  Switch has been pressed */
		dd->othc_sw_state = true;
		input_report_key(dd->othc_ipd, KEY_MEDIA, 1);
	} else if (level == 0 && dd->othc_sw_state == true) {
		/* Switch has been released */
		dd->othc_sw_state = false;
		input_report_key(dd->othc_ipd, KEY_MEDIA, 0);
	}
	input_sync(dd->othc_ipd);

	return IRQ_HANDLED;
}
コード例 #7
0
static ssize_t pmic8058_kp_pressed_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	int row, col;
	int keystate = 0;
	int pwrkeystate = 0;
	struct pmic8058_kp *kp = dev_get_drvdata(dev);

	for (row = 0; row < kp->pdata->num_rows; row++) {
		for (col = 0; col < kp->pdata->num_cols; col++) {
			if(!(kp->keystate[row] & (1 << col))) {
				keystate = 1;
			}
		}
	}
	pwrkeystate = pm8058_irq_get_rt_status(kp->pm_chip, PM8058_PWRKEY_PRESS_IRQ(PM8058_IRQ_BASE));

	if (keystate || pwrkeystate)
		sprintf(buf, "PRESS");
	else
		sprintf(buf, "RELEASE");

	return strlen(buf);
}
コード例 #8
0
static int
othc_configure_hsed(struct pm8058_othc *dd, struct platform_device *pd)
{
	int rc, i;
	struct input_dev *ipd;
	struct pmic8058_othc_config_pdata *pdata = pd->dev.platform_data;
	struct othc_hsed_config *hsed_config = pdata->hsed_config;

	dd->othc_sdev.name = "h2w";
	dd->othc_sdev.print_name = othc_headset_print_name;

	rc = switch_dev_register(&dd->othc_sdev);
	if (rc) {
		pr_err("%s: Unable to register switch device \n", __func__);
		return rc;
	}

	ipd = input_allocate_device();
	if (ipd == NULL) {
		pr_err("%s: Unable to allocate memory \n", __func__);
		rc = -ENOMEM;
		goto fail_input_alloc;
	}

	/* Get the IRQ for Headset Insert-remove and Switch-press */
	dd->othc_irq_sw = platform_get_irq(pd, 0);
	dd->othc_irq_ir = platform_get_irq(pd, 1);
	if (dd->othc_irq_ir < 0) {
		pr_err("%s: othc resource:IRQ_IR absent \n", __func__);
		rc = -ENXIO;
		goto fail_othc_config;
	}
	if (dd->othc_irq_sw < 0) {
		pr_err("%s: othc resource:IRQ_SW absent\n", __func__);
		rc = -ENXIO;
		goto fail_othc_config;
	}

	ipd->name = "pmic8058_othc";
	ipd->phys = "pmic8058_othc/input0";
	ipd->dev.parent = &pd->dev;

	dd->othc_ipd = ipd;
	dd->othc_sw_state = false;
	dd->othc_ir_state = false;
	dd->switch_debounce_ms = hsed_config->switch_debounce_ms;
	dd->othc_support_n_switch = hsed_config->othc_support_n_switch;

	if (dd->othc_support_n_switch == true) {
		pr_debug("%s: OTHC 'n' switch supported\n", __func__);
		if (!hsed_config->switch_config ||
				!hsed_config->switch_config->switch_info) {
			rc = -EINVAL;
			pr_err("%s: Switch pdata absent!\n", __func__);
			goto fail_othc_config;
		}
		dd->switch_config = hsed_config->switch_config;
		for (i = 0; i < dd->switch_config->num_keys; i++) {
			input_set_capability(ipd, EV_KEY,
				dd->switch_config->switch_info[i].key_code);
		}
	} else
		input_set_capability(ipd, EV_KEY, KEY_MEDIA);

	input_set_capability(ipd, EV_SW, SW_HEADPHONE_INSERT);
	input_set_drvdata(ipd, dd);
	spin_lock_init(&dd->lock);

	rc = pm8058_configure_othc(dd);
	if (rc < 0)
		goto fail_othc_config;

	rc = input_register_device(ipd);
	if (rc) {
		pr_err("%s: Unable to register OTHC device \n", __func__);
		goto fail_othc_config;
	}

	/* Check if the headset is already inserted during boot up */
	rc = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_ir);
	if (rc < 0) {
		pr_err("%s: Unable to get headset status at boot\n", __func__);
		goto fail_ir_irq;
	}
	if (rc) {
		pr_debug("%s: Headset inserted during boot up\n", __func__);
		/* Headset present */
		dd->othc_ir_state = true;
		switch_set_state(&dd->othc_sdev, 1);
		input_report_switch(dd->othc_ipd, SW_HEADPHONE_INSERT, 1);
		input_sync(dd->othc_ipd);
	}

	hrtimer_init(&dd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	dd->timer.function = pm8058_othc_timer;

	rc = request_threaded_irq(dd->othc_irq_ir, NULL, pm8058_nc_ir,
		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED,
				"pm8058_othc_ir", dd);
	if (rc < 0) {
		pr_err("%s: Unable to request pm8058_othc_ir IRQ\n", __func__);
		goto fail_ir_irq;
	}

	/* This irq is used only for NO type headset */
	rc = request_threaded_irq(dd->othc_irq_sw, NULL, pm8058_no_sw,
	IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED,
			"pm8058_othc_sw", dd);
	if (rc < 0) {
		pr_err("%s: Unable to request pm8058_othc_sw IRQ\n",
							__func__);
		goto fail_sw_irq;
	}

	device_init_wakeup(&pd->dev, hsed_config->othc_wakeup);

	INIT_WORK(&dd->headset_work, headset_work_f);

	if (dd->othc_support_n_switch == true)
		INIT_WORK(&dd->switch_work, switch_work_f);

	return 0;

fail_sw_irq:
	free_irq(dd->othc_irq_ir, dd);
fail_ir_irq:
	input_unregister_device(ipd);
	dd->othc_ipd = NULL;
fail_othc_config:
	input_free_device(ipd);
fail_input_alloc:
	switch_dev_unregister(&dd->othc_sdev);
	return rc;
}
コード例 #9
0
static int
othc_configure_hsed(struct pm8058_othc *dd, struct platform_device *pd)
{
	int rc;
	struct input_dev *ipd;
	struct pmic8058_othc_config_pdata *pdata = pd->dev.platform_data;
	struct othc_hsed_config *hsed_config = pdata->hsed_config;

	dd->othc_sdev.name = "h2w";
	dd->othc_sdev.print_name = othc_headset_print_name;

	rc = switch_dev_register(&dd->othc_sdev);
	if (rc) {
		pr_err("%s: Unable to register switch device \n", __func__);
		return rc;
	}

	ipd = input_allocate_device();
	if (ipd == NULL) {
		pr_err("%s: Unable to allocate memory \n", __func__);
		rc = -ENOMEM;
		goto fail_input_alloc;
	}

	/* Get the IRQ for Headset Insert-remove and Switch-press */
	dd->othc_irq_sw = platform_get_irq(pd, 0);
	dd->othc_irq_ir = platform_get_irq(pd, 1);
	if (dd->othc_irq_ir < 0) {
		pr_err("%s: othc resource:IRQ_IR absent \n", __func__);
		rc = -ENXIO;
		goto fail_othc_config;
	}
	if (hsed_config->othc_headset == OTHC_HEADSET_NO) {
		if (dd->othc_irq_sw < 0) {
			pr_err("%s: othc resource:IRQ_SW absent\n", __func__);
			rc = -ENXIO;
			goto fail_othc_config;
		}
	}

	ipd->name = "pmic8058_othc";
	ipd->phys = "pmic8058_othc/input0";
	ipd->dev.parent = &pd->dev;

	input_set_capability(ipd, EV_SW, SW_HEADPHONE_INSERT);
	input_set_capability(ipd, EV_KEY, KEY_MEDIA);

	input_set_drvdata(ipd, dd);

	dd->othc_ipd = ipd;
	dd->othc_sw_state = false;
	dd->othc_ir_state = false;

	if (hsed_config->othc_headset == OTHC_HEADSET_NC) {
		/* Check if NC specific pdata is present */
		if (!hsed_config->othc_nc_gpio_setup ||
					!hsed_config->othc_nc_gpio) {
			pr_err("%s: NC headset pdata missing \n", __func__);
			rc = -EINVAL;
			goto fail_othc_config;
		}
	}

	rc = pm8058_configure_othc(dd);
	if (rc < 0)
		goto fail_othc_config;

	rc = input_register_device(ipd);
	if (rc) {
		pr_err("%s: Unable to register OTHC device \n", __func__);
		goto fail_othc_config;
	}

	/* Check if the headset is already inserted during boot up */
	rc = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_ir);
	if (rc < 0) {
		pr_err("%s: Unable to get headset status at boot\n", __func__);
		goto fail_ir_irq;
	}
	if (rc) {
		pr_debug("%s: Headset inserted during boot up\n", __func__);
		/* Headset present */
		dd->othc_ir_state = true;
		switch_set_state(&dd->othc_sdev, 1);
		input_report_switch(dd->othc_ipd, SW_HEADPHONE_INSERT, 1);
		input_sync(dd->othc_ipd);
	}

	rc = request_irq(dd->othc_irq_ir, pm8058_nc_ir,
				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
				"pm8058_othc_ir", dd);
	if (rc < 0) {
		pr_err("%s: Unable to request pm8058_othc_ir IRQ\n", __func__);
		goto fail_ir_irq;
	}

	if (hsed_config->othc_headset == OTHC_HEADSET_NO) {
		/* This irq is used only for NO type headset */
		rc = request_irq(dd->othc_irq_sw, pm8058_no_sw,
			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
				"pm8058_othc_sw", dd);
		if (rc < 0) {
			pr_err("%s: Unable to request pm8058_othc_sw IRQ \n",
								__func__);
			goto fail_sw_irq;
		}
	} else {
		/* NC type of headset use GPIO for IR */
		rc = hsed_config->othc_nc_gpio_setup();
		if (rc < 0) {
			pr_err("%s: Unable to setup gpio for NC type \n",
								__func__);
			goto fail_sw_irq;
		}
	}

	device_init_wakeup(&pd->dev, hsed_config->othc_wakeup);

	INIT_WORK(&dd->switch_work, switch_work_f);

	return 0;

fail_sw_irq:
	free_irq(dd->othc_irq_ir, dd);
fail_ir_irq:
	input_unregister_device(ipd);
	dd->othc_ipd = NULL;
fail_othc_config:
	input_free_device(ipd);
fail_input_alloc:
	switch_dev_unregister(&dd->othc_sdev);
	return rc;
}
コード例 #10
0
static int __devinit kp_probe(struct platform_device *pdev)
{
	struct keypad_pmic_mogami_platform_data *pdata = pdev->dev.platform_data;
	struct kp_data *dt;
	struct pm8058_chip *pm_chip = platform_get_drvdata(pdev);
	int rc, i;

	if (pm_chip == NULL) {
		dev_err(&pdev->dev, "no parent pm8058\n");
		return -EINVAL;
	}
	if (pdata == NULL) {
		dev_err(&pdev->dev, "no pdata\n");
		return -EINVAL;
	}

	dt = kzalloc(sizeof(struct kp_data), GFP_KERNEL);
	if (dt == NULL)
		return -ENOMEM;

	dt->pm_chip = pm_chip;
	dt->num_keys = pdata->keymap_size;
	dt->pm_gpio_config = pdata->pm_gpio_config;
	dt->keys = kzalloc(dt->num_keys * sizeof(struct kp_key), GFP_KERNEL);
	if (dt->keys == NULL) {
		rc = -ENOMEM;
		goto err_key_alloc_failed;
	}

	platform_set_drvdata(pdev, dt);
	dt->dev	= &pdev->dev;

	dt->input = input_allocate_device();
	if (dt->input == NULL) {
		dev_err(&pdev->dev, "unable to allocate input device\n");
		rc = -ENOMEM;
		goto err_input_alloc_failed;
	}

	for (i = 0; i < dt->num_keys; ++i) {
		dt->keys[i].irq  = pdata->keymap[i].irq;
		dt->keys[i].gpio = pdata->keymap[i].gpio;
		dt->keys[i].code = pdata->keymap[i].code;
		dt->keys[i].wake = pdata->keymap[i].wake;
		/* our irq status will be a bitmask of the block which
		 * contains a certain gpio. since a block is only eight bits
		 * we need to find the correct bit in the block which
		 * reflects the requested gpio */
		dt->keys[i].id   = (pdata->keymap[i].gpio - 1) % 8;

		set_bit(dt->keys[i].code, dt->input->keybit);
		rc = kp_pm_gpio_config(dt, pm_chip, dt->keys[i].gpio);
		if (rc)
			goto err_bad_gpio_config;
	}

	dt->input->name = pdata->input_name;
	dt->input->phys = KP_DEVICE;
	dt->input->dev.parent	= &pdev->dev;
	dt->input->open		= kp_device_open;
	dt->input->close	= kp_device_close;
	dt->input->id.bustype	= BUS_HOST;
	dt->input->id.version	= 0x0001;
	dt->input->id.product	= 0x0001;
	dt->input->id.vendor	= 0x0001;
	set_bit(EV_KEY, dt->input->evbit);
	input_set_drvdata(dt->input, dt);

	rc = input_register_device(dt->input);
	if (rc < 0) {
		dev_err(&pdev->dev, "unable to register keypad input device\n");
		input_free_device(dt->input);
		goto err_input_register_failed;
	}

	for (i = 0; i < dt->num_keys; ++i) {
		rc = pm8058_irq_get_rt_status(dt->pm_chip, dt->keys[i].irq);
		if (rc < 0) {
			dev_err(&dt->input->dev, "unable to get irq status\n");
			/* non-fatal */
		} else {
			dt->keys[i].state = !rc;
			input_report_key(dt->input, dt->keys[i].code, !rc);
		}
		rc = request_threaded_irq(dt->keys[i].irq, NULL, kp_irq,
				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
				IRQF_DISABLED, KP_NAME, &dt->input->dev);
		if (rc < 0) {
			dev_err(&dt->input->dev, "unable to request irq\n");
			goto err_request_irq;
		}
		if (!dt->keys[i].wake)
			disable_irq(dt->keys[i].irq);
		else
			enable_irq_wake(dt->keys[i].irq);
	}

	return 0;

 err_request_irq:
	for (--i; i >= 0; --i)
		free_irq(dt->keys[i].irq, &dt->input->dev);
	input_unregister_device(dt->input);
 err_input_register_failed:
 err_bad_gpio_config:
 err_input_alloc_failed:
	kfree(dt->keys);
 err_key_alloc_failed:
	kfree(dt);
	return rc;
}
コード例 #11
0
static int
othc_configure_hsed(struct pm8058_othc *dd, struct platform_device *pd)
{
	int rc;
	struct input_dev *ipd;
	struct pmic8058_othc_config_pdata *pdata = pd->dev.platform_data;
	struct othc_hsed_config *hsed_config = pdata->hsed_config;

	dd->othc_sdev.name = "h2w";
	dd->othc_sdev.print_name = othc_headset_print_name;

	rc = switch_dev_register(&dd->othc_sdev);
	if (rc) {
		pr_err("Unable to register switch device\n");
		return rc;
	}

	ipd = input_allocate_device();
	if (ipd == NULL) {
		pr_err("Unable to allocate memory\n");
		rc = -ENOMEM;
		goto fail_input_alloc;
	}

	/* Get the IRQ for Headset Insert-remove and Switch-press */
	dd->othc_irq_sw = platform_get_irq(pd, 0);
	dd->othc_irq_ir = platform_get_irq(pd, 1);
	if (dd->othc_irq_ir < 0 || dd->othc_irq_sw < 0) {
		pr_err("othc resource:IRQs absent\n");
		rc = -ENXIO;
		goto fail_micbias_config;
	}

	if (pdata->hsed_name != NULL)
		ipd->name = pdata->hsed_name;
	else
		ipd->name = "pmic8058_othc";

	ipd->phys = "pmic8058_othc/input0";
	ipd->dev.parent = &pd->dev;

	dd->othc_ipd = ipd;
	dd->othc_sw_state = false;
	dd->switch_debounce_ms = hsed_config->switch_debounce_ms;
	dd->othc_support_n_switch = hsed_config->othc_support_n_switch;
	dd->accessory_support = pdata->hsed_config->accessories_support;
	dd->detection_delay_ms = pdata->hsed_config->detection_delay_ms;

	if (dd->othc_support_n_switch == true)
		dd->switch_config = hsed_config->switch_config;

	if (dd->accessory_support == true) {
		dd->accessory_info = pdata->hsed_config->accessories;
		dd->num_accessories = pdata->hsed_config->othc_num_accessories;
		dd->accessories_adc_support =
				pdata->hsed_config->accessories_adc_support;
		dd->accessories_adc_channel =
				pdata->hsed_config->accessories_adc_channel;
		dd->video_out_gpio = pdata->hsed_config->video_out_gpio;
	}

	/* Configure the MIC_BIAS line for headset detection */
	rc = pm8058_configure_micbias(dd);
	if (rc < 0)
		goto fail_micbias_config;

	/* Configure for the switch events */
	rc = pm8058_configure_switch(dd);
	if (rc < 0)
		goto fail_micbias_config;

	/* Configure the accessory */
	if (dd->accessory_support == true) {
		rc = pm8058_configure_accessory(dd);
		if (rc < 0)
			goto fail_micbias_config;
	}

	input_set_drvdata(ipd, dd);
	spin_lock_init(&dd->lock);

	rc = input_register_device(ipd);
	if (rc) {
		pr_err("Unable to register OTHC device\n");
		goto fail_micbias_config;
	}

	hrtimer_init(&dd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	dd->timer.function = pm8058_othc_timer;

	/* Request the HEADSET IR interrupt */
	rc = request_threaded_irq(dd->othc_irq_ir, NULL, pm8058_nc_ir,
		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED,
				"pm8058_othc_ir", dd);
	if (rc < 0) {
		pr_err("Unable to request pm8058_othc_ir IRQ\n");
		goto fail_ir_irq;
	}

	/* Request the  SWITCH press/release interrupt */
	rc = request_threaded_irq(dd->othc_irq_sw, NULL, pm8058_no_sw,
	IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED,
			"pm8058_othc_sw", dd);
	if (rc < 0) {
		pr_err("Unable to request pm8058_othc_sw IRQ\n");
		goto fail_sw_irq;
	}

	/* Check if the accessory is already inserted during boot up */
	rc = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_ir);
	if (rc < 0) {
		pr_err("Unable to get accessory status at boot\n");
		goto fail_ir_irq;
	}
	if (rc) {
		pr_debug("Accessory inserted during boot up\n");
		/* process the data and report the inserted accessory */
		rc = pm8058_accessory_report(dd, 1);
		if (rc)
			pr_debug("Unabele to detect accessory at boot up\n");
	}

	device_init_wakeup(&pd->dev,
			hsed_config->hsed_bias_config->othc_wakeup);

	INIT_DELAYED_WORK(&dd->detect_work, detect_work_f);

	if (dd->othc_support_n_switch == true)
		INIT_WORK(&dd->switch_work, switch_work_f);

	return 0;

fail_sw_irq:
	free_irq(dd->othc_irq_ir, dd);
fail_ir_irq:
	input_unregister_device(ipd);
	dd->othc_ipd = NULL;
fail_micbias_config:
	input_free_device(ipd);
fail_input_alloc:
	switch_dev_unregister(&dd->othc_sdev);
	return rc;
}
コード例 #12
0
static int pm8058_accessory_report(struct pm8058_othc *dd, int status)
{

#if 0
	int i, rc, detected = 0;
	u8 micbias_status, switch_status;

	if (dd->accessory_support == false) {
		/* Report default headset */
		switch_set_state(&dd->othc_sdev, !!status);
		input_report_switch(dd->othc_ipd, SW_HEADPHONE_INSERT,
							!!status);
		input_sync(dd->othc_ipd);
		return 0;
	}

	/* For accessory */
	if (dd->accessory_support == true && status == 0) {
		/* Report removal of the accessory. */

		/*
		 * If the current accessory is video cable, reject the removal
		 * interrupt.
		 */
		pr_info("Accessory [%d] removed\n", dd->curr_accessory);
		if (dd->curr_accessory == OTHC_SVIDEO_OUT)
			return 0;

		switch_set_state(&dd->othc_sdev, 0);
		input_report_switch(dd->othc_ipd, dd->curr_accessory_code, 0);
		input_sync(dd->othc_ipd);
		return 0;
	}

	/* Check the MIC_BIAS status */
	rc = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_ir);
	if (rc < 0) {
		pr_err("Unable to read IR status\n");
		goto fail_ir_accessory;
	}
	micbias_status = !!rc;

	/* Check the switch status */
	rc = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_sw);
	if (rc < 0) {
		pr_err("Unable to read SWITCH status\n");
		goto fail_ir_accessory;
	}
	switch_status = !!rc;

	/* Loop through to check which accessory is connected */
	for (i = 0; i < dd->num_accessories; i++) {
		detected = 0;
		if (dd->accessory_info[i].enabled == false)
			continue;

		if (dd->accessory_info[i].detect_flags & OTHC_MICBIAS_DETECT) {
			if (micbias_status)
				detected = 1;
			else
				continue;
		}
		if (dd->accessory_info[i].detect_flags & OTHC_SWITCH_DETECT) {
			if (switch_status)
				detected = 1;
			else
				continue;
		}
		if (dd->accessory_info[i].detect_flags & OTHC_GPIO_DETECT) {
			rc = gpio_get_value_cansleep(
						dd->accessory_info[i].gpio);
			if (rc < 0)
				continue;

			if (rc ^ dd->accessory_info[i].active_low)
				detected = 1;
			else
				continue;
		}
		if (dd->accessory_info[i].detect_flags & OTHC_ADC_DETECT)
			detected = accessory_adc_detect(dd, i);

		if (detected)
			break;
	}

	if (detected) {
		dd->curr_accessory = dd->accessory_info[i].accessory;
		dd->curr_accessory_code = dd->accessory_info[i].key_code;

		/* if Video out cable detected enable the video path*/
		if (dd->curr_accessory == OTHC_SVIDEO_OUT) {
			pm8058_othc_svideo_enable(
					dd->othc_pdata->micbias_select, true);

		} else {
			switch_set_state(&dd->othc_sdev, dd->curr_accessory);
			input_report_switch(dd->othc_ipd,
						dd->curr_accessory_code, 1);
			input_sync(dd->othc_ipd);
		}
		pr_info("Accessory [%d] inserted\n", dd->curr_accessory);
	} else
		pr_info("Unable to detect accessory. False interrupt!\n");

	return 0;

fail_ir_accessory:
	return rc;
#endif
        return 0;
}
コード例 #13
0
static int __devinit max8903_probe(struct platform_device *pdev)
{
	struct max8903_struct *max_chg;
	struct device *dev = &pdev->dev;
	struct max8903_platform_data *pdata = pdev->dev.platform_data;
    struct pm8058_chip *chip;
	int ret = 0;
    
    //printk("%s\n", __func__);
	max_chg = kzalloc(sizeof(struct max8903_struct), GFP_KERNEL);
	if (max_chg == NULL) {
		dev_err(dev, "Cannot allocate memory.\n");
		return -ENOMEM;
	}
	
	saved_msm_chg = max_chg;
	
	if (pdata == NULL) {
		dev_err(&pdev->dev, "%s no platform data\n", __func__);
		ret = -EINVAL;
		goto out;
	}

	INIT_DELAYED_WORK(&max_chg->charge_work, max8903_charge);
	INIT_DELAYED_WORK(&max_chg->ac_charger, ac_charger_detect);
	wake_lock_init(&max_chg->wl, WAKE_LOCK_SUSPEND, "max8903");
    
	max_chg->dev = &pdev->dev;;
    max_chg->irq = pdata->irq;
    max_chg->cen = pdata->cen;
    max_chg->chg = pdata->chg;
    max_chg->flt = pdata->flt;
	max_chg->usus = pdata->usus;
	max_chg->dock_det = pdata->dock_det;
	max_chg->usb_chg_enable = 1;	/* enable usb charge */

	if (BOARD_NUM(hw_ver) == BOARD_NUM_V11)
		max_chg->adapter_hw_chg.type = CHG_TYPE_AC;
	else
		max_chg->adapter_hw_chg.type = CHG_TYPE_USB;
	max_chg->adapter_hw_chg.rating = 2;
	max_chg->adapter_hw_chg.name = "max8903-charger";
	max_chg->adapter_hw_chg.start_charging = max8903_start_charging;
	max_chg->adapter_hw_chg.stop_charging = max8903_stop_charging;
	max_chg->adapter_hw_chg.charging_switched = max8903_charging_switched;
    
	platform_set_drvdata(pdev, max_chg);

	ret = gpio_request(max_chg->cen, "CHARGER_CEN_N");
	if (ret) {
		dev_err(max_chg->dev, "%s gpio_request failed for %d ret=%d\n",
			__func__, max_chg->cen, ret);
		goto free_max_chg;
	}

	ret = gpio_request(max_chg->chg, "CHARGER_STATUS");
	if (ret) {
		dev_err(max_chg->dev, "%s gpio_request failed for %d ret=%d\n",
			__func__, max_chg->chg, ret);
		goto free_cen;
	}
    gpio_direction_input(max_chg->chg);

    ret = gpio_request(max_chg->flt, "CHARGER_FAULT_N");
	if (ret) {
		dev_err(max_chg->dev, "%s gpio_request failed for %d ret=%d\n",
			__func__, max_chg->flt, ret);
		goto free_chg;
	}    
    gpio_direction_input(max_chg->flt);

    ret = request_threaded_irq(gpio_to_irq(max_chg->flt),
                            NULL, max8903_fault,
                            IRQF_TRIGGER_FALLING ,"MAX8903 Fault", pdev);
    if (ret) {
        dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
                gpio_to_irq(max_chg->flt), ret);
        goto free_flt;
    }

	ret = gpio_request(max_chg->usus, "USUS_CTRL");
	if (ret) {
		dev_err(max_chg->dev, "%s gpio_request failed for %d ret=%d\n",
			__func__, max_chg->usus, ret);
		goto err_flt_irq;
	}

	ret = gpio_request(max_chg->dock_det, "DOCK_DET");
	if (ret) {
		dev_err(max_chg->dev, "%s gpio_request failed for %d ret=%d\n",
			__func__, max_chg->dock_det, ret);
		goto free_usus;
	}
	gpio_direction_input(max_chg->dock_det);
    
	ret = msm_charger_register(&max_chg->adapter_hw_chg);
	if (ret) {
		dev_err(max_chg->dev,
			"%s msm_charger_register failed for ret =%d\n",
			__func__, ret);
		goto free_dock_det;
	}
	
	ret = device_create_file(max_chg->dev, &usb_chg_enable_attr);
	if (ret) {
		dev_err(max_chg->dev, "failed: create usb_chg_enable file\n");
	}
	
	ret = request_threaded_irq(max_chg->irq, NULL,
				   max_valid_handler,
				   IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
				   "max_valid_handler", pdev);
	if (ret) {
		dev_err(max_chg->dev,
			"%s request_threaded_irq failed for %d ret =%d\n",
			__func__, max_chg->irq, ret);
		goto unregister;
	}
	set_irq_wake(max_chg->irq, 1);

    chip = get_irq_data(max_chg->irq);
    ret = pm8058_irq_get_rt_status(chip, max_chg->irq);
	if (ret) {
	#if 0
		if (!gpio_get_value_cansleep(max_chg->dock_det))	/* Dock insert, think it as AC charger */
			max_chg->adapter_hw_chg.type = CHG_TYPE_AC;
		msm_charger_notify_event(&max_chg->adapter_hw_chg,
				CHG_INSERTED_EVENT);
	#else
		/* Charger inserted, but not a valid USB charger
		 * think it as a AC charger
		 */
		if (USB_CHG_TYPE__INVALID == cur_chg_type)
			max_chg->adapter_hw_chg.type = CHG_TYPE_AC;
		
		msm_charger_notify_event(&max_chg->adapter_hw_chg,
				CHG_INSERTED_EVENT);
	#endif
		max_chg->present = 1;
		wake_lock(&max_chg->wl);
	}

	msm_register_usb_charger_state(max8903_usb_charger_state);

	pr_info("%s OK chg_present=%d\n", __func__, max_chg->present);
	return 0;

unregister:
	msm_charger_unregister(&max_chg->adapter_hw_chg);
free_dock_det:
	gpio_free(max_chg->dock_det);
free_usus:
    gpio_free(max_chg->usus);
err_flt_irq:
    free_irq(gpio_to_irq(max_chg->flt), pdev);
free_flt:
    gpio_free(max_chg->flt);
free_chg:
    gpio_free(max_chg->chg);
free_cen:
    gpio_free(max_chg->cen);
free_max_chg:
	kfree(max_chg);
out:
	return ret;
}