Linux Serial subsystem development
 help / color / mirror / Atom feed
* Re: [Suggestion] drivers/tty: drivers/char/:  for MAX_ASYNC_BUFFER_SIZE
From: Chen Gang @ 2012-11-30  6:28 UTC (permalink / raw)
  To: Paul Fulghum
  Cc: Greg KH, linux-kernel@vger.kernel.org, linux-serial, Alan Cox
In-Reply-To: <C7D3911F-7B6B-4353-A84B-0218FAB27198@microgate.com>

于 2012年11月30日 11:27, Paul Fulghum 写道:
> 
> I’m the maintainer for these drivers. I only caught this message by
> chance and

  it seems you are not in MAINTAINER file.
  is it suitable to add your name into MAINTAINER file ?
    (if it was, please help adding ?  I am not quite familiar with it)

  thanks.

-- 
Chen Gang

Asianux Corporation

^ permalink raw reply

* Re: [Suggestion] drivers/tty: drivers/char/:  for MAX_ASYNC_BUFFER_SIZE
From: Chen Gang @ 2012-11-30  2:52 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel@vger.kernel.org, linux-serial, Alan Cox
In-Reply-To: <20121129183207.GA4688@kroah.com>

于 2012年11月30日 02:32, Greg KH 写道:
> On Thu, Nov 29, 2012 at 01:57:59PM +0800, Chen Gang wrote:
>>> And, I really don't understand here, why do you want to change this?
>>> What is it going to change?  And why?
>>>
>>
>> Why:
>>   for the context MGSLPC_INFO *info in drivers/char/pcmcia/synclink_cs.c
>>     info->max_frame_size can be the value between 4096 .. 65535 (can be
>> set by its module input parameter)
>>     info->flag_buf length is 4096 (MAX_ASYNC_BUFFER_SIZE)
>>   in function rx_get_frame
>>     the framesize is limit by info->max_frame_size, but may still be
>> larger that 4096.
>>     when call function ldisc_receive_buf, info->flag_buf is equal to
>> 4096, but framesize can be more than 4096. it will cause memory over flow.
> 
> Do you use that pcmcia driver for anything?  Are those cards still
> around?

I am not use them.

I am just through code review (so it is only a suggestion).

this issue has effect with 4 synclink drivers
I checked their source code, all of them have the same issue.
  drivers/char/pcmcia/synclink_cs.c:213:        char flag_buf[MAX_ASYNC_BUFFER_SIZE];
  drivers/tty/synclink_gt.c:320:        char flag_buf[MAX_ASYNC_BUFFER_SIZE];
  drivers/tty/synclink.c:294:   char flag_buf[MAX_ASYNC_BUFFER_SIZE];
  drivers/tty/synclinkmp.c:265: char flag_buf[MAX_ASYNC_BUFFER_SIZE];

by the way, for the char_buf, has already useless (can be removed)
  drivers/tty/synclink_gt.c:321:        char char_buf[MAX_ASYNC_BUFFER_SIZE];
  drivers/tty/synclink.c:295:   char char_buf[MAX_ASYNC_BUFFER_SIZE];   
  drivers/tty/synclinkmp.c:266: char char_buf[MAX_ASYNC_BUFFER_SIZE];



> 
>> What:
>>   #define MAX_ASYNC_BUFFER_SIZE  0x10000 (instead of 4096, originally).
>>   let it match the max frame size.
>>
>> At last:
>>   my suggestion may be incorrect, need relative member (who expert about
>> it) to help checking.
> 
> That driver might be incorrect, yes, care to make up a patch for it and
> test it to verify it fixes the problem?
> 

and now Alan Cox has his own opinions
  at least, I think it is valuable to continue discussing about it.

if Alan Cox agree with it (but it seems not),  I will make patch, and try to perform test.
also welcome another members to help testing.



> thanks,
> 
> greg k-h
> 
> 


-- 
Chen Gang

Asianux Corporation
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] tty: Correct tty buffer flushing.
From: Peter Hurley @ 2012-11-30  1:14 UTC (permalink / raw)
  To: Alan Cox
  Cc: Ilya Zykov, Alan Cox, Xiaobing Tu, Jiri Slaby, Alek Du,
	Greg Kroah-Hartman, linux-kernel, linux-serial
In-Reply-To: <20121129212809.5bc12088@bob.linux.org.uk>

On Thu, 2012-11-29 at 21:28 +0000, Alan Cox wrote:
> > Sorry, In you reply not all patch.
> > Main idea here -  we never flash last (struct tty_buffer) in the
> > active buffer. Only data for ldisc. (tty->buf.head->read =
> > tty->buf.head->commit). At that moment driver can collect(write) data
> > in buffer without conflict.
> 
> Ah.. now I understand. Yes that makes sense.  I will think about that
> carefully. This is why a good explanation with a patch is important.

FWIW, I've been running this on -next. The logic seems sound to me --
fundamentally, this technique is what flush_to_ldisc() does.

Regards,
Peter Hurley



^ permalink raw reply

* Re: [PATCH] tty: Correct tty buffer flushing.
From: Alan Cox @ 2012-11-29 21:28 UTC (permalink / raw)
  To: Ilya Zykov
  Cc: Alan Cox, Xiaobing Tu, Jiri Slaby, Alek Du, Greg Kroah-Hartman,
	linux-kernel, linux-serial
In-Reply-To: <50B76D96.20809@ilyx.ru>

> Sorry, In you reply not all patch.
> Main idea here -  we never flash last (struct tty_buffer) in the
> active buffer. Only data for ldisc. (tty->buf.head->read =
> tty->buf.head->commit). At that moment driver can collect(write) data
> in buffer without conflict.

Ah.. now I understand. Yes that makes sense.  I will think about that
carefully. This is why a good explanation with a patch is important.


^ permalink raw reply

* Re: [Suggestion] drivers/tty: drivers/char/:  for MAX_ASYNC_BUFFER_SIZE
From: Greg KH @ 2012-11-29 18:32 UTC (permalink / raw)
  To: Chen Gang; +Cc: linux-kernel@vger.kernel.org, linux-serial
In-Reply-To: <50B6F967.3050000@asianux.com>

On Thu, Nov 29, 2012 at 01:57:59PM +0800, Chen Gang wrote:
> > And, I really don't understand here, why do you want to change this?
> > What is it going to change?  And why?
> > 
> 
> Why:
>   for the context MGSLPC_INFO *info in drivers/char/pcmcia/synclink_cs.c
>     info->max_frame_size can be the value between 4096 .. 65535 (can be
> set by its module input parameter)
>     info->flag_buf length is 4096 (MAX_ASYNC_BUFFER_SIZE)
>   in function rx_get_frame
>     the framesize is limit by info->max_frame_size, but may still be
> larger that 4096.
>     when call function ldisc_receive_buf, info->flag_buf is equal to
> 4096, but framesize can be more than 4096. it will cause memory over flow.

Do you use that pcmcia driver for anything?  Are those cards still
around?

> What:
>   #define MAX_ASYNC_BUFFER_SIZE  0x10000 (instead of 4096, originally).
>   let it match the max frame size.
> 
> At last:
>   my suggestion may be incorrect, need relative member (who expert about
> it) to help checking.

That driver might be incorrect, yes, care to make up a patch for it and
test it to verify it fixes the problem?

thanks,

greg k-h

^ permalink raw reply

* Re: [PATCH] tty: Correct tty buffer flushing.
From: Ilya Zykov @ 2012-11-29 17:48 UTC (permalink / raw)
  To: Alan Cox
  Cc: Xiaobing Tu, Alan Cox, Jiri Slaby, Alek Du, Greg Kroah-Hartman,
	linux-kernel, linux-serial
In-Reply-To: <20121129135418.4237e7f8@pyramind.ukuu.org.uk>

