SPI clk glitch

1 post / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

[EDIT: DISCLAIMER: We were all running on very little sleep at the time we were doing all this. I'm very open to the idea that it's something simple and/or I should rtfm with working eyes ;)]

Hi all,

Trying to use SPI to control an LCD of ours has led to some really very annoying problems. We were only sending one 9-bit word. The LCD requires the SPI to communicate in mode 3 in which mode the clock line idles high, latches on rising edge.

Under both uboot an Linux, the problem is that the clock transitioning from low to high to ready itself for mode 3 transmission is done _while chip select is low_. This of course leads to the LCD latching the wrong 9 bits.

Under Linux, all lines were nice and clean and what you'd expect, except the chip select was a stupid amount larger than the actual data, including the idle clock transition.

In u-boot, the chip select was just doing weird things.

Traces attached, please forgive the poor quality.

We didn't end up resolving the issue under Linux but we got uboot to correctly shift the data by sending a dummy byte to an unpopulated chip select line in mode 3. The clock was then idling in the correct state and there were no unfortunate transitions.

Any insight as to why the chip select lines are missing the mark? Don't know whether the code below shows it, but in all cases the DLYBCT and DLYBS were set to 0 in the appropriate CSRx.

As you can see, the Linux code is no more or less than a rip from ltv530qv.c:

static struct spi_board_info spi_board_info[] __initdata = {
	{
		.modalias       = "lcd",
		.controller_data = (void *)GPIO_PIN_PA(4),
		.max_speed_hz   = 1000000,
		.bus_num        = 0,
		.chip_select    = 1,
		.mode		= SPI_MODE_3,
	},
}
    .
    .
    .

@lcd_probe():
	spi->mode = SPI_MODE_3;
	spi->bits_per_word = 9;
	spi->max_speed_hz = 1000000;
	ret = spi_setup(spi);
    .
    .
    .

@lcd_write_reg():

	struct spi_message msg;

	struct spi_transfer value_xfer = {
		.len		= 2,	/*2 bytes, 9 bits*/
		.cs_change	= 1,
	};

	spi_message_init(&msg);

	lcd->buffer[0] = val;
	value_xfer.tx_buf = lcd->buffer;
	spi_message_add_tail(&value_xfer, &msg);

	return spi_sync(lcd->spi, &msg);

#define write_lcd(_spi, val)					\
	do {							\
		ret = lcd_write_reg(_spi, val);		\
		if (ret)					\
			goto out;				\
	} while (0)
            .
            .
            .

	write_lcd(lcd,  17);

The code for uboot is pretty much a rip from the ltv350qv driver for it too. The working version is

vvv same struct for cs0 and cs1
static struct spi_options_t cs1 = {
	.reg		= 1,
	.baudrate	= 3000000,
	.bits		= 9,
	.spck_delay	= 0,
	.trans_delay	= 0,
	.stay_act	= 0,
	.spi_mode	= 3,
};
           .
           .
           .
	spi_select_chip(0);
	spi_write(&clear[0], 1, 0, 0);
	spi_unselect_chip(0);

	spi_select_chip(1);
	spi_write(&clear[0], 1, 0, 0);
	spi_unselect_chip(1);

The uboot trace below was captured when the first stanza regarding CS0 is left out.

Many thanks to anyone who made it this far ;)
-S.

Attachment(s):