Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 294db64b authored by Houston Hoffman's avatar Houston Hoffman
Browse files

NFC: add gpio clk interrupt for 8610 qrd device



Major rework for platforms where clock reference to qca199x is
sourced via MSM rather than PMIC. When the clock is sourced via the MSM
the CLK_REQ to the MSM does not control a clock enable pin (as in the
case of the PMIC), rather it is a standard MSM GPIO. In this instance, we
have implemented another interrupt handler which fires on both edges of
the CLK_REQ(from the qca199x). The control/mux of the CLK_REF is then
switched in the linked BH process. This has been tested on the 8x10 Hisense
qrd (MSM CLK_REF) and 8x26 MTP (PMIC CLK_REF) platforms. For other
platforms the configuration will be sourced from their respective device
tree files, however, there may still be some further aditional conditions
which need to be added to this driver.

Change-Id: I9aa62b9c2b07e7c6bbad7fdbc8e49e8c1b3babf8
Acked-by: default avatarUmesh Jagga <ujagga@qti.qualcomm.com>
Signed-off-by: default avatarHouston Hoffman <hhoffman@codeaurora.org>
parent f75c756e
Loading
Loading
Loading
Loading
+8 −6
Original line number Diff line number Diff line
@@ -8,13 +8,15 @@ Required properties:
- reg: NCI i2c slave address.
- qcom,dis-gpio: specific gpio for hardware reset.
- qcom,irq-gpio: specific gpio for read interrupt.
- qcom,clk-src: nfc clock source ("BBCLK2", "RFCLK3", "GPCLK", "GPCLK2" ...)
- qcom,clk-en-gpio: msm gpio clock,used ony if clock source is msm gpio
- qcom,clk-src: nfc clock source ("BBCLK2", "RFCLK3", "GPCLK", "GPCLK2", ...)
- qcom,clk-src-gpio: msm gpio clock,used ony if clock source is msm gpio
- qcom,clk-req-gpio: clk-req input gpio for MSM based clocks.
                     not used for pmic implementation
- vlogic-supply: LDO for power supply
- interrupt-parent: Should be phandle for the interrupt controller
                    that services interrupts for this device.
- interrupts: should contain the NFC interrupt. NFC has one read interrupt.
- qcom,clk-gpio: pmic gpio on which bbclk2 signal is coming.
- interrupts: Nfc read interrupt,gpio-clk-req interrupt
- qcom,clk-gpio: pmic or msm gpio on which bbclk2 signal is coming.

LDO example:

@@ -24,8 +26,8 @@ LDO example:
			reg = <0x0e>;
			qcom,irq-gpio = <&msmgpio 77 0x00>;
			qcom,dis-gpio = <&msmgpio 93 0x00>;
			qcom,clk-en-gpio = <&msmgpio 78 0x00>;
			qcom,clk-src = "GPCLK";
			qcom,clk-src-gpio = <&msmgpio 78 0x00>;
			qcom,clk-src = "GPCLK2";
			interrupt-parent = <&msmgpio>;
			interrupts = <77 0>;
			qcom,clk-gpio = <&msmgpio 75 0x00>;
+3 −2
Original line number Diff line number Diff line
@@ -151,10 +151,11 @@
			reg = <0x0e>;
			qcom,irq-gpio = <&msmgpio 77 0x00>;
			qcom,dis-gpio = <&msmgpio 93 0x00>;
			qcom,clk-en-gpio = <&msmgpio 78 0x00>;
			qcom,clk-req-gpio = <&msmgpio 75 0x00>;
			qcom,clk-src-gpio = <&msmgpio 78 0x00>;
			qcom,clk-src = "GPCLK";
			interrupt-parent = <&msmgpio>;
			interrupts = <77 0>;
			interrupts = <77 75 0>;
			qcom,clk-gpio = <&pm8110_gpios 1 0>;
		};
	};
