uboot/drivers/serial/serial_mpc8xx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2000
   4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   5 */
   6
   7#include <common.h>
   8#include <command.h>
   9#include <dm.h>
  10#include <serial.h>
  11#include <watchdog.h>
  12#include <asm/cpm_8xx.h>
  13#include <asm/global_data.h>
  14#include <linux/compiler.h>
  15
  16DECLARE_GLOBAL_DATA_PTR;
  17
  18#if defined(CONFIG_8xx_CONS_SMC1)       /* Console on SMC1 */
  19#define SMC_INDEX       0
  20#define PROFF_SMC       PROFF_SMC1
  21#define CPM_CR_CH_SMC   CPM_CR_CH_SMC1
  22#define IOPINS          0xc0
  23
  24#elif defined(CONFIG_8xx_CONS_SMC2)     /* Console on SMC2 */
  25#define SMC_INDEX       1
  26#define PROFF_SMC       PROFF_SMC2
  27#define CPM_CR_CH_SMC   CPM_CR_CH_SMC2
  28#define IOPINS          0xc00
  29
  30#endif /* CONFIG_8xx_CONS_SMCx */
  31
  32struct serialbuffer {
  33        cbd_t   rxbd;           /* Rx BD */
  34        cbd_t   txbd;           /* Tx BD */
  35        uint    rxindex;        /* index for next character to read */
  36        uchar   rxbuf[CONFIG_SYS_SMC_RXBUFLEN];/* rx buffers */
  37        uchar   txbuf;  /* tx buffers */
  38};
  39
  40static void serial_setdivisor(cpm8xx_t __iomem *cp, int baudrate)
  41{
  42        int divisor = (gd->cpu_clk + 8 * baudrate) / 16 / baudrate;
  43
  44        if (divisor / 16 > 0x1000) {
  45                /* bad divisor, assume 50MHz clock and 9600 baud */
  46                divisor = (50 * 1000 * 1000 + 8 * 9600) / 16 / 9600;
  47        }
  48
  49        divisor /= CONFIG_SYS_BRGCLK_PRESCALE;
  50
  51        if (divisor <= 0x1000)
  52                out_be32(&cp->cp_brgc1, ((divisor - 1) << 1) | CPM_BRG_EN);
  53        else
  54                out_be32(&cp->cp_brgc1, ((divisor / 16 - 1) << 1) | CPM_BRG_EN |
  55                         CPM_BRG_DIV16);
  56}
  57
  58/*
  59 * Minimal serial functions needed to use one of the SMC ports
  60 * as serial console interface.
  61 */
  62
  63static int serial_mpc8xx_setbrg(struct udevice *dev, int baudrate)
  64{
  65        immap_t __iomem *im = (immap_t __iomem *)CONFIG_SYS_IMMR;
  66        cpm8xx_t __iomem *cp = &(im->im_cpm);
  67
  68        /* Set up the baud rate generator.
  69         * See 8xx_io/commproc.c for details.
  70         *
  71         * Wire BRG1 to SMCx
  72         */
  73
  74        out_be32(&cp->cp_simode, 0);
  75
  76        serial_setdivisor(cp, baudrate);
  77
  78        return 0;
  79}
  80
  81static int serial_mpc8xx_probe(struct udevice *dev)
  82{
  83        immap_t __iomem *im = (immap_t __iomem *)CONFIG_SYS_IMMR;
  84        smc_t __iomem *sp;
  85        smc_uart_t __iomem *up;
  86        cpm8xx_t __iomem *cp = &(im->im_cpm);
  87        struct serialbuffer __iomem *rtx;
  88
  89        /* initialize pointers to SMC */
  90
  91        sp = cp->cp_smc + SMC_INDEX;
  92        up = (smc_uart_t __iomem *)&cp->cp_dparam[PROFF_SMC];
  93        /* Disable relocation */
  94        out_be16(&up->smc_rpbase, 0);
  95
  96        /* Disable transmitter/receiver. */
  97        clrbits_be16(&sp->smc_smcmr, SMCMR_REN | SMCMR_TEN);
  98
  99        /* Enable SDMA. */
 100        out_be32(&im->im_siu_conf.sc_sdcr, 1);
 101
 102        /* clear error conditions */
 103        out_8(&im->im_sdma.sdma_sdsr, CONFIG_SYS_SDSR);
 104
 105        /* clear SDMA interrupt mask */
 106        out_8(&im->im_sdma.sdma_sdmr, CONFIG_SYS_SDMR);
 107
 108        /* Use Port B for SMCx instead of other functions. */
 109        setbits_be32(&cp->cp_pbpar, IOPINS);
 110        clrbits_be32(&cp->cp_pbdir, IOPINS);
 111        clrbits_be16(&cp->cp_pbodr, IOPINS);
 112
 113        /* Set the physical address of the host memory buffers in
 114         * the buffer descriptors.
 115         */
 116        rtx = (struct serialbuffer __iomem *)&cp->cp_dpmem[CPM_SERIAL_BASE];
 117        /* Allocate space for two buffer descriptors in the DP ram.
 118         * For now, this address seems OK, but it may have to
 119         * change with newer versions of the firmware.
 120         * damm: allocating space after the two buffers for rx/tx data
 121         */
 122
 123        out_be32(&rtx->rxbd.cbd_bufaddr, (__force uint)&rtx->rxbuf);
 124        out_be16(&rtx->rxbd.cbd_sc, 0);
 125
 126        out_be32(&rtx->txbd.cbd_bufaddr, (__force uint)&rtx->txbuf);
 127        out_be16(&rtx->txbd.cbd_sc, 0);
 128
 129        /* Set up the uart parameters in the parameter ram. */
 130        out_be16(&up->smc_rbase, CPM_SERIAL_BASE);
 131        out_be16(&up->smc_tbase, CPM_SERIAL_BASE + sizeof(cbd_t));
 132        out_8(&up->smc_rfcr, SMC_EB);
 133        out_8(&up->smc_tfcr, SMC_EB);
 134
 135        /* Set UART mode, 8 bit, no parity, one stop.
 136         * Enable receive and transmit.
 137         */
 138        out_be16(&sp->smc_smcmr, smcr_mk_clen(9) | SMCMR_SM_UART);
 139
 140        /* Mask all interrupts and remove anything pending.
 141        */
 142        out_8(&sp->smc_smcm, 0);
 143        out_8(&sp->smc_smce, 0xff);
 144
 145        /* Set up the baud rate generator */
 146        serial_mpc8xx_setbrg(dev, gd->baudrate);
 147
 148        /* Make the first buffer the only buffer. */
 149        setbits_be16(&rtx->txbd.cbd_sc, BD_SC_WRAP);
 150        setbits_be16(&rtx->rxbd.cbd_sc, BD_SC_EMPTY | BD_SC_WRAP);
 151
 152        /* single/multi character receive. */
 153        out_be16(&up->smc_mrblr, CONFIG_SYS_SMC_RXBUFLEN);
 154        out_be16(&up->smc_maxidl, CONFIG_SYS_MAXIDLE);
 155        out_be32(&rtx->rxindex, 0);
 156
 157        /* Initialize Tx/Rx parameters. */
 158        while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG)      /* wait if cp is busy */
 159                ;
 160
 161        out_be16(&cp->cp_cpcr,
 162                 mk_cr_cmd(CPM_CR_CH_SMC, CPM_CR_INIT_TRX) | CPM_CR_FLG);
 163
 164        while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG)      /* wait if cp is busy */
 165                ;
 166
 167        /* Enable transmitter/receiver. */
 168        setbits_be16(&sp->smc_smcmr, SMCMR_REN | SMCMR_TEN);
 169
 170        return 0;
 171}
 172
 173static int serial_mpc8xx_putc(struct udevice *dev, const char c)
 174{
 175        immap_t __iomem *im = (immap_t __iomem *)CONFIG_SYS_IMMR;
 176        cpm8xx_t        __iomem *cpmp = &(im->im_cpm);
 177        struct serialbuffer     __iomem *rtx;
 178
 179        if (c == '\n')
 180                serial_mpc8xx_putc(dev, '\r');
 181
 182        rtx = (struct serialbuffer __iomem *)&cpmp->cp_dpmem[CPM_SERIAL_BASE];
 183
 184        /* Wait for last character to go. */
 185        out_8(&rtx->txbuf, c);
 186        out_be16(&rtx->txbd.cbd_datlen, 1);
 187        setbits_be16(&rtx->txbd.cbd_sc, BD_SC_READY);
 188
 189        while (in_be16(&rtx->txbd.cbd_sc) & BD_SC_READY)
 190                WATCHDOG_RESET();
 191
 192        return 0;
 193}
 194
 195static int serial_mpc8xx_getc(struct udevice *dev)
 196{
 197        immap_t __iomem *im = (immap_t __iomem *)CONFIG_SYS_IMMR;
 198        cpm8xx_t        __iomem *cpmp = &(im->im_cpm);
 199        struct serialbuffer     __iomem *rtx;
 200        unsigned char  c;
 201        uint rxindex;
 202
 203        rtx = (struct serialbuffer __iomem *)&cpmp->cp_dpmem[CPM_SERIAL_BASE];
 204
 205        /* Wait for character to show up. */
 206        while (in_be16(&rtx->rxbd.cbd_sc) & BD_SC_EMPTY)
 207                WATCHDOG_RESET();
 208
 209        /* the characters are read one by one,
 210         * use the rxindex to know the next char to deliver
 211         */
 212        rxindex = in_be32(&rtx->rxindex);
 213        c = in_8(rtx->rxbuf + rxindex);
 214        rxindex++;
 215
 216        /* check if all char are readout, then make prepare for next receive */
 217        if (rxindex >= in_be16(&rtx->rxbd.cbd_datlen)) {
 218                rxindex = 0;
 219                setbits_be16(&rtx->rxbd.cbd_sc, BD_SC_EMPTY);
 220        }
 221        out_be32(&rtx->rxindex, rxindex);
 222        return c;
 223}
 224
 225static int serial_mpc8xx_pending(struct udevice *dev, bool input)
 226{
 227        immap_t __iomem *im = (immap_t __iomem *)CONFIG_SYS_IMMR;
 228        cpm8xx_t        __iomem *cpmp = &(im->im_cpm);
 229        struct serialbuffer     __iomem *rtx;
 230
 231        if (!input)
 232                return 0;
 233
 234        rtx = (struct serialbuffer __iomem *)&cpmp->cp_dpmem[CPM_SERIAL_BASE];
 235
 236        return !(in_be16(&rtx->rxbd.cbd_sc) & BD_SC_EMPTY);
 237}
 238
 239static const struct dm_serial_ops serial_mpc8xx_ops = {
 240        .putc = serial_mpc8xx_putc,
 241        .pending = serial_mpc8xx_pending,
 242        .getc = serial_mpc8xx_getc,
 243        .setbrg = serial_mpc8xx_setbrg,
 244};
 245
 246static const struct udevice_id serial_mpc8xx_ids[] = {
 247        { .compatible = "fsl,pq1-smc" },
 248        { }
 249};
 250
 251U_BOOT_DRIVER(serial_mpc8xx) = {
 252        .name   = "serial_mpc8xx",
 253        .id     = UCLASS_SERIAL,
 254        .of_match = serial_mpc8xx_ids,
 255        .probe = serial_mpc8xx_probe,
 256        .ops    = &serial_mpc8xx_ops,
 257        .flags = DM_FLAG_PRE_RELOC,
 258};
 259