From 779087fe2fecf8b8aa7bdd69982f9c0c8266927b Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:12 +0200 Subject: [PATCH 001/113] kfifo: drop __kfifo_dma_out_finish_r() It is the same as __kfifo_skip_r(), so: * drop __kfifo_dma_out_finish_r() completely, and * replace its (only) use by __kfifo_skip_r(). Signed-off-by: Jiri Slaby (SUSE) Cc: Stefani Seibold Cc: Andrew Morton Link: https://lore.kernel.org/r/20240405060826.2521-2-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/kfifo.h | 4 +--- lib/kfifo.c | 8 -------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index 0b35a41440ff..bc7a1f5bb0ce 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -797,7 +797,7 @@ __kfifo_int_must_check_helper( \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (__recsize) \ - __kfifo_dma_out_finish_r(__kfifo, __recsize); \ + __kfifo_skip_r(__kfifo, __recsize); \ else \ __kfifo->out += __len / sizeof(*__tmp->type); \ }) @@ -879,8 +879,6 @@ extern void __kfifo_dma_in_finish_r(struct __kfifo *fifo, extern unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo, struct scatterlist *sgl, int nents, unsigned int len, size_t recsize); -extern void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize); - extern unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize); extern void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize); diff --git a/lib/kfifo.c b/lib/kfifo.c index 12f5a347aa13..958099cc4914 100644 --- a/lib/kfifo.c +++ b/lib/kfifo.c @@ -582,11 +582,3 @@ unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo, } EXPORT_SYMBOL(__kfifo_dma_out_prepare_r); -void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize) -{ - unsigned int len; - - len = __kfifo_peek_n(fifo, recsize); - fifo->out += len + recsize; -} -EXPORT_SYMBOL(__kfifo_dma_out_finish_r); From 76738194be605c9fb279f87d8a6b0b95904bfb60 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:13 +0200 Subject: [PATCH 002/113] kfifo: introduce and use kfifo_skip_count() kfifo_skip_count() is an extended version of kfifo_skip(), accepting also count. This will be useful in the serial code later. Now, it can be used to implement both kfifo_skip() and kfifo_dma_out_finish(). In the latter, 'len' only needs to be divided by 'type' size (as it was until now). And stop using statement expressions when the return value is cast to 'void'. Use classic 'do {} while (0)' instead. Note: perhaps we should skip 'count' records for the 'recsize' case, but the original (kfifo_dma_out_finish()) used to skip only one record. So this is kept unchanged and 'count' is still ignored in the recsize case. Signed-off-by: Jiri Slaby (SUSE) Cc: Stefani Seibold Cc: Andrew Morton Link: https://lore.kernel.org/r/20240405060826.2521-3-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/kfifo.h | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index bc7a1f5bb0ce..8f3369ec528b 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -304,19 +304,25 @@ __kfifo_uint_must_check_helper( \ ) /** - * kfifo_skip - skip output data + * kfifo_skip_count - skip output data * @fifo: address of the fifo to be used + * @count: count of data to skip */ -#define kfifo_skip(fifo) \ -(void)({ \ +#define kfifo_skip_count(fifo, count) do { \ typeof((fifo) + 1) __tmp = (fifo); \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ if (__recsize) \ __kfifo_skip_r(__kfifo, __recsize); \ else \ - __kfifo->out++; \ -}) + __kfifo->out += (count); \ +} while(0) + +/** + * kfifo_skip - skip output data + * @fifo: address of the fifo to be used + */ +#define kfifo_skip(fifo) kfifo_skip_count(fifo, 1) /** * kfifo_peek_len - gets the size of the next fifo record @@ -790,17 +796,10 @@ __kfifo_int_must_check_helper( \ * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macros. */ -#define kfifo_dma_out_finish(fifo, len) \ -(void)({ \ - typeof((fifo) + 1) __tmp = (fifo); \ - unsigned int __len = (len); \ - const size_t __recsize = sizeof(*__tmp->rectype); \ - struct __kfifo *__kfifo = &__tmp->kfifo; \ - if (__recsize) \ - __kfifo_skip_r(__kfifo, __recsize); \ - else \ - __kfifo->out += __len / sizeof(*__tmp->type); \ -}) +#define kfifo_dma_out_finish(fifo, len) do { \ + typeof((fifo) + 1) ___tmp = (fifo); \ + kfifo_skip_count(___tmp, (len) / sizeof(*___tmp->type)); \ +} while (0) /** * kfifo_out_peek - gets some data from the fifo From 4edd7e96a1f159f43bd1cb82616f81eaddd54262 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:14 +0200 Subject: [PATCH 003/113] kfifo: add kfifo_out_linear{,_ptr}() These are helpers which are going to be used in the serial layer. We need a wrapper around kfifo which provides us with a tail (sometimes "tail" offset, sometimes a pointer) to the kfifo data. And which returns count of available data -- but not larger than to the end of the buffer (hence _linear in the names). I.e. something like CIRC_CNT_TO_END() in the legacy circ_buf. This patch adds such two helpers. Signed-off-by: Jiri Slaby (SUSE) Cc: Stefani Seibold Cc: Andrew Morton Link: https://lore.kernel.org/r/20240405060826.2521-4-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/kfifo.h | 63 +++++++++++++++++++++++++++++++++++++++++++ lib/kfifo.c | 26 ++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index 8f3369ec528b..3def70e1a3e3 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -827,6 +827,63 @@ __kfifo_uint_must_check_helper( \ }) \ ) +/** + * kfifo_out_linear - gets a tail of/offset to available data + * @fifo: address of the fifo to be used + * @tail: pointer to an unsigned int to store the value of tail + * @n: max. number of elements to point at + * + * This macro obtains the offset (tail) to the available data in the fifo + * buffer and returns the + * numbers of elements available. It returns the available count till the end + * of data or till the end of the buffer. So that it can be used for linear + * data processing (like memcpy() of (@fifo->data + @tail) with count + * returned). + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_out_linear(fifo, tail, n) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + unsigned int *__tail = (tail); \ + unsigned long __n = (n); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + (__recsize) ? \ + __kfifo_out_linear_r(__kfifo, __tail, __n, __recsize) : \ + __kfifo_out_linear(__kfifo, __tail, __n); \ +}) \ +) + +/** + * kfifo_out_linear_ptr - gets a pointer to the available data + * @fifo: address of the fifo to be used + * @ptr: pointer to data to store the pointer to tail + * @n: max. number of elements to point at + * + * Similarly to kfifo_out_linear(), this macro obtains the pointer to the + * available data in the fifo buffer and returns the numbers of elements + * available. It returns the available count till the end of available data or + * till the end of the buffer. So that it can be used for linear data + * processing (like memcpy() of @ptr with count returned). + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_out_linear_ptr(fifo, ptr, n) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) ___tmp = (fifo); \ + unsigned int ___tail; \ + unsigned int ___n = kfifo_out_linear(___tmp, &___tail, (n)); \ + *(ptr) = ___tmp->kfifo.data + ___tail * kfifo_esize(___tmp); \ + ___n; \ +}) \ +) + + extern int __kfifo_alloc(struct __kfifo *fifo, unsigned int size, size_t esize, gfp_t gfp_mask); @@ -856,6 +913,9 @@ extern unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo, extern unsigned int __kfifo_out_peek(struct __kfifo *fifo, void *buf, unsigned int len); +extern unsigned int __kfifo_out_linear(struct __kfifo *fifo, + unsigned int *tail, unsigned int n); + extern unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf, unsigned int len, size_t recsize); @@ -885,6 +945,9 @@ extern void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize); extern unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf, unsigned int len, size_t recsize); +extern unsigned int __kfifo_out_linear_r(struct __kfifo *fifo, + unsigned int *tail, unsigned int n, size_t recsize); + extern unsigned int __kfifo_max_r(unsigned int len, size_t recsize); #endif diff --git a/lib/kfifo.c b/lib/kfifo.c index 958099cc4914..a36bfdbdb17d 100644 --- a/lib/kfifo.c +++ b/lib/kfifo.c @@ -163,6 +163,19 @@ unsigned int __kfifo_out_peek(struct __kfifo *fifo, } EXPORT_SYMBOL(__kfifo_out_peek); +unsigned int __kfifo_out_linear(struct __kfifo *fifo, + unsigned int *tail, unsigned int n) +{ + unsigned int size = fifo->mask + 1; + unsigned int off = fifo->out & fifo->mask; + + if (tail) + *tail = off; + + return min3(n, fifo->in - fifo->out, size - off); +} +EXPORT_SYMBOL(__kfifo_out_linear); + unsigned int __kfifo_out(struct __kfifo *fifo, void *buf, unsigned int len) { @@ -473,6 +486,19 @@ unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf, } EXPORT_SYMBOL(__kfifo_out_peek_r); +unsigned int __kfifo_out_linear_r(struct __kfifo *fifo, + unsigned int *tail, unsigned int n, size_t recsize) +{ + if (fifo->in == fifo->out) + return 0; + + if (tail) + *tail = fifo->out + recsize; + + return min(n, __kfifo_peek_n(fifo, recsize)); +} +EXPORT_SYMBOL(__kfifo_out_linear_r); + unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf, unsigned int len, size_t recsize) { From e9d9576de08ac65d2c973ef7ce542338d2da94f9 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:15 +0200 Subject: [PATCH 004/113] kfifo: remove support for physically non-contiguous memory First, there is no such user. The only user of this interface is caam_rng_fill_async() and that uses kfifo_alloc() -> kmalloc(). Second, the implementation does not allow anything else than direct mapping and kmalloc() (due to virt_to_phys()), anyway. Therefore, there is no point in having this dead (and complex) code in the kernel. Note the setup_sgl_buf() function now boils down to simple sg_set_buf(). That is called twice from setup_sgl() to take care of kfifo buffer wrap-around. setup_sgl_buf() will be extended shortly, so keeping it in place. Signed-off-by: Jiri Slaby (SUSE) Cc: Andrew Morton Cc: Stefani Seibold Link: https://lore.kernel.org/r/20240405060826.2521-5-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- lib/kfifo.c | 38 +++++--------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/lib/kfifo.c b/lib/kfifo.c index a36bfdbdb17d..d5830960ab87 100644 --- a/lib/kfifo.c +++ b/lib/kfifo.c @@ -305,43 +305,15 @@ int __kfifo_to_user(struct __kfifo *fifo, void __user *to, } EXPORT_SYMBOL(__kfifo_to_user); -static int setup_sgl_buf(struct scatterlist *sgl, void *buf, - int nents, unsigned int len) +static unsigned int setup_sgl_buf(struct scatterlist *sgl, void *buf, + int nents, unsigned int len) { - int n; - unsigned int l; - unsigned int off; - struct page *page; - - if (!nents) + if (!nents || !len) return 0; - if (!len) - return 0; + sg_set_buf(sgl, buf, len); - n = 0; - page = virt_to_page(buf); - off = offset_in_page(buf); - l = 0; - - while (len >= l + PAGE_SIZE - off) { - struct page *npage; - - l += PAGE_SIZE; - buf += PAGE_SIZE; - npage = virt_to_page(buf); - if (page_to_phys(page) != page_to_phys(npage) - l) { - sg_set_page(sgl, page, l - off, off); - sgl = sg_next(sgl); - if (++n == nents || sgl == NULL) - return n; - page = npage; - len -= l - off; - l = off = 0; - } - } - sg_set_page(sgl, page, len, off); - return n + 1; + return 1; } static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl, From ed6d22f5d8672f38d6020c719d5d658a4e3e6be5 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:16 +0200 Subject: [PATCH 005/113] kfifo: rename l to len_to_end in setup_sgl() So that one can make any sense of the name. Signed-off-by: Jiri Slaby (SUSE) Cc: Stefani Seibold Cc: Andrew Morton Link: https://lore.kernel.org/r/20240405060826.2521-6-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- lib/kfifo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/kfifo.c b/lib/kfifo.c index d5830960ab87..61e35550aea5 100644 --- a/lib/kfifo.c +++ b/lib/kfifo.c @@ -321,7 +321,7 @@ static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl, { unsigned int size = fifo->mask + 1; unsigned int esize = fifo->esize; - unsigned int l; + unsigned int len_to_end; unsigned int n; off &= fifo->mask; @@ -330,10 +330,10 @@ static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl, size *= esize; len *= esize; } - l = min(len, size - off); + len_to_end = min(len, size - off); - n = setup_sgl_buf(sgl, fifo->data + off, nents, l); - n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l); + n = setup_sgl_buf(sgl, fifo->data + off, nents, len_to_end); + n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - len_to_end); return n; } From fea0dde081621e25bcf7efd40cce6c8272e3aa13 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:17 +0200 Subject: [PATCH 006/113] kfifo: pass offset to setup_sgl_buf() instead of a pointer As a preparatory for dma addresses filling, we need the data offset instead of virtual pointer in setup_sgl_buf(). So pass the former instead the latter. And pointer to fifo is needed in setup_sgl_buf() now too. Signed-off-by: Jiri Slaby (SUSE) Cc: Stefani Seibold Cc: Andrew Morton Link: https://lore.kernel.org/r/20240405060826.2521-7-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- lib/kfifo.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/kfifo.c b/lib/kfifo.c index 61e35550aea5..3a249ce4f281 100644 --- a/lib/kfifo.c +++ b/lib/kfifo.c @@ -305,9 +305,12 @@ int __kfifo_to_user(struct __kfifo *fifo, void __user *to, } EXPORT_SYMBOL(__kfifo_to_user); -static unsigned int setup_sgl_buf(struct scatterlist *sgl, void *buf, - int nents, unsigned int len) +static unsigned int setup_sgl_buf(struct __kfifo *fifo, struct scatterlist *sgl, + unsigned int data_offset, int nents, + unsigned int len) { + const void *buf = fifo->data + data_offset; + if (!nents || !len) return 0; @@ -332,8 +335,8 @@ static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl, } len_to_end = min(len, size - off); - n = setup_sgl_buf(sgl, fifo->data + off, nents, len_to_end); - n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - len_to_end); + n = setup_sgl_buf(fifo, sgl, off, nents, len_to_end); + n += setup_sgl_buf(fifo, sgl + n, 0, nents - n, len - len_to_end); return n; } From d52b761e4b1acc897e65bcc8eff42b0537ac0134 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:18 +0200 Subject: [PATCH 007/113] kfifo: add kfifo_dma_out_prepare_mapped() When the kfifo buffer is already dma-mapped, one cannot use the kfifo API to fill in an SG list. Add kfifo_dma_in_prepare_mapped() which allows exactly this. A mapped dma_addr_t is passed and it is filled into provided sgl too. Including the dma_len. Signed-off-by: Jiri Slaby (SUSE) Cc: Stefani Seibold Cc: Andrew Morton Link: https://lore.kernel.org/r/20240405060826.2521-8-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/kfifo.h | 37 +++++++++++++++++++++++++------------ lib/kfifo.c | 34 ++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index 3def70e1a3e3..c7cc25b2808b 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -36,6 +36,7 @@ * to lock the reader. */ +#include #include #include #include @@ -709,11 +710,12 @@ __kfifo_int_must_check_helper( \ ) /** - * kfifo_dma_in_prepare - setup a scatterlist for DMA input + * kfifo_dma_in_prepare_mapped - setup a scatterlist for DMA input * @fifo: address of the fifo to be used * @sgl: pointer to the scatterlist array * @nents: number of entries in the scatterlist array * @len: number of elements to transfer + * @dma: mapped dma address to fill into @sgl * * This macro fills a scatterlist for DMA input. * It returns the number entries in the scatterlist array. @@ -721,7 +723,7 @@ __kfifo_int_must_check_helper( \ * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macros. */ -#define kfifo_dma_in_prepare(fifo, sgl, nents, len) \ +#define kfifo_dma_in_prepare_mapped(fifo, sgl, nents, len, dma) \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ struct scatterlist *__sgl = (sgl); \ @@ -730,10 +732,14 @@ __kfifo_int_must_check_helper( \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ (__recsize) ? \ - __kfifo_dma_in_prepare_r(__kfifo, __sgl, __nents, __len, __recsize) : \ - __kfifo_dma_in_prepare(__kfifo, __sgl, __nents, __len); \ + __kfifo_dma_in_prepare_r(__kfifo, __sgl, __nents, __len, __recsize, \ + dma) : \ + __kfifo_dma_in_prepare(__kfifo, __sgl, __nents, __len, dma); \ }) +#define kfifo_dma_in_prepare(fifo, sgl, nents, len) \ + kfifo_dma_in_prepare_mapped(fifo, sgl, nents, len, DMA_MAPPING_ERROR) + /** * kfifo_dma_in_finish - finish a DMA IN operation * @fifo: address of the fifo to be used @@ -758,11 +764,12 @@ __kfifo_int_must_check_helper( \ }) /** - * kfifo_dma_out_prepare - setup a scatterlist for DMA output + * kfifo_dma_out_prepare_mapped - setup a scatterlist for DMA output * @fifo: address of the fifo to be used * @sgl: pointer to the scatterlist array * @nents: number of entries in the scatterlist array * @len: number of elements to transfer + * @dma: mapped dma address to fill into @sgl * * This macro fills a scatterlist for DMA output which at most @len bytes * to transfer. @@ -772,7 +779,7 @@ __kfifo_int_must_check_helper( \ * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these macros. */ -#define kfifo_dma_out_prepare(fifo, sgl, nents, len) \ +#define kfifo_dma_out_prepare_mapped(fifo, sgl, nents, len, dma) \ ({ \ typeof((fifo) + 1) __tmp = (fifo); \ struct scatterlist *__sgl = (sgl); \ @@ -781,10 +788,14 @@ __kfifo_int_must_check_helper( \ const size_t __recsize = sizeof(*__tmp->rectype); \ struct __kfifo *__kfifo = &__tmp->kfifo; \ (__recsize) ? \ - __kfifo_dma_out_prepare_r(__kfifo, __sgl, __nents, __len, __recsize) : \ - __kfifo_dma_out_prepare(__kfifo, __sgl, __nents, __len); \ + __kfifo_dma_out_prepare_r(__kfifo, __sgl, __nents, __len, __recsize, \ + dma) : \ + __kfifo_dma_out_prepare(__kfifo, __sgl, __nents, __len, dma); \ }) +#define kfifo_dma_out_prepare(fifo, sgl, nents, len) \ + kfifo_dma_out_prepare_mapped(fifo, sgl, nents, len, DMA_MAPPING_ERROR) + /** * kfifo_dma_out_finish - finish a DMA OUT operation * @fifo: address of the fifo to be used @@ -905,10 +916,10 @@ extern int __kfifo_to_user(struct __kfifo *fifo, void __user *to, unsigned long len, unsigned int *copied); extern unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo, - struct scatterlist *sgl, int nents, unsigned int len); + struct scatterlist *sgl, int nents, unsigned int len, dma_addr_t dma); extern unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo, - struct scatterlist *sgl, int nents, unsigned int len); + struct scatterlist *sgl, int nents, unsigned int len, dma_addr_t dma); extern unsigned int __kfifo_out_peek(struct __kfifo *fifo, void *buf, unsigned int len); @@ -930,13 +941,15 @@ extern int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to, unsigned long len, unsigned int *copied, size_t recsize); extern unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo, - struct scatterlist *sgl, int nents, unsigned int len, size_t recsize); + struct scatterlist *sgl, int nents, unsigned int len, size_t recsize, + dma_addr_t dma); extern void __kfifo_dma_in_finish_r(struct __kfifo *fifo, unsigned int len, size_t recsize); extern unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo, - struct scatterlist *sgl, int nents, unsigned int len, size_t recsize); + struct scatterlist *sgl, int nents, unsigned int len, size_t recsize, + dma_addr_t dma); extern unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize); diff --git a/lib/kfifo.c b/lib/kfifo.c index 3a249ce4f281..75ce9225548a 100644 --- a/lib/kfifo.c +++ b/lib/kfifo.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -307,7 +308,7 @@ EXPORT_SYMBOL(__kfifo_to_user); static unsigned int setup_sgl_buf(struct __kfifo *fifo, struct scatterlist *sgl, unsigned int data_offset, int nents, - unsigned int len) + unsigned int len, dma_addr_t dma) { const void *buf = fifo->data + data_offset; @@ -316,11 +317,16 @@ static unsigned int setup_sgl_buf(struct __kfifo *fifo, struct scatterlist *sgl, sg_set_buf(sgl, buf, len); + if (dma != DMA_MAPPING_ERROR) { + sg_dma_address(sgl) = dma + data_offset; + sg_dma_len(sgl) = len; + } + return 1; } static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl, - int nents, unsigned int len, unsigned int off) + int nents, unsigned int len, unsigned int off, dma_addr_t dma) { unsigned int size = fifo->mask + 1; unsigned int esize = fifo->esize; @@ -335,14 +341,15 @@ static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl, } len_to_end = min(len, size - off); - n = setup_sgl_buf(fifo, sgl, off, nents, len_to_end); - n += setup_sgl_buf(fifo, sgl + n, 0, nents - n, len - len_to_end); + n = setup_sgl_buf(fifo, sgl, off, nents, len_to_end, dma); + n += setup_sgl_buf(fifo, sgl + n, 0, nents - n, len - len_to_end, dma); return n; } unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo, - struct scatterlist *sgl, int nents, unsigned int len) + struct scatterlist *sgl, int nents, unsigned int len, + dma_addr_t dma) { unsigned int l; @@ -350,12 +357,13 @@ unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo, if (len > l) len = l; - return setup_sgl(fifo, sgl, nents, len, fifo->in); + return setup_sgl(fifo, sgl, nents, len, fifo->in, dma); } EXPORT_SYMBOL(__kfifo_dma_in_prepare); unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo, - struct scatterlist *sgl, int nents, unsigned int len) + struct scatterlist *sgl, int nents, unsigned int len, + dma_addr_t dma) { unsigned int l; @@ -363,7 +371,7 @@ unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo, if (len > l) len = l; - return setup_sgl(fifo, sgl, nents, len, fifo->out); + return setup_sgl(fifo, sgl, nents, len, fifo->out, dma); } EXPORT_SYMBOL(__kfifo_dma_out_prepare); @@ -547,7 +555,8 @@ int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to, EXPORT_SYMBOL(__kfifo_to_user_r); unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo, - struct scatterlist *sgl, int nents, unsigned int len, size_t recsize) + struct scatterlist *sgl, int nents, unsigned int len, size_t recsize, + dma_addr_t dma) { BUG_ON(!nents); @@ -556,7 +565,7 @@ unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo, if (len + recsize > kfifo_unused(fifo)) return 0; - return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize); + return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize, dma); } EXPORT_SYMBOL(__kfifo_dma_in_prepare_r); @@ -570,7 +579,8 @@ void __kfifo_dma_in_finish_r(struct __kfifo *fifo, EXPORT_SYMBOL(__kfifo_dma_in_finish_r); unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo, - struct scatterlist *sgl, int nents, unsigned int len, size_t recsize) + struct scatterlist *sgl, int nents, unsigned int len, size_t recsize, + dma_addr_t dma) { BUG_ON(!nents); @@ -579,7 +589,7 @@ unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo, if (len + recsize > fifo->in - fifo->out) return 0; - return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize); + return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize, dma); } EXPORT_SYMBOL(__kfifo_dma_out_prepare_r); From 2ab682d221551d5eb9606dad1efa01e184de7b00 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:19 +0200 Subject: [PATCH 008/113] kfifo: fix typos in kernel-doc Obviously: "This macro finish" -> "This macro finishes" and similar. Signed-off-by: Jiri Slaby (SUSE) Cc: Stefani Seibold Cc: Andrew Morton Link: https://lore.kernel.org/r/20240405060826.2521-9-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/kfifo.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index c7cc25b2808b..d613748de7ff 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -585,7 +585,7 @@ __kfifo_uint_must_check_helper( \ * @buf: pointer to the storage buffer * @n: max. number of elements to get * - * This macro get some data from the fifo and return the numbers of elements + * This macro gets some data from the fifo and returns the numbers of elements * copied. * * Note that with only one concurrent reader and one concurrent @@ -612,7 +612,7 @@ __kfifo_uint_must_check_helper( \ * @n: max. number of elements to get * @lock: pointer to the spinlock to use for locking * - * This macro get the data from the fifo and return the numbers of elements + * This macro gets the data from the fifo and returns the numbers of elements * copied. */ #define kfifo_out_spinlocked(fifo, buf, n, lock) \ @@ -745,7 +745,7 @@ __kfifo_int_must_check_helper( \ * @fifo: address of the fifo to be used * @len: number of bytes to received * - * This macro finish a DMA IN operation. The in counter will be updated by + * This macro finishes a DMA IN operation. The in counter will be updated by * the len parameter. No error checking will be done. * * Note that with only one concurrent reader and one concurrent @@ -801,7 +801,7 @@ __kfifo_int_must_check_helper( \ * @fifo: address of the fifo to be used * @len: number of bytes transferred * - * This macro finish a DMA OUT operation. The out counter will be updated by + * This macro finishes a DMA OUT operation. The out counter will be updated by * the len parameter. No error checking will be done. * * Note that with only one concurrent reader and one concurrent @@ -818,7 +818,7 @@ __kfifo_int_must_check_helper( \ * @buf: pointer to the storage buffer * @n: max. number of elements to get * - * This macro get the data from the fifo and return the numbers of elements + * This macro gets the data from the fifo and returns the numbers of elements * copied. The data is not removed from the fifo. * * Note that with only one concurrent reader and one concurrent From 8192fabb0db269a4130d8cbdd8557a47bfa1ffec Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:20 +0200 Subject: [PATCH 009/113] tty: 8250_dma: use dmaengine_prep_slave_sg() This is a preparatory for the serial-to-kfifo switch. kfifo understands only scatter-gatter approach, so switch to that. No functional change intended, it's just dmaengine_prep_slave_single() inline expanded. Signed-off-by: Jiri Slaby (SUSE) Link: https://lore.kernel.org/r/20240405060826.2521-10-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dma.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 8b30ca8fdd3f..8b2c3f478b17 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -89,6 +89,7 @@ int serial8250_tx_dma(struct uart_8250_port *p) struct circ_buf *xmit = &p->port.state->xmit; struct dma_async_tx_descriptor *desc; struct uart_port *up = &p->port; + struct scatterlist sg; int ret; if (dma->tx_running) { @@ -111,10 +112,13 @@ int serial8250_tx_dma(struct uart_8250_port *p) serial8250_do_prepare_tx_dma(p); - desc = dmaengine_prep_slave_single(dma->txchan, - dma->tx_addr + xmit->tail, - dma->tx_size, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + sg_init_table(&sg, 1); + sg_dma_address(&sg) = dma->tx_addr + xmit->tail; + sg_dma_len(&sg) = dma->tx_size; + + desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { ret = -EBUSY; goto err; From 9054605ab8468936a514298211d9e9bb68bf24bd Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:21 +0200 Subject: [PATCH 010/113] tty: 8250_omap: use dmaengine_prep_slave_sg() This is a preparatory for the serial-to-kfifo switch. kfifo understands only scatter-gatter approach, so switch to that. No functional change intended, it's just dmaengine_prep_slave_single() inline expanded. Signed-off-by: Jiri Slaby (SUSE) Link: https://lore.kernel.org/r/20240405060826.2521-11-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 66901d93089a..879e77b30701 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1140,6 +1140,7 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) struct omap8250_priv *priv = p->port.private_data; struct circ_buf *xmit = &p->port.state->xmit; struct dma_async_tx_descriptor *desc; + struct scatterlist sg; unsigned int skip_byte = 0; int ret; @@ -1191,9 +1192,11 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) skip_byte = 1; } - desc = dmaengine_prep_slave_single(dma->txchan, - dma->tx_addr + xmit->tail + skip_byte, - dma->tx_size - skip_byte, DMA_MEM_TO_DEV, + sg_init_table(&sg, 1); + sg_dma_address(&sg) = dma->tx_addr + xmit->tail + skip_byte; + sg_dma_len(&sg) = dma->tx_size - skip_byte; + + desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { ret = -EBUSY; From f8fef2fa419febbfed2d04f0518111565df2673d Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:22 +0200 Subject: [PATCH 011/113] tty: msm_serial: use dmaengine_prep_slave_sg() This is a preparatory for the serial-to-kfifo switch. kfifo understands only scatter-gatter approach, so switch to that. No functional change intended, it's just dmaengine_prep_slave_single() inline expanded. And in this case, switch from dma_map_single() to dma_map_sg() too. This needs struct msm_dma changes. I split the rx and tx parts into an union. TX is now struct scatterlist, RX remains the old good phys-virt-count triple. Signed-off-by: Jiri Slaby (SUSE) Cc: Bjorn Andersson Cc: Konrad Dybcio Cc: linux-arm-msm@vger.kernel.org Link: https://lore.kernel.org/r/20240405060826.2521-12-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 86 +++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index d27c4c8c84e1..7bf30e632313 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -161,11 +161,16 @@ enum { struct msm_dma { struct dma_chan *chan; enum dma_data_direction dir; - dma_addr_t phys; - unsigned char *virt; + union { + struct { + dma_addr_t phys; + unsigned char *virt; + unsigned int count; + } rx; + struct scatterlist tx_sg; + }; dma_cookie_t cookie; u32 enable_bit; - unsigned int count; struct dma_async_tx_descriptor *desc; }; @@ -249,8 +254,12 @@ static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) unsigned int mapped; u32 val; - mapped = dma->count; - dma->count = 0; + if (dma->dir == DMA_TO_DEVICE) { + mapped = sg_dma_len(&dma->tx_sg); + } else { + mapped = dma->rx.count; + dma->rx.count = 0; + } dmaengine_terminate_all(dma->chan); @@ -265,8 +274,13 @@ static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) val &= ~dma->enable_bit; msm_write(port, val, UARTDM_DMEN); - if (mapped) - dma_unmap_single(dev, dma->phys, mapped, dma->dir); + if (mapped) { + if (dma->dir == DMA_TO_DEVICE) { + dma_unmap_sg(dev, &dma->tx_sg, 1, dma->dir); + sg_init_table(&dma->tx_sg, 1); + } else + dma_unmap_single(dev, dma->rx.phys, mapped, dma->dir); + } } static void msm_release_dma(struct msm_port *msm_port) @@ -285,7 +299,7 @@ static void msm_release_dma(struct msm_port *msm_port) if (dma->chan) { msm_stop_dma(&msm_port->uart, dma); dma_release_channel(dma->chan); - kfree(dma->virt); + kfree(dma->rx.virt); } memset(dma, 0, sizeof(*dma)); @@ -357,8 +371,8 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) of_property_read_u32(dev->of_node, "qcom,rx-crci", &crci); - dma->virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL); - if (!dma->virt) + dma->rx.virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL); + if (!dma->rx.virt) goto rel_rx; memset(&conf, 0, sizeof(conf)); @@ -385,7 +399,7 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) return; err: - kfree(dma->virt); + kfree(dma->rx.virt); rel_rx: dma_release_channel(dma->chan); no_rx: @@ -420,7 +434,7 @@ static void msm_start_tx(struct uart_port *port) struct msm_dma *dma = &msm_port->tx_dma; /* Already started in DMA mode */ - if (dma->count) + if (sg_dma_len(&dma->tx_sg)) return; msm_port->imr |= MSM_UART_IMR_TXLEV; @@ -448,12 +462,12 @@ static void msm_complete_tx_dma(void *args) uart_port_lock_irqsave(port, &flags); /* Already stopped */ - if (!dma->count) + if (!sg_dma_len(&dma->tx_sg)) goto done; dmaengine_tx_status(dma->chan, dma->cookie, &state); - dma_unmap_single(port->dev, dma->phys, dma->count, dma->dir); + dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir); val = msm_read(port, UARTDM_DMEN); val &= ~dma->enable_bit; @@ -464,9 +478,9 @@ static void msm_complete_tx_dma(void *args) msm_write(port, MSM_UART_CR_TX_ENABLE, MSM_UART_CR); } - count = dma->count - state.residue; + count = sg_dma_len(&dma->tx_sg) - state.residue; uart_xmit_advance(port, count); - dma->count = 0; + sg_init_table(&dma->tx_sg, 1); /* Restore "Tx FIFO below watermark" interrupt */ msm_port->imr |= MSM_UART_IMR_TXLEV; @@ -485,19 +499,18 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) struct circ_buf *xmit = &msm_port->uart.state->xmit; struct uart_port *port = &msm_port->uart; struct msm_dma *dma = &msm_port->tx_dma; - void *cpu_addr; int ret; u32 val; - cpu_addr = &xmit->buf[xmit->tail]; + sg_init_table(&dma->tx_sg, 1); + sg_set_buf(&dma->tx_sg, &xmit->buf[xmit->tail], count); - dma->phys = dma_map_single(port->dev, cpu_addr, count, dma->dir); - ret = dma_mapping_error(port->dev, dma->phys); + ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir); if (ret) return ret; - dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys, - count, DMA_MEM_TO_DEV, + dma->desc = dmaengine_prep_slave_sg(dma->chan, &dma->tx_sg, 1, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_PREP_FENCE); if (!dma->desc) { @@ -520,8 +533,6 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) msm_port->imr &= ~MSM_UART_IMR_TXLEV; msm_write(port, msm_port->imr, MSM_UART_IMR); - dma->count = count; - val = msm_read(port, UARTDM_DMEN); val |= dma->enable_bit; @@ -536,7 +547,8 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) dma_async_issue_pending(dma->chan); return 0; unmap: - dma_unmap_single(port->dev, dma->phys, count, dma->dir); + dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir); + sg_init_table(&dma->tx_sg, 1); return ret; } @@ -553,7 +565,7 @@ static void msm_complete_rx_dma(void *args) uart_port_lock_irqsave(port, &flags); /* Already stopped */ - if (!dma->count) + if (!dma->rx.count) goto done; val = msm_read(port, UARTDM_DMEN); @@ -570,14 +582,14 @@ static void msm_complete_rx_dma(void *args) port->icount.rx += count; - dma->count = 0; + dma->rx.count = 0; - dma_unmap_single(port->dev, dma->phys, UARTDM_RX_SIZE, dma->dir); + dma_unmap_single(port->dev, dma->rx.phys, UARTDM_RX_SIZE, dma->dir); for (i = 0; i < count; i++) { char flag = TTY_NORMAL; - if (msm_port->break_detected && dma->virt[i] == 0) { + if (msm_port->break_detected && dma->rx.virt[i] == 0) { port->icount.brk++; flag = TTY_BREAK; msm_port->break_detected = false; @@ -588,9 +600,9 @@ static void msm_complete_rx_dma(void *args) if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK)) flag = TTY_NORMAL; - sysrq = uart_prepare_sysrq_char(port, dma->virt[i]); + sysrq = uart_prepare_sysrq_char(port, dma->rx.virt[i]); if (!sysrq) - tty_insert_flip_char(tport, dma->virt[i], flag); + tty_insert_flip_char(tport, dma->rx.virt[i], flag); } msm_start_rx_dma(msm_port); @@ -614,13 +626,13 @@ static void msm_start_rx_dma(struct msm_port *msm_port) if (!dma->chan) return; - dma->phys = dma_map_single(uart->dev, dma->virt, + dma->rx.phys = dma_map_single(uart->dev, dma->rx.virt, UARTDM_RX_SIZE, dma->dir); - ret = dma_mapping_error(uart->dev, dma->phys); + ret = dma_mapping_error(uart->dev, dma->rx.phys); if (ret) goto sw_mode; - dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys, + dma->desc = dmaengine_prep_slave_single(dma->chan, dma->rx.phys, UARTDM_RX_SIZE, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!dma->desc) @@ -648,7 +660,7 @@ static void msm_start_rx_dma(struct msm_port *msm_port) msm_write(uart, msm_port->imr, MSM_UART_IMR); - dma->count = UARTDM_RX_SIZE; + dma->rx.count = UARTDM_RX_SIZE; dma_async_issue_pending(dma->chan); @@ -668,7 +680,7 @@ static void msm_start_rx_dma(struct msm_port *msm_port) return; unmap: - dma_unmap_single(uart->dev, dma->phys, UARTDM_RX_SIZE, dma->dir); + dma_unmap_single(uart->dev, dma->rx.phys, UARTDM_RX_SIZE, dma->dir); sw_mode: /* @@ -955,7 +967,7 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) } if (misr & (MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE)) { - if (dma->count) { + if (dma->rx.count) { val = MSM_UART_CR_CMD_STALE_EVENT_DISABLE; msm_write(port, val, MSM_UART_CR); val = MSM_UART_CR_CMD_RESET_STALE_INT; From 1788cf6a91d9fa9aa61fc2917afe192c23d67f6a Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:23 +0200 Subject: [PATCH 012/113] tty: serial: switch from circ_buf to kfifo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch from struct circ_buf to proper kfifo. kfifo provides much better API, esp. when wrap-around of the buffer needs to be taken into account. Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example. Kfifo API can also fill in scatter-gather DMA structures, so it easier for that use case too. Look at lpuart_dma_tx() for example. Note that not all drivers can be converted to that (like atmel_serial), they handle DMA specially. Note that usb-serial uses kfifo for TX for ages. omap needed a bit more care as it needs to put a char into FIFO to start the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to do kfifo_dma_out_prepare twice: once to find out the tx_size (to find out if it is worths to do DMA at all -- size >= 4), the second time for the actual transfer. All traces of circ_buf are removed from serial_core.h (and its struct uart_state). Signed-off-by: Jiri Slaby (SUSE) Cc: Al Cooper Cc: Matthias Brugger Cc: AngeloGioacchino Del Regno Cc: Kumaravel Thiagarajan Cc: Tharun Kumar P Cc: Russell King Cc: Vineet Gupta Cc: Richard Genoud Cc: Nicolas Ferre Cc: Alexandre Belloni Cc: Claudiu Beznea Cc: Alexander Shiyan Cc: Baruch Siach Cc: Maciej W. Rozycki Cc: Shawn Guo Cc: Sascha Hauer Cc: Fabio Estevam Cc: Neil Armstrong Cc: Kevin Hilman Cc: Jerome Brunet Cc: Martin Blumenstingl Cc: Taichi Sugaya Cc: Takao Orito Cc: Bjorn Andersson Cc: Konrad Dybcio Cc: Pali Rohár Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Christophe Leroy Cc: Aneesh Kumar K.V Cc: Naveen N. Rao Cc: Manivannan Sadhasivam Cc: Krzysztof Kozlowski Cc: Alim Akhtar Cc: Laxman Dewangan Cc: Thierry Reding Cc: Jonathan Hunter Cc: Orson Zhai Cc: Baolin Wang Cc: Chunyan Zhang Cc: Patrice Chotard Cc: Maxime Coquelin Cc: Alexandre Torgue Cc: David S. Miller Cc: Hammer Hsieh Cc: Peter Korsgaard Cc: Timur Tabi Cc: Michal Simek Cc: Sumit Semwal Cc: Christian König Link: https://lore.kernel.org/r/20240405060826.2521-13-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_bcm7271.c | 14 +++--- drivers/tty/serial/8250/8250_core.c | 3 +- drivers/tty/serial/8250/8250_dma.c | 23 ++++++---- drivers/tty/serial/8250/8250_exar.c | 5 ++- drivers/tty/serial/8250/8250_mtk.c | 2 +- drivers/tty/serial/8250/8250_omap.c | 47 +++++++++++++------- drivers/tty/serial/8250/8250_pci1xxxx.c | 50 ++++++++++------------ drivers/tty/serial/8250/8250_port.c | 22 ++++++---- drivers/tty/serial/amba-pl011.c | 46 +++++++++----------- drivers/tty/serial/ar933x_uart.c | 15 +++---- drivers/tty/serial/arc_uart.c | 8 ++-- drivers/tty/serial/atmel_serial.c | 57 +++++++++++++------------ drivers/tty/serial/clps711x.c | 12 +++--- drivers/tty/serial/cpm_uart.c | 20 +++------ drivers/tty/serial/digicolor-usart.c | 12 +++--- drivers/tty/serial/dz.c | 13 +++--- drivers/tty/serial/fsl_linflexuart.c | 17 ++++---- drivers/tty/serial/fsl_lpuart.c | 39 ++++++++--------- drivers/tty/serial/icom.c | 25 +++-------- drivers/tty/serial/imx.c | 54 ++++++++++------------- drivers/tty/serial/ip22zilog.c | 26 ++++++----- drivers/tty/serial/jsm/jsm_cls.c | 31 ++++---------- drivers/tty/serial/jsm/jsm_neo.c | 38 ++++++----------- drivers/tty/serial/max3100.c | 14 +++--- drivers/tty/serial/max310x.c | 33 ++++++-------- drivers/tty/serial/men_z135_uart.c | 26 +++++------ drivers/tty/serial/meson_uart.c | 11 +++-- drivers/tty/serial/milbeaut_usio.c | 15 ++++--- drivers/tty/serial/msm_serial.c | 30 ++++++------- drivers/tty/serial/mvebu-uart.c | 8 ++-- drivers/tty/serial/mxs-auart.c | 23 +++------- drivers/tty/serial/pch_uart.c | 21 +++++---- drivers/tty/serial/pic32_uart.c | 15 +++---- drivers/tty/serial/pmac_zilog.c | 24 +++++------ drivers/tty/serial/qcom_geni_serial.c | 36 ++++++++-------- drivers/tty/serial/rda-uart.c | 17 +++----- drivers/tty/serial/samsung_tty.c | 54 ++++++++++++----------- drivers/tty/serial/sb1250-duart.c | 13 +++--- drivers/tty/serial/sc16is7xx.c | 40 +++++++---------- drivers/tty/serial/sccnxp.c | 16 ++++--- drivers/tty/serial/serial-tegra.c | 43 ++++++++++--------- drivers/tty/serial/serial_core.c | 56 +++++++++--------------- drivers/tty/serial/serial_port.c | 2 +- drivers/tty/serial/sh-sci.c | 51 +++++++++++----------- drivers/tty/serial/sprd_serial.c | 20 ++++----- drivers/tty/serial/st-asc.c | 4 +- drivers/tty/serial/stm32-usart.c | 52 +++++++++------------- drivers/tty/serial/sunhv.c | 35 ++++++++------- drivers/tty/serial/sunplus-uart.c | 16 ++++--- drivers/tty/serial/sunsab.c | 30 +++++++------ drivers/tty/serial/sunsu.c | 15 ++++--- drivers/tty/serial/sunzilog.c | 27 ++++++------ drivers/tty/serial/tegra-tcu.c | 10 +++-- drivers/tty/serial/timbuart.c | 17 +++----- drivers/tty/serial/uartlite.c | 13 +++--- drivers/tty/serial/ucc_uart.c | 20 +++------ drivers/tty/serial/xilinx_uartps.c | 20 ++++----- drivers/tty/serial/zs.c | 13 +++--- include/linux/serial_core.h | 49 +++++++++++++-------- 59 files changed, 688 insertions(+), 780 deletions(-) diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 5daa38d9c64e..de270863eb5e 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -413,20 +413,18 @@ static int stop_tx_dma(struct uart_8250_port *p) static int brcmuart_tx_dma(struct uart_8250_port *p) { struct brcmuart_priv *priv = p->port.private_data; - struct circ_buf *xmit = &p->port.state->xmit; + struct tty_port *tport = &p->port.state->port; u32 tx_size; if (uart_tx_stopped(&p->port) || priv->tx_running || - uart_circ_empty(xmit)) { + kfifo_is_empty(&tport->xmit_fifo)) { return 0; } - tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); priv->dma.tx_err = 0; - memcpy(priv->tx_buf, &xmit->buf[xmit->tail], tx_size); - uart_xmit_advance(&p->port, tx_size); + tx_size = uart_fifo_out(&p->port, priv->tx_buf, UART_XMIT_SIZE); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&p->port); udma_writel(priv, REGS_DMA_TX, UDMA_TX_TRANSFER_LEN, tx_size); @@ -540,7 +538,7 @@ static void brcmuart_tx_isr(struct uart_port *up, u32 isr) struct brcmuart_priv *priv = up->private_data; struct device *dev = up->dev; struct uart_8250_port *port_8250 = up_to_u8250p(up); - struct circ_buf *xmit = &port_8250->port.state->xmit; + struct tty_port *tport = &port_8250->port.state->port; if (isr & UDMA_INTR_TX_ABORT) { if (priv->tx_running) @@ -548,7 +546,7 @@ static void brcmuart_tx_isr(struct uart_port *up, u32 isr) return; } priv->tx_running = false; - if (!uart_circ_empty(xmit) && !uart_tx_stopped(up)) + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(up)) brcmuart_tx_dma(port_8250); } diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index b62ad9006780..3a1936b7f05f 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -280,7 +280,8 @@ static void serial8250_backup_timeout(struct timer_list *t) */ lsr = serial_lsr_in(up); if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && - (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) && + (!kfifo_is_empty(&up->port.state->port.xmit_fifo) || + up->port.x_char) && (lsr & UART_LSR_THRE)) { iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); iir |= UART_IIR_THRI; diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 8b2c3f478b17..8a353e3cc3dd 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -15,7 +15,7 @@ static void __dma_tx_complete(void *param) { struct uart_8250_port *p = param; struct uart_8250_dma *dma = p->dma; - struct circ_buf *xmit = &p->port.state->xmit; + struct tty_port *tport = &p->port.state->port; unsigned long flags; int ret; @@ -28,7 +28,7 @@ static void __dma_tx_complete(void *param) uart_xmit_advance(&p->port, dma->tx_size); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&p->port); ret = serial8250_tx_dma(p); @@ -86,7 +86,7 @@ static void dma_rx_complete(void *param) int serial8250_tx_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; - struct circ_buf *xmit = &p->port.state->xmit; + struct tty_port *tport = &p->port.state->port; struct dma_async_tx_descriptor *desc; struct uart_port *up = &p->port; struct scatterlist sg; @@ -103,18 +103,23 @@ int serial8250_tx_dma(struct uart_8250_port *p) uart_xchar_out(up, UART_TX); } - if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) { + if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) { /* We have been called from __dma_tx_complete() */ return 0; } - dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - serial8250_do_prepare_tx_dma(p); sg_init_table(&sg, 1); - sg_dma_address(&sg) = dma->tx_addr + xmit->tail; - sg_dma_len(&sg) = dma->tx_size; + /* kfifo can do more than one sg, we don't (quite yet) */ + ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1, + UART_XMIT_SIZE, dma->tx_addr); + + /* we already checked empty fifo above, so there should be something */ + if (WARN_ON_ONCE(ret != 1)) + return 0; + + dma->tx_size = sg_dma_len(&sg); desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV, @@ -257,7 +262,7 @@ int serial8250_request_dma(struct uart_8250_port *p) /* TX buffer */ dma->tx_addr = dma_map_single(dma->txchan->device->dev, - p->port.state->xmit.buf, + p->port.state->port.xmit_buf, UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) { diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 0440df7de1ed..f89dabfc0f97 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -214,7 +214,7 @@ static void exar_shutdown(struct uart_port *port) { bool tx_complete = false; struct uart_8250_port *up = up_to_u8250p(port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; int i = 0; u16 lsr; @@ -225,7 +225,8 @@ static void exar_shutdown(struct uart_port *port) else tx_complete = false; usleep_range(1000, 1100); - } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000); + } while (!kfifo_is_empty(&tport->xmit_fifo) && + !tx_complete && i++ < 1000); serial8250_do_shutdown(port); } diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 9ff6bbe9c086..c365a349421a 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -199,7 +199,7 @@ static int mtk8250_startup(struct uart_port *port) if (up->dma) { data->rx_status = DMA_RX_START; - uart_circ_clear(&port->state->xmit); + kfifo_reset(&port->state->port.xmit_fifo); } #endif memset(&port->icount, 0, sizeof(port->icount)); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 879e77b30701..c07d924d5add 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1094,7 +1094,7 @@ static void omap_8250_dma_tx_complete(void *param) { struct uart_8250_port *p = param; struct uart_8250_dma *dma = p->dma; - struct circ_buf *xmit = &p->port.state->xmit; + struct tty_port *tport = &p->port.state->port; unsigned long flags; bool en_thri = false; struct omap8250_priv *priv = p->port.private_data; @@ -1113,10 +1113,10 @@ static void omap_8250_dma_tx_complete(void *param) omap8250_restore_regs(p); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&p->port); - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) { + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(&p->port)) { int ret; ret = omap_8250_tx_dma(p); @@ -1138,15 +1138,15 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; struct omap8250_priv *priv = p->port.private_data; - struct circ_buf *xmit = &p->port.state->xmit; + struct tty_port *tport = &p->port.state->port; struct dma_async_tx_descriptor *desc; struct scatterlist sg; - unsigned int skip_byte = 0; + int skip_byte = -1; int ret; if (dma->tx_running) return 0; - if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) { + if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) { /* * Even if no data, we need to return an error for the two cases @@ -1161,8 +1161,18 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) return 0; } - dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + sg_init_table(&sg, 1); + ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1, + UART_XMIT_SIZE, dma->tx_addr); + if (ret != 1) { + serial8250_clear_THRI(p); + return 0; + } + + dma->tx_size = sg_dma_len(&sg); + if (priv->habit & OMAP_DMA_TX_KICK) { + unsigned char c; u8 tx_lvl; /* @@ -1189,13 +1199,16 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) ret = -EINVAL; goto err; } - skip_byte = 1; + if (!kfifo_get(&tport->xmit_fifo, &c)) { + ret = -EINVAL; + goto err; + } + skip_byte = c; + /* now we need to recompute due to kfifo_get */ + kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1, + UART_XMIT_SIZE, dma->tx_addr); } - sg_init_table(&sg, 1); - sg_dma_address(&sg) = dma->tx_addr + xmit->tail + skip_byte; - sg_dma_len(&sg) = dma->tx_size - skip_byte; - desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { @@ -1218,11 +1231,13 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) dma->tx_err = 0; serial8250_clear_THRI(p); - if (skip_byte) - serial_out(p, UART_TX, xmit->buf[xmit->tail]); - return 0; + ret = 0; + goto out_skip; err: dma->tx_err = 1; +out_skip: + if (skip_byte >= 0) + serial_out(p, UART_TX, skip_byte); return ret; } @@ -1311,7 +1326,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) serial8250_modem_status(up); if (status & UART_LSR_THRE && up->dma->tx_err) { if (uart_tx_stopped(&up->port) || - uart_circ_empty(&up->port.state->xmit)) { + kfifo_is_empty(&up->port.state->port.xmit_fifo)) { up->dma->tx_err = 0; serial8250_tx_chars(up); } else { diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c index 2fbb5851f788..d3930bf32fe4 100644 --- a/drivers/tty/serial/8250/8250_pci1xxxx.c +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c @@ -382,10 +382,10 @@ static void pci1xxxx_rx_burst(struct uart_port *port, u32 uart_status) } static void pci1xxxx_process_write_data(struct uart_port *port, - struct circ_buf *xmit, int *data_empty_count, u32 *valid_byte_count) { + struct tty_port *tport = &port->state->port; u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE; /* @@ -395,41 +395,36 @@ static void pci1xxxx_process_write_data(struct uart_port *port, * one byte at a time. */ while (valid_burst_count) { + u32 c; + if (*data_empty_count - UART_BURST_SIZE < 0) break; - if (xmit->tail > (UART_XMIT_SIZE - UART_BURST_SIZE)) + if (kfifo_len(&tport->xmit_fifo) < UART_BURST_SIZE) break; - writel(*(unsigned int *)&xmit->buf[xmit->tail], - port->membase + UART_TX_BURST_FIFO); + if (WARN_ON(kfifo_out(&tport->xmit_fifo, (u8 *)&c, sizeof(c)) != + sizeof(c))) + break; + writel(c, port->membase + UART_TX_BURST_FIFO); *valid_byte_count -= UART_BURST_SIZE; *data_empty_count -= UART_BURST_SIZE; valid_burst_count -= UART_BYTE_SIZE; - - xmit->tail = (xmit->tail + UART_BURST_SIZE) & - (UART_XMIT_SIZE - 1); } while (*valid_byte_count) { - if (*data_empty_count - UART_BYTE_SIZE < 0) + u8 c; + + if (!kfifo_get(&tport->xmit_fifo, &c)) break; - writeb(xmit->buf[xmit->tail], port->membase + - UART_TX_BYTE_FIFO); + writeb(c, port->membase + UART_TX_BYTE_FIFO); *data_empty_count -= UART_BYTE_SIZE; *valid_byte_count -= UART_BYTE_SIZE; - /* - * When the tail of the circular buffer is reached, the next - * byte is transferred to the beginning of the buffer. - */ - xmit->tail = (xmit->tail + UART_BYTE_SIZE) & - (UART_XMIT_SIZE - 1); - /* * If there are any pending burst count, data is handled by * transmitting DWORDs at a time. */ - if (valid_burst_count && (xmit->tail < - (UART_XMIT_SIZE - UART_BURST_SIZE))) + if (valid_burst_count && + kfifo_len(&tport->xmit_fifo) >= UART_BURST_SIZE) break; } } @@ -437,11 +432,9 @@ static void pci1xxxx_process_write_data(struct uart_port *port, static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status) { struct uart_8250_port *up = up_to_u8250p(port); + struct tty_port *tport = &port->state->port; u32 valid_byte_count; int data_empty_count; - struct circ_buf *xmit; - - xmit = &port->state->xmit; if (port->x_char) { writeb(port->x_char, port->membase + UART_TX); @@ -450,25 +443,25 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status) return; } - if ((uart_tx_stopped(port)) || (uart_circ_empty(xmit))) { + if ((uart_tx_stopped(port)) || kfifo_is_empty(&tport->xmit_fifo)) { port->ops->stop_tx(port); } else { data_empty_count = (pci1xxxx_read_burst_status(port) & UART_BST_STAT_TX_COUNT_MASK) >> 8; do { - valid_byte_count = uart_circ_chars_pending(xmit); + valid_byte_count = kfifo_len(&tport->xmit_fifo); - pci1xxxx_process_write_data(port, xmit, + pci1xxxx_process_write_data(port, &data_empty_count, &valid_byte_count); port->icount.tx++; - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) break; } while (data_empty_count && valid_byte_count); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); /* @@ -476,7 +469,8 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status) * the HW can go idle. So we get here once again with empty FIFO and * disable the interrupt and RPM in __stop_tx() */ - if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)) + if (kfifo_is_empty(&tport->xmit_fifo) && + !(up->capabilities & UART_CAP_RPM)) port->ops->stop_tx(port); } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index fc9dd5d45295..0c19451d0332 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1630,7 +1630,7 @@ static void serial8250_start_tx(struct uart_port *port) /* Port locked to synchronize UART_IER access against the console. */ lockdep_assert_held_once(&port->lock); - if (!port->x_char && uart_circ_empty(&port->state->xmit)) + if (!port->x_char && kfifo_is_empty(&port->state->port.xmit_fifo)) return; serial8250_rpm_get_tx(up); @@ -1778,7 +1778,7 @@ EXPORT_SYMBOL_GPL(serial8250_rx_chars); void serial8250_tx_chars(struct uart_8250_port *up) { struct uart_port *port = &up->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; int count; if (port->x_char) { @@ -1789,14 +1789,19 @@ void serial8250_tx_chars(struct uart_8250_port *up) serial8250_stop_tx(port); return; } - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { __stop_tx(up); return; } count = up->tx_loadsz; do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); + unsigned char c; + + if (!uart_fifo_get(port, &c)) + break; + + serial_out(up, UART_TX, c); if (up->bugs & UART_BUG_TXRACE) { /* * The Aspeed BMC virtual UARTs have a bug where data @@ -1809,9 +1814,7 @@ void serial8250_tx_chars(struct uart_8250_port *up) */ serial_in(up, UART_SCR); } - uart_xmit_advance(port, 1); - if (uart_circ_empty(xmit)) - break; + if ((up->capabilities & UART_CAP_HFIFO) && !uart_lsr_tx_empty(serial_in(up, UART_LSR))) break; @@ -1821,7 +1824,7 @@ void serial8250_tx_chars(struct uart_8250_port *up) break; } while (--count > 0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); /* @@ -1829,7 +1832,8 @@ void serial8250_tx_chars(struct uart_8250_port *up) * HW can go idle. So we get here once again with empty FIFO and disable * the interrupt and RPM in __stop_tx() */ - if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)) + if (kfifo_is_empty(&tport->xmit_fifo) && + !(up->capabilities & UART_CAP_RPM)) __stop_tx(up); } EXPORT_SYMBOL_GPL(serial8250_tx_chars); diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 2fa3fb30dc6c..51b202eb8296 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -535,6 +535,7 @@ static void pl011_start_tx_pio(struct uart_amba_port *uap); static void pl011_dma_tx_callback(void *data) { struct uart_amba_port *uap = data; + struct tty_port *tport = &uap->port.state->port; struct pl011_dmatx_data *dmatx = &uap->dmatx; unsigned long flags; u16 dmacr; @@ -558,7 +559,7 @@ static void pl011_dma_tx_callback(void *data) * get further refills (hence we check dmacr). */ if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) || - uart_circ_empty(&uap->port.state->xmit)) { + kfifo_is_empty(&tport->xmit_fifo)) { uap->dmatx.queued = false; uart_port_unlock_irqrestore(&uap->port, flags); return; @@ -588,7 +589,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) struct dma_chan *chan = dmatx->chan; struct dma_device *dma_dev = chan->device; struct dma_async_tx_descriptor *desc; - struct circ_buf *xmit = &uap->port.state->xmit; + struct tty_port *tport = &uap->port.state->port; unsigned int count; /* @@ -597,7 +598,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) * the standard interrupt handling. This ensures that we * issue a uart_write_wakeup() at the appropriate time. */ - count = uart_circ_chars_pending(xmit); + count = kfifo_len(&tport->xmit_fifo); if (count < (uap->fifosize >> 1)) { uap->dmatx.queued = false; return 0; @@ -613,21 +614,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) if (count > PL011_DMA_BUFFER_SIZE) count = PL011_DMA_BUFFER_SIZE; - if (xmit->tail < xmit->head) { - memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count); - } else { - size_t first = UART_XMIT_SIZE - xmit->tail; - size_t second; - - if (first > count) - first = count; - second = count - first; - - memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], first); - if (second) - memcpy(&dmatx->buf[first], &xmit->buf[0], second); - } - + count = kfifo_out_peek(&tport->xmit_fifo, dmatx->buf, count); dmatx->len = count; dmatx->dma = dma_map_single(dma_dev->dev, dmatx->buf, count, DMA_TO_DEVICE); @@ -670,7 +657,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) */ uart_xmit_advance(&uap->port, count); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); return 1; @@ -1454,7 +1441,7 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, /* Returns true if tx interrupts have to be (kept) enabled */ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) { - struct circ_buf *xmit = &uap->port.state->xmit; + struct tty_port *tport = &uap->port.state->port; int count = uap->fifosize >> 1; if (uap->port.x_char) { @@ -1463,7 +1450,7 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) uap->port.x_char = 0; --count; } - if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&uap->port)) { pl011_stop_tx(&uap->port); return false; } @@ -1472,20 +1459,25 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) if (pl011_dma_tx_irq(uap)) return true; - do { + while (1) { + unsigned char c; + if (likely(from_irq) && count-- == 0) break; - if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq)) + if (!kfifo_peek(&tport->xmit_fifo, &c)) break; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - } while (!uart_circ_empty(xmit)); + if (!pl011_tx_char(uap, c, from_irq)) + break; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + kfifo_skip(&tport->xmit_fifo); + } + + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { pl011_stop_tx(&uap->port); return false; } diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 7790cbc57391..f2836ecc5c19 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -390,7 +390,7 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up) static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) { - struct circ_buf *xmit = &up->port.state->xmit; + struct tty_port *tport = &up->port.state->port; struct serial_rs485 *rs485conf = &up->port.rs485; int count; bool half_duplex_send = false; @@ -399,7 +399,7 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) return; if ((rs485conf->flags & SER_RS485_ENABLED) && - (up->port.x_char || !uart_circ_empty(xmit))) { + (up->port.x_char || !kfifo_is_empty(&tport->xmit_fifo))) { ar933x_uart_stop_rx_interrupt(up); gpiod_set_value(up->rts_gpiod, !!(rs485conf->flags & SER_RS485_RTS_ON_SEND)); half_duplex_send = true; @@ -408,6 +408,7 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) count = up->port.fifosize; do { unsigned int rdata; + unsigned char c; rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG); if ((rdata & AR933X_UART_DATA_TX_CSR) == 0) @@ -420,18 +421,16 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) continue; } - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(&up->port, &c)) break; - ar933x_uart_putc(up, xmit->buf[xmit->tail]); - - uart_xmit_advance(&up->port, 1); + ar933x_uart_putc(up, c); } while (--count > 0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); - if (!uart_circ_empty(xmit)) { + if (!kfifo_is_empty(&tport->xmit_fifo)) { ar933x_uart_start_tx_interrupt(up); } else if (half_duplex_send) { ar933x_uart_wait_tx_complete(up); diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 1aa5b2b49c26..5c4895d154c0 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -155,7 +155,7 @@ static unsigned int arc_serial_tx_empty(struct uart_port *port) */ static void arc_serial_tx_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; int sent = 0; unsigned char ch; @@ -164,9 +164,7 @@ static void arc_serial_tx_chars(struct uart_port *port) port->icount.tx++; port->x_char = 0; sent = 1; - } else if (!uart_circ_empty(xmit)) { - ch = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); + } else if (uart_fifo_get(port, &ch)) { while (!(UART_GET_STATUS(port) & TXEMPTY)) cpu_relax(); UART_SET_DATA(port, ch); @@ -177,7 +175,7 @@ static void arc_serial_tx_chars(struct uart_port *port) * If num chars in xmit buffer are too few, ask tty layer for more. * By Hard ISR to schedule processing in software interrupt part */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); if (sent) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 85667f709515..5bb5e4303754 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -857,7 +857,7 @@ static void atmel_complete_tx_dma(void *arg) { struct atmel_uart_port *atmel_port = arg; struct uart_port *port = &atmel_port->uart; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct dma_chan *chan = atmel_port->chan_tx; unsigned long flags; @@ -873,15 +873,15 @@ static void atmel_complete_tx_dma(void *arg) atmel_port->desc_tx = NULL; spin_unlock(&atmel_port->lock_tx); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); /* - * xmit is a circular buffer so, if we have just send data from - * xmit->tail to the end of xmit->buf, now we have to transmit the - * remaining data from the beginning of xmit->buf to xmit->head. + * xmit is a circular buffer so, if we have just send data from the + * tail to the end, now we have to transmit the remaining data from the + * beginning to the head. */ - if (!uart_circ_empty(xmit)) + if (!kfifo_is_empty(&tport->xmit_fifo)) atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx); else if (atmel_uart_is_half_duplex(port)) { /* @@ -919,18 +919,18 @@ static void atmel_release_tx_dma(struct uart_port *port) static void atmel_tx_dma(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct dma_chan *chan = atmel_port->chan_tx; struct dma_async_tx_descriptor *desc; struct scatterlist sgl[2], *sg, *sg_tx = &atmel_port->sg_tx; - unsigned int tx_len, part1_len, part2_len, sg_len; + unsigned int tx_len, tail, part1_len, part2_len, sg_len; dma_addr_t phys_addr; /* Make sure we have an idle channel */ if (atmel_port->desc_tx != NULL) return; - if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) { /* * DMA is idle now. * Port xmit buffer is already mapped, @@ -940,9 +940,8 @@ static void atmel_tx_dma(struct uart_port *port) * Take the port lock to get a * consistent xmit buffer state. */ - tx_len = CIRC_CNT_TO_END(xmit->head, - xmit->tail, - UART_XMIT_SIZE); + tx_len = kfifo_out_linear(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); if (atmel_port->fifo_size) { /* multi data mode */ @@ -956,7 +955,7 @@ static void atmel_tx_dma(struct uart_port *port) sg_init_table(sgl, 2); sg_len = 0; - phys_addr = sg_dma_address(sg_tx) + xmit->tail; + phys_addr = sg_dma_address(sg_tx) + tail; if (part1_len) { sg = &sgl[sg_len++]; sg_dma_address(sg) = phys_addr; @@ -973,7 +972,7 @@ static void atmel_tx_dma(struct uart_port *port) /* * save tx_len so atmel_complete_tx_dma() will increase - * xmit->tail correctly + * tail correctly */ atmel_port->tx_len = tx_len; @@ -1003,13 +1002,14 @@ static void atmel_tx_dma(struct uart_port *port) dma_async_issue_pending(chan); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } static int atmel_prepare_tx_dma(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct tty_port *tport = &port->state->port; struct device *mfd_dev = port->dev->parent; dma_cap_mask_t mask; struct dma_slave_config config; @@ -1031,11 +1031,11 @@ static int atmel_prepare_tx_dma(struct uart_port *port) spin_lock_init(&atmel_port->lock_tx); sg_init_table(&atmel_port->sg_tx, 1); /* UART circular tx buffer is an aligned page. */ - BUG_ON(!PAGE_ALIGNED(port->state->xmit.buf)); + BUG_ON(!PAGE_ALIGNED(tport->xmit_buf)); sg_set_page(&atmel_port->sg_tx, - virt_to_page(port->state->xmit.buf), + virt_to_page(tport->xmit_buf), UART_XMIT_SIZE, - offset_in_page(port->state->xmit.buf)); + offset_in_page(tport->xmit_buf)); nent = dma_map_sg(port->dev, &atmel_port->sg_tx, 1, @@ -1047,7 +1047,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port) } else { dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__, sg_dma_len(&atmel_port->sg_tx), - port->state->xmit.buf, + tport->xmit_buf, &sg_dma_address(&atmel_port->sg_tx)); } @@ -1459,9 +1459,8 @@ static void atmel_release_tx_pdc(struct uart_port *port) static void atmel_tx_pdc(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; - int count; /* nothing left to transmit? */ if (atmel_uart_readl(port, ATMEL_PDC_TCR)) @@ -1474,17 +1473,19 @@ static void atmel_tx_pdc(struct uart_port *port) /* disable PDC transmit */ atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS); - if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) { + unsigned int count, tail; + dma_sync_single_for_device(port->dev, pdc->dma_addr, pdc->dma_size, DMA_TO_DEVICE); - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + count = kfifo_out_linear(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); pdc->ofs = count; - atmel_uart_writel(port, ATMEL_PDC_TPR, - pdc->dma_addr + xmit->tail); + atmel_uart_writel(port, ATMEL_PDC_TPR, pdc->dma_addr + tail); atmel_uart_writel(port, ATMEL_PDC_TCR, count); /* re-enable PDC transmit */ atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); @@ -1498,7 +1499,7 @@ static void atmel_tx_pdc(struct uart_port *port) } } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } @@ -1506,9 +1507,9 @@ static int atmel_prepare_tx_pdc(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; - pdc->buf = xmit->buf; + pdc->buf = tport->xmit_buf; pdc->dma_addr = dma_map_single(port->dev, pdc->buf, UART_XMIT_SIZE, diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 7927725b8957..30425a3d19fb 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -146,7 +146,8 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) { struct uart_port *port = dev_id; struct clps711x_port *s = dev_get_drvdata(port->dev); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char c; if (port->x_char) { writew(port->x_char, port->membase + UARTDR_OFFSET); @@ -155,7 +156,7 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) return IRQ_HANDLED; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { if (s->tx_enabled) { disable_irq_nosync(port->irq); s->tx_enabled = 0; @@ -163,18 +164,17 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) return IRQ_HANDLED; } - while (!uart_circ_empty(xmit)) { + while (uart_fifo_get(port, &c)) { u32 sysflg = 0; - writew(xmit->buf[xmit->tail], port->membase + UARTDR_OFFSET); - uart_xmit_advance(port, 1); + writew(c, port->membase + UARTDR_OFFSET); regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); if (sysflg & SYSFLG_UTXFF) break; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); return IRQ_HANDLED; diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c index df56c6c5afd0..a927478f581d 100644 --- a/drivers/tty/serial/cpm_uart.c +++ b/drivers/tty/serial/cpm_uart.c @@ -648,7 +648,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) int count; struct uart_cpm_port *pinfo = container_of(port, struct uart_cpm_port, port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; /* Handle xon/xoff */ if (port->x_char) { @@ -673,7 +673,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) return 1; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { cpm_uart_stop_tx(port); return 0; } @@ -681,16 +681,10 @@ static int cpm_uart_tx_pump(struct uart_port *port) /* Pick next descriptor and fill from buffer */ bdp = pinfo->tx_cur; - while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) && !uart_circ_empty(xmit)) { - count = 0; + while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) && + !kfifo_is_empty(&tport->xmit_fifo)) { p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo); - while (count < pinfo->tx_fifosize) { - *p++ = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); - count++; - if (uart_circ_empty(xmit)) - break; - } + count = uart_fifo_out(port, p, pinfo->tx_fifosize); out_be16(&bdp->cbd_datlen, count); setbits16(&bdp->cbd_sc, BD_SC_READY); /* Get next BD. */ @@ -701,10 +695,10 @@ static int cpm_uart_tx_pump(struct uart_port *port) } pinfo->tx_cur = bdp; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { cpm_uart_stop_tx(port); return 0; } diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c index e419c4bde8b7..2ccd13cc0a89 100644 --- a/drivers/tty/serial/digicolor-usart.c +++ b/drivers/tty/serial/digicolor-usart.c @@ -179,8 +179,9 @@ static void digicolor_uart_rx(struct uart_port *port) static void digicolor_uart_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned long flags; + unsigned char c; if (digicolor_uart_tx_full(port)) return; @@ -194,20 +195,19 @@ static void digicolor_uart_tx(struct uart_port *port) goto out; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { digicolor_uart_stop_tx(port); goto out; } - while (!uart_circ_empty(xmit)) { - writeb(xmit->buf[xmit->tail], port->membase + UA_EMI_REC); - uart_xmit_advance(port, 1); + while (uart_fifo_get(port, &c)) { + writeb(c, port->membase + UA_EMI_REC); if (digicolor_uart_tx_full(port)) break; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); out: diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index 6df7af9edc1c..eba91daedef8 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -252,13 +252,13 @@ static inline void dz_receive_chars(struct dz_mux *mux) static inline void dz_transmit_chars(struct dz_mux *mux) { struct dz_port *dport = &mux->dport[0]; - struct circ_buf *xmit; + struct tty_port *tport; unsigned char tmp; u16 status; status = dz_in(dport, DZ_CSR); dport = &mux->dport[LINE(status)]; - xmit = &dport->port.state->xmit; + tport = &dport->port.state->port; if (dport->port.x_char) { /* XON/XOFF chars */ dz_out(dport, DZ_TDR, dport->port.x_char); @@ -267,7 +267,8 @@ static inline void dz_transmit_chars(struct dz_mux *mux) return; } /* If nothing to do or stopped or hardware stopped. */ - if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { + if (uart_tx_stopped(&dport->port) || + !uart_fifo_get(&dport->port, &tmp)) { uart_port_lock(&dport->port); dz_stop_tx(&dport->port); uart_port_unlock(&dport->port); @@ -278,15 +279,13 @@ static inline void dz_transmit_chars(struct dz_mux *mux) * If something to do... (remember the dz has no output fifo, * so we go one char at a time) :-< */ - tmp = xmit->buf[xmit->tail]; dz_out(dport, DZ_TDR, tmp); - uart_xmit_advance(&dport->port, 1); - if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < DZ_WAKEUP_CHARS) uart_write_wakeup(&dport->port); /* Are we are done. */ - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { uart_port_lock(&dport->port); dz_stop_tx(&dport->port); uart_port_unlock(&dport->port); diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 5426322b5f0c..e972df4b188d 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -174,17 +174,18 @@ static void linflex_put_char(struct uart_port *sport, unsigned char c) static inline void linflex_transmit_buffer(struct uart_port *sport) { - struct circ_buf *xmit = &sport->state->xmit; + struct tty_port *tport = &sport->state->port; + unsigned char c; - while (!uart_circ_empty(xmit)) { - linflex_put_char(sport, xmit->buf[xmit->tail]); - uart_xmit_advance(sport, 1); + while (uart_fifo_get(sport, &c)) { + linflex_put_char(sport, c); + sport->icount.tx++; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(sport); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) linflex_stop_tx(sport); } @@ -200,7 +201,7 @@ static void linflex_start_tx(struct uart_port *port) static irqreturn_t linflex_txint(int irq, void *dev_id) { struct uart_port *sport = dev_id; - struct circ_buf *xmit = &sport->state->xmit; + struct tty_port *tport = &sport->state->port; unsigned long flags; uart_port_lock_irqsave(sport, &flags); @@ -210,7 +211,7 @@ static irqreturn_t linflex_txint(int irq, void *dev_id) goto out; } - if (uart_circ_empty(xmit) || uart_tx_stopped(sport)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(sport)) { linflex_stop_tx(sport); goto out; } diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index bbcbc91482af..ae5e1ecc48fc 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -473,7 +474,7 @@ static void lpuart32_stop_rx(struct uart_port *port) static void lpuart_dma_tx(struct lpuart_port *sport) { - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; struct scatterlist *sgl = sport->tx_sgl; struct device *dev = sport->port.dev; struct dma_chan *chan = sport->dma_tx_chan; @@ -482,18 +483,10 @@ static void lpuart_dma_tx(struct lpuart_port *sport) if (sport->dma_tx_in_progress) return; - sport->dma_tx_bytes = uart_circ_chars_pending(xmit); - - if (xmit->tail < xmit->head || xmit->head == 0) { - sport->dma_tx_nents = 1; - sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes); - } else { - sport->dma_tx_nents = 2; - sg_init_table(sgl, 2); - sg_set_buf(sgl, xmit->buf + xmit->tail, - UART_XMIT_SIZE - xmit->tail); - sg_set_buf(sgl + 1, xmit->buf, xmit->head); - } + sg_init_table(sgl, ARRAY_SIZE(sport->tx_sgl)); + sport->dma_tx_bytes = kfifo_len(&tport->xmit_fifo); + sport->dma_tx_nents = kfifo_dma_out_prepare(&tport->xmit_fifo, sgl, + ARRAY_SIZE(sport->tx_sgl), sport->dma_tx_bytes); ret = dma_map_sg(chan->device->dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); @@ -521,14 +514,15 @@ static void lpuart_dma_tx(struct lpuart_port *sport) static bool lpuart_stopped_or_empty(struct uart_port *port) { - return uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port); + return kfifo_is_empty(&port->state->port.xmit_fifo) || + uart_tx_stopped(port); } static void lpuart_dma_tx_complete(void *arg) { struct lpuart_port *sport = arg; struct scatterlist *sgl = &sport->tx_sgl[0]; - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; struct dma_chan *chan = sport->dma_tx_chan; unsigned long flags; @@ -545,7 +539,7 @@ static void lpuart_dma_tx_complete(void *arg) sport->dma_tx_in_progress = false; uart_port_unlock_irqrestore(&sport->port, flags); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); if (waitqueue_active(&sport->dma_wait)) { @@ -756,8 +750,9 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport) static inline void lpuart32_transmit_buffer(struct lpuart_port *sport) { - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; unsigned long txcnt; + unsigned char c; if (sport->port.x_char) { lpuart32_write(&sport->port, sport->port.x_char, UARTDATA); @@ -774,18 +769,18 @@ static inline void lpuart32_transmit_buffer(struct lpuart_port *sport) txcnt = lpuart32_read(&sport->port, UARTWATER); txcnt = txcnt >> UARTWATER_TXCNT_OFF; txcnt &= UARTWATER_COUNT_MASK; - while (!uart_circ_empty(xmit) && (txcnt < sport->txfifo_size)) { - lpuart32_write(&sport->port, xmit->buf[xmit->tail], UARTDATA); - uart_xmit_advance(&sport->port, 1); + while (txcnt < sport->txfifo_size && + uart_fifo_get(&sport->port, &c)) { + lpuart32_write(&sport->port, c, UARTDATA); txcnt = lpuart32_read(&sport->port, UARTWATER); txcnt = txcnt >> UARTWATER_TXCNT_OFF; txcnt &= UARTWATER_COUNT_MASK; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) lpuart32_stop_tx(&sport->port); } diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index a75eafbcbea3..29e42831df39 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -877,10 +877,10 @@ static void shutdown(struct icom_port *icom_port) static int icom_write(struct uart_port *port) { struct icom_port *icom_port = to_icom_port(port); + struct tty_port *tport = &port->state->port; unsigned long data_count; unsigned char cmdReg; unsigned long offset; - int temp_tail = port->state->xmit.tail; trace(icom_port, "WRITE", 0); @@ -890,16 +890,8 @@ static int icom_write(struct uart_port *port) return 0; } - data_count = 0; - while ((port->state->xmit.head != temp_tail) && - (data_count <= XMIT_BUFF_SZ)) { - - icom_port->xmit_buf[data_count++] = - port->state->xmit.buf[temp_tail]; - - temp_tail++; - temp_tail &= (UART_XMIT_SIZE - 1); - } + data_count = kfifo_out_peek(&tport->xmit_fifo, icom_port->xmit_buf, + XMIT_BUFF_SZ); if (data_count) { icom_port->statStg->xmit[0].flags = @@ -956,7 +948,8 @@ static inline void check_modem_status(struct icom_port *icom_port) static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) { - u16 count, i; + struct tty_port *tport = &icom_port->uart_port.state->port; + u16 count; if (port_int_reg & (INT_XMIT_COMPLETED)) { trace(icom_port, "XMIT_COMPLETE", 0); @@ -968,13 +961,7 @@ static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) count = le16_to_cpu(icom_port->statStg->xmit[0].leLength); icom_port->uart_port.icount.tx += count; - for (i=0; iuart_port.state->xmit); i++) { - - icom_port->uart_port.state->xmit.tail++; - icom_port->uart_port.state->xmit.tail &= - (UART_XMIT_SIZE - 1); - } + kfifo_skip_count(&tport->xmit_fifo, count); if (!icom_write(&icom_port->uart_port)) /* activate write queue */ diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index e14813250616..56b76a221082 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -8,6 +8,7 @@ * Copyright (C) 2004 Pengutronix */ +#include #include #include #include @@ -521,7 +522,8 @@ static void imx_uart_dma_tx(struct imx_port *sport); /* called with port.lock taken and irqs off */ static inline void imx_uart_transmit_buffer(struct imx_port *sport) { - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; + unsigned char c; if (sport->port.x_char) { /* Send next char */ @@ -531,7 +533,8 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || + uart_tx_stopped(&sport->port)) { imx_uart_stop_tx(&sport->port); return; } @@ -555,26 +558,22 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport) return; } - while (!uart_circ_empty(xmit) && - !(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)) { - /* send xmit->buf[xmit->tail] - * out the port here */ - imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0); - uart_xmit_advance(&sport->port, 1); - } + while (!(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL) && + uart_fifo_get(&sport->port, &c)) + imx_uart_writel(sport, c, URTX0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) imx_uart_stop_tx(&sport->port); } static void imx_uart_dma_tx_callback(void *data) { struct imx_port *sport = data; + struct tty_port *tport = &sport->port.state->port; struct scatterlist *sgl = &sport->tx_sgl[0]; - struct circ_buf *xmit = &sport->port.state->xmit; unsigned long flags; u32 ucr1; @@ -592,10 +591,11 @@ static void imx_uart_dma_tx_callback(void *data) sport->dma_is_txing = 0; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port)) + if (!kfifo_is_empty(&tport->xmit_fifo) && + !uart_tx_stopped(&sport->port)) imx_uart_dma_tx(sport); else if (sport->port.rs485.flags & SER_RS485_ENABLED) { u32 ucr4 = imx_uart_readl(sport, UCR4); @@ -609,7 +609,7 @@ static void imx_uart_dma_tx_callback(void *data) /* called with port.lock taken and irqs off */ static void imx_uart_dma_tx(struct imx_port *sport) { - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; struct scatterlist *sgl = sport->tx_sgl; struct dma_async_tx_descriptor *desc; struct dma_chan *chan = sport->dma_chan_tx; @@ -624,18 +624,10 @@ static void imx_uart_dma_tx(struct imx_port *sport) ucr4 &= ~UCR4_TCEN; imx_uart_writel(sport, ucr4, UCR4); - sport->tx_bytes = uart_circ_chars_pending(xmit); - - if (xmit->tail < xmit->head || xmit->head == 0) { - sport->dma_tx_nents = 1; - sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes); - } else { - sport->dma_tx_nents = 2; - sg_init_table(sgl, 2); - sg_set_buf(sgl, xmit->buf + xmit->tail, - UART_XMIT_SIZE - xmit->tail); - sg_set_buf(sgl + 1, xmit->buf, xmit->head); - } + sg_init_table(sgl, ARRAY_SIZE(sport->tx_sgl)); + sport->tx_bytes = kfifo_len(&tport->xmit_fifo); + sport->dma_tx_nents = kfifo_dma_out_prepare(&tport->xmit_fifo, sgl, + ARRAY_SIZE(sport->tx_sgl), sport->tx_bytes); ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); if (ret == 0) { @@ -653,8 +645,7 @@ static void imx_uart_dma_tx(struct imx_port *sport) desc->callback = imx_uart_dma_tx_callback; desc->callback_param = sport; - dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n", - uart_circ_chars_pending(xmit)); + dev_dbg(dev, "TX: prepare to send %u bytes by DMA.\n", sport->tx_bytes); ucr1 = imx_uart_readl(sport, UCR1); ucr1 |= UCR1_TXDMAEN; @@ -671,9 +662,10 @@ static void imx_uart_dma_tx(struct imx_port *sport) static void imx_uart_start_tx(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; + struct tty_port *tport = &sport->port.state->port; u32 ucr1; - if (!sport->port.x_char && uart_circ_empty(&port->state->xmit)) + if (!sport->port.x_char && kfifo_is_empty(&tport->xmit_fifo)) return; /* @@ -749,7 +741,7 @@ static void imx_uart_start_tx(struct uart_port *port) return; } - if (!uart_circ_empty(&port->state->xmit) && + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) imx_uart_dma_tx(sport); return; diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c index 320b29cd4683..c2cae50f06f3 100644 --- a/drivers/tty/serial/ip22zilog.c +++ b/drivers/tty/serial/ip22zilog.c @@ -355,7 +355,8 @@ static void ip22zilog_status_handle(struct uart_ip22zilog_port *up, static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up, struct zilog_channel *channel) { - struct circ_buf *xmit; + struct tty_port *tport; + unsigned char c; if (ZS_IS_CONS(up)) { unsigned char status = readb(&channel->control); @@ -398,20 +399,18 @@ static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up, if (up->port.state == NULL) goto ack_tx_int; - xmit = &up->port.state->xmit; - if (uart_circ_empty(xmit)) - goto ack_tx_int; + tport = &up->port.state->port; if (uart_tx_stopped(&up->port)) goto ack_tx_int; + if (!uart_fifo_get(&up->port, &c)) + goto ack_tx_int; up->flags |= IP22ZILOG_FLAG_TX_ACTIVE; - writeb(xmit->buf[xmit->tail], &channel->data); + writeb(c, &channel->data); ZSDELAY(); ZS_WSYNC(channel); - uart_xmit_advance(&up->port, 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); return; @@ -600,17 +599,16 @@ static void ip22zilog_start_tx(struct uart_port *port) port->icount.tx++; port->x_char = 0; } else { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char c; - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(port, &c)) return; - writeb(xmit->buf[xmit->tail], &channel->data); + writeb(c, &channel->data); ZSDELAY(); ZS_WSYNC(channel); - uart_xmit_advance(port, 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); } } diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c index ddbd42c09637..6e40792f92cf 100644 --- a/drivers/tty/serial/jsm/jsm_cls.c +++ b/drivers/tty/serial/jsm/jsm_cls.c @@ -443,20 +443,14 @@ static void cls_copy_data_from_uart_to_queue(struct jsm_channel *ch) static void cls_copy_data_from_queue_to_uart(struct jsm_channel *ch) { - u16 tail; + struct tty_port *tport; int n; - int qlen; u32 len_written = 0; - struct circ_buf *circ; if (!ch) return; - circ = &ch->uart_port.state->xmit; - - /* No data to write to the UART */ - if (uart_circ_empty(circ)) - return; + tport = &ch->uart_port.state->port; /* If port is "stopped", don't send any data to the UART */ if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING)) @@ -467,29 +461,22 @@ static void cls_copy_data_from_queue_to_uart(struct jsm_channel *ch) return; n = 32; - - /* cache tail of queue */ - tail = circ->tail & (UART_XMIT_SIZE - 1); - qlen = uart_circ_chars_pending(circ); - - /* Find minimum of the FIFO space, versus queue length */ - n = min(n, qlen); - while (n > 0) { - writeb(circ->buf[tail], &ch->ch_cls_uart->txrx); - tail = (tail + 1) & (UART_XMIT_SIZE - 1); + unsigned char c; + + if (!kfifo_get(&tport->xmit_fifo, &c)) + break; + + writeb(c, &ch->ch_cls_uart->txrx); n--; ch->ch_txcount++; len_written++; } - /* Update the final tail */ - circ->tail = tail & (UART_XMIT_SIZE - 1); - if (len_written > ch->ch_t_tlevel) ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - if (uart_circ_empty(circ)) + if (kfifo_is_empty(&tport->xmit_fifo)) uart_write_wakeup(&ch->uart_port); } diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 1fa10f19368f..e8e13bf056e2 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -474,21 +474,21 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch) static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) { - u16 head; - u16 tail; + struct tty_port *tport; + unsigned char *tail; + unsigned char c; int n; int s; int qlen; u32 len_written = 0; - struct circ_buf *circ; if (!ch) return; - circ = &ch->uart_port.state->xmit; + tport = &ch->uart_port.state->port; /* No data to write to the UART */ - if (uart_circ_empty(circ)) + if (kfifo_is_empty(&tport->xmit_fifo)) return; /* If port is "stopped", don't send any data to the UART */ @@ -504,10 +504,9 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) if (ch->ch_cached_lsr & UART_LSR_THRE) { ch->ch_cached_lsr &= ~(UART_LSR_THRE); - writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx); - jsm_dbg(WRITE, &ch->ch_bd->pci_dev, - "Tx data: %x\n", circ->buf[circ->tail]); - circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1); + WARN_ON_ONCE(!kfifo_get(&tport->xmit_fifo, &c)); + writeb(c, &ch->ch_neo_uart->txrx); + jsm_dbg(WRITE, &ch->ch_bd->pci_dev, "Tx data: %x\n", c); ch->ch_txcount++; } return; @@ -520,38 +519,27 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) return; n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel; - - /* cache head and tail of queue */ - head = circ->head & (UART_XMIT_SIZE - 1); - tail = circ->tail & (UART_XMIT_SIZE - 1); - qlen = uart_circ_chars_pending(circ); + qlen = kfifo_len(&tport->xmit_fifo); /* Find minimum of the FIFO space, versus queue length */ n = min(n, qlen); while (n > 0) { - - s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail; - s = min(s, n); - + s = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, n); if (s <= 0) break; - memcpy_toio(&ch->ch_neo_uart->txrxburst, circ->buf + tail, s); - /* Add and flip queue if needed */ - tail = (tail + s) & (UART_XMIT_SIZE - 1); + memcpy_toio(&ch->ch_neo_uart->txrxburst, tail, s); + kfifo_skip_count(&tport->xmit_fifo, s); n -= s; ch->ch_txcount += s; len_written += s; } - /* Update the final tail */ - circ->tail = tail & (UART_XMIT_SIZE - 1); - if (len_written >= ch->ch_t_tlevel) ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - if (uart_circ_empty(circ)) + if (kfifo_is_empty(&tport->xmit_fifo)) uart_write_wakeup(&ch->uart_port); } diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 5efb2b593be3..b83eee37c17d 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -257,10 +257,11 @@ static int max3100_handlerx(struct max3100_port *s, u16 rx) static void max3100_work(struct work_struct *w) { struct max3100_port *s = container_of(w, struct max3100_port, work); + struct tty_port *tport = &s->port.state->port; + unsigned char ch; int rxchars; u16 tx, rx; int conf, cconf, crts; - struct circ_buf *xmit = &s->port.state->xmit; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -290,10 +291,9 @@ static void max3100_work(struct work_struct *w) tx = s->port.x_char; s->port.icount.tx++; s->port.x_char = 0; - } else if (!uart_circ_empty(xmit) && - !uart_tx_stopped(&s->port)) { - tx = xmit->buf[xmit->tail]; - uart_xmit_advance(&s->port, 1); + } else if (!uart_tx_stopped(&s->port) && + uart_fifo_get(&s->port, &ch)) { + tx = ch; } if (tx != 0xffff) { max3100_calc_parity(s, &tx); @@ -307,13 +307,13 @@ static void max3100_work(struct work_struct *w) tty_flip_buffer_push(&s->port.state->port); rxchars = 0; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&s->port); } while (!s->force_end_work && !freezing(current) && ((rx & MAX3100_R) || - (!uart_circ_empty(xmit) && + (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(&s->port)))); if (rxchars > 0) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 14dd9cfaa9f7..f0eb96429dae 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -747,8 +747,9 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) static void max310x_handle_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; - unsigned int txlen, to_send, until_end; + struct tty_port *tport = &port->state->port; + unsigned int txlen, to_send; + unsigned char *tail; if (unlikely(port->x_char)) { max310x_port_write(port, MAX310X_THR_REG, port->x_char); @@ -757,32 +758,26 @@ static void max310x_handle_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) return; - /* Get length of data pending in circular buffer */ - to_send = uart_circ_chars_pending(xmit); - until_end = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - if (likely(to_send)) { + /* + * It's a circ buffer -- wrap around. + * We could do that in one SPI transaction, but meh. + */ + while (!kfifo_is_empty(&tport->xmit_fifo)) { /* Limit to space available in TX FIFO */ txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); txlen = port->fifosize - txlen; - to_send = (to_send > txlen) ? txlen : to_send; + if (!txlen) + break; - if (until_end < to_send) { - /* - * It's a circ buffer -- wrap around. - * We could do that in one SPI transaction, but meh. - */ - max310x_batch_write(port, xmit->buf + xmit->tail, until_end); - max310x_batch_write(port, xmit->buf, to_send - until_end); - } else { - max310x_batch_write(port, xmit->buf + xmit->tail, to_send); - } + to_send = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen); + max310x_batch_write(port, tail, to_send); uart_xmit_advance(port, to_send); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index 8048fa542fc4..4bff422bb1bc 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -293,17 +293,14 @@ static void men_z135_handle_rx(struct men_z135_port *uart) static void men_z135_handle_tx(struct men_z135_port *uart) { struct uart_port *port = &uart->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char *tail; + unsigned int n, txfree; u32 txc; u32 wptr; int qlen; - int n; - int txfree; - int head; - int tail; - int s; - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) goto out; if (uart_tx_stopped(port)) @@ -313,7 +310,7 @@ static void men_z135_handle_tx(struct men_z135_port *uart) goto out; /* calculate bytes to copy */ - qlen = uart_circ_chars_pending(xmit); + qlen = kfifo_len(&tport->xmit_fifo); if (qlen <= 0) goto out; @@ -345,21 +342,18 @@ static void men_z135_handle_tx(struct men_z135_port *uart) if (n <= 0) goto irq_en; - head = xmit->head & (UART_XMIT_SIZE - 1); - tail = xmit->tail & (UART_XMIT_SIZE - 1); + n = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, + min_t(unsigned int, UART_XMIT_SIZE, n)); + memcpy_toio(port->membase + MEN_Z135_TX_RAM, tail, n); - s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail; - n = min(n, s); - - memcpy_toio(port->membase + MEN_Z135_TX_RAM, &xmit->buf[xmit->tail], n); iowrite32(n & 0x3ff, port->membase + MEN_Z135_TX_CTRL); uart_xmit_advance(port, n); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); irq_en: - if (!uart_circ_empty(xmit)) + if (!kfifo_is_empty(&tport->xmit_fifo)) men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); else men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 6feac459c0cf..4587ed4d4d5d 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -141,8 +141,8 @@ static void meson_uart_shutdown(struct uart_port *port) static void meson_uart_start_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; - unsigned int ch; + struct tty_port *tport = &port->state->port; + unsigned char ch; u32 val; if (uart_tx_stopped(port)) { @@ -158,21 +158,20 @@ static void meson_uart_start_tx(struct uart_port *port) continue; } - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(port, &ch)) break; - ch = xmit->buf[xmit->tail]; writel(ch, port->membase + AML_UART_WFIFO); uart_xmit_advance(port, 1); } - if (!uart_circ_empty(xmit)) { + if (!kfifo_is_empty(&tport->xmit_fifo)) { val = readl(port->membase + AML_UART_CONTROL); val |= AML_UART_TX_INT_EN; writel(val, port->membase + AML_UART_CONTROL); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c index da4c6f7e2a30..fb082ee73d5b 100644 --- a/drivers/tty/serial/milbeaut_usio.c +++ b/drivers/tty/serial/milbeaut_usio.c @@ -72,7 +72,7 @@ static void mlb_usio_stop_tx(struct uart_port *port) static void mlb_usio_tx_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; int count; writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE, @@ -87,7 +87,7 @@ static void mlb_usio_tx_chars(struct uart_port *port) port->x_char = 0; return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { mlb_usio_stop_tx(port); return; } @@ -96,12 +96,13 @@ static void mlb_usio_tx_chars(struct uart_port *port) (readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff); do { - writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR); + unsigned char ch; - uart_xmit_advance(port, 1); - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(port, &ch)) break; + writew(ch, port->membase + MLB_USIO_REG_DR); + port->icount.tx++; } while (--count > 0); writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ, @@ -110,10 +111,10 @@ static void mlb_usio_tx_chars(struct uart_port *port) writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE, port->membase + MLB_USIO_REG_SCR); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) mlb_usio_stop_tx(port); } diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 7bf30e632313..ae7a8e3cf467 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -452,7 +452,7 @@ static void msm_complete_tx_dma(void *args) { struct msm_port *msm_port = args; struct uart_port *port = &msm_port->uart; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct msm_dma *dma = &msm_port->tx_dma; struct dma_tx_state state; unsigned long flags; @@ -486,7 +486,7 @@ static void msm_complete_tx_dma(void *args) msm_port->imr |= MSM_UART_IMR_TXLEV; msm_write(port, msm_port->imr, MSM_UART_IMR); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); msm_handle_tx(port); @@ -496,14 +496,14 @@ static void msm_complete_tx_dma(void *args) static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) { - struct circ_buf *xmit = &msm_port->uart.state->xmit; struct uart_port *port = &msm_port->uart; + struct tty_port *tport = &port->state->port; struct msm_dma *dma = &msm_port->tx_dma; int ret; u32 val; sg_init_table(&dma->tx_sg, 1); - sg_set_buf(&dma->tx_sg, &xmit->buf[xmit->tail], count); + kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1, count); ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir); if (ret) @@ -843,8 +843,8 @@ static void msm_handle_rx(struct uart_port *port) static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count) { - struct circ_buf *xmit = &port->state->xmit; struct msm_port *msm_port = to_msm_port(port); + struct tty_port *tport = &port->state->port; unsigned int num_chars; unsigned int tf_pointer = 0; void __iomem *tf; @@ -858,8 +858,7 @@ static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count) msm_reset_dm_count(port, tx_count); while (tf_pointer < tx_count) { - int i; - char buf[4] = { 0 }; + unsigned char buf[4] = { 0 }; if (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY)) break; @@ -870,26 +869,23 @@ static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count) else num_chars = 1; - for (i = 0; i < num_chars; i++) - buf[i] = xmit->buf[xmit->tail + i]; - + num_chars = uart_fifo_out(port, buf, num_chars); iowrite32_rep(tf, buf, 1); - uart_xmit_advance(port, num_chars); tf_pointer += num_chars; } /* disable tx interrupts if nothing more to send */ - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) msm_stop_tx(port); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } static void msm_handle_tx(struct uart_port *port) { struct msm_port *msm_port = to_msm_port(port); - struct circ_buf *xmit = &msm_port->uart.state->xmit; + struct tty_port *tport = &port->state->port; struct msm_dma *dma = &msm_port->tx_dma; unsigned int pio_count, dma_count, dma_min; char buf[4] = { 0 }; @@ -913,13 +909,13 @@ static void msm_handle_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { msm_stop_tx(port); return; } - pio_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + dma_count = pio_count = kfifo_out_linear(&tport->xmit_fifo, NULL, + UART_XMIT_SIZE); dma_min = 1; /* Always DMA */ if (msm_port->is_uartdm > UARTDM_1P3) { diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 0255646bc175..5de57b77abdb 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -219,12 +219,10 @@ static void mvebu_uart_stop_tx(struct uart_port *port) static void mvebu_uart_start_tx(struct uart_port *port) { unsigned int ctl; - struct circ_buf *xmit = &port->state->xmit; + unsigned char c; - if (IS_EXTENDED(port) && !uart_circ_empty(xmit)) { - writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port)); - uart_xmit_advance(port, 1); - } + if (IS_EXTENDED(port) && uart_fifo_get(port, &c)) + writel(c, port->membase + UART_TSH(port)); ctl = readl(port->membase + UART_INTR(port)); ctl |= CTRL_TX_RDY_INT(port); diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 4749331fe618..144b35d31497 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -517,7 +517,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s); static void dma_tx_callback(void *param) { struct mxs_auart_port *s = param; - struct circ_buf *xmit = &s->port.state->xmit; + struct tty_port *tport = &s->port.state->port; dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE); @@ -526,7 +526,7 @@ static void dma_tx_callback(void *param) smp_mb__after_atomic(); /* wake up the possible processes. */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&s->port); mxs_auart_tx_chars(s); @@ -568,33 +568,22 @@ static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size) static void mxs_auart_tx_chars(struct mxs_auart_port *s) { - struct circ_buf *xmit = &s->port.state->xmit; + struct tty_port *tport = &s->port.state->port; bool pending; u8 ch; if (auart_dma_enabled(s)) { u32 i = 0; - int size; void *buffer = s->tx_dma_buf; if (test_and_set_bit(MXS_AUART_DMA_TX_SYNC, &s->flags)) return; - while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { - size = min_t(u32, UART_XMIT_SIZE - i, - CIRC_CNT_TO_END(xmit->head, - xmit->tail, - UART_XMIT_SIZE)); - memcpy(buffer + i, xmit->buf + xmit->tail, size); - xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1); - - i += size; - if (i >= UART_XMIT_SIZE) - break; - } - if (uart_tx_stopped(&s->port)) mxs_auart_stop_tx(&s->port); + else + i = kfifo_out(&tport->xmit_fifo, buffer, + UART_XMIT_SIZE); if (i) { mxs_auart_dma_tx(s, i); diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 89257cddf540..c7cee5fee603 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -808,7 +808,7 @@ static int dma_handle_rx(struct eg20t_port *priv) static unsigned int handle_tx(struct eg20t_port *priv) { struct uart_port *port = &priv->port; - struct circ_buf *xmit = &port->state->xmit; + unsigned char ch; int fifo_size; int tx_empty; @@ -830,9 +830,9 @@ static unsigned int handle_tx(struct eg20t_port *priv) fifo_size--; } - while (!uart_tx_stopped(port) && !uart_circ_empty(xmit) && fifo_size) { - iowrite8(xmit->buf[xmit->tail], priv->membase + PCH_UART_THR); - uart_xmit_advance(port, 1); + while (!uart_tx_stopped(port) && fifo_size && + uart_fifo_get(port, &ch)) { + iowrite8(ch, priv->membase + PCH_UART_THR); fifo_size--; tx_empty = 0; } @@ -850,14 +850,14 @@ static unsigned int handle_tx(struct eg20t_port *priv) static unsigned int dma_handle_tx(struct eg20t_port *priv) { struct uart_port *port = &priv->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct scatterlist *sg; int nent; int fifo_size; struct dma_async_tx_descriptor *desc; + unsigned int bytes, tail; int num; int i; - int bytes; int size; int rem; @@ -886,7 +886,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) fifo_size--; } - bytes = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + bytes = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); if (!bytes) { dev_dbg(priv->port.dev, "%s 0 bytes return\n", __func__); pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); @@ -920,10 +920,10 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) for (i = 0; i < num; i++, sg++) { if (i == (num - 1)) - sg_set_page(sg, virt_to_page(xmit->buf), + sg_set_page(sg, virt_to_page(tport->xmit_buf), rem, fifo_size * i); else - sg_set_page(sg, virt_to_page(xmit->buf), + sg_set_page(sg, virt_to_page(tport->xmit_buf), size, fifo_size * i); } @@ -937,8 +937,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) priv->nent = nent; for (i = 0; i < nent; i++, sg++) { - sg->offset = (xmit->tail & (UART_XMIT_SIZE - 1)) + - fifo_size * i; + sg->offset = tail + fifo_size * i; sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + sg->offset; if (i == (nent - 1)) diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index bbb46e6e98a2..f5af336a869b 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -342,7 +342,7 @@ static void pic32_uart_do_rx(struct uart_port *port) static void pic32_uart_do_tx(struct uart_port *port) { struct pic32_sport *sport = to_pic32_sport(port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned int max_count = PIC32_UART_TX_FIFO_DEPTH; if (port->x_char) { @@ -357,7 +357,7 @@ static void pic32_uart_do_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) goto txq_empty; /* keep stuffing chars into uart tx buffer @@ -371,21 +371,20 @@ static void pic32_uart_do_tx(struct uart_port *port) */ while (!(PIC32_UART_STA_UTXBF & pic32_uart_readl(sport, PIC32_UART_STA))) { - unsigned int c = xmit->buf[xmit->tail]; + unsigned char c; + if (!uart_fifo_get(port, &c)) + break; pic32_uart_writel(sport, PIC32_UART_TX, c); - uart_xmit_advance(port, 1); - if (uart_circ_empty(xmit)) - break; if (--max_count == 0) break; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) goto txq_empty; return; diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 05d97e89511e..63bc726273fd 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -347,7 +347,8 @@ static void pmz_status_handle(struct uart_pmac_port *uap) static void pmz_transmit_chars(struct uart_pmac_port *uap) { - struct circ_buf *xmit; + struct tty_port *tport; + unsigned char ch; if (ZS_IS_CONS(uap)) { unsigned char status = read_zsreg(uap, R0); @@ -398,8 +399,8 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap) if (uap->port.state == NULL) goto ack_tx_int; - xmit = &uap->port.state->xmit; - if (uart_circ_empty(xmit)) { + tport = &uap->port.state->port; + if (kfifo_is_empty(&tport->xmit_fifo)) { uart_write_wakeup(&uap->port); goto ack_tx_int; } @@ -407,12 +408,11 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap) goto ack_tx_int; uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; - write_zsdata(uap, xmit->buf[xmit->tail]); + WARN_ON(!uart_fifo_get(&uap->port, &ch)); + write_zsdata(uap, ch); zssync(uap); - uart_xmit_advance(&uap->port, 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); return; @@ -620,15 +620,15 @@ static void pmz_start_tx(struct uart_port *port) port->icount.tx++; port->x_char = 0; } else { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char ch; - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(&uap->port, &ch)) return; - write_zsdata(uap, xmit->buf[xmit->tail]); + write_zsdata(uap, ch); zssync(uap); - uart_xmit_advance(port, 1); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); } } diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index f9f7ac1a10df..7814982f1921 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -505,7 +505,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, */ qcom_geni_serial_poll_tx_done(uport); - if (!uart_circ_empty(&uport->state->xmit)) { + if (!kfifo_is_empty(&uport->state->port.xmit_fifo)) { irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); writel(irq_en | M_TX_FIFO_WATERMARK_EN, uport->membase + SE_GENI_M_IRQ_EN); @@ -620,22 +620,24 @@ static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport) static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); - struct circ_buf *xmit = &uport->state->xmit; + struct tty_port *tport = &uport->state->port; unsigned int xmit_size; + u8 *tail; int ret; if (port->tx_dma_addr) return; - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) return; - xmit_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); qcom_geni_serial_setup_tx(uport, xmit_size); - ret = geni_se_tx_dma_prep(&port->se, &xmit->buf[xmit->tail], - xmit_size, &port->tx_dma_addr); + ret = geni_se_tx_dma_prep(&port->se, tail, xmit_size, + &port->tx_dma_addr); if (ret) { dev_err(uport->dev, "unable to start TX SE DMA: %d\n", ret); qcom_geni_serial_stop_tx_dma(uport); @@ -853,18 +855,16 @@ static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport, unsigned int chunk) { struct qcom_geni_serial_port *port = to_dev_port(uport); - struct circ_buf *xmit = &uport->state->xmit; - unsigned int tx_bytes, c, remaining = chunk; + struct tty_port *tport = &uport->state->port; + unsigned int tx_bytes, remaining = chunk; u8 buf[BYTES_PER_FIFO_WORD]; while (remaining) { memset(buf, 0, sizeof(buf)); tx_bytes = min(remaining, BYTES_PER_FIFO_WORD); - for (c = 0; c < tx_bytes ; c++) { - buf[c] = xmit->buf[xmit->tail]; - uart_xmit_advance(uport, 1); - } + tx_bytes = kfifo_out(&tport->xmit_fifo, buf, tx_bytes); + uart_xmit_advance(uport, tx_bytes); iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1); @@ -877,7 +877,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport, bool done, bool active) { struct qcom_geni_serial_port *port = to_dev_port(uport); - struct circ_buf *xmit = &uport->state->xmit; + struct tty_port *tport = &uport->state->port; size_t avail; size_t pending; u32 status; @@ -890,7 +890,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport, if (active) pending = port->tx_remaining; else - pending = uart_circ_chars_pending(xmit); + pending = kfifo_len(&tport->xmit_fifo); /* All data has been transmitted and acknowledged as received */ if (!pending && !status && done) { @@ -933,24 +933,24 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport, uport->membase + SE_GENI_M_IRQ_EN); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(uport); } static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); - struct circ_buf *xmit = &uport->state->xmit; + struct tty_port *tport = &uport->state->port; uart_xmit_advance(uport, port->tx_remaining); geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining); port->tx_dma_addr = 0; port->tx_remaining = 0; - if (!uart_circ_empty(xmit)) + if (!kfifo_is_empty(&tport->xmit_fifo)) qcom_geni_serial_start_tx_dma(uport); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(uport); } diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index 82def9b8632a..663e35e424bd 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -330,8 +330,8 @@ static void rda_uart_set_termios(struct uart_port *port, static void rda_uart_send_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; - unsigned int ch; + struct tty_port *tport = &port->state->port; + unsigned char ch; u32 val; if (uart_tx_stopped(port)) @@ -347,19 +347,14 @@ static void rda_uart_send_chars(struct uart_port *port) port->x_char = 0; } - while (rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK) { - if (uart_circ_empty(xmit)) - break; - - ch = xmit->buf[xmit->tail]; + while ((rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK) && + uart_fifo_get(port, &ch)) rda_uart_write(port, ch, RDA_UART_RXTX_BUFFER); - uart_xmit_advance(port, 1); - } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (!uart_circ_empty(xmit)) { + if (!kfifo_is_empty(&tport->xmit_fifo)) { /* Re-enable Tx FIFO interrupt */ val = rda_uart_read(port, RDA_UART_IRQ_MASK); val |= RDA_UART_TX_DATA_NEEDED; diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index a2d07e05c502..dc35eb77d2ef 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -329,7 +329,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args) { struct s3c24xx_uart_port *ourport = args; struct uart_port *port = &ourport->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct s3c24xx_uart_dma *dma = ourport->dma; struct dma_tx_state state; unsigned long flags; @@ -348,7 +348,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args) uart_xmit_advance(port, count); ourport->tx_in_progress = 0; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); s3c24xx_serial_start_next_tx(ourport); @@ -431,17 +431,15 @@ static void s3c24xx_serial_start_tx_pio(struct s3c24xx_uart_port *ourport) } static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport, - unsigned int count) + unsigned int count, unsigned int tail) { - struct uart_port *port = &ourport->port; - struct circ_buf *xmit = &port->state->xmit; struct s3c24xx_uart_dma *dma = ourport->dma; if (ourport->tx_mode != S3C24XX_TX_DMA) enable_tx_dma(ourport); dma->tx_size = count & ~(dma_get_cache_alignment() - 1); - dma->tx_transfer_addr = dma->tx_addr + xmit->tail; + dma->tx_transfer_addr = dma->tx_addr + tail; dma_sync_single_for_device(dma->tx_chan->device->dev, dma->tx_transfer_addr, dma->tx_size, @@ -468,11 +466,11 @@ static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport, static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport) { struct uart_port *port = &ourport->port; - struct circ_buf *xmit = &port->state->xmit; - unsigned long count; + struct tty_port *tport = &port->state->port; + unsigned int count, tail; /* Get data size up to the end of buffer */ - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + count = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); if (!count) { s3c24xx_serial_stop_tx(port); @@ -481,16 +479,16 @@ static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport) if (!ourport->dma || !ourport->dma->tx_chan || count < ourport->min_dma_size || - xmit->tail & (dma_get_cache_alignment() - 1)) + tail & (dma_get_cache_alignment() - 1)) s3c24xx_serial_start_tx_pio(ourport); else - s3c24xx_serial_start_tx_dma(ourport, count); + s3c24xx_serial_start_tx_dma(ourport, count, tail); } static void s3c24xx_serial_start_tx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; if (!ourport->tx_enabled) { if (port->flags & UPF_CONS_FLOW) @@ -502,7 +500,8 @@ static void s3c24xx_serial_start_tx(struct uart_port *port) } if (ourport->dma && ourport->dma->tx_chan) { - if (!uart_circ_empty(xmit) && !ourport->tx_in_progress) + if (!kfifo_is_empty(&tport->xmit_fifo) && + !ourport->tx_in_progress) s3c24xx_serial_start_next_tx(ourport); } } @@ -868,18 +867,19 @@ static irqreturn_t s3c24xx_serial_rx_irq(int irq, void *dev_id) static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport) { struct uart_port *port = &ourport->port; - struct circ_buf *xmit = &port->state->xmit; - int count, dma_count = 0; + struct tty_port *tport = &port->state->port; + unsigned int count, dma_count = 0, tail; - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + count = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); if (ourport->dma && ourport->dma->tx_chan && count >= ourport->min_dma_size) { int align = dma_get_cache_alignment() - - (xmit->tail & (dma_get_cache_alignment() - 1)); + (tail & (dma_get_cache_alignment() - 1)); if (count - align >= ourport->min_dma_size) { dma_count = count - align; count = align; + tail += align; } } @@ -894,7 +894,7 @@ static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport) * stopped, disable the uart and exit */ - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { s3c24xx_serial_stop_tx(port); return; } @@ -906,24 +906,25 @@ static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport) dma_count = 0; } - while (!uart_circ_empty(xmit) && count > 0) { - if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) + while (!(rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)) { + unsigned char ch; + + if (!uart_fifo_get(port, &ch)) break; - wr_reg(port, S3C2410_UTXH, xmit->buf[xmit->tail]); - uart_xmit_advance(port, 1); + wr_reg(port, S3C2410_UTXH, ch); count--; } if (!count && dma_count) { - s3c24xx_serial_start_tx_dma(ourport, dma_count); + s3c24xx_serial_start_tx_dma(ourport, dma_count, tail); return; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) s3c24xx_serial_stop_tx(port); } @@ -1118,7 +1119,8 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) /* TX buffer */ dma->tx_addr = dma_map_single(dma->tx_chan->device->dev, - p->port.state->xmit.buf, UART_XMIT_SIZE, + p->port.state->port.xmit_buf, + UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(dma->tx_chan->device->dev, dma->tx_addr)) { reason = "DMA mapping error for TX buffer"; diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c index dbec29d9a6c3..b4e1b90e5960 100644 --- a/drivers/tty/serial/sb1250-duart.c +++ b/drivers/tty/serial/sb1250-duart.c @@ -382,7 +382,8 @@ static void sbd_receive_chars(struct sbd_port *sport) static void sbd_transmit_chars(struct sbd_port *sport) { struct uart_port *uport = &sport->port; - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; + unsigned char ch; unsigned int mask; int stop_tx; @@ -395,19 +396,19 @@ static void sbd_transmit_chars(struct sbd_port *sport) } /* If nothing to do or stopped or hardware stopped. */ - stop_tx = (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)); + stop_tx = uart_tx_stopped(&sport->port) || + !uart_fifo_get(&sport->port, &ch); /* Send char. */ if (!stop_tx) { - write_sbdchn(sport, R_DUART_TX_HOLD, xmit->buf[xmit->tail]); - uart_xmit_advance(&sport->port, 1); + write_sbdchn(sport, R_DUART_TX_HOLD, ch); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); } /* Are we are done? */ - if (stop_tx || uart_circ_empty(xmit)) { + if (stop_tx || kfifo_is_empty(&tport->xmit_fifo)) { /* Disable tx interrupts. */ mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2)); mask &= ~M_DUART_IMR_TX; diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 929206a9a6e1..c6983b7bd78c 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -676,9 +676,9 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, static void sc16is7xx_handle_tx(struct uart_port *port) { struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - struct circ_buf *xmit = &port->state->xmit; - unsigned int txlen, to_send, i; + struct tty_port *tport = &port->state->port; unsigned long flags; + unsigned int txlen; if (unlikely(port->x_char)) { sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char); @@ -687,40 +687,30 @@ static void sc16is7xx_handle_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { uart_port_lock_irqsave(port, &flags); sc16is7xx_stop_tx(port); uart_port_unlock_irqrestore(port, flags); return; } - /* Get length of data pending in circular buffer */ - to_send = uart_circ_chars_pending(xmit); - if (likely(to_send)) { - /* Limit to space available in TX FIFO */ - txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); - if (txlen > SC16IS7XX_FIFO_SIZE) { - dev_err_ratelimited(port->dev, - "chip reports %d free bytes in TX fifo, but it only has %d", - txlen, SC16IS7XX_FIFO_SIZE); - txlen = 0; - } - to_send = (to_send > txlen) ? txlen : to_send; - - /* Convert to linear buffer */ - for (i = 0; i < to_send; ++i) { - s->buf[i] = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); - } - - sc16is7xx_fifo_write(port, s->buf, to_send); + /* Limit to space available in TX FIFO */ + txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + if (txlen > SC16IS7XX_FIFO_SIZE) { + dev_err_ratelimited(port->dev, + "chip reports %d free bytes in TX fifo, but it only has %d", + txlen, SC16IS7XX_FIFO_SIZE); + txlen = 0; } + txlen = uart_fifo_out(port, s->buf, txlen); + sc16is7xx_fifo_write(port, s->buf, txlen); + uart_port_lock_irqsave(port, &flags); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) sc16is7xx_stop_tx(port); else sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT); diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index f24217a560d7..6d1d142fd216 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -439,7 +439,7 @@ static void sccnxp_handle_rx(struct uart_port *port) static void sccnxp_handle_tx(struct uart_port *port) { u8 sr; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct sccnxp_port *s = dev_get_drvdata(port->dev); if (unlikely(port->x_char)) { @@ -449,7 +449,7 @@ static void sccnxp_handle_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { /* Disable TX if FIFO is empty */ if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXEMT) { sccnxp_disable_irq(port, IMR_TXRDY); @@ -461,16 +461,20 @@ static void sccnxp_handle_tx(struct uart_port *port) return; } - while (!uart_circ_empty(xmit)) { + while (1) { + unsigned char ch; + sr = sccnxp_port_read(port, SCCNXP_SR_REG); if (!(sr & SR_TXRDY)) break; - sccnxp_port_write(port, SCCNXP_THR_REG, xmit->buf[xmit->tail]); - uart_xmit_advance(port, 1); + if (!uart_fifo_get(port, &ch)) + break; + + sccnxp_port_write(port, SCCNXP_THR_REG, ch); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 525f3a2f7bd4..1183ca54ab92 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -484,18 +484,18 @@ static void tegra_uart_release_port(struct uart_port *u) static void tegra_uart_fill_tx_fifo(struct tegra_uart_port *tup, int max_bytes) { - struct circ_buf *xmit = &tup->uport.state->xmit; + unsigned char ch; int i; for (i = 0; i < max_bytes; i++) { - BUG_ON(uart_circ_empty(xmit)); if (tup->cdata->tx_fifo_full_status) { unsigned long lsr = tegra_uart_read(tup, UART_LSR); if ((lsr & TEGRA_UART_LSR_TXFIFO_FULL)) break; } - tegra_uart_write(tup, xmit->buf[xmit->tail], UART_TX); - uart_xmit_advance(&tup->uport, 1); + if (WARN_ON_ONCE(!uart_fifo_get(&tup->uport, &ch))) + break; + tegra_uart_write(tup, ch, UART_TX); } } @@ -514,7 +514,7 @@ static void tegra_uart_start_pio_tx(struct tegra_uart_port *tup, static void tegra_uart_tx_dma_complete(void *args) { struct tegra_uart_port *tup = args; - struct circ_buf *xmit = &tup->uport.state->xmit; + struct tty_port *tport = &tup->uport.state->port; struct dma_tx_state state; unsigned long flags; unsigned int count; @@ -525,7 +525,7 @@ static void tegra_uart_tx_dma_complete(void *args) uart_port_lock_irqsave(&tup->uport, &flags); uart_xmit_advance(&tup->uport, count); tup->tx_in_progress = 0; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&tup->uport); tegra_uart_start_next_tx(tup); uart_port_unlock_irqrestore(&tup->uport, flags); @@ -534,11 +534,14 @@ static void tegra_uart_tx_dma_complete(void *args) static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup, unsigned long count) { - struct circ_buf *xmit = &tup->uport.state->xmit; + struct tty_port *tport = &tup->uport.state->port; dma_addr_t tx_phys_addr; + unsigned int tail; tup->tx_bytes = count & ~(0xF); - tx_phys_addr = tup->tx_dma_buf_phys + xmit->tail; + WARN_ON_ONCE(kfifo_out_linear(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE) < count); + tx_phys_addr = tup->tx_dma_buf_phys + tail; dma_sync_single_for_device(tup->uport.dev, tx_phys_addr, tup->tx_bytes, DMA_TO_DEVICE); @@ -562,18 +565,21 @@ static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup, static void tegra_uart_start_next_tx(struct tegra_uart_port *tup) { + struct tty_port *tport = &tup->uport.state->port; + unsigned char *tail_ptr; unsigned long tail; - unsigned long count; - struct circ_buf *xmit = &tup->uport.state->xmit; + unsigned int count; if (!tup->current_baud) return; - tail = (unsigned long)&xmit->buf[xmit->tail]; - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail_ptr, + UART_XMIT_SIZE); if (!count) return; + tail = (unsigned long)tail_ptr; + if (tup->use_tx_pio || count < TEGRA_UART_MIN_DMA) tegra_uart_start_pio_tx(tup, count); else if (BYTES_TO_ALIGN(tail) > 0) @@ -586,9 +592,9 @@ static void tegra_uart_start_next_tx(struct tegra_uart_port *tup) static void tegra_uart_start_tx(struct uart_port *u) { struct tegra_uart_port *tup = to_tegra_uport(u); - struct circ_buf *xmit = &u->state->xmit; + struct tty_port *tport = &u->state->port; - if (!uart_circ_empty(xmit) && !tup->tx_in_progress) + if (!kfifo_is_empty(&tport->xmit_fifo) && !tup->tx_in_progress) tegra_uart_start_next_tx(tup); } @@ -628,11 +634,11 @@ static void tegra_uart_stop_tx(struct uart_port *u) static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup) { - struct circ_buf *xmit = &tup->uport.state->xmit; + struct tty_port *tport = &tup->uport.state->port; tegra_uart_fill_tx_fifo(tup, tup->tx_bytes); tup->tx_in_progress = 0; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&tup->uport); tegra_uart_start_next_tx(tup); } @@ -1169,15 +1175,14 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, tup->rx_dma_buf_virt = dma_buf; tup->rx_dma_buf_phys = dma_phys; } else { + dma_buf = tup->uport.state->port.xmit_buf; dma_phys = dma_map_single(tup->uport.dev, - tup->uport.state->xmit.buf, UART_XMIT_SIZE, - DMA_TO_DEVICE); + dma_buf, UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(tup->uport.dev, dma_phys)) { dev_err(tup->uport.dev, "dma_map_single tx failed\n"); dma_release_channel(dma_chan); return -ENOMEM; } - dma_buf = tup->uport.state->xmit.buf; dma_sconfig.dst_addr = tup->uport.mapbase; dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma_sconfig.dst_maxburst = 16; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index ff85ebd3a007..3c0931fba1c6 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -272,9 +272,10 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, return -ENOMEM; uart_port_lock(state, flags); - if (!state->xmit.buf) { - state->xmit.buf = (unsigned char *) page; - uart_circ_clear(&state->xmit); + if (!state->port.xmit_buf) { + state->port.xmit_buf = (unsigned char *)page; + kfifo_init(&state->port.xmit_fifo, state->port.xmit_buf, + PAGE_SIZE); uart_port_unlock(uport, flags); } else { uart_port_unlock(uport, flags); @@ -387,8 +388,9 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) * can endup in printk() recursion. */ uart_port_lock(state, flags); - xmit_buf = state->xmit.buf; - state->xmit.buf = NULL; + xmit_buf = port->xmit_buf; + port->xmit_buf = NULL; + INIT_KFIFO(port->xmit_fifo); uart_port_unlock(uport, flags); free_page((unsigned long)xmit_buf); @@ -552,22 +554,17 @@ static int uart_put_char(struct tty_struct *tty, u8 c) { struct uart_state *state = tty->driver_data; struct uart_port *port; - struct circ_buf *circ; unsigned long flags; int ret = 0; - circ = &state->xmit; port = uart_port_lock(state, flags); - if (!circ->buf) { + if (WARN_ON_ONCE(!state->port.xmit_buf)) { uart_port_unlock(port, flags); return 0; } - if (port && uart_circ_chars_free(circ) != 0) { - circ->buf[circ->head] = c; - circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); - ret = 1; - } + if (port) + ret = kfifo_put(&state->port.xmit_fifo, c); uart_port_unlock(port, flags); return ret; } @@ -581,9 +578,8 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 *buf, size_t count) { struct uart_state *state = tty->driver_data; struct uart_port *port; - struct circ_buf *circ; unsigned long flags; - int c, ret = 0; + int ret = 0; /* * This means you called this function _after_ the port was @@ -593,24 +589,13 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 *buf, size_t count) return -EL3HLT; port = uart_port_lock(state, flags); - circ = &state->xmit; - if (!circ->buf) { + if (WARN_ON_ONCE(!state->port.xmit_buf)) { uart_port_unlock(port, flags); return 0; } - while (port) { - c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(circ->buf + circ->head, buf, c); - circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); - buf += c; - count -= c; - ret += c; - } + if (port) + ret = kfifo_in(&state->port.xmit_fifo, buf, count); __uart_start(state); uart_port_unlock(port, flags); @@ -625,7 +610,7 @@ static unsigned int uart_write_room(struct tty_struct *tty) unsigned int ret; port = uart_port_lock(state, flags); - ret = uart_circ_chars_free(&state->xmit); + ret = kfifo_avail(&state->port.xmit_fifo); uart_port_unlock(port, flags); return ret; } @@ -638,7 +623,7 @@ static unsigned int uart_chars_in_buffer(struct tty_struct *tty) unsigned int ret; port = uart_port_lock(state, flags); - ret = uart_circ_chars_pending(&state->xmit); + ret = kfifo_len(&state->port.xmit_fifo); uart_port_unlock(port, flags); return ret; } @@ -661,7 +646,7 @@ static void uart_flush_buffer(struct tty_struct *tty) port = uart_port_lock(state, flags); if (!port) return; - uart_circ_clear(&state->xmit); + kfifo_reset(&state->port.xmit_fifo); if (port->ops->flush_buffer) port->ops->flush_buffer(port); uart_port_unlock(port, flags); @@ -1064,7 +1049,7 @@ static int uart_get_lsr_info(struct tty_struct *tty, * interrupt happens). */ if (uport->x_char || - ((uart_circ_chars_pending(&state->xmit) > 0) && + (!kfifo_is_empty(&state->port.xmit_fifo) && !uart_tx_stopped(uport))) result &= ~TIOCSER_TEMT; @@ -1788,8 +1773,9 @@ static void uart_tty_port_shutdown(struct tty_port *port) * Free the transmit buffer. */ uart_port_lock_irq(uport); - buf = state->xmit.buf; - state->xmit.buf = NULL; + buf = port->xmit_buf; + port->xmit_buf = NULL; + INIT_KFIFO(port->xmit_fifo); uart_port_unlock_irq(uport); free_page((unsigned long)buf); diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c index 22b9eeb23e68..3408c8827561 100644 --- a/drivers/tty/serial/serial_port.c +++ b/drivers/tty/serial/serial_port.c @@ -23,7 +23,7 @@ static int __serial_port_busy(struct uart_port *port) { return !uart_tx_stopped(port) && - uart_circ_chars_pending(&port->state->xmit); + !kfifo_is_empty(&port->state->port.xmit_fifo); } static int serial_port_runtime_resume(struct device *dev) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index e512eaa57ed5..97031db26ae4 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -585,7 +585,7 @@ static void sci_start_tx(struct uart_port *port) sci_serial_out(port, SCSCR, new); } - if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + if (s->chan_tx && !kfifo_is_empty(&port->state->port.xmit_fifo) && dma_submit_error(s->cookie_tx)) { if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) /* Switch irq from SCIF to DMA */ @@ -817,7 +817,7 @@ static int sci_rxfill(struct uart_port *port) static void sci_transmit_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned int stopped = uart_tx_stopped(port); unsigned short status; unsigned short ctrl; @@ -826,7 +826,7 @@ static void sci_transmit_chars(struct uart_port *port) status = sci_serial_in(port, SCxSR); if (!(status & SCxSR_TDxE(port))) { ctrl = sci_serial_in(port, SCSCR); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) ctrl &= ~SCSCR_TIE; else ctrl |= SCSCR_TIE; @@ -842,15 +842,14 @@ static void sci_transmit_chars(struct uart_port *port) if (port->x_char) { c = port->x_char; port->x_char = 0; - } else if (!uart_circ_empty(xmit) && !stopped) { - c = xmit->buf[xmit->tail]; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - } else if (port->type == PORT_SCI && uart_circ_empty(xmit)) { - ctrl = sci_serial_in(port, SCSCR); - ctrl &= ~SCSCR_TE; - sci_serial_out(port, SCSCR, ctrl); - return; - } else { + } else if (stopped || !kfifo_get(&tport->xmit_fifo, &c)) { + if (port->type == PORT_SCI && + kfifo_is_empty(&tport->xmit_fifo)) { + ctrl = sci_serial_in(port, SCSCR); + ctrl &= ~SCSCR_TE; + sci_serial_out(port, SCSCR, ctrl); + return; + } break; } @@ -861,9 +860,9 @@ static void sci_transmit_chars(struct uart_port *port) sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port)); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { if (port->type == PORT_SCI) { ctrl = sci_serial_in(port, SCSCR); ctrl &= ~SCSCR_TIE; @@ -1199,7 +1198,7 @@ static void sci_dma_tx_complete(void *arg) { struct sci_port *s = arg; struct uart_port *port = &s->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned long flags; dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); @@ -1208,10 +1207,10 @@ static void sci_dma_tx_complete(void *arg) uart_xmit_advance(port, s->tx_dma_len); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (!uart_circ_empty(xmit)) { + if (!kfifo_is_empty(&tport->xmit_fifo)) { s->cookie_tx = 0; schedule_work(&s->work_tx); } else { @@ -1424,10 +1423,10 @@ static void sci_dma_tx_work_fn(struct work_struct *work) struct dma_async_tx_descriptor *desc; struct dma_chan *chan = s->chan_tx; struct uart_port *port = &s->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned long flags; + unsigned int tail; dma_addr_t buf; - int head, tail; /* * DMA is idle now. @@ -1437,10 +1436,9 @@ static void sci_dma_tx_work_fn(struct work_struct *work) * consistent xmit buffer state. */ uart_port_lock_irq(port); - head = xmit->head; - tail = xmit->tail; + s->tx_dma_len = kfifo_out_linear(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); buf = s->tx_dma_addr + tail; - s->tx_dma_len = CIRC_CNT_TO_END(head, tail, UART_XMIT_SIZE); if (!s->tx_dma_len) { /* Transmit buffer has been flushed */ uart_port_unlock_irq(port); @@ -1469,8 +1467,8 @@ static void sci_dma_tx_work_fn(struct work_struct *work) } uart_port_unlock_irq(port); - dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", - __func__, xmit->buf, tail, head, s->cookie_tx); + dev_dbg(port->dev, "%s: %p: %u, cookie %d\n", + __func__, tport->xmit_buf, tail, s->cookie_tx); dma_async_issue_pending(chan); return; @@ -1585,6 +1583,7 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port, static void sci_request_dma(struct uart_port *port) { struct sci_port *s = to_sci_port(port); + struct tty_port *tport = &port->state->port; struct dma_chan *chan; dev_dbg(port->dev, "%s: port %d\n", __func__, port->line); @@ -1613,7 +1612,7 @@ static void sci_request_dma(struct uart_port *port) if (chan) { /* UART circular tx buffer is an aligned page. */ s->tx_dma_addr = dma_map_single(chan->device->dev, - port->state->xmit.buf, + tport->xmit_buf, UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) { @@ -1622,7 +1621,7 @@ static void sci_request_dma(struct uart_port *port) } else { dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n", __func__, UART_XMIT_SIZE, - port->state->xmit.buf, &s->tx_dma_addr); + tport->xmit_buf, &s->tx_dma_addr); INIT_WORK(&s->work_tx, sci_dma_tx_work_fn); s->chan_tx_saved = s->chan_tx = chan; diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 15f14fa593da..3fc54cc02a1f 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -227,13 +227,13 @@ static int sprd_tx_buf_remap(struct uart_port *port) { struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port, port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char *tail; - sp->tx_dma.trans_len = - CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + sp->tx_dma.trans_len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); - sp->tx_dma.phys_addr = dma_map_single(port->dev, - (void *)&(xmit->buf[xmit->tail]), + sp->tx_dma.phys_addr = dma_map_single(port->dev, tail, sp->tx_dma.trans_len, DMA_TO_DEVICE); return dma_mapping_error(port->dev, sp->tx_dma.phys_addr); @@ -244,7 +244,7 @@ static void sprd_complete_tx_dma(void *data) struct uart_port *port = (struct uart_port *)data; struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port, port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned long flags; uart_port_lock_irqsave(port, &flags); @@ -253,10 +253,10 @@ static void sprd_complete_tx_dma(void *data) uart_xmit_advance(port, sp->tx_dma.trans_len); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit) || sprd_tx_buf_remap(port) || + if (kfifo_is_empty(&tport->xmit_fifo) || sprd_tx_buf_remap(port) || sprd_tx_dma_config(port)) sp->tx_dma.trans_len = 0; @@ -319,7 +319,7 @@ static void sprd_start_tx_dma(struct uart_port *port) { struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port, port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; if (port->x_char) { serial_out(port, SPRD_TXD, port->x_char); @@ -328,7 +328,7 @@ static void sprd_start_tx_dma(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { sprd_stop_tx_dma(port); return; } diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index a23e59551848..f91753a40a69 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -387,9 +387,9 @@ static unsigned int asc_get_mctrl(struct uart_port *port) /* There are probably characters waiting to be transmitted. */ static void asc_start_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; - if (!uart_circ_empty(xmit)) + if (!kfifo_is_empty(&tport->xmit_fifo)) asc_enable_tx_interrupts(port); } diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 58d169e5c1db..8c66abcfe6ca 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -696,18 +696,23 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + + while (1) { + unsigned char ch; - while (!uart_circ_empty(xmit)) { /* Check that TDR is empty before filling FIFO */ if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE)) break; - writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); - uart_xmit_advance(port, 1); + + if (!uart_fifo_get(port, &ch)) + break; + + writel_relaxed(ch, port->membase + ofs->tdr); } /* rely on TXE irq (mask or unmask) for sending remaining data */ - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) stm32_usart_tx_interrupt_disable(port); else stm32_usart_tx_interrupt_enable(port); @@ -716,7 +721,7 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port) static void stm32_usart_transmit_chars_dma(struct uart_port *port) { struct stm32_port *stm32port = to_stm32_port(port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct dma_async_tx_descriptor *desc = NULL; unsigned int count; int ret; @@ -728,25 +733,8 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port) return; } - count = uart_circ_chars_pending(xmit); - - if (count > TX_BUF_L) - count = TX_BUF_L; - - if (xmit->tail < xmit->head) { - memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], count); - } else { - size_t one = UART_XMIT_SIZE - xmit->tail; - size_t two; - - if (one > count) - one = count; - two = count - one; - - memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], one); - if (two) - memcpy(&stm32port->tx_buf[one], &xmit->buf[0], two); - } + count = kfifo_out_peek(&tport->xmit_fifo, &stm32port->tx_buf[0], + TX_BUF_L); desc = dmaengine_prep_slave_single(stm32port->tx_ch, stm32port->tx_dma_buf, @@ -792,14 +780,14 @@ static void stm32_usart_transmit_chars(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; u32 isr; int ret; if (!stm32_port->hw_flow_control && port->rs485.flags & SER_RS485_ENABLED && (port->x_char || - !(uart_circ_empty(xmit) || uart_tx_stopped(port)))) { + !(kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)))) { stm32_usart_tc_interrupt_disable(port); stm32_usart_rs485_rts_enable(port); } @@ -826,7 +814,7 @@ static void stm32_usart_transmit_chars(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { stm32_usart_tx_interrupt_disable(port); return; } @@ -841,10 +829,10 @@ static void stm32_usart_transmit_chars(struct uart_port *port) else stm32_usart_transmit_chars_pio(port); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { stm32_usart_tx_interrupt_disable(port); if (!stm32_port->hw_flow_control && port->rs485.flags & SER_RS485_ENABLED) { @@ -967,9 +955,9 @@ static void stm32_usart_stop_tx(struct uart_port *port) /* There are probably characters waiting to be transmitted. */ static void stm32_usart_start_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; - if (uart_circ_empty(xmit) && !port->x_char) { + if (kfifo_is_empty(&tport->xmit_fifo) && !port->x_char) { stm32_usart_rs485_rts_disable(port); return; } diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 8d612ab80680..7f60679fdde1 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -39,10 +39,13 @@ static char *con_read_page; static int hung_up = 0; -static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit) +static void transmit_chars_putchar(struct uart_port *port, + struct tty_port *tport) { - while (!uart_circ_empty(xmit)) { - long status = sun4v_con_putchar(xmit->buf[xmit->tail]); + unsigned char ch; + + while (kfifo_peek(&tport->xmit_fifo, &ch)) { + long status = sun4v_con_putchar(ch); if (status != HV_EOK) break; @@ -51,14 +54,16 @@ static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit } } -static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit) +static void transmit_chars_write(struct uart_port *port, struct tty_port *tport) { - while (!uart_circ_empty(xmit)) { - unsigned long ra = __pa(xmit->buf + xmit->tail); - unsigned long len, status, sent; + while (!kfifo_is_empty(&tport->xmit_fifo)) { + unsigned long len, ra, status, sent; + unsigned char *tail; + + len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); + ra = __pa(tail); - len = CIRC_CNT_TO_END(xmit->head, xmit->tail, - UART_XMIT_SIZE); status = sun4v_con_write(ra, len, &sent); if (status != HV_EOK) break; @@ -165,7 +170,7 @@ static int receive_chars_read(struct uart_port *port) } struct sunhv_ops { - void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit); + void (*transmit_chars)(struct uart_port *port, struct tty_port *tport); int (*receive_chars)(struct uart_port *port); }; @@ -196,18 +201,18 @@ static struct tty_port *receive_chars(struct uart_port *port) static void transmit_chars(struct uart_port *port) { - struct circ_buf *xmit; + struct tty_port *tport; if (!port->state) return; - xmit = &port->state->xmit; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + tport = &port->state->port; + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) return; - sunhv_ops->transmit_chars(port, xmit); + sunhv_ops->transmit_chars(port, tport); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c index f5e29eb4a4ce..abf7c449308d 100644 --- a/drivers/tty/serial/sunplus-uart.c +++ b/drivers/tty/serial/sunplus-uart.c @@ -200,7 +200,7 @@ static void sunplus_break_ctl(struct uart_port *port, int ctl) static void transmit_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; if (port->x_char) { sp_uart_put_char(port, port->x_char); @@ -209,22 +209,24 @@ static void transmit_chars(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { sunplus_stop_tx(port); return; } do { - sp_uart_put_char(port, xmit->buf[xmit->tail]); - uart_xmit_advance(port, 1); - if (uart_circ_empty(xmit)) + unsigned char ch; + + if (!uart_fifo_get(port, &ch)) break; + + sp_uart_put_char(port, ch); } while (sunplus_tx_buf_not_full(port)); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) sunplus_stop_tx(port); } diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index 1ea2f33a07a7..1acbe2fba746 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -232,7 +232,7 @@ static void sunsab_tx_idle(struct uart_sunsab_port *); static void transmit_chars(struct uart_sunsab_port *up, union sab82532_irq_status *stat) { - struct circ_buf *xmit = &up->port.state->xmit; + struct tty_port *tport = &up->port.state->port; int i; if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) { @@ -252,7 +252,7 @@ static void transmit_chars(struct uart_sunsab_port *up, set_bit(SAB82532_XPR, &up->irqflags); sunsab_tx_idle(up); - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&up->port)) { up->interrupt_mask1 |= SAB82532_IMR1_XPR; writeb(up->interrupt_mask1, &up->regs->w.imr1); return; @@ -265,21 +265,22 @@ static void transmit_chars(struct uart_sunsab_port *up, /* Stuff 32 bytes into Transmit FIFO. */ clear_bit(SAB82532_XPR, &up->irqflags); for (i = 0; i < up->port.fifosize; i++) { - writeb(xmit->buf[xmit->tail], - &up->regs->w.xfifo[i]); - uart_xmit_advance(&up->port, 1); - if (uart_circ_empty(xmit)) + unsigned char ch; + + if (!uart_fifo_get(&up->port, &ch)) break; + + writeb(ch, &up->regs->w.xfifo[i]); } /* Issue a Transmit Frame command. */ sunsab_cec_wait(up); writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) sunsab_stop_tx(&up->port); } @@ -435,10 +436,10 @@ static void sunsab_start_tx(struct uart_port *port) { struct uart_sunsab_port *up = container_of(port, struct uart_sunsab_port, port); - struct circ_buf *xmit = &up->port.state->xmit; + struct tty_port *tport = &up->port.state->port; int i; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) return; up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); @@ -451,11 +452,12 @@ static void sunsab_start_tx(struct uart_port *port) clear_bit(SAB82532_XPR, &up->irqflags); for (i = 0; i < up->port.fifosize; i++) { - writeb(xmit->buf[xmit->tail], - &up->regs->w.xfifo[i]); - uart_xmit_advance(&up->port, 1); - if (uart_circ_empty(xmit)) + unsigned char ch; + + if (!uart_fifo_get(&up->port, &ch)) break; + + writeb(ch, &up->regs->w.xfifo[i]); } /* Issue a Transmit Frame command. */ diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index c8b65f4b2710..67a5fc70bb4b 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -396,7 +396,8 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status) static void transmit_chars(struct uart_sunsu_port *up) { - struct circ_buf *xmit = &up->port.state->xmit; + struct tty_port *tport = &up->port.state->port; + unsigned char ch; int count; if (up->port.x_char) { @@ -409,23 +410,23 @@ static void transmit_chars(struct uart_sunsu_port *up) sunsu_stop_tx(&up->port); return; } - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { __stop_tx(up); return; } count = up->port.fifosize; do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - uart_xmit_advance(&up->port, 1); - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(&up->port, &ch)) break; + + serial_out(up, UART_TX, ch); } while (--count > 0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) __stop_tx(up); } diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index c99289c6c8f8..71758ad4241c 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -453,7 +453,8 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up, static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, struct zilog_channel __iomem *channel) { - struct circ_buf *xmit; + struct tty_port *tport; + unsigned char ch; if (ZS_IS_CONS(up)) { unsigned char status = readb(&channel->control); @@ -496,21 +497,20 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, if (up->port.state == NULL) goto ack_tx_int; - xmit = &up->port.state->xmit; - if (uart_circ_empty(xmit)) - goto ack_tx_int; + tport = &up->port.state->port; if (uart_tx_stopped(&up->port)) goto ack_tx_int; + if (!uart_fifo_get(&up->port, &ch)) + goto ack_tx_int; + up->flags |= SUNZILOG_FLAG_TX_ACTIVE; - writeb(xmit->buf[xmit->tail], &channel->data); + writeb(ch, &channel->data); ZSDELAY(); ZS_WSYNC(channel); - uart_xmit_advance(&up->port, 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); return; @@ -700,17 +700,16 @@ static void sunzilog_start_tx(struct uart_port *port) port->icount.tx++; port->x_char = 0; } else { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char ch; - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(&up->port, &ch)) return; - writeb(xmit->buf[xmit->tail], &channel->data); + writeb(ch, &channel->data); ZSDELAY(); ZS_WSYNC(channel); - uart_xmit_advance(port, 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); } } diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c index d9c78320eb02..21ca5fcadf49 100644 --- a/drivers/tty/serial/tegra-tcu.c +++ b/drivers/tty/serial/tegra-tcu.c @@ -91,15 +91,17 @@ static void tegra_tcu_write(struct tegra_tcu *tcu, const char *s, static void tegra_tcu_uart_start_tx(struct uart_port *port) { struct tegra_tcu *tcu = port->private_data; - struct circ_buf *xmit = &port->state->xmit; - unsigned long count; + struct tty_port *tport = &port->state->port; + unsigned char *tail; + unsigned int count; for (;;) { - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); if (!count) break; - tegra_tcu_write(tcu, &xmit->buf[xmit->tail], count); + tegra_tcu_write(tcu, tail, count); uart_xmit_advance(port, count); } diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 4bc89a9b380a..43fa0938b5e3 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -95,14 +95,11 @@ static void timbuart_rx_chars(struct uart_port *port) static void timbuart_tx_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + unsigned char ch; while (!(ioread32(port->membase + TIMBUART_ISR) & TXBF) && - !uart_circ_empty(xmit)) { - iowrite8(xmit->buf[xmit->tail], - port->membase + TIMBUART_TXFIFO); - uart_xmit_advance(port, 1); - } + uart_fifo_get(port, &ch)) + iowrite8(ch, port->membase + TIMBUART_TXFIFO); dev_dbg(port->dev, "%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n", @@ -117,9 +114,9 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) { struct timbuart_port *uart = container_of(port, struct timbuart_port, port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) return; if (port->x_char) @@ -130,7 +127,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) /* clear all TX interrupts */ iowrite32(TXFLAGS, port->membase + TIMBUART_ISR); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } else /* Re-enable any tx interrupt */ @@ -141,7 +138,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) * we wake up the upper layer later when we got the interrupt * to give it some time to go out... */ - if (!uart_circ_empty(xmit)) + if (!kfifo_is_empty(&tport->xmit_fifo)) *ier |= TXBAE; dev_dbg(port->dev, "%s - leaving\n", __func__); diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 10ba41b7be99..68357ac8ffe3 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -189,7 +189,8 @@ static int ulite_receive(struct uart_port *port, int stat) static int ulite_transmit(struct uart_port *port, int stat) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char ch; if (stat & ULITE_STATUS_TXFULL) return 0; @@ -201,14 +202,16 @@ static int ulite_transmit(struct uart_port *port, int stat) return 1; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + if (uart_tx_stopped(port)) return 0; - uart_out32(xmit->buf[xmit->tail], ULITE_TX, port); - uart_xmit_advance(port, 1); + if (!uart_fifo_get(port, &ch)) + return 0; + + uart_out32(ch, ULITE_TX, port); /* wake up */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); return 1; diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 397b95dff7ed..53bb8c5ef499 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -334,7 +334,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) unsigned char *p; unsigned int count; struct uart_port *port = &qe_port->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; /* Handle xon/xoff */ if (port->x_char) { @@ -358,7 +358,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) return 1; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { qe_uart_stop_tx(port); return 0; } @@ -366,16 +366,10 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) /* Pick next descriptor and fill from buffer */ bdp = qe_port->tx_cur; - while (!(ioread16be(&bdp->status) & BD_SC_READY) && !uart_circ_empty(xmit)) { - count = 0; + while (!(ioread16be(&bdp->status) & BD_SC_READY) && + !kfifo_is_empty(&tport->xmit_fifo)) { p = qe2cpu_addr(ioread32be(&bdp->buf), qe_port); - while (count < qe_port->tx_fifosize) { - *p++ = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); - count++; - if (uart_circ_empty(xmit)) - break; - } + count = uart_fifo_out(port, p, qe_port->tx_fifosize); iowrite16be(count, &bdp->length); qe_setbits_be16(&bdp->status, BD_SC_READY); @@ -388,10 +382,10 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) } qe_port->tx_cur = bdp; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { /* The kernel buffer is empty, so turn off TX interrupts. We don't need to be told when the QE is finished transmitting the data. */ diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 5f48ec37cb25..de3487206bcb 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -425,32 +425,32 @@ static void cdns_uart_handle_tx(void *dev_id) { struct uart_port *port = (struct uart_port *)dev_id; struct cdns_uart *cdns_uart = port->private_data; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned int numbytes; + unsigned char ch; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { /* Disable the TX Empty interrupt */ writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR); return; } numbytes = port->fifosize; - while (numbytes && !uart_circ_empty(xmit) && - !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) { - - writel(xmit->buf[xmit->tail], port->membase + CDNS_UART_FIFO); - uart_xmit_advance(port, 1); + while (numbytes && + !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL) && + uart_fifo_get(port, &ch)) { + writel(ch, port->membase + CDNS_UART_FIFO); numbytes--; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); /* Enable the TX Empty interrupt */ writel(CDNS_UART_IXR_TXEMPTY, cdns_uart->port->membase + CDNS_UART_IER); if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED && - (uart_circ_empty(xmit) || uart_tx_stopped(port))) { + (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))) { cdns_uart->tx_timer.function = &cdns_rs485_rx_callback; hrtimer_start(&cdns_uart->tx_timer, ns_to_ktime(cdns_calc_after_tx_delay(cdns_uart)), HRTIMER_MODE_REL); @@ -723,7 +723,7 @@ static void cdns_uart_start_tx(struct uart_port *port) status |= CDNS_UART_CR_TX_EN; writel(status, port->membase + CDNS_UART_CR); - if (uart_circ_empty(&port->state->xmit)) + if (kfifo_is_empty(&port->state->port.xmit_fifo)) return; /* Clear the TX Empty interrupt */ diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 65ca4da6e368..79ea7108a0f3 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -606,7 +606,8 @@ static void zs_receive_chars(struct zs_port *zport) static void zs_raw_transmit_chars(struct zs_port *zport) { - struct circ_buf *xmit = &zport->port.state->xmit; + struct tty_port *tport = &zport->port.state->port; + unsigned char ch; /* XON/XOFF chars. */ if (zport->port.x_char) { @@ -617,20 +618,20 @@ static void zs_raw_transmit_chars(struct zs_port *zport) } /* If nothing to do or stopped or hardware stopped. */ - if (uart_circ_empty(xmit) || uart_tx_stopped(&zport->port)) { + if (uart_tx_stopped(&zport->port) || + !uart_fifo_get(&zport->port, &ch)) { zs_raw_stop_tx(zport); return; } /* Send char. */ - write_zsdata(zport, xmit->buf[xmit->tail]); - uart_xmit_advance(&zport->port, 1); + write_zsdata(zport, ch); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&zport->port); /* Are we are done? */ - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) zs_raw_stop_tx(zport); } diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 0a0f6e21d40e..8cb65f50e830 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -699,7 +698,6 @@ struct uart_state { struct tty_port port; enum uart_pm_state pm_state; - struct circ_buf xmit; atomic_t refcount; wait_queue_head_t remove_wait; @@ -723,12 +721,35 @@ struct uart_state { */ static inline void uart_xmit_advance(struct uart_port *up, unsigned int chars) { - struct circ_buf *xmit = &up->state->xmit; + struct tty_port *tport = &up->state->port; - xmit->tail = (xmit->tail + chars) & (UART_XMIT_SIZE - 1); + kfifo_skip_count(&tport->xmit_fifo, chars); up->icount.tx += chars; } +static inline unsigned int uart_fifo_out(struct uart_port *up, + unsigned char *buf, unsigned int chars) +{ + struct tty_port *tport = &up->state->port; + + chars = kfifo_out(&tport->xmit_fifo, buf, chars); + up->icount.tx += chars; + + return chars; +} + +static inline unsigned int uart_fifo_get(struct uart_port *up, + unsigned char *ch) +{ + struct tty_port *tport = &up->state->port; + unsigned int chars; + + chars = kfifo_get(&tport->xmit_fifo, ch); + up->icount.tx += chars; + + return chars; +} + struct module; struct tty_driver; @@ -764,7 +785,7 @@ enum UART_TX_FLAGS { for_test, for_post) \ ({ \ struct uart_port *__port = (uport); \ - struct circ_buf *xmit = &__port->state->xmit; \ + struct tty_port *__tport = &__port->state->port; \ unsigned int pending; \ \ for (; (for_test) && (tx_ready); (for_post), __port->icount.tx++) { \ @@ -775,17 +796,18 @@ enum UART_TX_FLAGS { continue; \ } \ \ - if (uart_circ_empty(xmit) || uart_tx_stopped(__port)) \ + if (uart_tx_stopped(__port)) \ + break; \ + \ + if (!kfifo_get(&__tport->xmit_fifo, &(ch))) \ break; \ \ - (ch) = xmit->buf[xmit->tail]; \ (put_char); \ - xmit->tail = (xmit->tail + 1) % UART_XMIT_SIZE; \ } \ \ (tx_done); \ \ - pending = uart_circ_chars_pending(xmit); \ + pending = kfifo_len(&__tport->xmit_fifo); \ if (pending < WAKEUP_CHARS) { \ uart_write_wakeup(__port); \ \ @@ -974,15 +996,6 @@ bool uart_match_port(const struct uart_port *port1, int uart_suspend_port(struct uart_driver *reg, struct uart_port *port); int uart_resume_port(struct uart_driver *reg, struct uart_port *port); -#define uart_circ_empty(circ) ((circ)->head == (circ)->tail) -#define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0) - -#define uart_circ_chars_pending(circ) \ - (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE)) - -#define uart_circ_chars_free(circ) \ - (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) - static inline int uart_tx_stopped(struct uart_port *port) { struct tty_struct *tty = port->state->port.tty; From b9cea51b65abecb4dc327a19ab58e6fb116e7e85 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:24 +0200 Subject: [PATCH 013/113] tty: atmel_serial: use single DMA mapping for TX dma_map_single() provides much easier interface for simple mappings as used for TX in atmel_serial. So switch to that, removing all the s-g unnecessary handling. Note that it is not easy (maybe impossible) to use kfifo_dma_* API for atmel's serial purposes. It handles DMA very specially. Signed-off-by: Jiri Slaby (SUSE) Cc: Richard Genoud Cc: Nicolas Ferre Cc: Alexandre Belloni Cc: Claudiu Beznea Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240405060826.2521-14-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 35 +++++++++++++------------------ 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 5bb5e4303754..69ec80ffc97b 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -132,7 +132,7 @@ struct atmel_uart_port { struct dma_async_tx_descriptor *desc_rx; dma_cookie_t cookie_tx; dma_cookie_t cookie_rx; - struct scatterlist sg_tx; + dma_addr_t tx_phys; struct scatterlist sg_rx; struct tasklet_struct tasklet_rx; struct tasklet_struct tasklet_tx; @@ -904,8 +904,8 @@ static void atmel_release_tx_dma(struct uart_port *port) if (chan) { dmaengine_terminate_all(chan); dma_release_channel(chan); - dma_unmap_sg(port->dev, &atmel_port->sg_tx, 1, - DMA_TO_DEVICE); + dma_unmap_single(port->dev, atmel_port->tx_phys, + UART_XMIT_SIZE, DMA_TO_DEVICE); } atmel_port->desc_tx = NULL; @@ -922,7 +922,7 @@ static void atmel_tx_dma(struct uart_port *port) struct tty_port *tport = &port->state->port; struct dma_chan *chan = atmel_port->chan_tx; struct dma_async_tx_descriptor *desc; - struct scatterlist sgl[2], *sg, *sg_tx = &atmel_port->sg_tx; + struct scatterlist sgl[2], *sg; unsigned int tx_len, tail, part1_len, part2_len, sg_len; dma_addr_t phys_addr; @@ -955,7 +955,7 @@ static void atmel_tx_dma(struct uart_port *port) sg_init_table(sgl, 2); sg_len = 0; - phys_addr = sg_dma_address(sg_tx) + tail; + phys_addr = atmel_port->tx_phys + tail; if (part1_len) { sg = &sgl[sg_len++]; sg_dma_address(sg) = phys_addr; @@ -987,7 +987,8 @@ static void atmel_tx_dma(struct uart_port *port) return; } - dma_sync_sg_for_device(port->dev, sg_tx, 1, DMA_TO_DEVICE); + dma_sync_single_for_device(port->dev, atmel_port->tx_phys, + UART_XMIT_SIZE, DMA_TO_DEVICE); atmel_port->desc_tx = desc; desc->callback = atmel_complete_tx_dma; @@ -1014,7 +1015,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port) dma_cap_mask_t mask; struct dma_slave_config config; struct dma_chan *chan; - int ret, nent; + int ret; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -1029,26 +1030,18 @@ static int atmel_prepare_tx_dma(struct uart_port *port) dma_chan_name(atmel_port->chan_tx)); spin_lock_init(&atmel_port->lock_tx); - sg_init_table(&atmel_port->sg_tx, 1); /* UART circular tx buffer is an aligned page. */ BUG_ON(!PAGE_ALIGNED(tport->xmit_buf)); - sg_set_page(&atmel_port->sg_tx, - virt_to_page(tport->xmit_buf), - UART_XMIT_SIZE, - offset_in_page(tport->xmit_buf)); - nent = dma_map_sg(port->dev, - &atmel_port->sg_tx, - 1, - DMA_TO_DEVICE); + atmel_port->tx_phys = dma_map_single(port->dev, tport->xmit_buf, + UART_XMIT_SIZE, DMA_TO_DEVICE); - if (!nent) { + if (dma_mapping_error(port->dev, atmel_port->tx_phys)) { dev_dbg(port->dev, "need to release resource of dma\n"); goto chan_err; } else { - dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__, - sg_dma_len(&atmel_port->sg_tx), - tport->xmit_buf, - &sg_dma_address(&atmel_port->sg_tx)); + dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n", __func__, + UART_XMIT_SIZE, tport->xmit_buf, + &atmel_port->tx_phys); } /* Configure the slave DMA */ From 12bedddb67520d38274ae9163338a125c24732bb Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:25 +0200 Subject: [PATCH 014/113] tty: atmel_serial: define macro for RX size It is repeated in the code and there is also a big warning by ATMEL_SERIAL_RINGSIZE. So define ATMEL_SERIAL_RX_SIZE and use it appropriatelly. The macro uses array_size() and kmalloc_array() is switched to kmalloc(). Signed-off-by: Jiri Slaby (SUSE) Cc: Richard Genoud Cc: Nicolas Ferre Cc: Alexandre Belloni Cc: Claudiu Beznea Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240405060826.2521-15-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 69ec80ffc97b..5cde5077c429 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -96,7 +96,9 @@ struct atmel_uart_char { * can contain up to 1024 characters in PIO mode and up to 4096 characters in * DMA mode. */ -#define ATMEL_SERIAL_RINGSIZE 1024 +#define ATMEL_SERIAL_RINGSIZE 1024 +#define ATMEL_SERIAL_RX_SIZE array_size(sizeof(struct atmel_uart_char), \ + ATMEL_SERIAL_RINGSIZE) /* * at91: 6 USARTs and one DBGU port (SAM9260) @@ -1208,7 +1210,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port) BUG_ON(!PAGE_ALIGNED(ring->buf)); sg_set_page(&atmel_port->sg_rx, virt_to_page(ring->buf), - sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE, + ATMEL_SERIAL_RX_SIZE, offset_in_page(ring->buf)); nent = dma_map_sg(port->dev, &atmel_port->sg_rx, @@ -2947,9 +2949,7 @@ static int atmel_serial_probe(struct platform_device *pdev) if (!atmel_use_pdc_rx(&atmel_port->uart)) { ret = -ENOMEM; - data = kmalloc_array(ATMEL_SERIAL_RINGSIZE, - sizeof(struct atmel_uart_char), - GFP_KERNEL); + data = kmalloc(ATMEL_SERIAL_RX_SIZE, GFP_KERNEL); if (!data) goto err_clk_disable_unprepare; atmel_port->rx_ring.buf = data; From e51c3e1d236f8b0ea9e839f8f1f83aa31e0ce1e8 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 5 Apr 2024 08:08:26 +0200 Subject: [PATCH 015/113] tty: atmel_serial: use single DMA mapping for RX dma_map_single() provides much easier interface for simple mappings as used for RX in atmel_serial. So switch to that, removing all the s-g unnecessary handling. Signed-off-by: Jiri Slaby (SUSE) Cc: Richard Genoud Cc: Nicolas Ferre Cc: Alexandre Belloni Cc: Claudiu Beznea Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240405060826.2521-16-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 54 ++++++++++++------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 5cde5077c429..0a90964d6d10 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -135,7 +135,7 @@ struct atmel_uart_port { dma_cookie_t cookie_tx; dma_cookie_t cookie_rx; dma_addr_t tx_phys; - struct scatterlist sg_rx; + dma_addr_t rx_phys; struct tasklet_struct tasklet_rx; struct tasklet_struct tasklet_tx; atomic_t tasklet_shutdown; @@ -1088,8 +1088,8 @@ static void atmel_release_rx_dma(struct uart_port *port) if (chan) { dmaengine_terminate_all(chan); dma_release_channel(chan); - dma_unmap_sg(port->dev, &atmel_port->sg_rx, 1, - DMA_FROM_DEVICE); + dma_unmap_single(port->dev, atmel_port->rx_phys, + ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE); } atmel_port->desc_rx = NULL; @@ -1122,10 +1122,8 @@ static void atmel_rx_from_dma(struct uart_port *port) } /* CPU claims ownership of RX DMA buffer */ - dma_sync_sg_for_cpu(port->dev, - &atmel_port->sg_rx, - 1, - DMA_FROM_DEVICE); + dma_sync_single_for_cpu(port->dev, atmel_port->rx_phys, + ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE); /* * ring->head points to the end of data already written by the DMA. @@ -1134,8 +1132,8 @@ static void atmel_rx_from_dma(struct uart_port *port) * The current transfer size should not be larger than the dma buffer * length. */ - ring->head = sg_dma_len(&atmel_port->sg_rx) - state.residue; - BUG_ON(ring->head > sg_dma_len(&atmel_port->sg_rx)); + ring->head = ATMEL_SERIAL_RX_SIZE - state.residue; + BUG_ON(ring->head > ATMEL_SERIAL_RX_SIZE); /* * At this point ring->head may point to the first byte right after the * last byte of the dma buffer: @@ -1149,7 +1147,7 @@ static void atmel_rx_from_dma(struct uart_port *port) * tail to the end of the buffer then reset tail. */ if (ring->head < ring->tail) { - count = sg_dma_len(&atmel_port->sg_rx) - ring->tail; + count = ATMEL_SERIAL_RX_SIZE - ring->tail; tty_insert_flip_string(tport, ring->buf + ring->tail, count); ring->tail = 0; @@ -1162,17 +1160,15 @@ static void atmel_rx_from_dma(struct uart_port *port) tty_insert_flip_string(tport, ring->buf + ring->tail, count); /* Wrap ring->head if needed */ - if (ring->head >= sg_dma_len(&atmel_port->sg_rx)) + if (ring->head >= ATMEL_SERIAL_RX_SIZE) ring->head = 0; ring->tail = ring->head; port->icount.rx += count; } /* USART retreives ownership of RX DMA buffer */ - dma_sync_sg_for_device(port->dev, - &atmel_port->sg_rx, - 1, - DMA_FROM_DEVICE); + dma_sync_single_for_device(port->dev, atmel_port->rx_phys, + ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE); tty_flip_buffer_push(tport); @@ -1188,7 +1184,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port) struct dma_slave_config config; struct circ_buf *ring; struct dma_chan *chan; - int ret, nent; + int ret; ring = &atmel_port->rx_ring; @@ -1205,26 +1201,18 @@ static int atmel_prepare_rx_dma(struct uart_port *port) dma_chan_name(atmel_port->chan_rx)); spin_lock_init(&atmel_port->lock_rx); - sg_init_table(&atmel_port->sg_rx, 1); /* UART circular rx buffer is an aligned page. */ BUG_ON(!PAGE_ALIGNED(ring->buf)); - sg_set_page(&atmel_port->sg_rx, - virt_to_page(ring->buf), - ATMEL_SERIAL_RX_SIZE, - offset_in_page(ring->buf)); - nent = dma_map_sg(port->dev, - &atmel_port->sg_rx, - 1, - DMA_FROM_DEVICE); + atmel_port->rx_phys = dma_map_single(port->dev, ring->buf, + ATMEL_SERIAL_RX_SIZE, + DMA_FROM_DEVICE); - if (!nent) { + if (dma_mapping_error(port->dev, atmel_port->rx_phys)) { dev_dbg(port->dev, "need to release resource of dma\n"); goto chan_err; } else { - dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__, - sg_dma_len(&atmel_port->sg_rx), - ring->buf, - &sg_dma_address(&atmel_port->sg_rx)); + dev_dbg(port->dev, "%s: mapped %zu@%p to %pad\n", __func__, + ATMEL_SERIAL_RX_SIZE, ring->buf, &atmel_port->rx_phys); } /* Configure the slave DMA */ @@ -1245,9 +1233,9 @@ static int atmel_prepare_rx_dma(struct uart_port *port) * each one is half ring buffer size */ desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx, - sg_dma_address(&atmel_port->sg_rx), - sg_dma_len(&atmel_port->sg_rx), - sg_dma_len(&atmel_port->sg_rx)/2, + atmel_port->rx_phys, + ATMEL_SERIAL_RX_SIZE, + ATMEL_SERIAL_RX_SIZE / 2, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!desc) { From f03e8c1060f86c23eb49bafee99d9fcbd1c1bd77 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 27 Mar 2024 12:59:35 +0200 Subject: [PATCH 016/113] printk: Save console options for add_preferred_console_match() Driver subsystems may need to translate the preferred console name to the character device name used. We already do some of this in console_setup() with a few hardcoded names, but that does not scale well. The console options are parsed early in console_setup(), and the consoles are added with __add_preferred_console(). At this point we don't know much about the character device names and device drivers getting probed. To allow driver subsystems to set up a preferred console, let's save the kernel command line console options. To add a preferred console from a driver subsystem with optional character device name translation, let's add a new function add_preferred_console_match(). This allows the serial core layer to support console=DEVNAME:0.0 style hardware based addressing in addition to the current console=ttyS0 style naming. And we can start moving console_setup() character device parsing to the driver subsystem specific code. We use a separate array from the console_cmdline array as the character device name and index may be unknown at the console_setup() time. And eventually there's no need to call __add_preferred_console() until the subsystem is ready to handle the console. Adding the console name in addition to the character device name, and a flag for an added console, could be added to the struct console_cmdline. And the console_cmdline array handling could be modified accordingly. But that complicates things compared saving the console options, and then adding the consoles when the subsystems handling the consoles are ready. Co-developed-by: Andy Shevchenko Signed-off-by: Tony Lindgren Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240327110021.59793-2-tony@atomide.com Signed-off-by: Greg Kroah-Hartman --- include/linux/printk.h | 3 + kernel/printk/Makefile | 2 +- kernel/printk/conopt.c | 146 ++++++++++++++++++++++++++++++++ kernel/printk/console_cmdline.h | 6 ++ kernel/printk/printk.c | 14 ++- 5 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 kernel/printk/conopt.c diff --git a/include/linux/printk.h b/include/linux/printk.h index 955e31860095..5e038e782256 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -60,6 +60,9 @@ static inline const char *printk_skip_headers(const char *buffer) #define CONSOLE_LOGLEVEL_DEFAULT CONFIG_CONSOLE_LOGLEVEL_DEFAULT #define CONSOLE_LOGLEVEL_QUIET CONFIG_CONSOLE_LOGLEVEL_QUIET +int add_preferred_console_match(const char *match, const char *name, + const short idx); + extern int console_printk[]; #define console_loglevel (console_printk[0]) diff --git a/kernel/printk/Makefile b/kernel/printk/Makefile index 39a2b61c7232..040fe7d1eda2 100644 --- a/kernel/printk/Makefile +++ b/kernel/printk/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-y = printk.o +obj-y = printk.o conopt.o obj-$(CONFIG_PRINTK) += printk_safe.o nbcon.o obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille.o obj-$(CONFIG_PRINTK_INDEX) += index.o diff --git a/kernel/printk/conopt.c b/kernel/printk/conopt.c new file mode 100644 index 000000000000..9d507bac3657 --- /dev/null +++ b/kernel/printk/conopt.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kernel command line console options for hardware based addressing + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Tony Lindgren + */ + +#include +#include +#include +#include + +#include + +#include "console_cmdline.h" + +/* + * Allow longer DEVNAME:0.0 style console naming such as abcd0000.serial:0.0 + * in addition to the legacy ttyS0 style naming. + */ +#define CONSOLE_NAME_MAX 32 + +#define CONSOLE_OPT_MAX 16 +#define CONSOLE_BRL_OPT_MAX 16 + +struct console_option { + char name[CONSOLE_NAME_MAX]; + char opt[CONSOLE_OPT_MAX]; + char brl_opt[CONSOLE_BRL_OPT_MAX]; + u8 has_brl_opt:1; +}; + +/* Updated only at console_setup() time, no locking needed */ +static struct console_option conopt[MAX_CMDLINECONSOLES]; + +/** + * console_opt_save - Saves kernel command line console option for driver use + * @str: Kernel command line console name and option + * @brl_opt: Braille console options + * + * Saves a kernel command line console option for driver subsystems to use for + * adding a preferred console during init. Called from console_setup() only. + * + * Return: 0 on success, negative error code on failure. + */ +int __init console_opt_save(const char *str, const char *brl_opt) +{ + struct console_option *con; + size_t namelen, optlen; + const char *opt; + int i; + + namelen = strcspn(str, ","); + if (namelen == 0 || namelen >= CONSOLE_NAME_MAX) + return -EINVAL; + + opt = str + namelen; + if (*opt == ',') + opt++; + + optlen = strlen(opt); + if (optlen >= CONSOLE_OPT_MAX) + return -EINVAL; + + for (i = 0; i < MAX_CMDLINECONSOLES; i++) { + con = &conopt[i]; + + if (con->name[0]) { + if (!strncmp(str, con->name, namelen)) + return 0; + continue; + } + + /* + * The name isn't terminated, only opt is. Empty opt is fine, + * but brl_opt can be either empty or NULL. For more info, see + * _braille_console_setup(). + */ + strscpy(con->name, str, namelen + 1); + strscpy(con->opt, opt, CONSOLE_OPT_MAX); + if (brl_opt) { + strscpy(con->brl_opt, brl_opt, CONSOLE_BRL_OPT_MAX); + con->has_brl_opt = 1; + } + + return 0; + } + + return -ENOMEM; +} + +static struct console_option *console_opt_find(const char *name) +{ + struct console_option *con; + int i; + + for (i = 0; i < MAX_CMDLINECONSOLES; i++) { + con = &conopt[i]; + if (!strcmp(name, con->name)) + return con; + } + + return NULL; +} + +/** + * add_preferred_console_match - Adds a preferred console if a match is found + * @match: Expected console on kernel command line, such as console=DEVNAME:0.0 + * @name: Name of the console character device to add such as ttyS + * @idx: Index for the console + * + * Allows driver subsystems to add a console after translating the command + * line name to the character device name used for the console. Options are + * added automatically based on the kernel command line. Duplicate preferred + * consoles are ignored by __add_preferred_console(). + * + * Return: 0 on success, negative error code on failure. + */ +int add_preferred_console_match(const char *match, const char *name, + const short idx) +{ + struct console_option *con; + char *brl_opt = NULL; + + if (!match || !strlen(match) || !name || !strlen(name) || + idx < 0) + return -EINVAL; + + con = console_opt_find(match); + if (!con) + return -ENOENT; + + /* + * See __add_preferred_console(). It checks for NULL brl_options to set + * the preferred_console flag. Empty brl_opt instead of NULL leads into + * the preferred_console flag not set, and CON_CONSDEV not being set, + * and the boot console won't get disabled at the end of console_setup(). + */ + if (con->has_brl_opt) + brl_opt = con->brl_opt; + + console_opt_add_preferred_console(name, idx, con->opt, brl_opt); + + return 0; +} diff --git a/kernel/printk/console_cmdline.h b/kernel/printk/console_cmdline.h index 3ca74ad391d6..a125e0235589 100644 --- a/kernel/printk/console_cmdline.h +++ b/kernel/printk/console_cmdline.h @@ -2,6 +2,12 @@ #ifndef _CONSOLE_CMDLINE_H #define _CONSOLE_CMDLINE_H +#define MAX_CMDLINECONSOLES 8 + +int console_opt_save(const char *str, const char *brl_opt); +int console_opt_add_preferred_console(const char *name, const short idx, + char *options, char *brl_options); + struct console_cmdline { char name[16]; /* Name of the driver */ diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index adf99c05adca..05919491c5a2 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -383,9 +383,6 @@ static int console_locked; /* * Array of consoles built from command line options (console=) */ - -#define MAX_CMDLINECONSOLES 8 - static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES]; static int preferred_console = -1; @@ -2503,6 +2500,10 @@ static int __init console_setup(char *str) if (_braille_console_setup(&str, &brl_options)) return 1; + /* Save the console for driver subsystem use */ + if (console_opt_save(str, brl_options)) + return 1; + /* * Decode str into name, index, options. */ @@ -2533,6 +2534,13 @@ static int __init console_setup(char *str) } __setup("console=", console_setup); +/* Only called from add_preferred_console_match() */ +int console_opt_add_preferred_console(const char *name, const short idx, + char *options, char *brl_options) +{ + return __add_preferred_console(name, idx, options, brl_options, true); +} + /** * add_preferred_console - add a device to the list of preferred consoles. * @name: device name From 8a831c584e6e80cf68f79893dc395c16cdf47dc8 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 27 Mar 2024 12:59:36 +0200 Subject: [PATCH 017/113] printk: Don't try to parse DEVNAME:0.0 console options Currently console_setup() tries to make a console index out of any digits passed in the kernel command line for console. In the DEVNAME:0.0 case, the name can contain a device IO address, so bail out on console names with a ':'. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20240327110021.59793-3-tony@atomide.com Signed-off-by: Greg Kroah-Hartman --- kernel/printk/printk.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 05919491c5a2..67937e5c6010 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -2504,6 +2504,10 @@ static int __init console_setup(char *str) if (console_opt_save(str, brl_options)) return 1; + /* Don't attempt to parse a DEVNAME:0.0 style console */ + if (strchr(str, ':')) + return 1; + /* * Decode str into name, index, options. */ From b73c9cbe4f1fc02645228aa575998dd54067f8ef Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 27 Mar 2024 12:59:37 +0200 Subject: [PATCH 018/113] printk: Flag register_console() if console is set on command line If add_preferred_console() is not called early in setup_console(), we can end up having register_console() call try_enable_default_console() before a console device has called add_preferred_console(). Let's set console_set_on_cmdline flag in console_setup() to prevent this from happening. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20240327110021.59793-4-tony@atomide.com Signed-off-by: Greg Kroah-Hartman --- kernel/printk/printk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 67937e5c6010..1e3b21682547 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -2504,6 +2504,9 @@ static int __init console_setup(char *str) if (console_opt_save(str, brl_options)) return 1; + /* Flag register_console() to not call try_enable_default_console() */ + console_set_on_cmdline = 1; + /* Don't attempt to parse a DEVNAME:0.0 style console */ if (strchr(str, ':')) return 1; @@ -3507,7 +3510,7 @@ void register_console(struct console *newcon) * Note that a console with tty binding will have CON_CONSDEV * flag set and will be first in the list. */ - if (preferred_console < 0) { + if (preferred_console < 0 && !console_set_on_cmdline) { if (hlist_empty(&console_list) || !console_first()->device || console_first()->flags & CON_BOOT) { try_enable_default_console(newcon); From 787a1cabac01c99846070fcf702e53befaf89f79 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 27 Mar 2024 12:59:38 +0200 Subject: [PATCH 019/113] serial: core: Add support for DEVNAME:0.0 style naming for kernel console We can now add hardware based addressing for serial ports. Starting with commit 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM"), and all the related fixes to this commit, the serial core now knows to which serial port controller the ports are connected. The serial ports can be addressed with DEVNAME:0.0 style naming. The names are something like 00:04:0.0 for a serial port on qemu, and something like 2800000.serial:0.0 on platform device using systems like ARM64 for example. The DEVNAME is the unique serial port hardware controller device name, AKA the name for port->dev. The 0.0 are the serial core controller id and port id. Typically 0.0 are used for each controller and port instance unless the serial port hardware controller has multiple controllers or ports. Using DEVNAME:0.0 style naming actually solves two long term issues for addressing the serial ports: 1. According to Andy Shevchenko, using DEVNAME:0.0 style naming fixes an issue where depending on the BIOS settings, the kernel serial port ttyS instance number may change if HSUART is enabled 2. Device tree using architectures no longer necessarily need to specify aliases to find a specific serial port, and we can just allocate the ttyS instance numbers dynamically in whatever probe order To do this, let's match the hardware addressing style console name to the character device name used, and add a preferred console using the character device name. Note that when using console=DEVNAME:0.0 style kernel command line, the 8250 serial console gets enabled later compared to using console=ttyS naming for ISA ports. This is because the serial port DEVNAME to character device mapping is not known until the serial driver probe time. If used together with earlycon, this issue is avoided. Signed-off-by: Tony Lindgren Reviewed-by: Dhruva Gole Link: https://lore.kernel.org/r/20240327110021.59793-5-tony@atomide.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_base.h | 16 +++++++ drivers/tty/serial/serial_base_bus.c | 66 ++++++++++++++++++++++++++++ drivers/tty/serial/serial_core.c | 4 ++ 3 files changed, 86 insertions(+) diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h index c74c548f0db6..327a18b1813b 100644 --- a/drivers/tty/serial/serial_base.h +++ b/drivers/tty/serial/serial_base.h @@ -45,3 +45,19 @@ void serial_ctrl_unregister_port(struct uart_driver *drv, struct uart_port *port int serial_core_register_port(struct uart_driver *drv, struct uart_port *port); void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port); + +#ifdef CONFIG_SERIAL_CORE_CONSOLE + +int serial_base_add_preferred_console(struct uart_driver *drv, + struct uart_port *port); + +#else + +static inline +int serial_base_add_preferred_console(struct uart_driver *drv, + struct uart_port *port) +{ + return 0; +} + +#endif diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c index 4df2a4b10445..4ef26a1b09b1 100644 --- a/drivers/tty/serial/serial_base_bus.c +++ b/drivers/tty/serial/serial_base_bus.c @@ -8,6 +8,7 @@ * The serial core bus manages the serial core controller instances. */ +#include #include #include #include @@ -204,6 +205,71 @@ void serial_base_port_device_remove(struct serial_port_device *port_dev) put_device(&port_dev->dev); } +#ifdef CONFIG_SERIAL_CORE_CONSOLE + +static int serial_base_add_one_prefcon(const char *match, const char *dev_name, + int port_id) +{ + int ret; + + ret = add_preferred_console_match(match, dev_name, port_id); + if (ret == -ENOENT) + return 0; + + return ret; +} + +static int serial_base_add_prefcon(const char *name, int idx) +{ + const char *char_match __free(kfree) = NULL; + + /* Handle the traditional character device name style console=ttyS0 */ + char_match = kasprintf(GFP_KERNEL, "%s%i", name, idx); + if (!char_match) + return -ENOMEM; + + return serial_base_add_one_prefcon(char_match, name, idx); +} + +/** + * serial_base_add_preferred_console - Adds a preferred console + * @drv: Serial port device driver + * @port: Serial port instance + * + * Tries to add a preferred console for a serial port if specified in the + * kernel command line. Supports both the traditional character device such + * as console=ttyS0, and a hardware addressing based console=DEVNAME:0.0 + * style name. + * + * Translates the kernel command line option using a hardware based addressing + * console=DEVNAME:0.0 to the serial port character device such as ttyS0. + * Cannot be called early for ISA ports, depends on struct device. + * + * Note that duplicates are ignored by add_preferred_console(). + * + * Return: 0 on success, negative error code on failure. + */ +int serial_base_add_preferred_console(struct uart_driver *drv, + struct uart_port *port) +{ + const char *port_match __free(kfree) = NULL; + int ret; + + ret = serial_base_add_prefcon(drv->dev_name, port->line); + if (ret) + return ret; + + port_match = kasprintf(GFP_KERNEL, "%s:%i.%i", dev_name(port->dev), + port->ctrl_id, port->port_id); + if (!port_match) + return -ENOMEM; + + /* Translate a hardware addressing style console=DEVNAME:0.0 */ + return serial_base_add_one_prefcon(port_match, drv->dev_name, port->line); +} + +#endif + static int serial_base_init(void) { int ret; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 3c0931fba1c6..a78ded8c60b5 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -3393,6 +3393,10 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port) if (ret) goto err_unregister_ctrl_dev; + ret = serial_base_add_preferred_console(drv, port); + if (ret) + goto err_unregister_port_dev; + ret = serial_core_add_one_port(drv, port); if (ret) goto err_unregister_port_dev; From a0f32e2dd99867b164bfebcf36729c2a0d41b30b Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 27 Mar 2024 12:59:39 +0200 Subject: [PATCH 020/113] serial: core: Handle serial console options In order to start moving the serial console quirks out of console_setup(), let's add parsing for the quirks to the serial core layer. We can use serial_base_add_one_prefcon() to handle the quirks. Note that eventually we may want to set up driver specific console quirk handling for the serial port device drivers to use. But we need to figure out which driver(s) need to call the quirk. So for now, we just handle the sparc quirk directly. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20240327110021.59793-6-tony@atomide.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_base_bus.c | 49 ++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c index 4ef26a1b09b1..1375be3315e6 100644 --- a/drivers/tty/serial/serial_base_bus.c +++ b/drivers/tty/serial/serial_base_bus.c @@ -219,9 +219,58 @@ static int serial_base_add_one_prefcon(const char *match, const char *dev_name, return ret; } +#ifdef __sparc__ + +/* Handle Sparc ttya and ttyb options as done in console_setup() */ +static int serial_base_add_sparc_console(const char *dev_name, int idx) +{ + const char *name; + + switch (idx) { + case 0: + name = "ttya"; + break; + case 1: + name = "ttyb"; + break; + default: + return 0; + } + + return serial_base_add_one_prefcon(name, dev_name, idx); +} + +#else + +static inline int serial_base_add_sparc_console(const char *dev_name, int idx) +{ + return 0; +} + +#endif + static int serial_base_add_prefcon(const char *name, int idx) { const char *char_match __free(kfree) = NULL; + const char *nmbr_match __free(kfree) = NULL; + int ret; + + /* Handle ttyS specific options */ + if (strstarts(name, "ttyS")) { + /* No name, just a number */ + nmbr_match = kasprintf(GFP_KERNEL, "%i", idx); + if (!nmbr_match) + return -ENODEV; + + ret = serial_base_add_one_prefcon(nmbr_match, name, idx); + if (ret) + return ret; + + /* Sparc ttya and ttyb */ + ret = serial_base_add_sparc_console(name, idx); + if (ret) + return ret; + } /* Handle the traditional character device name style console=ttyS0 */ char_match = kasprintf(GFP_KERNEL, "%s%i", name, idx); From a8b04cfe7dad84e65df5996e14b435fd356fe62c Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 27 Mar 2024 12:59:40 +0200 Subject: [PATCH 021/113] serial: 8250: Add preferred console in serial8250_isa_init_ports() Prepare 8250 ISA ports to drop kernel command line serial console handling from console_setup(). We need to set the preferred console in serial8250_isa_init_ports() to drop a dependency to setup_console() handling the ttyS related quirks. Otherwise when console_setup() handles the ttyS related options, console gets enabled only at driver probe time. Note that this mostly affects x86 as this happens based on define SERIAL_PORT_DFNS. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20240327110021.59793-7-tony@atomide.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 5 +++++ drivers/tty/serial/serial_base.h | 8 ++++++++ drivers/tty/serial/serial_base_bus.c | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 3a1936b7f05f..43824a174a51 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -41,6 +42,8 @@ #include +#include "../serial_base.h" /* For serial_base_add_isa_preferred_console() */ + #include "8250.h" /* @@ -564,6 +567,8 @@ static void __init serial8250_isa_init_ports(void) port->irqflags |= irqflag; if (serial8250_isa_config != NULL) serial8250_isa_config(i, &up->port, &up->capabilities); + + serial_base_add_isa_preferred_console(serial8250_reg.dev_name, i); } } diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h index 327a18b1813b..39dab6bd4b76 100644 --- a/drivers/tty/serial/serial_base.h +++ b/drivers/tty/serial/serial_base.h @@ -51,6 +51,8 @@ void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port int serial_base_add_preferred_console(struct uart_driver *drv, struct uart_port *port); +int serial_base_add_isa_preferred_console(const char *name, int idx); + #else static inline @@ -60,4 +62,10 @@ int serial_base_add_preferred_console(struct uart_driver *drv, return 0; } +static inline +int serial_base_add_isa_preferred_console(const char *name, int idx) +{ + return 0; +} + #endif diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c index 1375be3315e6..ae18871c0f36 100644 --- a/drivers/tty/serial/serial_base_bus.c +++ b/drivers/tty/serial/serial_base_bus.c @@ -317,6 +317,27 @@ int serial_base_add_preferred_console(struct uart_driver *drv, return serial_base_add_one_prefcon(port_match, drv->dev_name, port->line); } +#ifdef CONFIG_SERIAL_8250_CONSOLE + +/* + * Early ISA ports initialize the console before there is no struct device. + * This should be only called from serial8250_isa_init_preferred_console(), + * other callers are likely wrong and should rely on earlycon instead. + */ +int serial_base_add_isa_preferred_console(const char *name, int idx) +{ + return serial_base_add_prefcon(name, idx); +} + +#else + +int serial_base_add_isa_preferred_console(const char *name, int idx) +{ + return 0; +} + +#endif + #endif static int serial_base_init(void) From 5c3a766e9f057ee7a54b5d7addff7fab02676fea Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 27 Mar 2024 12:59:41 +0200 Subject: [PATCH 022/113] Documentation: kernel-parameters: Add DEVNAME:0.0 format for serial ports Document the console option for DEVNAME:0.0 style addressing for serial ports. Suggested-by: Sebastian Reichel Signed-off-by: Tony Lindgren Reviewed-by: Dhruva Gole Link: https://lore.kernel.org/r/20240327110021.59793-8-tony@atomide.com Signed-off-by: Greg Kroah-Hartman --- .../admin-guide/kernel-parameters.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 623fce7d5fcd..ecf94382981f 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -785,6 +785,25 @@ Documentation/networking/netconsole.rst for an alternative. + :.[,options] + Use the specified serial port on the serial core bus. + The addressing uses DEVNAME of the physical serial port + device, followed by the serial core controller instance, + and the serial port instance. The options are the same + as documented for the ttyS addressing above. + + The mapping of the serial ports to the tty instances + can be viewed with: + + $ ls -d /sys/bus/serial-base/devices/*:*.*/tty/* + /sys/bus/serial-base/devices/00:04:0.0/tty/ttyS0 + + In the above example, the console can be addressed with + console=00:04:0.0. Note that a console addressed this + way will only get added when the related device driver + is ready. The use of an earlycon parameter in addition to + the console may be desired for console output early on. + uart[8250],io,[,options] uart[8250],mmio,[,options] uart[8250],mmio16,[,options] From a80451572968279cbc1e2d921fd5c9a3136d3217 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 7 Mar 2024 13:40:48 +0200 Subject: [PATCH 023/113] serial: 8250_omap: Remove unused of_gpio.h of_gpio.h is deprecated and subject to remove. The driver doesn't use it, simply remove the unused header. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240307114048.3642642-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index c07d924d5add..170639d12b2a 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include From 56de74b4d783f41c2e2b745555c662c4031b5d87 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 7 Mar 2024 13:42:43 +0200 Subject: [PATCH 024/113] serial: pic32_uart: Replace of_gpio.h by proper one of_gpio.h is deprecated and subject to remove. The driver doesn't use it directly, replace it with what is really being used. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240307114243.3642832-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pic32_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index f5af336a869b..261c8115a700 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -8,11 +8,11 @@ * Sorin-Andrei Pistirica */ +#include #include #include #include #include -#include #include #include #include From 4210022789f345642d56b8165757df6316560db7 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 7 Mar 2024 10:09:50 +0100 Subject: [PATCH 025/113] serial: sifive: Remove 0 from fu540-c000-uart0 binding. The driver is using "sifive,fu540-c000-uart0" as a binding. The device tree and documentation states "sifive,fu540-c000-uart" instead. This means the binding is not matched and not used. This did not cause any problems because the alternative binding, used in the device tree, "sifive,uart0" is not handling the hardware any different. Align the binding in the driver with the documentation. Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20240307090950.eLELkuyK@linutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sifive.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index 0670fd9f8496..cbfce65c9d22 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -761,7 +761,7 @@ static int __init early_sifive_serial_setup(struct earlycon_device *dev, } OF_EARLYCON_DECLARE(sifive, "sifive,uart0", early_sifive_serial_setup); -OF_EARLYCON_DECLARE(sifive, "sifive,fu540-c000-uart0", +OF_EARLYCON_DECLARE(sifive, "sifive,fu540-c000-uart", early_sifive_serial_setup); #endif /* CONFIG_SERIAL_EARLYCON */ @@ -1032,7 +1032,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(sifive_uart_pm_ops, sifive_serial_suspend, sifive_serial_resume); static const struct of_device_id sifive_serial_of_match[] = { - { .compatible = "sifive,fu540-c000-uart0" }, + { .compatible = "sifive,fu540-c000-uart" }, { .compatible = "sifive,uart0" }, {}, }; From b464199eaae81f51de8c7b07605643195645c663 Mon Sep 17 00:00:00 2001 From: Nghia Nguyen Date: Tue, 12 Mar 2024 10:00:55 +0100 Subject: [PATCH 026/113] dt-bindings: serial: renesas,scif: Document r8a779h0 bindings R-Car V4M (R8A779H0) SoC has the R-Car Gen4 compatible SCIF ports, so document the SoC specific bindings. Signed-off-by: Nghia Nguyen Signed-off-by: Geert Uytterhoeven Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/49b854603c2c3ed6b2edd441f1d55160e0453b70.1709741175.git.geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/renesas,scif.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/serial/renesas,scif.yaml b/Documentation/devicetree/bindings/serial/renesas,scif.yaml index 4610a5bd580c..f3a3eb2831e9 100644 --- a/Documentation/devicetree/bindings/serial/renesas,scif.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,scif.yaml @@ -68,6 +68,7 @@ properties: - renesas,scif-r8a779a0 # R-Car V3U - renesas,scif-r8a779f0 # R-Car S4-8 - renesas,scif-r8a779g0 # R-Car V4H + - renesas,scif-r8a779h0 # R-Car V4M - const: renesas,rcar-gen4-scif # R-Car Gen4 - const: renesas,scif # generic SCIF compatible UART From d78cc0df9d2b49ed7f87b46c7041aab1bce2995f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 15 Mar 2024 09:17:34 +0000 Subject: [PATCH 027/113] tty: hvc: Remove second semicolon There is a statement with two semicolons. Remove the second one, it is redundant. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20240315091734.2430416-1-colin.i.king@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvc_xen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 0e497501f8e3..388a71afd6ef 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -558,7 +558,7 @@ static void xencons_backend_changed(struct xenbus_device *dev, break; fallthrough; /* Missed the backend's CLOSING state */ case XenbusStateClosing: { - struct xencons_info *info = dev_get_drvdata(&dev->dev);; + struct xencons_info *info = dev_get_drvdata(&dev->dev); /* * Don't tear down the evtchn and grant ref before the other From da4e0ba419bb953fb8ae0aa4f85aef3febfbdf3e Mon Sep 17 00:00:00 2001 From: Justin Stitt Date: Mon, 18 Mar 2024 23:02:12 +0000 Subject: [PATCH 028/113] tty: n_gsm: replace deprecated strncpy with strscpy strncpy() is deprecated for use on NUL-terminated destination strings [1] and as such we should prefer more robust and less ambiguous string interfaces. We expect nc->if_name to be NUL-terminated based on existing manual NUL-byte assignments and checks: | nc.if_name[IFNAMSIZ-1] = '\0'; ... | if (nc->if_name[0] != '\0') Let's use the new 2-argument strscpy() since it guarantees NUL-termination on the destination buffer while correctly using the destination buffers size to bound the operation. Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1] Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2] Link: https://github.com/KSPP/linux/issues/90 Cc: linux-hardening@vger.kernel.org Signed-off-by: Justin Stitt Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20240318-strncpy-drivers-tty-n_gsm-c-v1-1-da37a07c642e@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 4036566febcb..f5b0d91d32a7 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -4010,7 +4010,7 @@ static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc) mux_net = netdev_priv(net); mux_net->dlci = dlci; kref_init(&mux_net->ref); - strncpy(nc->if_name, net->name, IFNAMSIZ); /* return net name */ + strscpy(nc->if_name, net->name); /* return net name */ /* reconfigure dlci for network */ dlci->prev_adaption = dlci->adaption; From 3bd85c6c97b2d232638594bf828de62083fe3389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 11 Mar 2024 12:30:18 +0100 Subject: [PATCH 029/113] tty: vt: conmakehash: Don't mention the full path of the input in output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change strips $abs_srctree of the input file containing the character mapping table in the generated output. The motivation for this change is Yocto emitting a build warning WARNING: linux-lxatac-6.7-r0 do_package_qa: QA Issue: File /usr/src/debug/linux-lxatac/6.7-r0/drivers/tty/vt/consolemap_deftbl.c in package linux-lxatac-src contains reference to TMPDIR So this change brings us one step closer to make the build result reproducible independent of the build path. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20240311113017.483101-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/conmakehash.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/tty/vt/conmakehash.c b/drivers/tty/vt/conmakehash.c index cddd789fe46e..dc2177fec715 100644 --- a/drivers/tty/vt/conmakehash.c +++ b/drivers/tty/vt/conmakehash.c @@ -76,7 +76,8 @@ static void addpair(int fp, int un) int main(int argc, char *argv[]) { FILE *ctbl; - char *tblname; + const char *tblname, *rel_tblname; + const char *abs_srctree; char buffer[65536]; int fontlen; int i, nuni, nent; @@ -101,6 +102,16 @@ int main(int argc, char *argv[]) } } + abs_srctree = getenv("abs_srctree"); + if (abs_srctree && !strncmp(abs_srctree, tblname, strlen(abs_srctree))) + { + rel_tblname = tblname + strlen(abs_srctree); + while (*rel_tblname == '/') + ++rel_tblname; + } + else + rel_tblname = tblname; + /* For now we assume the default font is always 256 characters. */ fontlen = 256; @@ -253,7 +264,7 @@ int main(int argc, char *argv[]) #include \n\ \n\ u8 dfont_unicount[%d] = \n\ -{\n\t", argv[1], fontlen); +{\n\t", rel_tblname, fontlen); for ( i = 0 ; i < fontlen ; i++ ) { From 9013517527b628019a9acecc7da4a13b0a62d851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 29 Mar 2024 22:54:40 +0100 Subject: [PATCH 030/113] serial: ami: Mark driver struct with __refdata to prevent section mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As described in the added code comment, a reference to .exit.text is ok for drivers registered via module_platform_driver_probe(). Make this explicit to prevent the following section mismatch warning WARNING: modpost: drivers/tty/amiserial: section mismatch in reference: amiga_serial_driver+0x8 (section: .data) -> amiga_serial_remove (section: .exit.text) that triggers on an allmodconfig W=1 build. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/043afcbc94ad90079301f3c7738136a7993a1748.1711748999.git.u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index e27360652d9b..8c964da75f2d 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1578,7 +1578,13 @@ static void __exit amiga_serial_remove(struct platform_device *pdev) free_irq(IRQ_AMIGA_RBF, state); } -static struct platform_driver amiga_serial_driver = { +/* + * amiga_serial_remove() lives in .exit.text. For drivers registered via + * module_platform_driver_probe() this is ok because they cannot get unbound at + * runtime. So mark the driver struct with __refdata to prevent modpost + * triggering a section mismatch warning. + */ +static struct platform_driver amiga_serial_driver __refdata = { .remove_new = __exit_p(amiga_serial_remove), .driver = { .name = "amiga-serial", From 7fb96133a76a14a970219bd2016c8fcc647010c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 29 Mar 2024 22:54:43 +0100 Subject: [PATCH 031/113] serial: pmac_zilog: Drop usage of platform_driver_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are considerations to drop platform_driver_probe() as a concept that isn't relevant any more today. It comes with an added complexity that makes many users hold it wrong. (E.g. this driver should have marked the driver struct with __refdata to prevent the below mentioned false positive section mismatch warning.) This fixes a W=1 build warning: WARNING: modpost: drivers/tty/serial/pmac_zilog: section mismatch in reference: pmz_driver+0x8 (section: .data) -> pmz_detach (section: .exit.text) Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/5ea3174616abc9fa256f115b4fb175d289ac1754.1711748999.git.u.kleine-koenig@pengutronix.de Tested-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pmac_zilog.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 63bc726273fd..b6909dcb137d 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1695,7 +1695,7 @@ static void pmz_dispose_port(struct uart_pmac_port *uap) memset(uap, 0, sizeof(struct uart_pmac_port)); } -static int __init pmz_attach(struct platform_device *pdev) +static int pmz_attach(struct platform_device *pdev) { struct uart_pmac_port *uap; int i; @@ -1714,7 +1714,7 @@ static int __init pmz_attach(struct platform_device *pdev) return uart_add_one_port(&pmz_uart_reg, &uap->port); } -static void __exit pmz_detach(struct platform_device *pdev) +static void pmz_detach(struct platform_device *pdev) { struct uart_pmac_port *uap = platform_get_drvdata(pdev); @@ -1789,7 +1789,8 @@ static struct macio_driver pmz_driver = { #else static struct platform_driver pmz_driver = { - .remove_new = __exit_p(pmz_detach), + .probe = pmz_attach, + .remove_new = pmz_detach, .driver = { .name = "scc", }, @@ -1837,7 +1838,7 @@ static int __init init_pmz(void) #ifdef CONFIG_PPC_PMAC return macio_register_driver(&pmz_driver); #else - return platform_driver_probe(&pmz_driver, pmz_attach); + return platform_driver_register(&pmz_driver); #endif } From 8c278ade67bdcd9882d32547015a091fb16132b1 Mon Sep 17 00:00:00 2001 From: Kanak Shilledar Date: Fri, 5 Apr 2024 13:32:32 +0530 Subject: [PATCH 032/113] dt-bindings: serial: actions,owl-uart: convert to dtschema Convert the Actions Semi Owl UART to newer DT schema. Created DT schema based on the .txt file which had `compatible`, `reg` and `interrupts` as the required properties. This binding is used by Actions S500, S700 and S900 SoC. S700 and S900 use the same UART compatible string. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Kanak Shilledar Link: https://lore.kernel.org/r/20240405080235.11563-1-kanakshilledar@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/actions,owl-uart.txt | 16 ------- .../bindings/serial/actions,owl-uart.yaml | 48 +++++++++++++++++++ 2 files changed, 48 insertions(+), 16 deletions(-) delete mode 100644 Documentation/devicetree/bindings/serial/actions,owl-uart.txt create mode 100644 Documentation/devicetree/bindings/serial/actions,owl-uart.yaml diff --git a/Documentation/devicetree/bindings/serial/actions,owl-uart.txt b/Documentation/devicetree/bindings/serial/actions,owl-uart.txt deleted file mode 100644 index aa873eada02d..000000000000 --- a/Documentation/devicetree/bindings/serial/actions,owl-uart.txt +++ /dev/null @@ -1,16 +0,0 @@ -Actions Semi Owl UART - -Required properties: -- compatible : "actions,s500-uart", "actions,owl-uart" for S500 - "actions,s900-uart", "actions,owl-uart" for S900 -- reg : Offset and length of the register set for the device. -- interrupts : Should contain UART interrupt. - - -Example: - - uart3: serial@b0126000 { - compatible = "actions,s500-uart", "actions,owl-uart"; - reg = <0xb0126000 0x1000>; - interrupts = ; - }; diff --git a/Documentation/devicetree/bindings/serial/actions,owl-uart.yaml b/Documentation/devicetree/bindings/serial/actions,owl-uart.yaml new file mode 100644 index 000000000000..ab1c4514ae93 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/actions,owl-uart.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/actions,owl-uart.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Actions Semi Owl UART + +maintainers: + - Kanak Shilledar + +allOf: + - $ref: serial.yaml + +properties: + compatible: + items: + - enum: + - actions,s500-uart + - actions,s900-uart + - const: actions,owl-uart + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + #include + uart0: serial@b0126000 { + compatible = "actions,s500-uart", "actions,owl-uart"; + reg = <0xb0126000 0x1000>; + clocks = <&cmu CLK_UART0>; + interrupts = ; + }; From 32f6ec282fb0ddb45da1a19145ef9eeef088968d Mon Sep 17 00:00:00 2001 From: Bastien Curutchet Date: Fri, 5 Apr 2024 14:05:52 +0200 Subject: [PATCH 033/113] serial: 8250_of: Add clock_notifier The UART's input clock rate can change at runtime but this is not handled by the driver. Add a clock_notifier callback that updates the divisors when the input clock is updated. The serial8250_update_uartclk() is used to do so. PRE_RATE_CHANGE and ABORT_RATE_CHANGE notifications are ignored, only the POST_RATE_CHANGE is used. Not using PRE_RATE_CHANGE notification can result in a few corrupted bytes during frequency transitions but, IMHO, it can be acceptable in many use cases. It has been tested on a DAVINCI/OMAP-L138 processor. Signed-off-by: Bastien Curutchet Reviewed-by: Herve Codina Link: https://lore.kernel.org/r/20240405120552.35991-1-bastien.curutchet@bootlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_of.c | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 5d1dd992d8fb..e14f47ef1172 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "8250.h" @@ -26,6 +27,7 @@ struct of_serial_info { struct reset_control *rst; int type; int line; + struct notifier_block clk_notifier; }; /* Nuvoton NPCM timeout register */ @@ -58,6 +60,26 @@ static int npcm_setup(struct uart_port *port) return 0; } +static inline struct of_serial_info *clk_nb_to_info(struct notifier_block *nb) +{ + return container_of(nb, struct of_serial_info, clk_notifier); +} + +static int of_platform_serial_clk_notifier_cb(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct of_serial_info *info = clk_nb_to_info(nb); + struct uart_8250_port *port8250 = serial8250_get_port(info->line); + struct clk_notifier_data *ndata = data; + + if (event == POST_RATE_CHANGE) { + serial8250_update_uartclk(&port8250->port, ndata->new_rate); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + /* * Fill a struct uart_port for a given device node */ @@ -218,7 +240,19 @@ static int of_platform_serial_probe(struct platform_device *ofdev) info->type = port_type; info->line = ret; platform_set_drvdata(ofdev, info); + + if (info->clk) { + info->clk_notifier.notifier_call = of_platform_serial_clk_notifier_cb; + ret = clk_notifier_register(info->clk, &info->clk_notifier); + if (ret) { + dev_err_probe(port8250.port.dev, ret, "Failed to set the clock notifier\n"); + goto err_unregister; + } + } + return 0; +err_unregister: + serial8250_unregister_port(info->line); err_dispose: pm_runtime_put_sync(&ofdev->dev); pm_runtime_disable(&ofdev->dev); @@ -234,6 +268,9 @@ static void of_platform_serial_remove(struct platform_device *ofdev) { struct of_serial_info *info = platform_get_drvdata(ofdev); + if (info->clk) + clk_notifier_unregister(info->clk, &info->clk_notifier); + serial8250_unregister_port(info->line); reset_control_assert(info->rst); From 77ab53371a2066fdf9b895246505f5ef5a4b5d47 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 2 Apr 2024 22:50:28 +0300 Subject: [PATCH 034/113] serial: max3100: Lock port->lock when calling uart_handle_cts_change() uart_handle_cts_change() has to be called with port lock taken, Since we run it in a separate work, the lock may not be taken at the time of running. Make sure that it's taken by explicitly doing that. Without it we got a splat: WARNING: CPU: 0 PID: 10 at drivers/tty/serial/serial_core.c:3491 uart_handle_cts_change+0xa6/0xb0 ... Workqueue: max3100-0 max3100_work [max3100] RIP: 0010:uart_handle_cts_change+0xa6/0xb0 ... max3100_handlerx+0xc5/0x110 [max3100] max3100_work+0x12a/0x340 [max3100] Fixes: 7831d56b0a35 ("tty: MAX3100") Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240402195306.269276-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index b83eee37c17d..53dde3391abf 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -213,7 +213,7 @@ static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx) return 0; } -static int max3100_handlerx(struct max3100_port *s, u16 rx) +static int max3100_handlerx_unlocked(struct max3100_port *s, u16 rx) { unsigned int status = 0; int ret = 0, cts; @@ -254,6 +254,17 @@ static int max3100_handlerx(struct max3100_port *s, u16 rx) return ret; } +static int max3100_handlerx(struct max3100_port *s, u16 rx) +{ + unsigned long flags; + int ret; + + uart_port_lock_irqsave(&s->port, &flags); + ret = max3100_handlerx_unlocked(s, rx); + uart_port_unlock_irqrestore(&s->port, flags); + return ret; +} + static void max3100_work(struct work_struct *w) { struct max3100_port *s = container_of(w, struct max3100_port, work); From 712a1fcb38dc7cac6da63ee79a88708fbf9c45ec Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 2 Apr 2024 22:50:29 +0300 Subject: [PATCH 035/113] serial: max3100: Update uart_driver_registered on driver removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The removal of the last MAX3100 device triggers the removal of the driver. However, code doesn't update the respective global variable and after insmod — rmmod — insmod cycle the kernel oopses: max3100 spi-PRP0001:01: max3100_probe: adding port 0 BUG: kernel NULL pointer dereference, address: 0000000000000408 ... RIP: 0010:serial_core_register_port+0xa0/0x840 ... max3100_probe+0x1b6/0x280 [max3100] spi_probe+0x8d/0xb0 Update the actual state so next time UART driver will be registered again. Hugo also noticed, that the error path in the probe also affected by having the variable set, and not cleared. Instead of clearing it move the assignment after the successfull uart_register_driver() call. Fixes: 7831d56b0a35 ("tty: MAX3100") Signed-off-by: Andy Shevchenko Reviewed-by: Hugo Villeneuve Link: https://lore.kernel.org/r/20240402195306.269276-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 53dde3391abf..14873576e52d 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -749,13 +749,14 @@ static int max3100_probe(struct spi_device *spi) mutex_lock(&max3100s_lock); if (!uart_driver_registered) { - uart_driver_registered = 1; retval = uart_register_driver(&max3100_uart_driver); if (retval) { printk(KERN_ERR "Couldn't register max3100 uart driver\n"); mutex_unlock(&max3100s_lock); return retval; } + + uart_driver_registered = 1; } for (i = 0; i < MAX_MAX3100; i++) @@ -841,6 +842,7 @@ static void max3100_remove(struct spi_device *spi) } pr_debug("removing max3100 driver\n"); uart_unregister_driver(&max3100_uart_driver); + uart_driver_registered = 0; mutex_unlock(&max3100s_lock); } From e60955dbecb97f080848a57524827e2db29c70fd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 2 Apr 2024 22:50:30 +0300 Subject: [PATCH 036/113] serial: max3100: Fix bitwise types Sparse is not happy about misuse of bitwise types: .../max3100.c:194:13: warning: incorrect type in assignment (different base types) .../max3100.c:194:13: expected unsigned short [addressable] [usertype] etx .../max3100.c:194:13: got restricted __be16 [usertype] .../max3100.c:202:15: warning: cast to restricted __be16 Fix this by choosing proper types for the respective variables. Fixes: 7831d56b0a35 ("tty: MAX3100") Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240402195306.269276-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 14873576e52d..8c6eaf908bce 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -45,6 +45,9 @@ #include #include #include +#include + +#include #include @@ -191,7 +194,7 @@ static void max3100_timeout(struct timer_list *t) static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx) { struct spi_message message; - u16 etx, erx; + __be16 etx, erx; int status; struct spi_transfer tran = { .tx_buf = &etx, From 0487724912abc14eb0e95c352a29e6691a733631 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 2 Apr 2024 22:50:31 +0300 Subject: [PATCH 037/113] serial: max3100: Make struct plat_max3100 local There is no user of the struct plat_max3100 outside the driver. Inline its contents into the driver. While at it, drop outdated example in the comment. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240402195306.269276-5-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 38 +++++++++++++-------------- include/linux/serial_max3100.h | 48 ---------------------------------- 2 files changed, 18 insertions(+), 68 deletions(-) delete mode 100644 include/linux/serial_max3100.h diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 8c6eaf908bce..0e30201e9689 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * * Copyright (C) 2008 Christian Pellegrin * * Notes: the MAX3100 doesn't provide an interrupt on CTS so we have @@ -8,24 +7,6 @@ * writing conf clears FIFO buffer and we cannot have this interrupt * always asking us for attention. * - * Example platform data: - - static struct plat_max3100 max3100_plat_data = { - .loopback = 0, - .crystal = 0, - .poll_time = 100, - }; - - static struct spi_board_info spi_board_info[] = { - { - .modalias = "max3100", - .platform_data = &max3100_plat_data, - .irq = IRQ_EINT12, - .max_speed_hz = 5*1000*1000, - .chip_select = 0, - }, - }; - * The initial minor number is 209 in the low-density serial port: * mknod /dev/ttyMAX0 c 204 209 */ @@ -49,7 +30,24 @@ #include -#include +/** + * struct plat_max3100 - MAX3100 SPI UART platform data + * @loopback: force MAX3100 in loopback + * @crystal: 1 for 3.6864 Mhz, 0 for 1.8432 + * @max3100_hw_suspend: MAX3100 has a shutdown pin. This is a hook + * called on suspend and resume to activate it. + * @poll_time: poll time for CTS signal in ms, 0 disables (so no hw + * flow ctrl is possible but you have less CPU usage) + * + * You should use this structure in your machine description to specify + * how the MAX3100 is connected. + */ +struct plat_max3100 { + int loopback; + int crystal; + void (*max3100_hw_suspend) (int suspend); + int poll_time; +}; #define MAX3100_C (1<<14) #define MAX3100_D (0<<14) diff --git a/include/linux/serial_max3100.h b/include/linux/serial_max3100.h deleted file mode 100644 index befd55c08a7c..000000000000 --- a/include/linux/serial_max3100.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * - * Copyright (C) 2007 Christian Pellegrin - */ - - -#ifndef _LINUX_SERIAL_MAX3100_H -#define _LINUX_SERIAL_MAX3100_H 1 - - -/** - * struct plat_max3100 - MAX3100 SPI UART platform data - * @loopback: force MAX3100 in loopback - * @crystal: 1 for 3.6864 Mhz, 0 for 1.8432 - * @max3100_hw_suspend: MAX3100 has a shutdown pin. This is a hook - * called on suspend and resume to activate it. - * @poll_time: poll time for CTS signal in ms, 0 disables (so no hw - * flow ctrl is possible but you have less CPU usage) - * - * You should use this structure in your machine description to specify - * how the MAX3100 is connected. Example: - * - * static struct plat_max3100 max3100_plat_data = { - * .loopback = 0, - * .crystal = 0, - * .poll_time = 100, - * }; - * - * static struct spi_board_info spi_board_info[] = { - * { - * .modalias = "max3100", - * .platform_data = &max3100_plat_data, - * .irq = IRQ_EINT12, - * .max_speed_hz = 5*1000*1000, - * .chip_select = 0, - * }, - * }; - * - **/ -struct plat_max3100 { - int loopback; - int crystal; - void (*max3100_hw_suspend) (int suspend); - int poll_time; -}; - -#endif From 3c37ac45718ebb25203314c908dc4b5c5b9b2119 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 2 Apr 2024 22:50:32 +0300 Subject: [PATCH 038/113] serial: max3100: Remove custom HW shutdown support While there is no user of this callback in the kernel, it also breaks the relationship in the driver model. The correct implementation should be done via GPIO or regulator framework. Remove custom HW shutdown support for good and, if needed, we will implement it correctly later on. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240402195306.269276-6-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 42 ++++++------------------------------ 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 0e30201e9689..ad6b692c7360 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -34,8 +34,6 @@ * struct plat_max3100 - MAX3100 SPI UART platform data * @loopback: force MAX3100 in loopback * @crystal: 1 for 3.6864 Mhz, 0 for 1.8432 - * @max3100_hw_suspend: MAX3100 has a shutdown pin. This is a hook - * called on suspend and resume to activate it. * @poll_time: poll time for CTS signal in ms, 0 disables (so no hw * flow ctrl is possible but you have less CPU usage) * @@ -45,7 +43,6 @@ struct plat_max3100 { int loopback; int crystal; - void (*max3100_hw_suspend) (int suspend); int poll_time; }; @@ -125,9 +122,6 @@ struct max3100_port { /* need to know we are suspending to avoid deadlock on workqueue */ int suspending; - /* hook for suspending MAX3100 via dedicated pin */ - void (*max3100_hw_suspend) (int suspend); - /* poll time (in ms) for ctrl lines */ int poll_time; /* and its timer */ @@ -553,6 +547,7 @@ static void max3100_shutdown(struct uart_port *port) struct max3100_port *s = container_of(port, struct max3100_port, port); + u16 rx; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -572,14 +567,7 @@ static void max3100_shutdown(struct uart_port *port) free_irq(s->irq, s); /* set shutdown mode to save power */ - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(1); - else { - u16 tx, rx; - - tx = MAX3100_WC | MAX3100_SHDN; - max3100_sr(s, tx, &rx); - } + max3100_sr(s, MAX3100_WC | MAX3100_SHDN, &rx); } static int max3100_startup(struct uart_port *port) @@ -625,8 +613,6 @@ static int max3100_startup(struct uart_port *port) max3100_sr(s, tx, &rx); } - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(0); s->conf_commit = 1; max3100_dowork(s); /* wait for clock to settle */ @@ -745,7 +731,7 @@ static int max3100_probe(struct spi_device *spi) { int i, retval; struct plat_max3100 *pdata; - u16 tx, rx; + u16 rx; mutex_lock(&max3100s_lock); @@ -786,7 +772,6 @@ static int max3100_probe(struct spi_device *spi) max3100s[i]->poll_time = msecs_to_jiffies(pdata->poll_time); if (pdata->poll_time > 0 && max3100s[i]->poll_time == 0) max3100s[i]->poll_time = 1; - max3100s[i]->max3100_hw_suspend = pdata->max3100_hw_suspend; max3100s[i]->minor = i; timer_setup(&max3100s[i]->timer, max3100_timeout, 0); @@ -806,12 +791,7 @@ static int max3100_probe(struct spi_device *spi) i, retval); /* set shutdown mode to save power. Will be woken-up on open */ - if (max3100s[i]->max3100_hw_suspend) - max3100s[i]->max3100_hw_suspend(1); - else { - tx = MAX3100_WC | MAX3100_SHDN; - max3100_sr(max3100s[i], tx, &rx); - } + max3100_sr(max3100s[i], MAX3100_WC | MAX3100_SHDN, &rx); mutex_unlock(&max3100s_lock); return 0; } @@ -853,6 +833,7 @@ static void max3100_remove(struct spi_device *spi) static int max3100_suspend(struct device *dev) { struct max3100_port *s = dev_get_drvdata(dev); + u16 rx; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -861,15 +842,8 @@ static int max3100_suspend(struct device *dev) s->suspending = 1; uart_suspend_port(&max3100_uart_driver, &s->port); - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(1); - else { - /* no HW suspend, so do SW one */ - u16 tx, rx; - - tx = MAX3100_WC | MAX3100_SHDN; - max3100_sr(s, tx, &rx); - } + /* no HW suspend, so do SW one */ + max3100_sr(s, MAX3100_WC | MAX3100_SHDN, &rx); return 0; } @@ -879,8 +853,6 @@ static int max3100_resume(struct device *dev) dev_dbg(&s->spi->dev, "%s\n", __func__); - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(0); uart_resume_port(&max3100_uart_driver, &s->port); s->suspending = 0; From e1cb4fa90fb8edbb7f47aca2328fc6425bcc582f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 2 Apr 2024 22:50:33 +0300 Subject: [PATCH 039/113] serial: max3100: Replace custom polling timeout with standard one Serial core provides a standard timeout for polling modem state. Use it instead of a custom approach. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240402195306.269276-7-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index ad6b692c7360..285675c3a848 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -34,8 +34,6 @@ * struct plat_max3100 - MAX3100 SPI UART platform data * @loopback: force MAX3100 in loopback * @crystal: 1 for 3.6864 Mhz, 0 for 1.8432 - * @poll_time: poll time for CTS signal in ms, 0 disables (so no hw - * flow ctrl is possible but you have less CPU usage) * * You should use this structure in your machine description to specify * how the MAX3100 is connected. @@ -43,7 +41,6 @@ struct plat_max3100 { int loopback; int crystal; - int poll_time; }; #define MAX3100_C (1<<14) @@ -122,9 +119,6 @@ struct max3100_port { /* need to know we are suspending to avoid deadlock on workqueue */ int suspending; - /* poll time (in ms) for ctrl lines */ - int poll_time; - /* and its timer */ struct timer_list timer; }; @@ -177,10 +171,8 @@ static void max3100_timeout(struct timer_list *t) { struct max3100_port *s = from_timer(s, t, timer); - if (s->port.state) { - max3100_dowork(s); - mod_timer(&s->timer, jiffies + s->poll_time); - } + max3100_dowork(s); + mod_timer(&s->timer, jiffies + uart_poll_timeout(&s->port)); } static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx) @@ -342,8 +334,7 @@ static void max3100_enable_ms(struct uart_port *port) struct max3100_port, port); - if (s->poll_time > 0) - mod_timer(&s->timer, jiffies); + mod_timer(&s->timer, jiffies); dev_dbg(&s->spi->dev, "%s\n", __func__); } @@ -526,9 +517,7 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, MAX3100_STATUS_PE | MAX3100_STATUS_FE | MAX3100_STATUS_OE; - if (s->poll_time > 0) - del_timer_sync(&s->timer); - + del_timer_sync(&s->timer); uart_update_timeout(port, termios->c_cflag, baud); spin_lock(&s->conf_lock); @@ -556,8 +545,7 @@ static void max3100_shutdown(struct uart_port *port) s->force_end_work = 1; - if (s->poll_time > 0) - del_timer_sync(&s->timer); + del_timer_sync(&s->timer); if (s->workqueue) { destroy_workqueue(s->workqueue); @@ -769,9 +757,6 @@ static int max3100_probe(struct spi_device *spi) pdata = dev_get_platdata(&spi->dev); max3100s[i]->crystal = pdata->crystal; max3100s[i]->loopback = pdata->loopback; - max3100s[i]->poll_time = msecs_to_jiffies(pdata->poll_time); - if (pdata->poll_time > 0 && max3100s[i]->poll_time == 0) - max3100s[i]->poll_time = 1; max3100s[i]->minor = i; timer_setup(&max3100s[i]->timer, max3100_timeout, 0); From 80949ca0f3a89bc046b6a84ec2fcc2b3a4241a93 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 2 Apr 2024 22:50:41 +0300 Subject: [PATCH 040/113] serial: max3100: Remove unneeded forward declaration There is no code using max3100_work() before the definition of it. Remove unneeded forward declaration. While at it, move max3100_dowork() and max3100_timeout() down in the code to be after actual max3100_work() implementation. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240402195306.269276-15-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 285675c3a848..75e96ff74571 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -159,22 +159,6 @@ static void max3100_calc_parity(struct max3100_port *s, u16 *c) *c |= max3100_do_parity(s, *c) << 8; } -static void max3100_work(struct work_struct *w); - -static void max3100_dowork(struct max3100_port *s) -{ - if (!s->force_end_work && !freezing(current) && !s->suspending) - queue_work(s->workqueue, &s->work); -} - -static void max3100_timeout(struct timer_list *t) -{ - struct max3100_port *s = from_timer(s, t, timer); - - max3100_dowork(s); - mod_timer(&s->timer, jiffies + uart_poll_timeout(&s->port)); -} - static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx) { struct spi_message message; @@ -318,6 +302,20 @@ static void max3100_work(struct work_struct *w) tty_flip_buffer_push(&s->port.state->port); } +static void max3100_dowork(struct max3100_port *s) +{ + if (!s->force_end_work && !freezing(current) && !s->suspending) + queue_work(s->workqueue, &s->work); +} + +static void max3100_timeout(struct timer_list *t) +{ + struct max3100_port *s = from_timer(s, t, timer); + + max3100_dowork(s); + mod_timer(&s->timer, jiffies + uart_poll_timeout(&s->port)); +} + static irqreturn_t max3100_irq(int irqno, void *dev_id) { struct max3100_port *s = dev_id; From 0867a9805549632969fbb996c50ba8adb1bc38bf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 2 Apr 2024 22:50:43 +0300 Subject: [PATCH 041/113] serial: max3100: Update Kconfig entry The driver actually supports more than one chip. Update Kconfig entry to list the supported chips. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240402195306.269276-17-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index ffcf4882b25f..5c80505cdf41 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -307,11 +307,14 @@ config SERIAL_TEGRA_TCU_CONSOLE If unsure, say Y. config SERIAL_MAX3100 - tristate "MAX3100 support" + tristate "MAX3100/3110/3111/3222 support" depends on SPI select SERIAL_CORE help - MAX3100 chip support + This selects support for an advanced UART from Maxim. + Supported ICs are MAX3100, MAX3110, MAX3111, MAX3222. + + Say Y here if you want to support these ICs. config SERIAL_MAX310X tristate "MAX310X support" From c9615d34ce26199054e188f634eac2e45c9cc906 Mon Sep 17 00:00:00 2001 From: wangkaiyuan Date: Mon, 18 Mar 2024 14:40:36 +0800 Subject: [PATCH 042/113] tty: serial: max310x: convert to use maple tree register cache The maple tree register cache is based on a much more modern data structure than the rbtree cache and makes optimisation choices which are probably more appropriate for modern systems than those made by the rbtree cache. Signed-off-by: wangkaiyuan Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240318064036.1656-1-wangkaiyuan@inspur.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index f0eb96429dae..35369a2f77b2 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1473,7 +1473,7 @@ static struct regmap_config regcfg = { .reg_bits = 8, .val_bits = 8, .write_flag_mask = MAX310X_WRITE_BIT, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .max_register = MAX310X_REG_1F, .writeable_reg = max310x_reg_writeable, .volatile_reg = max310x_reg_volatile, @@ -1577,7 +1577,7 @@ static int max310x_i2c_extended_reg_enable(struct device *dev, bool enable) static struct regmap_config regcfg_i2c = { .reg_bits = 8, .val_bits = 8, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .writeable_reg = max310x_reg_writeable, .volatile_reg = max310x_reg_volatile, .precious_reg = max310x_reg_precious, From 925b2c3f6b6f81a5405a647b381d12a93f06a730 Mon Sep 17 00:00:00 2001 From: wangkaiyuan Date: Mon, 18 Mar 2024 14:42:16 +0800 Subject: [PATCH 043/113] tty: serial: sc16is7xx: convert to use maple tree register cache The maple tree register cache is based on a much more modern data structure than the rbtree cache and makes optimisation choices which are probably more appropriate for modern systems than those made by the rbtree cache. Signed-off-by: wangkaiyuan Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240318064216.1765-1-wangkaiyuan@inspur.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index c6983b7bd78c..07aca9fc6d97 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1686,7 +1686,7 @@ static struct regmap_config regcfg = { .reg_bits = 5, .pad_bits = 3, .val_bits = 8, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = sc16is7xx_regmap_volatile, .precious_reg = sc16is7xx_regmap_precious, .writeable_noinc_reg = sc16is7xx_regmap_noinc, From 771d22bce79eec425c011fdf95a431a4a0b47c16 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 9 Apr 2024 17:45:48 +0300 Subject: [PATCH 044/113] serial: max3100: Enable TIOCM_LOOP Enable or disable loopback at run-time. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240409144721.638326-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 75e96ff74571..4a4343e7b1fa 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -32,14 +32,12 @@ /** * struct plat_max3100 - MAX3100 SPI UART platform data - * @loopback: force MAX3100 in loopback * @crystal: 1 for 3.6864 Mhz, 0 for 1.8432 * * You should use this structure in your machine description to specify * how the MAX3100 is connected. */ struct plat_max3100 { - int loopback; int crystal; }; @@ -109,6 +107,7 @@ struct max3100_port { int minor; /* minor number */ int crystal; /* 1 if 3.6864Mhz crystal 0 for 1.8432 */ + int loopback_commit; /* need to change loopback */ int loopback; /* 1 if we are in loopback mode */ /* for handling irqs: need workqueue since we do spi_sync */ @@ -241,9 +240,9 @@ static void max3100_work(struct work_struct *w) struct max3100_port *s = container_of(w, struct max3100_port, work); struct tty_port *tport = &s->port.state->port; unsigned char ch; + int conf, cconf, cloopback, crts; int rxchars; u16 tx, rx; - int conf, cconf, crts; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -253,11 +252,15 @@ static void max3100_work(struct work_struct *w) conf = s->conf; cconf = s->conf_commit; s->conf_commit = 0; + cloopback = s->loopback_commit; + s->loopback_commit = 0; crts = s->rts_commit; s->rts_commit = 0; spin_unlock(&s->conf_lock); if (cconf) max3100_sr(s, MAX3100_WC | conf, &rx); + if (cloopback) + max3100_sr(s, 0x4001, &rx); if (crts) { max3100_sr(s, MAX3100_WD | MAX3100_TE | (s->rts ? MAX3100_RTS : 0), &rx); @@ -395,18 +398,24 @@ static void max3100_set_mctrl(struct uart_port *port, unsigned int mctrl) struct max3100_port *s = container_of(port, struct max3100_port, port); - int rts; + int loopback, rts; dev_dbg(&s->spi->dev, "%s\n", __func__); + loopback = (mctrl & TIOCM_LOOP) > 0; rts = (mctrl & TIOCM_RTS) > 0; spin_lock(&s->conf_lock); + if (s->loopback != loopback) { + s->loopback = loopback; + s->loopback_commit = 1; + } if (s->rts != rts) { s->rts = rts; s->rts_commit = 1; - max3100_dowork(s); } + if (s->loopback_commit || s->rts_commit) + max3100_dowork(s); spin_unlock(&s->conf_lock); } @@ -593,12 +602,6 @@ static int max3100_startup(struct uart_port *port) return -EBUSY; } - if (s->loopback) { - u16 tx, rx; - tx = 0x4001; - max3100_sr(s, tx, &rx); - } - s->conf_commit = 1; max3100_dowork(s); /* wait for clock to settle */ @@ -754,7 +757,6 @@ static int max3100_probe(struct spi_device *spi) spi_set_drvdata(spi, max3100s[i]); pdata = dev_get_platdata(&spi->dev); max3100s[i]->crystal = pdata->crystal; - max3100s[i]->loopback = pdata->loopback; max3100s[i]->minor = i; timer_setup(&max3100s[i]->timer, max3100_timeout, 0); From 61f538f23a7a83fabfb1fb5ce7ab6f2c75e911f2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 9 Apr 2024 17:45:49 +0300 Subject: [PATCH 045/113] serial: max3100: Get crystal frequency via device property Get crystal frequency via device property instead of using a platform data. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240409144721.638326-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 49 +++++++++++++++--------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 4a4343e7b1fa..446bc78061e3 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -30,17 +31,6 @@ #include -/** - * struct plat_max3100 - MAX3100 SPI UART platform data - * @crystal: 1 for 3.6864 Mhz, 0 for 1.8432 - * - * You should use this structure in your machine description to specify - * how the MAX3100 is connected. - */ -struct plat_max3100 { - int crystal; -}; - #define MAX3100_C (1<<14) #define MAX3100_D (0<<14) #define MAX3100_W (1<<15) @@ -106,7 +96,6 @@ struct max3100_port { int irq; /* irq assigned to the max3100 */ int minor; /* minor number */ - int crystal; /* 1 if 3.6864Mhz crystal 0 for 1.8432 */ int loopback_commit; /* need to change loopback */ int loopback; /* 1 if we are in loopback mode */ @@ -426,7 +415,8 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, struct max3100_port *s = container_of(port, struct max3100_port, port); - int baud = 0; + unsigned int baud = port->uartclk / 16; + unsigned int baud230400 = (baud == 230400) ? 1 : 0; unsigned cflag; u32 param_new, param_mask, parity = 0; @@ -439,40 +429,40 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, param_new = s->conf & MAX3100_BAUD; switch (baud) { case 300: - if (s->crystal) + if (baud230400) baud = s->baud; else param_new = 15; break; case 600: - param_new = 14 + s->crystal; + param_new = 14 + baud230400; break; case 1200: - param_new = 13 + s->crystal; + param_new = 13 + baud230400; break; case 2400: - param_new = 12 + s->crystal; + param_new = 12 + baud230400; break; case 4800: - param_new = 11 + s->crystal; + param_new = 11 + baud230400; break; case 9600: - param_new = 10 + s->crystal; + param_new = 10 + baud230400; break; case 19200: - param_new = 9 + s->crystal; + param_new = 9 + baud230400; break; case 38400: - param_new = 8 + s->crystal; + param_new = 8 + baud230400; break; case 57600: - param_new = 1 + s->crystal; + param_new = 1 + baud230400; break; case 115200: - param_new = 0 + s->crystal; + param_new = 0 + baud230400; break; case 230400: - if (s->crystal) + if (baud230400) param_new = 0; else baud = s->baud; @@ -575,7 +565,7 @@ static int max3100_startup(struct uart_port *port) dev_dbg(&s->spi->dev, "%s\n", __func__); s->conf = MAX3100_RM; - s->baud = s->crystal ? 230400 : 115200; + s->baud = port->uartclk / 16; s->rx_enabled = 1; if (s->suspending) @@ -718,8 +708,8 @@ static int uart_driver_registered; static int max3100_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; int i, retval; - struct plat_max3100 *pdata; u16 rx; mutex_lock(&max3100s_lock); @@ -755,20 +745,21 @@ static int max3100_probe(struct spi_device *spi) max3100s[i]->irq = spi->irq; spin_lock_init(&max3100s[i]->conf_lock); spi_set_drvdata(spi, max3100s[i]); - pdata = dev_get_platdata(&spi->dev); - max3100s[i]->crystal = pdata->crystal; max3100s[i]->minor = i; timer_setup(&max3100s[i]->timer, max3100_timeout, 0); dev_dbg(&spi->dev, "%s: adding port %d\n", __func__, i); max3100s[i]->port.irq = max3100s[i]->irq; - max3100s[i]->port.uartclk = max3100s[i]->crystal ? 3686400 : 1843200; max3100s[i]->port.fifosize = 16; max3100s[i]->port.ops = &max3100_ops; max3100s[i]->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; max3100s[i]->port.line = i; max3100s[i]->port.type = PORT_MAX3100; max3100s[i]->port.dev = &spi->dev; + + /* Read clock frequency from a property, uart_add_one_port() will fail if it's not set */ + device_property_read_u32(dev, "clock-frequency", &max3100s[i]->port.uartclk); + retval = uart_add_one_port(&max3100_uart_driver, &max3100s[i]->port); if (retval < 0) dev_warn(&spi->dev, From 8c15f723caba9db359bdabcd54e7dda4c0b19c0b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 9 Apr 2024 17:45:50 +0300 Subject: [PATCH 046/113] serial: max3100: Remove duplicating irq field The struct uart_port has a copy of the IRQ that is also stored in the private data structure. Remove the duplication in the latter one. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240409144721.638326-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 446bc78061e3..e3104ea7a3ca 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -93,8 +93,6 @@ struct max3100_port { #define MAX3100_7BIT 4 int rx_enabled; /* if we should rx chars */ - int irq; /* irq assigned to the max3100 */ - int minor; /* minor number */ int loopback_commit; /* need to change loopback */ int loopback; /* 1 if we are in loopback mode */ @@ -548,8 +546,8 @@ static void max3100_shutdown(struct uart_port *port) destroy_workqueue(s->workqueue); s->workqueue = NULL; } - if (s->irq) - free_irq(s->irq, s); + if (port->irq) + free_irq(port->irq, s); /* set shutdown mode to save power */ max3100_sr(s, MAX3100_WC | MAX3100_SHDN, &rx); @@ -561,6 +559,7 @@ static int max3100_startup(struct uart_port *port) struct max3100_port, port); char b[12]; + int ret; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -583,10 +582,10 @@ static int max3100_startup(struct uart_port *port) } INIT_WORK(&s->work, max3100_work); - if (request_irq(s->irq, max3100_irq, - IRQF_TRIGGER_FALLING, "max3100", s) < 0) { - dev_warn(&s->spi->dev, "cannot allocate irq %d\n", s->irq); - s->irq = 0; + ret = request_irq(port->irq, max3100_irq, IRQF_TRIGGER_FALLING, "max3100", s); + if (ret < 0) { + dev_warn(&s->spi->dev, "cannot allocate irq %d\n", port->irq); + port->irq = 0; destroy_workqueue(s->workqueue); s->workqueue = NULL; return -EBUSY; @@ -742,14 +741,13 @@ static int max3100_probe(struct spi_device *spi) return -ENOMEM; } max3100s[i]->spi = spi; - max3100s[i]->irq = spi->irq; spin_lock_init(&max3100s[i]->conf_lock); spi_set_drvdata(spi, max3100s[i]); max3100s[i]->minor = i; timer_setup(&max3100s[i]->timer, max3100_timeout, 0); dev_dbg(&spi->dev, "%s: adding port %d\n", __func__, i); - max3100s[i]->port.irq = max3100s[i]->irq; + max3100s[i]->port.irq = spi->irq; max3100s[i]->port.fifosize = 16; max3100s[i]->port.ops = &max3100_ops; max3100s[i]->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; @@ -813,7 +811,7 @@ static int max3100_suspend(struct device *dev) dev_dbg(&s->spi->dev, "%s\n", __func__); - disable_irq(s->irq); + disable_irq(s->port.irq); s->suspending = 1; uart_suspend_port(&max3100_uart_driver, &s->port); @@ -832,7 +830,7 @@ static int max3100_resume(struct device *dev) uart_resume_port(&max3100_uart_driver, &s->port); s->suspending = 0; - enable_irq(s->irq); + enable_irq(s->port.irq); s->conf_commit = 1; if (s->workqueue) From bbcbf739215eb8bfae346b9738bf5d209b434604 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 9 Apr 2024 17:45:51 +0300 Subject: [PATCH 047/113] serial: max3100: Switch to use dev_err_probe() Switch to use dev_err_probe() to simplify the error path and unify a message template. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240409144721.638326-5-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index e3104ea7a3ca..09ec2ca06989 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -716,9 +716,8 @@ static int max3100_probe(struct spi_device *spi) if (!uart_driver_registered) { retval = uart_register_driver(&max3100_uart_driver); if (retval) { - printk(KERN_ERR "Couldn't register max3100 uart driver\n"); mutex_unlock(&max3100s_lock); - return retval; + return dev_err_probe(dev, retval, "Couldn't register max3100 uart driver\n"); } uart_driver_registered = 1; @@ -728,15 +727,12 @@ static int max3100_probe(struct spi_device *spi) if (!max3100s[i]) break; if (i == MAX_MAX3100) { - dev_warn(&spi->dev, "too many MAX3100 chips\n"); mutex_unlock(&max3100s_lock); - return -ENOMEM; + return dev_err_probe(dev, -ENOMEM, "too many MAX3100 chips\n"); } max3100s[i] = kzalloc(sizeof(struct max3100_port), GFP_KERNEL); if (!max3100s[i]) { - dev_warn(&spi->dev, - "kmalloc for max3100 structure %d failed!\n", i); mutex_unlock(&max3100s_lock); return -ENOMEM; } @@ -760,9 +756,7 @@ static int max3100_probe(struct spi_device *spi) retval = uart_add_one_port(&max3100_uart_driver, &max3100s[i]->port); if (retval < 0) - dev_warn(&spi->dev, - "uart_add_one_port failed for line %d with error %d\n", - i, retval); + dev_err_probe(dev, retval, "uart_add_one_port failed for line %d\n", i); /* set shutdown mode to save power. Will be woken-up on open */ max3100_sr(max3100s[i], MAX3100_WC | MAX3100_SHDN, &rx); From 8250b1c1fdf617e953511289b46c8a77d4d4457c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 9 Apr 2024 17:45:52 +0300 Subject: [PATCH 048/113] serial: max3100: Replace MODULE_ALIAS() with respective ID tables MODULE_ALIAS() in most cases is a pure hack to avoid placing ID tables. Replace it with the respective ID tables. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240409144721.638326-6-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 09ec2ca06989..3004a95e573f 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -840,13 +841,27 @@ static SIMPLE_DEV_PM_OPS(max3100_pm_ops, max3100_suspend, max3100_resume); #define MAX3100_PM_OPS NULL #endif +static const struct spi_device_id max3100_spi_id[] = { + { "max3100" }, + { } +}; +MODULE_DEVICE_TABLE(spi, max3100_spi_id); + +static const struct of_device_id max3100_of_match[] = { + { .compatible = "maxim,max3100" }, + { } +}; +MODULE_DEVICE_TABLE(of, max3100_of_match); + static struct spi_driver max3100_driver = { .driver = { .name = "max3100", + .of_match_table = max3100_of_match, .pm = MAX3100_PM_OPS, }, .probe = max3100_probe, .remove = max3100_remove, + .id_table = max3100_spi_id, }; module_spi_driver(max3100_driver); @@ -854,4 +869,3 @@ module_spi_driver(max3100_driver); MODULE_DESCRIPTION("MAX3100 driver"); MODULE_AUTHOR("Christian Pellegrin "); MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:max3100"); From 1d01740efb6856cf02905a84bf04b70d223993c2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 9 Apr 2024 17:45:53 +0300 Subject: [PATCH 049/113] serial: max3100: Switch to DEFINE_SIMPLE_DEV_PM_OPS() The SIMPLE_DEV_PM_OPS() is deprecated, replace it with the DEFINE_SIMPLE_DEV_PM_OPS() and use pm_sleep_ptr() for setting the driver's PM routines. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240409144721.638326-7-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 3004a95e573f..db543eaa39c8 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -797,8 +798,6 @@ static void max3100_remove(struct spi_device *spi) mutex_unlock(&max3100s_lock); } -#ifdef CONFIG_PM_SLEEP - static int max3100_suspend(struct device *dev) { struct max3100_port *s = dev_get_drvdata(dev); @@ -834,12 +833,7 @@ static int max3100_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(max3100_pm_ops, max3100_suspend, max3100_resume); -#define MAX3100_PM_OPS (&max3100_pm_ops) - -#else -#define MAX3100_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(max3100_pm_ops, max3100_suspend, max3100_resume); static const struct spi_device_id max3100_spi_id[] = { { "max3100" }, @@ -857,7 +851,7 @@ static struct spi_driver max3100_driver = { .driver = { .name = "max3100", .of_match_table = max3100_of_match, - .pm = MAX3100_PM_OPS, + .pm = pm_sleep_ptr(&max3100_pm_ops), }, .probe = max3100_probe, .remove = max3100_remove, From 69b2cc30315ac48e3a6308a7f736f67c3e3db0b1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 9 Apr 2024 17:45:54 +0300 Subject: [PATCH 050/113] serial: max3100: Extract to_max3100_port() helper macro Instead of using container_of() explicitly, introduce a helper macro. This saves a lot of lines of code. Signed-off-by: Andy Shevchenko Reviewed-by: Hugo Villeneuve Link: https://lore.kernel.org/r/20240409144721.638326-8-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 67 ++++++++++-------------------------- 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index db543eaa39c8..4e7cd037cfc2 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -16,6 +16,7 @@ /* 4 MAX3100s should be enough for everyone */ #define MAX_MAX3100 4 +#include #include #include #include @@ -110,6 +111,8 @@ struct max3100_port { struct timer_list timer; }; +#define to_max3100_port(port) container_of(port, struct max3100_port, port) + static struct max3100_port *max3100s[MAX_MAX3100]; /* the chips */ static DEFINE_MUTEX(max3100s_lock); /* race on probe */ @@ -320,9 +323,7 @@ static irqreturn_t max3100_irq(int irqno, void *dev_id) static void max3100_enable_ms(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); mod_timer(&s->timer, jiffies); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -330,9 +331,7 @@ static void max3100_enable_ms(struct uart_port *port) static void max3100_start_tx(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -341,9 +340,7 @@ static void max3100_start_tx(struct uart_port *port) static void max3100_stop_rx(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -357,9 +354,7 @@ static void max3100_stop_rx(struct uart_port *port) static unsigned int max3100_tx_empty(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -370,9 +365,7 @@ static unsigned int max3100_tx_empty(struct uart_port *port) static unsigned int max3100_get_mctrl(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -384,9 +377,7 @@ static unsigned int max3100_get_mctrl(struct uart_port *port) static void max3100_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); int loopback, rts; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -412,9 +403,7 @@ static void max3100_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); unsigned int baud = port->uartclk / 16; unsigned int baud230400 = (baud == 230400) ? 1 : 0; unsigned cflag; @@ -530,9 +519,7 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, static void max3100_shutdown(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); u16 rx; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -557,9 +544,7 @@ static void max3100_shutdown(struct uart_port *port) static int max3100_startup(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); char b[12]; int ret; @@ -605,9 +590,7 @@ static int max3100_startup(struct uart_port *port) static const char *max3100_type(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -616,18 +599,14 @@ static const char *max3100_type(struct uart_port *port) static void max3100_release_port(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); } static void max3100_config_port(struct uart_port *port, int flags) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -638,9 +617,7 @@ static void max3100_config_port(struct uart_port *port, int flags) static int max3100_verify_port(struct uart_port *port, struct serial_struct *ser) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); int ret = -EINVAL; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -652,18 +629,14 @@ static int max3100_verify_port(struct uart_port *port, static void max3100_stop_tx(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); } static int max3100_request_port(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); return 0; @@ -671,9 +644,7 @@ static int max3100_request_port(struct uart_port *port) static void max3100_break_ctl(struct uart_port *port, int break_state) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); } From 4fe952c141b285ca0b379c93c9c6c69d7240a495 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 9 Apr 2024 17:45:55 +0300 Subject: [PATCH 051/113] serial: max3100: Sort headers Sort the headers in alphabetic order in order to ease the maintenance for this part. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240409144721.638326-9-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 4e7cd037cfc2..ce24588150bf 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -18,18 +18,18 @@ #include #include -#include #include +#include #include #include #include #include #include #include +#include #include -#include -#include #include +#include #include #include From 838022def8ef2b676d6f3c19cad9185ce8046008 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Sun, 7 Apr 2024 02:27:06 +0200 Subject: [PATCH 052/113] serial: amba-pl011: get rid of useless wrapper pl011_get_rs485_mode() Due to earlier code changes function pl011_get_rs485_mode() is now merely a wrapper for uart_get_rs485_mode() which does not add any further functionality. So remove it and instead call uart_get_rs485_mode() directly. Reviewed-by: Lukas Wunner Signed-off-by: Lino Sanfilippo Link: https://lore.kernel.org/r/20240407002709.16224-2-l.sanfilippo@kunbus.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 51b202eb8296..76d2078ec086 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2692,18 +2692,6 @@ static int pl011_find_free_port(void) return -EBUSY; } -static int pl011_get_rs485_mode(struct uart_amba_port *uap) -{ - struct uart_port *port = &uap->port; - int ret; - - ret = uart_get_rs485_mode(port); - if (ret) - return ret; - - return 0; -} - static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap, struct resource *mmiobase, int index) { @@ -2724,7 +2712,7 @@ static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap, uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = index; - ret = pl011_get_rs485_mode(uap); + ret = uart_get_rs485_mode(&uap->port); if (ret) return ret; From 255abd49f18533300bc5f20d7dce77a0a4479f58 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Sun, 7 Apr 2024 02:27:07 +0200 Subject: [PATCH 053/113] serial: amba-pl011: move variable into CONFIG_DMA_ENGINE conditional Variable dmacr is only used if DMA is enabled, so move it into the CONFIG_DMA_ENGINE conditional. Signed-off-by: Lino Sanfilippo Link: https://lore.kernel.org/r/20240407002709.16224-3-l.sanfilippo@kunbus.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 76d2078ec086..8b1644f5411e 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -256,7 +256,6 @@ struct uart_amba_port { const u16 *reg_offset; struct clk *clk; const struct vendor_data *vendor; - unsigned int dmacr; /* dma control reg */ unsigned int im; /* interrupt mask */ unsigned int old_status; unsigned int fifosize; /* vendor-specific */ @@ -266,6 +265,7 @@ struct uart_amba_port { unsigned int rs485_tx_drain_interval; /* usecs */ #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ + unsigned int dmacr; /* dma control reg */ bool using_tx_dma; bool using_rx_dma; struct pl011_dmarx_data dmarx; From 384fa8647dc55ab47515bbb76ebda37b2350e5b3 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Sun, 7 Apr 2024 02:27:08 +0200 Subject: [PATCH 054/113] serial: 8250: Remove superfluous sanity check The serial core already checks the RS485 RTS settings for sanity, so remove the superfluous check in serial8250_em485_config(). Signed-off-by: Lino Sanfilippo Link: https://lore.kernel.org/r/20240407002709.16224-4-l.sanfilippo@kunbus.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 0c19451d0332..893bc493f662 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -612,13 +612,6 @@ int serial8250_em485_config(struct uart_port *port, struct ktermios *termios, { struct uart_8250_port *up = up_to_u8250p(port); - /* pick sane settings if the user hasn't */ - if (!!(rs485->flags & SER_RS485_RTS_ON_SEND) == - !!(rs485->flags & SER_RS485_RTS_AFTER_SEND)) { - rs485->flags |= SER_RS485_RTS_ON_SEND; - rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; - } - /* * Both serial8250_em485_init() and serial8250_em485_destroy() * are idempotent. From fff4a5d5609db86ccdf1cf5791b7651b7f6a9b19 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Sun, 7 Apr 2024 02:27:09 +0200 Subject: [PATCH 055/113] serial: ar933x: Remove unneeded static structure In case that no RTS GPIO is available do not use a dedicated nullified serial_rs485 struct to disable RS485 support, but simply delete the SER_RS485_ENABLED flag in the ports rs485_supported struct. This make the structure superfluous and it can be removed. Signed-off-by: Lino Sanfilippo Link: https://lore.kernel.org/r/20240407002709.16224-5-l.sanfilippo@kunbus.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ar933x_uart.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index f2836ecc5c19..47889a557119 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -692,7 +692,6 @@ static struct uart_driver ar933x_uart_driver = { .cons = NULL, /* filled in runtime */ }; -static const struct serial_rs485 ar933x_no_rs485 = {}; static const struct serial_rs485 ar933x_rs485_supported = { .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, }; @@ -788,7 +787,7 @@ static int ar933x_uart_probe(struct platform_device *pdev) up->rts_gpiod = mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS); if (!up->rts_gpiod) { - port->rs485_supported = ar933x_no_rs485; + port->rs485_supported.flags &= ~SER_RS485_ENABLED; if (port->rs485.flags & SER_RS485_ENABLED) { dev_err(&pdev->dev, "lacking rts-gpio, disabling RS485\n"); port->rs485.flags &= ~SER_RS485_ENABLED; From 2a8e4ab0c93fad30769479f86849e22d63cd0e12 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Tue, 9 Apr 2024 11:42:49 -0400 Subject: [PATCH 056/113] serial: sc16is7xx: add proper sched.h include for sched_set_fifo() Replace incorrect include with the proper one for sched_set_fifo() declaration. Fixes: 28d2f209cd16 ("sched,serial: Convert to sched_set_fifo()") Signed-off-by: Hugo Villeneuve Link: https://lore.kernel.org/r/20240409154253.3043822-2-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 07aca9fc6d97..afcfb4f38945 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #define SC16IS7XX_NAME "sc16is7xx" #define SC16IS7XX_MAX_DEVS 8 From 7f335744817092112be68efc06404466c135ae52 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Tue, 9 Apr 2024 11:42:50 -0400 Subject: [PATCH 057/113] serial: sc16is7xx: unconditionally clear line bit in sc16is7xx_remove() There is no need to check for previous port registration in sc16is7xx_remove() because if sc16is7xx_probe() succeeded, we are guaranteed to have successfully registered both ports. We can thus unconditionally clear the line allocation bit in sc16is7xx_lines. Signed-off-by: Hugo Villeneuve Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240409154253.3043822-3-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index afcfb4f38945..a134cd2a931d 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1660,8 +1660,8 @@ static void sc16is7xx_remove(struct device *dev) for (i = 0; i < s->devtype->nr_uart; i++) { kthread_cancel_delayed_work_sync(&s->p[i].ms_work); - if (test_and_clear_bit(s->p[i].port.line, sc16is7xx_lines)) - uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); + clear_bit(s->p[i].port.line, sc16is7xx_lines); + uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); sc16is7xx_power(&s->p[i].port, 0); } From d49216438139bca0454e69b6c4ab8a01af2b72ed Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Tue, 9 Apr 2024 11:42:51 -0400 Subject: [PATCH 058/113] serial: sc16is7xx: split into core and I2C/SPI parts (core) Split the common code from sc16is7xx driver and move the I2C and SPI bus parts into interface-specific source files. sc16is7xx becomes the core functions which can support multiple bus interfaces like I2C and SPI. No functional changes intended. Also simplify and improve Kconfig entries. - Capitalize SPI keyword for consistency - Display list of supported ICs each on a separate line (and aligned) to facilitate locating a specific IC model - Add Manufacturer name at start of description string - Add UART keyword to description string Suggested-by: Andy Shevchenko Signed-off-by: Hugo Villeneuve Link: https://lore.kernel.org/r/20240409154253.3043822-4-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 47 +++--- drivers/tty/serial/Makefile | 2 + drivers/tty/serial/sc16is7xx.c | 225 +++++------------------------ drivers/tty/serial/sc16is7xx.h | 41 ++++++ drivers/tty/serial/sc16is7xx_i2c.c | 64 ++++++++ drivers/tty/serial/sc16is7xx_spi.c | 87 +++++++++++ 6 files changed, 248 insertions(+), 218 deletions(-) create mode 100644 drivers/tty/serial/sc16is7xx.h create mode 100644 drivers/tty/serial/sc16is7xx_i2c.c create mode 100644 drivers/tty/serial/sc16is7xx_spi.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 5c80505cdf41..4fdd7857ef4d 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1024,41 +1024,30 @@ config SERIAL_SCCNXP_CONSOLE Support for console on SCCNXP serial ports. config SERIAL_SC16IS7XX_CORE - tristate - -config SERIAL_SC16IS7XX - tristate "SC16IS7xx serial support" + tristate "NXP SC16IS7xx UART support" select SERIAL_CORE - depends on (SPI_MASTER && !I2C) || I2C + select SERIAL_SC16IS7XX_SPI if SPI_MASTER + select SERIAL_SC16IS7XX_I2C if I2C help - This selects support for SC16IS7xx serial ports. - Supported ICs are SC16IS740, SC16IS741, SC16IS750, SC16IS752, - SC16IS760 and SC16IS762. Select supported buses using options below. + Core driver for NXP SC16IS7xx UARTs. + Supported ICs are: + + SC16IS740 + SC16IS741 + SC16IS750 + SC16IS752 + SC16IS760 + SC16IS762 + + The driver supports both I2C and SPI interfaces. config SERIAL_SC16IS7XX_I2C - bool "SC16IS7xx for I2C interface" - depends on SERIAL_SC16IS7XX - depends on I2C - select SERIAL_SC16IS7XX_CORE if SERIAL_SC16IS7XX - select REGMAP_I2C if I2C - default y - help - Enable SC16IS7xx driver on I2C bus, - If required say y, and say n to i2c if not required, - Enabled by default to support oldconfig. - You must select at least one bus for the driver to be built. + tristate + select REGMAP_I2C config SERIAL_SC16IS7XX_SPI - bool "SC16IS7xx for spi interface" - depends on SERIAL_SC16IS7XX - depends on SPI_MASTER - select SERIAL_SC16IS7XX_CORE if SERIAL_SC16IS7XX - select REGMAP_SPI if SPI_MASTER - help - Enable SC16IS7xx driver on SPI bus, - If required say y, and say n to spi if not required, - This is additional support to existing driver. - You must select at least one bus for the driver to be built. + tristate + select REGMAP_SPI config SERIAL_TIMBERDALE tristate "Support for timberdale UART" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index b25e9b54a660..faa45f2b8bb0 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -76,6 +76,8 @@ obj-$(CONFIG_SERIAL_SAMSUNG) += samsung_tty.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o obj-$(CONFIG_SERIAL_SC16IS7XX_CORE) += sc16is7xx.o +obj-$(CONFIG_SERIAL_SC16IS7XX_SPI) += sc16is7xx_spi.o +obj-$(CONFIG_SERIAL_SC16IS7XX_I2C) += sc16is7xx_i2c.o obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index a134cd2a931d..366eb3f53789 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1,19 +1,22 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * SC16IS7xx tty serial driver - Copyright (C) 2014 GridPoint - * Author: Jon Ringle + * SC16IS7xx tty serial driver - common code * - * Based on max310x.c, by Alexander Shiyan + * Copyright (C) 2014 GridPoint + * Author: Jon Ringle + * Based on max310x.c, by Alexander Shiyan */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#undef DEFAULT_SYMBOL_NAMESPACE +#define DEFAULT_SYMBOL_NAMESPACE SERIAL_NXP_SC16IS7XX #include #include #include #include +#include #include -#include +#include #include #include #include @@ -21,15 +24,15 @@ #include #include #include +#include #include #include -#include #include #include -#define SC16IS7XX_NAME "sc16is7xx" +#include "sc16is7xx.h" + #define SC16IS7XX_MAX_DEVS 8 -#define SC16IS7XX_MAX_PORTS 2 /* Maximum number of UART ports per IC. */ /* SC16IS7XX register definitions */ #define SC16IS7XX_RHR_REG (0x00) /* RX FIFO */ @@ -302,16 +305,9 @@ /* Misc definitions */ -#define SC16IS7XX_SPI_READ_BIT BIT(7) #define SC16IS7XX_FIFO_SIZE (64) #define SC16IS7XX_GPIOS_PER_BANK 4 -struct sc16is7xx_devtype { - char name[10]; - int nr_gpio; - int nr_uart; -}; - #define SC16IS7XX_RECONF_MD (1 << 0) #define SC16IS7XX_RECONF_IER (1 << 1) #define SC16IS7XX_RECONF_RS485 (1 << 2) @@ -492,35 +488,40 @@ static void sc16is7xx_stop_rx(struct uart_port *port) sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT); } -static const struct sc16is7xx_devtype sc16is74x_devtype = { +const struct sc16is7xx_devtype sc16is74x_devtype = { .name = "SC16IS74X", .nr_gpio = 0, .nr_uart = 1, }; +EXPORT_SYMBOL_GPL(sc16is74x_devtype); -static const struct sc16is7xx_devtype sc16is750_devtype = { +const struct sc16is7xx_devtype sc16is750_devtype = { .name = "SC16IS750", .nr_gpio = 8, .nr_uart = 1, }; +EXPORT_SYMBOL_GPL(sc16is750_devtype); -static const struct sc16is7xx_devtype sc16is752_devtype = { +const struct sc16is7xx_devtype sc16is752_devtype = { .name = "SC16IS752", .nr_gpio = 8, .nr_uart = 2, }; +EXPORT_SYMBOL_GPL(sc16is752_devtype); -static const struct sc16is7xx_devtype sc16is760_devtype = { +const struct sc16is7xx_devtype sc16is760_devtype = { .name = "SC16IS760", .nr_gpio = 8, .nr_uart = 1, }; +EXPORT_SYMBOL_GPL(sc16is760_devtype); -static const struct sc16is7xx_devtype sc16is762_devtype = { +const struct sc16is7xx_devtype sc16is762_devtype = { .name = "SC16IS762", .nr_gpio = 8, .nr_uart = 2, }; +EXPORT_SYMBOL_GPL(sc16is762_devtype); static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg) { @@ -1453,9 +1454,8 @@ static const struct serial_rs485 sc16is7xx_rs485_supported = { .delay_rts_after_send = 1, /* Not supported but keep returning -EINVAL */ }; -static int sc16is7xx_probe(struct device *dev, - const struct sc16is7xx_devtype *devtype, - struct regmap *regmaps[], int irq) +int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, + struct regmap *regmaps[], int irq) { unsigned long freq = 0, *pfreq = dev_get_platdata(dev); unsigned int val; @@ -1647,8 +1647,9 @@ static int sc16is7xx_probe(struct device *dev, return ret; } +EXPORT_SYMBOL_GPL(sc16is7xx_probe); -static void sc16is7xx_remove(struct device *dev) +void sc16is7xx_remove(struct device *dev) { struct sc16is7xx_port *s = dev_get_drvdata(dev); int i; @@ -1670,8 +1671,9 @@ static void sc16is7xx_remove(struct device *dev) clk_disable_unprepare(s->clk); } +EXPORT_SYMBOL_GPL(sc16is7xx_remove); -static const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { +const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { { .compatible = "nxp,sc16is740", .data = &sc16is74x_devtype, }, { .compatible = "nxp,sc16is741", .data = &sc16is74x_devtype, }, { .compatible = "nxp,sc16is750", .data = &sc16is750_devtype, }, @@ -1680,9 +1682,10 @@ static const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { { .compatible = "nxp,sc16is762", .data = &sc16is762_devtype, }, { } }; +EXPORT_SYMBOL_GPL(sc16is7xx_dt_ids); MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids); -static struct regmap_config regcfg = { +struct regmap_config sc16is7xx_regcfg = { .reg_bits = 5, .pad_bits = 3, .val_bits = 8, @@ -1695,8 +1698,9 @@ static struct regmap_config regcfg = { .max_raw_write = SC16IS7XX_FIFO_SIZE, .max_register = SC16IS7XX_EFCR_REG, }; +EXPORT_SYMBOL_GPL(sc16is7xx_regcfg); -static const char *sc16is7xx_regmap_name(u8 port_id) +const char *sc16is7xx_regmap_name(u8 port_id) { switch (port_id) { case 0: return "port0"; @@ -1706,184 +1710,27 @@ static const char *sc16is7xx_regmap_name(u8 port_id) return NULL; } } +EXPORT_SYMBOL_GPL(sc16is7xx_regmap_name); -static unsigned int sc16is7xx_regmap_port_mask(unsigned int port_id) +unsigned int sc16is7xx_regmap_port_mask(unsigned int port_id) { /* CH1,CH0 are at bits 2:1. */ return port_id << 1; } - -#ifdef CONFIG_SERIAL_SC16IS7XX_SPI -static int sc16is7xx_spi_probe(struct spi_device *spi) -{ - const struct sc16is7xx_devtype *devtype; - struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; - unsigned int i; - int ret; - - /* Setup SPI bus */ - spi->bits_per_word = 8; - /* For all variants, only mode 0 is supported */ - if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0) - return dev_err_probe(&spi->dev, -EINVAL, "Unsupported SPI mode\n"); - - spi->mode = spi->mode ? : SPI_MODE_0; - spi->max_speed_hz = spi->max_speed_hz ? : 4 * HZ_PER_MHZ; - ret = spi_setup(spi); - if (ret) - return ret; - - devtype = spi_get_device_match_data(spi); - if (!devtype) - return dev_err_probe(&spi->dev, -ENODEV, "Failed to match device\n"); - - for (i = 0; i < devtype->nr_uart; i++) { - regcfg.name = sc16is7xx_regmap_name(i); - /* - * If read_flag_mask is 0, the regmap code sets it to a default - * of 0x80. Since we specify our own mask, we must add the READ - * bit ourselves: - */ - regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i) | - SC16IS7XX_SPI_READ_BIT; - regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); - regmaps[i] = devm_regmap_init_spi(spi, ®cfg); - } - - return sc16is7xx_probe(&spi->dev, devtype, regmaps, spi->irq); -} - -static void sc16is7xx_spi_remove(struct spi_device *spi) -{ - sc16is7xx_remove(&spi->dev); -} - -static const struct spi_device_id sc16is7xx_spi_id_table[] = { - { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, - { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, - { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, - { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, - { } -}; - -MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table); - -static struct spi_driver sc16is7xx_spi_uart_driver = { - .driver = { - .name = SC16IS7XX_NAME, - .of_match_table = sc16is7xx_dt_ids, - }, - .probe = sc16is7xx_spi_probe, - .remove = sc16is7xx_spi_remove, - .id_table = sc16is7xx_spi_id_table, -}; -#endif - -#ifdef CONFIG_SERIAL_SC16IS7XX_I2C -static int sc16is7xx_i2c_probe(struct i2c_client *i2c) -{ - const struct sc16is7xx_devtype *devtype; - struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; - unsigned int i; - - devtype = i2c_get_match_data(i2c); - if (!devtype) - return dev_err_probe(&i2c->dev, -ENODEV, "Failed to match device\n"); - - for (i = 0; i < devtype->nr_uart; i++) { - regcfg.name = sc16is7xx_regmap_name(i); - regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i); - regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); - regmaps[i] = devm_regmap_init_i2c(i2c, ®cfg); - } - - return sc16is7xx_probe(&i2c->dev, devtype, regmaps, i2c->irq); -} - -static void sc16is7xx_i2c_remove(struct i2c_client *client) -{ - sc16is7xx_remove(&client->dev); -} - -static const struct i2c_device_id sc16is7xx_i2c_id_table[] = { - { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, - { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, - { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, - { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, - { } -}; -MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table); - -static struct i2c_driver sc16is7xx_i2c_uart_driver = { - .driver = { - .name = SC16IS7XX_NAME, - .of_match_table = sc16is7xx_dt_ids, - }, - .probe = sc16is7xx_i2c_probe, - .remove = sc16is7xx_i2c_remove, - .id_table = sc16is7xx_i2c_id_table, -}; - -#endif +EXPORT_SYMBOL_GPL(sc16is7xx_regmap_port_mask); static int __init sc16is7xx_init(void) { - int ret; - - ret = uart_register_driver(&sc16is7xx_uart); - if (ret) { - pr_err("Registering UART driver failed\n"); - return ret; - } - -#ifdef CONFIG_SERIAL_SC16IS7XX_I2C - ret = i2c_add_driver(&sc16is7xx_i2c_uart_driver); - if (ret < 0) { - pr_err("failed to init sc16is7xx i2c --> %d\n", ret); - goto err_i2c; - } -#endif - -#ifdef CONFIG_SERIAL_SC16IS7XX_SPI - ret = spi_register_driver(&sc16is7xx_spi_uart_driver); - if (ret < 0) { - pr_err("failed to init sc16is7xx spi --> %d\n", ret); - goto err_spi; - } -#endif - return ret; - -#ifdef CONFIG_SERIAL_SC16IS7XX_SPI -err_spi: -#endif -#ifdef CONFIG_SERIAL_SC16IS7XX_I2C - i2c_del_driver(&sc16is7xx_i2c_uart_driver); -err_i2c: -#endif - uart_unregister_driver(&sc16is7xx_uart); - return ret; + return uart_register_driver(&sc16is7xx_uart); } module_init(sc16is7xx_init); static void __exit sc16is7xx_exit(void) { -#ifdef CONFIG_SERIAL_SC16IS7XX_I2C - i2c_del_driver(&sc16is7xx_i2c_uart_driver); -#endif - -#ifdef CONFIG_SERIAL_SC16IS7XX_SPI - spi_unregister_driver(&sc16is7xx_spi_uart_driver); -#endif uart_unregister_driver(&sc16is7xx_uart); } module_exit(sc16is7xx_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jon Ringle "); -MODULE_DESCRIPTION("SC16IS7XX serial driver"); +MODULE_DESCRIPTION("SC16IS7xx tty serial core driver"); diff --git a/drivers/tty/serial/sc16is7xx.h b/drivers/tty/serial/sc16is7xx.h new file mode 100644 index 000000000000..2ee3ce83d95a --- /dev/null +++ b/drivers/tty/serial/sc16is7xx.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* SC16IS7xx SPI/I2C tty serial driver */ + +#ifndef _SC16IS7XX_H_ +#define _SC16IS7XX_H_ + +#include +#include +#include + +#define SC16IS7XX_NAME "sc16is7xx" +#define SC16IS7XX_MAX_PORTS 2 /* Maximum number of UART ports per IC. */ + +struct device; + +struct sc16is7xx_devtype { + char name[10]; + int nr_gpio; + int nr_uart; +}; + +extern struct regmap_config sc16is7xx_regcfg; + +extern const struct of_device_id sc16is7xx_dt_ids[]; + +extern const struct sc16is7xx_devtype sc16is74x_devtype; +extern const struct sc16is7xx_devtype sc16is750_devtype; +extern const struct sc16is7xx_devtype sc16is752_devtype; +extern const struct sc16is7xx_devtype sc16is760_devtype; +extern const struct sc16is7xx_devtype sc16is762_devtype; + +const char *sc16is7xx_regmap_name(u8 port_id); + +unsigned int sc16is7xx_regmap_port_mask(unsigned int port_id); + +int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, + struct regmap *regmaps[], int irq); + +void sc16is7xx_remove(struct device *dev); + +#endif /* _SC16IS7XX_H_ */ diff --git a/drivers/tty/serial/sc16is7xx_i2c.c b/drivers/tty/serial/sc16is7xx_i2c.c new file mode 100644 index 000000000000..de51d1675abf --- /dev/null +++ b/drivers/tty/serial/sc16is7xx_i2c.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* SC16IS7xx I2C interface driver */ + +#include +#include +#include +#include +#include +#include + +#include "sc16is7xx.h" + +static int sc16is7xx_i2c_probe(struct i2c_client *i2c) +{ + const struct sc16is7xx_devtype *devtype; + struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; + unsigned int i; + + devtype = i2c_get_match_data(i2c); + if (!devtype) + return dev_err_probe(&i2c->dev, -ENODEV, "Failed to match device\n"); + + for (i = 0; i < devtype->nr_uart; i++) { + sc16is7xx_regcfg.name = sc16is7xx_regmap_name(i); + sc16is7xx_regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i); + sc16is7xx_regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); + regmaps[i] = devm_regmap_init_i2c(i2c, &sc16is7xx_regcfg); + } + + return sc16is7xx_probe(&i2c->dev, devtype, regmaps, i2c->irq); +} + +static void sc16is7xx_i2c_remove(struct i2c_client *client) +{ + sc16is7xx_remove(&client->dev); +} + +static const struct i2c_device_id sc16is7xx_i2c_id_table[] = { + { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, + { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, + { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, + { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table); + +static struct i2c_driver sc16is7xx_i2c_driver = { + .driver = { + .name = SC16IS7XX_NAME, + .of_match_table = sc16is7xx_dt_ids, + }, + .probe = sc16is7xx_i2c_probe, + .remove = sc16is7xx_i2c_remove, + .id_table = sc16is7xx_i2c_id_table, +}; + +module_i2c_driver(sc16is7xx_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SC16IS7xx I2C interface driver"); +MODULE_IMPORT_NS(SERIAL_NXP_SC16IS7XX); diff --git a/drivers/tty/serial/sc16is7xx_spi.c b/drivers/tty/serial/sc16is7xx_spi.c new file mode 100644 index 000000000000..f110c4e6dce6 --- /dev/null +++ b/drivers/tty/serial/sc16is7xx_spi.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* SC16IS7xx SPI interface driver */ + +#include +#include +#include +#include +#include +#include +#include + +#include "sc16is7xx.h" + +/* SPI definitions */ +#define SC16IS7XX_SPI_READ_BIT BIT(7) + +static int sc16is7xx_spi_probe(struct spi_device *spi) +{ + const struct sc16is7xx_devtype *devtype; + struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; + unsigned int i; + int ret; + + /* Setup SPI bus */ + spi->bits_per_word = 8; + /* For all variants, only mode 0 is supported */ + if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0) + return dev_err_probe(&spi->dev, -EINVAL, "Unsupported SPI mode\n"); + + spi->mode = spi->mode ? : SPI_MODE_0; + spi->max_speed_hz = spi->max_speed_hz ? : 4 * HZ_PER_MHZ; + ret = spi_setup(spi); + if (ret) + return ret; + + devtype = spi_get_device_match_data(spi); + if (!devtype) + return dev_err_probe(&spi->dev, -ENODEV, "Failed to match device\n"); + + for (i = 0; i < devtype->nr_uart; i++) { + sc16is7xx_regcfg.name = sc16is7xx_regmap_name(i); + /* + * If read_flag_mask is 0, the regmap code sets it to a default + * of 0x80. Since we specify our own mask, we must add the READ + * bit ourselves: + */ + sc16is7xx_regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i) | + SC16IS7XX_SPI_READ_BIT; + sc16is7xx_regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); + regmaps[i] = devm_regmap_init_spi(spi, &sc16is7xx_regcfg); + } + + return sc16is7xx_probe(&spi->dev, devtype, regmaps, spi->irq); +} + +static void sc16is7xx_spi_remove(struct spi_device *spi) +{ + sc16is7xx_remove(&spi->dev); +} + +static const struct spi_device_id sc16is7xx_spi_id_table[] = { + { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, + { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, + { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, + { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table); + +static struct spi_driver sc16is7xx_spi_driver = { + .driver = { + .name = SC16IS7XX_NAME, + .of_match_table = sc16is7xx_dt_ids, + }, + .probe = sc16is7xx_spi_probe, + .remove = sc16is7xx_spi_remove, + .id_table = sc16is7xx_spi_id_table, +}; + +module_spi_driver(sc16is7xx_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SC16IS7xx SPI interface driver"); +MODULE_IMPORT_NS(SERIAL_NXP_SC16IS7XX); From cf9c37530fdabc0c25a896ff56f4c9208dcb9c86 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Tue, 9 Apr 2024 11:42:52 -0400 Subject: [PATCH 059/113] serial: sc16is7xx: split into core and I2C/SPI parts (sc16is7xx_lines) Before, sc16is7xx_lines was checked for a free (zero) bit first, and then later it was set only if UART port registration succeeded. Now that sc16is7xx_lines is shared for the I2C and SPI drivers, there is a possibility that the two drivers can simultaneously try to reserve the same line bit at the same time. To prevent this, make sure line allocation is reserved atomically, and use a new variable to hold the status of UART port registration. Now that we no longer need to search if a bit is set, it is now possible to simplify sc16is7xx_lines allocation by using the IDA framework. Link: https://lore.kernel.org/all/20231212150302.a9ec5d085a4ba65e89ca41af@hugovil.com/ Link: https://lore.kernel.org/all/CAHp75VebCZckUrNraYQj9k=Mrn2kbYs1Lx26f5-8rKJ3RXeh-w@mail.gmail.com/ Suggested-by: Andy Shevchenko Signed-off-by: Hugo Villeneuve Link: https://lore.kernel.org/r/20240409154253.3043822-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 366eb3f53789..9c9673243c4c 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -10,12 +10,12 @@ #undef DEFAULT_SYMBOL_NAMESPACE #define DEFAULT_SYMBOL_NAMESPACE SERIAL_NXP_SC16IS7XX -#include #include #include #include #include #include +#include #include #include #include @@ -345,7 +345,7 @@ struct sc16is7xx_port { struct sc16is7xx_one p[]; }; -static DECLARE_BITMAP(sc16is7xx_lines, SC16IS7XX_MAX_DEVS); +static DEFINE_IDA(sc16is7xx_lines); static struct uart_driver sc16is7xx_uart = { .owner = THIS_MODULE, @@ -1462,6 +1462,7 @@ int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, u32 uartclk = 0; int i, ret; struct sc16is7xx_port *s; + bool port_registered[SC16IS7XX_MAX_PORTS]; for (i = 0; i < devtype->nr_uart; i++) if (IS_ERR(regmaps[i])) @@ -1526,13 +1527,19 @@ int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, regmap_write(regmaps[0], SC16IS7XX_IOCONTROL_REG, SC16IS7XX_IOCONTROL_SRESET_BIT); + /* Mark each port line and status as uninitialised. */ for (i = 0; i < devtype->nr_uart; ++i) { - s->p[i].port.line = find_first_zero_bit(sc16is7xx_lines, - SC16IS7XX_MAX_DEVS); - if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) { - ret = -ERANGE; + s->p[i].port.line = SC16IS7XX_MAX_DEVS; + port_registered[i] = false; + } + + for (i = 0; i < devtype->nr_uart; ++i) { + ret = ida_alloc_max(&sc16is7xx_lines, + SC16IS7XX_MAX_DEVS - 1, GFP_KERNEL); + if (ret < 0) goto out_ports; - } + + s->p[i].port.line = ret; /* Initialize port data */ s->p[i].port.dev = dev; @@ -1578,7 +1585,7 @@ int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, if (ret) goto out_ports; - set_bit(s->p[i].port.line, sc16is7xx_lines); + port_registered[i] = true; /* Enable EFR */ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, @@ -1636,9 +1643,12 @@ int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, #endif out_ports: - for (i = 0; i < devtype->nr_uart; i++) - if (test_and_clear_bit(s->p[i].port.line, sc16is7xx_lines)) + for (i = 0; i < devtype->nr_uart; i++) { + if (s->p[i].port.line < SC16IS7XX_MAX_DEVS) + ida_free(&sc16is7xx_lines, s->p[i].port.line); + if (port_registered[i]) uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); + } kthread_stop(s->kworker_task); @@ -1661,7 +1671,7 @@ void sc16is7xx_remove(struct device *dev) for (i = 0; i < s->devtype->nr_uart; i++) { kthread_cancel_delayed_work_sync(&s->p[i].ms_work); - clear_bit(s->p[i].port.line, sc16is7xx_lines); + ida_free(&sc16is7xx_lines, s->p[i].port.line); uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); sc16is7xx_power(&s->p[i].port, 0); } From 48d4a801be0ff64740832fcc71ac4632c12fd73b Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Tue, 9 Apr 2024 11:42:53 -0400 Subject: [PATCH 060/113] serial: sc16is7xx: split into core and I2C/SPI parts (sc16is7xx_regcfg) Since each I2C/SPI probe function can modify sc16is7xx_regcfg at the same time, change structure to be constant and do the required modifications on a local copy. Signed-off-by: Hugo Villeneuve Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240409154253.3043822-6-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 2 +- drivers/tty/serial/sc16is7xx.h | 2 +- drivers/tty/serial/sc16is7xx_i2c.c | 11 +++++++---- drivers/tty/serial/sc16is7xx_spi.c | 11 +++++++---- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 9c9673243c4c..03cf30e20b75 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1695,7 +1695,7 @@ const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { EXPORT_SYMBOL_GPL(sc16is7xx_dt_ids); MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids); -struct regmap_config sc16is7xx_regcfg = { +const struct regmap_config sc16is7xx_regcfg = { .reg_bits = 5, .pad_bits = 3, .val_bits = 8, diff --git a/drivers/tty/serial/sc16is7xx.h b/drivers/tty/serial/sc16is7xx.h index 2ee3ce83d95a..afb784eaee45 100644 --- a/drivers/tty/serial/sc16is7xx.h +++ b/drivers/tty/serial/sc16is7xx.h @@ -19,7 +19,7 @@ struct sc16is7xx_devtype { int nr_uart; }; -extern struct regmap_config sc16is7xx_regcfg; +extern const struct regmap_config sc16is7xx_regcfg; extern const struct of_device_id sc16is7xx_dt_ids[]; diff --git a/drivers/tty/serial/sc16is7xx_i2c.c b/drivers/tty/serial/sc16is7xx_i2c.c index de51d1675abf..3ed47c306d85 100644 --- a/drivers/tty/serial/sc16is7xx_i2c.c +++ b/drivers/tty/serial/sc16is7xx_i2c.c @@ -14,17 +14,20 @@ static int sc16is7xx_i2c_probe(struct i2c_client *i2c) { const struct sc16is7xx_devtype *devtype; struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; + struct regmap_config regcfg; unsigned int i; devtype = i2c_get_match_data(i2c); if (!devtype) return dev_err_probe(&i2c->dev, -ENODEV, "Failed to match device\n"); + memcpy(®cfg, &sc16is7xx_regcfg, sizeof(struct regmap_config)); + for (i = 0; i < devtype->nr_uart; i++) { - sc16is7xx_regcfg.name = sc16is7xx_regmap_name(i); - sc16is7xx_regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i); - sc16is7xx_regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); - regmaps[i] = devm_regmap_init_i2c(i2c, &sc16is7xx_regcfg); + regcfg.name = sc16is7xx_regmap_name(i); + regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i); + regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); + regmaps[i] = devm_regmap_init_i2c(i2c, ®cfg); } return sc16is7xx_probe(&i2c->dev, devtype, regmaps, i2c->irq); diff --git a/drivers/tty/serial/sc16is7xx_spi.c b/drivers/tty/serial/sc16is7xx_spi.c index f110c4e6dce6..73df36f8a7fd 100644 --- a/drivers/tty/serial/sc16is7xx_spi.c +++ b/drivers/tty/serial/sc16is7xx_spi.c @@ -18,6 +18,7 @@ static int sc16is7xx_spi_probe(struct spi_device *spi) { const struct sc16is7xx_devtype *devtype; struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; + struct regmap_config regcfg; unsigned int i; int ret; @@ -37,17 +38,19 @@ static int sc16is7xx_spi_probe(struct spi_device *spi) if (!devtype) return dev_err_probe(&spi->dev, -ENODEV, "Failed to match device\n"); + memcpy(®cfg, &sc16is7xx_regcfg, sizeof(struct regmap_config)); + for (i = 0; i < devtype->nr_uart; i++) { - sc16is7xx_regcfg.name = sc16is7xx_regmap_name(i); + regcfg.name = sc16is7xx_regmap_name(i); /* * If read_flag_mask is 0, the regmap code sets it to a default * of 0x80. Since we specify our own mask, we must add the READ * bit ourselves: */ - sc16is7xx_regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i) | + regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i) | SC16IS7XX_SPI_READ_BIT; - sc16is7xx_regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); - regmaps[i] = devm_regmap_init_spi(spi, &sc16is7xx_regcfg); + regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); + regmaps[i] = devm_regmap_init_spi(spi, ®cfg); } return sc16is7xx_probe(&spi->dev, devtype, regmaps, spi->irq); From 4547cd76f08a6f301f6ad563f5d0e4566924ec6b Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 11 Apr 2024 11:06:20 +0300 Subject: [PATCH 061/113] serial: 8250: Fix add preferred console for serial8250_isa_init_ports() We need to inline serial_base_add_isa_preferred_console() based on CONFIG_SERIAL_8250_CONSOLE and not based on CONFIG_SERIAL_CORE_CONSOLE. Otherwise we can get the follwoing error as noted by Stephen: ERROR: modpost: "serial_base_add_isa_preferred_console" [drivers/tty/serial/8250/8250.ko] undefined! We also have a duplicate inlined serial_base_add_isa_preferred_console(), in serial_base_bus.c added by the same commit by accident, let's drop it. Fixes: a8b04cfe7dad ("serial: 8250: Add preferred console in serial8250_isa_init_ports()") Reported-by: Stephen Rothwell Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20240411080622.11929-1-tony@atomide.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_base.h | 10 ++++++++-- drivers/tty/serial/serial_base_bus.c | 11 ++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h index 39dab6bd4b76..ab6f1876726c 100644 --- a/drivers/tty/serial/serial_base.h +++ b/drivers/tty/serial/serial_base.h @@ -51,8 +51,6 @@ void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port int serial_base_add_preferred_console(struct uart_driver *drv, struct uart_port *port); -int serial_base_add_isa_preferred_console(const char *name, int idx); - #else static inline @@ -62,6 +60,14 @@ int serial_base_add_preferred_console(struct uart_driver *drv, return 0; } +#endif + +#ifdef CONFIG_SERIAL_8250_CONSOLE + +int serial_base_add_isa_preferred_console(const char *name, int idx); + +#else + static inline int serial_base_add_isa_preferred_console(const char *name, int idx) { diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c index ae18871c0f36..281a2d5a7332 100644 --- a/drivers/tty/serial/serial_base_bus.c +++ b/drivers/tty/serial/serial_base_bus.c @@ -219,6 +219,8 @@ static int serial_base_add_one_prefcon(const char *match, const char *dev_name, return ret; } +#endif + #ifdef __sparc__ /* Handle Sparc ttya and ttyb options as done in console_setup() */ @@ -329,15 +331,6 @@ int serial_base_add_isa_preferred_console(const char *name, int idx) return serial_base_add_prefcon(name, idx); } -#else - -int serial_base_add_isa_preferred_console(const char *name, int idx) -{ - return 0; -} - -#endif - #endif static int serial_base_init(void) From 46f2bba8314fdd818cc7408126919cb6421b4e19 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 11 Apr 2024 09:55:37 +0100 Subject: [PATCH 062/113] serial: omap: remove redundant assignment to variable tmout Variable tmout is being assigned a value that is never read, it is being re-assinged in the following for-loop statement. The assignment is redundant and can be removed. Cleans up clang scan warning: drivers/tty/serial/omap-serial.c:1096:3: warning: Value stored to 'tmout' is never read [deadcode.DeadStores] Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20240411085537.306020-1-colin.i.king@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 9be1c871cf11..d7e172eeaab1 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1093,7 +1093,6 @@ static void __maybe_unused wait_for_xmitr(struct uart_omap_port *up) /* Wait up to 1s for flow control if necessary */ if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; for (tmout = 1000000; tmout; tmout--) { unsigned int msr = serial_in(up, UART_MSR); From 25ca2d573ebae939d19f5df5fef2c58ef83ed97a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 10 Apr 2024 17:11:35 +0300 Subject: [PATCH 063/113] serial: max3100: Convert to_max3100_port() to be static inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As Jiri rightfully pointed out the current to_max3100_port() macro implementation is fragile in a sense that it expects the variable name to be port, otherwise it blow up the build. Change this to be static inline to prevent bad compilation. Suggested-by: Jiri Slaby Signed-off-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240410141135.1378948-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index ce24588150bf..fda63918d1eb 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -111,7 +111,10 @@ struct max3100_port { struct timer_list timer; }; -#define to_max3100_port(port) container_of(port, struct max3100_port, port) +static inline struct max3100_port *to_max3100_port(struct uart_port *port) +{ + return container_of(port, struct max3100_port, port); +} static struct max3100_port *max3100s[MAX_MAX3100]; /* the chips */ static DEFINE_MUTEX(max3100s_lock); /* race on probe */ From 693f75b91a9171e99f84fc193e39f48e21ba4a4f Mon Sep 17 00:00:00 2001 From: Sreenath Vijayan Date: Wed, 13 Mar 2024 15:50:52 +0530 Subject: [PATCH 064/113] printk: Add function to replay kernel log on consoles Add a generic function console_replay_all() for replaying the kernel log on consoles, in any context. It would allow viewing the logs on an unresponsive terminal via sysrq. Reuse the existing code from console_flush_on_panic() for resetting the sequence numbers, by introducing a new helper function __console_rewind_all(). It is safe to be called under console_lock(). Try to acquire lock on the console subsystem without waiting. If successful, reset the sequence number to oldest available record on all consoles and call console_unlock() which will automatically flush the messages to the consoles. Suggested-by: John Ogness Suggested-by: Petr Mladek Signed-off-by: Shimoyashiki Taichi Reviewed-by: John Ogness Signed-off-by: Sreenath Vijayan Link: https://lore.kernel.org/r/90ee131c643a5033d117b556c0792de65129d4c3.1710220326.git.sreenath.vijayan@sony.com Signed-off-by: Greg Kroah-Hartman --- include/linux/printk.h | 4 +++ kernel/printk/printk.c | 77 +++++++++++++++++++++++++++++------------- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/include/linux/printk.h b/include/linux/printk.h index 5e038e782256..1c5936ab991f 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -195,6 +195,7 @@ void show_regs_print_info(const char *log_lvl); extern asmlinkage void dump_stack_lvl(const char *log_lvl) __cold; extern asmlinkage void dump_stack(void) __cold; void printk_trigger_flush(void); +void console_replay_all(void); #else static inline __printf(1, 0) int vprintk(const char *s, va_list args) @@ -274,6 +275,9 @@ static inline void dump_stack(void) static inline void printk_trigger_flush(void) { } +static inline void console_replay_all(void) +{ +} #endif bool this_cpu_in_panic(void); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 1e3b21682547..6206804d275b 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -3161,6 +3161,40 @@ void console_unblank(void) pr_flush(1000, true); } +/* + * Rewind all consoles to the oldest available record. + * + * IMPORTANT: The function is safe only when called under + * console_lock(). It is not enforced because + * it is used as a best effort in panic(). + */ +static void __console_rewind_all(void) +{ + struct console *c; + short flags; + int cookie; + u64 seq; + + seq = prb_first_valid_seq(prb); + + cookie = console_srcu_read_lock(); + for_each_console_srcu(c) { + flags = console_srcu_read_flags(c); + + if (flags & CON_NBCON) { + nbcon_seq_force(c, seq); + } else { + /* + * This assignment is safe only when called under + * console_lock(). On panic, legacy consoles are + * only best effort. + */ + c->seq = seq; + } + } + console_srcu_read_unlock(cookie); +} + /** * console_flush_on_panic - flush console content on panic * @mode: flush all messages in buffer or just the pending ones @@ -3189,30 +3223,8 @@ void console_flush_on_panic(enum con_flush_mode mode) */ console_may_schedule = 0; - if (mode == CONSOLE_REPLAY_ALL) { - struct console *c; - short flags; - int cookie; - u64 seq; - - seq = prb_first_valid_seq(prb); - - cookie = console_srcu_read_lock(); - for_each_console_srcu(c) { - flags = console_srcu_read_flags(c); - - if (flags & CON_NBCON) { - nbcon_seq_force(c, seq); - } else { - /* - * This is an unsynchronized assignment. On - * panic legacy consoles are only best effort. - */ - c->seq = seq; - } - } - console_srcu_read_unlock(cookie); - } + if (mode == CONSOLE_REPLAY_ALL) + __console_rewind_all(); console_flush_all(false, &next_seq, &handover); } @@ -4301,6 +4313,23 @@ void kmsg_dump_rewind(struct kmsg_dump_iter *iter) } EXPORT_SYMBOL_GPL(kmsg_dump_rewind); +/** + * console_replay_all - replay kernel log on consoles + * + * Try to obtain lock on console subsystem and replay all + * available records in printk buffer on the consoles. + * Does nothing if lock is not obtained. + * + * Context: Any context. + */ +void console_replay_all(void) +{ + if (console_trylock()) { + __console_rewind_all(); + /* Consoles are flushed as part of console_unlock(). */ + console_unlock(); + } +} #endif #ifdef CONFIG_SMP From 1b743485e27f3d874695434cc8103f557dfdf4b9 Mon Sep 17 00:00:00 2001 From: Sreenath Vijayan Date: Wed, 13 Mar 2024 15:52:52 +0530 Subject: [PATCH 065/113] tty/sysrq: Replay kernel log messages on consoles via sysrq When terminal is unresponsive, one cannot use dmesg to view the printk ring buffer messages. Also, syslog services may be disabled, especially on embedded systems, to check the messages after a reboot. In this scenario, replay the messages in printk ring buffer on consoles via sysrq by pressing sysrq+R. The console loglevel will determine which all kernel log messages are displayed. The messages will be displayed only when console_trylock() succeeds. Users could repeat the sysrq key when it fails. If the owner of console subsystem lock is stuck, repeating the key won't work. Suggested-by: Petr Mladek Signed-off-by: Shimoyashiki Taichi Reviewed-by: John Ogness Signed-off-by: Sreenath Vijayan Link: https://lore.kernel.org/r/cc3b9b1aae60a236c6aed1dc7b0ffa2c7cd1f183.1710220326.git.sreenath.vijayan@sony.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/sysrq.rst | 9 +++++++++ drivers/tty/sysrq.c | 13 ++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/sysrq.rst b/Documentation/admin-guide/sysrq.rst index 2f2e5bd440f9..a85b3384d1e7 100644 --- a/Documentation/admin-guide/sysrq.rst +++ b/Documentation/admin-guide/sysrq.rst @@ -161,6 +161,8 @@ Command Function will be printed to your console. (``0``, for example would make it so that only emergency messages like PANICs or OOPSes would make it to your console.) + +``R`` Replay the kernel log messages on consoles. =========== =================================================================== Okay, so what can I use them for? @@ -211,6 +213,13 @@ processes. "just thaw ``it(j)``" is useful if your system becomes unresponsive due to a frozen (probably root) filesystem via the FIFREEZE ioctl. +``Replay logs(R)`` is useful to view the kernel log messages when system is hung +or you are not able to use dmesg command to view the messages in printk buffer. +User may have to press the key combination multiple times if console system is +busy. If it is completely locked up, then messages won't be printed. Output +messages depend on current console loglevel, which can be modified using +sysrq[0-9] (see above). + Sometimes SysRq seems to get 'stuck' after using it, what can I do? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 02217e3c916b..e5974b8239c9 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -450,6 +450,17 @@ static const struct sysrq_key_op sysrq_unrt_op = { .enable_mask = SYSRQ_ENABLE_RTNICE, }; +static void sysrq_handle_replay_logs(u8 key) +{ + console_replay_all(); +} +static struct sysrq_key_op sysrq_replay_logs_op = { + .handler = sysrq_handle_replay_logs, + .help_msg = "replay-kernel-logs(R)", + .action_msg = "Replay kernel logs on consoles", + .enable_mask = SYSRQ_ENABLE_DUMP, +}; + /* Key Operations table and lock */ static DEFINE_SPINLOCK(sysrq_key_table_lock); @@ -519,7 +530,7 @@ static const struct sysrq_key_op *sysrq_key_table[62] = { NULL, /* O */ NULL, /* P */ NULL, /* Q */ - NULL, /* R */ + &sysrq_replay_logs_op, /* R */ NULL, /* S */ NULL, /* T */ NULL, /* U */ From b20172ca6bf489534892b801a5db41bbf5ceec75 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sat, 13 Apr 2024 13:33:41 +0300 Subject: [PATCH 066/113] serial: core: Fix ifdef for serial base console functions If CONFIG_SERIAL_CORE_CONSOLE is not set, we get build errors. Let's fix the issue by moving the endif after the serial base console functions. Fixes: 4547cd76f08a ("serial: 8250: Fix add preferred console for serial8250_isa_init_ports()") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202404131416.VJljwvkS-lkp@intel.com/ Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20240413103343.24231-1-tony@atomide.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_base_bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c index 281a2d5a7332..73c6ee540c83 100644 --- a/drivers/tty/serial/serial_base_bus.c +++ b/drivers/tty/serial/serial_base_bus.c @@ -219,8 +219,6 @@ static int serial_base_add_one_prefcon(const char *match, const char *dev_name, return ret; } -#endif - #ifdef __sparc__ /* Handle Sparc ttya and ttyb options as done in console_setup() */ @@ -319,6 +317,8 @@ int serial_base_add_preferred_console(struct uart_driver *drv, return serial_base_add_one_prefcon(port_match, drv->dev_name, port->line); } +#endif + #ifdef CONFIG_SERIAL_8250_CONSOLE /* From 35fad98ed25ab9e08c4363f0f74efd612c8eb185 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Tue, 16 Apr 2024 07:48:25 +0200 Subject: [PATCH 067/113] serial: meson+qcom: don't advance the kfifo twice Marek reports, that the -next commit 1788cf6a91d9 (tty: serial: switch from circ_buf to kfifo) broke meson_uart and qcom_geni_serial. The commit mistakenly advanced the kfifo twice: once by uart_fifo_get()/kfifo_out() and second time by uart_xmit_advance(). To advance the fifo only once, drop the superfluous uart_xmit_advance() from both. To count the TX statistics properly, use uart_fifo_out() in qcom_geni_serial (meson_uart_start_tx() already uses that). I checked all other uses of uart_xmit_advance() and they appear correct: either they are finishing DMA transfers or are after peek/linear_ptr (i.e. they do not advance fifo). Signed-off-by: Jiri Slaby (SUSE) Reported-by: Marek Szyprowski Fixes: 1788cf6a91d9 ("tty: serial: switch from circ_buf to kfifo") Cc: Neil Armstrong Cc: Kevin Hilman Cc: Jerome Brunet Cc: Martin Blumenstingl Link: https://lore.kernel.org/r/20240416054825.6211-1-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/meson_uart.c | 1 - drivers/tty/serial/qcom_geni_serial.c | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 4587ed4d4d5d..8eb586ac3b0d 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -162,7 +162,6 @@ static void meson_uart_start_tx(struct uart_port *port) break; writel(ch, port->membase + AML_UART_WFIFO); - uart_xmit_advance(port, 1); } if (!kfifo_is_empty(&tport->xmit_fifo)) { diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 7814982f1921..2bd25afe0d92 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -855,7 +855,6 @@ static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport, unsigned int chunk) { struct qcom_geni_serial_port *port = to_dev_port(uport); - struct tty_port *tport = &uport->state->port; unsigned int tx_bytes, remaining = chunk; u8 buf[BYTES_PER_FIFO_WORD]; @@ -863,8 +862,7 @@ static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport, memset(buf, 0, sizeof(buf)); tx_bytes = min(remaining, BYTES_PER_FIFO_WORD); - tx_bytes = kfifo_out(&tport->xmit_fifo, buf, tx_bytes); - uart_xmit_advance(uport, tx_bytes); + tx_bytes = uart_fifo_out(uport, buf, tx_bytes); iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1); From 5f1149d2f4bff6269f04cceaf1038d6e80d27a62 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Tue, 16 Apr 2024 09:19:04 +0200 Subject: [PATCH 068/113] serial: drop debugging WARN_ON_ONCE() from uart_put_char() Pengfei Xu reports, that the -next commit 1788cf6a91d9 (tty: serial: switch from circ_buf to kfifo) tries to emit a WARNING and that leads to lockdep errors. Obviously we cannot print anything from uart_put_char()! This WARN_ON_ONCE() was/is a debug aid to check if the condition in uart_put_char() can happen at all. Pengfei Xu confirmed it can. Unlike me and kbuild bot in my queue. Second, I completely forgot about it, so I did not remove it in the final version, nor mentioned it in the commit log. Drop it now as we are all good. And we even have stack traces (and a reproducer)! Signed-off-by: Jiri Slaby (SUSE) Reported-by: Pengfei Xu Link: https://lore.kernel.org/r/20240416071904.21440-1-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a78ded8c60b5..5f88c45fbed3 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -558,7 +558,7 @@ static int uart_put_char(struct tty_struct *tty, u8 c) int ret = 0; port = uart_port_lock(state, flags); - if (WARN_ON_ONCE(!state->port.xmit_buf)) { + if (!state->port.xmit_buf) { uart_port_unlock(port, flags); return 0; } From abcd8632f26bbc24c4364b9cdf4feb8c9828c0c6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 9 Apr 2024 20:40:57 +0300 Subject: [PATCH 069/113] serial: core: Extract uart_alloc_xmit_buf() and uart_free_xmit_buf() After conversion to the kfifo, it becomes possible to extract two helper functions for better maintenance and code deduplication. Do it here. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240409174057.1104262-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 98 ++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 5f88c45fbed3..b9d631037ff6 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -243,25 +243,12 @@ static void uart_change_line_settings(struct tty_struct *tty, struct uart_state uart_port_unlock_irq(uport); } -/* - * Startup the port. This will be called once per open. All calls - * will be serialised by the per-port mutex. - */ -static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, - bool init_hw) +static int uart_alloc_xmit_buf(struct tty_port *port) { - struct uart_port *uport = uart_port_check(state); + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport; unsigned long flags; unsigned long page; - int retval = 0; - - if (uport->type == PORT_UNKNOWN) - return 1; - - /* - * Make sure the device is in D0 state. - */ - uart_change_pm(state, UART_PM_STATE_ON); /* * Initialise and allocate the transmit and temporary @@ -271,7 +258,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, if (!page) return -ENOMEM; - uart_port_lock(state, flags); + uport = uart_port_lock(state, flags); if (!state->port.xmit_buf) { state->port.xmit_buf = (unsigned char *)page; kfifo_init(&state->port.xmit_fifo, state->port.xmit_buf, @@ -281,11 +268,58 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, uart_port_unlock(uport, flags); /* * Do not free() the page under the port lock, see - * uart_shutdown(). + * uart_free_xmit_buf(). */ free_page(page); } + return 0; +} + +static void uart_free_xmit_buf(struct tty_port *port) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport; + unsigned long flags; + char *xmit_buf; + + /* + * Do not free() the transmit buffer page under the port lock since + * this can create various circular locking scenarios. For instance, + * console driver may need to allocate/free a debug object, which + * can end up in printk() recursion. + */ + uport = uart_port_lock(state, flags); + xmit_buf = port->xmit_buf; + port->xmit_buf = NULL; + INIT_KFIFO(port->xmit_fifo); + uart_port_unlock(uport, flags); + + free_page((unsigned long)xmit_buf); +} + +/* + * Startup the port. This will be called once per open. All calls + * will be serialised by the per-port mutex. + */ +static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, + bool init_hw) +{ + struct uart_port *uport = uart_port_check(state); + int retval; + + if (uport->type == PORT_UNKNOWN) + return 1; + + /* + * Make sure the device is in D0 state. + */ + uart_change_pm(state, UART_PM_STATE_ON); + + retval = uart_alloc_xmit_buf(&state->port); + if (retval) + return retval; + retval = uport->ops->startup(uport); if (retval == 0) { if (uart_console(uport) && uport->cons->cflag) { @@ -347,8 +381,6 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) { struct uart_port *uport = uart_port_check(state); struct tty_port *port = &state->port; - unsigned long flags; - char *xmit_buf = NULL; /* * Set the TTY IO error marker @@ -381,19 +413,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) */ tty_port_set_suspended(port, false); - /* - * Do not free() the transmit buffer page under the port lock since - * this can create various circular locking scenarios. For instance, - * console driver may need to allocate/free a debug object, which - * can endup in printk() recursion. - */ - uart_port_lock(state, flags); - xmit_buf = port->xmit_buf; - port->xmit_buf = NULL; - INIT_KFIFO(port->xmit_fifo); - uart_port_unlock(uport, flags); - - free_page((unsigned long)xmit_buf); + uart_free_xmit_buf(port); } /** @@ -1747,7 +1767,6 @@ static void uart_tty_port_shutdown(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = uart_port_check(state); - char *buf; /* * At this point, we stop accepting input. To do this, we @@ -1769,16 +1788,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) */ tty_port_set_suspended(port, false); - /* - * Free the transmit buffer. - */ - uart_port_lock_irq(uport); - buf = port->xmit_buf; - port->xmit_buf = NULL; - INIT_KFIFO(port->xmit_fifo); - uart_port_unlock_irq(uport); - - free_page((unsigned long)buf); + uart_free_xmit_buf(port); uart_change_pm(state, UART_PM_STATE_OFF); } From e533e4c62e9993e62e947ae9bbec34e4c7ae81c2 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Thu, 11 Apr 2024 14:19:23 +0200 Subject: [PATCH 070/113] serial: imx: Introduce timeout when waiting on transmitter empty By waiting at most 1 second for USR2_TXDC to be set, we avoid a potential deadlock. In case of the timeout, there is not much we can do, so we simply ignore the transmitter state and optimistically try to continue. Signed-off-by: Esben Haabendal Acked-by: Marc Kleine-Budde Link: https://lore.kernel.org/r/919647898c337a46604edcabaf13d42d80c0915d.1712837613.git.esben@geanix.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 56b76a221082..79f1503cd75b 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -2002,7 +2003,7 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) struct imx_port *sport = imx_uart_ports[co->index]; struct imx_port_ucrs old_ucr; unsigned long flags; - unsigned int ucr1; + unsigned int ucr1, usr2; int locked = 1; if (sport->port.sysrq) @@ -2033,8 +2034,8 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) * Finally, wait for transmitter to become empty * and restore UCR1/2/3 */ - while (!(imx_uart_readl(sport, USR2) & USR2_TXDC)); - + read_poll_timeout_atomic(imx_uart_readl, usr2, usr2 & USR2_TXDC, + 0, USEC_PER_SEC, false, sport, USR2); imx_uart_ucrs_restore(sport, &old_ucr); if (locked) From 5cb90c636d950c23ee63eea31fd03edbae99921f Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Thu, 11 Apr 2024 15:04:48 +0200 Subject: [PATCH 071/113] tty: serial: fsl_lpuart: use dev_err_probe for clocks Clocks might not be available yet when probing lpuart. Silence -517 errors by using dev_err_probe. Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20240411130449.1096090-1-alexander.stein@ew.tq-group.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index ae5e1ecc48fc..615291ea9b5e 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -2879,8 +2879,7 @@ static int lpuart_probe(struct platform_device *pdev) sport->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(sport->ipg_clk)) { ret = PTR_ERR(sport->ipg_clk); - dev_err(&pdev->dev, "failed to get uart ipg clk: %d\n", ret); - return ret; + return dev_err_probe(&pdev->dev, ret, "failed to get uart ipg clk\n"); } sport->baud_clk = NULL; @@ -2888,8 +2887,7 @@ static int lpuart_probe(struct platform_device *pdev) sport->baud_clk = devm_clk_get(&pdev->dev, "baud"); if (IS_ERR(sport->baud_clk)) { ret = PTR_ERR(sport->baud_clk); - dev_err(&pdev->dev, "failed to get uart baud clk: %d\n", ret); - return ret; + return dev_err_probe(&pdev->dev, ret, "failed to get uart baud clk\n"); } } From 6a533ed7350af50322a7e478a6557f1368222ea3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 12 Apr 2024 20:39:31 +0300 Subject: [PATCH 072/113] serial: 8250_dw: Deduplicate LCR checks All callers of dw8250_check_lcr() perform the same check. Deduplicate it by moving them into respective call. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240412173931.187411-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 41 +++++++++++-------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index a3acbf0f5da1..994604c77058 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -100,14 +100,18 @@ static void dw8250_force_idle(struct uart_port *p) (void)p->serial_in(p, UART_RX); } -static void dw8250_check_lcr(struct uart_port *p, int value) +static void dw8250_check_lcr(struct uart_port *p, int offset, int value) { - void __iomem *offset = p->membase + (UART_LCR << p->regshift); + struct dw8250_data *d = to_dw8250_data(p->private_data); + void __iomem *addr = p->membase + (offset << p->regshift); int tries = 1000; + if (offset != UART_LCR || d->uart_16550_compatible) + return; + /* Make sure LCR write wasn't ignored */ while (tries--) { - unsigned int lcr = p->serial_in(p, UART_LCR); + unsigned int lcr = p->serial_in(p, offset); if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) return; @@ -116,15 +120,15 @@ static void dw8250_check_lcr(struct uart_port *p, int value) #ifdef CONFIG_64BIT if (p->type == PORT_OCTEON) - __raw_writeq(value & 0xff, offset); + __raw_writeq(value & 0xff, addr); else #endif if (p->iotype == UPIO_MEM32) - writel(value, offset); + writel(value, addr); else if (p->iotype == UPIO_MEM32BE) - iowrite32be(value, offset); + iowrite32be(value, addr); else - writeb(value, offset); + writeb(value, addr); } /* * FIXME: this deadlocks if port->lock is already held @@ -158,12 +162,8 @@ static void dw8250_tx_wait_empty(struct uart_port *p) static void dw8250_serial_out(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - writeb(value, p->membase + (offset << p->regshift)); - - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } static void dw8250_serial_out38x(struct uart_port *p, int offset, int value) @@ -194,26 +194,19 @@ static unsigned int dw8250_serial_inq(struct uart_port *p, int offset) static void dw8250_serial_outq(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - value &= 0xff; __raw_writeq(value, p->membase + (offset << p->regshift)); /* Read back to ensure register write ordering. */ __raw_readq(p->membase + (UART_LCR << p->regshift)); - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } #endif /* CONFIG_64BIT */ static void dw8250_serial_out32(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - writel(value, p->membase + (offset << p->regshift)); - - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) @@ -225,12 +218,8 @@ static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) static void dw8250_serial_out32be(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - iowrite32be(value, p->membase + (offset << p->regshift)); - - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset) From c205edcd86dac01c205e4eadf6d0d95965e7a566 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 12 Apr 2024 20:39:37 +0300 Subject: [PATCH 073/113] serial: 8250_dw: Hide a cast in dw8250_serial_inq() dw8250_serial_inq() uses unsigned int variable to store 8-bit value. Due to that it needs a cast which can be avoided by switching to use u8 instead of unsigned int. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240412173937.187442-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 994604c77058..db0e67186a8b 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -185,9 +185,7 @@ static unsigned int dw8250_serial_in(struct uart_port *p, int offset) #ifdef CONFIG_64BIT static unsigned int dw8250_serial_inq(struct uart_port *p, int offset) { - unsigned int value; - - value = (u8)__raw_readq(p->membase + (offset << p->regshift)); + u8 value = __raw_readq(p->membase + (offset << p->regshift)); return dw8250_modify_msr(p, offset, value); } From 2a49b45cd0e7e8c9a0cd5e2f3993b558469ed744 Mon Sep 17 00:00:00 2001 From: Guanbing Huang Date: Tue, 16 Apr 2024 11:16:18 +0800 Subject: [PATCH 074/113] PNP: Add dev_is_pnp() macro Add dev_is_pnp() macro to determine whether the device is a PNP device. Signed-off-by: Guanbing Huang Suggested-by: Andy Shevchenko Reviewed-by: Bing Fan Tested-by: Linheng Du Reviewed-by: Andy Shevchenko Acked-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/4e68f5557ad53b671ca8103e572163eca52a8f29.1713234515.git.albanhuang@tencent.com Signed-off-by: Greg Kroah-Hartman --- include/linux/pnp.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/pnp.h b/include/linux/pnp.h index ddbe7c3ca4ce..82561242cda4 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -469,6 +469,8 @@ int compare_pnp_id(struct pnp_id *pos, const char *id); int pnp_register_driver(struct pnp_driver *drv); void pnp_unregister_driver(struct pnp_driver *drv); +#define dev_is_pnp(d) ((d)->bus == &pnp_bus_type) + #else /* device management */ @@ -500,6 +502,8 @@ static inline int compare_pnp_id(struct pnp_id *pos, const char *id) { return -E static inline int pnp_register_driver(struct pnp_driver *drv) { return -ENODEV; } static inline void pnp_unregister_driver(struct pnp_driver *drv) { } +#define dev_is_pnp(d) false + #endif /* CONFIG_PNP */ /** From 18ba7f2d99f698251294fe9521da3f00d03f96aa Mon Sep 17 00:00:00 2001 From: Guanbing Huang Date: Tue, 16 Apr 2024 11:16:37 +0800 Subject: [PATCH 075/113] serial: port: Add support of PNP IRQ to __uart_read_properties() The function __uart_read_properties doesn't cover PNP devices, so add IRQ processing for PNP devices in the branch. Signed-off-by: Guanbing Huang Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Reviewed-by: Bing Fan Tested-by: Linheng Du Link: https://lore.kernel.org/r/7f4ca31ef1cab4c6ecad22fafd82117686b696be.1713234515.git.albanhuang@tencent.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_port.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c index 3408c8827561..47dd8c3fd06d 100644 --- a/drivers/tty/serial/serial_port.c +++ b/drivers/tty/serial/serial_port.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -221,7 +222,11 @@ static int __uart_read_properties(struct uart_port *port, bool use_defaults) if (dev_is_platform(dev)) ret = platform_get_irq(to_platform_device(dev), 0); - else + else if (dev_is_pnp(dev)) { + ret = pnp_irq(to_pnp_dev(dev), 0); + if (ret < 0) + ret = -ENXIO; + } else ret = fwnode_irq_get(dev_fwnode(dev), 0); if (ret == -EPROBE_DEFER) return ret; From 64c79dfbc45863821e172a606f453b39c8a8be6b Mon Sep 17 00:00:00 2001 From: Guanbing Huang Date: Tue, 16 Apr 2024 11:16:59 +0800 Subject: [PATCH 076/113] serial: 8250_pnp: Support configurable reg shift property The 16550a serial port based on the ACPI table requires obtaining the reg-shift attribute. In the ACPI scenario, If the reg-shift property is not configured like in DTS, the 16550a serial driver cannot read or write controller registers properly during initialization. Signed-off-by: Guanbing Huang Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Reviewed-by: Bing Fan Tested-by: Linheng Du Link: https://lore.kernel.org/r/4726ecea8f7bfbfe42501b4f6ad9fe5b38994574.1713234515.git.albanhuang@tencent.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pnp.c | 40 +++++++++++++++++++----------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 1974bbadc975..8f72a7de1d1d 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -435,6 +435,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { struct uart_8250_port uart, *port; int ret, line, flags = dev_id->driver_data; + unsigned char iotype; if (flags & UNKNOWN_DEV) { ret = serial_pnp_guess_board(dev); @@ -443,37 +444,46 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) } memset(&uart, 0, sizeof(uart)); - if (pnp_irq_valid(dev, 0)) - uart.port.irq = pnp_irq(dev, 0); if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) { uart.port.iobase = pnp_port_start(dev, 2); - uart.port.iotype = UPIO_PORT; + iotype = UPIO_PORT; } else if (pnp_port_valid(dev, 0)) { uart.port.iobase = pnp_port_start(dev, 0); - uart.port.iotype = UPIO_PORT; + iotype = UPIO_PORT; } else if (pnp_mem_valid(dev, 0)) { uart.port.mapbase = pnp_mem_start(dev, 0); - uart.port.iotype = UPIO_MEM; + uart.port.mapsize = pnp_mem_len(dev, 0); + iotype = UPIO_MEM; uart.port.flags = UPF_IOREMAP; } else return -ENODEV; - dev_dbg(&dev->dev, - "Setup PNP port: port %#lx, mem %#llx, irq %u, type %u\n", - uart.port.iobase, (unsigned long long)uart.port.mapbase, - uart.port.irq, uart.port.iotype); + uart.port.uartclk = 1843200; + uart.port.dev = &dev->dev; + uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + + ret = uart_read_port_properties(&uart.port); + /* no interrupt -> fall back to polling */ + if (ret == -ENXIO) + ret = 0; + if (ret) + return ret; + + /* + * The previous call may not set iotype correctly when reg-io-width + * property is absent and it doesn't support IO port resource. + */ + uart.port.iotype = iotype; if (flags & CIR_PORT) { uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; uart.port.type = PORT_8250_CIR; } - uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; - if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) - uart.port.flags |= UPF_SHARE_IRQ; - uart.port.uartclk = 1843200; - device_property_read_u32(&dev->dev, "clock-frequency", &uart.port.uartclk); - uart.port.dev = &dev->dev; + dev_dbg(&dev->dev, + "Setup PNP port: port %#lx, mem %#llx, size %#llx, irq %u, type %u\n", + uart.port.iobase, (unsigned long long)uart.port.mapbase, + (unsigned long long)uart.port.mapsize, uart.port.irq, uart.port.iotype); line = serial8250_register_8250_port(&uart); if (line < 0 || (flags & CIR_PORT)) From b86ae40ffcf5a16b9569b1016da4a08c4f352ca2 Mon Sep 17 00:00:00 2001 From: Parker Newman Date: Tue, 16 Apr 2024 08:55:28 -0400 Subject: [PATCH 077/113] serial: exar: adding missing CTI and Exar PCI ids - Added Connect Tech and Exar IDs not already in pci_ids.h Signed-off-by: Parker Newman Link: https://lore.kernel.org/r/7c3d8e795a864dd9b0a00353b722060dc27c4e09.1713270624.git.pnewman@connecttech.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index f89dabfc0f97..604e5a292d4e 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -46,8 +46,50 @@ #define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 #define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 +#define PCI_VENDOR_ID_CONNECT_TECH 0x12c4 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_SP_OPTO 0x0340 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_A 0x0341 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_B 0x0342 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS 0x0350 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A 0x0351 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B 0x0352 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS 0x0353 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A 0x0354 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B 0x0355 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO 0x0360 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A 0x0361 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B 0x0362 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP 0x0370 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232 0x0371 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485 0x0372 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP 0x0373 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP 0x0374 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP 0x0375 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS 0x0376 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT 0x0380 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT 0x0381 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO 0x0382 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO 0x0392 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP 0x03A0 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232 0x03A1 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485 0x03A2 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS 0x03A3 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XEG001 0x0602 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_BASE 0x1000 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_2 0x1002 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_4 0x1004 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_8 0x1008 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_12 0x100C +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_16 0x1010 +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X 0x110c +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X 0x110d +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16 0x1110 + #define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 #define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 +#define PCI_DEVICE_ID_EXAR_XR17V252 0x0252 +#define PCI_DEVICE_ID_EXAR_XR17V254 0x0254 +#define PCI_DEVICE_ID_EXAR_XR17V258 0x0258 #define PCI_SUBDEVICE_ID_USR_2980 0x0128 #define PCI_SUBDEVICE_ID_USR_2981 0x0129 From 8c467f3300591a206fa8dcc6988d768910799872 Mon Sep 17 00:00:00 2001 From: Alexey Gladkov Date: Wed, 17 Apr 2024 19:37:35 +0200 Subject: [PATCH 078/113] VT: Use macros to define ioctls All other headers use _IOC() macros to describe ioctls for a long time now. This header is stuck in the last century. Simply use the _IO() macro. No other changes. Signed-off-by: Alexey Gladkov Link: https://lore.kernel.org/r/e4229fe2933a003341e338b558ab1ea8b63a51f6.1713375378.git.legion@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/kd.h | 96 +++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/include/uapi/linux/kd.h b/include/uapi/linux/kd.h index 6b384065c013..8ddb2219a84b 100644 --- a/include/uapi/linux/kd.h +++ b/include/uapi/linux/kd.h @@ -5,60 +5,61 @@ #include /* 0x4B is 'K', to avoid collision with termios and vt */ +#define KD_IOCTL_BASE 'K' -#define GIO_FONT 0x4B60 /* gets font in expanded form */ -#define PIO_FONT 0x4B61 /* use font in expanded form */ +#define GIO_FONT _IO(KD_IOCTL_BASE, 0x60) /* gets font in expanded form */ +#define PIO_FONT _IO(KD_IOCTL_BASE, 0x61) /* use font in expanded form */ -#define GIO_FONTX 0x4B6B /* get font using struct consolefontdesc */ -#define PIO_FONTX 0x4B6C /* set font using struct consolefontdesc */ +#define GIO_FONTX _IO(KD_IOCTL_BASE, 0x6B) /* get font using struct consolefontdesc */ +#define PIO_FONTX _IO(KD_IOCTL_BASE, 0x6C) /* set font using struct consolefontdesc */ struct consolefontdesc { unsigned short charcount; /* characters in font (256 or 512) */ unsigned short charheight; /* scan lines per character (1-32) */ char __user *chardata; /* font data in expanded form */ }; -#define PIO_FONTRESET 0x4B6D /* reset to default font */ +#define PIO_FONTRESET _IO(KD_IOCTL_BASE, 0x6D) /* reset to default font */ -#define GIO_CMAP 0x4B70 /* gets colour palette on VGA+ */ -#define PIO_CMAP 0x4B71 /* sets colour palette on VGA+ */ +#define GIO_CMAP _IO(KD_IOCTL_BASE, 0x70) /* gets colour palette on VGA+ */ +#define PIO_CMAP _IO(KD_IOCTL_BASE, 0x71) /* sets colour palette on VGA+ */ -#define KIOCSOUND 0x4B2F /* start sound generation (0 for off) */ -#define KDMKTONE 0x4B30 /* generate tone */ +#define KIOCSOUND _IO(KD_IOCTL_BASE, 0x2F) /* start sound generation (0 for off) */ +#define KDMKTONE _IO(KD_IOCTL_BASE, 0x30) /* generate tone */ -#define KDGETLED 0x4B31 /* return current led state */ -#define KDSETLED 0x4B32 /* set led state [lights, not flags] */ +#define KDGETLED _IO(KD_IOCTL_BASE, 0x31) /* return current led state */ +#define KDSETLED _IO(KD_IOCTL_BASE, 0x32) /* set led state [lights, not flags] */ #define LED_SCR 0x01 /* scroll lock led */ #define LED_NUM 0x02 /* num lock led */ #define LED_CAP 0x04 /* caps lock led */ -#define KDGKBTYPE 0x4B33 /* get keyboard type */ +#define KDGKBTYPE _IO(KD_IOCTL_BASE, 0x33) /* get keyboard type */ #define KB_84 0x01 #define KB_101 0x02 /* this is what we always answer */ #define KB_OTHER 0x03 -#define KDADDIO 0x4B34 /* add i/o port as valid */ -#define KDDELIO 0x4B35 /* del i/o port as valid */ -#define KDENABIO 0x4B36 /* enable i/o to video board */ -#define KDDISABIO 0x4B37 /* disable i/o to video board */ +#define KDADDIO _IO(KD_IOCTL_BASE, 0x34) /* add i/o port as valid */ +#define KDDELIO _IO(KD_IOCTL_BASE, 0x35) /* del i/o port as valid */ +#define KDENABIO _IO(KD_IOCTL_BASE, 0x36) /* enable i/o to video board */ +#define KDDISABIO _IO(KD_IOCTL_BASE, 0x37) /* disable i/o to video board */ -#define KDSETMODE 0x4B3A /* set text/graphics mode */ +#define KDSETMODE _IO(KD_IOCTL_BASE, 0x3A) /* set text/graphics mode */ #define KD_TEXT 0x00 #define KD_GRAPHICS 0x01 #define KD_TEXT0 0x02 /* obsolete */ #define KD_TEXT1 0x03 /* obsolete */ -#define KDGETMODE 0x4B3B /* get current mode */ +#define KDGETMODE _IO(KD_IOCTL_BASE, 0x3B) /* get current mode */ -#define KDMAPDISP 0x4B3C /* map display into address space */ -#define KDUNMAPDISP 0x4B3D /* unmap display from address space */ +#define KDMAPDISP _IO(KD_IOCTL_BASE, 0x3C) /* map display into address space */ +#define KDUNMAPDISP _IO(KD_IOCTL_BASE, 0x3D) /* unmap display from address space */ typedef char scrnmap_t; #define E_TABSZ 256 -#define GIO_SCRNMAP 0x4B40 /* get screen mapping from kernel */ -#define PIO_SCRNMAP 0x4B41 /* put screen mapping table in kernel */ -#define GIO_UNISCRNMAP 0x4B69 /* get full Unicode screen mapping */ -#define PIO_UNISCRNMAP 0x4B6A /* set full Unicode screen mapping */ +#define GIO_SCRNMAP _IO(KD_IOCTL_BASE, 0x40) /* get screen mapping from kernel */ +#define PIO_SCRNMAP _IO(KD_IOCTL_BASE, 0x41) /* put screen mapping table in kernel */ +#define GIO_UNISCRNMAP _IO(KD_IOCTL_BASE, 0x69) /* get full Unicode screen mapping */ +#define PIO_UNISCRNMAP _IO(KD_IOCTL_BASE, 0x6A) /* set full Unicode screen mapping */ -#define GIO_UNIMAP 0x4B66 /* get unicode-to-font mapping from kernel */ +#define GIO_UNIMAP _IO(KD_IOCTL_BASE, 0x66) /* get unicode-to-font mapping from kernel */ struct unipair { unsigned short unicode; unsigned short fontpos; @@ -67,8 +68,8 @@ struct unimapdesc { unsigned short entry_ct; struct unipair __user *entries; }; -#define PIO_UNIMAP 0x4B67 /* put unicode-to-font mapping in kernel */ -#define PIO_UNIMAPCLR 0x4B68 /* clear table, possibly advise hash algorithm */ +#define PIO_UNIMAP _IO(KD_IOCTL_BASE, 0x67) /* put unicode-to-font mapping in kernel */ +#define PIO_UNIMAPCLR _IO(KD_IOCTL_BASE, 0x68) /* clear table, possibly advise hash algorithm */ struct unimapinit { unsigned short advised_hashsize; /* 0 if no opinion */ unsigned short advised_hashstep; /* 0 if no opinion */ @@ -83,19 +84,19 @@ struct unimapinit { #define K_MEDIUMRAW 0x02 #define K_UNICODE 0x03 #define K_OFF 0x04 -#define KDGKBMODE 0x4B44 /* gets current keyboard mode */ -#define KDSKBMODE 0x4B45 /* sets current keyboard mode */ +#define KDGKBMODE _IO(KD_IOCTL_BASE, 0x44) /* gets current keyboard mode */ +#define KDSKBMODE _IO(KD_IOCTL_BASE, 0x45) /* sets current keyboard mode */ #define K_METABIT 0x03 #define K_ESCPREFIX 0x04 -#define KDGKBMETA 0x4B62 /* gets meta key handling mode */ -#define KDSKBMETA 0x4B63 /* sets meta key handling mode */ +#define KDGKBMETA _IO(KD_IOCTL_BASE, 0x62) /* gets meta key handling mode */ +#define KDSKBMETA _IO(KD_IOCTL_BASE, 0x63) /* sets meta key handling mode */ #define K_SCROLLLOCK 0x01 #define K_NUMLOCK 0x02 #define K_CAPSLOCK 0x04 -#define KDGKBLED 0x4B64 /* get led flags (not lights) */ -#define KDSKBLED 0x4B65 /* set led flags (not lights) */ +#define KDGKBLED _IO(KD_IOCTL_BASE, 0x64) /* get led flags (not lights) */ +#define KDSKBLED _IO(KD_IOCTL_BASE, 0x65) /* set led flags (not lights) */ struct kbentry { unsigned char kb_table; @@ -107,15 +108,15 @@ struct kbentry { #define K_ALTTAB 0x02 #define K_ALTSHIFTTAB 0x03 -#define KDGKBENT 0x4B46 /* gets one entry in translation table */ -#define KDSKBENT 0x4B47 /* sets one entry in translation table */ +#define KDGKBENT _IO(KD_IOCTL_BASE, 0x46) /* gets one entry in translation table */ +#define KDSKBENT _IO(KD_IOCTL_BASE, 0x47) /* sets one entry in translation table */ struct kbsentry { unsigned char kb_func; unsigned char kb_string[512]; }; -#define KDGKBSENT 0x4B48 /* gets one function key string entry */ -#define KDSKBSENT 0x4B49 /* sets one function key string entry */ +#define KDGKBSENT _IO(KD_IOCTL_BASE, 0x48) /* gets one function key string entry */ +#define KDSKBSENT _IO(KD_IOCTL_BASE, 0x49) /* sets one function key string entry */ struct kbdiacr { unsigned char diacr, base, result; @@ -124,8 +125,8 @@ struct kbdiacrs { unsigned int kb_cnt; /* number of entries in following array */ struct kbdiacr kbdiacr[256]; /* MAX_DIACR from keyboard.h */ }; -#define KDGKBDIACR 0x4B4A /* read kernel accent table */ -#define KDSKBDIACR 0x4B4B /* write kernel accent table */ +#define KDGKBDIACR _IO(KD_IOCTL_BASE, 0x4A) /* read kernel accent table */ +#define KDSKBDIACR _IO(KD_IOCTL_BASE, 0x4B) /* write kernel accent table */ struct kbdiacruc { unsigned int diacr, base, result; @@ -134,16 +135,16 @@ struct kbdiacrsuc { unsigned int kb_cnt; /* number of entries in following array */ struct kbdiacruc kbdiacruc[256]; /* MAX_DIACR from keyboard.h */ }; -#define KDGKBDIACRUC 0x4BFA /* read kernel accent table - UCS */ -#define KDSKBDIACRUC 0x4BFB /* write kernel accent table - UCS */ +#define KDGKBDIACRUC _IO(KD_IOCTL_BASE, 0xFA) /* read kernel accent table - UCS */ +#define KDSKBDIACRUC _IO(KD_IOCTL_BASE, 0xFB) /* write kernel accent table - UCS */ struct kbkeycode { unsigned int scancode, keycode; }; -#define KDGETKEYCODE 0x4B4C /* read kernel keycode table entry */ -#define KDSETKEYCODE 0x4B4D /* write kernel keycode table entry */ +#define KDGETKEYCODE _IO(KD_IOCTL_BASE, 0x4C) /* read kernel keycode table entry */ +#define KDSETKEYCODE _IO(KD_IOCTL_BASE, 0x4D) /* write kernel keycode table entry */ -#define KDSIGACCEPT 0x4B4E /* accept kbd generated signals */ +#define KDSIGACCEPT _IO(KD_IOCTL_BASE, 0x4E) /* accept kbd generated signals */ struct kbd_repeat { int delay; /* in msec; <= 0: don't change */ @@ -151,10 +152,11 @@ struct kbd_repeat { /* earlier this field was misnamed "rate" */ }; -#define KDKBDREP 0x4B52 /* set keyboard delay/repeat rate; - * actually used values are returned */ +#define KDKBDREP _IO(KD_IOCTL_BASE, 0x52) /* set keyboard delay/repeat rate; + * actually used values are returned + */ -#define KDFONTOP 0x4B72 /* font operations */ +#define KDFONTOP _IO(KD_IOCTL_BASE, 0x72) /* font operations */ struct console_font_op { unsigned int op; /* operation code KD_FONT_OP_* */ From c69fddf12ffcf375d24ccc32c94546d56b1ba2e8 Mon Sep 17 00:00:00 2001 From: Parker Newman Date: Wed, 17 Apr 2024 16:31:23 -0400 Subject: [PATCH 079/113] serial: exar: remove old Connect Tech setup Preparatory patch removing existing Connect Tech setup code and CONNECT_DEVICE macro. Subsequent patches in this series will add in new UART family specific setup code and new device definition macros to allow for supporting CTI serial cards with Exar PCI IDs and CTI PCI IDs. Signed-off-by: Parker Newman Link: https://lore.kernel.org/r/06a04b6c683ca20c50646cc0836be869c2dacd87.1713382717.git.pnewman@connecttech.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 37 ----------------------------- 1 file changed, 37 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 604e5a292d4e..04ce5e8ddb24 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -358,17 +358,6 @@ pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, return 0; } -static int -pci_connect_tech_setup(struct exar8250 *priv, struct pci_dev *pcidev, - struct uart_8250_port *port, int idx) -{ - unsigned int offset = idx * 0x200; - unsigned int baud = 1843200; - - port->port.uartclk = baud * 16; - return default_setup(priv, pcidev, idx, offset, port); -} - static int pci_xr17c154_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx) @@ -849,10 +838,6 @@ static const struct exar8250_board pbn_fastcom335_8 = { .setup = pci_fastcom335_setup, }; -static const struct exar8250_board pbn_connect = { - .setup = pci_connect_tech_setup, -}; - static const struct exar8250_board pbn_exar_ibm_saturn = { .num_ports = 1, .setup = pci_xr17c154_setup, @@ -897,15 +882,6 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { .exit = pci_xr17v35x_exit, }; -#define CONNECT_DEVICE(devid, sdevid, bd) { \ - PCI_DEVICE_SUB( \ - PCI_VENDOR_ID_EXAR, \ - PCI_DEVICE_ID_EXAR_##devid, \ - PCI_SUBVENDOR_ID_CONNECT_TECH, \ - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_##sdevid), 0, 0, \ - (kernel_ulong_t)&bd \ - } - #define EXAR_DEVICE(vend, devid, bd) { PCI_DEVICE_DATA(vend, devid, &bd) } #define IBM_DEVICE(devid, sdevid, bd) { \ @@ -935,19 +911,6 @@ static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(ACCESSIO, COM_4SM, pbn_exar_XR17C15x), EXAR_DEVICE(ACCESSIO, COM_8SM, pbn_exar_XR17C15x), - CONNECT_DEVICE(XR17C152, UART_2_232, pbn_connect), - CONNECT_DEVICE(XR17C154, UART_4_232, pbn_connect), - CONNECT_DEVICE(XR17C158, UART_8_232, pbn_connect), - CONNECT_DEVICE(XR17C152, UART_1_1, pbn_connect), - CONNECT_DEVICE(XR17C154, UART_2_2, pbn_connect), - CONNECT_DEVICE(XR17C158, UART_4_4, pbn_connect), - CONNECT_DEVICE(XR17C152, UART_2, pbn_connect), - CONNECT_DEVICE(XR17C154, UART_4, pbn_connect), - CONNECT_DEVICE(XR17C158, UART_8, pbn_connect), - CONNECT_DEVICE(XR17C152, UART_2_485, pbn_connect), - CONNECT_DEVICE(XR17C154, UART_4_485, pbn_connect), - CONNECT_DEVICE(XR17C158, UART_8_485, pbn_connect), - IBM_DEVICE(XR17C152, SATURN_SERIAL_ONE_PORT, pbn_exar_ibm_saturn), /* USRobotics USR298x-OEM PCI Modems */ From 477f6ee694fbd07047fecd37a9992bbe22a36323 Mon Sep 17 00:00:00 2001 From: Parker Newman Date: Wed, 17 Apr 2024 16:31:24 -0400 Subject: [PATCH 080/113] serial: exar: added a exar_get_nr_ports function Moved code for getting number of ports from exar_pci_probe() to a separate exar_get_nr_ports() function. CTI specific code will be added in another patch in this series. Signed-off-by: Parker Newman Link: https://lore.kernel.org/r/33f2bf66bc334573c10cf670a299ecef0b7264bc.1713382717.git.pnewman@connecttech.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 04ce5e8ddb24..72385c7d2eda 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -704,6 +704,21 @@ static irqreturn_t exar_misc_handler(int irq, void *data) return IRQ_HANDLED; } +static unsigned int exar_get_nr_ports(struct exar8250_board *board, + struct pci_dev *pcidev) +{ + unsigned int nr_ports = 0; + + if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO) + nr_ports = BIT(((pcidev->device & 0x38) >> 3) - 1); + else if (board->num_ports) + nr_ports = board->num_ports; + else + nr_ports = pcidev->device & 0x0f; + + return nr_ports; +} + static int exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { @@ -723,12 +738,12 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) maxnr = pci_resource_len(pcidev, bar) >> (board->reg_shift + 3); - if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO) - nr_ports = BIT(((pcidev->device & 0x38) >> 3) - 1); - else if (board->num_ports) - nr_ports = board->num_ports; - else - nr_ports = pcidev->device & 0x0f; + nr_ports = exar_get_nr_ports(board, pcidev); + if (nr_ports == 0) { + dev_err_probe(&pcidev->dev, -ENODEV, + "failed to get number of ports\n"); + return -ENODEV; + } priv = devm_kzalloc(&pcidev->dev, struct_size(priv, line, nr_ports), GFP_KERNEL); if (!priv) From 393b520a99b21d9ec6d4bb32e7c05bbb4f6dc777 Mon Sep 17 00:00:00 2001 From: Parker Newman Date: Wed, 17 Apr 2024 16:31:25 -0400 Subject: [PATCH 081/113] serial: exar: add optional board_init function Add an optional "board_init()" function pointer to struct exar8250_board which is called once during probe prior to setting up the ports. It will be used in subsequent patches of this series. Signed-off-by: Parker Newman Link: https://lore.kernel.org/r/0e72a3154114c733283ff273bc1e31456ee101f4.1713382717.git.pnewman@connecttech.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 72385c7d2eda..f14f73d250bb 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -177,12 +177,14 @@ struct exar8250_platform { * struct exar8250_board - board information * @num_ports: number of serial ports * @reg_shift: describes UART register mapping in PCI memory - * @setup: quirk run at ->probe() stage + * @board_init: quirk run once at ->probe() stage before setting up ports + * @setup: quirk run at ->probe() stage for each port * @exit: quirk run at ->remove() stage */ struct exar8250_board { unsigned int num_ports; unsigned int reg_shift; + int (*board_init)(struct exar8250 *priv, struct pci_dev *pcidev); int (*setup)(struct exar8250 *, struct pci_dev *, struct uart_8250_port *, int); void (*exit)(struct pci_dev *pcidev); @@ -773,6 +775,15 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) if (rc) return rc; + if (board->board_init) { + rc = board->board_init(priv, pcidev); + if (rc) { + dev_err_probe(&pcidev->dev, rc, + "failed to init serial board\n"); + return rc; + } + } + for (i = 0; i < nr_ports && i < maxnr; i++) { rc = board->setup(priv, pcidev, &uart, i); if (rc) { From 209a20d4bd91761f801116bb155758e45e9340a8 Mon Sep 17 00:00:00 2001 From: Parker Newman Date: Wed, 17 Apr 2024 16:31:26 -0400 Subject: [PATCH 082/113] serial: exar: moved generic_rs485 further up in 8250_exar.c Preparatory patch moving generic_rs485_config and generic_rs485_supported higher in the file to allow for CTI setup functions to use them. Signed-off-by: Parker Newman Link: https://lore.kernel.org/r/a7bf2a42de759908c058246ec15f60bde9a5dc65.1713382717.git.pnewman@connecttech.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 50 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index f14f73d250bb..e68029a59122 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -197,6 +197,31 @@ struct exar8250 { int line[]; }; +static int generic_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); + u8 __iomem *p = port->membase; + u8 value; + + value = readb(p + UART_EXAR_FCTR); + if (is_rs485) + value |= UART_FCTR_EXAR_485; + else + value &= ~UART_FCTR_EXAR_485; + + writeb(value, p + UART_EXAR_FCTR); + + if (is_rs485) + writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); + + return 0; +} + +static const struct serial_rs485 generic_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND, +}; + static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old) { /* @@ -459,27 +484,6 @@ static void xr17v35x_unregister_gpio(struct uart_8250_port *port) port->port.private_data = NULL; } -static int generic_rs485_config(struct uart_port *port, struct ktermios *termios, - struct serial_rs485 *rs485) -{ - bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); - u8 __iomem *p = port->membase; - u8 value; - - value = readb(p + UART_EXAR_FCTR); - if (is_rs485) - value |= UART_FCTR_EXAR_485; - else - value &= ~UART_FCTR_EXAR_485; - - writeb(value, p + UART_EXAR_FCTR); - - if (is_rs485) - writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); - - return 0; -} - static int sealevel_rs485_config(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485) { @@ -518,10 +522,6 @@ static int sealevel_rs485_config(struct uart_port *port, struct ktermios *termio return 0; } -static const struct serial_rs485 generic_rs485_supported = { - .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND, -}; - static const struct exar8250_platform exar8250_default_platform = { .register_gpio = xr17v35x_register_gpio, .unregister_gpio = xr17v35x_unregister_gpio, From 5aa84fd8d0592ee72387bd807a052a52abaad360 Mon Sep 17 00:00:00 2001 From: Parker Newman Date: Wed, 17 Apr 2024 16:31:27 -0400 Subject: [PATCH 083/113] serial: exar: add CTI cards to exar_get_nr_ports Add code for getting number of ports of CTI cards to exar_get_nr_ports(). Signed-off-by: Parker Newman Link: https://lore.kernel.org/r/0c64bdf852f39aec966b38696695d951e485d7e6.1713382717.git.pnewman@connecttech.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index e68029a59122..197f45e306ff 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -711,12 +711,28 @@ static unsigned int exar_get_nr_ports(struct exar8250_board *board, { unsigned int nr_ports = 0; - if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO) + if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO) { nr_ports = BIT(((pcidev->device & 0x38) >> 3) - 1); - else if (board->num_ports) + } else if (board->num_ports > 0) { + // Check if board struct overrides number of ports nr_ports = board->num_ports; - else + } else if (pcidev->vendor == PCI_VENDOR_ID_EXAR) { + // Exar encodes # ports in last nibble of PCI Device ID ex. 0358 nr_ports = pcidev->device & 0x0f; + } else if (pcidev->vendor == PCI_VENDOR_ID_CONNECT_TECH) { + // Handle CTI FPGA cards + switch (pcidev->device) { + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X: + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X: + nr_ports = 12; + break; + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16: + nr_ports = 16; + break; + default: + break; + } + } return nr_ports; } From f7ce07062988a26b83ca1448df8d1e4a9232d962 Mon Sep 17 00:00:00 2001 From: Parker Newman Date: Wed, 17 Apr 2024 16:31:28 -0400 Subject: [PATCH 084/113] serial: exar: add CTI specific setup code This is a large patch but is only additions. All changes and removals are made in previous patches in this series. - Add CTI board_init and port setup functions for each UART type - Add CTI_EXAR_DEVICE() and CTI_PCI_DEVICE() macros - Add support for reading a word from the Exar EEPROM. - Add support for configuring and setting a single MPIO - Add various helper functions for CTI boards. - Add osc_freq to struct exar8250 Signed-off-by: Parker Newman Link: https://lore.kernel.org/r/ae4a66e7342b686cb8d4b15317585dfb37222cf4.1713382717.git.pnewman@connecttech.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 846 ++++++++++++++++++++++++++++ 1 file changed, 846 insertions(+) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 197f45e306ff..6985aabe13cc 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -128,6 +129,19 @@ #define UART_EXAR_DLD 0x02 /* Divisor Fractional */ #define UART_EXAR_DLD_485_POLARITY 0x80 /* RS-485 Enable Signal Polarity */ +/* EEPROM registers */ +#define UART_EXAR_REGB 0x8e +#define UART_EXAR_REGB_EECK BIT(4) +#define UART_EXAR_REGB_EECS BIT(5) +#define UART_EXAR_REGB_EEDI BIT(6) +#define UART_EXAR_REGB_EEDO BIT(7) +#define UART_EXAR_REGB_EE_ADDR_SIZE 6 +#define UART_EXAR_REGB_EE_DATA_SIZE 16 + +#define UART_EXAR_XR17C15X_PORT_OFFSET 0x200 +#define UART_EXAR_XR17V25X_PORT_OFFSET 0x200 +#define UART_EXAR_XR17V35X_PORT_OFFSET 0x400 + /* * IOT2040 MPIO wiring semantics: * @@ -163,6 +177,52 @@ #define IOT2040_UARTS_ENABLE 0x03 #define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */ +/* CTI EEPROM offsets */ +#define CTI_EE_OFF_XR17C15X_OSC_FREQ 0x04 /* 2 words */ +#define CTI_EE_OFF_XR17V25X_OSC_FREQ 0x08 /* 2 words */ +#define CTI_EE_OFF_XR17C15X_PART_NUM 0x0A /* 4 words */ +#define CTI_EE_OFF_XR17V25X_PART_NUM 0x0E /* 4 words */ +#define CTI_EE_OFF_XR17C15X_SERIAL_NUM 0x0E /* 1 word */ +#define CTI_EE_OFF_XR17V25X_SERIAL_NUM 0x12 /* 1 word */ +#define CTI_EE_OFF_XR17V35X_SERIAL_NUM 0x11 /* 2 word */ +#define CTI_EE_OFF_XR17V35X_BRD_FLAGS 0x13 /* 1 word */ +#define CTI_EE_OFF_XR17V35X_PORT_FLAGS 0x14 /* 1 word */ + +#define CTI_EE_MASK_PORT_FLAGS_TYPE GENMASK(7, 0) +#define CTI_EE_MASK_OSC_FREQ_LOWER GENMASK(15, 0) +#define CTI_EE_MASK_OSC_FREQ_UPPER GENMASK(31, 16) + +#define CTI_FPGA_RS485_IO_REG 0x2008 +#define CTI_FPGA_CFG_INT_EN_REG 0x48 +#define CTI_FPGA_CFG_INT_EN_EXT_BIT BIT(15) /* External int enable bit */ + +#define CTI_DEFAULT_PCI_OSC_FREQ 29491200 +#define CTI_DEFAULT_PCIE_OSC_FREQ 125000000 +#define CTI_DEFAULT_FPGA_OSC_FREQ 33333333 + +/* + * CTI Serial port line types. These match the values stored in the first + * nibble of the CTI EEPROM port_flags word. + */ +enum cti_port_type { + CTI_PORT_TYPE_NONE = 0, + CTI_PORT_TYPE_RS232, // RS232 ONLY + CTI_PORT_TYPE_RS422_485, // RS422/RS485 ONLY + CTI_PORT_TYPE_RS232_422_485_HW, // RS232/422/485 HW ONLY Switchable + CTI_PORT_TYPE_RS232_422_485_SW, // RS232/422/485 SW ONLY Switchable + CTI_PORT_TYPE_RS232_422_485_4B, // RS232/422/485 HW/SW (4bit ex. BCG004) + CTI_PORT_TYPE_RS232_422_485_2B, // RS232/422/485 HW/SW (2bit ex. BBG008) + CTI_PORT_TYPE_MAX, +}; + +#define CTI_PORT_TYPE_VALID(_port_type) \ + (((_port_type) > CTI_PORT_TYPE_NONE) && \ + ((_port_type) < CTI_PORT_TYPE_MAX)) + +#define CTI_PORT_TYPE_RS485(_port_type) \ + (((_port_type) > CTI_PORT_TYPE_RS232) && \ + ((_port_type) < CTI_PORT_TYPE_MAX)) + struct exar8250; struct exar8250_platform { @@ -192,11 +252,201 @@ struct exar8250_board { struct exar8250 { unsigned int nr; + unsigned int osc_freq; struct exar8250_board *board; void __iomem *virt; int line[]; }; +static inline void exar_write_reg(struct exar8250 *priv, + unsigned int reg, u8 value) +{ + writeb(value, priv->virt + reg); +} + +static inline u8 exar_read_reg(struct exar8250 *priv, unsigned int reg) +{ + return readb(priv->virt + reg); +} + +static inline void exar_ee_select(struct exar8250 *priv) +{ + // Set chip select pin high to enable EEPROM reads/writes + exar_write_reg(priv, UART_EXAR_REGB, UART_EXAR_REGB_EECS); + // Min ~500ns delay needed between CS assert and EEPROM access + udelay(1); +} + +static inline void exar_ee_deselect(struct exar8250 *priv) +{ + exar_write_reg(priv, UART_EXAR_REGB, 0x00); +} + +static inline void exar_ee_write_bit(struct exar8250 *priv, int bit) +{ + u8 value = UART_EXAR_REGB_EECS; + + if (bit) + value |= UART_EXAR_REGB_EEDI; + + // Clock out the bit on the EEPROM interface + exar_write_reg(priv, UART_EXAR_REGB, value); + // 2us delay = ~500khz clock speed + udelay(2); + + value |= UART_EXAR_REGB_EECK; + + exar_write_reg(priv, UART_EXAR_REGB, value); + udelay(2); +} + +static inline u8 exar_ee_read_bit(struct exar8250 *priv) +{ + u8 regb; + u8 value = UART_EXAR_REGB_EECS; + + // Clock in the bit on the EEPROM interface + exar_write_reg(priv, UART_EXAR_REGB, value); + // 2us delay = ~500khz clock speed + udelay(2); + + value |= UART_EXAR_REGB_EECK; + + exar_write_reg(priv, UART_EXAR_REGB, value); + udelay(2); + + regb = exar_read_reg(priv, UART_EXAR_REGB); + + return (regb & UART_EXAR_REGB_EEDO ? 1 : 0); +} + +/** + * exar_ee_read() - Read a word from the EEPROM + * @priv: Device's private structure + * @ee_addr: Offset of EEPROM to read word from + * + * Read a single 16bit word from an Exar UART's EEPROM. + * + * Return: EEPROM word + */ +static u16 exar_ee_read(struct exar8250 *priv, u8 ee_addr) +{ + int i; + u16 data = 0; + + exar_ee_select(priv); + + // Send read command (opcode 110) + exar_ee_write_bit(priv, 1); + exar_ee_write_bit(priv, 1); + exar_ee_write_bit(priv, 0); + + // Send address to read from + for (i = 1 << (UART_EXAR_REGB_EE_ADDR_SIZE - 1); i; i >>= 1) + exar_ee_write_bit(priv, (ee_addr & i)); + + // Read data 1 bit at a time + for (i = 0; i <= UART_EXAR_REGB_EE_DATA_SIZE; i++) { + data <<= 1; + data |= exar_ee_read_bit(priv); + } + + exar_ee_deselect(priv); + + return data; +} + +/** + * exar_mpio_config_output() - Configure an Exar MPIO as an output + * @priv: Device's private structure + * @mpio_num: MPIO number/offset to configure + * + * Configure a single MPIO as an output and disable tristate. It is reccomended + * to set the level with exar_mpio_set_high()/exar_mpio_set_low() prior to + * calling this function to ensure default MPIO pin state. + * + * Return: 0 on success, negative error code on failure + */ +static int exar_mpio_config_output(struct exar8250 *priv, + unsigned int mpio_num) +{ + unsigned int mpio_offset; + u8 sel_reg; // MPIO Select register (input/output) + u8 tri_reg; // MPIO Tristate register + u8 value; + + if (mpio_num < 8) { + sel_reg = UART_EXAR_MPIOSEL_7_0; + tri_reg = UART_EXAR_MPIO3T_7_0; + mpio_offset = mpio_num; + } else if (mpio_num >= 8 && mpio_num < 16) { + sel_reg = UART_EXAR_MPIOSEL_15_8; + tri_reg = UART_EXAR_MPIO3T_15_8; + mpio_offset = mpio_num - 8; + } else { + return -EINVAL; + } + + // Disable MPIO pin tri-state + value = exar_read_reg(priv, tri_reg); + value &= ~BIT(mpio_offset); + exar_write_reg(priv, tri_reg, value); + + value = exar_read_reg(priv, sel_reg); + value &= ~BIT(mpio_offset); + exar_write_reg(priv, sel_reg, value); + + return 0; +} + +/** + * _exar_mpio_set() - Set an Exar MPIO output high or low + * @priv: Device's private structure + * @mpio_num: MPIO number/offset to set + * @high: Set MPIO high if true, low if false + * + * Set a single MPIO high or low. exar_mpio_config_output() must also be called + * to configure the pin as an output. + * + * Return: 0 on success, negative error code on failure + */ +static int _exar_mpio_set(struct exar8250 *priv, + unsigned int mpio_num, bool high) +{ + unsigned int mpio_offset; + u8 lvl_reg; + u8 value; + + if (mpio_num < 8) { + lvl_reg = UART_EXAR_MPIOLVL_7_0; + mpio_offset = mpio_num; + } else if (mpio_num >= 8 && mpio_num < 16) { + lvl_reg = UART_EXAR_MPIOLVL_15_8; + mpio_offset = mpio_num - 8; + } else { + return -EINVAL; + } + + value = exar_read_reg(priv, lvl_reg); + if (high) + value |= BIT(mpio_offset); + else + value &= ~BIT(mpio_offset); + exar_write_reg(priv, lvl_reg, value); + + return 0; +} + +static int exar_mpio_set_low(struct exar8250 *priv, unsigned int mpio_num) +{ + return _exar_mpio_set(priv, mpio_num, false); +} + +static int exar_mpio_set_high(struct exar8250 *priv, unsigned int mpio_num) +{ + return _exar_mpio_set(priv, mpio_num, true); +} + static int generic_rs485_config(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485) { @@ -385,6 +635,546 @@ pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, return 0; } +/** + * cti_tristate_disable() - Disable RS485 transciever tristate + * @priv: Device's private structure + * @port_num: Port number to set tristate off + * + * Most RS485 capable cards have a power on tristate jumper/switch that ensures + * the RS422/RS485 transciever does not drive a multi-drop RS485 bus when it is + * not the master. When this jumper is installed the user must set the RS485 + * mode to Full or Half duplex to disable tristate prior to using the port. + * + * Some Exar UARTs have an auto-tristate feature while others require setting + * an MPIO to disable the tristate. + * + * Return: 0 on success, negative error code on failure + */ +static int cti_tristate_disable(struct exar8250 *priv, unsigned int port_num) +{ + int ret; + + ret = exar_mpio_set_high(priv, port_num); + if (ret) + return ret; + + return exar_mpio_config_output(priv, port_num); +} + +/** + * cti_plx_int_enable() - Enable UART interrupts to PLX bridge + * @priv: Device's private structure + * + * Some older CTI cards require MPIO_0 to be set low to enable the + * interupts from the UART to the PLX PCI->PCIe bridge. + * + * Return: 0 on success, negative error code on failure + */ +static int cti_plx_int_enable(struct exar8250 *priv) +{ + int ret; + + ret = exar_mpio_set_low(priv, 0); + if (ret) + return ret; + + return exar_mpio_config_output(priv, 0); +} + +/** + * cti_read_osc_freq() - Read the UART oscillator frequency from EEPROM + * @priv: Device's private structure + * @eeprom_offset: Offset where the oscillator frequency is stored + * + * CTI XR17x15X and XR17V25X cards have the serial boards oscillator frequency + * stored in the EEPROM. FPGA and XR17V35X based cards use the PCI/PCIe clock. + * + * Return: frequency on success, negative error code on failure + */ +static int cti_read_osc_freq(struct exar8250 *priv, u8 eeprom_offset) +{ + u16 lower_word; + u16 upper_word; + int osc_freq; + + lower_word = exar_ee_read(priv, eeprom_offset); + // Check if EEPROM word was blank + if (lower_word == 0xFFFF) + return -EIO; + + upper_word = exar_ee_read(priv, (eeprom_offset + 1)); + if (upper_word == 0xFFFF) + return -EIO; + + osc_freq = FIELD_PREP(CTI_EE_MASK_OSC_FREQ_LOWER, lower_word) | + FIELD_PREP(CTI_EE_MASK_OSC_FREQ_UPPER, upper_word); + + return osc_freq; +} + +/** + * cti_get_port_type_xr17c15x_xr17v25x() - Get port type of xr17c15x/xr17v25x + * @priv: Device's private structure + * @port_num: Port to get type of + * + * CTI xr17c15x and xr17v25x based cards port types are based on PCI IDs. + * + * Return: port type on success, CTI_PORT_TYPE_NONE on failure + */ +static enum cti_port_type cti_get_port_type_xr17c15x_xr17v25x(struct exar8250 *priv, + struct pci_dev *pcidev, + unsigned int port_num) +{ + enum cti_port_type port_type; + + switch (pcidev->subsystem_device) { + // RS232 only cards + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS: + port_type = CTI_PORT_TYPE_RS232; + break; + // 1x RS232, 1x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1: + port_type = (port_num == 0) ? + CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + break; + // 2x RS232, 2x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2: + port_type = (port_num < 2) ? + CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + break; + // 4x RS232, 4x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: + port_type = (port_num < 4) ? + CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + break; + // RS232/RS422/RS485 HW (jumper) selectable + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_SP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: + port_type = CTI_PORT_TYPE_RS232_422_485_HW; + break; + // RS422/RS485 HW (jumper) selectable + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: + port_type = CTI_PORT_TYPE_RS422_485; + break; + // 6x RS232, 2x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: + port_type = (port_num < 6) ? + CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + break; + // 2x RS232, 6x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: + port_type = (port_num < 2) ? + CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + break; + default: + dev_err(&pcidev->dev, "unknown/unsupported device\n"); + port_type = CTI_PORT_TYPE_NONE; + } + + return port_type; +} + +/** + * cti_get_port_type_fpga() - Get the port type of a CTI FPGA card + * @priv: Device's private structure + * @port_num: Port to get type of + * + * FPGA based cards port types are based on PCI IDs. + * + * Return: port type on success, CTI_PORT_TYPE_NONE on failure + */ +static enum cti_port_type cti_get_port_type_fpga(struct exar8250 *priv, + struct pci_dev *pcidev, + unsigned int port_num) +{ + enum cti_port_type port_type; + + switch (pcidev->device) { + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X: + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X: + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16: + port_type = CTI_PORT_TYPE_RS232_422_485_HW; + break; + default: + dev_err(&pcidev->dev, "unknown/unsupported device\n"); + return CTI_PORT_TYPE_NONE; + } + + return port_type; +} + +/** + * cti_get_port_type_xr17v35x() - Read port type from the EEPROM + * @priv: Device's private structure + * @port_num: port offset + * + * CTI XR17V35X based cards have the port types stored in the EEPROM. + * This function reads the port type for a single port. + * + * Return: port type on success, CTI_PORT_TYPE_NONE on failure + */ +static enum cti_port_type cti_get_port_type_xr17v35x(struct exar8250 *priv, + struct pci_dev *pcidev, + unsigned int port_num) +{ + enum cti_port_type port_type; + u16 port_flags; + u8 offset; + + offset = CTI_EE_OFF_XR17V35X_PORT_FLAGS + port_num; + port_flags = exar_ee_read(priv, offset); + + port_type = FIELD_GET(CTI_EE_MASK_PORT_FLAGS_TYPE, port_flags); + if (!CTI_PORT_TYPE_VALID(port_type)) { + /* + * If the port type is missing the card assume it is a + * RS232/RS422/RS485 card to be safe. + * + * There is one known board (BEG013) that only has + * 3 of 4 port types written to the EEPROM so this + * acts as a work around. + */ + dev_warn(&pcidev->dev, + "failed to get port %d type from EEPROM\n", port_num); + port_type = CTI_PORT_TYPE_RS232_422_485_HW; + } + + return port_type; +} + +static int cti_rs485_config_mpio_tristate(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct exar8250 *priv = (struct exar8250 *)port->private_data; + int ret; + + ret = generic_rs485_config(port, termios, rs485); + if (ret) + return ret; + + // Disable power-on RS485 tri-state via MPIO + return cti_tristate_disable(priv, port->port_id); +} + +static int cti_port_setup_common(struct exar8250 *priv, + struct pci_dev *pcidev, + int idx, unsigned int offset, + struct uart_8250_port *port) +{ + int ret; + + if (priv->osc_freq == 0) + return -EINVAL; + + port->port.port_id = idx; + port->port.uartclk = priv->osc_freq; + + ret = serial8250_pci_setup_port(pcidev, port, 0, offset, 0); + if (ret) { + dev_err(&pcidev->dev, + "failed to setup pci for port %d err: %d\n", idx, ret); + return ret; + } + + port->port.private_data = (void *)priv; + port->port.pm = exar_pm; + port->port.shutdown = exar_shutdown; + + return 0; +} + +static int cti_port_setup_fpga(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + + port_type = cti_get_port_type_fpga(priv, pcidev, idx); + + // FPGA shares port offests with XR17C15X + offset = idx * UART_EXAR_XR17C15X_PORT_OFFSET; + port->port.type = PORT_XR17D15X; + + port->port.get_divisor = xr17v35x_get_divisor; + port->port.set_divisor = xr17v35x_set_divisor; + port->port.startup = xr17v35x_startup; + + if (CTI_PORT_TYPE_RS485(port_type)) { + port->port.rs485_config = generic_rs485_config; + port->port.rs485_supported = generic_rs485_supported; + } + + return cti_port_setup_common(priv, pcidev, idx, offset, port); +} + +static int cti_port_setup_xr17v35x(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + int ret; + + port_type = cti_get_port_type_xr17v35x(priv, pcidev, idx); + + offset = idx * UART_EXAR_XR17V35X_PORT_OFFSET; + port->port.type = PORT_XR17V35X; + + port->port.get_divisor = xr17v35x_get_divisor; + port->port.set_divisor = xr17v35x_set_divisor; + port->port.startup = xr17v35x_startup; + + switch (port_type) { + case CTI_PORT_TYPE_RS422_485: + case CTI_PORT_TYPE_RS232_422_485_HW: + port->port.rs485_config = cti_rs485_config_mpio_tristate; + port->port.rs485_supported = generic_rs485_supported; + break; + case CTI_PORT_TYPE_RS232_422_485_SW: + case CTI_PORT_TYPE_RS232_422_485_4B: + case CTI_PORT_TYPE_RS232_422_485_2B: + port->port.rs485_config = generic_rs485_config; + port->port.rs485_supported = generic_rs485_supported; + break; + default: + break; + } + + ret = cti_port_setup_common(priv, pcidev, idx, offset, port); + if (ret) + return ret; + + exar_write_reg(priv, (offset + UART_EXAR_8XMODE), 0x00); + exar_write_reg(priv, (offset + UART_EXAR_FCTR), UART_FCTR_EXAR_TRGD); + exar_write_reg(priv, (offset + UART_EXAR_TXTRG), 128); + exar_write_reg(priv, (offset + UART_EXAR_RXTRG), 128); + + return 0; +} + +static int cti_port_setup_xr17v25x(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + int ret; + + port_type = cti_get_port_type_xr17c15x_xr17v25x(priv, pcidev, idx); + + offset = idx * UART_EXAR_XR17V25X_PORT_OFFSET; + port->port.type = PORT_XR17D15X; + + // XR17V25X supports fractional baudrates + port->port.get_divisor = xr17v35x_get_divisor; + port->port.set_divisor = xr17v35x_set_divisor; + port->port.startup = xr17v35x_startup; + + if (CTI_PORT_TYPE_RS485(port_type)) { + switch (pcidev->subsystem_device) { + // These cards support power on 485 tri-state via MPIO + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: + port->port.rs485_config = cti_rs485_config_mpio_tristate; + break; + // Otherwise auto or no power on 485 tri-state support + default: + port->port.rs485_config = generic_rs485_config; + break; + } + + port->port.rs485_supported = generic_rs485_supported; + } + + ret = cti_port_setup_common(priv, pcidev, idx, offset, port); + if (ret) + return ret; + + exar_write_reg(priv, (offset + UART_EXAR_8XMODE), 0x00); + exar_write_reg(priv, (offset + UART_EXAR_FCTR), UART_FCTR_EXAR_TRGD); + exar_write_reg(priv, (offset + UART_EXAR_TXTRG), 32); + exar_write_reg(priv, (offset + UART_EXAR_RXTRG), 32); + + return 0; +} + +static int cti_port_setup_xr17c15x(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + + port_type = cti_get_port_type_xr17c15x_xr17v25x(priv, pcidev, idx); + + offset = idx * UART_EXAR_XR17C15X_PORT_OFFSET; + port->port.type = PORT_XR17D15X; + + if (CTI_PORT_TYPE_RS485(port_type)) { + switch (pcidev->subsystem_device) { + // These cards support power on 485 tri-state via MPIO + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: + port->port.rs485_config = cti_rs485_config_mpio_tristate; + break; + // Otherwise auto or no power on 485 tri-state support + default: + port->port.rs485_config = generic_rs485_config; + break; + } + + port->port.rs485_supported = generic_rs485_supported; + } + + return cti_port_setup_common(priv, pcidev, idx, offset, port); +} + +static int cti_board_init_xr17v35x(struct exar8250 *priv, + struct pci_dev *pcidev) +{ + // XR17V35X uses the PCIe clock rather than an oscillator + priv->osc_freq = CTI_DEFAULT_PCIE_OSC_FREQ; + + return 0; +} + +static int cti_board_init_xr17v25x(struct exar8250 *priv, + struct pci_dev *pcidev) +{ + int osc_freq; + + osc_freq = cti_read_osc_freq(priv, CTI_EE_OFF_XR17V25X_OSC_FREQ); + if (osc_freq < 0) { + dev_warn(&pcidev->dev, + "failed to read osc freq from EEPROM, using default\n"); + osc_freq = CTI_DEFAULT_PCI_OSC_FREQ; + } + + priv->osc_freq = osc_freq; + + /* enable interupts on cards that need the "PLX fix" */ + switch (pcidev->subsystem_device) { + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B: + cti_plx_int_enable(priv); + break; + default: + break; + } + + return 0; +} + +static int cti_board_init_xr17c15x(struct exar8250 *priv, + struct pci_dev *pcidev) +{ + int osc_freq; + + osc_freq = cti_read_osc_freq(priv, CTI_EE_OFF_XR17C15X_OSC_FREQ); + if (osc_freq <= 0) { + dev_warn(&pcidev->dev, + "failed to read osc freq from EEPROM, using default\n"); + osc_freq = CTI_DEFAULT_PCI_OSC_FREQ; + } + + priv->osc_freq = osc_freq; + + /* enable interrupts on cards that need the "PLX fix" */ + switch (pcidev->subsystem_device) { + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B: + cti_plx_int_enable(priv); + break; + default: + break; + } + + return 0; +} + +static int cti_board_init_fpga(struct exar8250 *priv, struct pci_dev *pcidev) +{ + int ret; + u16 cfg_val; + + // FPGA OSC is fixed to the 33MHz PCI clock + priv->osc_freq = CTI_DEFAULT_FPGA_OSC_FREQ; + + // Enable external interrupts in special cfg space register + ret = pci_read_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, &cfg_val); + if (ret) + return ret; + + cfg_val |= CTI_FPGA_CFG_INT_EN_EXT_BIT; + ret = pci_write_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, cfg_val); + if (ret) + return ret; + + // RS485 gate needs to be enabled; otherwise RTS/CTS will not work + exar_write_reg(priv, CTI_FPGA_RS485_IO_REG, 0x01); + + return 0; +} + static int pci_xr17c154_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx) @@ -880,6 +1670,26 @@ static const struct exar8250_board pbn_fastcom335_8 = { .setup = pci_fastcom335_setup, }; +static const struct exar8250_board pbn_cti_xr17c15x = { + .board_init = cti_board_init_xr17c15x, + .setup = cti_port_setup_xr17c15x, +}; + +static const struct exar8250_board pbn_cti_xr17v25x = { + .board_init = cti_board_init_xr17v25x, + .setup = cti_port_setup_xr17v25x, +}; + +static const struct exar8250_board pbn_cti_xr17v35x = { + .board_init = cti_board_init_xr17v35x, + .setup = cti_port_setup_xr17v35x, +}; + +static const struct exar8250_board pbn_cti_fpga = { + .board_init = cti_board_init_fpga, + .setup = cti_port_setup_fpga, +}; + static const struct exar8250_board pbn_exar_ibm_saturn = { .num_ports = 1, .setup = pci_xr17c154_setup, @@ -924,6 +1734,26 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { .exit = pci_xr17v35x_exit, }; +// For Connect Tech cards with Exar vendor/device PCI IDs +#define CTI_EXAR_DEVICE(devid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_EXAR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_SUBVENDOR_ID_CONNECT_TECH, \ + PCI_ANY_ID), 0, 0, \ + (kernel_ulong_t)&bd \ + } + +// For Connect Tech cards with Connect Tech vendor/device PCI IDs (FPGA based) +#define CTI_PCI_DEVICE(devid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_CONNECT_TECH, \ + PCI_DEVICE_ID_CONNECT_TECH_PCI_##devid, \ + PCI_ANY_ID, \ + PCI_ANY_ID), 0, 0, \ + (kernel_ulong_t)&bd \ + } + #define EXAR_DEVICE(vend, devid, bd) { PCI_DEVICE_DATA(vend, devid, &bd) } #define IBM_DEVICE(devid, sdevid, bd) { \ @@ -953,6 +1783,22 @@ static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(ACCESSIO, COM_4SM, pbn_exar_XR17C15x), EXAR_DEVICE(ACCESSIO, COM_8SM, pbn_exar_XR17C15x), + CTI_EXAR_DEVICE(XR17C152, pbn_cti_xr17c15x), + CTI_EXAR_DEVICE(XR17C154, pbn_cti_xr17c15x), + CTI_EXAR_DEVICE(XR17C158, pbn_cti_xr17c15x), + + CTI_EXAR_DEVICE(XR17V252, pbn_cti_xr17v25x), + CTI_EXAR_DEVICE(XR17V254, pbn_cti_xr17v25x), + CTI_EXAR_DEVICE(XR17V258, pbn_cti_xr17v25x), + + CTI_EXAR_DEVICE(XR17V352, pbn_cti_xr17v35x), + CTI_EXAR_DEVICE(XR17V354, pbn_cti_xr17v35x), + CTI_EXAR_DEVICE(XR17V358, pbn_cti_xr17v35x), + + CTI_PCI_DEVICE(XR79X_12_XIG00X, pbn_cti_fpga), + CTI_PCI_DEVICE(XR79X_12_XIG01X, pbn_cti_fpga), + CTI_PCI_DEVICE(XR79X_16, pbn_cti_fpga), + IBM_DEVICE(XR17C152, SATURN_SERIAL_ONE_PORT, pbn_exar_ibm_saturn), /* USRobotics USR298x-OEM PCI Modems */ From c6795fbffc4547b40933ec368200bd4926a41b44 Mon Sep 17 00:00:00 2001 From: Parker Newman Date: Wed, 17 Apr 2024 16:31:29 -0400 Subject: [PATCH 085/113] serial: exar: fix checkpach warnings -Fix several "missing identifier name" warnings from checkpatch in struct exar8250_platform and struct exar8250_board. Example: WARNING: function definition argument should also have an identifier name - Fix space before tab warning from checkpatch: WARNING: please, no space before tabs + * 0^I^I2 ^IMode bit 0$ Signed-off-by: Parker Newman Link: https://lore.kernel.org/r/f7fa6c16cb5f2c8c1c7cb7d29f80bc7be53f91ab.1713382717.git.pnewman@connecttech.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 6985aabe13cc..5e42558cbb01 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -147,7 +147,7 @@ * * MPIO Port Function * ---- ---- -------- - * 0 2 Mode bit 0 + * 0 2 Mode bit 0 * 1 2 Mode bit 1 * 2 2 Terminate bus * 3 - @@ -229,8 +229,8 @@ struct exar8250_platform { int (*rs485_config)(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485); const struct serial_rs485 *rs485_supported; - int (*register_gpio)(struct pci_dev *, struct uart_8250_port *); - void (*unregister_gpio)(struct uart_8250_port *); + int (*register_gpio)(struct pci_dev *pcidev, struct uart_8250_port *port); + void (*unregister_gpio)(struct uart_8250_port *port); }; /** @@ -245,8 +245,8 @@ struct exar8250_board { unsigned int num_ports; unsigned int reg_shift; int (*board_init)(struct exar8250 *priv, struct pci_dev *pcidev); - int (*setup)(struct exar8250 *, struct pci_dev *, - struct uart_8250_port *, int); + int (*setup)(struct exar8250 *priv, struct pci_dev *pcidev, + struct uart_8250_port *port, int idx); void (*exit)(struct pci_dev *pcidev); }; From f70f95b485d78838ad28dbec804b986d11ad7bb0 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 19 Apr 2024 10:09:31 +0200 Subject: [PATCH 086/113] serial: msm: check dma_map_sg() return value properly The -next commit f8fef2fa419f (tty: msm_serial: use dmaengine_prep_slave_sg()), switched to using dma_map_sg(). But the return value of dma_map_sg() is special: it returns number of elements mapped. And not a standard error value. The commit also forgot to reset dma->tx_sg in case of this failure. Fix both these mistakes. Thanks to Marek who helped debugging this. Signed-off-by: Jiri Slaby (SUSE) Reported-by: Marek Szyprowski Tested-by: Marek Szyprowski Link: https://lore.kernel.org/r/20240419080931.30949-1-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index ae7a8e3cf467..0a9c5219df88 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -499,15 +499,18 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) struct uart_port *port = &msm_port->uart; struct tty_port *tport = &port->state->port; struct msm_dma *dma = &msm_port->tx_dma; + unsigned int mapped; int ret; u32 val; sg_init_table(&dma->tx_sg, 1); kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1, count); - ret = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir); - if (ret) - return ret; + mapped = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir); + if (!mapped) { + ret = -EIO; + goto zero_sg; + } dma->desc = dmaengine_prep_slave_sg(dma->chan, &dma->tx_sg, 1, DMA_MEM_TO_DEV, @@ -548,6 +551,7 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) return 0; unmap: dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir); +zero_sg: sg_init_table(&dma->tx_sg, 1); return ret; } From 53ea31bb6b8a132bb7994b1dd211d715f1b0c0d7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:15:53 +0300 Subject: [PATCH 087/113] serial: 8250_exar: Don't return positive values as error codes PCI core may return positive codes for the specific errors. There is a special API to convert them to negative stanrard ones. Use that API to avoid returning positive values as error codes. Fixes: f7ce07062988 ("serial: exar: add CTI specific setup code") Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 5e42558cbb01..9930668610ca 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -1162,12 +1162,12 @@ static int cti_board_init_fpga(struct exar8250 *priv, struct pci_dev *pcidev) // Enable external interrupts in special cfg space register ret = pci_read_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, &cfg_val); if (ret) - return ret; + return pcibios_err_to_errno(ret); cfg_val |= CTI_FPGA_CFG_INT_EN_EXT_BIT; ret = pci_write_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, cfg_val); if (ret) - return ret; + return pcibios_err_to_errno(ret); // RS485 gate needs to be enabled; otherwise RTS/CTS will not work exar_write_reg(priv, CTI_FPGA_RS485_IO_REG, 0x01); From 19234a5f61c5431bd2468ff5b7d65fa99ebb9f02 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:15:54 +0300 Subject: [PATCH 088/113] serial: 8250_exar: Describe all parameters in kernel doc Kernel doc validator is not happy: warning: Function parameter or struct member 'pcidev' not described in 'cti_get_port_type_xr17c15x_xr17v25x' warning: Function parameter or struct member 'pcidev' not described in 'cti_get_port_type_fpga' warning: Function parameter or struct member 'pcidev' not described in 'cti_get_port_type_xr17v35x' Add missed descriptions. Fixes: f7ce07062988 ("serial: exar: add CTI specific setup code") Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 9930668610ca..150c4abd92fc 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -715,6 +715,7 @@ static int cti_read_osc_freq(struct exar8250 *priv, u8 eeprom_offset) /** * cti_get_port_type_xr17c15x_xr17v25x() - Get port type of xr17c15x/xr17v25x * @priv: Device's private structure + * @pcidev: Pointer to the PCI device for this port * @port_num: Port to get type of * * CTI xr17c15x and xr17v25x based cards port types are based on PCI IDs. @@ -807,6 +808,7 @@ static enum cti_port_type cti_get_port_type_xr17c15x_xr17v25x(struct exar8250 *p /** * cti_get_port_type_fpga() - Get the port type of a CTI FPGA card * @priv: Device's private structure + * @pcidev: Pointer to the PCI device for this port * @port_num: Port to get type of * * FPGA based cards port types are based on PCI IDs. @@ -836,6 +838,7 @@ static enum cti_port_type cti_get_port_type_fpga(struct exar8250 *priv, /** * cti_get_port_type_xr17v35x() - Read port type from the EEPROM * @priv: Device's private structure + * @pcidev: Pointer to the PCI device for this port * @port_num: port offset * * CTI XR17V35X based cards have the port types stored in the EEPROM. From 8e9f8261d7ce2e35f0e45b9751df4ca8e0079558 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:15:55 +0300 Subject: [PATCH 089/113] serial: 8250_exar: Kill CTI_PCI_DEVICE() The CTI_PCI_DEVICE() duplicates EXAR_DEVICE(). Kill the former. Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 150c4abd92fc..ab0abc14ecf8 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -1737,7 +1737,6 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { .exit = pci_xr17v35x_exit, }; -// For Connect Tech cards with Exar vendor/device PCI IDs #define CTI_EXAR_DEVICE(devid, bd) { \ PCI_DEVICE_SUB( \ PCI_VENDOR_ID_EXAR, \ @@ -1747,16 +1746,6 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { (kernel_ulong_t)&bd \ } -// For Connect Tech cards with Connect Tech vendor/device PCI IDs (FPGA based) -#define CTI_PCI_DEVICE(devid, bd) { \ - PCI_DEVICE_SUB( \ - PCI_VENDOR_ID_CONNECT_TECH, \ - PCI_DEVICE_ID_CONNECT_TECH_PCI_##devid, \ - PCI_ANY_ID, \ - PCI_ANY_ID), 0, 0, \ - (kernel_ulong_t)&bd \ - } - #define EXAR_DEVICE(vend, devid, bd) { PCI_DEVICE_DATA(vend, devid, &bd) } #define IBM_DEVICE(devid, sdevid, bd) { \ @@ -1786,6 +1775,7 @@ static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(ACCESSIO, COM_4SM, pbn_exar_XR17C15x), EXAR_DEVICE(ACCESSIO, COM_8SM, pbn_exar_XR17C15x), + /* Connect Tech cards with Exar vendor/device PCI IDs */ CTI_EXAR_DEVICE(XR17C152, pbn_cti_xr17c15x), CTI_EXAR_DEVICE(XR17C154, pbn_cti_xr17c15x), CTI_EXAR_DEVICE(XR17C158, pbn_cti_xr17c15x), @@ -1798,9 +1788,10 @@ static const struct pci_device_id exar_pci_tbl[] = { CTI_EXAR_DEVICE(XR17V354, pbn_cti_xr17v35x), CTI_EXAR_DEVICE(XR17V358, pbn_cti_xr17v35x), - CTI_PCI_DEVICE(XR79X_12_XIG00X, pbn_cti_fpga), - CTI_PCI_DEVICE(XR79X_12_XIG01X, pbn_cti_fpga), - CTI_PCI_DEVICE(XR79X_16, pbn_cti_fpga), + /* Connect Tech cards with Connect Tech vendor/device PCI IDs (FPGA based) */ + EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_12_XIG00X, pbn_cti_fpga), + EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_12_XIG01X, pbn_cti_fpga), + EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_16, pbn_cti_fpga), IBM_DEVICE(XR17C152, SATURN_SERIAL_ONE_PORT, pbn_exar_ibm_saturn), From a85f12ad12a6240f39218dca08bd7f8937db6b6c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:15:56 +0300 Subject: [PATCH 090/113] serial: 8250_exar: Use PCI_SUBVENDOR_ID_IBM for subvendor ID Use PCI_SUBVENDOR_ID_IBM for subvendor ID instead of vendor one. Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-5-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index ab0abc14ecf8..5405a1f463f1 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -1752,7 +1752,7 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { PCI_DEVICE_SUB( \ PCI_VENDOR_ID_EXAR, \ PCI_DEVICE_ID_EXAR_##devid, \ - PCI_VENDOR_ID_IBM, \ + PCI_SUBVENDOR_ID_IBM, \ PCI_SUBDEVICE_ID_IBM_##sdevid), 0, 0, \ (kernel_ulong_t)&bd \ } From 1cf8520a1220410bab6b0dcaa31107425f4576df Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:15:57 +0300 Subject: [PATCH 091/113] serial: 8250_exar: Trivia typo fixes Trivia typo fixes: interupts --> interrupts and others. Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-6-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 5405a1f463f1..ac373cde7e39 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -641,7 +641,7 @@ pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, * @port_num: Port number to set tristate off * * Most RS485 capable cards have a power on tristate jumper/switch that ensures - * the RS422/RS485 transciever does not drive a multi-drop RS485 bus when it is + * the RS422/RS485 transceiver does not drive a multi-drop RS485 bus when it is * not the master. When this jumper is installed the user must set the RS485 * mode to Full or Half duplex to disable tristate prior to using the port. * @@ -666,7 +666,7 @@ static int cti_tristate_disable(struct exar8250 *priv, unsigned int port_num) * @priv: Device's private structure * * Some older CTI cards require MPIO_0 to be set low to enable the - * interupts from the UART to the PLX PCI->PCIe bridge. + * interrupts from the UART to the PLX PCI->PCIe bridge. * * Return: 0 on success, negative error code on failure */ @@ -927,7 +927,7 @@ static int cti_port_setup_fpga(struct exar8250 *priv, port_type = cti_get_port_type_fpga(priv, pcidev, idx); - // FPGA shares port offests with XR17C15X + // FPGA shares port offsets with XR17C15X offset = idx * UART_EXAR_XR17C15X_PORT_OFFSET; port->port.type = PORT_XR17D15X; @@ -1109,7 +1109,7 @@ static int cti_board_init_xr17v25x(struct exar8250 *priv, priv->osc_freq = osc_freq; - /* enable interupts on cards that need the "PLX fix" */ + /* enable interrupts on cards that need the "PLX fix" */ switch (pcidev->subsystem_device) { case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: From c5f597479dfe8d6770d8a823f1f5626ad86c1367 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:15:58 +0300 Subject: [PATCH 092/113] serial: 8250_exar: Extract cti_board_init_osc_freq() helper Extract cti_board_init_osc_freq() helper to avoid code duplication. Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-7-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 48 ++++++++++++----------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index ac373cde7e39..956323e2de4a 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -695,7 +695,6 @@ static int cti_read_osc_freq(struct exar8250 *priv, u8 eeprom_offset) { u16 lower_word; u16 upper_word; - int osc_freq; lower_word = exar_ee_read(priv, eeprom_offset); // Check if EEPROM word was blank @@ -706,10 +705,8 @@ static int cti_read_osc_freq(struct exar8250 *priv, u8 eeprom_offset) if (upper_word == 0xFFFF) return -EIO; - osc_freq = FIELD_PREP(CTI_EE_MASK_OSC_FREQ_LOWER, lower_word) | - FIELD_PREP(CTI_EE_MASK_OSC_FREQ_UPPER, upper_word); - - return osc_freq; + return FIELD_PREP(CTI_EE_MASK_OSC_FREQ_LOWER, lower_word) | + FIELD_PREP(CTI_EE_MASK_OSC_FREQ_UPPER, upper_word); } /** @@ -890,6 +887,19 @@ static int cti_rs485_config_mpio_tristate(struct uart_port *port, return cti_tristate_disable(priv, port->port_id); } +static void cti_board_init_osc_freq(struct exar8250 *priv, struct pci_dev *pcidev, u8 eeprom_offset) +{ + int osc_freq; + + osc_freq = cti_read_osc_freq(priv, eeprom_offset); + if (osc_freq <= 0) { + dev_warn(&pcidev->dev, "failed to read OSC freq from EEPROM, using default\n"); + osc_freq = CTI_DEFAULT_PCI_OSC_FREQ; + } + + priv->osc_freq = osc_freq; +} + static int cti_port_setup_common(struct exar8250 *priv, struct pci_dev *pcidev, int idx, unsigned int offset, @@ -1095,19 +1105,9 @@ static int cti_board_init_xr17v35x(struct exar8250 *priv, return 0; } -static int cti_board_init_xr17v25x(struct exar8250 *priv, - struct pci_dev *pcidev) +static int cti_board_init_xr17v25x(struct exar8250 *priv, struct pci_dev *pcidev) { - int osc_freq; - - osc_freq = cti_read_osc_freq(priv, CTI_EE_OFF_XR17V25X_OSC_FREQ); - if (osc_freq < 0) { - dev_warn(&pcidev->dev, - "failed to read osc freq from EEPROM, using default\n"); - osc_freq = CTI_DEFAULT_PCI_OSC_FREQ; - } - - priv->osc_freq = osc_freq; + cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17V25X_OSC_FREQ); /* enable interrupts on cards that need the "PLX fix" */ switch (pcidev->subsystem_device) { @@ -1123,19 +1123,9 @@ static int cti_board_init_xr17v25x(struct exar8250 *priv, return 0; } -static int cti_board_init_xr17c15x(struct exar8250 *priv, - struct pci_dev *pcidev) +static int cti_board_init_xr17c15x(struct exar8250 *priv, struct pci_dev *pcidev) { - int osc_freq; - - osc_freq = cti_read_osc_freq(priv, CTI_EE_OFF_XR17C15X_OSC_FREQ); - if (osc_freq <= 0) { - dev_warn(&pcidev->dev, - "failed to read osc freq from EEPROM, using default\n"); - osc_freq = CTI_DEFAULT_PCI_OSC_FREQ; - } - - priv->osc_freq = osc_freq; + cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17C15X_OSC_FREQ); /* enable interrupts on cards that need the "PLX fix" */ switch (pcidev->subsystem_device) { From 709bb04522b4b82ec1602e49e47e40719d050140 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:15:59 +0300 Subject: [PATCH 093/113] serial: 8250_exar: Kill unneeded ->board_init() We may reuse ->setup() for the same purpose as it was done before. Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-8-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 171 +++++++++++++--------------- 1 file changed, 81 insertions(+), 90 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 956323e2de4a..4e683ca76de0 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -237,14 +237,12 @@ struct exar8250_platform { * struct exar8250_board - board information * @num_ports: number of serial ports * @reg_shift: describes UART register mapping in PCI memory - * @board_init: quirk run once at ->probe() stage before setting up ports * @setup: quirk run at ->probe() stage for each port * @exit: quirk run at ->remove() stage */ struct exar8250_board { unsigned int num_ports; unsigned int reg_shift; - int (*board_init)(struct exar8250 *priv, struct pci_dev *pcidev); int (*setup)(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx); void (*exit)(struct pci_dev *pcidev); @@ -907,9 +905,6 @@ static int cti_port_setup_common(struct exar8250 *priv, { int ret; - if (priv->osc_freq == 0) - return -EINVAL; - port->port.port_id = idx; port->port.uartclk = priv->osc_freq; @@ -927,6 +922,30 @@ static int cti_port_setup_common(struct exar8250 *priv, return 0; } +static int cti_board_init_fpga(struct exar8250 *priv, struct pci_dev *pcidev) +{ + int ret; + u16 cfg_val; + + // FPGA OSC is fixed to the 33MHz PCI clock + priv->osc_freq = CTI_DEFAULT_FPGA_OSC_FREQ; + + // Enable external interrupts in special cfg space register + ret = pci_read_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, &cfg_val); + if (ret) + return pcibios_err_to_errno(ret); + + cfg_val |= CTI_FPGA_CFG_INT_EN_EXT_BIT; + ret = pci_write_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, cfg_val); + if (ret) + return pcibios_err_to_errno(ret); + + // RS485 gate needs to be enabled; otherwise RTS/CTS will not work + exar_write_reg(priv, CTI_FPGA_RS485_IO_REG, 0x01); + + return 0; +} + static int cti_port_setup_fpga(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, @@ -934,6 +953,13 @@ static int cti_port_setup_fpga(struct exar8250 *priv, { enum cti_port_type port_type; unsigned int offset; + int ret; + + if (idx == 0) { + ret = cti_board_init_fpga(priv, pcidev); + if (ret) + return ret; + } port_type = cti_get_port_type_fpga(priv, pcidev, idx); @@ -953,6 +979,12 @@ static int cti_port_setup_fpga(struct exar8250 *priv, return cti_port_setup_common(priv, pcidev, idx, offset, port); } +static void cti_board_init_xr17v35x(struct exar8250 *priv, struct pci_dev *pcidev) +{ + // XR17V35X uses the PCIe clock rather than an oscillator + priv->osc_freq = CTI_DEFAULT_PCIE_OSC_FREQ; +} + static int cti_port_setup_xr17v35x(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, @@ -962,6 +994,9 @@ static int cti_port_setup_xr17v35x(struct exar8250 *priv, unsigned int offset; int ret; + if (idx == 0) + cti_board_init_xr17v35x(priv, pcidev); + port_type = cti_get_port_type_xr17v35x(priv, pcidev, idx); offset = idx * UART_EXAR_XR17V35X_PORT_OFFSET; @@ -999,6 +1034,22 @@ static int cti_port_setup_xr17v35x(struct exar8250 *priv, return 0; } +static void cti_board_init_xr17v25x(struct exar8250 *priv, struct pci_dev *pcidev) +{ + cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17V25X_OSC_FREQ); + + /* enable interrupts on cards that need the "PLX fix" */ + switch (pcidev->subsystem_device) { + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B: + cti_plx_int_enable(priv); + break; + default: + break; + } +} + static int cti_port_setup_xr17v25x(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, @@ -1008,6 +1059,9 @@ static int cti_port_setup_xr17v25x(struct exar8250 *priv, unsigned int offset; int ret; + if (idx == 0) + cti_board_init_xr17v25x(priv, pcidev); + port_type = cti_get_port_type_xr17c15x_xr17v25x(priv, pcidev, idx); offset = idx * UART_EXAR_XR17V25X_PORT_OFFSET; @@ -1055,6 +1109,25 @@ static int cti_port_setup_xr17v25x(struct exar8250 *priv, return 0; } +static void cti_board_init_xr17c15x(struct exar8250 *priv, struct pci_dev *pcidev) +{ + cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17C15X_OSC_FREQ); + + /* enable interrupts on cards that need the "PLX fix" */ + switch (pcidev->subsystem_device) { + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B: + cti_plx_int_enable(priv); + break; + default: + break; + } +} + static int cti_port_setup_xr17c15x(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, @@ -1063,6 +1136,9 @@ static int cti_port_setup_xr17c15x(struct exar8250 *priv, enum cti_port_type port_type; unsigned int offset; + if (idx == 0) + cti_board_init_xr17c15x(priv, pcidev); + port_type = cti_get_port_type_xr17c15x_xr17v25x(priv, pcidev, idx); offset = idx * UART_EXAR_XR17C15X_PORT_OFFSET; @@ -1096,78 +1172,6 @@ static int cti_port_setup_xr17c15x(struct exar8250 *priv, return cti_port_setup_common(priv, pcidev, idx, offset, port); } -static int cti_board_init_xr17v35x(struct exar8250 *priv, - struct pci_dev *pcidev) -{ - // XR17V35X uses the PCIe clock rather than an oscillator - priv->osc_freq = CTI_DEFAULT_PCIE_OSC_FREQ; - - return 0; -} - -static int cti_board_init_xr17v25x(struct exar8250 *priv, struct pci_dev *pcidev) -{ - cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17V25X_OSC_FREQ); - - /* enable interrupts on cards that need the "PLX fix" */ - switch (pcidev->subsystem_device) { - case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: - case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: - case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B: - cti_plx_int_enable(priv); - break; - default: - break; - } - - return 0; -} - -static int cti_board_init_xr17c15x(struct exar8250 *priv, struct pci_dev *pcidev) -{ - cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17C15X_OSC_FREQ); - - /* enable interrupts on cards that need the "PLX fix" */ - switch (pcidev->subsystem_device) { - case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS: - case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A: - case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B: - case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO: - case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A: - case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B: - cti_plx_int_enable(priv); - break; - default: - break; - } - - return 0; -} - -static int cti_board_init_fpga(struct exar8250 *priv, struct pci_dev *pcidev) -{ - int ret; - u16 cfg_val; - - // FPGA OSC is fixed to the 33MHz PCI clock - priv->osc_freq = CTI_DEFAULT_FPGA_OSC_FREQ; - - // Enable external interrupts in special cfg space register - ret = pci_read_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, &cfg_val); - if (ret) - return pcibios_err_to_errno(ret); - - cfg_val |= CTI_FPGA_CFG_INT_EN_EXT_BIT; - ret = pci_write_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, cfg_val); - if (ret) - return pcibios_err_to_errno(ret); - - // RS485 gate needs to be enabled; otherwise RTS/CTS will not work - exar_write_reg(priv, CTI_FPGA_RS485_IO_REG, 0x01); - - return 0; -} - static int pci_xr17c154_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx) @@ -1574,15 +1578,6 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) if (rc) return rc; - if (board->board_init) { - rc = board->board_init(priv, pcidev); - if (rc) { - dev_err_probe(&pcidev->dev, rc, - "failed to init serial board\n"); - return rc; - } - } - for (i = 0; i < nr_ports && i < maxnr; i++) { rc = board->setup(priv, pcidev, &uart, i); if (rc) { @@ -1664,22 +1659,18 @@ static const struct exar8250_board pbn_fastcom335_8 = { }; static const struct exar8250_board pbn_cti_xr17c15x = { - .board_init = cti_board_init_xr17c15x, .setup = cti_port_setup_xr17c15x, }; static const struct exar8250_board pbn_cti_xr17v25x = { - .board_init = cti_board_init_xr17v25x, .setup = cti_port_setup_xr17v25x, }; static const struct exar8250_board pbn_cti_xr17v35x = { - .board_init = cti_board_init_xr17v35x, .setup = cti_port_setup_xr17v35x, }; static const struct exar8250_board pbn_cti_fpga = { - .board_init = cti_board_init_fpga, .setup = cti_port_setup_fpga, }; From ee6c49a71a4774a4014cc7dbc602adeb50701ebe Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:16:00 +0300 Subject: [PATCH 094/113] serial: 8250_exar: Decrease indentation level Decrease indentation level in some places. No functional change intended. Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-9-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 100 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 4e683ca76de0..1f04d4562878 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -607,28 +607,30 @@ pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, writeb(32, p + UART_EXAR_TXTRG); writeb(32, p + UART_EXAR_RXTRG); + /* Skip the initial (per device) setup */ + if (idx) + return 0; + /* * Setup Multipurpose Input/Output pins. */ - if (idx == 0) { - switch (pcidev->device) { - case PCI_DEVICE_ID_COMMTECH_4222PCI335: - case PCI_DEVICE_ID_COMMTECH_4224PCI335: - writeb(0x78, p + UART_EXAR_MPIOLVL_7_0); - writeb(0x00, p + UART_EXAR_MPIOINV_7_0); - writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); - break; - case PCI_DEVICE_ID_COMMTECH_2324PCI335: - case PCI_DEVICE_ID_COMMTECH_2328PCI335: - writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); - writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); - writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); - break; - } - writeb(0x00, p + UART_EXAR_MPIOINT_7_0); - writeb(0x00, p + UART_EXAR_MPIO3T_7_0); - writeb(0x00, p + UART_EXAR_MPIOOD_7_0); + switch (pcidev->device) { + case PCI_DEVICE_ID_COMMTECH_4222PCI335: + case PCI_DEVICE_ID_COMMTECH_4224PCI335: + writeb(0x78, p + UART_EXAR_MPIOLVL_7_0); + writeb(0x00, p + UART_EXAR_MPIOINV_7_0); + writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); + break; + case PCI_DEVICE_ID_COMMTECH_2324PCI335: + case PCI_DEVICE_ID_COMMTECH_2328PCI335: + writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); + writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); + writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); + break; } + writeb(0x00, p + UART_EXAR_MPIOINT_7_0); + writeb(0x00, p + UART_EXAR_MPIO3T_7_0); + writeb(0x00, p + UART_EXAR_MPIOOD_7_0); return 0; } @@ -853,21 +855,19 @@ static enum cti_port_type cti_get_port_type_xr17v35x(struct exar8250 *priv, port_flags = exar_ee_read(priv, offset); port_type = FIELD_GET(CTI_EE_MASK_PORT_FLAGS_TYPE, port_flags); - if (!CTI_PORT_TYPE_VALID(port_type)) { - /* - * If the port type is missing the card assume it is a - * RS232/RS422/RS485 card to be safe. - * - * There is one known board (BEG013) that only has - * 3 of 4 port types written to the EEPROM so this - * acts as a work around. - */ - dev_warn(&pcidev->dev, - "failed to get port %d type from EEPROM\n", port_num); - port_type = CTI_PORT_TYPE_RS232_422_485_HW; - } + if (CTI_PORT_TYPE_VALID(port_type)) + return port_type; - return port_type; + /* + * If the port type is missing the card assume it is a + * RS232/RS422/RS485 card to be safe. + * + * There is one known board (BEG013) that only has 3 of 4 port types + * written to the EEPROM so this acts as a work around. + */ + dev_warn(&pcidev->dev, "failed to get port %d type from EEPROM\n", port_num); + + return CTI_PORT_TYPE_RS232_422_485_HW; } static int cti_rs485_config_mpio_tristate(struct uart_port *port, @@ -1190,11 +1190,10 @@ static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p) * devices will export them as GPIOs, so we pre-configure them safely * as inputs. */ - u8 dir = 0x00; if ((pcidev->vendor == PCI_VENDOR_ID_EXAR) && - (pcidev->subsystem_vendor != PCI_VENDOR_ID_SEALEVEL)) { + (pcidev->subsystem_vendor != PCI_VENDOR_ID_SEALEVEL)) { // Configure GPIO as inputs for Commtech adapters dir = 0xff; } else { @@ -1284,27 +1283,28 @@ static int sealevel_rs485_config(struct uart_port *port, struct ktermios *termio if (ret) return ret; - if (rs485->flags & SER_RS485_ENABLED) { - old_lcr = readb(p + UART_LCR); + if (!(rs485->flags & SER_RS485_ENABLED)) + return 0; - /* Set EFR[4]=1 to enable enhanced feature registers */ - efr = readb(p + UART_XR_EFR); - efr |= UART_EFR_ECB; - writeb(efr, p + UART_XR_EFR); + old_lcr = readb(p + UART_LCR); - /* Set MCR to use DTR as Auto-RS485 Enable signal */ - writeb(UART_MCR_OUT1, p + UART_MCR); + /* Set EFR[4]=1 to enable enhanced feature registers */ + efr = readb(p + UART_XR_EFR); + efr |= UART_EFR_ECB; + writeb(efr, p + UART_XR_EFR); - /* Set LCR[7]=1 to enable access to DLD register */ - writeb(old_lcr | UART_LCR_DLAB, p + UART_LCR); + /* Set MCR to use DTR as Auto-RS485 Enable signal */ + writeb(UART_MCR_OUT1, p + UART_MCR); - /* Set DLD[7]=1 for inverted RS485 Enable logic */ - dld = readb(p + UART_EXAR_DLD); - dld |= UART_EXAR_DLD_485_POLARITY; - writeb(dld, p + UART_EXAR_DLD); + /* Set LCR[7]=1 to enable access to DLD register */ + writeb(old_lcr | UART_LCR_DLAB, p + UART_LCR); - writeb(old_lcr, p + UART_LCR); - } + /* Set DLD[7]=1 for inverted RS485 Enable logic */ + dld = readb(p + UART_EXAR_DLD); + dld |= UART_EXAR_DLD_485_POLARITY; + writeb(dld, p + UART_EXAR_DLD); + + writeb(old_lcr, p + UART_LCR); return 0; } From 9b2bff26beca432c6d6ca3a49903eb3ae9816bc7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:16:01 +0300 Subject: [PATCH 095/113] serial: 8250_exar: Return directly from switch-cases There are two similar switch-cases which use different style. Unify them and one more to return from the cases directly. While at it, add missing default in another switch-case. Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-10-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 80 +++++++++++------------------ 1 file changed, 29 insertions(+), 51 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 1f04d4562878..51f6af16c557 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -627,6 +627,8 @@ pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); break; + default: + break; } writeb(0x00, p + UART_EXAR_MPIOINT_7_0); writeb(0x00, p + UART_EXAR_MPIO3T_7_0); @@ -723,8 +725,6 @@ static enum cti_port_type cti_get_port_type_xr17c15x_xr17v25x(struct exar8250 *p struct pci_dev *pcidev, unsigned int port_num) { - enum cti_port_type port_type; - switch (pcidev->subsystem_device) { // RS232 only cards case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232: @@ -734,24 +734,17 @@ static enum cti_port_type cti_get_port_type_xr17c15x_xr17v25x(struct exar8250 *p case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS: - port_type = CTI_PORT_TYPE_RS232; - break; + return CTI_PORT_TYPE_RS232; // 1x RS232, 1x RS422/RS485 case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1: - port_type = (port_num == 0) ? - CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; - break; + return (port_num == 0) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; // 2x RS232, 2x RS422/RS485 case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2: - port_type = (port_num < 2) ? - CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; - break; + return (port_num < 2) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; // 4x RS232, 4x RS422/RS485 case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: - port_type = (port_num < 4) ? - CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; - break; + return (port_num < 4) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; // RS232/RS422/RS485 HW (jumper) selectable case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4: @@ -774,32 +767,24 @@ static enum cti_port_type cti_get_port_type_xr17c15x_xr17v25x(struct exar8250 *p case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: - port_type = CTI_PORT_TYPE_RS232_422_485_HW; - break; + return CTI_PORT_TYPE_RS232_422_485_HW; // RS422/RS485 HW (jumper) selectable case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: - port_type = CTI_PORT_TYPE_RS422_485; - break; + return CTI_PORT_TYPE_RS422_485; // 6x RS232, 2x RS422/RS485 case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: - port_type = (port_num < 6) ? - CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; - break; + return (port_num < 6) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; // 2x RS232, 6x RS422/RS485 case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: - port_type = (port_num < 2) ? - CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; - break; + return (port_num < 2) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; default: dev_err(&pcidev->dev, "unknown/unsupported device\n"); - port_type = CTI_PORT_TYPE_NONE; + return CTI_PORT_TYPE_NONE; } - - return port_type; } /** @@ -816,20 +801,15 @@ static enum cti_port_type cti_get_port_type_fpga(struct exar8250 *priv, struct pci_dev *pcidev, unsigned int port_num) { - enum cti_port_type port_type; - switch (pcidev->device) { case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X: case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X: case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16: - port_type = CTI_PORT_TYPE_RS232_422_485_HW; - break; + return CTI_PORT_TYPE_RS232_422_485_HW; default: dev_err(&pcidev->dev, "unknown/unsupported device\n"); return CTI_PORT_TYPE_NONE; } - - return port_type; } /** @@ -1493,35 +1473,33 @@ static irqreturn_t exar_misc_handler(int irq, void *data) return IRQ_HANDLED; } -static unsigned int exar_get_nr_ports(struct exar8250_board *board, - struct pci_dev *pcidev) +static unsigned int exar_get_nr_ports(struct exar8250_board *board, struct pci_dev *pcidev) { - unsigned int nr_ports = 0; + if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO) + return BIT(((pcidev->device & 0x38) >> 3) - 1); - if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO) { - nr_ports = BIT(((pcidev->device & 0x38) >> 3) - 1); - } else if (board->num_ports > 0) { - // Check if board struct overrides number of ports - nr_ports = board->num_ports; - } else if (pcidev->vendor == PCI_VENDOR_ID_EXAR) { - // Exar encodes # ports in last nibble of PCI Device ID ex. 0358 - nr_ports = pcidev->device & 0x0f; - } else if (pcidev->vendor == PCI_VENDOR_ID_CONNECT_TECH) { - // Handle CTI FPGA cards + // Check if board struct overrides number of ports + if (board->num_ports > 0) + return board->num_ports; + + // Exar encodes # ports in last nibble of PCI Device ID ex. 0358 + if (pcidev->vendor == PCI_VENDOR_ID_EXAR) + return pcidev->device & 0x0f; + + // Handle CTI FPGA cards + if (pcidev->vendor == PCI_VENDOR_ID_CONNECT_TECH) { switch (pcidev->device) { case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X: case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X: - nr_ports = 12; - break; + return 12; case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16: - nr_ports = 16; - break; + return 16; default: - break; + return 0; } } - return nr_ports; + return 0; } static int From d72c3018300e86590b2d5037fe959d040e50cd33 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:16:02 +0300 Subject: [PATCH 096/113] serial: 8250_exar: Switch to use dev_err_probe() Switch to use dev_err_probe() to simplify the error path and unify a message template. Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-11-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 51f6af16c557..306bc6d7c141 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -889,11 +889,8 @@ static int cti_port_setup_common(struct exar8250 *priv, port->port.uartclk = priv->osc_freq; ret = serial8250_pci_setup_port(pcidev, port, 0, offset, 0); - if (ret) { - dev_err(&pcidev->dev, - "failed to setup pci for port %d err: %d\n", idx, ret); + if (ret) return ret; - } port->port.private_data = (void *)priv; port->port.pm = exar_pm; @@ -1522,11 +1519,8 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) maxnr = pci_resource_len(pcidev, bar) >> (board->reg_shift + 3); nr_ports = exar_get_nr_ports(board, pcidev); - if (nr_ports == 0) { - dev_err_probe(&pcidev->dev, -ENODEV, - "failed to get number of ports\n"); - return -ENODEV; - } + if (nr_ports == 0) + return dev_err_probe(&pcidev->dev, -ENODEV, "failed to get number of ports\n"); priv = devm_kzalloc(&pcidev->dev, struct_size(priv, line, nr_ports), GFP_KERNEL); if (!priv) @@ -1559,7 +1553,7 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) for (i = 0; i < nr_ports && i < maxnr; i++) { rc = board->setup(priv, pcidev, &uart, i); if (rc) { - dev_err(&pcidev->dev, "Failed to setup port %u\n", i); + dev_err_probe(&pcidev->dev, rc, "Failed to setup port %u\n", i); break; } @@ -1568,10 +1562,9 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) priv->line[i] = serial8250_register_8250_port(&uart); if (priv->line[i] < 0) { - dev_err(&pcidev->dev, - "Couldn't register serial port %lx, irq %d, type %d, error %d\n", - uart.port.iobase, uart.port.irq, - uart.port.iotype, priv->line[i]); + dev_err_probe(&pcidev->dev, priv->line[i], + "Couldn't register serial port %lx, type %d, irq %d\n", + uart.port.iobase, uart.port.iotype, uart.port.irq); break; } } From 3c089d7ebfcb31ad6fb0530970a32303b1168991 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:16:03 +0300 Subject: [PATCH 097/113] serial: 8250_exar: Use BIT() in exar_ee_read() Use BIT() in exar_ee_read() like other functions do. While at it, explain the EEPROM type and the dummy bit requirement. Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-12-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 306bc6d7c141..93d2c8dd3cd8 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -324,6 +324,7 @@ static inline u8 exar_ee_read_bit(struct exar8250 *priv) * @ee_addr: Offset of EEPROM to read word from * * Read a single 16bit word from an Exar UART's EEPROM. + * The type of the EEPROM is AT93C46D. * * Return: EEPROM word */ @@ -340,13 +341,13 @@ static u16 exar_ee_read(struct exar8250 *priv, u8 ee_addr) exar_ee_write_bit(priv, 0); // Send address to read from - for (i = 1 << (UART_EXAR_REGB_EE_ADDR_SIZE - 1); i; i >>= 1) - exar_ee_write_bit(priv, (ee_addr & i)); + for (i = UART_EXAR_REGB_EE_ADDR_SIZE - 1; i >= 0; i--) + exar_ee_write_bit(priv, ee_addr & BIT(i)); - // Read data 1 bit at a time - for (i = 0; i <= UART_EXAR_REGB_EE_DATA_SIZE; i++) { - data <<= 1; - data |= exar_ee_read_bit(priv); + // Read data 1 bit at a time starting with a dummy bit + for (i = UART_EXAR_REGB_EE_DATA_SIZE; i >= 0; i--) { + if (exar_ee_read_bit(priv)) + data |= BIT(i); } exar_ee_deselect(priv); From fa52ed247cfe3cbf008d70bc5831f5faf446e088 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:16:04 +0300 Subject: [PATCH 098/113] serial: 8250_exar: Make type of bit the same in exar_ee_*_bit() Make type of bit parameter and returned one the same in exar_ee_*_bit(). Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-13-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 93d2c8dd3cd8..85085b73706c 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -280,7 +280,7 @@ static inline void exar_ee_deselect(struct exar8250 *priv) exar_write_reg(priv, UART_EXAR_REGB, 0x00); } -static inline void exar_ee_write_bit(struct exar8250 *priv, int bit) +static inline void exar_ee_write_bit(struct exar8250 *priv, u8 bit) { u8 value = UART_EXAR_REGB_EECS; From aa0bb2092e090872739a32928feb12729f97bb9a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 May 2024 20:16:05 +0300 Subject: [PATCH 099/113] serial: 8250_exar: Keep the includes sorted Keep the includes sorted. Signed-off-by: Andy Shevchenko Tested-by: Parker Newman Link: https://lore.kernel.org/r/20240503171917.2921250-14-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 85085b73706c..616128254bbd 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -6,6 +6,7 @@ * * Copyright (C) 2017 Sudip Mukherjee, All Rights Reserved. */ +#include #include #include #include @@ -20,7 +21,6 @@ #include #include #include -#include #include #include From 6bb34cf737f85e1f9990266eb5534c4e8e302584 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 22 Apr 2024 11:32:37 +0300 Subject: [PATCH 100/113] serial: 8250_pnp: Switch to DEFINE_SIMPLE_DEV_PM_OPS() The SIMPLE_DEV_PM_OPS() is deprecated, replace it with the DEFINE_SIMPLE_DEV_PM_OPS() and use pm_sleep_ptr() for setting the driver's PM routines. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240422083237.207447-1-andy.shevchenko@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pnp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 8f72a7de1d1d..9188902fa5b3 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -10,6 +10,7 @@ */ #include #include +#include #include #include #include @@ -506,7 +507,7 @@ static void serial_pnp_remove(struct pnp_dev *dev) serial8250_unregister_port(line - 1); } -static int __maybe_unused serial_pnp_suspend(struct device *dev) +static int serial_pnp_suspend(struct device *dev) { long line = (long)dev_get_drvdata(dev); @@ -516,7 +517,7 @@ static int __maybe_unused serial_pnp_suspend(struct device *dev) return 0; } -static int __maybe_unused serial_pnp_resume(struct device *dev) +static int serial_pnp_resume(struct device *dev) { long line = (long)dev_get_drvdata(dev); @@ -526,14 +527,14 @@ static int __maybe_unused serial_pnp_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(serial_pnp_pm_ops, serial_pnp_suspend, serial_pnp_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(serial_pnp_pm_ops, serial_pnp_suspend, serial_pnp_resume); static struct pnp_driver serial_pnp_driver = { .name = "serial", .probe = serial_pnp_probe, .remove = serial_pnp_remove, .driver = { - .pm = &serial_pnp_pm_ops, + .pm = pm_sleep_ptr(&serial_pnp_pm_ops), }, .id_table = pnp_dev_table, }; From cc3236cd758b07c1f36cabd55ea1740f0881faa0 Mon Sep 17 00:00:00 2001 From: Manikanta Guntupalli Date: Thu, 25 Apr 2024 11:53:56 +0530 Subject: [PATCH 101/113] dt-bindings: serial: cdns,uart: Add optional reset property Add optional reset device-tree property to the uartps controller. Signed-off-by: Manikanta Guntupalli Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240425062358.1347684-2-manikanta.guntupalli@amd.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/cdns,uart.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/serial/cdns,uart.yaml b/Documentation/devicetree/bindings/serial/cdns,uart.yaml index 2129247d7c81..d7f047b0bf24 100644 --- a/Documentation/devicetree/bindings/serial/cdns,uart.yaml +++ b/Documentation/devicetree/bindings/serial/cdns,uart.yaml @@ -46,6 +46,9 @@ properties: power-domains: maxItems: 1 + resets: + maxItems: 1 + required: - compatible - reg From b4337685010990385901405cc317a5a46b147b5a Mon Sep 17 00:00:00 2001 From: Manikanta Guntupalli Date: Thu, 25 Apr 2024 11:53:57 +0530 Subject: [PATCH 102/113] arm64: zynqmp: Add resets property for UART nodes Add resets property for UART0 and UART1 nodes Signed-off-by: Manikanta Guntupalli Link: https://lore.kernel.org/r/20240425062358.1347684-3-manikanta.guntupalli@amd.com Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi index 25d20d803230..935504424ec6 100644 --- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi +++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi @@ -906,6 +906,7 @@ uart0: serial@ff000000 { reg = <0x0 0xff000000 0x0 0x1000>; clock-names = "uart_clk", "pclk"; power-domains = <&zynqmp_firmware PD_UART_0>; + resets = <&zynqmp_reset ZYNQMP_RESET_UART0>; }; uart1: serial@ff010000 { @@ -917,6 +918,7 @@ uart1: serial@ff010000 { reg = <0x0 0xff010000 0x0 0x1000>; clock-names = "uart_clk", "pclk"; power-domains = <&zynqmp_firmware PD_UART_1>; + resets = <&zynqmp_reset ZYNQMP_RESET_UART1>; }; usb0: usb@ff9d0000 { From e3896be240780ccade65cc6cc8925c6f12e7f6c6 Mon Sep 17 00:00:00 2001 From: Manikanta Guntupalli Date: Thu, 25 Apr 2024 11:53:58 +0530 Subject: [PATCH 103/113] tty: serial: uartps: Add support for uartps controller reset Add support for an optional reset for the uartps controller using the reset driver. If the uartps node contains the "resets" property, then cdns_uart_startup performs uartps controller non-pulse out of reset and reset in exit path. Signed-off-by: Manikanta Guntupalli Link: https://lore.kernel.org/r/20240425062358.1347684-4-manikanta.guntupalli@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index de3487206bcb..2acfcea403ce 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -25,6 +25,7 @@ #include #include #include +#include #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" @@ -198,6 +199,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * @gpiod_rts: Pointer to the gpio descriptor * @rs485_tx_started: RS485 tx state * @tx_timer: Timer for tx + * @rstc: Pointer to the reset control */ struct cdns_uart { struct uart_port *port; @@ -211,6 +213,7 @@ struct cdns_uart { struct gpio_desc *gpiod_rts; bool rs485_tx_started; struct hrtimer tx_timer; + struct reset_control *rstc; }; struct cdns_platform_data { u32 quirks; @@ -948,6 +951,10 @@ static int cdns_uart_startup(struct uart_port *port) is_brk_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; + ret = reset_control_deassert(cdns_uart->rstc); + if (ret) + return ret; + uart_port_lock_irqsave(port, &flags); /* Disable the TX and RX */ @@ -1721,6 +1728,13 @@ static int cdns_uart_probe(struct platform_device *pdev) dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n"); } + cdns_uart_data->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(cdns_uart_data->rstc)) { + rc = PTR_ERR(cdns_uart_data->rstc); + dev_err_probe(&pdev->dev, rc, "Cannot get UART reset\n"); + goto err_out_unregister_driver; + } + rc = clk_prepare_enable(cdns_uart_data->pclk); if (rc) { dev_err(&pdev->dev, "Unable to enable pclk clock.\n"); @@ -1881,6 +1895,7 @@ static void cdns_uart_remove(struct platform_device *pdev) if (console_port == port) console_port = NULL; #endif + reset_control_assert(cdns_uart_data->rstc); if (!--instances) uart_unregister_driver(cdns_uart_data->cdns_uart_driver); From 719910ebc3d7d06ca6c7db6e2b96a42405fbc0f9 Mon Sep 17 00:00:00 2001 From: Pratik Farkase Date: Thu, 25 Apr 2024 18:25:50 +0200 Subject: [PATCH 104/113] dt-bindings: serial: brcm,bcm2835-aux-uart: convert to dtschema Convert the Broadcom BCM2835 Auxiliary UART to newer DT schema. Created DT schema based on the .txt file which had `compatible`, `reg` `clocks` and `interrupts` as the required properties. This binding is used by Broadcom BCM2835 SOC used in some Raspberry PI boards. Changes from original file: Implemented complete example which the original txt binding lacked. Acked-by: Florian Fainelli Reviewed-by: Conor Dooley Signed-off-by: Pratik Farkase Link: https://lore.kernel.org/r/20240425162554.13576-1-pratik.farkase@wsisweden.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/brcm,bcm2835-aux-uart.txt | 18 -------- .../serial/brcm,bcm2835-aux-uart.yaml | 46 +++++++++++++++++++ 2 files changed, 46 insertions(+), 18 deletions(-) delete mode 100644 Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.txt create mode 100644 Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.yaml diff --git a/Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.txt b/Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.txt deleted file mode 100644 index b5cc6297cd1b..000000000000 --- a/Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.txt +++ /dev/null @@ -1,18 +0,0 @@ -* BCM2835 AUXILIAR UART - -Required properties: - -- compatible: "brcm,bcm2835-aux-uart" -- reg: The base address of the UART register bank. -- interrupts: A single interrupt specifier. -- clocks: Clock driving the hardware; used to figure out the baud rate - divisor. - -Example: - - uart1: serial@7e215040 { - compatible = "brcm,bcm2835-aux-uart"; - reg = <0x7e215040 0x40>; - interrupts = <1 29>; - clocks = <&aux BCM2835_AUX_CLOCK_UART>; - }; diff --git a/Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.yaml b/Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.yaml new file mode 100644 index 000000000000..6b72459f7dc8 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/brcm,bcm2835-aux-uart.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: BCM2835 AUXILIARY UART + +maintainers: + - Pratik Farkase + - Florian Fainelli + - Stefan Wahren + +allOf: + - $ref: serial.yaml + +properties: + compatible: + const: brcm,bcm2835-aux-uart + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + serial@7e215040 { + compatible = "brcm,bcm2835-aux-uart"; + reg = <0x7e215040 0x40>; + interrupts = <1 29>; + clocks = <&aux BCM2835_AUX_CLOCK_UART>; + }; From a47cf07f60dcb02d01daa19ecf2d5775d6cd12db Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 30 Apr 2024 12:59:30 +0300 Subject: [PATCH 105/113] serial: core: Call device_set_awake_path() for console port In case the UART port is used as a console, no_console_suspend is available in bootargs and UART port is part of a software-controlled power domain we need to call device_set_awake_path(). This lets the power domain core code know that this domain should not be powered off during system suspend. Otherwise, the UART port power domain is turned off, nothing is printed while suspending and the suspend/resume process is blocked. This was detected on the Renesas RZ/G3S SoC while adding support for power domains. Based on code investigation (on v6.9-rc5), this issue is present on other SoCs (e.g., Renesas R-Mobile A1 [1], IMX8QXP [2]) and different SoCs have particular implementation to handle it. Due to this the patch added the call of device_set_awake_path() in uart_suspend_port() instead of having it in the platform specific UART driver. [1] drivers/pmdomain/renesas/rmobile-sysc.c:116 [2] drivers/pmdomain/imx/scu-pd.c:357 Suggested-by: Ulf Hansson Suggested-by: Geert Uytterhoeven Reviewed-by: Ulf Hansson Reviewed-by: Andy Shevchenko Reviewed-by: Tony Lindgren Reviewed-by: Geert Uytterhoeven Tested-by: Geert Uytterhoeven Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20240430095930.2806067-1-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 8a76f378c445..84ae70d52b05 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2408,6 +2408,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) uport->ops->stop_rx(uport); uart_port_unlock_irq(uport); } + device_set_awake_path(uport->dev); goto unlock; } From 6bd23e0c2bb6c65d4f5754d1456bc9a4427fc59b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 23 Apr 2024 09:33:39 -0700 Subject: [PATCH 106/113] tty: add the option to have a tty reject a new ldisc ... and use it to limit the virtual terminals to just N_TTY. They are kind of special, and in particular, the "con_write()" routine violates the "writes cannot sleep" rule that some ldiscs rely on. This avoids the BUG: sleeping function called from invalid context at kernel/printk/printk.c:2659 when N_GSM has been attached to a virtual console, and gsmld_write() calls con_write() while holding a spinlock, and con_write() then tries to get the console lock. Tested-by: Tetsuo Handa Cc: Jiri Slaby Cc: Andrew Morton Cc: Daniel Starke Reported-by: syzbot Closes: https://syzkaller.appspot.com/bug?extid=dbac96d8e73b61aa559c Signed-off-by: Linus Torvalds Link: https://lore.kernel.org/r/20240423163339.59780-1-torvalds@linux-foundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 6 ++++++ drivers/tty/vt/vt.c | 10 ++++++++++ include/linux/tty_driver.h | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 3f68e213df1f..d80e9d4c974b 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -545,6 +545,12 @@ int tty_set_ldisc(struct tty_struct *tty, int disc) goto out; } + if (tty->ops->ldisc_ok) { + retval = tty->ops->ldisc_ok(tty, disc); + if (retval) + goto out; + } + old_ldisc = tty->ldisc; /* Shutdown the old discipline. */ diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 9b5b98dfc8b4..cd87e3d1291e 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3576,6 +3576,15 @@ static void con_cleanup(struct tty_struct *tty) tty_port_put(&vc->port); } +/* + * We can't deal with anything but the N_TTY ldisc, + * because we can sleep in our write() routine. + */ +static int con_ldisc_ok(struct tty_struct *tty, int ldisc) +{ + return ldisc == N_TTY ? 0 : -EINVAL; +} + static int default_color = 7; /* white */ static int default_italic_color = 2; // green (ASCII) static int default_underline_color = 3; // cyan (ASCII) @@ -3695,6 +3704,7 @@ static const struct tty_operations con_ops = { .resize = vt_resize, .shutdown = con_shutdown, .cleanup = con_cleanup, + .ldisc_ok = con_ldisc_ok, }; static struct cdev vc0_cdev; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 7372124fbf90..dd4b31ce6d5d 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -154,6 +154,13 @@ struct serial_struct; * * Optional. Called under the @tty->termios_rwsem. May sleep. * + * @ldisc_ok: ``int ()(struct tty_struct *tty, int ldisc)`` + * + * This routine allows the @tty driver to decide if it can deal + * with a particular @ldisc. + * + * Optional. Called under the @tty->ldisc_sem and @tty->termios_rwsem. + * * @set_ldisc: ``void ()(struct tty_struct *tty)`` * * This routine allows the @tty driver to be notified when the device's @@ -372,6 +379,7 @@ struct tty_operations { void (*hangup)(struct tty_struct *tty); int (*break_ctl)(struct tty_struct *tty, int state); void (*flush_buffer)(struct tty_struct *tty); + int (*ldisc_ok)(struct tty_struct *tty, int ldisc); void (*set_ldisc)(struct tty_struct *tty); void (*wait_until_sent)(struct tty_struct *tty, int timeout); void (*send_xchar)(struct tty_struct *tty, u8 ch); From aae20f6e34cd0cbd67a1d0e5877561c40109a81b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 6 May 2024 13:40:17 +0200 Subject: [PATCH 107/113] serial: sh-sci: protect invalidating RXDMA on shutdown The to-be-fixed commit removed locking when invalidating the DMA RX descriptors on shutdown. It overlooked that there is still a rx_timer running which may still access the protected data. So, re-add the locking. Reported-by: Dirk Behme Closes: https://lore.kernel.org/r/ee6c9e16-9f29-450e-81da-4a8dceaa8fc7@de.bosch.com Fixes: 2c4ee23530ff ("serial: sh-sci: Postpone DMA release when falling back to PIO") Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20240506114016.30498-7-wsa+renesas@sang-engineering.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 97031db26ae4..4d34950268b9 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1270,9 +1270,14 @@ static void sci_dma_rx_chan_invalidate(struct sci_port *s) static void sci_dma_rx_release(struct sci_port *s) { struct dma_chan *chan = s->chan_rx_saved; + struct uart_port *port = &s->port; + unsigned long flags; + uart_port_lock_irqsave(port, &flags); s->chan_rx_saved = NULL; sci_dma_rx_chan_invalidate(s); + uart_port_unlock_irqrestore(port, flags); + dmaengine_terminate_sync(chan); dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], sg_dma_address(&s->sg_rx[0])); From 8efc440549087de41abadf62f4bde1d827135338 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 6 May 2024 13:40:18 +0200 Subject: [PATCH 108/113] serial: sh-sci: describe locking requirements for invalidating RXDMA Make sure everyone knows that calling this function needs protection. Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20240506114016.30498-8-wsa+renesas@sang-engineering.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 4d34950268b9..7a7bc18e4705 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1257,6 +1257,7 @@ static int sci_dma_rx_find_active(struct sci_port *s) return -1; } +/* Must only be called with uart_port_lock taken */ static void sci_dma_rx_chan_invalidate(struct sci_port *s) { unsigned int i; From 0c9c1ea583f1b9887d248dc26f6921c4ae826b96 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 6 May 2024 13:40:19 +0200 Subject: [PATCH 109/113] serial: sh-sci: let timeout timer only run when DMA is scheduled The hrtimer for RXDMA timeout was unconditionally restarted in the RXDMA complete handler ignoring the fact that setting up DMA may fail and PIO is used instead. Explicitly stop the timer when DMA is completed and only restart it when setting up DMA was successful. This makes the intention of the timer much clearer, the driver easier to understand and simplifies assumptions about the timer. The latter avoids race conditions if these assumptions were not met or confused. Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20240506114016.30498-9-wsa+renesas@sang-engineering.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 7a7bc18e4705..d31b3aca5987 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1324,14 +1324,14 @@ static void sci_dma_rx_complete(void *arg) dev_dbg(port->dev, "%s(%d) active cookie %d\n", __func__, port->line, s->active_rx); + hrtimer_cancel(&s->rx_timer); + uart_port_lock_irqsave(port, &flags); active = sci_dma_rx_find_active(s); if (active >= 0) count = sci_dma_rx_push(s, s->rx_buf[active], s->buf_len_rx); - start_hrtimer_us(&s->rx_timer, s->rx_timeout); - if (count) tty_flip_buffer_push(&port->state->port); @@ -1354,6 +1354,9 @@ static void sci_dma_rx_complete(void *arg) uart_port_unlock_irqrestore(port, flags); dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", __func__, s->cookie_rx[active], active, s->active_rx); + + start_hrtimer_us(&s->rx_timer, s->rx_timeout); + return; fail: From ae1b6b4c0a0c93a53cb897e54c84079a017cd9d0 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 6 May 2024 13:40:20 +0200 Subject: [PATCH 110/113] serial: sh-sci: simplify locking when re-issuing RXDMA fails Avoid a superfluous unlock/lock-pair by simply moving the printout to the end of bailing out. Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20240506114016.30498-10-wsa+renesas@sang-engineering.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index d31b3aca5987..f738980a8b2c 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1360,14 +1360,12 @@ static void sci_dma_rx_complete(void *arg) return; fail: - uart_port_unlock_irqrestore(port, flags); - dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); /* Switch to PIO */ - uart_port_lock_irqsave(port, &flags); dmaengine_terminate_async(chan); sci_dma_rx_chan_invalidate(s); sci_dma_rx_reenable_irq(s); uart_port_unlock_irqrestore(port, flags); + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); } static void sci_dma_tx_release(struct sci_port *s) From abe7015a3630f80037d9e40347d31a8d1f95428a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Mon, 6 May 2024 15:12:02 +0300 Subject: [PATCH 111/113] serial: 8250_pnp: Simplify "line" related code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 8250_pnp sets drvdata to line + 1 if the probe is successful. The users of drvdata are in remove, suspend and resume callbacks, none of which will be called if probe failed. The line acquired from drvdata can never be zero in those functions and the checks for that can be removed. Eliminate also +/-1 step because all users of line subtract 1 from the value. These might have been leftover from legacy PM callbacks that could be called without probe being successful. Reviewed-by: Andy Shevchenko Signed-off-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240506121202.11253-1-ilpo.jarvinen@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pnp.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 9188902fa5b3..7c06ae79d8e2 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -435,8 +435,9 @@ static int serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { struct uart_8250_port uart, *port; - int ret, line, flags = dev_id->driver_data; + int ret, flags = dev_id->driver_data; unsigned char iotype; + long line; if (flags & UNKNOWN_DEV) { ret = serial_pnp_guess_board(dev); @@ -494,7 +495,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) if (uart_console(&port->port)) dev->capabilities |= PNP_CONSOLE; - pnp_set_drvdata(dev, (void *)((long)line + 1)); + pnp_set_drvdata(dev, (void *)line); return 0; } @@ -503,17 +504,14 @@ static void serial_pnp_remove(struct pnp_dev *dev) long line = (long)pnp_get_drvdata(dev); dev->capabilities &= ~PNP_CONSOLE; - if (line) - serial8250_unregister_port(line - 1); + serial8250_unregister_port(line); } static int serial_pnp_suspend(struct device *dev) { long line = (long)dev_get_drvdata(dev); - if (!line) - return -ENODEV; - serial8250_suspend_port(line - 1); + serial8250_suspend_port(line); return 0; } @@ -521,9 +519,7 @@ static int serial_pnp_resume(struct device *dev) { long line = (long)dev_get_drvdata(dev); - if (!line) - return -ENODEV; - serial8250_resume_port(line - 1); + serial8250_resume_port(line); return 0; } From a3d8728ab079951741efa11360df43dbfacba7ab Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 8 May 2024 15:37:44 +0200 Subject: [PATCH 112/113] serial: imx: Raise TX trigger level to 8 At the default TX trigger level of 2 in non-DMA mode (meaning that an interrupt is generated when less than 2 characters are left in the FIFO), we have observed frequent buffer underruns at 115200 Baud on an i.MX8M Nano. This can cause communication issues if the receiving side expects a continuous transfer. Increasing the level to 8 makes the UART trigger an interrupt earlier, giving the kernel enough time to refill the FIFO, at the cost of triggering one interrupt per ~24 instead of ~30 bytes of transmitted data (as the i.MX UART has a 32 byte FIFO). Signed-off-by: Michael Krummsdorf Signed-off-by: Matthias Schiffer Link: https://lore.kernel.org/r/20240508133744.35858-1-matthias.schiffer@ew.tq-group.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 79f1503cd75b..2eb22594960f 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1305,7 +1305,7 @@ static void imx_uart_clear_rx_errors(struct imx_port *sport) } -#define TXTL_DEFAULT 2 /* reset default */ +#define TXTL_DEFAULT 8 #define RXTL_DEFAULT 8 /* 8 characters or aging timer */ #define TXTL_DMA 8 /* DMA burst setting */ #define RXTL_DMA 9 /* DMA burst setting */ From e21de1455a721a0cb4217b18589ede846f5b0686 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 9 May 2024 16:15:49 +0200 Subject: [PATCH 113/113] serial: Clear UPF_DEAD before calling tty_port_register_device_attr_serdev() If a serdev_device_driver is already loaded for a serdev_tty_port when it gets registered by tty_port_register_device_attr_serdev() then that driver's probe() method will be called immediately. The serdev_device_driver's probe() method should then be able to call serdev_device_open() successfully, but because UPF_DEAD is still dead serdev_device_open() will fail with -ENXIO in this scenario: serdev_device_open() ctrl->ops->open() /* this callback being ttyport_open() */ tty->ops->open() /* this callback being uart_open() */ tty_port_open() port->ops->activate() /* this callback being uart_port_activate() */ Find bit UPF_DEAD is set in uport->flags and fail with errno -ENXIO. Fix this be clearing UPF_DEAD before tty_port_register_device_attr_serdev() note this only moves up the UPD_DEAD clearing a small bit, before: tty_port_register_device_attr_serdev(); mutex_unlock(&tty_port.mutex); uart_port.flags &= ~UPF_DEAD; mutex_unlock(&port_mutex); after: uart_port.flags &= ~UPF_DEAD; tty_port_register_device_attr_serdev(); mutex_unlock(&tty_port.mutex); mutex_unlock(&port_mutex); Reported-by: Weifeng Liu Closes: https://lore.kernel.org/platform-driver-x86/20240505130800.2546640-1-weifeng.liu.z@gmail.com/ Tested-by: Weifeng Liu Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240509141549.63704-1-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 84ae70d52b05..2c1a0254d3f4 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -3207,6 +3207,9 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u if (uport->attr_group) uport->tty_groups[1] = uport->attr_group; + /* Ensure serdev drivers can call serdev_device_open() right away */ + uport->flags &= ~UPF_DEAD; + /* * Register the port whether it's detected or not. This allows * setserial to be used to alter this port's parameters. @@ -3217,6 +3220,7 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u if (!IS_ERR(tty_dev)) { device_set_wakeup_capable(tty_dev, 1); } else { + uport->flags |= UPF_DEAD; dev_err(uport->dev, "Cannot register tty device on line %d\n", uport->line); } @@ -3426,8 +3430,6 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port) if (ret) goto err_unregister_port_dev; - port->flags &= ~UPF_DEAD; - mutex_unlock(&port_mutex); return 0;