in mips_ejtag_fdc.c [885:1046]
static int mips_ejtag_fdc_tty_probe(struct mips_cdmm_device *dev)
{
int ret, nport;
struct mips_ejtag_fdc_tty_port *dport;
struct mips_ejtag_fdc_tty *priv;
struct tty_driver *driver;
unsigned int cfg, tx_fifo;
priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->cpu = dev->cpu;
priv->dev = &dev->dev;
mips_cdmm_set_drvdata(dev, priv);
atomic_set(&priv->xmit_total, 0);
raw_spin_lock_init(&priv->lock);
priv->reg = devm_ioremap(priv->dev, dev->res.start,
resource_size(&dev->res));
if (!priv->reg) {
dev_err(priv->dev, "ioremap failed for resource %pR\n",
&dev->res);
return -ENOMEM;
}
cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
tx_fifo = (cfg & REG_FDCFG_TXFIFOSIZE) >> REG_FDCFG_TXFIFOSIZE_SHIFT;
/* Disable interrupts */
cfg &= ~(REG_FDCFG_TXINTTHRES | REG_FDCFG_RXINTTHRES);
cfg |= REG_FDCFG_TXINTTHRES_DISABLED;
cfg |= REG_FDCFG_RXINTTHRES_DISABLED;
mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
/* Make each port's xmit FIFO big enough to fill FDC TX FIFO */
priv->xmit_size = min(tx_fifo * 4, (unsigned int)SERIAL_XMIT_SIZE);
driver = tty_alloc_driver(NUM_TTY_CHANNELS, TTY_DRIVER_REAL_RAW);
if (IS_ERR(driver))
return PTR_ERR(driver);
priv->driver = driver;
driver->driver_name = "ejtag_fdc";
snprintf(priv->fdc_name, sizeof(priv->fdc_name), "ttyFDC%u", dev->cpu);
snprintf(priv->driver_name, sizeof(priv->driver_name), "%sc",
priv->fdc_name);
driver->name = priv->driver_name;
driver->major = 0; /* Auto-allocate */
driver->minor_start = 0;
driver->type = TTY_DRIVER_TYPE_SERIAL;
driver->subtype = SERIAL_TYPE_NORMAL;
driver->init_termios = tty_std_termios;
driver->init_termios.c_cflag |= CLOCAL;
driver->driver_state = priv;
tty_set_operations(driver, &mips_ejtag_fdc_tty_ops);
for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
dport = &priv->ports[nport];
dport->driver = priv;
tty_port_init(&dport->port);
dport->port.ops = &mips_ejtag_fdc_tty_port_ops;
raw_spin_lock_init(&dport->rx_lock);
spin_lock_init(&dport->xmit_lock);
/* The xmit buffer starts empty, i.e. completely written */
init_completion(&dport->xmit_empty);
complete(&dport->xmit_empty);
}
/* Set up the console */
mips_ejtag_fdc_con.regs[dev->cpu] = priv->reg;
if (dev->cpu == 0)
mips_ejtag_fdc_con.tty_drv = driver;
init_waitqueue_head(&priv->waitqueue);
/*
* Bind the writer thread to the right CPU so it can't migrate.
* The channels are per-CPU and we want all channel I/O to be on a
* single predictable CPU.
*/
priv->thread = kthread_run_on_cpu(mips_ejtag_fdc_put, priv,
dev->cpu, "ttyFDC/%u");
if (IS_ERR(priv->thread)) {
ret = PTR_ERR(priv->thread);
dev_err(priv->dev, "Couldn't create kthread (%d)\n", ret);
goto err_destroy_ports;
}
/* Look for an FDC IRQ */
priv->irq = get_c0_fdc_int();
/* Try requesting the IRQ */
if (priv->irq >= 0) {
/*
* IRQF_SHARED, IRQF_COND_SUSPEND: The FDC IRQ may be shared with
* other local interrupts such as the timer which sets
* IRQF_TIMER (including IRQF_NO_SUSPEND).
*
* IRQF_NO_THREAD: The FDC IRQ isn't individually maskable so it
* cannot be deferred and handled by a thread on RT kernels. For
* this reason any spinlocks used from the ISR are raw.
*/
ret = devm_request_irq(priv->dev, priv->irq, mips_ejtag_fdc_isr,
IRQF_PERCPU | IRQF_SHARED |
IRQF_NO_THREAD | IRQF_COND_SUSPEND,
priv->fdc_name, priv);
if (ret)
priv->irq = -1;
}
if (priv->irq >= 0) {
/* IRQ is usable, enable RX interrupt */
raw_spin_lock_irq(&priv->lock);
cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
cfg &= ~REG_FDCFG_RXINTTHRES;
cfg |= REG_FDCFG_RXINTTHRES_NOTEMPTY;
mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
raw_spin_unlock_irq(&priv->lock);
} else {
/* If we didn't get an usable IRQ, poll instead */
timer_setup(&priv->poll_timer, mips_ejtag_fdc_tty_timer,
TIMER_PINNED);
priv->poll_timer.expires = jiffies + FDC_TTY_POLL;
/*
* Always attach the timer to the right CPU. The channels are
* per-CPU so all polling should be from a single CPU.
*/
add_timer_on(&priv->poll_timer, dev->cpu);
dev_info(priv->dev, "No usable IRQ, polling enabled\n");
}
ret = tty_register_driver(driver);
if (ret < 0) {
dev_err(priv->dev, "Couldn't install tty driver (%d)\n", ret);
goto err_stop_irq;
}
return 0;
err_stop_irq:
if (priv->irq >= 0) {
raw_spin_lock_irq(&priv->lock);
cfg = mips_ejtag_fdc_read(priv, REG_FDCFG);
/* Disable interrupts */
cfg &= ~(REG_FDCFG_TXINTTHRES | REG_FDCFG_RXINTTHRES);
cfg |= REG_FDCFG_TXINTTHRES_DISABLED;
cfg |= REG_FDCFG_RXINTTHRES_DISABLED;
mips_ejtag_fdc_write(priv, REG_FDCFG, cfg);
raw_spin_unlock_irq(&priv->lock);
} else {
priv->removing = true;
del_timer_sync(&priv->poll_timer);
}
kthread_stop(priv->thread);
err_destroy_ports:
if (dev->cpu == 0)
mips_ejtag_fdc_con.tty_drv = NULL;
for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
dport = &priv->ports[nport];
tty_port_destroy(&dport->port);
}
tty_driver_kref_put(priv->driver);
return ret;
}