19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
275471687STerje Bergstrom /*
375471687STerje Bergstrom * Tegra host1x driver
475471687STerje Bergstrom *
575471687STerje Bergstrom * Copyright (c) 2010-2013, NVIDIA Corporation.
675471687STerje Bergstrom */
775471687STerje Bergstrom
875471687STerje Bergstrom #include <linux/clk.h>
96b6776e2SDmitry Osipenko #include <linux/delay.h>
10097452e6SAlexandre Courbot #include <linux/dma-mapping.h>
117e7d432cSThierry Reding #include <linux/io.h>
127e7d432cSThierry Reding #include <linux/list.h>
137e7d432cSThierry Reding #include <linux/module.h>
147e7d432cSThierry Reding #include <linux/of.h>
15*573cbf48SRob Herring #include <linux/of_platform.h>
16*573cbf48SRob Herring #include <linux/platform_device.h>
176b6776e2SDmitry Osipenko #include <linux/pm_runtime.h>
187e7d432cSThierry Reding #include <linux/slab.h>
1975471687STerje Bergstrom
206b6776e2SDmitry Osipenko #include <soc/tegra/common.h>
216b6776e2SDmitry Osipenko
2275471687STerje Bergstrom #define CREATE_TRACE_POINTS
2375471687STerje Bergstrom #include <trace/events/host1x.h>
24404bfb78SMikko Perttunen #undef CREATE_TRACE_POINTS
2575471687STerje Bergstrom
26d5185965SDmitry Osipenko #if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
27d5185965SDmitry Osipenko #include <asm/dma-iommu.h>
28d5185965SDmitry Osipenko #endif
29d5185965SDmitry Osipenko
30776dc384SThierry Reding #include "bus.h"
316579324aSTerje Bergstrom #include "channel.h"
328aa5bcb6SMikko Perttunen #include "context.h"
336236451dSTerje Bergstrom #include "debug.h"
347e7d432cSThierry Reding #include "dev.h"
357e7d432cSThierry Reding #include "intr.h"
367e7d432cSThierry Reding
3775471687STerje Bergstrom #include "hw/host1x01.h"
385407f31bSThierry Reding #include "hw/host1x02.h"
39e6fff4aaSThierry Reding #include "hw/host1x04.h"
40a134789aSThierry Reding #include "hw/host1x05.h"
41f1b53c4eSMikko Perttunen #include "hw/host1x06.h"
42ac1bdbf2SThierry Reding #include "hw/host1x07.h"
439abdd497SMikko Perttunen #include "hw/host1x08.h"
44f1b53c4eSMikko Perttunen
host1x_common_writel(struct host1x * host1x,u32 v,u32 r)4597dea367SMikko Perttunen void host1x_common_writel(struct host1x *host1x, u32 v, u32 r)
4697dea367SMikko Perttunen {
4797dea367SMikko Perttunen writel(v, host1x->common_regs + r);
4897dea367SMikko Perttunen }
4997dea367SMikko Perttunen
host1x_hypervisor_writel(struct host1x * host1x,u32 v,u32 r)50f1b53c4eSMikko Perttunen void host1x_hypervisor_writel(struct host1x *host1x, u32 v, u32 r)
51f1b53c4eSMikko Perttunen {
52f1b53c4eSMikko Perttunen writel(v, host1x->hv_regs + r);
53f1b53c4eSMikko Perttunen }
54f1b53c4eSMikko Perttunen
host1x_hypervisor_readl(struct host1x * host1x,u32 r)55f1b53c4eSMikko Perttunen u32 host1x_hypervisor_readl(struct host1x *host1x, u32 r)
56f1b53c4eSMikko Perttunen {
57f1b53c4eSMikko Perttunen return readl(host1x->hv_regs + r);
58f1b53c4eSMikko Perttunen }
5975471687STerje Bergstrom
host1x_sync_writel(struct host1x * host1x,u32 v,u32 r)6075471687STerje Bergstrom void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r)
6175471687STerje Bergstrom {
6275471687STerje Bergstrom void __iomem *sync_regs = host1x->regs + host1x->info->sync_offset;
6375471687STerje Bergstrom
6475471687STerje Bergstrom writel(v, sync_regs + r);
6575471687STerje Bergstrom }
6675471687STerje Bergstrom
host1x_sync_readl(struct host1x * host1x,u32 r)6775471687STerje Bergstrom u32 host1x_sync_readl(struct host1x *host1x, u32 r)
6875471687STerje Bergstrom {
6975471687STerje Bergstrom void __iomem *sync_regs = host1x->regs + host1x->info->sync_offset;
7075471687STerje Bergstrom
7175471687STerje Bergstrom return readl(sync_regs + r);
7275471687STerje Bergstrom }
7375471687STerje Bergstrom
host1x_ch_writel(struct host1x_channel * ch,u32 v,u32 r)746579324aSTerje Bergstrom void host1x_ch_writel(struct host1x_channel *ch, u32 v, u32 r)
756579324aSTerje Bergstrom {
766579324aSTerje Bergstrom writel(v, ch->regs + r);
776579324aSTerje Bergstrom }
786579324aSTerje Bergstrom
host1x_ch_readl(struct host1x_channel * ch,u32 r)796579324aSTerje Bergstrom u32 host1x_ch_readl(struct host1x_channel *ch, u32 r)
806579324aSTerje Bergstrom {
816579324aSTerje Bergstrom return readl(ch->regs + r);
826579324aSTerje Bergstrom }
836579324aSTerje Bergstrom
8475471687STerje Bergstrom static const struct host1x_info host1x01_info = {
8575471687STerje Bergstrom .nb_channels = 8,
8675471687STerje Bergstrom .nb_pts = 32,
8775471687STerje Bergstrom .nb_mlocks = 16,
8875471687STerje Bergstrom .nb_bases = 8,
8975471687STerje Bergstrom .init = host1x01_init,
9075471687STerje Bergstrom .sync_offset = 0x3000,
91097452e6SAlexandre Courbot .dma_mask = DMA_BIT_MASK(32),
9206867a36SThierry Reding .has_wide_gather = false,
938f45f507SThierry Reding .has_hypervisor = false,
948f45f507SThierry Reding .num_sid_entries = 0,
958f45f507SThierry Reding .sid_table = NULL,
96f5ba33fbSMikko Perttunen .reserve_vblank_syncpts = true,
9775471687STerje Bergstrom };
9875471687STerje Bergstrom
995407f31bSThierry Reding static const struct host1x_info host1x02_info = {
1005407f31bSThierry Reding .nb_channels = 9,
1015407f31bSThierry Reding .nb_pts = 32,
1025407f31bSThierry Reding .nb_mlocks = 16,
1035407f31bSThierry Reding .nb_bases = 12,
1045407f31bSThierry Reding .init = host1x02_init,
1055407f31bSThierry Reding .sync_offset = 0x3000,
106097452e6SAlexandre Courbot .dma_mask = DMA_BIT_MASK(32),
10706867a36SThierry Reding .has_wide_gather = false,
1088f45f507SThierry Reding .has_hypervisor = false,
1098f45f507SThierry Reding .num_sid_entries = 0,
1108f45f507SThierry Reding .sid_table = NULL,
111f5ba33fbSMikko Perttunen .reserve_vblank_syncpts = true,
1125407f31bSThierry Reding };
1135407f31bSThierry Reding
114e6fff4aaSThierry Reding static const struct host1x_info host1x04_info = {
115e6fff4aaSThierry Reding .nb_channels = 12,
116e6fff4aaSThierry Reding .nb_pts = 192,
117e6fff4aaSThierry Reding .nb_mlocks = 16,
118e6fff4aaSThierry Reding .nb_bases = 64,
119e6fff4aaSThierry Reding .init = host1x04_init,
120e6fff4aaSThierry Reding .sync_offset = 0x2100,
121097452e6SAlexandre Courbot .dma_mask = DMA_BIT_MASK(34),
12206867a36SThierry Reding .has_wide_gather = false,
1238f45f507SThierry Reding .has_hypervisor = false,
1248f45f507SThierry Reding .num_sid_entries = 0,
1258f45f507SThierry Reding .sid_table = NULL,
126f5ba33fbSMikko Perttunen .reserve_vblank_syncpts = false,
127e6fff4aaSThierry Reding };
128e6fff4aaSThierry Reding
129a134789aSThierry Reding static const struct host1x_info host1x05_info = {
130a134789aSThierry Reding .nb_channels = 14,
131a134789aSThierry Reding .nb_pts = 192,
132a134789aSThierry Reding .nb_mlocks = 16,
133a134789aSThierry Reding .nb_bases = 64,
134a134789aSThierry Reding .init = host1x05_init,
135a134789aSThierry Reding .sync_offset = 0x2100,
136097452e6SAlexandre Courbot .dma_mask = DMA_BIT_MASK(34),
13706867a36SThierry Reding .has_wide_gather = false,
1388f45f507SThierry Reding .has_hypervisor = false,
1398f45f507SThierry Reding .num_sid_entries = 0,
1408f45f507SThierry Reding .sid_table = NULL,
141f5ba33fbSMikko Perttunen .reserve_vblank_syncpts = false,
142a134789aSThierry Reding };
143a134789aSThierry Reding
1446841482bSThierry Reding static const struct host1x_sid_entry tegra186_sid_table[] = {
1456841482bSThierry Reding {
1466841482bSThierry Reding /* VIC */
1476841482bSThierry Reding .base = 0x1af0,
1486841482bSThierry Reding .offset = 0x30,
1496841482bSThierry Reding .limit = 0x34
1506841482bSThierry Reding },
15146f226c9SMikko Perttunen {
15246f226c9SMikko Perttunen /* NVDEC */
15346f226c9SMikko Perttunen .base = 0x1b00,
15446f226c9SMikko Perttunen .offset = 0x30,
15546f226c9SMikko Perttunen .limit = 0x34
15646f226c9SMikko Perttunen },
1576841482bSThierry Reding };
1586841482bSThierry Reding
159f1b53c4eSMikko Perttunen static const struct host1x_info host1x06_info = {
160f1b53c4eSMikko Perttunen .nb_channels = 63,
161f1b53c4eSMikko Perttunen .nb_pts = 576,
162f1b53c4eSMikko Perttunen .nb_mlocks = 24,
163f1b53c4eSMikko Perttunen .nb_bases = 16,
164f1b53c4eSMikko Perttunen .init = host1x06_init,
165f1b53c4eSMikko Perttunen .sync_offset = 0x0,
1668de896ebSThierry Reding .dma_mask = DMA_BIT_MASK(40),
16706867a36SThierry Reding .has_wide_gather = true,
168f1b53c4eSMikko Perttunen .has_hypervisor = true,
1696841482bSThierry Reding .num_sid_entries = ARRAY_SIZE(tegra186_sid_table),
1706841482bSThierry Reding .sid_table = tegra186_sid_table,
171f5ba33fbSMikko Perttunen .reserve_vblank_syncpts = false,
1726841482bSThierry Reding };
1736841482bSThierry Reding
1746841482bSThierry Reding static const struct host1x_sid_entry tegra194_sid_table[] = {
1756841482bSThierry Reding {
1766841482bSThierry Reding /* VIC */
1776841482bSThierry Reding .base = 0x1af0,
1786841482bSThierry Reding .offset = 0x30,
1796841482bSThierry Reding .limit = 0x34
1806841482bSThierry Reding },
18146f226c9SMikko Perttunen {
18246f226c9SMikko Perttunen /* NVDEC */
18346f226c9SMikko Perttunen .base = 0x1b00,
18446f226c9SMikko Perttunen .offset = 0x30,
18546f226c9SMikko Perttunen .limit = 0x34
18646f226c9SMikko Perttunen },
18746f226c9SMikko Perttunen {
18846f226c9SMikko Perttunen /* NVDEC1 */
18946f226c9SMikko Perttunen .base = 0x1bc0,
19046f226c9SMikko Perttunen .offset = 0x30,
19146f226c9SMikko Perttunen .limit = 0x34
19246f226c9SMikko Perttunen },
193f1b53c4eSMikko Perttunen };
194f1b53c4eSMikko Perttunen
195ac1bdbf2SThierry Reding static const struct host1x_info host1x07_info = {
196ac1bdbf2SThierry Reding .nb_channels = 63,
197ac1bdbf2SThierry Reding .nb_pts = 704,
198ac1bdbf2SThierry Reding .nb_mlocks = 32,
199ac1bdbf2SThierry Reding .nb_bases = 0,
200ac1bdbf2SThierry Reding .init = host1x07_init,
201ac1bdbf2SThierry Reding .sync_offset = 0x0,
202ac1bdbf2SThierry Reding .dma_mask = DMA_BIT_MASK(40),
20306867a36SThierry Reding .has_wide_gather = true,
204ac1bdbf2SThierry Reding .has_hypervisor = true,
2056841482bSThierry Reding .num_sid_entries = ARRAY_SIZE(tegra194_sid_table),
2066841482bSThierry Reding .sid_table = tegra194_sid_table,
207f5ba33fbSMikko Perttunen .reserve_vblank_syncpts = false,
208ac1bdbf2SThierry Reding };
209ac1bdbf2SThierry Reding
2109abdd497SMikko Perttunen /*
2119abdd497SMikko Perttunen * Tegra234 has two stream ID protection tables, one for setting stream IDs
2129abdd497SMikko Perttunen * through the channel path via SETSTREAMID, and one for setting them via
2139abdd497SMikko Perttunen * MMIO. We program each engine's data stream ID in the channel path table
2149abdd497SMikko Perttunen * and firmware stream ID in the MMIO path table.
2159abdd497SMikko Perttunen */
2169abdd497SMikko Perttunen static const struct host1x_sid_entry tegra234_sid_table[] = {
2179abdd497SMikko Perttunen {
2189abdd497SMikko Perttunen /* VIC channel */
2199abdd497SMikko Perttunen .base = 0x17b8,
2209abdd497SMikko Perttunen .offset = 0x30,
2219abdd497SMikko Perttunen .limit = 0x30
2229abdd497SMikko Perttunen },
2239abdd497SMikko Perttunen {
2249abdd497SMikko Perttunen /* VIC MMIO */
2259abdd497SMikko Perttunen .base = 0x1688,
2269abdd497SMikko Perttunen .offset = 0x34,
2279abdd497SMikko Perttunen .limit = 0x34
2289abdd497SMikko Perttunen },
22997b93b7aSMikko Perttunen {
23097b93b7aSMikko Perttunen /* NVDEC channel */
23197b93b7aSMikko Perttunen .base = 0x17c8,
23297b93b7aSMikko Perttunen .offset = 0x30,
23397b93b7aSMikko Perttunen .limit = 0x30,
23497b93b7aSMikko Perttunen },
23597b93b7aSMikko Perttunen {
23697b93b7aSMikko Perttunen /* NVDEC MMIO */
23797b93b7aSMikko Perttunen .base = 0x1698,
23897b93b7aSMikko Perttunen .offset = 0x34,
23997b93b7aSMikko Perttunen .limit = 0x34,
24097b93b7aSMikko Perttunen },
2419abdd497SMikko Perttunen };
2429abdd497SMikko Perttunen
2439abdd497SMikko Perttunen static const struct host1x_info host1x08_info = {
2449abdd497SMikko Perttunen .nb_channels = 63,
2459abdd497SMikko Perttunen .nb_pts = 1024,
2469abdd497SMikko Perttunen .nb_mlocks = 24,
2479abdd497SMikko Perttunen .nb_bases = 0,
2489abdd497SMikko Perttunen .init = host1x08_init,
2499abdd497SMikko Perttunen .sync_offset = 0x0,
2509abdd497SMikko Perttunen .dma_mask = DMA_BIT_MASK(40),
2519abdd497SMikko Perttunen .has_wide_gather = true,
2529abdd497SMikko Perttunen .has_hypervisor = true,
2539abdd497SMikko Perttunen .has_common = true,
2549abdd497SMikko Perttunen .num_sid_entries = ARRAY_SIZE(tegra234_sid_table),
2559abdd497SMikko Perttunen .sid_table = tegra234_sid_table,
2569abdd497SMikko Perttunen .streamid_vm_table = { 0x1004, 128 },
2579abdd497SMikko Perttunen .classid_vm_table = { 0x1404, 25 },
2589abdd497SMikko Perttunen .mmio_vm_table = { 0x1504, 25 },
2599abdd497SMikko Perttunen .reserve_vblank_syncpts = false,
2609abdd497SMikko Perttunen };
2619abdd497SMikko Perttunen
2626df633d0SThierry Reding static const struct of_device_id host1x_of_match[] = {
2639abdd497SMikko Perttunen { .compatible = "nvidia,tegra234-host1x", .data = &host1x08_info, },
264ac1bdbf2SThierry Reding { .compatible = "nvidia,tegra194-host1x", .data = &host1x07_info, },
265f1b53c4eSMikko Perttunen { .compatible = "nvidia,tegra186-host1x", .data = &host1x06_info, },
266a134789aSThierry Reding { .compatible = "nvidia,tegra210-host1x", .data = &host1x05_info, },
267e6fff4aaSThierry Reding { .compatible = "nvidia,tegra124-host1x", .data = &host1x04_info, },
2685407f31bSThierry Reding { .compatible = "nvidia,tegra114-host1x", .data = &host1x02_info, },
26975471687STerje Bergstrom { .compatible = "nvidia,tegra30-host1x", .data = &host1x01_info, },
27075471687STerje Bergstrom { .compatible = "nvidia,tegra20-host1x", .data = &host1x01_info, },
27175471687STerje Bergstrom { },
27275471687STerje Bergstrom };
27375471687STerje Bergstrom MODULE_DEVICE_TABLE(of, host1x_of_match);
27475471687STerje Bergstrom
host1x_setup_virtualization_tables(struct host1x * host)275939179faSMikko Perttunen static void host1x_setup_virtualization_tables(struct host1x *host)
2766841482bSThierry Reding {
2776841482bSThierry Reding const struct host1x_info *info = host->info;
2786841482bSThierry Reding unsigned int i;
2796841482bSThierry Reding
2806b6776e2SDmitry Osipenko if (!info->has_hypervisor)
2816b6776e2SDmitry Osipenko return;
2826b6776e2SDmitry Osipenko
2836841482bSThierry Reding for (i = 0; i < info->num_sid_entries; i++) {
2846841482bSThierry Reding const struct host1x_sid_entry *entry = &info->sid_table[i];
2856841482bSThierry Reding
2866841482bSThierry Reding host1x_hypervisor_writel(host, entry->offset, entry->base);
2876841482bSThierry Reding host1x_hypervisor_writel(host, entry->limit, entry->base + 4);
2886841482bSThierry Reding }
289939179faSMikko Perttunen
290939179faSMikko Perttunen for (i = 0; i < info->streamid_vm_table.count; i++) {
291939179faSMikko Perttunen /* Allow access to all stream IDs to all VMs. */
292939179faSMikko Perttunen host1x_hypervisor_writel(host, 0xff, info->streamid_vm_table.base + 4 * i);
293939179faSMikko Perttunen }
294939179faSMikko Perttunen
295939179faSMikko Perttunen for (i = 0; i < info->classid_vm_table.count; i++) {
296939179faSMikko Perttunen /* Allow access to all classes to all VMs. */
297939179faSMikko Perttunen host1x_hypervisor_writel(host, 0xff, info->classid_vm_table.base + 4 * i);
298939179faSMikko Perttunen }
299939179faSMikko Perttunen
300939179faSMikko Perttunen for (i = 0; i < info->mmio_vm_table.count; i++) {
301939179faSMikko Perttunen /* Use VM1 (that's us) as originator VMID for engine MMIO accesses. */
302939179faSMikko Perttunen host1x_hypervisor_writel(host, 0x1, info->mmio_vm_table.base + 4 * i);
303939179faSMikko Perttunen }
3046841482bSThierry Reding }
3056841482bSThierry Reding
host1x_wants_iommu(struct host1x * host1x)3064010e729SThierry Reding static bool host1x_wants_iommu(struct host1x *host1x)
3074010e729SThierry Reding {
308c2418f91SRobin Murphy /* Our IOMMU usage policy doesn't currently play well with GART */
309c2418f91SRobin Murphy if (of_machine_is_compatible("nvidia,tegra20"))
310c2418f91SRobin Murphy return false;
311c2418f91SRobin Murphy
3124010e729SThierry Reding /*
3134010e729SThierry Reding * If we support addressing a maximum of 32 bits of physical memory
3144010e729SThierry Reding * and if the host1x firewall is enabled, there's no need to enable
3154010e729SThierry Reding * IOMMU support. This can happen for example on Tegra20, Tegra30
3164010e729SThierry Reding * and Tegra114.
3174010e729SThierry Reding *
3184010e729SThierry Reding * Tegra124 and later can address up to 34 bits of physical memory and
3194010e729SThierry Reding * many platforms come equipped with more than 2 GiB of system memory,
3204010e729SThierry Reding * which requires crossing the 4 GiB boundary. But there's a catch: on
3214010e729SThierry Reding * SoCs before Tegra186 (i.e. Tegra124 and Tegra210), the host1x can
3224010e729SThierry Reding * only address up to 32 bits of memory in GATHER opcodes, which means
3234010e729SThierry Reding * that command buffers need to either be in the first 2 GiB of system
3244010e729SThierry Reding * memory (which could quickly lead to memory exhaustion), or command
3254010e729SThierry Reding * buffers need to be treated differently from other buffers (which is
3264010e729SThierry Reding * not possible with the current ABI).
3274010e729SThierry Reding *
3284010e729SThierry Reding * A third option is to use the IOMMU in these cases to make sure all
3294010e729SThierry Reding * buffers will be mapped into a 32-bit IOVA space that host1x can
3304010e729SThierry Reding * address. This allows all of the system memory to be used and works
3314010e729SThierry Reding * within the limitations of the host1x on these SoCs.
3324010e729SThierry Reding *
3334010e729SThierry Reding * In summary, default to enable IOMMU on Tegra124 and later. For any
3344010e729SThierry Reding * of the earlier SoCs, only use the IOMMU for additional safety when
3354010e729SThierry Reding * the host1x firewall is disabled.
3364010e729SThierry Reding */
3374010e729SThierry Reding if (host1x->info->dma_mask <= DMA_BIT_MASK(32)) {
3384010e729SThierry Reding if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL))
3394010e729SThierry Reding return false;
3404010e729SThierry Reding }
3414010e729SThierry Reding
3424010e729SThierry Reding return true;
3434010e729SThierry Reding }
3444010e729SThierry Reding
host1x_iommu_attach(struct host1x * host)34506867a36SThierry Reding static struct iommu_domain *host1x_iommu_attach(struct host1x *host)
34606867a36SThierry Reding {
34706867a36SThierry Reding struct iommu_domain *domain = iommu_get_domain_for_dev(host->dev);
34806867a36SThierry Reding int err;
34906867a36SThierry Reding
350d5185965SDmitry Osipenko #if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
351d5185965SDmitry Osipenko if (host->dev->archdata.mapping) {
352d5185965SDmitry Osipenko struct dma_iommu_mapping *mapping =
353d5185965SDmitry Osipenko to_dma_iommu_mapping(host->dev);
354d5185965SDmitry Osipenko arm_iommu_detach_device(host->dev);
355d5185965SDmitry Osipenko arm_iommu_release_mapping(mapping);
356d5185965SDmitry Osipenko
357d5185965SDmitry Osipenko domain = iommu_get_domain_for_dev(host->dev);
358d5185965SDmitry Osipenko }
359d5185965SDmitry Osipenko #endif
360d5185965SDmitry Osipenko
36106867a36SThierry Reding /*
3624010e729SThierry Reding * We may not always want to enable IOMMU support (for example if the
3634010e729SThierry Reding * host1x firewall is already enabled and we don't support addressing
3644010e729SThierry Reding * more than 32 bits of physical memory), so check for that first.
3654010e729SThierry Reding *
3664010e729SThierry Reding * Similarly, if host1x is already attached to an IOMMU (via the DMA
3674010e729SThierry Reding * API), don't try to attach again.
36806867a36SThierry Reding */
3694010e729SThierry Reding if (!host1x_wants_iommu(host) || domain)
37006867a36SThierry Reding return domain;
37106867a36SThierry Reding
37206867a36SThierry Reding host->group = iommu_group_get(host->dev);
37306867a36SThierry Reding if (host->group) {
37406867a36SThierry Reding struct iommu_domain_geometry *geometry;
37506867a36SThierry Reding dma_addr_t start, end;
37606867a36SThierry Reding unsigned long order;
37706867a36SThierry Reding
37806867a36SThierry Reding err = iova_cache_get();
37906867a36SThierry Reding if (err < 0)
38006867a36SThierry Reding goto put_group;
38106867a36SThierry Reding
38206867a36SThierry Reding host->domain = iommu_domain_alloc(&platform_bus_type);
38306867a36SThierry Reding if (!host->domain) {
38406867a36SThierry Reding err = -ENOMEM;
38506867a36SThierry Reding goto put_cache;
38606867a36SThierry Reding }
38706867a36SThierry Reding
38806867a36SThierry Reding err = iommu_attach_group(host->domain, host->group);
38906867a36SThierry Reding if (err) {
39006867a36SThierry Reding if (err == -ENODEV)
39106867a36SThierry Reding err = 0;
39206867a36SThierry Reding
39306867a36SThierry Reding goto free_domain;
39406867a36SThierry Reding }
39506867a36SThierry Reding
39606867a36SThierry Reding geometry = &host->domain->geometry;
39706867a36SThierry Reding start = geometry->aperture_start & host->info->dma_mask;
39806867a36SThierry Reding end = geometry->aperture_end & host->info->dma_mask;
39906867a36SThierry Reding
40006867a36SThierry Reding order = __ffs(host->domain->pgsize_bitmap);
40106867a36SThierry Reding init_iova_domain(&host->iova, 1UL << order, start >> order);
40206867a36SThierry Reding host->iova_end = end;
40306867a36SThierry Reding
40406867a36SThierry Reding domain = host->domain;
40506867a36SThierry Reding }
40606867a36SThierry Reding
40706867a36SThierry Reding return domain;
40806867a36SThierry Reding
40906867a36SThierry Reding free_domain:
41006867a36SThierry Reding iommu_domain_free(host->domain);
41106867a36SThierry Reding host->domain = NULL;
41206867a36SThierry Reding put_cache:
41306867a36SThierry Reding iova_cache_put();
41406867a36SThierry Reding put_group:
41506867a36SThierry Reding iommu_group_put(host->group);
41606867a36SThierry Reding host->group = NULL;
41706867a36SThierry Reding
41806867a36SThierry Reding return ERR_PTR(err);
41906867a36SThierry Reding }
42006867a36SThierry Reding
host1x_iommu_init(struct host1x * host)42106867a36SThierry Reding static int host1x_iommu_init(struct host1x *host)
42206867a36SThierry Reding {
42306867a36SThierry Reding u64 mask = host->info->dma_mask;
42406867a36SThierry Reding struct iommu_domain *domain;
42506867a36SThierry Reding int err;
42606867a36SThierry Reding
42706867a36SThierry Reding domain = host1x_iommu_attach(host);
42806867a36SThierry Reding if (IS_ERR(domain)) {
42906867a36SThierry Reding err = PTR_ERR(domain);
43006867a36SThierry Reding dev_err(host->dev, "failed to attach to IOMMU: %d\n", err);
43106867a36SThierry Reding return err;
43206867a36SThierry Reding }
43306867a36SThierry Reding
43406867a36SThierry Reding /*
43506867a36SThierry Reding * If we're not behind an IOMMU make sure we don't get push buffers
43606867a36SThierry Reding * that are allocated outside of the range addressable by the GATHER
43706867a36SThierry Reding * opcode.
43806867a36SThierry Reding *
43906867a36SThierry Reding * Newer generations of Tegra (Tegra186 and later) support a wide
44006867a36SThierry Reding * variant of the GATHER opcode that allows addressing more bits.
44106867a36SThierry Reding */
44206867a36SThierry Reding if (!domain && !host->info->has_wide_gather)
44306867a36SThierry Reding mask = DMA_BIT_MASK(32);
44406867a36SThierry Reding
44506867a36SThierry Reding err = dma_coerce_mask_and_coherent(host->dev, mask);
44606867a36SThierry Reding if (err < 0) {
44706867a36SThierry Reding dev_err(host->dev, "failed to set DMA mask: %d\n", err);
44806867a36SThierry Reding return err;
44906867a36SThierry Reding }
45006867a36SThierry Reding
45106867a36SThierry Reding return 0;
45206867a36SThierry Reding }
45306867a36SThierry Reding
host1x_iommu_exit(struct host1x * host)45406867a36SThierry Reding static void host1x_iommu_exit(struct host1x *host)
45506867a36SThierry Reding {
45606867a36SThierry Reding if (host->domain) {
45706867a36SThierry Reding put_iova_domain(&host->iova);
45806867a36SThierry Reding iommu_detach_group(host->domain, host->group);
45906867a36SThierry Reding
46006867a36SThierry Reding iommu_domain_free(host->domain);
46106867a36SThierry Reding host->domain = NULL;
46206867a36SThierry Reding
46306867a36SThierry Reding iova_cache_put();
46406867a36SThierry Reding
46506867a36SThierry Reding iommu_group_put(host->group);
46606867a36SThierry Reding host->group = NULL;
46706867a36SThierry Reding }
46806867a36SThierry Reding }
46906867a36SThierry Reding
host1x_get_resets(struct host1x * host)4706b6776e2SDmitry Osipenko static int host1x_get_resets(struct host1x *host)
4716b6776e2SDmitry Osipenko {
4726b6776e2SDmitry Osipenko int err;
4736b6776e2SDmitry Osipenko
4746b6776e2SDmitry Osipenko host->resets[0].id = "mc";
4756b6776e2SDmitry Osipenko host->resets[1].id = "host1x";
4766b6776e2SDmitry Osipenko host->nresets = ARRAY_SIZE(host->resets);
4776b6776e2SDmitry Osipenko
4786b6776e2SDmitry Osipenko err = devm_reset_control_bulk_get_optional_exclusive_released(
4796b6776e2SDmitry Osipenko host->dev, host->nresets, host->resets);
4806b6776e2SDmitry Osipenko if (err) {
4816b6776e2SDmitry Osipenko dev_err(host->dev, "failed to get reset: %d\n", err);
4826b6776e2SDmitry Osipenko return err;
4836b6776e2SDmitry Osipenko }
4846b6776e2SDmitry Osipenko
4856b6776e2SDmitry Osipenko return 0;
4866b6776e2SDmitry Osipenko }
4876b6776e2SDmitry Osipenko
host1x_probe(struct platform_device * pdev)48875471687STerje Bergstrom static int host1x_probe(struct platform_device *pdev)
48975471687STerje Bergstrom {
49075471687STerje Bergstrom struct host1x *host;
49175471687STerje Bergstrom int err;
49275471687STerje Bergstrom
4936a341fdfSThierry Reding host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
4946a341fdfSThierry Reding if (!host)
4956a341fdfSThierry Reding return -ENOMEM;
49675471687STerje Bergstrom
4976a341fdfSThierry Reding host->info = of_device_get_match_data(&pdev->dev);
49875471687STerje Bergstrom
499f1b53c4eSMikko Perttunen if (host->info->has_hypervisor) {
50097dea367SMikko Perttunen host->regs = devm_platform_ioremap_resource_byname(pdev, "vm");
50197dea367SMikko Perttunen if (IS_ERR(host->regs))
50297dea367SMikko Perttunen return PTR_ERR(host->regs);
503f1b53c4eSMikko Perttunen
50497dea367SMikko Perttunen host->hv_regs = devm_platform_ioremap_resource_byname(pdev, "hypervisor");
50597dea367SMikko Perttunen if (IS_ERR(host->hv_regs))
50697dea367SMikko Perttunen return PTR_ERR(host->hv_regs);
50797dea367SMikko Perttunen
50897dea367SMikko Perttunen if (host->info->has_common) {
50997dea367SMikko Perttunen host->common_regs = devm_platform_ioremap_resource_byname(pdev, "common");
51097dea367SMikko Perttunen if (IS_ERR(host->common_regs))
51197dea367SMikko Perttunen return PTR_ERR(host->common_regs);
512f1b53c4eSMikko Perttunen }
513f1b53c4eSMikko Perttunen } else {
51497dea367SMikko Perttunen host->regs = devm_platform_ioremap_resource(pdev, 0);
51597dea367SMikko Perttunen if (IS_ERR(host->regs))
51697dea367SMikko Perttunen return PTR_ERR(host->regs);
517f1b53c4eSMikko Perttunen }
51875471687STerje Bergstrom
519625d4ffbSMikko Perttunen host->syncpt_irq = platform_get_irq(pdev, 0);
5207d31677bSArnd Bergmann if (host->syncpt_irq < 0)
5217d31677bSArnd Bergmann return host->syncpt_irq;
52275471687STerje Bergstrom
523776dc384SThierry Reding mutex_init(&host->devices_lock);
524776dc384SThierry Reding INIT_LIST_HEAD(&host->devices);
525776dc384SThierry Reding INIT_LIST_HEAD(&host->list);
52675471687STerje Bergstrom host->dev = &pdev->dev;
52775471687STerje Bergstrom
52875471687STerje Bergstrom /* set common host1x device data */
52975471687STerje Bergstrom platform_set_drvdata(pdev, host);
53075471687STerje Bergstrom
531d98914ebSThierry Reding host->dev->dma_parms = &host->dma_parms;
532d98914ebSThierry Reding dma_set_max_seg_size(host->dev, UINT_MAX);
533d98914ebSThierry Reding
53475471687STerje Bergstrom if (host->info->init) {
53575471687STerje Bergstrom err = host->info->init(host);
53675471687STerje Bergstrom if (err)
53775471687STerje Bergstrom return err;
53875471687STerje Bergstrom }
53975471687STerje Bergstrom
54075471687STerje Bergstrom host->clk = devm_clk_get(&pdev->dev, NULL);
54175471687STerje Bergstrom if (IS_ERR(host->clk)) {
54275471687STerje Bergstrom err = PTR_ERR(host->clk);
5434bb923e8SThierry Reding
5444bb923e8SThierry Reding if (err != -EPROBE_DEFER)
5454bb923e8SThierry Reding dev_err(&pdev->dev, "failed to get clock: %d\n", err);
5464bb923e8SThierry Reding
54775471687STerje Bergstrom return err;
54875471687STerje Bergstrom }
54975471687STerje Bergstrom
5506b6776e2SDmitry Osipenko err = host1x_get_resets(host);
5516b6776e2SDmitry Osipenko if (err)
552b386c6b7SThierry Reding return err;
553af1cbfb9SThierry Reding
554e5d5db1aSChristophe JAILLET host1x_bo_cache_init(&host->cache);
555e5d5db1aSChristophe JAILLET
55606867a36SThierry Reding err = host1x_iommu_init(host);
55706867a36SThierry Reding if (err < 0) {
55806867a36SThierry Reding dev_err(&pdev->dev, "failed to setup IOMMU: %d\n", err);
559e5d5db1aSChristophe JAILLET goto destroy_cache;
56041c3068cSThierry Reding }
561404bfb78SMikko Perttunen
5628474b025SMikko Perttunen err = host1x_channel_list_init(&host->channel_list,
5638474b025SMikko Perttunen host->info->nb_channels);
5646579324aSTerje Bergstrom if (err) {
5656579324aSTerje Bergstrom dev_err(&pdev->dev, "failed to initialize channel list\n");
56606867a36SThierry Reding goto iommu_exit;
5676579324aSTerje Bergstrom }
5686579324aSTerje Bergstrom
5698aa5bcb6SMikko Perttunen err = host1x_memory_context_list_init(host);
5708aa5bcb6SMikko Perttunen if (err) {
5718aa5bcb6SMikko Perttunen dev_err(&pdev->dev, "failed to initialize context list\n");
5728aa5bcb6SMikko Perttunen goto free_channels;
5738aa5bcb6SMikko Perttunen }
5748aa5bcb6SMikko Perttunen
57575471687STerje Bergstrom err = host1x_syncpt_init(host);
57675471687STerje Bergstrom if (err) {
57775471687STerje Bergstrom dev_err(&pdev->dev, "failed to initialize syncpts\n");
5788aa5bcb6SMikko Perttunen goto free_contexts;
57975471687STerje Bergstrom }
58075471687STerje Bergstrom
581625d4ffbSMikko Perttunen err = host1x_intr_init(host);
5827ede0b0bSTerje Bergstrom if (err) {
5837ede0b0bSTerje Bergstrom dev_err(&pdev->dev, "failed to initialize interrupts\n");
58406867a36SThierry Reding goto deinit_syncpt;
5857ede0b0bSTerje Bergstrom }
5867ede0b0bSTerje Bergstrom
5876b6776e2SDmitry Osipenko pm_runtime_enable(&pdev->dev);
5886236451dSTerje Bergstrom
5896b6776e2SDmitry Osipenko err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
5906b6776e2SDmitry Osipenko if (err)
5916b6776e2SDmitry Osipenko goto pm_disable;
5926b6776e2SDmitry Osipenko
5936b6776e2SDmitry Osipenko /* the driver's code isn't ready yet for the dynamic RPM */
5946b6776e2SDmitry Osipenko err = pm_runtime_resume_and_get(&pdev->dev);
5956b6776e2SDmitry Osipenko if (err)
5966b6776e2SDmitry Osipenko goto pm_disable;
5976b6776e2SDmitry Osipenko
5986b6776e2SDmitry Osipenko host1x_debug_init(host);
5996841482bSThierry Reding
600776dc384SThierry Reding err = host1x_register(host);
601776dc384SThierry Reding if (err < 0)
602109be8b2SChristophe JAILLET goto deinit_debugfs;
603692e6d7bSTerje Bergstrom
604ca2030d5SThierry Reding err = devm_of_platform_populate(&pdev->dev);
605ca2030d5SThierry Reding if (err < 0)
606ca2030d5SThierry Reding goto unregister;
607ca2030d5SThierry Reding
60875471687STerje Bergstrom return 0;
6097ede0b0bSTerje Bergstrom
610ca2030d5SThierry Reding unregister:
611ca2030d5SThierry Reding host1x_unregister(host);
612109be8b2SChristophe JAILLET deinit_debugfs:
613109be8b2SChristophe JAILLET host1x_debug_deinit(host);
6146b6776e2SDmitry Osipenko
6156b6776e2SDmitry Osipenko pm_runtime_put_sync_suspend(&pdev->dev);
6166b6776e2SDmitry Osipenko pm_disable:
6176b6776e2SDmitry Osipenko pm_runtime_disable(&pdev->dev);
6186b6776e2SDmitry Osipenko
619776dc384SThierry Reding host1x_intr_deinit(host);
62006867a36SThierry Reding deinit_syncpt:
6217ede0b0bSTerje Bergstrom host1x_syncpt_deinit(host);
6228aa5bcb6SMikko Perttunen free_contexts:
6238aa5bcb6SMikko Perttunen host1x_memory_context_list_free(&host->context_list);
62406867a36SThierry Reding free_channels:
6258474b025SMikko Perttunen host1x_channel_list_free(&host->channel_list);
62606867a36SThierry Reding iommu_exit:
62706867a36SThierry Reding host1x_iommu_exit(host);
628e5d5db1aSChristophe JAILLET destroy_cache:
629e5d5db1aSChristophe JAILLET host1x_bo_cache_destroy(&host->cache);
630404bfb78SMikko Perttunen
6317ede0b0bSTerje Bergstrom return err;
63275471687STerje Bergstrom }
63375471687STerje Bergstrom
host1x_remove(struct platform_device * pdev)634452e7f0cSThierry Reding static int host1x_remove(struct platform_device *pdev)
63575471687STerje Bergstrom {
63675471687STerje Bergstrom struct host1x *host = platform_get_drvdata(pdev);
63775471687STerje Bergstrom
638776dc384SThierry Reding host1x_unregister(host);
63944156eeeSThierry Reding host1x_debug_deinit(host);
6406b6776e2SDmitry Osipenko
6416b6776e2SDmitry Osipenko pm_runtime_force_suspend(&pdev->dev);
6426b6776e2SDmitry Osipenko
6437ede0b0bSTerje Bergstrom host1x_intr_deinit(host);
64475471687STerje Bergstrom host1x_syncpt_deinit(host);
6458aa5bcb6SMikko Perttunen host1x_memory_context_list_free(&host->context_list);
646025c6643SChristophe JAILLET host1x_channel_list_free(&host->channel_list);
64706867a36SThierry Reding host1x_iommu_exit(host);
6481f39b1dfSThierry Reding host1x_bo_cache_destroy(&host->cache);
649404bfb78SMikko Perttunen
65075471687STerje Bergstrom return 0;
65175471687STerje Bergstrom }
65275471687STerje Bergstrom
host1x_runtime_suspend(struct device * dev)6536b6776e2SDmitry Osipenko static int __maybe_unused host1x_runtime_suspend(struct device *dev)
6546b6776e2SDmitry Osipenko {
6556b6776e2SDmitry Osipenko struct host1x *host = dev_get_drvdata(dev);
6566b6776e2SDmitry Osipenko int err;
6576b6776e2SDmitry Osipenko
6586b6776e2SDmitry Osipenko host1x_intr_stop(host);
6596b6776e2SDmitry Osipenko host1x_syncpt_save(host);
6606b6776e2SDmitry Osipenko
6616b6776e2SDmitry Osipenko err = reset_control_bulk_assert(host->nresets, host->resets);
6626b6776e2SDmitry Osipenko if (err) {
6636b6776e2SDmitry Osipenko dev_err(dev, "failed to assert reset: %d\n", err);
6646b6776e2SDmitry Osipenko goto resume_host1x;
6656b6776e2SDmitry Osipenko }
6666b6776e2SDmitry Osipenko
6676b6776e2SDmitry Osipenko usleep_range(1000, 2000);
6686b6776e2SDmitry Osipenko
6696b6776e2SDmitry Osipenko clk_disable_unprepare(host->clk);
6706b6776e2SDmitry Osipenko reset_control_bulk_release(host->nresets, host->resets);
6716b6776e2SDmitry Osipenko
6726b6776e2SDmitry Osipenko return 0;
6736b6776e2SDmitry Osipenko
6746b6776e2SDmitry Osipenko resume_host1x:
675939179faSMikko Perttunen host1x_setup_virtualization_tables(host);
6766b6776e2SDmitry Osipenko host1x_syncpt_restore(host);
6776b6776e2SDmitry Osipenko host1x_intr_start(host);
6786b6776e2SDmitry Osipenko
6796b6776e2SDmitry Osipenko return err;
6806b6776e2SDmitry Osipenko }
6816b6776e2SDmitry Osipenko
host1x_runtime_resume(struct device * dev)6826b6776e2SDmitry Osipenko static int __maybe_unused host1x_runtime_resume(struct device *dev)
6836b6776e2SDmitry Osipenko {
6846b6776e2SDmitry Osipenko struct host1x *host = dev_get_drvdata(dev);
6856b6776e2SDmitry Osipenko int err;
6866b6776e2SDmitry Osipenko
6876b6776e2SDmitry Osipenko err = reset_control_bulk_acquire(host->nresets, host->resets);
6886b6776e2SDmitry Osipenko if (err) {
6896b6776e2SDmitry Osipenko dev_err(dev, "failed to acquire reset: %d\n", err);
6906b6776e2SDmitry Osipenko return err;
6916b6776e2SDmitry Osipenko }
6926b6776e2SDmitry Osipenko
6936b6776e2SDmitry Osipenko err = clk_prepare_enable(host->clk);
6946b6776e2SDmitry Osipenko if (err) {
6956b6776e2SDmitry Osipenko dev_err(dev, "failed to enable clock: %d\n", err);
6966b6776e2SDmitry Osipenko goto release_reset;
6976b6776e2SDmitry Osipenko }
6986b6776e2SDmitry Osipenko
6996b6776e2SDmitry Osipenko err = reset_control_bulk_deassert(host->nresets, host->resets);
7006b6776e2SDmitry Osipenko if (err < 0) {
7016b6776e2SDmitry Osipenko dev_err(dev, "failed to deassert reset: %d\n", err);
7026b6776e2SDmitry Osipenko goto disable_clk;
7036b6776e2SDmitry Osipenko }
7046b6776e2SDmitry Osipenko
705939179faSMikko Perttunen host1x_setup_virtualization_tables(host);
7066b6776e2SDmitry Osipenko host1x_syncpt_restore(host);
7076b6776e2SDmitry Osipenko host1x_intr_start(host);
7086b6776e2SDmitry Osipenko
7096b6776e2SDmitry Osipenko return 0;
7106b6776e2SDmitry Osipenko
7116b6776e2SDmitry Osipenko disable_clk:
7126b6776e2SDmitry Osipenko clk_disable_unprepare(host->clk);
7136b6776e2SDmitry Osipenko release_reset:
7146b6776e2SDmitry Osipenko reset_control_bulk_release(host->nresets, host->resets);
7156b6776e2SDmitry Osipenko
7166b6776e2SDmitry Osipenko return err;
7176b6776e2SDmitry Osipenko }
7186b6776e2SDmitry Osipenko
7196b6776e2SDmitry Osipenko static const struct dev_pm_ops host1x_pm_ops = {
7206b6776e2SDmitry Osipenko SET_RUNTIME_PM_OPS(host1x_runtime_suspend, host1x_runtime_resume,
7216b6776e2SDmitry Osipenko NULL)
7226b6776e2SDmitry Osipenko /* TODO: add system suspend-resume once driver will be ready for that */
7236b6776e2SDmitry Osipenko };
7246b6776e2SDmitry Osipenko
725692e6d7bSTerje Bergstrom static struct platform_driver tegra_host1x_driver = {
72675471687STerje Bergstrom .driver = {
72775471687STerje Bergstrom .name = "tegra-host1x",
72875471687STerje Bergstrom .of_match_table = host1x_of_match,
7296b6776e2SDmitry Osipenko .pm = &host1x_pm_ops,
73075471687STerje Bergstrom },
731452e7f0cSThierry Reding .probe = host1x_probe,
732452e7f0cSThierry Reding .remove = host1x_remove,
73375471687STerje Bergstrom };
73475471687STerje Bergstrom
73528fae81fSThierry Reding static struct platform_driver * const drivers[] = {
73628fae81fSThierry Reding &tegra_host1x_driver,
73728fae81fSThierry Reding &tegra_mipi_driver,
73828fae81fSThierry Reding };
73928fae81fSThierry Reding
tegra_host1x_init(void)740692e6d7bSTerje Bergstrom static int __init tegra_host1x_init(void)
741692e6d7bSTerje Bergstrom {
742692e6d7bSTerje Bergstrom int err;
74375471687STerje Bergstrom
744f4c5cf88SThierry Reding err = bus_register(&host1x_bus_type);
745692e6d7bSTerje Bergstrom if (err < 0)
746692e6d7bSTerje Bergstrom return err;
747692e6d7bSTerje Bergstrom
74828fae81fSThierry Reding err = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
7494de6a2d6SThierry Reding if (err < 0)
750f4c5cf88SThierry Reding bus_unregister(&host1x_bus_type);
75128fae81fSThierry Reding
7524de6a2d6SThierry Reding return err;
753692e6d7bSTerje Bergstrom }
754692e6d7bSTerje Bergstrom module_init(tegra_host1x_init);
755692e6d7bSTerje Bergstrom
tegra_host1x_exit(void)756692e6d7bSTerje Bergstrom static void __exit tegra_host1x_exit(void)
757692e6d7bSTerje Bergstrom {
75828fae81fSThierry Reding platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
759f4c5cf88SThierry Reding bus_unregister(&host1x_bus_type);
760692e6d7bSTerje Bergstrom }
761692e6d7bSTerje Bergstrom module_exit(tegra_host1x_exit);
762692e6d7bSTerje Bergstrom
763501be6c1SThierry Reding /**
764501be6c1SThierry Reding * host1x_get_dma_mask() - query the supported DMA mask for host1x
765501be6c1SThierry Reding * @host1x: host1x instance
766501be6c1SThierry Reding *
767501be6c1SThierry Reding * Note that this returns the supported DMA mask for host1x, which can be
768501be6c1SThierry Reding * different from the applicable DMA mask under certain circumstances.
769501be6c1SThierry Reding */
host1x_get_dma_mask(struct host1x * host1x)770501be6c1SThierry Reding u64 host1x_get_dma_mask(struct host1x *host1x)
771501be6c1SThierry Reding {
772501be6c1SThierry Reding return host1x->info->dma_mask;
773501be6c1SThierry Reding }
774501be6c1SThierry Reding EXPORT_SYMBOL(host1x_get_dma_mask);
775501be6c1SThierry Reding
776692e6d7bSTerje Bergstrom MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
77775471687STerje Bergstrom MODULE_AUTHOR("Terje Bergstrom <tbergstrom@nvidia.com>");
77875471687STerje Bergstrom MODULE_DESCRIPTION("Host1x driver for Tegra products");
77975471687STerje Bergstrom MODULE_LICENSE("GPL");
780