xref: /openbsd/sys/dev/pci/drm/apple/afk.c (revision 44a5d259)
15dd0baa8Skettenis // SPDX-License-Identifier: GPL-2.0-only OR MIT
25dd0baa8Skettenis /* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
35dd0baa8Skettenis 
48f15cb74Skettenis #include <linux/bitfield.h>
55dd0baa8Skettenis #include <linux/dma-mapping.h>
65dd0baa8Skettenis #include <linux/slab.h>
75dd0baa8Skettenis #include <linux/workqueue.h>
85dd0baa8Skettenis #include <linux/soc/apple/rtkit.h>
95dd0baa8Skettenis 
105dd0baa8Skettenis #include "afk.h"
115dd0baa8Skettenis #include "trace.h"
125dd0baa8Skettenis 
135dd0baa8Skettenis struct afk_receive_message_work {
145dd0baa8Skettenis 	struct apple_dcp_afkep *ep;
155dd0baa8Skettenis 	u64 message;
165dd0baa8Skettenis 	struct work_struct work;
175dd0baa8Skettenis };
185dd0baa8Skettenis 
195dd0baa8Skettenis #define RBEP_TYPE GENMASK(63, 48)
205dd0baa8Skettenis 
215dd0baa8Skettenis enum rbep_msg_type {
225dd0baa8Skettenis 	RBEP_INIT = 0x80,
235dd0baa8Skettenis 	RBEP_INIT_ACK = 0xa0,
245dd0baa8Skettenis 	RBEP_GETBUF = 0x89,
255dd0baa8Skettenis 	RBEP_GETBUF_ACK = 0xa1,
265dd0baa8Skettenis 	RBEP_INIT_TX = 0x8a,
275dd0baa8Skettenis 	RBEP_INIT_RX = 0x8b,
285dd0baa8Skettenis 	RBEP_START = 0xa3,
295dd0baa8Skettenis 	RBEP_START_ACK = 0x86,
305dd0baa8Skettenis 	RBEP_SEND = 0xa2,
315dd0baa8Skettenis 	RBEP_RECV = 0x85,
325dd0baa8Skettenis 	RBEP_SHUTDOWN = 0xc0,
335dd0baa8Skettenis 	RBEP_SHUTDOWN_ACK = 0xc1,
345dd0baa8Skettenis };
355dd0baa8Skettenis 
365dd0baa8Skettenis #define BLOCK_SHIFT 6
375dd0baa8Skettenis 
385dd0baa8Skettenis #define GETBUF_SIZE GENMASK(31, 16)
395dd0baa8Skettenis #define GETBUF_TAG GENMASK(15, 0)
405dd0baa8Skettenis #define GETBUF_ACK_DVA GENMASK(47, 0)
415dd0baa8Skettenis 
425dd0baa8Skettenis #define INITRB_OFFSET GENMASK(47, 32)
435dd0baa8Skettenis #define INITRB_SIZE GENMASK(31, 16)
445dd0baa8Skettenis #define INITRB_TAG GENMASK(15, 0)
455dd0baa8Skettenis 
465dd0baa8Skettenis #define SEND_WPTR GENMASK(31, 0)
475dd0baa8Skettenis 
afk_send(struct apple_dcp_afkep * ep,u64 message)485dd0baa8Skettenis static void afk_send(struct apple_dcp_afkep *ep, u64 message)
495dd0baa8Skettenis {
505dd0baa8Skettenis 	dcp_send_message(ep->dcp, ep->endpoint, message);
515dd0baa8Skettenis }
525dd0baa8Skettenis 
afk_init(struct apple_dcp * dcp,u32 endpoint,const struct apple_epic_service_ops * ops)535dd0baa8Skettenis struct apple_dcp_afkep *afk_init(struct apple_dcp *dcp, u32 endpoint,
545dd0baa8Skettenis 				 const struct apple_epic_service_ops *ops)
555dd0baa8Skettenis {
565dd0baa8Skettenis 	struct apple_dcp_afkep *afkep;
575dd0baa8Skettenis 	int ret;
585dd0baa8Skettenis 
595dd0baa8Skettenis 	afkep = devm_kzalloc(dcp->dev, sizeof(*afkep), GFP_KERNEL);
605dd0baa8Skettenis 	if (!afkep)
615dd0baa8Skettenis 		return ERR_PTR(-ENOMEM);
625dd0baa8Skettenis 
635dd0baa8Skettenis 	afkep->ops = ops;
645dd0baa8Skettenis 	afkep->dcp = dcp;
655dd0baa8Skettenis 	afkep->endpoint = endpoint;
665dd0baa8Skettenis 	afkep->wq = alloc_ordered_workqueue("apple-dcp-afkep%02x",
675dd0baa8Skettenis 					    WQ_MEM_RECLAIM, endpoint);
685dd0baa8Skettenis 	if (!afkep->wq) {
695dd0baa8Skettenis 		ret = -ENOMEM;
705dd0baa8Skettenis 		goto out_free_afkep;
715dd0baa8Skettenis 	}
725dd0baa8Skettenis 
735dd0baa8Skettenis 	// TODO: devm_ for wq
745dd0baa8Skettenis 
755dd0baa8Skettenis 	init_completion(&afkep->started);
765dd0baa8Skettenis 	init_completion(&afkep->stopped);
775dd0baa8Skettenis 	mtx_init(&afkep->lock, IPL_TTY);
785dd0baa8Skettenis 
795dd0baa8Skettenis 	return afkep;
805dd0baa8Skettenis 
815dd0baa8Skettenis out_free_afkep:
825dd0baa8Skettenis 	devm_kfree(dcp->dev, afkep);
835dd0baa8Skettenis 	return ERR_PTR(ret);
845dd0baa8Skettenis }
855dd0baa8Skettenis 
afk_start(struct apple_dcp_afkep * ep)865dd0baa8Skettenis int afk_start(struct apple_dcp_afkep *ep)
875dd0baa8Skettenis {
885dd0baa8Skettenis 	int ret;
895dd0baa8Skettenis 
905dd0baa8Skettenis 	reinit_completion(&ep->started);
915dd0baa8Skettenis 	apple_rtkit_start_ep(ep->dcp->rtk, ep->endpoint);
925dd0baa8Skettenis 	afk_send(ep, FIELD_PREP(RBEP_TYPE, RBEP_INIT));
935dd0baa8Skettenis 
945dd0baa8Skettenis 	ret = wait_for_completion_timeout(&ep->started, msecs_to_jiffies(1000));
955dd0baa8Skettenis 	if (ret <= 0)
965dd0baa8Skettenis 		return -ETIMEDOUT;
975dd0baa8Skettenis 	else
985dd0baa8Skettenis 		return 0;
995dd0baa8Skettenis }
1005dd0baa8Skettenis 
afk_getbuf(struct apple_dcp_afkep * ep,u64 message)1015dd0baa8Skettenis static void afk_getbuf(struct apple_dcp_afkep *ep, u64 message)
1025dd0baa8Skettenis {
1035dd0baa8Skettenis 	u16 size = FIELD_GET(GETBUF_SIZE, message) << BLOCK_SHIFT;
1045dd0baa8Skettenis 	u16 tag = FIELD_GET(GETBUF_TAG, message);
1055dd0baa8Skettenis 	u64 reply;
1065dd0baa8Skettenis 
1075dd0baa8Skettenis 	trace_afk_getbuf(ep, size, tag);
1085dd0baa8Skettenis 
1095dd0baa8Skettenis 	if (ep->bfr) {
1105dd0baa8Skettenis 		dev_err(ep->dcp->dev,
1115dd0baa8Skettenis 			"Got GETBUF message but buffer already exists\n");
1125dd0baa8Skettenis 		return;
1135dd0baa8Skettenis 	}
1145dd0baa8Skettenis 
1155dd0baa8Skettenis 	ep->bfr = dmam_alloc_coherent(ep->dcp->dev, size, &ep->bfr_dma,
1165dd0baa8Skettenis 				      GFP_KERNEL);
1175dd0baa8Skettenis 	if (!ep->bfr) {
1185dd0baa8Skettenis 		dev_err(ep->dcp->dev, "Failed to allocate %d bytes buffer\n",
1195dd0baa8Skettenis 			size);
1205dd0baa8Skettenis 		return;
1215dd0baa8Skettenis 	}
1225dd0baa8Skettenis 
1235dd0baa8Skettenis 	ep->bfr_size = size;
1245dd0baa8Skettenis 	ep->bfr_tag = tag;
1255dd0baa8Skettenis 
1265dd0baa8Skettenis 	reply = FIELD_PREP(RBEP_TYPE, RBEP_GETBUF_ACK);
1275dd0baa8Skettenis 	reply |= FIELD_PREP(GETBUF_ACK_DVA, ep->bfr_dma);
1285dd0baa8Skettenis 	afk_send(ep, reply);
1295dd0baa8Skettenis }
1305dd0baa8Skettenis 
afk_init_rxtx(struct apple_dcp_afkep * ep,u64 message,struct afk_ringbuffer * bfr)1315dd0baa8Skettenis static void afk_init_rxtx(struct apple_dcp_afkep *ep, u64 message,
1325dd0baa8Skettenis 			  struct afk_ringbuffer *bfr)
1335dd0baa8Skettenis {
1345dd0baa8Skettenis 	u16 base = FIELD_GET(INITRB_OFFSET, message) << BLOCK_SHIFT;
1355dd0baa8Skettenis 	u16 size = FIELD_GET(INITRB_SIZE, message) << BLOCK_SHIFT;
1365dd0baa8Skettenis 	u16 tag = FIELD_GET(INITRB_TAG, message);
1375dd0baa8Skettenis 	u32 bufsz, end;
1385dd0baa8Skettenis 
1395dd0baa8Skettenis 	if (tag != ep->bfr_tag) {
140*44a5d259Skettenis 		dev_err(ep->dcp->dev, "AFK[ep:%02x]: expected tag 0x%x but got 0x%x\n",
1415dd0baa8Skettenis 			ep->endpoint, ep->bfr_tag, tag);
1425dd0baa8Skettenis 		return;
1435dd0baa8Skettenis 	}
1445dd0baa8Skettenis 
1455dd0baa8Skettenis 	if (bfr->ready) {
1465dd0baa8Skettenis 		dev_err(ep->dcp->dev, "AFK[ep:%02x]: buffer is already initialized\n",
1475dd0baa8Skettenis 			ep->endpoint);
1485dd0baa8Skettenis 		return;
1495dd0baa8Skettenis 	}
1505dd0baa8Skettenis 
1515dd0baa8Skettenis 	if (base >= ep->bfr_size) {
1525dd0baa8Skettenis 		dev_err(ep->dcp->dev,
153*44a5d259Skettenis 			"AFK[ep:%02x]: requested base 0x%x >= max size 0x%lx\n",
1545dd0baa8Skettenis 			ep->endpoint, base, ep->bfr_size);
1555dd0baa8Skettenis 		return;
1565dd0baa8Skettenis 	}
1575dd0baa8Skettenis 
1585dd0baa8Skettenis 	end = base + size;
1595dd0baa8Skettenis 	if (end > ep->bfr_size) {
1605dd0baa8Skettenis 		dev_err(ep->dcp->dev,
161*44a5d259Skettenis 			"AFK[ep:%02x]: requested end 0x%x > max size 0x%lx\n",
1625dd0baa8Skettenis 			ep->endpoint, end, ep->bfr_size);
1635dd0baa8Skettenis 		return;
1645dd0baa8Skettenis 	}
1655dd0baa8Skettenis 
1665dd0baa8Skettenis 	bfr->hdr = ep->bfr + base;
1675dd0baa8Skettenis 	bufsz = le32_to_cpu(bfr->hdr->bufsz);
1685dd0baa8Skettenis 	if (bufsz + sizeof(*bfr->hdr) != size) {
1695dd0baa8Skettenis 		dev_err(ep->dcp->dev,
170*44a5d259Skettenis 			"AFK[ep:%02x]: ring buffer size 0x%x != expected 0x%lx\n",
1715dd0baa8Skettenis 			ep->endpoint, bufsz, sizeof(*bfr->hdr));
1725dd0baa8Skettenis 		return;
1735dd0baa8Skettenis 	}
1745dd0baa8Skettenis 
1755dd0baa8Skettenis 	bfr->buf = bfr->hdr + 1;
1765dd0baa8Skettenis 	bfr->bufsz = bufsz;
1775dd0baa8Skettenis 	bfr->ready = true;
1785dd0baa8Skettenis 
1795dd0baa8Skettenis 	if (ep->rxbfr.ready && ep->txbfr.ready)
1805dd0baa8Skettenis 		afk_send(ep, FIELD_PREP(RBEP_TYPE, RBEP_START));
1815dd0baa8Skettenis }
1825dd0baa8Skettenis 
1835dd0baa8Skettenis static const struct apple_epic_service_ops *
afk_match_service(struct apple_dcp_afkep * ep,const char * name)1845dd0baa8Skettenis afk_match_service(struct apple_dcp_afkep *ep, const char *name)
1855dd0baa8Skettenis {
1865dd0baa8Skettenis 	const struct apple_epic_service_ops *ops;
1875dd0baa8Skettenis 
1885dd0baa8Skettenis 	if (!name[0])
1895dd0baa8Skettenis 		return NULL;
1905dd0baa8Skettenis 	if (!ep->ops)
1915dd0baa8Skettenis 		return NULL;
1925dd0baa8Skettenis 
1935dd0baa8Skettenis 	for (ops = ep->ops; ops->name[0]; ops++) {
1945dd0baa8Skettenis 		if (strcmp(ops->name, name))
1955dd0baa8Skettenis 			continue;
1965dd0baa8Skettenis 
1975dd0baa8Skettenis 		return ops;
1985dd0baa8Skettenis 	}
1995dd0baa8Skettenis 
2005dd0baa8Skettenis 	return NULL;
2015dd0baa8Skettenis }
2025dd0baa8Skettenis 
afk_epic_find_service(struct apple_dcp_afkep * ep,u32 channel)2035dd0baa8Skettenis static struct apple_epic_service *afk_epic_find_service(struct apple_dcp_afkep *ep,
2045dd0baa8Skettenis 						 u32 channel)
2055dd0baa8Skettenis {
2065dd0baa8Skettenis     for (u32 i = 0; i < ep->num_channels; i++)
2075dd0baa8Skettenis         if (ep->services[i].enabled && ep->services[i].channel == channel)
2085dd0baa8Skettenis             return &ep->services[i];
2095dd0baa8Skettenis 
2105dd0baa8Skettenis     return NULL;
2115dd0baa8Skettenis }
2125dd0baa8Skettenis 
afk_recv_handle_init(struct apple_dcp_afkep * ep,u32 channel,u8 * payload,size_t payload_size)2135dd0baa8Skettenis static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel,
2145dd0baa8Skettenis 				 u8 *payload, size_t payload_size)
2155dd0baa8Skettenis {
2165dd0baa8Skettenis 	char name[32];
2175dd0baa8Skettenis 	s64 epic_unit = -1;
2185dd0baa8Skettenis 	u32 ch_idx;
2195dd0baa8Skettenis 	const char *service_name = name;
2205dd0baa8Skettenis 	const char *epic_name = NULL, *epic_class = NULL;
2215dd0baa8Skettenis 	const struct apple_epic_service_ops *ops;
2225dd0baa8Skettenis 	struct dcp_parse_ctx ctx;
2235dd0baa8Skettenis 	u8 *props = payload + sizeof(name);
2245dd0baa8Skettenis 	size_t props_size = payload_size - sizeof(name);
2255dd0baa8Skettenis 
2265dd0baa8Skettenis 	WARN_ON(afk_epic_find_service(ep, channel));
2275dd0baa8Skettenis 
2285dd0baa8Skettenis 	if (payload_size < sizeof(name)) {
2295dd0baa8Skettenis 		dev_err(ep->dcp->dev, "AFK[ep:%02x]: payload too small: %lx\n",
2305dd0baa8Skettenis 			ep->endpoint, payload_size);
2315dd0baa8Skettenis 		return;
2325dd0baa8Skettenis 	}
2335dd0baa8Skettenis 
2345dd0baa8Skettenis 	if (ep->num_channels >= AFK_MAX_CHANNEL) {
2355dd0baa8Skettenis 		dev_err(ep->dcp->dev, "AFK[ep:%02x]: too many enabled services!\n",
2365dd0baa8Skettenis 			ep->endpoint);
2375dd0baa8Skettenis 		return;
2385dd0baa8Skettenis 	}
2395dd0baa8Skettenis 
2409bf77b47Skettenis 	strscpy(name, payload, sizeof(name));
2415dd0baa8Skettenis 
2425dd0baa8Skettenis 	/*
2435dd0baa8Skettenis 	 * in DCP firmware 13.2 DCP reports interface-name as name which starts
2445dd0baa8Skettenis 	 * with "dispext%d" using -1 s ID for "dcp". In the 12.3 firmware
2455dd0baa8Skettenis 	 * EPICProviderClass was used. If the init call has props parse them and
2465dd0baa8Skettenis 	 * use EPICProviderClass to match the service.
2475dd0baa8Skettenis 	 */
2485dd0baa8Skettenis 	if (props_size > 36) {
2495dd0baa8Skettenis 		int ret = parse(props, props_size, &ctx);
2505dd0baa8Skettenis 		if (ret) {
2515dd0baa8Skettenis 			dev_err(ep->dcp->dev,
2525dd0baa8Skettenis 				"AFK[ep:%02x]: Failed to parse service init props for %s\n",
2535dd0baa8Skettenis 				ep->endpoint, name);
2545dd0baa8Skettenis 			return;
2555dd0baa8Skettenis 		}
2565dd0baa8Skettenis 		ret = parse_epic_service_init(&ctx, &epic_name, &epic_class, &epic_unit);
2575dd0baa8Skettenis 		if (ret) {
2585dd0baa8Skettenis 			dev_err(ep->dcp->dev,
2595dd0baa8Skettenis 				"AFK[ep:%02x]: failed to extract init props: %d\n",
2605dd0baa8Skettenis 				ep->endpoint, ret);
2615dd0baa8Skettenis 			return;
2625dd0baa8Skettenis 		}
2635dd0baa8Skettenis 		service_name = epic_class;
2645dd0baa8Skettenis 	} else {
2655dd0baa8Skettenis             service_name = name;
2665dd0baa8Skettenis         }
2675dd0baa8Skettenis 
2685dd0baa8Skettenis 	ops = afk_match_service(ep, service_name);
2695dd0baa8Skettenis 	if (!ops) {
2705dd0baa8Skettenis 		dev_err(ep->dcp->dev,
2715dd0baa8Skettenis 			"AFK[ep:%02x]: unable to match service %s on channel %d\n",
2725dd0baa8Skettenis 			ep->endpoint, service_name, channel);
2735dd0baa8Skettenis 		goto free;
2745dd0baa8Skettenis 	}
2755dd0baa8Skettenis 
2765dd0baa8Skettenis 	ch_idx = ep->num_channels++;
2775dd0baa8Skettenis 	mtx_init(&ep->services[ch_idx].lock, IPL_TTY);
2785dd0baa8Skettenis 	ep->services[ch_idx].enabled = true;
2795dd0baa8Skettenis 	ep->services[ch_idx].ops = ops;
2805dd0baa8Skettenis 	ep->services[ch_idx].ep = ep;
2815dd0baa8Skettenis 	ep->services[ch_idx].channel = channel;
2825dd0baa8Skettenis 	ep->services[ch_idx].cmd_tag = 0;
2835dd0baa8Skettenis 	ops->init(&ep->services[ch_idx], epic_name, epic_class, epic_unit);
2845dd0baa8Skettenis 	dev_info(ep->dcp->dev, "AFK[ep:%02x]: new service %s on channel %d\n",
2855dd0baa8Skettenis 		 ep->endpoint, service_name, channel);
2865dd0baa8Skettenis free:
2875dd0baa8Skettenis 	kfree(epic_name);
2885dd0baa8Skettenis 	kfree(epic_class);
2895dd0baa8Skettenis }
2905dd0baa8Skettenis 
afk_recv_handle_teardown(struct apple_dcp_afkep * ep,u32 channel)2915dd0baa8Skettenis static void afk_recv_handle_teardown(struct apple_dcp_afkep *ep, u32 channel)
2925dd0baa8Skettenis {
2935dd0baa8Skettenis 	struct apple_epic_service *service;
2945dd0baa8Skettenis 	const struct apple_epic_service_ops *ops;
2955dd0baa8Skettenis 	unsigned long flags;
2965dd0baa8Skettenis 
2975dd0baa8Skettenis 	service = afk_epic_find_service(ep, channel);
2985dd0baa8Skettenis 	if (!service) {
2995dd0baa8Skettenis 		dev_warn(ep->dcp->dev, "AFK[ep:%02x]: teardown for disabled channel %u\n",
3005dd0baa8Skettenis 			 ep->endpoint, channel);
3015dd0baa8Skettenis 		return;
3025dd0baa8Skettenis 	}
3035dd0baa8Skettenis 
3045dd0baa8Skettenis 	// TODO: think through what locking is necessary
3055dd0baa8Skettenis 	spin_lock_irqsave(&service->lock, flags);
3065dd0baa8Skettenis 	service->enabled = false;
3075dd0baa8Skettenis 	ops = service->ops;
3085dd0baa8Skettenis 	spin_unlock_irqrestore(&service->lock, flags);
3095dd0baa8Skettenis 
3105dd0baa8Skettenis 	if (ops->teardown)
3115dd0baa8Skettenis 		ops->teardown(service);
3125dd0baa8Skettenis }
3135dd0baa8Skettenis 
afk_recv_handle_reply(struct apple_dcp_afkep * ep,u32 channel,u16 tag,void * payload,size_t payload_size)3145dd0baa8Skettenis static void afk_recv_handle_reply(struct apple_dcp_afkep *ep, u32 channel,
3155dd0baa8Skettenis 				  u16 tag, void *payload, size_t payload_size)
3165dd0baa8Skettenis {
3175dd0baa8Skettenis 	struct epic_cmd *cmd = payload;
3185dd0baa8Skettenis 	struct apple_epic_service *service;
3195dd0baa8Skettenis 	unsigned long flags;
3205dd0baa8Skettenis 	u8 idx = tag & 0xff;
3215dd0baa8Skettenis 	void *rxbuf, *txbuf;
3225dd0baa8Skettenis 	dma_addr_t rxbuf_dma, txbuf_dma;
3235dd0baa8Skettenis 	size_t rxlen, txlen;
3245dd0baa8Skettenis 
3255dd0baa8Skettenis 	service = afk_epic_find_service(ep, channel);
3265dd0baa8Skettenis 	if (!service) {
3275dd0baa8Skettenis 		dev_warn(ep->dcp->dev, "AFK[ep:%02x]: command reply on disabled channel %u\n",
3285dd0baa8Skettenis 			 ep->endpoint, channel);
3295dd0baa8Skettenis 		return;
3305dd0baa8Skettenis 	}
3315dd0baa8Skettenis 
3325dd0baa8Skettenis 	if (payload_size < sizeof(*cmd)) {
3335dd0baa8Skettenis 		dev_err(ep->dcp->dev,
3345dd0baa8Skettenis 			"AFK[ep:%02x]: command reply on channel %d too small: %ld\n",
3355dd0baa8Skettenis 			ep->endpoint, channel, payload_size);
3365dd0baa8Skettenis 		return;
3375dd0baa8Skettenis 	}
3385dd0baa8Skettenis 
3395dd0baa8Skettenis 	if (idx >= MAX_PENDING_CMDS) {
3405dd0baa8Skettenis 		dev_err(ep->dcp->dev,
3415dd0baa8Skettenis 			"AFK[ep:%02x]: command reply on channel %d out of range: %d\n",
3425dd0baa8Skettenis 			ep->endpoint, channel, idx);
3435dd0baa8Skettenis 		return;
3445dd0baa8Skettenis 	}
3455dd0baa8Skettenis 
3465dd0baa8Skettenis 	spin_lock_irqsave(&service->lock, flags);
3475dd0baa8Skettenis 	if (service->cmds[idx].done) {
3485dd0baa8Skettenis 		dev_err(ep->dcp->dev,
3495dd0baa8Skettenis 			"AFK[ep:%02x]: command reply on channel %d already handled\n",
3505dd0baa8Skettenis 			ep->endpoint, channel);
3515dd0baa8Skettenis 		spin_unlock_irqrestore(&service->lock, flags);
3525dd0baa8Skettenis 		return;
3535dd0baa8Skettenis 	}
3545dd0baa8Skettenis 
3555dd0baa8Skettenis 	if (tag != service->cmds[idx].tag) {
3565dd0baa8Skettenis 		dev_err(ep->dcp->dev,
3575dd0baa8Skettenis 			"AFK[ep:%02x]: command reply on channel %d has invalid tag: expected 0x%04x != 0x%04x\n",
3585dd0baa8Skettenis 			ep->endpoint, channel, tag, service->cmds[idx].tag);
3595dd0baa8Skettenis 		spin_unlock_irqrestore(&service->lock, flags);
3605dd0baa8Skettenis 		return;
3615dd0baa8Skettenis 	}
3625dd0baa8Skettenis 
3635dd0baa8Skettenis 	service->cmds[idx].done = true;
3645dd0baa8Skettenis 	service->cmds[idx].retcode = le32_to_cpu(cmd->retcode);
3655dd0baa8Skettenis 	if (service->cmds[idx].free_on_ack) {
3665dd0baa8Skettenis 		/* defer freeing until we're no longer in atomic context */
3675dd0baa8Skettenis 		rxbuf = service->cmds[idx].rxbuf;
3685dd0baa8Skettenis 		txbuf = service->cmds[idx].txbuf;
3695dd0baa8Skettenis 		rxlen = service->cmds[idx].rxlen;
3705dd0baa8Skettenis 		txlen = service->cmds[idx].txlen;
3715dd0baa8Skettenis 		rxbuf_dma = service->cmds[idx].rxbuf_dma;
3725dd0baa8Skettenis 		txbuf_dma = service->cmds[idx].txbuf_dma;
3735dd0baa8Skettenis 		bitmap_release_region(service->cmd_map, idx, 0);
3745dd0baa8Skettenis 	} else {
3755dd0baa8Skettenis 		rxbuf = txbuf = NULL;
3765dd0baa8Skettenis 		rxlen = txlen = 0;
3775dd0baa8Skettenis 	}
3785dd0baa8Skettenis 	if (service->cmds[idx].completion)
3795dd0baa8Skettenis 		complete(service->cmds[idx].completion);
3805dd0baa8Skettenis 
3815dd0baa8Skettenis 	spin_unlock_irqrestore(&service->lock, flags);
3825dd0baa8Skettenis 
3835dd0baa8Skettenis 	if (rxbuf && rxlen)
3845dd0baa8Skettenis 		dma_free_coherent(ep->dcp->dev, rxlen, rxbuf, rxbuf_dma);
3855dd0baa8Skettenis 	if (txbuf && txlen)
3865dd0baa8Skettenis 		dma_free_coherent(ep->dcp->dev, txlen, txbuf, txbuf_dma);
3875dd0baa8Skettenis }
3885dd0baa8Skettenis 
3895dd0baa8Skettenis struct epic_std_service_ap_call {
3905dd0baa8Skettenis 	__le32 unk0;
3915dd0baa8Skettenis 	__le32 unk1;
3925dd0baa8Skettenis 	__le32 type;
3935dd0baa8Skettenis 	__le32 len;
3945dd0baa8Skettenis 	__le32 magic;
3955dd0baa8Skettenis 	u8 _unk[48];
3965dd0baa8Skettenis } __attribute__((packed));
3975dd0baa8Skettenis 
afk_recv_handle_std_service(struct apple_dcp_afkep * ep,u32 channel,u32 type,struct epic_hdr * ehdr,struct epic_sub_hdr * eshdr,void * payload,size_t payload_size)3985dd0baa8Skettenis static void afk_recv_handle_std_service(struct apple_dcp_afkep *ep, u32 channel,
3995dd0baa8Skettenis 					u32 type, struct epic_hdr *ehdr,
4005dd0baa8Skettenis 					struct epic_sub_hdr *eshdr,
4015dd0baa8Skettenis 					void *payload, size_t payload_size)
4025dd0baa8Skettenis {
4035dd0baa8Skettenis 	struct apple_epic_service *service = afk_epic_find_service(ep, channel);
4045dd0baa8Skettenis 
4055dd0baa8Skettenis 	if (!service) {
4065dd0baa8Skettenis 		dev_warn(ep->dcp->dev,
4075dd0baa8Skettenis 			 "AFK[ep:%02x]: std service notify on disabled channel %u\n",
4085dd0baa8Skettenis 			 ep->endpoint, channel);
4095dd0baa8Skettenis 		return;
4105dd0baa8Skettenis 	}
4115dd0baa8Skettenis 
4125dd0baa8Skettenis 	if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_NOTIFY) {
4135dd0baa8Skettenis 		struct epic_std_service_ap_call *call = payload;
4145dd0baa8Skettenis 		size_t call_size;
4155dd0baa8Skettenis 		void *reply;
4165dd0baa8Skettenis 		int ret;
4175dd0baa8Skettenis 
4185dd0baa8Skettenis 		if (payload_size < sizeof(*call))
4195dd0baa8Skettenis 			return;
4205dd0baa8Skettenis 
4215dd0baa8Skettenis 		call_size = le32_to_cpu(call->len);
4225dd0baa8Skettenis 		if (payload_size < sizeof(*call) + call_size)
4235dd0baa8Skettenis 			return;
4245dd0baa8Skettenis 
4255dd0baa8Skettenis 		if (!service->ops->call)
4265dd0baa8Skettenis 			return;
4275dd0baa8Skettenis 		reply = kzalloc(payload_size, GFP_KERNEL);
4285dd0baa8Skettenis 		if (!reply)
4295dd0baa8Skettenis 			return;
4305dd0baa8Skettenis 
4315dd0baa8Skettenis 		ret = service->ops->call(service, le32_to_cpu(call->type),
4325dd0baa8Skettenis 					 payload + sizeof(*call), call_size,
4335dd0baa8Skettenis 					 reply + sizeof(*call), call_size);
4345dd0baa8Skettenis 		if (ret) {
4355dd0baa8Skettenis 			kfree(reply);
4365dd0baa8Skettenis 			return;
4375dd0baa8Skettenis 		}
4385dd0baa8Skettenis 
4395dd0baa8Skettenis 		memcpy(reply, call, sizeof(*call));
4405dd0baa8Skettenis 		afk_send_epic(ep, channel, le16_to_cpu(eshdr->tag),
4415dd0baa8Skettenis 			      EPIC_TYPE_NOTIFY_ACK, EPIC_CAT_REPLY,
4425dd0baa8Skettenis 			      EPIC_SUBTYPE_STD_SERVICE, reply, payload_size);
4435dd0baa8Skettenis 		kfree(reply);
4445dd0baa8Skettenis 
4455dd0baa8Skettenis 		return;
4465dd0baa8Skettenis 	}
4475dd0baa8Skettenis 
4485dd0baa8Skettenis 	if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_REPORT) {
4495dd0baa8Skettenis 		if (service->ops->report)
4505dd0baa8Skettenis 			service->ops->report(service, le16_to_cpu(eshdr->type),
4515dd0baa8Skettenis 					     payload, payload_size);
4525dd0baa8Skettenis 		return;
4535dd0baa8Skettenis 	}
4545dd0baa8Skettenis 
4555dd0baa8Skettenis 	dev_err(ep->dcp->dev,
4565dd0baa8Skettenis 		"AFK[ep:%02x]: channel %d received unhandled standard service message: %x / %x\n",
4575dd0baa8Skettenis 		ep->endpoint, channel, type, eshdr->category);
4585dd0baa8Skettenis 	print_hex_dump(KERN_INFO, "AFK: ", DUMP_PREFIX_NONE, 16, 1, payload,
4595dd0baa8Skettenis 				   payload_size, true);
4605dd0baa8Skettenis }
4615dd0baa8Skettenis 
afk_recv_handle(struct apple_dcp_afkep * ep,u32 channel,u32 type,u8 * data,size_t data_size)4625dd0baa8Skettenis static void afk_recv_handle(struct apple_dcp_afkep *ep, u32 channel, u32 type,
4635dd0baa8Skettenis 			    u8 *data, size_t data_size)
4645dd0baa8Skettenis {
4655dd0baa8Skettenis 	struct apple_epic_service *service;
4665dd0baa8Skettenis 	struct epic_hdr *ehdr = (struct epic_hdr *)data;
4675dd0baa8Skettenis 	struct epic_sub_hdr *eshdr =
4685dd0baa8Skettenis 		(struct epic_sub_hdr *)(data + sizeof(*ehdr));
4695dd0baa8Skettenis 	u16 subtype = le16_to_cpu(eshdr->type);
4705dd0baa8Skettenis 	u8 *payload = data + sizeof(*ehdr) + sizeof(*eshdr);
4715dd0baa8Skettenis 	size_t payload_size;
4725dd0baa8Skettenis 
4735dd0baa8Skettenis 	if (data_size < sizeof(*ehdr) + sizeof(*eshdr)) {
4745dd0baa8Skettenis 		dev_err(ep->dcp->dev, "AFK[ep:%02x]: payload too small: %lx\n",
4755dd0baa8Skettenis 			ep->endpoint, data_size);
4765dd0baa8Skettenis 		return;
4775dd0baa8Skettenis 	}
4785dd0baa8Skettenis 	payload_size = data_size - sizeof(*ehdr) - sizeof(*eshdr);
4795dd0baa8Skettenis 
4805dd0baa8Skettenis 	trace_afk_recv_handle(ep, channel, type, data_size, ehdr, eshdr);
4815dd0baa8Skettenis 
4825dd0baa8Skettenis 	service = afk_epic_find_service(ep, channel);
4835dd0baa8Skettenis 
4845dd0baa8Skettenis 	if (!service) {
4855dd0baa8Skettenis 		if (type != EPIC_TYPE_NOTIFY && type != EPIC_TYPE_REPLY) {
4865dd0baa8Skettenis 			dev_err(ep->dcp->dev,
4875dd0baa8Skettenis 				"AFK[ep:%02x]: expected notify but got 0x%x on channel %d\n",
4885dd0baa8Skettenis 				ep->endpoint, type, channel);
4895dd0baa8Skettenis 			return;
4905dd0baa8Skettenis 		}
4915dd0baa8Skettenis 		if (eshdr->category != EPIC_CAT_REPORT) {
4925dd0baa8Skettenis 			dev_err(ep->dcp->dev,
4935dd0baa8Skettenis 				"AFK[ep:%02x]: expected report but got 0x%x on channel %d\n",
4945dd0baa8Skettenis 				ep->endpoint, eshdr->category, channel);
4955dd0baa8Skettenis 			return;
4965dd0baa8Skettenis 		}
4975dd0baa8Skettenis 		if (subtype == EPIC_SUBTYPE_TEARDOWN) {
4985dd0baa8Skettenis 			dev_dbg(ep->dcp->dev,
4995dd0baa8Skettenis 				"AFK[ep:%02x]: teardown without service on channel %d\n",
5005dd0baa8Skettenis 				ep->endpoint, channel);
5015dd0baa8Skettenis 			return;
5025dd0baa8Skettenis 		}
5035dd0baa8Skettenis 		if (subtype != EPIC_SUBTYPE_ANNOUNCE) {
5045dd0baa8Skettenis 			dev_err(ep->dcp->dev,
5055dd0baa8Skettenis 				"AFK[ep:%02x]: expected announce but got 0x%x on channel %d\n",
5065dd0baa8Skettenis 				ep->endpoint, subtype, channel);
5075dd0baa8Skettenis 			return;
5085dd0baa8Skettenis 		}
5095dd0baa8Skettenis 
5105dd0baa8Skettenis 		return afk_recv_handle_init(ep, channel, payload, payload_size);
5115dd0baa8Skettenis 	}
5125dd0baa8Skettenis 
5135dd0baa8Skettenis 	if (!service) {
5145dd0baa8Skettenis 		dev_err(ep->dcp->dev, "AFK[ep:%02x]: channel %d has no service\n",
5155dd0baa8Skettenis 			ep->endpoint, channel);
5165dd0baa8Skettenis 		return;
5175dd0baa8Skettenis 	}
5185dd0baa8Skettenis 
5195dd0baa8Skettenis 	if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_REPORT &&
5205dd0baa8Skettenis 	    subtype == EPIC_SUBTYPE_TEARDOWN)
5215dd0baa8Skettenis 		return afk_recv_handle_teardown(ep, channel);
5225dd0baa8Skettenis 
5235dd0baa8Skettenis 	if (type == EPIC_TYPE_REPLY && eshdr->category == EPIC_CAT_REPLY)
5245dd0baa8Skettenis 		return afk_recv_handle_reply(ep, channel,
5255dd0baa8Skettenis 					     le16_to_cpu(eshdr->tag), payload,
5265dd0baa8Skettenis 					     payload_size);
5275dd0baa8Skettenis 
5285dd0baa8Skettenis 	if (subtype == EPIC_SUBTYPE_STD_SERVICE)
5295dd0baa8Skettenis 		return afk_recv_handle_std_service(
5305dd0baa8Skettenis 			ep, channel, type, ehdr, eshdr, payload, payload_size);
5315dd0baa8Skettenis 
5325dd0baa8Skettenis 	dev_err(ep->dcp->dev, "AFK[ep:%02x]: channel %d received unhandled message "
5335dd0baa8Skettenis 		"(type %x subtype %x)\n", ep->endpoint, channel, type, subtype);
5345dd0baa8Skettenis 	print_hex_dump(KERN_INFO, "AFK: ", DUMP_PREFIX_NONE, 16, 1, payload,
5355dd0baa8Skettenis 				   payload_size, true);
5365dd0baa8Skettenis }
5375dd0baa8Skettenis 
afk_recv(struct apple_dcp_afkep * ep)5385dd0baa8Skettenis static bool afk_recv(struct apple_dcp_afkep *ep)
5395dd0baa8Skettenis {
5405dd0baa8Skettenis 	struct afk_qe *hdr;
5415dd0baa8Skettenis 	u32 rptr, wptr;
5425dd0baa8Skettenis 	u32 magic, size, channel, type;
5435dd0baa8Skettenis 
5445dd0baa8Skettenis 	if (!ep->rxbfr.ready) {
5455dd0baa8Skettenis 		dev_err(ep->dcp->dev, "AFK[ep:%02x]: got RECV but not ready\n",
5465dd0baa8Skettenis 			ep->endpoint);
5475dd0baa8Skettenis 		return false;
5485dd0baa8Skettenis 	}
5495dd0baa8Skettenis 
5505dd0baa8Skettenis 	rptr = le32_to_cpu(ep->rxbfr.hdr->rptr);
5515dd0baa8Skettenis 	wptr = le32_to_cpu(ep->rxbfr.hdr->wptr);
5525dd0baa8Skettenis 	trace_afk_recv_rwptr_pre(ep, rptr, wptr);
5535dd0baa8Skettenis 
5545dd0baa8Skettenis 	if (rptr == wptr)
5555dd0baa8Skettenis 		return false;
5565dd0baa8Skettenis 
5575dd0baa8Skettenis 	if (rptr > (ep->rxbfr.bufsz - sizeof(*hdr))) {
5585dd0baa8Skettenis 		dev_warn(ep->dcp->dev,
5595dd0baa8Skettenis 			 "AFK[ep:%02x]: rptr out of bounds: 0x%x > 0x%lx\n",
5605dd0baa8Skettenis 			 ep->endpoint, rptr, ep->rxbfr.bufsz - sizeof(*hdr));
5615dd0baa8Skettenis 		return false;
5625dd0baa8Skettenis 	}
5635dd0baa8Skettenis 
5645dd0baa8Skettenis 	dma_rmb();
5655dd0baa8Skettenis 
5665dd0baa8Skettenis 	hdr = ep->rxbfr.buf + rptr;
5675dd0baa8Skettenis 	magic = le32_to_cpu(hdr->magic);
5685dd0baa8Skettenis 	size = le32_to_cpu(hdr->size);
5695dd0baa8Skettenis 	trace_afk_recv_qe(ep, rptr, magic, size);
5705dd0baa8Skettenis 
5715dd0baa8Skettenis 	if (magic != QE_MAGIC) {
5725dd0baa8Skettenis 		dev_warn(ep->dcp->dev, "AFK[ep:%02x]: invalid queue entry magic: 0x%x\n",
5735dd0baa8Skettenis 			 ep->endpoint, magic);
5745dd0baa8Skettenis 		return false;
5755dd0baa8Skettenis 	}
5765dd0baa8Skettenis 
5775dd0baa8Skettenis 	/*
5785dd0baa8Skettenis 	 * If there's not enough space for the payload the co-processor inserted
5795dd0baa8Skettenis 	 * the current dummy queue entry and we have to advance to the next one
5805dd0baa8Skettenis 	 * which will contain the real data.
5815dd0baa8Skettenis 	*/
5825dd0baa8Skettenis 	if (rptr + size + sizeof(*hdr) > ep->rxbfr.bufsz) {
5835dd0baa8Skettenis 		rptr = 0;
5845dd0baa8Skettenis 		hdr = ep->rxbfr.buf + rptr;
5855dd0baa8Skettenis 		magic = le32_to_cpu(hdr->magic);
5865dd0baa8Skettenis 		size = le32_to_cpu(hdr->size);
5875dd0baa8Skettenis 		trace_afk_recv_qe(ep, rptr, magic, size);
5885dd0baa8Skettenis 
5895dd0baa8Skettenis 		if (magic != QE_MAGIC) {
5905dd0baa8Skettenis 			dev_warn(ep->dcp->dev,
5915dd0baa8Skettenis 				 "AFK[ep:%02x]: invalid next queue entry magic: 0x%x\n",
5925dd0baa8Skettenis 				 ep->endpoint, magic);
5935dd0baa8Skettenis 			return false;
5945dd0baa8Skettenis 		}
5955dd0baa8Skettenis 
5965dd0baa8Skettenis 		ep->rxbfr.hdr->rptr = cpu_to_le32(rptr);
5975dd0baa8Skettenis 	}
5985dd0baa8Skettenis 
5995dd0baa8Skettenis 	if (rptr + size + sizeof(*hdr) > ep->rxbfr.bufsz) {
6005dd0baa8Skettenis 		dev_warn(ep->dcp->dev,
6015dd0baa8Skettenis 			 "AFK[ep:%02x]: queue entry out of bounds: 0x%lx > 0x%lx\n",
6025dd0baa8Skettenis 			 ep->endpoint, rptr + size + sizeof(*hdr), ep->rxbfr.bufsz);
6035dd0baa8Skettenis 		return false;
6045dd0baa8Skettenis 	}
6055dd0baa8Skettenis 
6065dd0baa8Skettenis 	channel = le32_to_cpu(hdr->channel);
6075dd0baa8Skettenis 	type = le32_to_cpu(hdr->type);
6085dd0baa8Skettenis 
6095dd0baa8Skettenis 	rptr = ALIGN(rptr + sizeof(*hdr) + size, 1 << BLOCK_SHIFT);
6105dd0baa8Skettenis 	if (WARN_ON(rptr > ep->rxbfr.bufsz))
6115dd0baa8Skettenis 		rptr = 0;
6125dd0baa8Skettenis 	if (rptr == ep->rxbfr.bufsz)
6135dd0baa8Skettenis 		rptr = 0;
6145dd0baa8Skettenis 
6155dd0baa8Skettenis 	dma_mb();
6165dd0baa8Skettenis 
6175dd0baa8Skettenis 	ep->rxbfr.hdr->rptr = cpu_to_le32(rptr);
6185dd0baa8Skettenis 	trace_afk_recv_rwptr_post(ep, rptr, wptr);
6195dd0baa8Skettenis 
6205dd0baa8Skettenis 	/*
6215dd0baa8Skettenis 	 * TODO: this is theoretically unsafe since DCP could overwrite data
6225dd0baa8Skettenis 	 *       after the read pointer was updated above. Do it anyway since
6235dd0baa8Skettenis 	 *       it avoids 2 problems in the DCP tracer:
6245dd0baa8Skettenis 	 *       1. the tracer sees replies before the the notifies from dcp
6255dd0baa8Skettenis 	 *       2. the tracer tries to read buffers after they are unmapped.
6265dd0baa8Skettenis 	 */
6275dd0baa8Skettenis 	afk_recv_handle(ep, channel, type, hdr->data, size);
6285dd0baa8Skettenis 
6295dd0baa8Skettenis 	return true;
6305dd0baa8Skettenis }
6315dd0baa8Skettenis 
afk_receive_message_worker(struct work_struct * work_)6325dd0baa8Skettenis static void afk_receive_message_worker(struct work_struct *work_)
6335dd0baa8Skettenis {
6345dd0baa8Skettenis 	struct afk_receive_message_work *work;
6355dd0baa8Skettenis 	u16 type;
6365dd0baa8Skettenis 
6375dd0baa8Skettenis 	work = container_of(work_, struct afk_receive_message_work, work);
6385dd0baa8Skettenis 
6395dd0baa8Skettenis 	type = FIELD_GET(RBEP_TYPE, work->message);
6405dd0baa8Skettenis 	switch (type) {
6415dd0baa8Skettenis 	case RBEP_INIT_ACK:
6425dd0baa8Skettenis 		break;
6435dd0baa8Skettenis 
6445dd0baa8Skettenis 	case RBEP_START_ACK:
6455dd0baa8Skettenis 		complete_all(&work->ep->started);
6465dd0baa8Skettenis 		break;
6475dd0baa8Skettenis 
6485dd0baa8Skettenis 	case RBEP_SHUTDOWN_ACK:
6495dd0baa8Skettenis 		complete_all(&work->ep->stopped);
6505dd0baa8Skettenis 		break;
6515dd0baa8Skettenis 
6525dd0baa8Skettenis 	case RBEP_GETBUF:
6535dd0baa8Skettenis 		afk_getbuf(work->ep, work->message);
6545dd0baa8Skettenis 		break;
6555dd0baa8Skettenis 
6565dd0baa8Skettenis 	case RBEP_INIT_TX:
6575dd0baa8Skettenis 		afk_init_rxtx(work->ep, work->message, &work->ep->txbfr);
6585dd0baa8Skettenis 		break;
6595dd0baa8Skettenis 
6605dd0baa8Skettenis 	case RBEP_INIT_RX:
6615dd0baa8Skettenis 		afk_init_rxtx(work->ep, work->message, &work->ep->rxbfr);
6625dd0baa8Skettenis 		break;
6635dd0baa8Skettenis 
6645dd0baa8Skettenis 	case RBEP_RECV:
6655dd0baa8Skettenis 		while (afk_recv(work->ep))
6665dd0baa8Skettenis 			;
6675dd0baa8Skettenis 		break;
6685dd0baa8Skettenis 
6695dd0baa8Skettenis 	default:
6705dd0baa8Skettenis 		dev_err(work->ep->dcp->dev,
6715dd0baa8Skettenis 			"Received unknown AFK message type: 0x%x\n", type);
6725dd0baa8Skettenis 	}
6735dd0baa8Skettenis 
6745dd0baa8Skettenis 	kfree(work);
6755dd0baa8Skettenis }
6765dd0baa8Skettenis 
afk_receive_message(struct apple_dcp_afkep * ep,u64 message)6775dd0baa8Skettenis int afk_receive_message(struct apple_dcp_afkep *ep, u64 message)
6785dd0baa8Skettenis {
6795dd0baa8Skettenis 	struct afk_receive_message_work *work;
6805dd0baa8Skettenis 
6815dd0baa8Skettenis 	// TODO: comment why decoupling from rtkit thread is required here
6825dd0baa8Skettenis 	work = kzalloc(sizeof(*work), GFP_KERNEL);
6835dd0baa8Skettenis 	if (!work)
6845dd0baa8Skettenis 		return -ENOMEM;
6855dd0baa8Skettenis 
6865dd0baa8Skettenis 	work->ep = ep;
6875dd0baa8Skettenis 	work->message = message;
6885dd0baa8Skettenis 	INIT_WORK(&work->work, afk_receive_message_worker);
6895dd0baa8Skettenis 	queue_work(ep->wq, &work->work);
6905dd0baa8Skettenis 
6915dd0baa8Skettenis 	return 0;
6925dd0baa8Skettenis }
6935dd0baa8Skettenis 
afk_send_epic(struct apple_dcp_afkep * ep,u32 channel,u16 tag,enum epic_type etype,enum epic_category ecat,u8 stype,const void * payload,size_t payload_len)6945dd0baa8Skettenis int afk_send_epic(struct apple_dcp_afkep *ep, u32 channel, u16 tag,
6955dd0baa8Skettenis 		  enum epic_type etype, enum epic_category ecat, u8 stype,
6965dd0baa8Skettenis 		  const void *payload, size_t payload_len)
6975dd0baa8Skettenis {
6985dd0baa8Skettenis 	u32 rptr, wptr;
6995dd0baa8Skettenis 	struct afk_qe *hdr, *hdr2;
7005dd0baa8Skettenis 	struct epic_hdr *ehdr;
7015dd0baa8Skettenis 	struct epic_sub_hdr *eshdr;
7025dd0baa8Skettenis 	unsigned long flags;
7035dd0baa8Skettenis 	size_t total_epic_size, total_size;
7045dd0baa8Skettenis 	int ret;
7055dd0baa8Skettenis 
7065dd0baa8Skettenis 	spin_lock_irqsave(&ep->lock, flags);
7075dd0baa8Skettenis 
7085dd0baa8Skettenis 	dma_rmb();
7095dd0baa8Skettenis 	rptr = le32_to_cpu(ep->txbfr.hdr->rptr);
7105dd0baa8Skettenis 	wptr = le32_to_cpu(ep->txbfr.hdr->wptr);
7115dd0baa8Skettenis 	trace_afk_send_rwptr_pre(ep, rptr, wptr);
7125dd0baa8Skettenis 	total_epic_size = sizeof(*ehdr) + sizeof(*eshdr) + payload_len;
7135dd0baa8Skettenis 	total_size = sizeof(*hdr) + total_epic_size;
7145dd0baa8Skettenis 
7155dd0baa8Skettenis 	hdr = hdr2 = NULL;
7165dd0baa8Skettenis 
7175dd0baa8Skettenis 	/*
7185dd0baa8Skettenis 	 * We need to figure out how to place the entire headers and payload
7195dd0baa8Skettenis 	 * into the ring buffer:
7205dd0baa8Skettenis 	 * - If the write pointer is in front of the read pointer we just need
7215dd0baa8Skettenis 	 *   enough space inbetween to store everything.
7225dd0baa8Skettenis 	 * - If the read pointer has already wrapper around the end of the
7235dd0baa8Skettenis 	 *   buffer we can
7245dd0baa8Skettenis 	 *    a) either store the entire payload at the writer pointer if
7255dd0baa8Skettenis 	 *       there's enough space until the end,
7265dd0baa8Skettenis 	 *    b) or just store the queue entry at the write pointer to indicate
7275dd0baa8Skettenis 	 *       that we need to wrap to the start and then store the headers
7285dd0baa8Skettenis 	 *       and the payload at the beginning of the buffer. The queue
7295dd0baa8Skettenis 	 *       header has to be store twice in this case.
7305dd0baa8Skettenis 	 * In either case we have to ensure that there's always enough space
7315dd0baa8Skettenis 	 * so that we don't accidentally overwrite other buffers.
7325dd0baa8Skettenis 	 */
7335dd0baa8Skettenis 	if (wptr < rptr) {
7345dd0baa8Skettenis 		/*
7355dd0baa8Skettenis 		 * If wptr < rptr we can't wrap around and only have to make
7365dd0baa8Skettenis 		 * sure that there's enough space for the entire payload.
7375dd0baa8Skettenis 		 */
7385dd0baa8Skettenis 		if (wptr + total_size > rptr) {
7395dd0baa8Skettenis 			ret = -ENOMEM;
7405dd0baa8Skettenis 			goto out;
7415dd0baa8Skettenis 		}
7425dd0baa8Skettenis 
7435dd0baa8Skettenis 		hdr = ep->txbfr.buf + wptr;
7445dd0baa8Skettenis 		wptr += sizeof(*hdr);
7455dd0baa8Skettenis 	} else {
7465dd0baa8Skettenis 		/* We need enough space to place at least a queue entry */
7475dd0baa8Skettenis 		if (wptr + sizeof(*hdr) > ep->txbfr.bufsz) {
7485dd0baa8Skettenis 			ret = -ENOMEM;
7495dd0baa8Skettenis 			goto out;
7505dd0baa8Skettenis 		}
7515dd0baa8Skettenis 
7525dd0baa8Skettenis 		/*
7535dd0baa8Skettenis 		 * If we can place a single queue entry but not the full payload
7545dd0baa8Skettenis 		 * we need to place one queue entry at the end of the ring
7555dd0baa8Skettenis 		 * buffer and then another one together with the entire
7565dd0baa8Skettenis 		 * payload at the beginning.
7575dd0baa8Skettenis 		 */
7585dd0baa8Skettenis 		if (wptr + total_size > ep->txbfr.bufsz) {
7595dd0baa8Skettenis 			/*
7605dd0baa8Skettenis 			 * Ensure there's space for the  queue entry at the
7615dd0baa8Skettenis 			 * beginning
7625dd0baa8Skettenis 			 */
7635dd0baa8Skettenis 			if (sizeof(*hdr) > rptr) {
7645dd0baa8Skettenis 				ret = -ENOMEM;
7655dd0baa8Skettenis 				goto out;
7665dd0baa8Skettenis 			}
7675dd0baa8Skettenis 
7685dd0baa8Skettenis 			/*
7695dd0baa8Skettenis 			 * Place two queue entries to indicate we want to wrap
7705dd0baa8Skettenis 			 * around to the firmware.
7715dd0baa8Skettenis 			 */
7725dd0baa8Skettenis 			hdr = ep->txbfr.buf + wptr;
7735dd0baa8Skettenis 			hdr2 = ep->txbfr.buf;
7745dd0baa8Skettenis 			wptr = sizeof(*hdr);
7755dd0baa8Skettenis 
7765dd0baa8Skettenis 			/* Ensure there's enough space for the entire payload */
7775dd0baa8Skettenis 			if (wptr + total_epic_size > rptr) {
7785dd0baa8Skettenis 				ret = -ENOMEM;
7795dd0baa8Skettenis 				goto out;
7805dd0baa8Skettenis 			}
7815dd0baa8Skettenis 		} else {
7825dd0baa8Skettenis 			/* We have enough space to place the entire payload */
7835dd0baa8Skettenis 			hdr = ep->txbfr.buf + wptr;
7845dd0baa8Skettenis 			wptr += sizeof(*hdr);
7855dd0baa8Skettenis 		}
7865dd0baa8Skettenis 	}
7875dd0baa8Skettenis 	/*
7885dd0baa8Skettenis 	 * At this point we're guaranteed that hdr (and possibly hdr2) point
7895dd0baa8Skettenis 	 * to a buffer large enough to fit the queue entry and that we have
7905dd0baa8Skettenis 	 * enough space at wptr to store the payload.
7915dd0baa8Skettenis 	 */
7925dd0baa8Skettenis 
7935dd0baa8Skettenis 	hdr->magic = cpu_to_le32(QE_MAGIC);
7945dd0baa8Skettenis 	hdr->size = cpu_to_le32(total_epic_size);
7955dd0baa8Skettenis 	hdr->channel = cpu_to_le32(channel);
7965dd0baa8Skettenis 	hdr->type = cpu_to_le32(etype);
7975dd0baa8Skettenis 	if (hdr2)
7985dd0baa8Skettenis 		memcpy(hdr2, hdr, sizeof(*hdr));
7995dd0baa8Skettenis 
8005dd0baa8Skettenis 	ehdr = ep->txbfr.buf + wptr;
8015dd0baa8Skettenis 	memset(ehdr, 0, sizeof(*ehdr));
8025dd0baa8Skettenis 	ehdr->version = 2;
8035dd0baa8Skettenis 	ehdr->seq = cpu_to_le16(ep->qe_seq++);
8045dd0baa8Skettenis 	ehdr->timestamp = cpu_to_le64(0);
8055dd0baa8Skettenis 	wptr += sizeof(*ehdr);
8065dd0baa8Skettenis 
8075dd0baa8Skettenis 	eshdr = ep->txbfr.buf + wptr;
8085dd0baa8Skettenis 	memset(eshdr, 0, sizeof(*eshdr));
8095dd0baa8Skettenis 	eshdr->length = cpu_to_le32(payload_len);
8105dd0baa8Skettenis 	eshdr->version = 4;
8115dd0baa8Skettenis 	eshdr->category = ecat;
8125dd0baa8Skettenis 	eshdr->type = cpu_to_le16(stype);
8135dd0baa8Skettenis 	eshdr->timestamp = cpu_to_le64(0);
8145dd0baa8Skettenis 	eshdr->tag = cpu_to_le16(tag);
8155dd0baa8Skettenis 	if (ecat == EPIC_CAT_REPLY)
8165dd0baa8Skettenis 		eshdr->inline_len = cpu_to_le16(payload_len - 4);
8175dd0baa8Skettenis 	else
8185dd0baa8Skettenis 		eshdr->inline_len = cpu_to_le16(0);
8195dd0baa8Skettenis 	wptr += sizeof(*eshdr);
8205dd0baa8Skettenis 
8215dd0baa8Skettenis 	memcpy(ep->txbfr.buf + wptr, payload, payload_len);
8225dd0baa8Skettenis 	wptr += payload_len;
8235dd0baa8Skettenis 	wptr = ALIGN(wptr, 1 << BLOCK_SHIFT);
8245dd0baa8Skettenis 	if (wptr == ep->txbfr.bufsz)
8255dd0baa8Skettenis 		wptr = 0;
8265dd0baa8Skettenis 	trace_afk_send_rwptr_post(ep, rptr, wptr);
8275dd0baa8Skettenis 
8285dd0baa8Skettenis 	ep->txbfr.hdr->wptr = cpu_to_le32(wptr);
8295dd0baa8Skettenis 	afk_send(ep, FIELD_PREP(RBEP_TYPE, RBEP_SEND) |
8305dd0baa8Skettenis 			     FIELD_PREP(SEND_WPTR, wptr));
8315dd0baa8Skettenis 	ret = 0;
8325dd0baa8Skettenis 
8335dd0baa8Skettenis out:
8345dd0baa8Skettenis 	spin_unlock_irqrestore(&ep->lock, flags);
8355dd0baa8Skettenis 	return ret;
8365dd0baa8Skettenis }
8375dd0baa8Skettenis 
afk_send_command(struct apple_epic_service * service,u8 type,const void * payload,size_t payload_len,void * output,size_t output_len,u32 * retcode)8385dd0baa8Skettenis int afk_send_command(struct apple_epic_service *service, u8 type,
8395dd0baa8Skettenis 		     const void *payload, size_t payload_len, void *output,
8405dd0baa8Skettenis 		     size_t output_len, u32 *retcode)
8415dd0baa8Skettenis {
8425dd0baa8Skettenis 	struct epic_cmd cmd;
8435dd0baa8Skettenis 	void *rxbuf, *txbuf;
8445dd0baa8Skettenis 	dma_addr_t rxbuf_dma, txbuf_dma;
8455dd0baa8Skettenis 	unsigned long flags;
8465dd0baa8Skettenis 	int ret, idx;
8475dd0baa8Skettenis 	u16 tag;
8485dd0baa8Skettenis 	struct apple_dcp_afkep *ep = service->ep;
8495dd0baa8Skettenis 	DECLARE_COMPLETION_ONSTACK(completion);
8505dd0baa8Skettenis 
8515dd0baa8Skettenis 	rxbuf = dma_alloc_coherent(ep->dcp->dev, output_len, &rxbuf_dma,
8525dd0baa8Skettenis 				   GFP_KERNEL);
8535dd0baa8Skettenis 	if (!rxbuf)
8545dd0baa8Skettenis 		return -ENOMEM;
8555dd0baa8Skettenis 	txbuf = dma_alloc_coherent(ep->dcp->dev, payload_len, &txbuf_dma,
8565dd0baa8Skettenis 				   GFP_KERNEL);
8575dd0baa8Skettenis 	if (!txbuf) {
8585dd0baa8Skettenis 		ret = -ENOMEM;
8595dd0baa8Skettenis 		goto err_free_rxbuf;
8605dd0baa8Skettenis 	}
8615dd0baa8Skettenis 
8625dd0baa8Skettenis 	memcpy(txbuf, payload, payload_len);
8635dd0baa8Skettenis 
8645dd0baa8Skettenis 	memset(&cmd, 0, sizeof(cmd));
8655dd0baa8Skettenis 	cmd.retcode = cpu_to_le32(0);
8665dd0baa8Skettenis 	cmd.rxbuf = cpu_to_le64(rxbuf_dma);
8675dd0baa8Skettenis 	cmd.rxlen = cpu_to_le32(output_len);
8685dd0baa8Skettenis 	cmd.txbuf = cpu_to_le64(txbuf_dma);
8695dd0baa8Skettenis 	cmd.txlen = cpu_to_le32(payload_len);
8705dd0baa8Skettenis 
8715dd0baa8Skettenis 	spin_lock_irqsave(&service->lock, flags);
8725dd0baa8Skettenis 	idx = bitmap_find_free_region(service->cmd_map, MAX_PENDING_CMDS, 0);
8735dd0baa8Skettenis 	if (idx < 0) {
8745dd0baa8Skettenis 		ret = -ENOSPC;
8755dd0baa8Skettenis 		goto err_unlock;
8765dd0baa8Skettenis 	}
8775dd0baa8Skettenis 
8785dd0baa8Skettenis 	tag = (service->cmd_tag & 0xff) << 8;
8795dd0baa8Skettenis 	tag |= idx & 0xff;
8805dd0baa8Skettenis 	service->cmd_tag++;
8815dd0baa8Skettenis 
8825dd0baa8Skettenis 	service->cmds[idx].tag = tag;
8835dd0baa8Skettenis 	service->cmds[idx].rxbuf = rxbuf;
8845dd0baa8Skettenis 	service->cmds[idx].txbuf = txbuf;
8855dd0baa8Skettenis 	service->cmds[idx].rxbuf_dma = rxbuf_dma;
8865dd0baa8Skettenis 	service->cmds[idx].txbuf_dma = txbuf_dma;
8875dd0baa8Skettenis 	service->cmds[idx].rxlen = output_len;
8885dd0baa8Skettenis 	service->cmds[idx].txlen = payload_len;
8895dd0baa8Skettenis 	service->cmds[idx].free_on_ack = false;
8905dd0baa8Skettenis 	service->cmds[idx].done = false;
8915dd0baa8Skettenis 	service->cmds[idx].completion = &completion;
8925dd0baa8Skettenis 	init_completion(&completion);
8935dd0baa8Skettenis 
8945dd0baa8Skettenis 	spin_unlock_irqrestore(&service->lock, flags);
8955dd0baa8Skettenis 
8965dd0baa8Skettenis 	ret = afk_send_epic(service->ep, service->channel, tag,
8975dd0baa8Skettenis 			    EPIC_TYPE_COMMAND, EPIC_CAT_COMMAND, type, &cmd,
8985dd0baa8Skettenis 			    sizeof(cmd));
8995dd0baa8Skettenis 	if (ret)
9005dd0baa8Skettenis 		goto err_free_cmd;
9015dd0baa8Skettenis 
9025dd0baa8Skettenis 	ret = wait_for_completion_timeout(&completion,
9035dd0baa8Skettenis 					  msecs_to_jiffies(MSEC_PER_SEC));
9045dd0baa8Skettenis 
9055dd0baa8Skettenis 	if (ret <= 0) {
9065dd0baa8Skettenis 		spin_lock_irqsave(&service->lock, flags);
9075dd0baa8Skettenis 		/*
9085dd0baa8Skettenis 		 * Check again while we're inside the lock to make sure
9095dd0baa8Skettenis 		 * the command wasn't completed just after
9105dd0baa8Skettenis 		 * wait_for_completion_timeout returned.
9115dd0baa8Skettenis 		 */
9125dd0baa8Skettenis 		if (!service->cmds[idx].done) {
9135dd0baa8Skettenis 			service->cmds[idx].completion = NULL;
9145dd0baa8Skettenis 			service->cmds[idx].free_on_ack = true;
9155dd0baa8Skettenis 			spin_unlock_irqrestore(&service->lock, flags);
9165dd0baa8Skettenis 			return -ETIMEDOUT;
9175dd0baa8Skettenis 		}
9185dd0baa8Skettenis 		spin_unlock_irqrestore(&service->lock, flags);
9195dd0baa8Skettenis 	}
9205dd0baa8Skettenis 
9215dd0baa8Skettenis 	ret = 0;
9225dd0baa8Skettenis 	if (retcode)
9235dd0baa8Skettenis 		*retcode = service->cmds[idx].retcode;
9245dd0baa8Skettenis 	if (output && output_len)
9255dd0baa8Skettenis 		memcpy(output, rxbuf, output_len);
9265dd0baa8Skettenis 
9275dd0baa8Skettenis err_free_cmd:
9285dd0baa8Skettenis 	spin_lock_irqsave(&service->lock, flags);
9295dd0baa8Skettenis 	bitmap_release_region(service->cmd_map, idx, 0);
9305dd0baa8Skettenis err_unlock:
9315dd0baa8Skettenis 	spin_unlock_irqrestore(&service->lock, flags);
9325dd0baa8Skettenis 	dma_free_coherent(ep->dcp->dev, payload_len, txbuf, txbuf_dma);
9335dd0baa8Skettenis err_free_rxbuf:
9345dd0baa8Skettenis 	dma_free_coherent(ep->dcp->dev, output_len, rxbuf, rxbuf_dma);
9355dd0baa8Skettenis 	return ret;
9365dd0baa8Skettenis }
9375dd0baa8Skettenis 
afk_service_call(struct apple_epic_service * service,u16 group,u32 command,const void * data,size_t data_len,size_t data_pad,void * output,size_t output_len,size_t output_pad)9385dd0baa8Skettenis int afk_service_call(struct apple_epic_service *service, u16 group, u32 command,
9395dd0baa8Skettenis 		     const void *data, size_t data_len, size_t data_pad,
9405dd0baa8Skettenis 		     void *output, size_t output_len, size_t output_pad)
9415dd0baa8Skettenis {
9425dd0baa8Skettenis 	struct epic_service_call *call;
9435dd0baa8Skettenis 	void *bfr;
9445dd0baa8Skettenis 	size_t bfr_len = max(data_len + data_pad, output_len + output_pad) +
9455dd0baa8Skettenis 			 sizeof(*call);
9465dd0baa8Skettenis 	int ret;
9475dd0baa8Skettenis 	u32 retcode;
9485dd0baa8Skettenis 	u32 retlen;
9495dd0baa8Skettenis 
9505dd0baa8Skettenis 	bfr = kzalloc(bfr_len, GFP_KERNEL);
9515dd0baa8Skettenis 	if (!bfr)
9525dd0baa8Skettenis 		return -ENOMEM;
9535dd0baa8Skettenis 
9545dd0baa8Skettenis 	call = bfr;
9555dd0baa8Skettenis 
9565dd0baa8Skettenis 	memset(call, 0, sizeof(*call));
9575dd0baa8Skettenis 	call->group = cpu_to_le16(group);
9585dd0baa8Skettenis 	call->command = cpu_to_le32(command);
9595dd0baa8Skettenis 	call->data_len = cpu_to_le32(data_len + data_pad);
9605dd0baa8Skettenis 	call->magic = cpu_to_le32(EPIC_SERVICE_CALL_MAGIC);
9615dd0baa8Skettenis 
9625dd0baa8Skettenis 	memcpy(bfr + sizeof(*call), data, data_len);
9635dd0baa8Skettenis 
9645dd0baa8Skettenis 	ret = afk_send_command(service, EPIC_SUBTYPE_STD_SERVICE, bfr, bfr_len,
9655dd0baa8Skettenis 			       bfr, bfr_len, &retcode);
9665dd0baa8Skettenis 	if (ret)
9675dd0baa8Skettenis 		goto out;
9685dd0baa8Skettenis 	if (retcode) {
9695dd0baa8Skettenis 		ret = -EINVAL;
9705dd0baa8Skettenis 		goto out;
9715dd0baa8Skettenis 	}
9725dd0baa8Skettenis 	if (le32_to_cpu(call->magic) != EPIC_SERVICE_CALL_MAGIC ||
9735dd0baa8Skettenis 	    le16_to_cpu(call->group) != group ||
9745dd0baa8Skettenis 	    le32_to_cpu(call->command) != command) {
9755dd0baa8Skettenis 		ret = -EINVAL;
9765dd0baa8Skettenis 		goto out;
9775dd0baa8Skettenis 	}
9785dd0baa8Skettenis 
9795dd0baa8Skettenis 	retlen = le32_to_cpu(call->data_len);
9805dd0baa8Skettenis 	if (output_len < retlen)
9815dd0baa8Skettenis 		retlen = output_len;
9825dd0baa8Skettenis 	if (output && output_len) {
9835dd0baa8Skettenis 		memset(output, 0, output_len);
9845dd0baa8Skettenis 		memcpy(output, bfr + sizeof(*call), retlen);
9855dd0baa8Skettenis 	}
9865dd0baa8Skettenis 
9875dd0baa8Skettenis out:
9885dd0baa8Skettenis 	kfree(bfr);
9895dd0baa8Skettenis 	return ret;
9905dd0baa8Skettenis }
991