18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2011-2017, The Linux Foundation 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/irq.h> 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 178c2ecf20Sopenharmony_ci#include "slimbus.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Manager registers */ 208c2ecf20Sopenharmony_ci#define MGR_CFG 0x200 218c2ecf20Sopenharmony_ci#define MGR_STATUS 0x204 228c2ecf20Sopenharmony_ci#define MGR_INT_EN 0x210 238c2ecf20Sopenharmony_ci#define MGR_INT_STAT 0x214 248c2ecf20Sopenharmony_ci#define MGR_INT_CLR 0x218 258c2ecf20Sopenharmony_ci#define MGR_TX_MSG 0x230 268c2ecf20Sopenharmony_ci#define MGR_RX_MSG 0x270 278c2ecf20Sopenharmony_ci#define MGR_IE_STAT 0x2F0 288c2ecf20Sopenharmony_ci#define MGR_VE_STAT 0x300 298c2ecf20Sopenharmony_ci#define MGR_CFG_ENABLE 1 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Framer registers */ 328c2ecf20Sopenharmony_ci#define FRM_CFG 0x400 338c2ecf20Sopenharmony_ci#define FRM_STAT 0x404 348c2ecf20Sopenharmony_ci#define FRM_INT_EN 0x410 358c2ecf20Sopenharmony_ci#define FRM_INT_STAT 0x414 368c2ecf20Sopenharmony_ci#define FRM_INT_CLR 0x418 378c2ecf20Sopenharmony_ci#define FRM_WAKEUP 0x41C 388c2ecf20Sopenharmony_ci#define FRM_CLKCTL_DONE 0x420 398c2ecf20Sopenharmony_ci#define FRM_IE_STAT 0x430 408c2ecf20Sopenharmony_ci#define FRM_VE_STAT 0x440 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Interface registers */ 438c2ecf20Sopenharmony_ci#define INTF_CFG 0x600 448c2ecf20Sopenharmony_ci#define INTF_STAT 0x604 458c2ecf20Sopenharmony_ci#define INTF_INT_EN 0x610 468c2ecf20Sopenharmony_ci#define INTF_INT_STAT 0x614 478c2ecf20Sopenharmony_ci#define INTF_INT_CLR 0x618 488c2ecf20Sopenharmony_ci#define INTF_IE_STAT 0x630 498c2ecf20Sopenharmony_ci#define INTF_VE_STAT 0x640 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Interrupt status bits */ 528c2ecf20Sopenharmony_ci#define MGR_INT_TX_NACKED_2 BIT(25) 538c2ecf20Sopenharmony_ci#define MGR_INT_MSG_BUF_CONTE BIT(26) 548c2ecf20Sopenharmony_ci#define MGR_INT_RX_MSG_RCVD BIT(30) 558c2ecf20Sopenharmony_ci#define MGR_INT_TX_MSG_SENT BIT(31) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* Framer config register settings */ 588c2ecf20Sopenharmony_ci#define FRM_ACTIVE 1 598c2ecf20Sopenharmony_ci#define CLK_GEAR 7 608c2ecf20Sopenharmony_ci#define ROOT_FREQ 11 618c2ecf20Sopenharmony_ci#define REF_CLK_GEAR 15 628c2ecf20Sopenharmony_ci#define INTR_WAKE 19 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \ 658c2ecf20Sopenharmony_ci ((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16)) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define SLIM_ROOT_FREQ 24576000 688c2ecf20Sopenharmony_ci#define QCOM_SLIM_AUTOSUSPEND 1000 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* MAX message size over control channel */ 718c2ecf20Sopenharmony_ci#define SLIM_MSGQ_BUF_LEN 40 728c2ecf20Sopenharmony_ci#define QCOM_TX_MSGS 2 738c2ecf20Sopenharmony_ci#define QCOM_RX_MSGS 8 748c2ecf20Sopenharmony_ci#define QCOM_BUF_ALLOC_RETRIES 10 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define CFG_PORT(r, v) ((v) ? CFG_PORT_V2(r) : CFG_PORT_V1(r)) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* V2 Component registers */ 798c2ecf20Sopenharmony_ci#define CFG_PORT_V2(r) ((r ## _V2)) 808c2ecf20Sopenharmony_ci#define COMP_CFG_V2 4 818c2ecf20Sopenharmony_ci#define COMP_TRUST_CFG_V2 0x3000 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* V1 Component registers */ 848c2ecf20Sopenharmony_ci#define CFG_PORT_V1(r) ((r ## _V1)) 858c2ecf20Sopenharmony_ci#define COMP_CFG_V1 0 868c2ecf20Sopenharmony_ci#define COMP_TRUST_CFG_V1 0x14 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Resource group info for manager, and non-ported generic device-components */ 898c2ecf20Sopenharmony_ci#define EE_MGR_RSC_GRP (1 << 10) 908c2ecf20Sopenharmony_ci#define EE_NGD_2 (2 << 6) 918c2ecf20Sopenharmony_ci#define EE_NGD_1 0 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistruct slim_ctrl_buf { 948c2ecf20Sopenharmony_ci void *base; 958c2ecf20Sopenharmony_ci spinlock_t lock; 968c2ecf20Sopenharmony_ci int head; 978c2ecf20Sopenharmony_ci int tail; 988c2ecf20Sopenharmony_ci int sl_sz; 998c2ecf20Sopenharmony_ci int n; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct qcom_slim_ctrl { 1038c2ecf20Sopenharmony_ci struct slim_controller ctrl; 1048c2ecf20Sopenharmony_ci struct slim_framer framer; 1058c2ecf20Sopenharmony_ci struct device *dev; 1068c2ecf20Sopenharmony_ci void __iomem *base; 1078c2ecf20Sopenharmony_ci void __iomem *slew_reg; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci struct slim_ctrl_buf rx; 1108c2ecf20Sopenharmony_ci struct slim_ctrl_buf tx; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci struct completion **wr_comp; 1138c2ecf20Sopenharmony_ci int irq; 1148c2ecf20Sopenharmony_ci struct workqueue_struct *rxwq; 1158c2ecf20Sopenharmony_ci struct work_struct wd; 1168c2ecf20Sopenharmony_ci struct clk *rclk; 1178c2ecf20Sopenharmony_ci struct clk *hclk; 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void qcom_slim_queue_tx(struct qcom_slim_ctrl *ctrl, void *buf, 1218c2ecf20Sopenharmony_ci u8 len, u32 tx_reg) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int count = (len + 3) >> 2; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci __iowrite32_copy(ctrl->base + tx_reg, buf, count); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Ensure Oder of subsequent writes */ 1288c2ecf20Sopenharmony_ci mb(); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void *slim_alloc_rxbuf(struct qcom_slim_ctrl *ctrl) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci unsigned long flags; 1348c2ecf20Sopenharmony_ci int idx; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->rx.lock, flags); 1378c2ecf20Sopenharmony_ci if ((ctrl->rx.tail + 1) % ctrl->rx.n == ctrl->rx.head) { 1388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->rx.lock, flags); 1398c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "RX QUEUE full!"); 1408c2ecf20Sopenharmony_ci return NULL; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci idx = ctrl->rx.tail; 1438c2ecf20Sopenharmony_ci ctrl->rx.tail = (ctrl->rx.tail + 1) % ctrl->rx.n; 1448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->rx.lock, flags); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return ctrl->rx.base + (idx * ctrl->rx.sl_sz); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void slim_ack_txn(struct qcom_slim_ctrl *ctrl, int err) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct completion *comp; 1528c2ecf20Sopenharmony_ci unsigned long flags; 1538c2ecf20Sopenharmony_ci int idx; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->tx.lock, flags); 1568c2ecf20Sopenharmony_ci idx = ctrl->tx.head; 1578c2ecf20Sopenharmony_ci ctrl->tx.head = (ctrl->tx.head + 1) % ctrl->tx.n; 1588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->tx.lock, flags); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci comp = ctrl->wr_comp[idx]; 1618c2ecf20Sopenharmony_ci ctrl->wr_comp[idx] = NULL; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci complete(comp); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic irqreturn_t qcom_slim_handle_tx_irq(struct qcom_slim_ctrl *ctrl, 1678c2ecf20Sopenharmony_ci u32 stat) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int err = 0; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (stat & MGR_INT_TX_MSG_SENT) 1728c2ecf20Sopenharmony_ci writel_relaxed(MGR_INT_TX_MSG_SENT, 1738c2ecf20Sopenharmony_ci ctrl->base + MGR_INT_CLR); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (stat & MGR_INT_TX_NACKED_2) { 1768c2ecf20Sopenharmony_ci u32 mgr_stat = readl_relaxed(ctrl->base + MGR_STATUS); 1778c2ecf20Sopenharmony_ci u32 mgr_ie_stat = readl_relaxed(ctrl->base + MGR_IE_STAT); 1788c2ecf20Sopenharmony_ci u32 frm_stat = readl_relaxed(ctrl->base + FRM_STAT); 1798c2ecf20Sopenharmony_ci u32 frm_cfg = readl_relaxed(ctrl->base + FRM_CFG); 1808c2ecf20Sopenharmony_ci u32 frm_intr_stat = readl_relaxed(ctrl->base + FRM_INT_STAT); 1818c2ecf20Sopenharmony_ci u32 frm_ie_stat = readl_relaxed(ctrl->base + FRM_IE_STAT); 1828c2ecf20Sopenharmony_ci u32 intf_stat = readl_relaxed(ctrl->base + INTF_STAT); 1838c2ecf20Sopenharmony_ci u32 intf_intr_stat = readl_relaxed(ctrl->base + INTF_INT_STAT); 1848c2ecf20Sopenharmony_ci u32 intf_ie_stat = readl_relaxed(ctrl->base + INTF_IE_STAT); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci writel_relaxed(MGR_INT_TX_NACKED_2, ctrl->base + MGR_INT_CLR); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "TX Nack MGR:int:0x%x, stat:0x%x\n", 1898c2ecf20Sopenharmony_ci stat, mgr_stat); 1908c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "TX Nack MGR:ie:0x%x\n", mgr_ie_stat); 1918c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "TX Nack FRM:int:0x%x, stat:0x%x\n", 1928c2ecf20Sopenharmony_ci frm_intr_stat, frm_stat); 1938c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "TX Nack FRM:cfg:0x%x, ie:0x%x\n", 1948c2ecf20Sopenharmony_ci frm_cfg, frm_ie_stat); 1958c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "TX Nack INTF:intr:0x%x, stat:0x%x\n", 1968c2ecf20Sopenharmony_ci intf_intr_stat, intf_stat); 1978c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "TX Nack INTF:ie:0x%x\n", 1988c2ecf20Sopenharmony_ci intf_ie_stat); 1998c2ecf20Sopenharmony_ci err = -ENOTCONN; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci slim_ack_txn(ctrl, err); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic irqreturn_t qcom_slim_handle_rx_irq(struct qcom_slim_ctrl *ctrl, 2088c2ecf20Sopenharmony_ci u32 stat) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci u32 *rx_buf, pkt[10]; 2118c2ecf20Sopenharmony_ci bool q_rx = false; 2128c2ecf20Sopenharmony_ci u8 mc, mt, len; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci pkt[0] = readl_relaxed(ctrl->base + MGR_RX_MSG); 2158c2ecf20Sopenharmony_ci mt = SLIM_HEADER_GET_MT(pkt[0]); 2168c2ecf20Sopenharmony_ci len = SLIM_HEADER_GET_RL(pkt[0]); 2178c2ecf20Sopenharmony_ci mc = SLIM_HEADER_GET_MC(pkt[0]>>8); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* 2208c2ecf20Sopenharmony_ci * this message cannot be handled by ISR, so 2218c2ecf20Sopenharmony_ci * let work-queue handle it 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci if (mt == SLIM_MSG_MT_CORE && mc == SLIM_MSG_MC_REPORT_PRESENT) { 2248c2ecf20Sopenharmony_ci rx_buf = (u32 *)slim_alloc_rxbuf(ctrl); 2258c2ecf20Sopenharmony_ci if (!rx_buf) { 2268c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "dropping RX:0x%x due to RX full\n", 2278c2ecf20Sopenharmony_ci pkt[0]); 2288c2ecf20Sopenharmony_ci goto rx_ret_irq; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci rx_buf[0] = pkt[0]; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci } else { 2338c2ecf20Sopenharmony_ci rx_buf = pkt; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci __ioread32_copy(rx_buf + 1, ctrl->base + MGR_RX_MSG + 4, 2378c2ecf20Sopenharmony_ci DIV_ROUND_UP(len, 4)); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci switch (mc) { 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci case SLIM_MSG_MC_REPORT_PRESENT: 2428c2ecf20Sopenharmony_ci q_rx = true; 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci case SLIM_MSG_MC_REPLY_INFORMATION: 2458c2ecf20Sopenharmony_ci case SLIM_MSG_MC_REPLY_VALUE: 2468c2ecf20Sopenharmony_ci slim_msg_response(&ctrl->ctrl, (u8 *)(rx_buf + 1), 2478c2ecf20Sopenharmony_ci (u8)(*rx_buf >> 24), (len - 4)); 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci default: 2508c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "unsupported MC,%x MT:%x\n", 2518c2ecf20Sopenharmony_ci mc, mt); 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_cirx_ret_irq: 2558c2ecf20Sopenharmony_ci writel(MGR_INT_RX_MSG_RCVD, ctrl->base + 2568c2ecf20Sopenharmony_ci MGR_INT_CLR); 2578c2ecf20Sopenharmony_ci if (q_rx) 2588c2ecf20Sopenharmony_ci queue_work(ctrl->rxwq, &ctrl->wd); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic irqreturn_t qcom_slim_interrupt(int irq, void *d) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct qcom_slim_ctrl *ctrl = d; 2668c2ecf20Sopenharmony_ci u32 stat = readl_relaxed(ctrl->base + MGR_INT_STAT); 2678c2ecf20Sopenharmony_ci int ret = IRQ_NONE; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (stat & MGR_INT_TX_MSG_SENT || stat & MGR_INT_TX_NACKED_2) 2708c2ecf20Sopenharmony_ci ret = qcom_slim_handle_tx_irq(ctrl, stat); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (stat & MGR_INT_RX_MSG_RCVD) 2738c2ecf20Sopenharmony_ci ret = qcom_slim_handle_rx_irq(ctrl, stat); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return ret; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int qcom_clk_pause_wakeup(struct slim_controller *sctrl) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct qcom_slim_ctrl *ctrl = dev_get_drvdata(sctrl->dev); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci clk_prepare_enable(ctrl->hclk); 2838c2ecf20Sopenharmony_ci clk_prepare_enable(ctrl->rclk); 2848c2ecf20Sopenharmony_ci enable_irq(ctrl->irq); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci writel_relaxed(1, ctrl->base + FRM_WAKEUP); 2878c2ecf20Sopenharmony_ci /* Make sure framer wakeup write goes through before ISR fires */ 2888c2ecf20Sopenharmony_ci mb(); 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * HW Workaround: Currently, slave is reporting lost-sync messages 2918c2ecf20Sopenharmony_ci * after SLIMbus comes out of clock pause. 2928c2ecf20Sopenharmony_ci * Transaction with slave fail before slave reports that message 2938c2ecf20Sopenharmony_ci * Give some time for that report to come 2948c2ecf20Sopenharmony_ci * SLIMbus wakes up in clock gear 10 at 24.576MHz. With each superframe 2958c2ecf20Sopenharmony_ci * being 250 usecs, we wait for 5-10 superframes here to ensure 2968c2ecf20Sopenharmony_ci * we get the message 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci usleep_range(1250, 2500); 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic void *slim_alloc_txbuf(struct qcom_slim_ctrl *ctrl, 3038c2ecf20Sopenharmony_ci struct slim_msg_txn *txn, 3048c2ecf20Sopenharmony_ci struct completion *done) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci unsigned long flags; 3078c2ecf20Sopenharmony_ci int idx; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->tx.lock, flags); 3108c2ecf20Sopenharmony_ci if (((ctrl->tx.head + 1) % ctrl->tx.n) == ctrl->tx.tail) { 3118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->tx.lock, flags); 3128c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "controller TX buf unavailable"); 3138c2ecf20Sopenharmony_ci return NULL; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci idx = ctrl->tx.tail; 3168c2ecf20Sopenharmony_ci ctrl->wr_comp[idx] = done; 3178c2ecf20Sopenharmony_ci ctrl->tx.tail = (ctrl->tx.tail + 1) % ctrl->tx.n; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->tx.lock, flags); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return ctrl->tx.base + (idx * ctrl->tx.sl_sz); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int qcom_xfer_msg(struct slim_controller *sctrl, 3268c2ecf20Sopenharmony_ci struct slim_msg_txn *txn) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct qcom_slim_ctrl *ctrl = dev_get_drvdata(sctrl->dev); 3298c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(done); 3308c2ecf20Sopenharmony_ci void *pbuf = slim_alloc_txbuf(ctrl, txn, &done); 3318c2ecf20Sopenharmony_ci unsigned long ms = txn->rl + HZ; 3328c2ecf20Sopenharmony_ci u8 *puc; 3338c2ecf20Sopenharmony_ci int ret = 0, timeout, retries = QCOM_BUF_ALLOC_RETRIES; 3348c2ecf20Sopenharmony_ci u8 la = txn->la; 3358c2ecf20Sopenharmony_ci u32 *head; 3368c2ecf20Sopenharmony_ci /* HW expects length field to be excluded */ 3378c2ecf20Sopenharmony_ci txn->rl--; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* spin till buffer is made available */ 3408c2ecf20Sopenharmony_ci if (!pbuf) { 3418c2ecf20Sopenharmony_ci while (retries--) { 3428c2ecf20Sopenharmony_ci usleep_range(10000, 15000); 3438c2ecf20Sopenharmony_ci pbuf = slim_alloc_txbuf(ctrl, txn, &done); 3448c2ecf20Sopenharmony_ci if (pbuf) 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (retries < 0 && !pbuf) 3508c2ecf20Sopenharmony_ci return -ENOMEM; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci puc = (u8 *)pbuf; 3538c2ecf20Sopenharmony_ci head = (u32 *)pbuf; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (txn->dt == SLIM_MSG_DEST_LOGICALADDR) { 3568c2ecf20Sopenharmony_ci *head = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, 3578c2ecf20Sopenharmony_ci txn->mc, 0, la); 3588c2ecf20Sopenharmony_ci puc += 3; 3598c2ecf20Sopenharmony_ci } else { 3608c2ecf20Sopenharmony_ci *head = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, 3618c2ecf20Sopenharmony_ci txn->mc, 1, la); 3628c2ecf20Sopenharmony_ci puc += 2; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (slim_tid_txn(txn->mt, txn->mc)) 3668c2ecf20Sopenharmony_ci *(puc++) = txn->tid; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (slim_ec_txn(txn->mt, txn->mc)) { 3698c2ecf20Sopenharmony_ci *(puc++) = (txn->ec & 0xFF); 3708c2ecf20Sopenharmony_ci *(puc++) = (txn->ec >> 8) & 0xFF; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (txn->msg && txn->msg->wbuf) 3748c2ecf20Sopenharmony_ci memcpy(puc, txn->msg->wbuf, txn->msg->num_bytes); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci qcom_slim_queue_tx(ctrl, head, txn->rl, MGR_TX_MSG); 3778c2ecf20Sopenharmony_ci timeout = wait_for_completion_timeout(&done, msecs_to_jiffies(ms)); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!timeout) { 3808c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc, 3818c2ecf20Sopenharmony_ci txn->mt); 3828c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return ret; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int qcom_set_laddr(struct slim_controller *sctrl, 3908c2ecf20Sopenharmony_ci struct slim_eaddr *ead, u8 laddr) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct qcom_slim_ctrl *ctrl = dev_get_drvdata(sctrl->dev); 3938c2ecf20Sopenharmony_ci struct { 3948c2ecf20Sopenharmony_ci __be16 manf_id; 3958c2ecf20Sopenharmony_ci __be16 prod_code; 3968c2ecf20Sopenharmony_ci u8 dev_index; 3978c2ecf20Sopenharmony_ci u8 instance; 3988c2ecf20Sopenharmony_ci u8 laddr; 3998c2ecf20Sopenharmony_ci } __packed p; 4008c2ecf20Sopenharmony_ci struct slim_val_inf msg = {0}; 4018c2ecf20Sopenharmony_ci DEFINE_SLIM_EDEST_TXN(txn, SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS, 4028c2ecf20Sopenharmony_ci 10, laddr, &msg); 4038c2ecf20Sopenharmony_ci int ret; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci p.manf_id = cpu_to_be16(ead->manf_id); 4068c2ecf20Sopenharmony_ci p.prod_code = cpu_to_be16(ead->prod_code); 4078c2ecf20Sopenharmony_ci p.dev_index = ead->dev_index; 4088c2ecf20Sopenharmony_ci p.instance = ead->instance; 4098c2ecf20Sopenharmony_ci p.laddr = laddr; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci msg.wbuf = (void *)&p; 4128c2ecf20Sopenharmony_ci msg.num_bytes = 7; 4138c2ecf20Sopenharmony_ci ret = slim_do_transfer(&ctrl->ctrl, &txn); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (ret) 4168c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "set LA:0x%x failed:ret:%d\n", 4178c2ecf20Sopenharmony_ci laddr, ret); 4188c2ecf20Sopenharmony_ci return ret; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic int slim_get_current_rxbuf(struct qcom_slim_ctrl *ctrl, void *buf) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci unsigned long flags; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->rx.lock, flags); 4268c2ecf20Sopenharmony_ci if (ctrl->rx.tail == ctrl->rx.head) { 4278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->rx.lock, flags); 4288c2ecf20Sopenharmony_ci return -ENODATA; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci memcpy(buf, ctrl->rx.base + (ctrl->rx.head * ctrl->rx.sl_sz), 4318c2ecf20Sopenharmony_ci ctrl->rx.sl_sz); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ctrl->rx.head = (ctrl->rx.head + 1) % ctrl->rx.n; 4348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->rx.lock, flags); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void qcom_slim_rxwq(struct work_struct *work) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci u8 buf[SLIM_MSGQ_BUF_LEN]; 4428c2ecf20Sopenharmony_ci u8 mc, mt; 4438c2ecf20Sopenharmony_ci int ret; 4448c2ecf20Sopenharmony_ci struct qcom_slim_ctrl *ctrl = container_of(work, struct qcom_slim_ctrl, 4458c2ecf20Sopenharmony_ci wd); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci while ((slim_get_current_rxbuf(ctrl, buf)) != -ENODATA) { 4488c2ecf20Sopenharmony_ci mt = SLIM_HEADER_GET_MT(buf[0]); 4498c2ecf20Sopenharmony_ci mc = SLIM_HEADER_GET_MC(buf[1]); 4508c2ecf20Sopenharmony_ci if (mt == SLIM_MSG_MT_CORE && 4518c2ecf20Sopenharmony_ci mc == SLIM_MSG_MC_REPORT_PRESENT) { 4528c2ecf20Sopenharmony_ci struct slim_eaddr ea; 4538c2ecf20Sopenharmony_ci u8 laddr; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci ea.manf_id = be16_to_cpup((__be16 *)&buf[2]); 4568c2ecf20Sopenharmony_ci ea.prod_code = be16_to_cpup((__be16 *)&buf[4]); 4578c2ecf20Sopenharmony_ci ea.dev_index = buf[6]; 4588c2ecf20Sopenharmony_ci ea.instance = buf[7]; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci ret = slim_device_report_present(&ctrl->ctrl, &ea, 4618c2ecf20Sopenharmony_ci &laddr); 4628c2ecf20Sopenharmony_ci if (ret < 0) 4638c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "assign laddr failed:%d\n", 4648c2ecf20Sopenharmony_ci ret); 4658c2ecf20Sopenharmony_ci } else { 4668c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "unexpected message:mc:%x, mt:%x\n", 4678c2ecf20Sopenharmony_ci mc, mt); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void qcom_slim_prg_slew(struct platform_device *pdev, 4738c2ecf20Sopenharmony_ci struct qcom_slim_ctrl *ctrl) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci if (!ctrl->slew_reg) { 4768c2ecf20Sopenharmony_ci /* SLEW RATE register for this SLIMbus */ 4778c2ecf20Sopenharmony_ci ctrl->slew_reg = devm_platform_ioremap_resource_byname(pdev, "slew"); 4788c2ecf20Sopenharmony_ci if (IS_ERR(ctrl->slew_reg)) 4798c2ecf20Sopenharmony_ci return; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci writel_relaxed(1, ctrl->slew_reg); 4838c2ecf20Sopenharmony_ci /* Make sure SLIMbus-slew rate enabling goes through */ 4848c2ecf20Sopenharmony_ci wmb(); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int qcom_slim_probe(struct platform_device *pdev) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct qcom_slim_ctrl *ctrl; 4908c2ecf20Sopenharmony_ci struct slim_controller *sctrl; 4918c2ecf20Sopenharmony_ci struct resource *slim_mem; 4928c2ecf20Sopenharmony_ci int ret, ver; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); 4958c2ecf20Sopenharmony_ci if (!ctrl) 4968c2ecf20Sopenharmony_ci return -ENOMEM; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci ctrl->hclk = devm_clk_get(&pdev->dev, "iface"); 4998c2ecf20Sopenharmony_ci if (IS_ERR(ctrl->hclk)) 5008c2ecf20Sopenharmony_ci return PTR_ERR(ctrl->hclk); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ctrl->rclk = devm_clk_get(&pdev->dev, "core"); 5038c2ecf20Sopenharmony_ci if (IS_ERR(ctrl->rclk)) 5048c2ecf20Sopenharmony_ci return PTR_ERR(ctrl->rclk); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci ret = clk_set_rate(ctrl->rclk, SLIM_ROOT_FREQ); 5078c2ecf20Sopenharmony_ci if (ret) { 5088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ref-clock set-rate failed:%d\n", ret); 5098c2ecf20Sopenharmony_ci return ret; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci ctrl->irq = platform_get_irq(pdev, 0); 5138c2ecf20Sopenharmony_ci if (ctrl->irq < 0) { 5148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no slimbus IRQ\n"); 5158c2ecf20Sopenharmony_ci return ctrl->irq; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci sctrl = &ctrl->ctrl; 5198c2ecf20Sopenharmony_ci sctrl->dev = &pdev->dev; 5208c2ecf20Sopenharmony_ci ctrl->dev = &pdev->dev; 5218c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ctrl); 5228c2ecf20Sopenharmony_ci dev_set_drvdata(ctrl->dev, ctrl); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); 5258c2ecf20Sopenharmony_ci ctrl->base = devm_ioremap_resource(ctrl->dev, slim_mem); 5268c2ecf20Sopenharmony_ci if (IS_ERR(ctrl->base)) 5278c2ecf20Sopenharmony_ci return PTR_ERR(ctrl->base); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci sctrl->set_laddr = qcom_set_laddr; 5308c2ecf20Sopenharmony_ci sctrl->xfer_msg = qcom_xfer_msg; 5318c2ecf20Sopenharmony_ci sctrl->wakeup = qcom_clk_pause_wakeup; 5328c2ecf20Sopenharmony_ci ctrl->tx.n = QCOM_TX_MSGS; 5338c2ecf20Sopenharmony_ci ctrl->tx.sl_sz = SLIM_MSGQ_BUF_LEN; 5348c2ecf20Sopenharmony_ci ctrl->rx.n = QCOM_RX_MSGS; 5358c2ecf20Sopenharmony_ci ctrl->rx.sl_sz = SLIM_MSGQ_BUF_LEN; 5368c2ecf20Sopenharmony_ci ctrl->wr_comp = kcalloc(QCOM_TX_MSGS, sizeof(struct completion *), 5378c2ecf20Sopenharmony_ci GFP_KERNEL); 5388c2ecf20Sopenharmony_ci if (!ctrl->wr_comp) 5398c2ecf20Sopenharmony_ci return -ENOMEM; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci spin_lock_init(&ctrl->rx.lock); 5428c2ecf20Sopenharmony_ci spin_lock_init(&ctrl->tx.lock); 5438c2ecf20Sopenharmony_ci INIT_WORK(&ctrl->wd, qcom_slim_rxwq); 5448c2ecf20Sopenharmony_ci ctrl->rxwq = create_singlethread_workqueue("qcom_slim_rx"); 5458c2ecf20Sopenharmony_ci if (!ctrl->rxwq) { 5468c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "Failed to start Rx WQ\n"); 5478c2ecf20Sopenharmony_ci return -ENOMEM; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci ctrl->framer.rootfreq = SLIM_ROOT_FREQ / 8; 5518c2ecf20Sopenharmony_ci ctrl->framer.superfreq = 5528c2ecf20Sopenharmony_ci ctrl->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8; 5538c2ecf20Sopenharmony_ci sctrl->a_framer = &ctrl->framer; 5548c2ecf20Sopenharmony_ci sctrl->clkgear = SLIM_MAX_CLK_GEAR; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci qcom_slim_prg_slew(pdev, ctrl); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, ctrl->irq, qcom_slim_interrupt, 5598c2ecf20Sopenharmony_ci IRQF_TRIGGER_HIGH, "qcom_slim_irq", ctrl); 5608c2ecf20Sopenharmony_ci if (ret) { 5618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request IRQ failed\n"); 5628c2ecf20Sopenharmony_ci goto err_request_irq_failed; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ctrl->hclk); 5668c2ecf20Sopenharmony_ci if (ret) 5678c2ecf20Sopenharmony_ci goto err_hclk_enable_failed; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ctrl->rclk); 5708c2ecf20Sopenharmony_ci if (ret) 5718c2ecf20Sopenharmony_ci goto err_rclk_enable_failed; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci ctrl->tx.base = devm_kcalloc(&pdev->dev, ctrl->tx.n, ctrl->tx.sl_sz, 5748c2ecf20Sopenharmony_ci GFP_KERNEL); 5758c2ecf20Sopenharmony_ci if (!ctrl->tx.base) { 5768c2ecf20Sopenharmony_ci ret = -ENOMEM; 5778c2ecf20Sopenharmony_ci goto err; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci ctrl->rx.base = devm_kcalloc(&pdev->dev,ctrl->rx.n, ctrl->rx.sl_sz, 5818c2ecf20Sopenharmony_ci GFP_KERNEL); 5828c2ecf20Sopenharmony_ci if (!ctrl->rx.base) { 5838c2ecf20Sopenharmony_ci ret = -ENOMEM; 5848c2ecf20Sopenharmony_ci goto err; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* Register with framework before enabling frame, clock */ 5888c2ecf20Sopenharmony_ci ret = slim_register_controller(&ctrl->ctrl); 5898c2ecf20Sopenharmony_ci if (ret) { 5908c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "error adding controller\n"); 5918c2ecf20Sopenharmony_ci goto err; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci ver = readl_relaxed(ctrl->base); 5958c2ecf20Sopenharmony_ci /* Version info in 16 MSbits */ 5968c2ecf20Sopenharmony_ci ver >>= 16; 5978c2ecf20Sopenharmony_ci /* Component register initialization */ 5988c2ecf20Sopenharmony_ci writel(1, ctrl->base + CFG_PORT(COMP_CFG, ver)); 5998c2ecf20Sopenharmony_ci writel((EE_MGR_RSC_GRP | EE_NGD_2 | EE_NGD_1), 6008c2ecf20Sopenharmony_ci ctrl->base + CFG_PORT(COMP_TRUST_CFG, ver)); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci writel((MGR_INT_TX_NACKED_2 | 6038c2ecf20Sopenharmony_ci MGR_INT_MSG_BUF_CONTE | MGR_INT_RX_MSG_RCVD | 6048c2ecf20Sopenharmony_ci MGR_INT_TX_MSG_SENT), ctrl->base + MGR_INT_EN); 6058c2ecf20Sopenharmony_ci writel(1, ctrl->base + MGR_CFG); 6068c2ecf20Sopenharmony_ci /* Framer register initialization */ 6078c2ecf20Sopenharmony_ci writel((1 << INTR_WAKE) | (0xA << REF_CLK_GEAR) | 6088c2ecf20Sopenharmony_ci (0xA << CLK_GEAR) | (1 << ROOT_FREQ) | (1 << FRM_ACTIVE) | 1, 6098c2ecf20Sopenharmony_ci ctrl->base + FRM_CFG); 6108c2ecf20Sopenharmony_ci writel(MGR_CFG_ENABLE, ctrl->base + MGR_CFG); 6118c2ecf20Sopenharmony_ci writel(1, ctrl->base + INTF_CFG); 6128c2ecf20Sopenharmony_ci writel(1, ctrl->base + CFG_PORT(COMP_CFG, ver)); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 6158c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, QCOM_SLIM_AUTOSUSPEND); 6168c2ecf20Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 6178c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 6188c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci dev_dbg(ctrl->dev, "QCOM SB controller is up:ver:0x%x!\n", ver); 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cierr: 6248c2ecf20Sopenharmony_ci clk_disable_unprepare(ctrl->rclk); 6258c2ecf20Sopenharmony_cierr_rclk_enable_failed: 6268c2ecf20Sopenharmony_ci clk_disable_unprepare(ctrl->hclk); 6278c2ecf20Sopenharmony_cierr_hclk_enable_failed: 6288c2ecf20Sopenharmony_cierr_request_irq_failed: 6298c2ecf20Sopenharmony_ci destroy_workqueue(ctrl->rxwq); 6308c2ecf20Sopenharmony_ci return ret; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int qcom_slim_remove(struct platform_device *pdev) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct qcom_slim_ctrl *ctrl = platform_get_drvdata(pdev); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 6388c2ecf20Sopenharmony_ci slim_unregister_controller(&ctrl->ctrl); 6398c2ecf20Sopenharmony_ci clk_disable_unprepare(ctrl->rclk); 6408c2ecf20Sopenharmony_ci clk_disable_unprepare(ctrl->hclk); 6418c2ecf20Sopenharmony_ci destroy_workqueue(ctrl->rxwq); 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci/* 6468c2ecf20Sopenharmony_ci * If PM_RUNTIME is not defined, these 2 functions become helper 6478c2ecf20Sopenharmony_ci * functions to be called from system suspend/resume. 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 6508c2ecf20Sopenharmony_cistatic int qcom_slim_runtime_suspend(struct device *device) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci struct qcom_slim_ctrl *ctrl = dev_get_drvdata(device); 6538c2ecf20Sopenharmony_ci int ret; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci dev_dbg(device, "pm_runtime: suspending...\n"); 6568c2ecf20Sopenharmony_ci ret = slim_ctrl_clk_pause(&ctrl->ctrl, false, SLIM_CLK_UNSPECIFIED); 6578c2ecf20Sopenharmony_ci if (ret) { 6588c2ecf20Sopenharmony_ci dev_err(device, "clk pause not entered:%d", ret); 6598c2ecf20Sopenharmony_ci } else { 6608c2ecf20Sopenharmony_ci disable_irq(ctrl->irq); 6618c2ecf20Sopenharmony_ci clk_disable_unprepare(ctrl->hclk); 6628c2ecf20Sopenharmony_ci clk_disable_unprepare(ctrl->rclk); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci return ret; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic int qcom_slim_runtime_resume(struct device *device) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct qcom_slim_ctrl *ctrl = dev_get_drvdata(device); 6708c2ecf20Sopenharmony_ci int ret = 0; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci dev_dbg(device, "pm_runtime: resuming...\n"); 6738c2ecf20Sopenharmony_ci ret = slim_ctrl_clk_pause(&ctrl->ctrl, true, 0); 6748c2ecf20Sopenharmony_ci if (ret) 6758c2ecf20Sopenharmony_ci dev_err(device, "clk pause not exited:%d", ret); 6768c2ecf20Sopenharmony_ci return ret; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci#endif 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 6818c2ecf20Sopenharmony_cistatic int qcom_slim_suspend(struct device *dev) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci int ret = 0; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (!pm_runtime_enabled(dev) || 6868c2ecf20Sopenharmony_ci (!pm_runtime_suspended(dev))) { 6878c2ecf20Sopenharmony_ci dev_dbg(dev, "system suspend"); 6888c2ecf20Sopenharmony_ci ret = qcom_slim_runtime_suspend(dev); 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic int qcom_slim_resume(struct device *dev) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) { 6978c2ecf20Sopenharmony_ci int ret; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci dev_dbg(dev, "system resume"); 7008c2ecf20Sopenharmony_ci ret = qcom_slim_runtime_resume(dev); 7018c2ecf20Sopenharmony_ci if (!ret) { 7028c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 7038c2ecf20Sopenharmony_ci pm_request_autosuspend(dev); 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci return ret; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci return 0; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic const struct dev_pm_ops qcom_slim_dev_pm_ops = { 7138c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(qcom_slim_suspend, qcom_slim_resume) 7148c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS( 7158c2ecf20Sopenharmony_ci qcom_slim_runtime_suspend, 7168c2ecf20Sopenharmony_ci qcom_slim_runtime_resume, 7178c2ecf20Sopenharmony_ci NULL 7188c2ecf20Sopenharmony_ci ) 7198c2ecf20Sopenharmony_ci}; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_slim_dt_match[] = { 7228c2ecf20Sopenharmony_ci { .compatible = "qcom,slim", }, 7238c2ecf20Sopenharmony_ci { .compatible = "qcom,apq8064-slim", }, 7248c2ecf20Sopenharmony_ci {} 7258c2ecf20Sopenharmony_ci}; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic struct platform_driver qcom_slim_driver = { 7288c2ecf20Sopenharmony_ci .probe = qcom_slim_probe, 7298c2ecf20Sopenharmony_ci .remove = qcom_slim_remove, 7308c2ecf20Sopenharmony_ci .driver = { 7318c2ecf20Sopenharmony_ci .name = "qcom_slim_ctrl", 7328c2ecf20Sopenharmony_ci .of_match_table = qcom_slim_dt_match, 7338c2ecf20Sopenharmony_ci .pm = &qcom_slim_dev_pm_ops, 7348c2ecf20Sopenharmony_ci }, 7358c2ecf20Sopenharmony_ci}; 7368c2ecf20Sopenharmony_cimodule_platform_driver(qcom_slim_driver); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm SLIMbus Controller"); 740