uboot/drivers/serial/serial_uniphier.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2012-2015 Panasonic Corporation
   4 * Copyright (C) 2015-2016 Socionext Inc.
   5 *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
   6 */
   7
   8#include <common.h>
   9#include <dm.h>
  10#include <linux/bitfield.h>
  11#include <linux/bitops.h>
  12#include <linux/bug.h>
  13#include <linux/io.h>
  14#include <linux/serial_reg.h>
  15#include <linux/sizes.h>
  16#include <linux/errno.h>
  17#include <serial.h>
  18#include <fdtdec.h>
  19
  20#define UNIPHIER_UART_REGSHIFT          2
  21
  22#define UNIPHIER_UART_RX                (0 << (UNIPHIER_UART_REGSHIFT))
  23#define UNIPHIER_UART_TX                UNIPHIER_UART_RX
  24/* bit[15:8] = CHAR, bit[7:0] = FCR */
  25#define UNIPHIER_UART_CHAR_FCR          (3 << (UNIPHIER_UART_REGSHIFT))
  26#define   UNIPHIER_UART_FCR_MASK                GENMASK(7, 0)
  27/* bit[15:8] = LCR, bit[7:0] = MCR */
  28#define UNIPHIER_UART_LCR_MCR           (4 << (UNIPHIER_UART_REGSHIFT))
  29#define   UNIPHIER_UART_LCR_MASK                GENMASK(15, 8)
  30#define UNIPHIER_UART_LSR               (5 << (UNIPHIER_UART_REGSHIFT))
  31/* Divisor Latch Register */
  32#define UNIPHIER_UART_DLR               (9 << (UNIPHIER_UART_REGSHIFT))
  33
  34struct uniphier_serial_priv {
  35        void __iomem *membase;
  36        unsigned int uartclk;
  37};
  38
  39static int uniphier_serial_setbrg(struct udevice *dev, int baudrate)
  40{
  41        struct uniphier_serial_priv *priv = dev_get_priv(dev);
  42        static const unsigned int mode_x_div = 16;
  43        unsigned int divisor;
  44
  45        divisor = DIV_ROUND_CLOSEST(priv->uartclk, mode_x_div * baudrate);
  46
  47        /* flush the trasmitter before changing hw setting */
  48        while (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_TEMT))
  49                ;
  50
  51        writel(divisor, priv->membase + UNIPHIER_UART_DLR);
  52
  53        return 0;
  54}
  55
  56static int uniphier_serial_getc(struct udevice *dev)
  57{
  58        struct uniphier_serial_priv *priv = dev_get_priv(dev);
  59
  60        if (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_DR))
  61                return -EAGAIN;
  62
  63        return readl(priv->membase + UNIPHIER_UART_RX);
  64}
  65
  66static int uniphier_serial_putc(struct udevice *dev, const char c)
  67{
  68        struct uniphier_serial_priv *priv = dev_get_priv(dev);
  69
  70        if (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_THRE))
  71                return -EAGAIN;
  72
  73        writel(c, priv->membase + UNIPHIER_UART_TX);
  74
  75        return 0;
  76}
  77
  78static int uniphier_serial_pending(struct udevice *dev, bool input)
  79{
  80        struct uniphier_serial_priv *priv = dev_get_priv(dev);
  81
  82        if (input)
  83                return readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_DR;
  84        else
  85                return !(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_THRE);
  86}
  87
  88/*
  89 * SPL does not have enough memory footprint for the clock driver.
  90 * Hardcode clock frequency for each SoC.
  91 */
  92struct uniphier_serial_clk_data {
  93        const char *compatible;
  94        unsigned int clk_rate;
  95};
  96
  97static const struct uniphier_serial_clk_data uniphier_serial_clk_data[] = {
  98        { .compatible = "socionext,uniphier-ld4",  .clk_rate = 36864000 },
  99        { .compatible = "socionext,uniphier-pro4", .clk_rate = 73728000 },
 100        { .compatible = "socionext,uniphier-sld8", .clk_rate = 80000000 },
 101        { .compatible = "socionext,uniphier-pro5", .clk_rate = 73728000 },
 102        { .compatible = "socionext,uniphier-pxs2", .clk_rate = 88888888 },
 103        { .compatible = "socionext,uniphier-ld6b", .clk_rate = 88888888 },
 104        { .compatible = "socionext,uniphier-ld11", .clk_rate = 58823529 },
 105        { .compatible = "socionext,uniphier-ld20", .clk_rate = 58823529 },
 106        { .compatible = "socionext,uniphier-pxs3", .clk_rate = 58823529 },
 107        { /* sentinel */ },
 108};
 109
 110static int uniphier_serial_probe(struct udevice *dev)
 111{
 112        struct uniphier_serial_priv *priv = dev_get_priv(dev);
 113        const struct uniphier_serial_clk_data *clk_data;
 114        ofnode root_node;
 115        fdt_addr_t base;
 116        u32 tmp;
 117
 118        base = dev_read_addr(dev);
 119        if (base == FDT_ADDR_T_NONE)
 120                return -EINVAL;
 121
 122        priv->membase = devm_ioremap(dev, base, SZ_64);
 123        if (!priv->membase)
 124                return -ENOMEM;
 125
 126        root_node = ofnode_path("/");
 127        clk_data = uniphier_serial_clk_data;
 128        while (clk_data->compatible) {
 129                if (ofnode_device_is_compatible(root_node,
 130                                                clk_data->compatible))
 131                        break;
 132                clk_data++;
 133        }
 134
 135        if (WARN_ON(!clk_data->compatible))
 136                return -ENOTSUPP;
 137
 138        priv->uartclk = clk_data->clk_rate;
 139
 140        /* flush the trasmitter before changing hw setting */
 141        while (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_TEMT))
 142                ;
 143
 144        /* enable FIFO */
 145        tmp = readl(priv->membase + UNIPHIER_UART_CHAR_FCR);
 146        tmp &= ~UNIPHIER_UART_FCR_MASK;
 147        tmp |= FIELD_PREP(UNIPHIER_UART_FCR_MASK, UART_FCR_ENABLE_FIFO);
 148        writel(tmp, priv->membase + UNIPHIER_UART_CHAR_FCR);
 149
 150        tmp = readl(priv->membase + UNIPHIER_UART_LCR_MCR);
 151        tmp &= ~UNIPHIER_UART_LCR_MASK;
 152        tmp |= FIELD_PREP(UNIPHIER_UART_LCR_MASK, UART_LCR_WLEN8);
 153        writel(tmp, priv->membase + UNIPHIER_UART_LCR_MCR);
 154
 155        return 0;
 156}
 157
 158static const struct udevice_id uniphier_uart_of_match[] = {
 159        { .compatible = "socionext,uniphier-uart" },
 160        { /* sentinel */ }
 161};
 162
 163static const struct dm_serial_ops uniphier_serial_ops = {
 164        .setbrg = uniphier_serial_setbrg,
 165        .getc = uniphier_serial_getc,
 166        .putc = uniphier_serial_putc,
 167        .pending = uniphier_serial_pending,
 168};
 169
 170U_BOOT_DRIVER(uniphier_serial) = {
 171        .name = "uniphier-uart",
 172        .id = UCLASS_SERIAL,
 173        .of_match = uniphier_uart_of_match,
 174        .probe = uniphier_serial_probe,
 175        .priv_auto      = sizeof(struct uniphier_serial_priv),
 176        .ops = &uniphier_serial_ops,
 177};
 178