On 29.11.2012 17:54, Alan Cox wrote:
>> diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
>> index 6c9b7cd..4f02f9c 100644
>> --- a/drivers/tty/tty_buffer.c
>> +++ b/drivers/tty/tty_buffer.c
>> @@ -114,11 +114,14 @@ static void __tty_buffer_flush(struct tty_struct *tty)
>>  {
>>  	struct tty_buffer *thead;
>>  
>> -	while ((thead = tty->buf.head) != NULL) {
>> -		tty->buf.head = thead->next;
>> -		tty_buffer_free(tty, thead);
>> +	if (tty->buf.head == NULL)
>> +		return;
>> +	while ((thead = tty->buf.head->next) != NULL) {
>> +		tty_buffer_free(tty, tty->buf.head);
>> +		tty->buf.head = thead;
> 
> This part of the change seems to have no effect at all. There are no
> locks held so there is nothing guaranteeing how the other processors
> views of the order of operations will be affected.
> 
> Alan
> 

Test program for this problem, 
after revert "commit f8f72f047" without "[PATCH] tty: Correct tty buffer flushing.", 
it cause of "BUG report"(see below) on SMP system linux-3.7.0-rc7.
Both patches resolve problem for this test. But my patch is more right.IMHO. 
And also fix problem with tty_prepare_string*(). 
Thank you.
----------------------------------------------------------------------
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <stdlib.h>

#define BUF_SIZE 4
#define ERROR_EXIT_CODE 1
#define parent child_id

static int
mfd=-1, sfd=-1, parent=1;

static char
pty_name[24];

static void
pty_exit(int ret, char * exit_message){
	if (sfd >= 0) close(sfd);
	if (mfd >= 0) close(mfd);
	printf("%s %s exit. \n %s",ret?"Error":"Normal", parent?"parent":"child",
			exit_message?exit_message:"");
	exit(ret);
}

static void
pty_init(void){
	int ptn;
	if( (mfd=open("/dev/ptmx", O_RDWR )) < 0 )
		pty_exit(ERROR_EXIT_CODE,"Couldn't open /dev/ptmx. \n");
	if (ioctl(mfd, TIOCGPTN, &ptn) < 0 )
		pty_exit(ERROR_EXIT_CODE,"Couldn't get pty number. \n");
	snprintf(pty_name, sizeof(pty_name), "/dev/pts/%d", ptn);
	printf("Slave pty name = %s.\n",pty_name);
	ptn=0;
	if (ioctl(mfd, TIOCSPTLCK, &ptn) < 0 )
		pty_exit(ERROR_EXIT_CODE,"Couldn't unlock pty slave. \n");
	if ( (sfd=open(pty_name, O_RDWR )) < 0 )
		pty_exit(ERROR_EXIT_CODE, "Couldn't open pty slave. \n");
}

int main(int argc,char *argv[]) {
	pty_init();
	char buf[]={ [0 ... BUF_SIZE-1]='1' };

	child_id=fork();
do {
	if(parent) {
		if ( write(mfd, buf, BUF_SIZE) < 0 )
			pty_exit(ERROR_EXIT_CODE, "Parent's write() error.\n");
	} else { //Child
		if ( tcflush(sfd, TCIFLUSH) < 0 )
			pty_exit(ERROR_EXIT_CODE, "Child's tcflush() error.\n");
	}
}	while(1);
	return 0; //Never
}
----------------------------------------------------------------------

Nov 29 20:42:07 bm kernel: BUG: unable to handle kernel NULL pointer dereference at 0000000000000018
Nov 29 20:42:07 bm kernel: IP: [<ffffffff81343294>] tty_insert_flip_string_fixed_flag+0x74/0xd0
Nov 29 20:42:07 bm kernel: PGD 114bc8067 PUD 11149d067 PMD 0 
Nov 29 20:42:07 bm kernel: Oops: 0000 [#1] SMP 
Nov 29 20:42:07 bm kernel: Modules linked in: fuse autofs4 sunrpc cpufreq_ondemand acpi_cpufreq freq_table mperf ipt_REJECT nf_conntrack_ipv4 nf_defrag_ipv4 iptable_filter ip_tables ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables ipv6 dm_mirror dm_region_hash dm_log dm_mod uinput iTCO_wdt iTCO_vendor_support gpio_ich sg joydev coretemp kvm_intel kvm microcode pcspkr serio_raw i2c_i801 asus_atk0110 hwmon lpc_ich sky2 snd_hda_codec_analog snd_hda_intel snd_hda_codec snd_hwdep snd_seq snd_seq_device snd_pcm snd_timer snd soundcore snd_page_alloc nvidia(PO) ext3 jbd mbcache sr_mod cdrom sd_mod crc_t10dif pata_acpi ata_generic pata_jmicron ahci libahci
Nov 29 20:42:07 bm kernel: CPU 1 
Nov 29 20:42:07 bm kernel: Pid: 8953, comm: a.out Tainted: P           O 3.7.0-rc7-1+ #23 System manufacturer P5K Premium/P5K Premium
Nov 29 20:42:07 bm kernel: RIP: 0010:[<ffffffff81343294>]  [<ffffffff81343294>] tty_insert_flip_string_fixed_flag+0x74/0xd0
Nov 29 20:42:07 bm kernel: RSP: 0018:ffff88011dee5d58  EFLAGS: 00010202
Nov 29 20:42:07 bm kernel: RAX: 0000000000000004 RBX: ffff88012934d000 RCX: ffff880119cfbc00
Nov 29 20:42:07 bm kernel: RDX: 0000000000000246 RSI: ffff880112de1800 RDI: 0000000000000246
Nov 29 20:42:07 bm kernel: RBP: ffff88011dee5da8 R08: ffff880112de1800 R09: 0000000000000000
Nov 29 20:42:07 bm kernel: R10: 00007fffcd7827a0 R11: 0000000000000246 R12: 0000000000000000
Nov 29 20:42:07 bm kernel: R13: 0000000000000004 R14: 0000000000000004 R15: 0000000000000004
Nov 29 20:42:07 bm kernel: FS:  00007f1e28985700(0000) GS:ffff88012fc80000(0000) knlGS:0000000000000000
Nov 29 20:42:07 bm kernel: CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
Nov 29 20:42:07 bm kernel: CR2: 0000000000000018 CR3: 0000000110321000 CR4: 00000000000407e0
Nov 29 20:42:07 bm kernel: DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
Nov 29 20:42:07 bm kernel: DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Nov 29 20:42:07 bm kernel: Process a.out (pid: 8953, threadinfo ffff88011dee4000, task ffff880128ba80c0)
Nov 29 20:42:07 bm kernel: Stack:
Nov 29 20:42:07 bm kernel: ffff88011dee5e78 0000000028852800 ffff880112de1800 0000000000000004
Nov 29 20:42:07 bm kernel: 0000000000000004 ffff880128852800 ffff88012934d000 ffff880112de1800
Nov 29 20:42:07 bm kernel: 0000000000000004 ffff880112de1800 ffff88011dee5dd8 ffffffff8134444b
Nov 29 20:42:07 bm kernel: Call Trace:
Nov 29 20:42:07 bm kernel: [<ffffffff8134444b>] pty_write+0x3b/0x80
Nov 29 20:42:07 bm kernel: [<ffffffff8107bcee>] ? add_wait_queue+0x4e/0x60
Nov 29 20:42:07 bm kernel: [<ffffffff8133e300>] n_tty_write+0x210/0x2e0
Nov 29 20:42:07 bm kernel: [<ffffffff8108ec10>] ? try_to_wake_up+0x2b0/0x2b0
Nov 29 20:42:07 bm kernel: [<ffffffff8133a161>] tty_write+0x1b1/0x290
Nov 29 20:42:07 bm kernel: [<ffffffff8133e0f0>] ? n_tty_ioctl+0xf0/0xf0
Nov 29 20:42:07 bm kernel: [<ffffffff8117d5d8>] vfs_write+0xc8/0x190
Nov 29 20:42:07 bm kernel: [<ffffffff8117de0f>] sys_write+0x5f/0xa0
Nov 29 20:42:07 bm kernel: [<ffffffff810d7a26>] ? __audit_syscall_exit+0x426/0x480
Nov 29 20:42:07 bm kernel: [<ffffffff815b4819>] system_call_fastpath+0x16/0x1b
Nov 29 20:42:07 bm kernel: Code: 00 07 00 00 48 0f 47 f0 48 63 f6 e8 47 fd ff ff 85 c0 41 89 c5 4c 8b a3 a8 01 00 00 74 42 48 98 48 8b 75 c0 45 01 ee 48 89 45 c8 <49> 63 7c 24 18 48 89 c2 49 03 7c 24 08 e8 9a 32 f4 ff 49 63 7c 
Nov 29 20:42:07 bm kernel: RIP  [<ffffffff81343294>] tty_insert_flip_string_fixed_flag+0x74/0xd0
Nov 29 20:42:07 bm kernel: RSP <ffff88011dee5d58>
Nov 29 20:42:07 bm kernel: CR2: 0000000000000018


^ permalink raw reply

* Re: [PATCH] tty: Correct tty buffer flushing.
From: Ilya Zykov @ 2012-11-29 14:13 UTC (permalink / raw)
  To: Alan Cox
  Cc: Xiaobing Tu, Alan Cox, Jiri Slaby, Alek Du, Greg Kroah-Hartman,
	linux-kernel, linux-serial
In-Reply-To: <20121129135418.4237e7f8@pyramind.ukuu.org.uk>

On 29.11.2012 17:54, Alan Cox wrote:
>> diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
>> index 6c9b7cd..4f02f9c 100644
>> --- a/drivers/tty/tty_buffer.c
>> +++ b/drivers/tty/tty_buffer.c
>> @@ -114,11 +114,14 @@ static void __tty_buffer_flush(struct tty_struct *tty)
>>  {
>>  	struct tty_buffer *thead;
>>  
>> -	while ((thead = tty->buf.head) != NULL) {
>> -		tty->buf.head = thead->next;
>> -		tty_buffer_free(tty, thead);
>> +	if (tty->buf.head == NULL)
>> +		return;
>> +	while ((thead = tty->buf.head->next) != NULL) {
>> +		tty_buffer_free(tty, tty->buf.head);
>> +		tty->buf.head = thead;
> 
> This part of the change seems to have no effect at all. There are no
> locks held so there is nothing guaranteeing how the other processors
> views of the order of operations will be affected.
> 
> Alan
> 
Sorry, In you reply not all patch.
Main idea here -  we never flash last (struct tty_buffer) in the active buffer.
Only data for ldisc. (tty->buf.head->read = tty->buf.head->commit).
At that moment driver can collect(write) data in buffer without conflict.


--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -114,11 +114,14 @@ static void __tty_buffer_flush(struct tty_struct *tty)
 {
 	struct tty_buffer *thead;
 
-	while ((thead = tty->buf.head) != NULL) {
-		tty->buf.head = thead->next;
-		tty_buffer_free(tty, thead);
+	if (tty->buf.head == NULL)
+		return;
+	while ((thead = tty->buf.head->next) != NULL) {
+		tty_buffer_free(tty, tty->buf.head);
+		tty->buf.head = thead;
 	}
-	tty->buf.tail = NULL;
+	WARN_ON(tty->buf.head != tty->buf.tail);
+	tty->buf.head->read = tty->buf.head->commit;
 }
 
 /**

^ permalink raw reply

* Re: [PATCH] tty: Correct tty buffer flushing.
From: Ilya Zykov @ 2012-11-29 14:03 UTC (permalink / raw)
  To: Alan Cox
  Cc: Xiaobing Tu, Alan Cox, Jiri Slaby, Alek Du, Greg Kroah-Hartman,
	linux-kernel, linux-serial
In-Reply-To: <20121129135418.4237e7f8@pyramind.ukuu.org.uk>

On 29.11.2012 17:54, Alan Cox wrote:
>> diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
>> index 6c9b7cd..4f02f9c 100644
>> --- a/drivers/tty/tty_buffer.c
>> +++ b/drivers/tty/tty_buffer.c
>> @@ -114,11 +114,14 @@ static void __tty_buffer_flush(struct tty_struct *tty)
>>  {
>>  	struct tty_buffer *thead;
>>  
>> -	while ((thead = tty->buf.head) != NULL) {
>> -		tty->buf.head = thead->next;
>> -		tty_buffer_free(tty, thead);
>> +	if (tty->buf.head == NULL)
>> +		return;
>> +	while ((thead = tty->buf.head->next) != NULL) {
>> +		tty_buffer_free(tty, tty->buf.head);
>> +		tty->buf.head = thead;
> 
> This part of the change seems to have no effect at all. There are no
> locks held so there is nothing guaranteeing how the other processors
> views of the order of operations will be affected.
> 
> Alan
> 
/**
 *	__tty_buffer_flush		-	flush full tty buffers
 *	@tty: tty to flush
 *
 *	flush all the buffers containing receive data. Caller must
 *	hold the buffer lock and must have ensured no parallel flush to
 *	ldisc is running.
 *
 *	Locking: Caller must hold tty->buf.lock
 */

Please, don't ignore my patch.
Please, Look at it one more time thoroughly.
Before REVERT [PATCH] tty: hold lock across tty buffer finding and buffer filling.
Thank you.



^ permalink raw reply

* Re: [PATCH] tty: Correct tty buffer flushing.
From: Alan Cox @ 2012-11-29 13:54 UTC (permalink / raw)
  To: Ilya Zykov
  Cc: Xiaobing Tu, Alan Cox, Jiri Slaby, Alek Du, Greg Kroah-Hartman,
	linux-kernel, linux-serial
In-Reply-To: <50B6A652.9000107@ilyx.ru>

> diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
> index 6c9b7cd..4f02f9c 100644
> --- a/drivers/tty/tty_buffer.c
> +++ b/drivers/tty/tty_buffer.c
> @@ -114,11 +114,14 @@ static void __tty_buffer_flush(struct tty_struct *tty)
>  {
>  	struct tty_buffer *thead;
>  
> -	while ((thead = tty->buf.head) != NULL) {
> -		tty->buf.head = thead->next;
> -		tty_buffer_free(tty, thead);
> +	if (tty->buf.head == NULL)
> +		return;
> +	while ((thead = tty->buf.head->next) != NULL) {
> +		tty_buffer_free(tty, tty->buf.head);
> +		tty->buf.head = thead;

This part of the change seems to have no effect at all. There are no
locks held so there is nothing guaranteeing how the other processors
views of the order of operations will be affected.

Alan

^ permalink raw reply

* Re: [PATCH] MAINTAINERS: TTY - Add linux-serial mailing list
From: Jiri Slaby @ 2012-11-29  8:23 UTC (permalink / raw)
  To: Joe Perches
  Cc: Greg KH, Chen Gang, linux-kernel@vger.kernel.org, linux-serial
In-Reply-To: <1354169679.29762.28.camel@joe-AO722>

On 11/29/2012 07:14 AM, Joe Perches wrote:
> Signed-off-by: Joe Perches <joe@perches.com>
> ---
>  MAINTAINERS |    1 +
>  1 files changed, 1 insertions(+), 0 deletions(-)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index afc0b27..255dafb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7658,6 +7658,7 @@ K:	^Subject:.*(?i)trivial
>  TTY LAYER
>  M:	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>  M:	Jiri Slaby <jslaby@suse.cz>
> +L:	linux-serial@vger.kernel.org

This might have the effect that people will try to reach us at that
list. But this is not true at least for me. serial != tty.

thanks,
-- 
js
suse labs

^ permalink raw reply

* Re: [PATCH] MAINTAINERS: TTY - Add linux-serial mailing list
From: Chen Gang @ 2012-11-29  6:27 UTC (permalink / raw)
  To: Joe Perches
  Cc: Greg KH, Jiri Slaby, linux-kernel@vger.kernel.org, linux-serial
In-Reply-To: <1354169679.29762.28.camel@joe-AO722>

于 2012年11月29日 14:14, Joe Perches 写道:
> Signed-off-by: Joe Perches <joe@perches.com>
> ---
>  MAINTAINERS |    1 +
>  1 files changed, 1 insertions(+), 0 deletions(-)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index afc0b27..255dafb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7658,6 +7658,7 @@ K:	^Subject:.*(?i)trivial
>  TTY LAYER
>  M:	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>  M:	Jiri Slaby <jslaby@suse.cz>
> +L:	linux-serial@vger.kernel.org
>  S:	Supported
>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
>  F:	drivers/tty/
> 
> 
> 
> 

  thank you very much.


-- 
Chen Gang

Asianux Corporation
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] MAINTAINERS: TTY - Add linux-serial mailing list
From: Joe Perches @ 2012-11-29  6:14 UTC (permalink / raw)
  To: Greg KH, Jiri Slaby; +Cc: Chen Gang, linux-kernel@vger.kernel.org, linux-serial
In-Reply-To: <50B6F967.3050000@asianux.com>

Signed-off-by: Joe Perches <joe@perches.com>
---
 MAINTAINERS |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index afc0b27..255dafb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7658,6 +7658,7 @@ K:	^Subject:.*(?i)trivial
 TTY LAYER
 M:	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 M:	Jiri Slaby <jslaby@suse.cz>
+L:	linux-serial@vger.kernel.org
 S:	Supported
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
 F:	drivers/tty/



^ permalink raw reply related

* Re: [Suggestion] drivers/tty: drivers/char/:  for MAX_ASYNC_BUFFER_SIZE
From: Chen Gang @ 2012-11-29  5:57 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel@vger.kernel.org, linux-serial
In-Reply-To: <20121129051335.GA4375@kroah.com>

于 2012年11月29日 13:13, Greg KH 写道:
> On Thu, Nov 29, 2012 at 12:40:49PM +0800, Chen Gang wrote:
>> Hello Greg Kroah-Hartman:
>>
>> for MAX_ASYNC_BUFFER_SIZE:
>>   it is defined as 4096;
>>   but for the max buffer size which it processes, is 65535.
>>   so suggest to #define MAX_ASYNC_BUFFER_SIZE 0x10000  (better than 0xffff)
> 
> Please, send tty questions to the linux-serial@vger.kernel.org list
> also.

  I cc to linux-serial@vger.kernel.org, in this reply.

  I referenced the file MAINTAINERS, before sent original mail:

  it seems all drivers/tty/serial/* are relative with
linux-serial@vger.kernel.org. but our case is not relative
drivers/tty/serial. so for not bother them, I am not send to them,
originally.

  in MAINTAINERS, line 7438, I find for common of driver/tty/*, can send
to you and no cc, so I send you and cc to linux-kernel@vger.kernel.org.

  next time, for all tty questions, I will cc to
linux-serial@vger.kernel.org (not cc to linux-kernel@vger.kernel.org).

> 
> And, I really don't understand here, why do you want to change this?
> What is it going to change?  And why?
> 

Why:
  for the context MGSLPC_INFO *info in drivers/char/pcmcia/synclink_cs.c
    info->max_frame_size can be the value between 4096 .. 65535 (can be
set by its module input parameter)
    info->flag_buf length is 4096 (MAX_ASYNC_BUFFER_SIZE)
  in function rx_get_frame
    the framesize is limit by info->max_frame_size, but may still be
larger that 4096.
    when call function ldisc_receive_buf, info->flag_buf is equal to
4096, but framesize can be more than 4096. it will cause memory over flow.

What:
  #define MAX_ASYNC_BUFFER_SIZE  0x10000 (instead of 4096, originally).
  let it match the max frame size.

At last:
  my suggestion may be incorrect, need relative member (who expert about
it) to help checking.


  welcome another suggestion or completions.

  thanks.

gchen.

> greg k-h
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
> 
> 


-- 
Chen Gang

Asianux Corporation

^ permalink raw reply

* Fwd: [PATCH] tty: Correct tty buffer flushing.
From: Ilya Zykov @ 2012-11-29  0:03 UTC (permalink / raw)
  To: Xiaobing Tu
  Cc: Alan Cox, Jiri Slaby, Alek Du, Greg Kroah-Hartman, ilya,
	linux-kernel, linux-serial
In-Reply-To: <50B55A93.4090900@ilyx.ru>

This patch abolish:
[PATCH] tty: hold lock across tty buffer finding and buffer filling.
commit f8f72f047b96c6c8b13f6e3ba53fa6feb4266813

The commit f8f72f047 very dirty, has many degradation on SMP systems, because take
spinlock at long time, and it doesn't resolve problem with tty_prepare_string*().
We lose all advantage from the use of flip buffer. Can't write/read to/from buffer
without lock.

The root of problem it use carelessly buffer flushing, then another thread can write to it.
This patch resolves this problem and doesn't let lose advantage from flip buffer use.

Signed-off-by: Ilya Zykov <ilya@ilyx.ru>
---
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index 6c9b7cd..4f02f9c 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -114,11 +114,14 @@ static void __tty_buffer_flush(struct tty_struct *tty)
 {
 	struct tty_buffer *thead;
 
-	while ((thead = tty->buf.head) != NULL) {
-		tty->buf.head = thead->next;
-		tty_buffer_free(tty, thead);
+	if (tty->buf.head == NULL)
+		return;
+	while ((thead = tty->buf.head->next) != NULL) {
+		tty_buffer_free(tty, tty->buf.head);
+		tty->buf.head = thead;
 	}
-	tty->buf.tail = NULL;
+	WARN_ON(tty->buf.head != tty->buf.tail);
+	tty->buf.head->read = tty->buf.head->commit;
 }
 
 /**

^ permalink raw reply related

* Re: Comments requested: driver for Quatech PCI serial cards
From: Jonathan Woithe @ 2012-11-28 22:33 UTC (permalink / raw)
  To: Greg KH; +Cc: Alan Cox, linux-serial, Jonathan Woithe
In-Reply-To: <20121128173010.GC32286@kroah.com>

On Wed, Nov 28, 2012 at 09:30:10AM -0800, Greg KH wrote:
> On Thu, Nov 29, 2012 at 12:11:40AM +1030, Jonathan Woithe wrote:
> > On Wed, Nov 28, 2012 at 01:24:19PM +0000, Alan Cox wrote:
> > > Can we get a Signed-off-by: for the first patch so we can try and get it
> > > into 3.8 ?
> > 
> > Sure.  See below.  The rest of the previous commit message was fine so I
> > haven't replicated it here.
> 
> Please do, otherwise I have to hand-edit the patch to add it back, and
> odds are, I will get it wrong...

No problem.  See below.  I took the opportunity to tweak the commit message
a touch.

jonathan


From: Alan Cox <alan@linux.intel.com>

quatech: add the other serial identifiers and preliminary control code
    
Jonathan Woithe posted an out of tree enabler/control module for these
cards.  Lift the relevant identifiers and put them in the 8250_pci driver
along with code used to control custom registers on these cards.
    
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Jonathan Woithe <jwoithe@just42.net>

---
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 17b7d26..43c6b4f 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1040,6 +1040,253 @@ static int pci_asix_setup(struct serial_private *priv,
 	return pci_default_setup(priv, board, port, idx);
 }
 
+/* Quatech devices have their own extra interface features */
+
+struct quatech_feature {
+	u16 devid;
+	bool amcc;
+};
+
+#define QPCR_TEST_FOR1		0x3F
+#define QPCR_TEST_GET1		0x00
+#define QPCR_TEST_FOR2		0x40
+#define QPCR_TEST_GET2		0x40
+#define QPCR_TEST_FOR3		0x80
+#define QPCR_TEST_GET3		0x40
+#define QPCR_TEST_FOR4		0xC0
+#define QPCR_TEST_GET4		0x80
+
+#define QOPR_CLOCK_X1		0x0000
+#define QOPR_CLOCK_X2		0x0001
+#define QOPR_CLOCK_X4		0x0002
+#define QOPR_CLOCK_X8		0x0003
+#define QOPR_CLOCK_RATE_MASK	0x0003
+
+
+static struct quatech_feature quatech_cards[] = {
+	{ PCI_DEVICE_ID_QUATECH_QSC100,   1 },
+	{ PCI_DEVICE_ID_QUATECH_DSC100,   1 },
+	{ PCI_DEVICE_ID_QUATECH_DSC100E,  0 },
+	{ PCI_DEVICE_ID_QUATECH_DSC200,   1 },
+	{ PCI_DEVICE_ID_QUATECH_DSC200E,  0 },
+	{ PCI_DEVICE_ID_QUATECH_ESC100D,  1 },
+	{ PCI_DEVICE_ID_QUATECH_ESC100M,  1 },
+	{ PCI_DEVICE_ID_QUATECH_QSCP100,  1 },
+	{ PCI_DEVICE_ID_QUATECH_DSCP100,  1 },
+	{ PCI_DEVICE_ID_QUATECH_QSCP200,  1 },
+	{ PCI_DEVICE_ID_QUATECH_DSCP200,  1 },
+	{ PCI_DEVICE_ID_QUATECH_ESCLP100, 0 },
+	{ PCI_DEVICE_ID_QUATECH_QSCLP100, 0 },
+	{ PCI_DEVICE_ID_QUATECH_DSCLP100, 0 },
+	{ PCI_DEVICE_ID_QUATECH_SSCLP100, 0 },
+	{ PCI_DEVICE_ID_QUATECH_QSCLP200, 0 },
+	{ PCI_DEVICE_ID_QUATECH_DSCLP200, 0 },
+	{ PCI_DEVICE_ID_QUATECH_SSCLP200, 0 },
+	{ PCI_DEVICE_ID_QUATECH_SPPXP_100, 0 },
+	{ 0, }
+};
+
+static int pci_quatech_amcc(u16 devid)
+{
+	struct quatech_feature *qf = &quatech_cards[0];
+	while (qf->devid) {
+		if (qf->devid == devid)
+			return qf->amcc;
+		qf++;
+	}
+	pr_err("quatech: unknown port type '0x%04X'.\n", devid);
+	return 0;
+};
+
+static int pci_quatech_rqopr(struct uart_8250_port *port)
+{
+	unsigned long base = port->port.iobase;
+	u8 LCR, val;
+
+	LCR = inb(base + UART_LCR);
+	outb(0xBF, base + UART_LCR);
+	val = inb(base + UART_SCR);
+	outb(LCR, base + UART_LCR);
+	return val;
+}
+
+static void pci_quatech_wqopr(struct uart_8250_port *port, u8 qopr)
+{
+	unsigned long base = port->port.iobase;
+	u8 LCR, val;
+
+	LCR = inb(base + UART_LCR);
+	outb(0xBF, base + UART_LCR);
+	val = inb(base + UART_SCR);
+	outb(qopr, base + UART_SCR);
+	outb(LCR, base + UART_LCR);
+}
+
+static int pci_quatech_rqmcr(struct uart_8250_port *port)
+{
+	unsigned long base = port->port.iobase;
+	u8 LCR, val, qmcr;
+
+	LCR = inb(base + UART_LCR);
+	outb(0xBF, base + UART_LCR);
+	val = inb(base + UART_SCR);
+	outb(val | 0x10, base + UART_SCR);
+	qmcr = inb(base + UART_MCR);
+	outb(val, base + UART_SCR);
+	outb(LCR, base + UART_LCR);
+
+	return qmcr;
+}
+
+static void pci_quatech_wqmcr(struct uart_8250_port *port, u8 qmcr)
+{
+	unsigned long base = port->port.iobase;
+	u8 LCR, val;
+
+	LCR = inb(base + UART_LCR);
+	outb(0xBF, base + UART_LCR);
+	val = inb(base + UART_SCR);
+	outb(val | 0x10, base + UART_SCR);
+	outb(qmcr, base + UART_MCR);
+	outb(val, base + UART_SCR);
+	outb(LCR, base + UART_LCR);
+}
+
+static int pci_quatech_has_qmcr(struct uart_8250_port *port)
+{
+	unsigned long base = port->port.iobase;
+	u8 LCR, val;
+
+	LCR = inb(base + UART_LCR);
+	outb(0xBF, base + UART_LCR);
+	val = inb(base + UART_SCR);
+	if (val & 0x20) {
+		outb(0x80, UART_LCR);
+		if (!(inb(UART_SCR) & 0x20)) {
+			outb(LCR, base + UART_LCR);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int pci_quatech_test(struct uart_8250_port *port)
+{
+	u8 reg;
+	u8 qopr = pci_quatech_rqopr(port);
+	pci_quatech_wqopr(port, qopr & QPCR_TEST_FOR1);
+	reg = pci_quatech_rqopr(port) & 0xC0;
+	if (reg != QPCR_TEST_GET1)
+		return -EINVAL;
+	pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR2);
+	reg = pci_quatech_rqopr(port) & 0xC0;
+	if (reg != QPCR_TEST_GET2)
+		return -EINVAL;
+	pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR3);
+	reg = pci_quatech_rqopr(port) & 0xC0;
+	if (reg != QPCR_TEST_GET3)
+		return -EINVAL;
+	pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR4);
+	reg = pci_quatech_rqopr(port) & 0xC0;
+	if (reg != QPCR_TEST_GET4)
+		return -EINVAL;
+
+	pci_quatech_wqopr(port, qopr);
+	return 0;	
+}
+
+static int pci_quatech_clock(struct uart_8250_port *port)
+{
+	u8 qopr, reg, set;
+	unsigned long clock;
+
+	if (pci_quatech_test(port) < 0)
+		return 1843200;
+
+	qopr = pci_quatech_rqopr(port);
+
+	pci_quatech_wqopr(port, qopr & ~QOPR_CLOCK_X8);
+	reg = pci_quatech_rqopr(port);
+	if (reg & QOPR_CLOCK_X8) {
+		clock = 1843200;
+		goto out;
+	}
+	pci_quatech_wqopr(port, qopr | QOPR_CLOCK_X8);
+	reg = pci_quatech_rqopr(port);
+	if (!(reg & QOPR_CLOCK_X8)) {
+		clock = 1843200;
+		goto out;
+	}
+	reg &= QOPR_CLOCK_X8;
+	if (reg == QOPR_CLOCK_X2) {
+		clock =  3685400;
+		set = QOPR_CLOCK_X2;
+	} else if (reg == QOPR_CLOCK_X4) {
+		clock = 7372800;
+		set = QOPR_CLOCK_X4;
+	} else if (reg == QOPR_CLOCK_X8) {
+		clock = 14745600;
+		set = QOPR_CLOCK_X8;
+	} else {
+		clock = 1843200;
+		set = QOPR_CLOCK_X1;
+	}
+	qopr &= ~QOPR_CLOCK_RATE_MASK;
+	qopr |= set;
+
+out:
+	pci_quatech_wqopr(port, qopr);
+	return clock;
+}
+
+static int pci_quatech_rs422(struct uart_8250_port *port)
+{
+	u8 qmcr;
+	int rs422 = 0;
+
+	if (!pci_quatech_has_qmcr(port))
+		return 0;
+	qmcr = pci_quatech_rqmcr(port);
+	pci_quatech_wqmcr(port, 0xFF);
+	if (pci_quatech_rqmcr(port))
+		rs422 = 1;
+	pci_quatech_wqmcr(port, qmcr);
+	return rs422;
+}
+
+static int pci_quatech_init(struct pci_dev *dev)
+{
+	if (pci_quatech_amcc(dev->device)) {
+		unsigned long base = pci_resource_start(dev, 0);
+		if (base) {
+			u32 tmp;
+			outl(inl(base + 0x38), base + 0x38);
+			tmp = inl(base + 0x3c);
+			outl(tmp | 0x01000000, base + 0x3c);
+			outl(tmp, base + 0x3c);
+		}
+	}
+	return 0;
+}
+
+static int pci_quatech_setup(struct serial_private *priv,
+		  const struct pciserial_board *board,
+		  struct uart_8250_port *port, int idx)
+{
+	/* Needed by pci_quatech calls below */
+	port->port.iobase = pci_resource_start(priv->dev, FL_GET_BASE(board->flags));
+	/* Set up the clocking */
+	port->port.uartclk = pci_quatech_clock(port);
+	/* For now just warn about RS422 */
+	if (pci_quatech_rs422(port)) 
+		pr_warn( "quatech: software control of RS422 features not currently supported.\n");
+	return pci_default_setup(priv, board, port, idx);
+}
+
+static void __devexit pci_quatech_exit(struct pci_dev *dev)
+{
+}
+
 static int pci_default_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
 		  struct uart_8250_port *port, int idx)
@@ -1503,6 +1750,16 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
 		.setup		= pci_default_setup,
 		.exit		= __devexit_p(pci_plx9050_exit),
 	},
+	/* Quatech */
+	{
+		.vendor		= PCI_VENDOR_ID_QUATECH,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_quatech_init,
+		.setup		= pci_quatech_setup,
+		.exit		= __devexit_p(pci_quatech_exit),
+	},
 	/*
 	 * SBS Technologies, Inc., PMC-OCTALPRO 232
 	 */
@@ -3257,18 +3514,70 @@ static struct pci_device_id serial_pci_tbl[] = {
 	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
 		0x10b5, 0x106a, 0, 0,
 		pbn_plx_romulus },
+	/*
+	 * Quatech cards. These actually have configurable clocks but for
+	 * now we just use the default.
+	 *
+	 * 100 series are RS232, 200 series RS422, 
+	 */
 	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b1_4_115200 },
 	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b1_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_4_115200 },
 	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b1_8_115200 },
 	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b1_8_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_4_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_4_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_4_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_1_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_4_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_1_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESCLP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_8_115200 },
+
 	{	PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
 		PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
 		0, 0,
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 9d36b82..ce45006 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1867,8 +1867,23 @@
 #define PCI_VENDOR_ID_QUATECH		0x135C
 #define PCI_DEVICE_ID_QUATECH_QSC100	0x0010
 #define PCI_DEVICE_ID_QUATECH_DSC100	0x0020
+#define PCI_DEVICE_ID_QUATECH_DSC200	0x0030
+#define PCI_DEVICE_ID_QUATECH_QSC200	0x0040
 #define PCI_DEVICE_ID_QUATECH_ESC100D	0x0050
 #define PCI_DEVICE_ID_QUATECH_ESC100M	0x0060
+#define PCI_DEVICE_ID_QUATECH_QSCP100	0x0120
+#define PCI_DEVICE_ID_QUATECH_DSCP100	0x0130
+#define PCI_DEVICE_ID_QUATECH_QSCP200	0x0140
+#define PCI_DEVICE_ID_QUATECH_DSCP200	0x0150
+#define PCI_DEVICE_ID_QUATECH_QSCLP100	0x0170
+#define PCI_DEVICE_ID_QUATECH_DSCLP100	0x0180
+#define PCI_DEVICE_ID_QUATECH_DSC100E	0x0181
+#define PCI_DEVICE_ID_QUATECH_SSCLP100	0x0190
+#define PCI_DEVICE_ID_QUATECH_QSCLP200	0x01A0
+#define PCI_DEVICE_ID_QUATECH_DSCLP200	0x01B0
+#define PCI_DEVICE_ID_QUATECH_DSC200E	0x01B1
+#define PCI_DEVICE_ID_QUATECH_SSCLP200	0x01C0
+#define PCI_DEVICE_ID_QUATECH_ESCLP100	0x01E0
 #define PCI_DEVICE_ID_QUATECH_SPPXP_100 0x0278
 
 #define PCI_VENDOR_ID_SEALEVEL		0x135e

^ permalink raw reply related

* [PATCH] OMAP/serial: Support 1Mbaud and similar baudrates that require Mode16 instead of Mode13
From: Alexey Pelykh @ 2012-11-28 19:03 UTC (permalink / raw)
  To: Alan Cox, Greg KH; +Cc: linux-serial

From: Alexey Pelykh <alexey.pelykh@gmail.com>

Original table in OMAP TRM named "UART Mode Baud Rates, Divisor
Values, and Error Rates" determines modes not for all common baud
rates. E.g. for 1000000 baud rate mode should be 16x, but according to
that table it's determined as 13x. According to current implementation
of mode divisor selection, after requesting 1000000 baudrate from
driver, later one will configure chip to use MODE13 divisor. Assuming
48Mhz as common UART clock speed, MODE13 divisor will effectively give
1230769 baudrate, what is quite far from desired 1000000 baudrate.
While with MODE16 divisor, chip will produce exact 1000000 baudrate.
In old driver that served UART devices (8250.c and serial_core.c) this
divisor could have been configured by user-space program, but in
omap_serial.c driver implementation this ability was not implemented
(afaik, by design) thus disallowing proper usage of MODE16-compatible
baudrates.

Signed-off-by: Alexey Pelykh <alexey.pelykh@gmail.com>

---
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 6d3d26a..6d567dd 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -182,24 +182,42 @@ static void serial_omap_enable_wakeup(struct
uart_omap_port *up, bool enable)
 }

 /*
+ * serial_omap_baud_is_mode16 - check if baud rate is MODE16X
+ * @port: uart port info
+ * @baud: baudrate for which mode needs to be determined
+ *
+ * Returns true if baud rate is MODE16X and false if MODE13X
+ * Original table in OMAP TRM named "UART Mode Baud Rates, Divisor Values,
+ * and Error Rates" determines modes not for all common baud rates.
+ * E.g. for 1000000 baud rate mode must be 16x, but according to that
+ * table it's determined as 13x.
+ */
+static bool
+serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud)
+{
+       unsigned int n13 = port->uartclk / (13 * baud);
+       unsigned int n16 = port->uartclk / (16 * baud);
+       int baudAbsDiff13 = baud - (port->uartclk / (13 * n13));
+       int baudAbsDiff16 = baud - (port->uartclk / (16 * n16));
+       if(baudAbsDiff13 < 0)
+               baudAbsDiff13 = -baudAbsDiff13;
+       if(baudAbsDiff16 < 0)
+               baudAbsDiff16 = -baudAbsDiff16;
+
+       return (baudAbsDiff13 > baudAbsDiff16);
+}
+
+/*
  * serial_omap_get_divisor - calculate divisor value
  * @port: uart port info
  * @baud: baudrate for which divisor needs to be calculated.
- *
- * We have written our own function to get the divisor so as to support
- * 13x mode. 3Mbps Baudrate as an different divisor.
- * Reference OMAP TRM Chapter 17:
- * Table 17-1. UART Mode Baud Rates, Divisor Values, and Error Rates
- * referring to oversampling - divisor value
- * baudrate 460,800 to 3,686,400 all have divisor 13
- * except 3,000,000 which has divisor value 16
  */
 static unsigned int
 serial_omap_get_divisor(struct uart_port *port, unsigned int baud)
 {
        unsigned int divisor;

-       if (baud > OMAP_MODE13X_SPEED && baud != 3000000)
+       if (!serial_omap_baud_is_mode16(port, baud))
                divisor = 13;
        else
                divisor = 16;
@@ -893,7 +911,7 @@ serial_omap_set_termios(struct uart_port *port,
struct ktermios *termios,
        serial_out(up, UART_EFR, up->efr);
        serial_out(up, UART_LCR, cval);

-       if (baud > 230400 && baud != 3000000)
+       if (!serial_omap_baud_is_mode16(port, baud))
                up->mdr1 = UART_OMAP_MDR1_13X_MODE;
        else
                up->mdr1 = UART_OMAP_MDR1_16X_MODE;

^ permalink raw reply related

* Re: Fwd: [PULL REQUEST] Tweak to support 1Mbaud and similar baudrates that require Mode16 instead of Mode13
From: Greg KH @ 2012-11-28 17:32 UTC (permalink / raw)
  To: Alexey Pelykh; +Cc: Alan Cox, linux-serial
In-Reply-To: <CAOmKuSq52wWgetb53S1HiMNghT_A3UukPaUO0E9dXtWor+GVvw@mail.gmail.com>

On Wed, Nov 28, 2012 at 07:26:32PM +0200, Alexey Pelykh wrote:
> Sorry, probably I've misunderstood you. Should I attach patch as a file to
> email or just embed the patch content (as I did last time) into email body?

As I stated, the file, Documentation/SubmittingPatches decribes all of
this.

thanks,

greg k-h

^ permalink raw reply

* Re: Comments requested: driver for Quatech PCI serial cards
From: Greg KH @ 2012-11-28 17:30 UTC (permalink / raw)
  To: Jonathan Woithe; +Cc: Alan Cox, linux-serial
In-Reply-To: <20121128134140.GA29452@marvin.atrad.com.au>

On Thu, Nov 29, 2012 at 12:11:40AM +1030, Jonathan Woithe wrote:
> On Wed, Nov 28, 2012 at 01:24:19PM +0000, Alan Cox wrote:
> > > With regard to the ioctl, should this be an implementation of the ioctl
> > > provided by the old out-of-tree driver, or is there an established
> > > standard/example which I should write against instead?
> > 
> > We have a standard (if possibly needing a few bits adding) ioctl for
> > basic RS422/485 features. That's TIOCG/SRS485 (which for all the naming
> > is also kind of 422 and other related bits as well)
> 
> Ok, thanks for the pointer.  I'll take a look at this in due course.
> 
> > Can we get a Signed-off-by: for the first patch so we can try and get it
> > into 3.8 ?
> 
> Sure.  See below.  The rest of the previous commit message was fine so I
> haven't replicated it here.

Please do, otherwise I have to hand-edit the patch to add it back, and
odds are, I will get it wrong...

thanks,

greg k-h

^ permalink raw reply

* Re: Fwd: [PULL REQUEST] Tweak to support 1Mbaud and similar baudrates that require Mode16 instead of Mode13
From: Greg KH @ 2012-11-28 17:25 UTC (permalink / raw)
  To: Alexey Pelykh; +Cc: Alan Cox, linux-serial
In-Reply-To: <CAOmKuSr+PYcs37DdiOj+h8wq0hjrdtHN4pCUvj-y97fr6-0DCA@mail.gmail.com>

On Wed, Nov 28, 2012 at 11:05:08AM +0200, Alexey Pelykh wrote:
> Original table in OMAP TRM named "UART Mode Baud Rates, Divisor
> Values, and Error Rates" determines modes not for all common baud
> rates. E.g. for 1000000 baud rate mode should be 16x, but according to
> that table it's determined as 13x. According to current implementation
> of mode
> divisor selection, after requesting 1000000 baudrate from driver,
> later one will configure chip to use MODE13 divisor. Assuming 48Mhz as
> common UART clock speed, MODE13 divisor will effectively give 1230769
> baudrate, what is quite far from desired 1000000 baudrate. While with
> MODE16 divisor, chip will produce exact 1000000 baudrate.
> 
> In old driver that served UART devices (8250.c and serial_core.c) this
> divisor could have been configured by user-space program, but in
> omap_serial.c driver implementation this ability was not implemented
> (afaik, by design) thus disallowing proper usage of MODE16-compatible
> baudrates.
> 
> Changes: I've fixed styling errors using ./scripts/checkpatch.pl
> 
> Signed-off-by: Alexey Pelykh <alexey.pelykh@gmail.com>
> 
> The following changes since commit 77b67063bb6bce6d475e910d3b886a606d0d91f7:

Again, I will not take a pull request, why do you keep on sending this?

Please just send your patch through mail, in a format that I can apply
it in (as described in Documentation/SubmittingPatches).

thanks,

greg k-h

^ permalink raw reply

* [PATCH 5/5] serial: 8250_dw: Set FIFO size dynamically
From: Heikki Krogerus @ 2012-11-28 14:29 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Alan Cox, Jamie Iles, linux-serial, LKML
In-Reply-To: <20121128133704.GB14698@as23.pp.htv.fi>

Designware UART provides optional Component Parameter
Register that lists most of the capabilities of the UART,
including FIFO size. This uses that register to set FIFO
size for the port before registering it.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/tty/serial/8250/8250_dw.c |   57 ++++++++++++++++++++++++++++++++++---
 1 file changed, 53 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index bfbfb07..2041220 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -25,6 +25,28 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
+/* Offsets for the DesignWare specific registers */
+#define DW_UART_USR	0x1f /* UART Status Register */
+#define DW_UART_CPR	0xf4 /* Component Parameter Register */
+#define DW_UART_UCV	0xf8 /* UART Component Version */
+
+/* Component Parameter Register bits */
+#define DW_UART_CPR_ABP_DATA_WIDTH	(3 << 0)
+#define DW_UART_CPR_AFCE_MODE		(1 << 4)
+#define DW_UART_CPR_THRE_MODE		(1 << 5)
+#define DW_UART_CPR_SIR_MODE		(1 << 6)
+#define DW_UART_CPR_SIR_LP_MODE		(1 << 7)
+#define DW_UART_CPR_ADDITIONAL_FEATURES	(1 << 8)
+#define DW_UART_CPR_FIFO_ACCESS		(1 << 9)
+#define DW_UART_CPR_FIFO_STAT		(1 << 10)
+#define DW_UART_CPR_SHADOW		(1 << 11)
+#define DW_UART_CPR_ENCODED_PARMS	(1 << 12)
+#define DW_UART_CPR_DMA_EXTRA		(1 << 13)
+#define DW_UART_CPR_FIFO_MODE		(0xff << 16)
+/* Helper for fifo size calculation */
+#define DW_UART_CPR_FIFO_SIZE(a)	(((a >> 16) & 0xff) * 16)
+
+
 struct dw8250_data {
 	int	last_lcr;
 	int	line;
@@ -66,9 +88,6 @@ static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
 	return readl(p->membase + offset);
 }
 
-/* Offset for the DesignWare's UART Status Register. */
-#define UART_USR	0x1f
-
 static int dw8250_handle_irq(struct uart_port *p)
 {
 	struct dw8250_data *d = p->private_data;
@@ -78,7 +97,7 @@ static int dw8250_handle_irq(struct uart_port *p)
 		return 1;
 	} else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
 		/* Clear the USR and write the LCR again. */
-		(void)p->serial_in(p, UART_USR);
+		(void)p->serial_in(p, DW_UART_USR);
 		p->serial_out(p, d->last_lcr, UART_LCR);
 
 		return 1;
@@ -119,6 +138,34 @@ static int __devinit dw8250_probe_of(struct uart_port *p)
 	return 0;
 }
 
+static void __devinit dw8250_setup_port(struct uart_8250_port *up)
+{
+	struct uart_port	*p = &up->port;
+	u32			reg = readl(p->membase + DW_UART_UCV);
+
+	/*
+	 * If the Component Version Register returns zero, we know that
+	 * ADDITIONAL_FEATURES are not enabled. No need to go any further.
+	 */
+	if (!reg)
+		return;
+
+	dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n",
+		(reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
+
+	reg = readl(p->membase + DW_UART_CPR);
+	if (!reg)
+		return;
+
+	/* Select the type based on fifo */
+	if (reg & DW_UART_CPR_FIFO_MODE) {
+		p->type = PORT_16550A;
+		p->flags |= UPF_FIXED_TYPE;
+		p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
+		up->tx_loadsz = p->fifosize;
+	}
+}
+
 static int __devinit dw8250_probe(struct platform_device *pdev)
 {
 	struct uart_8250_port uart = {};
@@ -156,6 +203,8 @@ static int __devinit dw8250_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	dw8250_setup_port(&uart);
+
 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
-- 
1.7.10.4

^ permalink raw reply related

* Re: Comments requested: driver for Quatech PCI serial cards
From: Jonathan Woithe @ 2012-11-28 13:41 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-serial, jwoithe
In-Reply-To: <20121128132419.1094d2d3@pyramind.ukuu.org.uk>

On Wed, Nov 28, 2012 at 01:24:19PM +0000, Alan Cox wrote:
> > With regard to the ioctl, should this be an implementation of the ioctl
> > provided by the old out-of-tree driver, or is there an established
> > standard/example which I should write against instead?
> 
> We have a standard (if possibly needing a few bits adding) ioctl for
> basic RS422/485 features. That's TIOCG/SRS485 (which for all the naming
> is also kind of 422 and other related bits as well)

Ok, thanks for the pointer.  I'll take a look at this in due course.

> Can we get a Signed-off-by: for the first patch so we can try and get it
> into 3.8 ?

Sure.  See below.  The rest of the previous commit message was fine so I
haven't replicated it here.

jonathan



Signed-off-by: Jonathan Woithe <jwoithe@just42.net>

diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 17b7d26..43c6b4f 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1040,6 +1040,253 @@ static int pci_asix_setup(struct serial_private *priv,
 	return pci_default_setup(priv, board, port, idx);
 }
 
+/* Quatech devices have their own extra interface features */
+
+struct quatech_feature {
+	u16 devid;
+	bool amcc;
+};
+
+#define QPCR_TEST_FOR1		0x3F
+#define QPCR_TEST_GET1		0x00
+#define QPCR_TEST_FOR2		0x40
+#define QPCR_TEST_GET2		0x40
+#define QPCR_TEST_FOR3		0x80
+#define QPCR_TEST_GET3		0x40
+#define QPCR_TEST_FOR4		0xC0
+#define QPCR_TEST_GET4		0x80
+
+#define QOPR_CLOCK_X1		0x0000
+#define QOPR_CLOCK_X2		0x0001
+#define QOPR_CLOCK_X4		0x0002
+#define QOPR_CLOCK_X8		0x0003
+#define QOPR_CLOCK_RATE_MASK	0x0003
+
+
+static struct quatech_feature quatech_cards[] = {
+	{ PCI_DEVICE_ID_QUATECH_QSC100,   1 },
+	{ PCI_DEVICE_ID_QUATECH_DSC100,   1 },
+	{ PCI_DEVICE_ID_QUATECH_DSC100E,  0 },
+	{ PCI_DEVICE_ID_QUATECH_DSC200,   1 },
+	{ PCI_DEVICE_ID_QUATECH_DSC200E,  0 },
+	{ PCI_DEVICE_ID_QUATECH_ESC100D,  1 },
+	{ PCI_DEVICE_ID_QUATECH_ESC100M,  1 },
+	{ PCI_DEVICE_ID_QUATECH_QSCP100,  1 },
+	{ PCI_DEVICE_ID_QUATECH_DSCP100,  1 },
+	{ PCI_DEVICE_ID_QUATECH_QSCP200,  1 },
+	{ PCI_DEVICE_ID_QUATECH_DSCP200,  1 },
+	{ PCI_DEVICE_ID_QUATECH_ESCLP100, 0 },
+	{ PCI_DEVICE_ID_QUATECH_QSCLP100, 0 },
+	{ PCI_DEVICE_ID_QUATECH_DSCLP100, 0 },
+	{ PCI_DEVICE_ID_QUATECH_SSCLP100, 0 },
+	{ PCI_DEVICE_ID_QUATECH_QSCLP200, 0 },
+	{ PCI_DEVICE_ID_QUATECH_DSCLP200, 0 },
+	{ PCI_DEVICE_ID_QUATECH_SSCLP200, 0 },
+	{ PCI_DEVICE_ID_QUATECH_SPPXP_100, 0 },
+	{ 0, }
+};
+
+static int pci_quatech_amcc(u16 devid)
+{
+	struct quatech_feature *qf = &quatech_cards[0];
+	while (qf->devid) {
+		if (qf->devid == devid)
+			return qf->amcc;
+		qf++;
+	}
+	pr_err("quatech: unknown port type '0x%04X'.\n", devid);
+	return 0;
+};
+
+static int pci_quatech_rqopr(struct uart_8250_port *port)
+{
+	unsigned long base = port->port.iobase;
+	u8 LCR, val;
+
+	LCR = inb(base + UART_LCR);
+	outb(0xBF, base + UART_LCR);
+	val = inb(base + UART_SCR);
+	outb(LCR, base + UART_LCR);
+	return val;
+}
+
+static void pci_quatech_wqopr(struct uart_8250_port *port, u8 qopr)
+{
+	unsigned long base = port->port.iobase;
+	u8 LCR, val;
+
+	LCR = inb(base + UART_LCR);
+	outb(0xBF, base + UART_LCR);
+	val = inb(base + UART_SCR);
+	outb(qopr, base + UART_SCR);
+	outb(LCR, base + UART_LCR);
+}
+
+static int pci_quatech_rqmcr(struct uart_8250_port *port)
+{
+	unsigned long base = port->port.iobase;
+	u8 LCR, val, qmcr;
+
+	LCR = inb(base + UART_LCR);
+	outb(0xBF, base + UART_LCR);
+	val = inb(base + UART_SCR);
+	outb(val | 0x10, base + UART_SCR);
+	qmcr = inb(base + UART_MCR);
+	outb(val, base + UART_SCR);
+	outb(LCR, base + UART_LCR);
+
+	return qmcr;
+}
+
+static void pci_quatech_wqmcr(struct uart_8250_port *port, u8 qmcr)
+{
+	unsigned long base = port->port.iobase;
+	u8 LCR, val;
+
+	LCR = inb(base + UART_LCR);
+	outb(0xBF, base + UART_LCR);
+	val = inb(base + UART_SCR);
+	outb(val | 0x10, base + UART_SCR);
+	outb(qmcr, base + UART_MCR);
+	outb(val, base + UART_SCR);
+	outb(LCR, base + UART_LCR);
+}
+
+static int pci_quatech_has_qmcr(struct uart_8250_port *port)
+{
+	unsigned long base = port->port.iobase;
+	u8 LCR, val;
+
+	LCR = inb(base + UART_LCR);
+	outb(0xBF, base + UART_LCR);
+	val = inb(base + UART_SCR);
+	if (val & 0x20) {
+		outb(0x80, UART_LCR);
+		if (!(inb(UART_SCR) & 0x20)) {
+			outb(LCR, base + UART_LCR);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int pci_quatech_test(struct uart_8250_port *port)
+{
+	u8 reg;
+	u8 qopr = pci_quatech_rqopr(port);
+	pci_quatech_wqopr(port, qopr & QPCR_TEST_FOR1);
+	reg = pci_quatech_rqopr(port) & 0xC0;
+	if (reg != QPCR_TEST_GET1)
+		return -EINVAL;
+	pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR2);
+	reg = pci_quatech_rqopr(port) & 0xC0;
+	if (reg != QPCR_TEST_GET2)
+		return -EINVAL;
+	pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR3);
+	reg = pci_quatech_rqopr(port) & 0xC0;
+	if (reg != QPCR_TEST_GET3)
+		return -EINVAL;
+	pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR4);
+	reg = pci_quatech_rqopr(port) & 0xC0;
+	if (reg != QPCR_TEST_GET4)
+		return -EINVAL;
+
+	pci_quatech_wqopr(port, qopr);
+	return 0;	
+}
+
+static int pci_quatech_clock(struct uart_8250_port *port)
+{
+	u8 qopr, reg, set;
+	unsigned long clock;
+
+	if (pci_quatech_test(port) < 0)
+		return 1843200;
+
+	qopr = pci_quatech_rqopr(port);
+
+	pci_quatech_wqopr(port, qopr & ~QOPR_CLOCK_X8);
+	reg = pci_quatech_rqopr(port);
+	if (reg & QOPR_CLOCK_X8) {
+		clock = 1843200;
+		goto out;
+	}
+	pci_quatech_wqopr(port, qopr | QOPR_CLOCK_X8);
+	reg = pci_quatech_rqopr(port);
+	if (!(reg & QOPR_CLOCK_X8)) {
+		clock = 1843200;
+		goto out;
+	}
+	reg &= QOPR_CLOCK_X8;
+	if (reg == QOPR_CLOCK_X2) {
+		clock =  3685400;
+		set = QOPR_CLOCK_X2;
+	} else if (reg == QOPR_CLOCK_X4) {
+		clock = 7372800;
+		set = QOPR_CLOCK_X4;
+	} else if (reg == QOPR_CLOCK_X8) {
+		clock = 14745600;
+		set = QOPR_CLOCK_X8;
+	} else {
+		clock = 1843200;
+		set = QOPR_CLOCK_X1;
+	}
+	qopr &= ~QOPR_CLOCK_RATE_MASK;
+	qopr |= set;
+
+out:
+	pci_quatech_wqopr(port, qopr);
+	return clock;
+}
+
+static int pci_quatech_rs422(struct uart_8250_port *port)
+{
+	u8 qmcr;
+	int rs422 = 0;
+
+	if (!pci_quatech_has_qmcr(port))
+		return 0;
+	qmcr = pci_quatech_rqmcr(port);
+	pci_quatech_wqmcr(port, 0xFF);
+	if (pci_quatech_rqmcr(port))
+		rs422 = 1;
+	pci_quatech_wqmcr(port, qmcr);
+	return rs422;
+}
+
+static int pci_quatech_init(struct pci_dev *dev)
+{
+	if (pci_quatech_amcc(dev->device)) {
+		unsigned long base = pci_resource_start(dev, 0);
+		if (base) {
+			u32 tmp;
+			outl(inl(base + 0x38), base + 0x38);
+			tmp = inl(base + 0x3c);
+			outl(tmp | 0x01000000, base + 0x3c);
+			outl(tmp, base + 0x3c);
+		}
+	}
+	return 0;
+}
+
+static int pci_quatech_setup(struct serial_private *priv,
+		  const struct pciserial_board *board,
+		  struct uart_8250_port *port, int idx)
+{
+	/* Needed by pci_quatech calls below */
+	port->port.iobase = pci_resource_start(priv->dev, FL_GET_BASE(board->flags));
+	/* Set up the clocking */
+	port->port.uartclk = pci_quatech_clock(port);
+	/* For now just warn about RS422 */
+	if (pci_quatech_rs422(port)) 
+		pr_warn( "quatech: software control of RS422 features not currently supported.\n");
+	return pci_default_setup(priv, board, port, idx);
+}
+
+static void __devexit pci_quatech_exit(struct pci_dev *dev)
+{
+}
+
 static int pci_default_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
 		  struct uart_8250_port *port, int idx)
@@ -1503,6 +1750,16 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
 		.setup		= pci_default_setup,
 		.exit		= __devexit_p(pci_plx9050_exit),
 	},
+	/* Quatech */
+	{
+		.vendor		= PCI_VENDOR_ID_QUATECH,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_quatech_init,
+		.setup		= pci_quatech_setup,
+		.exit		= __devexit_p(pci_quatech_exit),
+	},
 	/*
 	 * SBS Technologies, Inc., PMC-OCTALPRO 232
 	 */
@@ -3257,18 +3514,70 @@ static struct pci_device_id serial_pci_tbl[] = {
 	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
 		0x10b5, 0x106a, 0, 0,
 		pbn_plx_romulus },
+	/*
+	 * Quatech cards. These actually have configurable clocks but for
+	 * now we just use the default.
+	 *
+	 * 100 series are RS232, 200 series RS422, 
+	 */
 	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b1_4_115200 },
 	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b1_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_4_115200 },
 	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b1_8_115200 },
 	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_b1_8_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_4_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_4_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_4_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_1_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_4_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_1_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESCLP100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_8_115200 },
