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: [v9,1/2] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support From: Long Cheng Message-Id: <1546395178-8880-2-git-send-email-long.cheng@mediatek.com> Date: Wed, 2 Jan 2019 10:12:57 +0800 To: Vinod Koul , Randy Dunlap , Rob Herring , Mark Rutland , Ryder Lee , Sean Wang , Nicolas Boichat Cc: 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 , Long Cheng List-ID: SW4gRE1BIGVuZ2luZSBmcmFtZXdvcmssIGFkZCA4MjUwIHVhcnQgZG1hIHRvIHN1cHBvcnQgTWVk aWFUZWsgdWFydC4KSWYgTWVkaWFUZWsgdWFydCBlbmFibGVkKFNFUklBTF84MjUwX01UNjU3Nyks IGFuZCB3YW50IHRvIGltcHJvdmUKdGhlIHBlcmZvcm1hbmNlLCBjYW4gZW5hYmxlIHRoZSBmdW5j dGlvbi4KClNpZ25lZC1vZmYtYnk6IExvbmcgQ2hlbmcgPGxvbmcuY2hlbmdAbWVkaWF0ZWsuY29t PgotLS0KIGRyaXZlcnMvZG1hL21lZGlhdGVrLzgyNTBfbXRrX2RtYS5jIHwgIDY1MiArKysrKysr KysrKysrKysrKysrKysrKysrKysrKysrKysrKwogZHJpdmVycy9kbWEvbWVkaWF0ZWsvS2NvbmZp ZyAgICAgICAgfCAgIDExICsKIGRyaXZlcnMvZG1hL21lZGlhdGVrL01ha2VmaWxlICAgICAgIHwg ICAgMSArCiAzIGZpbGVzIGNoYW5nZWQsIDY2NCBpbnNlcnRpb25zKCspCiBjcmVhdGUgbW9kZSAx MDA2NDQgZHJpdmVycy9kbWEvbWVkaWF0ZWsvODI1MF9tdGtfZG1hLmMKCmRpZmYgLS1naXQgYS9k cml2ZXJzL2RtYS9tZWRpYXRlay84MjUwX210a19kbWEuYyBiL2RyaXZlcnMvZG1hL21lZGlhdGVr LzgyNTBfbXRrX2RtYS5jCm5ldyBmaWxlIG1vZGUgMTAwNjQ0CmluZGV4IDAwMDAwMDAuLmRiZjgx MWUKLS0tIC9kZXYvbnVsbAorKysgYi9kcml2ZXJzL2RtYS9tZWRpYXRlay84MjUwX210a19kbWEu YwpAQCAtMCwwICsxLDY1MiBAQAorLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEdQTC0yLjAK Ky8qCisgKiBNZWRpYVRlayA4MjUwIERNQSBkcml2ZXIuCisgKgorICogQ29weXJpZ2h0IChjKSAy MDE4IE1lZGlhVGVrIEluYy4KKyAqIEF1dGhvcjogTG9uZyBDaGVuZyA8bG9uZy5jaGVuZ0BtZWRp YXRlay5jb20+CisgKi8KKworI2luY2x1ZGUgPGxpbnV4L2Nsay5oPgorI2luY2x1ZGUgPGxpbnV4 L2RtYWVuZ2luZS5oPgorI2luY2x1ZGUgPGxpbnV4L2RtYS1tYXBwaW5nLmg+CisjaW5jbHVkZSA8 bGludXgvZXJyLmg+CisjaW5jbHVkZSA8bGludXgvaW5pdC5oPgorI2luY2x1ZGUgPGxpbnV4L2lu dGVycnVwdC5oPgorI2luY2x1ZGUgPGxpbnV4L2lvcG9sbC5oPgorI2luY2x1ZGUgPGxpbnV4L2tl cm5lbC5oPgorI2luY2x1ZGUgPGxpbnV4L2xpc3QuaD4KKyNpbmNsdWRlIDxsaW51eC9tb2R1bGUu aD4KKyNpbmNsdWRlIDxsaW51eC9vZl9kZXZpY2UuaD4KKyNpbmNsdWRlIDxsaW51eC9vZl9kbWEu aD4KKyNpbmNsdWRlIDxsaW51eC9wbGF0Zm9ybV9kZXZpY2UuaD4KKyNpbmNsdWRlIDxsaW51eC9w bV9ydW50aW1lLmg+CisjaW5jbHVkZSA8bGludXgvc2xhYi5oPgorI2luY2x1ZGUgPGxpbnV4L3Nw aW5sb2NrLmg+CisKKyNpbmNsdWRlICIuLi92aXJ0LWRtYS5oIgorCisjZGVmaW5lIE1US19VQVJU X0FQRE1BX0NIQU5ORUxTCQkoQ09ORklHX1NFUklBTF84MjUwX05SX1VBUlRTICogMikKKworI2Rl ZmluZSBWRkZfRU5fQgkJQklUKDApCisjZGVmaW5lIFZGRl9TVE9QX0IJCUJJVCgwKQorI2RlZmlu ZSBWRkZfRkxVU0hfQgkJQklUKDApCisjZGVmaW5lIFZGRl80R19TVVBQT1JUX0IJQklUKDApCisj ZGVmaW5lIFZGRl9SWF9JTlRfRU4wX0IJQklUKDApCS8qcnggdmFsaWQgc2l6ZSA+PSAgdmZmIHRo cmUqLworI2RlZmluZSBWRkZfUlhfSU5UX0VOMV9CCUJJVCgxKQorI2RlZmluZSBWRkZfVFhfSU5U X0VOX0IJCUJJVCgwKQkvKnR4IGxlZnQgc2l6ZSA+PSB2ZmYgdGhyZSovCisjZGVmaW5lIFZGRl9X QVJNX1JTVF9CCQlCSVQoMCkKKyNkZWZpbmUgVkZGX1JYX0lOVF9GTEFHX0NMUl9CCShCSVQoMCkg fCBCSVQoMSkpCisjZGVmaW5lIFZGRl9UWF9JTlRfRkxBR19DTFJfQgkwCisjZGVmaW5lIFZGRl9T VE9QX0NMUl9CCQkwCisjZGVmaW5lIFZGRl9JTlRfRU5fQ0xSX0IJMAorI2RlZmluZSBWRkZfNEdf U1VQUE9SVF9DTFJfQgkwCisKKy8qIGludGVycnVwdCB0cmlnZ2VyIGxldmVsIGZvciB0eCAqLwor I2RlZmluZSBWRkZfVFhfVEhSRShuKQkJKChuKSAqIDcgLyA4KQorLyogaW50ZXJydXB0IHRyaWdn ZXIgbGV2ZWwgZm9yIHJ4ICovCisjZGVmaW5lIFZGRl9SWF9USFJFKG4pCQkoKG4pICogMyAvIDQp CisKKyNkZWZpbmUgVkZGX1JJTkdfU0laRQkweGZmZmZVCisvKiBpbnZlcnQgdGhpcyBiaXQgd2hl biB3cmFwIHJpbmcgaGVhZCBhZ2FpbiovCisjZGVmaW5lIFZGRl9SSU5HX1dSQVAJMHgxMDAwMFUK KworI2RlZmluZSBWRkZfSU5UX0ZMQUcJCTB4MDAKKyNkZWZpbmUgVkZGX0lOVF9FTgkJMHgwNAor I2RlZmluZSBWRkZfRU4JCQkweDA4CisjZGVmaW5lIFZGRl9SU1QJCQkweDBjCisjZGVmaW5lIFZG Rl9TVE9QCQkweDEwCisjZGVmaW5lIFZGRl9GTFVTSAkJMHgxNAorI2RlZmluZSBWRkZfQUREUgkJ MHgxYworI2RlZmluZSBWRkZfTEVOCQkJMHgyNAorI2RlZmluZSBWRkZfVEhSRQkJMHgyOAorI2Rl ZmluZSBWRkZfV1BUCQkJMHgyYworI2RlZmluZSBWRkZfUlBUCQkJMHgzMAorLypUWDogdGhlIGJ1 ZmZlciBzaXplIEhXIGNhbiByZWFkLiBSWDogdGhlIGJ1ZmZlciBzaXplIFNXIGNhbiByZWFkLiov CisjZGVmaW5lIFZGRl9WQUxJRF9TSVpFCQkweDNjCisvKlRYOiB0aGUgYnVmZmVyIHNpemUgU1cg Y2FuIHdyaXRlLiBSWDogdGhlIGJ1ZmZlciBzaXplIEhXIGNhbiB3cml0ZS4qLworI2RlZmluZSBW RkZfTEVGVF9TSVpFCQkweDQwCisjZGVmaW5lIFZGRl9ERUJVR19TVEFUVVMJMHg1MAorI2RlZmlu ZSBWRkZfNEdfU1VQUE9SVAkJMHg1NAorCitzdHJ1Y3QgbXRrX3VhcnRfYXBkbWFkZXYgeworCXN0 cnVjdCBkbWFfZGV2aWNlIGRkZXY7CisJc3RydWN0IGNsayAqY2xrOworCWJvb2wgc3VwcG9ydF8z M2JpdHM7CisJdW5zaWduZWQgaW50IGRtYV9pcnFbTVRLX1VBUlRfQVBETUFfQ0hBTk5FTFNdOwor fTsKKworc3RydWN0IG10a191YXJ0X2FwZG1hX2Rlc2MgeworCXN0cnVjdCB2aXJ0X2RtYV9kZXNj IHZkOworCisJdW5zaWduZWQgaW50IGF2YWlsX2xlbjsKK307CisKK3N0cnVjdCBtdGtfY2hhbiB7 CisJc3RydWN0IHZpcnRfZG1hX2NoYW4gdmM7CisJc3RydWN0IGRtYV9zbGF2ZV9jb25maWcJY2Zn OworCXZvaWQgX19pb21lbSAqYmFzZTsKKwlzdHJ1Y3QgbXRrX3VhcnRfYXBkbWFfZGVzYyAqZGVz YzsKKworCWJvb2wgcmVxdWVzdGVkOworCisJdW5zaWduZWQgaW50IHJ4X3N0YXR1czsKK307CisK K3N0YXRpYyBpbmxpbmUgc3RydWN0IG10a191YXJ0X2FwZG1hZGV2ICoKK3RvX210a191YXJ0X2Fw ZG1hX2RldihzdHJ1Y3QgZG1hX2RldmljZSAqZCkKK3sKKwlyZXR1cm4gY29udGFpbmVyX29mKGQs IHN0cnVjdCBtdGtfdWFydF9hcGRtYWRldiwgZGRldik7Cit9CisKK3N0YXRpYyBpbmxpbmUgc3Ry dWN0IG10a19jaGFuICp0b19tdGtfdWFydF9hcGRtYV9jaGFuKHN0cnVjdCBkbWFfY2hhbiAqYykK K3sKKwlyZXR1cm4gY29udGFpbmVyX29mKGMsIHN0cnVjdCBtdGtfY2hhbiwgdmMuY2hhbik7Cit9 CisKK3N0YXRpYyBpbmxpbmUgc3RydWN0IG10a191YXJ0X2FwZG1hX2Rlc2MgKnRvX210a191YXJ0 X2FwZG1hX2Rlc2MKKwkoc3RydWN0IGRtYV9hc3luY190eF9kZXNjcmlwdG9yICp0KQoreworCXJl dHVybiBjb250YWluZXJfb2YodCwgc3RydWN0IG10a191YXJ0X2FwZG1hX2Rlc2MsIHZkLnR4KTsK K30KKworc3RhdGljIHZvaWQgbXRrX3VhcnRfYXBkbWFfd3JpdGUoc3RydWN0IG10a19jaGFuICpj LAorCQkJICAgICAgIHVuc2lnbmVkIGludCByZWcsIHVuc2lnbmVkIGludCB2YWwpCit7CisJd3Jp dGVsKHZhbCwgYy0+YmFzZSArIHJlZyk7Cit9CisKK3N0YXRpYyB1bnNpZ25lZCBpbnQgbXRrX3Vh cnRfYXBkbWFfcmVhZChzdHJ1Y3QgbXRrX2NoYW4gKmMsIHVuc2lnbmVkIGludCByZWcpCit7CisJ cmV0dXJuIHJlYWRsKGMtPmJhc2UgKyByZWcpOworfQorCitzdGF0aWMgdm9pZCBtdGtfdWFydF9h cGRtYV9kZXNjX2ZyZWUoc3RydWN0IHZpcnRfZG1hX2Rlc2MgKnZkKQoreworCXN0cnVjdCBkbWFf Y2hhbiAqY2hhbiA9IHZkLT50eC5jaGFuOworCXN0cnVjdCBtdGtfY2hhbiAqYyA9IHRvX210a191 YXJ0X2FwZG1hX2NoYW4oY2hhbik7CisKKwlrZnJlZShjLT5kZXNjKTsKK30KKworc3RhdGljIHZv aWQgbXRrX3VhcnRfYXBkbWFfc3RhcnRfdHgoc3RydWN0IG10a19jaGFuICpjKQoreworCXVuc2ln bmVkIGludCBsZW4sIHNlbmQsIGxlZnQsIHdwdCwgZF93cHQsIHRtcDsKKwlpbnQgcmV0OworCisJ bGVmdCA9IG10a191YXJ0X2FwZG1hX3JlYWQoYywgVkZGX0xFRlRfU0laRSk7CisJaWYgKCFsZWZ0 KSB7CisJCW10a191YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9JTlRfRU4sIFZGRl9UWF9JTlRfRU5f Qik7CisJCXJldHVybjsKKwl9CisKKwkvKiBXYWl0IDFzZWMgZm9yIGZsdXNoLCAgY2FuJ3Qgc2xl ZXAqLworCXJldCA9IHJlYWR4X3BvbGxfdGltZW91dChyZWFkbCwgYy0+YmFzZSArIFZGRl9GTFVT SCwgdG1wLAorCQkJdG1wICE9IFZGRl9GTFVTSF9CLCAwLCAxMDAwMDAwKTsKKwlpZiAocmV0KQor CQlkZXZfd2FybihjLT52Yy5jaGFuLmRldmljZS0+ZGV2LCAidHg6IGZhaWwsIGRlYnVnPTB4JXhc biIsCisJCQltdGtfdWFydF9hcGRtYV9yZWFkKGMsIFZGRl9ERUJVR19TVEFUVVMpKTsKKworCXNl bmQgPSBtaW5fdCh1bnNpZ25lZCBpbnQsIGxlZnQsIGMtPmRlc2MtPmF2YWlsX2xlbik7CisJd3B0 ID0gbXRrX3VhcnRfYXBkbWFfcmVhZChjLCBWRkZfV1BUKTsKKwlsZW4gPSBtdGtfdWFydF9hcGRt YV9yZWFkKGMsIFZGRl9MRU4pOworCisJZF93cHQgPSB3cHQgKyBzZW5kOworCWlmICgoZF93cHQg JiBWRkZfUklOR19TSVpFKSA+PSBsZW4pIHsKKwkJZF93cHQgPSBkX3dwdCAtIGxlbjsKKwkJZF93 cHQgPSBkX3dwdCBeIFZGRl9SSU5HX1dSQVA7CisJfQorCW10a191YXJ0X2FwZG1hX3dyaXRlKGMs IFZGRl9XUFQsIGRfd3B0KTsKKworCWMtPmRlc2MtPmF2YWlsX2xlbiAtPSBzZW5kOworCisJbXRr X3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX0lOVF9FTiwgVkZGX1RYX0lOVF9FTl9CKTsKKwlpZiAo bXRrX3VhcnRfYXBkbWFfcmVhZChjLCBWRkZfRkxVU0gpID09IDBVKQorCQltdGtfdWFydF9hcGRt YV93cml0ZShjLCBWRkZfRkxVU0gsIFZGRl9GTFVTSF9CKTsKK30KKworc3RhdGljIHZvaWQgbXRr X3VhcnRfYXBkbWFfc3RhcnRfcngoc3RydWN0IG10a19jaGFuICpjKQoreworCXN0cnVjdCBtdGtf dWFydF9hcGRtYV9kZXNjICpkID0gYy0+ZGVzYzsKKwl1bnNpZ25lZCBpbnQgbGVuLCB3Zywgcmcs IGNudDsKKworCWlmICgobXRrX3VhcnRfYXBkbWFfcmVhZChjLCBWRkZfVkFMSURfU0laRSkgPT0g MFUpIHx8CisJCSFkIHx8ICF2Y2hhbl9uZXh0X2Rlc2MoJmMtPnZjKSkKKwkJcmV0dXJuOworCisJ bGVuID0gbXRrX3VhcnRfYXBkbWFfcmVhZChjLCBWRkZfTEVOKTsKKwlyZyA9IG10a191YXJ0X2Fw ZG1hX3JlYWQoYywgVkZGX1JQVCk7CisJd2cgPSBtdGtfdWFydF9hcGRtYV9yZWFkKGMsIFZGRl9X UFQpOworCWlmICgocmcgXiB3ZykgJiBWRkZfUklOR19XUkFQKQorCQljbnQgPSAod2cgJiBWRkZf UklOR19TSVpFKSArIGxlbiAtIChyZyAmIFZGRl9SSU5HX1NJWkUpOworCWVsc2UKKwkJY250ID0g KHdnICYgVkZGX1JJTkdfU0laRSkgLSAocmcgJiBWRkZfUklOR19TSVpFKTsKKworCWMtPnJ4X3N0 YXR1cyA9IGNudDsKKwltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfUlBULCB3Zyk7CisKKwls aXN0X2RlbCgmZC0+dmQubm9kZSk7CisJdmNoYW5fY29va2llX2NvbXBsZXRlKCZkLT52ZCk7Cit9 CisKK3N0YXRpYyBpcnFyZXR1cm5fdCBtdGtfdWFydF9hcGRtYV9pcnFfaGFuZGxlcihpbnQgaXJx LCB2b2lkICpkZXZfaWQpCit7CisJc3RydWN0IGRtYV9jaGFuICpjaGFuID0gKHN0cnVjdCBkbWFf Y2hhbiAqKWRldl9pZDsKKwlzdHJ1Y3QgbXRrX2NoYW4gKmMgPSB0b19tdGtfdWFydF9hcGRtYV9j aGFuKGNoYW4pOworCXN0cnVjdCBtdGtfdWFydF9hcGRtYV9kZXNjICpkOworCXVuc2lnbmVkIGxv bmcgZmxhZ3M7CisKKwlzcGluX2xvY2tfaXJxc2F2ZSgmYy0+dmMubG9jaywgZmxhZ3MpOworCWlm IChjLT5jZmcuZGlyZWN0aW9uID09IERNQV9ERVZfVE9fTUVNKSB7CisJCW10a191YXJ0X2FwZG1h X3dyaXRlKGMsIFZGRl9JTlRfRkxBRywgVkZGX1JYX0lOVF9GTEFHX0NMUl9CKTsKKwkJbXRrX3Vh cnRfYXBkbWFfc3RhcnRfcngoYyk7CisJfSBlbHNlIGlmIChjLT5jZmcuZGlyZWN0aW9uID09IERN QV9NRU1fVE9fREVWKSB7CisJCWQgPSBjLT5kZXNjOworCisJCW10a191YXJ0X2FwZG1hX3dyaXRl KGMsIFZGRl9JTlRfRkxBRywgVkZGX1RYX0lOVF9GTEFHX0NMUl9CKTsKKworCQlpZiAoZC0+YXZh aWxfbGVuICE9IDBVKSB7CisJCQltdGtfdWFydF9hcGRtYV9zdGFydF90eChjKTsKKwkJfSBlbHNl IHsKKwkJCWxpc3RfZGVsKCZkLT52ZC5ub2RlKTsKKwkJCXZjaGFuX2Nvb2tpZV9jb21wbGV0ZSgm ZC0+dmQpOworCQl9CisJfQorCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUoJmMtPnZjLmxvY2ssIGZs YWdzKTsKKworCXJldHVybiBJUlFfSEFORExFRDsKK30KKworc3RhdGljIGludCBtdGtfdWFydF9h cGRtYV9hbGxvY19jaGFuX3Jlc291cmNlcyhzdHJ1Y3QgZG1hX2NoYW4gKmNoYW4pCit7CisJc3Ry dWN0IG10a191YXJ0X2FwZG1hZGV2ICptdGtkID0gdG9fbXRrX3VhcnRfYXBkbWFfZGV2KGNoYW4t PmRldmljZSk7CisJc3RydWN0IG10a19jaGFuICpjID0gdG9fbXRrX3VhcnRfYXBkbWFfY2hhbihj aGFuKTsKKwl1MzIgdG1wOworCWludCByZXQ7CisKKwlwbV9ydW50aW1lX2dldF9zeW5jKG10a2Qt PmRkZXYuZGV2KTsKKworCW10a191YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9BRERSLCAwKTsKKwlt dGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfVEhSRSwgMCk7CisJbXRrX3VhcnRfYXBkbWFfd3Jp dGUoYywgVkZGX0xFTiwgMCk7CisJbXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX1JTVCwgVkZG X1dBUk1fUlNUX0IpOworCisJcmV0ID0gcmVhZHhfcG9sbF90aW1lb3V0KHJlYWRsLCBjLT5iYXNl ICsgVkZGX0VOLCB0bXAsCisJCQl0bXAgPT0gMCwgMTAsIDEwMCk7CisJaWYgKHJldCkgeworCQlk ZXZfZXJyKGNoYW4tPmRldmljZS0+ZGV2LCAiZG1hIHJlc2V0OiBmYWlsLCB0aW1lb3V0XG4iKTsK KwkJcmV0dXJuIHJldDsKKwl9CisKKwlpZiAoIWMtPnJlcXVlc3RlZCkgeworCQljLT5yZXF1ZXN0 ZWQgPSB0cnVlOworCQlyZXQgPSByZXF1ZXN0X2lycShtdGtkLT5kbWFfaXJxW2NoYW4tPmNoYW5f aWRdLAorCQkJCSAgbXRrX3VhcnRfYXBkbWFfaXJxX2hhbmRsZXIsIElSUUZfVFJJR0dFUl9OT05F LAorCQkJCSAgS0JVSUxEX01PRE5BTUUsIGNoYW4pOworCQlpZiAocmV0IDwgMCkgeworCQkJZGV2 X2VycihjaGFuLT5kZXZpY2UtPmRldiwgIkNhbid0IHJlcXVlc3QgZG1hIElSUVxuIik7CisJCQly ZXR1cm4gLUVJTlZBTDsKKwkJfQorCX0KKworCWlmIChtdGtkLT5zdXBwb3J0XzMzYml0cykKKwkJ bXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGXzRHX1NVUFBPUlQsIFZGRl80R19TVVBQT1JUX0NM Ul9CKTsKKworCXJldHVybiByZXQ7Cit9CisKK3N0YXRpYyB2b2lkIG10a191YXJ0X2FwZG1hX2Zy ZWVfY2hhbl9yZXNvdXJjZXMoc3RydWN0IGRtYV9jaGFuICpjaGFuKQoreworCXN0cnVjdCBtdGtf dWFydF9hcGRtYWRldiAqbXRrZCA9IHRvX210a191YXJ0X2FwZG1hX2RldihjaGFuLT5kZXZpY2Up OworCXN0cnVjdCBtdGtfY2hhbiAqYyA9IHRvX210a191YXJ0X2FwZG1hX2NoYW4oY2hhbik7CisK KwlpZiAoYy0+cmVxdWVzdGVkKSB7CisJCWMtPnJlcXVlc3RlZCA9IGZhbHNlOworCQlmcmVlX2ly cShtdGtkLT5kbWFfaXJxW2NoYW4tPmNoYW5faWRdLCBjaGFuKTsKKwl9CisKKwl0YXNrbGV0X2tp bGwoJmMtPnZjLnRhc2spOworCisJdmNoYW5fZnJlZV9jaGFuX3Jlc291cmNlcygmYy0+dmMpOwor CisJcG1fcnVudGltZV9wdXRfc3luYyhtdGtkLT5kZGV2LmRldik7Cit9CisKK3N0YXRpYyBlbnVt IGRtYV9zdGF0dXMgbXRrX3VhcnRfYXBkbWFfdHhfc3RhdHVzKHN0cnVjdCBkbWFfY2hhbiAqY2hh biwKKwkJCQkJIGRtYV9jb29raWVfdCBjb29raWUsCisJCQkJCSBzdHJ1Y3QgZG1hX3R4X3N0YXRl ICp0eHN0YXRlKQoreworCXN0cnVjdCBtdGtfY2hhbiAqYyA9IHRvX210a191YXJ0X2FwZG1hX2No YW4oY2hhbik7CisJZW51bSBkbWFfc3RhdHVzIHJldDsKKwl1bnNpZ25lZCBsb25nIGZsYWdzOwor CisJaWYgKCF0eHN0YXRlKQorCQlyZXR1cm4gRE1BX0VSUk9SOworCisJcmV0ID0gZG1hX2Nvb2tp ZV9zdGF0dXMoY2hhbiwgY29va2llLCB0eHN0YXRlKTsKKwlzcGluX2xvY2tfaXJxc2F2ZSgmYy0+ dmMubG9jaywgZmxhZ3MpOworCWlmIChyZXQgPT0gRE1BX0lOX1BST0dSRVNTKSB7CisJCWMtPnJ4 X3N0YXR1cyA9IG10a191YXJ0X2FwZG1hX3JlYWQoYywgVkZGX1JQVCkgJiBWRkZfUklOR19TSVpF OworCQlkbWFfc2V0X3Jlc2lkdWUodHhzdGF0ZSwgYy0+cnhfc3RhdHVzKTsKKwl9IGVsc2UgaWYg KHJldCA9PSBETUFfQ09NUExFVEUgJiYgYy0+Y2ZnLmRpcmVjdGlvbiA9PSBETUFfREVWX1RPX01F TSkgeworCQlkbWFfc2V0X3Jlc2lkdWUodHhzdGF0ZSwgYy0+cnhfc3RhdHVzKTsKKwl9IGVsc2Ug eworCQlkbWFfc2V0X3Jlc2lkdWUodHhzdGF0ZSwgMCk7CisJfQorCXNwaW5fdW5sb2NrX2lycXJl c3RvcmUoJmMtPnZjLmxvY2ssIGZsYWdzKTsKKworCXJldHVybiByZXQ7Cit9CisKKy8qCisgKiBk bWFlbmdpbmVfcHJlcF9zbGF2ZV9zaW5nbGUgd2lsbCBjYWxsIHRoZSBmdW5jdGlvbi4gYW5kIHNn bGVuIGlzIDEuCisgKiA4MjUwIHVhcnQgdXNpbmcgb25lIHJpbmcgYnVmZmVyLCBhbmQgZGVhbCB3 aXRoIG9uZSBzZy4KKyAqLworc3RhdGljIHN0cnVjdCBkbWFfYXN5bmNfdHhfZGVzY3JpcHRvciAq bXRrX3VhcnRfYXBkbWFfcHJlcF9zbGF2ZV9zZworCShzdHJ1Y3QgZG1hX2NoYW4gKmNoYW4sIHN0 cnVjdCBzY2F0dGVybGlzdCAqc2dsLAorCXVuc2lnbmVkIGludCBzZ2xlbiwgZW51bSBkbWFfdHJh bnNmZXJfZGlyZWN0aW9uIGRpciwKKwl1bnNpZ25lZCBsb25nIHR4X2ZsYWdzLCB2b2lkICpjb250 ZXh0KQoreworCXN0cnVjdCBtdGtfY2hhbiAqYyA9IHRvX210a191YXJ0X2FwZG1hX2NoYW4oY2hh bik7CisJc3RydWN0IG10a191YXJ0X2FwZG1hX2Rlc2MgKmQ7CisKKwlpZiAoKGRpciAhPSBETUFf REVWX1RPX01FTSkgJiYKKwkJKGRpciAhPSBETUFfTUVNX1RPX0RFVikpIHsKKwkJZGV2X2Vycihj aGFuLT5kZXZpY2UtPmRldiwgImJhZCBkaXJlY3Rpb25cbiIpOworCQlyZXR1cm4gTlVMTDsKKwl9 CisKKwkvKiBOb3cgYWxsb2NhdGUgYW5kIHNldHVwIHRoZSBkZXNjcmlwdG9yICovCisJZCA9IGt6 YWxsb2Moc2l6ZW9mKCpkKSwgR0ZQX0FUT01JQyk7CisJaWYgKCFkKQorCQlyZXR1cm4gTlVMTDsK KworCS8qIHNnbGVuIGlzIDEgKi8KKwlkLT5hdmFpbF9sZW4gPSBzZ19kbWFfbGVuKHNnbCk7CisK KwlyZXR1cm4gdmNoYW5fdHhfcHJlcCgmYy0+dmMsICZkLT52ZCwgdHhfZmxhZ3MpOworfQorCitz dGF0aWMgdm9pZCBtdGtfdWFydF9hcGRtYV9pc3N1ZV9wZW5kaW5nKHN0cnVjdCBkbWFfY2hhbiAq Y2hhbikKK3sKKwlzdHJ1Y3QgbXRrX2NoYW4gKmMgPSB0b19tdGtfdWFydF9hcGRtYV9jaGFuKGNo YW4pOworCXN0cnVjdCB2aXJ0X2RtYV9kZXNjICp2ZDsKKwl1bnNpZ25lZCBsb25nIGZsYWdzOwor CisJc3Bpbl9sb2NrX2lycXNhdmUoJmMtPnZjLmxvY2ssIGZsYWdzKTsKKwlpZiAoYy0+Y2ZnLmRp cmVjdGlvbiA9PSBETUFfREVWX1RPX01FTSkgeworCQlpZiAodmNoYW5faXNzdWVfcGVuZGluZygm Yy0+dmMpKSB7CisJCQl2ZCA9IHZjaGFuX25leHRfZGVzYygmYy0+dmMpOworCQkJYy0+ZGVzYyA9 IHRvX210a191YXJ0X2FwZG1hX2Rlc2MoJnZkLT50eCk7CisJCQltdGtfdWFydF9hcGRtYV9zdGFy dF9yeChjKTsKKwkJfQorCX0gZWxzZSBpZiAoYy0+Y2ZnLmRpcmVjdGlvbiA9PSBETUFfTUVNX1RP X0RFVikgeworCQlpZiAodmNoYW5faXNzdWVfcGVuZGluZygmYy0+dmMpKSB7CisJCQl2ZCA9IHZj aGFuX25leHRfZGVzYygmYy0+dmMpOworCQkJYy0+ZGVzYyA9IHRvX210a191YXJ0X2FwZG1hX2Rl c2MoJnZkLT50eCk7CisJCQltdGtfdWFydF9hcGRtYV9zdGFydF90eChjKTsKKwkJfQorCX0KKwlz cGluX3VubG9ja19pcnFyZXN0b3JlKCZjLT52Yy5sb2NrLCBmbGFncyk7Cit9CisKK3N0YXRpYyBp bnQgbXRrX3VhcnRfYXBkbWFfc2xhdmVfY29uZmlnKHN0cnVjdCBkbWFfY2hhbiAqY2hhbiwKKwkJ CQlzdHJ1Y3QgZG1hX3NsYXZlX2NvbmZpZyAqY2ZnKQoreworCXN0cnVjdCBtdGtfY2hhbiAqYyA9 IHRvX210a191YXJ0X2FwZG1hX2NoYW4oY2hhbik7CisJc3RydWN0IG10a191YXJ0X2FwZG1hZGV2 ICptdGtkID0KKwkJCQl0b19tdGtfdWFydF9hcGRtYV9kZXYoYy0+dmMuY2hhbi5kZXZpY2UpOwor CisJYy0+Y2ZnID0gKmNmZzsKKworCWlmIChjZmctPmRpcmVjdGlvbiA9PSBETUFfREVWX1RPX01F TSkgeworCQl1bnNpZ25lZCBpbnQgcnhfbGVuID0gY2ZnLT5zcmNfYWRkcl93aWR0aCAqIDEwMjQ7 CisKKwkJbXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX0FERFIsIGNmZy0+c3JjX2FkZHIpOwor CQltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfTEVOLCByeF9sZW4pOworCQltdGtfdWFydF9h cGRtYV93cml0ZShjLCBWRkZfVEhSRSwgVkZGX1JYX1RIUkUocnhfbGVuKSk7CisJCW10a191YXJ0 X2FwZG1hX3dyaXRlKGMsIFZGRl9JTlRfRU4sCisJCQkJVkZGX1JYX0lOVF9FTjBfQiB8IFZGRl9S WF9JTlRfRU4xX0IpOworCQltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfUlBULCAwKTsKKwkJ bXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX0lOVF9GTEFHLCBWRkZfUlhfSU5UX0ZMQUdfQ0xS X0IpOworCQltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfRU4sIFZGRl9FTl9CKTsKKwl9IGVs c2UgaWYgKGNmZy0+ZGlyZWN0aW9uID09IERNQV9NRU1fVE9fREVWKQl7CisJCXVuc2lnbmVkIGlu dCB0eF9sZW4gPSBjZmctPmRzdF9hZGRyX3dpZHRoICogMTAyNDsKKworCQltdGtfdWFydF9hcGRt YV93cml0ZShjLCBWRkZfQUREUiwgY2ZnLT5kc3RfYWRkcik7CisJCW10a191YXJ0X2FwZG1hX3dy aXRlKGMsIFZGRl9MRU4sIHR4X2xlbik7CisJCW10a191YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9U SFJFLCBWRkZfVFhfVEhSRSh0eF9sZW4pKTsKKwkJbXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZG X1dQVCwgMCk7CisJCW10a191YXJ0X2FwZG1hX3dyaXRlKGMsIFZGRl9JTlRfRkxBRywgVkZGX1RY X0lOVF9GTEFHX0NMUl9CKTsKKwkJbXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX0VOLCBWRkZf RU5fQik7CisJfQorCisJaWYgKG10a2QtPnN1cHBvcnRfMzNiaXRzKQorCQltdGtfdWFydF9hcGRt YV93cml0ZShjLCBWRkZfNEdfU1VQUE9SVCwgVkZGXzRHX1NVUFBPUlRfQik7CisKKwlpZiAobXRr X3VhcnRfYXBkbWFfcmVhZChjLCBWRkZfRU4pICE9IFZGRl9FTl9CKSB7CisJCWRldl9lcnIoY2hh bi0+ZGV2aWNlLT5kZXYsICJkaXJbJWRdIGZhaWxcbiIsIGNmZy0+ZGlyZWN0aW9uKTsKKwkJcmV0 dXJuIC1FSU5WQUw7CisJfQorCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgbXRrX3VhcnRf YXBkbWFfdGVybWluYXRlX2FsbChzdHJ1Y3QgZG1hX2NoYW4gKmNoYW4pCit7CisJc3RydWN0IG10 a19jaGFuICpjID0gdG9fbXRrX3VhcnRfYXBkbWFfY2hhbihjaGFuKTsKKwl1bnNpZ25lZCBsb25n IGZsYWdzOworCXUzMiB0bXA7CisJaW50IHJldDsKKworCXNwaW5fbG9ja19pcnFzYXZlKCZjLT52 Yy5sb2NrLCBmbGFncyk7CisKKwltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfRkxVU0gsIFZG Rl9GTFVTSF9CKTsKKwkvKiBXYWl0IDFzZWMgZm9yIGZsdXNoLCAgY2FuJ3Qgc2xlZXAqLworCXJl dCA9IHJlYWR4X3BvbGxfdGltZW91dChyZWFkbCwgYy0+YmFzZSArIFZGRl9GTFVTSCwgdG1wLAor CQkJdG1wICE9IFZGRl9GTFVTSF9CLCAwLCAxMDAwMDAwKTsKKwlpZiAocmV0KQorCQlkZXZfZXJy KGMtPnZjLmNoYW4uZGV2aWNlLT5kZXYsICJmbHVzaDogZmFpbCwgZGVidWc9MHgleFxuIiwKKwkJ CW10a191YXJ0X2FwZG1hX3JlYWQoYywgVkZGX0RFQlVHX1NUQVRVUykpOworCisJLypzZXQgc3Rv cCBhcyAxIC0+IHdhaXQgdW50aWwgZW4gaXMgMCAtPiBzZXQgc3RvcCBhcyAwKi8KKwltdGtfdWFy dF9hcGRtYV93cml0ZShjLCBWRkZfU1RPUCwgVkZGX1NUT1BfQik7CisJcmV0ID0gcmVhZHhfcG9s bF90aW1lb3V0KHJlYWRsLCBjLT5iYXNlICsgVkZGX0VOLCB0bXAsCisJCQl0bXAgPT0gMCwgMTAs IDEwMCk7CisJaWYgKHJldCkKKwkJZGV2X2VycihjLT52Yy5jaGFuLmRldmljZS0+ZGV2LCAic3Rv cDogZmFpbCwgZGVidWc9MHgleFxuIiwKKwkJCW10a191YXJ0X2FwZG1hX3JlYWQoYywgVkZGX0RF QlVHX1NUQVRVUykpOworCisJbXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX1NUT1AsIFZGRl9T VE9QX0NMUl9CKTsKKwltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfSU5UX0VOLCBWRkZfSU5U X0VOX0NMUl9CKTsKKworCWlmIChjLT5jZmcuZGlyZWN0aW9uID09IERNQV9ERVZfVE9fTUVNKQor CQltdGtfdWFydF9hcGRtYV93cml0ZShjLCBWRkZfSU5UX0ZMQUcsIFZGRl9SWF9JTlRfRkxBR19D TFJfQik7CisJZWxzZSBpZiAoYy0+Y2ZnLmRpcmVjdGlvbiA9PSBETUFfTUVNX1RPX0RFVikKKwkJ bXRrX3VhcnRfYXBkbWFfd3JpdGUoYywgVkZGX0lOVF9GTEFHLCBWRkZfVFhfSU5UX0ZMQUdfQ0xS X0IpOworCisJc3Bpbl91bmxvY2tfaXJxcmVzdG9yZSgmYy0+dmMubG9jaywgZmxhZ3MpOworCisJ cmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgbXRrX3VhcnRfYXBkbWFfZGV2aWNlX3BhdXNlKHN0 cnVjdCBkbWFfY2hhbiAqY2hhbikKK3sKKwkvKiBqdXN0IGZvciBjaGVjayBjYXBzIHBhc3MgKi8K KwlyZXR1cm4gMDsKK30KKworc3RhdGljIGludCBtdGtfdWFydF9hcGRtYV9kZXZpY2VfcmVzdW1l KHN0cnVjdCBkbWFfY2hhbiAqY2hhbikKK3sKKwkvKiBqdXN0IGZvciBjaGVjayBjYXBzIHBhc3Mg Ki8KKwlyZXR1cm4gMDsKK30KKworc3RhdGljIHZvaWQgbXRrX3VhcnRfYXBkbWFfZnJlZShzdHJ1 Y3QgbXRrX3VhcnRfYXBkbWFkZXYgKm10a2QpCit7CisJd2hpbGUgKGxpc3RfZW1wdHkoJm10a2Qt PmRkZXYuY2hhbm5lbHMpID09IDApIHsKKwkJc3RydWN0IG10a19jaGFuICpjID0gbGlzdF9maXJz dF9lbnRyeSgmbXRrZC0+ZGRldi5jaGFubmVscywKKwkJCXN0cnVjdCBtdGtfY2hhbiwgdmMuY2hh bi5kZXZpY2Vfbm9kZSk7CisKKwkJbGlzdF9kZWwoJmMtPnZjLmNoYW4uZGV2aWNlX25vZGUpOwor CQl0YXNrbGV0X2tpbGwoJmMtPnZjLnRhc2spOworCX0KK30KKworc3RhdGljIGNvbnN0IHN0cnVj dCBvZl9kZXZpY2VfaWQgbXRrX3VhcnRfYXBkbWFfbWF0Y2hbXSA9IHsKKwl7IC5jb21wYXRpYmxl ID0gIm1lZGlhdGVrLG10NjU3Ny11YXJ0LWRtYSIsIH0sCisJeyAvKiBzZW50aW5lbCAqLyB9LAor fTsKK01PRFVMRV9ERVZJQ0VfVEFCTEUob2YsIG10a191YXJ0X2FwZG1hX21hdGNoKTsKKworc3Rh dGljIGludCBtdGtfdWFydF9hcGRtYV9wcm9iZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2 KQoreworCXN0cnVjdCBtdGtfdWFydF9hcGRtYWRldiAqbXRrZDsKKwlzdHJ1Y3QgcmVzb3VyY2Ug KnJlczsKKwlzdHJ1Y3QgbXRrX2NoYW4gKmM7CisJdW5zaWduZWQgaW50IGk7CisJaW50IHJjOwor CisJbXRrZCA9IGRldm1fa3phbGxvYygmcGRldi0+ZGV2LCBzaXplb2YoKm10a2QpLCBHRlBfS0VS TkVMKTsKKwlpZiAoIW10a2QpCisJCXJldHVybiAtRU5PTUVNOworCisJbXRrZC0+Y2xrID0gZGV2 bV9jbGtfZ2V0KCZwZGV2LT5kZXYsIE5VTEwpOworCWlmIChJU19FUlIobXRrZC0+Y2xrKSkgewor CQlkZXZfZXJyKCZwZGV2LT5kZXYsICJObyBjbG9jayBzcGVjaWZpZWRcbiIpOworCQlyYyA9IFBU Ul9FUlIobXRrZC0+Y2xrKTsKKwkJcmV0dXJuIHJjOworCX0KKworCWlmIChvZl9wcm9wZXJ0eV9y ZWFkX2Jvb2wocGRldi0+ZGV2Lm9mX25vZGUsICJkbWEtMzNiaXRzIikpCisJCW10a2QtPnN1cHBv cnRfMzNiaXRzID0gdHJ1ZTsKKworCXJjID0gZG1hX3NldF9tYXNrX2FuZF9jb2hlcmVudCgmcGRl di0+ZGV2LAorCQkJCURNQV9CSVRfTUFTSygzMiB8IG10a2QtPnN1cHBvcnRfMzNiaXRzKSk7CisJ aWYgKHJjKQorCQlyZXR1cm4gcmM7CisKKwlkbWFfY2FwX3NldChETUFfU0xBVkUsIG10a2QtPmRk ZXYuY2FwX21hc2spOworCW10a2QtPmRkZXYuZGV2aWNlX2FsbG9jX2NoYW5fcmVzb3VyY2VzID0K KwkJCQltdGtfdWFydF9hcGRtYV9hbGxvY19jaGFuX3Jlc291cmNlczsKKwltdGtkLT5kZGV2LmRl dmljZV9mcmVlX2NoYW5fcmVzb3VyY2VzID0KKwkJCQltdGtfdWFydF9hcGRtYV9mcmVlX2NoYW5f cmVzb3VyY2VzOworCW10a2QtPmRkZXYuZGV2aWNlX3R4X3N0YXR1cyA9IG10a191YXJ0X2FwZG1h X3R4X3N0YXR1czsKKwltdGtkLT5kZGV2LmRldmljZV9pc3N1ZV9wZW5kaW5nID0gbXRrX3VhcnRf YXBkbWFfaXNzdWVfcGVuZGluZzsKKwltdGtkLT5kZGV2LmRldmljZV9wcmVwX3NsYXZlX3NnID0g bXRrX3VhcnRfYXBkbWFfcHJlcF9zbGF2ZV9zZzsKKwltdGtkLT5kZGV2LmRldmljZV9jb25maWcg PSBtdGtfdWFydF9hcGRtYV9zbGF2ZV9jb25maWc7CisJbXRrZC0+ZGRldi5kZXZpY2VfcGF1c2Ug PSBtdGtfdWFydF9hcGRtYV9kZXZpY2VfcGF1c2U7CisJbXRrZC0+ZGRldi5kZXZpY2VfcmVzdW1l ID0gbXRrX3VhcnRfYXBkbWFfZGV2aWNlX3Jlc3VtZTsKKwltdGtkLT5kZGV2LmRldmljZV90ZXJt aW5hdGVfYWxsID0gbXRrX3VhcnRfYXBkbWFfdGVybWluYXRlX2FsbDsKKwltdGtkLT5kZGV2LnNy Y19hZGRyX3dpZHRocyA9IEJJVChETUFfU0xBVkVfQlVTV0lEVEhfMV9CWVRFKTsKKwltdGtkLT5k ZGV2LmRzdF9hZGRyX3dpZHRocyA9IEJJVChETUFfU0xBVkVfQlVTV0lEVEhfMV9CWVRFKTsKKwlt dGtkLT5kZGV2LmRpcmVjdGlvbnMgPSBCSVQoRE1BX0RFVl9UT19NRU0pIHwgQklUKERNQV9NRU1f VE9fREVWKTsKKwltdGtkLT5kZGV2LnJlc2lkdWVfZ3JhbnVsYXJpdHkgPSBETUFfUkVTSURVRV9H UkFOVUxBUklUWV9TRUdNRU5UOworCW10a2QtPmRkZXYuZGV2ID0gJnBkZXYtPmRldjsKKwlJTklU X0xJU1RfSEVBRCgmbXRrZC0+ZGRldi5jaGFubmVscyk7CisKKwlmb3IgKGkgPSAwOyBpIDwgTVRL X1VBUlRfQVBETUFfQ0hBTk5FTFM7IGkrKykgeworCQljID0gZGV2bV9remFsbG9jKG10a2QtPmRk ZXYuZGV2LCBzaXplb2YoKmMpLCBHRlBfS0VSTkVMKTsKKwkJaWYgKCFjKSB7CisJCQlyYyA9IC1F Tk9ERVY7CisJCQlnb3RvIGVycl9ub19kbWE7CisJCX0KKworCQlyZXMgPSBwbGF0Zm9ybV9nZXRf cmVzb3VyY2UocGRldiwgSU9SRVNPVVJDRV9NRU0sIGkpOworCQlpZiAoIXJlcykgeworCQkJcmMg PSAtRU5PREVWOworCQkJZ290byBlcnJfbm9fZG1hOworCQl9CisKKwkJYy0+YmFzZSA9IGRldm1f aW9yZW1hcF9yZXNvdXJjZSgmcGRldi0+ZGV2LCByZXMpOworCQlpZiAoSVNfRVJSKGMtPmJhc2Up KSB7CisJCQlyYyA9IFBUUl9FUlIoYy0+YmFzZSk7CisJCQlnb3RvIGVycl9ub19kbWE7CisJCX0K KwkJYy0+cmVxdWVzdGVkID0gZmFsc2U7CisJCWMtPnZjLmRlc2NfZnJlZSA9IG10a191YXJ0X2Fw ZG1hX2Rlc2NfZnJlZTsKKwkJdmNoYW5faW5pdCgmYy0+dmMsICZtdGtkLT5kZGV2KTsKKworCQlt dGtkLT5kbWFfaXJxW2ldID0gcGxhdGZvcm1fZ2V0X2lycShwZGV2LCBpKTsKKwkJaWYgKChpbnQp bXRrZC0+ZG1hX2lycVtpXSA8IDApIHsKKwkJCWRldl9lcnIoJnBkZXYtPmRldiwgImZhaWxlZCB0 byBnZXQgSVJRWyVkXVxuIiwgaSk7CisJCQlyYyA9IC1FSU5WQUw7CisJCQlnb3RvIGVycl9ub19k bWE7CisJCX0KKwl9CisKKwlwbV9ydW50aW1lX2VuYWJsZSgmcGRldi0+ZGV2KTsKKwlwbV9ydW50 aW1lX3NldF9hY3RpdmUoJnBkZXYtPmRldik7CisKKwlyYyA9IGRtYV9hc3luY19kZXZpY2VfcmVn aXN0ZXIoJm10a2QtPmRkZXYpOworCWlmIChyYykKKwkJZ290byBycG1fZGlzYWJsZTsKKworCXBs YXRmb3JtX3NldF9kcnZkYXRhKHBkZXYsIG10a2QpOworCisJaWYgKHBkZXYtPmRldi5vZl9ub2Rl KSB7CisJCS8qIERldmljZS10cmVlIERNQSBjb250cm9sbGVyIHJlZ2lzdHJhdGlvbiAqLworCQly YyA9IG9mX2RtYV9jb250cm9sbGVyX3JlZ2lzdGVyKHBkZXYtPmRldi5vZl9ub2RlLAorCQkJCQkJ b2ZfZG1hX3hsYXRlX2J5X2NoYW5faWQsCisJCQkJCQltdGtkKTsKKwkJaWYgKHJjKQorCQkJZ290 byBkbWFfcmVtb3ZlOworCX0KKworCXJldHVybiByYzsKKworZG1hX3JlbW92ZToKKwlkbWFfYXN5 bmNfZGV2aWNlX3VucmVnaXN0ZXIoJm10a2QtPmRkZXYpOworcnBtX2Rpc2FibGU6CisJcG1fcnVu dGltZV9kaXNhYmxlKCZwZGV2LT5kZXYpOworZXJyX25vX2RtYToKKwltdGtfdWFydF9hcGRtYV9m cmVlKG10a2QpOworCXJldHVybiByYzsKK30KKworc3RhdGljIGludCBtdGtfdWFydF9hcGRtYV9y ZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikKK3sKKwlzdHJ1Y3QgbXRrX3VhcnRf YXBkbWFkZXYgKm10a2QgPSBwbGF0Zm9ybV9nZXRfZHJ2ZGF0YShwZGV2KTsKKworCWlmIChwZGV2 LT5kZXYub2Zfbm9kZSkKKwkJb2ZfZG1hX2NvbnRyb2xsZXJfZnJlZShwZGV2LT5kZXYub2Zfbm9k ZSk7CisKKwlwbV9ydW50aW1lX2Rpc2FibGUoJnBkZXYtPmRldik7CisJcG1fcnVudGltZV9wdXRf bm9pZGxlKCZwZGV2LT5kZXYpOworCisJZG1hX2FzeW5jX2RldmljZV91bnJlZ2lzdGVyKCZtdGtk LT5kZGV2KTsKKwltdGtfdWFydF9hcGRtYV9mcmVlKG10a2QpOworCisJcmV0dXJuIDA7Cit9CisK KyNpZmRlZiBDT05GSUdfUE1fU0xFRVAKK3N0YXRpYyBpbnQgbXRrX3VhcnRfYXBkbWFfc3VzcGVu ZChzdHJ1Y3QgZGV2aWNlICpkZXYpCit7CisJc3RydWN0IG10a191YXJ0X2FwZG1hZGV2ICptdGtk ID0gZGV2X2dldF9kcnZkYXRhKGRldik7CisKKwlpZiAoIXBtX3J1bnRpbWVfc3VzcGVuZGVkKGRl dikpCisJCWNsa19kaXNhYmxlX3VucHJlcGFyZShtdGtkLT5jbGspOworCisJcmV0dXJuIDA7Cit9 CisKK3N0YXRpYyBpbnQgbXRrX3VhcnRfYXBkbWFfcmVzdW1lKHN0cnVjdCBkZXZpY2UgKmRldikK K3sKKwlpbnQgcmV0OworCXN0cnVjdCBtdGtfdWFydF9hcGRtYWRldiAqbXRrZCA9IGRldl9nZXRf ZHJ2ZGF0YShkZXYpOworCisJaWYgKCFwbV9ydW50aW1lX3N1c3BlbmRlZChkZXYpKSB7CisJCXJl dCA9IGNsa19wcmVwYXJlX2VuYWJsZShtdGtkLT5jbGspOworCQlpZiAocmV0KQorCQkJcmV0dXJu IHJldDsKKwl9CisKKwlyZXR1cm4gMDsKK30KKyNlbmRpZiAvKiBDT05GSUdfUE1fU0xFRVAgKi8K KworI2lmZGVmIENPTkZJR19QTQorc3RhdGljIGludCBtdGtfdWFydF9hcGRtYV9ydW50aW1lX3N1 c3BlbmQoc3RydWN0IGRldmljZSAqZGV2KQoreworCXN0cnVjdCBtdGtfdWFydF9hcGRtYWRldiAq bXRrZCA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOworCisJY2xrX2Rpc2FibGVfdW5wcmVwYXJlKG10 a2QtPmNsayk7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGludCBtdGtfdWFydF9hcGRtYV9y dW50aW1lX3Jlc3VtZShzdHJ1Y3QgZGV2aWNlICpkZXYpCit7CisJaW50IHJldDsKKwlzdHJ1Y3Qg bXRrX3VhcnRfYXBkbWFkZXYgKm10a2QgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsKKworCXJldCA9 IGNsa19wcmVwYXJlX2VuYWJsZShtdGtkLT5jbGspOworCWlmIChyZXQpCisJCXJldHVybiByZXQ7 CisKKwlyZXR1cm4gMDsKK30KKyNlbmRpZiAvKiBDT05GSUdfUE0gKi8KKworc3RhdGljIGNvbnN0 IHN0cnVjdCBkZXZfcG1fb3BzIG10a191YXJ0X2FwZG1hX3BtX29wcyA9IHsKKwlTRVRfU1lTVEVN X1NMRUVQX1BNX09QUyhtdGtfdWFydF9hcGRtYV9zdXNwZW5kLCBtdGtfdWFydF9hcGRtYV9yZXN1 bWUpCisJU0VUX1JVTlRJTUVfUE1fT1BTKG10a191YXJ0X2FwZG1hX3J1bnRpbWVfc3VzcGVuZCwK KwkJCSAgIG10a191YXJ0X2FwZG1hX3J1bnRpbWVfcmVzdW1lLCBOVUxMKQorfTsKKworc3RhdGlj IHN0cnVjdCBwbGF0Zm9ybV9kcml2ZXIgbXRrX3VhcnRfYXBkbWFfZHJpdmVyID0geworCS5wcm9i ZQk9IG10a191YXJ0X2FwZG1hX3Byb2JlLAorCS5yZW1vdmUJPSBtdGtfdWFydF9hcGRtYV9yZW1v dmUsCisJLmRyaXZlciA9IHsKKwkJLm5hbWUJCT0gS0JVSUxEX01PRE5BTUUsCisJCS5wbQkJPSAm bXRrX3VhcnRfYXBkbWFfcG1fb3BzLAorCQkub2ZfbWF0Y2hfdGFibGUgPSBvZl9tYXRjaF9wdHIo bXRrX3VhcnRfYXBkbWFfbWF0Y2gpLAorCX0sCit9OworCittb2R1bGVfcGxhdGZvcm1fZHJpdmVy KG10a191YXJ0X2FwZG1hX2RyaXZlcik7CisKK01PRFVMRV9ERVNDUklQVElPTigiTWVkaWFUZWsg VUFSVCBBUERNQSBDb250cm9sbGVyIERyaXZlciIpOworTU9EVUxFX0FVVEhPUigiTG9uZyBDaGVu ZyA8bG9uZy5jaGVuZ0BtZWRpYXRlay5jb20+Iik7CitNT0RVTEVfTElDRU5TRSgiR1BMIHYyIik7 CisKZGlmZiAtLWdpdCBhL2RyaXZlcnMvZG1hL21lZGlhdGVrL0tjb25maWcgYi9kcml2ZXJzL2Rt YS9tZWRpYXRlay9LY29uZmlnCmluZGV4IDI3YmFjMGIuLjFhNTIzYzg3IDEwMDY0NAotLS0gYS9k cml2ZXJzL2RtYS9tZWRpYXRlay9LY29uZmlnCisrKyBiL2RyaXZlcnMvZG1hL21lZGlhdGVrL0tj b25maWcKQEAgLTEsNCArMSwxNSBAQAogCitjb25maWcgRE1BX01US19VQVJUCisJdHJpc3RhdGUg Ik1lZGlhVGVrIFNvQ3MgQVBETUEgc3VwcG9ydCBmb3IgVUFSVCIKKwlkZXBlbmRzIG9uIE9GICYm IFNFUklBTF84MjUwX01UNjU3NworCXNlbGVjdCBETUFfRU5HSU5FCisJc2VsZWN0IERNQV9WSVJU VUFMX0NIQU5ORUxTCisJaGVscAorCSAgU3VwcG9ydCBmb3IgdGhlIFVBUlQgRE1BIGVuZ2luZSBm b3VuZCBvbiBNZWRpYVRlayBNVEsgU29Dcy4KKwkgIHdoZW4gU0VSSUFMXzgyNTBfTVQ2NTc3IGlz IGVuYWJsZWQsIGFuZCBpZiB5b3Ugd2FudCB0byB1c2UgRE1BLAorCSAgeW91IGNhbiBlbmFibGUg dGhlIGNvbmZpZy4gdGhlIERNQSBlbmdpbmUgY2FuIG9ubHkgYmUgdXNlZAorCSAgd2l0aCBNZWRp YVRlayBTb0NzLgorCiBjb25maWcgTVRLX0hTRE1BCiAJdHJpc3RhdGUgIk1lZGlhVGVrIEhpZ2gt U3BlZWQgRE1BIGNvbnRyb2xsZXIgc3VwcG9ydCIKIAlkZXBlbmRzIG9uIEFSQ0hfTUVESUFURUsg fHwgQ09NUElMRV9URVNUCmRpZmYgLS1naXQgYS9kcml2ZXJzL2RtYS9tZWRpYXRlay9NYWtlZmls ZSBiL2RyaXZlcnMvZG1hL21lZGlhdGVrL01ha2VmaWxlCmluZGV4IDZlNzc4ZjguLjJmMmVmZDkg MTAwNjQ0Ci0tLSBhL2RyaXZlcnMvZG1hL21lZGlhdGVrL01ha2VmaWxlCisrKyBiL2RyaXZlcnMv ZG1hL21lZGlhdGVrL01ha2VmaWxlCkBAIC0xICsxLDIgQEAKK29iai0kKENPTkZJR19ETUFfTVRL X1VBUlQpICs9IDgyNTBfbXRrX2RtYS5vCiBvYmotJChDT05GSUdfTVRLX0hTRE1BKSArPSBtdGst aHNkbWEubwo= From mboxrd@z Thu Jan 1 00:00:00 1970 From: Long Cheng Subject: [PATCH v9 1/2] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support Date: Wed, 2 Jan 2019 10:12:57 +0800 Message-ID: <1546395178-8880-2-git-send-email-long.cheng@mediatek.com> References: <1546395178-8880-1-git-send-email-long.cheng@mediatek.com> Mime-Version: 1.0 Content-Type: text/plain Return-path: In-Reply-To: <1546395178-8880-1-git-send-email-long.cheng@mediatek.com> Sender: linux-kernel-owner@vger.kernel.org To: Vinod Koul , Randy Dunlap , Rob Herring , Mark Rutland , Ryder Lee , Sean Wang , Nicolas Boichat Cc: 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 , Long Cheng List-Id: linux-mediatek@lists.infradead.org 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/8250_mtk_dma.c | 652 +++++++++++++++++++++++++++++++++++ drivers/dma/mediatek/Kconfig | 11 + drivers/dma/mediatek/Makefile | 1 + 3 files changed, 664 insertions(+) create mode 100644 drivers/dma/mediatek/8250_mtk_dma.c diff --git a/drivers/dma/mediatek/8250_mtk_dma.c b/drivers/dma/mediatek/8250_mtk_dma.c new file mode 100644 index 0000000..dbf811e --- /dev/null +++ b/drivers/dma/mediatek/8250_mtk_dma.c @@ -0,0 +1,652 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek 8250 DMA 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" + +#define MTK_UART_APDMA_CHANNELS (CONFIG_SERIAL_8250_NR_UARTS * 2) + +#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_FLAG_CLR_B (BIT(0) | BIT(1)) +#define VFF_TX_INT_FLAG_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_irq[MTK_UART_APDMA_CHANNELS]; +}; + +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; + + 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, 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); + if ((rg ^ wg) & VFF_RING_WRAP) + cnt = (wg & VFF_RING_SIZE) + len - (rg & VFF_RING_SIZE); + else + cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE); + + 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->cfg.direction == DMA_DEV_TO_MEM) { + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B); + mtk_uart_apdma_start_rx(c); + } else if (c->cfg.direction == DMA_MEM_TO_DEV) { + d = c->desc; + + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_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); + u32 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 == 0, 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->cfg.direction == 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; +} + +/* + * 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 ((dir != DMA_DEV_TO_MEM) && + (dir != DMA_MEM_TO_DEV)) { + dev_err(chan->device->dev, "bad direction\n"); + return NULL; + } + + /* 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 (c->cfg.direction == DMA_DEV_TO_MEM) { + if (vchan_issue_pending(&c->vc)) { + vd = vchan_next_desc(&c->vc); + c->desc = to_mtk_uart_apdma_desc(&vd->tx); + mtk_uart_apdma_start_rx(c); + } + } else if (c->cfg.direction == DMA_MEM_TO_DEV) { + if (vchan_issue_pending(&c->vc)) { + vd = vchan_next_desc(&c->vc); + c->desc = to_mtk_uart_apdma_desc(&vd->tx); + 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 *cfg) +{ + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); + struct mtk_uart_apdmadev *mtkd = + to_mtk_uart_apdma_dev(c->vc.chan.device); + + c->cfg = *cfg; + + if (cfg->direction == DMA_DEV_TO_MEM) { + unsigned int rx_len = cfg->src_addr_width * 1024; + + mtk_uart_apdma_write(c, VFF_ADDR, cfg->src_addr); + mtk_uart_apdma_write(c, VFF_LEN, rx_len); + mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(rx_len)); + 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_FLAG_CLR_B); + mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B); + } else if (cfg->direction == DMA_MEM_TO_DEV) { + unsigned int tx_len = cfg->dst_addr_width * 1024; + + mtk_uart_apdma_write(c, VFF_ADDR, cfg->dst_addr); + mtk_uart_apdma_write(c, VFF_LEN, tx_len); + mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(tx_len)); + mtk_uart_apdma_write(c, VFF_WPT, 0); + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_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", cfg->direction); + return -EINVAL; + } + + 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; + u32 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 == 0, 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->cfg.direction == DMA_DEV_TO_MEM) + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B); + else if (c->cfg.direction == DMA_MEM_TO_DEV) + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_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 int mtk_uart_apdma_device_resume(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) == 0) { + 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 mtk_uart_apdmadev *mtkd; + struct resource *res; + struct mtk_chan *c; + unsigned int i; + int rc; + + 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(pdev->dev.of_node, "dma-33bits")) + mtkd->support_33bits = true; + + rc = dma_set_mask_and_coherent(&pdev->dev, + DMA_BIT_MASK(32 | mtkd->support_33bits)); + 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_resume = mtk_uart_apdma_device_resume; + 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); + + for (i = 0; i < MTK_UART_APDMA_CHANNELS; 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); + + if (pdev->dev.of_node) { + /* Device-tree DMA controller registration */ + rc = of_dma_controller_register(pdev->dev.of_node, + 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"); + diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig index 27bac0b..1a523c87 100644 --- a/drivers/dma/mediatek/Kconfig +++ b/drivers/dma/mediatek/Kconfig @@ -1,4 +1,15 @@ +config DMA_MTK_UART + 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. + config MTK_HSDMA tristate "MediaTek High-Speed DMA controller support" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile index 6e778f8..2f2efd9 100644 --- a/drivers/dma/mediatek/Makefile +++ b/drivers/dma/mediatek/Makefile @@ -1 +1,2 @@ +obj-$(CONFIG_DMA_MTK_UART) += 8250_mtk_dma.o obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o -- 1.7.9.5 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=-11.4 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,USER_AGENT_GIT 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 AF815C43387 for ; Wed, 2 Jan 2019 02:13:58 +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 3C1E52171F for ; Wed, 2 Jan 2019 02:13:58 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="qs6z51pG" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 3C1E52171F 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: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=4gGxKnEWg+b0P+Jjtxjm31Y93qwSYKPLO4UVEIQA6zE=; b=qs6z51pGFp6HFK oDcgFoVrCktq8ICVFGBbJVAu44ZJ7yaJI31TW0i3+YWPayjW55AHnLVLC3/O1RUdyqoROOb2uoTFe Pdjv7dCzSP0+a++ja5fl13pt+pX0qE7G5aEfj/P7RrxlORyFhjQQCeP1ItfMhKibasgIwEJdUtCyR FFtY9sm0vBWbW6223CudkAw3qHQf7TedpaJqs4QmrfKZ3x3vgDhUdMpoqks2xNzsWkZA9Z+v/w3nh 8SSEjKRECcg4Kyld5WfzxsfFx+gYCNXAIEPIwL/cGaG/RhEqDox5GlBkkNWihJ58bcP13K4mPE2Dg +qLf/VE2HzmHbbRiG4BA==; 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 1geW2T-0001ui-Ox; Wed, 02 Jan 2019 02:13:45 +0000 Received: from [210.61.82.184] (helo=mailgw02.mediatek.com) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1geW22-0001Q6-BI; Wed, 02 Jan 2019 02:13:24 +0000 X-UUID: 14e7c4c4e96f4071876b92c6d1bfbcde-20190102 X-UUID: 14e7c4c4e96f4071876b92c6d1bfbcde-20190102 Received: from mtkcas07.mediatek.inc [(172.21.101.84)] by mailgw02.mediatek.com (envelope-from ) (mhqrelay.mediatek.com ESMTP with TLS) with ESMTP id 577237382; Wed, 02 Jan 2019 10:13:03 +0800 Received: from mtkcas07.mediatek.inc (172.21.101.84) by mtkmbs01n1.mediatek.inc (172.21.101.68) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Wed, 2 Jan 2019 10:13:01 +0800 Received: from localhost.localdomain (10.17.3.153) by mtkcas07.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Wed, 2 Jan 2019 10:13:01 +0800 From: Long Cheng To: Vinod Koul , Randy Dunlap , Rob Herring , Mark Rutland , Ryder Lee , Sean Wang , Nicolas Boichat Subject: [PATCH v9 1/2] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support Date: Wed, 2 Jan 2019 10:12:57 +0800 Message-ID: <1546395178-8880-2-git-send-email-long.cheng@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1546395178-8880-1-git-send-email-long.cheng@mediatek.com> References: <1546395178-8880-1-git-send-email-long.cheng@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190101_181319_446363_B6AB7EDE X-CRM114-Status: GOOD ( 20.11 ) 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: Zhenbao Liu , devicetree@vger.kernel.org, YT Shen , srv_heupstream@mediatek.com, Greg Kroah-Hartman , Sean Wang , linux-kernel@vger.kernel.org, dmaengine@vger.kernel.org, Long Cheng , linux-mediatek@lists.infradead.org, linux-serial@vger.kernel.org, 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 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/8250_mtk_dma.c | 652 +++++++++++++++++++++++++++++++++++ drivers/dma/mediatek/Kconfig | 11 + drivers/dma/mediatek/Makefile | 1 + 3 files changed, 664 insertions(+) create mode 100644 drivers/dma/mediatek/8250_mtk_dma.c diff --git a/drivers/dma/mediatek/8250_mtk_dma.c b/drivers/dma/mediatek/8250_mtk_dma.c new file mode 100644 index 0000000..dbf811e --- /dev/null +++ b/drivers/dma/mediatek/8250_mtk_dma.c @@ -0,0 +1,652 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek 8250 DMA 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" + +#define MTK_UART_APDMA_CHANNELS (CONFIG_SERIAL_8250_NR_UARTS * 2) + +#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_FLAG_CLR_B (BIT(0) | BIT(1)) +#define VFF_TX_INT_FLAG_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_irq[MTK_UART_APDMA_CHANNELS]; +}; + +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; + + 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, 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); + if ((rg ^ wg) & VFF_RING_WRAP) + cnt = (wg & VFF_RING_SIZE) + len - (rg & VFF_RING_SIZE); + else + cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE); + + 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->cfg.direction == DMA_DEV_TO_MEM) { + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B); + mtk_uart_apdma_start_rx(c); + } else if (c->cfg.direction == DMA_MEM_TO_DEV) { + d = c->desc; + + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_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); + u32 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 == 0, 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->cfg.direction == 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; +} + +/* + * 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 ((dir != DMA_DEV_TO_MEM) && + (dir != DMA_MEM_TO_DEV)) { + dev_err(chan->device->dev, "bad direction\n"); + return NULL; + } + + /* 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 (c->cfg.direction == DMA_DEV_TO_MEM) { + if (vchan_issue_pending(&c->vc)) { + vd = vchan_next_desc(&c->vc); + c->desc = to_mtk_uart_apdma_desc(&vd->tx); + mtk_uart_apdma_start_rx(c); + } + } else if (c->cfg.direction == DMA_MEM_TO_DEV) { + if (vchan_issue_pending(&c->vc)) { + vd = vchan_next_desc(&c->vc); + c->desc = to_mtk_uart_apdma_desc(&vd->tx); + 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 *cfg) +{ + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); + struct mtk_uart_apdmadev *mtkd = + to_mtk_uart_apdma_dev(c->vc.chan.device); + + c->cfg = *cfg; + + if (cfg->direction == DMA_DEV_TO_MEM) { + unsigned int rx_len = cfg->src_addr_width * 1024; + + mtk_uart_apdma_write(c, VFF_ADDR, cfg->src_addr); + mtk_uart_apdma_write(c, VFF_LEN, rx_len); + mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(rx_len)); + 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_FLAG_CLR_B); + mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B); + } else if (cfg->direction == DMA_MEM_TO_DEV) { + unsigned int tx_len = cfg->dst_addr_width * 1024; + + mtk_uart_apdma_write(c, VFF_ADDR, cfg->dst_addr); + mtk_uart_apdma_write(c, VFF_LEN, tx_len); + mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(tx_len)); + mtk_uart_apdma_write(c, VFF_WPT, 0); + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_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", cfg->direction); + return -EINVAL; + } + + 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; + u32 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 == 0, 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->cfg.direction == DMA_DEV_TO_MEM) + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B); + else if (c->cfg.direction == DMA_MEM_TO_DEV) + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_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 int mtk_uart_apdma_device_resume(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) == 0) { + 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 mtk_uart_apdmadev *mtkd; + struct resource *res; + struct mtk_chan *c; + unsigned int i; + int rc; + + 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(pdev->dev.of_node, "dma-33bits")) + mtkd->support_33bits = true; + + rc = dma_set_mask_and_coherent(&pdev->dev, + DMA_BIT_MASK(32 | mtkd->support_33bits)); + 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_resume = mtk_uart_apdma_device_resume; + 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); + + for (i = 0; i < MTK_UART_APDMA_CHANNELS; 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); + + if (pdev->dev.of_node) { + /* Device-tree DMA controller registration */ + rc = of_dma_controller_register(pdev->dev.of_node, + 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"); + diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig index 27bac0b..1a523c87 100644 --- a/drivers/dma/mediatek/Kconfig +++ b/drivers/dma/mediatek/Kconfig @@ -1,4 +1,15 @@ +config DMA_MTK_UART + 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. + config MTK_HSDMA tristate "MediaTek High-Speed DMA controller support" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile index 6e778f8..2f2efd9 100644 --- a/drivers/dma/mediatek/Makefile +++ b/drivers/dma/mediatek/Makefile @@ -1 +1,2 @@ +obj-$(CONFIG_DMA_MTK_UART) += 8250_mtk_dma.o obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o -- 1.7.9.5 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel 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=-9.0 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_PASS,UNPARSEABLE_RELAY, URIBL_BLOCKED,USER_AGENT_GIT 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 57059C43387 for ; Wed, 2 Jan 2019 02:13:24 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 10B922171F for ; Wed, 2 Jan 2019 02:13:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728545AbfABCNW (ORCPT ); Tue, 1 Jan 2019 21:13:22 -0500 Received: from mailgw02.mediatek.com ([210.61.82.184]:5150 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1728451AbfABCNV (ORCPT ); Tue, 1 Jan 2019 21:13:21 -0500 X-UUID: 14e7c4c4e96f4071876b92c6d1bfbcde-20190102 X-UUID: 14e7c4c4e96f4071876b92c6d1bfbcde-20190102 Received: from mtkcas07.mediatek.inc [(172.21.101.84)] by mailgw02.mediatek.com (envelope-from ) (mhqrelay.mediatek.com ESMTP with TLS) with ESMTP id 577237382; Wed, 02 Jan 2019 10:13:03 +0800 Received: from mtkcas07.mediatek.inc (172.21.101.84) by mtkmbs01n1.mediatek.inc (172.21.101.68) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Wed, 2 Jan 2019 10:13:01 +0800 Received: from localhost.localdomain (10.17.3.153) by mtkcas07.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Wed, 2 Jan 2019 10:13:01 +0800 From: Long Cheng To: Vinod Koul , Randy Dunlap , Rob Herring , Mark Rutland , Ryder Lee , Sean Wang , Nicolas Boichat CC: Matthias Brugger , Dan Williams , Greg Kroah-Hartman , Jiri Slaby , Sean Wang , , , , , , , , Yingjoe Chen , YT Shen , Zhenbao Liu , Long Cheng Subject: [PATCH v9 1/2] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support Date: Wed, 2 Jan 2019 10:12:57 +0800 Message-ID: <1546395178-8880-2-git-send-email-long.cheng@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1546395178-8880-1-git-send-email-long.cheng@mediatek.com> References: <1546395178-8880-1-git-send-email-long.cheng@mediatek.com> MIME-Version: 1.0 Content-Type: text/plain X-MTK: N Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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/8250_mtk_dma.c | 652 +++++++++++++++++++++++++++++++++++ drivers/dma/mediatek/Kconfig | 11 + drivers/dma/mediatek/Makefile | 1 + 3 files changed, 664 insertions(+) create mode 100644 drivers/dma/mediatek/8250_mtk_dma.c diff --git a/drivers/dma/mediatek/8250_mtk_dma.c b/drivers/dma/mediatek/8250_mtk_dma.c new file mode 100644 index 0000000..dbf811e --- /dev/null +++ b/drivers/dma/mediatek/8250_mtk_dma.c @@ -0,0 +1,652 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek 8250 DMA 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" + +#define MTK_UART_APDMA_CHANNELS (CONFIG_SERIAL_8250_NR_UARTS * 2) + +#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_FLAG_CLR_B (BIT(0) | BIT(1)) +#define VFF_TX_INT_FLAG_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_irq[MTK_UART_APDMA_CHANNELS]; +}; + +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; + + 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, 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); + if ((rg ^ wg) & VFF_RING_WRAP) + cnt = (wg & VFF_RING_SIZE) + len - (rg & VFF_RING_SIZE); + else + cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE); + + 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->cfg.direction == DMA_DEV_TO_MEM) { + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B); + mtk_uart_apdma_start_rx(c); + } else if (c->cfg.direction == DMA_MEM_TO_DEV) { + d = c->desc; + + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_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); + u32 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 == 0, 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->cfg.direction == 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; +} + +/* + * 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 ((dir != DMA_DEV_TO_MEM) && + (dir != DMA_MEM_TO_DEV)) { + dev_err(chan->device->dev, "bad direction\n"); + return NULL; + } + + /* 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 (c->cfg.direction == DMA_DEV_TO_MEM) { + if (vchan_issue_pending(&c->vc)) { + vd = vchan_next_desc(&c->vc); + c->desc = to_mtk_uart_apdma_desc(&vd->tx); + mtk_uart_apdma_start_rx(c); + } + } else if (c->cfg.direction == DMA_MEM_TO_DEV) { + if (vchan_issue_pending(&c->vc)) { + vd = vchan_next_desc(&c->vc); + c->desc = to_mtk_uart_apdma_desc(&vd->tx); + 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 *cfg) +{ + struct mtk_chan *c = to_mtk_uart_apdma_chan(chan); + struct mtk_uart_apdmadev *mtkd = + to_mtk_uart_apdma_dev(c->vc.chan.device); + + c->cfg = *cfg; + + if (cfg->direction == DMA_DEV_TO_MEM) { + unsigned int rx_len = cfg->src_addr_width * 1024; + + mtk_uart_apdma_write(c, VFF_ADDR, cfg->src_addr); + mtk_uart_apdma_write(c, VFF_LEN, rx_len); + mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(rx_len)); + 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_FLAG_CLR_B); + mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B); + } else if (cfg->direction == DMA_MEM_TO_DEV) { + unsigned int tx_len = cfg->dst_addr_width * 1024; + + mtk_uart_apdma_write(c, VFF_ADDR, cfg->dst_addr); + mtk_uart_apdma_write(c, VFF_LEN, tx_len); + mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(tx_len)); + mtk_uart_apdma_write(c, VFF_WPT, 0); + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_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", cfg->direction); + return -EINVAL; + } + + 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; + u32 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 == 0, 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->cfg.direction == DMA_DEV_TO_MEM) + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B); + else if (c->cfg.direction == DMA_MEM_TO_DEV) + mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_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 int mtk_uart_apdma_device_resume(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) == 0) { + 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 mtk_uart_apdmadev *mtkd; + struct resource *res; + struct mtk_chan *c; + unsigned int i; + int rc; + + 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(pdev->dev.of_node, "dma-33bits")) + mtkd->support_33bits = true; + + rc = dma_set_mask_and_coherent(&pdev->dev, + DMA_BIT_MASK(32 | mtkd->support_33bits)); + 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_resume = mtk_uart_apdma_device_resume; + 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); + + for (i = 0; i < MTK_UART_APDMA_CHANNELS; 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); + + if (pdev->dev.of_node) { + /* Device-tree DMA controller registration */ + rc = of_dma_controller_register(pdev->dev.of_node, + 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"); + diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig index 27bac0b..1a523c87 100644 --- a/drivers/dma/mediatek/Kconfig +++ b/drivers/dma/mediatek/Kconfig @@ -1,4 +1,15 @@ +config DMA_MTK_UART + 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. + config MTK_HSDMA tristate "MediaTek High-Speed DMA controller support" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile index 6e778f8..2f2efd9 100644 --- a/drivers/dma/mediatek/Makefile +++ b/drivers/dma/mediatek/Makefile @@ -1 +1,2 @@ +obj-$(CONFIG_DMA_MTK_UART) += 8250_mtk_dma.o obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o -- 1.7.9.5