/** * xuartps_set_baud_rate - Calculate and set the baud rate * @port: Handle to the uart port structure * @baud: Baud rate to set * Returns baud rate, requested baud when possible, or actual baud when there * was too much error, zero if no valid divisors are found. */ static unsigned int xuartps_set_baud_rate(struct uart_port *port, unsigned int baud) { unsigned int calc_baud; u32 cd = 0, bdiv = 0; u32 mreg; int div8; struct xuartps *xuartps = port->private_data; calc_baud = xuartps_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, &div8); /* Write new divisors to hardware */ mreg = xuartps_readl(XUARTPS_MR_OFFSET); if (div8) mreg |= XUARTPS_MR_CLKSEL; else mreg &= ~XUARTPS_MR_CLKSEL; xuartps_writel(mreg, XUARTPS_MR_OFFSET); xuartps_writel(cd, XUARTPS_BAUDGEN_OFFSET); xuartps_writel(bdiv, XUARTPS_BAUDDIV_OFFSET); xuartps->baud = baud; return calc_baud; }
/** * xuartps_clk_notitifer_cb - Clock notifier callback * @nb: Notifier block * @event: Notify event * @data: Notifier data * Returns NOTIFY_OK on success, NOTIFY_BAD on error. */ static int xuartps_clk_notifier_cb(struct notifier_block *nb, unsigned long event, void *data) { u32 ctrl_reg; struct uart_port *port; int locked = 0; struct clk_notifier_data *ndata = data; unsigned long flags = 0; struct xuartps *xuartps = to_xuartps(nb); port = xuartps->port; if (port->suspended) return NOTIFY_OK; switch (event) { case PRE_RATE_CHANGE: { u32 bdiv; u32 cd; int div8; /* * Find out if current baud-rate can be achieved with new clock * frequency. */ if (!xuartps_calc_baud_divs(ndata->new_rate, xuartps->baud, &bdiv, &cd, &div8)) return NOTIFY_BAD; spin_lock_irqsave(&xuartps->port->lock, flags); /* Disable the TX and RX to set baud rate */ xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS), XUARTPS_CR_OFFSET); spin_unlock_irqrestore(&xuartps->port->lock, flags); return NOTIFY_OK; } case POST_RATE_CHANGE: /* * Set clk dividers to generate correct baud with new clock * frequency. */ spin_lock_irqsave(&xuartps->port->lock, flags); locked = 1; port->uartclk = ndata->new_rate; xuartps->baud = xuartps_set_baud_rate(xuartps->port, xuartps->baud); /* fall through */ case ABORT_RATE_CHANGE: if (!locked) spin_lock_irqsave(&xuartps->port->lock, flags); /* Set TX/RX Reset */ xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST), XUARTPS_CR_OFFSET); while (xuartps_readl(XUARTPS_CR_OFFSET) & (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST)) cpu_relax(); /* * Clear the RX disable and TX disable bits and then set the TX * enable bit and RX enable bit to enable the transmitter and * receiver. */ xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); xuartps_writel( (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN), XUARTPS_CR_OFFSET); spin_unlock_irqrestore(&xuartps->port->lock, flags); return NOTIFY_OK; default: return NOTIFY_DONE; } }
/* * no clue yet how to implement this. i think we need access to the port * structure in this function to do the required changes. but i don't know how * to get it in here. * * Think I got it. port must have a notifier_block* member from where we then * can get to the outer port structure with a container_of() call. * * Not sure when to stop UART. probably it's enough to stop, reconfigure, start * in POST_RATE_CHANGE */ static int xuartps_clk_notifier_cb(struct notifier_block *nb, unsigned long event, void *data) { struct clk_notifier_data *ndata = data; struct uart_port *port; struct xuartps *xuartps = to_xuartps(nb); port = xuartps->port; switch (event) { case PRE_RATE_CHANGE: { u32 bdiv; u32 cd; int div8; /* Find out if current baud-rate can be achieved with new clock * frequency. */ if (!xuartps_calc_baud_divs(ndata->new_rate, xuartps->baud, &bdiv, &cd, &div8)) return NOTIFY_BAD; return NOTIFY_OK; } case POST_RATE_CHANGE: /* Set clk dividers to generate correct baud with new clock * frequency. */ { u32 ctrl_reg; unsigned long flags = 0; spin_lock_irqsave(&xuartps->port->lock, flags); port->uartclk = ndata->new_rate; /* Empty the receive FIFO 1st before making changes */ while ((xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) xuartps_readl(XUARTPS_FIFO_OFFSET); /* Disable the TX and RX to set baud rate */ xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS), XUARTPS_CR_OFFSET); xuartps->baud = xuartps_set_baud_rate(xuartps->port, xuartps->baud); /* Set TX/RX Reset */ xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST), XUARTPS_CR_OFFSET); /* * Clear the RX disable and TX disable bits and then set the TX * enable bit and RX enable bit to enable the transmitter and * receiver. */ ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); xuartps_writel( (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN), XUARTPS_CR_OFFSET); spin_unlock_irqrestore(&xuartps->port->lock, flags); return NOTIFY_OK; } case ABORT_RATE_CHANGE: return NOTIFY_OK; default: return NOTIFY_DONE; } }