+
 	{	PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
 		PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
 		0, 0,
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 9d36b82..ce45006 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1867,8 +1867,23 @@
 #define PCI_VENDOR_ID_QUATECH		0x135C
 #define PCI_DEVICE_ID_QUATECH_QSC100	0x0010
 #define PCI_DEVICE_ID_QUATECH_DSC100	0x0020
+#define PCI_DEVICE_ID_QUATECH_DSC200	0x0030
+#define PCI_DEVICE_ID_QUATECH_QSC200	0x0040
 #define PCI_DEVICE_ID_QUATECH_ESC100D	0x0050
 #define PCI_DEVICE_ID_QUATECH_ESC100M	0x0060
+#define PCI_DEVICE_ID_QUATECH_QSCP100	0x0120
+#define PCI_DEVICE_ID_QUATECH_DSCP100	0x0130
+#define PCI_DEVICE_ID_QUATECH_QSCP200	0x0140
+#define PCI_DEVICE_ID_QUATECH_DSCP200	0x0150
+#define PCI_DEVICE_ID_QUATECH_QSCLP100	0x0170
+#define PCI_DEVICE_ID_QUATECH_DSCLP100	0x0180
+#define PCI_DEVICE_ID_QUATECH_DSC100E	0x0181
+#define PCI_DEVICE_ID_QUATECH_SSCLP100	0x0190
+#define PCI_DEVICE_ID_QUATECH_QSCLP200	0x01A0
+#define PCI_DEVICE_ID_QUATECH_DSCLP200	0x01B0
+#define PCI_DEVICE_ID_QUATECH_DSC200E	0x01B1
+#define PCI_DEVICE_ID_QUATECH_SSCLP200	0x01C0
+#define PCI_DEVICE_ID_QUATECH_ESCLP100	0x01E0
 #define PCI_DEVICE_ID_QUATECH_SPPXP_100 0x0278
 
 #define PCI_VENDOR_ID_SEALEVEL		0x135e

^ permalink raw reply related

* Re: [PATCH 5/5] serial: 8250_dw: Set FIFO size dynamically
From: Heikki Krogerus @ 2012-11-28 13:37 UTC (permalink / raw)
  To: Alan Cox; +Cc: Greg Kroah-Hartman, Alan Cox, Jamie Iles, linux-serial, LKML
In-Reply-To: <20121128131826.38391319@pyramind.ukuu.org.uk>

On Wed, Nov 28, 2012 at 01:18:26PM +0000, Alan Cox wrote:
> > +	if (!reg || (reg & 0xff) != '*')
> > +		return;
> > +
> 
> That looks bogus. If reg == 0 then reg & 0xFF != '*'
> 
> So why the double test ?

No reason. It is something I forgot to cleanup. I'll resend this.

Thanks!

-- 
heikki

^ permalink raw reply

* Re: Comments requested: driver for Quatech PCI serial cards
From: Alan Cox @ 2012-11-28 13:24 UTC (permalink / raw)
  To: Jonathan Woithe; +Cc: linux-serial
In-Reply-To: <20121128022917.GD28558@marvin.atrad.com.au>

> So far as getting something working for our case, the patch below is all
> that is required.  I do know the QMCR and QOPR bit map details for at least
> the DSC-200/300 card as it's more or less described in the manual.  I will
> try to prepare a second patch which at least creates the necessary defines
> for these in the next few days.

Thanks.

> With regard to the ioctl, should this be an implementation of the ioctl
> provided by the old out-of-tree driver, or is there an established
> standard/example which I should write against instead?

We have a standard (if possibly needing a few bits adding) ioctl for
basic RS422/485 features. That's TIOCG/SRS485 (which for all the naming
is also kind of 422 and other related bits as well)

