📄 pmac_zilog.c
字号:
/* * Called upon match with an escc node in the devive-tree. */static int pmz_attach(struct macio_dev *mdev, const struct of_match *match){ int i; /* Iterate the pmz_ports array to find a matching entry */ for (i = 0; i < MAX_ZS_PORTS; i++) if (pmz_ports[i].node == mdev->ofdev.node) { struct uart_pmac_port *uap = &pmz_ports[i]; uap->dev = mdev; dev_set_drvdata(&mdev->ofdev.dev, uap); if (macio_request_resources(uap->dev, "pmac_zilog")) printk(KERN_WARNING "%s: Failed to request resource" ", port still active\n", uap->node->name); else uap->flags |= PMACZILOG_FLAG_RSRC_REQUESTED; return 0; } return -ENODEV;}/* * That one should not be called, macio isn't really a hotswap device, * we don't expect one of those serial ports to go away... */static int pmz_detach(struct macio_dev *mdev){ struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); if (!uap) return -ENODEV; if (uap->flags & PMACZILOG_FLAG_RSRC_REQUESTED) { macio_release_resources(uap->dev); uap->flags &= ~PMACZILOG_FLAG_RSRC_REQUESTED; } dev_set_drvdata(&mdev->ofdev.dev, NULL); uap->dev = NULL; return 0;}static int pmz_suspend(struct macio_dev *mdev, u32 pm_state){ struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); struct uart_state *state; unsigned long flags; if (uap == NULL) { printk("HRM... pmz_suspend with NULL uap\n"); return 0; } if (pm_state == mdev->ofdev.dev.power_state || pm_state < 2) return 0; pmz_debug("suspend, switching to state %d\n", pm_state); state = pmz_uart_reg.state + uap->port.line; down(&pmz_irq_sem); down(&state->sem); spin_lock_irqsave(&uap->port.lock, flags); if (ZS_IS_OPEN(uap) || ZS_IS_CONS(uap)) { /* Disable receiver and transmitter. */ uap->curregs[R3] &= ~RxENABLE; uap->curregs[R5] &= ~TxENABLE; /* Disable all interrupts and BRK assertion. */ uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); uap->curregs[R5] &= ~SND_BRK; pmz_load_zsregs(uap, uap->curregs); uap->flags |= PMACZILOG_FLAG_IS_ASLEEP; mb(); } spin_unlock_irqrestore(&uap->port.lock, flags); if (ZS_IS_OPEN(uap) || ZS_IS_OPEN(uap->mate)) if (ZS_IS_ASLEEP(uap->mate) && ZS_IS_IRQ_ON(pmz_get_port_A(uap))) { pmz_get_port_A(uap)->flags &= ~PMACZILOG_FLAG_IS_IRQ_ON; disable_irq(uap->port.irq); } if (ZS_IS_CONS(uap)) uap->port.cons->flags &= ~CON_ENABLED; /* Shut the chip down */ pmz_set_scc_power(uap, 0); up(&state->sem); up(&pmz_irq_sem); pmz_debug("suspend, switching complete\n"); mdev->ofdev.dev.power_state = pm_state; return 0;}static int pmz_resume(struct macio_dev *mdev){ struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); struct uart_state *state; unsigned long flags; int pwr_delay = 0; if (uap == NULL) return 0; if (mdev->ofdev.dev.power_state == 0) return 0; pmz_debug("resume, switching to state 0\n"); state = pmz_uart_reg.state + uap->port.line; down(&pmz_irq_sem); down(&state->sem); spin_lock_irqsave(&uap->port.lock, flags); if (!ZS_IS_OPEN(uap) && !ZS_IS_CONS(uap)) { spin_unlock_irqrestore(&uap->port.lock, flags); goto bail; } pwr_delay = __pmz_startup(uap); /* Take care of config that may have changed while asleep */ __pmz_set_termios(&uap->port, &uap->termios_cache, NULL); if (ZS_IS_OPEN(uap)) { /* Enable interrupts */ uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB; if (!ZS_IS_EXTCLK(uap)) uap->curregs[R1] |= EXT_INT_ENAB; write_zsreg(uap, R1, uap->curregs[R1]); } spin_unlock_irqrestore(&uap->port.lock, flags); if (ZS_IS_CONS(uap)) uap->port.cons->flags |= CON_ENABLED; /* Re-enable IRQ on the controller */ if (ZS_IS_OPEN(uap) && !ZS_IS_IRQ_ON(pmz_get_port_A(uap))) { pmz_get_port_A(uap)->flags |= PMACZILOG_FLAG_IS_IRQ_ON; enable_irq(uap->port.irq); } bail: up(&state->sem); up(&pmz_irq_sem); /* Right now, we deal with delay by blocking here, I'll be * smarter later on */ if (pwr_delay != 0) { pmz_debug("pmz: delaying %d ms\n", pwr_delay); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((pwr_delay * HZ)/1000); } pmz_debug("resume, switching complete\n"); mdev->ofdev.dev.power_state = 0; return 0;}/* * Probe all ports in the system and build the ports array, we register * with the serial layer at this point, the macio-type probing is only * used later to "attach" to the sysfs tree so we get power management * events */static int __init pmz_probe(void){ struct device_node *node_p, *node_a, *node_b, *np; int count = 0; int rc; /* * Find all escc chips in the system */ node_p = of_find_node_by_name(NULL, "escc"); while (node_p) { /* * First get channel A/B node pointers * * TODO: Add routines with proper locking to do that... */ node_a = node_b = NULL; for (np = NULL; (np = of_get_next_child(node_p, np)) != NULL;) { if (strncmp(np->name, "ch-a", 4) == 0) node_a = of_node_get(np); else if (strncmp(np->name, "ch-b", 4) == 0) node_b = of_node_get(np); } if (!node_a && !node_b) { of_node_put(node_a); of_node_put(node_b); printk(KERN_ERR "pmac_zilog: missing node %c for escc %s\n", (!node_a) ? 'a' : 'b', node_p->full_name); goto next; } /* * Fill basic fields in the port structures */ pmz_ports[count].mate = &pmz_ports[count+1]; pmz_ports[count+1].mate = &pmz_ports[count]; pmz_ports[count].flags = PMACZILOG_FLAG_IS_CHANNEL_A; pmz_ports[count].node = node_a; pmz_ports[count+1].node = node_b; pmz_ports[count].port.line = count; pmz_ports[count+1].port.line = count+1; /* * Setup the ports for real */ rc = pmz_init_port(&pmz_ports[count]); if (rc == 0 && node_b != NULL) rc = pmz_init_port(&pmz_ports[count+1]); if (rc != 0) { of_node_put(node_a); of_node_put(node_b); memset(&pmz_ports[count], 0, sizeof(struct uart_pmac_port)); memset(&pmz_ports[count+1], 0, sizeof(struct uart_pmac_port)); goto next; } count += 2;next: node_p = of_find_node_by_name(node_p, "escc"); } pmz_ports_count = count; return 0;}#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLEstatic void pmz_console_write(struct console *con, const char *s, unsigned int count);static int __init pmz_console_setup(struct console *co, char *options);static struct console pmz_console = { .name = "ttyS", .write = pmz_console_write, .device = uart_console_device, .setup = pmz_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &pmz_uart_reg,};#define PMACZILOG_CONSOLE &pmz_console#else /* CONFIG_SERIAL_PMACZILOG_CONSOLE */#define PMACZILOG_CONSOLE (NULL)#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE *//* * Register the driver, console driver and ports with the serial * core */static int __init pmz_register(void){ int i, rc; pmz_uart_reg.nr = pmz_ports_count; pmz_uart_reg.cons = PMACZILOG_CONSOLE; pmz_uart_reg.minor = 64; /* * Register this driver with the serial core */ rc = uart_register_driver(&pmz_uart_reg); if (rc) return rc; /* * Register each port with the serial core */ for (i = 0; i < pmz_ports_count; i++) { struct uart_pmac_port *uport = &pmz_ports[i]; /* NULL node may happen on wallstreet */ if (uport->node != NULL) rc = uart_add_one_port(&pmz_uart_reg, &uport->port); if (rc) goto err_out; } return 0;err_out: while (i-- > 0) { struct uart_pmac_port *uport = &pmz_ports[i]; uart_remove_one_port(&pmz_uart_reg, &uport->port); } uart_unregister_driver(&pmz_uart_reg); return rc;}static struct of_match pmz_match[] = { { .name = "ch-a", .type = OF_ANY_MATCH, .compatible = OF_ANY_MATCH }, { .name = "ch-b", .type = OF_ANY_MATCH, .compatible = OF_ANY_MATCH }, {},};static struct macio_driver pmz_driver = { .name = "pmac_zilog", .match_table = pmz_match, .probe = pmz_attach, .remove = pmz_detach, .suspend = pmz_suspend, .resume = pmz_resume,};static int __init init_pmz(void){ int rc, i; printk(KERN_INFO "%s\n", version); /* * First, we need to do a direct OF-based probe pass. We * do that because we want serial console up before the * macio stuffs calls us back, and since that makes it * easier to pass the proper number of channels to * uart_register_driver() */ if (pmz_ports_count == 0) pmz_probe(); /* * Bail early if no port found */ if (pmz_ports_count == 0) return -ENODEV; /* * Now we register with the serial layer */ rc = pmz_register(); if (rc) { printk(KERN_ERR "pmac_zilog: Error registering serial device, disabling pmac_zilog.\n" "pmac_zilog: Did another serial driver already claim the minors?\n"); /* effectively "pmz_unprobe()" */ for (i=0; i < pmz_ports_count; i++) pmz_dispose_port(&pmz_ports[i]); return rc; } /* * Then we register the macio driver itself */ return macio_register_driver(&pmz_driver);}static void __exit exit_pmz(void){ int i; /* Get rid of macio-driver (detach from macio) */ macio_unregister_driver(&pmz_driver); for (i = 0; i < pmz_ports_count; i++) { struct uart_pmac_port *uport = &pmz_ports[i]; if (uport->node != NULL) { uart_remove_one_port(&pmz_uart_reg, &uport->port); pmz_dispose_port(uport); } } /* Unregister UART driver */ uart_unregister_driver(&pmz_uart_reg);}#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE/* * Print a string to the serial port trying not to disturb * any possible real use of the port... */static void pmz_console_write(struct console *con, const char *s, unsigned int count){ struct uart_pmac_port *uap = &pmz_ports[con->index]; unsigned long flags; int i; spin_lock_irqsave(&uap->port.lock, flags); /* Turn of interrupts and enable the transmitter. */ write_zsreg(uap, R1, uap->curregs[1] & ~TxINT_ENAB); write_zsreg(uap, R5, uap->curregs[5] | TxENABLE | RTS | DTR); for (i = 0; i < count; i++) { /* Wait for the transmit buffer to empty. */ while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0) udelay(5); write_zsdata(uap, s[i]); if (s[i] == 10) { while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0) udelay(5); write_zsdata(uap, R13); } } /* Restore the values in the registers. */ write_zsreg(uap, R1, uap->curregs[1]); /* Don't disable the transmitter. */ spin_unlock_irqrestore(&uap->port.lock, flags);}/* * Setup the serial console */static int __init pmz_console_setup(struct console *co, char *options){ struct uart_pmac_port *uap; struct uart_port *port; int baud = 38400; int bits = 8; int parity = 'n'; int flow = 'n'; unsigned long pwr_delay; /* * XServe's default to 57600 bps */ if (machine_is_compatible("RackMac1,1") || machine_is_compatible("RackMac1,2") || machine_is_compatible("MacRISC4")) baud = 57600; /* * Check whether an invalid uart number has been specified, and * if so, search for the first available port that does have * console support. */ if (co->index >= pmz_ports_count) co->index = 0; uap = &pmz_ports[co->index]; if (uap->node == NULL) return -ENODEV; port = &uap->port; /* * Mark port as beeing a console */ uap->flags |= PMACZILOG_FLAG_IS_CONS; /* * Temporary fix for uart layer who didn't setup the spinlock yet */ spin_lock_init(&port->lock); /* * Enable the hardware */ pwr_delay = __pmz_startup(uap); if (pwr_delay) mdelay(pwr_delay); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); return uart_set_options(port, co, baud, parity, bits, flow);}static int __init pmz_console_init(void){ /* Probe ports */ pmz_probe(); /* TODO: Autoprobe console based on OF */ /* pmz_console.index = i; */ register_console(&pmz_console); return 0;}console_initcall(pmz_console_init);#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */module_init(init_pmz);module_exit(exit_pmz);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -