linux/drivers/slimbus/messaging.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2011-2017, The Linux Foundation
   4 */
   5
   6#include <linux/slab.h>
   7#include <linux/pm_runtime.h>
   8#include "slimbus.h"
   9
  10/**
  11 * slim_msg_response() - Deliver Message response received from a device to the
  12 *                      framework.
  13 *
  14 * @ctrl: Controller handle
  15 * @reply: Reply received from the device
  16 * @len: Length of the reply
  17 * @tid: Transaction ID received with which framework can associate reply.
  18 *
  19 * Called by controller to inform framework about the response received.
  20 * This helps in making the API asynchronous, and controller-driver doesn't need
  21 * to manage 1 more table other than the one managed by framework mapping TID
  22 * with buffers
  23 */
  24void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 len)
  25{
  26        struct slim_msg_txn *txn;
  27        struct slim_val_inf *msg;
  28        unsigned long flags;
  29
  30        spin_lock_irqsave(&ctrl->txn_lock, flags);
  31        txn = idr_find(&ctrl->tid_idr, tid);
  32        spin_unlock_irqrestore(&ctrl->txn_lock, flags);
  33
  34        if (txn == NULL)
  35                return;
  36
  37        msg = txn->msg;
  38        if (msg == NULL || msg->rbuf == NULL) {
  39                dev_err(ctrl->dev, "Got response to invalid TID:%d, len:%d\n",
  40                                tid, len);
  41                return;
  42        }
  43
  44        slim_free_txn_tid(ctrl, txn);
  45        memcpy(msg->rbuf, reply, len);
  46        if (txn->comp)
  47                complete(txn->comp);
  48
  49        /* Remove runtime-pm vote now that response was received for TID txn */
  50        pm_runtime_mark_last_busy(ctrl->dev);
  51        pm_runtime_put_autosuspend(ctrl->dev);
  52}
  53EXPORT_SYMBOL_GPL(slim_msg_response);
  54
  55/**
  56 * slim_alloc_txn_tid() - Allocate a tid to txn
  57 *
  58 * @ctrl: Controller handle
  59 * @txn: transaction to be allocated with tid.
  60 *
  61 * Return: zero on success with valid txn->tid and error code on failures.
  62 */
  63int slim_alloc_txn_tid(struct slim_controller *ctrl, struct slim_msg_txn *txn)
  64{
  65        unsigned long flags;
  66        int ret = 0;
  67
  68        spin_lock_irqsave(&ctrl->txn_lock, flags);
  69        ret = idr_alloc_cyclic(&ctrl->tid_idr, txn, 0,
  70                                SLIM_MAX_TIDS, GFP_ATOMIC);
  71        if (ret < 0) {
  72                spin_unlock_irqrestore(&ctrl->txn_lock, flags);
  73                return ret;
  74        }
  75        txn->tid = ret;
  76        spin_unlock_irqrestore(&ctrl->txn_lock, flags);
  77        return 0;
  78}
  79EXPORT_SYMBOL_GPL(slim_alloc_txn_tid);
  80
  81/**
  82 * slim_free_txn_tid() - Freee tid of txn
  83 *
  84 * @ctrl: Controller handle
  85 * @txn: transaction whose tid should be freed
  86 */
  87void slim_free_txn_tid(struct slim_controller *ctrl, struct slim_msg_txn *txn)
  88{
  89        unsigned long flags;
  90
  91        spin_lock_irqsave(&ctrl->txn_lock, flags);
  92        idr_remove(&ctrl->tid_idr, txn->tid);
  93        spin_unlock_irqrestore(&ctrl->txn_lock, flags);
  94}
  95EXPORT_SYMBOL_GPL(slim_free_txn_tid);
  96
  97/**
  98 * slim_do_transfer() - Process a SLIMbus-messaging transaction
  99 *
 100 * @ctrl: Controller handle
 101 * @txn: Transaction to be sent over SLIMbus
 102 *
 103 * Called by controller to transmit messaging transactions not dealing with
 104 * Interface/Value elements. (e.g. transmittting a message to assign logical
 105 * address to a slave device
 106 *
 107 * Return: -ETIMEDOUT: If transmission of this message timed out
 108 *      (e.g. due to bus lines not being clocked or driven by controller)
 109 */
 110int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn)
 111{
 112        DECLARE_COMPLETION_ONSTACK(done);
 113        bool need_tid = false, clk_pause_msg = false;
 114        int ret, timeout;
 115
 116        /*
 117         * do not vote for runtime-PM if the transactions are part of clock
 118         * pause sequence
 119         */
 120        if (ctrl->sched.clk_state == SLIM_CLK_ENTERING_PAUSE &&
 121                (txn->mt == SLIM_MSG_MT_CORE &&
 122                 txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
 123                 txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW))
 124                clk_pause_msg = true;
 125
 126        if (!clk_pause_msg) {
 127                ret = pm_runtime_get_sync(ctrl->dev);
 128                if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
 129                        dev_err(ctrl->dev, "ctrl wrong state:%d, ret:%d\n",
 130                                ctrl->sched.clk_state, ret);
 131                        goto slim_xfer_err;
 132                }
 133        }
 134
 135        need_tid = slim_tid_txn(txn->mt, txn->mc);
 136
 137        if (need_tid) {
 138                ret = slim_alloc_txn_tid(ctrl, txn);
 139                if (ret)
 140                        return ret;
 141
 142                if (!txn->msg->comp)
 143                        txn->comp = &done;
 144                else
 145                        txn->comp = txn->comp;
 146        }
 147
 148        ret = ctrl->xfer_msg(ctrl, txn);
 149
 150        if (!ret && need_tid && !txn->msg->comp) {
 151                unsigned long ms = txn->rl + HZ;
 152
 153                timeout = wait_for_completion_timeout(txn->comp,
 154                                                      msecs_to_jiffies(ms));
 155                if (!timeout) {
 156                        ret = -ETIMEDOUT;
 157                        slim_free_txn_tid(ctrl, txn);
 158                }
 159        }
 160
 161        if (ret)
 162                dev_err(ctrl->dev, "Tx:MT:0x%x, MC:0x%x, LA:0x%x failed:%d\n",
 163                        txn->mt, txn->mc, txn->la, ret);
 164
 165slim_xfer_err:
 166        if (!clk_pause_msg && (!need_tid  || ret == -ETIMEDOUT)) {
 167                /*
 168                 * remove runtime-pm vote if this was TX only, or
 169                 * if there was error during this transaction
 170                 */
 171                pm_runtime_mark_last_busy(ctrl->dev);
 172                pm_runtime_put_autosuspend(ctrl->dev);
 173        }
 174        return ret;
 175}
 176EXPORT_SYMBOL_GPL(slim_do_transfer);
 177
 178static int slim_val_inf_sanity(struct slim_controller *ctrl,
 179                               struct slim_val_inf *msg, u8 mc)
 180{
 181        if (!msg || msg->num_bytes > 16 ||
 182            (msg->start_offset + msg->num_bytes) > 0xC00)
 183                goto reterr;
 184        switch (mc) {
 185        case SLIM_MSG_MC_REQUEST_VALUE:
 186        case SLIM_MSG_MC_REQUEST_INFORMATION:
 187                if (msg->rbuf != NULL)
 188                        return 0;
 189                break;
 190
 191        case SLIM_MSG_MC_CHANGE_VALUE:
 192        case SLIM_MSG_MC_CLEAR_INFORMATION:
 193                if (msg->wbuf != NULL)
 194                        return 0;
 195                break;
 196
 197        case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
 198        case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
 199                if (msg->rbuf != NULL && msg->wbuf != NULL)
 200                        return 0;
 201                break;
 202        }
 203reterr:
 204        if (msg)
 205                dev_err(ctrl->dev, "Sanity check failed:msg:offset:0x%x, mc:%d\n",
 206                        msg->start_offset, mc);
 207        return -EINVAL;
 208}
 209
 210static u16 slim_slicesize(int code)
 211{
 212        static const u8 sizetocode[16] = {
 213                0, 1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7
 214        };
 215
 216        code = clamp(code, 1, (int)ARRAY_SIZE(sizetocode));
 217
 218        return sizetocode[code - 1];
 219}
 220
 221/**
 222 * slim_xfer_msg() - Transfer a value info message on slim device
 223 *
 224 * @sbdev: slim device to which this msg has to be transfered
 225 * @msg: value info message pointer
 226 * @mc: message code of the message
 227 *
 228 * Called by drivers which want to transfer a vlaue or info elements.
 229 *
 230 * Return: -ETIMEDOUT: If transmission of this message timed out
 231 */
 232int slim_xfer_msg(struct slim_device *sbdev, struct slim_val_inf *msg,
 233                  u8 mc)
 234{
 235        DEFINE_SLIM_LDEST_TXN(txn_stack, mc, 6, sbdev->laddr, msg);
 236        struct slim_msg_txn *txn = &txn_stack;
 237        struct slim_controller *ctrl = sbdev->ctrl;
 238        int ret;
 239        u16 sl;
 240
 241        if (!ctrl)
 242                return -EINVAL;
 243
 244        ret = slim_val_inf_sanity(ctrl, msg, mc);
 245        if (ret)
 246                return ret;
 247
 248        sl = slim_slicesize(msg->num_bytes);
 249
 250        dev_dbg(ctrl->dev, "SB xfer msg:os:%x, len:%d, MC:%x, sl:%x\n",
 251                msg->start_offset, msg->num_bytes, mc, sl);
 252
 253        txn->ec = ((sl | (1 << 3)) | ((msg->start_offset & 0xFFF) << 4));
 254
 255        switch (mc) {
 256        case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
 257        case SLIM_MSG_MC_CHANGE_VALUE:
 258        case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
 259        case SLIM_MSG_MC_CLEAR_INFORMATION:
 260                txn->rl += msg->num_bytes;
 261        default:
 262                break;
 263        }
 264
 265        if (slim_tid_txn(txn->mt, txn->mc))
 266                txn->rl++;
 267
 268        return slim_do_transfer(ctrl, txn);
 269}
 270EXPORT_SYMBOL_GPL(slim_xfer_msg);
 271
 272static void slim_fill_msg(struct slim_val_inf *msg, u32 addr,
 273                         size_t count, u8 *rbuf, u8 *wbuf)
 274{
 275        msg->start_offset = addr;
 276        msg->num_bytes = count;
 277        msg->rbuf = rbuf;
 278        msg->wbuf = wbuf;
 279        msg->comp = NULL;
 280}
 281
 282/**
 283 * slim_read() - Read SLIMbus value element
 284 *
 285 * @sdev: client handle.
 286 * @addr:  address of value element to read.
 287 * @count: number of bytes to read. Maximum bytes allowed are 16.
 288 * @val: will return what the value element value was
 289 *
 290 * Return: -EINVAL for Invalid parameters, -ETIMEDOUT If transmission of
 291 * this message timed out (e.g. due to bus lines not being clocked
 292 * or driven by controller)
 293 */
 294int slim_read(struct slim_device *sdev, u32 addr, size_t count, u8 *val)
 295{
 296        struct slim_val_inf msg;
 297
 298        slim_fill_msg(&msg, addr, count, val, NULL);
 299
 300        return slim_xfer_msg(sdev, &msg, SLIM_MSG_MC_REQUEST_VALUE);
 301}
 302EXPORT_SYMBOL_GPL(slim_read);
 303
 304/**
 305 * slim_readb() - Read byte from SLIMbus value element
 306 *
 307 * @sdev: client handle.
 308 * @addr:  address in the value element to read.
 309 *
 310 * Return: byte value of value element.
 311 */
 312int slim_readb(struct slim_device *sdev, u32 addr)
 313{
 314        int ret;
 315        u8 buf;
 316
 317        ret = slim_read(sdev, addr, 1, &buf);
 318        if (ret < 0)
 319                return ret;
 320        else
 321                return buf;
 322}
 323EXPORT_SYMBOL_GPL(slim_readb);
 324
 325/**
 326 * slim_write() - Write SLIMbus value element
 327 *
 328 * @sdev: client handle.
 329 * @addr:  address in the value element to write.
 330 * @count: number of bytes to write. Maximum bytes allowed are 16.
 331 * @val: value to write to value element
 332 *
 333 * Return: -EINVAL for Invalid parameters, -ETIMEDOUT If transmission of
 334 * this message timed out (e.g. due to bus lines not being clocked
 335 * or driven by controller)
 336 */
 337int slim_write(struct slim_device *sdev, u32 addr, size_t count, u8 *val)
 338{
 339        struct slim_val_inf msg;
 340
 341        slim_fill_msg(&msg, addr, count,  NULL, val);
 342
 343        return slim_xfer_msg(sdev, &msg, SLIM_MSG_MC_CHANGE_VALUE);
 344}
 345EXPORT_SYMBOL_GPL(slim_write);
 346
 347/**
 348 * slim_writeb() - Write byte to SLIMbus value element
 349 *
 350 * @sdev: client handle.
 351 * @addr:  address of value element to write.
 352 * @value: value to write to value element
 353 *
 354 * Return: -EINVAL for Invalid parameters, -ETIMEDOUT If transmission of
 355 * this message timed out (e.g. due to bus lines not being clocked
 356 * or driven by controller)
 357 *
 358 */
 359int slim_writeb(struct slim_device *sdev, u32 addr, u8 value)
 360{
 361        return slim_write(sdev, addr, 1, &value);
 362}
 363EXPORT_SYMBOL_GPL(slim_writeb);
 364