Blame view

drivers/iommu/shmobile-ipmmu.c 3.2 KB
c2c460f7c   Hideki EIRAKU   iommu/shmobile: A...
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
  /*
   * 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;
e87c621dc   Laurent Pinchart   iommu/shmobile: T...
37
  	spin_lock(&ipmmu->flush_lock);
c2c460f7c   Hideki EIRAKU   iommu/shmobile: A...
38
39
40
41
  	if (ipmmu->tlb_enabled)
  		ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH | IMCTR1_TLBEN);
  	else
  		ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH);
e87c621dc   Laurent Pinchart   iommu/shmobile: T...
42
  	spin_unlock(&ipmmu->flush_lock);
c2c460f7c   Hideki EIRAKU   iommu/shmobile: A...
43
44
45
46
47
48
49
  }
  
  void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size,
  		   int asid)
  {
  	if (!ipmmu)
  		return;
e87c621dc   Laurent Pinchart   iommu/shmobile: T...
50
  	spin_lock(&ipmmu->flush_lock);
c2c460f7c   Hideki EIRAKU   iommu/shmobile: A...
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
  	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);
e87c621dc   Laurent Pinchart   iommu/shmobile: T...
86
  	spin_unlock(&ipmmu->flush_lock);
c2c460f7c   Hideki EIRAKU   iommu/shmobile: A...
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  }
  
  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;
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (!res) {
  		dev_err(&pdev->dev, "cannot get platform resources
  ");
  		return -ENOENT;
  	}
  	ipmmu = devm_kzalloc(&pdev->dev, sizeof(*ipmmu), GFP_KERNEL);
  	if (!ipmmu) {
  		dev_err(&pdev->dev, "cannot allocate device data
  ");
  		return -ENOMEM;
  	}
e87c621dc   Laurent Pinchart   iommu/shmobile: T...
107
  	spin_lock_init(&ipmmu->flush_lock);
c2c460f7c   Hideki EIRAKU   iommu/shmobile: A...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  	ipmmu->dev = &pdev->dev;
  	ipmmu->ipmmu_base = devm_ioremap_nocache(&pdev->dev, res->start,
  						resource_size(res));
  	if (!ipmmu->ipmmu_base) {
  		dev_err(&pdev->dev, "ioremap_nocache failed
  ");
  		return -ENOMEM;
  	}
  	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 */
  	ipmmu_iommu_init(ipmmu);
  	return 0;
  }
  
  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);