Help: More than 4 SPI resources for each SPI master

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,
I am trying to enable a bunch of spi devices. In the stock form, the ngw100 can have 7, spi0.1-3 and spi1.0-3. I need more than this though.

I've modified kernel source code in 3 places, and have about 15 devices showing up under /dev.

These are the files I've modified:

***
-arch/avr32/mach-at32ap/at700x.c - added more pins (GPIO_PIN_PA(25), e.g.) to the spi0_pins[] and spi1_pins[].
-arch/avr32/boards/atngw100/setup.c - added more chip selects to the spi_board_info structures
-drivers/spi/stmel_spi.c - changed master->num_chipselect from 4 to 8
***

The problem:
I can see these devices under /dev. I can read from these devices in a userspace program, or cat /dev/spidev0.3, but doing the same for /dev/spidev0.4 to spidev0.7 results in the system crashing. It seems like I'm missing one last initialising step. The default pins are working, its the pins that I've added that are not.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is possible to reassign some of the other pins on the board as additional chip selects, correct? Or am I just dreaming that this is possible...?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can't see why it isn't possible, the chip select field is more than 2 bits long and the cs lines are just handled as gpio anyway. I haven't looked in to it at all to know what you haven't done yet sry :-)

-S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The SPI controller has four registers holding settings, and there's a mode bit which says that each register corresponds to a single chipselect. The SPI controller driver uses it that way.

However, the hardware supports another mode: where the four chipselect bits are demultiplexed externally (or, in this case, by GPIOs) and those four registers handle groups of devices (from memory, 0-3, 4-7, 8-11, 12-14) with some bit patterns disallowed (they're used to indicate nothing active).

You'd need to update the driver to understand about such banking, however it's done, since you wouldn't be able to use the hardware's scheme.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mojojojo wrote:
The SPI controller has four registers holding settings, and there's a mode bit which says that each register corresponds to a single chipselect. The SPI controller driver uses it that way.
The SPI controller can do that, but the Linux driver uses one mode register and activates the chip selects itself using gpio. See cs_activate() and cs_deactivate() at the top of atmel_spi.c

This is done to work around a dodgy assumption in old hardware and design bugs in new hardware as well as allowing the driver to support active-high chip-selecting despite the fact that the hardware doesn't believe that such devices exist.

As such I can't see a reason why you can't use just any old gpio pins.

-S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The Linux driver doesn't bother to update the CSR register before each transfer ... it's updated only by spi_setup(). CSR holds CPHA and CPOL settings from spi_device.mode, divisor from spi_device.max_speed_hz, and the wordsize from spi_device.bits_per_word.

The driver could need to update CSR before individual transfers if it supported the per-transfer overrides of max_speed_hz or bits_per_word ... but so far it doesn't.

So if there are going to be more spi_device nodes than CSR registers, the driver has to change so that CSR gets reprogrammed more often. Minimally before each message ... since the two devices sharing that CSR wouldn't generally have the same settings for all those device parameters.

Any old GPIO pins, sure. But that's not the issue I was showing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've been thinking about just using CSR0 for all transfers and (re)initialize it before each transfer. That will make it easier to support per-transfer overrides, you can use as many chipselects as you have GPIOs to spare, and we don't need to read-modify-write MR.

Well, perhaps not initialize it before _every_ transfer; that would prevent chaining. But before each transfer where any parameter changes.