From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Subject: [v10,1/3] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support From: Long Cheng Message-Id: <1548645916.3831.73.camel@mhfsdcap03> Date: Mon, 28 Jan 2019 11:25:16 +0800 To: Vinod Koul Cc: Randy Dunlap , Rob Herring , Mark Rutland , Ryder Lee , Sean Wang , Nicolas Boichat , Matthias Brugger , Dan Williams , Greg Kroah-Hartman , Jiri Slaby , Sean Wang , dmaengine@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, srv_heupstream@mediatek.com, Yingjoe Chen , YT Shen , Zhenbao Liu List-ID: T24gRnJpLCAyMDE5LTAxLTE4IGF0IDExOjEwICswODAwLCBMb25nIENoZW5nIHdyb3RlOgoKSGkg Vmlub2QgS291bCwKCkp1c3QgYSBnZW50bGUgcGluZyEKCnRoYW5rcy4KCj4gSW4gRE1BIGVuZ2lu ZSBmcmFtZXdvcmssIGFkZCA4MjUwIHVhcnQgZG1hIHRvIHN1cHBvcnQgTWVkaWFUZWsgdWFydC4K PiBJZiBNZWRpYVRlayB1YXJ0IGVuYWJsZWQoU0VSSUFMXzgyNTBfTVQ2NTc3KSwgYW5kIHdhbnQg dG8gaW1wcm92ZQo+IHRoZSBwZXJmb3JtYW5jZSwgY2FuIGVuYWJsZSB0aGUgZnVuY3Rpb24uCj4g Cj4gU2lnbmVkLW9mZi1ieTogTG9uZyBDaGVuZyA8bG9uZy5jaGVuZ0BtZWRpYXRlay5jb20+Cj4g LS0tCj4gIGRyaXZlcnMvZG1hL21lZGlhdGVrL0tjb25maWcgICAgICAgICAgfCAgIDExICsKPiAg ZHJpdmVycy9kbWEvbWVkaWF0ZWsvTWFrZWZpbGUgICAgICAgICB8ICAgIDEgKwo+ICBkcml2ZXJz L2RtYS9tZWRpYXRlay9tdGstdWFydC1hcGRtYS5jIHwgIDY2OSArKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysKPiAgMyBmaWxlcyBjaGFuZ2VkLCA2ODEgaW5zZXJ0aW9ucygrKQo+ICBj cmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy9kbWEvbWVkaWF0ZWsvbXRrLXVhcnQtYXBkbWEuYwo+ IAo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2RtYS9tZWRpYXRlay9LY29uZmlnIGIvZHJpdmVycy9k bWEvbWVkaWF0ZWsvS2NvbmZpZwo+IGluZGV4IDY4MGZjMDUuLmFjNDllYjYgMTAwNjQ0Cj4gLS0t IGEvZHJpdmVycy9kbWEvbWVkaWF0ZWsvS2NvbmZpZwo+ICsrKyBiL2RyaXZlcnMvZG1hL21lZGlh dGVrL0tjb25maWcKPiBAQCAtMjQsMyArMjQsMTQgQEAgY29uZmlnIE1US19DUURNQQo+ICAKPiAg CSAgVGhpcyBjb250cm9sbGVyIHByb3ZpZGVzIHRoZSBjaGFubmVscyB3aGljaCBpcyBkZWRpY2F0 ZWQgdG8KPiAgCSAgbWVtb3J5LXRvLW1lbW9yeSB0cmFuc2ZlciB0byBvZmZsb2FkIGZyb20gQ1BV Lgo+ICsKPiArY29uZmlnIE1US19VQVJUX0FQRE1BCj4gKwl0cmlzdGF0ZSAiTWVkaWFUZWsgU29D cyBBUERNQSBzdXBwb3J0IGZvciBVQVJUIgo+ICsJZGVwZW5kcyBvbiBPRiAmJiBTRVJJQUxfODI1 MF9NVDY1NzcKPiArCXNlbGVjdCBETUFfRU5HSU5FCj4gKwlzZWxlY3QgRE1BX1ZJUlRVQUxfQ0hB Tk5FTFMKPiArCWhlbHAKPiArCSAgU3VwcG9ydCBmb3IgdGhlIFVBUlQgRE1BIGVuZ2luZSBmb3Vu ZCBvbiBNZWRpYVRlayBNVEsgU29Dcy4KPiArCSAgV2hlbiBTRVJJQUxfODI1MF9NVDY1NzcgaXMg ZW5hYmxlZCwgYW5kIGlmIHlvdSB3YW50IHRvIHVzZSBETUEsCj4gKwkgIHlvdSBjYW4gZW5hYmxl IHRoZSBjb25maWcuIFRoZSBETUEgZW5naW5lIGNhbiBvbmx5IGJlIHVzZWQKPiArCSAgd2l0aCBN ZWRpYVRlayBTb0NzLgo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2RtYS9tZWRpYXRlay9NYWtlZmls ZSBiL2RyaXZlcnMvZG1hL21lZGlhdGVrL01ha2VmaWxlCj4gaW5kZXggNDFiYjM4MS4uNjFhNmQy OSAxMDA2NDQKPiAtLS0gYS9kcml2ZXJzL2RtYS9tZWRpYXRlay9NYWtlZmlsZQo+ICsrKyBiL2Ry aXZlcnMvZG1hL21lZGlhdGVrL01ha2VmaWxlCj4gQEAgLTEsMiArMSwzIEBACj4gK29iai0kKENP TkZJR19NVEtfVUFSVF9BUERNQSkgKz0gbXRrLXVhcnQtYXBkbWEubwo+ICBvYmotJChDT05GSUdf TVRLX0hTRE1BKSArPSBtdGstaHNkbWEubwo+ICBvYmotJChDT05GSUdfTVRLX0NRRE1BKSArPSBt dGstY3FkbWEubwo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2RtYS9tZWRpYXRlay9tdGstdWFydC1h cGRtYS5jIGIvZHJpdmVycy9kbWEvbWVkaWF0ZWsvbXRrLXVhcnQtYXBkbWEuYwo+IG5ldyBmaWxl IG1vZGUgMTAwNjQ0Cj4gaW5kZXggMDAwMDAwMC4uNDI3ZGI2OQo+IC0tLSAvZGV2L251bGwKPiAr KysgYi9kcml2ZXJzL2RtYS9tZWRpYXRlay9tdGstdWFydC1hcGRtYS5jCj4gQEAgLTAsMCArMSw2 NjkgQEAKPiArLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEdQTC0yLjAKPiArLyoKPiArICog TWVkaWFUZWsgVWFydCBBUERNQSBkcml2ZXIuCj4gKyAqCj4gKyAqIENvcHlyaWdodCAoYykgMjAx OCBNZWRpYVRlayBJbmMuCj4gKyAqIEF1dGhvcjogTG9uZyBDaGVuZyA8bG9uZy5jaGVuZ0BtZWRp YXRlay5jb20+Cj4gKyAqLwo+ICsKPiArI2luY2x1ZGUgPGxpbnV4L2Nsay5oPgo+ICsjaW5jbHVk ZSA8bGludXgvZG1hZW5naW5lLmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9kbWEtbWFwcGluZy5oPgo+ ICsjaW5jbHVkZSA8bGludXgvZXJyLmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9pbml0Lmg+Cj4gKyNp bmNsdWRlIDxsaW51eC9pbnRlcnJ1cHQuaD4KPiArI2luY2x1ZGUgPGxpbnV4L2lvcG9sbC5oPgo+ ICsjaW5jbHVkZSA8bGludXgva2VybmVsLmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9saXN0Lmg+Cj4g KyNpbmNsdWRlIDxsaW51eC9tb2R1bGUuaD4KPiArI2luY2x1ZGUgPGxpbnV4L29mX2RldmljZS5o Pgo+ICsjaW5jbHVkZSA8bGludXgvb2ZfZG1hLmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9wbGF0Zm9y bV9kZXZpY2UuaD4KPiArI2luY2x1ZGUgPGxpbnV4L3BtX3J1bnRpbWUuaD4KPiArI2luY2x1ZGUg PGxpbnV4L3NsYWIuaD4KPiArI2luY2x1ZGUgPGxpbnV4L3NwaW5sb2NrLmg+Cj4gKwo+ICsjaW5j bHVkZSAiLi4vdmlydC1kbWEuaCIKPiArCj4gKy8qIFRoZSBkZWZhdWx0IG51bWJlciBvZiB2aXJ0 dWFsIGNoYW5uZWwgKi8KPiArI2RlZmluZSBNVEtfVUFSVF9BUERNQV9OUl9WQ0hBTlMJOAo+ICsK PiArI2RlZmluZSBWRkZfRU5fQgkJQklUKDApCj4gKyNkZWZpbmUgVkZGX1NUT1BfQgkJQklUKDAp Cj4gKyNkZWZpbmUgVkZGX0ZMVVNIX0IJCUJJVCgwKQo+ICsjZGVmaW5lIFZGRl80R19TVVBQT1JU X0IJQklUKDApCj4gKyNkZWZpbmUgVkZGX1JYX0lOVF9FTjBfQglCSVQoMCkJLyogcnggdmFsaWQg c2l6ZSA+PSAgdmZmIHRocmUgKi8KPiArI2RlZmluZSBWRkZfUlhfSU5UX0VOMV9CCUJJVCgxKQo+ ICsjZGVmaW5lIFZGRl9UWF9JTlRfRU5fQgkJQklUKDApCS8qIHR4IGxlZnQgc2l6ZSA+PSB2ZmYg dGhyZSAqLwo+ICsjZGVmaW5lIFZGRl9XQVJNX1JTVF9CCQlCSVQoMCkKPiArI2RlZmluZSBWRkZf UlhfSU5UX0NMUl9CCShCSVQoMCkgfCBCSVQoMSkpCj4gKyNkZWZpbmUgVkZGX1RYX0lOVF9DTFJf QgkwCj4gKyNkZWZpbmUgVkZGX1NUT1BfQ0xSX0IJCTAKPiArI2RlZmluZSBWRkZfSU5UX0VOX0NM Ul9CCTAKPiArI2RlZmluZSBWRkZfNEdfU1VQUE9SVF9DTFJfQgkwCj4gKwo+ICsvKiBpbnRlcnJ1 cHQgdHJpZ2dlciBsZXZlbCBmb3IgdHggKi8KPiArI2RlZmluZSBWRkZfVFhfVEhSRShuKQkJKChu KSAqIDcgLyA4KQo+ICsvKiBpbnRlcnJ1cHQgdHJpZ2dlciBsZXZlbCBmb3IgcnggKi8KPiArI2Rl ZmluZSBWRkZfUlhfVEhSRShuKQkJKChuKSAqIDMgLyA0KQo+ICsKPiArI2RlZmluZSBWRkZfUklO R19TSVpFCTB4ZmZmZlUKPiArLyogaW52ZXJ0IHRoaXMgYml0IHdoZW4gd3JhcCByaW5nIGhlYWQg YWdhaW4gKi8KPiArI2RlZmluZSBWRkZfUklOR19XUkFQCTB4MTAwMDBVCj4gKwo+ICsjZGVmaW5l IFZGRl9JTlRfRkxBRwkJMHgwMAo+ICsjZGVmaW5lIFZGRl9JTlRfRU4JCTB4MDQKPiArI2RlZmlu ZSBWRkZfRU4JCQkweDA4Cj4gKyNkZWZpbmUgVkZGX1JTVAkJCTB4MGMKPiArI2RlZmluZSBWRkZf U1RPUAkJMHgxMAo+ICsjZGVmaW5lIFZGRl9GTFVTSAkJMHgxNAo+ICsjZGVmaW5lIFZGRl9BRERS CQkweDFjCj4gKyNkZWZpbmUgVkZGX0xFTgkJCTB4MjQKPiArI2RlZmluZSBWRkZfVEhSRQkJMHgy OAo+ICsjZGVmaW5lIFZGRl9XUFQJCQkweDJjCj4gKyNkZWZpbmUgVkZGX1JQVAkJCTB4MzAKPiAr LyogVFg6IHRoZSBidWZmZXIgc2l6ZSBIVyBjYW4gcmVhZC4gUlg6IHRoZSBidWZmZXIgc2l6ZSBT VyBjYW4gcmVhZC4gKi8KPiArI2RlZmluZSBWRkZfVkFMSURfU0laRQkJMHgzYwo+ICsvKiBUWDog dGhlIGJ1ZmZlciBzaXplIFNXIGNhbiB3cml0ZS4gUlg6IHRoZSBidWZmZXIgc2l6ZSBIVyBjYW4g d3JpdGUuICovCj4gKyNkZWZpbmUgVkZGX0xFRlRfU0laRQkJMHg0MAo+ICsjZGVmaW5lIFZGRl9E RUJVR19TVEFUVVMJMHg1MAo+ICsjZGVmaW5lIFZGRl80R19TVVBQT1JUCQkweDU0Cj4gKwo+ICtz dHJ1Y3QgbXRrX3VhcnRfYXBkbWFkZXYgewo+ICsJc3RydWN0IGRtYV9kZXZpY2UgZGRldjsKPiAr CXN0cnVjdCBjbGsgKmNsazsKPiArCWJvb2wgc3VwcG9ydF8zM2JpdHM7Cj4gKwl1bnNpZ25lZCBp bnQgZG1hX3JlcXVlc3RzOwo+ICsJdW5zaWduZWQgaW50ICpkbWFfaXJxOwo+ICt9Owo+ICsKPiAr c3RydWN0IG10a191YXJ0X2FwZG1hX2Rlc2Mgewo+ICsJc3RydWN0IHZpcnRfZG1hX2Rlc2MgdmQ7 Cj4gKwo+ICsJdW5zaWduZWQgaW50IGF2YWlsX2xlbjsKPiArfTsKPiArCj4gK3N0cnVjdCBtdGtf Y2hhbiB7Cj4gKwlzdHJ1Y3QgdmlydF9kbWFfY2hhbiB2YzsKPiArCXN0cnVjdCBkbWFfc2xhdmVf Y29uZmlnCWNmZzsKPiArCXZvaWQgX19pb21lbSAqYmFzZTsKPiArCXN0cnVjdCBtdGtfdWFydF9h cGRtYV9kZXNjICpkZXNjOwo+ICsKPiArCWVudW0gZG1hX3RyYW5zZmVyX2RpcmVjdGlvbiBkaXI7 Cj4gKwo+ICsJYm9vbCByZXF1ZXN0ZWQ7Cj4gKwo+ICsJdW5zaWduZWQgaW50IHJ4X3N0YXR1czsK PiArfTsKPiArCj4gK3N0YXRpYyBpbmxpbmUgc3RydWN0IG10a191YXJ0X2FwZG1hZGV2ICoKPiAr dG9fbXRrX3VhcnRfYXBkbWFfZGV2KHN0cnVjdCBkbWFfZGV2aWNlICpkKQo+ICt7Cj4gKwlyZXR1 cm4gY29udGFpbmVyX29mKGQsIHN0cnVjdCBtdGtfdWFydF9hcGRtYWRldiwgZGRldik7Cj4gK30K PiArCj4gK3N0YXRpYyBpbmxpbmUgc3RydWN0IG10a19jaGFuICp0b19tdGtfdWFydF9hcGRtYV9j aGFuKHN0cnVjdCBkbWFfY2hhbiAqYykKPiArewo+ICsJcmV0dXJuIGNvbnRhaW5lcl9vZihjLCBz dHJ1Y3QgbXRrX2NoYW4sIHZjLmNoYW4pOwo+ICt9Cj4gKwo+ICtzdGF0aWMgaW5saW5lIHN0cnVj dCBtdGtfdWFydF9hcGRtYV9kZXNjICp0b19tdGtfdWFydF9hcGRtYV9kZXNjCj4gKwkoc3RydWN0 IGRtYV9hc3luY190eF9kZXNjcmlwdG9yICp0KQo+ICt7Cj4gKwlyZXR1cm4gY29udGFpbmVyX29m KHQsIHN0cnVjdCBtdGtfdWFydF9hcGRtYV9kZXNjLCB2ZC50eCk7Cj4gK30KPiArCj4gK3N0YXRp YyB2b2lkIG10a191YXJ0X2FwZG1hX3dyaXRlKHN0cnVjdCBtdGtfY2hhbiAqYywKPiArCQkJICAg ICAgIHVuc2lnbmVkIGludCByZWcsIHVuc2lnbmVkIGludCB2YWwpCj4gK3sKPiArCXdyaXRlbCh2 YWwsIGMtPmJhc2UgKyByZWcpOwo+ICt9Cj4gKwo+ICtzdGF0aWMgdW5zaWduZWQgaW50IG10a191 YXJ0X2FwZG1hX3JlYWQoc3RydWN0IG10a19jaGFuICpjLCB1bnNpZ25lZCBpbnQgcmVnKQo+ICt7 Cj4gKwlyZXR1cm4gcmVhZGwoYy0+YmFzZSArIHJlZyk7Cj4gK30KPiArCj4gK3N0YXRpYyB2b2lk IG10a191YXJ0X2FwZG1hX2Rlc2NfZnJlZShzdHJ1Y3QgdmlydF9kbWFfZGVzYyAqdmQpCj4gK3sK PiArCXN0cnVjdCBkbWFfY2hhbiAqY2hhbiA9IHZkLT50eC5jaGFuOwo+ICsJc3RydWN0IG10a19j aGFuICpjID0gdG9fbXRrX3VhcnRfYXBkbWFfY2hhbihjaGFuKTsKPiArCj4gKwlrZnJlZShjLT5k ZXNjKTsKPiArfQo+ICsKPiArc3RhdGljIHZvaWQgbXRrX3VhcnRfYXBkbWFfc3RhcnRfdHgoc3Ry dWN0IG10a19jaGFuICpjKQo+ICt7Cj4gKwl1bnNpZ25lZCBpbnQgbGVuLCBzZW5kLCBsZWZ0LCB3 cHQsIGRfd3B0LCB0bXA7Cj4gKwlpbnQgcmV0Owo+ICsKPiArCWxlZnQgPSBtdGtfdWFydF9hcGRt YV9yZWFkKGMsIFZGRl9MRUZUX1NJWkUpOwo+ICsJaWYgKCFsZWZ0KSB7Cj4gKwkJbXRrX3VhcnRf YXBkbWFfd3JpdGUoYywgVkZGX0lOVF9FTiwgVkZGX1RYX0lOVF9FTl9CKTsKPiArCQlyZXR1cm47 Cj4gKwl9Cj4gKwo+ICsJLyogV2FpdCAxc2VjIGZvciBmbHVzaCwgY2FuJ3Qgc2xlZXAgKi8KPiAr CXJldCA9IHJlYWR4X3BvbGxfdGltZW91dChyZWFkbCwgYy0+YmFzZSArIFZGRl9GTFVTSCwgdG1w LAo+ICsJCQl0bXAgIT0gVkZGX0ZMVVNIX0IsIDAsIDEwMDAwMDApOwo+ICsJaWYgKHJldCkKPiAr CQlkZXZfd2FybihjLT52Yy5jaGFuLmRldmljZS0+ZGV2LCAidHg6IGZhaWwsIGRlYnVnPTB4JXhc biIsCj4gKwkJCW10a191YXJ0X2FwZG1hX3JlYWQoYywgVkZGX0RFQlVHX1NUQVRVUykpOwo+ICsK PiArCXNlbmQgPSBtaW5fdCh1bnNpZ25lZCBpbnQsIGxlZnQsIGMtPmRlc2MtPmF2YWlsX2xlbik7 Cj4gKwl3cHQgPSBtdGtfdWFydF9hcGRtYV9yZWFkKGMsIFZGRl9XUFQpOwo+ICsJbGVuID0gbXRr X3VhcnRfYXBkbWFfcmVhZChjLCBWRkZfTEVOKTsKPiArCj4gKwlkX3dwdCA9IHdwdCArIHNlbmQ7 Cj4gKwlpZiAoKGRfd3B0ICYgVkZGX1JJTkdfU0laRSkgPj0gbGVuKSB7Cj4gKwkJZF93cHQgPSBk X3dwdCAtIGxlbjsKPiArCQlkX3dwdCA9IGRfd3B0IF4gVkZGX1JJTkdfV1JBUDsKPiArCX0KPiAr CW10a191YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9XUFQsIGRfd3B0KTsKPiArCj4gKwljLT5kZXNj LT5hdmFpbF9sZW4gLT0gc2VuZDsKPiArCj4gKwltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZf SU5UX0VOLCBWRkZfVFhfSU5UX0VOX0IpOwo+ICsJaWYgKG10a191YXJ0X2FwZG1hX3JlYWQoYywg VkZGX0ZMVVNIKSA9PSAwVSkKPiArCQltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfRkxVU0gs IFZGRl9GTFVTSF9CKTsKPiArfQo+ICsKPiArc3RhdGljIHZvaWQgbXRrX3VhcnRfYXBkbWFfc3Rh cnRfcngoc3RydWN0IG10a19jaGFuICpjKQo+ICt7Cj4gKwlzdHJ1Y3QgbXRrX3VhcnRfYXBkbWFf ZGVzYyAqZCA9IGMtPmRlc2M7Cj4gKwl1bnNpZ25lZCBpbnQgbGVuLCB3Zywgcmc7Cj4gKwlpbnQg Y250Owo+ICsKPiArCWlmICgobXRrX3VhcnRfYXBkbWFfcmVhZChjLCBWRkZfVkFMSURfU0laRSkg PT0gMFUpIHx8Cj4gKwkJIWQgfHwgIXZjaGFuX25leHRfZGVzYygmYy0+dmMpKQo+ICsJCXJldHVy bjsKPiArCj4gKwlsZW4gPSBtdGtfdWFydF9hcGRtYV9yZWFkKGMsIFZGRl9MRU4pOwo+ICsJcmcg PSBtdGtfdWFydF9hcGRtYV9yZWFkKGMsIFZGRl9SUFQpOwo+ICsJd2cgPSBtdGtfdWFydF9hcGRt YV9yZWFkKGMsIFZGRl9XUFQpOwo+ICsJY250ID0gKHdnICYgVkZGX1JJTkdfU0laRSkgLSAocmcg JiBWRkZfUklOR19TSVpFKTsKPiArCS8qCj4gKwkgKiBUaGUgYnVmZmVyIGlzIHJpbmcgYnVmZmVy LiBJZiB3cmFwIGJpdCBkaWZmZXJlbnQsCj4gKwkgKiByZXByZXNlbnRzIHRoZSBzdGFydCBvZiB0 aGUgbmV4dCBjeWNsZSBmb3IgV1BUCj4gKwkgKi8KPiArCWlmICgocmcgXiB3ZykgJiBWRkZfUklO R19XUkFQKQo+ICsJCWNudCArPSBsZW47Cj4gKwo+ICsJYy0+cnhfc3RhdHVzID0gY250Owo+ICsJ bXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX1JQVCwgd2cpOwo+ICsKPiArCWxpc3RfZGVsKCZk LT52ZC5ub2RlKTsKPiArCXZjaGFuX2Nvb2tpZV9jb21wbGV0ZSgmZC0+dmQpOwo+ICt9Cj4gKwo+ ICtzdGF0aWMgaXJxcmV0dXJuX3QgbXRrX3VhcnRfYXBkbWFfaXJxX2hhbmRsZXIoaW50IGlycSwg dm9pZCAqZGV2X2lkKQo+ICt7Cj4gKwlzdHJ1Y3QgZG1hX2NoYW4gKmNoYW4gPSAoc3RydWN0IGRt YV9jaGFuICopZGV2X2lkOwo+ICsJc3RydWN0IG10a19jaGFuICpjID0gdG9fbXRrX3VhcnRfYXBk bWFfY2hhbihjaGFuKTsKPiArCXN0cnVjdCBtdGtfdWFydF9hcGRtYV9kZXNjICpkOwo+ICsJdW5z aWduZWQgbG9uZyBmbGFnczsKPiArCj4gKwlzcGluX2xvY2tfaXJxc2F2ZSgmYy0+dmMubG9jaywg ZmxhZ3MpOwo+ICsJaWYgKGMtPmRpciA9PSBETUFfREVWX1RPX01FTSkgewo+ICsJCW10a191YXJ0 X2FwZG1hX3dyaXRlKGMsIFZGRl9JTlRfRkxBRywgVkZGX1JYX0lOVF9DTFJfQik7Cj4gKwkJbXRr X3VhcnRfYXBkbWFfc3RhcnRfcngoYyk7Cj4gKwl9IGVsc2UgaWYgKGMtPmRpciA9PSBETUFfTUVN X1RPX0RFVikgewo+ICsJCWQgPSBjLT5kZXNjOwo+ICsKPiArCQltdGtfdWFydF9hcGRtYV93cml0 ZShjLCBWRkZfSU5UX0ZMQUcsIFZGRl9UWF9JTlRfQ0xSX0IpOwo+ICsKPiArCQlpZiAoZC0+YXZh aWxfbGVuICE9IDBVKSB7Cj4gKwkJCW10a191YXJ0X2FwZG1hX3N0YXJ0X3R4KGMpOwo+ICsJCX0g ZWxzZSB7Cj4gKwkJCWxpc3RfZGVsKCZkLT52ZC5ub2RlKTsKPiArCQkJdmNoYW5fY29va2llX2Nv bXBsZXRlKCZkLT52ZCk7Cj4gKwkJfQo+ICsJfQo+ICsJc3Bpbl91bmxvY2tfaXJxcmVzdG9yZSgm Yy0+dmMubG9jaywgZmxhZ3MpOwo+ICsKPiArCXJldHVybiBJUlFfSEFORExFRDsKPiArfQo+ICsK PiArc3RhdGljIGludCBtdGtfdWFydF9hcGRtYV9hbGxvY19jaGFuX3Jlc291cmNlcyhzdHJ1Y3Qg ZG1hX2NoYW4gKmNoYW4pCj4gK3sKPiArCXN0cnVjdCBtdGtfdWFydF9hcGRtYWRldiAqbXRrZCA9 IHRvX210a191YXJ0X2FwZG1hX2RldihjaGFuLT5kZXZpY2UpOwo+ICsJc3RydWN0IG10a19jaGFu ICpjID0gdG9fbXRrX3VhcnRfYXBkbWFfY2hhbihjaGFuKTsKPiArCXVuc2lnbmVkIGludCB0bXA7 Cj4gKwlpbnQgcmV0Owo+ICsKPiArCXBtX3J1bnRpbWVfZ2V0X3N5bmMobXRrZC0+ZGRldi5kZXYp Owo+ICsKPiArCW10a191YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9BRERSLCAwKTsKPiArCW10a191 YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9USFJFLCAwKTsKPiArCW10a191YXJ0X2FwZG1hX3dyaXRl KGMsIFZGRl9MRU4sIDApOwo+ICsJbXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX1JTVCwgVkZG X1dBUk1fUlNUX0IpOwo+ICsKPiArCXJldCA9IHJlYWR4X3BvbGxfdGltZW91dChyZWFkbCwgYy0+ YmFzZSArIFZGRl9FTiwgdG1wLCAhdG1wLCAxMCwgMTAwKTsKPiArCWlmIChyZXQpIHsKPiArCQlk ZXZfZXJyKGNoYW4tPmRldmljZS0+ZGV2LCAiZG1hIHJlc2V0OiBmYWlsLCB0aW1lb3V0XG4iKTsK PiArCQlyZXR1cm4gcmV0Owo+ICsJfQo+ICsKPiArCWlmICghYy0+cmVxdWVzdGVkKSB7Cj4gKwkJ Yy0+cmVxdWVzdGVkID0gdHJ1ZTsKPiArCQlyZXQgPSByZXF1ZXN0X2lycShtdGtkLT5kbWFfaXJx W2NoYW4tPmNoYW5faWRdLAo+ICsJCQkJICBtdGtfdWFydF9hcGRtYV9pcnFfaGFuZGxlciwgSVJR Rl9UUklHR0VSX05PTkUsCj4gKwkJCQkgIEtCVUlMRF9NT0ROQU1FLCBjaGFuKTsKPiArCQlpZiAo cmV0IDwgMCkgewo+ICsJCQlkZXZfZXJyKGNoYW4tPmRldmljZS0+ZGV2LCAiQ2FuJ3QgcmVxdWVz dCBkbWEgSVJRXG4iKTsKPiArCQkJcmV0dXJuIC1FSU5WQUw7Cj4gKwkJfQo+ICsJfQo+ICsKPiAr CWlmIChtdGtkLT5zdXBwb3J0XzMzYml0cykKPiArCQltdGtfdWFydF9hcGRtYV93cml0ZShjLCBW RkZfNEdfU1VQUE9SVCwgVkZGXzRHX1NVUFBPUlRfQ0xSX0IpOwo+ICsKPiArCXJldHVybiByZXQ7 Cj4gK30KPiArCj4gK3N0YXRpYyB2b2lkIG10a191YXJ0X2FwZG1hX2ZyZWVfY2hhbl9yZXNvdXJj ZXMoc3RydWN0IGRtYV9jaGFuICpjaGFuKQo+ICt7Cj4gKwlzdHJ1Y3QgbXRrX3VhcnRfYXBkbWFk ZXYgKm10a2QgPSB0b19tdGtfdWFydF9hcGRtYV9kZXYoY2hhbi0+ZGV2aWNlKTsKPiArCXN0cnVj dCBtdGtfY2hhbiAqYyA9IHRvX210a191YXJ0X2FwZG1hX2NoYW4oY2hhbik7Cj4gKwo+ICsJaWYg KGMtPnJlcXVlc3RlZCkgewo+ICsJCWMtPnJlcXVlc3RlZCA9IGZhbHNlOwo+ICsJCWZyZWVfaXJx KG10a2QtPmRtYV9pcnFbY2hhbi0+Y2hhbl9pZF0sIGNoYW4pOwo+ICsJfQo+ICsKPiArCXRhc2ts ZXRfa2lsbCgmYy0+dmMudGFzayk7Cj4gKwo+ICsJdmNoYW5fZnJlZV9jaGFuX3Jlc291cmNlcygm Yy0+dmMpOwo+ICsKPiArCXBtX3J1bnRpbWVfcHV0X3N5bmMobXRrZC0+ZGRldi5kZXYpOwo+ICt9 Cj4gKwo+ICtzdGF0aWMgZW51bSBkbWFfc3RhdHVzIG10a191YXJ0X2FwZG1hX3R4X3N0YXR1cyhz dHJ1Y3QgZG1hX2NoYW4gKmNoYW4sCj4gKwkJCQkJIGRtYV9jb29raWVfdCBjb29raWUsCj4gKwkJ CQkJIHN0cnVjdCBkbWFfdHhfc3RhdGUgKnR4c3RhdGUpCj4gK3sKPiArCXN0cnVjdCBtdGtfY2hh biAqYyA9IHRvX210a191YXJ0X2FwZG1hX2NoYW4oY2hhbik7Cj4gKwllbnVtIGRtYV9zdGF0dXMg cmV0Owo+ICsJdW5zaWduZWQgbG9uZyBmbGFnczsKPiArCj4gKwlpZiAoIXR4c3RhdGUpCj4gKwkJ cmV0dXJuIERNQV9FUlJPUjsKPiArCj4gKwlyZXQgPSBkbWFfY29va2llX3N0YXR1cyhjaGFuLCBj b29raWUsIHR4c3RhdGUpOwo+ICsJc3Bpbl9sb2NrX2lycXNhdmUoJmMtPnZjLmxvY2ssIGZsYWdz KTsKPiArCWlmIChyZXQgPT0gRE1BX0lOX1BST0dSRVNTKSB7Cj4gKwkJYy0+cnhfc3RhdHVzID0g bXRrX3VhcnRfYXBkbWFfcmVhZChjLCBWRkZfUlBUKSAmIFZGRl9SSU5HX1NJWkU7Cj4gKwkJZG1h X3NldF9yZXNpZHVlKHR4c3RhdGUsIGMtPnJ4X3N0YXR1cyk7Cj4gKwl9IGVsc2UgaWYgKHJldCA9 PSBETUFfQ09NUExFVEUgJiYgYy0+ZGlyID09IERNQV9ERVZfVE9fTUVNKSB7Cj4gKwkJZG1hX3Nl dF9yZXNpZHVlKHR4c3RhdGUsIGMtPnJ4X3N0YXR1cyk7Cj4gKwl9IGVsc2Ugewo+ICsJCWRtYV9z ZXRfcmVzaWR1ZSh0eHN0YXRlLCAwKTsKPiArCX0KPiArCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUo JmMtPnZjLmxvY2ssIGZsYWdzKTsKPiArCj4gKwlyZXR1cm4gcmV0Owo+ICt9Cj4gKwo+ICtzdGF0 aWMgdm9pZCBtdGtfdWFydF9hcGRtYV9jb25maWdfd3JpdGUoc3RydWN0IGRtYV9jaGFuICpjaGFu LAo+ICsJCQkgICAgICAgc3RydWN0IGRtYV9zbGF2ZV9jb25maWcgKmNmZywKPiArCQkJICAgICAg IGVudW0gZG1hX3RyYW5zZmVyX2RpcmVjdGlvbiBkaXIpCj4gK3sKPiArCXN0cnVjdCBtdGtfY2hh biAqYyA9IHRvX210a191YXJ0X2FwZG1hX2NoYW4oY2hhbik7Cj4gKwlzdHJ1Y3QgbXRrX3VhcnRf YXBkbWFkZXYgKm10a2QgPQo+ICsJCQkJdG9fbXRrX3VhcnRfYXBkbWFfZGV2KGMtPnZjLmNoYW4u ZGV2aWNlKTsKPiArCXVuc2lnbmVkIGludCB0bXA7Cj4gKwo+ICsJaWYgKG10a191YXJ0X2FwZG1h X3JlYWQoYywgVkZGX0VOKSA9PSBWRkZfRU5fQikKPiArCQlyZXR1cm47Cj4gKwo+ICsJYy0+ZGly ID0gZGlyOwo+ICsKPiArCWlmIChkaXIgPT0gRE1BX0RFVl9UT19NRU0pIHsKPiArCQl0bXAgPSBj ZmctPnNyY19hZGRyX3dpZHRoICogMTAyNDsKPiArCj4gKwkJbXRrX3VhcnRfYXBkbWFfd3JpdGUo YywgVkZGX0FERFIsIGNmZy0+c3JjX2FkZHIpOwo+ICsJCW10a191YXJ0X2FwZG1hX3dyaXRlKGMs IFZGRl9MRU4sIHRtcCk7Cj4gKwkJbXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX1RIUkUsIFZG Rl9SWF9USFJFKHRtcCkpOwo+ICsJCW10a191YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9JTlRfRU4s Cj4gKwkJCQlWRkZfUlhfSU5UX0VOMF9CIHwgVkZGX1JYX0lOVF9FTjFfQik7Cj4gKwkJbXRrX3Vh cnRfYXBkbWFfd3JpdGUoYywgVkZGX1JQVCwgMCk7Cj4gKwkJbXRrX3VhcnRfYXBkbWFfd3JpdGUo YywgVkZGX0lOVF9GTEFHLCBWRkZfUlhfSU5UX0NMUl9CKTsKPiArCX0gZWxzZSBpZiAoZGlyID09 IERNQV9NRU1fVE9fREVWKQl7Cj4gKwkJdG1wID0gY2ZnLT5kc3RfYWRkcl93aWR0aCAqIDEwMjQ7 Cj4gKwo+ICsJCW10a191YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9BRERSLCBjZmctPmRzdF9hZGRy KTsKPiArCQltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfTEVOLCB0bXApOwo+ICsJCW10a191 YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9USFJFLCBWRkZfVFhfVEhSRSh0bXApKTsKPiArCQltdGtf dWFydF9hcGRtYV93cml0ZShjLCBWRkZfV1BULCAwKTsKPiArCQltdGtfdWFydF9hcGRtYV93cml0 ZShjLCBWRkZfSU5UX0ZMQUcsIFZGRl9UWF9JTlRfQ0xSX0IpOwo+ICsJfQo+ICsKPiArCW10a191 YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9FTiwgVkZGX0VOX0IpOwo+ICsKPiArCWlmIChtdGtkLT5z dXBwb3J0XzMzYml0cykKPiArCQltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfNEdfU1VQUE9S VCwgVkZGXzRHX1NVUFBPUlRfQik7Cj4gKwo+ICsJaWYgKG10a191YXJ0X2FwZG1hX3JlYWQoYywg VkZGX0VOKSAhPSBWRkZfRU5fQikKPiArCQlkZXZfZXJyKGNoYW4tPmRldmljZS0+ZGV2LCAiZGly WyVkXSBmYWlsXG4iLCBkaXIpOwo+ICt9Cj4gKwo+ICsvKgo+ICsgKiBkbWFlbmdpbmVfcHJlcF9z bGF2ZV9zaW5nbGUgd2lsbCBjYWxsIHRoZSBmdW5jdGlvbi4gYW5kIHNnbGVuIGlzIDEuCj4gKyAq IDgyNTAgdWFydCB1c2luZyBvbmUgcmluZyBidWZmZXIsIGFuZCBkZWFsIHdpdGggb25lIHNnLgo+ ICsgKi8KPiArc3RhdGljIHN0cnVjdCBkbWFfYXN5bmNfdHhfZGVzY3JpcHRvciAqbXRrX3VhcnRf YXBkbWFfcHJlcF9zbGF2ZV9zZwo+ICsJKHN0cnVjdCBkbWFfY2hhbiAqY2hhbiwgc3RydWN0IHNj YXR0ZXJsaXN0ICpzZ2wsCj4gKwl1bnNpZ25lZCBpbnQgc2dsZW4sIGVudW0gZG1hX3RyYW5zZmVy X2RpcmVjdGlvbiBkaXIsCj4gKwl1bnNpZ25lZCBsb25nIHR4X2ZsYWdzLCB2b2lkICpjb250ZXh0 KQo+ICt7Cj4gKwlzdHJ1Y3QgbXRrX2NoYW4gKmMgPSB0b19tdGtfdWFydF9hcGRtYV9jaGFuKGNo YW4pOwo+ICsJc3RydWN0IG10a191YXJ0X2FwZG1hX2Rlc2MgKmQ7Cj4gKwo+ICsJaWYgKCFpc19z bGF2ZV9kaXJlY3Rpb24oZGlyKSkKPiArCQlyZXR1cm4gTlVMTDsKPiArCj4gKwltdGtfdWFydF9h cGRtYV9jb25maWdfd3JpdGUoY2hhbiwgJmMtPmNmZywgZGlyKTsKPiArCj4gKwkvKiBOb3cgYWxs b2NhdGUgYW5kIHNldHVwIHRoZSBkZXNjcmlwdG9yICovCj4gKwlkID0ga3phbGxvYyhzaXplb2Yo KmQpLCBHRlBfQVRPTUlDKTsKPiArCWlmICghZCkKPiArCQlyZXR1cm4gTlVMTDsKPiArCj4gKwkv KiBzZ2xlbiBpcyAxICovCj4gKwlkLT5hdmFpbF9sZW4gPSBzZ19kbWFfbGVuKHNnbCk7Cj4gKwo+ ICsJcmV0dXJuIHZjaGFuX3R4X3ByZXAoJmMtPnZjLCAmZC0+dmQsIHR4X2ZsYWdzKTsKPiArfQo+ ICsKPiArc3RhdGljIHZvaWQgbXRrX3VhcnRfYXBkbWFfaXNzdWVfcGVuZGluZyhzdHJ1Y3QgZG1h X2NoYW4gKmNoYW4pCj4gK3sKPiArCXN0cnVjdCBtdGtfY2hhbiAqYyA9IHRvX210a191YXJ0X2Fw ZG1hX2NoYW4oY2hhbik7Cj4gKwlzdHJ1Y3QgdmlydF9kbWFfZGVzYyAqdmQ7Cj4gKwl1bnNpZ25l ZCBsb25nIGZsYWdzOwo+ICsKPiArCXNwaW5fbG9ja19pcnFzYXZlKCZjLT52Yy5sb2NrLCBmbGFn cyk7Cj4gKwlpZiAodmNoYW5faXNzdWVfcGVuZGluZygmYy0+dmMpKSB7Cj4gKwkJdmQgPSB2Y2hh bl9uZXh0X2Rlc2MoJmMtPnZjKTsKPiArCQljLT5kZXNjID0gdG9fbXRrX3VhcnRfYXBkbWFfZGVz YygmdmQtPnR4KTsKPiArCX0KPiArCj4gKwlpZiAoYy0+ZGlyID09IERNQV9ERVZfVE9fTUVNKQo+ ICsJCW10a191YXJ0X2FwZG1hX3N0YXJ0X3J4KGMpOwo+ICsJZWxzZSBpZiAoYy0+ZGlyID09IERN QV9NRU1fVE9fREVWKQo+ICsJCW10a191YXJ0X2FwZG1hX3N0YXJ0X3R4KGMpOwo+ICsKPiArCXNw aW5fdW5sb2NrX2lycXJlc3RvcmUoJmMtPnZjLmxvY2ssIGZsYWdzKTsKPiArfQo+ICsKPiArc3Rh dGljIGludCBtdGtfdWFydF9hcGRtYV9zbGF2ZV9jb25maWcoc3RydWN0IGRtYV9jaGFuICpjaGFu LAo+ICsJCQkJICAgc3RydWN0IGRtYV9zbGF2ZV9jb25maWcgKmNvbmZpZykKPiArewo+ICsJc3Ry dWN0IG10a19jaGFuICpjID0gdG9fbXRrX3VhcnRfYXBkbWFfY2hhbihjaGFuKTsKPiArCj4gKwlt ZW1jcHkoJmMtPmNmZywgY29uZmlnLCBzaXplb2YoKmNvbmZpZykpOwo+ICsKPiArCXJldHVybiAw Owo+ICt9Cj4gKwo+ICtzdGF0aWMgaW50IG10a191YXJ0X2FwZG1hX3Rlcm1pbmF0ZV9hbGwoc3Ry dWN0IGRtYV9jaGFuICpjaGFuKQo+ICt7Cj4gKwlzdHJ1Y3QgbXRrX2NoYW4gKmMgPSB0b19tdGtf dWFydF9hcGRtYV9jaGFuKGNoYW4pOwo+ICsJdW5zaWduZWQgbG9uZyBmbGFnczsKPiArCXVuc2ln bmVkIGludCB0bXA7Cj4gKwlpbnQgcmV0Owo+ICsKPiArCXNwaW5fbG9ja19pcnFzYXZlKCZjLT52 Yy5sb2NrLCBmbGFncyk7Cj4gKwo+ICsJbXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX0ZMVVNI LCBWRkZfRkxVU0hfQik7Cj4gKwkvKiBXYWl0IDFzZWMgZm9yIGZsdXNoLCBjYW4ndCBzbGVlcCAq Lwo+ICsJcmV0ID0gcmVhZHhfcG9sbF90aW1lb3V0KHJlYWRsLCBjLT5iYXNlICsgVkZGX0ZMVVNI LCB0bXAsCj4gKwkJCXRtcCAhPSBWRkZfRkxVU0hfQiwgMCwgMTAwMDAwMCk7Cj4gKwlpZiAocmV0 KQo+ICsJCWRldl9lcnIoYy0+dmMuY2hhbi5kZXZpY2UtPmRldiwgImZsdXNoOiBmYWlsLCBkZWJ1 Zz0weCV4XG4iLAo+ICsJCQltdGtfdWFydF9hcGRtYV9yZWFkKGMsIFZGRl9ERUJVR19TVEFUVVMp KTsKPiArCj4gKwkvKiBzZXQgc3RvcCBhcyAxIC0+IHdhaXQgdW50aWwgZW4gaXMgMCAtPiBzZXQg c3RvcCBhcyAwICovCj4gKwltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfU1RPUCwgVkZGX1NU T1BfQik7Cj4gKwlyZXQgPSByZWFkeF9wb2xsX3RpbWVvdXQocmVhZGwsIGMtPmJhc2UgKyBWRkZf RU4sIHRtcCwgIXRtcCwgMTAsIDEwMCk7Cj4gKwlpZiAocmV0KQo+ICsJCWRldl9lcnIoYy0+dmMu Y2hhbi5kZXZpY2UtPmRldiwgInN0b3A6IGZhaWwsIGRlYnVnPTB4JXhcbiIsCj4gKwkJCW10a191 YXJ0X2FwZG1hX3JlYWQoYywgVkZGX0RFQlVHX1NUQVRVUykpOwo+ICsKPiArCW10a191YXJ0X2Fw ZG1hX3dyaXRlKGMsIFZGRl9TVE9QLCBWRkZfU1RPUF9DTFJfQik7Cj4gKwltdGtfdWFydF9hcGRt YV93cml0ZShjLCBWRkZfSU5UX0VOLCBWRkZfSU5UX0VOX0NMUl9CKTsKPiArCj4gKwlpZiAoYy0+ ZGlyID09IERNQV9ERVZfVE9fTUVNKQo+ICsJCW10a191YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9J TlRfRkxBRywgVkZGX1JYX0lOVF9DTFJfQik7Cj4gKwllbHNlIGlmIChjLT5kaXIgPT0gRE1BX01F TV9UT19ERVYpCj4gKwkJbXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX0lOVF9GTEFHLCBWRkZf VFhfSU5UX0NMUl9CKTsKPiArCj4gKwlzcGluX3VubG9ja19pcnFyZXN0b3JlKCZjLT52Yy5sb2Nr LCBmbGFncyk7Cj4gKwo+ICsJcmV0dXJuIDA7Cj4gK30KPiArCj4gK3N0YXRpYyBpbnQgbXRrX3Vh cnRfYXBkbWFfZGV2aWNlX3BhdXNlKHN0cnVjdCBkbWFfY2hhbiAqY2hhbikKPiArewo+ICsJLyog anVzdCBmb3IgY2hlY2sgY2FwcyBwYXNzICovCj4gKwlyZXR1cm4gMDsKPiArfQo+ICsKPiArc3Rh dGljIHZvaWQgbXRrX3VhcnRfYXBkbWFfZnJlZShzdHJ1Y3QgbXRrX3VhcnRfYXBkbWFkZXYgKm10 a2QpCj4gK3sKPiArCXdoaWxlICghbGlzdF9lbXB0eSgmbXRrZC0+ZGRldi5jaGFubmVscykpIHsK PiArCQlzdHJ1Y3QgbXRrX2NoYW4gKmMgPSBsaXN0X2ZpcnN0X2VudHJ5KCZtdGtkLT5kZGV2LmNo YW5uZWxzLAo+ICsJCQlzdHJ1Y3QgbXRrX2NoYW4sIHZjLmNoYW4uZGV2aWNlX25vZGUpOwo+ICsK PiArCQlsaXN0X2RlbCgmYy0+dmMuY2hhbi5kZXZpY2Vfbm9kZSk7Cj4gKwkJdGFza2xldF9raWxs KCZjLT52Yy50YXNrKTsKPiArCX0KPiArfQo+ICsKPiArc3RhdGljIGNvbnN0IHN0cnVjdCBvZl9k ZXZpY2VfaWQgbXRrX3VhcnRfYXBkbWFfbWF0Y2hbXSA9IHsKPiArCXsgLmNvbXBhdGlibGUgPSAi bWVkaWF0ZWssbXQ2NTc3LXVhcnQtZG1hIiwgfSwKPiArCXsgLyogc2VudGluZWwgKi8gfSwKPiAr fTsKPiArTU9EVUxFX0RFVklDRV9UQUJMRShvZiwgbXRrX3VhcnRfYXBkbWFfbWF0Y2gpOwo+ICsK PiArc3RhdGljIGludCBtdGtfdWFydF9hcGRtYV9wcm9iZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNl ICpwZGV2KQo+ICt7Cj4gKwlzdHJ1Y3QgZGV2aWNlX25vZGUgKm5wID0gcGRldi0+ZGV2Lm9mX25v ZGU7Cj4gKwlzdHJ1Y3QgbXRrX3VhcnRfYXBkbWFkZXYgKm10a2Q7Cj4gKwlzdHJ1Y3QgcmVzb3Vy Y2UgKnJlczsKPiArCXN0cnVjdCBtdGtfY2hhbiAqYzsKPiArCWludCBiaXRfbWFzayA9IDMyLCBy YzsKPiArCXVuc2lnbmVkIGludCBpOwo+ICsKPiArCW10a2QgPSBkZXZtX2t6YWxsb2MoJnBkZXYt PmRldiwgc2l6ZW9mKCptdGtkKSwgR0ZQX0tFUk5FTCk7Cj4gKwlpZiAoIW10a2QpCj4gKwkJcmV0 dXJuIC1FTk9NRU07Cj4gKwo+ICsJbXRrZC0+Y2xrID0gZGV2bV9jbGtfZ2V0KCZwZGV2LT5kZXYs IE5VTEwpOwo+ICsJaWYgKElTX0VSUihtdGtkLT5jbGspKSB7Cj4gKwkJZGV2X2VycigmcGRldi0+ ZGV2LCAiTm8gY2xvY2sgc3BlY2lmaWVkXG4iKTsKPiArCQlyYyA9IFBUUl9FUlIobXRrZC0+Y2xr KTsKPiArCQlyZXR1cm4gcmM7Cj4gKwl9Cj4gKwo+ICsJaWYgKG9mX3Byb3BlcnR5X3JlYWRfYm9v bChucCwgImRtYS0zM2JpdHMiKSkKPiArCQltdGtkLT5zdXBwb3J0XzMzYml0cyA9IHRydWU7Cj4g Kwo+ICsJaWYgKG10a2QtPnN1cHBvcnRfMzNiaXRzKQo+ICsJCWJpdF9tYXNrID0gMzM7Cj4gKwo+ ICsJcmMgPSBkbWFfc2V0X21hc2tfYW5kX2NvaGVyZW50KCZwZGV2LT5kZXYsIERNQV9CSVRfTUFT SyhiaXRfbWFzaykpOwo+ICsJaWYgKHJjKQo+ICsJCXJldHVybiByYzsKPiArCj4gKwlkbWFfY2Fw X3NldChETUFfU0xBVkUsIG10a2QtPmRkZXYuY2FwX21hc2spOwo+ICsJbXRrZC0+ZGRldi5kZXZp Y2VfYWxsb2NfY2hhbl9yZXNvdXJjZXMgPQo+ICsJCQkJbXRrX3VhcnRfYXBkbWFfYWxsb2NfY2hh bl9yZXNvdXJjZXM7Cj4gKwltdGtkLT5kZGV2LmRldmljZV9mcmVlX2NoYW5fcmVzb3VyY2VzID0K PiArCQkJCW10a191YXJ0X2FwZG1hX2ZyZWVfY2hhbl9yZXNvdXJjZXM7Cj4gKwltdGtkLT5kZGV2 LmRldmljZV90eF9zdGF0dXMgPSBtdGtfdWFydF9hcGRtYV90eF9zdGF0dXM7Cj4gKwltdGtkLT5k ZGV2LmRldmljZV9pc3N1ZV9wZW5kaW5nID0gbXRrX3VhcnRfYXBkbWFfaXNzdWVfcGVuZGluZzsK PiArCW10a2QtPmRkZXYuZGV2aWNlX3ByZXBfc2xhdmVfc2cgPSBtdGtfdWFydF9hcGRtYV9wcmVw X3NsYXZlX3NnOwo+ICsJbXRrZC0+ZGRldi5kZXZpY2VfY29uZmlnID0gbXRrX3VhcnRfYXBkbWFf c2xhdmVfY29uZmlnOwo+ICsJbXRrZC0+ZGRldi5kZXZpY2VfcGF1c2UgPSBtdGtfdWFydF9hcGRt YV9kZXZpY2VfcGF1c2U7Cj4gKwltdGtkLT5kZGV2LmRldmljZV90ZXJtaW5hdGVfYWxsID0gbXRr X3VhcnRfYXBkbWFfdGVybWluYXRlX2FsbDsKPiArCW10a2QtPmRkZXYuc3JjX2FkZHJfd2lkdGhz ID0gQklUKERNQV9TTEFWRV9CVVNXSURUSF8xX0JZVEUpOwo+ICsJbXRrZC0+ZGRldi5kc3RfYWRk cl93aWR0aHMgPSBCSVQoRE1BX1NMQVZFX0JVU1dJRFRIXzFfQllURSk7Cj4gKwltdGtkLT5kZGV2 LmRpcmVjdGlvbnMgPSBCSVQoRE1BX0RFVl9UT19NRU0pIHwgQklUKERNQV9NRU1fVE9fREVWKTsK PiArCW10a2QtPmRkZXYucmVzaWR1ZV9ncmFudWxhcml0eSA9IERNQV9SRVNJRFVFX0dSQU5VTEFS SVRZX1NFR01FTlQ7Cj4gKwltdGtkLT5kZGV2LmRldiA9ICZwZGV2LT5kZXY7Cj4gKwlJTklUX0xJ U1RfSEVBRCgmbXRrZC0+ZGRldi5jaGFubmVscyk7Cj4gKwo+ICsJbXRrZC0+ZG1hX3JlcXVlc3Rz ID0gTVRLX1VBUlRfQVBETUFfTlJfVkNIQU5TOwo+ICsJaWYgKG9mX3Byb3BlcnR5X3JlYWRfdTMy KG5wLCAiZG1hLXJlcXVlc3RzIiwgJm10a2QtPmRtYV9yZXF1ZXN0cykpIHsKPiArCQlkZXZfaW5m bygmcGRldi0+ZGV2LAo+ICsJCQkgIlVzaW5nICV1IGFzIG1pc3NpbmcgZG1hLXJlcXVlc3RzIHBy b3BlcnR5XG4iLAo+ICsJCQkgTVRLX1VBUlRfQVBETUFfTlJfVkNIQU5TKTsKPiArCX0KPiArCj4g KwltdGtkLT5kbWFfaXJxID0gZGV2bV9rY2FsbG9jKCZwZGV2LT5kZXYsIG10a2QtPmRtYV9yZXF1 ZXN0cywKPiArCQkJCSBzaXplb2YoKm10a2QtPmRtYV9pcnEpLCBHRlBfS0VSTkVMKTsKPiArCWlm ICghbXRrZC0+ZG1hX2lycSkKPiArCQlyZXR1cm4gLUVOT01FTTsKPiArCj4gKwlmb3IgKGkgPSAw OyBpIDwgbXRrZC0+ZG1hX3JlcXVlc3RzOyBpKyspIHsKPiArCQljID0gZGV2bV9remFsbG9jKG10 a2QtPmRkZXYuZGV2LCBzaXplb2YoKmMpLCBHRlBfS0VSTkVMKTsKPiArCQlpZiAoIWMpIHsKPiAr CQkJcmMgPSAtRU5PREVWOwo+ICsJCQlnb3RvIGVycl9ub19kbWE7Cj4gKwkJfQo+ICsKPiArCQly ZXMgPSBwbGF0Zm9ybV9nZXRfcmVzb3VyY2UocGRldiwgSU9SRVNPVVJDRV9NRU0sIGkpOwo+ICsJ CWlmICghcmVzKSB7Cj4gKwkJCXJjID0gLUVOT0RFVjsKPiArCQkJZ290byBlcnJfbm9fZG1hOwo+ ICsJCX0KPiArCj4gKwkJYy0+YmFzZSA9IGRldm1faW9yZW1hcF9yZXNvdXJjZSgmcGRldi0+ZGV2 LCByZXMpOwo+ICsJCWlmIChJU19FUlIoYy0+YmFzZSkpIHsKPiArCQkJcmMgPSBQVFJfRVJSKGMt PmJhc2UpOwo+ICsJCQlnb3RvIGVycl9ub19kbWE7Cj4gKwkJfQo+ICsJCWMtPnJlcXVlc3RlZCA9 IGZhbHNlOwo+ICsJCWMtPnZjLmRlc2NfZnJlZSA9IG10a191YXJ0X2FwZG1hX2Rlc2NfZnJlZTsK PiArCQl2Y2hhbl9pbml0KCZjLT52YywgJm10a2QtPmRkZXYpOwo+ICsKPiArCQltdGtkLT5kbWFf aXJxW2ldID0gcGxhdGZvcm1fZ2V0X2lycShwZGV2LCBpKTsKPiArCQlpZiAoKGludCltdGtkLT5k bWFfaXJxW2ldIDwgMCkgewo+ICsJCQlkZXZfZXJyKCZwZGV2LT5kZXYsICJmYWlsZWQgdG8gZ2V0 IElSUVslZF1cbiIsIGkpOwo+ICsJCQlyYyA9IC1FSU5WQUw7Cj4gKwkJCWdvdG8gZXJyX25vX2Rt YTsKPiArCQl9Cj4gKwl9Cj4gKwo+ICsJcG1fcnVudGltZV9lbmFibGUoJnBkZXYtPmRldik7Cj4g KwlwbV9ydW50aW1lX3NldF9hY3RpdmUoJnBkZXYtPmRldik7Cj4gKwo+ICsJcmMgPSBkbWFfYXN5 bmNfZGV2aWNlX3JlZ2lzdGVyKCZtdGtkLT5kZGV2KTsKPiArCWlmIChyYykKPiArCQlnb3RvIHJw bV9kaXNhYmxlOwo+ICsKPiArCXBsYXRmb3JtX3NldF9kcnZkYXRhKHBkZXYsIG10a2QpOwo+ICsK PiArCS8qIERldmljZS10cmVlIERNQSBjb250cm9sbGVyIHJlZ2lzdHJhdGlvbiAqLwo+ICsJcmMg PSBvZl9kbWFfY29udHJvbGxlcl9yZWdpc3RlcihucCwgb2ZfZG1hX3hsYXRlX2J5X2NoYW5faWQs IG10a2QpOwo+ICsJaWYgKHJjKQo+ICsJCWdvdG8gZG1hX3JlbW92ZTsKPiArCj4gKwlyZXR1cm4g cmM7Cj4gKwo+ICtkbWFfcmVtb3ZlOgo+ICsJZG1hX2FzeW5jX2RldmljZV91bnJlZ2lzdGVyKCZt dGtkLT5kZGV2KTsKPiArcnBtX2Rpc2FibGU6Cj4gKwlwbV9ydW50aW1lX2Rpc2FibGUoJnBkZXYt PmRldik7Cj4gK2Vycl9ub19kbWE6Cj4gKwltdGtfdWFydF9hcGRtYV9mcmVlKG10a2QpOwo+ICsJ cmV0dXJuIHJjOwo+ICt9Cj4gKwo+ICtzdGF0aWMgaW50IG10a191YXJ0X2FwZG1hX3JlbW92ZShz dHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQo+ICt7Cj4gKwlzdHJ1Y3QgbXRrX3VhcnRfYXBk bWFkZXYgKm10a2QgPSBwbGF0Zm9ybV9nZXRfZHJ2ZGF0YShwZGV2KTsKPiArCj4gKwlpZiAocGRl di0+ZGV2Lm9mX25vZGUpCj4gKwkJb2ZfZG1hX2NvbnRyb2xsZXJfZnJlZShwZGV2LT5kZXYub2Zf bm9kZSk7Cj4gKwo+ICsJcG1fcnVudGltZV9kaXNhYmxlKCZwZGV2LT5kZXYpOwo+ICsJcG1fcnVu dGltZV9wdXRfbm9pZGxlKCZwZGV2LT5kZXYpOwo+ICsKPiArCWRtYV9hc3luY19kZXZpY2VfdW5y ZWdpc3RlcigmbXRrZC0+ZGRldik7Cj4gKwltdGtfdWFydF9hcGRtYV9mcmVlKG10a2QpOwo+ICsK PiArCXJldHVybiAwOwo+ICt9Cj4gKwo+ICsjaWZkZWYgQ09ORklHX1BNX1NMRUVQCj4gK3N0YXRp YyBpbnQgbXRrX3VhcnRfYXBkbWFfc3VzcGVuZChzdHJ1Y3QgZGV2aWNlICpkZXYpCj4gK3sKPiAr CXN0cnVjdCBtdGtfdWFydF9hcGRtYWRldiAqbXRrZCA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOwo+ ICsKPiArCWlmICghcG1fcnVudGltZV9zdXNwZW5kZWQoZGV2KSkKPiArCQljbGtfZGlzYWJsZV91 bnByZXBhcmUobXRrZC0+Y2xrKTsKPiArCj4gKwlyZXR1cm4gMDsKPiArfQo+ICsKPiArc3RhdGlj IGludCBtdGtfdWFydF9hcGRtYV9yZXN1bWUoc3RydWN0IGRldmljZSAqZGV2KQo+ICt7Cj4gKwlp bnQgcmV0Owo+ICsJc3RydWN0IG10a191YXJ0X2FwZG1hZGV2ICptdGtkID0gZGV2X2dldF9kcnZk YXRhKGRldik7Cj4gKwo+ICsJaWYgKCFwbV9ydW50aW1lX3N1c3BlbmRlZChkZXYpKSB7Cj4gKwkJ cmV0ID0gY2xrX3ByZXBhcmVfZW5hYmxlKG10a2QtPmNsayk7Cj4gKwkJaWYgKHJldCkKPiArCQkJ cmV0dXJuIHJldDsKPiArCX0KPiArCj4gKwlyZXR1cm4gMDsKPiArfQo+ICsjZW5kaWYgLyogQ09O RklHX1BNX1NMRUVQICovCj4gKwo+ICsjaWZkZWYgQ09ORklHX1BNCj4gK3N0YXRpYyBpbnQgbXRr X3VhcnRfYXBkbWFfcnVudGltZV9zdXNwZW5kKHN0cnVjdCBkZXZpY2UgKmRldikKPiArewo+ICsJ c3RydWN0IG10a191YXJ0X2FwZG1hZGV2ICptdGtkID0gZGV2X2dldF9kcnZkYXRhKGRldik7Cj4g Kwo+ICsJY2xrX2Rpc2FibGVfdW5wcmVwYXJlKG10a2QtPmNsayk7Cj4gKwo+ICsJcmV0dXJuIDA7 Cj4gK30KPiArCj4gK3N0YXRpYyBpbnQgbXRrX3VhcnRfYXBkbWFfcnVudGltZV9yZXN1bWUoc3Ry dWN0IGRldmljZSAqZGV2KQo+ICt7Cj4gKwlpbnQgcmV0Owo+ICsJc3RydWN0IG10a191YXJ0X2Fw ZG1hZGV2ICptdGtkID0gZGV2X2dldF9kcnZkYXRhKGRldik7Cj4gKwo+ICsJcmV0ID0gY2xrX3By ZXBhcmVfZW5hYmxlKG10a2QtPmNsayk7Cj4gKwlpZiAocmV0KQo+ICsJCXJldHVybiByZXQ7Cj4g Kwo+ICsJcmV0dXJuIDA7Cj4gK30KPiArI2VuZGlmIC8qIENPTkZJR19QTSAqLwo+ICsKPiArc3Rh dGljIGNvbnN0IHN0cnVjdCBkZXZfcG1fb3BzIG10a191YXJ0X2FwZG1hX3BtX29wcyA9IHsKPiAr CVNFVF9TWVNURU1fU0xFRVBfUE1fT1BTKG10a191YXJ0X2FwZG1hX3N1c3BlbmQsIG10a191YXJ0 X2FwZG1hX3Jlc3VtZSkKPiArCVNFVF9SVU5USU1FX1BNX09QUyhtdGtfdWFydF9hcGRtYV9ydW50 aW1lX3N1c3BlbmQsCj4gKwkJCSAgIG10a191YXJ0X2FwZG1hX3J1bnRpbWVfcmVzdW1lLCBOVUxM KQo+ICt9Owo+ICsKPiArc3RhdGljIHN0cnVjdCBwbGF0Zm9ybV9kcml2ZXIgbXRrX3VhcnRfYXBk bWFfZHJpdmVyID0gewo+ICsJLnByb2JlCT0gbXRrX3VhcnRfYXBkbWFfcHJvYmUsCj4gKwkucmVt b3ZlCT0gbXRrX3VhcnRfYXBkbWFfcmVtb3ZlLAo+ICsJLmRyaXZlciA9IHsKPiArCQkubmFtZQkJ PSBLQlVJTERfTU9ETkFNRSwKPiArCQkucG0JCT0gJm10a191YXJ0X2FwZG1hX3BtX29wcywKPiAr CQkub2ZfbWF0Y2hfdGFibGUgPSBvZl9tYXRjaF9wdHIobXRrX3VhcnRfYXBkbWFfbWF0Y2gpLAo+ ICsJfSwKPiArfTsKPiArCj4gK21vZHVsZV9wbGF0Zm9ybV9kcml2ZXIobXRrX3VhcnRfYXBkbWFf ZHJpdmVyKTsKPiArCj4gK01PRFVMRV9ERVNDUklQVElPTigiTWVkaWFUZWsgVUFSVCBBUERNQSBD b250cm9sbGVyIERyaXZlciIpOwo+ICtNT0RVTEVfQVVUSE9SKCJMb25nIENoZW5nIDxsb25nLmNo ZW5nQG1lZGlhdGVrLmNvbT4iKTsKPiArTU9EVUxFX0xJQ0VOU0UoIkdQTCB2MiIpOwo+ICsK From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.0 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_PASS,UNPARSEABLE_RELAY, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AF3DAC282CA for ; Mon, 28 Jan 2019 03:25:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6298E2148E for ; Mon, 28 Jan 2019 03:25:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726682AbfA1DZf (ORCPT ); Sun, 27 Jan 2019 22:25:35 -0500 Received: from mailgw01.mediatek.com ([210.61.82.183]:4067 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1726604AbfA1DZf (ORCPT ); Sun, 27 Jan 2019 22:25:35 -0500 X-UUID: 2adea03ca632484c91f245576ac1b5da-20190128 X-UUID: 2adea03ca632484c91f245576ac1b5da-20190128 Received: from mtkcas07.mediatek.inc [(172.21.101.84)] by mailgw01.mediatek.com (envelope-from ) (mhqrelay.mediatek.com ESMTP with TLS) with ESMTP id 1812562220; Mon, 28 Jan 2019 11:25:20 +0800 Received: from MTKCAS32.mediatek.inc (172.27.4.184) by mtkmbs01n1.mediatek.inc (172.21.101.68) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 28 Jan 2019 11:25:18 +0800 Received: from [10.17.3.153] (10.17.3.153) by MTKCAS32.mediatek.inc (172.27.4.170) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Mon, 28 Jan 2019 11:25:17 +0800 Message-ID: <1548645916.3831.73.camel@mhfsdcap03> Subject: Re: [PATCH v10 1/3] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support From: Long Cheng To: Vinod Koul CC: Randy Dunlap , Rob Herring , Mark Rutland , Ryder Lee , Sean Wang , Nicolas Boichat , Matthias Brugger , Dan Williams , Greg Kroah-Hartman , Jiri Slaby , Sean Wang , , , , , , , , Yingjoe Chen , YT Shen , Zhenbao Liu Date: Mon, 28 Jan 2019 11:25:16 +0800 In-Reply-To: <1547781016-890-2-git-send-email-long.cheng@mediatek.com> References: <1547781016-890-1-git-send-email-long.cheng@mediatek.com> <1547781016-890-2-git-send-email-long.cheng@mediatek.com> Content-Type: text/plain; charset="UTF-8" X-Mailer: Evolution 3.2.3-0ubuntu6 Content-Transfer-Encoding: 7bit MIME-Version: 1.0 X-MTK: N Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Fri, 2019-01-18 at 11:10 +0800, Long Cheng wrote: Hi Vinod Koul, Just a gentle ping! thanks. > In DMA engine framework, add 8250 uart dma to support MediaTek uart. > If MediaTek uart enabled(SERIAL_8250_MT6577), and want to improve > the performance, can enable the function. > > Signed-off-by: Long Cheng > --- > drivers/dma/mediatek/Kconfig | 11 + > drivers/dma/mediatek/Makefile | 1 + > drivers/dma/mediatek/mtk-uart-apdma.c | 669 +++++++++++++++++++++++++++++++++ > 3 files changed, 681 insertions(+) > create mode 100644 drivers/dma/mediatek/mtk-uart-apdma.c > > diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig > index 680fc05..ac49eb6 100644 > --- a/drivers/dma/mediatek/Kconfig > +++ b/drivers/dma/mediatek/Kconfig > @@ -24,3 +24,14 @@ config MTK_CQDMA > > This controller provides the channels which is dedicated to > memory-to-memory transfer to offload from CPU. > + > +config MTK_UART_APDMA > + tristate "MediaTek SoCs APDMA support for UART" > + depends on OF && SERIAL_8250_MT6577 > + select DMA_ENGINE > + select DMA_VIRTUAL_CHANNELS > + help > + Support for the UART DMA engine found on MediaTek MTK SoCs. > + When SERIAL_8250_MT6577 is enabled, and if you want to use DMA, > + you can enable the config. The DMA engine can only be used > + with MediaTek SoCs. > diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile > index 41bb381..61a6d29 100644 > --- a/drivers/dma/mediatek/Makefile > +++ b/drivers/dma/mediatek/Makefile > @@ -1,2 +1,3 @@ > +obj-$(CONFIG_MTK_UART_APDMA) += mtk-uart-apdma.o > obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o > obj-$(CONFIG_MTK_CQDMA) += mtk-cqdma.o > diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c > new file mode 100644 > index 0000000..427db69 > --- /dev/null > +++ b/drivers/dma/mediatek/mtk-uart-apdma.c > @@ -0,0 +1,669 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * MediaTek Uart APDMA driver. > + * > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Long Cheng > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "../virt-dma.h" > + > +/* The default number of virtual channel */ > +#define MTK_UART_APDMA_NR_VCHANS 8 > + > +#define VFF_EN_B BIT(0) > +#define VFF_STOP_B BIT(0) > +#define VFF_FLUSH_B BIT(0) > +#define VFF_4G_SUPPORT_B BIT(0) > +#define VFF_RX_INT_EN0_B BIT(0) /* rx valid size >= vff thre */ > +#define VFF_RX_INT_EN1_B BIT(1) > +#define VFF_TX_INT_EN_B BIT(0) /* tx left size >= vff thre */ > +#define VFF_WARM_RST_B BIT(0) > +#define VFF_RX_INT_CLR_B (BIT(0) | BIT(1)) > +#define VFF_TX_INT_CLR_B 0 > +#define VFF_STOP_CLR_B 0 > +#define VFF_INT_EN_CLR_B 0 > +#define VFF_4G_SUPPORT_CLR_B 0 > + > +/* interrupt trigger level for tx */ > +#define VFF_TX_THRE(n) ((n) * 7 / 8) > +/* interrupt trigger level for rx */ > +#define VFF_RX_THRE(n) ((n) * 3 / 4) > + > +#define VFF_RING_SIZE 0xffffU > +/* invert this bit when wrap ring head again */ > +#define VFF_RING_WRAP 0x10000U > + > +#define VFF_INT_FLAG 0x00 > +#define VFF_INT_EN 0x04 > +#define VFF_EN 0x08 > +#define VFF_RST 0x0c > +#define VFF_STOP 0x10 > +#define VFF_FLUSH 0x14 > +#define VFF_ADDR 0x1c > +#define VFF_LEN 0x24 > +#define VFF_THRE 0x28 > +#define VFF_WPT 0x2c > +#define VFF_RPT 0x30 > +/* TX: the buffer size HW can read. RX: the buffer size SW can read. */ > +#define VFF_VALID_SIZE 0x3c > +/* TX: the buffer size SW can write. RX: the buffer size HW can write. */ > +#define VFF_LEFT_SIZE 0x40 > +#define VFF_DEBUG_STATUS 0x50 > +#define VFF_4G_SUPPORT 0x54 > + > +struct mtk_uart_apdmadev { > + struct dma_device ddev; > + struct clk *clk; > + bool support_33bits; > + unsigned int dma_requests; > + unsigned int *dma_irq; > +}; > + > +struct mtk_uart_apdma_desc { > + struct virt_dma_desc vd; > + > + unsigned int avail_len; > +}; > + > +struct mtk_chan { > + struct virt_dma_chan vc; > + struct dma_slave_config cfg; > + void __iomem *base; > + struct mtk_uart_apdma_desc *desc; > + > + enum dma_transfer_direction dir; > + > + bool requested; > + > + unsigned int rx_status; > +}; > + > +static inline struct mtk_uart_apdmadev * > +to_mtk_uart_apdma_dev(struct dma_device *d) > +{ > + return container_of(d, struct mtk_uart_apdmadev, ddev); > +} > + > +static inline struct mtk_chan *to_mtk_uart_apdma_chan(struct dma_chan *c) > +{ > + return container_of(c, struct mtk_chan, vc.chan); > +} > + > +static inline struct mtk_uart_apdma_desc *to_mtk_uart_apdma_desc > + (struct dma_async_tx_descriptor *t) > +{ > + return container_of(t, struct mtk_uart_apdma_desc, vd.tx); > +} > + > +static void mtk_uart_apdma_write(struct mtk_chan *c, > + unsigned int reg, unsigned int val) > +{ > + writel(val, c->base + reg); > +} > + > +static unsigned int mtk_uart_apdma_read(struct mtk_chan *c, unsigned int reg) > +{ > + return readl(c->base + reg); > +} > + > +static void mtk_uart_apdma_desc_free(struct virt_dma_desc *vd) > +{ > + struct dma_chan *chan = vd->tx.chan; > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + > + kfree(c->desc); > +} > + > +static void mtk_uart_apdma_start_tx(struct mtk_chan *c) > +{ > + unsigned int len, send, left, wpt, d_wpt, tmp; > + int ret; > + > + left = mtk_uart_apdma_read(c, VFF_LEFT_SIZE); > + if (!left) { > + mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B); > + return; > + } > + > + /* Wait 1sec for flush, can't sleep */ > + ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp, > + tmp != VFF_FLUSH_B, 0, 1000000); > + if (ret) > + dev_warn(c->vc.chan.device->dev, "tx: fail, debug=0x%x\n", > + mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); > + > + send = min_t(unsigned int, left, c->desc->avail_len); > + wpt = mtk_uart_apdma_read(c, VFF_WPT); > + len = mtk_uart_apdma_read(c, VFF_LEN); > + > + d_wpt = wpt + send; > + if ((d_wpt & VFF_RING_SIZE) >= len) { > + d_wpt = d_wpt - len; > + d_wpt = d_wpt ^ VFF_RING_WRAP; > + } > + mtk_uart_apdma_write(c, VFF_WPT, d_wpt); > + > + c->desc->avail_len -= send; > + > + mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B); > + if (mtk_uart_apdma_read(c, VFF_FLUSH) == 0U) > + mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B); > +} > + > +static void mtk_uart_apdma_start_rx(struct mtk_chan *c) > +{ > + struct mtk_uart_apdma_desc *d = c->desc; > + unsigned int len, wg, rg; > + int cnt; > + > + if ((mtk_uart_apdma_read(c, VFF_VALID_SIZE) == 0U) || > + !d || !vchan_next_desc(&c->vc)) > + return; > + > + len = mtk_uart_apdma_read(c, VFF_LEN); > + rg = mtk_uart_apdma_read(c, VFF_RPT); > + wg = mtk_uart_apdma_read(c, VFF_WPT); > + cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE); > + /* > + * The buffer is ring buffer. If wrap bit different, > + * represents the start of the next cycle for WPT > + */ > + if ((rg ^ wg) & VFF_RING_WRAP) > + cnt += len; > + > + c->rx_status = cnt; > + mtk_uart_apdma_write(c, VFF_RPT, wg); > + > + list_del(&d->vd.node); > + vchan_cookie_complete(&d->vd); > +} > + > +static irqreturn_t mtk_uart_apdma_irq_handler(int irq, void *dev_id) > +{ > + struct dma_chan *chan = (struct dma_chan *)dev_id; > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct mtk_uart_apdma_desc *d; > + unsigned long flags; > + > + spin_lock_irqsave(&c->vc.lock, flags); > + if (c->dir == DMA_DEV_TO_MEM) { > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); > + mtk_uart_apdma_start_rx(c); > + } else if (c->dir == DMA_MEM_TO_DEV) { > + d = c->desc; > + > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); > + > + if (d->avail_len != 0U) { > + mtk_uart_apdma_start_tx(c); > + } else { > + list_del(&d->vd.node); > + vchan_cookie_complete(&d->vd); > + } > + } > + spin_unlock_irqrestore(&c->vc.lock, flags); > + > + return IRQ_HANDLED; > +} > + > +static int mtk_uart_apdma_alloc_chan_resources(struct dma_chan *chan) > +{ > + struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device); > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + unsigned int tmp; > + int ret; > + > + pm_runtime_get_sync(mtkd->ddev.dev); > + > + mtk_uart_apdma_write(c, VFF_ADDR, 0); > + mtk_uart_apdma_write(c, VFF_THRE, 0); > + mtk_uart_apdma_write(c, VFF_LEN, 0); > + mtk_uart_apdma_write(c, VFF_RST, VFF_WARM_RST_B); > + > + ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100); > + if (ret) { > + dev_err(chan->device->dev, "dma reset: fail, timeout\n"); > + return ret; > + } > + > + if (!c->requested) { > + c->requested = true; > + ret = request_irq(mtkd->dma_irq[chan->chan_id], > + mtk_uart_apdma_irq_handler, IRQF_TRIGGER_NONE, > + KBUILD_MODNAME, chan); > + if (ret < 0) { > + dev_err(chan->device->dev, "Can't request dma IRQ\n"); > + return -EINVAL; > + } > + } > + > + if (mtkd->support_33bits) > + mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B); > + > + return ret; > +} > + > +static void mtk_uart_apdma_free_chan_resources(struct dma_chan *chan) > +{ > + struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device); > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + > + if (c->requested) { > + c->requested = false; > + free_irq(mtkd->dma_irq[chan->chan_id], chan); > + } > + > + tasklet_kill(&c->vc.task); > + > + vchan_free_chan_resources(&c->vc); > + > + pm_runtime_put_sync(mtkd->ddev.dev); > +} > + > +static enum dma_status mtk_uart_apdma_tx_status(struct dma_chan *chan, > + dma_cookie_t cookie, > + struct dma_tx_state *txstate) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + enum dma_status ret; > + unsigned long flags; > + > + if (!txstate) > + return DMA_ERROR; > + > + ret = dma_cookie_status(chan, cookie, txstate); > + spin_lock_irqsave(&c->vc.lock, flags); > + if (ret == DMA_IN_PROGRESS) { > + c->rx_status = mtk_uart_apdma_read(c, VFF_RPT) & VFF_RING_SIZE; > + dma_set_residue(txstate, c->rx_status); > + } else if (ret == DMA_COMPLETE && c->dir == DMA_DEV_TO_MEM) { > + dma_set_residue(txstate, c->rx_status); > + } else { > + dma_set_residue(txstate, 0); > + } > + spin_unlock_irqrestore(&c->vc.lock, flags); > + > + return ret; > +} > + > +static void mtk_uart_apdma_config_write(struct dma_chan *chan, > + struct dma_slave_config *cfg, > + enum dma_transfer_direction dir) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct mtk_uart_apdmadev *mtkd = > + to_mtk_uart_apdma_dev(c->vc.chan.device); > + unsigned int tmp; > + > + if (mtk_uart_apdma_read(c, VFF_EN) == VFF_EN_B) > + return; > + > + c->dir = dir; > + > + if (dir == DMA_DEV_TO_MEM) { > + tmp = cfg->src_addr_width * 1024; > + > + mtk_uart_apdma_write(c, VFF_ADDR, cfg->src_addr); > + mtk_uart_apdma_write(c, VFF_LEN, tmp); > + mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(tmp)); > + mtk_uart_apdma_write(c, VFF_INT_EN, > + VFF_RX_INT_EN0_B | VFF_RX_INT_EN1_B); > + mtk_uart_apdma_write(c, VFF_RPT, 0); > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); > + } else if (dir == DMA_MEM_TO_DEV) { > + tmp = cfg->dst_addr_width * 1024; > + > + mtk_uart_apdma_write(c, VFF_ADDR, cfg->dst_addr); > + mtk_uart_apdma_write(c, VFF_LEN, tmp); > + mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(tmp)); > + mtk_uart_apdma_write(c, VFF_WPT, 0); > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); > + } > + > + mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B); > + > + if (mtkd->support_33bits) > + mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_B); > + > + if (mtk_uart_apdma_read(c, VFF_EN) != VFF_EN_B) > + dev_err(chan->device->dev, "dir[%d] fail\n", dir); > +} > + > +/* > + * dmaengine_prep_slave_single will call the function. and sglen is 1. > + * 8250 uart using one ring buffer, and deal with one sg. > + */ > +static struct dma_async_tx_descriptor *mtk_uart_apdma_prep_slave_sg > + (struct dma_chan *chan, struct scatterlist *sgl, > + unsigned int sglen, enum dma_transfer_direction dir, > + unsigned long tx_flags, void *context) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct mtk_uart_apdma_desc *d; > + > + if (!is_slave_direction(dir)) > + return NULL; > + > + mtk_uart_apdma_config_write(chan, &c->cfg, dir); > + > + /* Now allocate and setup the descriptor */ > + d = kzalloc(sizeof(*d), GFP_ATOMIC); > + if (!d) > + return NULL; > + > + /* sglen is 1 */ > + d->avail_len = sg_dma_len(sgl); > + > + return vchan_tx_prep(&c->vc, &d->vd, tx_flags); > +} > + > +static void mtk_uart_apdma_issue_pending(struct dma_chan *chan) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct virt_dma_desc *vd; > + unsigned long flags; > + > + spin_lock_irqsave(&c->vc.lock, flags); > + if (vchan_issue_pending(&c->vc)) { > + vd = vchan_next_desc(&c->vc); > + c->desc = to_mtk_uart_apdma_desc(&vd->tx); > + } > + > + if (c->dir == DMA_DEV_TO_MEM) > + mtk_uart_apdma_start_rx(c); > + else if (c->dir == DMA_MEM_TO_DEV) > + mtk_uart_apdma_start_tx(c); > + > + spin_unlock_irqrestore(&c->vc.lock, flags); > +} > + > +static int mtk_uart_apdma_slave_config(struct dma_chan *chan, > + struct dma_slave_config *config) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + > + memcpy(&c->cfg, config, sizeof(*config)); > + > + return 0; > +} > + > +static int mtk_uart_apdma_terminate_all(struct dma_chan *chan) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + unsigned long flags; > + unsigned int tmp; > + int ret; > + > + spin_lock_irqsave(&c->vc.lock, flags); > + > + mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B); > + /* Wait 1sec for flush, can't sleep */ > + ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp, > + tmp != VFF_FLUSH_B, 0, 1000000); > + if (ret) > + dev_err(c->vc.chan.device->dev, "flush: fail, debug=0x%x\n", > + mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); > + > + /* set stop as 1 -> wait until en is 0 -> set stop as 0 */ > + mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_B); > + ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100); > + if (ret) > + dev_err(c->vc.chan.device->dev, "stop: fail, debug=0x%x\n", > + mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); > + > + mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_CLR_B); > + mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B); > + > + if (c->dir == DMA_DEV_TO_MEM) > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); > + else if (c->dir == DMA_MEM_TO_DEV) > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); > + > + spin_unlock_irqrestore(&c->vc.lock, flags); > + > + return 0; > +} > + > +static int mtk_uart_apdma_device_pause(struct dma_chan *chan) > +{ > + /* just for check caps pass */ > + return 0; > +} > + > +static void mtk_uart_apdma_free(struct mtk_uart_apdmadev *mtkd) > +{ > + while (!list_empty(&mtkd->ddev.channels)) { > + struct mtk_chan *c = list_first_entry(&mtkd->ddev.channels, > + struct mtk_chan, vc.chan.device_node); > + > + list_del(&c->vc.chan.device_node); > + tasklet_kill(&c->vc.task); > + } > +} > + > +static const struct of_device_id mtk_uart_apdma_match[] = { > + { .compatible = "mediatek,mt6577-uart-dma", }, > + { /* sentinel */ }, > +}; > +MODULE_DEVICE_TABLE(of, mtk_uart_apdma_match); > + > +static int mtk_uart_apdma_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct mtk_uart_apdmadev *mtkd; > + struct resource *res; > + struct mtk_chan *c; > + int bit_mask = 32, rc; > + unsigned int i; > + > + mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL); > + if (!mtkd) > + return -ENOMEM; > + > + mtkd->clk = devm_clk_get(&pdev->dev, NULL); > + if (IS_ERR(mtkd->clk)) { > + dev_err(&pdev->dev, "No clock specified\n"); > + rc = PTR_ERR(mtkd->clk); > + return rc; > + } > + > + if (of_property_read_bool(np, "dma-33bits")) > + mtkd->support_33bits = true; > + > + if (mtkd->support_33bits) > + bit_mask = 33; > + > + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(bit_mask)); > + if (rc) > + return rc; > + > + dma_cap_set(DMA_SLAVE, mtkd->ddev.cap_mask); > + mtkd->ddev.device_alloc_chan_resources = > + mtk_uart_apdma_alloc_chan_resources; > + mtkd->ddev.device_free_chan_resources = > + mtk_uart_apdma_free_chan_resources; > + mtkd->ddev.device_tx_status = mtk_uart_apdma_tx_status; > + mtkd->ddev.device_issue_pending = mtk_uart_apdma_issue_pending; > + mtkd->ddev.device_prep_slave_sg = mtk_uart_apdma_prep_slave_sg; > + mtkd->ddev.device_config = mtk_uart_apdma_slave_config; > + mtkd->ddev.device_pause = mtk_uart_apdma_device_pause; > + mtkd->ddev.device_terminate_all = mtk_uart_apdma_terminate_all; > + mtkd->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE); > + mtkd->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE); > + mtkd->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); > + mtkd->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; > + mtkd->ddev.dev = &pdev->dev; > + INIT_LIST_HEAD(&mtkd->ddev.channels); > + > + mtkd->dma_requests = MTK_UART_APDMA_NR_VCHANS; > + if (of_property_read_u32(np, "dma-requests", &mtkd->dma_requests)) { > + dev_info(&pdev->dev, > + "Using %u as missing dma-requests property\n", > + MTK_UART_APDMA_NR_VCHANS); > + } > + > + mtkd->dma_irq = devm_kcalloc(&pdev->dev, mtkd->dma_requests, > + sizeof(*mtkd->dma_irq), GFP_KERNEL); > + if (!mtkd->dma_irq) > + return -ENOMEM; > + > + for (i = 0; i < mtkd->dma_requests; i++) { > + c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL); > + if (!c) { > + rc = -ENODEV; > + goto err_no_dma; > + } > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, i); > + if (!res) { > + rc = -ENODEV; > + goto err_no_dma; > + } > + > + c->base = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(c->base)) { > + rc = PTR_ERR(c->base); > + goto err_no_dma; > + } > + c->requested = false; > + c->vc.desc_free = mtk_uart_apdma_desc_free; > + vchan_init(&c->vc, &mtkd->ddev); > + > + mtkd->dma_irq[i] = platform_get_irq(pdev, i); > + if ((int)mtkd->dma_irq[i] < 0) { > + dev_err(&pdev->dev, "failed to get IRQ[%d]\n", i); > + rc = -EINVAL; > + goto err_no_dma; > + } > + } > + > + pm_runtime_enable(&pdev->dev); > + pm_runtime_set_active(&pdev->dev); > + > + rc = dma_async_device_register(&mtkd->ddev); > + if (rc) > + goto rpm_disable; > + > + platform_set_drvdata(pdev, mtkd); > + > + /* Device-tree DMA controller registration */ > + rc = of_dma_controller_register(np, of_dma_xlate_by_chan_id, mtkd); > + if (rc) > + goto dma_remove; > + > + return rc; > + > +dma_remove: > + dma_async_device_unregister(&mtkd->ddev); > +rpm_disable: > + pm_runtime_disable(&pdev->dev); > +err_no_dma: > + mtk_uart_apdma_free(mtkd); > + return rc; > +} > + > +static int mtk_uart_apdma_remove(struct platform_device *pdev) > +{ > + struct mtk_uart_apdmadev *mtkd = platform_get_drvdata(pdev); > + > + if (pdev->dev.of_node) > + of_dma_controller_free(pdev->dev.of_node); > + > + pm_runtime_disable(&pdev->dev); > + pm_runtime_put_noidle(&pdev->dev); > + > + dma_async_device_unregister(&mtkd->ddev); > + mtk_uart_apdma_free(mtkd); > + > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int mtk_uart_apdma_suspend(struct device *dev) > +{ > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + if (!pm_runtime_suspended(dev)) > + clk_disable_unprepare(mtkd->clk); > + > + return 0; > +} > + > +static int mtk_uart_apdma_resume(struct device *dev) > +{ > + int ret; > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + if (!pm_runtime_suspended(dev)) { > + ret = clk_prepare_enable(mtkd->clk); > + if (ret) > + return ret; > + } > + > + return 0; > +} > +#endif /* CONFIG_PM_SLEEP */ > + > +#ifdef CONFIG_PM > +static int mtk_uart_apdma_runtime_suspend(struct device *dev) > +{ > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + clk_disable_unprepare(mtkd->clk); > + > + return 0; > +} > + > +static int mtk_uart_apdma_runtime_resume(struct device *dev) > +{ > + int ret; > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + ret = clk_prepare_enable(mtkd->clk); > + if (ret) > + return ret; > + > + return 0; > +} > +#endif /* CONFIG_PM */ > + > +static const struct dev_pm_ops mtk_uart_apdma_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(mtk_uart_apdma_suspend, mtk_uart_apdma_resume) > + SET_RUNTIME_PM_OPS(mtk_uart_apdma_runtime_suspend, > + mtk_uart_apdma_runtime_resume, NULL) > +}; > + > +static struct platform_driver mtk_uart_apdma_driver = { > + .probe = mtk_uart_apdma_probe, > + .remove = mtk_uart_apdma_remove, > + .driver = { > + .name = KBUILD_MODNAME, > + .pm = &mtk_uart_apdma_pm_ops, > + .of_match_table = of_match_ptr(mtk_uart_apdma_match), > + }, > +}; > + > +module_platform_driver(mtk_uart_apdma_driver); > + > +MODULE_DESCRIPTION("MediaTek UART APDMA Controller Driver"); > +MODULE_AUTHOR("Long Cheng "); > +MODULE_LICENSE("GPL v2"); > + From mboxrd@z Thu Jan 1 00:00:00 1970 From: Long Cheng Subject: Re: [PATCH v10 1/3] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support Date: Mon, 28 Jan 2019 11:25:16 +0800 Message-ID: <1548645916.3831.73.camel@mhfsdcap03> References: <1547781016-890-1-git-send-email-long.cheng@mediatek.com> <1547781016-890-2-git-send-email-long.cheng@mediatek.com> Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1547781016-890-2-git-send-email-long.cheng@mediatek.com> Sender: linux-kernel-owner@vger.kernel.org To: Vinod Koul Cc: Randy Dunlap , Rob Herring , Mark Rutland , Ryder Lee , Sean Wang , Nicolas Boichat , Matthias Brugger , Dan Williams , Greg Kroah-Hartman , Jiri Slaby , Sean Wang , dmaengine@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, srv_heupstream@mediatek.com, Yingjoe Chen , YT Shen , Zhenbao Liu List-Id: devicetree@vger.kernel.org On Fri, 2019-01-18 at 11:10 +0800, Long Cheng wrote: Hi Vinod Koul, Just a gentle ping! thanks. > In DMA engine framework, add 8250 uart dma to support MediaTek uart. > If MediaTek uart enabled(SERIAL_8250_MT6577), and want to improve > the performance, can enable the function. > > Signed-off-by: Long Cheng > --- > drivers/dma/mediatek/Kconfig | 11 + > drivers/dma/mediatek/Makefile | 1 + > drivers/dma/mediatek/mtk-uart-apdma.c | 669 +++++++++++++++++++++++++++++++++ > 3 files changed, 681 insertions(+) > create mode 100644 drivers/dma/mediatek/mtk-uart-apdma.c > > diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig > index 680fc05..ac49eb6 100644 > --- a/drivers/dma/mediatek/Kconfig > +++ b/drivers/dma/mediatek/Kconfig > @@ -24,3 +24,14 @@ config MTK_CQDMA > > This controller provides the channels which is dedicated to > memory-to-memory transfer to offload from CPU. > + > +config MTK_UART_APDMA > + tristate "MediaTek SoCs APDMA support for UART" > + depends on OF && SERIAL_8250_MT6577 > + select DMA_ENGINE > + select DMA_VIRTUAL_CHANNELS > + help > + Support for the UART DMA engine found on MediaTek MTK SoCs. > + When SERIAL_8250_MT6577 is enabled, and if you want to use DMA, > + you can enable the config. The DMA engine can only be used > + with MediaTek SoCs. > diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile > index 41bb381..61a6d29 100644 > --- a/drivers/dma/mediatek/Makefile > +++ b/drivers/dma/mediatek/Makefile > @@ -1,2 +1,3 @@ > +obj-$(CONFIG_MTK_UART_APDMA) += mtk-uart-apdma.o > obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o > obj-$(CONFIG_MTK_CQDMA) += mtk-cqdma.o > diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c > new file mode 100644 > index 0000000..427db69 > --- /dev/null > +++ b/drivers/dma/mediatek/mtk-uart-apdma.c > @@ -0,0 +1,669 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * MediaTek Uart APDMA driver. > + * > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Long Cheng > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "../virt-dma.h" > + > +/* The default number of virtual channel */ > +#define MTK_UART_APDMA_NR_VCHANS 8 > + > +#define VFF_EN_B BIT(0) > +#define VFF_STOP_B BIT(0) > +#define VFF_FLUSH_B BIT(0) > +#define VFF_4G_SUPPORT_B BIT(0) > +#define VFF_RX_INT_EN0_B BIT(0) /* rx valid size >= vff thre */ > +#define VFF_RX_INT_EN1_B BIT(1) > +#define VFF_TX_INT_EN_B BIT(0) /* tx left size >= vff thre */ > +#define VFF_WARM_RST_B BIT(0) > +#define VFF_RX_INT_CLR_B (BIT(0) | BIT(1)) > +#define VFF_TX_INT_CLR_B 0 > +#define VFF_STOP_CLR_B 0 > +#define VFF_INT_EN_CLR_B 0 > +#define VFF_4G_SUPPORT_CLR_B 0 > + > +/* interrupt trigger level for tx */ > +#define VFF_TX_THRE(n) ((n) * 7 / 8) > +/* interrupt trigger level for rx */ > +#define VFF_RX_THRE(n) ((n) * 3 / 4) > + > +#define VFF_RING_SIZE 0xffffU > +/* invert this bit when wrap ring head again */ > +#define VFF_RING_WRAP 0x10000U > + > +#define VFF_INT_FLAG 0x00 > +#define VFF_INT_EN 0x04 > +#define VFF_EN 0x08 > +#define VFF_RST 0x0c > +#define VFF_STOP 0x10 > +#define VFF_FLUSH 0x14 > +#define VFF_ADDR 0x1c > +#define VFF_LEN 0x24 > +#define VFF_THRE 0x28 > +#define VFF_WPT 0x2c > +#define VFF_RPT 0x30 > +/* TX: the buffer size HW can read. RX: the buffer size SW can read. */ > +#define VFF_VALID_SIZE 0x3c > +/* TX: the buffer size SW can write. RX: the buffer size HW can write. */ > +#define VFF_LEFT_SIZE 0x40 > +#define VFF_DEBUG_STATUS 0x50 > +#define VFF_4G_SUPPORT 0x54 > + > +struct mtk_uart_apdmadev { > + struct dma_device ddev; > + struct clk *clk; > + bool support_33bits; > + unsigned int dma_requests; > + unsigned int *dma_irq; > +}; > + > +struct mtk_uart_apdma_desc { > + struct virt_dma_desc vd; > + > + unsigned int avail_len; > +}; > + > +struct mtk_chan { > + struct virt_dma_chan vc; > + struct dma_slave_config cfg; > + void __iomem *base; > + struct mtk_uart_apdma_desc *desc; > + > + enum dma_transfer_direction dir; > + > + bool requested; > + > + unsigned int rx_status; > +}; > + > +static inline struct mtk_uart_apdmadev * > +to_mtk_uart_apdma_dev(struct dma_device *d) > +{ > + return container_of(d, struct mtk_uart_apdmadev, ddev); > +} > + > +static inline struct mtk_chan *to_mtk_uart_apdma_chan(struct dma_chan *c) > +{ > + return container_of(c, struct mtk_chan, vc.chan); > +} > + > +static inline struct mtk_uart_apdma_desc *to_mtk_uart_apdma_desc > + (struct dma_async_tx_descriptor *t) > +{ > + return container_of(t, struct mtk_uart_apdma_desc, vd.tx); > +} > + > +static void mtk_uart_apdma_write(struct mtk_chan *c, > + unsigned int reg, unsigned int val) > +{ > + writel(val, c->base + reg); > +} > + > +static unsigned int mtk_uart_apdma_read(struct mtk_chan *c, unsigned int reg) > +{ > + return readl(c->base + reg); > +} > + > +static void mtk_uart_apdma_desc_free(struct virt_dma_desc *vd) > +{ > + struct dma_chan *chan = vd->tx.chan; > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + > + kfree(c->desc); > +} > + > +static void mtk_uart_apdma_start_tx(struct mtk_chan *c) > +{ > + unsigned int len, send, left, wpt, d_wpt, tmp; > + int ret; > + > + left = mtk_uart_apdma_read(c, VFF_LEFT_SIZE); > + if (!left) { > + mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B); > + return; > + } > + > + /* Wait 1sec for flush, can't sleep */ > + ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp, > + tmp != VFF_FLUSH_B, 0, 1000000); > + if (ret) > + dev_warn(c->vc.chan.device->dev, "tx: fail, debug=0x%x\n", > + mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); > + > + send = min_t(unsigned int, left, c->desc->avail_len); > + wpt = mtk_uart_apdma_read(c, VFF_WPT); > + len = mtk_uart_apdma_read(c, VFF_LEN); > + > + d_wpt = wpt + send; > + if ((d_wpt & VFF_RING_SIZE) >= len) { > + d_wpt = d_wpt - len; > + d_wpt = d_wpt ^ VFF_RING_WRAP; > + } > + mtk_uart_apdma_write(c, VFF_WPT, d_wpt); > + > + c->desc->avail_len -= send; > + > + mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B); > + if (mtk_uart_apdma_read(c, VFF_FLUSH) == 0U) > + mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B); > +} > + > +static void mtk_uart_apdma_start_rx(struct mtk_chan *c) > +{ > + struct mtk_uart_apdma_desc *d = c->desc; > + unsigned int len, wg, rg; > + int cnt; > + > + if ((mtk_uart_apdma_read(c, VFF_VALID_SIZE) == 0U) || > + !d || !vchan_next_desc(&c->vc)) > + return; > + > + len = mtk_uart_apdma_read(c, VFF_LEN); > + rg = mtk_uart_apdma_read(c, VFF_RPT); > + wg = mtk_uart_apdma_read(c, VFF_WPT); > + cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE); > + /* > + * The buffer is ring buffer. If wrap bit different, > + * represents the start of the next cycle for WPT > + */ > + if ((rg ^ wg) & VFF_RING_WRAP) > + cnt += len; > + > + c->rx_status = cnt; > + mtk_uart_apdma_write(c, VFF_RPT, wg); > + > + list_del(&d->vd.node); > + vchan_cookie_complete(&d->vd); > +} > + > +static irqreturn_t mtk_uart_apdma_irq_handler(int irq, void *dev_id) > +{ > + struct dma_chan *chan = (struct dma_chan *)dev_id; > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct mtk_uart_apdma_desc *d; > + unsigned long flags; > + > + spin_lock_irqsave(&c->vc.lock, flags); > + if (c->dir == DMA_DEV_TO_MEM) { > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); > + mtk_uart_apdma_start_rx(c); > + } else if (c->dir == DMA_MEM_TO_DEV) { > + d = c->desc; > + > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); > + > + if (d->avail_len != 0U) { > + mtk_uart_apdma_start_tx(c); > + } else { > + list_del(&d->vd.node); > + vchan_cookie_complete(&d->vd); > + } > + } > + spin_unlock_irqrestore(&c->vc.lock, flags); > + > + return IRQ_HANDLED; > +} > + > +static int mtk_uart_apdma_alloc_chan_resources(struct dma_chan *chan) > +{ > + struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device); > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + unsigned int tmp; > + int ret; > + > + pm_runtime_get_sync(mtkd->ddev.dev); > + > + mtk_uart_apdma_write(c, VFF_ADDR, 0); > + mtk_uart_apdma_write(c, VFF_THRE, 0); > + mtk_uart_apdma_write(c, VFF_LEN, 0); > + mtk_uart_apdma_write(c, VFF_RST, VFF_WARM_RST_B); > + > + ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100); > + if (ret) { > + dev_err(chan->device->dev, "dma reset: fail, timeout\n"); > + return ret; > + } > + > + if (!c->requested) { > + c->requested = true; > + ret = request_irq(mtkd->dma_irq[chan->chan_id], > + mtk_uart_apdma_irq_handler, IRQF_TRIGGER_NONE, > + KBUILD_MODNAME, chan); > + if (ret < 0) { > + dev_err(chan->device->dev, "Can't request dma IRQ\n"); > + return -EINVAL; > + } > + } > + > + if (mtkd->support_33bits) > + mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B); > + > + return ret; > +} > + > +static void mtk_uart_apdma_free_chan_resources(struct dma_chan *chan) > +{ > + struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device); > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + > + if (c->requested) { > + c->requested = false; > + free_irq(mtkd->dma_irq[chan->chan_id], chan); > + } > + > + tasklet_kill(&c->vc.task); > + > + vchan_free_chan_resources(&c->vc); > + > + pm_runtime_put_sync(mtkd->ddev.dev); > +} > + > +static enum dma_status mtk_uart_apdma_tx_status(struct dma_chan *chan, > + dma_cookie_t cookie, > + struct dma_tx_state *txstate) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + enum dma_status ret; > + unsigned long flags; > + > + if (!txstate) > + return DMA_ERROR; > + > + ret = dma_cookie_status(chan, cookie, txstate); > + spin_lock_irqsave(&c->vc.lock, flags); > + if (ret == DMA_IN_PROGRESS) { > + c->rx_status = mtk_uart_apdma_read(c, VFF_RPT) & VFF_RING_SIZE; > + dma_set_residue(txstate, c->rx_status); > + } else if (ret == DMA_COMPLETE && c->dir == DMA_DEV_TO_MEM) { > + dma_set_residue(txstate, c->rx_status); > + } else { > + dma_set_residue(txstate, 0); > + } > + spin_unlock_irqrestore(&c->vc.lock, flags); > + > + return ret; > +} > + > +static void mtk_uart_apdma_config_write(struct dma_chan *chan, > + struct dma_slave_config *cfg, > + enum dma_transfer_direction dir) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct mtk_uart_apdmadev *mtkd = > + to_mtk_uart_apdma_dev(c->vc.chan.device); > + unsigned int tmp; > + > + if (mtk_uart_apdma_read(c, VFF_EN) == VFF_EN_B) > + return; > + > + c->dir = dir; > + > + if (dir == DMA_DEV_TO_MEM) { > + tmp = cfg->src_addr_width * 1024; > + > + mtk_uart_apdma_write(c, VFF_ADDR, cfg->src_addr); > + mtk_uart_apdma_write(c, VFF_LEN, tmp); > + mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(tmp)); > + mtk_uart_apdma_write(c, VFF_INT_EN, > + VFF_RX_INT_EN0_B | VFF_RX_INT_EN1_B); > + mtk_uart_apdma_write(c, VFF_RPT, 0); > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); > + } else if (dir == DMA_MEM_TO_DEV) { > + tmp = cfg->dst_addr_width * 1024; > + > + mtk_uart_apdma_write(c, VFF_ADDR, cfg->dst_addr); > + mtk_uart_apdma_write(c, VFF_LEN, tmp); > + mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(tmp)); > + mtk_uart_apdma_write(c, VFF_WPT, 0); > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); > + } > + > + mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B); > + > + if (mtkd->support_33bits) > + mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_B); > + > + if (mtk_uart_apdma_read(c, VFF_EN) != VFF_EN_B) > + dev_err(chan->device->dev, "dir[%d] fail\n", dir); > +} > + > +/* > + * dmaengine_prep_slave_single will call the function. and sglen is 1. > + * 8250 uart using one ring buffer, and deal with one sg. > + */ > +static struct dma_async_tx_descriptor *mtk_uart_apdma_prep_slave_sg > + (struct dma_chan *chan, struct scatterlist *sgl, > + unsigned int sglen, enum dma_transfer_direction dir, > + unsigned long tx_flags, void *context) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct mtk_uart_apdma_desc *d; > + > + if (!is_slave_direction(dir)) > + return NULL; > + > + mtk_uart_apdma_config_write(chan, &c->cfg, dir); > + > + /* Now allocate and setup the descriptor */ > + d = kzalloc(sizeof(*d), GFP_ATOMIC); > + if (!d) > + return NULL; > + > + /* sglen is 1 */ > + d->avail_len = sg_dma_len(sgl); > + > + return vchan_tx_prep(&c->vc, &d->vd, tx_flags); > +} > + > +static void mtk_uart_apdma_issue_pending(struct dma_chan *chan) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct virt_dma_desc *vd; > + unsigned long flags; > + > + spin_lock_irqsave(&c->vc.lock, flags); > + if (vchan_issue_pending(&c->vc)) { > + vd = vchan_next_desc(&c->vc); > + c->desc = to_mtk_uart_apdma_desc(&vd->tx); > + } > + > + if (c->dir == DMA_DEV_TO_MEM) > + mtk_uart_apdma_start_rx(c); > + else if (c->dir == DMA_MEM_TO_DEV) > + mtk_uart_apdma_start_tx(c); > + > + spin_unlock_irqrestore(&c->vc.lock, flags); > +} > + > +static int mtk_uart_apdma_slave_config(struct dma_chan *chan, > + struct dma_slave_config *config) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + > + memcpy(&c->cfg, config, sizeof(*config)); > + > + return 0; > +} > + > +static int mtk_uart_apdma_terminate_all(struct dma_chan *chan) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + unsigned long flags; > + unsigned int tmp; > + int ret; > + > + spin_lock_irqsave(&c->vc.lock, flags); > + > + mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B); > + /* Wait 1sec for flush, can't sleep */ > + ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp, > + tmp != VFF_FLUSH_B, 0, 1000000); > + if (ret) > + dev_err(c->vc.chan.device->dev, "flush: fail, debug=0x%x\n", > + mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); > + > + /* set stop as 1 -> wait until en is 0 -> set stop as 0 */ > + mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_B); > + ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100); > + if (ret) > + dev_err(c->vc.chan.device->dev, "stop: fail, debug=0x%x\n", > + mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); > + > + mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_CLR_B); > + mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B); > + > + if (c->dir == DMA_DEV_TO_MEM) > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); > + else if (c->dir == DMA_MEM_TO_DEV) > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); > + > + spin_unlock_irqrestore(&c->vc.lock, flags); > + > + return 0; > +} > + > +static int mtk_uart_apdma_device_pause(struct dma_chan *chan) > +{ > + /* just for check caps pass */ > + return 0; > +} > + > +static void mtk_uart_apdma_free(struct mtk_uart_apdmadev *mtkd) > +{ > + while (!list_empty(&mtkd->ddev.channels)) { > + struct mtk_chan *c = list_first_entry(&mtkd->ddev.channels, > + struct mtk_chan, vc.chan.device_node); > + > + list_del(&c->vc.chan.device_node); > + tasklet_kill(&c->vc.task); > + } > +} > + > +static const struct of_device_id mtk_uart_apdma_match[] = { > + { .compatible = "mediatek,mt6577-uart-dma", }, > + { /* sentinel */ }, > +}; > +MODULE_DEVICE_TABLE(of, mtk_uart_apdma_match); > + > +static int mtk_uart_apdma_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct mtk_uart_apdmadev *mtkd; > + struct resource *res; > + struct mtk_chan *c; > + int bit_mask = 32, rc; > + unsigned int i; > + > + mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL); > + if (!mtkd) > + return -ENOMEM; > + > + mtkd->clk = devm_clk_get(&pdev->dev, NULL); > + if (IS_ERR(mtkd->clk)) { > + dev_err(&pdev->dev, "No clock specified\n"); > + rc = PTR_ERR(mtkd->clk); > + return rc; > + } > + > + if (of_property_read_bool(np, "dma-33bits")) > + mtkd->support_33bits = true; > + > + if (mtkd->support_33bits) > + bit_mask = 33; > + > + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(bit_mask)); > + if (rc) > + return rc; > + > + dma_cap_set(DMA_SLAVE, mtkd->ddev.cap_mask); > + mtkd->ddev.device_alloc_chan_resources = > + mtk_uart_apdma_alloc_chan_resources; > + mtkd->ddev.device_free_chan_resources = > + mtk_uart_apdma_free_chan_resources; > + mtkd->ddev.device_tx_status = mtk_uart_apdma_tx_status; > + mtkd->ddev.device_issue_pending = mtk_uart_apdma_issue_pending; > + mtkd->ddev.device_prep_slave_sg = mtk_uart_apdma_prep_slave_sg; > + mtkd->ddev.device_config = mtk_uart_apdma_slave_config; > + mtkd->ddev.device_pause = mtk_uart_apdma_device_pause; > + mtkd->ddev.device_terminate_all = mtk_uart_apdma_terminate_all; > + mtkd->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE); > + mtkd->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE); > + mtkd->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); > + mtkd->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; > + mtkd->ddev.dev = &pdev->dev; > + INIT_LIST_HEAD(&mtkd->ddev.channels); > + > + mtkd->dma_requests = MTK_UART_APDMA_NR_VCHANS; > + if (of_property_read_u32(np, "dma-requests", &mtkd->dma_requests)) { > + dev_info(&pdev->dev, > + "Using %u as missing dma-requests property\n", > + MTK_UART_APDMA_NR_VCHANS); > + } > + > + mtkd->dma_irq = devm_kcalloc(&pdev->dev, mtkd->dma_requests, > + sizeof(*mtkd->dma_irq), GFP_KERNEL); > + if (!mtkd->dma_irq) > + return -ENOMEM; > + > + for (i = 0; i < mtkd->dma_requests; i++) { > + c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL); > + if (!c) { > + rc = -ENODEV; > + goto err_no_dma; > + } > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, i); > + if (!res) { > + rc = -ENODEV; > + goto err_no_dma; > + } > + > + c->base = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(c->base)) { > + rc = PTR_ERR(c->base); > + goto err_no_dma; > + } > + c->requested = false; > + c->vc.desc_free = mtk_uart_apdma_desc_free; > + vchan_init(&c->vc, &mtkd->ddev); > + > + mtkd->dma_irq[i] = platform_get_irq(pdev, i); > + if ((int)mtkd->dma_irq[i] < 0) { > + dev_err(&pdev->dev, "failed to get IRQ[%d]\n", i); > + rc = -EINVAL; > + goto err_no_dma; > + } > + } > + > + pm_runtime_enable(&pdev->dev); > + pm_runtime_set_active(&pdev->dev); > + > + rc = dma_async_device_register(&mtkd->ddev); > + if (rc) > + goto rpm_disable; > + > + platform_set_drvdata(pdev, mtkd); > + > + /* Device-tree DMA controller registration */ > + rc = of_dma_controller_register(np, of_dma_xlate_by_chan_id, mtkd); > + if (rc) > + goto dma_remove; > + > + return rc; > + > +dma_remove: > + dma_async_device_unregister(&mtkd->ddev); > +rpm_disable: > + pm_runtime_disable(&pdev->dev); > +err_no_dma: > + mtk_uart_apdma_free(mtkd); > + return rc; > +} > + > +static int mtk_uart_apdma_remove(struct platform_device *pdev) > +{ > + struct mtk_uart_apdmadev *mtkd = platform_get_drvdata(pdev); > + > + if (pdev->dev.of_node) > + of_dma_controller_free(pdev->dev.of_node); > + > + pm_runtime_disable(&pdev->dev); > + pm_runtime_put_noidle(&pdev->dev); > + > + dma_async_device_unregister(&mtkd->ddev); > + mtk_uart_apdma_free(mtkd); > + > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int mtk_uart_apdma_suspend(struct device *dev) > +{ > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + if (!pm_runtime_suspended(dev)) > + clk_disable_unprepare(mtkd->clk); > + > + return 0; > +} > + > +static int mtk_uart_apdma_resume(struct device *dev) > +{ > + int ret; > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + if (!pm_runtime_suspended(dev)) { > + ret = clk_prepare_enable(mtkd->clk); > + if (ret) > + return ret; > + } > + > + return 0; > +} > +#endif /* CONFIG_PM_SLEEP */ > + > +#ifdef CONFIG_PM > +static int mtk_uart_apdma_runtime_suspend(struct device *dev) > +{ > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + clk_disable_unprepare(mtkd->clk); > + > + return 0; > +} > + > +static int mtk_uart_apdma_runtime_resume(struct device *dev) > +{ > + int ret; > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + ret = clk_prepare_enable(mtkd->clk); > + if (ret) > + return ret; > + > + return 0; > +} > +#endif /* CONFIG_PM */ > + > +static const struct dev_pm_ops mtk_uart_apdma_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(mtk_uart_apdma_suspend, mtk_uart_apdma_resume) > + SET_RUNTIME_PM_OPS(mtk_uart_apdma_runtime_suspend, > + mtk_uart_apdma_runtime_resume, NULL) > +}; > + > +static struct platform_driver mtk_uart_apdma_driver = { > + .probe = mtk_uart_apdma_probe, > + .remove = mtk_uart_apdma_remove, > + .driver = { > + .name = KBUILD_MODNAME, > + .pm = &mtk_uart_apdma_pm_ops, > + .of_match_table = of_match_ptr(mtk_uart_apdma_match), > + }, > +}; > + > +module_platform_driver(mtk_uart_apdma_driver); > + > +MODULE_DESCRIPTION("MediaTek UART APDMA Controller Driver"); > +MODULE_AUTHOR("Long Cheng "); > +MODULE_LICENSE("GPL v2"); > + From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.0 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_PASS,UNPARSEABLE_RELAY,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6F048C282CA for ; Mon, 28 Jan 2019 03:26:25 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 3D8FF2148E for ; Mon, 28 Jan 2019 03:26:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="c1TDiK2E" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 3D8FF2148E Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=mediatek.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Date:To:From:Subject:Message-ID:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=PIWodW6hKH2zoSx1tMLDOsLmUehcf3XEUR8ZyAVfEjY=; b=c1TDiK2EkuMLgR cWsldBmjTRd9PCy1zTgxGiJP8iXEbRMhMxoCp6YOpppDiW6wiZH6noFYmyY8u7CoAjkr4r8N/quWi 2gtlMi0haNihy5m64cKtOa4IUpTKljb1cPfuPRfxxjeZr3Xe3BmVVW3INQhborWy2DUhctmGmIfje WIL0pMpX8g73pQVvTPs+5HTTPmvqYddmXuYy30M7f/lXOjSUSA/jhnJcvfYJtm1bUOVULOcyUpSS9 eiBe6Ubc+dtlp6bM1u0OrrRU+JlD6gWm9it7qvjuIOs4BR54BfmeOuIZc1Dmwp5fknaeGci/i4DLC LyB2usNfB1dRgQfBfCIg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gnxYx-000344-OM; Mon, 28 Jan 2019 03:26:19 +0000 Received: from mailgw01.mediatek.com ([216.200.240.184]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gnxYt-000339-EC; Mon, 28 Jan 2019 03:26:18 +0000 X-UUID: 1dd001fe89934746bebc13d8175c4129-20190127 X-UUID: 1dd001fe89934746bebc13d8175c4129-20190127 Received: from mtkcas68.mediatek.inc [(172.29.94.19)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 373497518; Sun, 27 Jan 2019 19:25:22 -0800 Received: from MTKMBS01N1.mediatek.inc (172.21.101.68) by MTKMBS62DR.mediatek.inc (172.29.94.18) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Sun, 27 Jan 2019 19:25:20 -0800 Received: from MTKCAS32.mediatek.inc (172.27.4.184) by mtkmbs01n1.mediatek.inc (172.21.101.68) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 28 Jan 2019 11:25:18 +0800 Received: from [10.17.3.153] (10.17.3.153) by MTKCAS32.mediatek.inc (172.27.4.170) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Mon, 28 Jan 2019 11:25:17 +0800 Message-ID: <1548645916.3831.73.camel@mhfsdcap03> Subject: Re: [PATCH v10 1/3] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support From: Long Cheng To: Vinod Koul Date: Mon, 28 Jan 2019 11:25:16 +0800 In-Reply-To: <1547781016-890-2-git-send-email-long.cheng@mediatek.com> References: <1547781016-890-1-git-send-email-long.cheng@mediatek.com> <1547781016-890-2-git-send-email-long.cheng@mediatek.com> X-Mailer: Evolution 3.2.3-0ubuntu6 MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190127_192615_491005_2BB10FBE X-CRM114-Status: GOOD ( 26.25 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , devicetree@vger.kernel.org, Nicolas Boichat , Zhenbao Liu , linux-serial@vger.kernel.org, srv_heupstream@mediatek.com, Greg Kroah-Hartman , Randy Dunlap , linux-kernel@vger.kernel.org, Rob Herring , Sean Wang , YT Shen , dmaengine@vger.kernel.org, Ryder Lee , linux-mediatek@lists.infradead.org, Sean Wang , Jiri Slaby , Matthias Brugger , Yingjoe Chen , Dan Williams , linux-arm-kernel@lists.infradead.org Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org On Fri, 2019-01-18 at 11:10 +0800, Long Cheng wrote: Hi Vinod Koul, Just a gentle ping! thanks. > In DMA engine framework, add 8250 uart dma to support MediaTek uart. > If MediaTek uart enabled(SERIAL_8250_MT6577), and want to improve > the performance, can enable the function. > > Signed-off-by: Long Cheng > --- > drivers/dma/mediatek/Kconfig | 11 + > drivers/dma/mediatek/Makefile | 1 + > drivers/dma/mediatek/mtk-uart-apdma.c | 669 +++++++++++++++++++++++++++++++++ > 3 files changed, 681 insertions(+) > create mode 100644 drivers/dma/mediatek/mtk-uart-apdma.c > > diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig > index 680fc05..ac49eb6 100644 > --- a/drivers/dma/mediatek/Kconfig > +++ b/drivers/dma/mediatek/Kconfig > @@ -24,3 +24,14 @@ config MTK_CQDMA > > This controller provides the channels which is dedicated to > memory-to-memory transfer to offload from CPU. > + > +config MTK_UART_APDMA > + tristate "MediaTek SoCs APDMA support for UART" > + depends on OF && SERIAL_8250_MT6577 > + select DMA_ENGINE > + select DMA_VIRTUAL_CHANNELS > + help > + Support for the UART DMA engine found on MediaTek MTK SoCs. > + When SERIAL_8250_MT6577 is enabled, and if you want to use DMA, > + you can enable the config. The DMA engine can only be used > + with MediaTek SoCs. > diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile > index 41bb381..61a6d29 100644 > --- a/drivers/dma/mediatek/Makefile > +++ b/drivers/dma/mediatek/Makefile > @@ -1,2 +1,3 @@ > +obj-$(CONFIG_MTK_UART_APDMA) += mtk-uart-apdma.o > obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o > obj-$(CONFIG_MTK_CQDMA) += mtk-cqdma.o > diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c > new file mode 100644 > index 0000000..427db69 > --- /dev/null > +++ b/drivers/dma/mediatek/mtk-uart-apdma.c > @@ -0,0 +1,669 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * MediaTek Uart APDMA driver. > + * > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Long Cheng > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "../virt-dma.h" > + > +/* The default number of virtual channel */ > +#define MTK_UART_APDMA_NR_VCHANS 8 > + > +#define VFF_EN_B BIT(0) > +#define VFF_STOP_B BIT(0) > +#define VFF_FLUSH_B BIT(0) > +#define VFF_4G_SUPPORT_B BIT(0) > +#define VFF_RX_INT_EN0_B BIT(0) /* rx valid size >= vff thre */ > +#define VFF_RX_INT_EN1_B BIT(1) > +#define VFF_TX_INT_EN_B BIT(0) /* tx left size >= vff thre */ > +#define VFF_WARM_RST_B BIT(0) > +#define VFF_RX_INT_CLR_B (BIT(0) | BIT(1)) > +#define VFF_TX_INT_CLR_B 0 > +#define VFF_STOP_CLR_B 0 > +#define VFF_INT_EN_CLR_B 0 > +#define VFF_4G_SUPPORT_CLR_B 0 > + > +/* interrupt trigger level for tx */ > +#define VFF_TX_THRE(n) ((n) * 7 / 8) > +/* interrupt trigger level for rx */ > +#define VFF_RX_THRE(n) ((n) * 3 / 4) > + > +#define VFF_RING_SIZE 0xffffU > +/* invert this bit when wrap ring head again */ > +#define VFF_RING_WRAP 0x10000U > + > +#define VFF_INT_FLAG 0x00 > +#define VFF_INT_EN 0x04 > +#define VFF_EN 0x08 > +#define VFF_RST 0x0c > +#define VFF_STOP 0x10 > +#define VFF_FLUSH 0x14 > +#define VFF_ADDR 0x1c > +#define VFF_LEN 0x24 > +#define VFF_THRE 0x28 > +#define VFF_WPT 0x2c > +#define VFF_RPT 0x30 > +/* TX: the buffer size HW can read. RX: the buffer size SW can read. */ > +#define VFF_VALID_SIZE 0x3c > +/* TX: the buffer size SW can write. RX: the buffer size HW can write. */ > +#define VFF_LEFT_SIZE 0x40 > +#define VFF_DEBUG_STATUS 0x50 > +#define VFF_4G_SUPPORT 0x54 > + > +struct mtk_uart_apdmadev { > + struct dma_device ddev; > + struct clk *clk; > + bool support_33bits; > + unsigned int dma_requests; > + unsigned int *dma_irq; > +}; > + > +struct mtk_uart_apdma_desc { > + struct virt_dma_desc vd; > + > + unsigned int avail_len; > +}; > + > +struct mtk_chan { > + struct virt_dma_chan vc; > + struct dma_slave_config cfg; > + void __iomem *base; > + struct mtk_uart_apdma_desc *desc; > + > + enum dma_transfer_direction dir; > + > + bool requested; > + > + unsigned int rx_status; > +}; > + > +static inline struct mtk_uart_apdmadev * > +to_mtk_uart_apdma_dev(struct dma_device *d) > +{ > + return container_of(d, struct mtk_uart_apdmadev, ddev); > +} > + > +static inline struct mtk_chan *to_mtk_uart_apdma_chan(struct dma_chan *c) > +{ > + return container_of(c, struct mtk_chan, vc.chan); > +} > + > +static inline struct mtk_uart_apdma_desc *to_mtk_uart_apdma_desc > + (struct dma_async_tx_descriptor *t) > +{ > + return container_of(t, struct mtk_uart_apdma_desc, vd.tx); > +} > + > +static void mtk_uart_apdma_write(struct mtk_chan *c, > + unsigned int reg, unsigned int val) > +{ > + writel(val, c->base + reg); > +} > + > +static unsigned int mtk_uart_apdma_read(struct mtk_chan *c, unsigned int reg) > +{ > + return readl(c->base + reg); > +} > + > +static void mtk_uart_apdma_desc_free(struct virt_dma_desc *vd) > +{ > + struct dma_chan *chan = vd->tx.chan; > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + > + kfree(c->desc); > +} > + > +static void mtk_uart_apdma_start_tx(struct mtk_chan *c) > +{ > + unsigned int len, send, left, wpt, d_wpt, tmp; > + int ret; > + > + left = mtk_uart_apdma_read(c, VFF_LEFT_SIZE); > + if (!left) { > + mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B); > + return; > + } > + > + /* Wait 1sec for flush, can't sleep */ > + ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp, > + tmp != VFF_FLUSH_B, 0, 1000000); > + if (ret) > + dev_warn(c->vc.chan.device->dev, "tx: fail, debug=0x%x\n", > + mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); > + > + send = min_t(unsigned int, left, c->desc->avail_len); > + wpt = mtk_uart_apdma_read(c, VFF_WPT); > + len = mtk_uart_apdma_read(c, VFF_LEN); > + > + d_wpt = wpt + send; > + if ((d_wpt & VFF_RING_SIZE) >= len) { > + d_wpt = d_wpt - len; > + d_wpt = d_wpt ^ VFF_RING_WRAP; > + } > + mtk_uart_apdma_write(c, VFF_WPT, d_wpt); > + > + c->desc->avail_len -= send; > + > + mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B); > + if (mtk_uart_apdma_read(c, VFF_FLUSH) == 0U) > + mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B); > +} > + > +static void mtk_uart_apdma_start_rx(struct mtk_chan *c) > +{ > + struct mtk_uart_apdma_desc *d = c->desc; > + unsigned int len, wg, rg; > + int cnt; > + > + if ((mtk_uart_apdma_read(c, VFF_VALID_SIZE) == 0U) || > + !d || !vchan_next_desc(&c->vc)) > + return; > + > + len = mtk_uart_apdma_read(c, VFF_LEN); > + rg = mtk_uart_apdma_read(c, VFF_RPT); > + wg = mtk_uart_apdma_read(c, VFF_WPT); > + cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE); > + /* > + * The buffer is ring buffer. If wrap bit different, > + * represents the start of the next cycle for WPT > + */ > + if ((rg ^ wg) & VFF_RING_WRAP) > + cnt += len; > + > + c->rx_status = cnt; > + mtk_uart_apdma_write(c, VFF_RPT, wg); > + > + list_del(&d->vd.node); > + vchan_cookie_complete(&d->vd); > +} > + > +static irqreturn_t mtk_uart_apdma_irq_handler(int irq, void *dev_id) > +{ > + struct dma_chan *chan = (struct dma_chan *)dev_id; > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct mtk_uart_apdma_desc *d; > + unsigned long flags; > + > + spin_lock_irqsave(&c->vc.lock, flags); > + if (c->dir == DMA_DEV_TO_MEM) { > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); > + mtk_uart_apdma_start_rx(c); > + } else if (c->dir == DMA_MEM_TO_DEV) { > + d = c->desc; > + > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); > + > + if (d->avail_len != 0U) { > + mtk_uart_apdma_start_tx(c); > + } else { > + list_del(&d->vd.node); > + vchan_cookie_complete(&d->vd); > + } > + } > + spin_unlock_irqrestore(&c->vc.lock, flags); > + > + return IRQ_HANDLED; > +} > + > +static int mtk_uart_apdma_alloc_chan_resources(struct dma_chan *chan) > +{ > + struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device); > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + unsigned int tmp; > + int ret; > + > + pm_runtime_get_sync(mtkd->ddev.dev); > + > + mtk_uart_apdma_write(c, VFF_ADDR, 0); > + mtk_uart_apdma_write(c, VFF_THRE, 0); > + mtk_uart_apdma_write(c, VFF_LEN, 0); > + mtk_uart_apdma_write(c, VFF_RST, VFF_WARM_RST_B); > + > + ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100); > + if (ret) { > + dev_err(chan->device->dev, "dma reset: fail, timeout\n"); > + return ret; > + } > + > + if (!c->requested) { > + c->requested = true; > + ret = request_irq(mtkd->dma_irq[chan->chan_id], > + mtk_uart_apdma_irq_handler, IRQF_TRIGGER_NONE, > + KBUILD_MODNAME, chan); > + if (ret < 0) { > + dev_err(chan->device->dev, "Can't request dma IRQ\n"); > + return -EINVAL; > + } > + } > + > + if (mtkd->support_33bits) > + mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B); > + > + return ret; > +} > + > +static void mtk_uart_apdma_free_chan_resources(struct dma_chan *chan) > +{ > + struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device); > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + > + if (c->requested) { > + c->requested = false; > + free_irq(mtkd->dma_irq[chan->chan_id], chan); > + } > + > + tasklet_kill(&c->vc.task); > + > + vchan_free_chan_resources(&c->vc); > + > + pm_runtime_put_sync(mtkd->ddev.dev); > +} > + > +static enum dma_status mtk_uart_apdma_tx_status(struct dma_chan *chan, > + dma_cookie_t cookie, > + struct dma_tx_state *txstate) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + enum dma_status ret; > + unsigned long flags; > + > + if (!txstate) > + return DMA_ERROR; > + > + ret = dma_cookie_status(chan, cookie, txstate); > + spin_lock_irqsave(&c->vc.lock, flags); > + if (ret == DMA_IN_PROGRESS) { > + c->rx_status = mtk_uart_apdma_read(c, VFF_RPT) & VFF_RING_SIZE; > + dma_set_residue(txstate, c->rx_status); > + } else if (ret == DMA_COMPLETE && c->dir == DMA_DEV_TO_MEM) { > + dma_set_residue(txstate, c->rx_status); > + } else { > + dma_set_residue(txstate, 0); > + } > + spin_unlock_irqrestore(&c->vc.lock, flags); > + > + return ret; > +} > + > +static void mtk_uart_apdma_config_write(struct dma_chan *chan, > + struct dma_slave_config *cfg, > + enum dma_transfer_direction dir) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct mtk_uart_apdmadev *mtkd = > + to_mtk_uart_apdma_dev(c->vc.chan.device); > + unsigned int tmp; > + > + if (mtk_uart_apdma_read(c, VFF_EN) == VFF_EN_B) > + return; > + > + c->dir = dir; > + > + if (dir == DMA_DEV_TO_MEM) { > + tmp = cfg->src_addr_width * 1024; > + > + mtk_uart_apdma_write(c, VFF_ADDR, cfg->src_addr); > + mtk_uart_apdma_write(c, VFF_LEN, tmp); > + mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(tmp)); > + mtk_uart_apdma_write(c, VFF_INT_EN, > + VFF_RX_INT_EN0_B | VFF_RX_INT_EN1_B); > + mtk_uart_apdma_write(c, VFF_RPT, 0); > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); > + } else if (dir == DMA_MEM_TO_DEV) { > + tmp = cfg->dst_addr_width * 1024; > + > + mtk_uart_apdma_write(c, VFF_ADDR, cfg->dst_addr); > + mtk_uart_apdma_write(c, VFF_LEN, tmp); > + mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(tmp)); > + mtk_uart_apdma_write(c, VFF_WPT, 0); > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); > + } > + > + mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B); > + > + if (mtkd->support_33bits) > + mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_B); > + > + if (mtk_uart_apdma_read(c, VFF_EN) != VFF_EN_B) > + dev_err(chan->device->dev, "dir[%d] fail\n", dir); > +} > + > +/* > + * dmaengine_prep_slave_single will call the function. and sglen is 1. > + * 8250 uart using one ring buffer, and deal with one sg. > + */ > +static struct dma_async_tx_descriptor *mtk_uart_apdma_prep_slave_sg > + (struct dma_chan *chan, struct scatterlist *sgl, > + unsigned int sglen, enum dma_transfer_direction dir, > + unsigned long tx_flags, void *context) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct mtk_uart_apdma_desc *d; > + > + if (!is_slave_direction(dir)) > + return NULL; > + > + mtk_uart_apdma_config_write(chan, &c->cfg, dir); > + > + /* Now allocate and setup the descriptor */ > + d = kzalloc(sizeof(*d), GFP_ATOMIC); > + if (!d) > + return NULL; > + > + /* sglen is 1 */ > + d->avail_len = sg_dma_len(sgl); > + > + return vchan_tx_prep(&c->vc, &d->vd, tx_flags); > +} > + > +static void mtk_uart_apdma_issue_pending(struct dma_chan *chan) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + struct virt_dma_desc *vd; > + unsigned long flags; > + > + spin_lock_irqsave(&c->vc.lock, flags); > + if (vchan_issue_pending(&c->vc)) { > + vd = vchan_next_desc(&c->vc); > + c->desc = to_mtk_uart_apdma_desc(&vd->tx); > + } > + > + if (c->dir == DMA_DEV_TO_MEM) > + mtk_uart_apdma_start_rx(c); > + else if (c->dir == DMA_MEM_TO_DEV) > + mtk_uart_apdma_start_tx(c); > + > + spin_unlock_irqrestore(&c->vc.lock, flags); > +} > + > +static int mtk_uart_apdma_slave_config(struct dma_chan *chan, > + struct dma_slave_config *config) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + > + memcpy(&c->cfg, config, sizeof(*config)); > + > + return 0; > +} > + > +static int mtk_uart_apdma_terminate_all(struct dma_chan *chan) > +{ > + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); > + unsigned long flags; > + unsigned int tmp; > + int ret; > + > + spin_lock_irqsave(&c->vc.lock, flags); > + > + mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B); > + /* Wait 1sec for flush, can't sleep */ > + ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp, > + tmp != VFF_FLUSH_B, 0, 1000000); > + if (ret) > + dev_err(c->vc.chan.device->dev, "flush: fail, debug=0x%x\n", > + mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); > + > + /* set stop as 1 -> wait until en is 0 -> set stop as 0 */ > + mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_B); > + ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100); > + if (ret) > + dev_err(c->vc.chan.device->dev, "stop: fail, debug=0x%x\n", > + mtk_uart_apdma_read(c, VFF_DEBUG_STATUS)); > + > + mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_CLR_B); > + mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B); > + > + if (c->dir == DMA_DEV_TO_MEM) > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B); > + else if (c->dir == DMA_MEM_TO_DEV) > + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B); > + > + spin_unlock_irqrestore(&c->vc.lock, flags); > + > + return 0; > +} > + > +static int mtk_uart_apdma_device_pause(struct dma_chan *chan) > +{ > + /* just for check caps pass */ > + return 0; > +} > + > +static void mtk_uart_apdma_free(struct mtk_uart_apdmadev *mtkd) > +{ > + while (!list_empty(&mtkd->ddev.channels)) { > + struct mtk_chan *c = list_first_entry(&mtkd->ddev.channels, > + struct mtk_chan, vc.chan.device_node); > + > + list_del(&c->vc.chan.device_node); > + tasklet_kill(&c->vc.task); > + } > +} > + > +static const struct of_device_id mtk_uart_apdma_match[] = { > + { .compatible = "mediatek,mt6577-uart-dma", }, > + { /* sentinel */ }, > +}; > +MODULE_DEVICE_TABLE(of, mtk_uart_apdma_match); > + > +static int mtk_uart_apdma_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct mtk_uart_apdmadev *mtkd; > + struct resource *res; > + struct mtk_chan *c; > + int bit_mask = 32, rc; > + unsigned int i; > + > + mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL); > + if (!mtkd) > + return -ENOMEM; > + > + mtkd->clk = devm_clk_get(&pdev->dev, NULL); > + if (IS_ERR(mtkd->clk)) { > + dev_err(&pdev->dev, "No clock specified\n"); > + rc = PTR_ERR(mtkd->clk); > + return rc; > + } > + > + if (of_property_read_bool(np, "dma-33bits")) > + mtkd->support_33bits = true; > + > + if (mtkd->support_33bits) > + bit_mask = 33; > + > + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(bit_mask)); > + if (rc) > + return rc; > + > + dma_cap_set(DMA_SLAVE, mtkd->ddev.cap_mask); > + mtkd->ddev.device_alloc_chan_resources = > + mtk_uart_apdma_alloc_chan_resources; > + mtkd->ddev.device_free_chan_resources = > + mtk_uart_apdma_free_chan_resources; > + mtkd->ddev.device_tx_status = mtk_uart_apdma_tx_status; > + mtkd->ddev.device_issue_pending = mtk_uart_apdma_issue_pending; > + mtkd->ddev.device_prep_slave_sg = mtk_uart_apdma_prep_slave_sg; > + mtkd->ddev.device_config = mtk_uart_apdma_slave_config; > + mtkd->ddev.device_pause = mtk_uart_apdma_device_pause; > + mtkd->ddev.device_terminate_all = mtk_uart_apdma_terminate_all; > + mtkd->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE); > + mtkd->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE); > + mtkd->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); > + mtkd->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; > + mtkd->ddev.dev = &pdev->dev; > + INIT_LIST_HEAD(&mtkd->ddev.channels); > + > + mtkd->dma_requests = MTK_UART_APDMA_NR_VCHANS; > + if (of_property_read_u32(np, "dma-requests", &mtkd->dma_requests)) { > + dev_info(&pdev->dev, > + "Using %u as missing dma-requests property\n", > + MTK_UART_APDMA_NR_VCHANS); > + } > + > + mtkd->dma_irq = devm_kcalloc(&pdev->dev, mtkd->dma_requests, > + sizeof(*mtkd->dma_irq), GFP_KERNEL); > + if (!mtkd->dma_irq) > + return -ENOMEM; > + > + for (i = 0; i < mtkd->dma_requests; i++) { > + c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL); > + if (!c) { > + rc = -ENODEV; > + goto err_no_dma; > + } > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, i); > + if (!res) { > + rc = -ENODEV; > + goto err_no_dma; > + } > + > + c->base = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(c->base)) { > + rc = PTR_ERR(c->base); > + goto err_no_dma; > + } > + c->requested = false; > + c->vc.desc_free = mtk_uart_apdma_desc_free; > + vchan_init(&c->vc, &mtkd->ddev); > + > + mtkd->dma_irq[i] = platform_get_irq(pdev, i); > + if ((int)mtkd->dma_irq[i] < 0) { > + dev_err(&pdev->dev, "failed to get IRQ[%d]\n", i); > + rc = -EINVAL; > + goto err_no_dma; > + } > + } > + > + pm_runtime_enable(&pdev->dev); > + pm_runtime_set_active(&pdev->dev); > + > + rc = dma_async_device_register(&mtkd->ddev); > + if (rc) > + goto rpm_disable; > + > + platform_set_drvdata(pdev, mtkd); > + > + /* Device-tree DMA controller registration */ > + rc = of_dma_controller_register(np, of_dma_xlate_by_chan_id, mtkd); > + if (rc) > + goto dma_remove; > + > + return rc; > + > +dma_remove: > + dma_async_device_unregister(&mtkd->ddev); > +rpm_disable: > + pm_runtime_disable(&pdev->dev); > +err_no_dma: > + mtk_uart_apdma_free(mtkd); > + return rc; > +} > + > +static int mtk_uart_apdma_remove(struct platform_device *pdev) > +{ > + struct mtk_uart_apdmadev *mtkd = platform_get_drvdata(pdev); > + > + if (pdev->dev.of_node) > + of_dma_controller_free(pdev->dev.of_node); > + > + pm_runtime_disable(&pdev->dev); > + pm_runtime_put_noidle(&pdev->dev); > + > + dma_async_device_unregister(&mtkd->ddev); > + mtk_uart_apdma_free(mtkd); > + > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int mtk_uart_apdma_suspend(struct device *dev) > +{ > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + if (!pm_runtime_suspended(dev)) > + clk_disable_unprepare(mtkd->clk); > + > + return 0; > +} > + > +static int mtk_uart_apdma_resume(struct device *dev) > +{ > + int ret; > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + if (!pm_runtime_suspended(dev)) { > + ret = clk_prepare_enable(mtkd->clk); > + if (ret) > + return ret; > + } > + > + return 0; > +} > +#endif /* CONFIG_PM_SLEEP */ > + > +#ifdef CONFIG_PM > +static int mtk_uart_apdma_runtime_suspend(struct device *dev) > +{ > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + clk_disable_unprepare(mtkd->clk); > + > + return 0; > +} > + > +static int mtk_uart_apdma_runtime_resume(struct device *dev) > +{ > + int ret; > + struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev); > + > + ret = clk_prepare_enable(mtkd->clk); > + if (ret) > + return ret; > + > + return 0; > +} > +#endif /* CONFIG_PM */ > + > +static const struct dev_pm_ops mtk_uart_apdma_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(mtk_uart_apdma_suspend, mtk_uart_apdma_resume) > + SET_RUNTIME_PM_OPS(mtk_uart_apdma_runtime_suspend, > + mtk_uart_apdma_runtime_resume, NULL) > +}; > + > +static struct platform_driver mtk_uart_apdma_driver = { > + .probe = mtk_uart_apdma_probe, > + .remove = mtk_uart_apdma_remove, > + .driver = { > + .name = KBUILD_MODNAME, > + .pm = &mtk_uart_apdma_pm_ops, > + .of_match_table = of_match_ptr(mtk_uart_apdma_match), > + }, > +}; > + > +module_platform_driver(mtk_uart_apdma_driver); > + > +MODULE_DESCRIPTION("MediaTek UART APDMA Controller Driver"); > +MODULE_AUTHOR("Long Cheng "); > +MODULE_LICENSE("GPL v2"); > + _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel