linux/drivers/tty/serial/8250/8250_ingenic.c

354 lines
8.4 KiB
C
Raw Normal View History

tty: add SPDX identifiers to all remaining files in drivers/tty/ It's good to have SPDX identifiers in all files to make it easier to audit the kernel tree for correct licenses. Update the drivers/tty files files with the correct SPDX license identifier based on the license text in the file itself. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This work is based on a script and data from Thomas Gleixner, Philippe Ombredanne, and Kate Stewart. Cc: Jiri Slaby <jslaby@suse.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Chris Metcalf <cmetcalf@mellanox.com> Cc: Jiri Kosina <jikos@kernel.org> Cc: David Sterba <dsterba@suse.com> Cc: James Hogan <jhogan@kernel.org> Cc: Rob Herring <robh@kernel.org> Cc: Eric Anholt <eric@anholt.net> Cc: Stefan Wahren <stefan.wahren@i2se.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: Ray Jui <rjui@broadcom.com> Cc: Scott Branden <sbranden@broadcom.com> Cc: bcm-kernel-feedback-list@broadcom.com Cc: "James E.J. Bottomley" <jejb@parisc-linux.org> Cc: Helge Deller <deller@gmx.de> Cc: Joachim Eastwood <manabian@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@linux-mips.org> Cc: "Uwe Kleine-König" <kernel@pengutronix.de> Cc: Pat Gefre <pfg@sgi.com> Cc: "Guilherme G. Piccoli" <gpiccoli@linux.vnet.ibm.com> Cc: Jason Wessel <jason.wessel@windriver.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Cc: Sylvain Lemieux <slemieux.tyco@gmail.com> Cc: Carlo Caione <carlo@caione.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Liviu Dudau <liviu.dudau@arm.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Andy Gross <andy.gross@linaro.org> Cc: David Brown <david.brown@linaro.org> Cc: "Andreas Färber" <afaerber@suse.de> Cc: Kevin Cernekee <cernekee@gmail.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Barry Song <baohua@kernel.org> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@st.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@tabi.org> Cc: Tony Prisk <linux@prisktech.co.nz> Cc: Michal Simek <michal.simek@xilinx.com> Cc: "Sören Brinkmann" <soren.brinkmann@xilinx.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Kate Stewart <kstewart@linuxfoundation.org> Cc: Philippe Ombredanne <pombredanne@nexb.com> Cc: Jiri Slaby <jslaby@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-07 01:11:51 +08:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2010 Lars-Peter Clausen <lars@metafoo.de>
* Copyright (C) 2015 Imagination Technologies
*
* Ingenic SoC UART support
*/
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/io.h>
#include <linux/libfdt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/serial_8250.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include "8250.h"
/** ingenic_uart_config: SOC specific config data. */
struct ingenic_uart_config {
int tx_loadsz;
int fifosize;
};
struct ingenic_uart_data {
struct clk *clk_module;
struct clk *clk_baud;
int line;
};
static const struct of_device_id of_match[];
#define UART_FCR_UME BIT(4)
#define UART_MCR_MDCE BIT(7)
#define UART_MCR_FCM BIT(6)
static struct earlycon_device *early_device;
static uint8_t early_in(struct uart_port *port, int offset)
{
return readl(port->membase + (offset << 2));
}
static void early_out(struct uart_port *port, int offset, uint8_t value)
{
writel(value, port->membase + (offset << 2));
}
serial: make uart_console_write->putchar()'s character an unsigned char Currently, uart_console_write->putchar's second parameter (the character) is of type int. It makes little sense, provided uart_console_write() accepts the input string as "const char *s" and passes its content -- the characters -- to putchar(). So switch the character's type to unsigned char. We don't use char as that is signed on some platforms. That would cause troubles for drivers which (implicitly) cast the char to u16 when writing to the device. Sign extension would happen in that case and the value written would be completely different to the provided char. DZ is an example of such a driver -- on MIPS, it uses u16 for dz_out in dz_console_putchar(). Note we do the char -> uchar conversion implicitly in uart_console_write(). Provided we do not change size of the data type, sign extension does not happen there, so the problem is void. This makes the types consistent and unified with the rest of the uart layer, which uses unsigned char in most places already. One exception is xmit_buf, but that is going to be converted later. Cc: Paul Cercueil <paul@crapouillou.net> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Ludovic Desroches <ludovic.desroches@microchip.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: bcm-kernel-feedback-list@broadcom.com Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Cc: Paul Walmsley <paul.walmsley@sifive.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Albert Ou <aou@eecs.berkeley.edu> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Karol Gugala <kgugala@antmicro.com> Cc: Mateusz Holenko <mholenko@antmicro.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Liviu Dudau <liviu.dudau@arm.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: "Andreas Färber" <afaerber@suse.de> Cc: Manivannan Sadhasivam <mani@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Andy Gross <agross@kernel.org> Cc: Bjorn Andersson <bjorn.andersson@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang7@gmail.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: Peter Korsgaard <peter@korsgaard.com> Cc: Michal Simek <michal.simek@xilinx.com> Acked-by: Richard Genoud <richard.genoud@gmail.com> [atmel_serial] Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-by: Paul Cercueil <paul@crapouillou.net> Acked-by: Neil Armstrong <narmstrong@baylibre.com> # meson_serial Signed-off-by: Jiri Slaby <jslaby@suse.cz> Link: https://lore.kernel.org/r/20220303080831.21783-1-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-03-03 16:08:31 +08:00
static void ingenic_early_console_putc(struct uart_port *port, unsigned char c)
{
uint8_t lsr;
do {
lsr = early_in(port, UART_LSR);
} while ((lsr & UART_LSR_TEMT) == 0);
early_out(port, UART_TX, c);
}
static void ingenic_early_console_write(struct console *console,
const char *s, unsigned int count)
{
uart_console_write(&early_device->port, s, count,
ingenic_early_console_putc);
}
static void __init ingenic_early_console_setup_clock(struct earlycon_device *dev)
{
void *fdt = initial_boot_params;
const __be32 *prop;
int offset;
offset = fdt_path_offset(fdt, "/ext");
if (offset < 0)
return;
prop = fdt_getprop(fdt, offset, "clock-frequency", NULL);
if (!prop)
return;
dev->port.uartclk = be32_to_cpup(prop);
}
static int __init ingenic_early_console_setup(struct earlycon_device *dev,
const char *opt)
{
struct uart_port *port = &dev->port;
unsigned int divisor;
int baud = 115200;
if (!dev->port.membase)
return -ENODEV;
if (opt) {
unsigned int parity, bits, flow; /* unused for now */
uart_parse_options(opt, &baud, &parity, &bits, &flow);
}
ingenic_early_console_setup_clock(dev);
if (dev->baud)
baud = dev->baud;
divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud);
early_out(port, UART_IER, 0);
early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
early_out(port, UART_DLL, 0);
early_out(port, UART_DLM, 0);
early_out(port, UART_LCR, UART_LCR_WLEN8);
early_out(port, UART_FCR, UART_FCR_UME | UART_FCR_CLEAR_XMIT |
UART_FCR_CLEAR_RCVR | UART_FCR_ENABLE_FIFO);
early_out(port, UART_MCR, UART_MCR_RTS | UART_MCR_DTR);
early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
early_out(port, UART_DLL, divisor & 0xff);
early_out(port, UART_DLM, (divisor >> 8) & 0xff);
early_out(port, UART_LCR, UART_LCR_WLEN8);
early_device = dev;
dev->con->write = ingenic_early_console_write;
return 0;
}
OF_EARLYCON_DECLARE(jz4740_uart, "ingenic,jz4740-uart",
ingenic_early_console_setup);
OF_EARLYCON_DECLARE(jz4770_uart, "ingenic,jz4770-uart",
ingenic_early_console_setup);
OF_EARLYCON_DECLARE(jz4775_uart, "ingenic,jz4775-uart",
ingenic_early_console_setup);
OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart",
ingenic_early_console_setup);
OF_EARLYCON_DECLARE(x1000_uart, "ingenic,x1000-uart",
ingenic_early_console_setup);
static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
{
int ier;
switch (offset) {
case UART_FCR:
/* UART module enable */
value |= UART_FCR_UME;
break;
case UART_IER:
/*
* Enable receive timeout interrupt with the receive line
* status interrupt.
*/
value |= (value & 0x4) << 2;
break;
case UART_MCR:
/*
* If we have enabled modem status IRQs we should enable
* modem mode.
*/
ier = p->serial_in(p, UART_IER);
if (ier & UART_IER_MSI)
value |= UART_MCR_MDCE | UART_MCR_FCM;
else
value &= ~(UART_MCR_MDCE | UART_MCR_FCM);
break;
default:
break;
}
writeb(value, p->membase + (offset << p->regshift));
}
static unsigned int ingenic_uart_serial_in(struct uart_port *p, int offset)
{
unsigned int value;
value = readb(p->membase + (offset << p->regshift));
/* Hide non-16550 compliant bits from higher levels */
switch (offset) {
case UART_FCR:
value &= ~UART_FCR_UME;
break;
case UART_MCR:
value &= ~(UART_MCR_MDCE | UART_MCR_FCM);
break;
default:
break;
}
return value;
}
static int ingenic_uart_probe(struct platform_device *pdev)
{
struct uart_8250_port uart = {};
struct ingenic_uart_data *data;
const struct ingenic_uart_config *cdata;
struct resource *regs;
int irq, err, line;
cdata = of_device_get_match_data(&pdev->dev);
if (!cdata) {
dev_err(&pdev->dev, "Error: No device match found\n");
return -ENODEV;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
dev_err(&pdev->dev, "no registers defined\n");
return -EINVAL;
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
spin_lock_init(&uart.port.lock);
uart.port.type = PORT_16550A;
uart.port.flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE;
uart.port.iotype = UPIO_MEM;
uart.port.mapbase = regs->start;
uart.port.regshift = 2;
uart.port.serial_out = ingenic_uart_serial_out;
uart.port.serial_in = ingenic_uart_serial_in;
uart.port.irq = irq;
uart.port.dev = &pdev->dev;
uart.port.fifosize = cdata->fifosize;
uart.tx_loadsz = cdata->tx_loadsz;
uart.capabilities = UART_CAP_FIFO | UART_CAP_RTOIE;
/* Check for a fixed line number */
line = of_alias_get_id(pdev->dev.of_node, "serial");
if (line >= 0)
uart.port.line = line;
uart.port.membase = devm_ioremap(&pdev->dev, regs->start,
resource_size(regs));
if (!uart.port.membase)
return -ENOMEM;
data->clk_module = devm_clk_get(&pdev->dev, "module");
if (IS_ERR(data->clk_module))
return dev_err_probe(&pdev->dev, PTR_ERR(data->clk_module),
"unable to get module clock\n");
data->clk_baud = devm_clk_get(&pdev->dev, "baud");
if (IS_ERR(data->clk_baud))
return dev_err_probe(&pdev->dev, PTR_ERR(data->clk_baud),
"unable to get baud clock\n");
err = clk_prepare_enable(data->clk_module);
if (err) {
dev_err(&pdev->dev, "could not enable module clock: %d\n", err);
goto out;
}
err = clk_prepare_enable(data->clk_baud);
if (err) {
dev_err(&pdev->dev, "could not enable baud clock: %d\n", err);
goto out_disable_moduleclk;
}
uart.port.uartclk = clk_get_rate(data->clk_baud);
data->line = serial8250_register_8250_port(&uart);
if (data->line < 0) {
err = data->line;
goto out_disable_baudclk;
}
platform_set_drvdata(pdev, data);
return 0;
out_disable_baudclk:
clk_disable_unprepare(data->clk_baud);
out_disable_moduleclk:
clk_disable_unprepare(data->clk_module);
out:
return err;
}
static int ingenic_uart_remove(struct platform_device *pdev)
{
struct ingenic_uart_data *data = platform_get_drvdata(pdev);
serial8250_unregister_port(data->line);
clk_disable_unprepare(data->clk_module);
clk_disable_unprepare(data->clk_baud);
return 0;
}
static const struct ingenic_uart_config jz4740_uart_config = {
.tx_loadsz = 8,
.fifosize = 16,
};
static const struct ingenic_uart_config jz4760_uart_config = {
.tx_loadsz = 16,
.fifosize = 32,
};
static const struct ingenic_uart_config jz4780_uart_config = {
.tx_loadsz = 32,
.fifosize = 64,
};
static const struct ingenic_uart_config x1000_uart_config = {
.tx_loadsz = 32,
.fifosize = 64,
};
static const struct of_device_id of_match[] = {
{ .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config },
{ .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config },
{ .compatible = "ingenic,jz4770-uart", .data = &jz4760_uart_config },
{ .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config },
{ .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config },
{ .compatible = "ingenic,x1000-uart", .data = &x1000_uart_config },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_match);
static struct platform_driver ingenic_uart_platform_driver = {
.driver = {
.name = "ingenic-uart",
.of_match_table = of_match,
},
.probe = ingenic_uart_probe,
.remove = ingenic_uart_remove,
};
module_platform_driver(ingenic_uart_platform_driver);
MODULE_AUTHOR("Paul Burton");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Ingenic SoC UART driver");