Exemple #1
0
static int moxart_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct device_node *node = dev->of_node;
	struct resource res_mmc;
	struct mmc_host *mmc;
	struct moxart_host *host = NULL;
	struct dma_slave_config cfg;
	struct clk *clk;
	void __iomem *reg_mmc;
	int irq, ret;
	u32 i;

	mmc = mmc_alloc_host(sizeof(struct moxart_host), dev);
	if (!mmc) {
		dev_err(dev, "mmc_alloc_host failed\n");
		ret = -ENOMEM;
		goto out;
	}

	ret = of_address_to_resource(node, 0, &res_mmc);
	if (ret) {
		dev_err(dev, "of_address_to_resource failed\n");
		goto out;
	}

	irq = irq_of_parse_and_map(node, 0);
	if (irq <= 0) {
		dev_err(dev, "irq_of_parse_and_map failed\n");
		ret = -EINVAL;
		goto out;
	}

	clk = devm_clk_get(dev, NULL);
	if (IS_ERR(clk)) {
		ret = PTR_ERR(clk);
		goto out;
	}

	reg_mmc = devm_ioremap_resource(dev, &res_mmc);
	if (IS_ERR(reg_mmc)) {
		ret = PTR_ERR(reg_mmc);
		goto out;
	}

	ret = mmc_of_parse(mmc);
	if (ret)
		goto out;

	host = mmc_priv(mmc);
	host->mmc = mmc;
	host->base = reg_mmc;
	host->reg_phys = res_mmc.start;
	host->timeout = msecs_to_jiffies(1000);
	host->sysclk = clk_get_rate(clk);
	host->fifo_width = readl(host->base + REG_FEATURE) << 2;
	host->dma_chan_tx = dma_request_slave_channel_reason(dev, "tx");
	host->dma_chan_rx = dma_request_slave_channel_reason(dev, "rx");

	spin_lock_init(&host->lock);

	mmc->ops = &moxart_ops;
	mmc->f_max = DIV_ROUND_CLOSEST(host->sysclk, 2);
	mmc->f_min = DIV_ROUND_CLOSEST(host->sysclk, CLK_DIV_MASK * 2);
	mmc->ocr_avail = 0xffff00;	/* Support 2.0v - 3.6v power. */

	if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) {
		if (PTR_ERR(host->dma_chan_tx) == -EPROBE_DEFER ||
		    PTR_ERR(host->dma_chan_rx) == -EPROBE_DEFER) {
			ret = -EPROBE_DEFER;
			goto out;
		}
		dev_dbg(dev, "PIO mode transfer enabled\n");
		host->have_dma = false;
	} else {
		dev_dbg(dev, "DMA channels found (%p,%p)\n",
			 host->dma_chan_tx, host->dma_chan_rx);
		host->have_dma = true;

		cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
		cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;

		cfg.direction = DMA_MEM_TO_DEV;
		cfg.src_addr = 0;
		cfg.dst_addr = host->reg_phys + REG_DATA_WINDOW;
		dmaengine_slave_config(host->dma_chan_tx, &cfg);

		cfg.direction = DMA_DEV_TO_MEM;
		cfg.src_addr = host->reg_phys + REG_DATA_WINDOW;
		cfg.dst_addr = 0;
		dmaengine_slave_config(host->dma_chan_rx, &cfg);
	}

	switch ((readl(host->base + REG_BUS_WIDTH) >> 3) & 3) {
	case 1:
		mmc->caps |= MMC_CAP_4_BIT_DATA;
		break;
	case 2:
		mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
		break;
	default:
		break;
	}

	writel(0, host->base + REG_INTERRUPT_MASK);

	writel(CMD_SDC_RESET, host->base + REG_COMMAND);
	for (i = 0; i < MAX_RETRIES; i++) {
		if (!(readl(host->base + REG_COMMAND) & CMD_SDC_RESET))
			break;
		udelay(5);
	}

	ret = devm_request_irq(dev, irq, moxart_irq, 0, "moxart-mmc", host);
	if (ret)
		goto out;

	dev_set_drvdata(dev, mmc);
	mmc_add_host(mmc);

	dev_dbg(dev, "IRQ=%d, FIFO is %d bytes\n", irq, host->fifo_width);

	return 0;

