From mboxrd@z Thu Jan 1 00:00:00 1970 From: =?euc-kr?B?wbaw5sij?= Subject: [PATCH 3/4] iommu/exynos: Add iommu driver for Exynos4 Platforms Date: Sat, 24 Sep 2011 07:35:45 +0000 (GMT) Message-ID: <5525949.50001316849744994.JavaMail.weblogic@epml24> Reply-To: pullip.cho@samsung.com Mime-Version: 1.0 Content-Type: text/plain; charset=euc-kr Content-Transfer-Encoding: base64 Return-path: Received: from mailout1.samsung.com ([203.254.224.24]:32612 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751512Ab1IXHfr (ORCPT ); Sat, 24 Sep 2011 03:35:47 -0400 Received: from epcpsbge8.samsung.com (mailout1.samsung.com [203.254.224.24]) by mailout1.samsung.com (Oracle Communications Messaging Exchange Server 7u4-19.01 64bit (built Sep 7 2010)) with ESMTP id <0LS000IU3NRID390@mailout1.samsung.com> for linux-samsung-soc@vger.kernel.org; Sat, 24 Sep 2011 16:35:45 +0900 (KST) MIME-version: 1.0 Sender: linux-samsung-soc-owner@vger.kernel.org List-Id: linux-samsung-soc@vger.kernel.org To: "linux-arm-kernel@lists.infradead.org" , "linux-samsung-soc@vger.kernel.org" , iommu@lists.linux-foundatio Cc: =?euc-kr?Q?=C1=B6=B0=E6=C8=A3?= , "joerg.roedel@amd.com" , =?euc-kr?Q?=C0=CC=BB=F3=C7=F6?= , =?euc-kr?Q?=B1=E8=B1=B9=C1=F8?= , =?euc-kr?Q?=B1=E8=BF=B5=B6=F4?= , "ohad@wizery.com" VGhpcyBpcyB0aGUgU3lzdGVtIE1NVSBkcml2ZXIgYW5kIElPTU1VIEFQSSBpbXBsZW1lbnRhdGlv biBmb3INCkV4eW5vczQgU09DIHBsYXRmb3Jtcy4gRXh5bm9zNCBwbGF0Zm9ybXMgaGFzIG1vcmUg dGhhbiAxMCBTeXN0ZW0NCk1NVXMgZGVkaWNhdGVkIGZvciBlYWNoIG11bHRpbWVkaWEgYWNjZWxs ZXJhdG9ycy4NCg0KU2lnbmVkLW9mZi1ieTogS3lvbmdIbyBDaG8gDQotLS0NCmRyaXZlcnMvaW9t bXUvS2NvbmZpZyAgICAgICAgfCAgIDE0ICsNCmRyaXZlcnMvaW9tbXUvTWFrZWZpbGUgICAgICAg fCAgICAxICsNCmRyaXZlcnMvaW9tbXUvZXh5bm9zX2lvbW11LmMgfCAgODU5ICsrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKw0KMyBmaWxlcyBjaGFuZ2VkLCA4NzQgaW5z ZXJ0aW9ucygrKSwgMCBkZWxldGlvbnMoLSkNCmNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL2lv bW11L2V4eW5vc19pb21tdS5jDQoNCmRpZmYgLS1naXQgYS9kcml2ZXJzL2lvbW11L0tjb25maWcg Yi9kcml2ZXJzL2lvbW11L0tjb25maWcNCmluZGV4IGI1N2IzZmEuLjFjNzU0Y2QgMTAwNjQ0DQot LS0gYS9kcml2ZXJzL2lvbW11L0tjb25maWcNCisrKyBiL2RyaXZlcnMvaW9tbXUvS2NvbmZpZw0K QEAgLTEwNyw0ICsxMDcsMTggQEAgY29uZmlnIElOVFJfUkVNQVANCiAgVG8gdXNlIHgyYXBpYyBt b2RlIGluIHRoZSBDUFUncyB3aGljaCBzdXBwb3J0IHgyQVBJQyBlbmhhbmNlbWVudHMgb3INCiAg dG8gc3VwcG9ydCBwbGF0Zm9ybXMgd2l0aCBDUFUncyBoYXZpbmcgPiA4IGJpdCBBUElDIElELCBz YXkgWS4NCg0KKyMgRVhZTk9TIElPTU1VIHN1cHBvcnQNCitjb25maWcgRVhZTk9TX0lPTU1VDQor IGJvb2wgIkV4eW5vcyBJT01NVSBTdXBwb3J0Ig0KKyBkZXBlbmRzIG9uIEFSQ0hfRVhZTk9TNA0K KyBzZWxlY3QgSU9NTVVfQVBJDQorIHNlbGVjdCBFWFlOT1M0X0RFVl9TWVNNTVUNCisgaGVscA0K KyAgIFN1cHBvcnQgZm9yIHRoZSBJT01NVXMgKFN5c3RlbSBNTVVzKSBTYW1zdW5nIEV4eW5vcyBh cHBsaWNhdGlvbg0KKyAgIHByb2Nlc3NvciBmYW1pbHkuIFRoaXMgZW5hYmxlcyBIL1cgbXVsdGlt ZWRpYSBhY2NlbGxlcmF0b3JzIHRvIHZpZXcNCisgICBub24tbGluZWFyIHBoeXNpY2FsIG1lbW9y eSBjaHVua3MgYXMgYSBsaW5lYXIgbWVtb3J5IGluIHRoZWlyIHZpcnR1YWwNCisgICBhZGRyZXNz IHNwYWNlcy4NCisNCisgICBJZiB1bnN1cmUsIHNheSBOIGhlcmUuDQorDQplbmRpZiAjIElPTU1V X1NVUFBPUlQNCmRpZmYgLS1naXQgYS9kcml2ZXJzL2lvbW11L01ha2VmaWxlIGIvZHJpdmVycy9p b21tdS9NYWtlZmlsZQ0KaW5kZXggNGQ0ZDc3ZC4uMWViOTI0ZiAxMDA2NDQNCi0tLSBhL2RyaXZl cnMvaW9tbXUvTWFrZWZpbGUNCisrKyBiL2RyaXZlcnMvaW9tbXUvTWFrZWZpbGUNCkBAIC0zLDMg KzMsNCBAQCBvYmotJChDT05GSUdfTVNNX0lPTU1VKSArPSBtc21faW9tbXUubyBtc21faW9tbXVf ZGV2Lm8NCm9iai0kKENPTkZJR19BTURfSU9NTVUpICs9IGFtZF9pb21tdS5vIGFtZF9pb21tdV9p bml0Lm8NCm9iai0kKENPTkZJR19ETUFSKSArPSBkbWFyLm8gaW92YS5vIGludGVsLWlvbW11Lm8N Cm9iai0kKENPTkZJR19JTlRSX1JFTUFQKSArPSBkbWFyLm8gaW50cl9yZW1hcHBpbmcubw0KK29i ai0kKENPTkZJR19FWFlOT1NfSU9NTVUpICs9IGV4eW5vc19pb21tdS5vDQpkaWZmIC0tZ2l0IGEv ZHJpdmVycy9pb21tdS9leHlub3NfaW9tbXUuYyBiL2RyaXZlcnMvaW9tbXUvZXh5bm9zX2lvbW11 LmMNCm5ldyBmaWxlIG1vZGUgMTAwNjQ0DQppbmRleCAwMDAwMDAwLi5mZTViNWQ4DQotLS0gL2Rl di9udWxsDQorKysgYi9kcml2ZXJzL2lvbW11L2V4eW5vc19pb21tdS5jDQpAQCAtMCwwICsxLDg1 OSBAQA0KKy8qIGxpbnV4L2RyaXZlcnMvaW9tbXUvZXh5bm9zX2lvbW11LmMNCisgKg0KKyAqIENv cHlyaWdodCAoYykgMjAxMSBTYW1zdW5nIEVsZWN0cm9uaWNzIENvLiwgTHRkLg0KKyAqIGh0dHA6 Ly93d3cuc2Ftc3VuZy5jb20NCisgKg0KKyAqIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJl OyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5DQorICogaXQgdW5kZXIgdGhl IHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSB2ZXJzaW9uIDIgYXMNCisg KiBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbi4NCisgKi8NCisNCisj aW5jbHVkZSANCisjaW5jbHVkZSANCisjaW5jbHVkZSANCisjaW5jbHVkZSANCisjaW5jbHVkZSAN CisjaW5jbHVkZSANCisjaW5jbHVkZSANCisjaW5jbHVkZSANCisjaW5jbHVkZSANCisjaW5jbHVk ZSANCisjaW5jbHVkZSANCisNCisjaW5jbHVkZSANCisjaW5jbHVkZSANCisNCisjaW5jbHVkZSAN CisjaW5jbHVkZSANCisjaW5jbHVkZSANCisNCisjZGVmaW5lIENUUkxfRU5BQkxFIDB4NQ0KKyNk ZWZpbmUgQ1RSTF9CTE9DSyAweDcNCisjZGVmaW5lIENUUkxfRElTQUJMRSAweDANCisNCitlbnVt IFM1UF9TWVNNTVVfSU5URVJSVVBUX1RZUEUgew0KKyBTWVNNTVVfUEFHRUZBVUxULA0KKyBTWVNN TVVfQVJfTVVMVElISVQsDQorIFNZU01NVV9BV19NVUxUSUhJVCwNCisgU1lTTU1VX0JVU0VSUk9S LA0KKyBTWVNNTVVfQVJfU0VDVVJJVFksDQorIFNZU01NVV9BUl9BQ0NFU1MsDQorIFNZU01NVV9B V19TRUNVUklUWSwNCisgU1lTTU1VX0FXX1BST1RFQ1RJT04sIC8qIDcgKi8NCisgU1lTTU1VX0ZB VUxUU19OVU0NCit9Ow0KKw0KK3N0YXRpYyB1bnNpZ25lZCBzaG9ydCBmYXVsdF9yZWdfb2Zmc2V0 W1NZU01NVV9GQVVMVFNfTlVNXSA9IHsNCisgUzVQX1BBR0VfRkFVTFRfQUREUiwNCisgUzVQX0FS X0ZBVUxUX0FERFIsDQorIFM1UF9BV19GQVVMVF9BRERSLA0KKyBTNVBfREVGQVVMVF9TTEFWRV9B RERSLA0KKyBTNVBfQVJfRkFVTFRfQUREUiwNCisgUzVQX0FSX0ZBVUxUX0FERFIsDQorIFM1UF9B V19GQVVMVF9BRERSLA0KKyBTNVBfQVdfRkFVTFRfQUREUg0KK307DQorDQorc3RhdGljIGNoYXIg KnN5c21tdV9mYXVsdF9uYW1lW1NZU01NVV9GQVVMVFNfTlVNXSA9IHsNCisgIlBBR0UgRkFVTFQi LA0KKyAiQVIgTVVMVEktSElUIEZBVUxUIiwNCisgIkFXIE1VTFRJLUhJVCBGQVVMVCIsDQorICJC VVMgRVJST1IiLA0KKyAiQVIgU0VDVVJJVFkgUFJPVEVDVElPTiBGQVVMVCIsDQorICJBUiBBQ0NF U1MgUFJPVEVDVElPTiBGQVVMVCIsDQorICJBVyBTRUNVUklUWSBQUk9URUNUSU9OIEZBVUxUIiwN CisgIkFXIEFDQ0VTUyBQUk9URUNUSU9OIEZBVUxUIg0KK307DQorDQorc3RydWN0IGV4eW5vc19p b21tdV9kb21haW4gew0KKyBzdHJ1Y3QgZGV2aWNlICpkZXY7DQorIHVuc2lnbmVkIGxvbmcgKnBn dGFibGU7DQorIHNwaW5sb2NrX3QgbG9jazsNCisgc3BpbmxvY2tfdCBwZ3RhYmxlbG9jazsNCit9 Ow0KKw0KKy8qIExpc3Qgb2Ygc3lzbW11X3BsYXRkYXRhICovDQorc3RhdGljIExJU1RfSEVBRChz eXNtbXVfbGlzdCk7DQorDQorc3RhdGljIGlubGluZSBzdHJ1Y3Qgc3lzbW11X3BsYXRkYXRhICpn ZXRfc3lzbW11X2RhdGEoc3RydWN0IGRldmljZSAqb3duZXIsDQorIHN0cnVjdCBzeXNtbXVfcGxh dGRhdGEgKnN0YXJ0KQ0KK3sNCisgc3RydWN0IGxpc3RfaGVhZCAqcG9zLCAqaGVhZDsNCisNCisg aGVhZCA9IChzdGFydCkgPyAmc3RhcnQtPm5vZGUgOiAmc3lzbW11X2xpc3Q7DQorDQorIGxpc3Rf Zm9yX2VhY2gocG9zLCBoZWFkKSB7DQorIHN0cnVjdCBzeXNtbXVfcGxhdGRhdGEgKm1tdWRhdGEg PQ0KKyBjb250YWluZXJfb2YocG9zLCBzdHJ1Y3Qgc3lzbW11X3BsYXRkYXRhLCBub2RlKTsNCisN CisgaWYgKHBvcyA9PSAmc3lzbW11X2xpc3QpDQorIHJldHVybiBOVUxMOw0KKw0KKyBpZiAobW11 ZGF0YS0+b3duZXIgPT0gb3duZXIpDQorIHJldHVybiBtbXVkYXRhOw0KKyB9DQorDQorIHJldHVy biBOVUxMOw0KK30NCisNCitzdGF0aWMgaW5saW5lIHN0cnVjdCBzeXNtbXVfcGxhdGRhdGEgKmdl dF9wbGF0ZGF0YShzdHJ1Y3QgZGV2aWNlICpkZXYpDQorew0KKyByZXR1cm4gZGV2X2dldF9wbGF0 ZGF0YShkZXYpOw0KK30NCisNCitzdGF0aWMgaW5saW5lIGJvb2wgc2V0X3N5c21tdV9hY3RpdmUo c3RydWN0IHN5c21tdV9wbGF0ZGF0YSAqbW11ZGF0YSkNCit7DQorIC8qIHJldHVybiB0cnVlIGlm IHRoZSBTeXN0ZW0gTU1VIHdhcyBub3QgYWN0aXZlIHByZXZpb3VzbHkNCisgICAgYW5kIGl0IG5l ZWRzIHRvIGJlIGluaXRpYWxpemVkICovDQorDQorIHJldHVybiBhdG9taWNfaW5jX3JldHVybigm bW11ZGF0YS0+YWN0aXZhdGlvbnMpID09IDE7DQorfQ0KKw0KK3N0YXRpYyBpbmxpbmUgYm9vbCBz ZXRfc3lzbW11X2luYWN0aXZlKHN0cnVjdCBzeXNtbXVfcGxhdGRhdGEgKm1tdWRhdGEpDQorew0K KyAvKiByZXR1cm4gdHJ1ZSBpZiB0aGUgU3lzdGVtIE1NVSBpcyBuZWVkZWQgdG8gYmUgZGlzYWJs ZWQgKi8NCisgaW50IHJlZjsNCisNCisgcmVmID0gYXRvbWljX2RlY19yZXR1cm4oJm1tdWRhdGEt PmFjdGl2YXRpb25zKTsNCisNCisgaWYgKHJlZiA9PSAwKQ0KKyByZXR1cm4gdHJ1ZTsNCisNCisg aWYgKFdBUk5fT04ocmVmIDwgMCkpIHsNCisgLyogU3lzdGVtIE1NVSBpcyBhbHJlYWR5IGRpc2Fi bGVkICovDQorIGF0b21pY19zZXQoJm1tdWRhdGEtPmFjdGl2YXRpb25zLCAwKTsNCisgcmVmID0g MDsNCisgfQ0KKw0KKyByZXR1cm4gZmFsc2U7DQorfQ0KKw0KK3N0YXRpYyBpbmxpbmUgYm9vbCBp c19zeXNtbXVfYWN0aXZlKHN0cnVjdCBzeXNtbXVfcGxhdGRhdGEgKm1tdWRhdGEpDQorew0KKyBy ZXR1cm4gYXRvbWljX3JlYWQoJm1tdWRhdGEtPmFjdGl2YXRpb25zKSAhPSAwOw0KK30NCisNCitz dGF0aWMgaW5saW5lIHZvaWQgc3lzbW11X2Jsb2NrKHZvaWQgX19pb21lbSAqc2ZyYmFzZSkNCit7 DQorIF9fcmF3X3dyaXRlbChDVFJMX0JMT0NLLCBzZnJiYXNlICsgUzVQX01NVV9DVFJMKTsNCit9 DQorDQorc3RhdGljIGlubGluZSB2b2lkIHN5c21tdV91bmJsb2NrKHZvaWQgX19pb21lbSAqc2Zy YmFzZSkNCit7DQorIF9fcmF3X3dyaXRlbChDVFJMX0VOQUJMRSwgc2ZyYmFzZSArIFM1UF9NTVVf Q1RSTCk7DQorfQ0KKw0KK3N0YXRpYyBpbmxpbmUgdm9pZCBfX3N5c21tdV90bGJfaW52YWxpZGF0 ZSh2b2lkIF9faW9tZW0gKnNmcmJhc2UpDQorew0KKyBfX3Jhd193cml0ZWwoMHgxLCBzZnJiYXNl ICsgUzVQX01NVV9GTFVTSCk7DQorfQ0KKw0KK3N0YXRpYyBpbmxpbmUgdm9pZCBfX3N5c21tdV9z ZXRfcHRiYXNlKHZvaWQgX19pb21lbSAqc2ZyYmFzZSwNCisgICAgICAgIHVuc2lnbmVkIGxvbmcg cGdkKQ0KK3sNCisgaWYgKHVubGlrZWx5KHBnZCA9PSAwKSkgew0KKyBwZ2QgPSAodW5zaWduZWQg bG9uZylaRVJPX1BBR0UoMCk7DQorIF9fcmF3X3dyaXRlbCgweDIwLCBzZnJiYXNlICsgUzVQX01N VV9DRkcpOyAvKiA0S0IgTFYxICovDQorIH0gZWxzZSB7DQorIF9fcmF3X3dyaXRlbCgweDAsIHNm cmJhc2UgKyBTNVBfTU1VX0NGRyk7IC8qIDE2S0IgTFYxICovDQorIH0NCisNCisgX19yYXdfd3Jp dGVsKHBnZCwgc2ZyYmFzZSArIFM1UF9QVF9CQVNFX0FERFIpOw0KKw0KKyBfX3N5c21tdV90bGJf aW52YWxpZGF0ZShzZnJiYXNlKTsNCit9DQorDQorc3RhdGljIGlycXJldHVybl90IGV4eW5vc19z eXNtbXVfaXJxKGludCBpcnEsIHZvaWQgKmRldl9pZCkNCit7DQorIC8qIFNZU01NVSBpcyBpbiBi bG9ja2VkIHdoZW4gaW50ZXJydXB0IG9jY3VycmVkLiAqLw0KKyB1bnNpZ25lZCBsb25nIGFkZHI7 DQorIHN0cnVjdCBzeXNtbXVfcGxhdGRhdGEgKm1tdWRhdGEgPSBkZXZfaWQ7DQorIGVudW0gUzVQ X1NZU01NVV9JTlRFUlJVUFRfVFlQRSBpdHlwZTsNCisNCisgV0FSTl9PTighaXNfc3lzbW11X2Fj dGl2ZShtbXVkYXRhKSk7DQorDQorIGl0eXBlID0gKGVudW0gUzVQX1NZU01NVV9JTlRFUlJVUFRf VFlQRSkNCisgX19mZnMoX19yYXdfcmVhZGwobW11ZGF0YS0+c2ZyYmFzZSArIFM1UF9JTlRfU1RB VFVTKSk7DQorDQorIEJVR19PTighKChpdHlwZSA+PSAwKSAmJiAoaXR5cGUgPCA4KSkpOw0KKw0K KyBkZXZfYWxlcnQobW11ZGF0YS0+ZGV2LCAiU1lTVEVNIE1NVSBGQVVMVCEhIS5cbiIpOw0KKw0K KyBpZiAoIW1tdWRhdGEtPmRvbWFpbikNCisgcmV0dXJuIElSUV9OT05FOw0KKw0KKyBhZGRyID0g X19yYXdfcmVhZGwobW11ZGF0YS0+c2ZyYmFzZSArIGZhdWx0X3JlZ19vZmZzZXRbaXR5cGVdKTsN CisNCisgaWYgKCFyZXBvcnRfaW9tbXVfZmF1bHQobW11ZGF0YS0+ZG9tYWluLCBtbXVkYXRhLT5v d25lciwgYWRkciwgaXR5cGUpKSB7DQorIF9fcmF3X3dyaXRlbCgxIDw8IGl0eXBlLCBtbXVkYXRh LT5zZnJiYXNlICsgUzVQX0lOVF9DTEVBUik7DQorIGRldl9ub3RpY2UobW11ZGF0YS0+ZGV2LA0K KyAiJXMgaXMgcmVzb2x2ZWQuIFJldHJ5aW5nIHRyYW5zbGF0aW9uLlxuIiwNCisgc3lzbW11X2Zh dWx0X25hbWVbaXR5cGVdKTsNCisgc3lzbW11X3VuYmxvY2sobW11ZGF0YS0+c2ZyYmFzZSk7DQor IH0gZWxzZSB7DQorIGRldl9ub3RpY2UobW11ZGF0YS0+ZGV2LCAiJXMgaXMgbm90IGhhbmRsZWQu XG4iLA0KKyBzeXNtbXVfZmF1bHRfbmFtZVtpdHlwZV0pOw0KKyB9DQorDQorIHJldHVybiBJUlFf SEFORExFRDsNCit9DQorDQordm9pZCBleHlub3Nfc3lzbW11X3NldF90YWJsZWJhc2VfcGdkKHN0 cnVjdCBkZXZpY2UgKm93bmVyLCB1bnNpZ25lZCBsb25nIHBnZCkNCit7DQorIHN0cnVjdCBzeXNt bXVfcGxhdGRhdGEgKm1tdWRhdGEgPSBOVUxMOw0KKw0KKyB3aGlsZSAoKG1tdWRhdGEgPSBnZXRf c3lzbW11X2RhdGEob3duZXIsIG1tdWRhdGEpKSkgew0KKyBpZiAoaXNfc3lzbW11X2FjdGl2ZSht bXVkYXRhKSkgew0KKyBzeXNtbXVfYmxvY2sobW11ZGF0YS0+c2ZyYmFzZSk7DQorIF9fc3lzbW11 X3NldF9wdGJhc2UobW11ZGF0YS0+c2ZyYmFzZSwgcGdkKTsNCisgc3lzbW11X3VuYmxvY2sobW11 ZGF0YS0+c2ZyYmFzZSk7DQorIGRldl9kYmcobW11ZGF0YS0+ZGV2LCAiTmV3IHBhZ2UgdGFibGUg YmFzZSBpcyAlcFxuIiwNCisgKHZvaWQgKilwZ2QpOw0KKyB9IGVsc2Ugew0KKyBkZXZfZGJnKG1t dWRhdGEtPmRldiwNCisgIkRpc2FibGVkOiBTa2lwcGluZyBzZXR0aW5nIHBhZ2UgdGFibGUgYmFz ZS5cbiIpOw0KKyB9DQorIH0NCit9DQorDQoraW50IGV4eW5vc19zeXNtbXVfZW5hYmxlKHN0cnVj dCBpb21tdV9kb21haW4gKmRvbWFpbikNCit7DQorIHN0cnVjdCBzeXNtbXVfcGxhdGRhdGEgKm1t dWRhdGEgPSBOVUxMOw0KKyBib29sIGVuYWJsZWQgPSBmYWxzZTsNCisgc3RydWN0IGV4eW5vc19p b21tdV9kb21haW4gKnByaXYgPSBkb21haW4tPnByaXY7DQorDQorIGlmICghcHJpdiB8fCAhcHJp di0+ZGV2KQ0KKyByZXR1cm4gLUVJTlZBTDsNCisNCisgLyogVGhlcmUgYXJlIHNvbWUgZGV2aWNl cyB0aGF0IGNvbnRyb2wgbW9yZSBTeXN0ZW0gTU1VcyB0aGFuIG9uZSBzdWNoDQorICogYXMgTUZD Lg0KKyAqLw0KKyB3aGlsZSAoKG1tdWRhdGEgPSBnZXRfc3lzbW11X2RhdGEocHJpdi0+ZGV2LCBt bXVkYXRhKSkpIHsNCisgZW5hYmxlZCA9IHRydWU7DQorDQorIGlmICghc2V0X3N5c21tdV9hY3Rp dmUobW11ZGF0YSkpIHsNCisgZGV2X2RiZyhtbXVkYXRhLT5kZXYsICJBbHJlYWR5IGVuYWJsZWQu XG4iKTsNCisgY29udGludWU7DQorIH0NCisNCisgcG1fcnVudGltZV9nZXRfc3luYyhtbXVkYXRh LT5kZXYpOw0KKw0KKyBjbGtfZW5hYmxlKG1tdWRhdGEtPmNsayk7DQorDQorIF9fc3lzbW11X3Nl dF9wdGJhc2UobW11ZGF0YS0+c2ZyYmFzZSwgX19wYShwcml2LT5wZ3RhYmxlKSk7DQorDQorIF9f cmF3X3dyaXRlbChDVFJMX0VOQUJMRSwgbW11ZGF0YS0+c2ZyYmFzZSArIFM1UF9NTVVfQ1RSTCk7 DQorDQorIG1tdWRhdGEtPmRvbWFpbiA9IGRvbWFpbjsNCisNCisgZGV2X2RiZyhtbXVkYXRhLT5k ZXYsICJFbmFibGVkLlxuIik7DQorIH0NCisNCisgcmV0dXJuIChlbmFibGVkKSA/IDAgOiAtRU5P REVWOw0KK30NCisNCit2b2lkIGV4eW5vc19zeXNtbXVfZGlzYWJsZShzdHJ1Y3QgaW9tbXVfZG9t YWluICpkb21haW4pDQorew0KKyBzdHJ1Y3Qgc3lzbW11X3BsYXRkYXRhICptbXVkYXRhID0gTlVM TDsNCisgYm9vbCBkaXNhYmxlZCA9IGZhbHNlOw0KKyBzdHJ1Y3QgZXh5bm9zX2lvbW11X2RvbWFp biAqcHJpdiA9IGRvbWFpbi0+cHJpdjsNCisNCisgaWYgKCFwcml2IHx8ICFwcml2LT5kZXYpDQor IHJldHVybjsNCisNCisgd2hpbGUgKChtbXVkYXRhID0gZ2V0X3N5c21tdV9kYXRhKHByaXYtPmRl diwgbW11ZGF0YSkpKSB7DQorIGRpc2FibGVkID0gdHJ1ZTsNCisNCisgaWYgKCFzZXRfc3lzbW11 X2luYWN0aXZlKG1tdWRhdGEpKSB7DQorIGRldl9kYmcobW11ZGF0YS0+ZGV2LA0KKyAiSW5hY3Rp dmF0aW9uIHJlcXVlc3QgaWdub3JyZWRcbiIpOw0KKyBjb250aW51ZTsNCisgfQ0KKw0KKyBfX3Jh d193cml0ZWwoQ1RSTF9ESVNBQkxFLCBtbXVkYXRhLT5zZnJiYXNlICsgUzVQX01NVV9DVFJMKTsN CisNCisgY2xrX2Rpc2FibGUobW11ZGF0YS0+Y2xrKTsNCisNCisgcG1fcnVudGltZV9wdXRfc3lu YyhtbXVkYXRhLT5kZXYpOw0KKw0KKyBtbXVkYXRhLT5kb21haW4gPSBOVUxMOw0KKw0KKyBkZXZf ZGJnKG1tdWRhdGEtPmRldiwgIkRpc2FibGVkLlxuIik7DQorIH0NCisNCisgQlVHX09OKCFkaXNh YmxlZCk7DQorfQ0KKw0KK3ZvaWQgZXh5bm9zX3N5c21tdV90bGJfaW52YWxpZGF0ZShzdHJ1Y3Qg ZGV2aWNlICpvd25lcikNCit7DQorIHN0cnVjdCBzeXNtbXVfcGxhdGRhdGEgKm1tdWRhdGEgPSBO VUxMOw0KKw0KKyB3aGlsZSAoKG1tdWRhdGEgPSBnZXRfc3lzbW11X2RhdGEob3duZXIsIG1tdWRh dGEpKSkgew0KKyBpZiAoaXNfc3lzbW11X2FjdGl2ZShtbXVkYXRhKSkgew0KKyBzeXNtbXVfYmxv Y2sobW11ZGF0YS0+c2ZyYmFzZSk7DQorIF9fc3lzbW11X3RsYl9pbnZhbGlkYXRlKG1tdWRhdGEt PnNmcmJhc2UpOw0KKyBzeXNtbXVfdW5ibG9jayhtbXVkYXRhLT5zZnJiYXNlKTsNCisgfSBlbHNl IHsNCisgZGV2X2RiZyhtbXVkYXRhLT5kZXYsDQorICJEaXNhYmxlZDogU2tpcHBpbmcgaW52YWxp ZGF0aW5nIFRMQi5cbiIpOw0KKyB9DQorIH0NCit9DQorDQorc3RhdGljIGludCBleHlub3Nfc3lz bW11X3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpDQorew0KKyBzdHJ1Y3QgcmVz b3VyY2UgKnJlcywgKmlvYXJlYTsNCisgaW50IHJldDsNCisgaW50IGlycTsNCisgc3RydWN0IGRl dmljZSAqZGV2Ow0KKyB2b2lkICpzZnI7DQorDQorIGRldiA9ICZwZGV2LT5kZXY7DQorIGlmICgh Z2V0X3BsYXRkYXRhKGRldikgfHwgKGdldF9wbGF0ZGF0YShkZXYpLT5vd25lciA9PSBOVUxMKSkg ew0KKyBkZXZfZXJyKGRldiwgIkZhaWxlZCB0byBwcm9iaW5nIHN5c3RlbSBNTVU6ICINCisgIk93 bmVyIGRldmljZSBpcyBub3Qgc2V0LiIpOw0KKyByZXR1cm4gLUVOWElPOw0KKyB9DQorDQorIHJl cyA9IHBsYXRmb3JtX2dldF9yZXNvdXJjZShwZGV2LCBJT1JFU09VUkNFX01FTSwgMCk7DQorIGlm ICghcmVzKSB7DQorIGRldl9lcnIoZGV2LA0KKyAiRmFpbGVkIHByb2Jpbmcgc3lzdGVtIE1NVTog ZmFpbGVkIHRvIGdldCByZXNvdXJjZS4iKTsNCisgcmV0dXJuIC1FTk9FTlQ7DQorIH0NCisNCisg aW9hcmVhID0gcmVxdWVzdF9tZW1fcmVnaW9uKHJlcy0+c3RhcnQsIHJlc291cmNlX3NpemUocmVz KSwgcGRldi0+bmFtZSk7DQorIGlmIChpb2FyZWEgPT0gTlVMTCkgew0KKyBkZXZfZXJyKGRldiwg IkZhaWxlZCBwcm9iaW5nIHN5c3RlbSBNTVU6ICINCisgImZhaWxlZCB0byByZXF1ZXN0IG1lbW9y eSByZWdpb24uIik7DQorIHJldHVybiAtRU5PTUVNOw0KKyB9DQorDQorIHNmciA9IGlvcmVtYXAo cmVzLT5zdGFydCwgcmVzb3VyY2Vfc2l6ZShyZXMpKTsNCisgaWYgKCFzZnIpIHsNCisgZGV2X2Vy cihkZXYsICJGYWlsZWQgcHJvYmluZyBzeXN0ZW0gTU1VOiAiDQorICJmYWlsZWQgdG8gY2FsbCBp b3JlbWFwKCkuIik7DQorIHJldCA9IC1FTk9FTlQ7DQorIGdvdG8gZXJyX2lvcmVtYXA7DQorIH0N CisNCisgaXJxID0gcGxhdGZvcm1fZ2V0X2lycShwZGV2LCAwKTsNCisgaWYgKGlycSA8PSAwKSB7 DQorIGRldl9lcnIoZGV2LCAiRmFpbGVkIHByb2Jpbmcgc3lzdGVtIE1NVTogIg0KKyAiZmFpbGVk IHRvIGdldCBpcnEgcmVzb3VyY2UuIik7DQorIHJldCA9IGlycTsNCisgZ290byBlcnJfaXJxOw0K KyB9DQorDQorIGlmIChyZXF1ZXN0X2lycShpcnEsIGV4eW5vc19zeXNtbXVfaXJxLCAwLCBkZXZf bmFtZSgmcGRldi0+ZGV2KSwNCisgZGV2X2dldF9wbGF0ZGF0YShkZXYpKSkgew0KKyBkZXZfZXJy KGRldiwgIkZhaWxlZCBwcm9iaW5nIHN5c3RlbSBNTVU6ICINCisgImZhaWxlZCB0byByZXF1ZXN0 IGlycS4iKTsNCisgcmV0ID0gLUVOT0VOVDsNCisgZ290byBlcnJfaXJxOw0KKyB9DQorDQorIGdl dF9wbGF0ZGF0YShkZXYpLT5jbGsgPSBjbGtfZ2V0KGRldiwgInN5c21tdSIpOw0KKw0KKyBpZiAo SVNfRVJSX09SX05VTEwoZ2V0X3BsYXRkYXRhKGRldiktPmNsaykpIHsNCisgZGV2X2VycihkZXYs ICJGYWlsZWQgdG8gcHJvYmluZyBTeXN0ZW0gTU1VOiAiDQorICJmYWlsZWQgdG8gZ2V0IGNsb2Nr IGRlc2NyaXB0b3IiKTsNCisgcmV0ID0gLUVOT0VOVDsNCisgZ290byBlcnJfY2xrOw0KKyB9DQor DQorIGdldF9wbGF0ZGF0YShkZXYpLT5zZnJiYXNlID0gc2ZyOw0KKw0KKyBsaXN0X2FkZCgmZ2V0 X3BsYXRkYXRhKGRldiktPm5vZGUsICZzeXNtbXVfbGlzdCk7DQorDQorIGlmIChkZXYtPnBhcmVu dCkNCisgcG1fcnVudGltZV9lbmFibGUoZGV2KTsNCisNCisgZGV2X2RiZyhkZXYsICJJbml0aWFs aXplZCBmb3IgJXMuXG4iLA0KKyBkZXZfbmFtZShnZXRfcGxhdGRhdGEoZGV2KS0+b3duZXIpKTsN CisgcmV0dXJuIDA7DQorZXJyX2NsazoNCisgZnJlZV9pcnEoaXJxLCBkZXZfZ2V0X3BsYXRkYXRh KGRldikpOw0KK2Vycl9pcnE6DQorIGlvdW5tYXAoZ2V0X3BsYXRkYXRhKGRldiktPnNmcmJhc2Up Ow0KK2Vycl9pb3JlbWFwOg0KKyByZWxlYXNlX3Jlc291cmNlKGlvYXJlYSk7DQorIGtmcmVlKGlv YXJlYSk7DQorIGRldl9lcnIoZGV2LCAiUHJvYmluZyBzeXN0ZW0gTU1VIGZhaWxlZC4iKTsNCisg cmV0dXJuIHJldDsNCit9DQorDQorc3RhdGljIGludCBleHlub3Nfc3lzbW11X3JlbW92ZShzdHJ1 Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQ0KK3sNCisgcmV0dXJuIDA7DQorfQ0KKw0KK2ludCBl eHlub3Nfc3lzbW11X3J1bnRpbWVfc3VzcGVuZChzdHJ1Y3QgZGV2aWNlICpkZXYpDQorew0KKyBp ZiAoV0FSTl9PTihpc19zeXNtbXVfYWN0aXZlKGRldl9nZXRfcGxhdGRhdGEoZGV2KSkpKQ0KKyBy ZXR1cm4gLUVGQVVMVDsNCisNCisgcmV0dXJuIDA7DQorfQ0KKw0KK2ludCBleHlub3Nfc3lzbW11 X3J1bnRpbWVfcmVzdW1lKHN0cnVjdCBkZXZpY2UgKmRldikNCit7DQorIGlmIChXQVJOX09OKGlz X3N5c21tdV9hY3RpdmUoZGV2X2dldF9wbGF0ZGF0YShkZXYpKSkpDQorIHJldHVybiAtRUZBVUxU Ow0KKw0KKyByZXR1cm4gMDsNCit9DQorDQorY29uc3Qgc3RydWN0IGRldl9wbV9vcHMgZXh5bm9z X3N5c21tdV9wbV9vcHMgPSB7DQorIC5ydW50aW1lX3N1c3BlbmQgPSBleHlub3Nfc3lzbW11X3J1 bnRpbWVfc3VzcGVuZCwNCisgLnJ1bnRpbWVfcmVzdW1lID0gZXh5bm9zX3N5c21tdV9ydW50aW1l X3Jlc3VtZSwNCit9Ow0KKw0KK3N0YXRpYyBzdHJ1Y3QgcGxhdGZvcm1fZHJpdmVyIGV4eW5vc19z eXNtbXVfZHJpdmVyID0gew0KKyAucHJvYmUgPSBleHlub3Nfc3lzbW11X3Byb2JlLA0KKyAucmVt b3ZlID0gZXh5bm9zX3N5c21tdV9yZW1vdmUsDQorIC5kcml2ZXIgPSB7DQorIC5vd25lciA9IFRI SVNfTU9EVUxFLA0KKyAubmFtZSA9ICJzNXAtc3lzbW11IiwNCisgLnBtID0gJmV4eW5vc19zeXNt bXVfcG1fb3BzLA0KKyB9DQorfTsNCisNCitzdGF0aWMgaW50IF9faW5pdCBleHlub3Nfc3lzbW11 X2luaXQodm9pZCkNCit7DQorIHJldHVybiBwbGF0Zm9ybV9kcml2ZXJfcmVnaXN0ZXIoJmV4eW5v c19zeXNtbXVfZHJpdmVyKTsNCit9DQorYXJjaF9pbml0Y2FsbChleHlub3Nfc3lzbW11X2luaXQp Ow0KKw0KKy8qIFdlIGRvZXMgbm90IGNvbnNpZGVyIHN1cGVyIHNlY3Rpb24gbWFwcGluZyAoMTZN QikgKi8NCisjZGVmaW5lIFM1UF9TUEFHRV9TSElGVCAxMg0KKyNkZWZpbmUgUzVQX0xQQUdFX1NI SUZUIDE2DQorI2RlZmluZSBTNVBfU0VDVElPTl9TSElGVCAyMA0KKw0KKyNkZWZpbmUgUzVQX1NQ QUdFX1NJWkUgKDEgPDwgUzVQX1NQQUdFX1NISUZUKQ0KKyNkZWZpbmUgUzVQX0xQQUdFX1NJWkUg KDEgPDwgUzVQX0xQQUdFX1NISUZUKQ0KKyNkZWZpbmUgUzVQX1NFQ1RJT05fU0laRSAoMSA8PCBT NVBfU0VDVElPTl9TSElGVCkNCisNCisjZGVmaW5lIFM1UF9TUEFHRV9NQVNLICh+KFM1UF9TUEFH RV9TSVpFIC0gMSkpDQorI2RlZmluZSBTNVBfTFBBR0VfTUFTSyAofihTNVBfTFBBR0VfU0laRSAt IDEpKQ0KKyNkZWZpbmUgUzVQX1NFQ1RJT05fTUFTSyAofihTNVBfU0VDVElPTl9TSVpFIC0gMSkp DQorDQorI2RlZmluZSBTNVBfU1BBR0VfT1JERVIgKFM1UF9TUEFHRV9TSElGVCAtIFBBR0VfU0hJ RlQpDQorI2RlZmluZSBTNVBfTFBBR0VfT1JERVIgKFM1UF9MUEFHRV9TSElGVCAtIFM1UF9TUEFH RV9TSElGVCkNCisjZGVmaW5lIFM1UF9TRUNUSU9OX09SREVSIChTNVBfU0VDVElPTl9TSElGVCAt IFM1UF9TUEFHRV9TSElGVCkNCisNCisjZGVmaW5lIFM1UF9MVjFUQUJMRV9FTlRSSUVTICgxIDw8 IChCSVRTX1BFUl9MT05HIC0gUzVQX1NFQ1RJT05fU0hJRlQpKQ0KKw0KKyNkZWZpbmUgUzVQX0xW MlRBQkxFX0VOVFJJRVMgKDEgPDwgUzVQX1NFQ1RJT05fT1JERVIpDQorI2RlZmluZSBTNVBfTFYy VEFCTEVfU0laRSAoUzVQX0xWMlRBQkxFX0VOVFJJRVMgKiBzaXplb2YobG9uZykpDQorI2RlZmlu ZSBTNVBfTFYyVEFCTEVfTUFTSyAofihTNVBfTFYyVEFCTEVfU0laRSAtIDEpKSAvKiAweEZGRkZG QzAwICovDQorDQorI2RlZmluZSBTNVBfU0VDVElPTl9MVjFfRU5UUlkoZW50cnkpICgoZW50cnkg JiAweDQwMDAzKSA9PSAyKQ0KKyNkZWZpbmUgUzVQX1NVUFNFQ1RfTFYxX0VOVFJZKGVudHJ5KSAo KGVudHJ5ICYgMHg0MDAwMykgPT0gMHg0MDAwMikNCisjZGVmaW5lIFM1UF9QQUdFX0xWMV9FTlRS WShlbnRyeSkgKChlbnRyeSAmIDMpID09IDEpDQorI2RlZmluZSBTNVBfRkFVTFRfTFYxX0VOVFJZ KGVudHJ5KSAoKChlbnRyeSAmIDMpID09IDApIHx8IChlbnRyeSAmIDMpID09IDMpDQorDQorI2Rl ZmluZSBTNVBfTFBBR0VfTFYyX0VOVFJZKGVudHJ5KSAoKGVudHJ5ICYgMykgPT0gMSkNCisjZGVm aW5lIFM1UF9TUEFHRV9MVjJfRU5UUlkoZW50cnkpICgoZW50cnkgJiAyKSA9PSAyKQ0KKyNkZWZp bmUgUzVQX0ZBVUxUX0xWMl9FTlRSWShlbnRyeSkgKChlbnRyeSAmIDMpID09IDApDQorDQorI2Rl ZmluZSBNQUtFX0ZBVUxUX0VOVFJZKGVudHJ5KSBkbyB7IGVudHJ5ID0gMDsgfSB3aGlsZSAoMCkN CisjZGVmaW5lIE1BS0VfU0VDVElPTl9FTlRSWShlbnRyeSwgcGEpIGRvIHsgZW50cnkgPSBwYSB8 IDI7IH0gd2hpbGUgKDApDQorI2RlZmluZSBNQUtFX1NVUFNFQ1RfRU5UUlkoZW50cnksIHBhKSBk byB7IGVudHJ5ID0gcGEgfCAweDQwMDAyOyB9IHdoaWxlICgwKQ0KKyNkZWZpbmUgTUFLRV9MVjJU QUJMRV9FTlRSWShlbnRyeSwgcGEpIGRvIHsgZW50cnkgPSBwYSB8IDE7IH0gd2hpbGUgKDApDQor DQorI2RlZmluZSBNQUtFX0xQQUdFX0VOVFJZKGVudHJ5LCBwYSkgZG8geyBlbnRyeSA9IHBhIHwg MTsgfSB3aGlsZSAoMCkNCisjZGVmaW5lIE1BS0VfU1BBR0VfRU5UUlkoZW50cnksIHBhKSBkbyB7 IGVudHJ5ID0gcGEgfCAzOyB9IHdoaWxlICgwKQ0KKw0KKyNkZWZpbmUgR0VUX0xWMkVOVFJZKGVu dHJ5LCBpb3ZhKSAoXA0KKyAodW5zaWduZWQgbG9uZyAqKXBoeXNfdG9fdmlydChlbnRyeSAmIFM1 UF9MVjJUQUJMRV9NQVNLKSArXA0KKyAoKGlvdmEgJiAoflM1UF9TRUNUSU9OX01BU0spKSA+PiBT NVBfU1BBR0VfU0hJRlQpKQ0KKw0KKy8qIHNsYWIgY2FjaGUgZm9yIGxldmVsIDIgcGFnZSB0YWJs ZXMgKi8NCitzdGF0aWMgc3RydWN0IGttZW1fY2FjaGUgKmwydGFibGVfY2FjaGVwOw0KKw0KK3N0 YXRpYyBpbmxpbmUgdm9pZCBwZ3RhYmxlX2ZsdXNoKHZvaWQgKnZhc3RhcnQsIHZvaWQgKnZhZW5k KQ0KK3sNCisgZG1hY19mbHVzaF9yYW5nZSh2YXN0YXJ0LCB2YWVuZCk7DQorIG91dGVyX2ZsdXNo X3JhbmdlKHZpcnRfdG9fcGh5cyh2YXN0YXJ0KSwNCisgdmlydF90b19waHlzKHZhZW5kKSk7DQor fQ0KKw0KK3N0YXRpYyBpbnQgZXh5bm9zX2lvbW11X2ZhdWx0X2hhbmRsZXIoc3RydWN0IGlvbW11 X2RvbWFpbiAqZG9tYWluLA0KKyBzdHJ1Y3QgZGV2aWNlICpkZXYsIHVuc2lnbmVkIGxvbmcgaW92 YSwgaW50IGZsYWdzKQ0KK3sNCisgc3RydWN0IGV4eW5vc19pb21tdV9kb21haW4gKnByaXYgPSBk b21haW4tPnByaXY7DQorDQorIGRldl9lcnIocHJpdi0+ZGV2LCAiJXMgb2NjdXJlZCBhdCAlcChQ YWdlIHRhYmxlIGJhc2U6ICVwKVxuIiwNCisgc3lzbW11X2ZhdWx0X25hbWVbZmxhZ3NdLCAodm9p ZCAqKWlvdmEsDQorICh2b2lkICopKF9fcGEocHJpdi0+cGd0YWJsZSkpKTsNCisgZGV2X2Vycihw cml2LT5kZXYsICJcdFx0R2VuZXJhdGluZyBLZXJuZWwgT09QUy4uLlxuIik7DQorIGRldl9lcnIo cHJpdi0+ZGV2LCAiXHRcdGJlY2F1c2UgaXQgaXMgdW5yZWNvdmVyYWJsZS5cbiIpOw0KKyBkZXZf ZXJyKHByaXYtPmRldiwNCisgIlx0XHRTZXQgRmF1bHQgaGFuZGxlciB3aXRoIGlvbW11X3NldF9m YXVsdF9oYW5kbGVyKCkuXG4iKTsNCisgZGV2X2Vycihwcml2LT5kZXYsICJcdFx0dG8gaGFuZGxl IFN5c3RlbSBNTVUgZmF1bHQuXG4iKTsNCisNCisgQlVHKCk7DQorDQorIHJldHVybiAwOw0KK30N CisNCitzdGF0aWMgaW50IGV4eW5vc19pb21tdV9kb21haW5faW5pdChzdHJ1Y3QgaW9tbXVfZG9t YWluICpkb21haW4pDQorew0KKyBzdHJ1Y3QgZXh5bm9zX2lvbW11X2RvbWFpbiAqcHJpdjsNCisN CisgcHJpdiA9IGt6YWxsb2Moc2l6ZW9mKCpwcml2KSwgR0ZQX0tFUk5FTCk7DQorIGlmICghcHJp dikNCisgcmV0dXJuIC1FTk9NRU07DQorDQorIHByaXYtPnBndGFibGUgPSAodW5zaWduZWQgbG9u ZyAqKV9fZ2V0X2ZyZWVfcGFnZXMoR0ZQX0tFUk5FTCwNCisgKFM1UF9MVjFUQUJMRV9FTlRSSUVT ICogc2l6ZW9mKHVuc2lnbmVkIGxvbmcpKSA+PiBQQUdFX1NISUZUKTsNCisgaWYgKCFwcml2LT5w Z3RhYmxlKSB7DQorIGtmcmVlKHByaXYpOw0KKyByZXR1cm4gLUVOT01FTTsNCisgfQ0KKw0KKyBt ZW1zZXQocHJpdi0+cGd0YWJsZSwgMCwgUzVQX0xWMVRBQkxFX0VOVFJJRVMgKiBzaXplb2YodW5z aWduZWQgbG9uZykpOw0KKyBwZ3RhYmxlX2ZsdXNoKHByaXYtPnBndGFibGUsIHByaXYtPnBndGFi bGUgKyBTNVBfTFYxVEFCTEVfRU5UUklFUyk7DQorDQorIHNwaW5fbG9ja19pbml0KCZwcml2LT5s b2NrKTsNCisNCisgZG9tYWluLT5wcml2ID0gcHJpdjsNCisNCisgaW9tbXVfc2V0X2ZhdWx0X2hh bmRsZXIoZG9tYWluLCAmZXh5bm9zX2lvbW11X2ZhdWx0X2hhbmRsZXIpOw0KKw0KKyByZXR1cm4g MDsNCit9DQorDQorc3RhdGljIHZvaWQgZXh5bm9zX2lvbW11X2RvbWFpbl9kZXN0cm95KHN0cnVj dCBpb21tdV9kb21haW4gKmRvbWFpbikNCit7DQorIHN0cnVjdCBleHlub3NfaW9tbXVfZG9tYWlu ICpwcml2ID0gZG9tYWluLT5wcml2Ow0KKw0KKyBmcmVlX3BhZ2VzKCh1bnNpZ25lZCBsb25nKXBy aXYtPnBndGFibGUsDQorIChTNVBfTFYxVEFCTEVfRU5UUklFUyAqIHNpemVvZih1bnNpZ25lZCBs b25nKSkgPj4gUEFHRV9TSElGVCk7DQorDQorIGtmcmVlKHByaXYpOw0KKw0KKyBkb21haW4tPnBy aXYgPSBOVUxMOw0KK30NCisNCitzdGF0aWMgaW50IGV4eW5vc19pb21tdV9hdHRhY2hfZGV2aWNl KHN0cnVjdCBpb21tdV9kb21haW4gKmRvbWFpbiwNCisgICAgc3RydWN0IGRldmljZSAqZGV2KQ0K K3sNCisgc3RydWN0IGV4eW5vc19pb21tdV9kb21haW4gKnByaXYgPSBkb21haW4tPnByaXY7DQor IGludCByZXQ7DQorDQorIHNwaW5fbG9jaygmcHJpdi0+bG9jayk7DQorDQorIHByaXYtPmRldiA9 IGRldjsNCisNCisgcmV0ID0gZXh5bm9zX3N5c21tdV9lbmFibGUoZG9tYWluKTsNCisgaWYgKHJl dCkNCisgcmV0dXJuIHJldDsNCisNCisgc3Bpbl91bmxvY2soJnByaXYtPmxvY2spOw0KKw0KKyBy ZXR1cm4gMDsNCit9DQorDQorc3RhdGljIHZvaWQgZXh5bm9zX2lvbW11X2RldGFjaF9kZXZpY2Uo c3RydWN0IGlvbW11X2RvbWFpbiAqZG9tYWluLA0KKyAgICAgc3RydWN0IGRldmljZSAqZGV2KQ0K K3sNCisgc3RydWN0IGV4eW5vc19pb21tdV9kb21haW4gKnByaXYgPSBkb21haW4tPnByaXY7DQor DQorIHNwaW5fbG9jaygmcHJpdi0+bG9jayk7DQorDQorIGlmIChwcml2LT5kZXYgPT0gZGV2KSB7 DQorIGV4eW5vc19zeXNtbXVfZGlzYWJsZShkb21haW4pOw0KKyBwcml2LT5kZXYgPSBOVUxMOw0K KyB9DQorDQorIHNwaW5fdW5sb2NrKCZwcml2LT5sb2NrKTsNCit9DQorDQorc3RhdGljIGJvb2wg c2VjdGlvbl9hdmFpbGFibGUoc3RydWN0IGlvbW11X2RvbWFpbiAqZG9tYWluLA0KKyAgICAgICB1 bnNpZ25lZCBsb25nICpsdjFlbnRyeSkNCit7DQorIHN0cnVjdCBleHlub3NfaW9tbXVfZG9tYWlu ICpwcml2ID0gZG9tYWluLT5wcml2Ow0KKw0KKyBpZiAoUzVQX1NFQ1RJT05fTFYxX0VOVFJZKCps djFlbnRyeSkpIHsNCisgZGV2X2Vycihwcml2LT5kZXYsICIxTUIgZW50cnkgYWxyZWFkIGV4aXN0 cyBhdCAweCUwOHhcbiIsDQorIChsdjFlbnRyeSAtIHByaXYtPnBndGFibGUpICogU1pfMU0pOw0K KyByZXR1cm4gZmFsc2U7DQorIH0NCisNCisgaWYgKFM1UF9QQUdFX0xWMV9FTlRSWSgqbHYxZW50 cnkpKSB7DQorIHVuc2lnbmVkIGxvbmcgKmx2MmVuZCwgKmx2MmJhc2U7DQorDQorIGx2MmJhc2Ug PSBwaHlzX3RvX3ZpcnQoKmx2MWVudHJ5ICYgUzVQX0xWMlRBQkxFX01BU0spOw0KKyBsdjJlbmQg PSBsdjJiYXNlICsgUzVQX0xWMlRBQkxFX0VOVFJJRVM7DQorIHdoaWxlIChsdjJiYXNlICE9IGx2 MmVuZCkgew0KKyBpZiAoIVM1UF9GQVVMVF9MVjJfRU5UUlkoKmx2MmJhc2UpKSB7DQorIGRldl9l cnIocHJpdi0+ZGV2LCAiRmFpbGVkIHRvIGZyZWUgTDIgcGFnZSAiDQorICJ0YWJsZSBmb3Igc2Vj dGlvbiBtYXBwaW5nLlxuIik7DQorIHJldHVybiBmYWxzZTsNCisgfQ0KKyBsdjJiYXNlKys7DQor IH0NCisNCisga21lbV9jYWNoZV9mcmVlKGwydGFibGVfY2FjaGVwLA0KKyBwaHlzX3RvX3ZpcnQo Kmx2MWVudHJ5ICYgUzVQX0xWMlRBQkxFX01BU0spKTsNCisNCisgTUFLRV9GQVVMVF9FTlRSWSgq bHYxZW50cnkpOw0KKyB9DQorDQorIHJldHVybiB0cnVlOw0KK30NCisNCitzdGF0aWMgYm9vbCB3 cml0ZV9scGFnZSh1bnNpZ25lZCBsb25nICpoZWFkX2VudHJ5LCB1bnNpZ25lZCBsb25nIHBoeXNf YWRkcikNCit7DQorIHVuc2lnbmVkIGxvbmcgKmVudHJ5LCAqZW5kOw0KKw0KKyBlbnRyeSA9IGhl YWRfZW50cnk7DQorIGVuZCA9IGVudHJ5ICsgKDEgPDwgUzVQX0xQQUdFX09SREVSKTsNCisNCisg d2hpbGUgKGVudHJ5ICE9IGVuZCkgew0KKyBpZiAoIVM1UF9GQVVMVF9MVjJfRU5UUlkoKmVudHJ5 KSkNCisgYnJlYWs7DQorDQorIE1BS0VfTFBBR0VfRU5UUlkoKmVudHJ5LCBwaHlzX2FkZHIpOw0K Kw0KKyBlbnRyeSsrOw0KKyB9DQorDQorIGlmIChlbnRyeSAhPSBlbmQpIHsNCisgZW5kID0gZW50 cnk7DQorIHdoaWxlIChlbnRyeSAhPSBoZWFkX2VudHJ5KQ0KKyBNQUtFX0ZBVUxUX0VOVFJZKCoo LS1lbnRyeSkpOw0KKw0KKyByZXR1cm4gZmFsc2U7DQorIH0NCisNCisgcmV0dXJuIHRydWU7DQor fQ0KKw0KK3N0YXRpYyBpbnQgZXh5bm9zX2lvbW11X21hcChzdHJ1Y3QgaW9tbXVfZG9tYWluICpk b21haW4sIHVuc2lnbmVkIGxvbmcgaW92YSwNCisgcGh5c19hZGRyX3QgcGFkZHIsIGludCBnZnBf b3JkZXIsIGludCBwcm90KQ0KK3sNCisgc3RydWN0IGV4eW5vc19pb21tdV9kb21haW4gKnByaXYg PSBkb21haW4tPnByaXY7DQorIHVuc2lnbmVkIGxvbmcgKnN0YXJ0X2VudHJ5LCAqZW50cnksICpl bmRfZW50cnk7DQorIGludCBudW1fZW50cnk7DQorIGludCByZXQgPSAwOw0KKyB1bnNpZ25lZCBs b25nIGZsYWdzOw0KKw0KKyBCVUdfT04ocHJpdi0+cGd0YWJsZSA9PSBOVUxMKTsNCisNCisgc3Bp bl9sb2NrX2lycXNhdmUoJnByaXYtPnBndGFibGVsb2NrLCBmbGFncyk7DQorDQorIHN0YXJ0X2Vu dHJ5ID0gZW50cnkgPSBwcml2LT5wZ3RhYmxlICsgKGlvdmEgPj4gUzVQX1NFQ1RJT05fU0hJRlQp Ow0KKw0KKyBpZiAoZ2ZwX29yZGVyID49IFM1UF9TRUNUSU9OX09SREVSKSB7DQorIEJVR19PTigo cGFkZHIgfCBpb3ZhKSAmIH5TNVBfU0VDVElPTl9NQVNLKTsNCisgLyogMU1pQiBtYXBwaW5nICov DQorDQorIG51bV9lbnRyeSA9IDEgPDwgKGdmcF9vcmRlciAtIFM1UF9TRUNUSU9OX09SREVSKTsN CisgZW5kX2VudHJ5ID0gZW50cnkgKyBudW1fZW50cnk7DQorDQorIHdoaWxlIChlbnRyeSAhPSBl bmRfZW50cnkpIHsNCisgaWYgKCFzZWN0aW9uX2F2YWlsYWJsZShkb21haW4sIGVudHJ5KSkNCisg YnJlYWs7DQorDQorIE1BS0VfU0VDVElPTl9FTlRSWSgqZW50cnksIHBhZGRyKTsNCisNCisgcGFk ZHIgKz0gUzVQX1NFQ1RJT05fU0laRTsNCisgZW50cnkrKzsNCisgfQ0KKw0KKyBpZiAoZW50cnkg IT0gZW5kX2VudHJ5KQ0KKyBnb3RvIG1hcHBpbmdfZXJyb3I7DQorDQorIHBndGFibGVfZmx1c2go c3RhcnRfZW50cnksIGVudHJ5KTsNCisgZ290byBtYXBwaW5nX2RvbmU7DQorIH0NCisNCisgaWYg KFM1UF9GQVVMVF9MVjFfRU5UUlkoKmVudHJ5KSkgew0KKyB1bnNpZ25lZCBsb25nICpsMnRhYmxl Ow0KKw0KKyBsMnRhYmxlID0ga21lbV9jYWNoZV96YWxsb2MobDJ0YWJsZV9jYWNoZXAsIEdGUF9L RVJORUwpOw0KKyBpZiAoIWwydGFibGUpIHsNCisgcmV0ID0gLUVOT01FTTsNCisgZ290byBub21l bV9lcnJvcjsNCisgfQ0KKw0KKyBwZ3RhYmxlX2ZsdXNoKGVudHJ5LCBlbnRyeSArIFM1UF9MVjJU QUJMRV9FTlRSSUVTKTsNCisNCisgTUFLRV9MVjJUQUJMRV9FTlRSWSgqZW50cnksIHZpcnRfdG9f cGh5cyhsMnRhYmxlKSk7DQorIHBndGFibGVfZmx1c2goZW50cnksIGVudHJ5ICsgMSk7DQorIH0N CisNCisgLyogJ2VudHJ5JyBwb2ludHMgbGV2ZWwgMiBlbnRyaWVzLCBoZXJlYWZ0ZXIgKi8NCisg ZW50cnkgPSBHRVRfTFYyRU5UUlkoKmVudHJ5LCBpb3ZhKTsNCisNCisgc3RhcnRfZW50cnkgPSBl bnRyeTsNCisgbnVtX2VudHJ5ID0gMSA8PCBnZnBfb3JkZXI7DQorIGVuZF9lbnRyeSA9IGVudHJ5 ICsgbnVtX2VudHJ5Ow0KKw0KKyBpZiAoZ2ZwX29yZGVyID49IFM1UF9MUEFHRV9PUkRFUikgew0K KyAvKiBsYXJnZSBwYWdlKDY0S2lCKSBtYXBwaW5nICovDQorIEJVR19PTigocGFkZHIgfCBpb3Zh KSAmIH5TNVBfTFBBR0VfTUFTSyk7DQorDQorIHdoaWxlIChlbnRyeSAhPSBlbmRfZW50cnkpIHsN CisgaWYgKCF3cml0ZV9scGFnZShlbnRyeSwgcGFkZHIpKSB7DQorIHByX2VycigiJXM6IEZhaWxl ZCB0byBhbGxvY2F0ZSBsYXJnZSBwYWdlIg0KKyAiIGVudHJ5LlxuIiwgX19mdW5jX18pOw0KKyBi cmVhazsNCisgfQ0KKw0KKyBwYWRkciArPSBTNVBfTFBBR0VfU0laRTsNCisgZW50cnkgKz0gKDEg PDwgUzVQX0xQQUdFX09SREVSKTsNCisgfQ0KKw0KKyBpZiAoZW50cnkgIT0gZW5kX2VudHJ5KSB7 DQorIGVudHJ5IC09IDEgPDwgUzVQX0xQQUdFX09SREVSOw0KKyBnb3RvIG1hcHBpbmdfZXJyb3I7 DQorIH0NCisgfSBlbHNlIHsNCisgLyogcGFnZSAoNEtpQikgbWFwcGluZyAqLw0KKyB3aGlsZSAo ZW50cnkgIT0gZW5kX2VudHJ5ICYmIFM1UF9GQVVMVF9MVjJfRU5UUlkoKmVudHJ5KSkgew0KKw0K KyBNQUtFX1NQQUdFX0VOVFJZKCplbnRyeSwgcGFkZHIpOw0KKw0KKyBlbnRyeSsrOw0KKyBwYWRk ciArPSBTNVBfU1BBR0VfU0laRTsNCisgfQ0KKw0KKyBpZiAoZW50cnkgIT0gZW5kX2VudHJ5KSB7 DQorIHByX2VycigiJXM6IEZhaWxlZCB0byBhbGxvY2F0ZSBzbWFsbCBwYWdlIGVudHJ5LlxuIiwN CisgX19mdW5jX18pOw0KKyBnb3RvIG1hcHBpbmdfZXJyb3I7DQorIH0NCisgfQ0KKw0KKyBwZ3Rh YmxlX2ZsdXNoKHN0YXJ0X2VudHJ5LCBlbnRyeSk7DQorbWFwcGluZ19lcnJvcjoNCisgaWYgKGVu dHJ5ICE9IGVuZF9lbnRyeSkgew0KKyB1bnNpZ25lZCBsb25nICpjdXJyZW50X2VudHJ5ID0gZW50 cnk7DQorIHdoaWxlIChlbnRyeSAhPSBzdGFydF9lbnRyeSkNCisgTUFLRV9GQVVMVF9FTlRSWSgq KC0tZW50cnkpKTsNCisgcGd0YWJsZV9mbHVzaChzdGFydF9lbnRyeSwgY3VycmVudF9lbnRyeSk7 DQorIHJldCA9IC1FQUREUklOVVNFOw0KKyB9DQorDQorbm9tZW1fZXJyb3I6DQorbWFwcGluZ19k b25lOg0KKyBzcGluX3VubG9ja19pcnFyZXN0b3JlKCZwcml2LT5wZ3RhYmxlbG9jaywgZmxhZ3Mp Ow0KKw0KKyByZXR1cm4gMDsNCit9DQorDQorc3RhdGljIGludCBleHlub3NfaW9tbXVfdW5tYXAo c3RydWN0IGlvbW11X2RvbWFpbiAqZG9tYWluLCB1bnNpZ25lZCBsb25nIGlvdmEsDQorICAgIGlu dCBnZnBfb3JkZXIpDQorew0KKyBzdHJ1Y3QgZXh5bm9zX2lvbW11X2RvbWFpbiAqcHJpdiA9IGRv bWFpbi0+cHJpdjsNCisgdW5zaWduZWQgbG9uZyAqZW50cnk7DQorIGludCBudW1fZW50cnk7DQor IHVuc2lnbmVkIGxvbmcgZmxhZ3M7DQorDQorIEJVR19PTihwcml2LT5wZ3RhYmxlID09IE5VTEwp Ow0KKw0KKyBzcGluX2xvY2tfaXJxc2F2ZSgmcHJpdi0+cGd0YWJsZWxvY2ssIGZsYWdzKTsNCisN CisgZW50cnkgPSBwcml2LT5wZ3RhYmxlICsgKGlvdmEgPj4gUzVQX1NFQ1RJT05fU0hJRlQpOw0K Kw0KKyBpZiAoZ2ZwX29yZGVyID49IFM1UF9TRUNUSU9OX09SREVSKSB7DQorIGdmcF9vcmRlciAt PSBTNVBfU0VDVElPTl9PUkRFUjsNCisgbnVtX2VudHJ5ID0gMSA8PCAoZ2ZwX29yZGVyIC0gUzVQ X1NFQ1RJT05fT1JERVIpOw0KKyB3aGlsZSAobnVtX2VudHJ5LS0pIHsNCisgaWYgKFM1UF9TRUNU SU9OX0xWMV9FTlRSWSgqZW50cnkpKSB7DQorIE1BS0VfRkFVTFRfRU5UUlkoKmVudHJ5KTsNCisg fSBlbHNlIGlmIChTNVBfUEFHRV9MVjFfRU5UUlkoKmVudHJ5KSkgew0KKyB1bnNpZ25lZCBsb25n ICpsdjJiZWcsICpsdjJlbmQ7DQorIGx2MmJlZyA9IHBoeXNfdG9fdmlydCgNCisgKmVudHJ5ICYg UzVQX0xWMlRBQkxFX01BU0spOw0KKyBsdjJlbmQgPSBsdjJiZWcgKyBTNVBfTFYyVEFCTEVfRU5U UklFUzsNCisgd2hpbGUgKGx2MmJlZyAhPSBsdjJlbmQpIHsNCisgTUFLRV9GQVVMVF9FTlRSWSgq bHYyYmVnKTsNCisgbHYyYmVnKys7DQorIH0NCisgfQ0KKyBlbnRyeSsrOw0KKyB9DQorIH0gZWxz ZSB7DQorIGVudHJ5ID0gR0VUX0xWMkVOVFJZKCplbnRyeSwgaW92YSk7DQorDQorIEJVR19PTihT NVBfTFBBR0VfTFYyX0VOVFJZKCplbnRyeSkgJiYNCisgKGdmcF9vcmRlciA8IFM1UF9MUEFHRV9P UkRFUikpOw0KKw0KKyBudW1fZW50cnkgPSAxIDw8IGdmcF9vcmRlcjsNCisNCisgd2hpbGUgKG51 bV9lbnRyeS0tKSB7DQorIE1BS0VfRkFVTFRfRU5UUlkoKmVudHJ5KTsNCisgZW50cnkrKzsNCisg fQ0KKyB9DQorDQorIGlmIChwcml2LT5kZXYpDQorIGV4eW5vc19zeXNtbXVfdGxiX2ludmFsaWRh dGUocHJpdi0+ZGV2KTsNCisNCisgc3Bpbl91bmxvY2tfaXJxcmVzdG9yZSgmcHJpdi0+cGd0YWJs ZWxvY2ssIGZsYWdzKTsNCisNCisgcmV0dXJuIDA7DQorfQ0KKw0KK3N0YXRpYyBwaHlzX2FkZHJf dCBleHlub3NfaW9tbXVfaW92YV90b19waHlzKHN0cnVjdCBpb21tdV9kb21haW4gKmRvbWFpbiwN CisgICB1bnNpZ25lZCBsb25nIGlvdmEpDQorew0KKyBzdHJ1Y3QgZXh5bm9zX2lvbW11X2RvbWFp biAqcHJpdiA9IGRvbWFpbi0+cHJpdjsNCisgdW5zaWduZWQgbG9uZyAqZW50cnk7DQorIHVuc2ln bmVkIGxvbmcgb2Zmc2V0Ow0KKw0KKyBlbnRyeSA9IHByaXYtPnBndGFibGUgKyAoaW92YSA+PiBT NVBfU0VDVElPTl9TSElGVCk7DQorDQorIGlmIChTNVBfRkFVTFRfTFYxX0VOVFJZKCplbnRyeSkp DQorIHJldHVybiAwOw0KKw0KKyBvZmZzZXQgPSBpb3ZhICYgflM1UF9TRUNUSU9OX01BU0s7DQor DQorIGlmIChTNVBfU0VDVElPTl9MVjFfRU5UUlkoKmVudHJ5KSkNCisgcmV0dXJuICgqZW50cnkg JiBTNVBfU0VDVElPTl9NQVNLKSArIG9mZnNldDsNCisNCisgZW50cnkgPSBHRVRfTFYyRU5UUlko KmVudHJ5LCBpb3ZhKTsNCisNCisgaWYgKFM1UF9TUEFHRV9MVjJfRU5UUlkoKmVudHJ5KSkNCisg cmV0dXJuICgqZW50cnkgJiBTNVBfU1BBR0VfTUFTSykgKyAoaW92YSAmIH5TNVBfU1BBR0VfTUFT Syk7DQorDQorIGlmIChTNVBfTFBBR0VfTFYyX0VOVFJZKCplbnRyeSkpDQorIHJldHVybiAoKmVu dHJ5ICYgUzVQX0xQQUdFX01BU0spICsgKGlvdmEgJiB+UzVQX0xQQUdFX01BU0spOw0KKw0KKyBy ZXR1cm4gMDsNCit9DQorDQorc3RhdGljIGludCBleHlub3NfaW9tbXVfZG9tYWluX2hhc19jYXAo c3RydWN0IGlvbW11X2RvbWFpbiAqZG9tYWluLA0KKyAgICAgdW5zaWduZWQgbG9uZyBjYXApDQor ew0KKyByZXR1cm4gMDsNCit9DQorDQorc3RhdGljIHN0cnVjdCBpb21tdV9vcHMgZXh5bm9zX2lv bW11X29wcyA9IHsNCisgLmRvbWFpbl9pbml0ID0gJmV4eW5vc19pb21tdV9kb21haW5faW5pdCwN CisgLmRvbWFpbl9kZXN0cm95ID0gJmV4eW5vc19pb21tdV9kb21haW5fZGVzdHJveSwNCisgLmF0 dGFjaF9kZXYgPSAmZXh5bm9zX2lvbW11X2F0dGFjaF9kZXZpY2UsDQorIC5kZXRhY2hfZGV2ID0g JmV4eW5vc19pb21tdV9kZXRhY2hfZGV2aWNlLA0KKyAubWFwID0gJmV4eW5vc19pb21tdV9tYXAs DQorIC51bm1hcCA9ICZleHlub3NfaW9tbXVfdW5tYXAsDQorIC5pb3ZhX3RvX3BoeXMgPSAmZXh5 bm9zX2lvbW11X2lvdmFfdG9fcGh5cywNCisgLmRvbWFpbl9oYXNfY2FwID0gJmV4eW5vc19pb21t dV9kb21haW5faGFzX2NhcCwNCit9Ow0KKw0KK3N0YXRpYyBpbnQgX19pbml0IGV4eW5vc19pb21t dV9pbml0KHZvaWQpDQorew0KKyBsMnRhYmxlX2NhY2hlcCA9IGttZW1fY2FjaGVfY3JlYXRlKCJT eXNNTVUgTHYyIFRhYmxlcyIsDQorIFM1UF9MVjJUQUJMRV9TSVpFLCBTNVBfTFYyVEFCTEVfU0la RSwgMCwgTlVMTCk7DQorIGlmICghbDJ0YWJsZV9jYWNoZXApDQorIHJldHVybiAtRU5PTUVNOw0K Kw0KKyByZWdpc3Rlcl9pb21tdSgmZXh5bm9zX2lvbW11X29wcyk7DQorDQorIHJldHVybiAwOw0K K30NCithcmNoX2luaXRjYWxsKGV4eW5vc19pb21tdV9pbml0KTsNCi0tIA0KMS43LjENCg== From mboxrd@z Thu Jan 1 00:00:00 1970 From: pullip.cho@samsung.com (=?euc-kr?B?wbaw5sij?=) Date: Sat, 24 Sep 2011 07:35:45 +0000 (GMT) Subject: [PATCH 3/4] iommu/exynos: Add iommu driver for Exynos4 Platforms Message-ID: <5525949.50001316849744994.JavaMail.weblogic@epml24> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org This is the System MMU driver and IOMMU API implementation for Exynos4 SOC platforms. Exynos4 platforms has more than 10 System MMUs dedicated for each multimedia accellerators. Signed-off-by: KyongHo Cho --- drivers/iommu/Kconfig | 14 + drivers/iommu/Makefile | 1 + drivers/iommu/exynos_iommu.c | 859 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 874 insertions(+), 0 deletions(-) create mode 100644 drivers/iommu/exynos_iommu.c diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index b57b3fa..1c754cd 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -107,4 +107,18 @@ config INTR_REMAP To use x2apic mode in the CPU's which support x2APIC enhancements or to support platforms with CPU's having > 8 bit APIC ID, say Y. +# EXYNOS IOMMU support +config EXYNOS_IOMMU + bool "Exynos IOMMU Support" + depends on ARCH_EXYNOS4 + select IOMMU_API + select EXYNOS4_DEV_SYSMMU + help + Support for the IOMMUs (System MMUs) Samsung Exynos application + processor family. This enables H/W multimedia accellerators to view + non-linear physical memory chunks as a linear memory in their virtual + address spaces. + + If unsure, say N here. + endif # IOMMU_SUPPORT diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 4d4d77d..1eb924f 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o +obj-$(CONFIG_EXYNOS_IOMMU) += exynos_iommu.o diff --git a/drivers/iommu/exynos_iommu.c b/drivers/iommu/exynos_iommu.c new file mode 100644 index 0000000..fe5b5d8 --- /dev/null +++ b/drivers/iommu/exynos_iommu.c @@ -0,0 +1,859 @@ +/* linux/drivers/iommu/exynos_iommu.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define CTRL_ENABLE 0x5 +#define CTRL_BLOCK 0x7 +#define CTRL_DISABLE 0x0 + +enum S5P_SYSMMU_INTERRUPT_TYPE { + SYSMMU_PAGEFAULT, + SYSMMU_AR_MULTIHIT, + SYSMMU_AW_MULTIHIT, + SYSMMU_BUSERROR, + SYSMMU_AR_SECURITY, + SYSMMU_AR_ACCESS, + SYSMMU_AW_SECURITY, + SYSMMU_AW_PROTECTION, /* 7 */ + SYSMMU_FAULTS_NUM +}; + +static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = { + S5P_PAGE_FAULT_ADDR, + S5P_AR_FAULT_ADDR, + S5P_AW_FAULT_ADDR, + S5P_DEFAULT_SLAVE_ADDR, + S5P_AR_FAULT_ADDR, + S5P_AR_FAULT_ADDR, + S5P_AW_FAULT_ADDR, + S5P_AW_FAULT_ADDR +}; + +static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { + "PAGE FAULT", + "AR MULTI-HIT FAULT", + "AW MULTI-HIT FAULT", + "BUS ERROR", + "AR SECURITY PROTECTION FAULT", + "AR ACCESS PROTECTION FAULT", + "AW SECURITY PROTECTION FAULT", + "AW ACCESS PROTECTION FAULT" +}; + +struct exynos_iommu_domain { + struct device *dev; + unsigned long *pgtable; + spinlock_t lock; + spinlock_t pgtablelock; +}; + +/* List of sysmmu_platdata */ +static LIST_HEAD(sysmmu_list); + +static inline struct sysmmu_platdata *get_sysmmu_data(struct device *owner, + struct sysmmu_platdata *start) +{ + struct list_head *pos, *head; + + head = (start) ? &start->node : &sysmmu_list; + + list_for_each(pos, head) { + struct sysmmu_platdata *mmudata = + container_of(pos, struct sysmmu_platdata, node); + + if (pos == &sysmmu_list) + return NULL; + + if (mmudata->owner == owner) + return mmudata; + } + + return NULL; +} + +static inline struct sysmmu_platdata *get_platdata(struct device *dev) +{ + return dev_get_platdata(dev); +} + +static inline bool set_sysmmu_active(struct sysmmu_platdata *mmudata) +{ + /* return true if the System MMU was not active previously + and it needs to be initialized */ + + return atomic_inc_return(&mmudata->activations) == 1; +} + +static inline bool set_sysmmu_inactive(struct sysmmu_platdata *mmudata) +{ + /* return true if the System MMU is needed to be disabled */ + int ref; + + ref = atomic_dec_return(&mmudata->activations); + + if (ref == 0) + return true; + + if (WARN_ON(ref < 0)) { + /* System MMU is already disabled */ + atomic_set(&mmudata->activations, 0); + ref = 0; + } + + return false; +} + +static inline bool is_sysmmu_active(struct sysmmu_platdata *mmudata) +{ + return atomic_read(&mmudata->activations) != 0; +} + +static inline void sysmmu_block(void __iomem *sfrbase) +{ + __raw_writel(CTRL_BLOCK, sfrbase + S5P_MMU_CTRL); +} + +static inline void sysmmu_unblock(void __iomem *sfrbase) +{ + __raw_writel(CTRL_ENABLE, sfrbase + S5P_MMU_CTRL); +} + +static inline void __sysmmu_tlb_invalidate(void __iomem *sfrbase) +{ + __raw_writel(0x1, sfrbase + S5P_MMU_FLUSH); +} + +static inline void __sysmmu_set_ptbase(void __iomem *sfrbase, + unsigned long pgd) +{ + if (unlikely(pgd == 0)) { + pgd = (unsigned long)ZERO_PAGE(0); + __raw_writel(0x20, sfrbase + S5P_MMU_CFG); /* 4KB LV1 */ + } else { + __raw_writel(0x0, sfrbase + S5P_MMU_CFG); /* 16KB LV1 */ + } + + __raw_writel(pgd, sfrbase + S5P_PT_BASE_ADDR); + + __sysmmu_tlb_invalidate(sfrbase); +} + +static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) +{ + /* SYSMMU is in blocked when interrupt occurred. */ + unsigned long addr; + struct sysmmu_platdata *mmudata = dev_id; + enum S5P_SYSMMU_INTERRUPT_TYPE itype; + + WARN_ON(!is_sysmmu_active(mmudata)); + + itype = (enum S5P_SYSMMU_INTERRUPT_TYPE) + __ffs(__raw_readl(mmudata->sfrbase + S5P_INT_STATUS)); + + BUG_ON(!((itype >= 0) && (itype < 8))); + + dev_alert(mmudata->dev, "SYSTEM MMU FAULT!!!.\n"); + + if (!mmudata->domain) + return IRQ_NONE; + + addr = __raw_readl(mmudata->sfrbase + fault_reg_offset[itype]); + + if (!report_iommu_fault(mmudata->domain, mmudata->owner, addr, itype)) { + __raw_writel(1 << itype, mmudata->sfrbase + S5P_INT_CLEAR); + dev_notice(mmudata->dev, + "%s is resolved. Retrying translation.\n", + sysmmu_fault_name[itype]); + sysmmu_unblock(mmudata->sfrbase); + } else { + dev_notice(mmudata->dev, "%s is not handled.\n", + sysmmu_fault_name[itype]); + } + + return IRQ_HANDLED; +} + +void exynos_sysmmu_set_tablebase_pgd(struct device *owner, unsigned long pgd) +{ + struct sysmmu_platdata *mmudata = NULL; + + while ((mmudata = get_sysmmu_data(owner, mmudata))) { + if (is_sysmmu_active(mmudata)) { + sysmmu_block(mmudata->sfrbase); + __sysmmu_set_ptbase(mmudata->sfrbase, pgd); + sysmmu_unblock(mmudata->sfrbase); + dev_dbg(mmudata->dev, "New page table base is %p\n", + (void *)pgd); + } else { + dev_dbg(mmudata->dev, + "Disabled: Skipping setting page table base.\n"); + } + } +} + +int exynos_sysmmu_enable(struct iommu_domain *domain) +{ + struct sysmmu_platdata *mmudata = NULL; + bool enabled = false; + struct exynos_iommu_domain *priv = domain->priv; + + if (!priv || !priv->dev) + return -EINVAL; + + /* There are some devices that control more System MMUs than one such + * as MFC. + */ + while ((mmudata = get_sysmmu_data(priv->dev, mmudata))) { + enabled = true; + + if (!set_sysmmu_active(mmudata)) { + dev_dbg(mmudata->dev, "Already enabled.\n"); + continue; + } + + pm_runtime_get_sync(mmudata->dev); + + clk_enable(mmudata->clk); + + __sysmmu_set_ptbase(mmudata->sfrbase, __pa(priv->pgtable)); + + __raw_writel(CTRL_ENABLE, mmudata->sfrbase + S5P_MMU_CTRL); + + mmudata->domain = domain; + + dev_dbg(mmudata->dev, "Enabled.\n"); + } + + return (enabled) ? 0 : -ENODEV; +} + +void exynos_sysmmu_disable(struct iommu_domain *domain) +{ + struct sysmmu_platdata *mmudata = NULL; + bool disabled = false; + struct exynos_iommu_domain *priv = domain->priv; + + if (!priv || !priv->dev) + return; + + while ((mmudata = get_sysmmu_data(priv->dev, mmudata))) { + disabled = true; + + if (!set_sysmmu_inactive(mmudata)) { + dev_dbg(mmudata->dev, + "Inactivation request ignorred\n"); + continue; + } + + __raw_writel(CTRL_DISABLE, mmudata->sfrbase + S5P_MMU_CTRL); + + clk_disable(mmudata->clk); + + pm_runtime_put_sync(mmudata->dev); + + mmudata->domain = NULL; + + dev_dbg(mmudata->dev, "Disabled.\n"); + } + + BUG_ON(!disabled); +} + +void exynos_sysmmu_tlb_invalidate(struct device *owner) +{ + struct sysmmu_platdata *mmudata = NULL; + + while ((mmudata = get_sysmmu_data(owner, mmudata))) { + if (is_sysmmu_active(mmudata)) { + sysmmu_block(mmudata->sfrbase); + __sysmmu_tlb_invalidate(mmudata->sfrbase); + sysmmu_unblock(mmudata->sfrbase); + } else { + dev_dbg(mmudata->dev, + "Disabled: Skipping invalidating TLB.\n"); + } + } +} + +static int exynos_sysmmu_probe(struct platform_device *pdev) +{ + struct resource *res, *ioarea; + int ret; + int irq; + struct device *dev; + void *sfr; + + dev = &pdev->dev; + if (!get_platdata(dev) || (get_platdata(dev)->owner == NULL)) { + dev_err(dev, "Failed to probing system MMU: " + "Owner device is not set."); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, + "Failed probing system MMU: failed to get resource."); + return -ENOENT; + } + + ioarea = request_mem_region(res->start, resource_size(res), pdev->name); + if (ioarea == NULL) { + dev_err(dev, "Failed probing system MMU: " + "failed to request memory region."); + return -ENOMEM; + } + + sfr = ioremap(res->start, resource_size(res)); + if (!sfr) { + dev_err(dev, "Failed probing system MMU: " + "failed to call ioremap()."); + ret = -ENOENT; + goto err_ioremap; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "Failed probing system MMU: " + "failed to get irq resource."); + ret = irq; + goto err_irq; + } + + if (request_irq(irq, exynos_sysmmu_irq, 0, dev_name(&pdev->dev), + dev_get_platdata(dev))) { + dev_err(dev, "Failed probing system MMU: " + "failed to request irq."); + ret = -ENOENT; + goto err_irq; + } + + get_platdata(dev)->clk = clk_get(dev, "sysmmu"); + + if (IS_ERR_OR_NULL(get_platdata(dev)->clk)) { + dev_err(dev, "Failed to probing System MMU: " + "failed to get clock descriptor"); + ret = -ENOENT; + goto err_clk; + } + + get_platdata(dev)->sfrbase = sfr; + + list_add(&get_platdata(dev)->node, &sysmmu_list); + + if (dev->parent) + pm_runtime_enable(dev); + + dev_dbg(dev, "Initialized for %s.\n", + dev_name(get_platdata(dev)->owner)); + return 0; +err_clk: + free_irq(irq, dev_get_platdata(dev)); +err_irq: + iounmap(get_platdata(dev)->sfrbase); +err_ioremap: + release_resource(ioarea); + kfree(ioarea); + dev_err(dev, "Probing system MMU failed."); + return ret; +} + +static int exynos_sysmmu_remove(struct platform_device *pdev) +{ + return 0; +} + +int exynos_sysmmu_runtime_suspend(struct device *dev) +{ + if (WARN_ON(is_sysmmu_active(dev_get_platdata(dev)))) + return -EFAULT; + + return 0; +} + +int exynos_sysmmu_runtime_resume(struct device *dev) +{ + if (WARN_ON(is_sysmmu_active(dev_get_platdata(dev)))) + return -EFAULT; + + return 0; +} + +const struct dev_pm_ops exynos_sysmmu_pm_ops = { + .runtime_suspend = exynos_sysmmu_runtime_suspend, + .runtime_resume = exynos_sysmmu_runtime_resume, +}; + +static struct platform_driver exynos_sysmmu_driver = { + .probe = exynos_sysmmu_probe, + .remove = exynos_sysmmu_remove, + .driver = { + .owner = THIS_MODULE, + .name = "s5p-sysmmu", + .pm = &exynos_sysmmu_pm_ops, + } +}; + +static int __init exynos_sysmmu_init(void) +{ + return platform_driver_register(&exynos_sysmmu_driver); +} +arch_initcall(exynos_sysmmu_init); + +/* We does not consider super section mapping (16MB) */ +#define S5P_SPAGE_SHIFT 12 +#define S5P_LPAGE_SHIFT 16 +#define S5P_SECTION_SHIFT 20 + +#define S5P_SPAGE_SIZE (1 << S5P_SPAGE_SHIFT) +#define S5P_LPAGE_SIZE (1 << S5P_LPAGE_SHIFT) +#define S5P_SECTION_SIZE (1 << S5P_SECTION_SHIFT) + +#define S5P_SPAGE_MASK (~(S5P_SPAGE_SIZE - 1)) +#define S5P_LPAGE_MASK (~(S5P_LPAGE_SIZE - 1)) +#define S5P_SECTION_MASK (~(S5P_SECTION_SIZE - 1)) + +#define S5P_SPAGE_ORDER (S5P_SPAGE_SHIFT - PAGE_SHIFT) +#define S5P_LPAGE_ORDER (S5P_LPAGE_SHIFT - S5P_SPAGE_SHIFT) +#define S5P_SECTION_ORDER (S5P_SECTION_SHIFT - S5P_SPAGE_SHIFT) + +#define S5P_LV1TABLE_ENTRIES (1 << (BITS_PER_LONG - S5P_SECTION_SHIFT)) + +#define S5P_LV2TABLE_ENTRIES (1 << S5P_SECTION_ORDER) +#define S5P_LV2TABLE_SIZE (S5P_LV2TABLE_ENTRIES * sizeof(long)) +#define S5P_LV2TABLE_MASK (~(S5P_LV2TABLE_SIZE - 1)) /* 0xFFFFFC00 */ + +#define S5P_SECTION_LV1_ENTRY(entry) ((entry & 0x40003) == 2) +#define S5P_SUPSECT_LV1_ENTRY(entry) ((entry & 0x40003) == 0x40002) +#define S5P_PAGE_LV1_ENTRY(entry) ((entry & 3) == 1) +#define S5P_FAULT_LV1_ENTRY(entry) (((entry & 3) == 0) || (entry & 3) == 3) + +#define S5P_LPAGE_LV2_ENTRY(entry) ((entry & 3) == 1) +#define S5P_SPAGE_LV2_ENTRY(entry) ((entry & 2) == 2) +#define S5P_FAULT_LV2_ENTRY(entry) ((entry & 3) == 0) + +#define MAKE_FAULT_ENTRY(entry) do { entry = 0; } while (0) +#define MAKE_SECTION_ENTRY(entry, pa) do { entry = pa | 2; } while (0) +#define MAKE_SUPSECT_ENTRY(entry, pa) do { entry = pa | 0x40002; } while (0) +#define MAKE_LV2TABLE_ENTRY(entry, pa) do { entry = pa | 1; } while (0) + +#define MAKE_LPAGE_ENTRY(entry, pa) do { entry = pa | 1; } while (0) +#define MAKE_SPAGE_ENTRY(entry, pa) do { entry = pa | 3; } while (0) + +#define GET_LV2ENTRY(entry, iova) (\ + (unsigned long *)phys_to_virt(entry & S5P_LV2TABLE_MASK) +\ + ((iova & (~S5P_SECTION_MASK)) >> S5P_SPAGE_SHIFT)) + +/* slab cache for level 2 page tables */ +static struct kmem_cache *l2table_cachep; + +static inline void pgtable_flush(void *vastart, void *vaend) +{ + dmac_flush_range(vastart, vaend); + outer_flush_range(virt_to_phys(vastart), + virt_to_phys(vaend)); +} + +static int exynos_iommu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, int flags) +{ + struct exynos_iommu_domain *priv = domain->priv; + + dev_err(priv->dev, "%s occured at %p(Page table base: %p)\n", + sysmmu_fault_name[flags], (void *)iova, + (void *)(__pa(priv->pgtable))); + dev_err(priv->dev, "\t\tGenerating Kernel OOPS...\n"); + dev_err(priv->dev, "\t\tbecause it is unrecoverable.\n"); + dev_err(priv->dev, + "\t\tSet Fault handler with iommu_set_fault_handler().\n"); + dev_err(priv->dev, "\t\tto handle System MMU fault.\n"); + + BUG(); + + return 0; +} + +static int exynos_iommu_domain_init(struct iommu_domain *domain) +{ + struct exynos_iommu_domain *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL, + (S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)) >> PAGE_SHIFT); + if (!priv->pgtable) { + kfree(priv); + return -ENOMEM; + } + + memset(priv->pgtable, 0, S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)); + pgtable_flush(priv->pgtable, priv->pgtable + S5P_LV1TABLE_ENTRIES); + + spin_lock_init(&priv->lock); + + domain->priv = priv; + + iommu_set_fault_handler(domain, &exynos_iommu_fault_handler); + + return 0; +} + +static void exynos_iommu_domain_destroy(struct iommu_domain *domain) +{ + struct exynos_iommu_domain *priv = domain->priv; + + free_pages((unsigned long)priv->pgtable, + (S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)) >> PAGE_SHIFT); + + kfree(priv); + + domain->priv = NULL; +} + +static int exynos_iommu_attach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct exynos_iommu_domain *priv = domain->priv; + int ret; + + spin_lock(&priv->lock); + + priv->dev = dev; + + ret = exynos_sysmmu_enable(domain); + if (ret) + return ret; + + spin_unlock(&priv->lock); + + return 0; +} + +static void exynos_iommu_detach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct exynos_iommu_domain *priv = domain->priv; + + spin_lock(&priv->lock); + + if (priv->dev == dev) { + exynos_sysmmu_disable(domain); + priv->dev = NULL; + } + + spin_unlock(&priv->lock); +} + +static bool section_available(struct iommu_domain *domain, + unsigned long *lv1entry) +{ + struct exynos_iommu_domain *priv = domain->priv; + + if (S5P_SECTION_LV1_ENTRY(*lv1entry)) { + dev_err(priv->dev, "1MB entry alread exists at 0x%08x\n", + (lv1entry - priv->pgtable) * SZ_1M); + return false; + } + + if (S5P_PAGE_LV1_ENTRY(*lv1entry)) { + unsigned long *lv2end, *lv2base; + + lv2base = phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK); + lv2end = lv2base + S5P_LV2TABLE_ENTRIES; + while (lv2base != lv2end) { + if (!S5P_FAULT_LV2_ENTRY(*lv2base)) { + dev_err(priv->dev, "Failed to free L2 page " + "table for section mapping.\n"); + return false; + } + lv2base++; + } + + kmem_cache_free(l2table_cachep, + phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK)); + + MAKE_FAULT_ENTRY(*lv1entry); + } + + return true; +} + +static bool write_lpage(unsigned long *head_entry, unsigned long phys_addr) +{ + unsigned long *entry, *end; + + entry = head_entry; + end = entry + (1 << S5P_LPAGE_ORDER); + + while (entry != end) { + if (!S5P_FAULT_LV2_ENTRY(*entry)) + break; + + MAKE_LPAGE_ENTRY(*entry, phys_addr); + + entry++; + } + + if (entry != end) { + end = entry; + while (entry != head_entry) + MAKE_FAULT_ENTRY(*(--entry)); + + return false; + } + + return true; +} + +static int exynos_iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, int gfp_order, int prot) +{ + struct exynos_iommu_domain *priv = domain->priv; + unsigned long *start_entry, *entry, *end_entry; + int num_entry; + int ret = 0; + unsigned long flags; + + BUG_ON(priv->pgtable == NULL); + + spin_lock_irqsave(&priv->pgtablelock, flags); + + start_entry = entry = priv->pgtable + (iova >> S5P_SECTION_SHIFT); + + if (gfp_order >= S5P_SECTION_ORDER) { + BUG_ON((paddr | iova) & ~S5P_SECTION_MASK); + /* 1MiB mapping */ + + num_entry = 1 << (gfp_order - S5P_SECTION_ORDER); + end_entry = entry + num_entry; + + while (entry != end_entry) { + if (!section_available(domain, entry)) + break; + + MAKE_SECTION_ENTRY(*entry, paddr); + + paddr += S5P_SECTION_SIZE; + entry++; + } + + if (entry != end_entry) + goto mapping_error; + + pgtable_flush(start_entry, entry); + goto mapping_done; + } + + if (S5P_FAULT_LV1_ENTRY(*entry)) { + unsigned long *l2table; + + l2table = kmem_cache_zalloc(l2table_cachep, GFP_KERNEL); + if (!l2table) { + ret = -ENOMEM; + goto nomem_error; + } + + pgtable_flush(entry, entry + S5P_LV2TABLE_ENTRIES); + + MAKE_LV2TABLE_ENTRY(*entry, virt_to_phys(l2table)); + pgtable_flush(entry, entry + 1); + } + + /* 'entry' points level 2 entries, hereafter */ + entry = GET_LV2ENTRY(*entry, iova); + + start_entry = entry; + num_entry = 1 << gfp_order; + end_entry = entry + num_entry; + + if (gfp_order >= S5P_LPAGE_ORDER) { + /* large page(64KiB) mapping */ + BUG_ON((paddr | iova) & ~S5P_LPAGE_MASK); + + while (entry != end_entry) { + if (!write_lpage(entry, paddr)) { + pr_err("%s: Failed to allocate large page" + " entry.\n", __func__); + break; + } + + paddr += S5P_LPAGE_SIZE; + entry += (1 << S5P_LPAGE_ORDER); + } + + if (entry != end_entry) { + entry -= 1 << S5P_LPAGE_ORDER; + goto mapping_error; + } + } else { + /* page (4KiB) mapping */ + while (entry != end_entry && S5P_FAULT_LV2_ENTRY(*entry)) { + + MAKE_SPAGE_ENTRY(*entry, paddr); + + entry++; + paddr += S5P_SPAGE_SIZE; + } + + if (entry != end_entry) { + pr_err("%s: Failed to allocate small page entry.\n", + __func__); + goto mapping_error; + } + } + + pgtable_flush(start_entry, entry); +mapping_error: + if (entry != end_entry) { + unsigned long *current_entry = entry; + while (entry != start_entry) + MAKE_FAULT_ENTRY(*(--entry)); + pgtable_flush(start_entry, current_entry); + ret = -EADDRINUSE; + } + +nomem_error: +mapping_done: + spin_unlock_irqrestore(&priv->pgtablelock, flags); + + return 0; +} + +static int exynos_iommu_unmap(struct iommu_domain *domain, unsigned long iova, + int gfp_order) +{ + struct exynos_iommu_domain *priv = domain->priv; + unsigned long *entry; + int num_entry; + unsigned long flags; + + BUG_ON(priv->pgtable == NULL); + + spin_lock_irqsave(&priv->pgtablelock, flags); + + entry = priv->pgtable + (iova >> S5P_SECTION_SHIFT); + + if (gfp_order >= S5P_SECTION_ORDER) { + gfp_order -= S5P_SECTION_ORDER; + num_entry = 1 << (gfp_order - S5P_SECTION_ORDER); + while (num_entry--) { + if (S5P_SECTION_LV1_ENTRY(*entry)) { + MAKE_FAULT_ENTRY(*entry); + } else if (S5P_PAGE_LV1_ENTRY(*entry)) { + unsigned long *lv2beg, *lv2end; + lv2beg = phys_to_virt( + *entry & S5P_LV2TABLE_MASK); + lv2end = lv2beg + S5P_LV2TABLE_ENTRIES; + while (lv2beg != lv2end) { + MAKE_FAULT_ENTRY(*lv2beg); + lv2beg++; + } + } + entry++; + } + } else { + entry = GET_LV2ENTRY(*entry, iova); + + BUG_ON(S5P_LPAGE_LV2_ENTRY(*entry) && + (gfp_order < S5P_LPAGE_ORDER)); + + num_entry = 1 << gfp_order; + + while (num_entry--) { + MAKE_FAULT_ENTRY(*entry); + entry++; + } + } + + if (priv->dev) + exynos_sysmmu_tlb_invalidate(priv->dev); + + spin_unlock_irqrestore(&priv->pgtablelock, flags); + + return 0; +} + +static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova) +{ + struct exynos_iommu_domain *priv = domain->priv; + unsigned long *entry; + unsigned long offset; + + entry = priv->pgtable + (iova >> S5P_SECTION_SHIFT); + + if (S5P_FAULT_LV1_ENTRY(*entry)) + return 0; + + offset = iova & ~S5P_SECTION_MASK; + + if (S5P_SECTION_LV1_ENTRY(*entry)) + return (*entry & S5P_SECTION_MASK) + offset; + + entry = GET_LV2ENTRY(*entry, iova); + + if (S5P_SPAGE_LV2_ENTRY(*entry)) + return (*entry & S5P_SPAGE_MASK) + (iova & ~S5P_SPAGE_MASK); + + if (S5P_LPAGE_LV2_ENTRY(*entry)) + return (*entry & S5P_LPAGE_MASK) + (iova & ~S5P_LPAGE_MASK); + + return 0; +} + +static int exynos_iommu_domain_has_cap(struct iommu_domain *domain, + unsigned long cap) +{ + return 0; +} + +static struct iommu_ops exynos_iommu_ops = { + .domain_init = &exynos_iommu_domain_init, + .domain_destroy = &exynos_iommu_domain_destroy, + .attach_dev = &exynos_iommu_attach_device, + .detach_dev = &exynos_iommu_detach_device, + .map = &exynos_iommu_map, + .unmap = &exynos_iommu_unmap, + .iova_to_phys = &exynos_iommu_iova_to_phys, + .domain_has_cap = &exynos_iommu_domain_has_cap, +}; + +static int __init exynos_iommu_init(void) +{ + l2table_cachep = kmem_cache_create("SysMMU Lv2 Tables", + S5P_LV2TABLE_SIZE, S5P_LV2TABLE_SIZE, 0, NULL); + if (!l2table_cachep) + return -ENOMEM; + + register_iommu(&exynos_iommu_ops); + + return 0; +} +arch_initcall(exynos_iommu_init); -- 1.7.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752429Ab1IXHft (ORCPT ); Sat, 24 Sep 2011 03:35:49 -0400 Received: from mailout1.samsung.com ([203.254.224.24]:32612 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752304Ab1IXHfs (ORCPT ); Sat, 24 Sep 2011 03:35:48 -0400 X-AuditID: cbfee612-b7b08ae000004454-4f-4e7d8852838e Date: Sat, 24 Sep 2011 07:35:46 +0000 (GMT) From: =?euc-kr?B?wbaw5sij?= Subject: [PATCH 3/4] iommu/exynos: Add iommu driver for Exynos4 Platforms To: "linux-arm-kernel@lists.infradead.org" , "linux-samsung-soc@vger.kernel.org" , "iommu@lists.linux-foundation.org" , "linux-kernel@vger.kernel.org" Cc: =?euc-kr?Q?=C1=B6=B0=E6=C8=A3?= , "joerg.roedel@amd.com" , =?euc-kr?Q?=C0=CC=BB=F3=C7=F6?= , =?euc-kr?Q?=B1=E8=B1=B9=C1=F8?= , =?euc-kr?Q?=B1=E8=BF=B5=B6=F4?= , "ohad@wizery.com" Reply-to: pullip.cho@samsung.com MIME-version: 1.0 X-MTR: 20110924073440400@pullip.cho Msgkey: 20110924073440400@pullip.cho X-EPLocale: ko_KR.euc-kr X-Priority: 3 X-EPWebmail-Msg-Type: personal X-EPWebmail-Reply-Demand: 0 X-EPApproval-Locale: X-EPHeader: ML X-EPTrCode: X-EPTrName: X-MLAttribute: X-RootMTR: 20110924072254035@pullip.cho X-ParentMTR: 20110924073001548@pullip.cho Content-type: text/plain; charset=euc-kr MIME-version: 1.0 Message-id: <5525949.50001316849744994.JavaMail.weblogic@epml24> X-Brightmail-Tracker: AAAAAA== Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: 8bit X-MIME-Autoconverted: from base64 to 8bit by nfs id p8O7Zwrn008740 This is the System MMU driver and IOMMU API implementation for Exynos4 SOC platforms. Exynos4 platforms has more than 10 System MMUs dedicated for each multimedia accellerators. Signed-off-by: KyongHo Cho --- drivers/iommu/Kconfig | 14 + drivers/iommu/Makefile | 1 + drivers/iommu/exynos_iommu.c | 859 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 874 insertions(+), 0 deletions(-) create mode 100644 drivers/iommu/exynos_iommu.c diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index b57b3fa..1c754cd 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -107,4 +107,18 @@ config INTR_REMAP To use x2apic mode in the CPU's which support x2APIC enhancements or to support platforms with CPU's having > 8 bit APIC ID, say Y. +# EXYNOS IOMMU support +config EXYNOS_IOMMU + bool "Exynos IOMMU Support" + depends on ARCH_EXYNOS4 + select IOMMU_API + select EXYNOS4_DEV_SYSMMU + help + Support for the IOMMUs (System MMUs) Samsung Exynos application + processor family. This enables H/W multimedia accellerators to view + non-linear physical memory chunks as a linear memory in their virtual + address spaces. + + If unsure, say N here. + endif # IOMMU_SUPPORT diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 4d4d77d..1eb924f 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o +obj-$(CONFIG_EXYNOS_IOMMU) += exynos_iommu.o diff --git a/drivers/iommu/exynos_iommu.c b/drivers/iommu/exynos_iommu.c new file mode 100644 index 0000000..fe5b5d8 --- /dev/null +++ b/drivers/iommu/exynos_iommu.c @@ -0,0 +1,859 @@ +/* linux/drivers/iommu/exynos_iommu.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define CTRL_ENABLE 0x5 +#define CTRL_BLOCK 0x7 +#define CTRL_DISABLE 0x0 + +enum S5P_SYSMMU_INTERRUPT_TYPE { + SYSMMU_PAGEFAULT, + SYSMMU_AR_MULTIHIT, + SYSMMU_AW_MULTIHIT, + SYSMMU_BUSERROR, + SYSMMU_AR_SECURITY, + SYSMMU_AR_ACCESS, + SYSMMU_AW_SECURITY, + SYSMMU_AW_PROTECTION, /* 7 */ + SYSMMU_FAULTS_NUM +}; + +static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = { + S5P_PAGE_FAULT_ADDR, + S5P_AR_FAULT_ADDR, + S5P_AW_FAULT_ADDR, + S5P_DEFAULT_SLAVE_ADDR, + S5P_AR_FAULT_ADDR, + S5P_AR_FAULT_ADDR, + S5P_AW_FAULT_ADDR, + S5P_AW_FAULT_ADDR +}; + +static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { + "PAGE FAULT", + "AR MULTI-HIT FAULT", + "AW MULTI-HIT FAULT", + "BUS ERROR", + "AR SECURITY PROTECTION FAULT", + "AR ACCESS PROTECTION FAULT", + "AW SECURITY PROTECTION FAULT", + "AW ACCESS PROTECTION FAULT" +}; + +struct exynos_iommu_domain { + struct device *dev; + unsigned long *pgtable; + spinlock_t lock; + spinlock_t pgtablelock; +}; + +/* List of sysmmu_platdata */ +static LIST_HEAD(sysmmu_list); + +static inline struct sysmmu_platdata *get_sysmmu_data(struct device *owner, + struct sysmmu_platdata *start) +{ + struct list_head *pos, *head; + + head = (start) ? &start->node : &sysmmu_list; + + list_for_each(pos, head) { + struct sysmmu_platdata *mmudata = + container_of(pos, struct sysmmu_platdata, node); + + if (pos == &sysmmu_list) + return NULL; + + if (mmudata->owner == owner) + return mmudata; + } + + return NULL; +} + +static inline struct sysmmu_platdata *get_platdata(struct device *dev) +{ + return dev_get_platdata(dev); +} + +static inline bool set_sysmmu_active(struct sysmmu_platdata *mmudata) +{ + /* return true if the System MMU was not active previously + and it needs to be initialized */ + + return atomic_inc_return(&mmudata->activations) == 1; +} + +static inline bool set_sysmmu_inactive(struct sysmmu_platdata *mmudata) +{ + /* return true if the System MMU is needed to be disabled */ + int ref; + + ref = atomic_dec_return(&mmudata->activations); + + if (ref == 0) + return true; + + if (WARN_ON(ref < 0)) { + /* System MMU is already disabled */ + atomic_set(&mmudata->activations, 0); + ref = 0; + } + + return false; +} + +static inline bool is_sysmmu_active(struct sysmmu_platdata *mmudata) +{ + return atomic_read(&mmudata->activations) != 0; +} + +static inline void sysmmu_block(void __iomem *sfrbase) +{ + __raw_writel(CTRL_BLOCK, sfrbase + S5P_MMU_CTRL); +} + +static inline void sysmmu_unblock(void __iomem *sfrbase) +{ + __raw_writel(CTRL_ENABLE, sfrbase + S5P_MMU_CTRL); +} + +static inline void __sysmmu_tlb_invalidate(void __iomem *sfrbase) +{ + __raw_writel(0x1, sfrbase + S5P_MMU_FLUSH); +} + +static inline void __sysmmu_set_ptbase(void __iomem *sfrbase, + unsigned long pgd) +{ + if (unlikely(pgd == 0)) { + pgd = (unsigned long)ZERO_PAGE(0); + __raw_writel(0x20, sfrbase + S5P_MMU_CFG); /* 4KB LV1 */ + } else { + __raw_writel(0x0, sfrbase + S5P_MMU_CFG); /* 16KB LV1 */ + } + + __raw_writel(pgd, sfrbase + S5P_PT_BASE_ADDR); + + __sysmmu_tlb_invalidate(sfrbase); +} + +static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) +{ + /* SYSMMU is in blocked when interrupt occurred. */ + unsigned long addr; + struct sysmmu_platdata *mmudata = dev_id; + enum S5P_SYSMMU_INTERRUPT_TYPE itype; + + WARN_ON(!is_sysmmu_active(mmudata)); + + itype = (enum S5P_SYSMMU_INTERRUPT_TYPE) + __ffs(__raw_readl(mmudata->sfrbase + S5P_INT_STATUS)); + + BUG_ON(!((itype >= 0) && (itype < 8))); + + dev_alert(mmudata->dev, "SYSTEM MMU FAULT!!!.\n"); + + if (!mmudata->domain) + return IRQ_NONE; + + addr = __raw_readl(mmudata->sfrbase + fault_reg_offset[itype]); + + if (!report_iommu_fault(mmudata->domain, mmudata->owner, addr, itype)) { + __raw_writel(1 << itype, mmudata->sfrbase + S5P_INT_CLEAR); + dev_notice(mmudata->dev, + "%s is resolved. Retrying translation.\n", + sysmmu_fault_name[itype]); + sysmmu_unblock(mmudata->sfrbase); + } else { + dev_notice(mmudata->dev, "%s is not handled.\n", + sysmmu_fault_name[itype]); + } + + return IRQ_HANDLED; +} + +void exynos_sysmmu_set_tablebase_pgd(struct device *owner, unsigned long pgd) +{ + struct sysmmu_platdata *mmudata = NULL; + + while ((mmudata = get_sysmmu_data(owner, mmudata))) { + if (is_sysmmu_active(mmudata)) { + sysmmu_block(mmudata->sfrbase); + __sysmmu_set_ptbase(mmudata->sfrbase, pgd); + sysmmu_unblock(mmudata->sfrbase); + dev_dbg(mmudata->dev, "New page table base is %p\n", + (void *)pgd); + } else { + dev_dbg(mmudata->dev, + "Disabled: Skipping setting page table base.\n"); + } + } +} + +int exynos_sysmmu_enable(struct iommu_domain *domain) +{ + struct sysmmu_platdata *mmudata = NULL; + bool enabled = false; + struct exynos_iommu_domain *priv = domain->priv; + + if (!priv || !priv->dev) + return -EINVAL; + + /* There are some devices that control more System MMUs than one such + * as MFC. + */ + while ((mmudata = get_sysmmu_data(priv->dev, mmudata))) { + enabled = true; + + if (!set_sysmmu_active(mmudata)) { + dev_dbg(mmudata->dev, "Already enabled.\n"); + continue; + } + + pm_runtime_get_sync(mmudata->dev); + + clk_enable(mmudata->clk); + + __sysmmu_set_ptbase(mmudata->sfrbase, __pa(priv->pgtable)); + + __raw_writel(CTRL_ENABLE, mmudata->sfrbase + S5P_MMU_CTRL); + + mmudata->domain = domain; + + dev_dbg(mmudata->dev, "Enabled.\n"); + } + + return (enabled) ? 0 : -ENODEV; +} + +void exynos_sysmmu_disable(struct iommu_domain *domain) +{ + struct sysmmu_platdata *mmudata = NULL; + bool disabled = false; + struct exynos_iommu_domain *priv = domain->priv; + + if (!priv || !priv->dev) + return; + + while ((mmudata = get_sysmmu_data(priv->dev, mmudata))) { + disabled = true; + + if (!set_sysmmu_inactive(mmudata)) { + dev_dbg(mmudata->dev, + "Inactivation request ignorred\n"); + continue; + } + + __raw_writel(CTRL_DISABLE, mmudata->sfrbase + S5P_MMU_CTRL); + + clk_disable(mmudata->clk); + + pm_runtime_put_sync(mmudata->dev); + + mmudata->domain = NULL; + + dev_dbg(mmudata->dev, "Disabled.\n"); + } + + BUG_ON(!disabled); +} + +void exynos_sysmmu_tlb_invalidate(struct device *owner) +{ + struct sysmmu_platdata *mmudata = NULL; + + while ((mmudata = get_sysmmu_data(owner, mmudata))) { + if (is_sysmmu_active(mmudata)) { + sysmmu_block(mmudata->sfrbase); + __sysmmu_tlb_invalidate(mmudata->sfrbase); + sysmmu_unblock(mmudata->sfrbase); + } else { + dev_dbg(mmudata->dev, + "Disabled: Skipping invalidating TLB.\n"); + } + } +} + +static int exynos_sysmmu_probe(struct platform_device *pdev) +{ + struct resource *res, *ioarea; + int ret; + int irq; + struct device *dev; + void *sfr; + + dev = &pdev->dev; + if (!get_platdata(dev) || (get_platdata(dev)->owner == NULL)) { + dev_err(dev, "Failed to probing system MMU: " + "Owner device is not set."); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, + "Failed probing system MMU: failed to get resource."); + return -ENOENT; + } + + ioarea = request_mem_region(res->start, resource_size(res), pdev->name); + if (ioarea == NULL) { + dev_err(dev, "Failed probing system MMU: " + "failed to request memory region."); + return -ENOMEM; + } + + sfr = ioremap(res->start, resource_size(res)); + if (!sfr) { + dev_err(dev, "Failed probing system MMU: " + "failed to call ioremap()."); + ret = -ENOENT; + goto err_ioremap; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "Failed probing system MMU: " + "failed to get irq resource."); + ret = irq; + goto err_irq; + } + + if (request_irq(irq, exynos_sysmmu_irq, 0, dev_name(&pdev->dev), + dev_get_platdata(dev))) { + dev_err(dev, "Failed probing system MMU: " + "failed to request irq."); + ret = -ENOENT; + goto err_irq; + } + + get_platdata(dev)->clk = clk_get(dev, "sysmmu"); + + if (IS_ERR_OR_NULL(get_platdata(dev)->clk)) { + dev_err(dev, "Failed to probing System MMU: " + "failed to get clock descriptor"); + ret = -ENOENT; + goto err_clk; + } + + get_platdata(dev)->sfrbase = sfr; + + list_add(&get_platdata(dev)->node, &sysmmu_list); + + if (dev->parent) + pm_runtime_enable(dev); + + dev_dbg(dev, "Initialized for %s.\n", + dev_name(get_platdata(dev)->owner)); + return 0; +err_clk: + free_irq(irq, dev_get_platdata(dev)); +err_irq: + iounmap(get_platdata(dev)->sfrbase); +err_ioremap: + release_resource(ioarea); + kfree(ioarea); + dev_err(dev, "Probing system MMU failed."); + return ret; +} + +static int exynos_sysmmu_remove(struct platform_device *pdev) +{ + return 0; +} + +int exynos_sysmmu_runtime_suspend(struct device *dev) +{ + if (WARN_ON(is_sysmmu_active(dev_get_platdata(dev)))) + return -EFAULT; + + return 0; +} + +int exynos_sysmmu_runtime_resume(struct device *dev) +{ + if (WARN_ON(is_sysmmu_active(dev_get_platdata(dev)))) + return -EFAULT; + + return 0; +} + +const struct dev_pm_ops exynos_sysmmu_pm_ops = { + .runtime_suspend = exynos_sysmmu_runtime_suspend, + .runtime_resume = exynos_sysmmu_runtime_resume, +}; + +static struct platform_driver exynos_sysmmu_driver = { + .probe = exynos_sysmmu_probe, + .remove = exynos_sysmmu_remove, + .driver = { + .owner = THIS_MODULE, + .name = "s5p-sysmmu", + .pm = &exynos_sysmmu_pm_ops, + } +}; + +static int __init exynos_sysmmu_init(void) +{ + return platform_driver_register(&exynos_sysmmu_driver); +} +arch_initcall(exynos_sysmmu_init); + +/* We does not consider super section mapping (16MB) */ +#define S5P_SPAGE_SHIFT 12 +#define S5P_LPAGE_SHIFT 16 +#define S5P_SECTION_SHIFT 20 + +#define S5P_SPAGE_SIZE (1 << S5P_SPAGE_SHIFT) +#define S5P_LPAGE_SIZE (1 << S5P_LPAGE_SHIFT) +#define S5P_SECTION_SIZE (1 << S5P_SECTION_SHIFT) + +#define S5P_SPAGE_MASK (~(S5P_SPAGE_SIZE - 1)) +#define S5P_LPAGE_MASK (~(S5P_LPAGE_SIZE - 1)) +#define S5P_SECTION_MASK (~(S5P_SECTION_SIZE - 1)) + +#define S5P_SPAGE_ORDER (S5P_SPAGE_SHIFT - PAGE_SHIFT) +#define S5P_LPAGE_ORDER (S5P_LPAGE_SHIFT - S5P_SPAGE_SHIFT) +#define S5P_SECTION_ORDER (S5P_SECTION_SHIFT - S5P_SPAGE_SHIFT) + +#define S5P_LV1TABLE_ENTRIES (1 << (BITS_PER_LONG - S5P_SECTION_SHIFT)) + +#define S5P_LV2TABLE_ENTRIES (1 << S5P_SECTION_ORDER) +#define S5P_LV2TABLE_SIZE (S5P_LV2TABLE_ENTRIES * sizeof(long)) +#define S5P_LV2TABLE_MASK (~(S5P_LV2TABLE_SIZE - 1)) /* 0xFFFFFC00 */ + +#define S5P_SECTION_LV1_ENTRY(entry) ((entry & 0x40003) == 2) +#define S5P_SUPSECT_LV1_ENTRY(entry) ((entry & 0x40003) == 0x40002) +#define S5P_PAGE_LV1_ENTRY(entry) ((entry & 3) == 1) +#define S5P_FAULT_LV1_ENTRY(entry) (((entry & 3) == 0) || (entry & 3) == 3) + +#define S5P_LPAGE_LV2_ENTRY(entry) ((entry & 3) == 1) +#define S5P_SPAGE_LV2_ENTRY(entry) ((entry & 2) == 2) +#define S5P_FAULT_LV2_ENTRY(entry) ((entry & 3) == 0) + +#define MAKE_FAULT_ENTRY(entry) do { entry = 0; } while (0) +#define MAKE_SECTION_ENTRY(entry, pa) do { entry = pa | 2; } while (0) +#define MAKE_SUPSECT_ENTRY(entry, pa) do { entry = pa | 0x40002; } while (0) +#define MAKE_LV2TABLE_ENTRY(entry, pa) do { entry = pa | 1; } while (0) + +#define MAKE_LPAGE_ENTRY(entry, pa) do { entry = pa | 1; } while (0) +#define MAKE_SPAGE_ENTRY(entry, pa) do { entry = pa | 3; } while (0) + +#define GET_LV2ENTRY(entry, iova) (\ + (unsigned long *)phys_to_virt(entry & S5P_LV2TABLE_MASK) +\ + ((iova & (~S5P_SECTION_MASK)) >> S5P_SPAGE_SHIFT)) + +/* slab cache for level 2 page tables */ +static struct kmem_cache *l2table_cachep; + +static inline void pgtable_flush(void *vastart, void *vaend) +{ + dmac_flush_range(vastart, vaend); + outer_flush_range(virt_to_phys(vastart), + virt_to_phys(vaend)); +} + +static int exynos_iommu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, int flags) +{ + struct exynos_iommu_domain *priv = domain->priv; + + dev_err(priv->dev, "%s occured at %p(Page table base: %p)\n", + sysmmu_fault_name[flags], (void *)iova, + (void *)(__pa(priv->pgtable))); + dev_err(priv->dev, "\t\tGenerating Kernel OOPS...\n"); + dev_err(priv->dev, "\t\tbecause it is unrecoverable.\n"); + dev_err(priv->dev, + "\t\tSet Fault handler with iommu_set_fault_handler().\n"); + dev_err(priv->dev, "\t\tto handle System MMU fault.\n"); + + BUG(); + + return 0; +} + +static int exynos_iommu_domain_init(struct iommu_domain *domain) +{ + struct exynos_iommu_domain *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL, + (S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)) >> PAGE_SHIFT); + if (!priv->pgtable) { + kfree(priv); + return -ENOMEM; + } + + memset(priv->pgtable, 0, S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)); + pgtable_flush(priv->pgtable, priv->pgtable + S5P_LV1TABLE_ENTRIES); + + spin_lock_init(&priv->lock); + + domain->priv = priv; + + iommu_set_fault_handler(domain, &exynos_iommu_fault_handler); + + return 0; +} + +static void exynos_iommu_domain_destroy(struct iommu_domain *domain) +{ + struct exynos_iommu_domain *priv = domain->priv; + + free_pages((unsigned long)priv->pgtable, + (S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)) >> PAGE_SHIFT); + + kfree(priv); + + domain->priv = NULL; +} + +static int exynos_iommu_attach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct exynos_iommu_domain *priv = domain->priv; + int ret; + + spin_lock(&priv->lock); + + priv->dev = dev; + + ret = exynos_sysmmu_enable(domain); + if (ret) + return ret; + + spin_unlock(&priv->lock); + + return 0; +} + +static void exynos_iommu_detach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct exynos_iommu_domain *priv = domain->priv; + + spin_lock(&priv->lock); + + if (priv->dev == dev) { + exynos_sysmmu_disable(domain); + priv->dev = NULL; + } + + spin_unlock(&priv->lock); +} + +static bool section_available(struct iommu_domain *domain, + unsigned long *lv1entry) +{ + struct exynos_iommu_domain *priv = domain->priv; + + if (S5P_SECTION_LV1_ENTRY(*lv1entry)) { + dev_err(priv->dev, "1MB entry alread exists at 0x%08x\n", + (lv1entry - priv->pgtable) * SZ_1M); + return false; + } + + if (S5P_PAGE_LV1_ENTRY(*lv1entry)) { + unsigned long *lv2end, *lv2base; + + lv2base = phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK); + lv2end = lv2base + S5P_LV2TABLE_ENTRIES; + while (lv2base != lv2end) { + if (!S5P_FAULT_LV2_ENTRY(*lv2base)) { + dev_err(priv->dev, "Failed to free L2 page " + "table for section mapping.\n"); + return false; + } + lv2base++; + } + + kmem_cache_free(l2table_cachep, + phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK)); + + MAKE_FAULT_ENTRY(*lv1entry); + } + + return true; +} + +static bool write_lpage(unsigned long *head_entry, unsigned long phys_addr) +{ + unsigned long *entry, *end; + + entry = head_entry; + end = entry + (1 << S5P_LPAGE_ORDER); + + while (entry != end) { + if (!S5P_FAULT_LV2_ENTRY(*entry)) + break; + + MAKE_LPAGE_ENTRY(*entry, phys_addr); + + entry++; + } + + if (entry != end) { + end = entry; + while (entry != head_entry) + MAKE_FAULT_ENTRY(*(--entry)); + + return false; + } + + return true; +} + +static int exynos_iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, int gfp_order, int prot) +{ + struct exynos_iommu_domain *priv = domain->priv; + unsigned long *start_entry, *entry, *end_entry; + int num_entry; + int ret = 0; + unsigned long flags; + + BUG_ON(priv->pgtable == NULL); + + spin_lock_irqsave(&priv->pgtablelock, flags); + + start_entry = entry = priv->pgtable + (iova >> S5P_SECTION_SHIFT); + + if (gfp_order >= S5P_SECTION_ORDER) { + BUG_ON((paddr | iova) & ~S5P_SECTION_MASK); + /* 1MiB mapping */ + + num_entry = 1 << (gfp_order - S5P_SECTION_ORDER); + end_entry = entry + num_entry; + + while (entry != end_entry) { + if (!section_available(domain, entry)) + break; + + MAKE_SECTION_ENTRY(*entry, paddr); + + paddr += S5P_SECTION_SIZE; + entry++; + } + + if (entry != end_entry) + goto mapping_error; + + pgtable_flush(start_entry, entry); + goto mapping_done; + } + + if (S5P_FAULT_LV1_ENTRY(*entry)) { + unsigned long *l2table; + + l2table = kmem_cache_zalloc(l2table_cachep, GFP_KERNEL); + if (!l2table) { + ret = -ENOMEM; + goto nomem_error; + } + + pgtable_flush(entry, entry + S5P_LV2TABLE_ENTRIES); + + MAKE_LV2TABLE_ENTRY(*entry, virt_to_phys(l2table)); + pgtable_flush(entry, entry + 1); + } + + /* 'entry' points level 2 entries, hereafter */ + entry = GET_LV2ENTRY(*entry, iova); + + start_entry = entry; + num_entry = 1 << gfp_order; + end_entry = entry + num_entry; + + if (gfp_order >= S5P_LPAGE_ORDER) { + /* large page(64KiB) mapping */ + BUG_ON((paddr | iova) & ~S5P_LPAGE_MASK); + + while (entry != end_entry) { + if (!write_lpage(entry, paddr)) { + pr_err("%s: Failed to allocate large page" + " entry.\n", __func__); + break; + } + + paddr += S5P_LPAGE_SIZE; + entry += (1 << S5P_LPAGE_ORDER); + } + + if (entry != end_entry) { + entry -= 1 << S5P_LPAGE_ORDER; + goto mapping_error; + } + } else { + /* page (4KiB) mapping */ + while (entry != end_entry && S5P_FAULT_LV2_ENTRY(*entry)) { + + MAKE_SPAGE_ENTRY(*entry, paddr); + + entry++; + paddr += S5P_SPAGE_SIZE; + } + + if (entry != end_entry) { + pr_err("%s: Failed to allocate small page entry.\n", + __func__); + goto mapping_error; + } + } + + pgtable_flush(start_entry, entry); +mapping_error: + if (entry != end_entry) { + unsigned long *current_entry = entry; + while (entry != start_entry) + MAKE_FAULT_ENTRY(*(--entry)); + pgtable_flush(start_entry, current_entry); + ret = -EADDRINUSE; + } + +nomem_error: +mapping_done: + spin_unlock_irqrestore(&priv->pgtablelock, flags); + + return 0; +} + +static int exynos_iommu_unmap(struct iommu_domain *domain, unsigned long iova, + int gfp_order) +{ + struct exynos_iommu_domain *priv = domain->priv; + unsigned long *entry; + int num_entry; + unsigned long flags; + + BUG_ON(priv->pgtable == NULL); + + spin_lock_irqsave(&priv->pgtablelock, flags); + + entry = priv->pgtable + (iova >> S5P_SECTION_SHIFT); + + if (gfp_order >= S5P_SECTION_ORDER) { + gfp_order -= S5P_SECTION_ORDER; + num_entry = 1 << (gfp_order - S5P_SECTION_ORDER); + while (num_entry--) { + if (S5P_SECTION_LV1_ENTRY(*entry)) { + MAKE_FAULT_ENTRY(*entry); + } else if (S5P_PAGE_LV1_ENTRY(*entry)) { + unsigned long *lv2beg, *lv2end; + lv2beg = phys_to_virt( + *entry & S5P_LV2TABLE_MASK); + lv2end = lv2beg + S5P_LV2TABLE_ENTRIES; + while (lv2beg != lv2end) { + MAKE_FAULT_ENTRY(*lv2beg); + lv2beg++; + } + } + entry++; + } + } else { + entry = GET_LV2ENTRY(*entry, iova); + + BUG_ON(S5P_LPAGE_LV2_ENTRY(*entry) && + (gfp_order < S5P_LPAGE_ORDER)); + + num_entry = 1 << gfp_order; + + while (num_entry--) { + MAKE_FAULT_ENTRY(*entry); + entry++; + } + } + + if (priv->dev) + exynos_sysmmu_tlb_invalidate(priv->dev); + + spin_unlock_irqrestore(&priv->pgtablelock, flags); + + return 0; +} + +static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova) +{ + struct exynos_iommu_domain *priv = domain->priv; + unsigned long *entry; + unsigned long offset; + + entry = priv->pgtable + (iova >> S5P_SECTION_SHIFT); + + if (S5P_FAULT_LV1_ENTRY(*entry)) + return 0; + + offset = iova & ~S5P_SECTION_MASK; + + if (S5P_SECTION_LV1_ENTRY(*entry)) + return (*entry & S5P_SECTION_MASK) + offset; + + entry = GET_LV2ENTRY(*entry, iova); + + if (S5P_SPAGE_LV2_ENTRY(*entry)) + return (*entry & S5P_SPAGE_MASK) + (iova & ~S5P_SPAGE_MASK); + + if (S5P_LPAGE_LV2_ENTRY(*entry)) + return (*entry & S5P_LPAGE_MASK) + (iova & ~S5P_LPAGE_MASK); + + return 0; +} + +static int exynos_iommu_domain_has_cap(struct iommu_domain *domain, + unsigned long cap) +{ + return 0; +} + +static struct iommu_ops exynos_iommu_ops = { + .domain_init = &exynos_iommu_domain_init, + .domain_destroy = &exynos_iommu_domain_destroy, + .attach_dev = &exynos_iommu_attach_device, + .detach_dev = &exynos_iommu_detach_device, + .map = &exynos_iommu_map, + .unmap = &exynos_iommu_unmap, + .iova_to_phys = &exynos_iommu_iova_to_phys, + .domain_has_cap = &exynos_iommu_domain_has_cap, +}; + +static int __init exynos_iommu_init(void) +{ + l2table_cachep = kmem_cache_create("SysMMU Lv2 Tables", + S5P_LV2TABLE_SIZE, S5P_LV2TABLE_SIZE, 0, NULL); + if (!l2table_cachep) + return -ENOMEM; + + register_iommu(&exynos_iommu_ops); + + return 0; +} +arch_initcall(exynos_iommu_init); -- 1.7.1 {.n++%ݶw{.n+{G{ayʇڙ,jfhz_(階ݢj"mG?&~iOzv^m ?I