+6 −6
Original line number Diff line number Diff line
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2014, The Linux Foundation. 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
@@ -28,18 +28,18 @@
        qcom,ext-spk-amp-gpio = <&msmgpio 92 0x0>;
    };

	i2c@f9925000 { /* BLSP-1 QUP-3 */
	i2c@f9924000 { /* BLSP-1 QUP-2 */
		nfc-nci@e {
			compatible = "qcom,nfc-nci";
			reg = <0x0e>;
			qcom,irq-gpio = <&msmgpio 77 0x00>;
			qcom,clk-req-gpio = <&msmgpio 75 0x00>;
			qcom,dis-gpio = <&msmgpio 93 0x00>;
			qcom,clk-en-gpio = <&msmgpio 78 0x00>;
			qcom,clk-src = "GPCLK";
			qcom,clk-src-gpio = <&msmgpio 78 0x00>;
			qcom,clk-src = "GPCLK2";
			interrupt-parent = <&msmgpio>;
			interrupts = <77 0>;
			interrupts = <77 75 0>;
			qcom,clk-gpio = <&msmgpio 75 0x00>;
			vlogic-supply = <&pm8110_l14>;
		};
	};
};
+7 −0
Original line number Diff line number Diff line
@@ -613,6 +613,13 @@ static struct gpiomux_setting interrupt_gpio_suspend_pulldown = {
};

static struct msm_gpiomux_config msm_interrupt_configs[] __initdata = {
	{
		.gpio = 75,	/* NFC_CLK_REQ_IRQ*/
		.settings = {
			[GPIOMUX_ACTIVE]    = &interrupt_gpio_active,
			[GPIOMUX_SUSPENDED] = &interrupt_gpio_suspend_pullup,
		},
	},
	{
		.gpio = 77,	/* NFC_IRQ */
		.settings = {
+262 −52
Original line number Diff line number Diff line
@@ -31,8 +31,10 @@

struct qca199x_platform_data {
	unsigned int irq_gpio;
	unsigned int	irq_gpio_clk_req;
	unsigned int	clk_req_irq_num;
	unsigned int dis_gpio;
	unsigned int ven_gpio;
	unsigned int clkreq_gpio;
	unsigned int reg;
	const char *clk_src_name;
	unsigned int clk_src_gpio;
@@ -58,24 +60,38 @@ MODULE_DEVICE_TABLE(of, msm_match_table);
#define	CORE_RESET_OID			(0x00)
#define CORE_RST_NTF_LENGTH		(0x02)

static void clk_req_update(struct work_struct *work);

struct qca199x_dev {
	wait_queue_head_t read_wq;
	struct	mutex		read_mutex;
	struct	i2c_client	*client;
	struct	miscdevice	qca199x_device;
	/* NFC_IRQ new NCI data available */
	unsigned int		irq_gpio;
	/* CLK_REQ IRQ to signal the state has changed */
	unsigned int		irq_gpio_clk_req;
	/* Actual IRQ no. assigned to CLK_REQ */
	unsigned int		clk_req_irq_num;
	unsigned int		dis_gpio;
	unsigned int ven_gpio;
	unsigned int		clkreq_gpio;
	/* NFC_IRQ state */
	bool			irq_enabled;
	bool			sent_first_nci_write;
	spinlock_t		irq_enabled_lock;
	unsigned int		count_irq;
	/* CLK_REQ IRQ state */
	bool			irq_enabled_clk_req;
	spinlock_t		irq_enabled_lock_clk_req;
	unsigned int		count_irq_clk_req;
	enum	nfcc_state	state;
	/* CLK control */
	unsigned int		clk_src_gpio;
	const	char		*clk_src_name;
	struct	clk		*s_clk;
	bool			clk_run;
	struct work_struct	msm_clock_controll_work;
	struct workqueue_struct *my_wq;
};

static int nfc_i2c_write(struct i2c_client *client, u8 *buf, int len);
@@ -97,6 +113,7 @@ static struct devicemode device_mode;
static int					ftm_raw_write_mode;
static int					ftm_werr_code;


unsigned int	disable_ctrl;

static void qca199x_init_stat(struct qca199x_dev *qca199x_dev)
@@ -159,6 +176,95 @@ static unsigned int nfc_poll(struct file *filp, poll_table *wait)
	return mask;
}

/* Handlers for CLK_REQ */
static void qca199x_disable_irq_clk_req(struct qca199x_dev *qca199x_dev)
{
	unsigned long flags;

	spin_lock_irqsave(&qca199x_dev->irq_enabled_lock_clk_req, flags);
	if (qca199x_dev->irq_enabled_clk_req) {
		disable_irq_nosync(qca199x_dev->clk_req_irq_num);
		qca199x_dev->irq_enabled_clk_req = false;
	}
	spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock_clk_req, flags);
}


static void qca199x_enable_irq_clk_req(struct qca199x_dev *qca199x_dev)
{
	unsigned long flags;

	spin_lock_irqsave(&qca199x_dev->irq_enabled_lock_clk_req, flags);
	if (!qca199x_dev->irq_enabled_clk_req) {
		qca199x_dev->irq_enabled_clk_req = true;
		enable_irq(qca199x_dev->clk_req_irq_num);
	}
	spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock_clk_req, flags);
}


static irqreturn_t qca199x_dev_irq_handler_clk_req(int irq, void *dev_id)
{
	struct qca199x_dev *qca199x_dev = dev_id;
	unsigned long flags;

	spin_lock_irqsave(&qca199x_dev->irq_enabled_lock_clk_req, flags);
	qca199x_dev->count_irq_clk_req++;
	spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock_clk_req, flags);