Can we get a Signed-off-by: for the first patch so we can try and get it
into 3.8 ?

Alan

^ permalink raw reply

* Re: [PATCH 5/5] serial: 8250_dw: Set FIFO size dynamically
From: Alan Cox @ 2012-11-28 13:18 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: Greg Kroah-Hartman, Alan Cox, Jamie Iles, linux-serial, LKML
In-Reply-To: <1354106924-11013-6-git-send-email-heikki.krogerus@linux.intel.com>

> +	if (!reg || (reg & 0xff) != '*')
> +		return;
> +

That looks bogus. If reg == 0 then reg & 0xFF != '*'

So why the double test ?


^ permalink raw reply

* [PATCH 5/5] serial: 8250_dw: Set FIFO size dynamically
From: Heikki Krogerus @ 2012-11-28 12:48 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Alan Cox, Jamie Iles, linux-serial, LKML
In-Reply-To: <1354106924-11013-1-git-send-email-heikki.krogerus@linux.intel.com>

Designware UART provides optional Component Parameter
Register that lists most of the capabilities of the UART,
including FIFO size. This uses that register to set FIFO
size for the port before registering it.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/tty/serial/8250/8250_dw.c |   57 ++++++++++++++++++++++++++++++++++---
 1 file changed, 53 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index bfbfb07..4c5e60a 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -25,6 +25,28 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