out:
	if (mmc)
		mmc_free_host(mmc);
	return ret;
}
Exemple #2
0
static inline u16 freq_to_clock_divider(unsigned int freq,
					unsigned int rollovers)
{
	return count_to_clock_divider(
		   DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers));
}
Exemple #3
0
static inline u16 ns_to_lpf_count(unsigned int ns)
{
	return count_to_lpf_count(
		DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
}
Exemple #4
0
static int vt8500_serial_probe(struct platform_device *pdev)
{
	struct vt8500_port *vt8500_port;
	struct resource *mmres, *irqres;
	struct device_node *np = pdev->dev.of_node;
	const struct of_device_id *match;
	const unsigned int *flags;
	int ret;
	int port;

	match = of_match_device(wmt_dt_ids, &pdev->dev);
	if (!match)
		return -EINVAL;

	flags = match->data;

	mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (!mmres || !irqres)
		return -ENODEV;

	if (np) {
		port = of_alias_get_id(np, "serial");
		if (port >= VT8500_MAX_PORTS)
			port = -1;
	} else {
		port = -1;
	}

	if (port < 0) {
		/* calculate the port id */
		port = find_first_zero_bit(&vt8500_ports_in_use,
					sizeof(vt8500_ports_in_use));
	}

	if (port >= VT8500_MAX_PORTS)
		return -ENODEV;

	/* reserve the port id */
	if (test_and_set_bit(port, &vt8500_ports_in_use)) {
		/* port already in use - shouldn't really happen */
		return -EBUSY;
	}

	vt8500_port = devm_kzalloc(&pdev->dev, sizeof(struct vt8500_port),
				   GFP_KERNEL);
	if (!vt8500_port)
		return -ENOMEM;

	vt8500_port->uart.membase = devm_ioremap_resource(&pdev->dev, mmres);
	if (IS_ERR(vt8500_port->uart.membase))
		return PTR_ERR(vt8500_port->uart.membase);

	vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
	if (IS_ERR(vt8500_port->clk)) {
		dev_err(&pdev->dev, "failed to get clock\n");
		return  -EINVAL;
	}

	ret = clk_prepare_enable(vt8500_port->clk);
	if (ret) {
		dev_err(&pdev->dev, "failed to enable clock\n");
		return ret;
	}

	vt8500_port->vt8500_uart_flags = *flags;
	vt8500_port->clk_predivisor = DIV_ROUND_CLOSEST(
					clk_get_rate(vt8500_port->clk),
					VT8500_RECOMMENDED_CLK
				      );
	vt8500_port->uart.type = PORT_VT8500;
	vt8500_port->uart.iotype = UPIO_MEM;
	vt8500_port->uart.mapbase = mmres->start;
	vt8500_port->uart.irq = irqres->start;
	vt8500_port->uart.fifosize = 16;
	vt8500_port->uart.ops = &vt8500_uart_pops;
	vt8500_port->uart.line = port;
	vt8500_port->uart.dev = &pdev->dev;
	vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;

	/* Serial core uses the magic "16" everywhere - adjust for it */
	vt8500_port->uart.uartclk = 16 * clk_get_rate(vt8500_port->clk) /
					vt8500_port->clk_predivisor /
					VT8500_OVERSAMPLING_DIVISOR;

	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
		 "VT8500 UART%d", pdev->id);

	vt8500_uart_ports[port] = vt8500_port;

	uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);

	platform_set_drvdata(pdev, vt8500_port);

	return 0;
}
Exemple #5
0
static inline u16 carrier_freq_to_clock_divider(unsigned int freq)
{
	return count_to_clock_divider(
			  DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16));
}
int s3cfb_set_clock(struct s3cfb_global *ctrl)
{
	struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev);
	u32 cfg, maxclk, src_clk, vclk, div;

	/* spec is under 100MHz */
	maxclk = 100 * 1000000;

	cfg = readl(ctrl->regs + S3C_VIDCON0);

	if (pdata->hw_ver == 0x70) {
		cfg &= ~(S3C_VIDCON0_CLKVALUP_MASK |
			S3C_VIDCON0_VCLKEN_MASK);
		cfg |= (S3C_VIDCON0_CLKVALUP_ALWAYS |
			S3C_VIDCON0_VCLKEN_FREERUN);

		src_clk = clk_get_rate(ctrl->clock);
		printk(KERN_DEBUG "FIMD src sclk = %d\n", src_clk);
	} else {
		cfg &= ~(S3C_VIDCON0_CLKSEL_MASK |
			S3C_VIDCON0_CLKVALUP_MASK |
			S3C_VIDCON0_VCLKEN_MASK |
			S3C_VIDCON0_CLKDIR_MASK);
		cfg |= (S3C_VIDCON0_CLKVALUP_ALWAYS |
			S3C_VIDCON0_VCLKEN_NORMAL |
			S3C_VIDCON0_CLKDIR_DIVIDED);

		if (strcmp(pdata->clk_name, "sclk_fimd") == 0) {
			cfg |= S3C_VIDCON0_CLKSEL_SCLK;
			src_clk = clk_get_rate(ctrl->clock);
			printk(KERN_DEBUG "FIMD src sclk = %d\n", src_clk);

		} else {
			cfg |= S3C_VIDCON0_CLKSEL_HCLK;
			src_clk = ctrl->clock->parent->rate;
			printk(KERN_DEBUG "FIMD src hclk = %d\n", src_clk);
		}
	}

	vclk = PICOS2KHZ(ctrl->fb[pdata->default_win]->var.pixclock) * 1000;

	if (vclk > maxclk) {
		dev_info(ctrl->dev, "vclk(%d) should be smaller than %d\n",
			vclk, maxclk);
		/* vclk = maxclk; */
	}

	div = DIV_ROUND_CLOSEST(src_clk, vclk);

	if (div == 0) {
		dev_err(ctrl->dev, "div(%d) should be non-zero\n", div);
		div = 1;
	}

	if ((src_clk/div) > maxclk)
		dev_info(ctrl->dev, "vclk(%d) should be smaller than %d Hz\n",
			src_clk/div, maxclk);

	cfg &= ~S3C_VIDCON0_CLKVAL_F(0xff);
	cfg |= S3C_VIDCON0_CLKVAL_F(div - 1);
	writel(cfg, ctrl->regs + S3C_VIDCON0);

	dev_info(ctrl->dev, "parent clock: %d, vclk: %d, vclk div: %d\n",
			src_clk, vclk, div);

	return 0;
}
Exemple #7
0
u32 vlv_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp,
		     struct intel_crtc_state *config)
{
	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
	struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
	u32 dsi_clock, pclk;
	u32 pll_ctl, pll_div;
	u32 m = 0, p = 0, n;
	int refclk = IS_CHERRYVIEW(dev_priv) ? 100000 : 25000;
	int i;

	DRM_DEBUG_KMS("\n");