	queue_work(qca199x_dev->my_wq, &qca199x_dev->msm_clock_controll_work);

	return IRQ_HANDLED;
}


static struct gpiomux_setting nfc_clk_on = {
	.func = GPIOMUX_FUNC_2,
	.drv  = GPIOMUX_DRV_2MA,
	.pull = GPIOMUX_PULL_NONE,
};
static struct gpiomux_setting nfc_clk_on_suspend = {
	.func = GPIOMUX_FUNC_2,
	.drv  = GPIOMUX_DRV_2MA,
	.pull = GPIOMUX_PULL_DOWN,
};
static struct gpiomux_setting nfc_clk_off = {
	.func = GPIOMUX_FUNC_GPIO,
	.drv  = GPIOMUX_DRV_2MA,
	.pull = GPIOMUX_PULL_DOWN,
};

static void clk_req_update(struct work_struct *work)
{
	struct	i2c_client *client;
	struct	qca199x_dev *qca199x_dev;
	int		gpio_clk_req_level = 0;

	qca199x_dev =	container_of(work, struct qca199x_dev,
			msm_clock_controll_work);
	client =	qca199x_dev->client;

	/* Read status level of CLK_REQ from NFC Controller, QCA199_x */
	gpio_clk_req_level = gpio_get_value(qca199x_dev->irq_gpio_clk_req);
	if (gpio_clk_req_level == 1) {
		if (qca199x_dev->clk_run == false) {
			msm_gpiomux_write(qca199x_dev->clk_src_gpio,
				GPIOMUX_ACTIVE, &nfc_clk_on, NULL);
			msm_gpiomux_write(qca199x_dev->clk_src_gpio,
				GPIOMUX_SUSPENDED, &nfc_clk_on_suspend, NULL);
			qca199x_dev->clk_run = true;
			}
	} else{
		if (qca199x_dev->clk_run == true) {
			msm_gpiomux_write(qca199x_dev->clk_src_gpio,
				GPIOMUX_ACTIVE, &nfc_clk_off, NULL);
			msm_gpiomux_write(qca199x_dev->clk_src_gpio,
				GPIOMUX_SUSPENDED, &nfc_clk_off, NULL);
			qca199x_dev->clk_run = false;
		}
	}
}

