131214108SAlexandre Courbot /*
231214108SAlexandre Courbot * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
331214108SAlexandre Courbot *
431214108SAlexandre Courbot * Permission is hereby granted, free of charge, to any person obtaining a
531214108SAlexandre Courbot * copy of this software and associated documentation files (the "Software"),
631214108SAlexandre Courbot * to deal in the Software without restriction, including without limitation
731214108SAlexandre Courbot * the rights to use, copy, modify, merge, publish, distribute, sublicense,
831214108SAlexandre Courbot * and/or sell copies of the Software, and to permit persons to whom the
931214108SAlexandre Courbot * Software is furnished to do so, subject to the following conditions:
1031214108SAlexandre Courbot *
1131214108SAlexandre Courbot * The above copyright notice and this permission notice shall be included in
1231214108SAlexandre Courbot * all copies or substantial portions of the Software.
1331214108SAlexandre Courbot *
1431214108SAlexandre Courbot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1531214108SAlexandre Courbot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1631214108SAlexandre Courbot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1731214108SAlexandre Courbot * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1831214108SAlexandre Courbot * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1931214108SAlexandre Courbot * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2031214108SAlexandre Courbot * DEALINGS IN THE SOFTWARE.
2131214108SAlexandre Courbot */
2231214108SAlexandre Courbot #include "priv.h"
2331214108SAlexandre Courbot
2431214108SAlexandre Courbot #include <subdev/mc.h>
252541626cSBen Skeggs #include <subdev/timer.h>
26de048192SBen Skeggs #include <subdev/top.h>
2731214108SAlexandre Courbot
28*a51c69eeSBen Skeggs static const struct nvkm_falcon_func_dma *
nvkm_falcon_dma(struct nvkm_falcon * falcon,enum nvkm_falcon_mem * mem_type,u32 * mem_base)29*a51c69eeSBen Skeggs nvkm_falcon_dma(struct nvkm_falcon *falcon, enum nvkm_falcon_mem *mem_type, u32 *mem_base)
30*a51c69eeSBen Skeggs {
31*a51c69eeSBen Skeggs switch (*mem_type) {
32*a51c69eeSBen Skeggs case IMEM: return falcon->func->imem_dma;
33*a51c69eeSBen Skeggs case DMEM: return falcon->func->dmem_dma;
34*a51c69eeSBen Skeggs default:
35*a51c69eeSBen Skeggs return NULL;
36*a51c69eeSBen Skeggs }
37*a51c69eeSBen Skeggs }
38*a51c69eeSBen Skeggs
39*a51c69eeSBen Skeggs int
nvkm_falcon_dma_wr(struct nvkm_falcon * falcon,const u8 * img,u64 dma_addr,u32 dma_base,enum nvkm_falcon_mem mem_type,u32 mem_base,int len,bool sec)40*a51c69eeSBen Skeggs nvkm_falcon_dma_wr(struct nvkm_falcon *falcon, const u8 *img, u64 dma_addr, u32 dma_base,
41*a51c69eeSBen Skeggs enum nvkm_falcon_mem mem_type, u32 mem_base, int len, bool sec)
42*a51c69eeSBen Skeggs {
43*a51c69eeSBen Skeggs const struct nvkm_falcon_func_dma *dma = nvkm_falcon_dma(falcon, &mem_type, &mem_base);
44*a51c69eeSBen Skeggs const char *type = nvkm_falcon_mem(mem_type);
45*a51c69eeSBen Skeggs const int dmalen = 256;
46*a51c69eeSBen Skeggs u32 dma_start = 0;
47*a51c69eeSBen Skeggs u32 dst, src, cmd;
48*a51c69eeSBen Skeggs int ret, i;
49*a51c69eeSBen Skeggs
50*a51c69eeSBen Skeggs if (WARN_ON(!dma->xfer))
51*a51c69eeSBen Skeggs return -EINVAL;
52*a51c69eeSBen Skeggs
53*a51c69eeSBen Skeggs if (mem_type == DMEM) {
54*a51c69eeSBen Skeggs dma_start = dma_base;
55*a51c69eeSBen Skeggs dma_addr += dma_base;
56*a51c69eeSBen Skeggs }
57*a51c69eeSBen Skeggs
58*a51c69eeSBen Skeggs FLCN_DBG(falcon, "%s %08x <- %08x bytes at %08x (%010llx %08x)",
59*a51c69eeSBen Skeggs type, mem_base, len, dma_base, dma_addr - dma_base, dma_start);
60*a51c69eeSBen Skeggs if (WARN_ON(!len || (len & (dmalen - 1))))
61*a51c69eeSBen Skeggs return -EINVAL;
62*a51c69eeSBen Skeggs
63*a51c69eeSBen Skeggs ret = dma->init(falcon, dma_addr, dmalen, mem_type, sec, &cmd);
64*a51c69eeSBen Skeggs if (ret)
65*a51c69eeSBen Skeggs return ret;
66*a51c69eeSBen Skeggs
67*a51c69eeSBen Skeggs dst = mem_base;
68*a51c69eeSBen Skeggs src = dma_base;
69*a51c69eeSBen Skeggs if (len) {
70*a51c69eeSBen Skeggs while (len >= dmalen) {
71*a51c69eeSBen Skeggs dma->xfer(falcon, dst, src - dma_start, cmd);
72*a51c69eeSBen Skeggs
73*a51c69eeSBen Skeggs if (img && nvkm_printk_ok(falcon->owner, falcon->user, NV_DBG_TRACE)) {
74*a51c69eeSBen Skeggs for (i = 0; i < dmalen; i += 4, mem_base += 4) {
75*a51c69eeSBen Skeggs const int w = 8, x = (i / 4) % w;
76*a51c69eeSBen Skeggs
77*a51c69eeSBen Skeggs if (x == 0)
78*a51c69eeSBen Skeggs printk(KERN_INFO "%s %08x <-", type, mem_base);
79*a51c69eeSBen Skeggs printk(KERN_CONT " %08x", *(u32 *)(img + src + i));
80*a51c69eeSBen Skeggs if (x == (w - 1) || ((i + 4) == dmalen))
81*a51c69eeSBen Skeggs printk(KERN_CONT " <- %08x+%08x", dma_base,
82*a51c69eeSBen Skeggs src + i - dma_base - (x * 4));
83*a51c69eeSBen Skeggs if (i == (7 * 4))
84*a51c69eeSBen Skeggs printk(KERN_CONT " *");
85*a51c69eeSBen Skeggs }
86*a51c69eeSBen Skeggs }
87*a51c69eeSBen Skeggs
88*a51c69eeSBen Skeggs if (nvkm_msec(falcon->owner->device, 2000,
89*a51c69eeSBen Skeggs if (dma->done(falcon))
90*a51c69eeSBen Skeggs break;
91*a51c69eeSBen Skeggs ) < 0)
92*a51c69eeSBen Skeggs return -ETIMEDOUT;
93*a51c69eeSBen Skeggs
94*a51c69eeSBen Skeggs src += dmalen;
95*a51c69eeSBen Skeggs dst += dmalen;
96*a51c69eeSBen Skeggs len -= dmalen;
97*a51c69eeSBen Skeggs }
98*a51c69eeSBen Skeggs WARN_ON(len);
99*a51c69eeSBen Skeggs }
100*a51c69eeSBen Skeggs
101*a51c69eeSBen Skeggs return 0;
102*a51c69eeSBen Skeggs }
103*a51c69eeSBen Skeggs
1040e44c217SBen Skeggs static const struct nvkm_falcon_func_pio *
nvkm_falcon_pio(struct nvkm_falcon * falcon,enum nvkm_falcon_mem * mem_type,u32 * mem_base)1050e44c217SBen Skeggs nvkm_falcon_pio(struct nvkm_falcon *falcon, enum nvkm_falcon_mem *mem_type, u32 *mem_base)
1060e44c217SBen Skeggs {
1070e44c217SBen Skeggs switch (*mem_type) {
1080e44c217SBen Skeggs case IMEM:
1090e44c217SBen Skeggs return falcon->func->imem_pio;
1100e44c217SBen Skeggs case DMEM:
1110e44c217SBen Skeggs if (!falcon->func->emem_addr || *mem_base < falcon->func->emem_addr)
1120e44c217SBen Skeggs return falcon->func->dmem_pio;
1130e44c217SBen Skeggs
1140e44c217SBen Skeggs *mem_base -= falcon->func->emem_addr;
1150e44c217SBen Skeggs fallthrough;
1162541626cSBen Skeggs case EMEM:
1172541626cSBen Skeggs return falcon->func->emem_pio;
1180e44c217SBen Skeggs default:
1190e44c217SBen Skeggs return NULL;
1200e44c217SBen Skeggs }
1210e44c217SBen Skeggs }
1220e44c217SBen Skeggs
1230e44c217SBen Skeggs int
nvkm_falcon_pio_rd(struct nvkm_falcon * falcon,u8 port,enum nvkm_falcon_mem mem_type,u32 mem_base,const u8 * img,u32 img_base,int len)1242541626cSBen Skeggs nvkm_falcon_pio_rd(struct nvkm_falcon *falcon, u8 port, enum nvkm_falcon_mem mem_type, u32 mem_base,
1252541626cSBen Skeggs const u8 *img, u32 img_base, int len)
1262541626cSBen Skeggs {
1272541626cSBen Skeggs const struct nvkm_falcon_func_pio *pio = nvkm_falcon_pio(falcon, &mem_type, &mem_base);
1282541626cSBen Skeggs const char *type = nvkm_falcon_mem(mem_type);
1292541626cSBen Skeggs int xfer_len;
1302541626cSBen Skeggs
1312541626cSBen Skeggs if (WARN_ON(!pio || !pio->rd))
1322541626cSBen Skeggs return -EINVAL;
1332541626cSBen Skeggs
1342541626cSBen Skeggs FLCN_DBG(falcon, "%s %08x -> %08x bytes at %08x", type, mem_base, len, img_base);
1352541626cSBen Skeggs if (WARN_ON(!len || (len & (pio->min - 1))))
1362541626cSBen Skeggs return -EINVAL;
1372541626cSBen Skeggs
1382541626cSBen Skeggs pio->rd_init(falcon, port, mem_base);
1392541626cSBen Skeggs do {
1402541626cSBen Skeggs xfer_len = min(len, pio->max);
1412541626cSBen Skeggs pio->rd(falcon, port, img, xfer_len);
1422541626cSBen Skeggs
1432541626cSBen Skeggs if (nvkm_printk_ok(falcon->owner, falcon->user, NV_DBG_TRACE)) {
1442541626cSBen Skeggs for (img_base = 0; img_base < xfer_len; img_base += 4, mem_base += 4) {
1452541626cSBen Skeggs if (((img_base / 4) % 8) == 0)
1462541626cSBen Skeggs printk(KERN_INFO "%s %08x ->", type, mem_base);
1472541626cSBen Skeggs printk(KERN_CONT " %08x", *(u32 *)(img + img_base));
1482541626cSBen Skeggs }
1492541626cSBen Skeggs }
1502541626cSBen Skeggs
1512541626cSBen Skeggs img += xfer_len;
1522541626cSBen Skeggs len -= xfer_len;
1532541626cSBen Skeggs } while (len);
1542541626cSBen Skeggs
1552541626cSBen Skeggs return 0;
1562541626cSBen Skeggs }
1572541626cSBen Skeggs
1582541626cSBen Skeggs int
nvkm_falcon_pio_wr(struct nvkm_falcon * falcon,const u8 * img,u32 img_base,u8 port,enum nvkm_falcon_mem mem_type,u32 mem_base,int len,u16 tag,bool sec)1590e44c217SBen Skeggs nvkm_falcon_pio_wr(struct nvkm_falcon *falcon, const u8 *img, u32 img_base, u8 port,
1600e44c217SBen Skeggs enum nvkm_falcon_mem mem_type, u32 mem_base, int len, u16 tag, bool sec)
1610e44c217SBen Skeggs {
1620e44c217SBen Skeggs const struct nvkm_falcon_func_pio *pio = nvkm_falcon_pio(falcon, &mem_type, &mem_base);
1630e44c217SBen Skeggs const char *type = nvkm_falcon_mem(mem_type);
1640e44c217SBen Skeggs int xfer_len;
1650e44c217SBen Skeggs
1660e44c217SBen Skeggs if (WARN_ON(!pio || !pio->wr))
1670e44c217SBen Skeggs return -EINVAL;
1680e44c217SBen Skeggs
1690e44c217SBen Skeggs FLCN_DBG(falcon, "%s %08x <- %08x bytes at %08x", type, mem_base, len, img_base);
1700e44c217SBen Skeggs if (WARN_ON(!len || (len & (pio->min - 1))))
1710e44c217SBen Skeggs return -EINVAL;
1720e44c217SBen Skeggs
1730e44c217SBen Skeggs pio->wr_init(falcon, port, sec, mem_base);
1740e44c217SBen Skeggs do {
1750e44c217SBen Skeggs xfer_len = min(len, pio->max);
1760e44c217SBen Skeggs pio->wr(falcon, port, img, xfer_len, tag++);
1770e44c217SBen Skeggs
1780e44c217SBen Skeggs if (nvkm_printk_ok(falcon->owner, falcon->user, NV_DBG_TRACE)) {
1790e44c217SBen Skeggs for (img_base = 0; img_base < xfer_len; img_base += 4, mem_base += 4) {
1800e44c217SBen Skeggs if (((img_base / 4) % 8) == 0)
1810e44c217SBen Skeggs printk(KERN_INFO "%s %08x <-", type, mem_base);
1820e44c217SBen Skeggs printk(KERN_CONT " %08x", *(u32 *)(img + img_base));
1830e44c217SBen Skeggs if ((img_base / 4) == 7 && mem_type == IMEM)
1840e44c217SBen Skeggs printk(KERN_CONT " %04x", tag - 1);
1850e44c217SBen Skeggs }
1860e44c217SBen Skeggs }
1870e44c217SBen Skeggs
1880e44c217SBen Skeggs img += xfer_len;
1890e44c217SBen Skeggs len -= xfer_len;
1900e44c217SBen Skeggs } while (len);
1910e44c217SBen Skeggs
1920e44c217SBen Skeggs return 0;
1930e44c217SBen Skeggs }
1940e44c217SBen Skeggs
19531214108SAlexandre Courbot void
nvkm_falcon_load_imem(struct nvkm_falcon * falcon,void * data,u32 start,u32 size,u16 tag,u8 port,bool secure)19631214108SAlexandre Courbot nvkm_falcon_load_imem(struct nvkm_falcon *falcon, void *data, u32 start,
19731214108SAlexandre Courbot u32 size, u16 tag, u8 port, bool secure)
19831214108SAlexandre Courbot {
19931214108SAlexandre Courbot if (secure && !falcon->secret) {
20031214108SAlexandre Courbot nvkm_warn(falcon->user,
20131214108SAlexandre Courbot "writing with secure tag on a non-secure falcon!\n");
20231214108SAlexandre Courbot return;
20331214108SAlexandre Courbot }
20431214108SAlexandre Courbot
20531214108SAlexandre Courbot falcon->func->load_imem(falcon, data, start, size, tag, port,
20631214108SAlexandre Courbot secure);
20731214108SAlexandre Courbot }
20831214108SAlexandre Courbot
20931214108SAlexandre Courbot void
nvkm_falcon_load_dmem(struct nvkm_falcon * falcon,void * data,u32 start,u32 size,u8 port)21031214108SAlexandre Courbot nvkm_falcon_load_dmem(struct nvkm_falcon *falcon, void *data, u32 start,
21131214108SAlexandre Courbot u32 size, u8 port)
21231214108SAlexandre Courbot {
213e444de56SAlexandre Courbot mutex_lock(&falcon->dmem_mutex);
214e444de56SAlexandre Courbot
21531214108SAlexandre Courbot falcon->func->load_dmem(falcon, data, start, size, port);
216e444de56SAlexandre Courbot
217e444de56SAlexandre Courbot mutex_unlock(&falcon->dmem_mutex);
21831214108SAlexandre Courbot }
21931214108SAlexandre Courbot
22031214108SAlexandre Courbot void
nvkm_falcon_start(struct nvkm_falcon * falcon)22131214108SAlexandre Courbot nvkm_falcon_start(struct nvkm_falcon *falcon)
22231214108SAlexandre Courbot {
22331214108SAlexandre Courbot falcon->func->start(falcon);
22431214108SAlexandre Courbot }
22531214108SAlexandre Courbot
22631214108SAlexandre Courbot int
nvkm_falcon_reset(struct nvkm_falcon * falcon)22731214108SAlexandre Courbot nvkm_falcon_reset(struct nvkm_falcon *falcon)
22831214108SAlexandre Courbot {
229f15cde64SBen Skeggs int ret;
23031214108SAlexandre Courbot
231f15cde64SBen Skeggs ret = falcon->func->disable(falcon);
232f15cde64SBen Skeggs if (WARN_ON(ret))
233f15cde64SBen Skeggs return ret;
234f15cde64SBen Skeggs
235f15cde64SBen Skeggs return nvkm_falcon_enable(falcon);
2364cdd2450SBen Skeggs }
2374cdd2450SBen Skeggs
2385a4b98cdSBen Skeggs static int
nvkm_falcon_oneinit(struct nvkm_falcon * falcon)2395a4b98cdSBen Skeggs nvkm_falcon_oneinit(struct nvkm_falcon *falcon)
24031214108SAlexandre Courbot {
241e938c4e7SBen Skeggs const struct nvkm_falcon_func *func = falcon->func;
2425a4b98cdSBen Skeggs const struct nvkm_subdev *subdev = falcon->owner;
24331214108SAlexandre Courbot u32 reg;
24431214108SAlexandre Courbot
245de048192SBen Skeggs if (!falcon->addr) {
246b7da823aSBen Skeggs falcon->addr = nvkm_top_addr(subdev->device, subdev->type, subdev->inst);
247de048192SBen Skeggs if (WARN_ON(!falcon->addr))
248de048192SBen Skeggs return -ENODEV;
249de048192SBen Skeggs }
250de048192SBen Skeggs
25131214108SAlexandre Courbot reg = nvkm_falcon_rd32(falcon, 0x12c);
25231214108SAlexandre Courbot falcon->version = reg & 0xf;
25331214108SAlexandre Courbot falcon->secret = (reg >> 4) & 0x3;
25431214108SAlexandre Courbot falcon->code.ports = (reg >> 8) & 0xf;
25531214108SAlexandre Courbot falcon->data.ports = (reg >> 12) & 0xf;
25631214108SAlexandre Courbot
25731214108SAlexandre Courbot reg = nvkm_falcon_rd32(falcon, 0x108);
25831214108SAlexandre Courbot falcon->code.limit = (reg & 0x1ff) << 8;
25931214108SAlexandre Courbot falcon->data.limit = (reg & 0x3fe00) >> 1;
26031214108SAlexandre Courbot
261e938c4e7SBen Skeggs if (func->debug) {
262e938c4e7SBen Skeggs u32 val = nvkm_falcon_rd32(falcon, func->debug);
263ad147b7fSAlexandre Courbot falcon->debug = (val >> 20) & 0x1;
264ad147b7fSAlexandre Courbot }
2655a4b98cdSBen Skeggs
2665a4b98cdSBen Skeggs return 0;
2675a4b98cdSBen Skeggs }
2685a4b98cdSBen Skeggs
2695a4b98cdSBen Skeggs void
nvkm_falcon_put(struct nvkm_falcon * falcon,struct nvkm_subdev * user)270a9d90860SBen Skeggs nvkm_falcon_put(struct nvkm_falcon *falcon, struct nvkm_subdev *user)
2715a4b98cdSBen Skeggs {
2725a4b98cdSBen Skeggs if (unlikely(!falcon))
2735a4b98cdSBen Skeggs return;
2745a4b98cdSBen Skeggs
2755a4b98cdSBen Skeggs mutex_lock(&falcon->mutex);
2765a4b98cdSBen Skeggs if (falcon->user == user) {
2775a4b98cdSBen Skeggs nvkm_debug(falcon->user, "released %s falcon\n", falcon->name);
2785a4b98cdSBen Skeggs falcon->user = NULL;
2795a4b98cdSBen Skeggs }
2805a4b98cdSBen Skeggs mutex_unlock(&falcon->mutex);
2815a4b98cdSBen Skeggs }
2825a4b98cdSBen Skeggs
2835a4b98cdSBen Skeggs int
nvkm_falcon_get(struct nvkm_falcon * falcon,struct nvkm_subdev * user)284a9d90860SBen Skeggs nvkm_falcon_get(struct nvkm_falcon *falcon, struct nvkm_subdev *user)
2855a4b98cdSBen Skeggs {
2865a4b98cdSBen Skeggs int ret = 0;
2875a4b98cdSBen Skeggs
2885a4b98cdSBen Skeggs mutex_lock(&falcon->mutex);
2895a4b98cdSBen Skeggs if (falcon->user) {
2905a4b98cdSBen Skeggs nvkm_error(user, "%s falcon already acquired by %s!\n",
2919c28abb7SBen Skeggs falcon->name, falcon->user->name);
2925a4b98cdSBen Skeggs mutex_unlock(&falcon->mutex);
2935a4b98cdSBen Skeggs return -EBUSY;
2945a4b98cdSBen Skeggs }
2955a4b98cdSBen Skeggs
2965a4b98cdSBen Skeggs nvkm_debug(user, "acquired %s falcon\n", falcon->name);
2975a4b98cdSBen Skeggs if (!falcon->oneinit)
2985a4b98cdSBen Skeggs ret = nvkm_falcon_oneinit(falcon);
2995a4b98cdSBen Skeggs falcon->user = user;
3005a4b98cdSBen Skeggs mutex_unlock(&falcon->mutex);
3015a4b98cdSBen Skeggs return ret;
3025a4b98cdSBen Skeggs }
3035a4b98cdSBen Skeggs
3045a4b98cdSBen Skeggs void
nvkm_falcon_dtor(struct nvkm_falcon * falcon)305a128bbfaSBen Skeggs nvkm_falcon_dtor(struct nvkm_falcon *falcon)
306a128bbfaSBen Skeggs {
307a128bbfaSBen Skeggs }
308a128bbfaSBen Skeggs
309a128bbfaSBen Skeggs int
nvkm_falcon_ctor(const struct nvkm_falcon_func * func,struct nvkm_subdev * subdev,const char * name,u32 addr,struct nvkm_falcon * falcon)3105a4b98cdSBen Skeggs nvkm_falcon_ctor(const struct nvkm_falcon_func *func,
3115a4b98cdSBen Skeggs struct nvkm_subdev *subdev, const char *name, u32 addr,
3125a4b98cdSBen Skeggs struct nvkm_falcon *falcon)
3135a4b98cdSBen Skeggs {
3145a4b98cdSBen Skeggs falcon->func = func;
3155a4b98cdSBen Skeggs falcon->owner = subdev;
3165a4b98cdSBen Skeggs falcon->name = name;
3175a4b98cdSBen Skeggs falcon->addr = addr;
318*a51c69eeSBen Skeggs falcon->addr2 = func->addr2;
3195a4b98cdSBen Skeggs mutex_init(&falcon->mutex);
3205a4b98cdSBen Skeggs mutex_init(&falcon->dmem_mutex);
321a128bbfaSBen Skeggs return 0;
32231214108SAlexandre Courbot }
323