source: mainline/uspace/drv/char/ns8250/ns8250.c@ d37dd3b9

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d37dd3b9 was d51838f, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Let leaf drivers enable/disable/clear interrupts via hw_res instead of directly using irc.

  • Property mode set to 100644
File size: 27.6 KB
RevLine 
[68414f4a]1/*
[5fe1c32]2 * Copyright (c) 2010 Lenka Trochtova
[68414f4a]3 * Copyright (c) 2011 Jiri Svoboda
[5fe1c32]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/**
[04c7003f]31 * @defgroup ns8250 Serial port driver.
[5fe1c32]32 * @brief HelenOS serial port driver.
33 * @{
34 */
35
36/** @file
37 */
38
39#include <assert.h>
40#include <stdio.h>
41#include <errno.h>
[3e6a98c5]42#include <stdbool.h>
[5fe1c32]43#include <fibril_synch.h>
44#include <stdlib.h>
[c47e1a8]45#include <str.h>
[5fe1c32]46#include <ctype.h>
47#include <macros.h>
48#include <malloc.h>
49#include <dirent.h>
[1f8657b]50#include <ddi.h>
[5fe1c32]51
[af6b5157]52#include <ddf/driver.h>
53#include <ddf/interrupt.h>
[fc51296]54#include <ddf/log.h>
[41b56084]55#include <ops/char_dev.h>
[5fe1c32]56
57#include <device/hw_res.h>
[f4ef3c2]58#include <ipc/serial_ctl.h>
[5fe1c32]59
[1f8657b]60#include "cyclic_buffer.h"
61
[04c7003f]62#define NAME "ns8250"
[5fe1c32]63
[1f8657b]64#define REG_COUNT 7
[f4ef3c2]65#define MAX_BAUD_RATE 115200
[cf8cc36]66#define DLAB_MASK (1 << 7)
67
[c7235d40]68/** Interrupt Enable Register definition. */
69#define NS8250_IER_RXREADY (1 << 0)
70#define NS8250_IER_THRE (1 << 1)
71#define NS8250_IER_RXSTATUS (1 << 2)
72#define NS8250_IER_MODEM_STATUS (1 << 3)
73
74/** Interrupt ID Register definition. */
75#define NS8250_IID_ACTIVE (1 << 0)
[e0cd9042]76#define NS8250_IID_CAUSE_MASK 0x0e
77#define NS8250_IID_CAUSE_RXSTATUS 0x06
[c7235d40]78
79/** FIFO Control Register definition. */
80#define NS8250_FCR_FIFOENABLE (1 << 0)
81#define NS8250_FCR_RXFIFORESET (1 << 1)
82#define NS8250_FCR_TXFIFORESET (1 << 2)
83#define NS8250_FCR_DMAMODE (1 << 3)
84#define NS8250_FCR_RXTRIGGERLOW (1 << 6)
85#define NS8250_FCR_RXTRIGGERHI (1 << 7)
86
87/** Line Control Register definition. */
88#define NS8250_LCR_STOPBITS (1 << 2)
89#define NS8250_LCR_PARITY (1 << 3)
90#define NS8250_LCR_SENDBREAK (1 << 6)
91#define NS8250_LCR_DLAB (1 << 7)
92
93/** Modem Control Register definition. */
94#define NS8250_MCR_DTR (1 << 0)
95#define NS8250_MCR_RTS (1 << 1)
96#define NS8250_MCR_OUT1 (1 << 2)
97#define NS8250_MCR_OUT2 (1 << 3)
98#define NS8250_MCR_LOOPBACK (1 << 4)
99#define NS8250_MCR_ALL (0x1f)
100
101/** Line Status Register definition. */
102#define NS8250_LSR_RXREADY (1 << 0)
103#define NS8250_LSR_OE (1 << 1)
104#define NS8250_LSR_PE (1 << 2)
105#define NS8250_LSR_FE (1 << 3)
106#define NS8250_LSR_BREAK (1 << 4)
107#define NS8250_LSR_THRE (1 << 5)
108#define NS8250_LSR_TSE (1 << 6)
109
110/** Modem Status Register definition. */
111#define NS8250_MSR_DELTACTS (1 << 0)
112#define NS8250_MSR_DELTADSR (1 << 1)
113#define NS8250_MSR_RITRAILING (1 << 2)
114#define NS8250_MSR_DELTADCD (1 << 3)
115#define NS8250_MSR_CTS (1 << 4)
116#define NS8250_MSR_DSR (1 << 5)
117#define NS8250_MSR_RI (1 << 6)
118#define NS8250_MSR_DCD (1 << 7)
119#define NS8250_MSR_SIGNALS (NS8250_MSR_CTS | NS8250_MSR_DSR \
120 | NS8250_MSR_RI | NS8250_MSR_DCD)
121
[49698fa]122/** The number of bits of one data unit send by the serial port. */
[cf8cc36]123typedef enum {
124 WORD_LENGTH_5,
125 WORD_LENGTH_6,
126 WORD_LENGTH_7,
[49698fa]127 WORD_LENGTH_8
[cf8cc36]128} word_length_t;
129
130/** The number of stop bits used by the serial port. */
131typedef enum {
132 /** Use one stop bit. */
133 ONE_STOP_BIT,
134 /** 1.5 stop bits for word length 5, 2 stop bits otherwise. */
[49698fa]135 TWO_STOP_BITS
[cf8cc36]136} stop_bit_t;
137
[c7235d40]138/** 8250 UART registers layout. */
139typedef struct {
140 ioport8_t data; /**< Data register. */
141 ioport8_t ier; /**< Interrupt Enable Reg. */
142 ioport8_t iid; /**< Interrupt ID Reg. */
143 ioport8_t lcr; /**< Line Control Reg. */
144 ioport8_t mcr; /**< Modem Control Reg. */
145 ioport8_t lsr; /**< Line Status Reg. */
146 ioport8_t msr; /**< Modem Status Reg. */
147} ns8250_regs_t;
148
[49698fa]149/** The driver data for the serial port devices. */
[68414f4a]150typedef struct ns8250 {
151 /** DDF device node */
[83a2f43]152 ddf_dev_t *dev;
[68414f4a]153 /** DDF function node */
[83a2f43]154 ddf_fun_t *fun;
[d51838f]155 /** Parent session */
156 async_sess_t *parent_sess;
[c7235d40]157 /** I/O registers **/
158 ns8250_regs_t *regs;
[4510e06]159 /** Are there any clients connected to the device? */
160 unsigned client_connections;
[cf8cc36]161 /** The irq assigned to this device. */
[1f8657b]162 int irq;
[3f74275]163 /** IRQ capability handle */
[e9d15d9]164 int irq_cap;
[cf8cc36]165 /** The base i/o address of the devices registers. */
[340513c]166 uintptr_t io_addr;
[cf8cc36]167 /** The i/o port used to access the serial ports registers. */
[1f8657b]168 ioport8_t *port;
[381ff2f]169 /** The buffer for incoming data. */
[1f8657b]170 cyclic_buffer_t input_buffer;
[49698fa]171 /** The fibril mutex for synchronizing the access to the device. */
172 fibril_mutex_t mutex;
[91ecaa10]173 /** Indicates that some data has become available */
174 fibril_condvar_t input_buffer_available;
[5b68e0c]175 /** True if device is removed. */
176 bool removed;
[68414f4a]177} ns8250_t;
[5fe1c32]178
[56fd7cf]179/** Obtain soft-state structure from device node */
180static ns8250_t *dev_ns8250(ddf_dev_t *dev)
181{
182 return ddf_dev_data_get(dev);
183}
184
185/** Obtain soft-state structure from function node */
186static ns8250_t *fun_ns8250(ddf_fun_t *fun)
187{
188 return dev_ns8250(ddf_fun_get_dev(fun));
189}
190
[381ff2f]191/** Find out if there is some incoming data available on the serial port.
[49698fa]192 *
193 * @param port The base address of the serial port device's ports.
194 * @return True if there are data waiting to be read, false
195 * otherwise.
[cf8cc36]196 */
[c7235d40]197static bool ns8250_received(ns8250_regs_t *regs)
[ca97cad]198{
[c7235d40]199 return (pio_read_8(&regs->lsr) & NS8250_LSR_RXREADY) != 0;
[ca97cad]200}
201
[cf8cc36]202/** Read one byte from the serial port.
[49698fa]203 *
204 * @param port The base address of the serial port device's ports.
205 * @return The data read.
[cf8cc36]206 */
[c7235d40]207static uint8_t ns8250_read_8(ns8250_regs_t *regs)
[ca97cad]208{
[c7235d40]209 return pio_read_8(&regs->data);
[ca97cad]210}
211
[cf8cc36]212/** Find out wheter it is possible to send data.
[49698fa]213 *
214 * @param port The base address of the serial port device's ports.
[cf8cc36]215 */
[c7235d40]216static bool is_transmit_empty(ns8250_regs_t *regs)
[ca97cad]217{
[c7235d40]218 return (pio_read_8(&regs->lsr) & NS8250_LSR_THRE) != 0;
[ca97cad]219}
220
[cf8cc36]221/** Write one character on the serial port.
[49698fa]222 *
223 * @param port The base address of the serial port device's ports.
224 * @param c The character to be written to the serial port device.
[cf8cc36]225 */
[c7235d40]226static void ns8250_write_8(ns8250_regs_t *regs, uint8_t c)
[ca97cad]227{
[c7235d40]228 while (!is_transmit_empty(regs))
[ca97cad]229 ;
230
[c7235d40]231 pio_write_8(&regs->data, c);
[ca97cad]232}
233
[cf8cc36]234/** Read data from the serial port device.
[49698fa]235 *
[68414f4a]236 * @param fun The serial port function
[956d4281]237 * @param buf The output buffer for read data.
[49698fa]238 * @param count The number of bytes to be read.
239 *
240 * @return The number of bytes actually read on success, negative
241 * error number otherwise.
[cf8cc36]242 */
[83a2f43]243static int ns8250_read(ddf_fun_t *fun, char *buf, size_t count)
[f658458]244{
[56fd7cf]245 ns8250_t *ns = fun_ns8250(fun);
[91ecaa10]246 int ret = 0;
247
248 if (count == 0) return 0;
[bb864a0]249
[68414f4a]250 fibril_mutex_lock(&ns->mutex);
[91ecaa10]251 while (buf_is_empty(&ns->input_buffer))
252 fibril_condvar_wait(&ns->input_buffer_available, &ns->mutex);
[68414f4a]253 while (!buf_is_empty(&ns->input_buffer) && (size_t)ret < count) {
254 buf[ret] = (char)buf_pop_front(&ns->input_buffer);
[bb864a0]255 ret++;
256 }
[68414f4a]257 fibril_mutex_unlock(&ns->mutex);
[bb864a0]258
259 return ret;
[f658458]260}
261
[cf8cc36]262/** Write a character to the serial port.
[49698fa]263 *
[68414f4a]264 * @param ns Serial port device
265 * @param c The character to be written
[cf8cc36]266 */
[68414f4a]267static inline void ns8250_putchar(ns8250_t *ns, uint8_t c)
[49698fa]268{
[68414f4a]269 fibril_mutex_lock(&ns->mutex);
[c7235d40]270 ns8250_write_8(ns->regs, c);
[68414f4a]271 fibril_mutex_unlock(&ns->mutex);
[ca97cad]272}
273
[cf8cc36]274/** Write data to the serial port.
[49698fa]275 *
[68414f4a]276 * @param fun The serial port function
277 * @param buf The data to be written
278 * @param count The number of bytes to be written
279 * @return Zero on success
[cf8cc36]280 */
[83a2f43]281static int ns8250_write(ddf_fun_t *fun, char *buf, size_t count)
[f658458]282{
[56fd7cf]283 ns8250_t *ns = fun_ns8250(fun);
[ca97cad]284 size_t idx;
[49698fa]285
286 for (idx = 0; idx < count; idx++)
[68414f4a]287 ns8250_putchar(ns, (uint8_t) buf[idx]);
[ca97cad]288
[84c89718]289 return count;
[f658458]290}
291
[83a2f43]292static ddf_dev_ops_t ns8250_dev_ops;
[5fe1c32]293
[49698fa]294/** The character interface's callbacks. */
[50c57df]295static char_dev_ops_t ns8250_char_dev_ops = {
[04c7003f]296 .read = &ns8250_read,
297 .write = &ns8250_write
[f658458]298};
299
[0c0f823b]300static int ns8250_dev_add(ddf_dev_t *dev);
[5b68e0c]301static int ns8250_dev_remove(ddf_dev_t *dev);
[5fe1c32]302
[49698fa]303/** The serial port device driver's standard operations. */
[04c7003f]304static driver_ops_t ns8250_ops = {
[0c0f823b]305 .dev_add = &ns8250_dev_add,
[5b68e0c]306 .dev_remove = &ns8250_dev_remove
[5fe1c32]307};
308
[49698fa]309/** The serial port device driver structure. */
[04c7003f]310static driver_t ns8250_driver = {
[5fe1c32]311 .name = NAME,
[04c7003f]312 .driver_ops = &ns8250_ops
[5fe1c32]313};
314
[68414f4a]315/** Clean up the serial port soft-state
[49698fa]316 *
[68414f4a]317 * @param ns Serial port device
[cf8cc36]318 */
[68414f4a]319static void ns8250_dev_cleanup(ns8250_t *ns)
[1f8657b]320{
321}
322
[cf8cc36]323/** Enable the i/o ports of the device.
[49698fa]324 *
[68414f4a]325 * @param ns Serial port device
326 * @return True on success, false otherwise
[cf8cc36]327 */
[68414f4a]328static bool ns8250_pio_enable(ns8250_t *ns)
[1f8657b]329{
[56fd7cf]330 ddf_msg(LVL_DEBUG, "ns8250_pio_enable %s", ddf_dev_get_name(ns->dev));
[1f8657b]331
[49698fa]332 /* Gain control over port's registers. */
[340513c]333 if (pio_enable((void *) ns->io_addr, REG_COUNT,
[68414f4a]334 (void **) &ns->port)) {
[340513c]335 ddf_msg(LVL_ERROR, "Cannot map the port %#" PRIxn
[56fd7cf]336 " for device %s.", ns->io_addr, ddf_dev_get_name(ns->dev));
[1f8657b]337 return false;
338 }
[c7235d40]339
340 ns->regs = (ns8250_regs_t *)ns->port;
[1f8657b]341
342 return true;
343}
344
[cf8cc36]345/** Probe the serial port device for its presence.
[49698fa]346 *
[68414f4a]347 * @param ns Serial port device
348 * @return True if the device is present, false otherwise
[cf8cc36]349 */
[68414f4a]350static bool ns8250_dev_probe(ns8250_t *ns)
[1f8657b]351{
[56fd7cf]352 ddf_msg(LVL_DEBUG, "ns8250_dev_probe %s", ddf_dev_get_name(ns->dev));
[1f8657b]353
354 bool res = true;
355 uint8_t olddata;
356
[c7235d40]357 olddata = pio_read_8(&ns->regs->mcr);
[1f8657b]358
[c7235d40]359 pio_write_8(&ns->regs->mcr, NS8250_MCR_LOOPBACK);
360 if (pio_read_8(&ns->regs->msr) & NS8250_MSR_SIGNALS)
[1f8657b]361 res = false;
362
[c7235d40]363 pio_write_8(&ns->regs->mcr, NS8250_MCR_ALL);
364 if ((pio_read_8(&ns->regs->msr) & NS8250_MSR_SIGNALS)
365 != NS8250_MSR_SIGNALS)
[1f8657b]366 res = false;
367
[c7235d40]368 pio_write_8(&ns->regs->mcr, olddata);
[1f8657b]369
[fc51296]370 if (!res) {
[ebcb05a]371 ddf_msg(LVL_DEBUG, "Device %s is not present.",
[56fd7cf]372 ddf_dev_get_name(ns->dev));
[fc51296]373 }
[1f8657b]374
[49698fa]375 return res;
[1f8657b]376}
377
[cf8cc36]378/** Initialize serial port device.
[49698fa]379 *
[68414f4a]380 * @param ns Serial port device
381 * @return Zero on success, negative error number otherwise
[cf8cc36]382 */
[68414f4a]383static int ns8250_dev_initialize(ns8250_t *ns)
[1f8657b]384{
[df747b9c]385 int ret = EOK;
[49698fa]386
[56fd7cf]387 ddf_msg(LVL_DEBUG, "ns8250_dev_initialize %s", ddf_dev_get_name(ns->dev));
388
[1f8657b]389 hw_resource_list_t hw_resources;
390 memset(&hw_resources, 0, sizeof(hw_resource_list_t));
391
[49698fa]392 /* Get hw resources. */
[d51838f]393 ret = hw_res_get_resource_list(ns->parent_sess, &hw_resources);
[be942bc]394 if (ret != EOK) {
[fc51296]395 ddf_msg(LVL_ERROR, "Failed to get HW resources for device "
[56fd7cf]396 "%s.", ddf_dev_get_name(ns->dev));
[1f8657b]397 goto failed;
[49698fa]398 }
[1f8657b]399
400 size_t i;
401 hw_resource_t *res;
402 bool irq = false;
403 bool ioport = false;
404
405 for (i = 0; i < hw_resources.count; i++) {
406 res = &hw_resources.resources[i];
407 switch (res->type) {
408 case INTERRUPT:
[68414f4a]409 ns->irq = res->res.interrupt.irq;
[1f8657b]410 irq = true;
[3a67d63]411 ddf_msg(LVL_NOTE, "Device %s was assigned irq = 0x%x.",
[56fd7cf]412 ddf_dev_get_name(ns->dev), ns->irq);
[1f8657b]413 break;
[49698fa]414
[1f8657b]415 case IO_RANGE:
[68414f4a]416 ns->io_addr = res->res.io_range.address;
[1f8657b]417 if (res->res.io_range.size < REG_COUNT) {
[fc51296]418 ddf_msg(LVL_ERROR, "I/O range assigned to "
[56fd7cf]419 "device %s is too small.", ddf_dev_get_name(ns->dev));
[be942bc]420 ret = ELIMIT;
[1f8657b]421 goto failed;
422 }
423 ioport = true;
[3a67d63]424 ddf_msg(LVL_NOTE, "Device %s was assigned I/O address = "
[340513c]425 "0x%#" PRIxn ".", ddf_dev_get_name(ns->dev), ns->io_addr);
[ebcb05a]426 break;
[49698fa]427
[dafe675]428 default:
429 break;
[1f8657b]430 }
431 }
432
433 if (!irq || !ioport) {
[ebcb05a]434 ddf_msg(LVL_ERROR, "Missing HW resource(s) for device %s.",
[56fd7cf]435 ddf_dev_get_name(ns->dev));
[be942bc]436 ret = ENOENT;
[1f8657b]437 goto failed;
[49698fa]438 }
[1f8657b]439
[f724e82]440 hw_res_clean_resource_list(&hw_resources);
[df747b9c]441 return ret;
[1f8657b]442
443failed:
[68414f4a]444 ns8250_dev_cleanup(ns);
[f724e82]445 hw_res_clean_resource_list(&hw_resources);
[49698fa]446 return ret;
[1f8657b]447}
448
[cf8cc36]449/** Enable interrupts on the serial port device.
[49698fa]450 *
[68414f4a]451 * Interrupt when data is received
[49698fa]452 *
453 * @param port The base address of the serial port device's ports.
[cf8cc36]454 */
[c7235d40]455static inline void ns8250_port_interrupts_enable(ns8250_regs_t *regs)
[bab6388]456{
[c7235d40]457 /* Interrupt when data received. */
[e0cd9042]458 pio_write_8(&regs->ier, NS8250_IER_RXREADY | NS8250_IER_RXSTATUS);
[c7235d40]459 pio_write_8(&regs->mcr, NS8250_MCR_DTR | NS8250_MCR_RTS
460 | NS8250_MCR_OUT2);
[f4ef3c2]461}
462
[cf8cc36]463/** Disable interrupts on the serial port device.
[49698fa]464 *
[68414f4a]465 * @param port The base address of the serial port device's ports
[cf8cc36]466 */
[c7235d40]467static inline void ns8250_port_interrupts_disable(ns8250_regs_t *regs)
[f4ef3c2]468{
[c7235d40]469 pio_write_8(&regs->ier, 0x0); /* Disable all interrupts. */
[f4ef3c2]470}
471
[cf8cc36]472/** Enable interrupts for the serial port device.
[49698fa]473 *
[68414f4a]474 * @param ns Serial port device
475 * @return Zero on success, negative error number otherwise
[cf8cc36]476 */
[68414f4a]477static int ns8250_interrupt_enable(ns8250_t *ns)
[cfe7716]478{
[ebc9c2c]479 /* Enable interrupt using IRC service. */
[d51838f]480 int rc = hw_res_enable_interrupt(ns->parent_sess, ns->irq);
[ebc9c2c]481 if (rc != EOK)
[128c78b]482 return EIO;
[ebc9c2c]483
[e0cd9042]484 /* Read LSR to clear possible previous LSR interrupt */
485 pio_read_8(&ns->regs->lsr);
[ebc9c2c]486
[49698fa]487 /* Enable interrupt on the serial port. */
[c7235d40]488 ns8250_port_interrupts_enable(ns->regs);
[cfe7716]489
490 return EOK;
491}
492
[cf8cc36]493/** Set Divisor Latch Access Bit.
[49698fa]494 *
495 * When the Divisor Latch Access Bit is set, it is possible to set baud rate of
496 * the serial port device.
497 *
498 * @param port The base address of the serial port device's ports.
[cf8cc36]499 */
[c7235d40]500static inline void enable_dlab(ns8250_regs_t *regs)
[cf8cc36]501{
[c7235d40]502 uint8_t val = pio_read_8(&regs->lcr);
503 pio_write_8(&regs->lcr, val | NS8250_LCR_DLAB);
[cf8cc36]504}
505
506/** Clear Divisor Latch Access Bit.
[49698fa]507 *
508 * @param port The base address of the serial port device's ports.
[cf8cc36]509 */
[c7235d40]510static inline void clear_dlab(ns8250_regs_t *regs)
[cf8cc36]511{
[c7235d40]512 uint8_t val = pio_read_8(&regs->lcr);
513 pio_write_8(&regs->lcr, val & (~NS8250_LCR_DLAB));
[cf8cc36]514}
515
516/** Set baud rate of the serial communication on the serial device.
[49698fa]517 *
518 * @param port The base address of the serial port device's ports.
519 * @param baud_rate The baud rate to be used by the device.
520 * @return Zero on success, negative error number otherwise (EINVAL
521 * if the specified baud_rate is not valid).
[cf8cc36]522 */
[c7235d40]523static int ns8250_port_set_baud_rate(ns8250_regs_t *regs, unsigned int baud_rate)
[f4ef3c2]524{
525 uint16_t divisor;
526 uint8_t div_low, div_high;
527
[33dbbd2]528 if (baud_rate < 50 || MAX_BAUD_RATE % baud_rate != 0) {
[ebcb05a]529 ddf_msg(LVL_ERROR, "Invalid baud rate %d requested.",
[fc51296]530 baud_rate);
[49698fa]531 return EINVAL;
[f4ef3c2]532 }
533
534 divisor = MAX_BAUD_RATE / baud_rate;
535 div_low = (uint8_t)divisor;
[49698fa]536 div_high = (uint8_t)(divisor >> 8);
[f4ef3c2]537
[49698fa]538 /* Enable DLAB to be able to access baud rate divisor. */
[c7235d40]539 enable_dlab(regs);
[f4ef3c2]540
[49698fa]541 /* Set divisor low byte. */
[c7235d40]542 pio_write_8(&regs->data, div_low);
[49698fa]543 /* Set divisor high byte. */
[c7235d40]544 pio_write_8(&regs->ier, div_high);
[f4ef3c2]545
[c7235d40]546 clear_dlab(regs);
[f4ef3c2]547
[49698fa]548 return EOK;
[f4ef3c2]549}
550
[cf8cc36]551/** Get baud rate used by the serial port device.
[49698fa]552 *
553 * @param port The base address of the serial port device's ports.
554 * @param baud_rate The ouput parameter to which the baud rate is stored.
[cf8cc36]555 */
[c7235d40]556static unsigned int ns8250_port_get_baud_rate(ns8250_regs_t *regs)
[f4ef3c2]557{
[cf8cc36]558 uint16_t divisor;
559 uint8_t div_low, div_high;
[f4ef3c2]560
[49698fa]561 /* Enable DLAB to be able to access baud rate divisor. */
[c7235d40]562 enable_dlab(regs);
[f619943a]563
[49698fa]564 /* Get divisor low byte. */
[c7235d40]565 div_low = pio_read_8(&regs->data);
[49698fa]566 /* Get divisor high byte. */
[c7235d40]567 div_high = pio_read_8(&regs->ier);
[f4ef3c2]568
[c7235d40]569 clear_dlab(regs);
[cf8cc36]570
571 divisor = (div_high << 8) | div_low;
572 return MAX_BAUD_RATE / divisor;
573}
574
[49698fa]575/** Get the parameters of the serial communication set on the serial port
576 * device.
577 *
578 * @param parity The parity used.
579 * @param word_length The length of one data unit in bits.
580 * @param stop_bits The number of stop bits used (one or two).
[cf8cc36]581 */
[c7235d40]582static void ns8250_port_get_com_props(ns8250_regs_t *regs, unsigned int *parity,
[49698fa]583 unsigned int *word_length, unsigned int *stop_bits)
[cf8cc36]584{
585 uint8_t val;
586
[c7235d40]587 val = pio_read_8(&regs->lcr);
588 *parity = ((val >> NS8250_LCR_PARITY) & 7);
[cf8cc36]589
[56fd7cf]590 /* Silence warnings */
591 *word_length = 0;
592
[cf8cc36]593 switch (val & 3) {
[49698fa]594 case WORD_LENGTH_5:
595 *word_length = 5;
596 break;
597 case WORD_LENGTH_6:
598 *word_length = 6;
599 break;
600 case WORD_LENGTH_7:
601 *word_length = 7;
602 break;
603 case WORD_LENGTH_8:
604 *word_length = 8;
605 break;
[cf8cc36]606 }
607
[c7235d40]608 if ((val >> NS8250_LCR_STOPBITS) & 1)
[cf8cc36]609 *stop_bits = 2;
[49698fa]610 else
[cf8cc36]611 *stop_bits = 1;
[f4ef3c2]612}
613
[cf8cc36]614/** Set the parameters of the serial communication on the serial port device.
[49698fa]615 *
616 * @param parity The parity to be used.
617 * @param word_length The length of one data unit in bits.
618 * @param stop_bits The number of stop bits used (one or two).
619 * @return Zero on success, EINVAL if some of the specified values
620 * is invalid.
[cf8cc36]621 */
[c7235d40]622static int ns8250_port_set_com_props(ns8250_regs_t *regs, unsigned int parity,
[49698fa]623 unsigned int word_length, unsigned int stop_bits)
[cf8cc36]624{
625 uint8_t val;
626
627 switch (word_length) {
[49698fa]628 case 5:
629 val = WORD_LENGTH_5;
630 break;
631 case 6:
632 val = WORD_LENGTH_6;
633 break;
634 case 7:
635 val = WORD_LENGTH_7;
636 break;
637 case 8:
638 val = WORD_LENGTH_8;
639 break;
640 default:
641 return EINVAL;
[cf8cc36]642 }
643
644 switch (stop_bits) {
[49698fa]645 case 1:
[c7235d40]646 val |= ONE_STOP_BIT << NS8250_LCR_STOPBITS;
[49698fa]647 break;
648 case 2:
[c7235d40]649 val |= TWO_STOP_BITS << NS8250_LCR_STOPBITS;
[49698fa]650 break;
651 default:
652 return EINVAL;
[cf8cc36]653 }
654
655 switch (parity) {
[49698fa]656 case SERIAL_NO_PARITY:
657 case SERIAL_ODD_PARITY:
658 case SERIAL_EVEN_PARITY:
659 case SERIAL_MARK_PARITY:
660 case SERIAL_SPACE_PARITY:
[c7235d40]661 val |= parity << NS8250_LCR_PARITY;
[49698fa]662 break;
663 default:
664 return EINVAL;
[cf8cc36]665 }
666
[c7235d40]667 pio_write_8(&regs->lcr, val);
[cf8cc36]668
669 return EOK;
670}
671
672/** Initialize the serial port device.
[49698fa]673 *
[cf8cc36]674 * Set the default parameters of the serial communication.
[49698fa]675 *
[68414f4a]676 * @param ns Serial port device
[cf8cc36]677 */
[68414f4a]678static void ns8250_initialize_port(ns8250_t *ns)
[cfe7716]679{
[49698fa]680 /* Disable interrupts. */
[c7235d40]681 ns8250_port_interrupts_disable(ns->regs);
[49698fa]682 /* Set baud rate. */
[c7235d40]683 ns8250_port_set_baud_rate(ns->regs, 38400);
[49698fa]684 /* 8 bits, no parity, two stop bits. */
[c7235d40]685 ns8250_port_set_com_props(ns->regs, SERIAL_NO_PARITY, 8, 2);
[188c76c]686 /*
687 * Enable FIFO, clear them, with 4-byte threshold for greater
688 * reliability.
689 */
[c7235d40]690 pio_write_8(&ns->regs->iid, NS8250_FCR_FIFOENABLE
[188c76c]691 | NS8250_FCR_RXFIFORESET | NS8250_FCR_TXFIFORESET
692 | NS8250_FCR_RXTRIGGERLOW);
[49698fa]693 /*
694 * RTS/DSR set (Request to Send and Data Terminal Ready lines enabled),
695 * Aux Output2 set - needed for interrupts.
696 */
[c7235d40]697 pio_write_8(&ns->regs->mcr, NS8250_MCR_DTR | NS8250_MCR_RTS
698 | NS8250_MCR_OUT2);
[49698fa]699}
700
[5b68e0c]701/** Deinitialize the serial port device.
702 *
703 * @param ns Serial port device
704 */
705static void ns8250_port_cleanup(ns8250_t *ns)
706{
707 /* Disable FIFO */
[c7235d40]708 pio_write_8(&ns->regs->iid, 0x00);
[5b68e0c]709 /* Disable DTR, RTS, OUT1, OUT2 (int. enable) */
[c7235d40]710 pio_write_8(&ns->regs->mcr, 0x00);
[5b68e0c]711 /* Disable all interrupts from the port */
[c7235d40]712 ns8250_port_interrupts_disable(ns->regs);
[5b68e0c]713}
714
[49698fa]715/** Read the data from the serial port device and store them to the input
716 * buffer.
717 *
[68414f4a]718 * @param ns Serial port device
[cf8cc36]719 */
[68414f4a]720static void ns8250_read_from_device(ns8250_t *ns)
[7f8b581]721{
[c7235d40]722 ns8250_regs_t *regs = ns->regs;
[dafe675]723 bool cont = true;
724
[c92e30f]725 fibril_mutex_lock(&ns->mutex);
[49698fa]726 while (cont) {
[c7235d40]727 cont = ns8250_received(regs);
[cf8cc36]728 if (cont) {
[c7235d40]729 uint8_t val = ns8250_read_8(regs);
[ca97cad]730
[4510e06]731 if (ns->client_connections > 0) {
[91ecaa10]732 bool buf_was_empty = buf_is_empty(&ns->input_buffer);
[68414f4a]733 if (!buf_push_back(&ns->input_buffer, val)) {
[fc51296]734 ddf_msg(LVL_WARN, "Buffer overflow on "
[56fd7cf]735 "%s.", ddf_dev_get_name(ns->dev));
[c92e30f]736 break;
[dafe675]737 } else {
[fc51296]738 ddf_msg(LVL_DEBUG2, "Character %c saved "
[ebcb05a]739 "to the buffer of %s.",
[56fd7cf]740 val, ddf_dev_get_name(ns->dev));
[91ecaa10]741 if (buf_was_empty)
742 fibril_condvar_broadcast(&ns->input_buffer_available);
[dafe675]743 }
[49698fa]744 }
[dafe675]745 }
[49698fa]746 }
[c92e30f]747 fibril_mutex_unlock(&ns->mutex);
748 fibril_yield();
[2300b9d]749}
750
[cf8cc36]751/** The interrupt handler.
[49698fa]752 *
[e0cd9042]753 * The serial port is initialized to interrupt when some data come or line
754 * status register changes, so the interrupt is handled by reading the incoming
755 * data and reading the line status register.
[49698fa]756 *
[8820544]757 * @param dev The serial port device.
758 *
[cf8cc36]759 */
[8820544]760static inline void ns8250_interrupt_handler(ipc_callid_t iid, ipc_call_t *icall,
761 ddf_dev_t *dev)
[2300b9d]762{
[56fd7cf]763 ns8250_t *ns = dev_ns8250(dev);
[e0cd9042]764 uint8_t iir = pio_read_8(&ns->regs->iid);
765 if ((iir & NS8250_IID_CAUSE_MASK) == NS8250_IID_CAUSE_RXSTATUS) {
766 uint8_t lsr = pio_read_8(&ns->regs->lsr);
767 if (lsr & NS8250_LSR_OE) {
[56fd7cf]768 ddf_msg(LVL_WARN, "Overrun error on %s", ddf_dev_get_name(ns->dev));
[e0cd9042]769 }
770 }
771
772 ns8250_read_from_device(ns);
[d51838f]773 hw_res_clear_interrupt(ns->parent_sess, ns->irq);
[7f8b581]774}
775
[cf8cc36]776/** Register the interrupt handler for the device.
[49698fa]777 *
[68414f4a]778 * @param ns Serial port device
[cf8cc36]779 */
[68414f4a]780static inline int ns8250_register_interrupt_handler(ns8250_t *ns)
[7f8b581]781{
[68414f4a]782 return register_interrupt_handler(ns->dev, ns->irq,
[7edfde9]783 ns8250_interrupt_handler, NULL);
[2300b9d]784}
785
[cf8cc36]786/** Unregister the interrupt handler for the device.
[49698fa]787 *
[68414f4a]788 * @param ns Serial port device
[cf8cc36]789 */
[68414f4a]790static inline int ns8250_unregister_interrupt_handler(ns8250_t *ns)
[2300b9d]791{
[e9d15d9]792 return unregister_interrupt_handler(ns->dev, ns->irq_cap);
[7f8b581]793}
794
[0c0f823b]795/** The dev_add callback method of the serial port driver.
[49698fa]796 *
[cf8cc36]797 * Probe and initialize the newly added device.
[49698fa]798 *
799 * @param dev The serial port device.
[cf8cc36]800 */
[0c0f823b]801static int ns8250_dev_add(ddf_dev_t *dev)
[5fe1c32]802{
[68414f4a]803 ns8250_t *ns = NULL;
[83a2f43]804 ddf_fun_t *fun = NULL;
[68414f4a]805 bool need_cleanup = false;
[dad0d2f]806 bool need_unreg_intr_handler = false;
[68414f4a]807 int rc;
808
[0c0f823b]809 ddf_msg(LVL_DEBUG, "ns8250_dev_add %s (handle = %d)",
[56fd7cf]810 ddf_dev_get_name(dev), (int) ddf_dev_get_handle(dev));
[5fe1c32]811
[68414f4a]812 /* Allocate soft-state for the device */
[5f6e25e]813 ns = ddf_dev_data_alloc(dev, sizeof(ns8250_t));
[68414f4a]814 if (ns == NULL) {
815 rc = ENOMEM;
816 goto fail;
817 }
818
[5f6e25e]819 fibril_mutex_initialize(&ns->mutex);
[91ecaa10]820 fibril_condvar_initialize(&ns->input_buffer_available);
[68414f4a]821 ns->dev = dev;
[1f8657b]822
[d51838f]823 ns->parent_sess = ddf_dev_parent_sess_get(ns->dev);
824 if (ns->parent_sess == NULL) {
825 ddf_msg(LVL_ERROR, "Failed to connect to parent driver of "
826 "device %s.", ddf_dev_get_name(ns->dev));
827 rc = EIO;
828 goto fail;
829 }
830
[68414f4a]831 rc = ns8250_dev_initialize(ns);
832 if (rc != EOK)
833 goto fail;
834
835 need_cleanup = true;
836
837 if (!ns8250_pio_enable(ns)) {
838 rc = EADDRNOTAVAIL;
839 goto fail;
[49698fa]840 }
[df747b9c]841
[49698fa]842 /* Find out whether the device is present. */
[68414f4a]843 if (!ns8250_dev_probe(ns)) {
844 rc = ENOENT;
845 goto fail;
[49698fa]846 }
[1f8657b]847
[49698fa]848 /* Serial port initialization (baud rate etc.). */
[68414f4a]849 ns8250_initialize_port(ns);
[cfe7716]850
[49698fa]851 /* Register interrupt handler. */
[e9d15d9]852 ns->irq_cap = ns8250_register_interrupt_handler(ns);
853 if (ns->irq_cap < 0) {
[ebcb05a]854 ddf_msg(LVL_ERROR, "Failed to register interrupt handler.");
[68414f4a]855 rc = EADDRNOTAVAIL;
856 goto fail;
[7f8b581]857 }
[dad0d2f]858 need_unreg_intr_handler = true;
[c4e30607]859
[49698fa]860 /* Enable interrupt. */
[68414f4a]861 rc = ns8250_interrupt_enable(ns);
862 if (rc != EOK) {
[fc51296]863 ddf_msg(LVL_ERROR, "Failed to enable the interrupt. Error code = "
[ebcb05a]864 "%d.", rc);
[68414f4a]865 goto fail;
[49698fa]866 }
[68414f4a]867
[97a62fe]868 fun = ddf_fun_create(dev, fun_exposed, "a");
869 if (fun == NULL) {
[ebcb05a]870 ddf_msg(LVL_ERROR, "Failed creating function.");
[97a62fe]871 goto fail;
872 }
[f658458]873
[49698fa]874 /* Set device operations. */
[56fd7cf]875 ddf_fun_set_ops(fun, &ns8250_dev_ops);
[97a62fe]876 rc = ddf_fun_bind(fun);
877 if (rc != EOK) {
[ebcb05a]878 ddf_msg(LVL_ERROR, "Failed binding function.");
[97a62fe]879 goto fail;
880 }
881
[68414f4a]882 ns->fun = fun;
[5fe1c32]883
[1dc4a5e]884 ddf_fun_add_to_category(fun, "serial");
[692c40cb]885
[ebcb05a]886 ddf_msg(LVL_NOTE, "Device %s successfully initialized.",
[56fd7cf]887 ddf_dev_get_name(dev));
[2300b9d]888
[df747b9c]889 return EOK;
[68414f4a]890fail:
[97a62fe]891 if (fun != NULL)
892 ddf_fun_destroy(fun);
[dad0d2f]893 if (need_unreg_intr_handler)
894 ns8250_unregister_interrupt_handler(ns);
[68414f4a]895 if (need_cleanup)
896 ns8250_dev_cleanup(ns);
897 return rc;
[5fe1c32]898}
899
[5b68e0c]900static int ns8250_dev_remove(ddf_dev_t *dev)
901{
[56fd7cf]902 ns8250_t *ns = dev_ns8250(dev);
[5b68e0c]903 int rc;
904
905 fibril_mutex_lock(&ns->mutex);
[956d4281]906 if (ns->client_connections > 0) {
[5b68e0c]907 fibril_mutex_unlock(&ns->mutex);
908 return EBUSY;
909 }
910 ns->removed = true;
911 fibril_mutex_unlock(&ns->mutex);
912
913 rc = ddf_fun_unbind(ns->fun);
914 if (rc != EOK) {
915 ddf_msg(LVL_ERROR, "Failed to unbind function.");
916 return rc;
917 }
918
919 ddf_fun_destroy(ns->fun);
920
921 ns8250_port_cleanup(ns);
922 ns8250_unregister_interrupt_handler(ns);
923 ns8250_dev_cleanup(ns);
924 return EOK;
925}
926
[25a7e11d]927/** Open the device.
[49698fa]928 *
929 * This is a callback function called when a client tries to connect to the
930 * device.
931 *
932 * @param dev The device.
[25a7e11d]933 */
[83a2f43]934static int ns8250_open(ddf_fun_t *fun)
[25a7e11d]935{
[56fd7cf]936 ns8250_t *ns = fun_ns8250(fun);
[25a7e11d]937 int res;
938
[5b68e0c]939 fibril_mutex_lock(&ns->mutex);
[4510e06]940 if (ns->removed) {
[5b68e0c]941 res = ENXIO;
[25a7e11d]942 } else {
943 res = EOK;
[4510e06]944 ns->client_connections++;
[25a7e11d]945 }
[5b68e0c]946 fibril_mutex_unlock(&ns->mutex);
[bab6388]947
[25a7e11d]948 return res;
949}
950
951/** Close the device.
[49698fa]952 *
953 * This is a callback function called when a client tries to disconnect from
954 * the device.
955 *
956 * @param dev The device.
[25a7e11d]957 */
[83a2f43]958static void ns8250_close(ddf_fun_t *fun)
[25a7e11d]959{
[56fd7cf]960 ns8250_t *data = fun_ns8250(fun);
[25a7e11d]961
962 fibril_mutex_lock(&data->mutex);
963
[4510e06]964 assert(data->client_connections > 0);
[25a7e11d]965
[4510e06]966 if (!(--data->client_connections))
967 buf_clear(&data->input_buffer);
[25a7e11d]968
[49698fa]969 fibril_mutex_unlock(&data->mutex);
[25a7e11d]970}
971
[49698fa]972/** Get parameters of the serial communication which are set to the specified
973 * device.
974 *
975 * @param dev The serial port device.
976 * @param baud_rate The baud rate used by the device.
977 * @param parity The type of parity used by the device.
978 * @param word_length The size of one data unit in bits.
979 * @param stop_bits The number of stop bits used.
[cf8cc36]980 */
[49698fa]981static void
[83a2f43]982ns8250_get_props(ddf_dev_t *dev, unsigned int *baud_rate, unsigned int *parity,
[49698fa]983 unsigned int *word_length, unsigned int* stop_bits)
984{
[56fd7cf]985 ns8250_t *data = dev_ns8250(dev);
[c7235d40]986 ns8250_regs_t *regs = data->regs;
[cf8cc36]987
[49698fa]988 fibril_mutex_lock(&data->mutex);
[c7235d40]989 ns8250_port_interrupts_disable(regs);
990 *baud_rate = ns8250_port_get_baud_rate(regs);
991 ns8250_port_get_com_props(regs, parity, word_length, stop_bits);
992 ns8250_port_interrupts_enable(regs);
[49698fa]993 fibril_mutex_unlock(&data->mutex);
[cf8cc36]994
[fc51296]995 ddf_msg(LVL_DEBUG, "ns8250_get_props: baud rate %d, parity 0x%x, word "
[ebcb05a]996 "length %d, stop bits %d", *baud_rate, *parity, *word_length,
[49698fa]997 *stop_bits);
[cf8cc36]998}
999
[49698fa]1000/** Set parameters of the serial communication to the specified serial port
1001 * device.
1002 *
1003 * @param dev The serial port device.
1004 * @param baud_rate The baud rate to be used by the device.
1005 * @param parity The type of parity to be used by the device.
1006 * @param word_length The size of one data unit in bits.
1007 * @param stop_bits The number of stop bits to be used.
[cf8cc36]1008 */
[83a2f43]1009static int ns8250_set_props(ddf_dev_t *dev, unsigned int baud_rate,
[33dbbd2]1010 unsigned int parity, unsigned int word_length, unsigned int stop_bits)
[cf8cc36]1011{
[fc51296]1012 ddf_msg(LVL_DEBUG, "ns8250_set_props: baud rate %d, parity 0x%x, word "
[ebcb05a]1013 "length %d, stop bits %d", baud_rate, parity, word_length,
[49698fa]1014 stop_bits);
[cf8cc36]1015
[56fd7cf]1016 ns8250_t *data = dev_ns8250(dev);
[c7235d40]1017 ns8250_regs_t *regs = data->regs;
[cf8cc36]1018 int ret;
1019
[49698fa]1020 fibril_mutex_lock(&data->mutex);
[c7235d40]1021 ns8250_port_interrupts_disable(regs);
1022 ret = ns8250_port_set_baud_rate(regs, baud_rate);
[33dbbd2]1023 if (ret == EOK)
[c7235d40]1024 ret = ns8250_port_set_com_props(regs, parity, word_length, stop_bits);
1025 ns8250_port_interrupts_enable(regs);
[49698fa]1026 fibril_mutex_unlock(&data->mutex);
[cf8cc36]1027
[49698fa]1028 return ret;
[cf8cc36]1029}
1030
[49698fa]1031/** Default handler for client requests which are not handled by the standard
1032 * interfaces.
1033 *
[f4ef3c2]1034 * Configure the parameters of the serial communication.
1035 */
[83a2f43]1036static void ns8250_default_handler(ddf_fun_t *fun, ipc_callid_t callid,
[33dbbd2]1037 ipc_call_t *call)
[f4ef3c2]1038{
[228e490]1039 sysarg_t method = IPC_GET_IMETHOD(*call);
[f4ef3c2]1040 int ret;
[49698fa]1041 unsigned int baud_rate, parity, word_length, stop_bits;
1042
1043 switch (method) {
1044 case SERIAL_GET_COM_PROPS:
[56fd7cf]1045 ns8250_get_props(ddf_fun_get_dev(fun), &baud_rate, &parity, &word_length,
[49698fa]1046 &stop_bits);
[ffa2c8ef]1047 async_answer_4(callid, EOK, baud_rate, parity, word_length,
[49698fa]1048 stop_bits);
1049 break;
[cf8cc36]1050
[49698fa]1051 case SERIAL_SET_COM_PROPS:
1052 baud_rate = IPC_GET_ARG1(*call);
1053 parity = IPC_GET_ARG2(*call);
1054 word_length = IPC_GET_ARG3(*call);
1055 stop_bits = IPC_GET_ARG4(*call);
[56fd7cf]1056 ret = ns8250_set_props(ddf_fun_get_dev(fun), baud_rate, parity, word_length,
[49698fa]1057 stop_bits);
[ffa2c8ef]1058 async_answer_0(callid, ret);
[49698fa]1059 break;
1060
1061 default:
[ffa2c8ef]1062 async_answer_0(callid, ENOTSUP);
[49698fa]1063 }
[f4ef3c2]1064}
1065
[25a7e11d]1066/** Initialize the serial port driver.
[49698fa]1067 *
1068 * Initialize device operations structures with callback methods for handling
[25a7e11d]1069 * client requests to the serial port devices.
1070 */
[49698fa]1071static void ns8250_init(void)
[5fe1c32]1072{
[267f235]1073 ddf_log_init(NAME);
[fc51296]1074
[5159ae9]1075 ns8250_dev_ops.open = &ns8250_open;
[49698fa]1076 ns8250_dev_ops.close = &ns8250_close;
[f658458]1077
[50c57df]1078 ns8250_dev_ops.interfaces[CHAR_DEV_IFACE] = &ns8250_char_dev_ops;
[5159ae9]1079 ns8250_dev_ops.default_handler = &ns8250_default_handler;
[5fe1c32]1080}
1081
1082int main(int argc, char *argv[])
1083{
[49698fa]1084 printf(NAME ": HelenOS serial port driver\n");
[04c7003f]1085 ns8250_init();
[83a2f43]1086 return ddf_driver_main(&ns8250_driver);
[5fe1c32]1087}
1088
1089/**
1090 * @}
[49698fa]1091 */
Note: See TracBrowser for help on using the repository browser.