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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c7235d40 was c7235d40, checked in by Jakub Jermar <jakub@…>, 14 years ago

Replace ns8250.c magic numbers with structured register definitions.

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