cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

8250_bcm2835aux.c (7209B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Serial port driver for BCM2835AUX UART
      4 *
      5 * Copyright (C) 2016 Martin Sperl <kernel@martin.sperl.org>
      6 *
      7 * Based on 8250_lpc18xx.c:
      8 * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
      9 *
     10 * The bcm2835aux is capable of RTS auto flow-control, but this driver doesn't
     11 * take advantage of it yet.  When adding support, be sure not to enable it
     12 * simultaneously to rs485.
     13 */
     14
     15#include <linux/clk.h>
     16#include <linux/io.h>
     17#include <linux/module.h>
     18#include <linux/of.h>
     19#include <linux/platform_device.h>
     20#include <linux/property.h>
     21
     22#include "8250.h"
     23
     24#define BCM2835_AUX_UART_CNTL		8
     25#define BCM2835_AUX_UART_CNTL_RXEN	0x01 /* Receiver enable */
     26#define BCM2835_AUX_UART_CNTL_TXEN	0x02 /* Transmitter enable */
     27#define BCM2835_AUX_UART_CNTL_AUTORTS	0x04 /* RTS set by RX fill level */
     28#define BCM2835_AUX_UART_CNTL_AUTOCTS	0x08 /* CTS stops transmitter */
     29#define BCM2835_AUX_UART_CNTL_RTS3	0x00 /* RTS set until 3 chars left */
     30#define BCM2835_AUX_UART_CNTL_RTS2	0x10 /* RTS set until 2 chars left */
     31#define BCM2835_AUX_UART_CNTL_RTS1	0x20 /* RTS set until 1 chars left */
     32#define BCM2835_AUX_UART_CNTL_RTS4	0x30 /* RTS set until 4 chars left */
     33#define BCM2835_AUX_UART_CNTL_RTSINV	0x40 /* Invert auto RTS polarity */
     34#define BCM2835_AUX_UART_CNTL_CTSINV	0x80 /* Invert auto CTS polarity */
     35
     36/**
     37 * struct bcm2835aux_data - driver private data of BCM2835 auxiliary UART
     38 * @clk: clock producer of the port's uartclk
     39 * @line: index of the port's serial8250_ports[] entry
     40 * @cntl: cached copy of CNTL register
     41 */
     42struct bcm2835aux_data {
     43	struct clk *clk;
     44	int line;
     45	u32 cntl;
     46};
     47
     48struct bcm2835_aux_serial_driver_data {
     49	resource_size_t offset;
     50};
     51
     52static void bcm2835aux_rs485_start_tx(struct uart_8250_port *up)
     53{
     54	if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) {
     55		struct bcm2835aux_data *data = dev_get_drvdata(up->port.dev);
     56
     57		data->cntl &= ~BCM2835_AUX_UART_CNTL_RXEN;
     58		serial_out(up, BCM2835_AUX_UART_CNTL, data->cntl);
     59	}
     60
     61	/*
     62	 * On the bcm2835aux, the MCR register contains no other
     63	 * flags besides RTS.  So no need for a read-modify-write.
     64	 */
     65	if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND)
     66		serial8250_out_MCR(up, 0);
     67	else
     68		serial8250_out_MCR(up, UART_MCR_RTS);
     69}
     70
     71static void bcm2835aux_rs485_stop_tx(struct uart_8250_port *up)
     72{
     73	if (up->port.rs485.flags & SER_RS485_RTS_AFTER_SEND)
     74		serial8250_out_MCR(up, 0);
     75	else
     76		serial8250_out_MCR(up, UART_MCR_RTS);
     77
     78	if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) {
     79		struct bcm2835aux_data *data = dev_get_drvdata(up->port.dev);
     80
     81		data->cntl |= BCM2835_AUX_UART_CNTL_RXEN;
     82		serial_out(up, BCM2835_AUX_UART_CNTL, data->cntl);
     83	}
     84}
     85
     86static int bcm2835aux_serial_probe(struct platform_device *pdev)
     87{
     88	const struct bcm2835_aux_serial_driver_data *bcm_data;
     89	struct uart_8250_port up = { };
     90	struct bcm2835aux_data *data;
     91	resource_size_t offset = 0;
     92	struct resource *res;
     93	unsigned int uartclk;
     94	int ret;
     95
     96	/* allocate the custom structure */
     97	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
     98	if (!data)
     99		return -ENOMEM;
    100
    101	/* initialize data */
    102	up.capabilities = UART_CAP_FIFO | UART_CAP_MINI;
    103	up.port.dev = &pdev->dev;
    104	up.port.regshift = 2;
    105	up.port.type = PORT_16550;
    106	up.port.iotype = UPIO_MEM;
    107	up.port.fifosize = 8;
    108	up.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE |
    109			UPF_SKIP_TEST | UPF_IOREMAP;
    110	up.port.rs485_config = serial8250_em485_config;
    111	up.rs485_start_tx = bcm2835aux_rs485_start_tx;
    112	up.rs485_stop_tx = bcm2835aux_rs485_stop_tx;
    113
    114	/* initialize cached copy with power-on reset value */
    115	data->cntl = BCM2835_AUX_UART_CNTL_RXEN | BCM2835_AUX_UART_CNTL_TXEN;
    116
    117	platform_set_drvdata(pdev, data);
    118
    119	/* get the clock - this also enables the HW */
    120	data->clk = devm_clk_get_optional(&pdev->dev, NULL);
    121
    122	/* get the interrupt */
    123	ret = platform_get_irq(pdev, 0);
    124	if (ret < 0)
    125		return ret;
    126	up.port.irq = ret;
    127
    128	/* map the main registers */
    129	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    130	if (!res) {
    131		dev_err(&pdev->dev, "memory resource not found");
    132		return -EINVAL;
    133	}
    134
    135	bcm_data = device_get_match_data(&pdev->dev);
    136
    137	/* Some UEFI implementations (e.g. tianocore/edk2 for the Raspberry Pi)
    138	 * describe the miniuart with a base address that encompasses the auxiliary
    139	 * registers shared between the miniuart and spi.
    140	 *
    141	 * This is due to historical reasons, see discussion here :
    142	 * https://edk2.groups.io/g/devel/topic/87501357#84349
    143	 *
    144	 * We need to add the offset between the miniuart and auxiliary
    145	 * registers to get the real miniuart base address.
    146	 */
    147	if (bcm_data)
    148		offset = bcm_data->offset;
    149
    150	up.port.mapbase = res->start + offset;
    151	up.port.mapsize = resource_size(res) - offset;
    152
    153	/* Check for a fixed line number */
    154	ret = of_alias_get_id(pdev->dev.of_node, "serial");
    155	if (ret >= 0)
    156		up.port.line = ret;
    157
    158	/* enable the clock as a last step */
    159	ret = clk_prepare_enable(data->clk);
    160	if (ret) {
    161		dev_err(&pdev->dev, "unable to enable uart clock - %d\n",
    162			ret);
    163		return ret;
    164	}
    165
    166	uartclk = clk_get_rate(data->clk);
    167	if (!uartclk) {
    168		ret = device_property_read_u32(&pdev->dev, "clock-frequency", &uartclk);
    169		if (ret)
    170			return dev_err_probe(&pdev->dev, ret, "could not get clk rate\n");
    171	}
    172
    173	/* the HW-clock divider for bcm2835aux is 8,
    174	 * but 8250 expects a divider of 16,
    175	 * so we have to multiply the actual clock by 2
    176	 * to get identical baudrates.
    177	 */
    178	up.port.uartclk = uartclk * 2;
    179
    180	/* register the port */
    181	ret = serial8250_register_8250_port(&up);
    182	if (ret < 0) {
    183		dev_err_probe(&pdev->dev, ret, "unable to register 8250 port\n");
    184		goto dis_clk;
    185	}
    186	data->line = ret;
    187
    188	return 0;
    189
    190dis_clk:
    191	clk_disable_unprepare(data->clk);
    192	return ret;
    193}
    194
    195static int bcm2835aux_serial_remove(struct platform_device *pdev)
    196{
    197	struct bcm2835aux_data *data = platform_get_drvdata(pdev);
    198
    199	serial8250_unregister_port(data->line);
    200	clk_disable_unprepare(data->clk);
    201
    202	return 0;
    203}
    204
    205static const struct bcm2835_aux_serial_driver_data bcm2835_acpi_data = {
    206	.offset = 0x40,
    207};
    208
    209static const struct of_device_id bcm2835aux_serial_match[] = {
    210	{ .compatible = "brcm,bcm2835-aux-uart" },
    211	{ },
    212};
    213MODULE_DEVICE_TABLE(of, bcm2835aux_serial_match);
    214
    215static const struct acpi_device_id bcm2835aux_serial_acpi_match[] = {
    216	{ "BCM2836", (kernel_ulong_t)&bcm2835_acpi_data },
    217	{ }
    218};
    219MODULE_DEVICE_TABLE(acpi, bcm2835aux_serial_acpi_match);
    220
    221static struct platform_driver bcm2835aux_serial_driver = {
    222	.driver = {
    223		.name = "bcm2835-aux-uart",
    224		.of_match_table = bcm2835aux_serial_match,
    225		.acpi_match_table = bcm2835aux_serial_acpi_match,
    226	},
    227	.probe  = bcm2835aux_serial_probe,
    228	.remove = bcm2835aux_serial_remove,
    229};
    230module_platform_driver(bcm2835aux_serial_driver);
    231
    232#ifdef CONFIG_SERIAL_8250_CONSOLE
    233
    234static int __init early_bcm2835aux_setup(struct earlycon_device *device,
    235					const char *options)
    236{
    237	if (!device->port.membase)
    238		return -ENODEV;
    239
    240	device->port.iotype = UPIO_MEM32;
    241	device->port.regshift = 2;
    242
    243	return early_serial8250_setup(device, NULL);
    244}
    245
    246OF_EARLYCON_DECLARE(bcm2835aux, "brcm,bcm2835-aux-uart",
    247		    early_bcm2835aux_setup);
    248#endif
    249
    250MODULE_DESCRIPTION("BCM2835 auxiliar UART driver");
    251MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>");
    252MODULE_LICENSE("GPL v2");