+/* Offsets for the DesignWare specific registers */
+#define DW_UART_USR	0x1f /* UART Status Register */
+#define DW_UART_CPR	0xf4 /* Component Parameter Register */
+#define DW_UART_UCV	0xf8 /* UART Component Version */
+
+/* Component Parameter Register bits */
+#define DW_UART_CPR_ABP_DATA_WIDTH	(3 << 0)
+#define DW_UART_CPR_AFCE_MODE		(1 << 4)
+#define DW_UART_CPR_THRE_MODE		(1 << 5)
+#define DW_UART_CPR_SIR_MODE		(1 << 6)
+#define DW_UART_CPR_SIR_LP_MODE		(1 << 7)
+#define DW_UART_CPR_ADDITIONAL_FEATURES	(1 << 8)
+#define DW_UART_CPR_FIFO_ACCESS		(1 << 9)
+#define DW_UART_CPR_FIFO_STAT		(1 << 10)
+#define DW_UART_CPR_SHADOW		(1 << 11)
+#define DW_UART_CPR_ENCODED_PARMS	(1 << 12)
+#define DW_UART_CPR_DMA_EXTRA		(1 << 13)
+#define DW_UART_CPR_FIFO_MODE		(0xff << 16)
+/* Helper for fifo size calculation */
+#define DW_UART_CPR_FIFO_SIZE(a)	(((a >> 16) & 0xff) * 16)
+
+
 struct dw8250_data {
 	int	last_lcr;
 	int	line;
@@ -66,9 +88,6 @@ static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
 	return readl(p->membase + offset);
 }
 