	mutex_lock(&dev_priv->sb_lock);
	pll_ctl = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_CONTROL);
	pll_div = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_DIVIDER);
	mutex_unlock(&dev_priv->sb_lock);

	config->dsi_pll.ctrl = pll_ctl & ~DSI_PLL_LOCK;
	config->dsi_pll.div = pll_div;

	/* mask out other bits and extract the P1 divisor */
	pll_ctl &= DSI_PLL_P1_POST_DIV_MASK;
	pll_ctl = pll_ctl >> (DSI_PLL_P1_POST_DIV_SHIFT - 2);

	/* N1 divisor */
	n = (pll_div & DSI_PLL_N1_DIV_MASK) >> DSI_PLL_N1_DIV_SHIFT;
	n = 1 << n; /* register has log2(N1) */

	/* mask out the other bits and extract the M1 divisor */
	pll_div &= DSI_PLL_M1_DIV_MASK;
	pll_div = pll_div >> DSI_PLL_M1_DIV_SHIFT;

	while (pll_ctl) {
		pll_ctl = pll_ctl >> 1;
		p++;
	}
	p--;

	if (!p) {
		DRM_ERROR("wrong P1 divisor\n");
		return 0;
	}

	for (i = 0; i < ARRAY_SIZE(lfsr_converts); i++) {
		if (lfsr_converts[i] == pll_div)
			break;
	}

	if (i == ARRAY_SIZE(lfsr_converts)) {
		DRM_ERROR("wrong m_seed programmed\n");
		return 0;
	}

	m = i + 62;

	dsi_clock = (m * refclk) / (p * n);

	/* pixel_format and pipe_bpp should agree */
	assert_bpp_mismatch(intel_dsi->pixel_format, pipe_bpp);

	pclk = DIV_ROUND_CLOSEST(dsi_clock * intel_dsi->lane_count, pipe_bpp);

	return pclk;
}
static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{
	struct iguanair *ir = dev->priv;
	uint8_t space, *payload;
	unsigned i, size, rc;
	struct send_packet *packet;

	mutex_lock(&ir->lock);

	/* convert from us to carrier periods */
	for (i = size = 0; i < count; i++) {
		txbuf[i] = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
		size += (txbuf[i] + 126) / 127;
	}

	packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL);
	if (!packet) {
		rc = -ENOMEM;
		goto out;
	}

	if (size > ir->bufsize) {
		rc = -E2BIG;
		goto out;
	}

	packet->header.start = 0;
	packet->header.direction = DIR_OUT;
	packet->header.cmd = CMD_SEND;
	packet->length = size;
	packet->channels = ir->channels << 4;
	packet->busy7 = ir->busy7;
	packet->busy4 = ir->busy4;

	space = 0;
	payload = packet->payload;

	for (i = 0; i < count; i++) {
		unsigned periods = txbuf[i];

		while (periods > 127) {
			*payload++ = 127 | space;
			periods -= 127;
		}

		*payload++ = periods | space;
		space ^= 0x80;
	}

	if (ir->receiver_on) {
		rc = iguanair_receiver(ir, false);
		if (rc) {
			dev_warn(ir->dev, "disable receiver before transmit failed\n");
			goto out;
		}
	}

	ir->tx_overflow = false;

	INIT_COMPLETION(ir->completion);

	rc = iguanair_send(ir, packet, size + 8, NULL, NULL);

	if (rc == 0) {
		wait_for_completion_timeout(&ir->completion, TIMEOUT);
		if (ir->tx_overflow)
			rc = -EOVERFLOW;
	}

	ir->tx_overflow = false;

	if (ir->receiver_on) {
		if (iguanair_receiver(ir, true))
			dev_warn(ir->dev, "re-enable receiver after transmit failed\n");
	}

out:
	mutex_unlock(&ir->lock);
	kfree(packet);

	return rc;
}
Exemple #9
0
static inline int calc_divisor(NS16550_t port, int clock, int baudrate)
{
	const unsigned int mode_x_div = 16;

	return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate);
}
Exemple #10
0
static inline int DS1621_TEMP_FROM_REG(u16 reg)
{
	return DIV_ROUND_CLOSEST(((s16)reg / 16) * 625, 10);
}
Exemple #11
0
/*
 * TEMP: 0.001C/bit (-55C to +125C)
 * REG:
 *  - 1621, 1625: 0.5C/bit, 7 zero-bits
 *  - 1631, 1721, 1731: 0.0625C/bit, 4 zero-bits
 */
static inline u16 DS1621_TEMP_TO_REG(long temp, u8 zbits)
{
	temp = clamp_val(temp, DS1621_TEMP_MIN, DS1621_TEMP_MAX);
	temp = DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits;
	return temp;
}
static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
		int duty_ns, int period_ns)
{
	struct s3c_chip *s3c = to_s3c_chip(chip);
	struct s3c_pwm_device *s3c_pwm = pwm_get_chip_data(pwm);
	void __iomem *reg_base = s3c->reg_base;
	unsigned long tin_rate;
	unsigned long tin_ns;
	unsigned long period;
	unsigned long flags;
	unsigned long tcon;
	unsigned long tcnt;
	long tcmp;
	enum duty_cycle duty_cycle;
	unsigned int id = pwm->pwm;

	/* We currently avoid using 64bit arithmetic by using the
	 * fact that anything faster than 1Hz is easily representable
	 * by 32bits. */

	if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ)
		return -ERANGE;

	if (duty_ns > period_ns)
		return -EINVAL;

	if (period_ns == s3c_pwm->period_ns &&
	    duty_ns == s3c_pwm->duty_ns)
		return 0;

	period = NS_IN_HZ / period_ns;

	/* Check to see if we are changing the clock rate of the PWM */

	if (s3c_pwm->period_ns != period_ns && pwm_is_tdiv(s3c_pwm)) {
		tin_rate = pwm_calc_tin(pwm, period);
		clk_set_rate(s3c_pwm->clk_div, tin_rate);
		tin_rate = clk_get_rate(s3c_pwm->clk_div);
		s3c_pwm->period_ns = period_ns;
		pwm_dbg(s3c, "tin_rate=%lu\n", tin_rate);
	} else {
		tin_rate = clk_get_rate(s3c_pwm->clk_tin);
	}

	if(!tin_rate)
		return -EFAULT;

	/* Note, counters count down */
	tin_ns = NS_IN_HZ / tin_rate;

	tcnt = DIV_ROUND_CLOSEST(period_ns, tin_ns);
	tcmp = DIV_ROUND_CLOSEST(duty_ns, tin_ns);

	if (tcnt <= 1) {
		/* Too small to generate a pulse */
		return -ERANGE;
	}

	pwm_dbg(s3c, "duty_ns=%d, period_ns=%d (%lu)\n",
		duty_ns, period_ns, period);

	if (tcmp == 0)
		duty_cycle = DUTY_CYCLE_ZERO;
	else if (tcmp == tcnt)
		duty_cycle = DUTY_CYCLE_FULL;
	else
		duty_cycle = DUTY_CYCLE_PULSE;

	tcmp = tcnt - tcmp;
	/* the pwm hw only checks the compare register after a decrement,
	   so the pin never toggles if tcmp = tcnt */
	if (tcmp == tcnt)
		tcmp--;

	/*
	 * PWM counts 1 hidden tick at the end of each period on S3C64XX and
	 * EXYNOS series, so tcmp and tcnt should be subtracted 1.
	 */
	if (!pwm_is_s3c24xx(s3c)) {
		tcnt--;
		/*
		 * tcmp can be -1. It appears 100% duty cycle and PWM never
		 * toggles when TCMPB is set to 0xFFFFFFFF (-1).
		 */
		tcmp--;
	}

	pwm_dbg(s3c, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);

	/* Update the PWM register block. */

	spin_lock_irqsave(&pwm_spinlock, flags);

	__raw_writel(tcmp, reg_base + REG_TCMPB(id));
	__raw_writel(tcnt, reg_base + REG_TCNTB(id));

	if (pwm_is_s3c24xx(s3c)) {
		tcon = __raw_readl(reg_base + REG_TCON);
		tcon |= pwm_tcon_manulupdate(s3c_pwm);
		tcon |= pwm_tcon_autoreload(s3c_pwm);
		__raw_writel(tcon, reg_base + REG_TCON);

		tcon &= ~pwm_tcon_manulupdate(s3c_pwm);
		__raw_writel(tcon, reg_base + REG_TCON);
	} else {
		tcon = __raw_readl(reg_base + REG_TCON);
		if (s3c_pwm->running == 1 &&
		    tcon & pwm_tcon_start(s3c_pwm) &&
		    s3c_pwm->duty_cycle != duty_cycle) {
			if (duty_cycle == DUTY_CYCLE_ZERO) {
				tcon |= pwm_tcon_manulupdate(s3c_pwm);
				__raw_writel(tcon, reg_base + REG_TCON);

				tcon &= ~pwm_tcon_manulupdate(s3c_pwm);
				tcon &= ~pwm_tcon_autoreload(s3c_pwm);
			} else {
				tcon |= pwm_tcon_autoreload(s3c_pwm);
			}
			__raw_writel(tcon, reg_base + REG_TCON);
		}
	}
	s3c_pwm->duty_ns = duty_ns;
	s3c_pwm->period_ns = period_ns;
	s3c_pwm->duty_cycle = duty_cycle;

	spin_unlock_irqrestore(&pwm_spinlock, flags);

	return 0;
}
Exemple #13
0
int get_divider(struct fb_info *fb)
{
	struct s3cfb_window *win = fb->par;
	struct s3cfb_global *fbdev = get_fimd_global(win->id);
	struct lcdfreq_info *lcdfreq = fbdev->data;
	struct clksrc_clk *sclk;
	struct clk *clk;
	u32 rate, reg, i;
	u8 fimd_div;

	sclk = container_of(fbdev->clock, struct clksrc_clk, clk);
	clk = clk_get_parent(clk_get_parent(fbdev->clock));
	rate = clk_get_rate(clk);

	lcdfreq->table[LEVEL_NORMAL].cmu_clkdiv =
		DIV_ROUND_CLOSEST(rate, lcdfreq->table[LEVEL_NORMAL].vclk);

	lcdfreq->table[LEVEL_LIMIT].cmu_clkdiv =
		DIV_ROUND_CLOSEST(rate, lcdfreq->table[LEVEL_LIMIT].vclk);

	fimd_div = gcd(lcdfreq->table[LEVEL_NORMAL].cmu_clkdiv, lcdfreq->table[LEVEL_LIMIT].cmu_clkdiv);

	if ((!fimd_div) || (fimd_div > 16)) {
		dev_info(fb->dev, "%s skip, %d\n", __func__, __LINE__);
		goto err;
	}

	lcdfreq->table[LEVEL_NORMAL].cmu_clkdiv /= fimd_div;
	lcdfreq->table[LEVEL_LIMIT].cmu_clkdiv /= fimd_div;

	dev_info(fb->dev, "%s rate is %d, fimd divider=%d\n", clk->name, rate, fimd_div);

	fimd_div--;
	for (i = 0; i < LCDFREQ_LEVEL_END; i++) {
		if (lcdfreq->table[i].cmu_clkdiv > 16) {
			dev_info(fb->dev, "%s skip, %d\n", __func__, __LINE__);
			goto err;
		}
		dev_info(fb->dev, "%dhz div is %d\n",
		lcdfreq->table[i].hz, lcdfreq->table[i].cmu_clkdiv);
		lcdfreq->table[i].cmu_clkdiv--;
	}

	reg = (readl(fbdev->regs + S3C_VIDCON0) & (S3C_VIDCON0_CLKVAL_F(0xff))) >> 6;
	if (fimd_div != reg) {
		dev_info(fb->dev, "%s skip, %d\n", __func__, __LINE__);
		goto err;
	}

	reg = (readl(sclk->reg_div.reg)) >> sclk->reg_div.shift;
	reg &= 0xf;
	if (lcdfreq->table[LEVEL_NORMAL].cmu_clkdiv != reg) {
		dev_info(fb->dev, "%s skip, %d\n", __func__, __LINE__);
		goto err;
	}

	return 0;

err:
	return -EINVAL;
}
static void
mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
			struct ktermios *old)
{
	struct uart_8250_port *up = up_to_u8250p(port);
	unsigned long flags;
	unsigned int baud, quot;

#ifdef CONFIG_SERIAL_8250_DMA
	if (up->dma) {
		if (uart_console(port)) {
			devm_kfree(up->port.dev, up->dma);
			up->dma = NULL;
		} else {
			mtk8250_dma_enable(up);
		}
	}
#endif

	serial8250_do_set_termios(port, termios, old);

	/*
	 * Mediatek UARTs use an extra highspeed register (UART_MTK_HIGHS)
	 *
	 * We need to recalcualte the quot register, as the claculation depends
	 * on the vaule in the highspeed register.
	 *
	 * Some baudrates are not supported by the chip, so we use the next
	 * lower rate supported and update termios c_flag.
	 *
	 * If highspeed register is set to 3, we need to specify sample count
	 * and sample point to increase accuracy. If not, we reset the
	 * registers to their default values.
	 */
	baud = uart_get_baud_rate(port, termios, old,
				  port->uartclk / 16 / UART_DIV_MAX,
				  port->uartclk);

	if (baud <= 115200) {
		serial_port_out(port, UART_MTK_HIGHS, 0x0);
		quot = uart_get_divisor(port, baud);
	} else if (baud <= 576000) {
		serial_port_out(port, UART_MTK_HIGHS, 0x2);

		/* Set to next lower baudrate supported */
		if ((baud == 500000) || (baud == 576000))
			baud = 460800;
		quot = DIV_ROUND_UP(port->uartclk, 4 * baud);
	} else {
		serial_port_out(port, UART_MTK_HIGHS, 0x3);
		quot = DIV_ROUND_UP(port->uartclk, 256 * baud);
	}

	/*
	 * Ok, we're now changing the port state.  Do it with
	 * interrupts disabled.
	 */
	spin_lock_irqsave(&port->lock, flags);

	/* set DLAB we have cval saved in up->lcr from the call to the core */
	serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
	serial_dl_write(up, quot);

	/* reset DLAB */
	serial_port_out(port, UART_LCR, up->lcr);

	if (baud > 460800) {
		unsigned int tmp;

		tmp = DIV_ROUND_CLOSEST(port->uartclk, quot * baud);
		serial_port_out(port, UART_MTK_SAMPLE_COUNT, tmp - 1);
		serial_port_out(port, UART_MTK_SAMPLE_POINT,
					(tmp - 2) >> 1);
	} else {
Exemple #15
0
static int max310x_set_ref_clk(struct max310x_port *s, unsigned long freq,
			       bool xtal)
{
	unsigned int div, clksrc, pllcfg = 0;
	long besterr = -1;
	unsigned long fdiv, fmul, bestfreq = freq;

	/* First, update error without PLL */
	max310x_update_best_err(freq, &besterr);

	/* Try all possible PLL dividers */
	for (div = 1; (div <= 63) && besterr; div++) {
		fdiv = DIV_ROUND_CLOSEST(freq, div);

		/* Try multiplier 6 */
		fmul = fdiv * 6;
		if ((fdiv >= 500000) && (fdiv <= 800000))
			if (!max310x_update_best_err(fmul, &besterr)) {
				pllcfg = (0 << 6) | div;
				bestfreq = fmul;
			}
		/* Try multiplier 48 */
		fmul = fdiv * 48;
		if ((fdiv >= 850000) && (fdiv <= 1200000))
			if (!max310x_update_best_err(fmul, &besterr)) {
				pllcfg = (1 << 6) | div;
				bestfreq = fmul;
			}
		/* Try multiplier 96 */
		fmul = fdiv * 96;
		if ((fdiv >= 425000) && (fdiv <= 1000000))
			if (!max310x_update_best_err(fmul, &besterr)) {
				pllcfg = (2 << 6) | div;
				bestfreq = fmul;
			}
		/* Try multiplier 144 */
		fmul = fdiv * 144;
		if ((fdiv >= 390000) && (fdiv <= 667000))
			if (!max310x_update_best_err(fmul, &besterr)) {
				pllcfg = (3 << 6) | div;
				bestfreq = fmul;
			}
	}

	/* Configure clock source */
	clksrc = xtal ? MAX310X_CLKSRC_CRYST_BIT : MAX310X_CLKSRC_EXTCLK_BIT;

	/* Configure PLL */
	if (pllcfg) {
		clksrc |= MAX310X_CLKSRC_PLL_BIT;
		regmap_write(s->regmap, MAX310X_PLLCFG_REG, pllcfg);
	} else
		clksrc |= MAX310X_CLKSRC_PLLBYP_BIT;

	regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc);

	/* Wait for crystal */
	if (pllcfg && xtal)
		msleep(10);

	return (int)bestfreq;
}
Exemple #16
0
/*
 * vrm is the VRM/VRD document version multiplied by 10.
 * val is the 4-bit or more VID code.
 * Returned value is in mV to avoid floating point in the kernel.
 * Some VID have some bits in uV scale, this is rounded to mV.
 */
int vid_from_reg(int val, u8 vrm)
{
	int vid;

	switch (vrm) {

	case 100:		/* VRD 10.0 */
		/* compute in uV, round to mV */
		val &= 0x3f;
		if ((val & 0x1f) == 0x1f)
			return 0;
		if ((val & 0x1f) <= 0x09 || val == 0x0a)
			vid = 1087500 - (val & 0x1f) * 25000;
		else
			vid = 1862500 - (val & 0x1f) * 25000;
		if (val & 0x20)
			vid -= 12500;
		return (vid + 500) / 1000;

	case 110:		/* Intel Conroe */
				/* compute in uV, round to mV */
		val &= 0xff;
		if (val < 0x02 || val > 0xb2)
			return 0;
		return (1600000 - (val - 2) * 6250 + 500) / 1000;

	case 24:		/* Athlon64 & Opteron */
		val &= 0x1f;
		if (val == 0x1f)
			return 0;
				/* fall through */
	case 25:		/* AMD NPT 0Fh */
		val &= 0x3f;
		return (val < 32) ? 1550 - 25 * val
			: 775 - (25 * (val - 31)) / 2;

	case 26:		/* AMD family 10h to 15h, serial VID */
		val &= 0x7f;
		if (val >= 0x7c)
			return 0;
		return DIV_ROUND_CLOSEST(15500 - 125 * val, 10);

	case 91:		/* VRM 9.1 */
	case 90:		/* VRM 9.0 */
		val &= 0x1f;
		return val == 0x1f ? 0 :
				     1850 - val * 25;

	case 85:		/* VRM 8.5 */
		val &= 0x1f;
		return (val & 0x10  ? 25 : 0) +
		       ((val & 0x0f) > 0x04 ? 2050 : 1250) -
		       ((val & 0x0f) * 50);

	case 84:		/* VRM 8.4 */
		val &= 0x0f;
				/* fall through */
	case 82:		/* VRM 8.2 */
		val &= 0x1f;
		return val == 0x1f ? 0 :
		       val & 0x10  ? 5100 - (val) * 100 :
				     2050 - (val) * 50;
	case 17:		/* Intel IMVP-II */
		val &= 0x1f;
		return val & 0x10 ? 975 - (val & 0xF) * 25 :
				    1750 - val * 50;
	case 13:
	case 131:
		val &= 0x3f;
		/* Exception for Eden ULV 500 MHz */
		if (vrm == 131 && val == 0x3f)
			val++;
		return 1708 - val * 16;
	case 14:		/* Intel Core */
				/* compute in uV, round to mV */
		val &= 0x7f;
		return val > 0x77 ? 0 : (1500000 - (val * 12500) + 500) / 1000;
	default:		/* report 0 for unknown */
		if (vrm)
			pr_warn("Requested unsupported VRM version (%u)\n",
				(unsigned int)vrm);
		return 0;
	}
}
Exemple #17
0
unsigned int
uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
		   struct ktermios *old, unsigned int min, unsigned int max)
{
	unsigned int try, baud, altbaud = 38400;
	int hung_up = 0;
	upf_t flags = port->flags & UPF_SPD_MASK;

	if (flags == UPF_SPD_HI)
		altbaud = 57600;
	else if (flags == UPF_SPD_VHI)
		altbaud = 115200;
	else if (flags == UPF_SPD_SHI)
		altbaud = 230400;
	else if (flags == UPF_SPD_WARP)
		altbaud = 460800;

	for (try = 0; try < 2; try++) {
		baud = tty_termios_baud_rate(termios);

		if (baud == 38400)
			baud = altbaud;

		if (baud == 0) {
			hung_up = 1;
			baud = 9600;
		}

		if (baud >= min && baud <= max)
			return baud;

		termios->c_cflag &= ~CBAUD;
		if (old) {
			baud = tty_termios_baud_rate(old);
			if (!hung_up)
				tty_termios_encode_baud_rate(termios,
								baud, baud);
			old = NULL;
			continue;
		}

		if (!hung_up) {
			if (baud <= min)
				tty_termios_encode_baud_rate(termios,
							min + 1, min + 1);
			else
				tty_termios_encode_baud_rate(termios,
							max - 1, max - 1);
		}
	}
	
	WARN_ON(1);
	return 0;
}

EXPORT_SYMBOL(uart_get_baud_rate);

unsigned int
uart_get_divisor(struct uart_port *port, unsigned int baud)
{
	unsigned int quot;

	if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
		quot = port->custom_divisor;
	else
		quot = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud);

	return quot;
}
Exemple #18
0
/**
 * ti_abb_init_timings() - setup ABB clock timing for the current platform
 * @dev:	device
 * @abb:	pointer to the abb instance
 *
 * Return: 0 if timing is updated, else returns error result.
 */
