162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2007,2012 Texas Instruments, Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/platform_device.h>
862306a36Sopenharmony_ci#include <linux/interrupt.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/w1.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define	MOD_NAME	"OMAP_HDQ:"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define OMAP_HDQ_REVISION			0x00
2162306a36Sopenharmony_ci#define OMAP_HDQ_TX_DATA			0x04
2262306a36Sopenharmony_ci#define OMAP_HDQ_RX_DATA			0x08
2362306a36Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS			0x0c
2462306a36Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_SINGLE		BIT(7)
2562306a36Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK	BIT(6)
2662306a36Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_CLOCKENABLE	BIT(5)
2762306a36Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_GO                 BIT(4)
2862306a36Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_PRESENCE		BIT(3)
2962306a36Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_INITIALIZATION	BIT(2)
3062306a36Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_DIR		BIT(1)
3162306a36Sopenharmony_ci#define OMAP_HDQ_INT_STATUS			0x10
3262306a36Sopenharmony_ci#define OMAP_HDQ_INT_STATUS_TXCOMPLETE		BIT(2)
3362306a36Sopenharmony_ci#define OMAP_HDQ_INT_STATUS_RXCOMPLETE		BIT(1)
3462306a36Sopenharmony_ci#define OMAP_HDQ_INT_STATUS_TIMEOUT		BIT(0)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define OMAP_HDQ_FLAG_CLEAR			0
3762306a36Sopenharmony_ci#define OMAP_HDQ_FLAG_SET			1
3862306a36Sopenharmony_ci#define OMAP_HDQ_TIMEOUT			(HZ/5)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define OMAP_HDQ_MAX_USER			4
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(hdq_wait_queue);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int w1_id;
4562306a36Sopenharmony_cimodule_param(w1_id, int, 0400);
4662306a36Sopenharmony_ciMODULE_PARM_DESC(w1_id, "1-wire id for the slave detection in HDQ mode");
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct hdq_data {
4962306a36Sopenharmony_ci	struct device		*dev;
5062306a36Sopenharmony_ci	void __iomem		*hdq_base;
5162306a36Sopenharmony_ci	/* lock read/write/break operations */
5262306a36Sopenharmony_ci	struct  mutex		hdq_mutex;
5362306a36Sopenharmony_ci	/* interrupt status and a lock for it */
5462306a36Sopenharmony_ci	u8			hdq_irqstatus;
5562306a36Sopenharmony_ci	spinlock_t		hdq_spinlock;
5662306a36Sopenharmony_ci	/* mode: 0-HDQ 1-W1 */
5762306a36Sopenharmony_ci	int                     mode;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* HDQ register I/O routines */
6262306a36Sopenharmony_cistatic inline u8 hdq_reg_in(struct hdq_data *hdq_data, u32 offset)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	return __raw_readl(hdq_data->hdq_base + offset);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	__raw_writel(val, hdq_data->hdq_base + offset);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset,
7362306a36Sopenharmony_ci			u8 val, u8 mask)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	u8 new_val = (__raw_readl(hdq_data->hdq_base + offset) & ~mask)
7662306a36Sopenharmony_ci			| (val & mask);
7762306a36Sopenharmony_ci	__raw_writel(new_val, hdq_data->hdq_base + offset);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return new_val;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/*
8362306a36Sopenharmony_ci * Wait for one or more bits in flag change.
8462306a36Sopenharmony_ci * HDQ_FLAG_SET: wait until any bit in the flag is set.
8562306a36Sopenharmony_ci * HDQ_FLAG_CLEAR: wait until all bits in the flag are cleared.
8662306a36Sopenharmony_ci * return 0 on success and -ETIMEDOUT in the case of timeout.
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_cistatic int hdq_wait_for_flag(struct hdq_data *hdq_data, u32 offset,
8962306a36Sopenharmony_ci		u8 flag, u8 flag_set, u8 *status)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	int ret = 0;
9262306a36Sopenharmony_ci	unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (flag_set == OMAP_HDQ_FLAG_CLEAR) {
9562306a36Sopenharmony_ci		/* wait for the flag clear */
9662306a36Sopenharmony_ci		while (((*status = hdq_reg_in(hdq_data, offset)) & flag)
9762306a36Sopenharmony_ci			&& time_before(jiffies, timeout)) {
9862306a36Sopenharmony_ci			schedule_timeout_uninterruptible(1);
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci		if (*status & flag)
10162306a36Sopenharmony_ci			ret = -ETIMEDOUT;
10262306a36Sopenharmony_ci	} else if (flag_set == OMAP_HDQ_FLAG_SET) {
10362306a36Sopenharmony_ci		/* wait for the flag set */
10462306a36Sopenharmony_ci		while (!((*status = hdq_reg_in(hdq_data, offset)) & flag)
10562306a36Sopenharmony_ci			&& time_before(jiffies, timeout)) {
10662306a36Sopenharmony_ci			schedule_timeout_uninterruptible(1);
10762306a36Sopenharmony_ci		}
10862306a36Sopenharmony_ci		if (!(*status & flag))
10962306a36Sopenharmony_ci			ret = -ETIMEDOUT;
11062306a36Sopenharmony_ci	} else
11162306a36Sopenharmony_ci		return -EINVAL;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return ret;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/* Clear saved irqstatus after using an interrupt */
11762306a36Sopenharmony_cistatic u8 hdq_reset_irqstatus(struct hdq_data *hdq_data, u8 bits)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	unsigned long irqflags;
12062306a36Sopenharmony_ci	u8 status;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
12362306a36Sopenharmony_ci	status = hdq_data->hdq_irqstatus;
12462306a36Sopenharmony_ci	/* this is a read-modify-write */
12562306a36Sopenharmony_ci	hdq_data->hdq_irqstatus &= ~bits;
12662306a36Sopenharmony_ci	spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return status;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/* write out a byte and fill *status with HDQ_INT_STATUS */
13262306a36Sopenharmony_cistatic int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	int ret;
13562306a36Sopenharmony_ci	u8 tmp_status;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
13862306a36Sopenharmony_ci	if (ret < 0) {
13962306a36Sopenharmony_ci		ret = -EINTR;
14062306a36Sopenharmony_ci		goto rtn;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (hdq_data->hdq_irqstatus)
14462306a36Sopenharmony_ci		dev_err(hdq_data->dev, "TX irqstatus not cleared (%02x)\n",
14562306a36Sopenharmony_ci			hdq_data->hdq_irqstatus);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	*status = 0;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	hdq_reg_out(hdq_data, OMAP_HDQ_TX_DATA, val);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* set the GO bit */
15262306a36Sopenharmony_ci	hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, OMAP_HDQ_CTRL_STATUS_GO,
15362306a36Sopenharmony_ci		OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO);
15462306a36Sopenharmony_ci	/* wait for the TXCOMPLETE bit */
15562306a36Sopenharmony_ci	ret = wait_event_timeout(hdq_wait_queue,
15662306a36Sopenharmony_ci		(hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_TXCOMPLETE),
15762306a36Sopenharmony_ci		OMAP_HDQ_TIMEOUT);
15862306a36Sopenharmony_ci	*status = hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_TXCOMPLETE);
15962306a36Sopenharmony_ci	if (ret == 0) {
16062306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "TX wait elapsed\n");
16162306a36Sopenharmony_ci		ret = -ETIMEDOUT;
16262306a36Sopenharmony_ci		goto out;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* check irqstatus */
16662306a36Sopenharmony_ci	if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) {
16762306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "timeout waiting for"
16862306a36Sopenharmony_ci			" TXCOMPLETE/RXCOMPLETE, %x\n", *status);
16962306a36Sopenharmony_ci		ret = -ETIMEDOUT;
17062306a36Sopenharmony_ci		goto out;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* wait for the GO bit return to zero */
17462306a36Sopenharmony_ci	ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS,
17562306a36Sopenharmony_ci			OMAP_HDQ_CTRL_STATUS_GO,
17662306a36Sopenharmony_ci			OMAP_HDQ_FLAG_CLEAR, &tmp_status);
17762306a36Sopenharmony_ci	if (ret) {
17862306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "timeout waiting GO bit"
17962306a36Sopenharmony_ci			" return to zero, %x\n", tmp_status);
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciout:
18362306a36Sopenharmony_ci	mutex_unlock(&hdq_data->hdq_mutex);
18462306a36Sopenharmony_cirtn:
18562306a36Sopenharmony_ci	return ret;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/* HDQ Interrupt service routine */
18962306a36Sopenharmony_cistatic irqreturn_t hdq_isr(int irq, void *_hdq)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct hdq_data *hdq_data = _hdq;
19262306a36Sopenharmony_ci	unsigned long irqflags;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
19562306a36Sopenharmony_ci	hdq_data->hdq_irqstatus |= hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
19662306a36Sopenharmony_ci	spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
19762306a36Sopenharmony_ci	dev_dbg(hdq_data->dev, "hdq_isr: %x\n", hdq_data->hdq_irqstatus);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (hdq_data->hdq_irqstatus &
20062306a36Sopenharmony_ci		(OMAP_HDQ_INT_STATUS_TXCOMPLETE | OMAP_HDQ_INT_STATUS_RXCOMPLETE
20162306a36Sopenharmony_ci		| OMAP_HDQ_INT_STATUS_TIMEOUT)) {
20262306a36Sopenharmony_ci		/* wake up sleeping process */
20362306a36Sopenharmony_ci		wake_up(&hdq_wait_queue);
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return IRQ_HANDLED;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/* W1 search callback function  in HDQ mode */
21062306a36Sopenharmony_cistatic void omap_w1_search_bus(void *_hdq, struct w1_master *master_dev,
21162306a36Sopenharmony_ci		u8 search_type, w1_slave_found_callback slave_found)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	u64 module_id, rn_le, cs, id;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (w1_id)
21662306a36Sopenharmony_ci		module_id = w1_id;
21762306a36Sopenharmony_ci	else
21862306a36Sopenharmony_ci		module_id = 0x1;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	rn_le = cpu_to_le64(module_id);
22162306a36Sopenharmony_ci	/*
22262306a36Sopenharmony_ci	 * HDQ might not obey truly the 1-wire spec.
22362306a36Sopenharmony_ci	 * So calculate CRC based on module parameter.
22462306a36Sopenharmony_ci	 */
22562306a36Sopenharmony_ci	cs = w1_calc_crc8((u8 *)&rn_le, 7);
22662306a36Sopenharmony_ci	id = (cs << 56) | module_id;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	slave_found(master_dev, id);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci/* Issue break pulse to the device */
23262306a36Sopenharmony_cistatic int omap_hdq_break(struct hdq_data *hdq_data)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	int ret = 0;
23562306a36Sopenharmony_ci	u8 tmp_status;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
23862306a36Sopenharmony_ci	if (ret < 0) {
23962306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
24062306a36Sopenharmony_ci		ret = -EINTR;
24162306a36Sopenharmony_ci		goto rtn;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (hdq_data->hdq_irqstatus)
24562306a36Sopenharmony_ci		dev_err(hdq_data->dev, "break irqstatus not cleared (%02x)\n",
24662306a36Sopenharmony_ci			hdq_data->hdq_irqstatus);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* set the INIT and GO bit */
24962306a36Sopenharmony_ci	hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS,
25062306a36Sopenharmony_ci		OMAP_HDQ_CTRL_STATUS_INITIALIZATION | OMAP_HDQ_CTRL_STATUS_GO,
25162306a36Sopenharmony_ci		OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_INITIALIZATION |
25262306a36Sopenharmony_ci		OMAP_HDQ_CTRL_STATUS_GO);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* wait for the TIMEOUT bit */
25562306a36Sopenharmony_ci	ret = wait_event_timeout(hdq_wait_queue,
25662306a36Sopenharmony_ci		(hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_TIMEOUT),
25762306a36Sopenharmony_ci		OMAP_HDQ_TIMEOUT);
25862306a36Sopenharmony_ci	tmp_status = hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_TIMEOUT);
25962306a36Sopenharmony_ci	if (ret == 0) {
26062306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "break wait elapsed\n");
26162306a36Sopenharmony_ci		ret = -EINTR;
26262306a36Sopenharmony_ci		goto out;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* check irqstatus */
26662306a36Sopenharmony_ci	if (!(tmp_status & OMAP_HDQ_INT_STATUS_TIMEOUT)) {
26762306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "timeout waiting for TIMEOUT, %x\n",
26862306a36Sopenharmony_ci			tmp_status);
26962306a36Sopenharmony_ci		ret = -ETIMEDOUT;
27062306a36Sopenharmony_ci		goto out;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/*
27462306a36Sopenharmony_ci	 * check for the presence detect bit to get
27562306a36Sopenharmony_ci	 * set to show that the slave is responding
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	if (!(hdq_reg_in(hdq_data, OMAP_HDQ_CTRL_STATUS) &
27862306a36Sopenharmony_ci			OMAP_HDQ_CTRL_STATUS_PRESENCE)) {
27962306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "Presence bit not set\n");
28062306a36Sopenharmony_ci		ret = -ETIMEDOUT;
28162306a36Sopenharmony_ci		goto out;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/*
28562306a36Sopenharmony_ci	 * wait for both INIT and GO bits rerurn to zero.
28662306a36Sopenharmony_ci	 * zero wait time expected for interrupt mode.
28762306a36Sopenharmony_ci	 */
28862306a36Sopenharmony_ci	ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS,
28962306a36Sopenharmony_ci			OMAP_HDQ_CTRL_STATUS_INITIALIZATION |
29062306a36Sopenharmony_ci			OMAP_HDQ_CTRL_STATUS_GO, OMAP_HDQ_FLAG_CLEAR,
29162306a36Sopenharmony_ci			&tmp_status);
29262306a36Sopenharmony_ci	if (ret)
29362306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "timeout waiting INIT&GO bits"
29462306a36Sopenharmony_ci			" return to zero, %x\n", tmp_status);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ciout:
29762306a36Sopenharmony_ci	mutex_unlock(&hdq_data->hdq_mutex);
29862306a36Sopenharmony_cirtn:
29962306a36Sopenharmony_ci	return ret;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int hdq_read_byte(struct hdq_data *hdq_data, u8 *val)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	int ret = 0;
30562306a36Sopenharmony_ci	u8 status;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
30862306a36Sopenharmony_ci	if (ret < 0) {
30962306a36Sopenharmony_ci		ret = -EINTR;
31062306a36Sopenharmony_ci		goto rtn;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (pm_runtime_suspended(hdq_data->dev)) {
31462306a36Sopenharmony_ci		ret = -EINVAL;
31562306a36Sopenharmony_ci		goto out;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (!(hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) {
31962306a36Sopenharmony_ci		hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS,
32062306a36Sopenharmony_ci			OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO,
32162306a36Sopenharmony_ci			OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO);
32262306a36Sopenharmony_ci		/*
32362306a36Sopenharmony_ci		 * The RX comes immediately after TX.
32462306a36Sopenharmony_ci		 */
32562306a36Sopenharmony_ci		wait_event_timeout(hdq_wait_queue,
32662306a36Sopenharmony_ci				   (hdq_data->hdq_irqstatus
32762306a36Sopenharmony_ci				    & (OMAP_HDQ_INT_STATUS_RXCOMPLETE |
32862306a36Sopenharmony_ci				       OMAP_HDQ_INT_STATUS_TIMEOUT)),
32962306a36Sopenharmony_ci				   OMAP_HDQ_TIMEOUT);
33062306a36Sopenharmony_ci		status = hdq_reset_irqstatus(hdq_data,
33162306a36Sopenharmony_ci					     OMAP_HDQ_INT_STATUS_RXCOMPLETE |
33262306a36Sopenharmony_ci					     OMAP_HDQ_INT_STATUS_TIMEOUT);
33362306a36Sopenharmony_ci		hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 0,
33462306a36Sopenharmony_ci			OMAP_HDQ_CTRL_STATUS_DIR);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		/* check irqstatus */
33762306a36Sopenharmony_ci		if (!(status & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) {
33862306a36Sopenharmony_ci			dev_dbg(hdq_data->dev, "timeout waiting for"
33962306a36Sopenharmony_ci				" RXCOMPLETE, %x", status);
34062306a36Sopenharmony_ci			ret = -ETIMEDOUT;
34162306a36Sopenharmony_ci			goto out;
34262306a36Sopenharmony_ci		}
34362306a36Sopenharmony_ci	} else { /* interrupt had occurred before hdq_read_byte was called */
34462306a36Sopenharmony_ci		hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_RXCOMPLETE);
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci	/* the data is ready. Read it in! */
34762306a36Sopenharmony_ci	*val = hdq_reg_in(hdq_data, OMAP_HDQ_RX_DATA);
34862306a36Sopenharmony_ciout:
34962306a36Sopenharmony_ci	mutex_unlock(&hdq_data->hdq_mutex);
35062306a36Sopenharmony_cirtn:
35162306a36Sopenharmony_ci	return ret;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/*
35662306a36Sopenharmony_ci * W1 triplet callback function - used for searching ROM addresses.
35762306a36Sopenharmony_ci * Registered only when controller is in 1-wire mode.
35862306a36Sopenharmony_ci */
35962306a36Sopenharmony_cistatic u8 omap_w1_triplet(void *_hdq, u8 bdir)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	u8 id_bit, comp_bit;
36262306a36Sopenharmony_ci	int err;
36362306a36Sopenharmony_ci	u8 ret = 0x3; /* no slaves responded */
36462306a36Sopenharmony_ci	struct hdq_data *hdq_data = _hdq;
36562306a36Sopenharmony_ci	u8 ctrl = OMAP_HDQ_CTRL_STATUS_SINGLE | OMAP_HDQ_CTRL_STATUS_GO |
36662306a36Sopenharmony_ci		  OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK;
36762306a36Sopenharmony_ci	u8 mask = ctrl | OMAP_HDQ_CTRL_STATUS_DIR;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	err = pm_runtime_get_sync(hdq_data->dev);
37062306a36Sopenharmony_ci	if (err < 0) {
37162306a36Sopenharmony_ci		pm_runtime_put_noidle(hdq_data->dev);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		return err;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	err = mutex_lock_interruptible(&hdq_data->hdq_mutex);
37762306a36Sopenharmony_ci	if (err < 0) {
37862306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
37962306a36Sopenharmony_ci		goto rtn;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/* read id_bit */
38362306a36Sopenharmony_ci	hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS,
38462306a36Sopenharmony_ci		      ctrl | OMAP_HDQ_CTRL_STATUS_DIR, mask);
38562306a36Sopenharmony_ci	err = wait_event_timeout(hdq_wait_queue,
38662306a36Sopenharmony_ci				 (hdq_data->hdq_irqstatus
38762306a36Sopenharmony_ci				  & OMAP_HDQ_INT_STATUS_RXCOMPLETE),
38862306a36Sopenharmony_ci				 OMAP_HDQ_TIMEOUT);
38962306a36Sopenharmony_ci	/* Must clear irqstatus for another RXCOMPLETE interrupt */
39062306a36Sopenharmony_ci	hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_RXCOMPLETE);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (err == 0) {
39362306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "RX wait elapsed\n");
39462306a36Sopenharmony_ci		goto out;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	id_bit = (hdq_reg_in(_hdq, OMAP_HDQ_RX_DATA) & 0x01);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* read comp_bit */
39962306a36Sopenharmony_ci	hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS,
40062306a36Sopenharmony_ci		      ctrl | OMAP_HDQ_CTRL_STATUS_DIR, mask);
40162306a36Sopenharmony_ci	err = wait_event_timeout(hdq_wait_queue,
40262306a36Sopenharmony_ci				 (hdq_data->hdq_irqstatus
40362306a36Sopenharmony_ci				  & OMAP_HDQ_INT_STATUS_RXCOMPLETE),
40462306a36Sopenharmony_ci				 OMAP_HDQ_TIMEOUT);
40562306a36Sopenharmony_ci	/* Must clear irqstatus for another RXCOMPLETE interrupt */
40662306a36Sopenharmony_ci	hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_RXCOMPLETE);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (err == 0) {
40962306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "RX wait elapsed\n");
41062306a36Sopenharmony_ci		goto out;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci	comp_bit = (hdq_reg_in(_hdq, OMAP_HDQ_RX_DATA) & 0x01);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (id_bit && comp_bit) {
41562306a36Sopenharmony_ci		ret = 0x03;  /* no slaves responded */
41662306a36Sopenharmony_ci		goto out;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci	if (!id_bit && !comp_bit) {
41962306a36Sopenharmony_ci		/* Both bits are valid, take the direction given */
42062306a36Sopenharmony_ci		ret = bdir ? 0x04 : 0;
42162306a36Sopenharmony_ci	} else {
42262306a36Sopenharmony_ci		/* Only one bit is valid, take that direction */
42362306a36Sopenharmony_ci		bdir = id_bit;
42462306a36Sopenharmony_ci		ret = id_bit ? 0x05 : 0x02;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/* write bdir bit */
42862306a36Sopenharmony_ci	hdq_reg_out(_hdq, OMAP_HDQ_TX_DATA, bdir);
42962306a36Sopenharmony_ci	hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS, ctrl, mask);
43062306a36Sopenharmony_ci	err = wait_event_timeout(hdq_wait_queue,
43162306a36Sopenharmony_ci				 (hdq_data->hdq_irqstatus
43262306a36Sopenharmony_ci				  & OMAP_HDQ_INT_STATUS_TXCOMPLETE),
43362306a36Sopenharmony_ci				 OMAP_HDQ_TIMEOUT);
43462306a36Sopenharmony_ci	/* Must clear irqstatus for another TXCOMPLETE interrupt */
43562306a36Sopenharmony_ci	hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_TXCOMPLETE);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (err == 0) {
43862306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "TX wait elapsed\n");
43962306a36Sopenharmony_ci		goto out;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS, 0,
44362306a36Sopenharmony_ci		      OMAP_HDQ_CTRL_STATUS_SINGLE);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ciout:
44662306a36Sopenharmony_ci	mutex_unlock(&hdq_data->hdq_mutex);
44762306a36Sopenharmony_cirtn:
44862306a36Sopenharmony_ci	pm_runtime_mark_last_busy(hdq_data->dev);
44962306a36Sopenharmony_ci	pm_runtime_put_autosuspend(hdq_data->dev);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return ret;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/* reset callback */
45562306a36Sopenharmony_cistatic u8 omap_w1_reset_bus(void *_hdq)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct hdq_data *hdq_data = _hdq;
45862306a36Sopenharmony_ci	int err;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	err = pm_runtime_get_sync(hdq_data->dev);
46162306a36Sopenharmony_ci	if (err < 0) {
46262306a36Sopenharmony_ci		pm_runtime_put_noidle(hdq_data->dev);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		return err;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	omap_hdq_break(hdq_data);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	pm_runtime_mark_last_busy(hdq_data->dev);
47062306a36Sopenharmony_ci	pm_runtime_put_autosuspend(hdq_data->dev);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return 0;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci/* Read a byte of data from the device */
47662306a36Sopenharmony_cistatic u8 omap_w1_read_byte(void *_hdq)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct hdq_data *hdq_data = _hdq;
47962306a36Sopenharmony_ci	u8 val = 0;
48062306a36Sopenharmony_ci	int ret;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	ret = pm_runtime_get_sync(hdq_data->dev);
48362306a36Sopenharmony_ci	if (ret < 0) {
48462306a36Sopenharmony_ci		pm_runtime_put_noidle(hdq_data->dev);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		return -1;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	ret = hdq_read_byte(hdq_data, &val);
49062306a36Sopenharmony_ci	if (ret)
49162306a36Sopenharmony_ci		val = -1;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	pm_runtime_mark_last_busy(hdq_data->dev);
49462306a36Sopenharmony_ci	pm_runtime_put_autosuspend(hdq_data->dev);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return val;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci/* Write a byte of data to the device */
50062306a36Sopenharmony_cistatic void omap_w1_write_byte(void *_hdq, u8 byte)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct hdq_data *hdq_data = _hdq;
50362306a36Sopenharmony_ci	int ret;
50462306a36Sopenharmony_ci	u8 status;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	ret = pm_runtime_get_sync(hdq_data->dev);
50762306a36Sopenharmony_ci	if (ret < 0) {
50862306a36Sopenharmony_ci		pm_runtime_put_noidle(hdq_data->dev);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		return;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	/*
51462306a36Sopenharmony_ci	 * We need to reset the slave before
51562306a36Sopenharmony_ci	 * issuing the SKIP ROM command, else
51662306a36Sopenharmony_ci	 * the slave will not work.
51762306a36Sopenharmony_ci	 */
51862306a36Sopenharmony_ci	if (byte == W1_SKIP_ROM)
51962306a36Sopenharmony_ci		omap_hdq_break(hdq_data);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	ret = hdq_write_byte(hdq_data, byte, &status);
52262306a36Sopenharmony_ci	if (ret < 0) {
52362306a36Sopenharmony_ci		dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status);
52462306a36Sopenharmony_ci		goto out_err;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ciout_err:
52862306a36Sopenharmony_ci	pm_runtime_mark_last_busy(hdq_data->dev);
52962306a36Sopenharmony_ci	pm_runtime_put_autosuspend(hdq_data->dev);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic struct w1_bus_master omap_w1_master = {
53362306a36Sopenharmony_ci	.read_byte	= omap_w1_read_byte,
53462306a36Sopenharmony_ci	.write_byte	= omap_w1_write_byte,
53562306a36Sopenharmony_ci	.reset_bus	= omap_w1_reset_bus,
53662306a36Sopenharmony_ci};
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic int __maybe_unused omap_hdq_runtime_suspend(struct device *dev)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct hdq_data *hdq_data = dev_get_drvdata(dev);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	hdq_reg_out(hdq_data, 0, hdq_data->mode);
54362306a36Sopenharmony_ci	hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return 0;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic int __maybe_unused omap_hdq_runtime_resume(struct device *dev)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct hdq_data *hdq_data = dev_get_drvdata(dev);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* select HDQ/1W mode & enable clocks */
55362306a36Sopenharmony_ci	hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS,
55462306a36Sopenharmony_ci		    OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
55562306a36Sopenharmony_ci		    OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK |
55662306a36Sopenharmony_ci		    hdq_data->mode);
55762306a36Sopenharmony_ci	hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return 0;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic const struct dev_pm_ops omap_hdq_pm_ops = {
56362306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(omap_hdq_runtime_suspend,
56462306a36Sopenharmony_ci			   omap_hdq_runtime_resume, NULL)
56562306a36Sopenharmony_ci};
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic int omap_hdq_probe(struct platform_device *pdev)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
57062306a36Sopenharmony_ci	struct hdq_data *hdq_data;
57162306a36Sopenharmony_ci	int ret, irq;
57262306a36Sopenharmony_ci	u8 rev;
57362306a36Sopenharmony_ci	const char *mode;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	hdq_data = devm_kzalloc(dev, sizeof(*hdq_data), GFP_KERNEL);
57662306a36Sopenharmony_ci	if (!hdq_data)
57762306a36Sopenharmony_ci		return -ENOMEM;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	hdq_data->dev = dev;
58062306a36Sopenharmony_ci	platform_set_drvdata(pdev, hdq_data);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	hdq_data->hdq_base = devm_platform_ioremap_resource(pdev, 0);
58362306a36Sopenharmony_ci	if (IS_ERR(hdq_data->hdq_base))
58462306a36Sopenharmony_ci		return PTR_ERR(hdq_data->hdq_base);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	mutex_init(&hdq_data->hdq_mutex);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	ret = of_property_read_string(pdev->dev.of_node, "ti,mode", &mode);
58962306a36Sopenharmony_ci	if (ret < 0 || !strcmp(mode, "hdq")) {
59062306a36Sopenharmony_ci		hdq_data->mode = 0;
59162306a36Sopenharmony_ci		omap_w1_master.search = omap_w1_search_bus;
59262306a36Sopenharmony_ci	} else {
59362306a36Sopenharmony_ci		hdq_data->mode = 1;
59462306a36Sopenharmony_ci		omap_w1_master.triplet = omap_w1_triplet;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
59862306a36Sopenharmony_ci	pm_runtime_use_autosuspend(&pdev->dev);
59962306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&pdev->dev, 300);
60062306a36Sopenharmony_ci	ret = pm_runtime_get_sync(&pdev->dev);
60162306a36Sopenharmony_ci	if (ret < 0) {
60262306a36Sopenharmony_ci		pm_runtime_put_noidle(&pdev->dev);
60362306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "pm_runtime_get_sync failed\n");
60462306a36Sopenharmony_ci		goto err_w1;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION);
60862306a36Sopenharmony_ci	dev_info(&pdev->dev, "OMAP HDQ Hardware Rev %c.%c. Driver in %s mode\n",
60962306a36Sopenharmony_ci		(rev >> 4) + '0', (rev & 0x0f) + '0', "Interrupt");
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	spin_lock_init(&hdq_data->hdq_spinlock);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
61462306a36Sopenharmony_ci	if (irq	< 0) {
61562306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "Failed to get IRQ: %d\n", irq);
61662306a36Sopenharmony_ci		ret = irq;
61762306a36Sopenharmony_ci		goto err_irq;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	ret = devm_request_irq(dev, irq, hdq_isr, 0, "omap_hdq", hdq_data);
62162306a36Sopenharmony_ci	if (ret < 0) {
62262306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "could not request irq\n");
62362306a36Sopenharmony_ci		goto err_irq;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	omap_hdq_break(hdq_data);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&pdev->dev);
62962306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&pdev->dev);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	omap_w1_master.data = hdq_data;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	ret = w1_add_master_device(&omap_w1_master);
63462306a36Sopenharmony_ci	if (ret) {
63562306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "Failure in registering w1 master\n");
63662306a36Sopenharmony_ci		goto err_w1;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return 0;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cierr_irq:
64262306a36Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
64362306a36Sopenharmony_cierr_w1:
64462306a36Sopenharmony_ci	pm_runtime_dont_use_autosuspend(&pdev->dev);
64562306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	return ret;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic int omap_hdq_remove(struct platform_device *pdev)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	int active;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	active = pm_runtime_get_sync(&pdev->dev);
65562306a36Sopenharmony_ci	if (active < 0)
65662306a36Sopenharmony_ci		pm_runtime_put_noidle(&pdev->dev);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	w1_remove_master_device(&omap_w1_master);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	pm_runtime_dont_use_autosuspend(&pdev->dev);
66162306a36Sopenharmony_ci	if (active >= 0)
66262306a36Sopenharmony_ci		pm_runtime_put_sync(&pdev->dev);
66362306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	return 0;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic const struct of_device_id omap_hdq_dt_ids[] = {
66962306a36Sopenharmony_ci	{ .compatible = "ti,omap3-1w" },
67062306a36Sopenharmony_ci	{ .compatible = "ti,am4372-hdq" },
67162306a36Sopenharmony_ci	{}
67262306a36Sopenharmony_ci};
67362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_hdq_dt_ids);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic struct platform_driver omap_hdq_driver = {
67662306a36Sopenharmony_ci	.probe = omap_hdq_probe,
67762306a36Sopenharmony_ci	.remove = omap_hdq_remove,
67862306a36Sopenharmony_ci	.driver = {
67962306a36Sopenharmony_ci		.name =	"omap_hdq",
68062306a36Sopenharmony_ci		.of_match_table = omap_hdq_dt_ids,
68162306a36Sopenharmony_ci		.pm = &omap_hdq_pm_ops,
68262306a36Sopenharmony_ci	},
68362306a36Sopenharmony_ci};
68462306a36Sopenharmony_cimodule_platform_driver(omap_hdq_driver);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ciMODULE_AUTHOR("Texas Instruments");
68762306a36Sopenharmony_ciMODULE_DESCRIPTION("HDQ-1W driver Library");
68862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
689