Ignore:
Timestamp:
2018-02-12T18:58:50Z (8 years ago)
Author:
Jiří Zárevúcky <zarevucky.jiri@…>
Parents:
8192d8a
git-author:
Jiří Zárevúcky <zarevucky.jiri@…> (2018-02-12 17:25:25)
git-committer:
Jiří Zárevúcky <zarevucky.jiri@…> (2018-02-12 18:58:50)
Message:

Generalize ns16550 driver to work with more chips.

Some UART controllers are compatible with 16550A, but have more spacing
between registers. In particular, ARMADA 385, which is the basis of
Turris Omnia router, has a compatible UART with 32b registers instead of 8b
(with the top three bytes unused).

Note: The device tree file hints that some UARTs may need register reads/writes
that are wider than 8 bits, and this commit does not address that possibility.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/genarch/src/drivers/ns16550/ns16550.c

    r8192d8a r466e75ad  
    11/*
    22 * Copyright (c) 2009 Jakub Jermar
     3 * Copyright (c) 2018 CZ.NIC, z.s.p.o.
    34 * All rights reserved.
    45 *
     
    4647#define LSR_TH_READY    0x20
    4748
     49static inline uint8_t _read(ns16550_instance_t *inst, int reg) {
     50        return pio_read_8(&inst->ns16550[reg << inst->reg_shift]);
     51}
     52
     53static inline void _write(ns16550_instance_t *inst, int reg, uint8_t val) {
     54        pio_write_8(&inst->ns16550[reg << inst->reg_shift], val);
     55}
     56
    4857static irq_ownership_t ns16550_claim(irq_t *irq)
    4958{
    5059        ns16550_instance_t *instance = irq->instance;
    51         ns16550_t *dev = instance->ns16550;
    52        
    53         if (pio_read_8(&dev->lsr) & LSR_DATA_READY)
     60
     61        if (_read(instance, NS16550_REG_LSR) & LSR_DATA_READY)
    5462                return IRQ_ACCEPT;
    5563        else
     
    6068{
    6169        ns16550_instance_t *instance = irq->instance;
    62         ns16550_t *dev = instance->ns16550;
    6370       
    64         if (pio_read_8(&dev->lsr) & LSR_DATA_READY) {
    65                 uint8_t data = pio_read_8(&dev->rbr);
     71        while (_read(instance, NS16550_REG_LSR) & LSR_DATA_READY) {
     72                uint8_t data = _read(instance, NS16550_REG_RBR);
    6673                indev_push_character(instance->input, data);
    6774        }
     
    6976
    7077/**< Clear input buffer. */
    71 static void ns16550_clear_buffer(ns16550_t *dev)
     78static void ns16550_clear_buffer(ns16550_instance_t *instance)
    7279{
    73         while ((pio_read_8(&dev->lsr) & LSR_DATA_READY))
    74                 (void) pio_read_8(&dev->rbr);
     80        while (_read(instance, NS16550_REG_LSR) & LSR_DATA_READY)
     81                (void) _read(instance, NS16550_REG_RBR);
    7582}
    7683
    77 static void ns16550_sendb(ns16550_t *dev, uint8_t byte)
     84static void ns16550_sendb(ns16550_instance_t *instance, uint8_t byte)
    7885{
    79         while (!(pio_read_8(&dev->lsr) & LSR_TH_READY))
     86        while (!(_read(instance, NS16550_REG_LSR) & LSR_TH_READY))
    8087                ;
    81         pio_write_8(&dev->thr, byte);
     88        _write(instance, NS16550_REG_THR, byte);
    8289}
    8390
     
    8895        if ((!instance->parea.mapped) || (console_override)) {
    8996                if (ascii_check(ch))
    90                         ns16550_sendb(instance->ns16550, (uint8_t) ch);
     97                        ns16550_sendb(instance, (uint8_t) ch);
    9198                else
    92                         ns16550_sendb(instance->ns16550, U_SPECIAL);
     99                        ns16550_sendb(instance, U_SPECIAL);
    93100        }
    94101}
     
    101108/** Initialize ns16550.
    102109 *
    103  * @param dev      Addrress of the beginning of the device in I/O space.
    104  * @param inr      Interrupt number.
    105  * @param cir      Clear interrupt function.
    106  * @param cir_arg  First argument to cir.
    107  * @param output   Where to store pointer to the output device
    108  *                 or NULL if the caller is not interested in
    109  *                 writing to the serial port.
     110 * @param dev        Address of the beginning of the device in I/O space.
     111 * @param reg_shift  Spacing between individual register addresses, in log2.
     112 *                   The individual register location is calculated as
     113 *                   `base + (register offset << reg_shift)`.
     114 * @param inr        Interrupt number.
     115 * @param cir        Clear interrupt function.
     116 * @param cir_arg    First argument to cir.
     117 * @param output     Where to store pointer to the output device
     118 *                   or NULL if the caller is not interested in
     119 *                   writing to the serial port.
    110120 *
    111121 * @return Keyboard instance or NULL on failure.
    112122 *
    113123 */
    114 ns16550_instance_t *ns16550_init(ns16550_t *dev, inr_t inr, cir_t cir,
    115     void *cir_arg, outdev_t **output)
     124ns16550_instance_t *ns16550_init(ioport8_t *dev, int reg_shift, inr_t inr,
     125    cir_t cir, void *cir_arg, outdev_t **output)
    116126{
    117127        ns16550_instance_t *instance
     
    119129        if (instance) {
    120130                instance->ns16550 = dev;
     131                instance->reg_shift = reg_shift;
    121132                instance->input = NULL;
    122133                instance->output = NULL;
     
    162173        irq_register(&instance->irq);
    163174       
    164         ns16550_clear_buffer(instance->ns16550);
     175        ns16550_clear_buffer(instance);
    165176       
    166177        /* Enable interrupts */
    167         pio_write_8(&instance->ns16550->ier, IER_ERBFI);
    168         pio_write_8(&instance->ns16550->mcr, MCR_OUT2);
     178        _write(instance, NS16550_REG_IER, IER_ERBFI);
     179        _write(instance, NS16550_REG_MCR, MCR_OUT2);
    169180}
    170181
Note: See TracChangeset for help on using the changeset viewer.