Ignore:
Timestamp:
2018-02-12T20:16:44Z (7 years ago)
Author:
GitHub <noreply@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
448e093
Parents:
8192d8a
git-author:
Jiří Zárevúcky <zarevucky.jiri@…> (2018-02-12 20:16:44)
git-committer:
GitHub <noreply@…> (2018-02-12 20:16:44)
Message:

Generalize ns16550 driver to work with more chips. (#20)

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 r38b0ae2  
    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.