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 149ad7fcbc3SSagar Dharia 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 302ad7fcbc3SSagar Dharia void *slim_alloc_txbuf(struct qcom_slim_ctrl *ctrl, struct slim_msg_txn *txn, 303ad7fcbc3SSagar Dharia struct completion *done) 304ad7fcbc3SSagar Dharia { 305ad7fcbc3SSagar Dharia unsigned long flags; 306ad7fcbc3SSagar Dharia int idx; 307ad7fcbc3SSagar Dharia 308ad7fcbc3SSagar Dharia spin_lock_irqsave(&ctrl->tx.lock, flags); 309ad7fcbc3SSagar Dharia if (((ctrl->tx.head + 1) % ctrl->tx.n) == ctrl->tx.tail) { 310ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->tx.lock, flags); 311ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "controller TX buf unavailable"); 312ad7fcbc3SSagar Dharia return NULL; 313ad7fcbc3SSagar Dharia } 314ad7fcbc3SSagar Dharia idx = ctrl->tx.tail; 315ad7fcbc3SSagar Dharia ctrl->wr_comp[idx] = done; 316ad7fcbc3SSagar Dharia ctrl->tx.tail = (ctrl->tx.tail + 1) % ctrl->tx.n; 317ad7fcbc3SSagar Dharia 318ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->tx.lock, flags); 319ad7fcbc3SSagar Dharia 320ad7fcbc3SSagar Dharia return ctrl->tx.base + (idx * ctrl->tx.sl_sz); 321ad7fcbc3SSagar Dharia } 322ad7fcbc3SSagar Dharia 323ad7fcbc3SSagar Dharia 324ad7fcbc3SSagar Dharia static int qcom_xfer_msg(struct slim_controller *sctrl, 325ad7fcbc3SSagar Dharia struct slim_msg_txn *txn) 326ad7fcbc3SSagar Dharia { 327ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl = dev_get_drvdata(sctrl->dev); 328ad7fcbc3SSagar Dharia DECLARE_COMPLETION_ONSTACK(done); 329ad7fcbc3SSagar Dharia void *pbuf = slim_alloc_txbuf(ctrl, txn, &done); 330ad7fcbc3SSagar Dharia unsigned long ms = txn->rl + HZ; 331ad7fcbc3SSagar Dharia u8 *puc; 332ad7fcbc3SSagar Dharia int ret = 0, timeout, retries = QCOM_BUF_ALLOC_RETRIES; 333ad7fcbc3SSagar Dharia u8 la = txn->la; 334ad7fcbc3SSagar Dharia u32 *head; 335ad7fcbc3SSagar Dharia /* HW expects length field to be excluded */ 336ad7fcbc3SSagar Dharia txn->rl--; 337ad7fcbc3SSagar Dharia 338ad7fcbc3SSagar Dharia /* spin till buffer is made available */ 339ad7fcbc3SSagar Dharia if (!pbuf) { 340ad7fcbc3SSagar Dharia while (retries--) { 341ad7fcbc3SSagar Dharia usleep_range(10000, 15000); 342ad7fcbc3SSagar Dharia pbuf = slim_alloc_txbuf(ctrl, txn, &done); 343ad7fcbc3SSagar Dharia if (pbuf) 344ad7fcbc3SSagar Dharia break; 345ad7fcbc3SSagar Dharia } 346ad7fcbc3SSagar Dharia } 347ad7fcbc3SSagar Dharia 348*551b9ee4SColin Ian King if (retries < 0 && !pbuf) 349ad7fcbc3SSagar Dharia return -ENOMEM; 350ad7fcbc3SSagar Dharia 351ad7fcbc3SSagar Dharia puc = (u8 *)pbuf; 352ad7fcbc3SSagar Dharia head = (u32 *)pbuf; 353ad7fcbc3SSagar Dharia 354ad7fcbc3SSagar Dharia if (txn->dt == SLIM_MSG_DEST_LOGICALADDR) { 355ad7fcbc3SSagar Dharia *head = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, 356ad7fcbc3SSagar Dharia txn->mc, 0, la); 357ad7fcbc3SSagar Dharia puc += 3; 358ad7fcbc3SSagar Dharia } else { 359ad7fcbc3SSagar Dharia *head = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, 360ad7fcbc3SSagar Dharia txn->mc, 1, la); 361ad7fcbc3SSagar Dharia puc += 2; 362ad7fcbc3SSagar Dharia } 363ad7fcbc3SSagar Dharia 364ad7fcbc3SSagar Dharia if (slim_tid_txn(txn->mt, txn->mc)) 365ad7fcbc3SSagar Dharia *(puc++) = txn->tid; 366ad7fcbc3SSagar Dharia 367ad7fcbc3SSagar Dharia if (slim_ec_txn(txn->mt, txn->mc)) { 368ad7fcbc3SSagar Dharia *(puc++) = (txn->ec & 0xFF); 369ad7fcbc3SSagar Dharia *(puc++) = (txn->ec >> 8) & 0xFF; 370ad7fcbc3SSagar Dharia } 371ad7fcbc3SSagar Dharia 372ad7fcbc3SSagar Dharia if (txn->msg && txn->msg->wbuf) 373ad7fcbc3SSagar Dharia memcpy(puc, txn->msg->wbuf, txn->msg->num_bytes); 374ad7fcbc3SSagar Dharia 375ad7fcbc3SSagar Dharia qcom_slim_queue_tx(ctrl, head, txn->rl, MGR_TX_MSG); 376ad7fcbc3SSagar Dharia timeout = wait_for_completion_timeout(&done, msecs_to_jiffies(ms)); 377ad7fcbc3SSagar Dharia 378ad7fcbc3SSagar Dharia if (!timeout) { 379ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc, 380ad7fcbc3SSagar Dharia txn->mt); 381ad7fcbc3SSagar Dharia ret = -ETIMEDOUT; 382ad7fcbc3SSagar Dharia } 383ad7fcbc3SSagar Dharia 384ad7fcbc3SSagar Dharia return ret; 385ad7fcbc3SSagar Dharia 386ad7fcbc3SSagar Dharia } 387ad7fcbc3SSagar Dharia 388ad7fcbc3SSagar Dharia static int qcom_set_laddr(struct slim_controller *sctrl, 389ad7fcbc3SSagar Dharia struct slim_eaddr *ead, u8 laddr) 390ad7fcbc3SSagar Dharia { 391ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl = dev_get_drvdata(sctrl->dev); 392ad7fcbc3SSagar Dharia struct { 393ad7fcbc3SSagar Dharia __be16 manf_id; 394ad7fcbc3SSagar Dharia __be16 prod_code; 395ad7fcbc3SSagar Dharia u8 dev_index; 396ad7fcbc3SSagar Dharia u8 instance; 397ad7fcbc3SSagar Dharia u8 laddr; 398ad7fcbc3SSagar Dharia } __packed p; 399ad7fcbc3SSagar Dharia struct slim_val_inf msg = {0}; 400ad7fcbc3SSagar Dharia DEFINE_SLIM_EDEST_TXN(txn, SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS, 401ad7fcbc3SSagar Dharia 10, laddr, &msg); 402ad7fcbc3SSagar Dharia int ret; 403ad7fcbc3SSagar Dharia 404ad7fcbc3SSagar Dharia p.manf_id = cpu_to_be16(ead->manf_id); 405ad7fcbc3SSagar Dharia p.prod_code = cpu_to_be16(ead->prod_code); 406ad7fcbc3SSagar Dharia p.dev_index = ead->dev_index; 407ad7fcbc3SSagar Dharia p.instance = ead->instance; 408ad7fcbc3SSagar Dharia p.laddr = laddr; 409ad7fcbc3SSagar Dharia 410ad7fcbc3SSagar Dharia msg.wbuf = (void *)&p; 411ad7fcbc3SSagar Dharia msg.num_bytes = 7; 412ad7fcbc3SSagar Dharia ret = slim_do_transfer(&ctrl->ctrl, &txn); 413ad7fcbc3SSagar Dharia 414ad7fcbc3SSagar Dharia if (ret) 415ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "set LA:0x%x failed:ret:%d\n", 416ad7fcbc3SSagar Dharia laddr, ret); 417ad7fcbc3SSagar Dharia return ret; 418ad7fcbc3SSagar Dharia } 419ad7fcbc3SSagar Dharia 420ad7fcbc3SSagar Dharia static int slim_get_current_rxbuf(struct qcom_slim_ctrl *ctrl, void *buf) 421ad7fcbc3SSagar Dharia { 422ad7fcbc3SSagar Dharia unsigned long flags; 423ad7fcbc3SSagar Dharia 424ad7fcbc3SSagar Dharia spin_lock_irqsave(&ctrl->rx.lock, flags); 425ad7fcbc3SSagar Dharia if (ctrl->rx.tail == ctrl->rx.head) { 426ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->rx.lock, flags); 427ad7fcbc3SSagar Dharia return -ENODATA; 428ad7fcbc3SSagar Dharia } 429ad7fcbc3SSagar Dharia memcpy(buf, ctrl->rx.base + (ctrl->rx.head * ctrl->rx.sl_sz), 430ad7fcbc3SSagar Dharia ctrl->rx.sl_sz); 431ad7fcbc3SSagar Dharia 432ad7fcbc3SSagar Dharia ctrl->rx.head = (ctrl->rx.head + 1) % ctrl->rx.n; 433ad7fcbc3SSagar Dharia spin_unlock_irqrestore(&ctrl->rx.lock, flags); 434ad7fcbc3SSagar Dharia 435ad7fcbc3SSagar Dharia return 0; 436ad7fcbc3SSagar Dharia } 437ad7fcbc3SSagar Dharia 438ad7fcbc3SSagar Dharia static void qcom_slim_rxwq(struct work_struct *work) 439ad7fcbc3SSagar Dharia { 440ad7fcbc3SSagar Dharia u8 buf[SLIM_MSGQ_BUF_LEN]; 441ad7fcbc3SSagar Dharia u8 mc, mt, len; 442ad7fcbc3SSagar Dharia int ret; 443ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl = container_of(work, struct qcom_slim_ctrl, 444ad7fcbc3SSagar Dharia wd); 445ad7fcbc3SSagar Dharia 446ad7fcbc3SSagar Dharia while ((slim_get_current_rxbuf(ctrl, buf)) != -ENODATA) { 447ad7fcbc3SSagar Dharia len = SLIM_HEADER_GET_RL(buf[0]); 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 struct resource *slew_mem; 476ad7fcbc3SSagar Dharia 477ad7fcbc3SSagar Dharia if (!ctrl->slew_reg) { 478ad7fcbc3SSagar Dharia /* SLEW RATE register for this SLIMbus */ 479ad7fcbc3SSagar Dharia slew_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, 480ad7fcbc3SSagar Dharia "slew"); 481ad7fcbc3SSagar Dharia ctrl->slew_reg = devm_ioremap(&pdev->dev, slew_mem->start, 482ad7fcbc3SSagar Dharia resource_size(slew_mem)); 483ad7fcbc3SSagar Dharia if (!ctrl->slew_reg) 484ad7fcbc3SSagar Dharia return; 485ad7fcbc3SSagar Dharia } 486ad7fcbc3SSagar Dharia 487ad7fcbc3SSagar Dharia writel_relaxed(1, ctrl->slew_reg); 488ad7fcbc3SSagar Dharia /* Make sure SLIMbus-slew rate enabling goes through */ 489ad7fcbc3SSagar Dharia wmb(); 490ad7fcbc3SSagar Dharia } 491ad7fcbc3SSagar Dharia 492ad7fcbc3SSagar Dharia static int qcom_slim_probe(struct platform_device *pdev) 493ad7fcbc3SSagar Dharia { 494ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl; 495ad7fcbc3SSagar Dharia struct slim_controller *sctrl; 496ad7fcbc3SSagar Dharia struct resource *slim_mem; 497ad7fcbc3SSagar Dharia int ret, ver; 498ad7fcbc3SSagar Dharia 499ad7fcbc3SSagar Dharia ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); 500ad7fcbc3SSagar Dharia if (!ctrl) 501ad7fcbc3SSagar Dharia return -ENOMEM; 502ad7fcbc3SSagar Dharia 503ad7fcbc3SSagar Dharia ctrl->hclk = devm_clk_get(&pdev->dev, "iface"); 504ad7fcbc3SSagar Dharia if (IS_ERR(ctrl->hclk)) 505ad7fcbc3SSagar Dharia return PTR_ERR(ctrl->hclk); 506ad7fcbc3SSagar Dharia 507ad7fcbc3SSagar Dharia ctrl->rclk = devm_clk_get(&pdev->dev, "core"); 508ad7fcbc3SSagar Dharia if (IS_ERR(ctrl->rclk)) 509ad7fcbc3SSagar Dharia return PTR_ERR(ctrl->rclk); 510ad7fcbc3SSagar Dharia 511ad7fcbc3SSagar Dharia ret = clk_set_rate(ctrl->rclk, SLIM_ROOT_FREQ); 512ad7fcbc3SSagar Dharia if (ret) { 513ad7fcbc3SSagar Dharia dev_err(&pdev->dev, "ref-clock set-rate failed:%d\n", ret); 514ad7fcbc3SSagar Dharia return ret; 515ad7fcbc3SSagar Dharia } 516ad7fcbc3SSagar Dharia 517ad7fcbc3SSagar Dharia ctrl->irq = platform_get_irq(pdev, 0); 518ad7fcbc3SSagar Dharia if (!ctrl->irq) { 519ad7fcbc3SSagar Dharia dev_err(&pdev->dev, "no slimbus IRQ\n"); 520ad7fcbc3SSagar Dharia return -ENODEV; 521ad7fcbc3SSagar Dharia } 522ad7fcbc3SSagar Dharia 523ad7fcbc3SSagar Dharia sctrl = &ctrl->ctrl; 524ad7fcbc3SSagar Dharia sctrl->dev = &pdev->dev; 525ad7fcbc3SSagar Dharia ctrl->dev = &pdev->dev; 526ad7fcbc3SSagar Dharia platform_set_drvdata(pdev, ctrl); 527ad7fcbc3SSagar Dharia dev_set_drvdata(ctrl->dev, ctrl); 528ad7fcbc3SSagar Dharia 529ad7fcbc3SSagar Dharia slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); 530ad7fcbc3SSagar Dharia ctrl->base = devm_ioremap_resource(ctrl->dev, slim_mem); 531ad7fcbc3SSagar Dharia if (!ctrl->base) { 532ad7fcbc3SSagar Dharia dev_err(&pdev->dev, "IOremap failed\n"); 533ad7fcbc3SSagar Dharia return -ENOMEM; 534ad7fcbc3SSagar Dharia } 535ad7fcbc3SSagar Dharia 536ad7fcbc3SSagar Dharia sctrl->set_laddr = qcom_set_laddr; 537ad7fcbc3SSagar Dharia sctrl->xfer_msg = qcom_xfer_msg; 538c088c335SSagar Dharia sctrl->wakeup = qcom_clk_pause_wakeup; 539ad7fcbc3SSagar Dharia ctrl->tx.n = QCOM_TX_MSGS; 540ad7fcbc3SSagar Dharia ctrl->tx.sl_sz = SLIM_MSGQ_BUF_LEN; 541ad7fcbc3SSagar Dharia ctrl->rx.n = QCOM_RX_MSGS; 542ad7fcbc3SSagar Dharia ctrl->rx.sl_sz = SLIM_MSGQ_BUF_LEN; 543ad7fcbc3SSagar Dharia ctrl->wr_comp = kzalloc(sizeof(struct completion *) * QCOM_TX_MSGS, 544ad7fcbc3SSagar Dharia GFP_KERNEL); 545ad7fcbc3SSagar Dharia if (!ctrl->wr_comp) 546ad7fcbc3SSagar Dharia return -ENOMEM; 547ad7fcbc3SSagar Dharia 548ad7fcbc3SSagar Dharia spin_lock_init(&ctrl->rx.lock); 549ad7fcbc3SSagar Dharia spin_lock_init(&ctrl->tx.lock); 550ad7fcbc3SSagar Dharia INIT_WORK(&ctrl->wd, qcom_slim_rxwq); 551ad7fcbc3SSagar Dharia ctrl->rxwq = create_singlethread_workqueue("qcom_slim_rx"); 552ad7fcbc3SSagar Dharia if (!ctrl->rxwq) { 553ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "Failed to start Rx WQ\n"); 554ad7fcbc3SSagar Dharia return -ENOMEM; 555ad7fcbc3SSagar Dharia } 556ad7fcbc3SSagar Dharia 557ad7fcbc3SSagar Dharia ctrl->framer.rootfreq = SLIM_ROOT_FREQ / 8; 558ad7fcbc3SSagar Dharia ctrl->framer.superfreq = 559ad7fcbc3SSagar Dharia ctrl->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8; 560ad7fcbc3SSagar Dharia sctrl->a_framer = &ctrl->framer; 561ad7fcbc3SSagar Dharia sctrl->clkgear = SLIM_MAX_CLK_GEAR; 562ad7fcbc3SSagar Dharia 563ad7fcbc3SSagar Dharia qcom_slim_prg_slew(pdev, ctrl); 564ad7fcbc3SSagar Dharia 565ad7fcbc3SSagar Dharia ret = devm_request_irq(&pdev->dev, ctrl->irq, qcom_slim_interrupt, 566ad7fcbc3SSagar Dharia IRQF_TRIGGER_HIGH, "qcom_slim_irq", ctrl); 567ad7fcbc3SSagar Dharia if (ret) { 568ad7fcbc3SSagar Dharia dev_err(&pdev->dev, "request IRQ failed\n"); 569ad7fcbc3SSagar Dharia goto err_request_irq_failed; 570ad7fcbc3SSagar Dharia } 571ad7fcbc3SSagar Dharia 572ad7fcbc3SSagar Dharia ret = clk_prepare_enable(ctrl->hclk); 573ad7fcbc3SSagar Dharia if (ret) 574ad7fcbc3SSagar Dharia goto err_hclk_enable_failed; 575ad7fcbc3SSagar Dharia 576ad7fcbc3SSagar Dharia ret = clk_prepare_enable(ctrl->rclk); 577ad7fcbc3SSagar Dharia if (ret) 578ad7fcbc3SSagar Dharia goto err_rclk_enable_failed; 579ad7fcbc3SSagar Dharia 580c127f98bSArnd Bergmann ctrl->tx.base = devm_kcalloc(&pdev->dev, ctrl->tx.n, ctrl->tx.sl_sz, 581c127f98bSArnd Bergmann GFP_KERNEL); 582ad7fcbc3SSagar Dharia if (!ctrl->tx.base) { 583ad7fcbc3SSagar Dharia ret = -ENOMEM; 584ad7fcbc3SSagar Dharia goto err; 585ad7fcbc3SSagar Dharia } 586ad7fcbc3SSagar Dharia 587c127f98bSArnd Bergmann ctrl->rx.base = devm_kcalloc(&pdev->dev,ctrl->rx.n, ctrl->rx.sl_sz, 588c127f98bSArnd Bergmann GFP_KERNEL); 589ad7fcbc3SSagar Dharia if (!ctrl->rx.base) { 590ad7fcbc3SSagar Dharia ret = -ENOMEM; 591ad7fcbc3SSagar Dharia goto err; 592ad7fcbc3SSagar Dharia } 593ad7fcbc3SSagar Dharia 594ad7fcbc3SSagar Dharia /* Register with framework before enabling frame, clock */ 595ad7fcbc3SSagar Dharia ret = slim_register_controller(&ctrl->ctrl); 596ad7fcbc3SSagar Dharia if (ret) { 597ad7fcbc3SSagar Dharia dev_err(ctrl->dev, "error adding controller\n"); 598ad7fcbc3SSagar Dharia goto err; 599ad7fcbc3SSagar Dharia } 600ad7fcbc3SSagar Dharia 601ad7fcbc3SSagar Dharia ver = readl_relaxed(ctrl->base); 602ad7fcbc3SSagar Dharia /* Version info in 16 MSbits */ 603ad7fcbc3SSagar Dharia ver >>= 16; 604ad7fcbc3SSagar Dharia /* Component register initialization */ 605ad7fcbc3SSagar Dharia writel(1, ctrl->base + CFG_PORT(COMP_CFG, ver)); 606ad7fcbc3SSagar Dharia writel((EE_MGR_RSC_GRP | EE_NGD_2 | EE_NGD_1), 607ad7fcbc3SSagar Dharia ctrl->base + CFG_PORT(COMP_TRUST_CFG, ver)); 608ad7fcbc3SSagar Dharia 609ad7fcbc3SSagar Dharia writel((MGR_INT_TX_NACKED_2 | 610ad7fcbc3SSagar Dharia MGR_INT_MSG_BUF_CONTE | MGR_INT_RX_MSG_RCVD | 611ad7fcbc3SSagar Dharia MGR_INT_TX_MSG_SENT), ctrl->base + MGR_INT_EN); 612ad7fcbc3SSagar Dharia writel(1, ctrl->base + MGR_CFG); 613ad7fcbc3SSagar Dharia /* Framer register initialization */ 614ad7fcbc3SSagar Dharia writel((1 << INTR_WAKE) | (0xA << REF_CLK_GEAR) | 615ad7fcbc3SSagar Dharia (0xA << CLK_GEAR) | (1 << ROOT_FREQ) | (1 << FRM_ACTIVE) | 1, 616ad7fcbc3SSagar Dharia ctrl->base + FRM_CFG); 617ad7fcbc3SSagar Dharia writel(MGR_CFG_ENABLE, ctrl->base + MGR_CFG); 618ad7fcbc3SSagar Dharia writel(1, ctrl->base + INTF_CFG); 619ad7fcbc3SSagar Dharia writel(1, ctrl->base + CFG_PORT(COMP_CFG, ver)); 620ad7fcbc3SSagar Dharia 621ad7fcbc3SSagar Dharia pm_runtime_use_autosuspend(&pdev->dev); 622ad7fcbc3SSagar Dharia pm_runtime_set_autosuspend_delay(&pdev->dev, QCOM_SLIM_AUTOSUSPEND); 623ad7fcbc3SSagar Dharia pm_runtime_set_active(&pdev->dev); 624ad7fcbc3SSagar Dharia pm_runtime_mark_last_busy(&pdev->dev); 625ad7fcbc3SSagar Dharia pm_runtime_enable(&pdev->dev); 626ad7fcbc3SSagar Dharia 627ad7fcbc3SSagar Dharia dev_dbg(ctrl->dev, "QCOM SB controller is up:ver:0x%x!\n", ver); 628ad7fcbc3SSagar Dharia return 0; 629ad7fcbc3SSagar Dharia 630ad7fcbc3SSagar Dharia err: 631ad7fcbc3SSagar Dharia clk_disable_unprepare(ctrl->rclk); 632ad7fcbc3SSagar Dharia err_rclk_enable_failed: 633ad7fcbc3SSagar Dharia clk_disable_unprepare(ctrl->hclk); 634ad7fcbc3SSagar Dharia err_hclk_enable_failed: 635ad7fcbc3SSagar Dharia err_request_irq_failed: 636ad7fcbc3SSagar Dharia destroy_workqueue(ctrl->rxwq); 637ad7fcbc3SSagar Dharia return ret; 638ad7fcbc3SSagar Dharia } 639ad7fcbc3SSagar Dharia 640ad7fcbc3SSagar Dharia static int qcom_slim_remove(struct platform_device *pdev) 641ad7fcbc3SSagar Dharia { 642ad7fcbc3SSagar Dharia struct qcom_slim_ctrl *ctrl = platform_get_drvdata(pdev); 643ad7fcbc3SSagar Dharia 644c088c335SSagar Dharia pm_runtime_disable(&pdev->dev); 645ad7fcbc3SSagar Dharia slim_unregister_controller(&ctrl->ctrl); 646ad7fcbc3SSagar Dharia destroy_workqueue(ctrl->rxwq); 647ad7fcbc3SSagar Dharia return 0; 648ad7fcbc3SSagar Dharia } 649ad7fcbc3SSagar Dharia 650c088c335SSagar Dharia /* 651c088c335SSagar Dharia * If PM_RUNTIME is not defined, these 2 functions become helper 652c088c335SSagar Dharia * functions to be called from system suspend/resume. 653c088c335SSagar Dharia */ 654c088c335SSagar Dharia #ifdef CONFIG_PM 655c088c335SSagar Dharia static int qcom_slim_runtime_suspend(struct device *device) 656c088c335SSagar Dharia { 657c088c335SSagar Dharia struct platform_device *pdev = to_platform_device(device); 658c088c335SSagar Dharia struct qcom_slim_ctrl *ctrl = platform_get_drvdata(pdev); 659c088c335SSagar Dharia int ret; 660c088c335SSagar Dharia 661c088c335SSagar Dharia dev_dbg(device, "pm_runtime: suspending...\n"); 662c088c335SSagar Dharia ret = slim_ctrl_clk_pause(&ctrl->ctrl, false, SLIM_CLK_UNSPECIFIED); 663c088c335SSagar Dharia if (ret) { 664c088c335SSagar Dharia dev_err(device, "clk pause not entered:%d", ret); 665c088c335SSagar Dharia } else { 666c088c335SSagar Dharia disable_irq(ctrl->irq); 667c088c335SSagar Dharia clk_disable_unprepare(ctrl->hclk); 668c088c335SSagar Dharia clk_disable_unprepare(ctrl->rclk); 669c088c335SSagar Dharia } 670c088c335SSagar Dharia return ret; 671c088c335SSagar Dharia } 672c088c335SSagar Dharia 673c088c335SSagar Dharia static int qcom_slim_runtime_resume(struct device *device) 674c088c335SSagar Dharia { 675c088c335SSagar Dharia struct platform_device *pdev = to_platform_device(device); 676c088c335SSagar Dharia struct qcom_slim_ctrl *ctrl = platform_get_drvdata(pdev); 677c088c335SSagar Dharia int ret = 0; 678c088c335SSagar Dharia 679c088c335SSagar Dharia dev_dbg(device, "pm_runtime: resuming...\n"); 680c088c335SSagar Dharia ret = slim_ctrl_clk_pause(&ctrl->ctrl, true, 0); 681c088c335SSagar Dharia if (ret) 682c088c335SSagar Dharia dev_err(device, "clk pause not exited:%d", ret); 683c088c335SSagar Dharia return ret; 684c088c335SSagar Dharia } 685c088c335SSagar Dharia #endif 686c088c335SSagar Dharia 687c088c335SSagar Dharia #ifdef CONFIG_PM_SLEEP 688c088c335SSagar Dharia static int qcom_slim_suspend(struct device *dev) 689c088c335SSagar Dharia { 690c088c335SSagar Dharia int ret = 0; 691c088c335SSagar Dharia 692c088c335SSagar Dharia if (!pm_runtime_enabled(dev) || 693c088c335SSagar Dharia (!pm_runtime_suspended(dev))) { 694c088c335SSagar Dharia dev_dbg(dev, "system suspend"); 695c088c335SSagar Dharia ret = qcom_slim_runtime_suspend(dev); 696c088c335SSagar Dharia } 697c088c335SSagar Dharia 698c088c335SSagar Dharia return ret; 699c088c335SSagar Dharia } 700c088c335SSagar Dharia 701c088c335SSagar Dharia static int qcom_slim_resume(struct device *dev) 702c088c335SSagar Dharia { 703c088c335SSagar Dharia if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) { 704c088c335SSagar Dharia int ret; 705c088c335SSagar Dharia 706c088c335SSagar Dharia dev_dbg(dev, "system resume"); 707c088c335SSagar Dharia ret = qcom_slim_runtime_resume(dev); 708c088c335SSagar Dharia if (!ret) { 709c088c335SSagar Dharia pm_runtime_mark_last_busy(dev); 710c088c335SSagar Dharia pm_request_autosuspend(dev); 711c088c335SSagar Dharia } 712c088c335SSagar Dharia return ret; 713c088c335SSagar Dharia 714c088c335SSagar Dharia } 715c088c335SSagar Dharia return 0; 716c088c335SSagar Dharia } 717c088c335SSagar Dharia #endif /* CONFIG_PM_SLEEP */ 718c088c335SSagar Dharia 719ad7fcbc3SSagar Dharia static const struct dev_pm_ops qcom_slim_dev_pm_ops = { 720ad7fcbc3SSagar Dharia SET_SYSTEM_SLEEP_PM_OPS(qcom_slim_suspend, qcom_slim_resume) 721ad7fcbc3SSagar Dharia SET_RUNTIME_PM_OPS( 722ad7fcbc3SSagar Dharia qcom_slim_runtime_suspend, 723ad7fcbc3SSagar Dharia qcom_slim_runtime_resume, 724ad7fcbc3SSagar Dharia NULL 725ad7fcbc3SSagar Dharia ) 726ad7fcbc3SSagar Dharia }; 727ad7fcbc3SSagar Dharia 728ad7fcbc3SSagar Dharia static const struct of_device_id qcom_slim_dt_match[] = { 729ad7fcbc3SSagar Dharia { .compatible = "qcom,slim", }, 730ad7fcbc3SSagar Dharia { .compatible = "qcom,apq8064-slim", }, 731ad7fcbc3SSagar Dharia {} 732ad7fcbc3SSagar Dharia }; 733ad7fcbc3SSagar Dharia 734ad7fcbc3SSagar Dharia static struct platform_driver qcom_slim_driver = { 735ad7fcbc3SSagar Dharia .probe = qcom_slim_probe, 736ad7fcbc3SSagar Dharia .remove = qcom_slim_remove, 737ad7fcbc3SSagar Dharia .driver = { 738ad7fcbc3SSagar Dharia .name = "qcom_slim_ctrl", 739ad7fcbc3SSagar Dharia .of_match_table = qcom_slim_dt_match, 740c088c335SSagar Dharia .pm = &qcom_slim_dev_pm_ops, 741ad7fcbc3SSagar Dharia }, 742ad7fcbc3SSagar Dharia }; 743ad7fcbc3SSagar Dharia module_platform_driver(qcom_slim_driver); 744ad7fcbc3SSagar Dharia 745ad7fcbc3SSagar Dharia MODULE_LICENSE("GPL v2"); 746ad7fcbc3SSagar Dharia MODULE_DESCRIPTION("Qualcomm SLIMbus Controller"); 747