/*
 * ONLY for FTM-RAW-I2C Mode
 * Required to instigate a read, which comes from DT layer. This means we need
@@ -399,7 +505,14 @@ static int nfc_open(struct inode *inode, struct file *filp)

	filp->private_data = qca199x_dev;
	qca199x_init_stat(qca199x_dev);
	/* Enable interrupts from NFCC NFC_INT new NCI data available */
	qca199x_enable_irq(qca199x_dev);

	if ((!strcmp(qca199x_dev->clk_src_name, "GPCLK")) ||
		(!strcmp(qca199x_dev->clk_src_name, "GPCLK2"))) {
		/* Enable interrupts from NFCC CLK_REQ */
		qca199x_enable_irq_clk_req(qca199x_dev);
	}
	dev_dbg(&qca199x_dev->client->dev,
			"%d,%d\n", imajor(inode), iminor(inode));
	return ret;
@@ -1006,10 +1119,6 @@ static int nfc_parse_dt(struct device *dev, struct qca199x_platform_data *pdata)
	if (r)
		return -EINVAL;

	r = of_property_read_u32(np, "qcom,clk-gpio", &pdata->ven_gpio);
	if (r)
		return -EINVAL;

	pdata->dis_gpio = of_get_named_gpio(np, "qcom,dis-gpio", 0);
	if ((!gpio_is_valid(pdata->dis_gpio)))
		return -EINVAL;
@@ -1021,11 +1130,24 @@ static int nfc_parse_dt(struct device *dev, struct qca199x_platform_data *pdata)

	r = of_property_read_string(np, "qcom,clk-src", &pdata->clk_src_name);

	if (strcmp(pdata->clk_src_name, "GPCLK2")) {
		r = of_property_read_u32(np, "qcom,clk-gpio",
					&pdata->clkreq_gpio);
		if (r)
			return -EINVAL;
	}

	if ((!strcmp(pdata->clk_src_name, "GPCLK")) ||
	    (!strcmp(pdata->clk_src_name, "GPCLK2")))
	    (!strcmp(pdata->clk_src_name, "GPCLK2"))) {
			pdata->clk_src_gpio = of_get_named_gpio(np,
				"qcom,clk-en-gpio", 0);

					"qcom,clk-src-gpio", 0);
			if ((!gpio_is_valid(pdata->clk_src_gpio)))
				return -EINVAL;
			pdata->irq_gpio_clk_req = of_get_named_gpio(np,
					"qcom,clk-req-gpio", 0);
			if ((!gpio_is_valid(pdata->irq_gpio_clk_req)))
				return -EINVAL;
	}
	if (r)
		return -EINVAL;
	return r;
