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