source: mainline/kernel/genarch/src/drivers/ns16550/ns16550.c@ 466e75ad

Last change on this file since 466e75ad was 466e75ad, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

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.

  • Property mode set to 100644
File size: 5.3 KB
Line 
1/*
2 * Copyright (c) 2009 Jakub Jermar
3 * Copyright (c) 2018 CZ.NIC, z.s.p.o.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup genarch
31 * @{
32 */
33/**
34 * @file
35 * @brief NS 16550 serial controller driver.
36 */
37
38#include <assert.h>
39#include <genarch/drivers/ns16550/ns16550.h>
40#include <ddi/irq.h>
41#include <arch/asm.h>
42#include <console/chardev.h>
43#include <mm/slab.h>
44#include <str.h>
45
46#define LSR_DATA_READY 0x01
47#define LSR_TH_READY 0x20
48
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
57static irq_ownership_t ns16550_claim(irq_t *irq)
58{
59 ns16550_instance_t *instance = irq->instance;
60
61 if (_read(instance, NS16550_REG_LSR) & LSR_DATA_READY)
62 return IRQ_ACCEPT;
63 else
64 return IRQ_DECLINE;
65}
66
67static void ns16550_irq_handler(irq_t *irq)
68{
69 ns16550_instance_t *instance = irq->instance;
70
71 while (_read(instance, NS16550_REG_LSR) & LSR_DATA_READY) {
72 uint8_t data = _read(instance, NS16550_REG_RBR);
73 indev_push_character(instance->input, data);
74 }
75}
76
77/**< Clear input buffer. */
78static void ns16550_clear_buffer(ns16550_instance_t *instance)
79{
80 while (_read(instance, NS16550_REG_LSR) & LSR_DATA_READY)
81 (void) _read(instance, NS16550_REG_RBR);
82}
83
84static void ns16550_sendb(ns16550_instance_t *instance, uint8_t byte)
85{
86 while (!(_read(instance, NS16550_REG_LSR) & LSR_TH_READY))
87 ;
88 _write(instance, NS16550_REG_THR, byte);
89}
90
91static void ns16550_putchar(outdev_t *dev, wchar_t ch)
92{
93 ns16550_instance_t *instance = (ns16550_instance_t *) dev->data;
94
95 if ((!instance->parea.mapped) || (console_override)) {
96 if (ascii_check(ch))
97 ns16550_sendb(instance, (uint8_t) ch);
98 else
99 ns16550_sendb(instance, U_SPECIAL);
100 }
101}
102
103static outdev_operations_t ns16550_ops = {
104 .write = ns16550_putchar,
105 .redraw = NULL
106};
107
108/** Initialize ns16550.
109 *
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.
120 *
121 * @return Keyboard instance or NULL on failure.
122 *
123 */
124ns16550_instance_t *ns16550_init(ioport8_t *dev, int reg_shift, inr_t inr,
125 cir_t cir, void *cir_arg, outdev_t **output)
126{
127 ns16550_instance_t *instance
128 = malloc(sizeof(ns16550_instance_t), FRAME_ATOMIC);
129 if (instance) {
130 instance->ns16550 = dev;
131 instance->reg_shift = reg_shift;
132 instance->input = NULL;
133 instance->output = NULL;
134
135 if (output) {
136 instance->output = malloc(sizeof(outdev_t),
137 FRAME_ATOMIC);
138 if (!instance->output) {
139 free(instance);
140 return NULL;
141 }
142
143 outdev_initialize("ns16550", instance->output,
144 &ns16550_ops);
145 instance->output->data = instance;
146 *output = instance->output;
147 }
148
149 irq_initialize(&instance->irq);
150 instance->irq.inr = inr;
151 instance->irq.claim = ns16550_claim;
152 instance->irq.handler = ns16550_irq_handler;
153 instance->irq.instance = instance;
154 instance->irq.cir = cir;
155 instance->irq.cir_arg = cir_arg;
156
157 instance->parea.pbase = (uintptr_t) dev;
158 instance->parea.frames = 1;
159 instance->parea.unpriv = false;
160 instance->parea.mapped = false;
161 ddi_parea_register(&instance->parea);
162 }
163
164 return instance;
165}
166
167void ns16550_wire(ns16550_instance_t *instance, indev_t *input)
168{
169 assert(instance);
170 assert(input);
171
172 instance->input = input;
173 irq_register(&instance->irq);
174
175 ns16550_clear_buffer(instance);
176
177 /* Enable interrupts */
178 _write(instance, NS16550_REG_IER, IER_ERBFI);
179 _write(instance, NS16550_REG_MCR, MCR_OUT2);
180}
181
182/** @}
183 */
Note: See TracBrowser for help on using the repository browser.