source: mainline/uspace/drv/char/ns8250/ns8250.c@ 9bd4615

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

Other clients should also use the IRC client stub.

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