lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Tue, 07 Dec 2010 17:40:59 +0530
From:	Anirudh Ghayal <aghayal@...eaurora.org>
To:	Dmitry Torokhov <dmitry.torokhov@...il.com>
CC:	Trilok Soni <tsoni@...eaurora.org>, linux-kernel@...r.kernel.org,
	linux-input@...r.kernel.org, rtc-linux@...glegroups.com,
	linux-arm-msm@...r.kernel.org
Subject: Re: [RFC v1 PATCH 5/6] input: pmic8058-othc: Add support for PM8058
 based OTHC

Hi Dimitry,

Thanks for your comments.

On 12/7/2010 3:34 PM, Dmitry Torokhov wrote:
> Hi Trilok,
>
> On Wed, Nov 10, 2010 at 06:18:00PM +0530, Trilok Soni wrote:
>> From: Anirudh Ghayal<aghayal@...eaurora.org>
>>
>> One-touch headset controller is a hardware module in Qualcomm's PMIC8058.
>> It supports headset insert/remove and switch press/release detection events
>> over 3 MIC BIAS lines. The MIC BIAS lines can be configured to support
>> headset detection or act as regular BIAS lines.
>>
>> Cc: Dmitry Torokhov<dmitry.torokhov@...il.com>
>> Signed-off-by: Anirudh Ghayal<aghayal@...eaurora.org>
>> ---
>>   drivers/input/misc/Kconfig          |   10 +
>>   drivers/input/misc/Makefile         |    1 +
>>   drivers/input/misc/pmic8058-othc.c  |  544 +++++++++++++++++++++++++++++++++++
>>   include/linux/input/pmic8058-othc.h |  117 ++++++++
>>   4 files changed, 672 insertions(+), 0 deletions(-)
>>   create mode 100644 drivers/input/misc/pmic8058-othc.c
>>   create mode 100644 include/linux/input/pmic8058-othc.h
>>
>> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
>> index aeb9165..df6097c 100644
>> --- a/drivers/input/misc/Kconfig
>> +++ b/drivers/input/misc/Kconfig
>> @@ -359,6 +359,16 @@ config INPUT_PMIC8058_PWRKEY
>>   	  To compile this driver as a module, choose M here: the
>>   	  module will be called pmic8058-pwrkey.
>>
>> +config INPUT_PMIC8058_OTHC
>> +	tristate "Qualcomm PMIC8058 OTHC support"
>> +	depends on PMIC8058
>> +	help
>> +	  Say Y here if you want support PMIC8058 One-touch Headset Controller
>> +	  (OTHC)
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called pmic8058-othc.
>> +
>>   config INPUT_GPIO_ROTARY_ENCODER
>>   	tristate "Rotary encoders connected to GPIO pins"
>>   	depends on GPIOLIB&&  GENERIC_GPIO
>> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
>> index c4357a0..a713370 100644
>> --- a/drivers/input/misc/Makefile
>> +++ b/drivers/input/misc/Makefile
>> @@ -29,6 +29,7 @@ obj-$(CONFIG_INPUT_PCAP)		+= pcap_keys.o
>>   obj-$(CONFIG_INPUT_PCF50633_PMU)	+= pcf50633-input.o
>>   obj-$(CONFIG_INPUT_PCF8574)		+= pcf8574_keypad.o
>>   obj-$(CONFIG_INPUT_PCSPKR)		+= pcspkr.o
>> +obj-$(CONFIG_INPUT_PMIC8058_OTHC)	+= pmic8058-othc.o
>>   obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o
>>   obj-$(CONFIG_INPUT_PWM_BEEPER)		+= pwm-beeper.o
>>   obj-$(CONFIG_INPUT_PMIC8058_PWRKEY)	+= pmic8058-pwrkey.o
>> diff --git a/drivers/input/misc/pmic8058-othc.c b/drivers/input/misc/pmic8058-othc.c
>> new file mode 100644
>> index 0000000..78f157a
>> --- /dev/null
>> +++ b/drivers/input/misc/pmic8058-othc.c
>> @@ -0,0 +1,544 @@
>> +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>> + * 02110-1301, USA.
>> + */
>> +
>> +#define pr_fmt(fmt) "%s:" fmt, __func__
>> +
>> +#include<linux/module.h>
>> +#include<linux/init.h>
>> +#include<linux/kernel.h>
>> +#include<linux/slab.h>
>> +#include<linux/interrupt.h>
>> +#include<linux/input.h>
>> +#include<linux/platform_device.h>
>> +#include<linux/pm.h>
>> +
>> +#include<linux/mfd/pmic8058.h>
>> +#include<linux/input/pmic8058-othc.h>
>> +
>> +#define PM8058_OTHC_LOW_CURR_MASK	0xF0
>> +#define PM8058_OTHC_HIGH_CURR_MASK	0x0F
>> +#define PM8058_OTHC_EN_SIG_MASK		0x3F
>> +#define PM8058_OTHC_HYST_PREDIV_MASK	0xC7
>> +#define PM8058_OTHC_CLK_PREDIV_MASK	0xF8
>> +#define PM8058_OTHC_HYST_CLK_MASK	0x0F
>> +#define PM8058_OTHC_PERIOD_CLK_MASK	0xF0
>> +
>> +#define PM8058_OTHC_LOW_CURR_SHIFT	0x4
>> +#define PM8058_OTHC_EN_SIG_SHIFT	0x6
>> +#define PM8058_OTHC_HYST_PREDIV_SHIFT	0x3
>> +#define PM8058_OTHC_HYST_CLK_SHIFT	0x4
>> +
>> +#define PM8058_OTHC_LOW_CURR_MIRROR	10
>> +#define PM8058_OTHC_HIGH_CURR_MIRROR	100
>> +#define PM8058_OTHC_CLK_SRC_SHIFT	10
>> +
>> +/**
>> + * struct pm8058_othc - othc driver data structure
>> + * @othc_ipd: input device for othc
>> + * @othc_pdata:	a pointer to the platform data
>> + * @othc_base: base address of the OTHC controller
>> + * @othc_irq_sw: switch detect irq number
>> + * @othc_irq_ir: headset jack detect irq number
>> + * @othc_sw_state: current state of the switch
>> + * @othc_ir_state: current state of the headset
>> + * @switch_reject: indicates if valid switch press
>> + * @switch_debounce_ms: the debounce time for the switch
>> + * @lock: spin lock variable
>> + * @timer: timer for switch debounce time
>> + * @pm_chip: pointer to the pm8058 parent chip
>> + */
>> +struct pm8058_othc {
>> +	struct input_dev *othc_ipd;
>> +	struct pmic8058_othc_config_pdata *othc_pdata;
>
> const?

Ok.

>
>> +	int othc_base;
>> +	int othc_irq_sw;
>> +	int othc_irq_ir;
>> +	bool othc_sw_state;
>> +	bool othc_ir_state;
>> +	bool switch_reject;
>> +	unsigned long switch_debounce_ms;
>> +	spinlock_t lock;
>> +	struct timer_list timer;
>> +	struct pm8058_chip *pm_chip;
>> +};
>> +
>> +static struct pm8058_othc *config[OTHC_MICBIAS_MAX];
>> +
>> +/**
>> + * pm8058_micbias_enable() - Enables/Disables the MIC_BIAS
>> + * @micbias:	MIC BIAS line which needs to be enabled
>> + * @enable:	operational state for the MIC BIAS line
>> + *
>> + * The API pm8058_micbias_enable()  configures the MIC_BIAS. Only the lines
>> + * which are not used for headset detection can be configured using this API.
>> + * The API returns an error code if it fails to configure, else it returns 0.
>> + */
>> +int pm8058_micbias_enable(enum othc_micbias micbias,
>> +		enum othc_micbias_enable enable)
>> +{
>> +	int rc;
>> +	u8 reg;
>> +	struct pm8058_othc *dd = config[micbias];
>> +
>> +	if (dd == NULL) {
>> +		pr_err("MIC_BIAS not registered, cannot enable\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (dd->othc_pdata->micbias_capability != OTHC_MICBIAS) {
>> +		pr_err("MIC_BIAS enable capability not supported\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	rc = pm8058_read(dd->pm_chip, dd->othc_base + 1,&reg, 1);
>> +	if (rc<  0) {
>> +		pr_err("PM8058 read failed\n");
>> +		return rc;
>> +	}
>> +
>> +	reg&= PM8058_OTHC_EN_SIG_MASK;
>> +	reg |= (enable<<  PM8058_OTHC_EN_SIG_SHIFT);
>> +
>> +	rc = pm8058_write(dd->pm_chip, dd->othc_base + 1,&reg, 1);
>> +	if (rc<  0) {
>> +		pr_err("PM8058 write failed\n");
>> +		return rc;
>> +	}
>> +
>> +	return rc;
>> +}
>> +EXPORT_SYMBOL(pm8058_micbias_enable);
>> +
>> +#ifdef CONFIG_PM
>> +static int pm8058_othc_suspend(struct device *dev)
>> +{
>> +	struct pm8058_othc *dd = dev_get_drvdata(dev);
>> +
>> +	if (dd->othc_pdata->micbias_capability == OTHC_MICBIAS_HSED) {
>> +		if (device_may_wakeup(dev)) {
>> +			enable_irq_wake(dd->othc_irq_sw);
>> +			enable_irq_wake(dd->othc_irq_ir);
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int pm8058_othc_resume(struct device *dev)
>> +{
>> +	struct pm8058_othc *dd = dev_get_drvdata(dev);
>> +
>> +	if (dd->othc_pdata->micbias_capability == OTHC_MICBIAS_HSED) {
>> +		if (device_may_wakeup(dev)) {
>> +			disable_irq_wake(dd->othc_irq_sw);
>> +			disable_irq_wake(dd->othc_irq_ir);
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct dev_pm_ops pm8058_othc_pm_ops = {
>> +	.suspend = pm8058_othc_suspend,
>> +	.resume = pm8058_othc_resume,
>> +};
>> +#endif
>> +
>> +static int __devexit pm8058_othc_remove(struct platform_device *pd)
>> +{
>> +	struct pm8058_othc *dd = platform_get_drvdata(pd);
>> +
>> +	if (dd->othc_pdata->micbias_capability == OTHC_MICBIAS_HSED) {
>
> Why do we even bind to devices that are not OTHC_MICBIAS_HSED?

On PMIC 8058 we have 3 MIC_BIAS lines and all 3 have the capability to 
support HSED OTHC controller.  So, I register all the 3 BIAS lines, 
though only the ones which are enabled for HSED regitser as input 
devices. Also, I export an API pm8058_micbias_enable (above) which 
allows to configure the non-HSED lines as regualr MIC_BIAS lines.

>
>> +		device_init_wakeup(&pd->dev, 0);
>> +		free_irq(dd->othc_irq_sw, dd);
>> +		free_irq(dd->othc_irq_ir, dd);
>> +		del_timer_sync(&dd->timer);
>> +		input_unregister_device(dd->othc_ipd);
>> +	}
>> +
>> +	kfree(dd);
>> +
>> +	return 0;
>> +}
>> +
>> +static void pm8058_othc_timer(unsigned long handle)
>> +{
>> +	unsigned long flags;
>> +	struct pm8058_othc *dd = (struct pm8058_othc *)handle;
>> +
>> +	spin_lock_irqsave(&dd->lock, flags);
>> +	dd->switch_reject = false;
>> +	spin_unlock_irqrestore(&dd->lock, flags);
>> +}
>> +
>> +static irqreturn_t pm8058_no_sw(int irq, void *dev_id)
>> +{
>> +	struct pm8058_othc *dd = dev_id;
>> +	unsigned long flags;
>> +
>> +	/*
>> +	 * Due to a hardware bug, spurious switch interrutps are seen while
>> +	 * inserting the headset slowly. A timer based logic rejects these
>> +	 * switch interrutps.
>> +	 */
>> +	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);
>
> I am not quite sure whether this locking helps anything... You do
> protect the check by condition can change once you leave the protected
> section since both IRQ handles and timer can run simultaneously...

Sorry, I didn't quite get your concern. Here, only the timer updates the 
varible and the int. handler checks for the condition and returns. As 
these both can occur in parallel we need a lock.

>
>> +
>> +	if (dd->othc_sw_state == false) {
>> +		dd->othc_sw_state = true;
>> +		input_report_key(dd->othc_ipd, KEY_MEDIA, 1);
>> +	} else if (dd->othc_sw_state == true) {
>> +		dd->othc_sw_state = false;
>> +		input_report_key(dd->othc_ipd, KEY_MEDIA, 0);
>> +	}
>> +	input_sync(dd->othc_ipd);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t pm8058_nc_ir(int irq, void *dev_id)
>> +{
>> +	unsigned long flags;
>> +	struct pm8058_othc *dd = dev_id;
>> +
>> +	spin_lock_irqsave(&dd->lock, flags);
>> +	dd->switch_reject = true;
>> +	spin_unlock_irqrestore(&dd->lock, flags);
>> +
>> +	mod_timer(&dd->timer, jiffies +
>> +		msecs_to_jiffies(dd->switch_debounce_ms));
>> +
>> +	if (dd->othc_ir_state == false) {
>> +		dd->othc_ir_state = true;
>> +		input_report_key(dd->othc_ipd, SW_HEADPHONE_INSERT, 1);
>> +	} else {
>> +		dd->othc_ir_state = false;
>> +		input_report_key(dd->othc_ipd, SW_HEADPHONE_INSERT, 0);
>> +	}
>> +
>> +	input_sync(dd->othc_ipd);
>
> 	dd->othc_ir_state = !dd->othc_ir_state;
> 	input_report_key(dd->othc_ipd, SW_HEADPHONE_INSERT, dd->othc_ir_state);
> 	input_sync(dd->othc_ipd);

Simpler :).

>
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int pm8058_configure_othc(struct pm8058_othc *dd)
>> +{
>> +	int rc;
>> +	u8 reg, value;
>> +	u32 value1;
>> +	u16 base_addr = dd->othc_base;
>> +	struct othc_hsed_config *hsed_config = dd->othc_pdata->hsed_config;
>> +
>> +	/* Control Register 1*/
>> +	rc = pm8058_read(dd->pm_chip, base_addr,&reg, 1);
>> +	if (rc<  0) {
>> +		pr_err("PM8058 read failed\n");
>> +		return rc;
>> +	}
>> +
>> +	if (hsed_config->othc_headset == OTHC_HEADSET_NO) {
>> +		/* set iDAC high current threshold */
>> +		value = (hsed_config->othc_highcurr_thresh_uA /
>> +					PM8058_OTHC_HIGH_CURR_MIRROR) - 2;
>> +		reg =  (reg&  PM8058_OTHC_HIGH_CURR_MASK) | value;
>> +	} else {
>> +		/* set iDAC low current threshold */
>> +		value = (hsed_config->othc_lowcurr_thresh_uA /
>> +					PM8058_OTHC_LOW_CURR_MIRROR) - 1;
>> +		reg&= PM8058_OTHC_LOW_CURR_MASK;
>> +		reg |= (value<<  PM8058_OTHC_LOW_CURR_SHIFT);
>> +	}
>> +
>> +	rc = pm8058_write(dd->pm_chip, base_addr,&reg, 1);
>> +	if (rc<  0) {
>> +		pr_err("PM8058 read failed\n");
>> +		return rc;
>> +	}
>> +
>> +	/* Control register 2*/
>> +	rc = pm8058_read(dd->pm_chip, base_addr + 1,&reg, 1);
>> +	if (rc<  0) {
>> +		pr_err("PM8058 read failed\n");
>> +		return rc;
>> +	}
>> +
>> +	value = dd->othc_pdata->micbias_enable;
>> +	reg&= PM8058_OTHC_EN_SIG_MASK;
>> +	reg |= (value<<  PM8058_OTHC_EN_SIG_SHIFT);
>> +
>> +	value = 0;
>> +	value1 = (hsed_config->othc_hyst_prediv_us<<
>> +				PM8058_OTHC_CLK_SRC_SHIFT) / USEC_PER_SEC;
>> +	while (value1 != 0) {
>> +		value1 = value1>>  1;
>> +		value++;
>> +	}
>> +	if (value>  7) {
>> +		pr_err("Invalid input argument - othc_hyst_prediv_us\n");
>> +		return -EINVAL;
>> +	}
>> +	reg&= PM8058_OTHC_HYST_PREDIV_MASK;
>> +	reg |= (value<<  PM8058_OTHC_HYST_PREDIV_SHIFT);
>> +
>> +	value = 0;
>> +	value1 = (hsed_config->othc_period_clkdiv_us<<
>> +				PM8058_OTHC_CLK_SRC_SHIFT) / USEC_PER_SEC;
>> +	while (value1 != 1) {
>> +		value1 = value1>>  1;
>> +		value++;
>> +	}
>> +	if (value>  8) {
>> +		pr_err("Invalid input argument - othc_period_clkdiv_us\n");
>> +		return -EINVAL;
>> +	}
>> +	reg = (reg&   PM8058_OTHC_CLK_PREDIV_MASK) | (value - 1);
>> +
>> +	rc = pm8058_write(dd->pm_chip, base_addr + 1,&reg, 1);
>> +	if (rc<  0) {
>> +		pr_err("PM8058 read failed\n");
>> +		return rc;
>> +	}
>> +
>> +	/* Control register 3 */
>> +	rc = pm8058_read(dd->pm_chip, base_addr + 2 ,&reg, 1);
>> +	if (rc<  0) {
>> +		pr_err("PM8058 read failed\n");
>> +		return rc;
>> +	}
>> +
>> +	value = hsed_config->othc_hyst_clk_us /
>> +					hsed_config->othc_hyst_prediv_us;
>> +	if (value>  15) {
>> +		pr_err("Invalid input argument - othc_hyst_prediv_us\n");
>> +		return -EINVAL;
>> +	}
>> +	reg&= PM8058_OTHC_HYST_CLK_MASK;
>> +	reg |= value<<  PM8058_OTHC_HYST_CLK_SHIFT;
>> +
>> +	value = hsed_config->othc_period_clk_us /
>> +					hsed_config->othc_period_clkdiv_us;
>> +	if (value>  15) {
>> +		pr_err("Invalid input argument - othc_hyst_prediv_us\n");
>> +		return -EINVAL;
>> +	}
>> +	reg = (reg&  PM8058_OTHC_PERIOD_CLK_MASK) | value;
>> +
>> +	rc = pm8058_write(dd->pm_chip, base_addr + 2,&reg, 1);
>> +	if (rc<  0) {
>> +		pr_err("PM8058 read failed\n");
>> +		return rc;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int
>> +pm8058_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;
>
> const?
>
>> +	struct othc_hsed_config *hsed_config = pdata->hsed_config;
>
> And here as well?
>

Sure.

>> +
>> +	ipd = input_allocate_device();
>> +	if (ipd == NULL) {
>> +		dev_err(&pd->dev, "Memory allocate to input device failed!\n");
>> +		rc = -ENOMEM;
>> +		goto fail_input_alloc;
>> +	}
>> +
>> +	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) {
>> +		dev_err(&pd->dev, "othc resource:IRQ_IR absent!\n");
>> +		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);
>
> What exactly this button is supposed to do? I do not think KEY_MEDIA is
> the one you need here.

This key acts as send/end key while in call and play/pause in case of 
audio playback. So we used KEY_MEDIA as a generic media key. Could you 
please suggest what can be used here?

>
>
>> +
>> +	input_set_drvdata(ipd, dd);
>> +
>> +	dd->othc_ipd = ipd;
>> +	dd->othc_sw_state = false;
>> +	dd->othc_ir_state = false;
>> +	spin_lock_init(&dd->lock);
>> +	dd->switch_debounce_ms = hsed_config->switch_debounce_ms;
>> +	setup_timer(&dd->timer, pm8058_othc_timer, (unsigned long) dd);
>> +
>> +	rc = pm8058_configure_othc(dd);
>> +	if (rc<  0)
>> +		goto fail_othc_config;
>> +
>> +	rc = input_register_device(ipd);
>> +	if (rc) {
>> +		dev_err(&pd->dev, "Register OTHC device failed!\n");
>> +		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) {
>> +		dev_err(&pd->dev, "Unable to get headset status at boot!\n");
>> +		goto fail_ir_irq;
>> +	}
>> +	if (rc) {
>> +		dev_dbg(&pd->dev, "Headset inserted during boot up!\n");
>> +		dd->othc_ir_state = true;
>> +		input_report_switch(dd->othc_ipd, SW_HEADPHONE_INSERT, 1);
>> +		input_sync(dd->othc_ipd);
>> +	}
>> +
>> +	rc = request_any_context_irq(dd->othc_irq_ir, pm8058_nc_ir,
>> +		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
>> +					"pm8058_othc_ir", dd);
>
> Hmm, non-threaded IRQs do not support IRQF_ONESHOT, do they? BTW, is
> oneshot really needed here?

Right, I donot need IRQF_ONESHOT here.

>
>> +	if (rc<  0) {
>> +		dev_err(&pd->dev, "Request pm8058_othc_ir IRQ failed!\n");
>> +		goto fail_ir_irq;
>> +	}
>> +
>> +	rc = request_any_context_irq(dd->othc_irq_sw, pm8058_no_sw,
>> +		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
>> +					"pm8058_othc_sw", dd);
>> +	if (rc<  0) {
>> +		dev_err(&pd->dev, "Request pm8058_othc_sw IRQ failed!\n");
>> +		goto fail_sw_irq;
>> +	}
>> +
>> +	device_init_wakeup(&pd->dev, hsed_config->othc_wakeup);
>> +
>> +	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:
>> +	return rc;
>> +}
>> +
>> +static int __devinit pm8058_othc_probe(struct platform_device *pd)
>> +{
>> +	int rc;
>> +	struct pm8058_othc *dd;
>> +	struct pm8058_chip *chip;
>> +	struct resource *res;
>> +	struct pmic8058_othc_config_pdata *pdata = pd->dev.platform_data;
>> +
>> +	chip = platform_get_drvdata(pd);
>> +	if (chip == NULL) {
>> +		dev_err(&pd->dev, "Invalid driver information!\n");
>> +		return  -EINVAL;
>> +	}
>> +
>> +	/* PMIC8058 version A0 not supported */
>> +	if (pm8058_rev(chip) == PM_8058_REV_1p0) {
>> +		dev_err(&pd->dev, "PMIC8058 version not supported!\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (pdata == NULL) {
>> +		dev_err(&pd->dev, "Platform data not present!\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
>> +	if (dd == NULL) {
>> +		dev_err(&pd->dev, "Unable to allocate memory!\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	res = platform_get_resource_byname(pd, IORESOURCE_IO, "othc_base");
>> +	if (res == NULL) {
>> +		dev_err(&pd->dev, "OTHC Base address, resource absent!\n");
>> +		rc = -ENXIO;
>> +		goto fail_get_res;
>> +	}
>> +
>> +	dd->othc_pdata = pdata;
>> +	dd->pm_chip = chip;
>> +	dd->othc_base = res->start;
>> +
>> +	platform_set_drvdata(pd, dd);
>> +
>> +	if (pdata->micbias_capability == OTHC_MICBIAS_HSED) {
>> +		/* HSED to be supported on this MICBIAS line */
>> +		if (pdata->hsed_config != NULL) {
>> +			rc = pm8058_othc_configure_hsed(dd, pd);
>> +			if (rc<  0)
>> +				goto fail_get_res;
>> +		} else {
>> +			dev_err(&pd->dev, "HSED config data absent!\n");
>> +			rc = -EINVAL;
>> +			goto fail_get_res;
>> +		}
>> +	}
>> +
>> +	/* Store the local driver data structure */
>> +	if (dd->othc_pdata->micbias_select<  OTHC_MICBIAS_MAX)
>> +		config[dd->othc_pdata->micbias_select] = dd;
>> +
>> +	dev_dbg(&pd->dev, "Device %s:%d successfully registered\n",
>> +						pd->name, pd->id);
>> +	return 0;
>> +
>> +fail_get_res:
>> +	kfree(dd);
>> +	return rc;
>> +}
>> +
>
> Thanks.
>


-- 
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists