Использование writel для записи 4 бита в адрес памяти с ioremap-ed

Я новичок в программировании ядра и теперь пытаюсь записать некоторые значения в 32-битный регистр GPIO в драйвере устройства. Ввод/вывод выполняется ioremap() по адресу памяти. Проблема в том, что я не знаю, как writel()/writeb()/writew() записывает биты в адреса.

В документах поставщика говорится, что регистр находится на 0xE5200000. Биты, которые я должен записать, это [0:3] бит, а оставшиеся 28 бит ([4:31] бит) оставить нулями.

Это часть кода в драйвере устройства, который я написал до сих пор:

#define TCON_ADDR 0xE250000 // The address as provided by the vendor
static void *TIMER_CON_ADDR;
// I have to map and write to the address when the device is opened
static int on_dev_open(struct inode *inode, struct file *file) {
    unsigned int data;
    TIMER_CON_ADDR = ioremap(TCON_ADDR, 4); // Map the [0:4] bits to TIMER_CON_ADDR
    data = 4; // 0100 in binary
    writel(data, TIMER_CON_ADDR); // Write 0100 to TIMER_CON_ADDR
    return 0;
}

Приведенный выше код может показаться вам откровенной тарабарщиной, но я не знаком с write(l|w|b) и ioremap().

Итак, мои вопросы:

  1. Правильно ли я сопоставил биты [0:4] с TIMER_CON_ADDR?
  2. Если нет, то как мне правильно их сопоставить?
  3. После того, как я правильно сопоставил 4 бита, как мне использовать любую из функций write(1|w|b) для записи битов (0100) в TIMER_CON_ADDR в правильном порядке?
  4. Что делает write(l|w|b) под капотом для записи битов?
  5. Есть ли какая-либо информация, которую я пропустил / ошибся?

Спасибо за вашу помощь заранее.


person Chung Lun Yuen    schedule 02.04.2017    source источник
comment
Это существующий драйвер или вы создаете его с нуля? В вашем коде много проблем: вы пропустили 0 в адресе, вы пропустили iounmap() вызов. В драйверах все вышеперечисленное делается совсем по-другому, хотя и с использованием тех же writel(), iomap()/iounmap() и им подобных.   -  person 0andriy    schedule 06.04.2017
comment
Я создаю драйвер с нуля для платы, которую я купил онлайн в Китае, чтобы изучить программирование ядра. Действительно, мне было нелегко искать стандартную документацию для функций и макросов пространства ядра. Я обнаружил, что документация, которую я искал, находится в источниках после их поиска. Все еще не понял внутреннюю работу ядра (например, какой смысл указывать размер сопоставленного адреса в iomap()? все равно будет писать целый байт/слово/длинное слово и т. д. и т. д..), но я думаю, что я Я приближаюсь каждый раз, когда я пытаюсь.   -  person Chung Lun Yuen    schedule 06.04.2017
comment
Лучше просто поискать аналогичный драйвер для другого оборудования, чтобы понять. Какое оборудование вы пытаетесь запрограммировать?   -  person 0andriy    schedule 06.04.2017
comment
@0andriy зуммер с ШИМ-регулировкой   -  person Chung Lun Yuen    schedule 06.04.2017
comment
Итак, в основном вам нужно искать в drivers/pwm/. Я сомневаюсь, что это именно то, что вам нужно, было бы лучше, если бы вы поделились URL-адресом спецификаций оборудования (техническое описание).   -  person 0andriy    schedule 06.04.2017


Ответы (1)


  1. Правильно ли я сопоставил биты [0:4] с TIMER_CON_ADDR?

нет, вы пишете 32 бита, writel пишете 4 байта, 4 * 8 = 32 бита

  1. Если нет, то как мне правильно их сопоставить?

Невозможно отобразить 4 бита, минимум 8 бит = 1 байт, но если вы работаете с 32-битным регистром, вам нужно отобразить 32 бита = 4 байта. Также не забывайте проверять и обрабатывать ошибки.

  1. После того, как я правильно сопоставил 4 бита, как мне использовать любую из функций write(1|w|b) для записи битов (0100) в TIMER_CON_ADDR в правильном порядке?

