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