* Re: [PATCH 02/12] usb: fix sillies in the metro USB driver
From: Alan Cox @ 2012-07-23 16:32 UTC (permalink / raw)
To: Jiri Slaby; +Cc: greg, linux-kernel, linux-serial, Jiri Slaby
In-Reply-To: <4FE78B62.4090106@suse.cz>
On Sun, 24 Jun 2012 23:49:22 +0200
Jiri Slaby <jslaby@suse.cz> wrote:
> On 06/22/2012 05:38 PM, Alan Cox wrote:
> > Bits noticed doing the termios conversion
> >
> > Signed-off-by: Alan Cox <alan@linux.intel.com>
> > ---
> >
> > drivers/usb/serial/metro-usb.c | 8 +-------
> > 1 file changed, 1 insertion(+), 7 deletions(-)
> >
> > diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c
> > index 81423f7..bad5f0c 100644
> > --- a/drivers/usb/serial/metro-usb.c
> > +++ b/drivers/usb/serial/metro-usb.c
> > @@ -130,20 +130,14 @@ static void metrousb_read_int_callback(struct urb *urb)
> ...
> > if (tty && urb->actual_length) {
> > /* Loop through the data copying each byte to the tty layer. */
> > tty_insert_flip_string(tty, data, urb->actual_length);
> >
> > /* Force the data to the tty layer. */
> > tty_flip_buffer_push(tty);
> > + tty_kref_put(tty);
> > }
> > - tty_kref_put(tty);
>
> This doesn't seem right. (Depends on whether tty is non-null iff
> urb->actual_length is non-zero.)
Doh fixed.. queued
^ permalink raw reply
* n_tty_read panic
From: 大林 @ 2012-07-25 1:47 UTC (permalink / raw)
To: linux-serial
Hello.
Here's a kernel panic caused by n_tty_read, linux kernel version is 2.6.38.8.
There are many discussions on the subject, this bug solved yet? Where can I download the patch?
thanks.
Jul 24 01:30:25 AnShion <8> klogd: [432687.542510] ------------[ cut here ]------------
Jul 24 01:30:25 AnShion <10> klogd: [432687.598836] kernel BUG at drivers/tty/n_tty.c:1725!
Jul 24 01:30:25 AnShion <8> klogd: [432687.658218] invalid opcode: 0000 [#384] SMP
Jul 24 01:30:25 AnShion <8> klogd: [432687.704085] last sysfs file: /sys/devices/pci0000:00/0000:00:1e.0/0000:05:05.0/resource
Jul 24 01:30:25 AnShion <12> klogd: [432687.704087] Modules linked in: igb e1000e
Jul 24 01:30:25 AnShion <12> klogd: [432687.704093]
Jul 24 01:30:25 AnShion <12> klogd: [432687.704095] Pid: 6856, comm: login Tainted: G D 2.6.38.8 #412 To be filled by O.E.M. To be filled by O.E.M./P8B-X series
Jul 24 01:30:25 AnShion <12> klogd: [432687.704098] EIP: 0060:[<c041b415>] EFLAGS: 00010246 CPU: 5
Jul 24 01:30:25 AnShion <12> klogd: [432687.704101] EIP is at n_tty_read+0x735/0x750
Jul 24 01:30:25 AnShion <12> klogd: [432687.704103] EAX: 00000000 EBX: eda6609c ECX: eda66128 EDX: 00000000
Jul 24 01:30:25 AnShion <12> klogd: [432687.704104] ESI: 00000000 EDI: eda66000 EBP: e4f59f3c ESP: e4f59ed0
Jul 24 01:30:25 AnShion <12> klogd: [432687.704105] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
Jul 24 01:30:25 AnShion <8> klogd: [432687.704106] Process login (pid: 6856, ti=e4f58000 task=dda1b200 task.ti=e4f58000)
Jul 24 01:30:25 AnShion <8> klogd: [432687.704107] Stack:
Jul 24 01:30:25 AnShion <12> klogd: [432687.704108] 00000001 dda1b200 dda1b200 b7571000 ee6e3a80 eda66124 eda663a8 eda6609c
Jul 24 01:30:25 AnShion <12> klogd: [432687.704110] 00000000 00000000 fffffffb 7fffffff 00000000 e4f59f14 eda66128 eda66190
Jul 24 01:30:25 AnShion <12> klogd: [432687.704112] eda66400 00000000 dda1b200 c01419c0 00100100 00200200 00000000 b7571000
Jul 24 01:30:25 AnShion <8> klogd: [432687.704114] Call Trace:
Jul 24 01:30:25 AnShion <12> klogd: [432687.704120] [<c01419c0>] ? default_wake_function+0x0/0x10
Jul 24 01:30:25 AnShion <12> klogd: [432687.704122] [<c041648c>] tty_read+0x7c/0xb0
Jul 24 01:30:25 AnShion <12> klogd: [432687.704123] [<c041ace0>] ? n_tty_read+0x0/0x750
Jul 24 01:30:25 AnShion <12> klogd: [432687.704126] [<c01f4119>] vfs_read+0x99/0x160
Jul 24 01:30:25 AnShion <12> klogd: [432687.704127] [<c0416410>] ? tty_read+0x0/0xb0
Jul 24 01:30:25 AnShion <12> klogd: [432687.704129] [<c01f429d>] sys_read+0x3d/0x70
Jul 24 01:30:25 AnShion <12> klogd: [432687.704131] [<c010315f>] sysenter_do_call+0x12/0x28
Jul 24 01:30:25 AnShion <8> klogd: [432687.704132] Code: 8b 55 08 89 45 bc 85 d2 0f 84 c4 fb ff ff 8b 4d b0 f0 80 a7 9c 00 00 00 bf 89 45 bc e9 b1 fb ff ff b8 f5 ff ff ff e9 c3 fb ff ff <0f> 0b eb fe c7 04 24 11 f8 a2 c0 e8 f1 b7 3f 00 e9 6c f9 ff ff
Jul 24 01:30:25 AnShion <8> klogd: [432687.704144] EIP: [<c041b415>] n_tty_read+0x735/0x750 SS:ESP 0068:e4f59ed0
Jul 24 01:30:25 AnShion <12> klogd: [432687.707970] ---[ end trace ef5af88a90ac84fe ]---
^ permalink raw reply
* Re: n_tty_read panic
From: Alan Cox @ 2012-07-25 11:44 UTC (permalink / raw)
To: 大林; +Cc: linux-serial
In-Reply-To: <2cd866$1c50tmu@irja2-156.sinamail.sina.com.cn>
On Wed, 25 Jul 2012 09:47:09 +0800
"大林" <yourdarling1999@sina.com> wrote:
> Hello.
>
> Here's a kernel panic caused by n_tty_read, linux kernel version is 2.6.38.8.
>
>
> There are many discussions on the subject, this bug solved yet? Where can I download the patch?
Thats a different trace to the others I've seen. Several races have been
fixed, there are certainly a couple of others left to sort eventually. I
don't believe anyone is currently working on them (and indeed until the
rest of the locking and lock splitting work is done it's difficult to see
how any useful progress could be made here). Patches are however welcome.
Alan
--
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
* No big TTY/serial patch merge for 3.6-rc1
From: Greg KH @ 2012-07-26 19:08 UTC (permalink / raw)
To: Linus Torvalds, Alan Cox, Jiri Slaby
Cc: Stephen Rothwell, Andrew Morton, linux-kernel, linux-serial
Hi Linus,
I don't really feel comfortable sending you the tty tree at the present
time to have merged for 3.6-rc1. It contains some tty changes that are
still causing build problems, as Stephen has pointed out over the past
week. These fixes are being resolved by Alan, but I don't feel that
they have had the time to fully be tested, and given the late arrival of
them (i.e. the past few days), and the lack of real amount of time
testing in linux-next, I'd really like to postpone the whole merge until
3.7.
Right now, this really isn't a whole lot of patches, there are only 62
patches in the tty-next tree. I've included below the full diffstat and
shortlog of them if anyone wants to see them.
There are maybe a few patches below that I think I should cherry-pick
and have you pull, but that's just a handful, and are only for a few
drivers, nothing in the tty core code at all.
Jiri, I know this postpones your patches from being merged, sorry about
that, but this gives us a few more months to ensure that they are
working properly :)
Alan, please keep sending me patches to fix these merge issues, but for
now, I think it's best to wait until 3.7 for this to go to Linus.
thanks,
greg k-h
------------------
.../bindings/tty/serial/nxp-lpc32xx-hsuart.txt | 14 +
.../devicetree/bindings/tty/serial/of-serial.txt | 3 +
arch/ia64/hp/sim/simserial.c | 2 +-
arch/um/drivers/chan_kern.c | 4 +-
arch/um/drivers/line.c | 32 +-
arch/um/drivers/line.h | 3 +-
drivers/bluetooth/hci_ath.c | 2 +-
drivers/char/mwave/mwavedd.c | 16 +-
drivers/char/pcmcia/synclink_cs.c | 24 +-
drivers/isdn/gigaset/interface.c | 4 +-
drivers/isdn/i4l/isdn_tty.c | 16 +-
drivers/misc/ibmasm/uart.c | 16 +-
drivers/mmc/card/sdio_uart.c | 20 +-
drivers/net/ethernet/sgi/ioc3-eth.c | 22 +-
drivers/net/irda/irtty-sir.c | 10 +-
drivers/net/usb/hso.c | 12 +-
drivers/tty/amiserial.c | 20 +-
drivers/tty/cyclades.c | 82 +-
drivers/tty/hvc/hvsi_lib.c | 2 +-
drivers/tty/isicom.c | 8 +-
drivers/tty/moxa.c | 10 +-
drivers/tty/mxser.c | 20 +-
drivers/tty/n_gsm.c | 8 +-
drivers/tty/n_tty.c | 8 +-
drivers/tty/pty.c | 144 ++--
drivers/tty/rocket.c | 18 +-
drivers/tty/serial/8250/8250.c | 88 +--
drivers/tty/serial/8250/8250.h | 31 +-
drivers/tty/serial/8250/8250_acorn.c | 22 +-
drivers/tty/serial/8250/8250_dw.c | 38 +-
drivers/tty/serial/8250/8250_gsc.c | 26 +-
drivers/tty/serial/8250/8250_hp300.c | 26 +-
drivers/tty/serial/8250/8250_pci.c | 126 +--
drivers/tty/serial/8250/8250_pnp.c | 28 +-
drivers/tty/serial/8250/serial_cs.c | 30 +-
drivers/tty/serial/Kconfig | 19 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/amba-pl011.c | 34 +-
drivers/tty/serial/bfin_uart.c | 2 +-
drivers/tty/serial/crisv10.c | 26 +-
drivers/tty/serial/imx.c | 2 +-
drivers/tty/serial/ioc4_serial.c | 2 +-
drivers/tty/serial/jsm/jsm_tty.c | 8 +-
drivers/tty/serial/lpc32xx_hs.c | 823 ++++++++++++++++++++
drivers/tty/serial/of_serial.c | 14 +-
drivers/tty/serial/pch_uart.c | 59 +-
drivers/tty/serial/pxa.c | 14 +
drivers/tty/serial/samsung.c | 30 +-
drivers/tty/serial/serial_core.c | 34 +-
drivers/tty/synclink.c | 36 +-
drivers/tty/synclink_gt.c | 24 +-
drivers/tty/synclinkmp.c | 24 +-
drivers/tty/tty_io.c | 104 +--
drivers/tty/tty_ioctl.c | 100 +--
drivers/tty/tty_ldisc.c | 12 +-
drivers/tty/tty_port.c | 23 +-
drivers/tty/vt/keyboard.c | 50 +-
drivers/tty/vt/vt.c | 63 +-
drivers/tty/vt/vt_ioctl.c | 47 +-
drivers/usb/class/cdc-acm.c | 2 +-
drivers/usb/serial/ark3116.c | 4 +-
drivers/usb/serial/belkin_sa.c | 2 +-
drivers/usb/serial/cp210x.c | 8 +-
drivers/usb/serial/cypress_m8.c | 40 +-
drivers/usb/serial/digi_acceleport.c | 14 +-
drivers/usb/serial/empeg.c | 2 +-
drivers/usb/serial/f81232.c | 3 +-
drivers/usb/serial/ftdi_sio.c | 2 +-
drivers/usb/serial/io_edgeport.c | 12 +-
drivers/usb/serial/io_ti.c | 12 +-
drivers/usb/serial/ir-usb.c | 2 +-
drivers/usb/serial/iuu_phoenix.c | 28 +-
drivers/usb/serial/keyspan.c | 6 +-
drivers/usb/serial/keyspan_pda.c | 4 +-
drivers/usb/serial/kl5kusb105.c | 18 +-
drivers/usb/serial/kobil_sct.c | 14 +-
drivers/usb/serial/mct_u232.c | 4 +-
drivers/usb/serial/metro-usb.c | 8 +-
drivers/usb/serial/mos7720.c | 14 +-
drivers/usb/serial/mos7840.c | 12 +-
drivers/usb/serial/oti6858.c | 10 +-
drivers/usb/serial/pl2303.c | 6 +-
drivers/usb/serial/quatech2.c | 4 +-
drivers/usb/serial/sierra.c | 2 +-
drivers/usb/serial/spcp8x5.c | 12 +-
drivers/usb/serial/ssu100.c | 4 +-
drivers/usb/serial/ti_usb_3410_5052.c | 10 +-
drivers/usb/serial/usb-serial.c | 7 +-
drivers/usb/serial/usb_wwan.c | 2 +-
drivers/usb/serial/whiteheat.c | 2 +-
include/linux/Kbuild | 3 -
include/linux/cd1400.h | 292 -------
include/linux/cdk.h | 486 ------------
include/linux/comstats.h | 119 ---
include/linux/generic_serial.h | 35 -
include/linux/istallion.h | 123 ---
include/linux/kbd_kern.h | 13 -
include/linux/sc26198.h | 533 -------------
include/linux/serial167.h | 157 ----
include/linux/serial_8250.h | 33 +-
include/linux/serial_core.h | 3 +-
include/linux/stallion.h | 147 ----
include/linux/tty.h | 52 +-
include/linux/tty_driver.h | 12 +-
include/net/irda/ircomm_tty.h | 17 +-
net/bluetooth/rfcomm/tty.c | 2 +-
net/irda/ircomm/ircomm_param.c | 5 -
net/irda/ircomm/ircomm_tty.c | 281 ++++---
net/irda/ircomm/ircomm_tty_attach.c | 40 +-
net/irda/ircomm/ircomm_tty_ioctl.c | 33 +-
110 files changed, 2081 insertions(+), 3061 deletions(-)
create mode 100644 Documentation/devicetree/bindings/tty/serial/nxp-lpc32xx-hsuart.txt
create mode 100644 drivers/tty/serial/lpc32xx_hs.c
delete mode 100644 include/linux/cd1400.h
delete mode 100644 include/linux/cdk.h
delete mode 100644 include/linux/comstats.h
delete mode 100644 include/linux/generic_serial.h
delete mode 100644 include/linux/istallion.h
delete mode 100644 include/linux/sc26198.h
delete mode 100644 include/linux/serial167.h
delete mode 100644 include/linux/stallion.h
---------------
Alan Cox (16):
tty: note race we need to fix
tty: localise the lock
usb: fix sillies in the metro USB driver
8250: use the 8250 register interface not the legacy one
8250: propogate the bugs field
8250: add support for ASIX devices with a FIFO bug
tty: revert incorrectly applied lock patch
tty: move the termios object into the tty
f81232: correct stubbed termios handler
usb, kobil: Sort out some bogus tty handling
tty: Fix up PPC fallout from the termios move
8250: three way resolve of the 8250 diffs
vt: fix the keyboard/led locking
tty: Move the handling of the tty release logic
pch_uart: Fix missing break for 16 byte fifo
pcmcia,synclink_cs: fix termios port I missed
Chao Xie (1):
serial: pxa: add spin lock for console write
Christopher Brannon (1):
tty: keyboard.c: Remove locking from vt_get_leds.
Corbin (1):
serial_core: Update buffer overrun statistics.
Dan Carpenter (1):
tty: double unlock on error in ptmx_open()
Darren Hart (1):
pch_uart: Add eg20t_port lock field, avoid recursive spinlocks
Gabor Juhos (1):
tty: of_serial: add no-loopback-test property
Jiri Slaby (23):
TTY: cyclades, add local pointer for card
TTY: ircomm, add tty_port
TTY: ircomm, use close times from tty_port
TTY: ircomm, use open counts from tty_port
TTY: ircomm, use flags from tty_port
TTY: ircomm, revamp locking
TTY: ircomm, use tty from tty_port
TTY: ircomm, define local tty_port
TTY: ircomm, define carrier routines
TTY: ircomm, use tty_port_close_end helper
TTY: ircomm, use tty_port_close_start helper
TTY: um/line, add tty_port
TTY: um/line, use tty from tty_port
PTY: remove one empty ops->remove
PTY: merge pty_install implementations
PTY: add tty_port
TTY: vt, remove con_schedule_flip
TTY: provide drivers with tty_port_install
TTY: vt, add ->install
TTY: usb-serial, use tty_port_install
TTY: centralize fail paths in tty_register_driver
TTY: add ports array to tty_driver
TTY: add tty_port_register_device helper
KeyYoung Park (1):
serial: samsung: protect NULL dereference of clock name
Kyoungil Kim (2):
serial: samsung: Remove NULL checking for baud clock
serial: samsung: Fixed wrong comparison for baudclk_rate
Laurent Pinchart (2):
serial: sh-sci: Fix probe error paths
serial: sh-sci: Make probe fail for ports that exceed the maximum count
Linus Walleij (1):
serial/amba-pl011: fix ages old copy-paste errors
Paul Bolle (2):
delete seven tty headers
Delete generic_serial.h
Rabin Vincent (1):
vt: fix race in vt_waitactive()
Roland Stigge (3):
serial/8250: Add LPC3220 standard UART type
serial/of-serial: Add LPC3220 standard UART compatible string
serial: Add driver for LPC32xx High Speed UARTs
Shachar Shemesh (1):
tty ldisc: Close/Reopen race prevention should check the proper flag
Shawn Bohrer (1):
8250_pci: Remove duplicate struct pciserial_board
Tomoya MORINAGA (2):
pch_uart: Fix rx error interrupt setting issue
pch_uart: Fix parity setting issue
Uwe Kleine-König (1):
serial/imx: make devdata member point to const data
--
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: No big TTY/serial patch merge for 3.6-rc1
From: Jiri Slaby @ 2012-07-26 19:12 UTC (permalink / raw)
To: Greg KH
Cc: Linus Torvalds, Alan Cox, Stephen Rothwell, Andrew Morton,
linux-kernel, linux-serial
In-Reply-To: <20120726190814.GA2194@kroah.com>
On 07/26/2012 09:08 PM, Greg KH wrote:
> Jiri, I know this postpones your patches from being merged, sorry about
> that, but this gives us a few more months to ensure that they are
> working properly :)
Fine with me.
When should I send you 3.7 material I have in my local queue -- now or
after 3.6-rc1 is out as usual?
thanks,
--
js
suse labs
^ permalink raw reply
* Re: No big TTY/serial patch merge for 3.6-rc1
From: Linus Torvalds @ 2012-07-26 19:17 UTC (permalink / raw)
To: Greg KH
Cc: Alan Cox, Jiri Slaby, Stephen Rothwell, Andrew Morton,
linux-kernel, linux-serial
In-Reply-To: <20120726190814.GA2194@kroah.com>
On Thu, Jul 26, 2012 at 12:08 PM, Greg KH <gregkh@linuxfoundation.org> wrote:
>
> I don't really feel comfortable sending you the tty tree at the present
> time to have merged for 3.6-rc1.
Good. This is what I like to see. If it's not ready to be merged,
let's not merge it. I don't think anybody will mind horribly.
Linus
^ permalink raw reply
* Re: No big TTY/serial patch merge for 3.6-rc1
From: Greg KH @ 2012-07-26 19:20 UTC (permalink / raw)
To: Jiri Slaby
Cc: Linus Torvalds, Alan Cox, Stephen Rothwell, Andrew Morton,
linux-kernel, linux-serial
In-Reply-To: <5011969D.5030902@suse.cz>
On Thu, Jul 26, 2012 at 09:12:29PM +0200, Jiri Slaby wrote:
> On 07/26/2012 09:08 PM, Greg KH wrote:
> > Jiri, I know this postpones your patches from being merged, sorry about
> > that, but this gives us a few more months to ensure that they are
> > working properly :)
>
> Fine with me.
>
> When should I send you 3.7 material I have in my local queue -- now or
> after 3.6-rc1 is out as usual?
After 3.6-rc1 is out is good, as I'll just be ignoring any new stuff
until then, with the exception of patches to fix build errors in the
tty-next tree :)
thanks,
greg k-h
^ permalink raw reply
* Re: No big TTY/serial patch merge for 3.6-rc1
From: Jiri Slaby @ 2012-07-26 19:21 UTC (permalink / raw)
To: Greg KH
Cc: Linus Torvalds, Alan Cox, Stephen Rothwell, Andrew Morton,
linux-kernel, linux-serial
In-Reply-To: <20120726192033.GA3488@kroah.com>
On 07/26/2012 09:20 PM, Greg KH wrote:
> I'll just be ignoring any new stuff
> until then, with the exception of patches to fix build errors in the
> tty-next tree :)
Hehe, OK :).
--
js
suse labs
^ permalink raw reply
* Re: No big TTY/serial patch merge for 3.6-rc1
From: Alan Cox @ 2012-07-26 21:23 UTC (permalink / raw)
To: Greg KH
Cc: Linus Torvalds, Jiri Slaby, Stephen Rothwell, Andrew Morton,
linux-kernel, linux-serial
In-Reply-To: <20120726190814.GA2194@kroah.com>
On Thu, 26 Jul 2012 12:08:14 -0700
Greg KH <gregkh@linuxfoundation.org> wrote:
> tty: Move the handling of the tty release logic
Can we lose that one specifically. I've chased down Ian Abbotts problem
and replicated it and that is the offending patch not the lock localise
(which still needs to be kept out as it depends upon this one)
I have it fixed but it's not had enough testing for -rc1 and moving the
termios data has enough spectacular hits all drivers fallout for 3.6
Alan
^ permalink raw reply
* Re: No big TTY/serial patch merge for 3.6-rc1
From: Greg KH @ 2012-07-26 21:28 UTC (permalink / raw)
To: Alan Cox
Cc: Linus Torvalds, Jiri Slaby, Stephen Rothwell, Andrew Morton,
linux-kernel, linux-serial
In-Reply-To: <20120726222313.5993a550@pyramind.ukuu.org.uk>
On Thu, Jul 26, 2012 at 10:23:13PM +0100, Alan Cox wrote:
> On Thu, 26 Jul 2012 12:08:14 -0700
> Greg KH <gregkh@linuxfoundation.org> wrote:
>
> > tty: Move the handling of the tty release logic
>
> Can we lose that one specifically. I've chased down Ian Abbotts problem
> and replicated it and that is the offending patch not the lock localise
> (which still needs to be kept out as it depends upon this one)
>
> I have it fixed but it's not had enough testing for -rc1 and moving the
> termios data has enough spectacular hits all drivers fallout for 3.6
Yes, I can go revert it. I'm working on building up a small tty-linus
branch at the moment for just the "must haves" for 3.6. Right now it's
just 10 patches listed below. Any objection to me sending these after
a round trip through linux-next?
thanks,
greg k-h
.../devicetree/bindings/tty/serial/of-serial.txt | 1 +
drivers/tty/serial/8250/8250.c | 8 +++
drivers/tty/serial/of_serial.c | 1 +
drivers/tty/serial/pch_uart.c | 59 +++++++++++++-------
drivers/tty/serial/samsung.c | 4 +-
drivers/tty/serial/serial_core.c | 6 +-
drivers/tty/tty_ldisc.c | 2 +-
drivers/tty/vt/vt_ioctl.c | 47 +++++++++++-----
include/linux/serial_core.h | 3 +-
9 files changed, 93 insertions(+), 38 deletions(-)
---------------
Alan Cox (1):
pch_uart: Fix missing break for 16 byte fifo
Corbin (1):
serial_core: Update buffer overrun statistics.
Darren Hart (1):
pch_uart: Add eg20t_port lock field, avoid recursive spinlocks
Kyoungil Kim (1):
serial: samsung: Fixed wrong comparison for baudclk_rate
Rabin Vincent (1):
vt: fix race in vt_waitactive()
Roland Stigge (2):
serial/8250: Add LPC3220 standard UART type
serial/of-serial: Add LPC3220 standard UART compatible string
Shachar Shemesh (1):
tty ldisc: Close/Reopen race prevention should check the proper flag
Tomoya MORINAGA (2):
pch_uart: Fix rx error interrupt setting issue
pch_uart: Fix parity setting issue
^ permalink raw reply
* Re: No big TTY/serial patch merge for 3.6-rc1
From: Alan Cox @ 2012-07-26 22:56 UTC (permalink / raw)
To: Greg KH
Cc: Linus Torvalds, Jiri Slaby, Stephen Rothwell, Andrew Morton,
linux-kernel, linux-serial
In-Reply-To: <20120726212807.GA9049@kroah.com>
On Thu, 26 Jul 2012 14:28:07 -0700
Greg KH <gregkh@linuxfoundation.org> wrote:
> On Thu, Jul 26, 2012 at 10:23:13PM +0100, Alan Cox wrote:
> > On Thu, 26 Jul 2012 12:08:14 -0700
> > Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > > tty: Move the handling of the tty release logic
> >
> > Can we lose that one specifically. I've chased down Ian Abbotts problem
> > and replicated it and that is the offending patch not the lock localise
> > (which still needs to be kept out as it depends upon this one)
> >
> > I have it fixed but it's not had enough testing for -rc1 and moving the
> > termios data has enough spectacular hits all drivers fallout for 3.6
>
> Yes, I can go revert it. I'm working on building up a small tty-linus
> branch at the moment for just the "must haves" for 3.6. Right now it's
> just 10 patches listed below. Any objection to me sending these after
> a round trip through linux-next?
All look good to me
^ permalink raw reply
* [PATCH] n_tty: Don't lose characters when PARMRK is enabled
From: Jaeden Amero @ 2012-07-26 22:12 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-serial, linux-kernel, Jaeden Amero
When PARMRK is set and large transfers of characters that will get
marked are being received, n_tty could drop data silently (i.e.
without reporting any error to the client). This is because
characters have the potential to take up to three bytes in the line
discipline (when they get marked with parity or framing errors), but
the amount of free space reported to tty_buffer flush_to_ldisc (via
tty->receive_room) is based on the pre-marked data size.
With this patch, the n_tty layer will no longer assume that each byte
will only take up one byte in the line discipline. Instead, it will
make an overly conservative estimate that each byte will take up
three bytes in the line discipline when PARMRK is set.
Signed-off-by: Jaeden Amero <jaeden.amero@ni.com>
---
drivers/tty/n_tty.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 101790c..5299cda 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -92,10 +92,20 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
static void n_tty_set_room(struct tty_struct *tty)
{
- /* tty->read_cnt is not read locked ? */
- int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+ int left;
int old_left;
+ /* tty->read_cnt is not read locked ? */
+ if (I_PARMRK(tty))
+ {
+ /* Multiply read_cnt by 3, since each byte might take up to
+ * three times as many spaces when PARMRK is set (depending on
+ * its flags, e.g. parity error). */
+ left = N_TTY_BUF_SIZE - tty->read_cnt * 3 - 1;
+ }
+ else
+ left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+
/*
* If we are doing input canonicalization, and there are no
* pending newlines, let characters through without limit, so
--
1.7.11.1
^ permalink raw reply related
* Re: Patch for panic in n_tty_read()
From: Stanislav Kozina @ 2012-07-27 12:05 UTC (permalink / raw)
To: Alan Cox; +Cc: Greg Kroah-Hartman, linux-serial
In-Reply-To: <20120720161123.58fc9703@pyramind.ukuu.org.uk>
[-- Attachment #1: Type: text/plain, Size: 448 bytes --]
Alan,
Thank you, updated fix (tested on bits based on commit
bdc0077af574800d24318b6945cf2344e8dbb050) is attached.
Is this correct now?
Thanks and regards,
-Stanislav Kozina
>> You mean call to tty_put_user(), correct? Thanks for this catch.
>> So what about to unlock the lock for this time? Because we need to hold
>> the lock while checking tty->read_cnt in the while loop condition, correct?
> I think you are right on that yes.
>
> Alan
[-- Attachment #2: tty_panic_2.patch --]
[-- Type: text/plain, Size: 773 bytes --]
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index ee1c268..54d1fc5 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1832,13 +1832,13 @@ do_it_again:
if (tty->icanon && !L_EXTPROC(tty)) {
/* N.B. avoid overrun if nr == 0 */
+ spin_lock_irqsave(&tty->read_lock, flags);
while (nr && tty->read_cnt) {
int eol;
eol = test_and_clear_bit(tty->read_tail,
tty->read_flags);
c = tty->read_buf[tty->read_tail];
- spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = ((tty->read_tail+1) &
(N_TTY_BUF_SIZE-1));
tty->read_cnt--;
@@ -1864,6 +1864,7 @@ do_it_again:
tty_audit_push(tty);
break;
}
+ spin_lock_irqsave(&tty->read_lock, flags);
}
if (retval)
break;
^ permalink raw reply related
* Re: Patch for panic in n_tty_read()
From: Alan Cox @ 2012-07-27 12:50 UTC (permalink / raw)
To: Stanislav Kozina; +Cc: Greg Kroah-Hartman, linux-serial
In-Reply-To: <501283FC.8070409@redhat.com>
On Fri, 27 Jul 2012 14:05:16 +0200
Stanislav Kozina <skozina@redhat.com> wrote:
> Alan,
>
> Thank you, updated fix (tested on bits based on commit
> bdc0077af574800d24318b6945cf2344e8dbb050) is attached.
> Is this correct now?
Looks good to me. However it changes the locking rules on
tty_audit_push() so please check the audit folks are ok with it. I don't
think that causes any problems.
Alan
^ permalink raw reply
* Re: [PATCH] n_tty: Don't lose characters when PARMRK is enabled
From: Alan Cox @ 2012-07-27 13:09 UTC (permalink / raw)
To: Jaeden Amero; +Cc: Greg Kroah-Hartman, linux-serial, linux-kernel
In-Reply-To: <1343340751-1942-1-git-send-email-jaeden.amero@ni.com>
On Thu, 26 Jul 2012 17:12:31 -0500
Jaeden Amero <jaeden.amero@ni.com> wrote:
> When PARMRK is set and large transfers of characters that will get
> marked are being received, n_tty could drop data silently (i.e.
> without reporting any error to the client). This is because
> characters have the potential to take up to three bytes in the line
> discipline (when they get marked with parity or framing errors), but
> the amount of free space reported to tty_buffer flush_to_ldisc (via
> tty->receive_room) is based on the pre-marked data size.
>
> With this patch, the n_tty layer will no longer assume that each byte
> will only take up one byte in the line discipline. Instead, it will
> make an overly conservative estimate that each byte will take up
> three bytes in the line discipline when PARMRK is set.
>
> Signed-off-by: Jaeden Amero <jaeden.amero@ni.com>
What a fun corner case. Patch looks good to me.
Acked-by: Alan Cox <alan@linux.intel.com>
^ permalink raw reply
* [PATCH] n_tty: Don't lose characters when PARMRK is enabled
From: Jaeden Amero @ 2012-07-27 13:43 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-kernel, linux-serial, Jaeden Amero
In-Reply-To: <1343340751-1942-1-git-send-email-jaeden.amero@ni.com>
When PARMRK is set and large transfers of characters that will get
marked are being received, n_tty could drop data silently (i.e.
without reporting any error to the client). This is because
characters have the potential to take up to three bytes in the line
discipline (when they get marked with parity or framing errors), but
the amount of free space reported to tty_buffer flush_to_ldisc (via
tty->receive_room) is based on the pre-marked data size.
With this patch, the n_tty layer will no longer assume that each byte
will only take up one byte in the line discipline. Instead, it will
make an overly conservative estimate that each byte will take up
three bytes in the line discipline when PARMRK is set.
Signed-off-by: Jaeden Amero <jaeden.amero@ni.com>
---
drivers/tty/n_tty.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 101790cea4ae45622c0628bf1833012087f9c7c5..e2473cf26d058d1de7059323fbe7a8f29fe0f74e 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -92,10 +92,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
static void n_tty_set_room(struct tty_struct *tty)
{
- /* tty->read_cnt is not read locked ? */
- int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+ int left;
int old_left;
+ /* tty->read_cnt is not read locked ? */
+ if (I_PARMRK(tty)) {
+ /* Multiply read_cnt by 3, since each byte might take up to
+ * three times as many spaces when PARMRK is set (depending on
+ * its flags, e.g. parity error). */
+ left = N_TTY_BUF_SIZE - tty->read_cnt * 3 - 1;
+ } else
+ left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+
/*
* If we are doing input canonicalization, and there are no
* pending newlines, let characters through without limit, so
--
1.7.11.1
^ permalink raw reply related
* [PATCH] serial: The new version of the driver MAX3107.
From: Alexander Shiyan @ 2012-07-27 15:01 UTC (permalink / raw)
To: linux-serial; +Cc: Alan Cox, Greg Kroah-Hartman, Alexander Shiyan
This is NOT a patch. Only for initial review and comment. So, do NOT apply.
Since the new version is very different from the old one I'm posting it as
a new driver to simplify the code view.
New features:
- Using the regmap. This makes it easy to add support for the driver
via I2C. Register cache removes unnecessary reading that improves
performance. I tested the driver on a slow processor with a very slow
SPI bus, so the speed was an important factor when writing code.
- Using devm_XXX-related functions.
- The use of threaded IRQ with IRQF_ONESHOT flag allows the driver to
the hardware that supports only level IRQ.
- Proper serial error handling.
- Proper treatment of FIFO queues.
- Advanced flags allows turn on RS-485 mode (Auto direction control).
- Ability to load multiple drivers MAX3107.
- Header moved to include/linux/platform_data.
- Cleanup, cleanup, cleanup...
So waiting for comments.
Thanks!
---
drivers/tty/serial/Kconfig | 7 +-
drivers/tty/serial/max3107.c | 1087 +++++++++++++++++++++++++++++++++
include/linux/platform_data/max3107.h | 56 ++
3 files changed, 1149 insertions(+), 1 deletions(-)
create mode 100644 drivers/tty/serial/max3107.c
create mode 100644 include/linux/platform_data/max3107.h
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 070b442..d2bbfd11 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -254,8 +254,13 @@ config SERIAL_MAX3100
tristate "MAX3100 support"
depends on SPI
select SERIAL_CORE
+ select REGMAP_SPI if SPI
help
- MAX3100 chip support
+ The MAX3107 is an advanced universal asynchronous receiver-transmitter
+ with 128 words each of receive and transmit first-in/first-out (FIFO)
+ that can be controlled through I2C or high-speed SPI.
+
+ Say Y here if you want to support this chip.
config SERIAL_MAX3107
tristate "MAX3107 support"
diff --git a/drivers/tty/serial/max3107.c b/drivers/tty/serial/max3107.c
new file mode 100644
index 0000000..c7ea7d0
--- /dev/null
+++ b/drivers/tty/serial/max3107.c
@@ -0,0 +1,1087 @@
+/*
+ * SPI UART protocol driver for Maxim (Dallas) MAX3107
+ *
+ * Based on max3100.c
+ * by Christian Pellegrin <chripell@evolware.org>
+ * and max3110.c
+ * by Feng Tang <feng.tang@intel.com>
+ *
+ * Rewritten by Alexander Shiyan <shc_work@mail.ru>
+ *
+ * Copyright (C) Aavamobile 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/* TODO: GPIO handling */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include <linux/platform_data/max3107.h>
+
+#define MAX3107_MAJOR 204
+#define MAX3107_MINOR 209
+
+/* Chip wakeup delay */
+#define MAX3107_WAKEUP_DELAY 50
+
+/* Sleep mode definitions */
+#define MAX3107_DISABLE_FORCED_SLEEP 0
+#define MAX3107_ENABLE_FORCED_SLEEP 1
+#define MAX3107_DISABLE_AUTOSLEEP 2
+#define MAX3107_ENABLE_AUTOSLEEP 3
+
+/* MAX3107 register definitions */
+#define MAX3107_RHR_REG (0x00) /* RX FIFO */
+#define MAX3107_THR_REG (0x00) /* TX FIFO */
+#define MAX3107_IRQEN_REG (0x01) /* IRQ enable */
+#define MAX3107_IRQSTS_REG (0x02) /* IRQ status */
+#define MAX3107_LSR_IRQEN_REG (0x03) /* LSR IRQ enable */
+#define MAX3107_LSR_IRQSTS_REG (0x04) /* LSR IRQ status */
+#define MAX3107_SPCHR_IRQEN_REG (0x05) /* Special char IRQ enable */
+#define MAX3107_SPCHR_IRQSTS_REG (0x06) /* Special char IRQ status */
+#define MAX3107_STS_IRQEN_REG (0x07) /* Status IRQ enable */
+#define MAX3107_STS_IRQSTS_REG (0x08) /* Status IRQ status */
+#define MAX3107_MODE1_REG (0x09) /* MODE1 */
+#define MAX3107_MODE2_REG (0x0a) /* MODE2 */
+#define MAX3107_LCR_REG (0x0b) /* LCR */
+#define MAX3107_RXTO_REG (0x0c) /* RX timeout */
+#define MAX3107_HDPIXDELAY_REG (0x0d) /* Auto transceiver delays */
+#define MAX3107_IRDA_REG (0x0e) /* IRDA settings */
+#define MAX3107_FLOWLVL_REG (0x0f) /* Flow control levels */
+#define MAX3107_FIFOTRIGLVL_REG (0x10) /* FIFO IRQ trigger levels */
+#define MAX3107_TXFIFOLVL_REG (0x11) /* TX FIFO level */
+#define MAX3107_RXFIFOLVL_REG (0x12) /* RX FIFO level */
+#define MAX3107_FLOWCTRL_REG (0x13) /* Flow control */
+#define MAX3107_XON1_REG (0x14) /* XON1 character */
+#define MAX3107_XON2_REG (0x15) /* XON2 character */
+#define MAX3107_XOFF1_REG (0x16) /* XOFF1 character */
+#define MAX3107_XOFF2_REG (0x17) /* XOFF2 character */
+#define MAX3107_GPIOCFG_REG (0x18) /* GPIO config */
+#define MAX3107_GPIODATA_REG (0x19) /* GPIO data */
+#define MAX3107_PLLCFG_REG (0x1a) /* PLL config */
+#define MAX3107_BRGCFG_REG (0x1b) /* Baud rate generator conf */
+#define MAX3107_BRGDIVLSB_REG (0x1c) /* Baud rate divisor LSB */
+#define MAX3107_BRGDIVMSB_REG (0x1d) /* Baud rate divisor MSB */
+#define MAX3107_CLKSRC_REG (0x1e) /* Clock source */
+#define MAX3107_REVID_REG (0x1f) /* Revision identification */
+
+/* IRQ register bits */
+#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */
+#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */
+#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */
+#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */
+#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */
+#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */
+#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */
+#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */
+
+/* LSR register bits */
+#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */
+#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */
+#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */
+#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */
+#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */
+#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */
+#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */
+
+/* Special character register bits */
+#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */
+#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */
+#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */
+#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */
+#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */
+#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */
+
+/* Status register bits */
+#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */
+#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */
+#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */
+#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */
+#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */
+#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */
+
+/* MODE1 register bits */
+#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */
+#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */
+#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */
+#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */
+#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */
+#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */
+#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */
+#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */
+
+/* MODE2 register bits */
+#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */
+#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */
+#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */
+#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */
+#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */
+#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */
+#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */
+#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */
+
+/* LCR register bits */
+#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */
+#define MAX3107_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1
+ *
+ * Word length bits table:
+ * 00 -> 5 bit words
+ * 01 -> 6 bit words
+ * 10 -> 7 bit words
+ * 11 -> 8 bit words
+ */
+#define MAX3107_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit
+ *
+ * STOP length bit table:
+ * 0 -> 1 stop bit
+ * 1 -> 1-1.5 stop bits if
+ * word length is 5,
+ * 2 stop bits otherwise
+ */
+#define MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */
+#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */
+#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */
+#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */
+#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */
+#define MAX3107_LCR_WORD_LEN_5 (0x00)
+#define MAX3107_LCR_WORD_LEN_6 (0x01)
+#define MAX3107_LCR_WORD_LEN_7 (0x02)
+#define MAX3107_LCR_WORD_LEN_8 (0x03)
+
+/* IRDA register bits */
+#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */
+#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */
+#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */
+#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */
+#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */
+#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */
+
+/* Flow control trigger level register masks */
+#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */
+#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */
+#define MAX3107_FLOWLVL_HALT(words) ((words / 8) & 0x0f)
+#define MAX3107_FLOWLVL_RES(words) (((words / 8) & 0x0f) << 4)
+
+/* FIFO interrupt trigger level register masks */
+#define MAX3107_FIFOTRIGLVL_TX_MASK (0x0f) /* TX FIFO trigger level */
+#define MAX3107_FIFOTRIGLVL_RX_MASK (0xf0) /* RX FIFO trigger level */
+#define MAX3107_FIFOTRIGLVL_TX(words) ((words / 8) & 0x0f)
+#define MAX3107_FIFOTRIGLVL_RX(words) (((words / 8) & 0x0f) << 4)
+
+/* Flow control register bits */
+#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */
+#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */
+#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs
+ * are used in conjunction with
+ * XOFF2 for definition of
+ * special character */
+#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */
+#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */
+#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1
+ *
+ * SWFLOW bits 1 & 0 table:
+ * 00 -> no transmitter flow
+ * control
+ * 01 -> receiver compares
+ * XON2 and XOFF2
+ * and controls
+ * transmitter
+ * 10 -> receiver compares
+ * XON1 and XOFF1
+ * and controls
+ * transmitter
+ * 11 -> receiver compares
+ * XON1, XON2, XOFF1 and
+ * XOFF2 and controls
+ * transmitter
+ */
+#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */
+#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3
+ *
+ * SWFLOW bits 3 & 2 table:
+ * 00 -> no received flow
+ * control
+ * 01 -> transmitter generates
+ * XON2 and XOFF2
+ * 10 -> transmitter generates
+ * XON1 and XOFF1
+ * 11 -> transmitter generates
+ * XON1, XON2, XOFF1 and
+ * XOFF2
+ */
+
+/* GPIO configuration register bits */
+#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */
+#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */
+#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */
+#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */
+#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */
+#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */
+#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */
+#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */
+
+/* GPIO DATA register bits */
+#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */
+#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */
+#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */
+#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */
+#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */
+#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */
+#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */
+#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */
+
+/* PLL configuration register masks */
+#define MAX3107_PLLCFG_PREDIV_MASK (0x3f) /* PLL predivision value */
+#define MAX3107_PLLCFG_PLLFACTOR_MASK (0xc0) /* PLL multiplication factor */
+
+/* Baud rate generator configuration register masks and bits */
+#define MAX3107_BRGCFG_FRACT_MASK (0x0f) /* Fractional portion of
+ * Baud rate generator divisor
+ */
+#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */
+#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */
+
+/* Clock source register bits */
+#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */
+#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */
+#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */
+#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */
+#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */
+
+/* Misc definitions */
+#define MAX3107_FIFO_SIZE (128)
+#define MAX3107_REV_ID (0xa0)
+#define MAX3107_REV_MASK (0xfe)
+
+/* Error status definitions */
+#define MAX3107_ERR_PARITY MAX3107_LSR_RXPAR_BIT
+#define MAX3107_ERR_FRAME MAX3107_LSR_FRERR_BIT
+#define MAX3107_ERR_OVERRUN MAX3107_LSR_RXOVR_BIT
+#define MAX3107_ERR_BREAK MAX3107_LSR_RXBRK_BIT
+
+/* IRQ status bits definitions */
+#define MAX3107_IRQ_TX (MAX3107_IRQ_TXFIFO_BIT | MAX3107_IRQ_TXEMPTY_BIT)
+#define MAX3107_IRQ_RX (MAX3107_IRQ_RXFIFO_BIT | MAX3107_IRQ_RXEMPTY_BIT)
+
+struct max3107_port {
+ struct uart_port port;
+
+ struct regmap *regmap;
+
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+
+ struct mutex max3107_mutex;
+
+ int suspended;
+
+ struct max3107_plat *pdata;
+};
+
+/* Baudrate table for 13 MHz external clock */
+const struct baud_table brg_table_13m[] = {
+ { 300, 0x0a9400 | 0x05 },
+ { 600, 0x054a00 | 0x03 },
+ { 1200, 0x02a500 | 0x01 },
+ { 2400, 0x015200 | 0x09 },
+ { 4800, 0x00a900 | 0x04 },
+ { 9600, 0x005400 | 0x0a },
+ { 19200, 0x002a00 | 0x05 },
+ { 38400, 0x001500 | 0x03 },
+ { 57600, 0x000e00 | 0x02 },
+ { 115200, 0x000700 | 0x01 },
+ { 230400, 0x000300 | 0x08 },
+ { 460800, 0x000100 | 0x0c },
+ { 921600, 0x000100 | 0x1c },
+ { 0, 0 }
+};
+EXPORT_SYMBOL_GPL(brg_table_13m);
+
+/* Baudrate table for 26 MHz external clock */
+const struct baud_table brg_table_26m[] = {
+ { 300, 0x152800 | 0x0a },
+ { 600, 0x0a9400 | 0x05 },
+ { 1200, 0x054a00 | 0x03 },
+ { 2400, 0x02a500 | 0x01 },
+ { 4800, 0x015200 | 0x09 },
+ { 9600, 0x00a900 | 0x04 },
+ { 19200, 0x005400 | 0x0a },
+ { 38400, 0x002a00 | 0x05 },
+ { 57600, 0x001c00 | 0x03 },
+ { 115200, 0x000e00 | 0x02 },
+ { 230400, 0x000700 | 0x01 },
+ { 460800, 0x000300 | 0x08 },
+ { 921600, 0x000100 | 0x0c },
+ { 0, 0 }
+};
+EXPORT_SYMBOL_GPL(brg_table_26m);
+
+/* Baudrate table for 3.6864 MHz external clock */
+const struct baud_table brg_table_3_6864m[] = {
+ { 300, 0x030000 | 0x00 },
+ { 600, 0x018000 | 0x00 },
+ { 1200, 0x00c000 | 0x00 },
+ { 2400, 0x006000 | 0x00 },
+ { 4800, 0x003000 | 0x00 },
+ { 9600, 0x001800 | 0x00 },
+ { 19200, 0x000c00 | 0x00 },
+ { 38400, 0x000600 | 0x00 },
+ { 57600, 0x000400 | 0x00 },
+ { 115200, 0x000200 | 0x00 },
+ { 230400, 0x000100 | 0x00 },
+ { 460800, 0x000100 | 0x10 },
+ { 921600, 0x000100 | 0x20 },
+ { 0, 0 }
+};
+EXPORT_SYMBOL_GPL(brg_table_3_6864m);
+
+static bool max3107_reg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX3107_IRQSTS_REG:
+ case MAX3107_LSR_IRQSTS_REG:
+ case MAX3107_SPCHR_IRQSTS_REG:
+ case MAX3107_STS_IRQSTS_REG:
+ case MAX3107_TXFIFOLVL_REG:
+ case MAX3107_RXFIFOLVL_REG:
+ case MAX3107_REVID_REG:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static bool max3107_reg_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX3107_RHR_REG:
+ case MAX3107_IRQSTS_REG:
+ case MAX3107_LSR_IRQSTS_REG:
+ case MAX3107_SPCHR_IRQSTS_REG:
+ case MAX3107_STS_IRQSTS_REG:
+ case MAX3107_TXFIFOLVL_REG:
+ case MAX3107_RXFIFOLVL_REG:
+ case MAX3107_GPIODATA_REG:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool max3107_reg_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX3107_RHR_REG:
+ case MAX3107_IRQSTS_REG:
+ case MAX3107_SPCHR_IRQSTS_REG:
+ case MAX3107_STS_IRQSTS_REG:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static struct regmap_config max3107_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x1f,
+ .read_flag_mask = 0x00,
+ .write_flag_mask= 0x80,
+ .cache_type = REGCACHE_RBTREE,
+ .writeable_reg = max3107_reg_writeable,
+ .volatile_reg = max3107_reg_volatile,
+ .precious_reg = max3107_reg_precious,
+};
+
+static u32 max3107_get_brg(int baud, struct max3107_port *s)
+{
+ int i;
+ const struct baud_table *baud_tbl = s->pdata->baud_tbl;
+
+ for (i = 0; baud_tbl[i].baud; i++) {
+ if (baud == baud_tbl[i].baud)
+ return baud_tbl[i].brg;
+ }
+
+ return 0;
+}
+
+static void max3107_handle_rx(struct max3107_port *s, unsigned int rxlen)
+{
+ unsigned int status, ch, flag;
+ struct tty_struct *tty = s->port.state->port.tty;
+
+ if (rxlen >= MAX3107_FIFO_SIZE) {
+ dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen);
+ /* Ensure sanity of RX level */
+ rxlen = MAX3107_FIFO_SIZE;
+ }
+
+ dev_dbg(s->port.dev, "RX Len = %u\n", rxlen);
+
+ while (rxlen--) {
+ regmap_read(s->regmap, MAX3107_RHR_REG, &ch);
+ regmap_read(s->regmap, MAX3107_LSR_IRQSTS_REG, &status);
+
+ status &= MAX3107_ERR_PARITY | MAX3107_ERR_FRAME |
+ MAX3107_ERR_OVERRUN | MAX3107_ERR_BREAK;
+
+ s->port.icount.rx++;
+ flag = TTY_NORMAL;
+
+ if (unlikely(status)) {
+ if (status & MAX3107_ERR_BREAK) {
+ s->port.icount.brk++;
+ if (uart_handle_break(&s->port))
+ continue;
+ } else if (status & MAX3107_ERR_PARITY)
+ s->port.icount.parity++;
+ else if (status & MAX3107_ERR_FRAME)
+ s->port.icount.frame++;
+ else if (status & MAX3107_ERR_OVERRUN)
+ s->port.icount.overrun++;
+
+ status &= s->port.read_status_mask;
+ if (status & MAX3107_ERR_BREAK)
+ flag = TTY_BREAK;
+ else if (status & MAX3107_ERR_PARITY)
+ flag = TTY_PARITY;
+ else if (status & MAX3107_ERR_FRAME)
+ flag = TTY_FRAME;
+ else if (status & MAX3107_ERR_OVERRUN)
+ flag = TTY_OVERRUN;
+ }
+
+ if (uart_handle_sysrq_char(s->port, ch))
+ continue;
+
+ if (status & s->port.ignore_status_mask)
+ continue;
+
+ uart_insert_char(&s->port, status, MAX3107_ERR_OVERRUN, ch, flag);
+ }
+
+ tty_flip_buffer_push(tty);
+}
+
+static void max3107_handle_tx(struct max3107_port *s)
+{
+ struct circ_buf *xmit = &s->port.state->xmit;
+ unsigned int txlen, to_send;
+
+ if (s->port.x_char) {
+ regmap_write(s->regmap, MAX3107_THR_REG, s->port.x_char);
+ s->port.icount.tx++;
+ s->port.x_char = 0;
+ return;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port))
+ return;
+
+ /* Get length of data pending in circular buffer */
+ to_send = uart_circ_chars_pending(xmit);
+ if (to_send) {
+ /* Limit to size of TX FIFO */
+ regmap_read(s->regmap, MAX3107_TXFIFOLVL_REG, &txlen);
+ txlen = MAX3107_FIFO_SIZE - txlen;
+ to_send = (to_send > txlen) ? txlen : to_send;
+
+ dev_dbg(s->port.dev, "TX Len = %u\n", to_send);
+
+ /* Add data to send */
+ s->port.icount.tx += to_send;
+ do {
+ regmap_write(s->regmap, MAX3107_THR_REG, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ } while (--to_send > 0);
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&s->port);
+}
+
+static irqreturn_t max3107_ist(int irq, void *dev_id)
+{
+ struct max3107_port *s = (struct max3107_port*)dev_id;
+ unsigned int ists, lsr, rxlen;
+
+ mutex_lock(&s->max3107_mutex);
+
+ for (;;) {
+ /* Read IRQ status & RX FIFO level */
+ regmap_read(s->regmap, MAX3107_IRQSTS_REG, &ists);
+ regmap_read(s->regmap, MAX3107_RXFIFOLVL_REG, &rxlen);
+ if (!ists && !(lsr & MAX3107_LSR_RXTO_BIT) && !rxlen)
+ break;
+
+ dev_dbg(s->port.dev, "IRQ status: 0x%02x\n", ists);
+
+ if (rxlen)
+ max3107_handle_rx(s, rxlen);
+ if (ists & MAX3107_IRQ_TX)
+ max3107_handle_tx(s);
+ if (ists & MAX3107_IRQ_CTS_BIT) {
+ regmap_read(s->regmap, MAX3107_LSR_IRQSTS_REG, &lsr);
+ uart_handle_cts_change(&s->port,
+ !!(lsr & MAX3107_LSR_CTS_BIT));
+ }
+ }
+
+ mutex_unlock(&s->max3107_mutex);
+
+ return IRQ_HANDLED;
+}
+
+static void max3107_wq_proc(struct work_struct *ws)
+{
+ struct max3107_port *s = container_of(ws, struct max3107_port, tx_work);
+
+ mutex_lock(&s->max3107_mutex);
+
+ max3107_handle_tx(s);
+
+ mutex_unlock(&s->max3107_mutex);
+}
+
+static void max3107_enable_ms(struct uart_port *port)
+{
+ /* Modem status not supported */
+}
+
+static void max3107_start_tx(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ queue_work(s->wq, &s->tx_work);
+}
+
+static void max3107_stop_tx(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static void max3107_stop_rx(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static unsigned int max3107_tx_empty(struct uart_port *port)
+{
+ unsigned int val;
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ mutex_lock(&s->max3107_mutex);
+ regmap_read(s->regmap, MAX3107_TXFIFOLVL_REG, &val);
+ mutex_unlock(&s->max3107_mutex);
+
+ return val ? 0: TIOCSER_TEMT;
+}
+
+static unsigned int max3107_get_mctrl(struct uart_port *port)
+{
+ /* DCD and DSR are not wired and CTS/RTS is handled automatically
+ * so just indicate DSR and CAR asserted
+ */
+ return TIOCM_DSR | TIOCM_CAR;
+}
+
+static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* DCD and DSR are not wired and CTS/RTS is hadnled automatically
+ * so do nothing
+ */
+}
+
+static void max3107_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+ struct tty_struct *tty;
+ int baud;
+ u16 lcr;
+ u32 brg = 0;
+
+ if (!port->state)
+ return;
+
+ tty = port->state->port.tty;
+ if (!tty)
+ return;
+
+ /* Word size */
+ if ((termios->c_cflag & CSIZE) == CS7)
+ lcr = MAX3107_LCR_WORD_LEN_7;
+ else
+ lcr = MAX3107_LCR_WORD_LEN_8;
+
+ /* Parity */
+ if (termios->c_cflag & PARENB) {
+ lcr |= MAX3107_LCR_PARITY_BIT;
+ if (!(termios->c_cflag & PARODD))
+ lcr |= MAX3107_LCR_EVENPARITY_BIT;
+ }
+
+ /* Stop bits */
+ if (termios->c_cflag & CSTOPB)
+ lcr |= MAX3107_LCR_STOPLEN_BIT; /* 2 stops */
+
+ /* Mask termios capabilities we don't support */
+ termios->c_cflag &= ~CMSPAR;
+
+ /* Set read status mask */
+ s->port.read_status_mask = MAX3107_ERR_OVERRUN;
+ if (termios->c_iflag & INPCK)
+ s->port.read_status_mask |= MAX3107_ERR_PARITY | MAX3107_ERR_FRAME;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ s->port.read_status_mask |= MAX3107_ERR_BREAK;
+
+ /* Set status ignore mask */
+ s->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNBRK)
+ s->port.ignore_status_mask |= MAX3107_ERR_BREAK;
+ if (!(termios->c_cflag & CREAD))
+ s->port.ignore_status_mask |= MAX3107_ERR_PARITY |
+ MAX3107_ERR_OVERRUN |
+ MAX3107_ERR_FRAME |
+ MAX3107_ERR_BREAK;
+
+ /* Get new baud rate generator configuration */
+ baud = tty_get_baud_rate(tty);
+
+ brg = max3107_get_brg(baud, s);
+ tty_termios_encode_baud_rate(termios, baud, baud);
+
+ /* Update timeout according to new baud rate */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ mutex_lock(&s->max3107_mutex);
+
+ regmap_write(s->regmap, MAX3107_LCR_REG, lcr);
+ regmap_write(s->regmap, MAX3107_BRGDIVMSB_REG, brg >> 16);
+ regmap_write(s->regmap, MAX3107_BRGDIVLSB_REG, brg >> 8);
+ regmap_write(s->regmap, MAX3107_BRGCFG_REG, brg);
+
+ mutex_unlock(&s->max3107_mutex);
+}
+
+static void max3107_register_init(struct max3107_port *s)
+{
+ unsigned int val;
+
+ /* Configure baud rate, 9600 as default */
+ val = max3107_get_brg(9600, s);
+
+ regmap_write(s->regmap, MAX3107_BRGDIVMSB_REG, val >> 16);
+ regmap_write(s->regmap, MAX3107_BRGDIVLSB_REG, val >> 8);
+ regmap_write(s->regmap, MAX3107_BRGCFG_REG, val);
+
+ /* Configure LCR register, 8N1 mode by default */
+ val = MAX3107_LCR_WORD_LEN_8;
+ regmap_write(s->regmap, MAX3107_LCR_REG, val);
+
+ /* Configure MODE1 register */
+ val = MAX3107_MODE1_IRQSEL_BIT; /* Enable IRQ pin */
+ if (s->pdata->flags & MAX3107_AUTO_DIR_CTRL)
+ val |= MAX3107_MODE1_TRNSCVCTRL_BIT;
+ regmap_write(s->regmap, MAX3107_MODE1_REG, val);
+
+ /* Configure MODE2 register */
+ val = MAX3107_MODE2_RXEMPTINV_BIT;
+ if (s->pdata->flags & MAX3107_LOOPBACK)
+ val |= MAX3107_MODE2_LOOPBACK_BIT;
+ if (s->pdata->flags & MAX3107_ECHO_SUPRESS)
+ val |= MAX3107_MODE2_ECHOSUPR_BIT;
+
+ /* Reset FIFOs */
+ val |= MAX3107_MODE2_FIFORST_BIT;
+ regmap_write(s->regmap, MAX3107_MODE2_REG, val);
+
+ /* Configure FIFO trigger level register */
+ /* RX FIFO trigger for 16 words, TX FIFO trigger for 64 words */
+ val = MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(64);
+ regmap_write(s->regmap, MAX3107_FIFOTRIGLVL_REG, val);
+
+ /* Configure flow control levels */
+ /* Flow control halt level 96, resume level 48 */
+ val = MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96);
+ regmap_write(s->regmap, MAX3107_FLOWLVL_REG, val);
+
+ /* Configure flow control */
+ val = (s->pdata->flags & MAX3107_AUTOCTS) ? MAX3107_FLOWCTRL_AUTOCTS_BIT : 0;
+ val |= (s->pdata->flags & MAX3107_AUTORTS) ? MAX3107_FLOWCTRL_AUTORTS_BIT : 0;
+ regmap_write(s->regmap, MAX3107_FLOWCTRL_REG, val);
+
+ /* Clear timeout register */
+ regmap_write(s->regmap, MAX3107_RXTO_REG, 0);
+
+ /* Configure LSR interrupt enable register */
+ /* Enable RX timeout interrupt */
+ val = MAX3107_LSR_RXTO_BIT;
+ regmap_write(s->regmap, MAX3107_LSR_IRQEN_REG, val);
+
+ /* Clear FIFO reset */
+ regmap_update_bits(s->regmap, MAX3107_MODE2_REG,
+ MAX3107_MODE2_FIFORST_BIT, 0);
+
+ /* Clear IRQ status register by reading it */
+ regmap_read(s->regmap, MAX3107_IRQSTS_REG, &val);
+
+ /* Configure interrupt enable register */
+ /* Enable CTS change interrupt */
+ val = MAX3107_IRQ_CTS_BIT;
+ /* Enable RX, TX interrupts */
+ val |= MAX3107_IRQ_RX | MAX3107_IRQ_TX;
+ regmap_write(s->regmap, MAX3107_IRQEN_REG, val);
+}
+
+static int max3107_startup(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ if (s->pdata->suspend)
+ s->pdata->suspend(s, 0);
+
+ /* Init registers */
+ max3107_register_init(s);
+
+ /* Setup interrupt */
+ if (devm_request_threaded_irq(port->dev, port->irq, NULL, max3107_ist,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max3107", s)) {
+ dev_err(port->dev, "Unable to reguest IRQ %i\n", port->irq);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void max3107_shutdown(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ if (s->suspended && s->pdata->suspend)
+ s->pdata->suspend(s, 0);
+
+ /* Disable all interrupts */
+ mutex_lock(&s->max3107_mutex);
+ regmap_write(s->regmap, MAX3107_IRQEN_REG, 0);
+ mutex_unlock(&s->max3107_mutex);
+
+ /* Free the interrupt */
+ devm_free_irq(port->dev, port->irq, s);
+
+ if (s->pdata->suspend)
+ s->pdata->suspend(s, 1);
+}
+
+static const char *max3107_type(struct uart_port *port)
+{
+ return (port->type == PORT_MAX3107) ? "MAX3107" : NULL;
+}
+
+static void max3107_release_port(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static int max3107_request_port(struct uart_port *port)
+{
+ /* Do nothing */
+ return 0;
+}
+
+static void max3107_config_port(struct uart_port *port, int flags)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ s->port.type = PORT_MAX3107;
+}
+
+static int max3107_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX3107))
+ return 0;
+
+ return -EINVAL;
+}
+
+static void max3107_break_ctl(struct uart_port *port, int break_state)
+{
+ /* We don't support break control, do nothing */
+}
+
+static struct uart_ops max3107_ops = {
+ .tx_empty = max3107_tx_empty,
+ .set_mctrl = max3107_set_mctrl,
+ .get_mctrl = max3107_get_mctrl,
+ .stop_tx = max3107_stop_tx,
+ .start_tx = max3107_start_tx,
+ .stop_rx = max3107_stop_rx,
+ .enable_ms = max3107_enable_ms,
+ .break_ctl = max3107_break_ctl,
+ .startup = max3107_startup,
+ .shutdown = max3107_shutdown,
+ .set_termios = max3107_set_termios,
+ .type = max3107_type,
+ .request_port = max3107_request_port,
+ .release_port = max3107_release_port,
+ .config_port = max3107_config_port,
+ .verify_port = max3107_verify_port,
+};
+
+static struct uart_driver max3107_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "ttyMAX",
+ .dev_name = "ttyMAX",
+ .major = MAX3107_MAJOR,
+ .minor = MAX3107_MINOR,
+ .nr = 1,
+ .cons = NULL,
+};
+
+static volatile int driver_registered = 0;
+
+static void max3107_set_sleep(struct max3107_port *s, int mode)
+{
+ unsigned int val;
+
+ mutex_lock(&s->max3107_mutex);
+
+ regmap_read(s->regmap, MAX3107_MODE1_REG, &val);
+
+ switch (mode) {
+ case MAX3107_DISABLE_FORCED_SLEEP:
+ val &= ~MAX3107_MODE1_FORCESLEEP_BIT;
+ break;
+ case MAX3107_ENABLE_FORCED_SLEEP:
+ val |= MAX3107_MODE1_FORCESLEEP_BIT;
+ break;
+ case MAX3107_DISABLE_AUTOSLEEP:
+ val &= ~MAX3107_MODE1_AUTOSLEEP_BIT;
+ break;
+ case MAX3107_ENABLE_AUTOSLEEP:
+ val |= MAX3107_MODE1_AUTOSLEEP_BIT;
+ break;
+ default:
+ dev_warn(s->port.dev, "Invalid sleep mode %i\n", mode);
+ mutex_unlock(&s->max3107_mutex);
+ return;
+ }
+
+ regmap_write(s->regmap, MAX3107_MODE1_REG, val);
+
+ if ((mode == MAX3107_DISABLE_AUTOSLEEP) ||
+ (mode == MAX3107_DISABLE_FORCED_SLEEP))
+ msleep(MAX3107_WAKEUP_DELAY);
+
+ mutex_unlock(&s->max3107_mutex);
+}
+
+static int max3107_suspend(struct spi_device *spi, pm_message_t state)
+{
+ int ret;
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ dev_dbg(&spi->dev, "Suspend\n");
+
+ ret = uart_suspend_port(&max3107_uart_driver, &s->port);
+
+ if (s->pdata->suspend)
+ s->pdata->suspend(s, 1);
+
+ return ret;
+}
+
+static int max3107_resume(struct spi_device *spi)
+{
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ dev_dbg(&spi->dev, "Resume\n");
+
+ if (s->pdata->suspend)
+ s->pdata->suspend(s, 0);
+
+ return uart_resume_port(&max3107_uart_driver, &s->port);
+}
+
+void max3107_default_suspend(struct max3107_port *s, int do_suspend)
+{
+ dev_dbg(s->port.dev, "Change suspend state to %i\n", do_suspend);
+
+ s->suspended = do_suspend;
+ max3107_set_sleep(s, do_suspend ? MAX3107_ENABLE_AUTOSLEEP :
+ MAX3107_DISABLE_AUTOSLEEP);
+}
+EXPORT_SYMBOL_GPL(max3107_default_suspend);
+
+/* Generic platform data */
+static struct max3107_plat generic_plat_data = {
+ .flags = MAX3107_EXT_CLK | MAX3107_AUTOCTS | MAX3107_AUTORTS,
+ .baud_tbl = brg_table_26m,
+ .suspend = &max3107_default_suspend,
+};
+
+static int __devinit max3107_probe(struct spi_device *spi)
+{
+ struct max3107_port *s;
+ struct max3107_plat *pdata = spi->dev.platform_data;
+ int ret;
+ unsigned int val;
+
+ /* Check for IRQ */
+ if (spi->irq <= 0) {
+ dev_err(&spi->dev, "No IRQ specified\n");
+ return -ENOTSUPP;
+ }
+
+ /* Alloc port structure */
+ s = devm_kzalloc(&spi->dev, sizeof(struct max3107_port), GFP_KERNEL);
+ if (!s) {
+ dev_err(&spi->dev, "Error allocating port structure\n");
+ return -ENOMEM;
+ }
+
+ if (!pdata) {
+ dev_warn(&spi->dev, "No platform data supplied, using defaults\n");
+ pdata = &generic_plat_data;
+ }
+ s->pdata = pdata;
+ dev_set_drvdata(&spi->dev, s);
+
+ /* Board specific configure */
+ if (pdata->configure)
+ pdata->configure();
+
+ mutex_init(&s->max3107_mutex);
+
+ /* Setup SPI bus */
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ spi->max_speed_hz = 26000000;
+ spi_setup(spi);
+
+ s->regmap = devm_regmap_init_spi(spi, &max3107_regmap_config);
+ if (IS_ERR(s->regmap)) {
+ ret = PTR_ERR(s->regmap);
+ dev_err(&spi->dev, "Failed to initialize register map: %d\n", ret);
+ goto err_out;
+ }
+
+ /* Check REV ID to ensure we are talking to what we expect */
+ if (regmap_read(s->regmap, MAX3107_REVID_REG, &val)) {
+ dev_err(&spi->dev, "SPI transfer failed\n");
+ ret = -EIO;
+ goto err_out;
+ }
+
+ if (((val & MAX3107_REV_MASK) != MAX3107_REV_ID)) {
+ dev_err(&spi->dev, "Device ID 0x%02x does not match\n", val);
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ /* Disable all interrupts */
+ regmap_write(s->regmap, MAX3107_IRQEN_REG, 0);
+
+ /* Configure clock source */
+ val = MAX3107_CLKSRC_PLLBYP_BIT; /* PLL bypass ON */
+ if (pdata->flags & MAX3107_EXT_CLK)
+ val |= MAX3107_CLKSRC_EXTCLK_BIT;
+ else
+ val |= MAX3107_CLKSRC_CRYST_BIT;
+ regmap_write(s->regmap, MAX3107_CLKSRC_REG, val);
+
+ /* Register UART driver */
+ if (!driver_registered) {
+ ret = uart_register_driver(&max3107_uart_driver);
+ if (ret) {
+ dev_err(&spi->dev, "Registering UART driver failed\n");
+ goto err_out;
+ }
+ }
+ driver_registered++;
+
+ /* Initialize UART port data */
+ s->port.fifosize = MAX3107_FIFO_SIZE;
+ s->port.ops = &max3107_ops;
+ s->port.dev = &spi->dev;
+ s->port.uartclk = 9600; /* Bogus value */
+ s->port.flags = UPF_SKIP_TEST;
+ s->port.irq = spi->irq;
+ s->port.type = PORT_MAX3107;
+
+ uart_add_one_port(&max3107_uart_driver, &s->port);
+
+ /* Initialize workqueue for start TX */
+ s->wq = create_freezable_workqueue("max3107_wq");
+ INIT_WORK(&s->tx_work, max3107_wq_proc);
+
+ /* Go to suspend mode */
+ if (pdata->suspend)
+ pdata->suspend(s, 1);
+
+ dev_info(&spi->dev, "MAX3107 UART driver registered\n");
+
+ return 0;
+
+err_out:
+ devm_kfree(&spi->dev, s);
+
+ return ret;
+}
+
+static int __devexit max3107_remove(struct spi_device *spi)
+{
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ dev_dbg(s->port.dev, "Removing port\n");
+
+ destroy_workqueue(s->wq);
+
+ uart_remove_one_port(&max3107_uart_driver, &s->port);
+ dev_set_drvdata(&spi->dev, NULL);
+
+ if (driver_registered == 1)
+ uart_unregister_driver(&max3107_uart_driver);
+ driver_registered--;
+
+ devm_kfree(&spi->dev, s);
+
+ return 0;
+}
+
+static struct spi_driver max3107_driver = {
+ .driver = {
+ .name = "max3107",
+ .owner = THIS_MODULE,
+ },
+ .probe = max3107_probe,
+ .remove = __devexit_p(max3107_remove),
+ .suspend = max3107_suspend,
+ .resume = max3107_resume,
+};
+module_spi_driver(max3107_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Aavamobile");
+MODULE_DESCRIPTION("MAX3107 UART driver");
diff --git a/include/linux/platform_data/max3107.h b/include/linux/platform_data/max3107.h
new file mode 100644
index 0000000..6e13701
--- /dev/null
+++ b/include/linux/platform_data/max3107.h
@@ -0,0 +1,56 @@
+/*
+ * SPI UART protocol driver for Maxim (Dallas) MAX3107
+ *
+ * Based on max3100.c
+ * by Christian Pellegrin <chripell@evolware.org>
+ * and max3110.c
+ * by Feng Tang <feng.tang@intel.com>
+ *
+ * Rewritten by Alexander Shiyan <shc_work@mail.ru>
+ *
+ * Copyright (C) Aavamobile 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _MAX3107_H_
+#define _MAX3107_H_
+
+struct baud_table {
+ int baud;
+ u32 brg;
+};
+
+struct max3107_port;
+
+/* MAX3107 platform data structure */
+struct max3107_plat {
+ /* Platform data flags definitions */
+ const unsigned int flags;
+#define MAX3107_LOOPBACK (0x00000001) /* Loopback mode enable */
+#define MAX3107_EXT_CLK (0x00000002) /* External clock enable */
+#define MAX3107_AUTOCTS (0x00000004) /* Enable auto CTS flow control */
+#define MAX3107_AUTORTS (0x00000008) /* Enable auto RTS flow control */
+#define MAX3107_ECHO_SUPRESS (0x00000010) /* Enable echo supress */
+#define MAX3107_AUTO_DIR_CTRL (0x00000020) /* Enable Auto direction control (RS-485) */
+ /* Baudrate table */
+ const struct baud_table *baud_tbl;
+ /* GPIO base number */
+ const unsigned int gpio_base; /* Not implemented yet */
+ /* Called before initialization */
+ void (*configure)(void);
+ /* HW suspend function */
+ void (*suspend)(struct max3107_port *s, int do_suspend);
+};
+
+/* Functions that can be reused in board support code */
+void max3107_default_suspend(struct max3107_port *s, int do_suspend);
+
+/* Tables that can be reused in board support code */
+extern const struct baud_table brg_table_13m[];
+extern const struct baud_table brg_table_26m[];
+extern const struct baud_table brg_table_3_6864m[];
+
+#endif
--
1.7.3.4
^ permalink raw reply related
* Re: [PATCH] serial: The new version of the driver MAX3107.
From: Alan Cox @ 2012-07-27 15:29 UTC (permalink / raw)
To: Alexander Shiyan; +Cc: linux-serial, Greg Kroah-Hartman
In-Reply-To: <1343401309-14862-1-git-send-email-shc_work@mail.ru>
> +static void max3107_handle_rx(struct max3107_port *s, unsigned int
> rxlen) +{
> + unsigned int status, ch, flag;
> + struct tty_struct *tty = s->port.state->port.tty;
This all wants to be using tty_port_tty_get() and tty_kref_put - thats
a problem from the original of course. tty could also be NULL.
>
> + tty = port->state->port.tty;
> + if (!tty)
> + return;
This seems to be a meaninless test ?
> +
> + /* Word size */
> + if ((termios->c_cflag & CSIZE) == CS7)
> + lcr = MAX3107_LCR_WORD_LEN_7;
> + else
> + lcr = MAX3107_LCR_WORD_LEN_8;
Should also write back the word size actually used if we can't see that
which was requested (ie mask ~CSIZE and | CS8 in the CS5/6 case)
> +static volatile int driver_registered = 0;
Shouldn't need a volatile. Module load/unload are serialized for
obvious reasons.
Looks a very improved driver.
Alan
^ permalink raw reply
* Re[2]: [PATCH] serial: The new version of the driver MAX3107.
From: Alexander Shiyan @ 2012-07-27 17:34 UTC (permalink / raw)
To: Alan Cox; +Cc: linux-serial, Greg Kroah-Hartman
In-Reply-To: <20120727162909.38244772@bob.linux.org.uk>
Hello.
Thanks for reply, I still wait for more comments and will post a final version after.
Should I send this patch then as one part (ie, the classic diff) or in two parts (the
first to remove the old and the second to add a new variant)? I ask this because
the classic diff do not reflect the changes that were made.
Fri, 27 Jul 2012 16:29:09 +0100 от Alan Cox <alan@linux.intel.com>:
> > +static void max3107_handle_rx(struct max3107_port *s, unsigned int
> > rxlen) +{
> > + unsigned int status, ch, flag;
> > + struct tty_struct *tty = s->port.state->port.tty;
>
> This all wants to be using tty_port_tty_get() and tty_kref_put - thats
> a problem from the original of course. tty could also be NULL.
>
> >
> > + tty = port->state->port.tty;
> > + if (!tty)
> > + return;
>
> This seems to be a meaninless test ?
>
> > +
> > + /* Word size */
> > + if ((termios->c_cflag & CSIZE) == CS7)
> > + lcr = MAX3107_LCR_WORD_LEN_7;
> > + else
> > + lcr = MAX3107_LCR_WORD_LEN_8;
>
> Should also write back the word size actually used if we can't see that
> which was requested (ie mask ~CSIZE and | CS8 in the CS5/6 case)
>
>
> > +static volatile int driver_registered = 0;
>
> Shouldn't need a volatile. Module load/unload are serialized for
> obvious reasons.
>
> Looks a very improved driver.
>
> Alan
^ permalink raw reply
* [GIT PATCH] TTY/serial patches for 3.6-rc1
From: Greg KH @ 2012-07-27 19:05 UTC (permalink / raw)
To: Linus Torvalds, Andrew Morton; +Cc: linux-kernel, linux-serial, Alan Cox
The following changes since commit 28a33cbc24e4256c143dce96c7d93bf423229f92:
Linux 3.5 (2012-07-21 13:58:29 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git/ tags/tty-3.6-rc1
for you to fetch changes up to 38bd2a1ac736901d1cf4971c78ef952ba92ef78b:
pch_uart: Fix parity setting issue (2012-07-26 13:37:14 -0700)
----------------------------------------------------------------
TTY/Serial patches for 3.6-rc1
Here's the "tiny" set of patches for 3.6-rc1 for the tty layer and
serial drivers. They were cherry-picked from the tty-next branch of the
tty git tree, as they are small and "obvious" fixes. The larger
changes, as mentioned before, will be saved for the 3.7-rc1 merge
window.
All of these changes have been in the linux-next releases for quite a
while.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----------------------------------------------------------------
Alan Cox (1):
pch_uart: Fix missing break for 16 byte fifo
Corbin (1):
serial_core: Update buffer overrun statistics.
Darren Hart (1):
pch_uart: Add eg20t_port lock field, avoid recursive spinlocks
Kyoungil Kim (1):
serial: samsung: Fixed wrong comparison for baudclk_rate
Rabin Vincent (1):
vt: fix race in vt_waitactive()
Roland Stigge (2):
serial/8250: Add LPC3220 standard UART type
serial/of-serial: Add LPC3220 standard UART compatible string
Shachar Shemesh (1):
tty ldisc: Close/Reopen race prevention should check the proper flag
Tomoya MORINAGA (2):
pch_uart: Fix rx error interrupt setting issue
pch_uart: Fix parity setting issue
.../devicetree/bindings/tty/serial/of-serial.txt | 1 +
drivers/tty/serial/8250/8250.c | 8 +++
drivers/tty/serial/of_serial.c | 1 +
drivers/tty/serial/pch_uart.c | 59 +++++++++++++-------
drivers/tty/serial/samsung.c | 4 +-
drivers/tty/serial/serial_core.c | 6 +-
drivers/tty/tty_ldisc.c | 2 +-
drivers/tty/vt/vt_ioctl.c | 47 +++++++++++-----
include/linux/serial_core.h | 3 +-
9 files changed, 93 insertions(+), 38 deletions(-)
^ permalink raw reply
* [PATCH] pch_uart: check kzalloc result in dma_handle_tx()
From: Fengguang Wu @ 2012-07-28 12:43 UTC (permalink / raw)
To: Tomoya MORINAGA
Cc: Alan Cox, Greg Kroah-Hartman, linux-serial, LKML, kernel-janitors
Reported by coccinelle:
drivers/tty/serial/pch_uart.c:979:1-14: alloc with no test, possible model on line 994
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
drivers/tty/serial/pch_uart.c | 4 ++++
1 file changed, 4 insertions(+)
WARNING: this may be a superficial fix!
--- linux.orig/drivers/tty/serial/pch_uart.c 2012-06-07 05:39:57.550846385 +0800
+++ linux/drivers/tty/serial/pch_uart.c 2012-07-28 20:37:27.803145392 +0800
@@ -974,6 +974,10 @@ static unsigned int dma_handle_tx(struct
priv->tx_dma_use = 1;
priv->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+ if (!priv->sg_tx_p) {
+ dev_err(priv->port.dev, "%s:kzalloc Failed\n", __func__);
+ return 0;
+ }
sg_init_table(priv->sg_tx_p, num); /* Initialize SG table */
sg = priv->sg_tx_p;
^ permalink raw reply
* [PATCH] OMAP/serial: Add support for driving a GPIO as DTR.
From: NeilBrown @ 2012-07-30 0:30 UTC (permalink / raw)
To: linux-serial, linux-omap, linux-kernel, Tony Lindgren, Alan Cox,
Greg Kroah-Hartman
Cc: Govindraj.R, Kevin Hilman, H. Peter Anvin
[-- Attachment #1: Type: text/plain, Size: 5405 bytes --]
Hi all,
this is my current patch which answers my question from May:
Question: How to power-manage UART-attached devices.
It teaches omap2/serial about the possibility of a GPIO which is to be
driven as a DTR line.
This allows me to power on/off devices that are accessed via a serial port
(GPS, Bluetooth) when the port is opened/closed. I simply write a driver
which exports a GPIO line and does the required magic when it is driven
high or low.
- because '0' is a valid GPIO number, I added an extra port_info field
call 'DTR_present' so existing code wouldn't accidentally request to
use GPIO-0 as a DTR - is there a better way to handle this?
- I added a DTR_inverted field so you could choose the polarity of the
GPIO line ... I'm not really sure this is needed so I'll probably drop it
unless encouraged otherwise.
- If the gpio 'can_sleep', then I need a work-queue to effect the change, and
I currently use the 'qos_work' rather than adding another work handler.
As both the qos_work and the gpio work tasks are idempotent and rare, this
seems reasonable. Is it OK? should I make another work_struct? or
something else?
Thanks,
NeilBrown
---------------------
OMAP hardware doesn't provide a phyisical DTR line, but
some configurations may need a DTR line which tracks whether
the device is open or not.
So allow a gpio to be configured as the DTR line.
Signed-off-by: NeilBrown <neilb@suse.de>
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index c1b93c7..25d53b2 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -304,6 +304,9 @@ void __init omap_serial_init_port(struct omap_board_data *bdata,
omap_up.dma_rx_timeout = info->dma_rx_timeout;
omap_up.dma_rx_poll_rate = info->dma_rx_poll_rate;
omap_up.autosuspend_timeout = info->autosuspend_timeout;
+ omap_up.DTR_gpio = info->DTR_gpio;
+ omap_up.DTR_inverted = info->DTR_inverted;
+ omap_up.DTR_present = info->DTR_present;
pdata = &omap_up;
pdata_size = sizeof(struct omap_uart_port_info);
diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h
index 1a52725..52d3de4 100644
--- a/arch/arm/plat-omap/include/plat/omap-serial.h
+++ b/arch/arm/plat-omap/include/plat/omap-serial.h
@@ -69,6 +69,9 @@ struct omap_uart_port_info {
unsigned int dma_rx_timeout;
unsigned int autosuspend_timeout;
unsigned int dma_rx_poll_rate;
+ int DTR_gpio;
+ int DTR_inverted;
+ int DTR_present;
int (*get_context_loss_count)(struct device *);
void (*set_forceidle)(struct platform_device *);
@@ -131,6 +134,10 @@ struct uart_omap_port {
u32 errata;
u8 wakeups_enabled;
+ int DTR_gpio;
+ int DTR_inverted;
+ int DTR_active;
+
struct pm_qos_request pm_qos_request;
u32 latency;
u32 calc_latency;
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index d3cda0c..aa603f2 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -39,6 +39,7 @@
#include <linux/irq.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
+#include <linux/gpio.h>
#include <plat/dma.h>
#include <plat/dmtimer.h>
@@ -507,6 +508,16 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
up->mcr |= mcr;
serial_out(up, UART_MCR, up->mcr);
pm_runtime_put(&up->pdev->dev);
+
+ if (gpio_is_valid(up->DTR_gpio) &&
+ !!(mctrl & TIOCM_DTR) != up->DTR_active) {
+ up->DTR_active = !up->DTR_active;
+ if (gpio_cansleep(up->DTR_gpio))
+ schedule_work(&up->qos_work);
+ else
+ gpio_set_value(up->DTR_gpio,
+ up->DTR_active != up->DTR_inverted);
+ }
}
static void serial_omap_break_ctl(struct uart_port *port, int break_state)
@@ -715,6 +726,9 @@ static void serial_omap_uart_qos_work(struct work_struct *work)
qos_work);
pm_qos_update_request(&up->pm_qos_request, up->latency);
+ if (gpio_is_valid(up->DTR_gpio))
+ gpio_set_value_cansleep(up->DTR_gpio,
+ up->DTR_active != up->DTR_inverted);
}
static void
@@ -1435,7 +1449,7 @@ static int serial_omap_probe(struct platform_device *pdev)
struct uart_omap_port *up;
struct resource *mem, *irq, *dma_tx, *dma_rx;
struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
- int ret = -ENOSPC;
+ int ret;
if (pdev->dev.of_node)
omap_up_info = of_get_uart_port_info(&pdev->dev);
@@ -1466,10 +1480,29 @@ static int serial_omap_probe(struct platform_device *pdev)
if (!dma_tx)
return -ENXIO;
+ if (gpio_is_valid(omap_up_info->DTR_gpio) &&
+ omap_up_info->DTR_present) {
+ ret = gpio_request(omap_up_info->DTR_gpio, "omap-serial");
+ if (ret < 0)
+ return ret;
+ ret = gpio_direction_output(omap_up_info->DTR_gpio,
+ omap_up_info->DTR_inverted);
+ if (ret < 0)
+ return ret;
+ }
+
up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL);
if (!up)
return -ENOMEM;
+ if (gpio_is_valid(omap_up_info->DTR_gpio) &&
+ omap_up_info->DTR_present) {
+ up->DTR_gpio = omap_up_info->DTR_gpio;
+ up->DTR_inverted = omap_up_info->DTR_inverted;
+ } else
+ up->DTR_gpio = -EINVAL;
+ up->DTR_active = 0;
+
up->pdev = pdev;
up->port.dev = &pdev->dev;
up->port.type = PORT_OMAP;
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply related
* Re: n_tty_read panic
From: Stanislav Kozina @ 2012-07-30 10:41 UTC (permalink / raw)
To: Alan Cox; +Cc: 大林, linux-serial
In-Reply-To: <20120725124416.474bed6b@pyramind.ukuu.org.uk>
Seems that tty->read_buf == NULL at the very beginning of n_tty_read():
1723 do_it_again:
1724
1725 BUG_ON(!tty->read_buf);
tty->read_buf access needs tty->read_lock to be held, correct? So
perhaps the patch would be:
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index ee1c268..9decb1c 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1728,7 +1728,11 @@ static ssize_t n_tty_read(struct tty_struct *tty,
struct file *file,
do_it_again:
+#ifdef CONFIG_BUG
+ spin_lock_irqsave(&tty->read_lock, flags);
BUG_ON(!tty->read_buf);
+ spin_unlock_irqrestore(&tty->read_lock, flags);
+#endif
c = job_control(tty, file);
if (c < 0)
Regards,
-Stanislav Kozina
> On Wed, 25 Jul 2012 09:47:09 +0800
> "大林"<yourdarling1999@sina.com> wrote:
>
>> Hello.
>>
>> Here's a kernel panic caused by n_tty_read, linux kernel version is 2.6.38.8.
>>
>>
>> There are many discussions on the subject, this bug solved yet? Where can I download the patch?
> Thats a different trace to the others I've seen. Several races have been
> fixed, there are certainly a couple of others left to sort eventually. I
> don't believe anyone is currently working on them (and indeed until the
> rest of the locking and lock splitting work is done it's difficult to see
> how any useful progress could be made here). Patches are however welcome.
>
> Alan
> --
> 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
--
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 related
* Re: Patch for panic in n_tty_read()
From: Stanislav Kozina @ 2012-07-30 11:58 UTC (permalink / raw)
To: Alan Cox; +Cc: Greg Kroah-Hartman, linux-serial
In-Reply-To: <20120727135044.356823f4@pyramind.ukuu.org.uk>
Alan,
I am very sorry, but I don't see it. We didn't held the lock while
calling tty_audit_push() before, and we don't hold it after the patch
neither.
So what's the locking scheme change here? Is there some binding between
n_tty_read() and tty_audit_push() I just don't see?
Thank you,
-Stanislav
> Looks good to me. However it changes the locking rules on
> tty_audit_push() so please check the audit folks are ok with it. I don't
> think that causes any problems.
>
> Alan
^ permalink raw reply
* [PATCH v2] serial: The new version of the driver MAX3107, now called MAX310X.
From: Alexander Shiyan @ 2012-07-30 14:08 UTC (permalink / raw)
To: linux-serial; +Cc: Alan Cox, Greg Kroah-Hartman, Alexander Shiyan
This is NOT a patch. Only for initial review and comment. so, do NOT apply.
New features:
- Using the regmap. This makes it easy to add support for the driver
via I2C. Register cache removes unnecessary reading that improves
performance. I tested the driver on a slow processor with a very slow
SPI bus, so the speed was an important factor when writing code.
- Using devm_XXX-related functions.
- The use of threaded IRQ with IRQF_ONESHOT flag allows the driver to
the hardware that supports only level IRQ.
- Proper serial error handling.
- Proper treatment of FIFO queues.
- Advanced flags allows turn on RS-485 mode (Auto direction control).
- Ability to load multiple instances of drivers.
- Header moved to include/linux/platform_data.
- Cleanup, cleanup, cleanup...
Changes since v1:
- Fixes that are indicated by Alan Cox.
- Proper hardware & software flow control support.
- Fixed bug with using uninitialized values passed to regmap_read.
- Tables for the data transfer speed is no longer needed. The input
parameter "frequency" is calculated with all possible multipliers
and dividers to get the value with minimal error. All possible
options for the clock or crystal supported.
- Added support for MAX3108, so the driver is renamed to MAX310X.
In the future, I plan to add support for MAX3109 and MAX14830 chips,
which have a compatible registers set.
---
drivers/tty/serial/Kconfig | 11 +-
drivers/tty/serial/Makefile | 2 +-
drivers/tty/serial/max3107.c | 1215 ---------------------------------
drivers/tty/serial/max3107.h | 441 ------------
drivers/tty/serial/max310x.c | 1205 ++++++++++++++++++++++++++++++++
include/linux/platform_data/max310x.h | 47 ++
include/linux/serial_core.h | 4 +-
7 files changed, 1263 insertions(+), 1662 deletions(-)
delete mode 100644 drivers/tty/serial/max3107.c
delete mode 100644 drivers/tty/serial/max3107.h
create mode 100644 drivers/tty/serial/max310x.c
create mode 100644 include/linux/platform_data/max310x.h
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 070b442..4ecef98 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -257,12 +257,17 @@ config SERIAL_MAX3100
help
MAX3100 chip support
-config SERIAL_MAX3107
- tristate "MAX3107 support"
+config SERIAL_MAX310X
+ bool "MAX310X support"
depends on SPI
select SERIAL_CORE
+ select REGMAP_SPI if SPI
help
- MAX3107 chip support
+ The MAX3107/8 is an advanced universal asynchronous receiver-transmitter
+ with 128 words each of receive and transmit first-in/first-out (FIFO)
+ that can be controlled through I2C or high-speed SPI.
+
+ Say Y here if you want to support this chip.
config SERIAL_DZ
bool "DECstation DZ serial driver"
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 7257c5d..6c63194 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -28,7 +28,7 @@ obj-$(CONFIG_SERIAL_BFIN) += bfin_uart.o
obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
-obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
+obj-$(CONFIG_SERIAL_MAX310X) += max310x.o
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
obj-$(CONFIG_SERIAL_MUX) += mux.o
obj-$(CONFIG_SERIAL_68328) += 68328serial.o
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
new file mode 100644
index 0000000..ed671e8
--- /dev/null
+++ b/drivers/tty/serial/max310x.c
@@ -0,0 +1,1205 @@
+/*
+ * UART protocol driver for Maxim (Dallas) MAX3107/8
+ *
+ * Based on max3100.c
+ * by Christian Pellegrin <chripell@evolware.org>
+ * and max3110.c
+ * by Feng Tang <feng.tang@intel.com>
+ * Rewritten by Alexander Shiyan <shc_work@mail.ru>
+ *
+ * Copyright (C) Aavamobile 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/* TODO: GPIO handling */
+/* TODO: MAX3109 support (Dual) */
+/* TODO: MAX14830 support (Quad) */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include <linux/platform_data/max310x.h>
+
+#define MAX310X_MAJOR 204
+#define MAX310X_MINOR 209
+
+/* Chip wakeup delay */
+#define MAX310X_WAKEUP_DELAY 50
+
+/* Sleep mode definitions */
+#define MAX310X_DISABLE_FORCED_SLEEP 0
+#define MAX310X_ENABLE_FORCED_SLEEP 1
+#define MAX310X_DISABLE_AUTOSLEEP 2
+#define MAX310X_ENABLE_AUTOSLEEP 3
+
+/* MAX310X register definitions */
+#define MAX310X_RHR_REG (0x00) /* RX FIFO */
+#define MAX310X_THR_REG (0x00) /* TX FIFO */
+#define MAX310X_IRQEN_REG (0x01) /* IRQ enable */
+#define MAX310X_IRQSTS_REG (0x02) /* IRQ status */
+#define MAX310X_LSR_IRQEN_REG (0x03) /* LSR IRQ enable */
+#define MAX310X_LSR_IRQSTS_REG (0x04) /* LSR IRQ status */
+#define MAX310X_SPCHR_IRQEN_REG (0x05) /* Special char IRQ enable */
+#define MAX310X_SPCHR_IRQSTS_REG (0x06) /* Special char IRQ status */
+#define MAX310X_STS_IRQEN_REG (0x07) /* Status IRQ enable */
+#define MAX310X_STS_IRQSTS_REG (0x08) /* Status IRQ status */
+#define MAX310X_MODE1_REG (0x09) /* MODE1 */
+#define MAX310X_MODE2_REG (0x0a) /* MODE2 */
+#define MAX310X_LCR_REG (0x0b) /* LCR */
+#define MAX310X_RXTO_REG (0x0c) /* RX timeout */
+#define MAX310X_HDPIXDELAY_REG (0x0d) /* Auto transceiver delays */
+#define MAX310X_IRDA_REG (0x0e) /* IRDA settings */
+#define MAX310X_FLOWLVL_REG (0x0f) /* Flow control levels */
+#define MAX310X_FIFOTRIGLVL_REG (0x10) /* FIFO IRQ trigger levels */
+#define MAX310X_TXFIFOLVL_REG (0x11) /* TX FIFO level */
+#define MAX310X_RXFIFOLVL_REG (0x12) /* RX FIFO level */
+#define MAX310X_FLOWCTRL_REG (0x13) /* Flow control */
+#define MAX310X_XON1_REG (0x14) /* XON1 character */
+#define MAX310X_XON2_REG (0x15) /* XON2 character */
+#define MAX310X_XOFF1_REG (0x16) /* XOFF1 character */
+#define MAX310X_XOFF2_REG (0x17) /* XOFF2 character */
+#define MAX310X_GPIOCFG_REG (0x18) /* GPIO config */
+#define MAX310X_GPIODATA_REG (0x19) /* GPIO data */
+#define MAX310X_PLLCFG_REG (0x1a) /* PLL config */
+#define MAX310X_BRGCFG_REG (0x1b) /* Baud rate generator conf */
+#define MAX310X_BRGDIVLSB_REG (0x1c) /* Baud rate divisor LSB */
+#define MAX310X_BRGDIVMSB_REG (0x1d) /* Baud rate divisor MSB */
+#define MAX310X_CLKSRC_REG (0x1e) /* Clock source */
+/* Only present in MAX3107 */
+#define MAX3107_REVID_REG (0x1f) /* Revision identification */
+
+/* IRQ register bits */
+#define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */
+#define MAX310X_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */
+#define MAX310X_IRQ_STS_BIT (1 << 2) /* Status interrupt */
+#define MAX310X_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */
+#define MAX310X_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */
+#define MAX310X_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */
+#define MAX310X_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */
+#define MAX310X_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */
+
+/* LSR register bits */
+#define MAX310X_LSR_RXTO_BIT (1 << 0) /* RX timeout */
+#define MAX310X_LSR_RXOVR_BIT (1 << 1) /* RX overrun */
+#define MAX310X_LSR_RXPAR_BIT (1 << 2) /* RX parity error */
+#define MAX310X_LSR_FRERR_BIT (1 << 3) /* Frame error */
+#define MAX310X_LSR_RXBRK_BIT (1 << 4) /* RX break */
+#define MAX310X_LSR_RXNOISE_BIT (1 << 5) /* RX noise */
+#define MAX310X_LSR_CTS_BIT (1 << 7) /* CTS pin state */
+
+/* Special character register bits */
+#define MAX310X_SPCHR_XON1_BIT (1 << 0) /* XON1 character */
+#define MAX310X_SPCHR_XON2_BIT (1 << 1) /* XON2 character */
+#define MAX310X_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */
+#define MAX310X_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */
+#define MAX310X_SPCHR_BREAK_BIT (1 << 4) /* RX break */
+#define MAX310X_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */
+
+/* Status register bits */
+#define MAX310X_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */
+#define MAX310X_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */
+#define MAX310X_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */
+#define MAX310X_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */
+#define MAX310X_STS_CLKREADY_BIT (1 << 5) /* Clock ready */
+#define MAX310X_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */
+
+/* MODE1 register bits */
+#define MAX310X_MODE1_RXDIS_BIT (1 << 0) /* RX disable */
+#define MAX310X_MODE1_TXDIS_BIT (1 << 1) /* TX disable */
+#define MAX310X_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */
+#define MAX310X_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */
+#define MAX310X_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */
+#define MAX310X_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */
+#define MAX310X_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */
+#define MAX310X_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */
+
+/* MODE2 register bits */
+#define MAX310X_MODE2_RST_BIT (1 << 0) /* Chip reset */
+#define MAX310X_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */
+#define MAX310X_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */
+#define MAX310X_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */
+#define MAX310X_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */
+#define MAX310X_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */
+#define MAX310X_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */
+#define MAX310X_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */
+
+/* LCR register bits */
+#define MAX310X_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */
+#define MAX310X_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1
+ *
+ * Word length bits table:
+ * 00 -> 5 bit words
+ * 01 -> 6 bit words
+ * 10 -> 7 bit words
+ * 11 -> 8 bit words
+ */
+#define MAX310X_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit
+ *
+ * STOP length bit table:
+ * 0 -> 1 stop bit
+ * 1 -> 1-1.5 stop bits if
+ * word length is 5,
+ * 2 stop bits otherwise
+ */
+#define MAX310X_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */
+#define MAX310X_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */
+#define MAX310X_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */
+#define MAX310X_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */
+#define MAX310X_LCR_RTS_BIT (1 << 7) /* RTS pin control */
+#define MAX310X_LCR_WORD_LEN_5 (0x00)
+#define MAX310X_LCR_WORD_LEN_6 (0x01)
+#define MAX310X_LCR_WORD_LEN_7 (0x02)
+#define MAX310X_LCR_WORD_LEN_8 (0x03)
+
+/* IRDA register bits */
+#define MAX310X_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */
+#define MAX310X_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */
+#define MAX310X_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */
+#define MAX310X_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */
+#define MAX310X_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */
+#define MAX310X_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */
+
+/* Flow control trigger level register masks */
+#define MAX310X_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */
+#define MAX310X_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */
+#define MAX310X_FLOWLVL_HALT(words) ((words / 8) & 0x0f)
+#define MAX310X_FLOWLVL_RES(words) (((words / 8) & 0x0f) << 4)
+
+/* FIFO interrupt trigger level register masks */
+#define MAX310X_FIFOTRIGLVL_TX_MASK (0x0f) /* TX FIFO trigger level */
+#define MAX310X_FIFOTRIGLVL_RX_MASK (0xf0) /* RX FIFO trigger level */
+#define MAX310X_FIFOTRIGLVL_TX(words) ((words / 8) & 0x0f)
+#define MAX310X_FIFOTRIGLVL_RX(words) (((words / 8) & 0x0f) << 4)
+
+/* Flow control register bits */
+#define MAX310X_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */
+#define MAX310X_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */
+#define MAX310X_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs
+ * are used in conjunction with
+ * XOFF2 for definition of
+ * special character */
+#define MAX310X_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */
+#define MAX310X_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */
+#define MAX310X_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1
+ *
+ * SWFLOW bits 1 & 0 table:
+ * 00 -> no transmitter flow
+ * control
+ * 01 -> receiver compares
+ * XON2 and XOFF2
+ * and controls
+ * transmitter
+ * 10 -> receiver compares
+ * XON1 and XOFF1
+ * and controls
+ * transmitter
+ * 11 -> receiver compares
+ * XON1, XON2, XOFF1 and
+ * XOFF2 and controls
+ * transmitter
+ */
+#define MAX310X_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */
+#define MAX310X_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3
+ *
+ * SWFLOW bits 3 & 2 table:
+ * 00 -> no received flow
+ * control
+ * 01 -> transmitter generates
+ * XON2 and XOFF2
+ * 10 -> transmitter generates
+ * XON1 and XOFF1
+ * 11 -> transmitter generates
+ * XON1, XON2, XOFF1 and
+ * XOFF2
+ */
+
+/* GPIO configuration register bits */
+#define MAX310X_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */
+#define MAX310X_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */
+#define MAX310X_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */
+#define MAX310X_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */
+#define MAX310X_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */
+#define MAX310X_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */
+#define MAX310X_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */
+#define MAX310X_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */
+
+/* GPIO DATA register bits */
+#define MAX310X_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */
+#define MAX310X_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */
+#define MAX310X_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */
+#define MAX310X_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */
+#define MAX310X_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */
+#define MAX310X_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */
+#define MAX310X_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */
+#define MAX310X_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */
+
+/* PLL configuration register masks */
+#define MAX310X_PLLCFG_PREDIV_MASK (0x3f) /* PLL predivision value */
+#define MAX310X_PLLCFG_PLLFACTOR_MASK (0xc0) /* PLL multiplication factor */
+
+/* Baud rate generator configuration register masks and bits */
+#define MAX310X_BRGCFG_FRACT_MASK (0x0f) /* Fractional portion of
+ * Baud rate generator divisor
+ */
+#define MAX310X_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */
+#define MAX310X_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */
+
+/* Clock source register bits */
+#define MAX310X_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */
+#define MAX310X_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */
+#define MAX310X_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */
+#define MAX310X_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */
+#define MAX310X_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */
+
+/* Misc definitions */
+#define MAX310X_FIFO_SIZE (128)
+
+/* MAX3107 specific */
+#define MAX3107_REV_ID (0xa0)
+#define MAX3107_REV_MASK (0xfe)
+
+/* Error status definitions */
+#define MAX310X_ERR_PARITY MAX310X_LSR_RXPAR_BIT
+#define MAX310X_ERR_FRAME MAX310X_LSR_FRERR_BIT
+#define MAX310X_ERR_OVERRUN MAX310X_LSR_RXOVR_BIT
+#define MAX310X_ERR_BREAK MAX310X_LSR_RXBRK_BIT
+
+/* IRQ status bits definitions */
+#define MAX310X_IRQ_TX (MAX310X_IRQ_TXFIFO_BIT | \
+ MAX310X_IRQ_TXEMPTY_BIT)
+#define MAX310X_IRQ_RX (MAX310X_IRQ_RXFIFO_BIT | \
+ MAX310X_IRQ_RXEMPTY_BIT)
+/* Supported chip types */
+enum {
+ MAX310X_TYPE_MAX3107,
+ MAX310X_TYPE_MAX3108,
+};
+
+struct max310x_port {
+ struct uart_port port;
+ struct uart_driver uart;
+
+ const char *name;
+
+ struct regmap *regmap;
+ struct regmap_config regcfg;
+
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+
+ struct mutex max310x_mutex;
+
+ int suspended;
+
+ struct max310x_pdata *pdata;
+
+ unsigned int nr_gpio;
+};
+
+static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX310X_IRQSTS_REG:
+ case MAX310X_LSR_IRQSTS_REG:
+ case MAX310X_SPCHR_IRQSTS_REG:
+ case MAX310X_STS_IRQSTS_REG:
+ case MAX310X_TXFIFOLVL_REG:
+ case MAX310X_RXFIFOLVL_REG:
+ case MAX3107_REVID_REG: /* Only available on MAX3107 */
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX310X_RHR_REG:
+ case MAX310X_IRQSTS_REG:
+ case MAX310X_LSR_IRQSTS_REG:
+ case MAX310X_SPCHR_IRQSTS_REG:
+ case MAX310X_STS_IRQSTS_REG:
+ case MAX310X_TXFIFOLVL_REG:
+ case MAX310X_RXFIFOLVL_REG:
+ case MAX310X_GPIODATA_REG:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool max310x_reg_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX310X_RHR_REG:
+ case MAX310X_IRQSTS_REG:
+ case MAX310X_SPCHR_IRQSTS_REG:
+ case MAX310X_STS_IRQSTS_REG:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static void max310x_set_baud(struct max310x_port *s, int baud)
+{
+ unsigned int mode = 0, div = s->port.uartclk / baud;
+
+ if (!(div / 16)) {
+ /* Mode x2 */
+ mode = MAX310X_BRGCFG_2XMODE_BIT;
+ div = (s->port.uartclk * 2) / baud;
+ }
+
+ if (!(div / 16)) {
+ /* Mode x4 */
+ mode = MAX310X_BRGCFG_4XMODE_BIT;
+ div = (s->port.uartclk * 4) / baud;
+ }
+
+ regmap_write(s->regmap, MAX310X_BRGDIVMSB_REG, ((div / 16) >> 8) & 0xff);
+ regmap_write(s->regmap, MAX310X_BRGDIVLSB_REG, (div / 16) & 0xff);
+ regmap_write(s->regmap, MAX310X_BRGCFG_REG, (div % 16) | mode);
+}
+
+static int __devinit max310x_update_best_err(unsigned long f, long *besterr)
+{
+ /* Use baudrate 115200 for calculate error */
+ long err = f % (115200 * 16);
+
+ if ((*besterr < 0) || (*besterr > err)) {
+ *besterr = err;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int __devinit max310x_set_ref_clk(struct max310x_port *s)
+{
+ unsigned int div, clksrc, sts = 0, pllcfg = 0;
+ long besterr = -1;
+ unsigned long fdiv, fmul, bestfreq = s->pdata->frequency;
+
+ /* First, update error without PLL */
+ max310x_update_best_err(s->pdata->frequency, &besterr);
+
+ /* Try all possible PLL dividers */
+ for (div = 1; (div <= 63) && besterr; div++) {
+ fdiv = DIV_ROUND_CLOSEST(s->pdata->frequency, div);
+
+ /* Try multiplier 6 */
+ fmul = fdiv * 6;
+ if ((fdiv >=500000) && (fdiv <= 800000))
+ if (!max310x_update_best_err(fmul, &besterr)) {
+ pllcfg = (0 << 6) | div;
+ bestfreq = fmul;
+ }
+ /* Try multiplier 48 */
+ fmul = fdiv * 48;
+ if ((fdiv >=850000) && (fdiv <= 1200000))
+ if (!max310x_update_best_err(fmul, &besterr)) {
+ pllcfg = (1 << 6) | div;
+ bestfreq = fmul;
+ }
+ /* Try multiplier 96 */
+ fmul = fdiv * 96;
+ if ((fdiv >=425000) && (fdiv <= 1000000))
+ if (!max310x_update_best_err(fmul, &besterr)) {
+ pllcfg = (2 << 6) | div;
+ bestfreq = fmul;
+ }
+ /* Try multiplier 144 */
+ fmul = fdiv * 144;
+ if ((fdiv >=390000) && (fdiv <= 667000))
+ if (!max310x_update_best_err(fmul, &besterr)) {
+ pllcfg = (3 << 6) | div;
+ bestfreq = fmul;
+ }
+ }
+
+ /* Configure clock source */
+ if (s->pdata->driver_flags & MAX310X_EXT_CLK)
+ clksrc = MAX310X_CLKSRC_EXTCLK_BIT;
+ else
+ clksrc = MAX310X_CLKSRC_CRYST_BIT;
+
+ /* Configure PLL */
+ if (pllcfg) {
+ clksrc |= MAX310X_CLKSRC_PLL_BIT;
+ regmap_write(s->regmap, MAX310X_PLLCFG_REG, pllcfg);
+ } else
+ clksrc |= MAX310X_CLKSRC_PLLBYP_BIT;
+
+ regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc);
+
+ if (pllcfg && !(s->pdata->driver_flags & MAX310X_EXT_CLK))
+ for (;;) {
+ /* Wait for PLL only if crystal is used */
+ regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &sts);
+ if (sts & MAX310X_STS_CLKREADY_BIT)
+ break;
+ }
+
+ dev_dbg(s->port.dev, "Reference clock set to %lu Hz\n", bestfreq);
+
+ return (int)bestfreq;
+}
+
+static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen)
+{
+ unsigned int status = 0, ch = 0, flag;
+ struct tty_struct *tty = tty_port_tty_get(&s->port.state->port);
+
+ if (!tty)
+ return;
+
+ if (unlikely(rxlen >= MAX310X_FIFO_SIZE)) {
+ dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen);
+ /* Ensure sanity of RX level */
+ rxlen = MAX310X_FIFO_SIZE;
+ }
+
+ dev_dbg(s->port.dev, "RX Len = %u\n", rxlen);
+
+ while (rxlen--) {
+ regmap_read(s->regmap, MAX310X_RHR_REG, &ch);
+ regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &status);
+
+ status &= MAX310X_ERR_PARITY | MAX310X_ERR_FRAME |
+ MAX310X_ERR_OVERRUN | MAX310X_ERR_BREAK;
+
+ s->port.icount.rx++;
+ flag = TTY_NORMAL;
+
+ if (unlikely(status)) {
+ if (status & MAX310X_ERR_BREAK) {
+ s->port.icount.brk++;
+ if (uart_handle_break(&s->port))
+ continue;
+ } else if (status & MAX310X_ERR_PARITY)
+ s->port.icount.parity++;
+ else if (status & MAX310X_ERR_FRAME)
+ s->port.icount.frame++;
+ else if (status & MAX310X_ERR_OVERRUN)
+ s->port.icount.overrun++;
+
+ status &= s->port.read_status_mask;
+ if (status & MAX310X_ERR_BREAK)
+ flag = TTY_BREAK;
+ else if (status & MAX310X_ERR_PARITY)
+ flag = TTY_PARITY;
+ else if (status & MAX310X_ERR_FRAME)
+ flag = TTY_FRAME;
+ else if (status & MAX310X_ERR_OVERRUN)
+ flag = TTY_OVERRUN;
+ }
+
+ if (uart_handle_sysrq_char(s->port, ch))
+ continue;
+
+ if (status & s->port.ignore_status_mask)
+ continue;
+
+ uart_insert_char(&s->port, status, MAX310X_ERR_OVERRUN, ch, flag);
+ }
+
+ tty_flip_buffer_push(tty);
+
+ tty_kref_put(tty);
+}
+
+static void max310x_handle_tx(struct max310x_port *s)
+{
+ struct circ_buf *xmit = &s->port.state->xmit;
+ unsigned int txlen = 0, to_send;
+
+ if (unlikely(s->port.x_char)) {
+ regmap_write(s->regmap, MAX310X_THR_REG, s->port.x_char);
+ s->port.icount.tx++;
+ s->port.x_char = 0;
+ return;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port))
+ return;
+
+ /* Get length of data pending in circular buffer */
+ to_send = uart_circ_chars_pending(xmit);
+ if (likely(to_send)) {
+ /* Limit to size of TX FIFO */
+ regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &txlen);
+ txlen = MAX310X_FIFO_SIZE - txlen;
+ to_send = (to_send > txlen) ? txlen : to_send;
+
+ dev_dbg(s->port.dev, "TX Len = %u\n", to_send);
+
+ /* Add data to send */
+ s->port.icount.tx += to_send;
+ do {
+ regmap_write(s->regmap, MAX310X_THR_REG, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ } while (--to_send > 0);
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&s->port);
+}
+
+static irqreturn_t max310x_ist(int irq, void *dev_id)
+{
+ struct max310x_port *s = (struct max310x_port*)dev_id;
+ unsigned int ists = 0, lsr = 0, rxlen = 0;
+
+ mutex_lock(&s->max310x_mutex);
+
+ for (;;) {
+ /* Read IRQ status & RX FIFO level */
+ regmap_read(s->regmap, MAX310X_IRQSTS_REG, &ists);
+ regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &lsr);
+ regmap_read(s->regmap, MAX310X_RXFIFOLVL_REG, &rxlen);
+ if (!ists && !(lsr & MAX310X_LSR_RXTO_BIT) && !rxlen)
+ break;
+
+ dev_dbg(s->port.dev, "IRQ status: 0x%02x\n", ists);
+
+ if (rxlen)
+ max310x_handle_rx(s, rxlen);
+ if (ists & MAX310X_IRQ_TX)
+ max310x_handle_tx(s);
+ if (ists & MAX310X_IRQ_CTS_BIT)
+ uart_handle_cts_change(&s->port,
+ !!(lsr & MAX310X_LSR_CTS_BIT));
+ }
+
+ mutex_unlock(&s->max310x_mutex);
+
+ return IRQ_HANDLED;
+}
+
+static void max310x_wq_proc(struct work_struct *ws)
+{
+ struct max310x_port *s = container_of(ws, struct max310x_port, tx_work);
+
+ mutex_lock(&s->max310x_mutex);
+ max310x_handle_tx(s);
+ mutex_unlock(&s->max310x_mutex);
+}
+
+static void max310x_enable_ms(struct uart_port *port)
+{
+ /* Modem status not supported */
+}
+
+static void max310x_start_tx(struct uart_port *port)
+{
+ struct max310x_port *s = container_of(port, struct max310x_port, port);
+
+ queue_work(s->wq, &s->tx_work);
+}
+
+static void max310x_stop_tx(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static void max310x_stop_rx(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static unsigned int max310x_tx_empty(struct uart_port *port)
+{
+ unsigned int val = 0;
+ struct max310x_port *s = container_of(port, struct max310x_port, port);
+
+ mutex_lock(&s->max310x_mutex);
+ regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &val);
+ mutex_unlock(&s->max310x_mutex);
+
+ return val ? 0: TIOCSER_TEMT;
+}
+
+static unsigned int max310x_get_mctrl(struct uart_port *port)
+{
+ /* DCD and DSR are not wired and CTS/RTS is handled automatically
+ * so just indicate DSR and CAR asserted
+ */
+ return TIOCM_DSR | TIOCM_CAR;
+}
+
+static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* DCD and DSR are not wired and CTS/RTS is hadnled automatically
+ * so do nothing
+ */
+}
+
+static void max310x_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct max310x_port *s = container_of(port, struct max310x_port, port);
+ unsigned int lcr, flow = 0;
+ int baud;
+
+ mutex_lock(&s->max310x_mutex);
+
+ /* Mask termios capabilities we don't support */
+ termios->c_cflag &= ~CMSPAR;
+ termios->c_iflag &= ~IXANY;
+
+ /* Word size */
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ lcr = MAX310X_LCR_WORD_LEN_5;
+ break;
+ case CS6:
+ lcr = MAX310X_LCR_WORD_LEN_6;
+ break;
+ case CS7:
+ lcr = MAX310X_LCR_WORD_LEN_7;
+ break;
+ case CS8:
+ default:
+ lcr = MAX310X_LCR_WORD_LEN_8;
+ break;
+ }
+
+ /* Parity */
+ if (termios->c_cflag & PARENB) {
+ lcr |= MAX310X_LCR_PARITY_BIT;
+ if (!(termios->c_cflag & PARODD))
+ lcr |= MAX310X_LCR_EVENPARITY_BIT;
+ }
+
+ /* Stop bits */
+ if (termios->c_cflag & CSTOPB)
+ lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */
+
+ /* Update LCR register */
+ regmap_write(s->regmap, MAX310X_LCR_REG, lcr);
+
+ /* Set read status mask */
+ s->port.read_status_mask = MAX310X_ERR_OVERRUN;
+ if (termios->c_iflag & INPCK)
+ s->port.read_status_mask |= MAX310X_ERR_PARITY |
+ MAX310X_ERR_FRAME;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ s->port.read_status_mask |= MAX310X_ERR_BREAK;
+
+ /* Set status ignore mask */
+ s->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNBRK)
+ s->port.ignore_status_mask |= MAX310X_ERR_BREAK;
+ if (!(termios->c_cflag & CREAD))
+ s->port.ignore_status_mask |= MAX310X_ERR_PARITY |
+ MAX310X_ERR_OVERRUN |
+ MAX310X_ERR_FRAME |
+ MAX310X_ERR_BREAK;
+
+ /* Configure flow control */
+ regmap_write(s->regmap, MAX310X_XON1_REG, termios->c_cc[VSTART]);
+ regmap_write(s->regmap, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]);
+ if (termios->c_cflag & CRTSCTS)
+ flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT |
+ MAX310X_FLOWCTRL_AUTORTS_BIT;
+ if (termios->c_iflag & IXON)
+ flow |= MAX310X_FLOWCTRL_SWFLOW3_BIT |
+ MAX310X_FLOWCTRL_SWFLOWEN_BIT;
+ if (termios->c_iflag & IXOFF)
+ flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT |
+ MAX310X_FLOWCTRL_SWFLOWEN_BIT;
+ regmap_write(s->regmap, MAX310X_FLOWCTRL_REG, flow);
+
+ /* Get baud rate generator configuration */
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0xffff,
+ port->uartclk / 4);
+
+ /* Setup baudrate generator */
+ max310x_set_baud(s, baud);
+
+ /* Update timeout according to new baud rate */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ mutex_unlock(&s->max310x_mutex);
+}
+
+static void max310x_register_init(struct max310x_port *s)
+{
+ unsigned int val;
+
+ /* Configure baud rate, 9600 as default */
+ max310x_set_baud(s, 9600);
+
+ /* Configure LCR register, 8N1 mode by default */
+ val = MAX310X_LCR_WORD_LEN_8;
+ regmap_write(s->regmap, MAX310X_LCR_REG, val);
+
+ /* Configure MODE1 register */
+ val = MAX310X_MODE1_IRQSEL_BIT; /* Enable IRQ pin */
+ if (s->pdata->uart_flags[s->port.line] & MAX310X_AUTO_DIR_CTRL)
+ val |= MAX310X_MODE1_TRNSCVCTRL_BIT;
+ regmap_write(s->regmap, MAX310X_MODE1_REG, val);
+
+ /* Configure MODE2 register */
+ val = MAX310X_MODE2_RXEMPTINV_BIT;
+ if (s->pdata->uart_flags[s->port.line] & MAX310X_LOOPBACK)
+ val |= MAX310X_MODE2_LOOPBACK_BIT;
+ if (s->pdata->uart_flags[s->port.line] & MAX310X_ECHO_SUPRESS)
+ val |= MAX310X_MODE2_ECHOSUPR_BIT;
+
+ /* Reset FIFOs */
+ val |= MAX310X_MODE2_FIFORST_BIT;
+ regmap_write(s->regmap, MAX310X_MODE2_REG, val);
+
+ /* Configure FIFO trigger level register */
+ /* RX FIFO trigger for 16 words, TX FIFO trigger for 64 words */
+ val = MAX310X_FIFOTRIGLVL_RX(16) | MAX310X_FIFOTRIGLVL_TX(64);
+ regmap_write(s->regmap, MAX310X_FIFOTRIGLVL_REG, val);
+
+ /* Configure flow control levels */
+ /* Flow control halt level 96, resume level 48 */
+ val = MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96);
+ regmap_write(s->regmap, MAX310X_FLOWLVL_REG, val);
+
+ /* Clear timeout register */
+ regmap_write(s->regmap, MAX310X_RXTO_REG, 0);
+
+ /* Configure LSR interrupt enable register */
+ /* Enable RX timeout interrupt */
+ val = MAX310X_LSR_RXTO_BIT;
+ regmap_write(s->regmap, MAX310X_LSR_IRQEN_REG, val);
+
+ /* Clear FIFO reset */
+ regmap_update_bits(s->regmap, MAX310X_MODE2_REG,
+ MAX310X_MODE2_FIFORST_BIT, 0);
+
+ /* Clear IRQ status register by reading it */
+ regmap_read(s->regmap, MAX310X_IRQSTS_REG, &val);
+
+ /* Configure interrupt enable register */
+ /* Enable CTS change interrupt */
+ val = MAX310X_IRQ_CTS_BIT;
+ /* Enable RX, TX interrupts */
+ val |= MAX310X_IRQ_RX | MAX310X_IRQ_TX;
+ regmap_write(s->regmap, MAX310X_IRQEN_REG, val);
+}
+
+static int max310x_startup(struct uart_port *port)
+{
+ struct max310x_port *s = container_of(port, struct max310x_port, port);
+
+ if (s->pdata->suspend)
+ s->pdata->suspend(s, 0);
+
+ /* Init registers */
+ max310x_register_init(s);
+
+ /* Setup interrupt */
+ if (devm_request_threaded_irq(port->dev, port->irq, NULL, max310x_ist,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(port->dev), s)) {
+ dev_err(port->dev, "Unable to reguest IRQ %i\n", port->irq);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void max310x_shutdown(struct uart_port *port)
+{
+ struct max310x_port *s = container_of(port, struct max310x_port, port);
+
+ if (s->suspended && s->pdata->suspend)
+ s->pdata->suspend(s, 0);
+
+ /* Disable all interrupts */
+ mutex_lock(&s->max310x_mutex);
+ regmap_write(s->regmap, MAX310X_IRQEN_REG, 0);
+ mutex_unlock(&s->max310x_mutex);
+
+ /* Free the interrupt */
+ devm_free_irq(port->dev, port->irq, s);
+
+ if (s->pdata->suspend)
+ s->pdata->suspend(s, 1);
+}
+
+static const char *max310x_type(struct uart_port *port)
+{
+ struct max310x_port *s = container_of(port, struct max310x_port, port);
+
+ return (port->type == PORT_MAX310X) ? s->name : NULL;
+}
+
+static int max310x_request_port(struct uart_port *port)
+{
+ /* Do nothing */
+ return 0;
+}
+
+static void max310x_release_port(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static void max310x_config_port(struct uart_port *port, int flags)
+{
+ struct max310x_port *s = container_of(port, struct max310x_port, port);
+
+ s->port.type = PORT_MAX310X;
+}
+
+static int max310x_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX310X))
+ return 0;
+
+ return -EINVAL;
+}
+
+static void max310x_break_ctl(struct uart_port *port, int break_state)
+{
+ struct max310x_port *s = container_of(port, struct max310x_port, port);
+
+ mutex_lock(&s->max310x_mutex);
+ regmap_update_bits(s->regmap, MAX310X_LCR_REG,
+ MAX310X_LCR_TXBREAK_BIT,
+ break_state ? MAX310X_LCR_TXBREAK_BIT : 0);
+ mutex_unlock(&s->max310x_mutex);
+}
+
+static struct uart_ops max310x_ops = {
+ .tx_empty = max310x_tx_empty,
+ .set_mctrl = max310x_set_mctrl,
+ .get_mctrl = max310x_get_mctrl,
+ .stop_tx = max310x_stop_tx,
+ .start_tx = max310x_start_tx,
+ .stop_rx = max310x_stop_rx,
+ .enable_ms = max310x_enable_ms,
+ .break_ctl = max310x_break_ctl,
+ .startup = max310x_startup,
+ .shutdown = max310x_shutdown,
+ .set_termios = max310x_set_termios,
+ .type = max310x_type,
+ .request_port = max310x_request_port,
+ .release_port = max310x_release_port,
+ .config_port = max310x_config_port,
+ .verify_port = max310x_verify_port,
+};
+
+static void max310x_set_sleep(struct max310x_port *s, int mode)
+{
+ unsigned int val = 0;
+
+ mutex_lock(&s->max310x_mutex);
+
+ regmap_read(s->regmap, MAX310X_MODE1_REG, &val);
+
+ switch (mode) {
+ case MAX310X_DISABLE_FORCED_SLEEP:
+ val &= ~MAX310X_MODE1_FORCESLEEP_BIT;
+ break;
+ case MAX310X_ENABLE_FORCED_SLEEP:
+ val |= MAX310X_MODE1_FORCESLEEP_BIT;
+ break;
+ case MAX310X_DISABLE_AUTOSLEEP:
+ val &= ~MAX310X_MODE1_AUTOSLEEP_BIT;
+ break;
+ case MAX310X_ENABLE_AUTOSLEEP:
+ val |= MAX310X_MODE1_AUTOSLEEP_BIT;
+ break;
+ default:
+ dev_warn(s->port.dev, "Invalid sleep mode %i\n", mode);
+ mutex_unlock(&s->max310x_mutex);
+ return;
+ }
+
+ regmap_write(s->regmap, MAX310X_MODE1_REG, val);
+
+ if ((mode == MAX310X_DISABLE_AUTOSLEEP) ||
+ (mode == MAX310X_DISABLE_FORCED_SLEEP))
+ msleep(MAX310X_WAKEUP_DELAY);
+
+ mutex_unlock(&s->max310x_mutex);
+}
+
+static int max310x_suspend(struct spi_device *spi, pm_message_t state)
+{
+ int ret;
+ struct max310x_port *s = dev_get_drvdata(&spi->dev);
+
+ dev_dbg(&spi->dev, "Suspend\n");
+
+ ret = uart_suspend_port(&s->uart, &s->port);
+
+ if (s->pdata->suspend)
+ s->pdata->suspend(s, 1);
+
+ return ret;
+}
+
+static int max310x_resume(struct spi_device *spi)
+{
+ struct max310x_port *s = dev_get_drvdata(&spi->dev);
+
+ dev_dbg(&spi->dev, "Resume\n");
+
+ if (s->pdata->suspend)
+ s->pdata->suspend(s, 0);
+
+ return uart_resume_port(&s->uart, &s->port);
+}
+
+void max310x_default_suspend(struct max310x_port *s, int do_suspend)
+{
+ dev_dbg(s->port.dev, "Change suspend state to %i\n", do_suspend);
+
+ s->suspended = do_suspend;
+ max310x_set_sleep(s, do_suspend ? MAX310X_ENABLE_AUTOSLEEP :
+ MAX310X_DISABLE_AUTOSLEEP);
+}
+EXPORT_SYMBOL_GPL(max310x_default_suspend);
+
+/* Generic platform data */
+static struct max310x_pdata generic_plat_data = {
+ .driver_flags = MAX310X_EXT_CLK,
+ .uart_flags[0] = MAX310X_ECHO_SUPRESS,
+ .frequency = 26000000,
+ .suspend = &max310x_default_suspend,
+};
+
+static int __devinit max310x_probe(struct spi_device *spi, int chiptype)
+{
+ struct max310x_port *s;
+ struct max310x_pdata *pdata = spi->dev.platform_data;
+ unsigned int val = 0;
+ int ret;
+
+ /* Check for IRQ */
+ if (spi->irq <= 0) {
+ dev_err(&spi->dev, "No IRQ specified\n");
+ return -ENOTSUPP;
+ }
+
+ /* Alloc port structure */
+ s = devm_kzalloc(&spi->dev, sizeof(struct max310x_port), GFP_KERNEL);
+ if (!s) {
+ dev_err(&spi->dev, "Error allocating port structure\n");
+ return -ENOMEM;
+ }
+
+ if (!pdata) {
+ dev_warn(&spi->dev, "No platform data supplied, using defaults\n");
+ pdata = &generic_plat_data;
+ }
+ s->pdata = pdata;
+ dev_set_drvdata(&spi->dev, s);
+
+ /* Individual settings for chips */
+ switch (chiptype) {
+ case MAX310X_TYPE_MAX3107:
+ s->name = "MAX3107";
+ s->nr_gpio = 4;
+ s->uart.nr = 1;
+ s->regcfg.max_register = 0x1f;
+ break;
+ case MAX310X_TYPE_MAX3108:
+ s->name = "MAX3108";
+ s->nr_gpio = 4;
+ s->uart.nr = 1;
+ s->regcfg.max_register = 0x1e;
+ break;
+ }
+
+ /* Check frequency for oscillator */
+ if ((pdata->driver_flags & MAX310X_EXT_CLK) &&
+ ((pdata->frequency < 500000) || (pdata->frequency > 35000000)))
+ goto err_freq;
+ /* Check frequency for quartz */
+ if (!(pdata->driver_flags & MAX310X_EXT_CLK) &&
+ ((pdata->frequency < 1000000) || (pdata->frequency > 4000000)))
+ goto err_freq;
+
+ /* Board specific configure */
+ if (pdata->configure)
+ pdata->configure();
+
+ mutex_init(&s->max310x_mutex);
+
+ /* Setup SPI bus */
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ spi->max_speed_hz = 26000000;
+ spi_setup(spi);
+
+ /* Setup regmap */
+ s->regcfg.reg_bits = 8,
+ s->regcfg.val_bits = 8,
+ s->regcfg.read_flag_mask = 0x00,
+ s->regcfg.write_flag_mask = 0x80,
+ s->regcfg.cache_type = REGCACHE_RBTREE,
+ s->regcfg.writeable_reg = max3107_8_reg_writeable,
+ s->regcfg.volatile_reg = max310x_reg_volatile,
+ s->regcfg.precious_reg = max310x_reg_precious,
+ s->regmap = devm_regmap_init_spi(spi, &s->regcfg);
+ if (IS_ERR(s->regmap)) {
+ ret = PTR_ERR(s->regmap);
+ dev_err(&spi->dev, "Failed to initialize register map: %d\n", ret);
+ goto err_out;
+ }
+
+ /* Reset chip */
+ ret = regmap_write(s->regmap, MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT);
+ if (ret) {
+ dev_err(&spi->dev, "SPI transfer failed: %d\n", ret);
+ goto err_out;
+ }
+ /* Clear chip reset */
+ regmap_write(s->regmap, MAX310X_MODE2_REG, 0);
+
+ if (chiptype == MAX310X_TYPE_MAX3107) {
+ /* Check REV ID to ensure we are talking to what we expect */
+ regmap_read(s->regmap, MAX3107_REVID_REG, &val);
+ if (((val & MAX3107_REV_MASK) != MAX3107_REV_ID)) {
+ dev_err(&spi->dev, "%s ID 0x%02x does not match\n",
+ s->name, val);
+ ret = -ENODEV;
+ goto err_out;
+ }
+ } else if (chiptype == MAX310X_TYPE_MAX3108) {
+ /* MAX3108 have not REV ID register, we just read the reset
+ * register to make sure everything works.
+ */
+ regmap_read(s->regmap, MAX310X_MODE2_REG, &val);
+ if (val) {
+ dev_err(&spi->dev, "%s not present\n", s->name);
+ ret = -ENODEV;
+ goto err_out;
+ }
+ }
+
+ /* Disable all interrupts */
+ regmap_write(s->regmap, MAX310X_IRQEN_REG, 0);
+
+ /* Register UART driver */
+ s->uart.owner = THIS_MODULE,
+ s->uart.driver_name = "ttyMAX",
+ s->uart.dev_name = "ttyMAX",
+ s->uart.major = MAX310X_MAJOR,
+ s->uart.minor = MAX310X_MINOR,
+ s->uart.cons = NULL,
+ ret = uart_register_driver(&s->uart);
+ if (ret) {
+ dev_err(&spi->dev, "Registering UART driver failed: %d\n", ret);
+ goto err_out;
+ }
+
+ /* Initialize UART port data */
+ s->port.line = 0;
+ s->port.dev = &spi->dev;
+ s->port.irq = spi->irq;
+ s->port.type = PORT_MAX310X;
+ s->port.fifosize = MAX310X_FIFO_SIZE;
+ s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE;
+ s->port.iotype = UPIO_PORT;
+ s->port.membase = (void __iomem*)0xffffffff; /* Bogus value */
+ s->port.uartclk = max310x_set_ref_clk(s);
+ s->port.ops = &max310x_ops;
+ uart_add_one_port(&s->uart, &s->port);
+
+ /* Initialize workqueue for start TX */
+ s->wq = create_freezable_workqueue(dev_name(s->port.dev));
+ INIT_WORK(&s->tx_work, max310x_wq_proc);
+
+ /* Go to suspend mode */
+ if (pdata->suspend)
+ pdata->suspend(s, 1);
+
+ return 0;
+
+err_freq:
+ dev_err(&spi->dev, "Frequency parameter incorrect\n");
+ ret = -EINVAL;
+
+err_out:
+ devm_kfree(&spi->dev, s);
+
+ return ret;
+}
+
+static int __devinit max3107_probe(struct spi_device *spi)
+{
+ return max310x_probe(spi, MAX310X_TYPE_MAX3107);
+}
+
+static int __devinit max3108_probe(struct spi_device *spi)
+{
+ return max310x_probe(spi, MAX310X_TYPE_MAX3108);
+}
+
+static int __devexit max310x_remove(struct spi_device *spi)
+{
+ struct max310x_port *s = dev_get_drvdata(&spi->dev);
+
+ dev_dbg(s->port.dev, "Removing port\n");
+
+ destroy_workqueue(s->wq);
+
+ uart_remove_one_port(&s->uart, &s->port);
+ dev_set_drvdata(&spi->dev, NULL);
+
+ uart_unregister_driver(&s->uart);
+
+ devm_kfree(&spi->dev, s);
+
+ return 0;
+}
+
+static struct spi_driver max3107_driver = {
+ .driver = {
+ .name = "max3107",
+ .owner = THIS_MODULE,
+ },
+ .probe = max3107_probe,
+ .remove = __devexit_p(max310x_remove),
+ .suspend = max310x_suspend,
+ .resume = max310x_resume,
+};
+module_spi_driver(max3107_driver);
+
+static struct spi_driver max3108_driver = {
+ .driver = {
+ .name = "max3108",
+ .owner = THIS_MODULE,
+ },
+ .probe = max3108_probe,
+ .remove = __devexit_p(max310x_remove),
+ .suspend = max310x_suspend,
+ .resume = max310x_resume,
+};
+module_spi_driver(max3108_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Aavamobile");
+MODULE_DESCRIPTION("MAX310X UART driver");
diff --git a/include/linux/platform_data/max310x.h b/include/linux/platform_data/max310x.h
new file mode 100644
index 0000000..dcd4c75
--- /dev/null
+++ b/include/linux/platform_data/max310x.h
@@ -0,0 +1,47 @@
+/*
+ * UART protocol driver for Maxim (Dallas) MAX3107/8
+ *
+ * Based on max3100.c
+ * by Christian Pellegrin <chripell@evolware.org>
+ * and max3110.c
+ * by Feng Tang <feng.tang@intel.com>
+ * Rewritten by Alexander Shiyan <shc_work@mail.ru>
+ *
+ * Copyright (C) Aavamobile 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _MAX310X_H_
+#define _MAX310X_H_
+
+struct max310x_port;
+
+#define MAX310X_MAX_UART_PER_DRIVER 1
+
+/* MAX310X platform data structure */
+struct max310x_pdata {
+ /* Flags global to driver */
+ const unsigned int driver_flags;
+#define MAX310X_EXT_CLK (0x00000001) /* External clock enable */
+ /* Flags global to UART port */
+ const unsigned int uart_flags[MAX310X_MAX_UART_PER_DRIVER];
+#define MAX310X_LOOPBACK (0x00000001) /* Loopback mode enable */
+#define MAX310X_ECHO_SUPRESS (0x00000002) /* Enable echo supress */
+#define MAX310X_AUTO_DIR_CTRL (0x00000004) /* Enable Auto direction control (RS-485) */
+ /* Frequency (extrenal clock or crystal) */
+ const int frequency;
+ /* GPIO base number */
+ const unsigned int gpio_base; /* Not implemented yet */
+ /* Called before initialization */
+ void (*configure)(void);
+ /* HW suspend function */
+ void (*suspend)(struct max310x_port *s, int do_suspend);
+};
+
+/* Function that can be reused in board support code */
+void max310x_default_suspend(struct max310x_port *s, int do_suspend);
+
+#endif
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 0253c20..7cf0b68 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -193,8 +193,8 @@
/* SH-SCI */
#define PORT_SCIFB 93
-/* MAX3107 */
-#define PORT_MAX3107 94
+/* MAX310X */
+#define PORT_MAX310X 94
/* High Speed UART for Medfield */
#define PORT_MFD 95
--
1.7.3.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox