linux/drivers/tty/serial/8250/8250_bcm2835aux.c
<<
>>
Prefs
   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
  11#include <linux/clk.h>
  12#include <linux/io.h>
  13#include <linux/module.h>
  14#include <linux/of.h>
  15#include <linux/platform_device.h>
  16
  17#include "8250.h"
  18
  19struct bcm2835aux_data {
  20        struct uart_8250_port uart;
  21        struct clk *clk;
  22        int line;
  23};
  24
  25static int bcm2835aux_serial_probe(struct platform_device *pdev)
  26{
  27        struct bcm2835aux_data *data;
  28        struct resource *res;
  29        int ret;
  30
  31        /* allocate the custom structure */
  32        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
  33        if (!data)
  34                return -ENOMEM;
  35
  36        /* initialize data */
  37        spin_lock_init(&data->uart.port.lock);
  38        data->uart.capabilities = UART_CAP_FIFO | UART_CAP_MINI;
  39        data->uart.port.dev = &pdev->dev;
  40        data->uart.port.regshift = 2;
  41        data->uart.port.type = PORT_16550;
  42        data->uart.port.iotype = UPIO_MEM;
  43        data->uart.port.fifosize = 8;
  44        data->uart.port.flags = UPF_SHARE_IRQ |
  45                                UPF_FIXED_PORT |
  46                                UPF_FIXED_TYPE |
  47                                UPF_SKIP_TEST;
  48
  49        /* get the clock - this also enables the HW */
  50        data->clk = devm_clk_get(&pdev->dev, NULL);
  51        ret = PTR_ERR_OR_ZERO(data->clk);
  52        if (ret) {
  53                dev_err(&pdev->dev, "could not get clk: %d\n", ret);
  54                return ret;
  55        }
  56
  57        /* get the interrupt */
  58        ret = platform_get_irq(pdev, 0);
  59        if (ret < 0) {
  60                dev_err(&pdev->dev, "irq not found - %i", ret);
  61                return ret;
  62        }
  63        data->uart.port.irq = ret;
  64
  65        /* map the main registers */
  66        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  67        if (!res) {
  68                dev_err(&pdev->dev, "memory resource not found");
  69                return -EINVAL;
  70        }
  71        data->uart.port.membase = devm_ioremap_resource(&pdev->dev, res);
  72        ret = PTR_ERR_OR_ZERO(data->uart.port.membase);
  73        if (ret)
  74                return ret;
  75
  76        /* Check for a fixed line number */
  77        ret = of_alias_get_id(pdev->dev.of_node, "serial");
  78        if (ret >= 0)
  79                data->uart.port.line = ret;
  80
  81        /* enable the clock as a last step */
  82        ret = clk_prepare_enable(data->clk);
  83        if (ret) {
  84                dev_err(&pdev->dev, "unable to enable uart clock - %d\n",
  85                        ret);
  86                return ret;
  87        }
  88
  89        /* the HW-clock divider for bcm2835aux is 8,
  90         * but 8250 expects a divider of 16,
  91         * so we have to multiply the actual clock by 2
  92         * to get identical baudrates.
  93         */
  94        data->uart.port.uartclk = clk_get_rate(data->clk) * 2;
  95
  96        /* register the port */
  97        ret = serial8250_register_8250_port(&data->uart);
  98        if (ret < 0) {
  99                dev_err(&pdev->dev, "unable to register 8250 port - %d\n",
 100                        ret);
 101                goto dis_clk;
 102        }
 103        data->line = ret;
 104
 105        platform_set_drvdata(pdev, data);
 106
 107        return 0;
 108
 109dis_clk:
 110        clk_disable_unprepare(data->clk);
 111        return ret;
 112}
 113
 114static int bcm2835aux_serial_remove(struct platform_device *pdev)
 115{
 116        struct bcm2835aux_data *data = platform_get_drvdata(pdev);
 117
 118        serial8250_unregister_port(data->uart.port.line);
 119        clk_disable_unprepare(data->clk);
 120
 121        return 0;
 122}
 123
 124static const struct of_device_id bcm2835aux_serial_match[] = {
 125        { .compatible = "brcm,bcm2835-aux-uart" },
 126        { },
 127};
 128MODULE_DEVICE_TABLE(of, bcm2835aux_serial_match);
 129
 130static struct platform_driver bcm2835aux_serial_driver = {
 131        .driver = {
 132                .name = "bcm2835-aux-uart",
 133                .of_match_table = bcm2835aux_serial_match,
 134        },
 135        .probe  = bcm2835aux_serial_probe,
 136        .remove = bcm2835aux_serial_remove,
 137};
 138module_platform_driver(bcm2835aux_serial_driver);
 139
 140MODULE_DESCRIPTION("BCM2835 auxiliar UART driver");
 141MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>");
 142MODULE_LICENSE("GPL v2");
 143