blob: 15d080586ec4f5543bb6b025bfe475e8b5919d84 [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
14#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
15
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>
20#include <linux/jiffies.h>
21#include <linux/kernel.h>
22#include <linux/list.h>
23#include <linux/mailbox_client.h> /* For dev_err */
24#include <linux/mailbox_controller.h>
25#include <linux/module.h>
26#include <linux/of.h>
27#include <linux/of_address.h>
28#include <linux/of_irq.h>
29#include <linux/platform_device.h>
30#include <linux/spinlock.h>
Lina Iyer88a8fda2016-04-01 08:23:31 -060031
32#include <asm-generic/io.h>
33
34#include <soc/qcom/tcs.h>
35
36#include <dt-bindings/soc/qcom,tcs-mbox.h>
37
38#include "mailbox.h"
39
Lina Iyerea921442016-05-26 15:07:48 -060040#define CREATE_TRACE_POINTS
41#include <trace/events/rpmh.h>
42
Lina Iyer88a8fda2016-04-01 08:23:31 -060043#define MAX_CMDS_PER_TCS 16
44#define MAX_TCS_PER_TYPE 3
45#define MAX_TCS_SLOTS (MAX_CMDS_PER_TCS * MAX_TCS_PER_TYPE)
46
47#define TCS_DRV_TCS_OFFSET 672
48#define TCS_DRV_CMD_OFFSET 20
49
50/* DRV Configuration Information Register */
51#define DRV_PRNT_CHLD_CONFIG 0x0C
52#define DRV_NUM_TCS_MASK 0x3F
53#define DRV_NUM_TCS_SHIFT 6
54#define DRV_NCPT_MASK 0x1F
55#define DRV_NCPT_SHIFT 27
56
57/* Register offsets */
58#define TCS_DRV_IRQ_ENABLE 0x00
59#define TCS_DRV_IRQ_STATUS 0x04
60#define TCS_DRV_IRQ_CLEAR 0x08
61#define TCS_DRV_CMD_WAIT_FOR_CMPL 0x10
62#define TCS_DRV_CONTROL 0x14
63#define TCS_DRV_STATUS 0x18
64#define TCS_DRV_CMD_ENABLE 0x1C
65#define TCS_DRV_CMD_MSGID 0x30
66#define TCS_DRV_CMD_ADDR 0x34
67#define TCS_DRV_CMD_DATA 0x38
68#define TCS_DRV_CMD_STATUS 0x3C
69#define TCS_DRV_CMD_RESP_DATA 0x40
70
71#define TCS_AMC_MODE_ENABLE BIT(16)
72#define TCS_AMC_MODE_TRIGGER BIT(24)
73
74/* TCS CMD register bit mask */
75#define CMD_MSGID_LEN 8
76#define CMD_MSGID_RESP_REQ BIT(8)
77#define CMD_MSGID_WRITE BIT(16)
78#define CMD_STATUS_ISSUED BIT(8)
79#define CMD_STATUS_COMPL BIT(16)
80
81/* Control/Hidden TCS */
Lina Iyerd1a6e682017-06-20 10:05:09 -060082#define TCS_HIDDEN_MAX_SLOTS 2
Lina Iyer88a8fda2016-04-01 08:23:31 -060083#define TCS_HIDDEN_CMD0_DRV_DATA 0x38
84#define TCS_HIDDEN_CMD_SHIFT 0x08
85
86#define TCS_TYPE_NR 4
Lina Iyer88a8fda2016-04-01 08:23:31 -060087#define MAX_POOL_SIZE (MAX_TCS_PER_TYPE * TCS_TYPE_NR)
Lina Iyerc8712ca2017-04-20 00:18:49 -060088#define TCS_M_INIT 0xFFFF
Lina Iyer88a8fda2016-04-01 08:23:31 -060089
90struct tcs_drv;
91
92struct tcs_response {
93 struct tcs_drv *drv;
94 struct mbox_chan *chan;
95 struct tcs_mbox_msg *msg;
96 u32 m; /* m-th TCS */
Lina Iyer88a8fda2016-04-01 08:23:31 -060097 int err;
Lina Iyerc8712ca2017-04-20 00:18:49 -060098 int idx;
99 bool in_use;
Lina Iyer00328f12017-05-30 14:08:49 -0600100 struct list_head list;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600101};
102
103struct tcs_response_pool {
Lina Iyerc8712ca2017-04-20 00:18:49 -0600104 struct tcs_response resp[MAX_POOL_SIZE];
Lina Iyer88a8fda2016-04-01 08:23:31 -0600105 spinlock_t lock;
106 DECLARE_BITMAP(avail, MAX_POOL_SIZE);
107};
108
109/* One per TCS type of a controller */
110struct tcs_mbox {
111 struct tcs_drv *drv;
112 u32 *cmd_addr;
113 int type;
114 u32 tcs_mask;
115 u32 tcs_offset;
116 int num_tcs;
117 int ncpt; /* num cmds per tcs */
118 DECLARE_BITMAP(slots, MAX_TCS_SLOTS);
119 spinlock_t tcs_lock; /* TCS type lock */
Lina Iyer88a8fda2016-04-01 08:23:31 -0600120};
121
122/* One per MBOX controller */
123struct tcs_drv {
Lina Iyer14424652017-06-03 15:55:25 -0600124 struct mbox_controller mbox;
Lina Iyer884981e2017-03-21 13:43:05 -0600125 const char *name;
Lina Iyer00328f12017-05-30 14:08:49 -0600126 void __iomem *base; /* start address of the RSC's registers */
127 void __iomem *reg_base; /* start address for DRV specific register */
Lina Iyer88a8fda2016-04-01 08:23:31 -0600128 int drv_id;
129 struct platform_device *pdev;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600130 struct tcs_mbox tcs[TCS_TYPE_NR];
131 int num_assigned;
132 int num_tcs;
Lina Iyer00328f12017-05-30 14:08:49 -0600133 struct tasklet_struct tasklet;
134 struct list_head response_pending;
135 spinlock_t drv_lock;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600136 struct tcs_response_pool *resp_pool;
Lina Iyer088ccec2017-04-24 20:18:48 -0600137 atomic_t tcs_in_use[MAX_POOL_SIZE];
Lina Iyer51bc14d2017-04-28 13:56:12 -0600138 /* Debug info */
139 u64 tcs_last_sent_ts[MAX_POOL_SIZE];
140 u64 tcs_last_recv_ts[MAX_POOL_SIZE];
Lina Iyer088ccec2017-04-24 20:18:48 -0600141 atomic_t tcs_send_count[MAX_POOL_SIZE];
142 atomic_t tcs_irq_count[MAX_POOL_SIZE];
Lina Iyer88a8fda2016-04-01 08:23:31 -0600143};
144
Lina Iyer88a8fda2016-04-01 08:23:31 -0600145static int tcs_response_pool_init(struct tcs_drv *drv)
146{
147 struct tcs_response_pool *pool;
148 int i;
149
150 pool = devm_kzalloc(&drv->pdev->dev, sizeof(*pool), GFP_KERNEL);
151 if (!pool)
152 return -ENOMEM;
153
Lina Iyer88a8fda2016-04-01 08:23:31 -0600154 for (i = 0; i < MAX_POOL_SIZE; i++) {
Lina Iyerc8712ca2017-04-20 00:18:49 -0600155 pool->resp[i].drv = drv;
156 pool->resp[i].idx = i;
157 pool->resp[i].m = TCS_M_INIT;
Lina Iyer00328f12017-05-30 14:08:49 -0600158 INIT_LIST_HEAD(&pool->resp[i].list);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600159 }
160
161 spin_lock_init(&pool->lock);
162 drv->resp_pool = pool;
163
164 return 0;
165}
166
Lina Iyerc8712ca2017-04-20 00:18:49 -0600167static struct tcs_response *setup_response(struct tcs_drv *drv,
168 struct tcs_mbox_msg *msg, struct mbox_chan *chan,
169 u32 m, int err)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600170{
171 struct tcs_response_pool *pool = drv->resp_pool;
172 struct tcs_response *resp = ERR_PTR(-ENOMEM);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600173 int pos;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600174 unsigned long flags;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600175
Lina Iyer51bc14d2017-04-28 13:56:12 -0600176 spin_lock_irqsave(&pool->lock, flags);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600177 pos = find_first_zero_bit(pool->avail, MAX_POOL_SIZE);
178 if (pos != MAX_POOL_SIZE) {
179 bitmap_set(pool->avail, pos, 1);
180 resp = &pool->resp[pos];
Lina Iyerc8712ca2017-04-20 00:18:49 -0600181 resp->chan = chan;
182 resp->msg = msg;
183 resp->m = m;
184 resp->err = err;
185 resp->in_use = false;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600186 }
Lina Iyer51bc14d2017-04-28 13:56:12 -0600187 spin_unlock_irqrestore(&pool->lock, flags);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600188
Lina Iyer00328f12017-05-30 14:08:49 -0600189 if (pos == MAX_POOL_SIZE)
190 pr_err("response pool is full\n");
191
Lina Iyer88a8fda2016-04-01 08:23:31 -0600192 return resp;
193}
194
Lina Iyerc8712ca2017-04-20 00:18:49 -0600195static void free_response(struct tcs_response *resp)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600196{
197 struct tcs_response_pool *pool = resp->drv->resp_pool;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600198 unsigned long flags;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600199
Lina Iyer51bc14d2017-04-28 13:56:12 -0600200 spin_lock_irqsave(&pool->lock, flags);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600201 resp->err = -EINVAL;
202 bitmap_clear(pool->avail, resp->idx, 1);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600203 spin_unlock_irqrestore(&pool->lock, flags);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600204}
205
Lina Iyer51bc14d2017-04-28 13:56:12 -0600206static inline struct tcs_response *get_response(struct tcs_drv *drv, u32 m,
207 bool for_use)
Lina Iyerc8712ca2017-04-20 00:18:49 -0600208{
209 struct tcs_response_pool *pool = drv->resp_pool;
210 struct tcs_response *resp = NULL;
211 int pos = 0;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600212 unsigned long flags;
Lina Iyerc8712ca2017-04-20 00:18:49 -0600213
Lina Iyer51bc14d2017-04-28 13:56:12 -0600214 spin_lock_irqsave(&pool->lock, flags);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600215 do {
216 pos = find_next_bit(pool->avail, MAX_POOL_SIZE, pos);
217 if (pos == MAX_POOL_SIZE)
218 break;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600219
Lina Iyerc8712ca2017-04-20 00:18:49 -0600220 resp = &pool->resp[pos];
221 if (resp->m == m && !resp->in_use) {
Lina Iyer51bc14d2017-04-28 13:56:12 -0600222 resp->in_use = for_use;
Lina Iyerc8712ca2017-04-20 00:18:49 -0600223 break;
224 }
225 pos++;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600226 udelay(1);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600227 } while (1);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600228 spin_unlock_irqrestore(&pool->lock, flags);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600229
230 return resp;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600231}
232
Lina Iyer51bc14d2017-04-28 13:56:12 -0600233static void print_response(struct tcs_drv *drv, int m)
234{
235 struct tcs_response *resp;
236 struct tcs_mbox_msg *msg;
237 int i;
238
239 resp = get_response(drv, m, false);
240 if (!resp)
241 return;
242
243 msg = resp->msg;
Lina Iyere2f42722017-07-13 12:37:50 -0600244 pr_warn("Response object idx=%d:\n\tfor-tcs=%d\tin-use=%d\n",
Lina Iyer51bc14d2017-04-28 13:56:12 -0600245 resp->idx, resp->m, resp->in_use);
Lina Iyere2f42722017-07-13 12:37:50 -0600246 pr_warn("Msg: state=%d\n", msg->state);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600247 for (i = 0; i < msg->num_payload; i++)
Lina Iyere2f42722017-07-13 12:37:50 -0600248 pr_warn("addr=0x%x data=0x%x complete=0x%x\n",
Lina Iyer51bc14d2017-04-28 13:56:12 -0600249 msg->payload[i].addr,
250 msg->payload[i].data,
251 msg->payload[i].complete);
252}
253
Lina Iyer88a8fda2016-04-01 08:23:31 -0600254static inline u32 read_drv_config(void __iomem *base)
255{
256 return le32_to_cpu(readl_relaxed(base + DRV_PRNT_CHLD_CONFIG));
257}
258
259static inline u32 read_tcs_reg(void __iomem *base, int reg, int m, int n)
260{
261 return le32_to_cpu(readl_relaxed(base + reg +
262 TCS_DRV_TCS_OFFSET * m + TCS_DRV_CMD_OFFSET * n));
263}
264
265static inline void write_tcs_reg(void __iomem *base, int reg, int m, int n,
266 u32 data)
267{
268 writel_relaxed(cpu_to_le32(data), base + reg +
269 TCS_DRV_TCS_OFFSET * m + TCS_DRV_CMD_OFFSET * n);
270}
271
272static inline void write_tcs_reg_sync(void __iomem *base, int reg, int m, int n,
273 u32 data)
274{
275 do {
276 write_tcs_reg(base, reg, m, n, data);
277 if (data == read_tcs_reg(base, reg, m, n))
278 break;
Lina Iyer8bb7d5a2017-04-20 09:50:41 -0600279 udelay(1);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600280 } while (1);
281}
282
Lina Iyerb68814f2017-04-14 12:49:07 -0600283static inline bool tcs_is_free(struct tcs_drv *drv, int m)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600284{
Lina Iyerb68814f2017-04-14 12:49:07 -0600285 void __iomem *base = drv->reg_base;
286
287 return read_tcs_reg(base, TCS_DRV_STATUS, m, 0) &&
288 !atomic_read(&drv->tcs_in_use[m]);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600289}
290
291static inline struct tcs_mbox *get_tcs_from_index(struct tcs_drv *drv, int m)
292{
Lina Iyer51bc14d2017-04-28 13:56:12 -0600293 struct tcs_mbox *tcs = NULL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600294 int i;
295
Lina Iyer51bc14d2017-04-28 13:56:12 -0600296 for (i = 0; i < drv->num_tcs; i++) {
Lina Iyer88a8fda2016-04-01 08:23:31 -0600297 tcs = &drv->tcs[i];
Lina Iyer51bc14d2017-04-28 13:56:12 -0600298 if (tcs->tcs_mask & (u32)BIT(m))
Lina Iyer88a8fda2016-04-01 08:23:31 -0600299 break;
300 }
301
Lina Iyer51bc14d2017-04-28 13:56:12 -0600302 if (i == drv->num_tcs) {
303 WARN(1, "Incorrect TCS index %d", m);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600304 tcs = NULL;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600305 }
Lina Iyer88a8fda2016-04-01 08:23:31 -0600306
307 return tcs;
308}
309
310static inline struct tcs_mbox *get_tcs_of_type(struct tcs_drv *drv, int type)
311{
312 int i;
313 struct tcs_mbox *tcs;
314
315 for (i = 0; i < TCS_TYPE_NR; i++)
316 if (type == drv->tcs[i].type)
317 break;
318
319 if (i == TCS_TYPE_NR)
320 return ERR_PTR(-EINVAL);
321
322 tcs = &drv->tcs[i];
323 if (!tcs->num_tcs)
324 return ERR_PTR(-EINVAL);
325
326 return tcs;
327}
328
329static inline struct tcs_mbox *get_tcs_for_msg(struct tcs_drv *drv,
330 struct tcs_mbox_msg *msg)
331{
332 int type = -1;
333
334 /* Which box are we dropping this in and do we trigger the TCS */
335 switch (msg->state) {
336 case RPMH_SLEEP_STATE:
337 type = SLEEP_TCS;
338 break;
339 case RPMH_WAKE_ONLY_STATE:
340 type = WAKE_TCS;
341 break;
342 case RPMH_ACTIVE_ONLY_STATE:
343 type = ACTIVE_TCS;
344 break;
Lina Iyer21c17882016-09-22 11:05:51 -0600345 case RPMH_AWAKE_STATE:
346 /*
347 * Awake state is only used when the DRV has no separate
348 * TCS for ACTIVE requests. Switch to WAKE TCS to send
349 * active votes. Otherwise, the caller should be explicit
350 * about the state.
351 */
352 if (IS_ERR(get_tcs_of_type(drv, ACTIVE_TCS)))
353 type = WAKE_TCS;
354 break;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600355 }
356
357 if (msg->is_read)
358 type = ACTIVE_TCS;
359
360 if (type < 0)
361 return ERR_PTR(-EINVAL);
362
363 return get_tcs_of_type(drv, type);
364}
365
Lina Iyer88a8fda2016-04-01 08:23:31 -0600366static inline void send_tcs_response(struct tcs_response *resp)
367{
Lina Iyer00328f12017-05-30 14:08:49 -0600368 struct tcs_drv *drv = resp->drv;
369 unsigned long flags;
370
371 spin_lock_irqsave(&drv->drv_lock, flags);
372 INIT_LIST_HEAD(&resp->list);
373 list_add_tail(&resp->list, &drv->response_pending);
374 spin_unlock_irqrestore(&drv->drv_lock, flags);
375
376 tasklet_schedule(&drv->tasklet);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600377}
378
Lina Iyere0ef5212017-04-28 09:55:32 -0600379static inline void enable_tcs_irq(struct tcs_drv *drv, int m, bool enable)
380{
381 void __iomem *base = drv->reg_base;
382 u32 data;
383
384 /* Enable interrupts for non-ACTIVE TCS */
385 data = read_tcs_reg(base, TCS_DRV_IRQ_ENABLE, 0, 0);
386 if (enable)
387 data |= BIT(m);
388 else
389 data &= ~BIT(m);
390 write_tcs_reg(base, TCS_DRV_IRQ_ENABLE, 0, 0, data);
391}
392
Lina Iyer88a8fda2016-04-01 08:23:31 -0600393/**
394 * tcs_irq_handler: TX Done / Recv data handler
395 */
396static irqreturn_t tcs_irq_handler(int irq, void *p)
397{
398 struct tcs_drv *drv = p;
399 void __iomem *base = drv->reg_base;
400 int m, i;
401 u32 irq_status, sts;
Lina Iyer21c17882016-09-22 11:05:51 -0600402 struct tcs_mbox *tcs;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600403 struct tcs_response *resp;
Lina Iyer7846e212017-03-22 10:35:53 -0600404 struct tcs_cmd *cmd;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600405 u32 data;
406
407 /* Know which TCSes were triggered */
408 irq_status = read_tcs_reg(base, TCS_DRV_IRQ_STATUS, 0, 0);
409
Lina Iyerb465ea62017-05-03 15:27:22 -0600410 for (m = 0; m < drv->num_tcs; m++) {
411 if (!(irq_status & (u32)BIT(m)))
Lina Iyer88a8fda2016-04-01 08:23:31 -0600412 continue;
Lina Iyer088ccec2017-04-24 20:18:48 -0600413 atomic_inc(&drv->tcs_irq_count[m]);
414
Lina Iyer51bc14d2017-04-28 13:56:12 -0600415 resp = get_response(drv, m, true);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600416 if (!resp) {
417 pr_err("No resp request for TCS-%d\n", m);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600418 goto no_resp;
Lina Iyer21c17882016-09-22 11:05:51 -0600419 }
Lina Iyer88a8fda2016-04-01 08:23:31 -0600420
421 /* Check if all commands were completed */
422 resp->err = 0;
423 for (i = 0; i < resp->msg->num_payload; i++) {
Lina Iyer7846e212017-03-22 10:35:53 -0600424 cmd = &resp->msg->payload[i];
Lina Iyer88a8fda2016-04-01 08:23:31 -0600425 sts = read_tcs_reg(base, TCS_DRV_CMD_STATUS, m, i);
Lina Iyer7846e212017-03-22 10:35:53 -0600426 if ((!(sts & CMD_STATUS_ISSUED)) ||
427 ((resp->msg->is_complete || cmd->complete) &&
428 (!(sts & CMD_STATUS_COMPL))))
Lina Iyer88a8fda2016-04-01 08:23:31 -0600429 resp->err = -EIO;
430 }
431
432 /* Check for response if this was a read request */
433 if (resp->msg->is_read) {
434 /* Respond the data back in the same req data */
435 data = read_tcs_reg(base, TCS_DRV_CMD_RESP_DATA, m, 0);
436 resp->msg->payload[0].data = data;
437 mbox_chan_received_data(resp->chan, resp->msg);
438 }
439
Lina Iyer884981e2017-03-21 13:43:05 -0600440 trace_rpmh_notify_irq(drv->name, m, resp->msg->payload[0].addr,
441 resp->err);
Lina Iyerea921442016-05-26 15:07:48 -0600442
Lina Iyer7846e212017-03-22 10:35:53 -0600443 /* Clear the AMC mode for non-ACTIVE TCSes */
Lina Iyer51bc14d2017-04-28 13:56:12 -0600444 tcs = get_tcs_from_index(drv, m);
445 if (tcs && tcs->type != ACTIVE_TCS) {
Lina Iyer7846e212017-03-22 10:35:53 -0600446 data = read_tcs_reg(base, TCS_DRV_CONTROL, m, 0);
447 data &= ~TCS_AMC_MODE_ENABLE;
448 write_tcs_reg(base, TCS_DRV_CONTROL, m, 0, data);
Lina Iyere0ef5212017-04-28 09:55:32 -0600449 /*
450 * Disable interrupt for this TCS to avoid being
451 * spammed with interrupts coming when the solver
452 * sends its wake votes.
453 */
454 enable_tcs_irq(drv, m, false);
Lina Iyer7846e212017-03-22 10:35:53 -0600455 } else {
456 /* Clear the enable bit for the commands */
457 write_tcs_reg(base, TCS_DRV_CMD_ENABLE, m, 0, 0);
458 }
459
Lina Iyer51bc14d2017-04-28 13:56:12 -0600460no_resp:
461 /* Record the recvd time stamp */
462 drv->tcs_last_recv_ts[m] = arch_counter_get_cntvct();
463
Lina Iyerc8712ca2017-04-20 00:18:49 -0600464 /* Clear the TCS IRQ status */
465 write_tcs_reg(base, TCS_DRV_IRQ_CLEAR, 0, 0, BIT(m));
466
Lina Iyer00328f12017-05-30 14:08:49 -0600467 /* Notify the client that this request is completed. */
468 atomic_set(&drv->tcs_in_use[m], 0);
469
Lina Iyerc8712ca2017-04-20 00:18:49 -0600470 /* Clean up response object and notify mbox in tasklet */
Lina Iyer51bc14d2017-04-28 13:56:12 -0600471 if (resp)
472 send_tcs_response(resp);
Lina Iyerb68814f2017-04-14 12:49:07 -0600473 }
474
Lina Iyer88a8fda2016-04-01 08:23:31 -0600475 return IRQ_HANDLED;
476}
477
478static inline void mbox_notify_tx_done(struct mbox_chan *chan,
479 struct tcs_mbox_msg *msg, int m, int err)
480{
Lina Iyer884981e2017-03-21 13:43:05 -0600481 struct tcs_drv *drv = container_of(chan->mbox, struct tcs_drv, mbox);
482
483 trace_rpmh_notify(drv->name, m, msg->payload[0].addr, err);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600484 mbox_chan_txdone(chan, err);
485}
486
Lina Iyer00328f12017-05-30 14:08:49 -0600487static void respond_tx_done(struct tcs_response *resp)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600488{
Lina Iyer88a8fda2016-04-01 08:23:31 -0600489 struct mbox_chan *chan = resp->chan;
490 struct tcs_mbox_msg *msg = resp->msg;
491 int err = resp->err;
492 int m = resp->m;
493
Lina Iyerc8712ca2017-04-20 00:18:49 -0600494 free_response(resp);
Lina Iyer00328f12017-05-30 14:08:49 -0600495 mbox_notify_tx_done(chan, msg, m, err);
496}
497
498/**
499 * tcs_notify_tx_done: TX Done for requests that do not trigger TCS
500 */
501static void tcs_notify_tx_done(unsigned long data)
502{
503 struct tcs_drv *drv = (struct tcs_drv *)data;
504 struct tcs_response *resp;
505 unsigned long flags;
506
507 do {
508 spin_lock_irqsave(&drv->drv_lock, flags);
509 if (list_empty(&drv->response_pending)) {
510 spin_unlock_irqrestore(&drv->drv_lock, flags);
511 break;
512 }
513 resp = list_first_entry(&drv->response_pending,
514 struct tcs_response, list);
515 list_del(&resp->list);
516 spin_unlock_irqrestore(&drv->drv_lock, flags);
517 respond_tx_done(resp);
518 } while (1);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600519}
520
Lina Iyer884981e2017-03-21 13:43:05 -0600521static void __tcs_buffer_write(struct tcs_drv *drv, int d, int m, int n,
Lina Iyer88a8fda2016-04-01 08:23:31 -0600522 struct tcs_mbox_msg *msg, bool trigger)
523{
Lina Iyer7846e212017-03-22 10:35:53 -0600524 u32 msgid, cmd_msgid = 0;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600525 u32 cmd_enable = 0;
526 u32 cmd_complete;
527 u32 enable = TCS_AMC_MODE_ENABLE;
528 struct tcs_cmd *cmd;
529 int i;
Lina Iyer884981e2017-03-21 13:43:05 -0600530 void __iomem *base = drv->reg_base;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600531
532 /* We have homologous command set i.e pure read or write, not a mix */
533 cmd_msgid = CMD_MSGID_LEN;
534 cmd_msgid |= (msg->is_complete) ? CMD_MSGID_RESP_REQ : 0;
535 cmd_msgid |= (!msg->is_read) ? CMD_MSGID_WRITE : 0;
536
537 /* Read the send-after-prev complete flag for those already in TCS */
538 cmd_complete = read_tcs_reg(base, TCS_DRV_CMD_WAIT_FOR_CMPL, m, 0);
539
540 for (i = 0; i < msg->num_payload; i++) {
541 cmd = &msg->payload[i];
542 cmd_enable |= BIT(n + i);
543 cmd_complete |= cmd->complete << (n + i);
Lina Iyer7846e212017-03-22 10:35:53 -0600544 msgid = cmd_msgid;
545 msgid |= (cmd->complete) ? CMD_MSGID_RESP_REQ : 0;
546 write_tcs_reg(base, TCS_DRV_CMD_MSGID, m, n + i, msgid);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600547 write_tcs_reg(base, TCS_DRV_CMD_ADDR, m, n + i, cmd->addr);
548 write_tcs_reg(base, TCS_DRV_CMD_DATA, m, n + i, cmd->data);
Lina Iyer7846e212017-03-22 10:35:53 -0600549 trace_rpmh_send_msg(drv->name, m, n + i, msgid, cmd->addr,
Lina Iyer884981e2017-03-21 13:43:05 -0600550 cmd->data, cmd->complete, trigger);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600551 }
552
553 /* Write the send-after-prev completion bits for the batch */
554 write_tcs_reg(base, TCS_DRV_CMD_WAIT_FOR_CMPL, m, 0, cmd_complete);
555
556 /* Enable the new commands in TCS */
557 cmd_enable |= read_tcs_reg(base, TCS_DRV_CMD_ENABLE, m, 0);
558 write_tcs_reg(base, TCS_DRV_CMD_ENABLE, m, 0, cmd_enable);
559
560 if (trigger) {
Lina Iyer88a8fda2016-04-01 08:23:31 -0600561 /* HW req: Clear the DRV_CONTROL and enable TCS again */
562 write_tcs_reg_sync(base, TCS_DRV_CONTROL, m, 0, 0);
563 write_tcs_reg_sync(base, TCS_DRV_CONTROL, m, 0, enable);
564 /* Enable the AMC mode on the TCS */
565 enable |= TCS_AMC_MODE_TRIGGER;
566 write_tcs_reg_sync(base, TCS_DRV_CONTROL, m, 0, enable);
567 }
568}
569
Lina Iyer0d81e942016-05-26 11:18:23 -0600570/**
571 * tcs_drv_is_idle: Check if any of the AMCs are busy.
572 *
573 * @mbox: The mailbox controller.
574 *
575 * Returns true if the AMCs are not engaged or absent.
576 */
577static bool tcs_drv_is_idle(struct mbox_controller *mbox)
578{
579 int m;
580 struct tcs_drv *drv = container_of(mbox, struct tcs_drv, mbox);
581 struct tcs_mbox *tcs = get_tcs_of_type(drv, ACTIVE_TCS);
582
Lina Iyer21c17882016-09-22 11:05:51 -0600583 /* Check for WAKE TCS if there are no ACTIVE TCS */
Lina Iyer0d81e942016-05-26 11:18:23 -0600584 if (IS_ERR(tcs))
Lina Iyer21c17882016-09-22 11:05:51 -0600585 tcs = get_tcs_of_type(drv, WAKE_TCS);
Lina Iyer0d81e942016-05-26 11:18:23 -0600586
587 for (m = tcs->tcs_offset; m < tcs->tcs_offset + tcs->num_tcs; m++)
Lina Iyerb68814f2017-04-14 12:49:07 -0600588 if (!tcs_is_free(drv, m))
Lina Iyer0d81e942016-05-26 11:18:23 -0600589 return false;
590
591 return true;
592}
593
Lina Iyercc114b22017-04-26 14:40:44 -0600594static int check_for_req_inflight(struct tcs_drv *drv, struct tcs_mbox *tcs,
Lina Iyer88a8fda2016-04-01 08:23:31 -0600595 struct tcs_mbox_msg *msg)
596{
Lina Iyercc114b22017-04-26 14:40:44 -0600597 u32 curr_enabled, addr;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600598 int i, j, k;
Lina Iyercc114b22017-04-26 14:40:44 -0600599 void __iomem *base = drv->reg_base;
600 int m = tcs->tcs_offset;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600601
Lina Iyercc114b22017-04-26 14:40:44 -0600602 for (i = 0; i < tcs->num_tcs; i++, m++) {
603 if (tcs_is_free(drv, m))
604 continue;
605
606 curr_enabled = read_tcs_reg(base, TCS_DRV_CMD_ENABLE, m, 0);
Lina Iyerb465ea62017-05-03 15:27:22 -0600607
608 for (j = 0; j < MAX_CMDS_PER_TCS; j++) {
609 if (!(curr_enabled & (u32)BIT(j)))
Lina Iyer88a8fda2016-04-01 08:23:31 -0600610 continue;
Lina Iyerb465ea62017-05-03 15:27:22 -0600611
Lina Iyercc114b22017-04-26 14:40:44 -0600612 addr = read_tcs_reg(base, TCS_DRV_CMD_ADDR, m, j);
613 for (k = 0; k < msg->num_payload; k++) {
614 if (addr == msg->payload[k].addr)
615 return -EBUSY;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600616 }
617 }
Lina Iyercc114b22017-04-26 14:40:44 -0600618 }
619
620 return 0;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600621}
622
623static int find_free_tcs(struct tcs_mbox *tcs)
624{
Lina Iyercc114b22017-04-26 14:40:44 -0600625 int slot = -EBUSY;
626 int m = 0;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600627
628 /* Loop until we find a free AMC */
Lina Iyercc114b22017-04-26 14:40:44 -0600629 for (m = 0; m < tcs->num_tcs; m++) {
Lina Iyerb68814f2017-04-14 12:49:07 -0600630 if (tcs_is_free(tcs->drv, tcs->tcs_offset + m)) {
Lina Iyer88a8fda2016-04-01 08:23:31 -0600631 slot = m * tcs->ncpt;
632 break;
633 }
Lina Iyercc114b22017-04-26 14:40:44 -0600634 }
Lina Iyer88a8fda2016-04-01 08:23:31 -0600635
636 return slot;
637}
638
639static int find_match(struct tcs_mbox *tcs, struct tcs_cmd *cmd, int len)
640{
641 bool found = false;
642 int i = 0, j;
643
644 /* Check for already cached commands */
645 while ((i = find_next_bit(tcs->slots, MAX_TCS_SLOTS, i)) <
646 MAX_TCS_SLOTS) {
647 if (tcs->cmd_addr[i] != cmd[0].addr) {
648 i++;
649 continue;
650 }
651 /* sanity check to ensure the seq is same */
652 for (j = 1; j < len; j++) {
653 WARN((tcs->cmd_addr[i + j] != cmd[j].addr),
654 "Message does not match previous sequence.\n");
655 return -EINVAL;
656 }
657 found = true;
658 break;
659 }
660
661 return found ? i : -1;
662}
663
664static int find_slots(struct tcs_mbox *tcs, struct tcs_mbox_msg *msg)
665{
666 int slot;
667 int n = 0;
668
669 /* For active requests find the first free AMC. */
670 if (tcs->type == ACTIVE_TCS)
671 return find_free_tcs(tcs);
672
673 /* Find if we already have the msg in our TCS */
674 slot = find_match(tcs, msg->payload, msg->num_payload);
675 if (slot >= 0)
676 return slot;
677
678 /* Do over, until we can fit the full payload in a TCS */
679 do {
680 slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS,
681 n, msg->num_payload, 0);
682 if (slot == MAX_TCS_SLOTS)
683 break;
684 n += tcs->ncpt;
685 } while (slot + msg->num_payload - 1 >= n);
686
687 return (slot != MAX_TCS_SLOTS) ? slot : -ENOMEM;
688}
689
Lina Iyer88a8fda2016-04-01 08:23:31 -0600690static int tcs_mbox_write(struct mbox_chan *chan, struct tcs_mbox_msg *msg,
691 bool trigger)
692{
Lina Iyer88a8fda2016-04-01 08:23:31 -0600693 struct tcs_drv *drv = container_of(chan->mbox, struct tcs_drv, mbox);
694 int d = drv->drv_id;
695 struct tcs_mbox *tcs;
Lina Iyercc114b22017-04-26 14:40:44 -0600696 int i, slot, offset, m, n, ret;
Channagoud Kadabi075db3b2017-03-16 14:26:17 -0700697 struct tcs_response *resp = NULL;
Lina Iyercc114b22017-04-26 14:40:44 -0600698 unsigned long flags;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600699
700 tcs = get_tcs_for_msg(drv, msg);
701 if (IS_ERR(tcs))
702 return PTR_ERR(tcs);
703
Lina Iyer00328f12017-05-30 14:08:49 -0600704 if (trigger) {
Lina Iyerc8712ca2017-04-20 00:18:49 -0600705 resp = setup_response(drv, msg, chan, TCS_M_INIT, 0);
Lina Iyer00328f12017-05-30 14:08:49 -0600706 if (IS_ERR_OR_NULL(resp))
707 return -EBUSY;
708 }
Lina Iyerc8712ca2017-04-20 00:18:49 -0600709
Lina Iyer88a8fda2016-04-01 08:23:31 -0600710 /* Identify the sequential slots that we can write to */
Lina Iyercc114b22017-04-26 14:40:44 -0600711 spin_lock_irqsave(&tcs->tcs_lock, flags);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600712 slot = find_slots(tcs, msg);
713 if (slot < 0) {
Lina Iyercc114b22017-04-26 14:40:44 -0600714 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
Lina Iyerc8712ca2017-04-20 00:18:49 -0600715 if (resp)
716 free_response(resp);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600717 return slot;
718 }
Lina Iyerc8712ca2017-04-20 00:18:49 -0600719
Lina Iyer14424652017-06-03 15:55:25 -0600720 /* Figure out the TCS-m and CMD-n to write to */
721 offset = slot / tcs->ncpt;
722 m = offset + tcs->tcs_offset;
723 n = slot % tcs->ncpt;
724
Lina Iyercc114b22017-04-26 14:40:44 -0600725 if (trigger) {
Lina Iyer14424652017-06-03 15:55:25 -0600726 /* Block, if we have an address from the msg in flight */
Lina Iyercc114b22017-04-26 14:40:44 -0600727 ret = check_for_req_inflight(drv, tcs, msg);
728 if (ret) {
729 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
Lina Iyer00328f12017-05-30 14:08:49 -0600730 if (resp)
731 free_response(resp);
Lina Iyercc114b22017-04-26 14:40:44 -0600732 return ret;
733 }
Lina Iyercc114b22017-04-26 14:40:44 -0600734
Lina Iyerc8712ca2017-04-20 00:18:49 -0600735 resp->m = m;
736 /* Mark the TCS as busy */
737 atomic_set(&drv->tcs_in_use[m], 1);
Lina Iyer088ccec2017-04-24 20:18:48 -0600738 atomic_inc(&drv->tcs_send_count[m]);
Lina Iyere0ef5212017-04-28 09:55:32 -0600739 /* Enable interrupt for active votes through wake TCS */
740 if (tcs->type != ACTIVE_TCS)
741 enable_tcs_irq(drv, m, true);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600742 drv->tcs_last_sent_ts[m] = arch_counter_get_cntvct();
Lina Iyer14424652017-06-03 15:55:25 -0600743 } else {
744 /* Mark the slots as in-use, before we unlock */
745 if (tcs->type == SLEEP_TCS || tcs->type == WAKE_TCS)
746 bitmap_set(tcs->slots, slot, msg->num_payload);
747
748 /* Copy the addresses of the resources over to the slots */
749 for (i = 0; tcs->cmd_addr && i < msg->num_payload; i++)
750 tcs->cmd_addr[slot + i] = msg->payload[i].addr;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600751 }
752
753 /* Write to the TCS or AMC */
Lina Iyer884981e2017-03-21 13:43:05 -0600754 __tcs_buffer_write(drv, d, m, n, msg, trigger);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600755
Lina Iyercc114b22017-04-26 14:40:44 -0600756 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600757
758 return 0;
759}
760
Lina Iyer7846e212017-03-22 10:35:53 -0600761static void __tcs_buffer_invalidate(void __iomem *base, int m)
762{
763 write_tcs_reg(base, TCS_DRV_CMD_ENABLE, m, 0, 0);
764}
765
766static int tcs_mbox_invalidate(struct mbox_chan *chan)
767{
768 struct tcs_drv *drv = container_of(chan->mbox, struct tcs_drv, mbox);
769 struct tcs_mbox *tcs;
770 int m, i;
771 int inv_types[] = { WAKE_TCS, SLEEP_TCS };
772 int type = 0;
Lina Iyercc114b22017-04-26 14:40:44 -0600773 unsigned long flags;
Lina Iyer7846e212017-03-22 10:35:53 -0600774
775 do {
776 tcs = get_tcs_of_type(drv, inv_types[type]);
777 if (IS_ERR(tcs))
778 return PTR_ERR(tcs);
779
Lina Iyercc114b22017-04-26 14:40:44 -0600780 spin_lock_irqsave(&tcs->tcs_lock, flags);
Lina Iyer7846e212017-03-22 10:35:53 -0600781 for (i = 0; i < tcs->num_tcs; i++) {
782 m = i + tcs->tcs_offset;
Lina Iyer7846e212017-03-22 10:35:53 -0600783 __tcs_buffer_invalidate(drv->reg_base, m);
Lina Iyer7846e212017-03-22 10:35:53 -0600784 }
785 /* Mark the TCS as free */
786 bitmap_zero(tcs->slots, MAX_TCS_SLOTS);
Lina Iyercc114b22017-04-26 14:40:44 -0600787 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
Lina Iyer7846e212017-03-22 10:35:53 -0600788 } while (++type < ARRAY_SIZE(inv_types));
789
790 return 0;
791}
792
Lina Iyer035ddd32017-05-30 22:11:53 -0600793static void print_tcs_regs(struct tcs_drv *drv, int m)
794{
795 int n;
796 struct tcs_mbox *tcs = get_tcs_from_index(drv, m);
797 void __iomem *base = drv->reg_base;
798 u32 enable, addr, data, msgid;
799
800 if (!tcs || tcs_is_free(drv, m))
801 return;
802
803 enable = read_tcs_reg(base, TCS_DRV_CMD_ENABLE, m, 0);
804 if (!enable)
805 return;
806
Lina Iyere2f42722017-07-13 12:37:50 -0600807 pr_warn("TCS-%d contents:\n", m);
Lina Iyer035ddd32017-05-30 22:11:53 -0600808 for (n = 0; n < tcs->ncpt; n++) {
809 if (!(enable & BIT(n)))
810 continue;
811 addr = read_tcs_reg(base, TCS_DRV_CMD_ADDR, m, n);
812 data = read_tcs_reg(base, TCS_DRV_CMD_DATA, m, n);
813 msgid = read_tcs_reg(base, TCS_DRV_CMD_MSGID, m, n);
Lina Iyere2f42722017-07-13 12:37:50 -0600814 pr_warn("\tn=%d addr=0x%x data=0x%x hdr=0x%x\n",
Lina Iyer035ddd32017-05-30 22:11:53 -0600815 n, addr, data, msgid);
816 }
817}
818
Lina Iyer51bc14d2017-04-28 13:56:12 -0600819static void dump_tcs_stats(struct tcs_drv *drv)
820{
821 int i;
822 unsigned long long curr = arch_counter_get_cntvct();
823
824 for (i = 0; i < drv->num_tcs; i++) {
825 if (!atomic_read(&drv->tcs_in_use[i]))
826 continue;
Lina Iyere2f42722017-07-13 12:37:50 -0600827 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 -0600828 curr, i,
829 atomic_read(&drv->tcs_send_count[i]),
830 drv->tcs_last_sent_ts[i],
831 atomic_read(&drv->tcs_irq_count[i]),
832 drv->tcs_last_recv_ts[i]);
Lina Iyer035ddd32017-05-30 22:11:53 -0600833 print_tcs_regs(drv, i);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600834 print_response(drv, i);
835 }
836}
837
Lina Iyere2f42722017-07-13 12:37:50 -0600838static void chan_debug(struct mbox_chan *chan)
839{
840 struct tcs_drv *drv = container_of(chan->mbox, struct tcs_drv, mbox);
841
842 dump_tcs_stats(drv);
843}
844
Lina Iyer88a8fda2016-04-01 08:23:31 -0600845/**
846 * chan_tcs_write: Validate the incoming message and write to the
847 * appropriate TCS block.
848 *
849 * @chan: the MBOX channel
850 * @data: the tcs_mbox_msg*
851 *
852 * Returns a negative error for invalid message structure and invalid
853 * message combination, -EBUSY if there is an other active request for
854 * the channel in process, otherwise bubbles up internal error.
855 */
856static int chan_tcs_write(struct mbox_chan *chan, void *data)
857{
Lina Iyer51bc14d2017-04-28 13:56:12 -0600858 struct tcs_drv *drv = container_of(chan->mbox, struct tcs_drv, mbox);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600859 struct tcs_mbox_msg *msg = data;
860 const struct device *dev = chan->cl->dev;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600861 int ret = 0;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600862
863 if (!msg) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600864 dev_err(dev, "Payload error\n");
Lina Iyer51bc14d2017-04-28 13:56:12 -0600865 ret = -EINVAL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600866 goto tx_fail;
867 }
868
869 if (!msg->payload || msg->num_payload > MAX_RPMH_PAYLOAD) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600870 dev_err(dev, "Payload error\n");
Lina Iyer51bc14d2017-04-28 13:56:12 -0600871 ret = -EINVAL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600872 goto tx_fail;
873 }
874
875 if (msg->invalidate || msg->is_control) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600876 dev_err(dev, "Incorrect API\n");
Lina Iyer51bc14d2017-04-28 13:56:12 -0600877 ret = -EINVAL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600878 goto tx_fail;
879 }
880
Lina Iyer21c17882016-09-22 11:05:51 -0600881 if (msg->state != RPMH_ACTIVE_ONLY_STATE &&
882 msg->state != RPMH_AWAKE_STATE) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600883 dev_err(dev, "Incorrect API\n");
Lina Iyer51bc14d2017-04-28 13:56:12 -0600884 ret = -EINVAL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600885 goto tx_fail;
886 }
887
888 /* Read requests should always be single */
889 if (msg->is_read && msg->num_payload > 1) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600890 dev_err(dev, "Incorrect read request\n");
Lina Iyer51bc14d2017-04-28 13:56:12 -0600891 ret = -EINVAL;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600892 goto tx_fail;
893 }
894
Lina Iyer7846e212017-03-22 10:35:53 -0600895 /*
896 * Since we are re-purposing the wake TCS, invalidate previous
897 * contents to avoid confusion.
898 */
899 if (msg->state == RPMH_AWAKE_STATE)
900 tcs_mbox_invalidate(chan);
901
Lina Iyer88a8fda2016-04-01 08:23:31 -0600902 /* Post the message to the TCS and trigger */
Lina Iyer64834df2017-06-09 11:36:46 -0600903 ret = tcs_mbox_write(chan, msg, true);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600904
905tx_fail:
Lina Iyer51bc14d2017-04-28 13:56:12 -0600906 /* If there was an error in the request, schedule a response */
907 if (ret < 0 && ret != -EBUSY) {
Lina Iyerc8712ca2017-04-20 00:18:49 -0600908 struct tcs_response *resp = setup_response(
909 drv, msg, chan, TCS_M_INIT, ret);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600910
911 dev_err(dev, "Error sending RPMH message %d\n", ret);
Lina Iyer00328f12017-05-30 14:08:49 -0600912 if (resp)
913 send_tcs_response(resp);
Lina Iyer51bc14d2017-04-28 13:56:12 -0600914 ret = 0;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600915 }
916
Lina Iyer51bc14d2017-04-28 13:56:12 -0600917 /* If we were just busy waiting for TCS, dump the state and return */
918 if (ret == -EBUSY) {
Lina Iyer94972202017-06-28 15:37:43 -0600919 pr_info_ratelimited("TCS Busy, retrying RPMH message send\n");
Lina Iyer348b2712017-05-30 22:13:59 -0600920 ret = -EAGAIN;
Lina Iyer51bc14d2017-04-28 13:56:12 -0600921 }
922
923 return ret;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600924}
925
Lina Iyer884981e2017-03-21 13:43:05 -0600926static void __tcs_write_hidden(struct tcs_drv *drv, int d,
927 struct tcs_mbox_msg *msg)
Lina Iyer88a8fda2016-04-01 08:23:31 -0600928{
929 int i;
Lina Iyer884981e2017-03-21 13:43:05 -0600930 void __iomem *addr = drv->base + TCS_HIDDEN_CMD0_DRV_DATA;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600931
Lina Iyer88a8fda2016-04-01 08:23:31 -0600932 for (i = 0; i < msg->num_payload; i++) {
933 /* Only data is write capable */
Lina Iyerd93fbce2017-03-01 09:07:20 -0700934 writel_relaxed(cpu_to_le32(msg->payload[i].data), addr);
Lina Iyer884981e2017-03-21 13:43:05 -0600935 trace_rpmh_control_msg(drv->name, msg->payload[i].data);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600936 addr += TCS_HIDDEN_CMD_SHIFT;
937 }
938}
939
940static int tcs_control_write(struct mbox_chan *chan, struct tcs_mbox_msg *msg)
941{
942 const struct device *dev = chan->cl->dev;
943 struct tcs_drv *drv = container_of(chan->mbox, struct tcs_drv, mbox);
944 struct tcs_mbox *tcs;
Lina Iyercc114b22017-04-26 14:40:44 -0600945 unsigned long flags;
Lina Iyer88a8fda2016-04-01 08:23:31 -0600946
947 tcs = get_tcs_of_type(drv, CONTROL_TCS);
948 if (IS_ERR(tcs))
949 return PTR_ERR(tcs);
950
951 if (msg->num_payload != tcs->ncpt) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600952 dev_err(dev, "Request must fit the control TCS size\n");
Lina Iyer88a8fda2016-04-01 08:23:31 -0600953 return -EINVAL;
954 }
955
Lina Iyercc114b22017-04-26 14:40:44 -0600956 spin_lock_irqsave(&tcs->tcs_lock, flags);
Lina Iyer884981e2017-03-21 13:43:05 -0600957 __tcs_write_hidden(tcs->drv, drv->drv_id, msg);
Lina Iyercc114b22017-04-26 14:40:44 -0600958 spin_unlock_irqrestore(&tcs->tcs_lock, flags);
Lina Iyer88a8fda2016-04-01 08:23:31 -0600959
960 return 0;
961}
962
963/**
964 * chan_tcs_ctrl_write: Write message to the controller, no ACK sent.
965 *
966 * @chan: the MBOX channel
967 * @data: the tcs_mbox_msg*
968 */
969static int chan_tcs_ctrl_write(struct mbox_chan *chan, void *data)
970{
971 struct tcs_mbox_msg *msg = data;
972 const struct device *dev = chan->cl->dev;
973 int ret = -EINVAL;
974
975 if (!msg) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600976 dev_err(dev, "Payload error\n");
Lina Iyer88a8fda2016-04-01 08:23:31 -0600977 goto tx_done;
978 }
979
980 if (msg->num_payload > MAX_RPMH_PAYLOAD) {
Lina Iyer5ddea732017-05-09 14:25:33 -0600981 dev_err(dev, "Payload error\n");
Lina Iyer88a8fda2016-04-01 08:23:31 -0600982 goto tx_done;
983 }
984
985 /* Invalidate sleep/wake TCS */
986 if (msg->invalidate) {
987 ret = tcs_mbox_invalidate(chan);
988 goto tx_done;
989 }
990
991 /* Control slots are unique. They carry specific data. */
992 if (msg->is_control) {
993 ret = tcs_control_write(chan, msg);
994 goto tx_done;
995 }
996
Lina Iyer88a8fda2016-04-01 08:23:31 -0600997 /* Post the message to the TCS without trigger */
998 ret = tcs_mbox_write(chan, msg, false);
999
1000tx_done:
1001 return ret;
1002}
1003
1004static int chan_init(struct mbox_chan *chan)
1005{
1006 return 0;
1007}
1008
1009static void chan_shutdown(struct mbox_chan *chan)
1010{ }
1011
1012static const struct mbox_chan_ops mbox_ops = {
1013 .send_data = chan_tcs_write,
1014 .send_controller_data = chan_tcs_ctrl_write,
1015 .startup = chan_init,
1016 .shutdown = chan_shutdown,
1017};
1018
1019static struct mbox_chan *of_tcs_mbox_xlate(struct mbox_controller *mbox,
1020 const struct of_phandle_args *sp)
1021{
1022 struct tcs_drv *drv = container_of(mbox, struct tcs_drv, mbox);
1023 struct mbox_chan *chan;
1024
1025 if (drv->num_assigned >= mbox->num_chans) {
1026 pr_err("TCS-Mbox out of channel memory\n");
1027 return ERR_PTR(-ENOMEM);
1028 }
1029
1030 chan = &mbox->chans[drv->num_assigned++];
Lina Iyer14424652017-06-03 15:55:25 -06001031 chan->con_priv = drv;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001032
1033 return chan;
1034}
1035
1036static int tcs_drv_probe(struct platform_device *pdev)
1037{
1038 struct device_node *dn = pdev->dev.of_node;
1039 struct device_node *np;
1040 struct tcs_drv *drv;
1041 struct mbox_chan *chans;
1042 struct tcs_mbox *tcs;
1043 struct of_phandle_args p;
1044 int irq;
1045 u32 val[8] = { 0 };
1046 int num_chans = 0;
1047 int st = 0;
1048 int i, j, ret, nelem;
1049 u32 config, max_tcs, ncpt;
Lina Iyer73101422017-02-16 14:09:25 -07001050 int tcs_type_count[TCS_TYPE_NR] = { 0 };
1051 struct resource *res;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001052
1053 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
1054 if (!drv)
1055 return -ENOMEM;
1056
Lina Iyer73101422017-02-16 14:09:25 -07001057 ret = of_property_read_u32(dn, "qcom,drv-id", &drv->drv_id);
1058 if (ret)
1059 return ret;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001060
Lina Iyer73101422017-02-16 14:09:25 -07001061 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1062 if (!res)
1063 return -EINVAL;
1064 drv->base = devm_ioremap_resource(&pdev->dev, res);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001065 if (IS_ERR(drv->base))
1066 return PTR_ERR(drv->base);
1067
Lina Iyer73101422017-02-16 14:09:25 -07001068 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
1069 if (!res)
1070 return -EINVAL;
1071 drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001072 if (IS_ERR(drv->reg_base))
1073 return PTR_ERR(drv->reg_base);
1074
1075 config = read_drv_config(drv->base);
1076 max_tcs = config & (DRV_NUM_TCS_MASK <<
1077 (DRV_NUM_TCS_SHIFT * drv->drv_id));
1078 max_tcs = max_tcs >> (DRV_NUM_TCS_SHIFT * drv->drv_id);
1079 ncpt = config & (DRV_NCPT_MASK << DRV_NCPT_SHIFT);
1080 ncpt = ncpt >> DRV_NCPT_SHIFT;
1081
1082 nelem = of_property_count_elems_of_size(dn, "qcom,tcs-config",
1083 sizeof(u32));
1084 if (!nelem || (nelem % 2) || (nelem > 2 * TCS_TYPE_NR))
1085 return -EINVAL;
1086
1087 ret = of_property_read_u32_array(dn, "qcom,tcs-config", val, nelem);
1088 if (ret)
1089 return ret;
1090
Lina Iyer73101422017-02-16 14:09:25 -07001091 /* Ensure we have exactly not more than one of each type in DT */
Lina Iyer88a8fda2016-04-01 08:23:31 -06001092 for (i = 0; i < (nelem / 2); i++) {
Lina Iyer73101422017-02-16 14:09:25 -07001093 if (val[2 * i] >= TCS_TYPE_NR)
1094 return -EINVAL;
1095 tcs_type_count[val[2 * i]]++;
1096 if (tcs_type_count[val[2 * i]] > 1)
1097 return -EINVAL;
1098 }
1099
1100 /* Ensure we have each type specified in DT */
1101 for (i = 0; i < ARRAY_SIZE(tcs_type_count); i++)
1102 if (!tcs_type_count[i])
1103 return -EINVAL;
1104
1105 for (i = 0; i < (nelem / 2); i++) {
1106 tcs = &drv->tcs[val[2 * i]];
Lina Iyer88a8fda2016-04-01 08:23:31 -06001107 tcs->drv = drv;
1108 tcs->type = val[2 * i];
1109 tcs->num_tcs = val[2 * i + 1];
1110 tcs->ncpt = (tcs->type == CONTROL_TCS) ? TCS_HIDDEN_MAX_SLOTS
1111 : ncpt;
1112 spin_lock_init(&tcs->tcs_lock);
1113
1114 if (tcs->num_tcs <= 0 || tcs->type == CONTROL_TCS)
1115 continue;
1116
1117 if (tcs->num_tcs > MAX_TCS_PER_TYPE)
1118 return -EINVAL;
1119
1120 if (st > max_tcs)
1121 return -EINVAL;
1122
1123 tcs->tcs_mask = ((1 << tcs->num_tcs) - 1) << st;
1124 tcs->tcs_offset = st;
1125 st += tcs->num_tcs;
1126
1127 tcs->cmd_addr = devm_kzalloc(&pdev->dev, sizeof(u32) *
1128 tcs->num_tcs * tcs->ncpt, GFP_KERNEL);
1129 if (!tcs->cmd_addr)
1130 return -ENOMEM;
1131
Lina Iyer88a8fda2016-04-01 08:23:31 -06001132 }
1133
1134 /* Allocate only that many channels specified in DT for our MBOX */
1135 for_each_node_with_property(np, "mboxes") {
1136 if (!of_device_is_available(np))
1137 continue;
1138 i = of_count_phandle_with_args(np, "mboxes", "#mbox-cells");
1139 for (j = 0; j < i; j++) {
1140 ret = of_parse_phandle_with_args(np, "mboxes",
1141 "#mbox-cells", j, &p);
1142 if (!ret && p.np == pdev->dev.of_node)
1143 break;
1144 }
1145 num_chans++;
1146 }
1147
1148 if (!num_chans) {
1149 pr_err("%s: No clients for controller (%s)\n", __func__,
1150 dn->full_name);
1151 return -ENODEV;
1152 }
1153
1154 chans = devm_kzalloc(&pdev->dev, num_chans * sizeof(*chans),
1155 GFP_KERNEL);
1156 if (!chans)
1157 return -ENOMEM;
1158
1159 for (i = 0; i < num_chans; i++) {
1160 chans[i].mbox = &drv->mbox;
1161 chans[i].txdone_method = TXDONE_BY_IRQ;
1162 }
1163
1164 drv->mbox.dev = &pdev->dev;
1165 drv->mbox.ops = &mbox_ops;
1166 drv->mbox.chans = chans;
1167 drv->mbox.num_chans = num_chans;
1168 drv->mbox.txdone_irq = true;
1169 drv->mbox.of_xlate = of_tcs_mbox_xlate;
Lina Iyer0d81e942016-05-26 11:18:23 -06001170 drv->mbox.is_idle = tcs_drv_is_idle;
Lina Iyere2f42722017-07-13 12:37:50 -06001171 drv->mbox.debug = chan_debug;
Lina Iyer88a8fda2016-04-01 08:23:31 -06001172 drv->num_tcs = st;
1173 drv->pdev = pdev;
Lina Iyer00328f12017-05-30 14:08:49 -06001174 INIT_LIST_HEAD(&drv->response_pending);
1175 spin_lock_init(&drv->drv_lock);
1176 tasklet_init(&drv->tasklet, tcs_notify_tx_done, (unsigned long)drv);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001177
Lina Iyer884981e2017-03-21 13:43:05 -06001178 drv->name = of_get_property(pdev->dev.of_node, "label", NULL);
1179 if (!drv->name)
1180 drv->name = dev_name(&pdev->dev);
1181
Lina Iyer88a8fda2016-04-01 08:23:31 -06001182 ret = tcs_response_pool_init(drv);
1183 if (ret)
1184 return ret;
1185
1186 irq = of_irq_get(dn, 0);
1187 if (irq < 0)
1188 return irq;
1189
Lina Iyerafcdc182017-04-19 18:31:04 -06001190 ret = devm_request_irq(&pdev->dev, irq, tcs_irq_handler,
Lina Iyer9837a422017-04-26 10:06:21 -06001191 IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND,
Lina Iyer88a8fda2016-04-01 08:23:31 -06001192 "tcs_irq", drv);
1193 if (ret)
1194 return ret;
1195
Lina Iyere0ef5212017-04-28 09:55:32 -06001196 /* Enable interrupts for AMC TCS */
1197 write_tcs_reg(drv->reg_base, TCS_DRV_IRQ_ENABLE, 0, 0,
1198 drv->tcs[ACTIVE_TCS].tcs_mask);
Lina Iyer88a8fda2016-04-01 08:23:31 -06001199
Lina Iyerb68814f2017-04-14 12:49:07 -06001200 for (i = 0; i < ARRAY_SIZE(drv->tcs_in_use); i++)
1201 atomic_set(&drv->tcs_in_use[i], 0);
1202
Lina Iyer88a8fda2016-04-01 08:23:31 -06001203 ret = mbox_controller_register(&drv->mbox);
1204 if (ret)
1205 return ret;
1206
1207 pr_debug("Mailbox controller (%s, drv=%d) registered\n",
1208 dn->full_name, drv->drv_id);
1209
1210 return 0;
1211}
1212
1213static const struct of_device_id tcs_drv_match[] = {
1214 { .compatible = "qcom,tcs-drv", },
1215 { }
1216};
1217
1218static struct platform_driver tcs_mbox_driver = {
1219 .probe = tcs_drv_probe,
1220 .driver = {
1221 .name = KBUILD_MODNAME,
1222 .of_match_table = tcs_drv_match,
1223 },
1224};
1225
1226static int __init tcs_mbox_driver_init(void)
1227{
1228 return platform_driver_register(&tcs_mbox_driver);
1229}
1230arch_initcall(tcs_mbox_driver_init);