static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb)
{
	u32 clock_cycles;
	u32 clk_rate, sr2_wt_cnt_val, cycle_rate;
	const struct ti_abb_reg *regs = abb->regs;
	int ret;
	char *pname = "ti,settling-time";

	/* read device tree properties */
	ret = of_property_read_u32(dev->of_node, pname, &abb->settling_time);
	if (ret) {
		dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret);
		return ret;
	}

	/* ABB LDO cannot be settle in 0 time */
	if (!abb->settling_time) {
		dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
		return -EINVAL;
	}

	pname = "ti,clock-cycles";
	ret = of_property_read_u32(dev->of_node, pname, &clock_cycles);
	if (ret) {
		dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret);
		return ret;
	}
	/* ABB LDO cannot be settle in 0 clock cycles */
	if (!clock_cycles) {
		dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
		return -EINVAL;
	}

	abb->clk = devm_clk_get(dev, NULL);
	if (IS_ERR(abb->clk)) {
		ret = PTR_ERR(abb->clk);
		dev_err(dev, "%s: Unable to get clk(%d)\n", __func__, ret);
		return ret;
	}

	/*
	 * SR2_WTCNT_VALUE is the settling time for the ABB ldo after a
	 * transition and must be programmed with the correct time at boot.
	 * The value programmed into the register is the number of SYS_CLK
	 * clock cycles that match a given wall time profiled for the ldo.
	 * This value depends on:
	 * settling time of ldo in micro-seconds (varies per OMAP family)
	 * # of clock cycles per SYS_CLK period (varies per OMAP family)
	 * the SYS_CLK frequency in MHz (varies per board)
	 * The formula is:
	 *
	 *                      ldo settling time (in micro-seconds)
	 * SR2_WTCNT_VALUE = ------------------------------------------
	 *                   (# system clock cycles) * (sys_clk period)
	 *
	 * Put another way:
	 *
	 * SR2_WTCNT_VALUE = settling time / (# SYS_CLK cycles / SYS_CLK rate))
	 *
	 * To avoid dividing by zero multiply both "# clock cycles" and
	 * "settling time" by 10 such that the final result is the one we want.
	 */

	/* Convert SYS_CLK rate to MHz & prevent divide by zero */
	clk_rate = DIV_ROUND_CLOSEST(clk_get_rate(abb->clk), 1000000);

	/* Calculate cycle rate */
	cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate);

	/* Calulate SR2_WTCNT_VALUE */
	sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate);

	dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__,
		clk_get_rate(abb->clk), sr2_wt_cnt_val);

	ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, abb->setup_reg);

	return 0;
}
Exemple #19
0
static inline u16 ns_to_clock_divider(unsigned int ns)
{
	return count_to_clock_divider(
		DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000));
}
static int current_to_voltage(struct bcl_context *bcl, int ua)
{
	return DIV_ROUND_CLOSEST(ua * bcl->btm_uv_to_ua_denominator,
			bcl->btm_uv_to_ua_numerator);
}
Exemple #21
0
static int gt_clockevent_set_periodic(struct clock_event_device *evt)
{
    gt_compare_set(DIV_ROUND_CLOSEST(gt_clk_rate, HZ), 1);
    return 0;
}
static int voltage_to_current(struct bcl_context *bcl, int uv)
{
	return DIV_ROUND_CLOSEST(uv * bcl->btm_uv_to_ua_numerator,
			bcl->btm_uv_to_ua_denominator);
}
Exemple #23
0
static inline unsigned int clock_divider_to_ns(unsigned int divider)
{
	/* Period of the Rx or Tx clock in ns */
	return DIV_ROUND_CLOSEST((divider + 1) * 1000,
				 CX25840_IR_REFCLK_FREQ / 1000000);
}
static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
{
	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
	const struct lm25066_data *data = to_lm25066_data(info);
	int ret;

	if (page > 1)
		return -ENXIO;

	/* Map READ_VAUX into READ_VOUT register on page 1 */
	if (page == 1) {
		switch (reg) {
		case PMBUS_READ_VOUT:
			ret = pmbus_read_word_data(client, 0,
						   LM25066_READ_VAUX);
			if (ret < 0)
				break;
			/* Adjust returned value to match VOUT coefficients */
			switch (data->id) {
			case lm25066:
				/* VOUT: 4.54 mV VAUX: 283.2 uV LSB */
				ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
				break;
			case lm5064:
				/* VOUT: 4.53 mV VAUX: 700 uV LSB */
				ret = DIV_ROUND_CLOSEST(ret * 70, 453);
				break;
			case lm5066:
				/* VOUT: 2.18 mV VAUX: 725 uV LSB */
				ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
				break;
			}
			break;
		default:
			/* No other valid registers on page 1 */
			ret = -ENXIO;
			break;
		}
		goto done;
	}

	switch (reg) {
	case PMBUS_READ_IIN:
		ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN);
		break;
	case PMBUS_READ_PIN:
		ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN);
		break;
	case PMBUS_IIN_OC_WARN_LIMIT:
		ret = pmbus_read_word_data(client, 0,
					   LM25066_MFR_IIN_OC_WARN_LIMIT);
		break;
	case PMBUS_PIN_OP_WARN_LIMIT:
		ret = pmbus_read_word_data(client, 0,
					   LM25066_MFR_PIN_OP_WARN_LIMIT);
		break;
	case PMBUS_VIRT_READ_VIN_AVG:
		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN);
		break;
	case PMBUS_VIRT_READ_VOUT_AVG:
		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT);
		break;
	case PMBUS_VIRT_READ_IIN_AVG:
		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN);
		break;
	case PMBUS_VIRT_READ_PIN_AVG:
		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN);
		break;
	case PMBUS_VIRT_READ_PIN_MAX:
		ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK);
		break;
	case PMBUS_VIRT_RESET_PIN_HISTORY:
		ret = 0;
		break;
	default:
		ret = -ENODATA;
		break;
	}
done:
	return ret;
}
Exemple #25
0
static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
{
	return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16);
}
Exemple #26
0
static int m88ds3103_read_status(struct dvb_frontend *fe,
				 enum fe_status *status)
{
	struct m88ds3103_dev *dev = fe->demodulator_priv;
	struct i2c_client *client = dev->client;
	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
	int ret, i, itmp;
	unsigned int utmp;
	u8 buf[3];

	*status = 0;

	if (!dev->warm) {
		ret = -EAGAIN;
		goto err;
	}

	switch (c->delivery_system) {
	case SYS_DVBS:
		ret = regmap_read(dev->regmap, 0xd1, &utmp);
		if (ret)
			goto err;

		if ((utmp & 0x07) == 0x07)
			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
					FE_HAS_VITERBI | FE_HAS_SYNC |
					FE_HAS_LOCK;
		break;
	case SYS_DVBS2:
		ret = regmap_read(dev->regmap, 0x0d, &utmp);
		if (ret)
			goto err;

		if ((utmp & 0x8f) == 0x8f)
			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
					FE_HAS_VITERBI | FE_HAS_SYNC |
					FE_HAS_LOCK;
		break;
	default:
		dev_dbg(&client->dev, "invalid delivery_system\n");
		ret = -EINVAL;
		goto err;
	}

	dev->fe_status = *status;
	dev_dbg(&client->dev, "lock=%02x status=%02x\n", utmp, *status);

	/* CNR */
	if (dev->fe_status & FE_HAS_VITERBI) {
		unsigned int cnr, noise, signal, noise_tot, signal_tot;

		cnr = 0;
		/* more iterations for more accurate estimation */
		#define M88DS3103_SNR_ITERATIONS 3

		switch (c->delivery_system) {
		case SYS_DVBS:
			itmp = 0;

			for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) {
				ret = regmap_read(dev->regmap, 0xff, &utmp);
				if (ret)
					goto err;

				itmp += utmp;
			}

			/* use of single register limits max value to 15 dB */
			/* SNR(X) dB = 10 * ln(X) / ln(10) dB */
			itmp = DIV_ROUND_CLOSEST(itmp, 8 * M88DS3103_SNR_ITERATIONS);
			if (itmp)
				cnr = div_u64((u64) 10000 * intlog2(itmp), intlog2(10));
			break;
		case SYS_DVBS2:
			noise_tot = 0;
			signal_tot = 0;

			for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) {
				ret = regmap_bulk_read(dev->regmap, 0x8c, buf, 3);
				if (ret)
					goto err;

				noise = buf[1] << 6;    /* [13:6] */
				noise |= buf[0] & 0x3f; /*  [5:0] */
				noise >>= 2;
				signal = buf[2] * buf[2];
				signal >>= 1;

				noise_tot += noise;
				signal_tot += signal;
			}

