summaryrefslogtreecommitdiffstats
path: root/drivers/iommu/shmobile-ipmmu.c
blob: bd97adecb1fd28fb1419db63c261a1fadd7cf4f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * IPMMU/IPMMUI
 * Copyright (C) 2012  Hideki EIRAKU
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 */

#include <linux/err.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/platform_data/sh_ipmmu.h>
#include "shmobile-ipmmu.h"

#define IMCTR1 0x000
#define IMCTR2 0x004
#define IMASID 0x010
#define IMTTBR 0x014
#define IMTTBCR 0x018

#define IMCTR1_TLBEN (1 << 0)
#define IMCTR1_FLUSH (1 << 1)

static void ipmmu_reg_write(struct shmobile_ipmmu *ipmmu, unsigned long reg_off,
			    unsigned long data)
{
	iowrite32(data, ipmmu->ipmmu_base + reg_off);
}

void ipmmu_tlb_flush(struct shmobile_ipmmu *ipmmu)
{
	if (!ipmmu)
		return;

	spin_lock(&ipmmu->flush_lock);
	if (ipmmu->tlb_enabled)
		ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH | IMCTR1_TLBEN);
	else
		ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH);
	spin_unlock(&ipmmu->flush_lock);
}

void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size,
		   int asid)
{
	if (!ipmmu)
		return;

	spin_lock(&ipmmu->flush_lock);
	switch (size) {
	default:
		ipmmu->tlb_enabled = 0;
		break;
	case 0x2000:
		ipmmu_reg_write(ipmmu, IMTTBCR, 1);
		ipmmu->tlb_enabled = 1;
		break;
	case 0x1000:
		ipmmu_reg_write(ipmmu, IMTTBCR, 2);
		ipmmu->tlb_enabled = 1;
		break;
	case 0x800:
		ipmmu_reg_write(ipmmu, IMTTBCR, 3);
		ipmmu->tlb_enabled = 1;
		break;
	case 0x400:
		ipmmu_reg_write(ipmmu, IMTTBCR, 4);
		ipmmu->tlb_enabled = 1;
		break;
	case 0x200:
		ipmmu_reg_write(ipmmu, IMTTBCR, 5);
		ipmmu->tlb_enabled = 1;
		break;
	case 0x100:
		ipmmu_reg_write(ipmmu, IMTTBCR, 6);
		ipmmu->tlb_enabled = 1;
		break;
	case 0x80:
		ipmmu_reg_write(ipmmu, IMTTBCR, 7);
		ipmmu->tlb_enabled = 1;
		break;
	}
	ipmmu_reg_write(ipmmu, IMTTBR, phys);
	ipmmu_reg_write(ipmmu, IMASID, asid);
	spin_unlock(&ipmmu->flush_lock);
}

static int ipmmu_probe(struct platform_device *pdev)
{
	struct shmobile_ipmmu *ipmmu;
	struct resource *res;
	struct shmobile_ipmmu_platform_data *pdata = pdev->dev.platform_data;

	ipmmu = devm_kzalloc(&pdev->dev, sizeof(*ipmmu), GFP_KERNEL);
	if (!ipmmu) {
		dev_err(&pdev->dev, "cannot allocate device data\n");
		return -ENOMEM;
	}
	spin_lock_init(&ipmmu->flush_lock);
	ipmmu->dev = &pdev->dev;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	ipmmu->ipmmu_base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(ipmmu->ipmmu_base))
		return PTR_ERR(ipmmu->ipmmu_base);

	ipmmu->dev_names = pdata->dev_names;
	ipmmu->num_dev_names = pdata->num_dev_names;
	platform_set_drvdata(pdev, ipmmu);
	ipmmu_reg_write(ipmmu, IMCTR1, 0x0); /* disable TLB */
	ipmmu_reg_write(ipmmu, IMCTR2, 0x0); /* disable PMB */
	return ipmmu_iommu_init(ipmmu);
}

static struct platform_driver ipmmu_driver = {
	.probe = ipmmu_probe,
	.driver = {
		.owner = THIS_MODULE,
		.name = "ipmmu",
	},
};

static int __init ipmmu_init(void)
{
	return platform_driver_register(&ipmmu_driver);
}
subsys_initcall(ipmmu_init);