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