blob: 7bf8a18b63d596386660c8a442de7c345a7e9255 [file] [log] [blame]
Lina Iyer73101422017-02-16 14:09:25 -07001/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
Lina Iyer88a8fda2016-04-01 08:23:31 -06002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
Lina Iyerd9abfa62017-07-27 17:23:05 -060014#define pr_fmt(fmt) "%s " fmt, KBUILD_MODNAME
Lina Iyer88a8fda2016-04-01 08:23:31 -060015
Lina Iyerb68814f2017-04-14 12:49:07 -060016#include <linux/atomic.h>
Lina Iyer88a8fda2016-04-01 08:23:31 -060017#include <linux/bitmap.h>
Lina Iyer8bb7d5a2017-04-20 09:50:41 -060018#include <linux/delay.h>
Lina Iyer88a8fda2016-04-01 08:23:31 -060019#include <linux/interrupt.h>
Lina Iyer47b708a2017-07-21 18:49:00 -060020#include <linux/ipc_logging.h>
Lina Iyer88a8fda2016-04-01 08:23:31 -060021#include <linux/jiffies.h>
22#include <linux/kernel.h>
23#include <linux/list.h>
24#include <linux/mailbox_client.h> /* For dev_err */
25#include <linux/mailbox_controller.h>
26#include <linux/module.h>
27#include <linux/of.h>
28#include <linux/of_address.h>
29#include <linux/of_irq.h>
30#include <linux/platform_device.h>
31#include <linux/spinlock.h>
Lina Iyer88a8fda2016-04-01 08:23:31 -060032
33#include <asm-generic/io.h>
34
35#include <soc/qcom/tcs.h>
36
37#include <dt-bindings/soc/qcom,tcs-mbox.h>
38
39#include "mailbox.h"
40
Lina Iyerea921442016-05-26 15:07:48 -060041#define CREATE_TRACE_POINTS
42#include <trace/events/rpmh.h>
43
Lina Iyerecb04eb2017-08-29 15:35:43 -060044#define RSC_DRV_IPC_LOG_SIZE 2
Lina Iyer47b708a2017-07-21 18:49:00 -060045
Lina Iyer88a8fda2016-04-01 08:23:31 -060046#define MAX_CMDS_PER_TCS 16
47#define MAX_TCS_PER_TYPE 3
48#define MAX_TCS_SLOTS (MAX_CMDS_PER_TCS * MAX_TCS_PER_TYPE)
49
Lina Iyerecb04eb2017-08-29 15:35:43 -060050#define RSC_DRV_TCS_OFFSET 672
51#define RSC_DRV_CMD_OFFSET 20
Lina Iyer88a8fda2016-04-01 08:23:31 -060052
53/* DRV Configuration Information Register */
54#define DRV_PRNT_CHLD_CONFIG 0x0C
55#define DRV_NUM_TCS_MASK 0x3F
56#define DRV_NUM_TCS_SHIFT 6
57#define DRV_NCPT_MASK 0x1F
58#define DRV_NCPT_SHIFT 27
59
60/* Register offsets */
Lina Iyerecb04eb2017-08-29 15:35:43 -060061#define RSC_DRV_IRQ_ENABLE 0x00
62#define RSC_DRV_IRQ_STATUS 0x04
63#define RSC_DRV_IRQ_CLEAR 0x08
64#define RSC_DRV_CMD_WAIT_FOR_CMPL 0x10
65#define RSC_DRV_CONTROL 0x14
66#define RSC_DRV_STATUS 0x18
67#define RSC_DRV_CMD_ENABLE 0x1C
68#define RSC_DRV_CMD_MSGID 0x30
69#define RSC_DRV_CMD_ADDR 0x34
70#define RSC_DRV_CMD_DATA 0x38
71#define RSC_DRV_CMD_STATUS 0x3C
72#define RSC_DRV_CMD_RESP_DATA 0x40
Lina Iyer88a8fda2016-04-01 08:23:31 -060073
74#define TCS_AMC_MODE_ENABLE BIT(16)
75#define TCS_AMC_MODE_TRIGGER BIT(24)
76
77/* TCS CMD register bit mask */
78#define CMD_MSGID_LEN 8
79#define CMD_MSGID_RESP_REQ BIT(8)
80#define CMD_MSGID_WRITE BIT(16)
81#define CMD_STATUS_ISSUED BIT(8)
82#define CMD_STATUS_COMPL BIT(16)
83
84/* Control/Hidden TCS */
Lina Iyerd1a6e682017-06-20 10:05:09 -060085#define TCS_HIDDEN_MAX_SLOTS 2
Lina Iyer88a8fda2016-04-01 08:23:31 -060086#define TCS_HIDDEN_CMD0_DRV_DATA 0x38
87#define TCS_HIDDEN_CMD_SHIFT 0x08
88
89#define TCS_TYPE_NR 4
Lina Iyer88a8fda2016-04-01 08:23:31 -060090#define MAX_POOL_SIZE (MAX_TCS_PER_TYPE * TCS_TYPE_NR)
Lina Iyerc8712ca2017-04-20 00:18:49 -060091#define TCS_M_INIT 0xFFFF
Lina Iyer88a8fda2016-04-01 08:23:31 -060092
Lina Iyerecb04eb2017-08-29 15:35:43 -060093struct rsc_drv;
Lina Iyer88a8fda2016-04-01 08:23:31 -060094
95struct tcs_response {
Lina Iyerecb04eb2017-08-29 15:35:43 -060096 struct rsc_drv *drv;
Lina Iyer88a8fda2016-04-01 08:23:31 -060097 struct mbox_chan *chan;
98 struct tcs_mbox_msg *msg;
99 u32 m; /* m-th TCS */
Lina Iyer88a8fda2016-04-01 08:23:31 -0600100 int err;
Lina Iyerc8712ca2017-04-20 00:18:49 -0600101 int idx;
102 bool in_use;
Lina Iyer00328f12017-05-30 14:08:49 -0600103 struct list_head list;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600104};
105
106struct tcs_response_pool {
Lina Iyerc8712ca2017-04-20 00:18:49 -0600107 struct tcs_response resp[MAX_POOL_SIZE];
Lina Iyer88a8fda2016-04-01 08:23:31 -0600108 spinlock_t lock;
109 DECLARE_BITMAP(avail, MAX_POOL_SIZE);
110};
111
112/* One per TCS type of a controller */
113struct tcs_mbox {
Lina Iyerecb04eb2017-08-29 15:35:43 -0600114 struct rsc_drv *drv;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600115 u32 *cmd_addr;
116 int type;
117 u32 tcs_mask;
118 u32 tcs_offset;
119 int num_tcs;
120 int ncpt; /* num cmds per tcs */
121 DECLARE_BITMAP(slots, MAX_TCS_SLOTS);
122 spinlock_t tcs_lock; /* TCS type lock */
Lina Iyer88a8fda2016-04-01 08:23:31 -0600123};
124
125/* One per MBOX controller */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600126struct rsc_drv {
Lina Iyer14424652017-06-03 15:55:25 -0600127 struct mbox_controller mbox;
Lina Iyer884981e2017-03-21 13:43:05 -0600128 const char *name;
Lina Iyer00328f12017-05-30 14:08:49 -0600129 void __iomem *base; /* start address of the RSC's registers */
130 void __iomem *reg_base; /* start address for DRV specific register */
Lina Iyer88a8fda2016-04-01 08:23:31 -0600131 int drv_id;
132 struct platform_device *pdev;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600133 struct tcs_mbox tcs[TCS_TYPE_NR];
134 int num_assigned;
135 int num_tcs;
Lina Iyer00328f12017-05-30 14:08:49 -0600136 struct tasklet_struct tasklet;
137 struct list_head response_pending;
138 spinlock_t drv_lock;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600139 struct tcs_response_pool *resp_pool;
Lina Iyer088ccec2017-04-24 20:18:48 -0600140 atomic_t tcs_in_use[MAX_POOL_SIZE];
Lina Iyer51bc14d2017-04-28 13:56:12 -0600141 /* Debug info */
142 u64 tcs_last_sent_ts[MAX_POOL_SIZE];
143 u64 tcs_last_recv_ts[MAX_POOL_SIZE];
Lina Iyer088ccec2017-04-24 20:18:48 -0600144 atomic_t tcs_send_count[MAX_POOL_SIZE];
145 atomic_t tcs_irq_count[MAX_POOL_SIZE];
Lina Iyer47b708a2017-07-21 18:49:00 -0600146 void *ipc_log_ctx;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600147};
148
Lina Iyer47b708a2017-07-21 18:49:00 -0600149/* Log to IPC and Ftrace */
150#define log_send_msg(drv, m, n, i, a, d, c, t) do { \
151 trace_rpmh_send_msg(drv->name, m, n, i, a, d, c, t); \
152 ipc_log_string(drv->ipc_log_ctx, \
153 "send msg: m=%d n=%d msgid=0x%x addr=0x%x data=0x%x cmpl=%d trigger=%d", \
154 m, n, i, a, d, c, t); \
155 } while (0)
156
157#define log_rpmh_notify_irq(drv, m, a, e) do { \
158 trace_rpmh_notify_irq(drv->name, m, a, e); \
159 ipc_log_string(drv->ipc_log_ctx, \
160 "irq response: m=%d addr=0x%x err=%d", m, a, e); \
161 } while (0)
162
163#define log_rpmh_control_msg(drv, d) do { \
164 trace_rpmh_control_msg(drv->name, d); \
165 ipc_log_string(drv->ipc_log_ctx, "ctrlr msg: data=0x%x", d); \
166 } while (0)
167
168#define log_rpmh_notify(drv, m, a, e) do { \
169 trace_rpmh_notify(drv->name, m, a, e); \
170 ipc_log_string(drv->ipc_log_ctx, \
171 "tx done: m=%d addr=0x%x err=%d", m, a, e); \
172 } while (0)
173
174
Lina Iyerecb04eb2017-08-29 15:35:43 -0600175static int tcs_response_pool_init(struct rsc_drv *drv)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600176{
177 struct tcs_response_pool *pool;
178 int i;
179
180 pool = devm_kzalloc(&drv->pdev->dev, sizeof(*pool), GFP_KERNEL);
181 if (!pool)
182 return -ENOMEM;
183
Lina Iyer88a8fda2016-04-01 08:23:31 -0600184 for (i = 0; i < MAX_POOL_SIZE; i++) {
Lina Iyerc8712ca2017-04-20 00:18:49 -0600185 pool->resp[i].drv = drv;
186 pool->resp[i].idx = i;
187 pool->resp[i].m = TCS_M_INIT;
Lina Iyer00328f12017-05-30 14:08:49 -0600188 INIT_LIST_HEAD(&pool->resp[i].list);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600189 }
190
191 spin_lock_init(&pool->lock);
192 drv->resp_pool = pool;
193
194 return 0;
195}
196
Lina Iyerecb04eb2017-08-29 15:35:43 -0600197static struct tcs_response *setup_response(struct rsc_drv *drv,
Lina Iyerc8712ca2017-04-20 00:18:49 -0600198 struct tcs_mbox_msg *msg, struct mbox_chan *chan,
199 u32 m, int err)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600200{
201 struct tcs_response_pool *pool = drv->resp_pool;
202 struct tcs_response *resp = ERR_PTR(-ENOMEM);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600203 int pos;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600204 unsigned long flags;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600205
Lina Iyer51bc14d2017-04-28 13:56:12 -0600206 spin_lock_irqsave(&pool->lock, flags);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600207 pos = find_first_zero_bit(pool->avail, MAX_POOL_SIZE);
208 if (pos != MAX_POOL_SIZE) {
209 bitmap_set(pool->avail, pos, 1);
210 resp = &pool->resp[pos];
Lina Iyerc8712ca2017-04-20 00:18:49 -0600211 resp->chan = chan;
212 resp->msg = msg;
213 resp->m = m;
214 resp->err = err;
215 resp->in_use = false;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600216 }
Lina Iyer51bc14d2017-04-28 13:56:12 -0600217 spin_unlock_irqrestore(&pool->lock, flags);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600218
Lina Iyer00328f12017-05-30 14:08:49 -0600219 if (pos == MAX_POOL_SIZE)
220 pr_err("response pool is full\n");
221
Lina Iyer88a8fda2016-04-01 08:23:31 -0600222 return resp;
223}
224
Lina Iyerc8712ca2017-04-20 00:18:49 -0600225static void free_response(struct tcs_response *resp)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600226{
227 struct tcs_response_pool *pool = resp->drv->resp_pool;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600228 unsigned long flags;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600229
Lina Iyer51bc14d2017-04-28 13:56:12 -0600230 spin_lock_irqsave(&pool->lock, flags);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600231 resp->err = -EINVAL;
232 bitmap_clear(pool->avail, resp->idx, 1);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600233 spin_unlock_irqrestore(&pool->lock, flags);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600234}
235
Lina Iyerecb04eb2017-08-29 15:35:43 -0600236static inline struct tcs_response *get_response(struct rsc_drv *drv, u32 m,
Lina Iyer51bc14d2017-04-28 13:56:12 -0600237 bool for_use)
Lina Iyerc8712ca2017-04-20 00:18:49 -0600238{
239 struct tcs_response_pool *pool = drv->resp_pool;
240 struct tcs_response *resp = NULL;
241 int pos = 0;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600242 unsigned long flags;
Lina Iyerc8712ca2017-04-20 00:18:49 -0600243
Lina Iyer51bc14d2017-04-28 13:56:12 -0600244 spin_lock_irqsave(&pool->lock, flags);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600245 do {
246 pos = find_next_bit(pool->avail, MAX_POOL_SIZE, pos);
247 if (pos == MAX_POOL_SIZE)
248 break;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600249
Lina Iyerc8712ca2017-04-20 00:18:49 -0600250 resp = &pool->resp[pos];
251 if (resp->m == m && !resp->in_use) {
Lina Iyer51bc14d2017-04-28 13:56:12 -0600252 resp->in_use = for_use;
Lina Iyerc8712ca2017-04-20 00:18:49 -0600253 break;
254 }
255 pos++;
256 } while (1);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600257 spin_unlock_irqrestore(&pool->lock, flags);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600258
259 return resp;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600260}
261
Lina Iyerecb04eb2017-08-29 15:35:43 -0600262static void print_response(struct rsc_drv *drv, int m)
Lina Iyer51bc14d2017-04-28 13:56:12 -0600263{
264 struct tcs_response *resp;
265 struct tcs_mbox_msg *msg;
266 int i;
267
268 resp = get_response(drv, m, false);
269 if (!resp)
270 return;
271
272 msg = resp->msg;
Lina Iyerd9abfa62017-07-27 17:23:05 -0600273 pr_warn("Response object [idx=%d for-tcs=%d in-use=%d]\n",
Lina Iyer51bc14d2017-04-28 13:56:12 -0600274 resp->idx, resp->m, resp->in_use);
Lina Iyere2f42722017-07-13 12:37:50 -0600275 pr_warn("Msg: state=%d\n", msg->state);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600276 for (i = 0; i < msg->num_payload; i++)
Lina Iyere2f42722017-07-13 12:37:50 -0600277 pr_warn("addr=0x%x data=0x%x complete=0x%x\n",
Lina Iyer51bc14d2017-04-28 13:56:12 -0600278 msg->payload[i].addr,
279 msg->payload[i].data,
280 msg->payload[i].complete);
281}
282
Lina Iyer88a8fda2016-04-01 08:23:31 -0600283static inline u32 read_drv_config(void __iomem *base)
284{
285 return le32_to_cpu(readl_relaxed(base + DRV_PRNT_CHLD_CONFIG));
286}
287
288static inline u32 read_tcs_reg(void __iomem *base, int reg, int m, int n)
289{
290 return le32_to_cpu(readl_relaxed(base + reg +
Lina Iyerecb04eb2017-08-29 15:35:43 -0600291 RSC_DRV_TCS_OFFSET * m + RSC_DRV_CMD_OFFSET * n));
Lina Iyer88a8fda2016-04-01 08:23:31 -0600292}
293
294static inline void write_tcs_reg(void __iomem *base, int reg, int m, int n,
295 u32 data)
296{
297 writel_relaxed(cpu_to_le32(data), base + reg +
Lina Iyerecb04eb2017-08-29 15:35:43 -0600298 RSC_DRV_TCS_OFFSET * m + RSC_DRV_CMD_OFFSET * n);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600299}
300
301static inline void write_tcs_reg_sync(void __iomem *base, int reg, int m, int n,
302 u32 data)
303{
304 do {
305 write_tcs_reg(base, reg, m, n, data);
306 if (data == read_tcs_reg(base, reg, m, n))
307 break;
Lina Iyer8bb7d5a2017-04-20 09:50:41 -0600308 udelay(1);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600309 } while (1);
310}
311
Lina Iyerecb04eb2017-08-29 15:35:43 -0600312static inline bool tcs_is_free(struct rsc_drv *drv, int m)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600313{
Lina Iyerb68814f2017-04-14 12:49:07 -0600314 void __iomem *base = drv->reg_base;
315
Lina Iyerecb04eb2017-08-29 15:35:43 -0600316 return read_tcs_reg(base, RSC_DRV_STATUS, m, 0) &&
Lina Iyerb68814f2017-04-14 12:49:07 -0600317 !atomic_read(&drv->tcs_in_use[m]);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600318}
319
Lina Iyerecb04eb2017-08-29 15:35:43 -0600320static inline struct tcs_mbox *get_tcs_from_index(struct rsc_drv *drv, int m)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600321{
Lina Iyer51bc14d2017-04-28 13:56:12 -0600322 struct tcs_mbox *tcs = NULL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600323 int i;
324
Lina Iyer51bc14d2017-04-28 13:56:12 -0600325 for (i = 0; i < drv->num_tcs; i++) {
Lina Iyer88a8fda2016-04-01 08:23:31 -0600326 tcs = &drv->tcs[i];
Lina Iyer51bc14d2017-04-28 13:56:12 -0600327 if (tcs->tcs_mask & (u32)BIT(m))
Lina Iyer88a8fda2016-04-01 08:23:31 -0600328 break;
329 }
330
Lina Iyer51bc14d2017-04-28 13:56:12 -0600331 if (i == drv->num_tcs) {
332 WARN(1, "Incorrect TCS index %d", m);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600333 tcs = NULL;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600334 }
Lina Iyer88a8fda2016-04-01 08:23:31 -0600335
336 return tcs;
337}
338
Lina Iyerecb04eb2017-08-29 15:35:43 -0600339static inline struct tcs_mbox *get_tcs_of_type(struct rsc_drv *drv, int type)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600340{
341 int i;
342 struct tcs_mbox *tcs;
343
344 for (i = 0; i < TCS_TYPE_NR; i++)
345 if (type == drv->tcs[i].type)
346 break;
347
348 if (i == TCS_TYPE_NR)
349 return ERR_PTR(-EINVAL);
350
351 tcs = &drv->tcs[i];
352 if (!tcs->num_tcs)
353 return ERR_PTR(-EINVAL);
354
355 return tcs;
356}
357
Lina Iyerecb04eb2017-08-29 15:35:43 -0600358static inline struct tcs_mbox *get_tcs_for_msg(struct rsc_drv *drv,
Lina Iyer88a8fda2016-04-01 08:23:31 -0600359 struct tcs_mbox_msg *msg)
360{
361 int type = -1;
362
363 /* Which box are we dropping this in and do we trigger the TCS */
364 switch (msg->state) {
365 case RPMH_SLEEP_STATE:
366 type = SLEEP_TCS;
367 break;
368 case RPMH_WAKE_ONLY_STATE:
369 type = WAKE_TCS;
370 break;
371 case RPMH_ACTIVE_ONLY_STATE:
372 type = ACTIVE_TCS;
373 break;
Lina Iyer21c17882016-09-22 11:05:51 -0600374 case RPMH_AWAKE_STATE:
375 /*
376 * Awake state is only used when the DRV has no separate
377 * TCS for ACTIVE requests. Switch to WAKE TCS to send
378 * active votes. Otherwise, the caller should be explicit
379 * about the state.
380 */
381 if (IS_ERR(get_tcs_of_type(drv, ACTIVE_TCS)))
382 type = WAKE_TCS;
383 break;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600384 }
385
386 if (msg->is_read)
387 type = ACTIVE_TCS;
388
389 if (type < 0)
390 return ERR_PTR(-EINVAL);
391
392 return get_tcs_of_type(drv, type);
393}
394
Lina Iyer88a8fda2016-04-01 08:23:31 -0600395static inline void send_tcs_response(struct tcs_response *resp)
396{
Lina Iyerecb04eb2017-08-29 15:35:43 -0600397 struct rsc_drv *drv = resp->drv;
Lina Iyer00328f12017-05-30 14:08:49 -0600398 unsigned long flags;
399
400 spin_lock_irqsave(&drv->drv_lock, flags);
401 INIT_LIST_HEAD(&resp->list);
402 list_add_tail(&resp->list, &drv->response_pending);
403 spin_unlock_irqrestore(&drv->drv_lock, flags);
404
405 tasklet_schedule(&drv->tasklet);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600406}
407
Lina Iyerecb04eb2017-08-29 15:35:43 -0600408static inline void enable_tcs_irq(struct rsc_drv *drv, int m, bool enable)
Lina Iyere0ef5212017-04-28 09:55:32 -0600409{
410 void __iomem *base = drv->reg_base;
411 u32 data;
412
413 /* Enable interrupts for non-ACTIVE TCS */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600414 data = read_tcs_reg(base, RSC_DRV_IRQ_ENABLE, 0, 0);
Lina Iyere0ef5212017-04-28 09:55:32 -0600415 if (enable)
416 data |= BIT(m);
417 else
418 data &= ~BIT(m);
Lina Iyerecb04eb2017-08-29 15:35:43 -0600419 write_tcs_reg(base, RSC_DRV_IRQ_ENABLE, 0, 0, data);
Lina Iyere0ef5212017-04-28 09:55:32 -0600420}
421
Lina Iyer88a8fda2016-04-01 08:23:31 -0600422/**
423 * tcs_irq_handler: TX Done / Recv data handler
424 */
425static irqreturn_t tcs_irq_handler(int irq, void *p)
426{
Lina Iyerecb04eb2017-08-29 15:35:43 -0600427 struct rsc_drv *drv = p;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600428 void __iomem *base = drv->reg_base;
429 int m, i;
430 u32 irq_status, sts;
Lina Iyer21c17882016-09-22 11:05:51 -0600431 struct tcs_mbox *tcs;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600432 struct tcs_response *resp;
Lina Iyer7846e212017-03-22 10:35:53 -0600433 struct tcs_cmd *cmd;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600434 u32 data;
435
436 /* Know which TCSes were triggered */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600437 irq_status = read_tcs_reg(base, RSC_DRV_IRQ_STATUS, 0, 0);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600438
Lina Iyerb465ea62017-05-03 15:27:22 -0600439 for (m = 0; m < drv->num_tcs; m++) {
440 if (!(irq_status & (u32)BIT(m)))
Lina Iyer88a8fda2016-04-01 08:23:31 -0600441 continue;
Lina Iyer088ccec2017-04-24 20:18:48 -0600442 atomic_inc(&drv->tcs_irq_count[m]);
443
Lina Iyer51bc14d2017-04-28 13:56:12 -0600444 resp = get_response(drv, m, true);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600445 if (!resp) {
446 pr_err("No resp request for TCS-%d\n", m);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600447 goto no_resp;
Lina Iyer21c17882016-09-22 11:05:51 -0600448 }
Lina Iyer88a8fda2016-04-01 08:23:31 -0600449
450 /* Check if all commands were completed */
451 resp->err = 0;
452 for (i = 0; i < resp->msg->num_payload; i++) {
Lina Iyer7846e212017-03-22 10:35:53 -0600453 cmd = &resp->msg->payload[i];
Lina Iyerecb04eb2017-08-29 15:35:43 -0600454 sts = read_tcs_reg(base, RSC_DRV_CMD_STATUS, m, i);
Lina Iyer7846e212017-03-22 10:35:53 -0600455 if ((!(sts & CMD_STATUS_ISSUED)) ||
456 ((resp->msg->is_complete || cmd->complete) &&
Lina Iyerfd7e0c22017-07-14 18:08:46 -0600457 (!(sts & CMD_STATUS_COMPL)))) {
Lina Iyer88a8fda2016-04-01 08:23:31 -0600458 resp->err = -EIO;
Lina Iyerfd7e0c22017-07-14 18:08:46 -0600459 break;
460 }
Lina Iyer88a8fda2016-04-01 08:23:31 -0600461 }
462
463 /* Check for response if this was a read request */
464 if (resp->msg->is_read) {
465 /* Respond the data back in the same req data */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600466 data = read_tcs_reg(base, RSC_DRV_CMD_RESP_DATA, m, 0);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600467 resp->msg->payload[0].data = data;
468 mbox_chan_received_data(resp->chan, resp->msg);
469 }
470
Lina Iyer47b708a2017-07-21 18:49:00 -0600471 log_rpmh_notify_irq(drv, m, resp->msg->payload[0].addr,
Lina Iyer884981e2017-03-21 13:43:05 -0600472 resp->err);
Lina Iyerea921442016-05-26 15:07:48 -0600473
Lina Iyer7846e212017-03-22 10:35:53 -0600474 /* Clear the AMC mode for non-ACTIVE TCSes */
Lina Iyer51bc14d2017-04-28 13:56:12 -0600475 tcs = get_tcs_from_index(drv, m);
476 if (tcs && tcs->type != ACTIVE_TCS) {
Lina Iyerecb04eb2017-08-29 15:35:43 -0600477 data = read_tcs_reg(base, RSC_DRV_CONTROL, m, 0);
Lina Iyerd3435fa2017-08-08 15:42:10 -0600478 data &= ~TCS_AMC_MODE_TRIGGER;
Lina Iyerecb04eb2017-08-29 15:35:43 -0600479 write_tcs_reg_sync(base, RSC_DRV_CONTROL, m, 0, data);
Lina Iyer7846e212017-03-22 10:35:53 -0600480 data &= ~TCS_AMC_MODE_ENABLE;
Lina Iyerecb04eb2017-08-29 15:35:43 -0600481 write_tcs_reg(base, RSC_DRV_CONTROL, m, 0, data);
Lina Iyere0ef5212017-04-28 09:55:32 -0600482 /*
483 * Disable interrupt for this TCS to avoid being
484 * spammed with interrupts coming when the solver
485 * sends its wake votes.
486 */
487 enable_tcs_irq(drv, m, false);
Lina Iyer7846e212017-03-22 10:35:53 -0600488 } else {
489 /* Clear the enable bit for the commands */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600490 write_tcs_reg(base, RSC_DRV_CMD_ENABLE, m, 0, 0);
Lina Iyer7846e212017-03-22 10:35:53 -0600491 }
492
Lina Iyer51bc14d2017-04-28 13:56:12 -0600493no_resp:
494 /* Record the recvd time stamp */
495 drv->tcs_last_recv_ts[m] = arch_counter_get_cntvct();
496
Lina Iyerc8712ca2017-04-20 00:18:49 -0600497 /* Clear the TCS IRQ status */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600498 write_tcs_reg(base, RSC_DRV_IRQ_CLEAR, 0, 0, BIT(m));
Lina Iyerc8712ca2017-04-20 00:18:49 -0600499
Lina Iyer00328f12017-05-30 14:08:49 -0600500 /* Notify the client that this request is completed. */
501 atomic_set(&drv->tcs_in_use[m], 0);
502
Lina Iyerc8712ca2017-04-20 00:18:49 -0600503 /* Clean up response object and notify mbox in tasklet */
Lina Iyer51bc14d2017-04-28 13:56:12 -0600504 if (resp)
505 send_tcs_response(resp);
Lina Iyerb68814f2017-04-14 12:49:07 -0600506 }
507
Lina Iyer88a8fda2016-04-01 08:23:31 -0600508 return IRQ_HANDLED;
509}
510
511static inline void mbox_notify_tx_done(struct mbox_chan *chan,
512 struct tcs_mbox_msg *msg, int m, int err)
513{
Lina Iyerecb04eb2017-08-29 15:35:43 -0600514 struct rsc_drv *drv = container_of(chan->mbox, struct rsc_drv, mbox);
Lina Iyer884981e2017-03-21 13:43:05 -0600515
Lina Iyer47b708a2017-07-21 18:49:00 -0600516 log_rpmh_notify(drv, m, msg->payload[0].addr, err);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600517 mbox_chan_txdone(chan, err);
518}
519
Lina Iyer00328f12017-05-30 14:08:49 -0600520static void respond_tx_done(struct tcs_response *resp)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600521{
Lina Iyer88a8fda2016-04-01 08:23:31 -0600522 struct mbox_chan *chan = resp->chan;
523 struct tcs_mbox_msg *msg = resp->msg;
524 int err = resp->err;
525 int m = resp->m;
526
Lina Iyerc8712ca2017-04-20 00:18:49 -0600527 free_response(resp);
Lina Iyer00328f12017-05-30 14:08:49 -0600528 mbox_notify_tx_done(chan, msg, m, err);
529}
530
531/**
532 * tcs_notify_tx_done: TX Done for requests that do not trigger TCS
533 */
534static void tcs_notify_tx_done(unsigned long data)
535{
Lina Iyerecb04eb2017-08-29 15:35:43 -0600536 struct rsc_drv *drv = (struct rsc_drv *)data;
Lina Iyer00328f12017-05-30 14:08:49 -0600537 struct tcs_response *resp;
538 unsigned long flags;
539
540 do {
541 spin_lock_irqsave(&drv->drv_lock, flags);
542 if (list_empty(&drv->response_pending)) {
543 spin_unlock_irqrestore(&drv->drv_lock, flags);
544 break;
545 }
546 resp = list_first_entry(&drv->response_pending,
547 struct tcs_response, list);
548 list_del(&resp->list);
549 spin_unlock_irqrestore(&drv->drv_lock, flags);
550 respond_tx_done(resp);
551 } while (1);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600552}
553
Lina Iyerecb04eb2017-08-29 15:35:43 -0600554static void __tcs_buffer_write(struct rsc_drv *drv, int d, int m, int n,
Lina Iyer88a8fda2016-04-01 08:23:31 -0600555 struct tcs_mbox_msg *msg, bool trigger)
556{
Lina Iyer7846e212017-03-22 10:35:53 -0600557 u32 msgid, cmd_msgid = 0;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600558 u32 cmd_enable = 0;
559 u32 cmd_complete;
Lina Iyerd3435fa2017-08-08 15:42:10 -0600560 u32 enable;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600561 struct tcs_cmd *cmd;
562 int i;
Lina Iyer884981e2017-03-21 13:43:05 -0600563 void __iomem *base = drv->reg_base;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600564
565 /* We have homologous command set i.e pure read or write, not a mix */
566 cmd_msgid = CMD_MSGID_LEN;
567 cmd_msgid |= (msg->is_complete) ? CMD_MSGID_RESP_REQ : 0;
568 cmd_msgid |= (!msg->is_read) ? CMD_MSGID_WRITE : 0;
569
570 /* Read the send-after-prev complete flag for those already in TCS */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600571 cmd_complete = read_tcs_reg(base, RSC_DRV_CMD_WAIT_FOR_CMPL, m, 0);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600572
573 for (i = 0; i < msg->num_payload; i++) {
574 cmd = &msg->payload[i];
575 cmd_enable |= BIT(n + i);
576 cmd_complete |= cmd->complete << (n + i);
Lina Iyer7846e212017-03-22 10:35:53 -0600577 msgid = cmd_msgid;
578 msgid |= (cmd->complete) ? CMD_MSGID_RESP_REQ : 0;
Lina Iyerecb04eb2017-08-29 15:35:43 -0600579 write_tcs_reg(base, RSC_DRV_CMD_MSGID, m, n + i, msgid);
580 write_tcs_reg(base, RSC_DRV_CMD_ADDR, m, n + i, cmd->addr);
581 write_tcs_reg(base, RSC_DRV_CMD_DATA, m, n + i, cmd->data);
Lina Iyer47b708a2017-07-21 18:49:00 -0600582 log_send_msg(drv, m, n + i, msgid, cmd->addr,
Lina Iyer884981e2017-03-21 13:43:05 -0600583 cmd->data, cmd->complete, trigger);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600584 }
585
586 /* Write the send-after-prev completion bits for the batch */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600587 write_tcs_reg(base, RSC_DRV_CMD_WAIT_FOR_CMPL, m, 0, cmd_complete);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600588
589 /* Enable the new commands in TCS */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600590 cmd_enable |= read_tcs_reg(base, RSC_DRV_CMD_ENABLE, m, 0);
591 write_tcs_reg(base, RSC_DRV_CMD_ENABLE, m, 0, cmd_enable);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600592
593 if (trigger) {
Lina Iyerd3435fa2017-08-08 15:42:10 -0600594 /*
595 * HW req: Clear the DRV_CONTROL and enable TCS again
596 * While clearing ensure that the AMC mode trigger is cleared
597 * and then the mode enable is cleared.
598 */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600599 enable = read_tcs_reg(base, RSC_DRV_CONTROL, m, 0);
Lina Iyerd3435fa2017-08-08 15:42:10 -0600600 enable &= ~TCS_AMC_MODE_TRIGGER;
Lina Iyerecb04eb2017-08-29 15:35:43 -0600601 write_tcs_reg_sync(base, RSC_DRV_CONTROL, m, 0, enable);
Lina Iyerd3435fa2017-08-08 15:42:10 -0600602 enable &= ~TCS_AMC_MODE_ENABLE;
Lina Iyerecb04eb2017-08-29 15:35:43 -0600603 write_tcs_reg_sync(base, RSC_DRV_CONTROL, m, 0, enable);
Lina Iyerd3435fa2017-08-08 15:42:10 -0600604
605 /* Enable the AMC mode on the TCS and then trigger the TCS */
606 enable = TCS_AMC_MODE_ENABLE;
Lina Iyerecb04eb2017-08-29 15:35:43 -0600607 write_tcs_reg_sync(base, RSC_DRV_CONTROL, m, 0, enable);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600608 enable |= TCS_AMC_MODE_TRIGGER;
Lina Iyerecb04eb2017-08-29 15:35:43 -0600609 write_tcs_reg(base, RSC_DRV_CONTROL, m, 0, enable);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600610 }
611}
612
Lina Iyer0d81e942016-05-26 11:18:23 -0600613/**
Lina Iyerecb04eb2017-08-29 15:35:43 -0600614 * rsc_drv_is_idle: Check if any of the AMCs are busy.
Lina Iyer0d81e942016-05-26 11:18:23 -0600615 *
616 * @mbox: The mailbox controller.
617 *
618 * Returns true if the AMCs are not engaged or absent.
619 */
Lina Iyerecb04eb2017-08-29 15:35:43 -0600620static bool rsc_drv_is_idle(struct mbox_controller *mbox)
Lina Iyer0d81e942016-05-26 11:18:23 -0600621{
622 int m;
Lina Iyerecb04eb2017-08-29 15:35:43 -0600623 struct rsc_drv *drv = container_of(mbox, struct rsc_drv, mbox);
Lina Iyer0d81e942016-05-26 11:18:23 -0600624 struct tcs_mbox *tcs = get_tcs_of_type(drv, ACTIVE_TCS);
625
Lina Iyer21c17882016-09-22 11:05:51 -0600626 /* Check for WAKE TCS if there are no ACTIVE TCS */
Lina Iyer0d81e942016-05-26 11:18:23 -0600627 if (IS_ERR(tcs))
Lina Iyer21c17882016-09-22 11:05:51 -0600628 tcs = get_tcs_of_type(drv, WAKE_TCS);
Lina Iyer0d81e942016-05-26 11:18:23 -0600629
630 for (m = tcs->tcs_offset; m < tcs->tcs_offset + tcs->num_tcs; m++)
Lina Iyerb68814f2017-04-14 12:49:07 -0600631 if (!tcs_is_free(drv, m))
Lina Iyer0d81e942016-05-26 11:18:23 -0600632 return false;
633
634 return true;
635}
636
Lina Iyerecb04eb2017-08-29 15:35:43 -0600637static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_mbox *tcs,
Lina Iyer88a8fda2016-04-01 08:23:31 -0600638 struct tcs_mbox_msg *msg)
639{
Lina Iyercc114b22017-04-26 14:40:44 -0600640 u32 curr_enabled, addr;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600641 int i, j, k;
Lina Iyercc114b22017-04-26 14:40:44 -0600642 void __iomem *base = drv->reg_base;
643 int m = tcs->tcs_offset;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600644
Lina Iyercc114b22017-04-26 14:40:44 -0600645 for (i = 0; i < tcs->num_tcs; i++, m++) {
646 if (tcs_is_free(drv, m))
647 continue;
648
Lina Iyerecb04eb2017-08-29 15:35:43 -0600649 curr_enabled = read_tcs_reg(base, RSC_DRV_CMD_ENABLE, m, 0);
Lina Iyerb465ea62017-05-03 15:27:22 -0600650
651 for (j = 0; j < MAX_CMDS_PER_TCS; j++) {
652 if (!(curr_enabled & (u32)BIT(j)))
Lina Iyer88a8fda2016-04-01 08:23:31 -0600653 continue;
Lina Iyerb465ea62017-05-03 15:27:22 -0600654
Lina Iyerecb04eb2017-08-29 15:35:43 -0600655 addr = read_tcs_reg(base, RSC_DRV_CMD_ADDR, m, j);
Lina Iyercc114b22017-04-26 14:40:44 -0600656 for (k = 0; k < msg->num_payload; k++) {
657 if (addr == msg->payload[k].addr)
658 return -EBUSY;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600659 }
660 }
Lina Iyercc114b22017-04-26 14:40:44 -0600661 }
662
663 return 0;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600664}
665
666static int find_free_tcs(struct tcs_mbox *tcs)
667{
Lina Iyercc114b22017-04-26 14:40:44 -0600668 int slot = -EBUSY;
669 int m = 0;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600670
671 /* Loop until we find a free AMC */
Lina Iyercc114b22017-04-26 14:40:44 -0600672 for (m = 0; m < tcs->num_tcs; m++) {
Lina Iyerb68814f2017-04-14 12:49:07 -0600673 if (tcs_is_free(tcs->drv, tcs->tcs_offset + m)) {
Lina Iyer88a8fda2016-04-01 08:23:31 -0600674 slot = m * tcs->ncpt;
675 break;
676 }
Lina Iyercc114b22017-04-26 14:40:44 -0600677 }
Lina Iyer88a8fda2016-04-01 08:23:31 -0600678
679 return slot;
680}
681
682static int find_match(struct tcs_mbox *tcs, struct tcs_cmd *cmd, int len)
683{
684 bool found = false;
685 int i = 0, j;
686
687 /* Check for already cached commands */
688 while ((i = find_next_bit(tcs->slots, MAX_TCS_SLOTS, i)) <
689 MAX_TCS_SLOTS) {
690 if (tcs->cmd_addr[i] != cmd[0].addr) {
691 i++;
692 continue;
693 }
694 /* sanity check to ensure the seq is same */
695 for (j = 1; j < len; j++) {
696 WARN((tcs->cmd_addr[i + j] != cmd[j].addr),
697 "Message does not match previous sequence.\n");
698 return -EINVAL;
699 }
700 found = true;
701 break;
702 }
703
704 return found ? i : -1;
705}
706
707static int find_slots(struct tcs_mbox *tcs, struct tcs_mbox_msg *msg)
708{
709 int slot;
710 int n = 0;
711
712 /* For active requests find the first free AMC. */
Lina Iyerfd7e0c22017-07-14 18:08:46 -0600713 if (msg->state == RPMH_ACTIVE_ONLY_STATE ||
714 msg->state == RPMH_AWAKE_STATE)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600715 return find_free_tcs(tcs);
716
717 /* Find if we already have the msg in our TCS */
718 slot = find_match(tcs, msg->payload, msg->num_payload);
719 if (slot >= 0)
720 return slot;
721
722 /* Do over, until we can fit the full payload in a TCS */
723 do {
724 slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS,
725 n, msg->num_payload, 0);
726 if (slot == MAX_TCS_SLOTS)
727 break;
728 n += tcs->ncpt;
729 } while (slot + msg->num_payload - 1 >= n);
730
731 return (slot != MAX_TCS_SLOTS) ? slot : -ENOMEM;
732}
733
Lina Iyer88a8fda2016-04-01 08:23:31 -0600734static int tcs_mbox_write(struct mbox_chan *chan, struct tcs_mbox_msg *msg,
735 bool trigger)
736{
Lina Iyerecb04eb2017-08-29 15:35:43 -0600737 struct rsc_drv *drv = container_of(chan->mbox, struct rsc_drv, mbox);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600738 int d = drv->drv_id;
739 struct tcs_mbox *tcs;
Lina Iyercc114b22017-04-26 14:40:44 -0600740 int i, slot, offset, m, n, ret;
Channagoud Kadabi075db3b2017-03-16 14:26:17 -0700741 struct tcs_response *resp = NULL;
Lina Iyercc114b22017-04-26 14:40:44 -0600742 unsigned long flags;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600743
744 tcs = get_tcs_for_msg(drv, msg);
745 if (IS_ERR(tcs))
746 return PTR_ERR(tcs);
747
Lina Iyer00328f12017-05-30 14:08:49 -0600748 if (trigger) {
Lina Iyerc8712ca2017-04-20 00:18:49 -0600749 resp = setup_response(drv, msg, chan, TCS_M_INIT, 0);
Lina Iyer00328f12017-05-30 14:08:49 -0600750 if (IS_ERR_OR_NULL(resp))
751 return -EBUSY;
752 }
Lina Iyerc8712ca2017-04-20 00:18:49 -0600753
Lina Iyer88a8fda2016-04-01 08:23:31 -0600754 /* Identify the sequential slots that we can write to */
Lina Iyercc114b22017-04-26 14:40:44 -0600755 spin_lock_irqsave(&tcs->tcs_lock, flags);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600756 slot = find_slots(tcs, msg);
757 if (slot < 0) {
Lina Iyercc114b22017-04-26 14:40:44 -0600758 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600759 if (resp)
760 free_response(resp);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600761 return slot;
762 }
Lina Iyerc8712ca2017-04-20 00:18:49 -0600763
Lina Iyer14424652017-06-03 15:55:25 -0600764 /* Figure out the TCS-m and CMD-n to write to */
765 offset = slot / tcs->ncpt;
766 m = offset + tcs->tcs_offset;
767 n = slot % tcs->ncpt;
768
Lina Iyercc114b22017-04-26 14:40:44 -0600769 if (trigger) {
Lina Iyer14424652017-06-03 15:55:25 -0600770 /* Block, if we have an address from the msg in flight */
Lina Iyercc114b22017-04-26 14:40:44 -0600771 ret = check_for_req_inflight(drv, tcs, msg);
772 if (ret) {
773 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
Lina Iyer00328f12017-05-30 14:08:49 -0600774 if (resp)
775 free_response(resp);
Lina Iyercc114b22017-04-26 14:40:44 -0600776 return ret;
777 }
Lina Iyercc114b22017-04-26 14:40:44 -0600778
Lina Iyerc8712ca2017-04-20 00:18:49 -0600779 resp->m = m;
780 /* Mark the TCS as busy */
781 atomic_set(&drv->tcs_in_use[m], 1);
Lina Iyer088ccec2017-04-24 20:18:48 -0600782 atomic_inc(&drv->tcs_send_count[m]);
Lina Iyere0ef5212017-04-28 09:55:32 -0600783 /* Enable interrupt for active votes through wake TCS */
784 if (tcs->type != ACTIVE_TCS)
785 enable_tcs_irq(drv, m, true);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600786 drv->tcs_last_sent_ts[m] = arch_counter_get_cntvct();
Lina Iyer14424652017-06-03 15:55:25 -0600787 } else {
788 /* Mark the slots as in-use, before we unlock */
789 if (tcs->type == SLEEP_TCS || tcs->type == WAKE_TCS)
790 bitmap_set(tcs->slots, slot, msg->num_payload);
791
792 /* Copy the addresses of the resources over to the slots */
793 for (i = 0; tcs->cmd_addr && i < msg->num_payload; i++)
794 tcs->cmd_addr[slot + i] = msg->payload[i].addr;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600795 }
796
797 /* Write to the TCS or AMC */
Lina Iyer884981e2017-03-21 13:43:05 -0600798 __tcs_buffer_write(drv, d, m, n, msg, trigger);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600799
Lina Iyercc114b22017-04-26 14:40:44 -0600800 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600801
802 return 0;
803}
804
Lina Iyer7846e212017-03-22 10:35:53 -0600805static void __tcs_buffer_invalidate(void __iomem *base, int m)
806{
Lina Iyerecb04eb2017-08-29 15:35:43 -0600807 write_tcs_reg(base, RSC_DRV_CMD_ENABLE, m, 0, 0);
Lina Iyer7846e212017-03-22 10:35:53 -0600808}
809
810static int tcs_mbox_invalidate(struct mbox_chan *chan)
811{
Lina Iyerecb04eb2017-08-29 15:35:43 -0600812 struct rsc_drv *drv = container_of(chan->mbox, struct rsc_drv, mbox);
Lina Iyer7846e212017-03-22 10:35:53 -0600813 struct tcs_mbox *tcs;
814 int m, i;
815 int inv_types[] = { WAKE_TCS, SLEEP_TCS };
816 int type = 0;
Lina Iyercc114b22017-04-26 14:40:44 -0600817 unsigned long flags;
Lina Iyer7846e212017-03-22 10:35:53 -0600818
819 do {
820 tcs = get_tcs_of_type(drv, inv_types[type]);
821 if (IS_ERR(tcs))
822 return PTR_ERR(tcs);
823
Lina Iyercc114b22017-04-26 14:40:44 -0600824 spin_lock_irqsave(&tcs->tcs_lock, flags);
Lina Iyer7846e212017-03-22 10:35:53 -0600825 for (i = 0; i < tcs->num_tcs; i++) {
826 m = i + tcs->tcs_offset;
Lina Iyerfd7e0c22017-07-14 18:08:46 -0600827 if (!tcs_is_free(drv, m)) {
828 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
829 return -EBUSY;
830 }
Lina Iyer7846e212017-03-22 10:35:53 -0600831 __tcs_buffer_invalidate(drv->reg_base, m);
Lina Iyer7846e212017-03-22 10:35:53 -0600832 }
833 /* Mark the TCS as free */
834 bitmap_zero(tcs->slots, MAX_TCS_SLOTS);
Lina Iyercc114b22017-04-26 14:40:44 -0600835 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
Lina Iyer7846e212017-03-22 10:35:53 -0600836 } while (++type < ARRAY_SIZE(inv_types));
837
838 return 0;
839}
840
Lina Iyerecb04eb2017-08-29 15:35:43 -0600841static void print_tcs_regs(struct rsc_drv *drv, int m)
Lina Iyer035ddd32017-05-30 22:11:53 -0600842{
843 int n;
844 struct tcs_mbox *tcs = get_tcs_from_index(drv, m);
845 void __iomem *base = drv->reg_base;
Lina Iyerd9abfa62017-07-27 17:23:05 -0600846 u32 enable, addr, data, msgid, sts, irq_sts;
Lina Iyer035ddd32017-05-30 22:11:53 -0600847
848 if (!tcs || tcs_is_free(drv, m))
849 return;
850
Lina Iyerecb04eb2017-08-29 15:35:43 -0600851 enable = read_tcs_reg(base, RSC_DRV_CMD_ENABLE, m, 0);
Lina Iyer035ddd32017-05-30 22:11:53 -0600852 if (!enable)
853 return;
854
Lina Iyerd9abfa62017-07-27 17:23:05 -0600855 pr_warn("RSC:%s\n", drv->name);
856
Lina Iyerecb04eb2017-08-29 15:35:43 -0600857 sts = read_tcs_reg(base, RSC_DRV_STATUS, m, 0);
858 data = read_tcs_reg(base, RSC_DRV_CONTROL, m, 0);
859 irq_sts = read_tcs_reg(base, RSC_DRV_IRQ_STATUS, 0, 0);
Lina Iyerd9abfa62017-07-27 17:23:05 -0600860 pr_warn("TCS=%d [ctrlr-sts:%s amc-mode:0x%x irq-sts:%s]\n",
861 m, sts ? "IDLE" : "BUSY", data,
862 (irq_sts & BIT(m)) ? "COMPLETED" : "PENDING");
863
Lina Iyer035ddd32017-05-30 22:11:53 -0600864 for (n = 0; n < tcs->ncpt; n++) {
865 if (!(enable & BIT(n)))
866 continue;
Lina Iyerecb04eb2017-08-29 15:35:43 -0600867 addr = read_tcs_reg(base, RSC_DRV_CMD_ADDR, m, n);
868 data = read_tcs_reg(base, RSC_DRV_CMD_DATA, m, n);
869 msgid = read_tcs_reg(base, RSC_DRV_CMD_MSGID, m, n);
870 sts = read_tcs_reg(base, RSC_DRV_CMD_STATUS, m, n);
Lina Iyerd9abfa62017-07-27 17:23:05 -0600871 pr_warn("\tCMD=%d [addr=0x%x data=0x%x hdr=0x%x sts=0x%x]\n",
872 n, addr, data, msgid, sts);
Lina Iyer035ddd32017-05-30 22:11:53 -0600873 }
874}
875
Lina Iyerecb04eb2017-08-29 15:35:43 -0600876static void dump_tcs_stats(struct rsc_drv *drv)
Lina Iyer51bc14d2017-04-28 13:56:12 -0600877{
878 int i;
879 unsigned long long curr = arch_counter_get_cntvct();
880
881 for (i = 0; i < drv->num_tcs; i++) {
882 if (!atomic_read(&drv->tcs_in_use[i]))
883 continue;
Lina Iyere2f42722017-07-13 12:37:50 -0600884 pr_warn("Time: %llu: TCS-%d:\n\tReq Sent:%d Last Sent:%llu\n\tResp Recv:%d Last Recvd:%llu\n",
Lina Iyer51bc14d2017-04-28 13:56:12 -0600885 curr, i,
886 atomic_read(&drv->tcs_send_count[i]),
887 drv->tcs_last_sent_ts[i],
888 atomic_read(&drv->tcs_irq_count[i]),
889 drv->tcs_last_recv_ts[i]);
Lina Iyer035ddd32017-05-30 22:11:53 -0600890 print_tcs_regs(drv, i);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600891 print_response(drv, i);
892 }
893}
894
Lina Iyere2f42722017-07-13 12:37:50 -0600895static void chan_debug(struct mbox_chan *chan)
896{
Lina Iyerecb04eb2017-08-29 15:35:43 -0600897 struct rsc_drv *drv = container_of(chan->mbox, struct rsc_drv, mbox);
Lina Iyere2f42722017-07-13 12:37:50 -0600898
899 dump_tcs_stats(drv);
900}
901
Lina Iyer88a8fda2016-04-01 08:23:31 -0600902/**
903 * chan_tcs_write: Validate the incoming message and write to the
904 * appropriate TCS block.
905 *
906 * @chan: the MBOX channel
907 * @data: the tcs_mbox_msg*
908 *
909 * Returns a negative error for invalid message structure and invalid
910 * message combination, -EBUSY if there is an other active request for
911 * the channel in process, otherwise bubbles up internal error.
912 */
913static int chan_tcs_write(struct mbox_chan *chan, void *data)
914{
Lina Iyerecb04eb2017-08-29 15:35:43 -0600915 struct rsc_drv *drv = container_of(chan->mbox, struct rsc_drv, mbox);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600916 struct tcs_mbox_msg *msg = data;
917 const struct device *dev = chan->cl->dev;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600918 int ret = 0;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600919
920 if (!msg) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600921 dev_err(dev, "Payload error\n");
Lina Iyer51bc14d2017-04-28 13:56:12 -0600922 ret = -EINVAL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600923 goto tx_fail;
924 }
925
Lina Iyerfd7e0c22017-07-14 18:08:46 -0600926 if (!msg->payload || !msg->num_payload ||
927 msg->num_payload > MAX_RPMH_PAYLOAD) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600928 dev_err(dev, "Payload error\n");
Lina Iyer51bc14d2017-04-28 13:56:12 -0600929 ret = -EINVAL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600930 goto tx_fail;
931 }
932
933 if (msg->invalidate || msg->is_control) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600934 dev_err(dev, "Incorrect API\n");
Lina Iyer51bc14d2017-04-28 13:56:12 -0600935 ret = -EINVAL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600936 goto tx_fail;
937 }
938
Lina Iyer21c17882016-09-22 11:05:51 -0600939 if (msg->state != RPMH_ACTIVE_ONLY_STATE &&
940 msg->state != RPMH_AWAKE_STATE) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600941 dev_err(dev, "Incorrect API\n");
Lina Iyer51bc14d2017-04-28 13:56:12 -0600942 ret = -EINVAL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600943 goto tx_fail;
944 }
945
946 /* Read requests should always be single */
947 if (msg->is_read && msg->num_payload > 1) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600948 dev_err(dev, "Incorrect read request\n");
Lina Iyer51bc14d2017-04-28 13:56:12 -0600949 ret = -EINVAL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600950 goto tx_fail;
951 }
952
Lina Iyer7846e212017-03-22 10:35:53 -0600953 /*
954 * Since we are re-purposing the wake TCS, invalidate previous
955 * contents to avoid confusion.
956 */
Lina Iyerfd7e0c22017-07-14 18:08:46 -0600957 if (msg->state == RPMH_AWAKE_STATE) {
958 ret = tcs_mbox_invalidate(chan);
959 if (ret)
960 goto tx_fail;
961 }
Lina Iyer7846e212017-03-22 10:35:53 -0600962
Lina Iyer88a8fda2016-04-01 08:23:31 -0600963 /* Post the message to the TCS and trigger */
Lina Iyer64834df2017-06-09 11:36:46 -0600964 ret = tcs_mbox_write(chan, msg, true);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600965
966tx_fail:
Lina Iyer51bc14d2017-04-28 13:56:12 -0600967 /* If there was an error in the request, schedule a response */
968 if (ret < 0 && ret != -EBUSY) {
Lina Iyerc8712ca2017-04-20 00:18:49 -0600969 struct tcs_response *resp = setup_response(
970 drv, msg, chan, TCS_M_INIT, ret);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600971
972 dev_err(dev, "Error sending RPMH message %d\n", ret);
Lina Iyerfd7e0c22017-07-14 18:08:46 -0600973 if (!IS_ERR(resp))
Lina Iyer00328f12017-05-30 14:08:49 -0600974 send_tcs_response(resp);
Lina Iyerfd7e0c22017-07-14 18:08:46 -0600975 else
976 dev_err(dev, "No response object %ld\n", PTR_ERR(resp));
Lina Iyer51bc14d2017-04-28 13:56:12 -0600977 ret = 0;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600978 }
979
Lina Iyer51bc14d2017-04-28 13:56:12 -0600980 /* If we were just busy waiting for TCS, dump the state and return */
981 if (ret == -EBUSY) {
Lina Iyer94972202017-06-28 15:37:43 -0600982 pr_info_ratelimited("TCS Busy, retrying RPMH message send\n");
Lina Iyer348b2712017-05-30 22:13:59 -0600983 ret = -EAGAIN;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600984 }
985
986 return ret;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600987}
988
Lina Iyerecb04eb2017-08-29 15:35:43 -0600989static void __tcs_write_hidden(struct rsc_drv *drv, int d,
Lina Iyer884981e2017-03-21 13:43:05 -0600990 struct tcs_mbox_msg *msg)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600991{
992 int i;
Lina Iyer884981e2017-03-21 13:43:05 -0600993 void __iomem *addr = drv->base + TCS_HIDDEN_CMD0_DRV_DATA;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600994
Lina Iyer88a8fda2016-04-01 08:23:31 -0600995 for (i = 0; i < msg->num_payload; i++) {
996 /* Only data is write capable */
Lina Iyerd93fbce2017-03-01 09:07:20 -0700997 writel_relaxed(cpu_to_le32(msg->payload[i].data), addr);
Lina Iyer47b708a2017-07-21 18:49:00 -0600998 log_rpmh_control_msg(drv, msg->payload[i].data);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600999 addr += TCS_HIDDEN_CMD_SHIFT;
1000 }
1001}
1002
1003static int tcs_control_write(struct mbox_chan *chan, struct tcs_mbox_msg *msg)
1004{
1005 const struct device *dev = chan->cl->dev;
Lina Iyerecb04eb2017-08-29 15:35:43 -06001006 struct rsc_drv *drv = container_of(chan->mbox, struct rsc_drv, mbox);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001007 struct tcs_mbox *tcs;
Lina Iyercc114b22017-04-26 14:40:44 -06001008 unsigned long flags;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001009
1010 tcs = get_tcs_of_type(drv, CONTROL_TCS);
1011 if (IS_ERR(tcs))
1012 return PTR_ERR(tcs);
1013
1014 if (msg->num_payload != tcs->ncpt) {
Lina Iyer5ddea732017-05-09 14:25:33 -06001015 dev_err(dev, "Request must fit the control TCS size\n");
Lina Iyer88a8fda2016-04-01 08:23:31 -06001016 return -EINVAL;
1017 }
1018
Lina Iyercc114b22017-04-26 14:40:44 -06001019 spin_lock_irqsave(&tcs->tcs_lock, flags);
Lina Iyer884981e2017-03-21 13:43:05 -06001020 __tcs_write_hidden(tcs->drv, drv->drv_id, msg);
Lina Iyercc114b22017-04-26 14:40:44 -06001021 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001022
1023 return 0;
1024}
1025
1026/**
1027 * chan_tcs_ctrl_write: Write message to the controller, no ACK sent.
1028 *
1029 * @chan: the MBOX channel
1030 * @data: the tcs_mbox_msg*
1031 */
1032static int chan_tcs_ctrl_write(struct mbox_chan *chan, void *data)
1033{
1034 struct tcs_mbox_msg *msg = data;
1035 const struct device *dev = chan->cl->dev;
1036 int ret = -EINVAL;
1037
1038 if (!msg) {
Lina Iyer5ddea732017-05-09 14:25:33 -06001039 dev_err(dev, "Payload error\n");
Lina Iyer88a8fda2016-04-01 08:23:31 -06001040 goto tx_done;
1041 }
1042
Lina Iyerfd7e0c22017-07-14 18:08:46 -06001043 if (!msg->payload || (!msg->num_payload && !msg->invalidate) ||
1044 msg->num_payload > MAX_RPMH_PAYLOAD) {
Lina Iyer5ddea732017-05-09 14:25:33 -06001045 dev_err(dev, "Payload error\n");
Lina Iyer88a8fda2016-04-01 08:23:31 -06001046 goto tx_done;
1047 }
1048
1049 /* Invalidate sleep/wake TCS */
1050 if (msg->invalidate) {
1051 ret = tcs_mbox_invalidate(chan);
1052 goto tx_done;
1053 }
1054
1055 /* Control slots are unique. They carry specific data. */
1056 if (msg->is_control) {
1057 ret = tcs_control_write(chan, msg);
1058 goto tx_done;
1059 }
1060
Lina Iyer88a8fda2016-04-01 08:23:31 -06001061 /* Post the message to the TCS without trigger */
1062 ret = tcs_mbox_write(chan, msg, false);
1063
1064tx_done:
1065 return ret;
1066}
1067
1068static int chan_init(struct mbox_chan *chan)
1069{
1070 return 0;
1071}
1072
1073static void chan_shutdown(struct mbox_chan *chan)
1074{ }
1075
1076static const struct mbox_chan_ops mbox_ops = {
1077 .send_data = chan_tcs_write,
Lina Iyer3d9bd812017-09-06 11:34:01 -06001078 .write_controller_data = chan_tcs_ctrl_write,
Lina Iyer88a8fda2016-04-01 08:23:31 -06001079 .startup = chan_init,
1080 .shutdown = chan_shutdown,
1081};
1082
1083static struct mbox_chan *of_tcs_mbox_xlate(struct mbox_controller *mbox,
1084 const struct of_phandle_args *sp)
1085{
Lina Iyerecb04eb2017-08-29 15:35:43 -06001086 struct rsc_drv *drv = container_of(mbox, struct rsc_drv, mbox);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001087 struct mbox_chan *chan;
1088
1089 if (drv->num_assigned >= mbox->num_chans) {
1090 pr_err("TCS-Mbox out of channel memory\n");
1091 return ERR_PTR(-ENOMEM);
1092 }
1093
1094 chan = &mbox->chans[drv->num_assigned++];
Lina Iyer14424652017-06-03 15:55:25 -06001095 chan->con_priv = drv;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001096
1097 return chan;
1098}
1099
Lina Iyerecb04eb2017-08-29 15:35:43 -06001100static int rsc_drv_probe(struct platform_device *pdev)
Lina Iyer88a8fda2016-04-01 08:23:31 -06001101{
1102 struct device_node *dn = pdev->dev.of_node;
1103 struct device_node *np;
Lina Iyerecb04eb2017-08-29 15:35:43 -06001104 struct rsc_drv *drv;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001105 struct mbox_chan *chans;
1106 struct tcs_mbox *tcs;
1107 struct of_phandle_args p;
1108 int irq;
1109 u32 val[8] = { 0 };
1110 int num_chans = 0;
1111 int st = 0;
1112 int i, j, ret, nelem;
1113 u32 config, max_tcs, ncpt;
Lina Iyer73101422017-02-16 14:09:25 -07001114 int tcs_type_count[TCS_TYPE_NR] = { 0 };
1115 struct resource *res;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001116
1117 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
1118 if (!drv)
1119 return -ENOMEM;
1120
Lina Iyer73101422017-02-16 14:09:25 -07001121 ret = of_property_read_u32(dn, "qcom,drv-id", &drv->drv_id);
1122 if (ret)
1123 return ret;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001124
Lina Iyer73101422017-02-16 14:09:25 -07001125 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1126 if (!res)
1127 return -EINVAL;
1128 drv->base = devm_ioremap_resource(&pdev->dev, res);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001129 if (IS_ERR(drv->base))
1130 return PTR_ERR(drv->base);
1131
Lina Iyer73101422017-02-16 14:09:25 -07001132 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
1133 if (!res)
1134 return -EINVAL;
1135 drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001136 if (IS_ERR(drv->reg_base))
1137 return PTR_ERR(drv->reg_base);
1138
1139 config = read_drv_config(drv->base);
1140 max_tcs = config & (DRV_NUM_TCS_MASK <<
1141 (DRV_NUM_TCS_SHIFT * drv->drv_id));
1142 max_tcs = max_tcs >> (DRV_NUM_TCS_SHIFT * drv->drv_id);
1143 ncpt = config & (DRV_NCPT_MASK << DRV_NCPT_SHIFT);
1144 ncpt = ncpt >> DRV_NCPT_SHIFT;
1145
1146 nelem = of_property_count_elems_of_size(dn, "qcom,tcs-config",
1147 sizeof(u32));
1148 if (!nelem || (nelem % 2) || (nelem > 2 * TCS_TYPE_NR))
1149 return -EINVAL;
1150
1151 ret = of_property_read_u32_array(dn, "qcom,tcs-config", val, nelem);
1152 if (ret)
1153 return ret;
1154
Lina Iyer73101422017-02-16 14:09:25 -07001155 /* Ensure we have exactly not more than one of each type in DT */
Lina Iyer88a8fda2016-04-01 08:23:31 -06001156 for (i = 0; i < (nelem / 2); i++) {
Lina Iyer73101422017-02-16 14:09:25 -07001157 if (val[2 * i] >= TCS_TYPE_NR)
1158 return -EINVAL;
1159 tcs_type_count[val[2 * i]]++;
1160 if (tcs_type_count[val[2 * i]] > 1)
1161 return -EINVAL;
1162 }
1163
1164 /* Ensure we have each type specified in DT */
1165 for (i = 0; i < ARRAY_SIZE(tcs_type_count); i++)
1166 if (!tcs_type_count[i])
1167 return -EINVAL;
1168
1169 for (i = 0; i < (nelem / 2); i++) {
1170 tcs = &drv->tcs[val[2 * i]];
Lina Iyer88a8fda2016-04-01 08:23:31 -06001171 tcs->drv = drv;
1172 tcs->type = val[2 * i];
1173 tcs->num_tcs = val[2 * i + 1];
1174 tcs->ncpt = (tcs->type == CONTROL_TCS) ? TCS_HIDDEN_MAX_SLOTS
1175 : ncpt;
1176 spin_lock_init(&tcs->tcs_lock);
1177
1178 if (tcs->num_tcs <= 0 || tcs->type == CONTROL_TCS)
1179 continue;
1180
Lina Iyerac6e850d2017-09-05 14:11:18 -06001181 if (tcs->num_tcs > MAX_TCS_PER_TYPE ||
1182 st + tcs->num_tcs > max_tcs ||
1183 st + tcs->num_tcs >=
1184 BITS_PER_BYTE * sizeof(tcs->tcs_mask))
Lina Iyer88a8fda2016-04-01 08:23:31 -06001185 return -EINVAL;
1186
1187 tcs->tcs_mask = ((1 << tcs->num_tcs) - 1) << st;
1188 tcs->tcs_offset = st;
1189 st += tcs->num_tcs;
1190
1191 tcs->cmd_addr = devm_kzalloc(&pdev->dev, sizeof(u32) *
1192 tcs->num_tcs * tcs->ncpt, GFP_KERNEL);
1193 if (!tcs->cmd_addr)
1194 return -ENOMEM;
1195
Lina Iyer88a8fda2016-04-01 08:23:31 -06001196 }
1197
1198 /* Allocate only that many channels specified in DT for our MBOX */
1199 for_each_node_with_property(np, "mboxes") {
1200 if (!of_device_is_available(np))
1201 continue;
1202 i = of_count_phandle_with_args(np, "mboxes", "#mbox-cells");
1203 for (j = 0; j < i; j++) {
1204 ret = of_parse_phandle_with_args(np, "mboxes",
1205 "#mbox-cells", j, &p);
Lina Iyerfd7e0c22017-07-14 18:08:46 -06001206 of_node_put(p.np);
1207 if (!ret && p.np == pdev->dev.of_node) {
1208 num_chans++;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001209 break;
Lina Iyerfd7e0c22017-07-14 18:08:46 -06001210 }
Lina Iyer88a8fda2016-04-01 08:23:31 -06001211 }
Lina Iyer88a8fda2016-04-01 08:23:31 -06001212 }
1213
1214 if (!num_chans) {
1215 pr_err("%s: No clients for controller (%s)\n", __func__,
1216 dn->full_name);
1217 return -ENODEV;
1218 }
1219
1220 chans = devm_kzalloc(&pdev->dev, num_chans * sizeof(*chans),
1221 GFP_KERNEL);
1222 if (!chans)
1223 return -ENOMEM;
1224
1225 for (i = 0; i < num_chans; i++) {
1226 chans[i].mbox = &drv->mbox;
1227 chans[i].txdone_method = TXDONE_BY_IRQ;
1228 }
1229
1230 drv->mbox.dev = &pdev->dev;
1231 drv->mbox.ops = &mbox_ops;
1232 drv->mbox.chans = chans;
1233 drv->mbox.num_chans = num_chans;
1234 drv->mbox.txdone_irq = true;
1235 drv->mbox.of_xlate = of_tcs_mbox_xlate;
Lina Iyerecb04eb2017-08-29 15:35:43 -06001236 drv->mbox.is_idle = rsc_drv_is_idle;
Lina Iyere2f42722017-07-13 12:37:50 -06001237 drv->mbox.debug = chan_debug;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001238 drv->num_tcs = st;
1239 drv->pdev = pdev;
Lina Iyer00328f12017-05-30 14:08:49 -06001240 INIT_LIST_HEAD(&drv->response_pending);
1241 spin_lock_init(&drv->drv_lock);
1242 tasklet_init(&drv->tasklet, tcs_notify_tx_done, (unsigned long)drv);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001243
Lina Iyer884981e2017-03-21 13:43:05 -06001244 drv->name = of_get_property(pdev->dev.of_node, "label", NULL);
1245 if (!drv->name)
1246 drv->name = dev_name(&pdev->dev);
1247
Lina Iyer88a8fda2016-04-01 08:23:31 -06001248 ret = tcs_response_pool_init(drv);
1249 if (ret)
1250 return ret;
1251
1252 irq = of_irq_get(dn, 0);
1253 if (irq < 0)
1254 return irq;
1255
Lina Iyerafcdc182017-04-19 18:31:04 -06001256 ret = devm_request_irq(&pdev->dev, irq, tcs_irq_handler,
Lina Iyer9837a422017-04-26 10:06:21 -06001257 IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND,
Lina Iyer81981ec2017-06-26 17:08:45 -06001258 drv->name, drv);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001259 if (ret)
1260 return ret;
1261
Lina Iyere0ef5212017-04-28 09:55:32 -06001262 /* Enable interrupts for AMC TCS */
Lina Iyerecb04eb2017-08-29 15:35:43 -06001263 write_tcs_reg(drv->reg_base, RSC_DRV_IRQ_ENABLE, 0, 0,
Lina Iyere0ef5212017-04-28 09:55:32 -06001264 drv->tcs[ACTIVE_TCS].tcs_mask);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001265
Lina Iyerb68814f2017-04-14 12:49:07 -06001266 for (i = 0; i < ARRAY_SIZE(drv->tcs_in_use); i++)
1267 atomic_set(&drv->tcs_in_use[i], 0);
1268
Lina Iyerecb04eb2017-08-29 15:35:43 -06001269 drv->ipc_log_ctx = ipc_log_context_create(RSC_DRV_IPC_LOG_SIZE,
Lina Iyer47b708a2017-07-21 18:49:00 -06001270 drv->name, 0);
1271
Lina Iyer88a8fda2016-04-01 08:23:31 -06001272 ret = mbox_controller_register(&drv->mbox);
1273 if (ret)
1274 return ret;
1275
1276 pr_debug("Mailbox controller (%s, drv=%d) registered\n",
1277 dn->full_name, drv->drv_id);
1278
1279 return 0;
1280}
1281
Lina Iyerecb04eb2017-08-29 15:35:43 -06001282static const struct of_device_id rsc_drv_match[] = {
Lina Iyer88a8fda2016-04-01 08:23:31 -06001283 { .compatible = "qcom,tcs-drv", },
1284 { }
1285};
1286
Lina Iyerecb04eb2017-08-29 15:35:43 -06001287static struct platform_driver rpmh_mbox_driver = {
1288 .probe = rsc_drv_probe,
Lina Iyer88a8fda2016-04-01 08:23:31 -06001289 .driver = {
1290 .name = KBUILD_MODNAME,
Lina Iyerecb04eb2017-08-29 15:35:43 -06001291 .of_match_table = rsc_drv_match,
Lina Iyer88a8fda2016-04-01 08:23:31 -06001292 },
1293};
1294
Lina Iyerecb04eb2017-08-29 15:35:43 -06001295static int __init rpmh_mbox_driver_init(void)
Lina Iyer88a8fda2016-04-01 08:23:31 -06001296{
Lina Iyerecb04eb2017-08-29 15:35:43 -06001297 return platform_driver_register(&rpmh_mbox_driver);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001298}
Lina Iyerecb04eb2017-08-29 15:35:43 -06001299arch_initcall(rpmh_mbox_driver_init);