			noise = noise_tot / M88DS3103_SNR_ITERATIONS;
			signal = signal_tot / M88DS3103_SNR_ITERATIONS;

			/* SNR(X) dB = 10 * log10(X) dB */
			if (signal > noise) {
				itmp = signal / noise;
				cnr = div_u64((u64) 10000 * intlog10(itmp), (1 << 24));
			}
			break;
		default:
			dev_dbg(&client->dev, "invalid delivery_system\n");
			ret = -EINVAL;
			goto err;
		}

		if (cnr) {
			c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
			c->cnr.stat[0].svalue = cnr;
		} else {
			c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
		}
	} else {
Exemple #27
0
static inline unsigned int clock_divider_to_freq(unsigned int divider,
						 unsigned int rollovers)
{
	return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ,
				 (divider + 1) * rollovers);
}
Exemple #28
0
static unsigned int get_clk_rate(struct platform_device *pdev, struct clk *sclk)
{
	struct s3c_platform_fb *pdata = pdev->dev.platform_data;
	struct s3cfb_lcd *lcd = (struct s3cfb_lcd *)pdata->lcd;
	struct s3cfb_lcd_timing *timing = &lcd->timing;
	u32 src_clk, vclk, div, rate;
	u32 vclk_limit, div_limit, fimd_div;

	src_clk = clk_get_rate(sclk);

	vclk = (lcd->freq *
		(timing->h_bp + timing->h_fp + timing->h_sw + lcd->width) *
		(timing->v_bp + timing->v_fp + timing->v_sw + lcd->height));

#ifdef CONFIG_FB_S5P_I80_LCD
	vclk *= 2;
#endif
	if (!vclk)
		vclk = src_clk;

	div = DIV_ROUND_CLOSEST(src_clk, vclk);

	if (lcd->freq_limit) {
		vclk_limit = (lcd->freq_limit *
			(timing->h_bp + timing->h_fp + timing->h_sw + lcd->width) *
			(timing->v_bp + timing->v_fp + timing->v_sw + lcd->height));

		div_limit = DIV_ROUND_CLOSEST(src_clk, vclk_limit);

		fimd_div = gcd(div, div_limit);

		if (div/fimd_div <= 16)
			div /= fimd_div;
	}

	if (!div) {
		dev_err(&pdev->dev, "div(%d) should be non-zero\n", div);
		div = 1;
	} else if (div > 16) {
		for (fimd_div = 2; fimd_div <= div; fimd_div++) {
			if ((div%fimd_div == 0) && (div/fimd_div <= 16))
				break;
		}
		div /= fimd_div;
	}

	div = (div > 16) ? 16 : div;
	rate = src_clk / div;

	if ((src_clk % rate) && (div != 1)) {
		div--;
		rate = src_clk / div;
		if (!(src_clk % rate))
			rate--;
	}

	dev_info(&pdev->dev, "src_clk=%d, vclk=%d, div=%d(%d), rate=%d\n",
		src_clk, vclk, DIV_ROUND_CLOSEST(src_clk, vclk), div, rate);

	return rate;
}
Exemple #29
0
static inline unsigned int lpf_count_to_us(unsigned int count)
{
	/* Duration of the Low Pass Filter rejection window in us */
	return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000);
}
static char *
minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
{
	const struct mcs_group *mg;
	unsigned int j, tp_max, tp_avg, eprob, tx_time;
	char htmode = '2';
	char gimode = 'L';
	u32 gflags;

	if (!mi->supported[i])
		return p;

	mg = &minstrel_mcs_groups[i];
	gflags = mg->flags;

	if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
		htmode = '4';
	else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
		htmode = '8';
	if (gflags & IEEE80211_TX_RC_SHORT_GI)
		gimode = 'S';

	for (j = 0; j < MCS_GROUP_RATES; j++) {
		struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
		static const int bitrates[4] = { 10, 20, 55, 110 };
		int idx = i * MCS_GROUP_RATES + j;
		unsigned int duration;

		if (!(mi->supported[i] & BIT(j)))
			continue;

		if (gflags & IEEE80211_TX_RC_MCS) {
			p += sprintf(p, "HT%c0  ", htmode);
			p += sprintf(p, "%cGI  ", gimode);
			p += sprintf(p, "%d  ", mg->streams);
		} else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
			p += sprintf(p, "VHT%c0 ", htmode);
			p += sprintf(p, "%cGI ", gimode);
			p += sprintf(p, "%d  ", mg->streams);
		} else {
			p += sprintf(p, "CCK    ");
			p += sprintf(p, "%cP  ", j < 4 ? 'L' : 'S');
			p += sprintf(p, "1 ");
		}

		*(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
		*(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
		*(p++) = (idx == mi->max_tp_rate[2]) ? 'C' : ' ';
		*(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' ';
		*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';

		if (gflags & IEEE80211_TX_RC_MCS) {
			p += sprintf(p, "  MCS%-2u", (mg->streams - 1) * 8 + j);
		} else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
			p += sprintf(p, "  MCS%-1u/%1u", j, mg->streams);
		} else {
			int r = bitrates[j % 4];

			p += sprintf(p, "   %2u.%1uM", r / 10, r % 10);
		}

		p += sprintf(p, "  %3u  ", idx);

		/* tx_time[rate(i)] in usec */
		duration = mg->duration[j];
		duration <<= mg->shift;
		tx_time = DIV_ROUND_CLOSEST(duration, 1000);
		p += sprintf(p, "%6u  ", tx_time);

		tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
		tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);

		p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u"
				"     %3u   %3u %-3u   "
				"%9llu   %-9llu\n",
				tp_max / 10, tp_max % 10,
				tp_avg / 10, tp_avg % 10,
				eprob / 10, eprob % 10,
				mrs->retry_count,
				mrs->last_success,
				mrs->last_attempts,
				(unsigned long long)mrs->succ_hist,
				(unsigned long long)mrs->att_hist);
	}

	return p;
}