@@ -1089,7 +1211,6 @@ static int qca199x_probe(struct i2c_client *client,
				platform_data->irq_gpio);
			goto err_irq;
		}
		gpio_to_irq(0);
		irqn = gpio_to_irq(platform_data->irq_gpio);
		if (irqn < 0) {
			r = irqn;
@@ -1101,13 +1222,45 @@ static int qca199x_probe(struct i2c_client *client,
		dev_err(&client->dev, "irq gpio not provided\n");
		goto err_free_dev;
	}
	/* Interrupt from NFCC CLK_REQ to handle REF_CLK
		o/p gating/selection */
	if ((!strcmp(platform_data->clk_src_name, "GPCLK")) ||
		(!strcmp(platform_data->clk_src_name, "GPCLK2"))) {
		if (gpio_is_valid(platform_data->irq_gpio_clk_req)) {
			r = gpio_request(platform_data->irq_gpio_clk_req,
				"nfc_irq_gpio_clk_en");
			if (r) {
				dev_err(&client->dev, "unable to request CLK_EN gpio [%d]\n",
					platform_data->irq_gpio_clk_req);
				goto err_irq;
			}
			r = gpio_direction_input(
					platform_data->irq_gpio_clk_req);
			if (r) {
				dev_err(&client->dev,
					"unable to set direction for CLK_EN gpio [%d]\n",
					platform_data->irq_gpio_clk_req);
				goto err_irq_clk;
			}
			gpio_to_irq(0);
			irqn = gpio_to_irq(platform_data->irq_gpio_clk_req);
			if (irqn < 0) {
				r = irqn;
				goto err_irq_clk;
			}
			platform_data->clk_req_irq_num = irqn;
		} else {
			dev_err(&client->dev, "irq CLK_EN gpio not provided\n");
			goto err_irq;
		}
	}
	if (gpio_is_valid(platform_data->dis_gpio)) {
		r = gpio_request(platform_data->dis_gpio, "nfc_reset_gpio");
		if (r) {
			dev_err(&client->dev,
			"NFC: unable to request gpio [%d]\n",
				platform_data->dis_gpio);
			goto err_irq;
			goto err_irq_clk;
		}
		r = gpio_direction_output(platform_data->dis_gpio, 1);
		if (r) {
@@ -1118,7 +1271,7 @@ static int qca199x_probe(struct i2c_client *client,
		}
	} else {
		dev_err(&client->dev, "dis gpio not provided\n");
		goto err_irq;
		goto err_irq_clk;
	}
	/* Get the clock source name and gpio from from Device Tree */
	qca199x_dev->clk_src_name = platform_data->clk_src_name;
@@ -1131,35 +1284,47 @@ static int qca199x_probe(struct i2c_client *client,
		else
			goto err_dis_gpio;
	}
	platform_data->ven_gpio = of_get_named_gpio(node,
						"qcom,clk-gpio", 0);

	if (gpio_is_valid(platform_data->ven_gpio)) {
		r = gpio_request(platform_data->ven_gpio, "nfc_ven_gpio");
	if (strcmp(platform_data->clk_src_name, "GPCLK2")) {
		platform_data->clkreq_gpio =
			of_get_named_gpio(node, "qcom,clk-gpio", 0);

		if (gpio_is_valid(platform_data->clkreq_gpio)) {
			r = gpio_request(platform_data->clkreq_gpio,
				"nfc_clkreq_gpio");
			if (r) {
				dev_err(&client->dev, "unable to request gpio [%d]\n",
						platform_data->ven_gpio);
			goto err_ven_gpio;
						platform_data->clkreq_gpio);
				goto err_clkreq_gpio;
			}
		r = gpio_direction_input(platform_data->ven_gpio);
			r = gpio_direction_input(platform_data->clkreq_gpio);
			if (r) {
				dev_err(&client->dev,
						"unable to set direction for gpio [%d]\n",
						platform_data->ven_gpio);
			goto err_ven_gpio;
						platform_data->clkreq_gpio);
				goto err_clkreq_gpio;
			}
		} else {
		dev_err(&client->dev, "ven gpio not provided\n");
			dev_err(&client->dev, "clkreq gpio not provided\n");
			goto err_clk;
		}
		qca199x_dev->clkreq_gpio = platform_data->clkreq_gpio;
	}
	qca199x_dev->dis_gpio = platform_data->dis_gpio;
	qca199x_dev->irq_gpio = platform_data->irq_gpio;
	qca199x_dev->ven_gpio = platform_data->ven_gpio;
	if ((!strcmp(platform_data->clk_src_name, "GPCLK")) ||
		(!strcmp(platform_data->clk_src_name, "GPCLK2"))) {
			qca199x_dev->irq_gpio_clk_req	=
						platform_data->irq_gpio_clk_req;
			qca199x_dev->clk_req_irq_num		=
						platform_data->clk_req_irq_num;
	}

	/* init mutex and queues */
	init_waitqueue_head(&qca199x_dev->read_wq);
	mutex_init(&qca199x_dev->read_mutex);
	spin_lock_init(&qca199x_dev->irq_enabled_lock);
	spin_lock_init(&qca199x_dev->irq_enabled_lock_clk_req);

	qca199x_dev->qca199x_device.minor = MISC_DYNAMIC_MINOR;
	qca199x_dev->qca199x_device.name = "nfc-nci";
@@ -1189,6 +1354,7 @@ static int qca199x_probe(struct i2c_client *client,
	* for reading.  It is cleared when all data has been read.
	*/
	device_mode.handle_flavour = UNSOLICITED_MODE;
	/* NFC_INT IRQ */
	qca199x_dev->irq_enabled = true;
	r = request_irq(client->irq, qca199x_dev_irq_handler,
			  IRQF_TRIGGER_RISING, client->name, qca199x_dev);
@@ -1197,6 +1363,31 @@ static int qca199x_probe(struct i2c_client *client,
		goto err_request_irq_failed;
	}
	qca199x_disable_irq(qca199x_dev);
	/* CLK_REQ IRQ */
	if ((!strcmp(platform_data->clk_src_name, "GPCLK")) ||
		(!strcmp(platform_data->clk_src_name, "GPCLK2"))) {
		r = request_irq(qca199x_dev->clk_req_irq_num,
				qca199x_dev_irq_handler_clk_req,
				(IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING),
						client->name, qca199x_dev);
		if (r) {
			dev_err(&client->dev,
			"nfc-nci probe: request_irq failed. irq no = %d\n, main irq =  %d",
				qca199x_dev->clk_req_irq_num, client->irq);
			goto err_request_irq_failed;
		}
		qca199x_dev->irq_enabled_clk_req = true;
		qca199x_disable_irq_clk_req(qca199x_dev);


		qca199x_dev->my_wq =
			create_singlethread_workqueue("qca1990x_CLK_REQ_queue");
		if (!qca199x_dev->my_wq)
			goto err_create_workq;

		INIT_WORK(&qca199x_dev->msm_clock_controll_work,
			clk_req_update);
	}
	i2c_set_clientdata(client, qca199x_dev);
	gpio_set_value(platform_data->dis_gpio, 1);
	dev_dbg(&client->dev,
@@ -1204,12 +1395,18 @@ static int qca199x_probe(struct i2c_client *client,
		 __func__);
	return 0;

err_create_workq:
	dev_err(&client->dev,
	"nfc-nci probe: %s, work_queue creation failure\n",
		 __func__);
	free_irq(client->irq, qca199x_dev);
err_request_irq_failed:
	misc_deregister(&qca199x_dev->qca199x_device);
err_misc_register:
	mutex_destroy(&qca199x_dev->read_mutex);
err_ven_gpio:
	gpio_free(platform_data->ven_gpio);
err_clkreq_gpio:
	if (strcmp(platform_data->clk_src_name, "GPCLK2"))
		gpio_free(platform_data->clkreq_gpio);
err_clk:
		qca199x_clock_deselect(qca199x_dev);
err_dis_gpio:
@@ -1224,6 +1421,14 @@ err_dis_gpio:
		gpio_free(platform_data->clk_src_gpio);
	}
	gpio_free(platform_data->dis_gpio);
err_irq_clk:
	if ((!strcmp(platform_data->clk_src_name, "GPCLK")) ||
		(!strcmp(platform_data->clk_src_name, "GPCLK2"))) {
		r = gpio_direction_input(platform_data->irq_gpio_clk_req);
		if (r)
			dev_err(&client->dev, "nfc-nci probe: Unable to set direction\n");
		gpio_free(platform_data->irq_gpio_clk_req);
	}
err_irq:
	gpio_free(platform_data->irq_gpio);
err_free_dev:
@@ -1240,8 +1445,13 @@ static int qca199x_remove(struct i2c_client *client)
	misc_deregister(&qca199x_dev->qca199x_device);
	mutex_destroy(&qca199x_dev->read_mutex);
	gpio_free(qca199x_dev->irq_gpio);
	if ((!strcmp(qca199x_dev->clk_src_name, "GPCLK")) ||
		(!strcmp(qca199x_dev->clk_src_name, "GPCLK2"))) {
		gpio_free(qca199x_dev->irq_gpio_clk_req);
	}
	gpio_free(qca199x_dev->dis_gpio);
	gpio_free(qca199x_dev->ven_gpio);
	if (strcmp(qca199x_dev->clk_src_name, "GPCLK2"))
		gpio_free(qca199x_dev->clkreq_gpio);
	kfree(qca199x_dev);

	return 0;