1ad7fcbc3SSagar Dharia // SPDX-License-Identifier: GPL-2.0 2ad7fcbc3SSagar Dharia /* 3ad7fcbc3SSagar Dharia * Copyright (c) 2011-2017, The Linux Foundation 4ad7fcbc3SSagar Dharia */ 5ad7fcbc3SSagar Dharia 6ad7fcbc3SSagar Dharia #include <linux/irq.h> 7ad7fcbc3SSagar Dharia #include <linux/kernel.h> 8ad7fcbc3SSagar Dharia #include <linux/init.h> 9ad7fcbc3SSagar Dharia #include <linux/slab.h> 10ad7fcbc3SSagar Dharia #include <linux/io.h> 11ad7fcbc3SSagar Dharia #include <linux/interrupt.h> 12ad7fcbc3SSagar Dharia #include <linux/platform_device.h> 13ad7fcbc3SSagar Dharia #include <linux/delay.h> 14ad7fcbc3SSagar Dharia #include <linux/clk.h> 15ad7fcbc3SSagar Dharia #include <linux/of.h> 16c088c335SSagar Dharia #include <linux/pm_runtime.h> 17ad7fcbc3SSagar Dharia #include "slimbus.h" 18ad7fcbc3SSagar Dharia 19ad7fcbc3SSagar Dharia /* Manager registers */ 20ad7fcbc3SSagar Dharia #define MGR_CFG 0x200 21ad7fcbc3SSagar Dharia #define MGR_STATUS 0x204 22ad7fcbc3SSagar Dharia #define MGR_INT_EN 0x210 23ad7fcbc3SSagar Dharia #define MGR_INT_STAT 0x214 24ad7fcbc3SSagar Dharia #define MGR_INT_CLR 0x218 25ad7fcbc3SSagar Dharia #define MGR_TX_MSG 0x230 26ad7fcbc3SSagar Dharia #define MGR_RX_MSG 0x270 27ad7fcbc3SSagar Dharia #define MGR_IE_STAT 0x2F0 28ad7fcbc3SSagar Dharia #define MGR_VE_STAT 0x300 29ad7fcbc3SSagar Dharia #define MGR_CFG_ENABLE 1 30ad7fcbc3SSagar Dharia 31ad7fcbc3SSagar Dharia /* Framer registers */ 32ad7fcbc3SSagar Dharia #define FRM_CFG 0x400 33ad7fcbc3SSagar Dharia #define FRM_STAT 0x404 34ad7fcbc3SSagar Dharia #define FRM_INT_EN 0x410 35ad7fcbc3SSagar Dharia #define FRM_INT_STAT 0x414 36ad7fcbc3SSagar Dharia #define FRM_INT_CLR 0x418 37ad7fcbc3SSagar Dharia #define FRM_WAKEUP 0x41C 38ad7fcbc3SSagar Dharia #define FRM_CLKCTL_DONE 0x420 39ad7fcbc3SSagar Dharia #define FRM_IE_STAT 0x430 40ad7fcbc3SSagar Dharia #define FRM_VE_STAT 0x440 41ad7fcbc3SSagar Dharia 42ad7fcbc3SSagar Dharia /* Interface registers */ 43ad7fcbc3SSagar Dharia #define INTF_CFG 0x600 44ad7fcbc3SSagar Dharia #define INTF_STAT 0x604 45ad7fcbc3SSagar Dharia #define INTF_INT_EN 0x610 46ad7fcbc3SSagar Dharia #define INTF_INT_STAT 0x614 47ad7fcbc3SSagar Dharia #define INTF_INT_CLR 0x618 48ad7fcbc3SSagar Dharia #define INTF_IE_STAT 0x630 49ad7fcbc3SSagar Dharia #define INTF_VE_STAT 0x640 50ad7fcbc3SSagar Dharia 51ad7fcbc3SSagar Dharia /* Interrupt status bits */ 52ad7fcbc3SSagar Dharia #define MGR_INT_TX_NACKED_2 BIT(25) 53ad7fcbc3SSagar Dharia #define MGR_INT_MSG_BUF_CONTE BIT(26) 54ad7fcbc3SSagar Dharia #define MGR_INT_RX_MSG_RCVD BIT(30) 55ad7fcbc3SSagar Dharia #define MGR_INT_TX_MSG_SENT BIT(31) 56ad7fcbc3SSagar Dharia 57ad7fcbc3SSagar Dharia /* Framer config register settings */ 58ad7fcbc3SSagar Dharia #define FRM_ACTIVE 1 59ad7fcbc3SSagar Dharia #define CLK_GEAR 7 60ad7fcbc3SSagar Dharia #define ROOT_FREQ 11 61ad7fcbc3SSagar Dharia #define REF_CLK_GEAR 15 62ad7fcbc3SSagar Dharia #define INTR_WAKE 19 63ad7fcbc3SSagar Dharia 64ad7fcbc3SSagar Dharia #define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \ 65ad7fcbc3SSagar Dharia ((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16)) 66ad7fcbc3SSagar Dharia 67ad7fcbc3SSagar Dharia #define SLIM_ROOT_FREQ 24576000 68c088c335SSagar Dharia #define QCOM_SLIM_AUTOSUSPEND 1000 69ad7fcbc3SSagar Dharia 70ad7fcbc3SSagar Dharia /* MAX message size over control channel */ 71ad7fcbc3SSagar Dharia #define SLIM_MSGQ_BUF_LEN 40 72ad7fcbc3SSagar Dharia #define QCOM_TX_MSGS 2 73ad7fcbc3SSagar Dharia #define QCOM_RX_MSGS 8 74ad7fcbc3SSagar Dharia #define QCOM_BUF_ALLOC_RETRIES 10 75ad7fcbc3SSagar Dharia 76ad7fcbc3SSagar Dharia #define CFG_PORT(r, v) ((v) ? CFG_PORT_V2(r) : CFG_PORT_V1(r)) 77ad7fcbc3SSagar Dharia 78ad7fcbc3SSagar Dharia /* V2 Component registers */ 79ad7fcbc3SSagar Dharia #define CFG_PORT_V2(r) ((r ## _V2)) 80ad7fcbc3SSagar Dharia #define COMP_CFG_V2 4 81ad7fcbc3SSagar Dharia #define COMP_TRUST_CFG_V2 0x3000 82ad7fcbc3SSagar Dharia 83ad7fcbc3SSagar Dharia /* V1 Component registers */ 84ad7fcbc3SSagar Dharia #define CFG_PORT_V1(r) ((r ## _V1)) 85ad7fcbc3SSagar Dharia #define COMP_CFG_V1 0 86ad7fcbc3SSagar Dharia #define COMP_TRUST_CFG_V1 0x14 87ad7fcbc3SSagar Dharia 88ad7fcbc3SSagar Dharia /* Resource group info for manager, and non-ported generic device-components */ 89ad7fcbc3SSagar Dharia #define EE_MGR_RSC_GRP (1 << 10) 90ad7fcbc3SSagar Dharia #define EE_NGD_2 (2 << 6) 91ad7fcbc3SSagar Dharia #define EE_NGD_1 0 92ad7fcbc3SSagar Dharia 93ad7fcbc3SSagar Dharia struct slim_ctrl_buf { 94ad7fcbc3SSagar Dharia void *base; 95ad7fcbc3SSagar Dharia spinlock_t lock; 96ad7fcbc3SSagar Dharia int head; 97ad7fcbc3SSagar Dharia int tail; 98ad7fcbc3SSagar Dharia int sl_sz; 99ad7fcbc3SSagar Dharia int n; 100ad7fcbc3SSagar Dharia }; 101ad7fcbc3SSagar Dharia 102ad7fcbc3SSagar Dharia struct qcom_slim_ctrl { 103ad7fcbc3SSagar Dharia struct slim_controller ctrl; 104ad7fcbc3SSagar Dharia struct slim_framer framer; 105ad7fcbc3SSagar Dharia struct device *dev; 106ad7fcbc3SSagar Dharia void __iomem *base; 107ad7fcbc3SSagar Dharia void __iomem *slew_reg; 108ad7fcbc3SSagar Dharia 109ad7fcbc3SSagar Dharia struct slim_ctrl_buf rx; 110ad7fcbc3SSagar Dharia struct slim_ctrl_buf tx; 111ad7fcbc3SSagar Dharia 112ad7fcbc3SSagar Dharia struct completion **wr_comp; 113ad7fcbc3SSagar Dharia int irq; 114ad7fcbc3SSagar Dharia struct workqueue_struct *rxwq; 115ad7fcbc3SSagar Dharia struct work_struct wd; 116ad7fcbc3SSagar Dharia struct clk *rclk; 117ad7fcbc3SSagar Dharia struct clk *hclk; 118ad7fcbc3SSagar Dharia }; 119ad7fcbc3SSagar Dharia 120ad7fcbc3SSagar Dharia static void qcom_slim_queue_tx(struct qcom_slim_ctrl *ctrl, void *buf, 121ad7fcbc3SSagar Dharia u8 len, u32 tx_reg) 122ad7fcbc3SSagar Dharia { 123ad7fcbc3SSagar Dharia int count = (len + 3) >> 2; 124ad7fcbc3SSagar Dharia 125ad7fcbc3SSagar Dharia __iowrite32_copy(ctrl->base + tx_reg, buf, count); 126ad7fcbc3SSagar Dharia 127ad7fcbc3SSagar Dharia /* Ensure Oder of subsequent writes */ 128ad7fcbc3SSagar Dharia mb(); 129ad7fcbc3SSagar Dharia } 130ad7fcbc3SSagar Dharia 131ad7fcbc3SSagar Dharia static void *slim_alloc_rxbuf(struct qcom_slim_ctrl *ctrl) 132ad7fcbc3SSagar Dharia { 133ad7fcbc3SSagar Dharia unsigned long flags; 134ad7fcbc3SSagar Dharia int idx; 135ad7fcbc3SSagar Dharia 136ad7fcbc3SSagar Dharia spin_lock_irqsave(&ctrl->rx.lock, flags); 137ad7fcbc3SSagar Dharia if ((ctrl->rx.tail + 1) % ctrl->rx.n == ctrl->rx.head) { 138ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->rx.lock, flags); 139ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "RX QUEUE full!"); 140ad7fcbc3SSagar Dharia return NULL; 141ad7fcbc3SSagar Dharia } 142ad7fcbc3SSagar Dharia idx = ctrl->rx.tail; 143ad7fcbc3SSagar Dharia ctrl->rx.tail = (ctrl->rx.tail + 1) % ctrl->rx.n; 144ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->rx.lock, flags); 145ad7fcbc3SSagar Dharia 146ad7fcbc3SSagar Dharia return ctrl->rx.base + (idx * ctrl->rx.sl_sz); 147ad7fcbc3SSagar Dharia } 148ad7fcbc3SSagar Dharia 149c88c8d7aSColin Ian King static void slim_ack_txn(struct qcom_slim_ctrl *ctrl, int err) 150ad7fcbc3SSagar Dharia { 151ad7fcbc3SSagar Dharia struct completion *comp; 152ad7fcbc3SSagar Dharia unsigned long flags; 153ad7fcbc3SSagar Dharia int idx; 154ad7fcbc3SSagar Dharia 155ad7fcbc3SSagar Dharia spin_lock_irqsave(&ctrl->tx.lock, flags); 156ad7fcbc3SSagar Dharia idx = ctrl->tx.head; 157ad7fcbc3SSagar Dharia ctrl->tx.head = (ctrl->tx.head + 1) % ctrl->tx.n; 158ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->tx.lock, flags); 159ad7fcbc3SSagar Dharia 160ad7fcbc3SSagar Dharia comp = ctrl->wr_comp[idx]; 161ad7fcbc3SSagar Dharia ctrl->wr_comp[idx] = NULL; 162ad7fcbc3SSagar Dharia 163ad7fcbc3SSagar Dharia complete(comp); 164ad7fcbc3SSagar Dharia } 165ad7fcbc3SSagar Dharia 166ad7fcbc3SSagar Dharia static irqreturn_t qcom_slim_handle_tx_irq(struct qcom_slim_ctrl *ctrl, 167ad7fcbc3SSagar Dharia u32 stat) 168ad7fcbc3SSagar Dharia { 169ad7fcbc3SSagar Dharia int err = 0; 170ad7fcbc3SSagar Dharia 171ad7fcbc3SSagar Dharia if (stat & MGR_INT_TX_MSG_SENT) 172ad7fcbc3SSagar Dharia writel_relaxed(MGR_INT_TX_MSG_SENT, 173ad7fcbc3SSagar Dharia ctrl->base + MGR_INT_CLR); 174ad7fcbc3SSagar Dharia 175ad7fcbc3SSagar Dharia if (stat & MGR_INT_TX_NACKED_2) { 176ad7fcbc3SSagar Dharia u32 mgr_stat = readl_relaxed(ctrl->base + MGR_STATUS); 177ad7fcbc3SSagar Dharia u32 mgr_ie_stat = readl_relaxed(ctrl->base + MGR_IE_STAT); 178ad7fcbc3SSagar Dharia u32 frm_stat = readl_relaxed(ctrl->base + FRM_STAT); 179ad7fcbc3SSagar Dharia u32 frm_cfg = readl_relaxed(ctrl->base + FRM_CFG); 180ad7fcbc3SSagar Dharia u32 frm_intr_stat = readl_relaxed(ctrl->base + FRM_INT_STAT); 181ad7fcbc3SSagar Dharia u32 frm_ie_stat = readl_relaxed(ctrl->base + FRM_IE_STAT); 182ad7fcbc3SSagar Dharia u32 intf_stat = readl_relaxed(ctrl->base + INTF_STAT); 183ad7fcbc3SSagar Dharia u32 intf_intr_stat = readl_relaxed(ctrl->base + INTF_INT_STAT); 184ad7fcbc3SSagar Dharia u32 intf_ie_stat = readl_relaxed(ctrl->base + INTF_IE_STAT); 185ad7fcbc3SSagar Dharia 186ad7fcbc3SSagar Dharia writel_relaxed(MGR_INT_TX_NACKED_2, ctrl->base + MGR_INT_CLR); 187ad7fcbc3SSagar Dharia 188ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "TX Nack MGR:int:0x%x, stat:0x%x\n", 189ad7fcbc3SSagar Dharia stat, mgr_stat); 190ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "TX Nack MGR:ie:0x%x\n", mgr_ie_stat); 191ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "TX Nack FRM:int:0x%x, stat:0x%x\n", 192ad7fcbc3SSagar Dharia frm_intr_stat, frm_stat); 193ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "TX Nack FRM:cfg:0x%x, ie:0x%x\n", 194ad7fcbc3SSagar Dharia frm_cfg, frm_ie_stat); 195ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "TX Nack INTF:intr:0x%x, stat:0x%x\n", 196ad7fcbc3SSagar Dharia intf_intr_stat, intf_stat); 197ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "TX Nack INTF:ie:0x%x\n", 198ad7fcbc3SSagar Dharia intf_ie_stat); 199ad7fcbc3SSagar Dharia err = -ENOTCONN; 200ad7fcbc3SSagar Dharia } 201ad7fcbc3SSagar Dharia 202ad7fcbc3SSagar Dharia slim_ack_txn(ctrl, err); 203ad7fcbc3SSagar Dharia 204ad7fcbc3SSagar Dharia return IRQ_HANDLED; 205ad7fcbc3SSagar Dharia } 206ad7fcbc3SSagar Dharia 207ad7fcbc3SSagar Dharia static irqreturn_t qcom_slim_handle_rx_irq(struct qcom_slim_ctrl *ctrl, 208ad7fcbc3SSagar Dharia u32 stat) 209ad7fcbc3SSagar Dharia { 210ad7fcbc3SSagar Dharia u32 *rx_buf, pkt[10]; 211ad7fcbc3SSagar Dharia bool q_rx = false; 212ad7fcbc3SSagar Dharia u8 mc, mt, len; 213ad7fcbc3SSagar Dharia 214ad7fcbc3SSagar Dharia pkt[0] = readl_relaxed(ctrl->base + MGR_RX_MSG); 215ad7fcbc3SSagar Dharia mt = SLIM_HEADER_GET_MT(pkt[0]); 216ad7fcbc3SSagar Dharia len = SLIM_HEADER_GET_RL(pkt[0]); 217ad7fcbc3SSagar Dharia mc = SLIM_HEADER_GET_MC(pkt[0]>>8); 218ad7fcbc3SSagar Dharia 219ad7fcbc3SSagar Dharia /* 220ad7fcbc3SSagar Dharia * this message cannot be handled by ISR, so 221ad7fcbc3SSagar Dharia * let work-queue handle it 222ad7fcbc3SSagar Dharia */ 223ad7fcbc3SSagar Dharia if (mt == SLIM_MSG_MT_CORE && mc == SLIM_MSG_MC_REPORT_PRESENT) { 224ad7fcbc3SSagar Dharia rx_buf = (u32 *)slim_alloc_rxbuf(ctrl); 225ad7fcbc3SSagar Dharia if (!rx_buf) { 226ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "dropping RX:0x%x due to RX full\n", 227ad7fcbc3SSagar Dharia pkt[0]); 228ad7fcbc3SSagar Dharia goto rx_ret_irq; 229ad7fcbc3SSagar Dharia } 230ad7fcbc3SSagar Dharia rx_buf[0] = pkt[0]; 231ad7fcbc3SSagar Dharia 232ad7fcbc3SSagar Dharia } else { 233ad7fcbc3SSagar Dharia rx_buf = pkt; 234ad7fcbc3SSagar Dharia } 235ad7fcbc3SSagar Dharia 236ad7fcbc3SSagar Dharia __ioread32_copy(rx_buf + 1, ctrl->base + MGR_RX_MSG + 4, 237ad7fcbc3SSagar Dharia DIV_ROUND_UP(len, 4)); 238ad7fcbc3SSagar Dharia 239ad7fcbc3SSagar Dharia switch (mc) { 240ad7fcbc3SSagar Dharia 241ad7fcbc3SSagar Dharia case SLIM_MSG_MC_REPORT_PRESENT: 242ad7fcbc3SSagar Dharia q_rx = true; 243ad7fcbc3SSagar Dharia break; 244ad7fcbc3SSagar Dharia case SLIM_MSG_MC_REPLY_INFORMATION: 245ad7fcbc3SSagar Dharia case SLIM_MSG_MC_REPLY_VALUE: 246ad7fcbc3SSagar Dharia slim_msg_response(&ctrl->ctrl, (u8 *)(rx_buf + 1), 247ad7fcbc3SSagar Dharia (u8)(*rx_buf >> 24), (len - 4)); 248ad7fcbc3SSagar Dharia break; 249ad7fcbc3SSagar Dharia default: 250ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "unsupported MC,%x MT:%x\n", 251ad7fcbc3SSagar Dharia mc, mt); 252ad7fcbc3SSagar Dharia break; 253ad7fcbc3SSagar Dharia } 254ad7fcbc3SSagar Dharia rx_ret_irq: 255ad7fcbc3SSagar Dharia writel(MGR_INT_RX_MSG_RCVD, ctrl->base + 256ad7fcbc3SSagar Dharia MGR_INT_CLR); 257ad7fcbc3SSagar Dharia if (q_rx) 258ad7fcbc3SSagar Dharia queue_work(ctrl->rxwq, &ctrl->wd); 259ad7fcbc3SSagar Dharia 260ad7fcbc3SSagar Dharia return IRQ_HANDLED; 261ad7fcbc3SSagar Dharia } 262ad7fcbc3SSagar Dharia 263ad7fcbc3SSagar Dharia static irqreturn_t qcom_slim_interrupt(int irq, void *d) 264ad7fcbc3SSagar Dharia { 265ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl = d; 266ad7fcbc3SSagar Dharia u32 stat = readl_relaxed(ctrl->base + MGR_INT_STAT); 267ad7fcbc3SSagar Dharia int ret = IRQ_NONE; 268ad7fcbc3SSagar Dharia 269ad7fcbc3SSagar Dharia if (stat & MGR_INT_TX_MSG_SENT || stat & MGR_INT_TX_NACKED_2) 270ad7fcbc3SSagar Dharia ret = qcom_slim_handle_tx_irq(ctrl, stat); 271ad7fcbc3SSagar Dharia 272ad7fcbc3SSagar Dharia if (stat & MGR_INT_RX_MSG_RCVD) 273ad7fcbc3SSagar Dharia ret = qcom_slim_handle_rx_irq(ctrl, stat); 274ad7fcbc3SSagar Dharia 275ad7fcbc3SSagar Dharia return ret; 276ad7fcbc3SSagar Dharia } 277ad7fcbc3SSagar Dharia 278c088c335SSagar Dharia static int qcom_clk_pause_wakeup(struct slim_controller *sctrl) 279c088c335SSagar Dharia { 280c088c335SSagar Dharia struct qcom_slim_ctrl *ctrl = dev_get_drvdata(sctrl->dev); 281c088c335SSagar Dharia 282c088c335SSagar Dharia clk_prepare_enable(ctrl->hclk); 283c088c335SSagar Dharia clk_prepare_enable(ctrl->rclk); 284c088c335SSagar Dharia enable_irq(ctrl->irq); 285c088c335SSagar Dharia 286c088c335SSagar Dharia writel_relaxed(1, ctrl->base + FRM_WAKEUP); 287c088c335SSagar Dharia /* Make sure framer wakeup write goes through before ISR fires */ 288c088c335SSagar Dharia mb(); 289c088c335SSagar Dharia /* 290c088c335SSagar Dharia * HW Workaround: Currently, slave is reporting lost-sync messages 291c088c335SSagar Dharia * after SLIMbus comes out of clock pause. 292c088c335SSagar Dharia * Transaction with slave fail before slave reports that message 293c088c335SSagar Dharia * Give some time for that report to come 294c088c335SSagar Dharia * SLIMbus wakes up in clock gear 10 at 24.576MHz. With each superframe 295c088c335SSagar Dharia * being 250 usecs, we wait for 5-10 superframes here to ensure 296c088c335SSagar Dharia * we get the message 297c088c335SSagar Dharia */ 298c088c335SSagar Dharia usleep_range(1250, 2500); 299c088c335SSagar Dharia return 0; 300c088c335SSagar Dharia } 301c088c335SSagar Dharia 302c88c8d7aSColin Ian King static void *slim_alloc_txbuf(struct qcom_slim_ctrl *ctrl, 303c88c8d7aSColin Ian King struct slim_msg_txn *txn, 304ad7fcbc3SSagar Dharia struct completion *done) 305ad7fcbc3SSagar Dharia { 306ad7fcbc3SSagar Dharia unsigned long flags; 307ad7fcbc3SSagar Dharia int idx; 308ad7fcbc3SSagar Dharia 309ad7fcbc3SSagar Dharia spin_lock_irqsave(&ctrl->tx.lock, flags); 310ad7fcbc3SSagar Dharia if (((ctrl->tx.head + 1) % ctrl->tx.n) == ctrl->tx.tail) { 311ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->tx.lock, flags); 312ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "controller TX buf unavailable"); 313ad7fcbc3SSagar Dharia return NULL; 314ad7fcbc3SSagar Dharia } 315ad7fcbc3SSagar Dharia idx = ctrl->tx.tail; 316ad7fcbc3SSagar Dharia ctrl->wr_comp[idx] = done; 317ad7fcbc3SSagar Dharia ctrl->tx.tail = (ctrl->tx.tail + 1) % ctrl->tx.n; 318ad7fcbc3SSagar Dharia 319ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->tx.lock, flags); 320ad7fcbc3SSagar Dharia 321ad7fcbc3SSagar Dharia return ctrl->tx.base + (idx * ctrl->tx.sl_sz); 322ad7fcbc3SSagar Dharia } 323ad7fcbc3SSagar Dharia 324ad7fcbc3SSagar Dharia 325ad7fcbc3SSagar Dharia static int qcom_xfer_msg(struct slim_controller *sctrl, 326ad7fcbc3SSagar Dharia struct slim_msg_txn *txn) 327ad7fcbc3SSagar Dharia { 328ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl = dev_get_drvdata(sctrl->dev); 329ad7fcbc3SSagar Dharia DECLARE_COMPLETION_ONSTACK(done); 330ad7fcbc3SSagar Dharia void *pbuf = slim_alloc_txbuf(ctrl, txn, &done); 331ad7fcbc3SSagar Dharia unsigned long ms = txn->rl + HZ; 332ad7fcbc3SSagar Dharia u8 *puc; 333ad7fcbc3SSagar Dharia int ret = 0, timeout, retries = QCOM_BUF_ALLOC_RETRIES; 334ad7fcbc3SSagar Dharia u8 la = txn->la; 335ad7fcbc3SSagar Dharia u32 *head; 336ad7fcbc3SSagar Dharia /* HW expects length field to be excluded */ 337ad7fcbc3SSagar Dharia txn->rl--; 338ad7fcbc3SSagar Dharia 339ad7fcbc3SSagar Dharia /* spin till buffer is made available */ 340ad7fcbc3SSagar Dharia if (!pbuf) { 341ad7fcbc3SSagar Dharia while (retries--) { 342ad7fcbc3SSagar Dharia usleep_range(10000, 15000); 343ad7fcbc3SSagar Dharia pbuf = slim_alloc_txbuf(ctrl, txn, &done); 344ad7fcbc3SSagar Dharia if (pbuf) 345ad7fcbc3SSagar Dharia break; 346ad7fcbc3SSagar Dharia } 347ad7fcbc3SSagar Dharia } 348ad7fcbc3SSagar Dharia 349551b9ee4SColin Ian King if (retries < 0 && !pbuf) 350ad7fcbc3SSagar Dharia return -ENOMEM; 351ad7fcbc3SSagar Dharia 352ad7fcbc3SSagar Dharia puc = (u8 *)pbuf; 353ad7fcbc3SSagar Dharia head = (u32 *)pbuf; 354ad7fcbc3SSagar Dharia 355ad7fcbc3SSagar Dharia if (txn->dt == SLIM_MSG_DEST_LOGICALADDR) { 356ad7fcbc3SSagar Dharia *head = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, 357ad7fcbc3SSagar Dharia txn->mc, 0, la); 358ad7fcbc3SSagar Dharia puc += 3; 359ad7fcbc3SSagar Dharia } else { 360ad7fcbc3SSagar Dharia *head = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, 361ad7fcbc3SSagar Dharia txn->mc, 1, la); 362ad7fcbc3SSagar Dharia puc += 2; 363ad7fcbc3SSagar Dharia } 364ad7fcbc3SSagar Dharia 365ad7fcbc3SSagar Dharia if (slim_tid_txn(txn->mt, txn->mc)) 366ad7fcbc3SSagar Dharia *(puc++) = txn->tid; 367ad7fcbc3SSagar Dharia 368ad7fcbc3SSagar Dharia if (slim_ec_txn(txn->mt, txn->mc)) { 369ad7fcbc3SSagar Dharia *(puc++) = (txn->ec & 0xFF); 370ad7fcbc3SSagar Dharia *(puc++) = (txn->ec >> 8) & 0xFF; 371ad7fcbc3SSagar Dharia } 372ad7fcbc3SSagar Dharia 373ad7fcbc3SSagar Dharia if (txn->msg && txn->msg->wbuf) 374ad7fcbc3SSagar Dharia memcpy(puc, txn->msg->wbuf, txn->msg->num_bytes); 375ad7fcbc3SSagar Dharia 376ad7fcbc3SSagar Dharia qcom_slim_queue_tx(ctrl, head, txn->rl, MGR_TX_MSG); 377ad7fcbc3SSagar Dharia timeout = wait_for_completion_timeout(&done, msecs_to_jiffies(ms)); 378ad7fcbc3SSagar Dharia 379ad7fcbc3SSagar Dharia if (!timeout) { 380ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc, 381ad7fcbc3SSagar Dharia txn->mt); 382ad7fcbc3SSagar Dharia ret = -ETIMEDOUT; 383ad7fcbc3SSagar Dharia } 384ad7fcbc3SSagar Dharia 385ad7fcbc3SSagar Dharia return ret; 386ad7fcbc3SSagar Dharia 387ad7fcbc3SSagar Dharia } 388ad7fcbc3SSagar Dharia 389ad7fcbc3SSagar Dharia static int qcom_set_laddr(struct slim_controller *sctrl, 390ad7fcbc3SSagar Dharia struct slim_eaddr *ead, u8 laddr) 391ad7fcbc3SSagar Dharia { 392ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl = dev_get_drvdata(sctrl->dev); 393ad7fcbc3SSagar Dharia struct { 394ad7fcbc3SSagar Dharia __be16 manf_id; 395ad7fcbc3SSagar Dharia __be16 prod_code; 396ad7fcbc3SSagar Dharia u8 dev_index; 397ad7fcbc3SSagar Dharia u8 instance; 398ad7fcbc3SSagar Dharia u8 laddr; 399ad7fcbc3SSagar Dharia } __packed p; 400ad7fcbc3SSagar Dharia struct slim_val_inf msg = {0}; 401ad7fcbc3SSagar Dharia DEFINE_SLIM_EDEST_TXN(txn, SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS, 402ad7fcbc3SSagar Dharia 10, laddr, &msg); 403ad7fcbc3SSagar Dharia int ret; 404ad7fcbc3SSagar Dharia 405ad7fcbc3SSagar Dharia p.manf_id = cpu_to_be16(ead->manf_id); 406ad7fcbc3SSagar Dharia p.prod_code = cpu_to_be16(ead->prod_code); 407ad7fcbc3SSagar Dharia p.dev_index = ead->dev_index; 408ad7fcbc3SSagar Dharia p.instance = ead->instance; 409ad7fcbc3SSagar Dharia p.laddr = laddr; 410ad7fcbc3SSagar Dharia 411ad7fcbc3SSagar Dharia msg.wbuf = (void *)&p; 412ad7fcbc3SSagar Dharia msg.num_bytes = 7; 413ad7fcbc3SSagar Dharia ret = slim_do_transfer(&ctrl->ctrl, &txn); 414ad7fcbc3SSagar Dharia 415ad7fcbc3SSagar Dharia if (ret) 416ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "set LA:0x%x failed:ret:%d\n", 417ad7fcbc3SSagar Dharia laddr, ret); 418ad7fcbc3SSagar Dharia return ret; 419ad7fcbc3SSagar Dharia } 420ad7fcbc3SSagar Dharia 421ad7fcbc3SSagar Dharia static int slim_get_current_rxbuf(struct qcom_slim_ctrl *ctrl, void *buf) 422ad7fcbc3SSagar Dharia { 423ad7fcbc3SSagar Dharia unsigned long flags; 424ad7fcbc3SSagar Dharia 425ad7fcbc3SSagar Dharia spin_lock_irqsave(&ctrl->rx.lock, flags); 426ad7fcbc3SSagar Dharia if (ctrl->rx.tail == ctrl->rx.head) { 427ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->rx.lock, flags); 428ad7fcbc3SSagar Dharia return -ENODATA; 429ad7fcbc3SSagar Dharia } 430ad7fcbc3SSagar Dharia memcpy(buf, ctrl->rx.base + (ctrl->rx.head * ctrl->rx.sl_sz), 431ad7fcbc3SSagar Dharia ctrl->rx.sl_sz); 432ad7fcbc3SSagar Dharia 433ad7fcbc3SSagar Dharia ctrl->rx.head = (ctrl->rx.head + 1) % ctrl->rx.n; 434ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->rx.lock, flags); 435ad7fcbc3SSagar Dharia 436ad7fcbc3SSagar Dharia return 0; 437ad7fcbc3SSagar Dharia } 438ad7fcbc3SSagar Dharia 439ad7fcbc3SSagar Dharia static void qcom_slim_rxwq(struct work_struct *work) 440ad7fcbc3SSagar Dharia { 441ad7fcbc3SSagar Dharia u8 buf[SLIM_MSGQ_BUF_LEN]; 44249cb93cbSSrinivas Kandagatla u8 mc, mt; 443ad7fcbc3SSagar Dharia int ret; 444ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl = container_of(work, struct qcom_slim_ctrl, 445ad7fcbc3SSagar Dharia wd); 446ad7fcbc3SSagar Dharia 447ad7fcbc3SSagar Dharia while ((slim_get_current_rxbuf(ctrl, buf)) != -ENODATA) { 448ad7fcbc3SSagar Dharia mt = SLIM_HEADER_GET_MT(buf[0]); 449ad7fcbc3SSagar Dharia mc = SLIM_HEADER_GET_MC(buf[1]); 450ad7fcbc3SSagar Dharia if (mt == SLIM_MSG_MT_CORE && 451ad7fcbc3SSagar Dharia mc == SLIM_MSG_MC_REPORT_PRESENT) { 452ad7fcbc3SSagar Dharia struct slim_eaddr ea; 453ad7fcbc3SSagar Dharia u8 laddr; 454ad7fcbc3SSagar Dharia 455ad7fcbc3SSagar Dharia ea.manf_id = be16_to_cpup((__be16 *)&buf[2]); 456ad7fcbc3SSagar Dharia ea.prod_code = be16_to_cpup((__be16 *)&buf[4]); 457ad7fcbc3SSagar Dharia ea.dev_index = buf[6]; 458ad7fcbc3SSagar Dharia ea.instance = buf[7]; 459ad7fcbc3SSagar Dharia 460ad7fcbc3SSagar Dharia ret = slim_device_report_present(&ctrl->ctrl, &ea, 461ad7fcbc3SSagar Dharia &laddr); 462ad7fcbc3SSagar Dharia if (ret < 0) 463ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "assign laddr failed:%d\n", 464ad7fcbc3SSagar Dharia ret); 465ad7fcbc3SSagar Dharia } else { 466ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "unexpected message:mc:%x, mt:%x\n", 467ad7fcbc3SSagar Dharia mc, mt); 468ad7fcbc3SSagar Dharia } 469ad7fcbc3SSagar Dharia } 470ad7fcbc3SSagar Dharia } 471ad7fcbc3SSagar Dharia 472ad7fcbc3SSagar Dharia static void qcom_slim_prg_slew(struct platform_device *pdev, 473ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl) 474ad7fcbc3SSagar Dharia { 475ad7fcbc3SSagar Dharia if (!ctrl->slew_reg) { 476ad7fcbc3SSagar Dharia /* SLEW RATE register for this SLIMbus */ 477428bb001SZhang Changzhong ctrl->slew_reg = devm_platform_ioremap_resource_byname(pdev, "slew"); 478428bb001SZhang Changzhong if (IS_ERR(ctrl->slew_reg)) 479ad7fcbc3SSagar Dharia return; 480ad7fcbc3SSagar Dharia } 481ad7fcbc3SSagar Dharia 482ad7fcbc3SSagar Dharia writel_relaxed(1, ctrl->slew_reg); 483ad7fcbc3SSagar Dharia /* Make sure SLIMbus-slew rate enabling goes through */ 484ad7fcbc3SSagar Dharia wmb(); 485ad7fcbc3SSagar Dharia } 486ad7fcbc3SSagar Dharia 487ad7fcbc3SSagar Dharia static int qcom_slim_probe(struct platform_device *pdev) 488ad7fcbc3SSagar Dharia { 489ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl; 490ad7fcbc3SSagar Dharia struct slim_controller *sctrl; 491ad7fcbc3SSagar Dharia struct resource *slim_mem; 492ad7fcbc3SSagar Dharia int ret, ver; 493ad7fcbc3SSagar Dharia 494ad7fcbc3SSagar Dharia ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); 495ad7fcbc3SSagar Dharia if (!ctrl) 496ad7fcbc3SSagar Dharia return -ENOMEM; 497ad7fcbc3SSagar Dharia 498ad7fcbc3SSagar Dharia ctrl->hclk = devm_clk_get(&pdev->dev, "iface"); 499ad7fcbc3SSagar Dharia if (IS_ERR(ctrl->hclk)) 500ad7fcbc3SSagar Dharia return PTR_ERR(ctrl->hclk); 501ad7fcbc3SSagar Dharia 502ad7fcbc3SSagar Dharia ctrl->rclk = devm_clk_get(&pdev->dev, "core"); 503ad7fcbc3SSagar Dharia if (IS_ERR(ctrl->rclk)) 504ad7fcbc3SSagar Dharia return PTR_ERR(ctrl->rclk); 505ad7fcbc3SSagar Dharia 506ad7fcbc3SSagar Dharia ret = clk_set_rate(ctrl->rclk, SLIM_ROOT_FREQ); 507ad7fcbc3SSagar Dharia if (ret) { 508ad7fcbc3SSagar Dharia dev_err(&pdev->dev, "ref-clock set-rate failed:%d\n", ret); 509ad7fcbc3SSagar Dharia return ret; 510ad7fcbc3SSagar Dharia } 511ad7fcbc3SSagar Dharia 512ad7fcbc3SSagar Dharia ctrl->irq = platform_get_irq(pdev, 0); 513*225baab0SMiaoqian Lin if (ctrl->irq < 0) { 514ad7fcbc3SSagar Dharia dev_err(&pdev->dev, "no slimbus IRQ\n"); 515*225baab0SMiaoqian Lin return ctrl->irq; 516ad7fcbc3SSagar Dharia } 517ad7fcbc3SSagar Dharia 518ad7fcbc3SSagar Dharia sctrl = &ctrl->ctrl; 519ad7fcbc3SSagar Dharia sctrl->dev = &pdev->dev; 520ad7fcbc3SSagar Dharia ctrl->dev = &pdev->dev; 521ad7fcbc3SSagar Dharia platform_set_drvdata(pdev, ctrl); 522ad7fcbc3SSagar Dharia dev_set_drvdata(ctrl->dev, ctrl); 523ad7fcbc3SSagar Dharia 524ad7fcbc3SSagar Dharia slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); 525ad7fcbc3SSagar Dharia ctrl->base = devm_ioremap_resource(ctrl->dev, slim_mem); 526e9cd2519SDing Xiang if (IS_ERR(ctrl->base)) 527ff595a33SWei Yongjun return PTR_ERR(ctrl->base); 528ad7fcbc3SSagar Dharia 529ad7fcbc3SSagar Dharia sctrl->set_laddr = qcom_set_laddr; 530ad7fcbc3SSagar Dharia sctrl->xfer_msg = qcom_xfer_msg; 531c088c335SSagar Dharia sctrl->wakeup = qcom_clk_pause_wakeup; 532ad7fcbc3SSagar Dharia ctrl->tx.n = QCOM_TX_MSGS; 533ad7fcbc3SSagar Dharia ctrl->tx.sl_sz = SLIM_MSGQ_BUF_LEN; 534ad7fcbc3SSagar Dharia ctrl->rx.n = QCOM_RX_MSGS; 535ad7fcbc3SSagar Dharia ctrl->rx.sl_sz = SLIM_MSGQ_BUF_LEN; 5366396bb22SKees Cook ctrl->wr_comp = kcalloc(QCOM_TX_MSGS, sizeof(struct completion *), 537ad7fcbc3SSagar Dharia GFP_KERNEL); 538ad7fcbc3SSagar Dharia if (!ctrl->wr_comp) 539ad7fcbc3SSagar Dharia return -ENOMEM; 540ad7fcbc3SSagar Dharia 541ad7fcbc3SSagar Dharia spin_lock_init(&ctrl->rx.lock); 542ad7fcbc3SSagar Dharia spin_lock_init(&ctrl->tx.lock); 543ad7fcbc3SSagar Dharia INIT_WORK(&ctrl->wd, qcom_slim_rxwq); 544ad7fcbc3SSagar Dharia ctrl->rxwq = create_singlethread_workqueue("qcom_slim_rx"); 545ad7fcbc3SSagar Dharia if (!ctrl->rxwq) { 546ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "Failed to start Rx WQ\n"); 547ad7fcbc3SSagar Dharia return -ENOMEM; 548ad7fcbc3SSagar Dharia } 549ad7fcbc3SSagar Dharia 550ad7fcbc3SSagar Dharia ctrl->framer.rootfreq = SLIM_ROOT_FREQ / 8; 551ad7fcbc3SSagar Dharia ctrl->framer.superfreq = 552ad7fcbc3SSagar Dharia ctrl->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8; 553ad7fcbc3SSagar Dharia sctrl->a_framer = &ctrl->framer; 554ad7fcbc3SSagar Dharia sctrl->clkgear = SLIM_MAX_CLK_GEAR; 555ad7fcbc3SSagar Dharia 556ad7fcbc3SSagar Dharia qcom_slim_prg_slew(pdev, ctrl); 557ad7fcbc3SSagar Dharia 558ad7fcbc3SSagar Dharia ret = devm_request_irq(&pdev->dev, ctrl->irq, qcom_slim_interrupt, 559ad7fcbc3SSagar Dharia IRQF_TRIGGER_HIGH, "qcom_slim_irq", ctrl); 560ad7fcbc3SSagar Dharia if (ret) { 561ad7fcbc3SSagar Dharia dev_err(&pdev->dev, "request IRQ failed\n"); 562ad7fcbc3SSagar Dharia goto err_request_irq_failed; 563ad7fcbc3SSagar Dharia } 564ad7fcbc3SSagar Dharia 565ad7fcbc3SSagar Dharia ret = clk_prepare_enable(ctrl->hclk); 566ad7fcbc3SSagar Dharia if (ret) 567ad7fcbc3SSagar Dharia goto err_hclk_enable_failed; 568ad7fcbc3SSagar Dharia 569ad7fcbc3SSagar Dharia ret = clk_prepare_enable(ctrl->rclk); 570ad7fcbc3SSagar Dharia if (ret) 571ad7fcbc3SSagar Dharia goto err_rclk_enable_failed; 572ad7fcbc3SSagar Dharia 573c127f98bSArnd Bergmann ctrl->tx.base = devm_kcalloc(&pdev->dev, ctrl->tx.n, ctrl->tx.sl_sz, 574c127f98bSArnd Bergmann GFP_KERNEL); 575ad7fcbc3SSagar Dharia if (!ctrl->tx.base) { 576ad7fcbc3SSagar Dharia ret = -ENOMEM; 577ad7fcbc3SSagar Dharia goto err; 578ad7fcbc3SSagar Dharia } 579ad7fcbc3SSagar Dharia 580c127f98bSArnd Bergmann ctrl->rx.base = devm_kcalloc(&pdev->dev,ctrl->rx.n, ctrl->rx.sl_sz, 581c127f98bSArnd Bergmann GFP_KERNEL); 582ad7fcbc3SSagar Dharia if (!ctrl->rx.base) { 583ad7fcbc3SSagar Dharia ret = -ENOMEM; 584ad7fcbc3SSagar Dharia goto err; 585ad7fcbc3SSagar Dharia } 586ad7fcbc3SSagar Dharia 587ad7fcbc3SSagar Dharia /* Register with framework before enabling frame, clock */ 588ad7fcbc3SSagar Dharia ret = slim_register_controller(&ctrl->ctrl); 589ad7fcbc3SSagar Dharia if (ret) { 590ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "error adding controller\n"); 591ad7fcbc3SSagar Dharia goto err; 592ad7fcbc3SSagar Dharia } 593ad7fcbc3SSagar Dharia 594ad7fcbc3SSagar Dharia ver = readl_relaxed(ctrl->base); 595ad7fcbc3SSagar Dharia /* Version info in 16 MSbits */ 596ad7fcbc3SSagar Dharia ver >>= 16; 597ad7fcbc3SSagar Dharia /* Component register initialization */ 598ad7fcbc3SSagar Dharia writel(1, ctrl->base + CFG_PORT(COMP_CFG, ver)); 599ad7fcbc3SSagar Dharia writel((EE_MGR_RSC_GRP | EE_NGD_2 | EE_NGD_1), 600ad7fcbc3SSagar Dharia ctrl->base + CFG_PORT(COMP_TRUST_CFG, ver)); 601ad7fcbc3SSagar Dharia 602ad7fcbc3SSagar Dharia writel((MGR_INT_TX_NACKED_2 | 603ad7fcbc3SSagar Dharia MGR_INT_MSG_BUF_CONTE | MGR_INT_RX_MSG_RCVD | 604ad7fcbc3SSagar Dharia MGR_INT_TX_MSG_SENT), ctrl->base + MGR_INT_EN); 605ad7fcbc3SSagar Dharia writel(1, ctrl->base + MGR_CFG); 606ad7fcbc3SSagar Dharia /* Framer register initialization */ 607ad7fcbc3SSagar Dharia writel((1 << INTR_WAKE) | (0xA << REF_CLK_GEAR) | 608ad7fcbc3SSagar Dharia (0xA << CLK_GEAR) | (1 << ROOT_FREQ) | (1 << FRM_ACTIVE) | 1, 609ad7fcbc3SSagar Dharia ctrl->base + FRM_CFG); 610ad7fcbc3SSagar Dharia writel(MGR_CFG_ENABLE, ctrl->base + MGR_CFG); 611ad7fcbc3SSagar Dharia writel(1, ctrl->base + INTF_CFG); 612ad7fcbc3SSagar Dharia writel(1, ctrl->base + CFG_PORT(COMP_CFG, ver)); 613ad7fcbc3SSagar Dharia 614ad7fcbc3SSagar Dharia pm_runtime_use_autosuspend(&pdev->dev); 615ad7fcbc3SSagar Dharia pm_runtime_set_autosuspend_delay(&pdev->dev, QCOM_SLIM_AUTOSUSPEND); 616ad7fcbc3SSagar Dharia pm_runtime_set_active(&pdev->dev); 617ad7fcbc3SSagar Dharia pm_runtime_mark_last_busy(&pdev->dev); 618ad7fcbc3SSagar Dharia pm_runtime_enable(&pdev->dev); 619ad7fcbc3SSagar Dharia 620ad7fcbc3SSagar Dharia dev_dbg(ctrl->dev, "QCOM SB controller is up:ver:0x%x!\n", ver); 621ad7fcbc3SSagar Dharia return 0; 622ad7fcbc3SSagar Dharia 623ad7fcbc3SSagar Dharia err: 624ad7fcbc3SSagar Dharia clk_disable_unprepare(ctrl->rclk); 625ad7fcbc3SSagar Dharia err_rclk_enable_failed: 626ad7fcbc3SSagar Dharia clk_disable_unprepare(ctrl->hclk); 627ad7fcbc3SSagar Dharia err_hclk_enable_failed: 628ad7fcbc3SSagar Dharia err_request_irq_failed: 629ad7fcbc3SSagar Dharia destroy_workqueue(ctrl->rxwq); 630ad7fcbc3SSagar Dharia return ret; 631ad7fcbc3SSagar Dharia } 632ad7fcbc3SSagar Dharia 633ad7fcbc3SSagar Dharia static int qcom_slim_remove(struct platform_device *pdev) 634ad7fcbc3SSagar Dharia { 635ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl = platform_get_drvdata(pdev); 636ad7fcbc3SSagar Dharia 637c088c335SSagar Dharia pm_runtime_disable(&pdev->dev); 638ad7fcbc3SSagar Dharia slim_unregister_controller(&ctrl->ctrl); 63989d93c6dSChuhong Yuan clk_disable_unprepare(ctrl->rclk); 64089d93c6dSChuhong Yuan clk_disable_unprepare(ctrl->hclk); 641ad7fcbc3SSagar Dharia destroy_workqueue(ctrl->rxwq); 642ad7fcbc3SSagar Dharia return 0; 643ad7fcbc3SSagar Dharia } 644ad7fcbc3SSagar Dharia 645c088c335SSagar Dharia /* 646c088c335SSagar Dharia * If PM_RUNTIME is not defined, these 2 functions become helper 647c088c335SSagar Dharia * functions to be called from system suspend/resume. 648c088c335SSagar Dharia */ 649c088c335SSagar Dharia #ifdef CONFIG_PM 650c088c335SSagar Dharia static int qcom_slim_runtime_suspend(struct device *device) 651c088c335SSagar Dharia { 652d003c346SWolfram Sang struct qcom_slim_ctrl *ctrl = dev_get_drvdata(device); 653c088c335SSagar Dharia int ret; 654c088c335SSagar Dharia 655c088c335SSagar Dharia dev_dbg(device, "pm_runtime: suspending...\n"); 656c088c335SSagar Dharia ret = slim_ctrl_clk_pause(&ctrl->ctrl, false, SLIM_CLK_UNSPECIFIED); 657c088c335SSagar Dharia if (ret) { 658c088c335SSagar Dharia dev_err(device, "clk pause not entered:%d", ret); 659c088c335SSagar Dharia } else { 660c088c335SSagar Dharia disable_irq(ctrl->irq); 661c088c335SSagar Dharia clk_disable_unprepare(ctrl->hclk); 662c088c335SSagar Dharia clk_disable_unprepare(ctrl->rclk); 663c088c335SSagar Dharia } 664c088c335SSagar Dharia return ret; 665c088c335SSagar Dharia } 666c088c335SSagar Dharia 667c088c335SSagar Dharia static int qcom_slim_runtime_resume(struct device *device) 668c088c335SSagar Dharia { 669d003c346SWolfram Sang struct qcom_slim_ctrl *ctrl = dev_get_drvdata(device); 670c088c335SSagar Dharia int ret = 0; 671c088c335SSagar Dharia 672c088c335SSagar Dharia dev_dbg(device, "pm_runtime: resuming...\n"); 673c088c335SSagar Dharia ret = slim_ctrl_clk_pause(&ctrl->ctrl, true, 0); 674c088c335SSagar Dharia if (ret) 675c088c335SSagar Dharia dev_err(device, "clk pause not exited:%d", ret); 676c088c335SSagar Dharia return ret; 677c088c335SSagar Dharia } 678c088c335SSagar Dharia #endif 679c088c335SSagar Dharia 680c088c335SSagar Dharia #ifdef CONFIG_PM_SLEEP 681c088c335SSagar Dharia static int qcom_slim_suspend(struct device *dev) 682c088c335SSagar Dharia { 683c088c335SSagar Dharia int ret = 0; 684c088c335SSagar Dharia 685c088c335SSagar Dharia if (!pm_runtime_enabled(dev) || 686c088c335SSagar Dharia (!pm_runtime_suspended(dev))) { 687c088c335SSagar Dharia dev_dbg(dev, "system suspend"); 688c088c335SSagar Dharia ret = qcom_slim_runtime_suspend(dev); 689c088c335SSagar Dharia } 690c088c335SSagar Dharia 691c088c335SSagar Dharia return ret; 692c088c335SSagar Dharia } 693c088c335SSagar Dharia 694c088c335SSagar Dharia static int qcom_slim_resume(struct device *dev) 695c088c335SSagar Dharia { 696c088c335SSagar Dharia if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) { 697c088c335SSagar Dharia int ret; 698c088c335SSagar Dharia 699c088c335SSagar Dharia dev_dbg(dev, "system resume"); 700c088c335SSagar Dharia ret = qcom_slim_runtime_resume(dev); 701c088c335SSagar Dharia if (!ret) { 702c088c335SSagar Dharia pm_runtime_mark_last_busy(dev); 703c088c335SSagar Dharia pm_request_autosuspend(dev); 704c088c335SSagar Dharia } 705c088c335SSagar Dharia return ret; 706c088c335SSagar Dharia 707c088c335SSagar Dharia } 708c088c335SSagar Dharia return 0; 709c088c335SSagar Dharia } 710c088c335SSagar Dharia #endif /* CONFIG_PM_SLEEP */ 711c088c335SSagar Dharia 712ad7fcbc3SSagar Dharia static const struct dev_pm_ops qcom_slim_dev_pm_ops = { 713ad7fcbc3SSagar Dharia SET_SYSTEM_SLEEP_PM_OPS(qcom_slim_suspend, qcom_slim_resume) 714ad7fcbc3SSagar Dharia SET_RUNTIME_PM_OPS( 715ad7fcbc3SSagar Dharia qcom_slim_runtime_suspend, 716ad7fcbc3SSagar Dharia qcom_slim_runtime_resume, 717ad7fcbc3SSagar Dharia NULL 718ad7fcbc3SSagar Dharia ) 719ad7fcbc3SSagar Dharia }; 720ad7fcbc3SSagar Dharia 721ad7fcbc3SSagar Dharia static const struct of_device_id qcom_slim_dt_match[] = { 722ad7fcbc3SSagar Dharia { .compatible = "qcom,slim", }, 723ad7fcbc3SSagar Dharia { .compatible = "qcom,apq8064-slim", }, 724ad7fcbc3SSagar Dharia {} 725ad7fcbc3SSagar Dharia }; 726ad7fcbc3SSagar Dharia 727ad7fcbc3SSagar Dharia static struct platform_driver qcom_slim_driver = { 728ad7fcbc3SSagar Dharia .probe = qcom_slim_probe, 729ad7fcbc3SSagar Dharia .remove = qcom_slim_remove, 730ad7fcbc3SSagar Dharia .driver = { 731ad7fcbc3SSagar Dharia .name = "qcom_slim_ctrl", 732ad7fcbc3SSagar Dharia .of_match_table = qcom_slim_dt_match, 733c088c335SSagar Dharia .pm = &qcom_slim_dev_pm_ops, 734ad7fcbc3SSagar Dharia }, 735ad7fcbc3SSagar Dharia }; 736ad7fcbc3SSagar Dharia module_platform_driver(qcom_slim_driver); 737ad7fcbc3SSagar Dharia 738ad7fcbc3SSagar Dharia MODULE_LICENSE("GPL v2"); 739ad7fcbc3SSagar Dharia MODULE_DESCRIPTION("Qualcomm SLIMbus Controller"); 740