18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * drivers/w1/masters/omap_hdq.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007,2012 Texas Instruments, Inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License 78c2ecf20Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of any 88c2ecf20Sopenharmony_ci * kind, whether express or implied. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/sched.h> 198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/w1.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define MOD_NAME "OMAP_HDQ:" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define OMAP_HDQ_REVISION 0x00 278c2ecf20Sopenharmony_ci#define OMAP_HDQ_TX_DATA 0x04 288c2ecf20Sopenharmony_ci#define OMAP_HDQ_RX_DATA 0x08 298c2ecf20Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS 0x0c 308c2ecf20Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_SINGLE BIT(7) 318c2ecf20Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK BIT(6) 328c2ecf20Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_CLOCKENABLE BIT(5) 338c2ecf20Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_GO BIT(4) 348c2ecf20Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_PRESENCE BIT(3) 358c2ecf20Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_INITIALIZATION BIT(2) 368c2ecf20Sopenharmony_ci#define OMAP_HDQ_CTRL_STATUS_DIR BIT(1) 378c2ecf20Sopenharmony_ci#define OMAP_HDQ_INT_STATUS 0x10 388c2ecf20Sopenharmony_ci#define OMAP_HDQ_INT_STATUS_TXCOMPLETE BIT(2) 398c2ecf20Sopenharmony_ci#define OMAP_HDQ_INT_STATUS_RXCOMPLETE BIT(1) 408c2ecf20Sopenharmony_ci#define OMAP_HDQ_INT_STATUS_TIMEOUT BIT(0) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define OMAP_HDQ_FLAG_CLEAR 0 438c2ecf20Sopenharmony_ci#define OMAP_HDQ_FLAG_SET 1 448c2ecf20Sopenharmony_ci#define OMAP_HDQ_TIMEOUT (HZ/5) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define OMAP_HDQ_MAX_USER 4 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(hdq_wait_queue); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int w1_id; 518c2ecf20Sopenharmony_cimodule_param(w1_id, int, S_IRUSR); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(w1_id, "1-wire id for the slave detection in HDQ mode"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct hdq_data { 558c2ecf20Sopenharmony_ci struct device *dev; 568c2ecf20Sopenharmony_ci void __iomem *hdq_base; 578c2ecf20Sopenharmony_ci /* lock read/write/break operations */ 588c2ecf20Sopenharmony_ci struct mutex hdq_mutex; 598c2ecf20Sopenharmony_ci /* interrupt status and a lock for it */ 608c2ecf20Sopenharmony_ci u8 hdq_irqstatus; 618c2ecf20Sopenharmony_ci spinlock_t hdq_spinlock; 628c2ecf20Sopenharmony_ci /* mode: 0-HDQ 1-W1 */ 638c2ecf20Sopenharmony_ci int mode; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* HDQ register I/O routines */ 688c2ecf20Sopenharmony_cistatic inline u8 hdq_reg_in(struct hdq_data *hdq_data, u32 offset) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return __raw_readl(hdq_data->hdq_base + offset); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci __raw_writel(val, hdq_data->hdq_base + offset); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset, 798c2ecf20Sopenharmony_ci u8 val, u8 mask) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci u8 new_val = (__raw_readl(hdq_data->hdq_base + offset) & ~mask) 828c2ecf20Sopenharmony_ci | (val & mask); 838c2ecf20Sopenharmony_ci __raw_writel(new_val, hdq_data->hdq_base + offset); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return new_val; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * Wait for one or more bits in flag change. 908c2ecf20Sopenharmony_ci * HDQ_FLAG_SET: wait until any bit in the flag is set. 918c2ecf20Sopenharmony_ci * HDQ_FLAG_CLEAR: wait until all bits in the flag are cleared. 928c2ecf20Sopenharmony_ci * return 0 on success and -ETIMEDOUT in the case of timeout. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic int hdq_wait_for_flag(struct hdq_data *hdq_data, u32 offset, 958c2ecf20Sopenharmony_ci u8 flag, u8 flag_set, u8 *status) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int ret = 0; 988c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (flag_set == OMAP_HDQ_FLAG_CLEAR) { 1018c2ecf20Sopenharmony_ci /* wait for the flag clear */ 1028c2ecf20Sopenharmony_ci while (((*status = hdq_reg_in(hdq_data, offset)) & flag) 1038c2ecf20Sopenharmony_ci && time_before(jiffies, timeout)) { 1048c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci if (*status & flag) 1078c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 1088c2ecf20Sopenharmony_ci } else if (flag_set == OMAP_HDQ_FLAG_SET) { 1098c2ecf20Sopenharmony_ci /* wait for the flag set */ 1108c2ecf20Sopenharmony_ci while (!((*status = hdq_reg_in(hdq_data, offset)) & flag) 1118c2ecf20Sopenharmony_ci && time_before(jiffies, timeout)) { 1128c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci if (!(*status & flag)) 1158c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 1168c2ecf20Sopenharmony_ci } else 1178c2ecf20Sopenharmony_ci return -EINVAL; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return ret; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* Clear saved irqstatus after using an interrupt */ 1238c2ecf20Sopenharmony_cistatic u8 hdq_reset_irqstatus(struct hdq_data *hdq_data, u8 bits) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci unsigned long irqflags; 1268c2ecf20Sopenharmony_ci u8 status; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); 1298c2ecf20Sopenharmony_ci status = hdq_data->hdq_irqstatus; 1308c2ecf20Sopenharmony_ci /* this is a read-modify-write */ 1318c2ecf20Sopenharmony_ci hdq_data->hdq_irqstatus &= ~bits; 1328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return status; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* write out a byte and fill *status with HDQ_INT_STATUS */ 1388c2ecf20Sopenharmony_cistatic int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int ret; 1418c2ecf20Sopenharmony_ci u8 tmp_status; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); 1448c2ecf20Sopenharmony_ci if (ret < 0) { 1458c2ecf20Sopenharmony_ci ret = -EINTR; 1468c2ecf20Sopenharmony_ci goto rtn; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (hdq_data->hdq_irqstatus) 1508c2ecf20Sopenharmony_ci dev_err(hdq_data->dev, "TX irqstatus not cleared (%02x)\n", 1518c2ecf20Sopenharmony_ci hdq_data->hdq_irqstatus); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci *status = 0; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci hdq_reg_out(hdq_data, OMAP_HDQ_TX_DATA, val); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* set the GO bit */ 1588c2ecf20Sopenharmony_ci hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, OMAP_HDQ_CTRL_STATUS_GO, 1598c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO); 1608c2ecf20Sopenharmony_ci /* wait for the TXCOMPLETE bit */ 1618c2ecf20Sopenharmony_ci ret = wait_event_timeout(hdq_wait_queue, 1628c2ecf20Sopenharmony_ci (hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_TXCOMPLETE), 1638c2ecf20Sopenharmony_ci OMAP_HDQ_TIMEOUT); 1648c2ecf20Sopenharmony_ci *status = hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_TXCOMPLETE); 1658c2ecf20Sopenharmony_ci if (ret == 0) { 1668c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "TX wait elapsed\n"); 1678c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 1688c2ecf20Sopenharmony_ci goto out; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* check irqstatus */ 1728c2ecf20Sopenharmony_ci if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) { 1738c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "timeout waiting for" 1748c2ecf20Sopenharmony_ci " TXCOMPLETE/RXCOMPLETE, %x\n", *status); 1758c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 1768c2ecf20Sopenharmony_ci goto out; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* wait for the GO bit return to zero */ 1808c2ecf20Sopenharmony_ci ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS, 1818c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_GO, 1828c2ecf20Sopenharmony_ci OMAP_HDQ_FLAG_CLEAR, &tmp_status); 1838c2ecf20Sopenharmony_ci if (ret) { 1848c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "timeout waiting GO bit" 1858c2ecf20Sopenharmony_ci " return to zero, %x\n", tmp_status); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ciout: 1898c2ecf20Sopenharmony_ci mutex_unlock(&hdq_data->hdq_mutex); 1908c2ecf20Sopenharmony_cirtn: 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* HDQ Interrupt service routine */ 1958c2ecf20Sopenharmony_cistatic irqreturn_t hdq_isr(int irq, void *_hdq) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct hdq_data *hdq_data = _hdq; 1988c2ecf20Sopenharmony_ci unsigned long irqflags; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); 2018c2ecf20Sopenharmony_ci hdq_data->hdq_irqstatus |= hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); 2028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); 2038c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "hdq_isr: %x\n", hdq_data->hdq_irqstatus); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (hdq_data->hdq_irqstatus & 2068c2ecf20Sopenharmony_ci (OMAP_HDQ_INT_STATUS_TXCOMPLETE | OMAP_HDQ_INT_STATUS_RXCOMPLETE 2078c2ecf20Sopenharmony_ci | OMAP_HDQ_INT_STATUS_TIMEOUT)) { 2088c2ecf20Sopenharmony_ci /* wake up sleeping process */ 2098c2ecf20Sopenharmony_ci wake_up(&hdq_wait_queue); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* W1 search callback function in HDQ mode */ 2168c2ecf20Sopenharmony_cistatic void omap_w1_search_bus(void *_hdq, struct w1_master *master_dev, 2178c2ecf20Sopenharmony_ci u8 search_type, w1_slave_found_callback slave_found) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci u64 module_id, rn_le, cs, id; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (w1_id) 2228c2ecf20Sopenharmony_ci module_id = w1_id; 2238c2ecf20Sopenharmony_ci else 2248c2ecf20Sopenharmony_ci module_id = 0x1; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci rn_le = cpu_to_le64(module_id); 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * HDQ might not obey truly the 1-wire spec. 2298c2ecf20Sopenharmony_ci * So calculate CRC based on module parameter. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci cs = w1_calc_crc8((u8 *)&rn_le, 7); 2328c2ecf20Sopenharmony_ci id = (cs << 56) | module_id; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci slave_found(master_dev, id); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* Issue break pulse to the device */ 2388c2ecf20Sopenharmony_cistatic int omap_hdq_break(struct hdq_data *hdq_data) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int ret = 0; 2418c2ecf20Sopenharmony_ci u8 tmp_status; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); 2448c2ecf20Sopenharmony_ci if (ret < 0) { 2458c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); 2468c2ecf20Sopenharmony_ci ret = -EINTR; 2478c2ecf20Sopenharmony_ci goto rtn; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (hdq_data->hdq_irqstatus) 2518c2ecf20Sopenharmony_ci dev_err(hdq_data->dev, "break irqstatus not cleared (%02x)\n", 2528c2ecf20Sopenharmony_ci hdq_data->hdq_irqstatus); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* set the INIT and GO bit */ 2558c2ecf20Sopenharmony_ci hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 2568c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_INITIALIZATION | OMAP_HDQ_CTRL_STATUS_GO, 2578c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_INITIALIZATION | 2588c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_GO); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* wait for the TIMEOUT bit */ 2618c2ecf20Sopenharmony_ci ret = wait_event_timeout(hdq_wait_queue, 2628c2ecf20Sopenharmony_ci (hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_TIMEOUT), 2638c2ecf20Sopenharmony_ci OMAP_HDQ_TIMEOUT); 2648c2ecf20Sopenharmony_ci tmp_status = hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_TIMEOUT); 2658c2ecf20Sopenharmony_ci if (ret == 0) { 2668c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "break wait elapsed\n"); 2678c2ecf20Sopenharmony_ci ret = -EINTR; 2688c2ecf20Sopenharmony_ci goto out; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* check irqstatus */ 2728c2ecf20Sopenharmony_ci if (!(tmp_status & OMAP_HDQ_INT_STATUS_TIMEOUT)) { 2738c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "timeout waiting for TIMEOUT, %x\n", 2748c2ecf20Sopenharmony_ci tmp_status); 2758c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2768c2ecf20Sopenharmony_ci goto out; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * check for the presence detect bit to get 2818c2ecf20Sopenharmony_ci * set to show that the slave is responding 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci if (!(hdq_reg_in(hdq_data, OMAP_HDQ_CTRL_STATUS) & 2848c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_PRESENCE)) { 2858c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "Presence bit not set\n"); 2868c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2878c2ecf20Sopenharmony_ci goto out; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * wait for both INIT and GO bits rerurn to zero. 2928c2ecf20Sopenharmony_ci * zero wait time expected for interrupt mode. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS, 2958c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_INITIALIZATION | 2968c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_GO, OMAP_HDQ_FLAG_CLEAR, 2978c2ecf20Sopenharmony_ci &tmp_status); 2988c2ecf20Sopenharmony_ci if (ret) 2998c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "timeout waiting INIT&GO bits" 3008c2ecf20Sopenharmony_ci " return to zero, %x\n", tmp_status); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ciout: 3038c2ecf20Sopenharmony_ci mutex_unlock(&hdq_data->hdq_mutex); 3048c2ecf20Sopenharmony_cirtn: 3058c2ecf20Sopenharmony_ci return ret; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int hdq_read_byte(struct hdq_data *hdq_data, u8 *val) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci int ret = 0; 3118c2ecf20Sopenharmony_ci u8 status; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); 3148c2ecf20Sopenharmony_ci if (ret < 0) { 3158c2ecf20Sopenharmony_ci ret = -EINTR; 3168c2ecf20Sopenharmony_ci goto rtn; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (pm_runtime_suspended(hdq_data->dev)) { 3208c2ecf20Sopenharmony_ci ret = -EINVAL; 3218c2ecf20Sopenharmony_ci goto out; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (!(hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) { 3258c2ecf20Sopenharmony_ci hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 3268c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO, 3278c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO); 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * The RX comes immediately after TX. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci wait_event_timeout(hdq_wait_queue, 3328c2ecf20Sopenharmony_ci (hdq_data->hdq_irqstatus 3338c2ecf20Sopenharmony_ci & (OMAP_HDQ_INT_STATUS_RXCOMPLETE | 3348c2ecf20Sopenharmony_ci OMAP_HDQ_INT_STATUS_TIMEOUT)), 3358c2ecf20Sopenharmony_ci OMAP_HDQ_TIMEOUT); 3368c2ecf20Sopenharmony_ci status = hdq_reset_irqstatus(hdq_data, 3378c2ecf20Sopenharmony_ci OMAP_HDQ_INT_STATUS_RXCOMPLETE | 3388c2ecf20Sopenharmony_ci OMAP_HDQ_INT_STATUS_TIMEOUT); 3398c2ecf20Sopenharmony_ci hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 0, 3408c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_DIR); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* check irqstatus */ 3438c2ecf20Sopenharmony_ci if (!(status & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) { 3448c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "timeout waiting for" 3458c2ecf20Sopenharmony_ci " RXCOMPLETE, %x", status); 3468c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 3478c2ecf20Sopenharmony_ci goto out; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci } else { /* interrupt had occurred before hdq_read_byte was called */ 3508c2ecf20Sopenharmony_ci hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_RXCOMPLETE); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci /* the data is ready. Read it in! */ 3538c2ecf20Sopenharmony_ci *val = hdq_reg_in(hdq_data, OMAP_HDQ_RX_DATA); 3548c2ecf20Sopenharmony_ciout: 3558c2ecf20Sopenharmony_ci mutex_unlock(&hdq_data->hdq_mutex); 3568c2ecf20Sopenharmony_cirtn: 3578c2ecf20Sopenharmony_ci return ret; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/* 3628c2ecf20Sopenharmony_ci * W1 triplet callback function - used for searching ROM addresses. 3638c2ecf20Sopenharmony_ci * Registered only when controller is in 1-wire mode. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_cistatic u8 omap_w1_triplet(void *_hdq, u8 bdir) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci u8 id_bit, comp_bit; 3688c2ecf20Sopenharmony_ci int err; 3698c2ecf20Sopenharmony_ci u8 ret = 0x3; /* no slaves responded */ 3708c2ecf20Sopenharmony_ci struct hdq_data *hdq_data = _hdq; 3718c2ecf20Sopenharmony_ci u8 ctrl = OMAP_HDQ_CTRL_STATUS_SINGLE | OMAP_HDQ_CTRL_STATUS_GO | 3728c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK; 3738c2ecf20Sopenharmony_ci u8 mask = ctrl | OMAP_HDQ_CTRL_STATUS_DIR; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci err = pm_runtime_get_sync(hdq_data->dev); 3768c2ecf20Sopenharmony_ci if (err < 0) { 3778c2ecf20Sopenharmony_ci pm_runtime_put_noidle(hdq_data->dev); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci err = mutex_lock_interruptible(&hdq_data->hdq_mutex); 3838c2ecf20Sopenharmony_ci if (err < 0) { 3848c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); 3858c2ecf20Sopenharmony_ci goto rtn; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* read id_bit */ 3898c2ecf20Sopenharmony_ci hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS, 3908c2ecf20Sopenharmony_ci ctrl | OMAP_HDQ_CTRL_STATUS_DIR, mask); 3918c2ecf20Sopenharmony_ci err = wait_event_timeout(hdq_wait_queue, 3928c2ecf20Sopenharmony_ci (hdq_data->hdq_irqstatus 3938c2ecf20Sopenharmony_ci & OMAP_HDQ_INT_STATUS_RXCOMPLETE), 3948c2ecf20Sopenharmony_ci OMAP_HDQ_TIMEOUT); 3958c2ecf20Sopenharmony_ci /* Must clear irqstatus for another RXCOMPLETE interrupt */ 3968c2ecf20Sopenharmony_ci hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_RXCOMPLETE); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (err == 0) { 3998c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "RX wait elapsed\n"); 4008c2ecf20Sopenharmony_ci goto out; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci id_bit = (hdq_reg_in(_hdq, OMAP_HDQ_RX_DATA) & 0x01); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* read comp_bit */ 4058c2ecf20Sopenharmony_ci hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS, 4068c2ecf20Sopenharmony_ci ctrl | OMAP_HDQ_CTRL_STATUS_DIR, mask); 4078c2ecf20Sopenharmony_ci err = wait_event_timeout(hdq_wait_queue, 4088c2ecf20Sopenharmony_ci (hdq_data->hdq_irqstatus 4098c2ecf20Sopenharmony_ci & OMAP_HDQ_INT_STATUS_RXCOMPLETE), 4108c2ecf20Sopenharmony_ci OMAP_HDQ_TIMEOUT); 4118c2ecf20Sopenharmony_ci /* Must clear irqstatus for another RXCOMPLETE interrupt */ 4128c2ecf20Sopenharmony_ci hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_RXCOMPLETE); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (err == 0) { 4158c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "RX wait elapsed\n"); 4168c2ecf20Sopenharmony_ci goto out; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci comp_bit = (hdq_reg_in(_hdq, OMAP_HDQ_RX_DATA) & 0x01); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (id_bit && comp_bit) { 4218c2ecf20Sopenharmony_ci ret = 0x03; /* no slaves responded */ 4228c2ecf20Sopenharmony_ci goto out; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci if (!id_bit && !comp_bit) { 4258c2ecf20Sopenharmony_ci /* Both bits are valid, take the direction given */ 4268c2ecf20Sopenharmony_ci ret = bdir ? 0x04 : 0; 4278c2ecf20Sopenharmony_ci } else { 4288c2ecf20Sopenharmony_ci /* Only one bit is valid, take that direction */ 4298c2ecf20Sopenharmony_ci bdir = id_bit; 4308c2ecf20Sopenharmony_ci ret = id_bit ? 0x05 : 0x02; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* write bdir bit */ 4348c2ecf20Sopenharmony_ci hdq_reg_out(_hdq, OMAP_HDQ_TX_DATA, bdir); 4358c2ecf20Sopenharmony_ci hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS, ctrl, mask); 4368c2ecf20Sopenharmony_ci err = wait_event_timeout(hdq_wait_queue, 4378c2ecf20Sopenharmony_ci (hdq_data->hdq_irqstatus 4388c2ecf20Sopenharmony_ci & OMAP_HDQ_INT_STATUS_TXCOMPLETE), 4398c2ecf20Sopenharmony_ci OMAP_HDQ_TIMEOUT); 4408c2ecf20Sopenharmony_ci /* Must clear irqstatus for another TXCOMPLETE interrupt */ 4418c2ecf20Sopenharmony_ci hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_TXCOMPLETE); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (err == 0) { 4448c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "TX wait elapsed\n"); 4458c2ecf20Sopenharmony_ci goto out; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS, 0, 4498c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_SINGLE); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ciout: 4528c2ecf20Sopenharmony_ci mutex_unlock(&hdq_data->hdq_mutex); 4538c2ecf20Sopenharmony_cirtn: 4548c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(hdq_data->dev); 4558c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(hdq_data->dev); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return ret; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/* reset callback */ 4618c2ecf20Sopenharmony_cistatic u8 omap_w1_reset_bus(void *_hdq) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct hdq_data *hdq_data = _hdq; 4648c2ecf20Sopenharmony_ci int err; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci err = pm_runtime_get_sync(hdq_data->dev); 4678c2ecf20Sopenharmony_ci if (err < 0) { 4688c2ecf20Sopenharmony_ci pm_runtime_put_noidle(hdq_data->dev); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return err; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci omap_hdq_break(hdq_data); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(hdq_data->dev); 4768c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(hdq_data->dev); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci/* Read a byte of data from the device */ 4828c2ecf20Sopenharmony_cistatic u8 omap_w1_read_byte(void *_hdq) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct hdq_data *hdq_data = _hdq; 4858c2ecf20Sopenharmony_ci u8 val = 0; 4868c2ecf20Sopenharmony_ci int ret; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(hdq_data->dev); 4898c2ecf20Sopenharmony_ci if (ret < 0) { 4908c2ecf20Sopenharmony_ci pm_runtime_put_noidle(hdq_data->dev); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return -1; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci ret = hdq_read_byte(hdq_data, &val); 4968c2ecf20Sopenharmony_ci if (ret) 4978c2ecf20Sopenharmony_ci val = -1; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(hdq_data->dev); 5008c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(hdq_data->dev); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return val; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/* Write a byte of data to the device */ 5068c2ecf20Sopenharmony_cistatic void omap_w1_write_byte(void *_hdq, u8 byte) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct hdq_data *hdq_data = _hdq; 5098c2ecf20Sopenharmony_ci int ret; 5108c2ecf20Sopenharmony_ci u8 status; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(hdq_data->dev); 5138c2ecf20Sopenharmony_ci if (ret < 0) { 5148c2ecf20Sopenharmony_ci pm_runtime_put_noidle(hdq_data->dev); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* 5208c2ecf20Sopenharmony_ci * We need to reset the slave before 5218c2ecf20Sopenharmony_ci * issuing the SKIP ROM command, else 5228c2ecf20Sopenharmony_ci * the slave will not work. 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_ci if (byte == W1_SKIP_ROM) 5258c2ecf20Sopenharmony_ci omap_hdq_break(hdq_data); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci ret = hdq_write_byte(hdq_data, byte, &status); 5288c2ecf20Sopenharmony_ci if (ret < 0) { 5298c2ecf20Sopenharmony_ci dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status); 5308c2ecf20Sopenharmony_ci goto out_err; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ciout_err: 5348c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(hdq_data->dev); 5358c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(hdq_data->dev); 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic struct w1_bus_master omap_w1_master = { 5398c2ecf20Sopenharmony_ci .read_byte = omap_w1_read_byte, 5408c2ecf20Sopenharmony_ci .write_byte = omap_w1_write_byte, 5418c2ecf20Sopenharmony_ci .reset_bus = omap_w1_reset_bus, 5428c2ecf20Sopenharmony_ci}; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int __maybe_unused omap_hdq_runtime_suspend(struct device *dev) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct hdq_data *hdq_data = dev_get_drvdata(dev); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci hdq_reg_out(hdq_data, 0, hdq_data->mode); 5498c2ecf20Sopenharmony_ci hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int __maybe_unused omap_hdq_runtime_resume(struct device *dev) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct hdq_data *hdq_data = dev_get_drvdata(dev); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* select HDQ/1W mode & enable clocks */ 5598c2ecf20Sopenharmony_ci hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS, 5608c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_CLOCKENABLE | 5618c2ecf20Sopenharmony_ci OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK | 5628c2ecf20Sopenharmony_ci hdq_data->mode); 5638c2ecf20Sopenharmony_ci hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic const struct dev_pm_ops omap_hdq_pm_ops = { 5698c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(omap_hdq_runtime_suspend, 5708c2ecf20Sopenharmony_ci omap_hdq_runtime_resume, NULL) 5718c2ecf20Sopenharmony_ci}; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int omap_hdq_probe(struct platform_device *pdev) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5768c2ecf20Sopenharmony_ci struct hdq_data *hdq_data; 5778c2ecf20Sopenharmony_ci int ret, irq; 5788c2ecf20Sopenharmony_ci u8 rev; 5798c2ecf20Sopenharmony_ci const char *mode; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci hdq_data = devm_kzalloc(dev, sizeof(*hdq_data), GFP_KERNEL); 5828c2ecf20Sopenharmony_ci if (!hdq_data) { 5838c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "unable to allocate memory\n"); 5848c2ecf20Sopenharmony_ci return -ENOMEM; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci hdq_data->dev = dev; 5888c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, hdq_data); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci hdq_data->hdq_base = devm_platform_ioremap_resource(pdev, 0); 5918c2ecf20Sopenharmony_ci if (IS_ERR(hdq_data->hdq_base)) 5928c2ecf20Sopenharmony_ci return PTR_ERR(hdq_data->hdq_base); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci mutex_init(&hdq_data->hdq_mutex); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci ret = of_property_read_string(pdev->dev.of_node, "ti,mode", &mode); 5978c2ecf20Sopenharmony_ci if (ret < 0 || !strcmp(mode, "hdq")) { 5988c2ecf20Sopenharmony_ci hdq_data->mode = 0; 5998c2ecf20Sopenharmony_ci omap_w1_master.search = omap_w1_search_bus; 6008c2ecf20Sopenharmony_ci } else { 6018c2ecf20Sopenharmony_ci hdq_data->mode = 1; 6028c2ecf20Sopenharmony_ci omap_w1_master.triplet = omap_w1_triplet; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 6068c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 6078c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 300); 6088c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(&pdev->dev); 6098c2ecf20Sopenharmony_ci if (ret < 0) { 6108c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 6118c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "pm_runtime_get_sync failed\n"); 6128c2ecf20Sopenharmony_ci goto err_w1; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION); 6168c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "OMAP HDQ Hardware Rev %c.%c. Driver in %s mode\n", 6178c2ecf20Sopenharmony_ci (rev >> 4) + '0', (rev & 0x0f) + '0', "Interrupt"); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci spin_lock_init(&hdq_data->hdq_spinlock); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 6228c2ecf20Sopenharmony_ci if (irq < 0) { 6238c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Failed to get IRQ: %d\n", irq); 6248c2ecf20Sopenharmony_ci ret = irq; 6258c2ecf20Sopenharmony_ci goto err_irq; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq, hdq_isr, 0, "omap_hdq", hdq_data); 6298c2ecf20Sopenharmony_ci if (ret < 0) { 6308c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "could not request irq\n"); 6318c2ecf20Sopenharmony_ci goto err_irq; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci omap_hdq_break(hdq_data); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 6378c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci omap_w1_master.data = hdq_data; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci ret = w1_add_master_device(&omap_w1_master); 6428c2ecf20Sopenharmony_ci if (ret) { 6438c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Failure in registering w1 master\n"); 6448c2ecf20Sopenharmony_ci goto err_w1; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return 0; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cierr_irq: 6508c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 6518c2ecf20Sopenharmony_cierr_w1: 6528c2ecf20Sopenharmony_ci pm_runtime_dont_use_autosuspend(&pdev->dev); 6538c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return ret; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic int omap_hdq_remove(struct platform_device *pdev) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci int active; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci active = pm_runtime_get_sync(&pdev->dev); 6638c2ecf20Sopenharmony_ci if (active < 0) 6648c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci w1_remove_master_device(&omap_w1_master); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci pm_runtime_dont_use_autosuspend(&pdev->dev); 6698c2ecf20Sopenharmony_ci if (active >= 0) 6708c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 6718c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return 0; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic const struct of_device_id omap_hdq_dt_ids[] = { 6778c2ecf20Sopenharmony_ci { .compatible = "ti,omap3-1w" }, 6788c2ecf20Sopenharmony_ci { .compatible = "ti,am4372-hdq" }, 6798c2ecf20Sopenharmony_ci {} 6808c2ecf20Sopenharmony_ci}; 6818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_hdq_dt_ids); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic struct platform_driver omap_hdq_driver = { 6848c2ecf20Sopenharmony_ci .probe = omap_hdq_probe, 6858c2ecf20Sopenharmony_ci .remove = omap_hdq_remove, 6868c2ecf20Sopenharmony_ci .driver = { 6878c2ecf20Sopenharmony_ci .name = "omap_hdq", 6888c2ecf20Sopenharmony_ci .of_match_table = omap_hdq_dt_ids, 6898c2ecf20Sopenharmony_ci .pm = &omap_hdq_pm_ops, 6908c2ecf20Sopenharmony_ci }, 6918c2ecf20Sopenharmony_ci}; 6928c2ecf20Sopenharmony_cimodule_platform_driver(omap_hdq_driver); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instruments"); 6958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HDQ-1W driver Library"); 6968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 697