Приветствую.
Случилось так, что появилась необходимость навесить на одну шину SPI 9 устройств. Вычитал в даташите на AT91SAM9260 что он поддерживает функцию Chip Select Decoding при использовании внешней логики для декодирования. В принципе это то, что мне надо. Но самим драйвером atmel_spi данная функция не поддерживается, к тому же там вмсето аппаратных NCPS используются GPIO-пины (из-за аппаратного бага в старой версии кристалла), что для меня даже лучше, т.к. мне надо иметь CS-пины на разъёме.
Недолго думая прилепил нужный функционал с помощью костылей и подпорок.
Проблема в том, что массив пинов указывается в at91sam9260_devices.c, а функции активации/деактивации cs_[de]activate находятся в /drivers/spi/atmel_spi.c. Пришлось туда тащить копию массива пинов, что, мягко выражаясь, нехорошо и неправильно. В эти функции передаётся только один пин, который надо дёрнуть. Через поле npcs_pin структуры atmel_spi_device, которая в свою очередь передаётся в поле controller_data структуры spi_device. В одном из списков рассылки увидел предложение использовать controller_data для передачи необходимых параметров, например, указателя на функцию в файле at91sam9260_devices.c, откуда имеется доступ к массиву пинов. А теперь главное, вопросы:
1) Как реализовать данную функцию _правильно_ и более универсально? Можно ли использовать controller_data для передачи, скажем, массива пинов?
2) В функции cs_deactivate есть комментарий, который меня сильно смущает:
Что с этим можно сделать?
3) В Kconfig-е сделал себе подменю "Chip Select Decoding" зависящее от SPI_ATMEL с конфигами "Enable Chip Selct Decoding on SPI bus 0" и "Enable Chip Selct Decoding on SPI bus 1". Проблема в том что на eMPU может быть разное кол-во SPI.
sasamy, это всё понятно, но у меня и так ног на разъёме не хватает. Мне нужен именно Chip Select Decoding и никак иначе. Нужно слепить плату расширения для существующего dev. board-а. Своя standalone железяка будет городится уже позже, а сейчас нужно уместится в пины на разъёме, хоть убейся.
Надо было сразу сказать что gpio не хватает - сейчас все ясно и опять же задача тривиально решается :) Вместо одного gpio в каждом элементе массива:
static const unsigned spi0_standard_cs[4] = { AT91_PIN_PA3, AT91_PIN_PC11, AT91_PIN_PC16, AT91_PIN_PC17 };
Если использовать стандартное поведение (которое используется в Linux по умолчанию) такая ситуация никогда не возникнет. Поясню расширенно :)
1 Все сообщения (spi_message http://lxr.free-electrons.com/source/include/linux/spi/spi.h#L452) не передаются в устройства немедленно а ставятся в очередь сообщений
2 Сообщения содержат от 1 до нескольких трансферов (spi_transfer http://lxr.free-electrons.com/source/include/linux/spi/spi.h#L365)
3 Стандартное поведение такое - перед передачей нового сообщения устройство активируется, после передачи трансфера - если трансфер последний в списке текущего сообщения, устройство деактивируется, если нет то устройство остается выбранным между трансферами текущего сообщения.
4 Стандартное поведение можно изменить - можно дективировать устройство после каждого трансфера и можно оставлять его активным после передаче последнего трансфера в сообщении.
И вот тут появляются грабли если на шине висит не одно устройство а несколько. Никто не гарантирует что все сообщения для одного устройства будут стоять в очереди подряд - в очереди они могут чередоваться для разных устройств - фрагментация неизбежна (при этом с трансферами одного сообщения такого никогда не будет - они передаются строго друг за другом для одного сообщения). При этом если в первом сообщении оставить после передачи устройство с активным CS а в очереди вопреки ожиданиям следующим будет сообщение для другого устройства - второе сообщение словят оба активных на данный момент устройства. Если использовать стандартное поведение - такого не произойдет, после передачи всех трансферов устройство деактивируется и не важно для кого второе сообщение - все пройдет четко по адресу.
sasamy, вот в том то вся и проблема. Есть у меня АЦП, для которого нужно держать CS активным после запуска конверсии, иначе нихрена он не сконвертит. То бишь для этой железки требуется держать CS активным на протяжении всего времени конверсии. АЦП точный 20-битный, при том весьма медленный, всего лишь около девяти сэмплов в секунду.
Что касается задумки с передачей всех пинов требующих активации в npcs_pin, то идея весьма хороша, это понятно и прозрачно. Но тут вылезают другие грабли.
1) В функции at91_add_device_spi нужно выставлять gpio используемые для cs как output'ы. (at91_set_gpio_output(cs_pin, 1);) Тут получается что в npcs_pin сразу несколько пинов, поэтому проще установить сразу все пины в массиве как выходы.
2) В функции atmel_spi_setup есть такой код:
Получается что один и тот же пин может быть использован для активации разных устройств, посему gpio_request будет возвращать -EBUSY. Пока я это решил тупо закомментив данный блок.
3) В связи с тем, что необходим режим удерживания CS, то в cs_deactivate лучше деактивировать все пины. А оттуда получить список всех пинов при таком подходе невозможно.
Просто в случае передачи в npcs_pin только тех пинов, которые нужно деактивировать, можно получить ситуацию когда будет активна передача к другому устройству, в итоге CS встанет чёрт знает в каое состояние.
1), 2) - я просто не стал расписывать подробней и не ошибся - вы сами все поняли :)
Вот это не распарсил честно говоря - помоему это деление на ноль :) Вообще если есть такое медленное устройство вешать его на общую шину из 9 устройств - утопия, он будет ловить все сообщения на шине так как будет по сути постоянно активен - если вас это устраивает тогда сажаете его CS аппаратно на 0 и дело с концом, к тому же с CS decode в общем случае невозможно как вы правильно заметили держать одно устройство постоянно активным - там черте что получится.
sasamy, на самом деле не всё так страшно, этот АЦП должен отрабатывать лишь один раз при включении железяки, для того чтобы получить аддитивную составляющую погрешности при измерениях. Там можно и какой-нибудь костыль прилепить в виде натуральной задержки после запуска конверсии.
Я речь вёл про то, что если уж и сложится ситуация при которой cs_deactivate таки отработает уже после запуска cs_activate для железяки с другим CS, то лучше чтобы в cs_deactivate все пины используемы для выбора CS ставились в неактивное положение (никакое устройство не выбрано).
Вам видней что вам нужно, спорить/доказывать я ничего не собирался - описал ситуацию.
Такая ситуация возможна только если настроить нестандартное поведение - после передачи сообщения оставлять устройство выбранным, если вы это решили использовать на общей шине из 9 устройств - СС3Б, в остальных случаях деактивировать все устройства на шине после каждого сообщения нет никакой надобности. Если по каким-то причинам вас не устраивает то что я описал - передавайте вместо cs указатель на свою ф-цию activate/deactivate как хотели сделать изначально - я ничего не имею против - вполне годный вариант :)