Linuxにおけるデバイスドライバーについて
NVIDIA社のJetson nano (tegra210-p3448-0002-p3449-0000-b00)
を260pin-SODIMMコネクタで接続して使うボードの開発をC言語で進めています。
私は組み込みソフトでC言語、OSレスでの開発は慣れているのですがLinuxは初めてです。
使用しているLinuxのディストリビューションはUbuntuで、バージョンは18.04、64bit版です。
ハード構成はひとまず「お試し」という感じでSPI0の先にmicrochip社の93LC56Bを接続して
読み書きをしようとしています。
ピン配置は次の通りです。
- MOSI:89pin
- MISO:93pin
- SCK:91pin
- CS0:95pin
ピン配置の設定はNVIDIA社のページからダウンロードできる「NV_Jetson_Nano_Module_Pinmux_Config_Template.xlsm」で設定を行い、データツリーの設定ファイルを自動生成させています。(なのでピン関係の設定は合っていると思っています)
「Linuxでは、デバイスはファイルとしてアクセスする」のが原則ということで、open、write、read等の関数からアクセスできるドライバー開発を目指しています。
普段レジスタを叩いて制御してた私としては「簡単だろう」と高をくくっていたのですが、LinuxによってCPU(SoC)が見えない状態で戸惑っている状況です。
現状困っていること
Linuxの仕組み自体を理解していないので的を射ていないかも知れませんが、大きく悩んでいるのは次の3点です。
0. 「/dev」フォルダの下にデバイスが現れてくれないこと(EEPROMも、SPIも)
0. デバイスツリーの設定によってEEPROMドライバだけがロードされたり、されなかったりすること
0. EEPROMドライバーはGitにあった型番の近いソースを改変しようとしているが、それがそもそも意図に沿っているかどうかがわからないこと
デバイスツリーのDTSIファイルが膨大なため、SPIとEEPROMに関連していそうなところだけ触っています。
上記1と2はデバイスツリーの記述を変えると挙動が変わります。
試したこと
たくさんのDTSIファイルがありますが、いじっているのはtegra210-porg-p3448-common.dtsiとtegra210-porg-eeprom-manager.dtsiだけです。
ケース1:上記2で、EEPROMドライバだけが自動ロードされる(lsmodコマンドのリストに表れる)
dtsi
1 /* common.dtsi */ 2 ... 3 spi@7000d400 { 4 status = "okay"; 5 compatible = "spidev"; 6 }; 7 ... 8---------------------------------------------------------------------------------- 9 /* eeprom-manager.dtsi */ 10/ { 11 eeprom-manager { 12 data-size = <0x100>; 13 eep@0 { 14 spi = <&spi0>; 15 status = "okay"; 16 compatible = "microchip,93xx56"; 17 }; 18 }; 19};
ケース2:上記2で、EEPROMドライバが自動ロードされない(lsmodコマンドのリストには表れない)
dtsi
1 /* common.dtsi */ 2 ... 3 spi@7000d400 { 4 status = "okay"; 5 compatible = "spidev"; 6 eep@0 { 7 spi = <&spi0>; 8 status = "okay"; 9 compatible = "microchip,93xx56"; 10 }; 11 }; 12 ... 13---------------------------------------------------------------------------------- 14 /* eeprom-manager.dtsi */ 15/ { 16 eeprom-manager { 17 data-size = <0x100>; 18 eep@0 { 19 spi = <&spi0>; 20 status = "disable"; 21 compatible = "microchip,93xx56"; 22 }; 23 };
ドライバのソースはほぼダウンロードしたものそのままです。いずれファイルとして扱えるようになったら、open関数等を実装しようと思っている物です。probe関数で必ず通るところにprintkを入れてもdmesgに何も出てきません。
c
1 ... 2static const struct of_device_id eeprom_93xx56_of_table[] = { 3 { .compatible = "microchip,eeprom-93xx56", }, 4 {} 5}; 6MODULE_DEVICE_TABLE(of, eeprom_93xx56_of_table); 7 8static int eeprom_93xx46_probe_dt(struct spi_device *spi) 9{ 10 const struct of_device_id *of_id = 11 of_match_device(eeprom_93xx46_of_table, &spi->dev); 12 struct device_node *np = spi->dev.of_node; 13 struct eeprom_93xx46_platform_data *pd; 14 u32 tmp; 15 int ret; 16 17 pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL); 18 if (!pd) 19 return -ENOMEM; 20 21 ret = of_property_read_u32(np, "data-size", &tmp); 22 if (ret < 0) { 23 dev_err(&spi->dev, "data-size property not found\n"); 24 return ret; 25 } 26 27 if (tmp == 8) { 28 pd->flags |= EE_ADDR8; 29 } else if (tmp == 16) { 30 pd->flags |= EE_ADDR16; 31 } else { 32 dev_err(&spi->dev, "invalid data-size (%d)\n", tmp); 33 return -EINVAL; 34 } 35 36 if (of_property_read_bool(np, "read-only")) 37 pd->flags |= EE_READONLY; 38 39 pd->select = devm_gpiod_get_optional(&spi->dev, "select", 40 GPIOD_OUT_LOW); 41 if (IS_ERR(pd->select)) 42 return PTR_ERR(pd->select); 43 44 pd->prepare = select_assert; 45 pd->finish = select_deassert; 46 gpiod_direction_output(pd->select, 0); 47 48 if (of_id->data) { 49 const struct eeprom_93xx46_devtype_data *data = of_id->data; 50 51 pd->quirks = data->quirks; 52 } 53 54 spi->dev.platform_data = pd; 55 56 return 0; 57} 58 59static int eeprom_93xx46_probe(struct spi_device *spi) 60{ 61 struct eeprom_93xx46_platform_data *pd; 62 struct eeprom_93xx46_dev *edev; 63 int err; 64 65 if (spi->dev.of_node) { 66 err = eeprom_93xx46_probe_dt(spi); 67 if (err < 0) 68 return err; 69 } 70 71 pd = spi->dev.platform_data; 72 if (!pd) { 73 dev_err(&spi->dev, "missing platform data\n"); 74 return -ENODEV; 75 } 76 77 edev = devm_kzalloc(&spi->dev, sizeof(*edev), GFP_KERNEL); 78 if (!edev) 79 return -ENOMEM; 80 81 if (pd->flags & EE_ADDR8) 82 edev->addrlen = 7; 83 else if (pd->flags & EE_ADDR16) 84 edev->addrlen = 6; 85 else { 86 dev_err(&spi->dev, "unspecified address type\n"); 87 return -EINVAL; 88 } 89 90 mutex_init(&edev->lock); 91 92 edev->spi = spi; 93 edev->pdata = pd; 94 95 edev->size = 128; 96 edev->nvmem_config.type = NVMEM_TYPE_EEPROM; 97 edev->nvmem_config.name = dev_name(&spi->dev); 98 edev->nvmem_config.dev = &spi->dev; 99 edev->nvmem_config.read_only = pd->flags & EE_READONLY; 100 edev->nvmem_config.root_only = true; 101 edev->nvmem_config.owner = THIS_MODULE; 102 edev->nvmem_config.compat = true; 103 edev->nvmem_config.base_dev = &spi->dev; 104 edev->nvmem_config.reg_read = eeprom_93xx46_read; 105 edev->nvmem_config.reg_write = eeprom_93xx46_write; 106 edev->nvmem_config.priv = edev; 107 edev->nvmem_config.stride = 4; 108 edev->nvmem_config.word_size = 1; 109 edev->nvmem_config.size = edev->size; 110 111 edev->nvmem = devm_nvmem_register(&spi->dev, &edev->nvmem_config); 112 if (IS_ERR(edev->nvmem)) 113 return PTR_ERR(edev->nvmem); 114 115 dev_info(&spi->dev, "%d-bit eeprom %s\n", 116 (pd->flags & EE_ADDR8) ? 8 : 16, 117 (pd->flags & EE_READONLY) ? "(readonly)" : ""); 118 119 if (!(pd->flags & EE_READONLY)) { 120 if (device_create_file(&spi->dev, &dev_attr_erase)) 121 dev_err(&spi->dev, "can't create erase interface\n"); 122 } 123 124 spi_set_drvdata(spi, edev); 125 return 0; 126} 127 128static int eeprom_93xx46_remove(struct spi_device *spi) 129{ 130 struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi); 131 132 if (!(edev->pdata->flags & EE_READONLY)) 133 device_remove_file(&spi->dev, &dev_attr_erase); 134 135 return 0; 136} 137 138static struct spi_driver eeprom_93xx56_driver = { 139 .driver = { 140 .name = "93xx56", 141 .of_match_table = of_match_ptr(eeprom_93xx46_of_table), 142 }, 143 .probe = eeprom_93xx46_probe, 144 .remove = eeprom_93xx46_remove, 145}; 146 147module_spi_driver(eeprom_93xx56_driver); 148 149MODULE_LICENSE("GPL"); 150MODULE_DESCRIPTION("Driver for 93xx56 EEPROMs"); 151MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); 152MODULE_ALIAS("spi:93xx56");
結局のところ
そもそもLinuxの知識が乏しいことと、デバイスツリーの理解も進んでいないことから、どこまでが正しいのかの線引きすらできていない状態です。デバイスツリー内のcompatibleの記述と、ドライバーのソース内のof_device_tableにある.compatibleメンバーの記述が合致していれば、kernelが勝手にドライバーを読み込んでくれるものと思っていました。
「/dev」フォルダ下にデバイスが現れ、それをファイルとして扱うことにこだわっていいのか、それとも別の方法があるのかも含めてご教示いただければと思います。
よろしくお願いします。
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。