Changeset 466e75ad in mainline


Ignore:
Timestamp:
2018-02-12T18:58:50Z (6 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.

Location:
kernel
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • kernel/arch/amd64/src/amd64.c

    r8192d8a r466e75ad  
    216216#endif
    217217        ns16550_instance_t *ns16550_instance
    218             = ns16550_init((ns16550_t *) NS16550_BASE, IRQ_NS16550, NULL, NULL,
     218            = ns16550_init(NS16550_BASE, 0, IRQ_NS16550, NULL, NULL,
    219219            ns16550_out_ptr);
    220220        if (ns16550_instance) {
  • kernel/arch/ia32/src/ia32.c

    r8192d8a r466e75ad  
    201201#endif
    202202        ns16550_instance_t *ns16550_instance
    203             = ns16550_init((ns16550_t *) NS16550_BASE, IRQ_NS16550, NULL, NULL,
     203            = ns16550_init(NS16550_BASE, 0, IRQ_NS16550, NULL, NULL,
    204204            ns16550_out_ptr);
    205205        if (ns16550_instance) {
  • kernel/arch/ia64/src/ia64.c

    r8192d8a r466e75ad  
    181181#ifdef CONFIG_NS16550
    182182        ns16550_instance_t *ns16550_instance
    183             = ns16550_init((ns16550_t *) NS16550_BASE, NS16550_IRQ, NULL, NULL,
     183            = ns16550_init(NS16550_BASE, 0, NS16550_IRQ, NULL, NULL,
    184184            NULL);
    185185        if (ns16550_instance) {
  • kernel/arch/sparc64/src/drivers/kbd.c

    r8192d8a r466e75ad  
    118118        size_t offset = pa - aligned_addr;
    119119       
    120         ns16550_t *ns16550 = (ns16550_t *) (km_map(aligned_addr, offset + size,
     120        ioport8_t *ns16550 = (ioport8_t *) (km_map(aligned_addr, offset + size,
    121121            PAGE_WRITE | PAGE_NOT_CACHEABLE) + offset);
    122122       
    123         ns16550_instance_t *ns16550_instance = ns16550_init(ns16550, inr, cir,
     123        ns16550_instance_t *ns16550_instance = ns16550_init(ns16550, 0, inr, cir,
    124124            cir_arg, NULL);
    125125        if (ns16550_instance) {
  • kernel/genarch/include/genarch/drivers/ns16550/ns16550.h

    r8192d8a r466e75ad  
    5050
    5151/** NS16550 registers. */
    52 typedef struct {
    53         union {
    54                 ioport8_t rbr;      /**< Receiver Buffer Register (read). */
    55                 ioport8_t thr;      /**< Transmitter Holder Register (write). */
    56         } __attribute__ ((packed));
    57         ioport8_t ier;      /**< Interrupt Enable Register. */
    58         union {
    59                 ioport8_t iir;  /**< Interrupt Ident Register (read). */
    60                 ioport8_t fcr;  /**< FIFO control register (write). */
    61         } __attribute__ ((packed));
    62         ioport8_t lcr;      /**< Line Control register. */
    63         ioport8_t mcr;      /**< Modem Control Register. */
    64         ioport8_t lsr;      /**< Line Status Register. */
    65 } __attribute__ ((packed)) ns16550_t;
     52enum {
     53        NS16550_REG_RBR = 0,  /**< Receiver Buffer Register (read). */
     54        NS16550_REG_THR = 0,  /**< Transmitter Holder Register (write). */
     55        NS16550_REG_IER = 1,  /**< Interrupt Enable Register. */
     56        NS16550_REG_IIR = 2,  /**< Interrupt Ident Register (read). */
     57        NS16550_REG_FCR = 2,  /**< FIFO control register (write). */
     58        NS16550_REG_LCR = 3,  /**< Line Control register. */
     59        NS16550_REG_MCR = 4,  /**< Modem Control Register. */
     60        NS16550_REG_LSR = 5,  /**< Line Status Register. */
     61};
    6662
    6763/** Structure representing the ns16550 device. */
    6864typedef struct {
    6965        irq_t irq;
    70         ns16550_t *ns16550;
     66        volatile ioport8_t *ns16550;
    7167        indev_t *input;
    7268        outdev_t *output;
    7369        parea_t parea;
     70        int reg_shift;
    7471} ns16550_instance_t;
    7572
    76 extern ns16550_instance_t *ns16550_init(ns16550_t *, inr_t, cir_t, void *,
     73extern ns16550_instance_t *ns16550_init(ioport8_t *, int, inr_t, cir_t, void *,
    7774    outdev_t **);
    7875extern void ns16550_wire(ns16550_instance_t *, indev_t *);
  • 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.