1/** 2 * \file 3 */ 4 5/* 6 * Copyright (c) 2009, ETH Zurich. 7 * All rights reserved. 8 * 9 * This file is distributed under the terms in the attached LICENSE file. 10 * If you do not find this file, copies can be found by writing to: 11 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 12 */ 13 14/* This represents a simple implementation of a 16550 uart controller. It 15 * neglects all timing issues, tranfer rate and error conditions. It declines to 16 * know about FIFOs (which make it to a 16450 controller in fact). In addition 17 * we are almost agnostic to the MCR and MSR register. Everything is 18 * forwarded directly to the terminal. */ 19 20#include "vmkitmon.h" 21#include "pc16550d.h" 22#include <stdlib.h> 23#include <barrelfish/terminal.h> 24 25#define FIFO_POS(x) ((x) & PC16550D_FIFO_MASK) 26 27struct pc16550d * 28pc16550d_new (uint16_t base_port, uint8_t irq, struct lpc *lpc) 29{ 30 assert(lpc != NULL); 31 32 struct pc16550d *u = calloc(1, sizeof(struct pc16550d)); 33 34 pc16550d_mem_initialize(&u->dev, (mackerel_addr_t)u->regs); 35 36 u->base_port = base_port; 37 u->irq = irq; 38 u->lpc = lpc; 39 40 // initialize the LSR register to have an empty transmit buffer 41 pc16550d_mem_lsr_thre_wrf(&u->dev, 1); 42 pc16550d_mem_lsr_temt_wrf(&u->dev, 1); 43 44 return u; 45} 46 47/** 48 * \brief Check all necessary conditions to raise a certain irq and does so. 49 * 50 * This method must be called at the end of every function returning back to 51 * the guest otherwise some interrupts may get lost. 52 */ 53static inline void 54process_interrupt_conditions (struct pc16550d *u) 55{ 56 // cycle through the sources for interrupts in the order of their priority 57 // check whethter they are enabled and pending and raise them accordingly 58 59 // data overrun 60 if (pc16550d_mem_ier_rd(&u->dev).elsi && 61 pc16550d_mem_lsr_rd(&u->dev).oe) { 62 63 pc16550d_mem_iir_iid_wrf(&u->dev, pc16550d_mem_irq_rls); 64 } 65 // receiver data available 66 else if (pc16550d_mem_ier_rd(&u->dev).erbfi && 67 pc16550d_mem_lsr_rd(&u->dev).dr) { 68 pc16550d_mem_iir_iid_wrf(&u->dev, pc16550d_mem_irq_rda); 69 } 70 // TODO: Here we need the timeout interrupt 71 // transmitter holding register emtpy 72 else if (pc16550d_mem_ier_rd(&u->dev).etbei && 73 pc16550d_mem_lsr_rd(&u->dev).thre) { 74 pc16550d_mem_iir_iid_wrf(&u->dev, pc16550d_mem_irq_thre); 75 } 76 // no interrupt condition available 77 else { 78 pc16550d_mem_iir_iid_wrf(&u->dev, pc16550d_mem_irq_none); 79 } 80 81 // if there is an intr pending then inform the PIC accordingly 82 if (pc16550d_mem_iir_rd(&u->dev).iid != pc16550d_mem_irq_none) { 83 lpc_pic_assert_irq(u->lpc, u->irq); 84 } 85} 86 87/* this method clears the current pending interrupt 88 * to eventually raise the next interrupt */ 89static inline void 90clear_interrupt (struct pc16550d *u) 91{ 92 pc16550d_mem_iir_iid_wrf(&u->dev, pc16550d_mem_irq_none); 93} 94 95static inline void 96process_lsr_change (struct pc16550d *u) 97{ 98 // for now we only process the interrupts 99} 100 101static inline void 102process_thr_change (struct pc16550d *u) 103{ 104 // put out the character 105 char chr = pc16550d_mem_thr_rd_raw(&u->dev); 106 int r = terminal_write(&chr, 1); 107 assert(r == 1); 108 109 // writing the THR reg resets the THRE interrupt pending state 110 if (pc16550d_mem_iir_rd(&u->dev).iid == pc16550d_mem_irq_thre) { 111 clear_interrupt(u); 112 } 113 114 // we always simulate the transmitter register to be empty (an infinitly 115 // fast serial line) 116 pc16550d_mem_lsr_thre_wrf(&u->dev, 1); 117 pc16550d_mem_lsr_temt_wrf(&u->dev, 1); 118 119 process_lsr_change(u); 120} 121 122static inline void 123process_fcr_change (struct pc16550d *u) 124{ 125 if (pc16550d_mem_fcr_rd(&u->dev).rfifo_reset) { 126 u->fifo_in_produced = u->fifo_in_consumed = 0; 127 pc16550d_mem_lsr_dr_wrf(&u->dev, 0); 128 pc16550d_mem_lsr_oe_wrf(&u->dev, 0); 129 process_lsr_change(u); 130 } 131} 132 133int 134pc16550d_handle_pio_read (struct pc16550d *u, uint16_t port, 135 enum opsize size, uint32_t *val) 136{ 137 assert(u != NULL); 138 assert(port >= u->base_port); 139 140 port -= u->base_port; 141 142 switch (port) { 143 case 0: 144 if (pc16550d_mem_lcr_rd(&u->dev).dlab) { 145 // DL(L) read 146 switch (size) { 147 case OPSIZE_8: 148 *val = pc16550d_mem_dll_rd_raw(&u->dev); 149 break; 150 default: 151 *val = pc16550d_mem_dl_rd_raw(&u->dev); 152 break; 153 } 154 } else { 155 // RBR read 156 if (FIFO_POS(u->fifo_in_produced) == 157 FIFO_POS(u->fifo_in_consumed)) { 158 *val = 0; 159 } else { 160 *val = u->fifo_in[FIFO_POS(u->fifo_in_consumed)]; 161 u->fifo_in_consumed++; 162 } 163 // reset the data ready bit 164 if (FIFO_POS(u->fifo_in_produced) == 165 FIFO_POS(u->fifo_in_consumed)) { 166 pc16550d_mem_lsr_dr_wrf(&u->dev, 0); 167 process_lsr_change(u); 168 } 169 } 170 break; 171 case 1: 172 if (pc16550d_mem_lcr_rd(&u->dev).dlab) { 173 // DLM read 174 *val = pc16550d_mem_dlm_rd_raw(&u->dev); 175 } else { 176 // IER READ 177 *val = pc16550d_mem_ier_rd_raw(&u->dev); 178 } 179 break; 180 case 2: 181 // IIR read 182 *val = pc16550d_mem_iir_rd_raw(&u->dev); 183 // reading the IIR reg resets the THRE interrupt pending state 184 // (after the read) 185 if (pc16550d_mem_iir_rd(&u->dev).iid == pc16550d_mem_irq_thre) { 186 clear_interrupt(u); 187 } 188 break; 189 case 3: 190 // LCR read 191 *val = pc16550d_mem_lcr_rd_raw(&u->dev); 192 break; 193 case 4: 194 // MCR read 195 *val = pc16550d_mem_mcr_rd_raw(&u->dev); 196 break; 197 case 5: 198 // LSR read 199 *val = pc16550d_mem_lsr_rd_raw(&u->dev); 200 // reset possible error conditions 201 pc16550d_mem_lsr_oe_wrf(&u->dev, 0); 202 process_lsr_change(u); 203 break; 204 case 6: 205 // MSR read 206 *val = pc16550d_mem_msr_rd_raw(&u->dev); 207 break; 208 case 7: 209 // SCR read 210 *val = pc16550d_mem_scr_rd_raw(&u->dev); 211 break; 212 default: 213 assert(!"pc16550d: read access to unknown port"); 214 break; 215 } 216 217 // check whether interrupts shall be raised 218 process_interrupt_conditions(u); 219 220 return HANDLER_ERR_OK; 221} 222 223int 224pc16550d_handle_pio_write (struct pc16550d *u, uint16_t port, 225 enum opsize size, uint32_t val) 226{ 227 assert(u != NULL); 228 assert(port >= u->base_port); 229 230 port -= u->base_port; 231 232 /* all registers which do no processing after they are written and do not 233 * abort the application just ignore their content and store it in case it 234 * is read by the user */ 235 236 switch (port) { 237 case 0: 238 if (pc16550d_mem_lcr_rd(&u->dev).dlab) { 239 // DL(L) write 240 switch (size) { 241 case OPSIZE_8: 242 pc16550d_mem_dll_wr_raw(&u->dev, val); 243 break; 244 default: 245 pc16550d_mem_dl_wr_raw(&u->dev, val); 246 } 247 pc16550d_mem_thr_wr_raw(&u->dev, val); 248 } else { 249 // THR write 250 pc16550d_mem_thr_wr_raw(&u->dev, val); 251 process_thr_change(u); 252 } 253 break; 254 case 1: 255 if (pc16550d_mem_lcr_rd(&u->dev).dlab) { 256 // DLM write 257 pc16550d_mem_dlm_wr_raw(&u->dev, val); 258 } else { 259 // IER write 260 pc16550d_mem_ier_wr_raw(&u->dev, val); 261 /* this register only holds info necessary for other operations 262 * therefore we do not need to do any processing */ 263 } 264 break; 265 case 2: 266 // FCR write 267 pc16550d_mem_fcr_wr_raw(&u->dev, val); 268 process_fcr_change(u); 269 break; 270 case 3: 271 // LCR write 272 pc16550d_mem_lcr_wr_raw(&u->dev, val); 273 break; 274 case 4: 275 // MCR write 276 pc16550d_mem_mcr_wr_raw(&u->dev, val); 277 break; 278 case 5: 279 // LSR write 280 assert(!"LSR should not be written to"); 281 pc16550d_mem_lsr_wr_raw(&u->dev, val); 282 process_lsr_change(u); 283 break; 284 case 6: 285 assert(!"MSR should not be written to"); 286 // MSR write 287 pc16550d_mem_msr_wr_raw(&u->dev, val); 288 break; 289 case 7: 290 // SCR write 291 pc16550d_mem_scr_wr_raw(&u->dev, val); 292 break; 293 default: 294 assert(!"pc16550d: write access to unknown port"); 295 break; 296 } 297 298 // check whether interrupts shall be raised 299 process_interrupt_conditions(u); 300 301 return HANDLER_ERR_OK; 302} 303 304#if 0 305static void 306input_handler (void *user_data, const char *str, size_t size) 307{ 308 assert(user_data != NULL); 309 310 struct pc16550d *u = user_data; 311 312 if (size == 0) { 313 return; 314 } 315 316 // copy the string into our fifo 317 for (int i = 0; i < size; i++) { 318 u->fifo_in[FIFO_POS(u->fifo_in_produced)] = str[i]; 319 u->fifo_in_produced++; 320 321 // check for overrun 322 if (FIFO_POS(u->fifo_in_produced) == 323 FIFO_POS(u->fifo_in_consumed)) { 324 u->fifo_in_produced = u->fifo_in_consumed = 0; 325 pc16550d_mem_lsr_oe_wrf(&u->dev, 1); 326 } 327 } 328 329 // tell the user that data is available 330 pc16550d_mem_lsr_dr_wrf(&u->dev, 1); 331 332 // raise interrupts when necessary 333 process_interrupt_conditions(u); 334} 335#endif 336 337void 338pc16550d_attach_to_console (struct pc16550d *u) 339{ 340 assert(u != NULL); 341 342 assert(!"NYI"); 343#if 0 344 errval_t err; 345 err = terminal_register_input_handler(input_handler, u); 346 assert(err_is_ok(err)); 347#endif 348} 349