вам нужно использовать readl, ядро ​​полно примеров, просто запустите grep внутри подкаталога drivers исходного дерева ядра linux. Общая идея чтения/записи:

u32 reg = readl(TIMER_CON_ADDR);
reg &= ~0xfu;
reg |= 4;
writel(reg, TIMER_CON_ADDR);
  1. Что делает write(l|w|b) под капотом для записи битов?

посмотрите на исходный код, это просто простые функции C, например:

static inline void __raw_writel(u32 value, volatile void __iomem *addr)
{
    *(volatile u32 __force *)addr = value;
}

основная идея заключается в том, чтобы сообщить компилятору, что он не должен удалять чтение/запись вашей памяти

  1. Есть ли какая-либо информация, которую я пропустил / ошибся?

почитайте исходники подобных драйверов, там уже есть почти все решения для таких простых драйверов.

person fghj    schedule 02.04.2017
comment
1. Почему нет? Если предполагается, что ввод-вывод должен быть 32-разрядным, то writel() является правильной командой для этого (при условии небольшого порядка следования байтов). - person 0andriy; 06.04.2017
comment
@0andriy Из-за того, что, насколько я понимаю, автор хочет отображать именно 4 бита, а не 4 байта. - person fghj; 06.04.2017
comment
Вы ошиблись, потому что ОП не знает этих деталей. Это зависит исключительно от аппаратного обеспечения (некоторые разрешают только 8-битный доступ, некоторые 32-битный, некоторые что угодно). Если вы читаете первое предложение, там упоминается 32-бит, поэтому я предполагаю, что аппаратному обеспечению нужны 32-битные средства доступа. - person 0andriy; 06.04.2017
comment
@ 0andriy Но комментарий в коде говорит, что // Map the [0:4] bits to TIMER_CON_ADDR, поэтому только автор вопроса знает ответ. - person fghj; 06.04.2017
comment
Нет, в регистр нельзя записать только 5 бит. Это невозможно. Прочтите мой комментарий выше внимательно. ОП не понимает этих деталей. - person 0andriy; 06.04.2017
comment
@0andriy Я знаю, 2. объясняет, почему. Я не понимаю вашу мысль, вы говорите, что автор думает, но если вы не автор, как вы можете это делать? - person fghj; 06.04.2017
comment
Опять же, я хочу сказать, что ваш ответ на 1. неверен. Я предполагаю, что доступ к регистрам должен быть 32-битным, основываясь на информации в посте. - person 0andriy; 06.04.2017
comment
О, спасибо, помог. То, что сказал @user1034749, было правильным - я должен посмотреть на эти модули ядра char. Они предлагают примеры того, как взаимодействовать с оборудованием. Я должен readl() из регистра изменить биты на то, что я хочу, и записать значения обратно в аппаратное обеспечение в зависимости от того, какую функцию - write(l|b|w) - я использую. - person Chung Lun Yuen; 06.04.2017
comment
@ user1034749 @0andriy Я просто предположил, что был прав в комментарии // Map the [0:4] bits to TIMER_CON_ADDR, чтобы избежать длинных комментариев. Не думал, что вызовет путаницу. Извиняюсь. - person Chung Lun Yuen; 06.04.2017
comment
3. git grep намного лучше и быстрее простого grep. - person 0andriy; 06.04.2017
comment
@ChungLunYuen, это было ясно, но ответ на 1 здесь неверен. Вы поступили правильно, вам просто нужно, как объяснено, прочитать значение, изменить биты, которые вы хотите изменить, и записать обратно. Запись 32-битного регистра производится точно так же, как и у вас, т.е. writel(). - person 0andriy; 06.04.2017
comment
@ 0andriy Я думаю, он пытается сказать мне, что я не могу просто writel() 4 бита, подчеркнув, что writel() на самом деле записывает 32 бита в оборудование. - person Chung Lun Yuen; 06.04.2017