1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
21d65c0b1SAlexander Shiyan /*
31d65c0b1SAlexander Shiyan * NXP (Philips) SCC+++(SCN+++) serial driver
41d65c0b1SAlexander Shiyan *
51d65c0b1SAlexander Shiyan * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
61d65c0b1SAlexander Shiyan *
71d65c0b1SAlexander Shiyan * Based on sc26xx.c, by Thomas Bogendörfer (tsbogend@alpha.franken.de)
81d65c0b1SAlexander Shiyan */
91d65c0b1SAlexander Shiyan
1090efa75fSAlexander Shiyan #include <linux/clk.h>
114ce193fdSAlexander Shiyan #include <linux/delay.h>
12eb612fa0SThierry Reding #include <linux/err.h>
131d65c0b1SAlexander Shiyan #include <linux/module.h>
14ac316725SRandy Dunlap #include <linux/mod_devicetable.h>
151d65c0b1SAlexander Shiyan #include <linux/device.h>
16d83b5425SStephen Rothwell #include <linux/console.h>
171d65c0b1SAlexander Shiyan #include <linux/serial_core.h>
181d65c0b1SAlexander Shiyan #include <linux/serial.h>
191d65c0b1SAlexander Shiyan #include <linux/io.h>
201d65c0b1SAlexander Shiyan #include <linux/tty.h>
211d65c0b1SAlexander Shiyan #include <linux/tty_flip.h>
22ec063899SAlexander Shiyan #include <linux/spinlock.h>
231d65c0b1SAlexander Shiyan #include <linux/platform_device.h>
24463dcc42SAlexander Shiyan #include <linux/platform_data/serial-sccnxp.h>
2531815c08SAlexander Shiyan #include <linux/regulator/consumer.h>
261d65c0b1SAlexander Shiyan
271d65c0b1SAlexander Shiyan #define SCCNXP_NAME "uart-sccnxp"
281d65c0b1SAlexander Shiyan #define SCCNXP_MAJOR 204
291d65c0b1SAlexander Shiyan #define SCCNXP_MINOR 205
301d65c0b1SAlexander Shiyan
311d65c0b1SAlexander Shiyan #define SCCNXP_MR_REG (0x00)
321d65c0b1SAlexander Shiyan # define MR0_BAUD_NORMAL (0 << 0)
331d65c0b1SAlexander Shiyan # define MR0_BAUD_EXT1 (1 << 0)
341d65c0b1SAlexander Shiyan # define MR0_BAUD_EXT2 (5 << 0)
351d65c0b1SAlexander Shiyan # define MR0_FIFO (1 << 3)
361d65c0b1SAlexander Shiyan # define MR0_TXLVL (1 << 4)
371d65c0b1SAlexander Shiyan # define MR1_BITS_5 (0 << 0)
381d65c0b1SAlexander Shiyan # define MR1_BITS_6 (1 << 0)
391d65c0b1SAlexander Shiyan # define MR1_BITS_7 (2 << 0)
401d65c0b1SAlexander Shiyan # define MR1_BITS_8 (3 << 0)
411d65c0b1SAlexander Shiyan # define MR1_PAR_EVN (0 << 2)
421d65c0b1SAlexander Shiyan # define MR1_PAR_ODD (1 << 2)
431d65c0b1SAlexander Shiyan # define MR1_PAR_NO (4 << 2)
441d65c0b1SAlexander Shiyan # define MR2_STOP1 (7 << 0)
451d65c0b1SAlexander Shiyan # define MR2_STOP2 (0xf << 0)
461d65c0b1SAlexander Shiyan #define SCCNXP_SR_REG (0x01)
471d65c0b1SAlexander Shiyan # define SR_RXRDY (1 << 0)
481d65c0b1SAlexander Shiyan # define SR_FULL (1 << 1)
491d65c0b1SAlexander Shiyan # define SR_TXRDY (1 << 2)
501d65c0b1SAlexander Shiyan # define SR_TXEMT (1 << 3)
511d65c0b1SAlexander Shiyan # define SR_OVR (1 << 4)
521d65c0b1SAlexander Shiyan # define SR_PE (1 << 5)
531d65c0b1SAlexander Shiyan # define SR_FE (1 << 6)
541d65c0b1SAlexander Shiyan # define SR_BRK (1 << 7)
55efa0f494SAlexander Shiyan #define SCCNXP_CSR_REG (SCCNXP_SR_REG)
56efa0f494SAlexander Shiyan # define CSR_TIMER_MODE (0x0d)
571d65c0b1SAlexander Shiyan #define SCCNXP_CR_REG (0x02)
581d65c0b1SAlexander Shiyan # define CR_RX_ENABLE (1 << 0)
591d65c0b1SAlexander Shiyan # define CR_RX_DISABLE (1 << 1)
601d65c0b1SAlexander Shiyan # define CR_TX_ENABLE (1 << 2)
611d65c0b1SAlexander Shiyan # define CR_TX_DISABLE (1 << 3)
621d65c0b1SAlexander Shiyan # define CR_CMD_MRPTR1 (0x01 << 4)
631d65c0b1SAlexander Shiyan # define CR_CMD_RX_RESET (0x02 << 4)
641d65c0b1SAlexander Shiyan # define CR_CMD_TX_RESET (0x03 << 4)
651d65c0b1SAlexander Shiyan # define CR_CMD_STATUS_RESET (0x04 << 4)
661d65c0b1SAlexander Shiyan # define CR_CMD_BREAK_RESET (0x05 << 4)
671d65c0b1SAlexander Shiyan # define CR_CMD_START_BREAK (0x06 << 4)
681d65c0b1SAlexander Shiyan # define CR_CMD_STOP_BREAK (0x07 << 4)
691d65c0b1SAlexander Shiyan # define CR_CMD_MRPTR0 (0x0b << 4)
701d65c0b1SAlexander Shiyan #define SCCNXP_RHR_REG (0x03)
711d65c0b1SAlexander Shiyan #define SCCNXP_THR_REG SCCNXP_RHR_REG
721d65c0b1SAlexander Shiyan #define SCCNXP_IPCR_REG (0x04)
731d65c0b1SAlexander Shiyan #define SCCNXP_ACR_REG SCCNXP_IPCR_REG
741d65c0b1SAlexander Shiyan # define ACR_BAUD0 (0 << 7)
751d65c0b1SAlexander Shiyan # define ACR_BAUD1 (1 << 7)
761d65c0b1SAlexander Shiyan # define ACR_TIMER_MODE (6 << 4)
771d65c0b1SAlexander Shiyan #define SCCNXP_ISR_REG (0x05)
781d65c0b1SAlexander Shiyan #define SCCNXP_IMR_REG SCCNXP_ISR_REG
791d65c0b1SAlexander Shiyan # define IMR_TXRDY (1 << 0)
801d65c0b1SAlexander Shiyan # define IMR_RXRDY (1 << 1)
811d65c0b1SAlexander Shiyan # define ISR_TXRDY(x) (1 << ((x * 4) + 0))
821d65c0b1SAlexander Shiyan # define ISR_RXRDY(x) (1 << ((x * 4) + 1))
83efa0f494SAlexander Shiyan #define SCCNXP_CTPU_REG (0x06)
84efa0f494SAlexander Shiyan #define SCCNXP_CTPL_REG (0x07)
851d65c0b1SAlexander Shiyan #define SCCNXP_IPR_REG (0x0d)
861d65c0b1SAlexander Shiyan #define SCCNXP_OPCR_REG SCCNXP_IPR_REG
871d65c0b1SAlexander Shiyan #define SCCNXP_SOP_REG (0x0e)
88efa0f494SAlexander Shiyan #define SCCNXP_START_COUNTER_REG SCCNXP_SOP_REG
891d65c0b1SAlexander Shiyan #define SCCNXP_ROP_REG (0x0f)
901d65c0b1SAlexander Shiyan
911d65c0b1SAlexander Shiyan /* Route helpers */
921d65c0b1SAlexander Shiyan #define MCTRL_MASK(sig) (0xf << (sig))
931d65c0b1SAlexander Shiyan #define MCTRL_IBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_IP0)
941d65c0b1SAlexander Shiyan #define MCTRL_OBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_OP0)
951d65c0b1SAlexander Shiyan
96ea4c39beSAlexander Shiyan #define SCCNXP_HAVE_IO 0x00000001
97ea4c39beSAlexander Shiyan #define SCCNXP_HAVE_MR0 0x00000002
98ea4c39beSAlexander Shiyan
99ea4c39beSAlexander Shiyan struct sccnxp_chip {
100ea4c39beSAlexander Shiyan const char *name;
101ea4c39beSAlexander Shiyan unsigned int nr;
102ea4c39beSAlexander Shiyan unsigned long freq_min;
103ea4c39beSAlexander Shiyan unsigned long freq_std;
104ea4c39beSAlexander Shiyan unsigned long freq_max;
105ea4c39beSAlexander Shiyan unsigned int flags;
106ea4c39beSAlexander Shiyan unsigned int fifosize;
1074ce193fdSAlexander Shiyan /* Time between read/write cycles */
1084ce193fdSAlexander Shiyan unsigned int trwd;
1091d65c0b1SAlexander Shiyan };
1101d65c0b1SAlexander Shiyan
1111d65c0b1SAlexander Shiyan struct sccnxp_port {
1121d65c0b1SAlexander Shiyan struct uart_driver uart;
1131d65c0b1SAlexander Shiyan struct uart_port port[SCCNXP_MAX_UARTS];
114ec063899SAlexander Shiyan bool opened[SCCNXP_MAX_UARTS];
1151d65c0b1SAlexander Shiyan
1161d65c0b1SAlexander Shiyan int irq;
1171d65c0b1SAlexander Shiyan u8 imr;
1181d65c0b1SAlexander Shiyan
119ea4c39beSAlexander Shiyan struct sccnxp_chip *chip;
1201d65c0b1SAlexander Shiyan
1211d65c0b1SAlexander Shiyan #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
1221d65c0b1SAlexander Shiyan struct console console;
1231d65c0b1SAlexander Shiyan #endif
1241d65c0b1SAlexander Shiyan
125ec063899SAlexander Shiyan spinlock_t lock;
126ec063899SAlexander Shiyan
127ec063899SAlexander Shiyan bool poll;
128ec063899SAlexander Shiyan struct timer_list timer;
1291d65c0b1SAlexander Shiyan
1301d65c0b1SAlexander Shiyan struct sccnxp_pdata pdata;
13131815c08SAlexander Shiyan
13231815c08SAlexander Shiyan struct regulator *regulator;
1331d65c0b1SAlexander Shiyan };
1341d65c0b1SAlexander Shiyan
135ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc2681 = {
136ea4c39beSAlexander Shiyan .name = "SC2681",
137ea4c39beSAlexander Shiyan .nr = 2,
138ea4c39beSAlexander Shiyan .freq_min = 1000000,
139ea4c39beSAlexander Shiyan .freq_std = 3686400,
140ea4c39beSAlexander Shiyan .freq_max = 4000000,
141ea4c39beSAlexander Shiyan .flags = SCCNXP_HAVE_IO,
142ea4c39beSAlexander Shiyan .fifosize = 3,
1434ce193fdSAlexander Shiyan .trwd = 200,
144ea4c39beSAlexander Shiyan };
1451d65c0b1SAlexander Shiyan
146ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc2691 = {
147ea4c39beSAlexander Shiyan .name = "SC2691",
148ea4c39beSAlexander Shiyan .nr = 1,
149ea4c39beSAlexander Shiyan .freq_min = 1000000,
150ea4c39beSAlexander Shiyan .freq_std = 3686400,
151ea4c39beSAlexander Shiyan .freq_max = 4000000,
152ea4c39beSAlexander Shiyan .flags = 0,
153ea4c39beSAlexander Shiyan .fifosize = 3,
1544ce193fdSAlexander Shiyan .trwd = 150,
155ea4c39beSAlexander Shiyan };
156ea4c39beSAlexander Shiyan
157ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc2692 = {
158ea4c39beSAlexander Shiyan .name = "SC2692",
159ea4c39beSAlexander Shiyan .nr = 2,
160ea4c39beSAlexander Shiyan .freq_min = 1000000,
161ea4c39beSAlexander Shiyan .freq_std = 3686400,
162ea4c39beSAlexander Shiyan .freq_max = 4000000,
163ea4c39beSAlexander Shiyan .flags = SCCNXP_HAVE_IO,
164ea4c39beSAlexander Shiyan .fifosize = 3,
1654ce193fdSAlexander Shiyan .trwd = 30,
166ea4c39beSAlexander Shiyan };
167ea4c39beSAlexander Shiyan
168ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc2891 = {
169ea4c39beSAlexander Shiyan .name = "SC2891",
170ea4c39beSAlexander Shiyan .nr = 1,
171ea4c39beSAlexander Shiyan .freq_min = 100000,
172ea4c39beSAlexander Shiyan .freq_std = 3686400,
173ea4c39beSAlexander Shiyan .freq_max = 8000000,
174ea4c39beSAlexander Shiyan .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
175ea4c39beSAlexander Shiyan .fifosize = 16,
1764ce193fdSAlexander Shiyan .trwd = 27,
177ea4c39beSAlexander Shiyan };
178ea4c39beSAlexander Shiyan
179ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc2892 = {
180ea4c39beSAlexander Shiyan .name = "SC2892",
181ea4c39beSAlexander Shiyan .nr = 2,
182ea4c39beSAlexander Shiyan .freq_min = 100000,
183ea4c39beSAlexander Shiyan .freq_std = 3686400,
184ea4c39beSAlexander Shiyan .freq_max = 8000000,
185ea4c39beSAlexander Shiyan .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
186ea4c39beSAlexander Shiyan .fifosize = 16,
1874ce193fdSAlexander Shiyan .trwd = 17,
188ea4c39beSAlexander Shiyan };
189ea4c39beSAlexander Shiyan
190ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc28202 = {
191ea4c39beSAlexander Shiyan .name = "SC28202",
192ea4c39beSAlexander Shiyan .nr = 2,
193ea4c39beSAlexander Shiyan .freq_min = 1000000,
194ea4c39beSAlexander Shiyan .freq_std = 14745600,
195ea4c39beSAlexander Shiyan .freq_max = 50000000,
196ea4c39beSAlexander Shiyan .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
197ea4c39beSAlexander Shiyan .fifosize = 256,
1984ce193fdSAlexander Shiyan .trwd = 10,
199ea4c39beSAlexander Shiyan };
200ea4c39beSAlexander Shiyan
201ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc68681 = {
202ea4c39beSAlexander Shiyan .name = "SC68681",
203ea4c39beSAlexander Shiyan .nr = 2,
204ea4c39beSAlexander Shiyan .freq_min = 1000000,
205ea4c39beSAlexander Shiyan .freq_std = 3686400,
206ea4c39beSAlexander Shiyan .freq_max = 4000000,
207ea4c39beSAlexander Shiyan .flags = SCCNXP_HAVE_IO,
208ea4c39beSAlexander Shiyan .fifosize = 3,
2094ce193fdSAlexander Shiyan .trwd = 200,
210ea4c39beSAlexander Shiyan };
211ea4c39beSAlexander Shiyan
212ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc68692 = {
213ea4c39beSAlexander Shiyan .name = "SC68692",
214ea4c39beSAlexander Shiyan .nr = 2,
215ea4c39beSAlexander Shiyan .freq_min = 1000000,
216ea4c39beSAlexander Shiyan .freq_std = 3686400,
217ea4c39beSAlexander Shiyan .freq_max = 4000000,
218ea4c39beSAlexander Shiyan .flags = SCCNXP_HAVE_IO,
219ea4c39beSAlexander Shiyan .fifosize = 3,
2204ce193fdSAlexander Shiyan .trwd = 200,
221ea4c39beSAlexander Shiyan };
2221d65c0b1SAlexander Shiyan
sccnxp_read(struct uart_port * port,u8 reg)2234ce193fdSAlexander Shiyan static u8 sccnxp_read(struct uart_port *port, u8 reg)
2241d65c0b1SAlexander Shiyan {
2254ce193fdSAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
2264ce193fdSAlexander Shiyan u8 ret;
2274ce193fdSAlexander Shiyan
2284ce193fdSAlexander Shiyan ret = readb(port->membase + (reg << port->regshift));
2294ce193fdSAlexander Shiyan
2304ce193fdSAlexander Shiyan ndelay(s->chip->trwd);
2314ce193fdSAlexander Shiyan
2324ce193fdSAlexander Shiyan return ret;
2331d65c0b1SAlexander Shiyan }
2341d65c0b1SAlexander Shiyan
sccnxp_write(struct uart_port * port,u8 reg,u8 v)2354ce193fdSAlexander Shiyan static void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
2361d65c0b1SAlexander Shiyan {
2374ce193fdSAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
2384ce193fdSAlexander Shiyan
239ea4c39beSAlexander Shiyan writeb(v, port->membase + (reg << port->regshift));
2404ce193fdSAlexander Shiyan
2414ce193fdSAlexander Shiyan ndelay(s->chip->trwd);
2421d65c0b1SAlexander Shiyan }
2431d65c0b1SAlexander Shiyan
sccnxp_port_read(struct uart_port * port,u8 reg)2444ce193fdSAlexander Shiyan static u8 sccnxp_port_read(struct uart_port *port, u8 reg)
2451d65c0b1SAlexander Shiyan {
2461d65c0b1SAlexander Shiyan return sccnxp_read(port, (port->line << 3) + reg);
2471d65c0b1SAlexander Shiyan }
2481d65c0b1SAlexander Shiyan
sccnxp_port_write(struct uart_port * port,u8 reg,u8 v)2494ce193fdSAlexander Shiyan static void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v)
2501d65c0b1SAlexander Shiyan {
2511d65c0b1SAlexander Shiyan sccnxp_write(port, (port->line << 3) + reg, v);
2521d65c0b1SAlexander Shiyan }
2531d65c0b1SAlexander Shiyan
sccnxp_update_best_err(int a,int b,int * besterr)2541d65c0b1SAlexander Shiyan static int sccnxp_update_best_err(int a, int b, int *besterr)
2551d65c0b1SAlexander Shiyan {
2561d65c0b1SAlexander Shiyan int err = abs(a - b);
2571d65c0b1SAlexander Shiyan
258efa0f494SAlexander Shiyan if (*besterr > err) {
2591d65c0b1SAlexander Shiyan *besterr = err;
2601d65c0b1SAlexander Shiyan return 0;
2611d65c0b1SAlexander Shiyan }
2621d65c0b1SAlexander Shiyan
2631d65c0b1SAlexander Shiyan return 1;
2641d65c0b1SAlexander Shiyan }
2651d65c0b1SAlexander Shiyan
2664bbed6bcSAlexander Shiyan static const struct {
2671d65c0b1SAlexander Shiyan u8 csr;
2681d65c0b1SAlexander Shiyan u8 acr;
2691d65c0b1SAlexander Shiyan u8 mr0;
2701d65c0b1SAlexander Shiyan int baud;
2714bbed6bcSAlexander Shiyan } baud_std[] = {
2721d65c0b1SAlexander Shiyan { 0, ACR_BAUD0, MR0_BAUD_NORMAL, 50, },
2731d65c0b1SAlexander Shiyan { 0, ACR_BAUD1, MR0_BAUD_NORMAL, 75, },
2741d65c0b1SAlexander Shiyan { 1, ACR_BAUD0, MR0_BAUD_NORMAL, 110, },
2751d65c0b1SAlexander Shiyan { 2, ACR_BAUD0, MR0_BAUD_NORMAL, 134, },
2761d65c0b1SAlexander Shiyan { 3, ACR_BAUD1, MR0_BAUD_NORMAL, 150, },
2771d65c0b1SAlexander Shiyan { 3, ACR_BAUD0, MR0_BAUD_NORMAL, 200, },
2781d65c0b1SAlexander Shiyan { 4, ACR_BAUD0, MR0_BAUD_NORMAL, 300, },
2791d65c0b1SAlexander Shiyan { 0, ACR_BAUD1, MR0_BAUD_EXT1, 450, },
2801d65c0b1SAlexander Shiyan { 1, ACR_BAUD0, MR0_BAUD_EXT2, 880, },
2811d65c0b1SAlexander Shiyan { 3, ACR_BAUD1, MR0_BAUD_EXT1, 900, },
2821d65c0b1SAlexander Shiyan { 5, ACR_BAUD0, MR0_BAUD_NORMAL, 600, },
2831d65c0b1SAlexander Shiyan { 7, ACR_BAUD0, MR0_BAUD_NORMAL, 1050, },
2841d65c0b1SAlexander Shiyan { 2, ACR_BAUD0, MR0_BAUD_EXT2, 1076, },
2851d65c0b1SAlexander Shiyan { 6, ACR_BAUD0, MR0_BAUD_NORMAL, 1200, },
2861d65c0b1SAlexander Shiyan { 10, ACR_BAUD1, MR0_BAUD_NORMAL, 1800, },
2871d65c0b1SAlexander Shiyan { 7, ACR_BAUD1, MR0_BAUD_NORMAL, 2000, },
2881d65c0b1SAlexander Shiyan { 8, ACR_BAUD0, MR0_BAUD_NORMAL, 2400, },
2891d65c0b1SAlexander Shiyan { 5, ACR_BAUD1, MR0_BAUD_EXT1, 3600, },
2901d65c0b1SAlexander Shiyan { 9, ACR_BAUD0, MR0_BAUD_NORMAL, 4800, },
2911d65c0b1SAlexander Shiyan { 10, ACR_BAUD0, MR0_BAUD_NORMAL, 7200, },
2921d65c0b1SAlexander Shiyan { 11, ACR_BAUD0, MR0_BAUD_NORMAL, 9600, },
2931d65c0b1SAlexander Shiyan { 8, ACR_BAUD0, MR0_BAUD_EXT1, 14400, },
2941d65c0b1SAlexander Shiyan { 12, ACR_BAUD1, MR0_BAUD_NORMAL, 19200, },
2951d65c0b1SAlexander Shiyan { 9, ACR_BAUD0, MR0_BAUD_EXT1, 28800, },
2961d65c0b1SAlexander Shiyan { 12, ACR_BAUD0, MR0_BAUD_NORMAL, 38400, },
2971d65c0b1SAlexander Shiyan { 11, ACR_BAUD0, MR0_BAUD_EXT1, 57600, },
2981d65c0b1SAlexander Shiyan { 12, ACR_BAUD1, MR0_BAUD_EXT1, 115200, },
2991d65c0b1SAlexander Shiyan { 12, ACR_BAUD0, MR0_BAUD_EXT1, 230400, },
3001d65c0b1SAlexander Shiyan { 0, 0, 0, 0 }
3011d65c0b1SAlexander Shiyan };
3021d65c0b1SAlexander Shiyan
sccnxp_set_baud(struct uart_port * port,int baud)3031685118fSAlexander Shiyan static int sccnxp_set_baud(struct uart_port *port, int baud)
3041d65c0b1SAlexander Shiyan {
3051d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
306efa0f494SAlexander Shiyan int div_std, tmp_baud, bestbaud = INT_MAX, besterr = INT_MAX;
307ea4c39beSAlexander Shiyan struct sccnxp_chip *chip = s->chip;
3081d65c0b1SAlexander Shiyan u8 i, acr = 0, csr = 0, mr0 = 0;
3091d65c0b1SAlexander Shiyan
310efa0f494SAlexander Shiyan /* Find divisor to load to the timer preset registers */
311efa0f494SAlexander Shiyan div_std = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * baud);
312efa0f494SAlexander Shiyan if ((div_std >= 2) && (div_std <= 0xffff)) {
313efa0f494SAlexander Shiyan bestbaud = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * div_std);
314efa0f494SAlexander Shiyan sccnxp_update_best_err(baud, bestbaud, &besterr);
315efa0f494SAlexander Shiyan csr = CSR_TIMER_MODE;
316efa0f494SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CTPU_REG, div_std >> 8);
317efa0f494SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CTPL_REG, div_std);
318efa0f494SAlexander Shiyan /* Issue start timer/counter command */
319efa0f494SAlexander Shiyan sccnxp_port_read(port, SCCNXP_START_COUNTER_REG);
320efa0f494SAlexander Shiyan }
321efa0f494SAlexander Shiyan
3221d65c0b1SAlexander Shiyan /* Find best baud from table */
3231d65c0b1SAlexander Shiyan for (i = 0; baud_std[i].baud && besterr; i++) {
324ea4c39beSAlexander Shiyan if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0))
3251d65c0b1SAlexander Shiyan continue;
326ea4c39beSAlexander Shiyan div_std = DIV_ROUND_CLOSEST(chip->freq_std, baud_std[i].baud);
3271d65c0b1SAlexander Shiyan tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std);
3281d65c0b1SAlexander Shiyan if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) {
3291d65c0b1SAlexander Shiyan acr = baud_std[i].acr;
3301d65c0b1SAlexander Shiyan csr = baud_std[i].csr;
3311d65c0b1SAlexander Shiyan mr0 = baud_std[i].mr0;
3321d65c0b1SAlexander Shiyan bestbaud = tmp_baud;
3331d65c0b1SAlexander Shiyan }
3341d65c0b1SAlexander Shiyan }
3351d65c0b1SAlexander Shiyan
336ea4c39beSAlexander Shiyan if (chip->flags & SCCNXP_HAVE_MR0) {
3371d65c0b1SAlexander Shiyan /* Enable FIFO, set half level for TX */
3381d65c0b1SAlexander Shiyan mr0 |= MR0_FIFO | MR0_TXLVL;
3391d65c0b1SAlexander Shiyan /* Update MR0 */
3401d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR0);
3411d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_MR_REG, mr0);
3421d65c0b1SAlexander Shiyan }
3431d65c0b1SAlexander Shiyan
3441d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_ACR_REG, acr | ACR_TIMER_MODE);
3451d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CSR_REG, (csr << 4) | csr);
3461d65c0b1SAlexander Shiyan
3471685118fSAlexander Shiyan if (baud != bestbaud)
3481d65c0b1SAlexander Shiyan dev_dbg(port->dev, "Baudrate desired: %i, calculated: %i\n",
3491d65c0b1SAlexander Shiyan baud, bestbaud);
3501685118fSAlexander Shiyan
3511685118fSAlexander Shiyan return bestbaud;
3521d65c0b1SAlexander Shiyan }
3531d65c0b1SAlexander Shiyan
sccnxp_enable_irq(struct uart_port * port,int mask)3541d65c0b1SAlexander Shiyan static void sccnxp_enable_irq(struct uart_port *port, int mask)
3551d65c0b1SAlexander Shiyan {
3561d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
3571d65c0b1SAlexander Shiyan
3581d65c0b1SAlexander Shiyan s->imr |= mask << (port->line * 4);
3591d65c0b1SAlexander Shiyan sccnxp_write(port, SCCNXP_IMR_REG, s->imr);
3601d65c0b1SAlexander Shiyan }
3611d65c0b1SAlexander Shiyan
sccnxp_disable_irq(struct uart_port * port,int mask)3621d65c0b1SAlexander Shiyan static void sccnxp_disable_irq(struct uart_port *port, int mask)
3631d65c0b1SAlexander Shiyan {
3641d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
3651d65c0b1SAlexander Shiyan
3661d65c0b1SAlexander Shiyan s->imr &= ~(mask << (port->line * 4));
3671d65c0b1SAlexander Shiyan sccnxp_write(port, SCCNXP_IMR_REG, s->imr);
3681d65c0b1SAlexander Shiyan }
3691d65c0b1SAlexander Shiyan
sccnxp_set_bit(struct uart_port * port,int sig,int state)3701d65c0b1SAlexander Shiyan static void sccnxp_set_bit(struct uart_port *port, int sig, int state)
3711d65c0b1SAlexander Shiyan {
3721d65c0b1SAlexander Shiyan u8 bitmask;
3731d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
3741d65c0b1SAlexander Shiyan
3751d65c0b1SAlexander Shiyan if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(sig)) {
3761d65c0b1SAlexander Shiyan bitmask = 1 << MCTRL_OBIT(s->pdata.mctrl_cfg[port->line], sig);
3771d65c0b1SAlexander Shiyan if (state)
3781d65c0b1SAlexander Shiyan sccnxp_write(port, SCCNXP_SOP_REG, bitmask);
3791d65c0b1SAlexander Shiyan else
3801d65c0b1SAlexander Shiyan sccnxp_write(port, SCCNXP_ROP_REG, bitmask);
3811d65c0b1SAlexander Shiyan }
3821d65c0b1SAlexander Shiyan }
3831d65c0b1SAlexander Shiyan
sccnxp_handle_rx(struct uart_port * port)3841d65c0b1SAlexander Shiyan static void sccnxp_handle_rx(struct uart_port *port)
3851d65c0b1SAlexander Shiyan {
386fd2b55f8SJiri Slaby u8 sr, ch, flag;
3871d65c0b1SAlexander Shiyan
3881d65c0b1SAlexander Shiyan for (;;) {
3891d65c0b1SAlexander Shiyan sr = sccnxp_port_read(port, SCCNXP_SR_REG);
3901d65c0b1SAlexander Shiyan if (!(sr & SR_RXRDY))
3911d65c0b1SAlexander Shiyan break;
3921d65c0b1SAlexander Shiyan sr &= SR_PE | SR_FE | SR_OVR | SR_BRK;
3931d65c0b1SAlexander Shiyan
3941d65c0b1SAlexander Shiyan ch = sccnxp_port_read(port, SCCNXP_RHR_REG);
3951d65c0b1SAlexander Shiyan
3961d65c0b1SAlexander Shiyan port->icount.rx++;
3971d65c0b1SAlexander Shiyan flag = TTY_NORMAL;
3981d65c0b1SAlexander Shiyan
3991d65c0b1SAlexander Shiyan if (unlikely(sr)) {
4001d65c0b1SAlexander Shiyan if (sr & SR_BRK) {
4011d65c0b1SAlexander Shiyan port->icount.brk++;
402f548b96dSAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG,
403f548b96dSAlexander Shiyan CR_CMD_BREAK_RESET);
4041d65c0b1SAlexander Shiyan if (uart_handle_break(port))
4051d65c0b1SAlexander Shiyan continue;
4061d65c0b1SAlexander Shiyan } else if (sr & SR_PE)
4071d65c0b1SAlexander Shiyan port->icount.parity++;
4081d65c0b1SAlexander Shiyan else if (sr & SR_FE)
4091d65c0b1SAlexander Shiyan port->icount.frame++;
410f548b96dSAlexander Shiyan else if (sr & SR_OVR) {
4111d65c0b1SAlexander Shiyan port->icount.overrun++;
412f548b96dSAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG,
413f548b96dSAlexander Shiyan CR_CMD_STATUS_RESET);
414f548b96dSAlexander Shiyan }
4151d65c0b1SAlexander Shiyan
4161d65c0b1SAlexander Shiyan sr &= port->read_status_mask;
4171d65c0b1SAlexander Shiyan if (sr & SR_BRK)
4181d65c0b1SAlexander Shiyan flag = TTY_BREAK;
4191d65c0b1SAlexander Shiyan else if (sr & SR_PE)
4201d65c0b1SAlexander Shiyan flag = TTY_PARITY;
4211d65c0b1SAlexander Shiyan else if (sr & SR_FE)
4221d65c0b1SAlexander Shiyan flag = TTY_FRAME;
4231d65c0b1SAlexander Shiyan else if (sr & SR_OVR)
4241d65c0b1SAlexander Shiyan flag = TTY_OVERRUN;
4251d65c0b1SAlexander Shiyan }
4261d65c0b1SAlexander Shiyan
4271d65c0b1SAlexander Shiyan if (uart_handle_sysrq_char(port, ch))
4281d65c0b1SAlexander Shiyan continue;
4291d65c0b1SAlexander Shiyan
4301d65c0b1SAlexander Shiyan if (sr & port->ignore_status_mask)
4311d65c0b1SAlexander Shiyan continue;
4321d65c0b1SAlexander Shiyan
4331d65c0b1SAlexander Shiyan uart_insert_char(port, sr, SR_OVR, ch, flag);
4341d65c0b1SAlexander Shiyan }
4351d65c0b1SAlexander Shiyan
4362e124b4aSJiri Slaby tty_flip_buffer_push(&port->state->port);
4371d65c0b1SAlexander Shiyan }
4381d65c0b1SAlexander Shiyan
sccnxp_handle_tx(struct uart_port * port)4391d65c0b1SAlexander Shiyan static void sccnxp_handle_tx(struct uart_port *port)
4401d65c0b1SAlexander Shiyan {
4411d65c0b1SAlexander Shiyan u8 sr;
4421d65c0b1SAlexander Shiyan struct circ_buf *xmit = &port->state->xmit;
4431d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
4441d65c0b1SAlexander Shiyan
4451d65c0b1SAlexander Shiyan if (unlikely(port->x_char)) {
4461d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_THR_REG, port->x_char);
4471d65c0b1SAlexander Shiyan port->icount.tx++;
4481d65c0b1SAlexander Shiyan port->x_char = 0;
4491d65c0b1SAlexander Shiyan return;
4501d65c0b1SAlexander Shiyan }
4511d65c0b1SAlexander Shiyan
4521d65c0b1SAlexander Shiyan if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
4531d65c0b1SAlexander Shiyan /* Disable TX if FIFO is empty */
4541d65c0b1SAlexander Shiyan if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXEMT) {
4551d65c0b1SAlexander Shiyan sccnxp_disable_irq(port, IMR_TXRDY);
4561d65c0b1SAlexander Shiyan
4571d65c0b1SAlexander Shiyan /* Set direction to input */
458ea4c39beSAlexander Shiyan if (s->chip->flags & SCCNXP_HAVE_IO)
4591d65c0b1SAlexander Shiyan sccnxp_set_bit(port, DIR_OP, 0);
4601d65c0b1SAlexander Shiyan }
4611d65c0b1SAlexander Shiyan return;
4621d65c0b1SAlexander Shiyan }
4631d65c0b1SAlexander Shiyan
4641d65c0b1SAlexander Shiyan while (!uart_circ_empty(xmit)) {
4651d65c0b1SAlexander Shiyan sr = sccnxp_port_read(port, SCCNXP_SR_REG);
4661d65c0b1SAlexander Shiyan if (!(sr & SR_TXRDY))
4671d65c0b1SAlexander Shiyan break;
4681d65c0b1SAlexander Shiyan
4691d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_THR_REG, xmit->buf[xmit->tail]);
4703ea03c02SIlpo Järvinen uart_xmit_advance(port, 1);
4711d65c0b1SAlexander Shiyan }
4721d65c0b1SAlexander Shiyan
4731d65c0b1SAlexander Shiyan if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
4741d65c0b1SAlexander Shiyan uart_write_wakeup(port);
4751d65c0b1SAlexander Shiyan }
4761d65c0b1SAlexander Shiyan
sccnxp_handle_events(struct sccnxp_port * s)477ec063899SAlexander Shiyan static void sccnxp_handle_events(struct sccnxp_port *s)
4781d65c0b1SAlexander Shiyan {
4791d65c0b1SAlexander Shiyan int i;
4801d65c0b1SAlexander Shiyan u8 isr;
4811d65c0b1SAlexander Shiyan
482ec063899SAlexander Shiyan do {
4831d65c0b1SAlexander Shiyan isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG);
4841d65c0b1SAlexander Shiyan isr &= s->imr;
4851d65c0b1SAlexander Shiyan if (!isr)
4861d65c0b1SAlexander Shiyan break;
4871d65c0b1SAlexander Shiyan
4881d65c0b1SAlexander Shiyan for (i = 0; i < s->uart.nr; i++) {
489ec063899SAlexander Shiyan if (s->opened[i] && (isr & ISR_RXRDY(i)))
4901d65c0b1SAlexander Shiyan sccnxp_handle_rx(&s->port[i]);
491ec063899SAlexander Shiyan if (s->opened[i] && (isr & ISR_TXRDY(i)))
4921d65c0b1SAlexander Shiyan sccnxp_handle_tx(&s->port[i]);
4931d65c0b1SAlexander Shiyan }
494ec063899SAlexander Shiyan } while (1);
4951d65c0b1SAlexander Shiyan }
4961d65c0b1SAlexander Shiyan
sccnxp_timer(struct timer_list * t)497fc3b00d7SKees Cook static void sccnxp_timer(struct timer_list *t)
498ec063899SAlexander Shiyan {
499fc3b00d7SKees Cook struct sccnxp_port *s = from_timer(s, t, timer);
500ec063899SAlexander Shiyan unsigned long flags;
501ec063899SAlexander Shiyan
502ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
503ec063899SAlexander Shiyan sccnxp_handle_events(s);
504ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
505ec063899SAlexander Shiyan
506bee18bdcSAlexander Shiyan mod_timer(&s->timer, jiffies + usecs_to_jiffies(s->pdata.poll_time_us));
507ec063899SAlexander Shiyan }
508ec063899SAlexander Shiyan
sccnxp_ist(int irq,void * dev_id)509ec063899SAlexander Shiyan static irqreturn_t sccnxp_ist(int irq, void *dev_id)
510ec063899SAlexander Shiyan {
511ec063899SAlexander Shiyan struct sccnxp_port *s = (struct sccnxp_port *)dev_id;
512ec063899SAlexander Shiyan unsigned long flags;
513ec063899SAlexander Shiyan
514ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
515ec063899SAlexander Shiyan sccnxp_handle_events(s);
516ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
5171d65c0b1SAlexander Shiyan
5181d65c0b1SAlexander Shiyan return IRQ_HANDLED;
5191d65c0b1SAlexander Shiyan }
5201d65c0b1SAlexander Shiyan
sccnxp_start_tx(struct uart_port * port)5211d65c0b1SAlexander Shiyan static void sccnxp_start_tx(struct uart_port *port)
5221d65c0b1SAlexander Shiyan {
5231d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
524ec063899SAlexander Shiyan unsigned long flags;
5251d65c0b1SAlexander Shiyan
526ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
5271d65c0b1SAlexander Shiyan
5281d65c0b1SAlexander Shiyan /* Set direction to output */
529ea4c39beSAlexander Shiyan if (s->chip->flags & SCCNXP_HAVE_IO)
5301d65c0b1SAlexander Shiyan sccnxp_set_bit(port, DIR_OP, 1);
5311d65c0b1SAlexander Shiyan
5321d65c0b1SAlexander Shiyan sccnxp_enable_irq(port, IMR_TXRDY);
5331d65c0b1SAlexander Shiyan
534ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
5351d65c0b1SAlexander Shiyan }
5361d65c0b1SAlexander Shiyan
sccnxp_stop_tx(struct uart_port * port)5371d65c0b1SAlexander Shiyan static void sccnxp_stop_tx(struct uart_port *port)
5381d65c0b1SAlexander Shiyan {
5391d65c0b1SAlexander Shiyan /* Do nothing */
5401d65c0b1SAlexander Shiyan }
5411d65c0b1SAlexander Shiyan
sccnxp_stop_rx(struct uart_port * port)5421d65c0b1SAlexander Shiyan static void sccnxp_stop_rx(struct uart_port *port)
5431d65c0b1SAlexander Shiyan {
5441d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
545ec063899SAlexander Shiyan unsigned long flags;
5461d65c0b1SAlexander Shiyan
547ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
5481d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE);
549ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
5501d65c0b1SAlexander Shiyan }
5511d65c0b1SAlexander Shiyan
sccnxp_tx_empty(struct uart_port * port)5521d65c0b1SAlexander Shiyan static unsigned int sccnxp_tx_empty(struct uart_port *port)
5531d65c0b1SAlexander Shiyan {
5541d65c0b1SAlexander Shiyan u8 val;
555ec063899SAlexander Shiyan unsigned long flags;
5561d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
5571d65c0b1SAlexander Shiyan
558ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
5591d65c0b1SAlexander Shiyan val = sccnxp_port_read(port, SCCNXP_SR_REG);
560ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
5611d65c0b1SAlexander Shiyan
5621d65c0b1SAlexander Shiyan return (val & SR_TXEMT) ? TIOCSER_TEMT : 0;
5631d65c0b1SAlexander Shiyan }
5641d65c0b1SAlexander Shiyan
sccnxp_set_mctrl(struct uart_port * port,unsigned int mctrl)5651d65c0b1SAlexander Shiyan static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl)
5661d65c0b1SAlexander Shiyan {
5671d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
568ec063899SAlexander Shiyan unsigned long flags;
5691d65c0b1SAlexander Shiyan
570ea4c39beSAlexander Shiyan if (!(s->chip->flags & SCCNXP_HAVE_IO))
5711d65c0b1SAlexander Shiyan return;
5721d65c0b1SAlexander Shiyan
573ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
5741d65c0b1SAlexander Shiyan
5751d65c0b1SAlexander Shiyan sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR);
5761d65c0b1SAlexander Shiyan sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS);
5771d65c0b1SAlexander Shiyan
578ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
5791d65c0b1SAlexander Shiyan }
5801d65c0b1SAlexander Shiyan
sccnxp_get_mctrl(struct uart_port * port)5811d65c0b1SAlexander Shiyan static unsigned int sccnxp_get_mctrl(struct uart_port *port)
5821d65c0b1SAlexander Shiyan {
5831d65c0b1SAlexander Shiyan u8 bitmask, ipr;
584ec063899SAlexander Shiyan unsigned long flags;
5851d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
5861d65c0b1SAlexander Shiyan unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
5871d65c0b1SAlexander Shiyan
588ea4c39beSAlexander Shiyan if (!(s->chip->flags & SCCNXP_HAVE_IO))
5891d65c0b1SAlexander Shiyan return mctrl;
5901d65c0b1SAlexander Shiyan
591ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
5921d65c0b1SAlexander Shiyan
5931d65c0b1SAlexander Shiyan ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG);
5941d65c0b1SAlexander Shiyan
5951d65c0b1SAlexander Shiyan if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DSR_IP)) {
5961d65c0b1SAlexander Shiyan bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
5971d65c0b1SAlexander Shiyan DSR_IP);
5981d65c0b1SAlexander Shiyan mctrl &= ~TIOCM_DSR;
5991d65c0b1SAlexander Shiyan mctrl |= (ipr & bitmask) ? TIOCM_DSR : 0;
6001d65c0b1SAlexander Shiyan }
6011d65c0b1SAlexander Shiyan if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(CTS_IP)) {
6021d65c0b1SAlexander Shiyan bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
6031d65c0b1SAlexander Shiyan CTS_IP);
6041d65c0b1SAlexander Shiyan mctrl &= ~TIOCM_CTS;
6051d65c0b1SAlexander Shiyan mctrl |= (ipr & bitmask) ? TIOCM_CTS : 0;
6061d65c0b1SAlexander Shiyan }
6071d65c0b1SAlexander Shiyan if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DCD_IP)) {
6081d65c0b1SAlexander Shiyan bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
6091d65c0b1SAlexander Shiyan DCD_IP);
6101d65c0b1SAlexander Shiyan mctrl &= ~TIOCM_CAR;
6111d65c0b1SAlexander Shiyan mctrl |= (ipr & bitmask) ? TIOCM_CAR : 0;
6121d65c0b1SAlexander Shiyan }
6131d65c0b1SAlexander Shiyan if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(RNG_IP)) {
6141d65c0b1SAlexander Shiyan bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
6151d65c0b1SAlexander Shiyan RNG_IP);
6161d65c0b1SAlexander Shiyan mctrl &= ~TIOCM_RNG;
6171d65c0b1SAlexander Shiyan mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0;
6181d65c0b1SAlexander Shiyan }
6191d65c0b1SAlexander Shiyan
620ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
6211d65c0b1SAlexander Shiyan
6221d65c0b1SAlexander Shiyan return mctrl;
6231d65c0b1SAlexander Shiyan }
6241d65c0b1SAlexander Shiyan
sccnxp_break_ctl(struct uart_port * port,int break_state)6251d65c0b1SAlexander Shiyan static void sccnxp_break_ctl(struct uart_port *port, int break_state)
6261d65c0b1SAlexander Shiyan {
6271d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
628ec063899SAlexander Shiyan unsigned long flags;
6291d65c0b1SAlexander Shiyan
630ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
6311d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, break_state ?
6321d65c0b1SAlexander Shiyan CR_CMD_START_BREAK : CR_CMD_STOP_BREAK);
633ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
6341d65c0b1SAlexander Shiyan }
6351d65c0b1SAlexander Shiyan
sccnxp_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)6361d65c0b1SAlexander Shiyan static void sccnxp_set_termios(struct uart_port *port,
637bec5b814SIlpo Järvinen struct ktermios *termios,
638bec5b814SIlpo Järvinen const struct ktermios *old)
6391d65c0b1SAlexander Shiyan {
6401d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
641ec063899SAlexander Shiyan unsigned long flags;
6421d65c0b1SAlexander Shiyan u8 mr1, mr2;
6431d65c0b1SAlexander Shiyan int baud;
6441d65c0b1SAlexander Shiyan
645ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
6461d65c0b1SAlexander Shiyan
6471d65c0b1SAlexander Shiyan /* Mask termios capabilities we don't support */
6481d65c0b1SAlexander Shiyan termios->c_cflag &= ~CMSPAR;
6491d65c0b1SAlexander Shiyan
6501d65c0b1SAlexander Shiyan /* Disable RX & TX, reset break condition, status and FIFOs */
6511d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET |
6521d65c0b1SAlexander Shiyan CR_RX_DISABLE | CR_TX_DISABLE);
6531d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET);
6541d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET);
6551d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET);
6561d65c0b1SAlexander Shiyan
6571d65c0b1SAlexander Shiyan /* Word size */
6581d65c0b1SAlexander Shiyan switch (termios->c_cflag & CSIZE) {
6591d65c0b1SAlexander Shiyan case CS5:
6601d65c0b1SAlexander Shiyan mr1 = MR1_BITS_5;
6611d65c0b1SAlexander Shiyan break;
6621d65c0b1SAlexander Shiyan case CS6:
6631d65c0b1SAlexander Shiyan mr1 = MR1_BITS_6;
6641d65c0b1SAlexander Shiyan break;
6651d65c0b1SAlexander Shiyan case CS7:
6661d65c0b1SAlexander Shiyan mr1 = MR1_BITS_7;
6671d65c0b1SAlexander Shiyan break;
6681d65c0b1SAlexander Shiyan case CS8:
66991f61ce2SAlexander Shiyan default:
6701d65c0b1SAlexander Shiyan mr1 = MR1_BITS_8;
6711d65c0b1SAlexander Shiyan break;
6721d65c0b1SAlexander Shiyan }
6731d65c0b1SAlexander Shiyan
6741d65c0b1SAlexander Shiyan /* Parity */
6751d65c0b1SAlexander Shiyan if (termios->c_cflag & PARENB) {
6761d65c0b1SAlexander Shiyan if (termios->c_cflag & PARODD)
6771d65c0b1SAlexander Shiyan mr1 |= MR1_PAR_ODD;
6781d65c0b1SAlexander Shiyan } else
6791d65c0b1SAlexander Shiyan mr1 |= MR1_PAR_NO;
6801d65c0b1SAlexander Shiyan
6811d65c0b1SAlexander Shiyan /* Stop bits */
6821d65c0b1SAlexander Shiyan mr2 = (termios->c_cflag & CSTOPB) ? MR2_STOP2 : MR2_STOP1;
6831d65c0b1SAlexander Shiyan
6841d65c0b1SAlexander Shiyan /* Update desired format */
6851d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR1);
6861d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_MR_REG, mr1);
6871d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_MR_REG, mr2);
6881d65c0b1SAlexander Shiyan
6891d65c0b1SAlexander Shiyan /* Set read status mask */
6901d65c0b1SAlexander Shiyan port->read_status_mask = SR_OVR;
6911d65c0b1SAlexander Shiyan if (termios->c_iflag & INPCK)
6921d65c0b1SAlexander Shiyan port->read_status_mask |= SR_PE | SR_FE;
693ef8b9ddcSPeter Hurley if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
6941d65c0b1SAlexander Shiyan port->read_status_mask |= SR_BRK;
6951d65c0b1SAlexander Shiyan
6961d65c0b1SAlexander Shiyan /* Set status ignore mask */
6971d65c0b1SAlexander Shiyan port->ignore_status_mask = 0;
6981d65c0b1SAlexander Shiyan if (termios->c_iflag & IGNBRK)
6991d65c0b1SAlexander Shiyan port->ignore_status_mask |= SR_BRK;
7002ce7c148SAlexander Shiyan if (termios->c_iflag & IGNPAR)
7012ce7c148SAlexander Shiyan port->ignore_status_mask |= SR_PE;
7021d65c0b1SAlexander Shiyan if (!(termios->c_cflag & CREAD))
7031d65c0b1SAlexander Shiyan port->ignore_status_mask |= SR_PE | SR_OVR | SR_FE | SR_BRK;
7041d65c0b1SAlexander Shiyan
7051d65c0b1SAlexander Shiyan /* Setup baudrate */
7061d65c0b1SAlexander Shiyan baud = uart_get_baud_rate(port, termios, old, 50,
707ea4c39beSAlexander Shiyan (s->chip->flags & SCCNXP_HAVE_MR0) ?
7081d65c0b1SAlexander Shiyan 230400 : 38400);
7091685118fSAlexander Shiyan baud = sccnxp_set_baud(port, baud);
7101d65c0b1SAlexander Shiyan
7111d65c0b1SAlexander Shiyan /* Update timeout according to new baud rate */
7121d65c0b1SAlexander Shiyan uart_update_timeout(port, termios->c_cflag, baud);
7131d65c0b1SAlexander Shiyan
714ec063899SAlexander Shiyan /* Report actual baudrate back to core */
7151685118fSAlexander Shiyan if (tty_termios_baud_rate(termios))
7161685118fSAlexander Shiyan tty_termios_encode_baud_rate(termios, baud, baud);
7171685118fSAlexander Shiyan
7181d65c0b1SAlexander Shiyan /* Enable RX & TX */
7191d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
7201d65c0b1SAlexander Shiyan
721ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
7221d65c0b1SAlexander Shiyan }
7231d65c0b1SAlexander Shiyan
sccnxp_startup(struct uart_port * port)7241d65c0b1SAlexander Shiyan static int sccnxp_startup(struct uart_port *port)
7251d65c0b1SAlexander Shiyan {
7261d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
727ec063899SAlexander Shiyan unsigned long flags;
7281d65c0b1SAlexander Shiyan
729ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
7301d65c0b1SAlexander Shiyan
731ea4c39beSAlexander Shiyan if (s->chip->flags & SCCNXP_HAVE_IO) {
7321d65c0b1SAlexander Shiyan /* Outputs are controlled manually */
7331d65c0b1SAlexander Shiyan sccnxp_write(port, SCCNXP_OPCR_REG, 0);
7341d65c0b1SAlexander Shiyan }
7351d65c0b1SAlexander Shiyan
7361d65c0b1SAlexander Shiyan /* Reset break condition, status and FIFOs */
7371d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET);
7381d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET);
7391d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET);
7401d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET);
7411d65c0b1SAlexander Shiyan
7421d65c0b1SAlexander Shiyan /* Enable RX & TX */
7431d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
7441d65c0b1SAlexander Shiyan
7451d65c0b1SAlexander Shiyan /* Enable RX interrupt */
7461d65c0b1SAlexander Shiyan sccnxp_enable_irq(port, IMR_RXRDY);
7471d65c0b1SAlexander Shiyan
748ec063899SAlexander Shiyan s->opened[port->line] = 1;
749ec063899SAlexander Shiyan
750ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
7511d65c0b1SAlexander Shiyan
7521d65c0b1SAlexander Shiyan return 0;
7531d65c0b1SAlexander Shiyan }
7541d65c0b1SAlexander Shiyan
sccnxp_shutdown(struct uart_port * port)7551d65c0b1SAlexander Shiyan static void sccnxp_shutdown(struct uart_port *port)
7561d65c0b1SAlexander Shiyan {
7571d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
758ec063899SAlexander Shiyan unsigned long flags;
7591d65c0b1SAlexander Shiyan
760ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
761ec063899SAlexander Shiyan
762ec063899SAlexander Shiyan s->opened[port->line] = 0;
7631d65c0b1SAlexander Shiyan
7641d65c0b1SAlexander Shiyan /* Disable interrupts */
7651d65c0b1SAlexander Shiyan sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
7661d65c0b1SAlexander Shiyan
7671d65c0b1SAlexander Shiyan /* Disable TX & RX */
7681d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE);
7691d65c0b1SAlexander Shiyan
7701d65c0b1SAlexander Shiyan /* Leave direction to input */
771ea4c39beSAlexander Shiyan if (s->chip->flags & SCCNXP_HAVE_IO)
7721d65c0b1SAlexander Shiyan sccnxp_set_bit(port, DIR_OP, 0);
7731d65c0b1SAlexander Shiyan
774ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
7751d65c0b1SAlexander Shiyan }
7761d65c0b1SAlexander Shiyan
sccnxp_type(struct uart_port * port)7771d65c0b1SAlexander Shiyan static const char *sccnxp_type(struct uart_port *port)
7781d65c0b1SAlexander Shiyan {
7791d65c0b1SAlexander Shiyan struct sccnxp_port *s = dev_get_drvdata(port->dev);
7801d65c0b1SAlexander Shiyan
781ea4c39beSAlexander Shiyan return (port->type == PORT_SC26XX) ? s->chip->name : NULL;
7821d65c0b1SAlexander Shiyan }
7831d65c0b1SAlexander Shiyan
sccnxp_release_port(struct uart_port * port)7841d65c0b1SAlexander Shiyan static void sccnxp_release_port(struct uart_port *port)
7851d65c0b1SAlexander Shiyan {
7861d65c0b1SAlexander Shiyan /* Do nothing */
7871d65c0b1SAlexander Shiyan }
7881d65c0b1SAlexander Shiyan
sccnxp_request_port(struct uart_port * port)7891d65c0b1SAlexander Shiyan static int sccnxp_request_port(struct uart_port *port)
7901d65c0b1SAlexander Shiyan {
7911d65c0b1SAlexander Shiyan /* Do nothing */
7921d65c0b1SAlexander Shiyan return 0;
7931d65c0b1SAlexander Shiyan }
7941d65c0b1SAlexander Shiyan
sccnxp_config_port(struct uart_port * port,int flags)7951d65c0b1SAlexander Shiyan static void sccnxp_config_port(struct uart_port *port, int flags)
7961d65c0b1SAlexander Shiyan {
7971d65c0b1SAlexander Shiyan if (flags & UART_CONFIG_TYPE)
7981d65c0b1SAlexander Shiyan port->type = PORT_SC26XX;
7991d65c0b1SAlexander Shiyan }
8001d65c0b1SAlexander Shiyan
sccnxp_verify_port(struct uart_port * port,struct serial_struct * s)8011d65c0b1SAlexander Shiyan static int sccnxp_verify_port(struct uart_port *port, struct serial_struct *s)
8021d65c0b1SAlexander Shiyan {
8031d65c0b1SAlexander Shiyan if ((s->type == PORT_UNKNOWN) || (s->type == PORT_SC26XX))
8041d65c0b1SAlexander Shiyan return 0;
8051d65c0b1SAlexander Shiyan if (s->irq == port->irq)
8061d65c0b1SAlexander Shiyan return 0;
8071d65c0b1SAlexander Shiyan
8081d65c0b1SAlexander Shiyan return -EINVAL;
8091d65c0b1SAlexander Shiyan }
8101d65c0b1SAlexander Shiyan
8111d65c0b1SAlexander Shiyan static const struct uart_ops sccnxp_ops = {
8121d65c0b1SAlexander Shiyan .tx_empty = sccnxp_tx_empty,
8131d65c0b1SAlexander Shiyan .set_mctrl = sccnxp_set_mctrl,
8141d65c0b1SAlexander Shiyan .get_mctrl = sccnxp_get_mctrl,
8151d65c0b1SAlexander Shiyan .stop_tx = sccnxp_stop_tx,
8161d65c0b1SAlexander Shiyan .start_tx = sccnxp_start_tx,
8171d65c0b1SAlexander Shiyan .stop_rx = sccnxp_stop_rx,
8181d65c0b1SAlexander Shiyan .break_ctl = sccnxp_break_ctl,
8191d65c0b1SAlexander Shiyan .startup = sccnxp_startup,
8201d65c0b1SAlexander Shiyan .shutdown = sccnxp_shutdown,
8211d65c0b1SAlexander Shiyan .set_termios = sccnxp_set_termios,
8221d65c0b1SAlexander Shiyan .type = sccnxp_type,
8231d65c0b1SAlexander Shiyan .release_port = sccnxp_release_port,
8241d65c0b1SAlexander Shiyan .request_port = sccnxp_request_port,
8251d65c0b1SAlexander Shiyan .config_port = sccnxp_config_port,
8261d65c0b1SAlexander Shiyan .verify_port = sccnxp_verify_port,
8271d65c0b1SAlexander Shiyan };
8281d65c0b1SAlexander Shiyan
8291d65c0b1SAlexander Shiyan #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
sccnxp_console_putchar(struct uart_port * port,unsigned char c)8303f8bab17SJiri Slaby static void sccnxp_console_putchar(struct uart_port *port, unsigned char c)
8311d65c0b1SAlexander Shiyan {
8321d65c0b1SAlexander Shiyan int tryes = 100000;
8331d65c0b1SAlexander Shiyan
8341d65c0b1SAlexander Shiyan while (tryes--) {
8351d65c0b1SAlexander Shiyan if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXRDY) {
8361d65c0b1SAlexander Shiyan sccnxp_port_write(port, SCCNXP_THR_REG, c);
8371d65c0b1SAlexander Shiyan break;
8381d65c0b1SAlexander Shiyan }
8391d65c0b1SAlexander Shiyan barrier();
8401d65c0b1SAlexander Shiyan }
8411d65c0b1SAlexander Shiyan }
8421d65c0b1SAlexander Shiyan
sccnxp_console_write(struct console * co,const char * c,unsigned n)8431d65c0b1SAlexander Shiyan static void sccnxp_console_write(struct console *co, const char *c, unsigned n)
8441d65c0b1SAlexander Shiyan {
8451d65c0b1SAlexander Shiyan struct sccnxp_port *s = (struct sccnxp_port *)co->data;
8461d65c0b1SAlexander Shiyan struct uart_port *port = &s->port[co->index];
847ec063899SAlexander Shiyan unsigned long flags;
8481d65c0b1SAlexander Shiyan
849ec063899SAlexander Shiyan spin_lock_irqsave(&s->lock, flags);
8501d65c0b1SAlexander Shiyan uart_console_write(port, c, n, sccnxp_console_putchar);
851ec063899SAlexander Shiyan spin_unlock_irqrestore(&s->lock, flags);
8521d65c0b1SAlexander Shiyan }
8531d65c0b1SAlexander Shiyan
sccnxp_console_setup(struct console * co,char * options)8541d65c0b1SAlexander Shiyan static int sccnxp_console_setup(struct console *co, char *options)
8551d65c0b1SAlexander Shiyan {
8561d65c0b1SAlexander Shiyan struct sccnxp_port *s = (struct sccnxp_port *)co->data;
8571d65c0b1SAlexander Shiyan struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0];
8581d65c0b1SAlexander Shiyan int baud = 9600, bits = 8, parity = 'n', flow = 'n';
8591d65c0b1SAlexander Shiyan
8601d65c0b1SAlexander Shiyan if (options)
8611d65c0b1SAlexander Shiyan uart_parse_options(options, &baud, &parity, &bits, &flow);
8621d65c0b1SAlexander Shiyan
8631d65c0b1SAlexander Shiyan return uart_set_options(port, co, baud, parity, bits, flow);
8641d65c0b1SAlexander Shiyan }
8651d65c0b1SAlexander Shiyan #endif
8661d65c0b1SAlexander Shiyan
867ea4c39beSAlexander Shiyan static const struct platform_device_id sccnxp_id_table[] = {
868ea4c39beSAlexander Shiyan { .name = "sc2681", .driver_data = (kernel_ulong_t)&sc2681, },
869ea4c39beSAlexander Shiyan { .name = "sc2691", .driver_data = (kernel_ulong_t)&sc2691, },
870ea4c39beSAlexander Shiyan { .name = "sc2692", .driver_data = (kernel_ulong_t)&sc2692, },
871ea4c39beSAlexander Shiyan { .name = "sc2891", .driver_data = (kernel_ulong_t)&sc2891, },
872ea4c39beSAlexander Shiyan { .name = "sc2892", .driver_data = (kernel_ulong_t)&sc2892, },
873ea4c39beSAlexander Shiyan { .name = "sc28202", .driver_data = (kernel_ulong_t)&sc28202, },
874ea4c39beSAlexander Shiyan { .name = "sc68681", .driver_data = (kernel_ulong_t)&sc68681, },
875ea4c39beSAlexander Shiyan { .name = "sc68692", .driver_data = (kernel_ulong_t)&sc68692, },
876ea4c39beSAlexander Shiyan { }
877ea4c39beSAlexander Shiyan };
878ea4c39beSAlexander Shiyan MODULE_DEVICE_TABLE(platform, sccnxp_id_table);
879ea4c39beSAlexander Shiyan
sccnxp_probe(struct platform_device * pdev)8809671f099SBill Pemberton static int sccnxp_probe(struct platform_device *pdev)
8811d65c0b1SAlexander Shiyan {
8821d65c0b1SAlexander Shiyan struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev);
883*0851efafSYangtao Li struct resource *res;
884ea4c39beSAlexander Shiyan int i, ret, uartclk;
8851d65c0b1SAlexander Shiyan struct sccnxp_port *s;
8861d65c0b1SAlexander Shiyan void __iomem *membase;
88790efa75fSAlexander Shiyan struct clk *clk;
8881d65c0b1SAlexander Shiyan
889*0851efafSYangtao Li membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
890e087ab74SAlexander Shiyan if (IS_ERR(membase))
891e087ab74SAlexander Shiyan return PTR_ERR(membase);
8921d65c0b1SAlexander Shiyan
8931d65c0b1SAlexander Shiyan s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL);
8941d65c0b1SAlexander Shiyan if (!s) {
8951d65c0b1SAlexander Shiyan dev_err(&pdev->dev, "Error allocating port structure\n");
8961d65c0b1SAlexander Shiyan return -ENOMEM;
8971d65c0b1SAlexander Shiyan }
8981d65c0b1SAlexander Shiyan platform_set_drvdata(pdev, s);
8991d65c0b1SAlexander Shiyan
900ec063899SAlexander Shiyan spin_lock_init(&s->lock);
9011d65c0b1SAlexander Shiyan
902ea4c39beSAlexander Shiyan s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data;
9031d65c0b1SAlexander Shiyan
904e087ab74SAlexander Shiyan s->regulator = devm_regulator_get(&pdev->dev, "vcc");
905e087ab74SAlexander Shiyan if (!IS_ERR(s->regulator)) {
906e087ab74SAlexander Shiyan ret = regulator_enable(s->regulator);
907e087ab74SAlexander Shiyan if (ret) {
908e087ab74SAlexander Shiyan dev_err(&pdev->dev,
909e087ab74SAlexander Shiyan "Failed to enable regulator: %i\n", ret);
910e087ab74SAlexander Shiyan return ret;
911e087ab74SAlexander Shiyan }
912e087ab74SAlexander Shiyan } else if (PTR_ERR(s->regulator) == -EPROBE_DEFER)
913e087ab74SAlexander Shiyan return -EPROBE_DEFER;
914e087ab74SAlexander Shiyan
91503e30f06SChristophe JAILLET clk = devm_clk_get_enabled(&pdev->dev, NULL);
91690efa75fSAlexander Shiyan if (IS_ERR(clk)) {
917e279e6d9SThomas Bogendoerfer ret = PTR_ERR(clk);
918e279e6d9SThomas Bogendoerfer if (ret == -EPROBE_DEFER)
91990efa75fSAlexander Shiyan goto err_out;
920e279e6d9SThomas Bogendoerfer uartclk = 0;
921e279e6d9SThomas Bogendoerfer } else {
922e279e6d9SThomas Bogendoerfer uartclk = clk_get_rate(clk);
92390efa75fSAlexander Shiyan }
924e279e6d9SThomas Bogendoerfer
925e279e6d9SThomas Bogendoerfer if (!uartclk) {
92690efa75fSAlexander Shiyan dev_notice(&pdev->dev, "Using default clock frequency\n");
927ea4c39beSAlexander Shiyan uartclk = s->chip->freq_std;
928e279e6d9SThomas Bogendoerfer }
92990efa75fSAlexander Shiyan
93090efa75fSAlexander Shiyan /* Check input frequency */
931ea4c39beSAlexander Shiyan if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) {
93290efa75fSAlexander Shiyan dev_err(&pdev->dev, "Frequency out of bounds\n");
93390efa75fSAlexander Shiyan ret = -EINVAL;
93490efa75fSAlexander Shiyan goto err_out;
93590efa75fSAlexander Shiyan }
93690efa75fSAlexander Shiyan
937461a8ecbSGreg Kroah-Hartman if (pdata)
938461a8ecbSGreg Kroah-Hartman memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata));
939461a8ecbSGreg Kroah-Hartman
940b786337dSAlexander Shiyan if (s->pdata.poll_time_us) {
941ec063899SAlexander Shiyan dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n",
942b786337dSAlexander Shiyan s->pdata.poll_time_us);
943ec063899SAlexander Shiyan s->poll = 1;
944461a8ecbSGreg Kroah-Hartman }
945461a8ecbSGreg Kroah-Hartman
946461a8ecbSGreg Kroah-Hartman if (!s->poll) {
9471d65c0b1SAlexander Shiyan s->irq = platform_get_irq(pdev, 0);
948ec063899SAlexander Shiyan if (s->irq < 0) {
9491d65c0b1SAlexander Shiyan ret = -ENXIO;
9501d65c0b1SAlexander Shiyan goto err_out;
9511d65c0b1SAlexander Shiyan }
952ec063899SAlexander Shiyan }
9531d65c0b1SAlexander Shiyan
9541d65c0b1SAlexander Shiyan s->uart.owner = THIS_MODULE;
9551d65c0b1SAlexander Shiyan s->uart.dev_name = "ttySC";
9561d65c0b1SAlexander Shiyan s->uart.major = SCCNXP_MAJOR;
9571d65c0b1SAlexander Shiyan s->uart.minor = SCCNXP_MINOR;
958ea4c39beSAlexander Shiyan s->uart.nr = s->chip->nr;
9591d65c0b1SAlexander Shiyan #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
9601d65c0b1SAlexander Shiyan s->uart.cons = &s->console;
9611d65c0b1SAlexander Shiyan s->uart.cons->device = uart_console_device;
9621d65c0b1SAlexander Shiyan s->uart.cons->write = sccnxp_console_write;
9631d65c0b1SAlexander Shiyan s->uart.cons->setup = sccnxp_console_setup;
9641d65c0b1SAlexander Shiyan s->uart.cons->flags = CON_PRINTBUFFER;
9651d65c0b1SAlexander Shiyan s->uart.cons->index = -1;
9661d65c0b1SAlexander Shiyan s->uart.cons->data = s;
9671d65c0b1SAlexander Shiyan strcpy(s->uart.cons->name, "ttySC");
9681d65c0b1SAlexander Shiyan #endif
9691d65c0b1SAlexander Shiyan ret = uart_register_driver(&s->uart);
9701d65c0b1SAlexander Shiyan if (ret) {
9711d65c0b1SAlexander Shiyan dev_err(&pdev->dev, "Registering UART driver failed\n");
9721d65c0b1SAlexander Shiyan goto err_out;
9731d65c0b1SAlexander Shiyan }
9741d65c0b1SAlexander Shiyan
9751d65c0b1SAlexander Shiyan for (i = 0; i < s->uart.nr; i++) {
9761d65c0b1SAlexander Shiyan s->port[i].line = i;
9771d65c0b1SAlexander Shiyan s->port[i].dev = &pdev->dev;
9781d65c0b1SAlexander Shiyan s->port[i].irq = s->irq;
9791d65c0b1SAlexander Shiyan s->port[i].type = PORT_SC26XX;
980ea4c39beSAlexander Shiyan s->port[i].fifosize = s->chip->fifosize;
9811d65c0b1SAlexander Shiyan s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE;
9821d65c0b1SAlexander Shiyan s->port[i].iotype = UPIO_MEM;
9831d65c0b1SAlexander Shiyan s->port[i].mapbase = res->start;
9841d65c0b1SAlexander Shiyan s->port[i].membase = membase;
9851d65c0b1SAlexander Shiyan s->port[i].regshift = s->pdata.reg_shift;
98690efa75fSAlexander Shiyan s->port[i].uartclk = uartclk;
9871d65c0b1SAlexander Shiyan s->port[i].ops = &sccnxp_ops;
988212d9371SDmitry Safonov s->port[i].has_sysrq = IS_ENABLED(CONFIG_SERIAL_SCCNXP_CONSOLE);
9891d65c0b1SAlexander Shiyan uart_add_one_port(&s->uart, &s->port[i]);
9901d65c0b1SAlexander Shiyan /* Set direction to input */
991ea4c39beSAlexander Shiyan if (s->chip->flags & SCCNXP_HAVE_IO)
9921d65c0b1SAlexander Shiyan sccnxp_set_bit(&s->port[i], DIR_OP, 0);
9931d65c0b1SAlexander Shiyan }
9941d65c0b1SAlexander Shiyan
9951d65c0b1SAlexander Shiyan /* Disable interrupts */
9961d65c0b1SAlexander Shiyan s->imr = 0;
9971d65c0b1SAlexander Shiyan sccnxp_write(&s->port[0], SCCNXP_IMR_REG, 0);
9981d65c0b1SAlexander Shiyan
999ec063899SAlexander Shiyan if (!s->poll) {
1000ec063899SAlexander Shiyan ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL,
1001ec063899SAlexander Shiyan sccnxp_ist,
1002ec063899SAlexander Shiyan IRQF_TRIGGER_FALLING |
1003ec063899SAlexander Shiyan IRQF_ONESHOT,
10041d65c0b1SAlexander Shiyan dev_name(&pdev->dev), s);
10051d65c0b1SAlexander Shiyan if (!ret)
10061d65c0b1SAlexander Shiyan return 0;
10071d65c0b1SAlexander Shiyan
10081d65c0b1SAlexander Shiyan dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq);
1009ec063899SAlexander Shiyan } else {
1010fc3b00d7SKees Cook timer_setup(&s->timer, sccnxp_timer, 0);
1011ec063899SAlexander Shiyan mod_timer(&s->timer, jiffies +
1012ec063899SAlexander Shiyan usecs_to_jiffies(s->pdata.poll_time_us));
1013ec063899SAlexander Shiyan return 0;
1014ec063899SAlexander Shiyan }
10151d65c0b1SAlexander Shiyan
10167a95b815SWei Yongjun uart_unregister_driver(&s->uart);
10171d65c0b1SAlexander Shiyan err_out:
1018e087ab74SAlexander Shiyan if (!IS_ERR(s->regulator))
1019c9126143SAlexey Khoroshilov regulator_disable(s->regulator);
1020e087ab74SAlexander Shiyan
10211d65c0b1SAlexander Shiyan return ret;
10221d65c0b1SAlexander Shiyan }
10231d65c0b1SAlexander Shiyan
sccnxp_remove(struct platform_device * pdev)1024ae8d8a14SBill Pemberton static int sccnxp_remove(struct platform_device *pdev)
10251d65c0b1SAlexander Shiyan {
10261d65c0b1SAlexander Shiyan int i;
10271d65c0b1SAlexander Shiyan struct sccnxp_port *s = platform_get_drvdata(pdev);
10281d65c0b1SAlexander Shiyan
1029ec063899SAlexander Shiyan if (!s->poll)
10301d65c0b1SAlexander Shiyan devm_free_irq(&pdev->dev, s->irq, s);
1031ec063899SAlexander Shiyan else
1032ec063899SAlexander Shiyan del_timer_sync(&s->timer);
10331d65c0b1SAlexander Shiyan
10341d65c0b1SAlexander Shiyan for (i = 0; i < s->uart.nr; i++)
10351d65c0b1SAlexander Shiyan uart_remove_one_port(&s->uart, &s->port[i]);
10361d65c0b1SAlexander Shiyan
10371d65c0b1SAlexander Shiyan uart_unregister_driver(&s->uart);
10381d65c0b1SAlexander Shiyan
103931815c08SAlexander Shiyan if (!IS_ERR(s->regulator))
104031815c08SAlexander Shiyan return regulator_disable(s->regulator);
10411d65c0b1SAlexander Shiyan
10421d65c0b1SAlexander Shiyan return 0;
10431d65c0b1SAlexander Shiyan }
10441d65c0b1SAlexander Shiyan
10451d65c0b1SAlexander Shiyan static struct platform_driver sccnxp_uart_driver = {
10461d65c0b1SAlexander Shiyan .driver = {
10471d65c0b1SAlexander Shiyan .name = SCCNXP_NAME,
10481d65c0b1SAlexander Shiyan },
10491d65c0b1SAlexander Shiyan .probe = sccnxp_probe,
10502d47b716SBill Pemberton .remove = sccnxp_remove,
10511d65c0b1SAlexander Shiyan .id_table = sccnxp_id_table,
10521d65c0b1SAlexander Shiyan };
10531d65c0b1SAlexander Shiyan module_platform_driver(sccnxp_uart_driver);
10541d65c0b1SAlexander Shiyan
10551d65c0b1SAlexander Shiyan MODULE_LICENSE("GPL v2");
10561d65c0b1SAlexander Shiyan MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
10571d65c0b1SAlexander Shiyan MODULE_DESCRIPTION("SCCNXP serial driver");
1058