* RS485 implementation questions (primarly in atmel_serial.c)
@ 2013-01-23 22:56 Guido Classen
2013-01-24 0:10 ` Grant Edwards
` (2 more replies)
0 siblings, 3 replies; 17+ messages in thread
From: Guido Classen @ 2013-01-23 22:56 UTC (permalink / raw)
To: linux-serial; +Cc: Nicolas Ferre
Hi everyone on linux serial list,
I'm currently working on upgrading ARM-Linux board of our company
to a recent Linux kernel and am glad to see, that RS485 support has
reached the mainline. Firstly many thanks to Claudio Scordino and
the other contributers, I really appreciate their work. I've contacted
Claudio, to discuss some issues about RS485. But he is currently to bus, so
I hope to find others here to discuss my questions and concerns.
I personally work for over 10 years with RS485 stuff (even on
microprocessor systems from the pre-Linux era) and also implemented
several years ago RS485 for our company's own AT91 Linux based boards.
My old implementation was based on the 1ms tick timer and supported
both, the 16550 chips and at Atmel AT91 USARTS.
I've recently looked at the current atmel_serial.c sources and have
trouble to understand why RS485 is implemented in this way.
First question is about what is the exact meaning of the
delay_rts_before/after_send fields in the RS485 ioctl? Unfortunately
there is no explanation (except from the sample code) in
Documentation/serial serial-rs485.txt. It's not even clear in which
Units this parameters are given. The crisv10.c driver gets the
delay_before_send in milliseconds and atmel_serial
delay_rts_after_send in bit-times (means depending on baud rate).
In my opinion (and according the practice in our company) the purpose
of delay_rts_before/after_send is to make the time in which RTS is
asserted a bit longer (at choice before and / or after) as the actual
transmission of the characters take. The reason is that longs lines
need some time for the transition from tri-state (open circuit voltage
supplied by pull-up / pull-down resistors) to the active transmission
level (logical "1"). I think this is called transient oscillation (I'm
not sure if this is the right English term). Furthermore active
devices in the RS485 line (repeater, party-line modems) often need to
know if you want to send a bit earlier than the first character
arrives to have some time to do internal switching from receiving to
transmission.
I this cases you need a way to assert RTS some milliseconds before the
first character of a frame is transmitted and leave it on some
milliseconds after the end of the frame. In my opinion that is what
delay_rts_before/after_send should be for. (In other equipment this
feature is also called turn on / turn off delay)
I've a short look to the sources of the crisv10.c driver (I don't
actually have such hardware). Assuming that a frame is written using
a single write() operation (and I have understood the code right...)
it will work as I expect. (Doing a single mssleep() per frame
afterwards RTS is asserted). delay_after_send seems not to be
implement at all (perhaps its not easly possible to detect when the
transmitter has shifted out the last bit of the frame).
But in the atmel_serial.c driver rthe Transmit Timeguard function of
the AT91 USART is used to implement this. The purpose of the Transmit
Timeguard function is to throttle transmission in case the receiving
device is able to receive/process incoming characters at line
speed. This means the USART will insert a gap between EACH transmitted
byte and not only the last of a frame. Atmel surely had its reasons
to implement such a function, but luckily nowadays such devices are
rare. But this function is not especially related to RS485. It is
also applicable in RS232 mode. So in my view it is not the
functionality which is meant by the delay_rts_before/after_send
fields. Traditional field bus protocols like Modbus or Profibus use
an idle gap between the transmitter characters (typically lager 3 char
length) as a new frame indicator. Also it is not possible to implement
such a (slave) device in a linux userland application, there are a lot
of devices which implement this scheme in hardware out there. But I
would appreciate if there is some other way to activate it from
usermode applications.
I think a more general way is an implementation using hrtimers, which
also can implement delay_rts_before_send.
An other point I don't understand is why ATMEL_US_TXEMPTY interrupt is
used when in RS485 mode insted of the normal ATMEL_US_TXRDY or
ATEM_US_ENDTX interrupts. In the RS485 mode the AT91 USART will
connect the RTS pin to the internal TXEMPTY signal to drive the RTS
until the last bit is shifted out. But this should not have any affect
on ow the drive will pump out the bytes to the USART's transmit hold register?
I'm happy if anyone can give me some explanation on this topics.
Regards from Germany
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: RS485 implementation questions (primarly in atmel_serial.c)
2013-01-23 22:56 RS485 implementation questions (primarly in atmel_serial.c) Guido Classen
@ 2013-01-24 0:10 ` Grant Edwards
2013-01-25 9:13 ` Guido Classen
2013-01-30 14:35 ` Jean-Pierre Tosoni
2 siblings, 0 replies; 17+ messages in thread
From: Grant Edwards @ 2013-01-24 0:10 UTC (permalink / raw)
To: linux-serial
On 2013-01-23, Guido Classen <clagix@gmail.com> wrote:
> I this cases you need a way to assert RTS some milliseconds before the
> first character of a frame is transmitted and leave it on some
> milliseconds after the end of the frame. In my opinion that is what
> delay_rts_before/after_send should be for. (In other equipment this
> feature is also called turn on / turn off delay)
In applications like links using half-duplex modems, you may have to
assert RTS for many hundreds of millisconds to allow the modulator to
key up and stabilze and the demodulator to lock onto the signal before
you can send data. Some modems will let you know when they're ready
by asserting CTS, and for others you just have to have a fixed delay.
In those cases, you also typically hold RTS for some time after the
end of the data for a time ranging up to several byte times.
In our non-Linux based products, we implement pre/pose data RTS "hold"
times as you describe in our device driver. Our Linux-based products
can't handle those applications.
--
Grant Edwards grant.b.edwards Yow! Are we laid back yet?
at
gmail.com
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: RS485 implementation questions (primarly in atmel_serial.c)
2013-01-23 22:56 RS485 implementation questions (primarly in atmel_serial.c) Guido Classen
2013-01-24 0:10 ` Grant Edwards
@ 2013-01-25 9:13 ` Guido Classen
2013-01-25 15:39 ` Grant Edwards
2013-01-30 14:35 ` Jean-Pierre Tosoni
2 siblings, 1 reply; 17+ messages in thread
From: Guido Classen @ 2013-01-25 9:13 UTC (permalink / raw)
To: linux-serial; +Cc: Grant Edwards
On 2014-01-23, Grant Edwards wrote:
> In applications like links using half-duplex modems, you may have to
> assert RTS for many hundreds of millisconds to allow the modulator to
> key up and stabilze and the demodulator to lock onto the signal before
> you can send data. Some modems will let you know when they're ready
> by asserting CTS, and for others you just have to have a fixed delay.
> In those cases, you also typically hold RTS for some time after the
> end of the data for a time ranging up to several byte times.
> In our non-Linux based products, we implement pre/pose data RTS "hold"
> times as you describe in our device driver. Our Linux-based products
> can't handle those applications.
I am glad to hear that there is at lease someone else out there who is using
such old technology. We still use a kind of V.23 modems which work as
you described in some projects in road traffic applications.
Why haven't you tried to implement that applications on Linux? On slow
baud-rates even an user-mode implementation should be possible in most
cases?
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: RS485 implementation questions (primarly in atmel_serial.c)
2013-01-25 9:13 ` Guido Classen
@ 2013-01-25 15:39 ` Grant Edwards
2013-01-29 21:56 ` Guido Classen
0 siblings, 1 reply; 17+ messages in thread
From: Grant Edwards @ 2013-01-25 15:39 UTC (permalink / raw)
To: linux-serial
On 2013-01-25, Guido Classen <clagix@gmail.com> wrote:
> On 2014-01-23, Grant Edwards wrote:
>
>> In applications like links using half-duplex modems, you may have to
>> assert RTS for many hundreds of millisconds to allow the modulator to
>> key up and stabilze and the demodulator to lock onto the signal before
>> you can send data. Some modems will let you know when they're ready
>> by asserting CTS, and for others you just have to have a fixed delay.
>> In those cases, you also typically hold RTS for some time after the
>> end of the data for a time ranging up to several byte times.
>
>> In our non-Linux based products, we implement pre/pose data RTS "hold"
>> times as you describe in our device driver. Our Linux-based products
>> can't handle those applications.
> I am glad to hear that there is at lease someone else out there who
> is using such old technology. We still use a kind of V.23 modems
> which work as you described in some projects in road traffic
> applications. Why haven't you tried to implement that applications on
> Linux?
Our Linux tty/serial drivers do support "plain" a RS-485 mode without
pre/post RTS hold times (the plain RS-485 mode is supported by the
UART itself). The pre/post RTS hold times feature can be used from
Linux applications, but to take advantage of those sort of features we
don't use Linux tty/serial device drivers. For many industrial I/O
applications we've found it much simpler to avoid the termios/tty
stuff and connect to the serial hardware via Ethernet and TCP/IP
instead.
Over the years we've found that the Unix "tty" API is rather
ill-suited for doing things other than talking to terminals. In other
news, we've found that a screwdriver is ill-suited for doing things
other than driving screws. :)
For example, our serial interfaces are used quite a bit in traffic and
parking applications, but in those cases the long-haul connections are
TCP/IP over fiber, and the serial ports are only used to communicate
locally within a roadside cabinet. To the user application, each of
the serial devices (camera controller, inductive loop sensor, ramp
light controller, card reader, gate arm, etc.) is just another network
device addressed via an <ipaddr,ipport> tuple.
Programmers seem to get themselves into much less trouble with the TCP
socket API than they do with the tty API
> On slow baud-rates even an user-mode implementation should be
> possible in most cases?
Yes, it should be possible, but customers seem quite happy using the
TCP socket API rather than a tty API, so we've never attempted it.
--
Grant Edwards grant.b.edwards Yow! Maybe we could paint
at GOLDIE HAWN a rich PRUSSIAN
gmail.com BLUE --
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: RS485 implementation questions (primarly in atmel_serial.c)
2013-01-25 15:39 ` Grant Edwards
@ 2013-01-29 21:56 ` Guido Classen
2013-01-29 22:29 ` Grant Edwards
0 siblings, 1 reply; 17+ messages in thread
From: Guido Classen @ 2013-01-29 21:56 UTC (permalink / raw)
To: linux-serial
> Our Linux tty/serial drivers do support "plain" a RS-485 mode without
> pre/post RTS hold times (the plain RS-485 mode is supported by the
> UART itself). The pre/post RTS hold times feature can be used from
> Linux applications, but to take advantage of those sort of features we
> don't use Linux tty/serial device drivers. For many industrial I/O
> applications we've found it much simpler to avoid the termios/tty
> stuff and connect to the serial hardware via Ethernet and TCP/IP
> instead.
>
> Over the years we've found that the Unix "tty" API is rather
> ill-suited for doing things other than talking to terminals. In other
> news, we've found that a screwdriver is ill-suited for doing things
> other than driving screws. :)
You are absolutely right, the "TTY" API is ill-suited for fieldbus style
half-duplex communication. But in my opinion this form of communication is
still very common and even today not every device has an ethernet connector.
So what are the consequences?
1. Don't use Linux at all for this purpose. For PCs and Server it may
be indeed the better solution to use TCP/IP instead. But for embedded
Linux the situation is different. One important application here is to
implement exactly these Ethernet/TCP/IP to "some lowlevel stuff"
boxes!
2. Sole Userspace software using Posix TTY API. This will work (more
or less) if the speed (baudrate) is relatively low and the time
between sending and receiving is long enough. You also can not benefit
from serial hardware which have special support for fieldbus style
communication like the Atmel AT91 USARTS.
3. Use some board specific drivers or modifications to the drivers and
Linux TTY stack (E.G. additional ioctls). I think this way is mostly
used in practically embedded Linux. Drawbacks are, that userspace
software must include support for each specific board it is intend to
run on.
4. The TIOCSRS485 ioctl may open new doors, but as I see there are
only few drivers implementing it.
Maybe someone else on this list will share his thoughts about this issues
>
> For example, our serial interfaces are used quite a bit in traffic and
> parking applications, but in those cases the long-haul connections are
> TCP/IP over fiber, and the serial ports are only used to communicate
> locally within a roadside cabinet. To the user application, each of
> the serial devices (camera controller, inductive loop sensor, ramp
> light controller, card reader, gate arm, etc.) is just another network
> device addressed via an <ipaddr,ipport> tuple.
>
> Programmers seem to get themselves into much less trouble with the TCP
> socket API than they do with the tty API
That's crazy, It seems we are working almost with the same things. I am
a software developer and so I also prefer TCP. It's much simpler to
use and has a clean, portable API. In new installations, TCP is quite
common. We also have fiber optics and copper rings for long-haul
connections. But there also very old copper wires which are to long to
be used with SHDSL. Sensors and actors are mostly connected serial. At least
in Germany for road traffic equipments protocols are often specified by
national standards. There also old installations which should be extended or
modified. So at the end we don't get rid of serial and we must be able to
support it in parallel to the TCP/IP infrastructure!
Regards,
Guido Classen
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: RS485 implementation questions (primarly in atmel_serial.c)
2013-01-29 21:56 ` Guido Classen
@ 2013-01-29 22:29 ` Grant Edwards
2013-01-30 15:24 ` Guido Classen
0 siblings, 1 reply; 17+ messages in thread
From: Grant Edwards @ 2013-01-29 22:29 UTC (permalink / raw)
To: linux-serial
On 2013-01-29, Guido Classen <clagix@gmail.com> wrote:
>> Our Linux tty/serial drivers do support "plain" a RS-485 mode without
>> pre/post RTS hold times (the plain RS-485 mode is supported by the
>> UART itself). The pre/post RTS hold times feature can be used from
>> Linux applications, but to take advantage of those sort of features we
>> don't use Linux tty/serial device drivers. For many industrial I/O
>> applications we've found it much simpler to avoid the termios/tty
>> stuff and connect to the serial hardware via Ethernet and TCP/IP
>> instead.
>>
>> Over the years we've found that the Unix "tty" API is rather
>> ill-suited for doing things other than talking to terminals. In other
>> news, we've found that a screwdriver is ill-suited for doing things
>> other than driving screws. :)
>
> You are absolutely right, the "TTY" API is ill-suited for fieldbus style
> half-duplex communication. But in my opinion this form of communication is
> still very common and even today not every device has an ethernet connector.
> So what are the consequences?
>
> 1. Don't use Linux at all for this purpose. For PCs and Server it may
> be indeed the better solution to use TCP/IP instead. But for embedded
> Linux the situation is different. One important application here is
> to implement exactly these Ethernet/TCP/IP to "some lowlevel stuff"
> boxes!
>
> 2. Sole Userspace software using Posix TTY API. This will work (more
> or less) if the speed (baudrate) is relatively low and the time
> between sending and receiving is long enough. You also can not benefit
> from serial hardware which have special support for fieldbus style
> communication like the Atmel AT91 USARTS.
>
> 3. Use some board specific drivers or modifications to the drivers and
> Linux TTY stack (E.G. additional ioctls). I think this way is mostly
> used in practically embedded Linux. Drawbacks are, that userspace
> software must include support for each specific board it is intend to
> run on.
What I'm thinking about doing is instead of using a tty driver,
writing a char driver. That eliminates the whole tty/ldisc tangle and
allows you to implement read()/write() as packet operations rather
than bytestream operations. You can still implement whatever subset
of the termios ioctl() calls make sense along with whatever new
ioctl() calls are needed to control/configure things like inter-byte
timeouts, 9th-bit addressing modes, frame-recognition state-machines,
etc.
> 4. The TIOCSRS485 ioctl may open new doors, but as I see there are
> only few drivers implementing it.
Too bad about the name.
It doesn't actually select RS485 mode (I work with board that _do_
have software-selectable electrical interfaces and can be set to
RS2323, RS485, RS422 modes). What's called "RS485" moide controls
enabling the use of RTS for half-duplex operation. RS485 is _one_
electrical interface that uses RTS like that, but there are lots of
others (RS232 and half-duplex modems is one). And not all use-cases
for RS485 use RTS for half-duplex communications either.
>> For example, our serial interfaces are used quite a bit in traffic and
>> parking applications, but in those cases the long-haul connections are
>> TCP/IP over fiber, and the serial ports are only used to communicate
>> locally within a roadside cabinet. To the user application, each of
>> the serial devices (camera controller, inductive loop sensor, ramp
>> light controller, card reader, gate arm, etc.) is just another network
>> device addressed via an <ipaddr,ipport> tuple.
>>
>> Programmers seem to get themselves into much less trouble with the TCP
>> socket API than they do with the tty API
>
> That's crazy, It seems we are working almost with the same things.
Well, some of my customers do that sort of stuff. :)
--
Grant Edwards grant.b.edwards Yow! What I want to find
at out is -- do parrots know
gmail.com much about Astro-Turf?
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: RS485 implementation questions (primarly in atmel_serial.c)
[not found] <CAJa4H3cYT6QOaacWEy2yZjb0hM3m4p+0L2_fXBYv8tWajYkmGw@mail.gmail.com>
@ 2013-01-30 10:30 ` Claudio Scordino
[not found] ` <510A3740.6060809@evidence.eu.com>
0 siblings, 1 reply; 17+ messages in thread
From: Claudio Scordino @ 2013-01-30 10:30 UTC (permalink / raw)
To: Guido Classen; +Cc: linux-serial, Nicolas Ferre
Il 23/01/2013 23:51, Guido Classen ha scritto:
> Hi everyone on linux serial list,
>
> I'm currently working on upgrading ARM-Linux board of our company
> to a recent Linux kernel and am glad to see, that RS485 support has
> reached the mainline. Firstly many thanks to Claudio Scordino and
> the other contributers, I really appreciate their work. I've contacted
> Claudio, to discuss some issues about RS485. But he is currently to bus, so
> I hope to find others here to discuss my questions and concerns.
>
> I personally work for over 10 years with RS485 stuff (even on
> microprocessor systems from the pre-Linux era) and also implemented
> several years ago RS485 for our company's own AT91 Linux based boards.
> My old implementation was based on the 1ms tick timer and supported
> both, the 16550 chips and at Atmel AT91 USARTS.
>
> I've recently looked at the current atmel_serial.c sources and have
> trouble to understand why RS485 is implemented in this way.
>
> First question is about what is the exact meaning of the
> delay_rts_before/after_send fields in the RS485 ioctl? Unfortunately
> there is no explanation (except from the sample code) in
> Documentation/serial serial-rs485.txt. It's not even clear in which
> Units this parameters are given. The crisv10.c driver gets the
> delay_before_send in milliseconds and atmel_serial
> delay_rts_after_send in bit-times (means depending on baud rate).
>
> In my opinion (and according the practice in our company) the purpose
> of delay_rts_before/after_send is to make the time in which RTS is
> asserted a bit longer (at choice before and / or after) as the actual
> transmission of the characters take. The reason is that longs lines
> need some time for the transition from tri-state (open circuit voltage
> supplied by pull-up / pull-down resistors) to the active transmission
> level (logical "1"). I think this is called transient oscillation (I'm
> not sure if this is the right English term). Furthermore active
> devices in the RS485 line (repeater, party-line modems) often need to
> know if you want to send a bit earlier than the first character
> arrives to have some time to do internal switching from receiving to
> transmission.
>
> I this cases you need a way to assert RTS some milliseconds before the
> first character of a frame is transmitted and leave it on some
> milliseconds after the end of the frame. In my opinion that is what
> delay_rts_before/after_send should be for. (In other equipment this
> feature is also called turn on / turn off delay)
>
> I've a short look to the sources of the crisv10.c driver (I don't
> actually have such hardware). Assuming that a frame is written using
> a single write() operation (and I have understood the code right...)
> it will work as I expect. (Doing a single mssleep() per frame
> afterwards RTS is asserted). delay_after_send seems not to be
> implement at all (perhaps its not easly possible to detect when the
> transmitter has shifted out the last bit of the frame).
>
> But in the atmel_serial.c driver rthe Transmit Timeguard function of
> the AT91 USART is used to implement this. The purpose of the Transmit
> Timeguard function is to throttle transmission in case the receiving
> device is able to receive/process incoming characters at line
> speed. This means the USART will insert a gap between EACH transmitted
> byte and not only the last of a frame. Atmel surely had its reasons
> to implement such a function, but luckily nowadays such devices are
> rare. But this function is not especially related to RS485. It is
> also applicable in RS232 mode. So in my view it is not the
> functionality which is meant by the delay_rts_before/after_send
> fields. Traditional field bus protocols like Modbus or Profibus use
> an idle gap between the transmitter characters (typically lager 3 char
> length) as a new frame indicator. Also it is not possible to implement
> such a (slave) device in a linux userland application, there are a lot
> of devices which implement this scheme in hardware out there. But I
> would appreciate if there is some other way to activate it from
> usermode applications.
>
> I think a more general way is an implementation using hrtimers, which
> also can implement delay_rts_before_send.
>
> An other point I don't understand is why ATMEL_US_TXEMPTY interrupt is
> used when in RS485 mode insted of the normal ATMEL_US_TXRDY or
> ATEM_US_ENDTX interrupts. In the RS485 mode the AT91 USART will
> connect the RTS pin to the internal TXEMPTY signal to drive the RTS
> until the last bit is shifted out. But this should not have any affect
> on ow the drive will pump out the bytes to the USART's transmit hold
> register?
>
> I'm happy if anyone can give me some explanation on this topics.
>
> Regards from Germany
>
> Guido Classen
>
>
Hi Guido.
Sorry for my late response.
I looked at the code, and I understand and agree with your concerns.
The RS485 support for atmel_serial went through several refinements
before being merged, so it is now hard to understand who implemented a
specific line and figure out the reason.
[The whole list of authors is available on commit number
e8faff7330a3501eafc9bfe5f4f15af444be29f5, and I'm going to send them an
email to join this thread of discussion. ]
Since this implementation is already in use in production systems (based
on either AVR32 and ARM), we have to pay attention to properly test any change.
Summarizing your email, you are suggesting to:
- Add the unit of measure (i.e., milliseconds) in the documentation
serial-rs485.txt (it's already available in the header file).
This is straightforward.
- Set delays using msleep (as in the cris driver) rather than using
TTGR. This change is trivial too. Unfortunately, however, I don't
have any hardware to test it at the moment. This means that we will
need some help for a proper testing.
- Add a specific ioctl to get/set TTGR. Looking at the existing code, I
didn't see any specific ioctl to set it. Since this functionality may
be available on other drivers, and since it is not strictly related
to RS485 mode, I guess that we should add general ioctls for serial
drivers. In this case, we need the agreement from the rest of the
community.
The following patch should take into account all these changes.
Best regards,
Claudio
Subject: atmel_serial: use msleep for delays
From: Claudio Scordino <claudio@evidence.eu.com>
This patch:
- Adds the unit of measure (i.e., milliseconds) in the documentation
serial-rs485.txt (it was already available in the header file).
- Sets delays using msleep (as in the cris driver) rather than using
TTGR.
- Adds two generic ioctls to get/set transmitter timeguards in serial drivers
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
Signed-off-by: Guido Classen <clagix@gmail.com>
---
Documentation/serial/serial-rs485.txt | 4 ++--
drivers/tty/serial/atmel_serial.c | 33 +++++++++++++++++++++++++--------
include/uapi/asm-generic/ioctls.h | 2 ++
3 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
index 41c8378..e30335d 100644
--- a/Documentation/serial/serial-rs485.txt
+++ b/Documentation/serial/serial-rs485.txt
@@ -110,10 +110,10 @@
/* or, set logical level for RTS pin equal to 0 after sending: */
rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);
- /* Set rts delay before send, if needed: */
+ /* Set rts delay before send (in milliseconds) if needed: */
rs485conf.delay_rts_before_send = ...;
- /* Set rts delay after send, if needed: */
+ /* Set rts delay after send (in milliseconds) if needed: */
rs485conf.delay_rts_after_send = ...;
/* Set this flag if you want to receive data even whilst sending data */
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 922e85a..9a7e525 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -30,6 +30,7 @@
#include <linux/serial.h>
#include <linux/clk.h>
#include <linux/console.h>
+#include <linux/delay.h>
#include <linux/sysrq.h>
#include <linux/tty_flip.h>
#include <linux/platform_device.h>
@@ -58,6 +59,9 @@
#define SUPPORT_SYSRQ
#endif
+/* Maximum value of TTGR according to Atmel datasheet */
+#define MAX_TTGR_VALUE 255
+
#include <linux/serial_core.h>
static void atmel_start_rx(struct uart_port *port);
@@ -98,6 +102,7 @@ static void atmel_stop_rx(struct uart_port *port);
#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
+#define UART_GET_TTGR(port, v) __raw_readl((port)->membase + ATMEL_US_TTGR)
/* PDC registers */
#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -228,8 +233,6 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
if (rs485conf->flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
- if ((rs485conf->delay_rts_after_send) > 0)
- UART_PUT_TTGR(port, rs485conf->delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
@@ -304,9 +307,6 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
- if ((atmel_port->rs485.delay_rts_after_send) > 0)
- UART_PUT_TTGR(port,
- atmel_port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
@@ -549,7 +549,12 @@ static void atmel_tx_chars(struct uart_port *port)
return;
while (UART_GET_CSR(port) & atmel_port->tx_done_mask) {
+ if ((atmel_port->rs485.delay_rts_before_send) > 0)
+ msleep(atmel_port->rs485.delay_rts_before_send);
UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
+ if ((atmel_port->rs485.delay_rts_after_send) > 0)
+ msleep(atmel_port->rs485.delay_rts_after_send);
+
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
if (uart_circ_empty(xmit))
@@ -1232,9 +1237,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
- if ((atmel_port->rs485.delay_rts_after_send) > 0)
- UART_PUT_TTGR(port,
- atmel_port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
@@ -1370,6 +1372,7 @@ static int
atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
{
struct serial_rs485 rs485conf;
+ u32 timeguard;
switch (cmd) {
case TIOCSRS485:
@@ -1387,6 +1390,20 @@ atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
return -EFAULT;
break;
+ case TIOCSTXTG:
+ if (copy_from_user(&timeguard, (u32 *) arg, sizeof(timeguard)))
+ return -EFAULT;
+ if (timeguard > MAX_TTGR_VALUE)
+ timeguard = MAX_TTGR_VALUE;
+ UART_PUT_TTGR(port, timeguard);
+ break;
+
+ case TIOCGTXTG:
+ UART_GET_TTGR(port, timeguard);
+ if (copy_to_user((u32 *) arg, &timeguard, sizeof(timeguard)))
+ return -EFAULT;
+ break;
+
default:
return -ENOIOCTLCMD;
}
diff --git a/include/uapi/asm-generic/ioctls.h b/include/uapi/asm-generic/ioctls.h
index 143dacb..46cfdee 100644
--- a/include/uapi/asm-generic/ioctls.h
+++ b/include/uapi/asm-generic/ioctls.h
@@ -77,6 +77,8 @@
#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */
+#define TIOCGTXTG 0x5441 /* Get transmitter timeguard (if available) */
+#define TIOCSTXTG 0x5442 /* Set transmitter timeguard (if available) */
#define FIONCLEX 0x5450
#define FIOCLEX 0x5451
--
1.7.9.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* RE: RS485 implementation questions (primarly in atmel_serial.c)
2013-01-23 22:56 RS485 implementation questions (primarly in atmel_serial.c) Guido Classen
2013-01-24 0:10 ` Grant Edwards
2013-01-25 9:13 ` Guido Classen
@ 2013-01-30 14:35 ` Jean-Pierre Tosoni
2013-01-31 14:42 ` Grant Edwards
2 siblings, 1 reply; 17+ messages in thread
From: Jean-Pierre Tosoni @ 2013-01-30 14:35 UTC (permalink / raw)
To: 'Guido Classen', linux-serial
Hi all,
At Guido's request here are my thoughts:
> Units this parameters are given. The crisv10.c driver gets the
> delay_before_send in milliseconds and atmel_serial
> delay_rts_after_send in bit-times (means depending on baud rate).
RTS delays should be given in ms not in bit-times since they are not related
to bit rate but to amplifiers and peer software response times.
> But in the atmel_serial.c driver rthe Transmit Timeguard function of
> the AT91 USART is used to implement this. The purpose of the Transmit
> Timeguard function is to throttle transmission in case the receiving
> device is able to receive/process incoming characters at line
> speed. This means the USART will insert a gap between EACH transmitted
> byte and not only the last of a frame. Atmel surely had its reasons
> to implement such a function, but luckily nowadays such devices are
Sounds to me like a "quick and dirty" implementation of the RTS toggling, to
avoid the burden of setting timers etc. The interbyte gap is not needed.
> An other point I don't understand is why ATMEL_US_TXEMPTY interrupt is
> used when in RS485 mode insted of the normal ATMEL_US_TXRDY or
> ATEM_US_ENDTX interrupts. In the RS485 mode the AT91 USART will
> connect the RTS pin to the internal TXEMPTY signal to drive the RTS
> until the last bit is shifted out. But this should not have any affect
> on ow the drive will pump out the bytes to the USART's transmit hold
> register?
Well, you need to ascertain that the previous frame is all out AND the RTS
has dropped before sending a new frame. Else the receiver might think it is
the same long frame.
Using TXEMPTY interrupt avoids setting an extra timer.
However in the real life you are half-duplex and never send two frames in a
row without waiting for an answer.
Other than that I agree with your remark.
> rare. But this function is not especially related to RS485. It is
> also applicable in RS232 mode. So in my view it is not the
True, it applies to some half-duplex RS232 radio modems too AFAIK.
French regards,
Jean-Pierre Tosoni
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: RS485 implementation questions (primarly in atmel_serial.c)
2013-01-29 22:29 ` Grant Edwards
@ 2013-01-30 15:24 ` Guido Classen
2013-01-30 17:57 ` Grant Edwards
0 siblings, 1 reply; 17+ messages in thread
From: Guido Classen @ 2013-01-30 15:24 UTC (permalink / raw)
To: Grant Edwards; +Cc: linux-serial
> What I'm thinking about doing is instead of using a tty driver,
> writing a char driver. That eliminates the whole tty/ldisc tangle and
> allows you to implement read()/write() as packet operations rather
> than bytestream operations. You can still implement whatever subset
> of the termios ioctl() calls make sense along with whatever new
> ioctl() calls are needed to control/configure things like inter-byte
> timeouts, 9th-bit addressing modes, frame-recognition state-machines,
> etc.
If I understand this right, you suggest to introduce a new subsystem for
half-duplex field-bus style communication which is fully independent from
the existing POSIX tty subsystem. This subsystem will include own ioctls
for settings timeouts, addressing, turn on and turn off times and so on,
will support some temios ioctls which are applicable like setting baud-rates.
There will be plugable frame-recognition state-machines. How will the
low-level side look like? Will it use the existing serial drivers (over some
kind of new interface?) Or will it have it's own set of serial drivers which
are designed for frame based half duplex communication.
This means duplicate drivers for each kind of UART hardware (8250,
atmel, ...) I am not sure if this is a good design, but on other hand there
are already duplicate drivers for 16550 style UARTs in the kernel for
special purposes like bluetooth, irda and MIDI-interfaces.
>From userspace view this will make thinks much easier, so I personally
will support this approach. But we have to be aware that this will
introduce a new API which is not standardized and portable to other
OSs. Furthermore there must be some way to have access to the same
hardware either over this API or over the TTY API.
>
>> 4. The TIOCSRS485 ioctl may open new doors, but as I see there are
>> only few drivers implementing it.
>
> Too bad about the name.
>
> It doesn't actually select RS485 mode (I work with board that _do_
> have software-selectable electrical interfaces and can be set to
> RS2323, RS485, RS422 modes). What's called "RS485" moide controls
> enabling the use of RTS for half-duplex operation. RS485 is _one_
> electrical interface that uses RTS like that, but there are lots of
> others (RS232 and half-duplex modems is one). And not all use-cases
> for RS485 use RTS for half-duplex communications either.
>
Your fully right. RS232 on the one side and RS485/RS422 on the other are
different physical layers. RS485 is always half-duplex. All RS232 and RS485
can be full or half-duplex. As you said there are RS232 half-duplex modems and
there are also external RS232 to RS485 converters which can be used on all
systems with a RS232 port.
So you have to differ about the hardware design of a port:
- fixed wired RS232 / RS485 / RS422 transceiver
operation mode can be hard coded in board code
- RS232 / RS485 / RS422 configurable by hardware (jumper or piggybacks)
operation mode must be configurable and must be set by the user
according the actual hardware configuration
- RS232 / RS485 / RS422 configurable from software
operation mode of the driver and configuration of the hardware
must be changed
An on other hand you have the kind of communication (full-duplex or
half-duplex).
which is as said not necessary bound to the physical variant of the port.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: RS485 implementation questions (primarly in atmel_serial.c)
2013-01-30 15:24 ` Guido Classen
@ 2013-01-30 17:57 ` Grant Edwards
0 siblings, 0 replies; 17+ messages in thread
From: Grant Edwards @ 2013-01-30 17:57 UTC (permalink / raw)
To: linux-serial
On 2013-01-30, Guido Classen <clagix@gmail.com> wrote:
>> What I'm thinking about doing is instead of using a tty driver,
>> writing a char driver. That eliminates the whole tty/ldisc tangle and
>> allows you to implement read()/write() as packet operations rather
>> than bytestream operations. You can still implement whatever subset
>> of the termios ioctl() calls make sense along with whatever new
>> ioctl() calls are needed to control/configure things like inter-byte
>> timeouts, 9th-bit addressing modes, frame-recognition state-machines,
>> etc.
>
> If I understand this right, you suggest to introduce a new subsystem for
> half-duplex field-bus style communication which is fully independent from
> the existing POSIX tty subsystem. This subsystem will include own ioctls
> for settings timeouts, addressing, turn on and turn off times and so on,
> will support some temios ioctls which are applicable like setting baud-rates.
It wouldn't have to be half-duplex.
> There will be plugable frame-recognition state-machines. How will the
> low-level side look like? Will it use the existing serial drivers
> (over some kind of new interface?)
I haven't gotten that far. Since the existing serial drivers assume
the existance and use of the tty layer, they probably can't be used
as-is.
> Or will it have it's own set of serial drivers which are designed for
> frame based half duplex communication. This means duplicate drivers
> for each kind of UART hardware (8250, atmel, ...)
Unfortunately, that's probably what it would take.
> I am not sure if this is a good design, but on other hand there are
> already duplicate drivers for 16550 style UARTs in the kernel for
> special purposes like bluetooth, irda and MIDI-interfaces.
>
> From userspace view this will make thinks much easier, so I
> personally will support this approach. But we have to be aware that
> this will introduce a new API which is not standardized and portable
> to other OSs.
That's a very real issue. The applications I'm initially interested
in porting aren't Unix/Linux apps anyway, so for my particular project
the applications have to be ported to something, and the existing tty
API just doesn't provide the required features.
> Furthermore there must be some way to have access to the same
> hardware either over this API or over the TTY API.
That's not one of my requirements. My plan is that you either enable
the serial_core driver or my "low-level char/frame" driver -- you
can't use both. You could, of course, build them both as modules and
switch back and forth without rebooting.
There's no reason that the driver I'm thinking about can't be
partially API compatible so that you can use the same libc calls to do
common things like set baud rate/parity, get/set modem status/control
lines, and so on.
--
Grant Edwards grant.b.edwards Yow! I can't decide which
at WRONG TURN to make first!!
gmail.com I wonder if BOB GUCCIONE
has these problems!
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: RS485 implementation questions (primarly in atmel_serial.c)
2013-01-30 14:35 ` Jean-Pierre Tosoni
@ 2013-01-31 14:42 ` Grant Edwards
0 siblings, 0 replies; 17+ messages in thread
From: Grant Edwards @ 2013-01-31 14:42 UTC (permalink / raw)
To: linux-serial
On 2013-01-30, Jean-Pierre Tosoni <jp.tosoni@acksys.fr> wrote:
>
> Well, you need to ascertain that the previous frame is all out AND
> the RTS has dropped before sending a new frame. Else the receiver
> might think it is the same long frame. Using TXEMPTY interrupt avoids
> setting an extra timer. However in the real life you are half-duplex
> and never send two frames in a row without waiting for an answer.
> Other than that I agree with your remark.
>
>> rare. But this function is not especially related to RS485. It is
>> also applicable in RS232 mode. So in my view it is not the
>
> True, it applies to some half-duplex RS232 radio modems too AFAIK.
It applies to half duplex modems and line-drivers whether they're
radio or not. Believe it or not, Bell-202 half-duplex over plain old
copper wire is still used a lot in the process control industry.
But, because of the general brokeness of both Windows and Linux serial
drivers when it comes to half-duplex communications, many of the
Bell-202 modems sold these days have automatic tx data detection and
handle the "RTS toggle" function themselves.
--
Grant Edwards grant.b.edwards Yow! Psychoanalysis??
at I thought this was a nude
gmail.com rap session!!!
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: RS485 implementation questions (primarly in atmel_serial.c)
[not found] ` <5114B500.7050007@evidence.eu.com>
@ 2013-02-08 12:12 ` Guido Classen
2013-02-11 15:54 ` [PATCH] atmel_serial: general fixes for RS485 and TTGR claudio
` (3 more replies)
0 siblings, 4 replies; 17+ messages in thread
From: Guido Classen @ 2013-02-08 12:12 UTC (permalink / raw)
To: Claudio Scordino, linux-serial
[-- Attachment #1: Type: text/plain, Size: 1470 bytes --]
On Fri, Feb 8, 2013 at 9:19 AM, Claudio Scordino
<claudio@evidence.eu.com> wrote:
>> The sysfs entry ttgr is there, but I couldn't manage to assign a
>> value. It is always read back as zero and seams to have no effect on
>> the gap between sent chars. I am not so familiar with sysfs, but I
>> could imagine that something white the deduction of the port pointer
>> may be wrong.
>
>
> Quite strange.
>
I found my mistake, the sysfs entries are lying under
/sys/devices/platform/atmel_usart.*
as expected, but they are numbered different to the /dev/ttyAT*
devices. So I used the entry of the DBGU. The DBGU lacks some features
the normal USARTs have include the TTGR. I wonder why the atmel_serial
driver not checks if the device is the DBGU or a USART and in case
DBGU don't allow to set features (example character length) not
supported?
I have split you patch in two parts. I think the TTGR sysfs entries
are a different concern than the RS485 timing issues. Please find
attached the TTGR patch with some modifications I made:
- simplify obtaining the uart_port pointer using dev_get_drvdata()
- present ttgr sysfs entry only when port is not the DBGU
The function is_uart_port_dbgu() is a kind of hack, I found no
better way to determinate the
type of the port. Perhaps we should add a is_dbgu field to the
atmel_uart_data structure which
is set by the CPU specific initialization.
Next I will have a look on the RS485 timing.
Regards,
Guido
[-- Attachment #2: 0001-add-generic-atmel-serial-TTGR-support.patch --]
[-- Type: application/octet-stream, Size: 3612 bytes --]
From 60149f0eb9545a01fd6ce29bed13717ce5926df7 Mon Sep 17 00:00:00 2001
From: Guido Classen <clagix@gmail.com>
Date: Fri, 8 Feb 2013 12:48:40 +0100
Subject: [PATCH] add generic atmel serial TTGR support
This patch adds an entry in sys/ to get/set the TTGR register for this
specific driver. When set greater than zero the driver will insert gaps
between the sent characters. The length of the gaps will be specified in
bit times. The feature can be used either in RS232 and RS485 mode to
slow down transmission if the receiving device is not capable to process
incoming characters at line speed.
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
Signed-off-by: Guido Classen <clagix@gmail.com>
---
drivers/tty/serial/atmel_serial.c | 46 +++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 8cc1e84..aeca9d5 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -59,6 +59,9 @@
#define SUPPORT_SYSRQ
#endif
+/* Maximum value of TTGR according to Atmel datasheet */
+#define MAX_TTGR_VALUE 255
+
#include <linux/serial_core.h>
static void atmel_start_rx(struct uart_port *port);
@@ -99,6 +102,7 @@ static void atmel_stop_rx(struct uart_port *port);
#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
+#define UART_GET_TTGR(port) __raw_readl((port)->membase + ATMEL_US_TTGR)
/* PDC registers */
#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -181,6 +185,15 @@ to_atmel_uart_port(struct uart_port *uart)
return container_of(uart, struct atmel_uart_port, uart);
}
+static inline bool
+is_uart_port_dbgu(struct uart_port *uart)
+{
+ struct atmel_uart_data *atmel_uart_data =
+ to_platform_device((uart)->dev)->dev.platform_data;
+ return atmel_uart_data->use_dma_tx == 0
+ && atmel_uart_data->use_dma_rx == 0;
+}
+
#ifdef CONFIG_SERIAL_ATMEL_PDC
static bool atmel_use_dma_rx(struct uart_port *port)
{
@@ -1423,6 +1436,33 @@ static struct uart_ops atmel_pops = {
#endif
};
+/* Entry in sys/ to get/set TTGR register */
+
+static ssize_t set_ttgr(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+ unsigned int value;
+ if (kstrtouint(buf, 10, &value))
+ return 0;
+ if (value > MAX_TTGR_VALUE)
+ value = MAX_TTGR_VALUE;
+ UART_PUT_TTGR(port, value);
+ return strnlen(buf, PAGE_SIZE);
+}
+
+static ssize_t get_ttgr(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+ unsigned int value;
+ value = UART_GET_TTGR(port);
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+}
+
+static DEVICE_ATTR(ttgr, 0644, get_ttgr, set_ttgr);
+
+
static void __devinit atmel_of_init_port(struct atmel_uart_port *atmel_port,
struct device_node *np)
{
@@ -1839,6 +1879,9 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
}
+ if (!is_uart_port_dbgu(&port->uart))
+ device_create_file(&(pdev->dev), &dev_attr_ttgr);
+
return 0;
err_add_port:
@@ -1873,6 +1916,9 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev)
clk_put(atmel_port->clk);
+ if (!is_uart_port_dbgu(port))
+ device_remove_file(&(pdev->dev), &dev_attr_ttgr);
+
return ret;
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH] atmel_serial: general fixes for RS485 and TTGR
2013-02-08 12:12 ` Guido Classen
@ 2013-02-11 15:54 ` claudio
2013-02-11 17:05 ` Guido Classen
2013-02-11 15:54 ` [PATCH 1/3] RS485: add unit of measure for delays claudio
` (2 subsequent siblings)
3 siblings, 1 reply; 17+ messages in thread
From: claudio @ 2013-02-11 15:54 UTC (permalink / raw)
To: linux-serial; +Cc: nicolas.ferre, clagix
Dear Guido.
I agree about splitting the patch. Therefore, I've created the following patchset which
- Adds the unit of measure (i.e., milliseconds) in the documentation
serial-rs485.txt (it is already available in the header file).
- Sets RS485 delays using msleep (as in the cris driver) rather than using
TTGR.
- Adds an entry in sys/ to get/set the TTGR register for this specific driver.
Have you tested the msleep part ?
Best regards,
Claudio
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 1/3] RS485: add unit of measure for delays
2013-02-08 12:12 ` Guido Classen
2013-02-11 15:54 ` [PATCH] atmel_serial: general fixes for RS485 and TTGR claudio
@ 2013-02-11 15:54 ` claudio
2013-02-11 15:54 ` [PATCH 2/3] atmel_serial: use msleep " claudio
2013-02-11 15:54 ` [PATCH 3/3] atmel_serial: add generic TTGR support claudio
3 siblings, 0 replies; 17+ messages in thread
From: claudio @ 2013-02-11 15:54 UTC (permalink / raw)
To: linux-serial; +Cc: nicolas.ferre, clagix, Claudio Scordino
From: Claudio Scordino <claudio@evidence.eu.com>
This patch adds the unit of measure (i.e., milliseconds) in the documentation
serial-rs485.txt (it is already available in the header file).
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
Signed-off-by: Guido Classen <clagix@gmail.com>
---
Documentation/serial/serial-rs485.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
index 41c8378..e30335d 100644
--- a/Documentation/serial/serial-rs485.txt
+++ b/Documentation/serial/serial-rs485.txt
@@ -110,10 +110,10 @@
/* or, set logical level for RTS pin equal to 0 after sending: */
rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);
- /* Set rts delay before send, if needed: */
+ /* Set rts delay before send (in milliseconds) if needed: */
rs485conf.delay_rts_before_send = ...;
- /* Set rts delay after send, if needed: */
+ /* Set rts delay after send (in milliseconds) if needed: */
rs485conf.delay_rts_after_send = ...;
/* Set this flag if you want to receive data even whilst sending data */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 2/3] atmel_serial: use msleep for delays
2013-02-08 12:12 ` Guido Classen
2013-02-11 15:54 ` [PATCH] atmel_serial: general fixes for RS485 and TTGR claudio
2013-02-11 15:54 ` [PATCH 1/3] RS485: add unit of measure for delays claudio
@ 2013-02-11 15:54 ` claudio
2013-02-11 15:54 ` [PATCH 3/3] atmel_serial: add generic TTGR support claudio
3 siblings, 0 replies; 17+ messages in thread
From: claudio @ 2013-02-11 15:54 UTC (permalink / raw)
To: linux-serial; +Cc: nicolas.ferre, clagix, Claudio Scordino
From: Claudio Scordino <claudio@evidence.eu.com>
The Transmitter TimeGuard Register (TTGR) should not be used for delays in
RS485 mode, because it is not a feature related only to RS485 (it should be
also available in RS232 mode) and bacause delays should be expressed in
milliseconds rrather than in bit times.
This patch sets RS485 delays using msleep (as in the cris driver) rather than
using TTGR.
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
Signed-off-by: Guido Classen <clagix@gmail.com>
---
drivers/tty/serial/atmel_serial.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 922e85a..52648ec 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -30,6 +30,7 @@
#include <linux/serial.h>
#include <linux/clk.h>
#include <linux/console.h>
+#include <linux/delay.h>
#include <linux/sysrq.h>
#include <linux/tty_flip.h>
#include <linux/platform_device.h>
@@ -228,8 +229,6 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
if (rs485conf->flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
- if ((rs485conf->delay_rts_after_send) > 0)
- UART_PUT_TTGR(port, rs485conf->delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
@@ -304,9 +303,6 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
- if ((atmel_port->rs485.delay_rts_after_send) > 0)
- UART_PUT_TTGR(port,
- atmel_port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
@@ -549,7 +545,12 @@ static void atmel_tx_chars(struct uart_port *port)
return;
while (UART_GET_CSR(port) & atmel_port->tx_done_mask) {
+ if ((atmel_port->rs485.delay_rts_before_send) > 0)
+ msleep(atmel_port->rs485.delay_rts_before_send);
UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
+ if ((atmel_port->rs485.delay_rts_after_send) > 0)
+ msleep(atmel_port->rs485.delay_rts_after_send);
+
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
if (uart_circ_empty(xmit))
@@ -1232,9 +1233,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
- if ((atmel_port->rs485.delay_rts_after_send) > 0)
- UART_PUT_TTGR(port,
- atmel_port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 3/3] atmel_serial: add generic TTGR support
2013-02-08 12:12 ` Guido Classen
` (2 preceding siblings ...)
2013-02-11 15:54 ` [PATCH 2/3] atmel_serial: use msleep " claudio
@ 2013-02-11 15:54 ` claudio
3 siblings, 0 replies; 17+ messages in thread
From: claudio @ 2013-02-11 15:54 UTC (permalink / raw)
To: linux-serial; +Cc: nicolas.ferre, clagix, Claudio Scordino
From: Claudio Scordino <claudio@evidence.eu.com>
This patch adds an entry in sys/ to get/set the TTGR register for this specific
driver.
When set greater than zero the driver will insert gaps between the sent
characters. The length of the gaps will be specified in bit times. The feature
can be used either in RS232 and RS485 mode to slow down transmission if the
receiving device is not capable to process incoming characters at line speed.
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
Signed-off-by: Guido Classen <clagix@gmail.com>
Tested-by: Guido Classen <clagix@gmail.com>
---
drivers/tty/serial/atmel_serial.c | 53 +++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 52648ec..df3d871 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -59,6 +59,9 @@
#define SUPPORT_SYSRQ
#endif
+/* Maximum value of TTGR according to Atmel datasheet */
+#define MAX_TTGR_VALUE 255
+
#include <linux/serial_core.h>
static void atmel_start_rx(struct uart_port *port);
@@ -99,6 +102,7 @@ static void atmel_stop_rx(struct uart_port *port);
#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
+#define UART_GET_TTGR(port) __raw_readl((port)->membase + ATMEL_US_TTGR)
/* PDC registers */
#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -181,6 +185,14 @@ to_atmel_uart_port(struct uart_port *uart)
return container_of(uart, struct atmel_uart_port, uart);
}
+static inline bool is_uart_port_dbgu(struct uart_port *uart)
+{
+ struct atmel_uart_data *atmel_uart_data =
+ to_platform_device((uart)->dev)->dev.platform_data;
+ return atmel_uart_data->use_dma_tx == 0
+ && atmel_uart_data->use_dma_rx == 0;
+}
+
#ifdef CONFIG_SERIAL_ATMEL_PDC
static bool atmel_use_dma_rx(struct uart_port *port)
{
@@ -1420,6 +1432,41 @@ static struct uart_ops atmel_pops = {
#endif
};
+/* Entry in sys/ to get/set TTGR register */
+
+static ssize_t set_ttgr(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct atmel_uart_data *pdata = pdev->dev.platform_data;
+ int id = pdata->num;
+ struct atmel_uart_port *atmel_port = &atmel_ports[id];
+ struct uart_port *port = &(atmel_port->uart);
+ unsigned int value;
+ if (kstrtouint(buf, 10, &value))
+ return 0;
+ if (value > MAX_TTGR_VALUE)
+ value = MAX_TTGR_VALUE;
+ UART_PUT_TTGR(port, value);
+ return strnlen(buf, PAGE_SIZE);
+}
+
+static ssize_t get_ttgr(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct atmel_uart_data *pdata = pdev->dev.platform_data;
+ int id = pdata->num;
+ struct atmel_uart_port *atmel_port = &atmel_ports[id];
+ struct uart_port *port = &(atmel_port->uart);
+ unsigned int value;
+ value = UART_GET_TTGR(port);
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+}
+
+static DEVICE_ATTR(ttgr, 0644, get_ttgr, set_ttgr);
+
+
static void atmel_of_init_port(struct atmel_uart_port *atmel_port,
struct device_node *np)
{
@@ -1824,6 +1871,9 @@ static int atmel_serial_probe(struct platform_device *pdev)
UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
}
+ if (!is_uart_port_dbgu(&port->uart))
+ device_create_file(&(pdev->dev), &dev_attr_ttgr);
+
return 0;
err_add_port:
@@ -1858,6 +1908,9 @@ static int atmel_serial_remove(struct platform_device *pdev)
clk_put(atmel_port->clk);
+ if (!is_uart_port_dbgu(port))
+ device_remove_file(&(pdev->dev), &dev_attr_ttgr);
+
return ret;
}
--
1.7.9.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH] atmel_serial: general fixes for RS485 and TTGR
2013-02-11 15:54 ` [PATCH] atmel_serial: general fixes for RS485 and TTGR claudio
@ 2013-02-11 17:05 ` Guido Classen
0 siblings, 0 replies; 17+ messages in thread
From: Guido Classen @ 2013-02-11 17:05 UTC (permalink / raw)
To: Claudio Scordino; +Cc: linux-serial, Nicolas Ferre
Dear Claudio,
I am not quite happy with your msleep patch because
a) It doesn't assert RTS during the sleep, so we don't have the intended effect
b) It will do the gaps before / after each character, not frame
c) It will raise a "BUG scheduling while atomic". As far as I see, it
is not allowed
to do sleeps inside of the tasklet code
The crisv10 driver does this things more the way I have expected
(although it doesn't
implement delay_rts_after_send). But a direct port of this code seams
not to be possible
because
- this driver still is build on the tty interface while the newer driver use the
uart_port interface from serial_core
- the crisv10 driver uses some architecture specific fast_timer code
I will continue to work on this issues and hopefully I can provide you
a patch in a few
days.
The TTGR patch looks good, but why haven't you used my dev_get_drvdata(dev)
code?
Regards,
Guido
On Mon, Feb 11, 2013 at 4:54 PM, <claudio@evidence.eu.com> wrote:
> Dear Guido.
>
> I agree about splitting the patch. Therefore, I've created the following patchset which
>
> - Adds the unit of measure (i.e., milliseconds) in the documentation
> serial-rs485.txt (it is already available in the header file).
>
> - Sets RS485 delays using msleep (as in the cris driver) rather than using
> TTGR.
>
> - Adds an entry in sys/ to get/set the TTGR register for this specific driver.
>
> Have you tested the msleep part ?
>
> Best regards,
>
> Claudio
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2013-02-11 17:05 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CAJa4H3cYT6QOaacWEy2yZjb0hM3m4p+0L2_fXBYv8tWajYkmGw@mail.gmail.com>
2013-01-30 10:30 ` RS485 implementation questions (primarly in atmel_serial.c) Claudio Scordino
[not found] ` <510A3740.6060809@evidence.eu.com>
[not found] ` <CAJa4H3dG4V6viRbVQBgHQKtJr4Sz26mkhj8-kT7RFaLGRFXW1g@mail.gmail.com>
[not found] ` <51125488.7090408@evidence.eu.com>
[not found] ` <CAJa4H3eNAS7B+aGnN0D3WJMPVtE=sxp5HhRMutq-TrBOmXb7dw@mail.gmail.com>
[not found] ` <5114B500.7050007@evidence.eu.com>
2013-02-08 12:12 ` Guido Classen
2013-02-11 15:54 ` [PATCH] atmel_serial: general fixes for RS485 and TTGR claudio
2013-02-11 17:05 ` Guido Classen
2013-02-11 15:54 ` [PATCH 1/3] RS485: add unit of measure for delays claudio
2013-02-11 15:54 ` [PATCH 2/3] atmel_serial: use msleep " claudio
2013-02-11 15:54 ` [PATCH 3/3] atmel_serial: add generic TTGR support claudio
2013-01-23 22:56 RS485 implementation questions (primarly in atmel_serial.c) Guido Classen
2013-01-24 0:10 ` Grant Edwards
2013-01-25 9:13 ` Guido Classen
2013-01-25 15:39 ` Grant Edwards
2013-01-29 21:56 ` Guido Classen
2013-01-29 22:29 ` Grant Edwards
2013-01-30 15:24 ` Guido Classen
2013-01-30 17:57 ` Grant Edwards
2013-01-30 14:35 ` Jean-Pierre Tosoni
2013-01-31 14:42 ` Grant Edwards
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).