-/* Offset for the DesignWare's UART Status Register. */
-#define UART_USR	0x1f
-
 static int dw8250_handle_irq(struct uart_port *p)
 {
 	struct dw8250_data *d = p->private_data;
@@ -78,7 +97,7 @@ static int dw8250_handle_irq(struct uart_port *p)
 		return 1;
 	} else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
 		/* Clear the USR and write the LCR again. */
-		(void)p->serial_in(p, UART_USR);
+		(void)p->serial_in(p, DW_UART_USR);
 		p->serial_out(p, d->last_lcr, UART_LCR);
 
 		return 1;
@@ -119,6 +138,34 @@ static int __devinit dw8250_probe_of(struct uart_port *p)
 	return 0;
 }
 
+static void __devinit dw8250_setup_port(struct uart_8250_port *up)
+{
+	struct uart_port	*p = &up->port;
+	u32			reg = readl(p->membase + DW_UART_UCV);
+
+	/*
+	 * If the Component Version Register returns zero, we know that
+	 * ADDITIONAL_FEATURES are not enabled. No need to go any further.
+	 */
+	if (!reg || (reg & 0xff) != '*')
+		return;
+
+	dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n",
+		(reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
+
+	reg = readl(p->membase + DW_UART_CPR);
+	if (!reg)
+		return;
+
+	/* Select the type based on fifo */
+	if (reg & DW_UART_CPR_FIFO_MODE) {
+		p->type = PORT_16550A;
+		p->flags |= UPF_FIXED_TYPE;
+		p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
+		up->tx_loadsz = p->fifosize;
+	}
+}
+
 static int __devinit dw8250_probe(struct platform_device *pdev)
 {
 	struct uart_8250_port uart = {};
@@ -156,6 +203,8 @@ static int __devinit dw8250_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	dw8250_setup_port(&uart);
+
 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
-- 
1.7.10.4

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox