From c431b61900d5fc2ef32c6266e01d0a408e1f2cce Mon Sep 17 00:00:00 2001 From: dgjames <1943357252@qq.com> Date: Fri, 14 Mar 2025 11:04:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0rtt=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .config | 152 +- .vscode/c_cpp_properties.json | 486 ++- .vscode/project.json | 519 +++ applications/init.c | 6 +- board/ports/fal/fal_spi_flash_sfud_port.c | 2 +- board/ports/spi_flash_init.c | 4 +- libraries/HAL_Drivers/drivers/drv_pwm.c | 2 +- libraries/HAL_Drivers/drivers/drv_sdio.h | 4 +- rt-thread/README.md | 3 +- rt-thread/components/dfs/Kconfig | 22 +- rt-thread/components/dfs/dfs_v1/SConscript | 10 +- .../dfs/dfs_v1/filesystems/iso9660/SConscript | 11 + .../dfs_v1/filesystems/iso9660/dfs_iso9660.c | 698 ++++ .../dfs_v1/filesystems/iso9660/dfs_iso9660.h | 16 + .../dfs/dfs_v1/filesystems/ramfs/dfs_ramfs.c | 6 +- rt-thread/components/dfs/dfs_v1/src/dfs.c | 122 +- .../components/dfs/dfs_v1/src/dfs_file.c | 58 +- rt-thread/components/dfs/dfs_v1/src/dfs_fs.c | 12 +- .../components/dfs/dfs_v1/src/dfs_posix.c | 60 +- .../dfs_v2/filesystems/cromfs/dfs_cromfs.c | 6 +- .../dfs_v2/filesystems/cromfs/dfs_cromfs.h | 3 + .../dfs/dfs_v2/filesystems/devfs/devfs.c | 55 +- .../dfs/dfs_v2/filesystems/devfs/devtmpfs.c | 50 +- .../dfs/dfs_v2/filesystems/procfs/README.md | 166 + .../dfs/dfs_v2/filesystems/procfs/SConscript | 11 + .../dfs/dfs_v2/filesystems/procfs/proc.c | 733 ++++ .../dfs/dfs_v2/filesystems/procfs/proc.h | 75 + .../dfs_v2/filesystems/procfs/proc_cmdline.c | 53 + .../dfs_v2/filesystems/procfs/proc_cpuinfo.c | 86 + .../dfs_v2/filesystems/procfs/proc_devices.c | 307 ++ .../filesystems/procfs/proc_filesystems.c | 85 + .../dfs_v2/filesystems/procfs/proc_loadavg.c | 41 + .../dfs_v2/filesystems/procfs/proc_meminfo.c | 66 + .../dfs_v2/filesystems/procfs/proc_mounts.c | 101 + .../dfs/dfs_v2/filesystems/procfs/proc_net.c | 107 + .../filesystems/procfs/proc_partitions.c | 215 ++ .../dfs/dfs_v2/filesystems/procfs/proc_pid.c | 449 +++ .../dfs/dfs_v2/filesystems/procfs/proc_self.c | 66 + .../dfs/dfs_v2/filesystems/procfs/proc_stat.c | 114 + .../dfs/dfs_v2/filesystems/procfs/proc_tty.c | 100 + .../dfs_v2/filesystems/procfs/proc_uptime.c | 38 + .../dfs_v2/filesystems/procfs/proc_version.c | 45 + .../dfs/dfs_v2/filesystems/procfs/procfs.c | 447 +++ .../dfs/dfs_v2/filesystems/procfs/procfs.h | 19 + .../dfs/dfs_v2/filesystems/ptyfs/ptyfs.c | 4 +- .../dfs/dfs_v2/filesystems/romfs/dfs_romfs.c | 1 + .../dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c | 35 +- .../dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h | 4 +- .../components/dfs/dfs_v2/include/dfs_fs.h | 33 + .../components/dfs/dfs_v2/include/dfs_mnt.h | 9 + .../dfs/dfs_v2/include/dfs_pcache.h | 1 + .../dfs/dfs_v2/include/dfs_seq_file.h | 2 +- .../components/dfs/dfs_v2/include/dfs_vfs.h | 50 + rt-thread/components/dfs/dfs_v2/src/dfs.c | 10 +- .../components/dfs/dfs_v2/src/dfs_file.c | 5 + rt-thread/components/dfs/dfs_v2/src/dfs_fs.c | 60 +- rt-thread/components/dfs/dfs_v2/src/dfs_mnt.c | 35 +- .../components/dfs/dfs_v2/src/dfs_pcache.c | 51 +- .../components/dfs/dfs_v2/src/dfs_posix.c | 70 +- .../components/dfs/dfs_v2/src/dfs_seq_file.c | 2 +- rt-thread/components/drivers/Kconfig | 13 + rt-thread/components/drivers/ata/Kconfig | 22 + rt-thread/components/drivers/ata/SConscript | 21 + rt-thread/components/drivers/ata/ahci-pci.c | 206 ++ rt-thread/components/drivers/ata/ahci.c | 896 +++++ .../drivers/audio/{audio.c => dev_audio.c} | 0 .../audio/{audio_pipe.c => dev_audio_pipe.c} | 32 +- .../audio/{audio_pipe.h => dev_audio_pipe.h} | 6 +- rt-thread/components/drivers/block/Kconfig | 7 + rt-thread/components/drivers/block/SConscript | 23 + rt-thread/components/drivers/block/blk.c | 573 +++ rt-thread/components/drivers/block/blk_dev.c | 297 ++ rt-thread/components/drivers/block/blk_dev.h | 49 + rt-thread/components/drivers/block/blk_dfs.c | 274 ++ rt-thread/components/drivers/block/blk_dfs.h | 23 + .../components/drivers/block/blk_partition.c | 154 + .../components/drivers/block/blk_partition.h | 22 + .../drivers/block/partitions/Kconfig | 12 + .../drivers/block/partitions/SConscript | 18 + .../components/drivers/block/partitions/dfs.c | 55 + .../components/drivers/block/partitions/efi.c | 738 ++++ .../components/drivers/block/partitions/efi.h | 141 + .../drivers/can/{can.c => dev_can.c} | 4 +- rt-thread/components/drivers/clk/Kconfig | 4 + rt-thread/components/drivers/clk/clk.c | 251 +- rt-thread/components/drivers/core/bus.c | 4 +- rt-thread/components/drivers/core/device.c | 20 +- rt-thread/components/drivers/core/dm.c | 131 + rt-thread/components/drivers/core/platform.c | 16 +- .../components/drivers/core/platform_ofw.c | 61 +- rt-thread/components/drivers/dma/Kconfig | 10 + rt-thread/components/drivers/dma/SConscript | 15 + rt-thread/components/drivers/dma/dma.c | 589 +++ rt-thread/components/drivers/dma/dma_pool.c | 691 ++++ .../components/drivers/hwtimer/hwtimer.c | 11 +- rt-thread/components/drivers/i2c/Kconfig | 414 ++- rt-thread/components/drivers/i2c/SConscript | 10 +- .../i2c/{i2c-bit-ops.c => dev_i2c_bit_ops.c} | 0 .../drivers/i2c/{i2c_bus.c => dev_i2c_bus.c} | 2 +- .../i2c/{i2c_core.c => dev_i2c_core.c} | 2 +- .../drivers/i2c/{i2c_dev.c => dev_i2c_dev.c} | 0 .../drivers/i2c/{i2c_dm.c => dev_i2c_dm.c} | 2 +- .../i2c/{soft_i2c.c => dev_soft_i2c.c} | 12 +- rt-thread/components/drivers/iio/SConscript | 15 + rt-thread/components/drivers/iio/iio.c | 71 + .../components/drivers/include/drivers/adc.h | 102 +- .../components/drivers/include/drivers/ahci.h | 397 +++ .../components/drivers/include/drivers/blk.h | 87 + .../components/drivers/include/drivers/clk.h | 6 + .../drivers/include/drivers/core/dm.h | 20 + .../drivers/include/drivers/core/master_id.h | 94 + .../drivers/include/drivers/cputime.h | 4 + .../components/drivers/include/drivers/dac.h | 96 +- .../include/drivers/{alarm.h => dev_alarm.h} | 6 +- .../include/drivers/{audio.h => dev_audio.h} | 8 +- .../include/drivers/{can.h => dev_can.h} | 203 +- .../drivers/include/drivers/dev_i2c.h | 401 +++ .../{i2c-bit-ops.h => dev_i2c_bit_ops.h} | 4 +- .../drivers/{i2c_dm.h => dev_i2c_dm.h} | 6 +- .../include/drivers/{mmc.h => dev_mmc.h} | 4 +- .../{mmcsd_core.h => dev_mmcsd_core.h} | 6 +- .../include/drivers/{pin.h => dev_pin.h} | 188 +- .../drivers/include/drivers/dev_pwm.h | 209 ++ .../include/drivers/{rtc.h => dev_rtc.h} | 122 +- .../include/drivers/{sd.h => dev_sd.h} | 4 +- .../include/drivers/{sdio.h => dev_sdio.h} | 4 +- .../drivers/{serial.h => dev_serial.h} | 139 +- .../drivers/{serial_v2.h => dev_serial_v2.h} | 172 +- .../drivers/include/drivers/dev_spi.h | 633 ++++ .../include/drivers/{touch.h => dev_touch.h} | 147 +- .../drivers/{watchdog.h => dev_watchdog.h} | 6 +- .../components/drivers/include/drivers/dma.h | 234 ++ .../components/drivers/include/drivers/gpt.h | 132 - .../components/drivers/include/drivers/i2c.h | 136 - .../drivers/include/drivers/i2c_dev.h | 44 - .../components/drivers/include/drivers/iio.h | 17 + .../components/drivers/include/drivers/led.h | 57 + .../drivers/include/drivers/mailbox.h | 77 + .../components/drivers/include/drivers/misc.h | 60 + .../drivers/include/drivers/mmcsd_card.h | 2 +- .../drivers/include/drivers/mmcsd_host.h | 1 + .../components/drivers/include/drivers/nvme.h | 899 +++++ .../components/drivers/include/drivers/ofw.h | 7 +- .../drivers/include/drivers/ofw_fdt.h | 1 + .../drivers/include/drivers/ofw_io.h | 21 + .../components/drivers/include/drivers/pci.h | 604 ++++ .../drivers/include/drivers/pci_endpoint.h | 203 ++ .../drivers/include/drivers/pci_msi.h | 189 + .../components/drivers/include/drivers/phy.h | 168 +- .../drivers/include/drivers/phy_mdio.h | 43 - .../components/drivers/include/drivers/phye.h | 91 + .../components/drivers/include/drivers/pic.h | 1 + .../drivers/include/drivers/platform.h | 10 +- .../components/drivers/include/drivers/pm.h | 40 +- .../drivers/include/drivers/regulator.h | 153 + .../drivers/include/drivers/reset.h | 82 + .../drivers/include/drivers/rt_drv_pwm.h | 66 - .../components/drivers/include/drivers/scsi.h | 461 +++ .../drivers/include/drivers/sensor.h | 15 +- .../drivers/include/drivers/sensor_v2.h | 2 +- .../drivers/include/drivers/serial_bypass.h | 69 + .../components/drivers/include/drivers/spi.h | 373 -- .../drivers/include/drivers/syscon.h | 35 + .../drivers/include/drivers/thermal.h | 205 ++ .../components/drivers/include/drivers/wlan.h | 10 +- .../drivers/include/dt-bindings/phye/phye.h | 24 + .../include/dt-bindings/thermal/thermal.h | 13 + .../drivers/include/ipc/completion.h | 18 +- .../drivers/include/ipc/workqueue.h | 4 +- .../components/drivers/include/rtdevice.h | 105 +- .../components/drivers/ipc/completion_comm.c | 22 + .../components/drivers/ipc/completion_mp.c | 36 +- .../components/drivers/ipc/completion_up.c | 22 + rt-thread/components/drivers/ipc/workqueue.c | 176 +- rt-thread/components/drivers/led/Kconfig | 15 + rt-thread/components/drivers/led/SConscript | 18 + rt-thread/components/drivers/led/led-gpio.c | 228 ++ rt-thread/components/drivers/led/led.c | 349 ++ rt-thread/components/drivers/mailbox/Kconfig | 14 + .../components/drivers/mailbox/SConscript | 18 + .../components/drivers/mailbox/mailbox-pic.c | 292 ++ .../components/drivers/mailbox/mailbox.c | 352 ++ rt-thread/components/drivers/mfd/Kconfig | 10 + rt-thread/components/drivers/mfd/SConscript | 17 + rt-thread/components/drivers/mfd/mfd-syscon.c | 244 ++ rt-thread/components/drivers/misc/rt_random.c | 4 +- rt-thread/components/drivers/nvme/Kconfig | 23 + rt-thread/components/drivers/nvme/SConscript | 18 + rt-thread/components/drivers/nvme/nvme-pci.c | 171 + rt-thread/components/drivers/nvme/nvme.c | 1302 +++++++ rt-thread/components/drivers/ofw/base.c | 4 +- rt-thread/components/drivers/ofw/fdt.c | 75 +- rt-thread/components/drivers/ofw/io.c | 69 + rt-thread/components/drivers/ofw/ofw.c | 34 +- rt-thread/components/drivers/pci/Kconfig | 49 + rt-thread/components/drivers/pci/SConscript | 28 + rt-thread/components/drivers/pci/access.c | 159 + rt-thread/components/drivers/pci/ecam.c | 72 + rt-thread/components/drivers/pci/ecam.h | 69 + .../drivers/pci/endpoint/SConscript | 15 + .../drivers/pci/endpoint/endpoint.c | 504 +++ .../components/drivers/pci/endpoint/mem.c | 205 ++ .../components/drivers/pci/host-bridge.c | 129 + rt-thread/components/drivers/pci/host/Kconfig | 12 + .../components/drivers/pci/host/SConscript | 25 + .../components/drivers/pci/host/dw/Kconfig | 13 + .../components/drivers/pci/host/dw/SConscript | 21 + .../components/drivers/pci/host/dw/pcie-dw.c | 645 ++++ .../components/drivers/pci/host/dw/pcie-dw.h | 440 +++ .../drivers/pci/host/dw/pcie-dw_ep.c | 863 +++++ .../drivers/pci/host/dw/pcie-dw_host.c | 644 ++++ .../drivers/pci/host/dw/pcie-dw_platfrom.c | 295 ++ .../drivers/pci/host/pci-host-common.c | 85 + .../drivers/pci/host/pci-host-generic.c | 66 + rt-thread/components/drivers/pci/irq.c | 60 + .../components/drivers/pci/msi/SConscript | 15 + rt-thread/components/drivers/pci/msi/device.c | 46 + rt-thread/components/drivers/pci/msi/irq.c | 146 + rt-thread/components/drivers/pci/msi/msi.c | 949 +++++ rt-thread/components/drivers/pci/ofw.c | 609 ++++ rt-thread/components/drivers/pci/pci.c | 1018 ++++++ rt-thread/components/drivers/pci/pci_ids.h | 272 ++ rt-thread/components/drivers/pci/pci_regs.h | 1090 ++++++ rt-thread/components/drivers/pci/pme.c | 121 + rt-thread/components/drivers/pci/probe.c | 926 +++++ rt-thread/components/drivers/phy/Kconfig | 5 + rt-thread/components/drivers/phy/SConscript | 14 +- rt-thread/components/drivers/phy/general.c | 349 ++ .../components/drivers/phy/general_phy.h | 143 + rt-thread/components/drivers/phy/mdio.c | 75 + rt-thread/components/drivers/phy/mdio.h | 98 + rt-thread/components/drivers/phy/ofw.c | 156 + rt-thread/components/drivers/phy/ofw.h | 58 + rt-thread/components/drivers/phy/phy.c | 513 ++- rt-thread/components/drivers/phye/Kconfig | 11 + rt-thread/components/drivers/phye/SConscript | 15 + rt-thread/components/drivers/phye/phye.c | 320 ++ rt-thread/components/drivers/pic/Kconfig | 23 +- rt-thread/components/drivers/pic/SConscript | 6 + .../components/drivers/pic/pic-gic-common.c | 5 +- .../components/drivers/pic/pic-gic-common.h | 10 +- rt-thread/components/drivers/pic/pic-gicv2.c | 9 + rt-thread/components/drivers/pic/pic-gicv2.h | 2 + rt-thread/components/drivers/pic/pic-gicv2m.c | 378 ++ .../components/drivers/pic/pic-gicv3-its.c | 1584 +++++++++ rt-thread/components/drivers/pic/pic-gicv3.c | 39 +- rt-thread/components/drivers/pic/pic-gicv3.h | 179 +- rt-thread/components/drivers/pic/pic.c | 58 +- rt-thread/components/drivers/pin/Kconfig | 4 + rt-thread/components/drivers/pin/SConscript | 6 +- .../drivers/pin/{pin.c => dev_pin.c} | 12 +- rt-thread/components/drivers/pin/dev_pin_dm.c | 458 +++ .../drivers/pin/{pin_dm.h => dev_pin_dm.h} | 8 +- .../drivers/pin/{pin_ofw.c => dev_pin_ofw.c} | 32 +- rt-thread/components/drivers/pin/pin_dm.c | 220 -- rt-thread/components/drivers/pinctrl/Kconfig | 3 + .../components/drivers/pinctrl/pinctrl.c | 7 + rt-thread/components/drivers/pm/pm.c | 287 +- .../components/drivers/regulator/Kconfig | 23 + .../components/drivers/regulator/SConscript | 21 + .../drivers/regulator/regulator-fixed.c | 171 + .../drivers/regulator/regulator-gpio.c | 309 ++ .../components/drivers/regulator/regulator.c | 629 ++++ .../drivers/regulator/regulator_dm.c | 59 + .../drivers/regulator/regulator_dm.h | 26 + rt-thread/components/drivers/reset/Kconfig | 14 + rt-thread/components/drivers/reset/SConscript | 18 + .../components/drivers/reset/reset-simple.c | 202 ++ .../components/drivers/reset/reset-simple.h | 50 + rt-thread/components/drivers/reset/reset.c | 432 +++ rt-thread/components/drivers/rtc/Kconfig | 14 + rt-thread/components/drivers/rtc/SConscript | 6 +- .../drivers/rtc/{alarm.c => dev_alarm.c} | 16 +- .../drivers/rtc/{rtc.c => dev_rtc.c} | 2 +- .../rtc/{soft_rtc.c => dev_soft_rtc.c} | 35 +- rt-thread/components/drivers/scsi/Kconfig | 20 + rt-thread/components/drivers/scsi/SConscript | 21 + rt-thread/components/drivers/scsi/scsi.c | 669 ++++ .../components/drivers/scsi/scsi_cdrom.c | 136 + rt-thread/components/drivers/scsi/scsi_sd.c | 251 ++ rt-thread/components/drivers/sdio/Kconfig | 8 +- rt-thread/components/drivers/sdio/SConscript | 18 +- rt-thread/components/drivers/sdio/block_dev.c | 952 ----- rt-thread/components/drivers/sdio/dev_block.c | 422 +++ .../drivers/sdio/{mmc.c => dev_mmc.c} | 4 +- .../sdio/{mmcsd_core.c => dev_mmcsd_core.c} | 8 +- .../drivers/sdio/{sd.c => dev_sd.c} | 6 +- .../drivers/sdio/{sdio.c => dev_sdio.c} | 6 +- rt-thread/components/drivers/sdio/gpt.c | 563 --- .../components/drivers/sdio/sdhci/fit-mmc.c | 320 ++ .../sdio/sdhci/include/sdhci-platform.h | 66 + .../drivers/sdio/sdhci/include/sdhci.h | 677 ++++ .../drivers/sdio/sdhci/include/sdhci_host.h | 345 ++ .../drivers/sdio/sdhci/include/sdhci_misc.h | 70 + .../drivers/sdio/sdhci/sdhci-platform.c | 125 + .../components/drivers/sdio/sdhci/sdhci.c | 3152 +++++++++++++++++ rt-thread/components/drivers/sensor/Kconfig | 2 +- .../components/drivers/sensor/v1/sensor.c | 5 +- .../components/drivers/sensor/v1/sensor_cmd.c | 34 +- .../components/drivers/sensor/v2/sensor_cmd.c | 26 +- rt-thread/components/drivers/serial/Kconfig | 5 +- .../components/drivers/serial/SConscript | 7 +- rt-thread/components/drivers/serial/bypass.c | 355 ++ .../drivers/serial/{serial.c => dev_serial.c} | 60 +- .../serial/{serial_v2.c => dev_serial_v2.c} | 4 +- .../components/drivers/serial/serial_tty.c | 54 +- .../components/drivers/smp_call/SConscript | 10 + .../components/drivers/smp_call/smp_call.c | 374 ++ .../components/drivers/smp_call/smp_call.h | 74 + rt-thread/components/drivers/spi/SConscript | 15 +- .../spi/{qspi_core.c => dev_qspi_core.c} | 21 +- .../drivers/spi/{spi_dev.c => dev_spi.c} | 73 +- .../spi/{spi-bit-ops.c => dev_spi_bit_ops.c} | 57 +- .../spi/{spi-bit-ops.h => dev_spi_bit_ops.h} | 4 +- .../components/drivers/spi/dev_spi_bus.c | 203 ++ .../spi/{spi_core.c => dev_spi_core.c} | 72 +- rt-thread/components/drivers/spi/dev_spi_dm.c | 106 + rt-thread/components/drivers/spi/dev_spi_dm.h | 29 + .../spi/{spi_flash.h => dev_spi_flash.h} | 4 +- ...{spi_flash_sfud.c => dev_spi_flash_sfud.c} | 4 +- ...{spi_flash_sfud.h => dev_spi_flash_sfud.h} | 17 +- .../drivers/spi/{spi_msd.c => dev_spi_msd.c} | 2 +- .../drivers/spi/{spi_msd.h => dev_spi_msd.h} | 8 +- ...{spi_wifi_rw009.c => dev_spi_wifi_rw009.c} | 4 +- ...{spi_wifi_rw009.h => dev_spi_wifi_rw009.h} | 6 +- rt-thread/components/drivers/spi/enc28j60.h | 8 +- rt-thread/components/drivers/thermal/Kconfig | 28 + .../components/drivers/thermal/SConscript | 18 + .../drivers/thermal/thermal-cool-pwm-fan.c | 288 ++ .../components/drivers/thermal/thermal.c | 917 +++++ .../components/drivers/thermal/thermal_dm.c | 64 + .../components/drivers/thermal/thermal_dm.h | 27 + rt-thread/components/drivers/touch/SConscript | 2 +- .../drivers/touch/{touch.c => dev_touch.c} | 0 .../drivers/usb/cherryusb/CherryUSB.svg | 2 +- .../components/drivers/usb/cherryusb/Kconfig | 36 +- .../drivers/usb/cherryusb/Kconfig.cherryusb | 312 -- .../drivers/usb/cherryusb/README.md | 92 +- .../drivers/usb/cherryusb/README_zh.md | 90 +- .../drivers/usb/cherryusb/SConscript | 62 +- .../components/drivers/usb/cherryusb/VERSION | 4 +- .../drivers/usb/cherryusb/cherryusb.cmake | 62 +- .../usb/cherryusb/cherryusb_config_template.h | 19 +- .../usb/cherryusb/class/adb/usbd_adb.c | 310 ++ .../usb/cherryusb/class/adb/usbd_adb.h | 38 + .../drivers/usb/cherryusb/class/aoa/usb_aoa.h | 48 + .../usb/cherryusb/class/aoa/usbh_aoa.c | 289 ++ .../usb/cherryusb/class/aoa/usbh_aoa.h | 40 + .../usb/cherryusb/class/audio/usb_audio.h | 135 +- .../usb/cherryusb/class/audio/usbd_audio.c | 69 +- .../usb/cherryusb/class/audio/usbd_audio.h | 2 +- .../usb/cherryusb/class/audio/usbh_audio.c | 442 ++- .../usb/cherryusb/class/audio/usbh_audio.h | 54 +- .../drivers/usb/cherryusb/class/cdc/usb_cdc.h | 16 +- .../usb/cherryusb/class/cdc/usbd_cdc.h | 24 +- .../class/cdc/{usbd_cdc.c => usbd_cdc_acm.c} | 18 +- .../usb/cherryusb/class/cdc/usbd_cdc_acm.h | 29 + .../usb/cherryusb/class/cdc/usbd_cdc_ecm.c | 105 +- .../usb/cherryusb/class/cdc/usbd_cdc_ecm.h | 14 +- .../usb/cherryusb/class/cdc/usbh_cdc_acm.c | 42 +- .../usb/cherryusb/class/cdc/usbh_cdc_ecm.c | 9 +- .../usb/cherryusb/class/cdc/usbh_cdc_ncm.c | 13 +- .../drivers/usb/cherryusb/class/hid/usb_hid.h | 12 +- .../usb/cherryusb/class/hid/usbd_hid.c | 37 + .../usb/cherryusb/class/hid/usbh_hid.c | 98 +- .../usb/cherryusb/class/hid/usbh_hid.h | 5 +- .../drivers/usb/cherryusb/class/hub/usb_hub.h | 96 +- .../usb/cherryusb/class/hub/usbh_hub.c | 177 +- .../usb/cherryusb/class/hub/usbh_hub.h | 4 - .../usb/cherryusb/class/msc/usbd_msc.c | 87 +- .../usb/cherryusb/class/msc/usbd_msc.h | 4 +- .../usb/cherryusb/class/msc/usbh_msc.c | 66 +- .../usb/cherryusb/class/template/usbh_xxx.c | 18 +- .../cherryusb/class/vendor/net/usbh_asix.c | 22 +- .../cherryusb/class/vendor/net/usbh_rtl8152.c | 41 +- .../class/vendor/serial/usbh_ch34x.c | 20 +- .../class/vendor/serial/usbh_cp210x.c | 22 +- .../cherryusb/class/vendor/serial/usbh_ftdi.c | 24 +- .../class/vendor/serial/usbh_pl2303.c | 22 +- .../usb/cherryusb/class/vendor/wifi/README.md | 6 + .../cherryusb/class/vendor/wifi/usbh_bl616.c | 512 +++ .../cherryusb/class/vendor/wifi/usbh_bl616.h | 220 ++ .../cherryusb/class/vendor/xbox/usbh_xbox.c | 228 ++ .../cherryusb/class/vendor/xbox/usbh_xbox.h | 31 + .../usb/cherryusb/class/video/usbd_video.c | 144 +- .../usb/cherryusb/class/video/usbd_video.h | 4 +- .../usb/cherryusb/class/video/usbh_video.c | 30 +- .../usb/cherryusb/class/wireless/usbd_rndis.c | 43 +- .../usb/cherryusb/class/wireless/usbd_rndis.h | 3 +- .../cherryusb/class/wireless/usbh_bluetooth.c | 16 +- .../usb/cherryusb/class/wireless/usbh_rndis.c | 27 +- .../drivers/usb/cherryusb/common/usb_dc.h | 7 + .../drivers/usb/cherryusb/common/usb_def.h | 2 +- .../drivers/usb/cherryusb/common/usb_log.h | 29 + .../drivers/usb/cherryusb/common/usb_osal.h | 3 + .../usb/cherryusb/common/usb_version.h | 21 + .../drivers/usb/cherryusb/core/usbd_core.c | 194 +- .../drivers/usb/cherryusb/core/usbd_core.h | 10 +- .../drivers/usb/cherryusb/core/usbh_core.c | 91 +- .../drivers/usb/cherryusb/core/usbh_core.h | 26 +- .../usb/cherryusb/demo/adb/cherryadb.png | Bin 0 -> 42508 bytes .../usb/cherryusb/demo/adb/cherrysh_port.c | 330 ++ .../cherryusb/demo/adb/usbd_adb_template.c | 228 ++ .../demo/audio_v1_mic_multichan_template.c | 4 +- .../audio_v1_mic_speaker_multichan_template.c | 4 +- .../demo/audio_v2_mic_multichan_template.c | 4 +- .../audio_v2_mic_speaker_multichan_template.c | 4 +- .../audio_v2_speaker_multichan_template.c | 4 +- .../usb/cherryusb/demo/bootuf2/bootuf2.c | 463 +++ .../usb/cherryusb/demo/bootuf2/bootuf2.h | 218 ++ .../cherryusb/demo/bootuf2/bootuf2_config.h | 25 + .../usb/cherryusb/demo/bootuf2/cherryuf2.png | Bin 0 -> 19847 bytes .../demo/bootuf2/msc_bootuf2_template.c | 168 + .../cherryusb/demo/cdc_acm_hid_msc_template.c | 15 +- .../usb/cherryusb/demo/cdc_acm_msc_template.c | 8 +- .../cherryusb/demo/cdc_acm_multi_template.c | 6 +- .../usb/cherryusb/demo/cdc_acm_template.c | 6 +- .../usb/cherryusb/demo/cdc_ecm_template.c | 14 +- .../usb/cherryusb/demo/cdc_rndis_template.c | 16 +- .../demo/dfu_with_st_tool_template.c | 4 +- .../demo/hid_custom_inout_template.c | 4 +- .../cherryusb/demo/hid_keyboard_template.c | 11 +- .../usb/cherryusb/demo/hid_mouse_template.c | 74 +- .../demo/hid_remote_wakeup_template.c | 331 ++ .../usb/cherryusb/demo/midi_template.c | 4 +- .../usb/cherryusb/demo/msc_ram_template.c | 13 +- .../usb/cherryusb/demo/msc_storage_template.c | 4 +- .../drivers/usb/cherryusb/demo/usb_host.c | 37 +- .../demo/video_audiov1_hid_template.c | 52 +- .../demo/video_static_h264_template.c | 42 +- .../demo/video_static_mjpeg_template.c | 47 +- .../demo/video_static_yuyv_template.c | 45 +- .../usb/cherryusb/demo/webusb_hid_template.c | 383 ++ .../usb/cherryusb/demo/webusb_template.c | 76 - .../usb/cherryusb/demo/winusb1.0_template.c | 12 +- .../cherryusb/demo/winusb2.0_cdc_template.c | 6 +- .../cherryusb/demo/winusb2.0_hid_template.c | 4 +- .../drivers/usb/cherryusb/idf_component.yml | 25 + .../usb/cherryusb/osal/usb_osal_rtthread.c | 44 +- .../cherryusb/platform/rtthread/usb_check.c | 8 +- .../cherryusb/platform/rtthread/usbh_dfs.c | 29 +- .../cherryusb/platform/rtthread/usbh_lwip.c | 19 + .../cherryusb/port/bouffalolab/usb_dc_bl.c | 47 +- .../usb/cherryusb/port/ch32/usb_dc_usbfs.c | 5 + .../usb/cherryusb/port/ch32/usb_dc_usbhs.c | 5 + .../usb/cherryusb/port/chipidea/README.md | 14 + .../port/chipidea/usb_chipidea_reg.h | 2249 ++++++++++++ .../cherryusb/port/chipidea/usb_dc_chipidea.c | 718 ++++ .../cherryusb/port/chipidea/usb_glue_mcx.c | 99 + .../drivers/usb/cherryusb/port/dwc2/README.md | 7 + .../usb/cherryusb/port/dwc2/usb_dc_dwc2.c | 369 +- .../usb/cherryusb/port/dwc2/usb_dwc2_reg.h | 3 + .../usb/cherryusb/port/dwc2/usb_glue_at.c | 12 +- .../usb/cherryusb/port/dwc2/usb_glue_esp.c | 23 +- .../usb/cherryusb/port/dwc2/usb_glue_gd.c | 11 +- .../usb/cherryusb/port/dwc2/usb_glue_hc.c | 5 + .../cherryusb/port/dwc2/usb_glue_kendryte.c | 157 + .../usb/cherryusb/port/dwc2/usb_glue_st.c | 13 +- .../usb/cherryusb/port/dwc2/usb_hc_dwc2.c | 48 +- .../drivers/usb/cherryusb/port/ehci/README.md | 11 + .../usb/cherryusb/port/ehci/usb_ehci_priv.h | 84 - .../usb/cherryusb/port/ehci/usb_ehci_reg.h | 393 ++ .../usb/cherryusb/port/ehci/usb_glue_aic.c | 38 +- .../usb/cherryusb/port/ehci/usb_glue_hpm.c | 15 +- .../usb/cherryusb/port/ehci/usb_glue_ma35d0.c | 4 +- .../usb/cherryusb/port/ehci/usb_glue_mcx.c | 156 + .../usb/cherryusb/port/ehci/usb_hc_ehci.c | 34 +- .../usb/cherryusb/port/ehci/usb_hc_ehci.h | 431 +-- .../usb/cherryusb/port/fsdev/usb_dc_fsdev.c | 20 +- .../usb/cherryusb/port/hpm/usb_dc_hpm.c | 39 +- .../usb/cherryusb/port/kinetis/README.md | 18 + .../cherryusb/port/kinetis/usb_dc_kinetis.c | 448 +++ .../usb/cherryusb/port/kinetis/usb_glue_mcx.c | 75 + .../cherryusb/port/kinetis/usb_hc_kinetis.c | 1 + .../cherryusb/port/kinetis/usb_kinetis_reg.h | 1487 ++++++++ .../usb/cherryusb/port/musb/usb_dc_musb.c | 32 +- .../usb/cherryusb/port/musb/usb_glue_bk.c | 5 + .../usb/cherryusb/port/musb/usb_glue_es.c | 7 +- .../usb/cherryusb/port/musb/usb_glue_sunxi.c | 7 +- .../usb/cherryusb/port/musb/usb_hc_musb.c | 3 + .../usb/cherryusb/port/musb/usb_musb_reg.h | 1 + .../drivers/usb/cherryusb/port/ohci/README.md | 13 +- .../usb/cherryusb/port/ohci/usb_glue_lpc.c | 45 + .../usb/cherryusb/port/ohci/usb_hc_ohci.c | 209 +- .../usb/cherryusb/port/ohci/usb_hc_ohci.h | 520 +-- .../usb/cherryusb/port/ohci/usb_ohci_priv.h | 24 - .../usb/cherryusb/port/ohci/usb_ohci_reg.h | 484 +++ .../usb/cherryusb/port/pusb2/README.md | 66 + .../port/pusb2/libpusb2_dc_a32_hardfp.a | Bin 0 -> 171332 bytes .../libpusb2_dc_a32_softfp_crypto_neon.a | Bin 0 -> 171328 bytes .../port/pusb2/libpusb2_dc_a32_softfp_neon.a | Bin 0 -> 325436 bytes .../cherryusb/port/pusb2/libpusb2_dc_a64.a | Bin 0 -> 141528 bytes .../port/pusb2/libpusb2_hc_a32_hardfp.a | Bin 0 -> 923220 bytes .../libpusb2_hc_a32_softfp_crypto_neon.a | Bin 0 -> 923192 bytes .../port/pusb2/libpusb2_hc_a32_softfp_neon.a | Bin 0 -> 328504 bytes .../cherryusb/port/pusb2/libpusb2_hc_a64.a | Bin 0 -> 1179500 bytes .../port/pusb2/rt-thread/usb_config.h | 265 ++ .../pusb2/rt-thread/usb_dc_glue_phytium.c | 89 + .../pusb2/rt-thread/usb_hc_glue_phytium.c | 109 + .../usb/cherryusb/port/xhci/phytium/README.md | 70 + .../port/xhci/phytium/libxhci_a32_hardfp.a | Bin 0 -> 52784 bytes .../phytium/libxhci_a32_softfp_crypto_neon.a | Bin 0 -> 73868 bytes .../xhci/phytium/libxhci_a32_softfp_neon.a | Bin 0 -> 104852 bytes .../cherryusb/port/xhci/phytium/libxhci_a64.a | Bin 0 -> 62300 bytes .../port/xhci/phytium/rt-thread/usb_config.h | 289 ++ .../xhci/phytium/rt-thread/usb_glue_phytium.c | 65 + .../phytium/rt-thread/usb_glue_phytium_plat.c | 49 + .../components/drivers/virtio/virtio_net.c | 56 +- .../watchdog/{watchdog.c => dev_watchdog.c} | 2 +- rt-thread/components/drivers/wlan/SConscript | 14 +- .../drivers/wlan/{wlan_dev.c => dev_wlan.c} | 64 +- .../drivers/wlan/{wlan_dev.h => dev_wlan.h} | 14 +- .../wlan/{wlan_cfg.c => dev_wlan_cfg.c} | 2 +- .../wlan/{wlan_cfg.h => dev_wlan_cfg.h} | 6 +- .../wlan/{wlan_cmd.c => dev_wlan_cmd.c} | 6 +- .../wlan/{wlan_lwip.c => dev_wlan_lwip.c} | 6 +- .../wlan/{wlan_mgnt.c => dev_wlan_mgnt.c} | 25 +- .../wlan/{wlan_mgnt.h => dev_wlan_mgnt.h} | 6 +- .../wlan/{wlan_prot.c => dev_wlan_prot.c} | 4 +- .../wlan/{wlan_prot.h => dev_wlan_prot.h} | 4 +- ...{wlan_workqueue.c => dev_wlan_workqueue.c} | 2 +- ...{wlan_workqueue.h => dev_wlan_workqueue.h} | 4 +- rt-thread/components/fal/Kconfig | 10 +- rt-thread/components/fal/docs/fal_api.md | 10 +- rt-thread/components/fal/docs/fal_api_en.md | 10 +- rt-thread/components/fal/inc/fal.h | 10 +- rt-thread/components/fal/inc/fal_def.h | 79 +- .../components/fal/samples/porting/README.md | 6 +- .../fal/samples/porting/fal_flash_sfud_port.c | 26 +- .../samples/porting/fal_flash_stm32f2_port.c | 58 +- rt-thread/components/fal/src/fal.c | 15 +- rt-thread/components/fal/src/fal_flash.c | 42 +- rt-thread/components/fal/src/fal_partition.c | 126 +- rt-thread/components/fal/src/fal_rtt.c | 85 +- rt-thread/components/finsh/finsh.h | 21 +- rt-thread/components/finsh/msh.c | 6 +- rt-thread/components/finsh/shell.c | 7 +- rt-thread/components/legacy/fdt/Kconfig | 2 +- .../libc/compilers/common/cstring.c | 10 +- .../components/libc/compilers/common/ctime.c | 46 +- .../components/libc/compilers/common/cwchar.c | 12 +- .../libc/compilers/common/include/dirent.h | 13 + .../components/libc/compilers/musl/fcntl.h | 25 +- .../libc/posix/io/timerfd/timerfd.c | 86 +- .../components/libc/posix/libdl/arch/arm.c | 2 +- .../components/libc/posix/libdl/arch/riscv.c | 2 +- .../components/libc/posix/libdl/arch/x86.c | 2 +- .../components/libc/posix/libdl/dlclose.c | 11 +- rt-thread/components/libc/posix/libdl/dlelf.c | 45 +- rt-thread/components/libc/posix/libdl/dlelf.h | 2 +- .../components/libc/posix/libdl/dlerror.c | 9 +- rt-thread/components/libc/posix/libdl/dlfcn.h | 2 +- .../components/libc/posix/libdl/dlmodule.c | 62 +- .../components/libc/posix/libdl/dlmodule.h | 2 +- .../components/libc/posix/libdl/dlopen.c | 15 +- rt-thread/components/libc/posix/libdl/dlsym.c | 13 +- .../components/libc/posix/libdl/dlsyms.c | 2 +- .../components/libc/posix/pthreads/pthread.c | 539 ++- .../libc/posix/pthreads/pthread_barrier.c | 115 +- .../libc/posix/pthreads/pthread_cond.c | 185 +- .../libc/posix/pthreads/pthread_mutex.c | 296 +- .../libc/posix/pthreads/pthread_rwlock.c | 224 +- .../libc/posix/pthreads/pthread_tls.c | 112 +- rt-thread/components/lwp/Kconfig | 17 +- rt-thread/components/lwp/SConscript | 17 +- .../components/lwp/arch/aarch64/common/vdso.c | 110 + .../lwp/arch/aarch64/common/vdso_data.c | 34 + .../lwp/arch/aarch64/cortex-a/lwp_arch.c | 10 +- .../lwp/arch/aarch64/cortex-a/lwp_gcc.S | 259 +- .../components/lwp/arch/x86/i386/lwp_arch.c | 4 +- rt-thread/components/lwp/libc_musl.h | 9 + rt-thread/components/lwp/lwp.c | 87 +- rt-thread/components/lwp/lwp.h | 7 + rt-thread/components/lwp/lwp_args.c | 23 +- rt-thread/components/lwp/lwp_elf.c | 15 + rt-thread/components/lwp/lwp_pgrp.c | 12 + rt-thread/components/lwp/lwp_pid.c | 66 +- rt-thread/components/lwp/lwp_pid.h | 4 + rt-thread/components/lwp/lwp_runtime.c | 161 + rt-thread/components/lwp/lwp_signal.c | 38 + rt-thread/components/lwp/lwp_signal.h | 2 + rt-thread/components/lwp/lwp_sys_socket.h | 8 + rt-thread/components/lwp/lwp_syscall.c | 228 +- rt-thread/components/lwp/lwp_syscall.h | 1 - rt-thread/components/lwp/lwp_tid.c | 6 + rt-thread/components/lwp/lwp_user_mm.c | 18 + rt-thread/components/lwp/syscall_generic.h | 3 +- rt-thread/components/lwp/terminal/Kconfig | 1 + .../components/lwp/terminal/freebsd/tty.c | 2 +- rt-thread/components/lwp/terminal/tty_ptmx.c | 14 +- rt-thread/components/lwp/vdso/Kconfig | 4 + rt-thread/components/lwp/vdso/SConscript | 48 + rt-thread/components/lwp/vdso/kernel/vdso.h | 39 + .../components/lwp/vdso/kernel/vdso_data.h | 48 + .../components/lwp/vdso/kernel/vdso_text.S | 21 + rt-thread/components/lwp/vdso/user/SConstruct | 39 + rt-thread/components/lwp/vdso/user/vdso.lds.S | 60 + rt-thread/components/lwp/vdso/user/vdso_sys.c | 95 + rt-thread/components/lwp/vdso/user/vdso_sys.h | 153 + rt-thread/components/lwp/vdso/user/xmake.lua | 58 + rt-thread/components/lwp/vdso/vdso_config.h | 38 + rt-thread/components/lwp/vdso/vdso_datapage.h | 75 + rt-thread/components/lwp/vdso/vdso_weak.c | 23 + rt-thread/components/mm/mm_memblock.c | 18 +- .../components/net/at/at_socket/at_socket.c | 550 ++- .../components/net/at/at_socket/at_socket.h | 5 +- rt-thread/components/net/at/include/at.h | 3 +- rt-thread/components/net/at/src/at_client.c | 8 +- rt-thread/components/net/at/src/at_server.c | 8 +- .../net/lwip-dhcpd/dhcp_server_raw.c | 2 +- rt-thread/components/net/lwip/Kconfig | 4 + .../net/lwip/lwip-2.1.2/src/core/dns.c | 67 +- .../net/lwip/lwip-2.1.2/src/core/ipv4/dhcp.c | 9 + .../net/lwip/lwip-2.1.2/src/core/ipv6/dhcp6.c | 9 + .../net/lwip/lwip-2.1.2/src/core/ipv6/nd6.c | 20 +- .../lwip-2.1.2/src/include/lwip/sockets.h | 13 + .../net/lwip/lwip-2.1.2/src/netif/ppp/ppp.c | 25 +- .../components/net/lwip/port/ethernetif.c | 12 +- rt-thread/components/net/lwip/port/lwipopts.h | 11 + rt-thread/components/net/netdev/Kconfig | 6 +- .../components/net/netdev/include/netdev.h | 8 +- rt-thread/components/net/netdev/src/netdev.c | 174 +- .../components/net/sal/impl/af_inet_at.c | 2 +- .../components/net/sal/impl/af_inet_lwip.c | 8 +- .../components/net/sal/socket/net_sockets.c | 492 ++- rt-thread/components/net/sal/src/sal_socket.c | 35 +- rt-thread/components/utilities/Kconfig | 15 +- rt-thread/components/utilities/ulog/ulog.c | 42 +- .../components/utilities/ulog/ulog_def.h | 6 - .../components/utilities/utest/TC_uassert.c | 83 + rt-thread/components/utilities/utest/utest.c | 139 +- rt-thread/components/utilities/utest/utest.h | 10 +- .../components/utilities/utest/utest_assert.h | 60 +- .../components/utilities/utest/utest_log.h | 4 +- rt-thread/components/utilities/ymodem/ry_sy.c | 3 +- .../components/utilities/ymodem/ymodem.c | 8 +- rt-thread/include/klibc/kerrno.h | 78 + rt-thread/include/klibc/kstdio.h | 32 + rt-thread/include/klibc/kstring.h | 39 + rt-thread/include/rtatomic.h | 63 +- rt-thread/include/rtcompiler.h | 11 + rt-thread/include/rtdbg.h | 30 - rt-thread/include/rtdef.h | 104 +- rt-thread/include/rthw.h | 4 +- rt-thread/include/rtklibc.h | 42 +- rt-thread/include/rtsched.h | 43 +- rt-thread/include/rtservice.h | 15 + rt-thread/include/rtthread.h | 13 +- rt-thread/include/rttypes.h | 36 +- rt-thread/libcpu/Kconfig | 62 +- rt-thread/libcpu/arm/AT91SAM7S/interrupt.c | 2 +- rt-thread/libcpu/arm/AT91SAM7X/interrupt.c | 2 +- rt-thread/libcpu/arm/am335x/interrupt.c | 2 +- rt-thread/libcpu/arm/cortex-a/backtrace.c | 2 +- rt-thread/libcpu/arm/cortex-m23/cpuport.c | 29 +- rt-thread/libcpu/arm/cortex-m23/cpuport.h | 45 + rt-thread/libcpu/arm/cortex-m4/README.md | 53 + rt-thread/libcpu/arm/cortex-m4/context_gcc.S | 7 + rt-thread/libcpu/arm/cortex-m4/context_iar.S | 11 +- rt-thread/libcpu/arm/cortex-m4/context_rvds.S | 9 +- rt-thread/libcpu/arm/cortex-m7/context_gcc.S | 4 +- rt-thread/libcpu/arm/cortex-r4/interrupt.c | 2 +- rt-thread/libcpu/arm/cortex-r52/backtrace.c | 2 +- rt-thread/libcpu/arm/cortex-r52/context_gcc.S | 227 +- rt-thread/libcpu/arm/cortex-r52/start_gcc.S | 58 +- rt-thread/libcpu/arm/cortex-r52/start_iar.S | 16 +- rt-thread/libcpu/arm/cortex-r52/vector_gcc.S | 10 +- rt-thread/libcpu/arm/cortex-r52/vector_iar.S | 2 + rt-thread/libcpu/arm/lpc214x/cpuport.c | 2 +- rt-thread/libcpu/arm/lpc24xx/interrupt.c | 2 +- .../libcpu/arm/realview-a8-vmm/interrupt.c | 2 +- rt-thread/libcpu/arm/s3c24x0/interrupt.c | 2 +- rt-thread/libcpu/arm/s3c44b0/interrupt.c | 2 +- rt-thread/libcpu/arm/sep4020/interrupt.c | 2 +- rt-thread/libcpu/arm/zynqmp-r5/interrupt.c | 2 +- rt-thread/src/Kconfig | 82 +- rt-thread/src/SConscript | 6 +- rt-thread/src/clock.c | 68 +- rt-thread/src/components.c | 7 +- rt-thread/src/defunct.c | 178 + rt-thread/src/idle.c | 183 +- rt-thread/src/ipc.c | 42 +- rt-thread/src/irq.c | 20 + rt-thread/src/klibc/Kconfig | 258 ++ rt-thread/src/klibc/SConscript | 23 + rt-thread/src/klibc/kerrno.c | 161 + rt-thread/src/klibc/kstdio.c | 779 +--- rt-thread/src/klibc/kstring.c | 100 +- rt-thread/src/klibc/rt_vsnprintf_std.c | 1351 +++++++ rt-thread/src/klibc/rt_vsnprintf_tiny.c | 611 ++++ rt-thread/src/klibc/rt_vsscanf.c | 700 ++++ rt-thread/src/klibc/utest/SConscript | 10 + rt-thread/src/klibc/utest/TC_rt_memcmp.c | 161 + rt-thread/src/klibc/utest/TC_rt_memcpy.c | 108 + rt-thread/src/klibc/utest/TC_rt_memmove.c | 108 + rt-thread/src/klibc/utest/TC_rt_memset.c | 102 + rt-thread/src/klibc/utest/TC_rt_sprintf.c | 1065 ++++++ rt-thread/src/klibc/utest/TC_rt_sscanf.c | 250 ++ rt-thread/src/kservice.c | 53 +- rt-thread/src/mem.c | 50 +- rt-thread/src/memheap.c | 10 +- rt-thread/src/object.c | 2 +- rt-thread/src/scheduler_comm.c | 30 +- rt-thread/src/scheduler_mp.c | 3 - rt-thread/src/scheduler_up.c | 12 +- rt-thread/src/signal.c | 10 +- rt-thread/src/slab.c | 52 +- rt-thread/src/thread.c | 80 +- rt-thread/tools/building.py | 74 +- rt-thread/tools/ci/bsp_buildings.py | 85 +- rt-thread/tools/ci/bsp_detail.py | 206 ++ rt-thread/tools/ci/bsp_detail.yml | 2606 ++++++++++++++ rt-thread/tools/ci/cpp_check.py | 39 +- rt-thread/tools/ci/install.sh | 219 ++ rt-thread/tools/ci/manual_bsp_build_all.py | 338 ++ rt-thread/tools/ci/requirements.txt | 2 + rt-thread/tools/ci/toolchain.sh | 87 + rt-thread/tools/ci/toolchain_bsp.yml | 428 +++ rt-thread/tools/cmake.py | 145 +- rt-thread/tools/eclipse.py | 2 +- rt-thread/tools/env.py | 55 - rt-thread/tools/env_utility.py | 488 +++ rt-thread/tools/install_env.py | 191 - rt-thread/tools/menukconfig.py | 392 -- rt-thread/tools/mkdist.py | 2 +- rt-thread/tools/options.py | 10 +- rt-thread/tools/utils.py | 2 +- rt-thread/tools/vsc.py | 225 +- rt-thread/tools/wizard.py | 2 +- rt-thread/tools/zigbuild.py | 100 + rtconfig.h | 90 +- 731 files changed, 77517 insertions(+), 9955 deletions(-) create mode 100644 .vscode/project.json create mode 100644 rt-thread/components/dfs/dfs_v1/filesystems/iso9660/SConscript create mode 100644 rt-thread/components/dfs/dfs_v1/filesystems/iso9660/dfs_iso9660.c create mode 100644 rt-thread/components/dfs/dfs_v1/filesystems/iso9660/dfs_iso9660.h create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/README.md create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/SConscript create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc.h create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_cmdline.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_devices.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_filesystems.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_net.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_partitions.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_self.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_tty.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_uptime.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_version.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/procfs.c create mode 100644 rt-thread/components/dfs/dfs_v2/filesystems/procfs/procfs.h create mode 100644 rt-thread/components/dfs/dfs_v2/include/dfs_vfs.h create mode 100644 rt-thread/components/drivers/ata/Kconfig create mode 100644 rt-thread/components/drivers/ata/SConscript create mode 100644 rt-thread/components/drivers/ata/ahci-pci.c create mode 100644 rt-thread/components/drivers/ata/ahci.c rename rt-thread/components/drivers/audio/{audio.c => dev_audio.c} (100%) rename rt-thread/components/drivers/audio/{audio_pipe.c => dev_audio_pipe.c} (90%) rename rt-thread/components/drivers/audio/{audio_pipe.h => dev_audio_pipe.h} (95%) create mode 100644 rt-thread/components/drivers/block/Kconfig create mode 100644 rt-thread/components/drivers/block/SConscript create mode 100644 rt-thread/components/drivers/block/blk.c create mode 100644 rt-thread/components/drivers/block/blk_dev.c create mode 100644 rt-thread/components/drivers/block/blk_dev.h create mode 100644 rt-thread/components/drivers/block/blk_dfs.c create mode 100644 rt-thread/components/drivers/block/blk_dfs.h create mode 100644 rt-thread/components/drivers/block/blk_partition.c create mode 100644 rt-thread/components/drivers/block/blk_partition.h create mode 100644 rt-thread/components/drivers/block/partitions/Kconfig create mode 100644 rt-thread/components/drivers/block/partitions/SConscript create mode 100644 rt-thread/components/drivers/block/partitions/dfs.c create mode 100644 rt-thread/components/drivers/block/partitions/efi.c create mode 100644 rt-thread/components/drivers/block/partitions/efi.h rename rt-thread/components/drivers/can/{can.c => dev_can.c} (99%) create mode 100644 rt-thread/components/drivers/dma/Kconfig create mode 100644 rt-thread/components/drivers/dma/SConscript create mode 100644 rt-thread/components/drivers/dma/dma.c create mode 100644 rt-thread/components/drivers/dma/dma_pool.c rename rt-thread/components/drivers/i2c/{i2c-bit-ops.c => dev_i2c_bit_ops.c} (100%) rename rt-thread/components/drivers/i2c/{i2c_bus.c => dev_i2c_bus.c} (99%) rename rt-thread/components/drivers/i2c/{i2c_core.c => dev_i2c_core.c} (98%) rename rt-thread/components/drivers/i2c/{i2c_dev.c => dev_i2c_dev.c} (100%) rename rt-thread/components/drivers/i2c/{i2c_dm.c => dev_i2c_dm.c} (98%) rename rt-thread/components/drivers/i2c/{soft_i2c.c => dev_soft_i2c.c} (94%) create mode 100644 rt-thread/components/drivers/iio/SConscript create mode 100644 rt-thread/components/drivers/iio/iio.c create mode 100644 rt-thread/components/drivers/include/drivers/ahci.h create mode 100644 rt-thread/components/drivers/include/drivers/blk.h create mode 100644 rt-thread/components/drivers/include/drivers/core/master_id.h rename rt-thread/components/drivers/include/drivers/{alarm.h => dev_alarm.h} (96%) rename rt-thread/components/drivers/include/drivers/{audio.h => dev_audio.h} (98%) rename rt-thread/components/drivers/include/drivers/{can.h => dev_can.h} (65%) create mode 100644 rt-thread/components/drivers/include/drivers/dev_i2c.h rename rt-thread/components/drivers/include/drivers/{i2c-bit-ops.h => dev_i2c_bit_ops.h} (93%) rename rt-thread/components/drivers/include/drivers/{i2c_dm.h => dev_i2c_dm.h} (95%) rename rt-thread/components/drivers/include/drivers/{mmc.h => dev_mmc.h} (99%) rename rt-thread/components/drivers/include/drivers/{mmcsd_core.h => dev_mmcsd_core.h} (98%) rename rt-thread/components/drivers/include/drivers/{pin.h => dev_pin.h} (50%) create mode 100644 rt-thread/components/drivers/include/drivers/dev_pwm.h rename rt-thread/components/drivers/include/drivers/{rtc.h => dev_rtc.h} (59%) rename rt-thread/components/drivers/include/drivers/{sd.h => dev_sd.h} (96%) rename rt-thread/components/drivers/include/drivers/{sdio.h => dev_sdio.h} (99%) rename rt-thread/components/drivers/include/drivers/{serial.h => dev_serial.h} (66%) rename rt-thread/components/drivers/include/drivers/{serial_v2.h => dev_serial_v2.h} (61%) create mode 100644 rt-thread/components/drivers/include/drivers/dev_spi.h rename rt-thread/components/drivers/include/drivers/{touch.h => dev_touch.h} (53%) rename rt-thread/components/drivers/include/drivers/{watchdog.h => dev_watchdog.h} (94%) create mode 100644 rt-thread/components/drivers/include/drivers/dma.h delete mode 100644 rt-thread/components/drivers/include/drivers/gpt.h delete mode 100644 rt-thread/components/drivers/include/drivers/i2c.h delete mode 100644 rt-thread/components/drivers/include/drivers/i2c_dev.h create mode 100644 rt-thread/components/drivers/include/drivers/iio.h create mode 100644 rt-thread/components/drivers/include/drivers/led.h create mode 100644 rt-thread/components/drivers/include/drivers/mailbox.h create mode 100644 rt-thread/components/drivers/include/drivers/nvme.h create mode 100644 rt-thread/components/drivers/include/drivers/pci.h create mode 100644 rt-thread/components/drivers/include/drivers/pci_endpoint.h create mode 100644 rt-thread/components/drivers/include/drivers/pci_msi.h delete mode 100644 rt-thread/components/drivers/include/drivers/phy_mdio.h create mode 100644 rt-thread/components/drivers/include/drivers/phye.h create mode 100644 rt-thread/components/drivers/include/drivers/regulator.h create mode 100644 rt-thread/components/drivers/include/drivers/reset.h delete mode 100644 rt-thread/components/drivers/include/drivers/rt_drv_pwm.h create mode 100644 rt-thread/components/drivers/include/drivers/scsi.h create mode 100644 rt-thread/components/drivers/include/drivers/serial_bypass.h delete mode 100644 rt-thread/components/drivers/include/drivers/spi.h create mode 100644 rt-thread/components/drivers/include/drivers/syscon.h create mode 100644 rt-thread/components/drivers/include/drivers/thermal.h create mode 100644 rt-thread/components/drivers/include/dt-bindings/phye/phye.h create mode 100644 rt-thread/components/drivers/include/dt-bindings/thermal/thermal.h create mode 100644 rt-thread/components/drivers/led/Kconfig create mode 100644 rt-thread/components/drivers/led/SConscript create mode 100644 rt-thread/components/drivers/led/led-gpio.c create mode 100644 rt-thread/components/drivers/led/led.c create mode 100644 rt-thread/components/drivers/mailbox/Kconfig create mode 100644 rt-thread/components/drivers/mailbox/SConscript create mode 100644 rt-thread/components/drivers/mailbox/mailbox-pic.c create mode 100644 rt-thread/components/drivers/mailbox/mailbox.c create mode 100644 rt-thread/components/drivers/mfd/Kconfig create mode 100644 rt-thread/components/drivers/mfd/SConscript create mode 100644 rt-thread/components/drivers/mfd/mfd-syscon.c create mode 100644 rt-thread/components/drivers/nvme/Kconfig create mode 100644 rt-thread/components/drivers/nvme/SConscript create mode 100644 rt-thread/components/drivers/nvme/nvme-pci.c create mode 100644 rt-thread/components/drivers/nvme/nvme.c create mode 100644 rt-thread/components/drivers/pci/Kconfig create mode 100644 rt-thread/components/drivers/pci/SConscript create mode 100644 rt-thread/components/drivers/pci/access.c create mode 100644 rt-thread/components/drivers/pci/ecam.c create mode 100644 rt-thread/components/drivers/pci/ecam.h create mode 100644 rt-thread/components/drivers/pci/endpoint/SConscript create mode 100644 rt-thread/components/drivers/pci/endpoint/endpoint.c create mode 100644 rt-thread/components/drivers/pci/endpoint/mem.c create mode 100644 rt-thread/components/drivers/pci/host-bridge.c create mode 100644 rt-thread/components/drivers/pci/host/Kconfig create mode 100644 rt-thread/components/drivers/pci/host/SConscript create mode 100644 rt-thread/components/drivers/pci/host/dw/Kconfig create mode 100644 rt-thread/components/drivers/pci/host/dw/SConscript create mode 100644 rt-thread/components/drivers/pci/host/dw/pcie-dw.c create mode 100644 rt-thread/components/drivers/pci/host/dw/pcie-dw.h create mode 100644 rt-thread/components/drivers/pci/host/dw/pcie-dw_ep.c create mode 100644 rt-thread/components/drivers/pci/host/dw/pcie-dw_host.c create mode 100644 rt-thread/components/drivers/pci/host/dw/pcie-dw_platfrom.c create mode 100644 rt-thread/components/drivers/pci/host/pci-host-common.c create mode 100644 rt-thread/components/drivers/pci/host/pci-host-generic.c create mode 100644 rt-thread/components/drivers/pci/irq.c create mode 100644 rt-thread/components/drivers/pci/msi/SConscript create mode 100644 rt-thread/components/drivers/pci/msi/device.c create mode 100644 rt-thread/components/drivers/pci/msi/irq.c create mode 100644 rt-thread/components/drivers/pci/msi/msi.c create mode 100644 rt-thread/components/drivers/pci/ofw.c create mode 100644 rt-thread/components/drivers/pci/pci.c create mode 100644 rt-thread/components/drivers/pci/pci_ids.h create mode 100644 rt-thread/components/drivers/pci/pci_regs.h create mode 100644 rt-thread/components/drivers/pci/pme.c create mode 100644 rt-thread/components/drivers/pci/probe.c create mode 100644 rt-thread/components/drivers/phy/general.c create mode 100644 rt-thread/components/drivers/phy/general_phy.h create mode 100644 rt-thread/components/drivers/phy/mdio.c create mode 100644 rt-thread/components/drivers/phy/mdio.h create mode 100644 rt-thread/components/drivers/phy/ofw.c create mode 100644 rt-thread/components/drivers/phy/ofw.h create mode 100644 rt-thread/components/drivers/phye/Kconfig create mode 100644 rt-thread/components/drivers/phye/SConscript create mode 100644 rt-thread/components/drivers/phye/phye.c create mode 100644 rt-thread/components/drivers/pic/pic-gicv2m.c create mode 100644 rt-thread/components/drivers/pic/pic-gicv3-its.c rename rt-thread/components/drivers/pin/{pin.c => dev_pin.c} (96%) create mode 100644 rt-thread/components/drivers/pin/dev_pin_dm.c rename rt-thread/components/drivers/pin/{pin_dm.h => dev_pin_dm.h} (70%) rename rt-thread/components/drivers/pin/{pin_ofw.c => dev_pin_ofw.c} (87%) delete mode 100644 rt-thread/components/drivers/pin/pin_dm.c create mode 100644 rt-thread/components/drivers/regulator/Kconfig create mode 100644 rt-thread/components/drivers/regulator/SConscript create mode 100644 rt-thread/components/drivers/regulator/regulator-fixed.c create mode 100644 rt-thread/components/drivers/regulator/regulator-gpio.c create mode 100644 rt-thread/components/drivers/regulator/regulator.c create mode 100644 rt-thread/components/drivers/regulator/regulator_dm.c create mode 100644 rt-thread/components/drivers/regulator/regulator_dm.h create mode 100644 rt-thread/components/drivers/reset/Kconfig create mode 100644 rt-thread/components/drivers/reset/SConscript create mode 100644 rt-thread/components/drivers/reset/reset-simple.c create mode 100644 rt-thread/components/drivers/reset/reset-simple.h create mode 100644 rt-thread/components/drivers/reset/reset.c rename rt-thread/components/drivers/rtc/{alarm.c => dev_alarm.c} (97%) rename rt-thread/components/drivers/rtc/{rtc.c => dev_rtc.c} (99%) rename rt-thread/components/drivers/rtc/{soft_rtc.c => dev_soft_rtc.c} (87%) create mode 100644 rt-thread/components/drivers/scsi/Kconfig create mode 100644 rt-thread/components/drivers/scsi/SConscript create mode 100644 rt-thread/components/drivers/scsi/scsi.c create mode 100644 rt-thread/components/drivers/scsi/scsi_cdrom.c create mode 100644 rt-thread/components/drivers/scsi/scsi_sd.c delete mode 100644 rt-thread/components/drivers/sdio/block_dev.c create mode 100644 rt-thread/components/drivers/sdio/dev_block.c rename rt-thread/components/drivers/sdio/{mmc.c => dev_mmc.c} (99%) rename rt-thread/components/drivers/sdio/{mmcsd_core.c => dev_mmcsd_core.c} (99%) rename rt-thread/components/drivers/sdio/{sd.c => dev_sd.c} (99%) rename rt-thread/components/drivers/sdio/{sdio.c => dev_sdio.c} (99%) delete mode 100644 rt-thread/components/drivers/sdio/gpt.c create mode 100644 rt-thread/components/drivers/sdio/sdhci/fit-mmc.c create mode 100644 rt-thread/components/drivers/sdio/sdhci/include/sdhci-platform.h create mode 100644 rt-thread/components/drivers/sdio/sdhci/include/sdhci.h create mode 100644 rt-thread/components/drivers/sdio/sdhci/include/sdhci_host.h create mode 100644 rt-thread/components/drivers/sdio/sdhci/include/sdhci_misc.h create mode 100644 rt-thread/components/drivers/sdio/sdhci/sdhci-platform.c create mode 100644 rt-thread/components/drivers/sdio/sdhci/sdhci.c create mode 100644 rt-thread/components/drivers/serial/bypass.c rename rt-thread/components/drivers/serial/{serial.c => dev_serial.c} (96%) rename rt-thread/components/drivers/serial/{serial_v2.c => dev_serial_v2.c} (99%) create mode 100644 rt-thread/components/drivers/smp_call/SConscript create mode 100644 rt-thread/components/drivers/smp_call/smp_call.c create mode 100644 rt-thread/components/drivers/smp_call/smp_call.h rename rt-thread/components/drivers/spi/{qspi_core.c => dev_qspi_core.c} (93%) rename rt-thread/components/drivers/spi/{spi_dev.c => dev_spi.c} (69%) rename rt-thread/components/drivers/spi/{spi-bit-ops.c => dev_spi_bit_ops.c} (90%) rename rt-thread/components/drivers/spi/{spi-bit-ops.h => dev_spi_bit_ops.h} (95%) create mode 100644 rt-thread/components/drivers/spi/dev_spi_bus.c rename rt-thread/components/drivers/spi/{spi_core.c => dev_spi_core.c} (89%) create mode 100644 rt-thread/components/drivers/spi/dev_spi_dm.c create mode 100644 rt-thread/components/drivers/spi/dev_spi_dm.h rename rt-thread/components/drivers/spi/{spi_flash.h => dev_spi_flash.h} (93%) rename rt-thread/components/drivers/spi/{spi_flash_sfud.c => dev_spi_flash_sfud.c} (99%) rename rt-thread/components/drivers/spi/{spi_flash_sfud.h => dev_spi_flash_sfud.h} (88%) rename rt-thread/components/drivers/spi/{spi_msd.c => dev_spi_msd.c} (99%) rename rt-thread/components/drivers/spi/{spi_msd.h => dev_spi_msd.h} (97%) rename rt-thread/components/drivers/spi/{spi_wifi_rw009.c => dev_spi_wifi_rw009.c} (99%) rename rt-thread/components/drivers/spi/{spi_wifi_rw009.h => dev_spi_wifi_rw009.h} (98%) create mode 100644 rt-thread/components/drivers/thermal/Kconfig create mode 100644 rt-thread/components/drivers/thermal/SConscript create mode 100644 rt-thread/components/drivers/thermal/thermal-cool-pwm-fan.c create mode 100644 rt-thread/components/drivers/thermal/thermal.c create mode 100644 rt-thread/components/drivers/thermal/thermal_dm.c create mode 100644 rt-thread/components/drivers/thermal/thermal_dm.h rename rt-thread/components/drivers/touch/{touch.c => dev_touch.c} (100%) delete mode 100644 rt-thread/components/drivers/usb/cherryusb/Kconfig.cherryusb create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/adb/usbd_adb.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/adb/usbd_adb.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/aoa/usb_aoa.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/aoa/usbh_aoa.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/aoa/usbh_aoa.h rename rt-thread/components/drivers/usb/cherryusb/class/cdc/{usbd_cdc.c => usbd_cdc_acm.c} (94%) create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_acm.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/README.md create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/usbh_bl616.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/usbh_bl616.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/vendor/xbox/usbh_xbox.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/class/vendor/xbox/usbh_xbox.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/common/usb_version.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/adb/cherryadb.png create mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/adb/cherrysh_port.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/adb/usbd_adb_template.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2_config.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/cherryuf2.png create mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/msc_bootuf2_template.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/hid_remote_wakeup_template.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/webusb_hid_template.c delete mode 100644 rt-thread/components/drivers/usb/cherryusb/demo/webusb_template.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/idf_component.yml create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/chipidea/README.md create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_chipidea_reg.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_dc_chipidea.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_glue_mcx.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_kendryte.c delete mode 100644 rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_ehci_priv.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_ehci_reg.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_mcx.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/kinetis/README.md create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_dc_kinetis.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_glue_mcx.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_hc_kinetis.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_kinetis_reg.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_glue_lpc.c delete mode 100644 rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_ohci_priv.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_ohci_reg.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/README.md create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_dc_a32_hardfp.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_dc_a32_softfp_crypto_neon.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_dc_a32_softfp_neon.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_dc_a64.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_hc_a32_hardfp.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_hc_a32_softfp_crypto_neon.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_hc_a32_softfp_neon.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_hc_a64.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/rt-thread/usb_config.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/rt-thread/usb_dc_glue_phytium.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/pusb2/rt-thread/usb_hc_glue_phytium.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/xhci/phytium/README.md create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/xhci/phytium/libxhci_a32_hardfp.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/xhci/phytium/libxhci_a32_softfp_crypto_neon.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/xhci/phytium/libxhci_a32_softfp_neon.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/xhci/phytium/libxhci_a64.a create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/xhci/phytium/rt-thread/usb_config.h create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/xhci/phytium/rt-thread/usb_glue_phytium.c create mode 100644 rt-thread/components/drivers/usb/cherryusb/port/xhci/phytium/rt-thread/usb_glue_phytium_plat.c rename rt-thread/components/drivers/watchdog/{watchdog.c => dev_watchdog.c} (98%) rename rt-thread/components/drivers/wlan/{wlan_dev.c => dev_wlan.c} (93%) rename rt-thread/components/drivers/wlan/{wlan_dev.h => dev_wlan.h} (96%) rename rt-thread/components/drivers/wlan/{wlan_cfg.c => dev_wlan_cfg.c} (99%) rename rt-thread/components/drivers/wlan/{wlan_cfg.h => dev_wlan_cfg.h} (94%) rename rt-thread/components/drivers/wlan/{wlan_cmd.c => dev_wlan_cmd.c} (99%) rename rt-thread/components/drivers/wlan/{wlan_lwip.c => dev_wlan_lwip.c} (99%) rename rt-thread/components/drivers/wlan/{wlan_mgnt.c => dev_wlan_mgnt.c} (98%) rename rt-thread/components/drivers/wlan/{wlan_mgnt.h => dev_wlan_mgnt.h} (98%) rename rt-thread/components/drivers/wlan/{wlan_prot.c => dev_wlan_prot.c} (99%) rename rt-thread/components/drivers/wlan/{wlan_prot.h => dev_wlan_prot.h} (97%) rename rt-thread/components/drivers/wlan/{wlan_workqueue.c => dev_wlan_workqueue.c} (98%) rename rt-thread/components/drivers/wlan/{wlan_workqueue.h => dev_wlan_workqueue.h} (92%) create mode 100644 rt-thread/components/lwp/arch/aarch64/common/vdso.c create mode 100644 rt-thread/components/lwp/arch/aarch64/common/vdso_data.c create mode 100644 rt-thread/components/lwp/lwp_runtime.c create mode 100644 rt-thread/components/lwp/vdso/Kconfig create mode 100644 rt-thread/components/lwp/vdso/SConscript create mode 100644 rt-thread/components/lwp/vdso/kernel/vdso.h create mode 100644 rt-thread/components/lwp/vdso/kernel/vdso_data.h create mode 100644 rt-thread/components/lwp/vdso/kernel/vdso_text.S create mode 100644 rt-thread/components/lwp/vdso/user/SConstruct create mode 100644 rt-thread/components/lwp/vdso/user/vdso.lds.S create mode 100644 rt-thread/components/lwp/vdso/user/vdso_sys.c create mode 100644 rt-thread/components/lwp/vdso/user/vdso_sys.h create mode 100644 rt-thread/components/lwp/vdso/user/xmake.lua create mode 100644 rt-thread/components/lwp/vdso/vdso_config.h create mode 100644 rt-thread/components/lwp/vdso/vdso_datapage.h create mode 100644 rt-thread/components/lwp/vdso/vdso_weak.c create mode 100644 rt-thread/components/utilities/utest/TC_uassert.c create mode 100644 rt-thread/include/klibc/kerrno.h create mode 100644 rt-thread/include/klibc/kstdio.h create mode 100644 rt-thread/include/klibc/kstring.h create mode 100644 rt-thread/libcpu/arm/cortex-m23/cpuport.h create mode 100644 rt-thread/libcpu/arm/cortex-m4/README.md create mode 100644 rt-thread/src/defunct.c create mode 100644 rt-thread/src/klibc/Kconfig create mode 100644 rt-thread/src/klibc/SConscript create mode 100644 rt-thread/src/klibc/kerrno.c create mode 100644 rt-thread/src/klibc/rt_vsnprintf_std.c create mode 100644 rt-thread/src/klibc/rt_vsnprintf_tiny.c create mode 100644 rt-thread/src/klibc/rt_vsscanf.c create mode 100644 rt-thread/src/klibc/utest/SConscript create mode 100644 rt-thread/src/klibc/utest/TC_rt_memcmp.c create mode 100644 rt-thread/src/klibc/utest/TC_rt_memcpy.c create mode 100644 rt-thread/src/klibc/utest/TC_rt_memmove.c create mode 100644 rt-thread/src/klibc/utest/TC_rt_memset.c create mode 100644 rt-thread/src/klibc/utest/TC_rt_sprintf.c create mode 100644 rt-thread/src/klibc/utest/TC_rt_sscanf.c create mode 100644 rt-thread/tools/ci/bsp_detail.py create mode 100644 rt-thread/tools/ci/bsp_detail.yml create mode 100644 rt-thread/tools/ci/install.sh create mode 100644 rt-thread/tools/ci/manual_bsp_build_all.py create mode 100644 rt-thread/tools/ci/requirements.txt create mode 100644 rt-thread/tools/ci/toolchain.sh create mode 100644 rt-thread/tools/ci/toolchain_bsp.yml delete mode 100644 rt-thread/tools/env.py create mode 100644 rt-thread/tools/env_utility.py delete mode 100644 rt-thread/tools/install_env.py delete mode 100644 rt-thread/tools/menukconfig.py create mode 100644 rt-thread/tools/zigbuild.py diff --git a/.config b/.config index f7bff01..f870e88 100644 --- a/.config +++ b/.config @@ -4,10 +4,126 @@ CONFIG_BOARD_STM32F407_SPARK=y # # RT-Thread Kernel # + +# +# klibc options +# + +# +# rt_vsnprintf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSNPRINTF is not set +CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER=y +# CONFIG_RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS is not set +CONFIG_RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE=32 +CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE=32 +CONFIG_RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION=6 +CONFIG_RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9 +CONFIG_RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS=4 +# end of rt_vsnprintf options + +# +# rt_vsscanf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSSCANF is not set +# end of rt_vsscanf options + +# +# rt_memset options +# +# CONFIG_RT_KLIBC_USING_USER_MEMSET is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMSET is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMSET is not set +# end of rt_memset options + +# +# rt_memcpy options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMCPY is not set +# end of rt_memcpy options + +# +# rt_memmove options +# +# CONFIG_RT_KLIBC_USING_USER_MEMMOVE is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMMOVE is not set +# end of rt_memmove options + +# +# rt_memcmp options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCMP is not set +# end of rt_memcmp options + +# +# rt_strstr options +# +# CONFIG_RT_KLIBC_USING_USER_STRSTR is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRSTR is not set +# end of rt_strstr options + +# +# rt_strcasecmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCASECMP is not set +# end of rt_strcasecmp options + +# +# rt_strncpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCPY is not set +# end of rt_strncpy options + +# +# rt_strcpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCPY is not set +# end of rt_strcpy options + +# +# rt_strncmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCMP is not set +# end of rt_strncmp options + +# +# rt_strcmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCMP is not set +# end of rt_strcmp options + +# +# rt_strlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRLEN is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRLEN is not set +# end of rt_strlen options + +# +# rt_strnlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRNLEN is not set +# end of rt_strnlen options + +# CONFIG_RT_UTEST_TC_USING_KLIBC is not set +# end of klibc options + CONFIG_RT_NAME_MAX=8 # CONFIG_RT_USING_ARCH_DATA_TYPE is not set -# CONFIG_RT_USING_SMART is not set # CONFIG_RT_USING_NANO is not set +# CONFIG_RT_USING_SMART is not set # CONFIG_RT_USING_AMP is not set # CONFIG_RT_USING_SMP is not set CONFIG_RT_CPUS_NR=1 @@ -17,6 +133,7 @@ CONFIG_RT_THREAD_PRIORITY_32=y # CONFIG_RT_THREAD_PRIORITY_256 is not set CONFIG_RT_THREAD_PRIORITY_MAX=32 CONFIG_RT_TICK_PER_SECOND=1000 +CONFIG_RT_USING_OVERFLOW_CHECK=y CONFIG_RT_USING_HOOK=y CONFIG_RT_HOOK_USING_FUNC_PTR=y # CONFIG_RT_USING_HOOKLIST is not set @@ -30,25 +147,17 @@ CONFIG_RT_TIMER_THREAD_STACK_SIZE=512 # CONFIG_RT_USING_CPU_USAGE_TRACER is not set # -# kservice optimization +# kservice options # # CONFIG_RT_USING_TINY_FFS is not set -# end of kservice optimization - -# -# klibc optimization -# -# CONFIG_RT_KLIBC_USING_STDLIB is not set -# CONFIG_RT_KLIBC_USING_TINY_SIZE is not set -# CONFIG_RT_KLIBC_USING_PRINTF_LONGLONG is not set -# end of klibc optimization +# end of kservice options CONFIG_RT_USING_DEBUG=y CONFIG_RT_DEBUGING_ASSERT=y CONFIG_RT_DEBUGING_COLOR=y CONFIG_RT_DEBUGING_CONTEXT=y # CONFIG_RT_DEBUGING_AUTO_INIT is not set -CONFIG_RT_USING_OVERFLOW_CHECK=y +# CONFIG_RT_USING_CI_ACTION is not set # # Inter-Thread communication @@ -83,7 +192,6 @@ CONFIG_RT_USING_DEVICE=y # CONFIG_RT_USING_DEVICE_OPS is not set # CONFIG_RT_USING_INTERRUPT_INFO is not set # CONFIG_RT_USING_THREADSAFE_PRINTF is not set -# CONFIG_RT_USING_SCHED_THREAD_CTX is not set CONFIG_RT_USING_CONSOLE=y CONFIG_RT_CONSOLEBUF_SIZE=128 CONFIG_RT_CONSOLE_DEVICE_NAME="uart1" @@ -172,8 +280,7 @@ CONFIG_RT_USING_DFS_ROMFS=y # end of DFS: device virtual file system CONFIG_RT_USING_FAL=y -CONFIG_FAL_DEBUG_CONFIG=y -CONFIG_FAL_DEBUG=1 +CONFIG_FAL_USING_DEBUG=y CONFIG_FAL_PART_HAS_TABLE_CFG=y CONFIG_FAL_USING_SFUD_PORT=y CONFIG_FAL_USING_NOR_FLASH_DEV_NAME="norflash0" @@ -193,6 +300,7 @@ CONFIG_RT_USING_SERIAL_V1=y # CONFIG_RT_USING_SERIAL_V2 is not set CONFIG_RT_SERIAL_USING_DMA=y CONFIG_RT_SERIAL_RB_BUFSZ=64 +# CONFIG_RT_USING_SERIAL_BYPASS is not set # CONFIG_RT_USING_CAN is not set # CONFIG_RT_USING_CPUTIME is not set CONFIG_RT_USING_I2C=y @@ -201,6 +309,7 @@ CONFIG_RT_USING_I2C_BITOPS=y # CONFIG_RT_I2C_BITOPS_DEBUG is not set # CONFIG_RT_USING_SOFT_I2C is not set # CONFIG_RT_USING_PHY is not set +# CONFIG_RT_USING_PHY_V2 is not set CONFIG_RT_USING_ADC=y # CONFIG_RT_USING_DAC is not set # CONFIG_RT_USING_NULL is not set @@ -222,6 +331,7 @@ CONFIG_RT_MMCSD_STACK_SIZE=1024 CONFIG_RT_MMCSD_THREAD_PREORITY=22 CONFIG_RT_MMCSD_MAX_PARTITION=16 # CONFIG_RT_SDIO_DEBUG is not set +# CONFIG_RT_USING_SDHCI is not set CONFIG_RT_USING_SPI=y # CONFIG_RT_USING_SPI_BITOPS is not set # CONFIG_RT_USING_QSPI is not set @@ -270,6 +380,15 @@ CONFIG_RT_WLAN_WORKQUEUE_THREAD_NAME="wlan" CONFIG_RT_WLAN_WORKQUEUE_THREAD_SIZE=2048 CONFIG_RT_WLAN_WORKQUEUE_THREAD_PRIO=15 # CONFIG_RT_WLAN_DEBUG is not set +CONFIG_RT_USING_BLK=y + +# +# Partition Types +# +CONFIG_RT_BLK_PARTITION_DFS=y +CONFIG_RT_BLK_PARTITION_EFI=y +# end of Partition Types + # CONFIG_RT_USING_VIRTIO is not set CONFIG_RT_USING_PIN=y # CONFIG_RT_USING_KTIME is not set @@ -355,6 +474,7 @@ CONFIG_NETDEV_USING_IFCONFIG=y CONFIG_NETDEV_USING_PING=y CONFIG_NETDEV_USING_NETSTAT=y CONFIG_NETDEV_USING_AUTO_DEFAULT=y +# CONFIG_NETDEV_USING_LINK_STATUS_CALLBACK is not set # CONFIG_NETDEV_USING_IPV6 is not set CONFIG_NETDEV_IPV4=1 CONFIG_NETDEV_IPV6=0 @@ -418,6 +538,7 @@ CONFIG_LWIP_NETIF_LOOPBACK=0 # CONFIG_RT_LWIP_USING_HW_CHECKSUM is not set CONFIG_RT_LWIP_USING_PING=y # CONFIG_LWIP_USING_DHCPD is not set +# CONFIG_RT_LWIP_ENABLE_USER_HOOKS is not set # CONFIG_RT_LWIP_DEBUG is not set CONFIG_RT_USING_AT=y CONFIG_AT_DEBUG=y @@ -955,6 +1076,7 @@ CONFIG_PKG_PERF_COUNTER_PATH="/packages/system/perf_counter" CONFIG_PKG_USING_PERF_COUNTER_V2241=y # CONFIG_PKG_USING_PERF_COUNTER_LATEST_VERSION is not set CONFIG_PKG_PERF_COUNTER_VER="v2.2.4.1" +CONFIG_FAL_DEBUG_CONFIG=y # CONFIG_PKG_USING_FILEX is not set # CONFIG_PKG_USING_LEVELX is not set # CONFIG_PKG_USING_FLASHDB is not set diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index f5dfcb1..824a4ca 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -4,12 +4,379 @@ "name": "rt-thread", "defines": [ "RT_USING_LIBC", - "RT_USING_NEWLIBC", - "STM32F407xx", "USE_HAL_DRIVER", - "_POSIX_C_SOURCE=1", "__RTTHREAD__", - "__perf_counter_printf__=rt_kprintf" + "__STDC_LIMIT_MACROS", + "__CLK_TCK=RT_TICK_PER_SECOND", + "STM32F407xx", + "RT_USING_ARMLIBC", + "__alignof__(x)=", + "__asm(x)=", + "__asm__(x)=", + "__forceinline=", + "__restrict=", + "__volatile__=", + "__inline=", + "__inline__=", + "__declspec(x)=", + "__attribute__(x)=", + "__nonnull__(x)=", + "__unaligned=", + "__promise(x)=", + "__irq=", + "__swi=", + "__weak=", + "__register=", + "__pure=", + "__value_in_regs=", + "__breakpoint(x)=", + "__current_pc()=0U", + "__current_sp()=0U", + "__disable_fiq()=", + "__disable_irq()=", + "__enable_fiq()=", + "__enable_irq()=", + "__force_stores()=", + "__memory_changed()=", + "__schedule_barrier()=", + "__semihost(x,y)=0", + "__vfp_status(x,y)=0", + "__builtin_arm_nop()=", + "__builtin_arm_wfi()=", + "__builtin_arm_wfe()=", + "__builtin_arm_sev()=", + "__builtin_arm_sevl()=", + "__builtin_arm_yield()=", + "__builtin_arm_isb(x)=", + "__builtin_arm_dsb(x)=", + "__builtin_arm_dmb(x)=", + "__builtin_bswap32(x)=0U", + "__builtin_bswap16(x)=0U", + "__builtin_arm_rbit(x)=0U", + "__builtin_clz(x)=0U", + "__builtin_arm_ldrex(x)=0U", + "__builtin_arm_strex(x,y)=0U", + "__builtin_arm_clrex()=", + "__builtin_arm_ssat(x,y)=0U", + "__builtin_arm_usat(x,y)=0U", + "__builtin_arm_ldaex(x)=0U", + "__builtin_arm_stlex(x,y)=0U", + "_ILP32=1", + "_USE_STATIC_INLINE=1", + "__APCS_32__=1", + "__ARMCC_VERSION=6070001", + "__ARMCOMPILER_VERSION=6070001", + "__ARMEL__=1", + "__ARM_32BIT_STATE=1", + "__ARM_ACLE=200", + "__ARM_ARCH=4", + "__ARM_ARCH_4T__=1", + "__ARM_ARCH_ISA_ARM=1", + "__ARM_ARCH_ISA_THUMB=1", + "__ARM_EABI__=1", + "__ARM_FP16_ARGS=1", + "__ARM_FP16_FORMAT_IEEE=1", + "__ARM_NO_IMAGINARY_TYPE=1", + "__ARM_PCS=1", + "__ARM_PROMISE=__builtin_assume", + "__ARM_SIZEOF_MINIMAL_ENUM=4", + "__ARM_SIZEOF_WCHAR_T=4", + "__ARM_TARGET_COPROC=1", + "__ARM_TARGET_COPROC_V4=1", + "__ATOMIC_ACQUIRE=2", + "__ATOMIC_ACQ_REL=4", + "__ATOMIC_CONSUME=1", + "__ATOMIC_RELAXED=0", + "__ATOMIC_RELEASE=3", + "__ATOMIC_SEQ_CST=5", + "__BIGGEST_ALIGNMENT__=8", + "__BYTE_ORDER__=__ORDER_LITTLE_ENDIAN__", + "__CHAR16_TYPE__=unsigned short", + "__CHAR32_TYPE__=unsigned int", + "__CHAR_BIT__=8", + "__CHAR_UNSIGNED__=1", + "__CONSTANT_CFSTRINGS__=1", + "__DBL_DECIMAL_DIG__=17", + "__DBL_DENORM_MIN__=4.9406564584124654e-324", + "__DBL_DIG__=15", + "__DBL_EPSILON__=2.2204460492503131e-16", + "__DBL_HAS_DENORM__=1", + "__DBL_HAS_INFINITY__=1", + "__DBL_HAS_QUIET_NAN__=1", + "__DBL_MANT_DIG__=53", + "__DBL_MAX_10_EXP__=308", + "__DBL_MAX_EXP__=1024", + "__DBL_MAX__=1.7976931348623157e+308", + "__DBL_MIN_10_EXP__=(-307)", + "__DBL_MIN_EXP__=(-1021)", + "__DBL_MIN__=2.2250738585072014e-308", + "__DECIMAL_DIG__=__LDBL_DECIMAL_DIG__", + "__ELF__=1", + "__ESCAPE__=", + "__FINITE_MATH_ONLY__=1", + "__FLT_DECIMAL_DIG__=9", + "__FLT_DENORM_MIN__=1.40129846e-45F", + "__FLT_DIG__=6", + "__FLT_EPSILON__=1.19209290e-7F", + "__FLT_EVAL_METHOD__=0", + "__FLT_HAS_DENORM__=1", + "__FLT_HAS_INFINITY__=1", + "__FLT_HAS_QUIET_NAN__=1", + "__FLT_MANT_DIG__=24", + "__FLT_MAX_10_EXP__=38", + "__FLT_MAX_EXP__=128", + "__FLT_MAX__=3.40282347e+38F", + "__FLT_MIN_10_EXP__=(-37)", + "__FLT_MIN_EXP__=(-125)", + "__FLT_MIN__=1.17549435e-38F", + "__FLT_RADIX__=2", + "__GCC_ATOMIC_BOOL_LOCK_FREE=1", + "__GCC_ATOMIC_CHAR16_T_LOCK_FREE=1", + "__GCC_ATOMIC_CHAR32_T_LOCK_FREE=1", + "__GCC_ATOMIC_CHAR_LOCK_FREE=1", + "__GCC_ATOMIC_INT_LOCK_FREE=1", + "__GCC_ATOMIC_LLONG_LOCK_FREE=1", + "__GCC_ATOMIC_LONG_LOCK_FREE=1", + "__GCC_ATOMIC_POINTER_LOCK_FREE=1", + "__GCC_ATOMIC_SHORT_LOCK_FREE=1", + "__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1", + "__GCC_ATOMIC_WCHAR_T_LOCK_FREE=1", + "__GNUC_MINOR__=2", + "__GNUC_PATCHLEVEL__=1", + "__GNUC_STDC_INLINE__=1", + "__GNUC__=4", + "__GXX_ABI_VERSION=1002", + "__ILP32__=1", + "__INT16_C_SUFFIX__=", + "__INT16_FMTd__=\"hd\"", + "__INT16_FMTi__=\"hi\"", + "__INT16_MAX__=32767", + "__INT16_TYPE__=short", + "__INT32_C_SUFFIX__=", + "__INT32_FMTd__=\"d\"", + "__INT32_FMTi__=\"i\"", + "__INT32_MAX__=2147483647", + "__INT32_TYPE__=int", + "__INT64_C_SUFFIX__=LL", + "__INT64_FMTd__=\"lld\"", + "__INT64_FMTi__=\"lli\"", + "__INT64_MAX__=9223372036854775807LL", + "__INT64_TYPE__=long long int", + "__INT8_C_SUFFIX__=", + "__INT8_FMTd__=\"hhd\"", + "__INT8_FMTi__=\"hhi\"", + "__INT8_MAX__=127", + "__INT8_TYPE__=signed char", + "__INTMAX_C_SUFFIX__=LL", + "__INTMAX_FMTd__=\"lld\"", + "__INTMAX_FMTi__=\"lli\"", + "__INTMAX_MAX__=9223372036854775807LL", + "__INTMAX_TYPE__=long long int", + "__INTMAX_WIDTH__=64", + "__INTPTR_FMTd__=\"ld\"", + "__INTPTR_FMTi__=\"li\"", + "__INTPTR_MAX__=2147483647L", + "__INTPTR_TYPE__=long int", + "__INTPTR_WIDTH__=32", + "__INT_FAST16_FMTd__=\"hd\"", + "__INT_FAST16_FMTi__=\"hi\"", + "__INT_FAST16_MAX__=32767", + "__INT_FAST16_TYPE__=short", + "__INT_FAST32_FMTd__=\"d\"", + "__INT_FAST32_FMTi__=\"i\"", + "__INT_FAST32_MAX__=2147483647", + "__INT_FAST32_TYPE__=int", + "__INT_FAST64_FMTd__=\"lld\"", + "__INT_FAST64_FMTi__=\"lli\"", + "__INT_FAST64_MAX__=9223372036854775807LL", + "__INT_FAST64_TYPE__=long long int", + "__INT_FAST8_FMTd__=\"hhd\"", + "__INT_FAST8_FMTi__=\"hhi\"", + "__INT_FAST8_MAX__=127", + "__INT_FAST8_TYPE__=signed char", + "__INT_LEAST16_FMTd__=\"hd\"", + "__INT_LEAST16_FMTi__=\"hi\"", + "__INT_LEAST16_MAX__=32767", + "__INT_LEAST16_TYPE__=short", + "__INT_LEAST32_FMTd__=\"d\"", + "__INT_LEAST32_FMTi__=\"i\"", + "__INT_LEAST32_MAX__=2147483647", + "__INT_LEAST32_TYPE__=int", + "__INT_LEAST64_FMTd__=\"lld\"", + "__INT_LEAST64_FMTi__=\"lli\"", + "__INT_LEAST64_MAX__=9223372036854775807LL", + "__INT_LEAST64_TYPE__=long long int", + "__INT_LEAST8_FMTd__=\"hhd\"", + "__INT_LEAST8_FMTi__=\"hhi\"", + "__INT_LEAST8_MAX__=127", + "__INT_LEAST8_TYPE__=signed char", + "__INT_MAX__=2147483647", + "__I__=1.0if", + "__LDBL_DECIMAL_DIG__=17", + "__LDBL_DENORM_MIN__=4.9406564584124654e-324L", + "__LDBL_DIG__=15", + "__LDBL_EPSILON__=2.2204460492503131e-16L", + "__LDBL_HAS_DENORM__=1", + "__LDBL_HAS_INFINITY__=1", + "__LDBL_HAS_QUIET_NAN__=1", + "__LDBL_MANT_DIG__=53", + "__LDBL_MAX_10_EXP__=308", + "__LDBL_MAX_EXP__=1024", + "__LDBL_MAX__=1.7976931348623157e+308L", + "__LDBL_MIN_10_EXP__=(-307)", + "__LDBL_MIN_EXP__=(-1021)", + "__LDBL_MIN__=2.2250738585072014e-308L", + "__LITTLE_ENDIAN__=1", + "__LONG_LONG_MAX__=9223372036854775807LL", + "__LONG_MAX__=2147483647L", + "__NO_INLINE__=1", + "__OBJC_BOOL_IS_BOOL=0", + "__ORDER_BIG_ENDIAN__=4321", + "__ORDER_LITTLE_ENDIAN__=1234", + "__ORDER_PDP_ENDIAN__=3412", + "__POINTER_WIDTH__=32", + "__PRAGMA_REDEFINE_EXTNAME=1", + "__PTRDIFF_FMTd__=\"d\"", + "__PTRDIFF_FMTi__=\"i\"", + "__PTRDIFF_MAX__=2147483647", + "__PTRDIFF_TYPE__=int", + "__PTRDIFF_WIDTH__=32", + "__REGISTER_PREFIX__=", + "__SCHAR_MAX__=127", + "__SHRT_MAX__=32767", + "__SIG_ATOMIC_MAX__=2147483647", + "__SIG_ATOMIC_WIDTH__=32", + "__SIZEOF_DOUBLE__=8", + "__SIZEOF_FLOAT__=4", + "__SIZEOF_INT__=4", + "__SIZEOF_LONG_DOUBLE__=8", + "__SIZEOF_LONG_LONG__=8", + "__SIZEOF_LONG__=4", + "__SIZEOF_POINTER__=4", + "__SIZEOF_PTRDIFF_T__=4", + "__SIZEOF_SHORT__=2", + "__SIZEOF_SIZE_T__=4", + "__SIZEOF_WCHAR_T__=4", + "__SIZEOF_WINT_T__=4", + "__SIZE_FMTX__=\"X\"", + "__SIZE_FMTo__=\"o\"", + "__SIZE_FMTu__=\"u\"", + "__SIZE_FMTx__=\"x\"", + "__SIZE_MAX__=4294967295U", + "__SIZE_TYPE__=unsigned int", + "__SIZE_WIDTH__=32", + "__STDC_HOSTED__=1", + "__STDC_UTF_16__=1", + "__STDC_UTF_32__=1", + "__STDC_VERSION__=201112L", + "__STDC__=1", + "__UINT16_C_SUFFIX__=", + "__UINT16_FMTX__=\"hX\"", + "__UINT16_FMTo__=\"ho\"", + "__UINT16_FMTu__=\"hu\"", + "__UINT16_FMTx__=\"hx\"", + "__UINT16_MAX__=65535", + "__UINT16_TYPE__=unsigned short", + "__UINT32_C_SUFFIX__=U", + "__UINT32_FMTX__=\"X\"", + "__UINT32_FMTo__=\"o\"", + "__UINT32_FMTu__=\"u\"", + "__UINT32_FMTx__=\"x\"", + "__UINT32_MAX__=4294967295U", + "__UINT32_TYPE__=unsigned int", + "__UINT64_C_SUFFIX__=ULL", + "__UINT64_FMTX__=\"llX\"", + "__UINT64_FMTo__=\"llo\"", + "__UINT64_FMTu__=\"llu\"", + "__UINT64_FMTx__=\"llx\"", + "__UINT64_MAX__=18446744073709551615ULL", + "__UINT64_TYPE__=long long unsigned int", + "__UINT8_C_SUFFIX__=", + "__UINT8_FMTX__=\"hhX\"", + "__UINT8_FMTo__=\"hho\"", + "__UINT8_FMTu__=\"hhu\"", + "__UINT8_FMTx__=\"hhx\"", + "__UINT8_MAX__=255", + "__UINT8_TYPE__=unsigned char", + "__UINTMAX_C_SUFFIX__=ULL", + "__UINTMAX_FMTX__=\"llX\"", + "__UINTMAX_FMTo__=\"llo\"", + "__UINTMAX_FMTu__=\"llu\"", + "__UINTMAX_FMTx__=\"llx\"", + "__UINTMAX_MAX__=18446744073709551615ULL", + "__UINTMAX_TYPE__=long long unsigned int", + "__UINTMAX_WIDTH__=64", + "__UINTPTR_FMTX__=\"lX\"", + "__UINTPTR_FMTo__=\"lo\"", + "__UINTPTR_FMTu__=\"lu\"", + "__UINTPTR_FMTx__=\"lx\"", + "__UINTPTR_MAX__=4294967295UL", + "__UINTPTR_TYPE__=long unsigned int", + "__UINTPTR_WIDTH__=32", + "__UINT_FAST16_FMTX__=\"hX\"", + "__UINT_FAST16_FMTo__=\"ho\"", + "__UINT_FAST16_FMTu__=\"hu\"", + "__UINT_FAST16_FMTx__=\"hx\"", + "__UINT_FAST16_MAX__=65535", + "__UINT_FAST16_TYPE__=unsigned short", + "__UINT_FAST32_FMTX__=\"X\"", + "__UINT_FAST32_FMTo__=\"o\"", + "__UINT_FAST32_FMTu__=\"u\"", + "__UINT_FAST32_FMTx__=\"x\"", + "__UINT_FAST32_MAX__=4294967295U", + "__UINT_FAST32_TYPE__=unsigned int", + "__UINT_FAST64_FMTX__=\"llX\"", + "__UINT_FAST64_FMTo__=\"llo\"", + "__UINT_FAST64_FMTu__=\"llu\"", + "__UINT_FAST64_FMTx__=\"llx\"", + "__UINT_FAST64_MAX__=18446744073709551615ULL", + "__UINT_FAST64_TYPE__=long long unsigned int", + "__UINT_FAST8_FMTX__=\"hhX\"", + "__UINT_FAST8_FMTo__=\"hho\"", + "__UINT_FAST8_FMTu__=\"hhu\"", + "__UINT_FAST8_FMTx__=\"hhx\"", + "__UINT_FAST8_MAX__=255", + "__UINT_FAST8_TYPE__=unsigned char", + "__UINT_LEAST16_FMTX__=\"hX\"", + "__UINT_LEAST16_FMTo__=\"ho\"", + "__UINT_LEAST16_FMTu__=\"hu\"", + "__UINT_LEAST16_FMTx__=\"hx\"", + "__UINT_LEAST16_MAX__=65535", + "__UINT_LEAST16_TYPE__=unsigned short", + "__UINT_LEAST32_FMTX__=\"X\"", + "__UINT_LEAST32_FMTo__=\"o\"", + "__UINT_LEAST32_FMTu__=\"u\"", + "__UINT_LEAST32_FMTx__=\"x\"", + "__UINT_LEAST32_MAX__=4294967295U", + "__UINT_LEAST32_TYPE__=unsigned int", + "__UINT_LEAST64_FMTX__=\"llX\"", + "__UINT_LEAST64_FMTo__=\"llo\"", + "__UINT_LEAST64_FMTu__=\"llu\"", + "__UINT_LEAST64_FMTx__=\"llx\"", + "__UINT_LEAST64_MAX__=18446744073709551615ULL", + "__UINT_LEAST64_TYPE__=long long unsigned int", + "__UINT_LEAST8_FMTX__=\"hhX\"", + "__UINT_LEAST8_FMTo__=\"hho\"", + "__UINT_LEAST8_FMTu__=\"hhu\"", + "__UINT_LEAST8_FMTx__=\"hhx\"", + "__UINT_LEAST8_MAX__=255", + "__UINT_LEAST8_TYPE__=unsigned char", + "__USER_LABEL_PREFIX__=", + "__VERSION__=\"4.2.1 Compatible Clang 5.0.0 \"", + "__WCHAR_MAX__=4294967295U", + "__WCHAR_TYPE__=unsigned int", + "__WCHAR_UNSIGNED__=1", + "__WCHAR_WIDTH__=32", + "__WINT_TYPE__=int", + "__WINT_WIDTH__=32", + "__arm=1", + "__arm__=1", + "__clang__=1", + "__clang_major__=5", + "__clang_minor__=0", + "__clang_patchlevel__=0", + "__clang_version__=\"5.0.0 \"", + "__llvm__=1" ], "intelliSenseMode": "gcc-arm", "compilerPath": "d:/DevTools/env2/tools/gnu_gcc/arm_gcc/mingw/bin/arm-none-eabi-gcc", @@ -19,78 +386,45 @@ "build/compile_commands.json" ], "includePath": [ - "packages\\aht10-latest\\inc", - "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra", - "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\mqtt\\impl", - "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\mqtt", - "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model", - "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\client", - "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\server", - "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_sign", - "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\wrappers", - "packages\\ap3216c-latest", - "applications", - "my_pro", - "rt-thread\\components\\net\\at\\include", - "packages\\cJSON-v1.7.17", - "rt-thread\\components\\libc\\compilers\\common\\include", - "rt-thread\\components\\libc\\compilers\\newlib", - "rt-thread\\components\\libc\\cplusplus", - "rt-thread\\components\\drivers\\include", - "rt-thread\\components\\drivers\\spi", - "rt-thread\\components\\drivers\\spi\\sfud\\inc", - "rt-thread\\components\\drivers\\wlan", - "board", - "board\\CubeMX_Config\\Inc", - "board\\ports", - "board\\ports\\fal", - "board\\ports\\lcd", - "board\\ports\\led_matrix", - "libraries\\HAL_Drivers\\drivers", - "libraries\\HAL_Drivers\\drivers\\config", - "libraries\\HAL_Drivers\\drivers\\drv_flash", - "libraries\\HAL_Drivers", - "libraries\\HAL_Drivers\\CMSIS\\Include", - "rt-thread\\components\\fal\\inc", - "rt-thread\\components\\dfs\\dfs_v1\\include", - "rt-thread\\components\\dfs\\dfs_v1\\filesystems\\devfs", - "rt-thread\\components\\dfs\\dfs_v1\\filesystems\\elmfat", - "rt-thread\\components\\dfs\\dfs_v1\\filesystems\\romfs", - "rt-thread\\components\\finsh", - "packages\\FlexibleButton-v1.0.0", - "packages\\icm20608-latest", - "packages\\infrared-v0.1.1\\inc", - ".", - "rt-thread\\include", - "packages\\kernel_samples-latest\\en", - "rt-thread\\components\\legacy", - "rt-thread\\components\\legacy\\dfs", - "rt-thread\\libcpu\\arm\\common", - "rt-thread\\libcpu\\arm\\cortex-m4", - "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Inc", - "libraries\\STM32F4xx_HAL\\CMSIS\\Device\\ST\\STM32F4xx\\Include", - "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\include", - "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\include\\ipv4", - "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\include\\netif", - "rt-thread\\components\\net\\lwip\\port", - "packages\\netutils-latest\\ntp", - "packages\\perf_counter-v2.2.4.1", - "rt-thread\\components\\libc\\posix\\io\\epoll", - "rt-thread\\components\\libc\\posix\\io\\eventfd", - "rt-thread\\components\\libc\\posix\\io\\poll", - "rt-thread\\components\\libc\\posix\\ipc", - "board\\ports\\rs485", - "rt-thread\\components\\legacy\\usb\\usbdevice", - "packages\\rw007-v2.1.0", - "packages\\rw007-v2.1.0\\inc", - "rt-thread\\components\\net\\netdev\\include", - "rt-thread\\components\\net\\sal\\include", - "rt-thread\\components\\net\\sal\\include\\socket", - "rt-thread\\components\\net\\sal\\impl", - "rt-thread\\components\\net\\sal\\include\\dfs_net", - "rt-thread\\components\\net\\sal\\include\\socket\\sys_socket", - "rt-thread\\components\\utilities\\ulog", - "packages\\vconsole-latest" + "d:\\components\\drivers\\include", + "d:\\Develop\\libraries\\HAL_Drivers\\drivers\\config", + "d:\\Develop\\libraries\\HAL_Drivers\\drivers", + "d:\\components\\libc\\posix\\io\\eventfd", + "d:\\Develop\\SumProject\\board", + "d:\\Develop\\SumProject\\board\\CubeMX_Config\\Inc", + "d:\\libcpu\\arm\\common", + "d:\\components\\libc\\posix\\ipc", + "d:\\Develop\\libraries\\HAL_Drivers\\CMSIS\\Include", + "d:\\Develop\\SumProject\\board\\ports", + "d:\\Develop\\libraries\\HAL_Drivers", + "d:\\components\\libc\\compilers\\common\\include", + "d:\\libcpu\\arm\\cortex-m4", + "d:\\components\\libc\\posix\\io\\poll", + "d:\\Develop\\SumProject", + "d:\\components\\libc\\posix\\io\\epoll", + "d:\\components\\finsh", + "d:\\Develop\\libraries\\STM32F4xx_HAL\\CMSIS\\Device\\ST\\STM32F4xx\\Include", + "d:\\Develop\\libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Inc", + "d:\\components\\libc\\compilers\\common\\extension", + "d:\\components\\libc\\compilers\\common\\extension\\fcntl\\octal", + "d:\\Develop\\SumProject\\applications", + "d:\\include", + "D:\\Keil5\\ARM\\ARMCLANG\\include", + "D:\\Keil5\\ARM\\ARMCLANG\\include\\libcxx", + "d:\\components\\libc\\compilers\\armlibc", + "d:\\components\\libc\\compilers\\common", + "d:\\components\\drivers\\core", + "d:\\components\\drivers\\i2c", + "d:\\components\\drivers\\ipc", + "d:\\components\\drivers\\misc", + "d:\\components\\drivers\\pin", + "d:\\components\\drivers\\serial", + "d:\\Develop\\libraries\\STM32F4xx_HAL\\CMSIS\\Device\\ST\\STM32F4xx\\Source\\Templates\\arm", + "d:\\Develop\\SumProject\\board\\CubeMX_Config\\Src", + "d:\\src", + "d:\\src\\klibc", + "d:\\Develop\\libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src", + "d:\\Develop\\libraries\\STM32F4xx_HAL\\CMSIS\\Device\\ST\\STM32F4xx\\Source\\Templates" ] } ], diff --git a/.vscode/project.json b/.vscode/project.json new file mode 100644 index 0000000..0d5ac30 --- /dev/null +++ b/.vscode/project.json @@ -0,0 +1,519 @@ +{ + "RT-Thread": "D:\\Develop\\SumProject\\rt-thread", + "Groups": [ + { + "name": "aht10", + "path": "packages\\aht10-latest", + "files": [ + "packages\\aht10-latest\\src\\aht10.c", + "packages\\aht10-latest\\SConscript" + ] + }, + { + "name": "ali-iotkit", + "path": "packages\\ali-iotkit-v3.0.2", + "files": [ + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\mqtt\\impl\\MQTTSerializePublish.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra\\infra_compat.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_ota.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_fota.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_ipc.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra\\infra_log.c", + "packages\\ali-iotkit-v3.0.2\\ports\\rtthread\\HAL_TCP_rtthread.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\mqtt\\impl\\MQTTDeserializePublish.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_sign\\dev_sign_mqtt.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra\\infra_prt_nwk_payload.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\mqtt\\impl\\MQTTConnectClient.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\iotx_cm.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\client\\dm_client.c", + "packages\\ali-iotkit-v3.0.2\\ports\\wrapper.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_log_report.c", + "packages\\ali-iotkit-v3.0.2\\ports\\rtthread\\HAL_UDP_rtthread.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\impl_linkkit.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\mqtt\\impl\\MQTTPacket.c", + "packages\\ali-iotkit-v3.0.2\\ports\\rtthread\\HAL_OS_rtthread.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra\\infra_report.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_message_cache.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\mqtt\\impl\\iotx_mqtt_client.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra\\infra_cjson.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\mqtt\\mqtt_api.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra\\infra_net.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_manager.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\mqtt\\impl\\MQTTSubscribeClient.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra\\infra_defs.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra\\infra_string.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_cota.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_opt.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\iotx_cm_mqtt.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\client\\dm_client_adapter.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_message.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_utils.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_api.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra\\infra_timer.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\dev_model\\dm_msg_process.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\mqtt\\impl\\MQTTUnsubscribeClient.c", + "packages\\ali-iotkit-v3.0.2\\iotkit-embedded\\src\\infra\\infra_sha256.c", + "packages\\ali-iotkit-v3.0.2\\SConscript" + ] + }, + { + "name": "ap3216c", + "path": "packages\\ap3216c-latest", + "files": [ + "packages\\ap3216c-latest\\ap3216c.c", + "packages\\ap3216c-latest\\SConscript" + ] + }, + { + "name": "Applications", + "path": "applications", + "files": [ + "applications\\app_lcd.c", + "applications\\init.c", + "applications\\main.c", + "applications\\sim.c", + "my_pro\\AHT10.c", + "my_pro\\icm_20608_sample.c", + "my_pro\\indicator_led.c", + "my_pro\\my_func.c", + "my_pro\\myinfrared.c", + "my_pro\\myproject.c", + "my_pro\\mysnake.c", + "my_pro\\mytest.c", + "my_pro\\pin_irq_example.c", + "my_pro\\test_drv_example.c", + "applications\\SConscript" + ] + }, + { + "name": "AT", + "path": "rt-thread\\components\\net\\at", + "files": [ + "rt-thread\\components\\net\\at\\src\\at_cli.c", + "rt-thread\\components\\net\\at\\src\\at_client.c", + "rt-thread\\components\\net\\at\\src\\at_utils.c", + "rt-thread\\components\\net\\at\\SConscript" + ] + }, + { + "name": "cJSON", + "path": "packages\\cJSON-v1.7.17", + "files": [ + "packages\\cJSON-v1.7.17\\cJSON_Utils.c", + "packages\\cJSON-v1.7.17\\cJSON.c", + "packages\\cJSON-v1.7.17\\SConscript" + ] + }, + { + "name": "Compiler", + "path": "rt-thread\\components\\libc\\compilers\\common", + "files": [ + "rt-thread\\components\\libc\\compilers\\common\\cctype.c", + "rt-thread\\components\\libc\\compilers\\common\\cstdlib.c", + "rt-thread\\components\\libc\\compilers\\common\\cstring.c", + "rt-thread\\components\\libc\\compilers\\common\\ctime.c", + "rt-thread\\components\\libc\\compilers\\common\\cunistd.c", + "rt-thread\\components\\libc\\compilers\\common\\cwchar.c", + "rt-thread\\components\\libc\\compilers\\newlib\\syscalls.c", + "rt-thread\\components\\libc\\compilers\\common\\SConscript" + ] + }, + { + "name": "CPP", + "path": "rt-thread\\components\\libc\\cplusplus", + "files": [ + "rt-thread\\components\\libc\\cplusplus\\cxx_crt_init.c", + "rt-thread\\components\\libc\\cplusplus\\cxx_crt.cpp", + "rt-thread\\components\\libc\\cplusplus\\SConscript" + ] + }, + { + "name": "DeviceDrivers", + "path": "rt-thread\\components\\drivers\\block", + "files": [ + "rt-thread\\components\\drivers\\block\\blk.c", + "rt-thread\\components\\drivers\\block\\blk_dev.c", + "rt-thread\\components\\drivers\\block\\blk_dfs.c", + "rt-thread\\components\\drivers\\block\\blk_partition.c", + "rt-thread\\components\\drivers\\block\\partitions\\dfs.c", + "rt-thread\\components\\drivers\\block\\partitions\\efi.c", + "rt-thread\\components\\drivers\\core\\device.c", + "rt-thread\\components\\drivers\\hwtimer\\hwtimer.c", + "rt-thread\\components\\drivers\\i2c\\dev_i2c_bit_ops.c", + "rt-thread\\components\\drivers\\i2c\\dev_i2c_core.c", + "rt-thread\\components\\drivers\\i2c\\dev_i2c_dev.c", + "rt-thread\\components\\drivers\\ipc\\completion_comm.c", + "rt-thread\\components\\drivers\\ipc\\completion_up.c", + "rt-thread\\components\\drivers\\ipc\\condvar.c", + "rt-thread\\components\\drivers\\ipc\\dataqueue.c", + "rt-thread\\components\\drivers\\ipc\\pipe.c", + "rt-thread\\components\\drivers\\ipc\\ringblk_buf.c", + "rt-thread\\components\\drivers\\ipc\\ringbuffer.c", + "rt-thread\\components\\drivers\\ipc\\waitqueue.c", + "rt-thread\\components\\drivers\\ipc\\workqueue.c", + "rt-thread\\components\\drivers\\misc\\adc.c", + "rt-thread\\components\\drivers\\misc\\rt_drv_pwm.c", + "rt-thread\\components\\drivers\\pin\\dev_pin.c", + "rt-thread\\components\\drivers\\rtc\\dev_rtc.c", + "rt-thread\\components\\drivers\\rtc\\dev_soft_rtc.c", + "rt-thread\\components\\drivers\\sdio\\dev_block.c", + "rt-thread\\components\\drivers\\sdio\\dev_mmc.c", + "rt-thread\\components\\drivers\\sdio\\dev_mmcsd_core.c", + "rt-thread\\components\\drivers\\sdio\\dev_sd.c", + "rt-thread\\components\\drivers\\sdio\\dev_sdio.c", + "rt-thread\\components\\drivers\\sensor\\v1\\sensor.c", + "rt-thread\\components\\drivers\\sensor\\v1\\sensor_cmd.c", + "rt-thread\\components\\drivers\\serial\\dev_serial.c", + "rt-thread\\components\\drivers\\spi\\dev_spi.c", + "rt-thread\\components\\drivers\\spi\\dev_spi_core.c", + "rt-thread\\components\\drivers\\spi\\dev_spi_flash_sfud.c", + "rt-thread\\components\\drivers\\spi\\sfud\\src\\sfud.c", + "rt-thread\\components\\drivers\\spi\\sfud\\src\\sfud_sfdp.c", + "rt-thread\\components\\drivers\\wlan\\dev_wlan.c", + "rt-thread\\components\\drivers\\wlan\\dev_wlan_cfg.c", + "rt-thread\\components\\drivers\\wlan\\dev_wlan_cmd.c", + "rt-thread\\components\\drivers\\wlan\\dev_wlan_lwip.c", + "rt-thread\\components\\drivers\\wlan\\dev_wlan_mgnt.c", + "rt-thread\\components\\drivers\\wlan\\dev_wlan_prot.c", + "rt-thread\\components\\drivers\\wlan\\dev_wlan_workqueue.c", + "rt-thread\\components\\drivers\\block\\SConscript" + ] + }, + { + "name": "Drivers", + "path": "board", + "files": [ + "board\\CubeMX_Config\\Src\\stm32f4xx_hal_msp.c", + "board\\board.c", + "board\\ports\\drv_filesystem.c", + "board\\ports\\fal\\fal_spi_flash_sfud_port.c", + "board\\ports\\lcd\\drv_lcd.c", + "board\\ports\\led_matrix\\drv_matrix_led.c", + "board\\ports\\spi_flash_init.c", + "board\\startup_stm32f407xx.s", + "libraries\\HAL_Drivers\\drivers\\drv_adc.c", + "libraries\\HAL_Drivers\\drivers\\drv_flash\\drv_flash_f4.c", + "libraries\\HAL_Drivers\\drivers\\drv_gpio.c", + "libraries\\HAL_Drivers\\drivers\\drv_pwm.c", + "libraries\\HAL_Drivers\\drivers\\drv_sdio.c", + "libraries\\HAL_Drivers\\drivers\\drv_soft_i2c.c", + "libraries\\HAL_Drivers\\drivers\\drv_spi.c", + "libraries\\HAL_Drivers\\drivers\\drv_test.c", + "libraries\\HAL_Drivers\\drivers\\drv_tim.c", + "libraries\\HAL_Drivers\\drivers\\drv_usart.c", + "libraries\\HAL_Drivers\\drivers\\drv_usbd.c", + "libraries\\HAL_Drivers\\drv_common.c", + "board\\SConscript" + ] + }, + { + "name": "Fal", + "path": "rt-thread\\components\\fal", + "files": [ + "rt-thread\\components\\fal\\src\\fal.c", + "rt-thread\\components\\fal\\src\\fal_partition.c", + "rt-thread\\components\\fal\\samples\\porting\\fal_flash_sfud_port.c", + "rt-thread\\components\\fal\\src\\fal_flash.c", + "rt-thread\\components\\fal\\src\\fal_rtt.c", + "rt-thread\\components\\fal\\SConscript" + ] + }, + { + "name": "Filesystem", + "path": "rt-thread\\components\\dfs\\dfs_v1", + "files": [ + "rt-thread\\components\\dfs\\dfs_v1\\filesystems\\devfs\\devfs.c", + "rt-thread\\components\\dfs\\dfs_v1\\filesystems\\elmfat\\dfs_elm.c", + "rt-thread\\components\\dfs\\dfs_v1\\filesystems\\elmfat\\ff.c", + "rt-thread\\components\\dfs\\dfs_v1\\filesystems\\elmfat\\ffunicode.c", + "rt-thread\\components\\dfs\\dfs_v1\\filesystems\\romfs\\dfs_romfs.c", + "rt-thread\\components\\dfs\\dfs_v1\\src\\dfs.c", + "rt-thread\\components\\dfs\\dfs_v1\\src\\dfs_file.c", + "rt-thread\\components\\dfs\\dfs_v1\\src\\dfs_fs.c", + "rt-thread\\components\\dfs\\dfs_v1\\src\\dfs_posix.c", + "rt-thread\\components\\dfs\\dfs_v1\\SConscript" + ] + }, + { + "name": "Finsh", + "path": "rt-thread\\components\\finsh", + "files": [ + "rt-thread\\components\\finsh\\msh_parse.c", + "rt-thread\\components\\finsh\\msh.c", + "rt-thread\\components\\finsh\\msh_file.c", + "rt-thread\\components\\finsh\\cmd.c", + "rt-thread\\components\\finsh\\shell.c", + "rt-thread\\components\\finsh\\SConscript" + ] + }, + { + "name": "flex_button", + "path": "packages\\FlexibleButton-v1.0.0", + "files": [ + "packages\\FlexibleButton-v1.0.0\\flexible_button.c", + "packages\\FlexibleButton-v1.0.0\\SConscript" + ] + }, + { + "name": "icm20608", + "path": "packages\\icm20608-latest", + "files": [ + "packages\\icm20608-latest\\icm20608.c", + "packages\\icm20608-latest\\SConscript" + ] + }, + { + "name": "Infrared_frame", + "path": "packages\\infrared-v0.1.1", + "files": [ + "packages\\infrared-v0.1.1\\src\\drv_infrared.c", + "packages\\infrared-v0.1.1\\src\\infrared.c", + "packages\\infrared-v0.1.1\\src\\nec_decoder.c", + "packages\\infrared-v0.1.1\\SConscript" + ] + }, + { + "name": "Kernel", + "path": ".", + "files": [ + "rt-thread\\src\\clock.c", + "rt-thread\\src\\components.c", + "rt-thread\\src\\cpu_up.c", + "rt-thread\\src\\defunct.c", + "rt-thread\\src\\idle.c", + "rt-thread\\src\\ipc.c", + "rt-thread\\src\\irq.c", + "rt-thread\\src\\kservice.c", + "rt-thread\\src\\mem.c", + "rt-thread\\src\\mempool.c", + "rt-thread\\src\\object.c", + "rt-thread\\src\\scheduler_comm.c", + "rt-thread\\src\\scheduler_up.c", + "rt-thread\\src\\thread.c", + "rt-thread\\src\\timer.c", + ".\\SConscript" + ] + }, + { + "name": "kernel-samples", + "path": "packages\\kernel_samples-latest\\en", + "files": [ + "packages\\kernel_samples-latest\\en\\semaphore_sample.c", + "packages\\kernel_samples-latest\\en\\mailbox_sample.c", + "packages\\kernel_samples-latest\\en\\msgq_sample.c", + "packages\\kernel_samples-latest\\en\\thread_sample.c", + "packages\\kernel_samples-latest\\en\\event_sample.c", + "packages\\kernel_samples-latest\\en\\mutex_sample.c", + "packages\\kernel_samples-latest\\en\\SConscript" + ] + }, + { + "name": "klibc", + "path": "rt-thread\\src\\klibc", + "files": [ + "rt-thread\\src\\klibc\\rt_vsscanf.c", + "rt-thread\\src\\klibc\\rt_vsnprintf_std.c", + "rt-thread\\src\\klibc\\kerrno.c", + "rt-thread\\src\\klibc\\kstdio.c", + "rt-thread\\src\\klibc\\kstring.c", + "rt-thread\\src\\klibc\\SConscript" + ] + }, + { + "name": "Legacy", + "path": "rt-thread\\components\\legacy", + "files": [ + "rt-thread\\components\\legacy\\ipc\\workqueue_legacy.c", + "rt-thread\\components\\legacy\\SConscript" + ] + }, + { + "name": "libcpu", + "path": "rt-thread\\libcpu\\arm\\common", + "files": [ + "rt-thread\\libcpu\\arm\\common\\atomic_arm.c", + "rt-thread\\libcpu\\arm\\common\\div0.c", + "rt-thread\\libcpu\\arm\\common\\showmem.c", + "rt-thread\\libcpu\\arm\\cortex-m4\\context_gcc.S", + "rt-thread\\libcpu\\arm\\cortex-m4\\cpuport.c", + "rt-thread\\libcpu\\arm\\common\\SConscript" + ] + }, + { + "name": "Libraries", + "path": "libraries\\STM32F4xx_HAL", + "files": [ + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_ll_usb.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_ll_fmc.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_sram.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_ll_sdmmc.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_i2c.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_rcc.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_hcd.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_tim.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_usart.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_dma.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_adc.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_rtc_ex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_i2c_ex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_flash_ramfunc.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_gpio.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_dma_ex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_rcc_ex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_rtc.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_pccard.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_cec.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_crc.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_pwr.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_sd.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_pcd.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_flash_ex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_cortex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_ll_fsmc.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_flash.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_adc_ex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_pwr_ex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_pcd_ex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_cryp_ex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_uart.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_qspi.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_tim_ex.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_spi.c", + "libraries\\STM32F4xx_HAL\\CMSIS\\Device\\ST\\STM32F4xx\\Source\\Templates\\system_stm32f4xx.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_lptim.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_rng.c", + "libraries\\STM32F4xx_HAL\\STM32F4xx_HAL_Driver\\Src\\stm32f4xx_hal_cryp.c", + "libraries\\STM32F4xx_HAL\\SConscript" + ] + }, + { + "name": "lwIP", + "path": "rt-thread\\components\\net\\lwip\\lwip-2.0.3", + "files": [ + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\api\\api_lib.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\api\\api_msg.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\api\\err.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\api\\netbuf.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\api\\netdb.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\api\\netifapi.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\api\\sockets.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\api\\tcpip.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\apps\\ping\\ping.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\def.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\dns.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\inet_chksum.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\init.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\ip.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\ipv4\\autoip.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\ipv4\\dhcp.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\ipv4\\etharp.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\ipv4\\icmp.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\ipv4\\igmp.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\ipv4\\ip4.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\ipv4\\ip4_addr.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\ipv4\\ip4_frag.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\memp.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\netif.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\pbuf.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\raw.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\stats.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\sys.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\tcp.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\tcp_in.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\tcp_out.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\timeouts.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\core\\udp.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\netif\\ethernet.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\src\\netif\\lowpan6.c", + "rt-thread\\components\\net\\lwip\\port\\ethernetif.c", + "rt-thread\\components\\net\\lwip\\port\\sys_arch.c", + "rt-thread\\components\\net\\lwip\\lwip-2.0.3\\SConscript" + ] + }, + { + "name": "NetUtils", + "path": "packages\\netutils-latest\\ntp", + "files": [ + "packages\\netutils-latest\\ntp\\ntp.c", + "packages\\netutils-latest\\ntp\\SConscript" + ] + }, + { + "name": "perf_counter", + "path": "packages\\perf_counter-v2.2.4.1", + "files": [ + "packages\\perf_counter-v2.2.4.1\\os\\perf_os_patch_rt_thread.c", + "packages\\perf_counter-v2.2.4.1\\perf_counter.c", + "packages\\perf_counter-v2.2.4.1\\SConscript" + ] + }, + { + "name": "POSIX", + "path": "rt-thread\\components\\libc\\posix\\io\\epoll", + "files": [ + "rt-thread\\components\\libc\\posix\\io\\poll\\poll.c", + "rt-thread\\components\\libc\\posix\\io\\poll\\select.c", + "rt-thread\\components\\libc\\posix\\io\\epoll\\SConscript" + ] + }, + { + "name": "RS485_port", + "path": "board\\ports\\rs485", + "files": [ + "board\\ports\\rs485\\drv_rs485.c", + "board\\ports\\rs485\\SConscript" + ] + }, + { + "name": "rt_usbd", + "path": "rt-thread\\components\\legacy\\usb\\usbdevice", + "files": [ + "rt-thread\\components\\legacy\\usb\\usbdevice\\core\\usbdevice.c", + "rt-thread\\components\\legacy\\usb\\usbdevice\\class\\cdc_vcom.c", + "rt-thread\\components\\legacy\\usb\\usbdevice\\core\\usbdevice_core.c", + "rt-thread\\components\\legacy\\usb\\usbdevice\\SConscript" + ] + }, + { + "name": "rw007", + "path": "packages\\rw007-v2.1.0", + "files": [ + "packages\\rw007-v2.1.0\\src\\spi_wifi_rw007.c", + "packages\\rw007-v2.1.0\\example\\rw007_stm32_port.c", + "packages\\rw007-v2.1.0\\SConscript" + ] + }, + { + "name": "SAL", + "path": "rt-thread\\components\\net\\netdev", + "files": [ + "rt-thread\\components\\net\\netdev\\src\\netdev.c", + "rt-thread\\components\\net\\netdev\\src\\netdev_ipaddr.c", + "rt-thread\\components\\net\\sal\\dfs_net\\dfs_net.c", + "rt-thread\\components\\net\\sal\\impl\\af_inet_lwip.c", + "rt-thread\\components\\net\\sal\\socket\\net_netdb.c", + "rt-thread\\components\\net\\sal\\socket\\net_sockets.c", + "rt-thread\\components\\net\\sal\\src\\sal_socket.c", + "rt-thread\\components\\net\\netdev\\SConscript" + ] + }, + { + "name": "Utilities", + "path": "rt-thread\\components\\utilities\\ulog", + "files": [ + "rt-thread\\components\\utilities\\ulog\\backend\\console_be.c", + "rt-thread\\components\\utilities\\ulog\\ulog.c", + "rt-thread\\components\\utilities\\ulog\\SConscript" + ] + }, + { + "name": "vconsole", + "path": "packages\\vconsole-latest", + "files": [ + "packages\\vconsole-latest\\vconsole.c", + "packages\\vconsole-latest\\SConscript" + ] + } + ] +} \ No newline at end of file diff --git a/applications/init.c b/applications/init.c index 26ae60a..2981fd8 100644 --- a/applications/init.c +++ b/applications/init.c @@ -1,9 +1,9 @@ #include "sim.h" #include #include -#include -#include -#include +#include +#include +#include #include #include "my_func.h" diff --git a/board/ports/fal/fal_spi_flash_sfud_port.c b/board/ports/fal/fal_spi_flash_sfud_port.c index 760fbfe..977430f 100644 --- a/board/ports/fal/fal_spi_flash_sfud_port.c +++ b/board/ports/fal/fal_spi_flash_sfud_port.c @@ -12,7 +12,7 @@ #include #ifdef RT_USING_SFUD -#include +#include #endif static int init(void); diff --git a/board/ports/spi_flash_init.c b/board/ports/spi_flash_init.c index 4b1f0f5..56f40fa 100644 --- a/board/ports/spi_flash_init.c +++ b/board/ports/spi_flash_init.c @@ -9,8 +9,8 @@ */ #include -#include "spi_flash.h" -#include "spi_flash_sfud.h" +#include "dev_spi_flash.h" +#include "dev_spi_flash_sfud.h" #include #include diff --git a/libraries/HAL_Drivers/drivers/drv_pwm.c b/libraries/HAL_Drivers/drivers/drv_pwm.c index 478d1d8..d097e68 100644 --- a/libraries/HAL_Drivers/drivers/drv_pwm.c +++ b/libraries/HAL_Drivers/drivers/drv_pwm.c @@ -15,7 +15,7 @@ #ifdef BSP_USING_PWM #include "drv_config.h" #include "drv_tim.h" -#include +#include //#define DRV_DEBUG #define LOG_TAG "drv.pwm" diff --git a/libraries/HAL_Drivers/drivers/drv_sdio.h b/libraries/HAL_Drivers/drivers/drv_sdio.h index 9a122e4..ebd0ff8 100644 --- a/libraries/HAL_Drivers/drivers/drv_sdio.h +++ b/libraries/HAL_Drivers/drivers/drv_sdio.h @@ -17,8 +17,8 @@ #include #include "drv_dma.h" #include -#include -#include +#include +#include #if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) #define SDCARD_INSTANCE_TYPE SDIO_TypeDef diff --git a/rt-thread/README.md b/rt-thread/README.md index 070c2fc..f0e7d25 100644 --- a/rt-thread/README.md +++ b/rt-thread/README.md @@ -11,7 +11,8 @@ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/RT-Thread/rt-thread?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GitHub pull-requests](https://img.shields.io/github/issues-pr/RT-Thread/rt-thread.svg)](https://github.com/RT-Thread/rt-thread/pulls) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](https://github.com/RT-Thread/rt-thread/pulls) - +[![RT-Thread BSP Static Build Check](https://github.com/RT-Thread/rt-thread/actions/workflows/bsp_buildings.yml/badge.svg)](https://github.com/RT-Thread/rt-thread/actions/workflows/bsp_buildings.yml) +Featured|HelloGitHub # RT-Thread RT-Thread was born in 2006, it is an open source, neutral, and community-based real-time operating system (RTOS). diff --git a/rt-thread/components/dfs/Kconfig b/rt-thread/components/dfs/Kconfig index 2c822cf..9b2f1b4 100644 --- a/rt-thread/components/dfs/Kconfig +++ b/rt-thread/components/dfs/Kconfig @@ -162,20 +162,32 @@ endif bool "Using devfs for device objects" default y - config RT_USING_DFS_ROMFS +if RT_USING_DFS_V1 + config RT_USING_DFS_ISO9660 + bool "Using ISO9660 filesystem" + depends on RT_USING_MEMHEAP + default n +endif + + menuconfig RT_USING_DFS_ROMFS bool "Enable ReadOnly file system on flash" default n - config RT_USING_DFS_ROMFS_USER_ROOT - bool "Use user's romfs root" - depends on RT_USING_DFS_ROMFS - default n + if RT_USING_DFS_ROMFS + config RT_USING_DFS_ROMFS_USER_ROOT + bool "Use user's romfs root" + depends on RT_USING_DFS_V1 + default n + endif if RT_USING_SMART config RT_USING_DFS_PTYFS bool "Using Pseudo-Teletype Filesystem (UNIX98 PTY)" depends on RT_USING_DFS_DEVFS default y + config RT_USING_DFS_PROCFS + bool "Enable proc file system" + default n endif config RT_USING_DFS_CROMFS diff --git a/rt-thread/components/dfs/dfs_v1/SConscript b/rt-thread/components/dfs/dfs_v1/SConscript index 181cf67..42f8b95 100644 --- a/rt-thread/components/dfs/dfs_v1/SConscript +++ b/rt-thread/components/dfs/dfs_v1/SConscript @@ -1,4 +1,6 @@ from building import * +from gcc import * +import rtconfig import os # The set of source files associated with this SConscript file. @@ -6,6 +8,7 @@ src = [] cwd = GetCurrentDir() CPPPATH = [cwd + "/include"] group = [] +LOCAL_CFLAGS = '' if GetDepend('RT_USING_DFS') and not GetDepend('RT_USING_DFS_V2'): src = ['src/dfs.c', 'src/dfs_file.c', 'src/dfs_fs.c'] @@ -13,7 +16,12 @@ if GetDepend('RT_USING_DFS') and not GetDepend('RT_USING_DFS_V2'): if GetDepend('DFS_USING_POSIX'): src += ['src/dfs_posix.c'] - group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS'], CPPPATH = CPPPATH) + if rtconfig.PLATFORM in GetGCCLikePLATFORM(): + LOCAL_CFLAGS += ' -std=c99' + elif rtconfig.PLATFORM in ['armcc']: + LOCAL_CFLAGS += ' --c99' + + group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS'], CPPPATH = CPPPATH, LOCAL_CFLAGS = LOCAL_CFLAGS) # search in the file system implementation list = os.listdir(cwd) diff --git a/rt-thread/components/dfs/dfs_v1/filesystems/iso9660/SConscript b/rt-thread/components/dfs/dfs_v1/filesystems/iso9660/SConscript new file mode 100644 index 0000000..d3d8e8c --- /dev/null +++ b/rt-thread/components/dfs/dfs_v1/filesystems/iso9660/SConscript @@ -0,0 +1,11 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS', 'RT_USING_DFS_ISO9660'], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/dfs/dfs_v1/filesystems/iso9660/dfs_iso9660.c b/rt-thread/components/dfs/dfs_v1/filesystems/iso9660/dfs_iso9660.c new file mode 100644 index 0000000..612b68c --- /dev/null +++ b/rt-thread/components/dfs/dfs_v1/filesystems/iso9660/dfs_iso9660.c @@ -0,0 +1,698 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "dfs.iso9660" +#define DBG_LVL DBG_INFO +#include + +#include "dfs_iso9660.h" +#include +#include +#include +#include +#include +#include + +#include + +#define ISO9660_FSTYPE_DIR 0040000 +#define ISO9660_FSTYPE_REG 0100000 +#define ISO9660_FSTYPE_SYMLINK 0120000 +#define ISO9660_FSTYPE_MASK 0170000 + +#define ISO9660_BLKSZ 2048 + +#define ISO9660_VOLDESC_BOOT 0 +#define ISO9660_VOLDESC_PRIMARY 1 +#define ISO9660_VOLDESC_SUPP 2 +#define ISO9660_VOLDESC_PART 3 +#define ISO9660_VOLDESC_END 255 + +rt_packed(struct iso9660_voldesc +{ + rt_uint8_t type; + rt_uint8_t magic[5]; + rt_uint8_t version; +}); + +rt_packed(struct iso9660_date2 +{ + rt_uint8_t year; + rt_uint8_t month; + rt_uint8_t day; + rt_uint8_t hour; + rt_uint8_t minute; + rt_uint8_t second; + rt_uint8_t offset; +}); + +/* Directory entry */ +rt_packed(struct iso9660_dir +{ + rt_uint8_t len; + rt_uint8_t ext_sectors; + rt_le32_t first_sector; + rt_le32_t first_sector_be; + rt_le32_t size; + rt_le32_t size_be; + struct iso9660_date2 mtime; +#define FLAG_TYPE_PLAIN 0 +#define FLAG_TYPE_DIR 2 +#define FLAG_TYPE 3 +#define FLAG_MORE_EXTENTS 0x80 + rt_uint8_t flags; + rt_uint8_t file_unit_size; + rt_uint8_t interleave_gap_size; + rt_le16_t vol_seq_num; + rt_le16_t vol_seq_num_be; +#define MAX_NAMELEN 255 + rt_uint8_t namelen; + char name[0]; +}); + +rt_packed(struct iso9660_date +{ + rt_uint8_t year[4]; + rt_uint8_t month[2]; + rt_uint8_t day[2]; + rt_uint8_t hour[2]; + rt_uint8_t minute[2]; + rt_uint8_t second[2]; + rt_uint8_t hundredth[2]; + rt_uint8_t offset; +}); + +/* Common volume descriptor */ +rt_packed(struct iso9660_common_voldesc +{ + struct iso9660_voldesc voldesc; + rt_uint8_t sysname[33]; + rt_uint8_t volname[32]; + rt_uint8_t unused2[8]; + rt_le32_t vol_space_size_le; + rt_le32_t vol_space_size_be; + rt_uint8_t escape[32]; + rt_le16_t vol_set_size_le; + rt_le16_t vol_set_size_be; + rt_le16_t vol_seq_num_le; + rt_le16_t vol_seq_num_be; + rt_le16_t logical_block_size_le; + rt_le16_t logical_block_size_be; + rt_le32_t path_table_size; + rt_le32_t path_table_size_be; + rt_le32_t path_table; + rt_le32_t path_table_be; + rt_uint8_t unused3[8]; + struct iso9660_dir rootdir; + rt_uint8_t unused4[624]; + struct iso9660_date created; + struct iso9660_date modified; + rt_uint8_t unused5[0 /* 1201 */]; +}); + +struct iso9660 +{ + struct rt_device *dev; + + rt_uint8_t joliet; + rt_uint8_t swap[ISO9660_BLKSZ]; + + struct iso9660_common_voldesc primary, supp; +}; + +struct iso9660_fd +{ + struct iso9660 *iso; + + struct iso9660_dir dirent; +}; + +struct iso9660_iterate +{ + struct iso9660_fd *fd; + + int i, index, count; + struct dirent *dirp; +}; + +static void iso9660_convert_string(char *dest, rt_uint16_t *src, int len) +{ + /* UTF16 to ASCII */ + len >>= 1; + + for (int i = 0; i < len; ++i) + { + rt_uint16_t utf16 = rt_be16_to_cpu(*src++); + + if (utf16 < 0x80) + { + *dest++ = (rt_uint8_t)utf16; + } + else + { + *dest++ = '?'; + } + } + *dest = '\0'; +} + +static void iso9660_convert_lower(char *dest, rt_uint8_t *src, int len) +{ + for (int i = 0; i < len; ++i, ++src) + { + if (*src >= 'A' && *src <= 'Z') + { + *dest++ = *src - ('A' - 'a'); + } + else + { + *dest++ = *src; + } + } + + *dest = '\0'; +} + +static time_t iso9660_convert_unixtime(struct iso9660_date *date) +{ + struct tm tm; + + tm.tm_sec = (date->second[0] - '0') * 10 + (date->second[1] - '0'); + tm.tm_min = (date->minute[0] - '0') * 10 + (date->minute[1] - '0'); + tm.tm_hour = (date->hour[0] - '0') * 10 + (date->hour[1] - '0'); + tm.tm_mday = (date->day[0] - '0') * 10 + (date->day[1] - '0'); + tm.tm_mon = (date->month[0] - '0') * 10 + (date->month[1] - '0'); + tm.tm_year = (date->year[0] - '0') * 1000 + (date->year[1] - '0') * 100 + + (date->year[2] - '0') * 10 + (date->year[3] - '0'); + tm.tm_wday = 0; + + return mktime(&tm); +} + +static time_t iso9660_convert_unixtime2(struct iso9660_date2 *date) +{ + struct tm tm; + + tm.tm_sec = date->second; + tm.tm_min = date->minute; + tm.tm_hour = date->hour; + tm.tm_mday = date->day; + tm.tm_mon = date->month; + tm.tm_year = date->year + 1900; + tm.tm_wday = 0; + + return mktime(&tm); +} + +static struct iso9660_fd *iso9660_lookup(struct iso9660 *iso, const char *path, + struct iso9660_iterate *it) +{ + rt_uint32_t lba; + rt_size_t sz, len, namelen; + char sname[MAX_NAMELEN]; + struct iso9660_fd *fd; + struct iso9660_dir *dirent; + + if (it) + { + fd = it->fd; + iso = fd->iso; + dirent = &fd->dirent; + + /* No next entry, always goon */ + len = 1; + } + else + { + if (!(fd = rt_malloc(sizeof(*fd)))) + { + return fd; + } + + fd->iso = iso; + dirent = iso->joliet ? &iso->supp.rootdir : &iso->primary.rootdir; + + if (!rt_strcmp(path, "/")) + { + rt_memcpy(&fd->dirent, dirent, sizeof(*dirent)); + return fd; + } + + /* Skip the first '/' */ + ++path; + len = strchrnul(path, '/') - path; + } + + lba = rt_le32_to_cpu(dirent->first_sector); + if (rt_device_read(iso->dev, lba, iso->swap, 1) <= 0) + { + goto _fail; + } + dirent = (void *)iso->swap; + sz = 0; + + do { + /* Ignore "." and ".." */ + do { + rt_uint32_t dlen = rt_le32_to_cpu(dirent->len); + + dirent = (void *)dirent + dlen; + sz += dlen; + + if (ISO9660_BLKSZ - sz < sizeof(*dirent)) + { + /* Sector end, goto the next sector */ + if (rt_device_read(iso->dev, ++lba, iso->swap, 1) <= 0) + { + goto _fail; + } + dirent = (void *)iso->swap; + sz = 0; + } + + if (rt_le32_to_cpu(dirent->first_sector) == 0) + { + /* Is end, no found. */ + goto _fail; + } + } while (dirent->name[0] >> 1 == 0 && rt_le32_to_cpu(dirent->namelen) == 1); + + namelen = rt_le32_to_cpu(dirent->namelen); + + if (iso->joliet) + { + iso9660_convert_string(sname, (rt_uint16_t *)dirent->name, namelen); + } + else + { + if (!(rt_le32_to_cpu(dirent->flags) & FLAG_TYPE_DIR)) + { + /* Remove ";1" */ + namelen -= 2; + } + + iso9660_convert_lower(sname, (rt_uint8_t *)dirent->name, namelen); + } + + if (it) + { + if (it->i < it->index) + { + goto _next; + } + + if ((rt_le32_to_cpu(dirent->flags) & FLAG_TYPE) == FLAG_TYPE_DIR) + { + it->dirp->d_type = DT_DIR; + } + else + { + it->dirp->d_type = DT_REG; + } + + it->dirp->d_namlen = namelen; + rt_strncpy(it->dirp->d_name, sname, namelen); + it->dirp->d_name[namelen] = '\0'; + it->dirp->d_reclen = (rt_uint16_t)sizeof(struct dirent); + + ++it->dirp; + + _next: + ++it->i; + + if (it->i - it->index >= it->count) + { + /* Iterate end */ + return RT_NULL; + } + + /* No next entry */ + continue; + } + + if (!rt_strncmp(sname, path, len)) + { + /* The end of path, found ok */ + if (!path[len]) + { + rt_memcpy(&fd->dirent, dirent, sizeof(*dirent)); + return fd; + } + + /* Next entry */ + lba = rt_le32_to_cpu(dirent->first_sector); + if (rt_device_read(iso->dev, lba, iso->swap, 1) <= 0) + { + goto _fail; + } + dirent = (void *)iso->swap; + sz = 0; + + path += len + 1; + len = strchrnul(path, '/') - path; + } + } while (len); + +_fail: + if (!it) + { + rt_free(fd); + } + + return RT_NULL; +} + +static int dfs_iso9660_open(struct dfs_file *fd) +{ + struct iso9660 *iso = fd->vnode->fs->data; + + fd->vnode->data = iso9660_lookup(iso, fd->vnode->path, RT_NULL); + + return fd->vnode->data ? 0 : -EINVAL; +} + +static int dfs_iso9660_close(struct dfs_file *fd) +{ + struct iso9660_fd *iso_fd = fd->vnode->data; + + rt_free(iso_fd); + + return 0; +} + +static int dfs_iso9660_read(struct dfs_file *fd, void *buf, size_t count) +{ + rt_uint32_t pos; + void *buf_ptr; + ssize_t read_blk, toread_blk; + size_t rcount = 0, remain, size; + struct iso9660_fd *iso_fd = fd->vnode->data; + struct iso9660 *iso = iso_fd->iso; + + if (fd->pos + count > rt_le32_to_cpu(iso_fd->dirent.size)) + { + count = rt_le32_to_cpu(iso_fd->dirent.size) - fd->pos; + } + pos = rt_le32_to_cpu(iso_fd->dirent.first_sector); + + /* Align to a sector */ + if (fd->pos) + { + pos += fd->pos / ISO9660_BLKSZ; + remain = fd->pos & (ISO9660_BLKSZ - 1); + + if (rt_device_read(iso->dev, pos, iso->swap, 1) <= 0) + { + return -EIO; + } + + size = rt_min_t(size_t, ISO9660_BLKSZ - remain, count); + rt_memcpy(buf, &iso->swap[remain], size); + rcount += size; + count -= size; + buf += size; + pos += 1; + fd->pos += size; + + if (!count) + { + goto _end; + } + } + + remain = count & (ISO9660_BLKSZ - 1); + count = rt_max_t(size_t, count / ISO9660_BLKSZ, 1); + + while ((ssize_t)count > 0) + { + if (count == 1) + { + buf_ptr = iso->swap; + toread_blk = 1; + } + else + { + buf_ptr = buf; + toread_blk = count; + } + + read_blk = rt_device_read(iso->dev, pos, buf_ptr, toread_blk); + + if (read_blk <= 0) + { + return (int)read_blk; + } + + if (count == 1) + { + size = remain; + rt_memcpy(buf, iso->swap, size); + } + else + { + size = read_blk * ISO9660_BLKSZ; + } + + rcount += size; + count -= read_blk; + buf += size; + pos += read_blk; + fd->pos += size; + } + +_end: + return rcount; +} + +static off_t dfs_iso9660_lseek(struct dfs_file *fd, off_t offset) +{ + int ret = -EIO; + + if (offset <= fd->vnode->size) + { + fd->pos = offset; + ret = fd->pos; + } + + return ret; +} + +static int dfs_iso9660_getdents(struct dfs_file *fd, struct dirent *dirp, uint32_t count) +{ + struct iso9660_iterate it; + struct iso9660_fd *iso_fd = fd->vnode->data; + + count = (count / sizeof(struct dirent)); + + if (!count) + { + return -EINVAL; + } + + it.fd = iso_fd; + it.i = 0; + it.index = fd->pos; + it.count = count; + it.dirp = dirp; + + iso9660_lookup(RT_NULL, RT_NULL, &it); + + count = it.i - it.index; + if (count > 0) + { + fd->pos += count; + } + + count *= sizeof(struct dirent); + + return count; +} + +static const struct dfs_file_ops _iso9660_fops = +{ + .open = dfs_iso9660_open, + .close = dfs_iso9660_close, + .read = dfs_iso9660_read, + .lseek = dfs_iso9660_lseek, + .getdents = dfs_iso9660_getdents, +}; + +static int dfs_iso9660_mount(struct dfs_filesystem *fs, + unsigned long rwflag, const void *data) +{ + int err; + struct iso9660 *iso; + struct iso9660_voldesc *voldesc; + struct rt_device_blk_geometry geometry; + + if (!(iso = rt_malloc(sizeof(*iso)))) + { + return -RT_ENOMEM; + } + + iso->dev = fs->dev_id; + rt_device_control(iso->dev, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry); + + if (geometry.bytes_per_sector != ISO9660_BLKSZ) + { + LOG_E("%s: Logical block size = %d is not supported", + iso->dev->parent.name, geometry.bytes_per_sector); + + err = -EINVAL; + goto _fail; + } + + iso->primary.rootdir.first_sector = 0; + iso->supp.rootdir.first_sector = 0; + + /* LBA 0-15 is the bootloader's information */ + for (int lba = 16; ; ++lba) + { + if (rt_device_read(iso->dev, lba, &iso->swap, 1) <= 0) + { + err = -EIO; + goto _fail; + } + + voldesc = (void *)iso->swap; + + if (rt_strncmp((char *)voldesc->magic, "CD001", 5)) + { + LOG_E("%s: Invalid magic \"%s\"", voldesc->magic); + + err = -EINVAL; + goto _fail; + } + + if (voldesc->type == ISO9660_VOLDESC_BOOT) + { + LOG_D("System Name: %s", ((struct iso9660_common_voldesc *)voldesc)->sysname); + LOG_D("Volume Name: %s", ((struct iso9660_common_voldesc *)voldesc)->volname); + } + else if (voldesc->type == ISO9660_VOLDESC_PRIMARY) + { + iso->joliet = 0; + rt_memcpy(&iso->primary, &iso->swap, sizeof(iso->primary)); + } + else if (voldesc->type == ISO9660_VOLDESC_SUPP) + { + rt_memcpy(&iso->supp, &iso->swap, sizeof(iso->supp)); + + if (iso->supp.escape[0] == 0x25 && iso->supp.escape[1] == 0x2f) + { + if (iso->supp.escape[2] == 0x40) + { + iso->joliet = 1; + } + else if (iso->supp.escape[2] == 0x43) + { + iso->joliet = 2; + } + else if (iso->supp.escape[2] == 0x45) + { + iso->joliet = 3; + } + else + { + continue; + } + } + } + else if (voldesc->type == ISO9660_VOLDESC_PART) + { + LOG_D("System Name: %s", ((struct iso9660_common_voldesc *)voldesc)->sysname); + LOG_D("Volume Name: %s", ((struct iso9660_common_voldesc *)voldesc)->volname); + } + else if (voldesc->type == ISO9660_VOLDESC_END) + { + break; + } + } + + if (!iso->primary.rootdir.first_sector || !iso->supp.rootdir.first_sector) + { + LOG_E("No primary or secondary partition found"); + + err = -EINVAL; + goto _fail; + } + + fs->data = iso; + + return 0; + +_fail: + rt_free(iso); + + return err; +} + +static int dfs_iso9660_unmount(struct dfs_filesystem *fs) +{ + struct iso9660 *iso = fs->data; + + rt_free(iso); + + return 0; +} + +static int dfs_iso9660_stat(struct dfs_filesystem *fs, + const char *filename, struct stat *st) +{ + struct iso9660 *iso = fs->data; + struct iso9660_fd *fd = iso9660_lookup(iso, filename, RT_NULL); + + if (!fd) + { + return -EINVAL; + } + + st->st_dev = 0; + st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH; + + if ((fd->dirent.flags & FLAG_TYPE) == FLAG_TYPE_DIR) + { + st->st_mode &= ~S_IFREG; + st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + } + + st->st_atime = iso9660_convert_unixtime(iso->joliet ? + &iso->supp.created : &iso->primary.created); + st->st_mtime = iso9660_convert_unixtime2(&fd->dirent.mtime); + st->st_size = rt_le32_to_cpu(fd->dirent.size); + + rt_free(fd); + + return 0; +} + +static const struct dfs_filesystem_ops _iso9660 = +{ + .name = "iso9660", + .flags = DFS_FS_FLAG_DEFAULT, + .fops = &_iso9660_fops, + + .mount = dfs_iso9660_mount, + .unmount = dfs_iso9660_unmount, + + .stat = dfs_iso9660_stat, +}; + +int dfs_iso9660_init(void) +{ + /* register iso9660 file system */ + return dfs_register(&_iso9660); +} +INIT_COMPONENT_EXPORT(dfs_iso9660_init); diff --git a/rt-thread/components/dfs/dfs_v1/filesystems/iso9660/dfs_iso9660.h b/rt-thread/components/dfs/dfs_v1/filesystems/iso9660/dfs_iso9660.h new file mode 100644 index 0000000..d4e524e --- /dev/null +++ b/rt-thread/components/dfs/dfs_v1/filesystems/iso9660/dfs_iso9660.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __DFS_ISO9660_H__ +#define __DFS_ISO9660_H__ + +int dfs_iso9660_init(void); + +#endif /* __DFS_ISO9660_H__ */ diff --git a/rt-thread/components/dfs/dfs_v1/filesystems/ramfs/dfs_ramfs.c b/rt-thread/components/dfs/dfs_v1/filesystems/ramfs/dfs_ramfs.c index fd7326b..92807a2 100644 --- a/rt-thread/components/dfs/dfs_v1/filesystems/ramfs/dfs_ramfs.c +++ b/rt-thread/components/dfs/dfs_v1/filesystems/ramfs/dfs_ramfs.c @@ -92,7 +92,7 @@ struct ramfs_dirent *dfs_ramfs_lookup(struct dfs_ramfs *ramfs, return NULL; } -int dfs_ramfs_read(struct dfs_file *file, void *buf, size_t count) +ssize_t dfs_ramfs_read(struct dfs_file *file, void *buf, size_t count) { rt_size_t length; struct ramfs_dirent *dirent; @@ -114,7 +114,7 @@ int dfs_ramfs_read(struct dfs_file *file, void *buf, size_t count) return length; } -int dfs_ramfs_write(struct dfs_file *fd, const void *buf, size_t count) +ssize_t dfs_ramfs_write(struct dfs_file *fd, const void *buf, size_t count) { struct ramfs_dirent *dirent; struct dfs_ramfs *ramfs; @@ -151,7 +151,7 @@ int dfs_ramfs_write(struct dfs_file *fd, const void *buf, size_t count) return count; } -int dfs_ramfs_lseek(struct dfs_file *file, off_t offset) +off_t dfs_ramfs_lseek(struct dfs_file *file, off_t offset) { if (offset <= (off_t)file->vnode->size) { diff --git a/rt-thread/components/dfs/dfs_v1/src/dfs.c b/rt-thread/components/dfs/dfs_v1/src/dfs.c index 8d2e9b1..fdaf93d 100644 --- a/rt-thread/components/dfs/dfs_v1/src/dfs.c +++ b/rt-thread/components/dfs/dfs_v1/src/dfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2022, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -107,7 +107,8 @@ int dfs_init(void) INIT_PREV_EXPORT(dfs_init); /** - * this function will lock device file system. + * @brief this function will lock device file system. + * this lock (fslock) is used for protecting filesystem_operation_table and filesystem_table. * * @note please don't invoke it on ISR. */ @@ -126,6 +127,12 @@ void dfs_lock(void) } } +/** + * @brief this function will lock file descriptors. + * this lock (fdlock) is used for protecting fd table (_fdtab). + * + * @note please don't invoke it on ISR. + */ void dfs_file_lock(void) { rt_err_t result = -RT_EBUSY; @@ -142,7 +149,7 @@ void dfs_file_lock(void) } /** - * this function will lock device file system. + * @brief this function will unlock device file system. * * @note please don't invoke it on ISR. */ @@ -151,33 +158,56 @@ void dfs_unlock(void) rt_mutex_release(&fslock); } -#ifdef DFS_USING_POSIX - +/** + * @brief this function will unlock fd table. + */ void dfs_file_unlock(void) { rt_mutex_release(&fdlock); } +#ifdef DFS_USING_POSIX +/** + * @brief Expand the file descriptor table to accommodate a specific file descriptor. + * + * This function ensures that the file descriptor table in the given `dfs_fdtable` structure + * has sufficient capacity to include the specified file descriptor `fd`. If the table + * needs to be expanded, it reallocates memory and initializes new slots to `NULL`. + * + * @param fdt Pointer to the `dfs_fdtable` structure representing the file descriptor table. + * @param fd The file descriptor that the table must accommodate. + * @return int + * - The input file descriptor `fd` if it is within the current or newly expanded table's capacity. + * - `-1` if the requested file descriptor exceeds `DFS_FD_MAX` or memory allocation fails. + */ static int fd_slot_expand(struct dfs_fdtable *fdt, int fd) { int nr; int index; struct dfs_file **fds = NULL; + /* If the file descriptor is already within the current capacity, no expansion is needed.*/ if (fd < fdt->maxfd) { return fd; } + + /* If the file descriptor exceeds the maximum allowable limit, return an error.*/ if (fd >= DFS_FD_MAX) { return -1; } + /* Calculate the new capacity, rounding up to the nearest multiple of 4.*/ nr = ((fd + 4) & ~3); + + /* Ensure the new capacity does not exceed the maximum limit.*/ if (nr > DFS_FD_MAX) { nr = DFS_FD_MAX; } + + /* Attempt to reallocate the file descriptor table to the new capacity.*/ fds = (struct dfs_file **)rt_realloc(fdt->fds, nr * sizeof(struct dfs_file *)); if (!fds) { @@ -189,12 +219,23 @@ static int fd_slot_expand(struct dfs_fdtable *fdt, int fd) { fds[index] = NULL; } + + /* Update the file descriptor table and its capacity.*/ fdt->fds = fds; fdt->maxfd = nr; return fd; } +/** + * @brief Allocate a file descriptor slot starting from a specified index. + * + * @param fdt fdt Pointer to the `dfs_fdtable` structure representing the file descriptor table. + * @param startfd The starting index for the search for an empty slot. + * @return int + * - The index of the first available slot if successful. + * - `-1` if no slot is available or if table expansion fails + */ static int fd_slot_alloc(struct dfs_fdtable *fdt, int startfd) { int idx; @@ -219,6 +260,17 @@ static int fd_slot_alloc(struct dfs_fdtable *fdt, int startfd) } return idx; } + +/** + * @brief Allocate a new file descriptor and associate it with a newly allocated `struct dfs_file`. + * + * @param fdt Pointer to the `dfs_fdtable` structure representing the file descriptor table. + * @param startfd The starting index for searching an available file descriptor slot. + * + * @return + * - The index of the allocated file descriptor if successful. + * - `-1` if no slot is available or memory allocation fails. + */ static int fd_alloc(struct dfs_fdtable *fdt, int startfd) { int idx; @@ -323,7 +375,11 @@ struct dfs_file *fd_get(int fd) /** * @ingroup Fd * - * This function will put the file descriptor. + * @brief This function will release the file descriptor. + * + * This function releases a file descriptor slot in the file descriptor table, decrements reference + * counts, and cleans up resources associated with the `dfs_file` and `dfs_vnode` structures when applicable. + * */ void fdt_fd_release(struct dfs_fdtable* fdt, int fd) { @@ -378,6 +434,20 @@ void fd_release(int fd) fdt_fd_release(fdt, fd); } +/** + * @brief Duplicates a file descriptor. + * + * This function duplicates an existing file descriptor (`oldfd`) and returns + * a new file descriptor that refers to the same underlying file object. + * + * @param oldfd The file descriptor to duplicate. It must be a valid file + * descriptor within the range of allocated descriptors. + * + * @return The new file descriptor if successful, or a negative value + * (e.g., -1) if an error occurs. + * + * @see sys_dup2() + */ rt_err_t sys_dup(int oldfd) { int newfd = -1; @@ -470,6 +540,23 @@ int fd_is_open(const char *pathname) return -1; } +/** + * @brief Duplicates a file descriptor to a specified file descriptor. + * + * This function duplicates an existing file descriptor (`oldfd`) and assigns it + * to the specified file descriptor (`newfd`). + * + * @param oldfd The file descriptor to duplicate. It must be a valid and open file + * descriptor within the range of allocated descriptors. + * @param newfd The target file descriptor. If `newfd` is already in use, it will + * be closed before duplication. If `newfd` exceeds the current file + * descriptor table size, the table will be expanded to accommodate it. + * + * @return The value of `newfd` on success, or a negative value (e.g., -1) if an + * error occurs. + * + * @see sys_dup() + */ rt_err_t sys_dup2(int oldfd, int newfd) { struct dfs_fdtable *fdt = NULL; @@ -550,6 +637,10 @@ static int fd_get_fd_index_form_fdt(struct dfs_fdtable *fdt, struct dfs_file *fi return fd; } +/** + * @brief get fd (index) by dfs file object. + * + */ int fd_get_fd_index(struct dfs_file *file) { struct dfs_fdtable *fdt; @@ -558,6 +649,21 @@ int fd_get_fd_index(struct dfs_file *file) return fd_get_fd_index_form_fdt(fdt, file); } +/** + * @brief Associates a file descriptor with a file object. + * + * This function associates a given file descriptor (`fd`) with a specified + * file object (`file`) in the file descriptor table (`fdt`). + * + * @param fdt The file descriptor table to operate on. It must be a valid + * and initialized `dfs_fdtable` structure. + * @param fd The file descriptor to associate. It must be within the range + * of allocated file descriptors and currently unoccupied. + * @param file The file object to associate with the file descriptor. It must + * be a valid and initialized `dfs_file` structure. + * + * @return The value of `fd` on success, or -1 if an error occurs. + */ int fd_associate(struct dfs_fdtable *fdt, int fd, struct dfs_file *file) { int retfd = -1; @@ -591,6 +697,10 @@ exit: return retfd; } +/** + * @brief initialize a dfs file object. + * + */ void fd_init(struct dfs_file *fd) { if (fd) diff --git a/rt-thread/components/dfs/dfs_v1/src/dfs_file.c b/rt-thread/components/dfs/dfs_v1/src/dfs_file.c index acdc124..be5747a 100644 --- a/rt-thread/components/dfs/dfs_v1/src/dfs_file.c +++ b/rt-thread/components/dfs/dfs_v1/src/dfs_file.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2021, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -18,10 +18,12 @@ #define DFS_VNODE_HASH_NR 128 +/*dfs vnode manager, for saving and searching vnodes.*/ struct dfs_vnode_mgr { - struct rt_mutex lock; - rt_list_t head[DFS_VNODE_HASH_NR]; + struct rt_mutex lock; /* mutex for protecting dfs vnode lists */ + rt_list_t head[DFS_VNODE_HASH_NR]; /* a group of dfs vnode lists, the dfs vnode is inserted to one of the lists + according to path string's hash-value mod DFS_VNODE_HASH_NR. */ }; static struct dfs_vnode_mgr dfs_fm; @@ -36,6 +38,10 @@ void dfs_fm_unlock(void) rt_mutex_release(&dfs_fm.lock); } +/** + * @brief Initialize dfs vnode manager structure, including a lock and hash tables for vnode. + * + */ void dfs_vnode_mgr_init(void) { int i = 0; @@ -47,6 +53,23 @@ void dfs_vnode_mgr_init(void) } } +/** + * @brief Initialize a DFS vnode structure. + * + * @param vnode Pointer to the DFS vnode structure to be initialized. + * The caller must ensure this is a valid, allocated structure. + * @param type The type of the vnode, representing its role or category (e.g., regular file, directory). + * @param fops Pointer to the file operations structure associated with this vnode. + * This structure defines the behavior of the vnode for operations such as open, read, write, etc. + * If `fops` is NULL, the vnode will have no associated file operations. + * + * @return 0 on success, or a negative error code on failure. + * + * @note The caller should ensure that: + * - The `vnode` pointer is valid and properly allocated. + * - The `fops` pointer (if not NULL) points to a valid `struct dfs_file_ops` + * instance, where all necessary function pointers are properly set. + */ int dfs_vnode_init(struct dfs_vnode *vnode, int type, const struct dfs_file_ops *fops) { if (vnode) @@ -64,7 +87,7 @@ int dfs_vnode_init(struct dfs_vnode *vnode, int type, const struct dfs_file_ops /* BKDR Hash Function */ static unsigned int bkdr_hash(const char *str) { - unsigned int seed = 131; // 31 131 1313 13131 131313 etc.. + unsigned int seed = 131; /* 31 131 1313 13131 131313 etc..*/ unsigned int hash = 0; while (*str) @@ -75,6 +98,22 @@ static unsigned int bkdr_hash(const char *str) return (hash % DFS_VNODE_HASH_NR); } +/** + * @brief Find a DFS vnode by its path. + * + * This function searches for a vnode in the vnode hash table using the specified path. + * If found, it returns a pointer to the vnode and updates the hash head if required. + * + * @param path The file path to search for. This should be a valid null-terminated string. + * @param hash_head Pointer to a location where the hash table head associated with the vnode + * can be stored. This can be NULL if the hash head is not needed. + * + * @return Pointer to the DFS vnode if found, or NULL if no vnode matches the specified path. + * + * @note The caller must ensure that: + * - The `path` pointer is valid and points to a properly null-terminated string. + * - If `hash_head` is not NULL, it points to a valid location to store the hash head. + */ static struct dfs_vnode *dfs_vnode_find(const char *path, rt_list_t **hash_head) { struct dfs_vnode *vnode = NULL; @@ -329,11 +368,12 @@ int dfs_file_close(struct dfs_file *fd) } /** - * this function will perform a io control on a file descriptor. + * this function will perform an io control on a file descriptor. * * @param fd the file descriptor. * @param cmd the command to send to file descriptor. * @param args the argument to send to file descriptor. + * - When `cmd` is `F_SETFL`, an additional integer argument specifies the new status flags. * * @return 0 on successful, -1 on failed. */ @@ -846,7 +886,7 @@ void cat(const char *filename) { buffer[length] = '\0'; rt_device_t out_device = rt_console_get_device(); - rt_device_write(out_device, 0, (void *)buffer, sizeof(buffer)); + rt_device_write(out_device, 0, (void *)buffer, length); } } while (length > 0); rt_kprintf("\n"); @@ -1026,14 +1066,14 @@ void copy(const char *src, const char *dst) flag |= FLAG_DST_IS_FILE; } - //2. check status + /*2. check status*/ if ((flag & FLAG_SRC_IS_DIR) && (flag & FLAG_DST_IS_FILE)) { rt_kprintf("cp faild, cp dir to file is not permitted!\n"); return ; } - //3. do copy + /*3. do copy*/ if (flag & FLAG_SRC_IS_FILE) { if (flag & FLAG_DST_IS_DIR) @@ -1053,7 +1093,7 @@ void copy(const char *src, const char *dst) copyfile(src, dst); } } - else //flag & FLAG_SRC_IS_DIR + else /*flag & FLAG_SRC_IS_DIR*/ { if (flag & FLAG_DST_IS_DIR) { diff --git a/rt-thread/components/dfs/dfs_v1/src/dfs_fs.c b/rt-thread/components/dfs/dfs_v1/src/dfs_fs.c index 4240736..3d35c5f 100644 --- a/rt-thread/components/dfs/dfs_v1/src/dfs_fs.c +++ b/rt-thread/components/dfs/dfs_v1/src/dfs_fs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2021, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -322,8 +322,8 @@ int dfs_mount(const char *device_name, /* open device, but do not check the status of device */ if (dev_id != NULL) { - if (rt_device_open(fs->dev_id, - RT_DEVICE_OFLAG_RDWR) != RT_EOK) + if (rt_device_open(fs->dev_id, RT_DEVICE_OFLAG_RDWR) != RT_EOK && + rt_device_open(fs->dev_id, RT_DEVICE_OFLAG_RDONLY) != RT_EOK) { /* The underlying device has error, clear the entry. */ dfs_lock(); @@ -529,7 +529,8 @@ int dfs_mount_device(rt_device_t dev) { int index = 0; - if(dev == RT_NULL) { + if(dev == RT_NULL) + { rt_kprintf("the device is NULL to be mounted.\n"); return -RT_ERROR; } @@ -538,7 +539,8 @@ int dfs_mount_device(rt_device_t dev) { if (mount_table[index].path == NULL) break; - if(strcmp(mount_table[index].device_name, dev->parent.name) == 0) { + if(strcmp(mount_table[index].device_name, dev->parent.name) == 0) + { if (dfs_mount(mount_table[index].device_name, mount_table[index].path, mount_table[index].filesystemtype, diff --git a/rt-thread/components/dfs/dfs_v1/src/dfs_posix.c b/rt-thread/components/dfs/dfs_v1/src/dfs_posix.c index 07276a8..d4ac04a 100644 --- a/rt-thread/components/dfs/dfs_v1/src/dfs_posix.c +++ b/rt-thread/components/dfs/dfs_v1/src/dfs_posix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2021, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -28,7 +28,17 @@ * return a file descriptor according specified flags. * * @param file the path name of file. - * @param flags the file open flags. + * @param flags the file open flags. Common values include: + * - Access modes (mutually exclusive): + * - `O_RDONLY`: Open for read-only access. + * - `O_WRONLY`: Open for write-only access. + * - `O_RDWR`: Open for both reading and writing. + * - File status flags (can be combined with bitwise OR `|`): + * - `O_CREAT`: Create the file if it does not exist. Requires a `mode` argument. + * - `O_TRUNC`: Truncate the file to zero length if it already exists. + * - `O_APPEND`: Append writes to the end of the file. + * - `O_EXCL`: Ensure that `O_CREAT` creates the file exclusively. + * - Other platform-specific flags * * @return the non-negative integer on successful open, others for failed. */ @@ -65,6 +75,22 @@ RTM_EXPORT(open); #ifndef AT_FDCWD #define AT_FDCWD (-100) #endif + +/** + * @brief Opens a file relative to a directory file descriptor. + * + * @param dirfd The file descriptor of the directory to base the relative path on. + * @param pathname The path to the file to be opened, relative to the directory specified by `dirfd`. + * Can be an absolute path (in which case `dirfd` is ignored). + * @param flags File access and status flags (e.g., `O_RDONLY`, `O_WRONLY`, `O_CREAT`). + * + * @return On success, returns a new file descriptor for the opened file. + * On failure, returns `-1` and sets `errno` to indicate the error. + * + * @note When using relative paths, ensure `dirfd` is a valid directory descriptor. + * When `pathname` is absolute, the `dirfd` argument is ignored. + * + */ int openat(int dirfd, const char *path, int flag, ...) { struct dfs_file *d; @@ -241,14 +267,22 @@ ssize_t write(int fd, const void *buf, size_t len) RTM_EXPORT(write); /** - * this function is a POSIX compliant version, which will seek the offset for + * this function is a POSIX compliant version, which will Reposition the file offset for * an open file descriptor. * - * @param fd the file descriptor. - * @param offset the offset to be seeked. - * @param whence the directory of seek. + * The `lseek` function sets the file offset for the file descriptor `fd` + * to a new value, determined by the `offset` and `whence` parameters. + * It can be used to seek to specific positions in a file for reading or writing. * - * @return the current read/write position in the file, or -1 on failed. + * @param fd the file descriptor. + * @param offset The offset, in bytes, to set the file position. + * The meaning of `offset` depends on the value of `whence`. + * @param whence the directive of seek. It can be one of: + * - `SEEK_SET`: Set the offset to `offset` bytes from the beginning of the file. + * - `SEEK_CUR`: Set the offset to its current location plus `offset` bytes. + * - `SEEK_END`: Set the offset to the size of the file plus `offset` bytes. + * + * @return the resulting read/write position in the file, or -1 on failed. */ off_t lseek(int fd, off_t offset, int whence) { @@ -436,9 +470,15 @@ RTM_EXPORT(fsync); * control functions on devices. * * @param fildes the file description - * @param cmd the specified command + * @param cmd the specified command, Common values include: + * - `F_DUPFD`: Duplicate a file descriptor. + * - `F_GETFD`: Get the file descriptor flags. + * - `F_SETFD`: Set the file descriptor flags. + * - `F_GETFL`: Get the file status flags. + * - `F_SETFL`: Set the file status flags. * @param ... represents the additional information that is needed by this - * specific device to perform the requested function. + * specific device to perform the requested function. For example: + * - When `cmd` is `F_SETFL`, an additional integer argument specifies the new status flags. * * @return 0 on successful completion. Otherwise, -1 shall be returned and errno * set to indicate the error. @@ -595,7 +635,7 @@ RTM_EXPORT(fstatfs); * this function is a POSIX compliant version, which will make a directory * * @param path the directory path to be made. - * @param mode + * @param mode The permission mode for the new directory (unused here, can be set to 0). * * @return 0 on successful, others on failed. */ diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.c b/rt-thread/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.c index 0d82914..ae67887 100644 --- a/rt-thread/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.c +++ b/rt-thread/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.c @@ -786,18 +786,18 @@ static ssize_t dfs_cromfs_read(struct dfs_file *file, void *buf, size_t count, o rt_err_t result = RT_EOK; file_info *fi = NULL; cromfs_info *ci = NULL; - uint32_t length = 0; + ssize_t length = 0; ci = (cromfs_info *)file->dentry->mnt->data; fi = (file_info *)file->vnode->data; - if (count < file->vnode->size - *pos) + if ((off_t)count < (off_t)file->vnode->size - *pos) { length = count; } else { - length = file->vnode->size - *pos; + length = (off_t)file->vnode->size - *pos; } if (length > 0) diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.h b/rt-thread/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.h index 97339bc..a22bd5e 100644 --- a/rt-thread/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.h +++ b/rt-thread/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.h @@ -11,6 +11,9 @@ #ifndef __DFS_CROMFS_H__ #define __DFS_CROMFS_H__ +#include + int dfs_cromfs_init(void); +uint8_t *cromfs_get_partition_data(uint32_t *len); #endif /*__DFS_CROMFS_H__*/ diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/devfs/devfs.c b/rt-thread/components/dfs/dfs_v2/filesystems/devfs/devfs.c index 6db468f..38e9a2e 100644 --- a/rt-thread/components/dfs/dfs_v2/filesystems/devfs/devfs.c +++ b/rt-thread/components/dfs/dfs_v2/filesystems/devfs/devfs.c @@ -77,6 +77,7 @@ static int dfs_devfs_open(struct dfs_file *file) } } } + rt_free(device_name); } return ret; @@ -113,6 +114,29 @@ static int dfs_devfs_close(struct dfs_file *file) return ret; } +static rt_ubase_t _get_unit_shift(rt_device_t device) +{ + rt_ubase_t shift = 0; + + /** + * transfer unit size from POSIX RW(in bytes) to rt_device_R/W + * (block size for blk device, otherwise in bytes). + */ + if (device->type == RT_Device_Class_Block) + { + struct rt_device_blk_geometry geometry = {0}; + + /* default to 512 */ + shift = 9; + if (!rt_device_control(device, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry)) + { + shift = __rt_ffs(geometry.block_size) - 1; + } + } + + return shift; +} + static ssize_t dfs_devfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos) { ssize_t ret = -RT_EIO; @@ -135,9 +159,14 @@ static ssize_t dfs_devfs_read(struct dfs_file *file, void *buf, size_t count, of if (device->ops) #endif /* RT_USING_POSIX_DEVIO */ { - /* read device data */ - ret = rt_device_read(device, *pos, buf, count); - *pos += ret; + rt_ubase_t shift = _get_unit_shift(device); + + ret = rt_device_read(device, *pos, buf, count >> shift); + if (ret > 0) + { + ret <<= shift; + *pos += ret; + } } } @@ -169,9 +198,15 @@ static ssize_t dfs_devfs_write(struct dfs_file *file, const void *buf, size_t co if (device->ops) #endif /* RT_USING_POSIX_DEVIO */ { + rt_ubase_t shift = _get_unit_shift(device); + /* read device data */ - ret = rt_device_write(device, *pos, buf, count); - *pos += ret; + ret = rt_device_write(device, *pos, buf, count >> shift); + if (ret > 0) + { + ret <<= shift; + *pos += ret; + } } } @@ -265,7 +300,7 @@ static int dfs_devfs_flush(struct dfs_file *file) static off_t dfs_devfs_lseek(struct dfs_file *file, off_t offset, int wherece) { - off_t ret = 0; + off_t ret = -EPERM; rt_device_t device; RT_ASSERT(file != RT_NULL); @@ -408,16 +443,16 @@ mode_t dfs_devfs_device_to_mode(struct rt_device *device) switch (device->type) { case RT_Device_Class_Char: - mode = S_IFCHR | 0777; + mode = S_IFCHR | 0666; break; case RT_Device_Class_Block: - mode = S_IFBLK | 0777; + mode = S_IFBLK | 0666; break; case RT_Device_Class_Pipe: - mode = S_IFIFO | 0777; + mode = S_IFIFO | 0666; break; default: - mode = S_IFCHR | 0777; + mode = S_IFCHR | 0666; break; } diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c b/rt-thread/components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c index 3bea0c1..82ab422 100644 --- a/rt-thread/components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c +++ b/rt-thread/components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -34,10 +35,9 @@ struct devtmpfs_file char name[DIRENT_NAME_MAX]; /* file name */ rt_uint32_t type; /* file type */ - rt_list_t subdirs; /* file subdir list */ - rt_list_t sibling; /* file sibling list */ + struct dfs_vfs_node node; /* file node in the devtmpfs */ - struct devtmpfs_sb *sb; /* superblock ptr */ + struct devtmpfs_sb *sb; /* superblock ptr */ rt_uint32_t mode; char *link; @@ -48,7 +48,6 @@ struct devtmpfs_sb rt_uint32_t magic; /* TMPFS_MAGIC */ struct devtmpfs_file root; /* root dir */ rt_size_t df_size; /* df size */ - rt_list_t sibling; /* sb sibling list */ struct rt_spinlock lock; /* tmpfs lock */ }; @@ -111,15 +110,13 @@ static int _get_subdir(const char *path, char *name) #if 0 static int _free_subdir(struct devtmpfs_file *dfile) { - struct devtmpfs_file *file; - rt_list_t *list, *temp_list; + struct devtmpfs_file *file, *tmp; struct devtmpfs_sb *superblock; RT_ASSERT(dfile->type == TMPFS_TYPE_DIR); - rt_list_for_each_safe(list, temp_list, &dfile->subdirs) + dfs_vfs_for_each_subnode(file, tmp, dfile, node) { - file = rt_list_entry(list, struct devtmpfs_file, sibling); if (file->type == TMPFS_TYPE_DIR) { _free_subdir(file); @@ -134,7 +131,7 @@ static int _free_subdir(struct devtmpfs_file *dfile) RT_ASSERT(superblock); rt_spin_lock(&superblock->lock); - rt_list_remove(&(file->sibling)); + dfs_vfs_remove_node(&file->node); rt_spin_unlock(&superblock->lock); rt_free(file); @@ -152,14 +149,12 @@ static int devtmpfs_mount(struct dfs_mnt *mnt, unsigned long rwflag, const void { superblock->df_size = sizeof(struct devtmpfs_sb); superblock->magic = TMPFS_MAGIC; - rt_list_init(&superblock->sibling); superblock->root.name[0] = '/'; superblock->root.sb = superblock; superblock->root.type = TMPFS_TYPE_DIR; superblock->root.mode = S_IFDIR | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH); - rt_list_init(&superblock->root.sibling); - rt_list_init(&superblock->root.subdirs); + dfs_vfs_init_node(&superblock->root.node); rt_spin_lock_init(&superblock->lock); @@ -193,8 +188,7 @@ static struct devtmpfs_file *devtmpfs_file_lookup(struct devtmpfs_sb *superblock { const char *subpath, *curpath, *filename = RT_NULL; char subdir_name[DIRENT_NAME_MAX]; - struct devtmpfs_file *file, *curfile; - rt_list_t *list; + struct devtmpfs_file *file, *curfile, *tmp; subpath = path; while (*subpath == '/' && *subpath) @@ -222,9 +216,8 @@ find_subpath: rt_spin_lock(&superblock->lock); - rt_list_for_each(list, &curfile->subdirs) + dfs_vfs_for_each_subnode(file, tmp, curfile, node) { - file = rt_list_entry(list, struct devtmpfs_file, sibling); if (filename) /* find file */ { if (rt_strcmp(file->name, filename) == 0) @@ -293,7 +286,9 @@ static int devtmpfs_stat(struct dfs_dentry *dentry, struct stat *st) static int devtmpfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count) { - struct devtmpfs_file *d_file; + rt_size_t index, end; + struct dirent *d; + struct devtmpfs_file *d_file, *n_file = RT_NULL, *tmp; struct devtmpfs_sb *superblock; RT_ASSERT(file); @@ -306,11 +301,6 @@ static int devtmpfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_ d_file = devtmpfs_file_lookup(superblock, file->dentry->pathname); if (d_file) { - rt_size_t index, end; - struct dirent *d; - struct devtmpfs_file *n_file; - rt_list_t *list; - /* make integer count */ count = (count / sizeof(struct dirent)); if (count == 0) @@ -322,12 +312,10 @@ static int devtmpfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_ index = 0; count = 0; - rt_list_for_each(list, &d_file->subdirs) + dfs_vfs_for_each_subnode(n_file, tmp, d_file, node) { if (index >= (rt_size_t)file->fpos) { - n_file = rt_list_entry(list, struct devtmpfs_file, sibling); - d = dirp + count; if (n_file->type == TMPFS_TYPE_FILE) { @@ -378,8 +366,7 @@ static int devtmpfs_symlink(struct dfs_dentry *parent_dentry, const char *target strncpy(l_file->name, linkpath, DIRENT_NAME_MAX - 1); - rt_list_init(&(l_file->subdirs)); - rt_list_init(&(l_file->sibling)); + dfs_vfs_init_node(&l_file->node); l_file->sb = superblock; l_file->type = TMPFS_TYPE_FILE; l_file->mode = p_file->mode; @@ -388,7 +375,7 @@ static int devtmpfs_symlink(struct dfs_dentry *parent_dentry, const char *target l_file->link = rt_strdup(target); rt_spin_lock(&superblock->lock); - rt_list_insert_after(&(p_file->subdirs), &(l_file->sibling)); + dfs_vfs_append_node(&p_file->node, &l_file->node); rt_spin_unlock(&superblock->lock); } } @@ -460,7 +447,7 @@ static int devtmpfs_unlink(struct dfs_dentry *dentry) } rt_spin_lock(&superblock->lock); - rt_list_remove(&(d_file->sibling)); + dfs_vfs_remove_node(&d_file->node); rt_spin_unlock(&superblock->lock); rt_free(d_file); @@ -537,8 +524,7 @@ static struct dfs_vnode *devtmpfs_create_vnode(struct dfs_dentry *dentry, int ty strncpy(d_file->name, file_name, DIRENT_NAME_MAX); - rt_list_init(&(d_file->subdirs)); - rt_list_init(&(d_file->sibling)); + dfs_vfs_init_node(&d_file->node); d_file->sb = superblock; vnode->nlink = 1; @@ -563,7 +549,7 @@ static struct dfs_vnode *devtmpfs_create_vnode(struct dfs_dentry *dentry, int ty d_file->mode = vnode->mode; rt_spin_lock(&superblock->lock); - rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling)); + dfs_vfs_append_node(&p_file->node, &d_file->node); rt_spin_unlock(&superblock->lock); } diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/README.md b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/README.md new file mode 100644 index 0000000..7ab0127 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/README.md @@ -0,0 +1,166 @@ +# 进程文件系统 (procfs) + +## 数据结构 + +```c +struct proc_dentry +{ + rt_uint32_t mode; + rt_atomic_t ref_count; + + struct proc_dentry *parent; + struct dfs_vfs_node node; + + const struct dfs_file_ops *fops; + const struct proc_ops *ops; + + char *name; + void *data; +}; +``` + +```log +root { mode: S_IFDIR, ref_count: 1, parent: root, name: /, child->next: file1->node } + | + |—— file1 { mode: S_IFREG, ref_count: 1, parent: root, name: file1, node->next: link1->node } + |—— link1 { mode: S_IFLNK, ref_count: 1, parent: root, name: link1, data: fullpath, node->next: dir1->node } + |—— dir1 { mode: S_IFDIR, ref_count: 1, parent: root, name: dir1, node->next: file3->node, child->next: file2->node } + | | + | |—— dir2 { mode: S_IFDIR, ref_count: 1, parent: dir1, name: dir2, node->next: link2->node } + | |—— link2 { mode: S_IFLNK, ref_count: 1, parent: dir1, name: link2, data: fullpath, node->next: file2->node } + | |—— file2 { mode: S_IFREG, ref_count: 1, parent: dir1, name: file2 } + | + |—— file3 { mode: S_IFREG, ref_count: 1, parent: root, name: file3 } +``` + +## API 介绍 + +```c +struct proc_dentry *dfs_proc_find(const char *name); + +struct proc_dentry *proc_mkdir_data(const char *name, mode_t mode, struct proc_dentry *parent, + const struct dfs_file_ops *fops, void *data); +struct proc_dentry *proc_mkdir_mode(const char *name, mode_t mode, struct proc_dentry *parent); +struct proc_dentry *proc_mkdir(const char *name, struct proc_dentry *parent); + +struct proc_dentry *proc_create_data(const char *name, mode_t mode, struct proc_dentry *parent, + const struct dfs_file_ops *fops, void *data); + +struct proc_dentry *proc_symlink(const char *name, struct proc_dentry *parent, const char *dest); + +struct proc_dentry *proc_acquire(struct proc_dentry *dentry); +void proc_release(struct proc_dentry *dentry); + +void proc_remove(struct proc_dentry *dentry); +``` + +- dfs_proc_find + + 查找指定节点,并返回节点数据指针 + + | 入参 | 说明 | + | ---- | ---------------------------------------------------- | + | name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2” | + +- proc_mkdir_data + + 创建一个目录,并返回节点数据指针 + + | 入参 | 说明 | + | ------ | ------------------------------------------------------------ | + | name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”
从 parent 起始的完整路径 | + | mode | 权限配置 | + | parent | 指定创建目录的起始节点 | + | fops | 文件操作接口配置 | + | data | 私有数据 | + +- proc_mkdir_mode + + 创建一个目录,并返回节点数据指针 + + | 入参 | 说明 | + | ------ | ------------------------------------------------------------ | + | name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”
从 parent 起始的完整路径 | + | mode | 权限配置 | + | parent | 指定创建目录的起始节点 | + +- proc_mkdir + + 创建一个目录,并返回节点数据指针 + + | 入参 | 说明 | + | ---- | ------------------------------------------------------------ | + | name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”
从 parent 起始的完整路径 | + | mode | 权限配置 | + +- proc_create_data + + 创建一个文件,并返回节点数据指针 + + | 入参 | 说明 | + | ------ | ------------------------------------------------------------ | + | name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”
从 parent 起始的完整路径 | + | mode | 权限配置 | + | parent | 指定创建文件的起始节点 | + | fops | 文件操作接口配置 | + | data | 私有数据 | + +- proc_symlink + + 创建一个符号链接,并返回节点数据指针 + + | 入参 | 说明 | + | ------ | ------------------------------------------------------------ | + | name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”
从 parent 起始的完整路径 | + | parent | 指定创建文件的起始节点 | + | dest | 链接的目标文件完整路径 | + +- proc_acquire + + 引用一个节点,并返回节点数据指针 + + | 入参 | 说明 | + | ------ | -------------- | + | dentry | 需要引用的节点 | + +- proc_release + + 释放一个节点 + + | 入参 | 说明 | + | ------ | -------------- | + | dentry | 需要释放的节点 | + +- proc_remove + + 删除一个节点包含子节点 + + | 入参 | 说明 | + | ------ | -------------- | + | dentry | 需要删除的节点 | + +## msh 调试命令 + +- proc_dump + + 遍历打印指定节点含子节点的信息(名称、引用计数),比如 `proc_dump /dir1` 或者 `proc_dump` + +- proc_remove + + 删除指定节点含子节点,比如 `proc_remove /dir1` 或者 `proc_remove /file3` + +- proc_symlink + + 创建一个符号链接,`proc_symlink /link3 /mnt` + +- proc_echo + + 创建一个空文件,`proc_echo /file4` + +- proc_mkdir + + 创建一个空目录,`proc_mkdir /dir3` + +- proc_pid + + 创建一个 pid 目录,`proc_pid /101` diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/SConscript b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/SConscript new file mode 100644 index 0000000..4af49a1 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/SConscript @@ -0,0 +1,11 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS', 'RT_USING_DFS_PROCFS'], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc.c new file mode 100644 index 0000000..0e0d202 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc.c @@ -0,0 +1,733 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +/* + * This is the root in the proc tree.. + */ +static struct proc_dentry _proc_root = { + .mode = S_IFDIR | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH), + .ref_count = 1, + + .parent = &_proc_root, + .node.sibling = RT_LIST_OBJECT_INIT(_proc_root.node.sibling), + .node.subnode = RT_LIST_OBJECT_INIT(_proc_root.node.subnode), + + .fops = RT_NULL, + + .name = "/", + .data = RT_NULL, +}; + +static int _proc_find(struct proc_dentry **parent, const char *name) +{ + struct proc_dentry *dentry = RT_NULL, *tmp; + + dfs_vfs_for_each_subnode(dentry, tmp, (*parent), node) + { + if (dentry == RT_NULL) + { + break; + } + + if (rt_strcmp(dentry->name, name) == 0) + { + *parent = dentry; + return 0; + } + } + + return -1; +} + +static int proc_find(struct proc_dentry **parent, const char **name, rt_bool_t force_lookup) +{ + int ret = 0; + char *tmp = RT_NULL; + + if (!(*parent)) + { + *parent = &_proc_root; + } + + tmp = rt_strdup(*name); + if (tmp) + { + char *begin = tmp, *end = RT_NULL; + if (*begin == '/') + { + begin++; + if (*begin == '\0') + { + rt_free(tmp); + *parent = proc_acquire(*parent); + return ret; + } + } + + while (1) + { + end = rt_strstr(begin, "/"); + if (end) + { + *end = '\0'; + ret = _proc_find(parent, begin); + if (ret < 0 || !S_ISDIR((*parent)->mode)) + { + *parent = RT_NULL; + ret = -1; + break; + } + begin = end + 1; + } + else if (force_lookup) + { + ret = _proc_find(parent, begin); + if (ret < 0) + { + if ((*parent)->ops && (*parent)->ops->lookup) + { + *parent = (*parent)->ops->lookup(*parent, begin); + if (*parent == RT_NULL) + { + ret = -1; + } + } + else + { + *parent = RT_NULL; + } + } + else + { + *parent = proc_acquire(*parent); + } + break; + } + else + { + *parent = proc_acquire(*parent); + break; + } + } + + *name = *name + (begin - tmp); + + rt_free(tmp); + } + + return ret; +} + +static void *single_start(struct dfs_seq_file *seq, off_t *index) +{ + return NULL + (*index == 0); +} + +static void *single_next(struct dfs_seq_file *seq, void *data, off_t *index) +{ + ++*index; + return NULL; +} + +static void single_stop(struct dfs_seq_file *seq, void *data) +{ +} + +static int proc_open(struct dfs_file *file) +{ + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + + if (entry->single_show) + { + struct dfs_seq_ops *seq_ops = (struct dfs_seq_ops *)rt_calloc(1, sizeof(struct dfs_seq_ops)); + if (seq_ops) + { + int ret = 0; + + seq_ops->start = single_start; + seq_ops->next = single_next; + seq_ops->stop = single_stop; + seq_ops->show = entry->single_show; + + ret = dfs_seq_open(file, seq_ops); + if (ret != 0) + { + rt_free(seq_ops); + } + return ret; + } + } + + return dfs_seq_open(file, entry->seq_ops); +} + +static int proc_close(struct dfs_file *file) +{ + struct dfs_seq_file *seq = file->data; + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + + if (seq && entry->single_show && seq->ops) + { + rt_free((void *)seq->ops); + seq->ops = RT_NULL; + } + + return dfs_seq_release(file); +} + +static const struct dfs_file_ops proc_file_ops = { + .open = proc_open, + .read = dfs_seq_read, + .lseek = dfs_seq_lseek, + .close = proc_close, +}; + +static struct proc_dentry *proc_create(struct proc_dentry **parent, const char *name, mode_t mode) +{ + int ret = 0; + struct proc_dentry *dentry = RT_NULL; + + ret = proc_find(parent, &name, 0); + if (ret >= 0) + { + dentry = *parent; + ret = proc_find(&dentry, &name, 1); + if (ret < 0) + { + dentry = rt_calloc(1, sizeof(struct proc_dentry)); + if (dentry) + { + dentry->mode = mode; + dentry->ref_count = 1; + dentry->name = rt_strdup(name); + dfs_vfs_init_node(&dentry->node); + } + } + else + { + proc_release(dentry); + dentry = RT_NULL; + } + } + + return dentry; +} + +/** + * @brief The dentry reference count is incremented by one + * + * @param dentry + * + * @return dentry + */ +struct proc_dentry *proc_acquire(struct proc_dentry *dentry) +{ + if (dentry) + { + dentry->ref_count += 1; + } + + return dentry; +} + +/** + * @brief The dentry reference count is minus one, or release + * + * @param dentry + * + * @return none + */ +void proc_release(struct proc_dentry *dentry) +{ + if (dentry) + { + if (dentry->ref_count == 1) + { + if (dentry->name) + { + rt_free(dentry->name); + } + + if (S_ISLNK(dentry->mode) && dentry->data) + { + rt_free(dentry->data); + } + + rt_free(dentry); + } + else + { + dentry->ref_count -= 1; + } + } +} + +static struct proc_dentry *proc_register(struct proc_dentry *parent, struct proc_dentry *child) +{ + child->parent = parent; + dfs_vfs_append_node(&parent->node, &child->node); + child->ref_count += 1; + child->pid = parent->pid; + + return child; +} + +/** + * @brief Make a dir + * + * @param name fullpath based on _proc_root or parent + * @param mode permission configuration + * @param parent can be empty + * @param fops + * @param data + * + * @return dentry + */ +struct proc_dentry *proc_mkdir_data(const char *name, mode_t mode, struct proc_dentry *parent, + const struct dfs_file_ops *fops, void *data) +{ + struct proc_dentry *dentry, *_parent = parent; + + if (mode == 0) + mode = (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH); + + dentry = proc_create(&_parent, name, S_IFDIR | mode); + if (dentry) + { + dentry->fops = fops; + dentry->data = data; + + dentry = proc_register(_parent, dentry); + } + proc_release(_parent); + + return dentry; +} + +/** + * @brief Make a dir + * + * @param name fullpath based on _proc_root or parent + * @param mode permission configuration + * @param parent can be empty + * + * @return dentry + */ +struct proc_dentry *proc_mkdir_mode(const char *name, mode_t mode, struct proc_dentry *parent) +{ + return proc_mkdir_data(name, mode, parent, NULL, NULL); +} + +/** + * @brief Make a dir + * + * @param name fullpath based on _proc_root or parent + * @param parent can be empty + * + * @return dentry + */ +struct proc_dentry *proc_mkdir(const char *name, struct proc_dentry *parent) +{ + return proc_mkdir_data(name, 0, parent, NULL, NULL); +} + +static struct proc_dentry *proc_create_reg(const char *name, mode_t mode, struct proc_dentry **parent) +{ + struct proc_dentry *dentry = RT_NULL; + + if ((mode & S_IFMT) == 0) + mode |= S_IFREG; + if ((mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)) == 0) + mode |= S_IRUSR | S_IRGRP | S_IROTH; + + if (!S_ISREG(mode)) + { + *parent = RT_NULL; + return dentry; + } + + return proc_create(parent, name, mode); +} + +/** + * @brief Make a file + * + * @param name fullpath based on _proc_root or parent + * @param mode permission configuration + * @param parent can be empty + * @param fops + * @param data + * + * @return dentry + */ +struct proc_dentry *proc_create_data(const char *name, mode_t mode, struct proc_dentry *parent, + const struct dfs_file_ops *fops, void *data) +{ + struct proc_dentry *dentry, *_parent = parent; + + dentry = proc_create_reg(name, mode, &_parent); + if (dentry) + { + dentry->fops = fops ? fops : &proc_file_ops; + dentry->data = data; + + dentry = proc_register(_parent, dentry); + } + proc_release(_parent); + + return dentry; +} + +/** + * @brief Make a file + * + * @param name fullpath based on _proc_root or parent + * @param mode permission configuration + * @param parent can be empty + * @param show + * @param data + * + * @return dentry + */ +struct proc_dentry *proc_create_single_data(const char *name, mode_t mode, struct proc_dentry *parent, + int (*show)(struct dfs_seq_file *, void *), void *data) +{ + struct proc_dentry *dentry, *_parent = parent; + + dentry = proc_create_reg(name, mode, &_parent); + if (dentry) + { + dentry->fops = &proc_file_ops; + dentry->single_show = show; + dentry->data = data; + + dentry = proc_register(_parent, dentry); + } + proc_release(_parent); + + return dentry; +} + +/** + * @brief Make a symlink + * + * @param name fullpath based on _proc_root or parent + * @param parent can be empty + * @param dest link file fullpath + * + * @return dentry + */ +struct proc_dentry *proc_symlink(const char *name, struct proc_dentry *parent, const char *dest) +{ + struct proc_dentry *dentry, *_parent = parent; + + dentry = proc_create(&_parent, name, (S_IFLNK | (S_IRUSR | S_IRGRP | S_IROTH) + | (S_IWUSR | S_IWGRP | S_IWOTH) | (S_IXUSR | S_IXGRP | S_IXOTH))); + if (dentry) + { + dentry->data = (void *)rt_strdup(dest); + if (dentry->data) + { + dentry = proc_register(_parent, dentry); + } + else + { + proc_release(dentry); + dentry = NULL; + } + } + proc_release(_parent); + + return dentry; +} + +static void remove_proc_subtree(struct proc_dentry *dentry) +{ + struct proc_dentry *iter = RT_NULL, *iter_tmp, *tmp = RT_NULL; + + dfs_vfs_for_each_subnode(iter, iter_tmp, dentry, node) + { + if (iter == RT_NULL) + { + break; + } + + if (tmp) + { + proc_release(tmp); + tmp = RT_NULL; + } + + tmp = iter; + + if (S_ISDIR(dentry->mode)) + { + remove_proc_subtree(iter); + } + } + + if (tmp) + { + proc_release(tmp); + tmp = RT_NULL; + } +} + +/** + * @brief remove a dentry + * + * @param dentry + * + * @return none + */ +void proc_remove(struct proc_dentry *dentry) +{ + if (dentry && dentry != &_proc_root) + { + if (S_ISDIR(dentry->mode)) + { + remove_proc_subtree(dentry); + } + + dfs_vfs_remove_node(&dentry->node); + proc_release(dentry); + } +} + +/** + * @brief find dentry exist + * + * @param name fullpath based on _proc_root + * + * @return dentry + */ +struct proc_dentry *dfs_proc_find(const char *name) +{ + struct proc_dentry *dentry = RT_NULL; + + proc_find(&dentry, &name, 1); + + return dentry; +} + +/** + * @brief remove a dentry on parent + * + * @param name fullpath based on parent + * @param parent + * + * @return none + */ +void proc_remove_dentry(const char *name, struct proc_dentry *parent) +{ + struct proc_dentry *dentry = parent; + + if (proc_find(&dentry, &name, 1) >= 0) + { + proc_remove(dentry); + proc_release(dentry); + } +} + +#define _COLOR_RED "\033[31m" +#define _COLOR_GREEN "\033[32m" +#define _COLOR_BLUE "\033[34m" +#define _COLOR_CYAN "\033[36m" +#define _COLOR_WHITE "\033[37m" +#define _COLOR_NORMAL "\033[0m" + +static void dump_proc_subtree(struct proc_dentry *dentry, int tab) +{ + struct proc_dentry *iter = RT_NULL, *tmp; + + dfs_vfs_for_each_subnode(iter, tmp, dentry, node) + { + if (iter == RT_NULL) + { + break; + } + + for(int i = 0; i < tab; i ++) + { + rt_kprintf("%-4s", i + 1 >= tab ? "|-" : " "); + } + + if (S_ISDIR(iter->mode)) + { + rt_kprintf(_COLOR_BLUE "%-20s" _COLOR_NORMAL " %d\n", iter->name, iter->ref_count); + dump_proc_subtree(iter, tab + 1); + } + else if (S_ISLNK(iter->mode)) + { + rt_kprintf(_COLOR_CYAN "%-20s" _COLOR_NORMAL " %d\n", iter->name, iter->ref_count); + } + else + { + rt_kprintf("%-20s %d\n", iter->name, iter->ref_count); + } + } +} + +static void proc_dump(struct proc_dentry *dentry) +{ + if (dentry) + { + if (S_ISDIR(dentry->mode)) + { + rt_kprintf(_COLOR_BLUE "%-20s" _COLOR_NORMAL " %d\n", dentry->name, dentry->ref_count); + dump_proc_subtree(dentry, 1); + } + else if (S_ISLNK(dentry->mode)) + { + rt_kprintf(_COLOR_CYAN "%-20s" _COLOR_NORMAL " %d\n", dentry->name, dentry->ref_count); + } + else + { + rt_kprintf("%-20s %d\n", dentry->name, dentry->ref_count); + } + } +} + +static int msh_proc_dump(int argc, char** argv) +{ + const char *name = argc > 1 ? argv[1] : "/"; + struct proc_dentry *dentry = RT_NULL; + + int ret = proc_find(&dentry, &name, 1); + if (ret >= 0) + { + proc_dump(dentry); + } + proc_release(dentry); + + return 0; +} +MSH_CMD_EXPORT_ALIAS(msh_proc_dump, proc_dump, proc dump); + +static int msh_proc_remove(int argc, char** argv) +{ + if (argc > 1) + { + const char *name = argv[1]; + struct proc_dentry *dentry = RT_NULL; + + int ret = proc_find(&dentry, &name, 1); + if (ret >= 0) + { + if (dentry != &_proc_root) + { + proc_remove(dentry); + } + else + { + struct proc_dentry *iter = RT_NULL, *iter_tmp, *tmp = RT_NULL; + + dfs_vfs_for_each_subnode(iter, iter_tmp, dentry, node) + { + if (iter == RT_NULL) + { + break; + } + + if (tmp) + { + proc_remove(tmp); + } + + tmp = iter; + } + + if (tmp) + { + proc_remove(tmp); + } + } + } + proc_release(dentry); + } + else + { + rt_kprintf("proc_remove path\n"); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(msh_proc_remove, proc_remove, proc remove); + +static int msh_proc_symlink(int argc, char** argv) +{ + if (argc > 2) + { + struct proc_dentry *entry = proc_symlink(argv[1], 0, argv[2]); + if (entry) + { + proc_release(entry); + } + } + else + { + rt_kprintf("proc_symlink path dest\n"); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(msh_proc_symlink, proc_symlink, proc symlink); + +static int msh_proc_echo(int argc, char** argv) +{ + if (argc > 1) + { + for(int i = 1; i <= argc - 1; i ++) + { + struct proc_dentry *entry = proc_create_data(argv[i], 0, 0, 0, 0); + if (entry) + { + proc_release(entry); + } + } + } + else + { + rt_kprintf("proc_echo path\n"); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(msh_proc_echo, proc_echo, proc echo); + +static int msh_proc_mkdir(int argc, char** argv) +{ + if (argc > 1) + { + for(int i = 1; i <= argc - 1; i ++) + { + struct proc_dentry *entry = proc_mkdir(argv[i], 0); + if (entry) + { + proc_release(entry); + } + } + } + else + { + rt_kprintf("proc_mkdir path\n"); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(msh_proc_mkdir, proc_mkdir, proc mkdir); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc.h b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc.h new file mode 100644 index 0000000..e697860 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __PROC_H__ +#define __PROC_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct proc_dentry; + +struct proc_ops +{ + struct proc_dentry *(*lookup)(struct proc_dentry *parent, const char *name); + int (*readlink)(struct proc_dentry *dentry, char *buf, int len); +}; + +struct proc_dentry +{ + rt_uint32_t mode; + rt_atomic_t ref_count; + + struct proc_dentry *parent; + struct dfs_vfs_node node; + + const struct dfs_file_ops *fops; + const struct proc_ops *ops; + const struct dfs_seq_ops *seq_ops; + int (*single_show)(struct dfs_seq_file *seq, void *data); + + int pid; + + char *name; + void *data; +}; + +struct proc_dentry *dfs_proc_find(const char *name); + +struct proc_dentry *proc_mkdir_data(const char *name, mode_t mode, struct proc_dentry *parent, + const struct dfs_file_ops *fops, void *data); +struct proc_dentry *proc_mkdir_mode(const char *name, mode_t mode, struct proc_dentry *parent); +struct proc_dentry *proc_mkdir(const char *name, struct proc_dentry *parent); + +struct proc_dentry *proc_create_data(const char *name, mode_t mode, struct proc_dentry *parent, + const struct dfs_file_ops *fops, void *data); +struct proc_dentry *proc_create_single_data(const char *name, mode_t mode, struct proc_dentry *parent, + int (*show)(struct dfs_seq_file *, void *), void *data); + +struct proc_dentry *proc_symlink(const char *name, struct proc_dentry *parent, const char *dest); + +struct proc_dentry *proc_acquire(struct proc_dentry *dentry); +void proc_release(struct proc_dentry *dentry); + +void proc_remove(struct proc_dentry *dentry); +void proc_remove_dentry(const char *name, struct proc_dentry *parent); + +int proc_pid(int pid); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_cmdline.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_cmdline.c new file mode 100644 index 0000000..5845b5f --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_cmdline.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include + +static char *__proc_cmdline = NULL; + +int proc_cmdline_save(const char *cmdline) +{ + if (__proc_cmdline) + { + free(__proc_cmdline); + __proc_cmdline = NULL; + } + + __proc_cmdline = strdup(cmdline); + + return 0; +} + +static int single_show(struct dfs_seq_file *seq, void *data) +{ + if (__proc_cmdline) + { + dfs_seq_puts(seq, __proc_cmdline); + } + + return 0; +} + +int proc_cmdline_init(void) +{ + struct proc_dentry *dentry = proc_create_single_data("cmdline", 0, NULL, single_show, NULL); + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_cmdline_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c new file mode 100644 index 0000000..6f49832 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include + +static void *seq_start(struct dfs_seq_file *seq, off_t *index) +{ + off_t i = *index; // seq->index + + return NULL + (i == 0); +} + +static void seq_stop(struct dfs_seq_file *seq, void *data) +{ +} + +static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index) +{ + /* data: The return value of the start or next*/ + off_t i = *index + 1; // seq->index + + *index = i; + + return NULL; +} + +static int seq_show(struct dfs_seq_file *seq, void *data) +{ + /* data: The return value of the start or next*/ + dfs_seq_puts(seq, "rt_weak const struct dfs_seq_ops *cpuinfo_get_seq_ops(void)\n--need your own function--\n"); + + return 0; +} + +static const struct dfs_seq_ops seq_ops = { + .start = seq_start, + .stop = seq_stop, + .next = seq_next, + .show = seq_show, +}; + +rt_weak const struct dfs_seq_ops *cpuinfo_get_seq_ops(void) +{ + return &seq_ops; +} + +static int proc_open(struct dfs_file *file) +{ + return dfs_seq_open(file, cpuinfo_get_seq_ops()); +} + +static int proc_close(struct dfs_file *file) +{ + return dfs_seq_release(file); +} + +static const struct dfs_file_ops file_ops = { + .open = proc_open, + .read = dfs_seq_read, + .lseek = dfs_seq_lseek, + .close = proc_close, +}; + +int proc_cpuinfo_init(void) +{ + struct proc_dentry *dentry = proc_create_data("cpuinfo", 0, NULL, &file_ops, NULL); + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_cpuinfo_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_devices.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_devices.c new file mode 100644 index 0000000..7bd0718 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_devices.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include + + +#define LIST_FIND_OBJ_NR 8 + +struct device_show +{ + char *buf; + int size; + int len; + int index; +}; + +typedef struct +{ + rt_list_t *list; + rt_list_t **array; + rt_uint8_t type; + int nr; /* input: max nr, can't be 0 */ + int nr_out; /* out: got nr */ +} list_get_next_t; + +static void list_find_init(list_get_next_t *p, rt_uint8_t type, rt_list_t **array, int nr) +{ + struct rt_object_information *info; + rt_list_t *list; + + info = rt_object_get_information((enum rt_object_class_type)type); + list = &info->object_list; + + p->list = list; + p->type = type; + p->array = array; + p->nr = nr; + p->nr_out = 0; +} + +static rt_list_t *list_get_next(rt_list_t *current, list_get_next_t *arg) +{ + int first_flag = 0; + rt_base_t level; + rt_list_t *node, *list; + rt_list_t **array; + struct rt_object_information *info; + int nr; + + arg->nr_out = 0; + + if (!arg->nr || !arg->type) + { + return (rt_list_t *)RT_NULL; + } + + list = arg->list; + info = rt_list_entry(list, struct rt_object_information, object_list); + + if (!current) /* find first */ + { + node = list; + first_flag = 1; + } + else + { + node = current; + } + + level = rt_spin_lock_irqsave(&info->spinlock); + + if (!first_flag) + { + struct rt_object *obj; + /* The node in the list? */ + obj = rt_list_entry(node, struct rt_object, list); + if ((obj->type & ~RT_Object_Class_Static) != arg->type) + { + rt_spin_unlock_irqrestore(&info->spinlock, level); + return (rt_list_t *)RT_NULL; + } + } + + nr = 0; + array = arg->array; + while (1) + { + node = node->next; + + if (node == list) + { + node = (rt_list_t *)RT_NULL; + break; + } + nr++; + *array++ = node; + if (nr == arg->nr) + { + break; + } + } + + rt_spin_unlock_irqrestore(&info->spinlock, level); + arg->nr_out = nr; + return node; +} + +static char *const device_type_str[RT_Device_Class_Unknown] = +{ + "Character Device", + "Block Device", + "Network Interface", + "MTD Device", + "CAN Device", + "RTC", + "Sound Device", + "Graphic Device", + "I2C Bus", + "USB Slave Device", + "USB Host Bus", + "USB OTG Bus", + "SPI Bus", + "SPI Device", + "SDIO Bus", + "PM Pseudo Device", + "Pipe", + "Portal Device", + "Timer Device", + "Miscellaneous Device", + "Sensor Device", + "Touch Device", + "Phy Device", + "Security Device", + "WLAN Device", + "Pin Device", + "ADC Device", + "DAC Device", + "WDT Device", + "PWM Device", + "Bus Device", +}; + +static void save_info(struct device_show *dev, char *dev_name) +{ + char tmp[256] = {0}; + int len; + + dev->index ++; + + rt_snprintf(tmp, 256, "%d %s\n", dev->index, dev_name); + tmp[255] = 0; + + len = rt_strlen(tmp); + if (dev->size > dev->len + len) + { + strcat(dev->buf, tmp); + dev->len += len; + } + else + { + if (dev->buf == RT_NULL) + { + dev->buf = rt_calloc(1, 4096); + } + else + { + dev->buf = rt_realloc(dev->buf, dev->size + 4096); + } + if (dev->buf) + { + dev->size += 4096; + strcat(dev->buf, tmp); + dev->len += len; + } + } +} + +static void list_device(struct device_show *dev) +{ + rt_base_t level; + list_get_next_t find_arg; + struct rt_object_information *info; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t *)RT_NULL; + + list_find_init(&find_arg, RT_Object_Class_Device, obj_list, sizeof(obj_list) / sizeof(obj_list[0])); + info = rt_list_entry(find_arg.list, struct rt_object_information, object_list); + + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_device *device; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_spin_lock_irqsave(&info->spinlock); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_spin_unlock_irqrestore(&info->spinlock, level); + continue; + } + + rt_spin_unlock_irqrestore(&info->spinlock, level); + + device = (struct rt_device *)obj; + + if (device->type < RT_Device_Class_Unknown) + { + save_info(dev + device->type, device->parent.name); + } + } + } + } + while (next != (rt_list_t *)RT_NULL); +} + +static int show_info(struct dfs_seq_file *seq) +{ + struct device_show _show[RT_Device_Class_Unknown] = {0}; + + list_device(_show); + + for (int i = 0; i < RT_Device_Class_Unknown; i++) + { + if (_show[i].buf) + { + dfs_seq_printf(seq, "%s:\n", device_type_str[i]); + dfs_seq_write(seq, _show[i].buf, _show[i].len); + dfs_seq_putc(seq, '\n'); + + rt_free(_show[i].buf); + } + } + + return 0; +} + +static void *seq_start(struct dfs_seq_file *seq, off_t *index) +{ + off_t i = *index; // seq->index + + return NULL + (i == 0); +} + +static void seq_stop(struct dfs_seq_file *seq, void *data) +{ +} + +static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index) +{ + /* data: The return value of the start or next*/ + off_t i = *index + 1; // seq->index + + *index = i; + + return NULL; +} + +static int seq_show(struct dfs_seq_file *seq, void *data) +{ + /* data: The return value of the start or next*/ + show_info(seq); + + return 0; +} + +static const struct dfs_seq_ops seq_ops = { + .start = seq_start, + .stop = seq_stop, + .next = seq_next, + .show = seq_show, +}; + +int proc_devices_init(void) +{ + struct proc_dentry *dentry = proc_create_data("devices", 0, NULL, NULL, NULL); + if (dentry) + { + dentry->seq_ops = &seq_ops; + } + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_devices_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_filesystems.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_filesystems.c new file mode 100644 index 0000000..94e7a85 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_filesystems.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include +#include + +static void *seq_start(struct dfs_seq_file *seq, off_t *index) +{ + off_t i = *index; // seq->index + struct dfs_filesystem_type *fs = dfs_filesystems(); + + if (fs) + { + while (i--) + { + fs = fs->next; + if (!fs) + { + break; + } + } + } + + return fs; +} + +static void seq_stop(struct dfs_seq_file *seq, void *data) +{ +} + +static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index) +{ + /* data: The return value of the start or next*/ + off_t i = *index + 1; // seq->index + struct dfs_filesystem_type *fs = (struct dfs_filesystem_type *)data; + + *index = i; + + return fs->next; +} + +static int seq_show(struct dfs_seq_file *seq, void *data) +{ + /* data: The return value of the start or next*/ + struct dfs_filesystem_type *fs = (struct dfs_filesystem_type *)data; + + dfs_seq_printf(seq, "%-9s%s\n", (fs->fs_ops->flags == FS_NEED_DEVICE) ? "" : "nodev", fs->fs_ops->name); + + return 0; +} + +static const struct dfs_seq_ops seq_ops = { + .start = seq_start, + .stop = seq_stop, + .next = seq_next, + .show = seq_show, +}; + +int proc_filesystems_init(void) +{ + struct proc_dentry *dentry = proc_create_data("filesystems", 0, NULL, NULL, NULL); + if (dentry) + { + dentry->seq_ops = &seq_ops; + } + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_filesystems_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c new file mode 100644 index 0000000..6ce1bc4 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include +#include + + +extern void rt_memory_info(rt_size_t *total, + rt_size_t *used, + rt_size_t *max_used); + +static int single_show(struct dfs_seq_file *seq, void *data) +{ + dfs_seq_printf(seq, "0.13 0.16 0.17 1/1035 380436\n"); + + return 0; +} + +int proc_loadavg_init(void) +{ + struct proc_dentry *dentry = proc_create_single_data("loadavg", 0, NULL, single_show, NULL); + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_loadavg_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c new file mode 100644 index 0000000..b6ae562 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include +#include + + +extern void rt_memory_info(rt_size_t *total, + rt_size_t *used, + rt_size_t *max_used); + +static int single_show(struct dfs_seq_file *seq, void *data) +{ + rt_size_t total, used, max_used, freed; + rt_size_t total_sum = 0; + rt_size_t total_freed = 0; + + rt_memory_info(&total, &used, &max_used); + total_sum = total_sum + total; + total_freed = total_freed + total - used; + + dfs_seq_printf(seq, "%-16s%8d KB\n", "MemMaxUsed:", max_used / 1024); + dfs_seq_printf(seq, "%-16s%8d KB\n", "MemAvailable:", (total - used) / 1024); + dfs_seq_printf(seq, "%-16s%8d KB\n", "Cached:", 0); + dfs_seq_printf(seq, "%-16s%8d KB\n", "SReclaimable:", 0); + + rt_page_get_info(&total, &freed); + total_sum = total_sum + total * RT_MM_PAGE_SIZE; + total_freed = total_freed + freed * RT_MM_PAGE_SIZE; + + dfs_seq_printf(seq, "%-16s%8d KB\n", "MemTotal:", total_sum / 1024); + dfs_seq_printf(seq, "%-16s%8d KB\n", "MemFree:", total_freed / 1024); + dfs_seq_printf(seq, "%-16s%8d KB\n", "LowPageTotal:", total * RT_MM_PAGE_SIZE / 1024); + dfs_seq_printf(seq, "%-16s%8d KB\n", "lowPageFree:", freed * RT_MM_PAGE_SIZE/ 1024); + + rt_page_high_get_info(&total, &freed); + + dfs_seq_printf(seq, "%-16s%8d KB\n", "HighPageTotal:", total * RT_MM_PAGE_SIZE / 1024); + dfs_seq_printf(seq, "%-16s%8d KB\n", "HighPageFree:", freed * RT_MM_PAGE_SIZE / 1024); + + return 0; +} + +int proc_meminfo_init(void) +{ + struct proc_dentry *dentry = proc_create_single_data("meminfo", 0, NULL, single_show, NULL); + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_meminfo_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c new file mode 100644 index 0000000..49a6ba9 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include +#include + + +const char *mnt_flag(int flag) +{ + /*if (flag & MNT_READONLY) + { + return "ro"; + }*/ + + return "rw"; +} + +static struct dfs_mnt* mnt_show(struct dfs_mnt *mnt, void *parameter) +{ + struct dfs_seq_file *seq = (struct dfs_seq_file *)parameter; + + if (mnt) + { + if (mnt->dev_id) + { + dfs_seq_printf(seq, "%s %s %s %s 0 0\n", mnt->dev_id->parent.name, mnt->fullpath, + mnt->fs_ops->name, mnt_flag(mnt->flags)); + } + else + { + dfs_seq_printf(seq, "%s %s %s %s 0 0\n", mnt->fs_ops->name, mnt->fullpath, + mnt->fs_ops->name, mnt_flag(mnt->flags)); + } + } + + return RT_NULL; +} + +static void *seq_start(struct dfs_seq_file *seq, off_t *index) +{ + off_t i = *index; // seq->index + + return NULL + (i == 0); +} + +static void seq_stop(struct dfs_seq_file *seq, void *data) +{ +} + +static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index) +{ + /* data: The return value of the start or next*/ + off_t i = *index + 1; // seq->index + + *index = i; + + return NULL; +} + +static int seq_show(struct dfs_seq_file *seq, void *data) +{ + /* data: The return value of the start or next*/ + dfs_mnt_foreach(mnt_show, seq); + + return 0; +} + +static const struct dfs_seq_ops seq_ops = { + .start = seq_start, + .stop = seq_stop, + .next = seq_next, + .show = seq_show, +}; + +int proc_mounts_init(void) +{ + struct proc_dentry *dentry = proc_create_data("mounts", 0, NULL, NULL, NULL); + if (dentry) + { + dentry->seq_ops = &seq_ops; + } + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_mounts_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_net.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_net.c new file mode 100644 index 0000000..e2b38e0 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_net.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include + +#ifdef RT_USING_LWIP +#include "lwip/opt.h" +#endif + +#if LWIP_ROUTE +extern int inet_route_foreach(void (*func)(const char *name, uint32_t ip_addr, uint32_t netmask, void *parameter), void *parameter); +#endif + +static void *seq_start(struct dfs_seq_file *seq, off_t *index) +{ + off_t i = *index; // seq->index + + return NULL + (i == 0); +} + +static void seq_stop(struct dfs_seq_file *seq, void *data) +{ +} + +static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index) +{ + /* data: The return value of the start or next*/ + off_t i = *index + 1; // seq->index + + *index = i; + + return NULL; +} + +static void route_show(const char *name, uint32_t ip_addr, uint32_t netmask, void *parameter) +{ + struct dfs_seq_file *seq = (struct dfs_seq_file *)parameter; + /* "Iface\tDestination\tGateway " + "\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU" + "\tWindow\tIRTT"); */ + /* "%63s%lx%lx%X%d%d%d%lx%d%d%d\n" */ + dfs_seq_printf(seq, "%s ", name); + dfs_seq_printf(seq, "%lx ", ip_addr); + dfs_seq_printf(seq, "%lx ", 0); + dfs_seq_printf(seq, "%X ", 1); + dfs_seq_printf(seq, "%d ", 0); + dfs_seq_printf(seq, "%d ", 0); + dfs_seq_printf(seq, "%d ", 0); + dfs_seq_printf(seq, "%lx ", netmask); + dfs_seq_printf(seq, "%d ", 0); + dfs_seq_printf(seq, "%d ", 0); + dfs_seq_printf(seq, "%d\n", 0); +} + +static int seq_show(struct dfs_seq_file *seq, void *data) +{ + /* data: The return value of the start or next*/ + dfs_seq_printf(seq, "\n"); +#if LWIP_ROUTE + inet_route_foreach(route_show, seq); +#endif + + return 0; +} + +static const struct dfs_seq_ops seq_ops = { + .start = seq_start, + .stop = seq_stop, + .next = seq_next, + .show = seq_show, +}; + +int proc_net_init(void) +{ + struct proc_dentry *dentry; + + dentry = proc_mkdir("net", NULL); + if (!dentry) + return -1; + + proc_release(dentry); + + dentry = proc_create_data("net/route", 0, NULL, NULL, NULL); + if (dentry) + { + dentry->seq_ops = &seq_ops; + } + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_net_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_partitions.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_partitions.c new file mode 100644 index 0000000..1c7a48a --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_partitions.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include + + +#define LIST_FIND_OBJ_NR 8 + +typedef struct +{ + rt_list_t *list; + rt_list_t **array; + rt_uint8_t type; + int nr; /* input: max nr, can't be 0 */ + int nr_out; /* out: got nr */ +} list_get_next_t; + +static void list_find_init(list_get_next_t *p, rt_uint8_t type, rt_list_t **array, int nr) +{ + struct rt_object_information *info; + rt_list_t *list; + + info = rt_object_get_information((enum rt_object_class_type)type); + list = &info->object_list; + + p->list = list; + p->type = type; + p->array = array; + p->nr = nr; + p->nr_out = 0; +} + +static rt_list_t *list_get_next(rt_list_t *current, list_get_next_t *arg) +{ + int first_flag = 0; + rt_base_t level; + rt_list_t *node, *list; + rt_list_t **array; + struct rt_object_information *info; + int nr; + + arg->nr_out = 0; + + if (!arg->nr || !arg->type) + { + return (rt_list_t *)RT_NULL; + } + + list = arg->list; + info = rt_list_entry(list, struct rt_object_information, object_list); + + if (!current) /* find first */ + { + node = list; + first_flag = 1; + } + else + { + node = current; + } + + level = rt_spin_lock_irqsave(&info->spinlock); + + if (!first_flag) + { + struct rt_object *obj; + /* The node in the list? */ + obj = rt_list_entry(node, struct rt_object, list); + if ((obj->type & ~RT_Object_Class_Static) != arg->type) + { + rt_spin_unlock_irqrestore(&info->spinlock, level); + return (rt_list_t *)RT_NULL; + } + } + + nr = 0; + array = arg->array; + while (1) + { + node = node->next; + + if (node == list) + { + node = (rt_list_t *)RT_NULL; + break; + } + nr++; + *array++ = node; + if (nr == arg->nr) + { + break; + } + } + + rt_spin_unlock_irqrestore(&info->spinlock, level); + arg->nr_out = nr; + return node; +} + +static int show_info(struct dfs_seq_file *seq) +{ + rt_base_t level; + list_get_next_t find_arg; + struct rt_object_information *info; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t *)RT_NULL; + + list_find_init(&find_arg, RT_Object_Class_Device, obj_list, sizeof(obj_list) / sizeof(obj_list[0])); + info = rt_list_entry(find_arg.list, struct rt_object_information, object_list); + + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_device *device; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_spin_lock_irqsave(&info->spinlock); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_spin_unlock_irqrestore(&info->spinlock, level); + continue; + } + + rt_spin_unlock_irqrestore(&info->spinlock, level); + + device = (struct rt_device *)obj; + + if (device->type == RT_Device_Class_Block) + { + struct rt_device_blk_geometry geometry = { 0 }; + + rt_device_control(device, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry); + + dfs_seq_printf(seq, "%4d %7d %14llu %s\n", 0, 0, + geometry.sector_count, device->parent.name); + } + } + } + } while (next != (rt_list_t *)RT_NULL); + + return 0; +} + +static void *seq_start(struct dfs_seq_file *seq, off_t *index) +{ + off_t i = *index; // seq->index + + return NULL + (i == 0); +} + +static void seq_stop(struct dfs_seq_file *seq, void *data) +{ +} + +static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index) +{ + /* data: The return value of the start or next*/ + off_t i = *index + 1; // seq->index + + *index = i; + + return NULL; +} + +static int seq_show(struct dfs_seq_file *seq, void *data) +{ + dfs_seq_puts(seq, "major minor #blocks name\n\n"); + /* data: The return value of the start or next*/ + show_info(seq); + + return 0; +} + +static const struct dfs_seq_ops seq_ops = { + .start = seq_start, + .stop = seq_stop, + .next = seq_next, + .show = seq_show, +}; + +int proc_partitions_init(void) +{ + struct proc_dentry *dentry = proc_create_data("partitions", 0, NULL, NULL, NULL); + if (dentry) + { + dentry->seq_ops = &seq_ops; + } + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_partitions_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c new file mode 100644 index 0000000..a413f64 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#define __RT_IPC_SOURCE__ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include "lwp_internal.h" +#include +#include "lwp_internal.h" + +#if defined(RT_USING_SMART) + +#include "lwp.h" +#include "lwp_pid.h" +#include + +struct pid_dentry +{ + const char *name; + mode_t mode; + const struct dfs_file_ops *fops; + const struct proc_ops *ops; + const struct dfs_seq_ops *seq_ops; + int (*single_show)(struct dfs_seq_file *seq, void *data); + void *data; +}; + +static char stat_transform(int __stat) +{ + switch (__stat) + { + case RT_THREAD_RUNNING: + return 'R'; + default: + return 'T'; + } +} + +static int stat_single_show(struct dfs_seq_file *seq, void *data) +{ + struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data; + rt_list_t *list; + int mask = 0; + rt_thread_t thread; + rt_uint64_t user_time_lwp = 0; + rt_uint64_t system_time_lwp = 0; + int lwp_oncpu = RT_CPUS_NR; + int lwp_oncpu_ok = 0; + struct rt_lwp *lwp = RT_NULL; + char** argv = RT_NULL; + char *filename = RT_NULL; + char *dot = RT_NULL; + + lwp_pid_lock_take(); + + lwp = lwp_from_pid_locked(dentry->pid); + argv = lwp_get_command_line_args(lwp); + + if (lwp) + { + dfs_seq_printf(seq,"%d ",dentry->pid); + if (argv) + { + filename = strrchr(argv[0], '/'); + dot = strchr(argv[0], '.'); + + if (filename != NULL) + { + filename++; + } + else + { + filename = argv[0]; + } + + if (dot != NULL) + { + *dot = '\0'; + } + + if (filename != NULL) + { + dfs_seq_printf(seq,"(%s) ", filename); + } + else + { + dfs_seq_printf(seq,"(%s) ", argv[0]); + } + + lwp_free_command_line_args(argv); + } + else + { + dfs_seq_printf(seq,"(%s) ", ""); + } + + if (lwp->terminated) + { + dfs_seq_printf(seq,"%c ",'Z'); + } + else + { + list = lwp->t_grp.next; + while (list != &lwp->t_grp) + { + thread = rt_list_entry(list, struct rt_thread, sibling); + user_time_lwp = user_time_lwp + thread->user_time; + system_time_lwp = system_time_lwp + thread->system_time; + + #if RT_CPUS_NR > 1 + #define ONCPU(thread) RT_SCHED_CTX(thread).oncpu + #else + #define ONCPU(thread) 0 + #endif + if (lwp_oncpu_ok == 0) + { + lwp_oncpu = ONCPU(thread); + lwp_oncpu_ok = 1; + } + if (stat_transform(RT_SCHED_CTX(thread).stat) == 'R') + { + lwp_oncpu = ONCPU(thread); + mask = 1; + } + list = list->next; + } + + if (mask == 1) + { + dfs_seq_printf(seq,"%c ",'R'); + } + else + { + dfs_seq_printf(seq,"%c ",'S'); + } + } + lwp_pid_lock_release(); + + if (lwp->parent != NULL) + dfs_seq_printf(seq,"%d ",lwp->parent->pid); + else + dfs_seq_printf(seq,"0 "); + + dfs_seq_printf(seq, "1 1 0 -1 4194560 48245 133976064 732 425574 "); + dfs_seq_printf(seq,"%llu ",user_time_lwp);//utime + dfs_seq_printf(seq,"%llu ",system_time_lwp);//stime + dfs_seq_printf(seq, "1204291 518742 20 0 1 0 50 "); + dfs_seq_printf(seq, "%d ",rt_aspace_count_vsz(lwp->aspace));//VSZ + dfs_seq_printf(seq, "1422 18446744073709551615 "); + dfs_seq_printf(seq, "1 1 0 0 0 0 671173123 4096 1260 0 0 0 17 "); + dfs_seq_printf(seq, "%d ", lwp_oncpu);//CPU + dfs_seq_printf(seq, "0 0 0 0 0 0 0 0 0 0 0 0 0"); + dfs_seq_printf(seq,"\n"); + } + else + { + lwp_pid_lock_release(); + } + + return 0; +} + +static int cmdline_single_show(struct dfs_seq_file *seq, void *data) +{ + struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data; + struct rt_lwp *lwp; + char** argv; + + lwp_pid_lock_take(); + lwp = lwp_from_pid_locked(dentry->pid); + argv = lwp_get_command_line_args(lwp); + lwp_pid_lock_release(); + + if (argv) + { + for (int i = 0; argv[i] != NULL; i++) + { + dfs_seq_printf(seq, "%s ", argv[i]); + } + dfs_seq_puts(seq, "\n"); + + lwp_free_command_line_args(argv); + } + else + { + dfs_seq_puts(seq, "error\n"); + } + + return 0; +} + +struct proc_dentry *proc_pid_fd_lookup(struct proc_dentry *parent, const char *name) +{ + struct proc_dentry *dentry = RT_NULL; + char num[DIRENT_NAME_MAX]; + struct rt_lwp *lwp; + struct dfs_fdtable *table; + + lwp_pid_lock_take(); + lwp = lwp_from_pid_locked(parent->pid); + table = lwp ? &lwp->fdt : RT_NULL; + lwp_pid_lock_release(); + + if (!table) + { + return RT_NULL; + } + + dfs_file_lock(); + for (int i = 0; i < table->maxfd; i++) + { + struct dfs_file *file = table->fds[i]; + if (file) + { + rt_snprintf(num, DIRENT_NAME_MAX, "%d", i); + if (rt_strcmp(num, name) == 0) + { + dentry = rt_calloc(1, sizeof(struct proc_dentry)); + if (dentry) + { + dentry->mode = (S_IFLNK | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IWUSR | S_IWGRP | S_IWOTH) | (S_IXUSR | S_IXGRP | S_IXOTH)); + dentry->ref_count = 1; + dentry->name = rt_strdup(name); + dentry->data = (void *)dfs_dentry_full_path(file->dentry); + + if (dentry->data == RT_NULL) + { + //todo add vnode->data + if (file->vnode->type == FT_SOCKET) + dentry->data = (void *)rt_strdup("socket"); + else if (file->vnode->type == FT_USER) + dentry->data = (void *)rt_strdup("user"); + else if (file->vnode->type == FT_DEVICE) + dentry->data = (void *)rt_strdup("device"); + else + dentry->data = (void *)rt_strdup("unknown"); + } + + dentry->pid = parent->pid; + break; + } + } + } + } + dfs_file_unlock(); + + return dentry; +} + +int proc_pid_fd_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count) +{ + int ret = 0, index = 0; + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + struct rt_lwp *lwp; + struct dfs_fdtable *table; + + lwp_pid_lock_take(); + lwp = lwp_from_pid_locked(entry->pid); + LWP_LOCK(lwp); + table = lwp ? &lwp->fdt : RT_NULL; + + if (!table->fds) + { + LWP_UNLOCK(lwp); + lwp_pid_lock_release(); + return 0; + } + + count = (count / sizeof(struct dirent)); + if (count == 0) + { + LWP_UNLOCK(lwp); + lwp_pid_lock_release(); + return -EINVAL; + } + + dfs_file_lock(); + for (int i = 0; i < table->maxfd; i++) + { + struct dfs_file *df = table->fds[i]; + if (df) + { + if (index >= file->fpos) + { + struct dirent *d = dirp + index - file->fpos; + + d->d_type = DT_SYMLINK; + d->d_reclen = (rt_uint16_t)sizeof(struct dirent); + rt_snprintf(d->d_name, DIRENT_NAME_MAX, "%d", i); + d->d_namlen = rt_strlen(d->d_name); + + ret++; + } + + index++; + if (index - file->fpos >= count) + { + break; + } + } + } + dfs_file_unlock(); + LWP_UNLOCK(lwp); + lwp_pid_lock_release(); + + if (ret > 0) + { + file->fpos = index; + ret = ret * sizeof(struct dirent); + } + + return ret; +} + +static const struct proc_ops proc_pid_fd_ops = { + .lookup = proc_pid_fd_lookup, +}; + +static const struct dfs_file_ops proc_pid_fd_fops = { + .getdents = proc_pid_fd_getdents, +}; + +int proc_pid_exe_readlink(struct proc_dentry *dentry, char *buf, int len) +{ + struct rt_lwp *lwp; + + lwp = lwp_self(); + len = rt_snprintf(buf, len, "%s", lwp ? lwp->exe_file : "null"); + + return len; +} + +static const struct proc_ops proc_pid_exe_ops = { + .readlink = proc_pid_exe_readlink, +}; + +int proc_pid_cwd_readlink(struct proc_dentry *dentry, char *buf, int len) +{ + struct rt_lwp *lwp; + + lwp = lwp_self(); + len = rt_snprintf(buf, len, "%s", lwp ? lwp->working_directory : "null"); + + return len; +} + +static const struct proc_ops proc_pid_cwd_ops = { + .readlink = proc_pid_cwd_readlink, +}; + +static struct pid_dentry pid_dentry_base[] = { + {"cmdline", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, cmdline_single_show, 0}, + {"cwd", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_cwd_ops, 0, 0}, + {"exe", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_exe_ops, 0, 0}, + {"fd", S_IFDIR | S_IRUSR | S_IXUSR, &proc_pid_fd_fops, &proc_pid_fd_ops, 0, 0, 0}, + {"mounts", S_IFLNK | S_IRUSR | S_IXUSR, 0, 0, 0, 0, "/proc/mounts"}, + {"stat", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, stat_single_show, 0}, +}; + +int proc_pid(int pid) +{ + char pid_str[64] = {0}; + struct proc_dentry *dentry; + + rt_snprintf(pid_str, 64, "%d", pid); + pid_str[63] = 0; + + dentry = proc_mkdir(pid_str, 0); + if (dentry) + { + struct proc_dentry *ent; + + dentry->pid = pid; + for (int j = 0; j < sizeof(pid_dentry_base) / sizeof(struct pid_dentry); j++) + { + if (S_ISDIR(pid_dentry_base[j].mode)) + { + ent = proc_mkdir_data(pid_dentry_base[j].name, pid_dentry_base[j].mode, dentry, + pid_dentry_base[j].fops, pid_dentry_base[j].data); + } + else if (S_ISLNK(pid_dentry_base[j].mode)) + { + if (pid_dentry_base[j].data == RT_NULL) + { + pid_dentry_base[j].data = "NULL"; + } + + ent = proc_symlink(pid_dentry_base[j].name, dentry, pid_dentry_base[j].data); + } + else + { + ent = proc_create_data(pid_dentry_base[j].name, pid_dentry_base[j].mode, dentry, + pid_dentry_base[j].fops, pid_dentry_base[j].data); + } + + if (ent) + { + if (pid_dentry_base[j].ops) + { + ent->ops = pid_dentry_base[j].ops; + } + + if (pid_dentry_base[j].seq_ops) + { + ent->seq_ops = pid_dentry_base[j].seq_ops; + } + + if (pid_dentry_base[j].single_show) + { + ent->single_show = pid_dentry_base[j].single_show; + } + + proc_release(ent); + } + } + proc_release(dentry); + } + + return 0; +} + +int msh_proc_pid(int argc, char **argv) +{ + if (argc > 1) + { + for (int i = 1; i <= argc - 1; i++) + { + proc_pid(atoi(argv[i])); + } + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(msh_proc_pid, proc_pid, proc pid); + +#endif diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_self.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_self.c new file mode 100644 index 0000000..a1082ea --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_self.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include + +#if defined(RT_USING_SMART) + +#include + + +int proc_self_readlink(struct proc_dentry *dentry, char *buf, int len) +{ + struct rt_lwp *lwp = RT_NULL; + + lwp = lwp_self(); + if (lwp) + { + rt_snprintf(buf, len, "%d", lwp_to_pid(lwp)); + buf[len - 1] = 0; + return rt_strlen(buf); + } + else + { + rt_snprintf(buf, len, "null"); + buf[len - 1] = 0; + return rt_strlen(buf); + } + + return -1; +} + +static const struct proc_ops proc_pid_fd_ops = { + .readlink = proc_self_readlink, +}; + +int proc_self_init(void) +{ + struct proc_dentry *ent; + + ent = proc_symlink("self", NULL, "NULL"); + if (ent) + { + ent->ops = &proc_pid_fd_ops; + } + proc_release(ent); + + return 0; +} +INIT_ENV_EXPORT(proc_self_init); + +#endif diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c new file mode 100644 index 0000000..aeba99d --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include + + +static void *seq_start(struct dfs_seq_file *seq, off_t *index) +{ + off_t i = *index; // seq->index + + return NULL + (i == 0); +} + +static void seq_stop(struct dfs_seq_file *seq, void *data) +{ +} + +static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index) +{ + /* data: The return value of the start or next*/ + off_t i = *index + 1; // seq->index + + *index = i; + + return NULL; +} + +static int seq_show(struct dfs_seq_file *seq, void *data) +{ + int i; + rt_cpu_t pcpu; + rt_uint64_t user_total = 0; + rt_uint64_t system_total = 0; + rt_uint64_t idle_total = 0; + + for (i = 0; i < RT_CPUS_NR; i++) + { + pcpu = rt_cpu_index(i); + user_total = user_total + pcpu->cpu_stat.user; + system_total = system_total + pcpu->cpu_stat.system; + idle_total = idle_total + pcpu->cpu_stat.idle; + } + dfs_seq_printf(seq, "cpu %llu 0 %llu %llu 0 0 0 0 0 0\n", user_total, system_total, idle_total); + + for (i = 0; i < RT_CPUS_NR; i++) + { + pcpu = rt_cpu_index(i); + dfs_seq_printf(seq, "cpu%d ",i); + dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.user);//user + dfs_seq_printf(seq, "0 ");//nice + dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.system);//system + dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.idle);//idle + dfs_seq_printf(seq, "0 ");//iowait + dfs_seq_printf(seq, "0 ");//irq + dfs_seq_printf(seq, "0 ");//softirq + dfs_seq_printf(seq, "0 0 0\n");//steal,guest,guest_nice + + } + + return 0; +} + +static const struct dfs_seq_ops seq_ops = { + .start = seq_start, + .stop = seq_stop, + .next = seq_next, + .show = seq_show, +}; + +rt_weak const struct dfs_seq_ops *stat_get_seq_ops(void) +{ + return &seq_ops; +} + +static int proc_open(struct dfs_file *file) +{ + return dfs_seq_open(file, stat_get_seq_ops()); +} + +static int proc_close(struct dfs_file *file) +{ + return dfs_seq_release(file); +} + +static const struct dfs_file_ops file_ops = { + .open = proc_open, + .read = dfs_seq_read, + .lseek = dfs_seq_lseek, + .close = proc_close, +}; + +int proc_stat_init(void) +{ + struct proc_dentry *dentry = proc_create_data("stat", 0, NULL, &file_ops, NULL); + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_stat_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_tty.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_tty.c new file mode 100644 index 0000000..dc77e4c --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_tty.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include + + +static void *seq_start(struct dfs_seq_file *seq, off_t *index) +{ + off_t i = *index; // seq->index + + return NULL + (i == 0); +} + +static void seq_stop(struct dfs_seq_file *seq, void *data) +{ +} + +static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index) +{ + /* data: The return value of the start or next*/ + off_t i = *index + 1; // seq->index + + *index = i; + + return NULL; +} + +static int seq_show(struct dfs_seq_file *seq, void *data) +{ + /* data: The return value of the start or next*/ + dfs_seq_puts(seq, "todo\n"); + + return 0; +} + +static const struct dfs_seq_ops seq_ops = { + .start = seq_start, + .stop = seq_stop, + .next = seq_next, + .show = seq_show, +}; + +void proc_tty_register_driver(void *driver) +{ + //todo +} + +void proc_tty_unregister_driver(void *driver) +{ + //todo +} + +int proc_tty_init(void) +{ + struct proc_dentry *dentry; + + dentry = proc_mkdir("tty", NULL); + if (!dentry) + return -1; + + proc_release(dentry); + + dentry = proc_mkdir("tty/ldisc", NULL); + proc_release(dentry); + + dentry = proc_mkdir_mode("tty/driver", S_IRUSR|S_IXUSR, NULL); + proc_release(dentry); + + dentry = proc_create_data("tty/ldiscs", 0, NULL, NULL, NULL); + if (dentry) + { + dentry->seq_ops = &seq_ops; + } + proc_release(dentry); + + dentry = proc_create_data("tty/drivers", 0, NULL, NULL, NULL); + if (dentry) + { + dentry->seq_ops = &seq_ops; + } + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_tty_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_uptime.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_uptime.c new file mode 100644 index 0000000..f16ab72 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_uptime.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include + + +static int single_show(struct dfs_seq_file *seq, void *data) +{ + dfs_seq_printf(seq, "%lu.%02lu %lu.%02lu\n", + (unsigned long)rt_tick_get_millisecond() / 1000, (unsigned long)(rt_tick_get_millisecond() % 1000) / 100, + (unsigned long)rt_tick_get_millisecond() / 1000, (unsigned long)(rt_tick_get_millisecond() % 1000) / 100); + + return 0; +} + +int proc_uptime_init(void) +{ + struct proc_dentry *dentry = proc_create_single_data("uptime", 0, NULL, single_show, NULL); + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_uptime_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_version.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_version.c new file mode 100644 index 0000000..1ca8035 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/proc_version.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include "proc.h" +#include "procfs.h" + +#include +#include + +#include +#include + +#include + + +static int single_show(struct dfs_seq_file *seq, void *data) +{ + dfs_seq_puts(seq, "\n \\ | /\n"); +#ifdef RT_USING_SMART + dfs_seq_puts(seq, "- RT - Thread Smart Operating System\n"); +#else + dfs_seq_puts(seq, "- RT - Thread Operating System\n"); +#endif + dfs_seq_printf(seq, " / | \\ %d.%d.%d build %s %s\n", + (rt_int32_t)RT_VERSION_MAJOR, (rt_int32_t)RT_VERSION_MINOR, (rt_int32_t)RT_VERSION_PATCH, + __DATE__, __TIME__); + dfs_seq_puts(seq, " 2006 - 2022 Copyright by RT-Thread team\n"); + + return 0; +} + +int proc_version_init(void) +{ + struct proc_dentry *dentry = proc_create_single_data("version", 0, NULL, single_show, NULL); + proc_release(dentry); + + return 0; +} +INIT_ENV_EXPORT(proc_version_init); diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/procfs.c b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/procfs.c new file mode 100644 index 0000000..c8b5c14 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/procfs.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "proc.h" +#include "procfs.h" + +#define PROC_DEBUG(...) //rt_kprintf + +static int dfs_procfs_open(struct dfs_file *file) +{ + rt_err_t ret = RT_EOK; + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + + + RT_ASSERT(file->ref_count > 0); + + // this file is opened and in an fdtable + if (file->ref_count > 1) + { + file->fpos = 0; + return ret; + } + + if (entry->fops && entry->fops->open) + { + ret = entry->fops->open(file); + } + + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret); + + return ret; +} + +static int dfs_procfs_close(struct dfs_file *file) +{ + rt_err_t ret = RT_EOK; + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + return ret; + } + + if (entry && entry->fops && entry->fops->close) + { + ret = entry->fops->close(file); + } + + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret); + + return ret; +} + +static ssize_t dfs_procfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos) +{ + ssize_t ret = -RT_ERROR; + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + + if (entry && entry->fops && entry->fops->read) + { + ret = entry->fops->read(file, buf, count, pos); + } + + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret); + + return ret; +} + +static ssize_t dfs_procfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos) +{ + ssize_t ret = -RT_ERROR; + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + + if (entry && entry->fops && entry->fops->write) + { + ret = entry->fops->write(file, buf, count, pos); + } + + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret); + + return ret; +} + +static int dfs_procfs_ioctl(struct dfs_file *file, int cmd, void *args) +{ + int ret = -RT_ERROR; + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + + if (entry && entry->fops && entry->fops->ioctl) + { + ret = entry->fops->ioctl(file, cmd, args); + } + + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret); + + return ret; +} + +static int dfs_procfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count) +{ + int ret = 0; + rt_uint32_t index = 0; + struct dirent *d; + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + + if (entry) + { + struct proc_dentry *iter = RT_NULL, *tmp; + + /* make integer count */ + count = (count / sizeof(struct dirent)); + if (count == 0) + { + return -EINVAL; + } + + dfs_vfs_for_each_subnode(iter, tmp, entry, node) + { + if (iter == RT_NULL) + { + break; + } + + if (index >= file->fpos) + { + d = dirp + index - file->fpos; + + if (S_ISDIR(entry->mode)) + { + d->d_type = DT_DIR; + } + else if (S_ISLNK(entry->mode)) + { + d->d_type = DT_SYMLINK; + } + else + { + d->d_type = DT_REG; + } + + d->d_namlen = rt_strlen(iter->name); + d->d_reclen = (rt_uint16_t)sizeof(struct dirent); + rt_strncpy(d->d_name, iter->name, rt_strlen(iter->name) + 1); + + ret ++; + } + + index++; + if (index - file->fpos >= count) + { + break; + } + } + + if (ret > 0) + { + file->fpos = index; + } + + if (entry->fops && entry->fops->getdents && ret < count) + { + int r; + + file->fpos -= index; + + r = entry->fops->getdents(file, dirp + ret, (count - ret) * sizeof(struct dirent)); + + ret = ret * sizeof(struct dirent); + + if (r > 0) + { + ret += r; + } + + file->fpos += index; + } + else + { + ret = ret * sizeof(struct dirent); + } + } + + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret); + + return ret; +} + +static int dfs_procfs_poll(struct dfs_file *file, struct rt_pollreq *req) +{ + int ret = -RT_ERROR; + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + + if (entry && entry->fops && entry->fops->poll) + { + ret = entry->fops->poll(file, req); + } + + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret); + + return ret; +} + +static int dfs_procfs_flush(struct dfs_file *file) +{ + int ret = -RT_ERROR; + struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data; + + if (entry && entry->fops && entry->fops->flush) + { + ret = entry->fops->flush(file); + } + + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret); + + return ret; +} + +static int dfs_procfs_mount(struct dfs_mnt *mnt, unsigned long rwflag, const void *data) +{ + RT_ASSERT(mnt != RT_NULL); + + return RT_EOK; +} + +static int dfs_procfs_umount(struct dfs_mnt *mnt) +{ + RT_ASSERT(mnt != RT_NULL); + + return RT_EOK; +} + +static int dfs_procfs_readlink(struct dfs_dentry *dentry, char *buf, int len) +{ + int ret = 0; + struct proc_dentry *entry = dfs_proc_find(dentry->pathname); + + if (entry) + { + if (S_ISLNK(entry->mode) && entry->data) + { + if (entry->ops && entry->ops->readlink) + { + ret = entry->ops->readlink(entry, buf, len); + } + else + { + rt_strncpy(buf, (const char *)entry->data, len); + buf[len - 1] = '\0'; + ret = rt_strlen(buf); + } + } + + proc_release(entry); + } + + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, dentry->pathname, ret); + + return ret; +} + +static int dfs_procfs_unlink(struct dfs_dentry *dentry) +{ + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, dentry->pathname, -1); + return -RT_ERROR; +} + +static int dfs_procfs_stat(struct dfs_dentry *dentry, struct stat *st) +{ + int ret = RT_EOK; + struct dfs_vnode *vnode; + + if (dentry && dentry->vnode) + { + vnode = dentry->vnode; + + st->st_dev = (dev_t)(dentry->mnt->dev_id); + st->st_ino = (ino_t)dfs_dentry_full_path_crc32(dentry); + + st->st_gid = vnode->gid; + st->st_uid = vnode->uid; + st->st_mode = vnode->mode; + st->st_nlink = vnode->nlink; + st->st_size = vnode->size; + st->st_mtim.tv_nsec = vnode->mtime.tv_nsec; + st->st_mtim.tv_sec = vnode->mtime.tv_sec; + st->st_ctim.tv_nsec = vnode->ctime.tv_nsec; + st->st_ctim.tv_sec = vnode->ctime.tv_sec; + st->st_atim.tv_nsec = vnode->atime.tv_nsec; + st->st_atim.tv_sec = vnode->atime.tv_sec; + } + + PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, dentry->pathname, ret); + + return ret; +} + +static int dfs_procfs_statfs(struct dfs_mnt *mnt, struct statfs *buf) +{ + if (mnt && buf) + { + buf->f_bsize = 512; + buf->f_blocks = 2048 * 64; // 64M + buf->f_bfree = buf->f_blocks; + buf->f_bavail = buf->f_bfree; + } + + PROC_DEBUG(" %s %d\n", __func__, __LINE__); + + return RT_EOK; +} + +static struct dfs_vnode *dfs_procfs_lookup(struct dfs_dentry *dentry) +{ + struct dfs_vnode *vnode = RT_NULL; + struct proc_dentry *entry = dfs_proc_find(dentry->pathname); + + if (entry) + { + vnode = dfs_vnode_create(); + if (vnode) + { + vnode->nlink = 1; + vnode->size = 0; + if (S_ISDIR(entry->mode)) + { + vnode->mode = entry->mode; + vnode->type = FT_DIRECTORY; + } + else if (S_ISLNK(entry->mode)) + { + vnode->mode = entry->mode; + vnode->type = FT_SYMLINK; + } + else + { + vnode->mode = entry->mode; + vnode->type = FT_REGULAR; + } + + vnode->data = entry; + vnode->mnt = dentry->mnt; + } + + proc_release(entry); + } + + PROC_DEBUG(" %s %d >> %s\n", __func__, __LINE__, dentry->pathname); + + return vnode; +} + +static struct dfs_vnode *dfs_procfs_create_vnode(struct dfs_dentry *dentry, int type, mode_t mode) +{ + return RT_NULL; +} + +static int dfs_procfs_free_vnode(struct dfs_vnode *vnode) +{ + return 0; +} + +static const struct dfs_file_ops _procfs_fops = +{ + .open = dfs_procfs_open, + .close = dfs_procfs_close, + .lseek = generic_dfs_lseek, + .read = dfs_procfs_read, + .write = dfs_procfs_write, + .ioctl = dfs_procfs_ioctl, + .getdents = dfs_procfs_getdents, + .poll = dfs_procfs_poll, + .flush = dfs_procfs_flush, +}; + +static const struct dfs_filesystem_ops _procfs_ops = +{ + .name = "procfs", + + .default_fops = &_procfs_fops, + + .mount = dfs_procfs_mount, + .umount = dfs_procfs_umount, + .readlink = dfs_procfs_readlink, + .unlink = dfs_procfs_unlink, + .stat = dfs_procfs_stat, + .statfs = dfs_procfs_statfs, + .lookup = dfs_procfs_lookup, + .create_vnode = dfs_procfs_create_vnode, + .free_vnode = dfs_procfs_free_vnode, +}; + +static struct dfs_filesystem_type _procfs = +{ + .fs_ops = &_procfs_ops, +}; + +int dfs_procfs_init(void) +{ + /* register procfs file system */ + dfs_register(&_procfs); + + return 0; +} +INIT_COMPONENT_EXPORT(dfs_procfs_init); + +int proc_read_data(struct dfs_file *file, void *buf, size_t count, off_t *pos) +{ + if (file->fpos >= file->vnode->size) + { + return 0; + } + + if (file->data) + { + count = file->vnode->size - file->fpos >= count ? count : file->vnode->size - file->fpos; + rt_strncpy(buf, file->data + file->fpos, count); + + file->fpos += count; + *pos = file->fpos; + } + else + { + return 0; + } + + return count; +} diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/procfs/procfs.h b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/procfs.h new file mode 100644 index 0000000..6e921ae --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/filesystems/procfs/procfs.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __PROC_FS_H__ +#define __PROC_FS_H__ + +#include + +int dfs_procfs_init(void); + +int proc_read_data(struct dfs_file *file, void *buf, size_t count, off_t *pos); + +#endif diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.c b/rt-thread/components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.c index c6f4f3d..75bffdf 100644 --- a/rt-thread/components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.c +++ b/rt-thread/components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.c @@ -262,7 +262,7 @@ ptsno_t ptyfs_register_pts(rt_device_t ptmx, rt_device_t pts) pts_file = rt_calloc(1, sizeof(struct ptyfs_file)); if (pts_file) { - snprintf(pts_file->basename, DIRENT_NAME_MAX, "%lu", rc); + snprintf(pts_file->basename, DIRENT_NAME_MAX, "%lu", (unsigned long)rc); ptyfile_init(pts_file, sb, 0, PTYFS_TYPE_FILE_SLAVE, PTS_DEFAULT_FILE_MODE, pts); ptyfile_add_to_root(sb, pts_file); @@ -296,7 +296,7 @@ rt_err_t ptyfs_unregister_pts(rt_device_t ptmx, ptsno_t ptsno) else { /* get path and findout device */ - snprintf(path_buf, sizeof(path_buf), "%lu", ptsno); + snprintf(path_buf, sizeof(path_buf), "%lu", (unsigned long)ptsno); pts_file = ptyfile_lookup(sb, path_buf); if (pts_file) { diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/romfs/dfs_romfs.c b/rt-thread/components/dfs/dfs_v2/filesystems/romfs/dfs_romfs.c index 7cb30b3..6350ad7 100644 --- a/rt-thread/components/dfs/dfs_v2/filesystems/romfs/dfs_romfs.c +++ b/rt-thread/components/dfs/dfs_v2/filesystems/romfs/dfs_romfs.c @@ -365,6 +365,7 @@ static const struct dfs_file_ops _rom_fops = { .open = dfs_romfs_open, .close = dfs_romfs_close, + .ioctl = dfs_romfs_ioctl, .lseek = generic_dfs_lseek, .read = dfs_romfs_read, .getdents = dfs_romfs_getdents, diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c b/rt-thread/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c index 1c2a509..c1ff4a1 100644 --- a/rt-thread/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c +++ b/rt-thread/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c @@ -99,15 +99,13 @@ static int _get_subdir(const char *path, char *name) static int _free_subdir(struct tmpfs_file *dfile) { - struct tmpfs_file *file; - rt_list_t *list, *temp_list; + struct tmpfs_file *file = RT_NULL, *tmp; struct tmpfs_sb *superblock; RT_ASSERT(dfile->type == TMPFS_TYPE_DIR); - rt_list_for_each_safe(list, temp_list, &dfile->subdirs) + dfs_vfs_for_each_subnode(file, tmp, dfile, node) { - file = rt_list_entry(list, struct tmpfs_file, sibling); if (file->type == TMPFS_TYPE_DIR) { _free_subdir(file); @@ -122,7 +120,7 @@ static int _free_subdir(struct tmpfs_file *dfile) RT_ASSERT(superblock != NULL); rt_spin_lock(&superblock->lock); - rt_list_remove(&(file->sibling)); + dfs_vfs_remove_node(&file->node); rt_spin_unlock(&superblock->lock); rt_free(file); @@ -141,13 +139,11 @@ static int dfs_tmpfs_mount(struct dfs_mnt *mnt, { superblock->df_size = sizeof(struct tmpfs_sb); superblock->magic = TMPFS_MAGIC; - rt_list_init(&superblock->sibling); superblock->root.name[0] = '/'; superblock->root.sb = superblock; superblock->root.type = TMPFS_TYPE_DIR; - rt_list_init(&superblock->root.sibling); - rt_list_init(&superblock->root.subdirs); + dfs_vfs_init_node(&superblock->root.node); rt_spin_lock_init(&superblock->lock); @@ -236,8 +232,7 @@ struct tmpfs_file *dfs_tmpfs_lookup(struct tmpfs_sb *superblock, { const char *subpath, *curpath, *filename = RT_NULL; char subdir_name[TMPFS_NAME_MAX]; - struct tmpfs_file *file, *curfile; - rt_list_t *list; + struct tmpfs_file *file, *curfile, *tmp; subpath = path; while (*subpath == '/' && *subpath) @@ -265,9 +260,8 @@ find_subpath: rt_spin_lock(&superblock->lock); - rt_list_for_each(list, &curfile->subdirs) + dfs_vfs_for_each_subnode(file, tmp, curfile, node) { - file = rt_list_entry(list, struct tmpfs_file, sibling); if (filename) /* find file */ { if (rt_strcmp(file->name, filename) == 0) @@ -503,8 +497,7 @@ static int dfs_tmpfs_getdents(struct dfs_file *file, { rt_size_t index, end; struct dirent *d; - struct tmpfs_file *d_file, *n_file; - rt_list_t *list; + struct tmpfs_file *d_file, *n_file, *tmp; struct tmpfs_sb *superblock; d_file = (struct tmpfs_file *)file->vnode->data; @@ -527,9 +520,8 @@ static int dfs_tmpfs_getdents(struct dfs_file *file, index = 0; count = 0; - rt_list_for_each(list, &d_file->subdirs) + dfs_vfs_for_each_subnode(n_file, tmp, d_file, node) { - n_file = rt_list_entry(list, struct tmpfs_file, sibling); if (index >= (rt_size_t)file->fpos) { d = dirp + count; @@ -573,7 +565,7 @@ static int dfs_tmpfs_unlink(struct dfs_dentry *dentry) return -ENOENT; rt_spin_lock(&superblock->lock); - rt_list_remove(&(d_file->sibling)); + dfs_vfs_remove_node(&d_file->node); rt_spin_unlock(&superblock->lock); if (rt_atomic_load(&(dentry->ref_count)) == 1) @@ -631,13 +623,13 @@ static int dfs_tmpfs_rename(struct dfs_dentry *old_dentry, struct dfs_dentry *ne RT_ASSERT(p_file != NULL); rt_spin_lock(&superblock->lock); - rt_list_remove(&(d_file->sibling)); + dfs_vfs_remove_node(&d_file->node); rt_spin_unlock(&superblock->lock); strncpy(d_file->name, file_name, TMPFS_NAME_MAX); rt_spin_lock(&superblock->lock); - rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling)); + dfs_vfs_append_node(&p_file->node, &d_file->node); rt_spin_unlock(&superblock->lock); rt_free(parent_path); @@ -745,8 +737,7 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t strncpy(d_file->name, file_name, TMPFS_NAME_MAX); - rt_list_init(&(d_file->subdirs)); - rt_list_init(&(d_file->sibling)); + dfs_vfs_init_node(&d_file->node); d_file->data = NULL; d_file->size = 0; d_file->sb = superblock; @@ -767,7 +758,7 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t #endif } rt_spin_lock(&superblock->lock); - rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling)); + dfs_vfs_append_node(&p_file->node, &d_file->node); rt_spin_unlock(&superblock->lock); vnode->mnt = dentry->mnt; diff --git a/rt-thread/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h b/rt-thread/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h index 365f1e0..8f822c6 100644 --- a/rt-thread/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h +++ b/rt-thread/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h @@ -12,6 +12,7 @@ #define __DFS_TMPFS_H__ #include +#include #define TMPFS_NAME_MAX 32 #define TMPFS_MAGIC 0x0B0B0B0B @@ -25,8 +26,7 @@ struct tmpfs_file { rt_uint32_t type; /* file type */ char name[TMPFS_NAME_MAX]; /* file name */ - rt_list_t subdirs; /* file subdir list */ - rt_list_t sibling; /* file sibling list */ + struct dfs_vfs_node node; /* file node in the tmpfs */ struct tmpfs_sb *sb; /* superblock ptr */ rt_uint8_t *data; /* file date ptr */ rt_size_t size; /* file size */ diff --git a/rt-thread/components/dfs/dfs_v2/include/dfs_fs.h b/rt-thread/components/dfs/dfs_v2/include/dfs_fs.h index d30967a..dcf2fa2 100644 --- a/rt-thread/components/dfs/dfs_v2/include/dfs_fs.h +++ b/rt-thread/components/dfs/dfs_v2/include/dfs_fs.h @@ -20,6 +20,38 @@ extern "C" { #endif +#define MS_RDONLY 1 +#define MS_NOSUID 2 +#define MS_NODEV 4 +#define MS_NOEXEC 8 +#define MS_SYNCHRONOUS 16 +#define MS_REMOUNT 32 +#define MS_MANDLOCK 64 +#define MS_DIRSYNC 128 +#define MS_NOATIME 1024 +#define MS_NODIRATIME 2048 +#define MS_BIND 4096 +#define MS_MOVE 8192 +#define MS_REC 16384 +#define MS_SILENT 32768 +#define MS_POSIXACL (1<<16) +#define MS_UNBINDABLE (1<<17) +#define MS_PRIVATE (1<<18) +#define MS_SLAVE (1<<19) +#define MS_SHARED (1<<20) +#define MS_RELATIME (1<<21) +#define MS_KERNMOUNT (1<<22) +#define MS_I_VERSION (1<<23) +#define MS_STRICTATIME (1<<24) +#define MS_LAZYTIME (1<<25) +#define MS_NOREMOTELOCK (1<<27) +#define MS_NOSEC (1<<28) +#define MS_BORN (1<<29) +#define MS_ACTIVE (1<<30) +#define MS_NOUSER (1U<<31) + +#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME) + /* file system partition table */ struct dfs_partition { @@ -87,6 +119,7 @@ int dfs_unregister(struct dfs_filesystem_type *fs); int dfs_register(struct dfs_filesystem_type *fs); const char *dfs_filesystem_get_mounted_path(struct rt_device *device); +int dfs_remount(const char *path, rt_ubase_t flags, void *data); int dfs_mount(const char *device_name, const char *path, const char *filesystemtype, diff --git a/rt-thread/components/dfs/dfs_v2/include/dfs_mnt.h b/rt-thread/components/dfs/dfs_v2/include/dfs_mnt.h index 207c9b6..da99ec2 100644 --- a/rt-thread/components/dfs/dfs_v2/include/dfs_mnt.h +++ b/rt-thread/components/dfs/dfs_v2/include/dfs_mnt.h @@ -39,6 +39,8 @@ struct dfs_mnt #define MNT_IS_UMOUNT 0x8 /* the mnt is unmount */ #define MNT_IS_LOCKED 0x10 /* the mnt is locked */ #define MNT_FORCE 0x20 /* the mnt force unmount */ +#define MNT_LAZY_UMNT 0x40 /* the mnt has pending umount */ +#define MNT_RDONLY 0x80 /* the mnt is read only */ rt_atomic_t ref_count; /* reference count */ @@ -60,9 +62,16 @@ const char *dfs_mnt_get_mounted_path(struct rt_device *device); struct dfs_mnt* dfs_mnt_ref(struct dfs_mnt* mnt); int dfs_mnt_unref(struct dfs_mnt* mnt); +int dfs_mnt_umount(struct dfs_mnt *mnt, int flags); +int dfs_mnt_setflags(struct dfs_mnt *mnt, int flags); + rt_bool_t dfs_mnt_has_child_mnt(struct dfs_mnt *mnt, const char* fullpath); int dfs_mnt_foreach(struct dfs_mnt* (*func)(struct dfs_mnt *mnt, void *parameter), void *parameter); +int dfs_mnt_umount_iter(rt_bool_t (*filter)(struct dfs_mnt *mnt, void *parameter), void *parameter); + +typedef void (*dfs_mnt_umnt_cb_t)(struct dfs_mnt *mnt); +RT_OBJECT_HOOKLIST_DECLARE(dfs_mnt_umnt_cb_t, dfs_mnt_umnt); #ifdef __cplusplus } diff --git a/rt-thread/components/dfs/dfs_v2/include/dfs_pcache.h b/rt-thread/components/dfs/dfs_v2/include/dfs_pcache.h index 01abfe3..5452196 100644 --- a/rt-thread/components/dfs/dfs_v2/include/dfs_pcache.h +++ b/rt-thread/components/dfs/dfs_v2/include/dfs_pcache.h @@ -118,6 +118,7 @@ int dfs_aspace_mmap_write(struct dfs_file *file, struct rt_varea *varea, void *d void dfs_pcache_release(size_t count); void dfs_pcache_unmount(struct dfs_mnt *mnt); +void dfs_pcache_clean(struct dfs_mnt *mnt); #ifdef __cplusplus } diff --git a/rt-thread/components/dfs/dfs_v2/include/dfs_seq_file.h b/rt-thread/components/dfs/dfs_v2/include/dfs_seq_file.h index 6dbc30f..f0c1de7 100644 --- a/rt-thread/components/dfs/dfs_v2/include/dfs_seq_file.h +++ b/rt-thread/components/dfs/dfs_v2/include/dfs_seq_file.h @@ -56,7 +56,7 @@ static inline void dfs_seq_setwidth(struct dfs_seq_file *seq, size_t size) int dfs_seq_open(struct dfs_file *file, const struct dfs_seq_ops *ops); ssize_t dfs_seq_read(struct dfs_file *file, void *buf, size_t size, off_t *pos); -ssize_t dfs_seq_lseek(struct dfs_file *file, off_t offset, int whence); +off_t dfs_seq_lseek(struct dfs_file *file, off_t offset, int whence); int dfs_seq_release(struct dfs_file *file); int dfs_seq_write(struct dfs_seq_file *seq, const void *data, size_t len); diff --git a/rt-thread/components/dfs/dfs_v2/include/dfs_vfs.h b/rt-thread/components/dfs/dfs_v2/include/dfs_vfs.h new file mode 100644 index 0000000..3674192 --- /dev/null +++ b/rt-thread/components/dfs/dfs_v2/include/dfs_vfs.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __DFS_VFS_H__ +#define __DFS_VFS_H__ + +#include "dfs_file.h" +#include "dfs_fs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct dfs_vfs_node +{ + rt_list_t subnode; /* file subnode list */ + rt_list_t sibling; /* file sibling list */ +}; + +rt_inline void dfs_vfs_init_node(struct dfs_vfs_node *node) +{ + rt_list_init(&node->subnode); + rt_list_init(&node->sibling); +} + +rt_inline void dfs_vfs_append_node(struct dfs_vfs_node *dir, struct dfs_vfs_node *node) +{ + rt_list_insert_after(&(dir->subnode), &(node->sibling)); +} + +rt_inline void dfs_vfs_remove_node(struct dfs_vfs_node *node) +{ + rt_list_remove(&(node->sibling)); +} + +#define dfs_vfs_for_each_subnode(node, tmp, dir, member) \ + rt_list_for_each_entry_safe(node, tmp, &dir->member.subnode, member.sibling) + +#ifdef __cplusplus +} +#endif + +#endif /*__DFS_VFS_H__*/ diff --git a/rt-thread/components/dfs/dfs_v2/src/dfs.c b/rt-thread/components/dfs/dfs_v2/src/dfs.c index 939e4dd..527ba89 100644 --- a/rt-thread/components/dfs/dfs_v2/src/dfs.c +++ b/rt-thread/components/dfs/dfs_v2/src/dfs.c @@ -537,6 +537,7 @@ int dfs_dup(int oldfd, int startfd) fdt = dfs_fdtable_get(); if ((oldfd < 0) || (oldfd >= fdt->maxfd)) { + rt_set_errno(-EBADF); goto exit; } if (!fdt->fds[oldfd]) @@ -668,12 +669,17 @@ sysret_t sys_dup(int oldfd) int sys_dup(int oldfd) #endif { + int err = 0; int newfd = dfs_dup(oldfd, (dfs_fdtable_get() == &_fdtab) ? DFS_STDIO_OFFSET : 0); + if(newfd < 0) + { + err = rt_get_errno(); + } #ifdef RT_USING_SMART - return (sysret_t)newfd; + return err < 0 ? err : newfd; #else - return newfd; + return err < 0 ? err : newfd; #endif } diff --git a/rt-thread/components/dfs/dfs_v2/src/dfs_file.c b/rt-thread/components/dfs/dfs_v2/src/dfs_file.c index 53e110e..57ff2e9 100644 --- a/rt-thread/components/dfs/dfs_v2/src/dfs_file.c +++ b/rt-thread/components/dfs/dfs_v2/src/dfs_file.c @@ -1379,6 +1379,7 @@ int dfs_file_link(const char *oldname, const char *newname) if (dfs_file_isdir(oldname) == 0) { + rt_set_errno(-EPERM); return ret; } @@ -1567,6 +1568,10 @@ int dfs_file_symlink(const char *target, const char *linkpath) rt_free(parent); } } + else + { + rt_set_errno(-EPERM); + } if (fullpath != linkpath) rt_free(fullpath); diff --git a/rt-thread/components/dfs/dfs_v2/src/dfs_fs.c b/rt-thread/components/dfs/dfs_v2/src/dfs_fs.c index e65ccee..ee077cd 100644 --- a/rt-thread/components/dfs/dfs_v2/src/dfs_fs.c +++ b/rt-thread/components/dfs/dfs_v2/src/dfs_fs.c @@ -95,6 +95,52 @@ int dfs_unregister(struct dfs_filesystem_type *fs) return ret; } +#define REMNT_UNSUPP_FLAGS (~(MS_REMOUNT | MS_RMT_MASK)) +int dfs_remount(const char *path, rt_ubase_t flags, void *data) +{ + int rc = 0; + char *fullpath = RT_NULL; + struct dfs_mnt *mnt = RT_NULL; + + if (flags & REMNT_UNSUPP_FLAGS) + { + return -EINVAL; + } + + fullpath = dfs_normalize_path(RT_NULL, path); + if (!fullpath) + { + rc = -ENOENT; + } + else + { + DLOG(msg, "dfs", "mnt", DLOG_MSG, "mnt = dfs_mnt_lookup(%s)", fullpath); + mnt = dfs_mnt_lookup(fullpath); + if (mnt) + { + dfs_lock(); + dfs_mnt_setflags(mnt, flags); + dfs_unlock(); + } + else + { + struct stat buf = {0}; + if (dfs_file_stat(fullpath, &buf) == 0 && S_ISBLK(buf.st_mode)) + { + /* path was not already mounted on target */ + rc = -EINVAL; + } + else + { + /* path is not a directory */ + rc = -ENOTDIR; + } + } + } + + return rc; +} + /* * parent(mount path) * mnt_parent <- - - - - - - + @@ -127,7 +173,7 @@ int dfs_mount(const char *device_name, } else { - rt_set_errno(ENOENT); + rt_set_errno(ENODEV); ret = -1; } @@ -300,7 +346,7 @@ int dfs_mount(const char *device_name, int dfs_umount(const char *specialfile, int flags) { - int ret = -RT_ERROR; + int ret = -1; char *fullpath = RT_NULL; struct dfs_mnt *mnt = RT_NULL; @@ -314,7 +360,7 @@ int dfs_umount(const char *specialfile, int flags) if (strcmp(mnt->fullpath, fullpath) == 0) { /* is the mount point */ - rt_atomic_t ref_count = rt_atomic_load(&(mnt->ref_count)); + rt_base_t ref_count = rt_atomic_load(&(mnt->ref_count)); if (!(mnt->flags & MNT_IS_LOCKED) && rt_list_isempty(&mnt->child) && (ref_count == 1 || (flags & MNT_FORCE))) { @@ -327,17 +373,19 @@ int dfs_umount(const char *specialfile, int flags) } else { - LOG_E("the file system is busy!"); + LOG_I("the file system is busy!"); + ret = -EBUSY; } } else { - LOG_E("the path:%s is not a mountpoint!", fullpath); + LOG_I("the path:%s is not a mountpoint!", fullpath); + ret = -EINVAL; } } else { - LOG_E("no filesystem found."); + LOG_I("no filesystem found."); } rt_free(fullpath); } diff --git a/rt-thread/components/dfs/dfs_v2/src/dfs_mnt.c b/rt-thread/components/dfs/dfs_v2/src/dfs_mnt.c index 4ad73c6..a207072 100644 --- a/rt-thread/components/dfs/dfs_v2/src/dfs_mnt.c +++ b/rt-thread/components/dfs/dfs_v2/src/dfs_mnt.c @@ -10,17 +10,21 @@ #include -#include "dfs.h" -#include "dfs_mnt.h" -#include "dfs_dentry.h" #include "dfs_private.h" +#include +#include +#include +#include + #define DBG_TAG "DFS.mnt" #define DBG_LVL DBG_WARNING #include static struct dfs_mnt *_root_mnt = RT_NULL; +RT_OBJECT_HOOKLIST_DEFINE(dfs_mnt_umnt); + /* * mnt tree structure * @@ -75,6 +79,7 @@ int dfs_mnt_insert(struct dfs_mnt* mnt, struct dfs_mnt* child) child = _root_mnt; rt_atomic_sub(&(_root_mnt->parent->ref_count), 1); rt_atomic_sub(&(_root_mnt->ref_count), 1); + _root_mnt->flags &= ~MNT_IS_LOCKED; _root_mnt = dfs_mnt_ref(mnt); mnt->parent = dfs_mnt_ref(mnt); @@ -242,21 +247,24 @@ struct dfs_mnt* dfs_mnt_ref(struct dfs_mnt* mnt) return mnt; } -int dfs_mnt_unref(struct dfs_mnt* mnt) +int dfs_mnt_unref(struct dfs_mnt *mnt) { rt_err_t ret = RT_EOK; + rt_base_t ref_count; if (mnt) { - rt_atomic_sub(&(mnt->ref_count), 1); + ref_count = rt_atomic_sub(&(mnt->ref_count), 1) - 1; - if (rt_atomic_load(&(mnt->ref_count)) == 0) + if (ref_count == 0) { dfs_lock(); if (mnt->flags & MNT_IS_UMOUNT) { mnt->fs_ops->umount(mnt); + + RT_OBJECT_HOOKLIST_CALL(dfs_mnt_umnt, (mnt)); } /* free full path */ @@ -278,6 +286,21 @@ int dfs_mnt_unref(struct dfs_mnt* mnt) return ret; } +int dfs_mnt_setflags(struct dfs_mnt *mnt, int flags) +{ + int error = 0; + + if (flags & MS_RDONLY) + { + mnt->flags |= MNT_RDONLY; +#ifdef RT_USING_PAGECACHE + dfs_pcache_clean(mnt); +#endif + } + + return error; +} + int dfs_mnt_destroy(struct dfs_mnt* mnt) { rt_err_t ret = RT_EOK; diff --git a/rt-thread/components/dfs/dfs_v2/src/dfs_pcache.c b/rt-thread/components/dfs/dfs_v2/src/dfs_pcache.c index 350ad1f..cba36d8 100644 --- a/rt-thread/components/dfs/dfs_v2/src/dfs_pcache.c +++ b/rt-thread/components/dfs/dfs_v2/src/dfs_pcache.c @@ -13,17 +13,19 @@ #define DBG_LVL DBG_WARNING #include -#include "dfs_pcache.h" -#include "dfs_dentry.h" -#include "dfs_mnt.h" -#include "mm_page.h" -#include -#include +#include +#include +#include #include #ifdef RT_USING_PAGECACHE +#include +#include +#include +#include + #ifndef RT_PAGECACHE_COUNT #define RT_PAGECACHE_COUNT 4096 #endif @@ -160,7 +162,7 @@ void dfs_pcache_release(size_t count) dfs_pcache_unlock(); } -void dfs_pcache_unmount(struct dfs_mnt *mnt) +static void _pcache_clean(struct dfs_mnt *mnt, int (*cb)(struct dfs_aspace *aspace)) { rt_list_t *node = RT_NULL; struct dfs_aspace *aspace = RT_NULL; @@ -175,7 +177,7 @@ void dfs_pcache_unmount(struct dfs_mnt *mnt) if (aspace && aspace->mnt == mnt) { dfs_aspace_clean(aspace); - dfs_aspace_release(aspace); + cb(aspace); } } @@ -187,13 +189,28 @@ void dfs_pcache_unmount(struct dfs_mnt *mnt) if (aspace && aspace->mnt == mnt) { dfs_aspace_clean(aspace); - dfs_aspace_release(aspace); + cb(aspace); } } dfs_pcache_unlock(); } +void dfs_pcache_unmount(struct dfs_mnt *mnt) +{ + _pcache_clean(mnt, dfs_aspace_release); +} + +static int _dummy_cb(struct dfs_aspace *mnt) +{ + return 0; +} + +void dfs_pcache_clean(struct dfs_mnt *mnt) +{ + _pcache_clean(mnt, _dummy_cb); +} + static int dfs_pcache_limit_check(void) { int index = 4; @@ -1138,14 +1155,21 @@ int dfs_aspace_write(struct dfs_file *file, const void *buf, size_t count, off_t if (file && file->vnode && file->vnode->aspace) { - if (!(file->vnode->aspace->ops->write)) - return ret; struct dfs_vnode *vnode = file->vnode; struct dfs_aspace *aspace = vnode->aspace; struct dfs_page *page; char *ptr = (char *)buf; + if (!(aspace->ops->write)) + { + return ret; + } + else if (aspace->mnt && (aspace->mnt->flags & MNT_RDONLY)) + { + return -EROFS; + } + ret = 0; while (count) @@ -1380,7 +1404,8 @@ int dfs_aspace_unmap(struct dfs_file *file, struct rt_varea *varea) rt_varea_unmap_page(map_varea, vaddr); - if (varea->attr == MMU_MAP_U_RWCB && page->fpos < page->aspace->vnode->size) + if (!rt_varea_is_private_locked(varea) && + page->fpos < page->aspace->vnode->size) { dfs_page_dirty(page); } @@ -1425,7 +1450,7 @@ int dfs_aspace_page_unmap(struct dfs_file *file, struct rt_varea *varea, void *v if (map && varea->aspace == map->aspace && vaddr == map->vaddr) { - if (varea->attr == MMU_MAP_U_RWCB) + if (!rt_varea_is_private_locked(varea)) { dfs_page_dirty(page); } diff --git a/rt-thread/components/dfs/dfs_v2/src/dfs_posix.c b/rt-thread/components/dfs/dfs_v2/src/dfs_posix.c index ef6e8e7..38e17c3 100644 --- a/rt-thread/components/dfs/dfs_v2/src/dfs_posix.c +++ b/rt-thread/components/dfs/dfs_v2/src/dfs_posix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -31,7 +31,17 @@ * return a file descriptor according specified flags. * * @param file the path name of file. - * @param flags the file open flags. + * @param flags the file open flags. Common values include: + * - Access modes (mutually exclusive): + * - `O_RDONLY`: Open for read-only access. + * - `O_WRONLY`: Open for write-only access. + * - `O_RDWR`: Open for both reading and writing. + * - File status flags (can be combined with bitwise OR `|`): + * - `O_CREAT`: Create the file if it does not exist. Requires a `mode` argument. + * - `O_TRUNC`: Truncate the file to zero length if it already exists. + * - `O_APPEND`: Append writes to the end of the file. + * - `O_EXCL`: Ensure that `O_CREAT` creates the file exclusively. + * - Other platform-specific flags * * @return the non-negative integer on successful open, others for failed. */ @@ -81,6 +91,22 @@ RTM_EXPORT(open); #ifndef AT_FDCWD #define AT_FDCWD (-100) #endif + +/** + * @brief Opens a file relative to a directory file descriptor. + * + * @param dirfd The file descriptor of the directory to base the relative path on. + * @param path The path to the file to be opened, relative to the directory specified by `dirfd`. + * Can be an absolute path (in which case `dirfd` is ignored). + * @param flag File access and status flags (e.g., `O_RDONLY`, `O_WRONLY`, `O_CREAT`). + * + * @return On success, returns a new file descriptor for the opened file. + * On failure, returns `-1` and sets `errno` to indicate the error. + * + * @note When using relative paths, ensure `dirfd` is a valid directory descriptor. + * When `pathname` is absolute, the `dirfd` argument is ignored. + * + */ int openat(int dirfd, const char *path, int flag, ...) { struct dfs_file *d; @@ -171,7 +197,7 @@ int utimensat(int __fd, const char *__path, const struct timespec __times[2], in } } - //update time + /*update time*/ attr.ia_valid = ATTR_ATIME_SET | ATTR_MTIME_SET; time(¤t_time); if (UTIME_NOW == __times[0].tv_nsec) @@ -374,14 +400,22 @@ ssize_t write(int fd, const void *buf, size_t len) RTM_EXPORT(write); /** - * this function is a POSIX compliant version, which will seek the offset for + * this function is a POSIX compliant version, which will Reposition the file offset for * an open file descriptor. * - * @param fd the file descriptor. - * @param offset the offset to be seeked. - * @param whence the directory of seek. + * The `lseek` function sets the file offset for the file descriptor `fd` + * to a new value, determined by the `offset` and `whence` parameters. + * It can be used to seek to specific positions in a file for reading or writing. * - * @return the current read/write position in the file, or -1 on failed. + * @param fd the file descriptor. + * @param offset The offset, in bytes, to set the file position. + * The meaning of `offset` depends on the value of `whence`. + * @param whence the directive of seek. It can be one of: + * - `SEEK_SET`: Set the offset to `offset` bytes from the beginning of the file. + * - `SEEK_CUR`: Set the offset to its current location plus `offset` bytes. + * - `SEEK_END`: Set the offset to the size of the file plus `offset` bytes. + * + * @return the resulting read/write position in the file, or -1 on failed. */ off_t lseek(int fd, off_t offset, int whence) { @@ -399,7 +433,7 @@ off_t lseek(int fd, off_t offset, int whence) result = dfs_file_lseek(file, offset, whence); if (result < 0) { - rt_set_errno(result); + rt_set_errno(-EPERM); return -1; } @@ -581,9 +615,15 @@ RTM_EXPORT(fsync); * control functions on devices. * * @param fildes the file description - * @param cmd the specified command + * @param cmd the specified command, Common values include: + * - `F_DUPFD`: Duplicate a file descriptor. + * - `F_GETFD`: Get the file descriptor flags. + * - `F_SETFD`: Set the file descriptor flags. + * - `F_GETFL`: Get the file status flags. + * - `F_SETFL`: Set the file status flags. * @param ... represents the additional information that is needed by this - * specific device to perform the requested function. + * specific device to perform the requested function. For example: + * - When `cmd` is `F_SETFL`, an additional integer argument specifies the new status flags. * * @return 0 on successful completion. Otherwise, -1 shall be returned and errno * set to indicate the error. @@ -765,7 +805,7 @@ RTM_EXPORT(fstatfs); * this function is a POSIX compliant version, which will make a directory * * @param path the directory path to be made. - * @param mode + * @param mode The permission mode for the new directory (unused here, can be set to 0). * * @return 0 on successful, others on failed. */ @@ -827,7 +867,7 @@ int rmdir(const char *pathname) if (!pathname) { - rt_set_errno(-RT_ERROR); + rt_set_errno(-EPERM); return -1; } @@ -852,7 +892,7 @@ int rmdir(const char *pathname) if (dirent) { - rt_set_errno(-RT_ERROR); + rt_set_errno(-EPERM); return -1; } } @@ -861,7 +901,7 @@ int rmdir(const char *pathname) { if (S_ISLNK(stat.st_mode)) { - rt_set_errno(-RT_ERROR); + rt_set_errno(-EPERM); return -1; } } diff --git a/rt-thread/components/dfs/dfs_v2/src/dfs_seq_file.c b/rt-thread/components/dfs/dfs_v2/src/dfs_seq_file.c index 9521f53..2980b03 100644 --- a/rt-thread/components/dfs/dfs_v2/src/dfs_seq_file.c +++ b/rt-thread/components/dfs/dfs_v2/src/dfs_seq_file.c @@ -256,7 +256,7 @@ Enomem: goto Done; } -ssize_t dfs_seq_lseek(struct dfs_file *file, off_t offset, int whence) +off_t dfs_seq_lseek(struct dfs_file *file, off_t offset, int whence) { struct dfs_seq_file *seq = file->data; off_t retval = -EINVAL; diff --git a/rt-thread/components/drivers/Kconfig b/rt-thread/components/drivers/Kconfig index 24856d5..3ffa4c3 100644 --- a/rt-thread/components/drivers/Kconfig +++ b/rt-thread/components/drivers/Kconfig @@ -21,8 +21,21 @@ rsource "touch/Kconfig" rsource "graphic/Kconfig" rsource "hwcrypto/Kconfig" rsource "wlan/Kconfig" +rsource "led/Kconfig" +rsource "mailbox/Kconfig" +rsource "phye/Kconfig" +rsource "ata/Kconfig" +rsource "nvme/Kconfig" +rsource "block/Kconfig" +rsource "scsi/Kconfig" +rsource "regulator/Kconfig" +rsource "reset/Kconfig" +rsource "thermal/Kconfig" rsource "virtio/Kconfig" +rsource "dma/Kconfig" +rsource "mfd/Kconfig" rsource "ofw/Kconfig" +rsource "pci/Kconfig" rsource "pic/Kconfig" rsource "pin/Kconfig" rsource "pinctrl/Kconfig" diff --git a/rt-thread/components/drivers/ata/Kconfig b/rt-thread/components/drivers/ata/Kconfig new file mode 100644 index 0000000..4b5cee5 --- /dev/null +++ b/rt-thread/components/drivers/ata/Kconfig @@ -0,0 +1,22 @@ +menuconfig RT_USING_ATA + bool "Using Advanced Technology Attachment (ATA) device drivers" + depends on RT_USING_DM + depends on RT_USING_BLK + depends on RT_USING_DMA + default n + +config RT_ATA_AHCI + bool "Advanced Host Controller Interface (AHCI)" + depends on RT_USING_ATA + depends on RT_USING_SCSI + default y + +config RT_ATA_AHCI_PCI + bool "AHCI support on PCI bus" + depends on RT_ATA_AHCI + depends on RT_USING_PCI + default n + +if RT_USING_ATA + osource "$(SOC_DM_ATA_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/ata/SConscript b/rt-thread/components/drivers/ata/SConscript new file mode 100644 index 0000000..f4a0922 --- /dev/null +++ b/rt-thread/components/drivers/ata/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_ATA']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = [] + +if GetDepend(['RT_ATA_AHCI']): + src += ['ahci.c'] + + if GetDepend(['RT_ATA_AHCI_PCI']): + src += ['ahci-pci.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/ata/ahci-pci.c b/rt-thread/components/drivers/ata/ahci-pci.c new file mode 100644 index 0000000..0bbec3e --- /dev/null +++ b/rt-thread/components/drivers/ata/ahci-pci.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#define AHCI_REG_BAR 5 + +struct pci_ahci_quirk +{ + int bar_idx; + rt_bool_t bar_offset; + + const struct rt_ahci_ops *ops; +}; + +struct pci_ahci_host +{ + struct rt_ahci_host parent; + const struct pci_ahci_quirk *quirk; + + rt_bool_t is_msi; +}; + +#define raw_to_pci_ahci_host(raw) rt_container_of(raw, struct pci_ahci_host, parent) + +static rt_err_t pci_ahci_init(struct rt_ahci_host *host) +{ + struct rt_pci_device *pdev; + + pdev = rt_container_of(host->parent.dev, struct rt_pci_device, parent); + + if (pdev->vendor == PCI_VENDOR_ID_JMICRON) + { + rt_pci_write_config_u8(pdev, 0x41, 0xa1); + } + + return RT_EOK; +} + +static const struct rt_ahci_ops pci_ahci_ops = +{ + .host_init = pci_ahci_init, +}; + +static rt_err_t pci_ahci_intel_init(struct rt_ahci_host *host) +{ + rt_uint16_t val; + struct rt_pci_device *pdev; + + pdev = rt_container_of(host->parent.dev, struct rt_pci_device, parent); + + rt_pci_read_config_u16(pdev, 0x92, &val); + rt_pci_write_config_u16(pdev, 0x92, val & ~0xf); + + rt_thread_mdelay(10); + rt_pci_write_config_u16(pdev, 0x92, val | 0xf); + + return RT_EOK; +} + +static const struct rt_ahci_ops pci_ahci_intel_ops = +{ + .host_init = pci_ahci_intel_init, +}; + +static rt_err_t pci_ahci_probe(struct rt_pci_device *pdev) +{ + rt_err_t err; + int bar_idx; + struct rt_ahci_host *ahci; + struct pci_ahci_host *pci_ahci = rt_calloc(1, sizeof(*pci_ahci)); + const struct pci_ahci_quirk *quirk = pdev->id->data; + + if (!pci_ahci) + { + return -RT_ENOMEM; + } + + pci_ahci->quirk = quirk; + ahci = &pci_ahci->parent; + ahci->parent.dev = &pdev->parent; + + bar_idx = quirk && quirk->bar_offset ? quirk->bar_idx : AHCI_REG_BAR; + + ahci->regs = rt_pci_iomap(pdev, bar_idx); + + if (!ahci->regs) + { + err = -RT_EIO; + goto _fail; + } + + ahci->ops = quirk && quirk->ops ? quirk->ops : &pci_ahci_ops; + + if (rt_pci_msi_enable(pdev) > 0) + { + pci_ahci->is_msi = RT_TRUE; + } + else + { + rt_pci_irq_unmask(pdev); + } + ahci->irq = pdev->irq; + + rt_pci_set_master(pdev); + + if ((err = rt_ahci_host_register(ahci))) + { + goto _disable; + } + + pdev->parent.user_data = pci_ahci; + + return RT_EOK; + +_disable: + if (pci_ahci->is_msi) + { + rt_pci_msix_disable(pdev); + } + else + { + rt_pci_irq_mask(pdev); + } + rt_pci_clear_master(pdev); + rt_iounmap(ahci->regs); + +_fail: + rt_free(pci_ahci); + + return err; +} + +static rt_err_t pci_ahci_remove(struct rt_pci_device *pdev) +{ + struct rt_ahci_host *ahci; + struct pci_ahci_host *pci_ahci = pdev->parent.user_data; + + ahci = &pci_ahci->parent; + + rt_ahci_host_unregister(ahci); + + if (pci_ahci->is_msi) + { + rt_pci_msi_disable(pdev); + } + else + { + /* INTx is shared, don't mask all */ + rt_hw_interrupt_umask(pdev->irq); + rt_pci_irq_mask(pdev); + } + + rt_pci_clear_master(pdev); + + rt_iounmap(ahci->regs); + rt_free(pci_ahci); + + return RT_EOK; +} + +static rt_err_t pci_ahci_shutdown(struct rt_pci_device *pdev) +{ + return pci_ahci_remove(pdev); +} + +static struct pci_ahci_quirk intel_quirk = +{ + .ops = &pci_ahci_intel_ops, +}; + +static struct pci_ahci_quirk cavium_sata_quirk = +{ + .bar_idx = 0, + .bar_offset = RT_TRUE, +}; + +static const struct rt_pci_device_id pci_ahci_ids[] = +{ + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_INTEL, 0x2922), .data = &intel_quirk }, + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_ASMEDIA, 0x0611) }, + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_MARVELL, 0x6121) }, + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_MARVELL, 0x6145) }, + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_CAVIUM, 0xa01c), .data = &cavium_sata_quirk }, + { RT_PCI_DEVICE_CLASS(PCIS_STORAGE_SATA_AHCI, ~0) }, + { /* sentinel */ } +}; + +static struct rt_pci_driver pci_ahci_driver = +{ + .name = "ahci-pci", + + .ids = pci_ahci_ids, + .probe = pci_ahci_probe, + .remove = pci_ahci_remove, + .shutdown = pci_ahci_shutdown, +}; +RT_PCI_DRIVER_EXPORT(pci_ahci_driver); diff --git a/rt-thread/components/drivers/ata/ahci.c b/rt-thread/components/drivers/ata/ahci.c new file mode 100644 index 0000000..c8a191b --- /dev/null +++ b/rt-thread/components/drivers/ata/ahci.c @@ -0,0 +1,896 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "rtdm.ahci" +#define DBG_LVL DBG_INFO +#include + +#define HWREG32_FLUSH(base, value) \ +do { \ + rt_uint32_t __value = value; \ + HWREG32(base) = __value; \ + __value = HWREG32(base); \ +} while (0) + +static void ahci_fill_cmd_slot(struct rt_ahci_port *port, rt_uint32_t opts) +{ + rt_ubase_t dma_addr = port->cmd_tbl_dma; + struct rt_ahci_cmd_hdr *cmd_slot = port->cmd_slot; + + cmd_slot->opts = rt_cpu_to_le32(opts); + cmd_slot->status = 0; + cmd_slot->tbl_addr_lo = rt_cpu_to_le32(rt_lower_32_bits(dma_addr)); + cmd_slot->tbl_addr_hi = rt_cpu_to_le32(rt_upper_32_bits(dma_addr)); +} + +static int ahci_fill_sg(struct rt_ahci_host *host, int id, + void *buffer, rt_size_t buffer_size) +{ + int sg_count; + rt_ubase_t dma_addr; + struct rt_ahci_port *port = &host->ports[id]; + struct rt_ahci_sg *ahci_sg = port->cmd_tbl_sg; + + sg_count = ((buffer_size - 1) / RT_ACHI_PRDT_BYTES_MAX) + 1; + + if (sg_count > RT_AHCI_MAX_SG) + { + return -1; + } + + dma_addr = (rt_ubase_t)rt_kmem_v2p(buffer); + + for (int i = 0; i < sg_count; ++i, ++ahci_sg) + { + ahci_sg->addr_lo = rt_cpu_to_le32(rt_lower_32_bits(dma_addr)); + ahci_sg->addr_hi = rt_cpu_to_le32(rt_upper_32_bits(dma_addr)); + + if (ahci_sg->addr_hi && !(host->cap & RT_AHCI_CAP_64)) + { + return -1; + } + + ahci_sg->flags_size = rt_cpu_to_le32(0x3fffff & + (rt_min_t(rt_uint32_t, buffer_size, RT_ACHI_PRDT_BYTES_MAX) - 1)); + + dma_addr += RT_ACHI_PRDT_BYTES_MAX; + buffer_size -= RT_ACHI_PRDT_BYTES_MAX; + } + + return sg_count; +} + +static rt_err_t ahci_request_io(struct rt_ahci_host *host, int id, + void *fis, rt_size_t fis_size, + void *buffer, rt_size_t buffer_size, rt_bool_t is_read) +{ + int sg_count; + rt_err_t err; + struct rt_ahci_port *port = &host->ports[id]; + + if ((HWREG32(port->regs + RT_AHCI_PORT_SSTS) & 0xf) != RT_AHCI_PORT_SSTS_DET_PHYRDY) + { + return -RT_EIO; + } + + if ((sg_count = ahci_fill_sg(host, id, buffer, buffer_size)) <= 0) + { + return -RT_EINVAL; + } + + rt_memcpy(port->cmd_tbl, fis, fis_size); + ahci_fill_cmd_slot(port, (fis_size >> 2) | (sg_count << 16) | (!is_read << 6)); + + if (!is_read) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, buffer, buffer_size); + } + + HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1); + + err = rt_completion_wait(&port->done, rt_tick_from_millisecond(10000)); + + if (!err && is_read) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, buffer, buffer_size); + } + + return err; +} + +static rt_err_t ahci_scsi_cmd_rw(struct rt_ahci_host *host, int id, + rt_off_t lba, void *buffer, rt_ssize_t size, rt_bool_t is_read) +{ + rt_err_t err; + rt_uint8_t fis[20]; + struct rt_ahci_port *port = &host->ports[id]; + + rt_memset(fis, 0, sizeof(fis)); + fis[0] = RT_AHCI_FIS_TYPE_REG_H2D; + fis[1] = 1 << 7; /* Command */ + fis[2] = is_read ? RT_AHCI_ATA_CMD_READ_EXT : RT_AHCI_ATA_CMD_WRITE_EXT; + + while (size > 0) + { + rt_size_t t_size, t_lba; + + t_lba = rt_min_t(rt_size_t, host->max_blocks, size); + t_size = port->block_size * t_lba; + + fis[3] = 0xe0; /* Features */ + fis[4] = (lba >> 0) & 0xff; /* LBA low register */ + fis[5] = (lba >> 8) & 0xff; /* LBA mid register */ + fis[6] = (lba >> 16) & 0xff; /* LBA high register */ + fis[7] = 1 << 6; /* Device */ + fis[8] = ((lba >> 24) & 0xff); /* LBA register, 31:24 */ + fis[9] = ((lba >> 32) & 0xff); /* LBA register, 39:32 */ + fis[10] = ((lba >> 40) & 0xff); /* LBA register, 47:40 */ + fis[12] = (t_lba >> 0) & 0xff; /* Count register, 7:0 */ + fis[13] = (t_lba >> 8) & 0xff; /* Count register, 15:8 */ + + if ((err = ahci_request_io(host, id, fis, sizeof(fis), buffer, t_size, is_read))) + { + return err; + } + + size -= t_lba; + lba += t_lba; + buffer += t_size; + } + + return RT_EOK; +} + +static rt_err_t ahci_scsi_synchronize_cache(struct rt_ahci_host *host, int id, + rt_off_t lba, rt_size_t size) +{ + rt_uint8_t fis[20]; + rt_uint16_t *ataid; + struct rt_ahci_port *port = &host->ports[id]; + + ataid = port->ataid; + + if (!rt_ahci_ata_id_wcache_enabled(ataid) && + !rt_ahci_ata_id_has_flush(ataid) && + !rt_ahci_ata_id_has_flush_ext(ataid)) + { + return -RT_ENOSYS; + } + + rt_memset(fis, 0, sizeof(fis)); + fis[0] = RT_AHCI_FIS_TYPE_REG_H2D; + fis[1] = 1 << 7; /* Command */ + + if (rt_ahci_ata_id_has_flush_ext(ataid)) + { + fis[2] = RT_AHCI_ATA_CMD_FLUSH_EXT; + } + else + { + fis[2] = RT_AHCI_ATA_CMD_FLUSH; + } + + rt_memcpy(port->cmd_tbl, fis, 20); + ahci_fill_cmd_slot(port, 5); + + HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1); + + return rt_completion_wait(&port->done, rt_tick_from_millisecond(5000)); +} + +static rt_err_t ahci_scsi_cmd_write_same(struct rt_ahci_host *host, int id, + rt_off_t lba, rt_size_t size) +{ + rt_uint8_t fis[20]; + struct rt_ahci_port *port = &host->ports[id]; + + rt_memset(fis, 0, sizeof(fis)); + fis[0] = RT_AHCI_FIS_TYPE_REG_H2D; + fis[1] = 1 << 7; /* Command */ + fis[2] = RT_AHCI_ATA_CMD_DSM; + fis[3] = RT_AHCI_ATA_DSM_TRIM; /* Features */ + fis[4] = (lba >> 0) & 0xff; /* LBA low register */ + fis[5] = (lba >> 8) & 0xff; /* LBA mid register */ + fis[6] = (lba >> 16) & 0xff; /* LBA high register */ + fis[7] = 1 << 6; /* Device */ + fis[8] = ((lba >> 24) & 0xff); /* LBA register, 31:24 */ + fis[9] = ((lba >> 32) & 0xff); /* LBA register, 39:32 */ + fis[10] = ((lba >> 40) & 0xff); /* LBA register, 47:40 */ + fis[12] = (size >> 0) & 0xff; /* Count register, 7:0 */ + fis[13] = (size >> 8) & 0xff; /* Count register, 15:8 */ + + HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1); + + return rt_completion_wait(&port->done, rt_tick_from_millisecond(5000)); +} + +static rt_err_t ahci_scsi_cmd_read_capacity(struct rt_ahci_host *host, int id, + rt_size_t *out_last_block, rt_size_t *out_block_size) +{ + struct rt_ahci_port *port = &host->ports[id]; + + if (!port->ataid) + { + return -RT_EIO; + } + + *out_last_block = rt_ahci_ata_id_n_sectors(port->ataid) - 1; + *out_block_size = port->block_size; + + return RT_EOK; +} + +static rt_err_t ahci_scsi_cmd_test_unit_ready(struct rt_ahci_host *host, int id) +{ + struct rt_ahci_port *port = &host->ports[id]; + + return port->ataid ? RT_EOK : -RT_EIO; +} + +static rt_err_t ahci_scsi_cmd_inquiry(struct rt_ahci_host *host, int id, + char *prodid, rt_size_t prodid_len, char *prodrev, rt_size_t prodrev_len) +{ + rt_err_t err; + rt_uint8_t fis[20]; + rt_uint16_t *ataid; + struct rt_ahci_port *port = &host->ports[id]; + + if (!port->link) + { + return -RT_EIO; + } + + if (!port->ataid && !(port->ataid = rt_malloc(RT_AHCI_ATA_ID_WORDS * 2))) + { + return -RT_ENOMEM; + } + ataid = port->ataid; + + rt_memset(fis, 0, sizeof(fis)); + fis[0] = RT_AHCI_FIS_TYPE_REG_H2D; + fis[1] = 1 << 7; /* Command */ + fis[2] = RT_AHCI_ATA_CMD_ID_ATA; + + if ((err = ahci_request_io(host, id, fis, sizeof(fis), + ataid, RT_AHCI_ATA_ID_WORDS * 2, RT_TRUE))) + { + return err; + } + + for (int i = 0; i < RT_AHCI_ATA_ID_WORDS; ++i) + { + ataid[i] = rt_le16_to_cpu(ataid[i]); + } + + for (int i = 0; i < prodid_len / 2; ++i) + { + rt_uint16_t src = ataid[RT_AHCI_ATA_ID_PROD + i]; + + prodid[i] = (src & 0x00ff) << 8 | (src & 0xff00) >> 8; + } + + for (int i = 0; i < prodrev_len / 2; ++i) + { + rt_uint16_t src = ataid[RT_AHCI_ATA_ID_FW_REV + i]; + + prodrev[i] = (src & 0x00ff) << 8 | (src & 0xff00) >> 8; + } + + return err; +} + +static rt_err_t ahci_scsi_transfer(struct rt_scsi_device *sdev, + struct rt_scsi_cmd *cmd) +{ + rt_err_t err; + struct rt_ahci_host *host; + + host = rt_container_of(sdev->host, struct rt_ahci_host, parent); + + switch (cmd->op.unknow.opcode) + { + case RT_SCSI_CMD_REQUEST_SENSE: + { + struct rt_scsi_request_sense_data *request_sense = &cmd->data.request_sense; + + request_sense->error_code = 0x72; + + err = RT_EOK; + } + break; + + case RT_SCSI_CMD_READ10: + { + struct rt_scsi_read10 *read10 = &cmd->op.read10; + + err = ahci_scsi_cmd_rw(host, sdev->id, + rt_be32_to_cpu(read10->lba), + cmd->data.ptr, + rt_be16_to_cpu(read10->size), + RT_TRUE); + } + break; + + case RT_SCSI_CMD_READ16: + { + struct rt_scsi_read16 *read16 = &cmd->op.read16; + + err = ahci_scsi_cmd_rw(host, sdev->id, + rt_be64_to_cpu(read16->lba), + cmd->data.ptr, + rt_be32_to_cpu(read16->size), + RT_TRUE); + } + break; + + case RT_SCSI_CMD_READ12: + { + struct rt_scsi_read12 *read12 = &cmd->op.read12; + + err = ahci_scsi_cmd_rw(host, sdev->id, + rt_be32_to_cpu(read12->lba), + cmd->data.ptr, + rt_be32_to_cpu(read12->size), + RT_TRUE); + } + break; + + case RT_SCSI_CMD_WRITE10: + { + struct rt_scsi_write10 *write10 = &cmd->op.write10; + + err = ahci_scsi_cmd_rw(host, sdev->id, + rt_be32_to_cpu(write10->lba), + cmd->data.ptr, + rt_be16_to_cpu(write10->size), + RT_FALSE); + } + break; + + case RT_SCSI_CMD_WRITE16: + { + struct rt_scsi_write16 *write16 = &cmd->op.write16; + + err = ahci_scsi_cmd_rw(host, sdev->id, + rt_be64_to_cpu(write16->lba), + cmd->data.ptr, + rt_be32_to_cpu(write16->size), + RT_FALSE); + } + break; + + case RT_SCSI_CMD_WRITE12: + { + struct rt_scsi_write12 *write12 = &cmd->op.write12; + + err = ahci_scsi_cmd_rw(host, sdev->id, + rt_be32_to_cpu(write12->lba), + cmd->data.ptr, + rt_be32_to_cpu(write12->size), + RT_FALSE); + } + break; + + case RT_SCSI_CMD_SYNCHRONIZE_CACHE10: + { + struct rt_scsi_synchronize_cache10 *synchronize_cache10 = &cmd->op.synchronize_cache10; + + err = ahci_scsi_synchronize_cache(host, sdev->id, + rt_be32_to_cpu(synchronize_cache10->lba), + rt_be16_to_cpu(synchronize_cache10->size)); + } + break; + + case RT_SCSI_CMD_SYNCHRONIZE_CACHE16: + { + struct rt_scsi_synchronize_cache16 *synchronize_cache16 = &cmd->op.synchronize_cache16; + + err = ahci_scsi_synchronize_cache(host, sdev->id, + rt_be64_to_cpu(synchronize_cache16->lba), + rt_be32_to_cpu(synchronize_cache16->size)); + } + break; + + case RT_SCSI_CMD_WRITE_SAME10: + { + struct rt_scsi_write_same10 *write_same10 = &cmd->op.write_same10; + + err = ahci_scsi_cmd_write_same(host, sdev->id, + rt_be32_to_cpu(write_same10->lba), rt_be16_to_cpu(write_same10->size)); + } + break; + + case RT_SCSI_CMD_WRITE_SAME16: + { + struct rt_scsi_write_same16 *write_same16 = &cmd->op.write_same16; + + err = ahci_scsi_cmd_write_same(host, sdev->id, + rt_be64_to_cpu(write_same16->lba), rt_be32_to_cpu(write_same16->size)); + } + break; + + case RT_SCSI_CMD_READ_CAPACITY10: + { + rt_size_t last_block, block_size; + struct rt_scsi_read_capacity10_data *data = &cmd->data.read_capacity10; + + err = ahci_scsi_cmd_read_capacity(host, sdev->id, &last_block, &block_size); + + if (!err) + { + if (last_block > 0x100000000ULL) + { + last_block = 0xffffffff; + } + + data->last_block = rt_cpu_to_be32(last_block); + data->block_size = rt_cpu_to_be32(block_size); + } + } + break; + + case RT_SCSI_CMD_READ_CAPACITY16: + { + rt_size_t last_block, block_size; + struct rt_scsi_read_capacity16_data *data = &cmd->data.read_capacity16; + + err = ahci_scsi_cmd_read_capacity(host, sdev->id, &last_block, &block_size); + + if (!err) + { + data->last_block = rt_cpu_to_be64(last_block); + data->block_size = rt_cpu_to_be32(block_size); + } + } + break; + + case RT_SCSI_CMD_TEST_UNIT_READY: + err = ahci_scsi_cmd_test_unit_ready(host, sdev->id); + break; + + case RT_SCSI_CMD_INQUIRY: + { + struct rt_ahci_port *port = &host->ports[sdev->id]; + struct rt_scsi_inquiry_data *inquiry = &cmd->data.inquiry; + + err = ahci_scsi_cmd_inquiry(host, sdev->id, + inquiry->prodid, sizeof(inquiry->prodid), + inquiry->prodrev, sizeof(inquiry->prodrev)); + + if (!err) + { + rt_memcpy(inquiry->vendor, "ATA ", sizeof(inquiry->vendor)); + + if (HWREG32(port->regs + RT_AHCI_PORT_SIG) != RT_AHCI_PORT_SIG_SATA_CDROM) + { + port->block_size = 512; + inquiry->devtype = SCSI_DEVICE_TYPE_DIRECT; + } + else + { + port->block_size = 2048; + inquiry->devtype = SCSI_DEVICE_TYPE_CDROM; + } + inquiry->rmb = 0; + inquiry->length = 95 - 4; + } + } + break; + + case RT_SCSI_CMD_MODE_SENSE: + case RT_SCSI_CMD_MODE_SENSE10: + case RT_SCSI_CMD_MODE_SELECT: + case RT_SCSI_CMD_MODE_SELECT10: + return -RT_ENOSYS; + + default: + return -RT_EINVAL; + } + + return err; +} + +static struct rt_scsi_ops ahci_scsi_ops = +{ + .transfer = ahci_scsi_transfer, +}; + +static void ahci_isr(int irqno, void *param) +{ + int id; + rt_uint32_t isr; + rt_bitmap_t int_map; + struct rt_ahci_port *port; + struct rt_ahci_host *host = param; + + int_map = HWREG32(host->regs + RT_AHCI_HBA_INTS); + + rt_bitmap_for_each_set_bit(&int_map, id, host->ports_nr) + { + port = &host->ports[id]; + + isr = HWREG32(port->regs + RT_AHCI_PORT_INTS); + + if (port->link) + { + if (host->ops->port_isr) + { + host->ops->port_isr(host, port, isr); + } + + rt_completion_done(&port->done); + } + + HWREG32(port->regs + RT_AHCI_PORT_INTS) = isr; + } + + HWREG32(host->regs + RT_AHCI_HBA_INTS) = int_map; +} + +rt_err_t rt_ahci_host_register(struct rt_ahci_host *host) +{ + rt_err_t err; + rt_uint32_t value; + char dev_name[RT_NAME_MAX]; + struct rt_scsi_host *scsi; + + if (!host || !host->parent.dev || !host->ops) + { + return -RT_EINVAL; + } + + host->max_blocks = host->max_blocks ? : 0x80; + + /* + * 1. Reset HBA. + */ + err = -RT_EIO; + value = HWREG32(host->regs + RT_AHCI_HBA_GHC); + + if (!(value & RT_AHCI_GHC_RESET)) + { + HWREG32_FLUSH(host->regs + RT_AHCI_HBA_GHC, value | RT_AHCI_GHC_RESET); + } + + for (int i = 0; i < 5; ++i) + { + rt_thread_mdelay(200); + + if (!(HWREG32(host->regs + RT_AHCI_HBA_GHC) & RT_AHCI_GHC_RESET)) + { + err = RT_EOK; + break; + } + } + + if (err) + { + goto _fail; + } + + /* + * 2. Enable AHCI and get the ports' information. + */ + HWREG32_FLUSH(host->regs + RT_AHCI_HBA_GHC, RT_AHCI_GHC_AHCI_EN); + + host->cap = HWREG32(host->regs + RT_AHCI_HBA_CAP); + host->cap &= RT_AHCI_CAP_SPM | RT_AHCI_CAP_SSS | RT_AHCI_CAP_SIS; + HWREG32(host->regs + RT_AHCI_HBA_CAP) = host->cap; + host->cap = HWREG32(host->regs + RT_AHCI_HBA_CAP); + + HWREG32_FLUSH(host->regs + RT_AHCI_HBA_PI, 0xf); + + if (host->ops->host_init && (err = host->ops->host_init(host))) + { + goto _fail; + } + + host->ports_nr = (host->cap & RT_AHCI_CAP_NP) + 1; + host->ports_map = HWREG32(host->regs + RT_AHCI_HBA_PI); + + /* Check implemented in firmware */ + rt_dm_dev_prop_read_u32(host->parent.dev, "ports-implemented", &host->ports_map); + + for (int i = 0; i < host->ports_nr; ++i) + { + struct rt_ahci_port *port; + + if (!(host->ports_map & RT_BIT(i))) + { + continue; + } + port = &host->ports[i]; + + /* + * 3. Alloc port io memory. + */ + port->regs = host->regs + 0x100 + (i * 0x80); + + /* + * 4. Make port stop. + */ + value = HWREG32(port->regs + RT_AHCI_PORT_CMD); + if (value & (RT_AHCI_PORT_CMD_LIST_ON | RT_AHCI_PORT_CMD_FIS_ON | + RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_START)) + { + value &= ~(RT_AHCI_PORT_CMD_LIST_ON | RT_AHCI_PORT_CMD_FIS_ON | + RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_START); + + HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CMD, value); + + rt_thread_mdelay(500); + } + + if (host->ops->port_init && (err = host->ops->port_init(host, port))) + { + LOG_E("Init port[%d] error = %s", rt_strerror(err)); + continue; + } + + value = HWREG32(port->regs + RT_AHCI_PORT_CMD); + value |= RT_AHCI_PORT_CMD_SPIN_UP; + HWREG32(port->regs + RT_AHCI_PORT_CMD) = value; + + /* + * 5. Enable port's SATA link. + */ + if (host->ops->port_link_up) + { + err = host->ops->port_link_up(host, port); + } + else + { + err = -RT_ETIMEOUT; + + for (int retry = 0; retry < 5; ++retry) + { + value = HWREG32(port->regs + RT_AHCI_PORT_SSTS); + + if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY) + { + err = RT_EOK; + break; + } + + rt_thread_mdelay(2); + } + } + + if (err) + { + if (HWREG32(port->regs + RT_AHCI_PORT_SSTS) & RT_AHCI_PORT_SSTS_DET_MASK) + { + LOG_E("SATA[%d] link error = %s", i, rt_strerror(err)); + } + else + { + LOG_D("SATA[%d] not device", i); + } + + continue; + } + + /* Clear error status */ + if ((value = HWREG32(port->regs + RT_AHCI_PORT_SERR))) + { + HWREG32(port->regs + RT_AHCI_PORT_SERR) = value; + } + + for (int retry = 0; retry < 5; ++retry) + { + value = HWREG32(port->regs + RT_AHCI_PORT_TFD); + if (!(value & (RT_AHCI_PORT_TFDATA_BSY | RT_AHCI_PORT_TFDATA_DRQ))) + { + break; + } + + rt_thread_mdelay(2); + + value = HWREG32(port->regs + RT_AHCI_PORT_SSTS); + if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY) + { + break; + } + } + + value = HWREG32(port->regs + RT_AHCI_PORT_SSTS) & RT_AHCI_PORT_SSTS_DET_MASK; + if (value == RT_AHCI_PORT_SSTS_DET_COMINIT) + { + /* Retry to setup */ + --i; + continue; + } + + /* Clear error */ + value = HWREG32(port->regs + RT_AHCI_PORT_SERR); + HWREG32(port->regs + RT_AHCI_PORT_SERR) = value; + + /* Clear pending IRQ */ + if ((value = HWREG32(port->regs + RT_AHCI_PORT_INTS))) + { + HWREG32(port->regs + RT_AHCI_PORT_INTS) = value; + } + + HWREG32(host->regs + RT_AHCI_HBA_INTS) = RT_BIT(i); + + value = HWREG32(port->regs + RT_AHCI_PORT_SSTS); + if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY) + { + port->link = RT_TRUE; + } + } + + HWREG32(host->regs + RT_AHCI_HBA_GHC) |= RT_AHCI_GHC_IRQ_EN; + + for (int i = 0; i < host->ports_nr; ++i) + { + void *dma; + rt_ubase_t dma_addr; + rt_tick_t timeout; + struct rt_ahci_port *port = &host->ports[i]; + + if (!port->link) + { + continue; + } + + /* + * 6. Alloc transport memory, Port x Command List and FIS Base Address. + */ + port->dma = rt_dma_alloc_coherent(host->parent.dev, + RT_AHCI_DMA_SIZE, &port->dma_handle); + + if (!port->dma) + { + LOG_E("No memory to setup port[%d]", i); + break; + } + dma = port->dma; + + rt_memset(dma, 0, RT_AHCI_DMA_SIZE); + + port->cmd_slot = dma; + dma += (RT_AHCI_CMD_SLOT_SIZE + 224); + + port->rx_fis = dma; + dma += RT_AHCI_RX_FIS_SIZE; + + port->cmd_tbl = dma; + port->cmd_tbl_dma = (rt_ubase_t)rt_kmem_v2p(dma); + dma += RT_AHCI_CMD_TBL_HDR; + + port->cmd_tbl_sg = dma; + + dma_addr = (rt_ubase_t)rt_kmem_v2p(port->cmd_slot); + HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CLB, rt_lower_32_bits(dma_addr)); + HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CLBU, rt_upper_32_bits(dma_addr)); + + dma_addr = (rt_ubase_t)rt_kmem_v2p(port->rx_fis); + HWREG32_FLUSH(port->regs + RT_AHCI_PORT_FB, rt_lower_32_bits(dma_addr)); + HWREG32_FLUSH(port->regs + RT_AHCI_PORT_FBU, rt_upper_32_bits(dma_addr)); + + if (host->ops->port_dma_init && (err = host->ops->port_dma_init(host, port))) + { + LOG_E("Init port[%d] DMA error = %s", rt_strerror(err)); + } + + HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CMD, RT_AHCI_PORT_CMD_ACTIVE | + RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_POWER_ON | + RT_AHCI_PORT_CMD_SPIN_UP | RT_AHCI_PORT_CMD_START); + + /* Wait spinup */ + err = -RT_ETIMEOUT; + timeout = rt_tick_from_millisecond(20000); + timeout += rt_tick_get(); + do { + if (!(HWREG32(port->regs + RT_AHCI_PORT_TFD) & RT_AHCI_PORT_TFDATA_BSY)) + { + err = RT_EOK; + break; + } + + rt_hw_cpu_relax(); + } while (rt_tick_get() < timeout); + + if (err) + { + rt_dma_free_coherent(host->parent.dev, RT_AHCI_DMA_SIZE, port->dma, + port->dma_handle); + port->dma = RT_NULL; + + LOG_E("Start up port[%d] fail", i); + continue; + } + + port->int_enabled |= RT_AHCI_PORT_INTE_HBUS_ERR | RT_AHCI_PORT_INTE_IF_ERR | + RT_AHCI_PORT_INTE_CONNECT | RT_AHCI_PORT_INTE_PHYRDY | + RT_AHCI_PORT_INTE_UNK_FIS | RT_AHCI_PORT_INTE_BAD_PMP | + RT_AHCI_PORT_INTE_TF_ERR | RT_AHCI_PORT_INTE_HBUS_DATA_ERR | + RT_AHCI_PORT_INTE_SG_DONE | RT_AHCI_PORT_INTE_SDB_FIS | + RT_AHCI_PORT_INTE_DMAS_FIS | RT_AHCI_PORT_INTE_PIOS_FIS | + RT_AHCI_PORT_INTE_D2H_REG_FIS; + + HWREG32(port->regs + RT_AHCI_PORT_INTE) = port->int_enabled; + + rt_completion_init(&port->done); + } + + rt_snprintf(dev_name, sizeof(dev_name), "ahci-%s", + rt_dm_dev_get_name(host->parent.dev)); + + rt_hw_interrupt_install(host->irq, ahci_isr, host, dev_name); + rt_hw_interrupt_umask(host->irq); + + scsi = &host->parent; + scsi->max_lun = rt_max_t(rt_size_t, scsi->max_lun, 1); + scsi->max_id = host->ports_nr; + scsi->ops = &ahci_scsi_ops; + + if ((err = rt_scsi_host_register(scsi))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_hw_interrupt_mask(host->irq); + rt_pic_detach_irq(host->irq, host); + + return err; +} + +rt_err_t rt_ahci_host_unregister(struct rt_ahci_host *host) +{ + rt_err_t err; + struct rt_scsi_host *scsi; + + if (!host) + { + return -RT_EINVAL; + } + + scsi = &host->parent; + + if ((err = rt_scsi_host_unregister(scsi))) + { + return err; + } + + rt_hw_interrupt_mask(host->irq); + rt_pic_detach_irq(host->irq, host); + + for (int i = 0; i < host->ports_nr; ++i) + { + struct rt_ahci_port *port = &host->ports[i]; + + if (port->ataid) + { + rt_free(port->ataid); + } + + HWREG32(port->regs) &= ~(RT_AHCI_PORT_CMD_ACTIVE | RT_AHCI_PORT_CMD_POWER_ON | + RT_AHCI_PORT_CMD_SPIN_UP | RT_AHCI_PORT_CMD_START); + + if (port->dma) + { + rt_dma_free_coherent(host->parent.dev, RT_AHCI_DMA_SIZE, port->dma, + port->dma_handle); + } + } + + HWREG32(host->regs + RT_AHCI_HBA_GHC) &= ~(RT_AHCI_GHC_AHCI_EN | RT_AHCI_GHC_IRQ_EN); + + return RT_EOK; +} diff --git a/rt-thread/components/drivers/audio/audio.c b/rt-thread/components/drivers/audio/dev_audio.c similarity index 100% rename from rt-thread/components/drivers/audio/audio.c rename to rt-thread/components/drivers/audio/dev_audio.c diff --git a/rt-thread/components/drivers/audio/audio_pipe.c b/rt-thread/components/drivers/audio/dev_audio_pipe.c similarity index 90% rename from rt-thread/components/drivers/audio/audio_pipe.c rename to rt-thread/components/drivers/audio/dev_audio_pipe.c index d4d24b8..24c0f26 100644 --- a/rt-thread/components/drivers/audio/audio_pipe.c +++ b/rt-thread/components/drivers/audio/dev_audio_pipe.c @@ -10,9 +10,9 @@ #include #include -#include "audio_pipe.h" +#include "dev_audio_pipe.h" -static void _rt_pipe_resume_writer(struct rt_audio_pipe *pipe) +static void _rt_audio_pipe_resume_writer(struct rt_audio_pipe *pipe) { if (!rt_list_isempty(&pipe->suspended_write_list)) { @@ -30,7 +30,7 @@ static void _rt_pipe_resume_writer(struct rt_audio_pipe *pipe) } } -static rt_ssize_t rt_pipe_read(rt_device_t dev, +static rt_ssize_t rt_audio_pipe_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) @@ -50,7 +50,7 @@ static rt_ssize_t rt_pipe_read(rt_device_t dev, /* if the ringbuffer is empty, there won't be any writer waiting */ if (read_nbytes) - _rt_pipe_resume_writer(pipe); + _rt_audio_pipe_resume_writer(pipe); rt_hw_interrupt_enable(level); @@ -78,7 +78,7 @@ static rt_ssize_t rt_pipe_read(rt_device_t dev, } else { - _rt_pipe_resume_writer(pipe); + _rt_audio_pipe_resume_writer(pipe); rt_hw_interrupt_enable(level); break; } @@ -88,7 +88,7 @@ static rt_ssize_t rt_pipe_read(rt_device_t dev, return read_nbytes; } -static void _rt_pipe_resume_reader(struct rt_audio_pipe *pipe) +static void _rt_audio_pipe_resume_reader(struct rt_audio_pipe *pipe) { if (pipe->parent.rx_indicate) pipe->parent.rx_indicate(&pipe->parent, @@ -110,7 +110,7 @@ static void _rt_pipe_resume_reader(struct rt_audio_pipe *pipe) } } -static rt_ssize_t rt_pipe_write(rt_device_t dev, +static rt_ssize_t rt_audio_pipe_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) @@ -135,7 +135,7 @@ static rt_ssize_t rt_pipe_write(rt_device_t dev, write_nbytes = rt_ringbuffer_put(&(pipe->ringbuffer), (const rt_uint8_t *)buffer, size); - _rt_pipe_resume_reader(pipe); + _rt_audio_pipe_resume_reader(pipe); rt_hw_interrupt_enable(level); @@ -164,7 +164,7 @@ static rt_ssize_t rt_pipe_write(rt_device_t dev, } else { - _rt_pipe_resume_reader(pipe); + _rt_audio_pipe_resume_reader(pipe); rt_hw_interrupt_enable(level); break; } @@ -174,7 +174,7 @@ static rt_ssize_t rt_pipe_write(rt_device_t dev, return write_nbytes; } -static rt_err_t rt_pipe_control(rt_device_t dev, int cmd, void *args) +static rt_err_t rt_audio_pipe_control(rt_device_t dev, int cmd, void *args) { struct rt_audio_pipe *pipe; @@ -191,9 +191,9 @@ const static struct rt_device_ops audio_pipe_ops = RT_NULL, RT_NULL, RT_NULL, - rt_pipe_read, - rt_pipe_write, - rt_pipe_control + rt_audio_pipe_read, + rt_audio_pipe_write, + rt_audio_pipe_control }; #endif @@ -235,9 +235,9 @@ rt_err_t rt_audio_pipe_init(struct rt_audio_pipe *pipe, pipe->parent.init = RT_NULL; pipe->parent.open = RT_NULL; pipe->parent.close = RT_NULL; - pipe->parent.read = rt_pipe_read; - pipe->parent.write = rt_pipe_write; - pipe->parent.control = rt_pipe_control; + pipe->parent.read = rt_audio_pipe_read; + pipe->parent.write = rt_audio_pipe_write; + pipe->parent.control = rt_audio_pipe_control; #endif return rt_device_register(&(pipe->parent), name, RT_DEVICE_FLAG_RDWR); diff --git a/rt-thread/components/drivers/audio/audio_pipe.h b/rt-thread/components/drivers/audio/dev_audio_pipe.h similarity index 95% rename from rt-thread/components/drivers/audio/audio_pipe.h rename to rt-thread/components/drivers/audio/dev_audio_pipe.h index 88cbeca..f22fd14 100644 --- a/rt-thread/components/drivers/audio/audio_pipe.h +++ b/rt-thread/components/drivers/audio/dev_audio_pipe.h @@ -6,8 +6,8 @@ * Change Logs: * Date Author Notes */ -#ifndef __AUDIO_PIPE_H__ -#define __AUDIO_PIPE_H__ +#ifndef __DEV_AUDIO_PIPE_H__ +#define __DEV_AUDIO_PIPE_H__ /** * Pipe Device @@ -72,4 +72,4 @@ rt_err_t rt_audio_pipe_create(const char *name, rt_int32_t flag, rt_size_t size) void rt_audio_pipe_destroy(struct rt_audio_pipe *pipe); #endif /* RT_USING_HEAP */ -#endif /* __AUDIO_PIPE_H__ */ +#endif /* __DEV_AUDIO_PIPE_H__ */ diff --git a/rt-thread/components/drivers/block/Kconfig b/rt-thread/components/drivers/block/Kconfig new file mode 100644 index 0000000..865df5e --- /dev/null +++ b/rt-thread/components/drivers/block/Kconfig @@ -0,0 +1,7 @@ +menuconfig RT_USING_BLK + bool "Using Block device drivers" + default n + +if RT_USING_BLK + rsource "partitions/Kconfig" +endif diff --git a/rt-thread/components/drivers/block/SConscript b/rt-thread/components/drivers/block/SConscript new file mode 100644 index 0000000..4aff1a8 --- /dev/null +++ b/rt-thread/components/drivers/block/SConscript @@ -0,0 +1,23 @@ +from building import * + +group = [] +objs = [] + +if not GetDepend(['RT_USING_BLK']): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = ['blk.c', 'blk_dev.c', 'blk_dfs.c', 'blk_partition.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/rt-thread/components/drivers/block/blk.c b/rt-thread/components/drivers/block/blk.c new file mode 100644 index 0000000..9f2659d --- /dev/null +++ b/rt-thread/components/drivers/block/blk.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#define DBG_TAG "rtdm.blk" +#define DBG_LVL DBG_INFO +#include + +#include "blk_dev.h" +#include "blk_dfs.h" + +static void blk_remove_all(struct rt_blk_disk *disk) +{ + struct rt_blk_device *blk, *blk_next; + + /* Remove all partitions */ + rt_list_for_each_entry_safe(blk, blk_next, &disk->part_nodes, list) + { + disk_remove_blk_dev(blk, RT_TRUE); + } +} + +static rt_err_t blk_open(rt_device_t dev, rt_uint16_t oflag) +{ + struct rt_blk_disk *disk = to_blk_disk(dev); + + if (disk->read_only && (oflag & RT_DEVICE_OFLAG_WRONLY)) + { + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t blk_close(rt_device_t dev) +{ + return RT_EOK; +} + +static rt_ssize_t blk_read(rt_device_t dev, rt_off_t sector, + void *buffer, rt_size_t sector_count) +{ + rt_ssize_t res; + struct rt_blk_disk *disk = to_blk_disk(dev); + + rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER); + + res = disk->ops->read(disk, sector, buffer, sector_count); + + rt_sem_release(&disk->usr_lock); + + return res; +} + +static rt_ssize_t blk_write(rt_device_t dev, rt_off_t sector, + const void *buffer, rt_size_t sector_count) +{ + rt_ssize_t res; + struct rt_blk_disk *disk = to_blk_disk(dev); + + if (!disk->read_only) + { + rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER); + + res = disk->ops->write(disk, sector, buffer, sector_count); + + rt_sem_release(&disk->usr_lock); + + return res; + } + + return -RT_ENOSYS; +} + +static rt_ssize_t blk_parallel_read(rt_device_t dev, rt_off_t sector, + void *buffer, rt_size_t sector_count) +{ + struct rt_blk_disk *disk = to_blk_disk(dev); + + return disk->ops->read(disk, sector, buffer, sector_count); +} + +static rt_ssize_t blk_parallel_write(rt_device_t dev, rt_off_t sector, + const void *buffer, rt_size_t sector_count) +{ + struct rt_blk_disk *disk = to_blk_disk(dev); + + if (!disk->read_only) + { + return disk->ops->write(disk, sector, buffer, sector_count); + } + + return -RT_ENOSYS; +} + +static rt_err_t blk_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err; + struct rt_blk_disk *disk = to_blk_disk(dev); + + switch (cmd) + { + case RT_DEVICE_CTRL_BLK_GETGEOME: + if (args) + { + err = disk->ops->getgeome(disk, args); + } + else + { + err = -RT_EINVAL; + } + + break; + + case RT_DEVICE_CTRL_BLK_SYNC: + if (disk->ops->sync) + { + rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER); + + spin_lock(&disk->lock); + + err = disk->ops->sync(disk); + + spin_unlock(&disk->lock); + + rt_sem_release(&disk->usr_lock); + } + else + { + err = -RT_ENOSYS; + } + break; + + case RT_DEVICE_CTRL_BLK_ERASE: + if (disk->ops->erase) + { + rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER); + + spin_lock(&disk->lock); + + if (disk->parent.ref_count != 1) + { + err = -RT_EBUSY; + goto _unlock; + } + + blk_remove_all(disk); + + err = disk->ops->erase(disk); + + _unlock: + spin_unlock(&disk->lock); + + rt_sem_release(&disk->usr_lock); + } + else + { + err = -RT_ENOSYS; + } + break; + + case RT_DEVICE_CTRL_BLK_AUTOREFRESH: + if (disk->ops->autorefresh) + { + err = disk->ops->autorefresh(disk, !!args); + } + else + { + err = -RT_ENOSYS; + } + break; + + case RT_DEVICE_CTRL_BLK_PARTITION: + err = -RT_EINVAL; + break; + + case RT_DEVICE_CTRL_BLK_SSIZEGET: + device_get_blk_ssize(dev, args); + err = RT_EOK; + break; + + case RT_DEVICE_CTRL_ALL_BLK_SSIZEGET: + device_get_all_blk_ssize(dev, args); + err = RT_EOK; + break; + + default: + if (disk->ops->control) + { + err = disk->ops->control(disk, RT_NULL, cmd, args); + } + else + { + err = -RT_ENOSYS; + } + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops blk_ops = +{ + .open = blk_open, + .close = blk_close, + .read = blk_read, + .write = blk_write, + .control = blk_control, +}; + +const static struct rt_device_ops blk_parallel_ops = +{ + .open = blk_open, + .close = blk_close, + .read = blk_parallel_read, + .write = blk_parallel_write, + .control = blk_control, +}; +#endif /* RT_USING_DEVICE_OPS */ + +rt_err_t rt_hw_blk_disk_register(struct rt_blk_disk *disk) +{ + rt_err_t err; +#ifdef RT_USING_DM + int device_id; +#endif + const char *disk_name; + rt_uint16_t flags = RT_DEVICE_FLAG_RDONLY; + + if (!disk || !disk->ops) + { + return -RT_EINVAL; + } + +#ifdef RT_USING_DM + if (!disk->ida) + { + return -RT_EINVAL; + } +#endif + +#if RT_NAME_MAX > 0 + if (disk->parent.parent.name[0] == '\0') +#else + if (disk->parent.parent.name) +#endif + { + return -RT_EINVAL; + } + +#ifdef RT_USING_DM + if ((device_id = rt_dm_ida_alloc(disk->ida)) < 0) + { + return -RT_EFULL; + } +#endif + + disk->__magic = RT_BLK_DISK_MAGIC; + disk_name = to_disk_name(disk); + + err = rt_sem_init(&disk->usr_lock, disk_name, 1, RT_IPC_FLAG_PRIO); + + if (err) + { + #ifdef RT_USING_DM + rt_dm_ida_free(disk->ida, device_id); + #endif + + LOG_E("%s: Init user mutex error = %s", rt_strerror(err)); + + return err; + } + + rt_list_init(&disk->part_nodes); + rt_spin_lock_init(&disk->lock); + + disk->parent.type = RT_Device_Class_Block; +#ifdef RT_USING_DEVICE_OPS + if (disk->parallel_io) + { + disk->parent.ops = &blk_parallel_ops; + } + else + { + disk->parent.ops = &blk_ops; + } +#else + disk->parent.open = blk_open; + disk->parent.close = blk_close; + + if (disk->parallel_io) + { + disk->parent.read = blk_parallel_read; + disk->parent.write = blk_parallel_write; + } + else + { + disk->parent.read = blk_read; + disk->parent.write = blk_write; + } + disk->parent.control = blk_control; +#endif + + if (!disk->ops->write) + { + disk->read_only = RT_TRUE; + } + + if (!disk->read_only) + { + flags |= RT_DEVICE_FLAG_WRONLY; + } + +#ifdef RT_USING_DM + disk->parent.master_id = disk->ida->master_id; + disk->parent.device_id = device_id; +#endif + device_set_blk_fops(&disk->parent); + + err = rt_device_register(&disk->parent, disk_name, flags); + + if (err) + { + rt_sem_detach(&disk->usr_lock); + } + + /* Ignore partition scanning errors */ + rt_blk_disk_probe_partition(disk); + + return err; +} + +rt_err_t rt_hw_blk_disk_unregister(struct rt_blk_disk *disk) +{ + rt_err_t err; + + if (!disk) + { + return -RT_EINVAL; + } + + spin_lock(&disk->lock); + + if (disk->parent.ref_count > 0) + { + err = -RT_EBUSY; + goto _unlock; + } + + /* Flush all data */ + if (disk->ops->sync) + { + err = disk->ops->sync(disk); + + if (err) + { + LOG_E("%s: Sync error = %s", to_disk_name(disk), rt_strerror(err)); + + goto _unlock; + } + } + + rt_sem_detach(&disk->usr_lock); + + blk_remove_all(disk); + +#ifdef RT_USING_DM + rt_dm_ida_free(disk->ida, disk->parent.device_id); +#endif + + err = rt_device_unregister(&disk->parent); + +_unlock: + spin_unlock(&disk->lock); + + return err; +} + +rt_ssize_t rt_blk_disk_get_capacity(struct rt_blk_disk *disk) +{ + rt_ssize_t res; + struct rt_device_blk_geometry geometry; + + if (!disk) + { + return -RT_EINVAL; + } + + res = disk->ops->getgeome(disk, &geometry); + + if (!res) + { + return geometry.sector_count; + } + + return res; +} + +rt_ssize_t rt_blk_disk_get_logical_block_size(struct rt_blk_disk *disk) +{ + rt_ssize_t res; + struct rt_device_blk_geometry geometry; + + if (!disk) + { + return -RT_EINVAL; + } + + res = disk->ops->getgeome(disk, &geometry); + + if (!res) + { + return geometry.bytes_per_sector; + } + + return res; +} + +#ifdef RT_USING_DFS_MNTTABLE +static int blk_dfs_mnt_table(void) +{ + rt_ubase_t level; + struct rt_object *obj; + struct rt_device *dev; + struct rt_blk_disk *disk; + struct rt_blk_device *blk_dev; + struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Device); + + level = rt_hw_interrupt_disable(); + + rt_list_for_each_entry(obj, &info->object_list, list) + { + dev = rt_container_of(obj, struct rt_device, parent); + + if (dev->type != RT_Device_Class_Block) + { + continue; + } + + disk = to_blk_disk(dev); + + if (disk->__magic != RT_BLK_DISK_MAGIC) + { + continue; + } + + if (disk->max_partitions == RT_BLK_PARTITION_NONE) + { + dfs_mount_device(&disk->parent); + continue; + } + + rt_list_for_each_entry(blk_dev, &disk->part_nodes, list) + { + dfs_mount_device(&blk_dev->parent); + } + } + + rt_hw_interrupt_enable(level); + + return 0; +} +INIT_ENV_EXPORT(blk_dfs_mnt_table); +#endif /* RT_USING_DFS_MNTTABLE */ + +#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH) +const char *convert_size(struct rt_device_blk_geometry *geome, + rt_size_t sector_count, rt_size_t *out_cap, rt_size_t *out_minor) +{ + rt_size_t cap, minor = 0; + int size_index = 0; + const char *size_name[] = { "B", "K", "M", "G", "T", "P", "E" }; + + cap = geome->bytes_per_sector * sector_count; + + for (size_index = 0; size_index < RT_ARRAY_SIZE(size_name) - 1; ++size_index) + { + if (cap < 1024) + { + break; + } + + /* Only one decimal point */ + minor = (cap % 1024) * 10 / 1024; + cap = cap / 1024; + } + + *out_cap = cap; + *out_minor = minor; + + return size_name[size_index]; +} + +static int list_blk(int argc, char**argv) +{ + rt_ubase_t level; + rt_size_t cap, minor; + const char *size_name; + struct rt_object *obj; + struct rt_device *dev; + struct rt_blk_disk *disk; + struct rt_blk_device *blk_dev; + struct rt_device_blk_geometry geome; + struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Device); + + level = rt_hw_interrupt_disable(); + + rt_kprintf("%-*.s MAJ:MIN RM SIZE\tRO TYPE MOUNTPOINT\n", RT_NAME_MAX, "NAME"); + + rt_list_for_each_entry(obj, &info->object_list, list) + { + dev = rt_container_of(obj, struct rt_device, parent); + + if (dev->type != RT_Device_Class_Block) + { + continue; + } + + disk = to_blk_disk(dev); + + if (disk->__magic != RT_BLK_DISK_MAGIC) + { + continue; + } + + if (disk->ops->getgeome(disk, &geome)) + { + continue; + } + + size_name = convert_size(&geome, geome.sector_count, &cap, &minor); + + rt_kprintf("%-*.s %3u.%-3u %u %u.%u%s\t%u disk %s\n", + RT_NAME_MAX, to_disk_name(disk), + #ifdef RT_USING_DM + disk->parent.master_id, disk->parent.device_id, + #else + 0, 0, + #endif + disk->removable, cap, minor, size_name, disk->read_only, + disk->max_partitions != RT_BLK_PARTITION_NONE ? "\b" : + (dfs_filesystem_get_mounted_path(&disk->parent) ? : "\b")); + + rt_list_for_each_entry(blk_dev, &disk->part_nodes, list) + { + size_name = convert_size(&geome, blk_dev->sector_count, &cap, &minor); + + rt_kprintf("%c--%-*.s %3u.%-3u %u %u.%u%s\t%u part %s\n", + blk_dev->list.next != &disk->part_nodes ? '|' : '`', + RT_NAME_MAX - 3, to_blk_name(blk_dev), + #ifdef RT_USING_DM + blk_dev->parent.master_id, blk_dev->parent.device_id, + #else + 0, 0, + #endif + disk->removable, cap, minor, size_name, disk->read_only, + dfs_filesystem_get_mounted_path(&blk_dev->parent) ? : ""); + } + } + + rt_hw_interrupt_enable(level); + + return 0; +} +MSH_CMD_EXPORT(list_blk, dump all of blks information); +#endif /* RT_USING_CONSOLE && RT_USING_MSH */ diff --git a/rt-thread/components/drivers/block/blk_dev.c b/rt-thread/components/drivers/block/blk_dev.c new file mode 100644 index 0000000..5c45aa5 --- /dev/null +++ b/rt-thread/components/drivers/block/blk_dev.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI first version + */ + +#include "blk_dev.h" +#include "blk_dfs.h" + +#define DBG_TAG "blk.dm" +#define DBG_LVL DBG_INFO +#include + +#ifdef RT_USING_DFS +#include +#endif + +static rt_err_t blk_dev_open(rt_device_t dev, rt_uint16_t oflag) +{ + struct rt_blk_device *blk = to_blk(dev); + + return rt_device_open(&blk->disk->parent, oflag); +} + +static rt_err_t blk_dev_close(rt_device_t dev) +{ + struct rt_blk_device *blk = to_blk(dev); + + return rt_device_close(&blk->disk->parent); +} + +static rt_ssize_t blk_dev_read(rt_device_t dev, rt_off_t sector, + void *buffer, rt_size_t sector_count) +{ + struct rt_blk_device *blk = to_blk(dev); + + if (sector <= blk->sector_start + blk->sector_count && + sector_count <= blk->sector_count) + { + return rt_device_read(&blk->disk->parent, + blk->sector_start + sector, buffer, sector_count); + } + + return -RT_EINVAL; +} + +static rt_ssize_t blk_dev_write(rt_device_t dev, rt_off_t sector, + const void *buffer, rt_size_t sector_count) +{ + struct rt_blk_device *blk = to_blk(dev); + + if (sector <= blk->sector_start + blk->sector_count && + sector_count <= blk->sector_count) + { + return rt_device_write(&blk->disk->parent, + blk->sector_start + sector, buffer, sector_count); + } + + return -RT_EINVAL; +} + +static rt_err_t blk_dev_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t err = -RT_EINVAL; + struct rt_blk_device *blk = to_blk(dev); + struct rt_blk_disk *disk = blk->disk; + struct rt_device_blk_geometry disk_geometry, *geometry; + + switch (cmd) + { + case RT_DEVICE_CTRL_BLK_GETGEOME: + if ((geometry = args)) + { + if (!(err = disk->ops->getgeome(disk, &disk_geometry))) + { + geometry->bytes_per_sector = disk_geometry.bytes_per_sector; + geometry->block_size = disk_geometry.block_size; + geometry->sector_count = blk->sector_count; + } + } + else + { + err = -RT_EINVAL; + } + + break; + + case RT_DEVICE_CTRL_BLK_SYNC: + rt_device_control(&disk->parent, cmd, args); + break; + + case RT_DEVICE_CTRL_BLK_ERASE: + case RT_DEVICE_CTRL_BLK_AUTOREFRESH: + if (disk->partitions <= 1) + { + rt_device_control(&disk->parent, cmd, args); + } + else + { + err = -RT_EIO; + } + break; + + case RT_DEVICE_CTRL_BLK_PARTITION: + if (args) + { + rt_memcpy(args, &blk->partition, sizeof(blk->partition)); + } + else + { + err = -RT_EINVAL; + } + + break; + + case RT_DEVICE_CTRL_BLK_SSIZEGET: + device_get_blk_ssize(dev, args); + err = RT_EOK; + break; + + case RT_DEVICE_CTRL_ALL_BLK_SSIZEGET: + device_get_all_blk_ssize(dev, args); + err = RT_EOK; + break; + + default: + if (disk->ops->control) + { + err = disk->ops->control(disk, blk, cmd, args); + } + break; + } + + return err; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops blk_dev_ops = +{ + .open = blk_dev_open, + .close = blk_dev_close, + .read = blk_dev_read, + .write = blk_dev_write, + .control = blk_dev_control, +}; +#endif + +rt_err_t blk_dev_initialize(struct rt_blk_device *blk) +{ + struct rt_device *dev; + + if (!blk) + { + return -RT_EINVAL; + } + + dev = &blk->parent; + dev->type = RT_Device_Class_Block; +#ifdef RT_USING_DEVICE_OPS + dev->ops = &blk_dev_ops; +#else + dev->open = blk_dev_open; + dev->close = blk_dev_close; + dev->read = blk_dev_read; + dev->write = blk_dev_write; + dev->control = blk_dev_control; +#endif + + return RT_EOK; +} + +rt_err_t disk_add_blk_dev(struct rt_blk_disk *disk, struct rt_blk_device *blk) +{ + rt_err_t err; +#ifdef RT_USING_DM + int device_id; +#endif + const char *disk_name, *name_fmt; + + if (!disk || !blk) + { + return -RT_EINVAL; + } + +#ifdef RT_USING_DM + if ((device_id = rt_dm_ida_alloc(disk->ida)) < 0) + { + return -RT_EFULL; + } +#endif + + blk->disk = disk; + rt_list_init(&blk->list); + + disk_name = to_disk_name(disk); + + /* End is [a-zA-Z] or [0-9] */ + if (disk_name[rt_strlen(disk_name) - 1] < 'a') + { + name_fmt = "%sp%d"; + } + else + { + name_fmt = "%s%d"; + } + +#ifdef RT_USING_DM + rt_dm_dev_set_name(&blk->parent, name_fmt, disk_name, blk->partno); + blk->parent.master_id = disk->ida->master_id; + blk->parent.device_id = device_id; +#else + rt_snprintf(blk->parent.parent.name, RT_NAME_MAX, name_fmt, disk_name, blk->partno); +#endif + device_set_blk_fops(&blk->parent); + + err = rt_device_register(&blk->parent, to_blk_name(blk), + disk->parent.flag & RT_DEVICE_FLAG_RDWR); + + if (err) + { + #ifdef RT_USING_DM + rt_dm_ida_free(disk->ida, device_id); + #endif + return err; + } + + spin_lock(&disk->lock); + + rt_list_insert_before(&disk->part_nodes, &blk->list); + + spin_unlock(&disk->lock); + + return RT_EOK; +} + +rt_err_t disk_remove_blk_dev(struct rt_blk_device *blk, rt_bool_t lockless) +{ + struct rt_blk_disk *disk; + + if (!blk) + { + return -RT_EINVAL; + } + + disk = blk->disk; + + if (!disk) + { + return -RT_EINVAL; + } + else + { + #ifdef RT_USING_DFS + const char *mountpath; + + if ((mountpath = dfs_filesystem_get_mounted_path(&blk->parent))) + { + dfs_unmount(mountpath); + LOG_D("%s: Unmount file system on %s", + to_blk_name(blk), mountpath); + } + #endif + } + +#ifdef RT_USING_DM + rt_dm_ida_free(disk->ida, blk->parent.device_id); +#endif + + rt_device_unregister(&blk->parent); + + if (!lockless) + { + spin_lock(&disk->lock); + } + + rt_list_remove(&blk->list); + + if (!lockless) + { + spin_unlock(&disk->lock); + } + + --disk->partitions; + + return RT_EOK; +} + +rt_uint32_t blk_request_ioprio(void) +{ + struct rt_thread *task = rt_thread_self(); + + return task ? RT_SCHED_PRIV(task).current_priority : 0; +} diff --git a/rt-thread/components/drivers/block/blk_dev.h b/rt-thread/components/drivers/block/blk_dev.h new file mode 100644 index 0000000..b433f57 --- /dev/null +++ b/rt-thread/components/drivers/block/blk_dev.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI first version + */ + +#ifndef __BLK_DEV_H__ +#define __BLK_DEV_H__ + +#include +#include +#include +#include + +#define to_blk_disk(dev) rt_container_of(dev, struct rt_blk_disk, parent) +#define to_blk(dev) rt_container_of(dev, struct rt_blk_device, parent) + +#ifdef RT_USING_DM +#define to_disk_name(disk) rt_dm_dev_get_name(&(disk)->parent) +#define to_blk_name(blk) rt_dm_dev_get_name(&(blk)->parent) +#else +#define to_disk_name(disk) (disk)->parent.parent.name +#define to_blk_name(blk) (blk)->parent.parent.name +#endif + +/* %c%c name */ +#define letter_name(n) ('a' + (n) / ((n) >= 26 ? (26 * 2) : 1)), ((n) >= 26 ? 'a' + (n) % 26 : '\0') + +rt_inline void spin_lock(struct rt_spinlock *spinlock) +{ + rt_hw_spin_lock(&spinlock->lock); +} + +rt_inline void spin_unlock(struct rt_spinlock *spinlock) +{ + rt_hw_spin_unlock(&spinlock->lock); +} + +rt_err_t blk_dev_initialize(struct rt_blk_device *blk); +rt_err_t disk_add_blk_dev(struct rt_blk_disk *disk, struct rt_blk_device *blk); +rt_err_t disk_remove_blk_dev(struct rt_blk_device *blk, rt_bool_t lockless); + +rt_uint32_t blk_request_ioprio(void); + +#endif /* __BLK_DEV_H__ */ diff --git a/rt-thread/components/drivers/block/blk_dfs.c b/rt-thread/components/drivers/block/blk_dfs.c new file mode 100644 index 0000000..96800e1 --- /dev/null +++ b/rt-thread/components/drivers/block/blk_dfs.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-08-08 GuEe-GUI first version + */ + +#include "blk_dfs.h" + +#include +#include + +#if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_DFS_V2) +struct blk_fops_data +{ + struct rt_device_blk_geometry geometry; +}; + +static int blk_fops_open(struct dfs_file *file) +{ + struct rt_device *dev = file->vnode->data; + struct blk_fops_data *data = rt_malloc(sizeof(*data)); + + if (!data) + { + return (int)-RT_ENOMEM; + } + + dev->user_data = data; + rt_device_control(dev, RT_DEVICE_CTRL_BLK_GETGEOME, &data->geometry); + rt_device_control(dev, RT_DEVICE_CTRL_ALL_BLK_SSIZEGET, &file->vnode->size); + + return 0; +} + +static int blk_fops_close(struct dfs_file *file) +{ + struct rt_device *dev = file->vnode->data; + + rt_free(dev->user_data); + dev->user_data = RT_NULL; + + return 0; +} + +static int blk_fops_ioctl(struct dfs_file *file, int cmd, void *arg) +{ + struct rt_device *dev = file->vnode->data; + + return (int)rt_device_control(dev, cmd, arg); +} + +static ssize_t blk_fops_read(struct dfs_file *file, void *buf, size_t count, off_t *pos) +{ + void *rbuf; + rt_ssize_t res = 0; + int bytes_per_sector, blk_pos, first_offs, rsize = 0; + struct rt_device *dev = file->vnode->data; + struct blk_fops_data *data = dev->user_data; + + bytes_per_sector = data->geometry.bytes_per_sector; + blk_pos = *pos / bytes_per_sector; + first_offs = *pos % bytes_per_sector; + + if ((rbuf = rt_malloc(bytes_per_sector))) + { + /* + ** #1: read first unalign block size. + */ + res = rt_device_read(dev, blk_pos, rbuf, 1); + + if (res == 1) + { + if (count > bytes_per_sector - first_offs) + { + rsize = bytes_per_sector - first_offs; + } + else + { + rsize = count; + } + rt_memcpy(buf, rbuf + first_offs, rsize); + ++blk_pos; + + /* + ** #2: read continuous block size. + */ + while (rsize < count) + { + res = rt_device_read(dev, blk_pos++, rbuf, 1); + + if (res != 1) + { + break; + } + + if (count - rsize >= bytes_per_sector) + { + rt_memcpy(buf + rsize, rbuf, bytes_per_sector); + rsize += bytes_per_sector; + } + else + { + rt_memcpy(buf + rsize, rbuf, count - rsize); + rsize = count; + } + } + + *pos += rsize; + } + + rt_free(rbuf); + } + + return rsize; +} + +static ssize_t blk_fops_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos) +{ + void *rbuf; + rt_ssize_t res = 0; + int bytes_per_sector, blk_pos, first_offs, wsize = 0; + struct rt_device *dev = file->vnode->data; + struct blk_fops_data *data = dev->user_data; + + bytes_per_sector = data->geometry.bytes_per_sector; + blk_pos = *pos / bytes_per_sector; + first_offs = *pos % bytes_per_sector; + + /* + ** #1: write first unalign block size. + */ + if (first_offs != 0) + { + if (count > bytes_per_sector - first_offs) + { + wsize = bytes_per_sector - first_offs; + } + else + { + wsize = count; + } + + if ((rbuf = rt_malloc(bytes_per_sector))) + { + res = rt_device_read(dev, blk_pos, rbuf, 1); + + if (res == 1) + { + rt_memcpy(rbuf + first_offs, buf, wsize); + res = rt_device_write(dev, blk_pos, (const void *)rbuf, 1); + + if (res == 1) + { + blk_pos += 1; + rt_free(rbuf); + + goto _goon; + } + } + + rt_free(rbuf); + } + + return 0; + } + +_goon: + /* + ** #2: write continuous block size. + */ + if ((count - wsize) / bytes_per_sector != 0) + { + res = rt_device_write(dev, blk_pos, buf + wsize, (count - wsize) / bytes_per_sector); + wsize += res * bytes_per_sector; + blk_pos += res; + + if (res != (count - wsize) / bytes_per_sector) + { + *pos += wsize; + return wsize; + } + } + + /* + ** # 3: write last unalign block size. + */ + if ((count - wsize) != 0) + { + if ((rbuf = rt_malloc(bytes_per_sector))) + { + res = rt_device_read(dev, blk_pos, rbuf, 1); + + if (res == 1) + { + rt_memcpy(rbuf, buf + wsize, count - wsize); + res = rt_device_write(dev, blk_pos, (const void *)rbuf, 1); + + if (res == 1) + { + wsize += count - wsize; + } + } + + rt_free(rbuf); + } + } + + *pos += wsize; + return wsize; +} + +static int blk_fops_flush(struct dfs_file *file) +{ + struct rt_device *dev = file->vnode->data; + + return (int)rt_device_control(dev, RT_DEVICE_CTRL_BLK_SYNC, RT_NULL); +} + +static int blk_fops_poll(struct dfs_file *file, struct rt_pollreq *req) +{ + int mask = 0; + + return mask; +} + +const static struct dfs_file_ops blk_fops = +{ + .open = blk_fops_open, + .close = blk_fops_close, + .ioctl = blk_fops_ioctl, + .read = blk_fops_read, + .write = blk_fops_write, + .flush = blk_fops_flush, + .lseek = generic_dfs_lseek, + .poll = blk_fops_poll +}; + +void device_set_blk_fops(struct rt_device *dev) +{ + dev->fops = &blk_fops; +} +#else +void device_set_blk_fops(struct rt_device *dev) +{ +} +#endif /* RT_USING_POSIX_DEVIO && RT_USING_DFS_V2 */ + +void device_get_blk_ssize(struct rt_device *dev, void *args) +{ + rt_uint32_t bytes_per_sector; + struct rt_device_blk_geometry geometry; + + rt_device_control(dev, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry); + bytes_per_sector = geometry.bytes_per_sector; + + RT_ASSERT(sizeof(bytes_per_sector) == sizeof(geometry.bytes_per_sector)); + + rt_memcpy(args, &bytes_per_sector, sizeof(bytes_per_sector)); +} + +void device_get_all_blk_ssize(struct rt_device *dev, void *args) +{ + rt_uint64_t count_mul_per; + struct rt_device_blk_geometry geometry; + + rt_device_control(dev, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry); + count_mul_per = geometry.bytes_per_sector * geometry.sector_count; + + rt_memcpy(args, &count_mul_per, sizeof(count_mul_per)); +} diff --git a/rt-thread/components/drivers/block/blk_dfs.h b/rt-thread/components/drivers/block/blk_dfs.h new file mode 100644 index 0000000..c532869 --- /dev/null +++ b/rt-thread/components/drivers/block/blk_dfs.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-08-08 GuEe-GUI first version + */ + +#ifndef __BLK_DFS_H__ +#define __BLK_DFS_H__ + +#include + +#define RT_DEVICE_CTRL_BLK_SSIZEGET 0x00001268 /**< get number of bytes per sector */ +#define RT_DEVICE_CTRL_ALL_BLK_SSIZEGET 0x80081272 /**< get number of bytes per sector * sector counts */ + +void device_set_blk_fops(struct rt_device *dev); +void device_get_blk_ssize(struct rt_device *dev, void *args); +void device_get_all_blk_ssize(struct rt_device *dev, void *args); + +#endif /* __BLK_DFS_H__ */ diff --git a/rt-thread/components/drivers/block/blk_partition.c b/rt-thread/components/drivers/block/blk_partition.c new file mode 100644 index 0000000..5a9e39d --- /dev/null +++ b/rt-thread/components/drivers/block/blk_partition.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#define DBG_TAG "blk.part" +#define DBG_LVL DBG_INFO +#include + +#include "blk_partition.h" + +static rt_err_t (*partition_list[])(struct rt_blk_disk *) = +{ +#ifdef RT_BLK_PARTITION_EFI + efi_partition, +#endif +#ifdef RT_BLK_PARTITION_DFS + dfs_partition, +#endif +}; + +rt_err_t blk_put_partition(struct rt_blk_disk *disk, const char *type, + rt_size_t start, rt_size_t count, int partno) +{ + rt_err_t err; + + struct rt_blk_device *blk = rt_calloc(1, sizeof(*blk)); + + if (type && rt_strcmp(type, "dfs")) + { + rt_uint32_t ssz = rt_blk_disk_get_logical_block_size(disk); + + rt_kprintf("found part[%u], begin: %lu, size: ", partno, start * ssz); + + if ((count >> 11) == 0) + { + rt_kprintf("%u%cB\n", count >> 1, 'K'); /* KB */ + } + else + { + rt_uint32_t size_mb = count >> 11; /* MB */ + + if ((size_mb >> 10) == 0) + { + rt_kprintf("%u.%u%cB\n", size_mb, (count >> 1) & 0x3ff, 'M'); + } + else + { + rt_kprintf("%u.%u%cB\n", size_mb >> 10, size_mb & 0x3ff, 'G'); + } + } + } + + if (!blk) + { + err = -RT_ENOMEM; + goto _fail; + } + + err = blk_dev_initialize(blk); + + if (err) + { + goto _fail; + } + + blk->partno = partno; + blk->sector_start = start; + blk->sector_count = count; + + blk->partition.offset = start; + blk->partition.size = count; + blk->partition.lock = &disk->usr_lock; + + err = disk_add_blk_dev(disk, blk); + + if (err) + { + goto _fail; + } + + ++disk->partitions; + + return RT_EOK; + +_fail: + LOG_E("%s: Put partition.%s[%u] start = %lu count = %lu error = %s", + to_disk_name(disk), type, partno, start, count, rt_strerror(err)); + + if (blk) + { + rt_free(blk); + } + + return err; +} + +rt_err_t rt_blk_disk_probe_partition(struct rt_blk_disk *disk) +{ + rt_err_t err = RT_EOK; + + if (!disk) + { + return -RT_EINVAL; + } + + LOG_D("%s: Probing disk partitions", to_disk_name(disk)); + + if (disk->partitions) + { + return err; + } + + err = -RT_EEMPTY; + + if (disk->max_partitions == RT_BLK_PARTITION_NONE) + { + LOG_D("%s: Unsupported partitions", to_disk_name(disk)); + + return err; + } + + for (int i = 0; i < RT_ARRAY_SIZE(partition_list); ++i) + { + rt_err_t part_err = partition_list[i](disk); + + if (part_err == -RT_ENOMEM) + { + err = part_err; + break; + } + + if (!part_err) + { + err = RT_EOK; + break; + } + } + + if ((err && err != -RT_ENOMEM) || disk->partitions == 0) + { + /* No partition found */ + rt_size_t total_sectors = rt_blk_disk_get_capacity(disk); + + err = blk_put_partition(disk, RT_NULL, 0, total_sectors, 0); + } + + return err; +} diff --git a/rt-thread/components/drivers/block/blk_partition.h b/rt-thread/components/drivers/block/blk_partition.h new file mode 100644 index 0000000..fb7158f --- /dev/null +++ b/rt-thread/components/drivers/block/blk_partition.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI first version + */ + +#ifndef __BLK_PARTITION_H__ +#define __BLK_PARTITION_H__ + +#include "blk_dev.h" + +rt_err_t blk_put_partition(struct rt_blk_disk *disk, const char *type, + rt_size_t start, rt_size_t count, int partno); + +rt_err_t dfs_partition(struct rt_blk_disk *disk); +rt_err_t efi_partition(struct rt_blk_disk *disk); + +#endif /* __BLK_PARTITION_H__ */ diff --git a/rt-thread/components/drivers/block/partitions/Kconfig b/rt-thread/components/drivers/block/partitions/Kconfig new file mode 100644 index 0000000..6df5e71 --- /dev/null +++ b/rt-thread/components/drivers/block/partitions/Kconfig @@ -0,0 +1,12 @@ +menu "Partition Types" + +config RT_BLK_PARTITION_DFS + bool "DFS Partition support" + depends on RT_USING_DFS + default y + +config RT_BLK_PARTITION_EFI + bool "EFI Globally Unique Identifier (GUID) Partition support" + default y + +endmenu diff --git a/rt-thread/components/drivers/block/partitions/SConscript b/rt-thread/components/drivers/block/partitions/SConscript new file mode 100644 index 0000000..06b320b --- /dev/null +++ b/rt-thread/components/drivers/block/partitions/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_BLK_PARTITION_DFS']): + src += ['dfs.c'] + +if GetDepend(['RT_BLK_PARTITION_EFI']): + src += ['efi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/block/partitions/dfs.c b/rt-thread/components/drivers/block/partitions/dfs.c new file mode 100644 index 0000000..3fdad1a --- /dev/null +++ b/rt-thread/components/drivers/block/partitions/dfs.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-07-25 weety first version + * 2023-02-25 GuEe-GUI make blk interface + */ + +#include "efi.h" + +#define DBG_TAG "blk.part.dfs" +#define DBG_LVL DBG_INFO +#include + +rt_err_t dfs_partition(struct rt_blk_disk *disk) +{ + rt_ssize_t res; + struct dfs_partition part; + rt_uint8_t *sector = rt_malloc(rt_blk_disk_get_logical_block_size(disk)); + + if (!sector) + { + return -RT_ENOMEM; + } + + res = disk->ops->read(disk, 0, sector, 1); + + if (res < 0) + { + rt_free(sector); + return res; + } + + for (rt_size_t i = 0; i < disk->max_partitions; ++i) + { + res = dfs_filesystem_get_partition(&part, sector, i); + + if (res) + { + break; + } + + if (blk_put_partition(disk, "dfs", part.offset, part.size, i) == -RT_ENOMEM) + { + break; + } + } + + rt_free(sector); + + return RT_EOK; +} diff --git a/rt-thread/components/drivers/block/partitions/efi.c b/rt-thread/components/drivers/block/partitions/efi.c new file mode 100644 index 0000000..a68a147 --- /dev/null +++ b/rt-thread/components/drivers/block/partitions/efi.c @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-05-05 linzhenxing first version + * 2023-02-25 GuEe-GUI make blk interface + */ + +#include "efi.h" + +#define DBG_TAG "blk.part.efi" +#define DBG_LVL DBG_INFO +#include + +static rt_bool_t force_gpt = 0; + +static int force_gpt_setup(void) +{ +#ifdef RT_USING_OFW + force_gpt = !!rt_ofw_bootargs_select("gpt", 0); +#endif + + return 0; +} +INIT_CORE_EXPORT(force_gpt_setup); + +/** + * @brief This function is EFI version of crc32 function. + * + * @param buf the buffer to calculate crc32 of. + * @param len the length of buf. + * @return EFI-style CRC32 value for @buf. + */ +rt_inline rt_uint32_t efi_crc32(const rt_uint8_t *buf, rt_size_t len) +{ + rt_ubase_t crc = 0xffffffffUL; + + for (rt_size_t i = 0; i < len; ++i) + { + crc ^= buf[i]; + + for (int j = 0; j < 8; ++j) + { + crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320L : 0); + } + } + + return ~crc; +} + +/** + * @brief This function get number of last logical block of device. + * + * @param disk the blk of disk. + * @return last LBA value on success, 0 on error. + * This is stored (by sd and ide-geometry) in + * the part[0] entry for this disk, and is the number of + * physical sectors available on the disk. + */ +static rt_size_t last_lba(struct rt_blk_disk *disk) +{ + return rt_blk_disk_get_capacity(disk) - 1ULL; +} + +rt_inline int pmbr_part_valid(gpt_mbr_record *part) +{ + if (part->os_type != EFI_PMBR_OSTYPE_EFI_GPT) + { + return 0; + } + + /* set to 0x00000001 (i.e., the LBA of the GPT Partition Header) */ + if (rt_le32_to_cpu(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA) + { + return 0; + } + + return GPT_MBR_PROTECTIVE; +} + +/** + * @brief This function test Protective MBR for validity. + * + * @param mbr the pointer to a legacy mbr structure. + * @param total_sectors the amount of sectors in the device + * @return + * 0 -> Invalid MBR + * 1 -> GPT_MBR_PROTECTIVE + * 2 -> GPT_MBR_HYBRID + */ +static int is_pmbr_valid(legacy_mbr *mbr, rt_size_t total_sectors) +{ + rt_uint32_t sz = 0; + int part = 0, ret = 0; /* invalid by default */ + + if (!mbr || rt_le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE) + { + goto _done; + } + + for (int i = 0; i < 4; ++i) + { + ret = pmbr_part_valid(&mbr->partition_record[i]); + + if (ret == GPT_MBR_PROTECTIVE) + { + part = i; + /* + * Ok, we at least know that there's a protective MBR, + * now check if there are other partition types for + * hybrid MBR. + */ + goto _check_hybrid; + } + } + + if (ret != GPT_MBR_PROTECTIVE) + { + goto _done; + } + +_check_hybrid: + for (int i = 0; i < 4; i++) + { + if (mbr->partition_record[i].os_type != EFI_PMBR_OSTYPE_EFI_GPT && + mbr->partition_record[i].os_type != 0x00) + { + ret = GPT_MBR_HYBRID; + } + } + + /* + * Protective MBRs take up the lesser of the whole disk + * or 2 TiB (32bit LBA), ignoring the rest of the disk. + * Some partitioning programs, nonetheless, choose to set + * the size to the maximum 32-bit limitation, disregarding + * the disk size. + * + * Hybrid MBRs do not necessarily comply with this. + * + * Consider a bad value here to be a warning to support dd'ing + * an image from a smaller disk to a larger disk. + */ + if (ret == GPT_MBR_PROTECTIVE) + { + sz = rt_le32_to_cpu(mbr->partition_record[part].size_in_lba); + + if (sz != (rt_uint32_t)total_sectors - 1 && sz != 0xffffffff) + { + LOG_W("GPT: mbr size in lba (%u) different than whole disk (%u)", + sz, rt_min_t(rt_uint32_t, total_sectors - 1, 0xffffffff)); + } + } + +_done: + return ret; +} + +/** + * @brief This function read bytes from disk, starting at given LBA. + * + * @param disk the blk of disk. + * @param lba the Logical Block Address of the partition table. + * @param buffer the destination buffer. + * @param count the bytes to read. + * @return number of bytes read on success, 0 on error. + */ +static rt_size_t read_lba(struct rt_blk_disk *disk, + rt_uint64_t lba, rt_uint8_t *buffer, rt_size_t count) +{ + rt_size_t totalreadcount = 0; + + if (!buffer || lba > last_lba(disk)) + { + return 0; + } + + for (rt_uint64_t n = lba; count; ++n) + { + int copied = 512; + + disk->ops->read(disk, n, buffer, 1); + + if (copied > count) + { + copied = count; + } + + buffer += copied; + totalreadcount += copied; + count -= copied; + } + + return totalreadcount; +} + +/** + * @brief This function reads partition entries from disk. + * + * @param disk the blk of disk. + * @param gpt the GPT header + * @return ptes on success, null on error. + */ +static gpt_entry *alloc_read_gpt_entries(struct rt_blk_disk *disk, + gpt_header *gpt) +{ + rt_size_t count; + gpt_entry *pte; + rt_uint64_t entry_lba; + + if (!gpt) + { + return RT_NULL; + } + + count = (rt_size_t)rt_le32_to_cpu(gpt->num_partition_entries) * + rt_le32_to_cpu(gpt->sizeof_partition_entry); + + if (!count) + { + return RT_NULL; + } + + pte = rt_malloc(count); + + if (!pte) + { + return RT_NULL; + } + + entry_lba = rt_le64_to_cpu(gpt->partition_entry_lba); + + if (read_lba(disk, entry_lba, (rt_uint8_t *)pte, count) < count) + { + rt_free(pte); + pte = RT_NULL; + + return RT_NULL; + } + + /* Remember to free pte when done */ + return pte; +} + +/** + * @brief This function allocates GPT header, reads into it from disk. + * + * @param disk the blk of disk. + * @param lba the Logical Block Address of the partition table + * @return GPT header on success, null on error. + */ +static gpt_header *alloc_read_gpt_header(struct rt_blk_disk *disk, rt_uint64_t lba) +{ + gpt_header *gpt; + rt_uint32_t ssz = rt_blk_disk_get_logical_block_size(disk); + + gpt = rt_malloc(ssz); + + if (!gpt) + { + return RT_NULL; + } + + if (read_lba(disk, lba, (rt_uint8_t *)gpt, ssz) < ssz) + { + rt_free(gpt); + gpt = RT_NULL; + + return RT_NULL; + } + + /* Remember to free gpt when finished with it */ + return gpt; +} + +/** + * @brief This function tests one GPT header and PTEs for validity. + * + * @param disk the blk of disk. + * @param lba the Logical Block Address of the GPT header to test. + * @param gpt the GPT header ptr, filled on return. + * @param ptes the PTEs ptr, filled on return. + * @returns true if valid, false on error. + * If valid, returns pointers to newly allocated GPT header and PTEs. + */ +static rt_bool_t is_gpt_valid(struct rt_blk_disk *disk, + rt_uint64_t lba, gpt_header **gpt, gpt_entry **ptes) +{ + rt_uint32_t crc, origcrc; + rt_uint64_t lastlba, pt_size; + rt_ssize_t logical_block_size; + + if (!ptes) + { + return RT_FALSE; + } + + if (!(*gpt = alloc_read_gpt_header(disk, lba))) + { + return RT_FALSE; + } + + /* Check the GUID Partition Table signature */ + if (rt_le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) + { + LOG_D("%s: GUID Partition Table Header signature is wrong: %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->signature), + (rt_uint64_t)GPT_HEADER_SIGNATURE); + + goto _fail; + } + + /* Check the GUID Partition Table header size is too big */ + logical_block_size = rt_blk_disk_get_logical_block_size(disk); + + if (rt_le32_to_cpu((*gpt)->header_size) > logical_block_size) + { + LOG_D("%s: GUID Partition Table Header size is too large: %u > %u", + to_disk_name(disk), + rt_le32_to_cpu((*gpt)->header_size), + logical_block_size); + + goto _fail; + } + + /* Check the GUID Partition Table header size is too small */ + if (rt_le32_to_cpu((*gpt)->header_size) < sizeof(gpt_header)) + { + LOG_D("%s: GUID Partition Table Header size is too small: %u < %u", + to_disk_name(disk), + rt_le32_to_cpu((*gpt)->header_size), + sizeof(gpt_header)); + + goto _fail; + } + + /* Check the GUID Partition Table CRC */ + origcrc = rt_le32_to_cpu((*gpt)->header_crc32); + (*gpt)->header_crc32 = 0; + crc = efi_crc32((const rt_uint8_t *)(*gpt), rt_le32_to_cpu((*gpt)->header_size)); + + if (crc != origcrc) + { + LOG_D("%s: GUID Partition Table Header CRC is wrong: %x != %x", + to_disk_name(disk), crc, origcrc); + + goto _fail; + } + + (*gpt)->header_crc32 = rt_cpu_to_le32(origcrc); + + /* + * Check that the start_lba entry points to the LBA that contains + * the GUID Partition Table + */ + if (rt_le64_to_cpu((*gpt)->start_lba) != lba) + { + LOG_D("%s: GPT start_lba incorrect: %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->start_lba), + (rt_uint64_t)lba); + + goto _fail; + } + + /* Check the first_usable_lba and last_usable_lba are within the disk */ + lastlba = last_lba(disk); + + if (rt_le64_to_cpu((*gpt)->first_usable_lba) > lastlba) + { + LOG_D("%s: GPT: first_usable_lba incorrect: %lld > %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->first_usable_lba), + (rt_uint64_t)lastlba); + + goto _fail; + } + + if (rt_le64_to_cpu((*gpt)->last_usable_lba) > lastlba) + { + LOG_D("%s: GPT: last_usable_lba incorrect: %lld > %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->last_usable_lba), + (rt_uint64_t)lastlba); + + goto _fail; + } + if (rt_le64_to_cpu((*gpt)->last_usable_lba) < rt_le64_to_cpu((*gpt)->first_usable_lba)) + { + LOG_D("%s: GPT: last_usable_lba incorrect: %lld > %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->last_usable_lba), + (rt_uint64_t)rt_le64_to_cpu((*gpt)->first_usable_lba)); + + goto _fail; + } + + /* Check that sizeof_partition_entry has the correct value */ + if (rt_le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) + { + LOG_D("%s: GUID Partition Entry Size check failed", to_disk_name(disk)); + + goto _fail; + } + + /* Sanity check partition table size */ + pt_size = (rt_uint64_t)rt_le32_to_cpu((*gpt)->num_partition_entries) * + rt_le32_to_cpu((*gpt)->sizeof_partition_entry); + + if (!(*ptes = alloc_read_gpt_entries(disk, *gpt))) + { + goto _fail; + } + + /* Check the GUID Partition Entry Array CRC */ + crc = efi_crc32((const rt_uint8_t *)(*ptes), pt_size); + + if (crc != rt_le32_to_cpu((*gpt)->partition_entry_array_crc32)) + { + LOG_D("%s: GUID Partition Entry Array CRC check failed", to_disk_name(disk)); + + goto _fail_ptes; + } + + /* We're done, all's well */ + return RT_TRUE; + +_fail_ptes: + rt_free(*ptes); + *ptes = RT_NULL; + +_fail: + rt_free(*gpt); + *gpt = RT_NULL; + + return RT_FALSE; +} + +/** + * @brief This function tests one PTE for validity. + * + * @param pte the pte to check. + * @param lastlba the last lba of the disk. + * @return valid boolean of pte. + */ +rt_inline rt_bool_t is_pte_valid(const gpt_entry *pte, const rt_size_t lastlba) +{ + if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) || + rt_le64_to_cpu(pte->starting_lba) > lastlba || + rt_le64_to_cpu(pte->ending_lba) > lastlba) + { + return RT_FALSE; + } + + return RT_TRUE; +} + +/** + * @brief This function search disk for valid GPT headers and PTEs. + * + * @param disk the blk of disk. + * @param pgpt the primary GPT header. + * @param agpt the alternate GPT header. + * @param lastlba the last LBA number. + */ +static void compare_gpts(struct rt_blk_disk *disk, + gpt_header *pgpt, gpt_header *agpt, rt_uint64_t lastlba) +{ + int error_found = 0; + + if (!pgpt || !agpt) + { + return; + } + + if (rt_le64_to_cpu(pgpt->start_lba) != rt_le64_to_cpu(agpt->alternate_lba)) + { + LOG_W("%s: GPT:Primary header LBA(%lld) != Alt(%lld), header alternate_lba", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(pgpt->start_lba), + (rt_uint64_t)rt_le64_to_cpu(agpt->alternate_lba)); + + ++error_found; + } + + if (rt_le64_to_cpu(pgpt->alternate_lba) != rt_le64_to_cpu(agpt->start_lba)) + { + LOG_W("%s: GPT:Primary header alternate_lba(%lld) != Alt(%lld), header start_lba", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(pgpt->alternate_lba), + (rt_uint64_t)rt_le64_to_cpu(agpt->start_lba)); + + ++error_found; + } + + if (rt_le64_to_cpu(pgpt->first_usable_lba) != rt_le64_to_cpu(agpt->first_usable_lba)) + { + LOG_W("%s: GPT:first_usable_lbas don't match %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(pgpt->first_usable_lba), + (rt_uint64_t)rt_le64_to_cpu(agpt->first_usable_lba)); + + ++error_found; + } + + if (rt_le64_to_cpu(pgpt->last_usable_lba) != rt_le64_to_cpu(agpt->last_usable_lba)) + { + LOG_W("%s: GPT:last_usable_lbas don't match %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(pgpt->last_usable_lba), + (rt_uint64_t)rt_le64_to_cpu(agpt->last_usable_lba)); + + ++error_found; + } + + if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) + { + LOG_W("%s: GPT:disk_guids don't match", to_disk_name(disk)); + + ++error_found; + } + + if (rt_le32_to_cpu(pgpt->num_partition_entries) != + rt_le32_to_cpu(agpt->num_partition_entries)) + { + LOG_W("%s: GPT:num_partition_entries don't match: 0x%x != 0x%x", + to_disk_name(disk), + rt_le32_to_cpu(pgpt->num_partition_entries), + rt_le32_to_cpu(agpt->num_partition_entries)); + + ++error_found; + } + + if (rt_le32_to_cpu(pgpt->sizeof_partition_entry) != + rt_le32_to_cpu(agpt->sizeof_partition_entry)) + { + LOG_W("%s: GPT:sizeof_partition_entry values don't match: 0x%x != 0x%x", + to_disk_name(disk), + rt_le32_to_cpu(pgpt->sizeof_partition_entry), + rt_le32_to_cpu(agpt->sizeof_partition_entry)); + + ++error_found; + } + + if (rt_le32_to_cpu(pgpt->partition_entry_array_crc32) != + rt_le32_to_cpu(agpt->partition_entry_array_crc32)) + { + LOG_W("%s: GPT:partition_entry_array_crc32 values don't match: 0x%x != 0x%x", + to_disk_name(disk), + rt_le32_to_cpu(pgpt->partition_entry_array_crc32), + rt_le32_to_cpu(agpt->partition_entry_array_crc32)); + + ++error_found; + } + + if (rt_le64_to_cpu(pgpt->alternate_lba) != lastlba) + { + LOG_W("%s: GPT:Primary header thinks Alt. header is not at the end of the disk: %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(pgpt->alternate_lba), + (rt_uint64_t)lastlba); + + ++error_found; + } + + if (rt_le64_to_cpu(agpt->start_lba) != lastlba) + { + LOG_W("%s: GPT:Alternate GPT header not at the end of the disk: %lld != %lld", + to_disk_name(disk), + (rt_uint64_t)rt_le64_to_cpu(agpt->start_lba), + (rt_uint64_t)lastlba); + + ++error_found; + } + + if (error_found) + { + LOG_W("GPT: Use GNU Parted to correct GPT errors"); + } +} + +/** + * @brief This function search disk for valid GPT headers and PTEs. + * + * @param disk the disk parsed partitions. + * @param gpt the GPT header ptr, filled on return. + * @param ptes the PTEs ptr, filled on return. + * @return 1 if valid, 0 on error. + * If valid, returns pointers to newly allocated GPT header and PTEs. + * Validity depends on PMBR being valid (or being overridden by the + * 'gpt' kernel command line option) and finding either the Primary + * GPT header and PTEs valid, or the Alternate GPT header and PTEs + * valid. If the Primary GPT header is not valid, the Alternate GPT header + * is not checked unless the 'gpt' kernel command line option is passed. + * This protects against devices which misreport their size, and forces + * the user to decide to use the Alternate GPT. + */ +static rt_bool_t find_valid_gpt(struct rt_blk_disk *disk, + gpt_header **gpt, gpt_entry **ptes) +{ + int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; + gpt_header *pgpt = RT_NULL, *agpt = RT_NULL; + gpt_entry *pptes = RT_NULL, *aptes = RT_NULL; + legacy_mbr *legacymbr; + rt_size_t total_sectors = rt_blk_disk_get_capacity(disk); + rt_size_t lastlba; + + if (!ptes) + { + return RT_FALSE; + } + + lastlba = last_lba(disk); + + if (!force_gpt) + { + /* This will be added to the EFI Spec. per Intel after v1.02. */ + legacymbr = rt_malloc(sizeof(*legacymbr)); + + if (!legacymbr) + { + return RT_FALSE; + } + + read_lba(disk, 0, (rt_uint8_t *)legacymbr, sizeof(*legacymbr)); + good_pmbr = is_pmbr_valid(legacymbr, total_sectors); + rt_free(legacymbr); + + if (!good_pmbr) + { + return RT_FALSE; + } + + LOG_D("%s: Device has a %s MBR", to_disk_name(disk), + good_pmbr == GPT_MBR_PROTECTIVE ? "protective" : "hybrid"); + } + + good_pgpt = is_gpt_valid(disk, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes); + + if (good_pgpt) + { + good_agpt = is_gpt_valid(disk, rt_le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes); + } + + if (!good_agpt && force_gpt) + { + good_agpt = is_gpt_valid(disk, lastlba, &agpt, &aptes); + } + + /* The obviously unsuccessful case */ + if (!good_pgpt && !good_agpt) + { + goto _fail; + } + + compare_gpts(disk, pgpt, agpt, lastlba); + + /* The good cases */ + if (good_pgpt) + { + *gpt = pgpt; + *ptes = pptes; + rt_free(agpt); + rt_free(aptes); + + if (!good_agpt) + { + LOG_D("%s: Alternate GPT is invalid, using primary GPT", to_disk_name(disk)); + } + + return RT_TRUE; + } + else if (good_agpt) + { + *gpt = agpt; + *ptes = aptes; + rt_free(pgpt); + rt_free(pptes); + + LOG_D("%s: Primary GPT is invalid, using alternate GPT", to_disk_name(disk)); + + return RT_TRUE; + } + +_fail: + rt_free(pgpt); + rt_free(agpt); + rt_free(pptes); + rt_free(aptes); + + *gpt = RT_NULL; + *ptes = RT_NULL; + + return RT_FALSE; +} + +rt_err_t efi_partition(struct rt_blk_disk *disk) +{ + rt_uint32_t entries_nr; + gpt_header *gpt = RT_NULL; + gpt_entry *ptes = RT_NULL; + + if (!find_valid_gpt(disk, &gpt, &ptes) || !gpt || !ptes) + { + rt_free(gpt); + rt_free(ptes); + + return -RT_EINVAL; + } + + entries_nr = rt_le32_to_cpu(gpt->num_partition_entries); + + for (int i = 0; i < entries_nr && i < disk->max_partitions; ++i) + { + rt_uint64_t start = rt_le64_to_cpu(ptes[i].starting_lba); + rt_uint64_t size = rt_le64_to_cpu(ptes[i].ending_lba) - + rt_le64_to_cpu(ptes[i].starting_lba) + 1ULL; + + if (!is_pte_valid(&ptes[i], last_lba(disk))) + { + continue; + } + + if (blk_put_partition(disk, "gpt", start, size, i) == -RT_ENOMEM) + { + break; + } + } + + rt_free(gpt); + rt_free(ptes); + + return RT_EOK; +} diff --git a/rt-thread/components/drivers/block/partitions/efi.h b/rt-thread/components/drivers/block/partitions/efi.h new file mode 100644 index 0000000..6a10ec8 --- /dev/null +++ b/rt-thread/components/drivers/block/partitions/efi.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-05-05 linzhenxing first version + * 2023-02-25 GuEe-GUI make blk interface + */ + +#ifndef __PARTITIONS_EFI_H__ +#define __PARTITIONS_EFI_H__ + +#include "../blk_partition.h" +#include +#include + +#define MSDOS_MBR_SIGNATURE 0xaa55 +#define EFI_PMBR_OSTYPE_EFI 0xef +#define EFI_PMBR_OSTYPE_EFI_GPT 0xee + +#define GPT_MBR_PROTECTIVE 1 +#define GPT_MBR_HYBRID 2 + +#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL +#define GPT_HEADER_REVISION_V1 0x00010000 +#define GPT_PRIMARY_PARTITION_TABLE_LBA 1 + +#ifndef __UUID_H__ +#define UUID_SIZE 16 + +typedef struct +{ + rt_uint8_t b[UUID_SIZE]; +} guid_t; +#endif /* __UUID_H__ */ + +#ifndef __EFI_H__ +typedef guid_t efi_guid_t rt_align(4); + +#define EFI_GUID(a, b, c, d...) (efi_guid_t) \ +{{ \ + (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, \ + d \ +}} + +#define NULL_GUID \ + EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + +rt_inline int efi_guidcmp(efi_guid_t left, efi_guid_t right) +{ + return rt_memcmp(&left, &right, sizeof (efi_guid_t)); +} +#endif /* __EFI_H__ */ + +#define PARTITION_SYSTEM_GUID \ + EFI_GUID(0xc12a7328, 0xf81f, 0x11d2, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b) + +#define LEGACY_MBR_PARTITION_GUID \ + EFI_GUID(0x024dee41, 0x33e7, 0x11d3, 0x9d, 0x69, 0x00, 0x08, 0xc7, 0x81, 0xf3, 0x9f) + +#define PARTITION_MSFT_RESERVED_GUID \ + EFI_GUID(0xe3c9e316, 0x0b5c, 0x4db8, 0x81, 0x7d, 0xf9, 0x2d, 0xf0, 0x02, 0x15, 0xae) + +#define PARTITION_BASIC_DATA_GUID \ + EFI_GUID(0xebd0a0a2, 0xb9e5, 0x4433, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7) + +rt_packed(struct _gpt_header +{ + rt_le64_t signature; + rt_le32_t revision; + rt_le32_t header_size; + rt_le32_t header_crc32; + rt_le32_t reserved1; + rt_le64_t start_lba; + rt_le64_t alternate_lba; + rt_le64_t first_usable_lba; + rt_le64_t last_usable_lba; + efi_guid_t disk_guid; + rt_le64_t partition_entry_lba; + rt_le32_t num_partition_entries; + rt_le32_t sizeof_partition_entry; + rt_le32_t partition_entry_array_crc32; + + /* + * The rest of the logical block is reserved by UEFI and must be zero. + * EFI standard handles this by: + * + * uint8_t reserved2[BlockSize - 92]; + */ +}); +typedef struct _gpt_header gpt_header; + +rt_packed(struct _gpt_entry_attributes +{ + rt_uint64_t required_to_function:1; + rt_uint64_t reserved:47; + rt_uint64_t type_guid_specific:16; +}); +typedef struct _gpt_entry_attributes gpt_entry_attributes; + +rt_packed(struct _gpt_entry +{ + efi_guid_t partition_type_guid; + efi_guid_t unique_partition_guid; + rt_le64_t starting_lba; + rt_le64_t ending_lba; + gpt_entry_attributes attributes; + rt_le16_t partition_name[72/sizeof(rt_le16_t)]; +}); +typedef struct _gpt_entry gpt_entry; + +rt_packed(struct _gpt_mbr_record +{ + rt_uint8_t boot_indicator; /* unused by EFI, set to 0x80 for bootable */ + rt_uint8_t start_head; /* unused by EFI, pt start in CHS */ + rt_uint8_t start_sector; /* unused by EFI, pt start in CHS */ + rt_uint8_t start_track; + rt_uint8_t os_type; /* EFI and legacy non-EFI OS types */ + rt_uint8_t end_head; /* unused by EFI, pt end in CHS */ + rt_uint8_t end_sector; /* unused by EFI, pt end in CHS */ + rt_uint8_t end_track; /* unused by EFI, pt end in CHS */ + rt_le32_t starting_lba; /* used by EFI - start addr of the on disk pt */ + rt_le32_t size_in_lba; /* used by EFI - size of pt in LBA */ +}); +typedef struct _gpt_mbr_record gpt_mbr_record; + +rt_packed(struct _legacy_mbr +{ + rt_uint8_t boot_code[440]; + rt_le32_t unique_mbr_signature; + rt_le16_t unknown; + gpt_mbr_record partition_record[4]; + rt_le16_t signature; +}); +typedef struct _legacy_mbr legacy_mbr; + +#endif /* __PARTITIONS_EFI_H__ */ diff --git a/rt-thread/components/drivers/can/can.c b/rt-thread/components/drivers/can/dev_can.c similarity index 99% rename from rt-thread/components/drivers/can/can.c rename to rt-thread/components/drivers/can/dev_can.c index f10e05d..186f113 100644 --- a/rt-thread/components/drivers/can/can.c +++ b/rt-thread/components/drivers/can/dev_can.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -435,7 +435,7 @@ static rt_err_t rt_can_close(struct rt_device *dev) } can->ops->control(can, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_CAN_INT_ERR); - + can->ops->control(can, RT_CAN_CMD_START, RT_FALSE); CAN_UNLOCK(can); return RT_EOK; diff --git a/rt-thread/components/drivers/clk/Kconfig b/rt-thread/components/drivers/clk/Kconfig index 3c34d38..c13ccbf 100644 --- a/rt-thread/components/drivers/clk/Kconfig +++ b/rt-thread/components/drivers/clk/Kconfig @@ -3,3 +3,7 @@ menuconfig RT_USING_CLK depends on RT_USING_DM select RT_USING_ADT_REF default y + +if RT_USING_CLK + osource "$(SOC_DM_CLK_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/clk/clk.c b/rt-thread/components/drivers/clk/clk.c index 053c67d..2bb45f8 100644 --- a/rt-thread/components/drivers/clk/clk.c +++ b/rt-thread/components/drivers/clk/clk.c @@ -55,6 +55,10 @@ static struct rt_clk *clk_alloc(struct rt_clk_node *clk_np, const char *dev_id, clk->fw_node = fw_node; } + else + { + clk = rt_err_ptr(-RT_ENOMEM); + } return clk; } @@ -76,7 +80,7 @@ static struct rt_clk *clk_create(struct rt_clk_node *clk_np, const char *dev_id, { struct rt_clk *clk = clk_alloc(clk_np, dev_id, con_id, fw_node); - if (clk) + if (!rt_is_err(clk)) { clk_get(clk_np); @@ -135,15 +139,6 @@ rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_ struct rt_clk *clk = RT_NULL; if (clk_np) - { - clk = clk_alloc(clk_np, RT_NULL, RT_NULL, RT_NULL); - } - else - { - err = -RT_EINVAL; - } - - if (!err && clk_np) { clk_np->clk = clk; @@ -152,6 +147,12 @@ rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_ clk_np->ops = &unused_clk_ops; } + #if RT_NAME_MAX > 0 + rt_strncpy(clk_np->rt_parent.name, RT_CLK_NODE_OBJ_NAME, RT_NAME_MAX); + #else + clk_np->rt_parent.name = RT_CLK_NODE_OBJ_NAME; + #endif + rt_ref_init(&clk_np->ref); rt_list_init(&clk_np->list); rt_list_init(&clk_np->children_nodes); @@ -159,7 +160,16 @@ rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_ if (parent_np) { - clk_set_parent(clk_np, parent_np); + clk_np->clk = clk_alloc(clk_np, RT_NULL, RT_NULL, RT_NULL); + + if (clk_np->clk) + { + clk_set_parent(clk_np, parent_np); + } + else + { + err = -RT_ENOMEM; + } } else { @@ -265,11 +275,16 @@ static rt_err_t clk_prepare(struct rt_clk *clk, struct rt_clk_node *clk_np) clk_prepare(clk_np->clk, clk_np->parent); } - if (clk_np->ops->prepare) + if (clk->prepare_count == 0 && clk_np->ops->prepare) { err = clk_np->ops->prepare(clk); } + if (!err) + { + ++clk->prepare_count; + } + return err; } @@ -287,10 +302,6 @@ rt_err_t rt_clk_prepare(struct rt_clk *clk) rt_hw_spin_unlock(&_clk_lock.lock); } - else - { - err = -RT_EINVAL; - } return err; } @@ -302,10 +313,14 @@ static void clk_unprepare(struct rt_clk *clk, struct rt_clk_node *clk_np) clk_unprepare(clk_np->clk, clk_np->parent); } - if (clk_np->ops->unprepare) + if (clk->prepare_count == 1 && clk_np->ops->unprepare) { clk_np->ops->unprepare(clk); } + if (clk->prepare_count) + { + --clk->prepare_count; + } } rt_err_t rt_clk_unprepare(struct rt_clk *clk) @@ -322,10 +337,6 @@ rt_err_t rt_clk_unprepare(struct rt_clk *clk) rt_hw_spin_unlock(&_clk_lock.lock); } - else - { - err = -RT_EINVAL; - } return err; } @@ -339,11 +350,16 @@ static rt_err_t clk_enable(struct rt_clk *clk, struct rt_clk_node *clk_np) clk_enable(clk_np->clk, clk_np->parent); } - if (clk_np->ops->enable) + if (clk->enable_count == 0 && clk_np->ops->enable) { err = clk_np->ops->enable(clk); } + if (!err) + { + ++clk->enable_count; + } + return err; } @@ -359,10 +375,6 @@ rt_err_t rt_clk_enable(struct rt_clk *clk) rt_hw_spin_unlock(&_clk_lock.lock); } - else - { - err = -RT_EINVAL; - } return err; } @@ -374,10 +386,14 @@ static void clk_disable(struct rt_clk *clk, struct rt_clk_node *clk_np) clk_disable(clk_np->clk, clk_np->parent); } - if (clk_np->ops->disable) + if (clk->enable_count == 1 && clk_np->ops->disable) { clk_np->ops->disable(clk); } + if (clk->enable_count) + { + --clk->enable_count; + } } void rt_clk_disable(struct rt_clk *clk) @@ -394,7 +410,7 @@ void rt_clk_disable(struct rt_clk *clk) rt_err_t rt_clk_prepare_enable(struct rt_clk *clk) { - rt_err_t err; + rt_err_t err = RT_EOK; RT_DEBUG_NOT_IN_INTERRUPT; @@ -412,10 +428,6 @@ rt_err_t rt_clk_prepare_enable(struct rt_clk *clk) } } } - else - { - err = -RT_EINVAL; - } return err; } @@ -453,10 +465,6 @@ rt_err_t rt_clk_array_prepare(struct rt_clk_array *clk_arr) } } } - else - { - err = -RT_EINVAL; - } return err; } @@ -478,10 +486,6 @@ rt_err_t rt_clk_array_unprepare(struct rt_clk_array *clk_arr) } } } - else - { - err = -RT_EINVAL; - } return err; } @@ -508,10 +512,6 @@ rt_err_t rt_clk_array_enable(struct rt_clk_array *clk_arr) } } } - else - { - err = -RT_EINVAL; - } return err; } @@ -529,29 +529,16 @@ void rt_clk_array_disable(struct rt_clk_array *clk_arr) rt_err_t rt_clk_array_prepare_enable(struct rt_clk_array *clk_arr) { - rt_err_t err = RT_EOK; + rt_err_t err; - if (clk_arr) + if ((err = rt_clk_array_prepare(clk_arr))) { - for (int i = 0; i < clk_arr->count; ++i) - { - if ((err = rt_clk_prepare_enable(clk_arr->clks[i]))) - { - LOG_E("CLK Array[%d] %s failed error = %s", i, - "prepare_enable", rt_strerror(err)); - - while (i --> 0) - { - rt_clk_disable_unprepare(clk_arr->clks[i]); - } - - break; - } - } + return err; } - else + + if ((err = rt_clk_array_enable(clk_arr))) { - err = -RT_EINVAL; + rt_clk_array_unprepare(clk_arr); } return err; @@ -559,13 +546,8 @@ rt_err_t rt_clk_array_prepare_enable(struct rt_clk_array *clk_arr) void rt_clk_array_disable_unprepare(struct rt_clk_array *clk_arr) { - if (clk_arr) - { - for (int i = 0; i < clk_arr->count; ++i) - { - rt_clk_disable_unprepare(clk_arr->clks[i]); - } - } + rt_clk_array_disable(clk_arr); + rt_clk_array_unprepare(clk_arr); } rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t max) @@ -604,10 +586,6 @@ rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t ma rt_hw_spin_unlock(&_clk_lock.lock); } - else - { - err = -RT_EINVAL; - } return err; } @@ -622,10 +600,6 @@ rt_err_t rt_clk_set_min_rate(struct rt_clk *clk, rt_ubase_t rate) err = rt_clk_set_rate_range(clk, rate, clk_np->max_rate); } - else - { - err = -RT_EINVAL; - } return err; } @@ -640,10 +614,6 @@ rt_err_t rt_clk_set_max_rate(struct rt_clk *clk, rt_ubase_t rate) err = rt_clk_set_rate_range(clk, clk_np->min_rate, rate); } - else - { - err = -RT_EINVAL; - } return err; } @@ -652,7 +622,9 @@ rt_err_t rt_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate) { rt_err_t err = RT_EOK; - if (clk && clk->clk_np) + rate = rt_clk_round_rate(clk, rate); + + if (clk && clk->clk_np && rate > 0) { struct rt_clk_node *clk_np = clk->clk_np; @@ -690,17 +662,13 @@ rt_err_t rt_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate) rt_hw_spin_unlock(&_clk_lock.lock); } - else - { - err = -RT_EINVAL; - } return err; } rt_ubase_t rt_clk_get_rate(struct rt_clk *clk) { - rt_ubase_t rate = -1UL; + rt_ubase_t rate = 0; if (clk) { @@ -729,10 +697,6 @@ rt_err_t rt_clk_set_phase(struct rt_clk *clk, int degrees) rt_hw_spin_unlock(&_clk_lock.lock); } - else - { - err = -RT_EINVAL; - } return err; } @@ -749,38 +713,49 @@ rt_base_t rt_clk_get_phase(struct rt_clk *clk) rt_hw_spin_unlock(&_clk_lock.lock); } - else - { - res = -RT_EINVAL; - } return res; } rt_base_t rt_clk_round_rate(struct rt_clk *clk, rt_ubase_t rate) { - rt_base_t res = RT_EOK; + rt_base_t res = -RT_EINVAL; - if (clk && clk->clk_np && clk->clk_np->ops->round_rate) + if (clk && clk->clk_np) { - rt_ubase_t best_parent_rate; struct rt_clk_node *clk_np = clk->clk_np; - rt_hw_spin_lock(&_clk_lock.lock); - - if (clk_np->min_rate && clk_np->max_rate) + if (clk_np->ops->round_rate) { - rate = rt_clamp(rate, clk_np->min_rate, clk_np->max_rate); + rt_ubase_t best_parent_rate; + + rt_hw_spin_lock(&_clk_lock.lock); + + if (clk_np->min_rate && clk_np->max_rate) + { + rate = rt_clamp(rate, clk_np->min_rate, clk_np->max_rate); + } + + res = clk_np->ops->round_rate(clk, rate, &best_parent_rate); + (void)best_parent_rate; + + rt_hw_spin_unlock(&_clk_lock.lock); + } + else + { + if (rate < clk_np->min_rate) + { + res = clk_np->min_rate; + } + else if (rate > clk_np->max_rate) + { + res = clk_np->max_rate; + } + else + { + res = rate; + } } - - res = clk->clk_np->ops->round_rate(clk, rate, &best_parent_rate); - (void)best_parent_rate; - - rt_hw_spin_unlock(&_clk_lock.lock); - } - else - { - res = -RT_EINVAL; } return res; @@ -798,10 +773,6 @@ rt_err_t rt_clk_set_parent(struct rt_clk *clk, struct rt_clk *clk_parent) rt_hw_spin_unlock(&_clk_lock.lock); } - else - { - err = -RT_EINVAL; - } return err; } @@ -887,7 +858,7 @@ void rt_clk_put(struct rt_clk *clk) } #ifdef RT_USING_OFW -static struct rt_clk *ofw_get_clk_no_lock(struct rt_ofw_node *np, int index, const char *name) +static struct rt_clk *ofw_get_clk_no_lock(struct rt_ofw_node *np, int index, const char *name, rt_bool_t locked) { struct rt_clk *clk = RT_NULL; struct rt_ofw_cell_args clk_args; @@ -895,10 +866,32 @@ static struct rt_clk *ofw_get_clk_no_lock(struct rt_ofw_node *np, int index, con if (!rt_ofw_parse_phandle_cells(np, "clocks", "#clock-cells", index, &clk_args)) { int count; + struct rt_object *obj; + struct rt_clk_node *clk_np = RT_NULL; struct rt_ofw_node *clk_ofw_np = clk_args.data; - struct rt_clk_node *clk_np = rt_ofw_data(clk_ofw_np); - count = rt_ofw_count_of_clk(clk_ofw_np); + if (!rt_ofw_data(clk_ofw_np)) + { + if (locked) + { + rt_hw_spin_unlock(&_clk_lock.lock); + } + + rt_platform_ofw_request(clk_ofw_np); + + if (locked) + { + rt_hw_spin_lock(&_clk_lock.lock); + } + } + + if (rt_ofw_data(clk_ofw_np) && (obj = rt_ofw_parse_object(clk_ofw_np, + RT_CLK_NODE_OBJ_NAME, "#clock-cells"))) + { + clk_np = rt_container_of(obj, struct rt_clk_node, rt_parent); + + count = rt_ofw_count_of_clk(clk_ofw_np); + } rt_ofw_node_put(clk_ofw_np); @@ -912,6 +905,10 @@ static struct rt_clk *ofw_get_clk_no_lock(struct rt_ofw_node *np, int index, con clk = clk_create(clk_np, np->full_name, name, &clk_args, np); } + else + { + clk = rt_err_ptr(-RT_ERROR); + } } return clk; @@ -923,7 +920,7 @@ static struct rt_clk *ofw_get_clk(struct rt_ofw_node *np, int index, const char rt_hw_spin_lock(&_clk_lock.lock); - clk = ofw_get_clk_no_lock(np, index, name); + clk = ofw_get_clk_no_lock(np, index, name, RT_TRUE); rt_hw_spin_unlock(&_clk_lock.lock); @@ -935,6 +932,11 @@ struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np) int count; struct rt_clk_array *clk_arr = RT_NULL; + if (!np) + { + return rt_err_ptr(-RT_EINVAL); + } + if ((count = rt_ofw_count_phandle_cells(np, "clocks", "#clock-cells")) > 0) { clk_arr = rt_calloc(1, sizeof(*clk_arr) + sizeof(clk_arr->clks[0]) * count); @@ -942,6 +944,7 @@ struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np) if (clk_arr) { int i; + rt_err_t err = RT_EOK; rt_bool_t has_name = rt_ofw_prop_read_bool(np, "clock-names"); clk_arr->count = count; @@ -957,10 +960,12 @@ struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np) rt_ofw_prop_read_string_index(np, "clock-names", i, &name); } - clk_arr->clks[i] = ofw_get_clk_no_lock(np, i, name); + clk_arr->clks[i] = ofw_get_clk_no_lock(np, i, name, RT_FALSE); - if (!clk_arr->clks[i]) + if (rt_is_err(clk_arr->clks[i])) { + err = rt_ptr_err(clk_arr->clks[i]); + --i; break; } @@ -971,7 +976,7 @@ struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np) if (i > 0 && i < count) { rt_clk_array_put(clk_arr); - clk_arr = RT_NULL; + clk_arr = rt_err_ptr(err); } } } diff --git a/rt-thread/components/drivers/core/bus.c b/rt-thread/components/drivers/core/bus.c index 13e154f..a1d6d7e 100644 --- a/rt-thread/components/drivers/core/bus.c +++ b/rt-thread/components/drivers/core/bus.c @@ -360,9 +360,9 @@ rt_err_t rt_bus_remove_device(rt_device_t dev) } else if (drv) { - if (drv->shutdown) + if (drv->remove) { - err = drv->shutdown(dev); + err = drv->remove(dev); } /* device and driver are in the same bus */ diff --git a/rt-thread/components/drivers/core/device.c b/rt-thread/components/drivers/core/device.c index c99cddf..751b662 100644 --- a/rt-thread/components/drivers/core/device.c +++ b/rt-thread/components/drivers/core/device.c @@ -13,6 +13,8 @@ * 2013-07-09 Grissiom add ref_count support * 2016-04-02 Bernard fix the open_flag initialization issue. * 2021-03-19 Meco Man remove rt_device_init_all() + * 2024-09-15 milo fix log format issue + * fix reopen with a different oflag issue */ #include @@ -29,9 +31,9 @@ #include /* for wqueue_init */ #endif /* RT_USING_POSIX_DEVIO */ -#ifdef RT_USING_DFS_V2 +#if defined (RT_USING_DFS_V2) && defined (RT_USING_DFS_DEVFS) #include -#endif /* RT_USING_DFS_V2 */ +#endif /* RT_USING_DFS_V2 RT_USING_DFS_DEVFS */ #ifdef RT_USING_DEVICE @@ -82,7 +84,7 @@ rt_err_t rt_device_register(rt_device_t dev, rt_wqueue_init(&(dev->wait_queue)); #endif /* RT_USING_POSIX_DEVIO */ -#ifdef RT_USING_DFS_V2 +#if defined (RT_USING_DFS_V2) && defined (RT_USING_DFS_DEVFS) dfs_devfs_device_add(dev); #endif /* RT_USING_DFS_V2 */ @@ -163,7 +165,7 @@ void rt_device_destroy(rt_device_t dev) { /* parameter check */ RT_ASSERT(dev != RT_NULL); - RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Null); RT_ASSERT(rt_object_is_systemobject(&dev->parent) == RT_FALSE); rt_object_detach(&(dev->parent)); @@ -195,8 +197,8 @@ rt_err_t rt_device_init(rt_device_t dev) result = device_init(dev); if (result != RT_EOK) { - LOG_E("To initialize device:%s failed. The error code is %d", - dev->parent.name, result); + LOG_E("To initialize device:%.*s failed. The error code is %d", + RT_NAME_MAX, dev->parent.name, result); } else { @@ -233,8 +235,8 @@ rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag) result = device_init(dev); if (result != RT_EOK) { - LOG_E("To initialize device:%s failed. The error code is %d", - dev->parent.name, result); + LOG_E("To initialize device:%.*s failed. The error code is %d", + RT_NAME_MAX, dev->parent.name, result); return result; } @@ -252,7 +254,7 @@ rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag) /* device is not opened or opened by other oflag, call device_open interface */ if (!(dev->open_flag & RT_DEVICE_OFLAG_OPEN) || - ((dev->open_flag & RT_DEVICE_OFLAG_MASK) != (oflag & RT_DEVICE_OFLAG_MASK))) + ((dev->open_flag & RT_DEVICE_OFLAG_MASK) != ((oflag & RT_DEVICE_OFLAG_MASK) | RT_DEVICE_OFLAG_OPEN))) { if (device_open != RT_NULL) { diff --git a/rt-thread/components/drivers/core/dm.c b/rt-thread/components/drivers/core/dm.c index 8c87cae..9d9f93f 100644 --- a/rt-thread/components/drivers/core/dm.c +++ b/rt-thread/components/drivers/core/dm.c @@ -53,6 +53,137 @@ void rt_dm_secondary_cpu_init(void) } #endif /* RT_USING_SMP */ +/** + * @brief This function will alloc an id in an IDA object + * + * @param ida is the IDA object + * + * @return the id or -RT_EEMPTY + */ +int rt_dm_ida_alloc(struct rt_dm_ida *ida) +{ + int id; + RT_ASSERT(ida != RT_NULL); + + rt_spin_lock(&ida->lock); + + id = rt_bitmap_next_clear_bit(ida->map, 0, RT_DM_IDA_NUM); + + if (id != RT_DM_IDA_NUM) + { + rt_bitmap_set_bit(ida->map, id); + } + + rt_spin_unlock(&ida->lock); + + if (id != RT_DM_IDA_NUM) + { + return id; + } + + return -RT_EEMPTY; +} + +/** + * @brief This function will take (force) an id in an IDA object + * + * @param ida is the IDA object + * + * @param id is the id that want to take + * + * @return the result of taking + */ +rt_bool_t rt_dm_ida_take(struct rt_dm_ida *ida, int id) +{ + RT_ASSERT(ida != RT_NULL); + RT_ASSERT(id >= 0); + + rt_spin_lock(&ida->lock); + + if (!rt_bitmap_test_bit(ida->map, id)) + { + rt_bitmap_set_bit(ida->map, id); + } + else + { + id = RT_DM_IDA_NUM; + } + + rt_spin_unlock(&ida->lock); + + return id != RT_DM_IDA_NUM; +} + +/** + * @brief This function will release an id in an IDA object + * + * @param ida is the IDA object + * + * @param id is the id of IDA object + */ +void rt_dm_ida_free(struct rt_dm_ida *ida, int id) +{ + RT_ASSERT(ida != RT_NULL); + RT_ASSERT(id >= 0); + + rt_spin_lock(&ida->lock); + + rt_bitmap_clear_bit(ida->map, id); + + rt_spin_unlock(&ida->lock); +} + +/** + * @brief This function will return the specified master id and device id of device. + * + * @param master_id is the master id (0, 255] of device + * + * @param device_id is the device id [-1, 255] of device, when device_id is -1, + * the function will end when find the first device. + * + * @return the device object or RT_NULL + */ +rt_device_t rt_dm_device_find(int master_id, int device_id) +{ + struct rt_device *dev, *ret_dev = RT_NULL; + struct rt_object_information *information = RT_NULL; + + if (master_id <= 0 || device_id > 255) + { + return RT_NULL; + } + + information = rt_object_get_information(RT_Object_Class_Device); + + /* parameter check */ + if (!information) + { + return RT_NULL; + } + + /* which is invoke in interrupt status */ + RT_DEBUG_NOT_IN_INTERRUPT; + + /* enter critical */ + rt_enter_critical(); + + /* try to find object */ + rt_list_for_each_entry(dev, &information->object_list, parent.list) + { + if (master_id == dev->master_id && + (device_id == -1 || device_id == dev->device_id)) + { + ret_dev = dev; + break; + } + } + + /* leave critical */ + rt_exit_critical(); + + return ret_dev; +} + struct prefix_track { rt_list_t list; diff --git a/rt-thread/components/drivers/core/platform.c b/rt-thread/components/drivers/core/platform.c index d8c6679..c3a1416 100644 --- a/rt-thread/components/drivers/core/platform.c +++ b/rt-thread/components/drivers/core/platform.c @@ -78,21 +78,21 @@ static rt_bool_t platform_match(rt_driver_t drv, rt_device_t dev) { struct rt_platform_driver *pdrv = rt_container_of(drv, struct rt_platform_driver, parent); struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent); - -#ifdef RT_USING_OFW struct rt_ofw_node *np = dev->ofw_node; /* 1、match with ofw node */ if (np) { + #ifdef RT_USING_OFW pdev->id = rt_ofw_node_match(np, pdrv->ids); - + #else + pdev->id = RT_NULL; + #endif if (pdev->id) { return RT_TRUE; } } -#endif /* 2、match with name */ if (pdev->name && pdrv->name) @@ -123,7 +123,13 @@ static rt_err_t platform_probe(rt_device_t dev) if (err && err != -RT_EEMPTY) { - LOG_E("Attach power domain error = %s in device %s", pdev->name, rt_strerror(err)); + LOG_E("Attach power domain error = %s in device %s", rt_strerror(err), + #ifdef RT_USING_OFW + (pdev->name && pdev->name[0]) ? pdev->name : rt_ofw_node_full_name(np) + #else + pdev->name + #endif + ); return err; } diff --git a/rt-thread/components/drivers/core/platform_ofw.c b/rt-thread/components/drivers/core/platform_ofw.c index f305fde..1b1aa27 100644 --- a/rt-thread/components/drivers/core/platform_ofw.c +++ b/rt-thread/components/drivers/core/platform_ofw.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "../ofw/ofw_internal.h" @@ -161,6 +162,7 @@ static rt_err_t platform_ofw_device_probe_once(struct rt_ofw_node *parent_np) } pdev->dev_id = ofw_alias_node_id(np); + np->dev = &pdev->parent; LOG_D("%s register to bus", np->full_name); rt_platform_device_register(pdev); @@ -199,6 +201,53 @@ rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np) return err; } +rt_err_t rt_platform_ofw_request(struct rt_ofw_node *np) +{ + rt_err_t err; + + if (np) + { + struct rt_device *dev = np->dev; + + if (dev) + { + /* Was create */ + if (dev->drv) + { + /* Was probe OK */ + err = RT_EOK; + } + else + { + err = rt_bus_reload_driver_device(dev->bus, dev); + } + } + else + { + struct rt_platform_device *pdev = alloc_ofw_platform_device(np); + + if (pdev) + { + pdev->dev_id = ofw_alias_node_id(np); + np->dev = &pdev->parent; + LOG_D("%s register to bus", np->full_name); + + err = rt_platform_device_register(pdev); + } + else + { + err = -RT_ENOMEM; + } + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + static int platform_ofw_device_probe(void) { rt_err_t err = RT_EOK; @@ -206,6 +255,8 @@ static int platform_ofw_device_probe(void) if (ofw_node_root) { + rt_ofw_node_get(ofw_node_root); + err = platform_ofw_device_probe_once(ofw_node_root); rt_ofw_node_put(ofw_node_root); @@ -216,11 +267,19 @@ static int platform_ofw_device_probe(void) rt_ofw_node_put(node); } + if ((node = rt_ofw_find_node_by_path("/clocks"))) + { + platform_ofw_device_probe_once(node); + rt_ofw_node_put(node); + } + + rt_ofw_node_get(ofw_node_chosen); if ((node = rt_ofw_get_child_by_compatible(ofw_node_chosen, "simple-framebuffer"))) { platform_ofw_device_probe_once(node); rt_ofw_node_put(node); } + rt_ofw_node_get(ofw_node_chosen); } else { @@ -244,7 +303,7 @@ rt_err_t rt_platform_ofw_free(struct rt_platform_device *pdev) rt_ofw_node_clear_flag(np, RT_OFW_F_PLATFORM); rt_ofw_node_put(np); - pdev->parent.ofw_node = RT_NULL; + rt_free(pdev); } } else diff --git a/rt-thread/components/drivers/dma/Kconfig b/rt-thread/components/drivers/dma/Kconfig new file mode 100644 index 0000000..7bf9d59 --- /dev/null +++ b/rt-thread/components/drivers/dma/Kconfig @@ -0,0 +1,10 @@ +menuconfig RT_USING_DMA + bool "Using Direct Memory Access (DMA)" + depends on RT_USING_DM + select RT_USING_ADT + select RT_USING_ADT_BITMAP + default n + +if RT_USING_DMA + osource "$(SOC_DM_DMA_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/dma/SConscript b/rt-thread/components/drivers/dma/SConscript new file mode 100644 index 0000000..ea3500c --- /dev/null +++ b/rt-thread/components/drivers/dma/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_DMA']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['dma.c', 'dma_pool.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/dma/dma.c b/rt-thread/components/drivers/dma/dma.c new file mode 100644 index 0000000..da2d80a --- /dev/null +++ b/rt-thread/components/drivers/dma/dma.c @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "rtdm.dma" +#define DBG_LVL DBG_INFO +#include + +static rt_list_t dmac_nodes = RT_LIST_OBJECT_INIT(dmac_nodes); +static struct rt_spinlock dmac_nodes_lock = {}; + +rt_err_t rt_dma_controller_register(struct rt_dma_controller *ctrl) +{ + const char *dev_name; + char dma_name[RT_NAME_MAX]; + + if (!ctrl || !ctrl->dev || !ctrl->ops) + { + return -RT_EINVAL; + } + + dev_name = rt_dm_dev_get_name(ctrl->dev); + + if (rt_bitmap_next_set_bit(ctrl->dir_cap, 0, RT_DMA_DIR_MAX) == RT_DMA_DIR_MAX) + { + LOG_E("%s: Not direction capability", dev_name); + + return -RT_EINVAL; + } + + rt_snprintf(dma_name, sizeof(dma_name), "%s-dmac", dev_name); + + rt_list_init(&ctrl->list); + + rt_spin_lock(&dmac_nodes_lock); + rt_list_insert_before(&dmac_nodes, &ctrl->list); + rt_spin_unlock(&dmac_nodes_lock); + + rt_list_init(&ctrl->channels_nodes); + rt_mutex_init(&ctrl->mutex, dma_name, RT_IPC_FLAG_PRIO); + + if (ctrl->dev->ofw_node) + { + rt_dm_dev_bind_fwdata(ctrl->dev, RT_NULL, ctrl); + } + + return RT_EOK; +} + +rt_err_t rt_dma_controller_unregister(struct rt_dma_controller *ctrl) +{ + if (!ctrl) + { + return -RT_EINVAL; + } + + rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER); + + if (!rt_list_isempty(&ctrl->channels_nodes)) + { + rt_mutex_release(&ctrl->mutex); + return -RT_EBUSY; + } + + if (ctrl->dev->ofw_node) + { + rt_dm_dev_unbind_fwdata(ctrl->dev, RT_NULL); + } + + rt_mutex_release(&ctrl->mutex); + rt_mutex_detach(&ctrl->mutex); + + rt_spin_lock(&dmac_nodes_lock); + rt_list_remove(&ctrl->list); + rt_spin_unlock(&dmac_nodes_lock); + + return RT_EOK; +} + +rt_err_t rt_dma_chan_start(struct rt_dma_chan *chan) +{ + rt_err_t err; + struct rt_dma_controller *ctrl; + + if (!chan) + { + return -RT_EINVAL; + } + + if (chan->prep_err) + { + LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave)); + + return chan->prep_err; + } + + ctrl = chan->ctrl; + + rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER); + + err = ctrl->ops->start(chan); + + rt_mutex_release(&ctrl->mutex); + + return err; +} + +rt_err_t rt_dma_chan_stop(struct rt_dma_chan *chan) +{ + rt_err_t err; + struct rt_dma_controller *ctrl; + + if (!chan) + { + return -RT_EINVAL; + } + + if (chan->prep_err) + { + LOG_D("%s: Not prepare done", rt_dm_dev_get_name(chan->slave)); + + return chan->prep_err; + } + + ctrl = chan->ctrl; + + rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER); + + err = ctrl->ops->stop(chan); + + rt_mutex_release(&ctrl->mutex); + + return err; +} + +rt_err_t rt_dma_chan_config(struct rt_dma_chan *chan, + struct rt_dma_slave_config *conf) +{ + rt_err_t err; + struct rt_dma_controller *ctrl; + enum rt_dma_transfer_direction dir; + + if (!chan || !conf) + { + err = -RT_EINVAL; + goto _end; + } + + dir = conf->direction; + + if (dir >= RT_DMA_DIR_MAX) + { + err = -RT_EINVAL; + goto _end; + } + + if (conf->src_addr_width >= RT_DMA_SLAVE_BUSWIDTH_BYTES_MAX || + conf->dst_addr_width >= RT_DMA_SLAVE_BUSWIDTH_BYTES_MAX) + { + err = -RT_EINVAL; + goto _end; + } + + ctrl = chan->ctrl; + + if (!rt_bitmap_test_bit(ctrl->dir_cap, dir)) + { + err = -RT_ENOSYS; + goto _end; + } + + if (!chan->name && dir != RT_DMA_MEM_TO_MEM) + { + LOG_E("%s: illegal config for uname channels", + rt_dm_dev_get_name(ctrl->dev)); + + err = -RT_EINVAL; + goto _end; + } + + rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER); + + err = ctrl->ops->config(chan, conf); + + rt_mutex_release(&ctrl->mutex); + + if (!err) + { + rt_memcpy(&chan->conf, conf, sizeof(*conf)); + } + +_end: + chan->conf_err = err; + + return err; +} + +rt_err_t rt_dma_chan_done(struct rt_dma_chan *chan, rt_size_t size) +{ + if (!chan) + { + return -RT_EINVAL; + } + + if (chan->callback) + { + chan->callback(chan, size); + } + + return RT_EOK; +} + +static rt_bool_t range_is_illegal(const char *name, const char *desc, + rt_ubase_t addr0, rt_ubase_t addr1) +{ + rt_bool_t illegal = addr0 < addr1; + + if (illegal) + { + LOG_E("%s: %s %p is out of config %p", name, desc, addr0, addr1); + } + + return illegal; +} + +rt_err_t rt_dma_prep_memcpy(struct rt_dma_chan *chan, + struct rt_dma_slave_transfer *transfer) +{ + rt_err_t err; + rt_size_t len; + rt_ubase_t dma_addr_src, dma_addr_dst; + struct rt_dma_controller *ctrl; + struct rt_dma_slave_config *conf; + + if (!chan || !transfer) + { + return -RT_EINVAL; + } + + ctrl = chan->ctrl; + conf = &chan->conf; + + if (chan->conf_err) + { + LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave)); + + return chan->conf_err; + } + + RT_ASSERT(chan->conf.direction == RT_DMA_MEM_TO_MEM); + dma_addr_src = transfer->src_addr; + dma_addr_dst = transfer->dst_addr; + len = transfer->buffer_len; + + if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "source", + dma_addr_src, conf->src_addr)) + { + return -RT_EINVAL; + } + + if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "dest", + dma_addr_dst, conf->dst_addr)) + { + return -RT_EINVAL; + } + + if (ctrl->ops->prep_memcpy) + { + rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER); + + err = ctrl->ops->prep_memcpy(chan, dma_addr_dst, dma_addr_src, len); + + rt_mutex_release(&ctrl->mutex); + } + else + { + err = -RT_ENOSYS; + } + + if (!err) + { + rt_memcpy(&chan->transfer, transfer, sizeof(*transfer)); + } + + chan->prep_err = err; + + return err; +} + +rt_err_t rt_dma_prep_cyclic(struct rt_dma_chan *chan, + struct rt_dma_slave_transfer *transfer) +{ + rt_err_t err; + rt_ubase_t dma_buf_addr; + struct rt_dma_controller *ctrl; + struct rt_dma_slave_config *conf; + enum rt_dma_transfer_direction dir; + + if (!chan || !transfer) + { + return -RT_EINVAL; + } + + ctrl = chan->ctrl; + conf = &chan->conf; + + if (chan->conf_err) + { + LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave)); + + return chan->conf_err; + } + + dir = chan->conf.direction; + + if (dir == RT_DMA_MEM_TO_DEV || dir == RT_DMA_MEM_TO_MEM) + { + dma_buf_addr = transfer->src_addr; + + if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "source", + dma_buf_addr, conf->src_addr)) + { + return -RT_EINVAL; + } + } + else if (dir == RT_DMA_DEV_TO_MEM) + { + dma_buf_addr = transfer->dst_addr; + + if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "dest", + dma_buf_addr, conf->dst_addr)) + { + return -RT_EINVAL; + } + } + else + { + dma_buf_addr = ~0UL; + } + + if (ctrl->ops->prep_cyclic) + { + rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER); + + err = ctrl->ops->prep_cyclic(chan, dma_buf_addr, + transfer->buffer_len, transfer->period_len, dir); + + rt_mutex_release(&ctrl->mutex); + } + else + { + err = -RT_ENOSYS; + } + + if (!err) + { + rt_memcpy(&chan->transfer, transfer, sizeof(*transfer)); + } + + chan->prep_err = err; + + return err; +} + +rt_err_t rt_dma_prep_single(struct rt_dma_chan *chan, + struct rt_dma_slave_transfer *transfer) +{ + rt_err_t err; + rt_ubase_t dma_buf_addr; + struct rt_dma_controller *ctrl; + struct rt_dma_slave_config *conf; + enum rt_dma_transfer_direction dir; + + if (!chan || !transfer) + { + return -RT_EINVAL; + } + + ctrl = chan->ctrl; + conf = &chan->conf; + + if (chan->conf_err) + { + LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave)); + + return chan->conf_err; + } + + dir = chan->conf.direction; + + if (dir == RT_DMA_MEM_TO_DEV || dir == RT_DMA_MEM_TO_MEM) + { + dma_buf_addr = transfer->src_addr; + + if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "source", + dma_buf_addr, conf->src_addr)) + { + return -RT_EINVAL; + } + } + else if (dir == RT_DMA_DEV_TO_MEM) + { + dma_buf_addr = transfer->dst_addr; + + if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "dest", + dma_buf_addr, conf->dst_addr)) + { + return -RT_EINVAL; + } + } + else + { + dma_buf_addr = ~0UL; + } + + if (ctrl->ops->prep_single) + { + rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER); + + err = ctrl->ops->prep_single(chan, dma_buf_addr, + transfer->buffer_len, dir); + + rt_mutex_release(&ctrl->mutex); + } + else + { + err = -RT_ENOSYS; + } + + if (!err) + { + rt_memcpy(&chan->transfer, transfer, sizeof(*transfer)); + } + + chan->prep_err = err; + + return err; +} + +static struct rt_dma_controller *ofw_find_dma_controller(struct rt_device *dev, + const char *name, struct rt_ofw_cell_args *args) +{ + struct rt_dma_controller *ctrl = RT_NULL; +#ifdef RT_USING_OFW + int index; + struct rt_ofw_node *np = dev->ofw_node, *ctrl_np; + + if (!np) + { + return RT_NULL; + } + + index = rt_ofw_prop_index_of_string(np, "dma-names", name); + + if (index < 0) + { + return RT_NULL; + } + + if (!rt_ofw_parse_phandle_cells(np, "dmas", "#dma-cells", index, args)) + { + ctrl_np = args->data; + + if (!rt_ofw_data(ctrl_np)) + { + rt_platform_ofw_request(ctrl_np); + } + + ctrl = rt_ofw_data(ctrl_np); + rt_ofw_node_put(ctrl_np); + } +#endif /* RT_USING_OFW */ + return ctrl; +} + +struct rt_dma_chan *rt_dma_chan_request(struct rt_device *dev, const char *name) +{ + void *fw_data = RT_NULL; + struct rt_dma_chan *chan; + struct rt_ofw_cell_args dma_args; + struct rt_dma_controller *ctrl = RT_NULL; + + if (!dev) + { + return rt_err_ptr(-RT_EINVAL); + } + + if (name) + { + fw_data = &dma_args; + ctrl = ofw_find_dma_controller(dev, name, &dma_args); + } + else + { + struct rt_dma_controller *ctrl_tmp; + + rt_spin_lock(&dmac_nodes_lock); + rt_list_for_each_entry(ctrl_tmp, &dmac_nodes, list) + { + /* Only memory to memory for uname request */ + if (rt_bitmap_test_bit(ctrl_tmp->dir_cap, RT_DMA_MEM_TO_MEM)) + { + ctrl = ctrl_tmp; + break; + } + } + rt_spin_unlock(&dmac_nodes_lock); + } + + if (rt_is_err_or_null(ctrl)) + { + return ctrl ? ctrl : rt_err_ptr(-RT_ENOSYS); + } + + if (ctrl->ops->request_chan) + { + chan = ctrl->ops->request_chan(ctrl, dev, fw_data); + } + else + { + chan = rt_calloc(1, sizeof(*chan)); + + if (!chan) + { + chan = rt_err_ptr(-RT_ENOMEM); + } + } + + if (rt_is_err(chan)) + { + return chan; + } + + if (!chan) + { + LOG_E("%s: unset request channels error", rt_dm_dev_get_name(ctrl->dev)); + + return rt_err_ptr(-RT_ERROR); + } + + chan->name = name; + chan->ctrl = ctrl; + chan->slave = dev; + + rt_list_init(&chan->list); + chan->conf_err = -RT_ERROR; + chan->prep_err = -RT_ERROR; + + rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER); + rt_list_insert_before(&ctrl->channels_nodes, &chan->list); + rt_mutex_release(&ctrl->mutex); + + return chan; +} + +rt_err_t rt_dma_chan_release(struct rt_dma_chan *chan) +{ + rt_err_t err = RT_EOK; + + if (!chan) + { + return -RT_EINVAL; + } + + rt_mutex_take(&chan->ctrl->mutex, RT_WAITING_FOREVER); + rt_list_remove(&chan->list); + rt_mutex_release(&chan->ctrl->mutex); + + if (chan->ctrl->ops->release_chan) + { + err = chan->ctrl->ops->release_chan(chan); + } + else + { + rt_free(chan); + } + + return err; +} diff --git a/rt-thread/components/drivers/dma/dma_pool.c b/rt-thread/components/drivers/dma/dma_pool.c new file mode 100644 index 0000000..8bd5291 --- /dev/null +++ b/rt-thread/components/drivers/dma/dma_pool.c @@ -0,0 +1,691 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "dma.pool" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +static struct rt_spinlock dma_pools_lock = {}; +static rt_list_t dma_pool_nodes = RT_LIST_OBJECT_INIT(dma_pool_nodes); + +static struct rt_dma_pool *dma_pool_install(rt_region_t *region); + +static void *dma_alloc(struct rt_device *dev, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags); +static void dma_free(struct rt_device *dev, rt_size_t size, + void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags); + +rt_inline void region_pool_lock(void) +{ + rt_hw_spin_lock(&dma_pools_lock.lock); +} + +rt_inline void region_pool_unlock(void) +{ + rt_hw_spin_unlock(&dma_pools_lock.lock); +} + +static rt_err_t dma_map_coherent_sync_out_data(struct rt_device *dev, + void *data, rt_size_t size, rt_ubase_t *dma_handle, rt_ubase_t flags) +{ + if (dma_handle) + { + *dma_handle = (rt_ubase_t)rt_kmem_v2p(data); + } + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data, size); + + return RT_EOK; +} + +static rt_err_t dma_map_coherent_sync_in_data(struct rt_device *dev, + void *out_data, rt_size_t size, rt_ubase_t dma_handle, rt_ubase_t flags) +{ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, out_data, size); + + return RT_EOK; +} + +static const struct rt_dma_map_ops dma_map_coherent_ops = +{ + .sync_out_data = dma_map_coherent_sync_out_data, + .sync_in_data = dma_map_coherent_sync_in_data, +}; + +static rt_err_t dma_map_nocoherent_sync_out_data(struct rt_device *dev, + void *data, rt_size_t size, rt_ubase_t *dma_handle, rt_ubase_t flags) +{ + if (dma_handle) + { + *dma_handle = (rt_ubase_t)rt_kmem_v2p(data); + } + + return RT_EOK; +} + +static rt_err_t dma_map_nocoherent_sync_in_data(struct rt_device *dev, + void *out_data, rt_size_t size, rt_ubase_t dma_handle, rt_ubase_t flags) +{ + return RT_EOK; +} + +static const struct rt_dma_map_ops dma_map_nocoherent_ops = +{ + .sync_out_data = dma_map_nocoherent_sync_out_data, + .sync_in_data = dma_map_nocoherent_sync_in_data, +}; + +#ifdef RT_USING_OFW +rt_inline rt_ubase_t ofw_addr_cpu2dma(struct rt_device *dev, rt_ubase_t addr) +{ + return (rt_ubase_t)rt_ofw_translate_cpu2dma(dev->ofw_node, addr); +} + +rt_inline rt_ubase_t ofw_addr_dma2cpu(struct rt_device *dev, rt_ubase_t addr) +{ + return (rt_ubase_t)rt_ofw_translate_dma2cpu(dev->ofw_node, addr); +} + +static void *ofw_dma_map_alloc(struct rt_device *dev, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags) +{ + void *cpu_addr = dma_alloc(dev, size, dma_handle, flags); + + if (cpu_addr && dma_handle) + { + *dma_handle = ofw_addr_cpu2dma(dev, *dma_handle); + } + + return cpu_addr; +} + +static void ofw_dma_map_free(struct rt_device *dev, rt_size_t size, + void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags) +{ + dma_handle = ofw_addr_dma2cpu(dev, dma_handle); + + dma_free(dev, size, cpu_addr, dma_handle, flags); +} + +static rt_err_t ofw_dma_map_sync_out_data(struct rt_device *dev, + void *data, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags) +{ + rt_err_t err; + + if (flags & RT_DMA_F_NOCACHE) + { + err = dma_map_nocoherent_sync_out_data(dev, data, size, dma_handle, flags); + } + else + { + err = dma_map_coherent_sync_out_data(dev, data, size, dma_handle, flags); + } + + if (!err && dma_handle) + { + *dma_handle = ofw_addr_cpu2dma(dev, *dma_handle); + } + + return err; +} + +static rt_err_t ofw_dma_map_sync_in_data(struct rt_device *dev, + void *out_data, rt_size_t size, + rt_ubase_t dma_handle, rt_ubase_t flags) +{ + dma_handle = ofw_addr_dma2cpu(dev, dma_handle); + + if (flags & RT_DMA_F_NOCACHE) + { + return dma_map_nocoherent_sync_in_data(dev, out_data, size, dma_handle, flags); + } + + return dma_map_coherent_sync_in_data(dev, out_data, size, dma_handle, flags); +} + +static const struct rt_dma_map_ops ofw_dma_map_ops = +{ + .alloc = ofw_dma_map_alloc, + .free = ofw_dma_map_free, + .sync_out_data = ofw_dma_map_sync_out_data, + .sync_in_data = ofw_dma_map_sync_in_data, +}; + +static const struct rt_dma_map_ops *ofw_device_dma_ops(struct rt_device *dev) +{ + rt_err_t err; + int region_nr = 0; + const fdt32_t *cell; + rt_phandle phandle; + rt_region_t region; + struct rt_ofw_prop *prop; + struct rt_dma_pool *dma_pool; + const struct rt_dma_map_ops *ops = RT_NULL; + struct rt_ofw_node *mem_np, *np = dev->ofw_node; + + rt_ofw_foreach_prop_u32(np, "memory-region", prop, cell, phandle) + { + rt_uint64_t addr, size; + + if (!(mem_np = rt_ofw_find_node_by_phandle(phandle))) + { + if (region_nr == 0) + { + return RT_NULL; + } + + break; + } + + if ((err = rt_ofw_get_address(mem_np, 0, &addr, &size))) + { + LOG_E("%s: Read '%s' error = %s", rt_ofw_node_full_name(mem_np), + "memory-region", rt_strerror(err)); + + rt_ofw_node_put(mem_np); + continue; + } + + region.start = addr; + region.end = addr + size; + region.name = rt_dm_dev_get_name(dev); + + rt_ofw_node_put(mem_np); + + if (!(dma_pool = dma_pool_install(®ion))) + { + return RT_NULL; + } + + if (rt_ofw_prop_read_bool(mem_np, "no-map")) + { + dma_pool->flags |= RT_DMA_F_NOMAP; + } + + if (!rt_dma_device_is_coherent(dev)) + { + dma_pool->flags |= RT_DMA_F_NOCACHE; + } + + dma_pool->dev = dev; + ++region_nr; + } + + if (region_nr) + { + ops = &ofw_dma_map_ops; + } + + return ops; +} +#endif /* RT_USING_OFW */ + +static const struct rt_dma_map_ops *device_dma_ops(struct rt_device *dev) +{ + const struct rt_dma_map_ops *ops = dev->dma_ops; + + if (ops) + { + return ops; + } + +#ifdef RT_USING_OFW + if (dev->ofw_node && (ops = ofw_device_dma_ops(dev))) + { + return ops; + } +#endif + + if (rt_dma_device_is_coherent(dev)) + { + ops = &dma_map_coherent_ops; + } + else + { + ops = &dma_map_nocoherent_ops; + } + + dev->dma_ops = ops; + + return ops; +} + +static rt_ubase_t dma_pool_alloc(struct rt_dma_pool *pool, rt_size_t size) +{ + rt_size_t bit, next_bit, end_bit, max_bits; + + size = RT_DIV_ROUND_UP(size, ARCH_PAGE_SIZE); + max_bits = pool->bits - size; + + rt_bitmap_for_each_clear_bit(pool->map, bit, max_bits) + { + end_bit = bit + size; + + for (next_bit = bit + 1; next_bit < end_bit; ++next_bit) + { + if (rt_bitmap_test_bit(pool->map, next_bit)) + { + bit = next_bit; + goto _next; + } + } + + if (next_bit == end_bit) + { + while (next_bit --> bit) + { + rt_bitmap_set_bit(pool->map, next_bit); + } + + return pool->start + bit * ARCH_PAGE_SIZE; + } + _next: + } + + return RT_NULL; +} + +static void dma_pool_free(struct rt_dma_pool *pool, rt_ubase_t offset, rt_size_t size) +{ + rt_size_t bit = (offset - pool->start) / ARCH_PAGE_SIZE, end_bit; + + size = RT_DIV_ROUND_UP(size, ARCH_PAGE_SIZE); + end_bit = bit + size; + + for (; bit < end_bit; ++bit) + { + rt_bitmap_clear_bit(pool->map, bit); + } +} + +static void *dma_alloc(struct rt_device *dev, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags) +{ + void *dma_buffer = RT_NULL; + struct rt_dma_pool *pool; + + region_pool_lock(); + + rt_list_for_each_entry(pool, &dma_pool_nodes, list) + { + if (pool->flags & RT_DMA_F_DEVICE) + { + if (!(flags & RT_DMA_F_DEVICE) || pool->dev != dev) + { + continue; + } + } + else if ((flags & RT_DMA_F_DEVICE)) + { + continue; + } + + if ((flags & RT_DMA_F_NOMAP) && !((pool->flags & RT_DMA_F_NOMAP))) + { + continue; + } + + if ((flags & RT_DMA_F_32BITS) && !((pool->flags & RT_DMA_F_32BITS))) + { + continue; + } + + if ((flags & RT_DMA_F_LINEAR) && !((pool->flags & RT_DMA_F_LINEAR))) + { + continue; + } + + *dma_handle = dma_pool_alloc(pool, size); + + if (*dma_handle && !(flags & RT_DMA_F_NOMAP)) + { + if (flags & RT_DMA_F_NOCACHE) + { + dma_buffer = rt_ioremap_nocache((void *)*dma_handle, size); + } + else + { + dma_buffer = rt_ioremap_cached((void *)*dma_handle, size); + } + + if (!dma_buffer) + { + dma_pool_free(pool, *dma_handle, size); + + continue; + } + + break; + } + else if (*dma_handle) + { + dma_buffer = (void *)*dma_handle; + + break; + } + } + + region_pool_unlock(); + + return dma_buffer; +} + +static void dma_free(struct rt_device *dev, rt_size_t size, + void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags) +{ + struct rt_dma_pool *pool; + + region_pool_lock(); + + rt_list_for_each_entry(pool, &dma_pool_nodes, list) + { + if (dma_handle >= pool->region.start && + dma_handle <= pool->region.end) + { + rt_iounmap(cpu_addr); + + dma_pool_free(pool, dma_handle, size); + + break; + } + } + + region_pool_unlock(); +} + +void *rt_dma_alloc(struct rt_device *dev, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags) +{ + void *dma_buffer = RT_NULL; + rt_ubase_t dma_handle_s = 0; + const struct rt_dma_map_ops *ops; + + if (!dev || !size) + { + return RT_NULL; + } + + ops = device_dma_ops(dev); + + if (ops->alloc) + { + dma_buffer = ops->alloc(dev, size, &dma_handle_s, flags); + } + else + { + dma_buffer = dma_alloc(dev, size, &dma_handle_s, flags); + } + + if (!dma_buffer) + { + return dma_buffer; + } + + if (dma_handle) + { + *dma_handle = dma_handle_s; + } + + return dma_buffer; +} + +void rt_dma_free(struct rt_device *dev, rt_size_t size, + void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags) +{ + const struct rt_dma_map_ops *ops; + + if (!dev || !size || !cpu_addr) + { + return; + } + + ops = device_dma_ops(dev); + + if (ops->free) + { + ops->free(dev, size, cpu_addr, dma_handle, flags); + } + else + { + dma_free(dev, size, cpu_addr, dma_handle, flags); + } +} + +rt_err_t rt_dma_sync_out_data(struct rt_device *dev, void *data, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags) +{ + rt_err_t err; + rt_ubase_t dma_handle_s = 0; + const struct rt_dma_map_ops *ops; + + if (!data || !size) + { + return -RT_EINVAL; + } + + ops = device_dma_ops(dev); + err = ops->sync_out_data(dev, data, size, &dma_handle_s, flags); + + if (dma_handle) + { + *dma_handle = dma_handle_s; + } + + return err; +} + +rt_err_t rt_dma_sync_in_data(struct rt_device *dev, void *out_data, rt_size_t size, + rt_ubase_t dma_handle, rt_ubase_t flags) +{ + rt_err_t err; + const struct rt_dma_map_ops *ops; + + if (!out_data || !size) + { + return -RT_EINVAL; + } + + ops = device_dma_ops(dev); + err = ops->sync_in_data(dev, out_data, size, dma_handle, flags); + + return err; +} + +static struct rt_dma_pool *dma_pool_install(rt_region_t *region) +{ + rt_err_t err; + struct rt_dma_pool *pool; + + if (!(pool = rt_calloc(1, sizeof(*pool)))) + { + LOG_E("Install pool[%p, %p] error = %s", + region->start, region->end, rt_strerror(-RT_ENOMEM)); + + return RT_NULL; + } + + rt_memcpy(&pool->region, region, sizeof(*region)); + + pool->flags |= RT_DMA_F_LINEAR; + + if (region->end < 4UL * SIZE_GB) + { + pool->flags |= RT_DMA_F_32BITS; + } + + pool->start = RT_ALIGN(pool->region.start, ARCH_PAGE_SIZE); + pool->bits = (pool->region.end - pool->start) / ARCH_PAGE_SIZE; + + if (!pool->bits) + { + err = -RT_EINVAL; + goto _fail; + } + + pool->map = rt_calloc(RT_BITMAP_LEN(pool->bits), sizeof(*pool->map)); + + if (!pool->map) + { + err = -RT_ENOMEM; + goto _fail; + } + + rt_list_init(&pool->list); + + region_pool_lock(); + rt_list_insert_before(&dma_pool_nodes, &pool->list); + region_pool_unlock(); + + return pool; + +_fail: + rt_free(pool); + + LOG_E("Install pool[%p, %p] error = %s", + region->start, region->end, rt_strerror(err)); + + return RT_NULL; +} + +struct rt_dma_pool *rt_dma_pool_install(rt_region_t *region) +{ + struct rt_dma_pool *pool; + + if (!region) + { + return RT_NULL; + } + + if ((pool = dma_pool_install(region))) + { + region = &pool->region; + + LOG_I("%s: Reserved %u.%u MiB at %p", + region->name, + (region->end - region->start) / SIZE_MB, + (region->end - region->start) / SIZE_KB & (SIZE_KB - 1), + region->start); + } + + return pool; +} + +rt_err_t rt_dma_pool_extract(rt_region_t *region_list, rt_size_t list_len, + rt_size_t cma_size, rt_size_t coherent_pool_size) +{ + struct rt_dma_pool *pool; + rt_region_t *region = region_list, *region_high = RT_NULL, cma, coherent_pool; + + if (!region_list || !list_len || cma_size < coherent_pool_size) + { + return -RT_EINVAL; + } + + for (rt_size_t i = 0; i < list_len; ++i, ++region) + { + if (!region->name) + { + continue; + } + + /* Always use low address in 4G */ + if (region->end - region->start >= cma_size) + { + if ((rt_ssize_t)((4UL * SIZE_GB) - region->start) < cma_size) + { + region_high = region; + continue; + } + + goto _found; + } + } + + if (region_high) + { + region = region_high; + LOG_W("No available DMA zone in 4G"); + + goto _found; + } + + return -RT_EEMPTY; + +_found: + if (region->end - region->start != cma_size) + { + cma.start = region->start; + cma.end = cma.start + cma_size; + + /* Update input region */ + region->start += cma_size; + } + else + { + rt_memcpy(&cma, region, sizeof(cma)); + } + + coherent_pool.name = "coherent-pool"; + coherent_pool.start = cma.start; + coherent_pool.end = coherent_pool.start + coherent_pool_size; + + cma.name = "cma"; + cma.start += coherent_pool_size; + + if (!(pool = rt_dma_pool_install(&coherent_pool))) + { + return -RT_ENOMEM; + } + + /* Use: CMA > coherent-pool */ + if (!(pool = rt_dma_pool_install(&cma))) + { + return -RT_ENOMEM; + } + + return RT_EOK; +} + +#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH) +static int list_dma_pool(int argc, char**argv) +{ + int count = 0; + rt_region_t *region; + struct rt_dma_pool *pool; + + rt_kprintf("%-*.s Region\n", RT_NAME_MAX, "Name"); + + region_pool_lock(); + + rt_list_for_each_entry(pool, &dma_pool_nodes, list) + { + region = &pool->region; + + rt_kprintf("%-*.s [%p, %p]\n", RT_NAME_MAX, region->name, + region->start, region->end); + + ++count; + } + + rt_kprintf("%d DMA memory found\n", count); + + region_pool_unlock(); + + return 0; +} +MSH_CMD_EXPORT(list_dma_pool, dump all dma memory pool); +#endif /* RT_USING_CONSOLE && RT_USING_MSH */ diff --git a/rt-thread/components/drivers/hwtimer/hwtimer.c b/rt-thread/components/drivers/hwtimer/hwtimer.c index bcf9ea9..1b27925 100644 --- a/rt-thread/components/drivers/hwtimer/hwtimer.c +++ b/rt-thread/components/drivers/hwtimer/hwtimer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -320,7 +320,14 @@ static rt_err_t rt_hwtimer_control(struct rt_device *dev, int cmd, void *args) break; default: { - result = -RT_ENOSYS; + if (timer->ops->control != RT_NULL) + { + result = timer->ops->control(timer, cmd, args); + } + else + { + result = -RT_ENOSYS; + } } break; } diff --git a/rt-thread/components/drivers/i2c/Kconfig b/rt-thread/components/drivers/i2c/Kconfig index 40f04d1..bb4c90d 100644 --- a/rt-thread/components/drivers/i2c/Kconfig +++ b/rt-thread/components/drivers/i2c/Kconfig @@ -17,203 +17,227 @@ if RT_USING_I2C default n endif - config RT_USING_SOFT_I2C + menuconfig RT_USING_SOFT_I2C bool "Use GPIO to soft simulate I2C" default n select RT_USING_PIN select RT_USING_I2C_BITOPS - if RT_USING_SOFT_I2C - config RT_USING_SOFT_I2C1 - bool "Enable I2C1 Bus (software simulation)" - default y - if RT_USING_SOFT_I2C1 - config RT_SOFT_I2C1_SCL_PIN - int "SCL pin number" - range 0 32767 - default 1 - config RT_SOFT_I2C1_SDA_PIN - int "SDA pin number" - range 0 32767 - default 2 - config RT_SOFT_I2C1_BUS_NAME - string "Bus name" - default "i2c1" - config RT_SOFT_I2C1_TIMING_DELAY - int "Timing delay (us)" - range 0 32767 - default 10 - config RT_SOFT_I2C1_TIMING_TIMEOUT - int "Timing timeout (tick)" - range 0 32767 - default 10 - endif - config RT_USING_SOFT_I2C2 - bool "Enable I2C2 Bus (software simulation)" - default n - if RT_USING_SOFT_I2C2 - config RT_SOFT_I2C2_SCL_PIN - int "SCL pin number" - range 0 32767 - default 3 - config RT_SOFT_I2C2_SDA_PIN - int "SDA pin number" - range 0 32767 - default 4 - config RT_SOFT_I2C2_BUS_NAME - string "Bus name" - default "i2c2" - config RT_SOFT_I2C2_TIMING_DELAY - int "Timing delay (us)" - range 0 32767 - default 10 - config RT_SOFT_I2C2_TIMING_TIMEOUT - int "Timing timeout (tick)" - range 0 32767 - default 10 - endif - config RT_USING_SOFT_I2C3 - bool "Enable I2C3 Bus (software simulation)" - default n - if RT_USING_SOFT_I2C3 - config RT_SOFT_I2C3_SCL_PIN - int "SCL pin number" - range 0 32767 - default 5 - config RT_SOFT_I2C3_SDA_PIN - int "SDA pin number" - range 0 32767 - default 6 - config RT_SOFT_I2C3_BUS_NAME - string "Bus name" - default "i2c3" - config RT_SOFT_I2C3_TIMING_DELAY - int "Timing delay (us)" - range 0 32767 - default 10 - config RT_SOFT_I2C3_TIMING_TIMEOUT - int "Timing timeout (tick)" - range 0 32767 - default 10 - endif - config RT_USING_SOFT_I2C4 - bool "Enable I2C4 Bus (software simulation)" - default n - if RT_USING_SOFT_I2C4 - config RT_SOFT_I2C4_SCL_PIN - int "SCL pin number" - range 0 32767 - default 7 - config RT_SOFT_I2C4_SDA_PIN - int "SDA pin number" - range 0 32767 - default 8 - config RT_SOFT_I2C4_BUS_NAME - string "Bus name" - default "i2c4" - config RT_SOFT_I2C4_TIMING_DELAY - int "Timing delay (us)" - range 0 32767 - default 10 - config RT_SOFT_I2C4_TIMING_TIMEOUT - int "Timing timeout (tick)" - range 0 32767 - default 10 - endif - config RT_USING_SOFT_I2C5 - bool "Enable I2C5 Bus (software simulation)" - default n - if RT_USING_SOFT_I2C5 - config RT_SOFT_I2C5_SCL_PIN - int "SCL pin number" - range 0 32767 - default 9 - config RT_SOFT_I2C5_SDA_PIN - int "SDA pin number" - range 0 32767 - default 10 - config RT_SOFT_I2C5_BUS_NAME - string "Bus name" - default "i2c5" - config RT_SOFT_I2C5_TIMING_DELAY - int "Timing delay (us)" - range 0 32767 - default 10 - config RT_SOFT_I2C5_TIMING_TIMEOUT - int "Timing timeout (tick)" - range 0 32767 - default 10 - endif - config RT_USING_SOFT_I2C6 - bool "Enable I2C6 Bus (software simulation)" - default n - if RT_USING_SOFT_I2C6 - config RT_SOFT_I2C6_SCL_PIN - int "SCL pin number" - range 0 32767 - default 11 - config RT_SOFT_I2C6_SDA_PIN - int "SDA pin number" - range 0 32767 - default 12 - config RT_SOFT_I2C6_BUS_NAME - string "Bus name" - default "i2c6" - config RT_SOFT_I2C6_TIMING_DELAY - int "Timing delay (us)" - range 0 32767 - default 10 - config RT_SOFT_I2C6_TIMING_TIMEOUT - int "Timing timeout (tick)" - range 0 32767 - default 10 - endif - config RT_USING_SOFT_I2C7 - bool "Enable I2C7 Bus (software simulation)" - default n - if RT_USING_SOFT_I2C7 - config RT_SOFT_I2C7_SCL_PIN - int "SCL pin number" - range 0 32767 - default 13 - config RT_SOFT_I2C7_SDA_PIN - int "SDA pin number" - range 0 32767 - default 14 - config RT_SOFT_I2C7_BUS_NAME - string "Bus name" - default "i2c7" - config RT_SOFT_I2C7_TIMING_DELAY - int "Timing delay (us)" - range 0 32767 - default 10 - config RT_SOFT_I2C7_TIMING_TIMEOUT - int "Timing timeout (tick)" - range 0 32767 - default 10 - endif - config RT_USING_SOFT_I2C8 - bool "Enable I2C8 Bus (software simulation)" - default n - if RT_USING_SOFT_I2C8 - config RT_SOFT_I2C8_SCL_PIN - int "SCL pin number" - range 0 32767 - default 15 - config RT_SOFT_I2C8_SDA_PIN - int "SDA pin number" - range 0 32767 - default 16 - config RT_SOFT_I2C8_BUS_NAME - string "Bus name" - default "i2c8" - config RT_SOFT_I2C8_TIMING_DELAY - int "Timing delay (us)" - range 0 32767 - default 10 - config RT_SOFT_I2C8_TIMING_TIMEOUT - int "Timing timeout (tick)" - range 0 32767 - default 10 - endif - endif + if RT_USING_SOFT_I2C + menuconfig RT_USING_SOFT_I2C0 + bool "Enable I2C0 Bus (software simulation)" + default y + if RT_USING_SOFT_I2C0 + config RT_SOFT_I2C0_SCL_PIN + int "SCL pin number" + range 0 32767 + default 1 + config RT_SOFT_I2C0_SDA_PIN + int "SDA pin number" + range 0 32767 + default 2 + config RT_SOFT_I2C0_BUS_NAME + string "Bus name" + default "i2c0" + config RT_SOFT_I2C0_TIMING_DELAY + int "Timing delay (us)" + range 0 32767 + default 10 + config RT_SOFT_I2C0_TIMING_TIMEOUT + int "Timing timeout (tick)" + range 0 32767 + default 10 + endif + menuconfig RT_USING_SOFT_I2C1 + bool "Enable I2C1 Bus (software simulation)" + default y + if RT_USING_SOFT_I2C1 + config RT_SOFT_I2C1_SCL_PIN + int "SCL pin number" + range 0 32767 + default 3 + config RT_SOFT_I2C1_SDA_PIN + int "SDA pin number" + range 0 32767 + default 4 + config RT_SOFT_I2C1_BUS_NAME + string "Bus name" + default "i2c1" + config RT_SOFT_I2C1_TIMING_DELAY + int "Timing delay (us)" + range 0 32767 + default 10 + config RT_SOFT_I2C1_TIMING_TIMEOUT + int "Timing timeout (tick)" + range 0 32767 + default 10 + endif + menuconfig RT_USING_SOFT_I2C2 + bool "Enable I2C2 Bus (software simulation)" + default n + if RT_USING_SOFT_I2C2 + config RT_SOFT_I2C2_SCL_PIN + int "SCL pin number" + range 0 32767 + default 5 + config RT_SOFT_I2C2_SDA_PIN + int "SDA pin number" + range 0 32767 + default 6 + config RT_SOFT_I2C2_BUS_NAME + string "Bus name" + default "i2c2" + config RT_SOFT_I2C2_TIMING_DELAY + int "Timing delay (us)" + range 0 32767 + default 10 + config RT_SOFT_I2C2_TIMING_TIMEOUT + int "Timing timeout (tick)" + range 0 32767 + default 10 + endif + menuconfig RT_USING_SOFT_I2C3 + bool "Enable I2C3 Bus (software simulation)" + default n + if RT_USING_SOFT_I2C3 + config RT_SOFT_I2C3_SCL_PIN + int "SCL pin number" + range 0 32767 + default 7 + config RT_SOFT_I2C3_SDA_PIN + int "SDA pin number" + range 0 32767 + default 8 + config RT_SOFT_I2C3_BUS_NAME + string "Bus name" + default "i2c3" + config RT_SOFT_I2C3_TIMING_DELAY + int "Timing delay (us)" + range 0 32767 + default 10 + config RT_SOFT_I2C3_TIMING_TIMEOUT + int "Timing timeout (tick)" + range 0 32767 + default 10 + endif + menuconfig RT_USING_SOFT_I2C4 + bool "Enable I2C4 Bus (software simulation)" + default n + if RT_USING_SOFT_I2C4 + config RT_SOFT_I2C4_SCL_PIN + int "SCL pin number" + range 0 32767 + default 9 + config RT_SOFT_I2C4_SDA_PIN + int "SDA pin number" + range 0 32767 + default 10 + config RT_SOFT_I2C4_BUS_NAME + string "Bus name" + default "i2c4" + config RT_SOFT_I2C4_TIMING_DELAY + int "Timing delay (us)" + range 0 32767 + default 10 + config RT_SOFT_I2C4_TIMING_TIMEOUT + int "Timing timeout (tick)" + range 0 32767 + default 10 + endif + menuconfig RT_USING_SOFT_I2C5 + bool "Enable I2C5 Bus (software simulation)" + default n + if RT_USING_SOFT_I2C5 + config RT_SOFT_I2C5_SCL_PIN + int "SCL pin number" + range 0 32767 + default 11 + config RT_SOFT_I2C5_SDA_PIN + int "SDA pin number" + range 0 32767 + default 12 + config RT_SOFT_I2C5_BUS_NAME + string "Bus name" + default "i2c5" + config RT_SOFT_I2C5_TIMING_DELAY + int "Timing delay (us)" + range 0 32767 + default 10 + config RT_SOFT_I2C5_TIMING_TIMEOUT + int "Timing timeout (tick)" + range 0 32767 + default 10 + endif + menuconfig RT_USING_SOFT_I2C6 + bool "Enable I2C6 Bus (software simulation)" + default n + if RT_USING_SOFT_I2C6 + config RT_SOFT_I2C6_SCL_PIN + int "SCL pin number" + range 0 32767 + default 13 + config RT_SOFT_I2C6_SDA_PIN + int "SDA pin number" + range 0 32767 + default 14 + config RT_SOFT_I2C6_BUS_NAME + string "Bus name" + default "i2c6" + config RT_SOFT_I2C6_TIMING_DELAY + int "Timing delay (us)" + range 0 32767 + default 10 + config RT_SOFT_I2C6_TIMING_TIMEOUT + int "Timing timeout (tick)" + range 0 32767 + default 10 + endif + menuconfig RT_USING_SOFT_I2C7 + bool "Enable I2C7 Bus (software simulation)" + default n + if RT_USING_SOFT_I2C7 + config RT_SOFT_I2C7_SCL_PIN + int "SCL pin number" + range 0 32767 + default 15 + config RT_SOFT_I2C7_SDA_PIN + int "SDA pin number" + range 0 32767 + default 16 + config RT_SOFT_I2C7_BUS_NAME + string "Bus name" + default "i2c7" + config RT_SOFT_I2C7_TIMING_DELAY + int "Timing delay (us)" + range 0 32767 + default 10 + config RT_SOFT_I2C7_TIMING_TIMEOUT + int "Timing timeout (tick)" + range 0 32767 + default 10 + endif + menuconfig RT_USING_SOFT_I2C8 + bool "Enable I2C8 Bus (software simulation)" + default n + if RT_USING_SOFT_I2C8 + config RT_SOFT_I2C8_SCL_PIN + int "SCL pin number" + range 0 32767 + default 17 + config RT_SOFT_I2C8_SDA_PIN + int "SDA pin number" + range 0 32767 + default 18 + config RT_SOFT_I2C8_BUS_NAME + string "Bus name" + default "i2c8" + config RT_SOFT_I2C8_TIMING_DELAY + int "Timing delay (us)" + range 0 32767 + default 10 + config RT_SOFT_I2C8_TIMING_TIMEOUT + int "Timing timeout (tick)" + range 0 32767 + default 10 + endif + endif endif diff --git a/rt-thread/components/drivers/i2c/SConscript b/rt-thread/components/drivers/i2c/SConscript index 3685e44..269c6f5 100644 --- a/rt-thread/components/drivers/i2c/SConscript +++ b/rt-thread/components/drivers/i2c/SConscript @@ -3,16 +3,16 @@ from building import * cwd = GetCurrentDir() src = Split(""" -i2c_core.c -i2c_dev.c +dev_i2c_core.c +dev_i2c_dev.c """) if GetDepend('RT_USING_I2C_BITOPS'): - src = src + ['i2c-bit-ops.c'] + src = src + ['dev_i2c_bit_ops.c'] if GetDepend('RT_USING_SOFT_I2C'): - src = src + ['soft_i2c.c'] + src = src + ['dev_soft_i2c.c'] if GetDepend(['RT_USING_DM']): - src += ['i2c_bus.c', 'i2c_dm.c'] + src += ['dev_i2c_bus.c', 'dev_i2c_dm.c'] # The set of source files associated with this SConscript file. path = [cwd + '/../include'] diff --git a/rt-thread/components/drivers/i2c/i2c-bit-ops.c b/rt-thread/components/drivers/i2c/dev_i2c_bit_ops.c similarity index 100% rename from rt-thread/components/drivers/i2c/i2c-bit-ops.c rename to rt-thread/components/drivers/i2c/dev_i2c_bit_ops.c diff --git a/rt-thread/components/drivers/i2c/i2c_bus.c b/rt-thread/components/drivers/i2c/dev_i2c_bus.c similarity index 99% rename from rt-thread/components/drivers/i2c/i2c_bus.c rename to rt-thread/components/drivers/i2c/dev_i2c_bus.c index e9626ca..8a52471 100644 --- a/rt-thread/components/drivers/i2c/i2c_bus.c +++ b/rt-thread/components/drivers/i2c/dev_i2c_bus.c @@ -10,7 +10,7 @@ #include -#define DBG_TAG "i2c.bus" +#define DBG_TAG "dev.i2c.bus" #define DBG_LVL DBG_INFO #include diff --git a/rt-thread/components/drivers/i2c/i2c_core.c b/rt-thread/components/drivers/i2c/dev_i2c_core.c similarity index 98% rename from rt-thread/components/drivers/i2c/i2c_core.c rename to rt-thread/components/drivers/i2c/dev_i2c_core.c index 2b8e58c..9d9bf6b 100644 --- a/rt-thread/components/drivers/i2c/i2c_core.c +++ b/rt-thread/components/drivers/i2c/dev_i2c_core.c @@ -30,7 +30,7 @@ rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus, res = rt_i2c_bus_device_device_init(bus, bus_name); - LOG_I("I2C bus [%s] registered", bus_name); + LOG_D("I2C bus [%s] registered", bus_name); #ifdef RT_USING_DM if (!res) diff --git a/rt-thread/components/drivers/i2c/i2c_dev.c b/rt-thread/components/drivers/i2c/dev_i2c_dev.c similarity index 100% rename from rt-thread/components/drivers/i2c/i2c_dev.c rename to rt-thread/components/drivers/i2c/dev_i2c_dev.c diff --git a/rt-thread/components/drivers/i2c/i2c_dm.c b/rt-thread/components/drivers/i2c/dev_i2c_dm.c similarity index 98% rename from rt-thread/components/drivers/i2c/i2c_dm.c rename to rt-thread/components/drivers/i2c/dev_i2c_dm.c index 95b1fe6..5701dd5 100644 --- a/rt-thread/components/drivers/i2c/i2c_dm.c +++ b/rt-thread/components/drivers/i2c/dev_i2c_dm.c @@ -10,7 +10,7 @@ #include -#define DBG_TAG "i2c.dm" +#define DBG_TAG "dev.i2c.dm" #define DBG_LVL DBG_INFO #include diff --git a/rt-thread/components/drivers/i2c/soft_i2c.c b/rt-thread/components/drivers/i2c/dev_soft_i2c.c similarity index 94% rename from rt-thread/components/drivers/i2c/soft_i2c.c rename to rt-thread/components/drivers/i2c/dev_soft_i2c.c index 54c7395..a9c661e 100644 --- a/rt-thread/components/drivers/i2c/soft_i2c.c +++ b/rt-thread/components/drivers/i2c/dev_soft_i2c.c @@ -11,7 +11,8 @@ #include #ifdef RT_USING_SOFT_I2C -#if !defined(RT_USING_SOFT_I2C1) && !defined(RT_USING_SOFT_I2C2) &&\ +#if !defined(RT_USING_SOFT_I2C0) &&\ + !defined(RT_USING_SOFT_I2C1) && !defined(RT_USING_SOFT_I2C2) &&\ !defined(RT_USING_SOFT_I2C3) && !defined(RT_USING_SOFT_I2C4) &&\ !defined(RT_USING_SOFT_I2C5) && !defined(RT_USING_SOFT_I2C6) &&\ !defined(RT_USING_SOFT_I2C7) && !defined(RT_USING_SOFT_I2C8) @@ -48,6 +49,15 @@ struct rt_soft_i2c struct soft_i2c_config i2c_cfg[] = { + #ifdef RT_USING_SOFT_I2C0 + { + .scl_pin = RT_SOFT_I2C0_SCL_PIN, + .sda_pin = RT_SOFT_I2C0_SDA_PIN, + .bus_name = RT_SOFT_I2C0_BUS_NAME, + .timing_delay = RT_SOFT_I2C0_TIMING_DELAY, + .timing_timeout = RT_SOFT_I2C0_TIMING_TIMEOUT, + }, + #endif //RT_USING_SOFT_I2C0 #ifdef RT_USING_SOFT_I2C1 { .scl_pin = RT_SOFT_I2C1_SCL_PIN, diff --git a/rt-thread/components/drivers/iio/SConscript b/rt-thread/components/drivers/iio/SConscript new file mode 100644 index 0000000..577dab2 --- /dev/null +++ b/rt-thread/components/drivers/iio/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_DM']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['iio.c',] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/iio/iio.c b/rt-thread/components/drivers/iio/iio.c new file mode 100644 index 0000000..548308d --- /dev/null +++ b/rt-thread/components/drivers/iio/iio.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include + +static void *ofw_iio_channel_get_by_index(struct rt_ofw_node *np, int index, int *out_channel) +{ + void *iio = RT_NULL; +#ifdef RT_USING_OFW + struct rt_ofw_node *iio_np; + struct rt_ofw_cell_args iio_args; + + if (!rt_ofw_parse_phandle_cells(np, "io-channels", "#io-channel-cells", index, &iio_args)) + { + iio_np = iio_args.data; + + if (!rt_ofw_data(iio_np)) + { + rt_platform_ofw_request(iio_np); + } + + iio = rt_ofw_data(iio_np); + rt_ofw_node_put(iio_np); + + if (out_channel) + { + *out_channel = iio_args.args[0]; + } + } +#endif /* RT_USING_OFW */ + return iio; +} + +void *rt_iio_channel_get_by_index(struct rt_device *dev, int index, int *out_channel) +{ + void *iio = RT_NULL; + + if (!dev || index < 0) + { + return RT_NULL; + } + + if (dev->ofw_node) + { + iio = ofw_iio_channel_get_by_index(dev->ofw_node, index, out_channel); + } + + return iio; +} + +void *rt_iio_channel_get_by_name(struct rt_device *dev, const char *name, int *out_channel) +{ + int index; + + if (!dev || !name) + { + return RT_NULL; + } + + index = rt_dm_dev_prop_index_of_string(dev, "io-channel-names", name); + + return rt_iio_channel_get_by_index(dev, index, out_channel); +} diff --git a/rt-thread/components/drivers/include/drivers/adc.h b/rt-thread/components/drivers/include/drivers/adc.h index 7c35f1e..7189ae5 100644 --- a/rt-thread/components/drivers/include/drivers/adc.h +++ b/rt-thread/components/drivers/include/drivers/adc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -14,12 +14,65 @@ #define __ADC_H__ #include +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup ADC ADC + * + * @brief ADC driver api + * + * Example + * @code {.c} + * #define ADC_DEV_NAME "adc1" + * #define ADC_DEV_CHANNEL 5 + * #define REFER_VOLTAGE 330 + * #define CONVERT_BITS (1 << 12) + * + * static int adc_vol_sample(int argc, char *argv[]) + * { + * rt_adc_device_t adc_dev; + * rt_uint32_t value, vol; + * + * rt_err_t ret = RT_EOK; + * + * adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME); + * if (adc_dev == RT_NULL) + * { + * rt_kprintf("adc sample run failed! can't find %s device!\n", ADC_DEV_NAME); + * return -RT_ERROR; + * } + * + * ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL); + * + * value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL); + * rt_kprintf("the value is :%d \n", value); + * + * vol = value * REFER_VOLTAGE / CONVERT_BITS; + * rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100); + * + * ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL); + * + * return ret; + * } + * MSH_CMD_EXPORT(adc_vol_sample, adc voltage convert sample); + * + * @endcode + * + * @ingroup Drivers + */ + +/*! + * @addtogroup ADC + * @{ + */ #define RT_ADC_INTERN_CH_TEMPER (-1) #define RT_ADC_INTERN_CH_VREF (-2) #define RT_ADC_INTERN_CH_VBAT (-3) struct rt_adc_device; +/** + * @brief Configure the adc device + */ struct rt_adc_ops { rt_err_t (*enabled)(struct rt_adc_device *device, rt_int8_t channel, rt_bool_t enabled); @@ -27,7 +80,9 @@ struct rt_adc_ops rt_uint8_t (*get_resolution)(struct rt_adc_device *device); rt_int16_t (*get_vref) (struct rt_adc_device *device); }; - +/** + * @brief adc device + */ struct rt_adc_device { struct rt_device parent; @@ -43,10 +98,53 @@ typedef enum RT_ADC_CMD_GET_VREF = RT_DEVICE_CTRL_BASE(ADC) + 4, /* get reference voltage */ } rt_adc_cmd_t; +/** + * @brief register the adc device + * @param adc adc device + * @param name device name + * @param ops device ops + * @param user_data device private data + * @return rt_err_t error code + * @ingroup ADC + */ rt_err_t rt_hw_adc_register(rt_adc_device_t adc,const char *name, const struct rt_adc_ops *ops, const void *user_data); + +/** + * @brief read the adc value + * @param dev adc device + * @param channel adc channel + * @return rt_uint32_t adc value + * @ingroup ADC + */ rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_int8_t channel); + +/** + * @brief enable the adc channel + * @param dev adc device + * @param channel adc channel + * @return rt_err_t error code + * @ingroup ADC + */ rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_int8_t channel); + +/** + * @brief disable the adc channel + * @param dev adc device + * @param channel adc channel + * @return rt_err_t error code + * @ingroup ADC + */ rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_int8_t channel); + +/** + * @brief get the adc resolution + * @param dev adc device + * @param channel adc channel + * @return rt_int16_t adc resolution + * @ingroup ADC + */ rt_int16_t rt_adc_voltage(rt_adc_device_t dev, rt_int8_t channel); +/*! @}*/ + #endif /* __ADC_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/ahci.h b/rt-thread/components/drivers/include/drivers/ahci.h new file mode 100644 index 0000000..45e47a8 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/ahci.h @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __AHCI_H__ +#define __AHCI_H__ + +#include +#include +#include +#include + +struct rt_ahci_ops; + +/* Generic Host Control */ +#define RT_AHCI_HBA_CAP 0x00 /* Host capability*/ +#define RT_AHCI_CAP_NP RT_GENMASK(4, 0) /* Number of Ports */ +#define RT_AHCI_CAP_NCS RT_GENMASK(8, 12) /* Number of Command Slots */ +#define RT_AHCI_CAP_PSC RT_BIT(13) /* Partial State Capable */ +#define RT_AHCI_CAP_SSC RT_BIT(14) /* Slumber capable */ +#define RT_AHCI_CAP_PMD RT_BIT(15) /* PIO Multiple DRQ Block */ +#define RT_AHCI_CAP_SPM RT_BIT(17) /* Port Multiplier */ +#define RT_AHCI_CAP_AHCI RT_BIT(18) /* AHCI only */ +#define RT_AHCI_CAP_SNZO RT_BIT(19) /* Non-Zero DMA Offsets */ +#define RT_AHCI_CAP_ISS RT_GENMASK(23, 20) /* Interface Speed Support */ +#define RT_AHCI_CAP_CLO RT_BIT(24) /* Command List Override support */ +#define RT_AHCI_CAP_SAL RT_BIT(25) /* Activity LED */ +#define RT_AHCI_CAP_SALP RT_BIT(26) /* Aggressive Link Power Management */ +#define RT_AHCI_CAP_SSS RT_BIT(27) /* Staggered Spin-up */ +#define RT_AHCI_CAP_SIS RT_BIT(28) /* Interlock Switch */ +#define RT_AHCI_CAP_NCQ RT_BIT(30) /* Native Command Queueing */ +#define RT_AHCI_CAP_64 RT_BIT(31) /* PCI DAC (64-bit DMA) support */ +#define RT_AHCI_HBA_GHC 0x04 /* Global host control */ +#define RT_AHCI_GHC_RESET RT_BIT(0) /* Reset controller; self-clear */ +#define RT_AHCI_GHC_IRQ_EN RT_BIT(1) /* Global IRQ enable */ +#define RT_AHCI_GHC_AHCI_EN RT_BIT(31) /* AHCI enabled */ +#define RT_AHCI_HBA_INTS 0x08 /* Interrupt status */ +#define RT_AHCI_HBA_PI 0x0c /* Port implemented */ +#define RT_AHCI_HBA_VS 0x10 /* Version */ +#define RT_AHCI_HBA_CCC_CTL 0x14 /* Command completion coalescing control */ +#define RT_AHCI_HBA_CCC_PTS 0x18 /* Command completion coalescing ports */ +#define RT_AHCI_HBA_EM_LOC 0x1c /* Enclosure management location */ +#define RT_AHCI_HBA_EM_CTL 0x20 /* Enclosure management control */ +#define RT_AHCI_HBA_CAP2 0x24 /* Host capabilities extended */ +#define RT_AHCI_HBA_BOHC 0x28 /* BIOS/OS handoff control and status */ +#define RT_AHCI_HBA_VENDOR 0xa0 /* Vendor specific registers (0xa0 - 0xff) */ + +#define RT_AHCI_PORT_CLB 0x00 /* Command list base address, 1K-byte aligned */ +#define RT_AHCI_PORT_CLBU 0x04 /* Command list base address upper 32 bits */ +#define RT_AHCI_PORT_FB 0x08 /* FIS base address, 256-byte aligned */ +#define RT_AHCI_PORT_FBU 0x0C /* FIS base address upper 32 bits */ +#define RT_AHCI_PORT_INTS 0x10 /* Interrupt status */ +#define RT_AHCI_PORT_INTE 0x14 /* Interrupt enable */ +#define RT_AHCI_PORT_INTE_D2H_REG_FIS RT_BIT(0) /* D2H Register FIS rx'd */ +#define RT_AHCI_PORT_INTE_PIOS_FIS RT_BIT(1) /* PIO Setup FIS rx'd */ +#define RT_AHCI_PORT_INTE_DMAS_FIS RT_BIT(2) /* DMA Setup FIS rx'd */ +#define RT_AHCI_PORT_INTE_SDB_FIS RT_BIT(3) /* Set Device Bits FIS rx'd */ +#define RT_AHCI_PORT_INTE_UNK_FIS RT_BIT(4) /* Unknown FIS rx'd */ +#define RT_AHCI_PORT_INTE_SG_DONE RT_BIT(5) /* Descriptor processed */ +#define RT_AHCI_PORT_INTE_CONNECT RT_BIT(6) /* Port connect change status */ +#define RT_AHCI_PORT_INTE_DMPS RT_BIT(7) /* Mechanical presence status */ +#define RT_AHCI_PORT_INTE_PHYRDY RT_BIT(22) /* PhyRdy changed */ +#define RT_AHCI_PORT_INTE_BAD_PMP RT_BIT(23) /* Incorrect port multiplier */ +#define RT_AHCI_PORT_INTE_OVERFLOW RT_BIT(24) /* Xfer exhausted available S/G */ +#define RT_AHCI_PORT_INTE_IF_NONFATAL RT_BIT(26) /* Interface non-fatal error */ +#define RT_AHCI_PORT_INTE_IF_ERR RT_BIT(27) /* Interface fatal error */ +#define RT_AHCI_PORT_INTE_HBUS_DATA_ERR RT_BIT(28) /* Host bus data error */ +#define RT_AHCI_PORT_INTE_HBUS_ERR RT_BIT(29) /* Host bus fatal error */ +#define RT_AHCI_PORT_INTE_TF_ERR RT_BIT(30) /* Task file error */ +#define RT_AHCI_PORT_INTE_COLD_PRES RT_BIT(31) /* Cold presence detect */ +#define RT_AHCI_PORT_CMD 0x18 /* Command and status */ +#define RT_AHCI_PORT_CMD_START RT_BIT(0) /* Enable port DMA engine */ +#define RT_AHCI_PORT_CMD_SPIN_UP RT_BIT(1) /* Spin up device */ +#define RT_AHCI_PORT_CMD_POWER_ON RT_BIT(2) /* Power up device */ +#define RT_AHCI_PORT_CMD_CLO RT_BIT(3) /* Command list override */ +#define RT_AHCI_PORT_CMD_FIS_RX RT_BIT(4) /* Enable FIS receive DMA engine */ +#define RT_AHCI_PORT_CMD_FIS_ON RT_BIT(14) /* FIS DMA engine running */ +#define RT_AHCI_PORT_CMD_LIST_ON RT_BIT(15) /* cmd list DMA engine running */ +#define RT_AHCI_PORT_CMD_ATAPI RT_BIT(24) /* Device is ATAPI */ +#define RT_AHCI_PORT_CMD_ACTIVE RT_BIT(28) /* Active state */ +#define RT_AHCI_PORT_TFD 0x20 /* Task file data */ +#define RT_AHCI_PORT_TFDATA_ERR RT_BIT(0) /* Indicates an error during the transfer */ +#define RT_AHCI_PORT_TFDATA_DRQ RT_BIT(3) /* Indicates a data transfer is requested */ +#define RT_AHCI_PORT_TFDATA_BSY RT_BIT(7) /* Indicates the interface is busy */ +#define RT_AHCI_PORT_SIG 0x24 /* Signature */ +#define RT_AHCI_PORT_SIG_REG_MASK 0xff +#define RT_AHCI_PORT_SIG_SECTOR_NR_SHIFT 0 /* Sector Count Register */ +#define RT_AHCI_PORT_SIG_LBA_LOW_SHIFT 8 /* LBA Low Register */ +#define RT_AHCI_PORT_SIG_LBA_MID_SHIFT 16 /* LBA Mid Register */ +#define RT_AHCI_PORT_SIG_LBA_HIGH_SHIFT 24 /* LBA High Register */ +#define RT_AHCI_PORT_SIG_SATA_CDROM 0xeb140101 +#define RT_AHCI_PORT_SIG_SATA_DISK 0x00000101 +#define RT_AHCI_PORT_SSTS 0x28 /* SATA status (SCR0:SStatus) */ +#define RT_AHCI_PORT_SSTS_DET_MASK 0x3 +#define RT_AHCI_PORT_SSTS_DET_COMINIT 0x1 +#define RT_AHCI_PORT_SSTS_DET_PHYRDY 0x3 +#define RT_AHCI_PORT_SCTL 0x2c /* SATA control (SCR2:SControl) */ +#define RT_AHCI_PORT_SERR 0x30 /* SATA error (SCR1:SError) */ +#define RT_AHCI_PORT_SERR_ERR_I RT_BIT(0) /* Recovered Data Integrity Error */ +#define RT_AHCI_PORT_SERR_ERR_M RT_BIT(1) /* Recovered Communications Error */ +#define RT_AHCI_PORT_SERR_ERR_T RT_BIT(8) /* Transient Data Integrity Error */ +#define RT_AHCI_PORT_SERR_ERR_C RT_BIT(9) /* Persistent Communication or Data Integrity Error */ +#define RT_AHCI_PORT_SERR_ERR_P RT_BIT(10) /* Protocol Error */ +#define RT_AHCI_PORT_SERR_ERR_E RT_BIT(11) /* Internal Error */ +#define RT_AHCI_PORT_SERR_DIAG_N RT_BIT(16) /* PhyRdy Change */ +#define RT_AHCI_PORT_SERR_DIAG_I RT_BIT(17) /* Phy Internal Error */ +#define RT_AHCI_PORT_SERR_DIAG_W RT_BIT(18) /* Comm Wake */ +#define RT_AHCI_PORT_SERR_DIAG_B RT_BIT(19) /* 10B to 8B Decode Error */ +#define RT_AHCI_PORT_SERR_DIAG_D RT_BIT(20) /* Disparity Error */ +#define RT_AHCI_PORT_SERR_DIAG_C RT_BIT(21) /* CRC Error */ +#define RT_AHCI_PORT_SERR_DIAG_H RT_BIT(22) /* Handshake Error */ +#define RT_AHCI_PORT_SERR_DIAG_S RT_BIT(23) /* Link Sequence Error */ +#define RT_AHCI_PORT_SERR_DIAG_T RT_BIT(24) /* Transport state transition error */ +#define RT_AHCI_PORT_SERR_DIAG_F RT_BIT(25) /* Unknown FIS Type */ +#define RT_AHCI_PORT_SERR_DIAG_X RT_BIT(26) /* Exchanged */ +#define RT_AHCI_PORT_SACT 0x34 /* SATA active (SCR3:SActive) */ +#define RT_AHCI_PORT_CI 0x38 /* Command issue */ +#define RT_AHCI_PORT_SNTF 0x3c /* SATA notification (SCR4:SNotification) */ +#define RT_AHCI_PORT_FBS 0x40 /* FIS-based switch control */ +#define RT_AHCI_PORT_VENDOR 0x70 /* Vendor specific (0x70 - 0x7f) */ + +#define RT_AHCI_MAX_SG 56 +#define RT_AHCI_CMD_SLOT_SIZE 32 +#define RT_AHCI_MAX_CMD_SLOT 32 +#define RT_AHCI_RX_FIS_SIZE 256 +#define RT_AHCI_CMD_TBL_HDR 0x80 +#define RT_AHCI_CMD_TBL_CDB 0x40 +#define RT_AHCI_CMD_TBL_SIZE RT_AHCI_CMD_TBL_HDR + (RT_AHCI_MAX_SG * 16) +#define RT_AHCI_DMA_SIZE (RT_AHCI_CMD_SLOT_SIZE * RT_AHCI_MAX_CMD_SLOT + RT_AHCI_CMD_TBL_SIZE + RT_AHCI_RX_FIS_SIZE) +#define RT_ACHI_PRDT_BYTES_MAX (4 * 1024 * 1024) + +#define RT_AHCI_FIS_TYPE_REG_H2D 0x27 /* Register FIS - host to device */ +#define RT_AHCI_FIS_TYPE_REG_D2H 0x34 /* Register FIS - device to host */ +#define RT_AHCI_FIS_TYPE_DMA_ACT 0x39 /* DMA activate FIS - device to host */ +#define RT_AHCI_FIS_TYPE_DMA_SETUP 0x41 /* DMA setup FIS - bidirectional */ +#define RT_AHCI_FIS_TYPE_DATA 0x46 /* Data FIS - bidirectional */ +#define RT_AHCI_FIS_TYPE_BIST 0x58 /* BIST activate FIS - bidirectional */ +#define RT_AHCI_FIS_TYPE_PIO_SETUP 0x5f /* PIO setup FIS - device to host */ +#define RT_AHCI_FIS_TYPE_DEV_BITS 0xa1 /* Set device bits FIS - device to host */ + +#define RT_AHCI_ATA_ID_WORDS 256 +#define RT_AHCI_ATA_ID_CONFIG 0 +#define RT_AHCI_ATA_ID_CYLS 1 +#define RT_AHCI_ATA_ID_HEADS 3 +#define RT_AHCI_ATA_ID_SECTORS 6 +#define RT_AHCI_ATA_ID_SERNO 10 +#define RT_AHCI_ATA_ID_BUF_SIZE 21 +#define RT_AHCI_ATA_ID_FW_REV 23 +#define RT_AHCI_ATA_ID_PROD 27 +#define RT_AHCI_ATA_ID_MAX_MULTSECT 47 +#define RT_AHCI_ATA_ID_DWORD_IO 48 +#define RT_AHCI_ATA_ID_TRUSTED 48 +#define RT_AHCI_ATA_ID_CAPABILITY 49 +#define RT_AHCI_ATA_ID_OLD_PIO_MODES 51 +#define RT_AHCI_ATA_ID_OLD_DMA_MODES 52 +#define RT_AHCI_ATA_ID_FIELD_VALID 53 +#define RT_AHCI_ATA_ID_CUR_CYLS 54 +#define RT_AHCI_ATA_ID_CUR_HEADS 55 +#define RT_AHCI_ATA_ID_CUR_SECTORS 56 +#define RT_AHCI_ATA_ID_MULTSECT 59 +#define RT_AHCI_ATA_ID_LBA_CAPACITY 60 +#define RT_AHCI_ATA_ID_SWDMA_MODES 62 +#define RT_AHCI_ATA_ID_MWDMA_MODES 63 +#define RT_AHCI_ATA_ID_PIO_MODES 64 +#define RT_AHCI_ATA_ID_EIDE_DMA_MIN 65 +#define RT_AHCI_ATA_ID_EIDE_DMA_TIME 66 +#define RT_AHCI_ATA_ID_EIDE_PIO 67 +#define RT_AHCI_ATA_ID_EIDE_PIO_IORDY 68 +#define RT_AHCI_ATA_ID_ADDITIONAL_SUPP 69 +#define RT_AHCI_ATA_ID_QUEUE_DEPTH 75 +#define RT_AHCI_ATA_ID_SATA_CAPABILITY 76 +#define RT_AHCI_ATA_ID_SATA_CAPABILITY_2 77 +#define RT_AHCI_ATA_ID_FEATURE_SUPP 78 +#define RT_AHCI_ATA_ID_MAJOR_VER 80 +#define RT_AHCI_ATA_ID_COMMAND_SET_1 82 +#define RT_AHCI_ATA_ID_COMMAND_SET_2 83 +#define RT_AHCI_ATA_ID_CFSSE 84 +#define RT_AHCI_ATA_ID_CFS_ENABLE_1 85 +#define RT_AHCI_ATA_ID_CFS_ENABLE_2 86 +#define RT_AHCI_ATA_ID_CSF_DEFAULT 87 +#define RT_AHCI_ATA_ID_UDMA_MODES 88 +#define RT_AHCI_ATA_ID_HW_CONFIG 93 +#define RT_AHCI_ATA_ID_SPG 98 +#define RT_AHCI_ATA_ID_LBA_CAPACITY_2 100 +#define RT_AHCI_ATA_ID_SECTOR_SIZE 106 +#define RT_AHCI_ATA_ID_WWN 108 +#define RT_AHCI_ATA_ID_LOGICAL_SECTOR_SIZE 117 +#define RT_AHCI_ATA_ID_COMMAND_SET_3 119 +#define RT_AHCI_ATA_ID_COMMAND_SET_4 120 +#define RT_AHCI_ATA_ID_LAST_LUN 126 +#define RT_AHCI_ATA_ID_DLF 128 +#define RT_AHCI_ATA_ID_CSFO 129 +#define RT_AHCI_ATA_ID_CFA_POWER 160 +#define RT_AHCI_ATA_ID_CFA_KEY_MGMT 162 +#define RT_AHCI_ATA_ID_CFA_MODES 163 +#define RT_AHCI_ATA_ID_DATA_SET_MGMT 169 +#define RT_AHCI_ATA_ID_SCT_CMD_XPORT 206 +#define RT_AHCI_ATA_ID_ROT_SPEED 217 +#define RT_AHCI_ATA_ID_PIO4 (1 << 1) +#define RT_AHCI_ATA_ID_SERNO_LEN 20 +#define RT_AHCI_ATA_ID_FW_REV_LEN 8 +#define RT_AHCI_ATA_ID_PROD_LEN 40 +#define RT_AHCI_ATA_ID_WWN_LEN 8 + +#define RT_AHCI_ATA_CMD_DSM 0x06 +#define RT_AHCI_ATA_CMD_DEV_RESET 0x08 /* ATAPI device reset */ +#define RT_AHCI_ATA_CMD_PIO_READ 0x20 /* Read sectors with retry */ +#define RT_AHCI_ATA_CMD_PIO_READ_EXT 0x24 +#define RT_AHCI_ATA_CMD_READ_EXT 0x25 +#define RT_AHCI_ATA_CMD_READ_NATIVE_MAX_EXT 0x27 +#define RT_AHCI_ATA_CMD_READ_MULTI_EXT 0x29 +#define RT_AHCI_ATA_CMD_READ_LOG_EXT 0x2f +#define RT_AHCI_ATA_CMD_PIO_WRITE 0x30 /* Write sectors with retry */ +#define RT_AHCI_ATA_CMD_PIO_WRITE_EXT 0x34 +#define RT_AHCI_ATA_CMD_WRITE_EXT 0x35 +#define RT_AHCI_ATA_CMD_SET_MAX_EXT 0x37 +#define RT_AHCI_ATA_CMD_WRITE_MULTI_EXT 0x39 +#define RT_AHCI_ATA_CMD_WRITE_FUA_EXT 0x3d +#define RT_AHCI_ATA_CMD_VERIFY 0x40 /* Read verify sectors with retry */ +#define RT_AHCI_ATA_CMD_VERIFY_EXT 0x42 +#define RT_AHCI_ATA_CMD_FPDMA_READ 0x60 +#define RT_AHCI_ATA_CMD_FPDMA_WRITE 0x61 +#define RT_AHCI_ATA_CMD_EDD 0x90 /* Execute device diagnostic */ +#define RT_AHCI_ATA_CMD_INIT_DEV_PARAMS 0x91 /* Initialize device parameters */ +#define RT_AHCI_ATA_CMD_PACKET 0xa0 /* ATAPI packet */ +#define RT_AHCI_ATA_CMD_ID_ATAPI 0xa1 /* ATAPI identify device */ +#define RT_AHCI_ATA_CMD_CONF_OVERLAY 0xb1 +#define RT_AHCI_ATA_CMD_READ_MULTI 0xc4 /* Read multiple */ +#define RT_AHCI_ATA_CMD_WRITE_MULTI 0xc5 /* Write multiple */ +#define RT_AHCI_ATA_CMD_SET_MULTI 0xc6 /* Set multiple mode */ +#define RT_AHCI_ATA_CMD_READ 0xc8 /* Read DMA with retry */ +#define RT_AHCI_ATA_CMD_WRITE 0xca /* Write DMA with retry */ +#define RT_AHCI_ATA_CMD_WRITE_MULTI_FUA_EXT 0xce +#define RT_AHCI_ATA_CMD_STANDBYNOW1 0xe0 /* Standby immediate */ +#define RT_AHCI_ATA_CMD_IDLEIMMEDIATE 0xe1 /* Idle immediate */ +#define RT_AHCI_ATA_CMD_STANDBY 0xe2 /* Place in standby power mode */ +#define RT_AHCI_ATA_CMD_IDLE 0xe3 /* Place in idle power mode */ +#define RT_AHCI_ATA_CMD_PMP_READ 0xe4 /* Read buffer */ +#define RT_AHCI_ATA_CMD_CHK_POWER 0xe5 /* Check power mode */ +#define RT_AHCI_ATA_CMD_SLEEP 0xe6 /* Sleep */ +#define RT_AHCI_ATA_CMD_FLUSH 0xe7 +#define RT_AHCI_ATA_CMD_PMP_WRITE 0xe8 /* Write buffer */ +#define RT_AHCI_ATA_CMD_FLUSH_EXT 0xea +#define RT_AHCI_ATA_CMD_ID_ATA 0xec /* Identify device */ +#define RT_AHCI_ATA_CMD_SET_FEATURES 0xef /* Set features */ +#define RT_AHCI_ATA_CMD_SEC_FREEZE_LOCK 0xf5 /* Security freeze */ +#define RT_AHCI_ATA_CMD_READ_NATIVE_MAX 0xf8 +#define RT_AHCI_ATA_CMD_SET_MAX 0xf9 + +#define RT_AHCI_ATA_DSM_TRIM 0x01 + +#define RT_AHCI_ATA_PROT_FLAG_PIO RT_BIT(0) +#define RT_AHCI_ATA_PROT_FLAG_DMA RT_BIT(1) +#define RT_AHCI_ATA_PROT_FLAG_NCQ RT_BIT(2) +#define RT_AHCI_ATA_PROT_FLAG_ATAPI RT_BIT(3) + +#define rt_ahci_ata_id_is_ata(id) (((id)[0] & (1 << 15)) == 0) +#define rt_ahci_ata_id_has_lba(id) ((id)[49] & (1 << 9)) +#define rt_ahci_ata_id_has_dma(id) ((id)[49] & (1 << 8)) +#define rt_ahci_ata_id_has_ncq(id) ((id)[76] & (1 << 8)) +#define rt_ahci_ata_id_queue_depth(id) (((id)[75] & 0x1f) + 1) +#define rt_ahci_ata_id_removeable(id) ((id)[0] & (1 << 7)) +#define rt_ahci_ata_id_iordy_disable(id) ((id)[49] & (1 << 10)) +#define rt_ahci_ata_id_has_iordy(id) ((id)[49] & (1 << 11)) + +#define rt_ahci_ata_id_u32(id, n) (((rt_uint32_t)(id)[(n) + 1] << 16) | ((rt_uint32_t) (id)[(n)])) +#define rt_ahci_ata_id_u64(id, n) (((rt_uint64_t)(id)[(n) + 3] << 48) | ((rt_uint64_t)(id)[(n) + 2] << 32) | \ + ((rt_uint64_t)(id)[(n) + 1] << 16) | ((rt_uint64_t)(id)[(n) + 0]) ) + +rt_inline rt_bool_t rt_ahci_ata_id_has_lba48(const rt_uint16_t *id) +{ + if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000 || + !rt_ahci_ata_id_u64(id, RT_AHCI_ATA_ID_LBA_CAPACITY_2)) + { + return 0; + } + + return !!(id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 10)); +} + +rt_inline rt_uint64_t rt_ahci_ata_id_n_sectors(rt_uint16_t *id) +{ + if (rt_ahci_ata_id_has_lba(id)) + { + if (rt_ahci_ata_id_has_lba48(id)) + { + return rt_ahci_ata_id_u64(id, RT_AHCI_ATA_ID_LBA_CAPACITY_2); + } + + return rt_ahci_ata_id_u32(id, RT_AHCI_ATA_ID_LBA_CAPACITY); + } + + return 0; +} + +rt_inline rt_bool_t rt_ahci_ata_id_wcache_enabled(const rt_uint16_t *id) +{ + if ((id[RT_AHCI_ATA_ID_CSF_DEFAULT] & 0xc000) != 0x4000) + { + return RT_FALSE; + } + return id[RT_AHCI_ATA_ID_CFS_ENABLE_1] & (1 << 5); +} + +rt_inline rt_bool_t rt_ahci_ata_id_has_flush(const rt_uint16_t *id) +{ + if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000) + { + return RT_FALSE; + } + return id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 12); +} + +rt_inline rt_bool_t rt_ahci_ata_id_has_flush_ext(const rt_uint16_t *id) +{ + if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000) + { + return RT_FALSE; + } + return id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 13); +} + +struct rt_ahci_cmd_hdr +{ + rt_uint32_t opts; + rt_uint32_t status; + rt_uint32_t tbl_addr_lo; + rt_uint32_t tbl_addr_hi; + rt_uint32_t reserved[4]; +}; + +struct rt_ahci_sg +{ + rt_uint32_t addr_lo; + rt_uint32_t addr_hi; + rt_uint32_t reserved; + rt_uint32_t flags_size; +}; + +struct rt_ahci_port +{ + void *regs; + + void *dma; + rt_ubase_t dma_handle; + + struct rt_ahci_cmd_hdr *cmd_slot; + struct rt_ahci_sg *cmd_tbl_sg; + void *cmd_tbl; + rt_ubase_t cmd_tbl_dma; + void *rx_fis; + + rt_uint32_t int_enabled; + rt_size_t block_size; + + rt_uint16_t *ataid; + + rt_bool_t link; + struct rt_completion done; +}; + +struct rt_ahci_host +{ + struct rt_scsi_host parent; + + int irq; + void *regs; + + rt_size_t ports_nr; + rt_uint32_t ports_map; + struct rt_ahci_port ports[32]; + + rt_uint32_t cap; + rt_uint32_t max_blocks; + + const struct rt_ahci_ops *ops; +}; + +struct rt_ahci_ops +{ + rt_err_t (*host_init)(struct rt_ahci_host *host); + rt_err_t (*port_init)(struct rt_ahci_host *host, struct rt_ahci_port *port); + rt_err_t (*port_link_up)(struct rt_ahci_host *host, struct rt_ahci_port *port); + rt_err_t (*port_dma_init)(struct rt_ahci_host *host, struct rt_ahci_port *port); + rt_err_t (*port_isr)(struct rt_ahci_host *host, struct rt_ahci_port *port, rt_uint32_t isr); +}; + +rt_err_t rt_ahci_host_register(struct rt_ahci_host *host); +rt_err_t rt_ahci_host_unregister(struct rt_ahci_host *host); + +#endif /* __AHCI_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/blk.h b/rt-thread/components/drivers/include/drivers/blk.h new file mode 100644 index 0000000..42159c4 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/blk.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI first version + */ + +#ifndef __BLK_H__ +#define __BLK_H__ + +#include +#include +#include + +struct rt_dm_ida; +struct rt_blk_device; +struct rt_blk_disk_ops; + +struct rt_blk_disk +{ + struct rt_device parent; + + const struct rt_blk_disk_ops *ops; +#ifdef RT_USING_DM + struct rt_dm_ida *ida; +#endif + + rt_uint32_t read_only:1; + rt_uint32_t parallel_io:1; + rt_uint32_t removable:1; +#define RT_BLK_DISK_MAGIC 0xbdaabdaa + rt_uint32_t __magic; + + rt_uint32_t partitions; +#define RT_BLK_PARTITION_NONE (-1) +#define RT_BLK_PARTITION_MAX (RT_UINT32_MAX >> 1) + rt_int32_t max_partitions; + rt_list_t part_nodes; + + struct rt_spinlock lock; + struct rt_semaphore usr_lock; +}; + +struct rt_blk_disk_ops +{ + rt_ssize_t (*read)(struct rt_blk_disk *disk, rt_off_t sector, void *buffer, + rt_size_t sector_count); + rt_ssize_t (*write)(struct rt_blk_disk *disk, rt_off_t sector, const void *buffer, + rt_size_t sector_count); + rt_err_t (*getgeome)(struct rt_blk_disk *disk, struct rt_device_blk_geometry *geometry); + rt_err_t (*sync)(struct rt_blk_disk *disk); + rt_err_t (*erase)(struct rt_blk_disk *disk); + rt_err_t (*autorefresh)(struct rt_blk_disk *disk, rt_bool_t is_auto); + rt_err_t (*control)(struct rt_blk_disk *disk, struct rt_blk_device *blk, int cmd, void *args); +}; + +#ifndef __DFS_H__ +#include + +struct rt_blk_device +{ + struct rt_device parent; + + int partno; + struct dfs_partition partition; + + rt_list_t list; + struct rt_blk_disk *disk; + + rt_size_t sector_start; + rt_size_t sector_count; +}; +#else +struct rt_blk_device; +#endif /* __DFS_H__ */ + +rt_err_t rt_hw_blk_disk_register(struct rt_blk_disk *disk); +rt_err_t rt_hw_blk_disk_unregister(struct rt_blk_disk *disk); + +rt_err_t rt_blk_disk_probe_partition(struct rt_blk_disk *disk); +rt_ssize_t rt_blk_disk_get_capacity(struct rt_blk_disk *disk); +rt_ssize_t rt_blk_disk_get_logical_block_size(struct rt_blk_disk *disk); + +#endif /* __BLK_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/clk.h b/rt-thread/components/drivers/include/drivers/clk.h index 83662d7..97491bf 100644 --- a/rt-thread/components/drivers/include/drivers/clk.h +++ b/rt-thread/components/drivers/include/drivers/clk.h @@ -16,6 +16,8 @@ #include #include +#define RT_CLK_NODE_OBJ_NAME "CLKNP" + struct rt_clk_ops; struct rt_reset_control_node; @@ -37,6 +39,8 @@ struct rt_clk_node * }; * We assume the 'N' is the max value of element in 'clock-indices' if OFW. */ + struct rt_object rt_parent; + rt_list_t list; rt_list_t children_nodes; @@ -74,6 +78,8 @@ struct rt_clk const char *con_id; rt_ubase_t rate; + int prepare_count; + int enable_count; void *fw_node; void *priv; diff --git a/rt-thread/components/drivers/include/drivers/core/dm.h b/rt-thread/components/drivers/include/drivers/core/dm.h index 94821fe..1a3723a 100644 --- a/rt-thread/components/drivers/include/drivers/core/dm.h +++ b/rt-thread/components/drivers/include/drivers/core/dm.h @@ -13,9 +13,11 @@ #include #include +#include #include #include #include +#include #ifndef RT_CPUS_NR #define RT_CPUS_NR 1 @@ -27,6 +29,24 @@ extern int rt_hw_cpu_id(void); void rt_dm_secondary_cpu_init(void); +/* ID Allocation */ +struct rt_dm_ida +{ + rt_uint8_t master_id; + +#define RT_DM_IDA_NUM 256 + RT_BITMAP_DECLARE(map, RT_DM_IDA_NUM); + struct rt_spinlock lock; +}; + +#define RT_DM_IDA_INIT(id) { .master_id = MASTER_ID_##id } + +int rt_dm_ida_alloc(struct rt_dm_ida *ida); +rt_bool_t rt_dm_ida_take(struct rt_dm_ida *ida, int id); +void rt_dm_ida_free(struct rt_dm_ida *ida, int id); + +rt_device_t rt_dm_device_find(int master_id, int device_id); + int rt_dm_dev_set_name_auto(rt_device_t dev, const char *prefix); int rt_dm_dev_get_name_id(rt_device_t dev); diff --git a/rt-thread/components/drivers/include/drivers/core/master_id.h b/rt-thread/components/drivers/include/drivers/core/master_id.h new file mode 100644 index 0000000..90f2699 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/core/master_id.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-04-20 GuEe-GUI the first version + */ + +#ifndef __RT_DM_MASTER_ID_H__ +#define __RT_DM_MASTER_ID_H__ + +#define MASTER_ID_CUSTOM 0 + +/* Block */ +#define MASTER_ID_NVME 1 +#define MASTER_ID_SCSI_SD 2 +#define MASTER_ID_SCSI_CDROM 3 +#define MASTER_ID_SDIO 4 +#define MASTER_ID_VIRTUAL_BLOCK 5 + +/* Char */ +#define MASTER_ID_RPMSG_EPT 11 +#define MASTER_ID_RPMSG_CHAR 12 +#define MASTER_ID_SERIAL 13 + +/* Clock Timer */ +#define MASTER_ID_HWTIMER 21 +#define MASTER_ID_PTP 22 +#define MASTER_ID_RTC 23 + +/* Graphic Display */ +#define MASTER_ID_GRAPHIC_BACKLIGHT 31 +#define MASTER_ID_GRAPHIC_FRAMEBUFFER 32 +#define MASTER_ID_LED 33 + +/* Hardware Monitor */ +#define MASTER_ID_DVFS 41 +#define MASTER_ID_SENSOR 42 +#define MASTER_ID_THERMAL 43 +#define MASTER_ID_WATCHDOG 44 + +/* I2C */ +#define MASTER_ID_I2C_BUS 51 +#define MASTER_ID_I2C_DEV 52 + +/* IO Contorl */ +#define MASTER_ID_ADC 61 +#define MASTER_ID_DAC 62 +#define MASTER_ID_PIN 63 +#define MASTER_ID_PWM 64 + +/* Memory */ +#define MASTER_ID_MEM 71 +#define MASTER_ID_MTD 72 + +/* MISC */ +#define MASTER_ID_MISC 81 + +/* Multimedia */ +#define MASTER_ID_AUDIO 91 + +/* Net */ +#define MASTER_ID_CAN 101 +#define MASTER_ID_ETH 102 +#define MASTER_ID_PHY 103 +#define MASTER_ID_WLAN 104 + +/* Input */ +#define MASTER_ID_INPUT 111 +#define MASTER_ID_TOUCH 112 + +/* Security */ +#define MASTER_ID_HWCRYPTO 121 +#define MASTER_ID_RNG 122 +#define MASTER_ID_TEE 123 + +/* SPI */ +#define MASTER_ID_SPI_BUS 131 +#define MASTER_ID_SPI_DEV 132 + +/* TTY */ +#define MASTER_ID_TTY 141 +#define MASTER_ID_TTY_SLAVES 142 +#define MASTER_ID_TTY_ALTERNATE 143 +#define MASTER_ID_PTMX 144 + +/* USB */ +#define MASTER_ID_USB_DEV 151 +#define MASTER_ID_USB_BUS 152 +#define MASTER_ID_USB_OTG 153 + +#endif /* __RT_DM_MASTER_ID_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/cputime.h b/rt-thread/components/drivers/include/drivers/cputime.h index 3b1eb2a..478ccfd 100644 --- a/rt-thread/components/drivers/include/drivers/cputime.h +++ b/rt-thread/components/drivers/include/drivers/cputime.h @@ -31,4 +31,8 @@ uint64_t clock_cpu_millisecond(uint64_t cpu_tick); int clock_cpu_setops(const struct rt_clock_cputime_ops *ops); +#ifdef RT_USING_CPUTIME_RISCV +int riscv_cputime_init(void); +#endif /* RT_USING_CPUTIME_RISCV */ + #endif diff --git a/rt-thread/components/drivers/include/drivers/dac.h b/rt-thread/components/drivers/include/drivers/dac.h index c03fa10..b1360c3 100644 --- a/rt-thread/components/drivers/include/drivers/dac.h +++ b/rt-thread/components/drivers/include/drivers/dac.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -11,8 +11,66 @@ #ifndef __DAC_H__ #define __DAC_H__ #include +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup DAC DAC + * + * @brief DAC driver api + * + * Example + * @code {.c} + * + * #include + * #include + * #include + * #define DAC_DEV_NAME "dac1" + * #define DAC_DEV_CHANNEL 1 + * #define REFER_VOLTAGE 330 + * #define CONVERT_BITS (1 << 12) + * + * static int dac_vol_sample(int argc, char *argv[]) + * { + * rt_dac_device_t dac_dev; + * rt_uint32_t value, vol; + * rt_err_t ret = RT_EOK; + * + * dac_dev = (rt_dac_device_t)rt_device_find(DAC_DEV_NAME); + * if (dac_dev == RT_NULL) + * { + * rt_kprintf("dac sample run failed! can't find %s device!\n", DAC_DEV_NAME); + * return -RT_ERROR; + * } + * + * ret = rt_dac_enable(dac_dev, DAC_DEV_CHANNEL); + * + * value = atoi(argv[1]); + * rt_dac_write(dac_dev, DAC_DEV_NAME, DAC_DEV_CHANNEL, value); + * rt_kprintf("the value is :%d \n", value); + * + * vol = value * REFER_VOLTAGE / CONVERT_BITS; + * rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100); + * + * rt_thread_mdelay(500); + * + * ret = rt_dac_disable(dac_dev, DAC_DEV_CHANNEL); + * + * return ret; + * } + * MSH_CMD_EXPORT(dac_vol_sample, dac voltage convert sample); + * + * @endcode + * + * @ingroup Drivers + */ +/*! + * @addtogroup DAC + * @{ + */ struct rt_dac_device; +/** + * @brief Configuration of DAC device + */ struct rt_dac_ops { rt_err_t (*disabled)(struct rt_dac_device *device, rt_uint32_t channel); @@ -20,7 +78,10 @@ struct rt_dac_ops rt_err_t (*convert)(struct rt_dac_device *device, rt_uint32_t channel, rt_uint32_t *value); rt_uint8_t (*get_resolution)(struct rt_dac_device *device); }; - +/** + * @brief DAC device structure + * + */ struct rt_dac_device { struct rt_device parent; @@ -35,10 +96,41 @@ typedef enum RT_DAC_CMD_GET_RESOLUTION = RT_DEVICE_CTRL_BASE(DAC) + 2, } rt_dac_cmd_t; +/** + * @brief Register a DAC device + * @param dac DAC device + * @param name DAC name + * @param ops the operations of DAC device + * @param user_data device private data + * @return rt_err_t error code + */ rt_err_t rt_hw_dac_register(rt_dac_device_t dac,const char *name, const struct rt_dac_ops *ops, const void *user_data); +/** + * @brief set the value of DAC + * @param dev DAC device + * @param channel DAC channel + * @param value the value of DAC + * @return rt_err_t error code + */ rt_err_t rt_dac_write(rt_dac_device_t dev, rt_uint32_t channel, rt_uint32_t value); + +/** + * @brief enable the DAC channel + * @param dev DAC device + * @param channel DAC channel + * @return rt_err_t error code + */ rt_err_t rt_dac_enable(rt_dac_device_t dev, rt_uint32_t channel); + +/** + * @brief disable the DAC channel + * @param dev DAC device + * @param channel DAC channel + * @return rt_err_t error code + */ rt_err_t rt_dac_disable(rt_dac_device_t dev, rt_uint32_t channel); +/*! @}*/ + #endif /* __dac_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/alarm.h b/rt-thread/components/drivers/include/drivers/dev_alarm.h similarity index 96% rename from rt-thread/components/drivers/include/drivers/alarm.h rename to rt-thread/components/drivers/include/drivers/dev_alarm.h index 864933d..4553569 100644 --- a/rt-thread/components/drivers/include/drivers/alarm.h +++ b/rt-thread/components/drivers/include/drivers/dev_alarm.h @@ -10,8 +10,8 @@ * 2020-10-15 zhangsz add alarm flags hour minute second. */ -#ifndef __ALARM_H__ -#define __ALARM_H__ +#ifndef __DEV_ALARM_H__ +#define __DEV_ALARM_H__ #include #include @@ -72,4 +72,4 @@ rt_err_t rt_alarm_start(rt_alarm_t alarm); rt_err_t rt_alarm_stop(rt_alarm_t alarm); int rt_alarm_system_init(void); -#endif /* __ALARM_H__ */ +#endif /* __DEV_ALARM_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/audio.h b/rt-thread/components/drivers/include/drivers/dev_audio.h similarity index 98% rename from rt-thread/components/drivers/include/drivers/audio.h rename to rt-thread/components/drivers/include/drivers/dev_audio.h index 6d98272..ef92a93 100644 --- a/rt-thread/components/drivers/include/drivers/audio.h +++ b/rt-thread/components/drivers/include/drivers/dev_audio.h @@ -10,10 +10,10 @@ * */ -#ifndef __AUDIO_H__ -#define __AUDIO_H__ +#ifndef __DEV_AUDIO_H__ +#define __DEV_AUDIO_H__ -#include "audio_pipe.h" +#include "dev_audio_pipe.h" /* AUDIO command */ #define _AUDIO_CTL(a) (RT_DEVICE_CTRL_BASE(Sound) + a) @@ -173,4 +173,4 @@ void rt_audio_rx_done(struct rt_audio_device *audio, rt_uint8_t *pbuf, rt #define CODEC_VOLUME_MAX (63) -#endif /* __AUDIO_H__ */ +#endif /* __DEV_AUDIO_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/can.h b/rt-thread/components/drivers/include/drivers/dev_can.h similarity index 65% rename from rt-thread/components/drivers/include/drivers/can.h rename to rt-thread/components/drivers/include/drivers/dev_can.h index 2ce60d8..aed78b2 100644 --- a/rt-thread/components/drivers/include/drivers/can.h +++ b/rt-thread/components/drivers/include/drivers/dev_can.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -10,8 +10,8 @@ * 2022-05-08 hpmicro add CANFD support, fixed typos */ -#ifndef CAN_H_ -#define CAN_H_ +#ifndef __DEV_CAN_H_ +#define __DEV_CAN_H_ #include @@ -63,12 +63,157 @@ enum CANBAUD #define RT_CAN_MODE_PRIV 0x01 #define RT_CAN_MODE_NOPRIV 0x00 -/** @defgroup CAN_receive_FIFO_number CAN Receive FIFO Number - * @{ - */ +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup CAN_Device CAN Driver + * + * @brief CAN driver api + * + * Example + * @code {.c} + * #include + * #include "rtdevice.h" + * + * #define CAN_DEV_NAME "can1" // CAN 设备名称 + * + * static struct rt_semaphore rx_sem; // 用于接收消息的信号量 + * static rt_device_t can_dev; // CAN 设备句柄 + * + * // 接收数据回调函数 + * static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size) + * { + * // CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 + * rt_sem_release(&rx_sem); + * + * return RT_EOK; + * } + * + * static void can_rx_thread(void *parameter) + * { + * int i; + * rt_err_t res; + * struct rt_can_msg rxmsg = {0}; + * + * // 设置接收回调函数 + * rt_device_set_rx_indicate(can_dev, can_rx_call); + * + * #ifdef RT_CAN_USING_HDR + * struct rt_can_filter_item items[5] = + * { + * RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 0, 0x700, RT_NULL, RT_NULL), // std,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 + * RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 0, 0x700, RT_NULL, RT_NULL), // std,match ID:0x300~0x3ff,hdr 为 - 1 + * RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 0, 0x7ff, RT_NULL, RT_NULL), // std,match ID:0x211,hdr 为 - 1 + * RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), // std,match ID:0x486,hdr 为 - 1 + * {0x555, 0, 0, 0, 0x7ff, 7,} // std,match ID:0x555,hdr 为 7,指定设置 7 号过滤表 + * }; + * struct rt_can_filter_config cfg = {5, 1, items}; // 一共有 5 个过滤表 + * // 设置硬件过滤表 + * res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg); + * RT_ASSERT(res == RT_EOK); + * #endif + * res = RT_TRUE; + * res = rt_device_control(can_dev, RT_CAN_CMD_START, &res); + * while (1) + * { + * // hdr 值为 - 1,表示直接从 uselist 链表读取数据 + * rxmsg.hdr = -1; + * // 阻塞等待接收信号量 + * rt_sem_take(&rx_sem, RT_WAITING_FOREVER); + * // 从 CAN 读取一帧数据 + * rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg)); + * // 打印数据 ID 及内容 + * rt_kprintf("ID:%x", rxmsg.id); + * for (i = 0; i < 8; i++) + * { + * rt_kprintf("%2x", rxmsg.data[i]); + * } + * + * rt_kprintf("\n"); + * } + * } + * + * int can_sample(int argc, char *argv[]) + * { + * struct rt_can_msg msg = {0}; + * rt_err_t res; + * rt_size_t size; + * rt_thread_t thread; + * char can_name[RT_NAME_MAX]; + * + * if (argc == 2) + * { + * rt_strncpy(can_name, argv[1], RT_NAME_MAX); + * } + * else + * { + * rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX); + * } + * // 查找 CAN 设备 + * can_dev = rt_device_find(can_name); + * if (!can_dev) + * { + * rt_kprintf("find %s failed!\n", can_name); + * return -RT_ERROR; + * } + * + * // 初始化 CAN 接收信号量 + * rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO); + * + * // 以中断接收及发送方式打开 CAN 设备 + * res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX); + * RT_ASSERT(res == RT_EOK); + * // 创建数据接收线程 + * thread = rt_thread_create("can_rx", can_rx_thread, RT_NULL, 1024, 25, 10); + * if (thread != RT_NULL) + * { + * rt_thread_startup(thread); + * } + * else + * { + * rt_kprintf("create can_rx thread failed!\n"); + * } + * + * msg.id = 0x78; // ID 为 0x78 + * msg.ide = RT_CAN_STDID; // 标准格式 + * msg.rtr = RT_CAN_DTR; // 数据帧 + * msg.len = 8; // 数据长度为 8 + * // 待发送的 8 字节数据 + * msg.data[0] = 0x00; + * msg.data[1] = 0x11; + * msg.data[2] = 0x22; + * msg.data[3] = 0x33; + * msg.data[4] = 0x44; + * msg.data[5] = 0x55; + * msg.data[6] = 0x66; + * msg.data[7] = 0x77; + * // 发送一帧 CAN 数据 + * size = rt_device_write(can_dev, 0, &msg, sizeof(msg)); + * if (size == 0) + * { + * rt_kprintf("can dev write data failed!\n"); + * } + * + * return res; + * } + * // 导出到 msh 命令列表中 + * MSH_CMD_EXPORT(can_sample, can device sample); + * @endcode + * + * @ingroup Drivers + * + */ + + +/*! + * @addtogroup CAN_Device + * @{ + */ #define CAN_RX_FIFO0 (0x00000000U) /*!< CAN receive FIFO 0 */ #define CAN_RX_FIFO1 (0x00000001U) /*!< CAN receive FIFO 1 */ +/** + * @brief CAN filter item + */ struct rt_can_filter_item { rt_uint32_t id : 29; @@ -118,6 +263,10 @@ struct rt_can_filter_item RT_CAN_FILTER_ITEM_INIT(id,1,0,1,0xFFFFFFFF) #endif + +/** + * @brief CAN filter configuration + */ struct rt_can_filter_config { rt_uint32_t count; @@ -125,6 +274,9 @@ struct rt_can_filter_config struct rt_can_filter_item *items; }; +/** + * @brief CAN timing configuration + */ struct rt_can_bit_timing { rt_uint16_t prescaler; /* Pre-scaler */ @@ -135,8 +287,8 @@ struct rt_can_bit_timing }; /** - * CAN bit timing configuration list - * NOTE: + * @brief CAN bit timing configuration list + * @note * items[0] always for CAN2.0/CANFD Arbitration Phase * items[1] always for CANFD (if it exists) */ @@ -146,6 +298,10 @@ struct rt_can_bit_timing_config struct rt_can_bit_timing *items; }; + +/** + * @brief CAN configuration + */ struct can_configure { rt_uint32_t baud_rate; @@ -190,6 +346,7 @@ struct rt_can_ops; #define RT_CAN_CMD_SET_CANFD 0x1A #define RT_CAN_CMD_SET_BAUD_FD 0x1B #define RT_CAN_CMD_SET_BITTIMING 0x1C +#define RT_CAN_CMD_START 0x1D #define RT_DEVICE_CAN_INT_ERR 0x1000 @@ -211,6 +368,9 @@ enum RT_CAN_BUS_ERR RT_CAN_BUS_CRC_ERR = 6, }; +/** + * @brief CAN status + */ struct rt_can_status { rt_uint32_t rcverrcnt; @@ -241,6 +401,7 @@ struct rt_can_hdr #endif struct rt_can_device; typedef rt_err_t (*rt_canstatus_ind)(struct rt_can_device *, void *); + typedef struct rt_can_status_ind_type { rt_canstatus_ind ind; @@ -347,6 +508,9 @@ struct rt_can_tx_fifo struct rt_list_node freelist; }; +/** + * @brief CAN operators + */ struct rt_can_ops { rt_err_t (*configure)(struct rt_can_device *can, struct can_configure *cfg); @@ -355,10 +519,29 @@ struct rt_can_ops rt_ssize_t (*recvmsg)(struct rt_can_device *can, void *buf, rt_uint32_t boxno); }; +/** + * @brief Register a CAN device to device list + * + * @param can the CAN device object + * @param name the name of CAN device + * @param ops the CAN device operators + * @param data the private data of CAN device + * + * @return the error code, RT_EOK on successfully + */ rt_err_t rt_hw_can_register(struct rt_can_device *can, const char *name, const struct rt_can_ops *ops, void *data); -void rt_hw_can_isr(struct rt_can_device *can, int event); -#endif /*_CAN_H*/ +/** + * @brief CAN interrupt service routine + * + * @param can the CAN device + * @param event the event mask + */ +void rt_hw_can_isr(struct rt_can_device *can, int event); + +/*! @}*/ + +#endif /*__DEV_CAN_H*/ diff --git a/rt-thread/components/drivers/include/drivers/dev_i2c.h b/rt-thread/components/drivers/include/drivers/dev_i2c.h new file mode 100644 index 0000000..dea75d2 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/dev_i2c.h @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-04-25 weety first version + * 2021-04-20 RiceChen added support for bus control api + */ + +#ifndef __DEV_I2C_H__ +#define __DEV_I2C_H__ + +#include +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup I2C I2C + * + * @brief I2C driver api + * + * Example + * @code {.c} + * #include + * #include + * + * #define AHT10_I2C_BUS_NAME "i2c1" // 传感器连接的I2C总线设备名称 + * #define AHT10_ADDR 0x38 // 从机地址 + * #define AHT10_CALIBRATION_CMD 0xE1 // 校准命令 + * #define AHT10_NORMAL_CMD 0xA8 // 一般命令 + * #define AHT10_GET_DATA 0xAC // 获取数据命令 + * + * static struct rt_i2c_bus_device *i2c_bus = RT_NULL; // I2C总线设备句柄 + * static rt_bool_t initialized = RT_FALSE; // 传感器初始化状态 + * + * // 写传感器寄存器 + * static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t *data) + * { + * rt_uint8_t buf[3]; + * struct rt_i2c_msg msgs; + * rt_uint32_t buf_size = 1; + * + * buf[0] = reg; //cmd + * if (data != RT_NULL) + * { + * buf[1] = data[0]; + * buf[2] = data[1]; + * buf_size = 3; + * } + * + * msgs.addr = AHT10_ADDR; + * msgs.flags = RT_I2C_WR; + * msgs.buf = buf; + * msgs.len = buf_size; + * + * // 调用I2C设备接口传输数据 + * if (rt_i2c_transfer(bus, &msgs, 1) == 1) + * { + * return RT_EOK; + * } + * else + * { + * return -RT_ERROR; + * } + * } + * + * // 读传感器寄存器数据 + * static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf) + * { + * struct rt_i2c_msg msgs; + * + * msgs.addr = AHT10_ADDR; + * msgs.flags = RT_I2C_RD; + * msgs.buf = buf; + * msgs.len = len; + * + * // 调用I2C设备接口传输数据 + * if (rt_i2c_transfer(bus, &msgs, 1) == 1) + * { + * return RT_EOK; + * } + * else + * { + * return -RT_ERROR; + * } + * } + * + * static void read_temp_humi(float *cur_temp, float *cur_humi) + * { + * rt_uint8_t temp[6]; + * + * write_reg(i2c_bus, AHT10_GET_DATA, RT_NULL); // 发送命令 + * rt_thread_mdelay(400); + * read_regs(i2c_bus, 6, temp); // 获取传感器数据 + * + * // 湿度数据转换 + * *cur_humi = (temp[1] << 12 | temp[2] << 4 | (temp[3] & 0xf0) >> 4) * 100.0 / (1 << 20); + * // 温度数据转换 + * *cur_temp = ((temp[3] & 0xf) << 16 | temp[4] << 8 | temp[5]) * 200.0 / (1 << 20) - 50; + * } + * + * static void aht10_init(const char *name) + * { + * rt_uint8_t temp[2] = {0, 0}; + * + * // 查找I2C总线设备,获取I2C总线设备句柄 + * i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name); + * + * if (i2c_bus == RT_NULL) + * { + * rt_kprintf("can't find %s device!\n", name); + * } + * else + * { + * write_reg(i2c_bus, AHT10_NORMAL_CMD, temp); + * rt_thread_mdelay(400); + * + * temp[0] = 0x08; + * temp[1] = 0x00; + * write_reg(i2c_bus, AHT10_CALIBRATION_CMD, temp); + * rt_thread_mdelay(400); + * initialized = RT_TRUE; + * } + * } + * + * static void i2c_aht10_sample(int argc, char *argv[]) + * { + * float humidity, temperature; + * char name[RT_NAME_MAX]; + * + * humidity = 0.0; + * temperature = 0.0; + * + * if (argc == 2) + * { + * rt_strncpy(name, argv[1], RT_NAME_MAX); + * } + * else + * { + * rt_strncpy(name, AHT10_I2C_BUS_NAME, RT_NAME_MAX); + * } + * + * if (!initialized) + * { + * // 传感器初始化 + * aht10_init(name); + * } + * if (initialized) + * { + * // 读取温湿度数据 + * read_temp_humi(&temperature, &humidity); + * + * rt_kprintf("read aht10 sensor humidity : %d.%d %%\n", (int)humidity, (int)(humidity * 10) % 10); + * if( temperature >= 0 ) + * { + * rt_kprintf("read aht10 sensor temperature: %d.%d°C\n", (int)temperature, (int)(temperature * 10) % 10); + * } + * else + * { + * rt_kprintf("read aht10 sensor temperature: %d.%d°C\n", (int)temperature, (int)(-temperature * 10) % 10); + * } + * } + * else + * { + * rt_kprintf("initialize sensor failed!\n"); + * } + * } + * // 导出到 msh 命令列表中 + * MSH_CMD_EXPORT(i2c_aht10_sample, i2c aht10 sample); + * @endcode + * + * @ingroup Drivers + */ + +/*! + * @addtogroup I2C + * @{ + */ +#ifdef __cplusplus +extern "C" { +#endif + +#define RT_I2C_WR 0x0000 /*!< i2c wirte flag */ +#define RT_I2C_RD (1u << 0) /*!< i2c read flag */ +#define RT_I2C_ADDR_10BIT (1u << 2) /*!< this is a ten bit chip address */ +#define RT_I2C_NO_START (1u << 4) /*!< do not generate START condition */ +#define RT_I2C_IGNORE_NACK (1u << 5) /*!< ignore NACK from slave */ +#define RT_I2C_NO_READ_ACK (1u << 6) /* when I2C reading, we do not ACK */ +#define RT_I2C_NO_STOP (1u << 7) /*!< do not generate STOP condition */ + +#define RT_I2C_DEV_CTRL_10BIT (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x01) +#define RT_I2C_DEV_CTRL_ADDR (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x02) +#define RT_I2C_DEV_CTRL_TIMEOUT (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x03) +#define RT_I2C_DEV_CTRL_RW (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x04) +#define RT_I2C_DEV_CTRL_CLK (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x05) +#define RT_I2C_DEV_CTRL_UNLOCK (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x06) +#define RT_I2C_DEV_CTRL_GET_STATE (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x07) +#define RT_I2C_DEV_CTRL_GET_MODE (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x08) +#define RT_I2C_DEV_CTRL_GET_ERROR (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x09) + +/** + * @brief I2C Private Data + */ +struct rt_i2c_priv_data +{ + struct rt_i2c_msg *msgs; + rt_size_t number; +}; + +/** + * @brief I2C Message + */ +struct rt_i2c_msg +{ + rt_uint16_t addr; + rt_uint16_t flags; + rt_uint16_t len; + rt_uint8_t *buf; +}; + +struct rt_i2c_bus_device; + +/** + * @brief I2C Bus Device Operations + */ +struct rt_i2c_bus_device_ops +{ + rt_ssize_t (*master_xfer)(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg msgs[], + rt_uint32_t num); + rt_ssize_t (*slave_xfer)(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg msgs[], + rt_uint32_t num); + rt_err_t (*i2c_bus_control)(struct rt_i2c_bus_device *bus, + int cmd, + void *args); +}; + +/** + * @brief I2C Bus Device + */ +struct rt_i2c_bus_device +{ + struct rt_device parent; + const struct rt_i2c_bus_device_ops *ops; + rt_uint16_t flags; + struct rt_mutex lock; + rt_uint32_t timeout; + rt_uint32_t retries; + void *priv; +}; + +/** + * @brief I2C Client + */ +struct rt_i2c_client +{ +#ifdef RT_USING_DM + struct rt_device parent; + + const char *name; + const struct rt_i2c_device_id *id; + const struct rt_ofw_node_id *ofw_id; +#endif + struct rt_i2c_bus_device *bus; + rt_uint16_t client_addr; +}; + +#ifdef RT_USING_DM +struct rt_i2c_device_id +{ + char name[20]; + void *data; +}; + +struct rt_i2c_driver +{ + struct rt_driver parent; + + const struct rt_i2c_device_id *ids; + const struct rt_ofw_node_id *ofw_ids; + + rt_err_t (*probe)(struct rt_i2c_client *client); + rt_err_t (*remove)(struct rt_i2c_client *client); + rt_err_t (*shutdown)(struct rt_i2c_client *client); +}; + +rt_err_t rt_i2c_driver_register(struct rt_i2c_driver *driver); +rt_err_t rt_i2c_device_register(struct rt_i2c_client *client); + +#define RT_I2C_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, i2c, BUILIN) +#endif /* RT_USING_DM */ + +/** + * @brief I2C Bus Device Initialization + * + * @param bus the I2C bus device + * @param name the name of I2C bus device + * + * @return rt_err_t error code + */ +rt_err_t rt_i2c_bus_device_device_init(struct rt_i2c_bus_device *bus, + const char *name); + +/** + * @brief I2C Bus Device Register + * + * @param bus the I2C bus device + * @param bus_name the name of I2C bus device + * + * @return rt_err_t error code + */ +rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus, + const char *bus_name); + +/** + * @brief I2C Bus Device Find + * + * @param bus_name the name of I2C bus device + * + * @return rt_i2c_bus_device the I2C bus device + */ +struct rt_i2c_bus_device *rt_i2c_bus_device_find(const char *bus_name); + +/** + * @brief I2C data transmission. + * + * @param bus the I2C bus device + * @param msgs the I2C message list + * @param num the number of I2C message + * + * @return rt_ssize_t the actual length of transmitted + */ +rt_ssize_t rt_i2c_transfer(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg msgs[], + rt_uint32_t num); + +/** + * @brief I2C Control + * + * @param bus the I2C bus device + * @param cmd the I2C control command + * @param args the I2C control arguments + * + * @return rt_err_t error code + */ +rt_err_t rt_i2c_control(struct rt_i2c_bus_device *bus, + int cmd, + void *args); + +/** + * @brief I2C Master Send + * + * @param bus the I2C bus device + * @param addr the I2C slave address + * @param flags the I2C flags + * @param buf the I2C send buffer + * @param count the I2C send buffer length + * + * @return rt_ssize_t the actual length of transmitted + */ +rt_ssize_t rt_i2c_master_send(struct rt_i2c_bus_device *bus, + rt_uint16_t addr, + rt_uint16_t flags, + const rt_uint8_t *buf, + rt_uint32_t count); + +/** + * @brief I2C Master Receive + * + * @param bus the I2C bus device + * @param addr the I2C slave address + * @param flags the I2C flags + * @param buf the I2C receive buffer + * @param count the I2C receive buffer length + * + * @return rt_ssize_t the actual length of received + */ +rt_ssize_t rt_i2c_master_recv(struct rt_i2c_bus_device *bus, + rt_uint16_t addr, + rt_uint16_t flags, + rt_uint8_t *buf, + rt_uint32_t count); + +rt_inline rt_err_t rt_i2c_bus_lock(struct rt_i2c_bus_device *bus, rt_tick_t timeout) +{ + return rt_mutex_take(&bus->lock, timeout); +} + +rt_inline rt_err_t rt_i2c_bus_unlock(struct rt_i2c_bus_device *bus) +{ + return rt_mutex_release(&bus->lock); +} + +#ifdef __cplusplus +} +#endif + +/*! @}*/ + +#endif diff --git a/rt-thread/components/drivers/include/drivers/i2c-bit-ops.h b/rt-thread/components/drivers/include/drivers/dev_i2c_bit_ops.h similarity index 93% rename from rt-thread/components/drivers/include/drivers/i2c-bit-ops.h rename to rt-thread/components/drivers/include/drivers/dev_i2c_bit_ops.h index 2b4ea57..3c28714 100644 --- a/rt-thread/components/drivers/include/drivers/i2c-bit-ops.h +++ b/rt-thread/components/drivers/include/drivers/dev_i2c_bit_ops.h @@ -8,8 +8,8 @@ * 2012-04-25 weety first version */ -#ifndef __I2C_BIT_OPS_H__ -#define __I2C_BIT_OPS_H__ +#ifndef __DEV_I2C_BIT_OPS_H__ +#define __DEV_I2C_BIT_OPS_H__ #ifdef __cplusplus extern "C" { diff --git a/rt-thread/components/drivers/include/drivers/i2c_dm.h b/rt-thread/components/drivers/include/drivers/dev_i2c_dm.h similarity index 95% rename from rt-thread/components/drivers/include/drivers/i2c_dm.h rename to rt-thread/components/drivers/include/drivers/dev_i2c_dm.h index 4f5b251..c59d6e9 100644 --- a/rt-thread/components/drivers/include/drivers/i2c_dm.h +++ b/rt-thread/components/drivers/include/drivers/dev_i2c_dm.h @@ -8,8 +8,8 @@ * 2022-11-26 GuEe-GUI first version */ -#ifndef __I2C_DM_H__ -#define __I2C_DM_H__ +#ifndef __DEV_I2C_DM_H__ +#define __DEV_I2C_DM_H__ #include #include @@ -48,4 +48,4 @@ rt_inline rt_err_t i2c_timings_ofw_parse(struct rt_ofw_node *dev_np, struct i2c_ void i2c_bus_scan_clients(struct rt_i2c_bus_device *bus); -#endif /* __I2C_DM_H__ */ +#endif /* __DEV_I2C_DM_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/mmc.h b/rt-thread/components/drivers/include/drivers/dev_mmc.h similarity index 99% rename from rt-thread/components/drivers/include/drivers/mmc.h rename to rt-thread/components/drivers/include/drivers/dev_mmc.h index 88ee740..a537287 100644 --- a/rt-thread/components/drivers/include/drivers/mmc.h +++ b/rt-thread/components/drivers/include/drivers/dev_mmc.h @@ -9,8 +9,8 @@ * 2024-05-25 HPMicro add strobe support */ -#ifndef __MMC_H__ -#define __MMC_H__ +#ifndef __DEV_MMC_H__ +#define __DEV_MMC_H__ #include #include diff --git a/rt-thread/components/drivers/include/drivers/mmcsd_core.h b/rt-thread/components/drivers/include/drivers/dev_mmcsd_core.h similarity index 98% rename from rt-thread/components/drivers/include/drivers/mmcsd_core.h rename to rt-thread/components/drivers/include/drivers/dev_mmcsd_core.h index 3a6ac13..c86e9dd 100644 --- a/rt-thread/components/drivers/include/drivers/mmcsd_core.h +++ b/rt-thread/components/drivers/include/drivers/dev_mmcsd_core.h @@ -8,8 +8,8 @@ * 2011-07-25 weety first version */ -#ifndef __CORE_H__ -#define __CORE_H__ +#ifndef __DEV_MMCSD_CORE_H__ +#define __DEV_MMCSD_CORE_H__ #include #include @@ -249,8 +249,6 @@ struct rt_mmcsd_host *mmcsd_alloc_host(void); void mmcsd_free_host(struct rt_mmcsd_host *host); int rt_mmcsd_core_init(void); -int rt_mmcsd_blk_init(void); -rt_int32_t read_lba(struct rt_mmcsd_card *card, size_t lba, uint8_t *buffer, size_t count); rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card); void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card); diff --git a/rt-thread/components/drivers/include/drivers/pin.h b/rt-thread/components/drivers/include/drivers/dev_pin.h similarity index 50% rename from rt-thread/components/drivers/include/drivers/pin.h rename to rt-thread/components/drivers/include/drivers/dev_pin.h index 22d4328..b1705ae 100644 --- a/rt-thread/components/drivers/include/drivers/pin.h +++ b/rt-thread/components/drivers/include/drivers/dev_pin.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -9,11 +9,72 @@ * 2017-10-20 ZYH add mode open drain and input pull down */ -#ifndef PIN_H__ -#define PIN_H__ +#ifndef DEV_PIN_H__ +#define DEV_PIN_H__ #include +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup Pin Pin + * + * @brief Pin driver api + * + * Example + * @code {.c} + * #include + * #include + * + * + * #ifndef BEEP_PIN_NUM + * #define BEEP_PIN_NUM 35 // PB0 + * #endif + * #ifndef KEY0_PIN_NUM + * #define KEY0_PIN_NUM 55 // PD8 + * #endif + * #ifndef KEY1_PIN_NUM + * #define KEY1_PIN_NUM 56 // PD9 + * #endif + * + * void beep_on(void *args) + * { + * rt_kprintf("turn on beep!\n"); + * + * rt_pin_write(BEEP_PIN_NUM, PIN_HIGH); + * } + * + * void beep_off(void *args) + * { + * rt_kprintf("turn off beep!\n"); + * + * rt_pin_write(BEEP_PIN_NUM, PIN_LOW); + * } + * + * static void pin_beep_sample(void) + * { + * rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT); + * rt_pin_write(BEEP_PIN_NUM, PIN_LOW); + * + * rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP); + * rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL); + * rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE); + * + * + * rt_pin_mode(KEY1_PIN_NUM, PIN_MODE_INPUT_PULLUP); + * rt_pin_attach_irq(KEY1_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_off, RT_NULL); + * rt_pin_irq_enable(KEY1_PIN_NUM, PIN_IRQ_ENABLE); + * } + * + * MSH_CMD_EXPORT(pin_beep_sample, pin beep sample); + * @endcode + * + * @ingroup Drivers + */ + +/*! + * @addtogroup Pin + * @{ + */ #ifdef __cplusplus extern "C" { #endif @@ -28,28 +89,38 @@ struct rt_pin_irqchip int irq; rt_base_t pin_range[2]; }; + +struct rt_pin_irq_hdr; #endif /* RT_USING_DM */ -/* pin device and operations for RT-Thread */ +/** + * @brief pin device structure + */ struct rt_device_pin { struct rt_device parent; #ifdef RT_USING_DM + /* MUST keep the order member after parent */ struct rt_pin_irqchip irqchip; + /* Fill by DM */ + rt_base_t pin_start; + rt_size_t pin_nr; + rt_list_t list; + struct rt_pin_irq_hdr *legacy_isr; #endif /* RT_USING_DM */ const struct rt_pin_ops *ops; }; #define PIN_NONE (-1) -#define PIN_LOW 0x00 -#define PIN_HIGH 0x01 +#define PIN_LOW 0x00 /*!< low level */ +#define PIN_HIGH 0x01 /*!< high level */ -#define PIN_MODE_OUTPUT 0x00 -#define PIN_MODE_INPUT 0x01 -#define PIN_MODE_INPUT_PULLUP 0x02 -#define PIN_MODE_INPUT_PULLDOWN 0x03 -#define PIN_MODE_OUTPUT_OD 0x04 +#define PIN_MODE_OUTPUT 0x00 /*!< output mode */ +#define PIN_MODE_INPUT 0x01 /*!< input mode */ +#define PIN_MODE_INPUT_PULLUP 0x02 /*!< input mode with pull-up */ +#define PIN_MODE_INPUT_PULLDOWN 0x03 /*!< input mode with pull-down */ +#define PIN_MODE_OUTPUT_OD 0x04 /*!< output mode with open-drain */ #ifdef RT_USING_PINCTRL enum @@ -84,29 +155,38 @@ enum }; #endif /* RT_USING_PINCTRL */ -#define PIN_IRQ_MODE_RISING 0x00 -#define PIN_IRQ_MODE_FALLING 0x01 -#define PIN_IRQ_MODE_RISING_FALLING 0x02 -#define PIN_IRQ_MODE_HIGH_LEVEL 0x03 -#define PIN_IRQ_MODE_LOW_LEVEL 0x04 +#define PIN_IRQ_MODE_RISING 0x00 /*!< rising edge trigger */ +#define PIN_IRQ_MODE_FALLING 0x01 /*!< falling edge trigger */ +#define PIN_IRQ_MODE_RISING_FALLING 0x02 /*!< rising and falling edge trigger */ +#define PIN_IRQ_MODE_HIGH_LEVEL 0x03 /*!< high level trigger */ +#define PIN_IRQ_MODE_LOW_LEVEL 0x04 /*!< low level trigger */ -#define PIN_IRQ_DISABLE 0x00 -#define PIN_IRQ_ENABLE 0x01 +#define PIN_IRQ_DISABLE 0x00 /*!< disable irq */ +#define PIN_IRQ_ENABLE 0x01 /*!< enable irq */ -#define PIN_IRQ_PIN_NONE PIN_NONE +#define PIN_IRQ_PIN_NONE PIN_NONE /*!< no pin irq */ +/** + * @brief pin mode structure + */ struct rt_device_pin_mode { rt_base_t pin; rt_uint8_t mode; /* e.g. PIN_MODE_OUTPUT */ }; +/** + * @brief pin value structure + */ struct rt_device_pin_value { rt_base_t pin; rt_uint8_t value; /* PIN_LOW or PIN_HIGH */ }; +/** + * @brief pin irq structure + */ struct rt_pin_irq_hdr { rt_base_t pin; @@ -116,6 +196,9 @@ struct rt_pin_irq_hdr }; #ifdef RT_USING_PINCTRL +/** + * @brief pin control configure structure + */ struct rt_pin_ctrl_conf_params { const char *propname; @@ -124,6 +207,9 @@ struct rt_pin_ctrl_conf_params }; #endif /* RT_USING_PINCTRL */ +/** + * @brief pin device operations + */ struct rt_pin_ops { void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode); @@ -134,6 +220,7 @@ struct rt_pin_ops rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_base_t pin); rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled); rt_base_t (*pin_get)(const char *name); + rt_err_t (*pin_debounce)(struct rt_device *device, rt_base_t pin, rt_uint32_t debounce); #ifdef RT_USING_DM rt_err_t (*pin_irq_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode); rt_ssize_t (*pin_parse)(struct rt_device *device, struct rt_ofw_cell_args *args, rt_uint32_t *flags); @@ -143,16 +230,77 @@ struct rt_pin_ops #endif /* RT_USING_PINCTRL */ }; +/** + * @brief register a pin device + * @param name the name of pin device + * @param ops the operations of pin device + * @param user_data the user data of pin device + * @return int error code + */ int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data); + +/** + * @brief set pin mode + * @param pin the pin number + * @param mode the pin mode + */ void rt_pin_mode(rt_base_t pin, rt_uint8_t mode); + +/** + * @brief write pin value + * @param pin the pin number + * @param value the pin value + */ void rt_pin_write(rt_base_t pin, rt_ssize_t value); + +/** + * @brief read pin value + * @param pin the pin number + * @return rt_ssize_t the pin value + */ rt_ssize_t rt_pin_read(rt_base_t pin); + +/** + * @brief get pin number by name + * @param name the pin name + * @return rt_base_t the pin number + */ rt_base_t rt_pin_get(const char *name); + +/** + * @brief bind the pin interrupt callback function + * @param pin the pin number + * @param mode the irq mode + * @param hdr the irq callback function + * @param args the argument of the callback function + * @return rt_err_t error code + */ rt_err_t rt_pin_attach_irq(rt_base_t pin, rt_uint8_t mode, void (*hdr)(void *args), void *args); + +/** + * @brief detach the pin interrupt callback function + * @param pin the pin number + * @return rt_err_t error code + */ rt_err_t rt_pin_detach_irq(rt_base_t pin); + +/** + * @brief enable or disable the pin interrupt + * @param pin the pin number + * @param enabled PIN_IRQ_ENABLE or PIN_IRQ_DISABLE + * @return rt_err_t error code + */ rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint8_t enabled); +/** + * @brief set the pin's debounce time + * @param pin the pin number + * @param debounce time + * @return rt_err_t error code + */ +rt_err_t rt_pin_debounce(rt_base_t pin, rt_uint32_t debounce); + #ifdef RT_USING_DM rt_ssize_t rt_pin_get_named_pin(struct rt_device *dev, const char *propname, int index, rt_uint8_t *out_mode, rt_uint8_t *out_value); @@ -175,4 +323,6 @@ rt_err_t rt_pin_ctrl_confs_apply_by_name(struct rt_device *device, const char *n } #endif +/*! @}*/ + #endif diff --git a/rt-thread/components/drivers/include/drivers/dev_pwm.h b/rt-thread/components/drivers/include/drivers/dev_pwm.h new file mode 100644 index 0000000..1e0970f --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/dev_pwm.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-05-07 aozima the first version + * 2022-09-24 yuqi add phase and dead time configuration + */ + +#ifndef __DEV_PWM_H__ +#define __DEV_PWM_H__ + +#include +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup PWM PWM + * + * @brief PWM driver api + * + * Example + * @code {.c} + * #include + * #include + * + * #define PWM_DEV_NAME "pwm3" // PWM设备名称 + * #define PWM_DEV_CHANNEL 4 // PWM通道 + * + * struct rt_device_pwm *pwm_dev; // PWM设备句柄 + * + * static int pwm_led_sample(int argc, char *argv[]) + * { + * rt_uint32_t period, pulse, dir; + * + * period = 500000; // 周期为0.5ms,单位为纳秒ns + * dir = 1; // PWM脉冲宽度值的增减方向 + * pulse = 0; // PWM脉冲宽度值,单位为纳秒ns + * + * // 查找设备 + * pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); + * if (pwm_dev == RT_NULL) + * { + * rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME); + * return -RT_ERROR; + * } + * + * // 设置PWM周期和脉冲宽度默认值 + * rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); + * // 使能设备 + * rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL); + * + * while (1) + * { + * rt_thread_mdelay(50); + * if (dir) + * { + * pulse += 5000; // 从0值开始每次增加5000ns + * } + * else + * { + * pulse -= 5000; // 从最大值开始每次减少5000ns + * } + * if (pulse >= period) + * { + * dir = 0; + * } + * if (0 == pulse) + * { + * dir = 1; + * } + * + * // 设置PWM周期和脉冲宽度 + * rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); + * } + * } + * + * MSH_CMD_EXPORT(pwm_led_sample, pwm sample); + * @endcode + * + * @ingroup Drivers + */ + +/*! + * @addtogroup PWM + * @{ + */ +#define PWM_CMD_ENABLE (RT_DEVICE_CTRL_BASE(PWM) + 0) +#define PWM_CMD_DISABLE (RT_DEVICE_CTRL_BASE(PWM) + 1) +#define PWM_CMD_SET (RT_DEVICE_CTRL_BASE(PWM) + 2) +#define PWM_CMD_GET (RT_DEVICE_CTRL_BASE(PWM) + 3) +#define PWMN_CMD_ENABLE (RT_DEVICE_CTRL_BASE(PWM) + 4) +#define PWMN_CMD_DISABLE (RT_DEVICE_CTRL_BASE(PWM) + 5) +#define PWM_CMD_SET_PERIOD (RT_DEVICE_CTRL_BASE(PWM) + 6) +#define PWM_CMD_SET_PULSE (RT_DEVICE_CTRL_BASE(PWM) + 7) +#define PWM_CMD_SET_DEAD_TIME (RT_DEVICE_CTRL_BASE(PWM) + 8) +#define PWM_CMD_SET_PHASE (RT_DEVICE_CTRL_BASE(PWM) + 9) +#define PWM_CMD_ENABLE_IRQ (RT_DEVICE_CTRL_BASE(PWM) + 10) +#define PWM_CMD_DISABLE_IRQ (RT_DEVICE_CTRL_BASE(PWM) + 11) + +/** + * @brief PWM configuration + */ +struct rt_pwm_configuration +{ + rt_uint32_t channel; /* 0 ~ n or 0 ~ -n, which depends on specific MCU requirements */ + rt_uint32_t period; /* unit:ns 1ns~4.29s:1Ghz~0.23hz */ + rt_uint32_t pulse; /* unit:ns (pulse<=period) */ + rt_uint32_t dead_time; /* unit:ns */ + rt_uint32_t phase; /*unit: degree, 0~360, which is the phase of pwm output, */ + /* + * RT_TRUE : The channel of pwm is complememtary. + * RT_FALSE : The channel of pwm is nomal. + */ + rt_bool_t complementary; +}; + +struct rt_device_pwm; +/** + * @brief PWM operations + */ +struct rt_pwm_ops +{ + rt_err_t (*control)(struct rt_device_pwm *device, int cmd, void *arg); +}; + +/** + * @brief PWM device + */ +struct rt_device_pwm +{ + struct rt_device parent; + const struct rt_pwm_ops *ops; +}; +/** + * @brief register a PWM device + * @param device the PWM device + * @param name the name of PWM device + * @param ops the operations of PWM device + * @param user_data the user data of PWM device + * @return rt_err_t error code + */ +rt_err_t rt_device_pwm_register(struct rt_device_pwm *device, const char *name, const struct rt_pwm_ops *ops, const void *user_data); + +/** + * @brief enable the PWM channel + * @param device the PWM device + * @param channel the channel of PWM + * @return rt_err_t error code + */ +rt_err_t rt_pwm_enable(struct rt_device_pwm *device, int channel); + +/** + * @brief disable the PWM channel + * @param device the PWM device + * @param channel the channel of PWM + * @return rt_err_t error code + */ +rt_err_t rt_pwm_disable(struct rt_device_pwm *device, int channel); + +/** + * @brief set the PWM channel + * @param device the PWM device + * @param channel the channel of PWM + * @param period the period of PWM + * @param pulse the pulse of PWM + * @return rt_err_t error code + */ +rt_err_t rt_pwm_set(struct rt_device_pwm *device, int channel, rt_uint32_t period, rt_uint32_t pulse); + +/** + * @brief set the PWM channel period + * @param device the PWM device + * @param channel the channel of PWM + * @param period the period of PWM + * @return rt_err_t error code +*/ +rt_err_t rt_pwm_set_period(struct rt_device_pwm *device, int channel, rt_uint32_t period); + +/** + * @brief set the PWM channel pulse + * @param device the PWM device + * @param channel the channel of PWM + * @param pulse the period of PWM + * @return rt_err_t error code +*/ +rt_err_t rt_pwm_set_pulse(struct rt_device_pwm *device, int channel, rt_uint32_t pulse); + +/** + * @brief set the dead zone time of PWM + * @param device the PWM device + * @param channel the channel of PWM + * @param dead_time dead zone time + * @return rt_err_t error code +*/ +rt_err_t rt_pwm_set_dead_time(struct rt_device_pwm *device, int channel, rt_uint32_t dead_time); + +/** + * @brief set the phase of PWM + * @param device the PWM device + * @param channel the channel of PWM + * @param phase phase + * @return rt_err_t error code +*/ +rt_err_t rt_pwm_set_phase(struct rt_device_pwm *device, int channel, rt_uint32_t phase); + +/*! @}*/ + +#endif /* __DEV_PWM_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/rtc.h b/rt-thread/components/drivers/include/drivers/dev_rtc.h similarity index 59% rename from rt-thread/components/drivers/include/drivers/rtc.h rename to rt-thread/components/drivers/include/drivers/dev_rtc.h index cba0ff9..740142d 100644 --- a/rt-thread/components/drivers/include/drivers/rtc.h +++ b/rt-thread/components/drivers/include/drivers/dev_rtc.h @@ -11,12 +11,75 @@ * 2022-04-05 tyx add timestamp function */ -#ifndef __RTC_H__ -#define __RTC_H__ +#ifndef __DEV_RTC_H__ +#define __DEV_RTC_H__ #include #include +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup RTC RTC + * + * @brief RTC driver api + * + * Example + * @code {.c} + * + * #include + * #include + * + * #define RTC_NAME "rtc" + * + * static int rtc_sample(int argc, char *argv[]) + * { + * rt_err_t ret = RT_EOK; + * time_t now; + * rt_device_t device = RT_NULL; + * + * device = rt_device_find(RTC_NAME); + * if (!device) + * { + * LOG_E("find %s failed!", RTC_NAME); + * return RT_ERROR; + * } + * + * if(rt_device_open(device, 0) != RT_EOK) + * { + * LOG_E("open %s failed!", RTC_NAME); + * return RT_ERROR; + * } + * + * ret = set_date(2018, 12, 3); + * if (ret != RT_EOK) + * { + * rt_kprintf("set RTC date failed\n"); + * return ret; + * } + * + * ret = set_time(11, 15, 50); + * if (ret != RT_EOK) + * { + * rt_kprintf("set RTC time failed\n"); + * return ret; + * } + * + * rt_thread_mdelay(3000); + * + * now = time(RT_NULL); + * rt_kprintf("%s\n", ctime(&now)); + * + * return ret; + * } + * MSH_CMD_EXPORT(rtc_sample, rtc sample); + * @endcode + * + * @ingroup Drivers + */ +/*! + * @addtogroup RTC + * @{ + */ #ifdef __cplusplus extern "C" { #endif @@ -31,7 +94,9 @@ extern "C" { #define RT_DEVICE_CTRL_RTC_SET_TIMESPEC (RT_DEVICE_CTRL_BASE(RTC) + 0x08) /**< set timespec for clock_settime */ #define RT_DEVICE_CTRL_RTC_GET_TIMERES (RT_DEVICE_CTRL_BASE(RTC) + 0x09) /**< get resolution for clock_getres */ -/* used for alarm function */ +/** + * @brief RTC alarm structure + */ struct rt_rtc_wkalarm { rt_bool_t enable; /* 0 = alarm disabled, 1 = alarm enabled */ @@ -42,7 +107,9 @@ struct rt_rtc_wkalarm rt_int32_t tm_mon; /* alarm at tm_mon */ rt_int32_t tm_year; /* alarm at tm_year */ }; - +/** + * @brief RTC operations + */ struct rt_rtc_ops { rt_err_t (*init)(void); @@ -54,20 +121,63 @@ struct rt_rtc_ops rt_err_t (*set_timeval)(struct timeval *tv); }; +/** + * @brief RTC device structure + */ typedef struct rt_rtc_device { struct rt_device parent; const struct rt_rtc_ops *ops; } rt_rtc_dev_t; +/** + * @brief Register a RTC device + * + * @param rtc RTC device + * @param name RTC device name + * @param flag RTC device flag + * @param data RTC device data + * @return rt_err_t error code + */ rt_err_t rt_hw_rtc_register(rt_rtc_dev_t *rtc, const char *name, rt_uint32_t flag, void *data); +/** + * @brief set date + * + * @param year year + * @param month month + * @param day day + * @return rt_err_t error code + */ rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day); + +/** + * @brief set time + * + * @param hour hour + * @param minute minute + * @param second second + * @return rt_err_t error code +*/ rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second); + +/** + * @brief set timestamp + * + * @param timestamp A pointer to time + * @return rt_err_t error code + */ rt_err_t set_timestamp(time_t timestamp); + +/** + * @brief get timestamp + * + * @param timestamp A secondary pointer to time + * @return rt_err_t error code + */ rt_err_t get_timestamp(time_t *timestamp); #ifdef RT_USING_SYSTEM_WORKQUEUE @@ -79,4 +189,6 @@ rt_err_t rt_soft_rtc_set_source(const char *name); } #endif -#endif /* __RTC_H__ */ +/*! @}*/ + +#endif /* __DEV_RTC_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/sd.h b/rt-thread/components/drivers/include/drivers/dev_sd.h similarity index 96% rename from rt-thread/components/drivers/include/drivers/sd.h rename to rt-thread/components/drivers/include/drivers/dev_sd.h index 4b7b093..ca8bb0c 100644 --- a/rt-thread/components/drivers/include/drivers/sd.h +++ b/rt-thread/components/drivers/include/drivers/dev_sd.h @@ -9,8 +9,8 @@ * 2024-05-26 HPMicro Add UHS-I support */ -#ifndef __SD_H__ -#define __SD_H__ +#ifndef __DEV_SD_H__ +#define __DEV_SD_H__ #include #include diff --git a/rt-thread/components/drivers/include/drivers/sdio.h b/rt-thread/components/drivers/include/drivers/dev_sdio.h similarity index 99% rename from rt-thread/components/drivers/include/drivers/sdio.h rename to rt-thread/components/drivers/include/drivers/dev_sdio.h index b145ffd..a8d30fd 100644 --- a/rt-thread/components/drivers/include/drivers/sdio.h +++ b/rt-thread/components/drivers/include/drivers/dev_sdio.h @@ -8,8 +8,8 @@ * 2012-01-15 weety first version */ -#ifndef __SDIO_H__ -#define __SDIO_H__ +#ifndef __DEV_SDIO_H__ +#define __DEV_SDIO_H__ #include #include diff --git a/rt-thread/components/drivers/include/drivers/serial.h b/rt-thread/components/drivers/include/drivers/dev_serial.h similarity index 66% rename from rt-thread/components/drivers/include/drivers/serial.h rename to rt-thread/components/drivers/include/drivers/dev_serial.h index d90b906..b7f627e 100644 --- a/rt-thread/components/drivers/include/drivers/serial.h +++ b/rt-thread/components/drivers/include/drivers/dev_serial.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2024, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -11,11 +11,109 @@ * the size of ring buffer. */ -#ifndef __SERIAL_H__ -#define __SERIAL_H__ +#ifndef __DEV_SERIAL_H__ +#define __DEV_SERIAL_H__ #include +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup Serial Serial + * + * @brief Serial driver api + * + * Example + * @code {.c} + * + * #include + * + * #define SAMPLE_UART_NAME "uart2" + * static struct rt_semaphore rx_sem; + * static rt_device_t serial; + * + * static rt_err_t uart_input(rt_device_t dev, rt_size_t size) + * { + * + * rt_sem_release(&rx_sem); + * + * return RT_EOK; + * } + * + * static void serial_thread_entry(void *parameter) + * { + * char ch; + * + * while (1) + * { + * + * while (rt_device_read(serial, -1, &ch, 1) != 1) + * { + * + * rt_sem_take(&rx_sem, RT_WAITING_FOREVER); + * } + * + * ch = ch + 1; + * rt_device_write(serial, 0, &ch, 1); + * } + * } + * + * static int uart_sample(int argc, char *argv[]) + * { + * rt_err_t ret = RT_EOK; + * char uart_name[RT_NAME_MAX]; + * char str[] = "hello RT-Thread!\r\n"; + * + * if (argc == 2) + * { + * rt_strncpy(uart_name, argv[1], RT_NAME_MAX); + * } + * else + * { + * rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX); + * } + * + * + * serial = rt_device_find(uart_name); + * if (!serial) + * { + * rt_kprintf("find %s failed!\n", uart_name); + * return -RT_ERROR; + * } + * + * + * rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO); + * + * rt_device_open(serial, RT_DEVICE_FLAG_INT_RX); + * + * rt_device_set_rx_indicate(serial, uart_input); + * + * rt_device_write(serial, 0, str, (sizeof(str) - 1)); + * + * + * rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10); + * + * if (thread != RT_NULL) + * { + * rt_thread_startup(thread); + * } + * else + * { + * ret = -RT_ERROR; + * } + * + * return ret; + * } + * + * MSH_CMD_EXPORT(uart_sample, uart device sample); + * @endcode + * + * @ingroup Drivers + */ + +/*! + * @addtogroup Serial + * @{ + */ #define BAUD_RATE_2400 2400 #define BAUD_RATE_4800 4800 #define BAUD_RATE_9600 9600 @@ -105,7 +203,6 @@ /** * @brief Sets a hook function when RX indicate is called * - * @param thread is the target thread that initializing */ typedef void (*rt_hw_serial_rxind_hookproto_t)(rt_device_t dev, rt_size_t size); RT_OBJECT_HOOKLIST_DECLARE(rt_hw_serial_rxind_hookproto_t, rt_hw_serial_rxind); @@ -167,13 +264,15 @@ struct rt_serial_device void *serial_tx; struct rt_spinlock spinlock; - +#ifdef RT_USING_SERIAL_BYPASS + struct rt_serial_bypass* bypass; +#endif struct rt_device_notify rx_notify; }; typedef struct rt_serial_device rt_serial_t; /** - * uart operators + * @brief Configure the serial device */ struct rt_uart_ops { @@ -186,13 +285,41 @@ struct rt_uart_ops rt_ssize_t (*dma_transmit)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction); }; +/** + * @brief Serial interrupt service routine + * @param serial serial device + * @param event event mask + * @ingroup Serial + */ void rt_hw_serial_isr(struct rt_serial_device *serial, int event); +/** + * @brief Register a serial device to device list + * + * @param serial serial device + * @param name device name + * @param flag device flag + * @param data device private data + * @return rt_err_t error code + * @note This function will register a serial device to system device list, + * and add a device object to system object list. + * @ingroup Serial + */ rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, const char *name, rt_uint32_t flag, void *data); +/** + * @brief register a serial device to system device list and add a device object to system object list + * + * @param serial serial device + * @return rt_err_t error code + * + * @ingroup Serial + */ rt_err_t rt_hw_serial_register_tty(struct rt_serial_device *serial); +/*! @}*/ + #endif diff --git a/rt-thread/components/drivers/include/drivers/serial_v2.h b/rt-thread/components/drivers/include/drivers/dev_serial_v2.h similarity index 61% rename from rt-thread/components/drivers/include/drivers/serial_v2.h rename to rt-thread/components/drivers/include/drivers/dev_serial_v2.h index d0dcf37..0bdc51b 100644 --- a/rt-thread/components/drivers/include/drivers/serial_v2.h +++ b/rt-thread/components/drivers/include/drivers/dev_serial_v2.h @@ -8,11 +8,127 @@ * 2021-06-01 KyleChan first version */ -#ifndef __SERIAL_V2_H__ -#define __SERIAL_V2_H__ +#ifndef __DEV_SERIAL_V2_H__ +#define __DEV_SERIAL_V2_H__ #include + +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup Serial_v2 Serial v2 + * + * @brief Serial v2 driver api + * + * Example + * @code {.c} + * + * #include + * #include + * + * #define SAMPLE_UART_NAME "uart1" + * + * struct rx_msg + * { + * rt_device_t dev; + * rt_size_t size; + * }; + * static rt_device_t serial; + * static struct rt_messagequeue rx_mq; + * + * static rt_err_t uart_input(rt_device_t dev, rt_size_t size) + * { + * struct rx_msg msg; + * rt_err_t result; + * msg.dev = dev; + * msg.size = size; + * + * result = rt_mq_send(&rx_mq, &msg, sizeof(msg)); + * if (result == -RT_EFULL) + * { + * rt_kprintf("message queue full!\n"); + * } + * return result; + * } + * + * static void serial_thread_entry(void *parameter) + * { + * struct rx_msg msg; + * rt_err_t result; + * rt_uint32_t rx_length; + * static char rx_buffer[BSP_UART1_RX_BUFSIZE + 1]; + * + * while (1) + * { + * rt_memset(&msg, 0, sizeof(msg)); + * result = rt_mq_recv(&rx_mq, &msg, sizeof(msg), RT_WAITING_FOREVER); + * if (result > 0) + * { + * rx_length = rt_device_read(msg.dev, 0, rx_buffer, msg.size); + * rx_buffer[rx_length] = '\0'; + * rt_device_write(serial, 0, rx_buffer, rx_length); + * rt_kprintf("%s\n",rx_buffer); + * } + * } + * } + * + * static int uart_dma_sample(int argc, char *argv[]) + * { + * rt_err_t ret = RT_EOK; + * char uart_name[RT_NAME_MAX]; + * static char msg_pool[256]; + * char str[] = "hello RT-Thread!\r\n"; + * + * if (argc == 2) + * { + * rt_strncpy(uart_name, argv[1], RT_NAME_MAX); + * } + * else + * { + * rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX); + * } + * + * serial = rt_device_find(uart_name); + * if (!serial) + * { + * rt_kprintf("find %s failed!\n", uart_name); + * return RT_ERROR; + * } + * + * rt_mq_init(&rx_mq, "rx_mq", + * msg_pool, + * sizeof(struct rx_msg), + * sizeof(msg_pool), + * RT_IPC_FLAG_FIFO); + * + * rt_device_open(serial, RT_DEVICE_FLAG_RX_NON_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING); + * rt_device_set_rx_indicate(serial, uart_input); + * rt_device_write(serial, 0, str, (sizeof(str) - 1)); + * + * rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10); + * if (thread != RT_NULL) + * { + * rt_thread_startup(thread); + * } + * else + * { + * ret = RT_ERROR; + * } + * + * return ret; + * } + * MSH_CMD_EXPORT(uart_dma_sample, uart device dma sample); + * @endcode + * + * @ingroup Drivers + */ + + +/*! + * @addtogroup Serial_v2 + * @{ + */ + #define BAUD_RATE_2400 2400 #define BAUD_RATE_4800 4800 #define BAUD_RATE_9600 9600 @@ -104,9 +220,8 @@ } /** - * @brief Sets a hook function when RX indicate is called - * - * @param thread is the target thread that initializing + * @brief Serial receive indicate hook function type + * */ typedef void (*rt_hw_serial_rxind_hookproto_t)(rt_device_t dev, rt_size_t size); RT_OBJECT_HOOKLIST_DECLARE(rt_hw_serial_rxind_hookproto_t, rt_hw_serial_rxind); @@ -126,8 +241,8 @@ struct serial_configure rt_uint32_t reserved :5; }; -/* - * Serial Receive FIFO mode +/** + * @brief Serial Receive FIFO mode */ struct rt_serial_rx_fifo { @@ -141,8 +256,9 @@ struct rt_serial_rx_fifo rt_uint8_t buffer[]; }; -/* - * Serial Transmit FIFO mode +/** + * @brief Serial Transmit FIFO mode + * */ struct rt_serial_tx_fifo { @@ -158,6 +274,10 @@ struct rt_serial_tx_fifo rt_uint8_t buffer[]; }; +/** + * @brief serial device structure + * + */ struct rt_serial_device { struct rt_device parent; @@ -172,7 +292,8 @@ struct rt_serial_device }; /** - * uart operators + * @brief uart device operations + * */ struct rt_uart_ops { @@ -192,12 +313,43 @@ struct rt_uart_ops rt_uint32_t tx_flag); }; +/** + * @brief Serial interrupt service routine + * @param serial serial device + * @param event event mask + * @ingroup Serial_v2 + */ void rt_hw_serial_isr(struct rt_serial_device *serial, int event); + +/** + * @brief Register a serial device to device list + * + * @param serial serial device + * @param name device name + * @param flag device flag + * @param data device private data + * @return rt_err_t error code + * @note This function will register a serial device to system device list, + * and add a device object to system object list. + * @ingroup Serial_v2 + */ rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, const char *name, rt_uint32_t flag, void *data); +/** + * @brief register a serial device to system device list and add a device object to system object list + * + * @param serial serial device + * @return rt_err_t error code + * + * @ingroup Serial_v2 + */ rt_err_t rt_hw_serial_register_tty(struct rt_serial_device *serial); + + +/*! @}*/ + #endif diff --git a/rt-thread/components/drivers/include/drivers/dev_spi.h b/rt-thread/components/drivers/include/drivers/dev_spi.h new file mode 100644 index 0000000..0aa08c4 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/dev_spi.h @@ -0,0 +1,633 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-11-23 Bernard Add extern "C" + * 2020-06-13 armink fix the 3 wires issue + * 2022-09-01 liYony fix api rt_spi_sendrecv16 about MSB and LSB bug + */ + +#ifndef __DEV_SPI_H__ +#define __DEV_SPI_H__ + +#include +#include +#include +#include + +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup SPI SPI + * + * @brief SPI driver api + * + * Example + * @code {.c} + * #include + * #include + * + * #define W25Q_SPI_DEVICE_NAME "qspi10" + * + * static void spi_w25q_sample(int argc, char *argv[]) + * { + * struct rt_spi_device *spi_dev_w25q; + * char name[RT_NAME_MAX]; + * rt_uint8_t w25x_read_id = 0x90; + * rt_uint8_t id[5] = {0}; + * + * if (argc == 2) + * { + * rt_strncpy(name, argv[1], RT_NAME_MAX); + * } + * else + * { + * rt_strncpy(name, W25Q_SPI_DEVICE_NAME, RT_NAME_MAX); + * } + * + * // 查找 spi 设备获取设备句柄 + * spi_dev_w25q = (struct rt_spi_device *)rt_device_find(name); + * if (!spi_dev_w25q) + * { + * rt_kprintf("spi sample run failed! can't find %s device!\n", name); + * } + * else + * { + * // 方式1:使用 rt_spi_send_then_recv()发送命令读取ID + * rt_spi_send_then_recv(spi_dev_w25q, &w25x_read_id, 1, id, 5); + * rt_kprintf("use rt_spi_send_then_recv() read w25q ID is:%x%x\n", id[3], id[4]); + * + * // 方式2:使用 rt_spi_transfer_message()发送命令读取ID + * struct rt_spi_message msg1, msg2; + * + * msg1.send_buf = &w25x_read_id; + * msg1.recv_buf = RT_NULL; + * msg1.length = 1; + * msg1.cs_take = 1; + * msg1.cs_release = 0; + * msg1.next = &msg2; + * + * msg2.send_buf = RT_NULL; + * msg2.recv_buf = id; + * msg2.length = 5; + * msg2.cs_take = 0; + * msg2.cs_release = 1; + * msg2.next = RT_NULL; + * + * rt_spi_transfer_message(spi_dev_w25q, &msg1); + * rt_kprintf("use rt_spi_transfer_message() read w25q ID is:%x%x\n", id[3], id[4]); + * + * } + * } + * // 导出到 msh 命令列表中 + * MSH_CMD_EXPORT(spi_w25q_sample, spi w25q sample); + * @endcode + * + * @ingroup Drivers + */ + +/*! + * @addtogroup SPI + * @{ + */ +#ifdef __cplusplus +extern "C"{ +#endif + +/** + * At CPOL=0 the base value of the clock is zero + * - For CPHA=0, data are captured on the clock's rising edge (low->high transition) + * and data are propagated on a falling edge (high->low clock transition). + * - For CPHA=1, data are captured on the clock's falling edge and data are + * propagated on a rising edge. + * At CPOL=1 the base value of the clock is one (inversion of CPOL=0) + * - For CPHA=0, data are captured on clock's falling edge and data are propagated + * on a rising edge. + * - For CPHA=1, data are captured on clock's rising edge and data are propagated + * on a falling edge. + */ +#define RT_SPI_CPHA (1<<0) /*!< bit[0]:CPHA, clock phase */ +#define RT_SPI_CPOL (1<<1) /*!< bit[1]:CPOL, clock polarity */ + +#define RT_SPI_LSB (0<<2) /*!< bit[2]: 0-LSB */ +#define RT_SPI_MSB (1<<2) /*!< bit[2]: 1-MSB */ + +#define RT_SPI_MASTER (0<<3) /*!< SPI master device */ +#define RT_SPI_SLAVE (1<<3) /*!< SPI slave device */ + +#define RT_SPI_CS_HIGH (1<<4) /*!< Chipselect active high */ +#define RT_SPI_NO_CS (1<<5) /*!< No chipselect */ +#define RT_SPI_3WIRE (1<<6) /*!< SI/SO pin shared */ +#define RT_SPI_READY (1<<7) /*!< Slave pulls low to pause */ + +#define RT_SPI_MODE_MASK (RT_SPI_CPHA | RT_SPI_CPOL | RT_SPI_MSB | RT_SPI_SLAVE | RT_SPI_CS_HIGH | RT_SPI_NO_CS | RT_SPI_3WIRE | RT_SPI_READY) + +#define RT_SPI_MODE_0 (0 | 0) /*!< CPOL = 0, CPHA = 0 */ +#define RT_SPI_MODE_1 (0 | RT_SPI_CPHA) /*!< CPOL = 0, CPHA = 1 */ +#define RT_SPI_MODE_2 (RT_SPI_CPOL | 0) /*!< CPOL = 1, CPHA = 0 */ +#define RT_SPI_MODE_3 (RT_SPI_CPOL | RT_SPI_CPHA) /*!< CPOL = 1, CPHA = 1 */ + +#define RT_SPI_BUS_MODE_SPI (1<<0) +#define RT_SPI_BUS_MODE_QSPI (1<<1) + +/** + * @brief SPI message structure + */ +struct rt_spi_message +{ + const void *send_buf; + void *recv_buf; + rt_size_t length; + struct rt_spi_message *next; + + unsigned cs_take : 1; + unsigned cs_release : 1; +}; + +/** + * @brief SPI configuration structure + */ +struct rt_spi_configuration +{ + rt_uint8_t mode; + rt_uint8_t data_width; +#ifdef RT_USING_DM + rt_uint8_t data_width_tx; + rt_uint8_t data_width_rx; +#else + rt_uint16_t reserved; +#endif + + rt_uint32_t max_hz; +}; + +struct rt_spi_ops; + +/** + * @brief SPI bus structure + */ +struct rt_spi_bus +{ + struct rt_device parent; + rt_uint8_t mode; + const struct rt_spi_ops *ops; + +#ifdef RT_USING_DM + rt_base_t *pins; + rt_bool_t slave; + int num_chipselect; +#endif /* RT_USING_DM */ + + struct rt_mutex lock; + struct rt_spi_device *owner; +}; + +/** + * @brief SPI operators + */ +struct rt_spi_ops +{ + rt_err_t (*configure)(struct rt_spi_device *device, struct rt_spi_configuration *configuration); + rt_ssize_t (*xfer)(struct rt_spi_device *device, struct rt_spi_message *message); +}; + +#ifdef RT_USING_DM +/** + * @brief SPI delay info + */ +struct rt_spi_delay +{ +#define RT_SPI_DELAY_UNIT_USECS 0 +#define RT_SPI_DELAY_UNIT_NSECS 1 +#define RT_SPI_DELAY_UNIT_SCK 2 + rt_uint16_t value; + rt_uint8_t unit; +}; +#endif /* RT_USING_DM */ + +/** + * @brief SPI Virtual BUS, one device must connected to a virtual BUS + */ +struct rt_spi_device +{ + struct rt_device parent; + struct rt_spi_bus *bus; + +#ifdef RT_USING_DM + const char *name; + const struct rt_spi_device_id *id; + const struct rt_ofw_node_id *ofw_id; + + rt_uint8_t chip_select; + struct rt_spi_delay cs_setup; + struct rt_spi_delay cs_hold; + struct rt_spi_delay cs_inactive; +#endif + + struct rt_spi_configuration config; + rt_base_t cs_pin; + void *user_data; +}; + +/** + * @brief QSPI message structure + */ +struct rt_qspi_message +{ + struct rt_spi_message parent; + + /* instruction stage */ + struct + { + rt_uint8_t content; + rt_uint8_t qspi_lines; + } instruction; + + /* address and alternate_bytes stage */ + struct + { + rt_uint32_t content; + rt_uint8_t size; + rt_uint8_t qspi_lines; + } address, alternate_bytes; + + /* dummy_cycles stage */ + rt_uint32_t dummy_cycles; + + /* number of lines in qspi data stage, the other configuration items are in parent */ + rt_uint8_t qspi_data_lines; +}; + +/** + * @brief QSPI configuration structure + */ +struct rt_qspi_configuration +{ + struct rt_spi_configuration parent; + /* The size of medium */ + rt_uint32_t medium_size; + /* double data rate mode */ + rt_uint8_t ddr_mode; + /* the data lines max width which QSPI bus supported, such as 1, 2, 4 */ + rt_uint8_t qspi_dl_width ; +}; + +/** + * @brief QSPI operators + */ +struct rt_qspi_device +{ + struct rt_spi_device parent; + + struct rt_qspi_configuration config; + + void (*enter_qspi_mode)(struct rt_qspi_device *device); + + void (*exit_qspi_mode)(struct rt_qspi_device *device); +}; + +#define SPI_DEVICE(dev) ((struct rt_spi_device *)(dev)) + +#ifdef RT_USING_DM +struct rt_spi_device_id +{ + char name[20]; + void *data; +}; + +struct rt_spi_driver +{ + struct rt_driver parent; + + const struct rt_spi_device_id *ids; + const struct rt_ofw_node_id *ofw_ids; + + rt_err_t (*probe)(struct rt_spi_device *device); + rt_err_t (*remove)(struct rt_spi_device *device); + rt_err_t (*shutdown)(struct rt_spi_device *device); +}; + +rt_err_t rt_spi_driver_register(struct rt_spi_driver *driver); +rt_err_t rt_spi_device_register(struct rt_spi_device *device); + +#define RT_SPI_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, spi, BUILIN) +#endif /* RT_USING_DM */ + +/** + * @brief register a SPI bus + * + * @param bus the SPI bus + * @param name the name of SPI bus + * @param ops the operations of SPI bus + * + * @return rt_err_t error code + */ +rt_err_t rt_spi_bus_register(struct rt_spi_bus *bus, + const char *name, + const struct rt_spi_ops *ops); + + +/** + * @brief attach a device on SPI bus + * + * @param device the SPI device + * @param name the name of SPI device + * @param bus_name the name of SPI bus + * @param user_data the user data of SPI device + * + * @return rt_err_t error code + */ +rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device, + const char *name, + const char *bus_name, + void *user_data); + + +/** + * @brief attach a device on SPI bus with CS pin + * + * @param device the SPI device + * @param name the name of SPI device + * @param bus_name the name of SPI bus + * @param cs_pin the CS pin of SPI device + * @param user_data the user data of SPI device + * + * @return rt_err_t error code + */ +rt_err_t rt_spi_bus_attach_device_cspin(struct rt_spi_device *device, + const char *name, + const char *bus_name, + rt_base_t cs_pin, + void *user_data); + +/** + * @brief Reconfigure the SPI bus for the specified device. + * + * @param device: Pointer to the SPI device attached to the SPI bus. + * @retval RT_EOK if the SPI device was successfully released and the bus was configured. + * RT_EBUSY if the SPI bus is currently in use; the new configuration will take effect once the device releases the bus. + * Other return values indicate failure to configure the SPI bus due to various reasons. + * @note If the configuration of the SPI device has been updated and requires bus re-initialization, + * call this function directly. This function will reconfigure the SPI bus for the specified device. + * If this is the first time to initialize the SPI device, please call rt_spi_configure or rt_qspi_configure. + * This function is used to reconfigure the SPI bus when the SPI device is already in use. + * For further details, refer to: + * https://github.com/RT-Thread/rt-thread/pull/8528 + */ +rt_err_t rt_spi_bus_configure(struct rt_spi_device *device); + +/** + * @brief This function takes SPI bus. + * + * @param device the SPI device attached to SPI bus + * + * @return RT_EOK on taken SPI bus successfully. others on taken SPI bus failed. + */ +rt_err_t rt_spi_take_bus(struct rt_spi_device *device); + +/** + * @brief This function releases SPI bus. + * + * @param device the SPI device attached to SPI bus + * + * @return RT_EOK on release SPI bus successfully. + */ +rt_err_t rt_spi_release_bus(struct rt_spi_device *device); + +/** + * @brief This function take SPI device (takes CS of SPI device). + * + * @param device the SPI device attached to SPI bus + * + * @return RT_EOK on release SPI bus successfully. others on taken SPI bus failed. + */ +rt_err_t rt_spi_take(struct rt_spi_device *device); + +/** + * @brief This function releases SPI device (releases CS of SPI device). + * + * @param device the SPI device attached to SPI bus + * + * @return RT_EOK on release SPI device successfully. + */ +rt_err_t rt_spi_release(struct rt_spi_device *device); + +/** + * @brief This function can set configuration on SPI device. + * + * @param device: the SPI device attached to SPI bus + * @param cfg: the configuration pointer. + * + * @retval RT_EOK on release SPI device successfully. + * RT_EBUSY is not an error condition and the configuration will take effect once the device has the bus + * others on taken SPI bus failed. + */ +rt_err_t rt_spi_configure(struct rt_spi_device *device, + struct rt_spi_configuration *cfg); + + +/** + * @brief This function can send data then receive data from SPI device. + * + * @param device the SPI device attached to SPI bus + * @param send_buf the buffer to be transmitted to SPI device. + * @param send_length the number of data to be transmitted. + * @param recv_buf the buffer to be recivied from SPI device. + * @param recv_length the data to be recivied. + * + * @return rt_err_t error code + */ +rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device, + const void *send_buf, + rt_size_t send_length, + void *recv_buf, + rt_size_t recv_length); + +/** + * @brief This function can send data then send data from SPI device. + * + * @param device the SPI device attached to SPI bus + * @param send_buf1 the buffer to be transmitted to SPI device. + * @param send_length1 the number of data to be transmitted. + * @param send_buf2 the buffer to be transmitted to SPI device. + * @param send_length2 the number of data to be transmitted. + * + * @return the status of transmit. + */ +rt_err_t rt_spi_send_then_send(struct rt_spi_device *device, + const void *send_buf1, + rt_size_t send_length1, + const void *send_buf2, + rt_size_t send_length2); + +/** + * @brief This function transmits data to SPI device. + * + * @param device the SPI device attached to SPI bus + * @param send_buf the buffer to be transmitted to SPI device. + * @param recv_buf the buffer to save received data from SPI device. + * @param length the length of transmitted data. + * + * @return the actual length of transmitted. + */ +rt_ssize_t rt_spi_transfer(struct rt_spi_device *device, + const void *send_buf, + void *recv_buf, + rt_size_t length); + +/** + * @brief The SPI device transmits 8 bytes of data + * + * @param device the SPI device attached to SPI bus + * @param senddata send data buffer + * @param recvdata receive data buffer + * + * @return rt_err_t error code + */ +rt_err_t rt_spi_sendrecv8(struct rt_spi_device *device, + rt_uint8_t senddata, + rt_uint8_t *recvdata); + +/** + * @brief The SPI device transmits 16 bytes of data + * + * @param device the SPI device attached to SPI bus + * @param senddata send data buffer + * @param recvdata receive data buffer + * + * @return rt_err_t error code + */ +rt_err_t rt_spi_sendrecv16(struct rt_spi_device *device, + rt_uint16_t senddata, + rt_uint16_t *recvdata); + +/** + * @brief This function transfers a message list to the SPI device. + * + * @param device the SPI device attached to SPI bus + * @param message the message list to be transmitted to SPI device + * + * @return RT_NULL if transmits message list successfully, + * SPI message which be transmitted failed. + */ +struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device *device, + struct rt_spi_message *message); + +/** + * @brief This function receives data from SPI device. + * + * @param device the SPI device attached to SPI bus + * @param recv_buf the buffer to be recivied from SPI device. + * @param length the data to be recivied. + * + * @return the actual length of received. +*/ +rt_inline rt_size_t rt_spi_recv(struct rt_spi_device *device, + void *recv_buf, + rt_size_t length) +{ + return rt_spi_transfer(device, RT_NULL, recv_buf, length); +} + +/** + * @brief This function sends data to SPI device. + * + * @param device the SPI device attached to SPI bus + * @param send_buf the buffer to be transmitted to SPI device. + * @param length the number of data to be transmitted. + * + * @return the actual length of send. + */ +rt_inline rt_size_t rt_spi_send(struct rt_spi_device *device, + const void *send_buf, + rt_size_t length) +{ + return rt_spi_transfer(device, send_buf, RT_NULL, length); +} + +/** + * @brief This function appends a message to the SPI message list. + * + * @param list the SPI message list header. + * @param message the message pointer to be appended to the message list. + */ +rt_inline void rt_spi_message_append(struct rt_spi_message *list, + struct rt_spi_message *message) +{ + RT_ASSERT(list != RT_NULL); + if (message == RT_NULL) + return; /* not append */ + + while (list->next != RT_NULL) + { + list = list->next; + } + + list->next = message; + message->next = RT_NULL; +} + +/** + * @brief This function can set configuration on QSPI device. + * + * @param device the QSPI device attached to QSPI bus. + * @param cfg the configuration pointer. + * + * @return the actual length of transmitted. + */ +rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configuration *cfg); + +/** + * @brief This function can register a SPI bus for QSPI mode. + * + * @param bus the SPI bus for QSPI mode. + * @param name The name of the spi bus. + * @param ops the SPI bus instance to be registered. + * + * @return the actual length of transmitted. + */ +rt_err_t rt_qspi_bus_register(struct rt_spi_bus *bus, const char *name, const struct rt_spi_ops *ops); + +/** + * @brief This function transmits data to QSPI device. + * + * @param device the QSPI device attached to QSPI bus. + * @param message the message pointer. + * + * @return the actual length of transmitted. + */ +rt_size_t rt_qspi_transfer_message(struct rt_qspi_device *device, struct rt_qspi_message *message); + +/** + * @brief This function can send data then receive data from QSPI device + * + * @param device the QSPI device attached to QSPI bus. + * @param send_buf the buffer to be transmitted to QSPI device. + * @param send_length the number of data to be transmitted. + * @param recv_buf the buffer to be recivied from QSPI device. + * @param recv_length the data to be recivied. + * + * @return the status of transmit. + */ +rt_err_t rt_qspi_send_then_recv(struct rt_qspi_device *device, const void *send_buf, rt_size_t send_length,void *recv_buf, rt_size_t recv_length); + +/** + * @brief This function can send data to QSPI device + * + * @param device the QSPI device attached to QSPI bus. + * @param send_buf the buffer to be transmitted to QSPI device. + * @param length the number of data to be transmitted. + * + * @return the status of transmit. + */ +rt_err_t rt_qspi_send(struct rt_qspi_device *device, const void *send_buf, rt_size_t length); + +#ifdef __cplusplus +} +#endif + +/*! @}*/ + +#endif diff --git a/rt-thread/components/drivers/include/drivers/touch.h b/rt-thread/components/drivers/include/drivers/dev_touch.h similarity index 53% rename from rt-thread/components/drivers/include/drivers/touch.h rename to rt-thread/components/drivers/include/drivers/dev_touch.h index aa29749..ba01ae4 100644 --- a/rt-thread/components/drivers/include/drivers/touch.h +++ b/rt-thread/components/drivers/include/drivers/dev_touch.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -8,12 +8,117 @@ * 2019-05-20 tyustli the first version */ -#ifndef __TOUCH_H__ -#define __TOUCH_H__ +#ifndef __DEV_TOUCH_H__ +#define __DEV_TOUCH_H__ #include -#include "pin.h" +#include "dev_pin.h" +/** + * @addtogroup Drivers RTTHREAD Driver + * @defgroup Touch Touch + * + * @brief Touch driver api + * + * Example + * @code {.c} + * #include + * #include "rtdevice.h" + * + * static rt_thread_t gt9147_thread = RT_NULL; + * static rt_sem_t gt9147_sem = RT_NULL; + * static rt_device_t dev = RT_NULL; + * static struct rt_touch_data *read_data; + * + * // 读取数据线程入口函数 + * static void gt9147_entry(void *parameter) + * { + * struct rt_touch_data *read_data; + * read_data = (struct rt_touch_data *)rt_malloc(sizeof(struct rt_touch_data) * 5); + * + * while (1) + * { + * // 请求信号量 + * rt_sem_take(gt9147_sem, RT_WAITING_FOREVER); + * // 读取五个点的触摸信息 + * if (rt_device_read(dev, 0, read_data, 5) == 5) + * { + * for (rt_uint8_t i = 0; i < 5; i++) + * { + * if (read_data[i].event == RT_TOUCH_EVENT_DOWN || read_data[i].event == RT_TOUCH_EVENT_MOVE) + * { + * rt_kprintf("%d %d %d %d %d\n", + * read_data[i].track_id, + * read_data[i].x_coordinate, + * read_data[i].y_coordinate, + * read_data[i].timestamp, + * read_data[i].width); + * } + * } + * } + * // 打开中断 + * rt_device_control(dev, RT_TOUCH_CTRL_ENABLE_INT, RT_NULL); + * } + * } + * + * // 接收回调函数 + * static rt_err_t rx_callback(rt_device_t dev, rt_size_t size) + * { + * // 关闭中断 + * rt_device_control(dev, RT_TOUCH_CTRL_DISABLE_INT, RT_NULL); + * // 释放信号量 + * rt_sem_release(gt9147_sem); + * return 0; + * } + * + * static int gt9147_sample(void) + * { + * // 查找 Touch 设备 + * dev = rt_device_find("touch"); + * + * if (dev == RT_NULL) + * { + * rt_kprintf("can't find device:%s\n", "touch"); + * return -1; + * } + * // 以中断的方式打开设备 + * if (rt_device_open(dev, RT_DEVICE_FLAG_INT_RX) != RT_EOK) + * { + * rt_kprintf("open device failed!"); + * return -1; + * } + * // 设置接收回调 + * rt_device_set_rx_indicate(dev, rx_callback); + * // 创建信号量 + * gt9147_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO); + * + * if (gt9147_sem == RT_NULL) + * { + * rt_kprintf("create dynamic semaphore failed.\n"); + * return -1; + * } + * // 创建读取数据线程 + * gt9147_thread = rt_thread_create("thread1", + * gt9147_entry, + * RT_NULL, + * THREAD_STACK_SIZE, + * THREAD_PRIORITY, + * THREAD_TIMESLICE); + * // 启动线程 + * if (gt9147_thread != RT_NULL) + * rt_thread_startup(gt9147_thread); + * + * return 0; + * } + * MSH_CMD_EXPORT(gt9147_sample, gt9147 sample); + * @endcode + * + * @ingroup Drivers + */ +/*! + * @addtogroup Touch + * @{ + */ #ifdef __cplusplus extern "C" { #endif @@ -53,6 +158,9 @@ extern "C" { #define RT_TOUCH_EVENT_DOWN (2) /* Touch down event */ #define RT_TOUCH_EVENT_MOVE (3) /* Touch move event */ +/** + * @brief Touch information +*/ struct rt_touch_info { rt_uint8_t type; /* The touch type */ @@ -62,6 +170,9 @@ struct rt_touch_info rt_int32_t range_y; /* Y coordinate range */ }; +/** + * @brief Touch configuration +*/ struct rt_touch_config { #ifdef RT_TOUCH_PIN_IRQ @@ -72,6 +183,9 @@ struct rt_touch_config }; typedef struct rt_touch_device *rt_touch_t; +/** + * @brief Touch device +*/ struct rt_touch_device { struct rt_device parent; /* The standard device */ @@ -82,6 +196,9 @@ struct rt_touch_device rt_err_t (*irq_handle)(rt_touch_t touch); /* Called when an interrupt is generated, registered by the driver */ }; +/** + * @brief Touch data +*/ struct rt_touch_data { rt_uint8_t event; /* The touch event of the data */ @@ -92,22 +209,40 @@ struct rt_touch_data rt_tick_t timestamp; /* The timestamp when the data was received */ }; +/** + * @brief Touch device operations +*/ struct rt_touch_ops { rt_size_t (*touch_readpoint)(struct rt_touch_device *touch, void *buf, rt_size_t touch_num); rt_err_t (*touch_control)(struct rt_touch_device *touch, int cmd, void *arg); }; +/** + * @brief register a touch device + * @param touch the touch device + * @param name the name of touch device + * @param flag the flag of touch device + * @param data the user data of touch device + * @return rt_err_t error code + */ int rt_hw_touch_register(rt_touch_t touch, const char *name, rt_uint32_t flag, void *data); -/* if you doesn't use pin device. you must call this function in your touch irq callback */ +/** + * @brief Touch irq handle + * @param touch the touch device + * + * @note If you doesn't use pin device. you must call this function in your touch irq callback + */ void rt_hw_touch_isr(rt_touch_t touch); #ifdef __cplusplus } #endif -#endif /* __TOUCH_H__ */ +/*! @}*/ + +#endif /* __DEV_TOUCH_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/watchdog.h b/rt-thread/components/drivers/include/drivers/dev_watchdog.h similarity index 94% rename from rt-thread/components/drivers/include/drivers/watchdog.h rename to rt-thread/components/drivers/include/drivers/dev_watchdog.h index e08d35d..4b5614f 100644 --- a/rt-thread/components/drivers/include/drivers/watchdog.h +++ b/rt-thread/components/drivers/include/drivers/dev_watchdog.h @@ -8,8 +8,8 @@ * 2012-09-12 heyuanjie87 first version. */ -#ifndef __WATCHDOG_H__ -#define __WATCHDOG_H__ +#ifndef __DEV_WATCHDOG_H__ +#define __DEV_WATCHDOG_H__ #include @@ -39,4 +39,4 @@ rt_err_t rt_hw_watchdog_register(rt_watchdog_t *wdt, rt_uint32_t flag, void *data); -#endif /* __WATCHDOG_H__ */ +#endif /* __DEV_WATCHDOG_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/dma.h b/rt-thread/components/drivers/include/drivers/dma.h new file mode 100644 index 0000000..3128c67 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/dma.h @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __DMA_H__ +#define __DMA_H__ + +#include +#include +#include +#include + +#include +#include +#include + +struct rt_dma_chan; +struct rt_dma_controller_ops; + +enum rt_dma_transfer_direction +{ + RT_DMA_MEM_TO_MEM, + RT_DMA_MEM_TO_DEV, + RT_DMA_DEV_TO_MEM, + RT_DMA_DEV_TO_DEV, + + RT_DMA_DIR_MAX, +}; + +enum rt_dma_slave_buswidth +{ + RT_DMA_SLAVE_BUSWIDTH_UNDEFINED = 0, + RT_DMA_SLAVE_BUSWIDTH_1_BYTE = 1, + RT_DMA_SLAVE_BUSWIDTH_2_BYTES = 2, + RT_DMA_SLAVE_BUSWIDTH_3_BYTES = 3, + RT_DMA_SLAVE_BUSWIDTH_4_BYTES = 4, + RT_DMA_SLAVE_BUSWIDTH_8_BYTES = 8, + RT_DMA_SLAVE_BUSWIDTH_16_BYTES = 16, + RT_DMA_SLAVE_BUSWIDTH_32_BYTES = 32, + RT_DMA_SLAVE_BUSWIDTH_64_BYTES = 64, + RT_DMA_SLAVE_BUSWIDTH_128_BYTES = 128, + + RT_DMA_SLAVE_BUSWIDTH_BYTES_MAX, +}; + +struct rt_dma_slave_config +{ + enum rt_dma_transfer_direction direction; + enum rt_dma_slave_buswidth src_addr_width; + enum rt_dma_slave_buswidth dst_addr_width; + + rt_ubase_t src_addr; + rt_ubase_t dst_addr; + + rt_uint32_t src_maxburst; + rt_uint32_t dst_maxburst; + rt_uint32_t src_port_window_size; + rt_uint32_t dst_port_window_size; +}; + +struct rt_dma_slave_transfer +{ + rt_ubase_t src_addr; + rt_ubase_t dst_addr; + + void *buffer; + rt_ubase_t dma_handle; + rt_size_t buffer_len; + rt_size_t period_len; +}; + +struct rt_dma_controller +{ + rt_list_t list; + + struct rt_device *dev; + + RT_BITMAP_DECLARE(dir_cap, RT_DMA_DIR_MAX); + const struct rt_dma_controller_ops *ops; + + rt_list_t channels_nodes; + struct rt_mutex mutex; +}; + +struct rt_dma_controller_ops +{ + struct rt_dma_chan *(*request_chan)(struct rt_dma_controller *ctrl, + struct rt_device *slave, void *fw_data); + rt_err_t (*release_chan)(struct rt_dma_chan *chan); + + rt_err_t (*start)(struct rt_dma_chan *chan); + rt_err_t (*stop)(struct rt_dma_chan *chan); + rt_err_t (*config)(struct rt_dma_chan *chan, struct rt_dma_slave_config *conf); + + rt_err_t (*prep_memcpy)(struct rt_dma_chan *chan, + rt_ubase_t dma_addr_src, rt_ubase_t dma_addr_dst, rt_size_t len); + + rt_err_t (*prep_cyclic)(struct rt_dma_chan *chan, + rt_ubase_t dma_buf_addr, rt_size_t buf_len, rt_size_t period_len, + enum rt_dma_transfer_direction dir); + + rt_err_t (*prep_single)(struct rt_dma_chan *chan, + rt_ubase_t dma_buf_addr, rt_size_t buf_len, + enum rt_dma_transfer_direction dir); +}; + +struct rt_dma_chan +{ + const char *name; + + struct rt_dma_controller *ctrl; + struct rt_device *slave; + + rt_list_t list; + rt_err_t conf_err; + rt_err_t prep_err; + struct rt_dma_slave_config conf; + struct rt_dma_slave_transfer transfer; + + void (*callback)(struct rt_dma_chan *chan, rt_size_t size); + + void *priv; +}; + +struct rt_dma_pool +{ + rt_region_t region; + + rt_list_t list; + + rt_ubase_t flags; + + rt_bitmap_t *map; + rt_size_t bits; + rt_ubase_t start; + + struct rt_device *dev; +}; + +struct rt_dma_map_ops +{ + void *(*alloc)(struct rt_device *dev, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags); + void (*free)(struct rt_device *dev, rt_size_t size, + void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags); + rt_err_t (*sync_out_data)(struct rt_device *dev, void *data, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags); + rt_err_t (*sync_in_data)(struct rt_device *dev, void *out_data, rt_size_t size, + rt_ubase_t dma_handle, rt_ubase_t flags); +}; + +rt_inline void rt_dma_controller_add_direction(struct rt_dma_controller *ctrl, + enum rt_dma_transfer_direction dir) +{ + RT_ASSERT(ctrl != RT_NULL); + RT_ASSERT(dir < RT_DMA_DIR_MAX); + + rt_bitmap_set_bit(ctrl->dir_cap, dir); +} + +rt_err_t rt_dma_controller_register(struct rt_dma_controller *ctrl); +rt_err_t rt_dma_controller_unregister(struct rt_dma_controller *ctrl); + +rt_err_t rt_dma_chan_start(struct rt_dma_chan *chan); +rt_err_t rt_dma_chan_stop(struct rt_dma_chan *chan); +rt_err_t rt_dma_chan_config(struct rt_dma_chan *chan, + struct rt_dma_slave_config *conf); +rt_err_t rt_dma_chan_done(struct rt_dma_chan *chan, rt_size_t size); + +rt_err_t rt_dma_prep_memcpy(struct rt_dma_chan *chan, + struct rt_dma_slave_transfer *transfer); +rt_err_t rt_dma_prep_cyclic(struct rt_dma_chan *chan, + struct rt_dma_slave_transfer *transfer); +rt_err_t rt_dma_prep_single(struct rt_dma_chan *chan, + struct rt_dma_slave_transfer *transfer); + +struct rt_dma_chan *rt_dma_chan_request(struct rt_device *dev, const char *name); +rt_err_t rt_dma_chan_release(struct rt_dma_chan *chan); + +#define RT_DMA_F_LINEAR RT_BIT(0) +#define RT_DMA_F_32BITS RT_BIT(1) +#define RT_DMA_F_NOCACHE RT_BIT(2) +#define RT_DMA_F_DEVICE RT_BIT(3) +#define RT_DMA_F_NOMAP RT_BIT(4) + +#define RT_DMA_PAGE_SIZE ARCH_PAGE_SIZE + +void *rt_dma_alloc(struct rt_device *dev, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags); + +void rt_dma_free(struct rt_device *dev, rt_size_t size, + void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags); + +rt_inline void *rt_dma_alloc_coherent(struct rt_device *dev, rt_size_t size, + rt_ubase_t *dma_handle) +{ + return rt_dma_alloc(dev, size, dma_handle, + RT_DMA_F_NOCACHE | RT_DMA_F_LINEAR); +} + +rt_inline void rt_dma_free_coherent(struct rt_device *dev, rt_size_t size, + void *cpu_addr, rt_ubase_t dma_handle) +{ + rt_dma_free(dev, size, cpu_addr, dma_handle, + RT_DMA_F_NOCACHE | RT_DMA_F_LINEAR); +} + +rt_err_t rt_dma_sync_out_data(struct rt_device *dev, void *data, rt_size_t size, + rt_ubase_t *dma_handle, rt_ubase_t flags); +rt_err_t rt_dma_sync_in_data(struct rt_device *dev, void *out_data, rt_size_t size, + rt_ubase_t dma_handle, rt_ubase_t flags); + +rt_inline rt_bool_t rt_dma_device_is_coherent(struct rt_device *dev) +{ + return rt_dm_dev_prop_read_bool(dev, "dma-coherent"); +} + +rt_inline void rt_dma_device_set_ops(struct rt_device *dev, + const struct rt_dma_map_ops *ops) +{ + dev->dma_ops = ops; +} + +struct rt_dma_pool *rt_dma_pool_install(rt_region_t *region); +rt_err_t rt_dma_pool_extract(rt_region_t *region_list, rt_size_t list_len, + rt_size_t cma_size, rt_size_t coherent_pool_size); + +#endif /* __DMA_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/gpt.h b/rt-thread/components/drivers/include/drivers/gpt.h deleted file mode 100644 index b887847..0000000 --- a/rt-thread/components/drivers/include/drivers/gpt.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2022-05-05 linzhenxing first version - */ -#ifndef __GPT_H -#define __GPT_H - -#include -#include - -typedef struct -{ - uint8_t b[16]; /* GUID 16 bytes*/ -} guid_t; - -#define MSDOS_MBR_SIGNATURE 0xaa55 -#define EFI_PMBR_OSTYPE_EFI 0xEF -#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE - -#define GPT_MBR_PROTECTIVE 1 -#define GPT_MBR_HYBRID 2 - -#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL -#define GPT_HEADER_REVISION_V1 0x00010000 -#define GPT_PRIMARY_PARTITION_TABLE_LBA 1 - -typedef guid_t gpt_guid_t __attribute__ ((aligned (4))); -#define EFI_GUID(a, b, c, d...) (gpt_guid_t){ { \ - (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ - (b) & 0xff, ((b) >> 8) & 0xff, \ - (c) & 0xff, ((c) >> 8) & 0xff, d } } - -#define NULL_GUID \ - EFI_GUID(0x00000000, 0x0000, 0x0000,\ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) -#define PARTITION_SYSTEM_GUID \ - EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \ - 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) -#define LEGACY_MBR_PARTITION_GUID \ - EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \ - 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F) -#define PARTITION_MSFT_RESERVED_GUID \ - EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \ - 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE) -#define PARTITION_BASIC_DATA_GUID \ - EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \ - 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7) -#define PARTITION_LINUX_RAID_GUID \ - EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \ - 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e) -#define PARTITION_LINUX_SWAP_GUID \ - EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \ - 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f) -#define PARTITION_LINUX_LVM_GUID \ - EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \ - 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28) -#pragma pack(push, 1) -typedef struct _gpt_header -{ - uint64_t signature; - uint32_t revision; - uint32_t header_size; - uint32_t header_crc32; - uint32_t reserved1; - uint64_t start_lba; /*GPT head start sector*/ - uint64_t alternate_lba; /*GPT head alternate sector*/ - uint64_t first_usable_lba; - uint64_t last_usable_lba; - gpt_guid_t disk_guid; - uint64_t partition_entry_lba; - uint32_t num_partition_entries; - uint32_t sizeof_partition_entry; - uint32_t partition_entry_array_crc32; - - /* The rest of the logical block is reserved by UEFI and must be zero. - * EFI standard handles this by: - * - * uint8_t reserved2[ BlockSize - 92 ]; - */ -} gpt_header; - -typedef struct _gpt_entry_attributes -{ - uint64_t required_to_function:1; - uint64_t reserved:47; - uint64_t type_guid_specific:16; -} gpt_entry_attributes; - -typedef struct _gpt_entry -{ - gpt_guid_t partition_type_guid; - gpt_guid_t unique_partition_guid; - uint64_t starting_lba; - uint64_t ending_lba; - gpt_entry_attributes attributes; - uint16_t partition_name[72/sizeof(uint16_t)]; -} gpt_entry; - -typedef struct _gpt_mbr_record -{ - uint8_t boot_indicator; /* unused by EFI, set to 0x80 for bootable */ - uint8_t start_head; /* unused by EFI, pt start in CHS */ - uint8_t start_sector; /* unused by EFI, pt start in CHS */ - uint8_t start_track; - uint8_t os_type; /* EFI and legacy non-EFI OS types */ - uint8_t end_head; /* unused by EFI, pt end in CHS */ - uint8_t end_sector; /* unused by EFI, pt end in CHS */ - uint8_t end_track; /* unused by EFI, pt end in CHS */ - uint32_t starting_lba; /* used by EFI - start addr of the on disk pt */ - uint32_t size_in_lba; /* used by EFI - size of pt in LBA */ -} gpt_mbr_record; - - -typedef struct _legacy_mbr -{ - uint8_t boot_code[440]; - uint32_t unique_mbr_signature; - uint16_t unknown; - gpt_mbr_record partition_record[4]; - uint16_t signature; -} legacy_mbr; -#pragma pack(pop) - -int check_gpt(struct rt_mmcsd_card *card); -int gpt_get_partition_param(struct rt_mmcsd_card *card, struct dfs_partition *part, uint32_t pindex); -void gpt_free(void); -#endif /*__GPT_H*/ diff --git a/rt-thread/components/drivers/include/drivers/i2c.h b/rt-thread/components/drivers/include/drivers/i2c.h deleted file mode 100644 index 0473a1e..0000000 --- a/rt-thread/components/drivers/include/drivers/i2c.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2012-04-25 weety first version - * 2021-04-20 RiceChen added support for bus control api - */ - -#ifndef __I2C_H__ -#define __I2C_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define RT_I2C_WR 0x0000 -#define RT_I2C_RD (1u << 0) -#define RT_I2C_ADDR_10BIT (1u << 2) /* this is a ten bit chip address */ -#define RT_I2C_NO_START (1u << 4) -#define RT_I2C_IGNORE_NACK (1u << 5) -#define RT_I2C_NO_READ_ACK (1u << 6) /* when I2C reading, we do not ACK */ -#define RT_I2C_NO_STOP (1u << 7) - -struct rt_i2c_msg -{ - rt_uint16_t addr; - rt_uint16_t flags; - rt_uint16_t len; - rt_uint8_t *buf; -}; - -struct rt_i2c_bus_device; - -struct rt_i2c_bus_device_ops -{ - rt_ssize_t (*master_xfer)(struct rt_i2c_bus_device *bus, - struct rt_i2c_msg msgs[], - rt_uint32_t num); - rt_ssize_t (*slave_xfer)(struct rt_i2c_bus_device *bus, - struct rt_i2c_msg msgs[], - rt_uint32_t num); - rt_err_t (*i2c_bus_control)(struct rt_i2c_bus_device *bus, - int cmd, - void *args); -}; - -/*for i2c bus driver*/ -struct rt_i2c_bus_device -{ - struct rt_device parent; - const struct rt_i2c_bus_device_ops *ops; - rt_uint16_t flags; - struct rt_mutex lock; - rt_uint32_t timeout; - rt_uint32_t retries; - void *priv; -}; - -struct rt_i2c_client -{ -#ifdef RT_USING_DM - struct rt_device parent; - - const char *name; - const struct rt_i2c_device_id *id; - const struct rt_ofw_node_id *ofw_id; -#endif - struct rt_i2c_bus_device *bus; - rt_uint16_t client_addr; -}; - -#ifdef RT_USING_DM -struct rt_i2c_device_id -{ - char name[20]; - void *data; -}; - -struct rt_i2c_driver -{ - struct rt_driver parent; - - const struct rt_i2c_device_id *ids; - const struct rt_ofw_node_id *ofw_ids; - - rt_err_t (*probe)(struct rt_i2c_client *client); - rt_err_t (*remove)(struct rt_i2c_client *client); - rt_err_t (*shutdown)(struct rt_i2c_client *client); -}; - -rt_err_t rt_i2c_driver_register(struct rt_i2c_driver *driver); -rt_err_t rt_i2c_device_register(struct rt_i2c_client *client); - -#define RT_I2C_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, i2c, BUILIN) -#endif /* RT_USING_DM */ - -rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus, - const char *bus_name); -struct rt_i2c_bus_device *rt_i2c_bus_device_find(const char *bus_name); -rt_ssize_t rt_i2c_transfer(struct rt_i2c_bus_device *bus, - struct rt_i2c_msg msgs[], - rt_uint32_t num); -rt_err_t rt_i2c_control(struct rt_i2c_bus_device *bus, - int cmd, - void *args); -rt_ssize_t rt_i2c_master_send(struct rt_i2c_bus_device *bus, - rt_uint16_t addr, - rt_uint16_t flags, - const rt_uint8_t *buf, - rt_uint32_t count); -rt_ssize_t rt_i2c_master_recv(struct rt_i2c_bus_device *bus, - rt_uint16_t addr, - rt_uint16_t flags, - rt_uint8_t *buf, - rt_uint32_t count); - -rt_inline rt_err_t rt_i2c_bus_lock(struct rt_i2c_bus_device *bus, rt_tick_t timeout) -{ - return rt_mutex_take(&bus->lock, timeout); -} - -rt_inline rt_err_t rt_i2c_bus_unlock(struct rt_i2c_bus_device *bus) -{ - return rt_mutex_release(&bus->lock); -} - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/rt-thread/components/drivers/include/drivers/i2c_dev.h b/rt-thread/components/drivers/include/drivers/i2c_dev.h deleted file mode 100644 index 6744fa1..0000000 --- a/rt-thread/components/drivers/include/drivers/i2c_dev.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2012-04-25 weety first version - * 2021-04-20 RiceChen added bus clock command - */ - -#ifndef __I2C_DEV_H__ -#define __I2C_DEV_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define RT_I2C_DEV_CTRL_10BIT (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x01) -#define RT_I2C_DEV_CTRL_ADDR (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x02) -#define RT_I2C_DEV_CTRL_TIMEOUT (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x03) -#define RT_I2C_DEV_CTRL_RW (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x04) -#define RT_I2C_DEV_CTRL_CLK (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x05) -#define RT_I2C_DEV_CTRL_UNLOCK (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x06) -#define RT_I2C_DEV_CTRL_GET_STATE (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x07) -#define RT_I2C_DEV_CTRL_GET_MODE (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x08) -#define RT_I2C_DEV_CTRL_GET_ERROR (RT_DEVICE_CTRL_BASE(I2CBUS) + 0x09) - -struct rt_i2c_priv_data -{ - struct rt_i2c_msg *msgs; - rt_size_t number; -}; - -rt_err_t rt_i2c_bus_device_device_init(struct rt_i2c_bus_device *bus, - const char *name); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/rt-thread/components/drivers/include/drivers/iio.h b/rt-thread/components/drivers/include/drivers/iio.h new file mode 100644 index 0000000..c69f507 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/iio.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#ifndef __IIO_H__ +#define __IIO_H__ + +void *rt_iio_channel_get_by_index(struct rt_device *dev, int index, int *out_channel); +void *rt_iio_channel_get_by_name(struct rt_device *dev, const char *name, int *out_channel); + +#endif /* __IIO_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/led.h b/rt-thread/components/drivers/include/drivers/led.h new file mode 100644 index 0000000..aeb80a6 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/led.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#ifndef __LED_H__ +#define __LED_H__ + +#include +#include + +struct rt_led_ops; + +enum rt_led_state +{ + RT_LED_S_OFF, + RT_LED_S_ON, + RT_LED_S_TOGGLE, + RT_LED_S_BLINK, + + RT_LED_STATE_NR, +}; + +struct rt_led_device +{ + struct rt_device parent; + + const struct rt_led_ops *ops; + + struct rt_spinlock spinlock; + + void *sysdata; + void *priv; +}; + +struct rt_led_ops +{ + rt_err_t (*set_state)(struct rt_led_device *led, enum rt_led_state state); + rt_err_t (*get_state)(struct rt_led_device *led, enum rt_led_state *out_state); + rt_err_t (*set_period)(struct rt_led_device *led, rt_uint32_t period_ms); + rt_err_t (*set_brightness)(struct rt_led_device *led, rt_uint32_t brightness); +}; + +rt_err_t rt_hw_led_register(struct rt_led_device *led); +rt_err_t rt_hw_led_unregister(struct rt_led_device *led); + +rt_err_t rt_led_set_state(struct rt_led_device *led, enum rt_led_state state); +rt_err_t rt_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state); +rt_err_t rt_led_set_period(struct rt_led_device *led, rt_uint32_t period_ms); +rt_err_t rt_led_set_brightness(struct rt_led_device *led, rt_uint32_t brightness); + +#endif /* __LED_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/mailbox.h b/rt-thread/components/drivers/include/drivers/mailbox.h new file mode 100644 index 0000000..954cd62 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/mailbox.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __MAILBOX_H__ +#define __MAILBOX_H__ + +#include +#include + +struct rt_mbox_chan; +struct rt_mbox_client; +struct rt_mbox_controller_ops; + +struct rt_mbox_controller +{ + rt_list_t list; + + struct rt_device *dev; + + const struct rt_mbox_controller_ops *ops; + + rt_size_t num_chans; + struct rt_mbox_chan *chans; +}; + +struct rt_mbox_controller_ops +{ + rt_err_t (*request)(struct rt_mbox_chan *); + void (*release)(struct rt_mbox_chan *); + rt_err_t (*send)(struct rt_mbox_chan *, const void *data); + rt_bool_t (*peek)(struct rt_mbox_chan *); + int (*ofw_parse)(struct rt_mbox_controller *, struct rt_ofw_cell_args *); +}; + +struct rt_mbox_chan +{ + struct rt_mbox_controller *ctrl; + struct rt_mbox_client *client; + + void *data; + rt_bool_t complete; + struct rt_timer timer; + struct rt_spinlock lock; + + void *priv; +}; + +struct rt_mbox_client +{ + struct rt_device *dev; + + void (*rx_callback)(struct rt_mbox_client *, void *data); + void (*tx_prepare)(struct rt_mbox_client *, const void *data); + void (*tx_done)(struct rt_mbox_client *, const void *data, rt_err_t err); +}; + +rt_err_t rt_mbox_controller_register(struct rt_mbox_controller *ctrl); +rt_err_t rt_mbox_controller_unregister(struct rt_mbox_controller *ctrl); + +rt_err_t rt_mbox_send(struct rt_mbox_chan *chan, const void *data, + rt_uint32_t timeout_ms); +void rt_mbox_send_done(struct rt_mbox_chan *chan, rt_err_t err); +rt_bool_t rt_mbox_peek(struct rt_mbox_chan *chan); +rt_err_t rt_mbox_recv(struct rt_mbox_chan *chan, void *data); + +struct rt_mbox_chan *rt_mbox_request_by_index(struct rt_mbox_client *client, int index); +struct rt_mbox_chan *rt_mbox_request_by_name(struct rt_mbox_client *client, char *name); +rt_err_t rt_mbox_release(struct rt_mbox_chan *chan); + +#endif /* __MAILBOX_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/misc.h b/rt-thread/components/drivers/include/drivers/misc.h index aa2b715..ea962a2 100644 --- a/rt-thread/components/drivers/include/drivers/misc.h +++ b/rt-thread/components/drivers/include/drivers/misc.h @@ -12,6 +12,7 @@ #define __MISC_H__ #include +#include #ifdef ARCH_CPU_64BIT #define RT_BITS_PER_LONG 64 @@ -33,6 +34,15 @@ (((__x) - ((__d) / 2)) / (__d)); \ }) +#define __KEY_PLACEHOLDER_1 0, +#define ____KEY_ENABLED(__ignored, val, ...) val +#define ___KEY_ENABLED(arg1_or_junk) ____KEY_ENABLED(arg1_or_junk 1, 0) +#define __KEY_ENABLED(value) ___KEY_ENABLED(__KEY_PLACEHOLDER_##value) +#define RT_KEY_ENABLED(key) __KEY_ENABLED(key) + +#define RT_FIELD_PREP(mask, val) (((rt_uint64_t)(val) << (__rt_ffsl((mask)) - 1)) & (mask)) +#define RT_FIELD_GET(mask, val) (((val) & (mask)) >> (__rt_ffsl((mask)) - 1)) + #define RT_BIT(n) (1UL << (n)) #define RT_BIT_ULL(n) (1ULL << (n)) #define RT_BIT_MASK(nr) (1UL << ((nr) % RT_BITS_PER_LONG)) @@ -48,6 +58,19 @@ #define RT_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) +#define rt_offsetof(s, field) ((rt_size_t)&((s *)0)->field) + +#define rt_err_ptr(err) ((void *)(rt_base_t)(err)) +#define rt_ptr_err(ptr) ((rt_err_t)(rt_base_t)(ptr)) +#define rt_is_err_value(ptr) ((rt_ubase_t)(void *)(ptr) >= (rt_ubase_t)-4095) +#define rt_is_err(ptr) rt_is_err_value(ptr) +#define rt_is_err_or_null(ptr) (!(ptr) || rt_is_err_value((rt_ubase_t)(ptr))) + +#define rt_upper_32_bits(n) ((rt_uint32_t)(((n) >> 16) >> 16)) +#define rt_lower_32_bits(n) ((rt_uint32_t)((n) & 0xffffffff)) +#define rt_upper_16_bits(n) ((rt_uint16_t)((n) >> 16)) +#define rt_lower_16_bits(n) ((rt_uint16_t)((n) & 0xffff)) + #define rt_min(x, y) \ ({ \ typeof(x) _x = (x); \ @@ -71,6 +94,13 @@ _x < _y ? _x: _y; \ }) +#define rt_max_t(type, x, y) \ +({ \ + type _x = (x); \ + type _y = (y); \ + _x > _y ? _x: _y; \ +}) + #define rt_clamp(val, lo, hi) rt_min((typeof(val))rt_max(val, lo), hi) #define rt_do_div(n, base) \ @@ -83,4 +113,34 @@ _rem; \ }) +#define rt_abs(x) \ +({ \ + long ret; \ + if (sizeof(x) == sizeof(long)) \ + { \ + long __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } \ + else \ + { \ + int __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } \ + ret; \ +}) + +#ifndef rt_ilog2 +rt_inline int rt_ilog2(rt_ubase_t v) +{ + int l = 0; + + while ((1UL << l) < v) + { + l++; + } + + return l; +} +#endif /* !rt_ilog2 */ + #endif /* __MISC_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/mmcsd_card.h b/rt-thread/components/drivers/include/drivers/mmcsd_card.h index 13173f1..6d86760 100644 --- a/rt-thread/components/drivers/include/drivers/mmcsd_card.h +++ b/rt-thread/components/drivers/include/drivers/mmcsd_card.h @@ -218,7 +218,7 @@ struct rt_mmcsd_card { struct rt_sdio_cccr cccr; /* common card info */ struct rt_sdio_cis cis; /* common tuple info */ struct rt_sdio_function *sdio_function[SDIO_MAX_FUNCTIONS + 1]; /* SDIO functions (devices) */ - rt_list_t blk_devices; /* for block device list */ + void *blk_dev; struct rt_mmc_ext_csd ext_csd; }; diff --git a/rt-thread/components/drivers/include/drivers/mmcsd_host.h b/rt-thread/components/drivers/include/drivers/mmcsd_host.h index 73c1b97..5632ee1 100644 --- a/rt-thread/components/drivers/include/drivers/mmcsd_host.h +++ b/rt-thread/components/drivers/include/drivers/mmcsd_host.h @@ -134,6 +134,7 @@ struct rt_mmcsd_host #define MMCSD_SUP_HS200_1V2 (1 << 10) #define MMCSD_SUP_HS200 (MMCSD_SUP_HS200_1V2 | MMCSD_SUP_HS200_1V8) /* hs200 sdr */ #define MMCSD_SUP_NONREMOVABLE (1 << 11) +#define controller_is_removable(host) (!(host->flags & MMCSD_SUP_NONREMOVABLE)) #define MMCSD_SUP_HS400_1V8 (1 << 12) #define MMCSD_SUP_HS400_1V2 (1 << 13) #define MMCSD_SUP_HS400 (MMCSD_SUP_HS400_1V2 | MMCSD_SUP_HS400_1V8) /* hs400 ddr */ diff --git a/rt-thread/components/drivers/include/drivers/nvme.h b/rt-thread/components/drivers/include/drivers/nvme.h new file mode 100644 index 0000000..1eca5d9 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/nvme.h @@ -0,0 +1,899 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __NVME_H__ +#define __NVME_H__ + +#include +#include +#include + +#define NVME_RSVD(offset, bytes_size) rt_uint8_t __rsvd##offset[bytes_size] + +enum +{ + /* + * Generic Command Status: + */ + RT_NVME_SC_SUCCESS = 0x0, + RT_NVME_SC_INVALID_OPCODE = 0x1, + RT_NVME_SC_INVALID_FIELD = 0x2, + RT_NVME_SC_CMDID_CONFLICT = 0x3, + RT_NVME_SC_DATA_XFER_ERROR = 0x4, + RT_NVME_SC_POWER_LOSS = 0x5, + RT_NVME_SC_INTERNAL = 0x6, + RT_NVME_SC_ABORT_REQ = 0x7, + RT_NVME_SC_ABORT_QUEUE = 0x8, + RT_NVME_SC_FUSED_FAIL = 0x9, + RT_NVME_SC_FUSED_MISSING = 0xa, + RT_NVME_SC_INVALID_NS = 0xb, + RT_NVME_SC_CMD_SEQ_ERROR = 0xc, + RT_NVME_SC_SGL_INVALID_LAST = 0xd, + RT_NVME_SC_SGL_INVALID_COUNT = 0xe, + RT_NVME_SC_SGL_INVALID_DATA = 0xf, + RT_NVME_SC_SGL_INVALID_METADATA = 0x10, + RT_NVME_SC_SGL_INVALID_TYPE = 0x11, + RT_NVME_SC_CMB_INVALID_USE = 0x12, + RT_NVME_SC_PRP_INVALID_OFFSET = 0x13, + RT_NVME_SC_ATOMIC_WU_EXCEEDED = 0x14, + RT_NVME_SC_OP_DENIED = 0x15, + RT_NVME_SC_SGL_INVALID_OFFSET = 0x16, + RT_NVME_SC_RESERVED = 0x17, + RT_NVME_SC_HOST_ID_INCONSIST = 0x18, + RT_NVME_SC_KA_TIMEOUT_EXPIRED = 0x19, + RT_NVME_SC_KA_TIMEOUT_INVALID = 0x1a, + RT_NVME_SC_ABORTED_PREEMPT_ABORT = 0x1b, + RT_NVME_SC_SANITIZE_FAILED = 0x1c, + RT_NVME_SC_SANITIZE_IN_PROGRESS = 0x1d, + RT_NVME_SC_SGL_INVALID_GRANULARITY = 0x1e, + RT_NVME_SC_CMD_NOT_SUP_CMB_QUEUE = 0x1f, + RT_NVME_SC_NS_WRITE_PROTECTED = 0x20, + RT_NVME_SC_CMD_INTERRUPTED = 0x21, + RT_NVME_SC_TRANSIENT_TR_ERR = 0x22, + RT_NVME_SC_ADMIN_COMMAND_MEDIA_NOT_READY = 0x24, + RT_NVME_SC_INVALID_IO_CMD_SET = 0x2c, + + RT_NVME_SC_LBA_RANGE = 0x80, + RT_NVME_SC_CAP_EXCEEDED = 0x81, + RT_NVME_SC_NS_NOT_READY = 0x82, + RT_NVME_SC_RESERVATION_CONFLICT = 0x83, + RT_NVME_SC_FORMAT_IN_PROGRESS = 0x84, + + /* + * Command Specific Status: + */ + RT_NVME_SC_CQ_INVALID = 0x100, + RT_NVME_SC_QID_INVALID = 0x101, + RT_NVME_SC_QUEUE_SIZE = 0x102, + RT_NVME_SC_ABORT_LIMIT = 0x103, + RT_NVME_SC_ABORT_MISSING = 0x104, + RT_NVME_SC_ASYNC_LIMIT = 0x105, + RT_NVME_SC_FIRMWARE_SLOT = 0x106, + RT_NVME_SC_FIRMWARE_IMAGE = 0x107, + RT_NVME_SC_INVALID_VECTOR = 0x108, + RT_NVME_SC_INVALID_LOG_PAGE = 0x109, + RT_NVME_SC_INVALID_FORMAT = 0x10a, + RT_NVME_SC_FW_NEEDS_CONV_RESET = 0x10b, + RT_NVME_SC_INVALID_QUEUE = 0x10c, + RT_NVME_SC_FEATURE_NOT_SAVEABLE = 0x10d, + RT_NVME_SC_FEATURE_NOT_CHANGEABLE = 0x10e, + RT_NVME_SC_FEATURE_NOT_PER_NS = 0x10f, + RT_NVME_SC_FW_NEEDS_SUBSYS_RESET = 0x110, + RT_NVME_SC_FW_NEEDS_RESET = 0x111, + RT_NVME_SC_FW_NEEDS_MAX_TIME = 0x112, + RT_NVME_SC_FW_ACTIVATE_PROHIBITED = 0x113, + RT_NVME_SC_OVERLAPPING_RANGE = 0x114, + RT_NVME_SC_NS_INSUFFICIENT_CAP = 0x115, + RT_NVME_SC_NS_ID_UNAVAILABLE = 0x116, + RT_NVME_SC_NS_ALREADY_ATTACHED = 0x118, + RT_NVME_SC_NS_IS_PRIVATE = 0x119, + RT_NVME_SC_NS_NOT_ATTACHED = 0x11a, + RT_NVME_SC_THIN_PROV_NOT_SUPP = 0x11b, + RT_NVME_SC_CTRL_LIST_INVALID = 0x11c, + RT_NVME_SC_SELT_TEST_IN_PROGRESS = 0x11d, + RT_NVME_SC_BP_WRITE_PROHIBITED = 0x11e, + RT_NVME_SC_CTRL_ID_INVALID = 0x11f, + RT_NVME_SC_SEC_CTRL_STATE_INVALID = 0x120, + RT_NVME_SC_CTRL_RES_NUM_INVALID = 0x121, + RT_NVME_SC_RES_ID_INVALID = 0x122, + RT_NVME_SC_PMR_SAN_PROHIBITED = 0x123, + RT_NVME_SC_ANA_GROUP_ID_INVALID = 0x124, + RT_NVME_SC_ANA_ATTACH_FAILED = 0x125, + + /* + * I/O Command Set Specific - NVM commands: + */ + RT_NVME_SC_BAD_ATTRIBUTES = 0x180, + RT_NVME_SC_INVALID_PI = 0x181, + RT_NVME_SC_READ_ONLY = 0x182, + RT_NVME_SC_ONCS_NOT_SUPPORTED = 0x183, + + /* + * I/O Command Set Specific - Fabrics commands: + */ + RT_NVME_SC_CONNECT_FORMAT = 0x180, + RT_NVME_SC_CONNECT_CTRL_BUSY = 0x181, + RT_NVME_SC_CONNECT_INVALID_PARAM = 0x182, + RT_NVME_SC_CONNECT_RESTART_DISC = 0x183, + RT_NVME_SC_CONNECT_INVALID_HOST = 0x184, + + RT_NVME_SC_DISCOVERY_RESTART = 0x190, + RT_NVME_SC_AUTH_REQUIRED = 0x191, + + /* + * I/O Command Set Specific - Zoned commands: + */ + RT_NVME_SC_ZONE_BOUNDARY_ERROR = 0x1b8, + RT_NVME_SC_ZONE_FULL = 0x1b9, + RT_NVME_SC_ZONE_READ_ONLY = 0x1ba, + RT_NVME_SC_ZONE_OFFLINE = 0x1bb, + RT_NVME_SC_ZONE_INVALID_WRITE = 0x1bc, + RT_NVME_SC_ZONE_TOO_MANY_ACTIVE = 0x1bd, + RT_NVME_SC_ZONE_TOO_MANY_OPEN = 0x1be, + RT_NVME_SC_ZONE_INVALID_TRANSITION = 0x1bf, + + /* + * Media and Data Integrity Errors: + */ + RT_NVME_SC_WRITE_FAULT = 0x280, + RT_NVME_SC_READ_ERROR = 0x281, + RT_NVME_SC_GUARD_CHECK = 0x282, + RT_NVME_SC_APPTAG_CHECK = 0x283, + RT_NVME_SC_REFTAG_CHECK = 0x284, + RT_NVME_SC_COMPARE_FAILED = 0x285, + RT_NVME_SC_ACCESS_DENIED = 0x286, + RT_NVME_SC_UNWRITTEN_BLOCK = 0x287, + + /* + * Path-related Errors: + */ + RT_NVME_SC_INTERNAL_PATH_ERROR = 0x300, + RT_NVME_SC_ANA_PERSISTENT_LOSS = 0x301, + RT_NVME_SC_ANA_INACCESSIBLE = 0x302, + RT_NVME_SC_ANA_TRANSITION = 0x303, + RT_NVME_SC_CTRL_PATH_ERROR = 0x360, + RT_NVME_SC_HOST_PATH_ERROR = 0x370, + RT_NVME_SC_HOST_ABORTED_CMD = 0x371, + + RT_NVME_SC_CRD = 0x1800, + RT_NVME_SC_MORE = 0x2000, + RT_NVME_SC_DNR = 0x4000, +}; + +/* Admin commands */ +enum +{ + RT_NVME_ADMIN_OPCODE_DELETE_SQ = 0x00, + RT_NVME_ADMIN_OPCODE_CREATE_SQ = 0x01, + RT_NVME_ADMIN_OPCODE_GET_LOG_PAGE = 0x02, + RT_NVME_ADMIN_OPCODE_DELETE_CQ = 0x04, + RT_NVME_ADMIN_OPCODE_CREATE_CQ = 0x05, + RT_NVME_ADMIN_OPCODE_IDENTIFY = 0x06, + RT_NVME_ADMIN_OPCODE_ABORT_CMD = 0x08, + RT_NVME_ADMIN_OPCODE_SET_FEATURES = 0x09, + RT_NVME_ADMIN_OPCODE_GET_FEATURES = 0x0a, + RT_NVME_ADMIN_OPCODE_ASYNC_EVENT = 0x0c, + RT_NVME_ADMIN_OPCODE_NS_MGMT = 0x0d, + RT_NVME_ADMIN_OPCODE_ACTIVATE_FW = 0x10, + RT_NVME_ADMIN_OPCODE_DOWNLOAD_FW = 0x11, + RT_NVME_ADMIN_OPCODE_DEV_SELF_TEST = 0x14, + RT_NVME_ADMIN_OPCODE_NS_ATTACH = 0x15, + RT_NVME_ADMIN_OPCODE_KEEP_ALIVE = 0x18, + RT_NVME_ADMIN_OPCODE_DIRECTIVE_SEND = 0x19, + RT_NVME_ADMIN_OPCODE_DIRECTIVE_RECV = 0x1a, + RT_NVME_ADMIN_OPCODE_VIRTUAL_MGMT = 0x1c, + RT_NVME_ADMIN_OPCODE_NVME_MI_SEND = 0x1d, + RT_NVME_ADMIN_OPCODE_NVME_MI_RECV = 0x1e, + RT_NVME_ADMIN_OPCODE_DBBUF = 0x7c, + RT_NVME_ADMIN_OPCODE_FORMAT_NVM = 0x80, + RT_NVME_ADMIN_OPCODE_SECURITY_SEND = 0x81, + RT_NVME_ADMIN_OPCODE_SECURITY_RECV = 0x82, + RT_NVME_ADMIN_OPCODE_SANITIZE_NVM = 0x84, + RT_NVME_ADMIN_OPCODE_GET_LBA_STATUS = 0x86, + RT_NVME_ADMIN_OPCODE_VENDOR_START = 0xc0, +}; + +/* I/O commands */ +enum +{ + RT_NVME_CMD_FLUSH = 0x00, + RT_NVME_CMD_WRITE = 0x01, + RT_NVME_CMD_READ = 0x02, + RT_NVME_CMD_WRITE_UNCOR = 0x04, + RT_NVME_CMD_COMPARE = 0x05, + RT_NVME_CMD_WRITE_ZEROES = 0x08, + RT_NVME_CMD_DSM = 0x09, + RT_NVME_CMD_VERIFY = 0x0c, + RT_NVME_CMD_RESV_REGISTER = 0x0d, + RT_NVME_CMD_RESV_REPORT = 0x0e, + RT_NVME_CMD_RESV_ACQUIRE = 0x11, + RT_NVME_CMD_RESV_RELEASE = 0x15, + RT_NVME_CMD_ZONE_MGMT_SEND = 0x79, + RT_NVME_CMD_ZONE_MGMT_RECV = 0x7a, + RT_NVME_CMD_ZONE_APPEND = 0x7d, + RT_NVME_CMD_VENDOR_START = 0x80, +}; + +enum +{ + RT_NVME_PSDT_PRP = 0x0, + RT_NVME_PSDT_SGL_MPTR_CONTIGUOUS = 0x1, + RT_NVME_PSDT_SGL_MPTR_SGL = 0x2, +}; + +/* Commands flags */ +enum +{ + RT_NVME_CMD_FLAGS_FUSE_SHIFT = 0x00, + RT_NVME_CMD_FLAGS_PSDT_SHIFT = 0x06, +}; + +struct rt_nvme_command_common +{ + rt_uint8_t opcode; + rt_uint8_t flags; + rt_uint16_t cmdid; + rt_le32_t nsid; + rt_le32_t cmd_dw2[2]; + rt_le64_t metadata; + rt_le64_t prp1; + rt_le64_t prp2; + rt_le32_t cmd_dw10[6]; +}; + +rt_packed(struct rt_nvme_sgl_desc +{ + rt_le64_t adddress; + rt_le32_t length; + rt_uint8_t reserved[3]; +#define SGL_DESC_TYPE_DATA_BLOCK 0x0 +#define SGL_DESC_TYPE_BIT_BUCKET 0x1 +#define SGL_DESC_TYPE_SEGMENT 0x2 +#define SGL_DESC_TYPE_LAST_SEGMENT 0x3 +#define SGL_DESC_TYPE_KEYED_DATA_BLOCK 0x4 +#define SGL_DESC_TYPE_VENDOR_SPECIFIC 0xf + rt_uint8_t sgl_identify; +}); + +struct rt_nvme_command_rw +{ + rt_uint8_t opcode; + rt_uint8_t flags; + rt_uint16_t cmdid; + rt_le32_t nsid; + NVME_RSVD(8, 8); + rt_le64_t metadata; + union + { + struct + { + rt_le64_t prp1; + rt_le64_t prp2; + }; + struct rt_nvme_sgl_desc sgl; + }; + rt_le64_t slba; + rt_le16_t length; + rt_le16_t control; + rt_le32_t dsmgmt; + rt_le32_t reftag; + rt_le16_t apptag; + rt_le16_t appmask; +}; + +enum +{ + RT_NVME_RW_LR = 1 << 15, + RT_NVME_RW_FUA = 1 << 14, + RT_NVME_RW_APPEND_PIREMAP = 1 << 9, + RT_NVME_RW_DSM_FREQ_UNSPEC = 0, + RT_NVME_RW_DSM_FREQ_TYPICAL = 1, + RT_NVME_RW_DSM_FREQ_RARE = 2, + RT_NVME_RW_DSM_FREQ_READS = 3, + RT_NVME_RW_DSM_FREQ_WRITES = 4, + RT_NVME_RW_DSM_FREQ_RW = 5, + RT_NVME_RW_DSM_FREQ_ONCE = 6, + RT_NVME_RW_DSM_FREQ_PREFETCH = 7, + RT_NVME_RW_DSM_FREQ_TEMP = 8, + RT_NVME_RW_DSM_LATENCY_NONE = 0 << 4, + RT_NVME_RW_DSM_LATENCY_IDLE = 1 << 4, + RT_NVME_RW_DSM_LATENCY_NORM = 2 << 4, + RT_NVME_RW_DSM_LATENCY_LOW = 3 << 4, + RT_NVME_RW_DSM_SEQ_REQ = 1 << 6, + RT_NVME_RW_DSM_COMPRESSED = 1 << 7, + RT_NVME_RW_PRINFO_PRCHK_REF = 1 << 10, + RT_NVME_RW_PRINFO_PRCHK_APP = 1 << 11, + RT_NVME_RW_PRINFO_PRCHK_GUARD = 1 << 12, + RT_NVME_RW_PRINFO_PRACT = 1 << 13, + RT_NVME_RW_DTYPE_STREAMS = 1 << 4, + RT_NVME_WZ_DEAC = 1 << 9, +}; + +enum +{ + RT_NVME_QUEUE_PHYS_CONTIG = (1 << 0), + RT_NVME_CQ_IRQ_ENABLED = (1 << 1), + RT_NVME_SQ_PRIO_URGENT = (0 << 1), + RT_NVME_SQ_PRIO_HIGH = (1 << 1), + RT_NVME_SQ_PRIO_MEDIUM = (2 << 1), + RT_NVME_SQ_PRIO_LOW = (3 << 1), + RT_NVME_FEAT_ARBITRATION = 0x01, + RT_NVME_FEAT_POWER_MGMT = 0x02, + RT_NVME_FEAT_LBA_RANGE = 0x03, + RT_NVME_FEAT_TEMP_THRESH = 0x04, + RT_NVME_FEAT_ERR_RECOVERY = 0x05, + RT_NVME_FEAT_VOLATILE_WC = 0x06, + RT_NVME_FEAT_NUM_QUEUES = 0x07, + RT_NVME_FEAT_IRQ_COALESCE = 0x08, + RT_NVME_FEAT_IRQ_CONFIG = 0x09, + RT_NVME_FEAT_WRITE_ATOMIC = 0x0a, + RT_NVME_FEAT_ASYNC_EVENT = 0x0b, + RT_NVME_FEAT_AUTO_PST = 0x0c, + RT_NVME_FEAT_SW_PROGRESS = 0x80, + RT_NVME_FEAT_HOST_ID = 0x81, + RT_NVME_FEAT_RESV_MASK = 0x82, + RT_NVME_FEAT_RESV_PERSIST = 0x83, + RT_NVME_LOG_ERROR = 0x01, + RT_NVME_LOG_SMART = 0x02, + RT_NVME_LOG_FW_SLOT = 0x03, + RT_NVME_LOG_RESERVATION = 0x80, + RT_NVME_FWACT_REPL = (0 << 3), + RT_NVME_FWACT_REPL_ACTV = (1 << 3), + RT_NVME_FWACT_ACTV = (2 << 3), +}; + +struct rt_nvme_command_identify +{ + rt_uint8_t opcode; + rt_uint8_t flags; + rt_uint16_t cmdid; + rt_le32_t nsid; + NVME_RSVD(8, 16); + rt_le64_t prp1; + rt_le64_t prp2; + rt_le32_t cns; + NVME_RSVD(64, 20); +}; + +struct rt_nvme_command_features +{ + rt_uint8_t opcode; + rt_uint8_t flags; + rt_uint16_t cmdid; + rt_le32_t nsid; + NVME_RSVD(8, 16); + rt_le64_t prp1; + rt_le64_t prp2; + rt_le32_t fid; + rt_le32_t dword11; + NVME_RSVD(68, 16); +}; + +struct rt_nvme_command_create_cq +{ + rt_uint8_t opcode; + rt_uint8_t flags; + rt_uint16_t cmdid; + NVME_RSVD(4, 20); + rt_le64_t prp1; + NVME_RSVD(32, 8); + rt_le16_t cqid; + rt_le16_t qsize; + rt_le16_t cq_flags; + rt_le16_t irq_vector; + NVME_RSVD(104, 16); +}; + +struct rt_nvme_command_create_sq +{ + rt_uint8_t opcode; + rt_uint8_t flags; + rt_uint16_t cmdid; + NVME_RSVD(4, 20); + rt_le64_t prp1; + NVME_RSVD(32, 8); + rt_le16_t sqid; + rt_le16_t qsize; + rt_le16_t sq_flags; + rt_le16_t cqid; + NVME_RSVD(104, 16); +}; + +struct rt_nvme_command_delete_queue +{ + rt_uint8_t opcode; + rt_uint8_t flags; + rt_uint16_t cmdid; + NVME_RSVD(4, 36); + rt_le16_t qid; + NVME_RSVD(42, 22); +}; + +struct rt_nvme_command_write_zeroes +{ + rt_uint8_t opcode; + rt_uint8_t flags; + rt_uint16_t cmdid; + rt_le32_t nsid; + NVME_RSVD(8, 8); + rt_le64_t metadata; + rt_le64_t prp1; + rt_le64_t prp2; + rt_le64_t slba; + rt_le16_t length; + rt_le16_t control; + rt_le32_t dsmgmt; + rt_le32_t reftag; + rt_le16_t apptag; + rt_le16_t appmask; +}; + +struct rt_nvme_command +{ + union + { + struct rt_nvme_command_common common; + struct rt_nvme_command_rw rw; + struct rt_nvme_command_identify identify; + struct rt_nvme_command_features features; + struct rt_nvme_command_create_cq create_cq; + struct rt_nvme_command_create_sq create_sq; + struct rt_nvme_command_delete_queue delete_queue; + struct rt_nvme_command_write_zeroes write_zeroes; + }; +}; + +struct rt_nvme_completion +{ + union + { + rt_le16_t u16; + rt_le32_t u32; + rt_le64_t u64; + } result; + rt_le16_t sq_head; /* How much of this queue may be reclaimed */ + rt_le16_t sq_id; /* Submission queue that generated this entry */ + rt_uint16_t cmdid; /* Which command completed */ + rt_le16_t status; /* Command status */ +}; + +enum +{ + RT_NVME_REG_CAP = 0x0000, /* Controller Capabilities */ + RT_NVME_REG_VS = 0x0008, /* Version */ + RT_NVME_REG_INTMS = 0x000c, /* Interrupt Mask Set */ + RT_NVME_REG_INTMC = 0x0010, /* Interrupt Mask Clear */ + RT_NVME_REG_CC = 0x0014, /* Controller Configuration */ + RT_NVME_REG_CSTS = 0x001c, /* Controller Status */ + RT_NVME_REG_NSSR = 0x0020, /* NVM Subsystem Reset */ + RT_NVME_REG_AQA = 0x0024, /* Admin Queue Attributes */ + RT_NVME_REG_ASQ = 0x0028, /* Admin SQ Base Address */ + RT_NVME_REG_ACQ = 0x0030, /* Admin CQ Base Address */ + RT_NVME_REG_CMBLOC = 0x0038, /* Controller Memory Buffer Location */ + RT_NVME_REG_CMBSZ = 0x003c, /* Controller Memory Buffer Size */ + RT_NVME_REG_BPINFO = 0x0040, /* Boot Partition Information */ + RT_NVME_REG_BPRSEL = 0x0044, /* Boot Partition Read Select */ + RT_NVME_REG_BPMBL = 0x0048, /* Boot Partition Memory Buffer Location */ + RT_NVME_REG_CMBMSC = 0x0050, /* Controller Memory Buffer Memory Space Control */ + RT_NVME_REG_CRTO = 0x0068, /* Controller Ready Timeouts */ + RT_NVME_REG_PMRCAP = 0x0e00, /* Persistent Memory Capabilities */ + RT_NVME_REG_PMRCTL = 0x0e04, /* Persistent Memory Region Control */ + RT_NVME_REG_PMRSTS = 0x0e08, /* Persistent Memory Region Status */ + RT_NVME_REG_PMREBS = 0x0e0c, /* Persistent Memory Region Elasticity Buffer Size */ + RT_NVME_REG_PMRSWTP = 0x0e10, /* Persistent Memory Region Sustained Write Throughput */ + RT_NVME_REG_DBS = 0x1000, /* SQ 0 Tail Doorbell */ +}; + +#define RT_NVME_CAP_MQES(cap) ((cap) & 0xffff) +#define RT_NVME_CAP_TIMEOUT(cap) (((cap) >> 24) & 0xff) +#define RT_NVME_CAP_STRIDE(cap) (((cap) >> 32) & 0xf) +#define RT_NVME_CAP_MPSMIN(cap) (((cap) >> 48) & 0xf) +#define RT_NVME_CAP_MPSMAX(cap) (((cap) >> 52) & 0xf) + +#define RT_NVME_VS(major, minor) (((major) << 16) | ((minor) << 8)) + +#define RT_NVME_AQ_DEPTH 32 +#define RT_NVME_NR_AEN_COMMANDS 1 +#define RT_NVME_AQ_BLK_MQ_DEPTH (RT_NVME_AQ_DEPTH - RT_NVME_NR_AEN_COMMANDS) +#define RT_NVME_AQ_MQ_TAG_DEPTH (RT_NVME_AQ_BLK_MQ_DEPTH - 1) + +enum +{ + RT_NVME_CC_ENABLE = 1 << 0, + RT_NVME_CC_CSS_NVM = 0 << 4, + RT_NVME_CC_MPS_SHIFT = 7, + RT_NVME_CC_ARB_RR = 0 << 11, + RT_NVME_CC_ARB_WRRU = 1 << 11, + RT_NVME_CC_ARB_VS = 7 << 11, + RT_NVME_CC_SHN_NONE = 0 << 14, + RT_NVME_CC_SHN_NORMAL = 1 << 14, + RT_NVME_CC_SHN_ABRUPT = 2 << 14, + RT_NVME_CC_SHN_MASK = 3 << 14, + RT_NVME_CC_IOSQES = 6 << 16, + RT_NVME_CC_IOCQES = 4 << 20, + RT_NVME_CSTS_RDY = 1 << 0, + RT_NVME_CSTS_CFS = 1 << 1, + RT_NVME_CSTS_SHST_NORMAL = 0 << 2, + RT_NVME_CSTS_SHST_OCCUR = 1 << 2, + RT_NVME_CSTS_SHST_CMPLT = 2 << 2, + RT_NVME_CSTS_SHST_MASK = 3 << 2, +}; + +rt_packed(struct rt_nvme_id_power_state +{ + rt_le16_t mp; /* Maximum Power */ + NVME_RSVD(1, 1); + rt_uint8_t mxps_nops; /* Max Power Scale, Non-Operational State */ + rt_le32_t enlat; /* Entry Latency: microseconds */ + rt_le32_t exlat; /* Exit Latency: microseconds */ + rt_uint8_t rrt; /* Relative Read Throughput */ + rt_uint8_t rrl; /* Relative Read Latency */ + rt_uint8_t rwt; /* Relative Write Throughput */ + rt_uint8_t rwl; /* Relative Write Latency */ + rt_le16_t idlp; /* Idle Power */ + rt_uint8_t ips; /* Idle Power Scale */ + NVME_RSVD(19, 1); + rt_le16_t actp; /* Active Power */ + rt_uint8_t apw_aps; /* Active Power Workload, Active Power Scale */ + NVME_RSVD(23, 9); +}); + +rt_packed(struct rt_nvme_id_ctrl +{ + /* Controller Capabilities and Features */ + rt_le16_t vid; /* PCI Vendor ID */ + rt_le16_t ssvid; /* PCI Subsystem Vendor */ + char sn[20]; /* Serial Number */ + char mn[40]; /* Model Number */ + char fr[8]; /* Firmware Revision */ + rt_uint8_t rab; /* Recommended Arbitration Burst */ + rt_uint8_t ieee[3]; /* IEEE OUI Identifier */ + rt_uint8_t mic; /* Controller Multi-Path I/O and Namespace Sharing Capabilities */ + rt_uint8_t mdts; /* Maximum Data Transfer Size */ + rt_uint16_t cntlid; /* Controller ID */ + rt_uint32_t ver; /* Version */ + rt_uint32_t rtd3r; /* RTD3 Resume Latency */ + rt_uint32_t rtd3e; /* RTD3 Entry Latency */ + rt_uint32_t oaes; /* Optional Asynchronous Events Supported */ +#define RT_NVME_ID_CTRATT_ELBAS 15 /* Extended LBA Formats Supported */ +#define RT_NVME_ID_CTRATT_DNVMS 14 /* Delete NVM Set */ +#define RT_NVME_ID_CTRATT_DEG 13 /* Delete Endurance Group */ +#define RT_NVME_ID_CTRATT_VCM 12 /* Variable Capacity Management */ +#define RT_NVME_ID_CTRATT_FCM 11 /* Fixed Capacity Management */ +#define RT_NVME_ID_CTRATT_MDS 10 /* Multi-Domain Subsystem */ +#define RT_NVME_ID_CTRATT_UUIDL 9 /* UUID List */ +#define RT_NVME_ID_CTRATT_SQA 8 /* SQ Associations */ +#define RT_NVME_ID_CTRATT_NG 7 /* Namespace Granularity */ +#define RT_NVME_ID_CTRATT_TBKAS 6 /* Traffic Based Keep Alive Support */ +#define RT_NVME_ID_CTRATT_PLM 5 /* Predictable Latency Mode */ +#define RT_NVME_ID_CTRATT_EG 4 /* Endurance Groups */ +#define RT_NVME_ID_CTRATT_RRL 3 /* Read Recovery Levels */ +#define RT_NVME_ID_CTRATT_NVMS 2 /* NVM Sets */ +#define RT_NVME_ID_CTRATT_NOPSPM 1 /* Non-Operational Power State Permissive Mode */ +#define RT_NVME_ID_CTRATT_HIS 0 /* Host Identifier Support */ + rt_uint32_t ctratt; /* Controller Attributes */ + rt_uint16_t rrls; /* Read Recovery Levels Supported */ + NVME_RSVD(102, 9); + rt_uint8_t cntrltype; /* Controller Type */ + rt_uint8_t fguid[16]; /* FRU Globally Unique Identifier */ + rt_uint16_t crdt1; /* Command Retry Delay Time 1 */ + rt_uint16_t crdt2; /* Command Retry Delay Time 2 */ + rt_uint16_t crdt3; /* Command Retry Delay Time 3 */ + NVME_RSVD(134, 119); +#define RT_NVME_ID_NVMSR_NVMEE 1 /* NVMe Enclosure */ +#define RT_NVME_ID_NVMSR_NVMESD 0 /* NVMe Storage Device */ + rt_uint8_t nvmsr; /* NVM Subsystem Report */ + +#define RT_NVME_ID_VWCI_VWCRV 7 /* VPD Write Cycles Remaining Valid */ +#define RT_NVME_ID_VWCI_VWCR 0 /* VPD Write Cycles Remaining */ + rt_uint8_t vwci; /* VPD Write Cycle Information */ +#define RT_NVME_ID_MEC_PCIEME 1 /* PCIe Port Management Endpoint */ +#define RT_NVME_ID_MEC_SMBUSME 0 /* SMBus/I2C Port Management Endpoint */ + rt_uint8_t mec; /* Management Endpoint Capabilities */ + + /* Admin Command Set Attributes & Optional Controller Capabilities */ + rt_le16_t oacs; /* Optional Admin Command Support */ + rt_uint8_t acl; /* Abort Command Limit */ + rt_uint8_t aerl; /* Asynchronous Event Request Limit */ +#define RT_NVME_ID_FRMW_SMUD 5 /* Support Multiple Update Detection */ +#define RT_NVME_ID_FRMW_FAWR 4 /* Firmware Activation Without Reset */ +#define RT_NVME_ID_FRMW_NOFS 1 /* Number Of Firmware Slots */ +#define RT_NVME_ID_FRMW_FFSRO 0 /* First Firmware Slot Read Only */ + rt_uint8_t frmw; /* Firmware Updates */ + rt_uint8_t lpa; /* Log Page Attributes */ + rt_uint8_t elpe; /* Error Log Page Entries */ + rt_uint8_t npss; /* Number of Power States Support */ + rt_uint8_t avscc; /* Admin Vendor Specific Command Configuration */ + rt_uint8_t apsta; /* Autonomous Power State Transition Attributes */ + rt_le16_t wctemp; /* Warning Composite Temperature Threshold */ + rt_le16_t cctemp; /* Critical Composite Temperature Threshold */ + rt_uint16_t mtfa; /* Maximum Time for Firmware Activation */ + rt_uint32_t hmpre; /* Host Memory Buffer Preferred Size */ + rt_uint32_t hmmin; /* Host Memory Buffer Minimum Size */ + rt_uint8_t tnvmcap[16]; /* Total NVM Capacity */ + rt_uint8_t unvmcap[16]; /* Unallocated NVM Capacity */ +#define RT_NVME_ID_RPMBS_ASZ 24 /* Access Size */ +#define RT_NVME_ID_RPMBS_TSZ 16 /* Total Size */ +#define RT_NVME_ID_RPMBS_AM 3 /* Authentication Method */ +#define RT_NVME_ID_RPMBS_NORPMBU 2 /* Number of RPMB Units */ + rt_uint32_t rpmbs; /* Replay Protected Memory Block Support */ + rt_uint16_t edstt; /* Extended Device Self-test Time */ + rt_uint8_t dsto; /* Device Self-test Options */ + rt_uint8_t fwug; /* Firmware Update Granularity */ + rt_uint16_t kas; /* Keep Alive Support */ + rt_uint16_t hctma; /* Host Controlled Thermal Management Attributes */ + rt_uint16_t mntmt; /* Minimum Thermal Management Temperature */ + rt_uint16_t mxtmt; /* Maximum Thermal Management Temperature */ +#define RT_NVME_ID_SANICAP_NODMMAS 30 /* No-Deallocate Modifies Media After Sanitize */ +#define RT_NVME_ID_SANICAP_NDI 29 /* No-Deallocate Inhibited */ +#define RT_NVME_ID_SANICAP_OWS 2 /* Overwrite Support */ +#define RT_NVME_ID_SANICAP_BES 1 /* Block Erase Support */ +#define RT_NVME_ID_SANICAP_CES 0 /* Crypto Erase Support */ + rt_uint32_t sanicap; /* Sanitize Capabilities */ + rt_uint32_t hmminds; /* Host Memory Buffer Minimum Descriptor Entry Size */ + rt_uint16_t hmmaxd; /* Host Memory Maximum Descriptors Entries */ + rt_uint16_t nsetidmax; /* NVM Set Identifier Maximum */ + rt_uint16_t endgidmax; /* Endurance Group Identifier Maximum */ + rt_uint8_t anatt; /* ANA Transition Time */ + rt_uint8_t anacap; /* Asymmetric Namespace Access Capabilities */ + rt_uint32_t anagrpmax; /* ANA Group Identifier Maximum */ + rt_uint32_t nanagrpid; /* Number of ANA Group Identifiers */ + rt_uint32_t pels; /* Persistent Event Log Size */ + rt_uint16_t dmid; /* Domain Identifier */ + NVME_RSVD(358, 10); + rt_uint8_t megcap[16]; /* Max Endurance Group Capacity */ + NVME_RSVD(384, 128); + + /* NVM Command Set Attributes */ + rt_uint8_t sqes; /* Submission Queue Entry Size */ + rt_uint8_t cqes; /* Completion Queue Entry Size */ + rt_le16_t maxcmd; /* Maximum Outstanding Commands */ + rt_le32_t nn; /* Number of Namespaces */ + rt_le16_t oncs; /* Optional NVM Command Support */ + rt_le16_t fuses; /* Fused Operation Support */ + rt_uint8_t fna; /* Format NVM Attributes */ + rt_uint8_t vwc; /* Volatile Write Cache */ + rt_le16_t awun; /* Atomic Write Unit Normal */ + rt_le16_t awupf; /* Atomic Write Unit Power Fail */ + rt_uint8_t nvscc; /* I/O Command Set Vendor Specific Command Configuration */ + rt_uint8_t nwpc; /* Namespace Write Protection Capabilities */ + rt_le16_t acwu; /* Atomic Compare & Write Unit */ + rt_le16_t cdfs; /* Copy Descriptor Formats Supported */ +#define RT_NVME_ID_SGL_SUPPORT_MASK 0x3 + rt_le32_t sgls; /* SGL Support */ + rt_uint32_t mnan; /* Maximum Number of Allowed Namespaces */ + char maxdna[16]; /* Maximum Domain Namespace Attachments */ + rt_le32_t maxcna; /* Maximum I/O Controller Namespace Attachments */ + NVME_RSVD(564, 204); + rt_uint8_t subnqn[256]; /* NVM Subsystem NVMe Qualified Name */ + NVME_RSVD(1024, 768); + rt_le32_t ioccsz; /* I/O Queue Command Capsule Supported Size */ + rt_le32_t iorcsz; /* I/O Queue Response Capsule Supported Size */ + rt_le16_t icdoff; /* In Capsule Data Offset */ + rt_uint8_t ctrattr; /* Fabrics Controller Attributes */ + rt_uint8_t msdbd; /* Maximum SGL Data Block Descriptors */ + rt_le16_t ofcs; /* Optional Fabric Commands Support */ + rt_uint8_t dctype; + NVME_RSVD(1807, 241); + + /* Power State Descriptors */ + struct rt_nvme_id_power_state psd[32]; + + /* Vendor Specific */ + rt_uint8_t vs[1024]; +}); + +enum +{ + RT_NVME_CTRL_CMIC_MULTI_PORT = 1 << 0, + RT_NVME_CTRL_CMIC_MULTI_CTRL = 1 << 1, + RT_NVME_CTRL_CMIC_ANA = 1 << 3, + RT_NVME_CTRL_ONCS_COMPARE = 1 << 0, + RT_NVME_CTRL_ONCS_WRITE_UNCORRECTABLE = 1 << 1, + RT_NVME_CTRL_ONCS_DSM = 1 << 2, + RT_NVME_CTRL_ONCS_WRITE_ZEROES = 1 << 3, + RT_NVME_CTRL_ONCS_RESERVATIONS = 1 << 5, + RT_NVME_CTRL_ONCS_TIMESTAMP = 1 << 6, + RT_NVME_CTRL_VWC_PRESENT = 1 << 0, + RT_NVME_CTRL_OACS_SEC_SUPP = 1 << 0, + RT_NVME_CTRL_OACS_NS_MNGT_SUPP = 1 << 3, + RT_NVME_CTRL_OACS_DIRECTIVES = 1 << 5, + RT_NVME_CTRL_OACS_DBBUF_SUPP = 1 << 8, + RT_NVME_CTRL_LPA_CMD_EFFECTS_LOG = 1 << 1, + RT_NVME_CTRL_CTRATT_128_ID = 1 << 0, + RT_NVME_CTRL_CTRATT_NON_OP_PSP = 1 << 1, + RT_NVME_CTRL_CTRATT_NVM_SETS = 1 << 2, + RT_NVME_CTRL_CTRATT_READ_RECV_LVLS = 1 << 3, + RT_NVME_CTRL_CTRATT_ENDURANCE_GROUPS = 1 << 4, + RT_NVME_CTRL_CTRATT_PREDICTABLE_LAT = 1 << 5, + RT_NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY = 1 << 7, + RT_NVME_CTRL_CTRATT_UUID_LIST = 1 << 9, +}; + +struct rt_nvme_lba_format +{ + rt_le16_t ms; /* Metadata size */ + rt_uint8_t ds; /* Data size */ + rt_uint8_t rp; /* Relative performance */ +}; + +rt_packed(struct rt_nvme_id_ns +{ + rt_le64_t nsze; /* Namespace size */ + rt_le64_t ncap; /* Namespace capacity */ + rt_le64_t nuse; /* Namespace utilization */ + rt_uint8_t nsfeat; /* Namespace features */ + rt_uint8_t nlbaf; /* Number of lba formats */ + rt_uint8_t flbas; /* Formatted lba size */ + rt_uint8_t mc; /* Metadata capabilities */ + rt_uint8_t dpc; /* End-to-end data protection capabilities */ + rt_uint8_t dps; /* End-to-end data protection type settings */ + rt_uint8_t nmic; /* Namespace Multi-path I/O and Namespace Sharing Capabilities */ + rt_uint8_t rescap; /* Reservation Capabilities */ + rt_uint8_t fpi; /* Format Progress Indicator */ + rt_uint8_t dlfeat; /* Deallocate Logical Block Features */ + rt_le16_t nawun; /* Namespace Atomic Write Unit Normal */ + rt_le16_t nawupf; /* Namespace Atomic Write Unit Power Fail */ + rt_le16_t nacwu; /* Namespace Atomic Compare & Write Unit */ + rt_le16_t nabsn; /* Namespace Atomic Boundary Size Normal */ + rt_le16_t nabo; /* Namespace Atomic Boundary Offset */ + rt_le16_t nabspf; /* Namespace Atomic Boundary Size Power Fail */ + rt_uint16_t noiob; /* Namespace Optimal IO Boundary */ + rt_le64_t nvmcap[2]; /* NVMe Capacity */ + rt_uint16_t npwg; /* Namespace Preferred Write Granularity */ + rt_uint16_t npwa; /* Namespace Preferred Write Alignment */ + rt_uint16_t npdg; /* Namespace Preferred Deallocate Granularity */ + rt_uint16_t npda; /* Namespace Preferred Deallocate Alignment */ + rt_uint16_t nows; /* Namespace Optimal Write Size */ + NVME_RSVD(118, 18); + rt_uint32_t anagrpid; /* ANA Group Identifier */ + NVME_RSVD(139, 3); + rt_uint8_t nsattr; /* Namespace Attributes */ + rt_uint16_t nvmsetid; /* NVMe Set Identifier */ + rt_uint16_t endgid; /* Endurance Group Identifier */ + rt_uint8_t nguid[16]; /* Namespace Globally Unique Identifier */ + rt_uint8_t eui64[8]; /* IEEE Extended Unique Identifier */ + + /* Logical Block Address Format */ + struct rt_nvme_lba_format lbaf[16]; + NVME_RSVD(171, 192); + + /* Vendor specific */ + rt_uint8_t vs[3712]; +}); + +enum +{ + RT_NVME_NS_FEAT_THIN = 1 << 0, + RT_NVME_NS_FLBAS_LBA_MASK = 0xf, + RT_NVME_NS_FLBAS_LBA_UMASK = 0x60, + RT_NVME_NS_FLBAS_LBA_SHIFT = 1, + RT_NVME_NS_FLBAS_META_EXT = 0x10, + RT_NVME_LBAF_RP_BEST = 0, + RT_NVME_LBAF_RP_BETTER = 1, + RT_NVME_LBAF_RP_GOOD = 2, + RT_NVME_LBAF_RP_DEGRADED = 3, + RT_NVME_NS_DPC_PI_LAST = 1 << 4, + RT_NVME_NS_DPC_PI_FIRST = 1 << 3, + RT_NVME_NS_DPC_PI_TYPE3 = 1 << 2, + RT_NVME_NS_DPC_PI_TYPE2 = 1 << 1, + RT_NVME_NS_DPC_PI_TYPE1 = 1 << 0, + RT_NVME_NS_DPS_PI_FIRST = 1 << 3, + RT_NVME_NS_DPS_PI_MASK = 0x7, + RT_NVME_NS_DPS_PI_TYPE1 = 1, + RT_NVME_NS_DPS_PI_TYPE2 = 2, + RT_NVME_NS_DPS_PI_TYPE3 = 3, +}; + +struct rt_nvme_ops; +struct rt_nvme_controller; + +/* + * An NVM Express queue. Each device has at least two (one for admin commands + * and one for I/O commands). + */ +struct rt_nvme_queue +{ + struct rt_nvme_controller *nvme; + struct rt_nvme_command *sq_cmds; + struct rt_nvme_completion *cq_entry; + + rt_ubase_t sq_cmds_phy; + rt_ubase_t cq_entry_phy; + + rt_uint32_t *doorbell; + rt_uint16_t qid; + rt_uint16_t depth; + rt_uint16_t sq_head; + rt_uint16_t sq_tail; + rt_uint16_t cq_head; + rt_uint16_t cq_phase; + + rt_err_t err; + struct rt_nvme_command *cmd; + + struct rt_completion done; + struct rt_spinlock lock; +}; + +struct rt_nvme_controller +{ + rt_list_t list; + struct rt_device *dev; + + int nvme_id; + char name[RT_NAME_MAX]; + + void *regs; + rt_uint64_t cap; + rt_uint32_t page_shift; + rt_uint32_t page_size; + rt_uint32_t queue_depth; + rt_uint32_t io_queue_max; + rt_uint32_t ctrl_config; + rt_uint32_t max_transfer_shift:8; + rt_uint32_t volatile_write_cache:8; + rt_uint32_t write_zeroes:1; + rt_uint32_t sgl_mode:2; + rt_uint32_t doorbell_stride; + rt_uint32_t *doorbell_tbl; + + const struct rt_nvme_ops *ops; + +#define RT_USING_NVME_QUEUE (1 + (RT_USING_NVME_IO_QUEUE * RT_CPUS_NR)) + int irqs_nr; + int irqs[RT_USING_NVME_QUEUE]; + union + { + struct + { + struct rt_nvme_queue admin_queue; + struct rt_nvme_queue io_queues[RT_USING_NVME_IO_QUEUE * RT_CPUS_NR]; + }; + struct rt_nvme_queue queue[RT_USING_NVME_QUEUE]; + }; + + volatile rt_atomic_t cmdid; + volatile rt_atomic_t ioqid[RT_CPUS_NR]; + + rt_list_t ns_nodes; +}; + +struct rt_nvme_device +{ + struct rt_blk_disk parent; + struct rt_nvme_controller *ctrl; + + rt_list_t list; + + rt_uint32_t nsid; + rt_uint32_t lba_shift; + struct rt_nvme_id_ns id; +}; +#define rt_disk_to_nvme_device(disk) rt_container_of(disk, struct rt_nvme_device, parent) + +struct rt_nvme_ops +{ + const char *name; + + /* Controller-specific NVM Express queue setup */ + rt_err_t (*setup_queue)(struct rt_nvme_queue *queue); + /* Controller-specific NVM Express queue cleanup */ + rt_err_t (*cleanup_queue)(struct rt_nvme_queue *queue); + /* Controller-specific NVM Express command submission */ + rt_err_t (*submit_cmd)(struct rt_nvme_queue *queue, struct rt_nvme_command *cmd); + /* Controller-specific NVM Express command completion */ + void (*complete_cmd)(struct rt_nvme_queue *queue, struct rt_nvme_command *cmd); +}; + +rt_err_t rt_nvme_controller_register(struct rt_nvme_controller *nvme); +rt_err_t rt_nvme_controller_unregister(struct rt_nvme_controller *nvme); + +#endif /* __NVME_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/ofw.h b/rt-thread/components/drivers/include/drivers/ofw.h index 7531dfe..3f23017 100644 --- a/rt-thread/components/drivers/include/drivers/ofw.h +++ b/rt-thread/components/drivers/include/drivers/ofw.h @@ -37,6 +37,7 @@ struct rt_ofw_node /* phandles range from 1 to 2^32-2 (0xfffffffe) */ rt_phandle phandle; + struct rt_device *dev; struct rt_ofw_prop *props; struct rt_ofw_node *parent; struct rt_ofw_node *child; @@ -376,9 +377,9 @@ rt_inline rt_bool_t rt_ofw_node_is_type(const struct rt_ofw_node *np, const char for (np = rt_ofw_find_node_by_type(RT_NULL, type); np; \ np = rt_ofw_find_node_by_type(np, type)) -#define rt_ofw_foreach_node_by_compatible(np, type, compatible) \ - for (np = rt_ofw_find_node_by_compatible(RT_NULL, type, compatible); np; \ - np = rt_ofw_find_node_by_compatible(np, type, compatible)) +#define rt_ofw_foreach_node_by_compatible(np, compatible) \ + for (np = rt_ofw_find_node_by_compatible(RT_NULL, compatible); np; \ + np = rt_ofw_find_node_by_compatible(np, compatible)) #define rt_ofw_foreach_node_by_ids_r(np, id, ids) \ for (np = rt_ofw_find_node_by_ids_r(RT_NULL, ids, id); \ diff --git a/rt-thread/components/drivers/include/drivers/ofw_fdt.h b/rt-thread/components/drivers/include/drivers/ofw_fdt.h index 4db4ece..7aeec71 100644 --- a/rt-thread/components/drivers/include/drivers/ofw_fdt.h +++ b/rt-thread/components/drivers/include/drivers/ofw_fdt.h @@ -70,6 +70,7 @@ rt_err_t rt_fdt_boot_dump(void); void rt_fdt_earlycon_output(const char *str); void rt_fdt_earlycon_kick(int why); rt_err_t rt_fdt_scan_chosen_stdout(void); +rt_err_t rt_fdt_bootargs_select(const char *key, int index, const char **out_result); rt_err_t rt_fdt_unflatten(void); struct rt_ofw_node *rt_fdt_unflatten_single(void *fdt); diff --git a/rt-thread/components/drivers/include/drivers/ofw_io.h b/rt-thread/components/drivers/include/drivers/ofw_io.h index 4ebfbe2..f458435 100644 --- a/rt-thread/components/drivers/include/drivers/ofw_io.h +++ b/rt-thread/components/drivers/include/drivers/ofw_io.h @@ -26,6 +26,27 @@ rt_err_t rt_ofw_get_address_by_name(struct rt_ofw_node *np, const char *name, int rt_ofw_get_address_array(struct rt_ofw_node *np, int nr, rt_uint64_t *out_regs); rt_uint64_t rt_ofw_translate_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address); +rt_uint64_t rt_ofw_reverse_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address); + +rt_inline rt_uint64_t rt_ofw_translate_dma2cpu(struct rt_ofw_node *np, rt_uint64_t address) +{ + rt_uint64_t bus_addr, cpu_addr; + + bus_addr = rt_ofw_reverse_address(np, "dma-ranges", address); + cpu_addr = rt_ofw_translate_address(np, "ranges", bus_addr); + + return cpu_addr != ~0ULL ? cpu_addr : address; +} + +rt_inline rt_uint64_t rt_ofw_translate_cpu2dma(struct rt_ofw_node *np, rt_uint64_t address) +{ + rt_uint64_t bus_addr, dma_addr; + + bus_addr = rt_ofw_reverse_address(np, "ranges", address); + dma_addr = rt_ofw_translate_address(np, "dma-ranges", bus_addr); + + return dma_addr != ~0ULL ? dma_addr : address; +} void *rt_ofw_iomap(struct rt_ofw_node *np, int index); void *rt_ofw_iomap_by_name(struct rt_ofw_node *np, const char *name); diff --git a/rt-thread/components/drivers/include/drivers/pci.h b/rt-thread/components/drivers/include/drivers/pci.h new file mode 100644 index 0000000..1ae4393 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/pci.h @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#ifndef __PCI_H__ +#define __PCI_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include "../../pci/pci_ids.h" +#include "../../pci/pci_regs.h" + +#define RT_PCI_INTX_PIN_MAX 4 +#define RT_PCI_BAR_NR_MAX 6 +#define RT_PCI_DEVICE_MAX 32 +#define RT_PCI_FUNCTION_MAX 8 + +#define RT_PCI_FIND_CAP_TTL 48 + +/* + * The PCI interface treats multi-function devices as independent + * devices. The slot/function address of each device is encoded + * in a single byte as follows: + * + * 7:3 = slot + * 2:0 = function + */ +#define RT_PCI_DEVID(bus, devfn) ((((rt_uint16_t)(bus)) << 8) | (devfn)) +#define RT_PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) +#define RT_PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) +#define RT_PCI_FUNC(devfn) ((devfn) & 0x07) + +#define PCIE_LINK_STATE_L0S RT_BIT(0) +#define PCIE_LINK_STATE_L1 RT_BIT(1) +#define PCIE_LINK_STATE_CLKPM RT_BIT(2) +#define PCIE_LINK_STATE_L1_1 RT_BIT(3) +#define PCIE_LINK_STATE_L1_2 RT_BIT(4) +#define PCIE_LINK_STATE_L1_1_PCIPM RT_BIT(5) +#define PCIE_LINK_STATE_L1_2_PCIPM RT_BIT(6) +#define PCIE_LINK_STATE_ALL \ +( \ + PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | \ + PCIE_LINK_STATE_CLKPM | \ + PCIE_LINK_STATE_L1_1 | PCIE_LINK_STATE_L1_2 | \ + PCIE_LINK_STATE_L1_1_PCIPM | PCIE_LINK_STATE_L1_2_PCIPM \ +) + +struct rt_pci_bus_region +{ + rt_uint64_t phy_addr; + rt_uint64_t cpu_addr; + rt_uint64_t size; + + rt_uint64_t bus_start; + +#define PCI_BUS_REGION_F_NONE 0xffffffff /* PCI no memory */ +#define PCI_BUS_REGION_F_MEM 0x00000000 /* PCI memory space */ +#define PCI_BUS_REGION_F_IO 0x00000001 /* PCI IO space */ +#define PCI_BUS_REGION_F_PREFETCH 0x00000008 /* Prefetchable PCI memory */ + rt_ubase_t flags; +}; + +struct rt_pci_bus_resource +{ + rt_ubase_t base; + rt_size_t size; + + rt_ubase_t flags; +}; + +/* + * PCI topology: + * + * +-----+-----+ +-------------+ PCI Bus 0 +------------+ PCI Bus 1 + * | RAM | CPU |---------| Host Bridge |--------+-----| PCI Bridge |-----+ + * +-----+-----+ +-------------+ | +------------+ | +-------------+ + * | +----| End Point 2 | + * +-------------+ +-------------+ | +-------------+ | +-------------+ + * | End Point 5 |----+ | End Point 0 |-------+ | End Point 3 |----+ + * +-------------+ | +-------------+ | +-------------+ | + * | | | + * +-------------+ | +-------------+ | +-------------+ | +-------------+ + * | End Point 6 |----+----| ISA Bridge |-------+-----| End Point 1 | +----| End Point 4 | + * +-------------+ +-------------+ | +-------------+ +-------------+ + * | + * +------+ +----------------+ | + * | Port |---------| CardBus Bridge |----+ + * +------+ +----------------+ + */ + +struct rt_pci_bus; + +struct rt_pci_device_id +{ +#define PCI_ANY_ID (~0) +#define RT_PCI_DEVICE_ID(vend, dev) \ + .vendor = (vend), \ + .device = (dev), \ + .subsystem_vendor = PCI_ANY_ID, \ + .subsystem_device = PCI_ANY_ID + +#define RT_PCI_DEVICE_CLASS(dev_class, dev_class_mask) \ + .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \ + .subsystem_vendor = PCI_ANY_ID, \ + .subsystem_device = PCI_ANY_ID, \ + .class = (dev_class), .class_mask = (dev_class_mask), + + rt_uint32_t vendor, device; /* Vendor and device ID or PCI_ANY_ID */ + rt_uint32_t subsystem_vendor; /* Subsystem ID's or PCI_ANY_ID */ + rt_uint32_t subsystem_device; /* Subsystem ID's or PCI_ANY_ID */ + rt_uint32_t class, class_mask; /* (class, subclass, prog-if) triplet */ + + const void *data; +}; + +struct rt_pci_device +{ + struct rt_device parent; + const char *name; + + rt_list_t list; + struct rt_pci_bus *bus; + struct rt_pci_bus *subbus; /* In PCI-to-PCI bridge, 'End Point' or 'Port' is NULL */ + + const struct rt_pci_device_id *id; + + rt_uint32_t devfn; /* Encoded device & function index */ + rt_uint16_t vendor; + rt_uint16_t device; + rt_uint16_t subsystem_vendor; + rt_uint16_t subsystem_device; + rt_uint32_t class; /* 3 bytes: (base, sub, prog-if) */ + rt_uint8_t revision; + rt_uint8_t hdr_type; + rt_uint8_t max_latency; + rt_uint8_t min_grantl; + rt_uint8_t int_pin; + rt_uint8_t int_line; + rt_uint16_t exp_flags; + rt_uint32_t cfg_size; + + void *sysdata; + + int irq; + rt_uint8_t pin; + struct rt_pic *intx_pic; + + struct rt_pci_bus_resource resource[RT_PCI_BAR_NR_MAX]; + + rt_uint8_t pme_cap; + rt_uint8_t msi_cap; + rt_uint8_t msix_cap; + rt_uint8_t pcie_cap; + + rt_uint8_t busmaster:1; /* Is the bus master */ + rt_uint8_t multi_function:1; /* Multi-function device */ + rt_uint8_t ari_enabled:1; /* Alternative Routing-ID Interpretation */ + rt_uint8_t no_msi:1; /* May not use MSI */ + rt_uint8_t no_64bit_msi:1; /* May only use 32-bit MSIs */ + rt_uint8_t msi_enabled:1; /* MSI enable */ + rt_uint8_t msix_enabled:1; /* MSIx enable */ + rt_uint8_t broken_intx_masking:1; /* INTx masking can't be used */ + rt_uint8_t pme_support:5; /* Bitmask of states from which PME# can be generated */ + +#ifdef RT_PCI_MSI + void *msix_base; + struct rt_pic *msi_pic; + rt_list_t msi_desc_nodes; + struct rt_spinlock msi_lock; +#endif +}; + +struct rt_pci_host_bridge +{ + struct rt_device parent; + + rt_uint32_t domain; + + struct rt_pci_bus *root_bus; + const struct rt_pci_ops *ops; + const struct rt_pci_ops *child_ops; + + rt_uint32_t bus_range[2]; + rt_size_t bus_regions_nr; + struct rt_pci_bus_region *bus_regions; + rt_size_t dma_regions_nr; + struct rt_pci_bus_region *dma_regions; + + rt_uint8_t (*irq_slot)(struct rt_pci_device *pdev, rt_uint8_t *pinp); + int (*irq_map)(struct rt_pci_device *pdev, rt_uint8_t slot, rt_uint8_t pin); + + void *sysdata; + rt_uint8_t priv[0]; +}; +#define rt_device_to_pci_host_bridge(dev) rt_container_of(dev, struct rt_pci_host_bridge, parent) + +struct rt_pci_ops +{ + rt_err_t (*add)(struct rt_pci_bus *bus); + rt_err_t (*remove)(struct rt_pci_bus *bus); + + void *(*map)(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg); + + rt_err_t (*read)(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t *value); + rt_err_t (*write)(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t value); +}; + +struct rt_pci_bus +{ + rt_list_t list; + rt_list_t children_nodes; + rt_list_t devices_nodes; + struct rt_pci_bus *parent; + + union + { + /* In PCI-to-PCI bridge, parent is not NULL */ + struct rt_pci_device *self; + /* In Host bridge, this is Root bus ('PCI Bus 0') */ + struct rt_pci_host_bridge *host_bridge; + }; + + const struct rt_pci_ops *ops; + + char name[48]; + char number; + struct rt_spinlock lock; + + void *sysdata; +}; + +struct rt_pci_driver +{ + struct rt_driver parent; + + const char *name; + const struct rt_pci_device_id *ids; + + rt_err_t (*probe)(struct rt_pci_device *pdev); + rt_err_t (*remove)(struct rt_pci_device *pdev); + rt_err_t (*shutdown)(struct rt_pci_device *pdev); +}; + +struct rt_pci_msix_entry +{ + int irq; + int index; +}; + +enum rt_pci_power +{ + RT_PCI_D0, + RT_PCI_D1, + RT_PCI_D2, + RT_PCI_D3HOT, + RT_PCI_D3COLD, + + RT_PCI_PME_MAX, +}; + +void rt_pci_pme_init(struct rt_pci_device *pdev); +void rt_pci_pme_active(struct rt_pci_device *pdev, rt_bool_t enable); +rt_err_t rt_pci_enable_wake(struct rt_pci_device *pci_dev, + enum rt_pci_power state, rt_bool_t enable); +rt_inline rt_bool_t rt_pci_pme_capable(struct rt_pci_device *pdev, + enum rt_pci_power state) +{ + if (!pdev->pme_cap) + { + return RT_FALSE; + } + + return !!(pdev->pme_support & (1 << state)); +} + +void rt_pci_msi_init(struct rt_pci_device *pdev); +void rt_pci_msix_init(struct rt_pci_device *pdev); + +void rt_pci_set_master(struct rt_pci_device *pdev); +void rt_pci_clear_master(struct rt_pci_device *pdev); + +struct rt_pci_host_bridge *rt_pci_host_bridge_alloc(rt_size_t priv_size); +rt_err_t rt_pci_host_bridge_free(struct rt_pci_host_bridge *); +rt_err_t rt_pci_host_bridge_init(struct rt_pci_host_bridge *host_bridge); +rt_err_t rt_pci_host_bridge_probe(struct rt_pci_host_bridge *host_bridge); + +struct rt_pci_device *rt_pci_alloc_device(struct rt_pci_bus *bus); +struct rt_pci_device *rt_pci_scan_single_device(struct rt_pci_bus *bus, rt_uint32_t devfn); +rt_err_t rt_pci_setup_device(struct rt_pci_device *pdev); +rt_size_t rt_pci_scan_slot(struct rt_pci_bus *bus, rt_uint32_t devfn); +rt_uint32_t rt_pci_scan_child_buses(struct rt_pci_bus *bus, rt_size_t buses); +rt_uint32_t rt_pci_scan_child_bus(struct rt_pci_bus *bus); + +rt_err_t rt_pci_host_bridge_register(struct rt_pci_host_bridge *host_bridge); +rt_err_t rt_pci_scan_root_bus_bridge(struct rt_pci_host_bridge *host_bridge); + +rt_err_t rt_pci_host_bridge_remove(struct rt_pci_host_bridge *host_bridge); +rt_err_t rt_pci_bus_remove(struct rt_pci_bus *bus); +rt_err_t rt_pci_device_remove(struct rt_pci_device *pdev); + +rt_uint32_t rt_pci_domain(struct rt_pci_device *pdev); + +rt_uint8_t rt_pci_bus_find_capability(struct rt_pci_bus *bus, rt_uint32_t devfn, int cap); +rt_uint8_t rt_pci_find_capability(struct rt_pci_device *pdev, int cap); +rt_uint8_t rt_pci_find_next_capability(struct rt_pci_device *pdev, rt_uint8_t pos, int cap); + +rt_uint16_t rt_pci_find_ext_capability(struct rt_pci_device *pdev, int cap); +rt_uint16_t rt_pci_find_ext_next_capability(struct rt_pci_device *pdev, rt_uint16_t pos, int cap); + +struct rt_pci_bus *rt_pci_find_root_bus(struct rt_pci_bus *bus); +struct rt_pci_host_bridge *rt_pci_find_host_bridge(struct rt_pci_bus *bus); + +rt_inline rt_uint16_t rt_pci_dev_id(struct rt_pci_device *pdev) +{ + return RT_PCI_DEVID(pdev->bus->number, pdev->devfn); +} + +rt_inline rt_bool_t rt_pci_is_root_bus(struct rt_pci_bus *bus) +{ + return bus->parent ? RT_FALSE : RT_TRUE; +} + +rt_inline rt_bool_t rt_pci_is_bridge(struct rt_pci_device *pdev) +{ + return pdev->hdr_type == PCIM_HDRTYPE_BRIDGE || + pdev->hdr_type == PCIM_HDRTYPE_CARDBUS; +} + +rt_inline rt_bool_t rt_pci_is_pcie(struct rt_pci_device *pdev) +{ + return !!pdev->pcie_cap; +} + +#define rt_pci_foreach_bridge(pdev, bus) \ + rt_list_for_each_entry(pdev, &bus->devices_nodes, list) \ + if (rt_pci_is_bridge(pdev)) + +rt_err_t rt_pci_bus_read_config_u8(struct rt_pci_bus *bus, + rt_uint32_t devfn, int pos, rt_uint8_t *value); +rt_err_t rt_pci_bus_read_config_u16(struct rt_pci_bus *bus, + rt_uint32_t devfn, int pos, rt_uint16_t *value); +rt_err_t rt_pci_bus_read_config_u32(struct rt_pci_bus *bus, + rt_uint32_t devfn, int pos, rt_uint32_t *value); + +rt_err_t rt_pci_bus_write_config_u8(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, rt_uint8_t value); +rt_err_t rt_pci_bus_write_config_u16(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, rt_uint16_t value); +rt_err_t rt_pci_bus_write_config_u32(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, rt_uint32_t value); + +rt_err_t rt_pci_bus_read_config_uxx(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t *value); +rt_err_t rt_pci_bus_write_config_uxx(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t value); + +rt_err_t rt_pci_bus_read_config_generic_u32(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t *value); +rt_err_t rt_pci_bus_write_config_generic_u32(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t value); + +rt_inline rt_err_t rt_pci_read_config_u8(const struct rt_pci_device *pdev, + int reg, rt_uint8_t *value) +{ + return rt_pci_bus_read_config_u8(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_err_t rt_pci_read_config_u16(const struct rt_pci_device *pdev, + int reg, rt_uint16_t *value) +{ + return rt_pci_bus_read_config_u16(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_err_t rt_pci_read_config_u32(const struct rt_pci_device *pdev, + int reg, rt_uint32_t *value) +{ + return rt_pci_bus_read_config_u32(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_err_t rt_pci_write_config_u8(const struct rt_pci_device *pdev, + int reg, rt_uint8_t value) +{ + return rt_pci_bus_write_config_u8(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_err_t rt_pci_write_config_u16(const struct rt_pci_device *pdev, + int reg, rt_uint16_t value) +{ + return rt_pci_bus_write_config_u16(pdev->bus, pdev->devfn, reg, value); +} + +rt_inline rt_err_t rt_pci_write_config_u32(const struct rt_pci_device *pdev, + int reg, rt_uint32_t value) +{ + return rt_pci_bus_write_config_u32(pdev->bus, pdev->devfn, reg, value); +} + +#ifdef RT_USING_OFW +int rt_pci_ofw_irq_parse_and_map(struct rt_pci_device *pdev, + rt_uint8_t slot, rt_uint8_t pin); + +rt_err_t rt_pci_ofw_parse_ranges(struct rt_ofw_node *dev_np, + struct rt_pci_host_bridge *host_bridge); + +rt_err_t rt_pci_ofw_host_bridge_init(struct rt_ofw_node *dev_np, + struct rt_pci_host_bridge *host_bridge); + +rt_err_t rt_pci_ofw_bus_init(struct rt_pci_bus *bus); +rt_err_t rt_pci_ofw_bus_free(struct rt_pci_bus *bus); +rt_err_t rt_pci_ofw_device_init(struct rt_pci_device *pdev); +rt_err_t rt_pci_ofw_device_free(struct rt_pci_device *pdev); +#else +rt_inline rt_err_t rt_pci_ofw_host_bridge_init(struct rt_ofw_node *dev_np, + struct rt_pci_host_bridge *host_bridge) +{ + return RT_EOK; +} +rt_inline rt_err_t rt_pci_ofw_bus_init(struct rt_pci_bus *bus) +{ + return RT_EOK; +} +rt_inline rt_err_t rt_pci_ofw_bus_free(struct rt_pci_bus *bus) +{ + return RT_EOK; +} +rt_inline rt_err_t rt_pci_ofw_device_init(struct rt_pci_device *pdev) +{ + return RT_EOK; +} +rt_inline rt_err_t rt_pci_ofw_device_free(struct rt_pci_device *pdev) +{ + return RT_EOK; +} +rt_inline int rt_pci_ofw_irq_parse_and_map(struct rt_pci_device *pdev, + rt_uint8_t slot, rt_uint8_t pin) +{ + return -1; +} +rt_inline rt_err_t rt_pci_ofw_parse_ranges(struct rt_ofw_node *dev_np, + struct rt_pci_host_bridge *host_bridge) +{ + return -RT_ENOSYS; +} +#endif /* RT_USING_OFW */ + +rt_inline void *rt_pci_iomap(struct rt_pci_device *pdev, int bar_idx) +{ + struct rt_pci_bus_resource *res = &pdev->resource[bar_idx]; + + RT_ASSERT(bar_idx < RT_ARRAY_SIZE(pdev->resource)); + + return rt_ioremap((void *)res->base, res->size); +} + +rt_uint8_t rt_pci_irq_intx(struct rt_pci_device *pdev, rt_uint8_t pin); +rt_uint8_t rt_pci_irq_slot(struct rt_pci_device *pdev, rt_uint8_t *pinp); + +void rt_pci_assign_irq(struct rt_pci_device *pdev); + +void rt_pci_intx(struct rt_pci_device *pdev, rt_bool_t enable); +rt_bool_t rt_pci_check_and_mask_intx(struct rt_pci_device *pdev); +rt_bool_t rt_pci_check_and_unmask_intx(struct rt_pci_device *pdev); + +void rt_pci_irq_mask(struct rt_pci_device *pdev); +void rt_pci_irq_unmask(struct rt_pci_device *pdev); + +#define RT_PCI_IRQ_F_LEGACY RT_BIT(0) /* Allow legacy interrupts */ +#define RT_PCI_IRQ_F_MSI RT_BIT(1) /* Allow MSI interrupts */ +#define RT_PCI_IRQ_F_MSIX RT_BIT(2) /* Allow MSI-X interrupts */ +#define RT_PCI_IRQ_F_AFFINITY RT_BIT(3) /* Auto-assign affinity */ +#define RT_PCI_IRQ_F_ALL_TYPES (RT_PCI_IRQ_F_LEGACY | RT_PCI_IRQ_F_MSI | RT_PCI_IRQ_F_MSIX) + +#ifdef RT_PCI_MSI +rt_ssize_t rt_pci_alloc_vector(struct rt_pci_device *pdev, int min, int max, + rt_uint32_t flags, RT_IRQ_AFFINITY_DECLARE((*affinities))); +void rt_pci_free_vector(struct rt_pci_device *pdev); + +rt_ssize_t rt_pci_msi_vector_count(struct rt_pci_device *pdev); +rt_err_t rt_pci_msi_disable(struct rt_pci_device *pdev); +rt_ssize_t rt_pci_msi_enable_range_affinity(struct rt_pci_device *pdev, + int min, int max, RT_IRQ_AFFINITY_DECLARE((*affinities))); + +rt_ssize_t rt_pci_msix_vector_count(struct rt_pci_device *pdev); +rt_err_t rt_pci_msix_disable(struct rt_pci_device *pdev); +rt_ssize_t rt_pci_msix_enable_range_affinity(struct rt_pci_device *pdev, + struct rt_pci_msix_entry *entries, int min, int max, + RT_IRQ_AFFINITY_DECLARE((*affinities))); +#else +rt_inline rt_ssize_t rt_pci_alloc_vector(struct rt_pci_device *pdev, int min, int max, + rt_uint32_t flags, RT_IRQ_AFFINITY_DECLARE((*affinities))) +{ + return -RT_ENOSYS; +} + +rt_inline void rt_pci_free_vector(struct rt_pci_device *pdev) +{ + return; +} + +rt_inline rt_ssize_t rt_pci_msi_vector_count(struct rt_pci_device *pdev) +{ + return 0; +} + +rt_inline rt_err_t rt_pci_msi_disable(struct rt_pci_device *pdev) +{ + return RT_EOK; +} + +rt_inline rt_ssize_t rt_pci_msi_enable_range_affinity(struct rt_pci_device *pdev, + int min, int max, RT_IRQ_AFFINITY_DECLARE((*affinities))) +{ + return -RT_ENOSYS; +} + +rt_inline rt_ssize_t rt_pci_msix_vector_count(struct rt_pci_device *pdev) +{ + return 0; +} + +rt_inline rt_err_t rt_pci_msix_disable(struct rt_pci_device *pdev) +{ + return RT_EOK; +} + +rt_inline rt_ssize_t rt_pci_msix_enable_range_affinity(struct rt_pci_device *pdev, + struct rt_pci_msix_entry *entries, int min, int max, + RT_IRQ_AFFINITY_DECLARE((*affinities))) +{ + return -RT_ENOSYS; +} +#endif /* RT_PCI_MSI */ + +rt_inline void rt_pci_msix_entry_index_linear(struct rt_pci_msix_entry *entries, + rt_size_t nvectors) +{ + for (int i = 0; i < nvectors; ++i) + { + entries[i].index = i; + } +} + +rt_inline rt_ssize_t rt_pci_msi_enable_range(struct rt_pci_device *pdev, + int min, int max) +{ + return rt_pci_msi_enable_range_affinity(pdev, min, max, RT_NULL); +} + +rt_inline rt_err_t rt_pci_msi_enable(struct rt_pci_device *pdev) +{ + rt_ssize_t res = rt_pci_msi_enable_range(pdev, 1, 1); + return res == 1 ? res : RT_EOK; +} + +rt_inline rt_ssize_t rt_pci_msix_enable_range(struct rt_pci_device *pdev, + struct rt_pci_msix_entry *entries, int min, int max) +{ + return rt_pci_msix_enable_range_affinity(pdev, entries, min, max, RT_NULL); +} + +rt_inline rt_ssize_t rt_pci_msix_enable(struct rt_pci_device *pdev, + struct rt_pci_msix_entry *entries, int count) +{ + return rt_pci_msix_enable_range(pdev, entries, count, count); +} + +rt_err_t rt_pci_region_setup(struct rt_pci_host_bridge *host_bridge); +struct rt_pci_bus_region *rt_pci_region_alloc(struct rt_pci_host_bridge *host_bridge, + void **out_addr, rt_size_t size, rt_ubase_t flags, rt_bool_t mem64); + +rt_err_t rt_pci_device_alloc_resource(struct rt_pci_host_bridge *host_bridge, + struct rt_pci_device *pdev); + +void rt_pci_enum_device(struct rt_pci_bus *bus, + rt_bool_t (callback(struct rt_pci_device *, void *)), void *data); + +const struct rt_pci_device_id *rt_pci_match_id(struct rt_pci_device *pdev, + const struct rt_pci_device_id *id); + +const struct rt_pci_device_id *rt_pci_match_ids(struct rt_pci_device *pdev, + const struct rt_pci_device_id *ids); + +rt_err_t rt_pci_driver_register(struct rt_pci_driver *pdrv); +rt_err_t rt_pci_device_register(struct rt_pci_device *pdev); +struct rt_pci_bus_resource *rt_pci_find_bar(struct rt_pci_device* pdev,rt_ubase_t flags,int index); +#define RT_PCI_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, pci, BUILIN) + +extern struct rt_spinlock rt_pci_lock; + +#endif /* __PCI_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/pci_endpoint.h b/rt-thread/components/drivers/include/drivers/pci_endpoint.h new file mode 100644 index 0000000..c43c182 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/pci_endpoint.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#ifndef __PCI_ENDPOINT_H__ +#define __PCI_ENDPOINT_H__ + +#include + +enum rt_pci_ep_pin +{ + RT_PCI_EP_PIN_UNKNOWN, + RT_PCI_EP_PIN_INTA, + RT_PCI_EP_PIN_INTB, + RT_PCI_EP_PIN_INTC, + RT_PCI_EP_PIN_INTD, +}; + +enum rt_pci_ep_irq +{ + RT_PCI_EP_IRQ_UNKNOWN, + RT_PCI_EP_IRQ_LEGACY, + RT_PCI_EP_IRQ_MSI, + RT_PCI_EP_IRQ_MSIX, +}; + +struct rt_pci_ep_header +{ + rt_uint16_t vendor; + rt_uint16_t device; + rt_uint8_t revision; + rt_uint8_t progif; + rt_uint8_t subclass; + rt_uint8_t class_code; + rt_uint8_t cache_line_size; + rt_uint16_t subsystem_vendor; + rt_uint16_t subsystem_device; + + enum rt_pci_ep_pin intx; +}; + +struct rt_pci_ep_bar +{ + /* To PCI Bus */ + struct rt_pci_bus_resource bus; + /* To CPU */ + rt_ubase_t cpu_addr; +}; + +/* + * Type of MSI-X table, For more format detail, + * please read `components/drivers/include/drivers/pci_msi.h` + */ +struct rt_pci_ep_msix_tbl +{ + union + { + rt_uint64_t msg_addr; + struct + { + rt_uint32_t msg_addr_upper; + rt_uint32_t msg_addr_lower; + }; + }; + rt_uint32_t msg_data; + rt_uint32_t vector_ctrl; +}; + +struct rt_pci_ep_ops; +struct rt_pci_ep_mem; + +struct rt_pci_ep +{ + rt_list_t list; + const char *name; + + struct rt_ref ref; + + const struct rt_device *rc_dev; + const struct rt_pci_ep_ops *ops; + + rt_size_t mems_nr; + struct rt_pci_ep_mem *mems; + + rt_uint8_t max_functions; + RT_BITMAP_DECLARE(functions_map, 8); + rt_list_t epf_nodes; + struct rt_mutex lock; + + void *priv; +}; + +struct rt_pci_ep_mem +{ + rt_ubase_t cpu_addr; + rt_size_t size; + rt_size_t page_size; + + rt_bitmap_t *map; + rt_size_t bits; +}; + +struct rt_pci_epf +{ + rt_list_t list; + const char *name; + + struct rt_pci_ep_header *header; + struct rt_pci_ep_bar bar[PCI_STD_NUM_BARS]; + + rt_uint8_t msi_interrupts; + rt_uint16_t msix_interrupts; + rt_uint8_t func_no; + + struct rt_pci_ep *ep; +}; + +struct rt_pci_ep_ops +{ + rt_err_t (*write_header)(struct rt_pci_ep *ep, rt_uint8_t func_no, + struct rt_pci_ep_header *hdr); + + rt_err_t (*set_bar)(struct rt_pci_ep *ep, rt_uint8_t func_no, + struct rt_pci_ep_bar *bar, int bar_idx); + rt_err_t (*clear_bar)(struct rt_pci_ep *ep, rt_uint8_t func_no, + struct rt_pci_ep_bar *bar, int bar_idx); + + rt_err_t (*map_addr)(struct rt_pci_ep *ep, rt_uint8_t func_no, + rt_ubase_t addr, rt_uint64_t pci_addr, rt_size_t size); + rt_err_t (*unmap_addr)(struct rt_pci_ep *ep, rt_uint8_t func_no, rt_ubase_t addr); + + rt_err_t (*set_msi)(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned irq_nr); + rt_err_t (*get_msi)(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned *out_irq_nr); + + rt_err_t (*set_msix)(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned irq_nr, int bar_idx, rt_off_t offset); + rt_err_t (*get_msix)(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned *out_irq_nr); + + rt_err_t (*raise_irq)(struct rt_pci_ep *ep, rt_uint8_t func_no, + enum rt_pci_ep_irq type, unsigned irq); + + rt_err_t (*start)(struct rt_pci_ep *ep); + rt_err_t (*stop)(struct rt_pci_ep *ep); +}; + +rt_err_t rt_pci_ep_write_header(struct rt_pci_ep *ep, rt_uint8_t func_no, + struct rt_pci_ep_header *hdr); + +rt_err_t rt_pci_ep_set_bar(struct rt_pci_ep *ep, rt_uint8_t func_no, + struct rt_pci_ep_bar *bar, int bar_idx); +rt_err_t rt_pci_ep_clear_bar(struct rt_pci_ep *ep, rt_uint8_t func_no, + struct rt_pci_ep_bar *bar, int bar_idx); + +rt_err_t rt_pci_ep_map_addr(struct rt_pci_ep *ep, rt_uint8_t func_no, + rt_ubase_t addr, rt_uint64_t pci_addr, rt_size_t size); +rt_err_t rt_pci_ep_unmap_addr(struct rt_pci_ep *ep, rt_uint8_t func_no, + rt_ubase_t addr); + +rt_err_t rt_pci_ep_set_msi(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned irq_nr); +rt_err_t rt_pci_ep_get_msi(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned *out_irq_nr); + +rt_err_t rt_pci_ep_set_msix(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned irq_nr, int bar_idx, rt_off_t offset); +rt_err_t rt_pci_ep_get_msix(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned *out_irq_nr); + +rt_err_t rt_pci_ep_raise_irq(struct rt_pci_ep *ep, rt_uint8_t func_no, + enum rt_pci_ep_irq type, unsigned irq); + +rt_err_t rt_pci_ep_start(struct rt_pci_ep *ep); +rt_err_t rt_pci_ep_stop(struct rt_pci_ep *ep); + +rt_err_t rt_pci_ep_register(struct rt_pci_ep *ep); +rt_err_t rt_pci_ep_unregister(struct rt_pci_ep *ep); + +rt_err_t rt_pci_ep_mem_array_init(struct rt_pci_ep *ep, + struct rt_pci_ep_mem *mems, rt_size_t mems_nr); +rt_err_t rt_pci_ep_mem_init(struct rt_pci_ep *ep, + rt_ubase_t cpu_addr, rt_size_t size, rt_size_t page_size); + +void *rt_pci_ep_mem_alloc(struct rt_pci_ep *ep, + rt_ubase_t *out_cpu_addr, rt_size_t size); +void rt_pci_ep_mem_free(struct rt_pci_ep *ep, + void *vaddr, rt_ubase_t cpu_addr, rt_size_t size); + +rt_err_t rt_pci_ep_add_epf(struct rt_pci_ep *ep, struct rt_pci_epf *epf); +rt_err_t rt_pci_ep_remove_epf(struct rt_pci_ep *ep, struct rt_pci_epf *epf); + +struct rt_pci_ep *rt_pci_ep_get(const char *name); +void rt_pci_ep_put(struct rt_pci_ep *ep); + +#endif /* __PCI_ENDPOINT_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/pci_msi.h b/rt-thread/components/drivers/include/drivers/pci_msi.h new file mode 100644 index 0000000..b8238e6 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/pci_msi.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#ifndef __PCI_MSI_H__ +#define __PCI_MSI_H__ + +#include + +/* + * MSI Format: + * T0: 32-bit Address + * T1: 64-bit Address + * T2: 32-bit Address with Per-Vector Masking + * T3: 64-bit Address with Per-Vector Masking + * + * 31 16 15 8 7 0 + * +---------------------------+-----------------+---------------+ + * | Message Control | Next Capability | Capability ID | DW0 + * | | Pointer | (05h) | + * +---------------------------+-----------------+---------------+ + * | Message Address [31:0] | DW1 + * +-------------------------------------------------------------+ + * | Message Address [63:32] | DW2 (T1: only 64-bit) + * +---------------------------+---------------------------------+ + * | Reserved | Message Data | DW3 + * +---------------------------+---------------------------------+ + * | Mask Bits | DW4 (T2/T3: only with Per-Vector Masking) + * +-------------------------------------------------------------+ + * | Pending Bits | DW5 (T2/T3: only with Per-Vector Masking) + * +-------------------------------------------------------------+ + * + * MSI Message Control: + * + * 15 9 8 7 6 4 3 1 0 + * +----------------------+---+---+---------------+----------+---+ + * | Reserved | | | | | | + * +----------------------+---+---+---------------+----------+---+ + * ^ ^ ^ ^ ^ + * | | | | | + * | | | | +---- MSI Enable (RW) + * | | | +----------- Multiple Message Capable (RO, log2n, [n <= 5]) + * | | +------------------------- Multiple Message Enable (RW, log2n, [n <= 5]) + * | +----------------------------------- 64-bit Address Capable + * +--------------------------------------- Per-Vector Masking Capable + */ + +struct rt_pci_msi_conf +{ + rt_uint32_t mask; + rt_uint8_t mask_pos; + int default_irq; + + struct + { + rt_uint8_t is_masking:1; + rt_uint8_t is_64bit:1; + rt_uint8_t multi_msg_max:3; /* log2 num of messages allocated */ + rt_uint8_t multi_msg_use:3; /* log2 num of messages supported */ + } cap; +}; + +/* + * MSI-X Format: + * + * 31 16 15 8 7 0 + * +---------------------------+-----------------+---------------+ + * | Message Control | Next Capability | Capability ID | DW0 + * | | Pointer | (11h) | + * +---------------------------+-----------------+---+-----------+ + * | MSI-X Table Offset | Table BIR | DW1 (BIR: BAR Index Register) + * +-------------------------------------------------+-----------+ | + * | Pending Bit Array (PBA) Offset | PBA BIR | DW2 --------+ | + * +-------------------------------------------------+-----------+ | | + * | | + * MSI-X Message Control: | | + * | | + * 15 14 13 11 10 0 | | + * +---+---+----------+------------------------------------------+ | | + * | | | Reserved | Table Size in N-1 (RO) | | | + * +---+---+----------+------------------------------------------+ | | + * ^ ^ | | + * | | | | + * | +---- Function Mask (RW) | | + * +-------- MSI-X Enable (RW) | | + * | | + * MSI-X Table (BAR[Table BIR] + MSI-X Table Offset): | | + * | | + * DW3 DW2 DW1 DW0 | | + * +----------------+--------------+---------------+---------------+ <---------|-+ + * | Vector Control | Message Data | Upper Address | Lower Address | Entry 0 | + * +----------------+--------------+---------------+---------------+ | + * | Vector Control | Message Data | Upper Address | Lower Address | Entry 1 | + * +----------------+--------------+---------------+---------------+ | + * | ...... | ...... | ...... | ...... | | + * +----------------+--------------+---------------+---------------+ | + * | Vector Control | Message Data | Upper Address | Lower Address | Entry N-1 | + * +----------------+--------------+---------------+---------------+ | + * ^ | + * | | + * +---- Bit 0 is vector Mask Bit (R/W) | + * | + * MSI-X Pending Bit Array (BAR[PBA BIR] + Pending Bit Array Offset): | + * | + * DW1 DW0 | + * +-------------------------------+ <-----------------------------------------+ + * | Pending Bits 0 - 63 | QW 0 + * +-------------------------------+ + * | Pending Bits 64 - 127 | QW 1 + * +-------------------------------+ + * | ...... | + * +-------------------------------+ + * | Pending Bits | QW (N-1)/64 + * +-------------------------------+ + */ + +struct rt_pci_msix_conf +{ + int index; + + rt_uint32_t msg_ctrl; + void *table_base; +}; + +struct rt_pci_msi_msg +{ + rt_uint32_t address_lo; + rt_uint32_t address_hi; + rt_uint32_t data; +}; + +struct rt_pci_msi_desc +{ + rt_list_t list; + + int irq; + rt_size_t vector_used; + rt_size_t vector_count; + + union + { + /* For MSI-X */ + rt_bitmap_t *affinity; + /* For MSI */ + rt_bitmap_t **affinities; + }; + + struct rt_pci_device *pdev; + struct rt_pci_msi_msg msg; + + void *write_msi_msg_data; + void (*write_msi_msg)(struct rt_pci_msi_desc *, void *); + + rt_bool_t is_msix; + union + { + struct rt_pci_msi_conf msi; + struct rt_pci_msix_conf msix; + }; + + void *priv; +}; + +#define rt_pci_msi_first_desc(pdev) \ + (rt_list_isempty(&(pdev)->msi_desc_nodes) ? RT_NULL : \ + rt_list_first_entry(&(pdev)->msi_desc_nodes, struct rt_pci_msi_desc, list)) + +#define rt_pci_msi_for_each_desc(pdev, desc) \ + rt_list_for_each_entry(desc, &(pdev)->msi_desc_nodes, list) + +#define rt_pci_msix_table_size(flags) ((flags & PCIM_MSIXCTRL_TABLE_SIZE) + 1) + +rt_err_t rt_pci_msi_setup_irqs(struct rt_pci_device *pdev, int nvec, int type); + +void rt_pci_msi_shutdown(struct rt_pci_device *pdev); +void rt_pci_msix_shutdown(struct rt_pci_device *pdev); +void rt_pci_msi_free_irqs(struct rt_pci_device *pdev); +void rt_pci_msi_write_msg(struct rt_pci_msi_desc *desc, struct rt_pci_msi_msg *msg); + +void rt_pci_msi_mask_irq(struct rt_pic_irq *pirq); +void rt_pci_msi_unmask_irq(struct rt_pic_irq *pirq); + +#endif /* __PCI_MSI_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/phy.h b/rt-thread/components/drivers/include/drivers/phy.h index c51c494..a8dcd08 100644 --- a/rt-thread/components/drivers/include/drivers/phy.h +++ b/rt-thread/components/drivers/include/drivers/phy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -7,18 +7,172 @@ * Date Author Notes * 2020-10-14 wangqiang the first version * 2022-08-17 xjy198903 add 1000M definition + * 2024-10-08 zhujiale add phy v2.0 */ - -#ifndef __PHY_H__ -#define __PHY_H__ - +#ifndef __NET_PHY_H__ +#define __NET_PHY_H__ #include +#include +#ifdef RT_USING_PHY_V2 +#include +#include +#include +#define RT_PHY_FIXED_ID 0xa5a55a5a +#define RT_PHY_NCSI_ID 0xbeefcafe + + +/* Indicates what features are supported by the interface. */ +#define RT_SUPPORTED_10baseT_Half (1 << 0) +#define RT_SUPPORTED_10baseT_Full (1 << 1) +#define RT_SUPPORTED_100baseT_Half (1 << 2) +#define RT_SUPPORTED_100baseT_Full (1 << 3) +#define RT_SUPPORTED_1000baseT_Half (1 << 4) +#define RT_SUPPORTED_1000baseT_Full (1 << 5) +#define RT_SUPPORTED_Autoneg (1 << 6) +#define RT_SUPPORTED_TP (1 << 7) +#define RT_SUPPORTED_AUI (1 << 8) +#define RT_SUPPORTED_MII (1 << 9) +#define RT_SUPPORTED_FIBRE (1 << 10) +#define RT_SUPPORTED_BNC (1 << 11) +#define RT_SUPPORTED_10000baseT_Full (1 << 12) +#define RT_SUPPORTED_Pause (1 << 13) +#define RT_SUPPORTED_Asym_Pause (1 << 14) +#define RT_SUPPORTED_2500baseX_Full (1 << 15) +#define RT_SUPPORTED_Backplane (1 << 16) +#define RT_SUPPORTED_1000baseKX_Full (1 << 17) +#define RT_SUPPORTED_10000baseKX4_Full (1 << 18) +#define RT_SUPPORTED_10000baseKR_Full (1 << 19) +#define RT_SUPPORTED_10000baseR_FEC (1 << 20) +#define RT_SUPPORTED_1000baseX_Half (1 << 21) +#define RT_SUPPORTED_1000baseX_Full (1 << 22) + +#define RT_PHY_FLAG_BROKEN_RESET (1 << 0) /* soft reset not supported */ +#define RT_PHY_DEFAULT_FEATURES (RT_SUPPORTED_Autoneg | RT_SUPPORTED_TP | RT_SUPPORTED_MII) + +#define RT_PHY_10BT_FEATURES (RT_SUPPORTED_10baseT_Half | RT_SUPPORTED_10baseT_Full) + +#define RT_PHY_100BT_FEATURES (RT_SUPPORTED_100baseT_Half | RT_SUPPORTED_100baseT_Full) + +#define RT_PHY_1000BT_FEATURES (RT_SUPPORTED_1000baseT_Half | RT_SUPPORTED_1000baseT_Full) + +#define RT_PHY_BASIC_FEATURES (RT_PHY_10BT_FEATURES | RT_PHY_100BT_FEATURES | RT_PHY_DEFAULT_FEATURES) + +#define RT_PHY_GBIT_FEATURES (RT_PHY_BASIC_FEATURES | RT_PHY_1000BT_FEATURES) + +#define RT_PHY_10G_FEATURES (RT_PHY_GBIT_FEATURES | RT_SUPPORTED_10000baseT_Full) +struct rt_phy_device +{ + struct rt_device parent; + struct mii_bus *bus; + struct rt_phy_driver *drv; + rt_uint32_t phy_id; + rt_uint32_t mmds; + int speed; + int duplex; + int link; + int port; + rt_uint32_t advertising; + rt_uint32_t supported; + rt_bool_t autoneg; + int pause; + rt_ubase_t addr; + rt_bool_t is_c45; + rt_uint32_t flags; + rt_phy_interface interface; + +#ifdef RT_USING_OFW + struct rt_ofw_node *node; +#endif + void *priv; +}; + +struct rt_phy_driver +{ + struct rt_driver parent; + char name[RT_NAME_MAX]; + rt_uint64_t uid; + rt_uint64_t mask; + rt_uint64_t mmds; + rt_uint32_t features; + + int (*probe)(struct rt_phy_device *phydev); + int (*config)(struct rt_phy_device *phydev); + int (*startup)(struct rt_phy_device *phydev); + int (*shutdown)(struct rt_phy_device *phydev); + int (*read)(struct rt_phy_device *phydev, int addr, int devad, int reg); + int (*write)(struct rt_phy_device *phydev, int addr, int devad, int reg, + rt_uint16_t val); + int (*read_mmd)(struct rt_phy_device *phydev, int devad, int reg); + int (*write_mmd)(struct rt_phy_device *phydev, int devad, int reg, + rt_uint16_t val); + + /* driver private data */ + void *data; +}; + +int rt_phy_read(struct rt_phy_device *phydev, int devad, int regnum); +int rt_phy_write(struct rt_phy_device *phydev, int devad, int regnum, rt_uint16_t val); +int rt_phy_read_mmd(struct rt_phy_device *phydev, int devad, int regnum); +int rt_phy_write_mmd(struct rt_phy_device *phydev, int devad, int regnum, rt_uint16_t val); +int rt_phy_reset(struct rt_phy_device *phydev); +int rt_phy_startup(struct rt_phy_device *phydev); +int rt_phy_config(struct rt_phy_device *phydev); +int rt_phy_shutdown(struct rt_phy_device *phydev); +int rt_phy_read_mmd(struct rt_phy_device *phydev, int devad, int regnum); +int rt_phy_set_supported(struct rt_phy_device *phydev, rt_uint32_t max_speed); + +void rt_phy_mmd_start_indirect(struct rt_phy_device *phydev, int devad, int regnum); + +rt_err_t rt_phy_device_register(struct rt_phy_device *pdev); +rt_err_t rt_phy_driver_register(struct rt_phy_driver *pdrv); +rt_err_t rt_ofw_get_phyid(struct rt_ofw_node *np,rt_uint32_t *id); + +struct rt_phy_device *rt_phy_device_create(struct mii_bus *bus, int addr, rt_uint32_t phy_id, rt_bool_t is_c45); +struct rt_phy_device *rt_phy_find_by_mask(struct mii_bus *bus, unsigned int phy_mask); +struct rt_phy_device *rt_ofw_create_phy(struct mii_bus *bus, struct rt_ofw_node *np, int phyaddr); +struct rt_phy_device *rt_phy_get_device(struct mii_bus *bus, struct rt_ofw_node *np, int addr, rt_phy_interface interface); + +#define RT_PHY_DEVICE_REGISTER(phy_dev) \ +static int rt_##phy_dev##_register(void) \ +{ \ + rt_phy_device_register(&phy_dev); \ + return 0; \ +} \ +INIT_PREV_EXPORT(rt_##phy_dev##_register); + +#define RT_PHY_DRIVER_REGISTER(phy_drv) \ +static int rt_##phy_drv##_register(void) \ +{ \ + rt_phy_driver_register(&phy_drv); \ + return 0; \ +} \ +INIT_PREV_EXPORT(rt_##phy_drv##_register); +#endif + +#ifdef RT_USING_PHY #ifdef __cplusplus extern "C" { #endif +struct rt_mdio_bus_ops +{ + rt_bool_t (*init)(void *bus, rt_uint32_t src_clock_hz); + rt_size_t (*read)(void *bus, rt_uint32_t addr, rt_uint32_t reg, void *data, rt_uint32_t size); + rt_size_t (*write)(void *bus, rt_uint32_t addr, rt_uint32_t reg, void *data, rt_uint32_t size); + rt_bool_t (*uninit)(void *bus); +}; + +struct rt_mdio_bus +{ + void *hw_obj; + char *name; + struct rt_mdio_bus_ops *ops; +}; + +typedef struct rt_mdio_bus rt_mdio_t; + /* Defines the PHY link speed. This is align with the speed for MAC. */ #define PHY_SPEED_10M 0U /* PHY 10M speed. */ #define PHY_SPEED_100M 1U /* PHY 100M speed. */ @@ -67,5 +221,5 @@ rt_err_t rt_hw_phy_register(struct rt_phy_device *phy, const char *name); #ifdef __cplusplus } #endif - -#endif /* __PHY_H__*/ +#endif +#endif diff --git a/rt-thread/components/drivers/include/drivers/phy_mdio.h b/rt-thread/components/drivers/include/drivers/phy_mdio.h deleted file mode 100644 index b7677d3..0000000 --- a/rt-thread/components/drivers/include/drivers/phy_mdio.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2020-10-14 wangqiang the first version - */ - -#ifndef __MDIO_H__ -#define __MDIO_H__ - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - - -struct rt_mdio_bus_ops -{ - rt_bool_t (*init)(void *bus, rt_uint32_t src_clock_hz); - rt_size_t (*read)(void *bus, rt_uint32_t addr, rt_uint32_t reg, void *data, rt_uint32_t size); - rt_size_t (*write)(void *bus, rt_uint32_t addr, rt_uint32_t reg, void *data, rt_uint32_t size); - rt_bool_t (*uninit)(void *bus); -}; - -struct rt_mdio_bus -{ - void *hw_obj; - char *name; - struct rt_mdio_bus_ops *ops; -}; - -typedef struct rt_mdio_bus rt_mdio_t; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/rt-thread/components/drivers/include/drivers/phye.h b/rt-thread/components/drivers/include/drivers/phye.h new file mode 100644 index 0000000..3776cde --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/phye.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#ifndef __PHYE_H__ +#define __PHYE_H__ + +#include +#include + +enum rt_phye_mode +{ + RT_PHYE_MODE_INVALID, + RT_PHYE_MODE_USB_HOST, + RT_PHYE_MODE_USB_HOST_LS, + RT_PHYE_MODE_USB_HOST_FS, + RT_PHYE_MODE_USB_HOST_HS, + RT_PHYE_MODE_USB_HOST_SS, + RT_PHYE_MODE_USB_DEVICE, + RT_PHYE_MODE_USB_DEVICE_LS, + RT_PHYE_MODE_USB_DEVICE_FS, + RT_PHYE_MODE_USB_DEVICE_HS, + RT_PHYE_MODE_USB_DEVICE_SS, + RT_PHYE_MODE_USB_OTG, + RT_PHYE_MODE_UFS_HS_A, + RT_PHYE_MODE_UFS_HS_B, + RT_PHYE_MODE_PCIE, + RT_PHYE_MODE_ETHERNET, + RT_PHYE_MODE_MIPI_DPHY, + RT_PHYE_MODE_SATA, + RT_PHYE_MODE_LVDS, + RT_PHYE_MODE_DP, + + RT_PHYE_MODE_MAX, + + /* PCIe */ + RT_PHYE_MODE_PCIE_RC = RT_PHYE_MODE_MAX, + RT_PHYE_MODE_PCIE_EP, + RT_PHYE_MODE_PCIE_BIFURCATION, +}; + +struct rt_phye_ops; + +struct rt_phye +{ + struct rt_device *dev; + + const struct rt_phye_ops *ops; + + int init_count; + int power_count; + struct rt_spinlock lock; +}; + +struct rt_phye_ops +{ + rt_err_t (*init)(struct rt_phye *phye); + rt_err_t (*exit)(struct rt_phye *phye); + rt_err_t (*reset)(struct rt_phye *phye); + rt_err_t (*power_on)(struct rt_phye *phye); + rt_err_t (*power_off)(struct rt_phye *phye); + rt_err_t (*set_mode)(struct rt_phye *phye, enum rt_phye_mode mode, int submode); + rt_err_t (*ofw_parse)(struct rt_phye *phye, struct rt_ofw_cell_args *phye_args); +}; + +rt_err_t rt_phye_register(struct rt_phye *phye); +rt_err_t rt_phye_unregister(struct rt_phye *phye); + +rt_err_t rt_phye_init(struct rt_phye *phye); +rt_err_t rt_phye_exit(struct rt_phye *phye); +rt_err_t rt_phye_reset(struct rt_phye *phye); +rt_err_t rt_phye_power_on(struct rt_phye *phye); +rt_err_t rt_phye_power_off(struct rt_phye *phye); +rt_err_t rt_phye_set_mode(struct rt_phye *phye, enum rt_phye_mode mode, int submode); + +rt_inline rt_err_t rt_phye_set_mode_simple(struct rt_phye *phye, enum rt_phye_mode mode) +{ + return rt_phye_set_mode(phye, mode, RT_PHYE_MODE_INVALID); +} + +struct rt_phye *rt_phye_get_by_index(struct rt_device *dev, int index); +struct rt_phye *rt_phye_get_by_name(struct rt_device *dev, const char *id); +void rt_phye_put(struct rt_phye *phye); + +#endif /* __PHYE_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/pic.h b/rt-thread/components/drivers/include/drivers/pic.h index fa1941a..c624d24 100644 --- a/rt-thread/components/drivers/include/drivers/pic.h +++ b/rt-thread/components/drivers/include/drivers/pic.h @@ -148,6 +148,7 @@ void rt_pic_default_name(struct rt_pic *pic); struct rt_pic *rt_pic_dynamic_cast(void *ptr); rt_err_t rt_pic_linear_irq(struct rt_pic *pic, rt_size_t irq_nr); +rt_err_t rt_pic_cancel_irq(struct rt_pic *pic); int rt_pic_config_ipi(struct rt_pic *pic, int ipi_index, int hwirq); int rt_pic_config_irq(struct rt_pic *pic, int irq_index, int hwirq); diff --git a/rt-thread/components/drivers/include/drivers/platform.h b/rt-thread/components/drivers/include/drivers/platform.h index 5dac676..c4a497d 100644 --- a/rt-thread/components/drivers/include/drivers/platform.h +++ b/rt-thread/components/drivers/include/drivers/platform.h @@ -12,10 +12,7 @@ #ifndef __PLATFORM_H__ #define __PLATFORM_H__ -#ifdef RT_USING_OFW #include -#endif - #include struct rt_platform_device @@ -25,10 +22,7 @@ struct rt_platform_device int dev_id; const char *name; - -#ifdef RT_USING_OFW const struct rt_ofw_node_id *id; -#endif void *priv; }; @@ -38,10 +32,7 @@ struct rt_platform_driver struct rt_driver parent; const char *name; - -#ifdef RT_USING_OFW const struct rt_ofw_node_id *ids; -#endif rt_err_t (*probe)(struct rt_platform_device *pdev); rt_err_t (*remove)(struct rt_platform_device *pdev); @@ -54,6 +45,7 @@ rt_err_t rt_platform_driver_register(struct rt_platform_driver *pdrv); rt_err_t rt_platform_device_register(struct rt_platform_device *pdev); rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np); +rt_err_t rt_platform_ofw_request(struct rt_ofw_node *np); rt_err_t rt_platform_ofw_free(struct rt_platform_device *pdev); #define RT_PLATFORM_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, platform, BUILIN) diff --git a/rt-thread/components/drivers/include/drivers/pm.h b/rt-thread/components/drivers/include/drivers/pm.h index 45b3ae0..f625782 100644 --- a/rt-thread/components/drivers/include/drivers/pm.h +++ b/rt-thread/components/drivers/include/drivers/pm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -10,6 +10,7 @@ * 2019-04-28 Zero-Free improve PM mode and device ops interface * 2020-11-23 zhangsz update pm mode select * 2020-11-27 zhangsz update pm 2.0 + * 2024-07-04 wdfk-prog The device is registered and uninstalled by linked list */ #ifndef __PM_H__ @@ -134,15 +135,16 @@ struct rt_pm_ops struct rt_device_pm_ops { - int (*suspend)(const struct rt_device *device, rt_uint8_t mode); + rt_err_t (*suspend)(const struct rt_device *device, rt_uint8_t mode); void (*resume)(const struct rt_device *device, rt_uint8_t mode); - int (*frequency_change)(const struct rt_device *device, rt_uint8_t mode); + rt_err_t (*frequency_change)(const struct rt_device *device, rt_uint8_t mode); }; struct rt_device_pm { const struct rt_device *device; const struct rt_device_pm_ops *ops; + rt_slist_t list; }; struct rt_pm_module @@ -172,7 +174,7 @@ struct rt_pm rt_uint32_t sleep_status[PM_SLEEP_MODE_MAX - 1][(PM_MODULE_MAX_ID + 31) / 32]; /* the list of device, which has PM feature */ - rt_uint8_t device_pm_number; + rt_slist_t device_list; struct rt_device_pm *device_pm; /* if the mode has timer, the corresponding bit is 1*/ @@ -194,10 +196,10 @@ struct rt_pm_notify void *data; }; -void rt_pm_request(rt_uint8_t sleep_mode); -void rt_pm_release(rt_uint8_t sleep_mode); -void rt_pm_release_all(rt_uint8_t sleep_mode); -int rt_pm_run_enter(rt_uint8_t run_mode); +rt_err_t rt_pm_request(rt_uint8_t sleep_mode); +rt_err_t rt_pm_release(rt_uint8_t sleep_mode); +rt_err_t rt_pm_release_all(rt_uint8_t sleep_mode); +rt_err_t rt_pm_run_enter(rt_uint8_t run_mode); void rt_pm_device_register(struct rt_device *device, const struct rt_device_pm_ops *ops); void rt_pm_device_unregister(struct rt_device *device); @@ -208,22 +210,22 @@ void rt_pm_default_set(rt_uint8_t sleep_mode); void rt_system_pm_init(const struct rt_pm_ops *ops, rt_uint8_t timer_mask, void *user_data); -void rt_pm_module_request(uint8_t module_id, rt_uint8_t sleep_mode); -void rt_pm_module_release(uint8_t module_id, rt_uint8_t sleep_mode); -void rt_pm_module_release_all(uint8_t module_id, rt_uint8_t sleep_mode); +rt_err_t rt_pm_module_request(uint8_t module_id, rt_uint8_t sleep_mode); +rt_err_t rt_pm_module_release(uint8_t module_id, rt_uint8_t sleep_mode); +rt_err_t rt_pm_module_release_all(uint8_t module_id, rt_uint8_t sleep_mode); void rt_pm_module_delay_sleep(rt_uint8_t module_id, rt_tick_t timeout); rt_uint32_t rt_pm_module_get_status(void); rt_uint8_t rt_pm_get_sleep_mode(void); struct rt_pm *rt_pm_get_handle(void); /* sleep : request or release */ -void rt_pm_sleep_request(rt_uint16_t module_id, rt_uint8_t mode); -void rt_pm_sleep_release(rt_uint16_t module_id, rt_uint8_t mode); -void rt_pm_sleep_none_request(rt_uint16_t module_id); -void rt_pm_sleep_none_release(rt_uint16_t module_id); -void rt_pm_sleep_idle_request(rt_uint16_t module_id); -void rt_pm_sleep_idle_release(rt_uint16_t module_id); -void rt_pm_sleep_light_request(rt_uint16_t module_id); -void rt_pm_sleep_light_release(rt_uint16_t module_id); +rt_err_t rt_pm_sleep_request(rt_uint16_t module_id, rt_uint8_t mode); +rt_err_t rt_pm_sleep_release(rt_uint16_t module_id, rt_uint8_t mode); +rt_err_t rt_pm_sleep_none_request(rt_uint16_t module_id); +rt_err_t rt_pm_sleep_none_release(rt_uint16_t module_id); +rt_err_t rt_pm_sleep_idle_request(rt_uint16_t module_id); +rt_err_t rt_pm_sleep_idle_release(rt_uint16_t module_id); +rt_err_t rt_pm_sleep_light_request(rt_uint16_t module_id); +rt_err_t rt_pm_sleep_light_release(rt_uint16_t module_id); #endif /* __PM_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/regulator.h b/rt-thread/components/drivers/include/drivers/regulator.h new file mode 100644 index 0000000..d74fc64 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/regulator.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __REGULATOR_H__ +#define __REGULATOR_H__ + +#include +#include +#include + +#include + +#define RT_REGULATOR_UVOLT_INVALID (((int)(RT_UINT32_MAX >> 1))) + +struct rt_regulator_param +{ + const char *name; + + int min_uvolt; /* In uV */ + int max_uvolt; /* In uV */ + int min_uamp; /* In uA */ + int max_uamp; /* In uA */ + int ramp_delay; /* In uV/usec */ + int enable_delay; /* In usec */ + int off_on_delay; /* In usec */ + + rt_uint32_t enable_active_high:1; + rt_uint32_t boot_on:1; /* Is enabled on boot */ + rt_uint32_t always_on:1; /* Must be enabled */ + rt_uint32_t soft_start:1; /* Ramp voltage slowly */ + rt_uint32_t pull_down:1; /* Pull down resistor when regulator off */ + rt_uint32_t over_current_protection:1; /* Auto disable on over current */ +}; + +struct rt_regulator_ops; + +struct rt_regulator_node +{ + rt_list_t list; + rt_list_t children_nodes; + + struct rt_device *dev; + struct rt_regulator_node *parent; + + const char *supply_name; + const struct rt_regulator_ops *ops; + + struct rt_ref ref; + rt_atomic_t enabled_count; + + const struct rt_regulator_param *param; + + rt_list_t notifier_nodes; + + void *priv; +}; + +/* + * NOTE: Power regulator control is dangerous work. We don't want non-internal + * consumer could access the power regulator tree without regulator's API. So + * we defined the `rt_regulator` member in core instead of here. + */ +struct rt_regulator; + +#define RT_REGULATOR_MODE_INVALID 0 +#define RT_REGULATOR_MODE_FAST RT_BIT(0) +#define RT_REGULATOR_MODE_NORMAL RT_BIT(1) +#define RT_REGULATOR_MODE_IDLE RT_BIT(2) +#define RT_REGULATOR_MODE_STANDBY RT_BIT(3) + +struct rt_regulator_ops +{ + rt_err_t (*enable)(struct rt_regulator_node *reg); + rt_err_t (*disable)(struct rt_regulator_node *reg); + rt_bool_t (*is_enabled)(struct rt_regulator_node *reg); + rt_err_t (*set_voltage)(struct rt_regulator_node *reg, int min_uvolt, int max_uvolt); + int (*get_voltage)(struct rt_regulator_node *reg); + rt_err_t (*set_mode)(struct rt_regulator_node *reg, rt_uint32_t mode); + rt_int32_t (*get_mode)(struct rt_regulator_node *reg); + rt_err_t (*set_ramp_delay)(struct rt_regulator_node *reg, int ramp); + rt_uint32_t (*enable_time)(struct rt_regulator_node *reg); +}; + +struct rt_regulator_notifier; + +#define RT_REGULATOR_MSG_ENABLE RT_BIT(0) +#define RT_REGULATOR_MSG_DISABLE RT_BIT(1) +#define RT_REGULATOR_MSG_VOLTAGE_CHANGE RT_BIT(2) +#define RT_REGULATOR_MSG_VOLTAGE_CHANGE_ERR RT_BIT(3) + +union rt_regulator_notifier_args +{ + struct + { + int old_uvolt; + int min_uvolt; + int max_uvolt; + }; +}; + +typedef rt_err_t (*rt_regulator_notifier_callback)(struct rt_regulator_notifier *notifier, + rt_ubase_t msg, void *data); + +struct rt_regulator_notifier +{ + rt_list_t list; + + struct rt_regulator *regulator; + rt_regulator_notifier_callback callback; + void *priv; +}; + +rt_err_t rt_regulator_register(struct rt_regulator_node *reg_np); +rt_err_t rt_regulator_unregister(struct rt_regulator_node *reg_np); + +rt_err_t rt_regulator_notifier_register(struct rt_regulator *reg, + struct rt_regulator_notifier *notifier); +rt_err_t rt_regulator_notifier_unregister(struct rt_regulator *reg, + struct rt_regulator_notifier *notifier); + +struct rt_regulator *rt_regulator_get(struct rt_device *dev, const char *id); +void rt_regulator_put(struct rt_regulator *reg); + +rt_err_t rt_regulator_enable(struct rt_regulator *reg); +rt_err_t rt_regulator_disable(struct rt_regulator *reg); +rt_bool_t rt_regulator_is_enabled(struct rt_regulator *reg); + +rt_bool_t rt_regulator_is_supported_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt); +rt_err_t rt_regulator_set_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt); +int rt_regulator_get_voltage(struct rt_regulator *reg); + +rt_err_t rt_regulator_set_mode(struct rt_regulator *reg, rt_uint32_t mode); +rt_int32_t rt_regulator_get_mode(struct rt_regulator *reg); + +rt_inline rt_err_t rt_regulator_set_voltage_triplet(struct rt_regulator *reg, + int min_uvolt, int target_uvolt, int max_uvolt) +{ + if (!rt_regulator_set_voltage(reg, target_uvolt, max_uvolt)) + { + return RT_EOK; + } + + return rt_regulator_set_voltage(reg, min_uvolt, max_uvolt); +} + +#endif /* __REGULATOR_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/reset.h b/rt-thread/components/drivers/include/drivers/reset.h new file mode 100644 index 0000000..6339fe9 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/reset.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __RESET_H__ +#define __RESET_H__ + +#include +#include +#include + +#define RT_RESET_CONTROLLER_OBJ_NAME "RSTC" + +struct rt_reset_control_ops; + +struct rt_reset_controller +{ + struct rt_object parent; + + rt_list_t rstc_nodes; + + const char *name; + const struct rt_reset_control_ops *ops; + + struct rt_ofw_node *ofw_node; + void *priv; + + struct rt_spinlock spinlock; +}; + +struct rt_reset_control +{ + rt_list_t list; + + struct rt_reset_controller *rstcer; + + int id; + const char *con_id; + rt_bool_t is_array; + + void *priv; +}; + +struct rt_reset_control_ops +{ + /* + * rt_ofw_cell_args return: + * args[0] = rstc.id + */ + rt_err_t (*ofw_parse)(struct rt_reset_control *rstc, struct rt_ofw_cell_args *args); + /* API */ + rt_err_t (*reset)(struct rt_reset_control *rstc); + rt_err_t (*assert)(struct rt_reset_control *rstc); + rt_err_t (*deassert)(struct rt_reset_control *rstc); + int (*status)(struct rt_reset_control *rstc); +}; + +rt_err_t rt_reset_controller_register(struct rt_reset_controller *rstcer); +rt_err_t rt_reset_controller_unregister(struct rt_reset_controller *rstcer); + +rt_err_t rt_reset_control_reset(struct rt_reset_control *rstc); +rt_err_t rt_reset_control_assert(struct rt_reset_control *rstc); +rt_err_t rt_reset_control_deassert(struct rt_reset_control *rstc); +int rt_reset_control_status(struct rt_reset_control *rstc); + +rt_ssize_t rt_reset_control_get_count(struct rt_device *dev); +struct rt_reset_control *rt_reset_control_get_array(struct rt_device *dev); +struct rt_reset_control *rt_reset_control_get_by_index(struct rt_device *dev, int index); +struct rt_reset_control *rt_reset_control_get_by_name(struct rt_device *dev, const char *name); +void rt_reset_control_put(struct rt_reset_control *rstc); + +struct rt_reset_control *rt_ofw_get_reset_control_array(struct rt_ofw_node *np); +struct rt_reset_control *rt_ofw_get_reset_control_by_index(struct rt_ofw_node *np, int index); +struct rt_reset_control *rt_ofw_get_reset_control_by_name(struct rt_ofw_node *np, const char *name); + +#endif /* __RESET_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/rt_drv_pwm.h b/rt-thread/components/drivers/include/drivers/rt_drv_pwm.h deleted file mode 100644 index e9e1707..0000000 --- a/rt-thread/components/drivers/include/drivers/rt_drv_pwm.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-05-07 aozima the first version - * 2022-09-24 yuqi add phase and dead time configuration - */ - -#ifndef __DRV_PWM_H_INCLUDE__ -#define __DRV_PWM_H_INCLUDE__ - -#include - -#define PWM_CMD_ENABLE (RT_DEVICE_CTRL_BASE(PWM) + 0) -#define PWM_CMD_DISABLE (RT_DEVICE_CTRL_BASE(PWM) + 1) -#define PWM_CMD_SET (RT_DEVICE_CTRL_BASE(PWM) + 2) -#define PWM_CMD_GET (RT_DEVICE_CTRL_BASE(PWM) + 3) -#define PWMN_CMD_ENABLE (RT_DEVICE_CTRL_BASE(PWM) + 4) -#define PWMN_CMD_DISABLE (RT_DEVICE_CTRL_BASE(PWM) + 5) -#define PWM_CMD_SET_PERIOD (RT_DEVICE_CTRL_BASE(PWM) + 6) -#define PWM_CMD_SET_PULSE (RT_DEVICE_CTRL_BASE(PWM) + 7) -#define PWM_CMD_SET_DEAD_TIME (RT_DEVICE_CTRL_BASE(PWM) + 8) -#define PWM_CMD_SET_PHASE (RT_DEVICE_CTRL_BASE(PWM) + 9) -#define PWM_CMD_ENABLE_IRQ (RT_DEVICE_CTRL_BASE(PWM) + 10) -#define PWM_CMD_DISABLE_IRQ (RT_DEVICE_CTRL_BASE(PWM) + 11) - -struct rt_pwm_configuration -{ - rt_uint32_t channel; /* 0 ~ n or 0 ~ -n, which depends on specific MCU requirements */ - rt_uint32_t period; /* unit:ns 1ns~4.29s:1Ghz~0.23hz */ - rt_uint32_t pulse; /* unit:ns (pulse<=period) */ - rt_uint32_t dead_time; /* unit:ns */ - rt_uint32_t phase; /*unit: degree, 0~360, which is the phase of pwm output, */ - /* - * RT_TRUE : The channel of pwm is complememtary. - * RT_FALSE : The channel of pwm is nomal. - */ - rt_bool_t complementary; -}; - -struct rt_device_pwm; -struct rt_pwm_ops -{ - rt_err_t (*control)(struct rt_device_pwm *device, int cmd, void *arg); -}; - -struct rt_device_pwm -{ - struct rt_device parent; - const struct rt_pwm_ops *ops; -}; - -rt_err_t rt_device_pwm_register(struct rt_device_pwm *device, const char *name, const struct rt_pwm_ops *ops, const void *user_data); - -rt_err_t rt_pwm_enable(struct rt_device_pwm *device, int channel); -rt_err_t rt_pwm_disable(struct rt_device_pwm *device, int channel); -rt_err_t rt_pwm_set(struct rt_device_pwm *device, int channel, rt_uint32_t period, rt_uint32_t pulse); -rt_err_t rt_pwm_set_period(struct rt_device_pwm *device, int channel, rt_uint32_t period); -rt_err_t rt_pwm_set_pulse(struct rt_device_pwm *device, int channel, rt_uint32_t pulse); -rt_err_t rt_pwm_set_dead_time(struct rt_device_pwm *device, int channel, rt_uint32_t dead_time); -rt_err_t rt_pwm_set_phase(struct rt_device_pwm *device, int channel, rt_uint32_t phase); - -#endif /* __DRV_PWM_H_INCLUDE__ */ diff --git a/rt-thread/components/drivers/include/drivers/scsi.h b/rt-thread/components/drivers/include/drivers/scsi.h new file mode 100644 index 0000000..7b6baaf --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/scsi.h @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __SCSI_H__ +#define __SCSI_H__ + +#include +#include +#include + +#define RT_SCSI_LUN_SHIFT 5 + +rt_packed(struct rt_scsi_unknow +{ + rt_uint8_t opcode; +}); + +rt_packed(struct rt_scsi_test_unit_ready +{ + rt_uint8_t opcode; + rt_uint8_t reserved[4]; + rt_uint8_t control; + rt_uint8_t pad[6]; /* To be ATAPI compatible */ +}); + +rt_packed(struct rt_scsi_inquiry +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-2 Reserved, 1 Obsolete Formerly CMDDT, 0 EVPD */ + rt_uint8_t page; /* Page code if EVPD=1 */ + rt_uint8_t reserved; + rt_uint8_t alloc_length; + rt_uint8_t control; + rt_uint8_t pad[6]; /* To be ATAPI compatible */ +}); + +rt_packed(struct rt_scsi_inquiry_data +{ +#define RT_SCSI_DEVTYPE_MASK 31 + rt_uint8_t devtype; +#define RT_SCSI_REMOVABLE_BIT 7 + rt_uint8_t rmb; + rt_uint8_t reserved[2]; + rt_uint8_t length; + rt_uint8_t reserved1[3]; + char vendor[8]; + char prodid[16]; + char prodrev[4]; +}); + +rt_packed(struct rt_scsi_request_sense +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-2 Reserved, 1 Obsolete, 0 SP */ + rt_uint8_t reserved[2]; + rt_uint8_t alloc_length; + rt_uint8_t control; + rt_uint8_t pad[6]; /* To be ATAPI compatible */ +}); + +rt_packed(struct rt_scsi_request_sense_data +{ + rt_uint8_t error_code; /* 7 Valid, 6-0 Err. code */ + rt_uint8_t segment_number; + rt_uint8_t sense_key; /* 7 FileMark, 6 EndOfMedia, 5 ILI, 4-0 sense key */ + rt_be32_t information; + rt_uint8_t additional_sense_length; + rt_be32_t cmd_specific_info; + rt_uint8_t additional_sense_code; + rt_uint8_t additional_sense_code_qualifier; + rt_uint8_t field_replaceable_unit_code; + rt_uint8_t sense_key_specific[3]; +}); + +rt_packed(struct rt_scsi_read_capacity10 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-1 Reserved, 0 Obsolete */ + rt_be32_t logical_block_addr; /* only if PMI=1 */ + rt_uint8_t reserved[2]; + rt_uint8_t pmi; + rt_uint8_t control; + rt_be16_t pad; /* To be ATAPI compatible */ +}); + +rt_packed(struct rt_scsi_read_capacity10_data +{ + rt_be32_t last_block; + rt_be32_t block_size; +}); + +rt_packed(struct rt_scsi_read_capacity16 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 Reserved, 4-0 SERVICE ACTION 0x10 */ + rt_be64_t logical_block_addr; /* only if PMI=1 */ + rt_be32_t alloc_len; + rt_uint8_t pmi; + rt_uint8_t control; +}); + +rt_packed(struct rt_scsi_read_capacity16_data +{ + rt_be64_t last_block; + rt_be32_t block_size; + rt_uint8_t pad[20]; +}); + +rt_packed(struct rt_scsi_read10 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 RDPROTECT, 4 DPO, 3 FUA, 2 RARC, 1 Obsolete, 0 Obsolete */ + rt_be32_t lba; + rt_uint8_t reserved; + rt_be16_t size; + rt_uint8_t reserved2; + rt_be16_t pad; +}); + +rt_packed(struct rt_scsi_read12 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 RDPROTECT, 4 DPO, 3 FUA, 2 RARC, 1 Obsolete, 0 Obsolete */ + rt_be32_t lba; + rt_be32_t size; + rt_uint8_t reserved; + rt_uint8_t control; +}); + +rt_packed(struct rt_scsi_read16 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 RDPROTECT, 4 DPO, 3 FUA, 2 RARC, 1 Obsolete, 0 DLD2 */ + rt_be64_t lba; + rt_be32_t size; + rt_uint8_t reserved; + rt_uint8_t control; +}); + +rt_packed(struct rt_scsi_write10 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 WRPROTECT, 4 DPO, 3 FUA, 2 Reserved, 1 Obsolete, 0 Obsolete */ + rt_be32_t lba; + rt_uint8_t reserved; + rt_be16_t size; + rt_uint8_t reserved2; + rt_be16_t pad; +}); + +rt_packed(struct rt_scsi_write12 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 WRPROTECT, 4 DPO, 3 FUA, 2 Reserved, 1 Obsolete, 0 Obsolete */ + rt_be32_t lba; + rt_be32_t size; + rt_uint8_t reserved; + rt_uint8_t control; +}); + +rt_packed(struct rt_scsi_write16 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 WRPROTECT, 4 DPO, 3 FUA, 2 Reserved, 1 Obsolete, 0 DLD2 */ + rt_be64_t lba; + rt_be32_t size; + rt_uint8_t reserved; + rt_uint8_t control; +}); + +rt_packed(struct rt_scsi_synchronize_cache10 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-3 Reserved, 2 Obsolete, 1 IMMED, 0 Obsolete */ + rt_be32_t lba; + rt_uint8_t reserved; + rt_be16_t size; + rt_uint8_t control; +}); + +rt_packed(struct rt_scsi_synchronize_cache16 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-3 Reserved, 2 Obsolete, 1 IMMED, 0 Obsolete */ + rt_be64_t lba; + rt_be32_t size; + rt_uint8_t reserved; + rt_uint8_t control; +}); + +#define RT_SCSI_UNMAP_SHIFT 3 + +rt_packed(struct rt_scsi_write_same10 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 WRPROTECT, 4 ANCHOR, 3 UNMAP, 2 Obsolete, 1 Obsolete, 0 Obsolete */ + rt_be32_t lba; + rt_uint8_t reserved; + rt_be16_t size; + rt_uint8_t control; +}); + +rt_packed(struct rt_scsi_write_same16 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 WRPROTECT, 4 ANCHOR, 3 UNMAP, 2 Obsolete, 1 Obsolete, 0 NDOB */ + rt_be64_t lba; + rt_be32_t size; + rt_uint8_t reserved; + rt_uint8_t control; +}); + +#define RT_SCSI_PF_SHIFT 4 +#define RT_SCSI_RTD_SHIFT 1 +#define RT_SCSI_SP_SHIFT 0 + +rt_packed(struct rt_scsi_mode_select6 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 Reserved, 4 PF, 3-2 Reserved, 1 RTD, 0 SP */ + rt_uint8_t reserved[2]; + rt_uint8_t param_list_len; + rt_uint8_t control; +}); + +rt_packed(struct rt_scsi_mode_select10 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 Reserved, 4 PF, 3-1 Reserved, 0 SP */ + rt_uint8_t reserved[5]; + rt_be16_t param_list_len; + rt_uint8_t control; +}); + +struct rt_scsi_mode_select_data +{ + rt_uint32_t length; + rt_uint16_t block_descriptor_length; + rt_uint8_t medium_type; + rt_uint8_t device_specific; + rt_uint8_t header_length; + rt_uint8_t longlba:1; +}; + +#define RT_SCSI_DBD_SHIFT 3 +#define RT_SCSI_LLBAA_SHIFT 4 +#define RT_SCSI_PC_SHIFT 6 +#define RT_SCSI_PAGE_CODE_SHIFT 0 + +rt_packed(struct rt_scsi_mode_sense6 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-4 Reserved, 3 DBD, 2-0 Reserved */ + rt_uint8_t page_control_code; + rt_uint8_t subpage_code; + rt_uint8_t allocation_len; + rt_uint8_t control; +}); + +rt_packed(struct rt_scsi_mode_sense10 +{ + rt_uint8_t opcode; + rt_uint8_t config; /* 7-5 Reserved, 4 LLBAA, 3 DBD, 2-0 Reserved */ + rt_uint8_t page_control_code; + rt_uint8_t subpage_code; + rt_uint8_t reserved[3]; + rt_be16_t allocation_len; + rt_uint8_t control; +}); + +#define RT_SCSI_CMD_TEST_UNIT_READY 0x00 +#define RT_SCSI_CMD_REQUEST_SENSE 0x03 +#define RT_SCSI_CMD_INQUIRY 0x12 +#define RT_SCSI_CMD_MODE_SELECT 0x15 +#define RT_SCSI_CMD_MODE_SENSE 0x1a +#define RT_SCSI_CMD_READ_CAPACITY10 0x25 +#define RT_SCSI_CMD_READ10 0x28 +#define RT_SCSI_CMD_WRITE10 0x2a +#define RT_SCSI_CMD_SYNCHRONIZE_CACHE10 0x35 +#define RT_SCSI_CMD_WRITE_SAME10 0x41 +#define RT_SCSI_CMD_MODE_SELECT10 0x55 +#define RT_SCSI_CMD_MODE_SENSE10 0x5a +#define RT_SCSI_CMD_READ16 0x88 +#define RT_SCSI_CMD_WRITE16 0x8a +#define RT_SCSI_CMD_SYNCHRONIZE_CACHE16 0x91 +#define RT_SCSI_CMD_WRITE_SAME16 0x93 +#define RT_SCSI_CMD_READ_CAPACITY16 0x9e +#define RT_SCSI_CMD_READ12 0xa8 +#define RT_SCSI_CMD_WRITE12 0xaa + +struct rt_scsi_cmd +{ + union + { + struct rt_scsi_unknow unknow; + struct rt_scsi_test_unit_ready test_unit_ready; + struct rt_scsi_inquiry inquiry; + struct rt_scsi_request_sense request_sense; + struct rt_scsi_read_capacity10 read_capacity10; + struct rt_scsi_read_capacity16 read_capacity16; + struct rt_scsi_read10 read10; + struct rt_scsi_read12 read12; + struct rt_scsi_read16 read16; + struct rt_scsi_write10 write10; + struct rt_scsi_write12 write12; + struct rt_scsi_write16 write16; + struct rt_scsi_synchronize_cache10 synchronize_cache10; + struct rt_scsi_synchronize_cache16 synchronize_cache16; + struct rt_scsi_write_same10 write_same10; + struct rt_scsi_write_same16 write_same16; + struct rt_scsi_mode_select6 mode_select6; + struct rt_scsi_mode_select10 mode_select10; + struct rt_scsi_mode_sense6 mode_sense6; + struct rt_scsi_mode_sense10 mode_sense10; + } op; + rt_size_t op_size; + + union + { + struct + { + struct rt_scsi_inquiry_data inquiry; + struct rt_scsi_request_sense_data request_sense; + struct rt_scsi_read_capacity10_data read_capacity10; + struct rt_scsi_read_capacity16_data read_capacity16; + }; + struct + { + void *ptr; + rt_size_t size; + }; + } data; +}; + +enum +{ + SCSI_DEVICE_TYPE_DIRECT = 0x00, /* DiskPeripheral (GenDisk) */ + SCSI_DEVICE_TYPE_SEQUENTIAL = 0x01, /* TapePeripheral */ + SCSI_DEVICE_TYPE_PRINTER = 0x02, /* PrinterPeripheral (GenPrinter) */ + SCSI_DEVICE_TYPE_PROCESSOR = 0x03, /* OtherPeripheral */ + SCSI_DEVICE_TYPE_WRITE_ONCE_READ_MULTIPLE = 0x04, /* WormPeripheral (GenWorm) */ + SCSI_DEVICE_TYPE_CDROM = 0x05, /* CdRomPeripheral (GenCdRom) */ + SCSI_DEVICE_TYPE_SCANNER = 0x06, /* ScannerPeripheral (GenScanner) */ + SCSI_DEVICE_TYPE_OPTICAL = 0x07, /* OpticalDiskPeripheral (GenOptical) */ + SCSI_DEVICE_TYPE_MEDIUM_CHANGER = 0x08, /* MediumChangerPeripheral (ScsiChanger) */ + SCSI_DEVICE_TYPE_COMMUNICATION = 0x09, /* CommunicationsPeripheral (ScsiNet) */ + SCSI_DEVICE_TYPE_ASC_PREPRESS_GRAPHICS10 = 0x0a, /* ASCPrePressGraphicsPeripheral (ScsiASCIT8) */ + SCSI_DEVICE_TYPE_ASC_PREPRESS_GRAPHICS11 = 0x0b, /* ASCPrePressGraphicsPeripheral (ScsiASCIT8) */ + SCSI_DEVICE_TYPE_ARRAY = 0x0c, /* ArrayPeripheral (ScsiArray) */ + SCSI_DEVICE_TYPE_ENCLOSURE = 0x0d, /* EnclosurePeripheral (ScsiEnclosure) */ + SCSI_DEVICE_TYPE_RBC = 0x0e, /* RBCPeripheral (ScsiRBC) */ + SCSI_DEVICE_TYPE_CARDREADER = 0x0f, /* CardReaderPeripheral (ScsiCardReader) */ + SCSI_DEVICE_TYPE_BRIDGE = 0x10, /* BridgePeripheral (ScsiBridge) */ + SCSI_DEVICE_TYPE_OTHER = 0x11, /* OtherPeripheral (ScsiOther) */ + SCSI_DEVICE_TYPE_MAX, +}; + +struct rt_scsi_ops; + +struct rt_scsi_host +{ + struct rt_device *dev; + + const struct rt_scsi_ops *ops; + + rt_size_t max_id; + rt_size_t max_lun; + + rt_list_t lun_nodes; +}; + +struct rt_scsi_device +{ + struct rt_scsi_host *host; + + rt_list_t list; + + rt_size_t id; + rt_size_t lun; + rt_uint32_t devtype; + rt_uint32_t removable; + rt_size_t last_block; + rt_size_t block_size; + + void *priv; +}; + +struct rt_scsi_ops +{ + rt_err_t (*reset)(struct rt_scsi_device *sdev); + rt_err_t (*transfer)(struct rt_scsi_device *sdev, struct rt_scsi_cmd *cmd); +}; + +rt_err_t rt_scsi_host_register(struct rt_scsi_host *scsi); +rt_err_t rt_scsi_host_unregister(struct rt_scsi_host *scsi); + +rt_inline rt_bool_t rt_scsi_cmd_is_write(struct rt_scsi_cmd *cmd) +{ + return cmd->op.write10.opcode == RT_SCSI_CMD_WRITE10 || + cmd->op.write12.opcode == RT_SCSI_CMD_WRITE16 || + cmd->op.write16.opcode == RT_SCSI_CMD_WRITE12; +} + +rt_err_t rt_scsi_request_sense(struct rt_scsi_device *sdev, + struct rt_scsi_request_sense_data *out_data); + +rt_err_t rt_scsi_test_unit_ready(struct rt_scsi_device *sdev); +rt_err_t rt_scsi_inquiry(struct rt_scsi_device *sdev, + struct rt_scsi_inquiry_data *out_data); + +rt_err_t rt_scsi_read_capacity10(struct rt_scsi_device *sdev, + struct rt_scsi_read_capacity10_data *out_data); +rt_err_t rt_scsi_read_capacity16(struct rt_scsi_device *sdev, + struct rt_scsi_read_capacity16_data *out_data); + +rt_err_t rt_scsi_read10(struct rt_scsi_device *sdev, + rt_off_t lba, void *buffer, rt_size_t size); +rt_err_t rt_scsi_read12(struct rt_scsi_device *sdev, + rt_off_t lba, void *buffer, rt_size_t size); +rt_err_t rt_scsi_read16(struct rt_scsi_device *sdev, + rt_off_t lba, void *buffer, rt_size_t size); + +rt_err_t rt_scsi_write10(struct rt_scsi_device *sdev, + rt_off_t lba, const void *buffer, rt_size_t size); +rt_err_t rt_scsi_write12(struct rt_scsi_device *sdev, + rt_off_t lba, const void *buffer, rt_size_t size); +rt_err_t rt_scsi_write16(struct rt_scsi_device *sdev, + rt_off_t lba, const void *buffer, rt_size_t size); + +rt_err_t rt_scsi_synchronize_cache10(struct rt_scsi_device *sdev, + rt_off_t lba, rt_size_t size); +rt_err_t rt_scsi_synchronize_cache16(struct rt_scsi_device *sdev, + rt_off_t lba, rt_size_t size); + +rt_err_t rt_scsi_write_same10(struct rt_scsi_device *sdev, + rt_off_t lba, rt_size_t size); +rt_err_t rt_scsi_write_same16(struct rt_scsi_device *sdev, + rt_off_t lba, rt_size_t size); + +rt_err_t rt_scsi_mode_select6(struct rt_scsi_device *sdev, + rt_uint8_t pf, rt_uint8_t sp, void *buffer, rt_size_t size, + struct rt_scsi_mode_select_data *data); +rt_err_t rt_scsi_mode_select10(struct rt_scsi_device *sdev, + rt_uint8_t pf, rt_uint8_t sp, void *buffer, rt_size_t size, + struct rt_scsi_mode_select_data *data); + +rt_err_t rt_scsi_mode_sense6(struct rt_scsi_device *sdev, + rt_uint8_t dbd, rt_uint8_t modepage, rt_uint8_t subpage, void *buffer, rt_size_t size, + struct rt_scsi_mode_select_data *data); +rt_err_t rt_scsi_mode_sense10(struct rt_scsi_device *sdev, + rt_uint8_t dbd, rt_uint8_t modepage, rt_uint8_t subpage, void *buffer, rt_size_t size, + struct rt_scsi_mode_select_data *data); + +#endif /* __SCSI_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/sensor.h b/rt-thread/components/drivers/include/drivers/sensor.h index 6c8b78e..ca1c84e 100644 --- a/rt-thread/components/drivers/include/drivers/sensor.h +++ b/rt-thread/components/drivers/include/drivers/sensor.h @@ -12,7 +12,7 @@ #define __SENSOR_H__ #include -#include "pin.h" +#include "dev_pin.h" #ifdef __cplusplus extern "C" { @@ -53,6 +53,9 @@ extern "C" { #define RT_SENSOR_CLASS_IAQ (19) /* IAQ sensor. */ #define RT_SENSOR_CLASS_ETOH (20) /* EtOH sensor. */ #define RT_SENSOR_CLASS_BP (21) /* Blood Pressure */ +#define RT_SENSOR_CLASS_VOLTAGE (22) /* Voltage sensor */ +#define RT_SENSOR_CLASS_CURRENT (23) /* Current sensor */ +#define RT_SENSOR_CLASS_POWER (24) /* Power sensor */ /* Sensor vendor types */ @@ -95,6 +98,9 @@ extern "C" { #define RT_SENSOR_UNIT_DD (17) /* Coordinates unit: DD */ #define RT_SENSOR_UNIT_MGM3 (18) /* Concentration unit: mg/m3 */ #define RT_SENSOR_UNIT_MMHG (19) /* Blood Pressure unit: mmHg */ +#define RT_SENSOR_UNIT_MV (20) /* Voltage unit: mV */ +#define RT_SENSOR_UNIT_MA (21) /* Current unit: mA */ +#define RT_SENSOR_UNIT_MW (22) /* Power unit: mW */ /* Sensor communication interface types */ #define RT_SENSOR_INTF_I2C (1 << 0) @@ -230,9 +236,12 @@ struct rt_sensor_data rt_uint32_t dust; /* Dust sensor. unit: ug/m3 */ rt_uint32_t eco2; /* eCO2 sensor. unit: ppm */ rt_uint32_t spo2; /* SpO2 sensor. unit: permillage */ - rt_uint32_t iaq; /* IAQ sensor. unit: 1 */ - rt_uint32_t etoh; /* EtOH sensor. unit: ppm */ + rt_uint32_t iaq; /* IAQ sensor. unit: 1 */ + rt_uint32_t etoh; /* EtOH sensor. unit: ppm */ struct sensor_bp bp; /* BloodPressure. unit: mmHg */ + float mv; /* Voltage sensor. unit: mv */ + float ma; /* Current sensor. unit: ma */ + float mw; /* Power sensor. unit: mw */ } data; }; diff --git a/rt-thread/components/drivers/include/drivers/sensor_v2.h b/rt-thread/components/drivers/include/drivers/sensor_v2.h index d90cef8..3cb00ca 100644 --- a/rt-thread/components/drivers/include/drivers/sensor_v2.h +++ b/rt-thread/components/drivers/include/drivers/sensor_v2.h @@ -13,7 +13,7 @@ #define __SENSOR_H__ #include -#include "pin.h" +#include "dev_pin.h" #ifdef __cplusplus extern "C" { diff --git a/rt-thread/components/drivers/include/drivers/serial_bypass.h b/rt-thread/components/drivers/include/drivers/serial_bypass.h new file mode 100644 index 0000000..890c894 --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/serial_bypass.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-11-20 zhujiale the first version + */ + +#ifndef __RTT_BYPASS_H__ +#define __RTT_BYPASS_H__ +#include +#include +#include +typedef rt_err_t(*bypass_function_t)(struct rt_serial_device* serial, char buf, void* data); + +#define RT_BYPASS_LEVEL_MAX 4 +#define RT_BYPASS_LEVEL_1 0 +#define RT_BYPASS_LEVEL_2 1 +#define RT_BYPASS_LEVEL_3 2 +#define RT_BYPASS_LEVEL_4 3 +#define RT_BYPASS_MAX_LEVEL 4 + +/*The protect level can be register but can not be unregister we should use it carefully*/ +#define RT_BYPASS_PROTECT_LEVEL_1 10 +#define RT_BYPASS_PROTECT_LEVEL_2 11 +#define RT_BYPASS_PROTECT_LEVEL_3 12 +#define RT_BYPASS_PROTECT_LEVEL_4 13 + +struct rt_serial_bypass_func { + /*The function pointer of the bypassed data processing*/ + bypass_function_t bypass; + /*The smaller the array of levels, the higher the priority of execution*/ + rt_uint8_t level; + rt_list_t node; + char name[RT_NAME_MAX]; + void* data; +}; + +struct rt_serial_bypass_head +{ + rt_list_t head; + struct rt_spinlock spinlock; +}; + +struct rt_serial_bypass { + struct rt_work work; + + struct rt_spinlock spinlock; + struct rt_workqueue* lower_workq; + struct rt_serial_bypass_head* upper_h; + struct rt_serial_bypass_head* lower_h; + rt_mutex_t mutex; + struct rt_ringbuffer* pipe; +}; + +int serial_bypass_list(int argc, char** argv); + +void rt_bypass_work_straight(struct rt_serial_device* serial); +void rt_bypass_putchar(struct rt_serial_device* serial, rt_uint8_t ch); +rt_size_t rt_bypass_getchar(struct rt_serial_device* serial, rt_uint8_t* ch); + +rt_err_t rt_bypass_upper_unregister(struct rt_serial_device* serial, rt_uint8_t level); +rt_err_t rt_bypass_lower_unregister(struct rt_serial_device* serial, rt_uint8_t level); + +rt_err_t rt_bypass_upper_register(struct rt_serial_device* serial, const char* name, rt_uint8_t level, bypass_function_t func, void* data); +rt_err_t rt_bypass_lower_register(struct rt_serial_device* serial, const char* name, rt_uint8_t level, bypass_function_t func, void* data); +#endif diff --git a/rt-thread/components/drivers/include/drivers/spi.h b/rt-thread/components/drivers/include/drivers/spi.h deleted file mode 100644 index b35c7b3..0000000 --- a/rt-thread/components/drivers/include/drivers/spi.h +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2012-11-23 Bernard Add extern "C" - * 2020-06-13 armink fix the 3 wires issue - * 2022-09-01 liYony fix api rt_spi_sendrecv16 about MSB and LSB bug - */ - -#ifndef __SPI_H__ -#define __SPI_H__ - -#include -#include -#include - -#ifdef __cplusplus -extern "C"{ -#endif - -/** - * At CPOL=0 the base value of the clock is zero - * - For CPHA=0, data are captured on the clock's rising edge (low->high transition) - * and data are propagated on a falling edge (high->low clock transition). - * - For CPHA=1, data are captured on the clock's falling edge and data are - * propagated on a rising edge. - * At CPOL=1 the base value of the clock is one (inversion of CPOL=0) - * - For CPHA=0, data are captured on clock's falling edge and data are propagated - * on a rising edge. - * - For CPHA=1, data are captured on clock's rising edge and data are propagated - * on a falling edge. - */ -#define RT_SPI_CPHA (1<<0) /* bit[0]:CPHA, clock phase */ -#define RT_SPI_CPOL (1<<1) /* bit[1]:CPOL, clock polarity */ - -#define RT_SPI_LSB (0<<2) /* bit[2]: 0-LSB */ -#define RT_SPI_MSB (1<<2) /* bit[2]: 1-MSB */ - -#define RT_SPI_MASTER (0<<3) /* SPI master device */ -#define RT_SPI_SLAVE (1<<3) /* SPI slave device */ - -#define RT_SPI_CS_HIGH (1<<4) /* Chipselect active high */ -#define RT_SPI_NO_CS (1<<5) /* No chipselect */ -#define RT_SPI_3WIRE (1<<6) /* SI/SO pin shared */ -#define RT_SPI_READY (1<<7) /* Slave pulls low to pause */ - -#define RT_SPI_MODE_MASK (RT_SPI_CPHA | RT_SPI_CPOL | RT_SPI_MSB | RT_SPI_SLAVE | RT_SPI_CS_HIGH | RT_SPI_NO_CS | RT_SPI_3WIRE | RT_SPI_READY) - -#define RT_SPI_MODE_0 (0 | 0) /* CPOL = 0, CPHA = 0 */ -#define RT_SPI_MODE_1 (0 | RT_SPI_CPHA) /* CPOL = 0, CPHA = 1 */ -#define RT_SPI_MODE_2 (RT_SPI_CPOL | 0) /* CPOL = 1, CPHA = 0 */ -#define RT_SPI_MODE_3 (RT_SPI_CPOL | RT_SPI_CPHA) /* CPOL = 1, CPHA = 1 */ - -#define RT_SPI_BUS_MODE_SPI (1<<0) -#define RT_SPI_BUS_MODE_QSPI (1<<1) - -/** - * SPI message structure - */ -struct rt_spi_message -{ - const void *send_buf; - void *recv_buf; - rt_size_t length; - struct rt_spi_message *next; - - unsigned cs_take : 1; - unsigned cs_release : 1; -}; - -/** - * SPI configuration structure - */ -struct rt_spi_configuration -{ - rt_uint8_t mode; - rt_uint8_t data_width; - rt_uint16_t reserved; - - rt_uint32_t max_hz; -}; - -struct rt_spi_ops; -struct rt_spi_bus -{ - struct rt_device parent; - rt_uint8_t mode; - const struct rt_spi_ops *ops; - - struct rt_mutex lock; - struct rt_spi_device *owner; -}; - -/** - * SPI operators - */ -struct rt_spi_ops -{ - rt_err_t (*configure)(struct rt_spi_device *device, struct rt_spi_configuration *configuration); - rt_ssize_t (*xfer)(struct rt_spi_device *device, struct rt_spi_message *message); -}; - -/** - * SPI Virtual BUS, one device must connected to a virtual BUS - */ -struct rt_spi_device -{ - struct rt_device parent; - struct rt_spi_bus *bus; - - struct rt_spi_configuration config; - rt_base_t cs_pin; - void *user_data; -}; - -struct rt_qspi_message -{ - struct rt_spi_message parent; - - /* instruction stage */ - struct - { - rt_uint8_t content; - rt_uint8_t qspi_lines; - } instruction; - - /* address and alternate_bytes stage */ - struct - { - rt_uint32_t content; - rt_uint8_t size; - rt_uint8_t qspi_lines; - } address, alternate_bytes; - - /* dummy_cycles stage */ - rt_uint32_t dummy_cycles; - - /* number of lines in qspi data stage, the other configuration items are in parent */ - rt_uint8_t qspi_data_lines; -}; - -struct rt_qspi_configuration -{ - struct rt_spi_configuration parent; - /* The size of medium */ - rt_uint32_t medium_size; - /* double data rate mode */ - rt_uint8_t ddr_mode; - /* the data lines max width which QSPI bus supported, such as 1, 2, 4 */ - rt_uint8_t qspi_dl_width ; -}; - -struct rt_qspi_device -{ - struct rt_spi_device parent; - - struct rt_qspi_configuration config; - - void (*enter_qspi_mode)(struct rt_qspi_device *device); - - void (*exit_qspi_mode)(struct rt_qspi_device *device); -}; - -#define SPI_DEVICE(dev) ((struct rt_spi_device *)(dev)) - -/* register a SPI bus */ -rt_err_t rt_spi_bus_register(struct rt_spi_bus *bus, - const char *name, - const struct rt_spi_ops *ops); - -/* attach a device on SPI bus */ -rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device, - const char *name, - const char *bus_name, - void *user_data); - -/* attach a device on SPI bus with CS pin */ -rt_err_t rt_spi_bus_attach_device_cspin(struct rt_spi_device *device, - const char *name, - const char *bus_name, - rt_base_t cs_pin, - void *user_data); - -/* re-configure SPI bus */ -rt_err_t rt_spi_bus_configure(struct rt_spi_device *device); - -/** - * This function takes SPI bus. - * - * @param device the SPI device attached to SPI bus - * - * @return RT_EOK on taken SPI bus successfully. others on taken SPI bus failed. - */ -rt_err_t rt_spi_take_bus(struct rt_spi_device *device); - -/** - * This function releases SPI bus. - * - * @param device the SPI device attached to SPI bus - * - * @return RT_EOK on release SPI bus successfully. - */ -rt_err_t rt_spi_release_bus(struct rt_spi_device *device); - -/** - * This function take SPI device (takes CS of SPI device). - * - * @param device the SPI device attached to SPI bus - * - * @return RT_EOK on release SPI bus successfully. others on taken SPI bus failed. - */ -rt_err_t rt_spi_take(struct rt_spi_device *device); - -/** - * This function releases SPI device (releases CS of SPI device). - * - * @param device the SPI device attached to SPI bus - * - * @return RT_EOK on release SPI device successfully. - */ -rt_err_t rt_spi_release(struct rt_spi_device *device); - -/* set configuration on SPI device */ -rt_err_t rt_spi_configure(struct rt_spi_device *device, - struct rt_spi_configuration *cfg); - -/* send data then receive data from SPI device */ -rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device, - const void *send_buf, - rt_size_t send_length, - void *recv_buf, - rt_size_t recv_length); - -rt_err_t rt_spi_send_then_send(struct rt_spi_device *device, - const void *send_buf1, - rt_size_t send_length1, - const void *send_buf2, - rt_size_t send_length2); - -/** - * This function transmits data to SPI device. - * - * @param device the SPI device attached to SPI bus - * @param send_buf the buffer to be transmitted to SPI device. - * @param recv_buf the buffer to save received data from SPI device. - * @param length the length of transmitted data. - * - * @return the actual length of transmitted. - */ -rt_ssize_t rt_spi_transfer(struct rt_spi_device *device, - const void *send_buf, - void *recv_buf, - rt_size_t length); - -rt_err_t rt_spi_sendrecv8(struct rt_spi_device *device, - rt_uint8_t senddata, - rt_uint8_t *recvdata); - -rt_err_t rt_spi_sendrecv16(struct rt_spi_device *device, - rt_uint16_t senddata, - rt_uint16_t *recvdata); - -/** - * This function transfers a message list to the SPI device. - * - * @param device the SPI device attached to SPI bus - * @param message the message list to be transmitted to SPI device - * - * @return RT_NULL if transmits message list successfully, - * SPI message which be transmitted failed. - */ -struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device *device, - struct rt_spi_message *message); - -rt_inline rt_size_t rt_spi_recv(struct rt_spi_device *device, - void *recv_buf, - rt_size_t length) -{ - return rt_spi_transfer(device, RT_NULL, recv_buf, length); -} - -rt_inline rt_size_t rt_spi_send(struct rt_spi_device *device, - const void *send_buf, - rt_size_t length) -{ - return rt_spi_transfer(device, send_buf, RT_NULL, length); -} - -/** - * This function appends a message to the SPI message list. - * - * @param list the SPI message list header. - * @param message the message pointer to be appended to the message list. - */ -rt_inline void rt_spi_message_append(struct rt_spi_message *list, - struct rt_spi_message *message) -{ - RT_ASSERT(list != RT_NULL); - if (message == RT_NULL) - return; /* not append */ - - while (list->next != RT_NULL) - { - list = list->next; - } - - list->next = message; - message->next = RT_NULL; -} - -/** - * This function can set configuration on QSPI device. - * - * @param device the QSPI device attached to QSPI bus. - * @param cfg the configuration pointer. - * - * @return the actual length of transmitted. - */ -rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configuration *cfg); - -/** - * This function can register a SPI bus for QSPI mode. - * - * @param bus the SPI bus for QSPI mode. - * @param name The name of the spi bus. - * @param ops the SPI bus instance to be registered. - * - * @return the actual length of transmitted. - */ -rt_err_t rt_qspi_bus_register(struct rt_spi_bus *bus, const char *name, const struct rt_spi_ops *ops); - -/** - * This function transmits data to QSPI device. - * - * @param device the QSPI device attached to QSPI bus. - * @param message the message pointer. - * - * @return the actual length of transmitted. - */ -rt_size_t rt_qspi_transfer_message(struct rt_qspi_device *device, struct rt_qspi_message *message); - -/** - * This function can send data then receive data from QSPI device - * - * @param device the QSPI device attached to QSPI bus. - * @param send_buf the buffer to be transmitted to QSPI device. - * @param send_length the number of data to be transmitted. - * @param recv_buf the buffer to be recivied from QSPI device. - * @param recv_length the data to be recivied. - * - * @return the status of transmit. - */ -rt_err_t rt_qspi_send_then_recv(struct rt_qspi_device *device, const void *send_buf, rt_size_t send_length,void *recv_buf, rt_size_t recv_length); - -/** - * This function can send data to QSPI device - * - * @param device the QSPI device attached to QSPI bus. - * @param send_buf the buffer to be transmitted to QSPI device. - * @param send_length the number of data to be transmitted. - * - * @return the status of transmit. - */ -rt_err_t rt_qspi_send(struct rt_qspi_device *device, const void *send_buf, rt_size_t length); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/rt-thread/components/drivers/include/drivers/syscon.h b/rt-thread/components/drivers/include/drivers/syscon.h new file mode 100644 index 0000000..15bb98b --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/syscon.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __SYSCON_H__ +#define __SYSCON_H__ + +#include + +struct rt_syscon +{ + rt_list_t list; + + struct rt_ofw_node *np; + + void *iomem_base; + rt_size_t iomem_size; + struct rt_spinlock rw_lock; +}; + +rt_err_t rt_syscon_read(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t *out_val); +rt_err_t rt_syscon_write(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t val); +rt_err_t rt_syscon_update_bits(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t mask, rt_uint32_t val); + +struct rt_syscon *rt_syscon_find_by_ofw_node(struct rt_ofw_node *np); +struct rt_syscon *rt_syscon_find_by_ofw_compatible(const char *compatible); +struct rt_syscon *rt_syscon_find_by_ofw_phandle(struct rt_ofw_node *np, const char *propname); + +#endif /* __SYSCON_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/thermal.h b/rt-thread/components/drivers/include/drivers/thermal.h new file mode 100644 index 0000000..0770c5c --- /dev/null +++ b/rt-thread/components/drivers/include/drivers/thermal.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#ifndef __THERMAL_H__ +#define __THERMAL_H__ + +#include +#include + +/* No upper/lower limit requirement */ +#define RT_THERMAL_NO_LIMIT ((rt_uint32_t)THERMAL_NO_LIMIT) +#define RT_THERMAL_TEMP_INVALID (-274000) + +struct rt_thermal_zone_ops; +struct rt_thermal_cooling_device; +struct rt_thermal_cooling_device_ops; +struct rt_thermal_cooling_governor; + +enum rt_thermal_trip_type +{ + RT_THERMAL_TRIP_ACTIVE = 0, + RT_THERMAL_TRIP_PASSIVE, + RT_THERMAL_TRIP_HOT, + RT_THERMAL_TRIP_CRITICAL, + + RT_THERMAL_TRIP_TYPE_MAX, +}; + +struct rt_thermal_trip +{ + /* Temperature value in millidegree celsius */ + int temperature; + /* Relative hysteresis in millidegree celsius */ + int hysteresis; + enum rt_thermal_trip_type type; + + void *priv; +}; + +struct rt_thermal_zone_params +{ + /* Sustainable power (heat) that this thermal zone can dissipate in mW */ + int sustainable_power; + /* Slope of a linear temperature adjustment curve */ + int slope; + /* Offset of a linear temperature adjustment curve */ + int offset; +}; + +struct rt_thermal_cooling_cell +{ + struct rt_thermal_cooling_device *cooling_devices; + + rt_uint32_t level_range[2]; +}; + +struct rt_thermal_cooling_map +{ + rt_uint32_t contribution; + + rt_size_t cells_nr; + struct rt_thermal_cooling_cell *cells; + struct rt_thermal_trip *trips; +}; + +struct rt_thermal_zone_device +{ + struct rt_device parent; + + int zone_id; + const struct rt_thermal_zone_ops *ops; + + rt_bool_t trips_free; + rt_size_t trips_nr; + struct rt_thermal_trip *trips; + struct rt_thermal_zone_params params; + + rt_bool_t enabled; + rt_bool_t cooling; + int temperature; + int last_temperature; + int prev_low_trip; + int prev_high_trip; + + rt_list_t notifier_nodes; + struct rt_spinlock nodes_lock; + + rt_size_t cooling_maps_nr; + struct rt_thermal_cooling_map *cooling_maps; + + rt_tick_t passive_delay, polling_delay; + struct rt_work poller; + + struct rt_mutex mutex; + + void *priv; +}; + +struct rt_thermal_zone_ops +{ + rt_err_t (*get_temp)(struct rt_thermal_zone_device *zdev, int *out_temp); + rt_err_t (*set_trips)(struct rt_thermal_zone_device *zdev, int low_temp, int high_temp); + rt_err_t (*set_trip_temp)(struct rt_thermal_zone_device *zdev, int trip_id, int temp); + rt_err_t (*set_trip_hyst)(struct rt_thermal_zone_device *zdev, int trip_id, int hyst); + void (*hot)(struct rt_thermal_zone_device *zdev); + void (*critical)(struct rt_thermal_zone_device *zdev); +}; + +/* + * We don't want to make a temperature control system + * that is finer than an air conditioner's temperature control, + * just ensure get a reliable heat dissipation under high-load task + * or when the SoC temperature is too high. + */ +struct rt_thermal_cooling_device +{ + struct rt_device parent; + + const struct rt_thermal_cooling_device_ops *ops; + + /* The cooling capacity indicator */ + rt_ubase_t max_level; + rt_list_t governor_node; + struct rt_thermal_cooling_governor *gov; + + void *priv; +}; + +struct rt_thermal_cooling_device_ops +{ + rt_err_t (*bind)(struct rt_thermal_cooling_device *cdev, struct rt_thermal_zone_device *zdev); + rt_err_t (*unbind)(struct rt_thermal_cooling_device *cdev, struct rt_thermal_zone_device *zdev); + rt_err_t (*get_max_level)(struct rt_thermal_cooling_device *cdev, rt_ubase_t *out_level); + rt_err_t (*get_cur_level)(struct rt_thermal_cooling_device *cdev, rt_ubase_t *out_level); + rt_err_t (*set_cur_level)(struct rt_thermal_cooling_device *cdev, rt_ubase_t level); +}; + +struct rt_thermal_cooling_governor +{ + rt_list_t list; + + const char *name; + rt_list_t cdev_nodes; + + void (*tuning)(struct rt_thermal_zone_device *zdev, + int map_idx, int cell_idx, rt_ubase_t *level); +}; + +struct rt_thermal_notifier; + +#define RT_THERMAL_MSG_EVENT_UNSPECIFIED RT_BIT(0) /* Unspecified event */ +#define RT_THERMAL_MSG_EVENT_TEMP_SAMPLE RT_BIT(1) /* New Temperature sample */ +#define RT_THERMAL_MSG_TRIP_VIOLATED RT_BIT(2) /* TRIP Point violation */ +#define RT_THERMAL_MSG_TRIP_CHANGED RT_BIT(3) /* TRIP Point temperature changed */ +#define RT_THERMAL_MSG_DEVICE_DOWN RT_BIT(4) /* Thermal device is down */ +#define RT_THERMAL_MSG_DEVICE_UP RT_BIT(5) /* Thermal device is up after a down event */ +#define RT_THERMAL_MSG_DEVICE_POWER_CAPABILITY_CHANGED RT_BIT(6) /* Power capability changed */ +#define RT_THERMAL_MSG_TABLE_CHANGED RT_BIT(7) /* Thermal table(s) changed */ +#define RT_THERMAL_MSG_EVENT_KEEP_ALIVE RT_BIT(8) /* Request for user space handler to respond */ + +typedef rt_err_t (*rt_thermal_notifier_callback)(struct rt_thermal_notifier *notifier, + rt_ubase_t msg); + +struct rt_thermal_notifier +{ + rt_list_t list; + + struct rt_thermal_zone_device *zdev; + rt_thermal_notifier_callback callback; + void *priv; +}; + +rt_err_t rt_thermal_zone_device_register(struct rt_thermal_zone_device *zdev); +rt_err_t rt_thermal_zone_device_unregister(struct rt_thermal_zone_device *zdev); + +rt_err_t rt_thermal_cooling_device_register(struct rt_thermal_cooling_device *cdev); +rt_err_t rt_thermal_cooling_device_unregister(struct rt_thermal_cooling_device *cdev); + +rt_err_t rt_thermal_cooling_governor_register(struct rt_thermal_cooling_governor *gov); +rt_err_t rt_thermal_cooling_governor_unregister(struct rt_thermal_cooling_governor *gov); + +rt_err_t rt_thermal_cooling_device_change_governor(struct rt_thermal_cooling_device *cdev, + const char *name); + +rt_err_t rt_thermal_zone_notifier_register(struct rt_thermal_zone_device *zdev, + struct rt_thermal_notifier *notifier); +rt_err_t rt_thermal_zone_notifier_unregister(struct rt_thermal_zone_device *zdev, + struct rt_thermal_notifier *notifier); + +void rt_thermal_zone_device_update(struct rt_thermal_zone_device *zdev, rt_ubase_t msg); +void rt_thermal_cooling_device_kick(struct rt_thermal_zone_device *zdev); + +rt_err_t rt_thermal_zone_set_trip(struct rt_thermal_zone_device *zdev, int trip_id, + const struct rt_thermal_trip *trip); +rt_err_t rt_thermal_zone_get_trip(struct rt_thermal_zone_device *zdev, int trip_id, + struct rt_thermal_trip *out_trip); + +#endif /* __THERMAL_H__ */ diff --git a/rt-thread/components/drivers/include/drivers/wlan.h b/rt-thread/components/drivers/include/drivers/wlan.h index 9468518..4a1ab5b 100644 --- a/rt-thread/components/drivers/include/drivers/wlan.h +++ b/rt-thread/components/drivers/include/drivers/wlan.h @@ -12,10 +12,10 @@ #define __WLAN_H__ #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #endif diff --git a/rt-thread/components/drivers/include/dt-bindings/phye/phye.h b/rt-thread/components/drivers/include/dt-bindings/phye/phye.h new file mode 100644 index 0000000..54a9cce --- /dev/null +++ b/rt-thread/components/drivers/include/dt-bindings/phye/phye.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_PHYE_H__ +#define __DT_BINDINGS_PHYE_H__ + +#define PHY_NONE 0 +#define PHY_TYPE_SATA 1 +#define PHY_TYPE_PCIE 2 +#define PHY_TYPE_USB2 3 +#define PHY_TYPE_USB3 4 +#define PHY_TYPE_UFS 5 +#define PHY_TYPE_DP 6 +#define PHY_TYPE_XPCS 7 +#define PHY_TYPE_SGMII 8 +#define PHY_TYPE_QSGMII 9 +#define PHY_TYPE_DPHY 10 +#define PHY_TYPE_CPHY 11 +#define PHY_TYPE_USXGMII 12 + +#endif /* __DT_BINDINGS_PHYE_H__ */ diff --git a/rt-thread/components/drivers/include/dt-bindings/thermal/thermal.h b/rt-thread/components/drivers/include/dt-bindings/thermal/thermal.h new file mode 100644 index 0000000..60414cf --- /dev/null +++ b/rt-thread/components/drivers/include/dt-bindings/thermal/thermal.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_THERMAL_THERMAL_H__ +#define __DT_BINDINGS_THERMAL_THERMAL_H__ + +/* On cooling devices upper and lower limits */ +#define THERMAL_NO_LIMIT (~0) + +#endif /* __DT_BINDINGS_THERMAL_THERMAL_H__ */ diff --git a/rt-thread/components/drivers/include/ipc/completion.h b/rt-thread/components/drivers/include/ipc/completion.h index 30f4acf..25581f6 100644 --- a/rt-thread/components/drivers/include/ipc/completion.h +++ b/rt-thread/components/drivers/include/ipc/completion.h @@ -6,6 +6,7 @@ * Change Logs: * Date Author Notes * 2024-04-28 Shell Add new wait_flags() & wakeup_by_errno() API + * 2024-10-24 yekai Add C++ support */ #ifndef COMPLETION_H_ #define COMPLETION_H_ @@ -14,9 +15,9 @@ #include /** - * Completion - A tiny & rapid IPC primitive for resource-constrained scenarios + * RT-Completion - A Tiny(resource-constrained) & Rapid(lockless) IPC Primitive * - * It's an IPC using one CPU word with the encoding: + * It's an IPC using one pointer word with the encoding: * * BIT | MAX-1 ----------------- 1 | 0 | * CONTENT | suspended_thread & ~1 | completed flag | @@ -30,12 +31,25 @@ struct rt_completion #define RT_COMPLETION_INIT(comp) {0} +#ifdef __cplusplus +extern "C" { +#endif + void rt_completion_init(struct rt_completion *completion); rt_err_t rt_completion_wait(struct rt_completion *completion, rt_int32_t timeout); +rt_err_t rt_completion_wait_noisr(struct rt_completion *completion, + rt_int32_t timeout); rt_err_t rt_completion_wait_flags(struct rt_completion *completion, rt_int32_t timeout, int suspend_flag); +rt_err_t rt_completion_wait_flags_noisr(struct rt_completion *completion, + rt_int32_t timeout, int suspend_flag); void rt_completion_done(struct rt_completion *completion); rt_err_t rt_completion_wakeup(struct rt_completion *completion); rt_err_t rt_completion_wakeup_by_errno(struct rt_completion *completion, rt_err_t error); + +#ifdef __cplusplus +} +#endif + #endif diff --git a/rt-thread/components/drivers/include/ipc/workqueue.h b/rt-thread/components/drivers/include/ipc/workqueue.h index e05a7ab..8d0c986 100644 --- a/rt-thread/components/drivers/include/ipc/workqueue.h +++ b/rt-thread/components/drivers/include/ipc/workqueue.h @@ -13,6 +13,7 @@ #include #include +#include "completion.h" #ifdef __cplusplus extern "C" { @@ -42,6 +43,7 @@ struct rt_workqueue struct rt_semaphore sem; rt_thread_t work_thread; struct rt_spinlock spinlock; + struct rt_completion wakeup_completion; }; struct rt_work @@ -52,7 +54,7 @@ struct rt_work void *work_data; rt_uint16_t flags; rt_uint16_t type; - struct rt_timer timer; + rt_tick_t timeout_tick; struct rt_workqueue *workqueue; }; diff --git a/rt-thread/components/drivers/include/rtdevice.h b/rt-thread/components/drivers/include/rtdevice.h index 5b080fc..fd4d316 100644 --- a/rt-thread/components/drivers/include/rtdevice.h +++ b/rt-thread/components/drivers/include/rtdevice.h @@ -45,6 +45,34 @@ extern "C" { #include "drivers/core/power_domain.h" #include "drivers/platform.h" +#ifdef RT_USING_ATA +#ifdef RT_ATA_AHCI +#include "drivers/ahci.h" +#endif /* RT_ATA_AHCI */ +#endif /* RT_USING_ATA */ + +#ifdef RT_USING_LED +#include "drivers/led.h" +#endif + +#ifdef RT_USING_MBOX +#include "drivers/mailbox.h" +#endif /* RT_USING_MBOX */ + +#ifdef RT_USING_BLK +#include "drivers/blk.h" +#endif /* RT_USING_BLK */ + +#ifdef RT_USING_DMA +#include "drivers/dma.h" +#endif /* RT_USING_DMA */ + +#include "drivers/iio.h" + +#ifdef RT_USING_NVME +#include "drivers/nvme.h" +#endif /* RT_USING_NVME */ + #ifdef RT_USING_OFW #include "drivers/ofw.h" #include "drivers/ofw_fdt.h" @@ -53,20 +81,54 @@ extern "C" { #include "drivers/ofw_raw.h" #endif /* RT_USING_OFW */ +#ifdef RT_USING_PHYE +#include "drivers/phye.h" +#endif /* RT_USING_PHYE */ + #ifdef RT_USING_PIC #include "drivers/pic.h" -#endif +#endif /* RT_USING_PIC */ + +#ifdef RT_USING_PCI +#include "drivers/pci.h" +#ifdef RT_PCI_MSI +#include "drivers/pci_msi.h" +#endif /* RT_PCI_MSI */ +#ifdef RT_PCI_ENDPOINT +#include "drivers/pci_endpoint.h" +#endif /* RT_PCI_ENDPOINT */ +#endif /* RT_USING_PCI */ + +#ifdef RT_USING_REGULATOR +#include "drivers/regulator.h" +#endif /* RT_USING_REGULATOR */ + +#ifdef RT_USING_RESET +#include "drivers/reset.h" +#endif /* RT_USING_RESET */ + +#ifdef RT_USING_SCSI +#include "drivers/scsi.h" +#endif /* RT_USING_SCSI */ + +#ifdef RT_MFD_SYSCON +#include "drivers/syscon.h" +#endif /* RT_MFD_SYSCON */ + +#ifdef RT_USING_THERMAL +#include "drivers/thermal.h" +#endif /* RT_USING_THERMAL */ #endif /* RT_USING_DM */ #ifdef RT_USING_RTC -#include "drivers/rtc.h" +#include "drivers/dev_rtc.h" #ifdef RT_USING_ALARM -#include "drivers/alarm.h" -#endif +#include "drivers/dev_alarm.h" +#endif /* RT_USING_ALARM */ #endif /* RT_USING_RTC */ #ifdef RT_USING_SPI -#include "drivers/spi.h" +#include "drivers/dev_spi.h" #endif /* RT_USING_SPI */ #ifdef RT_USING_MTD_NOR @@ -87,43 +149,44 @@ extern "C" { #ifdef RT_USING_SERIAL #ifdef RT_USING_SERIAL_V2 -#include "drivers/serial_v2.h" +#include "drivers/dev_serial_v2.h" #else -#include "drivers/serial.h" +#include "drivers/dev_serial.h" +#ifdef RT_USING_SERIAL_BYPASS +#include "drivers/serial_bypass.h" +#endif /* RT_USING_SERIAL_BYPASS */ #endif #endif /* RT_USING_SERIAL */ #ifdef RT_USING_I2C -#include "drivers/i2c.h" -#include "drivers/i2c_dev.h" +#include "drivers/dev_i2c.h" #ifdef RT_USING_I2C_BITOPS -#include "drivers/i2c-bit-ops.h" +#include "drivers/dev_i2c_bit_ops.h" #endif /* RT_USING_I2C_BITOPS */ #ifdef RT_USING_DM -#include "drivers/i2c_dm.h" +#include "drivers/dev_i2c_dm.h" #endif /* RT_USING_DM */ #endif /* RT_USING_I2C */ #ifdef RT_USING_PHY #include "drivers/phy.h" -#include "drivers/phy_mdio.h" #endif /* RT_USING_PHY */ #ifdef RT_USING_SDIO -#include "drivers/mmcsd_core.h" -#include "drivers/sd.h" -#include "drivers/sdio.h" +#include "drivers/dev_mmcsd_core.h" +#include "drivers/dev_sd.h" +#include "drivers/dev_sdio.h" #endif /* RT_USING_SDIO */ #ifdef RT_USING_WDT -#include "drivers/watchdog.h" +#include "drivers/dev_watchdog.h" #endif /* RT_USING_WDT */ #ifdef RT_USING_PIN -#include "drivers/pin.h" +#include "drivers/dev_pin.h" #endif /* RT_USING_PIN */ #ifdef RT_USING_SENSOR @@ -135,7 +198,7 @@ extern "C" { #endif /* RT_USING_SENSOR */ #ifdef RT_USING_CAN -#include "drivers/can.h" +#include "drivers/dev_can.h" #endif /* RT_USING_CAN */ #ifdef RT_USING_HWTIMER @@ -143,7 +206,7 @@ extern "C" { #endif /* RT_USING_HWTIMER */ #ifdef RT_USING_AUDIO -#include "drivers/audio.h" +#include "drivers/dev_audio.h" #endif /* RT_USING_AUDIO */ #ifdef RT_USING_CPUTIME @@ -159,7 +222,7 @@ extern "C" { #endif /* RT_USING_DAC */ #ifdef RT_USING_PWM -#include "drivers/rt_drv_pwm.h" +#include "drivers/dev_pwm.h" #endif /* RT_USING_PWM */ #ifdef RT_USING_PM @@ -191,7 +254,7 @@ extern "C" { #endif /* RT_USING_INPUT_CAPTURE */ #ifdef RT_USING_TOUCH -#include "drivers/touch.h" +#include "drivers/dev_touch.h" #endif #ifdef RT_USING_DEV_BUS diff --git a/rt-thread/components/drivers/ipc/completion_comm.c b/rt-thread/components/drivers/ipc/completion_comm.c index 6828714..67c00b4 100644 --- a/rt-thread/components/drivers/ipc/completion_comm.c +++ b/rt-thread/components/drivers/ipc/completion_comm.c @@ -35,6 +35,28 @@ rt_err_t rt_completion_wakeup(struct rt_completion *completion) return rt_completion_wakeup_by_errno(completion, -1); } +/** + * @brief This is same as rt_completion_wait(), except that this API is NOT + * ISR-safe (you can NOT call completion_done() on isr routine). + * + * @param completion is a pointer to a completion object. + * + * @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for + * the completion done up to the amount of time specified by the argument. + * NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the + * completion is unavailable, the thread will be waitting forever. + * + * @return Return the operation status. ONLY when the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the completion wait failed. + * + * @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context. + */ +rt_err_t rt_completion_wait_noisr(struct rt_completion *completion, + rt_int32_t timeout) +{ + return rt_completion_wait_flags_noisr(completion, timeout, RT_UNINTERRUPTIBLE); +} + /** * @brief This function will wait for a completion, if the completion is unavailable, the thread shall wait for * the completion up to a specified time. diff --git a/rt-thread/components/drivers/ipc/completion_mp.c b/rt-thread/components/drivers/ipc/completion_mp.c index 21627e0..38ffc6f 100644 --- a/rt-thread/components/drivers/ipc/completion_mp.c +++ b/rt-thread/components/drivers/ipc/completion_mp.c @@ -65,11 +65,10 @@ void rt_completion_init(struct rt_completion *completion) RTM_EXPORT(rt_completion_init); /** - * @brief This function will wait for a completion, if the completion is unavailable, the thread shall wait for - * the completion up to a specified time. + * @brief This is same as rt_completion_wait_flags(), except that this API is NOT + * ISR-safe (you can NOT call completion_done() on isr routine). * * @param completion is a pointer to a completion object. - * * @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for * the completion done up to the amount of time specified by the argument. * NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the @@ -81,8 +80,8 @@ RTM_EXPORT(rt_completion_init); * * @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context. */ -rt_err_t rt_completion_wait_flags(struct rt_completion *completion, - rt_int32_t timeout, int suspend_flag) +rt_err_t rt_completion_wait_flags_noisr(struct rt_completion *completion, + rt_int32_t timeout, int suspend_flag) { rt_err_t result = -RT_ERROR; rt_thread_t thread; @@ -159,6 +158,33 @@ rt_err_t rt_completion_wait_flags(struct rt_completion *completion, return result; } +/** + * @brief This function will wait for a completion, if the completion is unavailable, the thread shall wait for + * the completion up to a specified time. + * + * @param completion is a pointer to a completion object. + * @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for + * the completion done up to the amount of time specified by the argument. + * NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the + * completion is unavailable, the thread will be waitting forever. + * @param suspend_flag suspend flags. See rt_thread_suspend_with_flag() + * + * @return Return the operation status. ONLY when the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the completion wait failed. + * + * @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context. + */ +rt_err_t rt_completion_wait_flags(struct rt_completion *completion, + rt_int32_t timeout, int suspend_flag) +{ + rt_err_t error; + rt_ubase_t level; + level = rt_hw_local_irq_disable(); + error = rt_completion_wait_flags_noisr(completion, timeout, suspend_flag); + rt_hw_local_irq_enable(level); + return error; +} + static rt_base_t _wait_until_update(struct rt_completion *completion, rt_base_t expected) { rt_base_t current_value; diff --git a/rt-thread/components/drivers/ipc/completion_up.c b/rt-thread/components/drivers/ipc/completion_up.c index 5c8e546..8c3bbbf 100644 --- a/rt-thread/components/drivers/ipc/completion_up.c +++ b/rt-thread/components/drivers/ipc/completion_up.c @@ -148,6 +148,28 @@ __exit: return result; } +/** + * @brief This is same as rt_completion_wait_flags(), except that this API is NOT + * ISR-safe (you can NOT call completion_done() on isr routine). + * + * @param completion is a pointer to a completion object. + * @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for + * the completion done up to the amount of time specified by the argument. + * NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the + * completion is unavailable, the thread will be waitting forever. + * @param suspend_flag suspend flags. See rt_thread_suspend_with_flag() + * + * @return Return the operation status. ONLY when the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the completion wait failed. + * + * @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context. + */ +rt_err_t rt_completion_wait_flags_noisr(struct rt_completion *completion, + rt_int32_t timeout, int suspend_flag) +{ + return rt_completion_wait_flags(completion, timeout, suspend_flag); +} + /** * @brief This function indicates a completion has done and wakeup the thread * and update its errno. No update is applied if it's a negative value. diff --git a/rt-thread/components/drivers/ipc/workqueue.c b/rt-thread/components/drivers/ipc/workqueue.c index fb3657b..30b779e 100644 --- a/rt-thread/components/drivers/ipc/workqueue.c +++ b/rt-thread/components/drivers/ipc/workqueue.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2022, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -10,6 +10,7 @@ * 2021-08-14 Jackistang add comments for function interface * 2022-01-16 Meco Man add rt_work_urgent() * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable + * 2024-12-21 yuqingli delete timer, using list */ #include @@ -17,8 +18,6 @@ #ifdef RT_USING_HEAP -static void _delayed_work_timeout_handler(void *parameter); - rt_inline rt_err_t _workqueue_work_completion(struct rt_workqueue *queue) { rt_err_t result; @@ -50,38 +49,61 @@ rt_inline rt_err_t _workqueue_work_completion(struct rt_workqueue *queue) static void _workqueue_thread_entry(void *parameter) { - rt_base_t level; - struct rt_work *work; + rt_base_t level; + struct rt_work *work; struct rt_workqueue *queue; + rt_tick_t current_tick; + rt_int32_t delay_tick; + void (*work_func)(struct rt_work *work, void *work_data); + void *work_data; - queue = (struct rt_workqueue *) parameter; + queue = (struct rt_workqueue *)parameter; RT_ASSERT(queue != RT_NULL); while (1) { level = rt_spin_lock_irqsave(&(queue->spinlock)); + + /* timer check */ + current_tick = rt_tick_get(); + delay_tick = RT_WAITING_FOREVER; + while (!rt_list_isempty(&(queue->delayed_list))) + { + work = rt_list_entry(queue->delayed_list.next, struct rt_work, list); + if ((current_tick - work->timeout_tick) < RT_TICK_MAX / 2) + { + rt_list_remove(&(work->list)); + rt_list_insert_after(queue->work_list.prev, &(work->list)); + work->flags &= ~RT_WORK_STATE_SUBMITTING; + work->flags |= RT_WORK_STATE_PENDING; + } + else + { + delay_tick = work->timeout_tick - current_tick; + break; + } + } + if (rt_list_isempty(&(queue->work_list))) { - /* no software timer exist, suspend self. */ - rt_thread_suspend_with_flag(rt_thread_self(), RT_UNINTERRUPTIBLE); - - /* release lock after suspend so we will not lost any wakeups */ rt_spin_unlock_irqrestore(&(queue->spinlock), level); - - rt_schedule(); + /* wait for work completion */ + rt_completion_wait(&(queue->wakeup_completion), delay_tick); continue; } /* we have work to do with. */ work = rt_list_entry(queue->work_list.next, struct rt_work, list); rt_list_remove(&(work->list)); - queue->work_current = work; - work->flags &= ~RT_WORK_STATE_PENDING; - work->workqueue = RT_NULL; + queue->work_current = work; + work->flags &= ~RT_WORK_STATE_PENDING; + work->workqueue = RT_NULL; + work_func = work->work_func; + work_data = work->work_data; rt_spin_unlock_irqrestore(&(queue->spinlock), level); /* do work */ - work->work_func(work, work->work_data); + work_func(work, work_data); /* clean current work */ queue->work_current = RT_NULL; @@ -93,52 +115,52 @@ static void _workqueue_thread_entry(void *parameter) static rt_err_t _workqueue_submit_work(struct rt_workqueue *queue, struct rt_work *work, rt_tick_t ticks) { - rt_base_t level; - rt_err_t err = RT_EOK; + rt_base_t level; + rt_err_t err = RT_EOK; + struct rt_work *work_tmp; + rt_list_t *list_tmp; level = rt_spin_lock_irqsave(&(queue->spinlock)); - /* remove list */ rt_list_remove(&(work->list)); - work->flags &= ~RT_WORK_STATE_PENDING; + work->flags = 0; if (ticks == 0) { rt_list_insert_after(queue->work_list.prev, &(work->list)); - work->flags |= RT_WORK_STATE_PENDING; - work->workqueue = queue; + work->flags |= RT_WORK_STATE_PENDING; + work->workqueue = queue; - /* whether the workqueue is doing work */ - if (queue->work_current == RT_NULL) - { - /* resume work thread, and do a re-schedule if succeed */ - rt_thread_resume(queue->work_thread); - } + rt_completion_done(&(queue->wakeup_completion)); + err = RT_EOK; } else if (ticks < RT_TICK_MAX / 2) { - /* Timer started */ - if (work->flags & RT_WORK_STATE_SUBMITTING) - { - rt_timer_control(&work->timer, RT_TIMER_CTRL_SET_TIME, &ticks); - } - else - { - rt_timer_init(&(work->timer), "work", _delayed_work_timeout_handler, - work, ticks, RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER); - work->flags |= RT_WORK_STATE_SUBMITTING; - } - work->workqueue = queue; /* insert delay work list */ - rt_list_insert_after(queue->delayed_list.prev, &(work->list)); + work->flags |= RT_WORK_STATE_SUBMITTING; + work->workqueue = queue; + work->timeout_tick = rt_tick_get() + ticks; - err = rt_timer_start(&(work->timer)); + list_tmp = &(queue->delayed_list); + for (work_tmp = rt_list_entry(list_tmp->next, struct rt_work, list); + &work_tmp->list != list_tmp; + work_tmp = rt_list_entry(work_tmp->list.next, struct rt_work, list)) + { + if ((work_tmp->timeout_tick - work->timeout_tick) < RT_TICK_MAX / 2) + { + list_tmp = &(work_tmp->list); + break; + } + } + rt_list_insert_before(list_tmp, &(work->list)); + + rt_completion_done(&(queue->wakeup_completion)); + err = RT_EOK; } else { - err = - RT_ERROR; + err = -RT_ERROR; } - rt_spin_unlock_irqrestore(&(queue->spinlock), level); return err; } @@ -146,61 +168,17 @@ static rt_err_t _workqueue_submit_work(struct rt_workqueue *queue, static rt_err_t _workqueue_cancel_work(struct rt_workqueue *queue, struct rt_work *work) { rt_base_t level; - rt_err_t err; + rt_err_t err; level = rt_spin_lock_irqsave(&(queue->spinlock)); rt_list_remove(&(work->list)); - work->flags &= ~RT_WORK_STATE_PENDING; - /* Timer started */ - if (work->flags & RT_WORK_STATE_SUBMITTING) - { - if ((err = rt_timer_stop(&(work->timer))) != RT_EOK) - { - goto exit; - } - rt_timer_detach(&(work->timer)); - work->flags &= ~RT_WORK_STATE_SUBMITTING; - } - err = queue->work_current != work ? RT_EOK : -RT_EBUSY; + work->flags = 0; + err = queue->work_current != work ? RT_EOK : -RT_EBUSY; work->workqueue = RT_NULL; -exit: rt_spin_unlock_irqrestore(&(queue->spinlock), level); return err; } -static void _delayed_work_timeout_handler(void *parameter) -{ - struct rt_work *work; - struct rt_workqueue *queue; - rt_base_t level; - - work = (struct rt_work *)parameter; - queue = work->workqueue; - - RT_ASSERT(work->flags & RT_WORK_STATE_SUBMITTING); - RT_ASSERT(queue != RT_NULL); - - level = rt_spin_lock_irqsave(&(queue->spinlock)); - rt_timer_detach(&(work->timer)); - work->flags &= ~RT_WORK_STATE_SUBMITTING; - /* remove delay list */ - rt_list_remove(&(work->list)); - /* insert work queue */ - if (queue->work_current != work) - { - rt_list_insert_after(queue->work_list.prev, &(work->list)); - work->flags |= RT_WORK_STATE_PENDING; - } - /* whether the workqueue is doing work */ - if (queue->work_current == RT_NULL) - { - /* resume work thread, and do a re-schedule if succeed */ - rt_thread_resume(queue->work_thread); - } - - rt_spin_unlock_irqrestore(&(queue->spinlock), level); -} - /** * @brief Initialize a work item, binding with a callback function. * @@ -221,8 +199,8 @@ void rt_work_init(struct rt_work *work, work->work_func = work_func; work->work_data = work_data; work->workqueue = RT_NULL; - work->flags = 0; - work->type = 0; + work->flags = 0; + work->type = 0; } /** @@ -248,6 +226,7 @@ struct rt_workqueue *rt_workqueue_create(const char *name, rt_uint16_t stack_siz rt_list_init(&(queue->delayed_list)); queue->work_current = RT_NULL; rt_sem_init(&(queue->sem), "wqueue", 0, RT_IPC_FLAG_FIFO); + rt_completion_init(&(queue->wakeup_completion)); /* create the work thread */ queue->work_thread = rt_thread_create(name, _workqueue_thread_entry, queue, stack_size, priority, 10); @@ -292,7 +271,6 @@ rt_err_t rt_workqueue_destroy(struct rt_workqueue *queue) * @param work is a pointer to the work item object. * * @return RT_EOK Success. - * -RT_EBUSY This work item is executing. */ rt_err_t rt_workqueue_dowork(struct rt_workqueue *queue, struct rt_work *work) { @@ -314,7 +292,6 @@ rt_err_t rt_workqueue_dowork(struct rt_workqueue *queue, struct rt_work *work) * NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1) * * @return RT_EOK Success. - * -RT_EBUSY This work item is executing. * -RT_ERROR The ticks parameter is invalid. */ rt_err_t rt_workqueue_submit_work(struct rt_workqueue *queue, struct rt_work *work, rt_tick_t ticks) @@ -346,14 +323,10 @@ rt_err_t rt_workqueue_urgent_work(struct rt_workqueue *queue, struct rt_work *wo /* NOTE: the work MUST be initialized firstly */ rt_list_remove(&(work->list)); rt_list_insert_after(&queue->work_list, &(work->list)); - /* whether the workqueue is doing work */ - if (queue->work_current == RT_NULL) - { - /* resume work thread, and do a re-schedule if succeed */ - rt_thread_resume(queue->work_thread); - } + rt_completion_done(&(queue->wakeup_completion)); rt_spin_unlock_irqrestore(&(queue->spinlock), level); + return RT_EOK; } @@ -448,7 +421,6 @@ static struct rt_workqueue *sys_workq; /* system work queue */ * NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1) * * @return RT_EOK Success. - * -RT_EBUSY This work item is executing. * -RT_ERROR The ticks parameter is invalid. */ rt_err_t rt_work_submit(struct rt_work *work, rt_tick_t ticks) diff --git a/rt-thread/components/drivers/led/Kconfig b/rt-thread/components/drivers/led/Kconfig new file mode 100644 index 0000000..462aa0b --- /dev/null +++ b/rt-thread/components/drivers/led/Kconfig @@ -0,0 +1,15 @@ +menuconfig RT_USING_LED + bool "Using Light Emitting Diode (LED) device drivers" + depends on RT_USING_DM + default n + +config RT_LED_GPIO + bool "GPIO connected LEDs Support" + depends on RT_USING_LED + depends on RT_USING_PINCTRL + depends on RT_USING_OFW + default n + +if RT_USING_LED + osource "$(SOC_DM_LED_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/led/SConscript b/rt-thread/components/drivers/led/SConscript new file mode 100644 index 0000000..276ca0b --- /dev/null +++ b/rt-thread/components/drivers/led/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_DM', 'RT_USING_LED']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['led.c'] + +if GetDepend(['RT_LED_GPIO']): + src += ['led-gpio.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/led/led-gpio.c b/rt-thread/components/drivers/led/led-gpio.c new file mode 100644 index 0000000..0b6bd68 --- /dev/null +++ b/rt-thread/components/drivers/led/led-gpio.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "led.gpio" +#define DBG_LVL DBG_INFO +#include + +struct gpio_led +{ + struct rt_led_device parent; + + rt_base_t pin; + rt_uint8_t active_val; +}; + +#define raw_to_gpio_led(raw) rt_container_of(raw, struct gpio_led, parent); + +static rt_err_t gpio_led_set_state(struct rt_led_device *led, enum rt_led_state state) +{ + rt_err_t err = RT_EOK; + struct gpio_led *gled = raw_to_gpio_led(led); + + rt_pin_mode(gled->pin, PIN_MODE_OUTPUT); + + switch (state) + { + case RT_LED_S_OFF: + rt_pin_write(gled->pin, !gled->active_val); + break; + + case RT_LED_S_ON: + rt_pin_write(gled->pin, gled->active_val); + break; + + case RT_LED_S_TOGGLE: + err = led->ops->get_state(led, &state); + + if (!err) + { + err = led->ops->set_state(led, state == RT_LED_S_OFF ? RT_LED_S_ON : RT_LED_S_OFF); + } + break; + + default: + return -RT_ENOSYS; + } + + return err; +} + +static rt_err_t gpio_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state) +{ + struct gpio_led *gled = raw_to_gpio_led(led); + + switch (rt_pin_read(gled->pin)) + { + case PIN_LOW: + *out_state = RT_LED_S_OFF; + break; + + case PIN_HIGH: + *out_state = RT_LED_S_ON; + break; + + default: + return -RT_ERROR; + } + + return RT_EOK; +} + +const static struct rt_led_ops gpio_led_ops = +{ + .set_state = gpio_led_set_state, + .get_state = gpio_led_get_state, +}; + +static rt_err_t ofw_append_gpio_led(struct rt_ofw_node *np) +{ + rt_err_t err; + enum rt_led_state led_state = RT_LED_S_OFF; + const char *propname, *state, *trigger; + struct gpio_led *gled = rt_malloc(sizeof(*gled)); + + if (!gled) + { + return -RT_ENOMEM; + } + + gled->pin = rt_ofw_get_named_pin(np, RT_NULL, 0, RT_NULL, &gled->active_val); + + if (gled->pin < 0) + { + err = gled->pin; + + goto _fail; + } + + gled->parent.ops = &gpio_led_ops; + + if ((err = rt_hw_led_register(&gled->parent))) + { + goto _fail; + } + + if (!rt_ofw_prop_read_string(np, "default-state", &state)) + { + if (!rt_strcmp(state, "on")) + { + led_state = RT_LED_S_ON; + } + } + + if ((propname = rt_ofw_get_prop_fuzzy_name(np, "default-trigger$"))) + { + if (!rt_ofw_prop_read_string(np, propname, &trigger)) + { + if (!rt_strcmp(trigger, "heartbeat") || + !rt_strcmp(trigger, "timer")) + { + led_state = RT_LED_S_BLINK; + } + } + } + + rt_led_set_state(&gled->parent, led_state); + + rt_ofw_data(np) = &gled->parent; + + return RT_EOK; + +_fail: + rt_free(gled); + + return err; +} + +static rt_err_t gpio_led_probe(struct rt_platform_device *pdev) +{ + rt_bool_t pinctrl_apply = RT_FALSE; + struct rt_ofw_node *led_np, *np = pdev->parent.ofw_node; + + if (rt_ofw_prop_read_bool(np, "pinctrl-0")) + { + pinctrl_apply = RT_TRUE; + rt_pin_ctrl_confs_apply_by_name(&pdev->parent, RT_NULL); + } + + rt_ofw_foreach_available_child_node(np, led_np) + { + rt_err_t err = ofw_append_gpio_led(led_np); + + if (err == -RT_ENOMEM) + { + rt_ofw_node_put(led_np); + + return err; + } + else if (err) + { + LOG_E("%s: create LED fail", rt_ofw_node_full_name(led_np)); + continue; + } + + if (!pinctrl_apply) + { + struct rt_device dev_tmp; + + dev_tmp.ofw_node = led_np; + rt_pin_ctrl_confs_apply_by_name(&dev_tmp, RT_NULL); + } + } + + return RT_EOK; +} + +static rt_err_t gpio_led_remove(struct rt_platform_device *pdev) +{ + struct gpio_led *gled; + struct rt_led_device *led_dev; + struct rt_ofw_node *led_np, *np = pdev->parent.ofw_node; + + rt_ofw_foreach_available_child_node(np, led_np) + { + led_dev = rt_ofw_data(led_np); + + if (!led_dev) + { + continue; + } + + gled = rt_container_of(led_dev, struct gpio_led, parent); + + rt_ofw_data(led_np) = RT_NULL; + + rt_hw_led_unregister(&gled->parent); + + rt_free(gled); + } + + return RT_EOK; +} + +static const struct rt_ofw_node_id gpio_led_ofw_ids[] = +{ + { .compatible = "gpio-leds" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver gpio_led_driver = +{ + .name = "led-gpio", + .ids = gpio_led_ofw_ids, + + .probe = gpio_led_probe, + .remove = gpio_led_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(gpio_led_driver); diff --git a/rt-thread/components/drivers/led/led.c b/rt-thread/components/drivers/led/led.c new file mode 100644 index 0000000..611a16d --- /dev/null +++ b/rt-thread/components/drivers/led/led.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include + +#define DBG_TAG "rtdm.led" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +struct blink_timer +{ + rt_bool_t toggle; + rt_bool_t enabled; + struct rt_timer timer; +}; + +static struct rt_dm_ida led_ida = RT_DM_IDA_INIT(LED); + +static const char * const _led_states[] = +{ + [RT_LED_S_OFF] = "off", + [RT_LED_S_ON] = "on", + [RT_LED_S_TOGGLE] = "toggle", + [RT_LED_S_BLINK] = "blink", +}; + +static rt_ssize_t _led_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + rt_ssize_t res; + rt_size_t state_len; + enum rt_led_state state; + struct rt_led_device *led = rt_container_of(dev, struct rt_led_device, parent); + + if ((res = rt_led_get_state(led, &state))) + { + return res; + } + + state_len = rt_strlen(_led_states[state]); + + if (pos < state_len) + { + size = rt_min_t(rt_size_t, size, size - pos); + ((char *)buffer)[size - 1] = '\0'; + rt_strncpy(buffer, &_led_states[state][pos], size); + + return size; + } + else + { + return 0; + } +} + +static rt_ssize_t _led_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + rt_uint32_t brightness = 0; + const char *value = buffer; + struct rt_led_device *led = rt_container_of(dev, struct rt_led_device, parent); + + for (int i = 0; i < RT_ARRAY_SIZE(_led_states); ++i) + { + if (!rt_strncpy((char *)_led_states[i], buffer, size)) + { + return rt_led_set_state(led, i) ? : size; + } + } + + while (*value) + { + if (*value < '0' || *value > '9') + { + return -RT_EINVAL; + } + + brightness *= 10; + brightness += *value - '0'; + + ++value; + } + + rt_led_set_brightness(led, brightness); + + return size; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops _led_ops = +{ + .read = _led_read, + .write = _led_write, +}; +#endif + +static void _led_blink_timerout(void *param) +{ + struct rt_led_device *led = param; + struct blink_timer *btimer = led->sysdata; + + if (btimer->toggle) + { + led->ops->set_state(led, RT_LED_S_OFF); + } + else + { + led->ops->set_state(led, RT_LED_S_ON); + } + + btimer->toggle = !btimer->toggle; +} + +rt_err_t rt_hw_led_register(struct rt_led_device *led) +{ + rt_err_t err; + int device_id; + const char *dev_name; + struct blink_timer *btimer = RT_NULL; + + if (!led || !led->ops) + { + return -RT_EINVAL; + } + + if ((device_id = rt_dm_ida_alloc(&led_ida)) < 0) + { + return -RT_EFULL; + } + + rt_dm_dev_set_name(&led->parent, "led%u", device_id); + dev_name = rt_dm_dev_get_name(&led->parent); + + led->sysdata = RT_NULL; + rt_spin_lock_init(&led->spinlock); + + if (!led->ops->set_period && led->ops->set_state) + { + btimer = rt_malloc(sizeof(*btimer)); + + if (!btimer) + { + LOG_E("%s create blink timer failed", dev_name); + + err = -RT_ENOMEM; + goto _fail; + } + + led->sysdata = btimer; + + btimer->toggle = RT_FALSE; + btimer->enabled = RT_FALSE; + rt_timer_init(&btimer->timer, dev_name, _led_blink_timerout, led, + rt_tick_from_millisecond(500), RT_TIMER_FLAG_PERIODIC); + } + + led->parent.type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + led->parent.ops = &_led_ops; +#else + led->parent.read = _led_read; + led->parent.write = _led_write; +#endif + led->parent.master_id = led_ida.master_id; + led->parent.device_id = device_id; + + if ((err = rt_device_register(&led->parent, dev_name, RT_DEVICE_FLAG_RDWR))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_dm_ida_free(&led_ida, device_id); + + if (btimer) + { + rt_timer_detach(&btimer->timer); + rt_free(btimer); + + led->sysdata = RT_NULL; + } + + return err; +} + +rt_err_t rt_hw_led_unregister(struct rt_led_device *led) +{ + if (!led) + { + return -RT_EINVAL; + } + + rt_led_set_state(led, RT_LED_S_OFF); + + if (led->sysdata) + { + struct blink_timer *btimer = led->sysdata; + + rt_timer_detach(&btimer->timer); + + rt_free(btimer); + } + + rt_dm_ida_free(&led_ida, led->parent.device_id); + + rt_device_unregister(&led->parent); + + return RT_EOK; +} + +rt_err_t rt_led_set_state(struct rt_led_device *led, enum rt_led_state state) +{ + rt_err_t err; + struct blink_timer *btimer; + + if (!led) + { + return -RT_EINVAL; + } + + if (!led->ops->set_state) + { + return -RT_ENOSYS; + } + + rt_spin_lock(&led->spinlock); + + btimer = led->sysdata; + + if (btimer && btimer->enabled) + { + rt_timer_stop(&btimer->timer); + } + + err = led->ops->set_state(led, state); + + if (state == RT_LED_S_BLINK) + { + if (err == -RT_ENOSYS && btimer && !btimer->enabled) + { + btimer->enabled = RT_TRUE; + rt_timer_start(&btimer->timer); + } + } + else if (btimer && btimer->enabled) + { + if (err) + { + rt_timer_start(&btimer->timer); + } + else + { + btimer->enabled = RT_FALSE; + } + } + + rt_spin_unlock(&led->spinlock); + + return err; +} + +rt_err_t rt_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state) +{ + rt_err_t err; + + if (!led || !out_state) + { + return -RT_EINVAL; + } + + if (!led->ops->get_state) + { + return -RT_ENOSYS; + } + + rt_spin_lock(&led->spinlock); + + err = led->ops->get_state(led, out_state); + + rt_spin_unlock(&led->spinlock); + + return err; +} + +rt_err_t rt_led_set_period(struct rt_led_device *led, rt_uint32_t period_ms) +{ + rt_err_t err; + + if (!led) + { + return -RT_EINVAL; + } + + if (!led->ops->set_period && !led->sysdata) + { + return -RT_ENOSYS; + } + + rt_spin_lock(&led->spinlock); + + if (led->ops->set_period) + { + err = led->ops->set_period(led, period_ms); + } + else + { + struct blink_timer *btimer = led->sysdata; + rt_tick_t tick = rt_tick_from_millisecond(period_ms); + + err = rt_timer_control(&btimer->timer, RT_TIMER_CTRL_SET_TIME, &tick); + } + + rt_spin_unlock(&led->spinlock); + + return err; +} + +rt_err_t rt_led_set_brightness(struct rt_led_device *led, rt_uint32_t brightness) +{ + rt_err_t err; + + if (!led) + { + return -RT_EINVAL; + } + + if (!led->ops->set_brightness) + { + return -RT_ENOSYS; + } + + rt_spin_lock(&led->spinlock); + + err = led->ops->set_brightness(led, brightness); + + rt_spin_unlock(&led->spinlock); + + return err; +} diff --git a/rt-thread/components/drivers/mailbox/Kconfig b/rt-thread/components/drivers/mailbox/Kconfig new file mode 100644 index 0000000..9531d3a --- /dev/null +++ b/rt-thread/components/drivers/mailbox/Kconfig @@ -0,0 +1,14 @@ +menuconfig RT_USING_MBOX + bool "Using Hardware Mailbox device drivers" + depends on RT_USING_DM + depends on RT_USING_OFW + default n + +config RT_MBOX_PIC + bool "RT-Thread PIC Mailbox" + depends on RT_USING_MBOX + default y + +if RT_USING_MBOX + osource "$(SOC_DM_MBOX_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/mailbox/SConscript b/rt-thread/components/drivers/mailbox/SConscript new file mode 100644 index 0000000..f26a44b --- /dev/null +++ b/rt-thread/components/drivers/mailbox/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_MBOX']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['mailbox.c'] + +if GetDepend(['RT_MBOX_PIC']): + src += ['mailbox-pic.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/mailbox/mailbox-pic.c b/rt-thread/components/drivers/mailbox/mailbox-pic.c new file mode 100644 index 0000000..dbc90f0 --- /dev/null +++ b/rt-thread/components/drivers/mailbox/mailbox-pic.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "mailbox.pic" +#define DBG_LVL DBG_INFO +#include + +/* + * RT-Thread PIC Mailbox device driver + * + * The mailbox device(s) may be instantiated in one of three equivalent way: + * + * Device Tree node, eg.: + * + * interrupt-controller@0 { + * interrupt-controller; + * #interrupt-cells = <1>; + * }; + * + * pic_mailbox@10000 { + * compatible = "rt-thread,pic-mailbox"; + * reg = <0x10000 0x100>; + * position = <0>; + * interrupts = <34>; + * peer-interrupts = <35>; + * uid = <0>; + * #mbox-cells = <1>; + * }; + */ + +#define MAILBOX_IMASK 0x00 +#define MAILBOX_ISTATE 0x04 +#define MAILBOX_MSG(n) (0x08 + (n) * 4) + +struct pic_mbox +{ + struct rt_mbox_controller parent; + + void *regs; + void *peer_regs; + + int position; + int chans_nr; + int irq; + int peer_hwirq; + struct rt_pic *pic; + + struct rt_spinlock lock; +}; + +#define raw_to_pic_mbox(raw) rt_container_of(raw, struct pic_mbox, parent) + +static rt_err_t pic_mbox_request(struct rt_mbox_chan *chan) +{ + int index = chan - chan->ctrl->chans; + struct pic_mbox *pic_mbox = raw_to_pic_mbox(chan->ctrl); + + HWREG32(pic_mbox->regs + MAILBOX_IMASK) &= ~RT_BIT(index); + HWREG32(pic_mbox->regs + MAILBOX_ISTATE) = 0; + + return RT_EOK; +} + +static void pic_mbox_release(struct rt_mbox_chan *chan) +{ + int index = chan - chan->ctrl->chans; + struct pic_mbox *pic_mbox = raw_to_pic_mbox(chan->ctrl); + + HWREG32(pic_mbox->regs + MAILBOX_IMASK) |= RT_BIT(index); +} + +static rt_err_t pic_mbox_send(struct rt_mbox_chan *chan, const void *data) +{ + rt_ubase_t level; + int index = chan - chan->ctrl->chans; + struct pic_mbox *pic_mbox = raw_to_pic_mbox(chan->ctrl); + + while (HWREG32(pic_mbox->peer_regs + MAILBOX_ISTATE) & RT_BIT(index)) + { + rt_thread_yield(); + } + + if (HWREG32(pic_mbox->peer_regs + MAILBOX_IMASK) & RT_BIT(index)) + { + return -RT_ERROR; + } + + level = rt_spin_lock_irqsave(&pic_mbox->lock); + + HWREG32(pic_mbox->regs + MAILBOX_MSG(index)) = *(rt_uint32_t *)data; + HWREG32(pic_mbox->peer_regs + MAILBOX_ISTATE) |= RT_BIT(index); + rt_hw_wmb(); + + rt_pic_irq_set_state_raw(pic_mbox->pic, pic_mbox->peer_hwirq, + RT_IRQ_STATE_PENDING, RT_TRUE); + + rt_spin_unlock_irqrestore(&pic_mbox->lock, level); + + return RT_EOK; +} + +static const struct rt_mbox_controller_ops pic_mbox_ops = +{ + .request = pic_mbox_request, + .release = pic_mbox_release, + .send = pic_mbox_send, +}; + +static void pic_mbox_isr(int irqno, void *param) +{ + rt_uint32_t isr; + struct pic_mbox *pic_mbox = param; + + isr = HWREG32(pic_mbox->regs + MAILBOX_ISTATE); + + for (int idx = 0; idx < 32; ++idx) + { + rt_uint32_t msg; + + if (!(RT_BIT(idx) & isr)) + { + continue; + } + + rt_hw_rmb(); + msg = HWREG32(pic_mbox->peer_regs + MAILBOX_MSG(idx)); + + rt_mbox_recv(&pic_mbox->parent.chans[idx], &msg); + } + + HWREG32(pic_mbox->regs + MAILBOX_ISTATE) &= ~isr; +} + +static void pic_mbox_free_resource(struct pic_mbox *pic_mbox) +{ + if (pic_mbox->regs && pic_mbox->peer_regs) + { + if (pic_mbox->peer_regs > pic_mbox->regs) + { + rt_iounmap(pic_mbox->regs); + } + else + { + rt_iounmap(pic_mbox->peer_regs); + } + } + + rt_free(pic_mbox); +} + +static rt_err_t pic_mbox_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint64_t size; + rt_uint32_t value; + char dev_name[RT_NAME_MAX]; + struct rt_ofw_node *pic_np; + struct rt_device *dev = &pdev->parent; + struct pic_mbox *pic_mbox = rt_calloc(1, sizeof(*pic_mbox)); + + if (!pic_mbox) + { + return -RT_ENOMEM; + } + + if ((err = rt_dm_dev_get_address(dev, 0, RT_NULL, &size))) + { + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "position", &value))) + { + goto _fail; + } + + if (!value) + { + pic_mbox->regs = rt_dm_dev_iomap(dev, 0); + + if (!pic_mbox->regs) + { + goto _fail; + } + + pic_mbox->peer_regs = pic_mbox->regs + size / 2; + + /* Init by the captain */ + HWREG32(pic_mbox->regs + MAILBOX_IMASK) = 0xffffffff; + HWREG32(pic_mbox->regs + MAILBOX_ISTATE) = 0; + HWREG32(pic_mbox->peer_regs + MAILBOX_IMASK) = 0xffffffff; + HWREG32(pic_mbox->peer_regs + MAILBOX_ISTATE) = 0; + } + else + { + pic_mbox->peer_regs = rt_dm_dev_iomap(dev, 0); + + if (!pic_mbox->peer_regs) + { + goto _fail; + } + + pic_mbox->regs = pic_mbox->peer_regs + size / 2; + } + + pic_mbox->irq = rt_dm_dev_get_irq(dev, 0); + + if (pic_mbox->irq < 0) + { + err = pic_mbox->irq; + + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "peer-interrupts", &value))) + { + goto _fail; + } + pic_mbox->peer_hwirq = value; + + if ((err = rt_dm_dev_prop_read_u32(dev, "uid", &value))) + { + goto _fail; + } + + if (!(pic_np = rt_ofw_find_irq_parent(dev->ofw_node, RT_NULL))) + { + goto _fail; + } + pic_mbox->pic = rt_ofw_data(pic_np); + rt_ofw_node_put(pic_np); + + rt_spin_lock_init(&pic_mbox->lock); + + pic_mbox->parent.dev = dev; + pic_mbox->parent.num_chans = 32; + pic_mbox->parent.ops = &pic_mbox_ops; + + if ((err = rt_mbox_controller_register(&pic_mbox->parent))) + { + goto _fail; + } + + rt_snprintf(dev_name, sizeof(dev_name), "pic-mbox%d", value); + rt_hw_interrupt_install(pic_mbox->irq, pic_mbox_isr, pic_mbox, dev_name); + rt_hw_interrupt_umask(pic_mbox->irq); + + return RT_EOK; + +_fail: + pic_mbox_free_resource(pic_mbox); + + return err; +} + +static rt_err_t pic_mbox_remove(struct rt_platform_device *pdev) +{ + struct pic_mbox *pic_mbox = pdev->parent.user_data; + + rt_pic_detach_irq(pic_mbox->irq, pic_mbox); + + rt_mbox_controller_unregister(&pic_mbox->parent); + + pic_mbox_free_resource(pic_mbox); + + return RT_EOK; +} + +static const struct rt_ofw_node_id pic_mbox_ofw_ids[] = +{ + { .compatible = "rt-thread,pic-mailbox" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver pic_mbox_driver = +{ + .name = "mailbox-pic", + .ids = pic_mbox_ofw_ids, + + .probe = pic_mbox_probe, + .remove = pic_mbox_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(pic_mbox_driver); diff --git a/rt-thread/components/drivers/mailbox/mailbox.c b/rt-thread/components/drivers/mailbox/mailbox.c new file mode 100644 index 0000000..5e94241 --- /dev/null +++ b/rt-thread/components/drivers/mailbox/mailbox.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.mailbox" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include + +static struct rt_spinlock mbox_ops_lock = {}; +static rt_list_t mbox_nodes = RT_LIST_OBJECT_INIT(mbox_nodes); + +static void mbox_chan_timeout(void *param); + +rt_err_t rt_mbox_controller_register(struct rt_mbox_controller *ctrl) +{ + int len; + struct rt_mbox_chan *chan; + char timer_name[RT_NAME_MAX]; + + if (!ctrl || !ctrl->dev || !ctrl->ops || !ctrl->num_chans) + { + return -RT_EINVAL; + } + + ctrl->chans = rt_calloc(ctrl->num_chans, sizeof(struct rt_mbox_chan)); + + if (!ctrl->chans) + { + return -RT_ENOMEM; + } + + len = rt_snprintf(timer_name, sizeof(timer_name), "%s-", + rt_dm_dev_get_name(ctrl->dev)); + + RT_ASSERT(len < sizeof(timer_name)); + + chan = &ctrl->chans[0]; + + for (int i = 0; i < ctrl->num_chans; ++i, ++chan) + { + chan->ctrl = ctrl; + rt_spin_lock_init(&chan->lock); + + rt_snprintf(&timer_name[len], sizeof(timer_name) - len, "%d", i); + rt_timer_init(&chan->timer, timer_name, mbox_chan_timeout, chan, + 0, RT_TIMER_FLAG_ONE_SHOT); + } + + rt_list_init(&ctrl->list); + rt_dm_dev_bind_fwdata(ctrl->dev, RT_NULL, ctrl); + + rt_spin_lock(&mbox_ops_lock); + + rt_list_insert_after(&mbox_nodes, &ctrl->list); + + rt_spin_unlock(&mbox_ops_lock); + + return RT_EOK; +} + +rt_err_t rt_mbox_controller_unregister(struct rt_mbox_controller *ctrl) +{ + struct rt_mbox_chan *chan; + + if (!ctrl) + { + return -RT_EINVAL; + } + + rt_spin_lock(&mbox_ops_lock); + + rt_dm_dev_unbind_fwdata(ctrl->dev, RT_NULL); + rt_list_remove(&ctrl->list); + + rt_spin_unlock(&mbox_ops_lock); + + chan = &ctrl->chans[0]; + + for (int i = ctrl->num_chans - 1; i >= 0; --i, ++chan) + { + rt_mbox_release(&ctrl->chans[i]); + } + + rt_free(ctrl->chans); + + return RT_EOK; +} + +rt_err_t rt_mbox_send(struct rt_mbox_chan *chan, const void *data, + rt_uint32_t timeout_ms) +{ + rt_err_t err; + rt_ubase_t level; + rt_bool_t timer_go = RT_FALSE; + struct rt_mbox_client *client; + struct rt_mbox_controller *ctrl; + + if (!chan || !data) + { + return -RT_EINVAL; + } + + ctrl = chan->ctrl; + client = chan->client; + + level = rt_spin_lock_irqsave(&chan->lock); + + if (client->tx_prepare) + { + client->tx_prepare(client, data); + } + + chan->complete = RT_FALSE; + err = ctrl->ops->send(chan, data); + + if (!err) + { + chan->data = (void *)data; + + if (timeout_ms != RT_WAITING_FOREVER) + { + rt_tick_t tick = rt_tick_from_millisecond(timeout_ms); + + rt_timer_control(&chan->timer, RT_TIMER_CTRL_SET_TIME, &tick); + + timer_go = RT_TRUE; + } + } + else + { + chan->complete = RT_TRUE; + } + + rt_spin_unlock_irqrestore(&chan->lock, level); + + if (timer_go) + { + rt_timer_start(&chan->timer); + } + + return err; +} + +void rt_mbox_send_done(struct rt_mbox_chan *chan, rt_err_t err) +{ + void *data; + rt_ubase_t level; + + level = rt_spin_lock_irqsave(&chan->lock); + + data = chan->data; + chan->data = RT_NULL; + + rt_spin_unlock_irqrestore(&chan->lock, level); + + if (chan->client->tx_done) + { + chan->client->tx_done(chan->client, data, err); + } + + chan->complete = RT_TRUE; +} + +static void mbox_chan_timeout(void *param) +{ + rt_err_t err = RT_EOK; + struct rt_mbox_chan *chan = param; + + if (!chan->complete) + { + err = -RT_ETIMEOUT; + } + + rt_mbox_send_done(chan, err); +} + +rt_bool_t rt_mbox_peek(struct rt_mbox_chan *chan) +{ + if (chan && chan->ctrl->ops->peek) + { + return chan->ctrl->ops->peek(chan); + } + + return RT_FALSE; +} + +rt_err_t rt_mbox_recv(struct rt_mbox_chan *chan, void *data) +{ + if (!chan || !data) + { + return -RT_EINVAL; + } + + if (chan->client->rx_callback) + { + chan->client->rx_callback(chan->client, data); + } + + return RT_EOK; +} + +static int mbox_controller_ofw_parse_default(struct rt_mbox_controller *ctrl, + struct rt_ofw_cell_args *args) +{ + if (args->args_count != 1) + { + return -RT_EINVAL; + } + + return args->args[0]; +} + +struct rt_mbox_chan *rt_mbox_request_by_index(struct rt_mbox_client *client, int index) +{ + rt_err_t err; + struct rt_ofw_cell_args args; + struct rt_ofw_node *np, *ctrl_np; + struct rt_mbox_controller *ctrl; + struct rt_mbox_chan *chan = RT_NULL; + + if (!client && index < 0) + { + return rt_err_ptr(-RT_EINVAL); + } + + np = client->dev->ofw_node; + + rt_spin_lock(&mbox_ops_lock); + + err = rt_ofw_parse_phandle_cells(np, "mboxes", "#mbox-cells", index, &args); + + if (err) + { + chan = rt_err_ptr(err); + goto _out_lock; + } + + ctrl_np = args.data; + + if (!rt_ofw_data(ctrl_np)) + { + rt_spin_unlock(&mbox_ops_lock); + + rt_platform_ofw_request(ctrl_np); + + rt_spin_lock(&mbox_ops_lock); + } + + ctrl = rt_ofw_data(ctrl_np); + rt_ofw_node_put(ctrl_np); + + if (ctrl) + { + int index; + + if (ctrl->ops->ofw_parse) + { + index = ctrl->ops->ofw_parse(ctrl, &args); + } + else + { + index = mbox_controller_ofw_parse_default(ctrl, &args); + } + + if (index >= 0) + { + chan = &ctrl->chans[index]; + } + else + { + LOG_E("Parse chan from %s error = %s", + rt_dm_dev_get_name(ctrl->dev), rt_strerror(index)); + + chan = rt_err_ptr(index); + goto _out_lock; + } + + if (ctrl->ops->request) + { + rt_err_t err = ctrl->ops->request(chan); + + if (err) + { + LOG_E("Request chan[%d] from %s error = %s", + index, rt_dm_dev_get_name(ctrl->dev), rt_strerror(err)); + + rt_mbox_release(chan); + chan = rt_err_ptr(err); + } + } + + chan->client = client; + } + else + { + chan = rt_err_ptr(-RT_ENOSYS); + } + +_out_lock: + rt_spin_unlock(&mbox_ops_lock); + + return chan; +} + +struct rt_mbox_chan *rt_mbox_request_by_name(struct rt_mbox_client *client, char *name) +{ + int index; + struct rt_ofw_node *np; + + if (!client || !name) + { + return rt_err_ptr(-RT_EINVAL); + } + + np = client->dev->ofw_node; + index = rt_ofw_prop_index_of_string(np, "mbox-names", name); + + if (index < 0) + { + return RT_NULL; + } + + return rt_mbox_request_by_index(client, index); +} + +rt_err_t rt_mbox_release(struct rt_mbox_chan *chan) +{ + if (chan) + { + chan->ctrl->ops->release(chan); + } + else + { + return -RT_EINVAL; + } + + return RT_EOK; +} diff --git a/rt-thread/components/drivers/mfd/Kconfig b/rt-thread/components/drivers/mfd/Kconfig new file mode 100644 index 0000000..0b12ca4 --- /dev/null +++ b/rt-thread/components/drivers/mfd/Kconfig @@ -0,0 +1,10 @@ +menuconfig RT_USING_MFD + bool "Using Multifunction device drivers" + depends on RT_USING_DM + default n + +config RT_MFD_SYSCON + bool "System Controller Register R/W" + depends on RT_USING_MFD + depends on RT_USING_OFW + default y diff --git a/rt-thread/components/drivers/mfd/SConscript b/rt-thread/components/drivers/mfd/SConscript new file mode 100644 index 0000000..f952f68 --- /dev/null +++ b/rt-thread/components/drivers/mfd/SConscript @@ -0,0 +1,17 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_MFD']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] +src = [] + +if GetDepend(['RT_MFD_SYSCON']): + src += ['mfd-syscon.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/mfd/mfd-syscon.c b/rt-thread/components/drivers/mfd/mfd-syscon.c new file mode 100644 index 0000000..1778906 --- /dev/null +++ b/rt-thread/components/drivers/mfd/mfd-syscon.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#include +#include +#include +#include +#include + +static struct rt_spinlock _syscon_nodes_lock = { 0 }; +static rt_list_t _syscon_nodes = RT_LIST_OBJECT_INIT(_syscon_nodes); + +rt_err_t rt_syscon_read(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t *out_val) +{ + if (offset < syscon->iomem_size) + { + rt_ubase_t level = rt_spin_lock_irqsave(&syscon->rw_lock); + + *out_val = HWREG32(syscon->iomem_base + offset); + + rt_spin_unlock_irqrestore(&syscon->rw_lock, level); + + return RT_EOK; + } + else + { + return -RT_EINVAL; + } +} + +rt_err_t rt_syscon_write(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t val) +{ + if (offset < syscon->iomem_size) + { + rt_ubase_t level = rt_spin_lock_irqsave(&syscon->rw_lock); + + HWREG32(syscon->iomem_base + offset) = val; + + rt_spin_unlock_irqrestore(&syscon->rw_lock, level); + + return RT_EOK; + } + else + { + return -RT_EINVAL; + } +} + +rt_err_t rt_syscon_update_bits(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t mask, rt_uint32_t val) +{ + rt_err_t err; + rt_ubase_t level = rt_spin_lock_irqsave(&syscon->rw_lock); + + if (offset < syscon->iomem_size) + { + rt_uint32_t old_val = HWREG32(syscon->iomem_base + offset); + + old_val &= ~mask; + + HWREG32(syscon->iomem_base + offset) = old_val | val; + + err = RT_EOK; + } + else + { + err = -RT_EINVAL; + } + + rt_spin_unlock_irqrestore(&syscon->rw_lock, level); + + return err; +} + +static rt_err_t syscon_probe(struct rt_platform_device *pdev); + +struct rt_syscon *rt_syscon_find_by_ofw_node(struct rt_ofw_node *np) +{ + rt_ubase_t level; + struct rt_syscon *syscon = RT_NULL, *syscon_tmp; + struct rt_platform_device syscon_pdev; + + if (!np) + { + goto _exit; + } + + level = rt_spin_lock_irqsave(&_syscon_nodes_lock); + + /* ofw_data is not safety */ + rt_list_for_each_entry(syscon_tmp, &_syscon_nodes, list) + { + if (syscon_tmp->np == np) + { + syscon = syscon_tmp; + break; + } + } + + rt_spin_unlock_irqrestore(&_syscon_nodes_lock, level); + + if (syscon) + { + goto _exit; + } + + /* Not found, try probe this node */ + if (!rt_ofw_node_is_compatible(np, "syscon") && + !rt_ofw_node_is_compatible(np, "simple-mfd")) + { + goto _exit; + } + + syscon_pdev.parent.ofw_node = np; + + if (!syscon_probe(&syscon_pdev)) + { + syscon = rt_ofw_data(np); + } + +_exit: + return syscon; +} + +struct rt_syscon *rt_syscon_find_by_ofw_compatible(const char *compatible) +{ + struct rt_syscon *syscon = RT_NULL; + struct rt_ofw_node *syscon_np = rt_ofw_find_node_by_compatible(RT_NULL, compatible); + + if (syscon_np) + { + syscon = rt_syscon_find_by_ofw_node(syscon_np); + + rt_ofw_node_put(syscon_np); + } + + return syscon; +} + +struct rt_syscon *rt_syscon_find_by_ofw_phandle(struct rt_ofw_node *np, const char *propname) +{ + struct rt_syscon *syscon = RT_NULL; + struct rt_ofw_node *syscon_np = rt_ofw_parse_phandle(np, propname, 0); + + if (syscon_np) + { + syscon = rt_syscon_find_by_ofw_node(syscon_np); + + rt_ofw_node_put(syscon_np); + } + + return syscon; +} + +static rt_err_t syscon_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_ubase_t level; + struct rt_ofw_node *np; + rt_uint64_t iomem_range[2]; + struct rt_syscon *syscon = rt_calloc(1, sizeof(*syscon)); + + if (!syscon) + { + return -RT_ENOMEM; + } + + np = pdev->parent.ofw_node; + + if ((err = rt_ofw_get_address(np, 0, &iomem_range[0], &iomem_range[1]))) + { + goto _fail; + } + + syscon->iomem_size = (rt_size_t)iomem_range[1]; + syscon->iomem_base = rt_ioremap((void *)iomem_range[0], syscon->iomem_size); + + if (!syscon->iomem_base) + { + goto _fail; + } + + rt_list_init(&syscon->list); + level = rt_spin_lock_irqsave(&_syscon_nodes_lock); + rt_list_insert_after(&_syscon_nodes, &syscon->list); + rt_spin_unlock_irqrestore(&_syscon_nodes_lock, level); + + rt_spin_lock_init(&syscon->rw_lock); + + pdev->parent.user_data = syscon; + + syscon->np = pdev->parent.ofw_node; + rt_ofw_data(np) = syscon; + + return RT_EOK; + +_fail: + rt_free(syscon); + + return err; +} + +static rt_err_t syscon_remove(struct rt_platform_device *pdev) +{ + struct rt_syscon *syscon = pdev->parent.user_data; + + rt_iounmap(syscon->iomem_base); + + rt_free(syscon); + + return RT_EOK; +} + +static const struct rt_ofw_node_id syscon_ofw_ids[] = +{ + { .compatible = "syscon" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver syscon_driver = +{ + .name = "mfd-syscon", + .ids = syscon_ofw_ids, + + .probe = syscon_probe, + .remove = syscon_remove, +}; + +static int syscon_drv_register(void) +{ + rt_platform_driver_register(&syscon_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(syscon_drv_register); diff --git a/rt-thread/components/drivers/misc/rt_random.c b/rt-thread/components/drivers/misc/rt_random.c index da6b312..9f4be20 100644 --- a/rt-thread/components/drivers/misc/rt_random.c +++ b/rt-thread/components/drivers/misc/rt_random.c @@ -46,7 +46,7 @@ static rt_ssize_t random_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_si static rt_ssize_t random_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) { - ssize_t ret = sizeof(seed); + ssize_t ret = sizeof(seed) < size ? sizeof(seed) : size; rt_memcpy(&seed, buffer, ret); return ret; } @@ -137,7 +137,7 @@ static rt_ssize_t random_uread(rt_device_t dev, rt_off_t pos, void *buffer, rt_s static rt_ssize_t random_uwrite(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) { - ssize_t ret = sizeof(useed); + ssize_t ret = sizeof(useed) < size ? sizeof(useed) : size; rt_memcpy(&useed, buffer, ret); return ret; } diff --git a/rt-thread/components/drivers/nvme/Kconfig b/rt-thread/components/drivers/nvme/Kconfig new file mode 100644 index 0000000..83c731c --- /dev/null +++ b/rt-thread/components/drivers/nvme/Kconfig @@ -0,0 +1,23 @@ +menuconfig RT_USING_NVME + bool "Using Non-Volatile Memory Express (NVME) device drivers" + depends on RT_USING_DM + depends on RT_USING_BLK + depends on RT_USING_DMA + default n + +config RT_USING_NVME_IO_QUEUE + int "Number of I/O Command queue" + depends on RT_USING_NVME + default 2 if RT_THREAD_PRIORITY_8 + default 4 if RT_THREAD_PRIORITY_32 + default 8 if RT_THREAD_PRIORITY_256 + +config RT_NVME_PCI + bool "NVME support on PCI bus" + depends on RT_USING_NVME + depends on RT_USING_PCI + default y + +if RT_USING_NVME + osource "$(SOC_DM_NVME_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/nvme/SConscript b/rt-thread/components/drivers/nvme/SConscript new file mode 100644 index 0000000..6fc699b --- /dev/null +++ b/rt-thread/components/drivers/nvme/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_NVME']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['nvme.c'] + +if GetDepend(['RT_NVME_PCI']): + src += ['nvme-pci.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/nvme/nvme-pci.c b/rt-thread/components/drivers/nvme/nvme-pci.c new file mode 100644 index 0000000..d73260f --- /dev/null +++ b/rt-thread/components/drivers/nvme/nvme-pci.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#define NVME_REG_BAR 0 + +struct pci_nvme_quirk +{ + const struct rt_nvme_ops *ops; +}; + +struct pci_nvme_controller +{ + struct rt_nvme_controller parent; + const struct pci_nvme_quirk *quirk; + + rt_bool_t is_msi; + struct rt_pci_msix_entry msix_entries[RT_USING_NVME_QUEUE]; +}; + +static const struct rt_nvme_ops pci_nvme_std_ops = +{ + .name = "PCI", +}; + +static rt_err_t pci_nvme_probe(struct rt_pci_device *pdev) +{ + rt_err_t err; + rt_ssize_t msi_nr; + struct rt_nvme_controller *nvme; + struct pci_nvme_controller *pci_nvme = rt_calloc(1, sizeof(*pci_nvme)); + const struct pci_nvme_quirk *quirk = pdev->id->data; + + if (!pci_nvme) + { + return -RT_ENOMEM; + } + + pci_nvme->quirk = quirk; + nvme = &pci_nvme->parent; + nvme->dev = &pdev->parent; + nvme->regs = rt_pci_iomap(pdev, NVME_REG_BAR); + + if (!nvme->regs) + { + err = -RT_EIO; + goto _fail; + } + + nvme->ops = quirk && quirk->ops ? quirk->ops : &pci_nvme_std_ops; + + if ((msi_nr = rt_pci_msix_vector_count(pdev)) <= 0) + { + msi_nr = rt_pci_msi_vector_count(pdev); + } + if (msi_nr > 0) + { + nvme->irqs_nr = RT_ARRAY_SIZE(pci_nvme->msix_entries); + nvme->irqs_nr = rt_min_t(rt_size_t, msi_nr, nvme->irqs_nr); + } + + if (nvme->irqs_nr > 0) + { + rt_pci_msix_entry_index_linear(pci_nvme->msix_entries, nvme->irqs_nr); + + if (rt_pci_msix_enable(pdev, pci_nvme->msix_entries, nvme->irqs_nr) > 0) + { + pci_nvme->is_msi = RT_TRUE; + + for (int i = 0; i < nvme->irqs_nr; ++i) + { + nvme->irqs[i] = pci_nvme->msix_entries[i].irq; + } + } + } + + if (!pci_nvme->is_msi) + { + nvme->irqs_nr = 1; + nvme->irqs[0] = pdev->irq; + rt_pci_irq_unmask(pdev); + } + + rt_pci_set_master(pdev); + + if ((err = rt_nvme_controller_register(nvme))) + { + goto _disable; + } + + pdev->parent.user_data = pci_nvme; + + return RT_EOK; + +_disable: + if (pci_nvme->is_msi) + { + rt_pci_msix_disable(pdev); + } + else + { + rt_pci_irq_mask(pdev); + } + rt_pci_clear_master(pdev); + rt_iounmap(nvme->regs); + +_fail: + rt_free(pci_nvme); + + return err; +} + +static rt_err_t pci_nvme_remove(struct rt_pci_device *pdev) +{ + struct rt_nvme_controller *nvme; + struct pci_nvme_controller *pci_nvme = pdev->parent.user_data; + + nvme = &pci_nvme->parent; + + rt_nvme_controller_unregister(nvme); + + if (pci_nvme->is_msi) + { + rt_pci_msix_disable(pdev); + } + else + { + /* INTx is shared, don't mask all */ + rt_hw_interrupt_umask(pdev->irq); + rt_pci_irq_mask(pdev); + } + + rt_pci_clear_master(pdev); + + rt_iounmap(nvme->regs); + rt_free(pci_nvme); + + return RT_EOK; +} + +static rt_err_t pci_nvme_shutdown(struct rt_pci_device *pdev) +{ + return pci_nvme_remove(pdev); +} + +static const struct rt_pci_device_id pci_nvme_ids[] = +{ + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0010) }, + { RT_PCI_DEVICE_CLASS(PCIS_STORAGE_EXPRESS, ~0) }, + { /* sentinel */ } +}; + +static struct rt_pci_driver pci_nvme_driver = +{ + .name = "nvme-pci", + + .ids = pci_nvme_ids, + .probe = pci_nvme_probe, + .remove = pci_nvme_remove, + .shutdown = pci_nvme_shutdown, +}; +RT_PCI_DRIVER_EXPORT(pci_nvme_driver); diff --git a/rt-thread/components/drivers/nvme/nvme.c b/rt-thread/components/drivers/nvme/nvme.c new file mode 100644 index 0000000..dc00714 --- /dev/null +++ b/rt-thread/components/drivers/nvme/nvme.c @@ -0,0 +1,1302 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "rtdm.nvme" +#define DBG_LVL DBG_INFO +#include + +static struct rt_dm_ida nvme_controller_ida = RT_DM_IDA_INIT(CUSTOM); +static struct rt_dm_ida nvme_ida = RT_DM_IDA_INIT(NVME); + +static struct rt_spinlock nvme_lock = {}; +static rt_list_t nvme_nodes = RT_LIST_OBJECT_INIT(nvme_nodes); + +rt_inline rt_uint32_t nvme_readl(struct rt_nvme_controller *nvme, int offset) +{ + return HWREG32(nvme->regs + offset); +} + +rt_inline void nvme_writel(struct rt_nvme_controller *nvme, int offset, rt_uint32_t value) +{ + HWREG32(nvme->regs + offset) = value; +} + +rt_inline rt_uint64_t nvme_readq(struct rt_nvme_controller *nvme, int offset) +{ + rt_uint32_t lo32, hi32; + + lo32 = HWREG32(nvme->regs + offset); + hi32 = HWREG32(nvme->regs + offset + 4); + + return ((rt_uint64_t)hi32 << 32) + lo32; +} + +rt_inline void nvme_writeq(struct rt_nvme_controller *nvme, int offset, rt_uint64_t value) +{ + nvme_writel(nvme, offset, (rt_uint32_t)(value & 0xffffffff)); + nvme_writel(nvme, offset + 4, (rt_uint32_t)(value >> 32)); +} + +static rt_err_t nvme_poll_csts(struct rt_nvme_controller *nvme, + rt_uint32_t mask, rt_uint32_t value) +{ + rt_tick_t timeout; + + timeout = rt_tick_from_millisecond(RT_NVME_CAP_TIMEOUT(nvme->cap) * 500); + timeout += rt_tick_get(); + + do { + if ((nvme_readl(nvme, RT_NVME_REG_CSTS) & mask) == value) + { + return RT_EOK; + } + + rt_hw_cpu_relax(); + } while (rt_tick_get() < timeout); + + return -RT_ETIMEOUT; +} + +static rt_err_t nvme_enable_ctrl(struct rt_nvme_controller *nvme) +{ + nvme->ctrl_config &= ~RT_NVME_CC_SHN_MASK; + nvme->ctrl_config |= RT_NVME_CC_ENABLE; + nvme_writel(nvme, RT_NVME_REG_CC, nvme->ctrl_config); + + return nvme_poll_csts(nvme, RT_NVME_CSTS_RDY, RT_NVME_CSTS_RDY); +} + +static rt_err_t nvme_disable_ctrl(struct rt_nvme_controller *nvme) +{ + nvme->ctrl_config &= ~RT_NVME_CC_SHN_MASK; + nvme->ctrl_config &= ~RT_NVME_CC_ENABLE; + nvme_writel(nvme, RT_NVME_REG_CC, nvme->ctrl_config); + + return nvme_poll_csts(nvme, RT_NVME_CSTS_RDY, 0); +} + +static rt_err_t nvme_shutdown_ctrl(struct rt_nvme_controller *nvme) +{ + nvme->ctrl_config &= ~RT_NVME_CC_SHN_MASK; + nvme->ctrl_config |= RT_NVME_CC_SHN_NORMAL; + nvme_writel(nvme, RT_NVME_REG_CC, nvme->ctrl_config); + + return nvme_poll_csts(nvme, RT_NVME_CSTS_SHST_MASK, RT_NVME_CSTS_SHST_CMPLT); +} + +rt_inline rt_le16_t nvme_next_cmdid(struct rt_nvme_controller *nvme) +{ + return rt_cpu_to_le16((rt_uint16_t)rt_atomic_add(&nvme->cmdid, 1)); +} + +static rt_err_t nvme_submit_cmd(struct rt_nvme_queue *queue, + struct rt_nvme_command *cmd) +{ + rt_ubase_t level; + rt_err_t err = RT_EOK; + rt_uint16_t tail, head; + struct rt_nvme_controller *nvme = queue->nvme; + +_retry: + level = rt_spin_lock_irqsave(&queue->lock); + + tail = queue->sq_tail; + head = queue->cq_head; + + if (tail + 1 == head) + { + /* IO queue is full, waiting for the last IO command to complete. */ + rt_spin_unlock_irqrestore(&queue->lock, level); + + rt_thread_yield(); + + goto _retry; + } + + cmd->common.cmdid = nvme_next_cmdid(nvme); + rt_memcpy(&queue->sq_cmds[tail], cmd, sizeof(*cmd)); + + if (nvme->ops->submit_cmd) + { + if ((err = nvme->ops->submit_cmd(queue, cmd))) + { + return err; + } + } + + if (++tail == queue->depth) + { + tail = 0; + } + HWREG32(queue->doorbell) = tail; + queue->sq_tail = tail; + + queue->cmd = cmd; + queue->err = RT_EOK; + + rt_spin_unlock_irqrestore(&queue->lock, level); + + err = rt_completion_wait(&queue->done, + rt_tick_from_millisecond(queue->qid != 0 ? RT_WAITING_FOREVER : 60)); + + return err ? : queue->err; +} + +static rt_err_t nvme_set_features_simple(struct rt_nvme_controller *nvme, + rt_uint32_t fid, rt_uint32_t dword11) +{ + struct rt_nvme_command cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.features.opcode = RT_NVME_ADMIN_OPCODE_SET_FEATURES; + cmd.features.fid = rt_cpu_to_le32(fid); + cmd.features.dword11 = rt_cpu_to_le32(dword11); + + return nvme_submit_cmd(&nvme->admin_queue, &cmd); +} + +static rt_err_t nvme_submit_io_cmd(struct rt_nvme_controller *nvme, + struct rt_nvme_command *cmd) +{ + rt_uint16_t qid; + + qid = rt_atomic_add(&nvme->ioqid[rt_hw_cpu_id()], RT_CPUS_NR); + qid %= nvme->io_queue_max; + + return nvme_submit_cmd(&nvme->io_queues[qid], cmd); +} + +/* + * PRP Mode: + * + * |63 n+1|n 0| + * +----------------------------------------+----------+---+---+ + * | Page Base Address | Offset | 0 | 0 | + * +----------------------------------------+----------+---+---+ + * | + * v + * Host Physical Pages + * +----------------------------+ + * +--------------+----------+ | Page k | + * | PRP Entry1 | Offset +---------->+----------------------------+ + * +--------------+----------+ | Page k + 1 | + * +----------------------------+ + * ... + * +----------------------------+ + * +--------------+----------+ | Page k + m | + * | PRP Entry2 | 0 +---------->+----------------------------+ + * +--------------+----------+ | Page k + m + 1 | + * +----------------------------+ + * PRP List (In PRP Entry2): + * + * |63 n+1|n 0| + * +----------------------------------------+------------------+ + * | Page Base Address k | 0h | + * +----------------------------------------+------------------+ + * | Page Base Address k + 1 | 0h | + * +----------------------------------------+------------------+ + * | ... | + * +----------------------------------------+------------------+ + * | Page Base Address k + m | 0h | + * +----------------------------------------+------------------+ + * | Page Base Address k + m + 1 | 0h | + * +----------------------------------------+------------------+ + * + * SGL Mode: + * +----- Non-transport + * LBA / + * +---------------+---------------+-------/-------+---------------+ + * | 3KB | 4KB | 2KB | 4KB | + * +-------+-------+-------+-------+---------------+--------+------+ + * | +-------------------------+ | + * | | | + * | +--------------------|------+ + * | | | + * +-------v-------+ +-------v-------+ +-------v-------+ + * | A MEM BLOCK | | B MEM BLOCK | | C MEM BLOCK | + * +-------^-------+ +-------^-------+ +-------^-------+ + * | | | + * +----------------+ | | + * | | | + * Segment(0) | | | + * +----------+----------+ | | | + * | Address: A +--+ | | + * +----------+----------+ | | + * | Type: 0h | Len: 3KB | | | + * +----------+----------+ | | + * | Address: Segment(1) +--+ | | + * +----------+----------+ | | | + * | Type: 2h | Len: 48 | | | | + * +----------+----------+ | | | + * | | | + * +------------------------+ | | + * | | | + * v | | + * Segment(1) | | + * +----------+----------+ | | + * | Address: B +------+ | + * +----------+----------+ | + * | Type: 0h | Len: 4KB | | + * +----------+----------+ | + * | Address: | | + * +----------+----------+ | + * | Type: 1h | Len: 2KB | | + * +----------+----------+ | + * | Address: Segment(2) +--+ | + * +----------+----------+ | | + * | Type: 0h | Len: 16 | | | + * +----------+----------+ | | + * | | + * +------------------------+ | + * | | + * v | + * Segment(2) | + * +----------+----------+ | + * | Address: C +---------------------------+ + * +----------+----------+ + * | Type: 0h | Len: 4KB | + * +----------+----------+ + */ + +static rt_ssize_t nvme_blk_rw(struct rt_nvme_device *ndev, rt_off_t slba, + rt_ubase_t buffer_dma, rt_size_t lbas, rt_uint8_t opcode) +{ + rt_err_t err; + rt_uint16_t max_lbas; + rt_uint32_t lba_shift; + rt_size_t tlbas; + rt_ssize_t data_length; + struct rt_nvme_command cmd; + struct rt_nvme_controller *nvme = ndev->ctrl; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.rw.opcode = opcode; + cmd.rw.flags = nvme->sgl_mode << RT_NVME_CMD_FLAGS_PSDT_SHIFT; + cmd.rw.nsid = rt_cpu_to_le32(ndev->nsid); + + tlbas = lbas; + lba_shift = ndev->lba_shift; + max_lbas = 1 << (nvme->max_transfer_shift - lba_shift); + + if (nvme->sgl_mode) + { + while ((rt_ssize_t)lbas > 0) + { + if (lbas < max_lbas) + { + max_lbas = (rt_uint16_t)lbas; + } + + data_length = max_lbas << lba_shift; + + cmd.rw.sgl.adddress = rt_cpu_to_le64(buffer_dma); + cmd.rw.sgl.length = rt_cpu_to_le32(data_length); + cmd.rw.sgl.sgl_identify = SGL_DESC_TYPE_DATA_BLOCK; + cmd.rw.slba = rt_cpu_to_le16(slba); + cmd.rw.length = rt_cpu_to_le16(max_lbas - 1); + + if ((err = nvme_submit_io_cmd(nvme, &cmd))) + { + tlbas -= lbas; + break; + } + + lbas -= max_lbas; + slba += max_lbas; + buffer_dma += data_length; + } + } + else + { + void *prp_list = RT_NULL; + rt_size_t prp_list_size = 0, page_size; + + page_size = nvme->page_size; + + while ((rt_ssize_t)lbas > 0) + { + rt_uint64_t prp2_addr, dma_addr; + rt_ssize_t remain_length, page_offset; + + if (lbas < max_lbas) + { + max_lbas = (rt_uint16_t)lbas; + } + + /* + * PRP transfer: + * 1. data_length <= 4KB: + * prp1 = buffer_dma + * prp2 = 0 + * + * 2. 4KB < data_length <= 8KB: + * prp1 = buffer_dma + * prp2 = buffer_dma + * + * 3. 8KB < data_length: + * prp1 = buffer_dma(0, 4k) + * prp2 = buffer_dma(4k, ~) + */ + dma_addr = buffer_dma; + page_offset = buffer_dma & (page_size - 1); + data_length = max_lbas << lba_shift; + remain_length = data_length - (page_size - page_offset); + + do { + rt_size_t prps_per_page, prps, pages; + rt_uint64_t *prp_list_ptr, prp_list_dma; + + if (remain_length <= 0) + { + prp2_addr = 0; + break; + } + + if (remain_length) + { + dma_addr += (page_size - page_offset); + } + + if (remain_length <= page_size) + { + prp2_addr = dma_addr; + break; + } + + prps_per_page = page_size / sizeof(rt_uint64_t); + prps = RT_DIV_ROUND_UP(remain_length, page_size); + pages = RT_DIV_ROUND_UP(prps - 1, prps_per_page - 1); + + if (prps > prp_list_size) + { + if (prp_list) + { + rt_free_align(prp_list); + } + + prp_list = rt_malloc_align(pages * page_size, page_size); + + if (!prp_list) + { + LOG_D("No memory to create a PRP List"); + /* Ask user to try again */ + return tlbas - lbas; + } + + prp_list_size = pages * (prps_per_page - 1) + 1; + } + prp_list_ptr = prp_list; + prp_list_dma = (rt_uint64_t)rt_kmem_v2p(prp_list_ptr); + + prp2_addr = prp_list_dma; + + for (int i = 0; prps; --prps, ++i) + { + /* End of the entry, fill the next entry addr if remain */ + if ((i == (prps_per_page - 1)) && prps > 1) + { + prp_list_dma += page_size; + *prp_list_ptr = rt_cpu_to_le64(prp_list_dma); + + /* Start to fill the next PRP */ + i = 0; + } + + *prp_list_ptr = rt_cpu_to_le64(dma_addr); + dma_addr += page_size; + } + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, prp_list_ptr, prp_list_size); + } while (0); + + cmd.rw.prp1 = rt_cpu_to_le64(buffer_dma); + cmd.rw.prp2 = rt_cpu_to_le64(prp2_addr); + cmd.rw.slba = rt_cpu_to_le16(slba); + cmd.rw.length = rt_cpu_to_le16(max_lbas - 1); + + if ((err = nvme_submit_io_cmd(nvme, &cmd))) + { + tlbas -= lbas; + break; + } + + lbas -= max_lbas; + slba += max_lbas; + buffer_dma += data_length; + } + + if (prp_list) + { + rt_free_align(prp_list); + } + } + + return tlbas; +} + +static rt_ssize_t nvme_blk_read(struct rt_blk_disk *disk, rt_off_t sector, + void *buffer, rt_size_t sector_count) +{ + rt_ssize_t res; + rt_uint32_t page_bits; + rt_size_t buffer_size; + rt_ubase_t buffer_dma; + void *temp_buffer = RT_NULL; + struct rt_nvme_device *ndev = rt_disk_to_nvme_device(disk); + struct rt_nvme_controller *nvme = ndev->ctrl; + + buffer_size = (1 << ndev->lba_shift) * sector_count; + buffer_dma = (rt_ubase_t)rt_kmem_v2p(buffer); + + if ((nvme->sgl_mode && (buffer_dma & RT_GENMASK(1, 0))) || + (!nvme->sgl_mode && (buffer_dma & ARCH_PAGE_MASK))) + { + LOG_D("DMA PRP direct %s buffer MUST 4-bytes or page aligned", "read"); + + page_bits = rt_page_bits(buffer_size); + temp_buffer = rt_pages_alloc(page_bits); + + if (!temp_buffer) + { + return -RT_ENOMEM; + } + + buffer_dma = (rt_ubase_t)rt_kmem_v2p(temp_buffer); + } + + res = nvme_blk_rw(ndev, sector, buffer_dma, sector_count, RT_NVME_CMD_READ); + + if (res > 0) + { + if (res != sector_count) + { + /* + * Don't always aim for optimization, checking for equality + * is much faster than multiplication calculation. + */ + buffer_size = res * (1 << ndev->lba_shift); + } + + if (temp_buffer) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, temp_buffer, buffer_size); + rt_memcpy(buffer, temp_buffer, buffer_size); + } + else + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, buffer, buffer_size); + } + } + + if (temp_buffer) + { + rt_pages_free(temp_buffer, page_bits); + } + + return res; +} + +static rt_ssize_t nvme_blk_write(struct rt_blk_disk *disk, rt_off_t sector, + const void *buffer, rt_size_t sector_count) +{ + rt_ssize_t res; + rt_uint32_t page_bits; + rt_size_t buffer_size; + rt_ubase_t buffer_dma; + void *temp_buffer = RT_NULL; + struct rt_nvme_device *ndev = rt_disk_to_nvme_device(disk); + struct rt_nvme_controller *nvme = ndev->ctrl; + + buffer_size = (1 << ndev->lba_shift) * sector_count; + buffer_dma = (rt_ubase_t)rt_kmem_v2p((void *)buffer); + + if ((nvme->sgl_mode && (buffer_dma & RT_GENMASK(1, 0))) || + (!nvme->sgl_mode && (buffer_dma & ARCH_PAGE_MASK))) + { + LOG_D("DMA PRP direct %s buffer MUST 4-bytes or page aligned", "write"); + + page_bits = rt_page_bits(buffer_size); + temp_buffer = rt_pages_alloc(page_bits); + + if (!temp_buffer) + { + return -RT_ENOMEM; + } + + buffer_dma = (rt_ubase_t)rt_kmem_v2p(temp_buffer); + + rt_memcpy(temp_buffer, buffer, buffer_size); + buffer = temp_buffer; + } + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, (void *)buffer, buffer_size); + + res = nvme_blk_rw(ndev, sector, buffer_dma, sector_count, RT_NVME_CMD_WRITE); + + if (temp_buffer) + { + rt_pages_free(temp_buffer, page_bits); + } + + return res; +} + +static rt_err_t nvme_blk_getgeome(struct rt_blk_disk *disk, + struct rt_device_blk_geometry *geometry) +{ + struct rt_nvme_device *ndev = rt_disk_to_nvme_device(disk); + + geometry->bytes_per_sector = 1 << ndev->lba_shift; + geometry->block_size = 1 << ndev->lba_shift; + geometry->sector_count = rt_le64_to_cpu(ndev->id.nsze); + + return RT_EOK; +} + +static rt_err_t nvme_blk_sync(struct rt_blk_disk *disk) +{ + struct rt_nvme_command cmd; + struct rt_nvme_device *ndev = rt_disk_to_nvme_device(disk); + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.common.opcode = RT_NVME_CMD_FLUSH; + cmd.common.nsid = rt_cpu_to_le32(ndev->nsid); + + return nvme_submit_io_cmd(ndev->ctrl, &cmd); +} + +static rt_err_t nvme_blk_erase(struct rt_blk_disk *disk) +{ + rt_err_t err = RT_EOK; + rt_ssize_t slba, lbas, max_lbas; + struct rt_nvme_command cmd; + struct rt_nvme_device *ndev = rt_disk_to_nvme_device(disk); + struct rt_nvme_controller *nvme = ndev->ctrl; + + if (!nvme->write_zeroes) + { + return -RT_ENOSYS; + } + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.write_zeroes.opcode = RT_NVME_CMD_WRITE_ZEROES; + cmd.write_zeroes.nsid = rt_cpu_to_le32(ndev->nsid); + + slba = 0; + lbas = rt_le64_to_cpu(ndev->id.nsze); + max_lbas = 1 << (nvme->max_transfer_shift - ndev->lba_shift); + + while ((rt_ssize_t)lbas > 0) + { + if (lbas < max_lbas) + { + max_lbas = (rt_uint16_t)lbas; + } + + cmd.write_zeroes.slba = rt_cpu_to_le16(slba); + cmd.write_zeroes.length = rt_cpu_to_le16(max_lbas - 1); + + if ((err = nvme_submit_io_cmd(nvme, &cmd))) + { + break; + } + + lbas -= max_lbas; + slba += max_lbas; + } + + return err; +} + +static rt_err_t nvme_blk_autorefresh(struct rt_blk_disk *disk, rt_bool_t is_auto) +{ + struct rt_nvme_device *ndev = rt_disk_to_nvme_device(disk); + struct rt_nvme_controller *nvme = ndev->ctrl; + + if (nvme->volatile_write_cache & RT_NVME_CTRL_VWC_PRESENT) + { + return nvme_set_features_simple(nvme, RT_NVME_FEAT_VOLATILE_WC, !!is_auto); + } + else if (!is_auto) + { + return RT_EOK; + } + + return -RT_ENOSYS; +} + +static const struct rt_blk_disk_ops nvme_blk_ops = +{ + .read = nvme_blk_read, + .write = nvme_blk_write, + .getgeome = nvme_blk_getgeome, + .sync = nvme_blk_sync, + .erase = nvme_blk_erase, + .autorefresh = nvme_blk_autorefresh, +}; + +static void nvme_queue_isr(int irqno, void *param) +{ + rt_ubase_t level; + rt_uint16_t head, phase, status; + struct rt_nvme_queue *queue = param; + struct rt_nvme_controller *nvme = queue->nvme; + + level = rt_spin_lock_irqsave(&queue->lock); + + head = queue->cq_head; + phase = queue->cq_phase; + status = HWREG16(&queue->cq_entry[head].status); + status = rt_le16_to_cpu(status); + + if ((status & 0x01) == phase) + { + if ((status >> 1)) + { + queue->err = -RT_EIO; + goto _end_cmd; + } + + if (nvme->ops->complete_cmd) + { + nvme->ops->complete_cmd(queue, queue->cmd); + } + + _end_cmd: + if (++head == queue->depth) + { + head = 0; + phase = !phase; + } + + HWREG32(queue->doorbell + nvme->doorbell_stride) = head; + queue->cq_head = head; + queue->cq_phase = phase; + + rt_completion_done(&queue->done); + } + + rt_spin_unlock_irqrestore(&queue->lock, level); +} + +static rt_err_t nvme_identify(struct rt_nvme_controller *nvme, + rt_uint32_t nsid, rt_uint32_t cns, void *data) +{ + rt_err_t err; + rt_uint32_t page_size = nvme->page_size; + rt_ubase_t data_phy = (rt_ubase_t)rt_kmem_v2p(data); + int offset = data_phy & (page_size - 1); + struct rt_nvme_command cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.identify.opcode = RT_NVME_ADMIN_OPCODE_IDENTIFY; + cmd.identify.nsid = rt_cpu_to_le32(nsid); + cmd.identify.prp1 = rt_cpu_to_le64(data_phy); + + if (sizeof(struct rt_nvme_id_ctrl) <= page_size - offset) + { + cmd.identify.prp2 = 0; + } + else + { + data_phy += (page_size - offset); + cmd.identify.prp2 = rt_cpu_to_le64(data_phy); + } + cmd.identify.cns = rt_cpu_to_le32(cns); + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data, sizeof(struct rt_nvme_id_ctrl)); + + if (!(err = nvme_submit_cmd(&nvme->admin_queue, &cmd))) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data, sizeof(struct rt_nvme_id_ctrl)); + } + + return err; +} + +static rt_err_t nvme_attach_queue(struct rt_nvme_queue *queue, rt_uint8_t opcode) +{ + struct rt_nvme_command cmd; + struct rt_nvme_controller *nvme = queue->nvme; + rt_uint16_t flags = RT_NVME_QUEUE_PHYS_CONTIG; + + rt_memset(&cmd, 0, sizeof(cmd)); + + if (opcode == RT_NVME_ADMIN_OPCODE_CREATE_CQ) + { + cmd.create_cq.opcode = opcode; + cmd.create_cq.prp1 = rt_cpu_to_le64(queue->cq_entry_phy); + cmd.create_cq.cqid = rt_cpu_to_le16(queue->qid); + cmd.create_cq.qsize = rt_cpu_to_le16(queue->depth - 1); + cmd.create_cq.cq_flags = rt_cpu_to_le16(flags | RT_NVME_CQ_IRQ_ENABLED); + cmd.create_cq.irq_vector = rt_cpu_to_le16(nvme->irqs_nr > 1 ? queue->qid : 0); + } + else if (opcode == RT_NVME_ADMIN_OPCODE_CREATE_SQ) + { + cmd.create_sq.opcode = opcode; + cmd.create_sq.prp1 = rt_cpu_to_le64(queue->sq_cmds_phy); + cmd.create_sq.sqid = rt_cpu_to_le16(queue->qid); + cmd.create_sq.qsize = rt_cpu_to_le16(queue->depth - 1); + cmd.create_sq.sq_flags = rt_cpu_to_le16(flags | RT_NVME_SQ_PRIO_MEDIUM); + cmd.create_sq.cqid = rt_cpu_to_le16(queue->qid); + } + else + { + LOG_E("What the fuck opcode = %x", opcode); + RT_ASSERT(0); + } + + return nvme_submit_cmd(&nvme->admin_queue, &cmd); +} + +rt_inline rt_err_t nvme_attach_queue_sq(struct rt_nvme_queue *queue) +{ + return nvme_attach_queue(queue, RT_NVME_ADMIN_OPCODE_CREATE_SQ); +} + +rt_inline rt_err_t nvme_attach_queue_cq(struct rt_nvme_queue *queue) +{ + return nvme_attach_queue(queue, RT_NVME_ADMIN_OPCODE_CREATE_CQ); +} + +static rt_err_t nvme_detach_queue(struct rt_nvme_queue *queue, + rt_uint8_t opcode) +{ + struct rt_nvme_command cmd; + struct rt_nvme_controller *nvme = queue->nvme; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.delete_queue.opcode = opcode; + cmd.delete_queue.qid = rt_cpu_to_le16(queue->qid); + + return nvme_submit_cmd(&nvme->admin_queue, &cmd); +} + +rt_inline rt_ubase_t nvme_queue_dma_flags(void) +{ + return RT_DMA_F_NOCACHE | RT_DMA_F_LINEAR; +} + +static void nvme_free_queue(struct rt_nvme_queue *queue) +{ + rt_ubase_t dma_flags; + struct rt_nvme_controller *nvme = queue->nvme; + + if (nvme->ops->cleanup_queue) + { + rt_err_t err; + + if (!(err = nvme->ops->cleanup_queue(queue))) + { + LOG_W("Cleanup[%s] queue error = %s", nvme->ops->name, rt_strerror(err)); + } + } + + dma_flags = nvme_queue_dma_flags(); + + if (queue->sq_cmds) + { + rt_dma_free(nvme->dev, sizeof(*queue->sq_cmds) * queue->depth, + queue->sq_cmds, queue->sq_cmds_phy, dma_flags); + } + + if (queue->cq_entry) + { + rt_dma_free(nvme->dev, sizeof(*queue->cq_entry) * queue->depth, + queue->cq_entry, queue->cq_entry_phy, dma_flags); + } +} + +static struct rt_nvme_queue *nvme_alloc_queue(struct rt_nvme_controller *nvme, + int qid, int depth) +{ + rt_err_t err; + rt_ubase_t dma_flags; + struct rt_nvme_queue *queue = &nvme->queue[qid]; + + rt_memset(queue, 0, sizeof(*queue)); + + queue->nvme = nvme; + queue->doorbell = &nvme->doorbell_tbl[qid * 2 * nvme->doorbell_stride]; + queue->qid = qid; + queue->depth = depth; + queue->cq_head = 0; + queue->cq_phase = 1; + rt_completion_init(&queue->done); + rt_spin_lock_init(&queue->lock); + + dma_flags = nvme_queue_dma_flags(); + + /* struct rt_nvme_command */ + queue->sq_cmds = rt_dma_alloc(nvme->dev, + sizeof(*queue->sq_cmds) * depth, &queue->sq_cmds_phy, dma_flags); + + if (!queue->sq_cmds) + { + err = -RT_ENOMEM; + goto _fail; + } + + /* struct rt_nvme_completion */ + queue->cq_entry = rt_dma_alloc(nvme->dev, + sizeof(*queue->cq_entry) * depth, &queue->cq_entry_phy, dma_flags); + + if (!queue->cq_entry) + { + err = -RT_ENOMEM; + goto _fail; + } + + rt_memset(queue->sq_cmds, 0, sizeof(struct rt_nvme_command) * depth); + rt_memset(queue->cq_entry, 0, sizeof(struct rt_nvme_completion) * depth); + + if (nvme->ops->setup_queue) + { + if (!(err = nvme->ops->setup_queue(queue))) + { + LOG_E("Setup[%s] queue error = %s", nvme->ops->name, rt_strerror(err)); + + goto _fail; + } + } + + return queue; + +_fail: + nvme_free_queue(queue); + + return rt_err_ptr(err); +} + +static rt_err_t nvme_configure_admin_queue(struct rt_nvme_controller *nvme) +{ + rt_err_t err; + int irq; + char name[RT_NAME_MAX]; + rt_uint32_t aqa; + rt_uint32_t page_shift = ARCH_PAGE_SHIFT; + rt_uint32_t page_min = RT_NVME_CAP_MPSMIN(nvme->cap) + 12; + rt_uint32_t page_max = RT_NVME_CAP_MPSMAX(nvme->cap) + 12; + struct rt_nvme_queue *admin_queue; + + if (page_shift < page_min) + { + LOG_E("Device %s page size (%u) %s than host (%u)", + "minimum", 1 << page_min, "larger", 1 << page_shift); + return -RT_EINVAL; + } + + if (page_shift > page_max) + { + LOG_W("Device %s page size (%u) %s than host (%u)", + "maximum", 1 << page_max, "smaller", 1 << page_shift); + page_shift = page_max; + } + + if ((err = nvme_disable_ctrl(nvme))) + { + return err; + } + + admin_queue = nvme_alloc_queue(nvme, 0, RT_NVME_AQ_DEPTH); + + if (rt_is_err(admin_queue)) + { + return rt_ptr_err(admin_queue); + } + + aqa = admin_queue->depth - 1; + aqa |= aqa << 16; + + nvme->page_shift = page_shift; + nvme->page_size = 1U << page_shift; + + nvme->ctrl_config = RT_NVME_CC_CSS_NVM; + nvme->ctrl_config |= (page_shift - 12) << RT_NVME_CC_MPS_SHIFT; + nvme->ctrl_config |= RT_NVME_CC_ARB_RR | RT_NVME_CC_SHN_NONE; + nvme->ctrl_config |= RT_NVME_CC_IOSQES | RT_NVME_CC_IOCQES; + + nvme_writel(nvme, RT_NVME_REG_AQA, aqa); + nvme_writeq(nvme, RT_NVME_REG_ASQ, admin_queue->sq_cmds_phy); + nvme_writeq(nvme, RT_NVME_REG_ACQ, admin_queue->cq_entry_phy); + + if ((err = nvme_enable_ctrl(nvme))) + { + nvme_free_queue(admin_queue); + + return err; + } + + irq = nvme->irqs[0]; + + rt_snprintf(name, RT_NAME_MAX, "%s-admin-queue", nvme->name); + + rt_hw_interrupt_install(irq, nvme_queue_isr, &nvme->admin_queue, name); + rt_hw_interrupt_umask(irq); + + return RT_EOK; +} + +static rt_err_t nvme_setup_io_queues(struct rt_nvme_controller *nvme) +{ + rt_err_t err; + rt_uint32_t value; + int irq, cpuid = 0; + char name[RT_NAME_MAX]; + rt_bool_t affinity_fixup = RT_FALSE; + RT_IRQ_AFFINITY_DECLARE(affinity) = { 0 }; + struct rt_nvme_queue *queue; + + nvme->io_queue_max = nvme->irqs_nr > 1 ? nvme->irqs_nr - 1 : 1; + value = (nvme->io_queue_max - 1) | ((nvme->io_queue_max - 1) << 16); + + if ((err = nvme_set_features_simple(nvme, RT_NVME_FEAT_NUM_QUEUES, value))) + { + return err; + } + + for (int i = 0, q_idx = 1; i < nvme->io_queue_max; ++i, ++q_idx) + { + queue = nvme_alloc_queue(nvme, q_idx, nvme->queue_depth); + + if (!queue) + { + return -RT_ENOMEM; + } + + if ((err = nvme_attach_queue_cq(queue)) || + (err = nvme_attach_queue_sq(queue))) + { + return err; + } + } + + for (int i = 0, irq_idx = 1; i < nvme->io_queue_max; ++i, ++irq_idx) + { + irq = nvme->irqs[irq_idx % nvme->irqs_nr]; + + rt_snprintf(name, RT_NAME_MAX, "%s-io-queue%d", nvme->name, i); + + if (!affinity_fixup) + { + RT_IRQ_AFFINITY_SET(affinity, cpuid % RT_CPUS_NR); + if (rt_pic_irq_set_affinity(irq, affinity)) + { + /* Fixup in secondary CPU startup */ + affinity_fixup = RT_TRUE; + } + RT_IRQ_AFFINITY_CLEAR(affinity, cpuid++ % RT_CPUS_NR); + } + + rt_hw_interrupt_install(irq, nvme_queue_isr, &nvme->io_queues[i], name); + rt_hw_interrupt_umask(irq); + } + + return RT_EOK; +} + +static void nvme_remove_io_queues(struct rt_nvme_controller *nvme) +{ + int irq; + struct rt_nvme_queue *queue; + + for (int i = 0, irq_idx = 1; i < nvme->io_queue_max; ++i, ++irq_idx) + { + queue = &nvme->io_queues[i]; + + nvme_detach_queue(queue, RT_NVME_ADMIN_OPCODE_DELETE_SQ); + nvme_detach_queue(queue, RT_NVME_ADMIN_OPCODE_DELETE_CQ); + nvme_free_queue(queue); + + irq = nvme->irqs[irq_idx % nvme->irqs_nr]; + + rt_hw_interrupt_mask(irq); + rt_pic_detach_irq(irq, queue); + } +} + +static void nvme_remove_admin_queues(struct rt_nvme_controller *nvme) +{ + int irq = nvme->irqs[0]; + + rt_hw_interrupt_mask(irq); + rt_pic_detach_irq(irq, &nvme->admin_queue); + + nvme_free_queue(&nvme->admin_queue); +} + +static void nvme_remove_devices(struct rt_nvme_controller *nvme) +{ + struct rt_nvme_device *ndev, *next_ndev; + + rt_list_for_each_entry_safe(ndev, next_ndev, &nvme->ns_nodes, list) + { + rt_list_remove(&ndev->list); + + rt_hw_blk_disk_unregister(&ndev->parent); + rt_free(ndev); + } +} + +static rt_err_t nvme_scan_device(struct rt_nvme_controller *nvme, + rt_size_t number_of_ns) +{ + rt_err_t err = RT_EOK; + rt_uint32_t lbaf; + struct rt_nvme_id_ns *id = RT_NULL; + + if (!(id = rt_malloc_align(sizeof(*id), nvme->page_size))) + { + return -RT_ENOMEM; + } + + /* NVME Namespace is start with "1" */ + for (rt_uint32_t nsid = 1; nsid <= number_of_ns; ++nsid) + { + struct rt_nvme_device *ndev = rt_calloc(1, sizeof(*ndev)); + + if (!ndev) + { + err = -RT_ENOMEM; + goto _free_res; + } + + rt_memset(id, 0, sizeof(*id)); + if ((err = nvme_identify(nvme, nsid, 0, id))) + { + goto _free_res; + } + + if (!id->nsze) + { + continue; + } + + ndev->ctrl = nvme; + + rt_memcpy(&ndev->id, id, sizeof(ndev->id)); + lbaf = id->flbas & RT_NVME_NS_FLBAS_LBA_MASK; + lbaf |= ((id->flbas & RT_NVME_NS_FLBAS_LBA_UMASK) >> RT_NVME_NS_FLBAS_LBA_SHIFT); + + ndev->nsid = nsid; + ndev->lba_shift = id->lbaf[lbaf].ds; + + ndev->parent.ida = &nvme_ida; + ndev->parent.parallel_io = RT_TRUE; + ndev->parent.ops = &nvme_blk_ops; + ndev->parent.max_partitions = RT_BLK_PARTITION_MAX; + rt_dm_dev_set_name(&ndev->parent.parent, "%sn%u", nvme->name, nsid); + + if ((err = rt_hw_blk_disk_register(&ndev->parent))) + { + goto _free_res; + } + + rt_list_init(&ndev->list); + rt_list_insert_before(&nvme->ns_nodes, &ndev->list); + } + +_free_res: + rt_free_align(id); + + return err; +} + +rt_inline rt_size_t strip_len(const char *str, rt_size_t max_len) +{ + rt_size_t size = 0; + + for (int i = 0; *str && i < max_len; ++i, ++str) + { + if (*str != ' ') + { + size = i + 1; + } + } + + return size; +} + +rt_err_t rt_nvme_controller_register(struct rt_nvme_controller *nvme) +{ + rt_err_t err; + struct rt_nvme_id_ctrl *ctrl = RT_NULL; + + if (!nvme || !nvme->ops) + { + return -RT_EINVAL; + } + + if (nvme_readl(nvme, RT_NVME_REG_CSTS) == (rt_uint32_t)-1) + { + LOG_E("Out of memory"); + + return -RT_EINVAL; + } + + if ((nvme->nvme_id = rt_dm_ida_alloc(&nvme_controller_ida)) < 0) + { + return -RT_EFULL; + } + + rt_snprintf(nvme->name, RT_NAME_MAX, "nvme%u", nvme->nvme_id); + + nvme->cap = nvme_readq(nvme, RT_NVME_REG_CAP); + nvme->queue_depth = RT_NVME_CAP_MQES(nvme->cap) + 1; + nvme->doorbell_stride = 1 << RT_NVME_CAP_STRIDE(nvme->cap); + nvme->doorbell_tbl = nvme->regs + RT_NVME_REG_DBS; + + if ((err = nvme_configure_admin_queue(nvme))) + { + LOG_E("Configure admin queue error = %s", rt_strerror(err)); + goto _free_admin_queue; + } + + if ((err = nvme_setup_io_queues(nvme))) + { + LOG_E("Unable to setup I/O queues error = %s", rt_strerror(err)); + goto _free_admin_queue; + } + + if (!(ctrl = rt_malloc_align(sizeof(*ctrl), nvme->page_size))) + { + err = -RT_ENOMEM; + goto _fail; + } + + if ((err = nvme_identify(nvme, 0, 1, ctrl))) + { + goto _fail; + } + + if (ctrl->mdts) + { + nvme->max_transfer_shift = ctrl->mdts + (RT_NVME_CAP_MPSMIN(nvme->cap) + 12); + } + else + { + /* 1MB is recommended. */ + nvme->max_transfer_shift = 20; + } + nvme->volatile_write_cache = ctrl->vwc; + nvme->write_zeroes = !!(rt_le64_to_cpu(ctrl->oncs) & RT_NVME_CTRL_ONCS_WRITE_ZEROES); + + if ((rt_le32_to_cpu(ctrl->sgls) & RT_NVME_ID_SGL_SUPPORT_MASK)) + { + nvme->sgl_mode = RT_NVME_PSDT_SGL_MPTR_SGL; + } + + LOG_I("NVM Express v%d.%d (%s, %-*.s, %-*.s)", + nvme_readl(nvme, RT_NVME_REG_VS) >> 16, + nvme_readl(nvme, RT_NVME_REG_VS) & 0xff, + nvme->ops->name, + strip_len(ctrl->mn, sizeof(ctrl->mn)), ctrl->mn, + strip_len(ctrl->fr, sizeof(ctrl->fr)), ctrl->fr); + + rt_list_init(&nvme->ns_nodes); + if ((err = nvme_scan_device(nvme, rt_le32_to_cpu(ctrl->nn)))) + { + goto _fail; + } + + rt_free_align(ctrl); + + rt_spin_lock(&nvme_lock); + rt_list_insert_after(&nvme_nodes, &nvme->list); + rt_spin_unlock(&nvme_lock); + + return RT_EOK; + +_fail: + if (ctrl) + { + rt_free_align(ctrl); + } + nvme_remove_devices(nvme); + nvme_remove_io_queues(nvme); +_free_admin_queue: + nvme_remove_admin_queues(nvme); + + rt_dm_ida_free(&nvme_controller_ida, nvme->nvme_id); + + return err; +} + +rt_err_t rt_nvme_controller_unregister(struct rt_nvme_controller *nvme) +{ + rt_err_t err; + + if (!nvme) + { + return -RT_EINVAL; + } + + rt_spin_lock(&nvme_lock); + rt_list_remove(&nvme->list); + rt_spin_unlock(&nvme_lock); + + nvme_remove_devices(nvme); + nvme_remove_io_queues(nvme); + nvme_remove_admin_queues(nvme); + + rt_dm_ida_free(&nvme_controller_ida, nvme->nvme_id); + + if (!(err = nvme_shutdown_ctrl(nvme))) + { + err = nvme_disable_ctrl(nvme); + } + else + { + LOG_E("%s: shutdown error = %s", nvme->name, rt_strerror(err)); + } + + return err; +} + +/* + * NVME's IO queue should be Per-CPU, fixup the affinity after the secondary CPU + * startup, this stage can make sure the affinity setting success as possible. + */ +static int nvme_queue_affinify_fixup(void) +{ + int cpuid = rt_hw_cpu_id(); + struct rt_nvme_controller *nvme; + RT_IRQ_AFFINITY_DECLARE(affinity) = { 0 }; + RT_IRQ_AFFINITY_DECLARE(current_affinity) = { 0 }; + + RT_IRQ_AFFINITY_SET(affinity, cpuid); + + rt_hw_spin_lock(&nvme_lock.lock); + rt_list_for_each_entry(nvme, &nvme_nodes, list) + { + for (int i = cpuid % RT_CPUS_NR; i < nvme->io_queue_max; i += RT_CPUS_NR) + { + int irq = nvme->irqs[i]; + + if (!rt_pic_irq_get_affinity(irq, current_affinity) && + !rt_bitmap_test_bit(current_affinity, cpuid)) + { + rt_ubase_t level = rt_hw_interrupt_disable(); + + rt_pic_irq_set_affinity(irq, affinity); + + rt_hw_interrupt_enable(level); + } + } + } + rt_hw_spin_unlock(&nvme_lock.lock); + + return 0; +} +INIT_SECONDARY_CPU_EXPORT(nvme_queue_affinify_fixup); diff --git a/rt-thread/components/drivers/ofw/base.c b/rt-thread/components/drivers/ofw/base.c index c2d2f8f..78a2e9f 100644 --- a/rt-thread/components/drivers/ofw/base.c +++ b/rt-thread/components/drivers/ofw/base.c @@ -333,8 +333,6 @@ static int ofw_prop_index_of_string(struct rt_ofw_prop *prop, const char *string static rt_int32_t ofw_strcasecmp(const char *cs, const char *ct) { - extern rt_int32_t strcasecmp(const char *cs, const char *ct); - return rt_strcasecmp(cs, ct); } @@ -1412,7 +1410,7 @@ struct rt_ofw_node *rt_ofw_append_child(struct rt_ofw_node *parent, const char * } } - return np; + return rt_ofw_node_get(np); } rt_err_t rt_ofw_append_prop(struct rt_ofw_node *np, const char *name, int length, void *value) diff --git a/rt-thread/components/drivers/ofw/fdt.c b/rt-thread/components/drivers/ofw/fdt.c index 419f392..cca4cdd 100644 --- a/rt-thread/components/drivers/ofw/fdt.c +++ b/rt-thread/components/drivers/ofw/fdt.c @@ -37,6 +37,12 @@ static rt_phandle _phandle_max; static rt_size_t _root_size_cells; static rt_size_t _root_addr_cells; +#ifdef ARCH_CPU_64BIT +#define MIN_BIT 16 +#else +#define MIN_BIT 8 +#endif + const char *rt_fdt_node_name(const char *full_name) { const char *node_name = strrchr(full_name, '/'); @@ -85,15 +91,15 @@ rt_uint64_t rt_fdt_translate_address(void *fdt, int nodeoffset, rt_uint64_t addr if (parent >= 0) { - ranges = fdt_getprop(fdt, nodeoffset, "ranges", &length); + ranges = fdt_getprop(fdt, parent, "ranges", &length); } if (ranges && length > 0) { - local.addr_cells = fdt_address_cells(fdt, nodeoffset); - local.size_cells = fdt_size_cells(fdt, nodeoffset); - cpu.addr_cells = fdt_io_addr_cells(fdt, nodeoffset); - cpu.size_cells = fdt_io_size_cells(fdt, nodeoffset); + local.addr_cells = fdt_address_cells(fdt, parent); + local.size_cells = fdt_size_cells(fdt, parent); + cpu.addr_cells = fdt_io_addr_cells(fdt, parent); + cpu.size_cells = fdt_io_size_cells(fdt, parent); group_len = local.addr_cells + cpu.addr_cells + local.size_cells; @@ -105,7 +111,7 @@ rt_uint64_t rt_fdt_translate_address(void *fdt, int nodeoffset, rt_uint64_t addr if (local.addr <= address && local.addr + local.size > address) { - ret += address - cpu.addr; + ret = address - local.addr + cpu.addr; break; } @@ -247,9 +253,9 @@ static rt_err_t fdt_reserved_memory_reg(int nodeoffset, const char *uname) rt_bool_t is_nomap = fdt_getprop(_fdt, nodeoffset, "no-map", RT_NULL) ? RT_TRUE : RT_FALSE; base = rt_fdt_translate_address(_fdt, nodeoffset, base); - rt_memblock_reserve_memory(uname, base, base + size, is_nomap); - len -= t_len; + rt_memblock_reserve_memory(fdt_get_name(_fdt, nodeoffset, RT_NULL), + base, base + size, is_nomap); } } } @@ -358,11 +364,11 @@ static rt_err_t fdt_scan_memory(void) if (!err) { - LOG_I("Memory node(%d) ranges: %p - %p%s", no, base, base + size, ""); + LOG_I("Memory node(%d) ranges: 0x%.*lx - 0x%.*lx%s", no, MIN_BIT, base, MIN_BIT, base + size, ""); } else { - LOG_W("Memory node(%d) ranges: %p - %p%s", no, base, base + size, " unable to record"); + LOG_W("Memory node(%d) ranges: 0x%.*lx - 0x%.*lx%s", no, MIN_BIT, base, MIN_BIT, base + size, " unable to record"); } } } @@ -793,6 +799,55 @@ rt_err_t rt_fdt_scan_chosen_stdout(void) return err; } +rt_err_t rt_fdt_bootargs_select(const char *key, int index, const char **out_result) +{ + rt_err_t err; + + if (key && index >= 0 && out_result) + { + int offset = fdt_path_offset(_fdt, "/chosen"); + + if (offset >= 0) + { + int len, key_len = rt_strlen(key); + const char *bootargs = fdt_getprop(_fdt, offset, "bootargs", &len), *end; + + end = bootargs + len; + err = -RT_EEMPTY; + + for (int i = 0; bootargs < end; ++i) + { + bootargs = rt_strstr(bootargs, key); + + if (!bootargs) + { + break; + } + + bootargs += key_len; + + if (i == index) + { + *out_result = bootargs; + + err = -RT_EOK; + break; + } + } + } + else + { + err = -RT_ERROR; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + static void system_node_init_flag(struct rt_ofw_node *np) { if (np) diff --git a/rt-thread/components/drivers/ofw/io.c b/rt-thread/components/drivers/ofw/io.c index d9065c0..b0a7526 100644 --- a/rt-thread/components/drivers/ofw/io.c +++ b/rt-thread/components/drivers/ofw/io.c @@ -407,6 +407,75 @@ rt_uint64_t rt_ofw_translate_address(struct rt_ofw_node *np, const char *range_t return cpu_addr; } +rt_uint64_t rt_ofw_reverse_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address) +{ + rt_uint64_t bus_addr = address; + + if (!range_type) + { + range_type = "ranges"; + } + + rt_ofw_foreach_parent_node(np) + { + rt_ssize_t len; + struct rt_ofw_prop *prop; + struct bus_ranges *ranges = RT_NULL; + + prop = rt_ofw_get_prop(np, range_type, &len); + + if (!prop || !len) + { + continue; + } + + for (int i = 0; i < RT_ARRAY_SIZE(_bus_ranges); ++i) + { + if (!_bus_ranges[i]) + { + break; + } + + if (_bus_ranges[i]->np == np) + { + ranges = _bus_ranges[i]; + break; + } + } + + if (!ranges) + { + ranges = ofw_bus_ranges(np, prop); + } + + if (ranges) + { + for (int i = 0; i < ranges->nr; ++i) + { + rt_uint64_t parent_addr = ranges->parent_addr[i]; + rt_uint64_t child_size = ranges->child_size[i]; + + if (address >= parent_addr && address < parent_addr + child_size) + { + bus_addr = ranges->child_addr[i] + (address - parent_addr); + + break; + } + } + } + else + { + bus_addr = ~0ULL; + } + + rt_ofw_node_put(np); + + break; + } + + return bus_addr; +} + #ifdef ARCH_CPU_64BIT #define ofw_address_cpu_cast(np, address) (void *)(address) #else diff --git a/rt-thread/components/drivers/ofw/ofw.c b/rt-thread/components/drivers/ofw/ofw.c index d09fb4e..76389f3 100644 --- a/rt-thread/components/drivers/ofw/ofw.c +++ b/rt-thread/components/drivers/ofw/ofw.c @@ -69,6 +69,12 @@ struct ofw_obj_cmp_list static const struct ofw_obj_cmp_list ofw_obj_cmp_list[] = { +#ifdef RT_USING_CLK + { "#clock-cells", RT_CLK_NODE_OBJ_NAME, sizeof(struct rt_clk_node) }, +#endif +#ifdef RT_USING_RESET + { "#reset-cells", RT_RESET_CONTROLLER_OBJ_NAME, sizeof(struct rt_reset_controller) }, +#endif { "#power-domain-cells", RT_POWER_DOMAIN_PROXY_OBJ_NAME, sizeof(struct rt_dm_power_domain_proxy) }, { "#power-domain-cells", RT_POWER_DOMAIN_OBJ_NAME, sizeof(struct rt_dm_power_domain) }, }; @@ -103,14 +109,14 @@ static struct rt_object *ofw_parse_object(struct rt_ofw_node *np, const char *ce { item = &ofw_obj_cmp_list[i]; - if (!rt_strcmp(item->cells_name, cells_name)) - { - ret_obj = obj; - break; - } - if (!rt_strncmp(item->obj_name, obj->name, RT_NAME_MAX)) { + if (!rt_strcmp(item->cells_name, cells_name)) + { + ret_obj = obj; + break; + } + obj = (struct rt_object *)((rt_ubase_t)obj + item->obj_size); break; } @@ -134,7 +140,7 @@ struct rt_object *rt_ofw_parse_object(struct rt_ofw_node *np, const char *obj_na if (np && (test_obj = rt_ofw_data(np)) && cells_name) { /* The composite object is rare, so we try to find this object as much as possible at once. */ - if (obj_name && rt_strcmp(test_obj->name, obj_name)) + if (obj_name && !rt_strcmp(test_obj->name, obj_name)) { obj = test_obj; } @@ -386,6 +392,11 @@ const char *rt_ofw_bootargs_select(const char *key, int index) { *(char *)ch++ = '\0'; } + if (*ch == '\0') + { + /* space in the end */ + --bootargs_nr; + } --ch; } @@ -636,7 +647,14 @@ void rt_ofw_node_dump_dts(struct rt_ofw_node *np, rt_bool_t sibling_too) struct fdt_info *header = (struct fdt_info *)np->name; struct fdt_reserve_entry *rsvmap = header->rsvmap; - rt_kprintf("/dts-v%x/;\n\n", fdt_version(header->fdt)); + /* + * Shall be present to identify the file as a version 1 DTS + * (dts files without this tag will be treated by dtc + * as being in the obsolete version 0, which uses + * a different format for integers in addition to + * other small but incompatible changes). + */ + rt_kprintf("/dts-v1/;\n\n"); for (int i = header->rsvmap_nr - 1; i >= 0; --i) { diff --git a/rt-thread/components/drivers/pci/Kconfig b/rt-thread/components/drivers/pci/Kconfig new file mode 100644 index 0000000..f25fce6 --- /dev/null +++ b/rt-thread/components/drivers/pci/Kconfig @@ -0,0 +1,49 @@ +menuconfig RT_USING_PCI + bool "Using Peripheral Component Interconnect Express (PCIe/PCI)" + depends on RT_USING_DM + depends on RT_USING_PIC + select RT_USING_ADT + select RT_USING_ADT_BITMAP + default n + +config RT_PCI_MSI + bool "PCI MSI/MSI-X" + depends on RT_USING_PCI + default y + +config RT_PCI_ENDPOINT + bool "PCI Endpoint" + depends on RT_USING_PCI + select RT_USING_ADT_REF + default n + +config RT_PCI_SYS_64BIT + bool "PCI System 64bit" + depends on RT_USING_PCI + depends on ARCH_CPU_64BIT + default y + +config RT_PCI_CACHE_LINE_SIZE + int "PCI Cache line size" + depends on RT_USING_PCI + default 8 if ARCH_CPU_64BIT + default 4 + +config RT_PCI_LOCKLESS + bool "PCI Lock less in options" + depends on RT_USING_PCI + default n + +if RT_USING_PCI + +comment "PCI Device Drivers" + +config RT_PCI_ECAM + bool "PCIe ECAM" + depends on RT_USING_PCI + default y + help + PCIe Express Enhanced Configuration Access Mechanism + +rsource "host/Kconfig" +endif diff --git a/rt-thread/components/drivers/pci/SConscript b/rt-thread/components/drivers/pci/SConscript new file mode 100644 index 0000000..dfe3797 --- /dev/null +++ b/rt-thread/components/drivers/pci/SConscript @@ -0,0 +1,28 @@ +from building import * + +objs = [] + +if not GetDepend(['RT_USING_PCI']): + Return('objs') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = ['access.c', 'host-bridge.c', 'irq.c', 'pci.c', 'pme.c', 'probe.c'] + +if GetDepend(['RT_USING_OFW']): + src += ['ofw.c'] + +if GetDepend(['RT_PCI_ECAM']): + src += ['ecam.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/rt-thread/components/drivers/pci/access.c b/rt-thread/components/drivers/pci/access.c new file mode 100644 index 0000000..7b026c8 --- /dev/null +++ b/rt-thread/components/drivers/pci/access.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#include + +struct rt_spinlock rt_pci_lock = { 0 }; + +#ifdef RT_PCI_LOCKLESS +#define pci_lock_config(l) do { (void)(l); } while (0) +#define pci_unlock_config(l) do { (void)(l); } while (0) +#else +#define pci_lock_config(l) l = rt_spin_lock_irqsave(&rt_pci_lock) +#define pci_unlock_config(l) rt_spin_unlock_irqrestore(&rt_pci_lock, l) +#endif + +#define PCI_OPS_READ(name, type) \ +rt_err_t rt_pci_bus_read_config_##name(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, type *value) \ +{ \ + rt_err_t err; \ + rt_ubase_t level; \ + rt_uint32_t data = 0; \ + pci_lock_config(level); \ + err = bus->ops->read(bus, devfn, reg, sizeof(type), &data); \ + *value = err ? (type)(~0) : (type)data; \ + pci_unlock_config(level); \ + return err; \ +} + +#define PCI_OPS_WRITE(name, type) \ +rt_err_t rt_pci_bus_write_config_##name(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, type value) \ +{ \ + rt_err_t err; \ + rt_ubase_t level; \ + pci_lock_config(level); \ + err = bus->ops->write(bus, devfn, reg, sizeof(type), value); \ + pci_unlock_config(level); \ + return err; \ +} + +#define PCI_OPS(name, type) \ + PCI_OPS_READ(name, type) \ + PCI_OPS_WRITE(name, type) + +PCI_OPS(u8, rt_uint8_t) +PCI_OPS(u16, rt_uint16_t) +PCI_OPS(u32, rt_uint32_t) + +#undef PCI_OP_WRITE +#undef PCI_OP_READ +#undef PCI_OPS + +rt_err_t rt_pci_bus_read_config_uxx(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t *value) +{ + void *base; + + if ((base = bus->ops->map(bus, devfn, reg))) + { + if (width == 1) + { + *value = HWREG8(base); + } + else if (width == 2) + { + *value = HWREG16(base); + } + else + { + *value = HWREG32(base); + } + + return RT_EOK; + } + + return -RT_ERROR; +} + +rt_err_t rt_pci_bus_write_config_uxx(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t value) +{ + void *base; + + if ((base = bus->ops->map(bus, devfn, reg))) + { + if (width == 1) + { + HWREG8(base) = value; + } + else if (width == 2) + { + HWREG16(base) = value; + } + else + { + HWREG32(base) = value; + } + + return RT_EOK; + } + + return -RT_ERROR; +} + +rt_err_t rt_pci_bus_read_config_generic_u32(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t *value) +{ + void *base; + + if ((base = bus->ops->map(bus, devfn, reg))) + { + *value = HWREG32(base); + + if (width <= 2) + { + *value = (*value >> (8 * (reg & 3))) & ((1 << (width * 8)) - 1); + } + + return RT_EOK; + } + + return -RT_ERROR; +} + +rt_err_t rt_pci_bus_write_config_generic_u32(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t value) +{ + void *base; + + if ((base = bus->ops->map(bus, devfn, reg & ~0x3))) + { + if (width == 4) + { + HWREG32(base) = value; + } + else + { + rt_uint32_t mask, tmp; + + mask = ~(((1 << (width * 8)) - 1) << ((reg & 0x3) * 8)); + tmp = HWREG32(base) & mask; + tmp |= value << ((reg & 0x3) * 8); + HWREG32(base) = tmp; + } + + return RT_EOK; + } + + return -RT_ERROR; +} diff --git a/rt-thread/components/drivers/pci/ecam.c b/rt-thread/components/drivers/pci/ecam.c new file mode 100644 index 0000000..f5813f3 --- /dev/null +++ b/rt-thread/components/drivers/pci/ecam.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "pci.ecam" +#define DBG_LVL DBG_INFO +#include + +#include "ecam.h" + +struct pci_ecam_config_window *pci_ecam_create(struct rt_pci_host_bridge *host_bridge, + const struct pci_ecam_ops *ops) +{ + struct pci_ecam_config_window *conf_win = rt_calloc(1, sizeof(*conf_win)); + + if (!conf_win) + { + return RT_NULL; + } + + conf_win->bus_range = host_bridge->bus_range; + conf_win->bus_shift = ops->bus_shift; + conf_win->ops = ops; + + host_bridge->ops = (const struct rt_pci_ops *)&ops->pci_ops; + + return conf_win; +} + +void *pci_ecam_map(struct rt_pci_bus *bus, rt_uint32_t devfn, int where) +{ + struct pci_ecam_config_window *conf_win = bus->sysdata; + const struct pci_ecam_ops *eops = conf_win->ops; + void *win = conf_win->win, *map; + rt_uint32_t busn = bus->number, bus_shift = eops->bus_shift, devfn_shift = bus_shift - 8; + + busn -= conf_win->bus_range[0]; + + if (bus_shift) + { + rt_uint32_t bus_offset = (busn & PCIE_ECAM_BUS_MASK) << bus_shift; + rt_uint32_t devfn_offset = (devfn & PCIE_ECAM_DEVFN_MASK) << devfn_shift; + + where &= PCIE_ECAM_REG_MASK; + map = win + (bus_offset | devfn_offset | where); + } + else + { + map = win + PCIE_ECAM_OFFSET(busn, devfn, where); + } + + return map; +} + +const struct pci_ecam_ops pci_generic_ecam_ops = +{ + .pci_ops = + { + .map = pci_ecam_map, + .read = rt_pci_bus_read_config_uxx, + .write = rt_pci_bus_write_config_uxx, + } +}; diff --git a/rt-thread/components/drivers/pci/ecam.h b/rt-thread/components/drivers/pci/ecam.h new file mode 100644 index 0000000..98fa3eb --- /dev/null +++ b/rt-thread/components/drivers/pci/ecam.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#ifndef __RT_PCI_ECAM_H__ +#define __RT_PCI_ECAM_H__ + +#include +#include +#include +#include + +/* + * Memory address shift values for the byte-level address that + * can be used when accessing the PCI Express Configuration Space. + */ + +/* + * Enhanced Configuration Access Mechanism (ECAM) + * + * See PCI Express Base Specification, Revision 5.0, Version 1.0, + * Section 7.2.2, Table 7-1, p. 677. + */ +#define PCIE_ECAM_BUS_SHIFT 20 /* Bus number */ +#define PCIE_ECAM_DEVFN_SHIFT 12 /* Device and Function number */ + +#define PCIE_ECAM_BUS_MASK 0xff +#define PCIE_ECAM_DEVFN_MASK 0xff +#define PCIE_ECAM_REG_MASK 0xfff /* Limit offset to a maximum of 4K */ + +#define PCIE_ECAM_BUS(x) (((x) & PCIE_ECAM_BUS_MASK) << PCIE_ECAM_BUS_SHIFT) +#define PCIE_ECAM_DEVFN(x) (((x) & PCIE_ECAM_DEVFN_MASK) << PCIE_ECAM_DEVFN_SHIFT) +#define PCIE_ECAM_REG(x) ((x) & PCIE_ECAM_REG_MASK) + +#define PCIE_ECAM_OFFSET(bus, devfn, where) \ + (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEVFN(devfn) | PCIE_ECAM_REG(where)) + +struct pci_ecam_ops +{ + rt_uint32_t bus_shift; + const struct rt_pci_ops pci_ops; +}; + +struct pci_ecam_config_window +{ + rt_uint32_t *bus_range; + rt_uint32_t bus_shift; + + void *win; + void *priv; + const struct pci_ecam_ops *ops; +}; + +/* Default ECAM ops */ +extern const struct pci_ecam_ops pci_generic_ecam_ops; + +void *pci_ecam_map(struct rt_pci_bus *bus, rt_uint32_t devfn, int where); +struct pci_ecam_config_window *pci_ecam_create(struct rt_pci_host_bridge *host_bridge, + const struct pci_ecam_ops *ops); +rt_err_t pci_host_common_probe(struct rt_platform_device *pdev); +rt_err_t pci_host_common_remove(struct rt_platform_device *pdev); + +#endif /* __RT_PCI_ECAM_H__ */ diff --git a/rt-thread/components/drivers/pci/endpoint/SConscript b/rt-thread/components/drivers/pci/endpoint/SConscript new file mode 100644 index 0000000..9b76a96 --- /dev/null +++ b/rt-thread/components/drivers/pci/endpoint/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_PCI_ENDPOINT']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = ['endpoint.c', 'mem.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/pci/endpoint/endpoint.c b/rt-thread/components/drivers/pci/endpoint/endpoint.c new file mode 100644 index 0000000..3783f4e --- /dev/null +++ b/rt-thread/components/drivers/pci/endpoint/endpoint.c @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#include + +#define DBG_TAG "pci.ep" +#define DBG_LVL DBG_INFO +#include + +static rt_list_t _ep_nodes = RT_LIST_OBJECT_INIT(_ep_nodes); +static struct rt_spinlock _ep_lock = { 0 }; + +rt_err_t rt_pci_ep_write_header(struct rt_pci_ep *ep, rt_uint8_t func_no, + struct rt_pci_ep_header *hdr) +{ + rt_err_t err; + + if (ep && ep->ops && hdr && func_no < ep->max_functions) + { + if (ep->ops->write_header) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->write_header(ep, func_no, hdr); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_set_bar(struct rt_pci_ep *ep, rt_uint8_t func_no, + struct rt_pci_ep_bar *bar, int bar_idx) +{ + rt_err_t err = RT_EOK; + + if (ep && ep->ops && func_no < ep->max_functions && bar && + bar_idx < PCI_STD_NUM_BARS) + { + struct rt_pci_bus_resource *bus_bar = &bar->bus; + + if (bar_idx == (PCI_STD_NUM_BARS - 1) && + (bus_bar->flags & PCIM_BAR_MEM_TYPE_64)) + { + err = -RT_EINVAL; + LOG_E("%s: Set BAR[%d] can't not 64bit", ep->name, bar_idx); + } + + if (rt_upper_32_bits(bus_bar->size) && + !(bus_bar->flags & PCIM_BAR_MEM_TYPE_64)) + { + err = -RT_EINVAL; + LOG_E("%s: Set BAR[%d] size is no support 64bit", ep->name, bar_idx); + } + + if ((bus_bar->flags & PCIM_BAR_SPACE_IO) && + (bus_bar->flags & PCIM_BAR_IO_MASK)) + { + err = -RT_EINVAL; + LOG_E("%s: Set BAR[%d] io flags is invalid", ep->name, bar_idx); + } + + if (!err) + { + if (ep->ops->set_bar) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->set_bar(ep, func_no, bar, bar_idx); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_clear_bar(struct rt_pci_ep *ep, rt_uint8_t func_no, + struct rt_pci_ep_bar *bar, int bar_idx) +{ + rt_err_t err; + + if (ep && ep->ops && func_no < ep->max_functions && bar && + bar_idx < PCI_STD_NUM_BARS) + { + if (ep->ops->clear_bar) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->clear_bar(ep, func_no, bar, bar_idx); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_map_addr(struct rt_pci_ep *ep, rt_uint8_t func_no, + rt_ubase_t addr, rt_uint64_t pci_addr, rt_size_t size) +{ + rt_err_t err; + + if (ep && ep->ops && func_no < ep->max_functions && size) + { + if (ep->ops->map_addr) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->map_addr(ep, func_no, addr, pci_addr, size); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_unmap_addr(struct rt_pci_ep *ep, rt_uint8_t func_no, + rt_ubase_t addr) +{ + rt_err_t err; + + if (ep && ep->ops && func_no < ep->max_functions) + { + if (ep->ops->unmap_addr) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->unmap_addr(ep, func_no, addr); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_set_msi(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned irq_nr) +{ + rt_err_t err; + + if (ep && ep->ops && func_no < ep->max_functions) + { + if (ep->ops->set_msix) + { + err = -RT_EINVAL; + + for (int log2 = 0; log2 < 5; ++log2) + { + if (irq_nr <= (1 << log2)) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->set_msi(ep, func_no, log2); + rt_mutex_release(&ep->lock); + } + } + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_get_msi(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned *out_irq_nr) +{ + rt_err_t err; + + if (ep && ep->ops && func_no < ep->max_functions && out_irq_nr) + { + if (ep->ops->get_msi) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->get_msi(ep, func_no, out_irq_nr); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_set_msix(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned irq_nr, int bar_idx, rt_off_t offset) +{ + rt_err_t err; + + if (ep && ep->ops && func_no < ep->max_functions && irq_nr < 2048 && + bar_idx < PCI_STD_NUM_BARS) + { + if (ep->ops->set_msix) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->set_msix(ep, func_no, irq_nr, bar_idx, offset); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_get_msix(struct rt_pci_ep *ep, rt_uint8_t func_no, + unsigned *out_irq_nr) +{ + rt_err_t err; + + if (ep && ep->ops && func_no < ep->max_functions && out_irq_nr) + { + if (ep->ops->get_msix) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->get_msix(ep, func_no, out_irq_nr); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_raise_irq(struct rt_pci_ep *ep, rt_uint8_t func_no, + enum rt_pci_ep_irq type, unsigned irq) +{ + rt_err_t err; + + if (ep && ep->ops && func_no < ep->max_functions) + { + if (ep->ops->raise_irq) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->raise_irq(ep, func_no, type, irq); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_start(struct rt_pci_ep *ep) +{ + rt_err_t err; + + if (ep && ep->ops) + { + if (ep->ops->start) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->start(ep); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_stop(struct rt_pci_ep *ep) +{ + rt_err_t err; + + if (ep && ep->ops) + { + if (ep->ops->stop) + { + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + err = ep->ops->stop(ep); + rt_mutex_release(&ep->lock); + } + else + { + err = -RT_ENOSYS; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_ep_register(struct rt_pci_ep *ep) +{ + rt_ubase_t level; + + if (!ep || !ep->ops) + { + return -RT_EINVAL; + } + + rt_list_init(&ep->list); + rt_ref_init(&ep->ref); + + rt_list_init(&ep->epf_nodes); + rt_mutex_init(&ep->lock, ep->name, RT_IPC_FLAG_PRIO); + + level = rt_spin_lock_irqsave(&_ep_lock); + rt_list_insert_before(&_ep_nodes, &ep->list); + rt_spin_unlock_irqrestore(&_ep_lock, level); + + return RT_EOK; +} + +rt_err_t rt_pci_ep_unregister(struct rt_pci_ep *ep) +{ + rt_ubase_t level; + rt_err_t err = RT_EOK; + + if (!ep) + { + return -RT_EINVAL; + } + + level = rt_spin_lock_irqsave(&_ep_lock); + + if (rt_ref_read(&ep->ref) > 1) + { + err = -RT_EBUSY; + } + else + { + rt_list_remove(&ep->list); + rt_mutex_detach(&ep->lock); + } + + rt_spin_unlock_irqrestore(&_ep_lock, level); + + return err; +} + +rt_err_t rt_pci_ep_add_epf(struct rt_pci_ep *ep, struct rt_pci_epf *epf) +{ + rt_err_t err = RT_EOK; + + if (!ep || !epf || !epf->name) + { + return -RT_EINVAL; + } + + if (epf->func_no > ep->max_functions - 1) + { + LOG_E("%s function No(%d) > %s max function No(%d - 1)", + epf->name, epf->func_no, ep->name, ep->max_functions); + + return -RT_EINVAL; + } + + epf->ep = ep; + rt_list_init(&epf->list); + + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + + if (!rt_bitmap_test_bit(ep->functions_map, epf->func_no)) + { + rt_bitmap_set_bit(ep->functions_map, epf->func_no); + rt_list_insert_before(&ep->epf_nodes, &epf->list); + } + else + { + err = -RT_EINVAL; + LOG_E("%s function No(%d) is repeating", epf->name, epf->func_no); + } + + rt_mutex_release(&ep->lock); + + return err; +} + +rt_err_t rt_pci_ep_remove_epf(struct rt_pci_ep *ep, struct rt_pci_epf *epf) +{ + if (!ep || !epf) + { + return -RT_EINVAL; + } + + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + rt_bitmap_clear_bit(ep->functions_map, epf->func_no); + rt_list_remove(&epf->list); + rt_mutex_release(&ep->lock); + + return RT_EOK; +} + +struct rt_pci_ep *rt_pci_ep_get(const char *name) +{ + rt_ubase_t level; + struct rt_pci_ep *ep = RT_NULL, *ep_tmp; + + level = rt_spin_lock_irqsave(&_ep_lock); + + rt_list_for_each_entry(ep_tmp, &_ep_nodes, list) + { + if (!name || !rt_strcmp(ep_tmp->name, name)) + { + ep = ep_tmp; + rt_ref_get(&ep->ref); + break; + } + } + + rt_spin_unlock_irqrestore(&_ep_lock, level); + + return ep; +} + +static void pci_ep_release(struct rt_ref *ref) +{ + struct rt_pci_ep *ep = rt_container_of(ref, struct rt_pci_ep, ref); + + rt_pci_ep_unregister(ep); +} + +void rt_pci_ep_put(struct rt_pci_ep *ep) +{ + if (ep) + { + rt_ref_put(&ep->ref, &pci_ep_release); + } +} diff --git a/rt-thread/components/drivers/pci/endpoint/mem.c b/rt-thread/components/drivers/pci/endpoint/mem.c new file mode 100644 index 0000000..297db16 --- /dev/null +++ b/rt-thread/components/drivers/pci/endpoint/mem.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-25 GuEe-GUI first version + */ + +#include + +#define DBG_TAG "pci.ep.mem" +#define DBG_LVL DBG_INFO +#include + +rt_err_t rt_pci_ep_mem_array_init(struct rt_pci_ep *ep, + struct rt_pci_ep_mem *mems, rt_size_t mems_nr) +{ + rt_size_t idx; + rt_err_t err = RT_EOK; + + if (!ep || !mems) + { + return -RT_EINVAL; + } + + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + + ep->mems_nr = mems_nr; + ep->mems = rt_calloc(mems_nr, sizeof(*ep->mems)); + + if (!ep->mems) + { + return -RT_ENOMEM; + } + + for (idx = 0; idx < mems_nr; ++idx) + { + struct rt_pci_ep_mem *mem = &ep->mems[idx]; + + mem->cpu_addr = mems->cpu_addr; + mem->size = mems->size; + mem->page_size = mems->page_size; + mem->bits = mems->size / mems->page_size; + mem->map = rt_calloc(RT_BITMAP_LEN(mem->bits), sizeof(*mem->map)); + + if (!mem->map) + { + err = -RT_ENOMEM; + goto _out_lock; + } + } + +_out_lock: + if (err) + { + while (idx --> 0) + { + rt_free(ep->mems[idx].map); + } + rt_free(ep->mems); + + ep->mems_nr = 0; + ep->mems = RT_NULL; + } + + rt_mutex_release(&ep->lock); + + return err; +} + +rt_err_t rt_pci_ep_mem_init(struct rt_pci_ep *ep, + rt_ubase_t cpu_addr, rt_size_t size, rt_size_t page_size) +{ + struct rt_pci_ep_mem mem; + + if (!ep) + { + return -RT_EINVAL; + } + + mem.cpu_addr = cpu_addr; + mem.size = size; + mem.page_size = page_size; + + return rt_pci_ep_mem_array_init(ep, &mem, 1); +} + +static rt_ubase_t bitmap_region_alloc(struct rt_pci_ep_mem *mem, rt_size_t size) +{ + rt_size_t bit, next_bit, end_bit, max_bits; + + size /= mem->page_size; + max_bits = mem->bits - size; + + rt_bitmap_for_each_clear_bit(mem->map, bit, max_bits) + { + end_bit = bit + size; + + for (next_bit = bit + 1; next_bit < end_bit; ++next_bit) + { + if (rt_bitmap_test_bit(mem->map, next_bit)) + { + bit = next_bit; + goto _next; + } + } + + if (next_bit == end_bit) + { + while (next_bit --> bit) + { + rt_bitmap_set_bit(mem->map, next_bit); + } + + return mem->cpu_addr + bit * mem->page_size; + } + _next: + } + + return ~0ULL; +} + +static void bitmap_region_free(struct rt_pci_ep_mem *mem, + rt_ubase_t cpu_addr, rt_size_t size) +{ + rt_size_t bit = (cpu_addr - mem->cpu_addr) / mem->page_size, end_bit; + + size /= mem->page_size; + end_bit = bit + size; + + for (; bit < end_bit; ++bit) + { + rt_bitmap_clear_bit(mem->map, bit); + } +} + +void *rt_pci_ep_mem_alloc(struct rt_pci_ep *ep, + rt_ubase_t *out_cpu_addr, rt_size_t size) +{ + void *vaddr = RT_NULL; + + if (!ep || !out_cpu_addr) + { + return vaddr; + } + + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + + for (rt_size_t idx = 0; idx < ep->mems_nr; ++idx) + { + rt_ubase_t cpu_addr; + struct rt_pci_ep_mem *mem = &ep->mems[idx]; + + cpu_addr = bitmap_region_alloc(mem, size); + + if (cpu_addr != ~0ULL) + { + vaddr = rt_ioremap((void *)cpu_addr, size); + + if (!vaddr) + { + bitmap_region_free(mem, cpu_addr, size); + + /* Try next memory */ + continue; + } + + *out_cpu_addr = cpu_addr; + break; + } + } + + rt_mutex_release(&ep->lock); + + return vaddr; +} + +void rt_pci_ep_mem_free(struct rt_pci_ep *ep, + void *vaddr, rt_ubase_t cpu_addr, rt_size_t size) +{ + if (!ep || !vaddr || !size) + { + return; + } + + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + + for (rt_size_t idx = 0; idx < ep->mems_nr; ++idx) + { + struct rt_pci_ep_mem *mem = &ep->mems[idx]; + + if (mem->cpu_addr > cpu_addr && + mem->cpu_addr + mem->size >= cpu_addr + size) + { + rt_iounmap(mem); + bitmap_region_free(mem, cpu_addr, size); + + break; + } + } + + rt_mutex_release(&ep->lock); +} diff --git a/rt-thread/components/drivers/pci/host-bridge.c b/rt-thread/components/drivers/pci/host-bridge.c new file mode 100644 index 0000000..ada7ef6 --- /dev/null +++ b/rt-thread/components/drivers/pci/host-bridge.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +#include +#include + +#ifdef RT_USING_PM +struct host_bridge_pm_status +{ + rt_uint8_t mode; + rt_bool_t enable; +}; + +static const enum rt_pci_power system_pci_pm_mode[] = +{ + [PM_SLEEP_MODE_NONE] = RT_PCI_D0, + [PM_SLEEP_MODE_IDLE] = RT_PCI_D3HOT, + [PM_SLEEP_MODE_LIGHT] = RT_PCI_D1, + [PM_SLEEP_MODE_DEEP] = RT_PCI_D1, + [PM_SLEEP_MODE_STANDBY] = RT_PCI_D2, + [PM_SLEEP_MODE_SHUTDOWN] = RT_PCI_D3COLD, +}; + +static rt_bool_t pci_device_pm_ops(struct rt_pci_device *pdev, void *data) +{ + struct host_bridge_pm_status *status = data; + + rt_pci_enable_wake(pdev, system_pci_pm_mode[status->mode], status->enable); + + /* To find all devices, always return false */ + return RT_FALSE; +} + +static rt_err_t host_bridge_pm_suspend(const struct rt_device *device, rt_uint8_t mode) +{ + struct host_bridge_pm_status status; + struct rt_pci_device *pdev = rt_container_of(device, struct rt_pci_device, parent); + + status.mode = mode; + status.enable = RT_FALSE; + rt_pci_enum_device(pdev->bus, pci_device_pm_ops, &status); + + return RT_EOK; +} + +static void host_bridge_pm_resume(const struct rt_device *device, rt_uint8_t mode) +{ + struct host_bridge_pm_status status; + struct rt_pci_device *pdev = rt_container_of(device, struct rt_pci_device, parent); + + status.mode = mode; + status.enable = RT_TRUE; + rt_pci_enum_device(pdev->bus, pci_device_pm_ops, &status); +} + +static const struct rt_device_pm_ops host_bridge_pm_ops = +{ + .suspend = host_bridge_pm_suspend, + .resume = host_bridge_pm_resume, +}; +#endif /* RT_USING_PM */ + +static void host_bridge_free(struct rt_pci_device *pdev) +{ +#ifdef RT_USING_PM + rt_pm_device_unregister(&pdev->parent); +#endif +} + +static rt_err_t host_bridge_probe(struct rt_pci_device *pdev) +{ + rt_err_t err = RT_EOK; + + rt_pci_set_master(pdev); + +#ifdef RT_USING_PM + rt_pm_device_register(&pdev->parent, &host_bridge_pm_ops); +#endif + + return err; +} + +static rt_err_t host_bridge_remove(struct rt_pci_device *pdev) +{ + host_bridge_free(pdev); + rt_pci_clear_master(pdev); + + return RT_EOK; +} + +static rt_err_t host_bridge_shutdown(struct rt_pci_device *pdev) +{ + host_bridge_free(pdev); + + return RT_EOK; +} + +static const struct rt_pci_device_id host_bridge_pci_ids[] = +{ + /* PCI host bridges */ + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0008) }, + /* Any PCI-Express port */ + { RT_PCI_DEVICE_CLASS(PCIS_BRIDGE_PCI_NORMAL, ~0) }, + /* PCI-to-PCI bridge */ + { RT_PCI_DEVICE_CLASS(PCIS_BRIDGE_PCI_SUBTRACTIVE, ~0) }, + /* Any Root Complex Event Collector */ + { RT_PCI_DEVICE_CLASS(((PCIS_SYSTEM_RCEC << 8) | 0x00), ~0) }, + { /* sentinel */ } +}; + +static struct rt_pci_driver host_bridge_driver = +{ + .name = "host-bridge", + + .ids = host_bridge_pci_ids, + .probe = host_bridge_probe, + .remove = host_bridge_remove, + .shutdown = host_bridge_shutdown, +}; +RT_PCI_DRIVER_EXPORT(host_bridge_driver); diff --git a/rt-thread/components/drivers/pci/host/Kconfig b/rt-thread/components/drivers/pci/host/Kconfig new file mode 100644 index 0000000..221c997 --- /dev/null +++ b/rt-thread/components/drivers/pci/host/Kconfig @@ -0,0 +1,12 @@ +config RT_PCI_HOST_COMMON + bool "Common PCI host controller" + depends on RT_PCI_ECAM + default y + +config RT_PCI_HOST_GENERIC + bool "Generic PCI host controller" + depends on RT_PCI_ECAM + select RT_PCI_HOST_COMMON + default y + +rsource "dw/Kconfig" diff --git a/rt-thread/components/drivers/pci/host/SConscript b/rt-thread/components/drivers/pci/host/SConscript new file mode 100644 index 0000000..91555f8 --- /dev/null +++ b/rt-thread/components/drivers/pci/host/SConscript @@ -0,0 +1,25 @@ +from building import * + +objs = [] + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../../include'] + +src = [] + +if GetDepend(['RT_PCI_HOST_COMMON']): + src += ['pci-host-common.c'] + +if GetDepend(['RT_PCI_HOST_GENERIC']): + src += ['pci-host-generic.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') diff --git a/rt-thread/components/drivers/pci/host/dw/Kconfig b/rt-thread/components/drivers/pci/host/dw/Kconfig new file mode 100644 index 0000000..e76011b --- /dev/null +++ b/rt-thread/components/drivers/pci/host/dw/Kconfig @@ -0,0 +1,13 @@ +config RT_PCI_DW + bool "DesignWare-based PCIe" + depends on RT_MFD_SYSCON + depends on RT_USING_DMA + default n + +config RT_PCI_DW_HOST + bool + depends on RT_PCI_DW + +config RT_PCI_DW_EP + bool + depends on RT_PCI_DW diff --git a/rt-thread/components/drivers/pci/host/dw/SConscript b/rt-thread/components/drivers/pci/host/dw/SConscript new file mode 100644 index 0000000..ca6c031 --- /dev/null +++ b/rt-thread/components/drivers/pci/host/dw/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_PCI_DW']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../../include'] + +src = ['pcie-dw.c', 'pcie-dw_platfrom.c'] + +if GetDepend(['RT_PCI_DW_HOST']): + src += ['pcie-dw_host.c'] + +if GetDepend(['RT_PCI_DW_EP']): + src += ['pcie-dw_ep.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/pci/host/dw/pcie-dw.c b/rt-thread/components/drivers/pci/host/dw/pcie-dw.c new file mode 100644 index 0000000..2cca6f1 --- /dev/null +++ b/rt-thread/components/drivers/pci/host/dw/pcie-dw.c @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#define DBG_TAG "pcie.dw" +#define DBG_LVL DBG_INFO +#include + +#include "pcie-dw.h" + +static rt_uint8_t __dw_pcie_find_next_cap(struct dw_pcie *pci, + rt_uint8_t cap_ptr, rt_uint8_t cap) +{ + rt_uint16_t reg; + rt_uint8_t cap_id, next_cap_ptr; + + if (!cap_ptr) + { + return 0; + } + + reg = dw_pcie_readw_dbi(pci, cap_ptr); + cap_id = (reg & 0x00ff); + + if (cap_id > PCIY_MAX) + { + return 0; + } + + if (cap_id == cap) + { + return cap_ptr; + } + + next_cap_ptr = (reg & 0xff00) >> 8; + return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap); +} + +rt_uint8_t dw_pcie_find_capability(struct dw_pcie *pci, rt_uint8_t cap) +{ + rt_uint16_t reg; + rt_uint8_t next_cap_ptr; + + reg = dw_pcie_readw_dbi(pci, PCIR_CAP_PTR); + next_cap_ptr = (reg & 0x00ff); + + return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap); +} + +static rt_uint16_t dw_pcie_find_next_ext_capability(struct dw_pcie *pci, + rt_uint16_t start, rt_uint8_t cap) +{ + rt_uint32_t header; + int ttl, pos = PCI_REGMAX + 1; + + /* minimum 8 bytes per capability */ + ttl = ((PCIE_REGMAX + 1) - (PCI_REGMAX + 1)) / 8; + + if (start) + { + pos = start; + } + + header = dw_pcie_readl_dbi(pci, pos); + /* + * If we have no capabilities, this is indicated by cap ID, + * cap version and next pointer all being 0. + */ + if (header == 0) + { + return 0; + } + + while (ttl-- > 0) + { + if (PCI_EXTCAP_ID(header) == cap && pos != start) + { + return pos; + } + + pos = PCI_EXTCAP_NEXTPTR(header); + + if (pos < PCI_REGMAX + 1) + { + break; + } + + header = dw_pcie_readl_dbi(pci, pos); + } + + return 0; +} + +rt_uint16_t dw_pcie_find_ext_capability(struct dw_pcie *pci, rt_uint8_t cap) +{ + return dw_pcie_find_next_ext_capability(pci, 0, cap); +} + +rt_err_t dw_pcie_read(void *addr, rt_size_t size, rt_uint32_t *out_val) +{ + /* Check aligned */ + if ((rt_ubase_t)addr & ((rt_ubase_t)size - 1)) + { + *out_val = 0; + return -RT_EINVAL; + } + + if (size == 4) + { + *out_val = HWREG32(addr); + } + else if (size == 2) + { + *out_val = HWREG16(addr); + } + else if (size == 1) + { + *out_val = HWREG8(addr); + } + else + { + *out_val = 0; + return -RT_EINVAL; + } + + return RT_EOK; +} + +rt_err_t dw_pcie_write(void *addr, rt_size_t size, rt_uint32_t val) +{ + /* Check aligned */ + if ((rt_ubase_t)addr & ((rt_ubase_t)size - 1)) + { + return -RT_EINVAL; + } + + if (size == 4) + { + HWREG32(addr) = val; + } + else if (size == 2) + { + HWREG16(addr) = val; + } + else if (size == 1) + { + HWREG8(addr) = val; + } + else + { + return -RT_EINVAL; + } + + return RT_EOK; +} + +rt_uint32_t dw_pcie_read_dbi(struct dw_pcie *pci, rt_uint32_t reg, rt_size_t size) +{ + rt_err_t err; + rt_uint32_t val = 0; + + if (pci->ops->read_dbi) + { + return pci->ops->read_dbi(pci, pci->dbi_base, reg, size); + } + + if ((err = dw_pcie_read(pci->dbi_base + reg, size, &val))) + { + LOG_E("Read DBI address error = %s", rt_strerror(err)); + } + + return val; +} + +void dw_pcie_write_dbi(struct dw_pcie *pci, rt_uint32_t reg, rt_size_t size, rt_uint32_t val) +{ + rt_err_t err; + + if (pci->ops->write_dbi) + { + pci->ops->write_dbi(pci, pci->dbi_base, reg, size, val); + return; + } + + if ((err = dw_pcie_write(pci->dbi_base + reg, size, val))) + { + LOG_E("Write DBI address error = %s", rt_strerror(err)); + } +} + +void dw_pcie_write_dbi2(struct dw_pcie *pci, rt_uint32_t reg, rt_size_t size, rt_uint32_t val) +{ + rt_err_t err; + + if (pci->ops && pci->ops->write_dbi2) + { + pci->ops->write_dbi2(pci, pci->dbi_base2, reg, size, val); + return; + } + + if ((err = dw_pcie_write(pci->dbi_base2 + reg, size, val))) + { + LOG_E("Write DBI2 address error = %s", rt_strerror(err)); + } +} + +rt_uint32_t dw_pcie_readl_atu(struct dw_pcie *pci, rt_uint32_t reg) +{ + rt_err_t err; + rt_uint32_t val = 0; + + if (pci->ops->read_dbi) + { + return pci->ops->read_dbi(pci, pci->atu_base, reg, 4); + } + + if ((err = dw_pcie_read(pci->atu_base + reg, 4, &val))) + { + LOG_E("Read ATU address error = %s", rt_strerror(err)); + } + + return val; +} + +void dw_pcie_writel_atu(struct dw_pcie *pci, rt_uint32_t reg, rt_uint32_t val) +{ + rt_err_t err; + + if (pci->ops->write_dbi) + { + pci->ops->write_dbi(pci, pci->atu_base, reg, 4, val); + return; + } + + if ((err = dw_pcie_write(pci->atu_base + reg, 4, val))) + { + LOG_E("Write ATU address error = %s", rt_strerror(err)); + } +} + +static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, rt_uint8_t func_no, + int index, int type, rt_uint64_t cpu_addr, rt_uint64_t pci_addr, rt_size_t size) +{ + rt_uint64_t limit_addr = cpu_addr + size - 1; + + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, + rt_lower_32_bits(cpu_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, + rt_upper_32_bits(cpu_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_LIMIT, + rt_lower_32_bits(limit_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_LIMIT, + rt_upper_32_bits(limit_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, + rt_lower_32_bits(pci_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, + rt_upper_32_bits(pci_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, + type | PCIE_ATU_FUNC_NUM(func_no)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, + PCIE_ATU_ENABLE); + + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (int retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; ++retries) + { + if (dw_pcie_readl_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2) & PCIE_ATU_ENABLE) + { + return; + } + + rt_thread_mdelay(LINK_WAIT_IATU); + } + + LOG_E("Outbound iATU is not being enabled"); +} + +static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, rt_uint8_t func_no, + int index, int type, rt_uint64_t cpu_addr, rt_uint64_t pci_addr, rt_size_t size) +{ + if (pci->ops->cpu_addr_fixup) + { + cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr); + } + + if (pci->iatu_unroll_enabled & DWC_IATU_UNROLL_EN) + { + dw_pcie_prog_outbound_atu_unroll(pci, func_no, + index, type, cpu_addr, pci_addr, size); + + return; + } + + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_OUTBOUND | index); + dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, rt_lower_32_bits(cpu_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, rt_upper_32_bits(cpu_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, rt_lower_32_bits(cpu_addr + size - 1)); + dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, rt_lower_32_bits(pci_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, rt_upper_32_bits(pci_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type | PCIE_ATU_FUNC_NUM(func_no)); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE); + + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (int retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; ++retries) + { + if (dw_pcie_readl_dbi(pci, PCIE_ATU_CR2) & PCIE_ATU_ENABLE) + { + return; + } + + rt_thread_mdelay(LINK_WAIT_IATU); + } + + LOG_E("Outbound iATU is not being enabled"); +} + +void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, + int index, int type, rt_uint64_t cpu_addr, rt_uint64_t pci_addr, rt_size_t size) +{ + __dw_pcie_prog_outbound_atu(pci, 0, index, type, cpu_addr, pci_addr, size); +} + +void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, rt_uint8_t func_no, + int index, int type, rt_uint64_t cpu_addr, rt_uint64_t pci_addr, rt_size_t size) +{ + __dw_pcie_prog_outbound_atu(pci, func_no, index, type, cpu_addr, pci_addr, size); +} + +static rt_err_t dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, + rt_uint8_t func_no, int index, int bar, rt_uint64_t cpu_addr, + enum dw_pcie_aspace_type aspace_type) +{ + int type; + + dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, + rt_lower_32_bits(cpu_addr)); + dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, + rt_upper_32_bits(cpu_addr)); + + switch (aspace_type) + { + case DW_PCIE_ASPACE_MEM: + type = PCIE_ATU_TYPE_MEM; + break; + + case DW_PCIE_ASPACE_IO: + type = PCIE_ATU_TYPE_IO; + break; + + default: + return -RT_EINVAL; + } + + dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, + type | PCIE_ATU_FUNC_NUM(func_no)); + dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, + PCIE_ATU_FUNC_NUM_MATCH_EN | PCIE_ATU_ENABLE | + PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); + + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (int retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; ++retries) + { + if (dw_pcie_readl_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2) & PCIE_ATU_ENABLE) + { + return RT_EOK; + } + + rt_thread_mdelay(LINK_WAIT_IATU); + } + + LOG_E("Inbound iATU is not being enabled"); + + return -RT_EBUSY; +} + +rt_err_t dw_pcie_prog_inbound_atu(struct dw_pcie *pci, + rt_uint8_t func_no, int index, int bar, rt_uint64_t cpu_addr, + enum dw_pcie_aspace_type aspace_type) +{ + int type; + + if (pci->iatu_unroll_enabled & DWC_IATU_UNROLL_EN) + { + return dw_pcie_prog_inbound_atu_unroll(pci, func_no, + index, bar, cpu_addr, aspace_type); + } + + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND | index); + dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, rt_lower_32_bits(cpu_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, rt_upper_32_bits(cpu_addr)); + + switch (aspace_type) + { + case DW_PCIE_ASPACE_MEM: + type = PCIE_ATU_TYPE_MEM; + break; + + case DW_PCIE_ASPACE_IO: + type = PCIE_ATU_TYPE_IO; + break; + + default: + return -RT_EINVAL; + } + + dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type | PCIE_ATU_FUNC_NUM(func_no)); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE | + PCIE_ATU_FUNC_NUM_MATCH_EN | PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); + + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (int retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; ++retries) + { + if (dw_pcie_readl_dbi(pci, PCIE_ATU_CR2) & PCIE_ATU_ENABLE) + { + return RT_EOK; + } + + rt_thread_mdelay(LINK_WAIT_IATU); + } + + LOG_E("Inbound iATU is not being enabled"); + + return -RT_EBUSY; +} + +void dw_pcie_disable_atu(struct dw_pcie *pci, int index, enum dw_pcie_region_type type) +{ + rt_uint32_t region; + + switch (type) + { + case DW_PCIE_REGION_INBOUND: + region = PCIE_ATU_REGION_INBOUND; + break; + + case DW_PCIE_REGION_OUTBOUND: + region = PCIE_ATU_REGION_OUTBOUND; + break; + + default: + return; + } + + if (pci->iatu_unroll_enabled) + { + if (region == PCIE_ATU_REGION_INBOUND) + { + dw_pcie_writel_ib_unroll(pci, index, + PCIE_ATU_UNR_REGION_CTRL2, ~(rt_uint32_t)PCIE_ATU_ENABLE); + } + else + { + dw_pcie_writel_ob_unroll(pci, index, + PCIE_ATU_UNR_REGION_CTRL2, ~(rt_uint32_t)PCIE_ATU_ENABLE); + } + } + else + { + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, region | index); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~(rt_uint32_t)PCIE_ATU_ENABLE); + } +} + +rt_err_t dw_pcie_wait_for_link(struct dw_pcie *pci) +{ + /* Check if the link is up or not */ + for (int retries = 0; retries < LINK_WAIT_MAX_RETRIES; ++retries) + { + if (dw_pcie_link_up(pci)) + { + LOG_I("%s: Link up", rt_dm_dev_get_name(pci->dev)); + + return RT_EOK; + } + + rt_hw_us_delay((LINK_WAIT_USLEEP_MIN + LINK_WAIT_USLEEP_MAX) >> 1); + } + + LOG_I("PHY link never came up"); + + return -RT_ETIMEOUT; +} + +rt_bool_t dw_pcie_link_up(struct dw_pcie *pci) +{ + rt_uint32_t val; + + if (pci->ops->link_up) + { + return pci->ops->link_up(pci); + } + + val = HWREG32(pci->dbi_base + PCIE_PORT_DEBUG1); + + return (val & PCIE_PORT_DEBUG1_LINK_UP) && (!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING)); +} + +void dw_pcie_upconfig_setup(struct dw_pcie *pci) +{ + rt_uint32_t val; + + val = dw_pcie_readl_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL); + val |= PORT_MLTI_UPCFG_SUPPORT; + dw_pcie_writel_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL, val); +} + +static void dw_pcie_link_set_max_speed(struct dw_pcie *pci, rt_uint32_t link_gen) +{ + rt_uint32_t cap, ctrl2, link_speed; + rt_uint8_t offset = dw_pcie_find_capability(pci, PCIY_EXPRESS); + + cap = dw_pcie_readl_dbi(pci, offset + PCIER_LINK_CAP); + ctrl2 = dw_pcie_readl_dbi(pci, offset + PCIER_LINK_CTL2); + ctrl2 &= ~PCIEM_LNKCTL2_TLS; + + switch (link_gen) + { + case 1: link_speed = PCIEM_LNKCTL2_TLS_2_5GT; break; + case 2: link_speed = PCIEM_LNKCTL2_TLS_5_0GT; break; + case 3: link_speed = PCIEM_LNKCTL2_TLS_8_0GT; break; + case 4: link_speed = PCIEM_LNKCTL2_TLS_16_0GT; break; + default: + /* Use hardware capability */ + link_speed = RT_FIELD_GET(PCIEM_LINK_CAP_MAX_SPEED, cap); + ctrl2 &= ~PCIEM_LNKCTL2_HASD; + break; + } + + dw_pcie_writel_dbi(pci, offset + PCIER_LINK_CTL2, ctrl2 | link_speed); + + cap &= ~((rt_uint32_t)PCIEM_LINK_CAP_MAX_SPEED); + dw_pcie_writel_dbi(pci, offset + PCIER_LINK_CAP, cap | link_speed); +} + +void dw_pcie_setup(struct dw_pcie *pci) +{ + rt_uint32_t val; + struct rt_device *dev = pci->dev; + + if (pci->version >= 0x480a || (!pci->version && dw_pcie_iatu_unroll_enabled(pci))) + { + pci->iatu_unroll_enabled |= DWC_IATU_UNROLL_EN; + + if (!pci->atu_base) + { + pci->atu_base = rt_dm_dev_iomap_by_name(dev, "atu"); + } + + if (!pci->atu_base) + { + pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET; + } + } + + LOG_D("iATU unroll is %sabled", pci->iatu_unroll_enabled & DWC_IATU_UNROLL_EN ? "en" : "dis"); + + if (pci->link_gen > 0) + { + dw_pcie_link_set_max_speed(pci, pci->link_gen); + } + + /* Configure Gen1 N_FTS */ + if (pci->fts_number[0]) + { + val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR); + val &= ~(PORT_AFR_N_FTS_MASK | PORT_AFR_CC_N_FTS_MASK); + val |= PORT_AFR_N_FTS(pci->fts_number[0]); + val |= PORT_AFR_CC_N_FTS(pci->fts_number[0]); + dw_pcie_writel_dbi(pci, PCIE_PORT_AFR, val); + } + + /* Configure Gen2+ N_FTS */ + if (pci->fts_number[1]) + { + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_N_FTS_MASK; + val |= pci->fts_number[1]; + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + } + + val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); + val &= ~PORT_LINK_FAST_LINK_MODE; + val |= PORT_LINK_DLL_LINK_EN; + dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); + + if (rt_dm_dev_prop_read_bool(dev, "snps,enable-cdm-check")) + { + val = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS); + val |= PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS | PCIE_PL_CHK_REG_CHK_REG_START; + dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val); + } + + rt_dm_dev_prop_read_u32(dev, "num-lanes", &pci->num_lanes); + + if (!pci->num_lanes) + { + LOG_D("Using h/w default number of lanes"); + return; + } + + /* Set the number of lanes */ + val &= ~PORT_LINK_FAST_LINK_MODE; + val &= ~PORT_LINK_MODE_MASK; + switch (pci->num_lanes) + { + case 1: val |= PORT_LINK_MODE_1_LANES; break; + case 2: val |= PORT_LINK_MODE_2_LANES; break; + case 4: val |= PORT_LINK_MODE_4_LANES; break; + case 8: val |= PORT_LINK_MODE_8_LANES; break; + default: + LOG_E("Invail num-lanes = %d", pci->num_lanes); + return; + } + dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); + + /* Set link width speed control register */ + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_LINK_WIDTH_MASK; + switch (pci->num_lanes) + { + case 1: val |= PORT_LOGIC_LINK_WIDTH_1_LANES; break; + case 2: val |= PORT_LOGIC_LINK_WIDTH_2_LANES; break; + case 4: val |= PORT_LOGIC_LINK_WIDTH_4_LANES; break; + case 8: val |= PORT_LOGIC_LINK_WIDTH_8_LANES; break; + } + val |= pci->user_speed; + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); +} diff --git a/rt-thread/components/drivers/pci/host/dw/pcie-dw.h b/rt-thread/components/drivers/pci/host/dw/pcie-dw.h new file mode 100644 index 0000000..0a1183f --- /dev/null +++ b/rt-thread/components/drivers/pci/host/dw/pcie-dw.h @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __PCIE_DESIGNWARE_H__ +#define __PCIE_DESIGNWARE_H__ + +#include +#include + +/* Parameters for the waiting for link up routine */ +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_USLEEP_MIN 90000 +#define LINK_WAIT_USLEEP_MAX 100000 + +/* Parameters for the waiting for iATU enabled routine */ +#define LINK_WAIT_MAX_IATU_RETRIES 5 +#define LINK_WAIT_IATU 9 + +/* Synopsys-specific PCIe configuration registers */ +#define PCIE_PORT_AFR 0x70c +#define PORT_AFR_N_FTS_MASK RT_GENMASK(15, 8) +#define PORT_AFR_N_FTS(n) RT_FIELD_PREP(PORT_AFR_N_FTS_MASK, n) +#define PORT_AFR_CC_N_FTS_MASK RT_GENMASK(23, 16) +#define PORT_AFR_CC_N_FTS(n) RT_FIELD_PREP(PORT_AFR_CC_N_FTS_MASK, n) +#define PORT_AFR_ENTER_ASPM RT_BIT(30) +#define PORT_AFR_L0S_ENTRANCE_LAT_SHIFT 24 +#define PORT_AFR_L0S_ENTRANCE_LAT_MASK RT_GENMASK(26, 24) +#define PORT_AFR_L1_ENTRANCE_LAT_SHIFT 27 +#define PORT_AFR_L1_ENTRANCE_LAT_MASK RT_GENMASK(29, 27) + +#define PCIE_PORT_LINK_CONTROL 0x710 +#define PORT_LINK_LPBK_ENABLE RT_BIT(2) +#define PORT_LINK_DLL_LINK_EN RT_BIT(5) +#define PORT_LINK_FAST_LINK_MODE RT_BIT(7) +#define PORT_LINK_MODE_MASK RT_GENMASK(21, 16) +#define PORT_LINK_MODE(n) RT_FIELD_PREP(PORT_LINK_MODE_MASK, n) +#define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1) +#define PORT_LINK_MODE_2_LANES PORT_LINK_MODE(0x3) +#define PORT_LINK_MODE_4_LANES PORT_LINK_MODE(0x7) +#define PORT_LINK_MODE_8_LANES PORT_LINK_MODE(0xf) + +#define PCIE_PORT_DEBUG0 0x728 +#define PORT_LOGIC_LTSSM_STATE_MASK 0x1f +#define PORT_LOGIC_LTSSM_STATE_L0 0x11 +#define PCIE_PORT_DEBUG1 0x72c +#define PCIE_PORT_DEBUG1_LINK_UP RT_BIT(4) +#define PCIE_PORT_DEBUG1_LINK_IN_TRAINING RT_BIT(29) + +#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80c +#define PORT_LOGIC_N_FTS_MASK RT_GENMASK(7, 0) +#define PORT_LOGIC_SPEED_CHANGE RT_BIT(17) +#define PORT_LOGIC_LINK_WIDTH_MASK RT_GENMASK(12, 8) +#define PORT_LOGIC_LINK_WIDTH(n) RT_FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n) +#define PORT_LOGIC_LINK_WIDTH_1_LANES PORT_LOGIC_LINK_WIDTH(0x1) +#define PORT_LOGIC_LINK_WIDTH_2_LANES PORT_LOGIC_LINK_WIDTH(0x2) +#define PORT_LOGIC_LINK_WIDTH_4_LANES PORT_LOGIC_LINK_WIDTH(0x4) +#define PORT_LOGIC_LINK_WIDTH_8_LANES PORT_LOGIC_LINK_WIDTH(0x8) + +#define PCIE_MSI_ADDR_LO 0x820 +#define PCIE_MSI_ADDR_HI 0x824 +#define PCIE_MSI_INTR0_ENABLE 0x828 +#define PCIE_MSI_INTR0_MASK 0x82c +#define PCIE_MSI_INTR0_STATUS 0x830 + +#define PCIE_PORT_MULTI_LANE_CTRL 0x8c0 +#define PORT_MLTI_UPCFG_SUPPORT RT_BIT(7) + +#define PCIE_ATU_VIEWPORT 0x900 +#define PCIE_ATU_REGION_INBOUND RT_BIT(31) +#define PCIE_ATU_REGION_OUTBOUND 0 +#define PCIE_ATU_CR1 0x904 +#define PCIE_ATU_TYPE_MEM 0x0 +#define PCIE_ATU_TYPE_IO 0x2 +#define PCIE_ATU_TYPE_CFG0 0x4 +#define PCIE_ATU_TYPE_CFG1 0x5 +#define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20) +#define PCIE_ATU_CR2 0x908 +#define PCIE_ATU_ENABLE RT_BIT(31) +#define PCIE_ATU_BAR_MODE_ENABLE RT_BIT(30) +#define PCIE_ATU_FUNC_NUM_MATCH_EN RT_BIT(19) +#define PCIE_ATU_LOWER_BASE 0x90c +#define PCIE_ATU_UPPER_BASE 0x910 +#define PCIE_ATU_LIMIT 0x914 +#define PCIE_ATU_LOWER_TARGET 0x918 +#define PCIE_ATU_BUS(x) RT_FIELD_PREP(RT_GENMASK(31, 24), x) +#define PCIE_ATU_DEV(x) RT_FIELD_PREP(RT_GENMASK(23, 19), x) +#define PCIE_ATU_FUNC(x) RT_FIELD_PREP(RT_GENMASK(18, 16), x) +#define PCIE_ATU_UPPER_TARGET 0x91c + +#define PCIE_MISC_CONTROL_1_OFF 0x8bc +#define PCIE_DBI_RO_WR_EN RT_BIT(0) + +#define PCIE_MSIX_DOORBELL 0x948 +#define PCIE_MSIX_DOORBELL_PF_SHIFT 24 + +#define PCIE_PL_CHK_REG_CONTROL_STATUS 0xb20 +#define PCIE_PL_CHK_REG_CHK_REG_START RT_BIT(0) +#define PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS RT_BIT(1) +#define PCIE_PL_CHK_REG_CHK_REG_COMPARISON_ERROR RT_BIT(16) +#define PCIE_PL_CHK_REG_CHK_REG_LOGIC_ERROR RT_BIT(17) +#define PCIE_PL_CHK_REG_CHK_REG_COMPLETE RT_BIT(18) + +#define PCIE_PL_CHK_REG_ERR_ADDR 0xb28 + +/* + * iATU Unroll-specific register definitions + * From 4.80 core version the address translation will be made by unroll + */ +#define PCIE_ATU_UNR_REGION_CTRL1 0x00 +#define PCIE_ATU_UNR_REGION_CTRL2 0x04 +#define PCIE_ATU_UNR_LOWER_BASE 0x08 +#define PCIE_ATU_UNR_UPPER_BASE 0x0C +#define PCIE_ATU_UNR_LOWER_LIMIT 0x10 +#define PCIE_ATU_UNR_LOWER_TARGET 0x14 +#define PCIE_ATU_UNR_UPPER_TARGET 0x18 +#define PCIE_ATU_UNR_UPPER_LIMIT 0x20 + +/* + * The default address offset between dbi_base and atu_base. Root controller + * drivers are not required to initialize atu_base if the offset matches this + * default; the driver core automatically derives atu_base from dbi_base using + * this offset, if atu_base not set. + */ +#define DEFAULT_DBI_ATU_OFFSET (0x3 << 20) + +/* Register address builder */ +#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) ((region) << 9) +#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) (((region) << 9) | RT_BIT(8)) + +#define MAX_MSI_IRQS 256 +#define MAX_MSI_IRQS_PER_CTRL 32 +#define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL) +#define MSI_REG_CTRL_BLOCK_SIZE 12 +#define MSI_DEF_NUM_VECTORS 32 + +/* Maximum number of inbound/outbound iATUs */ +#define MAX_IATU_IN 256 +#define MAX_IATU_OUT 256 + +#define DWC_IATU_UNROLL_EN RT_BIT(0) +#define DWC_IATU_IOCFG_SHARED RT_BIT(1) + +struct dw_pcie_host_ops; +struct dw_pcie_ep_ops; +struct dw_pcie_ops; + +enum dw_pcie_region_type +{ + DW_PCIE_REGION_UNKNOWN, + DW_PCIE_REGION_INBOUND, + DW_PCIE_REGION_OUTBOUND, +}; + +enum dw_pcie_device_mode +{ + DW_PCIE_UNKNOWN_TYPE, + DW_PCIE_EP_TYPE, + DW_PCIE_LEG_EP_TYPE, + DW_PCIE_RC_TYPE, +}; + +enum dw_pcie_aspace_type +{ + DW_PCIE_ASPACE_UNKNOWN, + DW_PCIE_ASPACE_MEM, + DW_PCIE_ASPACE_IO, +}; + +struct dw_pcie_port +{ + void *cfg0_base; + rt_uint64_t cfg0_addr; + rt_uint64_t cfg0_size; + + rt_ubase_t io_addr; + rt_ubase_t io_bus_addr; + rt_size_t io_size; + + const struct dw_pcie_host_ops *ops; + + int sys_irq; + int msi_irq; + struct rt_pic *irq_pic; + struct rt_pic *msi_pic; + + void *msi_data; + rt_ubase_t msi_data_phy; + + rt_uint32_t irq_count; + rt_uint32_t irq_mask[MAX_MSI_CTRLS]; + + struct rt_pci_host_bridge *bridge; + const struct rt_pci_ops *bridge_child_ops; + + struct rt_spinlock lock; + RT_BITMAP_DECLARE(msi_map, MAX_MSI_IRQS); +}; + +struct dw_pcie_host_ops +{ + rt_err_t (*host_init)(struct dw_pcie_port *port); + rt_err_t (*msi_host_init)(struct dw_pcie_port *port); + void (*set_irq_count)(struct dw_pcie_port *port); +}; + +struct dw_pcie_ep_func +{ + rt_list_t list; + + rt_uint8_t func_no; + rt_uint8_t msi_cap; /* MSI capability offset */ + rt_uint8_t msix_cap; /* MSI-X capability offset */ +}; + +struct dw_pcie_ep +{ + struct rt_pci_ep *epc; + struct rt_pci_ep_bar *epc_bar[PCI_STD_NUM_BARS]; + + rt_list_t func_nodes; + + const struct dw_pcie_ep_ops *ops; + + rt_uint64_t aspace; + rt_uint64_t aspace_size; + rt_size_t page_size; + + rt_uint8_t bar_to_atu[PCI_STD_NUM_BARS]; + rt_ubase_t *outbound_addr; + + rt_bitmap_t *ib_window_map; + rt_bitmap_t *ob_window_map; + rt_uint32_t num_ib_windows; + rt_uint32_t num_ob_windows; + + void *msi_mem; + rt_ubase_t msi_mem_phy; +}; + +struct dw_pcie_ep_ops +{ + rt_err_t (*ep_init)(struct dw_pcie_ep *ep); + rt_err_t (*raise_irq)(struct dw_pcie_ep *ep, rt_uint8_t func_no, enum rt_pci_ep_irq type, unsigned irq); + rt_off_t (*func_select)(struct dw_pcie_ep *ep, rt_uint8_t func_no); +}; + +struct dw_pcie +{ + struct rt_device *dev; + + void *dbi_base; + void *dbi_base2; + void *atu_base; + + rt_uint32_t version; + rt_uint32_t num_viewport; + rt_uint32_t num_lanes; + rt_uint32_t link_gen; + rt_uint32_t user_speed; + rt_uint8_t iatu_unroll_enabled; /* Internal Address Translation Unit */ + rt_uint8_t fts_number[2]; /* Fast Training Sequences */ + + struct dw_pcie_port port; + struct dw_pcie_ep endpoint; + const struct dw_pcie_ops *ops; + + void *priv; +}; + +struct dw_pcie_ops +{ + rt_uint64_t (*cpu_addr_fixup)(struct dw_pcie *pcie, rt_uint64_t cpu_addr); + rt_uint32_t (*read_dbi)(struct dw_pcie *pcie, void *base, rt_uint32_t reg, rt_size_t size); + void (*write_dbi)(struct dw_pcie *pcie, void *base, rt_uint32_t reg, rt_size_t size, rt_uint32_t val); + void (*write_dbi2)(struct dw_pcie *pcie, void *base, rt_uint32_t reg, rt_size_t size, rt_uint32_t val); + rt_bool_t (*link_up)(struct dw_pcie *pcie); + rt_err_t (*start_link)(struct dw_pcie *pcie); + void (*stop_link)(struct dw_pcie *pcie); +}; + +#define to_dw_pcie_from_port(ptr) rt_container_of((ptr), struct dw_pcie, port) +#define to_dw_pcie_from_endpoint(ptr) rt_container_of((ptr), struct dw_pcie, endpoint) + +#ifdef RT_PCI_DW_HOST +#undef RT_PCI_DW_HOST +#define RT_PCI_DW_HOST 1 +#define HOST_API +#define HOST_RET(...) ; +#else +#define HOST_API rt_inline +#define HOST_RET(...) { return __VA_ARGS__; } +#endif + +#ifdef RT_PCI_DW_EP +#undef RT_PCI_DW_EP +#define RT_PCI_DW_EP 1 +#define EP_API +#define EP_RET(...) ; +#else +#define EP_API rt_inline +#define EP_RET(...) { return __VA_ARGS__; } +#endif + +rt_uint8_t dw_pcie_find_capability(struct dw_pcie *pci, rt_uint8_t cap); +rt_uint16_t dw_pcie_find_ext_capability(struct dw_pcie *pci, rt_uint8_t cap); + +rt_err_t dw_pcie_read(void *addr, rt_size_t size, rt_uint32_t *out_val); +rt_err_t dw_pcie_write(void *addr, rt_size_t size, rt_uint32_t val); + +rt_uint32_t dw_pcie_read_dbi(struct dw_pcie *pci, rt_uint32_t reg, rt_size_t size); +void dw_pcie_write_dbi(struct dw_pcie *pci, rt_uint32_t reg, rt_size_t size, rt_uint32_t val); +void dw_pcie_write_dbi2(struct dw_pcie *pci, rt_uint32_t reg, rt_size_t size, rt_uint32_t val); +rt_uint32_t dw_pcie_readl_atu(struct dw_pcie *pci, rt_uint32_t reg); +void dw_pcie_writel_atu(struct dw_pcie *pci, rt_uint32_t reg, rt_uint32_t val); +rt_bool_t dw_pcie_link_up(struct dw_pcie *pci); +void dw_pcie_upconfig_setup(struct dw_pcie *pci); +rt_err_t dw_pcie_wait_for_link(struct dw_pcie *pci); +void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, rt_uint64_t cpu_addr, rt_uint64_t pci_addr, rt_size_t size); +void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, rt_uint8_t func_no, int index, int type, rt_uint64_t cpu_addr, rt_uint64_t pci_addr, rt_size_t size); +rt_err_t dw_pcie_prog_inbound_atu(struct dw_pcie *pci, rt_uint8_t func_no, int index, int bar, rt_uint64_t cpu_addr, enum dw_pcie_aspace_type aspace_type); +void dw_pcie_disable_atu(struct dw_pcie *pci, int index, enum dw_pcie_region_type type); +void dw_pcie_setup(struct dw_pcie *pci); + +rt_inline void dw_pcie_writel_dbi(struct dw_pcie *pci, rt_uint32_t reg, rt_uint32_t val) +{ + dw_pcie_write_dbi(pci, reg, 0x4, val); +} + +rt_inline rt_uint32_t dw_pcie_readl_dbi(struct dw_pcie *pci, rt_uint32_t reg) +{ + return dw_pcie_read_dbi(pci, reg, 0x4); +} + +rt_inline void dw_pcie_writew_dbi(struct dw_pcie *pci, rt_uint32_t reg, rt_uint16_t val) +{ + dw_pcie_write_dbi(pci, reg, 0x2, val); +} + +rt_inline rt_uint16_t dw_pcie_readw_dbi(struct dw_pcie *pci, rt_uint32_t reg) +{ + return dw_pcie_read_dbi(pci, reg, 0x2); +} + +rt_inline void dw_pcie_writeb_dbi(struct dw_pcie *pci, rt_uint32_t reg, rt_uint8_t val) +{ + dw_pcie_write_dbi(pci, reg, 0x1, val); +} + +rt_inline rt_uint8_t dw_pcie_readb_dbi(struct dw_pcie *pci, rt_uint32_t reg) +{ + return dw_pcie_read_dbi(pci, reg, 0x1); +} + +rt_inline void dw_pcie_writel_dbi2(struct dw_pcie *pci, rt_uint32_t reg, rt_uint32_t val) +{ + dw_pcie_write_dbi2(pci, reg, 0x4, val); +} + +rt_inline void dw_pcie_dbi_ro_writable_enable(struct dw_pcie *pci, rt_bool_t enable) +{ + const rt_uint32_t reg = PCIE_MISC_CONTROL_1_OFF; + + if (enable) + { + dw_pcie_writel_dbi(pci, reg, dw_pcie_readl_dbi(pci, reg) | PCIE_DBI_RO_WR_EN); + } + else + { + dw_pcie_writel_dbi(pci, reg, dw_pcie_readl_dbi(pci, reg) & ~PCIE_DBI_RO_WR_EN); + } +} + +rt_inline rt_uint8_t dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) +{ + return dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) == 0xffffffff ? 1 : 0; +} + +rt_inline rt_uint32_t dw_pcie_readl_ob_unroll(struct dw_pcie *pci, + rt_uint32_t index, rt_uint32_t reg) +{ + return dw_pcie_readl_atu(pci, PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index) + reg); +} + +rt_inline void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, + rt_uint32_t index, rt_uint32_t reg, rt_uint32_t val) +{ + dw_pcie_writel_atu(pci, PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index) + reg, val); +} + +rt_inline rt_uint32_t dw_pcie_readl_ib_unroll(struct dw_pcie *pci, + rt_uint32_t index, rt_uint32_t reg) +{ + return dw_pcie_readl_atu(pci, PCIE_GET_ATU_INB_UNR_REG_OFFSET(index) + reg); +} + +rt_inline void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, + rt_uint32_t index, rt_uint32_t reg, rt_uint32_t val) +{ + dw_pcie_writel_atu(pci, reg + PCIE_GET_ATU_INB_UNR_REG_OFFSET(index), val); +} + +HOST_API rt_err_t dw_handle_msi_irq(struct dw_pcie_port *port) HOST_RET(-RT_ENOSYS) +HOST_API void dw_pcie_msi_init(struct dw_pcie_port *port) HOST_RET() +HOST_API void dw_pcie_free_msi(struct dw_pcie_port *port) HOST_RET() + +HOST_API void dw_pcie_setup_rc(struct dw_pcie_port *port) HOST_RET() + +HOST_API rt_err_t dw_pcie_host_init(struct dw_pcie_port *port) HOST_RET(-RT_ENOSYS) +HOST_API void dw_pcie_host_deinit(struct dw_pcie_port *port) HOST_RET() + +HOST_API void dw_pcie_host_free(struct dw_pcie_port *port) HOST_RET() + +HOST_API void *dw_pcie_own_conf_map(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg) HOST_RET(RT_NULL) + +EP_API rt_err_t dw_pcie_ep_init(struct dw_pcie_ep *ep) EP_RET(-RT_ENOSYS) +EP_API rt_err_t dw_pcie_ep_init_complete(struct dw_pcie_ep *ep) EP_RET(-RT_ENOSYS) +EP_API void dw_pcie_ep_exit(struct dw_pcie_ep *ep) EP_RET() + +EP_API rt_err_t dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, rt_uint8_t func_no) EP_RET(-RT_ENOSYS) +EP_API rt_err_t dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, rt_uint8_t func_no, unsigned irq) EP_RET(-RT_ENOSYS) +EP_API rt_err_t dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, rt_uint8_t func_no, unsigned irq) EP_RET(-RT_ENOSYS) +EP_API rt_err_t dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, rt_uint8_t func_no, unsigned irq) EP_RET(-RT_ENOSYS) + +EP_API void dw_pcie_ep_reset_bar(struct dw_pcie *pci, int bar_idx) EP_RET() + +EP_API rt_err_t dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, rt_uint8_t func_no, + int bar_idx, rt_ubase_t cpu_addr, enum dw_pcie_aspace_type aspace_type) EP_RET(-RT_ENOSYS) +EP_API rt_err_t dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, rt_uint8_t func_no, + rt_ubase_t phys_addr, rt_uint64_t pci_addr, rt_size_t size) EP_RET(-RT_ENOSYS) + +EP_API struct dw_pcie_ep_func *dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, rt_uint8_t func_no) EP_RET(RT_NULL) + +#endif /* __PCIE_DESIGNWARE_H__ */ diff --git a/rt-thread/components/drivers/pci/host/dw/pcie-dw_ep.c b/rt-thread/components/drivers/pci/host/dw/pcie-dw_ep.c new file mode 100644 index 0000000..b52f6b5 --- /dev/null +++ b/rt-thread/components/drivers/pci/host/dw/pcie-dw_ep.c @@ -0,0 +1,863 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#define DBG_TAG "pcie.dw-ep" +#define DBG_LVL DBG_INFO +#include + +#include "pcie-dw.h" + +struct dw_pcie_ep_func *dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, rt_uint8_t func_no) +{ + struct dw_pcie_ep_func *ep_func; + + rt_list_for_each_entry(ep_func, &ep->func_nodes, list) + { + if (ep_func->func_no == func_no) + { + return ep_func; + } + } + + return RT_NULL; +} + +static rt_uint8_t dw_pcie_ep_func_select(struct dw_pcie_ep *ep, rt_uint8_t func_no) +{ + rt_uint8_t func_offset = 0; + + if (ep->ops->func_select) + { + func_offset = ep->ops->func_select(ep, func_no); + } + + return func_offset; +} + +static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, rt_uint8_t func_no, + int bar_idx, int flags) +{ + rt_uint32_t reg; + rt_uint8_t func_offset = 0; + struct dw_pcie_ep *ep = &pci->endpoint; + + func_offset = dw_pcie_ep_func_select(ep, func_no); + reg = func_offset + PCIR_BAR(bar_idx); + + dw_pcie_dbi_ro_writable_enable(pci, RT_TRUE); + + dw_pcie_writel_dbi2(pci, reg, 0x0); + dw_pcie_writel_dbi(pci, reg, 0x0); + + if (flags & PCIM_BAR_MEM_TYPE_64) + { + dw_pcie_writel_dbi2(pci, reg + 4, 0x0); + dw_pcie_writel_dbi(pci, reg + 4, 0x0); + } + + dw_pcie_dbi_ro_writable_enable(pci, RT_FALSE); +} + +void dw_pcie_ep_reset_bar(struct dw_pcie *pci, int bar_idx) +{ + rt_uint8_t func_no, funcs = pci->endpoint.epc->max_functions; + + for (func_no = 0; func_no < funcs; ++func_no) + { + __dw_pcie_ep_reset_bar(pci, func_no, bar_idx, 0); + } +} + +static rt_uint8_t __dw_pcie_ep_find_next_cap(struct dw_pcie_ep *ep, rt_uint8_t func_no, + rt_uint8_t cap_ptr, rt_uint8_t cap) +{ + rt_uint16_t reg; + rt_uint8_t func_offset = 0, cap_id, next_cap_ptr; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + if (!cap_ptr) + { + return 0; + } + + func_offset = dw_pcie_ep_func_select(ep, func_no); + reg = dw_pcie_readw_dbi(pci, func_offset + cap_ptr); + cap_id = (reg & 0x00ff); + + if (cap_id > PCIY_MAX) + { + return 0; + } + + if (cap_id == cap) + { + return cap_ptr; + } + + next_cap_ptr = (reg & 0xff00) >> 8; + return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); +} + +static rt_uint8_t dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, rt_uint8_t func_no, + rt_uint8_t cap) +{ + rt_uint16_t reg; + rt_uint8_t func_offset = 0, next_cap_ptr; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + func_offset = dw_pcie_ep_func_select(ep, func_no); + reg = dw_pcie_readw_dbi(pci, func_offset + PCIR_CAP_PTR); + next_cap_ptr = reg & 0x00ff; + + return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); +} + +rt_err_t dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, rt_uint8_t func_no, + int bar_idx, rt_ubase_t cpu_addr, enum dw_pcie_aspace_type aspace_type) +{ + rt_err_t err; + rt_uint32_t free_win; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + free_win = rt_bitmap_next_clear_bit(ep->ib_window_map, 0, ep->num_ib_windows); + if (free_win >= ep->num_ib_windows) + { + LOG_E("No free inbound window"); + return -RT_EEMPTY; + } + + err = dw_pcie_prog_inbound_atu(pci, func_no, free_win, bar_idx, cpu_addr, aspace_type); + if (err) + { + LOG_E("Failed to program IB window error = %s", rt_strerror(err)); + return err; + } + + ep->bar_to_atu[bar_idx] = free_win; + rt_bitmap_set_bit(ep->ib_window_map, free_win); + + return RT_EOK; +} + +rt_err_t dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, rt_uint8_t func_no, + rt_ubase_t phys_addr, rt_uint64_t pci_addr, rt_size_t size) +{ + rt_uint32_t free_win; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + free_win = rt_bitmap_next_clear_bit(ep->ob_window_map, 0, ep->num_ob_windows); + if (free_win >= ep->num_ob_windows) + { + LOG_E("No free outbound window"); + return -RT_EEMPTY; + } + + dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win, PCIE_ATU_TYPE_MEM, + phys_addr, pci_addr, size); + + ep->outbound_addr[free_win] = phys_addr; + rt_bitmap_set_bit(ep->ob_window_map, free_win); + + return RT_EOK; +} + +static rt_err_t dw_pcie_ep_write_header(struct rt_pci_ep *epc, rt_uint8_t func_no, + struct rt_pci_ep_header *hdr) +{ + rt_uint8_t func_offset = 0; + struct dw_pcie_ep *ep = epc->priv; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + func_offset = dw_pcie_ep_func_select(ep, func_no); + + dw_pcie_dbi_ro_writable_enable(pci, RT_TRUE); + + dw_pcie_writew_dbi(pci, func_offset + PCIR_VENDOR, hdr->vendor); + dw_pcie_writew_dbi(pci, func_offset + PCIR_DEVICE, hdr->device); + dw_pcie_writeb_dbi(pci, func_offset + PCIR_REVID, hdr->revision); + dw_pcie_writeb_dbi(pci, func_offset + PCIR_PROGIF, hdr->progif); + dw_pcie_writew_dbi(pci, func_offset + PCIR_SUBCLASS, hdr->subclass | hdr->class_code << 8); + dw_pcie_writeb_dbi(pci, func_offset + PCIR_CACHELNSZ, hdr->cache_line_size); + dw_pcie_writew_dbi(pci, func_offset + PCIR_SUBVEND_0, hdr->subsystem_vendor); + dw_pcie_writew_dbi(pci, func_offset + PCIR_SUBDEV_0, hdr->subsystem_device); + dw_pcie_writeb_dbi(pci, func_offset + PCIR_INTPIN, hdr->intx); + + dw_pcie_dbi_ro_writable_enable(pci, RT_FALSE); + + return 0; +} + +static rt_err_t dw_pcie_ep_clear_bar(struct rt_pci_ep *epc, rt_uint8_t func_no, + struct rt_pci_ep_bar *bar, int bar_idx) +{ + rt_uint32_t atu_index; + struct dw_pcie_ep *ep = epc->priv; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + atu_index = ep->bar_to_atu[bar_idx]; + __dw_pcie_ep_reset_bar(pci, func_no, bar_idx, ep->epc_bar[bar_idx]->bus.flags); + + dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); + rt_bitmap_clear_bit(ep->ib_window_map, atu_index); + ep->epc_bar[bar_idx] = RT_NULL; + + return RT_EOK; +} + +static rt_err_t dw_pcie_ep_set_bar(struct rt_pci_ep *epc, rt_uint8_t func_no, + struct rt_pci_ep_bar *bar, int bar_idx) +{ + rt_err_t err; + rt_uint32_t reg; + rt_uint8_t func_offset = 0; + rt_size_t size = bar->bus.size; + rt_ubase_t flags = bar->bus.flags; + enum dw_pcie_aspace_type aspace_type; + struct dw_pcie_ep *ep = epc->priv; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + func_offset = dw_pcie_ep_func_select(ep, func_no); + reg = PCIR_BAR(bar_idx) + func_offset; + + if (!(flags & PCIM_BAR_SPACE)) + { + aspace_type = DW_PCIE_ASPACE_MEM; + } + else + { + aspace_type = DW_PCIE_ASPACE_IO; + } + + err = dw_pcie_ep_inbound_atu(ep, func_no, bar_idx, bar->bus.base, aspace_type); + if (err) + { + return err; + } + + dw_pcie_dbi_ro_writable_enable(pci, RT_TRUE); + + dw_pcie_writel_dbi2(pci, reg, rt_lower_32_bits(size - 1)); + dw_pcie_writel_dbi(pci, reg, flags); + + if (flags & PCIM_BAR_MEM_TYPE_64) + { + dw_pcie_writel_dbi2(pci, reg + 4, rt_upper_32_bits(size - 1)); + dw_pcie_writel_dbi(pci, reg + 4, 0); + } + + ep->epc_bar[bar_idx] = bar; + dw_pcie_dbi_ro_writable_enable(pci, RT_FALSE); + + return 0; +} + +static rt_err_t dw_pcie_find_index(struct dw_pcie_ep *ep, + rt_ubase_t addr, rt_uint32_t *atu_index) +{ + for (rt_uint32_t index = 0; index < ep->num_ob_windows; ++index) + { + if (ep->outbound_addr[index] != addr) + { + continue; + } + + *atu_index = index; + + return RT_EOK; + } + + return -RT_EINVAL; +} + +static rt_err_t dw_pcie_ep_unmap_addr(struct rt_pci_ep *epc, rt_uint8_t func_no, + rt_ubase_t addr) +{ + rt_err_t err; + rt_uint32_t atu_index; + struct dw_pcie_ep *ep = epc->priv; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + if ((err = dw_pcie_find_index(ep, addr, &atu_index))) + { + return err; + } + + dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND); + rt_bitmap_clear_bit(ep->ob_window_map, atu_index); + + return RT_EOK; +} + +static rt_err_t dw_pcie_ep_map_addr(struct rt_pci_ep *epc, rt_uint8_t func_no, + rt_ubase_t addr, rt_uint64_t pci_addr, rt_size_t size) +{ + rt_err_t err; + struct dw_pcie_ep *ep = epc->priv; + + err = dw_pcie_ep_outbound_atu(ep, func_no, addr, pci_addr, size); + if (err) + { + LOG_E("Failed to enable address error = %s", rt_strerror(err)); + return err; + } + + return RT_EOK; +} + +static rt_err_t dw_pcie_ep_set_msi(struct rt_pci_ep *epc, rt_uint8_t func_no, + unsigned irq_nr) +{ + rt_uint32_t val, reg; + rt_uint8_t func_offset = 0; + struct dw_pcie_ep_func *ep_func; + struct dw_pcie_ep *ep = epc->priv; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + if (!ep_func || !ep_func->msi_cap) + { + return -RT_EINVAL; + } + + func_offset = dw_pcie_ep_func_select(ep, func_no); + reg = ep_func->msi_cap + func_offset + PCIR_MSI_CTRL; + + val = dw_pcie_readw_dbi(pci, reg); + val &= ~PCIM_MSICTRL_MMC_MASK; + val |= (irq_nr << 1) & PCIM_MSICTRL_MMC_MASK; + + dw_pcie_dbi_ro_writable_enable(pci, RT_TRUE); + dw_pcie_writew_dbi(pci, reg, val); + dw_pcie_dbi_ro_writable_enable(pci, RT_FALSE); + + return RT_EOK; +} + +static rt_err_t dw_pcie_ep_get_msi(struct rt_pci_ep *epc, rt_uint8_t func_no, + unsigned *out_irq_nr) +{ + rt_uint32_t val, reg; + rt_uint8_t func_offset = 0; + struct dw_pcie_ep *ep = epc->priv; + struct dw_pcie_ep_func *ep_func; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + if (!ep_func || !ep_func->msi_cap) + { + return -RT_EINVAL; + } + + func_offset = dw_pcie_ep_func_select(ep, func_no); + reg = ep_func->msi_cap + func_offset + PCIR_MSI_CTRL; + + val = dw_pcie_readw_dbi(pci, reg); + if (!(val & PCIM_MSICTRL_MSI_ENABLE)) + { + return -RT_EINVAL; + } + + *out_irq_nr = (val & PCIM_MSICTRL_MME_MASK) >> 4; + + return RT_EOK; +} + +static rt_err_t dw_pcie_ep_set_msix(struct rt_pci_ep *epc, rt_uint8_t func_no, + unsigned irq_nr, int bar_idx, rt_off_t offset) +{ + rt_uint32_t val, reg; + rt_uint8_t func_offset = 0; + struct dw_pcie_ep_func *ep_func; + struct dw_pcie_ep *ep = epc->priv; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + if (!ep_func || !ep_func->msix_cap) + { + return -RT_EINVAL; + } + + dw_pcie_dbi_ro_writable_enable(pci, RT_TRUE); + + func_offset = dw_pcie_ep_func_select(ep, func_no); + reg = ep_func->msix_cap + func_offset + PCIR_MSIX_CTRL; + + val = dw_pcie_readw_dbi(pci, reg); + val &= ~PCIM_MSIXCTRL_TABLE_SIZE; + val |= irq_nr; + dw_pcie_writew_dbi(pci, reg, val); + + reg = ep_func->msix_cap + func_offset + PCIR_MSIX_TABLE; + val = offset | bar_idx; + dw_pcie_writel_dbi(pci, reg, val); + + reg = ep_func->msix_cap + func_offset + PCIR_MSIX_PBA; + val = (offset + (irq_nr * PCIM_MSIX_ENTRY_SIZE)) | bar_idx; + dw_pcie_writel_dbi(pci, reg, val); + + dw_pcie_dbi_ro_writable_enable(pci, RT_FALSE); + + return RT_EOK; +} + +static rt_err_t dw_pcie_ep_get_msix(struct rt_pci_ep *epc, rt_uint8_t func_no, + unsigned *out_irq_nr) +{ + rt_uint32_t val, reg; + rt_uint8_t func_offset = 0; + struct dw_pcie_ep_func *ep_func; + struct dw_pcie_ep *ep = epc->priv; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + if (!ep_func || !ep_func->msix_cap) + { + return -RT_EINVAL; + } + + func_offset = dw_pcie_ep_func_select(ep, func_no); + reg = ep_func->msix_cap + func_offset + PCIR_MSIX_CTRL; + + val = dw_pcie_readw_dbi(pci, reg); + if (!(val & PCIM_MSIXCTRL_MSIX_ENABLE)) + { + return -RT_EINVAL; + } + + *out_irq_nr = val & PCIM_MSIXCTRL_TABLE_SIZE; + + return RT_EOK; +} + +static rt_err_t dw_pcie_ep_raise_irq(struct rt_pci_ep *epc, rt_uint8_t func_no, + enum rt_pci_ep_irq type, unsigned irq) +{ + struct dw_pcie_ep *ep = epc->priv; + + if (!ep->ops->raise_irq) + { + return -RT_ENOSYS; + } + + return ep->ops->raise_irq(ep, func_no, type, irq); +} + +static rt_err_t dw_pcie_ep_stop(struct rt_pci_ep *epc) +{ + struct dw_pcie_ep *ep = epc->priv; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + if (pci->ops->stop_link) + { + pci->ops->stop_link(pci); + } + + return RT_EOK; +} + +static rt_err_t dw_pcie_ep_start(struct rt_pci_ep *epc) +{ + struct dw_pcie_ep *ep = epc->priv; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + if (pci->ops->start_link) + { + return pci->ops->start_link(pci); + } + + return RT_EOK; +} + +static const struct rt_pci_ep_ops dw_pcie_ep_ops = +{ + .write_header = dw_pcie_ep_write_header, + .set_bar = dw_pcie_ep_set_bar, + .clear_bar = dw_pcie_ep_clear_bar, + .map_addr = dw_pcie_ep_map_addr, + .unmap_addr = dw_pcie_ep_unmap_addr, + .set_msi = dw_pcie_ep_set_msi, + .get_msi = dw_pcie_ep_get_msi, + .set_msix = dw_pcie_ep_set_msix, + .get_msix = dw_pcie_ep_get_msix, + .raise_irq = dw_pcie_ep_raise_irq, + .start = dw_pcie_ep_start, + .stop = dw_pcie_ep_stop, +}; + +rt_err_t dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, rt_uint8_t func_no) +{ + LOG_E("EP cannot trigger legacy IRQs"); + + return -RT_EINVAL; +} + +rt_err_t dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, rt_uint8_t func_no, + unsigned irq) +{ + rt_err_t err; + rt_off_t aligned_offset; + rt_uint8_t func_offset = 0; + rt_uint64_t msg_addr; + rt_uint16_t msg_ctrl, msg_data; + rt_uint32_t msg_addr_lower, msg_addr_upper, reg; + struct rt_pci_ep *epc = ep->epc; + struct dw_pcie_ep_func *ep_func; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + if (!ep_func || !ep_func->msi_cap) + { + return -RT_EINVAL; + } + + func_offset = dw_pcie_ep_func_select(ep, func_no); + + /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */ + reg = ep_func->msi_cap + func_offset + PCIR_MSI_CTRL; + msg_ctrl = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + func_offset + PCIR_MSI_ADDR; + msg_addr_lower = dw_pcie_readl_dbi(pci, reg); + + if (!!(msg_ctrl & PCIM_MSICTRL_64BIT)) + { + reg = ep_func->msi_cap + func_offset + PCIR_MSI_ADDR_HIGH; + msg_addr_upper = dw_pcie_readl_dbi(pci, reg); + reg = ep_func->msi_cap + func_offset + PCIR_MSI_DATA_64BIT; + msg_data = dw_pcie_readw_dbi(pci, reg); + } + else + { + msg_addr_upper = 0; + reg = ep_func->msi_cap + func_offset + PCIR_MSI_DATA; + msg_data = dw_pcie_readw_dbi(pci, reg); + } + + aligned_offset = msg_addr_lower & (ep->page_size - 1); + msg_addr = ((rt_uint64_t)msg_addr_upper << 32) | (msg_addr_lower & ~aligned_offset); + + if ((err = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phy, msg_addr, ep->page_size))) + { + return err; + } + + HWREG32(ep->msi_mem + aligned_offset) = msg_data | (irq - 1); + dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phy); + + return RT_EOK; +} + +rt_err_t dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, rt_uint8_t func_no, + unsigned irq) +{ + rt_uint32_t msg_data; + struct dw_pcie_ep_func *ep_func; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + if (!ep_func || !ep_func->msix_cap) + { + return -RT_EINVAL; + } + + msg_data = (func_no << PCIE_MSIX_DOORBELL_PF_SHIFT) | (irq - 1); + dw_pcie_writel_dbi(pci, PCIE_MSIX_DOORBELL, msg_data); + + return RT_EOK; +} + +rt_err_t dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, rt_uint8_t func_no, + unsigned irq) +{ + rt_err_t err; + int bar_idx; + rt_uint64_t msg_addr; + rt_uint32_t tbl_offset; + rt_off_t aligned_offset; + rt_uint8_t func_offset = 0; + rt_uint32_t reg, msg_data, vec_ctrl; + struct rt_pci_ep *epc = ep->epc; + struct rt_pci_ep_msix_tbl *msix_tbl; + struct dw_pcie_ep_func *ep_func; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + if (!ep_func || !ep_func->msix_cap) + { + return -RT_EINVAL; + } + + func_offset = dw_pcie_ep_func_select(ep, func_no); + reg = ep_func->msix_cap + func_offset + PCIR_MSIX_TABLE; + + tbl_offset = dw_pcie_readl_dbi(pci, reg); + bar_idx = (tbl_offset & PCIM_MSIX_BIR_MASK); + tbl_offset &= PCIM_MSIX_TABLE_OFFSET; + + msix_tbl = (void *)ep->epc_bar[bar_idx]->cpu_addr + tbl_offset; + msg_addr = msix_tbl[(irq - 1)].msg_addr; + msg_data = msix_tbl[(irq - 1)].msg_data; + vec_ctrl = msix_tbl[(irq - 1)].vector_ctrl; + + if (vec_ctrl & PCIM_MSIX_ENTRYVECTOR_CTRL_MASK) + { + return -RT_EINVAL; + } + + aligned_offset = msg_addr & (ep->page_size - 1); + + if ((err = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phy, msg_addr, ep->page_size))) + { + return err; + } + + HWREG32(ep->msi_mem + aligned_offset) = msg_data; + dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phy); + + return RT_EOK; +} + +void dw_pcie_ep_exit(struct dw_pcie_ep *ep) +{ + struct rt_pci_ep *epc = ep->epc; + + if (ep->msi_mem) + { + rt_pci_ep_mem_free(epc, ep->msi_mem, ep->msi_mem_phy, ep->page_size); + } + + if (!rt_list_isempty(&ep->func_nodes)) + { + struct dw_pcie_ep_func *ep_func, *ep_func_next; + + rt_list_for_each_entry_safe(ep_func, ep_func_next, &ep->func_nodes, list) + { + rt_list_remove(&ep_func->list); + rt_free(ep_func); + } + } + + if (ep->ib_window_map) + { + rt_free(ep->ib_window_map); + } + + if (ep->ob_window_map) + { + rt_free(ep->ob_window_map); + } + + if (ep->outbound_addr) + { + rt_free(ep->outbound_addr); + } + + if (epc) + { + rt_free(epc); + } +} + +static rt_uint32_t dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap) +{ + rt_uint32_t header; + int pos = (PCI_REGMAX + 1); + + while (pos) + { + header = dw_pcie_readl_dbi(pci, pos); + + if (PCI_EXTCAP_ID(header) == cap) + { + return pos; + } + + if (!(pos = PCI_EXTCAP_NEXTPTR(header))) + { + break; + } + } + + return 0; +} + +rt_err_t dw_pcie_ep_init_complete(struct dw_pcie_ep *ep) +{ + rt_off_t offset; + rt_size_t bar_nr; + rt_uint32_t reg; + rt_uint8_t hdr_type; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + hdr_type = dw_pcie_readb_dbi(pci, PCIR_HDRTYPE) & PCIM_HDRTYPE; + if (hdr_type != PCIM_HDRTYPE_NORMAL) + { + LOG_E("PCIe controller is not set to EP mode hdr_type = %x", hdr_type); + return -RT_EIO; + } + + offset = dw_pcie_ep_find_ext_capability(pci, PCIZ_RESIZE_BAR); + + dw_pcie_dbi_ro_writable_enable(pci, RT_TRUE); + + if (offset) + { + reg = dw_pcie_readl_dbi(pci, offset + PCIM_REBAR_CTRL); + bar_nr = (reg & PCIM_REBAR_CTRL_NBAR_MASK) >> PCIM_REBAR_CTRL_NBAR_SHIFT; + + for (int i = 0; i < bar_nr; ++i, offset += PCIM_REBAR_CTRL) + { + dw_pcie_writel_dbi(pci, offset + PCIM_REBAR_CAP, 0x0); + } + } + + dw_pcie_setup(pci); + dw_pcie_dbi_ro_writable_enable(pci, RT_FALSE); + + return RT_EOK; +} + +rt_err_t dw_pcie_ep_init(struct dw_pcie_ep *ep) +{ + rt_err_t err; + struct rt_pci_ep *epc = RT_NULL; + struct dw_pcie_ep_func *ep_func; + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + struct rt_device *dev = pci->dev; + + rt_list_init(&ep->func_nodes); + + if (!pci->dbi_base || !pci->dbi_base2) + { + LOG_E("dbi_base/dbi_base2 is not populated"); + return -RT_EINVAL; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "num-ib-windows", &ep->num_ib_windows))) + { + LOG_E("Unable to read 'num-ib-windows' property"); + return err; + } + + if (ep->num_ib_windows > MAX_IATU_IN) + { + LOG_E("Invalid 'num-ib-windows'"); + return -RT_EINVAL; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "num-ob-windows", &ep->num_ob_windows))) + { + LOG_E("Unable to read 'num-ob-windows' property"); + return err; + } + + if (ep->num_ob_windows > MAX_IATU_OUT) + { + LOG_E("Invalid 'num-ob-windows'"); + return -RT_EINVAL; + } + + ep->ib_window_map = rt_calloc(RT_BITMAP_LEN(ep->num_ib_windows), sizeof(rt_bitmap_t)); + if (!ep->ib_window_map) + { + return -RT_ENOMEM; + } + + ep->ob_window_map = rt_calloc(RT_BITMAP_LEN(ep->num_ob_windows), sizeof(rt_bitmap_t)); + if (!ep->ob_window_map) + { + err = -RT_ENOMEM; + goto _fail; + } + + ep->outbound_addr = rt_calloc(ep->num_ob_windows, sizeof(rt_ubase_t)); + if (!ep->outbound_addr) + { + err = -RT_ENOMEM; + goto _fail; + } + + if (pci->link_gen < 1) + { + pci->link_gen = -1; + rt_dm_dev_prop_read_u32(dev, "max-link-speed", &pci->link_gen); + } + + epc = rt_calloc(1, sizeof(*epc)); + if (!epc) + { + err = -RT_ENOMEM; + goto _fail; + } + + epc->name = rt_dm_dev_get_name(dev); + epc->rc_dev = dev; + epc->ops = &dw_pcie_ep_ops; + epc->priv = ep; + + if ((err = rt_pci_ep_register(epc))) + { + goto _fail; + } + ep->epc = epc; + + if (rt_dm_dev_prop_read_u8(dev, "max-functions", &epc->max_functions)) + { + epc->max_functions = 1; + } + + for (rt_uint8_t func_no = 0; func_no < epc->max_functions; ++func_no) + { + ep_func = rt_calloc(1, sizeof(*ep_func)); + + if (!ep_func) + { + err = -RT_ENOMEM; + goto _fail; + } + + ep_func->func_no = func_no; + ep_func->msi_cap = dw_pcie_ep_find_capability(ep, func_no, PCIY_MSI); + ep_func->msix_cap = dw_pcie_ep_find_capability(ep, func_no, PCIY_MSIX); + + rt_list_init(&ep_func->list); + rt_list_insert_after(&ep->func_nodes, &ep_func->list); + } + + if (ep->ops->ep_init) + { + ep->ops->ep_init(ep); + } + + if ((err = rt_pci_ep_mem_init(epc, ep->aspace, ep->aspace_size, ep->page_size))) + { + goto _fail; + } + + ep->msi_mem = rt_pci_ep_mem_alloc(epc, &ep->msi_mem_phy, ep->page_size); + + if (!ep->msi_mem) + { + LOG_E("Failed to reserve memory for MSI/MSI-X"); + err = -RT_ENOMEM; + goto _fail; + } + + if ((err = dw_pcie_ep_init_complete(ep))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + dw_pcie_ep_exit(ep); + + return err; +} diff --git a/rt-thread/components/drivers/pci/host/dw/pcie-dw_host.c b/rt-thread/components/drivers/pci/host/dw/pcie-dw_host.c new file mode 100644 index 0000000..05a3dc7 --- /dev/null +++ b/rt-thread/components/drivers/pci/host/dw/pcie-dw_host.c @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#define DBG_TAG "pcie.dw-host" +#define DBG_LVL DBG_INFO +#include + +#include "pcie-dw.h" + +static void dw_pcie_irq_ack(struct rt_pic_irq *pirq) +{ + int hwirq = pirq->hwirq; + rt_uint32_t res, bit, ctrl; + struct dw_pcie_port *port = pirq->pic->priv_data; + struct dw_pcie *pci = to_dw_pcie_from_port(port); + + ctrl = hwirq / MAX_MSI_IRQS_PER_CTRL; + res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; + bit = hwirq % MAX_MSI_IRQS_PER_CTRL; + + dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_STATUS + res, RT_BIT(bit)); +} + +static void dw_pcie_irq_mask(struct rt_pic_irq *pirq) +{ + rt_ubase_t level; + int hwirq = pirq->hwirq; + rt_uint32_t res, bit, ctrl; + struct dw_pcie_port *port = pirq->pic->priv_data; + struct dw_pcie *pci = to_dw_pcie_from_port(port); + + rt_pci_msi_mask_irq(pirq); + + level = rt_spin_lock_irqsave(&port->lock); + + ctrl = hwirq / MAX_MSI_IRQS_PER_CTRL; + res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; + bit = hwirq % MAX_MSI_IRQS_PER_CTRL; + + port->irq_mask[ctrl] |= RT_BIT(bit); + dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, port->irq_mask[ctrl]); + + rt_spin_unlock_irqrestore(&port->lock, level); +} + +static void dw_pcie_irq_unmask(struct rt_pic_irq *pirq) +{ + rt_ubase_t level; + int hwirq = pirq->hwirq; + rt_uint32_t res, bit, ctrl; + struct dw_pcie_port *port = pirq->pic->priv_data; + struct dw_pcie *pci = to_dw_pcie_from_port(port); + + rt_pci_msi_unmask_irq(pirq); + + level = rt_spin_lock_irqsave(&port->lock); + + ctrl = hwirq / MAX_MSI_IRQS_PER_CTRL; + res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; + bit = hwirq % MAX_MSI_IRQS_PER_CTRL; + + port->irq_mask[ctrl] &= ~RT_BIT(bit); + dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, port->irq_mask[ctrl]); + + rt_spin_unlock_irqrestore(&port->lock, level); +} + +static void dw_pcie_compose_msi_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) +{ + rt_uint64_t msi_target; + struct dw_pcie_port *port = pirq->pic->priv_data; + + msi_target = (rt_uint64_t)port->msi_data_phy; + + msg->address_lo = rt_lower_32_bits(msi_target); + msg->address_hi = rt_upper_32_bits(msi_target); + + msg->data = pirq->hwirq; +} + +static int dw_pcie_irq_alloc_msi(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc) +{ + rt_ubase_t level; + int irq, hwirq; + struct rt_pic_irq *pirq; + struct dw_pcie_port *port = pic->priv_data; + + level = rt_spin_lock_irqsave(&port->lock); + hwirq = rt_bitmap_next_clear_bit(port->msi_map, 0, port->irq_count); + + if (hwirq >= port->irq_count) + { + irq = -RT_EEMPTY; + goto _out_lock; + } + + pirq = rt_pic_find_irq(pic, hwirq); + + irq = rt_pic_config_irq(pic, hwirq, hwirq); + pirq->mode = RT_IRQ_MODE_EDGE_RISING; + + rt_bitmap_set_bit(port->msi_map, hwirq); + +_out_lock: + rt_spin_unlock_irqrestore(&port->lock, level); + + return irq; +} + +static void dw_pcie_irq_free_msi(struct rt_pic *pic, int irq) +{ + rt_ubase_t level; + struct rt_pic_irq *pirq; + struct dw_pcie_port *port = pic->priv_data; + + pirq = rt_pic_find_pirq(pic, irq); + + if (!pirq) + { + return; + } + + level = rt_spin_lock_irqsave(&port->lock); + rt_bitmap_clear_bit(port->msi_map, pirq->hwirq); + rt_spin_unlock_irqrestore(&port->lock, level); +} + +const static struct rt_pic_ops dw_pci_msi_ops = +{ + .name = "DWPCI-MSI", + .irq_ack = dw_pcie_irq_ack, + .irq_mask = dw_pcie_irq_mask, + .irq_unmask = dw_pcie_irq_unmask, + .irq_compose_msi_msg = dw_pcie_compose_msi_msg, + .irq_alloc_msi = dw_pcie_irq_alloc_msi, + .irq_free_msi = dw_pcie_irq_free_msi, + .flags = RT_PIC_F_IRQ_ROUTING, +}; + +/* MSI int handler */ +rt_err_t dw_handle_msi_irq(struct dw_pcie_port *port) +{ + rt_err_t err; + int i, pos; + rt_bitmap_t status; + rt_uint32_t num_ctrls; + struct rt_pic_irq *pirq; + struct dw_pcie *pci = to_dw_pcie_from_port(port); + struct rt_pic *msi_pic = port->msi_pic; + + err = -RT_EEMPTY; + num_ctrls = RT_DIV_ROUND_UP(port->irq_count, MAX_MSI_IRQS_PER_CTRL); + + for (i = 0; i < num_ctrls; ++i) + { + status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS + + (i * MSI_REG_CTRL_BLOCK_SIZE)); + + if (!status) + { + continue; + } + + err = RT_EOK; + + rt_bitmap_for_each_set_bit(&status, pos, MAX_MSI_IRQS_PER_CTRL) + { + pirq = rt_pic_find_irq(msi_pic, pos + i * MAX_MSI_IRQS_PER_CTRL); + + dw_pcie_irq_ack(pirq); + + rt_pic_handle_isr(pirq); + } + } + + return err; +} + +static void dw_pcie_msi_isr(int irqno, void *param) +{ + struct dw_pcie_port *port = param; + + dw_handle_msi_irq(port); +} + +void dw_pcie_free_msi(struct dw_pcie_port *port) +{ + if (port->msi_irq >= 0) + { + rt_hw_interrupt_mask(port->msi_irq); + rt_pic_detach_irq(port->msi_irq, port); + } + + if (port->msi_data) + { + struct dw_pcie *pci = to_dw_pcie_from_port(port); + + rt_dma_free_coherent(pci->dev, sizeof(rt_uint64_t), port->msi_data, + port->msi_data_phy); + } +} + +void dw_pcie_msi_init(struct dw_pcie_port *port) +{ +#ifdef RT_PCI_MSI + struct dw_pcie *pci = to_dw_pcie_from_port(port); + rt_uint64_t msi_target = (rt_uint64_t)port->msi_data_phy; + + /* Program the msi_data_phy */ + dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, rt_lower_32_bits(msi_target)); + dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, rt_upper_32_bits(msi_target)); +#endif +} + +static const struct rt_pci_ops dw_child_pcie_ops; +static const struct rt_pci_ops dw_pcie_ops; + +rt_err_t dw_pcie_host_init(struct dw_pcie_port *port) +{ + rt_err_t err; + struct dw_pcie *pci = to_dw_pcie_from_port(port); + struct rt_device *dev = pci->dev; + struct rt_pci_host_bridge *bridge; + + rt_spin_lock_init(&port->lock); + + rt_dm_dev_get_address_by_name(dev, "config", &port->cfg0_addr, &port->cfg0_size); + + if (port->cfg0_addr) + { + port->cfg0_base = rt_ioremap((void *)port->cfg0_addr, port->cfg0_size); + + if (!port->cfg0_base) + { + return -RT_EIO; + } + } + else if (!port->cfg0_base) + { + LOG_E("Missing 'config' reg space"); + } + + if (!(bridge = rt_pci_host_bridge_alloc(0))) + { + return -RT_ENOMEM; + } + + bridge->parent.ofw_node = dev->ofw_node; + + if ((err = rt_pci_host_bridge_init(bridge))) + { + goto _err_free_bridge; + } + + port->bridge = bridge; + + for (int i = 0; i < bridge->bus_regions_nr; ++i) + { + struct rt_pci_bus_region *region = &bridge->bus_regions[i]; + + switch (region->flags) + { + case PCI_BUS_REGION_F_IO: + port->io_addr = region->cpu_addr; + port->io_bus_addr = region->phy_addr; + port->io_size = region->size; + break; + + case PCI_BUS_REGION_F_NONE: + port->cfg0_size = region->size; + port->cfg0_addr = region->cpu_addr; + + if (!pci->dbi_base) + { + pci->dbi_base = rt_ioremap((void *)port->cfg0_addr, port->cfg0_size); + + if (!pci->dbi_base) + { + LOG_E("Error with ioremap"); + return -RT_ENOMEM; + } + } + break; + + default: + break; + } + } + + if (!port->cfg0_base && port->cfg0_addr) + { + port->cfg0_base = rt_ioremap((void *)port->cfg0_addr, port->cfg0_size); + + if (!port->cfg0_base) + { + return -RT_ENOMEM; + } + } + + if (rt_dm_dev_prop_read_u32(dev, "num-viewport", &pci->num_viewport)) + { + pci->num_viewport = 2; + } + + if (pci->link_gen < 1) + { + pci->link_gen = -1; + rt_dm_dev_prop_read_u32(dev, "max-link-speed", &pci->link_gen); + } + + /* + * If a specific SoC driver needs to change the default number of vectors, + * it needs to implement the set_irq_count callback. + */ + if (!port->ops->set_irq_count) + { + port->irq_count = MSI_DEF_NUM_VECTORS; + } + else + { + port->ops->set_irq_count(port); + + if (port->irq_count > MAX_MSI_IRQS || port->irq_count == 0) + { + LOG_E("Invalid count of irq = %d", port->irq_count); + + return -RT_EINVAL; + } + } + + if (!port->ops->msi_host_init) + { + port->msi_pic = rt_calloc(1, sizeof(*port->msi_pic)); + + if (!port->msi_pic) + { + return -RT_ENOMEM; + } + + port->msi_pic->priv_data = port; + port->msi_pic->ops = &dw_pci_msi_ops; + rt_pic_linear_irq(port->msi_pic, port->irq_count); + rt_pic_user_extends(port->msi_pic); + + if (port->msi_irq) + { + rt_hw_interrupt_install(port->msi_irq, dw_pcie_msi_isr, port, "dwc-pci-msi"); + rt_hw_interrupt_umask(port->msi_irq); + } + + port->msi_data = rt_dma_alloc_coherent(pci->dev, sizeof(rt_uint64_t), + &port->msi_data_phy); + + if (!port->msi_data) + { + err = -RT_ENOMEM; + goto _err_free_msi; + } + } + else + { + if ((err = port->ops->msi_host_init(port))) + { + return err; + } + } + + /* Set default bus ops */ + bridge->ops = &dw_pcie_ops; + bridge->child_ops = &dw_child_pcie_ops; + + if (port->ops->host_init && (err = port->ops->host_init(port))) + { + goto _err_free_msi; + } + + bridge->sysdata = port; + + if ((err = rt_pci_host_bridge_probe(bridge))) + { + goto _err_free_msi; + } + + return RT_EOK; + +_err_free_msi: + if (!port->ops->msi_host_init) + { + dw_pcie_free_msi(port); + + rt_pic_cancel_irq(port->msi_pic); + rt_free(port->msi_pic); + port->msi_pic = RT_NULL; + } + +_err_free_bridge: + rt_pci_host_bridge_free(bridge); + port->bridge = RT_NULL; + + return err; +} + +void dw_pcie_host_deinit(struct dw_pcie_port *port) +{ + if (!port->ops->msi_host_init) + { + dw_pcie_free_msi(port); + } +} + +void dw_pcie_host_free(struct dw_pcie_port *port) +{ + if (!port->ops->msi_host_init) + { + dw_pcie_free_msi(port); + + rt_pic_cancel_irq(port->msi_pic); + rt_free(port->msi_pic); + } + + if (port->bridge) + { + rt_pci_host_bridge_free(port->bridge); + } +} + +static void *dw_pcie_other_conf_map(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg) +{ + int type; + rt_uint32_t busdev; + struct dw_pcie_port *port = bus->sysdata; + struct dw_pcie *pci = to_dw_pcie_from_port(port); + + /* + * Checking whether the link is up here is a last line of defense + * against platforms that forward errors on the system bus as + * SError upon PCI configuration transactions issued when the link is down. + * This check is racy by definition and does not stop the system from + * triggering an SError if the link goes down after this check is performed. + */ + if (!dw_pcie_link_up(pci)) + { + return RT_NULL; + } + + busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(RT_PCI_SLOT(devfn)) | + PCIE_ATU_FUNC(RT_PCI_FUNC(devfn)); + + if (rt_pci_is_root_bus(bus->parent)) + { + type = PCIE_ATU_TYPE_CFG0; + } + else + { + type = PCIE_ATU_TYPE_CFG1; + } + + dw_pcie_prog_outbound_atu(pci, 0, type, port->cfg0_addr, busdev, port->cfg0_size); + + return port->cfg0_base + reg; +} + +static rt_err_t dw_pcie_other_read_conf(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t *value) +{ + rt_err_t err; + struct dw_pcie_port *port = bus->sysdata; + struct dw_pcie *pci = to_dw_pcie_from_port(port); + + err = rt_pci_bus_read_config_uxx(bus, devfn, reg, width, value); + + if (!err && (pci->iatu_unroll_enabled & DWC_IATU_IOCFG_SHARED)) + { + dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, + port->io_addr, port->io_bus_addr, port->io_size); + } + + return err; +} + +static rt_err_t dw_pcie_other_write_conf(struct rt_pci_bus *bus, + rt_uint32_t devfn, int reg, int width, rt_uint32_t value) +{ + rt_err_t err; + struct dw_pcie_port *port = bus->sysdata; + struct dw_pcie *pci = to_dw_pcie_from_port(port); + + err = rt_pci_bus_write_config_uxx(bus, devfn, reg, width, value); + + if (!err && (pci->iatu_unroll_enabled & DWC_IATU_IOCFG_SHARED)) + { + dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, + port->io_addr, port->io_bus_addr, port->io_size); + } + + return err; +} + +static const struct rt_pci_ops dw_child_pcie_ops = +{ + .map = dw_pcie_other_conf_map, + .read = dw_pcie_other_read_conf, + .write = dw_pcie_other_write_conf, +}; + +void *dw_pcie_own_conf_map(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg) +{ + struct dw_pcie_port *port = bus->sysdata; + struct dw_pcie *pci = to_dw_pcie_from_port(port); + + if (RT_PCI_SLOT(devfn) > 0) + { + return RT_NULL; + } + + return pci->dbi_base + reg; +} + +static const struct rt_pci_ops dw_pcie_ops = +{ + .map = dw_pcie_own_conf_map, + .read = rt_pci_bus_read_config_uxx, + .write = rt_pci_bus_write_config_uxx, +}; + +void dw_pcie_setup_rc(struct dw_pcie_port *port) +{ + rt_uint32_t val, num_ctrls; + struct dw_pcie *pci = to_dw_pcie_from_port(port); + + /* + * Enable DBI read-only registers for writing/updating configuration. + * Write permission gets disabled towards the end of this function. + */ + dw_pcie_dbi_ro_writable_enable(pci, RT_TRUE); + + dw_pcie_setup(pci); + + if (!port->ops->msi_host_init) + { + num_ctrls = RT_DIV_ROUND_UP(port->irq_count, MAX_MSI_IRQS_PER_CTRL); + + /* Initialize IRQ Status array */ + for (int ctrl = 0; ctrl < num_ctrls; ++ctrl) + { + port->irq_mask[ctrl] = ~0; + + dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), port->irq_mask[ctrl]); + dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE + + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), ~0); + } + } + + /* Setup RC BARs */ + dw_pcie_writel_dbi(pci, PCIR_BAR(0), PCIM_BAR_MEM_TYPE_64); + dw_pcie_writel_dbi(pci, PCIR_BAR(1), PCIM_BAR_MEM_TYPE_32); + + /* Setup interrupt pins */ + val = dw_pcie_readl_dbi(pci, PCIR_INTLINE); + val &= 0xffff00ff; + val |= 0x00000100; + dw_pcie_writel_dbi(pci, PCIR_INTLINE, val); + + /* Setup bus numbers */ + val = dw_pcie_readl_dbi(pci, PCIR_PRIBUS_1); + val &= 0xff000000; + val |= 0x00ff0100; + dw_pcie_writel_dbi(pci, PCIR_PRIBUS_1, val); + + /* Setup command register */ + val = dw_pcie_readl_dbi(pci, PCIR_COMMAND); + val &= 0xffff0000; + val |= PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_SERRESPEN; + dw_pcie_writel_dbi(pci, PCIR_COMMAND, val); + + /* + * If the platform provides its own child bus config accesses, it means + * the platform uses its own address translation component rather than + * ATU, so we should not program the ATU here. + */ + if (pci->port.bridge->child_ops == &dw_child_pcie_ops) + { + int atu_idx = 0; + struct rt_pci_host_bridge *bridge = port->bridge; + + /* Get last memory resource entry */ + for (int i = 0; i < bridge->bus_regions_nr; ++i) + { + struct rt_pci_bus_region *region = &bridge->bus_regions[i]; + + if (region->flags != PCI_BUS_REGION_F_MEM) + { + continue; + } + + if (pci->num_viewport <= ++atu_idx) + { + break; + } + + dw_pcie_prog_outbound_atu(pci, atu_idx, + PCIE_ATU_TYPE_MEM, region->cpu_addr, + region->phy_addr, region->size); + } + + if (port->io_size) + { + if (pci->num_viewport > ++atu_idx) + { + dw_pcie_prog_outbound_atu(pci, atu_idx, + PCIE_ATU_TYPE_IO, port->io_addr, + port->io_bus_addr, port->io_size); + } + else + { + pci->iatu_unroll_enabled |= DWC_IATU_IOCFG_SHARED; + } + } + + if (pci->num_viewport <= atu_idx) + { + LOG_W("Resources exceed number of ATU entries (%d)", pci->num_viewport); + } + } + + dw_pcie_writel_dbi(pci, PCIR_BAR(0), 0); + + /* Program correct class for RC */ + dw_pcie_writew_dbi(pci, PCIR_SUBCLASS, PCIS_BRIDGE_PCI); + + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val |= PORT_LOGIC_SPEED_CHANGE; + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + + dw_pcie_dbi_ro_writable_enable(pci, RT_FALSE); +} diff --git a/rt-thread/components/drivers/pci/host/dw/pcie-dw_platfrom.c b/rt-thread/components/drivers/pci/host/dw/pcie-dw_platfrom.c new file mode 100644 index 0000000..878ac41 --- /dev/null +++ b/rt-thread/components/drivers/pci/host/dw/pcie-dw_platfrom.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "pcie.dw.platfrom" +#define DBG_LVL DBG_INFO +#include + +#include "pcie-dw.h" + +struct dw_dw_platform_pcie_soc_data +{ + enum dw_pcie_device_mode mode; +}; + +struct dw_platform_pcie +{ + struct dw_pcie *pci; + struct rt_syscon *regmap; + const struct dw_dw_platform_pcie_soc_data *soc_data; +}; + +static rt_err_t dw_platform_pcie_host_init(struct dw_pcie_port *port) +{ + struct dw_pcie *pci = to_dw_pcie_from_port(port); + + dw_pcie_setup_rc(port); + dw_pcie_wait_for_link(pci); + dw_pcie_msi_init(port); + + return RT_EOK; +} + +static void dw_platform_set_irq_count(struct dw_pcie_port *pp) +{ + pp->irq_count = MAX_MSI_IRQS; +} + +static const struct dw_pcie_host_ops dw_platform_pcie_host_ops = +{ + .host_init = dw_platform_pcie_host_init, + .set_irq_count = dw_platform_set_irq_count, +}; + +static rt_err_t dw_platform_pcie_establish_link(struct dw_pcie *pci) +{ + return RT_EOK; +} + +static const struct dw_pcie_ops dw_platform_pcie_ops = +{ + .start_link = dw_platform_pcie_establish_link, +}; + +static rt_err_t dw_platform_pcie_ep_init(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_endpoint(ep); + + for (int bar = 0; bar < PCI_STD_NUM_BARS; ++bar) + { + dw_pcie_ep_reset_bar(pci, bar); + } + + return RT_EOK; +} + +static rt_err_t dw_platform_pcie_ep_raise_irq(struct dw_pcie_ep *ep, + rt_uint8_t func_no, enum rt_pci_ep_irq type, unsigned irq) +{ + switch (type) + { + case RT_PCI_EP_IRQ_LEGACY: + return dw_pcie_ep_raise_legacy_irq(ep, func_no); + + case RT_PCI_EP_IRQ_MSI: + return dw_pcie_ep_raise_msi_irq(ep, func_no, irq); + + case RT_PCI_EP_IRQ_MSIX: + return dw_pcie_ep_raise_msix_irq(ep, func_no, irq); + + default: + LOG_E("Unknown IRQ type = %d", type); + } + + return RT_EOK; +} + +static const struct dw_pcie_ep_ops dw_platform_pcie_ep_ops = +{ + .ep_init = dw_platform_pcie_ep_init, + .raise_irq = dw_platform_pcie_ep_raise_irq, +}; + +static rt_err_t dw_platform_add_pcie_port(struct dw_platform_pcie *plat_pcie, + struct rt_device *dev) +{ + rt_err_t err; + struct dw_pcie *pci = plat_pcie->pci; + struct dw_pcie_port *port = &pci->port; + + port->sys_irq = rt_dm_dev_get_irq(dev, 1); + + if (port->sys_irq < 0) + { + return port->sys_irq; + } + +#ifdef RT_PCI_MSI + port->msi_irq = rt_dm_dev_get_irq(dev, 0); + + if (port->msi_irq < 0) + { + return port->msi_irq; + } +#endif + + port->ops = &dw_platform_pcie_host_ops; + + if ((err = dw_pcie_host_init(port))) + { + LOG_E("Failed to initialize host"); + return err; + } + + return RT_EOK; +} + +static rt_err_t dw_platform_add_pcie_ep(struct dw_platform_pcie *plat_pcie, + struct rt_device *dev) +{ + rt_err_t err; + struct dw_pcie *pci = plat_pcie->pci; + struct dw_pcie_ep *ep = &pci->endpoint; + + pci->dbi_base2 = rt_dm_dev_iomap_by_name(dev, "dbi2"); + + if (!pci->dbi_base2) + { + return -RT_EIO; + } + + err = rt_dm_dev_get_address_by_name(dev, "addr_space", &ep->aspace, &ep->aspace_size); + + if (err) + { + rt_iounmap(pci->dbi_base2); + return err; + } + + ep->ops = &dw_platform_pcie_ep_ops; + + if ((err = dw_pcie_ep_init(ep))) + { + LOG_E("Failed to initialize endpoint"); + return err; + } + + return RT_EOK; +} + +static rt_err_t dw_platform_pcie_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct dw_pcie *pci = RT_NULL; + struct dw_platform_pcie *plat_pcie; + struct rt_device *dev = &pdev->parent; + + if (!(plat_pcie = rt_calloc(1, sizeof(*plat_pcie)))) + { + return -RT_ENOMEM; + } + + if (!(pci = rt_calloc(1, sizeof(*pci)))) + { + err = -RT_ENOMEM; + goto _fail; + } + + plat_pcie->pci = pci; + plat_pcie->soc_data = pdev->id->data; + + pci->dev = dev; + pci->ops = &dw_platform_pcie_ops; + pci->dbi_base = rt_dm_dev_iomap_by_name(dev, "dbi"); + + if (!pci->dbi_base) + { + err = -RT_EIO; + goto _fail; + } + + dev->user_data = plat_pcie; + + switch (plat_pcie->soc_data->mode) + { + case DW_PCIE_RC_TYPE: + if (!RT_KEY_ENABLED(RT_PCI_DW_HOST)) + { + err = -RT_ENOSYS; + goto _fail; + } + + if ((err = dw_platform_add_pcie_port(plat_pcie, dev))) + { + goto _fail; + } + break; + + case DW_PCIE_EP_TYPE: + if (!RT_KEY_ENABLED(RT_PCI_DW_EP)) + { + err = -RT_ENOSYS; + goto _fail; + } + + if ((err = dw_platform_add_pcie_ep(plat_pcie, dev))) + { + goto _fail; + } + break; + + default: + LOG_E("Invalid device type %d", plat_pcie->soc_data->mode); + err = -RT_EINVAL; + goto _fail; + } + + return RT_EOK; + +_fail: + if (pci) + { + if (pci->dbi_base) + { + rt_iounmap(pci->dbi_base); + } + + rt_free(pci); + } + + rt_free(plat_pcie); + + return err; +} + +static rt_err_t dw_platform_pcie_remove(struct rt_platform_device *pdev) +{ + struct dw_platform_pcie *plat_pcie = pdev->parent.user_data; + + rt_pci_host_bridge_remove(plat_pcie->pci->port.bridge); + dw_pcie_host_free(&plat_pcie->pci->port); + + rt_iounmap(plat_pcie->pci->dbi_base); + rt_free(plat_pcie->pci); + + rt_free(plat_pcie); + + return RT_EOK; +} + +static const struct dw_dw_platform_pcie_soc_data dw_platform_pcie_rc_soc_data = +{ + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct dw_dw_platform_pcie_soc_data dw_platform_pcie_ep_soc_data = +{ + .mode = DW_PCIE_EP_TYPE, +}; + +static const struct rt_ofw_node_id dw_platform_pcie_ofw_ids[] = +{ + { .compatible = "snps,dw-pcie", .data = &dw_platform_pcie_rc_soc_data }, + { .compatible = "snps,dw-pcie-ep", .data = &dw_platform_pcie_ep_soc_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver dw_platform_pcie_driver = +{ + .name = "dw-pcie", + .ids = dw_platform_pcie_ofw_ids, + + .probe = dw_platform_pcie_probe, + .remove = dw_platform_pcie_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(dw_platform_pcie_driver); diff --git a/rt-thread/components/drivers/pci/host/pci-host-common.c b/rt-thread/components/drivers/pci/host/pci-host-common.c new file mode 100644 index 0000000..776c020 --- /dev/null +++ b/rt-thread/components/drivers/pci/host/pci-host-common.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +#include "../ecam.h" + +rt_err_t pci_host_common_probe(struct rt_platform_device *pdev) +{ + void *base; + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct pci_ecam_config_window *conf_win; + struct rt_pci_host_bridge *host_bridge = rt_pci_host_bridge_alloc(0); + + if (!host_bridge) + { + return -RT_ENOMEM; + } + + if (!(base = rt_dm_dev_iomap(dev, 0))) + { + err = -RT_EIO; + goto _fail; + } + + host_bridge->parent.ofw_node = dev->ofw_node; + + if ((err = rt_pci_host_bridge_init(host_bridge))) + { + goto _fail; + } + + host_bridge->sysdata = conf_win = pci_ecam_create(host_bridge, + (const struct pci_ecam_ops *)pdev->id->data); + + if (!conf_win) + { + err = -RT_ENOMEM; + goto _fail; + } + + conf_win->win = base; + conf_win->priv = host_bridge; + + if ((err = rt_pci_host_bridge_probe(host_bridge))) + { + goto _fail; + } + + dev->user_data = host_bridge; + + return RT_EOK; + +_fail: + if (base) + { + rt_iounmap(base); + } + rt_pci_host_bridge_free(host_bridge); + + return err; +} + +rt_err_t pci_host_common_remove(struct rt_platform_device *pdev) +{ + struct pci_ecam_config_window *conf_win; + struct rt_pci_host_bridge *host_bridge = pdev->parent.user_data; + + rt_pci_host_bridge_remove(host_bridge); + + conf_win = host_bridge->sysdata; + + rt_iounmap(conf_win->win); + rt_pci_host_bridge_free(host_bridge); + + return RT_EOK; +} diff --git a/rt-thread/components/drivers/pci/host/pci-host-generic.c b/rt-thread/components/drivers/pci/host/pci-host-generic.c new file mode 100644 index 0000000..7496d52 --- /dev/null +++ b/rt-thread/components/drivers/pci/host/pci-host-generic.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +#include "../ecam.h" + +static const struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = +{ + .bus_shift = 16, + .pci_ops = + { + .map = pci_ecam_map, + .read = rt_pci_bus_read_config_uxx, + .write = rt_pci_bus_write_config_uxx, + } +}; + +static void *pci_dw_ecam_map_bus(struct rt_pci_bus *bus, rt_uint32_t devfn, int where) +{ + struct pci_ecam_config_window *conf_win = bus->sysdata; + + if (bus->number == conf_win->bus_range[0] && RT_PCI_SLOT(devfn) > 0) + { + return RT_NULL; + } + + return pci_ecam_map(bus, devfn, where); +} + +static const struct pci_ecam_ops pci_dw_ecam_bus_ops = +{ + .pci_ops = + { + .map = pci_dw_ecam_map_bus, + .read = rt_pci_bus_read_config_uxx, + .write = rt_pci_bus_write_config_uxx, + } +}; + +static const struct rt_ofw_node_id gen_pci_ofw_ids[] = +{ + { .compatible = "pci-host-cam-generic", .data = &gen_pci_cfg_cam_bus_ops }, + { .compatible = "pci-host-ecam-generic", .data = &pci_generic_ecam_ops }, + { .compatible = "marvell,armada8k-pcie-ecam", .data = &pci_dw_ecam_bus_ops }, + { .compatible = "socionext,synquacer-pcie-ecam", .data = &pci_dw_ecam_bus_ops }, + { .compatible = "snps,dw-pcie-ecam", .data = &pci_dw_ecam_bus_ops }, + { /* sentinel */ } +}; + +static struct rt_platform_driver gen_pci_driver = +{ + .name = "pci-host-generic", + .ids = gen_pci_ofw_ids, + + .probe = pci_host_common_probe, + .remove = pci_host_common_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(gen_pci_driver); diff --git a/rt-thread/components/drivers/pci/irq.c b/rt-thread/components/drivers/pci/irq.c new file mode 100644 index 0000000..c7bd1a8 --- /dev/null +++ b/rt-thread/components/drivers/pci/irq.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#include + +#define DBG_TAG "pci.irq" +#define DBG_LVL DBG_INFO +#include + +#include + +void rt_pci_assign_irq(struct rt_pci_device *pdev) +{ + int irq = 0; + rt_uint8_t pin, slot = -1; + struct rt_pci_host_bridge *host_bridge = rt_pci_find_host_bridge(pdev->bus); + + if (!host_bridge->irq_map) + { + LOG_D("PCI-Device<%s> runtime IRQ mapping not provided by platform", + rt_dm_dev_get_name(&pdev->parent)); + + return; + } + + /* Must try the swizzle when interrupt line passes through a P2P bridge */ + rt_pci_read_config_u8(pdev, PCIR_INTPIN, &pin); + + if (pin > RT_PCI_INTX_PIN_MAX) + { + pin = 1; + } + + if (pin) + { + if (host_bridge->irq_slot) + { + slot = host_bridge->irq_slot(pdev, &pin); + } + + /* Map IRQ */ + if ((irq = host_bridge->irq_map(pdev, slot, pin)) == -1) + { + irq = 0; + } + } + pdev->irq = irq; + + LOG_D("PCI-Device<%s> assign IRQ: got %d", rt_dm_dev_get_name(&pdev->parent), pdev->irq); + + /* Save IRQ */ + rt_pci_write_config_u8(pdev, PCIR_INTLINE, irq); +} diff --git a/rt-thread/components/drivers/pci/msi/SConscript b/rt-thread/components/drivers/pci/msi/SConscript new file mode 100644 index 0000000..4def41c --- /dev/null +++ b/rt-thread/components/drivers/pci/msi/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_PCI_MSI']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = ['device.c', 'irq.c', 'msi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/pci/msi/device.c b/rt-thread/components/drivers/pci/msi/device.c new file mode 100644 index 0000000..36d0d0d --- /dev/null +++ b/rt-thread/components/drivers/pci/msi/device.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +void rt_pci_msi_init(struct rt_pci_device *pdev) +{ + if (pdev && (pdev->msi_cap = rt_pci_find_capability(pdev, PCIY_MSI))) + { + rt_uint16_t ctrl; + + rt_pci_read_config_u16(pdev, pdev->msi_cap + PCIR_MSI_CTRL, &ctrl); + + if (ctrl & PCIM_MSICTRL_MSI_ENABLE) + { + rt_pci_write_config_u16(pdev, pdev->msi_cap + PCIR_MSI_CTRL, ctrl & ~PCIM_MSICTRL_MSI_ENABLE); + } + + if (!(ctrl & PCIM_MSICTRL_64BIT)) + { + pdev->no_64bit_msi = RT_TRUE; + } + } +} + +void rt_pci_msix_init(struct rt_pci_device *pdev) +{ + if (pdev && (pdev->msix_cap = rt_pci_find_capability(pdev, PCIY_MSIX))) + { + rt_uint16_t ctrl; + + rt_pci_read_config_u16(pdev, pdev->msix_cap + PCIR_MSIX_CTRL, &ctrl); + + if (ctrl & PCIM_MSIXCTRL_MSIX_ENABLE) + { + rt_pci_write_config_u16(pdev, pdev->msix_cap + PCIR_MSIX_CTRL, ctrl & ~PCIM_MSIXCTRL_MSIX_ENABLE); + } + } +} diff --git a/rt-thread/components/drivers/pci/msi/irq.c b/rt-thread/components/drivers/pci/msi/irq.c new file mode 100644 index 0000000..8e81e5d --- /dev/null +++ b/rt-thread/components/drivers/pci/msi/irq.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +#define DBG_TAG "pci.msi.irq" +#define DBG_LVL DBG_INFO +#include + +static struct rt_spinlock msi_irq_map_lock = {}; +static RT_BITMAP_DECLARE(msi_irq_map, MAX_HANDLERS) = {}; + +rt_err_t rt_pci_msi_setup_irqs(struct rt_pci_device *pdev, int nvec, int type) +{ + int irq, index = 0, irq_nr = 0; + rt_err_t err = RT_EOK; + struct rt_pic_irq *pirq; + struct rt_pic *msi_pic; + struct rt_pci_msi_desc *desc; + + if (!pdev) + { + return -RT_EINVAL; + } + + msi_pic = pdev->msi_pic; + + if (type == PCIY_MSI) + { + int last_irq = -1, irq_idx; + rt_size_t irq_nr; + + desc = rt_pci_msi_first_desc(pdev); + irq_nr = 1 << desc->msi.cap.multi_msg_use; + + rt_hw_spin_lock(&msi_irq_map_lock.lock); + + _retry: + for (int i = 0; i < irq_nr; ++i) + { + if ((irq = msi_pic->ops->irq_alloc_msi(msi_pic, desc)) < 0) + { + err = irq; + + LOG_E("Setup %s[%d] IRQ error = %s", "MSI", i, rt_strerror(err)); + + break; + } + + if (last_irq >= 0 && last_irq + 1 != irq) + { + for (int idx = 0; idx < i; ++i, --last_irq) + { + rt_bitmap_set_bit(msi_irq_map, last_irq); + } + + last_irq = irq; + goto _retry; + } + + last_irq = irq; + } + + if (!err) + { + /* Get the first irq */ + desc->irq = irq - (irq_nr - 1); + } + + rt_bitmap_for_each_set_bit(msi_irq_map, irq_idx, MAX_HANDLERS) + { + msi_pic->ops->irq_free_msi(msi_pic, irq_idx); + + /* Free bit so the next user doesn't need to bzero */ + rt_bitmap_clear_bit(msi_irq_map, irq_idx); + } + + rt_hw_spin_unlock(&msi_irq_map_lock.lock); + + if (!err) + { + for (int idx = 0; idx < nvec; ++idx) + { + pirq = rt_pic_find_pirq(msi_pic, irq + idx); + pirq->msi_desc = desc; + + msi_pic->ops->irq_compose_msi_msg(pirq, &desc->msg); + + rt_pci_msi_write_msg(desc, &desc->msg); + } + } + } + else if (type == PCIY_MSIX) + { + rt_pci_msi_for_each_desc(pdev, desc) + { + if ((irq = msi_pic->ops->irq_alloc_msi(msi_pic, desc)) < 0) + { + err = irq; + + LOG_E("Setup %s[%d] IRQ error = %s", "MSI-X", + desc->msix.index, rt_strerror(err)); + + break; + } + + desc->irq = irq; + pirq = rt_pic_find_pirq(msi_pic, irq); + pirq->msi_desc = desc; + + msi_pic->ops->irq_compose_msi_msg(pirq, &desc->msg); + + rt_pci_msi_write_msg(desc, &desc->msg); + + ++irq_nr; + } + + if (err) + { + rt_pci_msi_for_each_desc(pdev, desc) + { + if (index >= irq_nr) + { + break; + } + + msi_pic->ops->irq_free_msi(msi_pic, desc->irq); + + ++index; + } + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} diff --git a/rt-thread/components/drivers/pci/msi/msi.c b/rt-thread/components/drivers/pci/msi/msi.c new file mode 100644 index 0000000..eb0d578 --- /dev/null +++ b/rt-thread/components/drivers/pci/msi/msi.c @@ -0,0 +1,949 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "pci.msi" +#define DBG_LVL DBG_INFO +#include + +/* PCI has 2048 max IRQs in MSI-X */ +static RT_IRQ_AFFINITY_DECLARE(msi_affinity_default[2048]) rt_section(".bss.noclean.pci.msi"); + +rt_inline void spin_lock(struct rt_spinlock *lock) +{ + rt_hw_spin_lock(&lock->lock); +} + +rt_inline void spin_unlock(struct rt_spinlock *lock) +{ + rt_hw_spin_unlock(&lock->lock); +} + +rt_inline void *msix_table_base(struct rt_pci_msix_conf *msix) +{ + return msix->table_base + msix->index * PCIM_MSIX_ENTRY_SIZE; +} + +rt_inline void *msix_vector_ctrl_base(struct rt_pci_msix_conf *msix) +{ + return msix_table_base(msix) + PCIM_MSIX_ENTRY_VECTOR_CTRL; +} + +rt_inline void msix_write_vector_ctrl(struct rt_pci_msix_conf *msix, + rt_uint32_t ctrl) +{ + void *vc_addr = msix_vector_ctrl_base(msix); + + HWREG32(vc_addr) = ctrl; +} + +rt_inline void msix_mask(struct rt_pci_msix_conf *msix) +{ + msix->msg_ctrl |= PCIM_MSIX_ENTRYVECTOR_CTRL_MASK; + msix_write_vector_ctrl(msix, msix->msg_ctrl); + + /* Flush write to device */ + HWREG32(msix->table_base); +} + +static void msix_update_ctrl(struct rt_pci_device *pdev, + rt_uint16_t clear, rt_uint16_t set) +{ + rt_uint16_t msgctl; + + rt_pci_read_config_u16(pdev, pdev->msix_cap + PCIR_MSIX_CTRL, &msgctl); + msgctl &= ~clear; + msgctl |= set; + rt_pci_write_config_u16(pdev, pdev->msix_cap + PCIR_MSIX_CTRL, msgctl); +} + +rt_inline void msix_unmask(struct rt_pci_msix_conf *msix) +{ + msix->msg_ctrl &= ~PCIM_MSIX_ENTRYVECTOR_CTRL_MASK; + msix_write_vector_ctrl(msix, msix->msg_ctrl); +} + +rt_inline rt_uint32_t msi_multi_mask(struct rt_pci_msi_conf *msi) +{ + if (msi->cap.multi_msg_max >= 5) + { + return 0xffffffff; + } + + return (1 << (1 << msi->cap.multi_msg_max)) - 1; +} + +static void msi_write_mask(struct rt_pci_msi_conf *msi, + rt_uint32_t clear, rt_uint32_t set, struct rt_pci_device *pdev) +{ + if (msi->cap.is_masking) + { + rt_ubase_t level = rt_spin_lock_irqsave(&pdev->msi_lock); + + msi->mask &= ~clear; + msi->mask |= set; + rt_pci_write_config_u32(pdev, msi->mask_pos, msi->mask); + + rt_spin_unlock_irqrestore(&pdev->msi_lock, level); + } +} + +rt_inline void msi_mask(struct rt_pci_msi_conf *msi, + rt_uint32_t mask, struct rt_pci_device *pdev) +{ + msi_write_mask(msi, 0, mask, pdev); +} + +rt_inline void msi_unmask(struct rt_pci_msi_conf *msi, + rt_uint32_t mask, struct rt_pci_device *pdev) +{ + msi_write_mask(msi, mask, 0, pdev); +} + +static void msi_write_enable(struct rt_pci_device *pdev, rt_bool_t enable) +{ + rt_uint16_t msgctl; + + rt_pci_read_config_u16(pdev, pdev->msi_cap + PCIR_MSI_CTRL, &msgctl); + + msgctl &= ~PCIM_MSICTRL_MSI_ENABLE; + + if (enable) + { + msgctl |= PCIM_MSICTRL_MSI_ENABLE; + } + + rt_pci_write_config_u16(pdev, pdev->msi_cap + PCIR_MSI_CTRL, msgctl); +} + +static void msi_affinity_init(struct rt_pci_msi_desc *desc, int msi_index, + rt_bitmap_t *cpumasks) +{ + int irq; + struct rt_pic_irq *pirq; + struct rt_pci_device *pdev = desc->pdev; + struct rt_pic *msi_pic = pdev->msi_pic; + + irq = desc->irq + desc->is_msix ? 0 : msi_index; + pirq = rt_pic_find_pirq(msi_pic, irq); + + /* Save affinity */ + if (desc->is_msix) + { + desc->affinity = pirq->affinity; + } + else + { + desc->affinities[msi_index] = pirq->affinity; + } + + if ((void *)cpumasks > (void *)msi_affinity_default && + (void *)cpumasks < (void *)msi_affinity_default + sizeof(msi_affinity_default)) + { + rt_uint64_t data_address; + + /* Get MSI/MSI-X write data adddress */ + data_address = desc->msg.address_hi; + data_address <<= 32; + data_address |= desc->msg.address_lo; + + /* Prepare affinity */ + cpumasks = pirq->affinity; + + rt_numa_memory_affinity(data_address, cpumasks); + } + else if (rt_bitmap_next_set_bit(cpumasks, 0, RT_CPUS_NR) >= RT_CPUS_NR) + { + /* No affinity info found, give up */ + return; + } + + if (!rt_pic_irq_set_affinity(irq, cpumasks)) + { + if (msi_pic->ops->irq_write_msi_msg) + { + msi_pic->ops->irq_write_msi_msg(pirq, &desc->msg); + } + } +} + +void rt_pci_msi_shutdown(struct rt_pci_device *pdev) +{ + struct rt_pci_msi_desc *desc; + + if (!pdev) + { + return; + } + + msi_write_enable(pdev, RT_FALSE); + rt_pci_intx(pdev, RT_TRUE); + + if ((desc = rt_pci_msi_first_desc(pdev))) + { + msi_unmask(&desc->msi, msi_multi_mask(&desc->msi), pdev); + } + + /* Restore pdev->irq to its default pin-assertion IRQ */ + pdev->irq = desc->msi.default_irq; + pdev->msi_enabled = RT_FALSE; +} + +void rt_pci_msix_shutdown(struct rt_pci_device *pdev) +{ + struct rt_pci_msi_desc *desc; + + if (!pdev) + { + return; + } + + rt_pci_msi_for_each_desc(pdev, desc) + { + msix_mask(&desc->msix); + } + + msix_update_ctrl(pdev, PCIM_MSIXCTRL_MSIX_ENABLE, 0); + + rt_pci_intx(pdev, RT_TRUE); + pdev->msix_enabled = RT_FALSE; +} + +void rt_pci_msi_free_irqs(struct rt_pci_device *pdev) +{ + struct rt_pci_msi_desc *desc, *last_desc = RT_NULL; + + if (!pdev) + { + return; + } + + if (pdev->msix_base) + { + rt_iounmap(pdev->msix_base); + pdev->msix_base = RT_NULL; + } + + rt_pci_msi_for_each_desc(pdev, desc) + { + /* To safety */ + if (last_desc) + { + rt_list_remove(&last_desc->list); + rt_free(last_desc); + } + last_desc = desc; + } + + /* The last one */ + if (last_desc) + { + rt_list_remove(&last_desc->list); + rt_free(last_desc); + } +} + +void rt_pci_msi_write_msg(struct rt_pci_msi_desc *desc, struct rt_pci_msi_msg *msg) +{ + struct rt_pci_device *pdev = desc->pdev; + + if (desc->is_msix) + { + void *msix_entry; + rt_bool_t unmasked; + rt_uint32_t msgctl; + struct rt_pci_msix_conf *msix = &desc->msix; + + msgctl = msix->msg_ctrl; + unmasked = !(msgctl & PCIM_MSIX_ENTRYVECTOR_CTRL_MASK); + msix_entry = msix_table_base(msix); + + if (unmasked) + { + msix_write_vector_ctrl(msix, msgctl | PCIM_MSIX_ENTRYVECTOR_CTRL_MASK); + } + + HWREG32(msix_entry + PCIM_MSIX_ENTRY_LOWER_ADDR) = msg->address_lo; + HWREG32(msix_entry + PCIM_MSIX_ENTRY_UPPER_ADDR) = msg->address_hi; + HWREG32(msix_entry + PCIM_MSIX_ENTRY_DATA) = msg->data; + + if (unmasked) + { + msix_write_vector_ctrl(msix, msgctl); + } + + /* Ensure that the writes are visible in the device */ + HWREG32(msix_entry + PCIM_MSIX_ENTRY_DATA); + } + else + { + rt_uint16_t msgctl; + int pos = pdev->msi_cap; + struct rt_pci_msi_conf *msi = &desc->msi; + + rt_pci_read_config_u16(pdev, pos + PCIR_MSI_CTRL, &msgctl); + msgctl &= ~PCIM_MSICTRL_MME_MASK; + msgctl |= msi->cap.multi_msg_use << PCIM_MSICTRL_MME_SHIFT; + rt_pci_write_config_u16(pdev, pos + PCIR_MSI_CTRL, msgctl); + + rt_pci_write_config_u32(pdev, pos + PCIR_MSI_ADDR, msg->address_lo); + + /* + * The value stored in this field is related to the processor system, + * the processor will initialize this field + * when the PCIe device is initialized, and the rules for filling + * in this field are not the same for different processors. + * If the Multiple Message Enable field is not 0b000 (multiple IRQs), + * the PCIe device can send different interrupt requests + * by changing the low data in the Message Data field + */ + if (msi->cap.is_64bit) + { + rt_pci_write_config_u32(pdev, pos + PCIR_MSI_ADDR_HIGH, msg->address_hi); + rt_pci_write_config_u16(pdev, pos + PCIR_MSI_DATA_64BIT, msg->data); + } + else + { + rt_pci_write_config_u16(pdev, pos + PCIR_MSI_DATA, msg->data); + } + + /* Ensure that the writes are visible in the device */ + rt_pci_read_config_u16(pdev, pos + PCIR_MSI_CTRL, &msgctl); + } + + desc->msg = *msg; + + if (desc->write_msi_msg) + { + desc->write_msi_msg(desc, desc->write_msi_msg_data); + } +} + +void rt_pci_msi_mask_irq(struct rt_pic_irq *pirq) +{ + struct rt_pci_msi_desc *desc; + + if (pirq && (desc = pirq->msi_desc)) + { + if (desc->is_msix) + { + msix_mask(&desc->msix); + } + else + { + msi_mask(&desc->msi, RT_BIT(pirq->irq - desc->irq), desc->pdev); + } + } +} + +void rt_pci_msi_unmask_irq(struct rt_pic_irq *pirq) +{ + struct rt_pci_msi_desc *desc; + + if (pirq && (desc = pirq->msi_desc)) + { + if (desc->is_msix) + { + msix_unmask(&desc->msix); + } + else + { + msi_unmask(&desc->msi, RT_BIT(pirq->irq - desc->irq), desc->pdev); + } + } +} + +rt_ssize_t rt_pci_alloc_vector(struct rt_pci_device *pdev, int min, int max, + rt_uint32_t flags, RT_IRQ_AFFINITY_DECLARE((*affinities))) +{ + rt_ssize_t res = -RT_ENOSYS; + + if (!pdev || min > max) + { + return -RT_EINVAL; + } + + if (flags & RT_PCI_IRQ_F_AFFINITY) + { + if (!affinities) + { + affinities = msi_affinity_default; + } + } + else + { + affinities = RT_NULL; + } + + if (flags & RT_PCI_IRQ_F_MSIX) + { + res = rt_pci_msix_enable_range_affinity(pdev, RT_NULL, min, max, affinities); + + if (res > 0) + { + return res; + } + } + + if (flags & RT_PCI_IRQ_F_MSI) + { + res = rt_pci_msi_enable_range_affinity(pdev, min, max, affinities); + + if (res > 0) + { + return res; + } + } + + if (flags & RT_PCI_IRQ_F_LEGACY) + { + if (min == 1 && pdev->irq >= 0) + { + if (affinities) + { + int cpuid; + RT_IRQ_AFFINITY_DECLARE(old_affinity); + + /* INTx is shared, we should update it */ + rt_pic_irq_get_affinity(pdev->irq, old_affinity); + + rt_bitmap_for_each_set_bit(affinities[0], cpuid, RT_CPUS_NR) + { + RT_IRQ_AFFINITY_SET(old_affinity, cpuid); + } + + rt_pic_irq_set_affinity(pdev->irq, old_affinity); + } + + rt_pci_intx(pdev, RT_TRUE); + + return min; + } + } + + return res; +} + +void rt_pci_free_vector(struct rt_pci_device *pdev) +{ + if (!pdev) + { + return; + } + + rt_pci_msi_disable(pdev); + rt_pci_msix_disable(pdev); + rt_pci_irq_mask(pdev); +} + +static rt_err_t msi_verify_entries(struct rt_pci_device *pdev) +{ + if (pdev->no_64bit_msi) + { + struct rt_pci_msi_desc *desc; + + rt_pci_msi_for_each_desc(pdev, desc) + { + if (desc->msg.address_hi) + { + LOG_D("%s: Arch assigned 64-bit MSI address %08x%08x" + "but device only supports 32 bits", + rt_dm_dev_get_name(&pdev->parent), + desc->msg.address_hi, desc->msg.address_lo); + + return -RT_EIO; + } + } + } + + return RT_EOK; +} + +static rt_err_t msi_insert_desc(struct rt_pci_device *pdev, + struct rt_pci_msi_desc *init_desc) +{ + rt_size_t msi_affinity_ptr_size = 0; + struct rt_pci_msi_desc *msi_desc; + + if (!init_desc->is_msix) + { + msi_affinity_ptr_size += sizeof(msi_desc->affinities[0]) * 32; + } + + msi_desc = rt_calloc(1, sizeof(*msi_desc) + msi_affinity_ptr_size); + + if (!msi_desc) + { + return -RT_ENOMEM; + } + + rt_memcpy(msi_desc, init_desc, sizeof(*msi_desc)); + + if (!init_desc->is_msix) + { + msi_desc->affinities = (void *)msi_desc + sizeof(*msi_desc); + } + + msi_desc->pdev = pdev; + rt_list_init(&msi_desc->list); + rt_list_insert_before(&pdev->msi_desc_nodes, &msi_desc->list); + + return RT_EOK; +} + +rt_ssize_t rt_pci_msi_vector_count(struct rt_pci_device *pdev) +{ + rt_uint16_t msgctl; + + if (!pdev) + { + return -RT_EINVAL; + } + + if (!pdev->msi_cap) + { + return -RT_EINVAL; + } + + rt_pci_read_config_u16(pdev, pdev->msi_cap + PCIR_MSI_CTRL, &msgctl); + + return 1 << ((msgctl & PCIM_MSICTRL_MMC_MASK) >> 1); +} + +rt_err_t rt_pci_msi_disable(struct rt_pci_device *pdev) +{ + if (!pdev) + { + return -RT_EINVAL; + } + + if (!pdev->msi_enabled) + { + return -RT_EINVAL; + } + + spin_lock(&pdev->msi_lock); + + rt_pci_msi_shutdown(pdev); + rt_pci_msi_free_irqs(pdev); + + spin_unlock(&pdev->msi_lock); + + return RT_EOK; +} + +static rt_err_t msi_setup_msi_desc(struct rt_pci_device *pdev, int nvec) +{ + rt_uint16_t msgctl; + struct rt_pci_msi_desc desc; + + rt_memset(&desc, 0, sizeof(desc)); + + desc.vector_used = nvec; + desc.vector_count = rt_pci_msi_vector_count(pdev); + desc.is_msix = RT_FALSE; + + rt_pci_read_config_u16(pdev, pdev->msi_cap + PCIR_MSI_CTRL, &msgctl); + + desc.msi.cap.is_64bit = !!(msgctl & PCIM_MSICTRL_64BIT); + desc.msi.cap.is_masking = !!(msgctl & PCIM_MSICTRL_VECTOR); + desc.msi.cap.multi_msg_max = (msgctl & PCIM_MSICTRL_MMC_MASK) >> 1; + + for (int log2 = 0; log2 < 5; ++log2) + { + if (nvec <= (1 << log2)) + { + desc.msi.cap.multi_msg_use = log2; + break; + } + } + + if (desc.msi.cap.is_64bit) + { + desc.msi.mask_pos = pdev->msi_cap + PCIR_MSI_MASK_64BIT; + } + else + { + desc.msi.mask_pos = pdev->msi_cap + PCIR_MSI_MASK; + } + + /* Save pdev->irq for its default pin-assertion IRQ */ + desc.msi.default_irq = pdev->irq; + + if (desc.msi.cap.is_masking) + { + /* Get the old mask status */ + rt_pci_read_config_u32(pdev, desc.msi.mask_pos, &desc.msi.mask); + } + + return msi_insert_desc(pdev, &desc); +} + +static rt_ssize_t msi_capability_init(struct rt_pci_device *pdev, + int nvec, RT_IRQ_AFFINITY_DECLARE((*affinities))) +{ + rt_err_t err; + struct rt_pci_msi_desc *desc; + + msi_write_enable(pdev, RT_FALSE); + + spin_lock(&pdev->msi_lock); + + if (!(err = msi_setup_msi_desc(pdev, nvec))) + { + /* All MSIs are unmasked by default; mask them all */ + desc = rt_pci_msi_first_desc(pdev); + msi_mask(&desc->msi, msi_multi_mask(&desc->msi), pdev); + + if (!(err = rt_pci_msi_setup_irqs(pdev, nvec, PCIY_MSI))) + { + err = msi_verify_entries(pdev); + } + + if (err) + { + msi_unmask(&desc->msi, msi_multi_mask(&desc->msi), pdev); + } + } + + spin_unlock(&pdev->msi_lock); + + if (err) + { + rt_pci_msi_free_irqs(pdev); + + LOG_E("%s: Setup %s interrupts(%d) error = %s", + rt_dm_dev_get_name(&pdev->parent), "MSI", nvec, rt_strerror(err)); + + return err; + } + + if (affinities) + { + for (int idx = 0; idx < nvec; ++idx) + { + msi_affinity_init(desc, idx, affinities[idx]); + } + } + + /* Disable INTX */ + rt_pci_intx(pdev, RT_FALSE); + + /* Set MSI enabled bits */ + msi_write_enable(pdev, RT_TRUE); + + pdev->irq = desc->irq; + + pdev->msi_enabled = RT_TRUE; + + return nvec; +} + +rt_ssize_t rt_pci_msi_enable_range_affinity(struct rt_pci_device *pdev, + int min, int max, RT_IRQ_AFFINITY_DECLARE((*affinities))) +{ + int nvec = max; + rt_size_t entries_nr; + + if (!pdev || min > max) + { + return -RT_EINVAL; + } + + if (pdev->no_msi) + { + return -RT_ENOSYS; + } + + if (!pdev->msi_pic) + { + return -RT_ENOSYS; + } + + if (pdev->msi_enabled) + { + LOG_W("%s: MSI is enabled", rt_dm_dev_get_name(&pdev->parent)); + + return -RT_EINVAL; + } + + entries_nr = rt_pci_msi_vector_count(pdev); + + if (entries_nr < 0) + { + return entries_nr; + } + + if (nvec > entries_nr) + { + return -RT_EEMPTY; + } + + return msi_capability_init(pdev, nvec, affinities); +} + +rt_ssize_t rt_pci_msix_vector_count(struct rt_pci_device *pdev) +{ + rt_uint16_t msgctl; + + if (!pdev) + { + return -RT_EINVAL; + } + + if (!pdev->msix_cap) + { + return -RT_EINVAL; + } + + rt_pci_read_config_u16(pdev, pdev->msix_cap + PCIR_MSIX_CTRL, &msgctl); + + return rt_pci_msix_table_size(msgctl); +} + +rt_err_t rt_pci_msix_disable(struct rt_pci_device *pdev) +{ + if (!pdev) + { + return -RT_EINVAL; + } + + if (!pdev->msix_enabled) + { + return -RT_EINVAL; + } + + spin_lock(&pdev->msi_lock); + + rt_pci_msix_shutdown(pdev); + rt_pci_msi_free_irqs(pdev); + + spin_unlock(&pdev->msi_lock); + + return RT_EOK; +} + +static void *msix_table_remap(struct rt_pci_device *pdev, rt_size_t entries_nr) +{ + rt_uint8_t bir; + rt_uint32_t table_offset; + rt_ubase_t table_base_phys; + + rt_pci_read_config_u32(pdev, pdev->msix_cap + PCIR_MSIX_TABLE, &table_offset); + bir = (rt_uint8_t)(table_offset & PCIM_MSIX_BIR_MASK); + + if (pdev->resource[bir].flags & PCI_BUS_REGION_F_NONE) + { + LOG_E("%s: BAR[bir = %d] is invalid", rt_dm_dev_get_name(&pdev->parent), bir); + + return RT_NULL; + } + + table_base_phys = pdev->resource[bir].base + (table_offset & ~PCIM_MSIX_BIR_MASK); + + return rt_ioremap((void *)table_base_phys, entries_nr * PCIM_MSIX_ENTRY_SIZE); +} + +static rt_err_t msix_setup_msi_descs(struct rt_pci_device *pdev, + void *table_base, struct rt_pci_msix_entry *entries, int nvec) +{ + rt_err_t err; + struct rt_pci_msi_desc desc; + + rt_memset(&desc, 0, sizeof(desc)); + + desc.vector_used = 1; + desc.vector_count = rt_pci_msix_vector_count(pdev); + + desc.is_msix = RT_TRUE; + desc.msix.table_base = table_base; + + for (int i = 0; i < nvec; ++i) + { + void *table_entry; + int index = entries ? entries[i].index : i; + + desc.msix.index = index; + table_entry = msix_table_base(&desc.msix); + + desc.msix.msg_ctrl = HWREG32(table_entry + PCIM_MSIX_ENTRY_VECTOR_CTRL); + + if ((err = msi_insert_desc(pdev, &desc))) + { + break; + } + } + + return err; +} + +static rt_ssize_t msix_capability_init(struct rt_pci_device *pdev, + struct rt_pci_msix_entry *entries, int nvec, + RT_IRQ_AFFINITY_DECLARE((*affinities))) +{ + rt_err_t err; + rt_uint16_t msgctl; + rt_size_t table_size; + void *table_base, *table_entry; + struct rt_pci_msi_desc *desc; + struct rt_pci_msix_entry *entry; + + /* + * Some devices require MSI-X to be enabled before the MSI-X + * registers can be accessed. + * Mask all the vectors to prevent interrupts coming in before + * they're fully set up. + */ + msix_update_ctrl(pdev, 0, PCIM_MSIXCTRL_FUNCTION_MASK | PCIM_MSIXCTRL_MSIX_ENABLE); + + rt_pci_read_config_u16(pdev, pdev->msix_cap + PCIR_MSIX_CTRL, &msgctl); + /* Request & Map MSI-X table region */ + table_size = rt_pci_msix_table_size(msgctl); + table_base = msix_table_remap(pdev, table_size); + + if (!table_base) + { + LOG_E("%s: Remap MSI-X table fail", rt_dm_dev_get_name(&pdev->parent)); + + err = -RT_ENOMEM; + goto _out_disbale_msix; + } + + pdev->msix_base = table_base; + + spin_lock(&pdev->msi_lock); + + if (!(err = msix_setup_msi_descs(pdev, table_base, entries, nvec))) + { + if (!(err = rt_pci_msi_setup_irqs(pdev, nvec, PCIY_MSIX))) + { + /* Check if all MSI entries honor device restrictions */ + err = msi_verify_entries(pdev); + } + } + + spin_unlock(&pdev->msi_lock); + + if (err) + { + rt_pci_msi_free_irqs(pdev); + + LOG_E("%s: Setup %s interrupts(%d) error = %s", + rt_dm_dev_get_name(&pdev->parent), "MSI-X", nvec, rt_strerror(err)); + + goto _out_disbale_msix; + } + + entry = entries; + rt_pci_msi_for_each_desc(pdev, desc) + { + if (affinities) + { + msi_affinity_init(desc, desc->msix.index, affinities[entry->index]); + } + + entry->irq = desc->irq; + ++entry; + } + + /* Disable INTX */ + rt_pci_intx(pdev, RT_FALSE); + + /* Maske all table entries */ + table_entry = table_base; + for (int i = 0; i < table_size; ++i, table_entry += PCIM_MSIX_ENTRY_SIZE) + { + HWREG32(table_entry + PCIM_MSIX_ENTRY_VECTOR_CTRL) = PCIM_MSIX_ENTRYVECTOR_CTRL_MASK; + } + msix_update_ctrl(pdev, PCIM_MSIXCTRL_FUNCTION_MASK, 0); + + pdev->msix_enabled = RT_TRUE; + + return nvec; + +_out_disbale_msix: + msix_update_ctrl(pdev, PCIM_MSIXCTRL_FUNCTION_MASK | PCIM_MSIXCTRL_MSIX_ENABLE, 0); + + return err; +} + +rt_ssize_t rt_pci_msix_enable_range_affinity(struct rt_pci_device *pdev, + struct rt_pci_msix_entry *entries, int min, int max, + RT_IRQ_AFFINITY_DECLARE((*affinities))) +{ + int nvec = max; + rt_size_t entries_nr; + + if (!pdev || min > max) + { + return -RT_EINVAL; + } + + if (pdev->no_msi) + { + return -RT_ENOSYS; + } + + if (!pdev->msi_pic) + { + return -RT_ENOSYS; + } + + if (pdev->msix_enabled) + { + LOG_W("%s: MSI-X is enabled", rt_dm_dev_get_name(&pdev->parent)); + + return -RT_EINVAL; + } + + entries_nr = rt_pci_msix_vector_count(pdev); + + if (entries_nr < 0) + { + return entries_nr; + } + + if (nvec > entries_nr) + { + return -RT_EEMPTY; + } + + if (!entries) + { + return 0; + } + + /* Check if entries is valid */ + for (int i = 0; i < nvec; ++i) + { + struct rt_pci_msix_entry *target = &entries[i]; + + if (target->index >= entries_nr) + { + return -RT_EINVAL; + } + + for (int j = i + 1; j < nvec; ++j) + { + /* Check duplicate */ + if (target->index == entries[j].index) + { + LOG_E("%s: msix entry[%d].index = entry[%d].index", + rt_dm_dev_get_name(&pdev->parent), i, j); + + return -RT_EINVAL; + } + } + } + + return msix_capability_init(pdev, entries, nvec, affinities); +} diff --git a/rt-thread/components/drivers/pci/ofw.c b/rt-thread/components/drivers/pci/ofw.c new file mode 100644 index 0000000..0207e7a --- /dev/null +++ b/rt-thread/components/drivers/pci/ofw.c @@ -0,0 +1,609 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "pci.ofw" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include +#include + +static rt_err_t pci_ofw_irq_parse(struct rt_pci_device *pdev, struct rt_ofw_cell_args *out_irq) +{ + rt_err_t err = RT_EOK; + rt_uint8_t pin; + fdt32_t map_addr[4]; + struct rt_pci_device *p2pdev; + struct rt_ofw_node *dev_np, *p2pnode = RT_NULL; + + /* Parse device tree if dev have a device node */ + dev_np = pdev->parent.ofw_node; + + if (dev_np) + { + err = rt_ofw_parse_irq_cells(dev_np, 0, out_irq); + + if (err) + { + return err; + } + } + + /* Assume #interrupt-cells is 1 */ + if ((err = rt_pci_read_config_u8(pdev, PCIR_INTPIN, &pin))) + { + goto _err; + } + + /* No pin, exit with no error message. */ + if (pin == 0) + { + return -RT_ENOSYS; + } + + /* Try local interrupt-map in the device node */ + if (rt_ofw_prop_read_raw(dev_np, "interrupt-map", RT_NULL)) + { + pin = rt_pci_irq_intx(pdev, pin); + p2pnode = dev_np; + } + + /* Walk up the PCI tree */ + while (!p2pnode) + { + p2pdev = pdev->bus->self; + + /* Is the root bus -> host bridge */ + if (rt_pci_is_root_bus(pdev->bus)) + { + struct rt_pci_host_bridge *host_bridge = pdev->bus->host_bridge; + + p2pnode = host_bridge->parent.ofw_node; + + if (!p2pnode) + { + err = -RT_EINVAL; + + goto _err; + } + } + else + { + /* Is P2P bridge */ + p2pnode = p2pdev->parent.ofw_node; + } + + if (p2pnode) + { + break; + } + + /* Try get INTx in P2P */ + pin = rt_pci_irq_intx(pdev, pin); + pdev = p2pdev; + } + + /* For more format detail, please read `components/drivers/ofw/irq.c:ofw_parse_irq_map` */ + out_irq->data = map_addr; + out_irq->args_count = 2; + out_irq->args[0] = 3; + out_irq->args[1] = 1; + + /* In addr cells */ + map_addr[0] = cpu_to_fdt32((pdev->bus->number << 16) | (pdev->devfn << 8)); + map_addr[1] = cpu_to_fdt32(0); + map_addr[2] = cpu_to_fdt32(0); + /* In pin cells */ + map_addr[3] = cpu_to_fdt32(pin); + + err = rt_ofw_parse_irq_map(p2pnode, out_irq); + +_err: + if (err == -RT_EEMPTY) + { + LOG_W("PCI-Device<%s> no interrupt-map found, INTx interrupts not available", + rt_dm_dev_get_name(&pdev->parent)); + LOG_W("PCI-Device<%s> possibly some PCI slots don't have level triggered interrupts capability", + rt_dm_dev_get_name(&pdev->parent)); + } + else if (err && err != -RT_ENOSYS) + { + LOG_E("PCI-Device<%s> irq parse failed with err = %s", + rt_dm_dev_get_name(&pdev->parent), rt_strerror(err)); + } + + return err; +} + +int rt_pci_ofw_irq_parse_and_map(struct rt_pci_device *pdev, + rt_uint8_t slot, rt_uint8_t pin) +{ + int irq = -1; + rt_err_t status; + struct rt_ofw_cell_args irq_args; + + if (!pdev) + { + goto _end; + } + + status = pci_ofw_irq_parse(pdev, &irq_args); + + if (status) + { + goto _end; + } + + irq = rt_ofw_map_irq(&irq_args); + + if (irq >= 0) + { + pdev->intx_pic = rt_pic_dynamic_cast(rt_ofw_data(irq_args.data)); + } + +_end: + return irq; +} + +static rt_err_t pci_ofw_parse_ranges(struct rt_ofw_node *dev_np, const char *propname, + int phy_addr_cells, int phy_size_cells, int cpu_addr_cells, + struct rt_pci_bus_region **out_regions, rt_size_t *out_regions_nr) +{ + const fdt32_t *cell; + rt_ssize_t total_cells; + int groups, space_code; + rt_uint32_t phy_addr[3]; + rt_uint64_t cpu_addr, phy_addr_size; + + *out_regions = RT_NULL; + *out_regions_nr = 0; + cell = rt_ofw_prop_read_raw(dev_np, propname, &total_cells); + + if (!cell) + { + return -RT_EEMPTY; + } + + groups = total_cells / sizeof(*cell) / (phy_addr_cells + phy_size_cells + cpu_addr_cells); + *out_regions = rt_malloc(groups * sizeof(struct rt_pci_bus_region)); + + if (!*out_regions) + { + return -RT_ENOMEM; + } + + for (int i = 0; i < groups; ++i) + { + /* + * ranges: + * phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr + * phys.low cell: llllllll llllllll llllllll llllllll + * phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh + * + * n: relocatable region flag (doesn't play a role here) + * p: prefetchable (cacheable) region flag + * t: aliased address flag (doesn't play a role here) + * ss: space code + * 00: configuration space + * 01: I/O space + * 10: 32 bit memory space + * 11: 64 bit memory space + * bbbbbbbb: The PCI bus number + * ddddd: The device number + * fff: The function number. Used for multifunction PCI devices. + * rrrrrrrr: Register number; used for configuration cycles. + */ + + for (int j = 0; j < phy_addr_cells; ++j) + { + phy_addr[j] = rt_fdt_read_number(cell++, 1); + } + + space_code = (phy_addr[0] >> 24) & 0x3; + + cpu_addr = rt_fdt_read_number(cell, cpu_addr_cells); + cell += cpu_addr_cells; + phy_addr_size = rt_fdt_read_number(cell, phy_size_cells); + cell += phy_size_cells; + + (*out_regions)[i].phy_addr = ((rt_uint64_t)phy_addr[1] << 32) | phy_addr[2]; + (*out_regions)[i].cpu_addr = cpu_addr; + (*out_regions)[i].size = phy_addr_size; + + (*out_regions)[i].bus_start = (*out_regions)[i].phy_addr; + + if (space_code & 2) + { + (*out_regions)[i].flags = phy_addr[0] & (1U << 30) ? + PCI_BUS_REGION_F_PREFETCH : PCI_BUS_REGION_F_MEM; + } + else if (space_code & 1) + { + (*out_regions)[i].flags = PCI_BUS_REGION_F_IO; + } + else + { + (*out_regions)[i].flags = PCI_BUS_REGION_F_NONE; + } + + ++*out_regions_nr; + } + + return RT_EOK; +} + +rt_err_t rt_pci_ofw_parse_ranges(struct rt_ofw_node *dev_np, + struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err; + int phy_addr_cells = -1, phy_size_cells = -1, cpu_addr_cells; + + if (!dev_np || !host_bridge) + { + return -RT_EINVAL; + } + + cpu_addr_cells = rt_ofw_io_addr_cells(dev_np); + rt_ofw_prop_read_s32(dev_np, "#address-cells", &phy_addr_cells); + rt_ofw_prop_read_s32(dev_np, "#size-cells", &phy_size_cells); + + if (phy_addr_cells != 3 || phy_size_cells < 1 || cpu_addr_cells < 1) + { + return -RT_EINVAL; + } + + if (pci_ofw_parse_ranges(dev_np, "ranges", + phy_addr_cells, phy_size_cells, cpu_addr_cells, + &host_bridge->bus_regions, &host_bridge->bus_regions_nr)) + { + return -RT_EINVAL; + } + + if ((err = rt_pci_region_setup(host_bridge))) + { + rt_free(host_bridge->bus_regions); + host_bridge->bus_regions_nr = 0; + + return err; + } + + err = pci_ofw_parse_ranges(dev_np, "dma-ranges", + phy_addr_cells, phy_size_cells, cpu_addr_cells, + &host_bridge->dma_regions, &host_bridge->dma_regions_nr); + + if (err != -RT_EEMPTY) + { + rt_free(host_bridge->bus_regions); + host_bridge->bus_regions_nr = 0; + + LOG_E("%s: Read dma-ranges error = %s", rt_ofw_node_full_name(dev_np), + rt_strerror(err)); + + return err; + } + + return RT_EOK; +} + +rt_err_t rt_pci_ofw_host_bridge_init(struct rt_ofw_node *dev_np, + struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err; + const char *propname; + + if (!dev_np || !host_bridge) + { + return -RT_EINVAL; + } + + host_bridge->irq_slot = rt_pci_irq_slot; + host_bridge->irq_map = rt_pci_ofw_irq_parse_and_map; + + if (rt_ofw_prop_read_u32_array_index(dev_np, "bus-range", 0, 2, host_bridge->bus_range) < 0) + { + return -RT_EIO; + } + + propname = rt_ofw_get_prop_fuzzy_name(dev_np, ",pci-domain$"); + rt_ofw_prop_read_u32(dev_np, propname, &host_bridge->domain); + + err = rt_pci_ofw_parse_ranges(dev_np, host_bridge); + + return err; +} + +rt_err_t rt_pci_ofw_bus_init(struct rt_pci_bus *bus) +{ + rt_err_t err = RT_EOK; + + return err; +} + +rt_err_t rt_pci_ofw_bus_free(struct rt_pci_bus *bus) +{ + rt_err_t err = RT_EOK; + + return err; +} + +/* + * RID (Requester ID) is formatted such that: + * Bits [15:8] are the Bus number. + * Bits [7:3] are the Device number. + * Bits [2:0] are the Function number. + * + * msi-map: Maps a Requester ID to an MSI controller and associated + * msi-specifier data. The property is an arbitrary number of tuples of + * (rid-base,msi-controller,msi-base,length), where: + * + * - rid-base is a single cell describing the first RID matched by the entry. + * + * - msi-controller is a single phandle to an MSI controller + * + * - msi-base is an msi-specifier describing the msi-specifier produced for + * the first RID matched by the entry. + * + * - length is a single cell describing how many consecutive RIDs are matched + * following the rid-base. + * + * Any RID r in the interval [rid-base, rid-base + length) is associated with + * the listed msi-controller, with the msi-specifier (r - rid-base + msi-base). + * + * msi-map-mask: A mask to be applied to each Requester ID prior to being mapped + * to an msi-specifier per the msi-map property. + * + * msi-parent: Describes the MSI parent of the root complex itself. Where + * the root complex and MSI controller do not pass sideband data with MSI + * writes, this property may be used to describe the MSI controller(s) + * used by PCI devices under the root complex, if defined as such in the + * binding for the root complex. + * + * / { + * #address-cells = <1>; + * #size-cells = <1>; + * + * msi_a: msi-controller@a { + * reg = <0xa 0x1>; + * msi-controller; + * #msi-cells = <1>; + * }; + * + * msi_b: msi-controller@b { + * reg = <0xb 0x1>; + * msi-controller; + * #msi-cells = <1>; + * }; + * + * msi_c: msi-controller@c { + * reg = <0xc 0x1>; + * msi-controller; + * #msi-cells = <1>; + * }; + * + * Example (1) + * =========== + * pci: pci@f { + * reg = <0xf 0x1>; + * device_type = "pci"; + * + * // The sideband data provided to the MSI controller is + * // the RID, identity-mapped. + * msi-map = <0x0 &msi_a 0x0 0x10000>; + * }; + * + * Example (2) + * =========== + * pci: pci@ff { + * reg = <0xff 0x1>; + * device_type = "pci"; + * + * // The sideband data provided to the MSI controller is + * // the RID, masked to only the device and function bits. + * msi-map = <0x0 &msi_a 0x0 0x100>; + * msi-map-mask = <0xff> + * }; + * + * Example (3) + * =========== + * pci: pci@fff { + * reg = <0xfff 0x1>; + * device_type = "pci"; + * + * // The sideband data provided to the MSI controller is + * // the RID, but the high bit of the bus number is ignored. + * msi-map = <0x0000 &msi_a 0x0000 0x8000>, + * <0x8000 &msi_a 0x0000 0x8000>; + * }; + * + * Example (4) + * =========== + * pci: pci@f { + * reg = <0xf 0x1>; + * device_type = "pci"; + * + * // The sideband data provided to the MSI controller is + * // the RID, but the high bit of the bus number is negated. + * msi-map = <0x0000 &msi 0x8000 0x8000>, + * <0x8000 &msi 0x0000 0x8000>; + * }; + * + * Example (5) + * =========== + * pci: pci@f { + * reg = <0xf 0x1>; + * device_type = "pci"; + * + * // The sideband data provided to MSI controller a is the + * // RID, but the high bit of the bus number is negated. + * // The sideband data provided to MSI controller b is the + * // RID, identity-mapped. + * // MSI controller c is not addressable. + * msi-map = <0x0000 &msi_a 0x8000 0x08000>, + * <0x8000 &msi_a 0x0000 0x08000>, + * <0x0000 &msi_b 0x0000 0x10000>; + * }; + * }; + */ +static void ofw_msi_pic_init(struct rt_pci_device *pdev) +{ +#ifdef RT_PCI_MSI + rt_uint32_t rid; + struct rt_pci_host_bridge *bridge; + struct rt_ofw_node *np, *msi_ic_np = RT_NULL; + + /* + * NOTE: Typically, a device's RID is equal to the PCI device's ID. + * However, in complex bus management scenarios such as servers and PCs, + * the RID needs to be associated with DMA. In these cases, + * the RID should be equal to the DMA alias assigned to the + * PCI device by the system bus. + */ + rid = rt_pci_dev_id(pdev); + + bridge = rt_pci_find_host_bridge(pdev->bus); + RT_ASSERT(bridge != RT_NULL); + + np = bridge->parent.ofw_node; + + if (!(msi_ic_np = rt_ofw_parse_phandle(np, "msi-parent", 0))) + { + rt_ofw_map_id(np, rid, "msi-map", "msi-map-mask", &msi_ic_np, RT_NULL); + } + + if (!msi_ic_np) + { + LOG_W("%s: MSI PIC not found", rt_dm_dev_get_name(&pdev->parent)); + + return; + } + + pdev->msi_pic = rt_pic_dynamic_cast(rt_ofw_data(msi_ic_np)); + + if (!pdev->msi_pic) + { + LOG_W("%s: '%s' not supported", rt_dm_dev_get_name(&pdev->parent), "msi-parent"); + + goto _out_put_msi_parent_node; + } + + if (!pdev->msi_pic->ops->irq_compose_msi_msg) + { + LOG_E("%s: MSI pic MUST implemented %s", + rt_ofw_node_full_name(msi_ic_np), "irq_compose_msi_msg"); + RT_ASSERT(0); + } + + if (!pdev->msi_pic->ops->irq_alloc_msi) + { + LOG_E("%s: MSI pic MUST implemented %s", + rt_ofw_node_full_name(msi_ic_np), "irq_alloc_msi"); + RT_ASSERT(0); + } + + if (!pdev->msi_pic->ops->irq_free_msi) + { + LOG_E("%s: MSI pic MUST implemented %s", + rt_ofw_node_full_name(msi_ic_np), "irq_free_msi"); + RT_ASSERT(0); + } + +_out_put_msi_parent_node: + rt_ofw_node_put(msi_ic_np); +#endif +} + +static rt_int32_t ofw_pci_devfn(struct rt_ofw_node *np) +{ + rt_int32_t res; + rt_uint32_t reg[5]; + + res = rt_ofw_prop_read_u32_array_index(np, "reg", 0, RT_ARRAY_SIZE(reg), reg); + + return res > 0 ? ((reg[0] >> 8) & 0xff) : res; +} + +static struct rt_ofw_node *ofw_find_device(struct rt_ofw_node *np, rt_uint32_t devfn) +{ + struct rt_ofw_node *dev_np, *mfd_np; + + rt_ofw_foreach_child_node(np, dev_np) + { + if (ofw_pci_devfn(dev_np) == devfn) + { + return dev_np; + } + + if (rt_ofw_node_tag_equ(dev_np, "multifunc-device")) + { + rt_ofw_foreach_child_node(dev_np, mfd_np) + { + if (ofw_pci_devfn(mfd_np) == devfn) + { + rt_ofw_node_put(dev_np); + + return mfd_np; + } + } + } + } + + return RT_NULL; +} + +rt_err_t rt_pci_ofw_device_init(struct rt_pci_device *pdev) +{ + struct rt_ofw_node *np = RT_NULL; + + if (!pdev) + { + return -RT_EINVAL; + } + + ofw_msi_pic_init(pdev); + + if (rt_pci_is_root_bus(pdev->bus) || !pdev->bus->self) + { + struct rt_pci_host_bridge *host_bridge; + + host_bridge = rt_pci_find_host_bridge(pdev->bus); + RT_ASSERT(host_bridge != RT_NULL); + + np = host_bridge->parent.ofw_node; + } + else + { + np = pdev->bus->self->parent.ofw_node; + } + + if (np) + { + pdev->parent.ofw_node = ofw_find_device(np, pdev->devfn); + } + + return RT_EOK; +} + +rt_err_t rt_pci_ofw_device_free(struct rt_pci_device *pdev) +{ + if (!pdev) + { + return -RT_EINVAL; + } + + rt_ofw_node_put(pdev->parent.ofw_node); + + return RT_EOK; +} diff --git a/rt-thread/components/drivers/pci/pci.c b/rt-thread/components/drivers/pci/pci.c new file mode 100644 index 0000000..369856e --- /dev/null +++ b/rt-thread/components/drivers/pci/pci.c @@ -0,0 +1,1018 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.pci" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include + +rt_inline void spin_lock(struct rt_spinlock *spinlock) +{ + rt_hw_spin_lock(&spinlock->lock); +} + +rt_inline void spin_unlock(struct rt_spinlock *spinlock) +{ + rt_hw_spin_unlock(&spinlock->lock); +} + +rt_uint32_t rt_pci_domain(struct rt_pci_device *pdev) +{ + struct rt_pci_host_bridge *host_bridge; + + if (!pdev) + { + return RT_UINT32_MAX; + } + + if ((host_bridge = rt_pci_find_host_bridge(pdev->bus))) + { + return host_bridge->domain; + } + + return RT_UINT32_MAX; +} + +static rt_uint8_t pci_find_next_cap_ttl(struct rt_pci_bus *bus, + rt_uint32_t devfn, rt_uint8_t pos, int cap, int *ttl) +{ + rt_uint8_t ret = 0, id; + rt_uint16_t ent; + + rt_pci_bus_read_config_u8(bus, devfn, pos, &pos); + + while ((*ttl)--) + { + if (pos < 0x40) + { + break; + } + + pos &= ~3; + rt_pci_bus_read_config_u16(bus, devfn, pos, &ent); + + id = ent & 0xff; + if (id == 0xff) + { + break; + } + if (id == cap) + { + ret = pos; + break; + } + pos = (ent >> 8); + } + + return ret; +} + +static rt_uint8_t pci_find_next_cap(struct rt_pci_bus *bus, + rt_uint32_t devfn, rt_uint8_t pos, int cap) +{ + int ttl = RT_PCI_FIND_CAP_TTL; + + return pci_find_next_cap_ttl(bus, devfn, pos, cap, &ttl); +} + +static rt_uint8_t pci_bus_find_cap_start(struct rt_pci_bus *bus, + rt_uint32_t devfn, rt_uint8_t hdr_type) +{ + rt_uint8_t res = 0; + rt_uint16_t status; + + rt_pci_bus_read_config_u16(bus, devfn, PCIR_STATUS, &status); + + if (status & PCIM_STATUS_CAPPRESENT) + { + switch (hdr_type) + { + case PCIM_HDRTYPE_NORMAL: + case PCIM_HDRTYPE_BRIDGE: + res = PCIR_CAP_PTR; + break; + + case PCIM_HDRTYPE_CARDBUS: + res = PCIR_CAP_PTR_2; + break; + } + } + + return res; +} + +rt_uint8_t rt_pci_bus_find_capability(struct rt_pci_bus *bus, rt_uint32_t devfn, int cap) +{ + rt_uint8_t hdr_type, ret = RT_UINT8_MAX; + + if (bus) + { + rt_pci_bus_read_config_u8(bus, devfn, PCIR_HDRTYPE, &hdr_type); + + ret = pci_bus_find_cap_start(bus, devfn, hdr_type & PCIM_HDRTYPE); + + if (ret) + { + ret = pci_find_next_cap(bus, devfn, ret, cap); + } + } + + return ret; +} + +rt_uint8_t rt_pci_find_capability(struct rt_pci_device *pdev, int cap) +{ + rt_uint8_t res = RT_UINT8_MAX; + + if (pdev) + { + res = pci_bus_find_cap_start(pdev->bus, pdev->devfn, pdev->hdr_type); + + if (res) + { + res = pci_find_next_cap(pdev->bus, pdev->devfn, res, cap); + } + } + + return res; +} + +rt_uint8_t rt_pci_find_next_capability(struct rt_pci_device *pdev, rt_uint8_t pos, int cap) +{ + rt_uint8_t res = RT_UINT8_MAX; + + if (pdev) + { + res = pci_find_next_cap(pdev->bus, pdev->devfn, pos + PCICAP_NEXTPTR, cap); + } + + return res; +} + +rt_uint16_t rt_pci_find_ext_capability(struct rt_pci_device *pdev, int cap) +{ + return rt_pci_find_ext_next_capability(pdev, 0, cap); +} + +rt_uint16_t rt_pci_find_ext_next_capability(struct rt_pci_device *pdev, rt_uint16_t pos, int cap) +{ + int ttl; + rt_uint32_t header; + rt_uint16_t start = pos; + + /* minimum 8 bytes per capability */ + ttl = ((PCIE_REGMAX + 1) - (PCI_REGMAX + 1)) / 8; + + if (pdev->cfg_size <= PCI_REGMAX + 1) + { + return 0; + } + + if (!pos) + { + pos = PCI_REGMAX + 1; + } + + if (rt_pci_read_config_u32(pdev, pos, &header)) + { + return 0; + } + + /* + * If we have no capabilities, this is indicated by cap ID, + * cap version and next pointer all being 0. + */ + if (header == 0) + { + return 0; + } + + while (ttl-- > 0) + { + if (PCI_EXTCAP_ID(header) == cap && pos != start) + { + return pos; + } + + pos = PCI_EXTCAP_NEXTPTR(header); + + if (pos < PCI_REGMAX + 1) + { + break; + } + + if (rt_pci_read_config_u32(pdev, pos, &header)) + { + break; + } + } + + return 0; +} + +static void pci_set_master(struct rt_pci_device *pdev, rt_bool_t enable) +{ + rt_uint16_t old_cmd, cmd; + + rt_pci_read_config_u16(pdev, PCIR_COMMAND, &old_cmd); + + if (enable) + { + cmd = old_cmd | PCIM_CMD_BUSMASTEREN; + } + else + { + cmd = old_cmd & ~PCIM_CMD_BUSMASTEREN; + } + + if (cmd != old_cmd) + { + rt_pci_write_config_u16(pdev, PCIR_COMMAND, cmd); + } + + pdev->busmaster = !!enable; +} + +void rt_pci_set_master(struct rt_pci_device *pdev) +{ + if (pdev) + { + pci_set_master(pdev, RT_TRUE); + } +} + +void rt_pci_clear_master(struct rt_pci_device *pdev) +{ + if (pdev) + { + pci_set_master(pdev, RT_FALSE); + } +} + +void rt_pci_intx(struct rt_pci_device *pdev, rt_bool_t enable) +{ + rt_uint16_t pci_command, new; + + if (!pdev) + { + return; + } + + rt_pci_read_config_u16(pdev, PCIR_COMMAND, &pci_command); + + if (enable) + { + new = pci_command & ~PCIM_CMD_INTxDIS; + } + else + { + new = pci_command | PCIM_CMD_INTxDIS; + } + + if (new != pci_command) + { + rt_pci_write_config_u16(pdev, PCIR_COMMAND, new); + } +} + +static rt_bool_t pci_check_and_set_intx_mask(struct rt_pci_device *pdev, rt_bool_t mask) +{ + rt_ubase_t level; + rt_bool_t irq_pending; + rt_bool_t res = RT_TRUE; + rt_uint16_t origcmd, newcmd; + rt_uint32_t cmd_status_dword; + struct rt_pci_bus *bus = pdev->bus; + + level = rt_spin_lock_irqsave(&rt_pci_lock); + + bus->ops->read(bus, pdev->devfn, PCIR_COMMAND, 4, &cmd_status_dword); + + irq_pending = (cmd_status_dword >> 16) & PCIM_STATUS_INTxSTATE; + + /* + * Check interrupt status register to see whether our device + * triggered the interrupt (when masking) or the next IRQ is + * already pending (when unmasking). + */ + if (mask != irq_pending) + { + res = RT_FALSE; + } + else + { + origcmd = cmd_status_dword; + newcmd = origcmd & ~PCIM_CMD_INTxDIS; + + if (mask) + { + newcmd |= PCIM_CMD_INTxDIS; + } + if (newcmd != origcmd) + { + bus->ops->write(bus, pdev->devfn, PCIR_COMMAND, 2, newcmd); + } + } + + rt_spin_unlock_irqrestore(&rt_pci_lock, level); + + return res; +} + +rt_bool_t rt_pci_check_and_mask_intx(struct rt_pci_device *pdev) +{ + rt_bool_t res = RT_FALSE; + + if (pdev) + { + res = pci_check_and_set_intx_mask(pdev, RT_TRUE); + } + + return res; +} + +rt_bool_t rt_pci_check_and_unmask_intx(struct rt_pci_device *pdev) +{ + rt_bool_t res = RT_FALSE; + + if (pdev) + { + res = pci_check_and_set_intx_mask(pdev, RT_FALSE); + } + + return res; +} + +void rt_pci_irq_mask(struct rt_pci_device *pdev) +{ + if (pdev) + { + rt_bool_t unused; + struct rt_pic_irq *pirq; + + rt_pci_intx(pdev, RT_FALSE); + + pirq = rt_pic_find_pirq(pdev->intx_pic, pdev->irq); + RT_ASSERT(pirq != RT_NULL); + + rt_hw_spin_lock(&pirq->rw_lock.lock); + unused = rt_list_isempty(&pirq->isr.list); + rt_hw_spin_unlock(&pirq->rw_lock.lock); + + if (unused) + { + rt_hw_interrupt_mask(pdev->irq); + } + } +} + +void rt_pci_irq_unmask(struct rt_pci_device *pdev) +{ + if (pdev) + { + rt_hw_interrupt_umask(pdev->irq); + rt_pci_intx(pdev, RT_TRUE); + } +} + +struct rt_pci_bus *rt_pci_find_root_bus(struct rt_pci_bus *bus) +{ + if (!bus) + { + return RT_NULL; + } + + while (bus->parent) + { + bus = bus->parent; + } + + return bus; +} + +struct rt_pci_host_bridge *rt_pci_find_host_bridge(struct rt_pci_bus *bus) +{ + if (!bus) + { + return RT_NULL; + } + + if ((bus = rt_pci_find_root_bus(bus))) + { + return rt_container_of(bus->host_bridge, struct rt_pci_host_bridge, parent); + } + + return RT_NULL; +} + +rt_uint8_t rt_pci_irq_intx(struct rt_pci_device *pdev, rt_uint8_t pin) +{ + int slot = 0; + + if (!pdev->ari_enabled) + { + slot = RT_PCI_SLOT(pdev->devfn); + } + + return (((pin - 1) + slot) % 4) + 1; +} + +rt_uint8_t rt_pci_irq_slot(struct rt_pci_device *pdev, rt_uint8_t *pinp) +{ + rt_uint8_t pin = *pinp; + + while (!rt_pci_is_root_bus(pdev->bus)) + { + pin = rt_pci_irq_intx(pdev, pin); + pdev = pdev->bus->self; + } + + *pinp = pin; + + return RT_PCI_SLOT(pdev->devfn); +} + +rt_err_t rt_pci_region_setup(struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err = host_bridge->bus_regions_nr == 0 ? -RT_EEMPTY : RT_EOK; + + for (int i = 0; i < host_bridge->bus_regions_nr; ++i) + { + struct rt_pci_bus_region *region = &host_bridge->bus_regions[i]; + /* + * Avoid allocating PCI resources from address 0 -- this is illegal + * according to PCI 2.1 and moreover. Use a reasonable starting value of + * 0x1000 instead if the bus start address is below 0x1000. + */ + region->bus_start = rt_max_t(rt_size_t, 0x1000, region->phy_addr); + + LOG_I("Bus %s region(%d):", + region->flags == PCI_BUS_REGION_F_MEM ? "Memory" : + (region->flags == PCI_BUS_REGION_F_PREFETCH ? "Prefetchable Mem" : + (region->flags == PCI_BUS_REGION_F_IO ? "I/O" : "Unknown")), i); + LOG_I(" cpu: [%p, %p]", region->cpu_addr, (region->cpu_addr + region->size - 1)); + LOG_I(" physical: [%p, %p]", region->phy_addr, (region->phy_addr + region->size - 1)); + } + + return err; +} + +struct rt_pci_bus_region *rt_pci_region_alloc(struct rt_pci_host_bridge *host_bridge, + void **out_addr, rt_size_t size, rt_ubase_t flags, rt_bool_t mem64) +{ + struct rt_pci_bus_region *bus_region, *region = RT_NULL; + + bus_region = &host_bridge->bus_regions[0]; + + for (int i = 0; i < host_bridge->bus_regions_nr; ++i, ++bus_region) + { + if (bus_region->flags == flags && bus_region->size > 0) + { + void *addr; + + region = bus_region; + addr = (void *)(((region->bus_start - 1) | (size - 1)) + 1); + + if ((rt_uint64_t)addr - region->phy_addr + size <= region->size) + { + rt_bool_t addr64 = !!rt_upper_32_bits((rt_ubase_t)addr); + + if (mem64) + { + if (!addr64) + { + region = RT_NULL; + + /* Try again */ + continue; + } + } + else if (addr64) + { + region = RT_NULL; + + /* Try again */ + continue; + } + + region->bus_start = ((rt_uint64_t)addr + size); + *out_addr = addr; + } + + break; + } + } + + if (!region && mem64) + { + /* Retry */ + region = rt_pci_region_alloc(host_bridge, out_addr, size, flags, RT_FALSE); + } + + return region; +} + +rt_err_t rt_pci_device_alloc_resource(struct rt_pci_host_bridge *host_bridge, + struct rt_pci_device *pdev) +{ + rt_err_t err = RT_EOK; + rt_size_t size; + rt_ubase_t addr = 0; + rt_uint32_t cfg; + rt_size_t bars_nr; + rt_uint8_t hdr_type; + rt_bool_t prefetch = RT_FALSE; + rt_uint16_t class, command = 0; + + for (int i = 0; i < host_bridge->bus_regions_nr; ++i) + { + if (host_bridge->bus_regions[i].flags == PCI_BUS_REGION_F_PREFETCH) + { + prefetch = RT_TRUE; + break; + } + } + + rt_pci_read_config_u16(pdev, PCIR_COMMAND, &command); + command = (command & ~(PCIM_CMD_PORTEN | PCIM_CMD_MEMEN)) | PCIM_CMD_BUSMASTEREN; + rt_pci_read_config_u8(pdev, PCIR_HDRTYPE, &hdr_type); + + if (pdev->hdr_type != hdr_type) + { + LOG_W("%s may not initialized", rt_dm_dev_get_name(&pdev->parent)); + } + + switch (hdr_type) + { + case PCIM_HDRTYPE_NORMAL: + bars_nr = PCI_STD_NUM_BARS; + break; + + case PCIM_HDRTYPE_BRIDGE: + bars_nr = 2; + break; + + case PCIM_HDRTYPE_CARDBUS: + bars_nr = 0; + break; + + default: + bars_nr = 0; + break; + } + + for (int i = 0; i < bars_nr; ++i) + { + rt_ubase_t flags; + rt_ubase_t bar_base; + rt_bool_t mem64 = RT_FALSE; + struct rt_pci_bus_region *region; + + cfg = 0; + bar_base = PCIR_BAR(i); + + rt_pci_write_config_u32(pdev, bar_base, RT_UINT32_MAX); + rt_pci_read_config_u32(pdev, bar_base, &cfg); + + if (!cfg) + { + continue; + } + else if (cfg == RT_UINT32_MAX) + { + rt_pci_write_config_u32(pdev, bar_base, 0UL); + continue; + } + + if (cfg & PCIM_BAR_SPACE) + { + mem64 = RT_FALSE; + flags = PCI_BUS_REGION_F_IO; + + size = cfg & PCIM_BAR_IO_MASK; + size &= ~(size - 1); + } + else + { + /* memory */ + if ((cfg & PCIM_BAR_MEM_TYPE_MASK) == PCIM_BAR_MEM_TYPE_64) + { + /* 64bits */ + rt_uint32_t cfg64; + rt_uint64_t bar64; + + mem64 = RT_TRUE; + + rt_pci_write_config_u32(pdev, bar_base + sizeof(rt_uint32_t), RT_UINT32_MAX); + rt_pci_read_config_u32(pdev, bar_base + sizeof(rt_uint32_t), &cfg64); + + bar64 = ((rt_uint64_t)cfg64 << 32) | cfg; + + size = ~(bar64 & PCIM_BAR_MEM_MASK) + 1; + } + else + { + /* 32bits */ + mem64 = RT_FALSE; + size = (rt_uint32_t)(~(cfg & PCIM_BAR_MEM_MASK) + 1); + } + + if (prefetch && (cfg & PCIM_BAR_MEM_PREFETCH)) + { + flags = PCI_BUS_REGION_F_PREFETCH; + } + else + { + flags = PCI_BUS_REGION_F_MEM; + } + } + + region = rt_pci_region_alloc(host_bridge, (void **)&addr, size, flags, mem64); + + if (region) + { + rt_pci_write_config_u32(pdev, bar_base, addr); + + if (mem64) + { + bar_base += sizeof(rt_uint32_t); + #ifdef RT_PCI_SYS_64BIT + rt_pci_write_config_u32(pdev, bar_base, (rt_uint32_t)(addr >> 32)); + #else + /* + * If we are a 64-bit decoder then increment to the upper 32 bits + * of the bar and force it to locate in the lower 4GB of memory. + */ + rt_pci_write_config_u32(pdev, bar_base, 0UL); + #endif + } + + pdev->resource[i].size = size; + pdev->resource[i].base = region->cpu_addr + (addr - region->phy_addr); + pdev->resource[i].flags = flags; + + if (mem64) + { + ++i; + pdev->resource[i].flags = PCI_BUS_REGION_F_NONE; + } + } + else + { + err = -RT_ERROR; + LOG_W("%s alloc bar(%d) address fail", rt_dm_dev_get_name(&pdev->parent), i); + } + + command |= (cfg & PCIM_BAR_SPACE) ? PCIM_CMD_PORTEN : PCIM_CMD_MEMEN; + } + + if (hdr_type == PCIM_HDRTYPE_NORMAL || hdr_type == PCIM_HDRTYPE_BRIDGE) + { + int rom_addr = (hdr_type == PCIM_HDRTYPE_NORMAL) ? PCIR_BIOS : PCIR_BIOS_1; + + rt_pci_write_config_u32(pdev, rom_addr, 0xfffffffe); + rt_pci_read_config_u32(pdev, rom_addr, &cfg); + + if (cfg) + { + size = -(cfg & ~1); + + if (rt_pci_region_alloc(host_bridge, (void **)&addr, size, PCI_BUS_REGION_F_MEM, RT_FALSE)) + { + rt_pci_write_config_u32(pdev, rom_addr, addr); + } + command |= PCIM_CMD_MEMEN; + } + } + + rt_pci_read_config_u16(pdev, PCIR_SUBCLASS, &class); + + if (class == PCIS_DISPLAY_VGA) + { + command |= PCIM_CMD_PORTEN; + } + + rt_pci_write_config_u16(pdev, PCIR_COMMAND, command); + rt_pci_write_config_u8(pdev, PCIR_CACHELNSZ, RT_PCI_CACHE_LINE_SIZE); + rt_pci_write_config_u8(pdev, PCIR_LATTIMER, 0x80); + + return err; +} + +struct rt_pci_bus_resource *rt_pci_find_bar(struct rt_pci_device* pdev,rt_ubase_t flags,int index) +{ + for (int i = 0; i < RT_PCI_BAR_NR_MAX; i++) + { + if (pdev->resource[i].flags == flags) + { + index--; + if (index == 0) + return &pdev->resource[i]; + } + } + return RT_NULL; +} + +void rt_pci_enum_device(struct rt_pci_bus *bus, + rt_bool_t (callback(struct rt_pci_device *, void *)), void *data) +{ + rt_bool_t is_end = RT_FALSE; + struct rt_spinlock *lock; + struct rt_pci_bus *parent; + struct rt_pci_device *pdev, *last_pdev = RT_NULL; + + /* Walk tree */ + while (bus && !is_end) + { + /* Goto bottom */ + for (;;) + { + lock = &bus->lock; + + spin_lock(lock); + if (rt_list_isempty(&bus->children_nodes)) + { + parent = bus->parent; + break; + } + bus = rt_list_entry(&bus->children_nodes, struct rt_pci_bus, list); + spin_unlock(lock); + } + + rt_list_for_each_entry(pdev, &bus->devices_nodes, list) + { + if (last_pdev) + { + spin_unlock(lock); + + if (callback(last_pdev, data)) + { + spin_lock(lock); + --last_pdev->parent.ref_count; + + is_end = RT_TRUE; + break; + } + + spin_lock(lock); + --last_pdev->parent.ref_count; + } + ++pdev->parent.ref_count; + last_pdev = pdev; + } + + if (!is_end && last_pdev) + { + spin_unlock(lock); + + if (callback(last_pdev, data)) + { + is_end = RT_TRUE; + } + + spin_lock(lock); + --last_pdev->parent.ref_count; + } + last_pdev = RT_NULL; + spin_unlock(lock); + + /* Up a level or goto next */ + while (!is_end) + { + lock = &bus->lock; + + if (!parent) + { + /* Root bus, is end */ + bus = RT_NULL; + break; + } + + spin_lock(lock); + if (bus->list.next != &parent->children_nodes) + { + /* Has next sibling */ + bus = rt_list_entry(bus->list.next, struct rt_pci_bus, list); + spin_unlock(lock); + break; + } + + /* All device on this buss' parent */ + rt_list_for_each_entry(pdev, &parent->devices_nodes, list) + { + if (last_pdev) + { + spin_unlock(lock); + + if (callback(last_pdev, data)) + { + spin_lock(lock); + --last_pdev->parent.ref_count; + + is_end = RT_TRUE; + break; + } + + spin_lock(lock); + --last_pdev->parent.ref_count; + } + ++pdev->parent.ref_count; + last_pdev = pdev; + } + + if (!is_end && last_pdev) + { + spin_unlock(lock); + + if (callback(last_pdev, data)) + { + is_end = RT_TRUE; + } + + spin_lock(lock); + --last_pdev->parent.ref_count; + } + last_pdev = RT_NULL; + + bus = parent; + parent = parent->parent; + spin_unlock(lock); + } + } +} + +const struct rt_pci_device_id *rt_pci_match_id(struct rt_pci_device *pdev, + const struct rt_pci_device_id *id) +{ + if ((id->vendor == PCI_ANY_ID || id->vendor == pdev->vendor) && + (id->device == PCI_ANY_ID || id->device == pdev->device) && + (id->subsystem_vendor == PCI_ANY_ID || id->subsystem_vendor == pdev->subsystem_vendor) && + (id->subsystem_device == PCI_ANY_ID || id->subsystem_device == pdev->subsystem_device) && + !((id->class ^ pdev->class) & id->class_mask)) + { + return id; + } + + return RT_NULL; +} + +const struct rt_pci_device_id *rt_pci_match_ids(struct rt_pci_device *pdev, + const struct rt_pci_device_id *ids) +{ + while (ids->vendor || ids->subsystem_vendor || ids->class_mask) + { + if (rt_pci_match_id(pdev, ids)) + { + return ids; + } + + ++ids; + } + + return RT_NULL; +} + +static struct rt_bus pci_bus; + +rt_err_t rt_pci_driver_register(struct rt_pci_driver *pdrv) +{ + RT_ASSERT(pdrv != RT_NULL); + + pdrv->parent.bus = &pci_bus; +#if RT_NAME_MAX > 0 + rt_strcpy(pdrv->parent.parent.name, pdrv->name); +#else + pdrv->parent.parent.name = pdrv->name; +#endif + + return rt_driver_register(&pdrv->parent); +} + +rt_err_t rt_pci_device_register(struct rt_pci_device *pdev) +{ + rt_err_t err; + RT_ASSERT(pdev != RT_NULL); + + if ((err = rt_bus_add_device(&pci_bus, &pdev->parent))) + { + return err; + } + + return RT_EOK; +} + +static rt_bool_t pci_match(rt_driver_t drv, rt_device_t dev) +{ + rt_bool_t match = RT_FALSE; + struct rt_pci_driver *pdrv = rt_container_of(drv, struct rt_pci_driver, parent); + struct rt_pci_device *pdev = rt_container_of(dev, struct rt_pci_device, parent); + + if (pdrv->name && pdev->name) + { + match = rt_strcmp(pdrv->name, pdev->name) ? RT_FALSE : RT_TRUE; + } + + if (!match) + { + pdev->id = rt_pci_match_ids(pdev, pdrv->ids); + + match = pdev->id ? RT_TRUE : RT_FALSE; + } + + return match; +} + +static rt_err_t pci_probe(rt_device_t dev) +{ + rt_err_t err = RT_EOK; + struct rt_pci_driver *pdrv = rt_container_of(dev->drv, struct rt_pci_driver, parent); + struct rt_pci_device *pdev = rt_container_of(dev, struct rt_pci_device, parent); + + rt_pci_assign_irq(pdev); + rt_pci_enable_wake(pdev, RT_PCI_D0, RT_TRUE); + + err = pdrv->probe(pdev); + + if (err) + { + rt_pci_enable_wake(pdev, RT_PCI_D0, RT_FALSE); + } + + return err; +} + +static rt_err_t pci_remove(rt_device_t dev) +{ + rt_err_t err = RT_EOK; + struct rt_pci_bus *bus; + struct rt_pci_driver *pdrv = rt_container_of(dev->drv, struct rt_pci_driver, parent); + struct rt_pci_device *pdev = rt_container_of(dev, struct rt_pci_device, parent); + + if (pdrv && pdrv->remove) + { + if ((err = pdrv->remove(pdev))) + { + return err; + } + } + + rt_pci_enable_wake(pdev, RT_PCI_D0, RT_FALSE); + + bus = pdev->bus; + rt_pci_device_remove(pdev); + /* Just try to remove */ + rt_pci_bus_remove(bus); + + return err; +} + +static rt_err_t pci_shutdown(rt_device_t dev) +{ + struct rt_pci_bus *bus; + struct rt_pci_driver *pdrv = rt_container_of(dev->drv, struct rt_pci_driver, parent); + struct rt_pci_device *pdev = rt_container_of(dev, struct rt_pci_device, parent); + + if (pdrv && pdrv->shutdown) + { + pdrv->shutdown(pdev); + } + + rt_pci_enable_wake(pdev, RT_PCI_D0, RT_FALSE); + + bus = pdev->bus; + rt_pci_device_remove(pdev); + /* Just try to remove */ + rt_pci_bus_remove(bus); + + return RT_EOK; +} + +static struct rt_bus pci_bus = +{ + .name = "pci", + .match = pci_match, + .probe = pci_probe, + .remove = pci_remove, + .shutdown = pci_shutdown, +}; + +static int pci_bus_init(void) +{ + rt_bus_register(&pci_bus); + + return 0; +} +INIT_CORE_EXPORT(pci_bus_init); diff --git a/rt-thread/components/drivers/pci/pci_ids.h b/rt-thread/components/drivers/pci/pci_ids.h new file mode 100644 index 0000000..2dab735 --- /dev/null +++ b/rt-thread/components/drivers/pci/pci_ids.h @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __PCI_IDS_H__ +#define __PCI_IDS_H__ + +#define PCI_VENDOR_ID_LOONGSON 0x0014 +#define PCI_VENDOR_ID_TTTECH 0x0357 +#define PCI_VENDOR_ID_DYNALINK 0x0675 +#define PCI_VENDOR_ID_UBIQUITI 0x0777 +#define PCI_VENDOR_ID_BERKOM 0x0871 +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_VENDOR_ID_NCR 0x1000 +#define PCI_VENDOR_ID_ATI 0x1002 +#define PCI_VENDOR_ID_VLSI 0x1004 +#define PCI_VENDOR_ID_ADL 0x1005 +#define PCI_VENDOR_ID_NS 0x100b +#define PCI_VENDOR_ID_TSENG 0x100c +#define PCI_VENDOR_ID_WEITEK 0x100e +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_VENDOR_ID_UNISYS 0x1018 +#define PCI_VENDOR_ID_COMPEX2 0x101a +#define PCI_VENDOR_ID_WD 0x101c +#define PCI_VENDOR_ID_AMI 0x101e +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#define PCI_VENDOR_ID_AI 0x1025 +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_VENDOR_ID_MATROX 0x102b +#define PCI_VENDOR_ID_MOBILITY_ELECTRONICS 0x14f2 +#define PCI_VENDOR_ID_CT 0x102c +#define PCI_VENDOR_ID_MIRO 0x1031 +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_VENDOR_ID_FD 0x1036 +#define PCI_VENDOR_ID_SI 0x1039 +#define PCI_VENDOR_ID_HP 0x103c +#define PCI_VENDOR_ID_PCTECH 0x1042 +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_VENDOR_ID_DPT 0x1044 +#define PCI_VENDOR_ID_OPTI 0x1045 +#define PCI_VENDOR_ID_ELSA 0x1048 +#define PCI_VENDOR_ID_STMICRO 0x104a +#define PCI_VENDOR_ID_BUSLOGIC 0x104b +#define PCI_VENDOR_ID_TI 0x104c +#define PCI_VENDOR_ID_SONY 0x104d +#define PCI_VENDOR_ID_ANIGMA 0x1051 +#define PCI_VENDOR_ID_EFAR 0x1055 +#define PCI_VENDOR_ID_MOTOROLA 0x1057 +#define PCI_VENDOR_ID_PROMISE 0x105a +#define PCI_VENDOR_ID_FOXCONN 0x105b +#define PCI_VENDOR_ID_UMC 0x1060 +#define PCI_VENDOR_ID_PICOPOWER 0x1066 +#define PCI_VENDOR_ID_MYLEX 0x1069 +#define PCI_VENDOR_ID_APPLE 0x106b +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#define PCI_VENDOR_ID_QLOGIC 0x1077 +#define PCI_VENDOR_ID_CYRIX 0x1078 +#define PCI_VENDOR_ID_CONTAQ 0x1080 +#define PCI_VENDOR_ID_OLICOM 0x108d +#define PCI_VENDOR_ID_SUN 0x108e +#define PCI_VENDOR_ID_NI 0x1093 +#define PCI_VENDOR_ID_CMD 0x1095 +#define PCI_VENDOR_ID_BROOKTREE 0x109e +#define PCI_VENDOR_ID_SGI 0x10a9 +#define PCI_VENDOR_ID_WINBOND 0x10ad +#define PCI_VENDOR_ID_PLX 0x10b5 +#define PCI_VENDOR_ID_MADGE 0x10b6 +#define PCI_VENDOR_ID_3COM 0x10b7 +#define PCI_VENDOR_ID_AL 0x10b9 +#define PCI_VENDOR_ID_NEOMAGIC 0x10c8 +#define PCI_VENDOR_ID_TCONRAD 0x10da +#define PCI_VENDOR_ID_ROHM 0x10db +#define PCI_VENDOR_ID_NVIDIA 0x10de +#define PCI_VENDOR_ID_IMS 0x10e0 +#define PCI_VENDOR_ID_AMCC 0x10e8 +#define PCI_VENDOR_ID_INTERG 0x10ea +#define PCI_VENDOR_ID_REALTEK 0x10ec +#define PCI_VENDOR_ID_XILINX 0x10ee +#define PCI_VENDOR_ID_INIT 0x1101 +#define PCI_VENDOR_ID_CREATIVE 0x1102 +#define PCI_VENDOR_ID_ECTIVA PCI_VENDOR_ID_CREATIVE +#define PCI_VENDOR_ID_TTI 0x1103 +#define PCI_VENDOR_ID_SIGMA 0x1105 +#define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_VENDOR_ID_SIEMENS 0x110a +#define PCI_VENDOR_ID_VORTEX 0x1119 +#define PCI_VENDOR_ID_EF 0x111a +#define PCI_VENDOR_ID_IDT 0x111d +#define PCI_VENDOR_ID_FORE 0x1127 +#define PCI_VENDOR_ID_PHILIPS 0x1131 +#define PCI_VENDOR_ID_EICON 0x1133 +#define PCI_VENDOR_ID_CISCO 0x1137 +#define PCI_VENDOR_ID_ZIATECH 0x1138 +#define PCI_VENDOR_ID_SYSKONNECT 0x1148 +#define PCI_VENDOR_ID_DIGI 0x114f +#define PCI_VENDOR_ID_XIRCOM 0x115d +#define PCI_VENDOR_ID_SERVERWORKS 0x1166 +#define PCI_VENDOR_ID_ALTERA 0x1172 +#define PCI_VENDOR_ID_SBE 0x1176 +#define PCI_VENDOR_ID_TOSHIBA 0x1179 +#define PCI_VENDOR_ID_TOSHIBA_2 0x102f +#define PCI_VENDOR_ID_ATTO 0x117c +#define PCI_VENDOR_ID_RICOH 0x1180 +#define PCI_VENDOR_ID_DLINK 0x1186 +#define PCI_VENDOR_ID_ARTOP 0x1191 +#define PCI_VENDOR_ID_ZEITNET 0x1193 +#define PCI_VENDOR_ID_FUJITSU_ME 0x119e +#define PCI_VENDOR_ID_MARVELL 0x11ab +#define PCI_VENDOR_ID_V3 0x11b0 +#define PCI_VENDOR_ID_ATT 0x11c1 +#define PCI_VENDOR_ID_SPECIALIX 0x11cb +#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 +#define PCI_VENDOR_ID_ZORAN 0x11de +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#define PCI_VENDOR_ID_PMC_Sierra 0x11f8 +#define PCI_VENDOR_ID_RP 0x11fe +#define PCI_VENDOR_ID_CYCLADES 0x120e +#define PCI_VENDOR_ID_ESSENTIAL 0x120f +#define PCI_VENDOR_ID_O2 0x1217 +#define PCI_VENDOR_ID_3DFX 0x121a +#define PCI_VENDOR_ID_QEMU 0x1234 +#define PCI_VENDOR_ID_AVM 0x1244 +#define PCI_VENDOR_ID_STALLION 0x124d +#define PCI_VENDOR_ID_ESS 0x125d +#define PCI_VENDOR_ID_SATSAGEM 0x1267 +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#define PCI_VENDOR_ID_TRANSMETA 0x1279 +#define PCI_VENDOR_ID_ROCKWELL 0x127a +#define PCI_VENDOR_ID_ITE 0x1283 +#define PCI_VENDOR_ID_ALTEON 0x12ae +#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2 +#define PCI_VENDOR_ID_PERICOM 0x12d8 +#define PCI_VENDOR_ID_AUREAL 0x12eb +#define PCI_VENDOR_ID_ELECTRONICDESIGNGMBH 0x12f8 +#define PCI_VENDOR_ID_ESDGMBH 0x12fe +#define PCI_VENDOR_ID_CB 0x1307 +#define PCI_VENDOR_ID_SIIG 0x131f +#define PCI_VENDOR_ID_RADISYS 0x1331 +#define PCI_VENDOR_ID_MICRO_MEMORY 0x1332 +#define PCI_VENDOR_ID_DOMEX 0x134a +#define PCI_VENDOR_ID_INTASHIELD 0x135a +#define PCI_VENDOR_ID_QUATECH 0x135c +#define PCI_VENDOR_ID_SEALEVEL 0x135e +#define PCI_VENDOR_ID_HYPERCOPE 0x1365 +#define PCI_VENDOR_ID_DIGIGRAM 0x1369 +#define PCI_VENDOR_ID_KAWASAKI 0x136b +#define PCI_VENDOR_ID_CNET 0x1371 +#define PCI_VENDOR_ID_LMC 0x1376 +#define PCI_VENDOR_ID_NETGEAR 0x1385 +#define PCI_VENDOR_ID_APPLICOM 0x1389 +#define PCI_VENDOR_ID_MOXA 0x1393 +#define PCI_VENDOR_ID_CCD 0x1397 +#define PCI_VENDOR_ID_EXAR 0x13a8 +#define PCI_VENDOR_ID_MICROGATE 0x13c0 +#define PCI_VENDOR_ID_3WARE 0x13c1 +#define PCI_VENDOR_ID_IOMEGA 0x13ca +#define PCI_VENDOR_ID_ABOCOM 0x13d1 +#define PCI_VENDOR_ID_SUNDANCE 0x13f0 +#define PCI_VENDOR_ID_CMEDIA 0x13f6 +#define PCI_VENDOR_ID_ADVANTECH 0x13fe +#define PCI_VENDOR_ID_MEILHAUS 0x1402 +#define PCI_VENDOR_ID_LAVA 0x1407 +#define PCI_VENDOR_ID_TIMEDIA 0x1409 +#define PCI_VENDOR_ID_ICE 0x1412 +#define PCI_VENDOR_ID_MICROSOFT 0x1414 +#define PCI_VENDOR_ID_OXSEMI 0x1415 +#define PCI_VENDOR_ID_CHELSIO 0x1425 +#define PCI_VENDOR_ID_ADLINK 0x144a +#define PCI_VENDOR_ID_SAMSUNG 0x144d +#define PCI_VENDOR_ID_GIGABYTE 0x1458 +#define PCI_VENDOR_ID_AMBIT 0x1468 +#define PCI_VENDOR_ID_MYRICOM 0x14c1 +#define PCI_VENDOR_ID_MEDIATEK 0x14c3 +#define PCI_VENDOR_ID_TITAN 0x14d2 +#define PCI_VENDOR_ID_PANACOM 0x14d4 +#define PCI_VENDOR_ID_SIPACKETS 0x14d9 +#define PCI_VENDOR_ID_AFAVLAB 0x14db +#define PCI_VENDOR_ID_AMPLICON 0x14dc +#define PCI_VENDOR_ID_BCM_GVC 0x14a4 +#define PCI_VENDOR_ID_TOPIC 0x151f +#define PCI_VENDOR_ID_MAINPINE 0x1522 +#define PCI_VENDOR_ID_SYBA 0x1592 +#define PCI_VENDOR_ID_MORETON 0x15aa +#define PCI_VENDOR_ID_VMWARE 0x15ad +#define PCI_VENDOR_ID_ZOLTRIX 0x15b0 +#define PCI_VENDOR_ID_MELLANOX 0x15b3 +#define PCI_VENDOR_ID_DFI 0x15bd +#define PCI_VENDOR_ID_QUICKNET 0x15e2 +#define PCI_VENDOR_ID_PDC 0x15e9 +#define PCI_VENDOR_ID_FARSITE 0x1619 +#define PCI_VENDOR_ID_ARIMA 0x161f +#define PCI_VENDOR_ID_BROCADE 0x1657 +#define PCI_VENDOR_ID_SIBYTE 0x166d +#define PCI_VENDOR_ID_ATHEROS 0x168c +#define PCI_VENDOR_ID_NETCELL 0x169c +#define PCI_VENDOR_ID_CENATEK 0x16ca +#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 +#define PCI_VENDOR_ID_USR 0x16ec +#define PCI_VENDOR_ID_VITESSE 0x1725 +#define PCI_VENDOR_ID_LINKSYS 0x1737 +#define PCI_VENDOR_ID_ALTIMA 0x173b +#define PCI_VENDOR_ID_CAVIUM 0x177d +#define PCI_VENDOR_ID_TECHWELL 0x1797 +#define PCI_VENDOR_ID_BELKIN 0x1799 +#define PCI_VENDOR_ID_RDC 0x17f3 +#define PCI_VENDOR_ID_GLI 0x17a0 +#define PCI_VENDOR_ID_LENOVO 0x17aa +#define PCI_VENDOR_ID_QCOM 0x17cb +#define PCI_VENDOR_ID_CDNS 0x17cd +#define PCI_VENDOR_ID_ARECA 0x17d3 +#define PCI_VENDOR_ID_S2IO 0x17d5 +#define PCI_VENDOR_ID_SITECOM 0x182d +#define PCI_VENDOR_ID_TOPSPIN 0x1867 +#define PCI_VENDOR_ID_COMMTECH 0x18f7 +#define PCI_VENDOR_ID_SILAN 0x1904 +#define PCI_VENDOR_ID_RENESAS 0x1912 +#define PCI_VENDOR_ID_SOLARFLARE 0x1924 +#define PCI_VENDOR_ID_TDI 0x192e +#define PCI_VENDOR_ID_FREESCALE 0x1957 +#define PCI_VENDOR_ID_NXP PCI_VENDOR_ID_FREESCALE +#define PCI_VENDOR_ID_PASEMI 0x1959 +#define PCI_VENDOR_ID_ATTANSIC 0x1969 +#define PCI_VENDOR_ID_JMICRON 0x197b +#define PCI_VENDOR_ID_KORENIX 0x1982 +#define PCI_VENDOR_ID_HUAWEI 0x19e5 +#define PCI_VENDOR_ID_NETRONOME 0x19ee +#define PCI_VENDOR_ID_QMI 0x1a32 +#define PCI_VENDOR_ID_AZWAVE 0x1a3b +#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_VENDOR_ID_ASMEDIA 0x1b21 +#define PCI_VENDOR_ID_REDHAT 0x1b36 +#define PCI_VENDOR_ID_SILICOM_DENMARK 0x1c2c +#define PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS 0x1c36 +#define PCI_VENDOR_ID_CIRCUITCO 0x1cc8 +#define PCI_VENDOR_ID_AMAZON 0x1d0f +#define PCI_VENDOR_ID_ZHAOXIN 0x1d17 +#define PCI_VENDOR_ID_HYGON 0x1d94 +#define PCI_VENDOR_ID_FUNGIBLE 0x1dad +#define PCI_VENDOR_ID_HXT 0x1dbf +#define PCI_VENDOR_ID_TEKRAM 0x1de1 +#define PCI_VENDOR_ID_TEHUTI 0x1fc9 +#define PCI_VENDOR_ID_SUNIX 0x1fd4 +#define PCI_VENDOR_ID_HINT 0x3388 +#define PCI_VENDOR_ID_3DLABS 0x3d3d +#define PCI_VENDOR_ID_NETXEN 0x4040 +#define PCI_VENDOR_ID_AKS 0x416c +#define PCI_VENDOR_ID_ACCESSIO 0x494f +#define PCI_VENDOR_ID_S3 0x5333 +#define PCI_VENDOR_ID_DUNORD 0x5544 +#define PCI_VENDOR_ID_DCI 0x6666 +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_VENDOR_ID_SCALEMP 0x8686 +#define PCI_VENDOR_ID_COMPUTONE 0x8e0e +#define PCI_VENDOR_ID_KTI 0x8e2e +#define PCI_VENDOR_ID_ADAPTEC 0x9004 +#define PCI_VENDOR_ID_ADAPTEC2 0x9005 +#define PCI_VENDOR_ID_HOLTEK 0x9412 +#define PCI_VENDOR_ID_NETMOS 0x9710 +#define PCI_VENDOR_ID_3COM_2 0xa727 +#define PCI_VENDOR_ID_DIGIUM 0xd161 +#define PCI_VENDOR_ID_TIGERJET 0xe159 +#define PCI_VENDOR_ID_XILINX_RME 0xea60 +#define PCI_VENDOR_ID_XEN 0x5853 +#define PCI_VENDOR_ID_OCZ 0x1b85 +#define PCI_VENDOR_ID_NCUBE 0x10ff + +#endif /* __PCI_IDS_H__ */ diff --git a/rt-thread/components/drivers/pci/pci_regs.h b/rt-thread/components/drivers/pci/pci_regs.h new file mode 100644 index 0000000..5a8822c --- /dev/null +++ b/rt-thread/components/drivers/pci/pci_regs.h @@ -0,0 +1,1090 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __PCI_REGS_H__ +#define __PCI_REGS_H__ + +#include + +/* + * PCI standard defines + * Copyright 1994, Drew Eckhardt + * Copyright 1997--1999 Martin Mares + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * For HyperTransport information, please consult the following manuals + * from http://www.hypertransport.org : + * + * The HyperTransport I/O Link Specification + * + * Mean of prefix: + * + * PCIM_xxx: mask to locate subfield in register + * PCIR_xxx: config register offset + * PCIC_xxx: device class + * PCIS_xxx: device subclass + * PCIP_xxx: device programming interface + * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices) + * PCID_xxx: device ID + * PCIY_xxx: capability identification number + * PCIZ_xxx: extended capability identification number + */ + +/* some PCI bus constants */ +#define PCI_DOMAINMAX 65535 /* highest supported domain number */ +#define PCI_BUSMAX 255 /* highest supported bus number */ +#define PCI_SLOTMAX 31 /* highest supported slot number */ +#define PCI_FUNCMAX 7 /* highest supported function number */ +#define PCI_REGMAX 255 /* highest supported config register addr */ +#define PCIE_REGMAX 4095 /* highest supported config register addr */ +#define PCI_MAXHDRTYPE 2 +#define PCI_STD_HEADER_SIZEOF 64 +#define PCI_STD_NUM_BARS 6 /* number of standard BARs */ + +/* PCI config header registers for all devices */ + +#define PCIR_DEVVENDOR 0x00 +#define PCIR_VENDOR 0x00 +#define PCIR_DEVICE 0x02 +#define PCIR_COMMAND 0x04 +#define PCIM_CMD_PORTEN 0x0001 +#define PCIM_CMD_MEMEN 0x0002 +#define PCIM_CMD_BUSMASTEREN 0x0004 +#define PCIM_CMD_SPECIALEN 0x0008 +#define PCIM_CMD_MWRICEN 0x0010 +#define PCIM_CMD_PERRESPEN 0x0040 +#define PCIM_CMD_SERRESPEN 0x0100 +#define PCIM_CMD_BACKTOBACK 0x0200 +#define PCIM_CMD_INTxDIS 0x0400 +#define PCIR_STATUS 0x06 +#define PCIM_STATUS_INTxSTATE 0x0008 +#define PCIM_STATUS_CAPPRESENT 0x0010 +#define PCIM_STATUS_66CAPABLE 0x0020 +#define PCIM_STATUS_BACKTOBACK 0x0080 +#define PCIM_STATUS_MDPERR 0x0100 +#define PCIM_STATUS_SEL_FAST 0x0000 +#define PCIM_STATUS_SEL_MEDIMUM 0x0200 +#define PCIM_STATUS_SEL_SLOW 0x0400 +#define PCIM_STATUS_SEL_MASK 0x0600 +#define PCIM_STATUS_STABORT 0x0800 +#define PCIM_STATUS_RTABORT 0x1000 +#define PCIM_STATUS_RMABORT 0x2000 +#define PCIM_STATUS_SERR 0x4000 +#define PCIM_STATUS_PERR 0x8000 +#define PCIR_REVID 0x08 +#define PCIR_PROGIF 0x09 +#define PCIR_SUBCLASS 0x0a +#define PCIR_CLASS 0x0b +#define PCIR_CACHELNSZ 0x0c +#define PCIR_LATTIMER 0x0d +#define PCIR_HDRTYPE 0x0e +#define PCIM_HDRTYPE 0x7f +#define PCIM_HDRTYPE_NORMAL 0x00 +#define PCIM_HDRTYPE_BRIDGE 0x01 +#define PCIM_HDRTYPE_CARDBUS 0x02 +#define PCIM_MFDEV 0x80 +#define PCIR_BIST 0x0f + +/* PCI Spec rev 2.2: 0FFFFh is an invalid value for Vendor ID. */ +#define PCIV_INVALID 0xffff + +/* Capability Register Offsets */ + +#define PCICAP_ID 0x0 +#define PCICAP_NEXTPTR 0x1 + +/* Capability Identification Numbers */ + +#define PCIY_PMG 0x01 /* PCI Power Management */ +#define PCIY_AGP 0x02 /* AGP */ +#define PCIY_VPD 0x03 /* Vital Product Data */ +#define PCIY_SLOTID 0x04 /* Slot Identification */ +#define PCIY_MSI 0x05 /* Message Signaled Interrupts */ +#define PCIY_CHSWP 0x06 /* CompactPCI Hot Swap */ +#define PCIY_PCIX 0x07 /* PCI-X */ +#define PCIY_HT 0x08 /* HyperTransport */ +#define PCIY_VENDOR 0x09 /* Vendor Unique */ +#define PCIY_DEBUG 0x0a /* Debug port */ +#define PCIY_CRES 0x0b /* CompactPCI central resource control */ +#define PCIY_HOTPLUG 0x0c /* PCI Hot-Plug */ +#define PCIY_SUBVENDOR 0x0d /* PCI-PCI bridge subvendor ID */ +#define PCIY_AGP8X 0x0e /* AGP 8x */ +#define PCIY_SECDEV 0x0f /* Secure Device */ +#define PCIY_EXPRESS 0x10 /* PCI Express */ +#define PCIY_MSIX 0x11 /* MSI-X */ +#define PCIY_SATA 0x12 /* SATA */ +#define PCIY_PCIAF 0x13 /* PCI Advanced Features */ +#define PCIY_EA 0x14 /* PCI Extended Allocation */ +#define PCIY_FPB 0x15 /* Flattening Portal Bridge */ +#define PCIY_MAX PCIY_FPB + +/* Extended Capability Register Fields */ + +#define PCIR_EXTCAP 0x100 +#define PCIM_EXTCAP_ID 0x0000ffff +#define PCIM_EXTCAP_VER 0x000f0000 +#define PCIM_EXTCAP_NEXTPTR 0xfff00000 +#define PCI_EXTCAP_ID(ecap) ((ecap) & PCIM_EXTCAP_ID) +#define PCI_EXTCAP_VER(ecap) (((ecap) & PCIM_EXTCAP_VER) >> 16) +#define PCI_EXTCAP_NEXTPTR(ecap) (((ecap) & PCIM_EXTCAP_NEXTPTR) >> 20) + +/* Extended Capability Identification Numbers */ + +#define PCIZ_AER 0x0001 /* Advanced Error Reporting */ +#define PCIZ_VC 0x0002 /* Virtual Channel if MFVC Ext Cap not set */ +#define PCIZ_SERNUM 0x0003 /* Device Serial Number */ +#define PCIZ_PWRBDGT 0x0004 /* Power Budgeting */ +#define PCIZ_RCLINK_DCL 0x0005 /* Root Complex Link Declaration */ +#define PCIZ_RCLINK_CTL 0x0006 /* Root Complex Internal Link Control */ +#define PCIZ_RCEC_ASSOC 0x0007 /* Root Complex Event Collector Association */ +#define PCIZ_MFVC 0x0008 /* Multi-Function Virtual Channel */ +#define PCIZ_VC2 0x0009 /* Virtual Channel if MFVC Ext Cap set */ +#define PCIZ_RCRB 0x000a /* RCRB Header */ +#define PCIZ_VENDOR 0x000b /* Vendor Unique */ +#define PCIZ_CAC 0x000c /* Configuration Access Correction -- obsolete */ +#define PCIZ_ACS 0x000d /* Access Control Services */ +#define PCIZ_ARI 0x000e /* Alternative Routing-ID Interpretation */ +#define PCIZ_ATS 0x000f /* Address Translation Services */ +#define PCIZ_SRIOV 0x0010 /* Single Root IO Virtualization */ +#define PCIZ_MRIOV 0x0011 /* Multiple Root IO Virtualization */ +#define PCIZ_MULTICAST 0x0012 /* Multicast */ +#define PCIZ_PAGE_REQ 0x0013 /* Page Request */ +#define PCIZ_AMD 0x0014 /* Reserved for AMD */ +#define PCIZ_RESIZE_BAR 0x0015 /* Resizable BAR */ +#define PCIZ_DPA 0x0016 /* Dynamic Power Allocation */ +#define PCIZ_TPH_REQ 0x0017 /* TPH Requester */ +#define PCIZ_LTR 0x0018 /* Latency Tolerance Reporting */ +#define PCIZ_SEC_PCIE 0x0019 /* Secondary PCI Express */ +#define PCIZ_PMUX 0x001a /* Protocol Multiplexing */ +#define PCIZ_PASID 0x001b /* Process Address Space ID */ +#define PCIZ_LN_REQ 0x001c /* LN Requester */ +#define PCIZ_DPC 0x001d /* Downstream Port Containment */ +#define PCIZ_L1PM 0x001e /* L1 PM Substates */ +#define PCIZ_PTM 0x001f /* Precision Time Measurement */ +#define PCIZ_M_PCIE 0x0020 /* PCIe over M-PHY */ +#define PCIZ_FRS 0x0021 /* FRS Queuing */ +#define PCIZ_RTR 0x0022 /* Readiness Time Reporting */ +#define PCIZ_DVSEC 0x0023 /* Designated Vendor-Specific */ +#define PCIZ_VF_REBAR 0x0024 /* VF Resizable BAR */ +#define PCIZ_DLNK 0x0025 /* Data Link Feature */ +#define PCIZ_16GT 0x0026 /* Physical Layer 16.0 GT/s */ +#define PCIZ_LMR 0x0027 /* Lane Margining at Receiver */ +#define PCIZ_HIER_ID 0x0028 /* Hierarchy ID */ +#define PCIZ_NPEM 0x0029 /* Native PCIe Enclosure Management */ +#define PCIZ_PL32 0x002a /* Physical Layer 32.0 GT/s */ +#define PCIZ_AP 0x002b /* Alternate Protocol */ +#define PCIZ_SFI 0x002c /* System Firmware Intermediary */ + +/* Resizable BARs */ +#define PCIM_REBAR_CAP 4 /* Capability register */ +#define PCIM_REBAR_CAP_SIZES 0x00fffff0 /* Supported BAR sizes */ +#define PCIM_REBAR_CTRL 8 /* Control register */ +#define PCIM_REBAR_CTRL_BAR_IDX 0x00000007 /* BAR index */ +#define PCIM_REBAR_CTRL_NBAR_MASK 0x000000e0 +#define PCIM_REBAR_CTRL_NBAR_SHIFT 5 /* Shift for # of BARs */ +#define PCIM_REBAR_CTRL_BAR_SIZE 0x00001f00 /* BAR size */ +#define PCIM_REBAR_CTRL_BAR_SHIFT 8 /* Shift for BAR size */ + +/* config registers for header type 0 devices */ + +#define PCIR_BARS 0x10 +#define PCIR_BAR(x) (PCIR_BARS + (x) * 4) +#define PCI_RID2BAR(rid) (((rid) - PCIR_BARS) / 4) +#define PCI_BAR_IO(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) +#define PCI_BAR_MEM(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE) +#define PCIM_BAR_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCIM_BAR_SPACE_IO 0x01 +#define PCIM_BAR_SPACE_MEMORY 0x00 +#define PCIM_BAR_MEM_TYPE_MASK 0x06 +#define PCIM_BAR_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCIM_BAR_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCIM_BAR_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCIM_BAR_MEM_PREFETCH 0x08 /* prefetchable? */ +#define PCIM_BAR_MEM_MASK (~0x0fUL) +#define PCIM_BAR_IO_MASK (~0x03UL) +#define PCIR_CIS 0x28 +#define PCIM_CIS_ASI_MASK 0x00000007 +#define PCIM_CIS_ASI_CONFIG 0 +#define PCIM_CIS_ASI_BAR0 1 +#define PCIM_CIS_ASI_BAR1 2 +#define PCIM_CIS_ASI_BAR2 3 +#define PCIM_CIS_ASI_BAR3 4 +#define PCIM_CIS_ASI_BAR4 5 +#define PCIM_CIS_ASI_BAR5 6 +#define PCIM_CIS_ASI_ROM 7 +#define PCIM_CIS_ADDR_MASK 0x0ffffff8 +#define PCIM_CIS_ROM_MASK 0xf0000000 +#define PCIM_CIS_CONFIG_MASK 0xff +#define PCIR_SUBVEND_0 0x2c +#define PCIR_SUBDEV_0 0x2e +#define PCIR_BIOS 0x30 +#define PCIM_BIOS_ENABLE 0x01 +#define PCIM_BIOS_ADDR_MASK 0xfffff800 +#define PCIR_CAP_PTR 0x34 +#define PCIR_INTLINE 0x3c +#define PCIR_INTPIN 0x3d +#define PCIR_MINGNT 0x3e +#define PCIR_MAXLAT 0x3f + +/* config registers for header type 1 (PCI-to-PCI bridge) devices */ + +#define PCIR_MAX_BAR_1 1 +#define PCIR_SECSTAT_1 0x1e + +#define PCIR_PRIBUS_1 0x18 +#define PCIR_SECBUS_1 0x19 +#define PCIR_SUBBUS_1 0x1a +#define PCIR_SECLAT_1 0x1b + +#define PCIR_IOBASEL_1 0x1c +#define PCIR_IOLIMITL_1 0x1d +#define PCIR_IOBASEH_1 0x30 +#define PCIR_IOLIMITH_1 0x32 +#define PCIM_BRIO_16 0x0 +#define PCIM_BRIO_32 0x1 +#define PCIM_BRIO_MASK 0xf + +#define PCIR_MEMBASE_1 0x20 +#define PCIR_MEMLIMIT_1 0x22 + +#define PCIR_PMBASEL_1 0x24 +#define PCIR_PMLIMITL_1 0x26 +#define PCIR_PMBASEH_1 0x28 +#define PCIR_PMLIMITH_1 0x2c +#define PCIM_BRPM_32 0x0 +#define PCIM_BRPM_64 0x1 +#define PCIM_BRPM_MASK 0xf + +#define PCIR_BIOS_1 0x38 +#define PCIR_BRIDGECTL_1 0x3e + +#define PCI_PPBMEMBASE(h, l) ((((rt_uint64_t)(h) << 32) + ((l) << 16)) & ~0xfffff) +#define PCI_PPBMEMLIMIT(h, l) ((((rt_uint64_t)(h) << 32) + ((l) << 16)) | 0xfffff) +#define PCI_PPBIOBASE(h, l) ((((h) << 16) + ((l) << 8)) & ~0xfff) +#define PCI_PPBIOLIMIT(h, l) ((((h) << 16) + ((l) << 8)) | 0xfff) + +/* config registers for header t ype 2 (CardBus) devices */ + +#define PCIR_MAX_BAR_2 0 +#define PCIR_CAP_PTR_2 0x14 +#define PCIR_SECSTAT_2 0x16 + +#define PCIR_PRIBUS_2 0x18 +#define PCIR_SECBUS_2 0x19 +#define PCIR_SUBBUS_2 0x1a +#define PCIR_SECLAT_2 0x1b + +#define PCIR_MEMBASE0_2 0x1c +#define PCIR_MEMLIMIT0_2 0x20 +#define PCIR_MEMBASE1_2 0x24 +#define PCIR_MEMLIMIT1_2 0x28 +#define PCIR_IOBASE0_2 0x2c +#define PCIR_IOLIMIT0_2 0x30 +#define PCIR_IOBASE1_2 0x34 +#define PCIR_IOLIMIT1_2 0x38 +#define PCIM_CBBIO_16 0x0 +#define PCIM_CBBIO_32 0x1 +#define PCIM_CBBIO_MASK 0x3 + +#define PCIR_BRIDGECTL_2 0x3e + +#define PCIR_SUBVEND_2 0x40 +#define PCIR_SUBDEV_2 0x42 + +#define PCIR_PCCARDIF_2 0x44 + +#define PCI_CBBMEMBASE(l) ((l) & ~0xfffff) +#define PCI_CBBMEMLIMIT(l) ((l) | 0xfffff) +#define PCI_CBBIOBASE(l) ((l) & ~0x3) +#define PCI_CBBIOLIMIT(l) ((l) | 0x3) + +/* PCI device class, subclass and programming interface definitions */ +#define PCIC_NOT_DEFINED 0x0000 +#define PCIS_NOT_DEFINED_VGA 0x0001 + +#define PCIC_STORAGE 0x01 +#define PCIS_STORAGE_SCSI 0x0100 +#define PCIS_STORAGE_IDE 0x0101 +#define PCIS_STORAGE_FLOPPY 0x0102 +#define PCIS_STORAGE_IPI 0x0103 +#define PCIS_STORAGE_RAID 0x0104 +#define PCIS_STORAGE_SATA 0x0106 +#define PCIS_STORAGE_SATA_AHCI 0x010601 +#define PCIS_STORAGE_SAS 0x0107 +#define PCIS_STORAGE_EXPRESS 0x010802 +#define PCIS_STORAGE_OTHER 0x0180 + +#define PCIC_NETWORK 0x02 +#define PCIS_NETWORK_ETHERNET 0x0200 +#define PCIS_NETWORK_TOKEN_RING 0x0201 +#define PCIS_NETWORK_FDDI 0x0202 +#define PCIS_NETWORK_ATM 0x0203 +#define PCIS_NETWORK_OTHER 0x0280 + +#define PCIC_DISPLAY 0x03 +#define PCIS_DISPLAY_VGA 0x0300 +#define PCIS_DISPLAY_XGA 0x0301 +#define PCIS_DISPLAY_3D 0x0302 +#define PCIS_DISPLAY_OTHER 0x0380 + +#define PCIC_MULTIMEDIA 0x04 +#define PCIS_MULTIMEDIA_VIDEO 0x0400 +#define PCIS_MULTIMEDIA_AUDIO 0x0401 +#define PCIS_MULTIMEDIA_PHONE 0x0402 +#define PCIS_MULTIMEDIA_HD_AUDIO 0x0403 +#define PCIS_MULTIMEDIA_OTHER 0x0480 + +#define PCIC_MEMORY 0x05 +#define PCIS_MEMORY_RAM 0x0500 +#define PCIS_MEMORY_FLASH 0x0501 +#define PCIS_MEMORY_CXL 0x0502 +#define PCIS_MEMORY_OTHER 0x0580 + +#define PCIC_BRIDGE 0x06 +#define PCIS_BRIDGE_HOST 0x0600 +#define PCIS_BRIDGE_ISA 0x0601 +#define PCIS_BRIDGE_EISA 0x0602 +#define PCIS_BRIDGE_MC 0x0603 +#define PCIS_BRIDGE_PCI 0x0604 +#define PCIS_BRIDGE_PCI_NORMAL 0x060400 +#define PCIS_BRIDGE_PCI_SUBTRACTIVE 0x060401 +#define PCIS_BRIDGE_PCMCIA 0x0605 +#define PCIS_BRIDGE_NUBUS 0x0606 +#define PCIS_BRIDGE_CARDBUS 0x0607 +#define PCIS_BRIDGE_RACEWAY 0x0608 +#define PCIS_BRIDGE_OTHER 0x0680 + +#define PCIC_COMMUNICATION 0x07 +#define PCIS_COMMUNICATION_SERIAL 0x0700 +#define PCIS_COMMUNICATION_PARALLEL 0x0701 +#define PCIS_COMMUNICATION_MULTISERIAL 0x0702 +#define PCIS_COMMUNICATION_MODEM 0x0703 +#define PCIS_COMMUNICATION_OTHER 0x0780 + +#define PCIC_SYSTEM 0x08 +#define PCIS_SYSTEM_PIC 0x0800 +#define PCIS_SYSTEM_PIC_IOAPIC 0x080010 +#define PCIS_SYSTEM_PIC_IOXAPIC 0x080020 +#define PCIS_SYSTEM_DMA 0x0801 +#define PCIS_SYSTEM_TIMER 0x0802 +#define PCIS_SYSTEM_RTC 0x0803 +#define PCIS_SYSTEM_PCI_HOTPLUG 0x0804 +#define PCIS_SYSTEM_SDHCI 0x0805 +#define PCIS_SYSTEM_RCEC 0x0807 +#define PCIS_SYSTEM_OTHER 0x0880 + +#define PCIC_INPUT 0x09 +#define PCIS_INPUT_KEYBOARD 0x0900 +#define PCIS_INPUT_PEN 0x0901 +#define PCIS_INPUT_MOUSE 0x0902 +#define PCIS_INPUT_SCANNER 0x0903 +#define PCIS_INPUT_GAMEPORT 0x0904 +#define PCIS_INPUT_OTHER 0x0980 + +#define PCIC_DOCKING 0x0a +#define PCIS_DOCKING_GENERIC 0x0a00 +#define PCIS_DOCKING_OTHER 0x0a80 + +#define PCIC_PROCESSOR 0x0b +#define PCIS_PROCESSOR_386 0x0b00 +#define PCIS_PROCESSOR_486 0x0b01 +#define PCIS_PROCESSOR_PENTIUM 0x0b02 +#define PCIS_PROCESSOR_ALPHA 0x0b10 +#define PCIS_PROCESSOR_POWERPC 0x0b20 +#define PCIS_PROCESSOR_MIPS 0x0b30 +#define PCIS_PROCESSOR_CO 0x0b40 + +#define PCIC_SERIAL 0x0c +#define PCIS_SERIAL_FIREWIRE 0x0c00 +#define PCIS_SERIAL_FIREWIRE_OHCI 0x0c0010 +#define PCIS_SERIAL_ACCESS 0x0c01 +#define PCIS_SERIAL_SSA 0x0c02 +#define PCIS_SERIAL_USB 0x0c03 +#define PCIS_SERIAL_USB_UHCI 0x0c0300 +#define PCIS_SERIAL_USB_OHCI 0x0c0310 +#define PCIS_SERIAL_USB_EHCI 0x0c0320 +#define PCIS_SERIAL_USB_XHCI 0x0c0330 +#define PCIS_SERIAL_USB_DEVICE 0x0c03fe +#define PCIS_SERIAL_FIBER 0x0c04 +#define PCIS_SERIAL_SMBUS 0x0c05 +#define PCIS_SERIAL_IPMI 0x0c07 +#define PCIS_SERIAL_IPMI_SMIC 0x0c0700 +#define PCIS_SERIAL_IPMI_KCS 0x0c0701 +#define PCIS_SERIAL_IPMI_BT 0x0c0702 + +#define PCIC_WIRELESS 0x0d +#define PCIS_WIRELESS_RF_CONTROLLER 0x0d10 +#define PCIS_WIRELESS_WHCI 0x0d1010 + +#define PCIC_INTELLIGENT 0x0e +#define PCIS_INTELLIGENT_I2O 0x0e00 + +#define PCIC_SATELLITE 0x0f +#define PCIS_SATELLITE_TV 0x0f00 +#define PCIS_SATELLITE_AUDIO 0x0f01 +#define PCIS_SATELLITE_VOICE 0x0f03 +#define PCIS_SATELLITE_DATA 0x0f04 + +#define PCIC_CRYPT 0x10 +#define PCIS_CRYPT_NETWORK 0x1000 +#define PCIS_CRYPT_ENTERTAINMENT 0x1001 +#define PCIS_CRYPT_OTHER 0x1080 + +#define PCIC_SIGNAL_PROCESSING 0x11 +#define PCIS_SP_DPIO 0x1100 +#define PCIS_SP_OTHER 0x1180 + +#define PCIS_OTHERS 0xff + +/* Bridge Control Values. */ +#define PCIB_BCR_PERR_ENABLE 0x0001 +#define PCIB_BCR_SERR_ENABLE 0x0002 +#define PCIB_BCR_ISA_ENABLE 0x0004 +#define PCIB_BCR_VGA_ENABLE 0x0008 +#define PCIB_BCR_MASTER_ABORT_MODE 0x0020 +#define PCIB_BCR_SECBUS_RESET 0x0040 +#define PCIB_BCR_SECBUS_BACKTOBACK 0x0080 +#define PCIB_BCR_PRI_DISCARD_TIMEOUT 0x0100 +#define PCIB_BCR_SEC_DISCARD_TIMEOUT 0x0200 +#define PCIB_BCR_DISCARD_TIMER_STATUS 0x0400 +#define PCIB_BCR_DISCARD_TIMER_SERREN 0x0800 + +#define CBB_BCR_PERR_ENABLE 0x0001 +#define CBB_BCR_SERR_ENABLE 0x0002 +#define CBB_BCR_ISA_ENABLE 0x0004 +#define CBB_BCR_VGA_ENABLE 0x0008 +#define CBB_BCR_MASTER_ABORT_MODE 0x0020 +#define CBB_BCR_CARDBUS_RESET 0x0040 +#define CBB_BCR_IREQ_INT_ENABLE 0x0080 +#define CBB_BCR_PREFETCH_0_ENABLE 0x0100 +#define CBB_BCR_PREFETCH_1_ENABLE 0x0200 +#define CBB_BCR_WRITE_POSTING_ENABLE 0x0400 + +/* PCI power manangement */ +#define PCIR_POWER_CAP 0x2 +#define PCIM_PCAP_SPEC 0x0007 +#define PCIM_PCAP_PMEREQCLK 0x0008 +#define PCIM_PCAP_DEVSPECINIT 0x0020 +#define PCIM_PCAP_AUXPWR_0 0x0000 +#define PCIM_PCAP_AUXPWR_55 0x0040 +#define PCIM_PCAP_AUXPWR_100 0x0080 +#define PCIM_PCAP_AUXPWR_160 0x00c0 +#define PCIM_PCAP_AUXPWR_220 0x0100 +#define PCIM_PCAP_AUXPWR_270 0x0140 +#define PCIM_PCAP_AUXPWR_320 0x0180 +#define PCIM_PCAP_AUXPWR_375 0x01c0 +#define PCIM_PCAP_AUXPWRMASK 0x01c0 +#define PCIM_PCAP_D1SUPP 0x0200 +#define PCIM_PCAP_D2SUPP 0x0400 +#define PCIM_PCAP_PMEMASK 0xf800 +#define PCIM_PCAP_D0PME 0x0800 +#define PCIM_PCAP_D1PME 0x1000 +#define PCIM_PCAP_D2PME 0x2000 +#define PCIM_PCAP_D3PME_HOT 0x4000 +#define PCIM_PCAP_D3PME_COLD 0x8000 + +#define PCIR_POWER_STATUS 0x4 +#define PCIM_PSTAT_D0 0x0000 +#define PCIM_PSTAT_D1 0x0001 +#define PCIM_PSTAT_D2 0x0002 +#define PCIM_PSTAT_D3 0x0003 +#define PCIM_PSTAT_DMASK 0x0003 +#define PCIM_PSTAT_NOSOFTRESET 0x0008 +#define PCIM_PSTAT_PMEENABLE 0x0100 +#define PCIM_PSTAT_D0POWER 0x0000 +#define PCIM_PSTAT_D1POWER 0x0200 +#define PCIM_PSTAT_D2POWER 0x0400 +#define PCIM_PSTAT_D3POWER 0x0600 +#define PCIM_PSTAT_D0HEAT 0x0800 +#define PCIM_PSTAT_D1HEAT 0x0a00 +#define PCIM_PSTAT_D2HEAT 0x0c00 +#define PCIM_PSTAT_D3HEAT 0x0e00 +#define PCIM_PSTAT_DATASELMASK 0x1e00 +#define PCIM_PSTAT_DATAUNKN 0x0000 +#define PCIM_PSTAT_DATADIV10 0x2000 +#define PCIM_PSTAT_DATADIV100 0x4000 +#define PCIM_PSTAT_DATADIV1000 0x6000 +#define PCIM_PSTAT_DATADIVMASK 0x6000 +#define PCIM_PSTAT_PME 0x8000 + +#define PCIR_POWER_BSE 0x6 +#define PCIM_PMCSR_BSE_D3B3 0x00 +#define PCIM_PMCSR_BSE_D3B2 0x40 +#define PCIM_PMCSR_BSE_BPCCE 0x80 + +#define PCIR_POWER_DATA 0x7 + +/* VPD capability registers */ +#define PCIR_VPD_ADDR 0x2 +#define PCIR_VPD_DATA 0x4 + +/* PCI Message Signalled Interrupts (MSI) */ +#define PCIR_MSI_CTRL 0x2 +#define PCIM_MSICTRL_VECTOR 0x0100 +#define PCIM_MSICTRL_64BIT 0x0080 +#define PCIM_MSICTRL_MME_MASK 0x0070 +#define PCIM_MSICTRL_MME_SHIFT 0x4 +#define PCIM_MSICTRL_MME_1 0x0000 +#define PCIM_MSICTRL_MME_2 0x0010 +#define PCIM_MSICTRL_MME_4 0x0020 +#define PCIM_MSICTRL_MME_8 0x0030 +#define PCIM_MSICTRL_MME_16 0x0040 +#define PCIM_MSICTRL_MME_32 0x0050 +#define PCIM_MSICTRL_MMC_MASK 0x000e +#define PCIM_MSICTRL_MMC_SHIFT 0x1 +#define PCIM_MSICTRL_MMC_1 0x0000 +#define PCIM_MSICTRL_MMC_2 0x0002 +#define PCIM_MSICTRL_MMC_4 0x0004 +#define PCIM_MSICTRL_MMC_8 0x0006 +#define PCIM_MSICTRL_MMC_16 0x0008 +#define PCIM_MSICTRL_MMC_32 0x000a +#define PCIM_MSICTRL_MSI_ENABLE 0x0001 +#define PCIR_MSI_ADDR 0x4 +#define PCIR_MSI_ADDR_HIGH 0x8 +#define PCIR_MSI_DATA 0x8 +#define PCIR_MSI_DATA_64BIT 0xc +#define PCIR_MSI_MASK 0xc +#define PCIR_MSI_MASK_64BIT 0x10 +#define PCIR_MSI_PENDING 0x14 + +/* PCI Enhanced Allocation registers */ +#define PCIR_EA_NUM_ENT 2 /* Number of Capability Entries */ +#define PCIM_EA_NUM_ENT_MASK 0x3f /* Num Entries Mask */ +#define PCIR_EA_FIRST_ENT 4 /* First EA Entry in List */ +#define PCIR_EA_FIRST_ENT_BRIDGE 8 /* First EA Entry for Bridges */ +#define PCIM_EA_ES 0x00000007 /* Entry Size */ +#define PCIM_EA_BEI 0x000000f0 /* BAR Equivalent Indicator */ +#define PCIM_EA_BEI_OFFSET 4 +/* 0-5 map to BARs 0-5 respectively */ +#define PCIM_EA_BEI_BAR_0 0 +#define PCIM_EA_BEI_BAR_5 5 +#define PCIM_EA_BEI_BAR(x) (((x) >> PCIM_EA_BEI_OFFSET) & 0xf) +#define PCIM_EA_BEI_BRIDGE 0x6 /* Resource behind bridge */ +#define PCIM_EA_BEI_ENI 0x7 /* Equivalent Not Indicated */ +#define PCIM_EA_BEI_ROM 0x8 /* Expansion ROM */ +/* 9-14 map to VF BARs 0-5 respectively */ +#define PCIM_EA_BEI_VF_BAR_0 9 +#define PCIM_EA_BEI_VF_BAR_5 14 +#define PCIM_EA_BEI_RESERVED 0xf /* Reserved - Treat like ENI */ +#define PCIM_EA_PP 0x0000ff00 /* Primary Properties */ +#define PCIM_EA_PP_OFFSET 8 +#define PCIM_EA_SP_OFFSET 16 +#define PCIM_EA_SP 0x00ff0000 /* Secondary Properties */ +#define PCIM_EA_P_MEM 0x00 /* Non-Prefetch Memory */ +#define PCIM_EA_P_MEM_PREFETCH 0x01 /* Prefetchable Memory */ +#define PCIM_EA_P_IO 0x02 /* I/O Space */ +#define PCIM_EA_P_VF_MEM_PREFETCH 0x03 /* VF Prefetchable Memory */ +#define PCIM_EA_P_VF_MEM 0x04 /* VF Non-Prefetch Memory */ +#define PCIM_EA_P_BRIDGE_MEM 0x05 /* Bridge Non-Prefetch Memory */ +#define PCIM_EA_P_BRIDGE_MEM_PREFETCH 0x06 /* Bridge Prefetchable Memory */ +#define PCIM_EA_P_BRIDGE_IO 0x07 /* Bridge I/O Space */ +/* 0x08-0xfc reserved */ +#define PCIM_EA_P_MEM_RESERVED 0xfd /* Reserved Memory */ +#define PCIM_EA_P_IO_RESERVED 0xfe /* Reserved I/O Space */ +#define PCIM_EA_P_UNAVAILABLE 0xff /* Entry Unavailable */ +#define PCIM_EA_WRITABLE 0x40000000 /* Writable: 1 = RW, 0 = HwInit */ +#define PCIM_EA_ENABLE 0x80000000 /* Enable for this entry */ +#define PCIM_EA_BASE 4 /* Base Address Offset */ +#define PCIM_EA_MAX_OFFSET 8 /* MaxOffset (resource length) */ +/* bit 0 is reserved */ +#define PCIM_EA_IS_64 0x00000002 /* 64-bit field flag */ +#define PCIM_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */ +/* Bridge config register */ +#define PCIM_EA_SEC_NR(reg) ((reg) & 0xff) +#define PCIM_EA_SUB_NR(reg) (((reg) >> 8) & 0xff) + +/* PCI-X definitions */ + +/* For header type 0 devices */ +#define PCIXR_COMMAND 0x2 +#define PCIXM_COMMAND_DPERR_E 0x0001 /* Data Parity Error Recovery */ +#define PCIXM_COMMAND_ERO 0x0002 /* Enable Relaxed Ordering */ +#define PCIXM_COMMAND_MAX_READ 0x000c /* Maximum Burst Read Count */ +#define PCIXM_COMMAND_MAX_READ_512 0x0000 +#define PCIXM_COMMAND_MAX_READ_1024 0x0004 +#define PCIXM_COMMAND_MAX_READ_2048 0x0008 +#define PCIXM_COMMAND_MAX_READ_4096 0x000c +#define PCIXM_COMMAND_MAX_SPLITS 0x0070 /* Maximum Split Transactions */ +#define PCIXM_COMMAND_MAX_SPLITS_1 0x0000 +#define PCIXM_COMMAND_MAX_SPLITS_2 0x0010 +#define PCIXM_COMMAND_MAX_SPLITS_3 0x0020 +#define PCIXM_COMMAND_MAX_SPLITS_4 0x0030 +#define PCIXM_COMMAND_MAX_SPLITS_8 0x0040 +#define PCIXM_COMMAND_MAX_SPLITS_12 0x0050 +#define PCIXM_COMMAND_MAX_SPLITS_16 0x0060 +#define PCIXM_COMMAND_MAX_SPLITS_32 0x0070 +#define PCIXM_COMMAND_VERSION 0x3000 +#define PCIXR_STATUS 0x4 +#define PCIXM_STATUS_DEVFN 0x000000ff +#define PCIXM_STATUS_BUS 0x0000ff00 +#define PCIXM_STATUS_64BIT 0x00010000 +#define PCIXM_STATUS_133CAP 0x00020000 +#define PCIXM_STATUS_SC_DISCARDED 0x00040000 +#define PCIXM_STATUS_UNEXP_SC 0x00080000 +#define PCIXM_STATUS_COMPLEX_DEV 0x00100000 +#define PCIXM_STATUS_MAX_READ 0x00600000 +#define PCIXM_STATUS_MAX_READ_512 0x00000000 +#define PCIXM_STATUS_MAX_READ_1024 0x00200000 +#define PCIXM_STATUS_MAX_READ_2048 0x00400000 +#define PCIXM_STATUS_MAX_READ_4096 0x00600000 +#define PCIXM_STATUS_MAX_SPLITS 0x03800000 +#define PCIXM_STATUS_MAX_SPLITS_1 0x00000000 +#define PCIXM_STATUS_MAX_SPLITS_2 0x00800000 +#define PCIXM_STATUS_MAX_SPLITS_3 0x01000000 +#define PCIXM_STATUS_MAX_SPLITS_4 0x01800000 +#define PCIXM_STATUS_MAX_SPLITS_8 0x02000000 +#define PCIXM_STATUS_MAX_SPLITS_12 0x02800000 +#define PCIXM_STATUS_MAX_SPLITS_16 0x03000000 +#define PCIXM_STATUS_MAX_SPLITS_32 0x03800000 +#define PCIXM_STATUS_MAX_CUM_READ 0x1c000000 +#define PCIXM_STATUS_RCVD_SC_ERR 0x20000000 +#define PCIXM_STATUS_266CAP 0x40000000 +#define PCIXM_STATUS_533CAP 0x80000000 + +/* For header type 1 devices (PCI-X bridges) */ +#define PCIXR_SEC_STATUS 0x2 +#define PCIXM_SEC_STATUS_64BIT 0x0001 +#define PCIXM_SEC_STATUS_133CAP 0x0002 +#define PCIXM_SEC_STATUS_SC_DISC 0x0004 +#define PCIXM_SEC_STATUS_UNEXP_SC 0x0008 +#define PCIXM_SEC_STATUS_SC_OVERRUN 0x0010 +#define PCIXM_SEC_STATUS_SR_DELAYED 0x0020 +#define PCIXM_SEC_STATUS_BUS_MODE 0x03c0 +#define PCIXM_SEC_STATUS_VERSION 0x3000 +#define PCIXM_SEC_STATUS_266CAP 0x4000 +#define PCIXM_SEC_STATUS_533CAP 0x8000 +#define PCIXR_BRIDGE_STATUS 0x4 +#define PCIXM_BRIDGE_STATUS_DEVFN 0x000000ff +#define PCIXM_BRIDGE_STATUS_BUS 0x0000ff00 +#define PCIXM_BRIDGE_STATUS_64BIT 0x00010000 +#define PCIXM_BRIDGE_STATUS_133CAP 0x00020000 +#define PCIXM_BRIDGE_STATUS_SC_DISCARDED 0x00040000 +#define PCIXM_BRIDGE_STATUS_UNEXP_SC 0x00080000 +#define PCIXM_BRIDGE_STATUS_SC_OVERRUN 0x00100000 +#define PCIXM_BRIDGE_STATUS_SR_DELAYED 0x00200000 +#define PCIXM_BRIDGE_STATUS_DEVID_MSGCAP 0x20000000 +#define PCIXM_BRIDGE_STATUS_266CAP 0x40000000 +#define PCIXM_BRIDGE_STATUS_533CAP 0x80000000 + +/* HT (HyperTransport) Capability definitions */ +#define PCIR_HT_COMMAND 0x2 +#define PCIM_HTCMD_CAP_MASK 0xf800 /* Capability type. */ +#define PCIM_HTCAP_SLAVE 0x0000 /* 000xx */ +#define PCIM_HTCAP_HOST 0x2000 /* 001xx */ +#define PCIM_HTCAP_SWITCH 0x4000 /* 01000 */ +#define PCIM_HTCAP_INTERRUPT 0x8000 /* 10000 */ +#define PCIM_HTCAP_REVISION_ID 0x8800 /* 10001 */ +#define PCIM_HTCAP_UNITID_CLUMPING 0x9000 /* 10010 */ +#define PCIM_HTCAP_EXT_CONFIG_SPACE 0x9800 /* 10011 */ +#define PCIM_HTCAP_ADDRESS_MAPPING 0xa000 /* 10100 */ +#define PCIM_HTCAP_MSI_MAPPING 0xa800 /* 10101 */ +#define PCIM_HTCAP_DIRECT_ROUTE 0xb000 /* 10110 */ +#define PCIM_HTCAP_VCSET 0xb800 /* 10111 */ +#define PCIM_HTCAP_RETRY_MODE 0xc000 /* 11000 */ +#define PCIM_HTCAP_X86_ENCODING 0xc800 /* 11001 */ +#define PCIM_HTCAP_GEN3 0xd000 /* 11010 */ +#define PCIM_HTCAP_FLE 0xd800 /* 11011 */ +#define PCIM_HTCAP_PM 0xe000 /* 11100 */ +#define PCIM_HTCAP_HIGH_NODE_COUNT 0xe800 /* 11101 */ + +/* HT MSI Mapping Capability definitions. */ +#define PCIM_HTCMD_MSI_ENABLE 0x0001 +#define PCIM_HTCMD_MSI_FIXED 0x0002 +#define PCIR_HTMSI_ADDRESS_LO 0x4 +#define PCIR_HTMSI_ADDRESS_HI 0x8 + +/* PCI Vendor capability definitions */ +#define PCIR_VENDOR_LENGTH 0x2 +#define PCIR_VENDOR_DATA 0x3 + +/* PCI Device capability definitions */ +#define PCIR_DEVICE_LENGTH 0x2 + +/* PCI EHCI Debug Port definitions */ +#define PCIR_DEBUG_PORT 0x2 +#define PCIM_DEBUG_PORT_OFFSET 0x1fff +#define PCIM_DEBUG_PORT_BAR 0xe000 + +/* PCI-PCI Bridge Subvendor definitions */ +#define PCIR_SUBVENDCAP_ID 0x4 +#define PCIR_SUBVENDCAP 0x4 +#define PCIR_SUBDEVCAP 0x6 + +/* PCI Express definitions */ +#define PCIER_FLAGS 0x2 +#define PCIEM_FLAGS_VERSION 0x000f +#define PCIEM_FLAGS_TYPE 0x00f0 +#define PCIEM_TYPE_ENDPOINT 0x0000 +#define PCIEM_TYPE_LEGACY_ENDPOINT 0x0010 +#define PCIEM_TYPE_ROOT_PORT 0x0040 +#define PCIEM_TYPE_UPSTREAM_PORT 0x0050 +#define PCIEM_TYPE_DOWNSTREAM_PORT 0x0060 +#define PCIEM_TYPE_PCI_BRIDGE 0x0070 +#define PCIEM_TYPE_PCIE_BRIDGE 0x0080 +#define PCIEM_TYPE_ROOT_INT_EP 0x0090 +#define PCIEM_TYPE_ROOT_EC 0x00a0 +#define PCIEM_FLAGS_SLOT 0x0100 +#define PCIEM_FLAGS_IRQ 0x3e00 +#define PCIER_DEVICE_CAP 0x4 +#define PCIEM_CAP_MAX_PAYLOAD 0x00000007 +#define PCIEM_CAP_PHANTHOM_FUNCS 0x00000018 +#define PCIEM_CAP_EXT_TAG_FIELD 0x00000020 +#define PCIEM_CAP_L0S_LATENCY 0x000001c0 +#define PCIEM_CAP_L1_LATENCY 0x00000e00 +#define PCIEM_CAP_ROLE_ERR_RPT 0x00008000 +#define PCIEM_CAP_SLOT_PWR_LIM_VAL 0x03fc0000 +#define PCIEM_CAP_SLOT_PWR_LIM_SCALE 0x0c000000 +#define PCIEM_CAP_FLR 0x10000000 +#define PCIER_DEVICE_CTL 0x8 +#define PCIEM_CTL_COR_ENABLE 0x0001 +#define PCIEM_CTL_NFER_ENABLE 0x0002 +#define PCIEM_CTL_FER_ENABLE 0x0004 +#define PCIEM_CTL_URR_ENABLE 0x0008 +#define PCIEM_CTL_RELAXED_ORD_ENABLE 0x0010 +#define PCIEM_CTL_MAX_PAYLOAD 0x00e0 +#define PCIEM_CTL_EXT_TAG_FIELD 0x0100 +#define PCIEM_CTL_PHANTHOM_FUNCS 0x0200 +#define PCIEM_CTL_AUX_POWER_PM 0x0400 +#define PCIEM_CTL_NOSNOOP_ENABLE 0x0800 +#define PCIEM_CTL_MAX_READ_REQUEST 0x7000 +#define PCIEM_CTL_BRDG_CFG_RETRY 0x8000 /* PCI-E - PCI/PCI-X bridges */ +#define PCIEM_CTL_INITIATE_FLR 0x8000 /* FLR capable endpoints */ +#define PCIER_DEVICE_STA 0xa +#define PCIEM_STA_CORRECTABLE_ERROR 0x0001 +#define PCIEM_STA_NON_FATAL_ERROR 0x0002 +#define PCIEM_STA_FATAL_ERROR 0x0004 +#define PCIEM_STA_UNSUPPORTED_REQ 0x0008 +#define PCIEM_STA_AUX_POWER 0x0010 +#define PCIEM_STA_TRANSACTION_PND 0x0020 +#define PCIER_LINK_CAP 0xc +#define PCIEM_LINK_CAP_MAX_SPEED 0x0000000f +#define PCIEM_LINK_CAP_MAX_WIDTH 0x000003f0 +#define PCIEM_LINK_CAP_ASPM 0x00000c00 +#define PCIEM_LINK_CAP_L0S_EXIT 0x00007000 +#define PCIEM_LINK_CAP_L1_EXIT 0x00038000 +#define PCIEM_LINK_CAP_CLOCK_PM 0x00040000 +#define PCIEM_LINK_CAP_SURPRISE_DOWN 0x00080000 +#define PCIEM_LINK_CAP_DL_ACTIVE 0x00100000 +#define PCIEM_LINK_CAP_LINK_BW_NOTIFY 0x00200000 +#define PCIEM_LINK_CAP_ASPM_COMPLIANCE 0x00400000 +#define PCIEM_LINK_CAP_PORT 0xff000000 +#define PCIER_LINK_CTL 0x10 +#define PCIEM_LINK_CTL_ASPMC_DIS 0x0000 +#define PCIEM_LINK_CTL_ASPMC_L0S 0x0001 +#define PCIEM_LINK_CTL_ASPMC_L1 0x0002 +#define PCIEM_LINK_CTL_ASPMC 0x0003 +#define PCIEM_LINK_CTL_RCB 0x0008 +#define PCIEM_LINK_CTL_LINK_DIS 0x0010 +#define PCIEM_LINK_CTL_RETRAIN_LINK 0x0020 +#define PCIEM_LINK_CTL_COMMON_CLOCK 0x0040 +#define PCIEM_LINK_CTL_EXTENDED_SYNC 0x0080 +#define PCIEM_LINK_CTL_ECPM 0x0100 +#define PCIEM_LINK_CTL_HAWD 0x0200 +#define PCIEM_LINK_CTL_LBMIE 0x0400 +#define PCIEM_LINK_CTL_LABIE 0x0800 +#define PCIER_LINK_STA 0x12 +#define PCIEM_LINK_STA_SPEED 0x000f +#define PCIEM_LINK_STA_WIDTH 0x03f0 +#define PCIEM_LINK_STA_TRAINING_ERROR 0x0400 +#define PCIEM_LINK_STA_TRAINING 0x0800 +#define PCIEM_LINK_STA_SLOT_CLOCK 0x1000 +#define PCIEM_LINK_STA_DL_ACTIVE 0x2000 +#define PCIEM_LINK_STA_LINK_BW_MGMT 0x4000 +#define PCIEM_LINK_STA_LINK_AUTO_BW 0x8000 +#define PCIER_SLOT_CAP 0x14 +#define PCIEM_SLOT_CAP_APB 0x00000001 +#define PCIEM_SLOT_CAP_PCP 0x00000002 +#define PCIEM_SLOT_CAP_MRLSP 0x00000004 +#define PCIEM_SLOT_CAP_AIP 0x00000008 +#define PCIEM_SLOT_CAP_PIP 0x00000010 +#define PCIEM_SLOT_CAP_HPS 0x00000020 +#define PCIEM_SLOT_CAP_HPC 0x00000040 +#define PCIEM_SLOT_CAP_SPLV 0x00007f80 +#define PCIEM_SLOT_CAP_SPLS 0x00018000 +#define PCIEM_SLOT_CAP_EIP 0x00020000 +#define PCIEM_SLOT_CAP_NCCS 0x00040000 +#define PCIEM_SLOT_CAP_PSN 0xfff80000 +#define PCIER_SLOT_CTL 0x18 +#define PCIEM_SLOT_CTL_ABPE 0x0001 +#define PCIEM_SLOT_CTL_PFDE 0x0002 +#define PCIEM_SLOT_CTL_MRLSCE 0x0004 +#define PCIEM_SLOT_CTL_PDCE 0x0008 +#define PCIEM_SLOT_CTL_CCIE 0x0010 +#define PCIEM_SLOT_CTL_HPIE 0x0020 +#define PCIEM_SLOT_CTL_AIC 0x00c0 +#define PCIEM_SLOT_CTL_AI_ON 0x0040 +#define PCIEM_SLOT_CTL_AI_BLINK 0x0080 +#define PCIEM_SLOT_CTL_AI_OFF 0x00c0 +#define PCIEM_SLOT_CTL_PIC 0x0300 +#define PCIEM_SLOT_CTL_PI_ON 0x0100 +#define PCIEM_SLOT_CTL_PI_BLINK 0x0200 +#define PCIEM_SLOT_CTL_PI_OFF 0x0300 +#define PCIEM_SLOT_CTL_PCC 0x0400 +#define PCIEM_SLOT_CTL_PC_ON 0x0000 +#define PCIEM_SLOT_CTL_PC_OFF 0x0400 +#define PCIEM_SLOT_CTL_EIC 0x0800 +#define PCIEM_SLOT_CTL_DLLSCE 0x1000 +#define PCIER_SLOT_STA 0x1a +#define PCIEM_SLOT_STA_ABP 0x0001 +#define PCIEM_SLOT_STA_PFD 0x0002 +#define PCIEM_SLOT_STA_MRLSC 0x0004 +#define PCIEM_SLOT_STA_PDC 0x0008 +#define PCIEM_SLOT_STA_CC 0x0010 +#define PCIEM_SLOT_STA_MRLSS 0x0020 +#define PCIEM_SLOT_STA_PDS 0x0040 +#define PCIEM_SLOT_STA_EIS 0x0080 +#define PCIEM_SLOT_STA_DLLSC 0x0100 +#define PCIER_ROOT_CTL 0x1c +#define PCIEM_ROOT_CTL_SERR_CORR 0x0001 +#define PCIEM_ROOT_CTL_SERR_NONFATAL 0x0002 +#define PCIEM_ROOT_CTL_SERR_FATAL 0x0004 +#define PCIEM_ROOT_CTL_PME 0x0008 +#define PCIEM_ROOT_CTL_CRS_VIS 0x0010 +#define PCIER_ROOT_CAP 0x1e +#define PCIEM_ROOT_CAP_CRS_VIS 0x0001 +#define PCIER_ROOT_STA 0x20 +#define PCIEM_ROOT_STA_PME_REQID_MASK 0x0000ffff +#define PCIEM_ROOT_STA_PME_STATUS 0x00010000 +#define PCIEM_ROOT_STA_PME_PEND 0x00020000 +#define PCIER_DEVICE_CAP2 0x24 +#define PCIEM_CAP2_COMP_TIMO_RANGES 0x0000000f +#define PCIEM_CAP2_COMP_TIMO_RANGE_A 0x00000001 +#define PCIEM_CAP2_COMP_TIMO_RANGE_B 0x00000002 +#define PCIEM_CAP2_COMP_TIMO_RANGE_C 0x00000004 +#define PCIEM_CAP2_COMP_TIMO_RANGE_D 0x00000008 +#define PCIEM_CAP2_COMP_TIMO_DISABLE 0x00000010 +#define PCIEM_CAP2_ARI 0x00000020 +#define PCIER_DEVICE_CTL2 0x28 +#define PCIEM_CTL2_COMP_TIMO_VAL 0x000f +#define PCIEM_CTL2_COMP_TIMO_50MS 0x0000 +#define PCIEM_CTL2_COMP_TIMO_100US 0x0001 +#define PCIEM_CTL2_COMP_TIMO_10MS 0x0002 +#define PCIEM_CTL2_COMP_TIMO_55MS 0x0005 +#define PCIEM_CTL2_COMP_TIMO_210MS 0x0006 +#define PCIEM_CTL2_COMP_TIMO_900MS 0x0009 +#define PCIEM_CTL2_COMP_TIMO_3500MS 0x000a +#define PCIEM_CTL2_COMP_TIMO_13S 0x000d +#define PCIEM_CTL2_COMP_TIMO_64S 0x000e +#define PCIEM_CTL2_COMP_TIMO_DISABLE 0x0010 +#define PCIEM_CTL2_ARI 0x0020 +#define PCIEM_CTL2_ATOMIC_REQ_ENABLE 0x0040 +#define PCIEM_CTL2_ATOMIC_EGR_BLOCK 0x0080 +#define PCIEM_CTL2_ID_ORDERED_REQ_EN 0x0100 +#define PCIEM_CTL2_ID_ORDERED_CMP_EN 0x0200 +#define PCIEM_CTL2_LTR_ENABLE 0x0400 +#define PCIEM_CTL2_OBFF 0x6000 +#define PCIEM_OBFF_DISABLE 0x0000 +#define PCIEM_OBFF_MSGA_ENABLE 0x2000 +#define PCIEM_OBFF_MSGB_ENABLE 0x4000 +#define PCIEM_OBFF_WAKE_ENABLE 0x6000 +#define PCIEM_CTL2_END2END_TLP 0x8000 +#define PCIER_DEVICE_STA2 0x2a +#define PCIER_LINK_CAP2 0x2c +#define PCIER_LINK_CTL2 0x30 +#define PCIEM_LNKCTL2_TLS 0x000f +#define PCIEM_LNKCTL2_TLS_2_5GT 0x0001 +#define PCIEM_LNKCTL2_TLS_5_0GT 0x0002 +#define PCIEM_LNKCTL2_TLS_8_0GT 0x0003 +#define PCIEM_LNKCTL2_TLS_16_0GT 0x0004 +#define PCIEM_LNKCTL2_TLS_32_0GT 0x0005 +#define PCIEM_LNKCTL2_TLS_64_0GT 0x0006 +#define PCIEM_LNKCTL2_ENTER_COMP 0x0010 +#define PCIEM_LNKCTL2_TX_MARGIN 0x0380 +#define PCIEM_LNKCTL2_HASD 0x0020 +#define PCIER_LINK_STA2 0x32 +#define PCIER_SLOT_CAP2 0x34 +#define PCIER_SLOT_CTL2 0x38 +#define PCIER_SLOT_STA2 0x3a + +/* MSI-X definitions */ +#define PCIR_MSIX_CTRL 0x2 +#define PCIM_MSIXCTRL_MSIX_ENABLE 0x8000 +#define PCIM_MSIXCTRL_FUNCTION_MASK 0x4000 +#define PCIM_MSIXCTRL_TABLE_SIZE 0x07ff +#define PCIR_MSIX_TABLE 0x4 +#define PCIR_MSIX_PBA 0x8 +#define PCIM_MSIX_BIR_MASK 0x7 +#define PCIM_MSIX_TABLE_OFFSET 0xfffffff8 +#define PCIM_MSIX_BIR_BAR_10 0 +#define PCIM_MSIX_BIR_BAR_14 1 +#define PCIM_MSIX_BIR_BAR_18 2 +#define PCIM_MSIX_BIR_BAR_1C 3 +#define PCIM_MSIX_BIR_BAR_20 4 +#define PCIM_MSIX_BIR_BAR_24 5 +#define PCIM_MSIX_ENTRY_SIZE 16 +#define PCIM_MSIX_ENTRY_LOWER_ADDR 0x0 /* Message Address */ +#define PCIM_MSIX_ENTRY_UPPER_ADDR 0x4 /* Message Upper Address */ +#define PCIM_MSIX_ENTRY_DATA 0x8 /* Message Data */ +#define PCIM_MSIX_ENTRY_VECTOR_CTRL 0xc /* Vector Control */ +#define PCIM_MSIX_ENTRYVECTOR_CTRL_MASK 0x1 + +/* PCI Advanced Features definitions */ +#define PCIR_PCIAF_CAP 0x3 +#define PCIM_PCIAFCAP_TP 0x01 +#define PCIM_PCIAFCAP_FLR 0x02 +#define PCIR_PCIAF_CTRL 0x4 +#define PCIR_PCIAFCTRL_FLR 0x01 +#define PCIR_PCIAF_STATUS 0x5 +#define PCIR_PCIAFSTATUS_TP 0x01 + +/* Advanced Error Reporting */ +#define PCIR_AER_UC_STATUS 0x04 +#define PCIM_AER_UC_TRAINING_ERROR 0x00000001 +#define PCIM_AER_UC_DL_PROTOCOL_ERROR 0x00000010 +#define PCIM_AER_UC_SURPRISE_LINK_DOWN 0x00000020 +#define PCIM_AER_UC_POISONED_TLP 0x00001000 +#define PCIM_AER_UC_FC_PROTOCOL_ERROR 0x00002000 +#define PCIM_AER_UC_COMPLETION_TIMEOUT 0x00004000 +#define PCIM_AER_UC_COMPLETER_ABORT 0x00008000 +#define PCIM_AER_UC_UNEXPECTED_COMPLETION 0x00010000 +#define PCIM_AER_UC_RECEIVER_OVERFLOW 0x00020000 +#define PCIM_AER_UC_MALFORMED_TLP 0x00040000 +#define PCIM_AER_UC_ECRC_ERROR 0x00080000 +#define PCIM_AER_UC_UNSUPPORTED_REQUEST 0x00100000 +#define PCIM_AER_UC_ACS_VIOLATION 0x00200000 +#define PCIM_AER_UC_INTERNAL_ERROR 0x00400000 +#define PCIM_AER_UC_MC_BLOCKED_TLP 0x00800000 +#define PCIM_AER_UC_ATOMIC_EGRESS_BLK 0x01000000 +#define PCIM_AER_UC_TLP_PREFIX_BLOCKED 0x02000000 +#define PCIR_AER_UC_MASK 0x08 /* Shares bits with UC_STATUS */ +#define PCIR_AER_UC_SEVERITY 0x0c /* Shares bits with UC_STATUS */ +#define PCIR_AER_COR_STATUS 0x10 +#define PCIM_AER_COR_RECEIVER_ERROR 0x00000001 +#define PCIM_AER_COR_BAD_TLP 0x00000040 +#define PCIM_AER_COR_BAD_DLLP 0x00000080 +#define PCIM_AER_COR_REPLAY_ROLLOVER 0x00000100 +#define PCIM_AER_COR_REPLAY_TIMEOUT 0x00001000 +#define PCIM_AER_COR_ADVISORY_NF_ERROR 0x00002000 +#define PCIM_AER_COR_INTERNAL_ERROR 0x00004000 +#define PCIM_AER_COR_HEADER_LOG_OVFLOW 0x00008000 +#define PCIR_AER_COR_MASK 0x14 /* Shares bits with COR_STATUS */ +#define PCIR_AER_CAP_CONTROL 0x18 +#define PCIM_AER_FIRST_ERROR_PTR 0x0000001f +#define PCIM_AER_ECRC_GEN_CAPABLE 0x00000020 +#define PCIM_AER_ECRC_GEN_ENABLE 0x00000040 +#define PCIM_AER_ECRC_CHECK_CAPABLE 0x00000080 +#define PCIM_AER_ECRC_CHECK_ENABLE 0x00000100 +#define PCIM_AER_MULT_HDR_CAPABLE 0x00000200 +#define PCIM_AER_MULT_HDR_ENABLE 0x00000400 +#define PCIM_AER_TLP_PREFIX_LOG_PRESENT 0x00000800 +#define PCIR_AER_HEADER_LOG 0x1c +#define PCIR_AER_ROOTERR_CMD 0x2c /* Only for root complex ports */ +#define PCIM_AER_ROOTERR_COR_ENABLE 0x00000001 +#define PCIM_AER_ROOTERR_NF_ENABLE 0x00000002 +#define PCIM_AER_ROOTERR_F_ENABLE 0x00000004 +#define PCIR_AER_ROOTERR_STATUS 0x30 /* Only for root complex ports */ +#define PCIM_AER_ROOTERR_COR_ERR 0x00000001 +#define PCIM_AER_ROOTERR_MULTI_COR_ERR 0x00000002 +#define PCIM_AER_ROOTERR_UC_ERR 0x00000004 +#define PCIM_AER_ROOTERR_MULTI_UC_ERR 0x00000008 +#define PCIM_AER_ROOTERR_FIRST_UC_FATAL 0x00000010 +#define PCIM_AER_ROOTERR_NF_ERR 0x00000020 +#define PCIM_AER_ROOTERR_F_ERR 0x00000040 +#define PCIM_AER_ROOTERR_INT_MESSAGE 0xf8000000 +#define PCIR_AER_COR_SOURCE_ID 0x34 /* Only for root complex ports */ +#define PCIR_AER_ERR_SOURCE_ID 0x36 /* Only for root complex ports */ +#define PCIR_AER_TLP_PREFIX_LOG 0x38 /* Only for TLP prefix functions */ + +/* Virtual Channel definitions */ +#define PCIR_VC_CAP1 0x04 +#define PCIM_VC_CAP1_EXT_COUNT 0x00000007 +#define PCIM_VC_CAP1_LOWPRI_EXT_COUNT 0x00000070 +#define PCIR_VC_CAP2 0x08 +#define PCIR_VC_CONTROL 0x0c +#define PCIR_VC_STATUS 0x0e +#define PCIR_VC_RESOURCE_CAP(n) (0x10 + (n) * 0x0c) +#define PCIR_VC_RESOURCE_CTL(n) (0x14 + (n) * 0x0c) +#define PCIR_VC_RESOURCE_STA(n) (0x18 + (n) * 0x0c) + +/* Serial Number definitions */ +#define PCIR_SERIAL_LOW 0x04 +#define PCIR_SERIAL_HIGH 0x08 + +/* SR-IOV definitions */ +#define PCIR_SRIOV_CTL 0x08 +#define PCIM_SRIOV_VF_EN 0x01 +#define PCIM_SRIOV_VF_MSE 0x08 /* Memory space enable. */ +#define PCIM_SRIOV_ARI_EN 0x10 +#define PCIR_SRIOV_TOTAL_VFS 0x0e +#define PCIR_SRIOV_NUM_VFS 0x10 +#define PCIR_SRIOV_VF_OFF 0x14 +#define PCIR_SRIOV_VF_STRIDE 0x16 +#define PCIR_SRIOV_VF_DID 0x1a +#define PCIR_SRIOV_PAGE_CAP 0x1c +#define PCIR_SRIOV_PAGE_SIZE 0x20 + +#define PCI_SRIOV_BASE_PAGE_SHIFT 12 + +#define PCIR_SRIOV_BARS 0x24 +#define PCIR_SRIOV_BAR(x) (PCIR_SRIOV_BARS + (x) * 4) + +/* Extended Capability Vendor-Specific definitions */ +#define PCIR_VSEC_HEADER 0x04 +#define PCIR_VSEC_ID(hdr) ((hdr) & 0xffff) +#define PCIR_VSEC_REV(hdr) (((hdr) & 0xf0000) >> 16) +#define PCIR_VSEC_LENGTH(hdr) (((hdr) & 0xfff00000) >> 20) +#define PCIR_VSEC_DATA 0x08 + +/* ASPM L1 PM Substates */ +#define PCIR_L1SS_CAP 0x04 /* Capabilities Register */ +#define PCIM_L1SS_CAP_PCIPM_L1_2 0x00000001 /* PCI-PM L1.2 Supported */ +#define PCIM_L1SS_CAP_PCIPM_L1_1 0x00000002 /* PCI-PM L1.1 Supported */ +#define PCIM_L1SS_CAP_ASPM_L1_2 0x00000004 /* ASPM L1.2 Supported */ +#define PCIM_L1SS_CAP_ASPM_L1_1 0x00000008 /* ASPM L1.1 Supported */ +#define PCIM_L1SS_CAP_L1_PM_SS 0x00000010 /* L1 PM Substates Supported */ +#define PCIM_L1SS_CAP_CM_RESTORE_TIME 0x0000ff00 /* Port Common_Mode_Restore_Time */ +#define PCIM_L1SS_CAP_P_PWR_ON_SCALE 0x00030000 /* Port T_POWER_ON scale */ +#define PCIM_L1SS_CAP_P_PWR_ON_VALUE 0x00f80000 /* Port T_POWER_ON value */ +#define PCIR_L1SS_CTL1 0x08 /* Control 1 Register */ +#define PCIM_L1SS_CTL1_PCIPM_L1_2 0x00000001 /* PCI-PM L1.2 Enable */ +#define PCIM_L1SS_CTL1_PCIPM_L1_1 0x00000002 /* PCI-PM L1.1 Enable */ +#define PCIM_L1SS_CTL1_ASPM_L1_2 0x00000004 /* ASPM L1.2 Enable */ +#define PCIM_L1SS_CTL1_ASPM_L1_1 0x00000008 /* ASPM L1.1 Enable */ +#define PCIM_L1SS_CTL1_L1_2_MASK 0x00000005 +#define PCIM_L1SS_CTL1_L1SS_MASK 0x0000000f +#define PCIM_L1SS_CTL1_CM_RESTORE_TIME 0x0000ff00 /* Common_Mode_Restore_Time */ +#define PCIM_L1SS_CTL1_LTR_L12_TH_VALUE 0x03ff0000 /* LTR_L1.2_THRESHOLD_Value */ +#define PCIM_L1SS_CTL1_LTR_L12_TH_SCALE 0xe0000000 /* LTR_L1.2_THRESHOLD_Scale */ +#define PCIR_L1SS_CTL2 0x0c /* Control 2 Register */ +#define PCIM_L1SS_CTL2_T_PWR_ON_SCALE 0x00000003 /* T_POWER_ON Scale */ +#define PCIM_L1SS_CTL2_T_PWR_ON_VALUE 0x000000f8 /* T_POWER_ON Value */ + +/* Alternative Routing-ID Interpretation */ +#define PCIR_ARI_CAP 0x04 /* Capabilities Register */ +#define PCIM_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */ +#define PCIM_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */ +#define PCIM_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */ +#define PCIR_ARI_CTRL 0x06 /* ARI Control Register */ +#define PCIM_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ +#define PCIM_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ +#define PCIM_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ +#define PCIR_EXT_CAP_ARI_SIZEOF 8 + +/* + * PCI Express Firmware Interface definitions + */ +#define PCI_OSC_STATUS 0 +#define PCI_OSC_SUPPORT 1 +#define PCIM_OSC_SUPPORT_EXT_PCI_CONF 0x01 /* Extended PCI Config Space */ +#define PCIM_OSC_SUPPORT_ASPM 0x02 /* Active State Power Management */ +#define PCIM_OSC_SUPPORT_CPMC 0x04 /* Clock Power Management Cap */ +#define PCIM_OSC_SUPPORT_SEG_GROUP 0x08 /* PCI Segment Groups supported */ +#define PCIM_OSC_SUPPORT_MSI 0x10 /* MSI signalling supported */ +#define PCI_OSC_CTL 2 +#define PCIM_OSC_CTL_PCIE_HP 0x01 /* PCIe Native Hot Plug */ +#define PCIM_OSC_CTL_SHPC_HP 0x02 /* SHPC Native Hot Plug */ +#define PCIM_OSC_CTL_PCIE_PME 0x04 /* PCIe Native Power Mgt Events */ +#define PCIM_OSC_CTL_PCIE_AER 0x08 /* PCIe Advanced Error Reporting */ +#define PCIM_OSC_CTL_PCIE_CAP_STRUCT 0x10 /* Various Capability Structures */ + +#endif /* __PCI_REGS_H__ */ diff --git a/rt-thread/components/drivers/pci/pme.c b/rt-thread/components/drivers/pci/pme.c new file mode 100644 index 0000000..b4278d3 --- /dev/null +++ b/rt-thread/components/drivers/pci/pme.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "pci.pme" +#define DBG_LVL DBG_INFO +#include + +/* + * Power Management Capability Register: + * + * 31 27 26 25 24 22 21 20 19 18 16 15 8 7 0 + * +---------+---+---+--------+---+---+---+------+-----------+----------------+ + * | | | | | | | | | | Capabilitiy ID | + * +---------+---+---+--------+---+---+---+------+-----------+----------------+ + * ^ ^ ^ ^ ^ ^ ^ ^ ^ + * | | | | | | | | | + * | | | | | | | | +---- Next Capabilitiy Pointer + * | | | | | | | +------------- Version + * | | | | | | +------------------- PME Clock + * | | | | | +----------------------- Immediate Readiness on Return to D0 + * | | | | +--------------------------- Device Specifiic Initializtion + * | | | +--------------------------------- Aux Current + * | | +---------------------------------------- D1 Support + * | +-------------------------------------------- D2 Support + * +--------------------------------------------------- PME Support + */ + +void rt_pci_pme_init(struct rt_pci_device *pdev) +{ + rt_uint16_t pmc; + + if (!pdev || !(pdev->pme_cap = rt_pci_find_capability(pdev, PCIY_PMG))) + { + return; + } + + rt_pci_read_config_u16(pdev, pdev->pme_cap + PCIR_POWER_CAP, &pmc); + + if ((pmc & PCIM_PCAP_SPEC) > 3) + { + LOG_E("%s: Unsupported PME CAP regs spec %u", + rt_dm_dev_get_name(&pdev->parent), pmc & PCIM_PCAP_SPEC); + + return; + } + + pmc &= PCIM_PCAP_PMEMASK; + + if (pmc) + { + pdev->pme_support = RT_FIELD_GET(PCIM_PCAP_PMEMASK, pmc); + + rt_pci_pme_active(pdev, RT_FALSE); + } +} + +rt_err_t rt_pci_enable_wake(struct rt_pci_device *pdev, + enum rt_pci_power state, rt_bool_t enable) +{ + if (!pdev || state >= RT_PCI_PME_MAX) + { + return -RT_EINVAL; + } + + if (enable) + { + if (rt_pci_pme_capable(pdev, state) || + rt_pci_pme_capable(pdev, RT_PCI_D3COLD)) + { + rt_pci_pme_active(pdev, RT_EOK); + } + } + else + { + rt_pci_pme_active(pdev, RT_FALSE); + } + + return RT_EOK; +} + +static void pci_pme_active(struct rt_pci_device *pdev, rt_bool_t enable) +{ + rt_uint16_t pmcsr; + + if (!pdev->pme_support) + { + return; + } + + rt_pci_read_config_u16(pdev, pdev->pme_cap + PCIR_POWER_STATUS, &pmcsr); + /* Clear PME_Status by writing 1 to it and enable PME# */ + pmcsr |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; + + if (!enable) + { + pmcsr &= ~PCIM_PSTAT_PMEENABLE; + } + + rt_pci_write_config_u16(pdev, pdev->pme_cap + PCIR_POWER_STATUS, pmcsr); +} + +void rt_pci_pme_active(struct rt_pci_device *pdev, rt_bool_t enable) +{ + if (!pdev) + { + return; + } + + pci_pme_active(pdev, enable); + rt_dm_power_domain_attach(&pdev->parent, enable); +} diff --git a/rt-thread/components/drivers/pci/probe.c b/rt-thread/components/drivers/pci/probe.c new file mode 100644 index 0000000..fc15eaa --- /dev/null +++ b/rt-thread/components/drivers/pci/probe.c @@ -0,0 +1,926 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#include + +#define DBG_TAG "pci.probe" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +rt_inline void spin_lock(struct rt_spinlock *spinlock) +{ + rt_hw_spin_lock(&spinlock->lock); +} + +rt_inline void spin_unlock(struct rt_spinlock *spinlock) +{ + rt_hw_spin_unlock(&spinlock->lock); +} + +struct rt_pci_host_bridge *rt_pci_host_bridge_alloc(rt_size_t priv_size) +{ + struct rt_pci_host_bridge *bridge = rt_calloc(1, sizeof(*bridge) + priv_size); + + return bridge; +} + +rt_err_t rt_pci_host_bridge_free(struct rt_pci_host_bridge *bridge) +{ + if (!bridge) + { + return -RT_EINVAL; + } + + if (bridge->bus_regions) + { + rt_free(bridge->bus_regions); + } + + if (bridge->dma_regions) + { + rt_free(bridge->dma_regions); + } + + rt_free(bridge); + + return RT_EOK; +} + +rt_err_t rt_pci_host_bridge_init(struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err = RT_EOK; + + if (host_bridge->parent.ofw_node) + { + err = rt_pci_ofw_host_bridge_init(host_bridge->parent.ofw_node, host_bridge); + } + + return err; +} + +struct rt_pci_device *rt_pci_alloc_device(struct rt_pci_bus *bus) +{ + struct rt_pci_device *pdev = rt_calloc(1, sizeof(*pdev)); + + if (!pdev) + { + return RT_NULL; + } + + rt_list_init(&pdev->list); + pdev->bus = bus; + + if (bus) + { + spin_lock(&bus->lock); + rt_list_insert_before(&bus->devices_nodes, &pdev->list); + spin_unlock(&bus->lock); + } + + pdev->subsystem_vendor = PCI_ANY_ID; + pdev->subsystem_device = PCI_ANY_ID; + + pdev->irq = -1; + + for (int i = 0; i < RT_ARRAY_SIZE(pdev->resource); ++i) + { + pdev->resource[i].flags = PCI_BUS_REGION_F_NONE; + } + +#ifdef RT_PCI_MSI + rt_list_init(&pdev->msi_desc_nodes); + rt_spin_lock_init(&pdev->msi_lock); +#endif + + return pdev; +} + +struct rt_pci_device *rt_pci_scan_single_device(struct rt_pci_bus *bus, rt_uint32_t devfn) +{ + rt_err_t err; + struct rt_pci_device *pdev = RT_NULL; + rt_uint16_t vendor = PCI_ANY_ID, device = PCI_ANY_ID; + + if (!bus) + { + goto _end; + } + + err = rt_pci_bus_read_config_u16(bus, devfn, PCIR_VENDOR, &vendor); + rt_pci_bus_read_config_u16(bus, devfn, PCIR_DEVICE, &device); + + if (vendor == (typeof(vendor))PCI_ANY_ID || + vendor == (typeof(vendor))0x0000 || err) + { + goto _end; + } + + if (!(pdev = rt_pci_alloc_device(bus))) + { + goto _end; + } + + pdev->devfn = devfn; + pdev->vendor = vendor; + pdev->device = device; + + rt_dm_dev_set_name(&pdev->parent, "%04x:%02x:%02x.%u", + rt_pci_domain(pdev), pdev->bus->number, + RT_PCI_SLOT(pdev->devfn), RT_PCI_FUNC(pdev->devfn)); + + if (rt_pci_setup_device(pdev)) + { + rt_free(pdev); + pdev = RT_NULL; + + goto _end; + } + + rt_pci_device_register(pdev); + +_end: + return pdev; +} + +static rt_bool_t pci_intx_mask_broken(struct rt_pci_device *pdev) +{ + rt_bool_t res = RT_FALSE; + rt_uint16_t orig, toggle, new; + + rt_pci_read_config_u16(pdev, PCIR_COMMAND, &orig); + toggle = orig ^ PCIM_CMD_INTxDIS; + rt_pci_write_config_u16(pdev, PCIR_COMMAND, toggle); + rt_pci_read_config_u16(pdev, PCIR_COMMAND, &new); + + rt_pci_write_config_u16(pdev, PCIR_COMMAND, orig); + + if (new != toggle) + { + res = RT_TRUE; + } + + return res; +} + +static void pci_read_irq(struct rt_pci_device *pdev) +{ + rt_uint8_t irq = 0; + + rt_pci_read_config_u8(pdev, PCIR_INTPIN, &irq); + pdev->pin = irq; + + if (irq) + { + rt_pci_read_config_u8(pdev, PCIR_INTLINE, &irq); + } + pdev->irq = irq; +} + +static void pcie_set_port_type(struct rt_pci_device *pdev) +{ + int pos; + + if (!(pos = rt_pci_find_capability(pdev, PCIY_EXPRESS))) + { + return; + } + + pdev->pcie_cap = pos; +} + +static void pci_configure_ari(struct rt_pci_device *pdev) +{ + rt_uint32_t cap, ctl2_ari; + struct rt_pci_device *bridge; + + if (!rt_pci_is_pcie(pdev) || pdev->devfn) + { + return; + } + + bridge = pdev->bus->self; + + if (rt_pci_is_root_bus(pdev->bus) || !bridge) + { + return; + } + + rt_pci_read_config_u32(bridge, bridge->pcie_cap + PCIER_DEVICE_CAP2, &cap); + if (!(cap & PCIEM_CAP2_ARI)) + { + return; + } + + rt_pci_read_config_u32(bridge, bridge->pcie_cap + PCIER_DEVICE_CTL2, &ctl2_ari); + + if (rt_pci_find_ext_capability(pdev, PCIZ_ARI)) + { + ctl2_ari |= PCIEM_CTL2_ARI; + bridge->ari_enabled = RT_TRUE; + } + else + { + ctl2_ari &= ~PCIEM_CTL2_ARI; + bridge->ari_enabled = RT_FALSE; + } + + rt_pci_write_config_u32(bridge, bridge->pcie_cap + PCIER_DEVICE_CTL2, ctl2_ari); +} + +static rt_uint16_t pci_cfg_space_size_ext(struct rt_pci_device *pdev) +{ + rt_uint32_t status; + + if (rt_pci_read_config_u32(pdev, PCI_REGMAX + 1, &status)) + { + return PCI_REGMAX + 1; + } + + return PCIE_REGMAX + 1; +} + +static rt_uint16_t pci_cfg_space_size(struct rt_pci_device *pdev) +{ + int pos; + rt_uint32_t status; + rt_uint16_t class = pdev->class >> 8; + + if (class == PCIS_BRIDGE_HOST) + { + return pci_cfg_space_size_ext(pdev); + } + + if (rt_pci_is_pcie(pdev)) + { + return pci_cfg_space_size_ext(pdev); + } + + pos = rt_pci_find_capability(pdev, PCIY_PCIX); + if (!pos) + { + return PCI_REGMAX + 1; + } + + rt_pci_read_config_u32(pdev, pos + PCIXR_STATUS, &status); + if (status & (PCIXM_STATUS_266CAP | PCIXM_STATUS_533CAP)) + { + return pci_cfg_space_size_ext(pdev); + } + + return PCI_REGMAX + 1; +} + +static void pci_init_capabilities(struct rt_pci_device *pdev) +{ + rt_pci_pme_init(pdev); + +#ifdef RT_PCI_MSI + rt_pci_msi_init(pdev); /* Disable MSI */ + rt_pci_msix_init(pdev); /* Disable MSI-X */ +#endif + + pcie_set_port_type(pdev); + pdev->cfg_size = pci_cfg_space_size(pdev); + pci_configure_ari(pdev); + + pdev->no_msi = RT_FALSE; + pdev->msi_enabled = RT_FALSE; + pdev->msix_enabled = RT_FALSE; +} + +rt_err_t rt_pci_setup_device(struct rt_pci_device *pdev) +{ + rt_uint8_t pos; + rt_uint32_t class = 0; + struct rt_pci_host_bridge *host_bridge; + + if (!pdev) + { + return -RT_EINVAL; + } + + if (!(host_bridge = rt_pci_find_host_bridge(pdev->bus))) + { + return -RT_EINVAL; + } + + rt_pci_ofw_device_init(pdev); + + rt_pci_read_config_u32(pdev, PCIR_REVID, &class); + + pdev->revision = class & 0xff; + pdev->class = class >> 8; /* Upper 3 bytes */ + rt_pci_read_config_u8(pdev, PCIR_HDRTYPE, &pdev->hdr_type); + + /* Clear errors left from system firmware */ + rt_pci_write_config_u16(pdev, PCIR_STATUS, 0xffff); + + if (pdev->hdr_type & 0x80) + { + pdev->multi_function = RT_TRUE; + } + pdev->hdr_type &= PCIM_HDRTYPE; + + if (pci_intx_mask_broken(pdev)) + { + pdev->broken_intx_masking = RT_TRUE; + } + + rt_dm_dev_set_name(&pdev->parent, "%04x:%02x:%02x.%u", rt_pci_domain(pdev), + pdev->bus->number, RT_PCI_SLOT(pdev->devfn), RT_PCI_FUNC(pdev->devfn)); + + switch (pdev->hdr_type) + { + case PCIM_HDRTYPE_NORMAL: + if (class == PCIS_BRIDGE_PCI) + { + goto error; + } + pci_read_irq(pdev); + rt_pci_device_alloc_resource(host_bridge, pdev); + rt_pci_read_config_u16(pdev, PCIR_SUBVEND_0, &pdev->subsystem_vendor); + rt_pci_read_config_u16(pdev, PCIR_SUBDEV_0, &pdev->subsystem_device); + break; + + case PCIM_HDRTYPE_BRIDGE: + pci_read_irq(pdev); + rt_pci_device_alloc_resource(host_bridge, pdev); + pos = rt_pci_find_capability(pdev, PCIY_SUBVENDOR); + if (pos) + { + rt_pci_read_config_u16(pdev, PCIR_SUBVENDCAP, &pdev->subsystem_vendor); + rt_pci_read_config_u16(pdev, PCIR_SUBDEVCAP, &pdev->subsystem_device); + } + break; + + case PCIM_HDRTYPE_CARDBUS: + if (class != PCIS_BRIDGE_CARDBUS) + { + goto error; + } + pci_read_irq(pdev); + rt_pci_device_alloc_resource(host_bridge, pdev); + rt_pci_read_config_u16(pdev, PCIR_SUBVEND_2, &pdev->subsystem_vendor); + rt_pci_read_config_u16(pdev, PCIR_SUBDEV_2, &pdev->subsystem_device); + break; + + default: + LOG_E("Ignoring device unknown header type %02x", pdev->hdr_type); + return -RT_EIO; + + error: + LOG_E("Ignoring class %08x (doesn't match header type %02x)", pdev->class, pdev->hdr_type); + pdev->class = PCIC_NOT_DEFINED << 8; + } + + pci_init_capabilities(pdev); + + if (rt_pci_is_pcie(pdev)) + { + rt_pci_read_config_u16(pdev, pdev->pcie_cap + PCIER_FLAGS, &pdev->exp_flags); + } + + return RT_EOK; +} + +static struct rt_pci_bus *pci_alloc_bus(struct rt_pci_bus *parent); + +static rt_err_t pci_child_bus_init(struct rt_pci_bus *bus, rt_uint32_t bus_no, + struct rt_pci_host_bridge *host_bridge, struct rt_pci_device *pdev) +{ + rt_err_t err; + struct rt_pci_bus *parent_bus = bus->parent; + + bus->sysdata = parent_bus->sysdata; + bus->self = pdev; + bus->ops = host_bridge->child_ops ? : parent_bus->ops; + + bus->number = bus_no; + rt_sprintf(bus->name, "%04x:%02x", host_bridge->domain, bus_no); + + rt_pci_ofw_bus_init(bus); + + if (bus->ops->add) + { + if ((err = bus->ops->add(bus))) + { + rt_pci_ofw_bus_free(bus); + + LOG_E("PCI-Bus<%s> add bus failed with err = %s", + bus->name, rt_strerror(err)); + + return err; + } + } + + return RT_EOK; +} + +static rt_bool_t pci_ea_fixed_busnrs(struct rt_pci_device *pdev, + rt_uint8_t *sec, rt_uint8_t *sub) +{ + int pos, offset; + rt_uint32_t dw; + rt_uint8_t ea_sec, ea_sub; + + pos = rt_pci_find_capability(pdev, PCIY_EA); + if (!pos) + { + return RT_FALSE; + } + + offset = pos + PCIR_EA_FIRST_ENT; + rt_pci_read_config_u32(pdev, offset, &dw); + ea_sec = PCIM_EA_SEC_NR(dw); + ea_sub = PCIM_EA_SUB_NR(dw); + if (ea_sec == 0 || ea_sub < ea_sec) + { + return RT_FALSE; + } + + *sec = ea_sec; + *sub = ea_sub; + + return RT_TRUE; +} + +static void pcie_fixup_link(struct rt_pci_device *pdev) +{ + int pos = pdev->pcie_cap; + rt_uint16_t exp_lnkctl, exp_lnkctl2, exp_lnksta; + rt_uint16_t exp_type = pdev->exp_flags & PCIEM_FLAGS_TYPE; + + if ((pdev->exp_flags & PCIEM_FLAGS_VERSION) < 2) + { + return; + } + + if (exp_type != PCIEM_TYPE_ROOT_PORT && + exp_type != PCIEM_TYPE_DOWNSTREAM_PORT && + exp_type != PCIEM_TYPE_PCIE_BRIDGE) + { + return; + } + + rt_pci_read_config_u16(pdev, pos + PCIER_LINK_CTL, &exp_lnkctl); + rt_pci_read_config_u16(pdev, pos + PCIER_LINK_CTL2, &exp_lnkctl2); + + rt_pci_write_config_u16(pdev, pos + PCIER_LINK_CTL2, + (exp_lnkctl2 & ~PCIEM_LNKCTL2_TLS) | PCIEM_LNKCTL2_TLS_2_5GT); + rt_pci_write_config_u16(pdev, pos + PCIER_LINK_CTL, + exp_lnkctl | PCIEM_LINK_CTL_RETRAIN_LINK); + + for (int i = 0; i < 20; ++i) + { + rt_pci_read_config_u16(pdev, pos + PCIER_LINK_STA, &exp_lnksta); + + if (!!(exp_lnksta & PCIEM_LINK_STA_DL_ACTIVE)) + { + goto _status_sync; + } + + rt_thread_mdelay(10); + } + + /* Fail, restore */ + rt_pci_write_config_u16(pdev, pos + PCIER_LINK_CTL2, exp_lnkctl2); + rt_pci_write_config_u16(pdev, pos + PCIER_LINK_CTL, + exp_lnkctl | PCIEM_LINK_CTL_RETRAIN_LINK); + +_status_sync: + /* Wait a while for success or failure */ + rt_thread_mdelay(100); +} + +static rt_uint32_t pci_scan_bridge_extend(struct rt_pci_bus *bus, struct rt_pci_device *pdev, + rt_uint32_t bus_no_start, rt_uint32_t buses, rt_bool_t reconfigured) +{ + rt_bool_t fixed_buses; + rt_uint8_t fixed_sub, fixed_sec; + rt_uint8_t primary, secondary, subordinate; + rt_uint32_t value, bus_no = bus_no_start; + struct rt_pci_bus *next_bus; + struct rt_pci_host_bridge *host_bridge; + + /* We not supported init CardBus, it always used in the PC servers. */ + if (pdev->hdr_type == PCIM_HDRTYPE_CARDBUS) + { + LOG_E("CardBus is not supported in system"); + + goto _end; + } + + rt_pci_read_config_u32(pdev, PCIR_PRIBUS_1, &value); + primary = value & 0xff; + secondary = (value >> 8) & 0xff; + subordinate = (value >> 16) & 0xff; + + if (primary == bus->number && bus->number > secondary && secondary > subordinate) + { + if (!reconfigured) + { + goto _end; + } + + LOG_I("Bridge configuration: primary(%02x) secondary(%02x) subordinate(%02x)", + primary, secondary, subordinate); + } + + if (pdev->pcie_cap) + { + pcie_fixup_link(pdev); + } + + ++bus_no; + /* Count of subordinate */ + buses -= !!buses; + + host_bridge = rt_pci_find_host_bridge(bus); + RT_ASSERT(host_bridge != RT_NULL); + + /* Clear errors */ + rt_pci_write_config_u16(pdev, PCIR_STATUS, RT_UINT16_MAX); + + fixed_buses = pci_ea_fixed_busnrs(pdev, &fixed_sec, &fixed_sub); + + if (!(next_bus = pci_alloc_bus(bus))) + { + goto _end; + } + + /* Clear bus info */ + rt_pci_write_config_u32(pdev, PCIR_PRIBUS_1, value & ~0xffffff); + + if (!(next_bus = pci_alloc_bus(bus))) + { + LOG_E("Alloc bus(%02x) fail", bus_no); + goto _end; + } + + if (pci_child_bus_init(next_bus, bus_no, host_bridge, pdev)) + { + goto _end; + } + + /* Fill primary, secondary */ + value = (buses & 0xff000000) | (bus->number << 0) | (next_bus->number << 8); + rt_pci_write_config_u32(pdev, PCIR_PRIBUS_1, value); + + bus_no = rt_pci_scan_child_buses(next_bus, buses); + + /* Fill subordinate */ + value |= next_bus->number + rt_list_len(&next_bus->children_nodes); + rt_pci_write_config_u32(pdev, PCIR_PRIBUS_1, value); + + if (fixed_buses) + { + bus_no = fixed_sub; + } + rt_pci_write_config_u8(pdev, PCIR_SUBBUS_1, bus_no); + +_end: + return bus_no; +} + +rt_uint32_t rt_pci_scan_bridge(struct rt_pci_bus *bus, struct rt_pci_device *pdev, + rt_uint32_t bus_no_start, rt_bool_t reconfigured) +{ + if (!bus || !pdev) + { + return RT_UINT32_MAX; + } + + return pci_scan_bridge_extend(bus, pdev, bus_no_start, 0, reconfigured); +} + +rt_inline rt_bool_t only_one_child(struct rt_pci_bus *bus) +{ + struct rt_pci_device *pdev; + + if (rt_pci_is_root_bus(bus)) + { + return RT_FALSE; + } + + pdev = bus->self; + + if (rt_pci_is_pcie(pdev)) + { + rt_uint16_t exp_type = pdev->exp_flags & PCIEM_FLAGS_TYPE; + + if (exp_type == PCIEM_TYPE_ROOT_PORT || + exp_type == PCIEM_TYPE_DOWNSTREAM_PORT || + exp_type == PCIEM_TYPE_PCIE_BRIDGE) + { + return RT_TRUE; + } + } + + return RT_FALSE; +} + +static int next_fn(struct rt_pci_bus *bus, struct rt_pci_device *pdev, int fn) +{ + if (!rt_pci_is_root_bus(bus) && bus->self->ari_enabled) + { + int pos, next_fn; + rt_uint16_t cap = 0; + + if (!pdev) + { + return -RT_EINVAL; + } + + pos = rt_pci_find_ext_capability(pdev, PCIZ_ARI); + + if (!pos) + { + return -RT_EINVAL; + } + + rt_pci_read_config_u16(pdev, pos + PCIR_ARI_CAP, &cap); + next_fn = PCIM_ARI_CAP_NFN(cap); + + if (next_fn <= fn) + { + return -RT_EINVAL; + } + + return next_fn; + } + + if (fn >= RT_PCI_FUNCTION_MAX - 1) + { + return -RT_EINVAL; + } + + if (pdev && !pdev->multi_function) + { + return -RT_EINVAL; + } + + return fn + 1; +} + +rt_size_t rt_pci_scan_slot(struct rt_pci_bus *bus, rt_uint32_t devfn) +{ + rt_size_t nr = 0; + struct rt_pci_device *pdev = RT_NULL; + + if (!bus) + { + return nr; + } + + if (devfn > 0 && only_one_child(bus)) + { + return nr; + } + + for (int func = 0; func >= 0; func = next_fn(bus, pdev, func)) + { + pdev = rt_pci_scan_single_device(bus, devfn + func); + + if (pdev) + { + ++nr; + + if (func > 0) + { + pdev->multi_function = RT_TRUE; + } + } + else if (func == 0) + { + break; + } + } + + return nr; +} + +rt_uint32_t rt_pci_scan_child_buses(struct rt_pci_bus *bus, rt_size_t buses) +{ + rt_uint32_t bus_no; + struct rt_pci_device *pdev = RT_NULL; + + if (!bus) + { + bus_no = RT_UINT32_MAX; + + goto _end; + } + + bus_no = bus->number; + + for (rt_uint32_t devfn = 0; + devfn < RT_PCI_DEVFN(RT_PCI_DEVICE_MAX - 1, RT_PCI_FUNCTION_MAX - 1); + devfn += RT_PCI_FUNCTION_MAX) + { + rt_pci_scan_slot(bus, devfn); + } + + rt_pci_foreach_bridge(pdev, bus) + { + int offset; + + bus_no = pci_scan_bridge_extend(bus, pdev, bus_no, buses, RT_TRUE); + offset = bus_no - bus->number; + + if (buses > offset) + { + buses -= offset; + } + else + { + break; + } + } + +_end: + return bus_no; +} + +rt_uint32_t rt_pci_scan_child_bus(struct rt_pci_bus *bus) +{ + return rt_pci_scan_child_buses(bus, 0); +} + +static struct rt_pci_bus *pci_alloc_bus(struct rt_pci_bus *parent) +{ + struct rt_pci_bus *bus = rt_calloc(1, sizeof(*bus)); + + if (!bus) + { + return RT_NULL; + } + + bus->parent = parent; + + rt_list_init(&bus->list); + rt_list_init(&bus->children_nodes); + rt_list_init(&bus->devices_nodes); + + rt_spin_lock_init(&bus->lock); + + return bus; +} + +rt_err_t rt_pci_host_bridge_register(struct rt_pci_host_bridge *host_bridge) +{ + struct rt_pci_bus *bus = pci_alloc_bus(RT_NULL); + + if (!bus) + { + return -RT_ENOMEM; + } + + host_bridge->root_bus = bus; + + bus->sysdata = host_bridge->sysdata; + bus->host_bridge = host_bridge; + bus->ops = host_bridge->ops; + + bus->number = host_bridge->bus_range[0]; + rt_sprintf(bus->name, "%04x:%02x", host_bridge->domain, bus->number); + + if (bus->ops->add) + { + rt_err_t err = bus->ops->add(bus); + + if (err) + { + LOG_E("PCI-Bus<%s> add bus failed with err = %s", bus->name, rt_strerror(err)); + } + } + + return RT_EOK; +} + +rt_err_t rt_pci_scan_root_bus_bridge(struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err; + + if ((err = rt_pci_host_bridge_register(host_bridge))) + { + return err; + } + + rt_pci_scan_child_bus(host_bridge->root_bus); + + return err; +} + +rt_err_t rt_pci_host_bridge_probe(struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err; + + err = rt_pci_scan_root_bus_bridge(host_bridge); + + return err; +} + +static rt_bool_t pci_remove_bus_device(struct rt_pci_device *pdev, void *data) +{ + /* Bus will free if this is the last device */ + rt_bus_remove_device(&pdev->parent); + + /* To find all devices, always return false */ + return RT_FALSE; +} + +rt_err_t rt_pci_host_bridge_remove(struct rt_pci_host_bridge *host_bridge) +{ + rt_err_t err = RT_EOK; + + if (host_bridge && host_bridge->root_bus) + { + rt_pci_enum_device(host_bridge->root_bus, pci_remove_bus_device, RT_NULL); + host_bridge->root_bus = RT_NULL; + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_bus_remove(struct rt_pci_bus *bus) +{ + rt_err_t err = RT_EOK; + + if (bus) + { + spin_lock(&bus->lock); + + if (rt_list_isempty(&bus->children_nodes) && + rt_list_isempty(&bus->devices_nodes)) + { + rt_list_remove(&bus->list); + spin_unlock(&bus->lock); + + if (bus->ops->remove) + { + bus->ops->remove(bus); + } + + rt_pci_ofw_bus_free(bus); + rt_free(bus); + } + else + { + spin_unlock(&bus->lock); + + err = -RT_EBUSY; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_pci_device_remove(struct rt_pci_device *pdev) +{ + rt_err_t err = RT_EOK; + + if (pdev) + { + struct rt_pci_bus *bus = pdev->bus; + + spin_lock(&bus->lock); + + while (pdev->parent.ref_count > 1) + { + spin_unlock(&bus->lock); + + rt_thread_yield(); + + spin_lock(&bus->lock); + } + rt_list_remove(&pdev->list); + + spin_unlock(&bus->lock); + + rt_free(pdev); + } + else + { + err = -RT_EINVAL; + } + + return err; +} diff --git a/rt-thread/components/drivers/phy/Kconfig b/rt-thread/components/drivers/phy/Kconfig index bb88a41..92dcba8 100644 --- a/rt-thread/components/drivers/phy/Kconfig +++ b/rt-thread/components/drivers/phy/Kconfig @@ -1,3 +1,8 @@ config RT_USING_PHY bool "Using ethernet phy device drivers" default n + +config RT_USING_PHY_V2 + bool "Using phy device and mii bus v2" + depends on !RT_USING_PHY + default n diff --git a/rt-thread/components/drivers/phy/SConscript b/rt-thread/components/drivers/phy/SConscript index 5b6effc..ba227c7 100644 --- a/rt-thread/components/drivers/phy/SConscript +++ b/rt-thread/components/drivers/phy/SConscript @@ -1,8 +1,18 @@ from building import * cwd = GetCurrentDir() +CPPPATH = [cwd, cwd + '/../include'] src = Glob('*.c') -CPPPATH = [cwd + '/../include'] -group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_PHY'], CPPPATH = CPPPATH) +if GetDepend('RT_USING_OFW') == False: + SrcRemove(src, ['ofw.c']) + +if GetDepend('RT_USING_PHY_V2') == False: + SrcRemove(src, ['general.c','mdio.c','ofw.c']) + +if GetDepend('RT_USING_PHY_V2') == False: + if GetDepend('RT_USING_PHY') == False: + SrcRemove(src, ['phy.c']) + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/rt-thread/components/drivers/phy/general.c b/rt-thread/components/drivers/phy/general.c new file mode 100644 index 0000000..969c38c --- /dev/null +++ b/rt-thread/components/drivers/phy/general.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-10-08 zhujiale the first version + */ +#include +#include +#include "general_phy.h" +#define DBG_TAG "rtdm.phy" +#define DBG_LVL DBG_INFO +#include + +static int __genphy_set_adv(int adv,int advertise) +{ + adv &= ~(RT_ADVERTISE_ALL | RT_ADVERTISE_100BASE4 | RT_ADVERTISE_PAUSE_CAP | + RT_ADVERTISE_PAUSE_ASYM); + if (advertise & RT_ADVERTISED__10baseT_Half) + adv |= RT_ADVERTISE_10HALF; + if (advertise & RT_ADVERTISED__10baseT_Full) + adv |= RT_ADVERTISE_10FULL; + if (advertise & RT_ADVERTISED__100baseT_Half) + adv |= RT_ADVERTISE_100HALF; + if (advertise & RT_ADVERTISED__100baseT_Full) + adv |= RT_ADVERTISE_100FULL; + if (advertise & RT_ADVERTISED__Pause) + adv |= RT_ADVERTISE_PAUSE_CAP; + if (advertise & RT_ADVERTISED__Asym_Pause) + adv |= RT_ADVERTISE_PAUSE_ASYM; + if (advertise & RT_ADVERTISED__1000baseX_Half) + adv |= RT_ADVERTISE_1000XHALF; + if (advertise & RT_ADVERTISED__1000baseX_Full) + adv |= RT_ADVERTISE_1000XFULL; + + return adv; +} +static int __genphy_config_advert(struct rt_phy_device *phydev) +{ + rt_uint32_t advertise; + int oldadv, adv, bmsr; + int err, changed = 0; + + phydev->advertising &= phydev->supported; + advertise = phydev->advertising; + + adv = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_ADVERTISE); + oldadv = adv; + + if (adv < 0) + return adv; + + adv = __genphy_set_adv(adv, advertise); + + if (adv != oldadv) + { + err = rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_ADVERTISE, adv); + + if (err < 0) + return err; + changed = 1; + } + + bmsr = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + if (bmsr < 0) + return bmsr; + + if (!(bmsr & RT_BMSR_ESTATEN)) + return changed; + + adv = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_CTRL1000); + oldadv = adv; + + if (adv < 0) + return adv; + + adv &= ~(RT_ADVERTISE_1000FULL | RT_ADVERTISE_1000HALF); + + if (phydev->supported & (RT_SUPPORTED_1000baseT_Half | + RT_SUPPORTED_1000baseT_Full)) + { + if (advertise & RT_SUPPORTED_1000baseT_Half) + adv |= RT_ADVERTISE_1000HALF; + if (advertise & RT_SUPPORTED_1000baseT_Full) + adv |= RT_ADVERTISE_1000FULL; + } + + if (adv != oldadv) + changed = 1; + + err = rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_CTRL1000, adv); + if (err < 0) + return err; + + return changed; +} + +int __genphy_restart_aneg(struct rt_phy_device *phydev) +{ + int ctl; + + ctl = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR); + + if (ctl < 0) + return ctl; + + ctl |= (RT_BMCR_ANENABLE | RT_BMCR_ANRESTART); + + ctl &= ~(RT_BMCR_ISOLATE); + + ctl = rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR, ctl); + + return ctl; +} + +int rt_genphy_config_aneg(struct rt_phy_device *phydev) +{ + int result; + int err; + int ctl = RT_BMCR_ANRESTART; + if (phydev->autoneg != AUTONEG_ENABLE) + { + phydev->pause = 0; + + if (phydev->speed == SPEED_1000) + ctl |= RT_BMCR_SPEED1000; + else if (phydev->speed == SPEED_100) + ctl |= RT_BMCR_SPEED100; + + if (phydev->duplex == DUPLEX_FULL) + ctl |= RT_BMCR_FULLDPLX; + + err = rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR, ctl); + + return err; + } + + result = __genphy_config_advert(phydev); + + if (result < 0) + return result; + + if (result == 0) + { + int ctl = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR); + + if (ctl < 0) + return ctl; + + if (!(ctl & RT_BMCR_ANENABLE) || (ctl & RT_BMCR_ISOLATE)) + result = 1; + } + + if (result > 0) + result = __genphy_restart_aneg(phydev); + + return result; +} + +int rt_genphy_update_link(struct rt_phy_device *phydev) +{ + unsigned int mii_reg; + + mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + + if (phydev->link && mii_reg & RT_BMSR_LSTATUS) + return 0; + + if ((phydev->autoneg == AUTONEG_ENABLE) && + !(mii_reg & RT_BMSR_ANEGCOMPLETE)) + { + int i = 0; + rt_kprintf("Waiting for PHY auto negotiation to complete"); + while (!(mii_reg & RT_BMSR_ANEGCOMPLETE)) + { + + if (i > (RT_PHY_ANEG_TIMEOUT)) + { + LOG_E(" TIMEOUT !\n"); + phydev->link = 0; + return -ETIMEDOUT; + } + + mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + + rt_thread_delay(100); + } + LOG_D(" done\n"); + phydev->link = 1; + } else { + mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + + if (mii_reg & RT_BMSR_LSTATUS) + phydev->link = 1; + else + phydev->link = 0; + } + + return 0; +} + +static void __genphy_auto_neg(struct rt_phy_device *phydev,int mii_reg) +{ + rt_uint32_t lpa = 0; + int gblpa = 0; + rt_uint32_t estatus = 0; + + if (phydev->supported & (RT_SUPPORTED_1000baseT_Full | + RT_SUPPORTED_1000baseT_Half)) + { + + gblpa = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_STAT1000); + if (gblpa < 0) + { + LOG_D("Could not read RT_MII_STAT1000. Ignoring gigabit capability\n"); + gblpa = 0; + } + gblpa &= rt_phy_read(phydev, + RT_MDIO_DEVAD_NONE, RT_MII_CTRL1000) << 2; + } + + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + if (gblpa & (RT_PHY_1000BTSR_1000FD | RT_PHY_1000BTSR_1000HD)) + { + phydev->speed = SPEED_1000; + + if (gblpa & RT_PHY_1000BTSR_1000FD) + phydev->duplex = DUPLEX_FULL; + + return ; + } + + lpa = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_ADVERTISE); + lpa &= rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_LPA); + + if (lpa & (RT_LINK_PARTNER__100FULL | RT_LINK_PARTNER__100HALF)) + { + phydev->speed = SPEED_100; + + if (lpa & RT_LINK_PARTNER__100FULL) + phydev->duplex = DUPLEX_FULL; + + } else if (lpa & RT_LINK_PARTNER__10FULL) + { + phydev->duplex = DUPLEX_FULL; + } + + if ((mii_reg & RT_BMSR_ESTATEN) && !(mii_reg & RT_BMSR_ERCAP)) + estatus = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, + RT_MII_ESTATUS); + + if (estatus & (RT_SUPORT_1000B_XFULL | RT_SUPORT_1000B_XHALF | + RT_SUPORT_1000B_TFULL | RT_SUPORT_1000B_THALF)) + { + phydev->speed = SPEED_1000; + if (estatus & (RT_SUPORT_1000B_XFULL | RT_SUPORT_1000B_TFULL)) + phydev->duplex = DUPLEX_FULL; + } +} +int rt_genphy_parse_link(struct rt_phy_device *phydev) +{ + int mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + + if (phydev->autoneg == AUTONEG_ENABLE) + { + __genphy_auto_neg(phydev, mii_reg); + } else { + rt_uint32_t bmcr = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR); + + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + if (bmcr & RT_BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; + + if (bmcr & RT_BMCR_SPEED1000) + phydev->speed = SPEED_1000; + else if (bmcr & RT_BMCR_SPEED100) + phydev->speed = SPEED_100; + } + + return 0; +} + +int rt_genphy_config(struct rt_phy_device *phydev) +{ + int val; + rt_uint32_t features; + + features = (RT_SUPPORTED_TP | RT_SUPPORTED_MII + | RT_SUPPORTED_AUI | RT_SUPPORTED_FIBRE | + RT_SUPPORTED_BNC); + + val = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + + if (val < 0) + return val; + + if (val & RT_BMSR_ANEGCAPABLE) + features |= RT_SUPPORTED_Autoneg; + + if (val & RT_BMSR_100FULL) + features |= RT_SUPPORTED_100baseT_Full; + if (val & RT_BMSR_100HALF) + features |= RT_SUPPORTED_100baseT_Half; + if (val & RT_BMSR_10FULL) + features |= RT_SUPPORTED_10baseT_Full; + if (val & RT_BMSR_10HALF) + features |= RT_SUPPORTED_10baseT_Half; + + if (val & RT_BMSR_ESTATEN) + { + val = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_ESTATUS); + + if (val < 0) + return val; + + if (val & RT_SUPORT_1000B_TFULL) + features |= RT_SUPPORTED_1000baseT_Full; + if (val & RT_SUPORT_1000B_THALF) + features |= RT_SUPPORTED_1000baseT_Half; + if (val & RT_SUPORT_1000B_XFULL) + features |= RT_SUPPORTED_1000baseX_Full; + if (val & RT_SUPORT_1000B_XHALF) + features |= RT_SUPPORTED_1000baseX_Half; + } + + phydev->supported &= features; + phydev->advertising &= features; + + rt_genphy_config_aneg(phydev); + + return 0; +} + +int rt_genphy_startup(struct rt_phy_device *phydev) +{ + int ret; + + ret = rt_genphy_update_link(phydev); + if (ret) + return ret; + + return rt_genphy_parse_link(phydev); +} diff --git a/rt-thread/components/drivers/phy/general_phy.h b/rt-thread/components/drivers/phy/general_phy.h new file mode 100644 index 0000000..37acb3e --- /dev/null +++ b/rt-thread/components/drivers/phy/general_phy.h @@ -0,0 +1,143 @@ + +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-10-08 zhujiale the first version + */ +#ifndef __PHY_GENERAL_H__ +#define __PHY_GENERAL_H__ +/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */ +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_2500 2500 +#define SPEED_10000 10000 +/* Advertisement control register. */ +#define RT_ADVERTISE_SLCT 0x001f /* Selector bits */ +#define RT_ADVERTISE_CSMA 0x0001 /* Only selector supported */ +#define RT_ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define RT_ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ +#define RT_ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define RT_ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */ +#define RT_ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define RT_ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */ +#define RT_ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define RT_ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ +#define RT_ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ +#define RT_ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ +#define RT_ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */ +#define RT_ADVERTISE_RESV 0x1000 /* Unused... */ +#define RT_ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ +#define RT_ADVERTISE_LPACK 0x4000 /* Ack link partners response */ +#define RT_ADVERTISE_NPAGE 0x8000 /* Next page bit */ + +#define RT_ADVERTISE_FULL (RT_ADVERTISE_100FULL | RT_ADVERTISE_10FULL | \ + RT_ADVERTISE_CSMA) +#define RT_ADVERTISE_ALL (RT_ADVERTISE_10HALF | RT_ADVERTISE_10FULL | \ + RT_ADVERTISE_100HALF | RT_ADVERTISE_100FULL) + +/* Indicates what features are advertised by the interface. */ +#define RT_ADVERTISED__10baseT_Half (1 << 0) +#define RT_ADVERTISED__10baseT_Full (1 << 1) +#define RT_ADVERTISED__100baseT_Half (1 << 2) +#define RT_ADVERTISED__100baseT_Full (1 << 3) +#define RT_ADVERTISED__1000baseT_Half (1 << 4) +#define RT_ADVERTISED__1000baseT_Full (1 << 5) +#define RT_ADVERTISED__Autoneg (1 << 6) +#define RT_ADVERTISED__TP (1 << 7) +#define RT_ADVERTISED__AUI (1 << 8) +#define RT_ADVERTISED__MII (1 << 9) +#define RT_ADVERTISED__FIBRE (1 << 10) +#define RT_ADVERTISED__BNC (1 << 11) +#define RT_ADVERTISED__10000baseT_Full (1 << 12) +#define RT_ADVERTISED__Pause (1 << 13) +#define RT_ADVERTISED__Asym_Pause (1 << 14) +#define RT_ADVERTISED__2500baseX_Full (1 << 15) +#define RT_ADVERTISED__Backplane (1 << 16) +#define RT_ADVERTISED__1000baseKX_Full (1 << 17) +#define RT_ADVERTISED__10000baseKX4_Full (1 << 18) +#define RT_ADVERTISED__10000baseKR_Full (1 << 19) +#define RT_ADVERTISED__10000baseR_FEC (1 << 20) +#define RT_ADVERTISED__1000baseX_Half (1 << 21) +#define RT_ADVERTISED__1000baseX_Full (1 << 22) + +/* Basic mode status register. */ +#define RT_BMSR_ERCAP 0x0001 /* Ext-reg capability */ +#define RT_BMSR_JCD 0x0002 /* Jabber detected */ +#define RT_BMSR_LSTATUS 0x0004 /* Link status */ +#define RT_BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ +#define RT_BMSR_RFAULT 0x0010 /* Remote fault detected */ +#define RT_BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ +#define RT_BMSR_RESV 0x00c0 /* Unused... */ +#define RT_BMSR_ESTATEN 0x0100 /* Extended Status in R15 */ +#define RT_BMSR_100HALF2 0x0200 /* Can do 100BASE-T2 HDX */ +#define RT_BMSR_100FULL2 0x0400 /* Can do 100BASE-T2 FDX */ +#define RT_BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ +#define RT_BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ +#define RT_BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ +#define RT_BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ +#define RT_BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */ +/* 1000BASE-T Control register */ +#define RT_ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ +#define RT_ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ +#define CTL1000_AS_MASTER 0x0800 +#define CTL1000_ENABLE_MASTER 0x1000 + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 + +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 +#define RT_PHY_1000BTSR_MSCF 0x8000 +#define RT_PHY_1000BTSR_MSCR 0x4000 +#define RT_PHY_1000BTSR_LRS 0x2000 +#define RT_PHY_1000BTSR_RRS 0x1000 +#define RT_PHY_1000BTSR_1000FD 0x0800 +#define RT_PHY_1000BTSR_1000HD 0x0400 + +/* Link partner ability register. */ +#define RT_LINK_PARTNER__SLCT 0x001f /* Same as advertise selector */ +#define RT_LINK_PARTNER__10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define RT_LINK_PARTNER__1000XFULL 0x0020 /* Can do 1000BASE-X full-duplex */ +#define RT_LINK_PARTNER__10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define RT_LINK_PARTNER__1000XHALF 0x0040 /* Can do 1000BASE-X half-duplex */ +#define RT_LINK_PARTNER__100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define RT_LINK_PARTNER__1000XPAUSE 0x0080 /* Can do 1000BASE-X pause */ +#define RT_LINK_PARTNER__100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define RT_LINK_PARTNER__1000XPAUSE_ASYM 0x0100 /* Can do 1000BASE-X pause asym*/ +#define RT_LINK_PARTNER__100BASE4 0x0200 /* Can do 100mbps 4k packets */ +#define RT_LINK_PARTNER__PAUSE_CAP 0x0400 /* Can pause */ +#define RT_LINK_PARTNER__PAUSE_ASYM 0x0800 /* Can pause asymetrically */ +#define RT_LINK_PARTNER__RESV 0x1000 /* Unused... */ +#define RT_LINK_PARTNER__RFAULT 0x2000 /* Link partner faulted */ +#define RT_LINK_PARTNER__LPACK 0x4000 /* Link partner acked us */ +#define RT_LINK_PARTNER__NPAGE 0x8000 /* Next page bit */ + +#define RT_LINK_PARTNER__DUPLEX (RT_LINK_PARTNER__10FULL | RT_LINK_PARTNER__100FULL) +#define RT_LINK_PARTNER__100 (RT_LINK_PARTNER__100FULL | RT_LINK_PARTNER__100HALF | RT_LINK_PARTNER__100BASE4) +/* Expansion register for auto-negotiation. */ +#define RT_EXPANSION_REG_NWAY 0x0001 /* Can do N-way auto-nego */ +#define RT_EXPANSION_REG_LCWP 0x0002 /* Got new RX page code word */ +#define RT_EXPANSION_REG_ENABLENPAGE 0x0004 /* This enables npage words */ +#define RT_EXPANSION_REG_NPCAPABLE 0x0008 /* Link partner supports npage */ +#define RT_EXPANSION_REG_MFAULTS 0x0010 /* Multiple faults detected */ +#define RT_EXPANSION_REG_RESV 0xffe0 /* Unused... */ + +#define RT_SUPORT_1000B_XFULL 0x8000 /* Can do 1000BX Full */ +#define RT_SUPORT_1000B_XHALF 0x4000 /* Can do 1000BX Half */ +#define RT_SUPORT_1000B_TFULL 0x2000 /* Can do 1000BT Full */ +#define RT_SUPORT_1000B_THALF 0x1000 /* Can do 1000BT Half */ +#define RT_PHY_ANEG_TIMEOUT 4000 +struct rt_phy_device; + +int rt_genphy_parse_link(struct rt_phy_device *phydev); +int rt_genphy_config_aneg(struct rt_phy_device *phydev); +int rt_genphy_update_link(struct rt_phy_device *phydev); +int rt_genphy_startup(struct rt_phy_device *phydev); +int rt_genphy_config(struct rt_phy_device *phydev); +#endif diff --git a/rt-thread/components/drivers/phy/mdio.c b/rt-thread/components/drivers/phy/mdio.c new file mode 100644 index 0000000..7f9be35 --- /dev/null +++ b/rt-thread/components/drivers/phy/mdio.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-10-08 zhujiale the first version + */ +#include "mdio.h" + +static struct rt_list_node mdio_list; + +struct mii_bus *rt_mdio_get_bus_by_name(const char *busname) +{ + struct rt_list_node *entry; + struct mii_bus *bus; + + if (!busname) + { + rt_kprintf("NULL bus name!\n"); + return RT_NULL; + } + + rt_list_for_each(entry, &mdio_list) + { + bus = rt_container_of(entry, struct mii_bus, node); + if (!strcmp(bus->name, busname)) + return bus; + } + + return RT_NULL; +} + +struct mii_bus *rt_mdio_alloc(void) +{ + struct mii_bus *mii; + mii = rt_malloc(sizeof(struct mii_bus)); + if (!mii) + return RT_NULL; + + rt_list_init(&mii->node); + return mii; +} + +rt_err_t rt_mdio_register(struct mii_bus *bus) +{ + if (!bus) + return -RT_ERROR; + + if(rt_mdio_get_bus_by_name(bus->name)) + { + rt_kprintf("mdio bus %s already exist!\n", bus->name); + return -RT_ERROR; + } + + rt_list_insert_before(&mdio_list, &bus->node); + return RT_EOK; +} + +rt_err_t rt_mdio_unregister(struct mii_bus *bus) +{ + if (!bus) + return -RT_ERROR; + + rt_list_remove(&bus->node); + return RT_EOK; +} + +static int mdio_init(void) +{ + rt_list_init(&mdio_list); + return 0; +} +INIT_CORE_EXPORT(mdio_init); diff --git a/rt-thread/components/drivers/phy/mdio.h b/rt-thread/components/drivers/phy/mdio.h new file mode 100644 index 0000000..89e98d4 --- /dev/null +++ b/rt-thread/components/drivers/phy/mdio.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-10-08 zhujiale the first version + */ +#ifndef __PHY_MDIO_H__ +#define __PHY_MDIO_H__ +#include +#include + +#define RT_MDIO_DEVAD_NONE (-1) +#define RT_MDIO_MMD_PMAPMD 1 /* Physical Medium Attachment/ + * Physical Medium Dependent */ +#define RT_MDIO_MMD_WIS 2 /* WAN Interface Sublayer */ +#define RT_MDIO_MMD_PCS 3 /* Physical Coding Sublayer */ +#define RT_MDIO_MMD_PHYXS 4 /* PHY Extender Sublayer */ +#define RT_MDIO_MMD_DTEXS 5 /* DTE Extender Sublayer */ +#define RT_MDIO_MMD_TC 6 /* Transmission Convergence */ +#define RT_MDIO_MMD_AN 7 /* Auto-Negotiation */ +#define RT_MDIO_MMD_C22EXT 29 /* Clause 22 extension */ +#define RT_MDIO_MMD_VEND1 30 /* Vendor specific 1 */ +#define RT_MDIO_MMD_VEND2 31 /* Vendor specific 2 */ + +#define RT_MII_BMCR 0x00 /* Basic mode control register */ +#define RT_MII_BMSR 0x01 /* Basic mode status register */ +#define RT_MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define RT_MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define RT_MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define RT_MII_LPA 0x05 /* Link partner ability reg */ +#define RT_MII_EXPANSION 0x06 /* Expansion register */ +#define RT_MII_CTRL1000 0x09 /* 1000BASE-T control */ +#define RT_MII_STAT1000 0x0a /* 1000BASE-T status */ +#define RT_MII_MMD_CTRL 0x0d /* MMD Access Control Register */ +#define RT_MII_MMD_DATA 0x0e /* MMD Access Data Register */ +#define RT_MII_ESTATUS 0x0f /* Extended Status */ +#define RT_MII_DCOUNTER 0x12 /* Disconnect counter */ +#define RT_MII_FCSCOUNTER 0x13 /* False carrier counter */ +#define RT_MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ +#define RT_MII_RERRCOUNTER 0x15 /* Receive error counter */ +#define RT_MII_SREVISION 0x16 /* Silicon revision */ +#define RT_MII_RESV1 0x17 /* Reserved... */ +#define RT_MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ +#define RT_MII_PHYADDR 0x19 /* PHY address */ +#define RT_MII_RESV2 0x1a /* Reserved... */ +#define RT_MII_TPISTATUS 0x1b /* TPI status for 10mbps */ +#define RT_MII_NCONFIG 0x1c /* Network interface config */ + +/* Basic mode control register. */ +#define RT_BMCR_RESV 0x003f /* Unused... */ +#define RT_BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */ +#define RT_BMCR_CTST 0x0080 /* Collision test */ +#define RT_BMCR_FULLDPLX 0x0100 /* Full duplex */ +#define RT_BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ +#define RT_BMCR_ISOLATE 0x0400 /* Isolate data paths from MII */ +#define RT_BMCR_PDOWN 0x0800 /* Enable low power state */ +#define RT_BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ +#define RT_BMCR_SPEED100 0x2000 /* Select 100Mbps */ +#define RT_BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ +#define RT_BMCR_RESET 0x8000 /* Reset to default state */ +#define RT_BMCR_SPEED10 0x0000 /* Select 10Mbps */ + +#define RT_MII_MMD_CTRL_DEVAD_MASK 0x1f /* Mask MMD DEVAD*/ +#define RT_MII_MMD_CTRL_ADDR 0x0000 /* Address */ +#define RT_MII_MMD_CTRL_NOINCR 0x4000 /* no post increment */ +#define RT_MII_MMD_CTRL_INCR_RDWT 0x8000 /* post increment on reads & writes */ +#define RT_MII_MMD_CTRL_INCR_ON_WT 0xC000 /* post increment on writes only */ + + +#define RT_PHY_MAX 32 + +struct mii_bus +{ + struct rt_list_node node; + char name[RT_NAME_MAX]; + void* priv; + int (*read)(struct mii_bus* bus, int addr, int devad, int reg); + int (*write)(struct mii_bus* bus, int addr, int devad, int reg, rt_uint16_t val); + /** @read_c45: Perform a C45 read transfer on the bus */ + int (*read_c45)(struct mii_bus* bus, int addr, int devad, int reg); + /** @write_c45: Perform a C45 write transfer on the bus */ + int (*write_c45)(struct mii_bus* bus, int addr, int devad, int reg, rt_uint16_t val); + int (*reset)(struct mii_bus* bus); + struct rt_phy_device* phymap[RT_PHY_MAX]; + rt_uint32_t phy_mask; + int reset_delay_us; + int reset_post_delay_us; +}; + +rt_err_t rt_mdio_register(struct mii_bus* bus); +rt_err_t rt_mdio_unregister(struct mii_bus* bus); + +struct mii_bus* rt_mdio_get_bus_by_name(const char* busname); +struct mii_bus* rt_mdio_alloc(void); +#endif diff --git a/rt-thread/components/drivers/phy/ofw.c b/rt-thread/components/drivers/phy/ofw.c new file mode 100644 index 0000000..316bbc2 --- /dev/null +++ b/rt-thread/components/drivers/phy/ofw.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-09-25 zhujiale the first version + */ +#include +#include +#include +#define DBG_TAG "rtdm.phy" +#define DBG_LVL DBG_INFO +#include +#include "ofw.h" + +static const char* const rt_phy_modes[] = +{ + [RT_PHY_INTERFACE_MODE_NA] = "", + [RT_PHY_INTERFACE_MODE_INTERNAL] = "internal", + [RT_PHY_INTERFACE_MODE_MII] = "mii", + [RT_PHY_INTERFACE_MODE_GMII] = "gmii", + [RT_PHY_INTERFACE_MODE_SGMII] = "sgmii", + [RT_PHY_INTERFACE_MODE_TBI] = "tbi", + [RT_PHY_INTERFACE_MODE_REVMII] = "rev-mii", + [RT_PHY_INTERFACE_MODE_RMII] = "rmii", + [RT_PHY_INTERFACE_MODE_REVRMII] = "rev-rmii", + [RT_PHY_INTERFACE_MODE_RGMII] = "rgmii", + [RT_PHY_INTERFACE_MODE_RGMII_ID] = "rgmii-id", + [RT_PHY_INTERFACE_MODE_RGMII_RXID] = "rgmii-rxid", + [RT_PHY_INTERFACE_MODE_RGMII_TXID] = "rgmii-txid", + [RT_PHY_INTERFACE_MODE_RTBI] = "rtbi", + [RT_PHY_INTERFACE_MODE_SMII] = "smii", + [RT_PHY_INTERFACE_MODE_XGMII] = "xgmii", + [RT_PHY_INTERFACE_MODE_XLGMII] = "xlgmii", + [RT_PHY_INTERFACE_MODE_MOCA] = "moca", + [RT_PHY_INTERFACE_MODE_PSGMII] = "psgmii", + [RT_PHY_INTERFACE_MODE_QSGMII] = "qsgmii", + [RT_PHY_INTERFACE_MODE_TRGMII] = "trgmii", + [RT_PHY_INTERFACE_MODE_1000BASEX] = "1000base-x", + [RT_PHY_INTERFACE_MODE_1000BASEKX] = "1000base-kx", + [RT_PHY_INTERFACE_MODE_2500BASEX] = "2500base-x", + [RT_PHY_INTERFACE_MODE_5GBASER] = "5gbase-r", + [RT_PHY_INTERFACE_MODE_RXAUI] = "rxaui", + [RT_PHY_INTERFACE_MODE_XAUI] = "xaui", + [RT_PHY_INTERFACE_MODE_10GBASER] = "10gbase-r", + [RT_PHY_INTERFACE_MODE_25GBASER] = "25gbase-r", + [RT_PHY_INTERFACE_MODE_USXGMII] = "usxgmii", + [RT_PHY_INTERFACE_MODE_10GKR] = "10gbase-kr", + [RT_PHY_INTERFACE_MODE_100BASEX] = "100base-x", + [RT_PHY_INTERFACE_MODE_QUSGMII] = "qusgmii", + [RT_PHY_INTERFACE_MODE_MAX] = "", +}; + +static rt_err_t _get_interface_by_name(const char *name, rt_phy_interface *interface) +{ + for (int i = 0; i < RT_PHY_INTERFACE_MODE_MAX; i++) + { + if (!strcmp(name, rt_phy_modes[i])) + { + *interface = i; + return RT_EOK; + } + } + return -RT_ERROR; +} + +rt_err_t rt_ofw_get_interface(struct rt_ofw_node *np, rt_phy_interface *interface) +{ + const char *phy_mode = RT_NULL; + + if (rt_ofw_prop_read_string(np, "phy-mode", &phy_mode)) + rt_ofw_prop_read_string(np, "phy-connection-type", &phy_mode); + if (!phy_mode) + return -RT_ERROR; + + return _get_interface_by_name(phy_mode, interface); +} + +rt_err_t rt_ofw_get_mac_addr_by_name(struct rt_ofw_node *np, const char *name, rt_uint8_t *addr) +{ + rt_ssize_t len; + const void *p; + p = rt_ofw_prop_read_raw(np, name, &len); + if (p) + { + rt_memcpy(addr, p, len); + return RT_EOK; + } + + return -RT_ERROR; +} + +rt_err_t rt_ofw_get_mac_addr(struct rt_ofw_node *np, rt_uint8_t *addr) +{ + if (!rt_ofw_get_mac_addr_by_name(np, "mac-address", addr)) + return RT_EOK; + + if (!rt_ofw_get_mac_addr_by_name(np, "local-mac-address", addr)) + return RT_EOK; + + if (!rt_ofw_get_mac_addr_by_name(np, "address", addr)) + return RT_EOK; + + return -RT_ERROR; +} + +rt_err_t rt_ofw_get_phyid(struct rt_ofw_node *np,rt_uint32_t *id) +{ + const char *phy_id; + unsigned int upper, lower; + int ret; + + ret = rt_ofw_prop_read_string(np,"compatible",&phy_id); + if (ret) + return ret; + + ret = rt_sscanf(phy_id,"ethernet-phy-id%4x.%4x",&upper, &lower); + if(ret != 2) + return -RT_ERROR; + + *id = ((upper & 0xffff) << 16) | (lower & 0xffff); + return RT_EOK; + +} +struct rt_phy_device *rt_ofw_create_phy(struct mii_bus *bus,struct rt_ofw_node *np,int phyaddr) +{ + struct rt_phy_device *dev = RT_NULL; + struct rt_ofw_node *phy_node; + int ret; + rt_uint32_t id = 0xffff; + + phy_node = rt_ofw_parse_phandle(np, "phy-handle", 0); + if (!phy_node) + { + LOG_D("Failed to find phy-handle"); + return RT_NULL; + } + + ret = rt_ofw_get_phyid(np, &id); + if (ret) + { + LOG_D("Failed to read eth PHY id, err: %d\n", ret); + return RT_NULL; + } + + LOG_D("Found a PHY id: 0x%x\n", id); + + dev = rt_phy_device_create(bus, phyaddr, id, RT_FALSE); + + if(dev) + dev->node = phy_node; + + return dev; +} diff --git a/rt-thread/components/drivers/phy/ofw.h b/rt-thread/components/drivers/phy/ofw.h new file mode 100644 index 0000000..d846aa4 --- /dev/null +++ b/rt-thread/components/drivers/phy/ofw.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-09-25 zhujiale the first version + */ +#ifndef __NET_OFW_H__ +#define __NET_OFW_H__ +#include +#include +typedef enum +{ + RT_PHY_INTERFACE_MODE_NA, + RT_PHY_INTERFACE_MODE_INTERNAL, + RT_PHY_INTERFACE_MODE_MII, + RT_PHY_INTERFACE_MODE_GMII, + RT_PHY_INTERFACE_MODE_SGMII, + RT_PHY_INTERFACE_MODE_TBI, + RT_PHY_INTERFACE_MODE_REVMII, + RT_PHY_INTERFACE_MODE_RMII, + RT_PHY_INTERFACE_MODE_REVRMII, + RT_PHY_INTERFACE_MODE_RGMII, + RT_PHY_INTERFACE_MODE_RGMII_ID, + RT_PHY_INTERFACE_MODE_RGMII_RXID, + RT_PHY_INTERFACE_MODE_RGMII_TXID, + RT_PHY_INTERFACE_MODE_RTBI, + RT_PHY_INTERFACE_MODE_SMII, + RT_PHY_INTERFACE_MODE_XGMII, + RT_PHY_INTERFACE_MODE_XLGMII, + RT_PHY_INTERFACE_MODE_MOCA, + RT_PHY_INTERFACE_MODE_PSGMII, + RT_PHY_INTERFACE_MODE_QSGMII, + RT_PHY_INTERFACE_MODE_TRGMII, + RT_PHY_INTERFACE_MODE_100BASEX, + RT_PHY_INTERFACE_MODE_1000BASEX, + RT_PHY_INTERFACE_MODE_2500BASEX, + RT_PHY_INTERFACE_MODE_5GBASER, + RT_PHY_INTERFACE_MODE_RXAUI, + RT_PHY_INTERFACE_MODE_XAUI, + /* 10GBASE-R, XFI, SFI - single lane 10G Serdes */ + RT_PHY_INTERFACE_MODE_10GBASER, + RT_PHY_INTERFACE_MODE_25GBASER, + RT_PHY_INTERFACE_MODE_USXGMII, + /* 10GBASE-KR - with Clause 73 AN */ + RT_PHY_INTERFACE_MODE_10GKR, + RT_PHY_INTERFACE_MODE_QUSGMII, + RT_PHY_INTERFACE_MODE_1000BASEKX, + RT_PHY_INTERFACE_MODE_MAX, +} rt_phy_interface; + +rt_err_t rt_ofw_get_mac_addr(struct rt_ofw_node *np, rt_uint8_t *addr); +rt_err_t rt_ofw_get_mac_addr_by_name(struct rt_ofw_node *np, const char *name, rt_uint8_t *addr); +rt_err_t rt_ofw_get_interface(struct rt_ofw_node *np, rt_phy_interface *interface); + +#endif diff --git a/rt-thread/components/drivers/phy/phy.c b/rt-thread/components/drivers/phy/phy.c index 363b483..47c35c2 100644 --- a/rt-thread/components/drivers/phy/phy.c +++ b/rt-thread/components/drivers/phy/phy.c @@ -1,22 +1,21 @@ - /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: - * Date Author Notes - * 2020-09-27 wangqiang first version + * Date Author Notes + * 2020-09-27 wangqiang first version + * 2024-10-08 zhujiale add phy v2.0 */ #include #include -#include #include - -#define DBG_TAG "PHY" +#define DBG_TAG "rtdm.phy" #define DBG_LVL DBG_INFO #include +#ifdef RT_USING_PHY static rt_ssize_t phy_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t count) { @@ -74,3 +73,503 @@ rt_err_t rt_hw_phy_register(struct rt_phy_device *phy, const char *name) return ret; } +#endif + +#ifdef RT_USING_PHY_V2 +int rt_phy_set_supported(struct rt_phy_device *phydev, rt_uint32_t max_speed) +{ + phydev->supported &= RT_PHY_DEFAULT_FEATURES; + + switch (max_speed) + { + default: + return -ENOTSUP; + case SPEED_1000: + phydev->supported |= RT_PHY_1000BT_FEATURES; + case SPEED_100: + phydev->supported |= RT_PHY_100BT_FEATURES; + case SPEED_10: + phydev->supported |= RT_PHY_10BT_FEATURES; + } + + return 0; +} + +int rt_phy_read(struct rt_phy_device *phydev, int devad, int regnum) +{ + struct mii_bus *bus = phydev->bus; + if(phydev->is_c45) + { + if(bus->read_c45) + return bus->read_c45(bus, phydev->addr, devad, regnum); + } + + if( bus && bus->read ) + return bus->read(bus, phydev->addr, devad, regnum); + + LOG_D("no read function\n"); + return -1; +} + +int rt_phy_write(struct rt_phy_device *phydev, int devad, int regnum, rt_uint16_t val) +{ + struct mii_bus *bus = phydev->bus; + if(phydev->is_c45) + { + if(bus->write_c45) + return bus->write_c45(bus, phydev->addr, devad, regnum, val); + } + if( bus && bus->write ) + return bus->write(bus, phydev->addr, devad, regnum, val); + + LOG_D("no write function\n"); + return -1; +} + +int rt_phy_startup(struct rt_phy_device *phydev) +{ + if(!phydev->drv) + { + LOG_D("PHY device hace no driver\n"); + return -1; + } + + if (phydev->drv->startup) + return phydev->drv->startup(phydev); + + LOG_D("phy startup err\n"); + return -1; +} + +int rt_phy_config(struct rt_phy_device *phydev) +{ + if(!phydev->drv) + { + LOG_D("PHY device hace no driver\n"); + return -1; + } + + if (phydev->drv->config) + return phydev->drv->config(phydev); + + LOG_D("no config function\n"); + return -1; +} + +int rt_phy_shutdown(struct rt_phy_device *phydev) +{ + if(!phydev->drv) + { + LOG_D("PHY device hace no driver\n"); + return -1; + } + + if (phydev->drv->shutdown) + phydev->drv->shutdown(phydev); + + LOG_D("no shutdown function\n"); + return -1; +} + +void rt_phy_mmd_start_indirect(struct rt_phy_device *phydev, int devad, int regnum) +{ + /* Write the desired MMD Devad */ + rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_MMD_CTRL, devad); + + /* Write the desired MMD register address */ + rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_MMD_DATA, regnum); + + /* Select the Function : DATA with no post increment */ + rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_MMD_CTRL, + (devad | RT_MII_MMD_CTRL_NOINCR)); +} + +int rt_phy_read_mmd(struct rt_phy_device *phydev, int devad, int regnum) +{ + struct rt_phy_driver *drv = phydev->drv; + + if (regnum > (rt_uint16_t)~0 || devad > 32 || !drv) + return -EINVAL; + + if (drv->read_mmd) + return drv->read_mmd(phydev, devad, regnum); + + if ((drv->features & RT_PHY_10G_FEATURES) == RT_PHY_10G_FEATURES || + devad == RT_MDIO_DEVAD_NONE || !devad) + return rt_phy_read(phydev, devad, regnum); + + rt_phy_mmd_start_indirect(phydev, devad, regnum); + + return rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_MMD_DATA); +} + +int rt_phy_write_mmd(struct rt_phy_device *phydev, int devad, int regnum, rt_uint16_t val) +{ + struct rt_phy_driver *drv = phydev->drv; + + if (regnum > (rt_uint16_t)~0 || devad > 32 || !drv) + return -EINVAL; + + if (drv->write_mmd) + return drv->write_mmd(phydev, devad, regnum, val); + + if ((drv->features & RT_PHY_10G_FEATURES) == RT_PHY_10G_FEATURES || + devad == RT_MDIO_DEVAD_NONE || !devad) + return rt_phy_write(phydev, devad, regnum, val); + + rt_phy_mmd_start_indirect(phydev, devad, regnum); + + return rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_MMD_DATA, val); +} + +int rt_phy_reset(struct rt_phy_device *phydev) +{ + int reg; + int timeout = 500; + int devad = RT_MDIO_DEVAD_NONE; + + if (phydev->flags & RT_PHY_FLAG_BROKEN_RESET) + return 0; + + if (rt_phy_write(phydev, devad, RT_MII_BMCR, RT_BMCR_RESET) < 0) + { + LOG_D("PHY reset failed\n"); + return -1; + } + + reg = rt_phy_read(phydev, devad, RT_MII_BMCR); + while ((reg & RT_BMCR_RESET) && timeout--) + { + reg = rt_phy_read(phydev, devad, RT_MII_BMCR); + + if (reg < 0) + { + LOG_D("PHY status read failed\n"); + return -1; + } + rt_thread_mdelay(1); + } + + if (reg & RT_BMCR_RESET) + { + LOG_D("PHY reset timed out\n"); + return -1; + } + + return 0; +} + +static struct rt_bus rt_phy_bus; + +/** + * @brief create a phy device + * + * Creates a new PHY device based on the given bus, address, PHY ID, and whether Clause 45 is supported. + * + * @param bus the pointer to the bus + * @param addr PHY device address + * @param phy_id PHY device id + * @param is_c45 if suport Clause 45 + * + * @return if create success return the phy device pointer,else return RT_NULL + */ +struct rt_phy_device *rt_phy_device_create(struct mii_bus *bus, int addr, + rt_uint32_t phy_id, rt_bool_t is_c45) +{ + struct rt_phy_device *dev = rt_malloc(sizeof(struct rt_phy_device)); + + if (!dev) + { + LOG_E("Failed to allocate PHY device for %s:%d\n", + bus ? bus->name : "(null bus)", addr); + return RT_NULL; + } + + memset(dev, 0, sizeof(*dev)); + + dev->duplex = -1; + dev->link = 0; + dev->interface = RT_PHY_INTERFACE_MODE_NA; +#ifdef RT_USING_OFW + dev->node = RT_NULL; +#endif + dev->autoneg = RT_TRUE; + + dev->addr = addr; + dev->phy_id = phy_id; + dev->is_c45 = is_c45; + dev->bus = bus; + + if(rt_phy_device_register(dev)) + { + LOG_D("register phy device filed") + } + + if (addr >= 0 && addr < RT_PHY_MAX && phy_id != RT_PHY_FIXED_ID && + phy_id != RT_PHY_NCSI_ID) + bus->phymap[addr] = dev; + + return dev; +} + +/** + * @brief get phy id + * + * get phy id by read the register 2 and 3 of PHY device, + * Register of the MII management interface stipulates that + * register 2 contains thehigh 16 bits of the PHY’s identifier + * the register 3 contains the low 16 bits of the PHY’s identifier + * + * @param bus MII bus pointer + * @param addr PHY device address + * @param devad dev addr if be set to zero it means c22 mode,else it means c45 mode + * @param phy_id the phy id which will be read + * + * @return if read success return 0,else return -RT_EIO + */ +static int get_phy_id(struct mii_bus *bus, int addr, int devad, rt_uint32_t *phy_id) +{ + int phy_reg; + + phy_reg = bus->read(bus, addr, devad, RT_MII_PHYSID1); + + if (phy_reg < 0) + return -RT_EIO; + + *phy_id = (phy_reg & 0xffff) << 16; + + phy_reg = bus->read(bus, addr, devad, RT_MII_PHYSID2); + + if (phy_reg < 0) + return -RT_EIO; + + *phy_id |= (phy_reg & 0xffff); + + return 0; +} + +/** + * @brief create phy device by mask + * + * @param bus MII bus pointer + * @param phy_mask the mask which phy addr corresponding will be set 1 + * @param devad dev addr if be set to zero it means c22 mode,else it means c45 mode + * + * @return if create success return the phy device pointer,if create fail return NULL + */ +static struct rt_phy_device *create_phy_by_mask(struct mii_bus *bus, unsigned int phy_mask,int devad) +{ + rt_uint32_t id = 0xffffffff; + rt_bool_t is_c45; + + while (phy_mask) + { + int addr = __rt_ffs(phy_mask) - 1; + int r = get_phy_id(bus, addr, devad, &id); + + if (r == 0 && id == 0) + { + phy_mask &= ~(1 << addr); + continue; + } + + if (r == 0 && (id & 0x1fffffff) != 0x1fffffff) + { + is_c45 = (devad == RT_MDIO_DEVAD_NONE) ? RT_FALSE : RT_TRUE; + return rt_phy_device_create(bus, addr, id, is_c45); + } + + } + return RT_NULL; +} + +/** + * @brief create phy device by mask + * + * it will create phy device by c22 mode or c45 mode + * + * @param bus mii bus pointer + * @param phy_mask PHY mask it depend on the phy addr, the phy addr corresponding will be set 1 + * + * @return if create success return the phy device pointer,if create fail return NULL + */ +static struct rt_phy_device *rt_phydev_create_by_mask(struct mii_bus *bus, unsigned int phy_mask) +{ + struct rt_phy_device *phy; + /* + *The bit of devad is dev addr which is the new features in c45 + *so if devad equal to zero it means it is c22 mode ,and if not + *equal to zero it means it is c45 mode,which include PMD/PMA , + *WIS ,PCS,PHY XS,PHY XS .... + */ + int devad[] = { + /* Clause-22 */ + RT_MDIO_DEVAD_NONE, + /* Clause-45 */ + RT_MDIO_MMD_PMAPMD, + RT_MDIO_MMD_WIS, + RT_MDIO_MMD_PCS, + RT_MDIO_MMD_PHYXS, + RT_MDIO_MMD_VEND1, + }; + + for (int i = 0; i < sizeof(devad)/sizeof(devad[0]); i++) + { + phy = create_phy_by_mask(bus, phy_mask, devad[i]); + if(phy) + return phy; + } + + return RT_NULL; +} + +struct rt_phy_device *rt_phy_find_by_mask(struct mii_bus *bus, unsigned int phy_mask) +{ + struct rt_phy_device *phy; + unsigned int mask = phy_mask; + unsigned int addr; + if (bus->reset) + { + bus->reset(bus); + + rt_thread_mdelay(15); + } + + while (mask) + { + /* + *Whichever bit of the mask is the 1, + *which bit is the addr as the array subscript to search + *such as mask = 1110 ,this loop will search for subscript + *1,2,3,if the array subscript is not null then return it, + *if the array subscript is null then continue to search + */ + addr = __rt_ffs(mask) - 1; + + if (bus->phymap[addr]) + return bus->phymap[addr]; + + mask &= ~(1U << addr); + } + /*ifcan't find phy device, create a new phy device*/ + phy = rt_phydev_create_by_mask(bus, phy_mask); + + return phy; + +} + +/** + * @brief get phy device by given mii bus, node and address + * @param bus MII bus pointer + * @param np the dtb node of device which need to get phy device + * @param addr address of phy device + * @param interface interface of phy device + * + * @return phy device pointer or NULL if not found + */ +struct rt_phy_device *rt_phy_get_device(struct mii_bus *bus,struct rt_ofw_node *np, int addr,rt_phy_interface interface) +{ + struct rt_phy_device *phy = RT_NULL; + unsigned int phy_mask = addr? 1 << addr:0xffffffff; + +#ifdef RT_USING_OFW + if(np) + phy = rt_ofw_create_phy(bus,np,addr); +#endif + if(!phy) + phy = rt_phy_find_by_mask(bus,phy_mask); + + if(phy) + { + rt_phy_reset(phy); + phy->interface = interface; + return phy; + } + + LOG_D("PHY device get failed"); + return RT_NULL; +} + +static struct rt_phy_driver genphy = { + .uid = 0xffffffff, + .mask = 0xffffffff, + .name = "Generic PHY", + .features = RT_PHY_GBIT_FEATURES | RT_SUPPORTED_MII | + RT_SUPPORTED_AUI | RT_SUPPORTED_FIBRE | + RT_SUPPORTED_BNC, + .config = rt_genphy_config, + .startup = rt_genphy_startup, +}; +RT_PHY_DRIVER_REGISTER(genphy); + +rt_err_t rt_phy_device_register(struct rt_phy_device *pdev) +{ + rt_err_t err; + RT_ASSERT(pdev != RT_NULL); + err = rt_bus_add_device(&rt_phy_bus, &pdev->parent); + if (err) + { + return err; + } + if(!pdev->drv) + pdev->drv = &genphy; + + return RT_EOK; +} + +rt_err_t rt_phy_driver_register(struct rt_phy_driver *pdrv) +{ + RT_ASSERT(pdrv != RT_NULL); + + pdrv->parent.bus = &rt_phy_bus; +#if RT_NAME_MAX > 0 + rt_strcpy(pdrv->parent.parent.name, pdrv->name); +#else + pdrv->parent.parent.name = pdrv->name; +#endif + + return rt_driver_register(&pdrv->parent); +} + +static rt_bool_t phy_match(rt_driver_t drv, rt_device_t dev) +{ + struct rt_phy_device *pdev = rt_container_of(dev, struct rt_phy_device, parent); + struct rt_phy_driver *pdrv = rt_container_of(drv, struct rt_phy_driver, parent); + if ((pdrv->uid & pdrv->mask) == (pdev->phy_id & pdrv->mask)) + return RT_TRUE; + + return RT_FALSE; + +} + +static rt_err_t phy_probe(rt_device_t dev) +{ + rt_err_t err = RT_EOK; + struct rt_phy_driver *pdrv = rt_container_of(dev->drv, struct rt_phy_driver, parent); + struct rt_phy_device *pdev = rt_container_of(dev, struct rt_phy_device, parent); + pdev->drv = pdrv; + pdev->advertising = pdev->drv->features; + pdev->supported = pdev->drv->features; + + pdev->mmds = pdev->drv->mmds; + if(pdrv->probe) + err = pdrv->probe(pdev); + + return err; +} + +static struct rt_bus rt_phy_bus = +{ + .name = "phy", + .match = phy_match, + .probe = phy_probe, +}; + +static int rt_phy_bus_init(void) +{ + rt_bus_register(&rt_phy_bus); + + return 0; +} +INIT_CORE_EXPORT(rt_phy_bus_init); +#endif diff --git a/rt-thread/components/drivers/phye/Kconfig b/rt-thread/components/drivers/phye/Kconfig new file mode 100644 index 0000000..c6672ee --- /dev/null +++ b/rt-thread/components/drivers/phye/Kconfig @@ -0,0 +1,11 @@ +menuconfig RT_USING_PHYE + bool "Using External Port Physical Layer (PHY) device drivers" + depends on RT_USING_DM + default n + help + This framework will be of use only to devices that use + external PHY (PHY functionality is not embedded within the controller). + +if RT_USING_PHYE + osource "$(SOC_DM_PHYE_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/phye/SConscript b/rt-thread/components/drivers/phye/SConscript new file mode 100644 index 0000000..23deafe --- /dev/null +++ b/rt-thread/components/drivers/phye/SConscript @@ -0,0 +1,15 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_PHYE']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['phye.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/phye/phye.c b/rt-thread/components/drivers/phye/phye.c new file mode 100644 index 0000000..7dc6709 --- /dev/null +++ b/rt-thread/components/drivers/phye/phye.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 GuEe-GUI first version + */ + +#define DBG_TAG "rtdm.phye" +#define DBG_LVL DBG_INFO +#include + +#include +#include + +rt_err_t rt_phye_register(struct rt_phye *phye) +{ + rt_err_t err; + + if (phye && phye->dev && phye->ops) + { + err = RT_EOK; + + rt_spin_lock_init(&phye->lock); + rt_dm_dev_bind_fwdata(phye->dev, RT_NULL, phye); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_phye_unregister(struct rt_phye *phye) +{ + rt_err_t err; + + if (phye) + { + err = RT_EOK; + + rt_spin_lock(&phye->lock); + + if (phye->dev->ref_count) + { + err = -RT_EBUSY; + LOG_E("%s is busy in unregister", rt_dm_dev_get_name(phye->dev)); + } + + rt_dm_dev_unbind_fwdata(phye->dev, RT_NULL); + + rt_spin_unlock(&phye->lock); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t rt_phye_init(struct rt_phye *phye) +{ + rt_err_t err; + + if (!phye) + { + return RT_EOK; + } + + err = RT_EOK; + + rt_spin_lock(&phye->lock); + + if (phye->init_count == 0 && phye->ops->init) + { + if ((err = phye->ops->init(phye))) + { + goto _out_lock; + } + } + ++phye->init_count; + +_out_lock: + rt_spin_unlock(&phye->lock); + + return err; +} + +rt_err_t rt_phye_exit(struct rt_phye *phye) +{ + rt_err_t err; + + if (!phye) + { + return RT_EOK; + } + + err = RT_EOK; + + rt_spin_lock(&phye->lock); + + if (phye->init_count == 1 && phye->ops->exit) + { + if ((err = phye->ops->exit(phye))) + { + goto _out_lock; + } + } + if (phye->init_count) + { + --phye->init_count; + } + +_out_lock: + rt_spin_unlock(&phye->lock); + + return err; +} + +rt_err_t rt_phye_reset(struct rt_phye *phye) +{ + rt_err_t err; + + if (!phye) + { + return RT_EOK; + } + + err = RT_EOK; + + rt_spin_lock(&phye->lock); + + if (phye->ops->reset) + { + err = phye->ops->reset(phye); + } + + rt_spin_unlock(&phye->lock); + + return err; +} + +rt_err_t rt_phye_power_on(struct rt_phye *phye) +{ + rt_err_t err; + + if (!phye) + { + return RT_EOK; + } + + err = RT_EOK; + + rt_spin_lock(&phye->lock); + + if (phye->power_count == 0 && phye->ops->power_on) + { + if ((err = phye->ops->power_on(phye))) + { + goto _out_lock; + } + } + ++phye->power_count; + +_out_lock: + rt_spin_unlock(&phye->lock); + + return err; +} + +rt_err_t rt_phye_power_off(struct rt_phye *phye) +{ + rt_err_t err; + + if (!phye) + { + return RT_EOK; + } + + err = RT_EOK; + + rt_spin_lock(&phye->lock); + + if (phye->power_count == 1 && phye->ops->power_off) + { + if ((err = phye->ops->power_off(phye))) + { + goto _out_lock; + } + } + if (phye->power_count) + { + --phye->power_count; + } + +_out_lock: + rt_spin_unlock(&phye->lock); + + return err; +} + +rt_err_t rt_phye_set_mode(struct rt_phye *phye, enum rt_phye_mode mode, int submode) +{ + rt_err_t err; + + if (!phye) + { + return RT_EOK; + } + + if (mode < RT_PHYE_MODE_MAX && + (submode == RT_PHYE_MODE_INVALID || submode >= RT_PHYE_MODE_MAX)) + { + err = RT_EOK; + + rt_spin_lock(&phye->lock); + + if (phye->ops->set_mode) + { + err = phye->ops->set_mode(phye, mode, submode); + } + + rt_spin_unlock(&phye->lock); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +static struct rt_phye *ofw_phye_get_by_index(struct rt_ofw_node *np, int index) +{ + struct rt_phye *phye = RT_NULL; +#ifdef RT_USING_OFW + rt_err_t err; + struct rt_ofw_node *phye_np; + struct rt_ofw_cell_args phye_args; + + if (!rt_ofw_parse_phandle_cells(np, "phys", "#phy-cells", index, &phye_args)) + { + phye_np = phye_args.data; + + if (!rt_ofw_data(phye_np)) + { + rt_platform_ofw_request(phye_np); + } + + phye = rt_ofw_data(phye_np); + rt_ofw_node_put(phye_np); + + if (phye && phye->ops->ofw_parse) + { + if ((err = phye->ops->ofw_parse(phye, &phye_args))) + { + phye = rt_err_ptr(err); + } + } + } +#endif /* RT_USING_OFW */ + return phye; +} + +struct rt_phye *rt_phye_get_by_index(struct rt_device *dev, int index) +{ + struct rt_phye *phye = RT_NULL; + + if (!dev || index < 0) + { + return rt_err_ptr(-RT_EINVAL); + } + + if (dev->ofw_node) + { + phye = ofw_phye_get_by_index(dev->ofw_node, index); + } + + if (!rt_is_err_or_null(phye)) + { + rt_spin_lock(&phye->lock); + ++phye->dev->ref_count; + rt_spin_unlock(&phye->lock); + } + + return phye; +} + +struct rt_phye *rt_phye_get_by_name(struct rt_device *dev, const char *id) +{ + int index; + + if (!dev || !id) + { + return rt_err_ptr(-RT_EINVAL); + } + + index = rt_dm_dev_prop_index_of_string(dev, "phy-names", id); + + if (index >= 0) + { + return rt_phye_get_by_index(dev, index); + } + + return RT_NULL; +} + +void rt_phye_put(struct rt_phye *phye) +{ + if (phye) + { + rt_spin_lock(&phye->lock); + --phye->dev->ref_count; + rt_spin_unlock(&phye->lock); + } +} diff --git a/rt-thread/components/drivers/pic/Kconfig b/rt-thread/components/drivers/pic/Kconfig index 16c1410..3cf00c0 100644 --- a/rt-thread/components/drivers/pic/Kconfig +++ b/rt-thread/components/drivers/pic/Kconfig @@ -1,12 +1,14 @@ menuconfig RT_USING_PIC bool "Using Programmable Interrupt Controller (PIC)" - select RT_USING_BITMAP + select RT_USING_ADT + select RT_USING_ADT_BITMAP depends on RT_USING_DM default n config RT_USING_PIC_STATISTICS bool "Enable ISR execution time statistics" depends on RT_USING_PIC + depends on RT_USING_KTIME depends on RT_USING_INTERRUPT_INFO default n @@ -22,12 +24,31 @@ config RT_PIC_ARM_GIC select RT_USING_OFW default n +config RT_PIC_ARM_GIC_V2M + bool "ARM GIC V2M" if RT_PIC_ARM_GIC && RT_PCI_MSI + depends on RT_USING_OFW + default n + config RT_PIC_ARM_GIC_V3 bool "ARM GICv3" depends on RT_USING_PIC select RT_USING_OFW default n +config RT_PIC_ARM_GIC_V3_ITS + bool "ARM GICv3 ITS (Interrupt Translation Service)" if RT_PIC_ARM_GIC_V3 && RT_PCI_MSI + depends on RT_USING_OFW + select RT_USING_ADT_REF + default n + +config RT_PIC_ARM_GIC_V3_ITS_IRQ_MAX + int "IRQ maximum used" + depends on RT_PIC_ARM_GIC_V3_ITS + default 127 if ARCH_CPU_64BIT + default 63 + help + Recommended to be based on the bit length (full bits) of maximum usage. + config RT_PIC_ARM_GIC_MAX_NR int depends on RT_USING_PIC diff --git a/rt-thread/components/drivers/pic/SConscript b/rt-thread/components/drivers/pic/SConscript index e820792..53cb66e 100644 --- a/rt-thread/components/drivers/pic/SConscript +++ b/rt-thread/components/drivers/pic/SConscript @@ -16,9 +16,15 @@ if GetDepend(['RT_PIC_ARM_GIC']) or GetDepend(['RT_PIC_ARM_GIC_V3']): if GetDepend(['RT_PIC_ARM_GIC']): src += ['pic-gicv2.c'] +if GetDepend(['RT_PIC_ARM_GIC_V2M']): + src += ['pic-gicv2m.c'] + if GetDepend(['RT_PIC_ARM_GIC_V3']): src += ['pic-gicv3.c'] +if GetDepend(['RT_PIC_ARM_GIC_V3_ITS']): + src += ['pic-gicv3-its.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/rt-thread/components/drivers/pic/pic-gic-common.c b/rt-thread/components/drivers/pic/pic-gic-common.c index 07e5f0b..5c6dc03 100644 --- a/rt-thread/components/drivers/pic/pic-gic-common.c +++ b/rt-thread/components/drivers/pic/pic-gic-common.c @@ -71,8 +71,9 @@ void gic_common_sgi_config(void *base, void *data, int irq_base) pirq = rt_pic_find_ipi(data, ipi); \ pirq->mode = RT_IRQ_MODE_EDGE_RISING; \ - DECLARE_GIC_IPI(RT_SCHEDULE_IPI, 0); - DECLARE_GIC_IPI(RT_STOP_IPI, 1); + DECLARE_GIC_IPI(RT_SCHEDULE_IPI, RT_SCHEDULE_IPI); + DECLARE_GIC_IPI(RT_STOP_IPI, RT_STOP_IPI); + DECLARE_GIC_IPI(RT_SMP_CALL_IPI, RT_SMP_CALL_IPI); #undef DECLARE_GIC_IPI } diff --git a/rt-thread/components/drivers/pic/pic-gic-common.h b/rt-thread/components/drivers/pic/pic-gic-common.h index 46841e8..d78e068 100644 --- a/rt-thread/components/drivers/pic/pic-gic-common.h +++ b/rt-thread/components/drivers/pic/pic-gic-common.h @@ -8,10 +8,14 @@ * 2023-01-30 GuEe-GUI first version */ -#ifndef __IRQ_GIC_COMMON_H__ -#define __IRQ_GIC_COMMON_H__ +#ifndef __PIC_GIC_COMMON_H__ +#define __PIC_GIC_COMMON_H__ #include + +#ifdef RT_PCI_MSI +#include +#endif #include #define GIC_SGI_NR 16 @@ -52,4 +56,4 @@ rt_err_t gicv2m_ofw_probe(struct rt_ofw_node *ic_np, const struct rt_ofw_node_id rt_err_t gicv3_its_ofw_probe(struct rt_ofw_node *ic_np, const struct rt_ofw_node_id *id); #endif -#endif /* __IRQ_GIC_COMMON_H__ */ +#endif /* __PIC_GIC_COMMON_H__ */ diff --git a/rt-thread/components/drivers/pic/pic-gicv2.c b/rt-thread/components/drivers/pic/pic-gicv2.c index adce046..dbfd443 100644 --- a/rt-thread/components/drivers/pic/pic-gicv2.c +++ b/rt-thread/components/drivers/pic/pic-gicv2.c @@ -83,6 +83,11 @@ static void gicv2_dist_init(struct gicv2 *gic) LOG_D("Max irq = %d", gic->max_irq); + if (gic->skip_init) + { + return; + } + HWREG32(base + GIC_DIST_CTRL) = GICD_DISABLE; /* Set all global (unused) interrupts to this CPU only. */ @@ -128,6 +133,8 @@ static void gicv2_cpu_init(struct gicv2 *gic) #ifdef ARCH_SUPPORT_HYP _gicv2_eoi_mode_ns = RT_TRUE; +#else + _gicv2_eoi_mode_ns = !!rt_ofw_bootargs_select("pic.gicv2_eoimode", 0); #endif if (_gicv2_eoi_mode_ns) @@ -618,6 +625,8 @@ static rt_err_t gicv2_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_ break; } + gic->skip_init = rt_ofw_prop_read_bool(np, "skip-init"); + gic_common_init_quirk_ofw(np, _gicv2_quirks, gic); gicv2_init(gic); diff --git a/rt-thread/components/drivers/pic/pic-gicv2.h b/rt-thread/components/drivers/pic/pic-gicv2.h index 69f9651..5fa9f2a 100644 --- a/rt-thread/components/drivers/pic/pic-gicv2.h +++ b/rt-thread/components/drivers/pic/pic-gicv2.h @@ -78,6 +78,8 @@ struct gicv2 rt_size_t hyp_size; void *vcpu_base; rt_size_t vcpu_size; + + rt_bool_t skip_init; }; #endif /* __IRQ_GICV2_H__ */ diff --git a/rt-thread/components/drivers/pic/pic-gicv2m.c b/rt-thread/components/drivers/pic/pic-gicv2m.c new file mode 100644 index 0000000..ae60137 --- /dev/null +++ b/rt-thread/components/drivers/pic/pic-gicv2m.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pic.gicv2m" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include "pic-gic-common.h" + +/* +* MSI_TYPER: +* [31:26] Reserved +* [25:16] lowest SPI assigned to MSI +* [15:10] Reserved +* [9:0] Numer of SPIs assigned to MSI +*/ +#define V2M_MSI_TYPER 0x008 +#define V2M_MSI_TYPER_BASE_SHIFT 16 +#define V2M_MSI_TYPER_BASE_MASK 0x3ff +#define V2M_MSI_TYPER_NUM_MASK 0x3ff +#define V2M_MSI_SETSPI_NS 0x040 +#define V2M_MIN_SPI 32 +#define V2M_MAX_SPI 1019 +#define V2M_MSI_IIDR 0xfcc + +#define V2M_MSI_TYPER_BASE_SPI(x) (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK) +#define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK) + +/* APM X-Gene with GICv2m MSI_IIDR register value */ +#define XGENE_GICV2M_MSI_IIDR 0x06000170 +/* Broadcom NS2 GICv2m MSI_IIDR register value */ +#define BCM_NS2_GICV2M_MSI_IIDR 0x0000013f + +/* List of flags for specific v2m implementation */ +#define GICV2M_NEEDS_SPI_OFFSET 0x00000001 +#define GICV2M_GRAVITON_ADDRESS_ONLY 0x00000002 + +struct gicv2m +{ + struct rt_pic parent; + + void *base; + void *base_phy; + rt_uint32_t spi_start; /* The SPI number that MSIs start */ + rt_uint32_t spis_nr; /* The number of SPIs for MSIs */ + rt_uint32_t spi_offset; /* Offset to be subtracted from SPI number */ + + rt_bitmap_t *vectors; /* MSI vector bitmap */ + rt_uint32_t flags; /* Flags for v2m's specific implementation */ + + void *gic; + struct rt_spinlock lock; +}; + +#define raw_to_gicv2m(raw) rt_container_of(raw, struct gicv2m, parent) + +static rt_ubase_t gicv2m_get_msi_addr(struct gicv2m *v2m, int hwirq) +{ + rt_ubase_t addr; + + if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) + { + addr = (rt_ubase_t)v2m->base_phy | ((hwirq - 32) << 3); + } + else + { + addr = (rt_ubase_t)v2m->base_phy + V2M_MSI_SETSPI_NS; + } + + return addr; +} + +static rt_bool_t is_msi_spi_valid(rt_uint32_t base, rt_uint32_t num) +{ + if (base < V2M_MIN_SPI) + { + LOG_E("Invalid MSI base SPI (base: %u)", base); + + return RT_FALSE; + } + else if ((num == 0) || (base + num > V2M_MAX_SPI)) + { + LOG_E("Number of SPIs (%u) exceed maximum (%u)", num, V2M_MAX_SPI - V2M_MIN_SPI + 1); + + return RT_FALSE; + } + + return RT_TRUE; +} + +static void gicv2m_irq_mask(struct rt_pic_irq *pirq) +{ + rt_pci_msi_mask_irq(pirq); + rt_pic_irq_parent_mask(pirq); +} + +static void gicv2m_irq_unmask(struct rt_pic_irq *pirq) +{ + rt_pci_msi_unmask_irq(pirq); + rt_pic_irq_parent_unmask(pirq); +} + +static void gicv2m_compose_msi_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) +{ + rt_ubase_t addr; + struct gicv2m *v2m = raw_to_gicv2m(pirq->pic); + + addr = gicv2m_get_msi_addr(v2m, pirq->hwirq); + + msg->address_hi = rt_upper_32_bits(addr); + msg->address_lo = rt_lower_32_bits(addr); + + if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) + { + msg->data = 0; + } + else + { + msg->data = pirq->hwirq; + } + + if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) + { + msg->data -= v2m->spi_offset; + } +} + +static int gicv2m_irq_alloc_msi(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc) +{ + rt_ubase_t level; + int irq, parent_irq, hwirq, hwirq_index; + struct rt_pic_irq *pirq; + struct gicv2m *v2m = raw_to_gicv2m(pic); + struct rt_pic *ppic = v2m->gic; + + level = rt_spin_lock_irqsave(&v2m->lock); + hwirq_index = rt_bitmap_next_clear_bit(v2m->vectors, 0, v2m->spis_nr); + + if (hwirq_index >= v2m->spis_nr) + { + irq = -RT_EEMPTY; + goto _out_lock; + } + + hwirq = v2m->spi_start + v2m->spi_offset + hwirq_index; + + parent_irq = ppic->ops->irq_map(ppic, hwirq, RT_IRQ_MODE_EDGE_RISING); + if (parent_irq < 0) + { + irq = parent_irq; + goto _out_lock; + } + + irq = rt_pic_config_irq(pic, hwirq_index, hwirq); + if (irq < 0) + { + goto _out_lock; + } + pirq = rt_pic_find_irq(pic, hwirq_index); + + pirq->mode = RT_IRQ_MODE_EDGE_RISING; + rt_pic_cascade(pirq, parent_irq); + + rt_bitmap_set_bit(v2m->vectors, hwirq_index); + +_out_lock: + rt_spin_unlock_irqrestore(&v2m->lock, level); + + return irq; +} + +static void gicv2m_irq_free_msi(struct rt_pic *pic, int irq) +{ + rt_ubase_t level; + struct rt_pic_irq *pirq; + struct gicv2m *v2m = raw_to_gicv2m(pic); + + pirq = rt_pic_find_pirq(pic, irq); + + if (!pirq) + { + return; + } + + rt_pic_uncascade(pirq); + + level = rt_spin_lock_irqsave(&v2m->lock); + rt_bitmap_clear_bit(v2m->vectors, pirq->hwirq - (v2m->spi_start + v2m->spi_offset)); + rt_spin_unlock_irqrestore(&v2m->lock, level); +} + +static rt_err_t gicv2m_irq_set_state(struct rt_pic *pic, int hwirq, int type, rt_bool_t state) +{ + struct gicv2m *v2m = raw_to_gicv2m(pic); + struct rt_pic *ppic = v2m->gic; + + return ppic->ops->irq_set_state(ppic, hwirq, type, state); +} + +static rt_err_t gicv2m_irq_get_state(struct rt_pic *pic, int hwirq, int type, rt_bool_t *out_state) +{ + struct gicv2m *v2m = raw_to_gicv2m(pic); + struct rt_pic *ppic = v2m->gic; + + return ppic->ops->irq_get_state(ppic, hwirq, type, out_state); +} + +const static struct rt_pic_ops gicv2m_ops = +{ + .name = "GICv2m", + .irq_ack = rt_pic_irq_parent_ack, + .irq_mask = gicv2m_irq_mask, + .irq_unmask = gicv2m_irq_unmask, + .irq_eoi = rt_pic_irq_parent_eoi, + .irq_set_priority = rt_pic_irq_parent_set_priority, + .irq_set_affinity = rt_pic_irq_parent_set_affinity, + .irq_compose_msi_msg = gicv2m_compose_msi_msg, + .irq_alloc_msi = gicv2m_irq_alloc_msi, + .irq_free_msi = gicv2m_irq_free_msi, + .irq_set_state = gicv2m_irq_set_state, + .irq_get_state = gicv2m_irq_get_state, + .flags = RT_PIC_F_IRQ_ROUTING, +}; + +static const struct rt_ofw_node_id gicv2m_ofw_match[] = +{ + { .compatible = "arm,gic-v2m-frame" }, + { /* sentinel */ } +}; + +rt_err_t gicv2m_ofw_probe(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) +{ + rt_err_t err = RT_EOK; + struct rt_ofw_node *v2m_np; + + rt_ofw_foreach_available_child_node(np, v2m_np) + { + struct gicv2m *v2m; + rt_size_t bitmap_size; + rt_uint32_t spi_start = 0, spis_nr = 0; + + if (!rt_ofw_node_match(v2m_np, gicv2m_ofw_match)) + { + continue; + } + + if (!rt_ofw_prop_read_bool(v2m_np, "msi-controller")) + { + continue; + } + + if (!(v2m = rt_malloc(sizeof(*v2m)))) + { + rt_ofw_node_put(v2m_np); + + err = -RT_ENOMEM; + break; + } + + v2m->base = rt_ofw_iomap(v2m_np, 0); + + if (!v2m->base) + { + LOG_E("%s: IO map failed", rt_ofw_node_full_name(v2m_np)); + continue; + } + + v2m->base_phy = rt_kmem_v2p(v2m->base); + v2m->flags = 0; + + if (!rt_ofw_prop_read_u32(v2m_np, "arm,msi-base-spi", &spi_start) && + !rt_ofw_prop_read_u32(v2m_np, "arm,msi-num-spis", &spis_nr)) + { + LOG_I("DT overriding V2M MSI_TYPER (base:%u, num:%u)", spi_start, spis_nr); + } + + if (spi_start && spis_nr) + { + v2m->spi_start = spi_start; + v2m->spis_nr = spis_nr; + } + else + { + rt_uint32_t typer; + + /* Graviton should always have explicit spi_start/spis_nr */ + if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) + { + goto _fail; + } + typer = HWREG32(v2m->base + V2M_MSI_TYPER); + + v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); + v2m->spis_nr = V2M_MSI_TYPER_NUM_SPI(typer); + } + + if (!is_msi_spi_valid(v2m->spi_start, v2m->spis_nr)) + { + goto _fail; + } + + /* + * APM X-Gene GICv2m implementation has an erratum where + * the MSI data needs to be the offset from the spi_start + * in order to trigger the correct MSI interrupt. This is + * different from the standard GICv2m implementation where + * the MSI data is the absolute value within the range from + * spi_start to (spi_start + num_spis). + * + * Broadcom NS2 GICv2m implementation has an erratum where the MSI data + * is 'spi_number - 32' + * + * Reading that register fails on the Graviton implementation + */ + if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) + { + switch (HWREG32(v2m->base + V2M_MSI_IIDR)) + { + case XGENE_GICV2M_MSI_IIDR: + v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; + v2m->spi_offset = v2m->spi_start; + break; + + case BCM_NS2_GICV2M_MSI_IIDR: + v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; + v2m->spi_offset = 32; + break; + } + } + + bitmap_size = RT_BITMAP_LEN(v2m->spis_nr) * sizeof(rt_bitmap_t); + + if (!(v2m->vectors = rt_calloc(1, bitmap_size))) + { + err = -RT_ENOMEM; + goto _fail; + } + + rt_spin_lock_init(&v2m->lock); + + v2m->parent.priv_data = v2m; + v2m->parent.ops = &gicv2m_ops; + v2m->gic = rt_ofw_data(np); + + rt_pic_linear_irq(&v2m->parent, v2m->spis_nr); + rt_pic_user_extends(&v2m->parent); + + rt_ofw_data(v2m_np) = &v2m->parent; + rt_ofw_node_set_flag(v2m_np, RT_OFW_F_READLY); + + continue; + + _fail: + rt_iounmap(v2m->base); + rt_free(v2m); + + if (err) + { + rt_ofw_node_put(v2m_np); + break; + } + } + + return err; +} diff --git a/rt-thread/components/drivers/pic/pic-gicv3-its.c b/rt-thread/components/drivers/pic/pic-gicv3-its.c new file mode 100644 index 0000000..f0772d9 --- /dev/null +++ b/rt-thread/components/drivers/pic/pic-gicv3-its.c @@ -0,0 +1,1584 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pic.gicv3-its" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include +#include "pic-gicv3.h" +#include "pic-gic-common.h" + +#define ITS_CMD_QUEUE_SIZE (64 * SIZE_KB) +#define ITS_CMD_QUEUE_ALIGN (64 * SIZE_KB) +#define ITS_CMD_QUEUE_NR (ITS_CMD_QUEUE_SIZE / sizeof(struct its_command)) + +#define ITS_ITT_ALIGN (256 * SIZE_KB) + +#define ITS_LPI_CONFIG_TABLE_ALIGN (64 * SIZE_KB) +#define ITS_LPI_CONFIG_PROP_DEFAULT_PRIO GICD_INT_DEF_PRI +#define ITS_LPI_CONFIG_PROP_SHIFT 2 +#define ITS_LPI_CONFIG_PROP_MASK RT_GENMASK(7, ITS_LPI_CONFIG_PROP_SHIFT) +#define ITS_LPI_PENDING_TABLE_ALIGN (64 * SIZE_KB) + +#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING RT_BIT(0) +#define RDIST_FLAGS_RD_TABLES_PREALLOCATED RT_BIT(1) +#define RDIST_FLAGS_FORCE_NON_SHAREABLE RT_BIT(2) + +#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING RT_BIT(0) +#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 RT_BIT(1) +#define ITS_FLAGS_FORCE_NON_SHAREABLE RT_BIT(2) + +#define RD_LOCAL_LPI_ENABLED RT_BIT(0) +#define RD_LOCAL_PENDTABLE_PREALLOCATED RT_BIT(1) +#define RD_LOCAL_MEMRESERVE_DONE RT_BIT(2) + +struct its_command +{ + union + { + rt_le64_t code_raw[4]; + rt_uint64_t code[4]; + }; +}; + +struct its_table +{ + void *base; + rt_uint64_t val; + rt_uint32_t size_bits; + rt_uint32_t page_size; + union + { + struct + { + rt_uint32_t itt_entries; + rt_uint32_t lvl2_bits; + }; + }; +}; + +struct its_collection +{ + rt_uint64_t target_address; + rt_uint16_t id; +}; + +struct gicv3_its; + +struct its_map +{ + rt_list_t list; + struct rt_ref ref; + struct gicv3_its *its; + + int device_id; + int lpi_base; + int cpu_id; + + void *itt; + void *lvl2_dte; +}; + +struct gicv3_its +{ + struct rt_pic parent; + rt_list_t list; + + void *base; + void *base_phy; + + void *cmd_base; + rt_ubase_t cmd_idx; + rt_uint32_t flags; + struct rt_spinlock cmd_lock; + + struct its_table tbls[GITS_BASER_NR_REGS]; + struct its_collection collections[RT_CPUS_NR]; + + struct gicv3 *gic; + struct rt_ofw_node *np; +}; + +#define raw_to_gicv3_its(raw) rt_container_of(raw, struct gicv3_its, parent) + +static rt_size_t lpi_nr; +static rt_uint32_t lpi_id_bits; +static void *lpi_table; +static void *lpi_pending_table; +static rt_bitmap_t *lpis_vectors = RT_NULL; +static struct rt_spinlock lpis_lock = {}, map_lock = {}; +static rt_list_t its_nodes = RT_LIST_OBJECT_INIT(its_nodes); +static rt_list_t map_nodes = RT_LIST_OBJECT_INIT(map_nodes); + +rt_inline rt_uint64_t its_readq(struct gicv3_its *its, int off) +{ + return HWREG32(its->base + off) | + (rt_uint64_t)HWREG32(its->base + off + 4) << 32; +} + +rt_inline void its_writeq(struct gicv3_its *its, int off, rt_uint64_t value) +{ + HWREG32(its->base + off) = (rt_uint32_t)value; + HWREG32(its->base + off + 4) = (rt_uint32_t)(value >> 32); +} + +rt_inline rt_uint32_t its_readl(struct gicv3_its *its, int off) +{ + return HWREG32(its->base + off); +} + +rt_inline void its_writel(struct gicv3_its *its, int off, rt_uint32_t value) +{ + HWREG32(its->base + off) = value; +} + +rt_inline rt_uint32_t its_pirq_event_id(struct gicv3_its *its, struct rt_pic_irq *pirq) +{ + return pirq->hwirq - 8192; +} + +rt_inline rt_uint32_t its_pirq_device_id(struct gicv3_its *its, struct rt_pic_irq *pirq) +{ + struct its_map *map = pirq->msi_desc->priv; + + return map->device_id; +} + +rt_inline rt_size_t its_device_id_bits(struct gicv3_its *its) +{ + return RT_FIELD_GET(GITS_TYPER_DEVBITS, HWREG64(its->base + GITS_TYPER)) + 1; +} + +rt_inline void *lpi_base_config(int index) +{ + return &((rt_uint8_t *)lpi_table)[index - 8192]; +} + +static void its_mask_encode(rt_uint64_t *raw_code, rt_uint64_t val, int h, int l) +{ + rt_uint64_t mask = RT_GENMASK_ULL(h, l); + *raw_code &= ~mask; + *raw_code |= (val << l) & mask; +} + +rt_inline void its_encode_cmd(struct its_command *cmd, rt_uint8_t cmd_nr) +{ + its_mask_encode(&cmd->code[0], cmd_nr, 7, 0); +} + +rt_inline void its_encode_valid(struct its_command *cmd, rt_bool_t valid) +{ + its_mask_encode(&cmd->code[2], !!valid, 63, 63); +} + +rt_inline void its_encode_phys_id(struct its_command *cmd, rt_uint32_t phys_id) +{ + its_mask_encode(&cmd->code[1], phys_id, 63, 32); +} + +rt_inline void its_encode_size(struct its_command *cmd, rt_uint8_t size) +{ + its_mask_encode(&cmd->code[1], size, 4, 0); +} + +rt_inline void its_encode_itt(struct its_command *cmd, rt_uint64_t itt_addr) +{ + its_mask_encode(&cmd->code[2], itt_addr >> 8, 51, 8); +} + +rt_inline void its_encode_target(struct its_command *cmd, rt_uint64_t target_addr) +{ + its_mask_encode(&cmd->code[2], target_addr >> 16, 51, 16); +} + +rt_inline void its_encode_device_id(struct its_command *cmd, rt_uint32_t device_id) +{ + its_mask_encode(&cmd->code[0], device_id, 63, 32); +} + +rt_inline void its_encode_event_id(struct its_command *cmd, rt_uint32_t event_id) +{ + its_mask_encode(&cmd->code[1], event_id, 31, 0); +} + +rt_inline void its_encode_collection(struct its_command *cmd, rt_uint16_t collection_id) +{ + its_mask_encode(&cmd->code[2], collection_id, 15, 0); +} + +static struct its_table *its_baser_type(struct gicv3_its *its, int type) +{ + for (int i = 0; i < RT_ARRAY_SIZE(its->tbls); ++i) + { + if (GITS_BASER_TYPE(its->tbls[i].val) == type) + { + return &its->tbls[i]; + } + } + + return RT_NULL; +} + +static struct its_command *its_cmd_alloc(struct gicv3_its *its) +{ + struct its_command *cmd = RT_NULL; + + for (rt_uint32_t count = 0; count <= 10000; ++count) + { + if ((its->cmd_idx + 1) % ITS_CMD_QUEUE_NR != its_readl(its, GITS_CREADR) / sizeof(*cmd)) + { + struct its_command *cmds = its->cmd_base; + + cmd = &cmds[its->cmd_idx++]; + its->cmd_idx %= ITS_CMD_QUEUE_NR; + + rt_memset(cmd, 0, sizeof(*cmd)); + + break; + } + + rt_hw_us_delay(10); + } + + return cmd; +} + +static rt_err_t its_cmd_submit_raw(struct gicv3_its *its, struct its_command *cmd) +{ + rt_uint64_t cwriter; + rt_bool_t retry = RT_FALSE; + + cwriter = (void *)(cmd + 1) - its->cmd_base; + rt_hw_rmb(); + +#ifdef ARCH_CPU_BIG_ENDIAN + cmd->code_raw[0] = rt_cpu_to_le64(cmd->code[0]); + cmd->code_raw[1] = rt_cpu_to_le64(cmd->code[1]); + cmd->code_raw[2] = rt_cpu_to_le64(cmd->code[2]); + cmd->code_raw[3] = rt_cpu_to_le64(cmd->code[3]); +#endif /* ARCH_CPU_BIG_ENDIAN */ + + /* Make sure the commands written to memory are observable by the ITS */ + if (its->flags & ITS_FLAGS_CMDQ_NEEDS_FLUSHING) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, cmd, sizeof(*cmd)); + } + else + { + rt_hw_wmb(); + } + + its_writel(its, GITS_CWRITER, cwriter); + + for (rt_uint32_t count = 0; count < 10000; ++count) + { + if (its_readl(its, GITS_CREADR) == cwriter) + { + return RT_EOK; + } + + /* Stalled */ + if (!retry && its_readl(its, GITS_CREADR) & 1) + { + /* Retry */ + its_writel(its, GITS_CWRITER, cwriter); + retry = RT_TRUE; + } + else if (retry) + { + LOG_E("Retry command 0x%02x fail", cmd->code[0] & 0xff); + + return -RT_EIO; + } + + rt_hw_us_delay(10); + } + + return -RT_ETIMEOUT; +} + +static rt_err_t its_cmd_submit_nomap(struct gicv3_its *its, struct its_command *cmd, + int cpu_id, rt_bool_t sync) +{ + rt_err_t err; + struct its_command *hw_cmd; + + rt_hw_spin_lock(&its->cmd_lock.lock); + + if (!(hw_cmd = its_cmd_alloc(its))) + { + err = -RT_EBUSY; + goto _out_lock; + } + + rt_memcpy(hw_cmd, cmd, sizeof(*hw_cmd)); + + if ((err = its_cmd_submit_raw(its, hw_cmd))) + { + goto _out_lock; + } + + if (sync) + { + if (!(hw_cmd = its_cmd_alloc(its))) + { + err = -RT_EBUSY; + goto _out_lock; + } + + its_encode_cmd(hw_cmd, GITS_CMD_SYNC); + its_encode_target(hw_cmd, its->collections[cpu_id].target_address); + + err = its_cmd_submit_raw(its, hw_cmd); + } + +_out_lock: + rt_hw_spin_unlock(&its->cmd_lock.lock); + + return err; +} + +static rt_err_t its_cmd_submit(struct gicv3_its *its, struct its_command *cmd, + struct its_map *map, rt_bool_t sync) +{ + return its_cmd_submit_nomap(its, cmd, map->cpu_id, sync); +} + +static rt_err_t lpi_flush_config(struct gicv3_its *its, rt_uint8_t *conf, + struct rt_pic_irq *pirq) +{ + struct its_command cmd; + struct its_map *map = pirq->msi_desc->priv; + + if (its->gic->redist_flags & RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING) + { + /* Clean D-cache under command */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, conf, sizeof(*conf)); + } + else + { + /* DSB inner shareable, store */ + rt_hw_wmb(); + } + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_INV); + its_encode_device_id(&cmd, its_pirq_device_id(its, pirq)); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + + return its_cmd_submit(its, &cmd, map, RT_FALSE); +} + +rt_inline void *gicr_rd_base_percpu(struct gicv3 *gic) +{ + return gic->redist_regions[rt_hw_cpu_id()].base; +} + +rt_inline void *gicr_rd_base(struct gicv3_its *its) +{ + return its->gic->redist_percpu_base[rt_hw_cpu_id()]; +} + +rt_inline rt_uint64_t *gicr_rd_flags(struct gicv3_its *its) +{ + return &its->gic->redist_percpu_flags[rt_hw_cpu_id()]; +} + +static rt_bool_t gicr_supports_plpis(struct gicv3_its *its) +{ + return !!(HWREG64(gicr_rd_base(its) + GICR_TYPER) & GICR_TYPER_PLPIS); +} + +static rt_err_t redist_disable_lpis(struct gicv3_its *its) +{ + void *gicr = gicr_rd_base(its); + rt_uint64_t timeout = 1000000L, val; + + if (!gicr_supports_plpis(its)) + { + LOG_E("CPU#%d: LPIs not supported", rt_hw_cpu_id()); + return -RT_ENOSYS; + } + + val = HWREG32(gicr + GICR_CTLR); + if (!(val & GICR_CTLR_ENABLE_LPIS)) + { + return RT_EOK; + } + + /* + * If coming via a CPU hotplug event, we don't need to disable + * LPIs before trying to re-enable them. They are already + * configured and all is well in the world. + * + * If running with preallocated tables, there is nothing to do. + */ + if ((*gicr_rd_flags(its) & RD_LOCAL_LPI_ENABLED) || + (its->gic->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED)) + { + return RT_EOK; + } + + /* From that point on, we only try to do some damage control */ + LOG_W("CPU%d: Booted with LPIs enabled, memory probably corrupted", rt_hw_cpu_id()); + + /* Disable LPIs */ + val &= ~GICR_CTLR_ENABLE_LPIS; + HWREG32(gicr + GICR_CTLR) = val; + + /* Make sure any change to GICR_CTLR is observable by the GIC */ + rt_hw_barrier(dsb, sy); + + /* + * Software must observe RWP==0 after clearing GICR_CTLR.EnableLPIs + * from 1 to 0 before programming GICR_PEND{PROP}BASER registers. + * Error out if we time out waiting for RWP to clear. + */ + while (HWREG32(gicr + GICR_CTLR) & GICR_CTLR_RWP) + { + if (!timeout) + { + LOG_E("CPU#%d: Timeout while disabling LPIs", rt_hw_cpu_id()); + + return -RT_ETIMEOUT; + } + + rt_hw_us_delay(1); + --timeout; + } + + /* + * After it has been written to 1, it is IMPLEMENTATION + * DEFINED whether GICR_CTLR.EnableLPI becomes RES1 or can be + * cleared to 0. Error out if clearing the bit failed. + */ + if (HWREG32(gicr + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) + { + LOG_E("CPU#%d: Failed to disable LPIs", rt_hw_cpu_id()); + + return -RT_EBUSY; + } + + return RT_EOK; +} + +static void gicv3_its_cpu_init_lpis(struct gicv3_its *its) +{ + void *gicr; + rt_ubase_t paddr; + rt_uint64_t val, tmp; + + if (*gicr_rd_flags(its) & RD_LOCAL_LPI_ENABLED) + { + return; + } + + gicr = gicr_rd_base(its); + + val = HWREG32(gicr + GICR_CTLR); + + if ((its->gic->redist_flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) && + (val & GICR_CTLR_ENABLE_LPIS)) + { + *gicr_rd_flags(its) |= RD_LOCAL_PENDTABLE_PREALLOCATED; + + goto _out; + } + + paddr = (rt_ubase_t)rt_kmem_v2p(lpi_pending_table); + + /* Set PROPBASE */ + val = ((rt_ubase_t)rt_kmem_v2p(lpi_table) | + GITS_CBASER_InnerShareable | + GITS_CBASER_RaWaWb | + ((lpi_id_bits - 1) & GICR_PROPBASER_IDBITS_MASK)); + + HWREG64(gicr + GICR_PROPBASER) = val; + tmp = HWREG64(gicr + GICR_PROPBASER); + + if (its->gic->redist_flags & RDIST_FLAGS_FORCE_NON_SHAREABLE) + { + tmp &= ~GICR_PBASER_SHARE_MASK_ALL; + } + + if ((tmp ^ val) & GICR_PBASER_SHARE_MASK_ALL) + { + if (!(tmp & GICR_PBASER_SHARE_MASK_ALL)) + { + /* + * The HW reports non-shareable, + * we must remove the cacheability attributes as well. + */ + val &= ~(GICR_PBASER_SHARE_MASK_ALL | GICR_PBASER_INNER_MASK_ALL); + val |= GICR_PBASER_nC; + HWREG64(gicr + GICR_PROPBASER) = val; + } + + if (!rt_hw_cpu_id()) + { + LOG_I("Using cache flushing for LPI property table"); + } + its->gic->redist_flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; + } + + val = (paddr | GICR_PBASER_InnerShareable | GICR_PBASER_RaWaWb); + + HWREG64(gicr + GICR_PENDBASER) = val; + tmp = HWREG64(gicr + GICR_PENDBASER); + + if (its->gic->redist_flags & RDIST_FLAGS_FORCE_NON_SHAREABLE) + { + tmp &= ~GICR_PBASER_SHARE_MASK_ALL; + } + + if (!(tmp & GICR_PBASER_SHARE_MASK_ALL)) + { + /* + * The HW reports non-shareable, we must remove the + * cacheability attributes as well. + */ + val &= ~(GICR_PBASER_SHARE_MASK_ALL | GICR_PBASER_INNER_MASK_ALL); + val |= GICR_PBASER_nC; + HWREG64(gicr + GICR_PENDBASER) = val; + } + + /* Enable LPIs */ + val = HWREG32(gicr + GICR_CTLR); + val |= GICR_CTLR_ENABLE_LPIS; + HWREG32(gicr + GICR_CTLR) = val; + + rt_hw_barrier(dsb, sy); + +_out: + *gicr_rd_flags(its) |= RD_LOCAL_LPI_ENABLED; +} + +static void gicv3_its_cpu_init_collection(struct gicv3_its *its) +{ + rt_uint64_t target; + int cpu_id = rt_hw_cpu_id(); + struct its_command cmd; + struct its_collection *collection; + + if (HWREG64(its->base + GITS_TYPER) & GITS_TYPER_PTA) + { + target = (rt_uint64_t)rt_kmem_v2p(gicr_rd_base(its)); + } + else + { + /* Linear by GICR processor number */ + target = HWREG64(gicr_rd_base(its) + GICR_TYPER); + target = GICR_TYPER_CPU_NO(target) << 16; + } + + collection = &its->collections[cpu_id]; + collection->target_address = target; + collection->id = cpu_id; + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_MAPC); + its_encode_collection(&cmd, collection->id); + its_encode_target(&cmd, target); + its_encode_valid(&cmd, RT_TRUE); + its_cmd_submit_nomap(its, &cmd, cpu_id, RT_TRUE); + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_INVALL); + its_encode_collection(&cmd, collection->id); + its_cmd_submit_nomap(its, &cmd, cpu_id, RT_TRUE); +} + +static rt_err_t gicv3_its_irq_init(struct rt_pic *pic) +{ + rt_err_t err; + struct gicv3_its *its = raw_to_gicv3_its(pic); + + if ((err = redist_disable_lpis(its))) + { + return err; + } + + gicv3_its_cpu_init_lpis(its); + gicv3_its_cpu_init_collection(its); + + return RT_EOK; +} + +static void gicv3_its_irq_mask(struct rt_pic_irq *pirq) +{ + rt_uint8_t *conf = lpi_base_config(pirq->hwirq); + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + + *conf &= ~GITS_LPI_CFG_ENABLED; + lpi_flush_config(its, conf, pirq); + + rt_pci_msi_mask_irq(pirq); +} + +static void gicv3_its_irq_unmask(struct rt_pic_irq *pirq) +{ + rt_uint8_t *conf = lpi_base_config(pirq->hwirq); + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + + *conf |= GITS_LPI_CFG_ENABLED; + lpi_flush_config(its, conf, pirq); + + rt_pci_msi_unmask_irq(pirq); +} + +static rt_err_t gicv3_its_irq_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority) +{ + rt_uint8_t *conf = lpi_base_config(pirq->hwirq); + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + + *conf = (priority << ITS_LPI_CONFIG_PROP_SHIFT) | (*conf & (~ITS_LPI_CONFIG_PROP_MASK)); + + return lpi_flush_config(its, conf, pirq); +} + +static rt_err_t gicv3_its_irq_set_affinity(struct rt_pic_irq *pirq, rt_bitmap_t *affinity) +{ + int cpu_id; + rt_err_t err; + struct its_map *map; + struct its_command cmd; + struct its_collection *collection; + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + + map = pirq->msi_desc->priv; + cpu_id = rt_bitmap_next_set_bit(affinity, 0, RT_CPUS_NR); + collection = &its->collections[cpu_id]; + + if (collection->target_address == ~0ULL) + { + return -RT_EIO; + } + + if (map->cpu_id == cpu_id) + { + return RT_EOK; + } + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_MOVI); + its_encode_device_id(&cmd, map->device_id); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + its_encode_collection(&cmd, collection->id); + + if (!(err = its_cmd_submit(its, &cmd, map, RT_TRUE))) + { + map->cpu_id = cpu_id; + } + + return err; +} + +static void gicv3_its_irq_compose_msi_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) +{ + rt_ubase_t addr; + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + + addr = (rt_ubase_t)its->base_phy + GITS_TRANSLATER; + + msg->address_hi = rt_upper_32_bits(addr); + msg->address_lo = rt_lower_32_bits(addr); + msg->data = its_pirq_event_id(its, pirq); +} + +static int gicv3_its_irq_alloc_msi(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc) +{ + rt_ubase_t level; + rt_uint32_t device_id = -1; + int irq = -1, hwirq, parent_irq, hwirq_index, lpi_base = 0; + struct its_map *map = RT_NULL, *map_tmp; + struct its_table *tbl; + struct its_command cmd; + struct rt_pic_irq *pirq; + struct rt_pci_device *pdev = msi_desc->pdev; + struct gicv3_its *its = raw_to_gicv3_its(pic); + struct rt_pic *ppic = &its->gic->parent; + + tbl = its_baser_type(its, GITS_BASER_TYPE_DEVICE); + RT_ASSERT(tbl != RT_NULL); + + if (!pdev->parent.ofw_node) + { + device_id = rt_pci_dev_id(pdev); + } + else + { + struct rt_ofw_cell_args args; + + for (int index = 0; ; ++index) + { + rt_err_t err = rt_ofw_parse_phandle_cells(pdev->parent.ofw_node, + "msi-parent", "#msi-cells", index, &args); + + if (err) + { + return (int)err; + } + + if (args.data == its->np) + { + device_id = args.args[0]; + } + + rt_ofw_node_put(args.data); + + if ((rt_int32_t)device_id >= 0) + { + break; + } + } + } + + if (device_id >= (1 << tbl->size_bits)) + { + LOG_E("Device ID = is %x not supported", device_id); + + return -RT_EINVAL; + } + + /* Find old map info */ + level = rt_spin_lock_irqsave(&map_lock); + rt_list_for_each_entry(map_tmp, &map_nodes, list) + { + if (map_tmp->device_id == device_id) + { + map = map_tmp; + lpi_base = map->lpi_base - 8192; + break; + } + } + rt_spin_unlock_irqrestore(&map_lock, level); + + if (!map) + { + rt_size_t itt_size; + + if (!(map = rt_calloc(1, sizeof(*map)))) + { + return -RT_ENOMEM; + } + + itt_size = tbl->itt_entries * (RT_FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, + HWREG64(its->base + GITS_TYPER)) + 1); + itt_size = rt_max_t(rt_size_t, itt_size, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; + + map->itt = rt_malloc_align(itt_size, ITS_ITT_ALIGN); + + if (!map->itt) + { + rt_free(map); + return -RT_ENOMEM; + } + + if (tbl->lvl2_bits) + { + void *lvl2_dte; + rt_uint64_t *entry; + + entry = tbl->base; + entry += device_id / (tbl->page_size / GITS_LVL1_ENTRY_SIZE); + + if (*entry) + { + lvl2_dte = (void *)(*entry - PV_OFFSET); + rt_page_ref_inc(lvl2_dte, tbl->lvl2_bits); + } + else + { + rt_size_t dte_size; + + lvl2_dte = rt_pages_alloc(tbl->lvl2_bits); + + if (!lvl2_dte) + { + rt_free_align(map->itt); + rt_free(map); + return -RT_ENOMEM; + } + + dte_size = rt_page_bits(tbl->lvl2_bits); + rt_memset(lvl2_dte, 0, dte_size); + + if (!(tbl->val & GITS_BASER_SHARE_MASK_ALL)) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lvl2_dte, dte_size); + } + + *entry = rt_cpu_to_le64((rt_uint64_t)rt_kmem_v2p(lvl2_dte) | GITS_BASER_VALID); + + if (!(tbl->val & GITS_BASER_SHARE_MASK_ALL)) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, entry, sizeof(*entry)); + } + + rt_hw_dsb(); + } + + map->lvl2_dte = lvl2_dte; + } + + rt_memset(map->itt, 0, itt_size); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, map->itt, itt_size); + } + msi_desc->priv = map; + + /* Alloc the LPI base on the first LPI */ + level = rt_spin_lock_irqsave(&lpis_lock); + hwirq_index = rt_bitmap_next_clear_bit(lpis_vectors, lpi_base, lpi_nr); + + if (hwirq_index >= lpi_nr) + { + irq = -RT_EEMPTY; + goto _out_lock; + } + + hwirq = 8192 + hwirq_index; + parent_irq = ppic->ops->irq_map(ppic, hwirq, RT_IRQ_MODE_EDGE_RISING); + if (parent_irq < 0) + { + irq = parent_irq; + goto _out_lock; + } + + irq = rt_pic_config_irq(pic, hwirq_index, hwirq); + if (irq < 0) + { + goto _out_lock; + } + pirq = rt_pic_find_irq(pic, hwirq_index); + + pirq->mode = RT_IRQ_MODE_EDGE_RISING; + rt_pic_cascade(pirq, parent_irq); + + rt_bitmap_set_bit(lpis_vectors, hwirq_index); + +_out_lock: + rt_spin_unlock_irqrestore(&lpis_lock, level); + + if (irq < 0) + { + return irq; + } + + if (map->its) + { + rt_ref_get(&map->ref); + } + else + { + rt_list_init(&map->list); + rt_ref_init(&map->ref); + map->its = its; + map->device_id = device_id; + map->lpi_base = hwirq; + + level = rt_spin_lock_irqsave(&map_lock); + rt_list_insert_before(&map_nodes, &map->list); + rt_spin_unlock_irqrestore(&map_lock, level); + } + + /* Default to CPU#0 */ + map->cpu_id = 0; + RT_IRQ_AFFINITY_SET(pirq->affinity, map->cpu_id); + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_MAPD); + its_encode_device_id(&cmd, device_id); + its_encode_size(&cmd, rt_ilog2(tbl->itt_entries) - 1); + its_encode_itt(&cmd, (rt_uint64_t)rt_kmem_v2p(map->itt)); + its_encode_valid(&cmd, RT_TRUE); + its_cmd_submit(its, &cmd, map, RT_FALSE); + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_MAPTI); + its_encode_device_id(&cmd, device_id); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + its_encode_phys_id(&cmd, hwirq); + its_encode_collection(&cmd, its->collections[map->cpu_id].id); + its_cmd_submit(its, &cmd, map, RT_TRUE); + + return irq; +} + +static void its_map_release(struct rt_ref *r) +{ + rt_ubase_t level; + struct gicv3_its *its; + struct its_table *tbl; + struct its_command cmd; + struct its_map *map = rt_container_of(r, struct its_map, ref); + + its = map->its; + tbl = its_baser_type(its, GITS_BASER_TYPE_DEVICE); + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_MAPD); + its_encode_device_id(&cmd, map->device_id); + its_encode_size(&cmd, rt_ilog2(tbl->itt_entries) - 1); + its_encode_itt(&cmd, (rt_uint64_t)rt_kmem_v2p(map->itt)); + its_encode_valid(&cmd, RT_FALSE); + its_cmd_submit(its, &cmd, map, RT_TRUE); + + level = rt_spin_lock_irqsave(&map_lock); + rt_list_insert_before(&map_nodes, &map->list); + rt_spin_unlock_irqrestore(&map_lock, level); + + if (map->itt) + { + rt_free_align(map->itt); + } + if (map->lvl2_dte) + { + if (rt_page_ref_get(map->lvl2_dte, tbl->lvl2_bits) == 1) + { + rt_uint64_t *entry; + + entry = tbl->base + (map->device_id / (tbl->page_size / GITS_LVL1_ENTRY_SIZE)); + *entry = rt_cpu_to_le64(0); + + if (!(tbl->val & GITS_BASER_SHARE_MASK_ALL)) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, entry, sizeof(*entry)); + } + } + rt_pages_free(map->lvl2_dte, tbl->lvl2_bits); + } + rt_free(map); +} + +static void gicv3_its_irq_free_msi(struct rt_pic *pic, int irq) +{ + rt_ubase_t level; + struct its_map *map; + struct its_command cmd; + struct rt_pic_irq *pirq; + struct gicv3_its *its = raw_to_gicv3_its(pic); + + pirq = rt_pic_find_pirq(pic, irq); + + if (!pirq) + { + return; + } + + map = pirq->msi_desc->priv; + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_DISCARD); + its_encode_device_id(&cmd, map->device_id); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + its_cmd_submit(its, &cmd, map, RT_TRUE); + + rt_pic_uncascade(pirq); + + level = rt_spin_lock_irqsave(&lpis_lock); + rt_bitmap_clear_bit(lpis_vectors, pirq->hwirq - 8192); + rt_spin_unlock_irqrestore(&lpis_lock, level); + + rt_ref_put(&map->ref, its_map_release); +} + +static rt_err_t gicv3_its_irq_set_state(struct rt_pic *pic, int hwirq, int type, rt_bool_t state) +{ + struct its_map *map; + struct its_command cmd; + struct rt_pic_irq *pirq; + struct gicv3_its *its = raw_to_gicv3_its(pic); + + if (type != RT_IRQ_STATE_PENDING || hwirq > 8192 + lpi_nr) + { + return -RT_ENOSYS; + } + + if (!(pirq = rt_pic_find_irq(pic, hwirq - 8192))) + { + return -RT_ENOSYS; + } + + map = pirq->msi_desc->priv; + rt_memset(&cmd, 0, sizeof(cmd)); + + if (state) + { + its_encode_cmd(&cmd, GITS_CMD_INT); + its_encode_device_id(&cmd, map->device_id); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + } + else + { + its_encode_cmd(&cmd, GITS_CMD_CLEAR); + its_encode_device_id(&cmd, map->device_id); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + } + + its_cmd_submit(its, &cmd, map, RT_TRUE); + + return RT_EOK; +} + +const static struct rt_pic_ops gicv3_its_ops = +{ + .name = "GICv3-ITS", + .irq_init = gicv3_its_irq_init, + .irq_ack = rt_pic_irq_parent_ack, + .irq_mask = gicv3_its_irq_mask, + .irq_unmask = gicv3_its_irq_unmask, + .irq_eoi = rt_pic_irq_parent_eoi, + .irq_set_priority = gicv3_its_irq_set_priority, + .irq_set_affinity = gicv3_its_irq_set_affinity, + .irq_compose_msi_msg = gicv3_its_irq_compose_msi_msg, + .irq_alloc_msi = gicv3_its_irq_alloc_msi, + .irq_free_msi = gicv3_its_irq_free_msi, + .irq_set_state = gicv3_its_irq_set_state, + .flags = RT_PIC_F_IRQ_ROUTING, +}; + +static rt_ssize_t its_baser_page_size(struct gicv3_its *its, struct its_table *tbl) +{ + rt_size_t page_size = 64 * SIZE_KB; + + while (page_size) + { + rt_uint64_t val, baser_page_size; + rt_off_t baser = GITS_BASERn((int)(tbl - its->tbls)); + + val = its_readq(its, baser); + val &= ~GITS_BASER_PAGE_SIZE_MASK; + + switch (page_size) + { + case 64 * SIZE_KB: + baser_page_size = GITS_BASER_PAGE_SIZE_64K; + break; + case 16 * SIZE_KB: + baser_page_size = GITS_BASER_PAGE_SIZE_16K; + break; + case 4 * SIZE_KB: + default: + baser_page_size = GITS_BASER_PAGE_SIZE_4K; + break; + } + + baser_page_size >>= GITS_BASER_PAGE_SIZE_SHIFT; + + val |= RT_FIELD_PREP(GITS_BASER_PAGE_SIZE_MASK, baser_page_size); + its_writeq(its, baser, val); + tbl->val = its_readq(its, baser); + + if (RT_FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, tbl->val) == baser_page_size) + { + break; + } + + switch (page_size) + { + case 64 * SIZE_KB: + page_size = 16 * SIZE_KB; + break; + case 16 * SIZE_KB: + page_size = 4 * SIZE_KB; + break; + case 4 * SIZE_KB: + default: + return -RT_EINVAL; + } + } + + return page_size; +} + +static rt_err_t its_table_init(struct gicv3_its *its) +{ + int inited = 0; + rt_off_t baser; + rt_bool_t indirect = RT_FALSE; + rt_size_t pages_nr, alloc_size; + rt_uint64_t val, type, entry_size, share, cache; + struct its_table *tbl; + + share = GITS_BASER_InnerShareable; + cache = GITS_BASER_RaWaWb; + + for (int i = 0; i < RT_ARRAY_SIZE(its->tbls); ++i) + { + tbl = &its->tbls[i]; + + val = its_readq(its, GITS_BASERn(i)); + type = GITS_BASER_TYPE(val); + + if (type != GITS_BASER_TYPE_DEVICE && + type != GITS_BASER_TYPE_COLLECTION) + { + continue; + } + + tbl->page_size = its_baser_page_size(its, tbl); + + if (tbl->page_size < 0) + { + continue; + } + + baser = GITS_BASERn((int)(tbl - its->tbls)); + entry_size = GITS_BASER_ENTRY_SIZE(val); + + if (type == GITS_BASER_TYPE_DEVICE) + { + tbl->size_bits = its_device_id_bits(its); + LOG_D("Device Max IDs = %lu", 1UL << tbl->size_bits); + + /* For MSI-X */ + tbl->itt_entries = 2048; + while (MAX_HANDLERS / tbl->itt_entries < (1 << tbl->size_bits) && + tbl->itt_entries > 32) + { + tbl->itt_entries >>= 1; + } + } + + its_writeq(its, baser, tbl->val | GITS_BASER_INDIRECT); + tbl->val = its_readq(its, baser); + + indirect = !!(tbl->val & GITS_BASER_INDIRECT); + if (indirect && type == GITS_BASER_TYPE_DEVICE) + { + /* The size of the level 2 table is equal to ITS page size */ + tbl->lvl2_bits = tbl->size_bits - rt_ilog2(tbl->page_size / (int)entry_size); + + /* Get level 1 entries count */ + alloc_size = (1 << tbl->size_bits) / (tbl->page_size / entry_size); + alloc_size *= GITS_LVL1_ENTRY_SIZE; + } + else + { + alloc_size = (1 << tbl->size_bits) * entry_size; + indirect = RT_FALSE; + } + + tbl->base = rt_malloc_align(alloc_size, tbl->page_size); + pages_nr = alloc_size / tbl->page_size; + + if (!tbl->base) + { + return -RT_ENOMEM; + } + + if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) + { + cache = GITS_BASER_nCnB; + } + + if (its->flags & ITS_FLAGS_FORCE_NON_SHAREABLE) + { + cache = GITS_BASER_nC; + share = 0; + } + + val = ((rt_ubase_t)rt_kmem_v2p(tbl->base) | + (type << GITS_BASER_TYPE_SHIFT) | + ((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | + (pages_nr << GITS_BASER_PAGES_SHIFT) | + cache | share | GITS_BASER_VALID); + val |= indirect ? GITS_BASER_INDIRECT : 0; + + switch (tbl->page_size) + { + case 4 * SIZE_KB: + val |= GITS_BASER_PAGE_SIZE_4K; + break; + case 16 * SIZE_KB: + val |= GITS_BASER_PAGE_SIZE_16K; + break; + case 64 * SIZE_KB: + val |= GITS_BASER_PAGE_SIZE_64K; + break; + } + + its_writeq(its, baser, val); + tbl->val = its_readq(its, baser); + + rt_memset(tbl->base, 0, alloc_size); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, tbl->base, alloc_size); + + cache = tbl->val & GITS_BASER_INNER_MASK_ALL; + share = tbl->val & GITS_BASER_SHARE_MASK_ALL; + + ++inited; + } + + return inited == 2 ? RT_EOK : -RT_ENOSYS; +} + +static rt_err_t its_cmd_queue_init(struct gicv3_its *its) +{ + void *cmd_phy_base; + rt_uint64_t baser, tmp; + + its->cmd_base = rt_malloc_align(ITS_CMD_QUEUE_SIZE, ITS_CMD_QUEUE_ALIGN); + + if (!its->cmd_base) + { + return -RT_ENOMEM; + } + + its->cmd_idx = 0; + rt_memset(its->cmd_base, 0, ITS_CMD_QUEUE_SIZE); + + cmd_phy_base = rt_kmem_v2p(its->cmd_base); + + baser = GITS_CBASER_VALID | GITS_CBASER_RaWaWb | GITS_CBASER_InnerShareable | \ + ((rt_uint64_t)cmd_phy_base) | (ITS_CMD_QUEUE_SIZE / (4 * SIZE_KB) - 1); + + its_writeq(its, GITS_CBASER, baser); + tmp = its_readq(its, GITS_CBASER); + + if (its->flags & ITS_FLAGS_FORCE_NON_SHAREABLE) + { + tmp &= ~GITS_CBASER_SHARE_MASK_ALL; + } + + if ((tmp ^ baser) & GITS_CBASER_SHARE_MASK_ALL) + { + if (!(tmp & GITS_CBASER_SHARE_MASK_ALL)) + { + /* The HW reports non-shareable, we must remove the cacheability attributes as well */ + baser &= ~(GITS_CBASER_SHARE_MASK_ALL | GITS_CBASER_INNER_MASK_ALL); + baser |= GITS_CBASER_nC; + + its_writeq(its, GITS_CBASER, baser); + } + + LOG_I("Using cache flushing for CMD queue"); + its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, its->cmd_base, ITS_CMD_QUEUE_SIZE); + } + + /* Get the next command from the start of the buffer */ + its_writeq(its, GITS_CWRITER, 0); + + return RT_EOK; +} + +static rt_err_t its_lpi_table_init(struct gicv3 *gic) +{ + rt_size_t lpi_table_size, lpi_pending_table_size; + rt_uint32_t numlpis = 1UL << GICD_TYPER_NUM_LPIS(gic->gicd_typer); + + if (HWREG32(gicr_rd_base_percpu(gic) + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) + { + gic->redist_flags |= RDIST_FLAGS_RD_TABLES_PREALLOCATED; + gic->redist_flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; + + LOG_I("Using preallocated redistributor tables"); + } + + lpi_id_bits = GICD_TYPER_ID_BITS(gic->gicd_typer); + + if (gic->redist_flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) + { + rt_uint64_t val = HWREG64(gicr_rd_base_percpu(gic) + GICR_PROPBASER); + lpi_id_bits = rt_min_t(rt_uint32_t, lpi_id_bits, (val & GICR_PROPBASER_IDBITS_MASK) + 1); + } + + lpi_nr = rt_min_t(rt_size_t, (1UL << lpi_id_bits) - 8192, gic->lpi_nr); + lpi_id_bits = __rt_clz(lpi_nr + 8192); + + if (numlpis > 2 && numlpis > lpi_nr) + { + lpi_nr = numlpis; + LOG_W("Using hypervisor restricted LPI range [%u]", lpi_nr); + } + + gic->lpi_nr = lpi_nr; + + /* LPI Configuration table entry is 1 byte, Pending table bytes is N / 8. */ + lpi_table_size = RT_GENMASK(lpi_id_bits, 0); + lpi_pending_table_size = lpi_table_size / 8; + + lpi_table = rt_malloc_align(lpi_table_size, ITS_LPI_CONFIG_TABLE_ALIGN); + lpi_pending_table = rt_malloc_align(lpi_pending_table_size, ITS_LPI_PENDING_TABLE_ALIGN); + lpis_vectors = rt_calloc(1, RT_BITMAP_LEN(lpi_nr) * sizeof(rt_bitmap_t)); + + if (!lpi_table || !lpi_pending_table || !lpis_vectors) + { + if (lpi_table) + { + rt_free_align(lpi_table); + } + if (lpi_pending_table) + { + rt_free_align(lpi_pending_table); + } + if (lpis_vectors) + { + rt_free_align(lpis_vectors); + } + + lpi_table = RT_NULL; + lpi_pending_table = RT_NULL; + lpis_vectors = RT_NULL; + + return -RT_ENOMEM; + } + + /* Set the default configuration */ + rt_memset(lpi_table, ITS_LPI_CONFIG_PROP_DEFAULT_PRIO | GITS_LPI_CFG_GROUP1, lpi_table_size); + /* + * We should make a full mask size with lpi_id_bits, + * otherwise 'undefined' LPI will occur. + */ + rt_memset(lpi_pending_table, 0, lpi_pending_table_size); + + /* Flush the table to memory */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lpi_table, lpi_table_size); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lpi_pending_table, lpi_pending_table_size); + + LOG_D("ITS: Allocator initialized for %u LPIs", lpi_nr); + + return RT_EOK; +} + +static void its_init_fail(struct gicv3_its *its) +{ + if (its->base) + { + rt_iounmap(its->base); + } + + if (its->cmd_base) + { + rt_free_align(its->cmd_base); + } + + for (int i = 0; i < RT_ARRAY_SIZE(its->tbls); ++i) + { + struct its_table *tbl = &its->tbls[i]; + + if (tbl->base) + { + rt_free_align(tbl->base); + } + } + + rt_list_remove(&its->list); + rt_free(its); +} + +static rt_err_t its_quirk_cavium_22375(void *data) +{ + struct gicv3_its *its = data; + + its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375; + + return RT_EOK; +} + +static rt_err_t its_enable_rockchip(void *data) +{ + struct gicv3_its *its = data; + struct gicv3 *gic = its->gic; + + if (!rt_ofw_machine_is_compatible("rockchip,rk3566") && + !rt_ofw_machine_is_compatible("rockchip,rk3567") && + !rt_ofw_machine_is_compatible("rockchip,rk3568") && + !rt_ofw_machine_is_compatible("rockchip,rk3588") && + !rt_ofw_machine_is_compatible("rockchip,rk3588s")) + { + return -RT_EINVAL; + } + + its->flags |= ITS_FLAGS_FORCE_NON_SHAREABLE; + gic->redist_flags |= RDIST_FLAGS_FORCE_NON_SHAREABLE; + + return RT_EOK; +} + +static rt_err_t its_set_non_coherent(void *data) +{ + struct gicv3_its *its = data; + + if (!rt_ofw_prop_read_bool(its->np, "dma-noncoherent")) + { + return -RT_EINVAL; + } + + its->flags |= ITS_FLAGS_FORCE_NON_SHAREABLE; + + return RT_EOK; +} + +static const struct gic_quirk _its_quirks[] = +{ + { + .desc = "ITS: Cavium ThunderX errata: 22375, 24313", + .iidr = 0xa100034c, + .iidr_mask = 0xffff0fff, + .init = its_quirk_cavium_22375, + }, + { + .desc = "ITS: Rockchip erratum RK3566 ~ RK3588", + .iidr = 0x0201743b, + .iidr_mask = 0xffffffff, + .init = its_enable_rockchip, + }, + { + .desc = "ITS: non-coherent attribute", + .compatible = "arm,gic-v3-its", + .init = its_set_non_coherent, + }, + { /* sentinel */ } +}; + +static const struct rt_ofw_node_id gicv3_its_ofw_match[] = +{ + { .compatible = "arm,gic-v3-its" }, + { /* sentinel */ } +}; + +rt_err_t gicv3_its_ofw_probe(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) +{ + rt_err_t err = -RT_EEMPTY; + struct rt_ofw_node *its_np; + struct gicv3_its *its, *its_next; + + rt_ofw_foreach_available_child_node(np, its_np) + { + if (!rt_ofw_node_match(its_np, gicv3_its_ofw_match)) + { + continue; + } + + if (!rt_ofw_prop_read_bool(its_np, "msi-controller")) + { + continue; + } + + if (!(its = rt_calloc(1, sizeof(struct gicv3_its)))) + { + rt_ofw_node_put(its_np); + + err = -RT_ENOMEM; + goto _free_all; + } + + its->base = rt_ofw_iomap(its_np, 0); + + if (!its->base) + { + LOG_E("%s: IO map failed", rt_ofw_node_full_name(its_np)); + its_init_fail(its); + continue; + } + + /* + * Make sure ALL the ITS are reset before we probe any, + * as they may be sharing memory + */ + for (int i = 0; i < GITS_BASER_NR_REGS; ++i) + { + its_writeq(its, GITS_BASER + (i << 3), 0); + } + + its->np = its_np; + rt_list_init(&its->list); + rt_list_insert_before(&its_nodes, &its->list); + } + + if (!rt_list_isempty(&its_nodes)) + { + if ((err = its_lpi_table_init(rt_ofw_data(np)))) + { + goto _free_all; + } + } + + rt_list_for_each_entry_safe(its, its_next, &its_nodes, list) + { + rt_uint32_t ctlr; + + its->base_phy = rt_kmem_v2p(its->base); + its->gic = rt_ofw_data(np); + + gic_common_init_quirk_hw(HWREG32(its->base + GITS_IIDR), _its_quirks, its); + gic_common_init_quirk_ofw(its->np, _its_quirks, its); + + if ((err = its_cmd_queue_init(its))) + { + goto _fail; + } + rt_spin_lock_init(&its->cmd_lock); + + if ((err = its_table_init(its))) + { + goto _fail; + } + + for (int i = 0; i < RT_CPUS_NR; ++i) + { + its->collections[i].target_address = ~0ULL; + } + + ctlr = its_readl(its, GITS_CTLR); + ctlr |= GITS_CTLR_ENABLE; + its_writel(its, GITS_CTLR, ctlr); + + its->parent.priv_data = its; + its->parent.ops = &gicv3_its_ops; + + rt_pic_linear_irq(&its->parent, its->gic->lpi_nr); + rt_pic_user_extends(&its->parent); + + its_np = its->np; + rt_ofw_data(its_np) = &its->parent; + rt_ofw_node_set_flag(its_np, RT_OFW_F_READLY); + + continue; + + _fail: + its_init_fail(its); + + if (err == -RT_ENOMEM) + { + break; + } + } + + if (rt_list_isempty(&its_nodes) && lpis_vectors) + { + rt_free(lpis_vectors); + rt_free_align(lpi_table); + rt_free_align(lpi_pending_table); + lpis_vectors = RT_NULL; + } + + return err; + +_free_all: + rt_list_for_each_entry_safe(its, its_next, &its_nodes, list) + { + rt_free(its); + rt_list_remove(&its->list); + } + + return err; +} diff --git a/rt-thread/components/drivers/pic/pic-gicv3.c b/rt-thread/components/drivers/pic/pic-gicv3.c index c5e4bc7..7a11f35 100644 --- a/rt-thread/components/drivers/pic/pic-gicv3.c +++ b/rt-thread/components/drivers/pic/pic-gicv3.c @@ -216,6 +216,11 @@ static void gicv3_dist_init(void) LOG_D("%d SPIs implemented", _gic.line_nr - 32); LOG_D("%d Extended SPIs implemented", _gic.espi_nr); + if (_gic.skip_init) + { + goto _get_max_irq; + } + /* Disable the distributor */ HWREG32(base + GICD_CTLR) = 0; gicv3_dist_wait_for_rwp(); @@ -266,20 +271,26 @@ static void gicv3_dist_init(void) HWREG64(base + GICD_IROUTERnE + i * 8) = affinity; } - if (GICD_TYPER_NUM_LPIS(_gic.gicd_typer)) +_get_max_irq: + if (GICD_TYPER_NUM_LPIS(_gic.gicd_typer) > 1) { /* Max LPI = 8192 + Math.pow(2, num_LPIs + 1) - 1 */ - rt_size_t num_lpis = (1 << (GICD_TYPER_NUM_LPIS(_gic.gicd_typer) + 1)) + 1; + rt_size_t num_lpis = 1UL << (GICD_TYPER_NUM_LPIS(_gic.gicd_typer) + 1); - _gic.lpi_nr = rt_min_t(int, num_lpis, 1 << GICD_TYPER_ID_BITS(_gic.gicd_typer)); + _gic.lpi_nr = rt_min_t(int, num_lpis, 1UL << GICD_TYPER_ID_BITS(_gic.gicd_typer)); } else { - _gic.lpi_nr = 1 << GICD_TYPER_ID_BITS(_gic.gicd_typer); + _gic.lpi_nr = 1UL << GICD_TYPER_ID_BITS(_gic.gicd_typer); } /* SPI + eSPI + LPIs */ - _gic.irq_nr = _gic.line_nr - 32 + _gic.espi_nr + _gic.lpi_nr; + _gic.irq_nr = _gic.line_nr - 32 + _gic.espi_nr; +#ifdef RT_PIC_ARM_GIC_V3_ITS + /* ITS will allocate the same number of lpi PIRQs */ + _gic.lpi_nr = rt_min_t(rt_size_t, RT_PIC_ARM_GIC_V3_ITS_IRQ_MAX, _gic.lpi_nr); + _gic.irq_nr += _gic.lpi_nr; +#endif } static void gicv3_redist_enable(rt_bool_t enable) @@ -389,6 +400,8 @@ static void gicv3_cpu_init(void) int cpu_id = rt_hw_cpu_id(); #ifdef ARCH_SUPPORT_HYP _gicv3_eoi_mode_ns = RT_TRUE; +#else + _gicv3_eoi_mode_ns = !!rt_ofw_bootargs_select("pic.gicv3_eoimode", 0); #endif base = gicv3_percpu_redist_sgi_base(); @@ -700,6 +713,7 @@ static int gicv3_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) if (pirq && hwirq >= GIC_SGI_NR) { pirq->mode = mode; + pirq->priority = GICD_INT_DEF_PRI; switch (gicv3_hwirq_type(hwirq)) { @@ -708,7 +722,6 @@ static int gicv3_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) break; case SPI_TYPE: case ESPI_TYPE: - pirq->priority = GICD_INT_DEF_PRI; RT_IRQ_AFFINITY_SET(pirq->affinity, _init_cpu_id); default: break; @@ -823,7 +836,18 @@ static rt_bool_t gicv3_handler(void *data) } else { - pirq = rt_pic_find_irq(&gic->parent, hwirq - GIC_SGI_NR); + int irq_index; + + if (hwirq < 8192) + { + irq_index = hwirq - GIC_SGI_NR; + } + else + { + irq_index = gic->irq_nr - gic->lpi_nr + hwirq - 8192; + } + + pirq = rt_pic_find_irq(&gic->parent, irq_index); } gicv3_irq_ack(pirq); @@ -1045,6 +1069,7 @@ static rt_err_t gicv3_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_ redist_stride = 0; } _gic.redist_stride = redist_stride; + _gic.skip_init = rt_ofw_prop_read_bool(np, "skip-init"); gic_common_init_quirk_ofw(np, _gicv3_quirks, &_gic.parent); gicv3_init(); diff --git a/rt-thread/components/drivers/pic/pic-gicv3.h b/rt-thread/components/drivers/pic/pic-gicv3.h index 6046d9b..61c5ae1 100644 --- a/rt-thread/components/drivers/pic/pic-gicv3.h +++ b/rt-thread/components/drivers/pic/pic-gicv3.h @@ -12,8 +12,8 @@ * 2023-02-01 GuEe-GUI move macros to header */ -#ifndef __IRQ_GICV3_H__ -#define __IRQ_GICV3_H__ +#ifndef __PIC_GICV3_H__ +#define __PIC_GICV3_H__ #include @@ -101,6 +101,8 @@ #define GICR_CTLR_IR (1UL << 2) #define GICR_CTLR_RWP (1UL << 3) +#define GICR_TYPER_CPU_NO(r) (((r) >> 8) & 0xffff) + #define GICR_RD_BASE_SIZE (64 * SIZE_KB) #define GICR_SGI_OFFSET (64 * SIZE_KB) #define GICR_SGI_BASE_SIZE GICR_SGI_OFFSET @@ -152,66 +154,142 @@ #define GITS_CTLR 0x0000 #define GITS_IIDR 0x0004 #define GITS_TYPER 0x0008 -#define GITS_MPAMIDR 0x0010 -#define GITS_PARTIDR 0x0014 #define GITS_MPIDR 0x0018 -#define GITS_STATUSR 0x0040 -#define GITS_UMSIR 0x0048 -#define GITS_CBASER 0x0048 +#define GITS_CBASER 0x0080 #define GITS_CWRITER 0x0088 #define GITS_CREADR 0x0090 -#define GITS_BASER 0x0100 /* 0x0100~0x0138 */ +#define GITS_BASER 0x0100 +#define GITS_IDREGS_BASE 0xffd0 +#define GITS_PIDR0 0xffe0 +#define GITS_PIDR1 0xffe4 +#define GITS_PIDR2 GICR_PIDR2 +#define GITS_PIDR4 0xffd0 +#define GITS_CIDR0 0xfff0 +#define GITS_CIDR1 0xfff4 +#define GITS_CIDR2 0xfff8 +#define GITS_CIDR3 0xfffc + +#define GITS_TRANSLATER 0x10040 + +#define GITS_SGIR 0x20020 + +#define GITS_SGIR_VPEID RT_GENMASK_ULL(47, 32) +#define GITS_SGIR_VINTID RT_GENMASK_ULL(3, 0) + +#define GITS_CTLR_ENABLE (1U << 0) +#define GITS_CTLR_ImDe (1U << 1) +#define GITS_CTLR_ITS_NUMBER_SHIFT 4 +#define GITS_CTLR_ITS_NUMBER (0xFU << GITS_CTLR_ITS_NUMBER_SHIFT) +#define GITS_CTLR_QUIESCENT (1U << 31) + +#define GITS_TYPER_PLPIS (1UL << 0) +#define GITS_TYPER_VLPIS (1UL << 1) +#define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT 4 +#define GITS_TYPER_ITT_ENTRY_SIZE RT_GENMASK_ULL(7, 4) +#define GITS_TYPER_IDBITS_SHIFT 8 +#define GITS_TYPER_DEVBITS_SHIFT 13 +#define GITS_TYPER_DEVBITS RT_GENMASK_ULL(17, 13) +#define GITS_TYPER_PTA (1UL << 19) +#define GITS_TYPER_HCC_SHIFT 24 +#define GITS_TYPER_HCC(r) (((r) >> GITS_TYPER_HCC_SHIFT) & 0xff) +#define GITS_TYPER_VMOVP (1ULL << 37) +#define GITS_TYPER_VMAPP (1ULL << 40) +#define GITS_TYPER_SVPET RT_GENMASK_ULL(42, 41) /* * ITS commands */ -#define GITS_CMD_MAPD 0x08 -#define GITS_CMD_MAPC 0x09 -#define GITS_CMD_MAPTI 0x0a -#define GITS_CMD_MAPI 0x0b -#define GITS_CMD_MOVI 0x01 -#define GITS_CMD_DISCARD 0x0f -#define GITS_CMD_INV 0x0c -#define GITS_CMD_MOVALL 0x0e -#define GITS_CMD_INVALL 0x0d -#define GITS_CMD_INT 0x03 -#define GITS_CMD_CLEAR 0x04 -#define GITS_CMD_SYNC 0x05 +#define GITS_CMD_MAPD 0x08 +#define GITS_CMD_MAPC 0x09 +#define GITS_CMD_MAPTI 0x0a +#define GITS_CMD_MAPI 0x0b +#define GITS_CMD_MOVI 0x01 +#define GITS_CMD_DISCARD 0x0f +#define GITS_CMD_INV 0x0c +#define GITS_CMD_MOVALL 0x0e +#define GITS_CMD_INVALL 0x0d +#define GITS_CMD_INT 0x03 +#define GITS_CMD_CLEAR 0x04 +#define GITS_CMD_SYNC 0x05 /* ITS Config Area */ -#define GITS_LPI_CFG_GROUP1 (1 << 1) -#define GITS_LPI_CFG_ENABLED (1 << 0) +#define GITS_LPI_CFG_GROUP1 (1 << 1) +#define GITS_LPI_CFG_ENABLED (1 << 0) /* ITS Command Queue Descriptor */ +#define GITS_BASER_SHAREABILITY_SHIFT 10 +#define GITS_BASER_INNER_CACHEABILITY_SHIFT 59 +#define GITS_BASER_OUTER_CACHEABILITY_SHIFT 53 #define GITS_CBASER_VALID (1UL << 63) -#define GITS_CBASER_SHAREABILITY_SHIFT (10) -#define GITS_CBASER_INNER_CACHEABILITY_SHIFT (59) -#define GITS_CBASER_OUTER_CACHEABILITY_SHIFT (53) +#define GITS_CBASER_SHAREABILITY_SHIFT 10 +#define GITS_CBASER_INNER_CACHEABILITY_SHIFT 59 +#define GITS_CBASER_OUTER_CACHEABILITY_SHIFT 53 +#define GICR_PBASER_SHAREABILITY_SHIFT 10 +#define GICR_PBASER_INNER_CACHEABILITY_SHIFT 7 +#define GICR_PBASER_OUTER_CACHEABILITY_SHIFT 56 -#define GITS_TRANSLATION_TABLE_DESCRIPTORS_NR 8 +#define GITS_BASER_NR_REGS 8 +#define GITS_BASERn(idx) (GITS_BASER + sizeof(rt_uint64_t) * idx) + +#define GITS_BASER_VALID (1ULL << 63) +#define GITS_BASER_INDIRECT (1ULL << 62) +#define GITS_BASER_TYPE_SHIFT 56 +#define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7) +#define GITS_BASER_ENTRY_SIZE_SHIFT 48 +#define GITS_BASER_ENTRY_SIZE(r) ((((r) >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1) +#define GITS_BASER_PAGE_SIZE_SHIFT 8 +#define GITS_BASER_PAGE_SIZE_4K (0ULL << GITS_BASER_PAGE_SIZE_SHIFT) +#define GITS_BASER_PAGE_SIZE_16K (1ULL << GITS_BASER_PAGE_SIZE_SHIFT) +#define GITS_BASER_PAGE_SIZE_64K (2ULL << GITS_BASER_PAGE_SIZE_SHIFT) +#define GITS_BASER_PAGE_SIZE_MASK (3ULL << GITS_BASER_PAGE_SIZE_SHIFT) +#define GITS_BASER_PAGES_SHIFT 0 + +#define GITS_LVL1_ENTRY_SIZE 8UL + +#define GITS_BASER_TYPE_NONE 0 +#define GITS_BASER_TYPE_DEVICE 1 +#define GITS_BASER_TYPE_VPE 2 +#define GITS_BASER_TYPE_RESERVED3 3 +#define GITS_BASER_TYPE_COLLECTION 4 +#define GITS_BASER_TYPE_RESERVED5 5 +#define GITS_BASER_TYPE_RESERVED6 6 +#define GITS_BASER_TYPE_RESERVED7 7 #define GITS_BASER_CACHEABILITY(reg, inner_outer, type) \ - (GITS_CBASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT) + (GIC_BASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT) #define GITS_BASER_SHAREABILITY(reg, type) \ - (GITS_CBASER_##type << reg##_SHAREABILITY_SHIFT) + (GIC_BASER_##type << reg##_SHAREABILITY_SHIFT) -#define GITS_CBASER_CACHE_DnGnRnE 0x0UL /* Device-nGnRnE. */ -#define GITS_CBASER_CACHE_NIN 0x1UL /* Normal Inner Non-cacheable. */ -#define GITS_CBASER_CACHE_NIRAWT 0x2UL /* Normal Inner Cacheable Read-allocate, Write-through. */ -#define GITS_CBASER_CACHE_NIRAWB 0x3UL /* Normal Inner Cacheable Read-allocate, Write-back. */ -#define GITS_CBASER_CACHE_NIWAWT 0x4UL /* Normal Inner Cacheable Write-allocate, Write-through. */ -#define GITS_CBASER_CACHE_NIWAWB 0x5UL /* Normal Inner Cacheable Write-allocate, Write-back. */ -#define GITS_CBASER_CACHE_NIRAWAWT 0x6UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-through. */ -#define GITS_CBASER_CACHE_NIRAWAWB 0x7UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-back. */ -#define GITS_CBASER_CACHE_MASK 0x7UL -#define GITS_CBASER_SHARE_NS 0x0UL /* Non-shareable. */ -#define GITS_CBASER_SHARE_IS 0x1UL /* Inner Shareable. */ -#define GITS_CBASER_SHARE_OS 0x2UL /* Outer Shareable. */ -#define GITS_CBASER_SHARE_RES 0x3UL /* Reserved. Treated as 0b00 */ -#define GITS_CBASER_SHARE_MASK 0x3UL +#define GIC_BASER_CACHE_DnGnRnE 0x0UL /* Device-nGnRnE. */ +#define GIC_BASER_CACHE_NIN 0x1UL /* Normal Inner Non-cacheable. */ +#define GIC_BASER_CACHE_NIRAWT 0x2UL /* Normal Inner Cacheable Read-allocate, Write-through. */ +#define GIC_BASER_CACHE_NIRAWB 0x3UL /* Normal Inner Cacheable Read-allocate, Write-back. */ +#define GIC_BASER_CACHE_NIWAWT 0x4UL /* Normal Inner Cacheable Write-allocate, Write-through. */ +#define GIC_BASER_CACHE_NIWAWB 0x5UL /* Normal Inner Cacheable Write-allocate, Write-back. */ +#define GIC_BASER_CACHE_NIRAWAWT 0x6UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-through. */ +#define GIC_BASER_CACHE_NIRAWAWB 0x7UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-back. */ +#define GIC_BASER_CACHE_MASK 0x7UL +#define GIC_BASER_SHARE_NS 0x0UL /* Non-shareable. */ +#define GIC_BASER_SHARE_IS 0x1UL /* Inner Shareable. */ +#define GIC_BASER_SHARE_OS 0x2UL /* Outer Shareable. */ +#define GIC_BASER_SHARE_RES 0x3UL /* Reserved. Treated as 0b00 */ +#define GIC_BASER_SHARE_MASK 0x3UL + +#define GITS_BASER_InnerShareable GITS_BASER_SHAREABILITY(GITS_BASER, SHARE_IS) +#define GITS_BASER_SHARE_MASK_ALL GITS_BASER_SHAREABILITY(GITS_BASER, SHARE_MASK) +#define GITS_BASER_INNER_MASK_ALL GITS_BASER_CACHEABILITY(GITS_BASER, INNER, MASK) +#define GITS_BASER_nCnB GITS_BASER_CACHEABILITY(GITS_BASER, INNER, DnGnRnE) +#define GITS_BASER_nC GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIN) +#define GITS_BASER_RaWt GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIRAWT) +#define GITS_BASER_RaWb GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIRAWB) +#define GITS_BASER_WaWt GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIWAWT) +#define GITS_BASER_WaWb GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIWAWB) +#define GITS_BASER_RaWaWt GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIRAWAWT) +#define GITS_BASER_RaWaWb GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIRAWAWB) #define GITS_CBASER_InnerShareable GITS_BASER_SHAREABILITY(GITS_CBASER, SHARE_IS) #define GITS_CBASER_SHARE_MASK_ALL GITS_BASER_SHAREABILITY(GITS_CBASER, SHARE_MASK) +#define GITS_CBASER_INNER_MASK_ALL GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, MASK) #define GITS_CBASER_nCnB GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, DnGnRnE) #define GITS_CBASER_nC GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIN) #define GITS_CBASER_RaWt GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIRAWT) @@ -221,6 +299,18 @@ #define GITS_CBASER_RaWaWt GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIRAWAWT) #define GITS_CBASER_RaWaWb GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIRAWAWB) +#define GICR_PBASER_InnerShareable GITS_BASER_SHAREABILITY(GICR_PBASER, SHARE_IS) +#define GICR_PBASER_SHARE_MASK_ALL GITS_BASER_SHAREABILITY(GICR_PBASER, SHARE_MASK) +#define GICR_PBASER_INNER_MASK_ALL GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, MASK) +#define GICR_PBASER_nCnB GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, DnGnRnE) +#define GICR_PBASER_nC GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIN) +#define GICR_PBASER_RaWt GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWT) +#define GICR_PBASER_RaWb GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWB) +#define GICR_PBASER_WaWt GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIWAWT) +#define GICR_PBASER_WaWb GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIWAWB) +#define GICR_PBASER_RaWaWt GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWAWT) +#define GICR_PBASER_RaWaWb GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWAWB) + #define GIC_EPPI_BASE_INTID 1056 #define GIC_ESPI_BASE_INTID 4096 @@ -283,6 +373,7 @@ struct gicv3 rt_size_t dist_size; void *redist_percpu_base[RT_CPUS_NR]; + rt_uint64_t redist_percpu_flags[RT_CPUS_NR]; rt_size_t percpu_ppi_nr[RT_CPUS_NR]; struct @@ -294,6 +385,8 @@ struct gicv3 rt_uint64_t redist_flags; rt_size_t redist_stride; rt_size_t redist_regions_nr; + + rt_bool_t skip_init; }; -#endif /* __IRQ_GICV3_H__ */ +#endif /* __PIC_GICV3_H__ */ diff --git a/rt-thread/components/drivers/pic/pic.c b/rt-thread/components/drivers/pic/pic.c index 4525c7d..032c965 100644 --- a/rt-thread/components/drivers/pic/pic.c +++ b/rt-thread/components/drivers/pic/pic.c @@ -16,7 +16,9 @@ #include #include +#ifdef RT_USING_PIC_STATISTICS #include +#endif struct irq_traps { @@ -31,6 +33,7 @@ static int _ipi_hash[] = #ifdef RT_USING_SMP [RT_SCHEDULE_IPI] = RT_SCHEDULE_IPI, [RT_STOP_IPI] = RT_STOP_IPI, + [RT_SMP_CALL_IPI] = RT_SMP_CALL_IPI, #endif }; @@ -173,19 +176,52 @@ rt_err_t rt_pic_linear_irq(struct rt_pic *pic, rt_size_t irq_nr) return err; } +rt_err_t rt_pic_cancel_irq(struct rt_pic *pic) +{ + rt_err_t err = RT_EOK; + + if (pic && pic->pirqs) + { + rt_ubase_t level = rt_spin_lock_irqsave(&_pic_lock); + + /* + * This is only to make system runtime safely, + * we don't recommend PICs to unregister. + */ + rt_list_remove(&pic->list); + + rt_spin_unlock_irqrestore(&_pic_lock, level); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + static void config_pirq(struct rt_pic *pic, struct rt_pic_irq *pirq, int irq, int hwirq) { rt_ubase_t level = rt_spin_lock_irqsave(&pirq->rw_lock); + if (pirq->irq < 0) + { + rt_list_init(&pirq->list); + rt_list_init(&pirq->children_nodes); + rt_list_init(&pirq->isr.list); + } + else if (pirq->pic != pic) + { + RT_ASSERT(rt_list_isempty(&pirq->list) == RT_TRUE); + RT_ASSERT(rt_list_isempty(&pirq->children_nodes) == RT_TRUE); + RT_ASSERT(rt_list_isempty(&pirq->isr.list) == RT_TRUE); + } + pirq->irq = irq; pirq->hwirq = hwirq; pirq->pic = pic; - rt_list_init(&pirq->list); - rt_list_init(&pirq->children_nodes); - rt_list_init(&pirq->isr.list); - rt_spin_unlock_irqrestore(&pirq->rw_lock, level); } @@ -494,6 +530,8 @@ rt_err_t rt_pic_do_traps(void) rt_err_t err = -RT_ERROR; struct irq_traps *traps; + rt_interrupt_enter(); + rt_list_for_each_entry(traps, &_traps_nodes, list) { if (traps->handler(traps->data)) @@ -504,6 +542,8 @@ rt_err_t rt_pic_do_traps(void) } } + rt_interrupt_leave(); + return err; } @@ -535,11 +575,17 @@ rt_err_t rt_pic_handle_isr(struct rt_pic_irq *pirq) rt_list_for_each_entry(child, &pirq->children_nodes, list) { - rt_pic_irq_ack(child->irq); + if (child->pic->ops->irq_ack) + { + child->pic->ops->irq_ack(child); + } err = rt_pic_handle_isr(child); - rt_pic_irq_eoi(child->irq); + if (child->pic->ops->irq_eoi) + { + child->pic->ops->irq_eoi(child); + } } } diff --git a/rt-thread/components/drivers/pin/Kconfig b/rt-thread/components/drivers/pin/Kconfig index 24a2c02..2520897 100644 --- a/rt-thread/components/drivers/pin/Kconfig +++ b/rt-thread/components/drivers/pin/Kconfig @@ -1,3 +1,7 @@ menuconfig RT_USING_PIN bool "Using Generic GPIO device drivers" default y + +if RT_USING_PIN + osource "$(SOC_DM_PIN_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/pin/SConscript b/rt-thread/components/drivers/pin/SConscript index bdad848..69e3b53 100644 --- a/rt-thread/components/drivers/pin/SConscript +++ b/rt-thread/components/drivers/pin/SConscript @@ -8,13 +8,13 @@ if not GetDepend(['RT_USING_PIN']): cwd = GetCurrentDir() CPPPATH = [cwd + '/../include'] -src = ['pin.c'] +src = ['dev_pin.c'] if GetDepend(['RT_USING_DM']): - src += ['pin_dm.c'] + src += ['dev_pin_dm.c'] if GetDepend(['RT_USING_OFW']): - src += ['pin_ofw.c'] + src += ['dev_pin_ofw.c'] group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) diff --git a/rt-thread/components/drivers/pin/pin.c b/rt-thread/components/drivers/pin/dev_pin.c similarity index 96% rename from rt-thread/components/drivers/pin/pin.c rename to rt-thread/components/drivers/pin/dev_pin.c index 303fb25..4403557 100644 --- a/rt-thread/components/drivers/pin/pin.c +++ b/rt-thread/components/drivers/pin/dev_pin.c @@ -10,7 +10,7 @@ * 2022-04-29 WangQiang add pin operate command in MSH */ -#include +#include static struct rt_device_pin _hw_pin; static rt_ssize_t _pin_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) @@ -132,6 +132,16 @@ rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint8_t enabled) return -RT_ENOSYS; } +rt_err_t rt_pin_debounce(rt_base_t pin, rt_uint32_t debounce) +{ + RT_ASSERT(_hw_pin.ops != RT_NULL); + if (_hw_pin.ops->pin_debounce) + { + return _hw_pin.ops->pin_debounce(&_hw_pin.parent, pin, debounce); + } + return -RT_ENOSYS; +} + /* RT-Thread Hardware PIN APIs */ void rt_pin_mode(rt_base_t pin, rt_uint8_t mode) { diff --git a/rt-thread/components/drivers/pin/dev_pin_dm.c b/rt-thread/components/drivers/pin/dev_pin_dm.c new file mode 100644 index 0000000..90cca94 --- /dev/null +++ b/rt-thread/components/drivers/pin/dev_pin_dm.c @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include "dev_pin_dm.h" + +static rt_size_t pin_total_nr = 0; +static struct rt_spinlock pin_lock = {}; +static rt_list_t pin_nodes = RT_LIST_OBJECT_INIT(pin_nodes); + +static struct rt_device_pin *pin_device_find(rt_ubase_t pin) +{ + struct rt_device_pin *gpio = RT_NULL, *gpio_tmp; + + rt_spin_lock(&pin_lock); + + rt_list_for_each_entry(gpio_tmp, &pin_nodes, list) + { + if (pin >= gpio_tmp->pin_start && + pin - gpio_tmp->pin_start < gpio_tmp->pin_nr) + { + gpio = gpio_tmp; + break; + } + } + + rt_spin_unlock(&pin_lock); + + return gpio; +} + +static void pin_api_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode) +{ + struct rt_device_pin *gpio = pin_device_find(pin); + + if (gpio && gpio->ops->pin_mode) + { + gpio->ops->pin_mode(&gpio->parent, pin - gpio->pin_start, mode); + } +} + +static void pin_api_write(struct rt_device *device, rt_base_t pin, rt_uint8_t value) +{ + struct rt_device_pin *gpio = pin_device_find(pin); + + if (gpio && gpio->ops->pin_write) + { + gpio->ops->pin_write(&gpio->parent, pin - gpio->pin_start, value); + } +} + +static rt_ssize_t pin_api_read(struct rt_device *device, rt_base_t pin) +{ + struct rt_device_pin *gpio = pin_device_find(pin); + + if (gpio && gpio->ops->pin_read) + { + return gpio->ops->pin_read(&gpio->parent, pin - gpio->pin_start); + } + + return -RT_EINVAL; +} + +static rt_err_t pin_api_attach_irq(struct rt_device *device, rt_base_t pin, + rt_uint8_t mode, void (*hdr)(void *args), void *args) +{ + struct rt_device_pin *gpio = pin_device_find(pin); + + if (gpio) + { + rt_base_t pin_index = pin - gpio->pin_start; + + if (!gpio->ops->pin_attach_irq) + { + rt_err_t err; + struct rt_pin_irq_hdr *legacy_isr; + + if ((err = gpio->ops->pin_irq_mode(&gpio->parent, pin_index, mode))) + { + return err; + } + + legacy_isr = &gpio->legacy_isr[pin_index]; + legacy_isr->pin = pin_index; + legacy_isr->mode = mode; + legacy_isr->hdr = hdr; + legacy_isr->args = args; + + return RT_EOK; + } + else + { + return gpio->ops->pin_attach_irq(&gpio->parent, pin_index, mode, hdr, args); + } + } + + return -RT_EINVAL; +} + +static rt_err_t pin_api_detach_irq(struct rt_device *device, rt_base_t pin) +{ + struct rt_device_pin *gpio = pin_device_find(pin); + + if (gpio) + { + rt_base_t pin_index = pin - gpio->pin_start; + + if (!gpio->ops->pin_detach_irq) + { + struct rt_pin_irq_hdr *legacy_isr; + + legacy_isr = &gpio->legacy_isr[pin_index]; + rt_memset(legacy_isr, 0, sizeof(*legacy_isr)); + + return RT_EOK; + } + else + { + return gpio->ops->pin_detach_irq(&gpio->parent, pin); + } + } + + return -RT_EINVAL; +} + +static rt_err_t pin_api_irq_enable(struct rt_device *device, rt_base_t pin, + rt_uint8_t enabled) +{ + struct rt_device_pin *gpio = pin_device_find(pin); + + if (gpio && gpio->ops->pin_irq_enable) + { + return gpio->ops->pin_irq_enable(&gpio->parent, pin - gpio->pin_start, enabled); + } + + return -RT_EINVAL; +} + +static rt_base_t pin_api_get(const char *name) +{ + rt_base_t res = -RT_EINVAL; + struct rt_device_pin *gpio; + + rt_spin_lock(&pin_lock); + + rt_list_for_each_entry(gpio, &pin_nodes, list) + { + if (gpio->ops->pin_get && !(res = gpio->ops->pin_get(name))) + { + break; + } + } + + rt_spin_unlock(&pin_lock); + + return res; +} + +static rt_err_t pin_api_debounce(struct rt_device *device, rt_base_t pin, + rt_uint32_t debounce) +{ + struct rt_device_pin *gpio = pin_device_find(pin); + + if (gpio && gpio->ops->pin_debounce) + { + return gpio->ops->pin_debounce(&gpio->parent, pin - gpio->pin_start, debounce); + } + + return -RT_EINVAL; +} + +static rt_err_t pin_api_irq_mode(struct rt_device *device, rt_base_t pin, + rt_uint8_t mode) +{ + struct rt_device_pin *gpio = pin_device_find(pin); + + if (gpio && gpio->ops->pin_irq_mode) + { + return gpio->ops->pin_irq_mode(&gpio->parent, pin - gpio->pin_start, mode); + } + + return -RT_EINVAL; +} + +static const struct rt_pin_ops pin_api_dm_ops = +{ + .pin_mode = pin_api_mode, + .pin_write = pin_api_write, + .pin_read = pin_api_read, + .pin_attach_irq = pin_api_attach_irq, + .pin_detach_irq = pin_api_detach_irq, + .pin_irq_enable = pin_api_irq_enable, + .pin_get = pin_api_get, + .pin_debounce = pin_api_debounce, + .pin_irq_mode = pin_api_irq_mode, +}; + +rt_err_t pin_api_init(struct rt_device_pin *gpio, rt_size_t pin_nr) +{ + rt_err_t err = RT_EOK; + + if (!gpio || !gpio->ops) + { + return -RT_EINVAL; + } + + rt_spin_lock(&pin_lock); + + if (rt_list_isempty(&pin_nodes)) + { + rt_spin_unlock(&pin_lock); + rt_device_pin_register("gpio", &pin_api_dm_ops, RT_NULL); + rt_spin_lock(&pin_lock); + } + + gpio->pin_start = pin_total_nr; + gpio->pin_nr = pin_nr; + pin_total_nr += pin_nr; + + rt_list_init(&gpio->list); + rt_list_insert_before(&pin_nodes, &gpio->list); + + rt_spin_unlock(&pin_lock); + + return err; +} + +static void pin_dm_irq_mask(struct rt_pic_irq *pirq) +{ + struct rt_device_pin *gpio = pirq->pic->priv_data; + + gpio->ops->pin_irq_enable(&gpio->parent, pirq->hwirq, 0); +} + +static void pin_dm_irq_unmask(struct rt_pic_irq *pirq) +{ + struct rt_device_pin *gpio = pirq->pic->priv_data; + + gpio->ops->pin_irq_enable(&gpio->parent, pirq->hwirq, 1); +} + +static rt_err_t pin_dm_irq_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode) +{ + rt_uint8_t pin_mode; + struct rt_device_pin *gpio = pirq->pic->priv_data; + + switch (mode) + { + case RT_IRQ_MODE_EDGE_RISING: + pin_mode = PIN_IRQ_MODE_RISING; + break; + + case RT_IRQ_MODE_EDGE_FALLING: + pin_mode = PIN_IRQ_MODE_FALLING; + break; + + case RT_IRQ_MODE_EDGE_BOTH: + pin_mode = PIN_IRQ_MODE_RISING_FALLING; + break; + + case RT_IRQ_MODE_LEVEL_HIGH: + pin_mode = PIN_IRQ_MODE_HIGH_LEVEL; + break; + + case RT_IRQ_MODE_LEVEL_LOW: + pin_mode = PIN_IRQ_MODE_LOW_LEVEL; + break; + + default: + return -RT_ENOSYS; + } + + return gpio->ops->pin_irq_mode(&gpio->parent, pirq->hwirq, pin_mode); +} + +static int pin_dm_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) +{ + int irq = -1; + struct rt_device_pin *gpio = pic->priv_data; + struct rt_pic_irq *pirq = rt_pic_find_irq(pic, hwirq); + + if (pirq) + { + irq = rt_pic_config_irq(pic, hwirq, hwirq); + + if (irq >= 0) + { + rt_pic_cascade(pirq, gpio->irqchip.irq); + rt_pic_irq_set_triger_mode(irq, mode); + } + } + + return irq; +} + +static rt_err_t pin_dm_irq_parse(struct rt_pic *pic, + struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) +{ + rt_err_t err = RT_EOK; + + if (args->args_count == 2) + { + out_pirq->hwirq = args->args[0]; + out_pirq->mode = args->args[1] & RT_IRQ_MODE_MASK; + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +const static struct rt_pic_ops pin_dm_ops = +{ + .name = "GPIO", + .irq_enable = pin_dm_irq_mask, + .irq_disable = pin_dm_irq_unmask, + .irq_mask = pin_dm_irq_mask, + .irq_unmask = pin_dm_irq_unmask, + .irq_set_triger_mode = pin_dm_irq_set_triger_mode, + .irq_map = pin_dm_irq_map, + .irq_parse = pin_dm_irq_parse, +}; + +rt_err_t pin_pic_handle_isr(struct rt_device_pin *gpio, rt_base_t pin) +{ + rt_err_t err; + + if (gpio) + { + rt_ubase_t pin_index = pin; + struct rt_pin_irqchip *irqchip = &gpio->irqchip; + + if (pin_index < gpio->pin_nr) + { + struct rt_pic_irq *pirq; + struct rt_pin_irq_hdr *legacy_isr; + + pirq = rt_pic_find_irq(&irqchip->parent, pin_index); + + if (pirq->irq >= 0) + { + err = rt_pic_handle_isr(pirq); + } + else + { + err = -RT_EINVAL; + } + + legacy_isr = &gpio->legacy_isr[pin_index]; + + if (legacy_isr->hdr) + { + legacy_isr->hdr(legacy_isr->args); + } + } + else + { + err = -RT_EINVAL; + } + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t pin_pic_init(struct rt_device_pin *gpio, int pin_irq) +{ + rt_err_t err; + + if (gpio) + { + struct rt_pin_irqchip *irqchip = &gpio->irqchip; + struct rt_pic *pic = &irqchip->parent; + + irqchip->irq = pin_irq; + + if (!gpio->pin_nr) + { + return -RT_EINVAL; + } + + gpio->legacy_isr = rt_calloc(gpio->pin_nr, sizeof(*gpio->legacy_isr)); + + if (!gpio->legacy_isr) + { + return -RT_ENOMEM; + } + + pic->priv_data = gpio; + pic->ops = &pin_dm_ops; + /* Make sure the type of gpio for pic */ + gpio->parent.parent.type = RT_Object_Class_Device; + rt_pic_default_name(&irqchip->parent); + + err = rt_pic_linear_irq(pic, gpio->pin_nr); + rt_pic_user_extends(pic); + + err = RT_EOK; + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_ssize_t rt_pin_get_named_pin(struct rt_device *dev, const char *propname, int index, + rt_uint8_t *out_mode, rt_uint8_t *out_value) +{ + rt_ssize_t res = -RT_ENOSYS; + + RT_ASSERT(dev != RT_NULL); + +#ifdef RT_USING_OFW + if (dev->ofw_node) + { + res = rt_ofw_get_named_pin(dev->ofw_node, propname, index, out_mode, out_value); + } + else + { + res = -RT_EINVAL; + } +#endif /* RT_USING_OFW */ + + return res; +} + +rt_ssize_t rt_pin_get_named_pin_count(struct rt_device *dev, const char *propname) +{ + rt_ssize_t count = -RT_ENOSYS; + + RT_ASSERT(dev != RT_NULL); + +#ifdef RT_USING_OFW + if (dev->ofw_node) + { + count = rt_ofw_get_named_pin_count(dev->ofw_node, propname); + } + else + { + count = -RT_EINVAL; + } +#endif /* RT_USING_OFW */ + + return count; +} diff --git a/rt-thread/components/drivers/pin/pin_dm.h b/rt-thread/components/drivers/pin/dev_pin_dm.h similarity index 70% rename from rt-thread/components/drivers/pin/pin_dm.h rename to rt-thread/components/drivers/pin/dev_pin_dm.h index b5146a2..09374ca 100644 --- a/rt-thread/components/drivers/pin/pin_dm.h +++ b/rt-thread/components/drivers/pin/dev_pin_dm.h @@ -15,7 +15,9 @@ #include #include -rt_err_t pin_pic_handle_isr(struct rt_device_pin *gpio, rt_base_t pin); -rt_err_t pin_pic_init(struct rt_device_pin *gpio); +rt_err_t pin_api_init(struct rt_device_pin *gpio, rt_size_t pin_nr); -#endif /* __PIN_DM_H__ */ +rt_err_t pin_pic_init(struct rt_device_pin *gpio, int pin_irq); +rt_err_t pin_pic_handle_isr(struct rt_device_pin *gpio, rt_base_t pin); + +#endif /* __DEV_PIN_DM_H__ */ diff --git a/rt-thread/components/drivers/pin/pin_ofw.c b/rt-thread/components/drivers/pin/dev_pin_ofw.c similarity index 87% rename from rt-thread/components/drivers/pin/pin_ofw.c rename to rt-thread/components/drivers/pin/dev_pin_ofw.c index 29d29d4..fbf0538 100644 --- a/rt-thread/components/drivers/pin/pin_ofw.c +++ b/rt-thread/components/drivers/pin/dev_pin_ofw.c @@ -10,7 +10,7 @@ #include -#include "pin_dm.h" +#include "dev_pin_dm.h" static const char * const gpio_suffixes[] = { @@ -59,6 +59,12 @@ rt_ssize_t rt_ofw_get_named_pin(struct rt_ofw_node *np, const char *propname, in } pin_dev_np = pin_args.data; + + if (!rt_ofw_data(pin_dev_np)) + { + rt_platform_ofw_request(pin_dev_np); + } + pin_dev = rt_ofw_data(pin_dev_np); if (!pin_dev) @@ -111,11 +117,11 @@ rt_ssize_t rt_ofw_get_named_pin(struct rt_ofw_node *np, const char *propname, in if (out_value) { - if (flags == (PIN_ACTIVE_HIGH | PIN_PUSH_PULL)) + if ((flags & 1) == PIN_ACTIVE_HIGH) { value = PIN_HIGH; } - else if (flags == (PIN_ACTIVE_LOW | PIN_PUSH_PULL)) + else if ((flags & 1) == PIN_ACTIVE_LOW) { value = PIN_LOW; } @@ -124,14 +130,20 @@ rt_ssize_t rt_ofw_get_named_pin(struct rt_ofw_node *np, const char *propname, in _out_converts: rt_ofw_node_put(pin_dev_np); - if (out_mode) + if (pin >= 0) { - *out_mode = mode; - } + /* Get virtual pin */ + pin += pin_dev->pin_start; - if (out_value) - { - *out_value = value; + if (out_mode) + { + *out_mode = mode; + } + + if (out_value) + { + *out_value = value; + } } return pin; @@ -142,7 +154,7 @@ rt_ssize_t rt_ofw_get_named_pin_count(struct rt_ofw_node *np, const char *propna char gpios_name[64]; rt_ssize_t count = 0; - if (!np || !propname) + if (!np) { return -RT_EINVAL; } diff --git a/rt-thread/components/drivers/pin/pin_dm.c b/rt-thread/components/drivers/pin/pin_dm.c deleted file mode 100644 index 52dd47f..0000000 --- a/rt-thread/components/drivers/pin/pin_dm.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2006-2022, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2022-11-26 GuEe-GUI first version - */ - -#include "pin_dm.h" - -static void pin_dm_irq_mask(struct rt_pic_irq *pirq) -{ - struct rt_device_pin *gpio = pirq->pic->priv_data; - - gpio->ops->pin_irq_enable(&gpio->parent, pirq->hwirq, 0); -} - -static void pin_dm_irq_unmask(struct rt_pic_irq *pirq) -{ - struct rt_device_pin *gpio = pirq->pic->priv_data; - - gpio->ops->pin_irq_enable(&gpio->parent, pirq->hwirq, 1); -} - -static rt_err_t pin_dm_irq_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode) -{ - rt_uint8_t pin_mode; - struct rt_device_pin *gpio = pirq->pic->priv_data; - - switch (mode) - { - case RT_IRQ_MODE_EDGE_RISING: - pin_mode = PIN_IRQ_MODE_RISING; - break; - - case RT_IRQ_MODE_EDGE_FALLING: - pin_mode = PIN_IRQ_MODE_FALLING; - break; - - case RT_IRQ_MODE_EDGE_BOTH: - pin_mode = PIN_IRQ_MODE_RISING_FALLING; - break; - - case RT_IRQ_MODE_LEVEL_HIGH: - pin_mode = PIN_IRQ_MODE_HIGH_LEVEL; - break; - - case RT_IRQ_MODE_LEVEL_LOW: - pin_mode = PIN_IRQ_MODE_LOW_LEVEL; - break; - - default: - return -RT_ENOSYS; - } - - return gpio->ops->pin_irq_mode(&gpio->parent, pirq->hwirq, pin_mode); -} - -static int pin_dm_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) -{ - int irq = -1; - struct rt_device_pin *gpio = pic->priv_data; - struct rt_pic_irq *pirq = rt_pic_find_irq(pic, hwirq); - - if (pirq) - { - irq = rt_pic_config_irq(pic, hwirq, hwirq); - - if (irq >= 0) - { - rt_pic_cascade(pirq, gpio->irqchip.irq); - rt_pic_irq_set_triger_mode(irq, mode); - } - } - - return irq; -} - -static rt_err_t pin_dm_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq) -{ - rt_err_t err = RT_EOK; - - if (args->args_count == 2) - { - out_pirq->hwirq = args->args[0]; - out_pirq->mode = args->args[1] & RT_IRQ_MODE_MASK; - } - else - { - err = -RT_EINVAL; - } - - return err; -} - -static struct rt_pic_ops pin_dm_ops = -{ - .name = "GPIO", - .irq_enable = pin_dm_irq_mask, - .irq_disable = pin_dm_irq_unmask, - .irq_mask = pin_dm_irq_mask, - .irq_unmask = pin_dm_irq_unmask, - .irq_set_triger_mode = pin_dm_irq_set_triger_mode, - .irq_map = pin_dm_irq_map, - .irq_parse = pin_dm_irq_parse, -}; - -rt_err_t pin_pic_handle_isr(struct rt_device_pin *gpio, rt_base_t pin) -{ - rt_err_t err; - - if (gpio) - { - struct rt_pin_irqchip *irqchip = &gpio->irqchip; - - if (pin >= irqchip->pin_range[0] && pin <= irqchip->pin_range[1]) - { - struct rt_pic_irq *pirq; - - pirq = rt_pic_find_irq(&irqchip->parent, pin - irqchip->pin_range[0]); - - if (pirq->irq >= 0) - { - err = rt_pic_handle_isr(pirq); - } - else - { - err = -RT_EINVAL; - } - } - else - { - err = -RT_EINVAL; - } - } - else - { - err = -RT_EINVAL; - } - - return err; -} - -rt_err_t pin_pic_init(struct rt_device_pin *gpio) -{ - rt_err_t err; - - if (gpio) - { - struct rt_pin_irqchip *irqchip = &gpio->irqchip; - - if (irqchip->pin_range[0] >= 0 && irqchip->pin_range[1] >= irqchip->pin_range[0]) - { - struct rt_pic *pic = &irqchip->parent; - rt_size_t pin_nr = irqchip->pin_range[1] - irqchip->pin_range[0] + 1; - - pic->priv_data = gpio; - pic->ops = &pin_dm_ops; - /* Make sure the type of gpio for pic */ - gpio->parent.parent.type = RT_Object_Class_Device; - rt_pic_default_name(&irqchip->parent); - - err = rt_pic_linear_irq(pic, pin_nr); - rt_pic_user_extends(pic); - } - else - { - err = -RT_EINVAL; - } - } - else - { - err = -RT_EINVAL; - } - - return err; -} - -rt_ssize_t rt_pin_get_named_pin(struct rt_device *dev, const char *propname, int index, - rt_uint8_t *out_mode, rt_uint8_t *out_value) -{ - rt_ssize_t res = -RT_ENOSYS; - - RT_ASSERT(dev != RT_NULL); - -#ifdef RT_USING_OFW - if (dev->ofw_node) - { - res = rt_ofw_get_named_pin(dev->ofw_node, propname, index, out_mode, out_value); - } - else - { - res = -RT_EINVAL; - } -#endif /* RT_USING_OFW */ - - return res; -} - -rt_ssize_t rt_pin_get_named_pin_count(struct rt_device *dev, const char *propname) -{ - rt_ssize_t count = -RT_ENOSYS; - - RT_ASSERT(dev != RT_NULL); - -#ifdef RT_USING_OFW - if (dev->ofw_node) - { - count = rt_ofw_get_named_pin_count(dev->ofw_node, propname); - } - else - { - count = -RT_EINVAL; - } -#endif /* RT_USING_OFW */ - - return count; -} diff --git a/rt-thread/components/drivers/pinctrl/Kconfig b/rt-thread/components/drivers/pinctrl/Kconfig index 227c8a9..536049b 100644 --- a/rt-thread/components/drivers/pinctrl/Kconfig +++ b/rt-thread/components/drivers/pinctrl/Kconfig @@ -4,3 +4,6 @@ menuconfig RT_USING_PINCTRL depends on RT_USING_PIN default n +if RT_USING_PINCTRL + osource "$(SOC_DM_PINCTRL_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/pinctrl/pinctrl.c b/rt-thread/components/drivers/pinctrl/pinctrl.c index f327492..2fbfa7a 100644 --- a/rt-thread/components/drivers/pinctrl/pinctrl.c +++ b/rt-thread/components/drivers/pinctrl/pinctrl.c @@ -92,6 +92,11 @@ static rt_err_t ofw_pin_ctrl_confs_apply(struct rt_ofw_node *np, int index) if (pinctrl_np) { + if (!rt_ofw_data(pinctrl_np)) + { + rt_platform_ofw_request(pinctrl_np); + } + pinctrl = rt_ofw_data(pinctrl_np); rt_ofw_node_put(pinctrl_np); @@ -217,6 +222,8 @@ rt_err_t rt_pin_ctrl_confs_apply_by_name(struct rt_device *device, const char *n err = ofw_pin_ctrl_confs_apply_by_name(device->ofw_node, name); } #endif /* RT_USING_OFW */ + + RT_UNUSED(name); } else { diff --git a/rt-thread/components/drivers/pm/pm.c b/rt-thread/components/drivers/pm/pm.c index 024e301..b4ad3b2 100644 --- a/rt-thread/components/drivers/pm/pm.c +++ b/rt-thread/components/drivers/pm/pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -10,6 +10,7 @@ * 2019-04-28 Zero-Free improve PM mode and device ops interface * 2020-11-23 zhangsz update pm mode select * 2020-11-27 zhangsz update pm 2.0 + * 2024-07-04 wdfk-prog The device is registered and uninstalled by linked list */ #include @@ -77,9 +78,6 @@ rt_weak void rt_pm_exit_critical(rt_uint32_t ctx, rt_uint8_t sleep_mode) /* lptimer start */ static void pm_lptimer_start(struct rt_pm *pm, uint32_t timeout) { - if (_pm.ops == RT_NULL) - return; - if (_pm.ops->timer_start != RT_NULL) _pm.ops->timer_start(pm, timeout); } @@ -87,9 +85,6 @@ static void pm_lptimer_start(struct rt_pm *pm, uint32_t timeout) /* lptimer stop */ static void pm_lptimer_stop(struct rt_pm *pm) { - if (_pm.ops == RT_NULL) - return; - if (_pm.ops->timer_stop != RT_NULL) _pm.ops->timer_stop(pm); } @@ -97,9 +92,6 @@ static void pm_lptimer_stop(struct rt_pm *pm) /* lptimer get timeout tick */ static rt_tick_t pm_lptimer_get_timeout(struct rt_pm *pm) { - if (_pm.ops == RT_NULL) - return RT_TICK_MAX; - if (_pm.ops->timer_get_tick != RT_NULL) return _pm.ops->timer_get_tick(pm); @@ -109,9 +101,6 @@ static rt_tick_t pm_lptimer_get_timeout(struct rt_pm *pm) /* enter sleep mode */ static void pm_sleep(struct rt_pm *pm, uint8_t sleep_mode) { - if (_pm.ops == RT_NULL) - return; - if (_pm.ops->sleep != RT_NULL) _pm.ops->sleep(pm, sleep_mode); } @@ -119,17 +108,22 @@ static void pm_sleep(struct rt_pm *pm, uint8_t sleep_mode) /** * This function will suspend all registered devices */ -static int _pm_device_suspend(rt_uint8_t mode) +static rt_err_t _pm_device_suspend(rt_uint8_t mode) { - int index, ret = RT_EOK; + rt_err_t ret = RT_EOK; + struct rt_device_pm *device_pm = RT_NULL; + rt_slist_t *node = RT_NULL; - for (index = 0; index < _pm.device_pm_number; index++) + for (node = rt_slist_first(&_pm.device_list); node; node = rt_slist_next(node)) { - if (_pm.device_pm[index].ops->suspend != RT_NULL) + device_pm = rt_slist_entry(node, struct rt_device_pm, list); + if (device_pm->ops != RT_NULL && device_pm->ops->suspend != RT_NULL) { - ret = _pm.device_pm[index].ops->suspend(_pm.device_pm[index].device, mode); + ret = device_pm->ops->suspend(device_pm->device, mode); if(ret != RT_EOK) + { break; + } } } @@ -141,13 +135,15 @@ static int _pm_device_suspend(rt_uint8_t mode) */ static void _pm_device_resume(rt_uint8_t mode) { - int index; + struct rt_device_pm *device_pm = RT_NULL; + rt_slist_t *node = RT_NULL; - for (index = 0; index < _pm.device_pm_number; index++) + for (node = rt_slist_first(&_pm.device_list); node; node = rt_slist_next(node)) { - if (_pm.device_pm[index].ops->resume != RT_NULL) + device_pm = rt_slist_entry(node, struct rt_device_pm, list); + if (device_pm->ops != RT_NULL && device_pm->ops->resume != RT_NULL) { - _pm.device_pm[index].ops->resume(_pm.device_pm[index].device, mode); + device_pm->ops->resume(device_pm->device, mode); } } } @@ -157,13 +153,16 @@ static void _pm_device_resume(rt_uint8_t mode) */ static void _pm_device_frequency_change(rt_uint8_t mode) { - rt_uint32_t index; + struct rt_device_pm *device_pm = RT_NULL; + rt_slist_t *node = RT_NULL; - /* make the frequency change */ - for (index = 0; index < _pm.device_pm_number; index ++) + for (node = rt_slist_first(&_pm.device_list); node; node = rt_slist_next(node)) { - if (_pm.device_pm[index].ops->frequency_change != RT_NULL) - _pm.device_pm[index].ops->frequency_change(_pm.device_pm[index].device, mode); + device_pm = rt_slist_entry(node, struct rt_device_pm, list); + if (device_pm->ops->frequency_change != RT_NULL) + { + device_pm->ops->frequency_change(device_pm->device, mode); + } } } @@ -172,13 +171,16 @@ static void _pm_device_frequency_change(rt_uint8_t mode) */ static void _pm_frequency_scaling(struct rt_pm *pm) { - rt_base_t level; + rt_base_t level = 0; if (pm->flags & RT_PM_FREQUENCY_PENDING) { level = rt_hw_interrupt_disable(); /* change system runing mode */ - pm->ops->run(pm, pm->run_mode); + if(pm->ops->run != RT_NULL) + { + pm->ops->run(pm, pm->run_mode); + } /* changer device frequency */ _pm_device_frequency_change(pm->run_mode); pm->flags &= ~RT_PM_FREQUENCY_PENDING; @@ -288,6 +290,12 @@ static rt_bool_t _pm_device_check_idle(void) return RT_TRUE; } +/** + * @brief Get the next system wake-up time + * @note When used by default, it goes into STANDBY mode and sleeps forever. tickless external rewriting is required + * @param mode: sleep mode + * @retval timeout_tick + */ rt_weak rt_tick_t pm_timer_next_timeout_tick(rt_uint8_t mode) { switch (mode) @@ -344,6 +352,7 @@ rt_weak rt_uint8_t pm_get_sleep_threshold_mode(rt_uint8_t cur_mode, rt_tick_t ti else if (timeout_tick < PM_STANDBY_THRESHOLD_TIME) sleep_mode = PM_SLEEP_MODE_DEEP; } + cur_mode = sleep_mode; #else if (timeout_tick < PM_TICKLESS_THRESHOLD_TIME) { @@ -359,8 +368,8 @@ rt_weak rt_uint8_t pm_get_sleep_threshold_mode(rt_uint8_t cur_mode, rt_tick_t ti */ static void _pm_change_sleep_mode(struct rt_pm *pm) { - rt_tick_t timeout_tick, delta_tick; - rt_base_t level; + rt_tick_t timeout_tick = 0, delta_tick = 0; + rt_base_t level = 0; uint8_t sleep_mode = PM_SLEEP_MODE_DEEP; level = rt_pm_enter_critical(pm->sleep_mode); @@ -380,23 +389,27 @@ static void _pm_change_sleep_mode(struct rt_pm *pm) if (_pm.sleep_mode == PM_SLEEP_MODE_NONE) { - pm->ops->sleep(pm, PM_SLEEP_MODE_NONE); + pm_sleep(pm, PM_SLEEP_MODE_NONE); rt_pm_exit_critical(level, pm->sleep_mode); } else { /* Notify app will enter sleep mode */ if (_pm_notify.notify) + { _pm_notify.notify(RT_PM_ENTER_SLEEP, pm->sleep_mode, _pm_notify.data); + } /* Suspend all peripheral device */ #ifdef PM_ENABLE_SUSPEND_SLEEP_MODE - int ret = _pm_device_suspend(pm->sleep_mode); + rt_err_t ret = _pm_device_suspend(pm->sleep_mode); if (ret != RT_EOK) { _pm_device_resume(pm->sleep_mode); if (_pm_notify.notify) + { _pm_notify.notify(RT_PM_EXIT_SLEEP, pm->sleep_mode, _pm_notify.data); + } if (pm->sleep_mode > PM_SUSPEND_SLEEP_MODE) { pm->sleep_mode = PM_SUSPEND_SLEEP_MODE; @@ -419,14 +432,7 @@ static void _pm_change_sleep_mode(struct rt_pm *pm) if (pm->timer_mask & (0x01 << pm->sleep_mode)) { - if (timeout_tick == RT_TICK_MAX) - { - pm_lptimer_start(pm, RT_TICK_MAX); - } - else - { - pm_lptimer_start(pm, timeout_tick); - } + pm_lptimer_start(pm, timeout_tick); } } @@ -440,7 +446,9 @@ static void _pm_change_sleep_mode(struct rt_pm *pm) pm_lptimer_stop(pm); if (delta_tick) { - rt_tick_set(rt_tick_get() + delta_tick); + rt_interrupt_enter(); + rt_tick_increase_tick(delta_tick); + rt_interrupt_leave(); } } @@ -451,14 +459,6 @@ static void _pm_change_sleep_mode(struct rt_pm *pm) _pm_notify.notify(RT_PM_EXIT_SLEEP, pm->sleep_mode, _pm_notify.data); rt_pm_exit_critical(level, pm->sleep_mode); - - if (pm->timer_mask & (0x01 << pm->sleep_mode)) - { - if (delta_tick) - { - rt_timer_check(); - } - } } } @@ -467,8 +467,10 @@ static void _pm_change_sleep_mode(struct rt_pm *pm) */ void rt_system_power_manager(void) { - if (_pm_init_flag == 0) + if (_pm_init_flag == 0 || _pm.ops == RT_NULL) + { return; + } /* CPU frequency scaling according to the runing mode settings */ _pm_frequency_scaling(&_pm); @@ -483,22 +485,28 @@ void rt_system_power_manager(void) * * @param parameter the parameter of run mode or sleep mode */ -void rt_pm_request(rt_uint8_t mode) +rt_err_t rt_pm_request(rt_uint8_t mode) { rt_base_t level; struct rt_pm *pm; if (_pm_init_flag == 0) - return; + { + return -RT_EPERM; + } if (mode > (PM_SLEEP_MODE_MAX - 1)) - return; + { + return -RT_EINVAL; + } level = rt_hw_interrupt_disable(); pm = &_pm; if (pm->modes[mode] < 255) pm->modes[mode] ++; rt_hw_interrupt_enable(level); + + return RT_EOK; } /** @@ -508,22 +516,28 @@ void rt_pm_request(rt_uint8_t mode) * @param parameter the parameter of run mode or sleep mode * */ -void rt_pm_release(rt_uint8_t mode) +rt_err_t rt_pm_release(rt_uint8_t mode) { rt_base_t level; struct rt_pm *pm; if (_pm_init_flag == 0) - return; + { + return -RT_EPERM; + } if (mode > (PM_SLEEP_MODE_MAX - 1)) - return; + { + return -RT_EINVAL; + } level = rt_hw_interrupt_disable(); pm = &_pm; if (pm->modes[mode] > 0) pm->modes[mode] --; rt_hw_interrupt_enable(level); + + return RT_EOK; } /** @@ -533,21 +547,27 @@ void rt_pm_release(rt_uint8_t mode) * @param parameter the parameter of run mode or sleep mode * */ -void rt_pm_release_all(rt_uint8_t mode) +rt_err_t rt_pm_release_all(rt_uint8_t mode) { rt_base_t level; struct rt_pm *pm; if (_pm_init_flag == 0) - return; + { + return -RT_EPERM; + } if (mode > (PM_SLEEP_MODE_MAX - 1)) - return; + { + return -RT_EINVAL; + } level = rt_hw_interrupt_disable(); pm = &_pm; pm->modes[mode] = 0; rt_hw_interrupt_enable(level); + + return RT_EOK; } /** @@ -557,19 +577,25 @@ void rt_pm_release_all(rt_uint8_t mode) * @param module_id the application or device module id * @param mode the system power sleep mode */ -void rt_pm_module_request(uint8_t module_id, rt_uint8_t mode) +rt_err_t rt_pm_module_request(uint8_t module_id, rt_uint8_t mode) { rt_base_t level; struct rt_pm *pm; if (_pm_init_flag == 0) - return; + { + return -RT_EPERM; + } if (mode > (PM_SLEEP_MODE_MAX - 1)) - return; + { + return -RT_EINVAL; + } if (module_id > (PM_MODULE_MAX_ID - 1)) - return; + { + return -RT_EINVAL; + } level = rt_hw_interrupt_disable(); pm = &_pm; @@ -577,6 +603,8 @@ void rt_pm_module_request(uint8_t module_id, rt_uint8_t mode) if (pm->modes[mode] < 255) pm->modes[mode] ++; rt_hw_interrupt_enable(level); + + return RT_EOK; } /** @@ -587,19 +615,25 @@ void rt_pm_module_request(uint8_t module_id, rt_uint8_t mode) * @param mode the system power sleep mode * */ -void rt_pm_module_release(uint8_t module_id, rt_uint8_t mode) +rt_err_t rt_pm_module_release(uint8_t module_id, rt_uint8_t mode) { rt_base_t level; struct rt_pm *pm; if (_pm_init_flag == 0) - return; + { + return -RT_EPERM; + } if (mode > (PM_SLEEP_MODE_MAX - 1)) - return; + { + return -RT_EINVAL; + } if (module_id > (PM_MODULE_MAX_ID - 1)) - return; + { + return -RT_EINVAL; + } level = rt_hw_interrupt_disable(); pm = &_pm; @@ -608,6 +642,8 @@ void rt_pm_module_release(uint8_t module_id, rt_uint8_t mode) if (pm->modes[mode] == 0) pm->module_status[module_id].req_status = 0x00; rt_hw_interrupt_enable(level); + + return RT_EOK; } /** @@ -618,22 +654,28 @@ void rt_pm_module_release(uint8_t module_id, rt_uint8_t mode) * @param mode the system power sleep mode * */ -void rt_pm_module_release_all(uint8_t module_id, rt_uint8_t mode) +rt_err_t rt_pm_module_release_all(uint8_t module_id, rt_uint8_t mode) { rt_base_t level; struct rt_pm *pm; if (_pm_init_flag == 0) - return; + { + return -RT_EPERM; + } if (mode > (PM_SLEEP_MODE_MAX - 1)) - return; + { + return -RT_EINVAL; + } level = rt_hw_interrupt_disable(); pm = &_pm; pm->modes[mode] = 0; pm->module_status[module_id].req_status = 0x00; rt_hw_interrupt_enable(level); + + return RT_EOK; } /** @@ -644,23 +686,24 @@ void rt_pm_module_release_all(uint8_t module_id, rt_uint8_t mode) * * @return none */ -void rt_pm_sleep_request(rt_uint16_t module_id, rt_uint8_t mode) +rt_err_t rt_pm_sleep_request(rt_uint16_t module_id, rt_uint8_t mode) { rt_base_t level; if (module_id >= PM_MODULE_MAX_ID) { - return; + return -RT_EINVAL; } if (mode >= (PM_SLEEP_MODE_MAX - 1)) { - return; + return -RT_EINVAL; } level = rt_hw_interrupt_disable(); _pm.sleep_status[mode][module_id / 32] |= 1 << (module_id % 32); rt_hw_interrupt_enable(level); + return RT_EOK; } /** @@ -670,9 +713,9 @@ void rt_pm_sleep_request(rt_uint16_t module_id, rt_uint8_t mode) * * @return NULL */ -void rt_pm_sleep_none_request(rt_uint16_t module_id) +rt_err_t rt_pm_sleep_none_request(rt_uint16_t module_id) { - rt_pm_sleep_request(module_id, PM_SLEEP_MODE_NONE); + return rt_pm_sleep_request(module_id, PM_SLEEP_MODE_NONE); } /** @@ -682,9 +725,9 @@ void rt_pm_sleep_none_request(rt_uint16_t module_id) * * @return NULL */ -void rt_pm_sleep_idle_request(rt_uint16_t module_id) +rt_err_t rt_pm_sleep_idle_request(rt_uint16_t module_id) { - rt_pm_sleep_request(module_id, PM_SLEEP_MODE_IDLE); + return rt_pm_sleep_request(module_id, PM_SLEEP_MODE_IDLE); } /** @@ -694,9 +737,9 @@ void rt_pm_sleep_idle_request(rt_uint16_t module_id) * * @return NULL */ -void rt_pm_sleep_light_request(rt_uint16_t module_id) +rt_err_t rt_pm_sleep_light_request(rt_uint16_t module_id) { - rt_pm_sleep_request(module_id, PM_SLEEP_MODE_LIGHT); + return rt_pm_sleep_request(module_id, PM_SLEEP_MODE_LIGHT); } /** @@ -707,23 +750,24 @@ void rt_pm_sleep_light_request(rt_uint16_t module_id) * * @return NULL */ -void rt_pm_sleep_release(rt_uint16_t module_id, rt_uint8_t mode) +rt_err_t rt_pm_sleep_release(rt_uint16_t module_id, rt_uint8_t mode) { rt_base_t level; if (module_id >= PM_MODULE_MAX_ID) { - return; + return -RT_EINVAL; } if (mode >= (PM_SLEEP_MODE_MAX - 1)) { - return; + return -RT_EINVAL; } level = rt_hw_interrupt_disable(); _pm.sleep_status[mode][module_id / 32] &= ~(1 << (module_id % 32)); rt_hw_interrupt_enable(level); + return RT_EOK; } /** @@ -733,9 +777,9 @@ void rt_pm_sleep_release(rt_uint16_t module_id, rt_uint8_t mode) * * @return none */ -void rt_pm_sleep_none_release(rt_uint16_t module_id) +rt_err_t rt_pm_sleep_none_release(rt_uint16_t module_id) { - rt_pm_sleep_release(module_id, PM_SLEEP_MODE_NONE); + return rt_pm_sleep_release(module_id, PM_SLEEP_MODE_NONE); } /** @@ -745,9 +789,9 @@ void rt_pm_sleep_none_release(rt_uint16_t module_id) * * @return none */ -void rt_pm_sleep_idle_release(rt_uint16_t module_id) +rt_err_t rt_pm_sleep_idle_release(rt_uint16_t module_id) { - rt_pm_sleep_release(module_id, PM_SLEEP_MODE_IDLE); + return rt_pm_sleep_release(module_id, PM_SLEEP_MODE_IDLE); } /** @@ -757,9 +801,9 @@ void rt_pm_sleep_idle_release(rt_uint16_t module_id) * * @return none */ -void rt_pm_sleep_light_release(rt_uint16_t module_id) +rt_err_t rt_pm_sleep_light_release(rt_uint16_t module_id) { - rt_pm_sleep_release(module_id, PM_SLEEP_MODE_LIGHT); + return rt_pm_sleep_release(module_id, PM_SLEEP_MODE_LIGHT); } /** @@ -770,24 +814,15 @@ void rt_pm_sleep_light_release(rt_uint16_t module_id) */ void rt_pm_device_register(struct rt_device *device, const struct rt_device_pm_ops *ops) { - rt_base_t level; struct rt_device_pm *device_pm; - RT_DEBUG_NOT_IN_INTERRUPT; - - level = rt_hw_interrupt_disable(); - - device_pm = (struct rt_device_pm *)RT_KERNEL_REALLOC(_pm.device_pm, - (_pm.device_pm_number + 1) * sizeof(struct rt_device_pm)); + device_pm = RT_KERNEL_MALLOC(sizeof(struct rt_device_pm)); if (device_pm != RT_NULL) { - _pm.device_pm = device_pm; - _pm.device_pm[_pm.device_pm_number].device = device; - _pm.device_pm[_pm.device_pm_number].ops = ops; - _pm.device_pm_number += 1; + rt_slist_append(&_pm.device_list, &device_pm->list); + device_pm->device = device; + device_pm->ops = ops; } - - rt_hw_interrupt_enable(level); } /** @@ -797,32 +832,18 @@ void rt_pm_device_register(struct rt_device *device, const struct rt_device_pm_o */ void rt_pm_device_unregister(struct rt_device *device) { - rt_base_t level; - rt_uint32_t index; - RT_DEBUG_NOT_IN_INTERRUPT; - - level = rt_hw_interrupt_disable(); - - for (index = 0; index < _pm.device_pm_number; index ++) + struct rt_device_pm *device_pm = RT_NULL; + rt_slist_t *node = RT_NULL; + for (node = rt_slist_first(&_pm.device_list); node; node = rt_slist_next(node)) { - if (_pm.device_pm[index].device == device) + device_pm = rt_slist_entry(node, struct rt_device_pm, list); + if (device_pm->device == device) { - /* remove current entry */ - for (; index < _pm.device_pm_number - 1; index ++) - { - _pm.device_pm[index] = _pm.device_pm[index + 1]; - } - - _pm.device_pm[_pm.device_pm_number - 1].device = RT_NULL; - _pm.device_pm[_pm.device_pm_number - 1].ops = RT_NULL; - - _pm.device_pm_number -= 1; - /* break out and not touch memory */ + rt_slist_remove(&_pm.device_list, &device_pm->list); + RT_KERNEL_FREE(device_pm); break; } } - - rt_hw_interrupt_enable(level); } /** @@ -914,10 +935,11 @@ static rt_err_t _rt_pm_device_control(rt_device_t dev, return RT_EOK; } -int rt_pm_run_enter(rt_uint8_t mode) +rt_err_t rt_pm_run_enter(rt_uint8_t mode) { - rt_base_t level; - struct rt_pm *pm; + rt_base_t level = 0; + struct rt_pm *pm = RT_NULL; + rt_err_t ret = RT_EOK; if (_pm_init_flag == 0) return -RT_EIO; @@ -925,12 +947,16 @@ int rt_pm_run_enter(rt_uint8_t mode) if (mode > PM_RUN_MODE_MAX) return -RT_EINVAL; - level = rt_hw_interrupt_disable(); pm = &_pm; + + level = rt_hw_interrupt_disable(); if (mode < pm->run_mode) { /* change system runing mode */ - pm->ops->run(pm, mode); + if(pm->ops != RT_NULL && pm->ops->run != RT_NULL) + { + pm->ops->run(pm, mode); + } /* changer device frequency */ _pm_device_frequency_change(mode); } @@ -941,7 +967,7 @@ int rt_pm_run_enter(rt_uint8_t mode) pm->run_mode = mode; rt_hw_interrupt_enable(level); - return RT_EOK; + return ret; } #ifdef RT_USING_DEVICE_OPS @@ -1004,7 +1030,8 @@ void rt_system_pm_init(const struct rt_pm_ops *ops, pm->ops = ops; pm->device_pm = RT_NULL; - pm->device_pm_number = 0; + + rt_slist_init(&pm->device_list); #if IDLE_THREAD_STACK_SIZE <= 256 #error "[pm.c ERR] IDLE Stack Size Too Small!" diff --git a/rt-thread/components/drivers/regulator/Kconfig b/rt-thread/components/drivers/regulator/Kconfig new file mode 100644 index 0000000..99bfdac --- /dev/null +++ b/rt-thread/components/drivers/regulator/Kconfig @@ -0,0 +1,23 @@ +menuconfig RT_USING_REGULATOR + bool "Using Voltage and Current Regulator" + select RT_USING_ADT + select RT_USING_ADT_REF + depends on RT_USING_DM + default n + +config RT_REGULATOR_FIXED + bool "Fixed regulator support" + depends on RT_USING_REGULATOR + depends on RT_USING_PIN + depends on RT_USING_PINCTRL + default y + +config RT_REGULATOR_GPIO + bool "GPIO regulator support" + depends on RT_USING_REGULATOR + depends on RT_USING_PIN + default y + +if RT_USING_REGULATOR + osource "$(SOC_DM_REGULATOR_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/regulator/SConscript b/rt-thread/components/drivers/regulator/SConscript new file mode 100644 index 0000000..29b567f --- /dev/null +++ b/rt-thread/components/drivers/regulator/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_REGULATOR']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['regulator.c', 'regulator_dm.c'] + +if GetDepend(['RT_REGULATOR_FIXED']): + src += ['regulator-fixed.c'] + +if GetDepend(['RT_REGULATOR_GPIO']): + src += ['regulator-gpio.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/regulator/regulator-fixed.c b/rt-thread/components/drivers/regulator/regulator-fixed.c new file mode 100644 index 0000000..5f5773b --- /dev/null +++ b/rt-thread/components/drivers/regulator/regulator-fixed.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include "regulator_dm.h" + +struct regulator_fixed +{ + struct rt_regulator_node parent; + struct rt_regulator_param param; + + rt_base_t enable_pin; + const char *input_supply; +}; + +#define raw_to_regulator_fixed(raw) rt_container_of(raw, struct regulator_fixed, parent) + +static rt_err_t regulator_fixed_enable(struct rt_regulator_node *reg_np) +{ + struct regulator_fixed *rf = raw_to_regulator_fixed(reg_np); + struct rt_regulator_param *param = &rf->param; + + if (rf->enable_pin < 0 || param->always_on) + { + return RT_EOK; + } + + rt_pin_mode(rf->enable_pin, PIN_MODE_OUTPUT); + rt_pin_write(rf->enable_pin, param->enable_active_high ? PIN_HIGH : PIN_LOW); + + return RT_EOK; +} + +static rt_err_t regulator_fixed_disable(struct rt_regulator_node *reg_np) +{ + struct regulator_fixed *rf = raw_to_regulator_fixed(reg_np); + struct rt_regulator_param *param = &rf->param; + + if (rf->enable_pin < 0 || param->always_on) + { + return RT_EOK; + } + + rt_pin_mode(rf->enable_pin, PIN_MODE_OUTPUT); + rt_pin_write(rf->enable_pin, param->enable_active_high ? PIN_LOW: PIN_HIGH); + + return RT_EOK; +} + +static rt_bool_t regulator_fixed_is_enabled(struct rt_regulator_node *reg_np) +{ + rt_uint8_t active; + struct regulator_fixed *rf = raw_to_regulator_fixed(reg_np); + struct rt_regulator_param *param = &rf->param; + + if (rf->enable_pin < 0 || param->always_on) + { + return RT_TRUE; + } + + rt_pin_mode(rf->enable_pin, PIN_MODE_INPUT); + active = rt_pin_read(rf->enable_pin); + + if (param->enable_active_high) + { + return active == PIN_HIGH; + } + + return active == PIN_LOW; +} + +static int regulator_fixed_get_voltage(struct rt_regulator_node *reg_np) +{ + struct regulator_fixed *rf = raw_to_regulator_fixed(reg_np); + + return rf->param.min_uvolt + (rf->param.max_uvolt - rf->param.min_uvolt) / 2; +} + +static const struct rt_regulator_ops regulator_fixed_ops = +{ + .enable = regulator_fixed_enable, + .disable = regulator_fixed_disable, + .is_enabled = regulator_fixed_is_enabled, + .get_voltage = regulator_fixed_get_voltage, +}; + +static rt_err_t regulator_fixed_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t val; + struct rt_device *dev = &pdev->parent; + struct regulator_fixed *rf = rt_calloc(1, sizeof(*rf)); + struct rt_regulator_node *rnp; + + if (!rf) + { + return -RT_ENOMEM; + } + + regulator_ofw_parse(dev->ofw_node, &rf->param); + + rnp = &rf->parent; + rnp->supply_name = rf->param.name; + rnp->ops = ®ulator_fixed_ops; + rnp->param = &rf->param; + rnp->dev = &pdev->parent; + + rf->enable_pin = rt_pin_get_named_pin(dev, "enable", 0, RT_NULL, RT_NULL); + + if (rf->enable_pin < 0) + { + rf->enable_pin = rt_pin_get_named_pin(dev, RT_NULL, 0, RT_NULL, RT_NULL); + } + + if (rf->enable_pin < 0) + { + rf->enable_pin = -1; + } + + rt_pin_ctrl_confs_apply(dev, 0); + + if (!rt_dm_dev_prop_read_u32(dev, "startup-delay-us", &val)) + { + rf->param.enable_delay = val; + } + + if (!rt_dm_dev_prop_read_u32(dev, "off-on-delay-us", &val)) + { + rf->param.off_on_delay = val; + } + + if ((err = rt_regulator_register(rnp))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_free(rf); + + return err; +} + +static const struct rt_ofw_node_id regulator_fixed_ofw_ids[] = +{ + { .compatible = "regulator-fixed" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver regulator_fixed_driver = +{ + .name = "reg-fixed-voltage", + .ids = regulator_fixed_ofw_ids, + + .probe = regulator_fixed_probe, +}; + +static int regulator_fixed_register(void) +{ + rt_platform_driver_register(®ulator_fixed_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(regulator_fixed_register); diff --git a/rt-thread/components/drivers/regulator/regulator-gpio.c b/rt-thread/components/drivers/regulator/regulator-gpio.c new file mode 100644 index 0000000..0698318 --- /dev/null +++ b/rt-thread/components/drivers/regulator/regulator-gpio.c @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include + +#include "regulator_dm.h" + +struct regulator_gpio_state +{ + rt_uint32_t value; + rt_uint32_t gpios; +}; + +struct regulator_gpio_desc +{ + rt_base_t pin; + rt_uint32_t flags; +}; + +struct regulator_gpio +{ + struct rt_regulator_node parent; + + rt_base_t enable_pin; + + rt_size_t pins_nr; + struct regulator_gpio_desc *pins_desc; + + int state; + rt_size_t states_nr; + struct regulator_gpio_state *states; + + const char *input_supply; + rt_uint32_t startup_delay; + rt_uint32_t off_on_delay; + rt_bool_t enabled_at_boot; + struct rt_regulator_param param; +}; + +#define raw_to_regulator_gpio(raw) rt_container_of(raw, struct regulator_gpio, parent) + +static rt_err_t regulator_gpio_enable(struct rt_regulator_node *reg_np) +{ + struct regulator_gpio *rg = raw_to_regulator_gpio(reg_np); + struct rt_regulator_param *param = &rg->param; + + if (param->always_on) + { + return RT_EOK; + } + + if (rg->enable_pin >= 0) + { + rt_pin_mode(rg->enable_pin, PIN_MODE_OUTPUT); + rt_pin_write(rg->enable_pin, param->enable_active_high ? PIN_HIGH : PIN_LOW); + } + + return RT_EOK; +} + +static rt_err_t regulator_gpio_disable(struct rt_regulator_node *reg_np) +{ + struct regulator_gpio *rg = raw_to_regulator_gpio(reg_np); + struct rt_regulator_param *param = &rg->param; + + if (param->always_on) + { + return RT_EOK; + } + + if (rg->enable_pin >= 0) + { + rt_pin_mode(rg->enable_pin, PIN_MODE_OUTPUT); + rt_pin_write(rg->enable_pin, param->enable_active_high ? PIN_LOW : PIN_HIGH); + } + + return RT_EOK; +} + +static rt_bool_t regulator_gpio_is_enabled(struct rt_regulator_node *reg_np) +{ + struct regulator_gpio *rg = raw_to_regulator_gpio(reg_np); + struct rt_regulator_param *param = &rg->param; + + if (param->always_on) + { + return RT_TRUE; + } + + if (rg->enable_pin >= 0) + { + rt_uint8_t active_val = param->enable_active_high ? PIN_LOW : PIN_HIGH; + + rt_pin_mode(rg->enable_pin, PIN_MODE_INPUT); + return rt_pin_read(rg->enable_pin) == active_val; + } + + return RT_TRUE; +} + +static rt_err_t regulator_gpio_set_voltage(struct rt_regulator_node *reg_np, + int min_uvolt, int max_uvolt) +{ + int target = 0, best_val = RT_REGULATOR_UVOLT_INVALID; + struct regulator_gpio *rg = raw_to_regulator_gpio(reg_np); + + for (int i = 0; i < rg->states_nr; ++i) + { + struct regulator_gpio_state *state = &rg->states[i]; + + if (state->value < best_val && + state->value >= min_uvolt && + state->value <= max_uvolt) + { + target = state->gpios; + best_val = state->value; + } + } + + if (best_val == RT_REGULATOR_UVOLT_INVALID) + { + return -RT_EINVAL; + } + + for (int i = 0; i < rg->pins_nr; ++i) + { + int state = (target >> i) & 1; + struct regulator_gpio_desc *gpiod = &rg->pins_desc[i]; + + rt_pin_mode(gpiod->pin, PIN_MODE_OUTPUT); + rt_pin_write(gpiod->pin, gpiod->flags == PIND_OUT_HIGH ? state : !state); + } + + rg->state = target; + + return RT_EOK; +} + +static int regulator_gpio_get_voltage(struct rt_regulator_node *reg_np) +{ + struct regulator_gpio *rg = raw_to_regulator_gpio(reg_np); + + for (int i = 0; i < rg->states_nr; ++i) + { + if (rg->states[i].gpios == rg->state) + { + return rg->states[i].value; + } + } + + return -RT_EINVAL; +} + +static const struct rt_regulator_ops regulator_gpio_ops = +{ + .enable = regulator_gpio_enable, + .disable = regulator_gpio_disable, + .is_enabled = regulator_gpio_is_enabled, + .set_voltage = regulator_gpio_set_voltage, + .get_voltage = regulator_gpio_get_voltage, +}; + +static rt_err_t regulator_gpio_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct regulator_gpio *rg = rt_calloc(1, sizeof(*rg)); + struct rt_regulator_node *rgp; + + if (!rg) + { + return -RT_ENOMEM; + } + + regulator_ofw_parse(dev->ofw_node, &rg->param); + + rgp = &rg->parent; + rgp->supply_name = rg->param.name; + rgp->ops = ®ulator_gpio_ops; + rgp->param = &rg->param; + rgp->dev = &pdev->parent; + + rt_dm_dev_prop_read_u32(dev, "startup-delay-us", &rg->startup_delay); + rt_dm_dev_prop_read_u32(dev, "off-on-delay-us", &rg->off_on_delay); + + /* GPIO flags are ignored, we check by enable-active-high */ + rg->enable_pin = rt_pin_get_named_pin(dev, "enable", 0, RT_NULL, RT_NULL); + + if (rg->enable_pin < 0 && rg->enable_pin != -RT_EEMPTY) + { + err = rg->enable_pin; + goto _fail; + } + + rg->pins_nr = rt_pin_get_named_pin_count(dev, "gpios"); + + if (rg->pins_nr > 0) + { + rg->pins_desc = rt_malloc(sizeof(*rg->pins_desc) * rg->pins_nr); + + if (!rg->pins_desc) + { + err = -RT_ENOMEM; + + goto _fail; + } + + for (int i = 0; i < rg->pins_nr; ++i) + { + rt_uint32_t val; + struct regulator_gpio_desc *gpiod = &rg->pins_desc[i]; + + gpiod->pin = rt_pin_get_named_pin(dev, RT_NULL, i, RT_NULL, RT_NULL); + + if (gpiod->pin < 0) + { + err = gpiod->pin; + goto _fail; + } + + if (rt_dm_dev_prop_read_u32_index(dev, "gpios-states", i, &val) < 0) + { + gpiod->flags = PIND_OUT_HIGH; + } + else + { + gpiod->flags = val ? PIND_OUT_HIGH : PIND_OUT_LOW; + } + + if (gpiod->flags == PIND_OUT_HIGH) + { + rg->state |= 1 << i; + } + } + } + + rg->states_nr = rt_dm_dev_prop_count_of_u32(dev, "states") / 2; + + if (rg->states_nr < 0) + { + err = -RT_EIO; + + goto _fail; + } + + rg->states = rt_malloc(sizeof(*rg->states) * rg->states_nr); + + if (!rg->states) + { + err = -RT_ENOMEM; + + goto _fail; + } + + for (int i = 0; i < rg->states_nr; ++i) + { + rt_dm_dev_prop_read_u32_index(dev, "states", i * 2, &rg->states[i].value); + rt_dm_dev_prop_read_u32_index(dev, "states", i * 2 + 1, &rg->states[i].gpios); + } + + if ((err = rt_regulator_register(rgp))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + if (rg->pins_desc) + { + rt_free(rg->pins_desc); + } + if (rg->states) + { + rt_free(rg->states); + } + rt_free(rg); + + return err; +} + +static const struct rt_ofw_node_id regulator_gpio_ofw_ids[] = +{ + { .compatible = "regulator-gpio" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver regulator_gpio_driver = +{ + .name = "regulator-gpio", + .ids = regulator_gpio_ofw_ids, + + .probe = regulator_gpio_probe, +}; + +static int regulator_gpio_register(void) +{ + rt_platform_driver_register(®ulator_gpio_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(regulator_gpio_register); diff --git a/rt-thread/components/drivers/regulator/regulator.c b/rt-thread/components/drivers/regulator/regulator.c new file mode 100644 index 0000000..29071e9 --- /dev/null +++ b/rt-thread/components/drivers/regulator/regulator.c @@ -0,0 +1,629 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.regulator" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include + +struct rt_regulator +{ + struct rt_regulator_node *reg_np; +}; + +static struct rt_spinlock _regulator_lock = { 0 }; + +static rt_err_t regulator_enable(struct rt_regulator_node *reg_np); +static rt_err_t regulator_disable(struct rt_regulator_node *reg_np); + +rt_err_t rt_regulator_register(struct rt_regulator_node *reg_np) +{ + const struct rt_regulator_param *param; + + if (!reg_np || !reg_np->dev || !reg_np->param || !reg_np->ops) + { + return -RT_EINVAL; + } + + rt_list_init(®_np->list); + rt_list_init(®_np->children_nodes); + rt_list_init(®_np->notifier_nodes); + rt_ref_init(®_np->ref); + rt_atomic_store(®_np->enabled_count, 0); + + param = reg_np->param; + + reg_np->parent = RT_NULL; + +#ifdef RT_USING_OFW + if (reg_np->dev->ofw_node) + { + rt_ofw_data(reg_np->dev->ofw_node) = reg_np; + } +#endif /* RT_USING_OFW */ + + if (param->boot_on || param->always_on) + { + regulator_enable(reg_np); + } + + return RT_EOK; +} + +rt_err_t rt_regulator_unregister(struct rt_regulator_node *reg_np) +{ + rt_err_t err = RT_EOK; + + if (!reg_np) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + if (rt_atomic_load(®_np->enabled_count) != 0) + { + err = -RT_EBUSY; + + LOG_E("%s was enabled by consumer", reg_np->supply_name); + + goto _unlock; + } + + if (!(reg_np->param->boot_on || reg_np->param->always_on)) + { + regulator_disable(reg_np); + } + + if (!rt_list_isempty(®_np->children_nodes) || rt_ref_read(®_np->ref) > 1) + { + err = -RT_EBUSY; + + goto _unlock; + } + + reg_np->parent = RT_NULL; + rt_list_remove(®_np->list); + +_unlock: + rt_hw_spin_unlock(&_regulator_lock.lock); + + return err; +} + +rt_err_t rt_regulator_notifier_register(struct rt_regulator *reg, + struct rt_regulator_notifier *notifier) +{ + struct rt_regulator_node *reg_np; + + if (!reg || !notifier) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + reg_np = reg->reg_np; + notifier->regulator = reg; + + rt_list_init(¬ifier->list); + rt_list_insert_after(®_np->notifier_nodes, ¬ifier->list); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return RT_EOK; +} + +rt_err_t rt_regulator_notifier_unregister(struct rt_regulator *reg, + struct rt_regulator_notifier *notifier) +{ + if (!reg || !notifier) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + rt_list_remove(¬ifier->list); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return RT_EOK; +} + +static rt_err_t regulator_notifier_call_chain(struct rt_regulator_node *reg_np, + rt_ubase_t msg, void *data) +{ + rt_err_t err = RT_EOK; + struct rt_regulator_notifier *notifier; + rt_list_t *head = ®_np->notifier_nodes; + + if (rt_list_isempty(head)) + { + return err; + } + + rt_list_for_each_entry(notifier, head, list) + { + err = notifier->callback(notifier, msg, data); + + if (err == -RT_EIO) + { + break; + } + } + + return err; +} + +static rt_uint32_t regulator_get_enable_time(struct rt_regulator_node *reg_np) +{ + if (reg_np->param->enable_delay) + { + return reg_np->param->enable_delay; + } + + if (reg_np->ops->enable_time) + { + return reg_np->ops->enable_time(reg_np); + } + + return 0; +} + +static void regulator_delay(rt_uint32_t delay) +{ + rt_uint32_t ms = delay / 1000; + rt_uint32_t us = delay % 1000; + + if (ms > 0) + { + /* + * For small enough values, handle super-millisecond + * delays in the usleep_range() call below. + */ + if (ms < 20) + { + us += ms * 1000; + } + else if (rt_thread_self()) + { + rt_thread_mdelay(ms); + } + else + { + rt_hw_us_delay(ms * 1000); + } + } + + /* + * Give the scheduler some room to coalesce with any other + * wakeup sources. For delays shorter than 10 us, don't even + * bother setting up high-resolution timers and just busy-loop. + */ + if (us >= 10) + { + rt_hw_us_delay((us + 100) >> 1); + } + else + { + rt_hw_us_delay(us); + } +} + +static rt_err_t regulator_enable(struct rt_regulator_node *reg_np) +{ + rt_err_t err = RT_EOK; + rt_uint32_t enable_delay = regulator_get_enable_time(reg_np); + + if (reg_np->ops->enable) + { + err = reg_np->ops->enable(reg_np); + + if (!err) + { + if (enable_delay) + { + regulator_delay(enable_delay); + } + + rt_atomic_add(®_np->enabled_count, 1); + err = regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_ENABLE, RT_NULL); + } + } + + if (!err && reg_np->parent) + { + err = regulator_enable(reg_np->parent); + } + + return err; +} + +rt_err_t rt_regulator_enable(struct rt_regulator *reg) +{ + rt_err_t err; + + if (!reg) + { + return -RT_EINVAL; + } + + if (rt_regulator_is_enabled(reg)) + { + return RT_EOK; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + err = regulator_enable(reg->reg_np); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return err; +} + +static rt_err_t regulator_disable(struct rt_regulator_node *reg_np) +{ + rt_err_t err = RT_EOK; + + if (reg_np->ops->disable) + { + err = reg_np->ops->disable(reg_np); + + if (!err) + { + if (reg_np->param->off_on_delay) + { + regulator_delay(reg_np->param->off_on_delay); + } + + err = regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_DISABLE, RT_NULL); + } + } + + if (!err && reg_np->parent) + { + err = regulator_disable(reg_np->parent); + } + + return err; +} + +rt_err_t rt_regulator_disable(struct rt_regulator *reg) +{ + rt_err_t err; + + if (!reg) + { + return -RT_EINVAL; + } + + if (!rt_regulator_is_enabled(reg)) + { + return RT_EOK; + } + + if (rt_atomic_load(®->reg_np->enabled_count) != 0) + { + rt_atomic_sub(®->reg_np->enabled_count, 1); + + return RT_EOK; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + err = regulator_disable(reg->reg_np); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return err; +} + +rt_bool_t rt_regulator_is_enabled(struct rt_regulator *reg) +{ + if (!reg) + { + return -RT_EINVAL; + } + + if (reg->reg_np->ops->is_enabled) + { + return reg->reg_np->ops->is_enabled(reg->reg_np); + } + + return rt_atomic_load(®->reg_np->enabled_count) > 0; +} + +static rt_err_t regulator_set_voltage(struct rt_regulator_node *reg_np, int min_uvolt, int max_uvolt) +{ + rt_err_t err = RT_EOK; + + if (reg_np->ops->set_voltage) + { + union rt_regulator_notifier_args args; + + RT_ASSERT(reg_np->ops->get_voltage != RT_NULL); + + args.old_uvolt = reg_np->ops->get_voltage(reg_np); + args.min_uvolt = min_uvolt; + args.max_uvolt = max_uvolt; + + err = regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_VOLTAGE_CHANGE, &args); + + if (!err) + { + err = reg_np->ops->set_voltage(reg_np, min_uvolt, max_uvolt); + } + + if (err) + { + regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_VOLTAGE_CHANGE_ERR, + (void *)(rt_base_t)args.old_uvolt); + } + } + + if (!err && reg_np->parent) + { + err = regulator_set_voltage(reg_np->parent, min_uvolt, max_uvolt); + } + + return err; +} + +rt_bool_t rt_regulator_is_supported_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt) +{ + const struct rt_regulator_param *param; + + RT_ASSERT(reg != RT_NULL); + + param = reg->reg_np->param; + + if (!param) + { + return RT_FALSE; + } + + return param->min_uvolt <= min_uvolt && param->max_uvolt >= max_uvolt; +} + +rt_err_t rt_regulator_set_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt) +{ + rt_err_t err; + + if (!reg) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + err = regulator_set_voltage(reg->reg_np, min_uvolt, max_uvolt); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return err; +} + +int rt_regulator_get_voltage(struct rt_regulator *reg) +{ + int uvolt = RT_REGULATOR_UVOLT_INVALID; + struct rt_regulator_node *reg_np; + + if (!reg) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + reg_np = reg->reg_np; + + if (reg_np->ops->get_voltage) + { + uvolt = reg_np->ops->get_voltage(reg->reg_np); + } + else + { + uvolt = -RT_ENOSYS; + } + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return uvolt; +} + +rt_err_t rt_regulator_set_mode(struct rt_regulator *reg, rt_uint32_t mode) +{ + rt_err_t err; + struct rt_regulator_node *reg_np; + + if (!reg) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + reg_np = reg->reg_np; + + if (reg_np->ops->set_mode) + { + err = reg_np->ops->set_mode(reg_np, mode); + } + else + { + err = -RT_ENOSYS; + } + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return err; +} + +rt_int32_t rt_regulator_get_mode(struct rt_regulator *reg) +{ + rt_int32_t mode; + struct rt_regulator_node *reg_np; + + if (!reg) + { + return -RT_EINVAL; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + reg_np = reg->reg_np; + + if (reg_np->ops->get_mode) + { + mode = reg_np->ops->get_mode(reg_np); + } + else + { + mode = -RT_ENOSYS; + } + + rt_hw_spin_unlock(&_regulator_lock.lock); + + return mode; +} + +static void regulator_check_parent(struct rt_regulator_node *reg_np) +{ + if (reg_np->parent) + { + return; + } + else + { + #ifdef RT_USING_OFW + rt_phandle parent_phandle = 0; + struct rt_ofw_node *np = reg_np->dev->ofw_node; + + while (np) + { + if (rt_ofw_prop_read_u32(np, "vin-supply", &parent_phandle)) + { + break; + } + + if (!(np = rt_ofw_find_node_by_phandle(parent_phandle))) + { + break; + } + + if (!(reg_np->parent = rt_ofw_data(np))) + { + LOG_W("%s parent ofw node = %s not init", + reg_np->supply_name, rt_ofw_node_full_name(np)); + + rt_ofw_node_put(np); + break; + } + + rt_list_insert_after(®_np->parent->children_nodes, ®_np->list); + rt_ofw_node_put(np); + } + #endif + } +} + +struct rt_regulator *rt_regulator_get(struct rt_device *dev, const char *id) +{ + struct rt_regulator *reg = RT_NULL; + struct rt_regulator_node *reg_np = RT_NULL; + + if (!dev || !id) + { + reg = rt_err_ptr(-RT_EINVAL); + goto _end; + } + +#ifdef RT_USING_OFW + if (dev->ofw_node) + { + rt_phandle supply_phandle; + struct rt_ofw_node *np = dev->ofw_node; + char supply_name[64]; + + rt_snprintf(supply_name, sizeof(supply_name), "%s-supply", id); + + if (rt_ofw_prop_read_u32(np, supply_name, &supply_phandle)) + { + goto _end; + } + + if (!(np = rt_ofw_find_node_by_phandle(supply_phandle))) + { + reg = rt_err_ptr(-RT_EIO); + goto _end; + } + + if (!rt_ofw_data(np)) + { + rt_platform_ofw_request(np); + } + + reg_np = rt_ofw_data(np); + rt_ofw_node_put(np); + } +#endif + + if (!reg_np) + { + reg = rt_err_ptr(-RT_ENOSYS); + goto _end; + } + + rt_hw_spin_lock(&_regulator_lock.lock); + + regulator_check_parent(reg_np); + + rt_hw_spin_unlock(&_regulator_lock.lock); + + reg = rt_calloc(1, sizeof(*reg)); + + if (!reg) + { + reg = rt_err_ptr(-RT_ENOMEM); + goto _end; + } + + reg->reg_np = reg_np; + rt_ref_get(®_np->ref); + +_end: + return reg; +} + +static void regulator_release(struct rt_ref *r) +{ + struct rt_regulator_node *reg_np = rt_container_of(r, struct rt_regulator_node, ref); + + rt_regulator_unregister(reg_np); +} + +void rt_regulator_put(struct rt_regulator *reg) +{ + if (!reg) + { + return; + } + + rt_ref_put(®->reg_np->ref, ®ulator_release); + rt_free(reg); +} diff --git a/rt-thread/components/drivers/regulator/regulator_dm.c b/rt-thread/components/drivers/regulator/regulator_dm.c new file mode 100644 index 0000000..d8096c4 --- /dev/null +++ b/rt-thread/components/drivers/regulator/regulator_dm.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#include "regulator_dm.h" + +#ifdef RT_USING_OFW +rt_err_t regulator_ofw_parse(struct rt_ofw_node *np, struct rt_regulator_param *param) +{ + rt_uint32_t pval; + + param->name = rt_ofw_prop_read_raw(np, "regulator-name", RT_NULL); + + if (!rt_ofw_prop_read_u32(np, "regulator-min-microvolt", &pval)) + { + param->min_uvolt = pval; + } + + if (!rt_ofw_prop_read_u32(np, "regulator-max-microvolt", &pval)) + { + param->max_uvolt = pval; + } + + if (!rt_ofw_prop_read_u32(np, "regulator-min-microamp", &pval)) + { + param->min_uamp = pval; + } + + if (!rt_ofw_prop_read_u32(np, "regulator-max-microamp", &pval)) + { + param->max_uamp = pval; + } + + if (!rt_ofw_prop_read_u32(np, "regulator-ramp-delay", &pval)) + { + param->ramp_delay = pval; + } + + if (!rt_ofw_prop_read_u32(np, "regulator-enable-ramp-delay", &pval)) + { + param->enable_delay = pval; + } + + param->enable_active_high = rt_ofw_prop_read_bool(np, "enable-active-high"); + param->boot_on = rt_ofw_prop_read_bool(np, "regulator-boot-on"); + param->always_on = rt_ofw_prop_read_bool(np, "regulator-always-on"); + param->soft_start = rt_ofw_prop_read_bool(np, "regulator-soft-start"); + param->pull_down = rt_ofw_prop_read_bool(np, "regulator-pull-down"); + param->over_current_protection = rt_ofw_prop_read_bool(np, "regulator-over-current-protection"); + + return RT_EOK; +} +#endif /* RT_USING_OFW */ diff --git a/rt-thread/components/drivers/regulator/regulator_dm.h b/rt-thread/components/drivers/regulator/regulator_dm.h new file mode 100644 index 0000000..207bb12 --- /dev/null +++ b/rt-thread/components/drivers/regulator/regulator_dm.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-09-23 GuEe-GUI first version + */ + +#ifndef __REGULATOR_DM_H__ +#define __REGULATOR_DM_H__ + +#include +#include + +#ifdef RT_USING_OFW +rt_err_t regulator_ofw_parse(struct rt_ofw_node *np, struct rt_regulator_param *param); +#else +rt_inline rt_err_t regulator_ofw_parse(struct rt_ofw_node *np, struct rt_regulator_param *param); +{ + return RT_EOK; +} +#endif /* RT_USING_OFW */ + +#endif /* __REGULATOR_DM_H__ */ diff --git a/rt-thread/components/drivers/reset/Kconfig b/rt-thread/components/drivers/reset/Kconfig new file mode 100644 index 0000000..ccba575 --- /dev/null +++ b/rt-thread/components/drivers/reset/Kconfig @@ -0,0 +1,14 @@ +menuconfig RT_USING_RESET + bool "Using Reset Controller support" + depends on RT_USING_DM + depends on RT_USING_OFW + default n + +config RT_RESET_SIMPLE + bool "Simple Reset Controller Driver" + depends on RT_USING_RESET + default n + +if RT_USING_RESET + osource "$(SOC_DM_RESET_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/reset/SConscript b/rt-thread/components/drivers/reset/SConscript new file mode 100644 index 0000000..8e64f78 --- /dev/null +++ b/rt-thread/components/drivers/reset/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_RESET']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['reset.c'] + +if GetDepend(['RT_RESET_SIMPLE']): + src += ['reset-simple.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/reset/reset-simple.c b/rt-thread/components/drivers/reset/reset-simple.c new file mode 100644 index 0000000..e66cb8e --- /dev/null +++ b/rt-thread/components/drivers/reset/reset-simple.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include "reset-simple.h" + +struct reset_simple_data +{ + rt_uint32_t reg_offset; + rt_bool_t active_low; + rt_bool_t status_active_low; +}; + +#define raw_to_reset_simple(raw) rt_container_of(raw, struct reset_simple, parent) + +static rt_err_t reset_simple_update(struct reset_simple *rsts, int id, rt_bool_t assert) +{ + rt_uint32_t reg; + rt_ubase_t level; + int reg_width = sizeof(rt_uint32_t); + int bank = id / (reg_width * 8); + int offset = id % (reg_width * 8); + + level = rt_spin_lock_irqsave(&rsts->lock); + + reg = HWREG32(rsts->mmio_base + (bank * reg_width)); + + if (assert ^ rsts->active_low) + { + reg |= RT_BIT(offset); + } + else + { + reg &= ~RT_BIT(offset); + } + + HWREG32(rsts->mmio_base + (bank * reg_width)) = reg; + + rt_spin_unlock_irqrestore(&rsts->lock, level); + + return RT_EOK; +} + +static rt_err_t reset_simple_assert(struct rt_reset_control *rstc) +{ + struct reset_simple *rsts = raw_to_reset_simple(rstc); + + return reset_simple_update(rsts, rstc->id, RT_TRUE); +} + +static rt_err_t reset_simple_deassert(struct rt_reset_control *rstc) +{ + struct reset_simple *rsts = raw_to_reset_simple(rstc); + + return reset_simple_update(rsts, rstc->id, RT_FALSE); +} + +static rt_err_t reset_simple_reset(struct rt_reset_control *rstc) +{ + rt_err_t err; + struct reset_simple *rsts = raw_to_reset_simple(rstc); + + if (!rsts->reset_us) + { + return -RT_ENOSYS; + } + + if ((err = reset_simple_assert(rstc))) + { + return err; + } + + rt_hw_us_delay(rsts->reset_us + (rsts->reset_us >> 1)); + + return reset_simple_deassert(rstc); +} + +static int reset_simple_status(struct rt_reset_control *rstc) +{ + rt_uint32_t value; + int reg_width = sizeof(rt_uint32_t); + int bank = rstc->id / (reg_width * 8); + int offset = rstc->id % (reg_width * 8); + struct reset_simple *rsts = raw_to_reset_simple(rstc); + + value = HWREG32(rsts->mmio_base + (bank * reg_width)); + + return !(value & RT_BIT(offset)) ^ !rsts->status_active_low; +} + +const struct rt_reset_control_ops reset_simple_ops = +{ + .reset = reset_simple_reset, + .assert = reset_simple_assert, + .deassert = reset_simple_deassert, + .status = reset_simple_status, +}; + +static rt_err_t reset_simple_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_reset_controller *rstcer; + struct rt_device *dev = &pdev->parent; + const struct reset_simple_data *rsts_data = pdev->id->data; + struct reset_simple *rsts = rt_calloc(1, sizeof(*rsts)); + + if (!rsts) + { + return -RT_ENOMEM; + } + + rsts->mmio_base = rt_dm_dev_iomap(dev, 0); + + if (!rsts->mmio_base) + { + err = -RT_EIO; + goto _fail; + } + + rt_spin_lock_init(&rsts->lock); + + rstcer = &rsts->parent; + + rstcer->priv = rsts; + rstcer->ofw_node = dev->ofw_node; + rstcer->ops = &reset_simple_ops; + + if ((err = rt_reset_controller_register(rstcer))) + { + goto _fail; + } + + if (rsts_data) + { + rsts->mmio_base += rsts_data->reg_offset; + rsts->active_low = rsts_data->active_low; + rsts->status_active_low = rsts_data->status_active_low; + } + + return RT_EOK; + +_fail: + if (rsts->mmio_base) + { + rt_iounmap(rsts->mmio_base); + } + + rt_free(rsts); + + return err; +} + +static const struct reset_simple_data reset_simple_socfpga = +{ + .reg_offset = 0x20, + .status_active_low = RT_TRUE, +}; + +static const struct reset_simple_data reset_simple_active_low = +{ + .active_low = RT_TRUE, + .status_active_low = RT_TRUE, +}; + +static const struct rt_ofw_node_id reset_simple_ofw_ids[] = +{ + { .compatible = "altr,stratix10-rst-mgr", .data = &reset_simple_socfpga }, + { .compatible = "st,stm32-rcc", }, + { .compatible = "allwinner,sun6i-a31-clock-reset", .data = &reset_simple_active_low }, + { .compatible = "zte,zx296718-reset", .data = &reset_simple_active_low }, + { .compatible = "aspeed,ast2400-lpc-reset" }, + { .compatible = "aspeed,ast2500-lpc-reset" }, + { .compatible = "aspeed,ast2600-lpc-reset" }, + { .compatible = "bitmain,bm1880-reset", .data = &reset_simple_active_low }, + { .compatible = "brcm,bcm4908-misc-pcie-reset", .data = &reset_simple_active_low }, + { .compatible = "snps,dw-high-reset" }, + { .compatible = "snps,dw-low-reset", .data = &reset_simple_active_low }, + { .compatible = "sophgo,sg2042-reset", .data = &reset_simple_active_low }, + { /* sentinel */ } +}; + +static struct rt_platform_driver reset_simple_driver = +{ + .name = "reset-simple", + .ids = reset_simple_ofw_ids, + + .probe = reset_simple_probe, +}; + +static int reset_simple_register(void) +{ + rt_platform_driver_register(&reset_simple_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(reset_simple_register); diff --git a/rt-thread/components/drivers/reset/reset-simple.h b/rt-thread/components/drivers/reset/reset-simple.h new file mode 100644 index 0000000..8223d69 --- /dev/null +++ b/rt-thread/components/drivers/reset/reset-simple.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __RESET_SIMPLE_H__ +#define __RESET_SIMPLE_H__ + +#include +#include + +struct reset_simple +{ + struct rt_reset_controller parent; + + void *mmio_base; + + /* + * If true, bits are cleared to assert the reset. + * Otherwise, bits are set to assert the reset. + */ + rt_bool_t active_low; + /* + * If true, bits read back as cleared while the reset is asserted. + * Otherwise, bits read back as set while the reset is asserted. + */ + rt_bool_t status_active_low; + + /* + * Minimum delay in microseconds needed that needs to be + * waited for between an assert and a deassert to reset the device. + * If multiple consumers with different delay + * requirements are connected to this controller, it must + * be the largest minimum delay. 0 means that such a delay is + * unknown and the reset operation is unsupported. + */ + rt_uint32_t reset_us; + + /* protect registers during read-modify-write cycles */ + struct rt_spinlock lock; +}; + +extern const struct rt_reset_control_ops reset_simple_ops; + +#endif /* __RESET_SIMPLE_H__ */ diff --git a/rt-thread/components/drivers/reset/reset.c b/rt-thread/components/drivers/reset/reset.c new file mode 100644 index 0000000..290d2a7 --- /dev/null +++ b/rt-thread/components/drivers/reset/reset.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.reset" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include + +struct reset_control_array +{ + struct rt_reset_control captain; + + rt_size_t count; + struct rt_reset_control *rstcs[]; +}; + +#define reset_control_to_array(rstc) rt_container_of(rstc, struct reset_control_array, captain) + +rt_err_t rt_reset_controller_register(struct rt_reset_controller *rstcer) +{ + if (!rstcer) + { + return -RT_EINVAL; + } + +#if RT_NAME_MAX > 0 + rt_strncpy(rstcer->parent.name, RT_RESET_CONTROLLER_OBJ_NAME, RT_NAME_MAX); +#else + rstcer->parent.name = RT_RESET_CONTROLLER_OBJ_NAME; +#endif + + rt_list_init(&rstcer->rstc_nodes); + rt_spin_lock_init(&rstcer->spinlock); + + if (rstcer->ofw_node) + { + if (!rt_ofw_data(rstcer->ofw_node)) + { + rt_ofw_data(rstcer->ofw_node) = rstcer; + } + } + + return RT_EOK; +} + +rt_err_t rt_reset_controller_unregister(struct rt_reset_controller *rstcer) +{ + rt_err_t err = RT_EOK; + + if (!rstcer) + { + return -RT_EINVAL; + } + + rt_spin_lock(&rstcer->spinlock); + + if (!rt_list_isempty(&rstcer->rstc_nodes)) + { + err = -RT_EBUSY; + goto _out_lock; + } + +_out_lock: + rt_spin_unlock(&rstcer->spinlock); + + return err; +} + +rt_err_t rt_reset_control_reset(struct rt_reset_control *rstc) +{ + rt_err_t err; + + if (!rstc) + { + return RT_EOK; + } + + if (rstc->rstcer->ops->reset) + { + if ((err = rstc->rstcer->ops->reset(rstc))) + { + return err; + } + } + + if (rstc->is_array) + { + struct reset_control_array *rstc_arr = reset_control_to_array(rstc); + + for (int i = 0; i < rstc_arr->count; ++i) + { + if ((err = rt_reset_control_reset(rstc_arr->rstcs[i]))) + { + return err; + } + } + } + + return RT_EOK; +} + +rt_err_t rt_reset_control_assert(struct rt_reset_control *rstc) +{ + rt_err_t err; + + if (!rstc) + { + return RT_EOK; + } + + if (rstc->rstcer->ops->assert) + { + if ((err = rstc->rstcer->ops->assert(rstc))) + { + return err; + } + } + + if (rstc->is_array) + { + struct reset_control_array *rstc_arr = reset_control_to_array(rstc); + + for (int i = 0; i < rstc_arr->count; ++i) + { + if ((err = rt_reset_control_assert(rstc_arr->rstcs[i]))) + { + if (rstc->rstcer->ops->deassert) + { + rstc->rstcer->ops->deassert(rstc); + } + + while (i --> 0) + { + rt_reset_control_deassert(rstc_arr->rstcs[i]); + } + + return err; + } + } + } + + return RT_EOK; +} + +rt_err_t rt_reset_control_deassert(struct rt_reset_control *rstc) +{ + rt_err_t err; + + if (!rstc) + { + return RT_EOK; + } + + if (rstc->rstcer->ops->deassert) + { + if ((err = rstc->rstcer->ops->deassert(rstc))) + { + return err; + } + } + + if (rstc->is_array) + { + struct reset_control_array *rstc_arr = reset_control_to_array(rstc); + + for (int i = 0; i < rstc_arr->count; ++i) + { + if ((err = rt_reset_control_deassert(rstc_arr->rstcs[i]))) + { + if (rstc->rstcer->ops->assert) + { + rstc->rstcer->ops->assert(rstc); + } + + while (i --> 0) + { + rt_reset_control_assert(rstc_arr->rstcs[i]); + } + + return err; + } + } + } + + return RT_EOK; +} + +int rt_reset_control_status(struct rt_reset_control *rstc) +{ + if (!rstc) + { + return RT_EOK; + } + + if (rstc->rstcer->ops->status) + { + return rstc->rstcer->ops->status(rstc); + } + + return -RT_ENOSYS; +} + +static void reset_free(struct rt_reset_control *rstc) +{ + if (rstc->is_array) + { + struct reset_control_array *rstc_arr = reset_control_to_array(rstc); + + for (int i = 0; i < rstc_arr->count; ++i) + { + rt_reset_control_put(rstc_arr->rstcs[i]); + } + } + + rt_free(rstc); +} + +struct rt_reset_control *rt_reset_control_get_array(struct rt_device *dev) +{ + return rt_ofw_get_reset_control_array(dev->ofw_node); +} + +struct rt_reset_control *rt_reset_control_get_by_index(struct rt_device *dev, int index) +{ + return rt_ofw_get_reset_control_by_index(dev->ofw_node, index); +} + +struct rt_reset_control *rt_reset_control_get_by_name(struct rt_device *dev, const char *name) +{ + return rt_ofw_get_reset_control_by_name(dev->ofw_node, name); +} + +void rt_reset_control_put(struct rt_reset_control *rstc) +{ + struct rt_reset_controller *rstcer; + + if (!rstc) + { + return; + } + + rstcer = rstc->rstcer; + + rt_spin_lock(&rstcer->spinlock); + + rt_list_remove(&rstc->list); + + rt_spin_unlock(&rstcer->spinlock); + + reset_free(rstc); +} + +static struct rt_reset_control *ofw_get_reset_control(struct rt_ofw_node *np, int index, + const char *name, rt_bool_t is_array) +{ + rt_err_t err = RT_EOK; + struct rt_reset_control *rstc; + struct rt_ofw_cell_args reset_args = {}; + struct rt_reset_controller *rstcer = RT_NULL; + + if (is_array) + { + rt_size_t rstc_nr; + struct reset_control_array *rstc_arr; + + rstc_nr = rt_ofw_count_phandle_cells(np, "resets", "#reset-cells"); + + if (!rstc_nr) + { + return RT_NULL; + } + + rstc_arr = rt_calloc(1, sizeof(*rstc_arr) + sizeof(struct rt_reset_control *) * rstc_nr); + + if (!rstc_arr) + { + LOG_E("No memory to create %s[%d] reset control", + rt_ofw_node_full_name(np), index); + + return rt_err_ptr(-RT_ENOMEM); + } + + rstc_arr->count = rstc_nr - 1; + + for (int i = 0; i < rstc_arr->count; ++i) + { + rstc_arr->rstcs[i] = ofw_get_reset_control(np, i + 1, RT_NULL, RT_FALSE); + + if (rt_is_err(rstc_arr->rstcs[i])) + { + err = rt_ptr_err(rstc_arr->rstcs[i]); + + while (i --> 0) + { + rt_reset_control_put(rstc_arr->rstcs[i]); + } + + rt_free(rstc_arr); + + return rt_err_ptr(err); + } + } + + rstc = &rstc_arr->captain; + rstc->is_array = RT_TRUE; + } + else + { + rstc = rt_calloc(1, sizeof(*rstc)); + + if (!rstc) + { + LOG_E("No memory to create %s[%d] reset control", + rt_ofw_node_full_name(np), index); + + return rt_err_ptr(-RT_ENOMEM); + } + } + + if (!rt_ofw_parse_phandle_cells(np, "resets", "#reset-cells", index, &reset_args)) + { + void *rt_data; + struct rt_object *obj; + struct rt_ofw_node *reset_np = reset_args.data; + + if (!rt_ofw_data(reset_np)) + { + rt_platform_ofw_request(reset_np); + } + + rt_data = rt_ofw_data(reset_np); + + if (rt_data && (obj = rt_ofw_parse_object(reset_args.data, + RT_RESET_CONTROLLER_OBJ_NAME, "#reset-cells"))) + { + rstcer = rt_container_of(obj, struct rt_reset_controller, parent); + } + + rt_ofw_node_put(reset_np); + + if (!rstcer) + { + err = -RT_EINVAL; + goto _fail; + } + } + else + { + /* Not reset */ + goto _fail; + } + + if (!name && rt_ofw_prop_read_bool(np, "reset-names")) + { + rt_ofw_prop_read_string_index(np, "reset-names", index, &name); + } + + rstc->con_id = name; + rstc->rstcer = rstcer; + + if (rstcer->ops->ofw_parse) + { + err = rstcer->ops->ofw_parse(rstc, &reset_args); + + if (err) + { + LOG_E("Parse %s reset control error = %s", + rt_ofw_node_full_name(np), rt_strerror(err)); + + goto _fail; + } + } + + rstc->id = reset_args.args[0]; + + rt_list_init(&rstc->list); + + rt_spin_lock(&rstcer->spinlock); + + rt_list_insert_after(&rstcer->rstc_nodes, &rstc->list); + + rt_spin_unlock(&rstcer->spinlock); + + return rstc; + +_fail: + if (rstc && !rstc->is_array) + { + rt_free(rstc); + } + + return rt_err_ptr(err); +} + +struct rt_reset_control *rt_ofw_get_reset_control_array(struct rt_ofw_node *np) +{ + return ofw_get_reset_control(np, 0, RT_NULL, RT_TRUE); +} + +struct rt_reset_control *rt_ofw_get_reset_control_by_index(struct rt_ofw_node *np, int index) +{ + return ofw_get_reset_control(np, index, RT_NULL, RT_FALSE); +} + +struct rt_reset_control *rt_ofw_get_reset_control_by_name(struct rt_ofw_node *np, const char *name) +{ + if (np) + { + int index = rt_ofw_prop_index_of_string(np, "reset-names", name); + + if (index >= 0) + { + return ofw_get_reset_control(np, index, name, RT_FALSE); + } + } + + return RT_NULL; +} diff --git a/rt-thread/components/drivers/rtc/Kconfig b/rt-thread/components/drivers/rtc/Kconfig index e4310cb..a8b3366 100644 --- a/rt-thread/components/drivers/rtc/Kconfig +++ b/rt-thread/components/drivers/rtc/Kconfig @@ -7,6 +7,20 @@ config RT_USING_RTC bool "Using RTC alarm" default n + if RT_USING_ALARM + config RT_ALARM_STACK_SIZE + int "stack size for alarm thread" + default 2048 + + config RT_ALARM_TIMESLICE + int "timeslice for alarm thread" + default 5 + + config RT_ALARM_PRIORITY + int "priority for alarm thread" + default 10 + endif + config RT_USING_SOFT_RTC bool "Using software simulation RTC device" default n diff --git a/rt-thread/components/drivers/rtc/SConscript b/rt-thread/components/drivers/rtc/SConscript index fe0d5e9..f1e180f 100644 --- a/rt-thread/components/drivers/rtc/SConscript +++ b/rt-thread/components/drivers/rtc/SConscript @@ -7,11 +7,11 @@ CPPPATH = [cwd + '/../include'] group = [] if GetDepend(['RT_USING_RTC']): - src = src + ['rtc.c'] + src = src + ['dev_rtc.c'] if GetDepend(['RT_USING_ALARM']): - src = src + ['alarm.c'] + src = src + ['dev_alarm.c'] if GetDepend(['RT_USING_SOFT_RTC']): - src = src + ['soft_rtc.c'] + src = src + ['dev_soft_rtc.c'] group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_RTC'], CPPPATH = CPPPATH) diff --git a/rt-thread/components/drivers/rtc/alarm.c b/rt-thread/components/drivers/rtc/dev_alarm.c similarity index 97% rename from rt-thread/components/drivers/rtc/alarm.c rename to rt-thread/components/drivers/rtc/dev_alarm.c index b78528a..aabc415 100644 --- a/rt-thread/components/drivers/rtc/alarm.c +++ b/rt-thread/components/drivers/rtc/dev_alarm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -9,6 +9,7 @@ * 2013-05-17 aozima initial alarm event & mutex in system init. * 2020-10-15 zhangsz add alarm flags hour minute second. * 2020-11-09 zhangsz fix alarm set when modify rtc time. + * 2024-09-29 milo make internal thread's attributes configurable. */ #include @@ -23,6 +24,15 @@ #endif #if (defined(RT_USING_RTC) && defined(RT_USING_ALARM)) +#ifndef RT_ALARM_STACK_SIZE +#define RT_ALARM_STACK_SIZE 2048 +#endif +#ifndef RT_ALARM_TIMESLICE +#define RT_ALARM_TIMESLICE 5 +#endif +#ifndef RT_ALARM_PRIORITY +#define RT_ALARM_PRIORITY 10 +#endif static struct rt_alarm_container _container; rt_inline rt_uint32_t alarm_mkdaysec(struct tm *time) @@ -789,7 +799,9 @@ int rt_alarm_system_init(void) tid = rt_thread_create("alarmsvc", rt_alarmsvc_thread_init, RT_NULL, - 2048, 10, 5); + RT_ALARM_STACK_SIZE, + RT_ALARM_PRIORITY, + RT_ALARM_TIMESLICE); if (tid != RT_NULL) rt_thread_startup(tid); diff --git a/rt-thread/components/drivers/rtc/rtc.c b/rt-thread/components/drivers/rtc/dev_rtc.c similarity index 99% rename from rt-thread/components/drivers/rtc/rtc.c rename to rt-thread/components/drivers/rtc/dev_rtc.c index 4f4fbc6..f789036 100644 --- a/rt-thread/components/drivers/rtc/rtc.c +++ b/rt-thread/components/drivers/rtc/dev_rtc.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #ifdef RT_USING_RTC diff --git a/rt-thread/components/drivers/rtc/soft_rtc.c b/rt-thread/components/drivers/rtc/dev_soft_rtc.c similarity index 87% rename from rt-thread/components/drivers/rtc/soft_rtc.c rename to rt-thread/components/drivers/rtc/dev_soft_rtc.c index 0eab72e..cd5a37e 100644 --- a/rt-thread/components/drivers/rtc/soft_rtc.c +++ b/rt-thread/components/drivers/rtc/dev_soft_rtc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -35,7 +35,6 @@ #endif static struct rt_work rtc_sync_work; -static rt_device_t source_device = RT_NULL; static struct rt_device soft_rtc_dev; static rt_tick_t init_tick; @@ -82,18 +81,6 @@ static void set_rtc_time(time_t t) #endif } -static void _source_device_control(int cmd, void *args) -{ - if (source_device == RT_NULL) - return; - - if (rt_device_open(source_device, 0) == RT_EOK) - { - rt_device_control(source_device, cmd, args); - rt_device_close(source_device); - } -} - static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) { time_t *t; @@ -114,7 +101,6 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) { t = (time_t *) args; set_rtc_time(*t); - _source_device_control(RT_DEVICE_CTRL_RTC_SET_TIME, t); break; } #ifdef RT_USING_ALARM @@ -143,7 +129,6 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) rt_ktime_boottime_get_us(&_tv); set_rtc_time(tv->tv_sec); init_tv.tv_usec = tv->tv_usec - _tv.tv_usec; - _source_device_control(RT_DEVICE_CTRL_RTC_SET_TIME, &(tv->tv_sec)); break; } case RT_DEVICE_CTRL_RTC_GET_TIMESPEC: @@ -162,7 +147,6 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) rt_ktime_boottime_get_ns(&_ts); set_rtc_time(ts->tv_sec); init_ts.tv_nsec = ts->tv_nsec - _ts.tv_nsec; - _source_device_control(RT_DEVICE_CTRL_RTC_SET_TIME, &(ts->tv_sec)); break; } case RT_DEVICE_CTRL_RTC_GET_TIMERES: @@ -187,7 +171,6 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) rt_tick_t tick = rt_tick_get() - init_tick; set_rtc_time(tv->tv_sec); init_tv.tv_usec = tv->tv_usec - ((tick % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND)); - _source_device_control(RT_DEVICE_CTRL_RTC_SET_TIME, &(tv->tv_sec)); break; } case RT_DEVICE_CTRL_RTC_GET_TIMERES: @@ -227,6 +210,9 @@ static int rt_soft_rtc_init(void) return 0; } /* make sure only one 'rtc' device */ +#if defined(RT_USING_SOFT_RTC) && defined(BSP_USING_ONCHIP_RTC) +#warning "Please note: Currently only one RTC device is allowed in the system, and the name is "rtc"." +#endif RT_ASSERT(!rt_device_find("rtc")); #ifdef RT_USING_ALARM @@ -272,13 +258,7 @@ rt_err_t rt_soft_rtc_sync(void) { time_t time = 0; - if (source_device == RT_NULL) - { - rt_kprintf("error: rtc source not found, please set it!!!\n"); - return RT_ENOSYS; - } - - _source_device_control(RT_DEVICE_CTRL_RTC_GET_TIME, &time); + rt_device_control(&soft_rtc_dev, RT_DEVICE_CTRL_RTC_GET_TIME, &time); set_rtc_time(time); return RT_EOK; } @@ -292,9 +272,8 @@ static void rtc_sync_work_func(struct rt_work *work, void *work_data) rt_err_t rt_soft_rtc_set_source(const char *name) { RT_ASSERT(name != RT_NULL); - RT_ASSERT(rt_device_find(name)); // make sure source is exist + RT_ASSERT(rt_device_find(name)); /* make sure source is exist*/ - source_device = rt_device_find(name); rt_work_init(&rtc_sync_work, rtc_sync_work_func, RT_NULL); rt_work_submit(&rtc_sync_work, rt_tick_from_millisecond(RTC_AUTO_SYNC_FIRST_DELAY * 1000)); @@ -317,7 +296,7 @@ static void cmd_rtc_sync(int argc, char **argv) rt_kprintf("local time: %.*s", 25, ctime(&now)); rt_kprintf("timestamps: %ld\n", (long)tv.tv_sec); } -MSH_CMD_EXPORT_ALIAS(cmd_rtc_sync, rtc_sync, Update time by real rtc); +MSH_CMD_EXPORT_ALIAS(cmd_rtc_sync, rtc_sync, Update time by soft rtc); #endif #endif /* RT_USING_SYSTEM_WORKQUEUE */ diff --git a/rt-thread/components/drivers/scsi/Kconfig b/rt-thread/components/drivers/scsi/Kconfig new file mode 100644 index 0000000..ba22188 --- /dev/null +++ b/rt-thread/components/drivers/scsi/Kconfig @@ -0,0 +1,20 @@ +menuconfig RT_USING_SCSI + bool "Using Small Computer System Interface (SCSI)" + depends on RT_USING_DM + default n + +config RT_SCSI_SD + bool "SD device on SCSI" + depends on RT_USING_SCSI + depends on RT_USING_BLK + default y + +config RT_SCSI_CDROM + bool "CD-ROM device on SCSI" + depends on RT_USING_SCSI + depends on RT_USING_BLK + default y + +if RT_USING_SCSI + osource "$(SOC_DM_SCSI_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/scsi/SConscript b/rt-thread/components/drivers/scsi/SConscript new file mode 100644 index 0000000..328d929 --- /dev/null +++ b/rt-thread/components/drivers/scsi/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_SCSI']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['scsi.c'] + +if GetDepend(['RT_SCSI_SD']): + src += ['scsi_sd.c'] + +if GetDepend(['RT_SCSI_CDROM']): + src += ['scsi_cdrom.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/scsi/scsi.c b/rt-thread/components/drivers/scsi/scsi.c new file mode 100644 index 0000000..f5ac977 --- /dev/null +++ b/rt-thread/components/drivers/scsi/scsi.c @@ -0,0 +1,669 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "rtdm.scsi" +#define DBG_LVL DBG_INFO +#include + +/* + * Since SCSI is used for storage (almost), + * we do not want to implement SCSI as a system DM bus. + */ +struct scsi_driver +{ + rt_err_t (*probe)(struct rt_scsi_device *sdev); + rt_err_t (*remove)(struct rt_scsi_device *sdev); +}; + +extern rt_err_t scsi_sd_probe(struct rt_scsi_device *sdev); +extern rt_err_t scsi_sd_remove(struct rt_scsi_device *sdev); + +extern rt_err_t scsi_cdrom_probe(struct rt_scsi_device *sdev); +extern rt_err_t scsi_cdrom_remove(struct rt_scsi_device *sdev); + +static struct scsi_driver driver_table[SCSI_DEVICE_TYPE_MAX] = +{ +#ifdef RT_SCSI_SD + [SCSI_DEVICE_TYPE_DIRECT] = + { + .probe = scsi_sd_probe, + .remove = scsi_sd_remove, + }, +#endif /* RT_SCSI_SD */ +#ifdef RT_SCSI_CDROM + [SCSI_DEVICE_TYPE_CDROM] = + { + .probe = scsi_cdrom_probe, + .remove = scsi_cdrom_remove, + }, +#endif /* RT_SCSI_CDROM */ +}; + +static rt_err_t scsi_device_setup(struct rt_scsi_device *sdev) +{ + rt_err_t err; + rt_tick_t timeout; + + if (sdev->host->ops->reset) + { + if ((err = sdev->host->ops->reset(sdev))) + { + return err; + } + } + + if (!driver_table[sdev->devtype].probe) + { + LOG_E("Device type %x is not supported", sdev->devtype); + + return -RT_ENOSYS; + } + + timeout = rt_tick_from_millisecond(5000); + timeout += rt_tick_get(); + + while ((err = rt_scsi_test_unit_ready(sdev))) + { + if (rt_tick_get() >= timeout) + { + return -RT_ETIMEOUT; + } + } + + return driver_table[sdev->devtype].probe(sdev); +} + +rt_err_t rt_scsi_host_register(struct rt_scsi_host *scsi) +{ + struct rt_scsi_device tmp_sdev, *sdev; + + if (!scsi || !scsi->dev || !scsi->ops) + { + return -RT_EINVAL; + } + + if (!scsi->max_id || !scsi->max_lun) + { + return -RT_EINVAL; + } + + rt_list_init(&scsi->lun_nodes); + + rt_memset(&tmp_sdev, 0, sizeof(tmp_sdev)); + tmp_sdev.host = scsi; + + for (rt_size_t id = 0; id < scsi->max_id; ++id) + { + for (rt_size_t lun = 0; lun < scsi->max_lun; ++lun) + { + tmp_sdev.id = id; + tmp_sdev.lun = lun; + + if (rt_scsi_inquiry(&tmp_sdev, RT_NULL)) + { + continue; + } + + if (tmp_sdev.devtype >= SCSI_DEVICE_TYPE_MAX) + { + /* + * This might seem odd, but we're only aiming to + * support simple SCSI. + * If devices appear on the bus out of order, + * we won't perform additional scans. + */ + scsi->max_id = id; + scsi->max_lun = lun; + + LOG_D("Scan is end of ID: %u LUN: %u", id, lun); + break; + } + + if (!(sdev = rt_malloc(sizeof(*sdev)))) + { + if (!rt_list_isempty(&scsi->lun_nodes)) + { + LOG_E("No memory to create device ID: %u, LUN: %u", id, lun); + + return RT_EOK; + } + + return -RT_ENOMEM; + } + + rt_memcpy(sdev, &tmp_sdev, sizeof(*sdev)); + rt_list_init(&sdev->list); + + if (scsi_device_setup(sdev)) + { + rt_free(sdev); + continue; + } + + rt_list_insert_before(&scsi->lun_nodes, &sdev->list); + } + } + + return rt_list_isempty(&scsi->lun_nodes) ? -RT_EEMPTY : RT_EOK; +} + +rt_err_t rt_scsi_host_unregister(struct rt_scsi_host *scsi) +{ + struct rt_scsi_device *sdev, *next_sdev; + + if (!scsi) + { + return -RT_EINVAL; + } + + rt_list_for_each_entry_safe(sdev, next_sdev, &scsi->lun_nodes, list) + { + rt_list_remove(&sdev->list); + + if (sdev->host->ops->reset) + { + sdev->host->ops->reset(sdev); + } + + if (!driver_table[sdev->devtype].remove) + { + driver_table[sdev->devtype].remove(sdev); + } + + rt_free(sdev); + } + + return RT_EOK; +} + +rt_inline rt_err_t scsi_transfer(struct rt_scsi_device *sdev, struct rt_scsi_cmd *cmd) +{ + return sdev->host->ops->transfer(sdev, cmd); +} + +rt_err_t rt_scsi_request_sense(struct rt_scsi_device *sdev, + struct rt_scsi_request_sense_data *out_data) +{ + rt_err_t err; + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.request_sense.opcode = RT_SCSI_CMD_REQUEST_SENSE; + cmd.op.request_sense.config = 0; + cmd.op.request_sense.alloc_length = 0x12; + cmd.op.request_sense.control = 0; + cmd.op_size = sizeof(cmd.op.request_sense); + cmd.data.ptr = &cmd.data.request_sense; + cmd.data.size = sizeof(cmd.data.request_sense); + + err = scsi_transfer(sdev, &cmd); + + if (!err && out_data) + { + rt_memcpy(out_data, &cmd.data.request_sense, sizeof(*out_data)); + } + + return err; +} + +rt_err_t rt_scsi_test_unit_ready(struct rt_scsi_device *sdev) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.test_unit_ready.opcode = RT_SCSI_CMD_TEST_UNIT_READY; + cmd.op.test_unit_ready.control = 0; + cmd.op_size = sizeof(cmd.op.test_unit_ready); + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_inquiry(struct rt_scsi_device *sdev, + struct rt_scsi_inquiry_data *out_data) +{ + rt_err_t err; + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.inquiry.opcode = RT_SCSI_CMD_INQUIRY; + cmd.op.inquiry.config = 0; + cmd.op.inquiry.page = 0; + cmd.op.inquiry.alloc_length = 0x24; + cmd.op.inquiry.control = 0; + cmd.op_size = sizeof(cmd.op.inquiry); + cmd.data.ptr = &cmd.data.inquiry; + cmd.data.size = sizeof(cmd.data.inquiry); + + err = scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); + + if (!err) + { + sdev->devtype = cmd.data.inquiry.devtype & RT_SCSI_DEVTYPE_MASK; + sdev->removable = cmd.data.inquiry.rmb >> RT_SCSI_REMOVABLE_BIT; + + if (out_data) + { + rt_memcpy(out_data, &cmd.data.inquiry, sizeof(*out_data)); + } + } + + return err; +} + +rt_err_t rt_scsi_read_capacity10(struct rt_scsi_device *sdev, + struct rt_scsi_read_capacity10_data *out_data) +{ + rt_err_t err; + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.read_capacity10.opcode = RT_SCSI_CMD_READ_CAPACITY10; + cmd.op.read_capacity10.config = 0; + cmd.op.read_capacity10.logical_block_addr = 0; + cmd.op.read_capacity10.pmi = 0; + cmd.op.read_capacity10.control = 0; + cmd.op_size = sizeof(cmd.op.read_capacity10); + cmd.data.ptr = &cmd.data.read_capacity10; + cmd.data.size = sizeof(cmd.data.read_capacity10); + + err = scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); + + if (!err) + { + sdev->last_block = rt_be32_to_cpu(cmd.data.read_capacity10.last_block); + sdev->block_size = rt_be32_to_cpu(cmd.data.read_capacity10.block_size); + + if (out_data) + { + rt_memcpy(out_data, &cmd.data.read_capacity10, sizeof(*out_data)); + } + } + + return err; +} + +rt_err_t rt_scsi_read_capacity16(struct rt_scsi_device *sdev, + struct rt_scsi_read_capacity16_data *out_data) +{ + rt_err_t err; + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.read_capacity16.opcode = RT_SCSI_CMD_READ_CAPACITY16; + cmd.op.read_capacity16.config = 0x10; + cmd.op.read_capacity16.logical_block_addr = 0; + cmd.op.read_capacity16.alloc_len = rt_cpu_to_be32(sizeof(cmd.data.read_capacity16)); + cmd.op.read_capacity16.pmi = 0; + cmd.op.read_capacity16.control = 0; + cmd.op_size = sizeof(cmd.op.read_capacity16); + cmd.data.ptr = &cmd.data.read_capacity16; + cmd.data.size = sizeof(cmd.data.read_capacity16); + + err = scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); + + if (!err) + { + sdev->last_block = rt_be64_to_cpu(cmd.data.read_capacity16.last_block); + sdev->block_size = rt_be32_to_cpu(cmd.data.read_capacity16.block_size); + + if (out_data) + { + rt_memcpy(out_data, &cmd.data.read_capacity16, sizeof(*out_data)); + } + } + + return err; +} + +rt_err_t rt_scsi_read10(struct rt_scsi_device *sdev, + rt_off_t lba, void *buffer, rt_size_t size) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.read10.opcode = RT_SCSI_CMD_READ10; + cmd.op.read10.config = 0; + cmd.op.read10.lba = rt_cpu_to_be32(lba); + cmd.op.read10.size = rt_cpu_to_be16(size); + cmd.op_size = sizeof(cmd.op.read10); + cmd.data.ptr = buffer; + cmd.data.size = size * sdev->block_size; + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_read12(struct rt_scsi_device *sdev, + rt_off_t lba, void *buffer, rt_size_t size) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.read12.opcode = RT_SCSI_CMD_READ12; + cmd.op.read12.config = 0; + cmd.op.read12.lba = rt_cpu_to_be32(lba); + cmd.op.read12.size = rt_cpu_to_be32(size); + cmd.op.read12.control = 0; + cmd.op_size = sizeof(cmd.op.read12); + cmd.data.ptr = buffer; + cmd.data.size = size * sdev->block_size; + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_read16(struct rt_scsi_device *sdev, + rt_off_t lba, void *buffer, rt_size_t size) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.read16.opcode = RT_SCSI_CMD_READ16; + cmd.op.read16.config = 0; + cmd.op.read16.lba = rt_cpu_to_be64(lba); + cmd.op.read16.size = rt_cpu_to_be32(size); + cmd.op.read16.control = 0; + cmd.op_size = sizeof(cmd.op.read16); + cmd.data.ptr = buffer; + cmd.data.size = size * sdev->block_size; + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_write10(struct rt_scsi_device *sdev, + rt_off_t lba, const void *buffer, rt_size_t size) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.write10.opcode = RT_SCSI_CMD_WRITE10; + cmd.op.write10.config = 0; + cmd.op.write10.lba = rt_cpu_to_be32(lba); + cmd.op.write10.size = rt_cpu_to_be16(size); + cmd.op_size = sizeof(cmd.op.write10); + cmd.data.ptr = (void *)buffer; + cmd.data.size = size * sdev->block_size; + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_write12(struct rt_scsi_device *sdev, + rt_off_t lba, const void *buffer, rt_size_t size) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.write12.opcode = RT_SCSI_CMD_WRITE12; + cmd.op.write12.config = 0; + cmd.op.write12.lba = rt_cpu_to_be32(lba); + cmd.op.write12.size = rt_cpu_to_be32(size); + cmd.op.write12.control = 0; + cmd.op_size = sizeof(cmd.op.write12); + cmd.data.ptr = (void *)buffer; + cmd.data.size = size * sdev->block_size; + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_write16(struct rt_scsi_device *sdev, + rt_off_t lba, const void *buffer, rt_size_t size) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.write16.opcode = RT_SCSI_CMD_WRITE16; + cmd.op.write16.config = 0; + cmd.op.write16.lba = rt_cpu_to_be64(lba); + cmd.op.write16.size = rt_cpu_to_be32(size); + cmd.op.write16.control = 0; + cmd.op_size = sizeof(cmd.op.write16); + cmd.data.ptr = (void *)buffer; + cmd.data.size = size * sdev->block_size; + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_synchronize_cache10(struct rt_scsi_device *sdev, + rt_off_t lba, rt_size_t size) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.synchronize_cache10.opcode = RT_SCSI_CMD_SYNCHRONIZE_CACHE10; + cmd.op.synchronize_cache10.config = 0; + cmd.op.synchronize_cache10.lba = rt_cpu_to_be32(lba); + cmd.op.synchronize_cache10.size = rt_cpu_to_be16(size); + cmd.op.synchronize_cache10.control = 0; + cmd.op_size = sizeof(cmd.op.synchronize_cache10); + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_synchronize_cache16(struct rt_scsi_device *sdev, + rt_off_t lba, rt_size_t size) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.synchronize_cache16.opcode = RT_SCSI_CMD_SYNCHRONIZE_CACHE16; + cmd.op.synchronize_cache16.config = 0; + cmd.op.synchronize_cache16.lba = rt_cpu_to_be64(lba); + cmd.op.synchronize_cache16.size = rt_cpu_to_be32(size); + cmd.op.synchronize_cache16.control = 0; + cmd.op_size = sizeof(cmd.op.synchronize_cache16); + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_write_same10(struct rt_scsi_device *sdev, + rt_off_t lba, rt_size_t size) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.write_same10.opcode = RT_SCSI_CMD_WRITE_SAME10; + cmd.op.write_same10.config = RT_BIT(RT_SCSI_UNMAP_SHIFT); + cmd.op.write_same10.lba = rt_cpu_to_be32(lba); + cmd.op.write_same10.size = rt_cpu_to_be16(size); + cmd.op.write_same10.control = 0; + cmd.op_size = sizeof(cmd.op.write_same10); + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_write_same16(struct rt_scsi_device *sdev, + rt_off_t lba, rt_size_t size) +{ + struct rt_scsi_cmd cmd; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.write_same16.opcode = RT_SCSI_CMD_WRITE_SAME16; + cmd.op.write_same16.config = RT_BIT(RT_SCSI_UNMAP_SHIFT); + cmd.op.write_same16.lba = rt_cpu_to_be64(lba); + cmd.op.write_same16.size = rt_cpu_to_be32(size); + cmd.op.write_same16.control = 0; + cmd.op_size = sizeof(cmd.op.write_same16); + + return scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); +} + +rt_err_t rt_scsi_mode_select6(struct rt_scsi_device *sdev, + rt_uint8_t pf, rt_uint8_t sp, void *buffer, rt_size_t size, + struct rt_scsi_mode_select_data *data) +{ + rt_err_t err; + rt_uint8_t *real_buffer; + struct rt_scsi_cmd cmd; + + real_buffer = rt_malloc(4 + size); + + if (!real_buffer) + { + return -RT_ENOMEM; + } + + rt_memcpy(real_buffer + 4, buffer, size); + size += 4; + real_buffer[0] = 0; + real_buffer[1] = data->medium_type; + real_buffer[2] = data->device_specific; + real_buffer[3] = data->block_descriptor_length; + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.mode_select6.opcode = RT_SCSI_CMD_MODE_SELECT; + cmd.op.mode_select6.config = pf ? RT_BIT(RT_SCSI_PF_SHIFT) : 0; + cmd.op.mode_select6.config |= sp ? RT_BIT(RT_SCSI_SP_SHIFT) : 0; + cmd.op.mode_select6.param_list_len = (rt_uint8_t)size; + cmd.op.mode_select6.control = 0; + cmd.op_size = sizeof(cmd.op.mode_select6); + cmd.data.ptr = real_buffer; + cmd.data.size = size; + + err = scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); + + rt_free(real_buffer); + + return err; +} + +rt_err_t rt_scsi_mode_select10(struct rt_scsi_device *sdev, + rt_uint8_t pf, rt_uint8_t sp, void *buffer, rt_size_t size, + struct rt_scsi_mode_select_data *data) +{ + rt_err_t err; + rt_uint8_t *real_buffer; + struct rt_scsi_cmd cmd; + + real_buffer = rt_malloc(8 + size); + + if (!real_buffer) + { + return -RT_ENOMEM; + } + + rt_memcpy(real_buffer + 8, buffer, size); + size += 8; + real_buffer[0] = 0; + real_buffer[1] = 0; + real_buffer[2] = data->medium_type; + real_buffer[3] = data->device_specific; + real_buffer[4] = data->longlba ? 0x01 : 0; + real_buffer[5] = 0; + real_buffer[6] = rt_cpu_to_be16(data->block_descriptor_length); + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.mode_select10.opcode = RT_SCSI_CMD_MODE_SELECT10; + cmd.op.mode_select10.config = pf ? RT_BIT(RT_SCSI_PF_SHIFT) : 0; + cmd.op.mode_select10.config |= sp ? RT_BIT(RT_SCSI_SP_SHIFT) : 0; + cmd.op.mode_select10.param_list_len = rt_cpu_to_be16(size); + cmd.op.mode_select10.control = 0; + cmd.op_size = sizeof(cmd.op.mode_select10); + cmd.data.ptr = real_buffer; + cmd.data.size = size; + + err = scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); + + rt_free(real_buffer); + + return err; +} + +static void scsi_mode_sense_fill(struct rt_scsi_mode_select_data *data, + rt_uint8_t modepage, rt_uint8_t *buffer, rt_bool_t use10) +{ + if (buffer[0] == 0x86 && buffer[1] == 0x0b && (modepage == 6 || modepage == 8)) + { + data->header_length = 0; + data->length = 13; + data->medium_type = 0; + data->device_specific = 0; + data->longlba = 0; + data->block_descriptor_length = 0; + } + else if (use10) + { + data->length = rt_be16_to_cpu(buffer[0]) + 2; + data->medium_type = buffer[2]; + data->device_specific = buffer[3]; + data->longlba = buffer[4] & 0x01; + data->block_descriptor_length = rt_be16_to_cpu(buffer[6]); + } + else + { + data->length = buffer[0] + 1; + data->medium_type = buffer[1]; + data->device_specific = buffer[2]; + data->block_descriptor_length = buffer[3]; + } +} + +rt_err_t rt_scsi_mode_sense6(struct rt_scsi_device *sdev, + rt_uint8_t dbd, rt_uint8_t modepage, rt_uint8_t subpage, void *buffer, rt_size_t size, + struct rt_scsi_mode_select_data *data) +{ + rt_err_t err; + struct rt_scsi_cmd cmd; + + rt_memset(buffer, 0, size); + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.mode_sense6.opcode = RT_SCSI_CMD_MODE_SENSE; + cmd.op.mode_sense6.config = dbd & 0x18; + cmd.op.mode_sense6.page_control_code = modepage; + cmd.op.mode_sense6.subpage_code = subpage; + cmd.op.mode_sense6.allocation_len = (rt_uint8_t)size; + cmd.op.mode_sense6.control = 0; + cmd.op_size = sizeof(cmd.op.mode_sense6); + cmd.data.ptr = buffer; + cmd.data.size = size; + + err = scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); + + if (!err) + { + data->header_length = 4; + scsi_mode_sense_fill(data, modepage, buffer, RT_FALSE); + } + + return err; +} + +rt_err_t rt_scsi_mode_sense10(struct rt_scsi_device *sdev, + rt_uint8_t dbd, rt_uint8_t modepage, rt_uint8_t subpage, void *buffer, rt_size_t size, + struct rt_scsi_mode_select_data *data) +{ + rt_err_t err; + struct rt_scsi_cmd cmd; + + rt_memset(buffer, 0, size); + + rt_memset(&cmd, 0, sizeof(cmd)); + cmd.op.mode_sense6.opcode = RT_SCSI_CMD_MODE_SENSE10; + cmd.op.mode_sense6.config = dbd & 0x18; + cmd.op.mode_sense6.page_control_code = modepage; + cmd.op.mode_sense6.subpage_code = subpage; + cmd.op.mode_sense6.allocation_len = rt_cpu_to_be16(size); + cmd.op.mode_sense6.control = 0; + cmd.op_size = sizeof(cmd.op.mode_sense6); + cmd.data.ptr = buffer; + cmd.data.size = size; + + err = scsi_transfer(sdev, &cmd) ? : rt_scsi_request_sense(sdev, RT_NULL); + + if (!err) + { + data->header_length = 8; + scsi_mode_sense_fill(data, modepage, buffer, RT_FALSE); + } + + return err; +} diff --git a/rt-thread/components/drivers/scsi/scsi_cdrom.c b/rt-thread/components/drivers/scsi/scsi_cdrom.c new file mode 100644 index 0000000..93608d1 --- /dev/null +++ b/rt-thread/components/drivers/scsi/scsi_cdrom.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "scsi.blk" +#define DBG_LVL DBG_INFO +#include + +struct scsi_cdrom +{ + struct rt_blk_disk parent; + struct rt_scsi_device *sdev; + + int cdrom_id; + struct rt_device_blk_geometry geometry; +}; + +#define raw_to_scsi_cdrom(raw) rt_container_of(raw, struct scsi_cdrom, parent) + +static struct rt_dm_ida cdrom_ida = RT_DM_IDA_INIT(CUSTOM); +static struct rt_dm_ida scsi_cdrom_ida = RT_DM_IDA_INIT(SCSI_CDROM); + +static rt_ssize_t scsi_cdrom_read(struct rt_blk_disk *disk, rt_off_t sector, + void *buffer, rt_size_t sector_count) +{ + rt_err_t err; + struct scsi_cdrom *scdrom = raw_to_scsi_cdrom(disk); + struct rt_scsi_device *sdev = scdrom->sdev; + + sector_count &= RT_UINT32_MAX; + + if (sector >> 32) + { + err = rt_scsi_read16(sdev, sector, buffer, sector_count); + } + else + { + err = rt_scsi_read12(sdev, sector, buffer, sector_count); + } + + return !err ? sector_count : (rt_ssize_t)err; +} + +static rt_err_t scsi_cdrom_getgeome(struct rt_blk_disk *disk, + struct rt_device_blk_geometry *geometry) +{ + struct scsi_cdrom *scdrom = raw_to_scsi_cdrom(disk); + + rt_memcpy(geometry, &scdrom->geometry, sizeof(scdrom->geometry)); + + return RT_EOK; +} + +static const struct rt_blk_disk_ops scsi_cdrom_ops = +{ + .read = scsi_cdrom_read, + .getgeome = scsi_cdrom_getgeome, +}; + +rt_err_t scsi_cdrom_probe(struct rt_scsi_device *sdev) +{ + rt_err_t err; + union + { + struct rt_scsi_read_capacity10_data capacity10; + struct rt_scsi_read_capacity16_data capacity16; + } data; + struct scsi_cdrom *scdrom = rt_calloc(1, sizeof(*scdrom)); + + if (!scdrom) + { + return -RT_ENOMEM; + } + + if ((scdrom->cdrom_id = rt_dm_ida_alloc(&cdrom_ida)) < 0) + { + return -RT_EFULL; + } + + sdev->priv = scdrom; + scdrom->sdev = sdev; + scdrom->parent.ida = &scsi_cdrom_ida; + scdrom->parent.read_only = RT_TRUE; + scdrom->parent.parallel_io = RT_FALSE; + scdrom->parent.ops = &scsi_cdrom_ops; + scdrom->parent.max_partitions = RT_BLK_PARTITION_NONE; + + if ((err = rt_scsi_read_capacity10(sdev, &data.capacity10))) + { + goto _fail; + } + if (data.capacity10.last_block == 0xffffffff) + { + if ((err = rt_scsi_read_capacity16(sdev, &data.capacity16))) + { + goto _fail; + } + } + scdrom->geometry.bytes_per_sector = sdev->block_size; + scdrom->geometry.block_size = sdev->block_size; + scdrom->geometry.sector_count = sdev->last_block + 1; + + rt_dm_dev_set_name(&scdrom->parent.parent, "cdrom%u", scdrom->cdrom_id); + + if ((err = rt_hw_blk_disk_register(&scdrom->parent))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_dm_ida_free(&cdrom_ida, scdrom->cdrom_id); + rt_free(scdrom); + + return err; +} + +rt_err_t scsi_cdrom_remove(struct rt_scsi_device *sdev) +{ + struct scsi_cdrom *scdrom = sdev->priv; + + rt_dm_ida_free(&cdrom_ida, scdrom->cdrom_id); + + return rt_hw_blk_disk_unregister(&scdrom->parent); +} diff --git a/rt-thread/components/drivers/scsi/scsi_sd.c b/rt-thread/components/drivers/scsi/scsi_sd.c new file mode 100644 index 0000000..cc509a7 --- /dev/null +++ b/rt-thread/components/drivers/scsi/scsi_sd.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "scsi.blk" +#define DBG_LVL DBG_INFO +#include + +#include "../block/blk_dev.h" + +struct scsi_sd +{ + struct rt_blk_disk parent; + struct rt_scsi_device *sdev; + + int sd_id; + rt_bool_t use16; + struct rt_device_blk_geometry geometry; +}; + +#define raw_to_scsi_sd(raw) rt_container_of(raw, struct scsi_sd, parent) + +static struct rt_dm_ida sd_ida = RT_DM_IDA_INIT(CUSTOM); +static struct rt_dm_ida scsi_sd_ida = RT_DM_IDA_INIT(SCSI_SD); + +static rt_ssize_t scsi_sd_read(struct rt_blk_disk *disk, rt_off_t sector, + void *buffer, rt_size_t sector_count) +{ + rt_err_t err; + struct scsi_sd *ssd = raw_to_scsi_sd(disk); + struct rt_scsi_device *sdev = ssd->sdev; + + sector_count &= RT_UINT32_MAX; + + if (sector >> 32) + { + err = rt_scsi_read16(sdev, sector, buffer, sector_count); + } + else + { + err = rt_scsi_read10(sdev, sector, buffer, sector_count); + } + + return !err ? sector_count : (rt_ssize_t)err; +} + +static rt_ssize_t scsi_sd_write(struct rt_blk_disk *disk, rt_off_t sector, + const void *buffer, rt_size_t sector_count) +{ + rt_err_t err; + struct scsi_sd *ssd = raw_to_scsi_sd(disk); + struct rt_scsi_device *sdev = ssd->sdev; + + sector_count &= RT_UINT32_MAX; + + if (sector >> 32) + { + err = rt_scsi_write16(sdev, sector, buffer, sector_count); + } + else + { + err = rt_scsi_write10(sdev, sector, buffer, sector_count); + } + + return !err ? sector_count : (rt_ssize_t)err; +} + +static rt_err_t scsi_sd_getgeome(struct rt_blk_disk *disk, + struct rt_device_blk_geometry *geometry) +{ + struct scsi_sd *ssd = raw_to_scsi_sd(disk); + + rt_memcpy(geometry, &ssd->geometry, sizeof(ssd->geometry)); + + return RT_EOK; +} + +static rt_err_t scsi_sd_sync(struct rt_blk_disk *disk) +{ + rt_err_t err; + rt_size_t lba_count; + struct scsi_sd *ssd = raw_to_scsi_sd(disk); + struct rt_scsi_device *sdev = ssd->sdev; + + lba_count = ssd->geometry.sector_count; + + if (ssd->use16) + { + err = rt_scsi_synchronize_cache16(sdev, 0, lba_count); + } + else + { + err = rt_scsi_synchronize_cache10(sdev, 0, lba_count); + } + + return err; +} + +static rt_err_t scsi_sd_erase(struct rt_blk_disk *disk) +{ + rt_err_t err; + rt_size_t lba_count; + struct scsi_sd *ssd = raw_to_scsi_sd(disk); + struct rt_scsi_device *sdev = ssd->sdev; + + lba_count = ssd->geometry.sector_count; + + if (ssd->use16) + { + err = rt_scsi_write_same16(sdev, 0, lba_count); + } + else + { + err = rt_scsi_write_same10(sdev, 0, lba_count); + } + + return err; +} + +static rt_err_t scsi_sd_autorefresh(struct rt_blk_disk *disk, rt_bool_t is_auto) +{ + rt_err_t err; + int sp; + rt_size_t size; + rt_uint8_t buffer[64]; + rt_uint8_t *buffer_data; + rt_bool_t use6 = RT_TRUE; + struct scsi_sd *ssd = raw_to_scsi_sd(disk); + struct rt_scsi_device *sdev = ssd->sdev; + struct rt_scsi_mode_select_data data; + + err = rt_scsi_mode_sense6(sdev, 0x08, 8, 0, buffer, sizeof(buffer), &data); + + if (err && err != -RT_ENOMEM) + { + use6 = RT_FALSE; + err = rt_scsi_mode_sense10(sdev, 0x08, 8, 0, buffer, sizeof(buffer), &data); + } + if (err) + { + return err; + } + + size = rt_min_t(rt_size_t, sizeof(buffer), + data.length - data.header_length - data.block_descriptor_length); + buffer_data = buffer + data.header_length + data.block_descriptor_length; + buffer_data[2] &= ~0x05; + buffer_data[2] |= (!!is_auto) << 2 | (!!is_auto); + sp = buffer_data[0] & 0x80 ? 1 : 0; + buffer_data[0] &= ~0x80; + data.device_specific = 0; + + if (use6) + { + err = rt_scsi_mode_select6(sdev, 1, sp, buffer_data, size, &data); + } + else + { + err = rt_scsi_mode_select10(sdev, 1, sp, buffer_data, size, &data); + } + + return err; +} + +static const struct rt_blk_disk_ops scsi_sd_ops = +{ + .read = scsi_sd_read, + .write = scsi_sd_write, + .getgeome = scsi_sd_getgeome, + .sync = scsi_sd_sync, + .erase = scsi_sd_erase, + .autorefresh = scsi_sd_autorefresh, +}; + +rt_err_t scsi_sd_probe(struct rt_scsi_device *sdev) +{ + rt_err_t err; + union + { + struct rt_scsi_read_capacity10_data capacity10; + struct rt_scsi_read_capacity16_data capacity16; + } data; + struct scsi_sd *ssd = rt_calloc(1, sizeof(*ssd)); + + if (!ssd) + { + return -RT_ENOMEM; + } + + if ((ssd->sd_id = rt_dm_ida_alloc(&sd_ida)) < 0) + { + return -RT_EFULL; + } + + sdev->priv = ssd; + ssd->sdev = sdev; + ssd->parent.ida = &scsi_sd_ida; + ssd->parent.parallel_io = RT_FALSE; + ssd->parent.ops = &scsi_sd_ops; + ssd->parent.max_partitions = RT_BLK_PARTITION_MAX; + + if ((err = rt_scsi_read_capacity10(sdev, &data.capacity10))) + { + goto _fail; + } + if (data.capacity10.last_block == 0xffffffff) + { + if ((err = rt_scsi_read_capacity16(sdev, &data.capacity16))) + { + goto _fail; + } + ssd->use16 = RT_TRUE; + } + ssd->geometry.bytes_per_sector = sdev->block_size; + ssd->geometry.block_size = sdev->block_size; + ssd->geometry.sector_count = sdev->last_block + 1; + + rt_dm_dev_set_name(&ssd->parent.parent, "sd%c%c", letter_name(ssd->sd_id)); + + if ((err = rt_hw_blk_disk_register(&ssd->parent))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + rt_dm_ida_free(&sd_ida, ssd->sd_id); + rt_free(ssd); + + return err; +} + +rt_err_t scsi_sd_remove(struct rt_scsi_device *sdev) +{ + struct scsi_sd *ssd = sdev->priv; + + rt_dm_ida_free(&sd_ida, ssd->sd_id); + + return rt_hw_blk_disk_unregister(&ssd->parent); +} diff --git a/rt-thread/components/drivers/sdio/Kconfig b/rt-thread/components/drivers/sdio/Kconfig index e5bff45..dd1fe0a 100644 --- a/rt-thread/components/drivers/sdio/Kconfig +++ b/rt-thread/components/drivers/sdio/Kconfig @@ -1,5 +1,6 @@ config RT_USING_SDIO bool "Using SD/MMC device drivers" + select RT_USING_BLK default n if RT_USING_SDIO @@ -24,5 +25,8 @@ config RT_USING_SDIO default 16 config RT_SDIO_DEBUG bool "Enable SDIO debug log output" - default n - endif + default n + config RT_USING_SDHCI + bool "Using sdhci for sd/mmc drivers" + default n + endif diff --git a/rt-thread/components/drivers/sdio/SConscript b/rt-thread/components/drivers/sdio/SConscript index d5b6eb1..98d71d1 100644 --- a/rt-thread/components/drivers/sdio/SConscript +++ b/rt-thread/components/drivers/sdio/SConscript @@ -3,16 +3,20 @@ from building import * cwd = GetCurrentDir() src = Split(""" -block_dev.c -mmcsd_core.c -sd.c -sdio.c -gpt.c -mmc.c +dev_block.c +dev_mmcsd_core.c +dev_sd.c +dev_sdio.c +dev_mmc.c """) # The set of source files associated with this SConscript file. -path = [cwd + '/../include'] +path = [cwd + '/../include' , cwd + '/sdhci/include'] + +if GetDepend('RT_USING_SDHCI'): + src += [os.path.join('sdhci', 'sdhci.c')] + src += [os.path.join('sdhci', 'fit-mmc.c')] + src += [os.path.join('sdhci', 'sdhci-platform.c')] group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SDIO'], CPPPATH = path) diff --git a/rt-thread/components/drivers/sdio/block_dev.c b/rt-thread/components/drivers/sdio/block_dev.c deleted file mode 100644 index fcef99f..0000000 --- a/rt-thread/components/drivers/sdio/block_dev.c +++ /dev/null @@ -1,952 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2011-07-25 weety first version - */ - -#include -#include -#include - -#include -#include - -#define DBG_TAG "SDIO" -#ifdef RT_SDIO_DEBUG -#define DBG_LVL DBG_LOG -#else -#define DBG_LVL DBG_INFO -#endif /* RT_SDIO_DEBUG */ -#include - -static rt_list_t blk_devices = RT_LIST_OBJECT_INIT(blk_devices); - -#define BLK_MIN(a, b) ((a) < (b) ? (a) : (b)) -#define RT_DEVICE_CTRL_BLK_SSIZEGET 0x1268 /**< get number of bytes per sector */ -#define RT_DEVICE_CTRL_ALL_BLK_SSIZEGET 0x80081272 /**< get number of bytes per sector * sector counts*/ - -struct mmcsd_blk_device -{ - struct rt_mmcsd_card *card; - rt_list_t list; - struct rt_device dev; - struct dfs_partition part; - struct rt_device_blk_geometry geometry; - rt_size_t max_req_size; -}; - -#ifndef RT_MMCSD_MAX_PARTITION -#define RT_MMCSD_MAX_PARTITION 16 -#endif -#define RT_GPT_PARTITION_MAX 128 - -static int __send_status(struct rt_mmcsd_card *card, rt_uint32_t *status, unsigned retries) -{ - int err; - struct rt_mmcsd_cmd cmd; - - cmd.busy_timeout = 0; - cmd.cmd_code = SEND_STATUS; - cmd.arg = card->rca << 16; - cmd.flags = RESP_R1 | CMD_AC; - err = mmcsd_send_cmd(card->host, &cmd, retries); - if (err) - return err; - - if (status) - *status = cmd.resp[0]; - - return 0; -} - -static int card_busy_detect(struct rt_mmcsd_card *card, unsigned int timeout_ms, - rt_uint32_t *resp_errs) -{ - int timeout = rt_tick_from_millisecond(timeout_ms); - int err = 0; - rt_uint32_t status; - rt_tick_t start; - - start = rt_tick_get(); - do - { - rt_bool_t out = (int)(rt_tick_get() - start) > timeout; - - err = __send_status(card, &status, 5); - if (err) - { - LOG_E("error %d requesting status", err); - return err; - } - - /* Accumulate any response error bits seen */ - if (resp_errs) - *resp_errs |= status; - - if (out) - { - LOG_E("wait card busy timeout"); - return -RT_ETIMEOUT; - } - /* - * Some cards mishandle the status bits, - * so make sure to check both the busy - * indication and the card state. - */ - } - while (!(status & R1_READY_FOR_DATA) || - (R1_CURRENT_STATE(status) == 7)); - - return err; -} - -rt_int32_t mmcsd_num_wr_blocks(struct rt_mmcsd_card *card) -{ - rt_int32_t err; - rt_uint32_t blocks; - - struct rt_mmcsd_req req; - struct rt_mmcsd_cmd cmd; - struct rt_mmcsd_data data; - rt_uint32_t timeout_us; - - rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); - - cmd.cmd_code = APP_CMD; - cmd.arg = card->rca << 16; - cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_AC; - - err = mmcsd_send_cmd(card->host, &cmd, 0); - if (err) - return -RT_ERROR; - if (!controller_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD)) - return -RT_ERROR; - - rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); - - cmd.cmd_code = SD_APP_SEND_NUM_WR_BLKS; - cmd.arg = 0; - cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC; - - rt_memset(&data, 0, sizeof(struct rt_mmcsd_data)); - - data.timeout_ns = card->tacc_ns * 100; - data.timeout_clks = card->tacc_clks * 100; - - timeout_us = data.timeout_ns / 1000; - timeout_us += data.timeout_clks * 1000 / - (card->host->io_cfg.clock / 1000); - - if (timeout_us > 100000) - { - data.timeout_ns = 100000000; - data.timeout_clks = 0; - } - - data.blksize = 4; - data.blks = 1; - data.flags = DATA_DIR_READ; - data.buf = &blocks; - - rt_memset(&req, 0, sizeof(struct rt_mmcsd_req)); - - req.cmd = &cmd; - req.data = &data; - - mmcsd_send_request(card->host, &req); - - if (cmd.err || data.err) - return -RT_ERROR; - - return blocks; -} - -static rt_err_t rt_mmcsd_req_blk(struct rt_mmcsd_card *card, - rt_uint32_t sector, - void *buf, - rt_size_t blks, - rt_uint8_t dir) -{ - struct rt_mmcsd_cmd cmd, stop; - struct rt_mmcsd_data data; - struct rt_mmcsd_req req; - struct rt_mmcsd_host *host = card->host; - rt_uint32_t r_cmd, w_cmd; - - mmcsd_host_lock(host); - rt_memset(&req, 0, sizeof(struct rt_mmcsd_req)); - rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); - rt_memset(&stop, 0, sizeof(struct rt_mmcsd_cmd)); - rt_memset(&data, 0, sizeof(struct rt_mmcsd_data)); - req.cmd = &cmd; - req.data = &data; - - cmd.arg = sector; - if (!(card->flags & CARD_FLAG_SDHC)) - { - cmd.arg <<= 9; - } - cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC; - - data.blksize = SECTOR_SIZE; - data.blks = blks; - - if (blks > 1) - { - if (!controller_is_spi(card->host) || !dir) - { - req.stop = &stop; - stop.cmd_code = STOP_TRANSMISSION; - stop.arg = 0; - stop.flags = RESP_SPI_R1B | RESP_R1B | CMD_AC; - } - r_cmd = READ_MULTIPLE_BLOCK; - w_cmd = WRITE_MULTIPLE_BLOCK; - } - else - { - req.stop = RT_NULL; - r_cmd = READ_SINGLE_BLOCK; - w_cmd = WRITE_BLOCK; - } - - if (!controller_is_spi(card->host) && (card->flags & 0x8000)) - { - /* last request is WRITE,need check busy */ - card_busy_detect(card, 10000, RT_NULL); - } - - if (!dir) - { - cmd.cmd_code = r_cmd; - data.flags |= DATA_DIR_READ; - card->flags &= 0x7fff; - } - else - { - cmd.cmd_code = w_cmd; - data.flags |= DATA_DIR_WRITE; - card->flags |= 0x8000; - } - - mmcsd_set_data_timeout(&data, card); - data.buf = buf; - - mmcsd_send_request(host, &req); - - mmcsd_host_unlock(host); - - if (cmd.err || data.err || stop.err) - { - LOG_E("mmcsd request blocks error"); - LOG_E("%d,%d,%d, 0x%08x,0x%08x", - cmd.err, data.err, stop.err, data.flags, sector); - - return -RT_ERROR; - } - - return RT_EOK; -} - -static rt_err_t rt_mmcsd_init(rt_device_t dev) -{ - return RT_EOK; -} - -static rt_err_t rt_mmcsd_open(rt_device_t dev, rt_uint16_t oflag) -{ - return RT_EOK; -} - -static rt_err_t rt_mmcsd_close(rt_device_t dev) -{ - return RT_EOK; -} - -static rt_err_t rt_mmcsd_control(rt_device_t dev, int cmd, void *args) -{ - struct mmcsd_blk_device *blk_dev = (struct mmcsd_blk_device *)dev->user_data; - - switch (cmd) - { - case RT_DEVICE_CTRL_BLK_GETGEOME: - rt_memcpy(args, &blk_dev->geometry, sizeof(struct rt_device_blk_geometry)); - break; - case RT_DEVICE_CTRL_BLK_PARTITION: - rt_memcpy(args, &blk_dev->part, sizeof(struct dfs_partition)); - break; - case RT_DEVICE_CTRL_BLK_SSIZEGET: - rt_memcpy(args, &blk_dev->geometry.bytes_per_sector, sizeof(rt_uint32_t)); - break; - case RT_DEVICE_CTRL_ALL_BLK_SSIZEGET: - { - rt_uint64_t count_mul_per = blk_dev->geometry.bytes_per_sector * blk_dev->geometry.sector_count; - rt_memcpy(args, &count_mul_per, sizeof(rt_uint64_t)); - } - break; - default: - break; - } - return RT_EOK; -} - -static rt_ssize_t rt_mmcsd_read(rt_device_t dev, - rt_off_t pos, - void *buffer, - rt_size_t size) -{ - rt_err_t err = 0; - rt_size_t offset = 0; - rt_size_t req_size = 0; - rt_size_t remain_size = size; - void *rd_ptr = (void *)buffer; - struct mmcsd_blk_device *blk_dev = (struct mmcsd_blk_device *)dev->user_data; - struct dfs_partition *part = &blk_dev->part; - - if (dev == RT_NULL) - { - rt_set_errno(-EINVAL); - return 0; - } - - rt_sem_take(part->lock, RT_WAITING_FOREVER); - while (remain_size) - { - req_size = (remain_size > blk_dev->max_req_size) ? blk_dev->max_req_size : remain_size; - err = rt_mmcsd_req_blk(blk_dev->card, part->offset + pos + offset, rd_ptr, req_size, 0); - if (err) - break; - offset += req_size; - rd_ptr = (void *)((rt_uint8_t *)rd_ptr + (req_size << 9)); - remain_size -= req_size; - } - rt_sem_release(part->lock); - - /* the length of reading must align to SECTOR SIZE */ - if (err) - { - rt_set_errno(-EIO); - return 0; - } - return size - remain_size; -} - -static rt_ssize_t rt_mmcsd_write(rt_device_t dev, - rt_off_t pos, - const void *buffer, - rt_size_t size) -{ - rt_err_t err = 0; - rt_size_t offset = 0; - rt_size_t req_size = 0; - rt_size_t remain_size = size; - void *wr_ptr = (void *)buffer; - struct mmcsd_blk_device *blk_dev = (struct mmcsd_blk_device *)dev->user_data; - struct dfs_partition *part = &blk_dev->part; - - if (dev == RT_NULL) - { - rt_set_errno(-EINVAL); - return 0; - } - - rt_sem_take(part->lock, RT_WAITING_FOREVER); - while (remain_size) - { - req_size = (remain_size > blk_dev->max_req_size) ? blk_dev->max_req_size : remain_size; - err = rt_mmcsd_req_blk(blk_dev->card, part->offset + pos + offset, wr_ptr, req_size, 1); - if (err) - break; - offset += req_size; - wr_ptr = (void *)((rt_uint8_t *)wr_ptr + (req_size << 9)); - remain_size -= req_size; - } - rt_sem_release(part->lock); - - /* the length of reading must align to SECTOR SIZE */ - if (err) - { - rt_set_errno(-EIO); - - return 0; - } - return size - remain_size; -} - -static rt_int32_t mmcsd_set_blksize(struct rt_mmcsd_card *card) -{ - struct rt_mmcsd_cmd cmd; - int err; - - /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */ - if (card->flags & CARD_FLAG_SDHC) - return 0; - - mmcsd_host_lock(card->host); - cmd.cmd_code = SET_BLOCKLEN; - cmd.arg = 512; - cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_AC; - err = mmcsd_send_cmd(card->host, &cmd, 5); - mmcsd_host_unlock(card->host); - - if (err) - { - LOG_E("MMCSD: unable to set block size to %d: %d", cmd.arg, err); - - return -RT_ERROR; - } - - return 0; -} -rt_int32_t read_lba(struct rt_mmcsd_card *card, size_t lba, uint8_t *buffer, size_t count) -{ - rt_uint8_t status = 0; - - status = mmcsd_set_blksize(card); - if (status) - { - return status; - } - rt_thread_mdelay(1); - status = rt_mmcsd_req_blk(card, lba, buffer, count, 0); - return status; -} - -#ifdef RT_USING_DEVICE_OPS -const static struct rt_device_ops mmcsd_blk_ops = -{ - rt_mmcsd_init, - rt_mmcsd_open, - rt_mmcsd_close, - rt_mmcsd_read, - rt_mmcsd_write, - rt_mmcsd_control -}; -#endif - -#ifdef RT_USING_DFS_V2 - -static ssize_t rt_mmcsd_fops_read(struct dfs_file *file, void *buf, size_t count, off_t *pos) -{ - int result = 0; - rt_device_t dev = (rt_device_t)file->vnode->data; - struct mmcsd_blk_device *blk_dev = (struct mmcsd_blk_device *)dev->user_data; - int bytes_per_sector = blk_dev->geometry.bytes_per_sector; - int blk_pos = *pos / bytes_per_sector; - int first_offs = *pos % bytes_per_sector; - char *rbuf; - int rsize = 0; - - rbuf = rt_malloc(bytes_per_sector); - if (!rbuf) - { - return 0; - } - - /* - ** #1: read first unalign block size. - */ - result = rt_mmcsd_read(dev, blk_pos, rbuf, 1); - if (result != 1) - { - rt_free(rbuf); - return 0; - } - - if (count > bytes_per_sector - first_offs) - { - rsize = bytes_per_sector - first_offs; - } - else - { - rsize = count; - } - rt_memcpy(buf, rbuf + first_offs, rsize); - blk_pos++; - - /* - ** #2: read continuous block size. - */ - while (rsize < count) - { - result = rt_mmcsd_read(dev, blk_pos++, rbuf, 1); - if (result != 1) - { - break; - } - - if (count - rsize >= bytes_per_sector) - { - rt_memcpy(buf + rsize, rbuf, bytes_per_sector); - rsize += bytes_per_sector; - } - else - { - rt_memcpy(buf + rsize, rbuf, count - rsize); - rsize = count; - } - } - - rt_free(rbuf); - *pos += rsize; - - return rsize; -} - -static int rt_mmcsd_fops_ioctl(struct dfs_file *file, int cmd, void *arg) -{ - rt_device_t dev = (rt_device_t)file->vnode->data; - - return rt_mmcsd_control(dev,cmd,arg); -} - -static int rt_mmcsd_fops_open(struct dfs_file *file) -{ - rt_device_t dev = (rt_device_t)file->vnode->data; - rt_mmcsd_control(dev, RT_DEVICE_CTRL_ALL_BLK_SSIZEGET, &file->vnode->size); - return RT_EOK; -} - -static int rt_mmcsd_fops_close(struct dfs_file *file) -{ - return RT_EOK; -} - -static ssize_t rt_mmcsd_fops_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos) -{ - int result = 0; - rt_device_t dev = (rt_device_t)file->vnode->data; - struct mmcsd_blk_device *blk_dev = (struct mmcsd_blk_device *)dev->user_data; - int bytes_per_sector = blk_dev->geometry.bytes_per_sector; - int blk_pos = *pos / bytes_per_sector; - int first_offs = *pos % bytes_per_sector; - char *rbuf = 0; - int wsize = 0; - - /* - ** #1: write first unalign block size. - */ - if (first_offs != 0) - { - if (count > bytes_per_sector - first_offs) - { - wsize = bytes_per_sector - first_offs; - } - else - { - wsize = count; - } - - rbuf = rt_malloc(bytes_per_sector); - if (!rbuf) - { - return 0; - } - - result = rt_mmcsd_read(dev, blk_pos, rbuf, 1); - if (result != 1) - { - rt_free(rbuf); - return 0; - } - - rt_memcpy(rbuf + first_offs, buf, wsize); - result = rt_mmcsd_write(dev, blk_pos, rbuf, 1); - if (result != 1) - { - rt_free(rbuf); - return 0; - } - rt_free(rbuf); - blk_pos += 1; - } - - /* - ** #2: write continuous block size. - */ - if ((count - wsize) / bytes_per_sector != 0) - { - result = rt_mmcsd_write(dev, blk_pos, buf + wsize, (count - wsize) / bytes_per_sector); - wsize += result * bytes_per_sector; - blk_pos += result; - if (result != (count - wsize) / bytes_per_sector) - { - *pos += wsize; - return wsize; - } - } - - /* - ** # 3: write last unalign block size. - */ - if ((count - wsize) != 0) - { - rbuf = rt_malloc(bytes_per_sector); - if (rbuf != RT_NULL) - { - result = rt_mmcsd_read(dev, blk_pos, rbuf, 1); - if (result == 1) - { - rt_memcpy(rbuf, buf + wsize, count - wsize); - result = rt_mmcsd_write(dev, blk_pos, rbuf, 1); - if (result == 1) - { - wsize += count - wsize; - } - } - - rt_free(rbuf); - } - } - - *pos += wsize; - return wsize; -} - -static int rt_mmcsd_fops_poll(struct dfs_file *file, struct rt_pollreq *req) -{ - int mask = 0; - - return mask; -} - -static int rt_mmcsd_fops_flush(struct dfs_file *file) -{ - return RT_EOK; -} - -const static struct dfs_file_ops mmcsd_blk_fops = -{ - rt_mmcsd_fops_open, - rt_mmcsd_fops_close, - rt_mmcsd_fops_ioctl, - rt_mmcsd_fops_read, - rt_mmcsd_fops_write, - rt_mmcsd_fops_flush, - generic_dfs_lseek, - RT_NULL, - RT_NULL, - rt_mmcsd_fops_poll -}; -#endif - -rt_int32_t gpt_device_probe(struct rt_mmcsd_card *card) -{ - rt_int32_t err = RT_EOK; - rt_uint8_t i, status; - char dname[10]; - char sname[16]; - struct mmcsd_blk_device *blk_dev = RT_NULL; - - blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device)); - if (!blk_dev) - { - LOG_E("mmcsd:malloc memory failed!"); - return -1; - } - - blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs * - card->host->max_seg_size) >> 9, - (card->host->max_blk_count * - card->host->max_blk_size) >> 9); - blk_dev->part.offset = 0; - blk_dev->part.size = 0; - rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, 0); - blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); - /* register mmcsd device */ - blk_dev->dev.type = RT_Device_Class_Block; -#ifdef RT_USING_DEVICE_OPS - blk_dev->dev.ops = &mmcsd_blk_ops; -#else - blk_dev->dev.init = rt_mmcsd_init; - blk_dev->dev.open = rt_mmcsd_open; - blk_dev->dev.close = rt_mmcsd_close; - blk_dev->dev.read = rt_mmcsd_read; - blk_dev->dev.write = rt_mmcsd_write; - blk_dev->dev.control = rt_mmcsd_control; -#endif - blk_dev->card = card; - - blk_dev->geometry.bytes_per_sector = 1 << 9; - blk_dev->geometry.block_size = card->card_blksize; - blk_dev->geometry.sector_count = - card->card_capacity * (1024 / 512); - - blk_dev->dev.user_data = blk_dev; - - rt_device_register(&(blk_dev->dev), card->host->name, - RT_DEVICE_FLAG_RDWR); -#ifdef RT_USING_POSIX_DEVIO -#ifdef RT_USING_DFS_V2 - blk_dev->dev.fops = &mmcsd_blk_fops; -#endif -#endif - rt_list_insert_after(&blk_devices, &blk_dev->list); - - for (i = 0; i < RT_GPT_PARTITION_MAX; i++) - { - blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device)); - if (!blk_dev) - { - LOG_E("mmcsd:malloc memory failed!"); - break; - } - blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs * - card->host->max_seg_size) >> 9, - (card->host->max_blk_count * - card->host->max_blk_size) >> 9); - - /* get the first partition */ - status = gpt_get_partition_param(card, &blk_dev->part, i); - if (status == RT_EOK) - { - rt_snprintf(dname, sizeof(dname) - 1, "%s%d", card->host->name, i); - rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, i + 1); - blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); - - /* register mmcsd device */ - blk_dev->dev.type = RT_Device_Class_Block; -#ifdef RT_USING_DEVICE_OPS - blk_dev->dev.ops = &mmcsd_blk_ops; -#else - blk_dev->dev.init = rt_mmcsd_init; - blk_dev->dev.open = rt_mmcsd_open; - blk_dev->dev.close = rt_mmcsd_close; - blk_dev->dev.read = rt_mmcsd_read; - blk_dev->dev.write = rt_mmcsd_write; - blk_dev->dev.control = rt_mmcsd_control; -#endif - blk_dev->card = card; - - blk_dev->geometry.bytes_per_sector = 1 << 9; - blk_dev->geometry.block_size = card->card_blksize; - blk_dev->geometry.sector_count = blk_dev->part.size; - - blk_dev->dev.user_data = blk_dev; - - rt_device_register(&(blk_dev->dev), dname, - RT_DEVICE_FLAG_RDWR); -#ifdef RT_USING_POSIX_DEVIO -#ifdef RT_USING_DFS_V2 - blk_dev->dev.fops = &mmcsd_blk_fops; -#endif -#endif - rt_list_insert_after(&blk_devices, &blk_dev->list); - } - else - { - rt_free(blk_dev); - blk_dev = RT_NULL; - break; - } - -#ifdef RT_USING_DFS_MNTTABLE - if (blk_dev) - { - LOG_I("try to mount file system!"); - /* try to mount file system on this block device */ - dfs_mount_device(&(blk_dev->dev)); - } -#endif - } - gpt_free(); - - return err; -} - -rt_int32_t mbr_device_probe(struct rt_mmcsd_card *card) -{ - rt_int32_t err = 0; - rt_uint8_t i, status; - rt_uint8_t *sector; - char dname[10]; - char sname[16]; - struct mmcsd_blk_device *blk_dev = RT_NULL; - - err = mmcsd_set_blksize(card); - if (err) - { - return err; - } - rt_thread_mdelay(1); - /* get the first sector to read partition table */ - sector = (rt_uint8_t *)rt_malloc(SECTOR_SIZE); - if (sector == RT_NULL) - { - LOG_E("allocate partition sector buffer failed!"); - - return -RT_ENOMEM; - } - - status = rt_mmcsd_req_blk(card, 0, sector, 1, 0); - if (status == RT_EOK) - { - blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device)); - if (!blk_dev) - { - LOG_E("mmcsd:malloc memory failed!"); - return -1; - } - blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs * - card->host->max_seg_size) >> 9, - (card->host->max_blk_count * - card->host->max_blk_size) >> 9); - blk_dev->part.offset = 0; - blk_dev->part.size = 0; - rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, 0); - blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); - /* register mmcsd device */ - blk_dev->dev.type = RT_Device_Class_Block; -#ifdef RT_USING_DEVICE_OPS - blk_dev->dev.ops = &mmcsd_blk_ops; -#else - blk_dev->dev.init = rt_mmcsd_init; - blk_dev->dev.open = rt_mmcsd_open; - blk_dev->dev.close = rt_mmcsd_close; - blk_dev->dev.read = rt_mmcsd_read; - blk_dev->dev.write = rt_mmcsd_write; - blk_dev->dev.control = rt_mmcsd_control; -#endif - blk_dev->card = card; - - blk_dev->geometry.bytes_per_sector = 1 << 9; - blk_dev->geometry.block_size = card->card_blksize; - blk_dev->geometry.sector_count = - card->card_capacity * (1024 / 512); - - blk_dev->dev.user_data = blk_dev; - - rt_device_register(&(blk_dev->dev), card->host->name, - RT_DEVICE_FLAG_RDWR); - rt_list_insert_after(&blk_devices, &blk_dev->list); - for (i = 0; i < RT_MMCSD_MAX_PARTITION; i++) - { - blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device)); - if (!blk_dev) - { - LOG_E("mmcsd:malloc memory failed!"); - break; - } - blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs * - card->host->max_seg_size) >> 9, - (card->host->max_blk_count * - card->host->max_blk_size) >> 9); - - /* get the first partition */ - status = dfs_filesystem_get_partition(&blk_dev->part, sector, i); - if (status == RT_EOK) - { - rt_snprintf(dname, sizeof(dname) - 1, "%s%d", card->host->name, i); - rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, i + 1); - blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); - - /* register mmcsd device */ - blk_dev->dev.type = RT_Device_Class_Block; -#ifdef RT_USING_DEVICE_OPS - blk_dev->dev.ops = &mmcsd_blk_ops; -#else - blk_dev->dev.init = rt_mmcsd_init; - blk_dev->dev.open = rt_mmcsd_open; - blk_dev->dev.close = rt_mmcsd_close; - blk_dev->dev.read = rt_mmcsd_read; - blk_dev->dev.write = rt_mmcsd_write; - blk_dev->dev.control = rt_mmcsd_control; -#endif - blk_dev->card = card; - - blk_dev->geometry.bytes_per_sector = 1 << 9; - blk_dev->geometry.block_size = card->card_blksize; - blk_dev->geometry.sector_count = blk_dev->part.size; - - blk_dev->dev.user_data = blk_dev; - - rt_device_register(&(blk_dev->dev), dname, - RT_DEVICE_FLAG_RDWR); - rt_list_insert_after(&blk_devices, &blk_dev->list); - } - else - { - rt_free(blk_dev); - blk_dev = RT_NULL; - break; - } - -#ifdef RT_USING_DFS_MNTTABLE - if (blk_dev) - { - LOG_I("try to mount file system!"); - /* try to mount file system on this block device */ - dfs_mount_device(&(blk_dev->dev)); - } -#endif - } - } - else - { - LOG_E("read mmcsd first sector failed"); - err = -RT_ERROR; - } - - /* release sector buffer */ - rt_free(sector); - - return err; - -} - -rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card) -{ - uint32_t err = 0; - - LOG_D("probe mmcsd block device!"); - if (check_gpt(card) != 0) - { - err = gpt_device_probe(card); - } - else - { - err = mbr_device_probe(card); - } - return err; -} - -void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card) -{ - rt_list_t *l, *n; - struct mmcsd_blk_device *blk_dev; - - for (l = (&blk_devices)->next, n = l->next; l != &blk_devices; l = n, n = n->next) - { - blk_dev = (struct mmcsd_blk_device *)rt_list_entry(l, struct mmcsd_blk_device, list); - if (blk_dev->card == card) - { - /* unmount file system */ - const char *mounted_path = dfs_filesystem_get_mounted_path(&(blk_dev->dev)); - if (mounted_path) - { - dfs_unmount(mounted_path); - LOG_D("unmount file system %s for device %s.\r\n", mounted_path, blk_dev->dev.parent.name); - } - rt_sem_delete(blk_dev->part.lock); - rt_device_unregister(&blk_dev->dev); - rt_list_remove(&blk_dev->list); - rt_free(blk_dev); - } - } -} - -/* - * This function will initialize block device on the mmc/sd. - * - * @deprecated since 2.1.0, this function does not need to be invoked - * in the system initialization. - */ -int rt_mmcsd_blk_init(void) -{ - /* nothing */ - return 0; -} diff --git a/rt-thread/components/drivers/sdio/dev_block.c b/rt-thread/components/drivers/sdio/dev_block.c new file mode 100644 index 0000000..d4f8f62 --- /dev/null +++ b/rt-thread/components/drivers/sdio/dev_block.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-07-25 weety first version + * 2023-08-08 GuEe-GUI port to the block + */ + +#include +#include +#include +#include + +#define DBG_TAG "SDIO" +#ifdef RT_SDIO_DEBUG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif /* RT_SDIO_DEBUG */ +#include + +#ifndef RT_MMCSD_MAX_PARTITION +#define RT_MMCSD_MAX_PARTITION 16 +#endif + +struct mmcsd_blk_device +{ + struct rt_blk_disk parent; + struct rt_mmcsd_card *card; + + rt_size_t max_req_size; + struct rt_device_blk_geometry geometry; +}; + +#define raw_to_mmcsd_blk(raw) rt_container_of(raw, struct mmcsd_blk_device, parent) + +#ifdef RT_USING_DM +static struct rt_dm_ida sdio_ida = RT_DM_IDA_INIT(SDIO); +#endif + +static int __send_status(struct rt_mmcsd_card *card, rt_uint32_t *status, unsigned retries) +{ + int err; + struct rt_mmcsd_cmd cmd; + + cmd.busy_timeout = 0; + cmd.cmd_code = SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = RESP_R1 | CMD_AC; + err = mmcsd_send_cmd(card->host, &cmd, retries); + if (err) + return err; + + if (status) + *status = cmd.resp[0]; + + return 0; +} + +static int card_busy_detect(struct rt_mmcsd_card *card, unsigned int timeout_ms, + rt_uint32_t *resp_errs) +{ + int timeout = rt_tick_from_millisecond(timeout_ms); + int err = 0; + rt_uint32_t status; + rt_tick_t start; + + start = rt_tick_get(); + do + { + rt_bool_t out = (int)(rt_tick_get() - start) > timeout; + + err = __send_status(card, &status, 5); + if (err) + { + LOG_E("error %d requesting status", err); + return err; + } + + /* Accumulate any response error bits seen */ + if (resp_errs) + *resp_errs |= status; + + if (out) + { + LOG_E("wait card busy timeout"); + return -RT_ETIMEOUT; + } + /* + * Some cards mishandle the status bits, + * so make sure to check both the busy + * indication and the card state. + */ + } + while (!(status & R1_READY_FOR_DATA) || + (R1_CURRENT_STATE(status) == 7)); + + return err; +} + +rt_int32_t mmcsd_num_wr_blocks(struct rt_mmcsd_card *card) +{ + rt_int32_t err; + rt_uint32_t blocks; + + struct rt_mmcsd_req req; + struct rt_mmcsd_cmd cmd; + struct rt_mmcsd_data data; + rt_uint32_t timeout_us; + + rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); + + cmd.cmd_code = APP_CMD; + cmd.arg = card->rca << 16; + cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_AC; + + err = mmcsd_send_cmd(card->host, &cmd, 0); + if (err) + return -RT_ERROR; + if (!controller_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD)) + return -RT_ERROR; + + rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); + + cmd.cmd_code = SD_APP_SEND_NUM_WR_BLKS; + cmd.arg = 0; + cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC; + + rt_memset(&data, 0, sizeof(struct rt_mmcsd_data)); + + data.timeout_ns = card->tacc_ns * 100; + data.timeout_clks = card->tacc_clks * 100; + + timeout_us = data.timeout_ns / 1000; + timeout_us += data.timeout_clks * 1000 / + (card->host->io_cfg.clock / 1000); + + if (timeout_us > 100000) + { + data.timeout_ns = 100000000; + data.timeout_clks = 0; + } + + data.blksize = 4; + data.blks = 1; + data.flags = DATA_DIR_READ; + data.buf = &blocks; + + rt_memset(&req, 0, sizeof(struct rt_mmcsd_req)); + + req.cmd = &cmd; + req.data = &data; + + mmcsd_send_request(card->host, &req); + + if (cmd.err || data.err) + return -RT_ERROR; + + return blocks; +} + +static rt_err_t rt_mmcsd_req_blk(struct rt_mmcsd_card *card, + rt_uint32_t sector, + void *buf, + rt_size_t blks, + rt_uint8_t dir) +{ + struct rt_mmcsd_cmd cmd, stop; + struct rt_mmcsd_data data; + struct rt_mmcsd_req req; + struct rt_mmcsd_host *host = card->host; + rt_uint32_t r_cmd, w_cmd; + + mmcsd_host_lock(host); + rt_memset(&req, 0, sizeof(struct rt_mmcsd_req)); + rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); + rt_memset(&stop, 0, sizeof(struct rt_mmcsd_cmd)); + rt_memset(&data, 0, sizeof(struct rt_mmcsd_data)); + req.cmd = &cmd; + req.data = &data; + + cmd.arg = sector; + if (!(card->flags & CARD_FLAG_SDHC)) + { + cmd.arg <<= 9; + } + cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC; + + data.blksize = SECTOR_SIZE; + data.blks = blks; + + if (blks > 1) + { + if (!controller_is_spi(card->host) || !dir) + { + req.stop = &stop; + stop.cmd_code = STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = RESP_SPI_R1B | RESP_R1B | CMD_AC; + } + r_cmd = READ_MULTIPLE_BLOCK; + w_cmd = WRITE_MULTIPLE_BLOCK; + } + else + { + req.stop = RT_NULL; + r_cmd = READ_SINGLE_BLOCK; + w_cmd = WRITE_BLOCK; + } + + if (!controller_is_spi(card->host) && (card->flags & 0x8000)) + { + /* last request is WRITE,need check busy */ + card_busy_detect(card, 10000, RT_NULL); + } + + if (!dir) + { + cmd.cmd_code = r_cmd; + data.flags |= DATA_DIR_READ; + card->flags &= 0x7fff; + } + else + { + cmd.cmd_code = w_cmd; + data.flags |= DATA_DIR_WRITE; + card->flags |= 0x8000; + } + + mmcsd_set_data_timeout(&data, card); + data.buf = buf; + + mmcsd_send_request(host, &req); + + mmcsd_host_unlock(host); + + if (cmd.err || data.err || stop.err) + { + LOG_E("mmcsd request blocks error"); + LOG_E("%d,%d,%d, 0x%08x,0x%08x", + cmd.err, data.err, stop.err, data.flags, sector); + + return -RT_ERROR; + } + + return RT_EOK; +} + +static rt_int32_t mmcsd_set_blksize(struct rt_mmcsd_card *card) +{ + struct rt_mmcsd_cmd cmd; + int err; + + /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */ + if (card->flags & CARD_FLAG_SDHC) + return 0; + + mmcsd_host_lock(card->host); + cmd.cmd_code = SET_BLOCKLEN; + cmd.arg = 512; + cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_AC; + err = mmcsd_send_cmd(card->host, &cmd, 5); + mmcsd_host_unlock(card->host); + + if (err) + { + LOG_E("MMCSD: unable to set block size to %d: %d", cmd.arg, err); + + return -RT_ERROR; + } + + return 0; +} + +static rt_ssize_t mmcsd_blk_read(struct rt_blk_disk *disk, rt_off_t sector, + void *buffer, rt_size_t sector_count) +{ + rt_err_t err; + rt_size_t offset = 0; + rt_size_t req_size = 0; + rt_size_t remain_size = sector_count; + void *rd_ptr = (void *)buffer; + struct mmcsd_blk_device *blk_dev = raw_to_mmcsd_blk(disk); + + while (remain_size) + { + req_size = rt_min_t(rt_size_t, remain_size, blk_dev->max_req_size); + + err = rt_mmcsd_req_blk(blk_dev->card, sector + offset, rd_ptr, req_size, 0); + + if (err) + { + return err; + } + + offset += req_size; + rd_ptr = (void *)((rt_uint8_t *)rd_ptr + (req_size << 9)); + remain_size -= req_size; + } + + return sector_count - remain_size; +} + +static rt_ssize_t mmcsd_blk_write(struct rt_blk_disk *disk, rt_off_t sector, + const void *buffer, rt_size_t sector_count) +{ + rt_err_t err; + rt_size_t offset = 0; + rt_size_t req_size = 0; + rt_size_t remain_size = sector_count; + void *wr_ptr = (void *)buffer; + struct mmcsd_blk_device *blk_dev = raw_to_mmcsd_blk(disk); + + while (remain_size) + { + req_size = rt_min_t(rt_size_t, remain_size, blk_dev->max_req_size); + + err = rt_mmcsd_req_blk(blk_dev->card, sector + offset, wr_ptr, req_size, 1); + + if (err) + { + return err; + } + + offset += req_size; + wr_ptr = (void *)((rt_uint8_t *)wr_ptr + (req_size << 9)); + remain_size -= req_size; + } + + return sector_count - remain_size; +} + +static rt_err_t mmcsd_blk_getgeome(struct rt_blk_disk *disk, + struct rt_device_blk_geometry *geometry) +{ + struct mmcsd_blk_device *blk_dev = raw_to_mmcsd_blk(disk); + + rt_memcpy(geometry, &blk_dev->geometry, sizeof(*geometry)); + + return RT_EOK; +} + +static const struct rt_blk_disk_ops mmcsd_blk_ops = +{ + .read = mmcsd_blk_read, + .write = mmcsd_blk_write, + .getgeome = mmcsd_blk_getgeome, +}; + +rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card) +{ + rt_err_t err; + struct rt_mmcsd_host *host = card->host; + struct mmcsd_blk_device *blk_dev = rt_calloc(1, sizeof(*blk_dev)); + + if (!blk_dev) + { + return -RT_ENOMEM; + } + card->blk_dev = blk_dev; + +#ifdef RT_USING_DM + blk_dev->parent.ida = &sdio_ida; +#endif + blk_dev->parent.parallel_io = RT_FALSE; + blk_dev->parent.removable = controller_is_removable(host); + blk_dev->parent.ops = &mmcsd_blk_ops; + blk_dev->parent.max_partitions = RT_MMCSD_MAX_PARTITION; + + blk_dev->card = card; + blk_dev->max_req_size = rt_min_t(rt_size_t, + host->max_dma_segs * host->max_seg_size, + host->max_blk_count * host->max_blk_size) >> 9; + blk_dev->geometry.bytes_per_sector = 1 << 9; + blk_dev->geometry.block_size = card->card_blksize; + blk_dev->geometry.sector_count = card->card_capacity * (1024 / 512); + + /* Set blk size before partitions probe, Why? */ + if ((err = mmcsd_set_blksize(card))) + { + goto _fail; + } + rt_thread_mdelay(1); + +#ifdef RT_USING_DM + rt_dm_dev_set_name(&blk_dev->parent.parent, host->name); +#else + rt_strncpy(blk_dev->parent.parent.parent.name, host->name, RT_NAME_MAX); +#endif + + if ((err = rt_hw_blk_disk_register(&blk_dev->parent))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + card->blk_dev = RT_NULL; + free(blk_dev); + + return err; +} + +void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card) +{ + struct mmcsd_blk_device *blk_dev = card->blk_dev; + + if (!blk_dev) + { + return; + } + + if (!rt_hw_blk_disk_unregister(&blk_dev->parent)) + { + card->blk_dev = RT_NULL; + rt_free(blk_dev); + } +} diff --git a/rt-thread/components/drivers/sdio/mmc.c b/rt-thread/components/drivers/sdio/dev_mmc.c similarity index 99% rename from rt-thread/components/drivers/sdio/mmc.c rename to rt-thread/components/drivers/sdio/dev_mmc.c index 3723d9d..6e1cdce 100644 --- a/rt-thread/components/drivers/sdio/mmc.c +++ b/rt-thread/components/drivers/sdio/dev_mmc.c @@ -9,8 +9,8 @@ * 2024-05-25 HPMicro add HS400 support */ -#include -#include +#include +#include #define DBG_TAG "SDIO" #ifdef RT_SDIO_DEBUG diff --git a/rt-thread/components/drivers/sdio/mmcsd_core.c b/rt-thread/components/drivers/sdio/dev_mmcsd_core.c similarity index 99% rename from rt-thread/components/drivers/sdio/mmcsd_core.c rename to rt-thread/components/drivers/sdio/dev_mmcsd_core.c index 7757026..33072c1 100644 --- a/rt-thread/components/drivers/sdio/mmcsd_core.c +++ b/rt-thread/components/drivers/sdio/dev_mmcsd_core.c @@ -9,10 +9,10 @@ */ #include -#include -#include -#include -#include +#include +#include +#include +#include #include #define DBG_TAG "SDIO" diff --git a/rt-thread/components/drivers/sdio/sd.c b/rt-thread/components/drivers/sdio/dev_sd.c similarity index 99% rename from rt-thread/components/drivers/sdio/sd.c rename to rt-thread/components/drivers/sdio/dev_sd.c index 6e826d5..22725db 100644 --- a/rt-thread/components/drivers/sdio/sd.c +++ b/rt-thread/components/drivers/sdio/dev_sd.c @@ -9,8 +9,8 @@ * 2024-05-26 HPMicro add UHS-I support */ -#include -#include +#include +#include #define DBG_TAG "SDIO" #ifdef RT_SDIO_DEBUG @@ -788,7 +788,7 @@ static rt_int32_t mmcsd_sd_init_card(struct rt_mmcsd_host *host, goto err1; /* set bus speed */ - max_data_rate = (unsigned int)-1; + max_data_rate = 0U; if (max_data_rate < card->hs_max_data_rate) { max_data_rate = card->hs_max_data_rate; diff --git a/rt-thread/components/drivers/sdio/sdio.c b/rt-thread/components/drivers/sdio/dev_sdio.c similarity index 99% rename from rt-thread/components/drivers/sdio/sdio.c rename to rt-thread/components/drivers/sdio/dev_sdio.c index 9fa3f53..013753a 100644 --- a/rt-thread/components/drivers/sdio/sdio.c +++ b/rt-thread/components/drivers/sdio/dev_sdio.c @@ -10,9 +10,9 @@ * 2024-07-05 Evlers fix a bug that read members in non-existent functions */ -#include -#include -#include +#include +#include +#include #define DBG_TAG "SDIO" #ifdef RT_SDIO_DEBUG diff --git a/rt-thread/components/drivers/sdio/gpt.c b/rt-thread/components/drivers/sdio/gpt.c deleted file mode 100644 index 53b686e..0000000 --- a/rt-thread/components/drivers/sdio/gpt.c +++ /dev/null @@ -1,563 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2022-05-05 linzhenxing first version - */ -#include -#include -#include -#include - -#define DBG_TAG "GPT" -#ifdef RT_SDIO_DEBUG -#define DBG_LVL DBG_LOG -#else -#define DBG_LVL DBG_INFO -#endif /* RT_SDIO_DEBUG */ -#include - -#define min(a, b) a < b ? a : b -static int force_gpt = 0; -static gpt_header *_gpt; -static gpt_entry *_ptes; -#define GPT_TYPE 1 -#define MBR_TYPE 0 - -static inline int efi_guidcmp (gpt_guid_t left, gpt_guid_t right) -{ - return rt_memcmp(&left, &right, sizeof (gpt_guid_t)); -} - -static uint32_t last_lba(struct rt_mmcsd_card *card) -{ - RT_ASSERT(card != RT_NULL); - return (card->card_sec_cnt) - 1; -} - -static inline int pmbr_part_valid(gpt_mbr_record *part) -{ - if (part->os_type != EFI_PMBR_OSTYPE_EFI_GPT) - { - goto invalid; - } - - /* set to 0x00000001 (i.e., the LBA of the GPT Partition Header) */ - if ((uint32_t)(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA) - { - goto invalid; - } - - return GPT_MBR_PROTECTIVE; -invalid: - return 0; -} - -/* -* -* return ret -* ret = 0, invalid mbr -* ret = 1, protect mbr -* ret = 2, hybrid mbr -*/ -int is_pmbr_valid(legacy_mbr *mbr, uint64_t total_sectors) -{ - uint32_t sz = 0; - int i, part = 0, ret = 0; /* invalid by default */ - - if (!mbr || (uint16_t)(mbr->signature) != MSDOS_MBR_SIGNATURE) - { - goto done; - } - - for (i = 0; i < 4; i++) - { - ret = pmbr_part_valid(&mbr->partition_record[i]); - if (ret == GPT_MBR_PROTECTIVE) - { - part = i; - /* - * Ok, we at least know that there's a protective MBR, - * now check if there are other partition types for - * hybrid MBR. - */ - goto check_hybrid; - } - } - - if (ret != GPT_MBR_PROTECTIVE) - { - goto done; - } - -check_hybrid: - for (i = 0; i < 4; i++) - { - if ((mbr->partition_record[i].os_type != - EFI_PMBR_OSTYPE_EFI_GPT) && - (mbr->partition_record[i].os_type != 0x00)) - { - ret = GPT_MBR_HYBRID; - } - - } - - /* - * Protective MBRs take up the lesser of the whole disk - * or 2 TiB (32bit LBA), ignoring the rest of the disk. - * Some partitioning programs, nonetheless, choose to set - * the size to the maximum 32-bit limitation, disregarding - * the disk size. - * - * Hybrid MBRs do not necessarily comply with this. - * - * Consider a bad value here to be a warning to support dd'ing - * an image from a smaller disk to a larger disk. - */ - if (ret == GPT_MBR_PROTECTIVE) - { - sz = (uint32_t)(mbr->partition_record[part].size_in_lba); - if (sz != (uint32_t) total_sectors - 1 && sz != 0xFFFFFFFF) - { - LOG_I("GPT: mbr size in lba (%u) different than whole disk (%u).", - sz, min(total_sectors - 1, 0xFFFFFFFF)); - } - } - -done: - return ret; - -} - -static gpt_entry *alloc_read_gpt_entries(struct rt_mmcsd_card *card, gpt_header *gpt) -{ - size_t count; - gpt_entry *pte; - - if (!gpt) - { - return RT_NULL; - } - - count = (size_t)(gpt->num_partition_entries) * (gpt->sizeof_partition_entry); - if (!count) - { - return RT_NULL; - } - - pte = rt_malloc(count); - if (!pte) - return RT_NULL; - - if (read_lba(card, (size_t)(gpt->partition_entry_lba),(uint8_t *)pte, count/512) != RT_EOK) - { - rt_free(pte); - return RT_NULL; - } - return pte; - -} - -static gpt_header *alloc_read_gpt_header(struct rt_mmcsd_card *card, size_t lba) -{ - gpt_header *gpt; - void *buf; - - buf = rt_malloc(512); - if (!buf) - { - return RT_NULL; - } - - if (read_lba(card, lba, (uint8_t *)buf, 1) != RT_EOK) - { - rt_free(buf); - return RT_NULL; - } - gpt = (gpt_header *)buf; - - return gpt; -} - -static int is_gpt_valid(struct rt_mmcsd_card *card, size_t lba, gpt_header **gpt, gpt_entry **ptes) -{ - size_t lastlba; - - if (!ptes || !gpt) - { - return 0; - } - - *gpt = alloc_read_gpt_header(card, lba); - if (!(*gpt)) - { - return 0; - } - - /* Check the GUID Partition Table signature */ - if ((uint64_t)((*gpt)->signature) != GPT_HEADER_SIGNATURE) - { - LOG_E("GUID Partition Table Header signature is wrong:" - "%ld != %ld",(uint64_t)((*gpt)->signature),(uint64_t)GPT_HEADER_SIGNATURE); - goto fail; - } - - /* Check the GUID Partition Table header size is too small */ - if ((uint32_t)((*gpt)->header_size) < sizeof(gpt_header)) - { - LOG_E("GUID Partition Table Header size is too small: %u < %zu", - (uint32_t)((*gpt)->header_size),sizeof(gpt_header)); - goto fail; - } - - /* Check that the start_lba entry points to the LBA that contains - * the GUID Partition Table */ - if ((uint64_t)((*gpt)->start_lba) != lba) - { - LOG_E("GPT start_lba incorrect: %ld != %ld", - (uint64_t)((*gpt)->start_lba), - (uint64_t)lba); - goto fail; - } - - /* Check the first_usable_lba and last_usable_lba are - * within the disk. - */ - lastlba = last_lba(card); - if ((uint64_t)((*gpt)->first_usable_lba) > lastlba) - { - LOG_E("GPT: first_usable_lba incorrect: %ld > %ld", - ((uint64_t)((*gpt)->first_usable_lba)), - (size_t)lastlba); - goto fail; - } - - if ((uint64_t)((*gpt)->last_usable_lba) > lastlba) - { - LOG_E("GPT: last_usable_lba incorrect: %ld > %ld", - (uint64_t)((*gpt)->last_usable_lba), - (size_t)lastlba); - goto fail; - } - - if ((uint64_t)((*gpt)->last_usable_lba) < (uint64_t)((*gpt)->first_usable_lba)) - { - LOG_E("GPT: last_usable_lba incorrect: %ld > %ld", - (uint64_t)((*gpt)->last_usable_lba), - (uint64_t)((*gpt)->first_usable_lba)); - goto fail; - } - /* Check that sizeof_partition_entry has the correct value */ - if ((uint32_t)((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) { - LOG_E("GUID Partition Entry Size check failed."); - goto fail; - } - - *ptes = alloc_read_gpt_entries(card, *gpt); - if (!(*ptes)) - { - goto fail; - } - - /* We're done, all's well */ - return 1; - - fail: - rt_free(*gpt); - *gpt = RT_NULL; - return 0; -} - -/** - * is_pte_valid() - tests one PTE for validity - * pte:pte to check - * lastlba: last lba of the disk - * - * Description: returns 1 if valid, 0 on error. - */ -static inline int is_pte_valid(const gpt_entry *pte, const size_t lastlba) -{ - if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) || - (uint64_t)(pte->starting_lba) > lastlba || - (uint64_t)(pte->ending_lba) > lastlba) - { - return 0; - } - - return 1; -} - -/** - * compare_gpts() - Search disk for valid GPT headers and PTEs - * pgpt: primary GPT header - * agpt: alternate GPT header - * lastlba: last LBA number - * - * Description: Returns nothing. Sanity checks pgpt and agpt fields - * and prints warnings on discrepancies. - * - */ -static void compare_gpts(gpt_header *pgpt, gpt_header *agpt, size_t lastlba) -{ - int error_found = 0; - if (!pgpt || !agpt) - { - return; - } - - if ((uint64_t)(pgpt->start_lba) != (uint64_t)(agpt->alternate_lba)) - { - LOG_I("GPT:Primary header LBA != Alt. header alternate_lba"); - LOG_I("GPT:%lld != %lld", - (uint64_t)(pgpt->start_lba), - (uint64_t)(agpt->alternate_lba)); - error_found++; - } - - if ((uint64_t)(pgpt->alternate_lba) != (uint64_t)(agpt->start_lba)) - { - LOG_I("GPT:Primary header alternate_lba != Alt. header start_lba"); - LOG_I("GPT:%lld != %lld", - (uint64_t)(pgpt->alternate_lba), - (uint64_t)(agpt->start_lba)); - error_found++; - } - - if ((uint64_t)(pgpt->first_usable_lba) != (uint64_t)(agpt->first_usable_lba)) - { - LOG_I("GPT:first_usable_lbas don't match."); - LOG_I("GPT:%lld != %lld", - (uint64_t)(pgpt->first_usable_lba), - (uint64_t)(agpt->first_usable_lba)); - error_found++; - } - - if ((uint64_t)(pgpt->last_usable_lba) != (uint64_t)(agpt->last_usable_lba)) - { - LOG_I("GPT:last_usable_lbas don't match."); - LOG_I("GPT:%lld != %lld", - (uint64_t)(pgpt->last_usable_lba), - (uint64_t)(agpt->last_usable_lba)); - error_found++; - } - - if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) - { - LOG_I("GPT:disk_guids don't match."); - error_found++; - } - - if ((pgpt->num_partition_entries) != (agpt->num_partition_entries)) - { - LOG_I("GPT:num_partition_entries don't match: " - "0x%x != 0x%x", - (pgpt->num_partition_entries), - (agpt->num_partition_entries)); - error_found++; - } - - if ((pgpt->sizeof_partition_entry) != (agpt->sizeof_partition_entry)) - { - LOG_I("GPT:sizeof_partition_entry values don't match: " - "0x%x != 0x%x", - (pgpt->sizeof_partition_entry), - (agpt->sizeof_partition_entry)); - error_found++; - } - - if ((pgpt->partition_entry_array_crc32) != (agpt->partition_entry_array_crc32)) - { - LOG_I("GPT:partition_entry_array_crc32 values don't match: " - "0x%x != 0x%x", - (pgpt->partition_entry_array_crc32), - (agpt->partition_entry_array_crc32)); - error_found++; - } - - if ((pgpt->alternate_lba) != lastlba) - { - LOG_I("GPT:Primary header thinks Alt. header is not at the end of the disk."); - LOG_I("GPT:%lld != %lld", - (uint64_t)(pgpt->alternate_lba), - (size_t)lastlba); - error_found++; - } - - if ((agpt->start_lba) != lastlba) - { - LOG_I("GPT:Alternate GPT header not at the end of the disk."); - LOG_I("GPT:%lld != %lld", - (uint64_t)(agpt->start_lba), - (size_t)lastlba); - error_found++; - } - - if (error_found) - { - LOG_I("GPT: Use GNU Parted to correct GPT errors."); - } - return; -} - -/** - * find_valid_gpt() - Search disk for valid GPT headers and PTEs - * state: disk parsed partitions - * gpt: GPT header ptr, filled on return. - * ptes: PTEs ptr, filled on return. - * - * Description: Returns 1 if valid, 0 on error. - * If valid, returns pointers to newly allocated GPT header and PTEs. - * Validity depends on PMBR being valid (or being overridden by the - * 'gpt' kernel command line option) and finding either the Primary - * GPT header and PTEs valid, or the Alternate GPT header and PTEs - * valid. If the Primary GPT header is not valid, the Alternate GPT header - * is not checked unless the 'gpt' kernel command line option is passed. - * This protects against devices which misreport their size, and forces - * the user to decide to use the Alternate GPT. - */ -static int find_valid_gpt(struct rt_mmcsd_card *card, gpt_header **gpt, - gpt_entry **ptes) -{ - int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; - gpt_header *pgpt = RT_NULL, *agpt = RT_NULL; - gpt_entry *pptes = RT_NULL, *aptes = RT_NULL; - legacy_mbr *legacymbr; - size_t total_sectors = last_lba(card) + 1; - size_t lastlba; - int status = 0; - - if (!ptes) - { - return 0; - } - - lastlba = last_lba(card); - if (!force_gpt) - { - /* This will be added to the EFI Spec. per Intel after v1.02. */ - legacymbr = rt_malloc(512); - if (!legacymbr) - { - goto fail; - } - - status = read_lba(card, 0, (uint8_t *)legacymbr, 1); - if (status) - { - LOG_I("status:%d", status); - goto fail; - } - - good_pmbr = is_pmbr_valid(legacymbr, total_sectors); - rt_free(legacymbr); - - if (!good_pmbr) - { - goto fail; - } - - rt_kprintf("Device has a %s MBR\n", - good_pmbr == GPT_MBR_PROTECTIVE ? - "protective" : "hybrid"); - } - - good_pgpt = is_gpt_valid(card, GPT_PRIMARY_PARTITION_TABLE_LBA, - &pgpt, &pptes); - if (good_pgpt) - { - good_agpt = is_gpt_valid(card, (pgpt->alternate_lba), &agpt, &aptes); - if (!good_agpt && force_gpt) - { - good_agpt = is_gpt_valid(card, lastlba, &agpt, &aptes); - } - - /* The obviously unsuccessful case */ - if (!good_pgpt && !good_agpt) - { - goto fail; - } - - compare_gpts(pgpt, agpt, lastlba); - - /* The good cases */ - if (good_pgpt) - { - *gpt = pgpt; - *ptes = pptes; - rt_free(agpt); - rt_free(aptes); - if (!good_agpt) - { - LOG_D("Alternate GPT is invalid, using primary GPT."); - } - return 1; - } - else if (good_agpt) - { - *gpt = agpt; - *ptes = aptes; - rt_free(pgpt); - rt_free(pptes); - LOG_D("Primary GPT is invalid, using alternate GPT."); - return 1; - } - } - - fail: - rt_free(pgpt); - rt_free(agpt); - rt_free(pptes); - rt_free(aptes); - *gpt = RT_NULL; - *ptes = RT_NULL; - return 0; -} - -int check_gpt(struct rt_mmcsd_card *card) -{ - if (!find_valid_gpt(card, &_gpt, &_ptes) || !_gpt || !_ptes) - { - rt_free(_gpt); - rt_free(_ptes); - return MBR_TYPE; - } - return GPT_TYPE; -} - -int gpt_get_partition_param(struct rt_mmcsd_card *card, struct dfs_partition *part, uint32_t pindex) -{ - if (!is_pte_valid(&_ptes[pindex], last_lba(card))) - { - return -1; - } - - part->offset = (off_t)(_ptes[pindex].starting_lba); - part->size = (_ptes[pindex].ending_lba) - (_ptes[pindex].starting_lba) + 1ULL; - - rt_kprintf("found part[%d], begin(sector): %d, end(sector):%d size: ", - pindex, _ptes[pindex].starting_lba, _ptes[pindex].ending_lba); - - if ((part->size >> 11) == 0) - { - rt_kprintf("%d%s", part->size >> 1, "KB\n"); /* KB */ - } - else - { - unsigned int part_size; - part_size = part->size >> 11; /* MB */ - if ((part_size >> 10) == 0) - rt_kprintf("%d.%d%s", part_size, (part->size >> 1) & 0x3FF, "MB\n"); - else - rt_kprintf("%d.%d%s", part_size >> 10, part_size & 0x3FF, "GB\n"); - } - return 0; -} - -void gpt_free(void) -{ - rt_free(_ptes); - rt_free(_gpt); -} diff --git a/rt-thread/components/drivers/sdio/sdhci/fit-mmc.c b/rt-thread/components/drivers/sdio/sdhci/fit-mmc.c new file mode 100644 index 0000000..a3fe063 --- /dev/null +++ b/rt-thread/components/drivers/sdio/sdhci/fit-mmc.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ +#include +#include "sdhci.h" +#include +#include +#include + + +static void rt_plat_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req) +{ + struct rt_mmc_host *mmc = (struct rt_mmc_host *)host; + rt_uint32_t flags = req->cmd->flags; + + switch (flags & RESP_MASK) + { + case RESP_NONE: + flags |= MMC_RSP_NONE; + break; + case RESP_R1: + flags |= MMC_RSP_R1; + break; + case RESP_R1B: + flags |= MMC_RSP_R1B; + break; + case RESP_R2: + flags |= MMC_RSP_R2; + break; + case RESP_R3: + flags |= MMC_RSP_R3; + break; + case RESP_R4: + flags |= MMC_RSP_R4; + break; + case RESP_R5: + flags |= MMC_RSP_R5; + break; + case RESP_R6: + flags |= MMC_RSP_R6; + break; + case RESP_R7: + flags |= MMC_RSP_R7; + break; + } + if (req->data) + { + if ((rt_uint64_t)rt_kmem_v2p(req->data->buf) > 0xffffffff) + { + void *dma_buffer = rt_malloc(req->data->blks * req->data->blksize); + void *req_buf = NULL; + + if (req->data->flags & DATA_DIR_WRITE) + { + rt_memcpy(dma_buffer, req->data->buf, req->data->blks * req->data->blksize); + req_buf = req->data->buf; + req->data->buf = dma_buffer; + } + else if (req->data->flags & DATA_DIR_READ) + { + req_buf = req->data->buf; + req->data->buf = dma_buffer; + } + req->cmd->flags |= flags; + mmc->ops->request(mmc, req); + + rt_sem_take(&host->sem_ack, RT_WAITING_FOREVER); + + if (req->data->flags & DATA_DIR_READ) + { + rt_memcpy(req_buf, dma_buffer, req->data->blksize * req->data->blks); + req->data->buf = req_buf; + }else{ + req->data->buf = req_buf; + } + + rt_free(dma_buffer); + rt_sem_release(&host->sem_ack); + } + else + { + req->cmd->flags |= flags; + mmc->ops->request(mmc, req); + } + } + else + { + req->cmd->flags |= flags; + mmc->ops->request(mmc, req); + } +} + +static void rt_plat_set_ioconfig(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *iocfg) +{ + struct rt_mmc_host *mmc = (struct rt_mmc_host *)host; + + LOG_D("clock:%d,width:%d,power:%d,vdd:%d,timing:%d\n", + iocfg->clock, iocfg->bus_width, + iocfg->power_mode, iocfg->vdd, iocfg->timing); + + mmc->ops->set_ios(mmc, iocfg); +} + +static rt_int32_t rt_plat_get_card_status(struct rt_mmcsd_host *host) +{ + struct rt_mmc_host *mmc = (struct rt_mmc_host *)host; + + return mmc->ops->get_cd(mmc); +} + +static rt_int32_t rt_plat_execute_tuning(struct rt_mmcsd_host *host, rt_int32_t opcode) +{ + struct rt_mmc_host *mmc = (struct rt_mmc_host *)host; + + return mmc->ops->execute_tuning(mmc, opcode); +} + +static void rt_plat_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t en) +{ + struct rt_mmc_host *mmc = (struct rt_mmc_host *)host; + + return mmc->ops->enable_sdio_irq(mmc, en); +} + + +static const struct rt_mmcsd_host_ops rt_mmcsd_ops = { + .request = rt_plat_request, + .set_iocfg = rt_plat_set_ioconfig, + .get_card_status = rt_plat_get_card_status, + .enable_sdio_irq = rt_plat_enable_sdio_irq, + .execute_tuning = rt_plat_execute_tuning, +}; + + +void rt_mmc_request_done(struct rt_mmc_host *host, struct rt_mmcsd_req *mrq) +{ + mmcsd_req_complete(&host->rthost); +} + +/*add host in rtt while sdhci complete*/ +int rt_mmc_add_host(struct rt_mmc_host *mmc) +{ + mmc->rthost.ops = &rt_mmcsd_ops; + mmc->rthost.flags = mmc->caps; + mmc->rthost.freq_max = mmc->f_max; + mmc->rthost.freq_min = 400000; + mmc->rthost.max_dma_segs = mmc->max_segs; + mmc->rthost.max_seg_size = mmc->max_seg_size; + mmc->rthost.max_blk_size = mmc->max_blk_size; + mmc->rthost.max_blk_count = mmc->max_blk_count; + mmc->rthost.valid_ocr = VDD_165_195|VDD_20_21|VDD_21_22|VDD_22_23|VDD_24_25|VDD_25_26|VDD_26_27|VDD_27_28|VDD_28_29|VDD_29_30|VDD_30_31|VDD_32_33|VDD_33_34|VDD_34_35|VDD_35_36; + + + mmcsd_change(&mmc->rthost); + return 0; +} + +struct rt_mmc_host *rt_mmc_alloc_host(int extra, struct rt_device *dev) +{ + struct rt_mmc_host *mmc; + + mmc = rt_malloc(sizeof(*mmc) + extra); + if (mmc) + { + rt_memset(mmc, 0, sizeof(*mmc) + extra); + mmc->parent = dev; + mmcsd_host_init(&mmc->rthost); + } + + return mmc; +} + +void rt_mmc_remove_host(struct rt_mmc_host *host) +{ + rt_free(host); +} + +int rt_mmc_abort_tuning(struct rt_mmc_host *host, rt_uint32_t opcode) +{ + return 0; +} + + +int rt_mmc_gpio_get_cd(struct rt_mmc_host *host) +{ + return -ENOSYS; +} + +void rt_mmc_detect_change(struct rt_mmc_host *host, unsigned long delay) +{ +} + + +int rt_mmc_regulator_set_vqmmc(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios) +{ + return 0; +} + +rt_bool_t rt_mmc_can_gpio_ro(struct rt_mmc_host *host) +{ + return RT_FALSE; +} + +int rt_mmc_gpio_get_ro(struct rt_mmc_host *host) +{ + return 0; +} + +int rt_mmc_send_abort_tuning(struct rt_mmc_host *host, rt_uint32_t opcode) +{ + return 0; +} +int rt_mmc_of_parse(struct rt_mmc_host *host) +{ + struct rt_device *dev = host->parent; + rt_uint32_t bus_width; + + if (!dev || !dev->ofw_node) + return 0; + + /* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */ + if (rt_dm_dev_prop_read_u32(dev, "bus-width", &bus_width) < 0) + { + bus_width = 1; + } + + switch (bus_width) + { + case 8: + host->caps |= MMC_CAP_8_BIT_DATA; + break; /* Hosts capable of 8-bit can also do 4 bits */ + case 4: + host->caps |= MMC_CAP_4_BIT_DATA; + break; + case 1: + break; + default: + return -EINVAL; + } + + /* f_max is obtained from the optional "max-frequency" property */ + rt_dm_dev_prop_read_u32(dev, "max-frequency", &host->f_max); + + if (rt_dm_dev_prop_read_bool(dev, "cap-mmc-highspeed")) + { + host->caps |= MMC_CAP_MMC_HIGHSPEED; + } + + if (rt_dm_dev_prop_read_bool(dev, "mmc-hs200-1_8v")) + { + host->caps |= MMC_CAP2_HS200_1_8V_SDR; + } + + if (rt_dm_dev_prop_read_bool(dev, "non-removable")) + { + host->caps |= MMC_CAP_NONREMOVABLE; + } + + if (rt_dm_dev_prop_read_bool(dev, "no-sdio")) + { + host->caps2 |= MMC_CAP2_NO_SDIO; + } + + if (rt_dm_dev_prop_read_bool(dev, "no-sd")) + { + host->caps2 |= MMC_CAP2_NO_SD; + } + + if (rt_dm_dev_prop_read_bool(dev, "mmc-ddr-3_3v")) + { + host->caps |= MMC_CAP_3_3V_DDR; + } + + if (rt_dm_dev_prop_read_bool(dev, "mmc-ddr-1_8v")) + { + host->caps |= MMC_CAP_1_8V_DDR; + } + + if (rt_dm_dev_prop_read_bool(dev, "mmc-ddr-1_2v")) + { + host->caps |= MMC_CAP_1_2V_DDR; + } + + return 0; +} + + +void rt_mmc_free_host(struct rt_mmc_host *host) +{ +} + +rt_bool_t rt_mmc_can_gpio_cd(struct rt_mmc_host *host) +{ + return RT_FALSE; +} + +int mmc_regulator_get_supply(struct rt_mmc_host *mmc) +{ + mmc->supply.vmmc = -RT_NULL; + mmc->supply.vqmmc = -RT_NULL; + + return 0; +} +int regulator_get_current_limit(struct regulator *regulator) +{ + return 0; +} + +int regulator_is_supported_voltage(struct regulator *regulator, + + int min_uV, int max_uV) +{ + return 0; +} diff --git a/rt-thread/components/drivers/sdio/sdhci/include/sdhci-platform.h b/rt-thread/components/drivers/sdio/sdhci/include/sdhci-platform.h new file mode 100644 index 0000000..f8d9644 --- /dev/null +++ b/rt-thread/components/drivers/sdio/sdhci/include/sdhci-platform.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ + +#ifndef _DRIVERS_MMC_RT_SDHCI_PLTFM_H +#define _DRIVERS_MMC_RT_SDHCI_PLTFM_H +#include +#include +#include +#include +#include +#include "sdhci.h" + +struct rt_sdhci_pltfm_data +{ + const struct rt_sdhci_ops *ops; + unsigned int quirks; + unsigned int quirks2; +}; + +struct rt_sdhci_pltfm_host +{ + struct rt_clk *clk; + unsigned int clock; + rt_uint64_t xfer_mode_shadow; + + unsigned long private[]; +}; +void rt_sdhci_get_property(struct rt_platform_device *pdev); + +static inline void sdhci_get_of_property(struct rt_platform_device *pdev) +{ + return rt_sdhci_get_property(pdev); +} +extern struct rt_sdhci_host *rt_sdhci_pltfm_init(struct rt_platform_device *pdev, + const struct rt_sdhci_pltfm_data *pdata, + size_t priv_size); +extern void rt_sdhci_pltfm_free(struct rt_platform_device *pdev); + +extern int rt_sdhci_pltfm_init_and_add_host(struct rt_platform_device *pdev, + const struct rt_sdhci_pltfm_data *pdata, + size_t priv_size); +extern void rt_sdhci_pltfm_remove(struct rt_platform_device *pdev); + +extern unsigned int rt_sdhci_pltfm_clk_get_max_clock(struct rt_sdhci_host *host); + +static inline void *sdhci_pltfm_priv(struct rt_sdhci_pltfm_host *host) +{ + return host->private; +} + +static inline int sdhci_pltfm_suspend(struct rt_device *dev) +{ + return 0; +} +static inline int sdhci_pltfm_resume(struct rt_device *dev) +{ + return 0; +} +#endif diff --git a/rt-thread/components/drivers/sdio/sdhci/include/sdhci.h b/rt-thread/components/drivers/sdio/sdhci/include/sdhci.h new file mode 100644 index 0000000..1ad3db1 --- /dev/null +++ b/rt-thread/components/drivers/sdio/sdhci/include/sdhci.h @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ +#ifndef __RT_SDHCI_HW_H +#define __RT_SDHCI_HW_H + +#include "sdhci_host.h" +#include "sdhci_misc.h" +#include "sdhci-platform.h" +#include +#include +#include +#include + +#define lower_32_bits(n) ((rt_uint32_t)((n) & 0xffffffff)) +#define upper_32_bits(n) ((rt_uint32_t)(((n) >> 16) >> 16)) + +#define MAX_TUNING_LOOP 40 +/* + * Controller registers + */ +#define RT_SDHCI_DMA_ADDRESS 0x00 +#define RT_SDHCI_ARGUMENT2 RT_SDHCI_DMA_ADDRESS +#define RT_SDHCI_32BIT_BLK_CNT RT_SDHCI_DMA_ADDRESS + +#define RT_SDHCI_BLOCK_SIZE 0x04 +#define RT_SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) + +#define RT_SDHCI_BLOCK_COUNT 0x06 + +#define RT_SDHCI_ARGUMENT 0x08 + +#define RT_SDHCI_TRANSFER_MODE 0x0C +#define RT_SDHCI_TRNS_DMA 0x01 +#define RT_SDHCI_TRNS_BLK_CNT_EN 0x02 +#define RT_SDHCI_TRNS_AUTO_CMD12 0x04 +#define RT_SDHCI_TRNS_AUTO_CMD23 0x08 +#define RT_SDHCI_TRNS_AUTO_SEL 0x0C +#define RT_SDHCI_TRNS_READ 0x10 +#define RT_SDHCI_TRNS_MULTI 0x20 + +#define RT_SDHCI_COMMAND 0x0E +#define RT_SDHCI_CMD_RESP_MASK 0x03 +#define RT_SDHCI_CMD_CRC 0x08 +#define RT_SDHCI_CMD_INDEX 0x10 +#define RT_SDHCI_CMD_DATA 0x20 +#define RT_SDHCI_CMD_ABORTCMD 0xC0 + +#define RT_SDHCI_CMD_RESP_NONE 0x00 +#define RT_SDHCI_CMD_RESP_LONG 0x01 +#define RT_SDHCI_CMD_RESP_SHORT 0x02 +#define RT_SDHCI_CMD_RESP_SHORT_BUSY 0x03 + +#define RT_SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) +#define RT_SDHCI_GET_CMD(c) ((c >> 8) & 0x3f) + +#define RT_SDHCI_RESPONSE 0x10 + +#define RT_SDHCI_BUFFER 0x20 + +#define RT_SDHCI_PRESENT_STATE 0x24 +#define RT_SDHCI_CMD_INHIBIT 0x00000001 +#define RT_SDHCI_DATA_INHIBIT 0x00000002 +#define RT_SDHCI_DOING_WRITE 0x00000100 +#define RT_SDHCI_DOING_READ 0x00000200 +#define RT_SDHCI_SPACE_AVAILABLE 0x00000400 +#define RT_SDHCI_DATA_AVAILABLE 0x00000800 +#define RT_SDHCI_CARD_PRESENT 0x00010000 +#define RT_SDHCI_CARD_PRES_SHIFT 16 +#define RT_SDHCI_CD_STABLE 0x00020000 +#define RT_SDHCI_CD_LVL 0x00040000 +#define RT_SDHCI_CD_LVL_SHIFT 18 +#define RT_SDHCI_WRITE_PROTECT 0x00080000 +#define RT_SDHCI_DATA_LVL_MASK 0x00F00000 +#define RT_SDHCI_DATA_LVL_SHIFT 20 +#define RT_SDHCI_DATA_0_LVL_MASK 0x00100000 +#define RT_SDHCI_CMD_LVL 0x01000000 + +#define RT_SDHCI_HOST_CONTROL 0x28 +#define RT_SDHCI_CTRL_LED 0x01 +#define RT_SDHCI_CTRL_4BITBUS 0x02 +#define RT_SDHCI_CTRL_HISPD 0x04 +#define RT_SDHCI_CTRL_DMA_MASK 0x18 +#define RT_SDHCI_CTRL_SDMA 0x00 +#define RT_SDHCI_CTRL_ADMA1 0x08 +#define RT_SDHCI_CTRL_ADMA32 0x10 +#define RT_SDHCI_CTRL_ADMA64 0x18 +#define RT_SDHCI_CTRL_ADMA3 0x18 +#define RT_SDHCI_CTRL_8BITBUS 0x20 +#define RT_SDHCI_CTRL_CDTEST_INS 0x40 +#define RT_SDHCI_CTRL_CDTEST_EN 0x80 + +#define RT_SDHCI_POWER_CONTROL 0x29 +#define RT_SDHCI_POWER_ON 0x01 +#define RT_SDHCI_POWER_180 0x0A +#define RT_SDHCI_POWER_300 0x0C +#define RT_SDHCI_POWER_330 0x0E +/* + * VDD2 - UHS2 or PCIe/NVMe + * VDD2 power on/off and voltage select + */ +#define RT_SDHCI_VDD2_POWER_ON 0x10 +#define RT_SDHCI_VDD2_POWER_120 0x80 +#define RT_SDHCI_VDD2_POWER_180 0xA0 + +#define RT_SDHCI_BLOCK_GAP_CONTROL 0x2A + +#define RT_SDHCI_WAKE_UP_CONTROL 0x2B +#define RT_SDHCI_WAKE_ON_INT 0x01 +#define RT_SDHCI_WAKE_ON_INSERT 0x02 +#define RT_SDHCI_WAKE_ON_REMOVE 0x04 + +#define RT_SDHCI_CLOCK_CONTROL 0x2C +#define RT_SDHCI_DIVIDER_SHIFT 8 +#define RT_SDHCI_DIVIDER_HI_SHIFT 6 +#define RT_SDHCI_DIV_MASK 0xFF +#define RT_SDHCI_DIV_MASK_LEN 8 +#define RT_SDHCI_DIV_HI_MASK 0x300 +#define RT_SDHCI_PROG_CLOCK_MODE 0x0020 +#define RT_SDHCI_CLOCK_CARD_EN 0x0004 +#define RT_SDHCI_CLOCK_PLL_EN 0x0008 +#define RT_SDHCI_CLOCK_INT_STABLE 0x0002 +#define RT_SDHCI_CLOCK_INT_EN 0x0001 + +#define RT_SDHCI_TIMEOUT_CONTROL 0x2E + +#define RT_SDHCI_SOFTWARE_RESET 0x2F +#define RT_SDHCI_RESET_ALL 0x01 +#define RT_SDHCI_RESET_CMD 0x02 +#define RT_SDHCI_RESET_DATA 0x04 + +#define RT_SDHCI_INT_STATUS 0x30 +#define RT_SDHCI_INT_ENABLE 0x34 +#define RT_SDHCI_SIGNAL_ENABLE 0x38 +#define RT_SDHCI_INT_RESPONSE 0x00000001 +#define RT_SDHCI_INT_DATA_END 0x00000002 +#define RT_SDHCI_INT_BLK_GAP 0x00000004 +#define RT_SDHCI_INT_DMA_END 0x00000008 +#define RT_SDHCI_INT_SPACE_AVAIL 0x00000010 +#define RT_SDHCI_INT_DATA_AVAIL 0x00000020 +#define RT_SDHCI_INT_CARD_INSERT 0x00000040 +#define RT_SDHCI_INT_CARD_REMOVE 0x00000080 +#define RT_SDHCI_INT_CARD_INT 0x00000100 +#define RT_SDHCI_INT_RETUNE 0x00001000 +#define RT_SDHCI_INT_CQE 0x00004000 +#define RT_SDHCI_INT_ERROR 0x00008000 +#define RT_SDHCI_INT_TIMEOUT 0x00010000 +#define RT_SDHCI_INT_CRC 0x00020000 +#define RT_SDHCI_INT_END_BIT 0x00040000 +#define RT_SDHCI_INT_INDEX 0x00080000 +#define RT_SDHCI_INT_DATA_TIMEOUT 0x00100000 +#define RT_SDHCI_INT_DATA_CRC 0x00200000 +#define RT_SDHCI_INT_DATA_END_BIT 0x00400000 +#define RT_SDHCI_INT_BUS_POWER 0x00800000 +#define RT_SDHCI_INT_AUTO_CMD_ERR 0x01000000 +#define RT_SDHCI_INT_ADMA_ERROR 0x02000000 + +#define RT_SDHCI_INT_NORMAL_MASK 0x00007FFF +#define RT_SDHCI_INT_ERROR_MASK 0xFFFF8000 + +#define RT_SDHCI_INT_CMD_MASK (RT_SDHCI_INT_RESPONSE | RT_SDHCI_INT_TIMEOUT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_INDEX | RT_SDHCI_INT_AUTO_CMD_ERR) +#define RT_SDHCI_INT_DATA_MASK (RT_SDHCI_INT_DATA_END | RT_SDHCI_INT_DMA_END | RT_SDHCI_INT_DATA_AVAIL | RT_SDHCI_INT_SPACE_AVAIL | RT_SDHCI_INT_DATA_TIMEOUT | RT_SDHCI_INT_DATA_CRC | RT_SDHCI_INT_DATA_END_BIT | RT_SDHCI_INT_ADMA_ERROR | RT_SDHCI_INT_BLK_GAP) +#define RT_SDHCI_INT_ALL_MASK ((unsigned int)-1) + +#define RT_SDHCI_CQE_INT_ERR_MASK ( \ + RT_SDHCI_INT_ADMA_ERROR | RT_SDHCI_INT_BUS_POWER | RT_SDHCI_INT_DATA_END_BIT | RT_SDHCI_INT_DATA_CRC | RT_SDHCI_INT_DATA_TIMEOUT | RT_SDHCI_INT_INDEX | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_TIMEOUT) + +#define RT_SDHCI_CQE_INT_MASK (RT_SDHCI_CQE_INT_ERR_MASK | RT_SDHCI_INT_CQE) + +#define RT_SDHCI_AUTO_CMD_STATUS 0x3C +#define RT_SDHCI_AUTO_CMD_TIMEOUT 0x00000002 +#define RT_SDHCI_AUTO_CMD_CRC 0x00000004 +#define RT_SDHCI_AUTO_CMD_END_BIT 0x00000008 +#define RT_SDHCI_AUTO_CMD_INDEX 0x00000010 + +#define RT_SDHCI_HOST_CONTROL2 0x3E +#define RT_SDHCI_CTRL_UHS_MASK 0x0007 +#define RT_SDHCI_CTRL_UHS_SDR12 0x0000 +#define RT_SDHCI_CTRL_UHS_SDR25 0x0001 +#define RT_SDHCI_CTRL_UHS_SDR50 0x0002 +#define RT_SDHCI_CTRL_UHS_SDR104 0x0003 +#define RT_SDHCI_CTRL_UHS_DDR50 0x0004 +#define RT_SDHCI_CTRL_HS400 0x0005 /* Non-standard */ +#define RT_SDHCI_CTRL_VDD_180 0x0008 +#define RT_SDHCI_CTRL_DRV_TYPE_MASK 0x0030 +#define RT_SDHCI_CTRL_DRV_TYPE_B 0x0000 +#define RT_SDHCI_CTRL_DRV_TYPE_A 0x0010 +#define RT_SDHCI_CTRL_DRV_TYPE_C 0x0020 +#define RT_SDHCI_CTRL_DRV_TYPE_D 0x0030 +#define RT_SDHCI_CTRL_EXEC_TUNING 0x0040 +#define RT_SDHCI_CTRL_TUNED_CLK 0x0080 +#define RT_SDHCI_CMD23_ENABLE 0x0800 +#define RT_SDHCI_CTRL_V4_MODE 0x1000 +#define RT_SDHCI_CTRL_64BIT_ADDR 0x2000 +#define RT_SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000 + +#define RT_SDHCI_CAPABILITIES 0x40 +#define RT_SDHCI_TIMEOUT_CLK_MASK RT_GENMASK(5, 0) +#define RT_SDHCI_TIMEOUT_CLK_SHIFT 0 +#define RT_SDHCI_TIMEOUT_CLK_UNIT 0x00000080 +#define RT_SDHCI_CLOCK_BASE_MASK RT_GENMASK(13, 8) +#define RT_SDHCI_CLOCK_BASE_SHIFT 8 +#define RT_SDHCI_CLOCK_V3_BASE_MASK RT_GENMASK(15, 8) +#define RT_SDHCI_MAX_BLOCK_MASK 0x00030000 +#define RT_SDHCI_MAX_BLOCK_SHIFT 16 +#define RT_SDHCI_CAN_DO_8BIT 0x00040000 +#define RT_SDHCI_CAN_DO_ADMA2 0x00080000 +#define RT_SDHCI_CAN_DO_ADMA1 0x00100000 +#define RT_SDHCI_CAN_DO_HISPD 0x00200000 +#define RT_SDHCI_CAN_DO_SDMA 0x00400000 +#define RT_SDHCI_CAN_DO_SUSPEND 0x00800000 +#define RT_SDHCI_CAN_VDD_330 0x01000000 +#define RT_SDHCI_CAN_VDD_300 0x02000000 +#define RT_SDHCI_CAN_VDD_180 0x04000000 +#define RT_SDHCI_CAN_64BIT_V4 0x08000000 +#define RT_SDHCI_CAN_64BIT 0x10000000 + +#define RT_SDHCI_CAPABILITIES_1 0x44 +#define RT_SDHCI_SUPPORT_SDR50 0x00000001 +#define RT_SDHCI_SUPPORT_SDR104 0x00000002 +#define RT_SDHCI_SUPPORT_DDR50 0x00000004 +#define RT_SDHCI_DRIVER_TYPE_A 0x00000010 +#define RT_SDHCI_DRIVER_TYPE_C 0x00000020 +#define RT_SDHCI_DRIVER_TYPE_D 0x00000040 +#define RT_SDHCI_RETUNING_TIMER_COUNT_MASK RT_GENMASK(11, 8) +#define RT_SDHCI_USE_SDR50_TUNING 0x00002000 +#define RT_SDHCI_RETUNING_MODE_MASK RT_GENMASK(15, 14) +#define RT_SDHCI_CLOCK_MUL_MASK RT_GENMASK(23, 16) +#define RT_SDHCI_CAN_DO_ADMA3 0x08000000 +#define RT_SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */ + +#define RT_SDHCI_MAX_CURRENT 0x48 +#define RT_SDHCI_MAX_CURRENT_LIMIT RT_GENMASK(7, 0) +#define RT_SDHCI_MAX_CURRENT_330_MASK RT_GENMASK(7, 0) +#define RT_SDHCI_MAX_CURRENT_300_MASK RT_GENMASK(15, 8) +#define RT_SDHCI_MAX_CURRENT_180_MASK RT_GENMASK(23, 16) +#define RT_SDHCI_MAX_CURRENT_MULTIPLIER 4 + +/* 4C-4F reserved for more max current */ + +#define RT_SDHCI_SET_ACMD12_ERROR 0x50 +#define RT_SDHCI_SET_INT_ERROR 0x52 + +#define RT_SDHCI_ADMA_ERROR 0x54 + +/* 55-57 reserved */ + +#define RT_SDHCI_ADMA_ADDRESS 0x58 +#define RT_SDHCI_ADMA_ADDRESS_HI 0x5C + +/* 60-FB reserved */ + +#define RT_SDHCI_PRESET_FOR_HIGH_SPEED 0x64 +#define RT_SDHCI_PRESET_FOR_SDR12 0x66 +#define RT_SDHCI_PRESET_FOR_SDR25 0x68 +#define RT_SDHCI_PRESET_FOR_SDR50 0x6A +#define RT_SDHCI_PRESET_FOR_SDR104 0x6C +#define RT_SDHCI_PRESET_FOR_DDR50 0x6E +#define RT_SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */ +#define RT_SDHCI_PRESET_DRV_MASK RT_GENMASK(15, 14) +#define BIT(nr) ((1) << (nr)) + +#define RT_SDHCI_PRESET_CLKGEN_SEL BIT(10) +#define RT_SDHCI_PRESET_SDCLK_FREQ_MASK RT_GENMASK(9, 0) + +#define RT_SDHCI_SLOT_INT_STATUS 0xFC + +#define RT_SDHCI_HOST_VERSION 0xFE +#define RT_SDHCI_VENDOR_VER_MASK 0xFF00 +#define RT_SDHCI_VENDOR_VER_SHIFT 8 +#define RT_SDHCI_SPEC_VER_MASK 0x00FF +#define RT_SDHCI_SPEC_VER_SHIFT 0 +#define RT_SDHCI_SPEC_100 0 +#define RT_SDHCI_SPEC_200 1 +#define RT_SDHCI_SPEC_300 2 +#define RT_SDHCI_SPEC_400 3 +#define RT_SDHCI_SPEC_410 4 +#define RT_SDHCI_SPEC_420 5 + +/* + * End of controller registers. + */ + +#define RT_SDHCI_MAX_DIV_SPEC_200 256 +#define RT_SDHCI_MAX_DIV_SPEC_300 2046 + +/* + * Host SDMA buffer boundary. Valid values from 4K to 512K in powers of 2. + */ +#define RT_SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024) +#define ilog2(v) __rt_ffs(v) +#define RT_SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(RT_SDHCI_DEFAULT_BOUNDARY_SIZE) - 12) +#define RT_SDHCI_MAX_SEGS 128 + +/* Allow for a command request and a data request at the same time */ +#define RT_SDHCI_MAX_MRQS 2 +#define MMC_CMD_TRANSFER_TIME (10 * 1000000L) /* max 10 ms */ + + +enum rt_sdhci_cookie +{ + COOKIE_UNMAPPED, + COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */ + COOKIE_MAPPED, /* mapped by sdhci_prepare_data() */ +}; + +struct rt_sdhci_host +{ + const char *hw_name; /* Hardware bus name */ + + unsigned int quirks; /* Deviations from spec. */ + + void *data_buf; +/* Controller doesn't honor resets unless we touch the clock register */ +#define RT_SDHCI_QUIRK_CLOCK_BEFORE_RESET (1 << 0) +/* Controller has bad caps bits, but really supports DMA */ +#define RT_SDHCI_QUIRK_FORCE_DMA (1 << 1) +/* Controller doesn't like to be reset when there is no card inserted. */ +#define RT_SDHCI_QUIRK_NO_CARD_NO_RESET (1 << 2) +/* Controller doesn't like clearing the power reg before a change */ +#define RT_SDHCI_QUIRK_SINGLE_POWER_WRITE (1 << 3) +/* Controller has an unusable DMA engine */ +#define RT_SDHCI_QUIRK_BROKEN_DMA (1 << 5) +/* Controller has an unusable ADMA engine */ +#define RT_SDHCI_QUIRK_BROKEN_ADMA (1 << 6) +/* Controller can only DMA from 32-bit aligned addresses */ +#define RT_SDHCI_QUIRK_32BIT_DMA_ADDR (1 << 7) +/* Controller can only DMA chunk sizes that are a multiple of 32 bits */ +#define RT_SDHCI_QUIRK_32BIT_DMA_SIZE (1 << 8) +/* Controller can only ADMA chunks that are a multiple of 32 bits */ +#define RT_SDHCI_QUIRK_32BIT_ADMA_SIZE (1 << 9) +/* Controller needs to be reset after each request to stay stable */ +#define RT_SDHCI_QUIRK_RESET_AFTER_REQUEST (1 << 10) +/* Controller needs voltage and power writes to happen separately */ +#define RT_SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1 << 11) +/* Controller provides an incorrect timeout value for transfers */ +#define RT_SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1 << 12) +/* Controller has an issue with buffer bits for small transfers */ +#define RT_SDHCI_QUIRK_BROKEN_SMALL_PIO (1 << 13) +/* Controller does not provide transfer-complete interrupt when not busy */ +#define RT_SDHCI_QUIRK_NO_BUSY_IRQ (1 << 14) +/* Controller has unreliable card detection */ +#define RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION (1 << 15) +/* Controller reports inverted write-protect state */ +#define RT_SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1 << 16) +/* Controller has unusable command queue engine */ +#define RT_SDHCI_QUIRK_BROKEN_CQE (1 << 17) +/* Controller does not like fast PIO transfers */ +#define RT_SDHCI_QUIRK_PIO_NEEDS_DELAY (1 << 18) +/* Controller does not have a LED */ +#define RT_SDHCI_QUIRK_NO_LED (1 << 19) +/* Controller has to be forced to use block size of 2048 bytes */ +#define RT_SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1 << 20) +/* Controller cannot do multi-block transfers */ +#define RT_SDHCI_QUIRK_NO_MULTIBLOCK (1 << 21) +/* Controller can only handle 1-bit data transfers */ +#define RT_SDHCI_QUIRK_FORCE_1_BIT_DATA (1 << 22) +/* Controller needs 10ms delay between applying power and clock */ +#define RT_SDHCI_QUIRK_DELAY_AFTER_POWER (1 << 23) +/* Controller uses SDCLK instead of TMCLK for data timeouts */ +#define RT_SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1 << 24) +/* Controller reports wrong base clock capability */ +#define RT_SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1 << 25) +/* Controller cannot support End Attribute in NOP ADMA descriptor */ +#define RT_SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1 << 26) +/* Controller uses Auto CMD12 command to stop the transfer */ +#define RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1 << 28) +/* Controller doesn't have HISPD bit field in HI-SPEED SD card */ +#define RT_SDHCI_QUIRK_NO_HISPD_BIT (1 << 29) +/* Controller treats ADMA descriptors with length 0000h incorrectly */ +#define RT_SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1 << 30) +/* The read-only detection via RT_SDHCI_PRESENT_STATE register is unstable */ +#define RT_SDHCI_QUIRK_UNSTABLE_RO_DETECT (1 << 31) + + unsigned int quirks2; /* More deviations from spec. */ + +#define RT_SDHCI_QUIRK2_HOST_OFF_CARD_ON (1 << 0) +#define RT_SDHCI_QUIRK2_HOST_NO_CMD23 (1 << 1) +/* The system physically doesn't support 1.8v, even if the host does */ +#define RT_SDHCI_QUIRK2_NO_1_8_V (1 << 2) +#define RT_SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1 << 3) +#define RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1 << 4) +/* Controller has a non-standard host control register */ +#define RT_SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1 << 5) +/* Controller does not support HS200 */ +#define RT_SDHCI_QUIRK2_BROKEN_HS200 (1 << 6) +/* Controller does not support DDR50 */ +#define RT_SDHCI_QUIRK2_BROKEN_DDR50 (1 << 7) +/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */ +#define RT_SDHCI_QUIRK2_STOP_WITH_TC (1 << 8) +/* Controller does not support 64-bit DMA */ +#define RT_SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1 << 9) +/* need clear transfer mode register before send cmd */ +#define RT_SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1 << 10) +/* Capability register bit-63 indicates HS400 support */ +#define RT_SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1 << 11) +/* forced tuned clock */ +#define RT_SDHCI_QUIRK2_TUNING_WORK_AROUND (1 << 12) +/* disable the block count for single block transactions */ +#define RT_SDHCI_QUIRK2_SUPPORT_SINGLE (1 << 13) +/* Controller broken with using ACMD23 */ +#define RT_SDHCI_QUIRK2_ACMD23_BROKEN (1 << 14) +/* Broken Clock divider zero in controller */ +#define RT_SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1 << 15) +/* Controller has CRC in 136 bit Command Response */ +#define RT_SDHCI_QUIRK2_RSP_136_HAS_CRC (1 << 16) + +#define RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT (1 << 17) + +#define RT_SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1 << 18) +/* Issue CMD and DATA reset together */ +#define RT_SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER (1 << 19) + + int irq; /* Device IRQ */ + void *ioaddr; /* Mapped address */ + char *bounce_buffer; /* For packing SDMA reads/writes */ + rt_uint64_t bounce_addr; + unsigned int bounce_buffer_size; + + const struct rt_sdhci_ops *ops; /* Low level hw interface */ + + /* Internal data */ + struct rt_mmc_host *mmc; /* MMC structure */ + struct mmc_host_ops mmc_host_ops; /* MMC host ops */ + rt_uint64_t dma_mask; /* custom DMA mask */ + + rt_spinlock_t lock; + int flags; /* Host attributes */ +#define RT_SDHCI_USE_SDMA (1 << 0) /* Host is SDMA capable */ +#define RT_SDHCI_USE_ADMA (1 << 1) /* Host is ADMA capable */ +#define RT_SDHCI_REQ_USE_DMA (1 << 2) /* Use DMA for this req. */ +#define RT_SDHCI_DEVICE_DEAD (1 << 3) /* Device unresponsive */ +#define RT_SDHCI_SDR50_NEEDS_TUNING (1 << 4) /* SDR50 needs tuning */ +#define RT_SDHCI_AUTO_CMD12 (1 << 6) /* Auto CMD12 support */ +#define RT_SDHCI_AUTO_CMD23 (1 << 7) /* Auto CMD23 support */ +#define RT_SDHCI_PV_ENABLED (1 << 8) /* Preset value enabled */ +#define RT_SDHCI_USE_64_BIT_DMA (1 << 12) /* Use 64-bit DMA */ +#define RT_SDHCI_HS400_TUNING (1 << 13) /* Tuning for HS400 */ +#define RT_SDHCI_SIGNALING_330 (1 << 14) /* Host is capable of 3.3V signaling */ +#define RT_SDHCI_SIGNALING_180 (1 << 15) /* Host is capable of 1.8V signaling */ +#define RT_SDHCI_SIGNALING_120 (1 << 16) /* Host is capable of 1.2V signaling */ + + unsigned int version; /* RT_SDHCI spec. version */ + + unsigned int max_clk; /* Max possible freq (MHz) */ + unsigned int timeout_clk; /* Timeout freq (KHz) */ + rt_uint8_t max_timeout_count; /* Vendor specific max timeout count */ + unsigned int clk_mul; /* Clock Muliplier value */ + + unsigned int clock; /* Current clock (MHz) */ + rt_uint8_t pwr; /* Current voltage */ + rt_uint8_t drv_type; /* Current UHS-I driver type */ + rt_bool_t reinit_uhs; /* Force UHS-related re-initialization */ + + rt_bool_t runtime_suspended; /* Host is runtime suspended */ + rt_bool_t bus_on; /* Bus power prevents runtime suspend */ + rt_bool_t preset_enabled; /* Preset is enabled */ + rt_bool_t pending_reset; /* Cmd/data reset is pending */ + rt_bool_t irq_wake_enabled; /* IRQ wakeup is enabled */ + rt_bool_t v4_mode; /* Host Version 4 Enable */ + rt_bool_t always_defer_done; /* Always defer to complete requests */ + + struct rt_mmcsd_req *mrqs_done[RT_SDHCI_MAX_MRQS]; /* Requests done */ + struct rt_mmcsd_cmd *cmd; /* Current command */ + struct rt_mmcsd_cmd *data_cmd; /* Current data command */ + struct rt_mmcsd_cmd *deferred_cmd; /* Deferred command */ + struct rt_mmcsd_data *data; /* Current data request */ + unsigned int data_early : 1; /* Data finished before cmd */ + + unsigned int blocks; /* remaining PIO blocks */ + size_t align_buffer_sz; /* Bounce buffer size */ + rt_uint64_t align_addr; /* Mapped bounce buffer */ + + struct rt_workqueue *complete_wq; /* Request completion wq */ + struct rt_work complete_work; /* Request completion work */ + + struct rt_workqueue *irq_wq; + struct rt_work irq_work; + + struct rt_timer timer; /* Timer for timeouts */ + struct rt_timer data_timer; /* Timer for data timeouts */ + + rt_uint32_t caps; /* CAPABILITY_0 */ + rt_uint32_t caps1; /* CAPABILITY_1 */ + rt_bool_t read_caps; /* Capability flags have been read */ + + rt_bool_t sdhci_core_to_disable_vqmmc; /* sdhci core can disable vqmmc */ + unsigned int ocr_avail_sdio; /* OCR bit masks */ + unsigned int ocr_avail_sd; + unsigned int ocr_avail_mmc; + rt_uint32_t ocr_mask; /* available voltages */ + + unsigned timing; /* Current timing */ + + rt_uint32_t thread_isr; + + /* cached registers */ + rt_uint32_t ier; + + rt_bool_t cqe_on; /* CQE is operating */ + rt_uint32_t cqe_ier; /* CQE interrupt mask */ + rt_uint32_t cqe_err_ier; /* CQE error interrupt mask */ + + rt_wqueue_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ + unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ + + unsigned int tuning_count; /* Timer count for re-tuning */ + unsigned int tuning_mode; /* Re-tuning mode supported by host */ + unsigned int tuning_err; /* Error code for re-tuning */ +#define RT_SDHCI_TUNING_MODE_1 0 +#define RT_SDHCI_TUNING_MODE_2 1 +#define RT_SDHCI_TUNING_MODE_3 2 + /* Delay (ms) between tuning commands */ + int tuning_delay; + int tuning_loop_count; + + /* Host SDMA buffer boundary. */ + rt_uint32_t sdma_boundary; + rt_uint64_t data_timeout; + + unsigned long private[]; +}; + +static inline rt_uint8_t u8_read(const volatile void *addr) +{ + return *(const volatile rt_uint8_t *)addr; +} + +static inline rt_uint16_t u16_read(const volatile void *addr) +{ + return *(const volatile rt_uint16_t *)addr; +} + +static inline rt_uint32_t u32_read(const volatile void *addr) +{ + return *(const volatile rt_uint32_t *)addr; +} + +static inline void u8_write(rt_uint8_t value, volatile void *addr) +{ + *(volatile rt_uint8_t *)addr = value; +} + +static inline void u16_write(rt_uint16_t value, volatile void *addr) +{ + *(volatile rt_uint16_t *)addr = value; +} + +static inline void u32_write(rt_uint32_t value, volatile void *addr) +{ + *(volatile rt_uint32_t *)addr = value; +} + +#define readb(c) u8_read(c) +#define readw(c) u16_read(c) +#define readl(c) u32_read(c) +#define readsb(p, d, l) ({ __raw_readsb(p,d,l); __iormb(); }) +#define readsw(p, d, l) ({ __raw_readsw(p,d,l); __iormb(); }) +#define readsl(p, d, l) ({ __raw_readsl(p,d,l); __iormb(); }) + +#define writeb(v, c) u8_write(v, c) +#define writew(v, c) u16_write(v, c) +#define writel(v, c) u32_write(v, c) +#define writesb(p, d, l) ({ __iowmb(); __raw_writesb(p,d,l); }) +#define writesw(p, d, l) ({ __iowmb(); __raw_writesw(p,d,l); }) +#define writesl(p, d, l) ({ __iowmb(); __raw_writesl(p,d,l); }) + +static inline void rt_sdhci_writel(struct rt_sdhci_host *host, rt_uint32_t val, int reg) +{ + writel(val, host->ioaddr + reg); +} + +static inline void rt_sdhci_writew(struct rt_sdhci_host *host, rt_uint16_t val, int reg) +{ + writew(val, host->ioaddr + reg); +} + +static inline void rt_sdhci_writeb(struct rt_sdhci_host *host, rt_uint8_t val, int reg) +{ + writeb(val, host->ioaddr + reg); +} + +static inline rt_uint32_t rt_sdhci_readl(struct rt_sdhci_host *host, int reg) +{ + return readl(host->ioaddr + reg); +} + +static inline rt_uint16_t rt_sdhci_readw(struct rt_sdhci_host *host, int reg) +{ + return readw(host->ioaddr + reg); +} + +static inline rt_uint8_t rt_sdhci_readb(struct rt_sdhci_host *host, int reg) +{ + return readb(host->ioaddr + reg); +} + + +struct rt_sdhci_ops +{ + void (*set_clock)(struct rt_sdhci_host *host, unsigned int clock); + void (*set_power)(struct rt_sdhci_host *host, unsigned char mode, + unsigned short vdd); + rt_uint32_t (*irq)(struct rt_sdhci_host *host, rt_uint32_t intmask); + int (*set_dma_mask)(struct rt_sdhci_host *host); + int (*enable_dma)(struct rt_sdhci_host *host); + unsigned int (*get_max_clock)(struct rt_sdhci_host *host); + unsigned int (*get_min_clock)(struct rt_sdhci_host *host); + unsigned int (*get_timeout_clock)(struct rt_sdhci_host *host); + unsigned int (*get_max_timeout_count)(struct rt_sdhci_host *host); + void (*set_timeout)(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd); + void (*set_bus_width)(struct rt_sdhci_host *host, int width); + unsigned int (*get_ro)(struct rt_sdhci_host *host); + void (*reset)(struct rt_sdhci_host *host, rt_uint8_t mask); + int (*platform_execute_tuning)(struct rt_sdhci_host *host, rt_uint32_t opcode); + void (*set_uhs_signaling)(struct rt_sdhci_host *host, unsigned int uhs); + void (*hw_reset)(struct rt_sdhci_host *host); + void (*card_event)(struct rt_sdhci_host *host); + void (*voltage_switch)(struct rt_sdhci_host *host); + void (*request_done)(struct rt_sdhci_host *host, + struct rt_mmcsd_req *mrq); +}; + + +struct rt_sdhci_host *rt_sdhci_alloc_host(struct rt_device *dev, size_t priv_size); +void rt_sdhci_free_host(struct rt_sdhci_host *host); + +static inline void *sdhci_priv(struct rt_sdhci_host *host) +{ + return host->private; +} + +void rt_sdhci_read_caps(struct rt_sdhci_host *host, const rt_uint16_t *ver, + const rt_uint32_t *caps, const rt_uint32_t *caps1); +int rt_sdhci_setup_host(struct rt_sdhci_host *host); +void rt_sdhci_cleanup_host(struct rt_sdhci_host *host); +int rt_sdhci_set_and_add_host(struct rt_sdhci_host *host); +int rt_sdhci_init_host(struct rt_sdhci_host *host); +void rt_sdhci_uninit_host(struct rt_sdhci_host *host, int dead); + +rt_uint16_t rt_sdhci_clk_set(struct rt_sdhci_host *host, unsigned int clock, + unsigned int *actual_clock); +void rt_sdhci_set_clock(struct rt_sdhci_host *host, unsigned int clock); +void rt_sdhci_clk_enable(struct rt_sdhci_host *host, rt_uint16_t clk); +void rt_sdhci_set_power(struct rt_sdhci_host *host, unsigned char mode,unsigned short vdd); +void rt_read_reg(struct rt_sdhci_host* host); + +void rt_sdhci_set_power_with_noreg(struct rt_sdhci_host *host, unsigned char mode, + unsigned short vdd); +void rt_sdhci_start_request(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq); +int rt_sdhci_start_request_atomic(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq); +void rt_sdhci_set_bus_width(struct rt_sdhci_host *host, int width); +void rt_sdhci_reset(struct rt_sdhci_host *host, rt_uint8_t mask); +void rt_sdhci_set_uhs(struct rt_sdhci_host *host, unsigned timing); +int rt_sdhci_execute_tuning(struct rt_mmc_host *mmc, rt_uint32_t opcode); +int __sdhci_execute_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode); +void rt_sdhci_ios_set(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios); +int rt_sdhci_start_signal_voltage_switch(struct rt_mmc_host *mmc, + struct rt_mmcsd_io_cfg *ios); +void rt_sdhci_enable_io_irq(struct rt_mmc_host *mmc, int enable); +void rt_sdhci_start_tuning(struct rt_sdhci_host *host); +void rt_sdhci_end_tuning(struct rt_sdhci_host *host); +void rt_sdhci_reset_tuning(struct rt_sdhci_host *host); +void rt_sdhci_send_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode); +void rt_sdhci_abort_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode); +void rt_sdhci_data_irq_timeout(struct rt_sdhci_host *host, rt_bool_t enable); +void rt_sdhci_timeout_set(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd); +void rt_read_reg_debug(struct rt_sdhci_host* host); + +#endif /* __RT_SDHCI_HW_H */ diff --git a/rt-thread/components/drivers/sdio/sdhci/include/sdhci_host.h b/rt-thread/components/drivers/sdio/sdhci/include/sdhci_host.h new file mode 100644 index 0000000..8584ab4 --- /dev/null +++ b/rt-thread/components/drivers/sdio/sdhci/include/sdhci_host.h @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ +#ifndef __RT_SDHCI_MMC_H__ +#define __RT_SDHCI_MMC_H__ + +#include +#include +#include +#include +#include +#define mmc_dev(x) ((x)->parent) + +#define MMC_SEND_TUNING_BLOCK_HS200 SEND_TUNING_BLOCK_HS200 +#define MMC_SEND_TUNING_BLOCK SEND_TUNING_BLOCK +#define MMC_STOP_TRANSMISSION STOP_TRANSMISSION +#define MMC_BUS_TEST_R 14 /* adtc R1 */ +#define MMC_WRITE_MULTIPLE_BLOCK WRITE_MULTIPLE_BLOCK +#define MMC_READ_MULTIPLE_BLOCK READ_MULTIPLE_BLOCK + +#define MMC_TIMING_UHS_DDR50 MMCSD_TIMING_UHS_DDR50 +#define MMC_TIMING_UHS_SDR50 MMCSD_TIMING_UHS_SDR50 +#define MMC_TIMING_MMC_HS200 MMCSD_TIMING_MMC_HS200 +#define MMC_TIMING_MMC_HS400 MMCSD_TIMING_MMC_HS400 +#define MMC_TIMING_UHS_SDR104 MMCSD_TIMING_UHS_SDR104 +#define MMC_TIMING_UHS_SDR25 MMCSD_TIMING_UHS_SDR25 +#define MMC_TIMING_MMC_DDR52 MMCSD_TIMING_MMC_DDR52 +#define MMC_TIMING_UHS_SDR12 MMCSD_TIMING_UHS_SDR12 +#define MMC_TIMING_SD_HS MMCSD_TIMING_SD_HS +#define MMC_TIMING_MMC_HS MMCSD_TIMING_MMC_HS + +#define MMC_POWER_OFF MMCSD_POWER_OFF +#define MMC_POWER_UP MMCSD_POWER_UP +#define MMC_POWER_ON MMCSD_POWER_ON +#define MMC_POWER_UNDEFINED 3 + +#define MMC_SET_DRIVER_TYPE_B 0 +#define MMC_SET_DRIVER_TYPE_A 1 +#define MMC_SET_DRIVER_TYPE_C 2 +#define MMC_SET_DRIVER_TYPE_D 3 + +#define MMC_SIGNAL_VOLTAGE_330 0 +#define MMC_SIGNAL_VOLTAGE_180 1 +#define MMC_SIGNAL_VOLTAGE_120 2 + +#define MMC_RSP_PRESENT (1 << 16) +#define MMC_RSP_136 (1 << 17) /* 136 bit response */ +#define MMC_RSP_CRC (1 << 18) /* expect valid crc */ +#define MMC_RSP_BUSY (1 << 19) /* card may send busy */ +#define MMC_RSP_OPCODE (1 << 20) /* response contains opcode */ + +#define MMC_RSP_NONE (0) +#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R1B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY) +#define MMC_RSP_R2 (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC) +#define MMC_RSP_R3 (MMC_RSP_PRESENT) +#define MMC_RSP_R4 (MMC_RSP_PRESENT) +#define MMC_RSP_R5 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) + +#define MMC_CMD_ADTC CMD_ADTC + +#define MMC_BUS_WIDTH_8 MMCSD_BUS_WIDTH_8 +#define MMC_BUS_WIDTH_4 MMCSD_BUS_WIDTH_4 +#define MMC_BUS_WIDTH_1 MMCSD_BUS_WIDTH_1 + +#define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ +#define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ +enum mmc_blk_status +{ + MMC_BLK_SUCCESS = 0, + MMC_BLK_PARTIAL, + MMC_BLK_CMD_ERR, + MMC_BLK_RETRY, + MMC_BLK_ABORT, + MMC_BLK_DATA_ERR, + MMC_BLK_ECC_ERR, + MMC_BLK_NOMEDIUM, + MMC_BLK_NEW_REQUEST, +}; + +#define MMC_NUM_CLK_PHASES (MMC_TIMING_MMC_HS400 + 1) + +struct rt_mmc_host ; + +struct mmc_host_ops +{ + void (*request)(struct rt_mmc_host *host, struct rt_mmcsd_req *req); + void (*set_ios)(struct rt_mmc_host *host, struct rt_mmcsd_io_cfg *ios); + int (*get_ro)(struct rt_mmc_host *host); + int (*get_cd)(struct rt_mmc_host *host); + void (*enable_sdio_irq)(struct rt_mmc_host *host, int enable); + void (*ack_sdio_irq)(struct rt_mmc_host *host); + int (*start_signal_voltage_switch)(struct rt_mmc_host *host, struct rt_mmcsd_io_cfg *ios); + int (*card_busy)(struct rt_mmc_host *host); + int (*execute_tuning)(struct rt_mmc_host *host, unsigned opcode); + int (*prepare_hs400_tuning)(struct rt_mmc_host *host, struct rt_mmcsd_io_cfg *ios); + int (*hs400_prepare_ddr)(struct rt_mmc_host *host); + void (*hs400_downgrade)(struct rt_mmc_host *host); + void (*hs400_complete)(struct rt_mmc_host *host); + void (*hs400_enhanced_strobe)(struct rt_mmc_host *host, + struct rt_mmcsd_io_cfg* ios); + void (*hw_reset)(struct rt_mmc_host* host); + void (*card_event)(struct rt_mmc_host* host); +}; + +struct regulator; +struct mmc_pwrseq; + +struct mmc_supply +{ + struct regulator *vmmc; /* Card power supply */ + struct regulator *vqmmc; /* Optional Vccq supply */ +}; + +struct mmc_ctx +{ + struct task_struct *task; +}; + +/* VDD voltage 3.3 ~ 3.4 */ +#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */ +#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */ + +#define MMC_CAP2_HS200_1_8V_SDR MMCSD_SUP_HS200_1V8 +#define MMC_CAP_4_BIT_DATA MMCSD_BUSWIDTH_4 +#define MMC_CAP_8_BIT_DATA MMCSD_BUSWIDTH_8 +#define MMC_CAP2_HS200 MMCSD_SUP_HS200 +#define MMC_CAP_MMC_HIGHSPEED MMCSD_SUP_HIGHSPEED +#define MMC_CAP_SD_HIGHSPEED MMCSD_SUP_HIGHSPEED +#define MMC_CAP_1_8V_DDR MMCSD_SUP_DDR_1V8 +#define MMC_CAP_3_3V_DDR MMCSD_SUP_DDR_3V3 +#define MMC_CAP_1_2V_DDR MMCSD_SUP_DDR_1V2 +#define MMC_CAP_NONREMOVABLE MMCSD_SUP_NONREMOVABLE + + +#define MMC_CAP_UHS_DDR50 0 +#define MMC_CAP2_HS400 0 +#define MMC_CAP_UHS_SDR50 0 +#define MMC_CAP_UHS_SDR25 0 +#define MMC_CAP_UHS_SDR12 0 +#define MMC_CAP_UHS_SDR104 0 +#define MMC_CAP_UHS 0 +#define MMC_CAP2_HSX00_1_8V 0 +#define MMC_CAP2_HS400_ES 0 +#define MMC_CAP_NEEDS_POLL 0 +#define MMC_CAP2_HSX00_1_2V 0 +#define MMC_CAP2_HS400_1_8V 0 +#define MMC_CAP_DRIVER_TYPE_D 0 +#define MMC_CAP_DRIVER_TYPE_C 0 +#define MMC_SET_DRIVER_TYPE_B 0 +#define MMC_CAP_DRIVER_TYPE_A 0 +#define MMC_CAP2_SDIO_IRQ_NOTHREAD 0 +#define MMC_CAP_CMD23 0 +#define MMC_CAP_SDIO_IRQ 0 + +#define MMC_CAP2_NO_SDIO (1 << 19) +#define MMC_CAP2_NO_SD (1 << 21) +#define MMC_CAP2_NO_MMC (1 << 22) +#define MMC_CAP2_CQE (1 << 23) + +#define MMC_VDD_165_195 VDD_165_195 +#define MMC_VDD_20_21 VDD_20_21 +#define MMC_VDD_29_30 VDD_29_30 +#define MMC_VDD_30_31 VDD_30_31 +#define MMC_VDD_32_33 VDD_32_33 +#define MMC_VDD_33_34 VDD_33_34 + + +struct rt_mmc_host +{ + struct rt_mmcsd_host rthost; + struct rt_device *parent; + int index; + const struct mmc_host_ops *ops; + unsigned int f_min; + unsigned int f_max; + unsigned int f_init; + rt_uint32_t ocr_avail; + rt_uint32_t ocr_avail_sdio; /* SDIO-specific OCR */ + rt_uint32_t ocr_avail_sd; /* SD-specific OCR */ + rt_uint32_t ocr_avail_mmc; /* MMC-specific OCR */ + struct wakeup_source *ws; /* Enable consume of uevents */ + rt_uint32_t max_current_330; + rt_uint32_t max_current_300; + rt_uint32_t max_current_180; + rt_uint32_t caps; /* Host capabilities */ + + rt_uint32_t caps2; /* More host capabilities */ + + + /* host specific block data */ + unsigned int max_seg_size; /* see blk_queue_max_segment_size */ + unsigned short max_segs; /* see blk_queue_max_segments */ + unsigned short unused; + unsigned int max_req_size; /* maximum number of bytes in one req */ + unsigned int max_blk_size; /* maximum size of one mmc block */ + unsigned int max_blk_count; /* maximum number of blocks in one req */ + unsigned int max_busy_timeout; /* max busy timeout in ms */ + struct rt_mmcsd_io_cfg ios; /* current io bus settings */ + unsigned int retune_period; + /* group bitfields together to minimize padding */ + unsigned int use_spi_crc : 1; + unsigned int claimed : 1; /* host exclusively claimed */ + unsigned int doing_init_tune : 1; /* initial tuning in progress */ + unsigned int can_retune : 1; /* re-tuning can be used */ + unsigned int doing_retune : 1; /* re-tuning in progress */ + unsigned int retune_now : 1; /* do re-tuning at next req */ + unsigned int retune_paused : 1; /* re-tuning is temporarily disabled */ + unsigned int retune_crc_disable : 1; /* don't trigger retune upon crc */ + unsigned int can_dma_map_merge : 1; /* merging can be used */ + unsigned int vqmmc_enabled : 1; /* vqmmc regulator is enabled */ + + int need_retune; /* re-tuning is needed */ + int hold_retune; /* hold off re-tuning */ + rt_bool_t trigger_card_event; /* card_event necessary */ + unsigned int sdio_irqs; + rt_bool_t sdio_irq_pending; + + struct led_trigger *led; /* activity led */ + + struct mmc_supply supply; + + + /* Ongoing data transfer that allows commands during transfer */ + struct rt_mmcsd_req *ongoing_mrq; + + + unsigned int actual_clock; /* Actual HC clock rate */ + rt_uint32_t pm_caps; + unsigned long private[]; +}; + + +static inline int mmc_card_is_removable(struct rt_mmc_host *host) +{ + return !(host->caps & MMC_CAP_NONREMOVABLE); +} + +struct device_node; +struct rt_mmc_host *rt_mmc_alloc_host(int extra, struct rt_device *); +int rt_mmc_add_host(struct rt_mmc_host *); +void rt_mmc_remove_host(struct rt_mmc_host *); +void rt_mmc_free_host(struct rt_mmc_host *); +int rt_mmc_of_parse(struct rt_mmc_host *host); +int rt_mmc_of_parse_voltage(struct rt_mmc_host *host, rt_uint32_t *mask); + +static inline void *mmc_priv(struct rt_mmc_host *host) +{ + return (void *)host->private; +} + + +#define mmc_host_is_spi(host) ((host)->caps & MMC_CAP_SPI) + +#define mmc_dev(x) ((x)->parent) +#define mmc_classdev(x) (&(x)->class_dev) +#define mmc_hostname(x) (x->parent->parent.name) + +void rt_mmc_detect_change(struct rt_mmc_host *, unsigned long delay); +void rt_mmc_request_done(struct rt_mmc_host *, struct rt_mmcsd_req *); +void mmc_command_done(struct rt_mmc_host *host, struct rt_mmcsd_req *mrq); + +void mmc_cqe_request_done(struct rt_mmc_host *host, struct rt_mmcsd_req *mrq); + +static inline rt_bool_t sdio_irq_claimed(struct rt_mmc_host *host) +{ + return host->sdio_irqs > 0; +} + +static inline int mmc_regulator_set_ocr(struct rt_mmc_host *mmc, + struct regulator *supply, + unsigned short vdd_bit) +{ + return 0; +} + +int mmc_regulator_get_supply(struct rt_mmc_host *mmc); +int mmc_regulator_enable_vqmmc(struct rt_mmc_host *mmc); +void mmc_regulator_disable_vqmmc(struct rt_mmc_host *mmc); + +void mmc_retune_timer_stop(struct rt_mmc_host* host); + +enum dma_data_direction +{ + DMA_BIDIRECTIONAL = 0, + DMA_TO_DEVICE = 1, + DMA_FROM_DEVICE = 2, + DMA_NONE = 3, +}; +#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL << (n)) - 1)) +static inline void mmc_retune_needed(struct rt_mmc_host *host) +{ + if (host->can_retune) + host->need_retune = 1; +} + +static inline rt_bool_t mmc_can_retune(struct rt_mmc_host *host) +{ + return host->can_retune == 1; +} + +static inline rt_bool_t mmc_doing_retune(struct rt_mmc_host *host) +{ + return host->doing_retune == 1; +} + +static inline rt_bool_t mmc_doing_tune(struct rt_mmc_host *host) +{ + return host->doing_retune == 1 || host->doing_init_tune == 1; +} + +static inline int mmc_get_dma_dir(struct rt_mmcsd_data *data) +{ + return data->flags & DATA_DIR_WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE; +} + +static inline rt_bool_t mmc_op_multi(rt_uint32_t opcode) +{ + return opcode == MMC_WRITE_MULTIPLE_BLOCK || opcode == MMC_READ_MULTIPLE_BLOCK; +} + +static inline rt_bool_t mmc_op_tuning(rt_uint32_t opcode) +{ + return opcode == MMC_SEND_TUNING_BLOCK || opcode == MMC_SEND_TUNING_BLOCK_HS200; +} + +int rt_mmc_gpio_get_cd(struct rt_mmc_host *host); +void rt_mmc_detect_change(struct rt_mmc_host *host, unsigned long delay); +int rt_mmc_regulator_set_vqmmc(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios); +rt_bool_t rt_mmc_can_gpio_ro(struct rt_mmc_host *host); +int rt_mmc_gpio_get_ro(struct rt_mmc_host *host); + +int rt_mmc_send_abort_tuning(struct rt_mmc_host *host, rt_uint32_t opcode); +int rt_mmc_of_parse(struct rt_mmc_host *host); + + +#endif diff --git a/rt-thread/components/drivers/sdio/sdhci/include/sdhci_misc.h b/rt-thread/components/drivers/sdio/sdhci/include/sdhci_misc.h new file mode 100644 index 0000000..4614458 --- /dev/null +++ b/rt-thread/components/drivers/sdio/sdhci/include/sdhci_misc.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ +#ifndef __RT_SDHCI_MISC_H__ +#define __RT_SDHCI_MISC_H__ + + +#define __BF_FIELD_CHECK(...) +#define __bf_shf(x) (__builtin_ffsll(x) - 1) +#define FIELD_GET(_mask, _reg) \ + ({ \ + __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: "); \ + (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ + }) + +#define FIELD_PREP(_mask, _val) \ + ({ \ + __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \ + ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ + }) + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#define min_t(type, x, y) (((type)x < (type)y) ? x : y) +#define max_t(type, x, y) (((type)x > (type)y) ? x : y) +#define min(x, y) ((x) < (y) ? (x) : (y)) + +#define from_timer(var, callback_timer, timer_fieldname) \ + container_of(callback_timer, typeof(*var), timer_fieldname) + + +#define le32_to_cpu(x) (x) +#define le16_to_cpu(x) (x) +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define lower_32_bits(n) ((rt_uint32_t)((n) & 0xffffffff)) +#define upper_32_bits(n) ((rt_uint32_t)(((n) >> 16) >> 16)) + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#define do_div(n, base) ({ \ + uint32_t __base = (base); \ + uint32_t __rem; \ + __rem = ((uint64_t)(n)) % __base; \ + (n) = ((uint64_t)(n)) / __base; \ + __rem; \ +}) + +#define fallthrough \ + do { \ + } while (0) + +int regulator_is_supported_voltage(struct regulator *regulator, + int min_uV, int max_uV); +rt_bool_t rt_mmc_can_gpio_cd(struct rt_mmc_host *host); + +struct regulator +{ + const char *supply_name; +}; + +int regulator_get_current_limit(struct regulator *regulator); + +#endif diff --git a/rt-thread/components/drivers/sdio/sdhci/sdhci-platform.c b/rt-thread/components/drivers/sdio/sdhci/sdhci-platform.c new file mode 100644 index 0000000..d6cf4ed --- /dev/null +++ b/rt-thread/components/drivers/sdio/sdhci/sdhci-platform.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ +#include "sdhci-platform.h" + +static const struct rt_sdhci_ops sdhci_pltfm_ops = { + .set_clock = rt_sdhci_set_clock, + .set_bus_width = rt_sdhci_set_bus_width, + .reset = rt_sdhci_reset, + .set_uhs_signaling = rt_sdhci_set_uhs, +}; + +void rt_sdhci_get_property(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct rt_sdhci_host *host = pdev->priv; + struct rt_sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + rt_uint32_t bus_width; + + if (rt_dm_dev_prop_read_bool(dev, "sdhci,auto-cmd12")) + host->quirks |= RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; + + if (rt_dm_dev_prop_read_bool(dev, "sdhci,1-bit-only") || (rt_dm_dev_prop_read_u32(dev, "bus-width", &bus_width) == 0 && bus_width == 1)) + host->quirks |= RT_SDHCI_QUIRK_FORCE_1_BIT_DATA; + + if (rt_dm_dev_prop_read_bool(dev, "broken-cd")) + host->quirks |= RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION; + + if (rt_dm_dev_prop_read_bool(dev, "no-1-8-v")) + host->quirks2 |= RT_SDHCI_QUIRK2_NO_1_8_V; + + rt_dm_dev_prop_read_u32(dev, "clock-frequency", &pltfm_host->clock); + + if (rt_dm_dev_prop_read_bool(dev, "keep-power-in-suspend")) + host->mmc->pm_caps |= MMC_PM_KEEP_POWER; + + if (rt_dm_dev_prop_read_bool(dev, "wakeup-source") || rt_dm_dev_prop_read_bool(dev, "enable-sdio-wakeup")) /* legacy */ + host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; +} + +struct rt_sdhci_host *rt_sdhci_pltfm_init(struct rt_platform_device *pdev, + const struct rt_sdhci_pltfm_data *pdata, + size_t priv_size) +{ + struct rt_sdhci_host *host; + struct rt_device *dev = &pdev->parent; + void *ioaddr; + int irq; + + ioaddr = rt_dm_dev_iomap(dev, 0); + if (!ioaddr) + { + return RT_NULL; + } + + irq = rt_dm_dev_get_irq(dev, 0); + if (irq < 0) + { + return RT_NULL; + } + host = rt_sdhci_alloc_host(dev,sizeof(struct rt_sdhci_pltfm_host) + priv_size); + if (!host) + { + return RT_NULL; + } + host->irq = irq; + host->ioaddr = ioaddr; + host->hw_name = rt_dm_dev_get_name(dev); + + if (pdata && pdata->ops) + host->ops = pdata->ops; + else + host->ops = &sdhci_pltfm_ops; + if (pdata) + { + host->quirks = pdata->quirks; + host->quirks2 = pdata->quirks2; + } + + pdev->priv = host; + + return host; +} + +int rt_sdhci_pltfm_init_and_add_host(struct rt_platform_device *pdev, + const struct rt_sdhci_pltfm_data *pdata, + size_t priv_size) +{ + struct rt_sdhci_host *host; + int ret = 0; + + host = rt_sdhci_pltfm_init(pdev, pdata, priv_size); + if (!host) + return -RT_ERROR; + + rt_sdhci_get_property(pdev); + + ret = rt_sdhci_init_host(host); + if (ret) + rt_sdhci_pltfm_free(pdev); + + return ret; +} + +void rt_sdhci_pltfm_free(struct rt_platform_device *pdev) +{ + struct rt_sdhci_host *host = pdev->priv; + + rt_sdhci_free_host(host); +} + +void rt_sdhci_pltfm_remove(struct rt_platform_device *pdev) +{ + struct rt_sdhci_host *host = pdev->priv; + int dead = (readl(host->ioaddr + RT_SDHCI_INT_STATUS) == 0xffffffff); + + rt_sdhci_uninit_host(host, dead); + rt_sdhci_pltfm_free(pdev); +} diff --git a/rt-thread/components/drivers/sdio/sdhci/sdhci.c b/rt-thread/components/drivers/sdio/sdhci/sdhci.c new file mode 100644 index 0000000..6f52b5e --- /dev/null +++ b/rt-thread/components/drivers/sdio/sdhci/sdhci.c @@ -0,0 +1,3152 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ + +#include +#include +#include "sdhci.h" +#include +#define DBG_TAG "RT_SDHCI" +#ifdef DRV_DEBUG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif /* DRV_DEBUG */ +#include +static unsigned int debug_quirks = 0; +static unsigned int debug_quirks2; +/********************************************************* */ +/* cmd */ +/********************************************************* */ + + +void rt_read_reg_debug(struct rt_sdhci_host *host) +{ + rt_kprintf("0x00 addddddddddddd = %x \n", rt_sdhci_readl(host, 0x00)); + rt_kprintf("0x04 EMMC_BLOCKSIZE = %x \n", rt_sdhci_readw(host, 0x04)); + rt_kprintf("0x06 EMMC_BLOCKCOUNT = %x \n", rt_sdhci_readw(host, 0x06)); + rt_kprintf("0x08 RT_SDHCI_ARGUMENT = %x \n", rt_sdhci_readl(host, 0x08)); + rt_kprintf("0x0c EMMC_XFER_MODE = %x \n", rt_sdhci_readw(host, 0x0c)); + rt_kprintf("0x0e RT_SDHCI_COMMAND = %x \n", rt_sdhci_readw(host, 0x0e)); + rt_kprintf("0x24 RT_SDHCI_PRESENT_STATE = %x \n", rt_sdhci_readl(host, 0x24)); + rt_kprintf("0x28 RT_SDHCI_HOST_CONTROL = %x \n", rt_sdhci_readb(host, 0x28)); + rt_kprintf("0x29 RT_SDHCI_POWER_CONTROL = %x \n", rt_sdhci_readb(host, 0x29)); + rt_kprintf("0x2a EMMC_BGAP_CTRL = %x \n", rt_sdhci_readb(host, 0x2a)); + rt_kprintf("0x2c EMMC_CLK_CTRL = %x \n", rt_sdhci_readw(host, 0x2c)); + rt_kprintf("0x2e EMMC_TOUT_CTRL = %x \n", rt_sdhci_readb(host, 0x2e)); + rt_kprintf("0x2f EMMC_SW_RST = %x \n", rt_sdhci_readb(host, 0x2f)); + rt_kprintf("0x30 RT_SDHCI_INT_STATUS = %x \n", rt_sdhci_readw(host, 0x30)); + rt_kprintf("0x32 RT_SDHCI_ERR_INT_STATUS = %x \n", rt_sdhci_readw(host, 0x32)); + rt_kprintf("0x34 RT_SDHCI_INT_ENABLE = %x \n", rt_sdhci_readw(host, 0x34)); + rt_kprintf("0x36 EMMC ERROR INT STATEN = %x \n", rt_sdhci_readw(host, 0x36)); + rt_kprintf("0x38 EMMC NORMAL INT SIGNAL EN = %x \n", rt_sdhci_readw(host, 0x38)); + rt_kprintf("0x3a EMMC ERROR INT SIGNAL EN = %x \n", rt_sdhci_readw(host, 0x3a)); + rt_kprintf("0x3c EMMC_AUTO_CMD_STAT = %x \n", rt_sdhci_readw(host, 0x3c)); + rt_kprintf("0x3e EMMC_HOST_CTRL2 = %x \n", rt_sdhci_readw(host, 0x3e)); + rt_kprintf("0x40 EMMC_CAPABILITIES1 = %x \n", rt_sdhci_readl(host, 0x40)); + rt_kprintf("0x44 EMMC_CAPABILITIES2 = %x \n", rt_sdhci_readl(host, 0x44)); + rt_kprintf("0x52 EMMC_FORC_ERR_INT_STAT = %x \n", rt_sdhci_readw(host, 0x52)); + rt_kprintf("0x54 EMMC_ADMA_ERR_STAT = %x \n", rt_sdhci_readb(host, 0x54)); + rt_kprintf("0x58 EMMC_ADMA_SA = %x \n", rt_sdhci_readl(host, 0x58)); + rt_kprintf("0x66 EMMC_PRESET_SDR12 = %x \n", rt_sdhci_readw(host, 0x66)); + rt_kprintf("0x68 EMMC_PRESET_SDR25 = %x \n", rt_sdhci_readw(host, 0x68)); + rt_kprintf("0x6a EMMC_PRESET_SDR50 = %x \n", rt_sdhci_readw(host, 0x6a)); + rt_kprintf("0x6c EMMC_PRESET_SDR104 = %x \n", rt_sdhci_readw(host, 0x6c)); + rt_kprintf("0x6e EMMC_PRESET_DDR50 = %x \n", rt_sdhci_readw(host, 0x6e)); + rt_kprintf("0x78 EMMC_ADMA_ID = %x \n", rt_sdhci_readl(host, 0x78)); + rt_kprintf("0xfe EMMC_HOST_CNTRL_VERS = %x \n", rt_sdhci_readw(host, 0xfe)); + +} +static inline rt_bool_t sdhci_has_requests(struct rt_sdhci_host *host) +{ + return host->cmd || host->data_cmd; +} + +static inline rt_bool_t sdhci_auto_cmd23(struct rt_sdhci_host *host, + struct rt_mmcsd_req *mrq) +{ + return mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD23); +} + +static inline rt_bool_t sdhci_auto_cmd12(struct rt_sdhci_host *host, + struct rt_mmcsd_req *mrq) +{ + return !mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD12) && !mrq->cap_cmd_during_tfr; +} + +static inline rt_bool_t sdhci_manual_cmd23(struct rt_sdhci_host *host, + struct rt_mmcsd_req *mrq) +{ + return mrq->sbc && !(host->flags & RT_SDHCI_AUTO_CMD23); +} + +static inline rt_bool_t sdhci_data_line_cmd(struct rt_mmcsd_cmd *cmd) +{ + return cmd->data || cmd->flags & MMC_RSP_BUSY; +} + +void rt_sdhci_data_irq_timeout(struct rt_sdhci_host *host, rt_bool_t enable) +{ + if (enable) + host->ier |= RT_SDHCI_INT_DATA_TIMEOUT; + else + host->ier &= ~RT_SDHCI_INT_DATA_TIMEOUT; + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); +} + +void rt_sdhci_set_uhs(struct rt_sdhci_host *host, unsigned timing) +{ + rt_uint16_t ctrl_2; + + ctrl_2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + + ctrl_2 &= ~RT_SDHCI_CTRL_UHS_MASK; + if ((timing == MMC_TIMING_MMC_HS200) || (timing == MMC_TIMING_UHS_SDR104)) + ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR104; + else if (timing == MMC_TIMING_UHS_SDR12) + ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR12; + else if (timing == MMC_TIMING_UHS_SDR25) + ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR25; + else if (timing == MMC_TIMING_UHS_SDR50) + ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR50; + else if ((timing == MMC_TIMING_UHS_DDR50) || (timing == MMC_TIMING_MMC_DDR52)) + ctrl_2 |= RT_SDHCI_CTRL_UHS_DDR50; + else if (timing == MMC_TIMING_MMC_HS400) + ctrl_2 |= RT_SDHCI_CTRL_HS400; /* Non-standard */ + rt_sdhci_writew(host, ctrl_2, RT_SDHCI_HOST_CONTROL2); +} + +void rt_sdhci_set_bus_width(struct rt_sdhci_host *host, int width) +{ + rt_uint8_t ctrl; + + ctrl = rt_sdhci_readb(host, RT_SDHCI_HOST_CONTROL); + if (width == MMC_BUS_WIDTH_8) + { + ctrl &= ~RT_SDHCI_CTRL_4BITBUS; + ctrl |= RT_SDHCI_CTRL_8BITBUS; + } + else + { + if (host->mmc->caps & MMC_CAP_8_BIT_DATA) + ctrl &= ~RT_SDHCI_CTRL_8BITBUS; + if (width == MMC_BUS_WIDTH_4) + ctrl |= RT_SDHCI_CTRL_4BITBUS; + else + ctrl &= ~RT_SDHCI_CTRL_4BITBUS; + } + rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL); +} + +static inline rt_bool_t sdhci_can_64bit_dma(struct rt_sdhci_host *host) +{ + if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode) + return host->caps & RT_SDHCI_CAN_64BIT_V4; + + return host->caps & RT_SDHCI_CAN_64BIT; +} + +static void sdhci_do_enable_v4_mode(struct rt_sdhci_host *host) +{ + rt_uint16_t ctrl2; + + ctrl2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + if (ctrl2 & RT_SDHCI_CTRL_V4_MODE) + return; + + ctrl2 |= RT_SDHCI_CTRL_V4_MODE; + rt_sdhci_writew(host, ctrl2, RT_SDHCI_HOST_CONTROL2); +} + +void rt_sdhci_cleanup_host(struct rt_sdhci_host *host) +{ + return; +} + +static void sdhci_set_default_irqs(struct rt_sdhci_host *host) +{ + host->ier = RT_SDHCI_INT_BUS_POWER | RT_SDHCI_INT_DATA_END_BIT | RT_SDHCI_INT_DATA_CRC | RT_SDHCI_INT_DATA_TIMEOUT | RT_SDHCI_INT_INDEX | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_TIMEOUT | RT_SDHCI_INT_DATA_END | RT_SDHCI_INT_RESPONSE; + + if (host->tuning_mode == RT_SDHCI_TUNING_MODE_2 || host->tuning_mode == RT_SDHCI_TUNING_MODE_3) + host->ier |= RT_SDHCI_INT_RETUNE; + + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); +} + + +static inline void sdhci_auto_cmd_select(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd, + rt_uint16_t *mode) +{ + rt_bool_t use_cmd12 = sdhci_auto_cmd12(host, cmd->mrq) && (cmd->cmd_code != SD_IO_RW_EXTENDED); + rt_bool_t use_cmd23 = sdhci_auto_cmd23(host, cmd->mrq); + rt_uint16_t ctrl2; + + if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode && (use_cmd12 || use_cmd23)) + { + *mode |= RT_SDHCI_TRNS_AUTO_SEL; + + ctrl2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + if (use_cmd23) + ctrl2 |= RT_SDHCI_CMD23_ENABLE; + else + ctrl2 &= ~RT_SDHCI_CMD23_ENABLE; + rt_sdhci_writew(host, ctrl2, RT_SDHCI_HOST_CONTROL2); + + return; + } + + if (use_cmd12) + *mode |= RT_SDHCI_TRNS_AUTO_CMD12; + else if (use_cmd23) + *mode |= RT_SDHCI_TRNS_AUTO_CMD23; +} + + +static rt_bool_t sdhci_present_error(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd, rt_bool_t present) +{ + if (!present || host->flags & RT_SDHCI_DEVICE_DEAD) + { + cmd->err = -ENOMEDIUM; + return RT_TRUE; + } + + return RT_FALSE; +} + +static rt_uint16_t sdhci_get_preset_value(struct rt_sdhci_host *host) +{ + rt_uint16_t preset = 0; + + switch (host->timing) + { + case MMC_TIMING_MMC_HS: + case MMC_TIMING_SD_HS: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_HIGH_SPEED); + break; + case MMC_TIMING_UHS_SDR12: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR12); + break; + case MMC_TIMING_UHS_SDR25: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR25); + break; + case MMC_TIMING_UHS_SDR50: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR50); + break; + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR104); + break; + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_DDR50); + break; + case MMC_TIMING_MMC_HS400: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_HS400); + break; + default: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR12); + break; + } + return preset; +} + +static void sdhci_set_card_detection(struct rt_sdhci_host *host, rt_bool_t enable) +{ + rt_uint32_t present; + + if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION) || !mmc_card_is_removable(host->mmc)) + return; + + if (enable) + { + present = rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & RT_SDHCI_CARD_PRESENT; + + host->ier |= present ? RT_SDHCI_INT_CARD_REMOVE : RT_SDHCI_INT_CARD_INSERT; + } + else + { + host->ier &= ~(RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CARD_INSERT); + } + + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); +} + +static void sdhci_enable_card_detection(struct rt_sdhci_host *host) +{ + sdhci_set_card_detection(host, RT_TRUE); +} + +/********************************************************* */ +/* reset */ +/********************************************************* */ +enum sdhci_reset_reason +{ + RT_SDHCI_RESET_FOR_INIT, + RT_SDHCI_RESET_FOR_REQUEST_ERROR, + RT_SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY, + RT_SDHCI_RESET_FOR_TUNING_ABORT, + RT_SDHCI_RESET_FOR_CARD_REMOVED, + RT_SDHCI_RESET_FOR_CQE_RECOVERY, +}; + +static rt_bool_t sdhci_needs_reset(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq) +{ + return (!(host->flags & RT_SDHCI_DEVICE_DEAD) && ((mrq->cmd && mrq->cmd->err) || (mrq->sbc && mrq->sbc->err) || (mrq->data && mrq->data->stop && mrq->data->stop->err) || (host->quirks & RT_SDHCI_QUIRK_RESET_AFTER_REQUEST))); +} + +static rt_bool_t sdhci_do_reset(struct rt_sdhci_host *host, rt_uint8_t mask) +{ + if (host->quirks & RT_SDHCI_QUIRK_NO_CARD_NO_RESET) + { + struct rt_mmc_host *mmc = host->mmc; + + if (!mmc->ops->get_cd(mmc)) + return RT_FALSE; + } + if (host->ops->reset) + { + host->ops->reset(host, mask); + } + return RT_TRUE; +} + +static void sdhci_reset_for_reason(struct rt_sdhci_host *host, enum sdhci_reset_reason reason) +{ + if (host->quirks2 & RT_SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER) + { + sdhci_do_reset(host, RT_SDHCI_RESET_CMD | RT_SDHCI_RESET_DATA); + return; + } + + switch (reason) + { + case RT_SDHCI_RESET_FOR_INIT: + sdhci_do_reset(host, RT_SDHCI_RESET_CMD | RT_SDHCI_RESET_DATA); + break; + case RT_SDHCI_RESET_FOR_REQUEST_ERROR: + case RT_SDHCI_RESET_FOR_TUNING_ABORT: + case RT_SDHCI_RESET_FOR_CARD_REMOVED: + case RT_SDHCI_RESET_FOR_CQE_RECOVERY: + sdhci_do_reset(host, RT_SDHCI_RESET_CMD); + sdhci_do_reset(host, RT_SDHCI_RESET_DATA); + break; + case RT_SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY: + sdhci_do_reset(host, RT_SDHCI_RESET_DATA); + break; + } +} + +#define sdhci_reset_for(h, r) sdhci_reset_for_reason((h), RT_SDHCI_RESET_FOR_##r) + +static void sdhci_reset_for_all(struct rt_sdhci_host *host) +{ + if (sdhci_do_reset(host, RT_SDHCI_RESET_ALL)) + { + if (host->flags & (RT_SDHCI_USE_SDMA)) + { + if (host->ops->enable_dma) + host->ops->enable_dma(host); + } + host->preset_enabled = RT_FALSE; + } +} + + +static void sdhci_runtime_pm_bus_on(struct rt_sdhci_host *host) +{ + if (host->bus_on) + return; + host->bus_on = RT_TRUE; +} + +static void sdhci_runtime_pm_bus_off(struct rt_sdhci_host *host) +{ + if (!host->bus_on) + return; + host->bus_on = RT_FALSE; +} + +void rt_sdhci_reset(struct rt_sdhci_host *host, rt_uint8_t mask) +{ + ssize_t timeout; + + rt_sdhci_writeb(host, mask, RT_SDHCI_SOFTWARE_RESET); + + if (mask & RT_SDHCI_RESET_ALL) + { + host->clock = 0; + if (host->quirks2 & RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_off(host); + } + + timeout = rt_tick_from_millisecond(150); + while (1) + { + timeout = timeout - rt_tick_get(); + + + if (!(rt_sdhci_readb(host, RT_SDHCI_SOFTWARE_RESET) & mask)) + break; + if (timeout < 0) + { + rt_kprintf("%s: Reset 0x%x never completed.\n", + mmc_hostname(host->mmc), (int)mask); + rt_read_reg_debug(host); + return; + } + rt_hw_us_delay(10); + } +} + +/********************************************************* */ +/* data */ +/********************************************************* */ +static rt_ubase_t sdhci_sdma_address(struct rt_sdhci_host *host) +{ + return (rt_ubase_t)rt_kmem_v2p(host->data->buf); +} + +static void sdhci_set_adma_addr(struct rt_sdhci_host *host, rt_uint32_t addr) +{ + rt_sdhci_writel(host, lower_32_bits(addr), RT_SDHCI_ADMA_ADDRESS); + if (host->flags & RT_SDHCI_USE_64_BIT_DMA) + rt_sdhci_writel(host, upper_32_bits(addr), RT_SDHCI_ADMA_ADDRESS_HI); +} + +static void sdhci_set_sdma_addr(struct rt_sdhci_host *host, rt_uint32_t addr) +{ + if (host->v4_mode) + sdhci_set_adma_addr(host, addr); + else + rt_sdhci_writel(host, addr, RT_SDHCI_DMA_ADDRESS); +} + +static void sdhci_config_dma(struct rt_sdhci_host *host) +{ + rt_uint8_t ctrl; + rt_uint16_t ctrl2; + + if (host->version < RT_SDHCI_SPEC_200) + return; + + ctrl = rt_sdhci_readb(host, RT_SDHCI_HOST_CONTROL); + + ctrl &= ~RT_SDHCI_CTRL_DMA_MASK; + if (!(host->flags & RT_SDHCI_REQ_USE_DMA)) + goto out; + + /* Note if DMA Select is zero then SDMA is selected */ + if (host->flags & RT_SDHCI_USE_64_BIT_DMA) + { + + if (host->v4_mode) + { + ctrl2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + ctrl2 |= RT_SDHCI_CTRL_64BIT_ADDR; + rt_sdhci_writew(host, ctrl2, RT_SDHCI_HOST_CONTROL2); + } + } + +out: + rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL); +} + +static inline void sdhci_set_block_info(struct rt_sdhci_host *host, + struct rt_mmcsd_data *data) +{ + int boundary; + size_t total_size = data->blks * data->blksize; + + if (total_size <= 512) + boundary = 0; /* 4k bytes*/ + else if (total_size <= 1024) + boundary = 1; /* 8 KB*/ + else if (total_size <= 2048) + boundary = 2; /* 16 KB*/ + else if (total_size <= 4096) + boundary = 3; /* 32 KB*/ + else if (total_size <= 8192) + boundary = 4; /* 64 KB*/ + else if (total_size <= 16384) + boundary = 5; /* 128 KB*/ + else if (total_size <= 32768) + boundary = 6; /* 256 KB*/ + else + boundary = 7; /* 512 KB*/ + rt_sdhci_writew(host, + RT_SDHCI_MAKE_BLKSZ(boundary, data->blksize), + RT_SDHCI_BLOCK_SIZE); + + if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode && (host->quirks2 & RT_SDHCI_QUIRK2_USE_32BIT_BLK_CNT)) + { + if (rt_sdhci_readw(host, RT_SDHCI_BLOCK_COUNT)) + rt_sdhci_writew(host, 0, RT_SDHCI_BLOCK_COUNT); + rt_sdhci_writew(host, data->blks, RT_SDHCI_32BIT_BLK_CNT); + } + else + { + rt_sdhci_writew(host, data->blks, RT_SDHCI_BLOCK_COUNT); + } +} + +static void sdhci_set_transfer_irqs(struct rt_sdhci_host *host) +{ + rt_uint32_t pio_irqs = RT_SDHCI_INT_DATA_AVAIL | RT_SDHCI_INT_SPACE_AVAIL; + rt_uint32_t dma_irqs = RT_SDHCI_INT_DMA_END; + + if (host->flags & RT_SDHCI_REQ_USE_DMA) + host->ier = (host->ier & ~pio_irqs) | dma_irqs; + else + host->ier = (host->ier & ~dma_irqs) | pio_irqs; + + if (host->flags & (RT_SDHCI_AUTO_CMD23 | RT_SDHCI_AUTO_CMD12)) + host->ier |= RT_SDHCI_INT_AUTO_CMD_ERR; + else + host->ier &= ~RT_SDHCI_INT_AUTO_CMD_ERR; + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); +} + +static void sdhci_prepare_data(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd) +{ + struct rt_mmcsd_data *data = cmd->data; + + LOG_D(data->blksize * data->blks > 524288); + LOG_D(data->blksize > host->mmc->max_blk_size); + LOG_D(data->blks > 65535); + + host->data = data; + host->data_early = 0; + host->data->bytes_xfered = 0; + + if (host->flags & RT_SDHCI_USE_SDMA) + { + unsigned int length_mask, offset_mask; + + host->flags |= RT_SDHCI_REQ_USE_DMA; + + length_mask = 0; + offset_mask = 0; + if (host->quirks & RT_SDHCI_QUIRK_32BIT_DMA_SIZE) + length_mask = 3; + if (host->quirks & RT_SDHCI_QUIRK_32BIT_DMA_ADDR) + offset_mask = 3; + + if ((data->blks * data->blksize) & length_mask) + { + host->flags &= ~RT_SDHCI_REQ_USE_DMA; + } + else if ((rt_ubase_t)rt_kmem_v2p(data->buf) & offset_mask) + { + host->flags &= ~RT_SDHCI_REQ_USE_DMA; + } + } + + sdhci_config_dma(host); + + if (host->flags & RT_SDHCI_REQ_USE_DMA) + { + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize); + else + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize); + + sdhci_set_sdma_addr(host, sdhci_sdma_address(host)); + } + + if (!(host->flags & RT_SDHCI_REQ_USE_DMA)) + { + host->blocks = data->blks; + } + + sdhci_set_transfer_irqs(host); + + sdhci_set_block_info(host, data); +} + +static void sdhci_set_mrq_done(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq) +{ + int i; + + for (i = 0; i < RT_SDHCI_MAX_MRQS; i++) + { + if (host->mrqs_done[i] == mrq) + { + LOG_D(1); + return; + } + } + + for (i = 0; i < RT_SDHCI_MAX_MRQS; i++) + { + if (!host->mrqs_done[i]) + { + host->mrqs_done[i] = mrq; + break; + } + } + + LOG_D(i >= RT_SDHCI_MAX_MRQS); +} + +static inline rt_bool_t sdhci_defer_done(struct rt_sdhci_host *host, + struct rt_mmcsd_req *mrq) +{ + struct rt_mmcsd_data *data = mrq->data; + + return host->pending_reset || host->always_defer_done || ((host->flags & RT_SDHCI_REQ_USE_DMA) && data && data->host_cookie == COOKIE_MAPPED); +} + + +/********************************************************* */ +/* pio */ +/********************************************************* */ + +static void rt_sdhci_read_block_pio(struct rt_sdhci_host *host,void **buf) +{ + rt_uint32_t scratch; + size_t len; + + rt_uint32_t blksize = host->data->blksize; + while (blksize) + { + len = min(4U, blksize); + + scratch = rt_sdhci_readl(host, RT_SDHCI_BUFFER); + rt_memcpy(*buf, &scratch, len); + + *buf += len; + blksize -= len; + } +} + +static void rt_sdhci_write_block_pio(struct rt_sdhci_host *host,void **buf) +{ + size_t blksize, len; + rt_uint32_t scratch; + LOG_D("PIO writing\n"); + + blksize = host->data->blksize; + scratch = 0; + + while (blksize) + { + len = min(4U, blksize); + rt_memcpy(&scratch, *buf, len); + *buf += len; + blksize -= len; + rt_sdhci_writel(host, scratch, RT_SDHCI_BUFFER); + } +} + +static void sdhci_transfer_pio(struct rt_sdhci_host *host) +{ + rt_uint32_t mask; + + if (host->blocks == 0) + return; + + if (host->data->flags & DATA_DIR_READ) + mask = RT_SDHCI_DATA_AVAILABLE; + else + mask = RT_SDHCI_SPACE_AVAILABLE; + + if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_SMALL_PIO) && (host->data->blks == 1)) + { + mask = ~0; + } + void *buf = (void *)host->data->buf; + while (rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & mask) + { + if (host->quirks & RT_SDHCI_QUIRK_PIO_NEEDS_DELAY) + rt_hw_us_delay(100); + + if (host->data->flags & DATA_DIR_READ) + rt_sdhci_read_block_pio(host,&buf); + else + rt_sdhci_write_block_pio(host,&buf); + + host->data->blks--; + if (host->data->blks == 0) + break; + } +} + +/********************************************************* */ +/* config */ +/********************************************************* */ + + +static rt_bool_t sdhci_timing_has_preset(unsigned char timing) +{ + switch (timing) + { + case MMC_TIMING_UHS_SDR12: + case MMC_TIMING_UHS_SDR25: + case MMC_TIMING_UHS_SDR50: + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + return RT_TRUE; + } + return RT_FALSE; +} + +static rt_bool_t sdhci_preset_needed(struct rt_sdhci_host *host, unsigned char timing) +{ + return !(host->quirks2 & RT_SDHCI_QUIRK2_PRESET_VALUE_BROKEN) && sdhci_timing_has_preset(timing); +} + +static rt_bool_t sdhci_presetable_values_change(struct rt_sdhci_host *host, struct rt_mmcsd_io_cfg *ios) +{ + return !host->preset_enabled && (sdhci_preset_needed(host, ios->timing) || host->drv_type != ios->drv_type); +} + + +static void sdhci_preset_value_enable(struct rt_sdhci_host *host, rt_bool_t enable) +{ + if (host->version < RT_SDHCI_SPEC_300) + return; + + if (host->preset_enabled != enable) + { + rt_uint16_t ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + + if (enable) + ctrl |= RT_SDHCI_CTRL_PRESET_VAL_ENABLE; + else + ctrl &= ~RT_SDHCI_CTRL_PRESET_VAL_ENABLE; + + rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2); + + if (enable) + host->flags |= RT_SDHCI_PV_ENABLED; + else + host->flags &= ~RT_SDHCI_PV_ENABLED; + + host->preset_enabled = enable; + } +} + +static void sdhci_set_power_reg(struct rt_sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + struct rt_mmc_host *mmc = host->mmc; + + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + + if (mode != MMC_POWER_OFF) + rt_sdhci_writeb(host, RT_SDHCI_POWER_ON, RT_SDHCI_POWER_CONTROL); + else + rt_sdhci_writeb(host, 0, RT_SDHCI_POWER_CONTROL); +} + +void rt_sdhci_set_power_with_noreg(struct rt_sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + rt_uint8_t pwr = 0; + + if (mode != MMC_POWER_OFF) + { + switch (1 << vdd) + { + case MMC_VDD_165_195: + case MMC_VDD_20_21: + pwr = RT_SDHCI_POWER_180; + break; + case MMC_VDD_29_30: + case MMC_VDD_30_31: + pwr = RT_SDHCI_POWER_300; + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + case MMC_VDD_34_35: + case MMC_VDD_35_36: + pwr = RT_SDHCI_POWER_330; + break; + default: + break; + } + } + + if (host->pwr == pwr) + return; + + host->pwr = pwr; + + if (pwr == 0) + { + rt_sdhci_writeb(host, 0, RT_SDHCI_POWER_CONTROL); + if (host->quirks2 & RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_off(host); + } + else + { + if (!(host->quirks & RT_SDHCI_QUIRK_SINGLE_POWER_WRITE)) + rt_sdhci_writeb(host, 0, RT_SDHCI_POWER_CONTROL); + + if (host->quirks & RT_SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) + rt_sdhci_writeb(host, pwr, RT_SDHCI_POWER_CONTROL); + + pwr |= RT_SDHCI_POWER_ON; + + rt_sdhci_writeb(host, pwr, RT_SDHCI_POWER_CONTROL); + + if (host->quirks2 & RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_on(host); + + if (host->quirks & RT_SDHCI_QUIRK_DELAY_AFTER_POWER) + rt_thread_mdelay(10); + } +} + +void rt_sdhci_set_power(struct rt_sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + if (!host->mmc->supply.vmmc) + rt_sdhci_set_power_with_noreg(host, mode, vdd); + else + sdhci_set_power_reg(host, mode, vdd); +} + + +int rt_sdhci_start_signal_voltage_switch(struct rt_mmc_host *mmc, + struct rt_mmcsd_io_cfg *ios) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_uint16_t ctrl; + int ret; + + if (host->version < RT_SDHCI_SPEC_300) + return 0; + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + + switch (ios->signal_voltage) + { + case MMC_SIGNAL_VOLTAGE_330: + if (!(host->flags & RT_SDHCI_SIGNALING_330)) + return -EINVAL; + ctrl &= ~RT_SDHCI_CTRL_VDD_180; + rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2); + + if (!mmc->supply.vqmmc) + { + ret = rt_mmc_regulator_set_vqmmc(mmc, ios); + if (ret < 0) + { + return -EIO; + } + } + rt_thread_mdelay(5); + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + if (!(ctrl & RT_SDHCI_CTRL_VDD_180)) + return 0; + return -EAGAIN; + case MMC_SIGNAL_VOLTAGE_180: + if (!(host->flags & RT_SDHCI_SIGNALING_180)) + return -EINVAL; + if (!mmc->supply.vqmmc) + { + ret = rt_mmc_regulator_set_vqmmc(mmc, ios); + if (ret < 0) + { + LOG_D("%s: Switching to 1.8V signalling voltage failed\n", + mmc_hostname(mmc)); + return -EIO; + } + } + + ctrl |= RT_SDHCI_CTRL_VDD_180; + rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2); + + if (host->ops->voltage_switch) + host->ops->voltage_switch(host); + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + if (ctrl & RT_SDHCI_CTRL_VDD_180) + return 0; + + LOG_D("%s: 1.8V regulator output did not become stable\n", + mmc_hostname(mmc)); + + return -EAGAIN; + case MMC_SIGNAL_VOLTAGE_120: + if (!(host->flags & RT_SDHCI_SIGNALING_120)) + return -EINVAL; + if (!mmc->supply.vqmmc) + { + ret = rt_mmc_regulator_set_vqmmc(mmc, ios); + if (ret < 0) + { + LOG_D("%s: Switching to 1.2V signalling voltage failed\n", + mmc_hostname(mmc)); + return -EIO; + } + } + return 0; + default: + return 0; + } +} + + +static int sdhci_get_cd(struct rt_mmc_host *mmc) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + int gpio_cd = rt_mmc_gpio_get_cd(mmc); + + if (host->flags & RT_SDHCI_DEVICE_DEAD) + return 0; + + if (!mmc_card_is_removable(mmc)) + return 1; + + if (gpio_cd >= 0) + return !!gpio_cd; + + if (host->quirks & RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION) + return 1; + + return !!(rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & RT_SDHCI_CARD_PRESENT); +} + +static int sdhci_check_ro(struct rt_sdhci_host *host) +{ + int is_readonly; + rt_base_t flags; + flags = rt_spin_lock_irqsave(&host->lock); + + if (host->flags & RT_SDHCI_DEVICE_DEAD) + is_readonly = 0; + else if (host->ops->get_ro) + is_readonly = host->ops->get_ro(host); + else if (rt_mmc_can_gpio_ro(host->mmc)) + is_readonly = rt_mmc_gpio_get_ro(host->mmc); + else + is_readonly = !(rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) + & RT_SDHCI_WRITE_PROTECT); + + rt_spin_unlock_irqrestore(&host->lock, flags); + + return host->quirks & RT_SDHCI_QUIRK_INVERTED_WRITE_PROTECT ? !is_readonly : is_readonly; +} + +#define SAMPLE_COUNT 5 +static int rt_sdhci_ro_get(struct rt_mmc_host *mmc) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + int i, ro_count; + + if (!(host->quirks & RT_SDHCI_QUIRK_UNSTABLE_RO_DETECT)) + return sdhci_check_ro(host); + + ro_count = 0; + for (i = 0; i < SAMPLE_COUNT; i++) + { + if (sdhci_check_ro(host)) + { + if (++ro_count > SAMPLE_COUNT / 2) + return 1; + } + rt_thread_mdelay(30); + } + return 0; +} + +static void rt_sdhci_enable_io_irq_nolock(struct rt_sdhci_host *host, int enable) +{ + if (!(host->flags & RT_SDHCI_DEVICE_DEAD)) + { + if (enable) + host->ier |= RT_SDHCI_INT_CARD_INT; + else + host->ier &= ~RT_SDHCI_INT_CARD_INT; + + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); + } +} + +static void sdhci_ack_sdio_irq(struct rt_mmc_host *mmc) +{ + rt_base_t flags; + struct rt_sdhci_host *host = mmc_priv(mmc); + flags = rt_spin_lock_irqsave(&host->lock); + rt_sdhci_enable_io_irq_nolock(host, RT_TRUE); + rt_spin_unlock_irqrestore(&host->lock, flags); +} + +static void sdhci_del_timer(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq) +{ + if (sdhci_data_line_cmd(mrq->cmd)) + rt_timer_stop(&host->data_timer); + else + rt_timer_stop(&host->timer); +} + +static unsigned int sdhci_target_timeout(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd, + struct rt_mmcsd_data *data) +{ + unsigned int target_timeout; + + if (!data) + { + target_timeout = cmd->busy_timeout * 1000; + } + else + { + target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000); + if (host->clock && data->timeout_clks) + { + rt_uint32_t val; + + val = 1000000ULL * data->timeout_clks; + if (do_div(val, host->clock)) + target_timeout++; + target_timeout += val; + } + } + + return target_timeout; +} + +static rt_uint8_t sdhci_calc_timeout(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd, + rt_bool_t *too_big) +{ + rt_uint8_t count; + struct rt_mmcsd_data *data; + unsigned target_timeout, current_timeout; + + *too_big = RT_FALSE; + + if (host->quirks & RT_SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) + return host->max_timeout_count; + + if (cmd == NULL) + return host->max_timeout_count; + + data = cmd->data; + if (!data && !cmd->busy_timeout) + return host->max_timeout_count; + + target_timeout = sdhci_target_timeout(host, cmd, data); + + count = 0; + current_timeout = (1 << 13) * 1000 / host->timeout_clk; + while (current_timeout < target_timeout) + { + count++; + current_timeout <<= 1; + if (count > host->max_timeout_count) + { + if (!(host->quirks2 & RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT)) + LOG_D("Too large timeout 0x%x requested for CMD%d!\n", + count, cmd->cmd_code); + count = host->max_timeout_count; + *too_big = RT_TRUE; + break; + } + } + + return count; +} + +static void sdhci_calc_sw_timeout(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd) +{ + struct rt_mmcsd_data *data = cmd->data; + struct rt_mmc_host *mmc = host->mmc; + struct rt_mmcsd_io_cfg *ios = &mmc->ios; + unsigned char bus_width = 1 << ios->bus_width; + unsigned int blksz; + unsigned int freq; + rt_uint64_t target_timeout; + rt_uint64_t transfer_time; + + target_timeout = sdhci_target_timeout(host, cmd, data); + target_timeout *= 1000L; + + if (data) + { + blksz = data->blksize; + freq = mmc->actual_clock ?: host->clock; + transfer_time = (rt_uint64_t)blksz * 1000000000L * (8 / bus_width); + do_div(transfer_time, freq); + transfer_time = transfer_time * 2; + host->data_timeout = data->blks * target_timeout + transfer_time; + } + else + { + host->data_timeout = target_timeout; + } + + if (host->data_timeout) + host->data_timeout += MMC_CMD_TRANSFER_TIME; +} + + +void rt_sdhci_timeout_set(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd) +{ + rt_bool_t too_big = RT_FALSE; + rt_uint8_t count = sdhci_calc_timeout(host, cmd, &too_big); + + if (too_big && host->quirks2 & RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) + { + sdhci_calc_sw_timeout(host, cmd); + rt_sdhci_data_irq_timeout(host, RT_FALSE); + } + else if (!(host->ier & RT_SDHCI_INT_DATA_TIMEOUT)) + { + rt_sdhci_data_irq_timeout(host, RT_FALSE); + } + + rt_sdhci_writeb(host, count, RT_SDHCI_TIMEOUT_CONTROL); +} + +static void sdhci_set_timeout(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd) +{ + if (host->ops->set_timeout) + host->ops->set_timeout(host, cmd); + else + rt_sdhci_timeout_set(host, cmd); +} + +static void sdhci_start_timer(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq, + unsigned long timeout) +{ + if (sdhci_data_line_cmd(mrq->cmd)) + { + rt_tick_t tick = rt_tick_get(); + + if (timeout < tick) + { + timeout = tick; + } + tick = timeout - tick; + + rt_timer_stop(&host->data_timer); + rt_timer_control(&host->data_timer, RT_TIMER_CTRL_SET_TIME, &tick); + rt_timer_start(&host->data_timer); + } + else + { + rt_tick_t tick = rt_tick_get(); + + if (timeout < tick) + { + timeout = tick; + } + tick = timeout - tick; + + rt_timer_stop(&host->timer); + rt_timer_control(&host->timer, RT_TIMER_CTRL_SET_TIME, &tick); + rt_timer_start(&host->timer); + } +} + +static void __sdhci_finish_mrq(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq) +{ + if (host->cmd && host->cmd->mrq == mrq) + host->cmd = NULL; + + if (host->data_cmd && host->data_cmd->mrq == mrq) + host->data_cmd = NULL; + + if (host->deferred_cmd && host->deferred_cmd->mrq == mrq) + host->deferred_cmd = NULL; + + if (host->data && host->data->mrq == mrq) + host->data = NULL; + + if (sdhci_needs_reset(host, mrq)) + host->pending_reset = RT_TRUE; + + sdhci_set_mrq_done(host, mrq); + + sdhci_del_timer(host, mrq); +} + +static void sdhci_finish_mrq(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq) +{ + __sdhci_finish_mrq(host, mrq); + + rt_workqueue_submit_work(host->complete_wq, &host->complete_work, 0); +} + +static void sdhci_error_out_mrqs(struct rt_sdhci_host *host, int err) +{ + if (host->data_cmd) + { + host->data_cmd->err = err; + sdhci_finish_mrq(host, host->data_cmd->mrq); + } + + if (host->cmd) + { + host->cmd->err = err; + sdhci_finish_mrq(host, host->cmd->mrq); + } +} + +static void sdhci_card_event(struct rt_mmc_host *mmc) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_uint32_t flags; + int present; + + if (host->ops->card_event) + host->ops->card_event(host); + + present = mmc->ops->get_cd(mmc); + + flags = rt_spin_lock_irqsave(&host->lock); + + if (sdhci_has_requests(host) && !present) + { + rt_kprintf("%s: Card removed during transfer!\n", + mmc_hostname(mmc)); + rt_kprintf("%s: Resetting controller.\n", + mmc_hostname(mmc)); + + sdhci_do_reset(host, RT_SDHCI_RESET_CMD); + sdhci_do_reset(host, RT_SDHCI_RESET_DATA); + sdhci_error_out_mrqs(host, -ENOMEDIUM); + } + + rt_spin_unlock_irqrestore(&host->lock, flags); +} + +static int sdhci_card_busy(struct rt_mmc_host *mmc) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_uint32_t present_state; + + present_state = rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE); + + return !(present_state & RT_SDHCI_DATA_0_LVL_MASK); +} + + +static int sdhci_prepare_hs400_tuning(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_uint32_t flags; + + flags = rt_spin_lock_irqsave(&host->lock); + host->flags |= RT_SDHCI_HS400_TUNING; + rt_spin_unlock_irqrestore(&host->lock, flags); + + return 0; +} + + +static void sdhci_set_transfer_mode(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd) +{ + rt_uint16_t mode = 0; + struct rt_mmcsd_data *data = cmd->data; + + if (data == NULL) + { + if (host->quirks2 & RT_SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) + { + if (!mmc_op_tuning(cmd->cmd_code)) + rt_sdhci_writew(host, 0x0, RT_SDHCI_TRANSFER_MODE); + } + else + { + mode = rt_sdhci_readw(host, RT_SDHCI_TRANSFER_MODE); + rt_sdhci_writew(host, mode & ~(RT_SDHCI_TRNS_AUTO_CMD12 | RT_SDHCI_TRNS_AUTO_CMD23), RT_SDHCI_TRANSFER_MODE); + } + return; + } + + if (!(host->quirks2 & RT_SDHCI_QUIRK2_SUPPORT_SINGLE)) + mode = RT_SDHCI_TRNS_BLK_CNT_EN; + + if (mmc_op_multi(cmd->cmd_code) || data->blks > 1) + { + mode = RT_SDHCI_TRNS_BLK_CNT_EN | RT_SDHCI_TRNS_MULTI; + sdhci_auto_cmd_select(host, cmd, &mode); + if (sdhci_auto_cmd23(host, cmd->mrq)) + rt_sdhci_writel(host, cmd->mrq->sbc->arg, RT_SDHCI_ARGUMENT2); + } + + if (data->flags & DATA_DIR_READ) + mode |= RT_SDHCI_TRNS_READ; + if (host->flags & RT_SDHCI_REQ_USE_DMA) + mode |= RT_SDHCI_TRNS_DMA; + + rt_sdhci_writew(host, mode, RT_SDHCI_TRANSFER_MODE); +} + +static rt_bool_t sdhci_send_command(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd) +{ + int flags; + rt_uint32_t mask; + unsigned long timeout; + cmd->err = 0; + + if ((host->quirks2 & RT_SDHCI_QUIRK2_STOP_WITH_TC) && cmd->cmd_code == MMC_STOP_TRANSMISSION) + cmd->flags |= MMC_RSP_BUSY; + + mask = RT_SDHCI_CMD_INHIBIT; + if (sdhci_data_line_cmd(cmd)) + mask |= RT_SDHCI_DATA_INHIBIT; + + if (cmd->mrq->data && (cmd == cmd->mrq->data->stop)) + mask &= ~RT_SDHCI_DATA_INHIBIT; + + if (rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & mask) + return RT_FALSE; + + host->cmd = cmd; + host->data_timeout = 0; + if (sdhci_data_line_cmd(cmd)) + { + host->data_cmd = cmd; + sdhci_set_timeout(host, cmd); + } + + if (cmd->data) + { + sdhci_prepare_data(host, cmd); + } + rt_sdhci_writel(host, cmd->arg, RT_SDHCI_ARGUMENT); + + sdhci_set_transfer_mode(host, cmd); + + if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) + { + cmd->flags &= ~MMC_RSP_BUSY; + } + + if (!(cmd->flags & MMC_RSP_PRESENT)) + flags = RT_SDHCI_CMD_RESP_NONE; + else if (cmd->flags & MMC_RSP_136) + flags = RT_SDHCI_CMD_RESP_LONG; + else if (cmd->flags & MMC_RSP_BUSY) + flags = RT_SDHCI_CMD_RESP_SHORT_BUSY; + else + flags = RT_SDHCI_CMD_RESP_SHORT; + + if (cmd->flags & MMC_RSP_CRC) + flags |= RT_SDHCI_CMD_CRC; + if (cmd->flags & MMC_RSP_OPCODE) + flags |= RT_SDHCI_CMD_INDEX; + + if (cmd->data || mmc_op_tuning(cmd->cmd_code)) + flags |= RT_SDHCI_CMD_DATA; + + timeout = rt_tick_get(); + if (host->data_timeout) + timeout += rt_tick_from_millisecond(host->data_timeout * 1000); + else if (!cmd->data && cmd->busy_timeout > 9000) + timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * RT_TICK_PER_SECOND + RT_TICK_PER_SECOND; + else + timeout += 10 * RT_TICK_PER_SECOND; + sdhci_start_timer(host, cmd->mrq, timeout); + + rt_sdhci_writew(host, RT_SDHCI_MAKE_CMD(cmd->cmd_code, flags), RT_SDHCI_COMMAND); + return RT_TRUE; +} + +/********************************************************* */ +/* dma */ +/********************************************************* */ +static void __sdhci_finish_data(struct rt_sdhci_host *host, rt_bool_t sw_data_timeout) +{ + struct rt_mmcsd_cmd *data_cmd = host->data_cmd; + struct rt_mmcsd_data *data = host->data; + + host->data = NULL; + host->data_cmd = NULL; + + if (data->err) + { + if (!host->cmd || host->cmd == data_cmd) + sdhci_reset_for(host, REQUEST_ERROR); + else + sdhci_reset_for(host, REQUEST_ERROR_DATA_ONLY); + } + + if (data->err) + { + data->bytes_xfered = 0; + } + else + { + data->bytes_xfered = data->blksize * data->blks; + } + + if (data->stop && ((!data->mrq->sbc && !sdhci_auto_cmd12(host, data->mrq)) || data->err)) + { + if (data->mrq->cap_cmd_during_tfr) + { + __sdhci_finish_mrq(host, data->mrq); + } + else + { + host->cmd = NULL; + if (!sdhci_send_command(host, data->stop)) + { + if (sw_data_timeout) + { + data->stop->err = -EIO; + __sdhci_finish_mrq(host, data->mrq); + } + else + { + host->deferred_cmd = data->stop; + } + } + } + } + else + { + __sdhci_finish_mrq(host, data->mrq); + } +} + +static void sdhci_finish_data(struct rt_sdhci_host *host) +{ + __sdhci_finish_data(host, RT_FALSE); +} + + +/********************************************************* */ +/* irq */ +/********************************************************* */ +static void sdhci_data_irq(struct rt_sdhci_host *host, rt_uint32_t intmask) +{ + rt_uint32_t command; + + if (intmask & RT_SDHCI_INT_DATA_AVAIL && !host->data) + { + command = RT_SDHCI_GET_CMD(rt_sdhci_readw(host, RT_SDHCI_COMMAND)); + if (command == MMC_SEND_TUNING_BLOCK || command == MMC_SEND_TUNING_BLOCK_HS200) + { + host->tuning_done = 1; + rt_wqueue_wakeup(&host->buf_ready_int, 0); + return; + } + } + + if (!host->data) + { + struct rt_mmcsd_cmd *data_cmd = host->data_cmd; + + if (data_cmd && (data_cmd->flags & MMC_RSP_BUSY)) + { + if (intmask & RT_SDHCI_INT_DATA_TIMEOUT) + { + host->data_cmd = NULL; + data_cmd->err = -ETIMEDOUT; + __sdhci_finish_mrq(host, data_cmd->mrq); + return; + } + if (intmask & RT_SDHCI_INT_DATA_END) + { + host->data_cmd = NULL; + + if (host->cmd == data_cmd) + return; + + __sdhci_finish_mrq(host, data_cmd->mrq); + return; + } + } + + + if (host->pending_reset) + return; + rt_kprintf("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n", + mmc_hostname(host->mmc), (unsigned)intmask); + rt_read_reg_debug(host); + + return; + } + + if (intmask & RT_SDHCI_INT_DATA_TIMEOUT) + host->data->err = -ETIMEDOUT; + else if (intmask & RT_SDHCI_INT_DATA_END_BIT) + host->data->err = -EILSEQ; + else if ((intmask & RT_SDHCI_INT_DATA_CRC) && RT_SDHCI_GET_CMD(rt_sdhci_readw(host, RT_SDHCI_COMMAND)) != MMC_BUS_TEST_R) + { + host->data->err = -EILSEQ; + } + + + if (host->data->err) + { + sdhci_finish_data(host); + } + else + { + if (intmask & (RT_SDHCI_INT_DATA_AVAIL | RT_SDHCI_INT_SPACE_AVAIL)) + sdhci_transfer_pio(host); + + if (intmask & RT_SDHCI_INT_DMA_END) + { + rt_uint32_t dmastart, dmanow; + + dmastart = sdhci_sdma_address(host); + dmanow = dmastart + host->data->bytes_xfered; + dmanow = (dmanow & ~((rt_uint32_t)RT_SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + RT_SDHCI_DEFAULT_BOUNDARY_SIZE; + host->data->bytes_xfered = dmanow - dmastart; + LOG_D("DMA base %pad, transferred 0x%06x bytes, next %pad\n", + &dmastart, host->data->bytes_xfered, &dmanow); + sdhci_set_sdma_addr(host, dmanow); + } + + if (intmask & RT_SDHCI_INT_DATA_END) + { + struct rt_mmcsd_data *data = host->data; + if (data->buf) + { + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize); + } else { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize); + } + } + if (host->cmd == host->data_cmd) + { + host->data_early = 1; + } + else + { + sdhci_finish_data(host); + } + } + } +} + +static void rt_sdhci_read_rsp_136(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd) +{ + int i, reg; + + for (i = 0; i < 4; i++) + { + reg = RT_SDHCI_RESPONSE + (3 - i) * 4; + cmd->resp[i] = rt_sdhci_readl(host, reg); + } + + if (host->quirks2 & RT_SDHCI_QUIRK2_RSP_136_HAS_CRC) + return; + + for (i = 0; i < 4; i++) + { + cmd->resp[i] <<= 8; + if (i != 3) + cmd->resp[i] |= cmd->resp[i + 1] >> 24; + } +} + +static void sdhci_command_end(struct rt_sdhci_host *host) +{ + struct rt_mmcsd_cmd *cmd = host->cmd; + + host->cmd = NULL; + + if (cmd->flags & MMC_RSP_PRESENT) + { + if (cmd->flags & MMC_RSP_136) + { + rt_sdhci_read_rsp_136(host, cmd); + } + else + { + cmd->resp[0] = rt_sdhci_readl(host, RT_SDHCI_RESPONSE); + } + } + + if (cmd->flags & MMC_RSP_BUSY) + { + if (cmd->data) + { + LOG_D("Cannot wait for busy signal when also doing a data transfer"); + } + else if (!(host->quirks & RT_SDHCI_QUIRK_NO_BUSY_IRQ) && cmd == host->data_cmd) + { + return; + } + } + + if (cmd == cmd->mrq->sbc) + { + if (!sdhci_send_command(host, cmd->mrq->cmd)) + { + host->deferred_cmd = cmd->mrq->cmd; + } + } + else + { + if (host->data && host->data_early) + sdhci_finish_data(host); + + if (!cmd->data) + __sdhci_finish_mrq(host, cmd->mrq); + } +} + + +static void sdhci_cmd_irq(struct rt_sdhci_host *host, rt_uint32_t intmask, rt_uint32_t *intmask_p) +{ + if (intmask & RT_SDHCI_INT_AUTO_CMD_ERR && host->data_cmd) + { + struct rt_mmcsd_req *mrq = host->data_cmd->mrq; + rt_uint16_t auto_cmd_status = rt_sdhci_readw(host, RT_SDHCI_AUTO_CMD_STATUS); + int data_err_bit = (auto_cmd_status & RT_SDHCI_AUTO_CMD_TIMEOUT) ? RT_SDHCI_INT_DATA_TIMEOUT : RT_SDHCI_INT_DATA_CRC; + + if (!mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD12)) + { + *intmask_p |= data_err_bit; + return; + } + } + + if (!host->cmd) + { + if (host->pending_reset) + return; + rt_kprintf("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n", + mmc_hostname(host->mmc), (unsigned)intmask); + rt_read_reg_debug(host); + return; + } + if (intmask & (RT_SDHCI_INT_TIMEOUT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_INDEX)) + { + if (intmask & RT_SDHCI_INT_TIMEOUT) + host->cmd->err = -ETIMEDOUT; + else + host->cmd->err = -EILSEQ; + + /* Treat data command CRC error the same as data CRC error */ + if (host->cmd->data && (intmask & (RT_SDHCI_INT_CRC | RT_SDHCI_INT_TIMEOUT)) == RT_SDHCI_INT_CRC) + { + host->cmd = NULL; + *intmask_p |= RT_SDHCI_INT_DATA_CRC; + return; + } + + __sdhci_finish_mrq(host, host->cmd->mrq); + return; + } + + if (intmask & RT_SDHCI_INT_AUTO_CMD_ERR) + { + struct rt_mmcsd_req *mrq = host->cmd->mrq; + rt_uint16_t auto_cmd_status = rt_sdhci_readw(host, RT_SDHCI_AUTO_CMD_STATUS); + int err = (auto_cmd_status & RT_SDHCI_AUTO_CMD_TIMEOUT) ? -ETIMEDOUT : -EILSEQ; + + if (mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD23)) + { + mrq->sbc->err = err; + __sdhci_finish_mrq(host, mrq); + return; + } + } + + if (intmask & RT_SDHCI_INT_RESPONSE) + sdhci_command_end(host); +} + +static void sdhci_irq(int irq, void *dev_id) +{ +#define IRQ_NONE 0 +#define IRQ_WAIT 1 +#define IRQ_DONE 2 + + struct rt_mmcsd_req* mrqs_done[RT_SDHCI_MAX_MRQS] = { 0 }; + struct rt_sdhci_host *host = dev_id; + rt_uint32_t intmask, mask, unexpected = 0; + int max_loops = 16; + int i, result= IRQ_NONE ; + rt_spin_lock(&host->lock); + + if (host->runtime_suspended) + { + rt_spin_unlock(&host->lock); + return; + } + + intmask = rt_sdhci_readl(host, RT_SDHCI_INT_STATUS); + if (!intmask || intmask == 0xffffffff) + { + result = IRQ_NONE; + goto out; + } + + do { + LOG_D("IRQ status 0x%08x\n", intmask); + + if (host->ops->irq) + { + intmask = host->ops->irq(host, intmask); + if (!intmask) + goto cont; + } + + /* Clear selected interrupts. */ + mask = intmask & (RT_SDHCI_INT_CMD_MASK | RT_SDHCI_INT_DATA_MASK | RT_SDHCI_INT_BUS_POWER); + rt_sdhci_writel(host, mask, RT_SDHCI_INT_STATUS); + + if (intmask & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE)) + { + rt_uint32_t present = rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & RT_SDHCI_CARD_PRESENT; + + host->ier &= ~(RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE); + host->ier |= present ? RT_SDHCI_INT_CARD_REMOVE : RT_SDHCI_INT_CARD_INSERT; + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); + + rt_sdhci_writel(host, intmask & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE), RT_SDHCI_INT_STATUS); + + host->thread_isr |= intmask & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE); + result = IRQ_WAIT; + } + + if (intmask & RT_SDHCI_INT_CMD_MASK) + sdhci_cmd_irq(host, intmask & RT_SDHCI_INT_CMD_MASK, &intmask); + + if (intmask & RT_SDHCI_INT_DATA_MASK) + sdhci_data_irq(host, intmask & RT_SDHCI_INT_DATA_MASK); + + if (intmask & RT_SDHCI_INT_BUS_POWER) + rt_kprintf("%s: Card is consuming too much power!\n", + mmc_hostname(host->mmc)); + + intmask &= ~(RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CMD_MASK | RT_SDHCI_INT_DATA_MASK | RT_SDHCI_INT_ERROR | RT_SDHCI_INT_BUS_POWER | RT_SDHCI_INT_RETUNE | RT_SDHCI_INT_CARD_INT); + + if (intmask) + { + unexpected |= intmask; + rt_sdhci_writel(host, intmask, RT_SDHCI_INT_STATUS); + } + cont: + if (result == IRQ_NONE) + result = IRQ_WAIT; + intmask = rt_sdhci_readl(host, RT_SDHCI_INT_STATUS); + } while (intmask && --max_loops); + + for (i = 0; i < RT_SDHCI_MAX_MRQS; i++) + { + struct rt_mmcsd_req *mrq = host->mrqs_done[i]; + + if (!mrq) + continue; + + if (sdhci_defer_done(host, mrq)) + { + result = IRQ_WAIT; + } + else + { + mrqs_done[i] = mrq; + host->mrqs_done[i] = NULL; + } + } +out: + if (host->deferred_cmd) + result = IRQ_WAIT; + + rt_spin_unlock(&host->lock); + + for (i = 0; i < RT_SDHCI_MAX_MRQS; i++) + { + if (!mrqs_done[i]) + continue; + + if (host->ops->request_done) + host->ops->request_done(host, mrqs_done[i]); + else + rt_mmc_request_done(host->mmc, mrqs_done[i]); + } + + if (unexpected) + { + rt_kprintf("%s: Unexpected interrupt 0x%08x.\n", + mmc_hostname(host->mmc), unexpected); + rt_read_reg_debug(host); + } + + if (result == IRQ_WAIT) + { + rt_workqueue_submit_work(host->irq_wq, &host->irq_work, 0); + } +} + +static rt_bool_t sdhci_send_command_retry(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd, + unsigned long flags) +{ + struct rt_mmcsd_cmd *deferred_cmd = host->deferred_cmd; + int timeout = 10; /* Approx. 10 ms */ + rt_bool_t present; + while (!sdhci_send_command(host, cmd)) + { + if (!timeout--) + { + rt_kprintf("%s: Controller never released inhibit bit(s).\n", + mmc_hostname(host->mmc)); + rt_read_reg_debug(host); + cmd->err = -EIO; + return RT_FALSE; + } + + rt_spin_unlock_irqrestore(&host->lock, flags); + + rt_thread_mdelay(1); + + present = host->mmc->ops->get_cd(host->mmc); + + flags = rt_spin_lock_irqsave(&host->lock); + + if (cmd == deferred_cmd && cmd != host->deferred_cmd) + return RT_TRUE; + + if (sdhci_present_error(host, cmd, present)) + return RT_FALSE; + } + + if (cmd == host->deferred_cmd) + host->deferred_cmd = NULL; + + return RT_TRUE; +} + +static rt_bool_t rt_sdhci_start_request_done(struct rt_sdhci_host *host) +{ + rt_base_t flags; + struct rt_mmcsd_req *mrq; + int i; + + flags = rt_spin_lock_irqsave(&host->lock); + + for (i = 0; i < RT_SDHCI_MAX_MRQS; i++) + { + mrq = host->mrqs_done[i]; + if (mrq) + break; + } + + if (!mrq) + { + rt_spin_unlock_irqrestore(&host->lock, flags); + return RT_TRUE; + } + + if (sdhci_needs_reset(host, mrq)) + { + if (host->cmd || host->data_cmd) + { + rt_spin_unlock_irqrestore(&host->lock, flags); + return RT_TRUE; + } + + /* Some controllers need this kick or reset won't work here */ + if (host->quirks & RT_SDHCI_QUIRK_CLOCK_BEFORE_RESET) + /* This is to force an update */ + host->ops->set_clock(host, host->clock); + + sdhci_reset_for(host, REQUEST_ERROR); + + host->pending_reset = RT_FALSE; + } + + if (host->flags & RT_SDHCI_REQ_USE_DMA) + { + struct rt_mmcsd_data *data = mrq->data; + + if (data && data->host_cookie == COOKIE_MAPPED) + { + if (host->bounce_buffer) + { + /* + * On reads, copy the bounced data into the + * sglist + */ + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) + { + unsigned int length = data->bytes_xfered; + + if (length > host->bounce_buffer_size) + { + LOG_E("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n", + mmc_hostname(host->mmc), + host->bounce_buffer_size, + data->bytes_xfered); + /* Cap it down and continue */ + length = host->bounce_buffer_size; + } + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize); + } else { + /* No copying, just switch ownership */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize); + } + } + data->host_cookie = COOKIE_UNMAPPED; + } + else { + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize); + } else { + /* No copying, just switch ownership */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize); + } + } + } + + host->mrqs_done[i] = NULL; + + rt_spin_unlock_irqrestore(&host->lock, flags); + + if (host->ops->request_done) + host->ops->request_done(host, mrq); + else + rt_mmc_request_done(host->mmc, mrq); + + return RT_FALSE; +} + + +static void sdhci_thread_irq(struct rt_work *work, void *work_data) +{ + struct rt_sdhci_host* host = work_data; + struct rt_mmcsd_cmd *cmd; + rt_base_t flags; + rt_uint32_t isr; + + while (!rt_sdhci_start_request_done(host)); + + flags = rt_spin_lock_irqsave(&host->lock); + + isr = host->thread_isr; + host->thread_isr = 0; + + cmd = host->deferred_cmd; + if (cmd && !sdhci_send_command_retry(host, cmd, flags)) + sdhci_finish_mrq(host, cmd->mrq); + + rt_spin_unlock_irqrestore(&host->lock, flags); + + if (isr & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE)) + { + struct rt_mmc_host *mmc = host->mmc; + + mmc->ops->card_event(mmc); + } +} + + +void rt_sdhci_enable_io_irq(struct rt_mmc_host *mmc, int enable) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_uint32_t flags; + + flags = rt_spin_lock_irqsave(&host->lock); + rt_sdhci_enable_io_irq_nolock(host, enable); + rt_spin_unlock_irqrestore(&host->lock, flags); +} + + +/********************************************************* */ +/* request */ +/********************************************************* */ + +void rt_sdhci_start_request(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + struct rt_mmcsd_cmd *cmd; + rt_base_t flags; + rt_bool_t present; + + /* Firstly check card presence */ + present = mmc->ops->get_cd(mmc); + + flags = rt_spin_lock_irqsave(&host->lock); + + if (sdhci_present_error(host, mrq->cmd, present)) + goto out_finish; + + cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd; + + if (!sdhci_send_command_retry(host, cmd, flags)) + goto out_finish; + + rt_spin_unlock_irqrestore(&host->lock, flags); + + return; + +out_finish: + sdhci_finish_mrq(host, mrq); + rt_spin_unlock_irqrestore(&host->lock, flags); +} + + +static void sdhci_complete_work(struct rt_work *work, void *work_data) +{ + struct rt_sdhci_host *host = work_data; + + while (!rt_sdhci_start_request_done(host)); +} + + +/********************************************************* */ +/* timer */ +/********************************************************* */ +static void sdhci_timeout_timer(void *parameter) +{ + struct rt_sdhci_host *host = parameter; + rt_base_t flags; + + flags = rt_spin_lock_irqsave(&host->lock); + + if (host->cmd && !sdhci_data_line_cmd(host->cmd)) + { + rt_kprintf("%s: Timeout waiting for hardware cmd interrupt.\n", + mmc_hostname(host->mmc)); + rt_read_reg_debug(host); + + host->cmd->err = -ETIMEDOUT; + sdhci_finish_mrq(host, host->cmd->mrq); + } + + rt_spin_unlock_irqrestore(&host->lock, flags); +} + +static void sdhci_timeout_data_timer(void *parameter) +{ + struct rt_sdhci_host *host = parameter; + rt_base_t flags; + + flags = rt_spin_lock_irqsave(&host->lock); + + if (host->data || host->data_cmd || (host->cmd && sdhci_data_line_cmd(host->cmd))) + { + rt_kprintf("%s: Timeout waiting for hardware interrupt.\n", + mmc_hostname(host->mmc)); + rt_read_reg_debug(host); + + if (host->data) + { + host->data->err = -ETIMEDOUT; + __sdhci_finish_data(host, RT_TRUE); + rt_workqueue_submit_work(host->complete_wq, &host->complete_work, 0); + } + else if (host->data_cmd) + { + host->data_cmd->err = -ETIMEDOUT; + sdhci_finish_mrq(host, host->data_cmd->mrq); + } + else + { + host->cmd->err = -ETIMEDOUT; + sdhci_finish_mrq(host, host->cmd->mrq); + } + } + + rt_spin_unlock_irqrestore(&host->lock, flags); +} + + +/********************************************************* */ +/* tuning */ +/********************************************************* */ +int rt_sdhci_execute_tuning(struct rt_mmc_host *mmc, rt_uint32_t opcode) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + int err = 0; + unsigned int tuning_count = 0; + rt_bool_t hs400_tuning; + + hs400_tuning = host->flags & RT_SDHCI_HS400_TUNING; + + if (host->tuning_mode == RT_SDHCI_TUNING_MODE_1) + tuning_count = host->tuning_count; + + switch (host->timing) + { + /* HS400 tuning is done in HS200 mode */ + case MMC_TIMING_MMC_HS400: + err = -EINVAL; + goto out; + + case MMC_TIMING_MMC_HS200: + if (hs400_tuning) + tuning_count = 0; + break; + + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_DDR50: + break; + + case MMC_TIMING_UHS_SDR50: + if (host->flags & RT_SDHCI_SDR50_NEEDS_TUNING) + break; + fallthrough; + + default: + goto out; + } + + if (host->ops->platform_execute_tuning) + { + err = host->ops->platform_execute_tuning(host, opcode); + goto out; + } + + mmc->retune_period = tuning_count; + + if (host->tuning_delay < 0) + host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK; + + rt_sdhci_start_tuning(host); + + host->tuning_err = __sdhci_execute_tuning(host, opcode); + + rt_sdhci_end_tuning(host); +out: + host->flags &= ~RT_SDHCI_HS400_TUNING; + + return err; +} + +int __sdhci_execute_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode) +{ + int i; + + for (i = 0; i < host->tuning_loop_count; i++) + { + rt_uint16_t ctrl; + + rt_sdhci_send_tuning(host, opcode); + + if (!host->tuning_done) + { + rt_sdhci_abort_tuning(host, opcode); + return -ETIMEDOUT; + } + + if (host->tuning_delay > 0) + rt_thread_mdelay(host->tuning_delay); + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + if (!(ctrl & RT_SDHCI_CTRL_EXEC_TUNING)) + { + if (ctrl & RT_SDHCI_CTRL_TUNED_CLK) + return 0; /* Success! */ + break; + } + } + + LOG_D("%s: Tuning failed, falling back to fixed sampling clock\n", + mmc_hostname(host->mmc)); + rt_sdhci_reset_tuning(host); + return -EAGAIN; +} + +void rt_sdhci_start_tuning(struct rt_sdhci_host *host) +{ + rt_uint16_t ctrl; + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + ctrl |= RT_SDHCI_CTRL_EXEC_TUNING; + if (host->quirks2 & RT_SDHCI_QUIRK2_TUNING_WORK_AROUND) + ctrl |= RT_SDHCI_CTRL_TUNED_CLK; + rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2); + + rt_sdhci_writel(host, RT_SDHCI_INT_DATA_AVAIL, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, RT_SDHCI_INT_DATA_AVAIL, RT_SDHCI_SIGNAL_ENABLE); +} + +void rt_sdhci_end_tuning(struct rt_sdhci_host *host) +{ + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); +} + +void rt_sdhci_abort_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode) +{ + rt_sdhci_reset_tuning(host); + + sdhci_reset_for(host, TUNING_ABORT); + + rt_sdhci_end_tuning(host); +} + +void rt_sdhci_send_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode) +{ + struct rt_mmc_host *mmc = host->mmc; + struct rt_mmcsd_cmd cmd = {}; + struct rt_mmcsd_req mrq = {}; + unsigned long flags; + rt_uint32_t b = host->sdma_boundary; + + flags = rt_spin_lock_irqsave(&host->lock); + + cmd.cmd_code = opcode; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.mrq = &mrq; + + mrq.cmd = &cmd; + + if (cmd.cmd_code == MMC_SEND_TUNING_BLOCK_HS200 && mmc->ios.bus_width == MMC_BUS_WIDTH_8) + rt_sdhci_writew(host, RT_SDHCI_MAKE_BLKSZ(b, 128), RT_SDHCI_BLOCK_SIZE); + else + rt_sdhci_writew(host, RT_SDHCI_MAKE_BLKSZ(b, 64), RT_SDHCI_BLOCK_SIZE); + + rt_sdhci_writew(host, RT_SDHCI_TRNS_READ, RT_SDHCI_TRANSFER_MODE); + + if (!sdhci_send_command_retry(host, &cmd, flags)) + { + rt_spin_unlock_irqrestore(&host->lock, flags); + host->tuning_done = 0; + return; + } + + host->cmd = NULL; + + sdhci_del_timer(host, &mrq); + + host->tuning_done = 0; + + rt_spin_unlock_irqrestore(&host->lock, flags); +} + +void rt_sdhci_reset_tuning(struct rt_sdhci_host *host) +{ + rt_uint16_t ctrl; + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + ctrl &= ~RT_SDHCI_CTRL_TUNED_CLK; + ctrl &= ~RT_SDHCI_CTRL_EXEC_TUNING; + rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2); +} + + +/********************************************************* */ +/* error */ +/********************************************************* */ +static const struct mmc_host_ops rt_sdhci_ops = { + .request = rt_sdhci_start_request, + .set_ios = rt_sdhci_ios_set, + .get_cd = sdhci_get_cd, + .get_ro = rt_sdhci_ro_get, + .enable_sdio_irq = rt_sdhci_enable_io_irq, + .ack_sdio_irq = sdhci_ack_sdio_irq, + .start_signal_voltage_switch = rt_sdhci_start_signal_voltage_switch, + .prepare_hs400_tuning = sdhci_prepare_hs400_tuning, + .execute_tuning = rt_sdhci_execute_tuning, + .card_event = sdhci_card_event, + .card_busy = sdhci_card_busy, +}; + + +void rt_sdhci_uninit_host(struct rt_sdhci_host *host, int dead) +{ + struct rt_mmc_host *mmc = host->mmc; + unsigned long flags; + + if (dead) + { + flags = rt_spin_lock_irqsave(&host->lock); + + host->flags |= RT_SDHCI_DEVICE_DEAD; + + if (sdhci_has_requests(host)) + { + rt_kprintf("%s: Controller removed during " + " transfer!\n", + mmc_hostname(mmc)); + sdhci_error_out_mrqs(host, -ENOMEDIUM); + } + + rt_spin_unlock_irqrestore(&host->lock, flags); + } + + sdhci_set_card_detection(host, RT_FALSE); + + rt_mmc_remove_host(mmc); + + + if (!dead) + sdhci_reset_for_all(host); + + rt_sdhci_writel(host, 0, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, 0, RT_SDHCI_SIGNAL_ENABLE); + + rt_timer_delete(&host->timer); + rt_timer_delete(&host->data_timer); + + rt_workqueue_destroy(host->complete_wq); + +} + +rt_uint16_t rt_sdhci_clk_set(struct rt_sdhci_host *host, unsigned int clock, + unsigned int *actual_clock) +{ + int div = 0; /* Initialized for compiler warning */ + int real_div = div, clk_mul = 1; + rt_uint16_t clk = 0; + rt_bool_t switch_base_clk = RT_FALSE; + + if (host->version >= RT_SDHCI_SPEC_300) + { + if (host->preset_enabled) + { + rt_uint16_t pre_val; + + clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL); + pre_val = sdhci_get_preset_value(host); + div = FIELD_GET(RT_SDHCI_PRESET_SDCLK_FREQ_MASK, pre_val); + if (host->clk_mul && (pre_val & RT_SDHCI_PRESET_CLKGEN_SEL)) + { + clk = RT_SDHCI_PROG_CLOCK_MODE; + real_div = div + 1; + clk_mul = host->clk_mul; + } + else + { + real_div = max_t(int, 1, div << 1); + } + goto clock_set; + } + + if (host->clk_mul) + { + for (div = 1; div <= 1024; div++) + { + if ((host->max_clk * host->clk_mul / div) + <= clock) + break; + } + if ((host->max_clk * host->clk_mul / div) <= clock) + { + + clk = RT_SDHCI_PROG_CLOCK_MODE; + real_div = div; + clk_mul = host->clk_mul; + div--; + } + else + { + + switch_base_clk = RT_TRUE; + } + } + + if (!host->clk_mul || switch_base_clk) + { + if (host->max_clk <= clock) + div = 1; + else + { + for (div = 2; div < RT_SDHCI_MAX_DIV_SPEC_300; + div += 2) + { + if ((host->max_clk / div) <= clock) + break; + } + } + real_div = div; + div >>= 1; + if ((host->quirks2 & RT_SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN) + && !div && host->max_clk <= 25000000) + div = 1; + } + } + else + { + for (div = 1; div < RT_SDHCI_MAX_DIV_SPEC_200; div *= 2) + { + if ((host->max_clk / div) <= clock) + break; + } + real_div = div; + div >>= 1; + } + +clock_set: + if (real_div) + *actual_clock = (host->max_clk * clk_mul) / real_div; + clk |= (div & RT_SDHCI_DIV_MASK) << RT_SDHCI_DIVIDER_SHIFT; + clk |= ((div & RT_SDHCI_DIV_HI_MASK) >> RT_SDHCI_DIV_MASK_LEN) + << RT_SDHCI_DIVIDER_HI_SHIFT; + + return clk; +} + +void rt_sdhci_clk_enable(struct rt_sdhci_host *host, rt_uint16_t clk) +{ + long timeout; + + clk |= RT_SDHCI_CLOCK_INT_EN; + rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL); + + timeout = rt_tick_from_millisecond(150); + while (1) + { + timeout = timeout - rt_tick_get(); + + clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL); + if (clk & RT_SDHCI_CLOCK_INT_STABLE) + break; + if (timeout < 0) + { + rt_kprintf("%s: Internal clock never stabilised.\n", + mmc_hostname(host->mmc)); + rt_read_reg_debug(host); + return; + } + rt_hw_us_delay(10); + } + + if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode) + { + clk |= RT_SDHCI_CLOCK_PLL_EN; + clk &= ~RT_SDHCI_CLOCK_INT_STABLE; + rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL); + + timeout = rt_tick_from_millisecond(150); + while (1) + { + timeout = timeout - rt_tick_get(); + + clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL); + if (clk & RT_SDHCI_CLOCK_INT_STABLE) + break; + if (timeout < 0) + { + rt_kprintf("%s: PLL clock never stabilised.\n", + mmc_hostname(host->mmc)); + rt_read_reg_debug(host); + return; + } + rt_hw_us_delay(10); + } + } + + clk |= RT_SDHCI_CLOCK_CARD_EN; + rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL); +} + +void rt_sdhci_set_clock(struct rt_sdhci_host *host, unsigned int clock) +{ + rt_uint16_t clk; + + host->mmc->actual_clock = 0; + + rt_sdhci_writew(host, 0, RT_SDHCI_CLOCK_CONTROL); + + if (clock == 0) + return; + + clk = rt_sdhci_clk_set(host, clock, &host->mmc->actual_clock); + rt_sdhci_clk_enable(host, clk); +} + +void rt_sdhci_read_caps(struct rt_sdhci_host *host, const rt_uint16_t *ver, + const rt_uint32_t *caps, const rt_uint32_t *caps1) +{ + rt_uint16_t v; + rt_uint64_t dt_caps_mask = 0; + rt_uint64_t dt_caps = 0; + + if (host->read_caps) + return; + + host->read_caps = RT_TRUE; + + if (debug_quirks) + host->quirks = debug_quirks; + + if (debug_quirks2) + host->quirks2 = debug_quirks2; + + sdhci_reset_for_all(host); + + if (host->v4_mode) + sdhci_do_enable_v4_mode(host); +#ifdef RT_USING_OFW + rt_ofw_prop_read_u64(mmc_dev(host->mmc)->ofw_node, + "sdhci-caps-mask", &dt_caps_mask); + rt_ofw_prop_read_u64(mmc_dev(host->mmc)->ofw_node, + "sdhci-caps", &dt_caps); +#endif + v = ver ? *ver : rt_sdhci_readw(host, RT_SDHCI_HOST_VERSION); + host->version = (v & RT_SDHCI_SPEC_VER_MASK) >> RT_SDHCI_SPEC_VER_SHIFT; + + if (caps) + { + host->caps = *caps; + } + else + { + host->caps = rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES); + host->caps &= ~lower_32_bits(dt_caps_mask); + host->caps |= lower_32_bits(dt_caps); + } + + if (host->version < RT_SDHCI_SPEC_300) + return; + + if (caps1) + { + host->caps1 = *caps1; + } + else + { + host->caps1 = rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES_1); + host->caps1 &= ~upper_32_bits(dt_caps_mask); + host->caps1 |= upper_32_bits(dt_caps); + } +} + +struct rt_sdhci_host *rt_sdhci_alloc_host(struct rt_device *dev, + size_t priv_size) +{ + struct rt_mmc_host *mmc; + struct rt_sdhci_host *host; + + mmc = rt_mmc_alloc_host(sizeof(struct rt_sdhci_host) + priv_size, dev); + if (!mmc) + return NULL; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->mmc_host_ops = rt_sdhci_ops; + mmc->ops = &host->mmc_host_ops; + + host->flags = RT_SDHCI_SIGNALING_330; + + host->cqe_ier = RT_SDHCI_CQE_INT_MASK; + host->cqe_err_ier = RT_SDHCI_CQE_INT_ERR_MASK; + + host->tuning_delay = -1; + host->tuning_loop_count = MAX_TUNING_LOOP; + + host->sdma_boundary = RT_SDHCI_DEFAULT_BOUNDARY_ARG; + + host->max_timeout_count = 0xE; + + return host; +} + +int rt_sdhci_setup_host(struct rt_sdhci_host *host) +{ + struct rt_mmc_host *mmc; + size_t max_current_caps; + unsigned int ocr_avail; + unsigned int override_timeout_clk; + size_t max_clk; + int ret = 0; + bool enable_vqmmc = RT_FALSE; + + RT_ASSERT(host != NULL); + + + mmc = host->mmc; + + if (!mmc->supply.vqmmc) + { + if (ret) + return ret; + enable_vqmmc = RT_TRUE; + } + + LOG_D("Version: 0x%08x | Present: 0x%08x\n", + rt_sdhci_readw(host, RT_SDHCI_HOST_VERSION), + rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE)); + LOG_D("Caps: 0x%08x | Caps_1: 0x%08x\n", + rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES), + rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES_1)); + + rt_sdhci_read_caps(host,RT_NULL,RT_NULL,RT_NULL); + + override_timeout_clk = host->timeout_clk; + + if (host->version > RT_SDHCI_SPEC_420) + { + rt_kprintf("%s: Unknown controller version (%d). You may experience problems.\n", + mmc_hostname(mmc), host->version); + } + + if (host->quirks & RT_SDHCI_QUIRK_FORCE_DMA) + host->flags |= RT_SDHCI_USE_SDMA; + else if (!(host->caps & RT_SDHCI_CAN_DO_SDMA)) + LOG_D("Controller doesn't have SDMA capability\n"); + else + host->flags |= RT_SDHCI_USE_SDMA; + + if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_DMA) && (host->flags & RT_SDHCI_USE_SDMA)) + { + LOG_D("Disabling DMA as it is marked broken\n"); + host->flags &= ~RT_SDHCI_USE_SDMA; + } + + if (sdhci_can_64bit_dma(host)) + host->flags |= RT_SDHCI_USE_64_BIT_DMA; + + if (host->flags & RT_SDHCI_USE_SDMA) + { + if (host->ops->set_dma_mask) + ret = host->ops->set_dma_mask(host); + + if (!ret && host->ops->enable_dma) + ret = host->ops->enable_dma(host); + + if (ret) + { + rt_kprintf("%s: No suitable DMA available - falling back to PIO\n", + mmc_hostname(mmc)); + host->flags &= ~RT_SDHCI_USE_SDMA; + + ret = 0; + } + } + + if ((host->flags & RT_SDHCI_USE_64_BIT_DMA) && !host->v4_mode) + host->flags &= ~RT_SDHCI_USE_SDMA; + + if (!(host->flags & RT_SDHCI_USE_SDMA)) + { + host->dma_mask = DMA_BIT_MASK(64); + } + if (host->version >= RT_SDHCI_SPEC_300) + host->max_clk = FIELD_GET(RT_SDHCI_CLOCK_V3_BASE_MASK, host->caps); + else + host->max_clk = FIELD_GET(RT_SDHCI_CLOCK_BASE_MASK, host->caps); + + host->max_clk *= 1000000; + if (host->max_clk == 0 || host->quirks & RT_SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) + { + if (!host->ops->get_max_clock) + { + rt_kprintf("%s: Hardware doesn't specify base clock frequency. %p \n", + mmc_hostname(mmc), host->ops->get_max_clock); + ret = -ENODEV; + goto undma; + } + host->max_clk = host->ops->get_max_clock(host); + } + + host->clk_mul = FIELD_GET(RT_SDHCI_CLOCK_MUL_MASK, host->caps1); + + if (host->clk_mul) + host->clk_mul += 1; + + max_clk = host->max_clk; + + if (host->ops->get_min_clock) + mmc->f_min = host->ops->get_min_clock(host); + else if (host->version >= RT_SDHCI_SPEC_300) + { + if (host->clk_mul) + max_clk = host->max_clk * host->clk_mul; + + mmc->f_min = host->max_clk / RT_SDHCI_MAX_DIV_SPEC_300; + } + else + mmc->f_min = host->max_clk / RT_SDHCI_MAX_DIV_SPEC_200; + + if (!mmc->f_max || mmc->f_max > max_clk) + mmc->f_max = max_clk; + + if (!(host->quirks & RT_SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) + { + host->timeout_clk = FIELD_GET(RT_SDHCI_TIMEOUT_CLK_MASK, host->caps); + + if (host->caps & RT_SDHCI_TIMEOUT_CLK_UNIT) + host->timeout_clk *= 1000; + + if (host->timeout_clk == 0) + { + if (!host->ops->get_timeout_clock) + { + rt_kprintf("%s: Hardware doesn't specify timeout clock frequency.\n", + mmc_hostname(mmc)); + ret = -ENODEV; + goto undma; + } + + host->timeout_clk = + DIV_ROUND_UP(host->ops->get_timeout_clock(host), + 1000); + } + + if (override_timeout_clk) + host->timeout_clk = override_timeout_clk; + + mmc->max_busy_timeout = host->ops->get_max_timeout_count ? host->ops->get_max_timeout_count(host) : 1 << 27; + mmc->max_busy_timeout /= host->timeout_clk; + } + + if (host->quirks2 & RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT && !host->ops->get_max_timeout_count) + mmc->max_busy_timeout = 0; + + mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23; + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; + + if (host->quirks & RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) + host->flags |= RT_SDHCI_AUTO_CMD12; + + if ((host->version >= RT_SDHCI_SPEC_300) && (!(host->flags & RT_SDHCI_USE_SDMA) || host->v4_mode) && !(host->quirks2 & RT_SDHCI_QUIRK2_ACMD23_BROKEN)) + { + host->flags |= RT_SDHCI_AUTO_CMD23; + LOG_D("Auto-CMD23 available\n"); + } + else + { + LOG_D("Auto-CMD23 unavailable\n"); + } + + if (!(host->quirks & RT_SDHCI_QUIRK_FORCE_1_BIT_DATA)) + mmc->caps |= MMC_CAP_4_BIT_DATA; + + if (host->quirks2 & RT_SDHCI_QUIRK2_HOST_NO_CMD23) + mmc->caps &= ~MMC_CAP_CMD23; + + if (host->caps & RT_SDHCI_CAN_DO_HISPD) + mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION) && mmc_card_is_removable(mmc) && rt_mmc_gpio_get_cd(mmc) < 0) + mmc->caps |= MMC_CAP_NEEDS_POLL; + + if (mmc->supply.vqmmc) + { + if (enable_vqmmc) + { + host->sdhci_core_to_disable_vqmmc = !ret; + } + + if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000, + 1950000)) + host->caps1 &= ~(RT_SDHCI_SUPPORT_SDR104 | RT_SDHCI_SUPPORT_SDR50 | RT_SDHCI_SUPPORT_DDR50); + + if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 2700000, + 3600000)) + host->flags &= ~RT_SDHCI_SIGNALING_330; + + if (ret) + { + rt_kprintf("%s: Failed to enable vqmmc regulator: %d\n", + mmc_hostname(mmc), ret); + mmc->supply.vqmmc = (void *)-EINVAL; + } + } + if (host->quirks2 & RT_SDHCI_QUIRK2_NO_1_8_V) + { + host->caps1 &= ~(RT_SDHCI_SUPPORT_SDR104 | RT_SDHCI_SUPPORT_SDR50 | RT_SDHCI_SUPPORT_DDR50); + mmc->caps2 &= ~(MMC_CAP2_HSX00_1_8V | MMC_CAP2_HS400_ES); + mmc->caps &= ~(MMC_CAP_1_8V_DDR | MMC_CAP_UHS); + } + + if (host->caps1 & (RT_SDHCI_SUPPORT_SDR104 | RT_SDHCI_SUPPORT_SDR50 | RT_SDHCI_SUPPORT_DDR50)) + mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; + + if (host->caps1 & RT_SDHCI_SUPPORT_SDR104) + { + mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50; + + if (!(host->quirks2 & RT_SDHCI_QUIRK2_BROKEN_HS200)) + mmc->caps2 |= MMC_CAP2_HS200; + } + else if (host->caps1 & RT_SDHCI_SUPPORT_SDR50) + { + mmc->caps |= MMC_CAP_UHS_SDR50; + } + + if (host->quirks2 & RT_SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 && (host->caps1 & RT_SDHCI_SUPPORT_HS400)) + mmc->caps2 |= MMC_CAP2_HS400; + if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) && (!mmc->supply.vqmmc || !regulator_is_supported_voltage(mmc->supply.vqmmc, 1100000, 1300000))) + mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V; + if ((host->caps1 & RT_SDHCI_SUPPORT_DDR50) && !(host->quirks2 & RT_SDHCI_QUIRK2_BROKEN_DDR50)) + mmc->caps |= MMC_CAP_UHS_DDR50; + + if (host->caps1 & RT_SDHCI_USE_SDR50_TUNING) + host->flags |= RT_SDHCI_SDR50_NEEDS_TUNING; + + if (host->caps1 & RT_SDHCI_DRIVER_TYPE_A) + mmc->caps |= MMC_CAP_DRIVER_TYPE_A; + if (host->caps1 & RT_SDHCI_DRIVER_TYPE_C) + mmc->caps |= MMC_CAP_DRIVER_TYPE_C; + if (host->caps1 & RT_SDHCI_DRIVER_TYPE_D) + mmc->caps |= MMC_CAP_DRIVER_TYPE_D; + + host->tuning_count = FIELD_GET(RT_SDHCI_RETUNING_TIMER_COUNT_MASK, + host->caps1); + + if (host->tuning_count) + host->tuning_count = 1 << (host->tuning_count - 1); + + /* Re-tuning mode supported by the Host Controller */ + host->tuning_mode = FIELD_GET(RT_SDHCI_RETUNING_MODE_MASK, host->caps1); + + ocr_avail = 0; + + max_current_caps = rt_sdhci_readl(host, RT_SDHCI_MAX_CURRENT); + + if (!max_current_caps && mmc->supply.vmmc) + { + int curr = regulator_get_current_limit(mmc->supply.vmmc); + if (curr > 0) + { + curr = curr / 1000; /* convert to mA */ + curr = curr / RT_SDHCI_MAX_CURRENT_MULTIPLIER; + + curr = min_t(rt_uint32_t, curr, RT_SDHCI_MAX_CURRENT_LIMIT); + max_current_caps = + FIELD_PREP(RT_SDHCI_MAX_CURRENT_330_MASK, curr) | FIELD_PREP(RT_SDHCI_MAX_CURRENT_300_MASK, curr) | FIELD_PREP(RT_SDHCI_MAX_CURRENT_180_MASK, curr); + } + } + + if (host->caps & RT_SDHCI_CAN_VDD_330) + { + ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34; + + mmc->max_current_330 = FIELD_GET(RT_SDHCI_MAX_CURRENT_330_MASK, + max_current_caps) + * RT_SDHCI_MAX_CURRENT_MULTIPLIER; + } + if (host->caps & RT_SDHCI_CAN_VDD_300) + { + ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31; + + mmc->max_current_300 = FIELD_GET(RT_SDHCI_MAX_CURRENT_300_MASK, + max_current_caps) + * RT_SDHCI_MAX_CURRENT_MULTIPLIER; + } + if (host->caps & RT_SDHCI_CAN_VDD_180) + { + ocr_avail |= MMC_VDD_165_195; + + mmc->max_current_180 = FIELD_GET(RT_SDHCI_MAX_CURRENT_180_MASK, + max_current_caps) + * RT_SDHCI_MAX_CURRENT_MULTIPLIER; + } + + if (host->ocr_mask) + ocr_avail = host->ocr_mask; + + if (mmc->ocr_avail) + ocr_avail = mmc->ocr_avail; + + mmc->ocr_avail = ocr_avail; + mmc->ocr_avail_sdio = ocr_avail; + if (host->ocr_avail_sdio) + mmc->ocr_avail_sdio &= host->ocr_avail_sdio; + mmc->ocr_avail_sd = ocr_avail; + if (host->ocr_avail_sd) + mmc->ocr_avail_sd &= host->ocr_avail_sd; + else + mmc->ocr_avail_sd &= ~MMC_VDD_165_195; + mmc->ocr_avail_mmc = ocr_avail; + if (host->ocr_avail_mmc) + mmc->ocr_avail_mmc &= host->ocr_avail_mmc; + + if (mmc->ocr_avail == 0) + { + rt_kprintf("%s: Hardware doesn't report any support voltages.\n", + mmc_hostname(mmc)); + ret = -ENODEV; + goto unreg; + } + + if ((mmc->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR)) || (mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V))) + host->flags |= RT_SDHCI_SIGNALING_180; + + if (mmc->caps2 & MMC_CAP2_HSX00_1_2V) + host->flags |= RT_SDHCI_SIGNALING_120; + + rt_spin_lock_init(&host->lock); + + mmc->max_req_size = 524288; + if (host->flags & RT_SDHCI_USE_SDMA) + { + mmc->max_segs = 1; + } + else + { /* PIO */ + mmc->max_segs = RT_SDHCI_MAX_SEGS; + } + + mmc->max_seg_size = mmc->max_req_size; + + if (host->quirks & RT_SDHCI_QUIRK_FORCE_BLK_SZ_2048) + { + mmc->max_blk_size = 2; + } + else + { + mmc->max_blk_size = (host->caps & RT_SDHCI_MAX_BLOCK_MASK) >> RT_SDHCI_MAX_BLOCK_SHIFT; + if (mmc->max_blk_size >= 3) + { + rt_kprintf("%s: Invalid maximum block size, assuming 512 bytes\n", + mmc_hostname(mmc)); + mmc->max_blk_size = 0; + } + } + + mmc->max_blk_size = 512 << mmc->max_blk_size; + + /* + * Maximum block count. + */ + mmc->max_blk_count = (host->quirks & RT_SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535; + return 0; + +unreg: +undma: + return ret; +} + +static void sdhci_init(struct rt_sdhci_host *host, int soft) +{ + struct rt_mmc_host *mmc = host->mmc; + rt_base_t flags; + + if (soft) + { + sdhci_do_reset(host, RT_SDHCI_RESET_CMD | RT_SDHCI_RESET_DATA); + } + else + { + sdhci_do_reset(host, RT_SDHCI_RESET_ALL); + } + if (host->v4_mode) + { + sdhci_do_enable_v4_mode(host); + } + flags = rt_spin_lock_irqsave(&host->lock); + sdhci_set_default_irqs(host); + rt_spin_unlock_irqrestore(&host->lock, flags); + + host->cqe_on = RT_FALSE; + + if (soft) + { + /* force clock reconfiguration */ + host->clock = 0; + host->reinit_uhs = RT_TRUE; + mmc->ops->set_ios(mmc, &mmc->ios); + } +} + +static void sdhci_reinit(struct rt_sdhci_host *host) +{ + rt_uint32_t cd = host->ier & (RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CARD_INSERT); + + sdhci_init(host, 0); + sdhci_enable_card_detection(host); + + if (cd != (host->ier & (RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CARD_INSERT))) + rt_mmc_detect_change(host->mmc, rt_tick_from_millisecond(200)); +} + +int rt_sdhci_init_host(struct rt_sdhci_host *host) +{ + struct rt_mmc_host *mmc = host->mmc; + int ret; + + if ((mmc->caps2 & MMC_CAP2_CQE) && (host->quirks & RT_SDHCI_QUIRK_BROKEN_CQE)) + { + mmc->caps2 &= ~MMC_CAP2_CQE; + } + + host->complete_wq = rt_workqueue_create("sdhci", 4096, 20); + if (!host->complete_wq) + return -ENOMEM; + + rt_work_init(&host->complete_work, sdhci_complete_work, host); + + rt_timer_init(&host->timer, "sdhci_timer", sdhci_timeout_timer, host, 0, RT_TIMER_FLAG_SOFT_TIMER); + rt_timer_init(&host->data_timer, "sdhci_data_timer", sdhci_timeout_data_timer, host, 0, RT_TIMER_FLAG_SOFT_TIMER); + + rt_wqueue_init(&host->buf_ready_int); + + sdhci_init(host, 0); + + host->irq_wq = rt_workqueue_create("sdhci_irq", 8192, 1); + rt_work_init(&host->irq_work, sdhci_thread_irq, host); + rt_hw_interrupt_install(host->irq, sdhci_irq, host, mmc_hostname(mmc)); + rt_pic_irq_unmask(host->irq); + ret = rt_mmc_add_host(mmc); + if (ret) + goto unirq; + + LOG_D("%s: RT_SDHCI controller on %s [%s] using %s\n", + mmc_hostname(mmc), host->hw_name, mmc_dev(mmc)->parent.name, + (host->flags & RT_SDHCI_USE_SDMA) ? "DMA" : "PIO"); + + sdhci_enable_card_detection(host); + + return 0; + +unirq: + sdhci_reset_for_all(host); + rt_sdhci_writel(host, 0, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, 0, RT_SDHCI_SIGNAL_ENABLE); + + return ret; +} + +int rt_sdhci_set_and_add_host(struct rt_sdhci_host *host) +{ + int ret; + ret = rt_sdhci_setup_host(host); + if (ret) + return ret; + + ret = rt_sdhci_init_host(host); + if (ret) + goto cleanup; + + return 0; + +cleanup: + rt_sdhci_cleanup_host(host); + + return ret; +} + +void rt_sdhci_ios_set(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_bool_t reinit_uhs = host->reinit_uhs; + rt_bool_t turning_on_clk = RT_FALSE; + rt_uint8_t ctrl; + + host->reinit_uhs = RT_FALSE; + + if (ios->power_mode == MMC_POWER_UNDEFINED) + return; + + if (host->flags & RT_SDHCI_DEVICE_DEAD) + { + if (mmc->supply.vmmc && ios->power_mode == MMC_POWER_OFF) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + return; + } + + if (ios->power_mode == MMC_POWER_OFF) + { + rt_sdhci_writel(host, 0, RT_SDHCI_SIGNAL_ENABLE); + sdhci_reinit(host); + } + + if (host->version >= RT_SDHCI_SPEC_300 && (ios->power_mode == MMC_POWER_UP) && !(host->quirks2 & RT_SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) + sdhci_preset_value_enable(host, RT_FALSE); + + if (!ios->clock || ios->clock != host->clock) + { + turning_on_clk = ios->clock && !host->clock; + + host->ops->set_clock(host, ios->clock); + host->clock = ios->clock; + + if (host->quirks & RT_SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK && host->clock) + { + host->timeout_clk = mmc->actual_clock ? mmc->actual_clock / 1000 : host->clock / 1000; + mmc->max_busy_timeout = + host->ops->get_max_timeout_count ? host->ops->get_max_timeout_count(host) : 1 << 27; + mmc->max_busy_timeout /= host->timeout_clk; + } + } + + if (host->ops->set_power) + host->ops->set_power(host, ios->power_mode, ios->vdd); + else + rt_sdhci_set_power(host, ios->power_mode, ios->vdd); + + host->ops->set_bus_width(host, ios->bus_width); + + if (!reinit_uhs && turning_on_clk && host->timing == ios->timing && host->version >= RT_SDHCI_SPEC_300 && !sdhci_presetable_values_change(host, ios)) + return; + + ctrl = rt_sdhci_readb(host, RT_SDHCI_HOST_CONTROL); + + if (!(host->quirks & RT_SDHCI_QUIRK_NO_HISPD_BIT)) + { + if (ios->timing == MMC_TIMING_SD_HS || ios->timing == MMC_TIMING_MMC_HS || ios->timing == MMC_TIMING_MMC_HS400 || ios->timing == MMC_TIMING_MMC_HS200 || ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_SDR50 || ios->timing == MMC_TIMING_UHS_SDR104 || ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_UHS_SDR25) + ctrl |= RT_SDHCI_CTRL_HISPD; + else + ctrl &= ~RT_SDHCI_CTRL_HISPD; + } + + if (host->version >= RT_SDHCI_SPEC_300) + { + rt_uint16_t clk, ctrl_2; + + clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL); + if (clk & RT_SDHCI_CLOCK_CARD_EN) + { + clk &= ~RT_SDHCI_CLOCK_CARD_EN; + rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL); + } + + rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL); + + if (!host->preset_enabled) + { + ctrl_2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + ctrl_2 &= ~RT_SDHCI_CTRL_DRV_TYPE_MASK; + if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) + ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_A; + else if (ios->drv_type == MMC_SET_DRIVER_TYPE_B) + ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_B; + else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C) + ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_C; + else if (ios->drv_type == MMC_SET_DRIVER_TYPE_D) + ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_D; + else + { + LOG_D("%s: invalid driver type, default to driver type B\n", + mmc_hostname(mmc)); + ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_B; + } + + rt_sdhci_writew(host, ctrl_2, RT_SDHCI_HOST_CONTROL2); + host->drv_type = ios->drv_type; + } + + host->ops->set_uhs_signaling(host, ios->timing); + host->timing = ios->timing; + + if (sdhci_preset_needed(host, ios->timing)) + { + rt_uint16_t preset; + + sdhci_preset_value_enable(host, RT_TRUE); + preset = sdhci_get_preset_value(host); + ios->drv_type = FIELD_GET(RT_SDHCI_PRESET_DRV_MASK, + preset); + host->drv_type = ios->drv_type; + } + + host->ops->set_clock(host, host->clock); + } + else + rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL); +} +void rt_sdhci_free_host(struct rt_sdhci_host *host) +{ + rt_sdhci_cleanup_host(host); + rt_free(host); +} diff --git a/rt-thread/components/drivers/sensor/Kconfig b/rt-thread/components/drivers/sensor/Kconfig index 86f8711..976e6a7 100644 --- a/rt-thread/components/drivers/sensor/Kconfig +++ b/rt-thread/components/drivers/sensor/Kconfig @@ -10,6 +10,6 @@ if RT_USING_SENSOR config RT_USING_SENSOR_CMD bool "Using Sensor cmd" - select PKG_USING_RT_VSNPRINTF_FULL if RT_USING_SENSOR_V2 + select RT_KLIBC_USING_VSNPRINTF_STANDARD if RT_USING_SENSOR_V2 default y endif diff --git a/rt-thread/components/drivers/sensor/v1/sensor.c b/rt-thread/components/drivers/sensor/v1/sensor.c index 6da12f9..479cf83 100644 --- a/rt-thread/components/drivers/sensor/v1/sensor.c +++ b/rt-thread/components/drivers/sensor/v1/sensor.c @@ -40,7 +40,10 @@ static char *const sensor_name_str[] = "spo2_", /* SpO2 sensor */ "iaq_", /* IAQ sensor */ "etoh_", /* EtOH sensor */ - "bp_" /* Blood Pressure */ + "bp_", /* Blood Pressure */ + "volt_", /* Voltage sensor */ + "curr_", /* Current sensor */ + "pow_" /* Power sensor */ }; /* Sensor interrupt correlation function */ diff --git a/rt-thread/components/drivers/sensor/v1/sensor_cmd.c b/rt-thread/components/drivers/sensor/v1/sensor_cmd.c index 0410af4..486bd0b 100644 --- a/rt-thread/components/drivers/sensor/v1/sensor_cmd.c +++ b/rt-thread/components/drivers/sensor/v1/sensor_cmd.c @@ -83,6 +83,15 @@ static void sensor_show_data(rt_size_t num, rt_sensor_t sensor, struct rt_sensor case RT_SENSOR_CLASS_BP: LOG_I("num:%3d, bp.sbp:%5d mmHg, bp.dbp:%5d mmHg, timestamp:%5d", num, sensor_data->data.bp.sbp, sensor_data->data.bp.dbp, sensor_data->timestamp); break; + case RT_SENSOR_CLASS_VOLTAGE: + LOG_I("num:%3d, voltage:%5d mV, timestamp:%5d", num, sensor_data->data.mv, sensor_data->timestamp); + break; + case RT_SENSOR_CLASS_CURRENT: + LOG_I("num:%3d, current:%5d mA, timestamp:%5d", num, sensor_data->data.ma, sensor_data->timestamp); + break; + case RT_SENSOR_CLASS_POWER: + LOG_I("num:%3d, power:%5d mW, timestamp:%5d", num, sensor_data->data.mv, sensor_data->timestamp); + break; default: break; } @@ -281,7 +290,7 @@ static void sensor_polling(int argc, char **argv) MSH_CMD_EXPORT(sensor_polling, Sensor polling mode test function); #endif -static void sensor(int argc, char **argv) +static int sensor(int argc, char **argv) { static rt_device_t dev = RT_NULL; struct rt_sensor_data data; @@ -302,7 +311,7 @@ static void sensor(int argc, char **argv) rt_kprintf(" sodr Set output date rate to var\n"); rt_kprintf(" read [num] Read [num] times sensor\n"); rt_kprintf(" num default 5\n"); - return ; + return -RT_EINVAL; } else if (!strcmp(argv[1], "info")) { @@ -310,7 +319,7 @@ static void sensor(int argc, char **argv) if (dev == RT_NULL) { LOG_W("Please probe sensor device first!"); - return ; + return -RT_ERROR; } rt_device_control(dev, RT_SENSOR_CTRL_GET_INFO, &info); switch (info.vendor) @@ -418,6 +427,15 @@ static void sensor(int argc, char **argv) case RT_SENSOR_UNIT_MMHG: rt_kprintf("unit :mmHg\n"); break; + case RT_SENSOR_UNIT_MV: + rt_kprintf("unit :mV\n"); + break; + case RT_SENSOR_UNIT_MA: + rt_kprintf("unit :mA\n"); + break; + case RT_SENSOR_UNIT_MW: + rt_kprintf("unit :mW\n"); + break; } rt_kprintf("range_max :%d\n", info.range_max); rt_kprintf("range_min :%d\n", info.range_min); @@ -431,7 +449,7 @@ static void sensor(int argc, char **argv) if (dev == RT_NULL) { LOG_W("Please probe sensor device first!"); - return ; + return -RT_ERROR; } if (argc == 3) { @@ -469,12 +487,12 @@ static void sensor(int argc, char **argv) if (dev == RT_NULL) { LOG_E("Can't find device:%s", argv[2]); - return; + return -RT_ERROR; } if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) { LOG_E("open device failed!"); - return; + return -RT_ERROR; } rt_device_control(dev, RT_SENSOR_CTRL_GET_ID, ®); LOG_I("device id: 0x%x!", reg); @@ -483,7 +501,7 @@ static void sensor(int argc, char **argv) else if (dev == RT_NULL) { LOG_W("Please probe sensor first!"); - return ; + return -RT_ERROR; } else if (!strcmp(argv[1], "sr")) { @@ -510,6 +528,8 @@ static void sensor(int argc, char **argv) { LOG_W("Unknown command, please enter 'sensor' get help information!"); } + + return RT_EOK; } #ifdef RT_USING_FINSH MSH_CMD_EXPORT(sensor, sensor test function); diff --git a/rt-thread/components/drivers/sensor/v2/sensor_cmd.c b/rt-thread/components/drivers/sensor/v2/sensor_cmd.c index e278f34..1b65c00 100644 --- a/rt-thread/components/drivers/sensor/v2/sensor_cmd.c +++ b/rt-thread/components/drivers/sensor/v2/sensor_cmd.c @@ -550,7 +550,7 @@ static void sensor_cmd_warning_probe(void) LOG_W("Please probe sensor device first!"); } -static void sensor(int argc, char **argv) +static int sensor(int argc, char **argv) { static rt_device_t dev = RT_NULL; struct rt_sensor_data data; @@ -562,14 +562,14 @@ static void sensor(int argc, char **argv) if (argc < 2) { sensor_cmd_warning_unknown(); - return; + return -RT_ERROR; } else if (!rt_strcmp(argv[1], "info")) { if (dev == RT_NULL) { sensor_cmd_warning_probe(); - return ; + return -RT_ERROR; } sensor = (rt_sensor_t)dev; rt_kprintf("name :%s\n", sensor->info.name); @@ -595,7 +595,7 @@ static void sensor(int argc, char **argv) if (dev == RT_NULL) { sensor_cmd_warning_probe(); - return; + return -RT_ERROR; } if (argc == 3) { @@ -628,7 +628,7 @@ static void sensor(int argc, char **argv) information = rt_object_get_information(RT_Object_Class_Device); if(information == RT_NULL) - return; + return -RT_ERROR; rt_kprintf("device name sensor name sensor type mode resolution range\n"); rt_kprintf("----------- ------------- ------------------ ---- ---------- ----------\n"); @@ -655,7 +655,7 @@ static void sensor(int argc, char **argv) if (dev == RT_NULL) { sensor_cmd_warning_probe(); - return; + return -RT_ERROR; } if (rt_device_control(dev, RT_SENSOR_CTRL_SOFT_RESET, RT_NULL) != RT_EOK) @@ -671,19 +671,19 @@ static void sensor(int argc, char **argv) if (argc < 3) { sensor_cmd_warning_unknown(); - return; + return -RT_ERROR; } new_dev = rt_device_find(argv[2]); if (new_dev == RT_NULL) { LOG_E("Can't find device:%s", argv[2]); - return; + return -RT_ERROR; } if (rt_device_open(new_dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) { LOG_E("open device failed!"); - return; + return -RT_ERROR; } if (rt_device_control(new_dev, RT_SENSOR_CTRL_GET_ID, ®) == RT_EOK) { @@ -702,7 +702,7 @@ static void sensor(int argc, char **argv) if (dev == RT_NULL) { sensor_cmd_warning_probe(); - return; + return -RT_ERROR; } sensor = (rt_sensor_t)dev; @@ -734,7 +734,7 @@ static void sensor(int argc, char **argv) if (dev == RT_NULL) { sensor_cmd_warning_probe(); - return; + return -RT_ERROR; } sensor = (rt_sensor_t)dev; @@ -766,7 +766,7 @@ static void sensor(int argc, char **argv) if (dev == RT_NULL) { sensor_cmd_warning_probe(); - return; + return -RT_ERROR; } sensor = (rt_sensor_t)dev; @@ -795,5 +795,7 @@ static void sensor(int argc, char **argv) { sensor_cmd_warning_unknown(); } + + return RT_EOK; } MSH_CMD_EXPORT(sensor, sensor test function); diff --git a/rt-thread/components/drivers/serial/Kconfig b/rt-thread/components/drivers/serial/Kconfig index 00fcfb6..349aaea 100644 --- a/rt-thread/components/drivers/serial/Kconfig +++ b/rt-thread/components/drivers/serial/Kconfig @@ -21,4 +21,7 @@ menuconfig RT_USING_SERIAL int "Set RX buffer size" depends on !RT_USING_SERIAL_V2 default 64 - endif + config RT_USING_SERIAL_BYPASS + bool "Using serial bypass" + default n + endif diff --git a/rt-thread/components/drivers/serial/SConscript b/rt-thread/components/drivers/serial/SConscript index 7ee41a4..04fa37e 100644 --- a/rt-thread/components/drivers/serial/SConscript +++ b/rt-thread/components/drivers/serial/SConscript @@ -12,9 +12,12 @@ if GetDepend(['RT_USING_SMART']): src += Glob('serial_tty.c') if GetDepend(['RT_USING_SERIAL_V2']): - src += ['serial_v2.c'] + src += ['dev_serial_v2.c'] else: - src += ['serial.c'] + src += ['dev_serial.c'] + +if GetDepend(['RT_USING_SERIAL_BYPASS']): + src += ['bypass.c'] if GetDepend(['RT_USING_DM']): src += ['serial_dm.c'] diff --git a/rt-thread/components/drivers/serial/bypass.c b/rt-thread/components/drivers/serial/bypass.c new file mode 100644 index 0000000..04ed71a --- /dev/null +++ b/rt-thread/components/drivers/serial/bypass.c @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-11-20 zhujiale the first version + */ + +#include +#define DBG_TAG "UART" +#define DBG_LVL DBG_INFO +#include + +static struct rt_serial_bypass_func* rt_bypass_alloc_func(const char* name, rt_uint8_t level, bypass_function_t func, void* data) +{ + struct rt_serial_bypass_func* bypass; + + if (!func) + return RT_NULL; + + bypass = rt_malloc(sizeof(struct rt_serial_bypass_func)); + rt_memset(bypass, 0, sizeof(struct rt_serial_bypass_func)); + + if (rt_strlen(name) > RT_NAME_MAX - 1) + rt_memcpy(bypass->name, name, RT_NAME_MAX); + else + rt_memcpy(bypass->name, name, rt_strlen(name) + 1); + + bypass->level = level; + + rt_list_init(&bypass->node); + bypass->bypass = func; + bypass->data = data; + + return bypass; +} + +rt_err_t rt_serial_bypass_init(struct rt_serial_device* serial) +{ + serial->bypass = rt_malloc(sizeof(struct rt_serial_bypass)); + rt_memset(serial->bypass, 0, sizeof(struct rt_serial_bypass)); + serial->bypass->pipe = rt_ringbuffer_create(serial->config.bufsz); + serial->bypass->mutex = rt_mutex_create("serial_bypass", RT_IPC_FLAG_FIFO); + + return RT_EOK; +} + +static rt_err_t rt_bypass_register(struct rt_serial_bypass_head* bypass, const char* name, rt_uint8_t level, bypass_function_t func, void* data) +{ + struct rt_serial_bypass_func* pass = RT_NULL; + struct rt_list_node* node; + int flags; + + RT_DEBUG_NOT_IN_INTERRUPT; + + pass = rt_bypass_alloc_func(name, level, func, data); + + RT_ASSERT(bypass != RT_NULL); + + node = bypass->head.next; + + if (node == &bypass->head) + { + rt_list_insert_before(&pass->node, node); + return RT_EOK; + } + flags = rt_spin_lock_irqsave(&(bypass->spinlock)); + do { + struct rt_serial_bypass_func* temp_curr; + temp_curr = rt_container_of(node, struct rt_serial_bypass_func, node); + + if (level < temp_curr->level) + { + rt_list_insert_before(node, &pass->node); + + rt_spin_unlock_irqrestore(&(bypass->spinlock), flags); + return RT_EOK; + } + else if (level == temp_curr->level) + { + rt_spin_unlock_irqrestore(&(bypass->spinlock), flags); + LOG_E("Conflict: bypass [%s] level conflicts with [%s] at level [%d]\n", name, temp_curr->name, level); + rt_free(pass); + return -RT_ERROR; + } + + node = node->next; + + } while (node != &bypass->head); + + rt_list_insert_before(&bypass->head, &pass->node); + + rt_spin_unlock_irqrestore(&(bypass->spinlock), flags); + return RT_EOK; + +} + +rt_err_t rt_bypass_upper_register(struct rt_serial_device* serial, const char* name, rt_uint8_t level, bypass_function_t func, void* data) +{ + if (!serial->bypass) + rt_serial_bypass_init(serial); + + if (!serial->bypass->upper_h) + { + serial->bypass->upper_h = rt_malloc(sizeof(struct rt_serial_bypass_head)); + rt_spin_lock_init(&serial->bypass->upper_h->spinlock); + rt_list_init(&serial->bypass->upper_h->head); + + } + + return rt_bypass_register(serial->bypass->upper_h, name, level, func, data); +} + + + +void rt_bypass_putchar(struct rt_serial_device* serial, rt_uint8_t ch) +{ + rt_mutex_take(serial->bypass->mutex, RT_WAITING_FOREVER); + rt_ringbuffer_putchar(serial->bypass->pipe, ch); + rt_mutex_release(serial->bypass->mutex); +} + +rt_size_t rt_bypass_getchar(struct rt_serial_device* serial, rt_uint8_t* ch) +{ + int flags; + rt_mutex_take(serial->bypass->mutex, RT_WAITING_FOREVER); + flags = rt_ringbuffer_getchar(serial->bypass->pipe, ch); + rt_mutex_release(serial->bypass->mutex); + return flags; +} + +static inline rt_err_t _bypass_getchar_form_serial_fifo(struct rt_serial_device* serial, char* ch) +{ + rt_base_t level; + struct rt_serial_rx_fifo* rx_fifo; + rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx; + /* disable interrupt */ + level = rt_spin_lock_irqsave(&(serial->spinlock)); + + /* there's no data: */ + if ((rx_fifo->get_index == rx_fifo->put_index) && (rx_fifo->is_full == RT_FALSE)) + { + /* no data, enable interrupt and break out */ + rt_spin_unlock_irqrestore(&(serial->spinlock), level); + return -RT_EEMPTY; + } + + /* otherwise there's the data: */ + *ch = rx_fifo->buffer[rx_fifo->get_index]; + rx_fifo->get_index += 1; + if (rx_fifo->get_index >= serial->config.bufsz) rx_fifo->get_index = 0; + + if (rx_fifo->is_full == RT_TRUE) + { + rx_fifo->is_full = RT_FALSE; + } + + /* enable interrupt */ + rt_spin_unlock_irqrestore(&(serial->spinlock), level); + + return RT_EOK; +} + +static void _lower_work(struct rt_serial_device* serial) +{ + struct rt_list_node* node; + struct rt_serial_bypass_func* temp_curr = RT_NULL; + if (serial->bypass && serial->bypass->lower_h) + { + while (1) + { + char ch; + if (_bypass_getchar_form_serial_fifo(serial, &ch)) + return; + + node = serial->bypass->lower_h->head.next; + + while (node != &serial->bypass->lower_h->head) + { + temp_curr = rt_container_of(node, struct rt_serial_bypass_func, node); + + if (!temp_curr->bypass(serial, ch, temp_curr->data)) + { + break; + } + + node = node->next; + } + if (node == &serial->bypass->lower_h->head) + { + rt_bypass_putchar(serial, ch); + } + } + } +} + + +static void rt_lower_work(struct rt_work* work, void* work_data) +{ + struct rt_serial_device* serial = (struct rt_serial_device*)work_data; + + RT_ASSERT(serial != RT_NULL); + + _lower_work(serial); +} + +rt_err_t rt_bypass_lower_register(struct rt_serial_device* serial, const char* name, rt_uint8_t level, bypass_function_t func, void* data) +{ + if (!serial->bypass) + rt_serial_bypass_init(serial); + + if (!serial->bypass->lower_h) + { + serial->bypass->lower_workq = rt_workqueue_create("serial bypass", RT_SYSTEM_WORKQUEUE_STACKSIZE, + RT_SYSTEM_WORKQUEUE_PRIORITY); + rt_work_init(&serial->bypass->work, rt_lower_work, (void*)serial); + + serial->bypass->lower_h = rt_malloc(sizeof(struct rt_serial_bypass_head)); + rt_spin_lock_init(&serial->bypass->lower_h->spinlock); + rt_list_init(&serial->bypass->lower_h->head); + + } + + return rt_bypass_register(serial->bypass->lower_h, name, level, func, data); +} + +void rt_bypass_work_straight(struct rt_serial_device* serial) +{ + if (serial->bypass && serial->bypass->lower_h) + { + _lower_work(serial); + return; + } + + while (1) + { + char ch; + if (_bypass_getchar_form_serial_fifo(serial, &ch)) + return; + + rt_bypass_putchar(serial, ch); + } + +} + +rt_err_t rt_bypass_unregister(struct rt_serial_bypass_head* bypass, rt_uint8_t level) +{ + struct rt_list_node* node; + struct rt_serial_bypass_func* temp_curr = RT_NULL; + int flags; + + /*Can not unregister protect level in bypass it general be msh or tty*/ + if (level > RT_BYPASS_PROTECT_LEVEL_1) + return -RT_ERROR; + + if (!bypass) + return -RT_ERROR; + + node = bypass->head.next; + flags = rt_spin_lock_irqsave(&(bypass->spinlock)); + + do { + temp_curr = rt_container_of(node, struct rt_serial_bypass_func, node); + + if (level == temp_curr->level) + { + rt_list_remove(node); + rt_spin_unlock_irqrestore(&(bypass->spinlock), flags); + rt_free(temp_curr); + return RT_EOK; + } + + node = node->next; + } while (node != &bypass->head); + + LOG_E("Can't find bypass with level [%d]", level); + rt_spin_unlock_irqrestore(&(bypass->spinlock), flags); + return -RT_ERROR; +} + +rt_err_t rt_bypass_upper_unregister(struct rt_serial_device* serial, rt_uint8_t level) +{ + if (!serial->bypass || !serial->bypass->upper_h) + return -RT_ERROR; + return rt_bypass_unregister(serial->bypass->upper_h, level); +} + +rt_err_t rt_bypass_lower_unregister(struct rt_serial_device* serial, rt_uint8_t level) +{ + if (!serial->bypass || !serial->bypass->lower_h) + return -RT_ERROR; + return rt_bypass_unregister(serial->bypass->lower_h, level); +} + +int serial_bypass_list(int argc, char** argv) +{ + struct rt_serial_device* serial = RT_NULL; + struct rt_serial_bypass_func* current; + struct rt_list_node* node; + int flags; + + serial = (struct rt_serial_device*)rt_console_get_device(); + if (!serial || !serial->bypass) + { + rt_kprintf("Serial bypass not initialized.\n"); + return -1; + } + + /* 遍历 Upper Bypass 链表 */ + if (serial->bypass->upper_h) + { + rt_kprintf("Upper bypass chain:\n"); + node = serial->bypass->upper_h->head.next; + + flags = rt_spin_lock_irqsave(&(serial->bypass->upper_h->spinlock)); /* 加锁*/ + while (node != &serial->bypass->upper_h->head) + { + current = rt_container_of(node, struct rt_serial_bypass_func, node); + rt_kprintf(" - Name: [%s], Level: [%d]\n", current->name, current->level); + node = node->next; + } + rt_spin_unlock_irqrestore(&(serial->bypass->upper_h->spinlock), flags); /* 解锁*/ + } + else + { + rt_kprintf("Upper bypass chain is empty.\n"); + } + + /* 遍历 Lower Bypass 链表 */ + if (serial->bypass->lower_h) + { + rt_kprintf("Lower bypass chain:\n"); + node = serial->bypass->lower_h->head.next; + + flags = rt_spin_lock_irqsave(&(serial->bypass->lower_h->spinlock)); /* 加锁*/ + while (node != &serial->bypass->lower_h->head) + { + current = rt_container_of(node, struct rt_serial_bypass_func, node); + rt_kprintf(" - Name: [%s], Level: [%d]\n", current->name, current->level); + node = node->next; + } + rt_spin_unlock_irqrestore(&(serial->bypass->lower_h->spinlock), flags); /* 解锁*/ + } + else + { + rt_kprintf("Lower bypass chain is empty.\n"); + } + + return 0; +} + + +MSH_CMD_EXPORT(serial_bypass_list, serial bypass list) diff --git a/rt-thread/components/drivers/serial/serial.c b/rt-thread/components/drivers/serial/dev_serial.c similarity index 96% rename from rt-thread/components/drivers/serial/serial.c rename to rt-thread/components/drivers/serial/dev_serial.c index 19043e2..458a5b7 100644 --- a/rt-thread/components/drivers/serial/serial.c +++ b/rt-thread/components/drivers/serial/dev_serial.c @@ -27,6 +27,7 @@ * 2020-12-14 Meco Man implement function of setting window's size(TIOCSWINSZ) * 2021-08-22 Meco Man implement function of getting window's size(TIOCGWINSZ) * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable + * 2024-11-25 zhujiale add bypass mode */ #include @@ -303,6 +304,23 @@ rt_inline int _serial_int_rx(struct rt_serial_device *serial, rt_uint8_t *data, rx_fifo = (struct rt_serial_rx_fifo*) serial->serial_rx; RT_ASSERT(rx_fifo != RT_NULL); +#ifdef RT_USING_SERIAL_BYPASS + if (serial->bypass) + { + rt_bypass_work_straight(serial); + while (length) + { + rt_uint8_t ch; + if (!rt_bypass_getchar(serial, &ch)) + break; + + *data = ch & 0xff; + data++; length--; + } + + return size - length; + } +#endif /* read from software FIFO */ while (length) { @@ -1413,10 +1431,31 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) ch = serial->ops->getc(serial); if (ch == -1) break; - /* disable interrupt */ - level = rt_spin_lock_irqsave(&(serial->spinlock)); +#ifdef RT_USING_SERIAL_BYPASS + if (serial->bypass && serial->bypass->upper_h && (serial->bypass->upper_h->head.next != &serial->bypass->upper_h->head)) + { + rt_bool_t skip = RT_FALSE; + char buf = (char)ch; + int ret; + rt_list_t* node = serial->bypass->upper_h->head.next; + do { + struct rt_serial_bypass_func* bypass_run = rt_container_of(node, struct rt_serial_bypass_func, node); + ret = bypass_run->bypass(serial, buf, bypass_run->data); + if (!ret) + { + skip = RT_TRUE; + break; + } + node = node->next; + } while (node != &serial->bypass->upper_h->head); + if (skip) + continue; + } + +#endif + level = rt_spin_lock_irqsave(&(serial->spinlock)); rx_fifo->buffer[rx_fifo->put_index] = ch; rx_fifo->put_index += 1; if (rx_fifo->put_index >= serial->config.bufsz) rx_fifo->put_index = 0; @@ -1435,17 +1474,12 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) rt_spin_unlock_irqrestore(&(serial->spinlock), level); } - /** - * Invoke callback. - * First try notify if any, and if notify is existed, rx_indicate() - * is not callback. This separate the priority and makes the reuse - * of same serial device reasonable for RT console. - */ - if (serial->rx_notify.notify) - { - serial->rx_notify.notify(serial->rx_notify.dev); - } - else if (serial->parent.rx_indicate != RT_NULL) +#ifdef RT_USING_SERIAL_BYPASS + if (serial->bypass && serial->bypass->lower_h) + rt_workqueue_dowork(serial->bypass->lower_workq, &serial->bypass->work); +#endif + + if (serial->parent.rx_indicate != RT_NULL) { rt_size_t rx_length; diff --git a/rt-thread/components/drivers/serial/serial_v2.c b/rt-thread/components/drivers/serial/dev_serial_v2.c similarity index 99% rename from rt-thread/components/drivers/serial/serial_v2.c rename to rt-thread/components/drivers/serial/dev_serial_v2.c index 111bfbc..18f9c89 100644 --- a/rt-thread/components/drivers/serial/serial_v2.c +++ b/rt-thread/components/drivers/serial/dev_serial_v2.c @@ -599,7 +599,7 @@ static rt_ssize_t _serial_fifo_tx_nonblocking(struct rt_device *dev, rt_hw_interrupt_enable(level); rt_uint8_t *put_ptr = RT_NULL; - /* Get the linear length buffer from rinbuffer */ + /* Get the linear length buffer from ringbuffer */ tx_fifo->put_size = rt_serial_get_linear_buffer(&(tx_fifo->rb), &put_ptr); /* Call the transmit interface for transmission */ serial->ops->transmit(serial, @@ -1624,7 +1624,7 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) tx_fifo->activated = RT_TRUE; rt_uint8_t *put_ptr = RT_NULL; - /* Get the linear length buffer from rinbuffer */ + /* Get the linear length buffer from ringbuffer */ tx_fifo->put_size = rt_serial_get_linear_buffer(&(tx_fifo->rb), &put_ptr); /* Call the transmit interface for transmission again */ serial->ops->transmit(serial, diff --git a/rt-thread/components/drivers/serial/serial_tty.c b/rt-thread/components/drivers/serial/serial_tty.c index bbf61f8..b5c4549 100644 --- a/rt-thread/components/drivers/serial/serial_tty.c +++ b/rt-thread/components/drivers/serial/serial_tty.c @@ -114,59 +114,24 @@ static void _setup_debug_rxind_hook(void) #endif /* LWP_DEBUG_INIT */ -static void _tty_rx_notify(struct rt_device *device) +static rt_err_t _serial_ty_bypass(struct rt_serial_device* serial, char ch,void *data) { lwp_tty_t tp; - struct serial_tty_context *softc; - - tp = rt_container_of(device, struct lwp_tty, parent); - RT_ASSERT(tp); - - softc = tty_softc(tp); - - if (_ttyworkq) - rt_workqueue_submit_work(_ttyworkq, &softc->work, 0); -} - -static void _tty_rx_worker(struct rt_work *work, void *data) -{ - char input; - rt_ssize_t readbytes; - lwp_tty_t tp = data; - struct serial_tty_context *softc; - struct rt_serial_device *serial; + tp = (lwp_tty_t)data; tty_lock(tp); - - while (1) - { - softc = tty_softc(tp); - serial = softc->parent; - readbytes = rt_device_read(&serial->parent, -1, &input, 1); - if (readbytes != 1) - { - break; - } - - ttydisc_rint(tp, input, 0); - } - + ttydisc_rint(tp, ch, 0); ttydisc_rint_done(tp); tty_unlock(tp); + + return RT_EOK; + } -rt_inline void _setup_serial(struct rt_serial_device *serial, lwp_tty_t tp, +rt_inline void _setup_serial(struct rt_serial_device* serial, lwp_tty_t tp, struct serial_tty_context *softc) { - struct rt_device_notify notify; - - softc->backup_notify = serial->rx_notify; - notify.dev = &tp->parent; - notify.notify = _tty_rx_notify; - - rt_device_init(&serial->parent); - - rt_device_control(&serial->parent, RT_DEVICE_CTRL_NOTIFY_SET, ¬ify); + rt_bypass_lower_register(serial, "tty", RT_BYPASS_PROTECT_LEVEL_1, _serial_ty_bypass, (void *)tp); } rt_inline void _restore_serial(struct rt_serial_device *serial, lwp_tty_t tp, @@ -267,7 +232,7 @@ static void serial_tty_close(struct lwp_tty *tp) LOG_D("%s", __func__); - _restore_serial(serial, tp, softc); + rt_bypass_lower_unregister(serial, RT_BYPASS_PROTECT_LEVEL_1); rt_device_close(&serial->parent); } @@ -345,7 +310,6 @@ rt_err_t rt_hw_serial_register_tty(struct rt_serial_device *serial) { _serial_tty_set_speed(tty); rc = lwp_tty_register(tty, dev_name); - rt_work_init(&softc->work, _tty_rx_worker, tty); if (rc != RT_EOK) { diff --git a/rt-thread/components/drivers/smp_call/SConscript b/rt-thread/components/drivers/smp_call/SConscript new file mode 100644 index 0000000..2f4fe0b --- /dev/null +++ b/rt-thread/components/drivers/smp_call/SConscript @@ -0,0 +1,10 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +if GetDepend("RT_USING_SMP"): + src += Glob('*.c') +CPPPATH = [cwd] +group = DefineGroup('smp', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/smp_call/smp_call.c b/rt-thread/components/drivers/smp_call/smp_call.c new file mode 100644 index 0000000..6c65e82 --- /dev/null +++ b/rt-thread/components/drivers/smp_call/smp_call.c @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024/9/12 zhujiale the first version + * 2024/10/24 Shell added non-blocking IPI calling method; + * fixup data racing + */ + +#include "smp_call.h" + +#define DBG_TAG "SMP" +#define DBG_LVL DBG_INFO +#include + +static struct smp_data +{ + /* call request data to each cores */ + struct rt_smp_call_req call_req_cores[RT_CPUS_NR]; + + /* call queue of this core */ + rt_ll_slist_t call_queue; +} _smp_data_cores[RT_CPUS_NR]; + +#define _CALL_REQ_USAGE_FREED 0 +#define _CALL_REQ_USAGE_BUSY 1 +static void _call_req_take(struct rt_smp_call_req *req) +{ + rt_base_t exp; + do + { + exp = _CALL_REQ_USAGE_FREED; + } + while (!rt_atomic_compare_exchange_strong(&req->event.typed.usage_tracer, &exp, _CALL_REQ_USAGE_BUSY)); +} + +static void _call_req_release(struct rt_smp_call_req *req) +{ + rt_atomic_store(&req->event.typed.usage_tracer, _CALL_REQ_USAGE_FREED); +} + +void rt_smp_request_wait_freed(struct rt_smp_call_req *req) +{ + rt_base_t usage_tracer; + + RT_DEBUG_IN_THREAD_CONTEXT; + + usage_tracer = rt_atomic_load(&req->event.typed.usage_tracer); + while (usage_tracer != _CALL_REQ_USAGE_FREED) + { + rt_thread_yield(); + usage_tracer = rt_atomic_load(&req->event.typed.usage_tracer); + } +} + +static void _mask_out_cpu(struct rt_smp_event *event, int oncpu) +{ + rt_base_t new_mask, old_mask; + rt_atomic_t *maskp = event->typed.calling_cpu_mask; + do + { + old_mask = rt_atomic_load(maskp); + new_mask = old_mask & ~(1ul << oncpu); + } while (!rt_atomic_compare_exchange_strong(maskp, &old_mask, new_mask)); +} + +static void _do_glob_request(struct rt_smp_call_req *req_global, + struct rt_smp_call_req *req_local) +{ + struct rt_smp_event *event; + + /* release the global request data */ + rt_memcpy(req_local, req_global, sizeof(struct rt_smp_call_req)); + rt_hw_spin_unlock(&req_global->freed_lock); + + event = &req_local->event; + RT_ASSERT(!!event->func); + event->func(event->data); + + return ; +} + +static void _do_request(struct rt_smp_call_req *req) +{ + struct rt_smp_event *event; + + event = &req->event; + RT_ASSERT(!!event->func); + event->func(event->data); + + _call_req_release(req); + return ; +} + +static rt_err_t _smp_call_handler(struct rt_smp_call_req *req, int oncpu) +{ + switch (req->event.event_id) + { + case SMP_CALL_EVENT_GLOB_SYNC: + { + struct rt_smp_call_req req_local; + _do_glob_request(req, &req_local); + _mask_out_cpu(&req_local.event, oncpu); + break; + } + case SMP_CALL_EVENT_GLOB_ASYNC: + { + struct rt_smp_call_req req_local; + _do_glob_request(req, &req_local); + break; + } + case SMP_CALL_EVENT_REQUEST: + { + _do_request(req); + break; + } + default: + LOG_E("error event id\n"); + return -RT_ERROR; + } + return RT_EOK; +} + +void rt_smp_call_ipi_handler(int vector, void *param) +{ + int oncpu = rt_hw_cpu_id(); + struct rt_smp_call_req *request; + + RT_ASSERT(rt_interrupt_get_nest()); + + while (1) + { + rt_ll_slist_t *node = rt_ll_slist_dequeue(&_smp_data_cores[oncpu].call_queue); + if (node) + { + request = rt_list_entry(node, struct rt_smp_call_req, slist_node); + + _smp_call_handler(request, oncpu); + } + else + { + break; + } + } +} + +static void _smp_call_remote_request(int callcpu, rt_smp_call_cb_t func, + void *data, rt_uint8_t flags, + struct rt_smp_call_req *call_req) +{ + rt_base_t cpu_mask = 1ul << callcpu; + + _call_req_take(call_req); + + rt_ll_slist_enqueue(&_smp_data_cores[callcpu].call_queue, &call_req->slist_node); + + rt_hw_ipi_send(RT_SMP_CALL_IPI, cpu_mask); +} + +/** + * @brief SMP call request with user provided @call_req. Compare to + * rt_smp_call_func* family, you can call it in ISR or IRQ-masked + * environment. + * + * @param callcpu the logical core id of the target + * @param flags control flags of your request + * @param call_req the pre-initialized request data + * @return rt_err_t RT_EOK on succeed, otherwise the errno to failure + */ +rt_err_t rt_smp_call_request(int callcpu, rt_uint8_t flags, struct rt_smp_call_req *call_req) +{ + rt_ubase_t clvl; + int oncpu; + + if (rt_atomic_load(&call_req->event.typed.usage_tracer) == + _CALL_REQ_USAGE_BUSY) + { + return -RT_EBUSY; + } + + if (flags & SMP_CALL_WAIT_ALL) + { + return -RT_EINVAL; + } + + clvl = rt_enter_critical(); + oncpu = rt_hw_cpu_id(); + + if (oncpu == callcpu && !(flags & SMP_CALL_NO_LOCAL)) + { + rt_ubase_t level; + + /* handle IPI on irq-masked environment */ + level = rt_hw_local_irq_disable(); + call_req->event.func(call_req->event.data); + rt_hw_local_irq_enable(level); + } + else if (callcpu < RT_CPUS_NR) + { + _smp_call_remote_request(callcpu, call_req->event.func, call_req->event.data, flags, call_req); + } + + rt_exit_critical_safe(clvl); + + return RT_EOK; +} + +void rt_smp_call_req_init(struct rt_smp_call_req *call_req, + rt_smp_call_cb_t func, void *data) +{ + call_req->event.typed.usage_tracer = 0; + call_req->event.data = data; + call_req->event.func = func; + call_req->event.event_id = SMP_CALL_EVENT_REQUEST; +} + +static void _smp_call_func_cond(int oncpu, rt_ubase_t cpu_mask, + rt_smp_call_cb_t func, void *data, + rt_uint8_t flags, rt_smp_cond_t cond) +{ + rt_ubase_t tmp_mask; + rt_bool_t sync_call = RT_FALSE; + rt_ubase_t oncpu_mask = 1 << oncpu; + rt_atomic_t calling_cpu_mask, *maskp; + int tmp_id = 0, rcpu_cnt = 0, event_id, call_local; + + if (!(flags & SMP_CALL_NO_LOCAL) && (oncpu_mask & cpu_mask)) + { + call_local = RT_TRUE; + cpu_mask = cpu_mask & (~oncpu_mask); + } + else + { + call_local = RT_FALSE; + } + + if (cpu_mask) + { + tmp_mask = cpu_mask; + + if (flags & SMP_CALL_WAIT_ALL) + { + sync_call = RT_TRUE; + maskp = &calling_cpu_mask; + event_id = SMP_CALL_EVENT_GLOB_SYNC; + rt_atomic_store(maskp, cpu_mask); + } + else + { + event_id = SMP_CALL_EVENT_GLOB_ASYNC; + maskp = RT_NULL; + } + + while (tmp_mask) + { + struct rt_smp_call_req *call_req; + struct rt_smp_event *event; + int lz_bit = __rt_ffsl(tmp_mask); + + tmp_id = lz_bit - 1; + tmp_mask &= ~(1ul << tmp_id); + + if (cond && !cond(tmp_id, data)) + { + cpu_mask &= ~(1ul << tmp_id); + continue; + } + + /* need to wait one more */ + rcpu_cnt++; + + call_req = &_smp_data_cores[oncpu].call_req_cores[tmp_id]; + + /* very careful here, spinning wait on previous occupation */ + rt_hw_spin_lock(&call_req->freed_lock); + + event = &call_req->event; + event->event_id = event_id; + event->func = func; + event->data = data; + event->typed.calling_cpu_mask = maskp; + + rt_ll_slist_enqueue(&_smp_data_cores[tmp_id].call_queue, &call_req->slist_node); + } + + if (cpu_mask) + { + RT_ASSERT(rcpu_cnt); + + rt_hw_ipi_send(RT_SMP_CALL_IPI, cpu_mask); + } + } + + if (call_local && (!cond || cond(tmp_id, data))) + { + rt_ubase_t level; + + /* callback on local with sims ISR */ + level = rt_hw_local_irq_disable(); + func(data); + rt_hw_local_irq_enable(level); + } + + if (sync_call && rcpu_cnt) + { + while (rt_atomic_load(maskp) & cpu_mask) + ; + } +} + +/** + * @brief call function on specified CPU , + * + * @param cpu_mask cpu mask for call + * @param func the function pointer + * @param data the data pointer + * @param flag call flag if you set SMP_CALL_WAIT_ALL + * then it will wait all cpu call finish and return + * else it will call function on specified CPU and return immediately + * @param cond the condition function pointer,if you set it then it will call function only when cond return true + */ +void rt_smp_call_func_cond(rt_ubase_t cpu_mask, rt_smp_call_cb_t func, void *data, rt_uint8_t flag, rt_smp_cond_t cond) +{ + int oncpu; + rt_ubase_t clvl; + + RT_ASSERT(!rt_hw_interrupt_is_disabled()); + + clvl = rt_enter_critical(); + oncpu = rt_hw_cpu_id(); + + if (cpu_mask <= RT_ALL_CPU) + { + _smp_call_func_cond(oncpu, cpu_mask, func, data, flag, cond); + } + + rt_exit_critical_safe(clvl); +} + +void rt_smp_call_each_cpu(rt_smp_call_cb_t func, void *data, rt_uint8_t flag) +{ + rt_smp_call_func_cond(RT_ALL_CPU, func, data, flag, RT_NULL); +} + +void rt_smp_call_each_cpu_cond(rt_smp_call_cb_t func, void *data, rt_uint8_t flag, rt_smp_cond_t cond_func) +{ + rt_smp_call_func_cond(RT_ALL_CPU, func, data, flag, cond_func); +} + +void rt_smp_call_cpu_mask(rt_ubase_t cpu_mask, rt_smp_call_cb_t func, void *data, rt_uint8_t flag) +{ + rt_smp_call_func_cond(cpu_mask, func, data, flag, RT_NULL); +} + +void rt_smp_call_cpu_mask_cond(rt_ubase_t cpu_mask, rt_smp_call_cb_t func, void *data, rt_uint8_t flag, rt_smp_cond_t cond_func) +{ + rt_smp_call_func_cond(cpu_mask, func, data, flag, cond_func); +} + +void rt_smp_call_init(void) +{ + rt_memset(&_smp_data_cores, 0, sizeof(_smp_data_cores)); + + for (int i = 0; i < RT_CPUS_NR; i++) + { + for (int j = 0; j < RT_CPUS_NR; j++) + { + rt_hw_spin_lock_init(&_smp_data_cores[i].call_req_cores[j].freed_lock); + } + } +} diff --git a/rt-thread/components/drivers/smp_call/smp_call.h b/rt-thread/components/drivers/smp_call/smp_call.h new file mode 100644 index 0000000..0ff9dd3 --- /dev/null +++ b/rt-thread/components/drivers/smp_call/smp_call.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024/9/12 zhujiale the first version + * 2024/10/24 Shell added non-blocking IPI calling method + */ + +#ifndef __SMP_IPI_H__ +#define __SMP_IPI_H__ +#include + +#ifdef RT_USING_SMP + +/* callback of smp call */ +typedef void (*rt_smp_call_cb_t)(void *data); +typedef rt_bool_t (*rt_smp_cond_t)(int cpu, void *info); + +#define SMP_CALL_EVENT_GLOB_ASYNC 0x1 +#define SMP_CALL_EVENT_GLOB_SYNC 0x2 +#define SMP_CALL_EVENT_REQUEST 0x4 + +#define SMP_CALL_WAIT_ALL (1ul << 0) +#define SMP_CALL_NO_LOCAL (1ul << 1) +#define SMP_CALL_SIGNAL (1ul << 2) + +#define RT_ALL_CPU ((1 << RT_CPUS_NR) - 1) +struct rt_smp_event +{ + int event_id; + void *data; + rt_smp_call_cb_t func; + + union + { + rt_atomic_t *calling_cpu_mask; + rt_atomic_t usage_tracer; + } typed; +}; + +struct rt_smp_call_req +{ + /* handle the busy status synchronization */ + rt_hw_spinlock_t freed_lock; + struct rt_smp_event event; + rt_ll_slist_t slist_node; +}; + +void rt_smp_call_ipi_handler(int vector, void *param); +void rt_smp_call_each_cpu(rt_smp_call_cb_t func, void *data, rt_uint8_t flags); +void rt_smp_call_each_cpu_cond(rt_smp_call_cb_t func, void *data, rt_uint8_t flag, rt_smp_cond_t cond_func); +void rt_smp_call_cpu_mask(rt_ubase_t cpu_mask, rt_smp_call_cb_t func, void *data, rt_uint8_t flags); +void rt_smp_call_cpu_mask_cond(rt_ubase_t cpu_mask, rt_smp_call_cb_t func, void *data, rt_uint8_t flag, rt_smp_cond_t cond_func); +void rt_smp_call_init(void); + +rt_err_t rt_smp_call_request(int callcpu, rt_uint8_t flags, struct rt_smp_call_req *call_req); +void rt_smp_call_req_init(struct rt_smp_call_req *call_req, + rt_smp_call_cb_t func, void *data); +void rt_smp_request_wait_freed(struct rt_smp_call_req *req); + +#define rt_smp_for_each_cpu(_iter) for (_iter = 0; (_iter) < RT_CPUS_NR; (_iter)++) +rt_inline size_t rt_smp_get_next_remote(size_t iter, size_t cpuid) +{ + iter++; + return iter == cpuid ? iter + 1 : iter; +} +#define rt_smp_for_each_remote_cpu(_iter, _cpuid) for (_iter = rt_smp_get_next_remote(-1, _cpuid); (_iter) < RT_CPUS_NR; _iter=rt_smp_get_next_remote(_iter, _cpuid)) + +#endif // RT_USING_SMP + +#endif // __SMP_IPI_H__ diff --git a/rt-thread/components/drivers/spi/SConscript b/rt-thread/components/drivers/spi/SConscript index cea447f..6897dab 100644 --- a/rt-thread/components/drivers/spi/SConscript +++ b/rt-thread/components/drivers/spi/SConscript @@ -3,29 +3,29 @@ from gcc import * import rtconfig cwd = GetCurrentDir() -src = ['spi_core.c', 'spi_dev.c'] +src = ['dev_spi_core.c', 'dev_spi.c'] CPPPATH = [cwd, cwd + '/../include'] LOCAL_CFLAGS = '' if GetDepend('RT_USING_SPI_BITOPS'): - src += ['spi-bit-ops.c'] + src += ['dev_spi_bit_ops.c'] if GetDepend('RT_USING_QSPI'): - src += ['qspi_core.c'] + src += ['dev_qspi_core.c'] src_device = [] if GetDepend('RT_USING_SPI_WIFI'): - src_device += ['spi_wifi_rw009.c'] + src_device += ['dev_spi_wifi_rw009.c'] if GetDepend('RT_USING_ENC28J60'): src_device += ['enc28j60.c'] if GetDepend('RT_USING_SPI_MSD'): - src_device += ['spi_msd.c'] + src_device += ['dev_spi_msd.c'] if GetDepend('RT_USING_SFUD'): - src_device += ['spi_flash_sfud.c', 'sfud/src/sfud.c'] + src_device += ['dev_spi_flash_sfud.c', 'sfud/src/sfud.c'] CPPPATH += [cwd + '/sfud/inc'] if GetDepend('RT_SFUD_USING_SFDP'): src_device += ['sfud/src/sfud_sfdp.c'] @@ -35,6 +35,9 @@ if GetDepend('RT_USING_SFUD'): elif rtconfig.PLATFORM in ['armcc']: LOCAL_CFLAGS += ' --c99' +if GetDepend('RT_USING_DM'): + src += ['dev_spi_dm.c', 'dev_spi_bus.c'] + src += src_device group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SPI'], CPPPATH = CPPPATH, LOCAL_CFLAGS = LOCAL_CFLAGS) diff --git a/rt-thread/components/drivers/spi/qspi_core.c b/rt-thread/components/drivers/spi/dev_qspi_core.c similarity index 93% rename from rt-thread/components/drivers/spi/qspi_core.c rename to rt-thread/components/drivers/spi/dev_qspi_core.c index cd40c51..9dd2c5f 100644 --- a/rt-thread/components/drivers/spi/qspi_core.c +++ b/rt-thread/components/drivers/spi/dev_qspi_core.c @@ -8,7 +8,7 @@ * 2018-11-16 zylx first version. */ -#include +#include "drivers/dev_spi.h" rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configuration *cfg) { @@ -18,10 +18,23 @@ rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configu /* reset the CS pin */ if (device->parent.cs_pin != PIN_NONE) { - if (cfg->parent.mode & RT_SPI_CS_HIGH) - rt_pin_write(device->parent.cs_pin, PIN_LOW); + rt_err_t result = rt_mutex_take(&(device->parent.bus->lock), RT_WAITING_FOREVER); + if (result == RT_EOK) + { + if (cfg->parent.mode & RT_SPI_CS_HIGH) + { + rt_pin_write(device->parent.cs_pin, PIN_LOW); + } + else + { + rt_pin_write(device->parent.cs_pin, PIN_HIGH); + } + rt_mutex_release(&(device->parent.bus->lock)); + } else - rt_pin_write(device->parent.cs_pin, PIN_HIGH); + { + return result; + } } /* If the configurations are the same, we don't need to set again. */ diff --git a/rt-thread/components/drivers/spi/spi_dev.c b/rt-thread/components/drivers/spi/dev_spi.c similarity index 69% rename from rt-thread/components/drivers/spi/spi_dev.c rename to rt-thread/components/drivers/spi/dev_spi.c index 963b70c..3f55fd0 100644 --- a/rt-thread/components/drivers/spi/spi_dev.c +++ b/rt-thread/components/drivers/spi/dev_spi.c @@ -8,7 +8,15 @@ */ #include -#include +#include "drivers/dev_spi.h" + +#define DBG_TAG "spi.dev" +#define DBG_LVL DBG_INFO +#include + +#ifdef RT_USING_DM +#include "dev_spi_dm.h" +#endif /* SPI bus device interface, compatible with RT-Thread 0.3.x/1.0.x */ static rt_ssize_t _spi_bus_device_read(rt_device_t dev, @@ -155,3 +163,66 @@ rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name) /* register to device manager */ return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR); } + +#ifdef RT_USING_DM +static rt_err_t spidev_probe(struct rt_spi_device *spi_dev) +{ + const char *bus_name; + struct rt_device *dev = &spi_dev->parent; + + if (spi_dev->parent.ofw_node) + { + if (rt_dm_dev_prop_index_of_string(dev, "compatible", "spidev") >= 0) + { + LOG_E("spidev is not supported in OFW"); + + return -RT_EINVAL; + } + } + + bus_name = rt_dm_dev_get_name(&spi_dev->bus->parent); + rt_dm_dev_set_name(dev, "%s_%d", bus_name, spi_dev->chip_select); + + return RT_EOK; +} + +static const struct rt_spi_device_id spidev_ids[] = +{ + { .name = "dh2228fv" }, + { .name = "ltc2488" }, + { .name = "sx1301" }, + { .name = "bk4" }, + { .name = "dhcom-board" }, + { .name = "m53cpld" }, + { .name = "spi-petra" }, + { .name = "spi-authenta" }, + { .name = "em3581" }, + { .name = "si3210" }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id spidev_ofw_ids[] = +{ + { .compatible = "cisco,spi-petra" }, + { .compatible = "dh,dhcom-board" }, + { .compatible = "lineartechnology,ltc2488" }, + { .compatible = "lwn,bk4" }, + { .compatible = "menlo,m53cpld" }, + { .compatible = "micron,spi-authenta" }, + { .compatible = "rohm,dh2228fv" }, + { .compatible = "semtech,sx1301" }, + { .compatible = "silabs,em3581" }, + { .compatible = "silabs,si3210" }, + { .compatible = "rockchip,spidev" }, + { /* sentinel */ }, +}; + +static struct rt_spi_driver spidev_driver = +{ + .ids = spidev_ids, + .ofw_ids = spidev_ofw_ids, + + .probe = spidev_probe, +}; +RT_SPI_DRIVER_EXPORT(spidev_driver); +#endif /* RT_USING_DM */ diff --git a/rt-thread/components/drivers/spi/spi-bit-ops.c b/rt-thread/components/drivers/spi/dev_spi_bit_ops.c similarity index 90% rename from rt-thread/components/drivers/spi/spi-bit-ops.c rename to rt-thread/components/drivers/spi/dev_spi_bit_ops.c index f551741..d2a26a7 100644 --- a/rt-thread/components/drivers/spi/spi-bit-ops.c +++ b/rt-thread/components/drivers/spi/dev_spi_bit_ops.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -8,7 +8,7 @@ * 2021-10-11 kyle first version */ -#include +#include #include #define DBG_TAG "SPI" @@ -423,6 +423,7 @@ rt_ssize_t spi_bit_xfer(struct rt_spi_device *device, struct rt_spi_message *mes struct rt_spi_bit_ops *ops = obj->ops; struct rt_spi_configuration *config = &obj->config; rt_base_t cs_pin = device->cs_pin; + rt_ssize_t length = 0; RT_ASSERT(device != NULL); RT_ASSERT(message != NULL); @@ -443,10 +444,17 @@ rt_ssize_t spi_bit_xfer(struct rt_spi_device *device, struct rt_spi_message *mes #endif /* take CS */ - if (message->cs_take && (cs_pin != PIN_NONE)) + if (message->cs_take && !(device->config.mode & RT_SPI_NO_CS) && (cs_pin != PIN_NONE)) { LOG_I("spi take cs\n"); - rt_pin_write(cs_pin, PIN_LOW); + if (device->config.mode & RT_SPI_CS_HIGH) + { + rt_pin_write(cs_pin, PIN_HIGH); + } + else + { + rt_pin_write(cs_pin, PIN_LOW); + } spi_delay(ops); /* spi phase */ @@ -461,50 +469,41 @@ rt_ssize_t spi_bit_xfer(struct rt_spi_device *device, struct rt_spi_message *mes { if (config->data_width <= 8) { - spi_xfer_3line_data8(ops, - config, - message->send_buf, - message->recv_buf, - message->length); + length = spi_xfer_3line_data8(ops, config, message->send_buf, message->recv_buf, message->length); } else if (config->data_width <= 16) { - spi_xfer_3line_data16(ops, - config, - message->send_buf, - message->recv_buf, - message->length); + length = spi_xfer_3line_data16(ops, config, message->send_buf, message->recv_buf, message->length); } } else { if (config->data_width <= 8) { - spi_xfer_4line_data8(ops, - config, - message->send_buf, - message->recv_buf, - message->length); + length = spi_xfer_4line_data8(ops, config, message->send_buf, message->recv_buf, message->length); } else if (config->data_width <= 16) { - spi_xfer_4line_data16(ops, - config, - message->send_buf, - message->recv_buf, - message->length); + length = spi_xfer_4line_data16(ops, config, message->send_buf, message->recv_buf, message->length); } } /* release CS */ - if (message->cs_release && (cs_pin != PIN_NONE)) + if (message->cs_take && !(device->config.mode & RT_SPI_NO_CS) && (cs_pin != PIN_NONE)) { spi_delay(ops); - rt_pin_write(cs_pin, PIN_HIGH); + if (device->config.mode & RT_SPI_CS_HIGH) + { + rt_pin_write(cs_pin, PIN_LOW); + } + else + { + rt_pin_write(cs_pin, PIN_HIGH); + } LOG_I("spi release cs\n"); } - return message->length; + return length; } static const struct rt_spi_ops spi_bit_bus_ops = @@ -522,9 +521,5 @@ rt_err_t rt_spi_bit_add_bus(struct rt_spi_bit_obj *obj, obj->config.max_hz = 1 * 1000 * 1000; obj->config.mode = RT_SPI_MASTER | RT_SPI_MSB | RT_SPI_MODE_0; - /* idle status */ - if (obj->config.mode & RT_SPI_CPOL) SCLK_H(ops); - else SCLK_L(ops); - return rt_spi_bus_register(&obj->bus, bus_name, &spi_bit_bus_ops); } diff --git a/rt-thread/components/drivers/spi/spi-bit-ops.h b/rt-thread/components/drivers/spi/dev_spi_bit_ops.h similarity index 95% rename from rt-thread/components/drivers/spi/spi-bit-ops.h rename to rt-thread/components/drivers/spi/dev_spi_bit_ops.h index d1e83cd..cf2335e 100644 --- a/rt-thread/components/drivers/spi/spi-bit-ops.h +++ b/rt-thread/components/drivers/spi/dev_spi_bit_ops.h @@ -9,8 +9,8 @@ * 2022-6-14 solar Remove the const attribute of private data in ops */ -#ifndef __SPI_BIT_OPS_H__ -#define __SPI_BIT_OPS_H__ +#ifndef __DEV_SPI_BIT_OPS_H__ +#define __DEV_SPI_BIT_OPS_H__ #include diff --git a/rt-thread/components/drivers/spi/dev_spi_bus.c b/rt-thread/components/drivers/spi/dev_spi_bus.c new file mode 100644 index 0000000..34aa298 --- /dev/null +++ b/rt-thread/components/drivers/spi/dev_spi_bus.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "dev_spi_dm.h" + +#define DBG_TAG "spi.bus" +#define DBG_LVL DBG_INFO +#include + +extern rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name); + +static struct rt_bus spi_bus; + +void spi_bus_scan_devices(struct rt_spi_bus *bus) +{ +#ifdef RT_USING_OFW + if (bus->parent.ofw_node) + { + struct rt_ofw_node *np = bus->parent.ofw_node, *spi_dev_np; + + rt_ofw_foreach_available_child_node(np, spi_dev_np) + { + rt_uint64_t reg_offset; + struct rt_spi_device *spi_dev; + + if (!rt_ofw_prop_read_bool(spi_dev_np, "compatible")) + { + continue; + } + + spi_dev = rt_calloc(1, sizeof(*spi_dev)); + + if (!spi_dev) + { + rt_ofw_node_put(spi_dev_np); + LOG_E("Not memory to create spi device: %s", + rt_ofw_node_full_name(spi_dev_np)); + + return; + } + + rt_ofw_get_address(spi_dev_np, 0, ®_offset, RT_NULL); + + spi_dev->parent.ofw_node = spi_dev_np; + spi_dev->parent.type = RT_Device_Class_Unknown; + spi_dev->name = rt_ofw_node_name(spi_dev_np); + spi_dev->bus = bus; + + rt_dm_dev_set_name(&spi_dev->parent, rt_ofw_node_full_name(spi_dev_np)); + + if (spi_device_ofw_parse(spi_dev)) + { + continue; + } + + rt_spi_device_register(spi_dev); + } + } +#endif /* RT_USING_OFW */ +} + +rt_err_t rt_spi_driver_register(struct rt_spi_driver *driver) +{ + RT_ASSERT(driver != RT_NULL); + + driver->parent.bus = &spi_bus; + + return rt_driver_register(&driver->parent); +} + +rt_err_t rt_spi_device_register(struct rt_spi_device *device) +{ + RT_ASSERT(device != RT_NULL); + + return rt_bus_add_device(&spi_bus, &device->parent); +} + +static rt_bool_t spi_match(rt_driver_t drv, rt_device_t dev) +{ + const struct rt_spi_device_id *id; + struct rt_spi_driver *driver = rt_container_of(drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if ((id = driver->ids)) + { + for (; id->name[0]; ++id) + { + if (!rt_strcmp(id->name, device->name)) + { + device->id = id; + device->ofw_id = RT_NULL; + + return RT_TRUE; + } + } + } + +#ifdef RT_USING_OFW + device->ofw_id = rt_ofw_node_match(device->parent.ofw_node, driver->ofw_ids); + + if (device->ofw_id) + { + device->id = RT_NULL; + + return RT_TRUE; + } +#endif + + return RT_FALSE; +} + +static rt_err_t spi_probe(rt_device_t dev) +{ + rt_err_t err; + struct rt_spi_bus *bus; + struct rt_spi_driver *driver = rt_container_of(dev->drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if (!device->bus) + { + return -RT_EINVAL; + } + + err = driver->probe(device); + + if (err) + { + return err; + } + + bus = device->bus; + + if (bus->pins) + { + device->cs_pin = bus->pins[device->chip_select]; + + rt_pin_mode(device->cs_pin, PIN_MODE_OUTPUT); + } + else + { + device->cs_pin = PIN_NONE; + } + + /* Driver not register SPI device to system */ + if (device->parent.type == RT_Device_Class_Unknown) + { + rt_spidev_device_init(device, rt_dm_dev_get_name(&device->parent)); + } + + return err; +} + +static rt_err_t spi_remove(rt_device_t dev) +{ + struct rt_spi_driver *driver = rt_container_of(dev->drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if (driver && driver->remove) + { + driver->remove(device); + } + rt_free(device); + + return RT_EOK; +} + +static rt_err_t spi_shutdown(rt_device_t dev) +{ + struct rt_spi_driver *driver = rt_container_of(dev->drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if (driver && driver->shutdown) + { + driver->shutdown(device); + } + rt_free(device); + + return RT_EOK; +} + +static struct rt_bus spi_bus = +{ + .name = "spi", + .match = spi_match, + .probe = spi_probe, + .remove = spi_remove, + .shutdown = spi_shutdown, +}; + +static int spi_bus_init(void) +{ + rt_bus_register(&spi_bus); + + return 0; +} +INIT_CORE_EXPORT(spi_bus_init); diff --git a/rt-thread/components/drivers/spi/spi_core.c b/rt-thread/components/drivers/spi/dev_spi_core.c similarity index 89% rename from rt-thread/components/drivers/spi/spi_core.c rename to rt-thread/components/drivers/spi/dev_spi_core.c index d9d4221..d84aaac 100644 --- a/rt-thread/components/drivers/spi/spi_core.c +++ b/rt-thread/components/drivers/spi/dev_spi_core.c @@ -13,12 +13,16 @@ * 2012-09-28 aozima fixed rt_spi_release_bus assert error. */ -#include +#include "drivers/dev_spi.h" #define DBG_TAG "spi.core" #define DBG_LVL DBG_INFO #include +#ifdef RT_USING_DM +#include "dev_spi_dm.h" +#endif + extern rt_err_t rt_spi_bus_device_init(struct rt_spi_bus *bus, const char *name); extern rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name); @@ -41,6 +45,46 @@ rt_err_t rt_spi_bus_register(struct rt_spi_bus *bus, /* set bus mode */ bus->mode = RT_SPI_BUS_MODE_SPI; +#ifdef RT_USING_DM + if (!bus->slave) + { + int pin_count = rt_pin_get_named_pin_count(&bus->parent, "cs"); + + if (pin_count > 0) + { + pin_count = rt_max_t(int, pin_count, bus->num_chipselect); + bus->pins = rt_malloc(sizeof(bus->pins[0]) * pin_count); + + if (!bus->pins) + { + rt_device_unregister(&bus->parent); + return -RT_ENOMEM; + } + + for (int i = 0; i < pin_count; ++i) + { + bus->pins[i] = rt_pin_get_named_pin(&bus->parent, "cs", i, + RT_NULL, RT_NULL); + } + } + else if (pin_count == 0) + { + bus->pins = RT_NULL; + } + else + { + result = pin_count; + + LOG_E("CS PIN find error = %s", rt_strerror(result)); + + rt_device_unregister(&bus->parent); + return result; + } + } + + spi_bus_scan_devices(bus); +#endif + return RT_EOK; } @@ -106,6 +150,13 @@ rt_err_t rt_spi_bus_configure(struct rt_spi_device *device) LOG_E("SPI device %s configuration failed", device->parent.parent.name); } } + else + { + /* RT_EBUSY is not an error condition and + * the configuration will take effect once the device has the bus + */ + result = -RT_EBUSY; + } /* release lock */ rt_mutex_release(&(device->bus->lock)); @@ -128,10 +179,23 @@ rt_err_t rt_spi_configure(struct rt_spi_device *device, /* reset the CS pin */ if (device->cs_pin != PIN_NONE) { - if (cfg->mode & RT_SPI_CS_HIGH) - rt_pin_write(device->cs_pin, PIN_LOW); + rt_err_t result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER); + if (result == RT_EOK) + { + if (cfg->mode & RT_SPI_CS_HIGH) + { + rt_pin_write(device->cs_pin, PIN_LOW); + } + else + { + rt_pin_write(device->cs_pin, PIN_HIGH); + } + rt_mutex_release(&(device->bus->lock)); + } else - rt_pin_write(device->cs_pin, PIN_HIGH); + { + return result; + } } /* If the configurations are the same, we don't need to set again. */ diff --git a/rt-thread/components/drivers/spi/dev_spi_dm.c b/rt-thread/components/drivers/spi/dev_spi_dm.c new file mode 100644 index 0000000..063615c --- /dev/null +++ b/rt-thread/components/drivers/spi/dev_spi_dm.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "dev_spi_dm.h" + +#define DBG_TAG "spi.dm" +#define DBG_LVL DBG_INFO +#include + +#ifdef RT_USING_OFW +static void ofw_parse_delay(struct rt_ofw_node *np, struct rt_spi_delay *delay, + const char *prop) +{ + rt_uint32_t value; + + if (!rt_ofw_prop_read_u32(np, prop, &value)) + { + if (value > RT_UINT16_MAX) + { + delay->value = RT_DIV_ROUND_UP(value, 1000); + delay->unit = RT_SPI_DELAY_UNIT_USECS; + } + else + { + delay->value = value; + delay->unit = RT_SPI_DELAY_UNIT_NSECS; + } + } +} + +rt_err_t spi_device_ofw_parse(struct rt_spi_device *spi_dev) +{ + rt_err_t err; + rt_uint32_t value; + struct rt_spi_bus *spi_bus = spi_dev->bus; + struct rt_ofw_node *np = spi_dev->parent.ofw_node; + struct rt_spi_configuration *conf = &spi_dev->config; + + if (rt_ofw_prop_read_bool(np, "spi-cpha")) + { + conf->mode |= RT_SPI_CPHA; + } + if (rt_ofw_prop_read_bool(np, "spi-cpol")) + { + conf->mode |= RT_SPI_CPOL; + } + if (rt_ofw_prop_read_bool(np, "spi-3wire")) + { + conf->mode |= RT_SPI_3WIRE; + } + if (rt_ofw_prop_read_bool(np, "spi-lsb-first")) + { + conf->mode |= RT_SPI_LSB; + } + if (rt_ofw_prop_read_bool(np, "spi-cs-high")) + { + conf->mode |= RT_SPI_CS_HIGH; + } + + value = 1; + rt_ofw_prop_read_u32(np, "spi-tx-bus-width", &value); + conf->data_width_tx = value; + + value = 1; + rt_ofw_prop_read_u32(np, "spi-rx-bus-width", &value); + conf->data_width_rx = value; + + if (spi_bus->slave) + { + if (!rt_ofw_node_tag_equ(np, "slave")) + { + LOG_E("Invalid SPI device = %s", rt_ofw_node_full_name(np)); + + return -RT_EINVAL; + } + + return RT_EOK; + } + + if ((err = rt_ofw_prop_read_u32(np, "reg", &value))) + { + LOG_E("Find 'reg' failed"); + + return err; + } + spi_dev->chip_select = value; + + if (!rt_ofw_prop_read_u32(np, "spi-max-frequency", &value)) + { + conf->max_hz = value; + } + + ofw_parse_delay(np, &spi_dev->cs_setup, "spi-cs-setup-delay-ns"); + ofw_parse_delay(np, &spi_dev->cs_hold, "spi-cs-hold-delay-ns"); + ofw_parse_delay(np, &spi_dev->cs_inactive, "spi-cs-inactive-delay-ns"); + + return RT_EOK; +} +#endif /* RT_USING_OFW */ diff --git a/rt-thread/components/drivers/spi/dev_spi_dm.h b/rt-thread/components/drivers/spi/dev_spi_dm.h new file mode 100644 index 0000000..c124295 --- /dev/null +++ b/rt-thread/components/drivers/spi/dev_spi_dm.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __DEV_SPI_DM_H__ +#define __DEV_SPI_DM_H__ + +#include +#include +#include + +#ifdef RT_USING_OFW +rt_err_t spi_device_ofw_parse(struct rt_spi_device *spi_dev); +#else +rt_inline rt_err_t spi_device_ofw_parse(struct rt_spi_device *spi_dev) +{ + return RT_EOK; +} +#endif /* RT_USING_OFW */ + +void spi_bus_scan_devices(struct rt_spi_bus *bus); + +#endif /* __DEV_SPI_DM_H__ */ diff --git a/rt-thread/components/drivers/spi/spi_flash.h b/rt-thread/components/drivers/spi/dev_spi_flash.h similarity index 93% rename from rt-thread/components/drivers/spi/spi_flash.h rename to rt-thread/components/drivers/spi/dev_spi_flash.h index 2651c76..5ae54c4 100644 --- a/rt-thread/components/drivers/spi/spi_flash.h +++ b/rt-thread/components/drivers/spi/dev_spi_flash.h @@ -9,8 +9,8 @@ * 2020/1/7 redoc add include */ -#ifndef SPI_FLASH_H__ -#define SPI_FLASH_H__ +#ifndef __DEV_SPI_FLASH_H__ +#define __DEV_SPI_FLASH_H__ #include diff --git a/rt-thread/components/drivers/spi/spi_flash_sfud.c b/rt-thread/components/drivers/spi/dev_spi_flash_sfud.c similarity index 99% rename from rt-thread/components/drivers/spi/spi_flash_sfud.c rename to rt-thread/components/drivers/spi/dev_spi_flash_sfud.c index 761c9cc..e1c90db 100644 --- a/rt-thread/components/drivers/spi/spi_flash_sfud.c +++ b/rt-thread/components/drivers/spi/dev_spi_flash_sfud.c @@ -11,8 +11,8 @@ #include #include #include -#include "spi_flash.h" -#include "spi_flash_sfud.h" +#include "dev_spi_flash.h" +#include "dev_spi_flash_sfud.h" #ifdef RT_USING_SFUD diff --git a/rt-thread/components/drivers/spi/spi_flash_sfud.h b/rt-thread/components/drivers/spi/dev_spi_flash_sfud.h similarity index 88% rename from rt-thread/components/drivers/spi/spi_flash_sfud.h rename to rt-thread/components/drivers/spi/dev_spi_flash_sfud.h index 6099c98..5c3236f 100644 --- a/rt-thread/components/drivers/spi/spi_flash_sfud.h +++ b/rt-thread/components/drivers/spi/dev_spi_flash_sfud.h @@ -6,15 +6,20 @@ * Change Logs: * Date Author Notes * 2016-09-28 armink first version. + * 2024-10-24 yekai Add C++ support */ -#ifndef _SPI_FLASH_SFUD_H_ -#define _SPI_FLASH_SFUD_H_ +#ifndef __DEV_SPI_FLASH_SFUD_H__ +#define __DEV_SPI_FLASH_SFUD_H__ #include #include #include "./sfud/inc/sfud.h" -#include "spi_flash.h" +#include "dev_spi_flash.h" + +#ifdef __cplusplus +extern "C" { +#endif /** * Probe SPI flash by SFUD(Serial Flash Universal Driver) driver library and though SPI device. @@ -66,4 +71,8 @@ sfud_flash_t rt_sfud_flash_find(const char *spi_dev_name); */ sfud_flash_t rt_sfud_flash_find_by_dev_name(const char *flash_dev_name); -#endif /* _SPI_FLASH_SFUD_H_ */ +#ifdef __cplusplus +} +#endif + +#endif /* __DEV_SPI_FLASH_SFUD_H__ */ diff --git a/rt-thread/components/drivers/spi/spi_msd.c b/rt-thread/components/drivers/spi/dev_spi_msd.c similarity index 99% rename from rt-thread/components/drivers/spi/spi_msd.c rename to rt-thread/components/drivers/spi/dev_spi_msd.c index 0ca1bdf..8d81e34 100644 --- a/rt-thread/components/drivers/spi/spi_msd.c +++ b/rt-thread/components/drivers/spi/dev_spi_msd.c @@ -14,7 +14,7 @@ */ #include -#include "spi_msd.h" +#include "dev_spi_msd.h" //#define MSD_TRACE diff --git a/rt-thread/components/drivers/spi/spi_msd.h b/rt-thread/components/drivers/spi/dev_spi_msd.h similarity index 97% rename from rt-thread/components/drivers/spi/spi_msd.h rename to rt-thread/components/drivers/spi/dev_spi_msd.h index 2b66be7..7304b13 100644 --- a/rt-thread/components/drivers/spi/spi_msd.h +++ b/rt-thread/components/drivers/spi/dev_spi_msd.h @@ -8,12 +8,12 @@ * 2009-04-17 Bernard first version. */ -#ifndef SPI_MSD_H_INCLUDED -#define SPI_MSD_H_INCLUDED +#ifndef __DEV_SPI_MSD_H_INCLUDED__ +#define __DEV_SPI_MSD_H_INCLUDED__ #include #include -#include +#include "drivers/dev_spi.h" /* SD command (SPI mode) */ #define GO_IDLE_STATE 0 /* CMD0 R1 */ @@ -125,4 +125,4 @@ struct msd_device extern rt_err_t msd_init(const char * sd_device_name, const char * spi_device_name); -#endif // SPI_MSD_H_INCLUDED +#endif // __DEV_SPI_MSD_H_INCLUDED diff --git a/rt-thread/components/drivers/spi/spi_wifi_rw009.c b/rt-thread/components/drivers/spi/dev_spi_wifi_rw009.c similarity index 99% rename from rt-thread/components/drivers/spi/spi_wifi_rw009.c rename to rt-thread/components/drivers/spi/dev_spi_wifi_rw009.c index 44f70e2..b3bd807 100644 --- a/rt-thread/components/drivers/spi/spi_wifi_rw009.c +++ b/rt-thread/components/drivers/spi/dev_spi_wifi_rw009.c @@ -11,7 +11,7 @@ */ #include -#include +#include "drivers/dev_spi.h" #include #include @@ -32,7 +32,7 @@ #endif /* #ifdef WIFI_DEBUG_ON */ /********************************* RW009 **************************************/ -#include "spi_wifi_rw009.h" +#include "dev_spi_wifi_rw009.h" /* tools */ #define node_entry(node, type, member) \ diff --git a/rt-thread/components/drivers/spi/spi_wifi_rw009.h b/rt-thread/components/drivers/spi/dev_spi_wifi_rw009.h similarity index 98% rename from rt-thread/components/drivers/spi/spi_wifi_rw009.h rename to rt-thread/components/drivers/spi/dev_spi_wifi_rw009.h index 9bba269..5aecbce 100644 --- a/rt-thread/components/drivers/spi/spi_wifi_rw009.h +++ b/rt-thread/components/drivers/spi/dev_spi_wifi_rw009.h @@ -9,8 +9,8 @@ * 2014-09-18 aozima update command & response. */ -#ifndef SPI_WIFI_H_INCLUDED -#define SPI_WIFI_H_INCLUDED +#ifndef __DEV_SPI_WIFI_H_INCLUDED__ +#define __DEV_SPI_WIFI_H_INCLUDED__ #include @@ -209,4 +209,4 @@ extern int32_t rw009_rssi(void); extern rt_err_t rw009_join(const char * SSID, const char * passwd); extern rt_err_t rw009_softap(const char * SSID, const char * passwd,uint32_t security,uint32_t channel); -#endif // SPI_WIFI_H_INCLUDED +#endif // __DEV_SPI_WIFI_H_INCLUDED__ diff --git a/rt-thread/components/drivers/spi/enc28j60.h b/rt-thread/components/drivers/spi/enc28j60.h index 017877a..013222c 100644 --- a/rt-thread/components/drivers/spi/enc28j60.h +++ b/rt-thread/components/drivers/spi/enc28j60.h @@ -6,13 +6,13 @@ * Change Logs: * Date Author Notes */ -#ifndef EN28J60_H_INCLUDED -#define EN28J60_H_INCLUDED +#ifndef __EN28J60_H_INCLUDED__ +#define __EN28J60_H_INCLUDED__ #include #include -#include +#include "drivers/dev_spi.h" #include // ENC28J60 Control Registers @@ -340,4 +340,4 @@ struct net_device extern rt_err_t enc28j60_attach(const char *spi_device_name); extern void enc28j60_isr(void); -#endif // EN28J60_H_INCLUDED +#endif // __EN28J60_H_INCLUDED__ diff --git a/rt-thread/components/drivers/thermal/Kconfig b/rt-thread/components/drivers/thermal/Kconfig new file mode 100644 index 0000000..b993aaf --- /dev/null +++ b/rt-thread/components/drivers/thermal/Kconfig @@ -0,0 +1,28 @@ +menuconfig RT_USING_THERMAL + bool "Using Thermal Management device drivers" + depends on RT_USING_DM + default n + +if RT_USING_THERMAL + comment "Thermal Sensors Drivers" +endif + +if RT_USING_THERMAL + osource "$(SOC_DM_THERMAL_DIR)/Kconfig" +endif + +if RT_USING_THERMAL + comment "Thermal Cool Drivers" +endif + +config RT_THERMAL_COOL_PWM_FAN + bool "PWM Fan" + depends on RT_USING_THERMAL + depends on RT_USING_PWM + depends on RT_USING_REGULATOR + depends on RT_USING_OFW + default n + +if RT_USING_THERMAL + osource "$(SOC_DM_THERMAL_COOL_DIR)/Kconfig" +endif diff --git a/rt-thread/components/drivers/thermal/SConscript b/rt-thread/components/drivers/thermal/SConscript new file mode 100644 index 0000000..d33d2d2 --- /dev/null +++ b/rt-thread/components/drivers/thermal/SConscript @@ -0,0 +1,18 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_THERMAL']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +src = ['thermal.c', 'thermal_dm.c'] + +if GetDepend(['RT_THERMAL_COOL_PWM_FAN']): + src += ['thermal-cool-pwm-fan.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/rt-thread/components/drivers/thermal/thermal-cool-pwm-fan.c b/rt-thread/components/drivers/thermal/thermal-cool-pwm-fan.c new file mode 100644 index 0000000..d6c2539 --- /dev/null +++ b/rt-thread/components/drivers/thermal/thermal-cool-pwm-fan.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "thermal.cool.pwm-fan" +#define DBG_LVL DBG_INFO +#include + +#define MAX_PWM 255 + +struct pwm_fan_cool +{ + struct rt_thermal_cooling_device parent; + + rt_uint32_t pwm_fan_level; + rt_uint32_t pwm_fan_max_level; + rt_uint32_t *pwm_fan_cooling_levels; + + struct rt_device_pwm *pwm_dev; + struct rt_pwm_configuration pwm_conf; + + struct rt_regulator *supply; + struct rt_spinlock lock; +}; + +#define raw_to_pwm_fan_cool(raw) rt_container_of(raw, struct pwm_fan_cool, parent) + +static rt_err_t pwm_fan_power_on(struct pwm_fan_cool *pf_cool) +{ + rt_err_t err = RT_EOK; + + if ((err = rt_pwm_enable(pf_cool->pwm_dev, pf_cool->pwm_conf.channel))) + { + return err; + } + + if (pf_cool->supply && (err = rt_regulator_enable(pf_cool->supply))) + { + rt_pwm_disable(pf_cool->pwm_dev, pf_cool->pwm_conf.channel); + + return err; + } + + return err; +} + +static rt_err_t pwm_fan_power_off(struct pwm_fan_cool *pf_cool) +{ + rt_err_t err = RT_EOK; + + if (pf_cool->supply && (err = rt_regulator_disable(pf_cool->supply))) + { + return err; + } + + if ((err = rt_pwm_disable(pf_cool->pwm_dev, pf_cool->pwm_conf.channel))) + { + rt_regulator_enable(pf_cool->supply); + + return err; + } + + return err; +} + +static rt_err_t pwm_fan_cool_get_max_level(struct rt_thermal_cooling_device *cdev, + rt_ubase_t *out_level) +{ + struct pwm_fan_cool *pf_cool = raw_to_pwm_fan_cool(cdev); + + *out_level = pf_cool->pwm_fan_max_level; + + return RT_EOK; +} + +static rt_err_t pwm_fan_cool_get_cur_level(struct rt_thermal_cooling_device *cdev, + rt_ubase_t *out_level) +{ + struct pwm_fan_cool *pf_cool = raw_to_pwm_fan_cool(cdev); + + *out_level = pf_cool->pwm_fan_level; + + return RT_EOK; +} + +static rt_err_t pwm_fan_cool_set_cur_level(struct rt_thermal_cooling_device *cdev, + rt_ubase_t level) +{ + rt_ubase_t pwm; + rt_err_t err = RT_EOK; + struct pwm_fan_cool *pf_cool = raw_to_pwm_fan_cool(cdev); + + if (pf_cool->pwm_fan_level == level) + { + return RT_EOK; + } + + rt_spin_lock(&pf_cool->lock); + + if ((pwm = pf_cool->pwm_fan_cooling_levels[level])) + { + rt_ubase_t period; + struct rt_pwm_configuration *pwm_conf = &pf_cool->pwm_conf; + + period = pwm_conf->period; + pwm_conf->pulse = RT_DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); + + err = rt_pwm_set(pf_cool->pwm_dev, + pwm_conf->channel, pwm_conf->period, pwm_conf->pulse); + + if (!err && pf_cool->pwm_fan_level == 0) + { + err = pwm_fan_power_on(pf_cool); + } + } + else if (pf_cool->pwm_fan_level > 0) + { + err = pwm_fan_power_off(pf_cool); + } + + rt_spin_unlock(&pf_cool->lock); + + if (!err) + { + pf_cool->pwm_fan_level = level; + } + + return RT_EOK; +} + +const static struct rt_thermal_cooling_device_ops pwm_fan_cool_ops = +{ + .get_max_level = pwm_fan_cool_get_max_level, + .get_cur_level = pwm_fan_cool_get_cur_level, + .set_cur_level = pwm_fan_cool_set_cur_level, +}; + +static void pwm_fan_cool_free(struct pwm_fan_cool *pf_cool) +{ + if (!rt_is_err_or_null(pf_cool->supply)) + { + rt_regulator_put(pf_cool->supply); + } + + if (pf_cool->pwm_fan_cooling_levels) + { + rt_free(pf_cool->pwm_fan_cooling_levels); + } + + rt_free(pf_cool); +} + +static rt_err_t pwm_fan_cool_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + int levels_nr; + struct rt_ofw_cell_args pwm_args; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_node *np = dev->ofw_node, *pwm_np; + struct pwm_fan_cool *pf_cool = rt_calloc(1, sizeof(*pf_cool)); + + if (!pf_cool) + { + return -RT_ENOMEM; + } + + if (rt_ofw_parse_phandle_cells(np, "pwms", "#pwm-cells", 0, &pwm_args)) + { + err = -RT_EINVAL; + goto _fail; + } + + pwm_np = pwm_args.data; + + if (!rt_ofw_data(pwm_np)) + { + rt_platform_ofw_request(pwm_np); + } + + pf_cool->pwm_dev = rt_ofw_data(pwm_np); + rt_ofw_node_put(pwm_np); + + if (!pf_cool->pwm_dev) + { + err = -RT_EINVAL; + goto _fail; + } + + pf_cool->pwm_conf.channel = pwm_args.args[0]; + pf_cool->pwm_conf.period = pwm_args.args[1]; + + pf_cool->supply = rt_regulator_get(dev, "fan"); + + if (rt_is_err(pf_cool->supply)) + { + err = rt_ptr_err(pf_cool->supply); + goto _fail; + } + + if ((levels_nr = rt_dm_dev_prop_count_of_u32(dev, "cooling-levels")) <= 0) + { + err = -RT_EINVAL; + goto _fail; + } + + pf_cool->pwm_fan_cooling_levels = rt_calloc(levels_nr, sizeof(rt_uint32_t)); + + if (!pf_cool->pwm_fan_cooling_levels) + { + err = -RT_ENOMEM; + goto _fail; + } + + if (rt_dm_dev_prop_read_u32_array_index(dev, "cooling-levels", + 0, levels_nr, pf_cool->pwm_fan_cooling_levels) <= 0) + { + err = -RT_EINVAL; + goto _fail; + } + + pf_cool->pwm_fan_level = MAX_PWM; + pf_cool->pwm_fan_max_level = levels_nr - 1; + + rt_spin_lock_init(&pf_cool->lock); + pwm_fan_cool_set_cur_level(&pf_cool->parent, 0); + + rt_dm_dev_set_name(&pf_cool->parent.parent, "%s", rt_dm_dev_get_name(&pdev->parent)); + pf_cool->parent.parent.ofw_node = dev->ofw_node; + pf_cool->parent.ops = &pwm_fan_cool_ops; + + if ((err = rt_thermal_cooling_device_register(&pf_cool->parent))) + { + goto _fail; + } + + dev->user_data = pf_cool; + + return RT_EOK; + +_fail: + pwm_fan_cool_free(pf_cool); + + return err; +} + +static rt_err_t pwm_fan_cool_remove(struct rt_platform_device *pdev) +{ + struct pwm_fan_cool *pf_cool = pdev->parent.ofw_node; + + rt_thermal_cooling_device_unregister(&pf_cool->parent); + + pwm_fan_power_off(pf_cool); + pwm_fan_cool_free(pf_cool); + + return RT_EOK; +} + +static rt_err_t pwm_fan_cool_shutdown(struct rt_platform_device *pdev) +{ + return pwm_fan_cool_remove(pdev); +} + +static const struct rt_ofw_node_id pwm_fan_cool_ofw_ids[] = +{ + { .compatible = "pwm-fan" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver pwm_fan_cool_driver = +{ + .name = "pwm-fan-cool", + .ids = pwm_fan_cool_ofw_ids, + + .probe = pwm_fan_cool_probe, + .remove = pwm_fan_cool_remove, + .shutdown = pwm_fan_cool_shutdown, +}; +RT_PLATFORM_DRIVER_EXPORT(pwm_fan_cool_driver); diff --git a/rt-thread/components/drivers/thermal/thermal.c b/rt-thread/components/drivers/thermal/thermal.c new file mode 100644 index 0000000..e9765c8 --- /dev/null +++ b/rt-thread/components/drivers/thermal/thermal.c @@ -0,0 +1,917 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include + +#define DBG_TAG "rtdm.thermal" +#define DBG_LVL DBG_INFO +#include + +#include "thermal_dm.h" + +#ifndef INT_MAX +#define INT_MAX (RT_UINT32_MAX >> 1) +#endif + +#define device_list(dev) (dev)->parent.parent.list +#define device_foreach(dev, nodes) rt_list_for_each_entry(dev, nodes, parent.parent.list) + +static struct rt_spinlock nodes_lock = {}; +static rt_list_t thermal_zone_device_nodes = RT_LIST_OBJECT_INIT(thermal_zone_device_nodes); +static rt_list_t thermal_cooling_device_nodes = RT_LIST_OBJECT_INIT(thermal_cooling_device_nodes); +static rt_list_t thermal_cooling_governor_nodes = RT_LIST_OBJECT_INIT(thermal_cooling_governor_nodes); + +#ifdef RT_USING_OFW +static void thermal_ofw_params_parse(struct rt_ofw_node *np, + struct rt_thermal_zone_params *tz_params) +{ + rt_uint32_t coef[2], prop; + + if (!np) + { + return; + } + + if (!rt_ofw_prop_read_u32(np, "sustainable-power", &prop)) + { + tz_params->sustainable_power = prop; + } + + /* + * For now, the thermal framework supports only one sensor per thermal zone. + * Thus, we are considering only the first two values as slope and offset. + */ + if (rt_ofw_prop_read_u32_array_index(np, "coefficients", 0, 1, coef) < 0) + { + coef[0] = 1; + coef[1] = 0; + } + + tz_params->slope = coef[0]; + tz_params->offset = coef[1]; +} + +static void thermal_ofw_setup(struct rt_ofw_node *np, struct rt_thermal_zone_device *zdev) +{ + int i = 0; + rt_uint32_t delay, pdelay; + struct rt_ofw_cell_args args; + struct rt_ofw_node *tmp_np, *tz_np, *trip_np, *cm_np, *cdev_np; + + if (!np || !zdev) + { + return; + } + + tmp_np = rt_ofw_find_node_by_path("/thermal-zones"); + + if (!tmp_np) + { + return; + } + + rt_ofw_foreach_child_node(tmp_np, tz_np) + { + if (!rt_ofw_parse_phandle_cells(tz_np, "thermal-sensors", "#thermal-sensor-cells", 0, &args)) + { + if (args.data == np && (!args.args_count || args.args[0] == zdev->zone_id)) + { + rt_ofw_node_put(args.data); + + goto _found; + } + rt_ofw_node_put(args.data); + } + } + + return; + +_found: + rt_ofw_prop_read_u32(tz_np, "polling-delay-passive", &pdelay); + rt_ofw_prop_read_u32(tz_np, "polling-delay", &delay); + + zdev->passive_delay = rt_tick_from_millisecond(pdelay); + zdev->polling_delay = rt_tick_from_millisecond(delay); + + thermal_ofw_params_parse(tz_np, &zdev->params); + + if (zdev->trips_nr) + { + goto _scan_cooling; + } + + tmp_np = rt_ofw_get_child_by_tag(tz_np, "trips"); + if (!tmp_np) + { + goto _scan_cooling; + } + + zdev->trips_nr = rt_ofw_get_child_count(tmp_np); + if (!zdev->trips_nr) + { + goto _scan_cooling; + } + zdev->trips = rt_calloc(zdev->trips_nr, sizeof(*zdev->trips)); + zdev->trips_free = RT_TRUE; + + if (!zdev->trips) + { + LOG_E("%s: No memory to create %s", rt_ofw_node_full_name(np), "trips"); + RT_ASSERT(0); + } + + rt_ofw_foreach_child_node(tmp_np, trip_np) + { + const char *type; + + rt_ofw_prop_read_u32(trip_np, "temperature", (rt_uint32_t *)&zdev->trips[i].temperature); + rt_ofw_prop_read_u32(trip_np, "hysteresis", (rt_uint32_t *)&zdev->trips[i].hysteresis); + rt_ofw_prop_read_string(trip_np, "type", &type); + zdev->trips[i].type = thermal_type(type); + + rt_ofw_data(trip_np) = &zdev->trips[i]; + + ++i; + } + +_scan_cooling: + i = 0; + tmp_np = rt_ofw_get_child_by_tag(tz_np, "cooling-maps"); + if (!tmp_np) + { + goto _end; + } + + zdev->cooling_maps_nr = rt_ofw_get_child_count(tmp_np); + if (!zdev->cooling_maps_nr) + { + goto _end; + } + zdev->cooling_maps = rt_calloc(zdev->cooling_maps_nr, sizeof(*zdev->cooling_maps)); + + if (!zdev->cooling_maps) + { + LOG_E("%s: No memory to create %s", rt_ofw_node_full_name(np), "cooling_maps"); + RT_ASSERT(0); + } + + rt_ofw_foreach_child_node(tmp_np, cm_np) + { + struct rt_thermal_cooling_device *cdev; + struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i++]; + + map->cells_nr = rt_ofw_count_phandle_cells(cm_np, "cooling-device", "#cooling-cells"); + map->cells = rt_calloc(sizeof(*map->cells), map->cells_nr); + + if (!map->cells) + { + LOG_E("%s: No memory to create %s", rt_ofw_node_full_name(np), "cells"); + RT_ASSERT(0); + } + + trip_np = rt_ofw_parse_phandle(cm_np, "trip", 0); + map->trips = rt_ofw_data(trip_np); + rt_ofw_node_put(trip_np); + + if (!map->trips) + { + LOG_E("%s: trips(%s) not found", rt_ofw_node_full_name(np), + rt_ofw_node_full_name(trip_np)); + RT_ASSERT(0); + } + + rt_ofw_prop_read_u32(cm_np, "contribution", &map->contribution); + + for (int c = 0; c < map->cells_nr; ++c) + { + struct rt_thermal_cooling_cell *cell = &map->cells[c]; + + if (rt_ofw_parse_phandle_cells(cm_np, "cooling-device", "#cooling-cells", c, &args)) + { + continue; + } + + cdev_np = args.data; + + rt_spin_lock(&nodes_lock); + device_foreach(cdev, &thermal_cooling_device_nodes) + { + if (cdev->parent.ofw_node == cdev_np) + { + cell->cooling_devices = cdev; + break; + } + } + rt_spin_unlock(&nodes_lock); + + cell->level_range[0] = args.args[0]; + cell->level_range[1] = args.args[1]; + + if (cell->cooling_devices) + { + thermal_bind(cell->cooling_devices, zdev); + } + + rt_ofw_node_put(cdev_np); + } + } +_end: +} +#else +rt_inline void thermal_ofw_setup(struct rt_ofw_node *np, struct rt_thermal_zone_device *zdev) +{ +} +#endif /* RT_USING_OFW */ + +static void thermal_zone_poll(struct rt_work *work, void *work_data) +{ + struct rt_thermal_zone_device *zdev = work_data; + + rt_thermal_zone_device_update(zdev, RT_THERMAL_MSG_EVENT_UNSPECIFIED); +} + +rt_err_t rt_thermal_zone_device_register(struct rt_thermal_zone_device *zdev) +{ + if (!zdev || !zdev->ops || !zdev->ops->get_temp) + { + return -RT_EINVAL; + } + + zdev->ops->get_temp(zdev, &zdev->temperature); + zdev->last_temperature = zdev->temperature; + + if (!zdev->trips) + { + zdev->trips_nr = 0; + } + + rt_spin_lock_init(&zdev->nodes_lock); + rt_list_init(&zdev->notifier_nodes); + rt_list_init(&device_list(zdev)); + rt_mutex_init(&zdev->mutex, rt_dm_dev_get_name(&zdev->parent), RT_IPC_FLAG_PRIO); + + zdev->temperature = RT_THERMAL_TEMP_INVALID; + zdev->prev_low_trip = -INT_MAX; + zdev->prev_high_trip = INT_MAX; + + rt_spin_lock(&nodes_lock); + rt_list_insert_before(&thermal_zone_device_nodes, &device_list(zdev)); + rt_spin_unlock(&nodes_lock); + + thermal_ofw_setup(zdev->parent.ofw_node, zdev); + + rt_work_init(&zdev->poller, thermal_zone_poll, zdev); + zdev->enabled = RT_TRUE; + + /* Start to poll */ + rt_work_submit(&zdev->poller, zdev->polling_delay); + + return RT_EOK; +} + +rt_err_t rt_thermal_zone_device_unregister(struct rt_thermal_zone_device *zdev) +{ + if (!zdev) + { + return -RT_EINVAL; + } + + rt_spin_lock(&zdev->nodes_lock); + if (rt_list_isempty(&zdev->notifier_nodes)) + { + LOG_E("%s: there is %u user", rt_dm_dev_get_name(&zdev->parent), + rt_list_len(&zdev->notifier_nodes)); + + rt_spin_unlock(&zdev->nodes_lock); + + return -RT_EBUSY; + } + rt_spin_unlock(&zdev->nodes_lock); + + rt_work_cancel(&zdev->poller); + + rt_spin_lock(&nodes_lock); + rt_list_remove(&device_list(zdev)); + rt_spin_unlock(&nodes_lock); + + if (zdev->trips_free && zdev->trips) + { + rt_free(zdev->trips); + } + + if (zdev->cooling_maps_nr && zdev->cooling_maps_nr) + { + for (int i = 0; i < zdev->cooling_maps_nr; ++i) + { + struct rt_thermal_cooling_device *cdev; + struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i]; + + for (int c = 0; c < map->cells_nr; ++c) + { + cdev = map->cells[i].cooling_devices; + + if (cdev) + { + thermal_unbind(cdev, zdev); + } + } + + rt_free(map->cells); + } + + rt_free(zdev->cooling_maps); + } + + rt_mutex_detach(&zdev->mutex); + + return RT_EOK; +} + +rt_err_t rt_thermal_cooling_device_register(struct rt_thermal_cooling_device *cdev) +{ + rt_err_t err; + + if (!cdev || !cdev->ops || + !cdev->ops->get_max_level || !cdev->ops->get_cur_level || !cdev->ops->set_cur_level) + { + return -RT_EINVAL; + } + + if ((err = cdev->ops->get_max_level(cdev, &cdev->max_level))) + { + return err; + } + + rt_list_init(&device_list(cdev)); + rt_list_init(&cdev->governor_node); + + rt_spin_lock(&nodes_lock); + rt_list_insert_before(&thermal_cooling_device_nodes, &device_list(cdev)); + rt_spin_unlock(&nodes_lock); + + err = rt_thermal_cooling_device_change_governor(cdev, RT_NULL); + + return err; +} + +rt_err_t rt_thermal_cooling_device_unregister(struct rt_thermal_cooling_device *cdev) +{ + if (!cdev) + { + return -RT_EINVAL; + } + + if (cdev->parent.ref_count) + { + LOG_E("%s: there is %u user", + rt_dm_dev_get_name(&cdev->parent), cdev->parent.ref_count); + return -RT_EINVAL; + } + + rt_spin_lock(&nodes_lock); + rt_list_remove(&device_list(cdev)); + rt_spin_unlock(&nodes_lock); + + return RT_EOK; +} + +static void dumb_governor_tuning(struct rt_thermal_zone_device *zdev, + int map_idx, int cell_idx, rt_ubase_t *level) +{ + struct rt_thermal_cooling_map *map = &zdev->cooling_maps[map_idx]; + + if (zdev->cooling && zdev->temperature > map->trips->temperature) + { + if (zdev->temperature - zdev->last_temperature > map->trips->hysteresis) + { + ++*level; + } + else if (zdev->last_temperature - zdev->temperature > map->trips->hysteresis) + { + --*level; + } + } + else + { + *level = 0; + } +} + +static struct rt_thermal_cooling_governor dumb_governor = +{ + .name = "dumb", + .tuning = dumb_governor_tuning, +}; + +static int system_thermal_cooling_governor_init(void) +{ + rt_thermal_cooling_governor_register(&dumb_governor); + + return 0; +} +INIT_CORE_EXPORT(system_thermal_cooling_governor_init); + +rt_err_t rt_thermal_cooling_governor_register(struct rt_thermal_cooling_governor *gov) +{ + rt_err_t err = RT_EOK; + struct rt_thermal_cooling_governor *gov_tmp; + + if (!gov || !gov->name || !gov->tuning) + { + return -RT_EINVAL; + } + + rt_list_init(&gov->list); + rt_list_init(&gov->cdev_nodes); + + rt_spin_lock(&nodes_lock); + + rt_list_for_each_entry(gov_tmp, &thermal_cooling_governor_nodes, list) + { + if (!rt_strcmp(gov_tmp->name, gov->name)) + { + err = -RT_ERROR; + goto _out_unlock; + } + } + + rt_list_insert_before(&thermal_cooling_governor_nodes, &gov->list); + +_out_unlock: + rt_spin_unlock(&nodes_lock); + + return err; +} + +rt_err_t rt_thermal_cooling_governor_unregister(struct rt_thermal_cooling_governor *gov) +{ + if (!gov) + { + return -RT_EINVAL; + } + + if (gov == &dumb_governor) + { + return -RT_EINVAL; + } + + rt_spin_lock(&nodes_lock); + + if (!rt_list_isempty(&gov->cdev_nodes)) + { + goto _out_unlock; + } + + rt_list_remove(&gov->list); + +_out_unlock: + rt_spin_unlock(&nodes_lock); + + return RT_EOK; +} + +rt_err_t rt_thermal_cooling_device_change_governor(struct rt_thermal_cooling_device *cdev, + const char *name) +{ + rt_err_t err; + struct rt_thermal_cooling_governor *gov; + + if (!cdev) + { + return -RT_EINVAL; + } + + name = name ? : dumb_governor.name; + err = -RT_ENOSYS; + + rt_spin_lock(&nodes_lock); + + rt_list_for_each_entry(gov, &thermal_cooling_governor_nodes, list) + { + if (!rt_strcmp(gov->name, name)) + { + if (cdev->gov) + { + rt_list_remove(&cdev->governor_node); + } + + cdev->gov = gov; + rt_list_insert_before(&cdev->governor_node, &gov->cdev_nodes); + + err = RT_EOK; + break; + } + } + + rt_spin_unlock(&nodes_lock); + + return err; +} + +rt_err_t rt_thermal_zone_notifier_register(struct rt_thermal_zone_device *zdev, + struct rt_thermal_notifier *notifier) +{ + if (!zdev || !notifier) + { + return -RT_EINVAL; + } + + notifier->zdev = zdev; + rt_list_init(¬ifier->list); + + rt_spin_lock(&zdev->nodes_lock); + rt_list_insert_after(&zdev->notifier_nodes, ¬ifier->list); + rt_spin_unlock(&zdev->nodes_lock); + + return RT_EOK; +} + +rt_err_t rt_thermal_zone_notifier_unregister(struct rt_thermal_zone_device *zdev, + struct rt_thermal_notifier *notifier) +{ + if (!zdev || !notifier) + { + return -RT_EINVAL; + } + + rt_spin_lock(&zdev->nodes_lock); + rt_list_remove(¬ifier->list); + rt_spin_unlock(&zdev->nodes_lock); + + return RT_EOK; +} + +void rt_thermal_zone_device_update(struct rt_thermal_zone_device *zdev, rt_ubase_t msg) +{ + rt_err_t err; + rt_bool_t passive = RT_FALSE, need_cool = RT_FALSE; + struct rt_thermal_notifier *notifier, *next_notifier; + + RT_ASSERT(zdev != RT_NULL); + + if (!rt_interrupt_get_nest()) + { + rt_mutex_take(&zdev->mutex, RT_WAITING_FOREVER); + } + + /* Check thermal zone status */ + if (msg == RT_THERMAL_MSG_DEVICE_DOWN) + { + zdev->enabled = RT_FALSE; + } + else if (msg == RT_THERMAL_MSG_DEVICE_UP) + { + zdev->enabled = RT_TRUE; + } + + /* Read temperature */ + zdev->last_temperature = zdev->temperature; + zdev->ops->get_temp(zdev, &zdev->temperature); + + for (int i = 0; i < zdev->trips_nr; ++i) + { + struct rt_thermal_trip *tmp_trip = &zdev->trips[i]; + + if (zdev->temperature <= tmp_trip->temperature) + { + continue; + } + + switch (tmp_trip->type) + { + case RT_THERMAL_TRIP_PASSIVE: + passive = RT_TRUE; + goto cooling; + + case RT_THERMAL_TRIP_CRITICAL: + if (zdev->ops->critical) + { + zdev->ops->critical(zdev); + } + else if (zdev->last_temperature > tmp_trip->temperature) + { + /* Tried to cool already, but failed */ + rt_hw_cpu_reset(); + } + else + { + goto cooling; + } + break; + + case RT_THERMAL_TRIP_HOT: + if (zdev->ops->hot) + { + zdev->ops->hot(zdev); + break; + } + + default: + cooling: + zdev->cooling = need_cool = RT_TRUE; + rt_thermal_cooling_device_kick(zdev); + break; + } + } + + if (!need_cool && zdev->cooling) + { + rt_thermal_cooling_device_kick(zdev); + } + + /* Set the new trips */ + if (zdev->ops->set_trips) + { + rt_bool_t same_trip = RT_FALSE; + int low = -INT_MAX, high = INT_MAX; + struct rt_thermal_trip trip; + + for (int i = 0; i < zdev->trips_nr; ++i) + { + int trip_low; + rt_bool_t low_set = RT_FALSE; + + if (i >= zdev->trips_nr) + { + goto _call_notifier; + } + rt_memcpy(&trip, &zdev->trips[i], sizeof(trip)); + + trip_low = trip.temperature - trip.hysteresis; + + if (trip_low < zdev->temperature && trip_low > low) + { + low = trip_low; + low_set = RT_TRUE; + same_trip = RT_FALSE; + } + + if (trip.temperature > zdev->temperature && trip.temperature < high) + { + high = trip.temperature; + same_trip = low_set; + } + } + + /* No need to change trip points */ + if (zdev->prev_low_trip == low && zdev->prev_high_trip == high) + { + goto _call_notifier; + } + + if (same_trip && + (zdev->prev_low_trip != -INT_MAX || zdev->prev_high_trip != INT_MAX)) + { + goto _call_notifier; + } + + zdev->prev_low_trip = low; + zdev->prev_high_trip = high; + + if ((err = zdev->ops->set_trips(zdev, low, high))) + { + LOG_E("%s: Set trips error = %s", rt_dm_dev_get_name(&zdev->parent), + rt_strerror(err)); + } + } + + /* Call all notifier, maybe have governor */ +_call_notifier: + rt_spin_lock(&zdev->nodes_lock); + + rt_list_for_each_entry_safe(notifier, next_notifier, &zdev->notifier_nodes, list) + { + rt_spin_unlock(&zdev->nodes_lock); + + notifier->callback(notifier, msg); + + rt_spin_lock(&zdev->nodes_lock); + } + + rt_spin_unlock(&zdev->nodes_lock); + + /* Prepare for the next report */ + if (!zdev->enabled) + { + rt_work_cancel(&zdev->poller); + } + else if (passive && zdev->passive_delay) + { + rt_work_submit(&zdev->poller, zdev->passive_delay); + } + else if (zdev->polling_delay) + { + rt_work_submit(&zdev->poller, zdev->polling_delay); + } + + if (!rt_interrupt_get_nest()) + { + rt_mutex_release(&zdev->mutex); + } +} + +void rt_thermal_cooling_device_kick(struct rt_thermal_zone_device *zdev) +{ + RT_ASSERT(zdev != RT_NULL); + + for (int i = 0; i < zdev->cooling_maps_nr; ++i) + { + rt_ubase_t level; + struct rt_thermal_cooling_device *cdev; + struct rt_thermal_cooling_cell *cell; + struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i]; + + for (int c = 0; c < map->cells_nr; ++c) + { + cell = &map->cells[c]; + cdev = cell->cooling_devices; + + if (!cdev) + { + continue; + } + + /* Update status */ + if (cdev->ops->get_max_level(cdev, &cdev->max_level)) + { + continue; + } + + if (cdev->ops->get_cur_level(cdev, &level) || level > cdev->max_level) + { + continue; + } + + /* Check if cooling is required */ + if (level >= cell->level_range[0] && level <= cell->level_range[1]) + { + /* Is cooling, not call */ + continue; + } + + cdev->gov->tuning(zdev, i, c, &level); + level = rt_min_t(rt_ubase_t, level, cdev->max_level); + + cdev->ops->set_cur_level(cdev, level); + } + } +} + +rt_err_t rt_thermal_zone_set_trip(struct rt_thermal_zone_device *zdev, int trip_id, + const struct rt_thermal_trip *trip) +{ + rt_err_t err; + struct rt_thermal_trip tmp_trip; + + if (!zdev || !trip) + { + return -RT_EINVAL; + } + + rt_mutex_take(&zdev->mutex, RT_WAITING_FOREVER); + + if (!zdev->ops->set_trip_temp && !zdev->ops->set_trip_hyst && !zdev->trips) + { + err = -RT_EINVAL; + goto _out_unlock; + } + + if (trip_id >= zdev->trips_nr) + { + err = -RT_EINVAL; + goto _out_unlock; + } + + rt_memcpy(&tmp_trip, &zdev->trips[trip_id], sizeof(tmp_trip)); + + if (tmp_trip.type != trip->type) + { + err = -RT_EINVAL; + goto _out_unlock; + } + + if (tmp_trip.temperature != trip->temperature && zdev->ops->set_trip_temp) + { + if ((err = zdev->ops->set_trip_temp(zdev, trip_id, trip->temperature))) + { + goto _out_unlock; + } + } + + if (tmp_trip.hysteresis != trip->hysteresis && zdev->ops->set_trip_hyst) + { + if ((err = zdev->ops->set_trip_hyst(zdev, trip_id, trip->hysteresis))) + { + goto _out_unlock; + } + } + + if (zdev->trips && + (tmp_trip.temperature != trip->temperature || tmp_trip.hysteresis != trip->hysteresis)) + { + zdev->trips[trip_id] = *trip; + } + +_out_unlock: + rt_mutex_release(&zdev->mutex); + + if (!err) + { + rt_thermal_zone_device_update(zdev, RT_THERMAL_MSG_TRIP_CHANGED); + } + + return err; +} + +rt_err_t rt_thermal_zone_get_trip(struct rt_thermal_zone_device *zdev, int trip_id, + struct rt_thermal_trip *out_trip) +{ + rt_err_t err = RT_EOK; + + if (!zdev || !out_trip) + { + return -RT_EINVAL; + } + + rt_mutex_take(&zdev->mutex, RT_WAITING_FOREVER); + + if (!zdev->trips_nr) + { + err = -RT_ENOSYS; + goto _out_unlock; + } + + if (trip_id >= zdev->trips_nr) + { + err = -RT_EINVAL; + goto _out_unlock; + } + + *out_trip = zdev->trips[trip_id]; + +_out_unlock: + rt_mutex_release(&zdev->mutex); + + return err; +} + +#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH) +static int list_thermal(int argc, char**argv) +{ + struct rt_thermal_zone_device *zdev; + + /* Thermal is an important subsystem, please do not output too much. */ + rt_spin_lock(&nodes_lock); + device_foreach(zdev, &thermal_zone_device_nodes) + { + int temperature = zdev->temperature; + + rt_kprintf("%s-%d\n", rt_dm_dev_get_name(&zdev->parent), zdev->zone_id); + rt_kprintf("temperature:\t%+d.%u C\n", temperature / 1000, rt_abs(temperature) % 1000); + + for (int i = 0, id = 0; i < zdev->cooling_maps_nr; ++i) + { + rt_ubase_t level; + struct rt_thermal_trip *trips; + struct rt_thermal_cooling_device *cdev; + struct rt_thermal_cooling_cell *cell; + struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i]; + + for (int c = 0; c < map->cells_nr; ++c, ++id) + { + trips = map->trips; + cell = &map->cells[c]; + cdev = cell->cooling_devices; + + if (cdev) + { + cdev->ops->get_cur_level(cdev, &level); + + rt_kprintf("cooling%u:\t%s[%+d.%u C] %d\n", id, + rt_dm_dev_get_name(&cdev->parent), + trips->temperature / 1000, rt_abs(trips->temperature) % 1000, + level); + } + else + { + rt_kprintf("cooling%u:\t%s[%+d.%u C] %d\n", id, + "(not supported)", + trips->temperature / 1000, rt_abs(trips->temperature) % 1000, + 0); + } + } + } + } + rt_spin_unlock(&nodes_lock); + + return 0; +} +MSH_CMD_EXPORT(list_thermal, dump all of thermal information); +#endif /* RT_USING_CONSOLE && RT_USING_MSH */ diff --git a/rt-thread/components/drivers/thermal/thermal_dm.c b/rt-thread/components/drivers/thermal/thermal_dm.c new file mode 100644 index 0000000..9c1d81f --- /dev/null +++ b/rt-thread/components/drivers/thermal/thermal_dm.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#define DBG_TAG "rtdm.thermal" +#define DBG_LVL DBG_INFO +#include + +#include "thermal_dm.h" + +enum rt_thermal_trip_type thermal_type(const char *type) +{ + if (!type) + { + return RT_THERMAL_TRIP_TYPE_MAX; + } + + if (!rt_strcmp(type, "active")) + { + return RT_THERMAL_TRIP_ACTIVE; + } + else if (!rt_strcmp(type, "passive")) + { + return RT_THERMAL_TRIP_PASSIVE; + } + else if (!rt_strcmp(type, "hot")) + { + return RT_THERMAL_TRIP_HOT; + } + else if (!rt_strcmp(type, "critical")) + { + return RT_THERMAL_TRIP_CRITICAL; + } + + return RT_THERMAL_TRIP_TYPE_MAX; +} + +rt_err_t thermal_bind(struct rt_thermal_cooling_device *cdev, + struct rt_thermal_zone_device *zdev) +{ + if (cdev->ops->bind) + { + return cdev->ops->bind(cdev, zdev); + } + + return RT_EOK; +} + +rt_err_t thermal_unbind(struct rt_thermal_cooling_device *cdev, + struct rt_thermal_zone_device *zdev) +{ + if (cdev->ops->unbind) + { + return cdev->ops->unbind(cdev, zdev); + } + + return RT_EOK; +} diff --git a/rt-thread/components/drivers/thermal/thermal_dm.h b/rt-thread/components/drivers/thermal/thermal_dm.h new file mode 100644 index 0000000..15d3314 --- /dev/null +++ b/rt-thread/components/drivers/thermal/thermal_dm.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#ifndef __THERMAL_DM_H__ +#define __THERMAL_DM_H__ + +#include +#include +#include + +#include + +enum rt_thermal_trip_type thermal_type(const char *type); + +rt_err_t thermal_bind(struct rt_thermal_cooling_device *cdev, + struct rt_thermal_zone_device *zdev); +rt_err_t thermal_unbind(struct rt_thermal_cooling_device *cdev, + struct rt_thermal_zone_device *zdev); + +#endif /* __THERMAL_DM_H__ */ diff --git a/rt-thread/components/drivers/touch/SConscript b/rt-thread/components/drivers/touch/SConscript index df698c7..ba646b0 100644 --- a/rt-thread/components/drivers/touch/SConscript +++ b/rt-thread/components/drivers/touch/SConscript @@ -3,7 +3,7 @@ from building import * cwd = GetCurrentDir() -src = ['touch.c'] +src = ['dev_touch.c'] CPPPATH = [cwd, cwd + '/../include'] group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_TOUCH', 'RT_USING_DEVICE'], CPPPATH = CPPPATH) diff --git a/rt-thread/components/drivers/touch/touch.c b/rt-thread/components/drivers/touch/dev_touch.c similarity index 100% rename from rt-thread/components/drivers/touch/touch.c rename to rt-thread/components/drivers/touch/dev_touch.c diff --git a/rt-thread/components/drivers/usb/cherryusb/CherryUSB.svg b/rt-thread/components/drivers/usb/cherryusb/CherryUSB.svg index c1ed478..91de329 100644 --- a/rt-thread/components/drivers/usb/cherryusb/CherryUSB.svg +++ b/rt-thread/components/drivers/usb/cherryusb/CherryUSB.svg @@ -1,3 +1,3 @@ -
CherryUSB
CherryUSB
Hardware
Hardware
BL
BL
DWC2
DWC2
OHCI
OHCI
EHCI
EHCI
XHCI
XHCI
MUSB
MUSB
FSDEV
FSDEV
HPM
HPM
Device Controller Driver (DCD)
Device Controller Driver (DCD)
Host Controller Driver (HCD)
Host Controller Driver (HCD)
OS Abstraction Layer (OSAL)
OS Abstraction Layer (OSAL)
USB Host
USB Host
USB Host Core
USB Host Core
HID
HID
MSC
MSC
CDC
CDC
UAC
UAC
UVC
UVC
RNDIS
RNDIS
VENDOR
VENDOR
HUB
HUB
USB Device
USB Device
USB Device Core
USB Device Core
HID
HID
MSC
MSC
CDC
CDC
UAC
UAC
UVC
UVC
RNDIS
RNDIS
VENDOR
VENDOR
DFU
DFU
optional
optional
required
required
Text is not SVG - cannot display
\ No newline at end of file +
CherryUSB
CherryUSB
Hardware
Hardware
CDNS3
CDNS3
CHIPIDEA
CHIPIDEA
XHCI
XHCI
EHCI
EHCI
OHCI
OHCI
DWC2
DWC2
MUSB
MUSB
FOTG
FOTG
Device Controller Driver (DCD)
Device Controller Driver (DCD)
Host Controller Driver (HCD)
Host Controller Driver (HCD)
OS Abstraction Layer (OSAL)
OS Abstraction Layer (OSAL)
USB Host
USB Host
USB Host Core
USB Host Core
HID
HID
MSC
MSC
CDC
CDC
UAC
UAC
UVC
UVC
RNDIS
RNDIS
VENDOR
VENDOR
HUB
HUB
USB Device
USB Device
USB Device Core
USB Device Core
HID
HID
MSC
MSC
CDC
CDC
UAC
UAC
UVC
UVC
RNDIS
RNDIS
VENDOR
VENDOR
DFU
DFU
optional
optional
required
required
\ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/Kconfig b/rt-thread/components/drivers/usb/cherryusb/Kconfig index 3e9ce5f..67c6ad5 100644 --- a/rt-thread/components/drivers/usb/cherryusb/Kconfig +++ b/rt-thread/components/drivers/usb/cherryusb/Kconfig @@ -38,6 +38,8 @@ if RT_USING_CHERRYUSB bool "dwc2_gd" config RT_CHERRYUSB_DEVICE_DWC2_HC bool "dwc2_hc" + config RT_CHERRYUSB_DEVICE_DWC2_KENDRYTE + bool "dwc2_kendryte" config RT_CHERRYUSB_DEVICE_DWC2_CUSTOM bool "dwc2_custom" config RT_CHERRYUSB_DEVICE_MUSB_ES @@ -48,6 +50,14 @@ if RT_USING_CHERRYUSB bool "musb_bk" config RT_CHERRYUSB_DEVICE_MUSB_CUSTOM bool "musb_custom" + config RT_CHERRYUSB_DEVICE_KINETIS_MCX + bool "kinetis_mcx" + config RT_CHERRYUSB_DEVICE_KINETIS_CUSTOM + bool "kinetis_custom" + config RT_CHERRYUSB_DEVICE_CHIPIDEA_MCX + bool "chipidea_mcx" + config RT_CHERRYUSB_DEVICE_CHIPIDEA_CUSTOM + bool "chipidea_custom" config RT_CHERRYUSB_DEVICE_BL bool "bouffalo" config RT_CHERRYUSB_DEVICE_CH32 @@ -107,7 +117,7 @@ if RT_USING_CHERRYUSB choice prompt "Select usb device template" - default RT_CHERRYUSB_DEVICE_TEMPLATE + default RT_CHERRYUSB_DEVICE_TEMPLATE_NONE config RT_CHERRYUSB_DEVICE_TEMPLATE_NONE bool "none (Implement it yourself)" config RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM @@ -162,16 +172,20 @@ if RT_USING_CHERRYUSB bool "ehci_hpm" config RT_CHERRYUSB_HOST_EHCI_AIC bool "ehci_aic" - config RT_CHERRYUSB_HOST_EHCI_NUVOTON_NUC980 - bool "ehci_nuvoton_nuc980" - config RT_CHERRYUSB_HOST_EHCI_NUVOTON_MA35D0 - bool "ehci_nuvoton_ma35d0" + config RT_CHERRYUSB_HOST_EHCI_MCX + bool "ehci_mcx" + config RT_CHERRYUSB_HOST_EHCI_NUC980 + bool "ehci_nuc980" + config RT_CHERRYUSB_HOST_EHCI_MA35D0 + bool "ehci_ma35d0" config RT_CHERRYUSB_HOST_EHCI_CUSTOM bool "ehci_custom" config RT_CHERRYUSB_HOST_DWC2_ST bool "dwc2_st" config RT_CHERRYUSB_HOST_DWC2_ESP bool "dwc2_esp" + config RT_CHERRYUSB_HOST_DWC2_KENDRYTE + bool "dwc2_kendryte" config RT_CHERRYUSB_HOST_DWC2_CUSTOM bool "dwc2_custom" config RT_CHERRYUSB_HOST_MUSB_ES @@ -186,6 +200,10 @@ if RT_USING_CHERRYUSB bool "pusb2" config RT_CHERRYUSB_HOST_XHCI bool "xhci" + config RT_CHERRYUSB_HOST_KINETIS_MCX + bool "kinetis_mcx" + config RT_CHERRYUSB_HOST_KINETIS_CUSTOM + bool "kinetis_custom" endchoice config RT_CHERRYUSB_HOST_CDC_ACM @@ -203,6 +221,7 @@ if RT_USING_CHERRYUSB prompt "Enable usb msc driver" default n select RT_USING_DFS + select RT_USING_DFS_ELMFAT config RT_CHERRYUSB_HOST_CDC_ECM bool @@ -294,7 +313,12 @@ if RT_USING_CHERRYUSB config CONFIG_USBHOST_PLATFORM_RTL8152 bool - config CHERRYUSB_HOST_TEMPLATE + config RT_LWIP_PBUF_POOL_BUFSIZE + int "The size of each pbuf in the pbuf pool" + range 1500 2000 + default 1600 + + config RT_CHERRYUSB_HOST_TEMPLATE bool prompt "Use usb host template" default n diff --git a/rt-thread/components/drivers/usb/cherryusb/Kconfig.cherryusb b/rt-thread/components/drivers/usb/cherryusb/Kconfig.cherryusb deleted file mode 100644 index 4d44a75..0000000 --- a/rt-thread/components/drivers/usb/cherryusb/Kconfig.cherryusb +++ /dev/null @@ -1,312 +0,0 @@ -# Kconfig file for CherryUSB -menuconfig CHERRYUSB - bool "Using CherryUSB" - default n - -if CHERRYUSB - - menuconfig CHERRYUSB_DEVICE - bool "Enable usb device mode" - default n - - if CHERRYUSB_DEVICE - choice - prompt "Select usb device speed" - default CHERRYUSB_DEVICE_SPEED_FS - config CHERRYUSB_DEVICE_SPEED_FS - bool "FS" - config CHERRYUSB_DEVICE_SPEED_HS - bool "HS" - config CHERRYUSB_DEVICE_SPEED_AUTO - bool "AUTO" - endchoice - - choice - prompt "Select usb device ip, and some ip need config in usb_config.h, please check" - default CHERRYUSB_DEVICE_CUSTOM - config CHERRYUSB_DEVICE_CUSTOM - bool "CUSTOM (Implement it yourself)" - config CHERRYUSB_DEVICE_FSDEV - bool "fsdev" - config CHERRYUSB_DEVICE_DWC2_ST - bool "dwc2_st" - config CHERRYUSB_DEVICE_DWC2_ESP - bool "dwc2_esp" - config CHERRYUSB_DEVICE_DWC2_AT - bool "dwc2_at" - config CHERRYUSB_DEVICE_DWC2_GD - bool "dwc2_gd" - config CHERRYUSB_DEVICE_DWC2_HC - bool "dwc2_hc" - config CHERRYUSB_DEVICE_DWC2_CUSTOM - bool "dwc2_custom" - config CHERRYUSB_DEVICE_MUSB_ES - bool "musb_es" - config CHERRYUSB_DEVICE_MUSB_SUNXI - bool "musb_sunxi" - config CHERRYUSB_DEVICE_MUSB_BK - bool "musb_bk" - config CHERRYUSB_DEVICE_MUSB_CUSTOM - bool "musb_custom" - config CHERRYUSB_DEVICE_BL - bool "bouffalo" - config CHERRYUSB_DEVICE_HPM - bool "hpm" - config CHERRYUSB_DEVICE_AIC - bool "aic" - config CHERRYUSB_DEVICE_CH32 - bool "ch32" - config CHERRYUSB_DEVICE_PUSB2 - bool "pusb2" - endchoice - - config CHERRYUSB_DEVICE_CDC_ACM - bool - prompt "Enable usb cdc acm device" - default n - - config CHERRYUSB_DEVICE_HID - bool - prompt "Enable usb hid device" - default n - - config CHERRYUSB_DEVICE_MSC - bool - prompt "Enable usb msc device" - default n - - config CHERRYUSB_DEVICE_AUDIO - bool - prompt "Enable usb audio device" - default n - - config CHERRYUSB_DEVICE_VIDEO - bool - prompt "Enable usb video device" - default n - - config CHERRYUSB_DEVICE_CDC_RNDIS - bool - prompt "Enable usb cdc rndis device" - default n - - config CHERRYUSB_DEVICE_CDC_ECM - bool - prompt "Enable usb cdc ecm device" - default n - - config CHERRYUSB_DEVICE_CDC_NCM - bool - prompt "Enable usb cdc ncm device" - default n - - config CHERRYUSB_DEVICE_DFU - bool - prompt "Enable usb dfu device" - default n - - choice - prompt "Select usb device template" - default CHERRYUSB_DEVICE_TEMPLATE - config CHERRYUSB_DEVICE_TEMPLATE_NONE - bool "none (Implement it yourself)" - config CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM - bool "cdc_acm" - config CHERRYUSB_DEVICE_TEMPLATE_MSC - bool "msc" - config CHERRYUSB_DEVICE_TEMPLATE_HID_KEYBOARD - bool "hid_keyboard" - config CHERRYUSB_DEVICE_TEMPLATE_HID_MOUSE - bool "hid_mouse" - config CHERRYUSB_DEVICE_TEMPLATE_HID_CUSTOM - bool "hid_custom" - config CHERRYUSB_DEVICE_TEMPLATE_VIDEO - bool "video" - config CHERRYUSB_DEVICE_TEMPLATE_AUDIO_V1_MIC_SPEAKER - bool "audio_v1_mic_speaker_multichan" - config CHERRYUSB_DEVICE_TEMPLATE_AUDIO_V2_MIC_SPEAKER - bool "audio_v2_mic_speaker_multichan" - config CHERRYUSB_DEVICE_TEMPLATE_CDC_RNDIS - bool "cdc_rndis" - config CHERRYUSB_DEVICE_TEMPLATE_CDC_ECM - bool "cdc_ecm" - config CHERRYUSB_DEVICE_TEMPLATE_CDC_NCM - bool "cdc_ncm" - config CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_MSC - bool "cdc_acm_msc" - config CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_MSC_HID - bool "cdc_acm_msc_hid" - config CHERRYUSB_DEVICE_TEMPLATE_WINUSBV1 - bool "winusbv1" - config CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2_CDC - bool "winusbv2_cdc" - config CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2_HID - bool "winusbv2_hid" - endchoice - - endif - - menuconfig CHERRYUSB_HOST - bool "Enable usb host mode" - default n - - if CHERRYUSB_HOST - choice - prompt "Select usb host ip, and some ip need config in usb_config.h, please check" - default CHERRYUSB_HOST_CUSTOM - config CHERRYUSB_HOST_CUSTOM - bool "CUSTOM (Implement it yourself)" - config CHERRYUSB_HOST_EHCI_BL - bool "ehci_bouffalo" - config CHERRYUSB_HOST_EHCI_HPM - bool "ehci_hpm" - config CHERRYUSB_HOST_EHCI_AIC - bool "ehci_aic" - config CHERRYUSB_HOST_EHCI_NUVOTON_NUC980 - bool "ehci_nuvoton_nuc980" - config CHERRYUSB_HOST_EHCI_NUVOTON_MA35D0 - bool "ehci_nuvoton_ma35d0" - config CHERRYUSB_HOST_EHCI_CUSTOM - bool "ehci_custom" - config CHERRYUSB_HOST_DWC2_ST - bool "dwc2_st" - config CHERRYUSB_HOST_DWC2_ESP - bool "dwc2_esp" - config CHERRYUSB_HOST_DWC2_HC - bool "dwc2_hc" - config CHERRYUSB_HOST_DWC2_CUSTOM - bool "dwc2_custom" - config CHERRYUSB_HOST_MUSB_ES - bool "musb_es" - config CHERRYUSB_HOST_MUSB_SUNXI - bool "musb_sunxi" - config CHERRYUSB_HOST_MUSB_BK - bool "musb_bk" - config CHERRYUSB_HOST_MUSB_CUSTOM - bool "musb_custom" - config CHERRYUSB_HOST_PUSB2 - bool "pusb2" - config CHERRYUSB_HOST_XHCI - bool "xhci" - endchoice - - config CHERRYUSB_HOST_CDC_ACM - bool - prompt "Enable usb cdc acm driver" - default n - - config CHERRYUSB_HOST_HID - bool - prompt "Enable usb hid driver" - default n - - config CHERRYUSB_HOST_MSC - bool - prompt "Enable usb msc driver" - default n - - config CHERRYUSB_HOST_CDC_ECM - bool - prompt "Enable usb cdc ecm driver" - select USBHOST_PLATFORM_CDC_ECM - default n - - config CHERRYUSB_HOST_CDC_RNDIS - bool - prompt "Enable usb rndis driver" - select USBHOST_PLATFORM_CDC_RNDIS - default n - - config CHERRYUSB_HOST_CDC_NCM - bool - prompt "Enable usb cdc ncm driver" - select USBHOST_PLATFORM_CDC_NCM - default n - - config CHERRYUSB_HOST_VIDEO - bool - prompt "Enable usb video driver, it is commercial charge" - default n - - config CHERRYUSB_HOST_AUDIO - bool - prompt "Enable usb audio driver, it is commercial charge" - default n - - config CHERRYUSB_HOST_BLUETOOTH - bool - prompt "Enable usb bluetooth driver" - default n - - config CHERRYUSB_HOST_ASIX - bool - prompt "Enable usb asix driver" - select USBHOST_PLATFORM_ASIX - default n - - config CHERRYUSB_HOST_RTL8152 - bool - prompt "Enable usb rtl8152 driver" - select USBHOST_PLATFORM_RTL8152 - default n - - config CHERRYUSB_HOST_FTDI - bool - prompt "Enable usb ftdi driver" - default n - - config CHERRYUSB_HOST_CH34X - bool - prompt "Enable usb ch34x driver" - default n - - config CHERRYUSB_HOST_CP210X - bool - prompt "Enable usb cp210x driver" - default n - - config CHERRYUSB_HOST_PL2303 - bool - prompt "Enable usb pl2303 driver" - default n - - config USBHOST_PLATFORM_CDC_ECM - bool - - config USBHOST_PLATFORM_CDC_RNDIS - bool - - config USBHOST_PLATFORM_CDC_NCM - bool - - config USBHOST_PLATFORM_ASIX - bool - - config USBHOST_PLATFORM_RTL8152 - bool - - config CHERRYUSB_HOST_TEMPLATE - bool - prompt "Use usb host template" - default n - - if CHERRYUSB_HOST_TEMPLATE - config TEST_USBH_CDC_ACM - int - prompt "demo for test cdc acm" - default 0 - depends on CHERRYUSB_HOST_CDC_ACM - config TEST_USBH_HID - int - prompt "demo for test hid" - default 0 - depends on CHERRYUSB_HOST_HID - config TEST_USBH_MSC - int - prompt "demo for test msc" - default 0 - depends on CHERRYUSB_HOST_MSC - endif - endif - -endif diff --git a/rt-thread/components/drivers/usb/cherryusb/README.md b/rt-thread/components/drivers/usb/cherryusb/README.md index d79df88..e56946a 100644 --- a/rt-thread/components/drivers/usb/cherryusb/README.md +++ b/rt-thread/components/drivers/usb/cherryusb/README.md @@ -1,6 +1,16 @@ -# CherryUSB +

CherryUSB

+

+ + + + +

-[中文版](./README_zh.md) +

+ 中文 + | + English +

CherryUSB is a tiny, beautiful and portable USB host and device stack for embedded system with USB IP. @@ -17,12 +27,12 @@ In order to make it easier for users to learn USB basics, enumeration, driver lo - Class-drivers and porting-drivers are templating and simplification - Clear API classification (slave: initialisation, registration api, command callback api, data sending and receiving api; host: initialisation, lookup api, data sending and receiving api) -### Easy to use USB +### Easy to use USB In order to facilitate the use of the USB interface and to take into account the fact that users have learned about uart and dma, the following advantages have been designed for the data sending and receiving class of interface: - Equivalent to using uart tx dma/uart rx dma -- There is no limit to the length of send and receive, the user does not need to care about the USB packetization process (the porting driver does the packetization process) +- There is no limit to the length of send and receive, the user does not need to care about the USB packetization process (the porting driver does it) ### Easy to bring out USB performance @@ -32,9 +42,9 @@ Taking into account USB performance issues and trying to achieve the theoretical - Memory zero copy - If IP has DMA then uses DMA mode (DMA with hardware packetization) - Unlimited length make it easier to interface with hardware DMA and take advantage of DMA -- Subcontracting function is handled in interrupt +- Packetization is handled in interrupt -## Directoy Structure +## Directory Structure | Directory | Description | |:-------------:|:---------------------------:| @@ -65,21 +75,23 @@ CherryUSB Device Stack has the following functions: - Support Device Firmware Upgrade CLASS (DFU) - Support USB MIDI CLASS (MIDI) - Support Remote NDIS (RNDIS) -- Support WINUSB1.0、WINUSB2.0(with BOS) +- Support WINUSB1.0、WINUSB2.0、WEBUSB、BOS - Support Vendor class +- Support UF2 +- Support Android Debug Bridge (Only support shell) - Support multi device with the same USB IP CherryUSB Device Stack resource usage (GCC 10.2 with -O2): | file | FLASH (Byte) | No Cache RAM (Byte) | RAM (Byte) | Heap (Byte) | |:-------------:|:--------------:|:-------------------------:|:-------------:|:----------------:| -|usbd_core.c | 3516 | 256(default) + 320 | 0 | 0 | -|usbd_cdc.c | 392 | 0 | 0 | 0 | -|usbd_msc.c | 2839 | 128 + 512(default) | 16 | 0 | -|usbd_hid.c | 364 | 0 | 0 | 0 | -|usbd_audio.c | 1455 | 0 | 0 | 0 | -|usbd_video.c | 2494 | 0 | 84 | 0 | -|usbd_rndis.c | 2109 | 3340 | 76 | 0 | +|usbd_core.c | ~4400 | 512(default) + 320 | 0 | 0 | +|usbd_cdc_acm.c | ~400 | 0 | 0 | 0 | +|usbd_msc.c | ~3800 | 128 + 512(default) | 16 | 0 | +|usbd_hid.c | ~360 | 0 | 0 | 0 | +|usbd_audio.c | ~1500 | 0 | 0 | 0 | +|usbd_video.c | ~2600 | 0 | 84 | 0 | +|usbd_rndis.c | ~2100 | 2 * 1580(default)+156+8 | 76 | 0 | ## Host Stack Overview @@ -87,6 +99,7 @@ The CherryUSB Host Stack has a standard enumeration implementation for devices m CherryUSB Host Stack has the following functions: +- Support low speed, full speed, high speed and super speed devices - Automatic loading of supported Class drivers - Support blocking transfers and asynchronous transfers - Support Composite Device @@ -98,8 +111,9 @@ CherryUSB Host Stack has the following functions: - Support USB Audio CLASS (UAC1.0) - Support Remote NDIS (RNDIS) - Support USB Bluetooth class (support nimble and zephyr bluetooth stack, support **CLASS:0xE0** or vendor class like cdc acm) -- Support Vendor class +- Support Vendor class (serial, net, wifi) - Support USB modeswitch +- Support Android Open Accessory - Support multi host with the same USB IP The CherryUSB Host stack also provides the lsusb function, which allows you to view information about all mounted devices, including those on external hubs, with the help of a shell plugin. @@ -108,16 +122,17 @@ CherryUSB Host Stack resource usage (GCC 10.2 with -O2): | file | FLASH (Byte) | No Cache RAM (Byte) | RAM (Byte) | Heap (Byte) | |:-------------:|:--------------:|:-------------------------------:|:---------------------------:|:------------:| -|usbh_core.c | ~7700 | 512 + 8 * (1+x) *n | 28 | 0 | -|usbh_hub.c | ~5600 | 32 + 4* (1+x) | 12 + sizeof(struct usbh_hub) * (1+x) | 0 | -|usbh_cdc_acm.c | ~1200 | 7 | 4 + sizeof(struct usbh_cdc_acm) * x | 0 | -|usbh_msc.c | ~2500 | 32 | 4 + sizeof(struct usbh_msc) * x | 0 | -|usbh_hid.c | ~1000 | 128 | 4 + sizeof(struct usbh_hid) * x | 0 | -|usbh_video.c | ~3700 | 128 | 4 + sizeof(struct usbh_video) * x | 0 | -|usbh_audio.c | ~3100 | 128 | 4 + sizeof(struct usbh_audio) * x | 0 | -|usbh_rndis.c | ~3900 | 4096 + 2 * 2048(default)| sizeof(struct usbh_rndis) * 1 | 0 | -|usbh_cdc_ecm.c | ~2500 | 2 * 1514 | sizeof(struct usbh_cdc_ecm) * 1 | 0 | -|usbh_bluetooth.c | ~2300 | 2 * 2048(default) | sizeof(struct usbh_bluetooth) * 1 | 0 | +|usbh_core.c | ~9000 | 512 + 8 * (1+x) *n | 28 | raw_config_desc | +|usbh_hub.c | ~6000 | 32 + 4 * (1+x) | 12 + sizeof(struct usbh_hub) * (1+x) | 0 | +|usbh_cdc_acm.c | ~900 | 7 | 4 + sizeof(struct usbh_cdc_acm) * x | 0 | +|usbh_msc.c | ~2700 | 64 | 4 + sizeof(struct usbh_msc) * x | 0 | +|usbh_hid.c | ~1400 | 256 | 4 + sizeof(struct usbh_hid) * x | 0 | +|usbh_video.c | ~3800 | 128 | 4 + sizeof(struct usbh_video) * x | 0 | +|usbh_audio.c | ~4100 | 128 | 4 + sizeof(struct usbh_audio) * x | 0 | +|usbh_rndis.c | ~4200 | 512 + 2 * 2048(default)| sizeof(struct usbh_rndis) * 1 | 0 | +|usbh_cdc_ecm.c | ~2200 | 2 * 1514 + 16 | sizeof(struct usbh_cdc_ecm) * 1 | 0 | +|usbh_cdc_ncm.c | ~3300 | 2 * 2048(default) + 16 + 32 | sizeof(struct usbh_cdc_ncm) * 1 | 0 | +|usbh_bluetooth.c | ~1000 | 2 * 2048(default) | sizeof(struct usbh_bluetooth) * 1 | 0 | Among them, `sizeof(struct usbh_hub)` and `sizeof(struct usbh_hubport)` are affected by the following macros: @@ -145,13 +160,14 @@ Only standard and commercial USB IP are listed. | IP | device | host | Support status | |:----------------:|:----------:|:--------:|:--------------:| -| OHCI(intel) | none | OHCI | × | +| OHCI(intel) | none | OHCI | √ | | EHCI(intel) | none | EHCI | √ | | XHCI(intel) | none | XHCI | √ | | UHCI(intel) | none | UHCI | × | | DWC2(synopsys) | DWC2 | DWC2 | √ | | MUSB(mentor) | MUSB | MUSB | √ | | FOTG210(faraday)| FOTG210 | EHCI | √ | +| CHIPIDEA(synopsys)| CHIPIDEA | EHCI | √ | | CDNS2(cadence) | CDNS2 | CDNS2 | √ | | CDNS3(cadence) | CDNS3 | XHCI | × | | DWC3(synopsys) | DWC3 | XHCI | × | @@ -175,17 +191,27 @@ USB basic concepts and how the CherryUSB Device stack is implemented, see [Cherr |Bouffalolab | BL702/BL616/BL808 | bouffalolab/ehci|[bouffalo_sdk](https://github.com/CherryUSB/bouffalo_sdk)|<= latest | Long-term | |ST | STM32F1x | fsdev |[stm32_repo](https://github.com/CherryUSB/cherryusb_stm32)|<= latest | Long-term | |ST | STM32F4/STM32H7 | dwc2 |[stm32_repo](https://github.com/CherryUSB/cherryusb_stm32)|<= latest | Long-term | -|HPMicro | HPM6750 | hpm/ehci |[hpm_sdk](https://github.com/CherryUSB/hpm_sdk)|<= latest | Long-term | +|HPMicro | HPM6000/HPM5000 | hpm/ehci |[hpm_sdk](https://github.com/CherryUSB/hpm_sdk)|<= latest | Long-term | |Essemi | ES32F36xx | musb |[es32f369_repo](https://github.com/CherryUSB/cherryusb_es32)|<= latest | Long-term | -|Phytium | e2000 | pusb2/xhci |[phytium_repo](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk)|v0.10.2 | Long-term | +|Phytium | e2000 | pusb2/xhci |[phytium_repo](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk)|>=1.4.0 | Long-term | |Artinchip | d12x/d13x/d21x | aic/ehci/ohci |[luban-lite](https://gitee.com/artinchip/luban-lite)|<= latest | Long-term | -|Espressif | esp32s2/esp32s3 | dwc2 |[esp32_repo](https://github.com/CherryUSB/cherryusb_esp32)|<= latest | the same with ST | -|AllwinnerTech | F1C100S/F1C200S | musb |[cherryusb_rtt_f1c100s](https://github.com/CherryUSB/cherryusb_rtt_f1c100s)|<= latest | the same with Essemi | -|Bekencorp | bk7256/bk7258 | musb |[bk_idk](https://github.com/CherryUSB/bk_idk)| v0.7.0 | the same with Essemi | -|Sophgo | cv18xx | dwc2 |[cvi_alios_open](https://github.com/CherryUSB/cvi_alios_open)| v0.7.0 | the same with ST | +|Espressif | esp32s2/esp32s3/esp32p4 | dwc2 |[esp32_repo](https://github.com/CherryUSB/cherryusb_esp32)|<= latest | Long-term | +|NXP | mcx | kinetis/chipidea/ehci |[nxp_mcx_repo](https://github.com/CherryUSB/cherryusb_mcx)|<= latest | Long-term | +|Kendryte | k230 | dwc2 |[k230_repo](https://github.com/CherryUSB/canmv_k230)|v1.2.0 | Long-term | +|AllwinnerTech | F1C100S/F1C200S | musb |[cherryusb_rtt_f1c100s](https://github.com/CherryUSB/cherryusb_rtt_f1c100s)|<= latest | the same with musb | +|Bekencorp | bk7256/bk7258 | musb |[bk_idk](https://github.com/CherryUSB/bk_idk)| v0.7.0 | the same with musb | +|Sophgo | cv18xx | dwc2 |[cvi_alios_open](https://github.com/CherryUSB/cvi_alios_open)| v0.7.0 | TBD | |WCH | CH32V307/ch58x | ch32_usbfs/ch32_usbhs/ch58x |[wch_repo](https://github.com/CherryUSB/cherryusb_wch)|<= v0.10.2 | TBD | |Raspberry pi | rp2040 | rp2040 |[pico-examples](https://github.com/CherryUSB/pico-examples)|<= v0.10.2 | No more updated | +## Package Support + +CherryUSB package is available as follows: + +- [RT-Thread](https://packages.rt-thread.org/detail.html?package=CherryUSB) +- [YOC](https://www.xrvm.cn/document?temp=usb-host-protocol-stack-device-driver-adaptation-instructions&slug=yocbook) +- [ESP-Registry](https://components.espressif.com/components/cherry-embedded/cherryusb) + ## Commercial Support Refer to https://cherryusb.readthedocs.io/zh-cn/latest/support/index.html. @@ -198,4 +224,4 @@ CherryUSB discord: https://discord.com/invite/wFfvrSAey8. Thanks to the following companies for their support (in no particular order). - \ No newline at end of file + diff --git a/rt-thread/components/drivers/usb/cherryusb/README_zh.md b/rt-thread/components/drivers/usb/cherryusb/README_zh.md index 0aee5a7..50d2b6f 100644 --- a/rt-thread/components/drivers/usb/cherryusb/README_zh.md +++ b/rt-thread/components/drivers/usb/cherryusb/README_zh.md @@ -1,6 +1,16 @@ -# CherryUSB +

CherryUSB

+

+ + + + +

-[English](./README.md) +

+ 中文 + | + English +

CherryUSB 是一个小而美的、可移植性高的、用于嵌入式系统(带 USB IP)的 USB 主从协议栈。 @@ -22,7 +32,7 @@ CherryUSB 是一个小而美的、可移植性高的、用于嵌入式系统(带 为了方便用户使用 USB 接口,考虑到用户学习过 uart 和 dma,因此,设计的数据收发类接口具备以下优点: - 等价于使用 uart tx dma/uart rx dma -- 收发长度没有限制,用户不需要关心 USB 分包过程(porting 驱动做分包过程) +- 收发长度没有限制,用户不需要关心 USB 分包过程(分包过程在 porting 中处理) ### 易于发挥 USB 性能 @@ -32,7 +42,7 @@ CherryUSB 是一个小而美的、可移植性高的、用于嵌入式系统(带 - Memory zero copy - IP 如果带 DMA 则使用 DMA 模式(DMA 带硬件分包功能) - 长度无限制,方便对接硬件 DMA 并且发挥 DMA 的优势 -- 分包功能在中断中处理 +- 分包过程在中断中执行 ## 目录结构 @@ -65,21 +75,23 @@ CherryUSB Device 协议栈当前实现以下功能: - 支持 Device Firmware Upgrade CLASS (DFU) - 支持 USB MIDI CLASS (MIDI) - 支持 Remote NDIS (RNDIS) -- 支持 WINUSB1.0、WINUSB2.0(带 BOS ) +- 支持 WINUSB1.0、WINUSB2.0、WEBUSB、BOS - 支持 Vendor 类 class +- 支持 UF2 +- 支持 Android Debug Bridge (Only support shell) - 支持相同 USB IP 的多从机 CherryUSB Device 协议栈资源占用说明(GCC 10.2 with -O2): | file | FLASH (Byte) | No Cache RAM (Byte) | RAM (Byte) | Heap (Byte) | |:-------------:|:--------------:|:-------------------------:|:-------------:|:----------------:| -|usbd_core.c | 3516 | 256(default) + 320 | 0 | 0 | -|usbd_cdc.c | 392 | 0 | 0 | 0 | -|usbd_msc.c | 2839 | 128 + 512(default) | 16 | 0 | -|usbd_hid.c | 364 | 0 | 0 | 0 | -|usbd_audio.c | 1455 | 0 | 0 | 0 | -|usbd_video.c | 2494 | 0 | 84 | 0 | -|usbd_rndis.c | 2109 | 3340 | 76 | 0 | +|usbd_core.c | ~4400 | 512(default) + 320 | 0 | 0 | +|usbd_cdc_acm.c | ~400 | 0 | 0 | 0 | +|usbd_msc.c | ~3800 | 128 + 512(default) | 16 | 0 | +|usbd_hid.c | ~360 | 0 | 0 | 0 | +|usbd_audio.c | ~1500 | 0 | 0 | 0 | +|usbd_video.c | ~2600 | 0 | 84 | 0 | +|usbd_rndis.c | ~2100 | 2 * 1580(default)+156+8 | 76 | 0 | ## Host 协议栈简介 @@ -87,6 +99,7 @@ CherryUSB Host 协议栈对挂载在 roothub、外部 hub 上的设备规范了 CherryUSB Host 协议栈当前实现以下功能: +- 支持 low speed, full speed, high speed 和 super speed 设备 - 自动加载支持的Class 驱动 - 支持阻塞式传输和异步传输 - 支持复合设备 @@ -98,8 +111,9 @@ CherryUSB Host 协议栈当前实现以下功能: - Support USB Audio CLASS (UAC1.0) - 支持 Remote NDIS (RNDIS) - 支持 USB Bluetooth (支持 nimble and zephyr bluetooth 协议栈,支持 **CLASS: 0xE0** 或者厂家自定义类,类似于 cdc acm 功能) -- 支持 Vendor 类 class +- 支持 Vendor 类 class (serial, net, wifi) - 支持 USB modeswitch +- 支持 Android Open Accessory - 支持相同 USB IP 的多主机 同时,CherryUSB Host 协议栈还提供了 lsusb 的功能,借助 shell 插件可以查看所有挂载设备的信息,包括外部 hub 上的设备的信息。 @@ -108,16 +122,17 @@ CherryUSB Host 协议栈资源占用说明(GCC 10.2 with -O2): | file | FLASH (Byte) | No Cache RAM (Byte) | RAM (Byte) | Heap (Byte) | |:-------------:|:--------------:|:-------------------------------:|:---------------------------:|:------------:| -|usbh_core.c | ~7700 | 512 + 8 * (1+x) *n | 28 | 0 | -|usbh_hub.c | ~5600 | 32 + 4* (1+x) | 12 + sizeof(struct usbh_hub) * (1+x) | 0 | -|usbh_cdc_acm.c | ~1200 | 7 | 4 + sizeof(struct usbh_cdc_acm) * x | 0 | -|usbh_msc.c | ~2500 | 32 | 4 + sizeof(struct usbh_msc) * x | 0 | -|usbh_hid.c | ~1000 | 128 | 4 + sizeof(struct usbh_hid) * x | 0 | -|usbh_video.c | ~3700 | 128 | 4 + sizeof(struct usbh_video) * x | 0 | -|usbh_audio.c | ~3100 | 128 | 4 + sizeof(struct usbh_audio) * x | 0 | -|usbh_rndis.c | ~3900 | 4096 + 2 * 2048(default)| sizeof(struct usbh_rndis) * 1 | 0 | -|usbh_cdc_ecm.c | ~2500 | 2 * 1514 | sizeof(struct usbh_cdc_ecm) * 1 | 0 | -|usbh_bluetooth.c | ~2300 | 2 * 2048(default) | sizeof(struct usbh_bluetooth) * 1 | 0 | +|usbh_core.c | ~9000 | 512 + 8 * (1+x) *n | 28 | raw_config_desc | +|usbh_hub.c | ~6000 | 32 + 4 * (1+x) | 12 + sizeof(struct usbh_hub) * (1+x) | 0 | +|usbh_cdc_acm.c | ~900 | 7 | 4 + sizeof(struct usbh_cdc_acm) * x | 0 | +|usbh_msc.c | ~2700 | 64 | 4 + sizeof(struct usbh_msc) * x | 0 | +|usbh_hid.c | ~1400 | 256 | 4 + sizeof(struct usbh_hid) * x | 0 | +|usbh_video.c | ~3800 | 128 | 4 + sizeof(struct usbh_video) * x | 0 | +|usbh_audio.c | ~4100 | 128 | 4 + sizeof(struct usbh_audio) * x | 0 | +|usbh_rndis.c | ~4200 | 512 + 2 * 2048(default)| sizeof(struct usbh_rndis) * 1 | 0 | +|usbh_cdc_ecm.c | ~2200 | 2 * 1514 + 16 | sizeof(struct usbh_cdc_ecm) * 1 | 0 | +|usbh_cdc_ncm.c | ~3300 | 2 * 2048(default) + 16 + 32 | sizeof(struct usbh_cdc_ncm) * 1 | 0 | +|usbh_bluetooth.c | ~1000 | 2 * 2048(default) | sizeof(struct usbh_bluetooth) * 1 | 0 | 其中,`sizeof(struct usbh_hub)` 和 `sizeof(struct usbh_hubport)` 受以下宏影响: @@ -145,13 +160,14 @@ x 受以下宏影响: | IP | device | host | Support status | |:----------------:|:----------:|:--------:|:--------------:| -| OHCI(intel) | none | OHCI | × | +| OHCI(intel) | none | OHCI | √ | | EHCI(intel) | none | EHCI | √ | | XHCI(intel) | none | XHCI | √ | | UHCI(intel) | none | UHCI | × | | DWC2(synopsys) | DWC2 | DWC2 | √ | | MUSB(mentor) | MUSB | MUSB | √ | | FOTG210(faraday)| FOTG210 | EHCI | √ | +| CHIPIDEA(synopsys)| CHIPIDEA | EHCI | √ | | CDNS2(cadence) | CDNS2 | CDNS2 | √ | | CDNS3(cadence) | CDNS3 | XHCI | × | | DWC3(synopsys) | DWC3 | XHCI | × | @@ -176,20 +192,30 @@ CherryUSB 快速入门、USB 基本概念,API 手册,Class 基本概念和 |Bouffalolab | BL702/BL616/BL808 | bouffalolab/ehci|[bouffalo_sdk](https://github.com/CherryUSB/bouffalo_sdk)|<= latest | Long-term | |ST | STM32F1x | fsdev |[stm32_repo](https://github.com/CherryUSB/cherryusb_stm32)|<= latest | Long-term | |ST | STM32F4/STM32H7 | dwc2 |[stm32_repo](https://github.com/CherryUSB/cherryusb_stm32)|<= latest | Long-term | -|HPMicro | HPM6750 | hpm/ehci |[hpm_sdk](https://github.com/CherryUSB/hpm_sdk)|<= latest | Long-term | +|HPMicro | HPM6000/HPM5000 | hpm/ehci |[hpm_sdk](https://github.com/CherryUSB/hpm_sdk)|<= latest | Long-term | |Essemi | ES32F36xx | musb |[es32f369_repo](https://github.com/CherryUSB/cherryusb_es32)|<= latest | Long-term | -|Phytium | e2000 | pusb2/xhci |[phytium_repo](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk)|v0.10.2 | Long-term | +|Phytium | e2000 | pusb2/xhci |[phytium_repo](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk)|>=1.4.0 | Long-term | |Artinchip | d12x/d13x/d21x | aic/ehci/ohci |[luban-lite](https://gitee.com/artinchip/luban-lite)|<= latest | Long-term | -|Espressif | esp32s2/esp32s3 | dwc2 |[esp32_repo](https://github.com/CherryUSB/cherryusb_esp32)|<= latest | the same with ST | -|AllwinnerTech | F1C100S/F1C200S | musb |[cherryusb_rtt_f1c100s](https://github.com/CherryUSB/cherryusb_rtt_f1c100s)|<= latest | the same with Essemi | -|Bekencorp | bk7256/bk7258 | musb |[bk_idk](https://github.com/CherryUSB/bk_idk)| v0.7.0 | the same with Essemi | -|Sophgo | cv18xx | dwc2 |[cvi_alios_open](https://github.com/CherryUSB/cvi_alios_open)| v0.7.0 | the same with ST | +|Espressif | esp32s2/esp32s3/esp32p4 | dwc2 |[esp32_repo](https://github.com/CherryUSB/cherryusb_esp32)|<= latest | Long-term | +|NXP | mcx | kinetis/chipidea/ehci |[nxp_mcx_repo](https://github.com/CherryUSB/cherryusb_mcx)|<= latest | Long-term | +|Kendryte | k230 | dwc2 |[k230_repo](https://github.com/CherryUSB/canmv_k230)|v1.2.0 | Long-term | +|AllwinnerTech | F1C100S/F1C200S | musb |[cherryusb_rtt_f1c100s](https://github.com/CherryUSB/cherryusb_rtt_f1c100s)|<= latest | the same with musb | +|Bekencorp | bk7256/bk7258 | musb |[bk_idk](https://github.com/CherryUSB/bk_idk)| v0.7.0 | the same with musb | +|Sophgo | cv18xx | dwc2 |[cvi_alios_open](https://github.com/CherryUSB/cvi_alios_open)| v0.7.0 | TBD | |WCH | CH32V307/ch58x | ch32_usbfs/ch32_usbhs/ch58x |[wch_repo](https://github.com/CherryUSB/cherryusb_wch)|<= v0.10.2 | TBD | |Raspberry pi | rp2040 | rp2040 |[pico-examples](https://github.com/CherryUSB/pico-examples)|<= v0.10.2 | No more updated | +## 软件包支持 + +CherryUSB 软件包可以通过以下方式获取: + +- [RT-Thread](https://packages.rt-thread.org/detail.html?package=CherryUSB) +- [YOC](https://www.xrvm.cn/document?temp=usb-host-protocol-stack-device-driver-adaptation-instructions&slug=yocbook) +- [ESP-Registry](https://components.espressif.com/components/cherry-embedded/cherryusb) + ## 商业支持 -参考 https://cherryusb.readthedocs.io/zh-cn/latest/support/index.html。 +参考 https://cherryusb.readthedocs.io/zh-cn/latest/support/index.html 。 ## 联系 @@ -200,4 +226,4 @@ CherryUSB 微信群:与我联系后邀请加入 感谢以下企业支持(顺序不分先后)。 - + diff --git a/rt-thread/components/drivers/usb/cherryusb/SConscript b/rt-thread/components/drivers/usb/cherryusb/SConscript index ee4dc91..58d0aa0 100644 --- a/rt-thread/components/drivers/usb/cherryusb/SConscript +++ b/rt-thread/components/drivers/usb/cherryusb/SConscript @@ -15,6 +15,8 @@ path += [cwd + '/class/vendor/net'] path += [cwd + '/class/vendor/serial'] src = [] +LIBS = [] +LIBPATH = [] CPPDEFINES = [] # USB DEVICE @@ -43,6 +45,9 @@ if GetDepend(['RT_CHERRYUSB_DEVICE']): if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_HC']): src += Glob('port/dwc2/usb_dc_dwc2.c') src += Glob('port/dwc2/usb_glue_hc.c') + if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_KENDRYTE']): + src += Glob('port/dwc2/usb_dc_dwc2.c') + src += Glob('port/dwc2/usb_glue_kendryte.c') if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_CUSTOM']): src += Glob('port/dwc2/usb_dc_dwc2.c') if GetDepend(['RT_CHERRYUSB_DEVICE_MUSB_ES']): @@ -56,6 +61,14 @@ if GetDepend(['RT_CHERRYUSB_DEVICE']): src += Glob('port/musb/usb_glue_bk.c') if GetDepend(['RT_CHERRYUSB_DEVICE_MUSB_CUSTOM']): src += Glob('port/musb/usb_dc_musb.c') + if GetDepend(['RT_CHERRYUSB_DEVICE_KINETIS_MCX']): + src += Glob('port/kinetis/usb_dc_kinetis.c') + src += Glob('port/kinetis/usb_glue_mcx.c') + if GetDepend(['RT_CHERRYUSB_DEVICE_KINETIS_CUSTOM']): + src += Glob('port/kinetis/usb_dc_kinetis.c') + if GetDepend(['RT_CHERRYUSB_DEVICE_CHIPIDEA_MCX']): + src += Glob('port/chipidea/usb_dc_chipidea.c') + src += Glob('port/chipidea/usb_glue_mcx.c') if GetDepend(['RT_CHERRYUSB_DEVICE_BL']): src += Glob('port/bouffalolab/usb_dc_bl.c') if GetDepend(['RT_CHERRYUSB_DEVICE_HPM']): @@ -68,9 +81,18 @@ if GetDepend(['RT_CHERRYUSB_DEVICE']): src += Glob('port/ch32/usb_dc_usbhs.c') else: src += Glob('port/ch32/usb_dc_usbfs.c') + if GetDepend(['RT_CHERRYUSB_DEVICE_PUSB2']): + path += [cwd + '/port/xhci/rt-thread'] + src += Glob('port/pusb2/rt-thread/usb_dc_glue_phytium.c') + if GetDepend(['ARCH_ARMV8']): + LIBPATH = [cwd + '/port/pusb2'] + LIBS = ['libpusb2_dc_a64.a'] + if GetDepend(['ARCH_ARM_CORTEX_A']): + LIBPATH = [cwd + '/port/pusb2'] + LIBS = ['libpusb2_dc_a32_softfp_neon.a'] if GetDepend(['RT_CHERRYUSB_DEVICE_CDC_ACM']): - src += Glob('class/cdc/usbd_cdc.c') + src += Glob('class/cdc/usbd_cdc_acm.c') if GetDepend(['RT_CHERRYUSB_DEVICE_HID']): src += Glob('class/hid/usbd_hid.c') if GetDepend(['RT_CHERRYUSB_DEVICE_MSC']): @@ -140,10 +162,14 @@ if GetDepend(['RT_CHERRYUSB_HOST']): src += Glob('port/ehci/usb_hc_ehci.c') src += Glob('port/ehci/usb_glue_aic.c') src += Glob('port/ohci/usb_hc_ohci.c') - if GetDepend(['RT_CHERRYUSB_HOST_EHCI_NUVOTON_NUC980']): + if GetDepend(['RT_CHERRYUSB_HOST_EHCI_MCX']): + path += [cwd + '/port/chipidea'] + src += Glob('port/ehci/usb_hc_ehci.c') + src += Glob('port/ehci/usb_glue_mcx.c') + if GetDepend(['RT_CHERRYUSB_HOST_EHCI_NUC980']): src += Glob('port/ehci/usb_hc_ehci.c') src += Glob('port/ehci/usb_glue_nuc980.c') - if GetDepend(['RT_CHERRYUSB_HOST_EHCI_NUVOTON_MA35D0']): + if GetDepend(['RT_CHERRYUSB_HOST_EHCI_MA35D0']): src += Glob('port/ehci/usb_hc_ehci.c') src += Glob('port/ehci/usb_glue_ma35d0.c') if GetDepend(['RT_CHERRYUSB_HOST_EHCI_CUSTOM']): @@ -154,6 +180,9 @@ if GetDepend(['RT_CHERRYUSB_HOST']): if GetDepend(['RT_CHERRYUSB_HOST_DWC2_ESP']): src += Glob('port/dwc2/usb_hc_dwc2.c') src += Glob('port/dwc2/usb_glue_esp.c') + if GetDepend(['RT_CHERRYUSB_HOST_DWC2_KENDRYTE']): + src += Glob('port/dwc2/usb_hc_dwc2.c') + src += Glob('port/dwc2/usb_glue_kendryte.c') if GetDepend(['RT_CHERRYUSB_HOST_DWC2_CUSTOM']): src += Glob('port/dwc2/usb_hc_dwc2.c') if GetDepend(['RT_CHERRYUSB_HOST_MUSB_STANDARD']): @@ -169,6 +198,31 @@ if GetDepend(['RT_CHERRYUSB_HOST']): src += Glob('port/musb/usb_glue_bk.c') if GetDepend(['RT_CHERRYUSB_HOST_MUSB_CUSTOM']): src += Glob('port/musb/usb_hc_musb.c') + if GetDepend(['RT_CHERRYUSB_HOST_KINETIS_MCX']): + src += Glob('port/kinetis/usb_hc_kinetis.c') + src += Glob('port/kinetis/usb_glue_mcx.c') + if GetDepend(['RT_CHERRYUSB_HOST_KINETIS_CUSTOM']): + src += Glob('port/kinetis/usb_hc_kinetis.c') + if GetDepend(['RT_CHERRYUSB_HOST_PUSB2']): + path += [cwd + '/port/pusb2/rt-thread'] + src += Glob('port/pusb2/rt-thread/usb_hc_glue_phytium.c') + if GetDepend(['ARCH_ARMV8']): + LIBPATH = [cwd + '/port/pusb2'] + LIBS = ['libpusb2_hc_a64.a'] + if GetDepend(['ARCH_ARM_CORTEX_A']): + LIBPATH = [cwd + '/port/pusb2'] + LIBS = ['libpusb2_hc_a32_softfp_neon.a'] + + if GetDepend(['RT_CHERRYUSB_HOST_XHCI']): + path += [cwd + '/port/xhci/phytium/rt-thread'] + src += Glob('port/xhci/phytium/rt-thread/usb_glue_phytium_plat.c') + src += Glob('port/xhci/phytium/rt-thread/usb_glue_phytium.c') + if GetDepend(['ARCH_ARMV8']): + LIBPATH = [cwd + '/port/xhci/phytium'] + LIBS = ['libxhci_a64.a'] + if GetDepend(['ARCH_ARM_CORTEX_A']): + LIBPATH = [cwd + '/port/xhci/phytium'] + LIBS = ['libxhci_a32_softfp_neon.a'] if GetDepend(['RT_CHERRYUSB_HOST_CDC_ACM']): src += Glob('class/cdc/usbh_cdc_acm.c') @@ -217,7 +271,7 @@ if GetDepend(['RT_CHERRYUSB_HOST']): src += Glob('platform/rtthread/usb_msh.c') src += Glob('platform/rtthread/usb_check.c') -group = DefineGroup('CherryUSB', src, depend = ['RT_USING_CHERRYUSB'], CPPPATH = path, CPPDEFINES = CPPDEFINES) +group = DefineGroup('CherryUSB', src, depend = ['RT_USING_CHERRYUSB'], LIBS = LIBS, LIBPATH=LIBPATH, CPPPATH = path, CPPDEFINES = CPPDEFINES) Return('group') diff --git a/rt-thread/components/drivers/usb/cherryusb/VERSION b/rt-thread/components/drivers/usb/cherryusb/VERSION index 8a0bfa8..340d4ae 100644 --- a/rt-thread/components/drivers/usb/cherryusb/VERSION +++ b/rt-thread/components/drivers/usb/cherryusb/VERSION @@ -1,5 +1,5 @@ VERSION_MAJOR = 1 -VERSION_MINOR = 3 -PATCHLEVEL = 1 +VERSION_MINOR = 4 +PATCHLEVEL = 2 VERSION_TWEAK = 0 EXTRAVERSION = 0 diff --git a/rt-thread/components/drivers/usb/cherryusb/cherryusb.cmake b/rt-thread/components/drivers/usb/cherryusb/cherryusb.cmake index d8451d2..2717170 100644 --- a/rt-thread/components/drivers/usb/cherryusb/cherryusb.cmake +++ b/rt-thread/components/drivers/usb/cherryusb/cherryusb.cmake @@ -36,14 +36,17 @@ ${CMAKE_CURRENT_LIST_DIR}/class/audio ${CMAKE_CURRENT_LIST_DIR}/class/video ${CMAKE_CURRENT_LIST_DIR}/class/wireless ${CMAKE_CURRENT_LIST_DIR}/class/midi +${CMAKE_CURRENT_LIST_DIR}/class/adb ${CMAKE_CURRENT_LIST_DIR}/class/vendor/net ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial +${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi +${CMAKE_CURRENT_LIST_DIR}/class/aoa ) if(CONFIG_CHERRYUSB_DEVICE) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/core/usbd_core.c) - if(CONFIG_CHERRYUSB_DEVICE_CDC) - list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/cdc/usbd_cdc.c) + if(CONFIG_CHERRYUSB_DEVICE_CDC_ACM) + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/cdc/usbd_cdc_acm.c) endif() if(CONFIG_CHERRYUSB_DEVICE_HID) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/hid/usbd_hid.c) @@ -69,6 +72,9 @@ if(CONFIG_CHERRYUSB_DEVICE) if(CONFIG_CHERRYUSB_DEVICE_DFU) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/dfu/usbd_dfu.c) endif() + if(CONFIG_CHERRYUSB_DEVICE_ADB) + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/adb/usbd_adb.c) + endif() if(DEFINED CONFIG_CHERRYUSB_DEVICE_DCD) if("${CONFIG_CHERRYUSB_DEVICE_DCD}" STREQUAL "fsdev") @@ -97,6 +103,12 @@ if(CONFIG_CHERRYUSB_DEVICE) elseif("${CONFIG_CHERRYUSB_DEVICE_DCD}" STREQUAL "musb_bk") list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/musb/usb_dc_musb.c) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/musb/usb_glue_bk.c) + elseif("${CONFIG_CHERRYUSB_DEVICE_DCD}" STREQUAL "chipidea_mcx") + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/chipidea/usb_dc_chipidea.c) + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/chipidea/usb_glue_mcx.c) + elseif("${CONFIG_CHERRYUSB_DEVICE_DCD}" STREQUAL "kinetis_mcx") + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/kinetis/usb_dc_kinetis.c) + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/kinetis/usb_glue_mcx.c) elseif("${CONFIG_CHERRYUSB_DEVICE_DCD}" STREQUAL "hpm") list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/hpm/usb_dc_hpm.c) elseif("${CONFIG_CHERRYUSB_DEVICE_DCD}" STREQUAL "bl") @@ -121,6 +133,9 @@ if(CONFIG_CHERRYUSB_HOST) if(CONFIG_CHERRYUSB_HOST_CDC_ECM) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/cdc/usbh_cdc_ecm.c) endif() + if(CONFIG_CHERRYUSB_HOST_CDC_RNDIS) + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/wireless/usbh_rndis.c) + endif() if(CONFIG_CHERRYUSB_HOST_CDC_NCM) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/cdc/usbh_cdc_ncm.c) endif() @@ -142,18 +157,10 @@ if(CONFIG_CHERRYUSB_HOST) endif() if(CONFIG_CHERRYUSB_HOST_VIDEO) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/video/usbh_video.c) - list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherryrb/chry_ringbuffer.c) - list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrypool/chry_pool.c) - list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrypool/usbh_uvc_queue.c) - list(APPEND cherryusb_incs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherryrb) - list(APPEND cherryusb_incs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrypool) endif() if(CONFIG_CHERRYUSB_HOST_AUDIO) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/audio/usbh_audio.c) endif() - if(CONFIG_CHERRYUSB_HOST_CDC_RNDIS) - list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/wireless/usbh_rndis.c) - endif() if(CONFIG_CHERRYUSB_HOST_BLUETOOTH) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/wireless/usbh_bluetooth.c) @@ -209,6 +216,12 @@ if(CONFIG_CHERRYUSB_HOST) if(CONFIG_CHERRYUSB_HOST_PL2303) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial/usbh_pl2303.c) endif() + if(CONFIG_CHERRYUSB_HOST_BL616) + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi/usbh_bl616.c) + endif() + if(CONFIG_CHERRYUSB_HOST_AOA) + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/aoa/usbh_aoa.c) + endif() if(DEFINED CONFIG_CHERRYUSB_HOST_HCD) if("${CONFIG_CHERRYUSB_HOST_HCD}" STREQUAL "ehci_bouffalo") @@ -226,6 +239,12 @@ if(CONFIG_CHERRYUSB_HOST) #list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/ehci/usb_hc_ehci_iso.c) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/ehci/usb_glue_aic.c) list(APPEND cherryusb_incs ${CMAKE_CURRENT_LIST_DIR}/port/ehci) + elseif("${CONFIG_CHERRYUSB_HOST_HCD}" STREQUAL "ehci_mcx") + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/ehci/usb_hc_ehci.c) + #list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/ehci/usb_hc_ehci_iso.c) + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/ehci/usb_glue_mcx.c) + list(APPEND cherryusb_incs ${CMAKE_CURRENT_LIST_DIR}/port/ehci) + list(APPEND cherryusb_incs ${CMAKE_CURRENT_LIST_DIR}/port/chipidea) elseif("${CONFIG_CHERRYUSB_HOST_HCD}" STREQUAL "ehci_nuvoton") list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/ehci/usb_hc_ehci.c) #list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/ehci/usb_hc_ehci_iso.c) @@ -248,6 +267,9 @@ if(CONFIG_CHERRYUSB_HOST) elseif("${CONFIG_CHERRYUSB_HOST_HCD}" STREQUAL "musb_bk") list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/musb/usb_hc_musb.c) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/musb/usb_glue_bk.c) + elseif("${CONFIG_CHERRYUSB_HOST_HCD}" STREQUAL "kinetis_mcx") + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/kinetis/usb_hc_kinetis.c) + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/kinetis/usb_glue_mcx.c) endif() endif() @@ -260,5 +282,25 @@ if(DEFINED CONFIG_CHERRYUSB_OSAL) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/osal/usb_osal_rtthread.c) elseif("${CONFIG_CHERRYUSB_OSAL}" STREQUAL "yoc") list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/osal/usb_osal_yoc.c) + elseif("${CONFIG_CHERRYUSB_OSAL}" STREQUAL "idf") + list(APPEND cherryusb_incs ${CMAKE_CURRENT_LIST_DIR}/osal/idf) + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/osal/idf/usb_osal_idf.c) + elseif("${CONFIG_CHERRYUSB_OSAL}" STREQUAL "threadx") + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/osal/usb_osal_threadx.c) + endif() +endif() + +if(CONFIG_CHERRYRB) +list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherryrb/chry_ringbuffer.c) +list(APPEND cherryusb_incs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherryrb) +endif() + +if(CONFIG_CHERRYMP) +list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrymp/chry_mempool.c) +list(APPEND cherryusb_incs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrymp) + if("${CONFIG_CHERRYUSB_OSAL}" STREQUAL "freertos") + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrymp/chry_mempool_osal_freertos.c) + elseif("${CONFIG_CHERRYUSB_OSAL}" STREQUAL "rtthread") + list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/third_party/cherrymp/chry_mempool_osal_rtthread.c) endif() endif() \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/cherryusb_config_template.h b/rt-thread/components/drivers/usb/cherryusb/cherryusb_config_template.h index 493f8a4..e7d5230 100644 --- a/rt-thread/components/drivers/usb/cherryusb/cherryusb_config_template.h +++ b/rt-thread/components/drivers/usb/cherryusb/cherryusb_config_template.h @@ -6,16 +6,10 @@ #ifndef CHERRYUSB_CONFIG_H #define CHERRYUSB_CONFIG_H -#define CHERRYUSB_VERSION 0x010301 -#define CHERRYUSB_VERSION_STR "v1.3.1" - /* ================ USB common Configuration ================ */ #define CONFIG_USB_PRINTF(...) printf(__VA_ARGS__) -#define usb_malloc(size) malloc(size) -#define usb_free(ptr) free(ptr) - #ifndef CONFIG_USB_DBG_LEVEL #define CONFIG_USB_DBG_LEVEL USB_DBG_INFO #endif @@ -72,6 +66,10 @@ #define CONFIG_USBDEV_MSC_VERSION_STRING "0.01" #endif +/* move msc read & write from isr to while(1), you should call usbd_msc_polling in while(1) */ +// #define CONFIG_USBDEV_MSC_POLLING + +/* move msc read & write from isr to thread */ // #define CONFIG_USBDEV_MSC_THREAD #ifndef CONFIG_USBDEV_MSC_PRIO @@ -210,6 +208,9 @@ #define CONFIG_USBDEV_EP_NUM 8 #endif +/* When your chip hardware supports high-speed and wants to initialize it in high-speed mode, the relevant IP will configure the internal or external high-speed PHY according to CONFIG_USB_HS. */ +// #define CONFIG_USB_HS + /* ---------------- FSDEV Configuration ---------------- */ //#define CONFIG_USBDEV_FSDEV_PMA_ACCESS 2 // maybe 1 or 2, many chips may have a difference @@ -220,7 +221,7 @@ // #define CONFIG_USB_DWC2_RXALL_FIFO_SIZE (1024 / 4) /* IN Endpoints Max packet Size / 4 */ // #define CONFIG_USB_DWC2_TX0_FIFO_SIZE (64 / 4) -// #define CONFIG_USB_DWC2_TX1_FIFO_SIZE (512 / 4) +// #define CONFIG_USB_DWC2_TX1_FIFO_SIZE (1024 / 4) // #define CONFIG_USB_DWC2_TX2_FIFO_SIZE (64 / 4) // #define CONFIG_USB_DWC2_TX3_FIFO_SIZE (64 / 4) // #define CONFIG_USB_DWC2_TX4_FIFO_SIZE (0 / 4) @@ -229,6 +230,8 @@ // #define CONFIG_USB_DWC2_TX7_FIFO_SIZE (0 / 4) // #define CONFIG_USB_DWC2_TX8_FIFO_SIZE (0 / 4) +// #define CONFIG_USB_DWC2_DMA_ENABLE + /* ---------------- MUSB Configuration ---------------- */ // #define CONFIG_USB_MUSB_SUNXI @@ -268,7 +271,7 @@ * (largest USB packet used / 4) + 1 for status information + 1 transfer complete + * 1 location each for Bulk/Control endpoint for handling NAK/NYET scenario */ -// #define CONFIG_USB_DWC2_RX_FIFO_SIZE ((1012 - CONFIG_USB_DWC2_NPTX_FIFO_SIZE - CONFIG_USB_DWC2_PTX_FIFO_SIZE) / 4) +// #define CONFIG_USB_DWC2_RX_FIFO_SIZE ((1012 - CONFIG_USB_DWC2_NPTX_FIFO_SIZE - CONFIG_USB_DWC2_PTX_FIFO_SIZE)) /* ---------------- MUSB Configuration ---------------- */ // #define CONFIG_USB_MUSB_SUNXI diff --git a/rt-thread/components/drivers/usb/cherryusb/class/adb/usbd_adb.c b/rt-thread/components/drivers/usb/cherryusb/class/adb/usbd_adb.c new file mode 100644 index 0000000..7629ea3 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/adb/usbd_adb.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbd_core.h" +#include "usbd_adb.h" + +#define ADB_OUT_EP_IDX 0 +#define ADB_IN_EP_IDX 1 + +#define ADB_STATE_READ_MSG 0 +#define ADB_STATE_READ_DATA 1 +#define ADB_STATE_WRITE_MSG 2 +#define ADB_STATE_WRITE_DATA 3 +#define ADB_STATE_AWRITE_MSG 4 +#define ADB_STATE_AWRITE_DATA 5 + +#define MAX_PAYLOAD_V1 (4 * 1024) +#define MAX_PAYLOAD_V2 (256 * 1024) +#define MAX_PAYLOAD MAX_PAYLOAD_V1 +#define A_VERSION 0x01000000 + +#define A_SYNC 0x434e5953 +#define A_CNXN 0x4e584e43 +#define A_OPEN 0x4e45504f +#define A_OKAY 0x59414b4f +#define A_CLSE 0x45534c43 +#define A_WRTE 0x45545257 +#define A_AUTH 0x48545541 + +struct adb_msg { + uint32_t command; /* command identifier constant (A_CNXN, ...) */ + uint32_t arg0; /* first argument */ + uint32_t arg1; /* second argument */ + uint32_t data_length; /* length of payload (0 is allowed) */ + uint32_t data_crc32; /* crc32 of data payload */ + uint32_t magic; /* command ^ 0xffffffff */ +}; + +struct adb_packet { + struct adb_msg msg; + uint8_t payload[MAX_PAYLOAD]; +}; + +struct usbd_adb { + uint8_t state; + uint8_t common_state; + uint8_t write_state; + bool writable; + uint32_t localid; + uint32_t shell_remoteid; + uint32_t file_remoteid; +} adb_client; + +static struct usbd_endpoint adb_ep_data[2]; + +USB_NOCACHE_RAM_SECTION struct adb_packet tx_packet; +USB_NOCACHE_RAM_SECTION struct adb_packet rx_packet; + +static inline uint32_t adb_packet_checksum(struct adb_packet *packet) +{ + uint32_t sum = 0; + uint32_t i; + + for (i = 0; i < packet->msg.data_length; ++i) { + sum += (uint32_t)(packet->payload[i]); + } + + return sum; +} + +static uint32_t usbd_adb_get_remoteid(uint32_t localid) +{ + if (localid == ADB_SHELL_LOALID) { + return adb_client.shell_remoteid; + } else { + return adb_client.file_remoteid; + } +} + +static void adb_send_msg(struct adb_packet *packet) +{ + adb_client.common_state = ADB_STATE_WRITE_MSG; + + packet->msg.data_crc32 = adb_packet_checksum(packet); + packet->msg.magic = packet->msg.command ^ 0xffffffff; + + usbd_ep_start_write(0, adb_ep_data[ADB_IN_EP_IDX].ep_addr, (uint8_t *)&packet->msg, sizeof(struct adb_msg)); +} + +static void adb_send_okay(struct adb_packet *packet, uint32_t localid) +{ + packet->msg.command = A_OKAY; + packet->msg.arg0 = localid; + packet->msg.arg1 = usbd_adb_get_remoteid(localid); + packet->msg.data_length = 0; + + adb_send_msg(&tx_packet); +} + +static void adb_send_close(struct adb_packet *packet, uint32_t localid, uint32_t remoteid) +{ + packet->msg.command = A_CLSE; + packet->msg.arg0 = localid; + packet->msg.arg1 = remoteid; + packet->msg.data_length = 0; + + adb_send_msg(&tx_packet); +} + +void usbd_adb_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) +{ + (void)ep; + + if (adb_client.common_state == ADB_STATE_READ_MSG) { + if (nbytes != sizeof(struct adb_msg)) { + USB_LOG_ERR("invalid adb msg size:%d\r\n", nbytes); + return; + } + + USB_LOG_DBG("command:%x arg0:%x arg1:%x len:%d\r\n", + rx_packet.msg.command, + rx_packet.msg.arg0, + rx_packet.msg.arg1, + rx_packet.msg.data_length); + + if (rx_packet.msg.data_length) { + /* setup next out ep read transfer */ + adb_client.common_state = ADB_STATE_READ_DATA; + usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, rx_packet.payload, rx_packet.msg.data_length); + } else { + if (rx_packet.msg.command == A_CLSE) { + adb_client.writable = false; + usbd_adb_notify_write_done(); + USB_LOG_INFO("Close remoteid:%x\r\n", rx_packet.msg.arg0); + } + adb_client.common_state = ADB_STATE_READ_MSG; + /* setup first out ep read transfer */ + usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg)); + } + } else if (adb_client.common_state == ADB_STATE_READ_DATA) { + switch (rx_packet.msg.command) { + case A_SYNC: + + break; + case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ + char *support_feature = "device::" + "ro.product.name=cherryadb;" + "ro.product.model=cherrysh;" + "ro.product.device=cherryadb;" + "features=cmd,shell_v1"; + + tx_packet.msg.command = A_CNXN; + tx_packet.msg.arg0 = A_VERSION; + tx_packet.msg.arg1 = MAX_PAYLOAD; + tx_packet.msg.data_length = strlen(support_feature); + memcpy(tx_packet.payload, support_feature, strlen(support_feature)); + + adb_send_msg(&tx_packet); + + adb_client.writable = false; + break; + case A_OPEN: /* OPEN(local-id, 0, "destination") */ + rx_packet.payload[rx_packet.msg.data_length] = '\0'; + + if (strncmp((const char *)rx_packet.payload, "shell:", 6) == 0) { + adb_client.localid = ADB_SHELL_LOALID; + adb_client.shell_remoteid = rx_packet.msg.arg0; + adb_send_okay(&tx_packet, ADB_SHELL_LOALID); + + USB_LOG_INFO("Open shell service, remoteid:%x\r\n", rx_packet.msg.arg0); + } else if (strncmp((const char *)rx_packet.payload, "sync:", 5) == 0) { + adb_client.localid = ADB_FILE_LOALID; + adb_client.file_remoteid = rx_packet.msg.arg0; + adb_send_okay(&tx_packet, ADB_FILE_LOALID); + USB_LOG_INFO("Open file service, remoteid:%x\r\n", rx_packet.msg.arg0); + } + break; + case A_OKAY: + + break; + case A_CLSE: + + break; + case A_WRTE: /* WRITE(local-id, remote-id, "data") */ + if ((rx_packet.msg.arg0 == adb_client.shell_remoteid) && (rx_packet.msg.arg1 == ADB_SHELL_LOALID)) { + adb_send_okay(&tx_packet, rx_packet.msg.arg1); + } else if ((rx_packet.msg.arg0 == adb_client.file_remoteid) && (rx_packet.msg.arg1 == ADB_FILE_LOALID)) { + adb_send_okay(&tx_packet, rx_packet.msg.arg1); + } else { + adb_send_close(&tx_packet, 0, rx_packet.msg.arg0); + } + break; + case A_AUTH: + + break; + + default: + break; + } + } +} + +void usbd_adb_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes) +{ + (void)ep; + (void)nbytes; + + if (adb_client.common_state == ADB_STATE_WRITE_MSG) { + if (tx_packet.msg.data_length) { + adb_client.common_state = ADB_STATE_WRITE_DATA; + usbd_ep_start_write(busid, adb_ep_data[ADB_IN_EP_IDX].ep_addr, tx_packet.payload, tx_packet.msg.data_length); + } else { + if (rx_packet.msg.command == A_WRTE) { + adb_client.writable = true; + if (adb_client.localid == ADB_SHELL_LOALID) { + usbd_adb_notify_shell_read(rx_packet.payload, rx_packet.msg.data_length); + } else { + } + } + adb_client.common_state = ADB_STATE_READ_MSG; + /* setup first out ep read transfer */ + usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg)); + } + } else if (adb_client.common_state == ADB_STATE_WRITE_DATA) { + adb_client.common_state = ADB_STATE_READ_MSG; + /* setup first out ep read transfer */ + usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg)); + } else if (adb_client.write_state == ADB_STATE_AWRITE_MSG) { + if (tx_packet.msg.data_length) { + adb_client.write_state = ADB_STATE_AWRITE_DATA; + usbd_ep_start_write(busid, adb_ep_data[ADB_IN_EP_IDX].ep_addr, tx_packet.payload, tx_packet.msg.data_length); + } else { + } + } else if (adb_client.write_state == ADB_STATE_AWRITE_DATA) { + usbd_adb_notify_write_done(); + } +} + +void adb_notify_handler(uint8_t busid, uint8_t event, void *arg) +{ + (void)arg; + + switch (event) { + case USBD_EVENT_INIT: + break; + case USBD_EVENT_DEINIT: + break; + case USBD_EVENT_RESET: + break; + case USBD_EVENT_CONFIGURED: + adb_client.common_state = ADB_STATE_READ_MSG; + /* setup first out ep read transfer */ + usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg)); + break; + + default: + break; + } +} + +struct usbd_interface *usbd_adb_init_intf(uint8_t busid, struct usbd_interface *intf, uint8_t in_ep, uint8_t out_ep) +{ + (void)busid; + + intf->class_interface_handler = NULL; + intf->class_endpoint_handler = NULL; + intf->vendor_handler = NULL; + intf->notify_handler = adb_notify_handler; + + adb_ep_data[ADB_OUT_EP_IDX].ep_addr = out_ep; + adb_ep_data[ADB_OUT_EP_IDX].ep_cb = usbd_adb_bulk_out; + adb_ep_data[ADB_IN_EP_IDX].ep_addr = in_ep; + adb_ep_data[ADB_IN_EP_IDX].ep_cb = usbd_adb_bulk_in; + + usbd_add_endpoint(busid, &adb_ep_data[ADB_OUT_EP_IDX]); + usbd_add_endpoint(busid, &adb_ep_data[ADB_IN_EP_IDX]); + + return intf; +} + +bool usbd_adb_can_write(void) +{ + return adb_client.writable; +} + +int usbd_abd_write(uint32_t localid, const uint8_t *data, uint32_t len) +{ + struct adb_packet *packet; + + packet = &tx_packet; + packet->msg.command = A_WRTE; + packet->msg.arg0 = localid; + packet->msg.arg1 = usbd_adb_get_remoteid(localid); + packet->msg.data_length = len; + memcpy(packet->payload, data, len); + + packet->msg.data_crc32 = adb_packet_checksum(packet); + packet->msg.magic = packet->msg.command ^ 0xffffffff; + + adb_client.write_state = ADB_STATE_AWRITE_MSG; + usbd_ep_start_write(0, adb_ep_data[ADB_IN_EP_IDX].ep_addr, (uint8_t *)&packet->msg, sizeof(struct adb_msg)); + return 0; +} + +void usbd_adb_close(uint32_t localid) +{ + adb_send_close(&tx_packet, 0, usbd_adb_get_remoteid(localid)); +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/adb/usbd_adb.h b/rt-thread/components/drivers/usb/cherryusb/class/adb/usbd_adb.h new file mode 100644 index 0000000..4dbc730 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/adb/usbd_adb.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USBD_ADB_H +#define USBD_ADB_H + +#include + +#define ADB_SHELL_LOALID 0x01 +#define ADB_FILE_LOALID 0x02 + +// clang-format off +#define ADB_DESCRIPTOR_INIT(bFirstInterface, in_ep, out_ep, wMaxPacketSize) \ + USB_INTERFACE_DESCRIPTOR_INIT(bFirstInterface, 0x00, 0x02, 0xff, 0x42, 0x01, 0x02), \ + USB_ENDPOINT_DESCRIPTOR_INIT(in_ep, 0x02, wMaxPacketSize, 0x00), \ + USB_ENDPOINT_DESCRIPTOR_INIT(out_ep, 0x02, wMaxPacketSize, 0x00) +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +struct usbd_interface *usbd_adb_init_intf(uint8_t busid, struct usbd_interface *intf, uint8_t in_ep, uint8_t out_ep); + +void usbd_adb_notify_shell_read(uint8_t *data, uint32_t len); +void usbd_adb_notify_file_read(uint8_t *data, uint32_t len); +void usbd_adb_notify_write_done(void); +bool usbd_adb_can_write(void); +int usbd_abd_write(uint32_t localid, const uint8_t *data, uint32_t len); +void usbd_adb_close(uint32_t localid); + +#ifdef __cplusplus +} +#endif + +#endif /* USBD_ADB_H */ \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/aoa/usb_aoa.h b/rt-thread/components/drivers/usb/cherryusb/class/aoa/usb_aoa.h new file mode 100644 index 0000000..6b6c715 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/aoa/usb_aoa.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USB_AOA_H +#define USB_AOA_H + +//AOA 1.0 +#define AOA_ACCESSORY_VENDOR_ID 0x18D1 +#define AOA_ACCESSORY_PRODUCT_ID 0x2D00 +#define AOA_ACCESSORY_ADB_PRODUCT_ID 0x2D01 + +//AOA 2.0 +#define AOA_AUDIO_PRODUCT_ID 0x2D02 +#define AOA_AUDIO_ADB_PRODUCT_ID 0x2D03 +#define AOA_ACCESSORY_AUDIO_PRODUCT_ID 0x2D04 +#define AOA_ACCESSORY_AUDIO_ADB_PRODUCT_ID 0x2D05 + +//AOA 1.0 +#define AOA_ACCESSORY_GET_PROTOCOL 51 +#define AOA_ACCESSORY_SEND_STRING 52 +#define AOA_ACCESSORY_START 53 + +//AOA 2.0 +#define AOA_ACCESSORY_REGISTER_HID 54 +#define AOA_ACCESSORY_UNREGISTER_HID 55 +#define AOA_ACCESSORY_SET_HID_REPORT_DESC 56 +#define AOA_ACCESSORY_SEND_HID_EVENT 57 +#define AOA_ACCESSORY_SET_AUDIO_MODE 58 + +#define AOA_ACCESSORY_STRING_MANUFACTURER 0 +#define AOA_ACCESSORY_STRING_MODEL 1 +#define AOA_ACCESSORY_STRING_DESCRIPTION 2 +#define AOA_ACCESSORY_STRING_VERSION 3 +#define AOA_ACCESSORY_STRING_URI 4 +#define AOA_ACCESSORY_STRING_SERIAL 5 + +struct aoa_string_info { + char acc_manufacturer[64]; + char acc_model[64]; + char acc_description[64]; + char acc_version[64]; + char acc_uri[64]; + char acc_serial[64]; +}; + +#endif /* USB_AOA_H */ \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/aoa/usbh_aoa.c b/rt-thread/components/drivers/usb/cherryusb/class/aoa/usbh_aoa.c new file mode 100644 index 0000000..20cef52 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/aoa/usbh_aoa.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbh_core.h" +#include "usbh_aoa.h" + +#undef USB_DBG_TAG +#define USB_DBG_TAG "usbh_aoa" +#include "usb_log.h" + +#define DEV_FORMAT "/dev/aoa" + +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_aoa_buffer[128]; + +static struct usbh_aoa g_aoa_class; + +int usbh_aoa_switch(struct usbh_hubport *hport, struct aoa_string_info *info) +{ + struct usb_setup_packet *setup; + int ret; + + setup = hport->setup; + + if (setup == NULL) { + return -USB_ERR_INVAL; + } + + USB_LOG_INFO("Try switch into aoa mode\r\n"); + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_GET_PROTOCOL; + setup->wValue = 0; + setup->wIndex = 0; + setup->wLength = 2; + + ret = usbh_control_transfer(hport, setup, g_aoa_buffer); + if (ret < 0) { + return ret; + } + + USB_LOG_INFO("AOA version: v%d.%d\r\n", g_aoa_buffer[0], g_aoa_buffer[1]); + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_SEND_STRING; + setup->wValue = 0; + setup->wIndex = AOA_ACCESSORY_STRING_MANUFACTURER; + setup->wLength = strlen(info->acc_manufacturer) + 1; + + memcpy(g_aoa_buffer, info->acc_manufacturer, strlen(info->acc_manufacturer)); + ret = usbh_control_transfer(hport, setup, g_aoa_buffer); + if (ret < 0) { + return ret; + } + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_SEND_STRING; + setup->wValue = 0; + setup->wIndex = AOA_ACCESSORY_STRING_MODEL; + setup->wLength = strlen(info->acc_model) + 1; + + memcpy(g_aoa_buffer, info->acc_model, strlen(info->acc_model)); + ret = usbh_control_transfer(hport, setup, g_aoa_buffer); + if (ret < 0) { + return ret; + } + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_SEND_STRING; + setup->wValue = 0; + setup->wIndex = AOA_ACCESSORY_STRING_DESCRIPTION; + setup->wLength = strlen(info->acc_description) + 1; + + memcpy(g_aoa_buffer, info->acc_description, strlen(info->acc_description)); + ret = usbh_control_transfer(hport, setup, g_aoa_buffer); + if (ret < 0) { + return ret; + } + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_SEND_STRING; + setup->wValue = 0; + setup->wIndex = AOA_ACCESSORY_STRING_VERSION; + setup->wLength = strlen(info->acc_version) + 1; + + memcpy(g_aoa_buffer, info->acc_version, strlen(info->acc_version)); + ret = usbh_control_transfer(hport, setup, g_aoa_buffer); + if (ret < 0) { + return ret; + } + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_SEND_STRING; + setup->wValue = 0; + setup->wIndex = AOA_ACCESSORY_STRING_URI; + setup->wLength = strlen(info->acc_uri) + 1; + + memcpy(g_aoa_buffer, info->acc_uri, strlen(info->acc_uri)); + ret = usbh_control_transfer(hport, setup, g_aoa_buffer); + if (ret < 0) { + return ret; + } + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_SEND_STRING; + setup->wValue = 0; + setup->wIndex = AOA_ACCESSORY_STRING_SERIAL; + setup->wLength = strlen(info->acc_serial) + 1; + + memcpy(g_aoa_buffer, info->acc_serial, strlen(info->acc_serial)); + ret = usbh_control_transfer(hport, setup, g_aoa_buffer); + if (ret < 0) { + return ret; + } + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_START; + setup->wValue = 0; + setup->wIndex = 0; + setup->wLength = 0; + + ret = usbh_control_transfer(hport, setup, NULL); + if (ret < 0) { + return ret; + } + + USB_LOG_INFO("Switch into aoa mode success, wait usb device restart...\r\n"); + return 0; +} + +int usbh_aoa_register_hid(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *report, uint32_t report_len) +{ + struct usb_setup_packet *setup; + int ret; + uint8_t len; + uint32_t offset; + + if (!aoa_class || !aoa_class->hport) { + return -USB_ERR_INVAL; + } + setup = aoa_class->hport->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_REGISTER_HID; + setup->wValue = id; + setup->wIndex = report_len; + setup->wLength = 0; + + ret = usbh_control_transfer(aoa_class->hport, setup, NULL); + if (ret < 0) { + return ret; + } + + offset = 0; + while (report_len > 0) { + len = report_len > 64 ? 64 : report_len; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_SET_HID_REPORT_DESC; + setup->wValue = id; + setup->wIndex = offset; + setup->wLength = len; + + memcpy(g_aoa_buffer, report + offset, len); + ret = usbh_control_transfer(aoa_class->hport, setup, g_aoa_buffer); + if (ret < 0) { + return ret; + } + offset += len; + report_len -= len; + } + return ret; +} + +int usbh_aoa_send_hid_event(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *event, uint32_t event_len) +{ + struct usb_setup_packet *setup; + int ret; + uint8_t len; + uint32_t offset; + + if (!aoa_class || !aoa_class->hport) { + return -USB_ERR_INVAL; + } + setup = aoa_class->hport->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = AOA_ACCESSORY_SEND_HID_EVENT; + setup->wValue = id; + setup->wIndex = 0; + setup->wLength = event_len; + + memcpy(g_aoa_buffer, event, event_len); + return usbh_control_transfer(aoa_class->hport, setup, event); +} + +static int usbh_aoa_connect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usb_endpoint_descriptor *ep_desc; + int ret = 0; + + struct usbh_aoa *aoa_class = &g_aoa_class; + + memset(aoa_class, 0, sizeof(struct usbh_aoa)); + + aoa_class->hport = hport; + aoa_class->intf = intf; + + hport->config.intf[intf].priv = aoa_class; + + for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) { + ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc; + + if (ep_desc->bEndpointAddress & 0x80) { + USBH_EP_INIT(aoa_class->bulkin, ep_desc); + } else { + USBH_EP_INIT(aoa_class->bulkout, ep_desc); + } + } + + strncpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN); + + USB_LOG_INFO("Register AOA Class:%s\r\n", hport->config.intf[intf].devname); + + usbh_aoa_run(aoa_class); + return 0; +} + +static int usbh_aoa_disconnect(struct usbh_hubport *hport, uint8_t intf) +{ + int ret = 0; + + struct usbh_aoa *aoa_class = (struct usbh_aoa *)hport->config.intf[intf].priv; + + if (aoa_class) { + if (aoa_class->bulkin) { + usbh_kill_urb(&aoa_class->bulkin_urb); + } + + if (aoa_class->bulkout) { + usbh_kill_urb(&aoa_class->bulkout_urb); + } + + if (hport->config.intf[intf].devname[0] != '\0') { + USB_LOG_INFO("Unregister AOA Class:%s\r\n", hport->config.intf[intf].devname); + usbh_aoa_stop(aoa_class); + } + + memset(aoa_class, 0, sizeof(struct usbh_aoa)); + } + + return ret; +} + +__WEAK void usbh_aoa_run(struct usbh_aoa *aoa_class) +{ + (void)aoa_class; +} + +__WEAK void usbh_aoa_stop(struct usbh_aoa *aoa_class) +{ + (void)aoa_class; +} + +static const uint16_t aoa_id_table[][2] = { + { AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_PRODUCT_ID }, + { AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_ADB_PRODUCT_ID }, + { AOA_ACCESSORY_VENDOR_ID, AOA_AUDIO_PRODUCT_ID }, + { AOA_ACCESSORY_VENDOR_ID, AOA_AUDIO_ADB_PRODUCT_ID }, + { AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_AUDIO_PRODUCT_ID }, + { AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_AUDIO_ADB_PRODUCT_ID }, + { 0, 0 }, +}; + +const struct usbh_class_driver aoa_class_driver = { + .driver_name = "aoa", + .connect = usbh_aoa_connect, + .disconnect = usbh_aoa_disconnect +}; + +CLASS_INFO_DEFINE const struct usbh_class_info aoa_intf_class_info = { + .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0x00, + .id_table = aoa_id_table, + .class_driver = &aoa_class_driver +}; \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/aoa/usbh_aoa.h b/rt-thread/components/drivers/usb/cherryusb/class/aoa/usbh_aoa.h new file mode 100644 index 0000000..3b184e8 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/aoa/usbh_aoa.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USBH_AOA_H +#define USBH_AOA_H + +#include "usb_aoa.h" + +struct usbh_aoa { + struct usbh_hubport *hport; + struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */ + struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */ + + struct usbh_urb bulkout_urb; + struct usbh_urb bulkin_urb; + + uint8_t intf; + uint8_t minor; + + void *user_data; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int usbh_aoa_switch(struct usbh_hubport *hport, struct aoa_string_info *info); +int usbh_aoa_register_hid(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *report, uint32_t report_len); +int usbh_aoa_send_hid_event(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *event, uint32_t event_len); + +void usbh_aoa_run(struct usbh_aoa *aoa_class); +void usbh_aoa_stop(struct usbh_aoa *aoa_class); + +#ifdef __cplusplus +} +#endif + +#endif /* USBH_AOA_H */ \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/audio/usb_audio.h b/rt-thread/components/drivers/usb/cherryusb/class/audio/usb_audio.h index 2b3cdaf..ff7a9fe 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/audio/usb_audio.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/audio/usb_audio.h @@ -132,11 +132,11 @@ #define AUDIO_FORMAT_ALAW 0x0004 #define AUDIO_FORMAT_MULAW 0x0005 -#define AUDIO_V2_FORMAT_PCM 0x00000001 -#define AUDIO_V2_FORMAT_PCM8 0x00000002 -#define AUDIO_V2_FORMAT_IEEE_FLOAT 0x00000004 -#define AUDIO_V2_FORMAT_ALAW 0x00000008 -#define AUDIO_V2_FORMAT_MULAW 0x00000010 +#define AUDIO_V2_FORMAT_PCM 0x00000001 +#define AUDIO_V2_FORMAT_PCM8 0x00000002 +#define AUDIO_V2_FORMAT_IEEE_FLOAT 0x00000004 +#define AUDIO_V2_FORMAT_ALAW 0x00000008 +#define AUDIO_V2_FORMAT_MULAW 0x00000010 /* bmChannelConfig: a bitmap field that indicates which spatial locations * are occupied by the channels present in the cluster. The bit allocations @@ -648,6 +648,18 @@ struct audio_cs_if_ac_feature_unit_descriptor { #define AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(ch, n) (7 + (ch + 1) * n) +struct audio_cs_if_ac_selector_unit_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bUnitID; + uint8_t bNrInPins; + uint8_t baSourceID[1]; + uint8_t iSelector; +} __PACKED; + +#define AUDIO_SIZEOF_AC_SELECTOR_UNIT_DESC(n) (6 + n) + struct audio_cs_if_as_general_descriptor { uint8_t bLength; uint8_t bDescriptorType; @@ -812,6 +824,60 @@ struct audio_cs_ep_ep_general_descriptor { #define AUDIO_AS_DESCRIPTOR_INIT_LEN(n) (0x09 + 0x09 + 0x07 + 0x08 + 3 * n + 0x09 + 0x07) +#define AUDIO_AS_ALTSETTING_DESCRIPTOR_INIT(bInterfaceNumber, bAlternateSetting, bTerminalLink, bNrChannels, bSubFrameSize, bBitResolution, bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval, ...) \ + 0x09, /* bLength */ \ + USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ + bInterfaceNumber, /* bInterfaceNumber */ \ + bAlternateSetting, /* bAlternateSetting */ \ + 0x01, /* bNumEndpoints */ \ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \ + AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ \ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ \ + 0x00, /* iInterface */ \ + 0x07, /* bLength */ \ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \ + AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ \ + bTerminalLink, /* bTerminalLink : Unit ID of the Output Terminal*/ \ + 0x01, /* bDelay */ \ + WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag : AUDIO_FORMAT_PCM */ \ + 0x08 + PP_NARG(__VA_ARGS__), /* bLength */ \ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \ + AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ \ + AUDIO_FORMAT_TYPE_I, /* bFormatType */ \ + bNrChannels, /* bNrChannels */ \ + bSubFrameSize, /* bSubFrameSize : Bytes per audio subframe */ \ + bBitResolution, /* bBitResolution : bits per sample */ \ + (PP_NARG(__VA_ARGS__)/3), /* bSamFreqType : only one frequency supported */ \ + __VA_ARGS__, /* tSamFreq : Audio sampling frequency coded on 3 bytes */ \ + 0x09, /* bLength */ \ + USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \ + bEndpointAddress, /* bEndpointAddress : IN endpoint 1 */ \ + bmAttributes, /* bmAttributes */ \ + WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \ + bInterval, /* bInterval : one packet per frame */ \ + 0x00, /* bRefresh */ \ + 0x00, /* bSynchAddress */ \ + 0x07, /* bLength */ \ + AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ \ + AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ \ + AUDIO_EP_CONTROL_SAMPLING_FEQ, /* bmAttributes AUDIO_SAMPLING_FREQ_CONTROL */ \ + 0x00, /* bLockDelayUnits */ \ + 0x00, /* wLockDelay */ \ + 0x00 + +#define AUDIO_AS_ALTSETTING_DESCRIPTOR_INIT_LEN(n) (0x09 + 0x07 + 0x08 + 3 * n + 0x09 + 0x07) + +#define AUDIO_AS_ALTSETTING0_DESCRIPTOR_INIT(bInterfaceNumber) \ + 0x09, /* bLength */ \ + USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ + bInterfaceNumber, /* bInterfaceNumber */ \ + 0x00, /* bAlternateSetting */ \ + 0x01, /* bNumEndpoints */ \ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \ + AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ \ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ \ + 0x00 /* iInterface */ + #define AUDIO_MS_STANDARD_DESCRIPTOR_INIT(bInterfaceNumber, bNumEndpoints) \ 0x09, /* bLength */ \ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ @@ -1096,6 +1162,58 @@ struct audio_v2_control_range3_param_block { 0x00, /* wLockDelay */ \ 0x00 +#define AUDIO_V2_AS_ALTSETTING_DESCRIPTOR_INIT(bInterfaceNumber, bAlternateSetting, bTerminalLink, bNrChannels, bmChannelConfig, bSubslotSize, bBitResolution, bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval) \ + 0x09, /* bLength */ \ + USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ + bInterfaceNumber, /* bInterfaceNumber */ \ + bAlternateSetting, /* bAlternateSetting */ \ + 0x01, /* bNumEndpoints */ \ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \ + AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ \ + AUDIO_PROTOCOLv20, /* bInterfaceProtocol */ \ + 0x00, /* iInterface */ \ + 0x10, /* bLength */ \ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \ + AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ \ + bTerminalLink, /* bTerminalLink : Unit ID of the Output or Input Terminal*/ \ + 0x00, /* bmControls */ \ + AUDIO_FORMAT_TYPE_I, /* bFormatType : AUDIO_FORMAT_TYPE_I */ \ + DBVAL(AUDIO_V2_FORMAT_PCM), /* bmFormats PCM */ \ + bNrChannels, /* bNrChannels */ \ + DBVAL(bmChannelConfig), /* bmChannelConfig */ \ + 0x00, /* iChannelNames */ \ + 0x06, /* bLength */ \ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \ + AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ \ + AUDIO_FORMAT_TYPE_I, /* bFormatType */ \ + bSubslotSize, /* bSubslotSize */ \ + bBitResolution, /* bBitResolution */ \ + 0x07, /* bLength */ \ + USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \ + bEndpointAddress, /* bEndpointAddress 3 out endpoint for Audio */ \ + bmAttributes, /* bmAttributes */ \ + WBVAL(wMaxPacketSize), /* XXXX wMaxPacketSize in Bytes (SampleRate * SlotByteSize * NumChannels) */ \ + bInterval, /* bInterval */ \ + 0x08, /* bLength */ \ + AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ \ + AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ \ + 0x00, /* bmAttributes */ \ + 0x00, /* bmControls */ \ + 0x00, /* bLockDelayUnits */ \ + 0x00, /* wLockDelay */ \ + 0x00 + +#define AUDIO_V2_AS_ALTSETTING0_DESCRIPTOR_INIT(bInterfaceNumber) \ + 0x09, /* bLength */ \ + USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ + bInterfaceNumber, /* bInterfaceNumber */ \ + 0x00, /* bAlternateSetting */ \ + 0x01, /* bNumEndpoints */ \ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \ + AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ \ + AUDIO_PROTOCOLv20, /* bInterfaceProtocol */ \ + 0x00 /* iInterface */ + #define AUDIO_V2_AS_FEEDBACK_DESCRIPTOR_INIT(bInterfaceNumber, bTerminalLink, bNrChannels, bmChannelConfig, bSubslotSize, bBitResolution, bEndpointAddress, wMaxPacketSize, bInterval, bFeedbackEndpointAddress) \ 0x09, /* bLength */ \ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ @@ -1150,12 +1268,13 @@ struct audio_v2_control_range3_param_block { bFeedbackEndpointAddress, /* bFeedbackEndpointAddress Revise Dir to bEndpointAddress */ \ 0x11, /* bmAttributes: TransferType=Isochronous SyncType=None EndpointType=Feedback */ \ WBVAL(4), /* XXXX wMaxPacketSize in Bytes */ \ - bInterval /* bInterval */ \ + bInterval /* bInterval */ // clang-format on -#define AUDIO_V2_AS_DESCRIPTOR_INIT_LEN (0x09 + 0x09 + 0x10 + 0x06 + 0x07 + 0x08) -#define AUDIO_V2_AS_FEEDBACK_DESCRIPTOR_INIT_LEN (0x09 + 0x09 + 0x10 + 0x06 + 0x07 + 0x08 + 0x07) +#define AUDIO_V2_AS_DESCRIPTOR_INIT_LEN (0x09 + 0x09 + 0x10 + 0x06 + 0x07 + 0x08) +#define AUDIO_V2_AS_ALTSETTING_DESCRIPTOR_INIT_LEN (0x09 + 0x10 + 0x06 + 0x07 + 0x08) +#define AUDIO_V2_AS_FEEDBACK_DESCRIPTOR_INIT_LEN (0x09 + 0x09 + 0x10 + 0x06 + 0x07 + 0x08 + 0x07) #define AUDIO_SAMPLE_FREQ_NUM(num) (uint8_t)(num), (uint8_t)((num >> 8)) #define AUDIO_SAMPLE_FREQ_3B(frq) (uint8_t)(frq), (uint8_t)((frq >> 8)), (uint8_t)((frq >> 16)) diff --git a/rt-thread/components/drivers/usb/cherryusb/class/audio/usbd_audio.c b/rt-thread/components/drivers/usb/cherryusb/class/audio/usbd_audio.c index c78c8ed..458f1c1 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/audio/usbd_audio.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/audio/usbd_audio.c @@ -62,7 +62,7 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup setup->bRequest); uint8_t entity_id; - uint8_t ep; + uint8_t ep = 0; uint8_t subtype = 0x01; uint8_t control_selector; uint8_t ch; @@ -136,27 +136,26 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup memcpy(&volume, *data, *len); if (volume < 0x8000) { volume_db = volume / 256; - } else if (volume > 0x8000) { - volume_db = (0xffff - volume + 1) / -256; + } else { + volume_db = (volume - 0x10000) / 256; } - volume_db += 128; /* 0 ~ 255 */ - USB_LOG_DBG("Set ep:0x%02x ch:%d volume:0x%04x\r\n", ep, ch, volume); + USB_LOG_DBG("Set ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db); usbd_audio_set_volume(busid, ep, ch, volume_db); break; case AUDIO_REQUEST_GET_CUR: volume_db = usbd_audio_get_volume(busid, ep, ch); - volume_db -= 128; if (volume_db >= 0) { volume = volume_db * 256; } else { - volume = volume_db * 256 + 0xffff + 1; + volume = volume_db * 256 + 0x10000; } + USB_LOG_DBG("Get ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db); memcpy(*data, &volume, 2); *len = 2; break; case AUDIO_REQUEST_GET_MIN: - (*data)[0] = 0x00; /* -2560/256 dB */ - (*data)[1] = 0xdb; + (*data)[0] = 0x00; /* -100 dB */ + (*data)[1] = 0x9c; *len = 2; break; case AUDIO_REQUEST_GET_MAX: @@ -165,7 +164,7 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup *len = 2; break; case AUDIO_REQUEST_GET_RES: - (*data)[0] = 0x00; /* -256/256 dB */ + (*data)[0] = 0x00; /* 1 dB */ (*data)[1] = 0x01; *len = 2; break; @@ -178,22 +177,31 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup case AUDIO_REQUEST_CUR: if (setup->bmRequestType & USB_REQUEST_DIR_MASK) { volume_db = usbd_audio_get_volume(busid, ep, ch); - volume = volume_db; + if (volume_db >= 0) { + volume = volume_db * 256; + } else { + volume = volume_db * 256 + 0x10000; + } + USB_LOG_DBG("Get ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db); memcpy(*data, &volume, 2); *len = 2; } else { memcpy(&volume, *data, *len); - volume_db = volume; - USB_LOG_DBG("Set ep:0x%02x ch:%d volume:0x%02x\r\n", ep, ch, volume); + if (volume < 0x8000) { + volume_db = volume / 256; + } else { + volume_db = (volume - 0x10000) / 256; + } + USB_LOG_DBG("Set ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db); usbd_audio_set_volume(busid, ep, ch, volume_db); } break; case AUDIO_REQUEST_RANGE: if (setup->bmRequestType & USB_REQUEST_DIR_MASK) { *((uint16_t *)(*data + 0)) = 1; - *((uint16_t *)(*data + 2)) = 0; - *((uint16_t *)(*data + 4)) = 100; - *((uint16_t *)(*data + 6)) = 1; + *((uint16_t *)(*data + 2)) = 0x9c00; /* MIN -100 dB */ + *((uint16_t *)(*data + 4)) = 0x0000; /* MAX 0 dB */ + *((uint16_t *)(*data + 6)) = 0x100; /* RES 1 dB */ *len = 8; } else { } @@ -287,7 +295,7 @@ static void audio_notify_handler(uint8_t busid, uint8_t event, void *arg) } } -struct usbd_interface *usbd_audio_init_intf(uint8_t busid, +struct usbd_interface *usbd_audio_init_intf(uint8_t busid, struct usbd_interface *intf, uint16_t uac_version, struct audio_entity_info *table, @@ -312,33 +320,58 @@ struct usbd_interface *usbd_audio_init_intf(uint8_t busid, return intf; } -__WEAK void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume) +__WEAK void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume_db) { + (void)busid; + (void)ep; + (void)ch; + (void)volume_db; } __WEAK int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch) { + (void)busid; + (void)ep; + (void)ch; + return 0; } __WEAK void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute) { + (void)busid; + (void)ep; + (void)ch; + (void)mute; } __WEAK bool usbd_audio_get_mute(uint8_t busid, uint8_t ep, uint8_t ch) { + (void)busid; + (void)ep; + (void)ch; + return 0; } __WEAK void usbd_audio_set_sampling_freq(uint8_t busid, uint8_t ep, uint32_t sampling_freq) { + (void)busid; + (void)ep; + (void)sampling_freq; } __WEAK uint32_t usbd_audio_get_sampling_freq(uint8_t busid, uint8_t ep) { + (void)busid; + (void)ep; + return 0; } __WEAK void usbd_audio_get_sampling_freq_table(uint8_t busid, uint8_t ep, uint8_t **sampling_freq_table) { + (void)busid; + (void)ep; + (void)sampling_freq_table; } diff --git a/rt-thread/components/drivers/usb/cherryusb/class/audio/usbd_audio.h b/rt-thread/components/drivers/usb/cherryusb/class/audio/usbd_audio.h index 442405a..75a0b17 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/audio/usbd_audio.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/audio/usbd_audio.h @@ -27,7 +27,7 @@ struct usbd_interface *usbd_audio_init_intf(uint8_t busid, struct usbd_interface void usbd_audio_open(uint8_t busid, uint8_t intf); void usbd_audio_close(uint8_t busid, uint8_t intf); -void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume); +void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume_db); int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch); void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute); bool usbd_audio_get_mute(uint8_t busid, uint8_t ep, uint8_t ch); diff --git a/rt-thread/components/drivers/usb/cherryusb/class/audio/usbh_audio.c b/rt-thread/components/drivers/usb/cherryusb/class/audio/usbh_audio.c index 223c340..6d8b455 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/audio/usbh_audio.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/audio/usbh_audio.c @@ -21,10 +21,6 @@ #define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */ #define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */ -#ifndef CONFIG_USBHOST_MAX_AUDIO_CLASS -#define CONFIG_USBHOST_MAX_AUDIO_CLASS 4 -#endif - USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_audio_buf[128]; static struct usbh_audio g_audio_class[CONFIG_USBHOST_MAX_AUDIO_CLASS]; @@ -32,11 +28,11 @@ static uint32_t g_devinuse = 0; static struct usbh_audio *usbh_audio_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_AUDIO_CLASS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_audio_class[devno], 0, sizeof(struct usbh_audio)); g_audio_class[devno].minor = devno; return &g_audio_class[devno]; @@ -47,15 +43,15 @@ static struct usbh_audio *usbh_audio_class_alloc(void) static void usbh_audio_class_free(struct usbh_audio *audio_class) { - int devno = audio_class->minor; + uint8_t devno = audio_class->minor; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(audio_class, 0, sizeof(struct usbh_audio)); } -int usbh_audio_open(struct usbh_audio *audio_class, const char *name, uint32_t samp_freq) +int usbh_audio_open(struct usbh_audio *audio_class, const char *name, uint32_t samp_freq, uint8_t bitresolution) { struct usb_setup_packet *setup; struct usb_endpoint_descriptor *ep_desc; @@ -74,20 +70,24 @@ int usbh_audio_open(struct usbh_audio *audio_class, const char *name, uint32_t s return 0; } - for (uint8_t i = 0; i < audio_class->module_num; i++) { - if (strcmp(name, audio_class->module[i].name) == 0) { - for (uint8_t j = 0; j < audio_class->num_of_intf_altsettings; j++) { - for (uint8_t k = 0; k < audio_class->module[i].altsetting[j].sampfreq_num; k++) { - if (audio_class->module[i].altsetting[j].sampfreq[k] == samp_freq) { - intf = audio_class->module[i].data_intf; - altsetting = j; - goto freq_found; + for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) { + if (strcmp(name, audio_class->as_msg_table[i].stream_name) == 0) { + intf = audio_class->as_msg_table[i].stream_intf; + for (uint8_t j = 1; j < audio_class->as_msg_table[i].num_of_altsetting; j++) { + if (audio_class->as_msg_table[i].as_format[j].bBitResolution == bitresolution) { + for (uint8_t k = 0; k < audio_class->as_msg_table[i].as_format[j].bSamFreqType; k++) { + uint32_t freq = 0; + + memcpy(&freq, &audio_class->as_msg_table[i].as_format[j].tSamFreq[3 * k], 3); + if (freq == samp_freq) { + altsetting = j; + goto freq_found; + } } } } } } - return -USB_ERR_NODEV; freq_found: @@ -105,16 +105,18 @@ freq_found: ep_desc = &audio_class->hport->config.intf[intf].altsetting[altsetting].ep[0].ep_desc; - setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_ENDPOINT; - setup->bRequest = AUDIO_REQUEST_SET_CUR; - setup->wValue = (AUDIO_EP_CONTROL_SAMPLING_FEQ << 8) | 0x00; - setup->wIndex = ep_desc->bEndpointAddress; - setup->wLength = 3; + if (audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].ep_attr & AUDIO_EP_CONTROL_SAMPLING_FEQ) { + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_ENDPOINT; + setup->bRequest = AUDIO_REQUEST_SET_CUR; + setup->wValue = (AUDIO_EP_CONTROL_SAMPLING_FEQ << 8) | 0x00; + setup->wIndex = ep_desc->bEndpointAddress; + setup->wLength = 3; - memcpy(g_audio_buf, &samp_freq, 3); - ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf); - if (ret < 0) { - return ret; + memcpy(g_audio_buf, &samp_freq, 3); + ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf); + if (ret < 0) { + return ret; + } } mult = (ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_MASK) >> USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_SHIFT; @@ -127,7 +129,7 @@ freq_found: USBH_EP_INIT(audio_class->isoout, ep_desc); } - USB_LOG_INFO("Open audio module :%s, altsetting: %u\r\n", name, altsetting); + USB_LOG_INFO("Open audio stream :%s, altsetting: %u\r\n", name, altsetting); audio_class->is_opened = true; return ret; } @@ -145,9 +147,9 @@ int usbh_audio_close(struct usbh_audio *audio_class, const char *name) } setup = audio_class->hport->setup; - for (size_t i = 0; i < audio_class->module_num; i++) { - if (strcmp(name, audio_class->module[i].name) == 0) { - intf = audio_class->module[i].data_intf; + for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) { + if (strcmp(name, audio_class->as_msg_table[i].stream_name) == 0) { + intf = audio_class->as_msg_table[i].stream_intf; } } @@ -155,7 +157,17 @@ int usbh_audio_close(struct usbh_audio *audio_class, const char *name) return -USB_ERR_NODEV; } - USB_LOG_INFO("Close audio module :%s\r\n", name); + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = USB_REQUEST_SET_INTERFACE; + setup->wValue = 0; + setup->wIndex = intf; + setup->wLength = 0; + + ret = usbh_control_transfer(audio_class->hport, setup, NULL); + if (ret < 0) { + return ret; + } + USB_LOG_INFO("Close audio stream :%s\r\n", name); audio_class->is_opened = false; ep_desc = &audio_class->hport->config.intf[intf].altsetting[altsetting].ep[0].ep_desc; @@ -169,52 +181,128 @@ int usbh_audio_close(struct usbh_audio *audio_class, const char *name) } } - setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE; - setup->bRequest = USB_REQUEST_SET_INTERFACE; - setup->wValue = 0; - setup->wIndex = intf; - setup->wLength = 0; - - ret = usbh_control_transfer(audio_class->hport, setup, NULL); - return ret; } -int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, uint8_t volume) +int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, int volume_db) { struct usb_setup_packet *setup; int ret; - uint8_t intf = 0xff; uint8_t feature_id = 0xff; + uint8_t intf; uint16_t volume_hex; + int volume_min_db; + int volume_max_db; if (!audio_class || !audio_class->hport) { return -USB_ERR_INVAL; } + + if ((volume_db > 127) || (volume_db < -127)) { + return -USB_ERR_INVAL; + } + setup = audio_class->hport->setup; - for (size_t i = 0; i < audio_class->module_num; i++) { - if (strcmp(name, audio_class->module[i].name) == 0) { - intf = audio_class->ctrl_intf; - feature_id = audio_class->module[i].feature_unit_id; + for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) { + if (strcmp(name, audio_class->as_msg_table[i].stream_name) == 0) { + feature_id = audio_class->as_msg_table[i].feature_terminal_id; + intf = audio_class->as_msg_table[i].stream_intf; } } - if (intf == 0xff) { + if (feature_id == 0xff) { return -USB_ERR_NODEV; } + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = AUDIO_REQUEST_GET_CUR; + setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch; + setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf; + setup->wLength = 2; + + ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf); + if (ret < 0) { + return ret; + } + + memcpy(&audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_cur, g_audio_buf, 2); + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = AUDIO_REQUEST_GET_MIN; + setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch; + setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf; + setup->wLength = 2; + + ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf); + if (ret < 0) { + return ret; + } + + memcpy(&audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_min, g_audio_buf, 2); + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = AUDIO_REQUEST_GET_MAX; + setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch; + setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf; + setup->wLength = 2; + + ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf); + if (ret < 0) { + return ret; + } + memcpy(&audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_max, g_audio_buf, 2); + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = AUDIO_REQUEST_GET_RES; + setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch; + setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf; + setup->wLength = 2; + + ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf); + if (ret < 0) { + return ret; + } + memcpy(&audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_res, g_audio_buf, 2); + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; setup->bRequest = AUDIO_REQUEST_SET_CUR; setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch; - setup->wIndex = (feature_id << 8) | intf; + setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf; setup->wLength = 2; - volume_hex = -0xDB00 / 100 * volume + 0xdb00; + if (audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_min < 0x8000) { + volume_min_db = audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_min / 256; + } else { + volume_min_db = (audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_min - 0x10000) / 256; + } + + if (audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_max < 0x8000) { + volume_max_db = audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_max / 256; + } else { + volume_max_db = (audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_max - 0x10000) / 256; + } + + USB_LOG_INFO("Get ch:%d dB range: %d dB ~ %d dB\r\n", volume_min_db, volume_max_db); + + if (volume_db >= 0) { + volume_hex = volume_db * 256; + if (volume_hex > audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_max) { + return -USB_ERR_RANGE; + } + } else { + volume_hex = volume_db * 256 + 0x10000; + if (volume_hex < audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_min) { + return -USB_ERR_RANGE; + } + } memcpy(g_audio_buf, &volume_hex, 2); - ret = usbh_control_transfer(audio_class->hport, setup, NULL); - + ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf); + if (ret < 0) { + return ret; + } + audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].volume_cur = volume_hex; return ret; } @@ -222,34 +310,37 @@ int usbh_audio_set_mute(struct usbh_audio *audio_class, const char *name, uint8_ { struct usb_setup_packet *setup; int ret; - uint8_t intf = 0xff; uint8_t feature_id = 0xff; + uint8_t intf = 0xff; if (!audio_class || !audio_class->hport) { return -USB_ERR_INVAL; } setup = audio_class->hport->setup; - for (size_t i = 0; i < audio_class->module_num; i++) { - if (strcmp(name, audio_class->module[i].name) == 0) { - intf = audio_class->ctrl_intf; - feature_id = audio_class->module[i].feature_unit_id; + for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) { + if (strcmp(name, audio_class->as_msg_table[i].stream_name) == 0) { + feature_id = audio_class->as_msg_table[i].feature_terminal_id; + intf = audio_class->as_msg_table[i].stream_intf; } } - if (intf == 0xff) { + if (feature_id == 0xff) { return -USB_ERR_NODEV; } setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; setup->bRequest = AUDIO_REQUEST_SET_CUR; setup->wValue = (AUDIO_FU_CONTROL_MUTE << 8) | ch; - setup->wIndex = (feature_id << 8) | intf; + setup->wIndex = (feature_id << 8) | audio_class->ctrl_intf; setup->wLength = 1; memcpy(g_audio_buf, &mute, 1); ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf); - + if (ret < 0) { + return ret; + } + audio_class->as_msg_table[intf - audio_class->ctrl_intf - 1].mute = mute; return ret; } @@ -257,26 +348,28 @@ void usbh_audio_list_module(struct usbh_audio *audio_class) { USB_LOG_INFO("============= Audio module information ===================\r\n"); USB_LOG_RAW("bcdADC :%04x\r\n", audio_class->bcdADC); - USB_LOG_RAW("Num of modules :%u\r\n", audio_class->module_num); - USB_LOG_RAW("Num of altsettings:%u\r\n", audio_class->num_of_intf_altsettings); + USB_LOG_RAW("Num of audio stream :%u\r\n", audio_class->stream_intf_num); - for (uint8_t i = 0; i < audio_class->module_num; i++) { - USB_LOG_RAW(" module name :%s\r\n", audio_class->module[i].name); - USB_LOG_RAW(" module feature unit id :%d\r\n", audio_class->module[i].feature_unit_id); + for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) { + USB_LOG_RAW("\tstream name :%s\r\n", audio_class->as_msg_table[i].stream_name); + USB_LOG_RAW("\tstream intf :%u\r\n", audio_class->as_msg_table[i].stream_intf); + USB_LOG_RAW("\tNum of altsetting :%u\r\n", audio_class->as_msg_table[i].num_of_altsetting); - for (uint8_t j = 0; j < audio_class->num_of_intf_altsettings; j++) { + for (uint8_t j = 0; j < audio_class->as_msg_table[i].num_of_altsetting; j++) { if (j == 0) { - USB_LOG_RAW(" Ingore altsetting 0\r\n"); + USB_LOG_RAW("\t\tIngore altsetting 0\r\n"); continue; } - USB_LOG_RAW(" Altsetting %u\r\n", j); - USB_LOG_RAW(" module channels :%u\r\n", audio_class->module[i].altsetting[j].channels); - //USB_LOG_RAW(" module format_type :%u\r\n",audio_class->module[i].altsetting[j].format_type); - USB_LOG_RAW(" module bitresolution :%u\r\n", audio_class->module[i].altsetting[j].bitresolution); - USB_LOG_RAW(" module sampfreq num :%u\r\n", audio_class->module[i].altsetting[j].sampfreq_num); + USB_LOG_RAW("\t\tAltsetting :%u\r\n", j); + USB_LOG_RAW("\t\t\tbNrChannels :%u\r\n", audio_class->as_msg_table[i].as_format[j].bNrChannels); + USB_LOG_RAW("\t\t\tbBitResolution :%u\r\n", audio_class->as_msg_table[i].as_format[j].bBitResolution); + USB_LOG_RAW("\t\t\tbSamFreqType :%u\r\n", audio_class->as_msg_table[i].as_format[j].bSamFreqType); - for (uint8_t k = 0; k < audio_class->module[i].altsetting[j].sampfreq_num; k++) { - USB_LOG_RAW(" module sampfreq :%d hz\r\n", audio_class->module[i].altsetting[j].sampfreq[k]); + for (uint8_t k = 0; k < audio_class->as_msg_table[i].as_format[j].bSamFreqType; k++) { + uint32_t freq = 0; + + memcpy(&freq, &audio_class->as_msg_table[i].as_format[j].tSamFreq[3 * k], 3); + USB_LOG_RAW("\t\t\t\tSampleFreq :%u\r\n", freq); } } } @@ -287,14 +380,14 @@ void usbh_audio_list_module(struct usbh_audio *audio_class) static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf) { int ret; - uint8_t cur_iface = 0xff; - uint8_t cur_iface_count = 0xff; - uint8_t cur_alt_setting = 0xff; + uint8_t cur_iface = 0; + uint8_t cur_iface_count = 0; + uint8_t cur_alt_setting = 0; uint8_t input_offset = 0; uint8_t output_offset = 0; uint8_t feature_unit_offset = 0; - uint8_t format_offset = 0; uint8_t *p; + struct usbh_audio_ac_msg ac_msg_table[CONFIG_USBHOST_AUDIO_MAX_STREAMS]; struct usbh_audio *audio_class = usbh_audio_class_alloc(); if (audio_class == NULL) { @@ -304,8 +397,6 @@ static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf) audio_class->hport = hport; audio_class->ctrl_intf = intf; - audio_class->num_of_intf_altsettings = hport->config.intf[intf + 1].altsetting_num; - hport->config.intf[intf].priv = audio_class; p = hport->raw_config_desc; @@ -331,72 +422,49 @@ static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf) case AUDIO_CONTROL_INPUT_TERMINAL: { struct audio_cs_if_ac_input_terminal_descriptor *desc = (struct audio_cs_if_ac_input_terminal_descriptor *)p; - audio_class->module[input_offset].input_terminal_id = desc->bTerminalID; - audio_class->module[input_offset].input_terminal_type = desc->wTerminalType; - audio_class->module[input_offset].input_channel_config = desc->wChannelConfig; - - if (desc->wTerminalType == AUDIO_TERMINAL_STREAMING) { - audio_class->module[input_offset].terminal_link_id = desc->bTerminalID; - } - if (desc->wTerminalType == AUDIO_INTERM_MIC) { - audio_class->module[input_offset].name = "mic"; - } + memcpy(&ac_msg_table[input_offset].ac_input, desc, sizeof(struct audio_cs_if_ac_input_terminal_descriptor)); input_offset++; } break; - break; case AUDIO_CONTROL_OUTPUT_TERMINAL: { struct audio_cs_if_ac_output_terminal_descriptor *desc = (struct audio_cs_if_ac_output_terminal_descriptor *)p; - audio_class->module[output_offset].output_terminal_id = desc->bTerminalID; - audio_class->module[output_offset].output_terminal_type = desc->wTerminalType; - if (desc->wTerminalType == AUDIO_TERMINAL_STREAMING) { - audio_class->module[output_offset].terminal_link_id = desc->bTerminalID; - } - if (desc->wTerminalType == AUDIO_OUTTERM_SPEAKER) { - audio_class->module[output_offset].name = "speaker"; - } + + memcpy(&ac_msg_table[output_offset].ac_output, desc, sizeof(struct audio_cs_if_ac_output_terminal_descriptor)); output_offset++; } break; case AUDIO_CONTROL_FEATURE_UNIT: { struct audio_cs_if_ac_feature_unit_descriptor *desc = (struct audio_cs_if_ac_feature_unit_descriptor *)p; - audio_class->module[feature_unit_offset].feature_unit_id = desc->bUnitID; - audio_class->module[feature_unit_offset].feature_unit_controlsize = desc->bControlSize; - for (uint8_t j = 0; j < desc->bControlSize; j++) { - audio_class->module[feature_unit_offset].feature_unit_controls[j] = p[6 + j]; - } + memcpy(&ac_msg_table[feature_unit_offset].ac_feature_unit, desc, desc->bLength); feature_unit_offset++; } break; - case AUDIO_CONTROL_PROCESSING_UNIT: + default: + USB_LOG_ERR("Do not support %02x subtype\r\n", p[DESC_bDescriptorSubType]); + return -USB_ERR_NOTSUPP; + } + } else if ((cur_iface > audio_class->ctrl_intf) && (cur_iface < (audio_class->ctrl_intf + cur_iface_count))) { + switch (p[DESC_bDescriptorSubType]) { + case AUDIO_STREAMING_GENERAL: { + struct audio_cs_if_as_general_descriptor *desc = (struct audio_cs_if_as_general_descriptor *)p; - break; + /* all altsetting have the same general */ + audio_class->as_msg_table[cur_iface - audio_class->ctrl_intf - 1].stream_intf = cur_iface; + memcpy(&audio_class->as_msg_table[cur_iface - audio_class->ctrl_intf - 1].as_general, desc, sizeof(struct audio_cs_if_as_general_descriptor)); + } break; + case AUDIO_STREAMING_FORMAT_TYPE: { + struct audio_cs_if_as_format_type_descriptor *desc = (struct audio_cs_if_as_format_type_descriptor *)p; + audio_class->as_msg_table[cur_iface - audio_class->ctrl_intf - 1].num_of_altsetting = (cur_alt_setting + 1); + memcpy(&audio_class->as_msg_table[cur_iface - audio_class->ctrl_intf - 1].as_format[cur_alt_setting], desc, desc->bLength); + } break; default: break; } - } else if ((cur_iface < (audio_class->ctrl_intf + cur_iface_count)) && (cur_iface > audio_class->ctrl_intf)) { - switch (p[DESC_bDescriptorSubType]) { - case AUDIO_STREAMING_GENERAL: - - break; - case AUDIO_STREAMING_FORMAT_TYPE: { - struct audio_cs_if_as_format_type_descriptor *desc = (struct audio_cs_if_as_format_type_descriptor *)p; - - audio_class->module[format_offset].data_intf = cur_iface; - audio_class->module[format_offset].altsetting[cur_alt_setting].channels = desc->bNrChannels; - audio_class->module[format_offset].altsetting[cur_alt_setting].format_type = desc->bFormatType; - audio_class->module[format_offset].altsetting[cur_alt_setting].bitresolution = desc->bBitResolution; - audio_class->module[format_offset].altsetting[cur_alt_setting].sampfreq_num = desc->bSamFreqType; - - for (uint8_t j = 0; j < desc->bSamFreqType; j++) { - audio_class->module[format_offset].altsetting[cur_alt_setting].sampfreq[j] = (uint32_t)(p[10 + j * 3] << 16) | - (uint32_t)(p[9 + j * 3] << 8) | - (uint32_t)(p[8 + j * 3] << 0); - } - if (cur_alt_setting == (hport->config.intf[intf + 1].altsetting_num - 1)) { - format_offset++; - } - } break; - default: - break; + } + break; + case AUDIO_ENDPOINT_DESCRIPTOR_TYPE: + if ((cur_iface > audio_class->ctrl_intf) && (cur_iface < (audio_class->ctrl_intf + cur_iface_count))) { + if (p[DESC_bDescriptorSubType] == AUDIO_ENDPOINT_GENERAL) { + struct audio_cs_ep_ep_general_descriptor *desc = (struct audio_cs_ep_ep_general_descriptor *)p; + audio_class->as_msg_table[cur_iface - audio_class->ctrl_intf - 1].ep_attr = desc->bmAttributes; } } break; @@ -407,16 +475,98 @@ static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf) p += p[DESC_bLength]; } - if ((input_offset != output_offset) && (input_offset != feature_unit_offset) && (input_offset != format_offset)) { + if ((input_offset != output_offset) && (input_offset != feature_unit_offset)) { + USB_LOG_ERR("Audio control descriptor is invalid\r\n"); return -USB_ERR_INVAL; } - audio_class->module_num = input_offset; + if (cur_iface_count == 0xff) { + USB_LOG_ERR("Audio descriptor must have iad descriptor\r\n"); + return -USB_ERR_INVAL; + } - for (size_t i = 0; i < audio_class->module_num; i++) { - ret = usbh_audio_close(audio_class, audio_class->module[i].name); + audio_class->stream_intf_num = input_offset; + + for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) { + /* Search 0x0101 in input or output desc */ + for (uint8_t streamidx = 0; streamidx < audio_class->stream_intf_num; streamidx++) { + if (audio_class->as_msg_table[i].as_general.bTerminalLink == ac_msg_table[streamidx].ac_input.bTerminalID) { + /* INPUT --> FEATURE UNIT --> OUTPUT */ + audio_class->as_msg_table[i].input_terminal_id = ac_msg_table[streamidx].ac_input.bTerminalID; + + /* Search input terminal id in feature desc */ + for (uint8_t featureidx = 0; featureidx < audio_class->stream_intf_num; featureidx++) { + if (ac_msg_table[streamidx].ac_input.bTerminalID == ac_msg_table[featureidx].ac_feature_unit.bSourceID) { + audio_class->as_msg_table[i].feature_terminal_id = ac_msg_table[featureidx].ac_feature_unit.bUnitID; + + /* Search feature unit id in output desc */ + for (uint8_t outputid = 0; outputid < audio_class->stream_intf_num; outputid++) { + if (ac_msg_table[featureidx].ac_feature_unit.bUnitID == ac_msg_table[outputid].ac_output.bSourceID) { + audio_class->as_msg_table[i].output_terminal_id = ac_msg_table[outputid].ac_output.bTerminalID; + + switch (ac_msg_table[outputid].ac_output.wTerminalType) { + case AUDIO_OUTTERM_SPEAKER: + audio_class->as_msg_table[i].stream_name = "speaker"; + break; + case AUDIO_OUTTERM_HEADPHONES: + audio_class->as_msg_table[i].stream_name = "headphoens"; + break; + case AUDIO_OUTTERM_HEADDISPLAY: + audio_class->as_msg_table[i].stream_name = "headdisplay"; + break; + default: + audio_class->as_msg_table[i].stream_name = "unknown"; + break; + } + break; + } + } + break; + } + } + } else if (audio_class->as_msg_table[i].as_general.bTerminalLink == ac_msg_table[streamidx].ac_output.bTerminalID) { + /* OUTPUT --> FEATURE UNIT --> INPUT */ + audio_class->as_msg_table[i].output_terminal_id = ac_msg_table[streamidx].ac_output.bTerminalID; + + /* Search output terminal id in feature desc */ + for (uint8_t featureidx = 0; featureidx < audio_class->stream_intf_num; featureidx++) { + if (ac_msg_table[streamidx].ac_output.bSourceID == ac_msg_table[featureidx].ac_feature_unit.bUnitID) { + audio_class->as_msg_table[i].feature_terminal_id = ac_msg_table[featureidx].ac_feature_unit.bUnitID; + + /* Search feature unit id in input desc */ + for (uint8_t inputid = 0; inputid < audio_class->stream_intf_num; inputid++) { + if (ac_msg_table[featureidx].ac_feature_unit.bSourceID == ac_msg_table[inputid].ac_input.bTerminalID) { + audio_class->as_msg_table[i].input_terminal_id = ac_msg_table[inputid].ac_input.bTerminalID; + + switch (ac_msg_table[inputid].ac_input.wTerminalType) { + case AUDIO_INTERM_MIC: + audio_class->as_msg_table[i].stream_name = "mic"; + break; + default: + audio_class->as_msg_table[i].stream_name = "unknown"; + break; + } + break; + } + } + break; + } + } + } + } + } + + for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) { + if (audio_class->as_msg_table[i].stream_name == NULL) { + USB_LOG_ERR("Audio stream search fail\r\n"); + return -USB_ERR_NODEV; + } + } + + for (uint8_t i = 0; i < audio_class->stream_intf_num; i++) { + ret = usbh_audio_close(audio_class, audio_class->as_msg_table[i].stream_name); if (ret < 0) { - USB_LOG_ERR("Fail to close audio module :%s\r\n", audio_class->module[i].name); + USB_LOG_ERR("Fail to close audio stream :%s\r\n", audio_class->as_msg_table[i].stream_name); return ret; } } @@ -456,20 +606,26 @@ static int usbh_audio_ctrl_disconnect(struct usbh_hubport *hport, uint8_t intf) static int usbh_audio_data_connect(struct usbh_hubport *hport, uint8_t intf) { + (void)hport; + (void)intf; return 0; } static int usbh_audio_data_disconnect(struct usbh_hubport *hport, uint8_t intf) { + (void)hport; + (void)intf; return 0; } __WEAK void usbh_audio_run(struct usbh_audio *audio_class) { + (void)audio_class; } __WEAK void usbh_audio_stop(struct usbh_audio *audio_class) { + (void)audio_class; } const struct usbh_class_driver audio_ctrl_class_driver = { @@ -486,18 +642,18 @@ const struct usbh_class_driver audio_streaming_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info audio_ctrl_intf_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS, - .class = USB_DEVICE_CLASS_AUDIO, - .subclass = AUDIO_SUBCLASS_AUDIOCONTROL, - .protocol = 0x00, + .bInterfaceClass = USB_DEVICE_CLASS_AUDIO, + .bInterfaceSubClass = AUDIO_SUBCLASS_AUDIOCONTROL, + .bInterfaceProtocol = 0x00, .id_table = NULL, .class_driver = &audio_ctrl_class_driver }; CLASS_INFO_DEFINE const struct usbh_class_info audio_streaming_intf_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS, - .class = USB_DEVICE_CLASS_AUDIO, - .subclass = AUDIO_SUBCLASS_AUDIOSTREAMING, - .protocol = 0x00, + .bInterfaceClass = USB_DEVICE_CLASS_AUDIO, + .bInterfaceSubClass = AUDIO_SUBCLASS_AUDIOSTREAMING, + .bInterfaceProtocol = 0x00, .id_table = NULL, .class_driver = &audio_streaming_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/audio/usbh_audio.h b/rt-thread/components/drivers/usb/cherryusb/class/audio/usbh_audio.h index 038a8f7..2e583ac 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/audio/usbh_audio.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/audio/usbh_audio.h @@ -8,34 +8,31 @@ #include "usb_audio.h" -struct usbh_audio_format_type { - uint8_t channels; - uint8_t format_type; - uint8_t bitresolution; - uint8_t sampfreq_num; - uint32_t sampfreq[3]; +#ifndef CONFIG_USBHOST_AUDIO_MAX_STREAMS +#define CONFIG_USBHOST_AUDIO_MAX_STREAMS 3 +#endif + +struct usbh_audio_ac_msg { + struct audio_cs_if_ac_input_terminal_descriptor ac_input; + struct audio_cs_if_ac_feature_unit_descriptor ac_feature_unit; + struct audio_cs_if_ac_output_terminal_descriptor ac_output; }; -/** - * bSourceID in feature_unit = input_terminal_id - * bSourceID in output_terminal = feature_unit_id - * terminal_link_id = input_terminal_id or output_terminal_id (if input_terminal_type or output_terminal_type is 0x0101) - * - * -*/ -struct usbh_audio_module { - const char *name; - uint8_t data_intf; +struct usbh_audio_as_msg { + const char *stream_name; + uint8_t stream_intf; uint8_t input_terminal_id; - uint16_t input_terminal_type; - uint16_t input_channel_config; + uint8_t feature_terminal_id; uint8_t output_terminal_id; - uint16_t output_terminal_type; - uint8_t feature_unit_id; - uint8_t feature_unit_controlsize; - uint8_t feature_unit_controls[8]; - uint8_t terminal_link_id; - struct usbh_audio_format_type altsetting[CONFIG_USBHOST_MAX_INTF_ALTSETTINGS]; + uint8_t ep_attr; + uint8_t num_of_altsetting; + uint16_t volume_min; + uint16_t volume_max; + uint16_t volume_res; + uint16_t volume_cur; + bool mute; + struct audio_cs_if_as_general_descriptor as_general; + struct audio_cs_if_as_format_type_descriptor as_format[CONFIG_USBHOST_MAX_INTF_ALTSETTINGS]; }; struct usbh_audio { @@ -50,9 +47,8 @@ struct usbh_audio { bool is_opened; uint16_t bcdADC; uint8_t bInCollection; - uint8_t num_of_intf_altsettings; - struct usbh_audio_module module[2]; - uint8_t module_num; + uint8_t stream_intf_num; + struct usbh_audio_as_msg as_msg_table[CONFIG_USBHOST_AUDIO_MAX_STREAMS]; void *user_data; }; @@ -61,9 +57,9 @@ struct usbh_audio { extern "C" { #endif -int usbh_audio_open(struct usbh_audio *audio_class, const char *name, uint32_t samp_freq); +int usbh_audio_open(struct usbh_audio *audio_class, const char *name, uint32_t samp_freq, uint8_t bitresolution); int usbh_audio_close(struct usbh_audio *audio_class, const char *name); -int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, uint8_t volume); +int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, int volume_db); int usbh_audio_set_mute(struct usbh_audio *audio_class, const char *name, uint8_t ch, bool mute); void usbh_audio_run(struct usbh_audio *audio_class); diff --git a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usb_cdc.h b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usb_cdc.h index 2bf0a17..d1546ee 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usb_cdc.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usb_cdc.h @@ -421,7 +421,7 @@ struct cdc_ncm_ndp16 { 0x02, /* bInterfaceCount */ \ USB_DEVICE_CLASS_CDC, /* bFunctionClass */ \ CDC_ABSTRACT_CONTROL_MODEL, /* bFunctionSubClass */ \ - CDC_COMMON_PROTOCOL_AT_COMMANDS, /* bFunctionProtocol */ \ + CDC_COMMON_PROTOCOL_NONE, /* bFunctionProtocol */ \ 0x00, /* iFunction */ \ 0x09, /* bLength */ \ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ @@ -430,7 +430,7 @@ struct cdc_ncm_ndp16 { 0x01, /* bNumEndpoints */ \ USB_DEVICE_CLASS_CDC, /* bInterfaceClass */ \ CDC_ABSTRACT_CONTROL_MODEL, /* bInterfaceSubClass */ \ - CDC_COMMON_PROTOCOL_AT_COMMANDS, /* bInterfaceProtocol */ \ + CDC_COMMON_PROTOCOL_NONE, /* bInterfaceProtocol */ \ str_idx, /* iInterface */ \ 0x05, /* bLength */ \ CDC_CS_INTERFACE, /* bDescriptorType */ \ @@ -489,8 +489,8 @@ struct cdc_ncm_ndp16 { bFirstInterface, /* bFirstInterface */ \ 0x02, /* bInterfaceCount */ \ USB_DEVICE_CLASS_WIRELESS, /* bFunctionClass */ \ - CDC_DIRECT_LINE_CONTROL_MODEL, /* bFunctionSubClass */ \ - CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101_AND_ANNEXO, /* bFunctionProtocol */ \ + 0x01, /* bFunctionSubClass */ \ + 0x03, /* bFunctionProtocol */ \ 0x00, /* iFunction */ \ 0x09, /* bLength */ \ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ @@ -498,8 +498,8 @@ struct cdc_ncm_ndp16 { 0x00, /* bAlternateSetting */ \ 0x01, /* bNumEndpoints */ \ USB_DEVICE_CLASS_WIRELESS, /* bInterfaceClass */ \ - CDC_DIRECT_LINE_CONTROL_MODEL, /* bInterfaceSubClass */ \ - CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101_AND_ANNEXO, /* bInterfaceProtocol */ \ + 0x01, /* bInterfaceSubClass */ \ + 0x03, /* bInterfaceProtocol */ \ str_idx, /* iInterface */ \ 0x05, /* bLength */ \ CDC_CS_INTERFACE, /* bDescriptorType */ \ @@ -524,7 +524,7 @@ struct cdc_ncm_ndp16 { int_ep, /* bEndpointAddress */ \ 0x03, /* bmAttributes */ \ 0x08, 0x00, /* wMaxPacketSize */ \ - 0x10, /* bInterval */ \ + 0x05, /* bInterval */ \ 0x09, /* bLength */ \ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ (uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \ @@ -596,7 +596,7 @@ eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx) int_ep, /* bEndpointAddress */ \ 0x03, /* bmAttributes */ \ 0x10, 0x00, /* wMaxPacketSize */ \ - 0x10, /* bInterval */ \ + 0x05, /* bInterval */ \ 0x09, /* bLength */ \ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \ (uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \ diff --git a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.h b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.h index ebf3d02..2cf3df1 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.h @@ -1,29 +1,13 @@ /* - * Copyright (c) 2022, sakumisu + * Copyright (c) 2024, sakumisu * * SPDX-License-Identifier: Apache-2.0 */ #ifndef USBD_CDC_H #define USBD_CDC_H -#include "usb_cdc.h" +// legacy for old version -#ifdef __cplusplus -extern "C" { -#endif +#include "usbd_cdc_acm.h" -/* Init cdc acm interface driver */ -struct usbd_interface *usbd_cdc_acm_init_intf(uint8_t busid, struct usbd_interface *intf); - -/* Setup request command callback api */ -void usbd_cdc_acm_set_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding); -void usbd_cdc_acm_get_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding); -void usbd_cdc_acm_set_dtr(uint8_t busid, uint8_t intf, bool dtr); -void usbd_cdc_acm_set_rts(uint8_t busid, uint8_t intf, bool rts); -void usbd_cdc_acm_send_break(uint8_t busid, uint8_t intf); - -#ifdef __cplusplus -} -#endif - -#endif /* USBD_CDC_H */ +#endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.c b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_acm.c similarity index 94% rename from rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.c rename to rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_acm.c index 50d13d7..cf7c3c0 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_acm.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ #include "usbd_core.h" -#include "usbd_cdc.h" +#include "usbd_cdc_acm.h" const char *stop_name[] = { "1", "1.5", "2" }; const char *parity_name[] = { "N", "O", "E", "M", "S" }; @@ -85,6 +85,8 @@ static int cdc_acm_class_interface_request_handler(uint8_t busid, struct usb_set struct usbd_interface *usbd_cdc_acm_init_intf(uint8_t busid, struct usbd_interface *intf) { + (void)busid; + intf->class_interface_handler = cdc_acm_class_interface_request_handler; intf->class_endpoint_handler = NULL; intf->vendor_handler = NULL; @@ -95,10 +97,16 @@ struct usbd_interface *usbd_cdc_acm_init_intf(uint8_t busid, struct usbd_interfa __WEAK void usbd_cdc_acm_set_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding) { + (void)busid; + (void)intf; + (void)line_coding; } __WEAK void usbd_cdc_acm_get_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding) { + (void)busid; + (void)intf; + line_coding->dwDTERate = 2000000; line_coding->bDataBits = 8; line_coding->bParityType = 0; @@ -107,12 +115,20 @@ __WEAK void usbd_cdc_acm_get_line_coding(uint8_t busid, uint8_t intf, struct cdc __WEAK void usbd_cdc_acm_set_dtr(uint8_t busid, uint8_t intf, bool dtr) { + (void)busid; + (void)intf; + (void)dtr; } __WEAK void usbd_cdc_acm_set_rts(uint8_t busid, uint8_t intf, bool rts) { + (void)busid; + (void)intf; + (void)rts; } __WEAK void usbd_cdc_acm_send_break(uint8_t busid, uint8_t intf) { + (void)busid; + (void)intf; } diff --git a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_acm.h b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_acm.h new file mode 100644 index 0000000..662c238 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_acm.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USBD_CDC_ACM_H +#define USBD_CDC_ACM_H + +#include "usb_cdc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Init cdc acm interface driver */ +struct usbd_interface *usbd_cdc_acm_init_intf(uint8_t busid, struct usbd_interface *intf); + +/* Setup request command callback api */ +void usbd_cdc_acm_set_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding); +void usbd_cdc_acm_get_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding); +void usbd_cdc_acm_set_dtr(uint8_t busid, uint8_t intf, bool dtr); +void usbd_cdc_acm_set_rts(uint8_t busid, uint8_t intf, bool rts); +void usbd_cdc_acm_send_break(uint8_t busid, uint8_t intf); + +#ifdef __cplusplus +} +#endif + +#endif /* USBD_CDC_ACM_H */ diff --git a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.c b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.c index b0c1217..5ae10a2 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.c @@ -7,17 +7,21 @@ #include "usbd_cdc_ecm.h" #define CDC_ECM_OUT_EP_IDX 0 -#define CDC_ECM_IN_EP_IDX 1 -#define CDC_ECM_INT_EP_IDX 2 +#define CDC_ECM_IN_EP_IDX 1 +#define CDC_ECM_INT_EP_IDX 2 + +/* Ethernet Maximum Segment size, typically 1514 bytes */ +#define CONFIG_CDC_ECM_ETH_MAX_SEGSZE 1536U /* Describe EndPoints configuration */ static struct usbd_endpoint cdc_ecm_ep_data[3]; +#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE]; static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE]; +#endif static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_notify_buf[16]; -volatile uint8_t *g_cdc_ecm_rx_data_buffer = NULL; volatile uint32_t g_cdc_ecm_rx_data_length = 0; volatile uint32_t g_cdc_ecm_tx_data_length = 0; @@ -68,8 +72,10 @@ void usbd_cdc_ecm_send_notify(uint8_t notifycode, uint8_t value, uint32_t *speed break; } - if (bytes2send) { - usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr, g_cdc_ecm_notify_buf, bytes2send); + if (usb_device_is_configured(0)) { + if (bytes2send) { + usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr, g_cdc_ecm_notify_buf, bytes2send); + } } } @@ -79,6 +85,10 @@ static int cdc_ecm_class_interface_request_handler(uint8_t busid, struct usb_set "bRequest 0x%02x\r\n", setup->bRequest); + (void)busid; + (void)data; + (void)len; + g_cmd_intf = LO_BYTE(setup->wIndex); switch (setup->bRequest) { @@ -89,11 +99,11 @@ static int cdc_ecm_class_interface_request_handler(uint8_t busid, struct usb_set * bit3 Broadcast * bit4 Multicast */ - if (g_current_net_status == 0) { - g_current_net_status = 1; - usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL); - } - +#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP + g_connect_speed_table[0] = 100000000; /* 100 Mbps */ + g_connect_speed_table[1] = 100000000; /* 100 Mbps */ + usbd_cdc_ecm_set_connect(true, g_connect_speed_table); +#endif break; default: USB_LOG_WRN("Unhandled CDC ECM Class bRequest 0x%02x\r\n", setup->bRequest); @@ -105,15 +115,19 @@ static int cdc_ecm_class_interface_request_handler(uint8_t busid, struct usb_set void cdc_ecm_notify_handler(uint8_t busid, uint8_t event, void *arg) { + (void)busid; + (void)arg; + switch (event) { case USBD_EVENT_RESET: g_current_net_status = 0; g_cdc_ecm_rx_data_length = 0; g_cdc_ecm_tx_data_length = 0; - g_cdc_ecm_rx_data_buffer = NULL; break; case USBD_EVENT_CONFIGURED: - usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], usbd_get_ep_mps(busid, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr)); +#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP + usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE); +#endif break; default: @@ -123,36 +137,45 @@ void cdc_ecm_notify_handler(uint8_t busid, uint8_t event, void *arg) void cdc_ecm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) { - g_cdc_ecm_rx_data_length += nbytes; + (void)busid; - if (nbytes < usbd_get_ep_mps(busid, ep)) { - g_cdc_ecm_rx_data_buffer = g_cdc_ecm_rx_buffer; - usbd_cdc_ecm_data_recv_done(g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length); - } else { - usbd_ep_start_read(0, ep, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], usbd_get_ep_mps(busid, ep)); - } + g_cdc_ecm_rx_data_length = nbytes; + usbd_cdc_ecm_data_recv_done(g_cdc_ecm_rx_data_length); } void cdc_ecm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes) { - if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) { + (void)busid; + + if ((nbytes % usbd_get_ep_mps(0, ep)) == 0 && nbytes) { /* send zlp */ usbd_ep_start_write(0, ep, NULL, 0); } else { + usbd_cdc_ecm_data_send_done(g_cdc_ecm_tx_data_length); g_cdc_ecm_tx_data_length = 0; } } void cdc_ecm_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes) { - if (g_current_net_status == 1) { - g_current_net_status = 2; - usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, g_connect_speed_table); + (void)busid; + (void)ep; + (void)nbytes; + + if (g_current_net_status == 2) { + g_current_net_status = 3; + usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE, 0, g_connect_speed_table); + } else { + g_current_net_status = 0; } } int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len) { + if (!usb_device_is_configured(0)) { + return -USB_ERR_NODEV; + } + if (g_cdc_ecm_tx_data_length > 0) { return -USB_ERR_BUSY; } @@ -160,14 +183,17 @@ int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len) g_cdc_ecm_tx_data_length = len; USB_LOG_DBG("txlen:%d\r\n", g_cdc_ecm_tx_data_length); - return usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, buf, g_cdc_ecm_tx_data_length); + return usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, buf, len); } -void usbd_cdc_ecm_start_read_next(void) +int usbd_cdc_ecm_start_read(uint8_t *buf, uint32_t len) { + if (!usb_device_is_configured(0)) { + return -USB_ERR_NODEV; + } + g_cdc_ecm_rx_data_length = 0; - g_cdc_ecm_rx_data_buffer = NULL; - usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, g_cdc_ecm_rx_buffer, usbd_get_ep_mps(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr)); + return usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, buf, len); } #ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP @@ -175,19 +201,19 @@ struct pbuf *usbd_cdc_ecm_eth_rx(void) { struct pbuf *p; - if (g_cdc_ecm_rx_data_buffer == NULL) { + if (g_cdc_ecm_rx_data_length == 0) { return NULL; } p = pbuf_alloc(PBUF_RAW, g_cdc_ecm_rx_data_length, PBUF_POOL); if (p == NULL) { - usbd_cdc_ecm_start_read_next(); + usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE); return NULL; } usb_memcpy(p->payload, (uint8_t *)g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length); p->len = g_cdc_ecm_rx_data_length; USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_data_length); - usbd_cdc_ecm_start_read_next(); + usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE); return p; } @@ -235,11 +261,24 @@ struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const return intf; } -void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2]) +void usbd_cdc_ecm_set_connect(bool connect, uint32_t speed[2]) { - memcpy(g_connect_speed_table, speed, 8); + if (connect) { + g_current_net_status = 2; + memcpy(g_connect_speed_table, speed, 8); + usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL); + } else { + g_current_net_status = 1; + usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_DISCONNECTED, NULL); + } } -__WEAK void usbd_cdc_ecm_data_recv_done(uint8_t *buf, uint32_t len) +__WEAK void usbd_cdc_ecm_data_recv_done(uint32_t len) { + (void)len; +} + +__WEAK void usbd_cdc_ecm_data_send_done(uint32_t len) +{ + (void)len; } diff --git a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.h b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.h index 8d9e5c9..3e0284e 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.h @@ -12,21 +12,15 @@ extern "C" { #endif -/* Ethernet Maximum Segment size, typically 1514 bytes */ -#define CONFIG_CDC_ECM_ETH_MAX_SEGSZE 1514U - /* Init cdc ecm interface driver */ struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const uint8_t int_ep, const uint8_t out_ep, const uint8_t in_ep); -/* Setup request command callback api */ -void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2]); +void usbd_cdc_ecm_set_connect(bool connect, uint32_t speed[2]); -/* Api for eth only without any net stack */ -uint8_t *usbd_cdc_ecm_get_tx_buffer(void); -void usbd_cdc_ecm_send_done(void); +void usbd_cdc_ecm_data_recv_done(uint32_t len); +void usbd_cdc_ecm_data_send_done(uint32_t len); int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len); -void usbd_cdc_ecm_data_recv_done(uint8_t *buf, uint32_t len); -void usbd_cdc_ecm_start_read_next(void); +int usbd_cdc_ecm_start_read(uint8_t *buf, uint32_t len); #ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP #include "lwip/netif.h" diff --git a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.c b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.c index c2c953a..53666b3 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.c @@ -12,18 +12,18 @@ #define DEV_FORMAT "/dev/ttyACM%d" -USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_acm_buf[64]; +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_acm_buf[CONFIG_USBHOST_MAX_CDC_ACM_CLASS][USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)]; static struct usbh_cdc_acm g_cdc_acm_class[CONFIG_USBHOST_MAX_CDC_ACM_CLASS]; static uint32_t g_devinuse = 0; static struct usbh_cdc_acm *usbh_cdc_acm_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_CDC_ACM_CLASS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_cdc_acm_class[devno], 0, sizeof(struct usbh_cdc_acm)); g_cdc_acm_class[devno].minor = devno; return &g_cdc_acm_class[devno]; @@ -34,10 +34,10 @@ static struct usbh_cdc_acm *usbh_cdc_acm_class_alloc(void) static void usbh_cdc_acm_class_free(struct usbh_cdc_acm *cdc_acm_class) { - int devno = cdc_acm_class->minor; + uint8_t devno = cdc_acm_class->minor; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(cdc_acm_class, 0, sizeof(struct usbh_cdc_acm)); } @@ -57,9 +57,9 @@ int usbh_cdc_acm_set_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_ setup->wIndex = cdc_acm_class->intf; setup->wLength = 7; - memcpy(g_cdc_acm_buf, line_coding, sizeof(struct cdc_line_coding)); + memcpy(g_cdc_acm_buf[cdc_acm_class->minor], line_coding, sizeof(struct cdc_line_coding)); - return usbh_control_transfer(cdc_acm_class->hport, setup, g_cdc_acm_buf); + return usbh_control_transfer(cdc_acm_class->hport, setup, g_cdc_acm_buf[cdc_acm_class->minor]); } int usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding) @@ -78,11 +78,11 @@ int usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_ setup->wIndex = cdc_acm_class->intf; setup->wLength = 7; - ret = usbh_control_transfer(cdc_acm_class->hport, setup, g_cdc_acm_buf); + ret = usbh_control_transfer(cdc_acm_class->hport, setup, g_cdc_acm_buf[cdc_acm_class->minor]); if (ret < 0) { return ret; } - memcpy(line_coding, g_cdc_acm_buf, sizeof(struct cdc_line_coding)); + memcpy(line_coding, g_cdc_acm_buf[cdc_acm_class->minor], sizeof(struct cdc_line_coding)); return ret; } @@ -231,20 +231,26 @@ int usbh_cdc_acm_bulk_out_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t * static int usbh_cdc_data_connect(struct usbh_hubport *hport, uint8_t intf) { + (void)hport; + (void)intf; return 0; } static int usbh_cdc_data_disconnect(struct usbh_hubport *hport, uint8_t intf) { + (void)hport; + (void)intf; return 0; } __WEAK void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class) { + (void)cdc_acm_class; } __WEAK void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class) { + (void)cdc_acm_class; } const struct usbh_class_driver cdc_acm_class_driver = { @@ -260,19 +266,19 @@ const struct usbh_class_driver cdc_data_class_driver = { }; CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_class_info = { - .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, - .class = USB_DEVICE_CLASS_CDC, - .subclass = CDC_ABSTRACT_CONTROL_MODEL, - .protocol = CDC_COMMON_PROTOCOL_AT_COMMANDS, + .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS, + .bInterfaceClass = USB_DEVICE_CLASS_CDC, + .bInterfaceSubClass = CDC_ABSTRACT_CONTROL_MODEL, + .bInterfaceProtocol = 0x00, .id_table = NULL, .class_driver = &cdc_acm_class_driver }; CLASS_INFO_DEFINE const struct usbh_class_info cdc_data_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS, - .class = USB_DEVICE_CLASS_CDC_DATA, - .subclass = 0x00, - .protocol = 0x00, + .bInterfaceClass = USB_DEVICE_CLASS_CDC_DATA, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, .id_table = NULL, .class_driver = &cdc_data_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.c b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.c index ad05d51..c4743e5 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.c @@ -236,6 +236,7 @@ void usbh_cdc_ecm_rx_thread(void *argument) uint32_t g_cdc_ecm_rx_length; int ret; + (void)argument; USB_LOG_INFO("Create cdc ecm rx thread\r\n"); // clang-format off find_class: @@ -306,10 +307,12 @@ int usbh_cdc_ecm_eth_output(uint32_t buflen) __WEAK void usbh_cdc_ecm_run(struct usbh_cdc_ecm *cdc_ecm_class) { + (void)cdc_ecm_class; } __WEAK void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class) { + (void)cdc_ecm_class; } const struct usbh_class_driver cdc_ecm_class_driver = { @@ -320,9 +323,9 @@ const struct usbh_class_driver cdc_ecm_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info cdc_ecm_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, - .class = USB_DEVICE_CLASS_CDC, - .subclass = CDC_ETHERNET_NETWORKING_CONTROL_MODEL, - .protocol = CDC_COMMON_PROTOCOL_NONE, + .bInterfaceClass = USB_DEVICE_CLASS_CDC, + .bInterfaceSubClass = CDC_ETHERNET_NETWORKING_CONTROL_MODEL, + .bInterfaceProtocol = CDC_COMMON_PROTOCOL_NONE, .id_table = NULL, .class_driver = &cdc_ecm_class_driver }; \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.c b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.c index 99e8871..f9f3c53 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.c @@ -48,7 +48,7 @@ static int usbh_cdc_ncm_get_ntb_parameters(struct usbh_cdc_ncm *cdc_ncm_class, s setup->wLength = 28; ret = usbh_control_transfer(cdc_ncm_class->hport, setup, g_cdc_ncm_buf); - if (ret < 0) { + if (ret < 8) { return ret; } @@ -259,6 +259,7 @@ void usbh_cdc_ncm_rx_thread(void *argument) uint32_t transfer_size = (16 * 1024); #endif + (void)argument; USB_LOG_INFO("Create cdc ncm rx thread\r\n"); // clang-format off find_class: @@ -330,7 +331,7 @@ find_class: #else if ((g_cdc_ncm_rx_length + (16 * 1024)) > CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE) { #endif - USB_LOG_ERR("Rx packet is overflow, please ruduce tcp window size or increase CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE\r\n"); + USB_LOG_ERR("Rx packet is overflow, please reduce tcp window size or increase CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE\r\n"); while (1) { } } @@ -386,10 +387,12 @@ int usbh_cdc_ncm_eth_output(uint32_t buflen) __WEAK void usbh_cdc_ncm_run(struct usbh_cdc_ncm *cdc_ncm_class) { + (void)cdc_ncm_class; } __WEAK void usbh_cdc_ncm_stop(struct usbh_cdc_ncm *cdc_ncm_class) { + (void)cdc_ncm_class; } const struct usbh_class_driver cdc_ncm_class_driver = { @@ -400,9 +403,9 @@ const struct usbh_class_driver cdc_ncm_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info cdc_ncm_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, - .class = USB_DEVICE_CLASS_CDC, - .subclass = CDC_NETWORK_CONTROL_MODEL, - .protocol = CDC_COMMON_PROTOCOL_NONE, + .bInterfaceClass = USB_DEVICE_CLASS_CDC, + .bInterfaceSubClass = CDC_NETWORK_CONTROL_MODEL, + .bInterfaceProtocol = CDC_COMMON_PROTOCOL_NONE, .id_table = NULL, .class_driver = &cdc_ncm_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/hid/usb_hid.h b/rt-thread/components/drivers/usb/cherryusb/class/hid/usb_hid.h index 5a88a34..b244d11 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/hid/usb_hid.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/hid/usb_hid.h @@ -558,26 +558,24 @@ struct usb_hid_kbd_report }; /* Keyboard output report (1 byte) (HID B.1), - * see USBHID_KBDOUT_* definitions + * see HID_KBD_OUTPUT_* definitions */ /* Mouse input report (HID B.2) */ struct usb_hid_mouse_report { uint8_t buttons; /* See HID_MOUSE_INPUT_BUTTON_* definitions */ - uint8_t xdisp; /* X displacement */ - uint8_t ydisp; /* y displacement */ + int8_t xdisp; /* X displacement */ + int8_t ydisp; /* y displacement */ /* Device specific additional bytes may follow */ -#ifdef CONFIG_INPUT_MOUSE_WHEEL uint8_t wdisp; /* Wheel displacement */ -#endif }; /* Joystick input report (1 bytes) (HID D.1) */ struct usb_hid_js_report { - uint8_t xpos; /* X position */ - uint8_t ypos; /* X position */ + int8_t xpos; /* X position */ + int8_t ypos; /* X position */ uint8_t buttons; /* See USBHID_JSIN_* definitions */ uint8_t throttle; /* Throttle */ }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/hid/usbd_hid.c b/rt-thread/components/drivers/usb/cherryusb/class/hid/usbd_hid.c index d98b82e..fed749f 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/hid/usbd_hid.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/hid/usbd_hid.c @@ -50,6 +50,8 @@ static int hid_class_interface_request_handler(uint8_t busid, struct usb_setup_p struct usbd_interface *usbd_hid_init_intf(uint8_t busid, struct usbd_interface *intf, const uint8_t *desc, uint32_t desc_len) { + (void)busid; + intf->class_interface_handler = hid_class_interface_request_handler; intf->class_endpoint_handler = NULL; intf->vendor_handler = NULL; @@ -60,30 +62,65 @@ struct usbd_interface *usbd_hid_init_intf(uint8_t busid, struct usbd_interface * return intf; } +/* + * Appendix G: HID Request Support Requirements + * + * The following table enumerates the requests that need to be supported by various types of HID class devices. + * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol + * ------------------------------------------------------------------------------------------ + * Boot Mouse Required Optional Optional Optional Required Required + * Non-Boot Mouse Required Optional Optional Optional Optional Optional + * Boot Keyboard Required Optional Required Required Required Required + * Non-Boot Keybrd Required Optional Required Required Optional Optional + * Other Device Required Optional Optional Optional Optional Optional + */ + __WEAK void usbd_hid_get_report(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t report_type, uint8_t **data, uint32_t *len) { + (void)busid; + (void)intf; + (void)report_id; + (void)report_type; (*data[0]) = 0; *len = 1; } __WEAK uint8_t usbd_hid_get_idle(uint8_t busid, uint8_t intf, uint8_t report_id) { + (void)busid; + (void)intf; + (void)report_id; return 0; } __WEAK uint8_t usbd_hid_get_protocol(uint8_t busid, uint8_t intf) { + (void)busid; + (void)intf; return 0; } __WEAK void usbd_hid_set_report(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t report_type, uint8_t *report, uint32_t report_len) { + (void)busid; + (void)intf; + (void)report_id; + (void)report_type; + (void)report; + (void)report_len; } __WEAK void usbd_hid_set_idle(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t duration) { + (void)busid; + (void)intf; + (void)report_id; + (void)duration; } __WEAK void usbd_hid_set_protocol(uint8_t busid, uint8_t intf, uint8_t protocol) { + (void)busid; + (void)intf; + (void)protocol; } \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/hid/usbh_hid.c b/rt-thread/components/drivers/usb/cherryusb/class/hid/usbh_hid.c index 1761ba6..0fd58ab 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/hid/usbh_hid.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/hid/usbh_hid.c @@ -12,18 +12,26 @@ #define DEV_FORMAT "/dev/input%d" -USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_hid_buf[128]; +/* general descriptor field offsets */ +#define DESC_bLength 0 /** Length offset */ +#define DESC_bDescriptorType 1 /** Descriptor type offset */ + +/* interface descriptor field offsets */ +#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */ +#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */ + +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_hid_buf[CONFIG_USBHOST_MAX_HID_CLASS][USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)]; static struct usbh_hid g_hid_class[CONFIG_USBHOST_MAX_HID_CLASS]; static uint32_t g_devinuse = 0; static struct usbh_hid *usbh_hid_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_HID_CLASS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_hid_class[devno], 0, sizeof(struct usbh_hid)); g_hid_class[devno].minor = devno; return &g_hid_class[devno]; @@ -34,18 +42,17 @@ static struct usbh_hid *usbh_hid_class_alloc(void) static void usbh_hid_class_free(struct usbh_hid *hid_class) { - int devno = hid_class->minor; + uint8_t devno = hid_class->minor; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(hid_class, 0, sizeof(struct usbh_hid)); } -static int usbh_hid_get_report_descriptor(struct usbh_hid *hid_class, uint8_t *buffer) +int usbh_hid_get_report_descriptor(struct usbh_hid *hid_class, uint8_t *buffer, uint32_t buflen) { struct usb_setup_packet *setup; - int ret; if (!hid_class || !hid_class->hport) { return -USB_ERR_INVAL; @@ -56,14 +63,9 @@ static int usbh_hid_get_report_descriptor(struct usbh_hid *hid_class, uint8_t *b setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; setup->wValue = HID_DESCRIPTOR_TYPE_HID_REPORT << 8; setup->wIndex = hid_class->intf; - setup->wLength = 128; + setup->wLength = buflen; - ret = usbh_control_transfer(hid_class->hport, setup, g_hid_buf); - if (ret < 0) { - return ret; - } - memcpy(buffer, g_hid_buf, ret - 8); - return ret; + return usbh_control_transfer(hid_class->hport, setup, buffer); } int usbh_hid_set_idle(struct usbh_hid *hid_class, uint8_t report_id, uint8_t duration) @@ -100,11 +102,11 @@ int usbh_hid_get_idle(struct usbh_hid *hid_class, uint8_t *buffer) setup->wIndex = hid_class->intf; setup->wLength = 1; - ret = usbh_control_transfer(hid_class->hport, setup, g_hid_buf); - if (ret < 0) { + ret = usbh_control_transfer(hid_class->hport, setup, g_hid_buf[hid_class->minor]); + if (ret < 8) { return ret; } - memcpy(buffer, g_hid_buf, 1); + memcpy(buffer, g_hid_buf[hid_class->minor], ret - 8); return ret; } @@ -147,6 +149,7 @@ int usbh_hid_set_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t int usbh_hid_get_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen) { struct usb_setup_packet *setup; + int ret; if (!hid_class || !hid_class->hport) { return -USB_ERR_INVAL; @@ -159,13 +162,21 @@ int usbh_hid_get_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t setup->wIndex = 0; setup->wLength = buflen; - return usbh_control_transfer(hid_class->hport, setup, buffer); + ret = usbh_control_transfer(hid_class->hport, setup, g_hid_buf[hid_class->minor]); + if (ret < 8) { + return ret; + } + memcpy(buffer, g_hid_buf[hid_class->minor], ret - 8); + return ret; } int usbh_hid_connect(struct usbh_hubport *hport, uint8_t intf) { struct usb_endpoint_descriptor *ep_desc; int ret; + uint8_t cur_iface = 0xff; + uint8_t *p; + bool found = false; struct usbh_hid *hid_class = usbh_hid_class_alloc(); if (hid_class == NULL) { @@ -178,6 +189,42 @@ int usbh_hid_connect(struct usbh_hubport *hport, uint8_t intf) hport->config.intf[intf].priv = hid_class; + p = hport->raw_config_desc; + while (p[DESC_bLength]) { + switch (p[DESC_bDescriptorType]) { + case USB_DESCRIPTOR_TYPE_INTERFACE: + cur_iface = p[INTF_DESC_bInterfaceNumber]; + if (cur_iface == intf) { + hid_class->protocol = p[7]; + struct usb_hid_descriptor *desc = (struct usb_hid_descriptor *)(p + 9); + + if (desc->bDescriptorType != HID_DESCRIPTOR_TYPE_HID) { + USB_LOG_ERR("HID descriptor not found\r\n"); + return -USB_ERR_INVAL; + } + + if (desc->subdesc[0].bDescriptorType != HID_DESCRIPTOR_TYPE_HID_REPORT) { + USB_LOG_ERR("HID report descriptor not found\r\n"); + return -USB_ERR_INVAL; + } + + hid_class->report_size = desc->subdesc[0].wDescriptorLength; + found = true; + goto found; + } + break; + default: + break; + } + /* skip to next descriptor */ + p += p[DESC_bLength]; + } + + if (found == false) { + USB_LOG_ERR("HID interface not found\r\n"); + return -USB_ERR_INVAL; + } +found: // /* 0x0 = boot protocol, 0x1 = report protocol */ // ret = usbh_hid_set_protocol(hid_class, 0x1); // if (ret < 0) { @@ -189,7 +236,8 @@ int usbh_hid_connect(struct usbh_hubport *hport, uint8_t intf) USB_LOG_WRN("Do not support set idle\r\n"); } - ret = usbh_hid_get_report_descriptor(hid_class, hid_class->report_desc); + /* We read report desc but do nothing (because of too much memory usage for parsing report desc, parsed by users) */ + ret = usbh_hid_get_report_descriptor(hid_class, g_hid_buf[hid_class->minor], MIN(sizeof(g_hid_buf[hid_class->minor]), hid_class->report_size)); if (ret < 0) { return ret; } @@ -239,10 +287,12 @@ int usbh_hid_disconnect(struct usbh_hubport *hport, uint8_t intf) __WEAK void usbh_hid_run(struct usbh_hid *hid_class) { + (void)hid_class; } __WEAK void usbh_hid_stop(struct usbh_hid *hid_class) { + (void)hid_class; } const struct usbh_class_driver hid_class_driver = { @@ -253,9 +303,9 @@ const struct usbh_class_driver hid_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info hid_custom_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS, - .class = USB_DEVICE_CLASS_HID, - .subclass = 0x00, - .protocol = 0x00, + .bInterfaceClass = USB_DEVICE_CLASS_HID, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, .id_table = NULL, .class_driver = &hid_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/hid/usbh_hid.h b/rt-thread/components/drivers/usb/cherryusb/class/hid/usbh_hid.h index 17caaba..5e8fce6 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/hid/usbh_hid.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/hid/usbh_hid.h @@ -15,7 +15,9 @@ struct usbh_hid { struct usbh_urb intin_urb; /* INTR IN urb */ struct usbh_urb intout_urb; /* INTR OUT urb */ - uint8_t report_desc[256]; + uint16_t report_size; + + uint8_t protocol; uint8_t intf; /* interface number */ uint8_t minor; @@ -26,6 +28,7 @@ struct usbh_hid { extern "C" { #endif +int usbh_hid_get_report_descriptor(struct usbh_hid *hid_class, uint8_t *buffer, uint32_t buflen); int usbh_hid_set_idle(struct usbh_hid *hid_class, uint8_t report_id, uint8_t duration); int usbh_hid_get_idle(struct usbh_hid *hid_class, uint8_t *buffer); int usbh_hid_set_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen); diff --git a/rt-thread/components/drivers/usb/cherryusb/class/hub/usb_hub.h b/rt-thread/components/drivers/usb/cherryusb/class/hub/usb_hub.h index 7956b16..68e0ecf 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/hub/usb_hub.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/hub/usb_hub.h @@ -10,6 +10,12 @@ #define HUB_DESCRIPTOR_TYPE_HUB 0x29 #define HUB_DESCRIPTOR_TYPE_HUB3 0x2A +#define HUB_MAX_DEPTH 5 + +#define HUB_SUBCLASS 0x00 +#define HUB_PROTOCOL_STT 0x01 +#define HUB_PROTOCOL_MTT 0x02 + /* Hub class requests */ #define HUB_REQUEST_GET_STATUS USB_REQUEST_GET_STATUS #define HUB_REQUEST_CLEAR_FEATURE USB_REQUEST_CLEAR_FEATURE @@ -27,23 +33,31 @@ #define HUB_FEATURE_HUB_C_OVERCURRENT (0x1) /* Port features */ -#define HUB_PORT_FEATURE_CONNECTION (0x00) -#define HUB_PORT_FEATURE_ENABLE (0x01) -#define HUB_PORT_FEATURE_SUSPEND (0x02) -#define HUB_PORT_FEATURE_OVERCURRENT (0x03) -#define HUB_PORT_FEATURE_RESET (0x04) -#define HUB_PORT_FEATURE_L1 (0x05) -#define HUB_PORT_FEATURE_POWER (0x08) -#define HUB_PORT_FEATURE_LOWSPEED (0x09) -#define HUB_PORT_FEATURE_HIGHSPEED (0x0a) +#define HUB_PORT_FEATURE_CONNECTION (0x00) +#define HUB_PORT_FEATURE_ENABLE (0x01) +#define HUB_PORT_FEATURE_SUSPEND (0x02) +#define HUB_PORT_FEATURE_OVERCURRENT (0x03) +#define HUB_PORT_FEATURE_RESET (0x04) +#define HUB_PORT_FEATURE_L1 (0x05) /* USB 2.0 only */ + +#define HUB_PORT_FEATURE_POWER (0x08) /* USB 2.0 only */ +#define HUB_PORT_FEATURE_POWER_SS (0x09) /* USB 3.0 only */ +/* This is a bit tricky because HUB_PORT_FEATURE_POWER_SS and + HUB_PORT_FEATURE_LOWSPEED share the same bit. */ +#define HUB_PORT_FEATURE_LOWSPEED (0x09) /* USB 2.0 only */ +#define HUB_PORT_FEATURE_HIGHSPEED (0x0a) /* USB 2.0 only */ +#define HUB_PORT_FEATURE_TEST (0x0b) /* USB 2.0 only */ +#define HUB_PORT_FEATURE_INDICATOR (0x0c) /* USB 2.0 only */ + +/* Port status change (wPortChange) */ #define HUB_PORT_FEATURE_C_CONNECTION (0x10) -#define HUB_PORT_FEATURE_C_ENABLE (0x11) -#define HUB_PORT_FEATURE_C_SUSPEND (0x12) +#define HUB_PORT_FEATURE_C_ENABLE (0x11) /* USB 2.0 only */ +#define HUB_PORT_FEATURE_C_SUSPEND (0x12) /* USB 2.0 only */ #define HUB_PORT_FEATURE_C_OVER_CURREN (0x13) #define HUB_PORT_FEATURE_C_RESET (0x14) -#define HUB_PORT_FEATURE_TEST (0x15) -#define HUB_PORT_FEATURE_INDICATOR (0x16) -#define HUB_PORT_FEATURE_C_PORTL1 (0x17) +#define HUB_PORT_FEATURE_C_BH_RESET (0x15) /* USB 3.0 only */ +#define HUB_PORT_FEATURE_C_LINK_STATE (0x16) /* USB 3.0 only */ +#define HUB_PORT_FEATURE_C_CONFIG_ERR (0x17) /* USB 3.0 only */ /* Hub status */ #define HUB_STATUS_LOCALPOWER (1 << 0) @@ -56,23 +70,42 @@ /* Hub port status */ #define HUB_PORT_STATUS_CONNECTION (1 << 0) #define HUB_PORT_STATUS_ENABLE (1 << 1) -#define HUB_PORT_STATUS_SUSPEND (1 << 2) +#define HUB_PORT_STATUS_SUSPEND (1 << 2) /* USB 2.0 only */ #define HUB_PORT_STATUS_OVERCURRENT (1 << 3) #define HUB_PORT_STATUS_RESET (1 << 4) -#define HUB_PORT_STATUS_L1 (1 << 5) -#define HUB_PORT_STATUS_POWER (1 << 8) -#define HUB_PORT_STATUS_LOW_SPEED (1 << 9) -#define HUB_PORT_STATUS_HIGH_SPEED (1 << 10) -#define HUB_PORT_STATUS_TEST (1 << 11) -#define HUB_PORT_STATUS_INDICATOR (1 << 12) +#define HUB_PORT_STATUS_L1 (1 << 5) /* USB 2.0 only */ + +/* Port Link State (PORT_LINK_STATE), USB 3.0 only */ +#define HUB_PORT_STATUS_LS_U0 (0x00 << 5) +#define HUB_PORT_STATUS_LS_U1 (0x01 << 5) +#define HUB_PORT_STATUS_LS_U2 (0x02 << 5) +#define HUB_PORT_STATUS_LS_U3 (0x03 << 5) +#define HUB_PORT_STATUS_LS_SS_DISABLED (0x04 << 5) +#define HUB_PORT_STATUS_LS_RX_DETECT (0x05 << 5) +#define HUB_PORT_STATUS_LS_SS_INACTIVE (0x06 << 5) +#define HUB_PORT_STATUS_LS_POLLING (0x07 << 5) +#define HUB_PORT_STATUS_LS_RECOVERY (0x08 << 5) +#define HUB_PORT_STATUS_LS_HOT_RESET (0x09 << 5) +#define HUB_PORT_STATUS_LS_COMP_MOD (0x0a << 5) +#define HUB_PORT_STATUS_LS_LOOPBACK (0x0b << 5) + +#define HUB_PORT_STATUS_POWER (1 << 8) +#define HUB_PORT_STATUS_POWER_SS (1 << 9) /* USB 3.0 only */ +#define HUB_PORT_STATUS_LOW_SPEED (1 << 9) /* USB 2.0 only */ +#define HUB_PORT_STATUS_HIGH_SPEED (1 << 10) /* USB 2.0 only */ +#define HUB_PORT_STATUS_TEST (1 << 11) /* USB 2.0 only */ +#define HUB_PORT_STATUS_INDICATOR (1 << 12) /* USB 2.0 only */ /* Hub port status change */ #define HUB_PORT_STATUS_C_CONNECTION (1 << 0) -#define HUB_PORT_STATUS_C_ENABLE (1 << 1) -#define HUB_PORT_STATUS_C_SUSPEND (1 << 2) +#define HUB_PORT_STATUS_C_ENABLE (1 << 1) /* USB 2.0 only */ +#define HUB_PORT_STATUS_C_SUSPEND (1 << 2) /* USB 2.0 only */ #define HUB_PORT_STATUS_C_OVERCURRENT (1 << 3) #define HUB_PORT_STATUS_C_RESET (1 << 4) -#define HUB_PORT_STATUS_C_L1 (1 << 5) +#define HUB_PORT_STATUS_C_L1 (1 << 5) /* USB 2.0 only */ +#define HUB_PORT_STATUS_C_BH_RESET (1 << 5) /* USB 3.0 only */ +#define HUB_PORT_STATUS_C_PORTLINK (1 << 6) /* USB 3.0 only */ +#define HUB_PORT_STATUS_C_CONFIGERR (1 << 7) /* USB 3.0 only */ /* Hub characteristics */ #define HUB_CHAR_LPSM_SHIFT (0) /* Bits 0-1: Logical Power Switching Mode */ @@ -106,6 +139,21 @@ struct usb_hub_descriptor { #define USB_SIZEOF_HUB_DESC 9 +/* Super speed Hub descriptor */ +struct usb_hub_ss_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bNbrPorts; + uint16_t wHubCharacteristics; + uint8_t bPwrOn2PwrGood; + uint8_t bHubContrCurrent; + uint8_t bHubHdrDecLat; + uint16_t wHubDelay; + uint8_t DeviceRemovable; +} __PACKED; + +#define USB_SIZEOF_HUB_SS_DESC 11 + /* Hub status */ struct hub_status { uint16_t wPortStatus; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/hub/usbh_hub.c b/rt-thread/components/drivers/usb/cherryusb/class/hub/usbh_hub.c index 1745d8c..9dae396 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/hub/usbh_hub.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/hub/usbh_hub.c @@ -33,11 +33,11 @@ static uint32_t g_devinuse = 0; static struct usbh_hub *usbh_hub_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_EXTHUBS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_hub_class[devno], 0, sizeof(struct usbh_hub)); g_hub_class[devno].index = EXTHUB_FIRST_INDEX + devno; return &g_hub_class[devno]; @@ -48,16 +48,14 @@ static struct usbh_hub *usbh_hub_class_alloc(void) static void usbh_hub_class_free(struct usbh_hub *hub_class) { - int devno = hub_class->index - EXTHUB_FIRST_INDEX; + uint8_t devno = hub_class->index - EXTHUB_FIRST_INDEX; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(hub_class, 0, sizeof(struct usbh_hub)); } -#endif -#if CONFIG_USBHOST_MAX_EXTHUBS > 0 static int _usbh_hub_get_hub_descriptor(struct usbh_hub *hub, uint8_t *buffer) { struct usb_setup_packet *setup; @@ -67,15 +65,7 @@ static int _usbh_hub_get_hub_descriptor(struct usbh_hub *hub, uint8_t *buffer) setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE; setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; - - /* TODO: hub descriptor has some difference between USB 2.0 and USB 3.x, - and we havn't handle the difference here */ - if ((hub->parent->speed == USB_SPEED_SUPER) || - (hub->parent->speed == USB_SPEED_SUPER_PLUS)) { - setup->wValue = HUB_DESCRIPTOR_TYPE_HUB3 << 8; - } else { - setup->wValue = HUB_DESCRIPTOR_TYPE_HUB << 8; - } + setup->wValue = HUB_DESCRIPTOR_TYPE_HUB << 8; setup->wIndex = 0; setup->wLength = USB_SIZEOF_HUB_DESC; @@ -87,8 +77,8 @@ static int _usbh_hub_get_hub_descriptor(struct usbh_hub *hub, uint8_t *buffer) memcpy(buffer, g_hub_buf[hub->bus->busid], USB_SIZEOF_HUB_DESC); return ret; } -#if 0 -static int _usbh_hub_get_status(struct usbh_hub *hub, uint8_t *buffer) + +static int _usbh_hub_get_hub_ss_descriptor(struct usbh_hub *hub, uint8_t *buffer) { struct usb_setup_packet *setup; int ret; @@ -96,20 +86,20 @@ static int _usbh_hub_get_status(struct usbh_hub *hub, uint8_t *buffer) setup = hub->parent->setup; setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE; - setup->bRequest = HUB_REQUEST_GET_STATUS; - setup->wValue = 0; + setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; + setup->wValue = HUB_DESCRIPTOR_TYPE_HUB3 << 8; + setup->wIndex = 0; - setup->wLength = 2; + setup->wLength = USB_SIZEOF_HUB_SS_DESC; ret = usbh_control_transfer(hub->parent, setup, g_hub_buf[hub->bus->busid]); if (ret < 0) { return ret; } - memcpy(buffer, g_hub_buf[hub->bus->busid], 2); + memcpy(buffer, g_hub_buf[hub->bus->busid], USB_SIZEOF_HUB_SS_DESC); return ret; } #endif -#endif static int _usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct hub_port_status *port_status) { @@ -180,6 +170,8 @@ static int _usbh_hub_set_depth(struct usbh_hub *hub, uint16_t depth) #if CONFIG_USBHOST_MAX_EXTHUBS > 0 static int parse_hub_descriptor(struct usb_hub_descriptor *desc, uint16_t length) { + (void)length; + if (desc->bLength != USB_SIZEOF_HUB_DESC) { USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength); return -1; @@ -199,6 +191,29 @@ static int parse_hub_descriptor(struct usb_hub_descriptor *desc, uint16_t length } return 0; } + +static int parse_hub_ss_descriptor(struct usb_hub_ss_descriptor *desc, uint16_t length) +{ + (void)length; + + if (desc->bLength < USB_SIZEOF_HUB_SS_DESC) { + USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength); + return -1; + } else if (desc->bDescriptorType != HUB_DESCRIPTOR_TYPE_HUB3) { + USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType); + return -2; + } else { + USB_LOG_RAW("SuperSpeed Hub Descriptor:\r\n"); + USB_LOG_RAW("bLength: 0x%02x \r\n", desc->bLength); + USB_LOG_RAW("bDescriptorType: 0x%02x \r\n", desc->bDescriptorType); + USB_LOG_RAW("bNbrPorts: 0x%02x \r\n", desc->bNbrPorts); + USB_LOG_RAW("wHubCharacteristics: 0x%04x \r\n", desc->wHubCharacteristics); + USB_LOG_RAW("bPwrOn2PwrGood: 0x%02x \r\n", desc->bPwrOn2PwrGood); + USB_LOG_RAW("bHubContrCurrent: 0x%02x \r\n", desc->bHubContrCurrent); + USB_LOG_RAW("DeviceRemovable: 0x%02x \r\n", desc->DeviceRemovable); + } + return 0; +} #endif static int usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct hub_port_status *port_status) @@ -311,22 +326,65 @@ static int usbh_hub_connect(struct usbh_hubport *hport, uint8_t intf) hub->hub_addr = hport->dev_addr; hub->parent = hport; hub->bus = hport->bus; + hub->speed = hport->speed; + hport->self = hub; hport->config.intf[intf].priv = hub; - ret = _usbh_hub_get_hub_descriptor(hub, (uint8_t *)&hub->hub_desc); - if (ret < 0) { - return ret; + if (hport->depth > HUB_MAX_DEPTH) { + USB_LOG_ERR("Hub depth(%d) is overflow\r\n", hport->depth); + return -USB_ERR_INVAL; } - parse_hub_descriptor(&hub->hub_desc, USB_SIZEOF_HUB_DESC); + /* + * Super-Speed hubs need to know their depth to be able to + * parse the bits of the route-string that correspond to + * their downstream port number. + * + */ + if ((hport->depth != 0) && (hport->speed == USB_SPEED_SUPER)) { + ret = usbh_hub_set_depth(hub, hport->depth - 1); + if (ret < 0) { + USB_LOG_ERR("Unable to set hub depth \r\n"); + return ret; + } + } - for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + /* Get hub descriptor. */ + if (hport->speed == USB_SPEED_SUPER) { + ret = _usbh_hub_get_hub_ss_descriptor(hub, (uint8_t *)&hub->hub_ss_desc); + if (ret < 0) { + return ret; + } + + parse_hub_ss_descriptor(&hub->hub_ss_desc, USB_SIZEOF_HUB_SS_DESC); + hub->nports = hub->hub_ss_desc.bNbrPorts; + hub->powerdelay = hub->hub_ss_desc.bPwrOn2PwrGood * 2; + hub->tt_think = 0U; + } else { + ret = _usbh_hub_get_hub_descriptor(hub, (uint8_t *)&hub->hub_desc); + if (ret < 0) { + return ret; + } + + parse_hub_descriptor(&hub->hub_desc, USB_SIZEOF_HUB_DESC); + hub->nports = hub->hub_desc.bNbrPorts; + hub->powerdelay = hub->hub_desc.bPwrOn2PwrGood * 2; + hub->tt_think = ((hub->hub_desc.wHubCharacteristics & HUB_CHAR_TTTT_MASK) >> 5); + } + + for (uint8_t port = 0; port < hub->nports; port++) { hub->child[port].port = port + 1; hub->child[port].parent = hub; hub->child[port].bus = hport->bus; } + if (hport->device_desc.bDeviceProtocol == HUB_PROTOCOL_MTT) { + hub->ismtt = 1; + } else { + hub->ismtt = 0; + } + ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc; if (ep_desc->bEndpointAddress & 0x80) { USBH_EP_INIT(hub->intin, ep_desc); @@ -334,28 +392,16 @@ static int usbh_hub_connect(struct usbh_hubport *hport, uint8_t intf) return -1; } - if (hport->speed == USB_SPEED_SUPER) { - uint16_t depth = 0; - struct usbh_hubport *parent = hport->parent->parent; - while (parent) { - depth++; - parent = parent->parent->parent; - } - - ret = usbh_hub_set_depth(hub, depth); - if (ret < 0) { - return ret; - } - } - - for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + for (uint8_t port = 0; port < hub->nports; port++) { ret = usbh_hub_set_feature(hub, port + 1, HUB_PORT_FEATURE_POWER); if (ret < 0) { return ret; } } - for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + usb_osal_msleep(hub->powerdelay); + + for (uint8_t port = 0; port < hub->nports; port++) { ret = usbh_hub_get_portstatus(hub, port + 1, &port_status); USB_LOG_INFO("port %u, status:0x%02x, change:0x%02x\r\n", port + 1, port_status.wPortStatus, port_status.wPortChange); if (ret < 0) { @@ -395,7 +441,7 @@ static int usbh_hub_disconnect(struct usbh_hubport *hport, uint8_t intf) usb_osal_timer_delete(hub->int_timer); } - for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + for (uint8_t port = 0; port < hub->nports; port++) { child = &hub->child[port]; usbh_hubport_release(child); child->parent = NULL; @@ -415,7 +461,7 @@ static void usbh_hub_events(struct usbh_hub *hub) { struct usbh_hubport *child; struct hub_port_status port_status; - uint8_t portchange_index; + uint16_t portchange_index; uint16_t portstatus; uint16_t portchange; uint16_t mask; @@ -429,11 +475,10 @@ static void usbh_hub_events(struct usbh_hub *hub) } flags = usb_osal_enter_critical_section(); - portchange_index = hub->int_buffer[0]; - hub->int_buffer[0] &= ~portchange_index; + memcpy(&portchange_index, hub->int_buffer, 2); usb_osal_leave_critical_section(flags); - for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + for (uint8_t port = 0; port < hub->nports; port++) { USB_LOG_DBG("Port change:0x%02x\r\n", portchange_index); if (!(portchange_index & (1 << (port + 1)))) { @@ -539,12 +584,23 @@ static void usbh_hub_events(struct usbh_hub *hub) } } - if (portstatus & HUB_PORT_STATUS_HIGH_SPEED) { - speed = USB_SPEED_HIGH; - } else if (portstatus & HUB_PORT_STATUS_LOW_SPEED) { - speed = USB_SPEED_LOW; + /* + * Figure out device speed. This is a bit tricky because + * HUB_PORT_STATUS_POWER_SS and HUB_PORT_STATUS_LOW_SPEED share the same bit. + */ + if (portstatus & HUB_PORT_STATUS_POWER) { + if (portstatus & HUB_PORT_STATUS_HIGH_SPEED) { + speed = USB_SPEED_HIGH; + } else if (portstatus & HUB_PORT_STATUS_LOW_SPEED) { + speed = USB_SPEED_LOW; + } else { + speed = USB_SPEED_FULL; + } + } else if (portstatus & HUB_PORT_STATUS_POWER_SS) { + speed = USB_SPEED_SUPER; } else { - speed = USB_SPEED_FULL; + USB_LOG_WRN("Port %u does not enable power\r\n", port + 1); + continue; } child = &hub->child[port]; @@ -553,6 +609,7 @@ static void usbh_hub_events(struct usbh_hub *hub) memset(child, 0, sizeof(struct usbh_hubport)); child->parent = hub; + child->depth = (hub->parent ? hub->parent->depth : 0) + 1; child->connected = true; child->port = port + 1; child->speed = speed; @@ -624,7 +681,7 @@ int usbh_hub_initialize(struct usbh_bus *bus) hub->is_roothub = true; hub->parent = NULL; hub->hub_addr = 1; - hub->hub_desc.bNbrPorts = CONFIG_USBHOST_MAX_RHPORTS; + hub->nports = CONFIG_USBHOST_MAX_RHPORTS; hub->int_buffer = bus->hcd.roothub_intbuf; hub->bus = bus; @@ -652,7 +709,7 @@ int usbh_hub_deinitialize(struct usbh_bus *bus) flags = usb_osal_enter_critical_section(); hub = &bus->hcd.roothub; - for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + for (uint8_t port = 0; port < hub->nports; port++) { hport = &hub->child[port]; usbh_hubport_release(hport); @@ -677,9 +734,9 @@ const struct usbh_class_driver hub_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info hub_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS, - .class = USB_DEVICE_CLASS_HUB, - .subclass = 0, - .protocol = 0, + .bInterfaceClass = USB_DEVICE_CLASS_HUB, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, .id_table = NULL, .class_driver = &hub_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/hub/usbh_hub.h b/rt-thread/components/drivers/usb/cherryusb/class/hub/usbh_hub.h index 1f34c53..dcce660 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/hub/usbh_hub.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/hub/usbh_hub.h @@ -10,10 +10,6 @@ struct usbh_hub; -#define USBH_HUB_MAX_PORTS 4 -/* Maximum size of an interrupt IN transfer */ -#define USBH_HUB_INTIN_BUFSIZE ((USBH_HUB_MAX_PORTS + 8) >> 3) - #ifdef __cplusplus extern "C" { #endif diff --git a/rt-thread/components/drivers/usb/cherryusb/class/msc/usbd_msc.c b/rt-thread/components/drivers/usb/cherryusb/class/msc/usbd_msc.c index 055cdc4..1f84d02 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/msc/usbd_msc.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/msc/usbd_msc.c @@ -9,6 +9,8 @@ #include "usb_scsi.h" #if defined(CONFIG_USBDEV_MSC_THREAD) #include "usb_osal.h" +#elif defined(CONFIG_USBDEV_MSC_POLLING) +#include "chry_ringbuffer.h" #endif #define MSD_OUT_EP_IDX 0 @@ -50,6 +52,10 @@ USB_NOCACHE_RAM_SECTION struct usbd_msc_priv { usb_osal_mq_t usbd_msc_mq; usb_osal_thread_t usbd_msc_thread; uint32_t nbytes; +#elif defined(CONFIG_USBDEV_MSC_POLLING) + chry_ringbuffer_t msc_rb; + uint8_t msc_rb_pool[2]; + uint32_t nbytes; #endif } g_usbd_msc[CONFIG_USBDEV_MAX_BUS]; @@ -94,21 +100,25 @@ static int msc_storage_class_interface_request_handler(uint8_t busid, struct usb void msc_storage_notify_handler(uint8_t busid, uint8_t event, void *arg) { + (void)arg; + switch (event) { case USBD_EVENT_INIT: -#ifdef CONFIG_USBDEV_MSC_THREAD +#if defined(CONFIG_USBDEV_MSC_THREAD) g_usbd_msc[busid].usbd_msc_mq = usb_osal_mq_create(1); if (g_usbd_msc[busid].usbd_msc_mq == NULL) { USB_LOG_ERR("No memory to alloc for g_usbd_msc[busid].usbd_msc_mq\r\n"); } - g_usbd_msc[busid].usbd_msc_thread = usb_osal_thread_create("usbd_msc", CONFIG_USBDEV_MSC_STACKSIZE, CONFIG_USBDEV_MSC_PRIO, usbdev_msc_thread, (void *)busid); + g_usbd_msc[busid].usbd_msc_thread = usb_osal_thread_create("usbd_msc", CONFIG_USBDEV_MSC_STACKSIZE, CONFIG_USBDEV_MSC_PRIO, usbdev_msc_thread, (void *)(uint32_t)busid); if (g_usbd_msc[busid].usbd_msc_thread == NULL) { USB_LOG_ERR("No memory to alloc for g_usbd_msc[busid].usbd_msc_thread\r\n"); } +#elif defined(CONFIG_USBDEV_MSC_POLLING) + chry_ringbuffer_init(&g_usbd_msc[busid].msc_rb, g_usbd_msc[busid].msc_rb_pool, sizeof(g_usbd_msc[busid].msc_rb_pool)); #endif break; case USBD_EVENT_DEINIT: -#ifdef CONFIG_USBDEV_MSC_THREAD +#if defined(CONFIG_USBDEV_MSC_THREAD) if (g_usbd_msc[busid].usbd_msc_mq) { usb_osal_mq_delete(g_usbd_msc[busid].usbd_msc_mq); } @@ -500,6 +510,9 @@ static bool SCSI_readCapacity10(uint8_t busid, uint8_t **data, uint32_t *len) static bool SCSI_read10(uint8_t busid, uint8_t **data, uint32_t *len) { + (void)data; + (void)len; + if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x80U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) { SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND); return false; @@ -522,9 +535,12 @@ static bool SCSI_read10(uint8_t busid, uint8_t **data, uint32_t *len) return false; } g_usbd_msc[busid].stage = MSC_DATA_IN; -#ifdef CONFIG_USBDEV_MSC_THREAD +#if defined(CONFIG_USBDEV_MSC_THREAD) usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_IN); return true; +#elif defined(CONFIG_USBDEV_MSC_POLLING) + chry_ringbuffer_write_byte(&g_usbd_msc[busid].msc_rb, MSC_DATA_IN); + return true; #else return SCSI_processRead(busid); #endif @@ -532,6 +548,9 @@ static bool SCSI_read10(uint8_t busid, uint8_t **data, uint32_t *len) static bool SCSI_read12(uint8_t busid, uint8_t **data, uint32_t *len) { + (void)data; + (void)len; + if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x80U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) { SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND); return false; @@ -554,9 +573,12 @@ static bool SCSI_read12(uint8_t busid, uint8_t **data, uint32_t *len) return false; } g_usbd_msc[busid].stage = MSC_DATA_IN; -#ifdef CONFIG_USBDEV_MSC_THREAD +#if defined(CONFIG_USBDEV_MSC_THREAD) usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_IN); return true; +#elif defined(CONFIG_USBDEV_MSC_POLLING) + chry_ringbuffer_write_byte(&g_usbd_msc[busid].msc_rb, MSC_DATA_IN); + return true; #else return SCSI_processRead(busid); #endif @@ -565,6 +587,10 @@ static bool SCSI_read12(uint8_t busid, uint8_t **data, uint32_t *len) static bool SCSI_write10(uint8_t busid, uint8_t **data, uint32_t *len) { uint32_t data_len = 0; + + (void)data; + (void)len; + if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x00U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) { SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND); return false; @@ -594,6 +620,10 @@ static bool SCSI_write10(uint8_t busid, uint8_t **data, uint32_t *len) static bool SCSI_write12(uint8_t busid, uint8_t **data, uint32_t *len) { uint32_t data_len = 0; + + (void)data; + (void)len; + if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x00U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) { SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND); return false; @@ -803,6 +833,8 @@ static bool SCSI_CBWDecode(uint8_t busid, uint32_t nbytes) void mass_storage_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) { + (void)ep; + switch (g_usbd_msc[busid].stage) { case MSC_READ_CBW: if (SCSI_CBWDecode(busid, nbytes) == false) { @@ -815,9 +847,12 @@ void mass_storage_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) switch (g_usbd_msc[busid].cbw.CB[0]) { case SCSI_CMD_WRITE10: case SCSI_CMD_WRITE12: -#ifdef CONFIG_USBDEV_MSC_THREAD +#if defined(CONFIG_USBDEV_MSC_THREAD) g_usbd_msc[busid].nbytes = nbytes; usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_OUT); +#elif defined(CONFIG_USBDEV_MSC_POLLING) + g_usbd_msc[busid].nbytes = nbytes; + chry_ringbuffer_write_byte(&g_usbd_msc[busid].msc_rb, MSC_DATA_OUT); #else if (SCSI_processWrite(busid, nbytes) == false) { usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ @@ -835,13 +870,18 @@ void mass_storage_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) void mass_storage_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes) { + (void)ep; + (void)nbytes; + switch (g_usbd_msc[busid].stage) { case MSC_DATA_IN: switch (g_usbd_msc[busid].cbw.CB[0]) { case SCSI_CMD_READ10: case SCSI_CMD_READ12: -#ifdef CONFIG_USBDEV_MSC_THREAD +#if defined(CONFIG_USBDEV_MSC_THREAD) usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_IN); +#elif defined(CONFIG_USBDEV_MSC_POLLING) + chry_ringbuffer_write_byte(&g_usbd_msc[busid].msc_rb, MSC_DATA_IN); #else if (SCSI_processRead(busid) == false) { usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ @@ -870,19 +910,38 @@ void mass_storage_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes) } } -#ifdef CONFIG_USBDEV_MSC_THREAD +#if defined(CONFIG_USBDEV_MSC_THREAD) static void usbdev_msc_thread(void *argument) { uintptr_t event; int ret; - uint8_t busid = (uint8_t)argument; + uint8_t busid = (uint8_t)(uint32_t)argument; while (1) { ret = usb_osal_mq_recv(g_usbd_msc[busid].usbd_msc_mq, (uintptr_t *)&event, USB_OSAL_WAITING_FOREVER); if (ret < 0) { continue; } - USB_LOG_DBG("%d\r\n", event); + USB_LOG_DBG("event:%d\r\n", event); + if (event == MSC_DATA_OUT) { + if (SCSI_processWrite(busid, g_usbd_msc[busid].nbytes) == false) { + usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ + } + } else if (event == MSC_DATA_IN) { + if (SCSI_processRead(busid) == false) { + usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ + } + } else { + } + } +} +#elif defined(CONFIG_USBDEV_MSC_POLLING) +void usbd_msc_polling(uint8_t busid) +{ + uint8_t event; + + if (chry_ringbuffer_read_byte(&g_usbd_msc[busid].msc_rb, &event)) { + USB_LOG_DBG("event:%d\r\n", event); if (event == MSC_DATA_OUT) { if (SCSI_processWrite(busid, g_usbd_msc[busid].nbytes) == false) { usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/ @@ -918,9 +977,9 @@ struct usbd_interface *usbd_msc_init_intf(uint8_t busid, struct usbd_interface * for (uint8_t i = 0u; i <= g_usbd_msc[busid].max_lun; i++) { usbd_msc_get_cap(busid, i, &g_usbd_msc[busid].scsi_blk_nbr[i], &g_usbd_msc[busid].scsi_blk_size[i]); - if (g_usbd_msc[busid].scsi_blk_size[i] > CONFIG_USBDEV_MSC_MAX_BUFSIZE) { - USB_LOG_ERR("msc block buffer overflow\r\n"); - return NULL; + if (CONFIG_USBDEV_MSC_MAX_BUFSIZE % g_usbd_msc[busid].scsi_blk_size[i]) { + USB_LOG_ERR("CONFIG_USBDEV_MSC_MAX_BUFSIZE must be a multiple of block size\r\n"); + while(1){} } } @@ -932,7 +991,7 @@ void usbd_msc_set_readonly(uint8_t busid, bool readonly) g_usbd_msc[busid].readonly = readonly; } -bool usbd_msc_set_popup(uint8_t busid) +bool usbd_msc_get_popup(uint8_t busid) { return g_usbd_msc[busid].popup; } diff --git a/rt-thread/components/drivers/usb/cherryusb/class/msc/usbd_msc.h b/rt-thread/components/drivers/usb/cherryusb/class/msc/usbd_msc.h index c71f64a..63a8752 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/msc/usbd_msc.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/msc/usbd_msc.h @@ -23,7 +23,9 @@ int usbd_msc_sector_read(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *b int usbd_msc_sector_write(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length); void usbd_msc_set_readonly(uint8_t busid, bool readonly); -bool usbd_msc_set_popup(uint8_t busid); +bool usbd_msc_get_popup(uint8_t busid); + +void usbd_msc_polling(uint8_t busid); #ifdef __cplusplus } diff --git a/rt-thread/components/drivers/usb/cherryusb/class/msc/usbh_msc.c b/rt-thread/components/drivers/usb/cherryusb/class/msc/usbh_msc.c index 42e12d9..96cb53d 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/msc/usbh_msc.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/msc/usbh_msc.c @@ -15,7 +15,7 @@ #define MSC_INQUIRY_TIMEOUT 500 -USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_msc_buf[64]; +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_msc_buf[CONFIG_USBHOST_MAX_MSC_CLASS][USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)]; static struct usbh_msc g_msc_class[CONFIG_USBHOST_MAX_MSC_CLASS]; static uint32_t g_devinuse = 0; @@ -23,11 +23,11 @@ static struct usbh_msc_modeswitch_config *g_msc_modeswitch_config = NULL; static struct usbh_msc *usbh_msc_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_MSC_CLASS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_msc_class[devno], 0, sizeof(struct usbh_msc)); g_msc_class[devno].sdchar = 'a' + devno; return &g_msc_class[devno]; @@ -38,10 +38,10 @@ static struct usbh_msc *usbh_msc_class_alloc(void) static void usbh_msc_class_free(struct usbh_msc *msc_class) { - int devno = msc_class->sdchar - 'a'; + uint8_t devno = msc_class->sdchar - 'a'; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(msc_class, 0, sizeof(struct usbh_msc)); } @@ -87,6 +87,8 @@ static void usbh_msc_cbw_dump(struct CBW *cbw) static void usbh_msc_csw_dump(struct CSW *csw) { + (void)csw; + USB_LOG_DBG("CSW:\r\n"); USB_LOG_DBG(" signature: 0x%08x\r\n", (unsigned int)csw->dSignature); USB_LOG_DBG(" tag: 0x%08x\r\n", (unsigned int)csw->dTag); @@ -182,14 +184,14 @@ static inline int usbh_msc_scsi_testunitready(struct usbh_msc *msc_class) struct CBW *cbw; /* Construct the CBW */ - cbw = (struct CBW *)g_msc_buf; + cbw = (struct CBW *)g_msc_buf[msc_class->sdchar - 'a']; memset(cbw, 0, USB_SIZEOF_MSC_CBW); cbw->dSignature = MSC_CBW_Signature; cbw->bCBLength = SCSICMD_TESTUNITREADY_SIZEOF; cbw->CB[0] = SCSI_CMD_TESTUNITREADY; - return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, NULL, MSC_INQUIRY_TIMEOUT); + return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf[msc_class->sdchar - 'a'], NULL, MSC_INQUIRY_TIMEOUT); } static inline int usbh_msc_scsi_requestsense(struct usbh_msc *msc_class) @@ -197,7 +199,7 @@ static inline int usbh_msc_scsi_requestsense(struct usbh_msc *msc_class) struct CBW *cbw; /* Construct the CBW */ - cbw = (struct CBW *)g_msc_buf; + cbw = (struct CBW *)g_msc_buf[msc_class->sdchar - 'a']; memset(cbw, 0, USB_SIZEOF_MSC_CBW); cbw->dSignature = MSC_CBW_Signature; @@ -207,7 +209,7 @@ static inline int usbh_msc_scsi_requestsense(struct usbh_msc *msc_class) cbw->CB[0] = SCSI_CMD_REQUESTSENSE; cbw->CB[4] = SCSIRESP_FIXEDSENSEDATA_SIZEOF; - return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, g_msc_buf, MSC_INQUIRY_TIMEOUT); + return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf[msc_class->sdchar - 'a'], g_msc_buf[msc_class->sdchar - 'a'], MSC_INQUIRY_TIMEOUT); } static inline int usbh_msc_scsi_inquiry(struct usbh_msc *msc_class) @@ -215,7 +217,7 @@ static inline int usbh_msc_scsi_inquiry(struct usbh_msc *msc_class) struct CBW *cbw; /* Construct the CBW */ - cbw = (struct CBW *)g_msc_buf; + cbw = (struct CBW *)g_msc_buf[msc_class->sdchar - 'a']; memset(cbw, 0, USB_SIZEOF_MSC_CBW); cbw->dSignature = MSC_CBW_Signature; @@ -225,7 +227,7 @@ static inline int usbh_msc_scsi_inquiry(struct usbh_msc *msc_class) cbw->CB[0] = SCSI_CMD_INQUIRY; cbw->CB[4] = SCSIRESP_INQUIRY_SIZEOF; - return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, g_msc_buf, MSC_INQUIRY_TIMEOUT); + return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf[msc_class->sdchar - 'a'], g_msc_buf[msc_class->sdchar - 'a'], MSC_INQUIRY_TIMEOUT); } static inline int usbh_msc_scsi_readcapacity10(struct usbh_msc *msc_class) @@ -233,7 +235,7 @@ static inline int usbh_msc_scsi_readcapacity10(struct usbh_msc *msc_class) struct CBW *cbw; /* Construct the CBW */ - cbw = (struct CBW *)g_msc_buf; + cbw = (struct CBW *)g_msc_buf[msc_class->sdchar - 'a']; memset(cbw, 0, USB_SIZEOF_MSC_CBW); cbw->dSignature = MSC_CBW_Signature; @@ -242,7 +244,7 @@ static inline int usbh_msc_scsi_readcapacity10(struct usbh_msc *msc_class) cbw->bCBLength = SCSICMD_READCAPACITY10_SIZEOF; cbw->CB[0] = SCSI_CMD_READCAPACITY10; - return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, g_msc_buf, MSC_INQUIRY_TIMEOUT); + return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf[msc_class->sdchar - 'a'], g_msc_buf[msc_class->sdchar - 'a'], MSC_INQUIRY_TIMEOUT); } static inline void usbh_msc_modeswitch(struct usbh_msc *msc_class, const uint8_t *message) @@ -250,11 +252,11 @@ static inline void usbh_msc_modeswitch(struct usbh_msc *msc_class, const uint8_t struct CBW *cbw; /* Construct the CBW */ - cbw = (struct CBW *)g_msc_buf; + cbw = (struct CBW *)g_msc_buf[msc_class->sdchar - 'a']; - memcpy(g_msc_buf, message, 31); + memcpy(g_msc_buf[msc_class->sdchar - 'a'], message, 31); - usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, NULL, MSC_INQUIRY_TIMEOUT); + usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf[msc_class->sdchar - 'a'], NULL, MSC_INQUIRY_TIMEOUT); } static int usbh_msc_connect(struct usbh_hubport *hport, uint8_t intf) @@ -274,12 +276,17 @@ static int usbh_msc_connect(struct usbh_hubport *hport, uint8_t intf) hport->config.intf[intf].priv = msc_class; - ret = usbh_msc_get_maxlun(msc_class, g_msc_buf); + ret = usbh_msc_get_maxlun(msc_class, g_msc_buf[msc_class->sdchar - 'a']); if (ret < 0) { - return ret; + if (ret == -USB_ERR_STALL) { + USB_LOG_WRN("Device does not support multiple LUNs\r\n"); + g_msc_buf[msc_class->sdchar - 'a'][0] = 0; + } else { + return ret; + } } - USB_LOG_INFO("Get max LUN:%u\r\n", g_msc_buf[0] + 1); + USB_LOG_INFO("Get max LUN:%u\r\n", g_msc_buf[msc_class->sdchar - 'a'][0] + 1); for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) { ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc; @@ -370,13 +377,12 @@ static int usbh_msc_disconnect(struct usbh_hubport *hport, uint8_t intf) return ret; } - int usbh_msc_scsi_write10(struct usbh_msc *msc_class, uint32_t start_sector, const uint8_t *buffer, uint32_t nsectors) { struct CBW *cbw; /* Construct the CBW */ - cbw = (struct CBW *)g_msc_buf; + cbw = (struct CBW *)g_msc_buf[msc_class->sdchar - 'a']; memset(cbw, 0, USB_SIZEOF_MSC_CBW); cbw->dSignature = MSC_CBW_Signature; @@ -387,7 +393,7 @@ int usbh_msc_scsi_write10(struct usbh_msc *msc_class, uint32_t start_sector, con SET_BE32(&cbw->CB[2], start_sector); SET_BE16(&cbw->CB[7], nsectors); - return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, (uint8_t *)buffer, CONFIG_USBHOST_MSC_TIMEOUT); + return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf[msc_class->sdchar - 'a'], (uint8_t *)buffer, CONFIG_USBHOST_MSC_TIMEOUT); } int usbh_msc_scsi_read10(struct usbh_msc *msc_class, uint32_t start_sector, const uint8_t *buffer, uint32_t nsectors) @@ -395,7 +401,7 @@ int usbh_msc_scsi_read10(struct usbh_msc *msc_class, uint32_t start_sector, cons struct CBW *cbw; /* Construct the CBW */ - cbw = (struct CBW *)g_msc_buf; + cbw = (struct CBW *)g_msc_buf[msc_class->sdchar - 'a']; memset(cbw, 0, USB_SIZEOF_MSC_CBW); cbw->dSignature = MSC_CBW_Signature; @@ -407,7 +413,7 @@ int usbh_msc_scsi_read10(struct usbh_msc *msc_class, uint32_t start_sector, cons SET_BE32(&cbw->CB[2], start_sector); SET_BE16(&cbw->CB[7], nsectors); - return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, (uint8_t *)buffer, CONFIG_USBHOST_MSC_TIMEOUT); + return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf[msc_class->sdchar - 'a'], (uint8_t *)buffer, CONFIG_USBHOST_MSC_TIMEOUT); } void usbh_msc_modeswitch_enable(struct usbh_msc_modeswitch_config *config) @@ -421,10 +427,12 @@ void usbh_msc_modeswitch_enable(struct usbh_msc_modeswitch_config *config) __WEAK void usbh_msc_run(struct usbh_msc *msc_class) { + (void)msc_class; } __WEAK void usbh_msc_stop(struct usbh_msc *msc_class) { + (void)msc_class; } const struct usbh_class_driver msc_class_driver = { @@ -435,9 +443,9 @@ const struct usbh_class_driver msc_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info msc_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, - .class = USB_DEVICE_CLASS_MASS_STORAGE, - .subclass = MSC_SUBCLASS_SCSI, - .protocol = MSC_PROTOCOL_BULK_ONLY, + .bInterfaceClass = USB_DEVICE_CLASS_MASS_STORAGE, + .bInterfaceSubClass = MSC_SUBCLASS_SCSI, + .bInterfaceProtocol = MSC_PROTOCOL_BULK_ONLY, .id_table = NULL, .class_driver = &msc_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/template/usbh_xxx.c b/rt-thread/components/drivers/usb/cherryusb/class/template/usbh_xxx.c index 22b3636..4456b3c 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/template/usbh_xxx.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/template/usbh_xxx.c @@ -9,11 +9,11 @@ static uint32_t g_devinuse = 0; static struct usbh_xxx *usbh_xxx_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_CUSTOM_CLASS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_xxx_class[devno], 0, sizeof(struct usbh_xxx)); g_xxx_class[devno].minor = devno; return &g_xxx_class[devno]; @@ -24,10 +24,10 @@ static struct usbh_xxx *usbh_xxx_class_alloc(void) static void usbh_xxx_class_free(struct usbh_xxx *xxx_class) { - int devno = xxx_class->minor; + uint8_t devno = xxx_class->minor; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(xxx_class, 0, sizeof(struct usbh_xxx)); } @@ -89,9 +89,9 @@ static const struct usbh_class_driver xxx_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info xxx_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, - .class = 0, - .subclass = 0, - .protocol = 0, + .bInterfaceClass = 0, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, .id_table = NULL, .class_driver = &xxx_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.c b/rt-thread/components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.c index 9d3e973..66c1f07 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.c @@ -70,7 +70,7 @@ static int usbh_asix_read_cmd(struct usbh_asix *asix_class, setup->wLength = size; ret = usbh_control_transfer(asix_class->hport, setup, g_asix_buf); - if (ret < 0) { + if (ret < 8) { return ret; } memcpy(data, g_asix_buf, ret - 8); @@ -98,9 +98,12 @@ static int usbh_asix_write_cmd(struct usbh_asix *asix_class, setup->wIndex = index; setup->wLength = size; - memcpy(g_asix_buf, data, size); - - return usbh_control_transfer(asix_class->hport, setup, g_asix_buf); + if (data && size) { + memcpy(g_asix_buf, data, size); + return usbh_control_transfer(asix_class->hport, setup, g_asix_buf); + } else { + return usbh_control_transfer(asix_class->hport, setup, NULL); + } } static int usbh_asix_mdio_write(struct usbh_asix *asix_class, int phy_id, int loc, int val) @@ -680,6 +683,7 @@ void usbh_asix_rx_thread(void *argument) uint32_t transfer_size = (16 * 1024); #endif + (void)argument; USB_LOG_INFO("Create asix rx thread\r\n"); // clang-format off find_class: @@ -742,7 +746,7 @@ find_class: #else if ((g_asix_rx_length + (16 * 1024)) > CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE) { #endif - USB_LOG_ERR("Rx packet is overflow, please ruduce tcp window size or increase CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE\r\n"); + USB_LOG_ERR("Rx packet is overflow, please reduce tcp window size or increase CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE\r\n"); while (1) { } } @@ -791,10 +795,12 @@ int usbh_asix_eth_output(uint32_t buflen) __WEAK void usbh_asix_run(struct usbh_asix *asix_class) { + (void)asix_class; } __WEAK void usbh_asix_stop(struct usbh_asix *asix_class) { + (void)asix_class; } static const uint16_t asix_id_table[][2] = { @@ -811,9 +817,9 @@ static const struct usbh_class_driver asix_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info asix_class_info = { .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS, - .class = 0xff, - .subclass = 0x00, - .protocol = 0x00, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, .id_table = asix_id_table, .class_driver = &asix_class_driver }; \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.c b/rt-thread/components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.c index dde665d..10fc048 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.c @@ -961,7 +961,7 @@ static int usbh_rtl8152_read_regs(struct usbh_rtl8152 *rtl8152_class, setup->wLength = size; ret = usbh_control_transfer(rtl8152_class->hport, setup, g_rtl8152_buf); - if (ret < 0) { + if (ret < 8) { return ret; } memcpy(data, g_rtl8152_buf, ret - 8); @@ -997,9 +997,10 @@ static int generic_ocp_read(struct usbh_rtl8152 *tp, uint16_t index, uint16_t si { uint16_t limit = 64; int ret = 0; + uint8_t *buf = data; /* both size and indix must be 4 bytes align */ - if ((size & 3) || !size || (index & 3) || !data) + if ((size & 3) || !size || (index & 3) || !buf) return -USB_ERR_INVAL; if ((uint32_t)index + (uint32_t)size > 0xffff) @@ -1007,20 +1008,20 @@ static int generic_ocp_read(struct usbh_rtl8152 *tp, uint16_t index, uint16_t si while (size) { if (size > limit) { - ret = usbh_rtl8152_read_regs(tp, index, type, limit, data); + ret = usbh_rtl8152_read_regs(tp, index, type, limit, buf); if (ret < 0) break; index += limit; - data += limit; + buf += limit; size -= limit; } else { - ret = usbh_rtl8152_read_regs(tp, index, type, size, data); + ret = usbh_rtl8152_read_regs(tp, index, type, size, buf); if (ret < 0) break; index += size; - data += size; + buf += size; size = 0; break; } @@ -1035,9 +1036,10 @@ static int generic_ocp_write(struct usbh_rtl8152 *tp, uint16_t index, uint16_t b int ret; uint16_t byteen_start, byteen_end, byen; uint16_t limit = 512; + uint8_t *buf = data; /* both size and indix must be 4 bytes align */ - if ((size & 3) || !size || (index & 3) || !data) + if ((size & 3) || !size || (index & 3) || !buf) return -USB_ERR_INVAL; if ((uint32_t)index + (uint32_t)size > 0xffff) @@ -1050,12 +1052,12 @@ static int generic_ocp_write(struct usbh_rtl8152 *tp, uint16_t index, uint16_t b /* Split the first DWORD if the byte_en is not 0xff */ if (byen != BYTE_EN_DWORD) { - ret = usbh_rtl8152_write_regs(tp, index, type | byen, 4, data); + ret = usbh_rtl8152_write_regs(tp, index, type | byen, 4, buf); if (ret < 0) goto error1; index += 4; - data += 4; + buf += 4; size -= 4; } @@ -1070,22 +1072,22 @@ static int generic_ocp_write(struct usbh_rtl8152 *tp, uint16_t index, uint16_t b if (size > limit) { ret = usbh_rtl8152_write_regs(tp, index, type | BYTE_EN_DWORD, - limit, data); + limit, buf); if (ret < 0) goto error1; index += limit; - data += limit; + buf += limit; size -= limit; } else { ret = usbh_rtl8152_write_regs(tp, index, type | BYTE_EN_DWORD, - size, data); + size, buf); if (ret < 0) goto error1; index += size; - data += size; + buf += size; size = 0; break; } @@ -1093,7 +1095,7 @@ static int generic_ocp_write(struct usbh_rtl8152 *tp, uint16_t index, uint16_t b /* Set the last DWORD */ if (byen != BYTE_EN_DWORD) - ret = usbh_rtl8152_write_regs(tp, index, type | byen, 4, data); + ret = usbh_rtl8152_write_regs(tp, index, type | byen, 4, buf); } error1: @@ -2140,6 +2142,7 @@ void usbh_rtl8152_rx_thread(void *argument) uint32_t transfer_size = (16 * 1024); #endif + (void)argument; USB_LOG_INFO("Create rtl8152 rx thread\r\n"); // clang-format off find_class: @@ -2210,7 +2213,7 @@ find_class: #else if ((g_rtl8152_rx_length + (16 * 1024)) > CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE) { #endif - USB_LOG_ERR("Rx packet is overflow, please ruduce tcp window size or increase CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE\r\n"); + USB_LOG_ERR("Rx packet is overflow, please reduce tcp window size or increase CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE\r\n"); while (1) { } } @@ -2248,10 +2251,12 @@ int usbh_rtl8152_eth_output(uint32_t buflen) __WEAK void usbh_rtl8152_run(struct usbh_rtl8152 *rtl8152_class) { + (void)rtl8152_class; } __WEAK void usbh_rtl8152_stop(struct usbh_rtl8152 *rtl8152_class) { + (void)rtl8152_class; } static const uint16_t rtl_id_table[][2] = { @@ -2267,9 +2272,9 @@ static const struct usbh_class_driver rtl8152_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info rtl8152_class_info = { .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS, - .class = 0xff, - .subclass = 0x00, - .protocol = 0x00, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, .id_table = rtl_id_table, .class_driver = &rtl8152_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.c b/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.c index 7c275e1..3bee462 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.c @@ -17,11 +17,11 @@ static uint32_t g_devinuse = 0; static struct usbh_ch34x *usbh_ch34x_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_ch34x_class[devno], 0, sizeof(struct usbh_ch34x)); g_ch34x_class[devno].minor = devno; return &g_ch34x_class[devno]; @@ -32,10 +32,10 @@ static struct usbh_ch34x *usbh_ch34x_class_alloc(void) static void usbh_ch34x_class_free(struct usbh_ch34x *ch34x_class) { - int devno = ch34x_class->minor; + uint8_t devno = ch34x_class->minor; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(ch34x_class, 0, sizeof(struct usbh_ch34x)); } @@ -349,10 +349,12 @@ int usbh_ch34x_bulk_out_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer __WEAK void usbh_ch34x_run(struct usbh_ch34x *ch34x_class) { + (void)ch34x_class; } __WEAK void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class) { + (void)ch34x_class; } static const uint16_t ch34x_id_table[][2] = { @@ -368,9 +370,9 @@ const struct usbh_class_driver ch34x_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info ch34x_class_info = { .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS, - .class = 0xff, - .subclass = 0x00, - .protocol = 0x00, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, .id_table = ch34x_id_table, .class_driver = &ch34x_class_driver }; \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.c b/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.c index ae882fb..6f41b41 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.c @@ -10,18 +10,18 @@ USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cp210x_buf[64]; -#define CONFIG_USBHOST_MAX_CP210X_CLASS 4 +#define CONFIG_USBHOST_MAX_CP210X_CLASS 1 static struct usbh_cp210x g_cp210x_class[CONFIG_USBHOST_MAX_CP210X_CLASS]; static uint32_t g_devinuse = 0; static struct usbh_cp210x *usbh_cp210x_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_cp210x_class[devno], 0, sizeof(struct usbh_cp210x)); g_cp210x_class[devno].minor = devno; return &g_cp210x_class[devno]; @@ -32,10 +32,10 @@ static struct usbh_cp210x *usbh_cp210x_class_alloc(void) static void usbh_cp210x_class_free(struct usbh_cp210x *cp210x_class) { - int devno = cp210x_class->minor; + uint8_t devno = cp210x_class->minor; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(cp210x_class, 0, sizeof(struct usbh_cp210x)); } @@ -298,10 +298,12 @@ int usbh_cp210x_bulk_out_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buf __WEAK void usbh_cp210x_run(struct usbh_cp210x *cp210x_class) { + (void)cp210x_class; } __WEAK void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class) { + (void)cp210x_class; } static const uint16_t cp210x_id_table[][2] = { @@ -317,9 +319,9 @@ const struct usbh_class_driver cp210x_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info cp210x_class_info = { .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS, - .class = 0xff, - .subclass = 0x00, - .protocol = 0x00, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, .id_table = cp210x_id_table, .class_driver = &cp210x_class_driver }; \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.c b/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.c index ba5aae5..82defaa 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.c @@ -10,18 +10,18 @@ USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_ftdi_buf[64]; -#define CONFIG_USBHOST_MAX_FTDI_CLASS 4 +#define CONFIG_USBHOST_MAX_FTDI_CLASS 1 static struct usbh_ftdi g_ftdi_class[CONFIG_USBHOST_MAX_FTDI_CLASS]; static uint32_t g_devinuse = 0; static struct usbh_ftdi *usbh_ftdi_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_FTDI_CLASS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_ftdi_class[devno], 0, sizeof(struct usbh_ftdi)); g_ftdi_class[devno].minor = devno; return &g_ftdi_class[devno]; @@ -32,10 +32,10 @@ static struct usbh_ftdi *usbh_ftdi_class_alloc(void) static void usbh_ftdi_class_free(struct usbh_ftdi *ftdi_class) { - int devno = ftdi_class->minor; + uint8_t devno = ftdi_class->minor; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(ftdi_class, 0, sizeof(struct usbh_ftdi)); } @@ -57,7 +57,7 @@ static void usbh_ftdi_caculate_baudrate(uint32_t *itdf_divisor, uint32_t actual_ } int divisor = FTDI_USB_CLK / baudrate; int frac_bits = 0; - for (int i = 0; i < sizeof(frac) / sizeof(frac[0]); i++) { + for (uint8_t i = 0; i < sizeof(frac) / sizeof(frac[0]); i++) { if ((divisor & 0xF) == frac[i]) { frac_bits = i; break; @@ -370,10 +370,12 @@ int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, u __WEAK void usbh_ftdi_run(struct usbh_ftdi *ftdi_class) { + (void)ftdi_class; } __WEAK void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class) { + (void)ftdi_class; } static const uint16_t ftdi_id_table[][2] = { @@ -390,9 +392,9 @@ const struct usbh_class_driver ftdi_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info ftdi_class_info = { .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS, - .class = 0xff, - .subclass = 0x00, - .protocol = 0x00, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, .id_table = ftdi_id_table, .class_driver = &ftdi_class_driver }; \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.c b/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.c index c64a1c3..01ee700 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.c @@ -15,7 +15,7 @@ USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_pl2303_buf[64]; -#define CONFIG_USBHOST_MAX_PL2303_CLASS 4 +#define CONFIG_USBHOST_MAX_PL2303_CLASS 1 #define UT_WRITE_VENDOR_DEVICE (USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE) #define UT_READ_VENDOR_DEVICE (USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE) @@ -25,11 +25,11 @@ static uint32_t g_devinuse = 0; static struct usbh_pl2303 *usbh_pl2303_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_PL2303_CLASS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_pl2303_class[devno], 0, sizeof(struct usbh_pl2303)); g_pl2303_class[devno].minor = devno; return &g_pl2303_class[devno]; @@ -40,10 +40,10 @@ static struct usbh_pl2303 *usbh_pl2303_class_alloc(void) static void usbh_pl2303_class_free(struct usbh_pl2303 *pl2303_class) { - int devno = pl2303_class->minor; + uint8_t devno = pl2303_class->minor; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(pl2303_class, 0, sizeof(struct usbh_pl2303)); } @@ -413,10 +413,12 @@ int usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buf __WEAK void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class) { + (void)pl2303_class; } __WEAK void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class) { + (void)pl2303_class; } static const uint16_t pl2303_id_table[][2] = { @@ -438,9 +440,9 @@ const struct usbh_class_driver pl2303_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info pl2303_class_info = { .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS, - .class = 0xff, - .subclass = 0x00, - .protocol = 0x00, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, .id_table = pl2303_id_table, .class_driver = &pl2303_class_driver }; \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/README.md b/rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/README.md new file mode 100644 index 0000000..18384f6 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/README.md @@ -0,0 +1,6 @@ +# BL616 USB WIFI + +Usbwifi firmware please contact bouffalolab. You can purchase a module in the following ways: + +- https://iot.mi.com/moduleBrowser.html +- https://docs.ai-thinker.com/ai_m61 \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/usbh_bl616.c b/rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/usbh_bl616.c new file mode 100644 index 0000000..9275faa --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/usbh_bl616.c @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbh_core.h" +#include "usbh_bl616.h" + +#undef USB_DBG_TAG +#define USB_DBG_TAG "usbh_bl616" +#include "usb_log.h" + +#define DEV_FORMAT "/dev/wifi/bl616" + +#define MAC_FMT "%02X:%02X:%02X:%02X:%02X:%02X" +#define ARR_ELE_6(e) (e)[0], (e)[1], (e)[2], (e)[3], (e)[4], (e)[5] + +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bl616_tx_buffer[2048 + 512]; +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bl616_rx_buffer[2048 + 512]; + +static struct usbh_bl616 g_bl616_class; + +static const char *auth_to_str(uint8_t auth) +{ + const char *table[RNM_WIFI_AUTH_MAX] = { + [RNM_WIFI_AUTH_UNKNOWN] = "UNKNOWN", + [RNM_WIFI_AUTH_OPEN] = "OPEN", + [RNM_WIFI_AUTH_WEP] = "WEP", + [RNM_WIFI_AUTH_WPA_PSK] = "WPA-PSK", + [RNM_WIFI_AUTH_WPA2_PSK] = "WPA2-PSK", + [RNM_WIFI_AUTH_WPA_WPA2_PSK] = "WPA2-PSK/WPA-PSK", + [RNM_WIFI_AUTH_WPA_ENTERPRISE] = "WPA-ENT", + [RNM_WIFI_AUTH_WPA3_SAE] = "WPA3-SAE", + [RNM_WIFI_AUTH_WPA2_PSK_WPA3_SAE] = "WPA2-PSK/WPA3-SAE", + }; + if (auth < RNM_WIFI_AUTH_MAX) + return table[auth]; + else + return table[RNM_WIFI_AUTH_UNKNOWN]; +} + +static const char *cipher_to_str(uint8_t cipher) +{ + const char *table[RNM_WIFI_CIPHER_MAX] = { + [RNM_WIFI_CIPHER_UNKNOWN] = "UNKNOWN", + [RNM_WIFI_CIPHER_NONE] = "NONE", + [RNM_WIFI_CIPHER_WEP] = "WEP", + [RNM_WIFI_CIPHER_AES] = "AES", + [RNM_WIFI_CIPHER_TKIP] = "TKIP", + [RNM_WIFI_CIPHER_TKIP_AES] = "TKIP/AES", + }; + if (cipher < RNM_WIFI_CIPHER_MAX) + return table[cipher]; + else + return table[RNM_WIFI_CIPHER_UNKNOWN]; +} + +static int parse_get_mac_rsp_msg(struct usbh_bl616 *bl616_class, void *buf, int buf_len) +{ + usb_data_t *usb_hdr = buf; + rnm_mac_addr_ind_msg_t *rsp = buf + sizeof(usb_data_t); + + if (buf_len != sizeof(usb_data_t) + sizeof(rnm_mac_addr_ind_msg_t)) { + return -1; + } + if (usb_hdr->type != USBWIFI_DATA_TYPE_CMD || usb_hdr->length != sizeof(rnm_mac_addr_ind_msg_t)) { + return -1; + } + if (rsp->hdr.cmd != BFLB_CMD_GET_MAC_ADDR || !(rsp->hdr.flags & RNM_MSG_FLAG_ACK)) { + return -1; + } + memcpy(bl616_class->sta_mac, rsp->sta_mac, 6); + memcpy(bl616_class->ap_mac, rsp->ap_mac, 6); + + return 0; +} + +static int usbh_bl616_bulk_in_transfer(struct usbh_bl616 *bl616_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout) +{ + int ret; + struct usbh_urb *urb = &bl616_class->bulkin_urb; + + usbh_bulk_urb_fill(urb, bl616_class->hport, bl616_class->bulkin, buffer, buflen, timeout, NULL, NULL); + ret = usbh_submit_urb(urb); + if (ret == 0) { + ret = urb->actual_length; + } + return ret; +} + +static int usbh_bl616_bulk_out_transfer(struct usbh_bl616 *bl616_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout) +{ + int ret; + struct usbh_urb *urb = &bl616_class->bulkout_urb; + + usbh_bulk_urb_fill(urb, bl616_class->hport, bl616_class->bulkout, buffer, buflen, timeout, NULL, NULL); + ret = usbh_submit_urb(urb); + if (ret == 0) { + ret = urb->actual_length; + } + return ret; +} + +static int usbh_bl616_get_wifi_mac(struct usbh_bl616 *bl616_class) +{ + int ret; + uint32_t msg_len; + usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer; + rnm_base_msg_t *rnm_msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t)); + + memset(usb_hdr, 0, sizeof(usb_data_t)); + memset(rnm_msg, 0, sizeof(rnm_base_msg_t)); + + usb_hdr->type = USBWIFI_DATA_TYPE_CMD; + usb_hdr->length = sizeof(rnm_base_msg_t); + usb_hdr->payload_offset = sizeof(usb_data_t); + + rnm_msg->cmd = BFLB_CMD_GET_MAC_ADDR; + + msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t); + + ret = usbh_bl616_bulk_out_transfer(bl616_class, g_bl616_tx_buffer, msg_len, 500); + if (ret < 0) { + return ret; + } + ret = usbh_bl616_bulk_in_transfer(bl616_class, g_bl616_rx_buffer, sizeof(g_bl616_rx_buffer), 500); + if (ret < 0) { + return ret; + } + + ret = parse_get_mac_rsp_msg(bl616_class, g_bl616_rx_buffer, ret); + return ret; +} + +static int usbh_bl616_wifi_open(struct usbh_bl616 *bl616_class) +{ + uint32_t msg_len; + usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer; + rnm_base_msg_t *msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t)); + + memset(usb_hdr, 0, sizeof(usb_data_t)); + memset(msg, 0, sizeof(rnm_base_msg_t)); + + usb_hdr->type = USBWIFI_DATA_TYPE_CMD; + usb_hdr->length = sizeof(rnm_base_msg_t); + usb_hdr->payload_offset = sizeof(usb_data_t); + + msg->cmd = BFLB_CMD_HELLO; + + msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t); + + return usbh_bl616_bulk_out_transfer(bl616_class, g_bl616_tx_buffer, msg_len, 500); +} + +static int usbh_bl616_wifi_close(struct usbh_bl616 *bl616_class) +{ + uint32_t msg_len; + usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer; + rnm_base_msg_t *msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t)); + + memset(usb_hdr, 0, sizeof(usb_data_t)); + memset(msg, 0, sizeof(rnm_base_msg_t)); + + usb_hdr->type = USBWIFI_DATA_TYPE_CMD; + usb_hdr->length = sizeof(rnm_base_msg_t); + usb_hdr->payload_offset = sizeof(usb_data_t); + + msg->cmd = BFLB_CMD_UNLOAD_DRV; + + msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t); + + return usbh_bl616_bulk_out_transfer(bl616_class, g_bl616_tx_buffer, msg_len, 500); +} + +int usbh_bl616_wifi_sta_connect(const char *ssid, + const int ssid_len, + const char *password, + const int pwd_len) +{ + uint32_t msg_len; + usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer; + rnm_sta_connect_msg_t *msg = (rnm_sta_connect_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t)); + + memset(usb_hdr, 0, sizeof(usb_data_t)); + memset(msg, 0, sizeof(rnm_sta_connect_msg_t)); + + usb_hdr->type = USBWIFI_DATA_TYPE_CMD; + usb_hdr->length = sizeof(rnm_sta_connect_msg_t); + usb_hdr->payload_offset = sizeof(usb_data_t); + + msg->hdr.cmd = BFLB_CMD_STA_CONNECT; + msg->hdr.msg_id = 0x0001; + msg->hdr.session_id = 0x0002; + msg->ssid_len = ssid_len; + memcpy(msg->ssid, ssid, ssid_len); + if (password) { + memcpy(msg->password, password, pwd_len); + } + + msg_len = sizeof(usb_data_t) + sizeof(rnm_sta_connect_msg_t); + + return usbh_bl616_bulk_out_transfer(&g_bl616_class, g_bl616_tx_buffer, msg_len, 500); +} + +int usbh_bl616_wifi_sta_disconnect(void) +{ + uint32_t msg_len; + usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer; + rnm_base_msg_t *msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t)); + + memset(usb_hdr, 0, sizeof(usb_data_t)); + memset(msg, 0, sizeof(rnm_base_msg_t)); + + usb_hdr->type = USBWIFI_DATA_TYPE_CMD; + usb_hdr->length = sizeof(rnm_base_msg_t); + usb_hdr->payload_offset = sizeof(usb_data_t); + + msg->cmd = BFLB_CMD_STA_DISCONNECT; + + msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t); + + return usbh_bl616_bulk_out_transfer(&g_bl616_class, g_bl616_tx_buffer, msg_len, 500); +} + +int usbh_bl616_get_wifi_scan_result(void) +{ + uint32_t msg_len; + usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer; + rnm_base_msg_t *msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t)); + + memset(usb_hdr, 0, sizeof(usb_data_t)); + memset(msg, 0, sizeof(rnm_base_msg_t)); + + usb_hdr->type = USBWIFI_DATA_TYPE_CMD; + usb_hdr->length = sizeof(rnm_base_msg_t); + usb_hdr->payload_offset = sizeof(usb_data_t); + + msg->cmd = BFLB_CMD_SCAN_RESULTS; + + msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t); + + return usbh_bl616_bulk_out_transfer(&g_bl616_class, g_bl616_tx_buffer, msg_len, 500); +} + +int usbh_bl616_wifi_scan(void) +{ + int ret; + uint32_t msg_len; + usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer; + rnm_base_msg_t *msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t)); + + memset(usb_hdr, 0, sizeof(usb_data_t)); + memset(msg, 0, sizeof(rnm_base_msg_t)); + + usb_hdr->type = USBWIFI_DATA_TYPE_CMD; + usb_hdr->length = sizeof(rnm_base_msg_t); + usb_hdr->payload_offset = sizeof(usb_data_t); + + msg->cmd = BFLB_CMD_SCAN; + + msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t); + + ret = usbh_bl616_bulk_out_transfer(&g_bl616_class, g_bl616_tx_buffer, msg_len, 500); + if (ret < 0) { + return ret; + } + + usb_osal_msleep(500); + return usbh_bl616_get_wifi_scan_result(); +} + +static int usbh_bl616_connect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usb_endpoint_descriptor *ep_desc; + int ret = 0; + + struct usbh_bl616 *bl616_class = &g_bl616_class; + + memset(bl616_class, 0, sizeof(struct usbh_bl616)); + + bl616_class->hport = hport; + bl616_class->intf = intf; + + hport->config.intf[intf].priv = bl616_class; + + for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) { + ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc; + + if (ep_desc->bEndpointAddress & 0x80) { + USBH_EP_INIT(bl616_class->bulkin, ep_desc); + } else { + USBH_EP_INIT(bl616_class->bulkout, ep_desc); + } + } + + usbh_bl616_get_wifi_mac(bl616_class); + usbh_bl616_wifi_close(bl616_class); + usbh_bl616_wifi_open(bl616_class); + + USB_LOG_INFO("BL616 WIFI STA MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n", + bl616_class->sta_mac[0], + bl616_class->sta_mac[1], + bl616_class->sta_mac[2], + bl616_class->sta_mac[3], + bl616_class->sta_mac[4], + bl616_class->sta_mac[5]); + + USB_LOG_INFO("BL616 WIFI AP MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n", + bl616_class->ap_mac[0], + bl616_class->ap_mac[1], + bl616_class->ap_mac[2], + bl616_class->ap_mac[3], + bl616_class->ap_mac[4], + bl616_class->ap_mac[5]); + + strncpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN); + + USB_LOG_INFO("Register BL616 WIFI Class:%s\r\n", hport->config.intf[intf].devname); + + usbh_bl616_run(bl616_class); + return ret; +} + +static int usbh_bl616_disconnect(struct usbh_hubport *hport, uint8_t intf) +{ + int ret = 0; + + struct usbh_bl616 *bl616_class = (struct usbh_bl616 *)hport->config.intf[intf].priv; + + if (bl616_class) { + if (bl616_class->bulkin) { + usbh_kill_urb(&bl616_class->bulkin_urb); + } + + if (bl616_class->bulkout) { + usbh_kill_urb(&bl616_class->bulkout_urb); + } + + if (hport->config.intf[intf].devname[0] != '\0') { + USB_LOG_INFO("Unregister BL616 WIFI Class:%s\r\n", hport->config.intf[intf].devname); + usbh_bl616_stop(bl616_class); + } + + memset(bl616_class, 0, sizeof(struct usbh_bl616)); + } + + return ret; +} + +void usbh_bl616_rx_thread(void *argument) +{ + int ret; + usb_data_t *usb_hdr; + rnm_base_msg_t *msg; + rnm_sta_ip_update_ind_msg_t *ipmsg; + rnm_scan_ind_msg_t *scanmsg; + uint8_t *data; + + (void)argument; + USB_LOG_INFO("Create bl616 wifi rx thread\r\n"); + + while (1) { + ret = usbh_bl616_bulk_in_transfer(&g_bl616_class, g_bl616_rx_buffer, sizeof(g_bl616_rx_buffer), USB_OSAL_WAITING_FOREVER); + if (ret < 0) { + break; + } + + usb_hdr = (usb_data_t *)g_bl616_rx_buffer; + + if (usb_hdr->type == USBWIFI_DATA_TYPE_CMD) { + msg = (rnm_base_msg_t *)(g_bl616_rx_buffer + usb_hdr->payload_offset); + + switch (msg->cmd) { + case BFLB_CMD_STA_CONNECTED_IND: + USB_LOG_INFO("AP connected\n"); + g_bl616_class.connect_status = true; + usbh_bl616_sta_connect_callback(); + + break; + case BFLB_CMD_STA_DISCONNECTED_IND: + if (g_bl616_class.connect_status == true) { + g_bl616_class.connect_status = false; + USB_LOG_INFO("AP disconnected\n"); + usbh_bl616_sta_disconnect_callback(); + } + break; + case BFLB_CMD_STA_IP_UPDATE_IND: + ipmsg = (rnm_sta_ip_update_ind_msg_t *)(g_bl616_rx_buffer + usb_hdr->payload_offset); + + USB_LOG_INFO("WIFI IP update\r\n"); + USB_LOG_INFO("WIFI IPv4 Address : %d:%d:%d:%d\r\n", + ipmsg->ip4_addr[0], + ipmsg->ip4_addr[1], + ipmsg->ip4_addr[2], + ipmsg->ip4_addr[3]); + USB_LOG_INFO("WIFI IPv4 Mask : %d:%d:%d:%d\r\n", + ipmsg->ip4_mask[0], + ipmsg->ip4_mask[1], + ipmsg->ip4_mask[2], + ipmsg->ip4_mask[3]); + USB_LOG_INFO("WIFI IPv4 Gateway : %d:%d:%d:%d\r\n\r\n", + ipmsg->ip4_gw[0], + ipmsg->ip4_gw[1], + ipmsg->ip4_gw[2], + ipmsg->ip4_gw[3]); + + g_bl616_class.mode = BL_MODE_STA; + usbh_bl616_sta_update_ip(ipmsg->ip4_addr, ipmsg->ip4_mask, ipmsg->ip4_gw); + break; + case BFLB_CMD_SCAN_RESULTS: + scanmsg = (rnm_scan_ind_msg_t *)(g_bl616_rx_buffer + usb_hdr->payload_offset); + USB_LOG_INFO("WIFI scan result:\r\n"); + for (uint32_t i = 0; i < scanmsg->num; ++i) { + struct bf1b_wifi_scan_record *r = &scanmsg->records[i]; + USB_LOG_INFO("BSSID " MAC_FMT ", channel %u, rssi %d, auth %s, cipher %s, SSID %s\r\n", + ARR_ELE_6(r->bssid), r->channel, r->rssi, + auth_to_str(r->auth_mode), cipher_to_str(r->cipher), r->ssid); + } + break; + default: + break; + } + } else if (usb_hdr->type == USBWIFI_DATA_TYPE_PKT) { + data = (uint8_t *)(g_bl616_rx_buffer + usb_hdr->payload_offset); + usbh_bl616_eth_input(data, usb_hdr->length); + } else { + } + } + + USB_LOG_INFO("Delete bl616 wifi rx thread\r\n"); + usb_osal_thread_delete(NULL); +} + +uint8_t *usbh_bl616_get_eth_txbuf(void) +{ + return (g_bl616_tx_buffer + sizeof(usb_data_t)); +} + +int usbh_bl616_eth_output(uint32_t buflen) +{ + usb_data_t *usb_hdr; + uint32_t txlen; + + if (g_bl616_class.connect_status == false) { + return -USB_ERR_NOTCONN; + } + + usb_hdr = (usb_data_t *)g_bl616_tx_buffer; + memset(usb_hdr, 0, sizeof(usb_data_t)); + + usb_hdr->type = USBWIFI_DATA_TYPE_PKT; + usb_hdr->length = buflen; + usb_hdr->payload_offset = sizeof(usb_data_t); + + txlen = buflen + sizeof(usb_data_t); + if (!(txlen % USB_GET_MAXPACKETSIZE(g_bl616_class.bulkout->wMaxPacketSize))) { + txlen += 1; + } + USB_LOG_DBG("txlen:%d\r\n", txlen); + + usbh_bulk_urb_fill(&g_bl616_class.bulkout_urb, g_bl616_class.hport, g_bl616_class.bulkout, g_bl616_tx_buffer, txlen, USB_OSAL_WAITING_FOREVER, NULL, NULL); + return usbh_submit_urb(&g_bl616_class.bulkout_urb); +} + +int wifi_sta_connect(int argc, char **argv) +{ + if (argc < 3) { + USB_LOG_ERR("Usage: %s \r\n", argv[0]); + return -1; + } + usbh_bl616_wifi_sta_connect(argv[1], strlen(argv[1]), argv[2], strlen(argv[2])); + return 0; +} + +int wifi_scan(int argc, char **argv) +{ + (void)argc; + (void)argv; + + usbh_bl616_wifi_scan(); + return 0; +} + +__WEAK void usbh_bl616_run(struct usbh_bl616 *bl616_class) +{ + (void)bl616_class; +} + +__WEAK void usbh_bl616_stop(struct usbh_bl616 *bl616_class) +{ + (void)bl616_class; +} + +static const uint16_t bl616_id_table[][2] = { + { 0x349b, 0x616f }, + { 0, 0 }, +}; + +static const struct usbh_class_driver bl616_class_driver = { + .driver_name = "bl616_wifi", + .connect = usbh_bl616_connect, + .disconnect = usbh_bl616_disconnect +}; + +CLASS_INFO_DEFINE const struct usbh_class_info bl616_class_info = { + .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, + .id_table = bl616_id_table, + .class_driver = &bl616_class_driver +}; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/usbh_bl616.h b/rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/usbh_bl616.h new file mode 100644 index 0000000..6ec5a7a --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/wifi/usbh_bl616.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USBH_BL616_H +#define USBH_BL616_H + +#define USBWIFI_DATA_TYPE_CMD 0xA55A +#define USBWIFI_DATA_TYPE_PKT 0x6996 + +#define USB_DATA_FLAG_AP_PKT (1u << 0) + +typedef enum { + BFLB_CMD_REBOOT = 0, + BFLB_CMD_RESET, + BFLB_CMD_HELLO, + BFLB_CMD_PING, + + BFLB_CMD_GET_MAC_ADDR, + + // Scan + BFLB_CMD_SCAN, + BFLB_CMD_SCAN_RESULTS, + + // STA + BFLB_CMD_STA_CONNECT, + BFLB_CMD_STA_DISCONNECT, + BFLB_CMD_STA_CONNECTED_IND, + BFLB_CMD_STA_DISCONNECTED_IND, + BFLB_CMD_STA_IP_UPDATE_IND, + BFLB_CMD_STA_SET_AUTO_RECONNECT, + BFLB_CMD_STA_GET_LINK_STATUS, + + // AP + BFLB_CMD_AP_START, + BFLB_CMD_AP_STOP, + BFLB_CMD_AP_STARTED_IND, + BFLB_CMD_AP_STOPPED_IND, + BFLB_CMD_AP_GET_STA_LIST, + + // Monitor + BFLB_CMD_MONITOR_START, + BFLB_CMD_MONITOR_STOP, + BFLB_CMD_MONITOR_SET_CHANNEL, + BFLB_CMD_MONITOR_GET_CHANNEL, + + BFLB_CMD_SET_LPM_MODE, + + // OTA + BFLB_CMD_GET_DEV_VERSION, + BFLB_CMD_OTA, + + BFLB_CMD_EXT, + + BFLB_CMD_USER_EXT, + BFLB_CMD_UNLOAD_DRV, + + BFLB_CMD_MAX, +} bflb_cmd_t; + +typedef enum { + STATUS_OK, + STATUS_NOMEM = 128, + STATUS_INVALID_INPUT, + STATUS_INVALID_MODE, + STATUS_ERR_UNSPECIFIED, + STATUS_NOT_IMPLEMENTED, +} cmd_status_t; + +typedef enum { + RNM_WIFI_AUTH_UNKNOWN = 0, + RNM_WIFI_AUTH_OPEN, + RNM_WIFI_AUTH_WEP, + RNM_WIFI_AUTH_WPA_PSK, + RNM_WIFI_AUTH_WPA2_PSK, + RNM_WIFI_AUTH_WPA_WPA2_PSK, + RNM_WIFI_AUTH_WPA_ENTERPRISE, + RNM_WIFI_AUTH_WPA3_SAE, + RNM_WIFI_AUTH_WPA2_PSK_WPA3_SAE, + RNM_WIFI_AUTH_MAX, +} rnm_wifi_auth_mode_t; + +typedef enum { + RNM_WIFI_CIPHER_UNKNOWN = 0, + RNM_WIFI_CIPHER_NONE, + RNM_WIFI_CIPHER_WEP, + RNM_WIFI_CIPHER_AES, + RNM_WIFI_CIPHER_TKIP, + RNM_WIFI_CIPHER_TKIP_AES, + RNM_WIFI_CIPHER_MAX, +} rnm_wifi_cipher_t; + +/* common header */ +typedef struct { + uint16_t cmd; + // flag ACK is used by server to indicate a response to client +#define RNM_MSG_FLAG_ACK (1 << 0) + // flag TRANSPARENT is never transfered to peer but used locally +#define RNM_MSG_FLAG_TRANSPARENT (1 << 1) + // flag ASYNC is used by server to notify client events such as STA_CONNECTED +#define RNM_MSG_FLAG_ASYNC (1 << 2) + uint16_t flags; + uint16_t status; + uint16_t msg_id; + uint16_t session_id; + uint16_t msg_id_replying; +} rnm_base_msg_t; + +typedef struct { + rnm_base_msg_t hdr; +} rnm_ack_msg_t; + +typedef struct { + rnm_base_msg_t hdr; + uint8_t sta_mac[6]; + uint8_t ap_mac[6]; +} rnm_mac_addr_ind_msg_t; + +typedef struct { + rnm_base_msg_t hdr; + uint16_t ssid_len; + uint8_t ssid[32]; + uint8_t password[64]; +} rnm_sta_connect_msg_t; + +typedef struct { + rnm_base_msg_t hdr; + uint8_t ip4_addr[4]; + uint8_t ip4_mask[4]; + uint8_t ip4_gw[4]; + uint8_t ip4_dns1[4]; + uint8_t ip4_dns2[4]; + uint8_t gw_mac[6]; +} rnm_sta_ip_update_ind_msg_t; + +struct bf1b_wifi_scan_record { + uint8_t bssid[6]; + // TODO use compressed SSID encoding to save room + uint8_t ssid[32 + 1]; + uint16_t channel; + int8_t rssi; + uint8_t auth_mode; + uint8_t cipher; +} __PACKED; + +typedef struct { + rnm_base_msg_t hdr; + uint16_t num; + struct bf1b_wifi_scan_record records[]; +} rnm_scan_ind_msg_t; + +typedef enum { + BL_MODE_NONE, + BL_MODE_STA, // card is STA + BL_MODE_AP, // card is AP + BL_MODE_STA_AP, // card is STA&AP + BL_MODE_SNIFFER, // card is sniffer + BL_MODE_MAX, +} bl_wifi_mode_t; + +typedef struct { + uint16_t type; + uint16_t length; + uint16_t flags; + uint16_t payload_offset; + uint32_t rsvd[8]; + uint8_t payload[]; +} __attribute__((aligned(4))) usb_data_t; + +struct usbh_bl616 { + struct usbh_hubport *hport; + struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */ + struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */ + + struct usbh_urb bulkout_urb; + struct usbh_urb bulkin_urb; + + uint8_t intf; + + uint8_t sta_mac[6]; + uint8_t ap_mac[6]; + uint8_t mode; + bool connect_status; + + void *user_data; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int usbh_bl616_wifi_sta_connect(const char *ssid, + const int ssid_len, + const char *password, + const int pwd_len); + +int usbh_bl616_wifi_sta_disconnect(void); +int usbh_bl616_wifi_scan(void); + +void usbh_bl616_sta_connect_callback(void); +void usbh_bl616_sta_disconnect_callback(void); +void usbh_bl616_sta_update_ip(uint8_t ip4_addr[4], uint8_t ip4_mask[4], uint8_t ip4_gw[4]); + +uint8_t *usbh_bl616_get_eth_txbuf(void); +int usbh_bl616_eth_output(uint32_t buflen); +void usbh_bl616_eth_input(uint8_t *buf, uint32_t buflen); +void usbh_bl616_rx_thread(void *argument); + +void usbh_bl616_run(struct usbh_bl616 *bl616_class); +void usbh_bl616_stop(struct usbh_bl616 *bl616_class); + +int wifi_sta_connect(int argc, char **argv); +int wifi_scan(int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif /* USBH_BL616_H */ diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/xbox/usbh_xbox.c b/rt-thread/components/drivers/usb/cherryusb/class/vendor/xbox/usbh_xbox.c new file mode 100644 index 0000000..0c3339c --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/xbox/usbh_xbox.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2024 Till Harbaum + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbh_core.h" +#include "usbh_xbox.h" + +#define DEV_FORMAT "/dev/xbox%d" + +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_xbox_buf[128]; + +static struct usbh_xbox g_xbox_class[CONFIG_USBHOST_MAX_XBOX_CLASS]; +static uint32_t g_devinuse = 0; + +static struct usbh_xbox *usbh_xbox_class_alloc(void) +{ + uint8_t devno; + + for (devno = 0; devno < CONFIG_USBHOST_MAX_XBOX_CLASS; devno++) { + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); + memset(&g_xbox_class[devno], 0, sizeof(struct usbh_xbox)); + g_xbox_class[devno].minor = devno; + return &g_xbox_class[devno]; + } + } + return NULL; +} + +static void usbh_xbox_class_free(struct usbh_xbox *xbox_class) +{ + uint8_t devno = xbox_class->minor; + + if (devno < 32) { + g_devinuse &= ~(1U << devno); + } + memset(xbox_class, 0, sizeof(struct usbh_xbox)); +} + +int usbh_xbox_connect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usb_endpoint_descriptor *ep_desc; + + struct usbh_xbox *xbox_class = usbh_xbox_class_alloc(); + if (xbox_class == NULL) { + USB_LOG_ERR("Fail to alloc xbox_class\r\n"); + return -USB_ERR_NOMEM; + } + + xbox_class->hport = hport; + xbox_class->intf = intf; + + hport->config.intf[intf].priv = xbox_class; + + for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) { + ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc; + if (ep_desc->bEndpointAddress & 0x80) { + USBH_EP_INIT(xbox_class->intin, ep_desc); + } else { + USBH_EP_INIT(xbox_class->intout, ep_desc); + } + } + + snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, xbox_class->minor); + + USB_LOG_INFO("Register XBOX Class:%s\r\n", hport->config.intf[intf].devname); + + usbh_xbox_run(xbox_class); + return 0; +} + +int usbh_xbox_disconnect(struct usbh_hubport *hport, uint8_t intf) +{ + int ret = 0; + + struct usbh_xbox *xbox_class = (struct usbh_xbox *)hport->config.intf[intf].priv; + + if (xbox_class) { + if (xbox_class->intin) { + usbh_kill_urb(&xbox_class->intin_urb); + } + + if (xbox_class->intout) { + usbh_kill_urb(&xbox_class->intout_urb); + } + + if (hport->config.intf[intf].devname[0] != '\0') { + USB_LOG_INFO("Unregister XBOX Class:%s\r\n", hport->config.intf[intf].devname); + usbh_xbox_stop(xbox_class); + } + + usbh_xbox_class_free(xbox_class); + } + + return ret; +} + +__WEAK void usbh_xbox_run(struct usbh_xbox *xbox_class) +{ +} + +__WEAK void usbh_xbox_stop(struct usbh_xbox *xbox_class) +{ +} + +const struct usbh_class_driver xbox_class_driver = { + .driver_name = "xbox", + .connect = usbh_xbox_connect, + .disconnect = usbh_xbox_disconnect +}; + +static const uint16_t xbox_id_table[][2] = { + { 0x0079, 0x18d4 }, // GPD Win 2 X-Box Controller + { 0x03eb, 0xff01 }, // Wooting One (Legacy) + { 0x03eb, 0xff02 }, // Wooting Two (Legacy) + { 0x044f, 0xb326 }, // Thrustmaster Gamepad GP XID + { 0x045e, 0x028e }, // Microsoft X-Box 360 pad + { 0x045e, 0x028f }, // Microsoft X-Box 360 pad v2 + { 0x046d, 0xc21d }, // Logitech Gamepad F310 + { 0x046d, 0xc21e }, // Logitech Gamepad F510 + { 0x046d, 0xc21f }, // Logitech Gamepad F710 + { 0x046d, 0xc242 }, // Logitech Chillstream Controller + { 0x046d, 0xcaa3 }, // Logitech DriveFx Racing Wheel + { 0x056e, 0x2004 }, // Elecom JC-U3613M + { 0x06a3, 0xf51a }, // Saitek P3600 + { 0x0738, 0x4716 }, // Mad Catz Wired Xbox 360 Controller + { 0x0738, 0x4718 }, // Mad Catz Street Fighter IV FightStick SE + { 0x0738, 0x4726 }, // Mad Catz Xbox 360 Controller + { 0x0738, 0x4736 }, // Mad Catz MicroCon Gamepad + { 0x0738, 0x4740 }, // Mad Catz Beat Pad + { 0x0738, 0x9871 }, // Mad Catz Portable Drum + { 0x0738, 0xb726 }, // Mad Catz Xbox controller - MW2 + { 0x0738, 0xbeef }, // Mad Catz JOYTECH NEO SE Advanced GamePad + { 0x0738, 0xcb02 }, // Saitek Cyborg Rumble Pad - PC/Xbox 360 + { 0x0738, 0xcb03 }, // Saitek P3200 Rumble Pad - PC/Xbox 360 + { 0x0738, 0xcb29 }, // Saitek Aviator Stick AV8R02 + { 0x0738, 0xf738 }, // Super SFIV FightStick TE S + { 0x07ff, 0xffff }, // Mad Catz GamePad + { 0x0e6f, 0x0113 }, // Afterglow AX.1 Gamepad for Xbox 360 + { 0x0e6f, 0x011f }, // Rock Candy Gamepad Wired Controller + { 0x0e6f, 0x0131 }, // PDP EA Sports Controller + { 0x0e6f, 0x0133 }, // Xbox 360 Wired Controller + { 0x0e6f, 0x0201 }, // Pelican PL-3601 'TSZ' Wired Xbox 360 Controller + { 0x0e6f, 0x0213 }, // Afterglow Gamepad for Xbox 360 + { 0x0e6f, 0x021f }, // Rock Candy Gamepad for Xbox 360 + { 0x0e6f, 0x0301 }, // Logic3 Controller + { 0x0e6f, 0x0401 }, // Logic3 Controller + { 0x0e6f, 0x0413 }, // Afterglow AX.1 Gamepad for Xbox 360 + { 0x0e6f, 0x0501 }, // PDP Xbox 360 Controller + { 0x0e6f, 0xf900 }, // PDP Afterglow AX.1 + { 0x0f0d, 0x000a }, // Hori Co. DOA4 FightStick + { 0x0f0d, 0x000c }, // Hori PadEX Turbo + { 0x1038, 0x1430 }, // SteelSeries Stratus Duo + { 0x1038, 0x1431 }, // SteelSeries Stratus Duo + { 0x11c9, 0x55f0 }, // Nacon GC-100XF + { 0x1209, 0x2882 }, // Ardwiino Controller + { 0x12ab, 0x0301 }, // PDP AFTERGLOW AX.1 + { 0x1430, 0x4748 }, // RedOctane Guitar Hero X-plorer + { 0x1430, 0xf801 }, // RedOctane Controller + { 0x146b, 0x0601 }, // BigBen Interactive XBOX 360 Controller + { 0x1532, 0x0037 }, // Razer Sabertooth + { 0x15e4, 0x3f00 }, // Power A Mini Pro Elite + { 0x15e4, 0x3f0a }, // Xbox Airflo wired controller + { 0x15e4, 0x3f10 }, // Batarang Xbox 360 controller + { 0x162e, 0xbeef }, // Joytech Neo-Se Take2 + { 0x1689, 0xfd00 }, // Razer Onza Tournament Edition + { 0x1689, 0xfd01 }, // Razer Onza Classic Edition + { 0x1689, 0xfe00 }, // Razer Sabertooth + { 0x1949, 0x041a }, // Amazon Game Controller + { 0x1bad, 0x0002 }, // Harmonix Rock Band Guitar + { 0x1bad, 0xf016 }, // Mad Catz Xbox 360 Controller + { 0x1bad, 0xf021 }, // Mad Cats Ghost Recon FS GamePad + { 0x1bad, 0xf023 }, // MLG Pro Circuit Controller (Xbox) + { 0x1bad, 0xf025 }, // Mad Catz Call Of Duty + { 0x1bad, 0xf027 }, // Mad Catz FPS Pro + { 0x1bad, 0xf028 }, // Street Fighter IV FightPad + { 0x1bad, 0xf030 }, // Mad Catz Xbox 360 MC2 MicroCon Racing Wheel + { 0x1bad, 0xf036 }, // Mad Catz MicroCon GamePad Pro + { 0x1bad, 0xf038 }, // Street Fighter IV FightStick TE + { 0x1bad, 0xf501 }, // HoriPad EX2 Turbo + { 0x1bad, 0xf506 }, // Hori Real Arcade Pro.EX Premium VLX + { 0x1bad, 0xf900 }, // Harmonix Xbox 360 Controller + { 0x1bad, 0xf901 }, // Gamestop Xbox 360 Controller + { 0x1bad, 0xf903 }, // Tron Xbox 360 controller + { 0x1bad, 0xf904 }, // PDP Versus Fighting Pad + { 0x1bad, 0xfa01 }, // MadCatz GamePad + { 0x1bad, 0xfd00 }, // Razer Onza TE + { 0x1bad, 0xfd01 }, // Razer Onza + { 0x20d6, 0x2001 }, // BDA Xbox Series X Wired Controller + { 0x20d6, 0x281f }, // PowerA Wired Controller For Xbox 360 + { 0x24c6, 0x5300 }, // PowerA MINI PROEX Controller + { 0x24c6, 0x5303 }, // Xbox Airflo wired controller + { 0x24c6, 0x530a }, // Xbox 360 Pro EX Controller + { 0x24c6, 0x531a }, // PowerA Pro Ex + { 0x24c6, 0x5397 }, // FUS1ON Tournament Controller + { 0x24c6, 0x5500 }, // Hori XBOX 360 EX 2 with Turbo + { 0x24c6, 0x5501 }, // Hori Real Arcade Pro VX-SA + { 0x24c6, 0x5506 }, // Hori SOULCALIBUR V Stick + { 0x24c6, 0x550d }, // Hori GEM Xbox controller + { 0x24c6, 0x5b00 }, // ThrustMaster Ferrari 458 Racing Wheel + { 0x24c6, 0x5b02 }, // Thrustmaster, Inc. GPX Controller + { 0x24c6, 0x5b03 }, // Thrustmaster Ferrari 458 Racing Wheel + { 0x24c6, 0x5d04 }, // Razer Sabertooth + { 0x24c6, 0xfafe }, // Rock Candy Gamepad for Xbox 360 + { 0x2563, 0x058d }, // OneXPlayer Gamepad + { 0x2dc8, 0x3106 }, // 8BitDo Ultimate Wireless / Pro 2 Wired Controller + { 0x2dc8, 0x3109 }, // 8BitDo Ultimate Wireless Bluetooth + { 0x31e3, 0x1100 }, // Wooting One + { 0x31e3, 0x1200 }, // Wooting Two + { 0x31e3, 0x1210 }, // Wooting Lekker + { 0x31e3, 0x1220 }, // Wooting Two HE + { 0x31e3, 0x1230 }, // Wooting Two HE (ARM) + { 0x31e3, 0x1300 }, // Wooting 60HE (AVR) + { 0x31e3, 0x1310 }, // Wooting 60HE (ARM) + { 0x3285, 0x0607 }, // Nacon GC-100 + { 0x413d, 0x2104 }, // Black Shark Green Ghost Gamepad + { 0x0000, 0x0000 } // end of list +}; + +CLASS_INFO_DEFINE const struct usbh_class_info xbox_custom_class_info = { + .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, + .bInterfaceClass = USB_DEVICE_CLASS_VEND_SPECIFIC, + .bInterfaceSubClass = 0x5d, + .bInterfaceProtocol = 0x01, + .id_table = xbox_id_table, + .class_driver = &xbox_class_driver +}; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/vendor/xbox/usbh_xbox.h b/rt-thread/components/drivers/usb/cherryusb/class/vendor/xbox/usbh_xbox.h new file mode 100644 index 0000000..85d4672 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/class/vendor/xbox/usbh_xbox.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, Till Harbaum + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USBH_XBOX_H +#define USBH_XBOX_H + +struct usbh_xbox { + struct usbh_hubport *hport; + struct usb_endpoint_descriptor *intin; /* INTR IN endpoint */ + struct usb_endpoint_descriptor *intout; /* INTR OUT endpoint */ + struct usbh_urb intin_urb; /* INTR IN urb */ + struct usbh_urb intout_urb; /* INTR OUT urb */ + + uint8_t intf; /* interface number */ + uint8_t minor; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +void usbh_xbox_run(struct usbh_xbox *xbox_class); +void usbh_xbox_stop(struct usbh_xbox *xbox_class); + +#ifdef __cplusplus +} +#endif + +#endif /* USBH_XBOX_H */ diff --git a/rt-thread/components/drivers/usb/cherryusb/class/video/usbd_video.c b/rt-thread/components/drivers/usb/cherryusb/class/video/usbd_video.c index b11fa6a..dae28eb 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/video/usbd_video.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/video/usbd_video.c @@ -18,12 +18,28 @@ struct usbd_video_priv { uint8_t power_mode; uint8_t error_code; struct video_entity_info info[3]; + uint8_t *ep_buf0; + uint8_t *ep_buf1; + bool ep_buf0_ready; + bool ep_buf1_ready; + uint32_t ep_buf0_len; + uint32_t ep_buf1_len; + uint8_t ep_buf_idx; + bool stream_finish; + uint32_t max_packets; + uint8_t *stream_buf; + uint32_t stream_len; + uint32_t stream_offset; + uint8_t stream_frameid; + uint32_t stream_headerlen; } g_usbd_video[CONFIG_USBDEV_MAX_BUS]; static int usbd_video_control_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) { uint8_t control_selector = (uint8_t)(setup->wValue >> 8); + (void)busid; + switch (control_selector) { case VIDEO_VC_VIDEO_POWER_MODE_CONTROL: switch (setup->bRequest) { @@ -669,7 +685,7 @@ static int video_class_interface_request_handler(uint8_t busid, struct usb_setup } else { return usbd_video_control_unit_terminal_request_handler(busid, setup, data, len); /* Unit and Terminal Requests */ } - } else if (intf_num == 1) { /* Video Stream Inteface */ + } else if (intf_num == 1) { /* Video Stream Inteface */ return usbd_video_stream_request_handler(busid, setup, data, len); /* Interface Stream Requests */ } return -1; @@ -698,7 +714,7 @@ static void video_notify_handler(uint8_t busid, uint8_t event, void *arg) } } -void usbd_video_probe_and_commit_controls_init(uint8_t busid, uint32_t dwFrameInterval, uint32_t dwMaxVideoFrameSize, uint32_t dwMaxPayloadTransferSize) +static void usbd_video_probe_and_commit_controls_init(uint8_t busid, uint32_t dwFrameInterval, uint32_t dwMaxVideoFrameSize, uint32_t dwMaxPayloadTransferSize) { g_usbd_video[busid].probe.hintUnion.bmHint = 0x01; g_usbd_video[busid].probe.hintUnion1.bmHint = 0; @@ -735,9 +751,47 @@ void usbd_video_probe_and_commit_controls_init(uint8_t busid, uint32_t dwFrameIn g_usbd_video[busid].commit.bPreferedVersion = 0; g_usbd_video[busid].commit.bMinVersion = 0; g_usbd_video[busid].commit.bMaxVersion = 0; + + g_usbd_video[busid].stream_frameid = 0; + g_usbd_video[busid].stream_headerlen = 2; } -struct usbd_interface *usbd_video_init_intf(uint8_t busid, struct usbd_interface *intf, +static uint32_t usbd_video_prepare_ep_buf_data(uint8_t busid, uint32_t remain, uint8_t *ep_buf) +{ + struct video_payload_header *header; + uint32_t len; + uint32_t offset; + + len = MIN(remain, (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - g_usbd_video[busid].stream_headerlen) * g_usbd_video[busid].max_packets); + offset = 0; + while (len > 0) { + header = (struct video_payload_header *)&ep_buf[offset]; + header->bHeaderLength = g_usbd_video[busid].stream_headerlen; + header->headerInfoUnion.bmheaderInfo = 0; + header->headerInfoUnion.headerInfoBits.endOfHeader = 1; + header->headerInfoUnion.headerInfoBits.endOfFrame = 0; + header->headerInfoUnion.headerInfoBits.frameIdentifier = g_usbd_video[busid].stream_frameid; + + uint32_t len2 = MIN(len, g_usbd_video[busid].probe.dwMaxPayloadTransferSize - g_usbd_video[busid].stream_headerlen); + + usb_memcpy(&ep_buf[offset + g_usbd_video[busid].stream_headerlen], + &g_usbd_video[busid].stream_buf[g_usbd_video[busid].stream_offset], + len2); + + g_usbd_video[busid].stream_offset += len2; + len -= len2; + offset += (len2 + g_usbd_video[busid].stream_headerlen); + + if (g_usbd_video[busid].stream_offset == g_usbd_video[busid].stream_len) { + header->headerInfoUnion.headerInfoBits.endOfFrame = 1; + } + } + + return offset; +} + +struct usbd_interface *usbd_video_init_intf(uint8_t busid, + struct usbd_interface *intf, uint32_t dwFrameInterval, uint32_t dwMaxVideoFrameSize, uint32_t dwMaxPayloadTransferSize) @@ -761,28 +815,70 @@ struct usbd_interface *usbd_video_init_intf(uint8_t busid, struct usbd_interface return intf; } -uint32_t usbd_video_payload_fill(uint8_t busid, uint8_t *input, uint32_t input_len, uint8_t *output, uint32_t *out_len) +bool usbd_video_stream_split_transfer(uint8_t busid, uint8_t ep) { - uint32_t packets; - uint32_t last_packet_size; - uint32_t picture_pos = 0; - static uint8_t uvc_header[2] = { 0x02, 0x80 }; + uint32_t remain; - packets = (input_len + (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2) ) / (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2); - last_packet_size = input_len - ((packets - 1) * (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2)); - - for (size_t i = 0; i < packets; i++) { - output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i] = uvc_header[0]; - output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i + 1] = uvc_header[1]; - if (i == (packets - 1)) { - memcpy(&output[2 + g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i], &input[picture_pos], last_packet_size); - output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i + 1] |= (1 << 1); - } else { - memcpy(&output[2 + g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i], &input[picture_pos], g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2); - picture_pos += g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2; + if (g_usbd_video[busid].ep_buf1_ready && (g_usbd_video[busid].ep_buf_idx == 0)) { /* callback: buf1 ready and buf0 was sent */ + g_usbd_video[busid].ep_buf0_ready = false; + g_usbd_video[busid].ep_buf_idx = 1; + usbd_ep_start_write(busid, ep, g_usbd_video[busid].ep_buf1, g_usbd_video[busid].ep_buf1_len); + } else if (g_usbd_video[busid].ep_buf0_ready && (g_usbd_video[busid].ep_buf_idx == 1)) { /* callback: buf0 ready and buf1 was sent */ + g_usbd_video[busid].ep_buf1_ready = false; + g_usbd_video[busid].ep_buf_idx = 0; + usbd_ep_start_write(busid, ep, g_usbd_video[busid].ep_buf0, g_usbd_video[busid].ep_buf0_len); + } else { + if (g_usbd_video[busid].stream_finish) { + return true; } } - uvc_header[1] ^= 1; - *out_len = (input_len + 2 * packets); - return packets; -} \ No newline at end of file + + if (!g_usbd_video[busid].ep_buf0_ready) { + remain = g_usbd_video[busid].stream_len - g_usbd_video[busid].stream_offset; + if (remain == 0) { + g_usbd_video[busid].stream_frameid ^= 1; + g_usbd_video[busid].stream_finish = true; + } else { + g_usbd_video[busid].ep_buf0_len = usbd_video_prepare_ep_buf_data(busid, remain, g_usbd_video[busid].ep_buf0); + g_usbd_video[busid].ep_buf0_ready = true; + if (!g_usbd_video[busid].ep_buf1_ready) { + g_usbd_video[busid].ep_buf_idx = 0; + usbd_ep_start_write(busid, ep, g_usbd_video[busid].ep_buf0, g_usbd_video[busid].ep_buf0_len); + } + } + } + + if (!g_usbd_video[busid].ep_buf1_ready) { + remain = g_usbd_video[busid].stream_len - g_usbd_video[busid].stream_offset; + if (remain == 0) { + g_usbd_video[busid].stream_frameid ^= 1; + g_usbd_video[busid].stream_finish = true; + } else { + g_usbd_video[busid].ep_buf1_len = usbd_video_prepare_ep_buf_data(busid, remain, g_usbd_video[busid].ep_buf1); + g_usbd_video[busid].ep_buf1_ready = true; + } + } + + return false; +} + +int usbd_video_stream_start_write(uint8_t busid, uint8_t ep, uint8_t *ep_buf0, uint8_t *ep_buf1, uint32_t ep_bufsize, uint8_t *stream_buf, uint32_t stream_len) +{ + if ((usb_device_is_configured(busid) == 0) || (stream_len == 0)) { + return -1; + } + + g_usbd_video[busid].ep_buf0 = ep_buf0; + g_usbd_video[busid].ep_buf1 = ep_buf1; + g_usbd_video[busid].ep_buf0_ready = false; + g_usbd_video[busid].ep_buf1_ready = false; + g_usbd_video[busid].ep_buf_idx = 0; + g_usbd_video[busid].stream_finish = false; + g_usbd_video[busid].max_packets = ep_bufsize / g_usbd_video[busid].probe.dwMaxPayloadTransferSize; + g_usbd_video[busid].stream_buf = stream_buf; + g_usbd_video[busid].stream_len = stream_len; + g_usbd_video[busid].stream_offset = 0; + + usbd_video_stream_split_transfer(busid, ep); + return 0; +} diff --git a/rt-thread/components/drivers/usb/cherryusb/class/video/usbd_video.h b/rt-thread/components/drivers/usb/cherryusb/class/video/usbd_video.h index 14636fb..a2a98bb 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/video/usbd_video.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/video/usbd_video.h @@ -20,7 +20,9 @@ struct usbd_interface *usbd_video_init_intf(uint8_t busid, struct usbd_interface void usbd_video_open(uint8_t busid, uint8_t intf); void usbd_video_close(uint8_t busid, uint8_t intf); -uint32_t usbd_video_payload_fill(uint8_t busid, uint8_t *input, uint32_t input_len, uint8_t *output, uint32_t *out_len); + +bool usbd_video_stream_split_transfer(uint8_t busid, uint8_t ep); +int usbd_video_stream_start_write(uint8_t busid, uint8_t ep, uint8_t *ep_buf0, uint8_t *ep_buf1, uint32_t ep_bufsize, uint8_t *stream_buf, uint32_t stream_len); #ifdef __cplusplus } diff --git a/rt-thread/components/drivers/usb/cherryusb/class/video/usbh_video.c b/rt-thread/components/drivers/usb/cherryusb/class/video/usbh_video.c index 0d3f79f..7379151 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/video/usbh_video.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/video/usbh_video.c @@ -34,11 +34,11 @@ static uint32_t g_devinuse = 0; static struct usbh_video *usbh_video_class_alloc(void) { - int devno; + uint8_t devno; for (devno = 0; devno < CONFIG_USBHOST_MAX_VIDEO_CLASS; devno++) { - if ((g_devinuse & (1 << devno)) == 0) { - g_devinuse |= (1 << devno); + if ((g_devinuse & (1U << devno)) == 0) { + g_devinuse |= (1U << devno); memset(&g_video_class[devno], 0, sizeof(struct usbh_video)); g_video_class[devno].minor = devno; return &g_video_class[devno]; @@ -49,10 +49,10 @@ static struct usbh_video *usbh_video_class_alloc(void) static void usbh_video_class_free(struct usbh_video *video_class) { - int devno = video_class->minor; + uint8_t devno = video_class->minor; - if (devno >= 0 && devno < 32) { - g_devinuse &= ~(1 << devno); + if (devno < 32) { + g_devinuse &= ~(1U << devno); } memset(video_class, 0, sizeof(struct usbh_video)); } @@ -495,20 +495,26 @@ static int usbh_video_ctrl_disconnect(struct usbh_hubport *hport, uint8_t intf) static int usbh_video_streaming_connect(struct usbh_hubport *hport, uint8_t intf) { + (void)hport; + (void)intf; return 0; } static int usbh_video_streaming_disconnect(struct usbh_hubport *hport, uint8_t intf) { + (void)hport; + (void)intf; return 0; } __WEAK void usbh_video_run(struct usbh_video *video_class) { + (void)video_class; } __WEAK void usbh_video_stop(struct usbh_video *video_class) { + (void)video_class; } const struct usbh_class_driver video_ctrl_class_driver = { @@ -525,18 +531,18 @@ const struct usbh_class_driver video_streaming_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info video_ctrl_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, - .class = USB_DEVICE_CLASS_VIDEO, - .subclass = VIDEO_SC_VIDEOCONTROL, - .protocol = VIDEO_PC_PROTOCOL_UNDEFINED, + .bInterfaceClass = USB_DEVICE_CLASS_VIDEO, + .bInterfaceSubClass = VIDEO_SC_VIDEOCONTROL, + .bInterfaceProtocol = VIDEO_PC_PROTOCOL_UNDEFINED, .id_table = NULL, .class_driver = &video_ctrl_class_driver }; CLASS_INFO_DEFINE const struct usbh_class_info video_streaming_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, - .class = USB_DEVICE_CLASS_VIDEO, - .subclass = VIDEO_SC_VIDEOSTREAMING, - .protocol = VIDEO_PC_PROTOCOL_UNDEFINED, + .bInterfaceClass = USB_DEVICE_CLASS_VIDEO, + .bInterfaceSubClass = VIDEO_SC_VIDEOSTREAMING, + .bInterfaceProtocol = VIDEO_PC_PROTOCOL_UNDEFINED, .id_table = NULL, .class_driver = &video_streaming_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.c b/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.c index c357b25..4384970 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.c @@ -102,6 +102,8 @@ static void rndis_notify_rsp(void) static int rndis_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) { + (void)busid; + switch (setup->bRequest) { case CDC_REQUEST_SEND_ENCAPSULATED_COMMAND: rndis_encapsulated_cmd_handler(*data, setup->wLength); @@ -152,6 +154,8 @@ static int rndis_init_cmd_handler(uint8_t *data, uint32_t len) rndis_initialize_msg_t *cmd = (rndis_initialize_msg_t *)data; rndis_initialize_cmplt_t *resp; + (void)len; + resp = ((rndis_initialize_cmplt_t *)rndis_encapsulated_resp_buffer); resp->RequestId = cmd->RequestId; resp->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT; @@ -177,6 +181,9 @@ static int rndis_halt_cmd_handler(uint8_t *data, uint32_t len) { rndis_halt_msg_t *resp; + (void)data; + (void)len; + resp = ((rndis_halt_msg_t *)rndis_encapsulated_resp_buffer); resp->MessageLength = 0; @@ -192,6 +199,8 @@ static int rndis_query_cmd_handler(uint8_t *data, uint32_t len) uint8_t *infomation_buffer; uint32_t infomation_len = 0; + (void)len; + resp = ((rndis_query_cmplt_t *)rndis_encapsulated_resp_buffer); resp->MessageType = REMOTE_NDIS_QUERY_CMPLT; resp->RequestId = cmd->RequestId; @@ -338,6 +347,8 @@ static int rndis_set_cmd_handler(uint8_t *data, uint32_t len) rndis_set_cmplt_t *resp; rndis_config_parameter_t *param; + (void)len; + resp = ((rndis_set_cmplt_t *)rndis_encapsulated_resp_buffer); resp->RequestId = cmd->RequestId; resp->MessageType = REMOTE_NDIS_SET_CMPLT; @@ -394,6 +405,9 @@ static int rndis_reset_cmd_handler(uint8_t *data, uint32_t len) // rndis_reset_msg_t *cmd = (rndis_reset_msg_t *)data; rndis_reset_cmplt_t *resp; + (void)data; + (void)len; + resp = ((rndis_reset_cmplt_t *)rndis_encapsulated_resp_buffer); resp->MessageType = REMOTE_NDIS_RESET_CMPLT; resp->MessageLength = sizeof(rndis_reset_cmplt_t); @@ -412,6 +426,8 @@ static int rndis_keepalive_cmd_handler(uint8_t *data, uint32_t len) rndis_keepalive_msg_t *cmd = (rndis_keepalive_msg_t *)data; rndis_keepalive_cmplt_t *resp; + (void)len; + resp = ((rndis_keepalive_cmplt_t *)rndis_encapsulated_resp_buffer); resp->RequestId = cmd->RequestId; resp->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT; @@ -425,6 +441,9 @@ static int rndis_keepalive_cmd_handler(uint8_t *data, uint32_t len) static void rndis_notify_handler(uint8_t busid, uint8_t event, void *arg) { + (void)busid; + (void)arg; + switch (event) { case USBD_EVENT_RESET: g_usbd_rndis.link_status = NDIS_MEDIA_STATE_DISCONNECTED; @@ -445,6 +464,9 @@ void rndis_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) { rndis_data_packet_t *hdr; + (void)busid; + (void)ep; + hdr = (rndis_data_packet_t *)g_rndis_rx_buffer; g_rndis_rx_data_buffer = g_rndis_rx_buffer; if ((hdr->MessageType != REMOTE_NDIS_PACKET_MSG) || (nbytes < hdr->MessageLength)) { @@ -456,21 +478,28 @@ void rndis_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) g_rndis_rx_data_buffer += hdr->DataOffset + sizeof(rndis_generic_msg_t); g_rndis_rx_data_length = hdr->DataLength; - usbd_rndis_data_recv_done(); + usbd_rndis_data_recv_done(g_rndis_rx_data_length); } void rndis_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes) { - if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) { + (void)busid; + + if ((nbytes % usbd_get_ep_mps(0, ep)) == 0 && nbytes) { /* send zlp */ usbd_ep_start_write(0, ep, NULL, 0); } else { + usbd_rndis_data_send_done(g_rndis_tx_data_length); g_rndis_tx_data_length = 0; } } void rndis_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes) { + (void)busid; + (void)ep; + (void)nbytes; + //USB_LOG_DBG("len:%d\r\n", nbytes); } @@ -564,3 +593,13 @@ struct usbd_interface *usbd_rndis_init_intf(struct usbd_interface *intf, return intf; } + +__WEAK void usbd_rndis_data_recv_done(uint32_t len) +{ + (void)len; +} + +__WEAK void usbd_rndis_data_send_done(uint32_t len) +{ + (void)len; +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.h b/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.h index 8a9581f..72b89ea 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.h +++ b/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.h @@ -18,7 +18,8 @@ struct usbd_interface *usbd_rndis_init_intf(struct usbd_interface *intf, const uint8_t in_ep, const uint8_t int_ep, uint8_t mac[6]); -void usbd_rndis_data_recv_done(void); +void usbd_rndis_data_recv_done(uint32_t len); +void usbd_rndis_data_send_done(uint32_t len); #ifdef CONFIG_USBDEV_RNDIS_USING_LWIP struct pbuf *usbd_rndis_eth_rx(void); diff --git a/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.c b/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.c index e73eeb0..991f095 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.c @@ -365,14 +365,18 @@ delete : __WEAK void usbh_bluetooth_hci_read_callback(uint8_t *data, uint32_t len) { + (void)data; + (void)len; } __WEAK void usbh_bluetooth_run(struct usbh_bluetooth *bluetooth_class) { + (void)bluetooth_class; } __WEAK void usbh_bluetooth_stop(struct usbh_bluetooth *bluetooth_class) { + (void)bluetooth_class; } static const struct usbh_class_driver bluetooth_class_driver = { @@ -389,18 +393,18 @@ static const uint16_t bluetooth_id_table[][2] = { CLASS_INFO_DEFINE const struct usbh_class_info bluetooth_h4_nrf_class_info = { .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS, - .class = 0xff, - .subclass = 0x00, - .protocol = 0x00, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, .id_table = bluetooth_id_table, .class_driver = &bluetooth_class_driver }; #else CLASS_INFO_DEFINE const struct usbh_class_info bluetooth_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, - .class = USB_DEVICE_CLASS_WIRELESS, - .subclass = 0x01, - .protocol = 0x01, + .bInterfaceClass = USB_DEVICE_CLASS_WIRELESS, + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x01, .id_table = NULL, .class_driver = &bluetooth_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbh_rndis.c b/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbh_rndis.c index e722e50..e9eaff0 100644 --- a/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbh_rndis.c +++ b/rt-thread/components/drivers/usb/cherryusb/class/wireless/usbh_rndis.c @@ -13,7 +13,7 @@ #define DEV_FORMAT "/dev/rndis" -USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_buf[4096]; +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_buf[512]; #define CONFIG_USBHOST_RNDIS_ETH_MAX_FRAME_SIZE 1514 #define CONFIG_USBHOST_RNDIS_ETH_MSG_SIZE (CONFIG_USBHOST_RNDIS_ETH_MAX_FRAME_SIZE + 44) @@ -26,6 +26,7 @@ static struct usbh_rndis g_rndis_class; static int usbh_rndis_get_notification(struct usbh_rndis *rndis_class) { + (void)rndis_class; // int ret; // struct usbh_urb *urb = &rndis_class->intin_urb; @@ -79,7 +80,7 @@ static int usbh_rndis_init_msg_transfer(struct usbh_rndis *rndis_class) setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE; setup->wValue = 0; setup->wIndex = 0; - setup->wLength = 4096; + setup->wLength = sizeof(g_rndis_buf); ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp); if (ret < 0) { @@ -137,7 +138,7 @@ int usbh_rndis_query_msg_transfer(struct usbh_rndis *rndis_class, uint32_t oid, setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE; setup->wValue = 0; setup->wIndex = 0; - setup->wLength = 4096; + setup->wLength = sizeof(g_rndis_buf); ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp); if (ret < 0) { @@ -194,7 +195,7 @@ static int usbh_rndis_set_msg_transfer(struct usbh_rndis *rndis_class, uint32_t setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE; setup->wValue = 0; setup->wIndex = 0; - setup->wLength = 4096; + setup->wLength = sizeof(g_rndis_buf); ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp); if (ret < 0) { @@ -261,7 +262,7 @@ int usbh_rndis_keepalive(struct usbh_rndis *rndis_class) setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE; setup->wValue = 0; setup->wIndex = 0; - setup->wLength = 4096; + setup->wLength = sizeof(g_rndis_buf); ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp); if (ret < 0) { @@ -386,14 +387,14 @@ static int usbh_rndis_connect(struct usbh_hubport *hport, uint8_t intf) } uint32_t packet_filter = 0x0f; - usbh_rndis_set_msg_transfer(rndis_class, OID_GEN_CURRENT_PACKET_FILTER, (uint8_t *)&packet_filter, 4); + ret = usbh_rndis_set_msg_transfer(rndis_class, OID_GEN_CURRENT_PACKET_FILTER, (uint8_t *)&packet_filter, 4); if (ret < 0) { return ret; } USB_LOG_INFO("rndis set OID_GEN_CURRENT_PACKET_FILTER success\r\n"); uint8_t multicast_list[6] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0x01 }; - usbh_rndis_set_msg_transfer(rndis_class, OID_802_3_MULTICAST_LIST, multicast_list, 6); + ret = usbh_rndis_set_msg_transfer(rndis_class, OID_802_3_MULTICAST_LIST, multicast_list, 6); if (ret < 0) { return ret; } @@ -460,6 +461,8 @@ void usbh_rndis_rx_thread(void *argument) uint32_t transfer_size = (16 * 1024); #endif + (void)argument; + USB_LOG_INFO("Create rndis rx thread\r\n"); // clang-format off find_class: @@ -531,7 +534,7 @@ find_class: #else if ((g_rndis_rx_length + (16 * 1024)) > CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE) { #endif - USB_LOG_ERR("Rx packet is overflow, please ruduce tcp window size or increase CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE\r\n"); + USB_LOG_ERR("Rx packet is overflow, please reduce tcp window size or increase CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE\r\n"); while (1) { } } @@ -581,10 +584,12 @@ int usbh_rndis_eth_output(uint32_t buflen) __WEAK void usbh_rndis_run(struct usbh_rndis *rndis_class) { + (void)rndis_class; } __WEAK void usbh_rndis_stop(struct usbh_rndis *rndis_class) { + (void)rndis_class; } static const struct usbh_class_driver rndis_class_driver = { @@ -595,9 +600,9 @@ static const struct usbh_class_driver rndis_class_driver = { CLASS_INFO_DEFINE const struct usbh_class_info rndis_class_info = { .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, - .class = USB_DEVICE_CLASS_WIRELESS, - .subclass = 0x01, - .protocol = 0x03, + .bInterfaceClass = USB_DEVICE_CLASS_WIRELESS, + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x03, .id_table = NULL, .class_driver = &rndis_class_driver }; diff --git a/rt-thread/components/drivers/usb/cherryusb/common/usb_dc.h b/rt-thread/components/drivers/usb/cherryusb/common/usb_dc.h index a10869a..1f2fddd 100644 --- a/rt-thread/components/drivers/usb/cherryusb/common/usb_dc.h +++ b/rt-thread/components/drivers/usb/cherryusb/common/usb_dc.h @@ -33,6 +33,13 @@ int usb_dc_deinit(uint8_t busid); */ int usbd_set_address(uint8_t busid, const uint8_t addr); +/** + * @brief Set remote wakeup feature + * + * @return On success will return 0, and others indicate fail. + */ +int usbd_set_remote_wakeup(uint8_t busid); + /** * @brief Get USB device speed * diff --git a/rt-thread/components/drivers/usb/cherryusb/common/usb_def.h b/rt-thread/components/drivers/usb/cherryusb/common/usb_def.h index aea3d6a..27b4d82 100644 --- a/rt-thread/components/drivers/usb/cherryusb/common/usb_def.h +++ b/rt-thread/components/drivers/usb/cherryusb/common/usb_def.h @@ -614,7 +614,7 @@ struct usb_webusb_url_descriptor { char URL[]; } __PACKED; -struct usb_webusb_url_ex_descriptor { +struct usb_webusb_descriptor { uint8_t vendor_code; const uint8_t *string; uint32_t string_len; diff --git a/rt-thread/components/drivers/usb/cherryusb/common/usb_log.h b/rt-thread/components/drivers/usb/cherryusb/common/usb_log.h index 923e800..8c798c7 100644 --- a/rt-thread/components/drivers/usb/cherryusb/common/usb_log.h +++ b/rt-thread/components/drivers/usb/cherryusb/common/usb_log.h @@ -82,4 +82,33 @@ void usb_assert(const char *filename, int linenum); usb_assert(__FILE__, __LINE__); \ } while (0) +#define ___is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') +static inline void usb_hexdump(const void *ptr, uint32_t buflen) +{ + unsigned char *buf = (unsigned char *)ptr; + uint32_t i, j; + + (void)buf; + + for (i = 0; i < buflen; i += 16) { + CONFIG_USB_PRINTF("%08X:", i); + + for (j = 0; j < 16; j++) + if (i + j < buflen) { + if ((j % 8) == 0) { + CONFIG_USB_PRINTF(" "); + } + + CONFIG_USB_PRINTF("%02X ", buf[i + j]); + } else + CONFIG_USB_PRINTF(" "); + CONFIG_USB_PRINTF(" "); + + for (j = 0; j < 16; j++) + if (i + j < buflen) + CONFIG_USB_PRINTF("%c", ___is_print(buf[i + j]) ? buf[i + j] : '.'); + CONFIG_USB_PRINTF("\n"); + } +} + #endif /* USB_LOG_H */ diff --git a/rt-thread/components/drivers/usb/cherryusb/common/usb_osal.h b/rt-thread/components/drivers/usb/cherryusb/common/usb_osal.h index f487c48..a3380d1 100644 --- a/rt-thread/components/drivers/usb/cherryusb/common/usb_osal.h +++ b/rt-thread/components/drivers/usb/cherryusb/common/usb_osal.h @@ -58,4 +58,7 @@ void usb_osal_leave_critical_section(size_t flag); void usb_osal_msleep(uint32_t delay); +void *usb_osal_malloc(size_t size); +void usb_osal_free(void *ptr); + #endif /* USB_OSAL_H */ diff --git a/rt-thread/components/drivers/usb/cherryusb/common/usb_version.h b/rt-thread/components/drivers/usb/cherryusb/common/usb_version.h new file mode 100644 index 0000000..d207af9 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/common/usb_version.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USB_VERSION_H +#define USB_VERSION_H + +#ifdef CHERRYUSB_VERSION +#warning "Please do not define CHERRYUSB_VERSION in usb_config.h" +#undef CHERRYUSB_VERSION +#endif +#ifdef CHERRYUSB_VERSION_STR +#warning "Please do not define CHERRYUSB_VERSION_STR in usb_config.h" +#undef CHERRYUSB_VERSION_STR +#endif + +#define CHERRYUSB_VERSION 0x010402 +#define CHERRYUSB_VERSION_STR "v1.4.2" + +#endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/core/usbd_core.c b/rt-thread/components/drivers/usb/cherryusb/core/usbd_core.c index e072d8a..d7fb351 100644 --- a/rt-thread/components/drivers/usb/cherryusb/core/usbd_core.c +++ b/rt-thread/components/drivers/usb/cherryusb/core/usbd_core.c @@ -45,12 +45,18 @@ USB_NOCACHE_RAM_SECTION struct usbd_core_priv { struct usb_msosv1_descriptor *msosv1_desc; struct usb_msosv2_descriptor *msosv2_desc; struct usb_bos_descriptor *bos_desc; + struct usb_webusb_descriptor *webusb_url_desc; #endif /* Buffer used for storing standard, class and vendor request data */ USB_MEM_ALIGNX uint8_t req_data[CONFIG_USBDEV_REQUEST_BUFFER_LEN]; /** Currently selected configuration */ uint8_t configuration; + uint8_t device_address; + bool self_powered; + bool remote_wakeup_support; + bool remote_wakeup_enabled; + bool is_suspend; #ifdef CONFIG_USBDEV_ADVANCE_DESC uint8_t speed; #endif @@ -58,6 +64,7 @@ USB_NOCACHE_RAM_SECTION struct usbd_core_priv { bool test_req; #endif struct usbd_interface *intf[16]; + uint8_t intf_altsetting[16]; uint8_t intf_offset; struct usbd_tx_rx_msg tx_msg[CONFIG_USBDEV_EP_NUM]; @@ -92,6 +99,7 @@ static bool is_device_configured(uint8_t busid) * This function sets endpoint configuration according to one specified in USB * endpoint descriptor and then enables it for data transfers. * + * @param [in] busid busid * @param [in] ep Endpoint descriptor byte array * * @return true if successfully configured and enabled @@ -99,9 +107,9 @@ static bool is_device_configured(uint8_t busid) static bool usbd_set_endpoint(uint8_t busid, const struct usb_endpoint_descriptor *ep) { USB_LOG_DBG("Open ep:0x%02x type:%u mps:%u\r\n", - ep->bEndpointAddress, - USB_GET_ENDPOINT_TYPE(ep->bmAttributes), - USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize)); + ep->bEndpointAddress, + USB_GET_ENDPOINT_TYPE(ep->bmAttributes), + USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize)); if (ep->bEndpointAddress & 0x80) { g_usbd_core[busid].tx_msg[ep->bEndpointAddress & 0x7f].ep_mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); @@ -119,6 +127,7 @@ static bool usbd_set_endpoint(uint8_t busid, const struct usb_endpoint_descripto * This function cancels transfers that are associated with endpoint and * disabled endpoint itself. * + * @param [in] busid busid * @param [in] ep Endpoint descriptor byte array * * @return true if successfully deconfigured and disabled @@ -126,8 +135,8 @@ static bool usbd_set_endpoint(uint8_t busid, const struct usb_endpoint_descripto static bool usbd_reset_endpoint(uint8_t busid, const struct usb_endpoint_descriptor *ep) { USB_LOG_DBG("Close ep:0x%02x type:%u\r\n", - ep->bEndpointAddress, - USB_GET_ENDPOINT_TYPE(ep->bmAttributes)); + ep->bEndpointAddress, + USB_GET_ENDPOINT_TYPE(ep->bmAttributes)); return usbd_ep_close(busid, ep->bEndpointAddress) == 0 ? true : false; } @@ -138,6 +147,7 @@ static bool usbd_reset_endpoint(uint8_t busid, const struct usb_endpoint_descrip * This function parses the list of installed USB descriptors and attempts * to find the specified USB descriptor. * + * @param [in] busid busid * @param [in] type_index Type and index of the descriptor * @param [out] data Descriptor data * @param [out] len Descriptor length @@ -174,6 +184,9 @@ static bool usbd_get_descriptor(uint8_t busid, uint16_t type_index, uint8_t **da break; } desc_len = ((desc[CONF_DESC_wTotalLength]) | (desc[CONF_DESC_wTotalLength + 1] << 8)); + + g_usbd_core[busid].self_powered = (desc[7] & USB_CONFIG_POWERED_MASK) ? true : false; + g_usbd_core[busid].remote_wakeup_support = (desc[7] & USB_CONFIG_REMOTE_WAKEUP) ? true : false; break; case USB_DESCRIPTOR_TYPE_STRING: if (index == USB_OSDESC_STRING_DESC_INDEX) { @@ -336,6 +349,9 @@ static bool usbd_get_descriptor(uint8_t busid, uint16_t type_index, uint8_t **da */ *len = (p[CONF_DESC_wTotalLength]) | (p[CONF_DESC_wTotalLength + 1] << 8); + + g_usbd_core[busid].self_powered = (p[7] & USB_CONFIG_POWERED_MASK) ? true : false; + g_usbd_core[busid].remote_wakeup_support = (p[7] & USB_CONFIG_REMOTE_WAKEUP) ? true : false; } else { /* normally length is at offset 0 */ *len = p[DESC_bLength]; @@ -358,6 +374,7 @@ static bool usbd_get_descriptor(uint8_t busid, uint16_t type_index, uint8_t **da * index and alternate setting by parsing the installed USB descriptor list. * A configuration index of 0 unconfigures the device. * + * @param [in] busid busid * @param [in] config_index Configuration index * @param [in] alt_setting Alternate setting number * @@ -427,6 +444,7 @@ static bool usbd_set_configuration(uint8_t busid, uint8_t config_index, uint8_t /** * @brief set USB interface * + * @param [in] busid busid * @param [in] iface Interface index * @param [in] alt_setting Alternate setting number * @@ -477,10 +495,12 @@ static bool usbd_set_interface(uint8_t busid, uint8_t iface, uint8_t alt_setting if (cur_iface == iface) { ep_desc = (struct usb_endpoint_descriptor *)p; - if (cur_alt_setting != alt_setting) { + if (alt_setting == 0) { ret = usbd_reset_endpoint(busid, ep_desc); - } else { + goto find_end; + } else if (cur_alt_setting == alt_setting) { ret = usbd_set_endpoint(busid, ep_desc); + } else { } } @@ -498,6 +518,7 @@ static bool usbd_set_interface(uint8_t busid, uint8_t iface, uint8_t alt_setting } } +find_end: usbd_class_event_notify_handler(busid, USBD_EVENT_SET_INTERFACE, (void *)if_desc); return ret; @@ -506,6 +527,7 @@ static bool usbd_set_interface(uint8_t busid, uint8_t iface, uint8_t alt_setting /** * @brief handle a standard device request * + * @param [in] busid busid * @param [in] setup The setup packet * @param [in,out] data Data buffer * @param [in,out] len Pointer to data length @@ -522,6 +544,12 @@ static bool usbd_std_device_req_handler(uint8_t busid, struct usb_setup_packet * /* bit 0: self-powered */ /* bit 1: remote wakeup */ (*data)[0] = 0x00; + if (g_usbd_core[busid].self_powered) { + (*data)[0] |= USB_GETSTATUS_SELF_POWERED; + } + if (g_usbd_core[busid].remote_wakeup_enabled) { + (*data)[0] |= USB_GETSTATUS_REMOTE_WAKEUP; + } (*data)[1] = 0x00; *len = 2; break; @@ -530,8 +558,10 @@ static bool usbd_std_device_req_handler(uint8_t busid, struct usb_setup_packet * case USB_REQUEST_SET_FEATURE: if (value == USB_FEATURE_REMOTE_WAKEUP) { if (setup->bRequest == USB_REQUEST_SET_FEATURE) { + g_usbd_core[busid].remote_wakeup_enabled = true; g_usbd_core[busid].event_handler(busid, USBD_EVENT_SET_REMOTE_WAKEUP); } else { + g_usbd_core[busid].remote_wakeup_enabled = false; g_usbd_core[busid].event_handler(busid, USBD_EVENT_CLR_REMOTE_WAKEUP); } } else if (value == USB_FEATURE_TEST_MODE) { @@ -543,6 +573,7 @@ static bool usbd_std_device_req_handler(uint8_t busid, struct usb_setup_packet * break; case USB_REQUEST_SET_ADDRESS: + g_usbd_core[busid].device_address = value; usbd_set_address(busid, value); *len = 0; break; @@ -556,17 +587,20 @@ static bool usbd_std_device_req_handler(uint8_t busid, struct usb_setup_packet * break; case USB_REQUEST_GET_CONFIGURATION: - *data = (uint8_t *)&g_usbd_core[busid].configuration; + (*data)[0] = g_usbd_core[busid].configuration; *len = 1; break; case USB_REQUEST_SET_CONFIGURATION: value &= 0xFF; - if (!usbd_set_configuration(busid, value, 0)) { + if (value == 0) { + g_usbd_core[busid].configuration = 0; + } else if (!usbd_set_configuration(busid, value, 0)) { ret = false; } else { g_usbd_core[busid].configuration = value; + g_usbd_core[busid].is_suspend = false; usbd_class_event_notify_handler(busid, USBD_EVENT_CONFIGURED, NULL); g_usbd_core[busid].event_handler(busid, USBD_EVENT_CONFIGURED); } @@ -589,6 +623,7 @@ static bool usbd_std_device_req_handler(uint8_t busid, struct usb_setup_packet * /** * @brief handle a standard interface request * + * @param [in] busid busid * @param [in] setup The setup packet * @param [in,out] data Data buffer * @param [in,out] len Pointer to data length @@ -600,6 +635,16 @@ static bool usbd_std_interface_req_handler(uint8_t busid, struct usb_setup_packe uint8_t type = HI_BYTE(setup->wValue); uint8_t intf_num = LO_BYTE(setup->wIndex); bool ret = true; + const uint8_t *p; + uint32_t desc_len = 0; + uint32_t current_desc_len = 0; + uint8_t cur_iface = 0xFF; + +#ifdef CONFIG_USBDEV_ADVANCE_DESC + p = g_usbd_core[busid].descriptors->config_descriptor_callback(g_usbd_core[busid].speed); +#else + p = (uint8_t *)g_usbd_core[busid].descriptors; +#endif /* Only when device is configured, then interface requests can be valid. */ if (!is_device_configured(busid)) { @@ -614,7 +659,39 @@ static bool usbd_std_interface_req_handler(uint8_t busid, struct usb_setup_packe break; case USB_REQUEST_GET_DESCRIPTOR: - if (type == 0x22) { /* HID_DESCRIPTOR_TYPE_HID_REPORT */ + if (type == 0x21) { /* HID_DESCRIPTOR_TYPE_HID */ + while (p[DESC_bLength] != 0U) { + switch (p[DESC_bDescriptorType]) { + case USB_DESCRIPTOR_TYPE_CONFIGURATION: + current_desc_len = 0; + desc_len = (p[CONF_DESC_wTotalLength]) | + (p[CONF_DESC_wTotalLength + 1] << 8); + + break; + + case USB_DESCRIPTOR_TYPE_INTERFACE: + cur_iface = p[INTF_DESC_bInterfaceNumber]; + break; + case 0x21: + if (cur_iface == intf_num) { + *data = (uint8_t *)p; + //memcpy(*data, p, p[DESC_bLength]); + *len = p[DESC_bLength]; + return true; + } + break; + default: + break; + } + + /* skip to next descriptor */ + p += p[DESC_bLength]; + current_desc_len += p[DESC_bLength]; + if (current_desc_len >= desc_len && desc_len) { + break; + } + } + } else if (type == 0x22) { /* HID_DESCRIPTOR_TYPE_HID_REPORT */ for (uint8_t i = 0; i < g_usbd_core[busid].intf_offset; i++) { struct usbd_interface *intf = g_usbd_core[busid].intf[i]; @@ -633,11 +710,12 @@ static bool usbd_std_interface_req_handler(uint8_t busid, struct usb_setup_packe ret = false; break; case USB_REQUEST_GET_INTERFACE: - (*data)[0] = 0; + (*data)[0] = g_usbd_core[busid].intf_altsetting[intf_num]; *len = 1; break; case USB_REQUEST_SET_INTERFACE: + g_usbd_core[busid].intf_altsetting[intf_num] = LO_BYTE(setup->wValue); usbd_set_interface(busid, setup->wIndex, setup->wValue); *len = 0; break; @@ -653,6 +731,7 @@ static bool usbd_std_interface_req_handler(uint8_t busid, struct usb_setup_packe /** * @brief handle a standard endpoint request * + * @param [in] busid busid * @param [in] setup The setup packet * @param [in,out] data Data buffer * @param [in,out] len Pointer to data length @@ -663,6 +742,7 @@ static bool usbd_std_endpoint_req_handler(uint8_t busid, struct usb_setup_packet { uint8_t ep = (uint8_t)setup->wIndex; bool ret = true; + uint8_t stalled; /* Only when device is configured, then endpoint requests can be valid. */ if (!is_device_configured(busid)) { @@ -671,7 +751,12 @@ static bool usbd_std_endpoint_req_handler(uint8_t busid, struct usb_setup_packet switch (setup->bRequest) { case USB_REQUEST_GET_STATUS: - (*data)[0] = 0x00; + usbd_ep_is_stalled(busid, ep, &stalled); + if (stalled) { + (*data)[0] = 0x01; + } else { + (*data)[0] = 0x00; + } (*data)[1] = 0x00; *len = 2; break; @@ -711,6 +796,7 @@ static bool usbd_std_endpoint_req_handler(uint8_t busid, struct usb_setup_packet /** * @brief handle standard requests (list in chapter 9) * + * @param [in] busid busid * @param [in] setup The setup packet * @param [in,out] data Data buffer * @param [in,out] len Pointer to data length @@ -754,8 +840,7 @@ static int usbd_standard_request_handler(uint8_t busid, struct usb_setup_packet /** * @brief handler for class requests * - * If a custom request handler was installed, this handler is called first. - * + * @param [in] busid busid * @param [in] setup The setup packet * @param [in,out] data Data buffer * @param [in,out] len Pointer to data length @@ -787,8 +872,7 @@ static int usbd_class_request_handler(uint8_t busid, struct usb_setup_packet *se /** * @brief handler for vendor requests * - * If a custom request handler was installed, this handler is called first. - * + * @param [in] busid busid * @param [in] setup The setup packet * @param [in,out] data Data buffer * @param [in,out] len Pointer to data length @@ -841,10 +925,12 @@ static int usbd_vendor_request_handler(uint8_t busid, struct usb_setup_packet *s return -1; } } - } else if (g_usbd_core[busid].descriptors->webusb_url_descriptor) { + } + + if (g_usbd_core[busid].descriptors->webusb_url_descriptor) { if (setup->bRequest == g_usbd_core[busid].descriptors->webusb_url_descriptor->vendor_code) { switch (setup->wIndex) { - case WINUSB_REQUEST_GET_DESCRIPTOR_SET: + case WEBUSB_REQUEST_GET_URL: desclen = g_usbd_core[busid].descriptors->webusb_url_descriptor->string_len; *data = (uint8_t *)g_usbd_core[busid].descriptors->webusb_url_descriptor->string; //memcpy(*data, g_usbd_core[busid].descriptors->webusb_url_descriptor->string, desclen); @@ -897,6 +983,22 @@ static int usbd_vendor_request_handler(uint8_t busid, struct usb_setup_packet *s } } } + + if (g_usbd_core[busid].webusb_url_desc) { + if (setup->bRequest == g_usbd_core[busid].webusb_url_desc->vendor_code) { + switch (setup->wIndex) { + case WEBUSB_REQUEST_GET_URL: + desclen = g_usbd_core[busid].webusb_url_desc->string_len; + *data = (uint8_t *)g_usbd_core[busid].webusb_url_desc->string; + //memcpy(*data, g_usbd_core[busid].webusb_url_desc->string, desclen); + *len = desclen; + return 0; + default: + USB_LOG_ERR("unknown vendor code\r\n"); + return -1; + } + } + } #endif for (uint8_t i = 0; i < g_usbd_core[busid].intf_offset; i++) { struct usbd_interface *intf = g_usbd_core[busid].intf[i]; @@ -912,6 +1014,7 @@ static int usbd_vendor_request_handler(uint8_t busid, struct usb_setup_packet *s /** * @brief handle setup request( standard/class/vendor/other) * + * @param [in] busid busid * @param [in] setup The setup packet * @param [in,out] data Data buffer * @param [in,out] len Pointer to data length @@ -985,17 +1088,22 @@ void usbd_event_disconnect_handler(uint8_t busid) void usbd_event_resume_handler(uint8_t busid) { + g_usbd_core[busid].is_suspend = false; g_usbd_core[busid].event_handler(busid, USBD_EVENT_RESUME); } void usbd_event_suspend_handler(uint8_t busid) { - g_usbd_core[busid].event_handler(busid, USBD_EVENT_SUSPEND); + if (g_usbd_core[busid].device_address > 0) { + g_usbd_core[busid].is_suspend = true; + g_usbd_core[busid].event_handler(busid, USBD_EVENT_SUSPEND); + } } void usbd_event_reset_handler(uint8_t busid) { usbd_set_address(busid, 0); + g_usbd_core[busid].device_address = 0; g_usbd_core[busid].configuration = 0; #ifdef CONFIG_USBDEV_ADVANCE_DESC g_usbd_core[busid].speed = USB_SPEED_UNKNOWN; @@ -1068,7 +1176,7 @@ void usbd_event_ep0_setup_complete_handler(uint8_t busid, uint8_t *psetup) #ifdef CONFIG_USBDEV_EP0_INDATA_NO_COPY g_usbd_core[busid].ep0_data_buf = buf; #else - memcpy(g_usbd_core[busid].ep0_data_buf, buf, g_usbd_core[busid].ep0_data_buf_residue); + usb_memcpy(g_usbd_core[busid].ep0_data_buf, buf, g_usbd_core[busid].ep0_data_buf_residue); #endif } else { /* use memcpy(*data, xxx, len); has copied into ep0 buffer, we do nothing */ @@ -1091,6 +1199,8 @@ void usbd_event_ep0_in_complete_handler(uint8_t busid, uint8_t ep, uint32_t nbyt { struct usb_setup_packet *setup = &g_usbd_core[busid].setup; + (void)ep; + g_usbd_core[busid].ep0_data_buf += nbytes; g_usbd_core[busid].ep0_data_buf_residue -= nbytes; @@ -1130,6 +1240,8 @@ void usbd_event_ep0_out_complete_handler(uint8_t busid, uint8_t ep, uint32_t nby { struct usb_setup_packet *setup = &g_usbd_core[busid].setup; + (void)ep; + if (nbytes > 0) { g_usbd_core[busid].ep0_data_buf += nbytes; g_usbd_core[busid].ep0_data_buf_residue -= nbytes; @@ -1213,6 +1325,11 @@ void usbd_bos_desc_register(uint8_t busid, struct usb_bos_descriptor *desc) { g_usbd_core[busid].bos_desc = desc; } + +void usbd_webusb_desc_register(uint8_t busid, struct usb_webusb_descriptor *desc) +{ + g_usbd_core[busid].webusb_url_desc = desc; +} #endif void usbd_add_interface(uint8_t busid, struct usbd_interface *intf) @@ -1256,7 +1373,30 @@ bool usb_device_is_configured(uint8_t busid) return g_usbd_core[busid].configuration; } -int usbd_initialize(uint8_t busid, uint32_t reg_base, void (*event_handler)(uint8_t busid, uint8_t event)) +bool usb_device_is_suspend(uint8_t busid) +{ + return g_usbd_core[busid].is_suspend; +} + +int usbd_send_remote_wakeup(uint8_t busid) +{ + if (g_usbd_core[busid].remote_wakeup_support && g_usbd_core[busid].remote_wakeup_enabled && g_usbd_core[busid].is_suspend) { + return usbd_set_remote_wakeup(busid); + } else { + if (!g_usbd_core[busid].remote_wakeup_support) { + USB_LOG_ERR("device does not support remote wakeup\r\n"); + } + if (!g_usbd_core[busid].remote_wakeup_enabled) { + USB_LOG_ERR("device remote wakeup is not enabled\r\n"); + } + if (!g_usbd_core[busid].is_suspend) { + USB_LOG_ERR("device is not in suspend state\r\n"); + } + return -1; + } +} + +int usbd_initialize(uint8_t busid, uintptr_t reg_base, void (*event_handler)(uint8_t busid, uint8_t event)) { int ret; struct usbd_bus *bus; @@ -1279,9 +1419,15 @@ int usbd_initialize(uint8_t busid, uint32_t reg_base, void (*event_handler)(uint int usbd_deinitialize(uint8_t busid) { - g_usbd_core[busid].intf_offset = 0; - usb_dc_deinit(busid); - usbd_class_event_notify_handler(busid, USBD_EVENT_DEINIT, NULL); + if (busid >= CONFIG_USBDEV_MAX_BUS) { + USB_LOG_ERR("bus overflow\r\n"); + while (1) { + } + } + g_usbd_core[busid].event_handler(busid, USBD_EVENT_DEINIT); + usbd_class_event_notify_handler(busid, USBD_EVENT_DEINIT, NULL); + usb_dc_deinit(busid); + g_usbd_core[busid].intf_offset = 0; return 0; } diff --git a/rt-thread/components/drivers/usb/cherryusb/core/usbd_core.h b/rt-thread/components/drivers/usb/cherryusb/core/usbd_core.h index 01335f2..71bdb29 100644 --- a/rt-thread/components/drivers/usb/cherryusb/core/usbd_core.h +++ b/rt-thread/components/drivers/usb/cherryusb/core/usbd_core.h @@ -23,6 +23,7 @@ extern "C" { #include "usb_log.h" #include "usb_dc.h" #include "usb_memcpy.h" +#include "usb_version.h" enum usbd_event_type { /* USB DCD IRQ */ @@ -71,13 +72,13 @@ struct usb_descriptor { const char *(*string_descriptor_callback)(uint8_t speed, uint8_t index); const struct usb_msosv1_descriptor *msosv1_descriptor; const struct usb_msosv2_descriptor *msosv2_descriptor; - const struct usb_webusb_url_ex_descriptor *webusb_url_descriptor; + const struct usb_webusb_descriptor *webusb_url_descriptor; const struct usb_bos_descriptor *bos_descriptor; }; struct usbd_bus { uint8_t busid; - uint32_t reg_base; + uintptr_t reg_base; }; extern struct usbd_bus g_usbdev_bus[]; @@ -93,6 +94,7 @@ void usbd_desc_register(uint8_t busid, const uint8_t *desc); void usbd_msosv1_desc_register(uint8_t busid, struct usb_msosv1_descriptor *desc); void usbd_msosv2_desc_register(uint8_t busid, struct usb_msosv2_descriptor *desc); void usbd_bos_desc_register(uint8_t busid, struct usb_bos_descriptor *desc); +void usbd_webusb_desc_register(uint8_t busid, struct usb_webusb_descriptor *desc); #endif void usbd_add_interface(uint8_t busid, struct usbd_interface *intf); @@ -101,8 +103,10 @@ void usbd_add_endpoint(uint8_t busid, struct usbd_endpoint *ep); uint16_t usbd_get_ep_mps(uint8_t busid, uint8_t ep); uint8_t usbd_get_ep_mult(uint8_t busid, uint8_t ep); bool usb_device_is_configured(uint8_t busid); +bool usb_device_is_suspend(uint8_t busid); +int usbd_send_remote_wakeup(uint8_t busid); -int usbd_initialize(uint8_t busid, uint32_t reg_base, void (*event_handler)(uint8_t busid, uint8_t event)); +int usbd_initialize(uint8_t busid, uintptr_t reg_base, void (*event_handler)(uint8_t busid, uint8_t event)); int usbd_deinitialize(uint8_t busid); #ifdef __cplusplus diff --git a/rt-thread/components/drivers/usb/cherryusb/core/usbh_core.c b/rt-thread/components/drivers/usb/cherryusb/core/usbh_core.c index d1fc769..cb36f97 100644 --- a/rt-thread/components/drivers/usb/cherryusb/core/usbh_core.c +++ b/rt-thread/components/drivers/usb/cherryusb/core/usbh_core.c @@ -93,19 +93,22 @@ static const struct usbh_class_driver *usbh_find_class_driver(uint8_t class, uin struct usbh_class_info *index = NULL; for (index = usbh_class_info_table_begin; index < usbh_class_info_table_end; index++) { - if ((index->match_flags & USB_CLASS_MATCH_INTF_CLASS) && !(index->class == class)) { + if ((index->match_flags & USB_CLASS_MATCH_INTF_CLASS) && !(index->bInterfaceClass == class)) { continue; } - if ((index->match_flags & USB_CLASS_MATCH_INTF_SUBCLASS) && !(index->subclass == subclass)) { + if ((index->match_flags & USB_CLASS_MATCH_INTF_SUBCLASS) && !(index->bInterfaceSubClass == subclass)) { continue; } - if ((index->match_flags & USB_CLASS_MATCH_INTF_PROTOCOL) && !(index->protocol == protocol)) { + if ((index->match_flags & USB_CLASS_MATCH_INTF_PROTOCOL) && !(index->bInterfaceProtocol == protocol)) { continue; } if (index->match_flags & USB_CLASS_MATCH_VID_PID && index->id_table) { /* scan id table */ uint32_t i; - for (i = 0; index->id_table[i][0] && index->id_table[i][0] != vid && index->id_table[i][1] != pid; i++) { + for (i = 0; index->id_table[i][0]; i++) { + if (index->id_table[i][0] == vid && index->id_table[i][1] == pid) { + break; + } } /* do not match, continue next */ if (!index->id_table[i][0]) { @@ -486,7 +489,7 @@ int usbh_enumerate(struct usbh_hubport *hport) goto errout; } USB_LOG_INFO("The device has %d interfaces\r\n", ((struct usb_configuration_descriptor *)ep0_request_buffer[hport->bus->busid])->bNumInterfaces); - hport->raw_config_desc = usb_malloc(wTotalLength); + hport->raw_config_desc = usb_osal_malloc(wTotalLength + 1); if (hport->raw_config_desc == NULL) { ret = -USB_ERR_NOMEM; USB_LOG_ERR("No memory to alloc for raw_config_desc\r\n"); @@ -495,6 +498,8 @@ int usbh_enumerate(struct usbh_hubport *hport) config_value = ((struct usb_configuration_descriptor *)ep0_request_buffer[hport->bus->busid])->bConfigurationValue; memcpy(hport->raw_config_desc, ep0_request_buffer[hport->bus->busid], wTotalLength); + hport->raw_config_desc[wTotalLength] = '\0'; + #ifdef CONFIG_USBHOST_GET_STRING_DESC uint8_t string_buffer[128]; @@ -549,7 +554,7 @@ int usbh_enumerate(struct usbh_hubport *hport) setup->wLength = 16; ret = usbh_control_transfer(hport, setup, ep0_request_buffer[hport->bus->busid]); - if (ret < 0 && (ret != -EPERM)) { + if (ret < 0 && (ret != -USB_ERR_STALL)) { USB_LOG_ERR("Failed to get msosv1 compat id,errorcode:%d\r\n", ret); goto errout; } @@ -576,7 +581,7 @@ int usbh_enumerate(struct usbh_hubport *hport) errout: if (hport->raw_config_desc) { - usb_free(hport->raw_config_desc); + usb_osal_free(hport->raw_config_desc); hport->raw_config_desc = NULL; } return ret; @@ -600,7 +605,7 @@ void usbh_hubport_release(struct usbh_hubport *hport) } } -static void usbh_bus_init(struct usbh_bus *bus, uint8_t busid, uint32_t reg_base) +static void usbh_bus_init(struct usbh_bus *bus, uint8_t busid, uintptr_t reg_base) { memset(bus, 0, sizeof(struct usbh_bus)); bus->busid = busid; @@ -613,7 +618,7 @@ static void usbh_bus_init(struct usbh_bus *bus, uint8_t busid, uint32_t reg_base usb_slist_add_tail(&g_bus_head, &bus->list); } -int usbh_initialize(uint8_t busid, uint32_t reg_base) +int usbh_initialize(uint8_t busid, uintptr_t reg_base) { struct usbh_bus *bus; @@ -649,6 +654,12 @@ int usbh_deinitialize(uint8_t busid) { struct usbh_bus *bus; + if (busid >= CONFIG_USBHOST_MAX_BUS) { + USB_LOG_ERR("bus overflow\r\n"); + while (1) { + } + } + bus = &g_usbhost_bus[busid]; usbh_hub_deinitialize(bus); @@ -731,7 +742,7 @@ static void *usbh_list_all_interface_name(struct usbh_hub *hub, const char *devn struct usbh_hub *hub_next; void *priv; - for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + for (uint8_t port = 0; port < hub->nports; port++) { hport = &hub->child[port]; if (hport->connected) { for (uint8_t itf = 0; itf < hport->config.config_desc.bNumInterfaces; itf++) { @@ -760,8 +771,9 @@ static void usbh_list_all_interface_driver(struct usbh_hub *hub) { struct usbh_hubport *hport; struct usbh_hub *hub_next; + const char *speed_table[] = { "error-speed", "low-speed", "full-speed", "high-speed", "wireless-speed", "super-speed", "superplus-speed" }; - for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + for (uint8_t port = 0; port < hub->nports; port++) { hport = &hub->child[port]; if (hport->connected) { for (uint8_t itf = 0; itf < hport->config.config_desc.bNumInterfaces; itf++) { @@ -770,11 +782,12 @@ static void usbh_list_all_interface_driver(struct usbh_hub *hub) USB_LOG_RAW("\t"); } - USB_LOG_RAW("|__Port %u, dev addr:0x%02x, If %u, ClassDriver=%s\r\n", + USB_LOG_RAW("|__Port %u, dev addr:0x%02x, If %u, ClassDriver=%s, %s\r\n", hport->port, hport->dev_addr, itf, - hport->config.intf[itf].class_driver->driver_name); + hport->config.intf[itf].class_driver->driver_name, + speed_table[hport->speed]); if (strcmp(hport->config.intf[itf].class_driver->driver_name, "hub") == 0) { hub_next = hport->config.intf[itf].priv; @@ -794,7 +807,7 @@ static void usbh_list_all_interface_desc(struct usbh_bus *bus, struct usbh_hub * struct usbh_hubport *hport; struct usbh_hub *hub_next; - for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + for (uint8_t port = 0; port < hub->nports; port++) { hport = &hub->child[port]; if (hport->connected) { USB_LOG_RAW("\r\nBus %u, Hub %u, Port %u, dev addr:0x%02x, VID:PID 0x%04x:0x%04x\r\n", @@ -821,6 +834,37 @@ static void usbh_list_all_interface_desc(struct usbh_bus *bus, struct usbh_hub * } } +static struct usbh_hubport *usbh_list_all_hubport(struct usbh_hub *hub, uint8_t hub_index, uint8_t hub_port) +{ + struct usbh_hubport *hport; + struct usbh_hub *hub_next; + + if (hub->index == hub_index) { + hport = &hub->child[hub_port - 1]; + return hport; + } else { + for (uint8_t port = 0; port < hub->nports; port++) { + hport = &hub->child[port]; + if (hport->connected) { + for (uint8_t itf = 0; itf < hport->config.config_desc.bNumInterfaces; itf++) { + if (hport->config.intf[itf].class_driver && hport->config.intf[itf].class_driver->driver_name) { + if (strcmp(hport->config.intf[itf].class_driver->driver_name, "hub") == 0) { + hub_next = hport->config.intf[itf].priv; + + if (hub_next && hub_next->connected) { + hport = usbh_list_all_hubport(hub_next, hub_index, hub_port); + if (hport) { + return hport; + } + } + } + } + } + } + } + } + return NULL; +} void *usbh_find_class_instance(const char *devname) { usb_slist_t *bus_list; @@ -845,6 +889,23 @@ void *usbh_find_class_instance(const char *devname) return NULL; } +struct usbh_hubport *usbh_find_hubport(uint8_t busid, uint8_t hub_index, uint8_t hub_port) +{ + struct usbh_hub *hub; + struct usbh_bus *bus; + struct usbh_hubport *hport; + size_t flags; + + flags = usb_osal_enter_critical_section(); + + bus = &g_usbhost_bus[busid]; + hub = &bus->hcd.roothub; + + hport = usbh_list_all_hubport(hub, hub_index, hub_port); + usb_osal_leave_critical_section(flags); + return hport; +} + int lsusb(int argc, char **argv) { usb_slist_t *bus_list; @@ -888,7 +949,7 @@ int lsusb(int argc, char **argv) USB_LOG_RAW("/: Bus %u, Hub %u, ports=%u, is roothub\r\n", bus->busid, hub->index, - hub->hub_desc.bNbrPorts); + hub->nports); usbh_list_all_interface_driver(hub); } } diff --git a/rt-thread/components/drivers/usb/cherryusb/core/usbh_core.h b/rt-thread/components/drivers/usb/cherryusb/core/usbh_core.h index 3210988..eaddb30 100644 --- a/rt-thread/components/drivers/usb/cherryusb/core/usbh_core.h +++ b/rt-thread/components/drivers/usb/cherryusb/core/usbh_core.h @@ -21,6 +21,7 @@ #include "usb_osal.h" #include "usbh_hub.h" #include "usb_memcpy.h" +#include "usb_version.h" #ifdef __cplusplus extern "C" { @@ -60,9 +61,9 @@ extern "C" { struct usbh_class_info { uint8_t match_flags; /* Used for product specific matches; range is inclusive */ - uint8_t class; /* Base device class code */ - uint8_t subclass; /* Sub-class, depends on base class. Eg. */ - uint8_t protocol; /* Protocol, depends on base class. Eg. */ + uint8_t bInterfaceClass; /* Base device class code */ + uint8_t bInterfaceSubClass; /* Sub-class, depends on base class. Eg. */ + uint8_t bInterfaceProtocol; /* Protocol, depends on base class. Eg. */ const uint16_t (*id_table)[2]; /* List of Vendor/Product ID pairs */ const struct usbh_class_driver *class_driver; }; @@ -101,6 +102,9 @@ struct usbh_hubport { uint8_t port; /* Hub port index */ uint8_t dev_addr; /* device address */ uint8_t speed; /* device speed */ + uint8_t depth; /* distance from root hub */ + uint8_t route; /* route string */ + uint8_t slot_id; /* slot id */ struct usb_device_descriptor device_desc; struct usbh_configuration config; const char *iManufacturer; @@ -109,6 +113,7 @@ struct usbh_hubport { uint8_t *raw_config_desc; struct usb_setup_packet *setup; struct usbh_hub *parent; + struct usbh_hub *self; /* if this hubport is a hub */ struct usbh_bus *bus; struct usb_endpoint_descriptor ep0; struct usbh_urb ep0_urb; @@ -120,7 +125,13 @@ struct usbh_hub { bool is_roothub; uint8_t index; uint8_t hub_addr; - struct usb_hub_descriptor hub_desc; + uint8_t speed; + uint8_t nports; + uint8_t powerdelay; + uint8_t tt_think; + bool ismtt; + struct usb_hub_descriptor hub_desc; /* USB 2.0 only */ + struct usb_hub_ss_descriptor hub_ss_desc; /* USB 3.0 only */ struct usbh_hubport child[CONFIG_USBHOST_MAX_EHPORTS]; struct usbh_hubport *parent; struct usbh_bus *bus; @@ -143,9 +154,9 @@ struct usbh_devaddr_map { }; struct usbh_hcd { - uint32_t reg_base; + uintptr_t reg_base; uint8_t hcd_id; - uint8_t roothub_intbuf[1]; + uint8_t roothub_intbuf[2]; /* at most 15 roothub ports */ struct usbh_hub roothub; }; @@ -261,9 +272,10 @@ int usbh_get_string_desc(struct usbh_hubport *hport, uint8_t index, uint8_t *out */ int usbh_set_interface(struct usbh_hubport *hport, uint8_t intf, uint8_t altsetting); -int usbh_initialize(uint8_t busid, uint32_t reg_base); +int usbh_initialize(uint8_t busid, uintptr_t reg_base); int usbh_deinitialize(uint8_t busid); void *usbh_find_class_instance(const char *devname); +struct usbh_hubport *usbh_find_hubport(uint8_t busid, uint8_t hub_index, uint8_t hub_port); int lsusb(int argc, char **argv); diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/adb/cherryadb.png b/rt-thread/components/drivers/usb/cherryusb/demo/adb/cherryadb.png new file mode 100644 index 0000000000000000000000000000000000000000..512586b954e80be18f05f84d734cbe204792ce64 GIT binary patch literal 42508 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 literal 0 HcmV?d00001 diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/adb/cherrysh_port.c b/rt-thread/components/drivers/usb/cherryusb/demo/adb/cherrysh_port.c new file mode 100644 index 0000000..8597172 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/demo/adb/cherrysh_port.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "FreeRTOS.h" +#include "task.h" +#include "event_groups.h" +#include "csh.h" +#include "usbd_core.h" +#include "usbd_adb.h" +#include "chry_ringbuffer.h" + +static chry_ringbuffer_t shell_rb; +static uint8_t mempool[1024]; + +#ifndef task_repl_PRIORITY +#define task_repl_PRIORITY (configMAX_PRIORITIES - 4U) +#endif + +#ifndef task_exec_PRIORITY +#define task_exec_PRIORITY (configMAX_PRIORITIES - 5U) +#endif + +static chry_shell_t csh; +static volatile bool login = false; + +static StaticTask_t task_buffer_repl; +static StaticTask_t task_buffer_exec; + +static StackType_t task_stack_repl[1024]; +static StackType_t task_stack_exec[1024]; + +static TaskHandle_t task_hdl_repl = NULL; +static TaskHandle_t task_hdl_exec = NULL; + +static EventGroupHandle_t event_hdl; +static StaticEventGroup_t event_grp; + +void usbd_adb_notify_shell_read(uint8_t *data, uint32_t len) +{ + chry_ringbuffer_write(&shell_rb, data, len); + + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xEventGroupSetBitsFromISR(event_hdl, 0x10, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +void usbd_adb_notify_write_done(void) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xEventGroupSetBitsFromISR(event_hdl, 0x20, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +static uint16_t csh_sput_cb(chry_readline_t *rl, const void *data, uint16_t size) +{ + (void)rl; + + if (!usb_device_is_configured(0)) { + return size; + } + + if (usbd_adb_can_write() && size) { + usbd_abd_write(ADB_SHELL_LOALID, data, size); + xEventGroupWaitBits(event_hdl, 0x20, pdTRUE, pdFALSE, portMAX_DELAY); + } + + return size; +} + +static uint16_t csh_sget_cb(chry_readline_t *rl, void *data, uint16_t size) +{ + (void)rl; + + return chry_ringbuffer_read(&shell_rb, data, size); +} + +static void wait_char(void) +{ + EventBits_t event; +wait: + /* In order to lock the log from being disrupted , wait for REPL task execution to complete */ + event = xEventGroupWaitBits(event_hdl, (0x10 | 0x01 | 0x04), pdTRUE, pdFALSE, portMAX_DELAY); + if ((event & 0x10) == 0) { + if (event & 0x01) { + chry_readline_erase_line(&csh.rl); + xEventGroupSetBits(event_hdl, 0x02); + } + if (event & 0x04) { + chry_readline_edit_refresh(&csh.rl); + xEventGroupSetBits(event_hdl, 0x08); + } + + goto wait; + } +} + +static void task_repl(void *param) +{ + (void)param; + int ret; + volatile uint8_t *pexec = (void *)&csh.exec; + + for (;;) { + restart: + if (login) { + goto repl; + } else { + } + + ret = csh_login(&csh); + if (ret == 0) { + login = true; + } else if (ret == 1) { + /*!< no enough char */ + wait_char(); + continue; + } else { + continue; + } + + repl: + ret = chry_shell_task_repl(&csh); + + if (ret == -1) { + /*!< error */ + goto restart; + } else if (ret == 1) { + /*!< no enough char */ + wait_char(); + } else { + /*!< restart */ + } + + /*!< check flag */ + if (*pexec == CSH_STATUS_EXEC_DONE) { + *pexec = CSH_STATUS_EXEC_IDLE; + chry_readline_auto_refresh(&csh.rl, true); + chry_readline_ignore(&csh.rl, false); + chry_readline_edit_refresh(&csh.rl); + } + + if (login == false) { + chry_readline_erase_line(&csh.rl); + csh.rl.noblock = false; + } + } +} + +static void task_exec(void *param) +{ + (void)param; + + /*!< execute shell command */ + chry_shell_task_exec(&csh); + + /*!< notify REPL task execute done */ + xEventGroupSetBits(event_hdl, 0x10); + + /*!< wait for REPL task delete */ + vTaskSuspend(NULL); +} + +int chry_shell_port_create_context(chry_shell_t *csh, int argc, const char **argv) +{ + volatile TaskHandle_t *p_task_hdl_exec = (void *)&task_hdl_exec; + (void)csh; + (void)argc; + (void)argv; + + if (*p_task_hdl_exec != NULL) { + vTaskDelete(*p_task_hdl_exec); + } + + *p_task_hdl_exec = xTaskCreateStatic(task_exec, "task_exec", 1024U, NULL, task_exec_PRIORITY, task_stack_exec, &task_buffer_exec); + return 0; +} + +void chry_shell_port_default_handler(chry_shell_t *csh, int sig) +{ + volatile uint8_t *pexec = (void *)&csh->exec; + volatile TaskHandle_t *p_task_hdl_exec = (void *)&task_hdl_exec; + + switch (sig) { + case CSH_SIGINT: + case CSH_SIGQUIT: + case CSH_SIGKILL: + case CSH_SIGTERM: + break; + default: + return; + } + + /*!< force delete task */ + if (*p_task_hdl_exec != NULL) { + vTaskDelete(task_hdl_exec); + *p_task_hdl_exec = NULL; + } + + switch (sig) { + case CSH_SIGINT: + csh->rl.sput(&csh->rl, "^SIGINT" CONFIG_CSH_NEWLINE, sizeof("^SIGINT" CONFIG_CSH_NEWLINE) - 1); + break; + case CSH_SIGQUIT: + csh->rl.sput(&csh->rl, "^SIGQUIT" CONFIG_CSH_NEWLINE, sizeof("^SIGQUIT" CONFIG_CSH_NEWLINE) - 1); + break; + case CSH_SIGKILL: + csh->rl.sput(&csh->rl, "^SIGKILL" CONFIG_CSH_NEWLINE, sizeof("^SIGKILL" CONFIG_CSH_NEWLINE) - 1); + break; + case CSH_SIGTERM: + csh->rl.sput(&csh->rl, "^SIGTERM" CONFIG_CSH_NEWLINE, sizeof("^SIGTERM" CONFIG_CSH_NEWLINE) - 1); + break; + default: + return; + } + + *pexec = CSH_STATUS_EXEC_IDLE; + chry_readline_auto_refresh(&csh->rl, true); + chry_readline_ignore(&csh->rl, false); + chry_readline_edit_refresh(&csh->rl); +} + +int shell_init(bool need_login) +{ + chry_shell_init_t csh_init; + + if (chry_ringbuffer_init(&shell_rb, mempool, sizeof(mempool))) { + return -1; + } + + if (need_login) { + login = false; + } else { + login = true; + } + + /*!< I/O callback */ + csh_init.sput = csh_sput_cb; + csh_init.sget = csh_sget_cb; + +#if defined(CONFIG_CSH_SYMTAB) && CONFIG_CSH_SYMTAB + extern const int __fsymtab_start; + extern const int __fsymtab_end; + extern const int __vsymtab_start; + extern const int __vsymtab_end; + + /*!< get table from ld symbol */ + csh_init.command_table_beg = &__fsymtab_start; + csh_init.command_table_end = &__fsymtab_end; + csh_init.variable_table_beg = &__vsymtab_start; + csh_init.variable_table_end = &__vsymtab_end; +#endif + +#if defined(CONFIG_CSH_PROMPTEDIT) && CONFIG_CSH_PROMPTEDIT + static char csh_prompt_buffer[128]; + + /*!< set prompt buffer */ + csh_init.prompt_buffer = csh_prompt_buffer; + csh_init.prompt_buffer_size = sizeof(csh_prompt_buffer); +#endif + +#if defined(CONFIG_CSH_HISTORY) && CONFIG_CSH_HISTORY + static char csh_history_buffer[128]; + + /*!< set history buffer */ + csh_init.history_buffer = csh_history_buffer; + csh_init.history_buffer_size = sizeof(csh_history_buffer); +#endif + +#if defined(CONFIG_CSH_LNBUFF_STATIC) && CONFIG_CSH_LNBUFF_STATIC + static char csh_line_buffer[128]; + + /*!< set linebuffer */ + csh_init.line_buffer = csh_line_buffer; + csh_init.line_buffer_size = sizeof(csh_line_buffer); +#endif + + csh_init.uid = 0; + csh_init.user[0] = "cherry"; + + /*!< The port hash function is required, + and the strcmp attribute is used weakly by default, + int chry_shell_port_hash_strcmp(const char *hash, const char *str); */ + csh_init.hash[0] = "12345678"; /*!< If there is no password, set to NULL */ + csh_init.host = "cherryadb"; + csh_init.user_data = NULL; + + int ret = chry_shell_init(&csh, &csh_init); + if (ret) { + return -1; + } + + task_hdl_exec = NULL; + event_hdl = xEventGroupCreateStatic(&event_grp); + task_hdl_repl = xTaskCreateStatic(task_repl, "task_repl", 1024U, NULL, task_repl_PRIORITY, task_stack_repl, &task_buffer_repl); + + return 0; +} + +void shell_lock(void) +{ + xEventGroupSetBits(event_hdl, 0x01); + xEventGroupWaitBits(event_hdl, 0x02, pdTRUE, pdTRUE, portMAX_DELAY); +} + +void shell_unlock(void) +{ + xEventGroupSetBits(event_hdl, 0x04); + xEventGroupWaitBits(event_hdl, 0x08, pdTRUE, pdTRUE, portMAX_DELAY); +} + +static int csh_exit(int argc, char **argv) +{ + (void)argc; + (void)argv; + + usbd_adb_close(ADB_SHELL_LOALID); + + return 0; +} +CSH_SCMD_EXPORT_ALIAS(csh_exit, exit, ); + +#define __ENV_PATH "/sbin:/bin" +const char ENV_PATH[] = __ENV_PATH; +CSH_RVAR_EXPORT(ENV_PATH, PATH, sizeof(__ENV_PATH)); + +#define __ENV_ZERO "" +const char ENV_ZERO[] = __ENV_ZERO; +CSH_RVAR_EXPORT(ENV_ZERO, ZERO, sizeof(__ENV_ZERO)); diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/adb/usbd_adb_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/adb/usbd_adb_template.c new file mode 100644 index 0000000..2090dae --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/demo/adb/usbd_adb_template.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbd_core.h" +#include "usbd_adb.h" + +/*!< endpoint address */ +#define WINUSB_IN_EP 0x81 +#define WINUSB_OUT_EP 0x02 + +#define USBD_VID 0xFFFF +#define USBD_PID 0xFFFF +#define USBD_MAX_POWER 100 +#define USBD_LANGID_STRING 1033 + +/*!< config descriptor size */ +#define USB_CONFIG_SIZE (9 + 9 + 7 + 7) + +#ifdef CONFIG_USB_HS +#define WINUSB_MAX_MPS 512 +#else +#define WINUSB_MAX_MPS 64 +#endif + +#define WCID_VENDOR_CODE 0x17 +#define ADB_INTF_NUM 0 + +__ALIGN_BEGIN const uint8_t WCID_StringDescriptor_MSOS[18] __ALIGN_END = { + /////////////////////////////////////// + /// MS OS string descriptor + /////////////////////////////////////// + 0x12, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + /* MSFT100 */ + 'M', 0x00, 'S', 0x00, 'F', 0x00, 'T', 0x00, /* wcChar_7 */ + '1', 0x00, '0', 0x00, '0', 0x00, /* wcChar_7 */ + WCID_VENDOR_CODE, /* bVendorCode */ + 0x00, /* bReserved */ +}; + +__ALIGN_BEGIN const uint8_t WINUSB_WCIDDescriptor[40] __ALIGN_END = { + /////////////////////////////////////// + /// WCID descriptor + /////////////////////////////////////// + 0x28, 0x00, 0x00, 0x00, /* dwLength */ + 0x00, 0x01, /* bcdVersion */ + 0x04, 0x00, /* wIndex */ + 0x01, /* bCount */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bReserved_7 */ + + /////////////////////////////////////// + /// WCID function descriptor + /////////////////////////////////////// + ADB_INTF_NUM, /* bFirstInterfaceNumber */ + 0x01, /* bReserved */ + /* Compatible ID */ + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, /* cCID_8: WINUSB */ + /* */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* cSubCID_8 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bReserved_6 */ +}; + +__ALIGN_BEGIN const uint8_t WINUSB_IF0_WCIDProperties[142] __ALIGN_END = { + /////////////////////////////////////// + /// WCID property descriptor + /////////////////////////////////////// + 0x8e, 0x00, 0x00, 0x00, /* dwLength */ + 0x00, 0x01, /* bcdVersion */ + 0x05, 0x00, /* wIndex */ + 0x01, 0x00, /* wCount */ + + /////////////////////////////////////// + /// registry propter descriptor + /////////////////////////////////////// + 0x84, 0x00, 0x00, 0x00, /* dwSize */ + 0x01, 0x00, 0x00, 0x00, /* dwPropertyDataType */ + 0x28, 0x00, /* wPropertyNameLength */ + /* DeviceInterfaceGUID */ + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, /* wcName_20 */ + 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, /* wcName_20 */ + 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, /* wcName_20 */ + 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, /* wcName_20 */ + 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, /* wcName_20 */ + 0x4e, 0x00, 0x00, 0x00, /* dwPropertyDataLength */ + /* {1D4B2365-4749-48EA-B38A-7C6FDDDD7E26} */ + '{', 0x00, '1', 0x00, 'D', 0x00, '4', 0x00, /* wcData_39 */ + 'B', 0x00, '2', 0x00, '3', 0x00, '6', 0x00, /* wcData_39 */ + '5', 0x00, '-', 0x00, '4', 0x00, '7', 0x00, /* wcData_39 */ + '4', 0x00, '9', 0x00, '-', 0x00, '4', 0x00, /* wcData_39 */ + '8', 0x00, 'E', 0x00, 'A', 0x00, '-', 0x00, /* wcData_39 */ + 'B', 0x00, '3', 0x00, '8', 0x00, 'A', 0x00, /* wcData_39 */ + '-', 0x00, '7', 0x00, 'C', 0x00, '6', 0x00, /* wcData_39 */ + 'F', 0x00, 'D', 0x00, 'D', 0x00, 'D', 0x00, /* wcData_39 */ + 'D', 0x00, '7', 0x00, 'E', 0x00, '2', 0x00, /* wcData_39 */ + '6', 0x00, '}', 0x00, 0x00, 0x00, /* wcData_39 */ +}; + +const uint8_t *WINUSB_IFx_WCIDProperties[] = { + WINUSB_IF0_WCIDProperties, +}; + +struct usb_msosv1_descriptor msosv1_desc = { + .string = WCID_StringDescriptor_MSOS, + .vendor_code = WCID_VENDOR_CODE, + .compat_id = WINUSB_WCIDDescriptor, + .comp_id_property = WINUSB_IFx_WCIDProperties, +}; + +/*!< global descriptor */ +static const uint8_t adb_descriptor[] = { + USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0100, 0x01), + USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), + ADB_DESCRIPTOR_INIT(ADB_INTF_NUM, WINUSB_IN_EP, WINUSB_OUT_EP, WINUSB_MAX_MPS), + /////////////////////////////////////// + /// string0 descriptor + /////////////////////////////////////// + USB_LANGID_INIT(USBD_LANGID_STRING), + /////////////////////////////////////// + /// string1 descriptor + /////////////////////////////////////// + 0x14, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'U', 0x00, /* wcChar6 */ + 'S', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + /////////////////////////////////////// + /// string2 descriptor + /////////////////////////////////////// + 0x14, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'A', 0x00, /* wcChar6 */ + 'D', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + /////////////////////////////////////// + /// string3 descriptor + /////////////////////////////////////// + 0x1C, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'A', 0x00, /* wcChar6 */ + 'D', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + '2', 0x00, /* wcChar9 */ + '0', 0x00, /* wcChar10 */ + '2', 0x00, /* wcChar11 */ + '4', 0x00, /* wcChar12 */ +#ifdef CONFIG_USB_HS + /////////////////////////////////////// + /// device qualifier descriptor + /////////////////////////////////////// + 0x0a, + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, + 0x00, + 0x02, + 0x02, + 0x02, + 0x01, + 0x40, + 0x00, + 0x00, +#endif + 0x00 +}; + +static void usbd_event_handler(uint8_t busid, uint8_t event) +{ + switch (event) { + case USBD_EVENT_RESET: + break; + case USBD_EVENT_CONNECTED: + break; + case USBD_EVENT_DISCONNECTED: + break; + case USBD_EVENT_RESUME: + break; + case USBD_EVENT_SUSPEND: + break; + case USBD_EVENT_CONFIGURED: + + break; + case USBD_EVENT_SET_REMOTE_WAKEUP: + break; + case USBD_EVENT_CLR_REMOTE_WAKEUP: + break; + + default: + break; + } +} + +static struct usbd_interface intf0; + +extern int shell_init(bool need_login); +void cherryadb_init(uint8_t busid, uint32_t reg_base) +{ + /* default password is : 12345678 */ + /* shell_init() must be called in-task */ + if (0 != shell_init(false)) { + /* shell failed to be initialized */ + printf("Failed to initialize shell\r\n"); + for (;;) { + ; + } + } + + usbd_desc_register(busid, adb_descriptor); + usbd_add_interface(busid, usbd_adb_init_intf(busid, &intf0, WINUSB_IN_EP, WINUSB_OUT_EP)); + usbd_initialize(busid, reg_base, usbd_event_handler); +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/audio_v1_mic_multichan_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/audio_v1_mic_multichan_template.c index a80a221..56684e4 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/audio_v1_mic_multichan_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/audio_v1_mic_multichan_template.c @@ -144,7 +144,7 @@ const uint8_t audio_v1_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -212,7 +212,7 @@ struct audio_entity_info audio_entity_table[] = { .ep = AUDIO_IN_EP }, }; -void audio_v1_init(uint8_t busid, uint32_t reg_base) +void audio_v1_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, audio_v1_descriptor); usbd_add_interface(busid, usbd_audio_init_intf(busid, &intf0, 0x0100, audio_entity_table, 1)); diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/audio_v1_mic_speaker_multichan_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/audio_v1_mic_speaker_multichan_template.c index fbd53e3..4beccc6 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/audio_v1_mic_speaker_multichan_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/audio_v1_mic_speaker_multichan_template.c @@ -139,7 +139,7 @@ const uint8_t audio_v1_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -238,7 +238,7 @@ struct audio_entity_info audio_entity_table[] = { .ep = AUDIO_OUT_EP }, }; -void audio_v1_init(uint8_t busid, uint32_t reg_base) +void audio_v1_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, audio_v1_descriptor); usbd_add_interface(busid, usbd_audio_init_intf(busid, &intf0, 0x0100, audio_entity_table, 2)); diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_mic_multichan_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_mic_multichan_template.c index bf85762..c00fffa 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_mic_multichan_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_mic_multichan_template.c @@ -149,7 +149,7 @@ const uint8_t audio_v2_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -229,7 +229,7 @@ struct audio_entity_info audio_entity_table[] = { .ep = AUDIO_IN_EP }, }; -void audio_v2_init(uint8_t busid, uint32_t reg_base) +void audio_v2_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, audio_v2_descriptor); usbd_add_interface(busid, usbd_audio_init_intf(busid, &intf0, 0x0200, audio_entity_table, 2)); diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_mic_speaker_multichan_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_mic_speaker_multichan_template.c index e8afabe..0aebe57 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_mic_speaker_multichan_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_mic_speaker_multichan_template.c @@ -196,7 +196,7 @@ uint8_t audio_v2_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -346,7 +346,7 @@ struct audio_entity_info audio_entity_table[] = { .ep = AUDIO_IN_EP }, }; -void audio_v2_init(uint8_t busid, uint32_t reg_base) +void audio_v2_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, audio_v2_descriptor); usbd_add_interface(busid, usbd_audio_init_intf(busid, &intf0, 0x0200, audio_entity_table, 4)); diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_speaker_multichan_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_speaker_multichan_template.c index 3fc7a48..0abb677 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_speaker_multichan_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/audio_v2_speaker_multichan_template.c @@ -149,7 +149,7 @@ const uint8_t audio_v2_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -247,7 +247,7 @@ struct audio_entity_info audio_entity_table[] = { .ep = AUDIO_OUT_EP }, }; -void audio_v2_init(uint8_t busid, uint32_t reg_base) +void audio_v2_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, audio_v2_descriptor); usbd_add_interface(busid, usbd_audio_init_intf(busid, &intf0, 0x0200, audio_entity_table, 2)); diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2.c b/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2.c new file mode 100644 index 0000000..c4c6ee5 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2.c @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2024, sakumisu + * Copyright (c) 2024, Egahp + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "bootuf2.h" +#include "usbd_core.h" + +char file_INFO[] = { + "CherryUSB UF2 BOOT\r\n" + "Model: " CONFIG_PRODUCT "\r\n" + "Board-ID: " CONFIG_BOARD "\r\n" +}; + +const char file_IDEX[] = { + "\n" + "" + "" + "" + "" + "\n" +}; + +const char file_JOIN[] = { + "\n" + "" + "" + "" + "" + "\n" +}; + +const char file_ID__[12] = BOOTUF2_FAMILYID_ARRAY; + +static struct bootuf2_FILE files[] = { + [0] = { .Name = file_ID__, .Content = NULL, .FileSize = 0 }, + [1] = { .Name = "INFO_UF2TXT", .Content = file_INFO, .FileSize = sizeof(file_INFO) - 1 }, + [2] = { .Name = "INDEX HTM", .Content = file_IDEX, .FileSize = sizeof(file_IDEX) - 1 }, + [3] = { .Name = "JOIN HTM", .Content = file_JOIN, .FileSize = sizeof(file_JOIN) - 1 }, +}; + +struct bootuf2_data { + const struct bootuf2_DBR *const DBR; + struct bootuf2_STATE *const STATE; + uint8_t *const fbuff; + uint8_t *const erase; + size_t page_count; + uint8_t *const cache; + const size_t cache_size; + uint32_t cached_address; + size_t cached_bytes; +}; + +/*!< define DBRs */ +static const struct bootuf2_DBR bootuf2_DBR = { + .JMPInstruction = { 0xEB, 0x3C, 0x90 }, + .OEM = "UF2 UF2 ", + .BPB = { + .BytesPerSector = CONFIG_BOOTUF2_SECTOR_SIZE, + .SectorsPerCluster = CONFIG_BOOTUF2_SECTOR_PER_CLUSTER, + .ReservedSectors = CONFIG_BOOTUF2_SECTOR_RESERVED, + .NumberOfFAT = CONFIG_BOOTUF2_NUM_OF_FAT, + .RootEntries = CONFIG_BOOTUF2_ROOT_ENTRIES, + .Sectors = (BOOTUF2_SECTORS(0) > 0xFFFF) ? 0 : BOOTUF2_SECTORS(0), + .MediaDescriptor = 0xF8, + .SectorsPerFAT = BOOTUF2_SECTORS_PER_FAT(0), + .SectorsPerTrack = 1, + .Heads = 1, + .HiddenSectors = 0, + .SectorsOver32MB = (BOOTUF2_SECTORS(0) > 0xFFFF) ? BOOTUF2_SECTORS(0) : 0, + .BIOSDrive = 0x80, + .Reserved = 0, + .ExtendBootSignature = 0x29, + .VolumeSerialNumber = 0x00420042, + .VolumeLabel = "CHERRYUF2", + .FileSystem = "FAT16 ", + }, +}; + +/*!< define mask */ +static uint8_t __attribute__((aligned(4))) bootuf2_mask[BOOTUF2_BLOCKSMAX / 8 + 1] = { 0 }; + +/*!< define state */ +static struct bootuf2_STATE bootuf2_STATE = { + .NumberOfBlock = 0, + .NumberOfWritten = 0, + .Mask = bootuf2_mask, + .Enable = 1, +}; + +/*!< define flash cache */ +static uint8_t __attribute__((aligned(4))) bootuf2_disk_cache[CONFIG_BOOTUF2_CACHE_SIZE]; + +/*!< define flash buff */ +static uint8_t __attribute__((aligned(4))) bootuf2_disk_fbuff[256]; + +/*!< define erase flag buff */ +static uint8_t __attribute__((aligned(4))) bootuf2_disk_erase[BOOTUF2_DIVCEIL(CONFIG_BOOTUF2_PAGE_COUNTMAX, 8)]; + +/*!< define disk */ +static struct bootuf2_data bootuf2_disk = { + .DBR = &bootuf2_DBR, + .STATE = &bootuf2_STATE, + .fbuff = bootuf2_disk_fbuff, + .erase = bootuf2_disk_erase, + .cache = bootuf2_disk_cache, + .cache_size = sizeof(bootuf2_disk_cache), +}; + +static void fname_copy(char *dst, char const *src, uint16_t len) +{ + for (size_t i = 0; i < len; ++i) { + if (*src) + *dst++ = *src++; + else + *dst++ = ' '; + } +} + +static void fcalculate_cluster(struct bootuf2_data *ctx) +{ + /*!< init files cluster */ + uint16_t cluster_beg = 2; + for (int i = 0; i < ARRAY_SIZE(files); i++) { + files[i].ClusterBeg = cluster_beg; + files[i].ClusterEnd = -1 + cluster_beg + + BOOTUF2_DIVCEIL(files[i].FileSize, + ctx->DBR->BPB.BytesPerSector * + ctx->DBR->BPB.SectorsPerCluster); + cluster_beg = files[i].ClusterEnd + 1; + } +} + +static int ffind_by_cluster(uint32_t cluster) +{ + if (cluster >= 0xFFF0) { + return -1; + } + + for (uint32_t i = 0; i < ARRAY_SIZE(files); i++) { + if ((files[i].ClusterBeg <= cluster) && + (cluster <= files[i].ClusterEnd)) { + return i; + } + } + + return -1; +} + +static bool bootuf2block_check_writable(struct bootuf2_STATE *STATE, + struct bootuf2_BLOCK *uf2, uint32_t block_max) +{ + if (uf2->NumberOfBlock) { + if (uf2->BlockIndex < block_max) { + uint8_t mask = 1 << (uf2->BlockIndex % 8); + uint32_t pos = uf2->BlockIndex / 8; + + if ((STATE->Mask[pos] & mask) == 0) { + return true; + } + } + } + + return false; +} + +static void bootuf2block_state_update(struct bootuf2_STATE *STATE, + struct bootuf2_BLOCK *uf2, uint32_t block_max) +{ + if (uf2->NumberOfBlock) { + if (STATE->NumberOfBlock != uf2->NumberOfBlock) { + if ((uf2->NumberOfBlock >= BOOTUF2_BLOCKSMAX) || + STATE->NumberOfBlock) { + /*!< uf2 block only can be update once */ + /*!< this will cause never auto reboot */ + STATE->NumberOfBlock = 0xffffffff; + } else { + STATE->NumberOfBlock = uf2->NumberOfBlock; + } + } + + if (uf2->BlockIndex < block_max) { + uint8_t mask = 1 << (uf2->BlockIndex % 8); + uint32_t pos = uf2->BlockIndex / 8; + + if ((STATE->Mask[pos] & mask) == 0) { + STATE->Mask[pos] |= mask; + STATE->NumberOfWritten++; + } + } + } + + USB_LOG_DBG("UF2 block total %d written %d index %d\r\n", + uf2->NumberOfBlock, STATE->NumberOfWritten, uf2->BlockIndex); +} + +static bool bootuf2block_state_check(struct bootuf2_STATE *STATE) +{ + return (STATE->NumberOfWritten >= STATE->NumberOfBlock) && + STATE->NumberOfBlock; +} + +static int bootuf2_flash_flush(struct bootuf2_data *ctx) +{ + int err; + + if (ctx->cached_bytes == 0) { + return 0; + } + + err = bootuf2_flash_write(ctx->cached_address, ctx->cache, ctx->cached_bytes); + + if (err) { + USB_LOG_ERR("UF2 slot flash write error %d at offset %08lx len %d\r\n", + err, ctx->cached_address, ctx->cached_bytes); + return -1; + } + + ctx->cached_bytes = 0; + + return 0; +} + +int bootuf2_flash_write_internal(struct bootuf2_data *ctx, struct bootuf2_BLOCK *uf2) +{ + /*!< 1.cache not empty and address not continue */ + /*!< 2.cache full */ + if ((ctx->cached_bytes && ((ctx->cached_address + ctx->cached_bytes) != uf2->TargetAddress)) || + (ctx->cached_bytes == ctx->cache_size)) { + int err = bootuf2_flash_flush(ctx); + if (err) + return err; + } + + /*!< write len always is 256, cache_size always is a multiple of 256 */ + memcpy(ctx->cache + ctx->cached_bytes, uf2->Data, uf2->PayloadSize); + + ctx->cached_address = uf2->TargetAddress - ctx->cached_bytes; + ctx->cached_bytes += uf2->PayloadSize; + + return 0; +} + +void bootuf2_init(void) +{ + struct bootuf2_data *ctx; + + ctx = &bootuf2_disk; + + fcalculate_cluster(ctx); + + ctx->cached_bytes = 0; + ctx->cached_address = 0; +} + +int boot2uf2_read_sector(uint32_t start_sector, uint8_t *buff, uint32_t sector_count) +{ + struct bootuf2_data *ctx; + + ctx = &bootuf2_disk; + + while (sector_count) { + memset(buff, 0, ctx->DBR->BPB.BytesPerSector); + + uint32_t sector_relative = start_sector; + + /*!< DBR sector */ + if (start_sector == BOOTUF2_SECTOR_DBR_END) { + memcpy(buff, ctx->DBR, sizeof(struct bootuf2_DBR)); + buff[510] = 0x55; + buff[511] = 0xaa; + } + /*!< FAT sector */ + else if (start_sector < BOOTUF2_SECTOR_FAT_END(ctx->DBR)) { + uint16_t *buff16 = (uint16_t *)buff; + + sector_relative -= BOOTUF2_SECTOR_RSVD_END(ctx->DBR); + + /*!< Perform the same operation on all FAT tables */ + while (sector_relative >= ctx->DBR->BPB.SectorsPerFAT) { + sector_relative -= ctx->DBR->BPB.SectorsPerFAT; + } + + uint16_t cluster_unused = files[ARRAY_SIZE(files) - 1].ClusterEnd + 1; + uint16_t cluster_absolute_first = sector_relative * + BOOTUF2_FAT16_PER_SECTOR(ctx->DBR); + + /*!< cluster used link to chain, or unsed */ + for (uint16_t i = 0, cluster_absolute = cluster_absolute_first; + i < BOOTUF2_FAT16_PER_SECTOR(ctx->DBR); + i++, cluster_absolute++) { + if (cluster_absolute >= cluster_unused) + buff16[i] = 0; + else + buff16[i] = cluster_absolute + 1; + } + + /*!< cluster 0 and 1 */ + if (sector_relative == 0) { + buff[0] = ctx->DBR->BPB.MediaDescriptor; + buff[1] = 0xff; + buff16[1] = 0xffff; + } + + /*!< cluster end of file */ + for (uint32_t i = 0; i < ARRAY_SIZE(files); i++) { + uint16_t cluster_file_last = files[i].ClusterEnd; + + if (cluster_file_last >= cluster_absolute_first) { + uint16_t idx = cluster_file_last - cluster_absolute_first; + if (idx < BOOTUF2_FAT16_PER_SECTOR(ctx->DBR)) { + buff16[idx] = 0xffff; + } + } + } + } + /*!< root entries */ + else if (start_sector < BOOTUF2_SECTOR_ROOT_END(ctx->DBR)) { + sector_relative -= BOOTUF2_SECTOR_FAT_END(ctx->DBR); + + struct bootuf2_ENTRY *ent = (void *)buff; + int remain_entries = BOOTUF2_ENTRY_PER_SECTOR(ctx->DBR); + + uint32_t file_index_first; + + /*!< volume label entry */ + if (sector_relative == 0) { + fname_copy(ent->Name, (char const *)ctx->DBR->BPB.VolumeLabel, 11); + ent->Attribute = 0x28; + ent++; + remain_entries--; + file_index_first = 0; + } else { + /*!< -1 to account for volume label in first sector */ + file_index_first = sector_relative * BOOTUF2_ENTRY_PER_SECTOR(ctx->DBR) - 1; + } + + for (uint32_t idx = file_index_first; + (remain_entries > 0) && (idx < ARRAY_SIZE(files)); + idx++, ent++) { + const uint32_t cluster_beg = files[idx].ClusterBeg; + + const struct bootuf2_FILE *f = &files[idx]; + + if ((0 == f->FileSize) && + (0 != idx)) { + continue; + } + + fname_copy(ent->Name, f->Name, 11); + ent->Attribute = 0x05; + ent->CreateTimeTeenth = BOOTUF2_SECONDS_INT % 2 * 100; + ent->CreateTime = BOOTUF2_DOS_TIME; + ent->CreateDate = BOOTUF2_DOS_DATE; + ent->LastAccessDate = BOOTUF2_DOS_DATE; + ent->FirstClustH16 = cluster_beg >> 16; + ent->UpdateTime = BOOTUF2_DOS_TIME; + ent->UpdateDate = BOOTUF2_DOS_DATE; + ent->FirstClustL16 = cluster_beg & 0xffff; + ent->FileSize = f->FileSize; + } + } + /*!< data */ + else if (start_sector < BOOTUF2_SECTOR_DATA_END(ctx->DBR)) { + sector_relative -= BOOTUF2_SECTOR_ROOT_END(ctx->DBR); + + int fid = ffind_by_cluster(2 + sector_relative / ctx->DBR->BPB.SectorsPerCluster); + + if (fid >= 0) { + const struct bootuf2_FILE *f = &files[fid]; + + uint32_t sector_relative_file = + sector_relative - + (files[fid].ClusterBeg - 2) * ctx->DBR->BPB.SectorsPerCluster; + + size_t fcontent_offset = sector_relative_file * ctx->DBR->BPB.BytesPerSector; + size_t fcontent_length = f->FileSize; + + if (fcontent_length > fcontent_offset) { + const void *src = (void *)((uint8_t *)(f->Content) + fcontent_offset); + size_t copy_size = fcontent_length - fcontent_offset; + + if (copy_size > ctx->DBR->BPB.BytesPerSector) { + copy_size = ctx->DBR->BPB.BytesPerSector; + } + + memcpy(buff, src, copy_size); + } + } + } + /*!< unknown sector, ignore */ + + start_sector++; + sector_count--; + buff += ctx->DBR->BPB.BytesPerSector; + } + + return 0; +} + +int bootuf2_write_sector(uint32_t start_sector, const uint8_t *buff, uint32_t sector_count) +{ + struct bootuf2_data *ctx; + + ctx = &bootuf2_disk; + + while (sector_count) { + struct bootuf2_BLOCK *uf2 = (void *)buff; + + if (!((uf2->MagicStart0 == BOOTUF2_MAGIC_START0) && + (uf2->MagicStart1 == BOOTUF2_MAGIC_START1) && + (uf2->MagicEnd == BOOTUF2_MAGIC_END) && + (uf2->Flags & BOOTUF2_FLAG_FAMILID_PRESENT) && + !(uf2->Flags & BOOTUF2_FLAG_NOT_MAIN_FLASH))) { + goto next; + } + + if (uf2->FamilyID == CONFIG_BOOTUF2_FAMILYID) { + if (bootuf2block_check_writable(ctx->STATE, uf2, CONFIG_BOOTUF2_FLASHMAX)) { + bootuf2_flash_write_internal(ctx, uf2); + bootuf2block_state_update(ctx->STATE, uf2, CONFIG_BOOTUF2_FLASHMAX); + } else { + USB_LOG_DBG("UF2 block %d already written\r\n", + uf2->BlockIndex); + } + } else { + USB_LOG_DBG("UF2 block illegal id %08x\r\n", uf2->FamilyID); + } + + next: + start_sector++; + sector_count--; + buff += ctx->DBR->BPB.BytesPerSector; + } + + return 0; +} + +uint16_t bootuf2_get_sector_size(void) +{ + return bootuf2_disk.DBR->BPB.BytesPerSector; +} + +uint32_t bootuf2_get_sector_count(void) +{ + return bootuf2_disk.DBR->BPB.SectorsOver32MB + bootuf2_disk.DBR->BPB.Sectors; +} + +bool bootuf2_is_write_done(void) +{ + if (bootuf2block_state_check(bootuf2_disk.STATE)) { + bootuf2_flash_flush(&bootuf2_disk); + USB_LOG_DBG("UF2 update ok\r\n"); + return true; + } else { + return false; + } +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2.h b/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2.h new file mode 100644 index 0000000..ec31d3e --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2024, sakumisu + * Copyright (c) 2024, Egahp + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef BOOTUF2_H +#define BOOTUF2_H + +#include +#include +#include +#include +#include +#include + +#ifndef __PACKED +#define __PACKED __attribute__((packed)) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) \ + ((int)((sizeof(array) / sizeof((array)[0])))) +#endif + +struct bootuf2_BLOCK +{ + // 32 byte header + uint32_t MagicStart0; + uint32_t MagicStart1; + uint32_t Flags; + uint32_t TargetAddress; + uint32_t PayloadSize; + uint32_t BlockIndex; + uint32_t NumberOfBlock; + uint32_t FamilyID; // or file_size + uint8_t Data[476]; + uint32_t MagicEnd; +} __PACKED; +//BUILD_ASSERT(sizeof(struct bootuf2_BLOCK) == 512, "bootuf2_BLOCK not sector sized"); + +struct bootuf2_STATE +{ + uint32_t NumberOfBlock; + uint32_t NumberOfWritten; + uint8_t *const Mask; + uint8_t Enable; +}; + +struct bootuf2_DBR +{ + /*!< offset 0 */ + uint8_t JMPInstruction[3]; + /*!< offset 3 */ + uint8_t OEM[8]; + /*!< offset 11 */ + struct + { + uint16_t BytesPerSector; + uint8_t SectorsPerCluster; + uint16_t ReservedSectors; + uint8_t NumberOfFAT; + uint16_t RootEntries; + uint16_t Sectors; + uint8_t MediaDescriptor; + uint16_t SectorsPerFAT; + uint16_t SectorsPerTrack; + uint16_t Heads; + uint32_t HiddenSectors; + uint32_t SectorsOver32MB; + uint8_t BIOSDrive; + uint8_t Reserved; + uint8_t ExtendBootSignature; + uint32_t VolumeSerialNumber; + uint8_t VolumeLabel[11]; + uint8_t FileSystem[8]; + } __PACKED BPB; + /*!< offset 62 */ + /*!< BootLoader */ + /*!< offset 511 */ + /*!< 0x55 0xAA */ +} __PACKED; +//BUILD_ASSERT(sizeof(struct bootuf2_DBR) == 62, "bootuf2_DBR size must be 62 byte"); + +struct bootuf2_ENTRY +{ + char Name[11]; + uint8_t Attribute; + uint8_t NTReserved; + uint8_t CreateTimeTeenth; + uint16_t CreateTime; + uint16_t CreateDate; + uint16_t LastAccessDate; + uint16_t FirstClustH16; + uint16_t UpdateTime; + uint16_t UpdateDate; + uint16_t FirstClustL16; + uint32_t FileSize; +} __PACKED; +//BUILD_ASSERT(sizeof(struct bootuf2_ENTRY) == 32, "bootuf2_ENTRY size must be 32 byte"); + +struct bootuf2_FILE +{ + const char *const Name; + const void *const Content; + uint32_t FileSize; + uint16_t ClusterBeg; + uint16_t ClusterEnd; +}; + +#define BOOTUF2_DIVCEIL(_v, _d) (((_v) / (_d)) + ((_v) % (_d) ? 1 : 0)) + +#define BOOTUF2_MAGIC_START0 0x0A324655u +#define BOOTUF2_MAGIC_START1 0x9E5D5157u +#define BOOTUF2_MAGIC_SERIAL 0x251B18BDu +#define BOOTUF2_MAGIC_END 0x0AB16F30u + +#define BOOTUF2_FLAG_NOT_MAIN_FLASH 0x00000001u +#define BOOTUF2_FLAG_FILE_CONTAINER 0x00001000u +#define BOOTUF2_FLAG_FAMILID_PRESENT 0x00002000u +#define BOOTUF2_FLAG_MD5_PRESENT 0x00004000u + +#define BOOTUF2_CMD_READ 0 +#define BOOTUF2_CMD_SYNC 1 + +#define BOOTUF2_BLOCKSMAX (((CONFIG_BOOTUF2_FLASHMAX) / 256) + (((CONFIG_BOOTUF2_FLASHMAX) % 256) ? 1 : 0)) + +#define BOOTUF2_FAMILYID_POSNUM(n) (((CONFIG_BOOTUF2_FAMILYID) / (0x10000000 >> ((n) * 4))) % 0x10) +#define BOOTUF2_FAMILYID_ARRAY \ + { \ + ((BOOTUF2_FAMILYID_POSNUM(0) >= 10) ? BOOTUF2_FAMILYID_POSNUM(0) - 10 + 'A' : BOOTUF2_FAMILYID_POSNUM(0) + '0'), \ + ((BOOTUF2_FAMILYID_POSNUM(1) >= 10) ? BOOTUF2_FAMILYID_POSNUM(1) - 10 + 'A' : BOOTUF2_FAMILYID_POSNUM(1) + '0'), \ + ((BOOTUF2_FAMILYID_POSNUM(2) >= 10) ? BOOTUF2_FAMILYID_POSNUM(2) - 10 + 'A' : BOOTUF2_FAMILYID_POSNUM(2) + '0'), \ + ((BOOTUF2_FAMILYID_POSNUM(3) >= 10) ? BOOTUF2_FAMILYID_POSNUM(3) - 10 + 'A' : BOOTUF2_FAMILYID_POSNUM(3) + '0'), \ + ((BOOTUF2_FAMILYID_POSNUM(4) >= 10) ? BOOTUF2_FAMILYID_POSNUM(4) - 10 + 'A' : BOOTUF2_FAMILYID_POSNUM(4) + '0'), \ + ((BOOTUF2_FAMILYID_POSNUM(5) >= 10) ? BOOTUF2_FAMILYID_POSNUM(5) - 10 + 'A' : BOOTUF2_FAMILYID_POSNUM(5) + '0'), \ + ((BOOTUF2_FAMILYID_POSNUM(6) >= 10) ? BOOTUF2_FAMILYID_POSNUM(6) - 10 + 'A' : BOOTUF2_FAMILYID_POSNUM(6) + '0'), \ + ((BOOTUF2_FAMILYID_POSNUM(7) >= 10) ? BOOTUF2_FAMILYID_POSNUM(7) - 10 + 'A' : BOOTUF2_FAMILYID_POSNUM(7) + '0'), \ + ('I'), \ + ('D'), \ + (' '), \ + ('\0'), \ + }; + +#define BOOTUF2_FAT16_PER_SECTOR(pDBR) (pDBR->BPB.BytesPerSector / 2) +#define BOOTUF2_ENTRY_PER_SECTOR(pDBR) (pDBR->BPB.BytesPerSector / sizeof(struct bootuf2_ENTRY)) +#define BOOTUF2_CLUSTERSMAX (0xFFF0 - 2) +#define BOOTUF2_SECTOR_DBR_END (0) +#define BOOTUF2_SECTOR_RSVD_END(pDBR) BOOTUF2_SECTOR_DBR_END + (pDBR->BPB.ReservedSectors) +#define BOOTUF2_SECTOR_FAT_END(pDBR) BOOTUF2_SECTOR_RSVD_END(pDBR) + (pDBR->BPB.SectorsPerFAT * pDBR->BPB.NumberOfFAT) +#define BOOTUF2_SECTOR_ROOT_END(pDBR) BOOTUF2_SECTOR_FAT_END(pDBR) + (pDBR->BPB.RootEntries / (pDBR->BPB.BytesPerSector / sizeof(struct bootuf2_ENTRY))) +#define BOOTUF2_SECTOR_DATA_END(pDBR) (pDBR->BPB.Sectors + pDBR->BPB.SectorsOver32MB) + +#define BOOTUF2_SECTORS_PER_FAT(n) \ + BOOTUF2_DIVCEIL(BOOTUF2_CLUSTERSMAX, (CONFIG_BOOTUF2_SECTOR_SIZE / 2)) +#define BOOTUF2_SECTORS_FOR_ENTRIES(n) \ + (CONFIG_BOOTUF2_ROOT_ENTRIES / (CONFIG_BOOTUF2_SECTOR_SIZE / sizeof(struct bootuf2_ENTRY))) +#define BOOTUF2_SECTORS(n) \ + (CONFIG_BOOTUF2_SECTOR_RESERVED + \ + CONFIG_BOOTUF2_NUM_OF_FAT * BOOTUF2_SECTORS_PER_FAT(n) + \ + BOOTUF2_SECTORS_FOR_ENTRIES(n) + \ + BOOTUF2_CLUSTERSMAX * CONFIG_BOOTUF2_SECTOR_PER_CLUSTER) + +#define BOOTUF2_YEAR_INT ( \ + (__DATE__[7u] - '0') * 1000u + \ + (__DATE__[8u] - '0') * 100u + \ + (__DATE__[9u] - '0') * 10u + \ + (__DATE__[10u] - '0') * 1u) + +#define BOOTUF2_MONTH_INT ( \ + (__DATE__[2u] == 'n' && __DATE__[1u] == 'a') ? 1u /*Jan*/ \ + : (__DATE__[2u] == 'b') ? 2u /*Feb*/ \ + : (__DATE__[2u] == 'r' && __DATE__[1u] == 'a') ? 3u /*Mar*/ \ + : (__DATE__[2u] == 'r') ? 4u /*Apr*/ \ + : (__DATE__[2u] == 'y') ? 5u /*May*/ \ + : (__DATE__[2u] == 'n') ? 6u /*Jun*/ \ + : (__DATE__[2u] == 'l') ? 7u /*Jul*/ \ + : (__DATE__[2u] == 'g') ? 8u /*Aug*/ \ + : (__DATE__[2u] == 'p') ? 9u /*Sep*/ \ + : (__DATE__[2u] == 't') ? 10u /*Oct*/ \ + : (__DATE__[2u] == 'v') ? 11u /*Nov*/ \ + : 12u /*Dec*/) + +#define BOOTUF2_DAY_INT ( \ + (__DATE__[4u] == ' ' ? 0 : __DATE__[4u] - '0') * 10u + \ + (__DATE__[5u] - '0')) + +#define BOOTUF2_HOUR_INT ( \ + (__TIME__[0u] == '?' ? 0 : __TIME__[0u] - '0') * 10u + (__TIME__[1u] == '?' ? 0 : __TIME__[1u] - '0')) + +#define BOOTUF2_MINUTE_INT ( \ + (__TIME__[3u] == '?' ? 0 : __TIME__[3u] - '0') * 10u + (__TIME__[4u] == '?' ? 0 : __TIME__[4u] - '0')) + +#define BOOTUF2_SECONDS_INT ( \ + (__TIME__[6u] == '?' ? 0 : __TIME__[6u] - '0') * 10u + (__TIME__[7u] == '?' ? 0 : __TIME__[7u] - '0')) + +#define BOOTUF2_DOS_DATE ( \ + ((BOOTUF2_YEAR_INT - 1980u) << 9u) | \ + (BOOTUF2_MONTH_INT << 5u) | \ + (BOOTUF2_DAY_INT << 0u)) + +#define BOOTUF2_DOS_TIME ( \ + (BOOTUF2_HOUR_INT << 11u) | \ + (BOOTUF2_MINUTE_INT << 5u) | \ + (BOOTUF2_SECONDS_INT << 0u)) + +void bootuf2_init(void); +int boot2uf2_read_sector(uint32_t start_sector, uint8_t *buff, uint32_t sector_count); +int bootuf2_write_sector(uint32_t start_sector, const uint8_t *buff, uint32_t sector_count); +uint16_t bootuf2_get_sector_size(void); +uint32_t bootuf2_get_sector_count(void); + +bool bootuf2_is_write_done(void); + +void boot2uf2_flash_init(void); +int bootuf2_flash_write(uint32_t address, const uint8_t *data, size_t size); + +#endif /* BOOTUF2_H */ diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2_config.h b/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2_config.h new file mode 100644 index 0000000..307d09d --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/bootuf2_config.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef BOOTUF2_CONFIG_H +#define BOOTUF2_CONFIG_H + +#define CONFIG_PRODUCT "CherryUSB" +#define CONFIG_BOARD "CherryUSB BOARD" +#define CONFIG_BOOTUF2_INDEX_URL "https://github.com/cherry-embedded" +#define CONFIG_BOOTUF2_JOIN_URL "http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GyH2M5XfWTHQzmZis4ClpgvfdObPrvtk&authKey=LmcLhfno%2BiW51wmgVC%2F8WoYwUXqiclzWDHMU1Jy1d6S8cECJ4Q7bfJ%2FTe67RLakI&noverify=0&group_code=642693751" + +#define CONFIG_BOOTUF2_CACHE_SIZE 4096 +#define CONFIG_BOOTUF2_SECTOR_SIZE 512 +#define CONFIG_BOOTUF2_SECTOR_PER_CLUSTER 2 +#define CONFIG_BOOTUF2_SECTOR_RESERVED 1 +#define CONFIG_BOOTUF2_NUM_OF_FAT 2 +#define CONFIG_BOOTUF2_ROOT_ENTRIES 64 + +#define CONFIG_BOOTUF2_FAMILYID 0xFFFFFFFF +#define CONFIG_BOOTUF2_FLASHMAX 0x800000 +#define CONFIG_BOOTUF2_PAGE_COUNTMAX 1024 + +#endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/cherryuf2.png b/rt-thread/components/drivers/usb/cherryusb/demo/bootuf2/cherryuf2.png new file mode 100644 index 0000000000000000000000000000000000000000..aff7ee4df875678cdcdf79d2485fc35fdaabffee GIT binary patch literal 19847 zcmdpeWmH^Emo9{a1PBlyxH~isAvlE4xVyUtcZURb*Wm8%9$bUFyEP7tH{4F%@0*#m z=FY79`_>N@tGcSrsl9hqJ^R_aPKcb0C^8}*A`A=+vbY#f0R{$69{T^~J9y~dvRzph z7#I>5aiE}*Yud>wlB*IpUEuka;`_n3!;)mAjU_C#Fg&R(cACDWf)Bo7UoO68O5d|1 z*N=}QfG?Br8kfPOt9P|iM#l|E(!`&iXEm^jOIfOlRRL;D2$TSo>^U+DU|aTg*tnFJ z+bcc?ld&`|2NRj(mG#Qcv7^mirhB8Kw_Y7d7lizm=I`>n&b!`nf@c)pw&DOGk^9_~ zJq||KTMn7to>Czq8R1=wFlc|uq;2;HVc? t3LZ&l3O_ws*hfJDr|zcpxXzy7a{ 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 I&MBb@0Mz5reE CURSOR_WIDTH) { + step_x_y++; + step_x_y = step_x_y % 4; + move_cnt = 0; + } + switch (step_x_y) { + case 0: { + y = 0; + x = CURSOR_STEP; + + } break; + + case 1: { + x = 0; + y = CURSOR_STEP; + + } break; + + case 2: { + y = 0; + x = (int8_t)(-CURSOR_STEP); + + } break; + + case 3: { + x = 0; + y = (int8_t)(-CURSOR_STEP); + + } break; + } + + buf[0] = 0; + buf[1] = x; + buf[2] = y; + buf[3] = 0; +} + +/* https://cps-check.com/cn/polling-rate-check */ void hid_mouse_test(uint8_t busid) { + if(usb_device_is_configured(busid) == false) { + return; + } + int counter = 0; while (counter < 1000) { - /*!< move mouse pointer */ - mouse_cfg.x += 40; - mouse_cfg.y += 0; - - int ret = usbd_ep_start_write(busid, HID_INT_EP, (uint8_t *)&mouse_cfg, 4); - if (ret < 0) { - return; - } + draw_circle((uint8_t *)&mouse_cfg); hid_state = HID_STATE_BUSY; + usbd_ep_start_write(busid, HID_INT_EP, (uint8_t *)&mouse_cfg, 4); while (hid_state == HID_STATE_BUSY) { } diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/hid_remote_wakeup_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/hid_remote_wakeup_template.c new file mode 100644 index 0000000..786c324 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/demo/hid_remote_wakeup_template.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbd_core.h" +#include "usbd_hid.h" + +/*!< endpoint address */ +#define HID_INT_EP 0x81 +#define HID_INT_EP_SIZE 4 +#define HID_INT_EP_INTERVAL 1 + +#define USBD_VID 0xffff +#define USBD_PID 0xffff +#define USBD_MAX_POWER 100 +#define USBD_LANGID_STRING 1033 + +/*!< config descriptor size */ +#define USB_HID_CONFIG_DESC_SIZ 34 +/*!< report descriptor size */ +#define HID_MOUSE_REPORT_DESC_SIZE 74 + +/*!< global descriptor */ +const uint8_t hid_descriptor[] = { + USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0002, 0x01), + USB_CONFIG_DESCRIPTOR_INIT(USB_HID_CONFIG_DESC_SIZ, 0x01, 0x01, USB_CONFIG_REMOTE_WAKEUP | USB_CONFIG_SELF_POWERED, USBD_MAX_POWER), + + /************** Descriptor of Joystick Mouse interface ****************/ + /* 09 */ + 0x09, /* bLength: Interface Descriptor size */ + USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType: Interface descriptor type */ + 0x00, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x01, /* bNumEndpoints */ + 0x03, /* bInterfaceClass: HID */ + 0x01, /* bInterfaceSubClass : 1=BOOT, 0=no boot */ + 0x02, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */ + 0, /* iInterface: Index of string descriptor */ + /******************** Descriptor of Joystick Mouse HID ********************/ + /* 18 */ + 0x09, /* bLength: HID Descriptor size */ + HID_DESCRIPTOR_TYPE_HID, /* bDescriptorType: HID */ + 0x11, /* bcdHID: HID Class Spec release number */ + 0x01, + 0x00, /* bCountryCode: Hardware target country */ + 0x01, /* bNumDescriptors: Number of HID class descriptors to follow */ + 0x22, /* bDescriptorType */ + HID_MOUSE_REPORT_DESC_SIZE, /* wItemLength: Total length of Report descriptor */ + 0x00, + /******************** Descriptor of Mouse endpoint ********************/ + /* 27 */ + 0x07, /* bLength: Endpoint Descriptor size */ + USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType: */ + HID_INT_EP, /* bEndpointAddress: Endpoint Address (IN) */ + 0x03, /* bmAttributes: Interrupt endpoint */ + HID_INT_EP_SIZE, /* wMaxPacketSize: 4 Byte max */ + 0x00, + HID_INT_EP_INTERVAL, /* bInterval: Polling Interval */ + /* 34 */ + /////////////////////////////////////// + /// string0 descriptor + /////////////////////////////////////// + USB_LANGID_INIT(USBD_LANGID_STRING), + /////////////////////////////////////// + /// string1 descriptor + /////////////////////////////////////// + 0x14, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'U', 0x00, /* wcChar6 */ + 'S', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + /////////////////////////////////////// + /// string2 descriptor + /////////////////////////////////////// + 0x26, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'U', 0x00, /* wcChar6 */ + 'S', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + ' ', 0x00, /* wcChar9 */ + 'H', 0x00, /* wcChar10 */ + 'I', 0x00, /* wcChar11 */ + 'D', 0x00, /* wcChar12 */ + ' ', 0x00, /* wcChar13 */ + 'D', 0x00, /* wcChar14 */ + 'E', 0x00, /* wcChar15 */ + 'M', 0x00, /* wcChar16 */ + 'O', 0x00, /* wcChar17 */ + /////////////////////////////////////// + /// string3 descriptor + /////////////////////////////////////// + 0x16, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + '2', 0x00, /* wcChar0 */ + '0', 0x00, /* wcChar1 */ + '2', 0x00, /* wcChar2 */ + '2', 0x00, /* wcChar3 */ + '1', 0x00, /* wcChar4 */ + '2', 0x00, /* wcChar5 */ + '3', 0x00, /* wcChar6 */ + '4', 0x00, /* wcChar7 */ + '5', 0x00, /* wcChar8 */ + '6', 0x00, /* wcChar9 */ +#ifdef CONFIG_USB_HS + /////////////////////////////////////// + /// device qualifier descriptor + /////////////////////////////////////// + 0x0a, + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, +#endif + 0x00 +}; + +/*!< hid mouse report descriptor */ +static const uint8_t hid_mouse_report_desc[HID_MOUSE_REPORT_DESC_SIZE] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xA1, 0x01, // COLLECTION (Application) + 0x09, 0x01, // USAGE (Pointer) + + 0xA1, 0x00, // COLLECTION (Physical) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x03, // USAGE_MAXIMUM (Button 3) + + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x95, 0x03, // REPORT_COUNT (3) + 0x75, 0x01, // REPORT_SIZE (1) + + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x05, // REPORT_SIZE (5) + 0x81, 0x01, // INPUT (Cnst,Var,Abs) + + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x09, 0x38, + + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7F, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x03, // REPORT_COUNT (2) + + 0x81, 0x06, // INPUT (Data,Var,Rel) + 0xC0, 0x09, + 0x3c, 0x05, + 0xff, 0x09, + + 0x01, 0x15, + 0x00, 0x25, + 0x01, 0x75, + 0x01, 0x95, + + 0x02, 0xb1, + 0x22, 0x75, + 0x06, 0x95, + 0x01, 0xb1, + + 0x01, 0xc0 // END_COLLECTION +}; + +/*!< mouse report struct */ +struct hid_mouse { + uint8_t buttons; + int8_t x; + int8_t y; + int8_t wheel; +}; + +/*!< mouse report */ +static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX struct hid_mouse mouse_cfg; + +#define HID_STATE_IDLE 0 +#define HID_STATE_BUSY 1 + +/*!< hid state ! Data can be sent only when state is idle */ +static volatile uint8_t hid_state = HID_STATE_IDLE; + +static void usbd_event_handler(uint8_t busid, uint8_t event) +{ + switch (event) { + case USBD_EVENT_RESET: + break; + case USBD_EVENT_CONNECTED: + break; + case USBD_EVENT_DISCONNECTED: + break; + case USBD_EVENT_RESUME: + break; + case USBD_EVENT_SUSPEND: + break; + case USBD_EVENT_CONFIGURED: + hid_state = HID_STATE_IDLE; + break; + case USBD_EVENT_SET_REMOTE_WAKEUP: + break; + case USBD_EVENT_CLR_REMOTE_WAKEUP: + break; + + default: + break; + } +} + +/* function ------------------------------------------------------------------*/ +static void usbd_hid_int_callback(uint8_t busid, uint8_t ep, uint32_t nbytes) +{ + hid_state = HID_STATE_IDLE; +} + +/*!< endpoint call back */ +static struct usbd_endpoint hid_in_ep = { + .ep_cb = usbd_hid_int_callback, + .ep_addr = HID_INT_EP +}; + +static struct usbd_interface intf0; + +void hid_mouse_init(uint8_t busid, uintptr_t reg_base) +{ + usbd_desc_register(busid, hid_descriptor); + usbd_add_interface(busid, usbd_hid_init_intf(busid, &intf0, hid_mouse_report_desc, HID_MOUSE_REPORT_DESC_SIZE)); + usbd_add_endpoint(busid, &hid_in_ep); + + usbd_initialize(busid, reg_base, usbd_event_handler); + + /*!< init mouse report data */ + mouse_cfg.buttons = 0; + mouse_cfg.wheel = 0; + mouse_cfg.x = 0; + mouse_cfg.y = 0; +} + +#define CURSOR_STEP 2U +#define CURSOR_WIDTH 20U + +void draw_circle(uint8_t *buf) +{ + static int32_t move_cnt = 0; + static uint8_t step_x_y = 0; + static int8_t x = 0, y = 0; + + move_cnt++; + if (move_cnt > CURSOR_WIDTH) { + step_x_y++; + step_x_y = step_x_y % 4; + move_cnt = 0; + } + switch (step_x_y) { + case 0: { + y = 0; + x = CURSOR_STEP; + + } break; + + case 1: { + x = 0; + y = CURSOR_STEP; + + } break; + + case 2: { + y = 0; + x = (int8_t)(-CURSOR_STEP); + + } break; + + case 3: { + x = 0; + y = (int8_t)(-CURSOR_STEP); + + } break; + } + + buf[0] = 0; + buf[1] = x; + buf[2] = y; + buf[3] = 0; +} + +/* https://cps-check.com/cn/polling-rate-check */ +void hid_mouse_test(uint8_t busid) +{ + static uint32_t count = 1000; + int ret; + + if(usb_device_is_configured(busid) == false) { + return; + } + + // if (gpio_read_pin(GPIO_PIN) == 1) { + // ret = usbd_send_remote_wakeup(busid); + // if (ret < 0) { + // return; + // } + // count = 5000; + // } + + while (count) { + draw_circle((uint8_t *)&mouse_cfg); + hid_state = HID_STATE_BUSY; + usbd_ep_start_write(busid, HID_INT_EP, (uint8_t *)&mouse_cfg, 4); + while (hid_state == HID_STATE_BUSY) { + } + + count--; + } +} diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/midi_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/midi_template.c index d09a943..aa3b2ad 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/midi_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/midi_template.c @@ -145,7 +145,7 @@ const uint8_t midi_descriptor[] = { 0x02, 0x01, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -202,7 +202,7 @@ struct usbd_endpoint midi_in_ep = { .ep_cb = usbd_midi_bulk_in }; -void midi_init(uint8_t busid, uint32_t reg_base) +void midi_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, midi_descriptor); usbd_add_interface(busid, &intf0); diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/msc_ram_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/msc_ram_template.c index 933cd88..9021483 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/msc_ram_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/msc_ram_template.c @@ -94,7 +94,7 @@ const uint8_t msc_ram_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -154,12 +154,19 @@ int usbd_msc_sector_write(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t * return 0; } -struct usbd_interface intf0; +static struct usbd_interface intf0; -void msc_ram_init(uint8_t busid, uint32_t reg_base) +void msc_ram_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, msc_ram_descriptor); usbd_add_interface(busid, usbd_msc_init_intf(busid, &intf0, MSC_OUT_EP, MSC_IN_EP)); usbd_initialize(busid, reg_base, usbd_event_handler); } + +#if defined(CONFIG_USBDEV_MSC_POLLING) +void msc_ram_polling(uint8_t busid) +{ + usbd_msc_polling(busid); +} +#endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/msc_storage_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/msc_storage_template.c index a335164..047bad8 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/msc_storage_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/msc_storage_template.c @@ -97,7 +97,7 @@ const uint8_t msc_storage_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -154,7 +154,7 @@ int usbd_msc_sector_write(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t * return 0; } -void msc_storage_init(uint8_t busid, uint32_t reg_base) +void msc_storage_init(uint8_t busid, uintptr_t reg_base) { rt_err_t res; diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/usb_host.c b/rt-thread/components/drivers/usb/cherryusb/demo/usb_host.c index 8c8ac22..817103d 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/usb_host.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/usb_host.c @@ -120,7 +120,7 @@ void usbh_hid_callback(void *arg, int nbytes) struct usbh_hid *hid_class = (struct usbh_hid *)arg; if (nbytes > 0) { - for (size_t i = 0; i < nbytes; i++) { + for (int i = 0; i < nbytes; i++) { USB_LOG_RAW("0x%02x ", hid_buffer[i]); } USB_LOG_RAW("nbytes:%d\r\n", nbytes); @@ -254,38 +254,38 @@ delete: } #endif +#if TEST_USBH_CDC_ACM void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class) { -#if TEST_USBH_CDC_ACM usb_osal_thread_create("usbh_cdc", 2048, CONFIG_USBHOST_PSC_PRIO + 1, usbh_cdc_acm_thread, cdc_acm_class); -#endif } void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class) { } +#endif +#if TEST_USBH_HID void usbh_hid_run(struct usbh_hid *hid_class) { -#if TEST_USBH_HID usb_osal_thread_create("usbh_hid", 2048, CONFIG_USBHOST_PSC_PRIO + 1, usbh_hid_thread, hid_class); -#endif } void usbh_hid_stop(struct usbh_hid *hid_class) { } +#endif +#if TEST_USBH_MSC void usbh_msc_run(struct usbh_msc *msc_class) { -#if TEST_USBH_MSC usb_osal_thread_create("usbh_msc", 2048, CONFIG_USBHOST_PSC_PRIO + 1, usbh_msc_thread, msc_class); -#endif } void usbh_msc_stop(struct usbh_msc *msc_class) { } +#endif #if TEST_USBH_AUDIO #error "commercial charge" @@ -294,3 +294,26 @@ void usbh_msc_stop(struct usbh_msc *msc_class) #if TEST_USBH_VIDEO #error "commercial charge" #endif + +#if 0 +#include "usbh_aoa.h" + +static struct aoa_string_info deviceinfo = { + .acc_manufacturer = "CherryUSB", + .acc_model = "CherryUSB", + .acc_description = "Android Open Accessory CherryUSB", + .acc_version = "1.0", + .acc_uri = "http://developer.android.com/tools/adk/index.html", + .acc_serial = "CherryUSB" +}; + +int aoa_switch(int argc, char **argv) +{ + struct usbh_hubport *hport = usbh_find_hubport(0, 1, 1); + + usbh_aoa_switch(hport, &deviceinfo); + return 0; +} + +SHELL_CMD_EXPORT_ALIAS(aoa_switch, aoa_switch, aoa_switch); +#endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/video_audiov1_hid_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/video_audiov1_hid_template.c index 97f1743..6a41162 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/video_audiov1_hid_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/video_audiov1_hid_template.c @@ -9,6 +9,8 @@ #include "usbd_hid.h" #include "cherryusb_mjpeg.h" +#define MAX_PACKETS_IN_ONE_TRANSFER 1 + #define VIDEO_IN_EP 0x81 #define VIDEO_INT_EP 0x86 @@ -224,7 +226,7 @@ const uint8_t video_audio_hid_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -267,7 +269,7 @@ static const uint8_t hid_keyboard_report_desc[HID_KEYBOARD_REPORT_DESC_SIZE] = { USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t audio_read_buffer[AUDIO_OUT_PACKET]; USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t audio_write_buffer[AUDIO_IN_PACKET]; -USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t video_packet_buffer[40 * 1024]; +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t video_packet_buffer[2][MAX_PACKETS_IN_ONE_TRANSFER * MAX_PAYLOAD_SIZE]; volatile bool video_tx_flag = 0; volatile bool audio_tx_flag = 0; @@ -331,8 +333,10 @@ void usbd_video_close(uint8_t busid, uint8_t intf) void usbd_video_iso_callback(uint8_t busid, uint8_t ep, uint32_t nbytes) { - //USB_LOG_RAW("actual in len:%d\r\n", nbytes); - video_iso_tx_busy = false; + if (usbd_video_stream_split_transfer(busid, ep)) { + /* one frame has done */ + video_iso_tx_busy = false; + } } static struct usbd_endpoint video_in_ep = { @@ -416,7 +420,7 @@ struct audio_entity_info audio_entity_table[] = { .ep = AUDIO_OUT_EP }, }; -void composite_init(uint8_t busid, uint32_t reg_base) +void composite_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, video_audio_hid_descriptor); usbd_add_interface(busid, usbd_video_init_intf(busid, &intf0, INTERVAL, MAX_FRAME_SIZE, MAX_PAYLOAD_SIZE)); @@ -458,56 +462,30 @@ void hid_keyboard_test(uint8_t busid) { const uint8_t sendbuffer[8] = { 0x00, 0x00, HID_KBD_USAGE_A, 0x00, 0x00, 0x00, 0x00, 0x00 }; - memcpy(hid_write_buffer, sendbuffer, 8); - int ret = usbd_ep_start_write(busid, HID_INT_EP, hid_write_buffer, 8); - if (ret < 0) { + if(usb_device_is_configured(busid) == false) { return; } + + memcpy(hid_write_buffer, sendbuffer, 8); hid_state = HID_STATE_BUSY; + usbd_ep_start_write(busid, HID_INT_EP, hid_write_buffer, 8); while (hid_state == HID_STATE_BUSY) { } } void video_test(uint8_t busid) { - uint32_t out_len; - uint32_t packets; + memset(video_packet_buffer, 0, sizeof(video_packet_buffer)); - (void)packets; - memset(video_packet_buffer, 0, 40 * 1024); while (1) { if (video_tx_flag) { - packets = usbd_video_payload_fill(busid, (uint8_t *)cherryusb_mjpeg, sizeof(cherryusb_mjpeg), video_packet_buffer, &out_len); -#if 1 video_iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, video_packet_buffer, out_len); + usbd_video_stream_start_write(busid, VIDEO_IN_EP, &video_packet_buffer[0][0], &video_packet_buffer[1][0], MAX_PACKETS_IN_ONE_TRANSFER * MAX_PAYLOAD_SIZE, (uint8_t *)cherryusb_mjpeg, sizeof(cherryusb_mjpeg)); while (video_iso_tx_busy) { if (video_tx_flag == 0) { break; } } -#else - /* dwc2 must use this method */ - for (uint32_t i = 0; i < packets; i++) { - if (i == (packets - 1)) { - video_iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, &video_packet_buffer[i * MAX_PAYLOAD_SIZE], out_len - (packets - 1) * MAX_PAYLOAD_SIZE); - while (video_iso_tx_busy) { - if (video_tx_flag == 0) { - break; - } - } - } else { - video_iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, &video_packet_buffer[i * MAX_PAYLOAD_SIZE], MAX_PAYLOAD_SIZE); - while (video_iso_tx_busy) { - if (video_tx_flag == 0) { - break; - } - } - } - } -#endif } } } \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/video_static_h264_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/video_static_h264_template.c index adfb879..e325964 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/video_static_h264_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/video_static_h264_template.c @@ -7,6 +7,8 @@ #include "usbd_video.h" #include "cherryusb_h264.h" +#define MAX_PACKETS_IN_ONE_TRANSFER 1 + #define VIDEO_IN_EP 0x81 #define VIDEO_INT_EP 0x83 @@ -129,7 +131,7 @@ const uint8_t video_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -180,8 +182,10 @@ void usbd_video_close(uint8_t busid, uint8_t intf) void usbd_video_iso_callback(uint8_t busid, uint8_t ep, uint32_t nbytes) { - //USB_LOG_RAW("actual in len:%d\r\n", nbytes); - iso_tx_busy = false; + if (usbd_video_stream_split_transfer(busid, ep)) { + /* one frame has done */ + iso_tx_busy = false; + } } static struct usbd_endpoint video_in_ep = { @@ -192,7 +196,7 @@ static struct usbd_endpoint video_in_ep = { struct usbd_interface intf0; struct usbd_interface intf1; -void video_init(uint8_t busid, uint32_t reg_base) +void video_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, video_descriptor); usbd_add_interface(busid, usbd_video_init_intf(busid, &intf0, INTERVAL, MAX_FRAME_SIZE, MAX_PAYLOAD_SIZE)); @@ -202,7 +206,7 @@ void video_init(uint8_t busid, uint32_t reg_base) usbd_initialize(busid, reg_base, usbd_event_handler); } -USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t packet_buffer[40 * 1024]; +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t packet_buffer[2][40 * 1024]; void video_test(uint8_t busid) { @@ -210,40 +214,16 @@ void video_test(uint8_t busid) uint32_t packets; (void)packets; - memset(packet_buffer, 0, 40 * 1024); + memset(packet_buffer, 0, sizeof(packet_buffer)); while (1) { if (tx_flag) { - packets = usbd_video_payload_fill(busid, (uint8_t *)cherryusb_h264, sizeof(cherryusb_h264), packet_buffer, &out_len); -#if 1 iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, packet_buffer, out_len); + usbd_video_stream_start_write(busid, VIDEO_IN_EP, &packet_buffer[0][0], &packet_buffer[1][0], MAX_PACKETS_IN_ONE_TRANSFER * MAX_PAYLOAD_SIZE, (uint8_t *)cherryusb_h264, sizeof(cherryusb_h264)); while (iso_tx_busy) { if (tx_flag == 0) { break; } } -#else - /* dwc2 must use this method */ - for (uint32_t i = 0; i < packets; i++) { - if (i == (packets - 1)) { - iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, &packet_buffer[i * MAX_PAYLOAD_SIZE], out_len - (packets - 1) * MAX_PAYLOAD_SIZE); - while (iso_tx_busy) { - if (tx_flag == 0) { - break; - } - } - } else { - iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, &packet_buffer[i * MAX_PAYLOAD_SIZE], MAX_PAYLOAD_SIZE); - while (iso_tx_busy) { - if (tx_flag == 0) { - break; - } - } - } - } -#endif } } } \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/video_static_mjpeg_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/video_static_mjpeg_template.c index 85b659d..1d2ae01 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/video_static_mjpeg_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/video_static_mjpeg_template.c @@ -7,6 +7,8 @@ #include "usbd_video.h" #include "cherryusb_mjpeg.h" +#define MAX_PACKETS_IN_ONE_TRANSFER 1 + #define VIDEO_IN_EP 0x81 #define VIDEO_INT_EP 0x83 @@ -34,7 +36,7 @@ #define MAX_BIT_RATE (unsigned long)(WIDTH * HEIGHT * 16 * CAM_FPS) #define MAX_FRAME_SIZE (unsigned long)(WIDTH * HEIGHT * 2) -#define VS_HEADER_SIZ (unsigned int)(VIDEO_SIZEOF_VS_INPUT_HEADER_DESC(1,1) + VIDEO_SIZEOF_VS_FORMAT_MJPEG_DESC + VIDEO_SIZEOF_VS_FRAME_MJPEG_DESC(1)) +#define VS_HEADER_SIZ (unsigned int)(VIDEO_SIZEOF_VS_INPUT_HEADER_DESC(1, 1) + VIDEO_SIZEOF_VS_FORMAT_MJPEG_DESC + VIDEO_SIZEOF_VS_FRAME_MJPEG_DESC(1)) #define USB_VIDEO_DESC_SIZ (unsigned long)(9 + \ VIDEO_VC_NOEP_DESCRIPTOR_LEN + \ @@ -129,7 +131,7 @@ const uint8_t video_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -180,8 +182,10 @@ void usbd_video_close(uint8_t busid, uint8_t intf) void usbd_video_iso_callback(uint8_t busid, uint8_t ep, uint32_t nbytes) { - //USB_LOG_RAW("actual in len:%d\r\n", nbytes); - iso_tx_busy = false; + if (usbd_video_stream_split_transfer(busid, ep)) { + /* one frame has done */ + iso_tx_busy = false; + } } static struct usbd_endpoint video_in_ep = { @@ -192,7 +196,7 @@ static struct usbd_endpoint video_in_ep = { struct usbd_interface intf0; struct usbd_interface intf1; -void video_init(uint8_t busid, uint32_t reg_base) +void video_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, video_descriptor); usbd_add_interface(busid, usbd_video_init_intf(busid, &intf0, INTERVAL, MAX_FRAME_SIZE, MAX_PAYLOAD_SIZE)); @@ -202,48 +206,21 @@ void video_init(uint8_t busid, uint32_t reg_base) usbd_initialize(busid, reg_base, usbd_event_handler); } -USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t packet_buffer[40 * 1024]; +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t packet_buffer[2][MAX_PACKETS_IN_ONE_TRANSFER * MAX_PAYLOAD_SIZE]; void video_test(uint8_t busid) { - uint32_t out_len; - uint32_t packets; + memset(packet_buffer, 0, sizeof(packet_buffer)); - (void)packets; - memset(packet_buffer, 0, 40 * 1024); while (1) { if (tx_flag) { - packets = usbd_video_payload_fill(busid, (uint8_t *)cherryusb_mjpeg, sizeof(cherryusb_mjpeg), packet_buffer, &out_len); -#if 1 iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, packet_buffer, out_len); + usbd_video_stream_start_write(busid, VIDEO_IN_EP, &packet_buffer[0][0], &packet_buffer[1][0], MAX_PACKETS_IN_ONE_TRANSFER * MAX_PAYLOAD_SIZE, (uint8_t *)cherryusb_mjpeg, sizeof(cherryusb_mjpeg)); while (iso_tx_busy) { if (tx_flag == 0) { break; } } -#else - /* dwc2 must use this method */ - for (uint32_t i = 0; i < packets; i++) { - if (i == (packets - 1)) { - iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, &packet_buffer[i * MAX_PAYLOAD_SIZE], out_len - (packets - 1) * MAX_PAYLOAD_SIZE); - while (iso_tx_busy) { - if (tx_flag == 0) { - break; - } - } - } else { - iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, &packet_buffer[i * MAX_PAYLOAD_SIZE], MAX_PAYLOAD_SIZE); - while (iso_tx_busy) { - if (tx_flag == 0) { - break; - } - } - } - } -#endif } } } \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/video_static_yuyv_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/video_static_yuyv_template.c index febbaf9..4ba7170 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/video_static_yuyv_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/video_static_yuyv_template.c @@ -7,6 +7,8 @@ #include "usbd_video.h" #include "cherryusb_yuyv.h" +#define MAX_PACKETS_IN_ONE_TRANSFER 1 + #define VIDEO_IN_EP 0x81 #define VIDEO_INT_EP 0x83 @@ -131,7 +133,7 @@ const uint8_t video_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -182,8 +184,10 @@ void usbd_video_close(uint8_t busid, uint8_t intf) void usbd_video_iso_callback(uint8_t busid, uint8_t ep, uint32_t nbytes) { - //USB_LOG_RAW("actual in len:%d\r\n", nbytes); - iso_tx_busy = false; + if (usbd_video_stream_split_transfer(busid, ep)) { + /* one frame has done */ + iso_tx_busy = false; + } } static struct usbd_endpoint video_in_ep = { @@ -194,7 +198,7 @@ static struct usbd_endpoint video_in_ep = { struct usbd_interface intf0; struct usbd_interface intf1; -void video_init(uint8_t busid, uint32_t reg_base) +void video_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, video_descriptor); usbd_add_interface(busid, usbd_video_init_intf(busid, &intf0, INTERVAL, MAX_FRAME_SIZE, MAX_PAYLOAD_SIZE)); @@ -204,48 +208,21 @@ void video_init(uint8_t busid, uint32_t reg_base) usbd_initialize(busid, reg_base, usbd_event_handler); } -USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t packet_buffer[40 * 1024]; +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t packet_buffer[2][MAX_PACKETS_IN_ONE_TRANSFER * MAX_PAYLOAD_SIZE]; void video_test(uint8_t busid) { - uint32_t out_len; - uint32_t packets; + memset(packet_buffer, 0, sizeof(packet_buffer)); - (void)packets; - memset(packet_buffer, 0, 40 * 1024); while (1) { if (tx_flag) { - packets = usbd_video_payload_fill(busid, (uint8_t *)cherryusb_yuyv, sizeof(cherryusb_yuyv), packet_buffer, &out_len); -#if 1 iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, packet_buffer, out_len); + usbd_video_stream_start_write(busid, VIDEO_IN_EP, &packet_buffer[0][0], &packet_buffer[1][0], MAX_PACKETS_IN_ONE_TRANSFER * MAX_PAYLOAD_SIZE, (uint8_t *)cherryusb_yuyv, sizeof(cherryusb_yuyv)); while (iso_tx_busy) { if (tx_flag == 0) { break; } } -#else - /* dwc2 must use this method */ - for (uint32_t i = 0; i < packets; i++) { - if (i == (packets - 1)) { - iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, &packet_buffer[i * MAX_PAYLOAD_SIZE], out_len - (packets - 1) * MAX_PAYLOAD_SIZE); - while (iso_tx_busy) { - if (tx_flag == 0) { - break; - } - } - } else { - iso_tx_busy = true; - usbd_ep_start_write(busid, VIDEO_IN_EP, &packet_buffer[i * MAX_PAYLOAD_SIZE], MAX_PAYLOAD_SIZE); - while (iso_tx_busy) { - if (tx_flag == 0) { - break; - } - } - } - } -#endif } } } \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/webusb_hid_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/webusb_hid_template.c new file mode 100644 index 0000000..5257087 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/demo/webusb_hid_template.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbd_core.h" +#include "usbd_hid.h" + +#define USBD_VID 0xffff +#define USBD_PID 0xffff +#define USBD_MAX_POWER 100 +#define USBD_LANGID_STRING 1033 + +#define HID_INT_EP 0x81 +#define HID_INT_EP_SIZE 8 +#define HID_INT_EP_INTERVAL 10 + +#define USB_HID_CONFIG_DESC_SIZ (34 + 9) +#define HID_KEYBOARD_REPORT_DESC_SIZE 63 + +#define USBD_WEBUSB_VENDOR_CODE (0x22) +#define USBD_WINUSB_VENDOR_CODE (0x21) + +#define USBD_WINUSB_DESC_SET_LEN (0xB2) +#define URL_DESCRIPTOR_LENGTH (3 + 36) + +#define USBD_WEBUSB_INTF_NUM 0x01 + +#define WEBUSB_URL_STRINGS \ + 'g', 'i', 't', 'h', 'u', 'b', '.', 'c', 'o', 'm', '/', \ + 'c', 'h', 'e', 'r', 'r', 'y', '-', 'e', 'm', 'b', 'e', 'd', 'd', 'e', 'd', '/', 'C', 'h', 'e', 'r', 'r', 'y', 'U', 'S', 'B', + +const uint8_t USBD_WinUSBDescriptorSetDescriptor[USBD_WINUSB_DESC_SET_LEN] = { + // Microsoft OS 2.0 描述符集标头 + 0x0A, 0x00, // Descriptor size (10 bytes) + 0x00, 0x00, // MS OS 2.0 descriptor set header + 0x00, 0x00, 0x03, 0x06, // Windows version (8.1) (0x06030000) + USBD_WINUSB_DESC_SET_LEN, 0x00, // Size, MS OS 2.0 descriptor set + + // Microsoft OS 2.0 配置子集标头 + 0x08, 0x00, // wLength + 0x01, 0x00, // wDescriptorType + 0x00, // 适用于配置 1 + 0x00, // bReserved + 0XA8, 0X00, // Size, MS OS 2.0 configuration subset + + // Microsoft OS 2.0 功能子集头 + 0x08, 0x00, // Descriptor size (8 bytes) + 0x02, 0x00, // MS OS 2.0 function subset header + USBD_WEBUSB_INTF_NUM, // bFirstInterface + 0x00, // 必须设置为 0 + 0xA0, 0x00, + + // Microsoft OS 2.0 兼容 ID 描述符 + // 兼容 ID 描述符告诉 Windows 此设备与 WinUSB 驱动程序兼容 + 0x14, 0x00, // wLength 20 + 0x03, 0x00, // MS_OS_20_FEATURE_COMPATIBLE_ID + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // Microsoft OS 2.0 注册表属性描述符 + // 注册表属性分配设备接口 GUID + 0x84, 0x00, //wLength: 132 + 0x04, 0x00, // wDescriptorType: MS_OS_20_FEATURE_REG_PROPERTY: 0x04 (Table 9) + 0x07, 0x00, //wPropertyDataType: REG_MULTI_SZ (Table 15) + 0x2a, 0x00, //wPropertyNameLength: + //bPropertyName: “DeviceInterfaceGUID” + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, + 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, + 0x00, 0x00, + 0x50, 0x00, // wPropertyDataLength + //bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”. + '{', 0x00, '9', 0x00, 'd', 0x00, '7', 0x00, 'd', 0x00, 'e', 0x00, 'b', 0x00, 'b', 0x00, 'c', 0x00, '-', 0x00, + 'c', 0x00, '8', 0x00, '5', 0x00, 'd', 0x00, '-', 0x00, '1', 0x00, '1', 0x00, 'd', 0x00, '1', 0x00, '-', 0x00, + '9', 0x00, 'e', 0x00, 'b', 0x00, '4', 0x00, '-', 0x00, '0', 0x00, '0', 0x00, '6', 0x00, '0', 0x00, '0', 0x00, + '8', 0x00, 'c', 0x00, '3', 0x00, 'a', 0x00, '1', 0x00, '9', 0x00, 'a', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const uint8_t USBD_WebUSBURLDescriptor[URL_DESCRIPTOR_LENGTH] = { + URL_DESCRIPTOR_LENGTH, + WEBUSB_URL_TYPE, + WEBUSB_URL_SCHEME_HTTPS, + WEBUSB_URL_STRINGS +}; + +#define USBD_BOS_WTOTALLENGTH 0x39 + +#define LANDING_PAGE 0x01 +uint8_t USBD_BinaryObjectStoreDescriptor[USBD_BOS_WTOTALLENGTH] = { + // BOS描述符 + 0x05, // bLength 固长为5 + 0x0F, // bDescriptorType 固定为15 + USBD_BOS_WTOTALLENGTH, 0x00, // wTotalLength BOS描述符的总大小 + 0x02, // bNumDeviceCaps BOS描述符中独立设备功能特性描述符的数量 + + // WebUSB 平台功能描述符 + 0x18, // Descriptor size (24 bytes) + 0x10, // Descriptor type (Device Capability) 设备功能描述符 + 0x05, // Capability type (Platform) 平台描述符 + 0x00, // Reserved + + // WebUSB Platform Capability ID (3408b638-09a9-47a0-8bfd-a0768815b665) + // 平台功能 UUID 将此标识为WebUSB 平台功能描述符,它提供有关设备的基本信息 + 0x38, 0xB6, 0x08, 0x34, + 0xA9, 0x09, + 0xA0, 0x47, + 0x8B, 0xFD, + 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65, + + 0x00, 0x01, // WebUSB version 1.0 + USBD_WEBUSB_VENDOR_CODE, // Vendor-assigned WebUSB request code + LANDING_PAGE, // Landing page + + // Microsoft 平台功能描述符 + // 标头 + 0x1C, // Descriptor size (28 bytes) + 0x10, // Descriptor type (Device Capability) + 0x05, // Capability type (Platform) + 0x00, // Reserved + + 0xDF, 0x60, 0xDD, 0xD8, /* PlatformCapabilityUUID */ + 0x89, 0x45, 0xC7, 0x4C, + 0x9C, 0xD2, 0x65, 0x9D, + 0x9E, 0x64, 0x8A, 0x9F, + + // 描述符集信息结构 + 0x00, 0x00, 0x03, 0x06, /* >= Win 8.1 * dwWindowsVersion 最低兼容 Windows 版本 */ + + USBD_WINUSB_DESC_SET_LEN, 0X00, /* wDescriptorSetTotalLength */ + + USBD_WINUSB_VENDOR_CODE, /* bVendorCode */ + 0X00 /* bAltEnumCode */ +}; + +struct usb_webusb_descriptor webusb_url_desc = { + .vendor_code = USBD_WEBUSB_VENDOR_CODE, + .string = USBD_WebUSBURLDescriptor, + .string_len = USBD_WINUSB_DESC_SET_LEN +}; + +struct usb_msosv2_descriptor msosv2_desc = { + .vendor_code = USBD_WINUSB_VENDOR_CODE, + .compat_id = USBD_WinUSBDescriptorSetDescriptor, + .compat_id_len = USBD_WINUSB_DESC_SET_LEN, +}; + +struct usb_bos_descriptor bos_desc = { + .string = USBD_BinaryObjectStoreDescriptor, + .string_len = USBD_BOS_WTOTALLENGTH +}; + +static const uint8_t webusb_hid_descriptor[] = { + USB_DEVICE_DESCRIPTOR_INIT(USB_2_1, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0002, 0x01), + USB_CONFIG_DESCRIPTOR_INIT(USB_HID_CONFIG_DESC_SIZ, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), + + /************** Descriptor of Joystick Mouse interface ****************/ + /* 09 */ + 0x09, /* bLength: Interface Descriptor size */ + USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType: Interface descriptor type */ + 0x00, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x01, /* bNumEndpoints */ + 0x03, /* bInterfaceClass: HID */ + 0x01, /* bInterfaceSubClass : 1=BOOT, 0=no boot */ + 0x01, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */ + 0, /* iInterface: Index of string descriptor */ + /******************** Descriptor of Joystick Mouse HID ********************/ + /* 18 */ + 0x09, /* bLength: HID Descriptor size */ + HID_DESCRIPTOR_TYPE_HID, /* bDescriptorType: HID */ + 0x11, /* bcdHID: HID Class Spec release number */ + 0x01, + 0x00, /* bCountryCode: Hardware target country */ + 0x01, /* bNumDescriptors: Number of HID class descriptors to follow */ + 0x22, /* bDescriptorType */ + HID_KEYBOARD_REPORT_DESC_SIZE, /* wItemLength: Total length of Report descriptor */ + 0x00, + /******************** Descriptor of Mouse endpoint ********************/ + /* 27 */ + 0x07, /* bLength: Endpoint Descriptor size */ + USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType: */ + HID_INT_EP, /* bEndpointAddress: Endpoint Address (IN) */ + 0x03, /* bmAttributes: Interrupt endpoint */ + HID_INT_EP_SIZE, /* wMaxPacketSize: 4 Byte max */ + 0x00, + HID_INT_EP_INTERVAL, /* bInterval: Polling Interval */ + /* 34 */ + USB_INTERFACE_DESCRIPTOR_INIT(USBD_WEBUSB_INTF_NUM, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00), + /////////////////////////////////////// + /// string0 descriptor + /////////////////////////////////////// + USB_LANGID_INIT(USBD_LANGID_STRING), + /////////////////////////////////////// + /// string1 descriptor + /////////////////////////////////////// + 0x14, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'U', 0x00, /* wcChar6 */ + 'S', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + /////////////////////////////////////// + /// string2 descriptor + /////////////////////////////////////// + 0x26, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + 'C', 0x00, /* wcChar0 */ + 'h', 0x00, /* wcChar1 */ + 'e', 0x00, /* wcChar2 */ + 'r', 0x00, /* wcChar3 */ + 'r', 0x00, /* wcChar4 */ + 'y', 0x00, /* wcChar5 */ + 'U', 0x00, /* wcChar6 */ + 'S', 0x00, /* wcChar7 */ + 'B', 0x00, /* wcChar8 */ + ' ', 0x00, /* wcChar9 */ + 'H', 0x00, /* wcChar10 */ + 'I', 0x00, /* wcChar11 */ + 'D', 0x00, /* wcChar12 */ + ' ', 0x00, /* wcChar13 */ + 'D', 0x00, /* wcChar14 */ + 'E', 0x00, /* wcChar15 */ + 'M', 0x00, /* wcChar16 */ + 'O', 0x00, /* wcChar17 */ + /////////////////////////////////////// + /// string3 descriptor + /////////////////////////////////////// + 0x16, /* bLength */ + USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ + '2', 0x00, /* wcChar0 */ + '0', 0x00, /* wcChar1 */ + '2', 0x00, /* wcChar2 */ + '2', 0x00, /* wcChar3 */ + '1', 0x00, /* wcChar4 */ + '2', 0x00, /* wcChar5 */ + '3', 0x00, /* wcChar6 */ + '4', 0x00, /* wcChar7 */ + '5', 0x00, /* wcChar8 */ + '6', 0x00, /* wcChar9 */ +#ifdef CONFIG_USB_HS + /////////////////////////////////////// + /// device qualifier descriptor + /////////////////////////////////////// + 0x0a, + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, +#endif + 0x00 +}; + +/* USB HID device Configuration Descriptor */ +static uint8_t hid_desc[9] __ALIGN_END = { + /* 18 */ + 0x09, /* bLength: HID Descriptor size */ + HID_DESCRIPTOR_TYPE_HID, /* bDescriptorType: HID */ + 0x11, /* bcdHID: HID Class Spec release number */ + 0x01, + 0x00, /* bCountryCode: Hardware target country */ + 0x01, /* bNumDescriptors: Number of HID class descriptors to follow */ + 0x22, /* bDescriptorType */ + HID_KEYBOARD_REPORT_DESC_SIZE, /* wItemLength: Total length of Report descriptor */ + 0x00, +}; + +static const uint8_t hid_keyboard_report_desc[HID_KEYBOARD_REPORT_DESC_SIZE] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x05, 0x07, // USAGE_PAGE (Keyboard) + 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) + 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x01, // REPORT_SIZE (1) + 0x05, 0x08, // USAGE_PAGE (LEDs) + 0x19, 0x01, // USAGE_MINIMUM (Num Lock) + 0x29, 0x05, // USAGE_MAXIMUM (Kana) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x03, // REPORT_SIZE (3) + 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) + 0x95, 0x06, // REPORT_COUNT (6) + 0x75, 0x08, // REPORT_SIZE (8) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0xFF, // LOGICAL_MAXIMUM (255) + 0x05, 0x07, // USAGE_PAGE (Keyboard) + 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) + 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) + 0x81, 0x00, // INPUT (Data,Ary,Abs) + 0xc0 // END_COLLECTION +}; + +#define HID_STATE_IDLE 0 +#define HID_STATE_BUSY 1 + +/*!< hid state ! Data can be sent only when state is idle */ +static volatile uint8_t hid_state = HID_STATE_IDLE; + +static void usbd_event_handler(uint8_t busid, uint8_t event) +{ + switch (event) { + case USBD_EVENT_RESET: + break; + case USBD_EVENT_CONNECTED: + break; + case USBD_EVENT_DISCONNECTED: + break; + case USBD_EVENT_RESUME: + break; + case USBD_EVENT_SUSPEND: + break; + case USBD_EVENT_CONFIGURED: + hid_state = HID_STATE_IDLE; + break; + case USBD_EVENT_SET_REMOTE_WAKEUP: + break; + case USBD_EVENT_CLR_REMOTE_WAKEUP: + break; + + default: + break; + } +} + +void usbd_hid_int_callback(uint8_t busid, uint8_t ep, uint32_t nbytes) +{ + hid_state = HID_STATE_IDLE; +} + +static struct usbd_endpoint hid_in_ep = { + .ep_cb = usbd_hid_int_callback, + .ep_addr = HID_INT_EP +}; + +static struct usbd_interface intf0; + +void webusb_hid_keyboard_init(uint8_t busid, uintptr_t reg_base) +{ + usbd_desc_register(busid, webusb_hid_descriptor); + usbd_bos_desc_register(busid, &bos_desc); + usbd_msosv2_desc_register(busid, &msosv2_desc); + usbd_webusb_desc_register(busid, &webusb_url_desc); + usbd_add_interface(busid, usbd_hid_init_intf(busid, &intf0, hid_keyboard_report_desc, HID_KEYBOARD_REPORT_DESC_SIZE)); + usbd_add_endpoint(busid, &hid_in_ep); + + usbd_initialize(busid, reg_base, usbd_event_handler); +} + +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t write_buffer[64]; + +void hid_keyboard_test(uint8_t busid) +{ + const uint8_t sendbuffer[8] = { 0x00, 0x00, HID_KBD_USAGE_A, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + if(usb_device_is_configured(busid) == false) { + return; + } + + memcpy(write_buffer, sendbuffer, 8); + hid_state = HID_STATE_BUSY; + usbd_ep_start_write(busid, HID_INT_EP, write_buffer, 8); + while (hid_state == HID_STATE_BUSY) { + } +} diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/webusb_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/webusb_template.c deleted file mode 100644 index dc8bc57..0000000 --- a/rt-thread/components/drivers/usb/cherryusb/demo/webusb_template.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2024, sakumisu - * - * SPDX-License-Identifier: Apache-2.0 - */ -#include "usbd_core.h" - -#define MS_OS_20_DESCRIPTOR_LENGTH (0xB2) - -#define WEBUSB_URL_STRINGS \ - 'd', 'e', 'v', 'a', 'n', 'l', 'a', 'i', '.', 'g', 'i', 't', 'h', 'u', 'b', '.', 'i', 'o', '/', 'w', 'e', 'b', 'd', 'f', 'u', '/', 'd', 'f', 'u', '-', 'u', 't', 'i', 'l' - -#define WL_REQUEST_WEBUSB (0x22) -#define WL_REQUEST_WINUSB (0x21) - -#define URL_DESCRIPTOR_LENGTH 0x2C - -// 描述符集信息 -const uint8_t MS_OS_20_DESCRIPTOR_SET[MS_OS_20_DESCRIPTOR_LENGTH] = { - // Microsoft OS 2.0 描述符集标头 - 0x0A, 0x00, // Descriptor size (10 bytes) - 0x00, 0x00, // MS OS 2.0 descriptor set header - 0x00, 0x00, 0x03, 0x06, // Windows version (8.1) (0x06030000) - MS_OS_20_DESCRIPTOR_LENGTH, 0x00, // Size, MS OS 2.0 descriptor set - - // Microsoft OS 2.0 配置子集标头 - 0x08, 0x00, // wLength - 0x01, 0x00, // wDescriptorType - 0x00, // 适用于配置 1 - 0x00, // bReserved - 0XA8, 0X00, // Size, MS OS 2.0 configuration subset - - // Microsoft OS 2.0 功能子集头 - 0x08, 0x00, // Descriptor size (8 bytes) - 0x02, 0x00, // MS OS 2.0 function subset header - 0x01, // 第2个接口 - 0x00, // 必须设置为 0 - 0xA0, 0x00, - - // Microsoft OS 2.0 兼容 ID 描述符 - // 兼容 ID 描述符告诉 Windows 此设备与 WinUSB 驱动程序兼容 - 0x14, 0x00, // wLength 20 - 0x03, 0x00, // MS_OS_20_FEATURE_COMPATIBLE_ID - 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - // Microsoft OS 2.0 注册表属性描述符 - // 注册表属性分配设备接口 GUID - 0x84, 0x00, //wLength: 132 - 0x04, 0x00, // wDescriptorType: MS_OS_20_FEATURE_REG_PROPERTY: 0x04 (Table 9) - 0x07, 0x00, //wPropertyDataType: REG_MULTI_SZ (Table 15) - 0x2a, 0x00, //wPropertyNameLength: - //bPropertyName: “DeviceInterfaceGUID” - 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, - 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, - 0x00, 0x00, - 0x50, 0x00, // wPropertyDataLength - //bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”. - '{', 0x00, '9', 0x00, 'd', 0x00, '7', 0x00, 'd', 0x00, 'e', 0x00, 'b', 0x00, 'b', 0x00, 'c', 0x00, '-', 0x00, - 'c', 0x00, '8', 0x00, '5', 0x00, 'd', 0x00, '-', 0x00, '1', 0x00, '1', 0x00, 'd', 0x00, '1', 0x00, '-', 0x00, - '9', 0x00, 'e', 0x00, 'b', 0x00, '4', 0x00, '-', 0x00, '0', 0x00, '0', 0x00, '6', 0x00, '0', 0x00, '0', 0x00, - '8', 0x00, 'c', 0x00, '3', 0x00, 'a', 0x00, '1', 0x00, '9', 0x00, 'a', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -const uint8_t USBD_WebUSBURLDescriptor[URL_DESCRIPTOR_LENGTH] = { - URL_DESCRIPTOR_LENGTH, - WEBUSB_URL_TYPE, - WEBUSB_URL_SCHEME_HTTPS, - WEBUSB_URL_STRINGS -}; - -struct usb_webusb_url_ex_descriptor webusb_url_desc = { - .vendor_code = WL_REQUEST_WEBUSB, - .string = MS_OS_20_DESCRIPTOR_SET, - .string_len = MS_OS_20_DESCRIPTOR_LENGTH -}; \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/winusb1.0_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/winusb1.0_template.c index c1fbba9..1eb390b 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/winusb1.0_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/winusb1.0_template.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ #include "usbd_core.h" -#include "usbd_cdc.h" +#include "usbd_cdc_acm.h" #define WCID_VENDOR_CODE 0x17 @@ -151,18 +151,16 @@ __ALIGN_BEGIN const uint8_t WINUSB_IF1_WCIDProperties [142] __ALIGN_END = { const uint8_t *WINUSB_IFx_WCIDProperties[] = { WINUSB_IF0_WCIDProperties, +#if DOUBLE_WINUSB == 1 WINUSB_IF1_WCIDProperties, +#endif }; struct usb_msosv1_descriptor msosv1_desc = { .string = WCID_StringDescriptor_MSOS, .vendor_code = WCID_VENDOR_CODE, .compat_id = WINUSB_WCIDDescriptor, -#if DOUBLE_WINUSB == 0 - .comp_id_property = &WINUSB_IF0_WCIDProperties, -#else .comp_id_property = WINUSB_IFx_WCIDProperties, -#endif }; #define WINUSB_IN_EP 0x81 @@ -328,7 +326,7 @@ const uint8_t winusb_descriptor[] = { 0x02, 0x01, 0x40, - 0x01, + 0x00, 0x00, #endif 0x00 @@ -446,7 +444,7 @@ struct usbd_interface intf1; #endif -void winusb_init(uint8_t busid, uint32_t reg_base) +void winusb_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, winusb_descriptor); usbd_msosv1_desc_register(busid, &msosv1_desc); diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/winusb2.0_cdc_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/winusb2.0_cdc_template.c index 8fe6e99..493668e 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/winusb2.0_cdc_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/winusb2.0_cdc_template.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ #include "usbd_core.h" -#include "usbd_cdc.h" +#include "usbd_cdc_acm.h" #define WINUSB_IN_EP 0x81 #define WINUSB_OUT_EP 0x02 @@ -219,7 +219,7 @@ const uint8_t winusbv2_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif /* End */ @@ -318,7 +318,7 @@ struct usb_bos_descriptor bos_desc = { .string_len = USBD_BOS_WTOTALLENGTH }; -void winusbv2_init(uint8_t busid, uint32_t reg_base) +void winusbv2_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, winusbv2_descriptor); usbd_bos_desc_register(busid, &bos_desc); diff --git a/rt-thread/components/drivers/usb/cherryusb/demo/winusb2.0_hid_template.c b/rt-thread/components/drivers/usb/cherryusb/demo/winusb2.0_hid_template.c index 8df623e..a209dbd 100644 --- a/rt-thread/components/drivers/usb/cherryusb/demo/winusb2.0_hid_template.c +++ b/rt-thread/components/drivers/usb/cherryusb/demo/winusb2.0_hid_template.c @@ -255,7 +255,7 @@ const uint8_t winusbv2_descriptor[] = { 0x00, 0x00, 0x40, - 0x01, + 0x00, 0x00, #endif /* End */ @@ -422,7 +422,7 @@ struct usb_bos_descriptor bos_desc = { .string_len = USBD_BOS_WTOTALLENGTH }; -void winusbv2_init(uint8_t busid, uint32_t reg_base) +void winusbv2_init(uint8_t busid, uintptr_t reg_base) { usbd_desc_register(busid, winusbv2_descriptor); usbd_bos_desc_register(busid, &bos_desc); diff --git a/rt-thread/components/drivers/usb/cherryusb/idf_component.yml b/rt-thread/components/drivers/usb/cherryusb/idf_component.yml new file mode 100644 index 0000000..a500a24 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/idf_component.yml @@ -0,0 +1,25 @@ +version: "1.4.2" +description: CherryUSB is a tiny and portable USB Stack (device & host) for embedded system with USB IP +tags: + - usb + - usb-device + - usb-host + - cdc_acm + - cdc_ecm + - cdc_ncm + - hid + - msc + - rndis + - uvc + - uac + - winusb +url: https://github.com/cherry-embedded/CherryUSB +repository: https://github.com/cherry-embedded/CherryUSB.git +documentation: https://cherryusb.readthedocs.io/ +issues: https://github.com/cherry-embedded/CherryUSB/issues +dependencies: + idf: ">=5.0" +targets: + - esp32s2 + - esp32s3 + - esp32p4 \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/osal/usb_osal_rtthread.c b/rt-thread/components/drivers/usb/cherryusb/osal/usb_osal_rtthread.c index 18b66a4..d355699 100644 --- a/rt-thread/components/drivers/usb/cherryusb/osal/usb_osal_rtthread.c +++ b/rt-thread/components/drivers/usb/cherryusb/osal/usb_osal_rtthread.c @@ -5,6 +5,8 @@ */ #include "usb_osal.h" #include "usb_errno.h" +#include "usb_config.h" +#include "usb_log.h" #include #include @@ -12,6 +14,11 @@ usb_osal_thread_t usb_osal_thread_create(const char *name, uint32_t stack_size, { rt_thread_t htask; htask = rt_thread_create(name, entry, args, stack_size, prio, 10); + if (htask == NULL) { + USB_LOG_ERR("Create thread %s failed\r\n", name); + while (1) { + } + } rt_thread_startup(htask); return (usb_osal_thread_t)htask; } @@ -27,7 +34,13 @@ void usb_osal_thread_delete(usb_osal_thread_t thread) usb_osal_sem_t usb_osal_sem_create(uint32_t initial_count) { - return (usb_osal_sem_t)rt_sem_create("usbh_sem", initial_count, RT_IPC_FLAG_FIFO); + usb_osal_sem_t sem = (usb_osal_sem_t)rt_sem_create("usbh_sem", initial_count, RT_IPC_FLAG_FIFO); + if (sem == NULL) { + USB_LOG_ERR("Create semaphore failed\r\n"); + while (1) { + } + } + return sem; } void usb_osal_sem_delete(usb_osal_sem_t sem) @@ -68,7 +81,13 @@ void usb_osal_sem_reset(usb_osal_sem_t sem) usb_osal_mutex_t usb_osal_mutex_create(void) { - return (usb_osal_mutex_t)rt_mutex_create("usbh_mutex", RT_IPC_FLAG_FIFO); + usb_osal_mutex_t mutex = (usb_osal_mutex_t)rt_mutex_create("usbh_mutex", RT_IPC_FLAG_FIFO); + if (mutex == NULL) { + USB_LOG_ERR("Create mutex failed\r\n"); + while (1) { + } + } + return mutex; } void usb_osal_mutex_delete(usb_osal_mutex_t mutex) @@ -127,11 +146,18 @@ struct usb_osal_timer *usb_osal_timer_create(const char *name, uint32_t timeout_ struct usb_osal_timer *timer; timer = rt_malloc(sizeof(struct usb_osal_timer)); + if (timer == NULL) { + USB_LOG_ERR("Create usb_osal_timer failed\r\n"); + while (1) { + } + } memset(timer, 0, sizeof(struct usb_osal_timer)); - timer->timer = (void *)rt_timer_create("usb_tim", handler, argument, timeout_ms, is_period ? (RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER) : (RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER)); + timer->timer = (void *)rt_timer_create(name, handler, argument, timeout_ms, is_period ? (RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER) : (RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER)); if (timer->timer == NULL) { - return NULL; + USB_LOG_ERR("Create timer failed\r\n"); + while (1) { + } } return timer; } @@ -167,3 +193,13 @@ void usb_osal_msleep(uint32_t delay) { rt_thread_mdelay(delay); } + +void *usb_osal_malloc(size_t size) +{ + return rt_malloc(size); +} + +void usb_osal_free(void *ptr) +{ + rt_free(ptr); +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usb_check.c b/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usb_check.c index d4baf03..b20e17d 100644 --- a/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usb_check.c +++ b/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usb_check.c @@ -1,17 +1,13 @@ #include "rtthread.h" -#ifdef PKG_CHERRYUSB_HOST +#ifdef RT_CHERRYUSB_HOST #ifndef RT_USING_TIMER_SOFT #error must enable RT_USING_TIMER_SOFT to support timer callback in thread #endif -#if IDLE_THREAD_STACK_SIZE < 2048 -#error "IDLE_THREAD_STACK_SIZE must be greater than 2048" -#endif - #if RT_TIMER_THREAD_STACK_SIZE < 2048 -#error "RT_TIMER_THREAD_STACK_SIZE must be greater than 2048" +#error "RT_TIMER_THREAD_STACK_SIZE must be >= 2048" #endif #endif diff --git a/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usbh_dfs.c b/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usbh_dfs.c index 12799e6..073d52e 100644 --- a/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usbh_dfs.c +++ b/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usbh_dfs.c @@ -9,7 +9,7 @@ #include "rtthread.h" #include -#define DEV_FORMAT "/sd%c" +#define DEV_FORMAT "/dev/sd%c" #ifndef CONFIG_USB_DFS_MOUNT_POINT #define CONFIG_USB_DFS_MOUNT_POINT "/" @@ -36,17 +36,6 @@ void rt_hw_cpu_dcache_ops(int ops, void *addr, int size) bflb_l1c_dcache_invalidate_range(addr, size); } } -#elif defined(SOC_HPM5000) || defined(SOC_HPM6000) -#include "hpm_l1c_drv.h" - -void rt_hw_cpu_dcache_ops(int ops, void *addr, int size) -{ - if (ops == RT_HW_CACHE_FLUSH) { - l1c_dc_flush((uint32_t)addr, size); - } else { - l1c_dc_invalidate((uint32_t)addr, size); - } -} #endif USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t msc_sector[512]; @@ -57,7 +46,7 @@ static rt_err_t rt_udisk_init(rt_device_t dev) } static ssize_t rt_udisk_read(rt_device_t dev, rt_off_t pos, void *buffer, - rt_size_t size) + rt_size_t size) { struct usbh_msc *msc_class = (struct usbh_msc *)dev->user_data; int ret; @@ -96,7 +85,7 @@ static ssize_t rt_udisk_read(rt_device_t dev, rt_off_t pos, void *buffer, } static ssize_t rt_udisk_write(rt_device_t dev, rt_off_t pos, const void *buffer, - rt_size_t size) + rt_size_t size) { struct usbh_msc *msc_class = (struct usbh_msc *)dev->user_data; int ret; @@ -186,15 +175,17 @@ int udisk_init(struct usbh_msc *msc_class) ret = usbh_msc_scsi_read10(msc_class, 0, msc_sector, 1); if (ret != RT_EOK) { rt_kprintf("usb mass_storage read failed\n"); + rt_free(dev); return ret; } - for (i = 0; i < 16; i++) { + for (i = 0; i < 4; i++) { /* Get the first partition */ ret = dfs_filesystem_get_partition(&part0, msc_sector, i); if (ret == RT_EOK) { rt_kprintf("Found partition %d: type = %d, offet=0x%x, size=0x%x\n", i, part0.type, part0.offset, part0.size); + break; } else { break; } @@ -230,6 +221,8 @@ void usbh_msc_run(struct usbh_msc *msc_class) void usbh_msc_stop(struct usbh_msc *msc_class) { + struct rt_device *dev; + char name[CONFIG_USBHOST_DEV_NAMELEN]; char mount_point[CONFIG_USBHOST_DEV_NAMELEN]; @@ -237,5 +230,9 @@ void usbh_msc_stop(struct usbh_msc *msc_class) snprintf(mount_point, CONFIG_USBHOST_DEV_NAMELEN, CONFIG_USB_DFS_MOUNT_POINT, msc_class->sdchar); dfs_unmount(mount_point); - rt_device_unregister(rt_device_find(name)); + dev = rt_device_find(name); + if (dev) { + rt_device_unregister(dev); + rt_free(dev); + } } diff --git a/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usbh_lwip.c b/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usbh_lwip.c index 360a848..34dc385 100644 --- a/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usbh_lwip.c +++ b/rt-thread/components/drivers/usb/cherryusb/platform/rtthread/usbh_lwip.c @@ -44,6 +44,10 @@ #error PBUF_POOL_BUFSIZE must be larger than 1600 #endif +#if RT_LWIP_TCPTHREAD_STACKSIZE < 2048 +#error RT_LWIP_TCPTHREAD_STACKSIZE must be >= 2048 +#endif + // #define CONFIG_USBHOST_PLATFORM_CDC_ECM // #define CONFIG_USBHOST_PLATFORM_CDC_RNDIS // #define CONFIG_USBHOST_PLATFORM_CDC_NCM @@ -118,6 +122,7 @@ static rt_err_t rt_usbh_cdc_ecm_control(rt_device_t dev, int cmd, void *args) static rt_err_t rt_usbh_cdc_ecm_eth_tx(rt_device_t dev, struct pbuf *p) { int ret; + (void)dev; usbh_lwip_eth_output_common(p, usbh_cdc_ecm_get_eth_txbuf()); ret = usbh_cdc_ecm_eth_output(p->tot_len); @@ -150,6 +155,8 @@ void usbh_cdc_ecm_run(struct usbh_cdc_ecm *cdc_ecm_class) void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class) { + (void)cdc_ecm_class; + eth_device_deinit(&g_cdc_ecm_dev); } #endif @@ -204,6 +211,7 @@ static rt_err_t rt_usbh_rndis_control(rt_device_t dev, int cmd, void *args) static rt_err_t rt_usbh_rndis_eth_tx(rt_device_t dev, struct pbuf *p) { int ret; + (void)dev; usbh_lwip_eth_output_common(p, usbh_rndis_get_eth_txbuf()); ret = usbh_rndis_eth_output(p->tot_len); @@ -237,6 +245,8 @@ void usbh_rndis_run(struct usbh_rndis *rndis_class) void usbh_rndis_stop(struct usbh_rndis *rndis_class) { + (void)rndis_class; + eth_device_deinit(&g_rndis_dev); // rt_timer_stop(keep_timer); // rt_timer_delete(keep_timer); @@ -273,6 +283,7 @@ static rt_err_t rt_usbh_cdc_ncm_control(rt_device_t dev, int cmd, void *args) static rt_err_t rt_usbh_cdc_ncm_eth_tx(rt_device_t dev, struct pbuf *p) { int ret; + (void)dev; usbh_lwip_eth_output_common(p, usbh_cdc_ncm_get_eth_txbuf()); ret = usbh_cdc_ncm_eth_output(p->tot_len); @@ -305,6 +316,8 @@ void usbh_cdc_ncm_run(struct usbh_cdc_ncm *cdc_ncm_class) void usbh_cdc_ncm_stop(struct usbh_cdc_ncm *cdc_ncm_class) { + (void)cdc_ncm_class; + eth_device_deinit(&g_cdc_ncm_dev); } #endif @@ -339,6 +352,7 @@ static rt_err_t rt_usbh_asix_control(rt_device_t dev, int cmd, void *args) static rt_err_t rt_usbh_asix_eth_tx(rt_device_t dev, struct pbuf *p) { int ret; + (void)dev; usbh_lwip_eth_output_common(p, usbh_asix_get_eth_txbuf()); ret = usbh_asix_eth_output(p->tot_len); @@ -371,6 +385,8 @@ void usbh_asix_run(struct usbh_asix *asix_class) void usbh_asix_stop(struct usbh_asix *asix_class) { + (void)asix_class; + eth_device_deinit(&g_asix_dev); } #endif @@ -405,6 +421,7 @@ static rt_err_t rt_usbh_rtl8152_control(rt_device_t dev, int cmd, void *args) static rt_err_t rt_usbh_rtl8152_eth_tx(rt_device_t dev, struct pbuf *p) { int ret; + (void)dev; usbh_lwip_eth_output_common(p, usbh_rtl8152_get_eth_txbuf()); ret = usbh_rtl8152_eth_output(p->tot_len); @@ -437,6 +454,8 @@ void usbh_rtl8152_run(struct usbh_rtl8152 *rtl8152_class) void usbh_rtl8152_stop(struct usbh_rtl8152 *rtl8152_class) { + (void)rtl8152_class; + eth_device_deinit(&g_rtl8152_dev); } #endif diff --git a/rt-thread/components/drivers/usb/cherryusb/port/bouffalolab/usb_dc_bl.c b/rt-thread/components/drivers/usb/cherryusb/port/bouffalolab/usb_dc_bl.c index 6f45329..b6859f8 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/bouffalolab/usb_dc_bl.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/bouffalolab/usb_dc_bl.c @@ -613,6 +613,23 @@ int usbd_set_address(uint8_t busid, const uint8_t addr) return 0; } +int usbd_set_remote_wakeup(uint8_t busid) +{ + uint32_t regval; + + regval = getreg32(BFLB_USB_BASE + USB_DEV_CTL_OFFSET); + regval |= USB_CAP_RMWAKUP; + putreg32(regval, BFLB_USB_BASE + USB_DEV_CTL_OFFSET); + + bflb_mtimer_delay_ms(10); + + regval = getreg32(BFLB_USB_BASE + USB_DEV_CTL_OFFSET); + regval &= ~USB_CAP_RMWAKUP; + putreg32(regval, BFLB_USB_BASE + USB_DEV_CTL_OFFSET); + + return 0; +} + uint8_t usbd_get_port_speed(uint8_t busid) { uint8_t speed = 3; @@ -638,7 +655,7 @@ int usbd_ep_open(uint8_t busid, const struct usb_endpoint_descriptor *ep) uint8_t ep_idx = USB_EP_GET_IDX(ep_addr); - if ((ep_idx > 4) && (ep_idx < 9)) { + if (ep_idx > 4) { return 0; } @@ -822,6 +839,29 @@ int usbd_ep_clear_stall(uint8_t busid, const uint8_t ep) int usbd_ep_is_stalled(uint8_t busid, const uint8_t ep, uint8_t *stalled) { + uint32_t regval; + + uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (ep_idx == 0) { + } else { + if (USB_EP_DIR_IS_OUT(ep)) { + regval = getreg32(BFLB_USB_BASE + USB_DEV_OUTMPS1_OFFSET + (ep_idx - 1) * 4); + if (regval & USB_STL_OEP1) { + *stalled = 1; + } else { + *stalled = 0; + } + } else { + regval = getreg32(BFLB_USB_BASE + USB_DEV_INMPS1_OFFSET + (ep_idx - 1) * 4); + if (regval & USB_STL_IEP1) { + *stalled = 1; + } else { + *stalled = 0; + } + } + } + return 0; } @@ -1019,12 +1059,11 @@ void USBD_IRQHandler(uint8_t busid) } #ifdef CONFIG_USBDEV_TEST_MODE -void usbd_execute_test_mode(struct usb_setup_packet *setup) +void usbd_execute_test_mode(uint8_t busid, uint8_t test_mode) { uint32_t regval; - uint8_t index = setup->wIndex >> 8; - switch (index) { + switch (test_mode) { case 1: // Test_J { regval = getreg32(BFLB_USB_BASE + USB_PHY_TST_OFFSET); diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ch32/usb_dc_usbfs.c b/rt-thread/components/drivers/usb/cherryusb/port/ch32/usb_dc_usbfs.c index 4b4b72d..7aabd41 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ch32/usb_dc_usbfs.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/ch32/usb_dc_usbfs.c @@ -101,6 +101,11 @@ int usbd_set_address(uint8_t busid, const uint8_t addr) return 0; } +int usbd_set_remote_wakeup(uint8_t busid) +{ + return -1; +} + uint8_t usbd_get_port_speed(uint8_t busid) { return USB_SPEED_FULL; diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ch32/usb_dc_usbhs.c b/rt-thread/components/drivers/usb/cherryusb/port/ch32/usb_dc_usbhs.c index 758ffd7..a8d3036 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ch32/usb_dc_usbhs.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/ch32/usb_dc_usbhs.c @@ -101,6 +101,11 @@ int usbd_set_address(uint8_t busid, const uint8_t addr) return 0; } +int usbd_set_remote_wakeup(uint8_t busid) +{ + return -1; +} + uint8_t usbd_get_port_speed(uint8_t busid) { return USB_SPEED_HIGH; diff --git a/rt-thread/components/drivers/usb/cherryusb/port/chipidea/README.md b/rt-thread/components/drivers/usb/cherryusb/port/chipidea/README.md new file mode 100644 index 0000000..6a43f79 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/chipidea/README.md @@ -0,0 +1,14 @@ +# Note + +## Support Chip List + +### NXP + +Modify USB_NOCACHE_RAM_SECTION + +``` +#define USB_NOCACHE_RAM_SECTION __attribute__((section(".NonCacheable"))) +``` + +- IMRT10XX/IMRT11XX +- MCXN9XX/MCXN236 diff --git a/rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_chipidea_reg.h b/rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_chipidea_reg.h new file mode 100644 index 0000000..4867ef5 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_chipidea_reg.h @@ -0,0 +1,2249 @@ +/* + * Copyright (c) 2021-2024 HPMicro + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + + +#ifndef HPM_USB_H +#define HPM_USB_H + +#define __R volatile const /* Define "read-only" permission */ +#define __RW volatile /* Define "read-write" permission */ +#define __W volatile /* Define "write-only" permission */ + +typedef struct { + __R uint8_t RESERVED0[128]; /* 0x0 - 0x7F: Reserved */ + __RW uint32_t GPTIMER0LD; /* 0x80: General Purpose Timer #0 Load Register */ + __RW uint32_t GPTIMER0CTRL; /* 0x84: General Purpose Timer #0 Controller Register */ + __RW uint32_t GPTIMER1LD; /* 0x88: General Purpose Timer #1 Load Register */ + __RW uint32_t GPTIMER1CTRL; /* 0x8C: General Purpose Timer #1 Controller Register */ + __RW uint32_t SBUSCFG; /* 0x90: System Bus Config Register */ + __R uint8_t RESERVED1[172]; /* 0x94 - 0x13F: Reserved */ + __RW uint32_t USBCMD; /* 0x140: USB Command Register */ + __RW uint32_t USBSTS; /* 0x144: USB Status Register */ + __RW uint32_t USBINTR; /* 0x148: Interrupt Enable Register */ + __RW uint32_t FRINDEX; /* 0x14C: USB Frame Index Register */ + __R uint8_t RESERVED2[4]; /* 0x150 - 0x153: Reserved */ + union { + __RW uint32_t DEVICEADDR; /* 0x154: Device Address Register */ + __RW uint32_t PERIODICLISTBASE; /* 0x154: Frame List Base Address Register */ + }; + union { + __RW uint32_t ASYNCLISTADDR; /* 0x158: Next Asynch. Address Register */ + __RW uint32_t ENDPTLISTADDR; /* 0x158: Endpoint List Address Register */ + }; + __R uint8_t RESERVED3[4]; /* 0x15C - 0x15F: Reserved */ + __RW uint32_t BURSTSIZE; /* 0x160: Programmable Burst Size Register */ + __RW uint32_t TXFILLTUNING; /* 0x164: TX FIFO Fill Tuning Register */ + __R uint8_t RESERVED4[16]; /* 0x168 - 0x177: Reserved */ + __RW uint32_t ENDPTNAK; /* 0x178: Endpoint NAK Register */ + __RW uint32_t ENDPTNAKEN; /* 0x17C: Endpoint NAK Enable Register */ + __R uint8_t RESERVED5[4]; /* 0x180 - 0x183: Reserved */ + __RW uint32_t PORTSC1; /* 0x184: Port Status & Control */ + __R uint8_t RESERVED6[28]; /* 0x188 - 0x1A3: Reserved */ + __RW uint32_t OTGSC; /* 0x1A4: On-The-Go Status & control Register */ + __RW uint32_t USBMODE; /* 0x1A8: USB Device Mode Register */ + __RW uint32_t ENDPTSETUPSTAT; /* 0x1AC: Endpoint Setup Status Register */ + __RW uint32_t ENDPTPRIME; /* 0x1B0: Endpoint Prime Register */ + __RW uint32_t ENDPTFLUSH; /* 0x1B4: Endpoint Flush Register */ + __R uint32_t ENDPTSTAT; /* 0x1B8: Endpoint Status Register */ + __RW uint32_t ENDPTCOMPLETE; /* 0x1BC: Endpoint Complete Register */ + __RW uint32_t ENDPTCTRL[8]; /* 0x1C0 - 0x1DC: Endpoint Control0 Register... Endpoint Control7 Register */ +} CHIPIDEA_TypeDef; + + +/* Bitfield definition for register: GPTIMER0LD */ +/* + * GPTLD (RW) + * + * GPTLD + * General Purpose Timer Load Value + * These bit fields are loaded to GPTCNT bits when GPTRST bit is set '1b'. + * This value represents the time in microseconds minus 1 for the timer duration. + * Example: for a one millisecond timer, load 1000-1=999 or 0x0003E7. + * NOTE: Max value is 0xFFFFFF or 16.777215 seconds. + */ +#define USB_GPTIMER0LD_GPTLD_MASK (0xFFFFFFUL) +#define USB_GPTIMER0LD_GPTLD_SHIFT (0U) +#define USB_GPTIMER0LD_GPTLD_SET(x) (((uint32_t)(x) << USB_GPTIMER0LD_GPTLD_SHIFT) & USB_GPTIMER0LD_GPTLD_MASK) +#define USB_GPTIMER0LD_GPTLD_GET(x) (((uint32_t)(x) & USB_GPTIMER0LD_GPTLD_MASK) >> USB_GPTIMER0LD_GPTLD_SHIFT) + +/* Bitfield definition for register: GPTIMER0CTRL */ +/* + * GPTRUN (RW) + * + * GPTRUN + * General Purpose Timer Run + * GPTCNT bits are not effected when setting or clearing this bit. + * 0 - Stop counting + * 1 - Run + */ +#define USB_GPTIMER0CTRL_GPTRUN_MASK (0x80000000UL) +#define USB_GPTIMER0CTRL_GPTRUN_SHIFT (31U) +#define USB_GPTIMER0CTRL_GPTRUN_SET(x) (((uint32_t)(x) << USB_GPTIMER0CTRL_GPTRUN_SHIFT) & USB_GPTIMER0CTRL_GPTRUN_MASK) +#define USB_GPTIMER0CTRL_GPTRUN_GET(x) (((uint32_t)(x) & USB_GPTIMER0CTRL_GPTRUN_MASK) >> USB_GPTIMER0CTRL_GPTRUN_SHIFT) + +/* + * GPTRST (WO) + * + * GPTRST + * General Purpose Timer Reset + * 0 - No action + * 1 - Load counter value from GPTLD bits in n_GPTIMER0LD + */ +#define USB_GPTIMER0CTRL_GPTRST_MASK (0x40000000UL) +#define USB_GPTIMER0CTRL_GPTRST_SHIFT (30U) +#define USB_GPTIMER0CTRL_GPTRST_SET(x) (((uint32_t)(x) << USB_GPTIMER0CTRL_GPTRST_SHIFT) & USB_GPTIMER0CTRL_GPTRST_MASK) +#define USB_GPTIMER0CTRL_GPTRST_GET(x) (((uint32_t)(x) & USB_GPTIMER0CTRL_GPTRST_MASK) >> USB_GPTIMER0CTRL_GPTRST_SHIFT) + +/* + * GPTMODE (RW) + * + * GPTMODE + * General Purpose Timer Mode + * In one shot mode, the timer will count down to zero, generate an interrupt, and stop until the counter is + * reset by software; + * In repeat mode, the timer will count down to zero, generate an interrupt and automatically reload the + * counter value from GPTLD bits to start again. + * 0 - One Shot Mode + * 1 - Repeat Mode + */ +#define USB_GPTIMER0CTRL_GPTMODE_MASK (0x1000000UL) +#define USB_GPTIMER0CTRL_GPTMODE_SHIFT (24U) +#define USB_GPTIMER0CTRL_GPTMODE_SET(x) (((uint32_t)(x) << USB_GPTIMER0CTRL_GPTMODE_SHIFT) & USB_GPTIMER0CTRL_GPTMODE_MASK) +#define USB_GPTIMER0CTRL_GPTMODE_GET(x) (((uint32_t)(x) & USB_GPTIMER0CTRL_GPTMODE_MASK) >> USB_GPTIMER0CTRL_GPTMODE_SHIFT) + +/* + * GPTCNT (RO) + * + * GPTCNT + * General Purpose Timer Counter. + * This field is the count value of the countdown timer. + */ +#define USB_GPTIMER0CTRL_GPTCNT_MASK (0xFFFFFFUL) +#define USB_GPTIMER0CTRL_GPTCNT_SHIFT (0U) +#define USB_GPTIMER0CTRL_GPTCNT_GET(x) (((uint32_t)(x) & USB_GPTIMER0CTRL_GPTCNT_MASK) >> USB_GPTIMER0CTRL_GPTCNT_SHIFT) + +/* Bitfield definition for register: GPTIMER1LD */ +/* + * GPTLD (RW) + * + * GPTLD + * General Purpose Timer Load Value + * These bit fields are loaded to GPTCNT bits when GPTRST bit is set '1b'. + * This value represents the time in microseconds minus 1 for the timer duration. + * Example: for a one millisecond timer, load 1000-1=999 or 0x0003E7. + * NOTE: Max value is 0xFFFFFF or 16.777215 seconds. + */ +#define USB_GPTIMER1LD_GPTLD_MASK (0xFFFFFFUL) +#define USB_GPTIMER1LD_GPTLD_SHIFT (0U) +#define USB_GPTIMER1LD_GPTLD_SET(x) (((uint32_t)(x) << USB_GPTIMER1LD_GPTLD_SHIFT) & USB_GPTIMER1LD_GPTLD_MASK) +#define USB_GPTIMER1LD_GPTLD_GET(x) (((uint32_t)(x) & USB_GPTIMER1LD_GPTLD_MASK) >> USB_GPTIMER1LD_GPTLD_SHIFT) + +/* Bitfield definition for register: GPTIMER1CTRL */ +/* + * GPTRUN (RW) + * + * GPTRUN + * General Purpose Timer Run + * GPTCNT bits are not effected when setting or clearing this bit. + * 0 - Stop counting + * 1 - Run + */ +#define USB_GPTIMER1CTRL_GPTRUN_MASK (0x80000000UL) +#define USB_GPTIMER1CTRL_GPTRUN_SHIFT (31U) +#define USB_GPTIMER1CTRL_GPTRUN_SET(x) (((uint32_t)(x) << USB_GPTIMER1CTRL_GPTRUN_SHIFT) & USB_GPTIMER1CTRL_GPTRUN_MASK) +#define USB_GPTIMER1CTRL_GPTRUN_GET(x) (((uint32_t)(x) & USB_GPTIMER1CTRL_GPTRUN_MASK) >> USB_GPTIMER1CTRL_GPTRUN_SHIFT) + +/* + * GPTRST (WO) + * + * GPTRST + * General Purpose Timer Reset + * 0 - No action + * 1 - Load counter value from GPTLD bits in USB_n_GPTIMER1LD + */ +#define USB_GPTIMER1CTRL_GPTRST_MASK (0x40000000UL) +#define USB_GPTIMER1CTRL_GPTRST_SHIFT (30U) +#define USB_GPTIMER1CTRL_GPTRST_SET(x) (((uint32_t)(x) << USB_GPTIMER1CTRL_GPTRST_SHIFT) & USB_GPTIMER1CTRL_GPTRST_MASK) +#define USB_GPTIMER1CTRL_GPTRST_GET(x) (((uint32_t)(x) & USB_GPTIMER1CTRL_GPTRST_MASK) >> USB_GPTIMER1CTRL_GPTRST_SHIFT) + +/* + * GPTMODE (RW) + * + * GPTMODE + * General Purpose Timer Mode + * In one shot mode, the timer will count down to zero, generate an interrupt, and stop until the counter is + * reset by software. In repeat mode, the timer will count down to zero, generate an interrupt and + * automatically reload the counter value from GPTLD bits to start again. + * 0 - One Shot Mode + * 1 - Repeat Mode + */ +#define USB_GPTIMER1CTRL_GPTMODE_MASK (0x1000000UL) +#define USB_GPTIMER1CTRL_GPTMODE_SHIFT (24U) +#define USB_GPTIMER1CTRL_GPTMODE_SET(x) (((uint32_t)(x) << USB_GPTIMER1CTRL_GPTMODE_SHIFT) & USB_GPTIMER1CTRL_GPTMODE_MASK) +#define USB_GPTIMER1CTRL_GPTMODE_GET(x) (((uint32_t)(x) & USB_GPTIMER1CTRL_GPTMODE_MASK) >> USB_GPTIMER1CTRL_GPTMODE_SHIFT) + +/* + * GPTCNT (RO) + * + * GPTCNT + * General Purpose Timer Counter. + * This field is the count value of the countdown timer. + */ +#define USB_GPTIMER1CTRL_GPTCNT_MASK (0xFFFFFFUL) +#define USB_GPTIMER1CTRL_GPTCNT_SHIFT (0U) +#define USB_GPTIMER1CTRL_GPTCNT_GET(x) (((uint32_t)(x) & USB_GPTIMER1CTRL_GPTCNT_MASK) >> USB_GPTIMER1CTRL_GPTCNT_SHIFT) + +/* Bitfield definition for register: SBUSCFG */ +/* + * AHBBRST (RW) + * + * AHBBRST + * AHB master interface Burst configuration + * These bits control AHB master transfer type sequence (or priority). + * NOTE: This register overrides n_BURSTSIZE register when its value is not zero. + * 000 - Incremental burst of unspecified length only + * 001 - INCR4 burst, then single transfer + * 010 - INCR8 burst, INCR4 burst, then single transfer + * 011 - INCR16 burst, INCR8 burst, INCR4 burst, then single transfer + * 100 - Reserved, don't use + * 101 - INCR4 burst, then incremental burst of unspecified length + * 110 - INCR8 burst, INCR4 burst, then incremental burst of unspecified length + * 111 - INCR16 burst, INCR8 burst, INCR4 burst, then incremental burst of unspecified length + */ +#define USB_SBUSCFG_AHBBRST_MASK (0x7U) +#define USB_SBUSCFG_AHBBRST_SHIFT (0U) +#define USB_SBUSCFG_AHBBRST_SET(x) (((uint32_t)(x) << USB_SBUSCFG_AHBBRST_SHIFT) & USB_SBUSCFG_AHBBRST_MASK) +#define USB_SBUSCFG_AHBBRST_GET(x) (((uint32_t)(x) & USB_SBUSCFG_AHBBRST_MASK) >> USB_SBUSCFG_AHBBRST_SHIFT) + +/* Bitfield definition for register: USBCMD */ +/* + * ITC (RW) + * + * ITC + * Interrupt Threshold Control -Read/Write. + * The system software uses this field to set the maximum rate at which the host/device controller will issue interrupts. + * ITC contains the maximum interrupt interval measured in micro-frames. Valid values are + * shown below. + * Value Maximum Interrupt Interval + * 00000000 - Immediate (no threshold) + * 00000001 - 1 micro-frame + * 00000010 - 2 micro-frames + * 00000100 - 4 micro-frames + * 00001000 - 8 micro-frames + * 00010000 - 16 micro-frames + * 00100000 - 32 micro-frames + * 01000000 - 64 micro-frames + */ +#define USB_USBCMD_ITC_MASK (0xFF0000UL) +#define USB_USBCMD_ITC_SHIFT (16U) +#define USB_USBCMD_ITC_SET(x) (((uint32_t)(x) << USB_USBCMD_ITC_SHIFT) & USB_USBCMD_ITC_MASK) +#define USB_USBCMD_ITC_GET(x) (((uint32_t)(x) & USB_USBCMD_ITC_MASK) >> USB_USBCMD_ITC_SHIFT) + +/* + * FS_2 (RW) + * + * FS_2 + * Frame List Size - (Read/Write or Read Only). [host mode only] + * This field is Read/Write only if Programmable Frame List Flag in the HCCPARAMS registers is set to one. + * This field specifies the size of the frame list that controls which bits in the Frame Index Register should be used for the Frame List Current index. + * NOTE: This field is made up from USBCMD bits 15, 3 and 2. + * Value Meaning + * 0b000 - 1024 elements (4096 bytes) Default value + * 0b001 - 512 elements (2048 bytes) + * 0b010 - 256 elements (1024 bytes) + * 0b011 - 128 elements (512 bytes) + * 0b100 - 64 elements (256 bytes) + * 0b101 - 32 elements (128 bytes) + * 0b110 - 16 elements (64 bytes) + * 0b111 - 8 elements (32 bytes) + */ +#define USB_USBCMD_FS_2_MASK (0x8000U) +#define USB_USBCMD_FS_2_SHIFT (15U) +#define USB_USBCMD_FS_2_SET(x) (((uint32_t)(x) << USB_USBCMD_FS_2_SHIFT) & USB_USBCMD_FS_2_MASK) +#define USB_USBCMD_FS_2_GET(x) (((uint32_t)(x) & USB_USBCMD_FS_2_MASK) >> USB_USBCMD_FS_2_SHIFT) + +/* + * ATDTW (RW) + * + * ATDTW + * Add dTD TripWire - Read/Write. [device mode only] + * This bit is used as a semaphore to ensure proper addition of a new dTD to an active (primed) endpoint's + * linked list. This bit is set and cleared by software. + * This bit would also be cleared by hardware when state machine is hazard region for which adding a dTD + * to a primed endpoint may go unrecognized. + */ +#define USB_USBCMD_ATDTW_MASK (0x4000U) +#define USB_USBCMD_ATDTW_SHIFT (14U) +#define USB_USBCMD_ATDTW_SET(x) (((uint32_t)(x) << USB_USBCMD_ATDTW_SHIFT) & USB_USBCMD_ATDTW_MASK) +#define USB_USBCMD_ATDTW_GET(x) (((uint32_t)(x) & USB_USBCMD_ATDTW_MASK) >> USB_USBCMD_ATDTW_SHIFT) + +/* + * SUTW (RW) + * + * SUTW + * Setup TripWire - Read/Write. [device mode only] + * This bit is used as a semaphore to ensure that the setup data payload of 8 bytes is extracted from a QH by the DCD without being corrupted. + * If the setup lockout mode is off (SLOM bit in USB core register n_USBMODE, see USBMODE ) then + * there is a hazard when new setup data arrives while the DCD is copying the setup data payload + * from the QH for a previous setup packet. This bit is set and cleared by software. + * This bit would also be cleared by hardware when a hazard detected. + */ +#define USB_USBCMD_SUTW_MASK (0x2000U) +#define USB_USBCMD_SUTW_SHIFT (13U) +#define USB_USBCMD_SUTW_SET(x) (((uint32_t)(x) << USB_USBCMD_SUTW_SHIFT) & USB_USBCMD_SUTW_MASK) +#define USB_USBCMD_SUTW_GET(x) (((uint32_t)(x) & USB_USBCMD_SUTW_MASK) >> USB_USBCMD_SUTW_SHIFT) + +/* + * ASPE (RW) + * + * ASPE + * Asynchronous Schedule Park Mode Enable - Read/Write. + * If the Asynchronous Park Capability bit in the HCCPARAMS register is a one, then this bit defaults to a 1h and is R/W. + * Otherwise the bit must be a zero and is RO. Software uses this bit to enable or disable Park mode. + * When this bit is one, Park mode is enabled. When this bit is a zero, Park mode is disabled. + * NOTE: ASPE bit reset value: '0b' for OTG controller . + */ +#define USB_USBCMD_ASPE_MASK (0x800U) +#define USB_USBCMD_ASPE_SHIFT (11U) +#define USB_USBCMD_ASPE_SET(x) (((uint32_t)(x) << USB_USBCMD_ASPE_SHIFT) & USB_USBCMD_ASPE_MASK) +#define USB_USBCMD_ASPE_GET(x) (((uint32_t)(x) & USB_USBCMD_ASPE_MASK) >> USB_USBCMD_ASPE_SHIFT) + +/* + * ASP (RW) + * + * ASP + * Asynchronous Schedule Park Mode Count - Read/Write. + * If the Asynchronous Park Capability bit in the HCCPARAMS register is a one, then this field defaults to 3h and is R/W. Otherwise it defaults to zero and is Read-Only. + * It contains a count of the number of successive transactions the host controller is allowed to + * execute from a high-speed queue head on the Asynchronous schedule before continuing traversal of the Asynchronous schedule. + * Valid values are 1h to 3h. Software must not write a zero to this bit when Park Mode Enable is a one as this will result in undefined behavior. + * This field is set to 3h in all controller core. + */ +#define USB_USBCMD_ASP_MASK (0x300U) +#define USB_USBCMD_ASP_SHIFT (8U) +#define USB_USBCMD_ASP_SET(x) (((uint32_t)(x) << USB_USBCMD_ASP_SHIFT) & USB_USBCMD_ASP_MASK) +#define USB_USBCMD_ASP_GET(x) (((uint32_t)(x) & USB_USBCMD_ASP_MASK) >> USB_USBCMD_ASP_SHIFT) + +/* + * IAA (RW) + * + * IAA + * Interrupt on Async Advance Doorbell - Read/Write. + * This bit is used as a doorbell by software to tell the host controller to issue an interrupt the next time it advances asynchronous schedule. Software must write a 1 to this bit to ring the doorbell. + * When the host controller has evicted all appropriate cached schedule states, + * it sets the Interrupt on Async Advance status bit in the USBSTS register. + * If the Interrupt on Sync Advance Enable bit in the USBINTR register is one, then the host controller will assert an interrupt at the next interrupt threshold. + * The host controller sets this bit to zero after it has set the Interrupt on Sync Advance status bit in the USBSTS register to one. + * Software should not write a one to this bit when the asynchronous schedule is inactive. Doing so will yield undefined results. + * This bit is only used in host mode. Writing a one to this bit when device mode is selected will have undefined results. + */ +#define USB_USBCMD_IAA_MASK (0x40U) +#define USB_USBCMD_IAA_SHIFT (6U) +#define USB_USBCMD_IAA_SET(x) (((uint32_t)(x) << USB_USBCMD_IAA_SHIFT) & USB_USBCMD_IAA_MASK) +#define USB_USBCMD_IAA_GET(x) (((uint32_t)(x) & USB_USBCMD_IAA_MASK) >> USB_USBCMD_IAA_SHIFT) + +/* + * ASE (RW) + * + * ASE + * Asynchronous Schedule Enable - Read/Write. Default 0b. + * This bit controls whether the host controller skips processing the Asynchronous Schedule. + * Only the host controller uses this bit. + * Values Meaning + * 0 - Do not process the Asynchronous Schedule. + * 1 - Use the ASYNCLISTADDR register to access the Asynchronous Schedule. + */ +#define USB_USBCMD_ASE_MASK (0x20U) +#define USB_USBCMD_ASE_SHIFT (5U) +#define USB_USBCMD_ASE_SET(x) (((uint32_t)(x) << USB_USBCMD_ASE_SHIFT) & USB_USBCMD_ASE_MASK) +#define USB_USBCMD_ASE_GET(x) (((uint32_t)(x) & USB_USBCMD_ASE_MASK) >> USB_USBCMD_ASE_SHIFT) + +/* + * PSE (RW) + * + * PSE + * Periodic Schedule Enable- Read/Write. Default 0b. + * This bit controls whether the host controller skips processing the Periodic Schedule. + * Only the host controller uses this bit. + * Values Meaning + * 0 - Do not process the Periodic Schedule + * 1 - Use the PERIODICLISTBASE register to access the Periodic Schedule. + */ +#define USB_USBCMD_PSE_MASK (0x10U) +#define USB_USBCMD_PSE_SHIFT (4U) +#define USB_USBCMD_PSE_SET(x) (((uint32_t)(x) << USB_USBCMD_PSE_SHIFT) & USB_USBCMD_PSE_MASK) +#define USB_USBCMD_PSE_GET(x) (((uint32_t)(x) & USB_USBCMD_PSE_MASK) >> USB_USBCMD_PSE_SHIFT) + +/* + * FS_1 (RW) + * + * FS_1 + * See description at bit 15 + */ +#define USB_USBCMD_FS_1_MASK (0xCU) +#define USB_USBCMD_FS_1_SHIFT (2U) +#define USB_USBCMD_FS_1_SET(x) (((uint32_t)(x) << USB_USBCMD_FS_1_SHIFT) & USB_USBCMD_FS_1_MASK) +#define USB_USBCMD_FS_1_GET(x) (((uint32_t)(x) & USB_USBCMD_FS_1_MASK) >> USB_USBCMD_FS_1_SHIFT) + +/* + * RST (RW) + * + * RST + * Controller Reset (RESET) - Read/Write. Software uses this bit to reset the controller. + * This bit is set to zero by the Host/Device Controller when the reset process is complete. Software cannot terminate the reset process early by writing a zero to this register. + * Host operation mode: + * When software writes a one to this bit, the Controller resets its internal pipelines, timers, counters, state machines etc. to their initial value. + * Any transaction currently in progress on USB is immediately terminated. A USB reset is not driven on downstream ports. + * Software should not set this bit to a one when the HCHalted bit in the USBSTS register is a zero. + * Attempting to reset an actively running host controller will result in undefined behavior. + * Device operation mode: + * When software writes a one to this bit, the Controller resets its internal pipelines, timers, counters, state machines etc. to their initial value. + * Writing a one to this bit when the device is in the attached state is not recommended, because the effect on an attached host is undefined. + * In order to ensure that the device is not in an attached state before initiating a device controller reset, all primed endpoints should be flushed and the USBCMD Run/Stop bit should be set to 0. + */ +#define USB_USBCMD_RST_MASK (0x2U) +#define USB_USBCMD_RST_SHIFT (1U) +#define USB_USBCMD_RST_SET(x) (((uint32_t)(x) << USB_USBCMD_RST_SHIFT) & USB_USBCMD_RST_MASK) +#define USB_USBCMD_RST_GET(x) (((uint32_t)(x) & USB_USBCMD_RST_MASK) >> USB_USBCMD_RST_SHIFT) + +/* + * RS (RW) + * + * RS + * Run/Stop (RS) - Read/Write. Default 0b. 1=Run. 0=Stop. + * Host operation mode: + * When set to '1b', the Controller proceeds with the execution of the schedule. The Controller continues execution as long as this bit is set to a one. + * When this bit is set to 0, the Host Controller completes the current transaction on the USB and then halts. + * The HC Halted bit in the status register indicates when the Controller has finished the transaction and has entered the stopped state. + * Software should not write a one to this field unless the controller is in the Halted state (that is, HCHalted in the USBSTS register is a one). + * Device operation mode: + * Writing a one to this bit will cause the controller to enable a pull-up on D+ and initiate an attach event. + * This control bit is not directly connected to the pull-up enable, as the pull-up will become disabled upon transitioning into high-speed mode. + * Software should use this bit to prevent an attach event before the controller has been properly initialized. Writing a 0 to this will cause a detach event. + */ +#define USB_USBCMD_RS_MASK (0x1U) +#define USB_USBCMD_RS_SHIFT (0U) +#define USB_USBCMD_RS_SET(x) (((uint32_t)(x) << USB_USBCMD_RS_SHIFT) & USB_USBCMD_RS_MASK) +#define USB_USBCMD_RS_GET(x) (((uint32_t)(x) & USB_USBCMD_RS_MASK) >> USB_USBCMD_RS_SHIFT) + +/* Bitfield definition for register: USBSTS */ +/* + * TI1 (RWC) + * + * TI1 + * General Purpose Timer Interrupt 1(GPTINT1)--R/WC. + * This bit is set when the counter in the GPTIMER1CTRL register transitions to zero, writing a one to this + * bit will clear it. + */ +#define USB_USBSTS_TI1_MASK (0x2000000UL) +#define USB_USBSTS_TI1_SHIFT (25U) +#define USB_USBSTS_TI1_SET(x) (((uint32_t)(x) << USB_USBSTS_TI1_SHIFT) & USB_USBSTS_TI1_MASK) +#define USB_USBSTS_TI1_GET(x) (((uint32_t)(x) & USB_USBSTS_TI1_MASK) >> USB_USBSTS_TI1_SHIFT) + +/* + * TI0 (RWC) + * + * TI0 + * General Purpose Timer Interrupt 0(GPTINT0)--R/WC. + * This bit is set when the counter in the GPTIMER0CTRL register transitions to zero, writing a one to this + * bit clears it. + */ +#define USB_USBSTS_TI0_MASK (0x1000000UL) +#define USB_USBSTS_TI0_SHIFT (24U) +#define USB_USBSTS_TI0_SET(x) (((uint32_t)(x) << USB_USBSTS_TI0_SHIFT) & USB_USBSTS_TI0_MASK) +#define USB_USBSTS_TI0_GET(x) (((uint32_t)(x) & USB_USBSTS_TI0_MASK) >> USB_USBSTS_TI0_SHIFT) + +/* + * UPI (RWC) + * + * USB Host Periodic Interrupt – RWC. Default = 0b. + * This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction + * where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set and the TD was from the periodic schedule. + * This bit is also set by the Host Controller when a short packet is detected and the packet is on the periodic schedule. + * A short packet is when the actual number of bytes received was less than expected. + * This bit is not used by the device controller and will always be zero. + */ +#define USB_USBSTS_UPI_MASK (0x80000UL) +#define USB_USBSTS_UPI_SHIFT (19U) +#define USB_USBSTS_UPI_SET(x) (((uint32_t)(x) << USB_USBSTS_UPI_SHIFT) & USB_USBSTS_UPI_MASK) +#define USB_USBSTS_UPI_GET(x) (((uint32_t)(x) & USB_USBSTS_UPI_MASK) >> USB_USBSTS_UPI_SHIFT) + +/* + * UAI (RWC) + * + * USB Host Asynchronous Interrupt – RWC. Default = 0b. + * This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction + * where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set AND the TD was from the asynchronous schedule. + * This bit is also set by the Host when a short packet is detected and the packet is on the asynchronous schedule. + * A short packet is when the actual number of bytes received was less than expected. + * This bit is not used by the device controller and will always be zero + */ +#define USB_USBSTS_UAI_MASK (0x40000UL) +#define USB_USBSTS_UAI_SHIFT (18U) +#define USB_USBSTS_UAI_SET(x) (((uint32_t)(x) << USB_USBSTS_UAI_SHIFT) & USB_USBSTS_UAI_MASK) +#define USB_USBSTS_UAI_GET(x) (((uint32_t)(x) & USB_USBSTS_UAI_MASK) >> USB_USBSTS_UAI_SHIFT) + +/* + * NAKI (RO) + * + * NAKI + * NAK Interrupt Bit--RO. + * This bit is set by hardware when for a particular endpoint both the TX/RX Endpoint NAK bit and + * corresponding TX/RX Endpoint NAK Enable bit are set. This bit is automatically cleared by hardware + * when all Enabled TX/RX Endpoint NAK bits are cleared. + */ +#define USB_USBSTS_NAKI_MASK (0x10000UL) +#define USB_USBSTS_NAKI_SHIFT (16U) +#define USB_USBSTS_NAKI_GET(x) (((uint32_t)(x) & USB_USBSTS_NAKI_MASK) >> USB_USBSTS_NAKI_SHIFT) + +/* + * AS (RO) + * + * AS + * Asynchronous Schedule Status - Read Only. + * This bit reports the current real status of the Asynchronous Schedule. When set to zero the asynchronous schedule status is disabled and if set to one the status is enabled. + * The Host Controller is not required to immediately disable or enable the Asynchronous Schedule when software transitions the Asynchronous Schedule Enable bit in the USBCMD register. + * When this bit and the Asynchronous Schedule Enable bit are the same value, the Asynchronous Schedule is either enabled (1) or disabled (0). + * Only used in the host operation mode. + */ +#define USB_USBSTS_AS_MASK (0x8000U) +#define USB_USBSTS_AS_SHIFT (15U) +#define USB_USBSTS_AS_GET(x) (((uint32_t)(x) & USB_USBSTS_AS_MASK) >> USB_USBSTS_AS_SHIFT) + +/* + * PS (RO) + * + * PS + * Periodic Schedule Status - Read Only. + * This bit reports the current real status of the Periodic Schedule. When set to zero the periodic schedule is disabled, and if set to one the status is enabled. + * The Host Controller is not required to immediately disable or enable the Periodic Schedule when software transitions the Periodic Schedule Enable bit in the USBCMD register. + * When this bit and the Periodic Schedule Enable bit are the same value, the Periodic Schedule is either enabled (1) or disabled (0). + * Only used in the host operation mode. + */ +#define USB_USBSTS_PS_MASK (0x4000U) +#define USB_USBSTS_PS_SHIFT (14U) +#define USB_USBSTS_PS_GET(x) (((uint32_t)(x) & USB_USBSTS_PS_MASK) >> USB_USBSTS_PS_SHIFT) + +/* + * RCL (RO) + * + * RCL + * Reclamation - Read Only. + * This is a read-only status bit used to detect an empty asynchronous schedule. + * Only used in the host operation mode. + */ +#define USB_USBSTS_RCL_MASK (0x2000U) +#define USB_USBSTS_RCL_SHIFT (13U) +#define USB_USBSTS_RCL_GET(x) (((uint32_t)(x) & USB_USBSTS_RCL_MASK) >> USB_USBSTS_RCL_SHIFT) + +/* + * HCH (RO) + * + * HCH + * HCHaIted - Read Only. + * This bit is a zero whenever the Run/Stop bit is a one. + * The Controller sets this bit to one after it has stopped executing because of the Run/Stop bit being set to 0, + * either by software or by the Controller hardware (for example, an internal error). + * Only used in the host operation mode. + * Default value is '0b' for OTG core . + * This is because OTG core is not operating as host in default. Please see CM bit in USB_n_USBMODE + * register. + * NOTE: HCH bit reset value: '0b' for OTG controller core . + */ +#define USB_USBSTS_HCH_MASK (0x1000U) +#define USB_USBSTS_HCH_SHIFT (12U) +#define USB_USBSTS_HCH_GET(x) (((uint32_t)(x) & USB_USBSTS_HCH_MASK) >> USB_USBSTS_HCH_SHIFT) + +/* + * SLI (RWC) + * + * SLI + * DCSuspend - R/WC. + * When a controller enters a suspend state from an active state, this bit will be set to a one. The device controller clears the bit upon exiting from a suspend state. + * Only used in device operation mode. + */ +#define USB_USBSTS_SLI_MASK (0x100U) +#define USB_USBSTS_SLI_SHIFT (8U) +#define USB_USBSTS_SLI_SET(x) (((uint32_t)(x) << USB_USBSTS_SLI_SHIFT) & USB_USBSTS_SLI_MASK) +#define USB_USBSTS_SLI_GET(x) (((uint32_t)(x) & USB_USBSTS_SLI_MASK) >> USB_USBSTS_SLI_SHIFT) + +/* + * SRI (RWC) + * + * SRI + * SOF Received - R/WC. + * When the device controller detects a Start Of (micro) Frame, this bit will be set to a one. + * When a SOF is extremely late, the device controller will automatically set this bit to indicate that an SOF was expected. + * Therefore, this bit will be set roughly every 1ms in device FS mode and every 125ms in HS mode and will be synchronized to the actual SOF that is received. + * Because the device controller is initialized to FS before connect, this bit will be set at an interval of 1ms during the prelude to connect and chirp. + * In host mode, this bit will be set every 125us and can be used by host controller driver as a time base. + * Software writes a 1 to this bit to clear it. + */ +#define USB_USBSTS_SRI_MASK (0x80U) +#define USB_USBSTS_SRI_SHIFT (7U) +#define USB_USBSTS_SRI_SET(x) (((uint32_t)(x) << USB_USBSTS_SRI_SHIFT) & USB_USBSTS_SRI_MASK) +#define USB_USBSTS_SRI_GET(x) (((uint32_t)(x) & USB_USBSTS_SRI_MASK) >> USB_USBSTS_SRI_SHIFT) + +/* + * URI (RWC) + * + * URI + * USB Reset Received - R/WC. + * When the device controller detects a USB Reset and enters the default state, this bit will be set to a one. + * Software can write a 1 to this bit to clear the USB Reset Received status bit. + * Only used in device operation mode. + */ +#define USB_USBSTS_URI_MASK (0x40U) +#define USB_USBSTS_URI_SHIFT (6U) +#define USB_USBSTS_URI_SET(x) (((uint32_t)(x) << USB_USBSTS_URI_SHIFT) & USB_USBSTS_URI_MASK) +#define USB_USBSTS_URI_GET(x) (((uint32_t)(x) & USB_USBSTS_URI_MASK) >> USB_USBSTS_URI_SHIFT) + +/* + * AAI (RWC) + * + * AAI + * Interrupt on Async Advance - R/WC. + * System software can force the host controller to issue an interrupt the next time the host controller advances the asynchronous schedule + * by writing a one to the Interrupt on Async Advance Doorbell bit in the n_USBCMD register. This status bit indicates the assertion of that interrupt source. + * Only used in host operation mode. + */ +#define USB_USBSTS_AAI_MASK (0x20U) +#define USB_USBSTS_AAI_SHIFT (5U) +#define USB_USBSTS_AAI_SET(x) (((uint32_t)(x) << USB_USBSTS_AAI_SHIFT) & USB_USBSTS_AAI_MASK) +#define USB_USBSTS_AAI_GET(x) (((uint32_t)(x) & USB_USBSTS_AAI_MASK) >> USB_USBSTS_AAI_SHIFT) + +/* + * SEI (RWC) + * + * System Error – RWC. Default = 0b. + * In the BVCI implementation of the USBHS core, this bit is not used, and will always be cleared to '0b'. + * In the AMBA implementation, this bit will be set to '1b' when an Error response is seen by the master interface (HRESP[1:0]=ERROR) + */ +#define USB_USBSTS_SEI_MASK (0x10U) +#define USB_USBSTS_SEI_SHIFT (4U) +#define USB_USBSTS_SEI_SET(x) (((uint32_t)(x) << USB_USBSTS_SEI_SHIFT) & USB_USBSTS_SEI_MASK) +#define USB_USBSTS_SEI_GET(x) (((uint32_t)(x) & USB_USBSTS_SEI_MASK) >> USB_USBSTS_SEI_SHIFT) + +/* + * FRI (RWC) + * + * FRI + * Frame List Rollover - R/WC. + * The Host Controller sets this bit to a one when the Frame List Index rolls over from its maximum value to + * zero. The exact value at which the rollover occurs depends on the frame list size. For example. If the + * frame list size (as programmed in the Frame List Size field of the USB_n_USBCMD register) is 1024, the + * Frame Index Register rolls over every time FRINDEX [13] toggles. Similarly, if the size is 512, the Host + * Controller sets this bit to a one every time FHINDEX [12] toggles. + * Only used in host operation mode. + */ +#define USB_USBSTS_FRI_MASK (0x8U) +#define USB_USBSTS_FRI_SHIFT (3U) +#define USB_USBSTS_FRI_SET(x) (((uint32_t)(x) << USB_USBSTS_FRI_SHIFT) & USB_USBSTS_FRI_MASK) +#define USB_USBSTS_FRI_GET(x) (((uint32_t)(x) & USB_USBSTS_FRI_MASK) >> USB_USBSTS_FRI_SHIFT) + +/* + * PCI (RWC) + * + * PCI + * Port Change Detect - R/WC. + * The Host Controller sets this bit to a one when on any port a Connect Status occurs, a Port Enable/Disable Change occurs, + * or the Force Port Resume bit is set as the result of a J-K transition on the suspended port. + * The Device Controller sets this bit to a one when the port controller enters the full or high-speed operational state. + * When the port controller exits the full or high-speed operation states due to Reset or Suspend events, + * the notification mechanisms are the USB Reset Received bit and the DCSuspend bits Respectively. + */ +#define USB_USBSTS_PCI_MASK (0x4U) +#define USB_USBSTS_PCI_SHIFT (2U) +#define USB_USBSTS_PCI_SET(x) (((uint32_t)(x) << USB_USBSTS_PCI_SHIFT) & USB_USBSTS_PCI_MASK) +#define USB_USBSTS_PCI_GET(x) (((uint32_t)(x) & USB_USBSTS_PCI_MASK) >> USB_USBSTS_PCI_SHIFT) + +/* + * UEI (RWC) + * + * UEI + * USB Error Interrupt (USBERRINT) - R/WC. + * When completion of a USB transaction results in an error condition, this bit is set by the Host/Device Controller. + * This bit is set along with the USBINT bit, if the TD on which the error interrupt occurred also had its interrupt on complete (IOC) bit set. + */ +#define USB_USBSTS_UEI_MASK (0x2U) +#define USB_USBSTS_UEI_SHIFT (1U) +#define USB_USBSTS_UEI_SET(x) (((uint32_t)(x) << USB_USBSTS_UEI_SHIFT) & USB_USBSTS_UEI_MASK) +#define USB_USBSTS_UEI_GET(x) (((uint32_t)(x) & USB_USBSTS_UEI_MASK) >> USB_USBSTS_UEI_SHIFT) + +/* + * UI (RWC) + * + * UI + * USB Interrupt (USBINT) - R/WC. + * This bit is set by the Host/Device Controller when the cause of an interrupt is a completion of a USB + * transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set. + * This bit is also set by the Host/Device Controller when a short packet is detected. A short packet is when + * the actual number of bytes received was less than the expected number of bytes. + */ +#define USB_USBSTS_UI_MASK (0x1U) +#define USB_USBSTS_UI_SHIFT (0U) +#define USB_USBSTS_UI_SET(x) (((uint32_t)(x) << USB_USBSTS_UI_SHIFT) & USB_USBSTS_UI_MASK) +#define USB_USBSTS_UI_GET(x) (((uint32_t)(x) & USB_USBSTS_UI_MASK) >> USB_USBSTS_UI_SHIFT) + +/* Bitfield definition for register: USBINTR */ +/* + * TIE1 (RW) + * + * TIE1 + * General Purpose Timer #1 Interrupt Enable + * When this bit is one and the TI1 bit in n_USBSTS register is a one the controller will issue an interrupt. + */ +#define USB_USBINTR_TIE1_MASK (0x2000000UL) +#define USB_USBINTR_TIE1_SHIFT (25U) +#define USB_USBINTR_TIE1_SET(x) (((uint32_t)(x) << USB_USBINTR_TIE1_SHIFT) & USB_USBINTR_TIE1_MASK) +#define USB_USBINTR_TIE1_GET(x) (((uint32_t)(x) & USB_USBINTR_TIE1_MASK) >> USB_USBINTR_TIE1_SHIFT) + +/* + * TIE0 (RW) + * + * TIE0 + * General Purpose Timer #0 Interrupt Enable + * When this bit is one and the TI0 bit in n_USBSTS register is a one the controller will issue an interrupt. + */ +#define USB_USBINTR_TIE0_MASK (0x1000000UL) +#define USB_USBINTR_TIE0_SHIFT (24U) +#define USB_USBINTR_TIE0_SET(x) (((uint32_t)(x) << USB_USBINTR_TIE0_SHIFT) & USB_USBINTR_TIE0_MASK) +#define USB_USBINTR_TIE0_GET(x) (((uint32_t)(x) & USB_USBINTR_TIE0_MASK) >> USB_USBINTR_TIE0_SHIFT) + +/* + * UPIE (RW) + * + * UPIE + * USB Host Periodic Interrupt Enable + * When this bit is one, and the UPI bit in the n_USBSTS register is one, host controller will issue an + * interrupt at the next interrupt threshold. + */ +#define USB_USBINTR_UPIE_MASK (0x80000UL) +#define USB_USBINTR_UPIE_SHIFT (19U) +#define USB_USBINTR_UPIE_SET(x) (((uint32_t)(x) << USB_USBINTR_UPIE_SHIFT) & USB_USBINTR_UPIE_MASK) +#define USB_USBINTR_UPIE_GET(x) (((uint32_t)(x) & USB_USBINTR_UPIE_MASK) >> USB_USBINTR_UPIE_SHIFT) + +/* + * UAIE (RW) + * + * UAIE + * USB Host Asynchronous Interrupt Enable + * When this bit is one, and the UAI bit in the n_USBSTS register is one, host controller will issue an + * interrupt at the next interrupt threshold. + */ +#define USB_USBINTR_UAIE_MASK (0x40000UL) +#define USB_USBINTR_UAIE_SHIFT (18U) +#define USB_USBINTR_UAIE_SET(x) (((uint32_t)(x) << USB_USBINTR_UAIE_SHIFT) & USB_USBINTR_UAIE_MASK) +#define USB_USBINTR_UAIE_GET(x) (((uint32_t)(x) & USB_USBINTR_UAIE_MASK) >> USB_USBINTR_UAIE_SHIFT) + +/* + * NAKE (RO) + * + * NAKE + * NAK Interrupt Enable + * When this bit is one and the NAKI bit in n_USBSTS register is a one the controller will issue an interrupt. + */ +#define USB_USBINTR_NAKE_MASK (0x10000UL) +#define USB_USBINTR_NAKE_SHIFT (16U) +#define USB_USBINTR_NAKE_GET(x) (((uint32_t)(x) & USB_USBINTR_NAKE_MASK) >> USB_USBINTR_NAKE_SHIFT) + +/* + * SLE (RW) + * + * SLE + * Sleep Interrupt Enable + * When this bit is one and the SLI bit in n_n_USBSTS register is a one the controller will issue an interrupt. + * Only used in device operation mode. + */ +#define USB_USBINTR_SLE_MASK (0x100U) +#define USB_USBINTR_SLE_SHIFT (8U) +#define USB_USBINTR_SLE_SET(x) (((uint32_t)(x) << USB_USBINTR_SLE_SHIFT) & USB_USBINTR_SLE_MASK) +#define USB_USBINTR_SLE_GET(x) (((uint32_t)(x) & USB_USBINTR_SLE_MASK) >> USB_USBINTR_SLE_SHIFT) + +/* + * SRE (RW) + * + * SRE + * SOF Received Interrupt Enable + * When this bit is one and the SRI bit in n_USBSTS register is a one the controller will issue an interrupt. + */ +#define USB_USBINTR_SRE_MASK (0x80U) +#define USB_USBINTR_SRE_SHIFT (7U) +#define USB_USBINTR_SRE_SET(x) (((uint32_t)(x) << USB_USBINTR_SRE_SHIFT) & USB_USBINTR_SRE_MASK) +#define USB_USBINTR_SRE_GET(x) (((uint32_t)(x) & USB_USBINTR_SRE_MASK) >> USB_USBINTR_SRE_SHIFT) + +/* + * URE (RW) + * + * URE + * USB Reset Interrupt Enable + * When this bit is one and the URI bit in n_USBSTS register is a one the controller will issue an interrupt. + * Only used in device operation mode. + */ +#define USB_USBINTR_URE_MASK (0x40U) +#define USB_USBINTR_URE_SHIFT (6U) +#define USB_USBINTR_URE_SET(x) (((uint32_t)(x) << USB_USBINTR_URE_SHIFT) & USB_USBINTR_URE_MASK) +#define USB_USBINTR_URE_GET(x) (((uint32_t)(x) & USB_USBINTR_URE_MASK) >> USB_USBINTR_URE_SHIFT) + +/* + * AAE (RW) + * + * AAE + * Async Advance Interrupt Enable + * When this bit is one and the AAI bit in n_USBSTS register is a one the controller will issue an interrupt. + * Only used in host operation mode. + */ +#define USB_USBINTR_AAE_MASK (0x20U) +#define USB_USBINTR_AAE_SHIFT (5U) +#define USB_USBINTR_AAE_SET(x) (((uint32_t)(x) << USB_USBINTR_AAE_SHIFT) & USB_USBINTR_AAE_MASK) +#define USB_USBINTR_AAE_GET(x) (((uint32_t)(x) & USB_USBINTR_AAE_MASK) >> USB_USBINTR_AAE_SHIFT) + +/* + * SEE (RW) + * + * SEE + * System Error Interrupt Enable + * When this bit is one and the SEI bit in n_USBSTS register is a one the controller will issue an interrupt. + * Only used in host operation mode. + */ +#define USB_USBINTR_SEE_MASK (0x10U) +#define USB_USBINTR_SEE_SHIFT (4U) +#define USB_USBINTR_SEE_SET(x) (((uint32_t)(x) << USB_USBINTR_SEE_SHIFT) & USB_USBINTR_SEE_MASK) +#define USB_USBINTR_SEE_GET(x) (((uint32_t)(x) & USB_USBINTR_SEE_MASK) >> USB_USBINTR_SEE_SHIFT) + +/* + * FRE (RW) + * + * FRE + * Frame List Rollover Interrupt Enable + * When this bit is one and the FRI bit in n_USBSTS register is a one the controller will issue an interrupt. + * Only used in host operation mode. + */ +#define USB_USBINTR_FRE_MASK (0x8U) +#define USB_USBINTR_FRE_SHIFT (3U) +#define USB_USBINTR_FRE_SET(x) (((uint32_t)(x) << USB_USBINTR_FRE_SHIFT) & USB_USBINTR_FRE_MASK) +#define USB_USBINTR_FRE_GET(x) (((uint32_t)(x) & USB_USBINTR_FRE_MASK) >> USB_USBINTR_FRE_SHIFT) + +/* + * PCE (RW) + * + * PCE + * Port Change Detect Interrupt Enable + * When this bit is one and the PCI bit in n_USBSTS register is a one the controller will issue an interrupt. + */ +#define USB_USBINTR_PCE_MASK (0x4U) +#define USB_USBINTR_PCE_SHIFT (2U) +#define USB_USBINTR_PCE_SET(x) (((uint32_t)(x) << USB_USBINTR_PCE_SHIFT) & USB_USBINTR_PCE_MASK) +#define USB_USBINTR_PCE_GET(x) (((uint32_t)(x) & USB_USBINTR_PCE_MASK) >> USB_USBINTR_PCE_SHIFT) + +/* + * UEE (RWC) + * + * UEE + * USB Error Interrupt Enable + * When this bit is one and the UEI bit in n_USBSTS register is a one the controller will issue an interrupt. + */ +#define USB_USBINTR_UEE_MASK (0x2U) +#define USB_USBINTR_UEE_SHIFT (1U) +#define USB_USBINTR_UEE_SET(x) (((uint32_t)(x) << USB_USBINTR_UEE_SHIFT) & USB_USBINTR_UEE_MASK) +#define USB_USBINTR_UEE_GET(x) (((uint32_t)(x) & USB_USBINTR_UEE_MASK) >> USB_USBINTR_UEE_SHIFT) + +/* + * UE (RW) + * + * UE + * USB Interrupt Enable + * When this bit is one and the UI bit in n_USBSTS register is a one the controller will issue an interrupt. + */ +#define USB_USBINTR_UE_MASK (0x1U) +#define USB_USBINTR_UE_SHIFT (0U) +#define USB_USBINTR_UE_SET(x) (((uint32_t)(x) << USB_USBINTR_UE_SHIFT) & USB_USBINTR_UE_MASK) +#define USB_USBINTR_UE_GET(x) (((uint32_t)(x) & USB_USBINTR_UE_MASK) >> USB_USBINTR_UE_SHIFT) + +/* Bitfield definition for register: FRINDEX */ +/* + * FRINDEX (RW) + * + * FRINDEX + * Frame Index. + * The value, in this register, increments at the end of each time frame (micro-frame). Bits [N: 3] are used for the Frame List current index. + * This means that each location of the frame list is accessed 8 times (frames or micro-frames) before moving to the next index. + * The following illustrates values of N based on the value of the Frame List Size field in the USBCMD register, when used in host mode. + * USBCMD [Frame List Size] Number Elements N + * In device mode the value is the current frame number of the last frame transmitted. It is not used as an index. + * In either mode bits 2:0 indicate the current microframe. + * The bit field values description below is represented as (Frame List Size) Number Elements N. + * 00000000000000 - (1024) 12 + * 00000000000001 - (512) 11 + * 00000000000010 - (256) 10 + * 00000000000011 - (128) 9 + * 00000000000100 - (64) 8 + * 00000000000101 - (32) 7 + * 00000000000110 - (16) 6 + * 00000000000111 - (8) 5 + */ +#define USB_FRINDEX_FRINDEX_MASK (0x3FFFU) +#define USB_FRINDEX_FRINDEX_SHIFT (0U) +#define USB_FRINDEX_FRINDEX_SET(x) (((uint32_t)(x) << USB_FRINDEX_FRINDEX_SHIFT) & USB_FRINDEX_FRINDEX_MASK) +#define USB_FRINDEX_FRINDEX_GET(x) (((uint32_t)(x) & USB_FRINDEX_FRINDEX_MASK) >> USB_FRINDEX_FRINDEX_SHIFT) + +/* Bitfield definition for register: DEVICEADDR */ +/* + * USBADR (RW) + * + * USBADR + * Device Address. + * These bits correspond to the USB device address + */ +#define USB_DEVICEADDR_USBADR_MASK (0xFE000000UL) +#define USB_DEVICEADDR_USBADR_SHIFT (25U) +#define USB_DEVICEADDR_USBADR_SET(x) (((uint32_t)(x) << USB_DEVICEADDR_USBADR_SHIFT) & USB_DEVICEADDR_USBADR_MASK) +#define USB_DEVICEADDR_USBADR_GET(x) (((uint32_t)(x) & USB_DEVICEADDR_USBADR_MASK) >> USB_DEVICEADDR_USBADR_SHIFT) + +/* + * USBADRA (RW) + * + * USBADRA + * Device Address Advance. Default=0. + * When this bit is '0', any writes to USBADR are instantaneous. + * When this bit is written to a '1' at the same time or before USBADR is written, the write to the USBADR field is staged and held in a hidden register. + * After an IN occurs on endpoint 0 and is ACKed, USBADR will be loaded from the holding register. + * Hardware will automatically clear this bit on the following conditions: + * 1) IN is ACKed to endpoint 0. (USBADR is updated from staging register). + * 2) OUT/SETUP occur to endpoint 0. (USBADR is not updated). + * 3) Device Reset occurs (USBADR is reset to 0). + * NOTE: After the status phase of the SET_ADDRESS descriptor, the DCD has 2 ms to program the USBADR field. + * This mechanism will ensure this specification is met when the DCD can not write of the device address within 2ms from the SET_ADDRESS status phase. + * If the DCD writes the USBADR with USBADRA=1 after the SET_ADDRESS data phase (before the prime of the status phase), + * the USBADR will be programmed instantly at the correct time and meet the 2ms USB requirement. + */ +#define USB_DEVICEADDR_USBADRA_MASK (0x1000000UL) +#define USB_DEVICEADDR_USBADRA_SHIFT (24U) +#define USB_DEVICEADDR_USBADRA_SET(x) (((uint32_t)(x) << USB_DEVICEADDR_USBADRA_SHIFT) & USB_DEVICEADDR_USBADRA_MASK) +#define USB_DEVICEADDR_USBADRA_GET(x) (((uint32_t)(x) & USB_DEVICEADDR_USBADRA_MASK) >> USB_DEVICEADDR_USBADRA_SHIFT) + +/* Bitfield definition for register: PERIODICLISTBASE */ +/* + * BASEADR (RW) + * + * BASEADR + * Base Address (Low). + * These bits correspond to memory address signals [31:12], respectively. + * Only used by the host controller. + */ +#define USB_PERIODICLISTBASE_BASEADR_MASK (0xFFFFF000UL) +#define USB_PERIODICLISTBASE_BASEADR_SHIFT (12U) +#define USB_PERIODICLISTBASE_BASEADR_SET(x) (((uint32_t)(x) << USB_PERIODICLISTBASE_BASEADR_SHIFT) & USB_PERIODICLISTBASE_BASEADR_MASK) +#define USB_PERIODICLISTBASE_BASEADR_GET(x) (((uint32_t)(x) & USB_PERIODICLISTBASE_BASEADR_MASK) >> USB_PERIODICLISTBASE_BASEADR_SHIFT) + +/* Bitfield definition for register: ASYNCLISTADDR */ +/* + * ASYBASE (RW) + * + * ASYBASE + * Link Pointer Low (LPL). + * These bits correspond to memory address signals [31:5], respectively. This field may only reference a + * Queue Head (QH). + * Only used by the host controller. + */ +#define USB_ASYNCLISTADDR_ASYBASE_MASK (0xFFFFFFE0UL) +#define USB_ASYNCLISTADDR_ASYBASE_SHIFT (5U) +#define USB_ASYNCLISTADDR_ASYBASE_SET(x) (((uint32_t)(x) << USB_ASYNCLISTADDR_ASYBASE_SHIFT) & USB_ASYNCLISTADDR_ASYBASE_MASK) +#define USB_ASYNCLISTADDR_ASYBASE_GET(x) (((uint32_t)(x) & USB_ASYNCLISTADDR_ASYBASE_MASK) >> USB_ASYNCLISTADDR_ASYBASE_SHIFT) + +/* Bitfield definition for register: ENDPTLISTADDR */ +/* + * EPBASE (RW) + * + * EPBASE + * Endpoint List Pointer(Low). These bits correspond to memory address signals [31:11], respectively. + * This field will reference a list of up to 32 Queue Head (QH) (that is, one queue head per endpoint & direction). + */ +#define USB_ENDPTLISTADDR_EPBASE_MASK (0xFFFFF800UL) +#define USB_ENDPTLISTADDR_EPBASE_SHIFT (11U) +#define USB_ENDPTLISTADDR_EPBASE_SET(x) (((uint32_t)(x) << USB_ENDPTLISTADDR_EPBASE_SHIFT) & USB_ENDPTLISTADDR_EPBASE_MASK) +#define USB_ENDPTLISTADDR_EPBASE_GET(x) (((uint32_t)(x) & USB_ENDPTLISTADDR_EPBASE_MASK) >> USB_ENDPTLISTADDR_EPBASE_SHIFT) + +/* Bitfield definition for register: BURSTSIZE */ +/* + * TXPBURST (RW) + * + * TXPBURST + * Programmable TX Burst Size. + * Default value is determined by TXBURST bits in n_HWTXBUF. + * This register represents the maximum length of a the burst in 32-bit words while moving data from system + * memory to the USB bus. + */ +#define USB_BURSTSIZE_TXPBURST_MASK (0xFF00U) +#define USB_BURSTSIZE_TXPBURST_SHIFT (8U) +#define USB_BURSTSIZE_TXPBURST_SET(x) (((uint32_t)(x) << USB_BURSTSIZE_TXPBURST_SHIFT) & USB_BURSTSIZE_TXPBURST_MASK) +#define USB_BURSTSIZE_TXPBURST_GET(x) (((uint32_t)(x) & USB_BURSTSIZE_TXPBURST_MASK) >> USB_BURSTSIZE_TXPBURST_SHIFT) + +/* + * RXPBURST (RW) + * + * RXPBURST + * Programmable RX Burst Size. + * Default value is determined by TXBURST bits in n_HWRXBUF. + * This register represents the maximum length of a the burst in 32-bit words while moving data from the + * USB bus to system memory. + */ +#define USB_BURSTSIZE_RXPBURST_MASK (0xFFU) +#define USB_BURSTSIZE_RXPBURST_SHIFT (0U) +#define USB_BURSTSIZE_RXPBURST_SET(x) (((uint32_t)(x) << USB_BURSTSIZE_RXPBURST_SHIFT) & USB_BURSTSIZE_RXPBURST_MASK) +#define USB_BURSTSIZE_RXPBURST_GET(x) (((uint32_t)(x) & USB_BURSTSIZE_RXPBURST_MASK) >> USB_BURSTSIZE_RXPBURST_SHIFT) + +/* Bitfield definition for register: TXFILLTUNING */ +/* + * TXFIFOTHRES (RW) + * + * TXFIFOTHRES + * FIFO Burst Threshold. (Read/Write) + * This register controls the number of data bursts that are posted to the TX latency FIFO in host mode before the packet begins on to the bus. + * The minimum value is 2 and this value should be a low as possible to maximize USB performance. + * A higher value can be used in systems with unpredictable latency and/or insufficient bandwidth + * where the FIFO may underrun because the data transferred from the latency FIFO to USB occurs before it can be replenished from system memory. + * This value is ignored if the Stream Disable bit in USB_n_USBMODE register is set. + */ +#define USB_TXFILLTUNING_TXFIFOTHRES_MASK (0x3F0000UL) +#define USB_TXFILLTUNING_TXFIFOTHRES_SHIFT (16U) +#define USB_TXFILLTUNING_TXFIFOTHRES_SET(x) (((uint32_t)(x) << USB_TXFILLTUNING_TXFIFOTHRES_SHIFT) & USB_TXFILLTUNING_TXFIFOTHRES_MASK) +#define USB_TXFILLTUNING_TXFIFOTHRES_GET(x) (((uint32_t)(x) & USB_TXFILLTUNING_TXFIFOTHRES_MASK) >> USB_TXFILLTUNING_TXFIFOTHRES_SHIFT) + +/* + * TXSCHHEALTH (RWC) + * + * TXSCHHEALTH + * Scheduler Health Counter. (Read/Write To Clear) + * Table continues on the next page + * This register increments when the host controller fails to fill the TX latency FIFO to the level programmed by TXFIFOTHRES + * before running out of time to send the packet before the next Start-Of-Frame. + * This health counter measures the number of times this occurs to provide feedback to selecting a proper TXSCHOH. + * Writing to this register will clear the counter and this counter will max. at 31. + */ +#define USB_TXFILLTUNING_TXSCHHEALTH_MASK (0x1F00U) +#define USB_TXFILLTUNING_TXSCHHEALTH_SHIFT (8U) +#define USB_TXFILLTUNING_TXSCHHEALTH_SET(x) (((uint32_t)(x) << USB_TXFILLTUNING_TXSCHHEALTH_SHIFT) & USB_TXFILLTUNING_TXSCHHEALTH_MASK) +#define USB_TXFILLTUNING_TXSCHHEALTH_GET(x) (((uint32_t)(x) & USB_TXFILLTUNING_TXSCHHEALTH_MASK) >> USB_TXFILLTUNING_TXSCHHEALTH_SHIFT) + +/* + * TXSCHOH (RW) + * + * TXSCHOH + * Scheduler Overhead. (Read/Write) [Default = 0] + * This register adds an additional fixed offset to the schedule time estimator described above as Tff. + * As an approximation, the value chosen for this register should limit the number of back-off events captured + * in the TXSCHHEALTH to less than 10 per second in a highly utilized bus. + * Choosing a value that is too high for this register is not desired as it can needlessly reduce USB utilization. + * The time unit represented in this register is 1.267us when a device is connected in High-Speed Mode. + * The time unit represented in this register is 6.333us when a device is connected in Low/Full Speed Mode. + * Default value is '08h' for OTG controller core . + */ +#define USB_TXFILLTUNING_TXSCHOH_MASK (0x7FU) +#define USB_TXFILLTUNING_TXSCHOH_SHIFT (0U) +#define USB_TXFILLTUNING_TXSCHOH_SET(x) (((uint32_t)(x) << USB_TXFILLTUNING_TXSCHOH_SHIFT) & USB_TXFILLTUNING_TXSCHOH_MASK) +#define USB_TXFILLTUNING_TXSCHOH_GET(x) (((uint32_t)(x) & USB_TXFILLTUNING_TXSCHOH_MASK) >> USB_TXFILLTUNING_TXSCHOH_SHIFT) + +/* Bitfield definition for register: ENDPTNAK */ +/* + * EPTN (RWC) + * + * EPTN + * TX Endpoint NAK - R/WC. + * Each TX endpoint has 1 bit in this field. The bit is set when the + * device sends a NAK handshake on a received IN token for the corresponding endpoint. + * Bit [N] - Endpoint #[N], N is 0-7 + */ +#define USB_ENDPTNAK_EPTN_MASK (0xFFFF0000UL) +#define USB_ENDPTNAK_EPTN_SHIFT (16U) +#define USB_ENDPTNAK_EPTN_SET(x) (((uint32_t)(x) << USB_ENDPTNAK_EPTN_SHIFT) & USB_ENDPTNAK_EPTN_MASK) +#define USB_ENDPTNAK_EPTN_GET(x) (((uint32_t)(x) & USB_ENDPTNAK_EPTN_MASK) >> USB_ENDPTNAK_EPTN_SHIFT) + +/* + * EPRN (RWC) + * + * EPRN + * RX Endpoint NAK - R/WC. + * Each RX endpoint has 1 bit in this field. The bit is set when the + * device sends a NAK handshake on a received OUT or PING token for the corresponding endpoint. + * Bit [N] - Endpoint #[N], N is 0-7 + */ +#define USB_ENDPTNAK_EPRN_MASK (0xFFFFU) +#define USB_ENDPTNAK_EPRN_SHIFT (0U) +#define USB_ENDPTNAK_EPRN_SET(x) (((uint32_t)(x) << USB_ENDPTNAK_EPRN_SHIFT) & USB_ENDPTNAK_EPRN_MASK) +#define USB_ENDPTNAK_EPRN_GET(x) (((uint32_t)(x) & USB_ENDPTNAK_EPRN_MASK) >> USB_ENDPTNAK_EPRN_SHIFT) + +/* Bitfield definition for register: ENDPTNAKEN */ +/* + * EPTNE (RW) + * + * EPTNE + * TX Endpoint NAK Enable - R/W. + * Each bit is an enable bit for the corresponding TX Endpoint NAK bit. If this bit is set and the + * corresponding TX Endpoint NAK bit is set, the NAK Interrupt bit is set. + * Bit [N] - Endpoint #[N], N is 0-7 + */ +#define USB_ENDPTNAKEN_EPTNE_MASK (0xFFFF0000UL) +#define USB_ENDPTNAKEN_EPTNE_SHIFT (16U) +#define USB_ENDPTNAKEN_EPTNE_SET(x) (((uint32_t)(x) << USB_ENDPTNAKEN_EPTNE_SHIFT) & USB_ENDPTNAKEN_EPTNE_MASK) +#define USB_ENDPTNAKEN_EPTNE_GET(x) (((uint32_t)(x) & USB_ENDPTNAKEN_EPTNE_MASK) >> USB_ENDPTNAKEN_EPTNE_SHIFT) + +/* + * EPRNE (RW) + * + * EPRNE + * RX Endpoint NAK Enable - R/W. + * Each bit is an enable bit for the corresponding RX Endpoint NAK bit. If this bit is set and the + * corresponding RX Endpoint NAK bit is set, the NAK Interrupt bit is set. + * Bit [N] - Endpoint #[N], N is 0-7 + */ +#define USB_ENDPTNAKEN_EPRNE_MASK (0xFFFFU) +#define USB_ENDPTNAKEN_EPRNE_SHIFT (0U) +#define USB_ENDPTNAKEN_EPRNE_SET(x) (((uint32_t)(x) << USB_ENDPTNAKEN_EPRNE_SHIFT) & USB_ENDPTNAKEN_EPRNE_MASK) +#define USB_ENDPTNAKEN_EPRNE_GET(x) (((uint32_t)(x) & USB_ENDPTNAKEN_EPRNE_MASK) >> USB_ENDPTNAKEN_EPRNE_SHIFT) + +/* Bitfield definition for register: PORTSC1 */ +/* + * STS (RW) + * + * STS + * Serial Transceiver Select + * 1 Serial Interface Engine is selected + * 0 Parallel Interface signals is selected + * Serial Interface Engine can be used in combination with UTMI+/ULPI physical interface to provide FS/LS signaling instead of the parallel interface signals. + * When this bit is set '1b', serial interface engine will be used instead of parallel interface signals. + */ +#define USB_PORTSC1_STS_MASK (0x20000000UL) +#define USB_PORTSC1_STS_SHIFT (29U) +#define USB_PORTSC1_STS_SET(x) (((uint32_t)(x) << USB_PORTSC1_STS_SHIFT) & USB_PORTSC1_STS_MASK) +#define USB_PORTSC1_STS_GET(x) (((uint32_t)(x) & USB_PORTSC1_STS_MASK) >> USB_PORTSC1_STS_SHIFT) + +/* + * PTW (RW) + * + * PTW + * Parallel Transceiver Width + * This bit has no effect if serial interface engine is used. + * 0 - Select the 8-bit UTMI interface [60MHz] + * 1 - Select the 16-bit UTMI interface [30MHz] + */ +#define USB_PORTSC1_PTW_MASK (0x10000000UL) +#define USB_PORTSC1_PTW_SHIFT (28U) +#define USB_PORTSC1_PTW_SET(x) (((uint32_t)(x) << USB_PORTSC1_PTW_SHIFT) & USB_PORTSC1_PTW_MASK) +#define USB_PORTSC1_PTW_GET(x) (((uint32_t)(x) & USB_PORTSC1_PTW_MASK) >> USB_PORTSC1_PTW_SHIFT) + +/* + * PSPD (RO) + * + * PSPD + * Port Speed - Read Only. + * This register field indicates the speed at which the port is operating. + * 00 - Full Speed + * 01 - Low Speed + * 10 - High Speed + * 11 - Undefined + */ +#define USB_PORTSC1_PSPD_MASK (0xC000000UL) +#define USB_PORTSC1_PSPD_SHIFT (26U) +#define USB_PORTSC1_PSPD_GET(x) (((uint32_t)(x) & USB_PORTSC1_PSPD_MASK) >> USB_PORTSC1_PSPD_SHIFT) + +/* + * PFSC (RW) + * + * PFSC + * Port Force Full Speed Connect - Read/Write. Default = 0b. + * When this bit is set to '1b', the port will be forced to only connect at Full Speed, It disables the chirp + * sequence that allows the port to identify itself as High Speed. + * 0 - Normal operation + * 1 - Forced to full speed + */ +#define USB_PORTSC1_PFSC_MASK (0x1000000UL) +#define USB_PORTSC1_PFSC_SHIFT (24U) +#define USB_PORTSC1_PFSC_SET(x) (((uint32_t)(x) << USB_PORTSC1_PFSC_SHIFT) & USB_PORTSC1_PFSC_MASK) +#define USB_PORTSC1_PFSC_GET(x) (((uint32_t)(x) & USB_PORTSC1_PFSC_MASK) >> USB_PORTSC1_PFSC_SHIFT) + +/* + * PHCD (RW) + * + * PHCD + * PHY Low Power Suspend - Clock Disable (PLPSCD) - Read/Write. Default = 0b. + * When this bit is set to '1b', the PHY clock is disabled. Reading this bit will indicate the status of the PHY + * clock. + * NOTE: The PHY clock cannot be disabled if it is being used as the system clock. + * In device mode, The PHY can be put into Low Power Suspend when the device is not running (USBCMD + * Run/Stop=0b) or the host has signalled suspend (PORTSC1 SUSPEND=1b). PHY Low power suspend + * will be cleared automatically when the host initials resume. Before forcing a resume from the device, the + * device controller driver must clear this bit. + * In host mode, the PHY can be put into Low Power Suspend when the downstream device has been put + * into suspend mode or when no downstream device is connected. Low power suspend is completely + * under the control of software. + * 0 - Enable PHY clock + * 1 - Disable PHY clock + */ +#define USB_PORTSC1_PHCD_MASK (0x800000UL) +#define USB_PORTSC1_PHCD_SHIFT (23U) +#define USB_PORTSC1_PHCD_SET(x) (((uint32_t)(x) << USB_PORTSC1_PHCD_SHIFT) & USB_PORTSC1_PHCD_MASK) +#define USB_PORTSC1_PHCD_GET(x) (((uint32_t)(x) & USB_PORTSC1_PHCD_MASK) >> USB_PORTSC1_PHCD_SHIFT) + +/* + * WKOC (RW) + * + * WKOC + * Wake on Over-current Enable (WKOC_E) - Read/Write. Default = 0b. + * Writing this bit to a one enables the port to be sensitive to over-current conditions as wake-up events. + * This field is zero if Port Power(PORTSC1) is zero. + */ +#define USB_PORTSC1_WKOC_MASK (0x400000UL) +#define USB_PORTSC1_WKOC_SHIFT (22U) +#define USB_PORTSC1_WKOC_SET(x) (((uint32_t)(x) << USB_PORTSC1_WKOC_SHIFT) & USB_PORTSC1_WKOC_MASK) +#define USB_PORTSC1_WKOC_GET(x) (((uint32_t)(x) & USB_PORTSC1_WKOC_MASK) >> USB_PORTSC1_WKOC_SHIFT) + +/* + * WKDC (RW) + * + * WKDC + * Wake on Disconnect Enable (WKDSCNNT_E) - Read/Write. Default=0b. Writing this bit to a one enables + * the port to be sensitive to device disconnects as wake-up events. + * This field is zero if Port Power(PORTSC1) is zero or in device mode. + */ +#define USB_PORTSC1_WKDC_MASK (0x200000UL) +#define USB_PORTSC1_WKDC_SHIFT (21U) +#define USB_PORTSC1_WKDC_SET(x) (((uint32_t)(x) << USB_PORTSC1_WKDC_SHIFT) & USB_PORTSC1_WKDC_MASK) +#define USB_PORTSC1_WKDC_GET(x) (((uint32_t)(x) & USB_PORTSC1_WKDC_MASK) >> USB_PORTSC1_WKDC_SHIFT) + +/* + * WKCN (RW) + * + * WKCN + * Wake on Connect Enable (WKCNNT_E) - Read/Write. Default=0b. + * Writing this bit to a one enables the port to be sensitive to device connects as wake-up events. + * This field is zero if Port Power(PORTSC1) is zero or in device mode. + */ +#define USB_PORTSC1_WKCN_MASK (0x100000UL) +#define USB_PORTSC1_WKCN_SHIFT (20U) +#define USB_PORTSC1_WKCN_SET(x) (((uint32_t)(x) << USB_PORTSC1_WKCN_SHIFT) & USB_PORTSC1_WKCN_MASK) +#define USB_PORTSC1_WKCN_GET(x) (((uint32_t)(x) & USB_PORTSC1_WKCN_MASK) >> USB_PORTSC1_WKCN_SHIFT) + +/* + * PTC (RW) + * + * PTC + * Port Test Control - Read/Write. Default = 0000b. + * Refer to Port Test Mode for the operational model for using these test modes and the USB Specification Revision 2.0, Chapter 7 for details on each test mode. + * The FORCE_ENABLE_FS and FORCE ENABLE_LS are extensions to the test mode support specified in the EHCI specification. + * Writing the PTC field to any of the FORCE_ENABLE_{HS/FS/LS} values will force the port into the connected and enabled state at the selected speed. + * Writing the PTC field back to TEST_MODE_DISABLE will allow the port state machines to progress normally from that point. + * NOTE: Low speed operations are not supported as a peripheral device. + * Any other value than zero indicates that the port is operating in test mode. + * Value Specific Test + * 0000 - TEST_MODE_DISABLE + * 0001 - J_STATE + * 0010 - K_STATE + * 0011 - SE0 (host) / NAK (device) + * 0100 - Packet + * 0101 - FORCE_ENABLE_HS + * 0110 - FORCE_ENABLE_FS + * 0111 - FORCE_ENABLE_LS + * 1000-1111 - Reserved + */ +#define USB_PORTSC1_PTC_MASK (0xF0000UL) +#define USB_PORTSC1_PTC_SHIFT (16U) +#define USB_PORTSC1_PTC_SET(x) (((uint32_t)(x) << USB_PORTSC1_PTC_SHIFT) & USB_PORTSC1_PTC_MASK) +#define USB_PORTSC1_PTC_GET(x) (((uint32_t)(x) & USB_PORTSC1_PTC_MASK) >> USB_PORTSC1_PTC_SHIFT) + +/* + * PP (RW) + * + * PP + * Port Power (PP)-Read/Write or Read Only. + * The function of this bit depends on the value of the Port Power Switching (PPC) field in the HCSPARAMS register. The behavior is as follows: + * PPC + * PP Operation + * 0 + * 1b Read Only - Host controller does not have port power control switches. Each port is hard-wired to power. + * 1 + * 1b/0b - Read/Write. OTG controller requires port power control switches. This bit represents the current setting of the switch (0=off, 1=on). + * When power is not available on a port (that is, PP equals a 0), the port is non-functional and will not report attaches, detaches, etc. + * When an over-current condition is detected on a powered port and PPC is a one, + * the PP bit in each affected port may be transitional by the host controller driver from a one to a zero (removing power from the port). + * This feature is implemented in all controller cores (PPC = 1). + */ +#define USB_PORTSC1_PP_MASK (0x1000U) +#define USB_PORTSC1_PP_SHIFT (12U) +#define USB_PORTSC1_PP_SET(x) (((uint32_t)(x) << USB_PORTSC1_PP_SHIFT) & USB_PORTSC1_PP_MASK) +#define USB_PORTSC1_PP_GET(x) (((uint32_t)(x) & USB_PORTSC1_PP_MASK) >> USB_PORTSC1_PP_SHIFT) + +/* + * LS (RO) + * + * LS + * Line Status-Read Only. These bits reflect the current logical levels of the D+ (bit 11) and D- (bit 10) signal + * lines. + * In host mode, the use of linestate by the host controller driver is not necessary (unlike EHCI), because + * the port controller state machine and the port routing manage the connection of LS and FS. + * In device mode, the use of linestate by the device controller driver is not necessary. + * The encoding of the bits are: + * Bits [11:10] Meaning + * 00 - SE0 + * 01 - K-state + * 10 - J-state + * 11 - Undefined + */ +#define USB_PORTSC1_LS_MASK (0xC00U) +#define USB_PORTSC1_LS_SHIFT (10U) +#define USB_PORTSC1_LS_GET(x) (((uint32_t)(x) & USB_PORTSC1_LS_MASK) >> USB_PORTSC1_LS_SHIFT) + +/* + * HSP (RO) + * + * HSP + * High-Speed Port - Read Only. Default = 0b. + * When the bit is one, the host/device connected to the port is in high-speed mode and if set to zero, the + * host/device connected to the port is not in a high-speed mode. + * NOTE: HSP is redundant with PSPD(bit 27, 26) but remained for compatibility. + */ +#define USB_PORTSC1_HSP_MASK (0x200U) +#define USB_PORTSC1_HSP_SHIFT (9U) +#define USB_PORTSC1_HSP_GET(x) (((uint32_t)(x) & USB_PORTSC1_HSP_MASK) >> USB_PORTSC1_HSP_SHIFT) + +/* + * PR (RW) + * + * PR + * Port Reset - Read/Write or Read Only. Default = 0b. + * In Host Mode: Read/Write. 1=Port is in Reset. 0=Port is not in Reset. Default 0. + * When software writes a one to this bit the bus-reset sequence as defined in the USB Specification Revision 2.0 is started. + * This bit will automatically change to zero after the reset sequence is complete. + * This behavior is different from EHCI where the host controller driver is required to set this bit to a zero after the reset duration is timed in the driver. + * In Device Mode: This bit is a read only status bit. Device reset from the USB bus is also indicated in the USBSTS register. + */ +#define USB_PORTSC1_PR_MASK (0x100U) +#define USB_PORTSC1_PR_SHIFT (8U) +#define USB_PORTSC1_PR_SET(x) (((uint32_t)(x) << USB_PORTSC1_PR_SHIFT) & USB_PORTSC1_PR_MASK) +#define USB_PORTSC1_PR_GET(x) (((uint32_t)(x) & USB_PORTSC1_PR_MASK) >> USB_PORTSC1_PR_SHIFT) + +/* + * SUSP (RW) + * + * SUSP + * Suspend - Read/Write or Read Only. Default = 0b. + * 1=Port in suspend state. 0=Port not in suspend state. + * In Host Mode: Read/Write. + * Port Enabled Bit and Suspend bit of this register define the port states as follows: + * Bits [Port Enabled, Suspend] Port State + * 0x Disable + * 10 Enable + * 11 Suspend + * When in suspend state, downstream propagation of data is blocked on this port, except for port reset. + * The blocking occurs at the end of the current transaction if a transaction was in progress when this bit was written to 1. + * In the suspend state, the port is sensitive to resume detection. + * Note that the bit status does not change until the port is suspended and that there may be a delay in suspending a port if there is a transaction currently in progress on the USB. + * The host controller will unconditionally set this bit to zero when software sets the Force Port Resume bit to zero. The host controller ignores a write of zero to this bit. + * If host software sets this bit to a one when the port is not enabled (that is, Port enabled bit is a zero) the results are undefined. + * This field is zero if Port Power(PORTSC1) is zero in host mode. + * In Device Mode: Read Only. + * In device mode this bit is a read only status bit. + */ +#define USB_PORTSC1_SUSP_MASK (0x80U) +#define USB_PORTSC1_SUSP_SHIFT (7U) +#define USB_PORTSC1_SUSP_SET(x) (((uint32_t)(x) << USB_PORTSC1_SUSP_SHIFT) & USB_PORTSC1_SUSP_MASK) +#define USB_PORTSC1_SUSP_GET(x) (((uint32_t)(x) & USB_PORTSC1_SUSP_MASK) >> USB_PORTSC1_SUSP_SHIFT) + +/* + * FPR (RW) + * + * FPR + * Force Port Resume -Read/Write. 1= Resume detected/driven on port. 0=No resume (K-state) detected driven on port. Default = 0. + * In Host Mode: + * Software sets this bit to one to drive resume signaling. The Host Controller sets this bit to one if a J-to-K transition is detected while the port is in the Suspend state. + * When this bit transitions to a one because a J-to-K transition is detected, the Port Change Detect bit in the USBSTS register is also set to one. + * This bit will automatically change to zero after the resume sequence is complete. + * This behavior is different from EHCI where the host controller driver is required to set this bit to a zero after the resume duration is timed in the driver. + * Note that when the Host controller owns the port, the resume sequence follows the defined sequence documented in the USB Specification Revision 2.0. + * The resume signaling (Full-speed 'K') is driven on the port as long as this bit remains a one. This bit will remain a one until the port has switched to the high-speed idle. + * Writing a zero has no effect because the port controller will time the resume operation, clear the bit the port control state switches to HS or FS idle. + * This field is zero if Port Power(PORTSC1) is zero in host mode. + * This bit is not-EHCI compatible. + * In Device mode: + * After the device has been in Suspend State for 5ms or more, software must set this bit to one to drive resume signaling before clearing. + * The Device Controller will set this bit to one if a J-to-K transition is detected while the port is in the Suspend state. + * The bit will be cleared when the device returns to normal operation. + * Also, when this bit wil be cleared because a K-to-J transition detected, the Port Change Detect bit in the USBSTS register is also set to one. + */ +#define USB_PORTSC1_FPR_MASK (0x40U) +#define USB_PORTSC1_FPR_SHIFT (6U) +#define USB_PORTSC1_FPR_SET(x) (((uint32_t)(x) << USB_PORTSC1_FPR_SHIFT) & USB_PORTSC1_FPR_MASK) +#define USB_PORTSC1_FPR_GET(x) (((uint32_t)(x) & USB_PORTSC1_FPR_MASK) >> USB_PORTSC1_FPR_SHIFT) + +/* + * OCC (RW) + * + * OCC + * Over-current Change-R/WC. Default=0. + * This bit is set '1b' by hardware when there is a change to Over-current Active. Software can clear this bit by writing a one to this bit position. + */ +#define USB_PORTSC1_OCC_MASK (0x20U) +#define USB_PORTSC1_OCC_SHIFT (5U) +#define USB_PORTSC1_OCC_SET(x) (((uint32_t)(x) << USB_PORTSC1_OCC_SHIFT) & USB_PORTSC1_OCC_MASK) +#define USB_PORTSC1_OCC_GET(x) (((uint32_t)(x) & USB_PORTSC1_OCC_MASK) >> USB_PORTSC1_OCC_SHIFT) + +/* + * OCA (RO) + * + * OCA + * Over-current Active-Read Only. Default 0. + * This bit will automatically transition from one to zero when the over current condition is removed. + * 0 - This port does not have an over-current condition. + * 1 - This port currently has an over-current condition + */ +#define USB_PORTSC1_OCA_MASK (0x10U) +#define USB_PORTSC1_OCA_SHIFT (4U) +#define USB_PORTSC1_OCA_GET(x) (((uint32_t)(x) & USB_PORTSC1_OCA_MASK) >> USB_PORTSC1_OCA_SHIFT) + +/* + * PEC (RWC) + * + * PEC + * Port Enable/Disable Change-R/WC. 1=Port enabled/disabled status has changed. 0=No change. Default = 0. + * In Host Mode: + * For the root hub, this bit is set to a one only when a port is disabled due to disconnect on the port or + * due to the appropriate conditions existing at the EOF2 point (See Chapter 11 of the USB Specification). + * Software clears this by writing a one to it. + * This field is zero if Port Power(PORTSC1) is zero. + * In Device mode: + * The device port is always enabled, so this bit is always '0b'. + */ +#define USB_PORTSC1_PEC_MASK (0x8U) +#define USB_PORTSC1_PEC_SHIFT (3U) +#define USB_PORTSC1_PEC_SET(x) (((uint32_t)(x) << USB_PORTSC1_PEC_SHIFT) & USB_PORTSC1_PEC_MASK) +#define USB_PORTSC1_PEC_GET(x) (((uint32_t)(x) & USB_PORTSC1_PEC_MASK) >> USB_PORTSC1_PEC_SHIFT) + +/* + * PE (RWC) + * + * PE + * Port Enabled/Disabled-Read/Write. 1=Enable. 0=Disable. Default 0. + * In Host Mode: + * Ports can only be enabled by the host controller as a part of the reset and enable. Software cannot enable a port by writing a one to this field. + * Ports can be disabled by either a fault condition (disconnect event or other fault condition) or by the host software. + * Note that the bit status does not change until the port state actually changes. There may be a delay in disabling or enabling a port due to other host controller and bus events. + * When the port is disabled, (0b) downstream propagation of data is blocked except for reset. + * This field is zero if Port Power(PORTSC1) is zero in host mode. + * In Device Mode: + * The device port is always enabled, so this bit is always '1b'. + */ +#define USB_PORTSC1_PE_MASK (0x4U) +#define USB_PORTSC1_PE_SHIFT (2U) +#define USB_PORTSC1_PE_SET(x) (((uint32_t)(x) << USB_PORTSC1_PE_SHIFT) & USB_PORTSC1_PE_MASK) +#define USB_PORTSC1_PE_GET(x) (((uint32_t)(x) & USB_PORTSC1_PE_MASK) >> USB_PORTSC1_PE_SHIFT) + +/* + * CSC (RWC) + * + * CSC + * Connect Status Change-R/WC. 1 =Change in Current Connect Status. 0=No change. Default 0. + * In Host Mode: + * Indicates a change has occurred in the port's Current Connect Status. + * The host/device controller sets this bit for all changes to the port device connect status, even if system software has not cleared an existing connect status change. + * For example, the insertion status changes twice before system software has cleared the changed condition, + * hub hardware will be 'setting' an already-set bit (that is, the bit will remain set). Software clears this bit by writing a one to it. + * This field is zero if Port Power(PORTSC1) is zero in host mode. + * In Device Mode: + * This bit is undefined in device controller mode. + */ +#define USB_PORTSC1_CSC_MASK (0x2U) +#define USB_PORTSC1_CSC_SHIFT (1U) +#define USB_PORTSC1_CSC_SET(x) (((uint32_t)(x) << USB_PORTSC1_CSC_SHIFT) & USB_PORTSC1_CSC_MASK) +#define USB_PORTSC1_CSC_GET(x) (((uint32_t)(x) & USB_PORTSC1_CSC_MASK) >> USB_PORTSC1_CSC_SHIFT) + +/* + * CCS (RWC) + * + * CCS + * Current Connect Status-Read Only. + * In Host Mode: + * 1=Device is present on port. 0=No device is present. Default = 0. + * This value reflects the current state of the port, and may not correspond directly to the event that caused the Connect Status Change bit (Bit 1) to be set. + * This field is zero if Port Power(PORTSC1) is zero in host mode. + * In Device Mode: + * 1=Attached. 0=Not Attached. Default=0. + * A one indicates that the device successfully attached and is operating in either high speed or full speed as indicated by the High Speed Port bit in this register. + * A zero indicates that the device did not attach successfully or was forcibly disconnected by the software writing a zero to the Run bit in the USBCMD register. + * It does not state the device being disconnected or Suspended. + */ +#define USB_PORTSC1_CCS_MASK (0x1U) +#define USB_PORTSC1_CCS_SHIFT (0U) +#define USB_PORTSC1_CCS_SET(x) (((uint32_t)(x) << USB_PORTSC1_CCS_SHIFT) & USB_PORTSC1_CCS_MASK) +#define USB_PORTSC1_CCS_GET(x) (((uint32_t)(x) & USB_PORTSC1_CCS_MASK) >> USB_PORTSC1_CCS_SHIFT) + +/* Bitfield definition for register: OTGSC */ +/* + * ASVIE (RW) + * + * ASVIE + * A Session Valid Interrupt Enable - Read/Write. + */ +#define USB_OTGSC_ASVIE_MASK (0x4000000UL) +#define USB_OTGSC_ASVIE_SHIFT (26U) +#define USB_OTGSC_ASVIE_SET(x) (((uint32_t)(x) << USB_OTGSC_ASVIE_SHIFT) & USB_OTGSC_ASVIE_MASK) +#define USB_OTGSC_ASVIE_GET(x) (((uint32_t)(x) & USB_OTGSC_ASVIE_MASK) >> USB_OTGSC_ASVIE_SHIFT) + +/* + * AVVIE (RW) + * + * AVVIE + * A VBus Valid Interrupt Enable - Read/Write. + * Setting this bit enables the A VBus valid interrupt. + */ +#define USB_OTGSC_AVVIE_MASK (0x2000000UL) +#define USB_OTGSC_AVVIE_SHIFT (25U) +#define USB_OTGSC_AVVIE_SET(x) (((uint32_t)(x) << USB_OTGSC_AVVIE_SHIFT) & USB_OTGSC_AVVIE_MASK) +#define USB_OTGSC_AVVIE_GET(x) (((uint32_t)(x) & USB_OTGSC_AVVIE_MASK) >> USB_OTGSC_AVVIE_SHIFT) + +/* + * IDIE (RW) + * + * IDIE + * USB ID Interrupt Enable - Read/Write. + * Setting this bit enables the USB ID interrupt. + */ +#define USB_OTGSC_IDIE_MASK (0x1000000UL) +#define USB_OTGSC_IDIE_SHIFT (24U) +#define USB_OTGSC_IDIE_SET(x) (((uint32_t)(x) << USB_OTGSC_IDIE_SHIFT) & USB_OTGSC_IDIE_MASK) +#define USB_OTGSC_IDIE_GET(x) (((uint32_t)(x) & USB_OTGSC_IDIE_MASK) >> USB_OTGSC_IDIE_SHIFT) + +/* + * ASVIS (RWC) + * + * ASVIS + * A Session Valid Interrupt Status - Read/Write to Clear. + * This bit is set when VBus has either risen above or fallen below the A session valid threshold. + * Software must write a one to clear this bit. + */ +#define USB_OTGSC_ASVIS_MASK (0x40000UL) +#define USB_OTGSC_ASVIS_SHIFT (18U) +#define USB_OTGSC_ASVIS_SET(x) (((uint32_t)(x) << USB_OTGSC_ASVIS_SHIFT) & USB_OTGSC_ASVIS_MASK) +#define USB_OTGSC_ASVIS_GET(x) (((uint32_t)(x) & USB_OTGSC_ASVIS_MASK) >> USB_OTGSC_ASVIS_SHIFT) + +/* + * AVVIS (RWC) + * + * AVVIS + * A VBus Valid Interrupt Status - Read/Write to Clear. + * This bit is set when VBus has either risen above or fallen below the VBus valid threshold on an A device. + * Software must write a one to clear this bit. + */ +#define USB_OTGSC_AVVIS_MASK (0x20000UL) +#define USB_OTGSC_AVVIS_SHIFT (17U) +#define USB_OTGSC_AVVIS_SET(x) (((uint32_t)(x) << USB_OTGSC_AVVIS_SHIFT) & USB_OTGSC_AVVIS_MASK) +#define USB_OTGSC_AVVIS_GET(x) (((uint32_t)(x) & USB_OTGSC_AVVIS_MASK) >> USB_OTGSC_AVVIS_SHIFT) + +/* + * IDIS (RWC) + * + * IDIS + * USB ID Interrupt Status - Read/Write. + * This bit is set when a change on the ID input has been detected. + * Software must write a one to clear this bit. + */ +#define USB_OTGSC_IDIS_MASK (0x10000UL) +#define USB_OTGSC_IDIS_SHIFT (16U) +#define USB_OTGSC_IDIS_SET(x) (((uint32_t)(x) << USB_OTGSC_IDIS_SHIFT) & USB_OTGSC_IDIS_MASK) +#define USB_OTGSC_IDIS_GET(x) (((uint32_t)(x) & USB_OTGSC_IDIS_MASK) >> USB_OTGSC_IDIS_SHIFT) + +/* + * ASV (RO) + * + * ASV + * A Session Valid - Read Only. + * Indicates VBus is above the A session valid threshold. + */ +#define USB_OTGSC_ASV_MASK (0x400U) +#define USB_OTGSC_ASV_SHIFT (10U) +#define USB_OTGSC_ASV_GET(x) (((uint32_t)(x) & USB_OTGSC_ASV_MASK) >> USB_OTGSC_ASV_SHIFT) + +/* + * AVV (RO) + * + * AVV + * A VBus Valid - Read Only. + * Indicates VBus is above the A VBus valid threshold. + */ +#define USB_OTGSC_AVV_MASK (0x200U) +#define USB_OTGSC_AVV_SHIFT (9U) +#define USB_OTGSC_AVV_GET(x) (((uint32_t)(x) & USB_OTGSC_AVV_MASK) >> USB_OTGSC_AVV_SHIFT) + +/* + * ID (RO) + * + * ID + * USB ID - Read Only. + * 0 = A device, 1 = B device + */ +#define USB_OTGSC_ID_MASK (0x100U) +#define USB_OTGSC_ID_SHIFT (8U) +#define USB_OTGSC_ID_GET(x) (((uint32_t)(x) & USB_OTGSC_ID_MASK) >> USB_OTGSC_ID_SHIFT) + +/* + * IDPU (RW) + * + * IDPU + * ID Pullup - Read/Write + * This bit provide control over the ID pull-up resistor; 0 = off, 1 = on [default]. When this bit is 0, the ID input + * will not be sampled. + */ +#define USB_OTGSC_IDPU_MASK (0x20U) +#define USB_OTGSC_IDPU_SHIFT (5U) +#define USB_OTGSC_IDPU_SET(x) (((uint32_t)(x) << USB_OTGSC_IDPU_SHIFT) & USB_OTGSC_IDPU_MASK) +#define USB_OTGSC_IDPU_GET(x) (((uint32_t)(x) & USB_OTGSC_IDPU_MASK) >> USB_OTGSC_IDPU_SHIFT) + +/* + * VC (RW) + * + * VC + * VBUS Charge - Read/Write. + * Setting this bit causes the VBus line to be charged. This is used for VBus pulsing during SRP. + */ +#define USB_OTGSC_VC_MASK (0x2U) +#define USB_OTGSC_VC_SHIFT (1U) +#define USB_OTGSC_VC_SET(x) (((uint32_t)(x) << USB_OTGSC_VC_SHIFT) & USB_OTGSC_VC_MASK) +#define USB_OTGSC_VC_GET(x) (((uint32_t)(x) & USB_OTGSC_VC_MASK) >> USB_OTGSC_VC_SHIFT) + +/* + * VD (RW) + * + * VD + * VBUS_Discharge - Read/Write. + * Setting this bit causes VBus to discharge through a resistor. + */ +#define USB_OTGSC_VD_MASK (0x1U) +#define USB_OTGSC_VD_SHIFT (0U) +#define USB_OTGSC_VD_SET(x) (((uint32_t)(x) << USB_OTGSC_VD_SHIFT) & USB_OTGSC_VD_MASK) +#define USB_OTGSC_VD_GET(x) (((uint32_t)(x) & USB_OTGSC_VD_MASK) >> USB_OTGSC_VD_SHIFT) + +/* Bitfield definition for register: USBMODE */ +/* + * SDIS (RW) + * + * SDIS + * Stream Disable Mode. (0 - Inactive [default]; 1 - Active) + * Device Mode: Setting to a '1' disables double priming on both RX and TX for low bandwidth systems. + * This mode ensures that when the RX and TX buffers are sufficient to contain an entire packet that the standard double buffering scheme is disabled to prevent overruns/underruns in bandwidth limited systems. + * Note: In High Speed Mode, all packets received are responded to with a NYET handshake when stream disable is active. + * Host Mode: Setting to a '1' ensures that overruns/underruns of the latency FIFO are eliminated for low bandwidth systems + * where the RX and TX buffers are sufficient to contain the entire packet. Enabling stream disable also has the effect of ensuring the TX latency is filled to capacity before the packet is launched onto the USB. + * NOTE: Time duration to pre-fill the FIFO becomes significant when stream disable is active. See TXFILLTUNING and TXTTFILLTUNING [MPH Only] to characterize the adjustments needed for + * the scheduler when using this feature. + * NOTE: The use of this feature substantially limits of the overall USB performance that can be achieved. + */ +#define USB_USBMODE_SDIS_MASK (0x10U) +#define USB_USBMODE_SDIS_SHIFT (4U) +#define USB_USBMODE_SDIS_SET(x) (((uint32_t)(x) << USB_USBMODE_SDIS_SHIFT) & USB_USBMODE_SDIS_MASK) +#define USB_USBMODE_SDIS_GET(x) (((uint32_t)(x) & USB_USBMODE_SDIS_MASK) >> USB_USBMODE_SDIS_SHIFT) + +/* + * SLOM (RW) + * + * SLOM + * Setup Lockout Mode. In device mode, this bit controls behavior of the setup lock mechanism. See Control Endpoint Operation Model . + * 0 - Setup Lockouts On (default); + * 1 - Setup Lockouts Off. DCD requires use of Setup Data Buffer Tripwire in USBCMD. + */ +#define USB_USBMODE_SLOM_MASK (0x8U) +#define USB_USBMODE_SLOM_SHIFT (3U) +#define USB_USBMODE_SLOM_SET(x) (((uint32_t)(x) << USB_USBMODE_SLOM_SHIFT) & USB_USBMODE_SLOM_MASK) +#define USB_USBMODE_SLOM_GET(x) (((uint32_t)(x) & USB_USBMODE_SLOM_MASK) >> USB_USBMODE_SLOM_SHIFT) + +/* + * ES (RW) + * + * ES + * Endian Select - Read/Write. This bit can change the byte alignment of the transfer buffers to match the + * host microprocessor. The bit fields in the microprocessor interface and the data structures are unaffected + * by the value of this bit because they are based upon the 32-bit word. + * Bit Meaning + * 0 - Little Endian [Default] + * 1 - Big Endian + */ +#define USB_USBMODE_ES_MASK (0x4U) +#define USB_USBMODE_ES_SHIFT (2U) +#define USB_USBMODE_ES_SET(x) (((uint32_t)(x) << USB_USBMODE_ES_SHIFT) & USB_USBMODE_ES_MASK) +#define USB_USBMODE_ES_GET(x) (((uint32_t)(x) & USB_USBMODE_ES_MASK) >> USB_USBMODE_ES_SHIFT) + +/* + * CM (RW) + * + * CM + * Controller Mode - R/WO. Controller mode is defaulted to the proper mode for host only and device only + * implementations. For those designs that contain both host & device capability, the controller defaults to + * an idle state and needs to be initialized to the desired operating mode after reset. For combination host/ + * device controllers, this register can only be written once after reset. If it is necessary to switch modes, + * software must reset the controller by writing to the RESET bit in the USBCMD register before + * reprogramming this register. + * For OTG controller core, reset value is '00b'. + * 00 - Idle [Default for combination host/device] + * 01 - Reserved + * 10 - Device Controller [Default for device only controller] + * 11 - Host Controller [Default for host only controller] + */ +#define USB_USBMODE_CM_MASK (0x3U) +#define USB_USBMODE_CM_SHIFT (0U) +#define USB_USBMODE_CM_SET(x) (((uint32_t)(x) << USB_USBMODE_CM_SHIFT) & USB_USBMODE_CM_MASK) +#define USB_USBMODE_CM_GET(x) (((uint32_t)(x) & USB_USBMODE_CM_MASK) >> USB_USBMODE_CM_SHIFT) + +/* Bitfield definition for register: ENDPTSETUPSTAT */ +/* + * ENDPTSETUPSTAT (RWC) + * + * ENDPTSETUPSTAT + * Setup Endpoint Status. For every setup transaction that is received, a corresponding bit in this register is set to one. + * Software must clear or acknowledge the setup transfer by writing a one to a respective bit after it has read the setup data from Queue head. + * The response to a setup packet as in the order of operations and total response time is crucial to limit bus time outs while the setup lock out mechanism is engaged. + * This register is only used in device mode. + */ +#define USB_ENDPTSETUPSTAT_ENDPTSETUPSTAT_MASK (0xFFFFU) +#define USB_ENDPTSETUPSTAT_ENDPTSETUPSTAT_SHIFT (0U) +#define USB_ENDPTSETUPSTAT_ENDPTSETUPSTAT_SET(x) (((uint32_t)(x) << USB_ENDPTSETUPSTAT_ENDPTSETUPSTAT_SHIFT) & USB_ENDPTSETUPSTAT_ENDPTSETUPSTAT_MASK) +#define USB_ENDPTSETUPSTAT_ENDPTSETUPSTAT_GET(x) (((uint32_t)(x) & USB_ENDPTSETUPSTAT_ENDPTSETUPSTAT_MASK) >> USB_ENDPTSETUPSTAT_ENDPTSETUPSTAT_SHIFT) + +/* Bitfield definition for register: ENDPTPRIME */ +/* + * PETB (RWS) + * + * PETB + * Prime Endpoint Transmit Buffer - R/WS. For each endpoint a corresponding bit is used to request that a + * buffer is prepared for a transmit operation in order to respond to a USB IN/INTERRUPT transaction. + * Software should write a one to the corresponding bit when posting a new transfer descriptor to an + * endpoint queue head. Hardware automatically uses this bit to begin parsing for a new transfer descriptor + * from the queue head and prepare a transmit buffer. Hardware clears this bit when the associated + * endpoint(s) is (are) successfully primed. + * NOTE: These bits are momentarily set by hardware during hardware re-priming operations when a dTD + * is retired, and the dQH is updated. + * PETB[N] - Endpoint #N, N is in 0..7 + */ +#define USB_ENDPTPRIME_PETB_MASK (0xFFFF0000UL) +#define USB_ENDPTPRIME_PETB_SHIFT (16U) +#define USB_ENDPTPRIME_PETB_SET(x) (((uint32_t)(x) << USB_ENDPTPRIME_PETB_SHIFT) & USB_ENDPTPRIME_PETB_MASK) +#define USB_ENDPTPRIME_PETB_GET(x) (((uint32_t)(x) & USB_ENDPTPRIME_PETB_MASK) >> USB_ENDPTPRIME_PETB_SHIFT) + +/* + * PERB (RWS) + * + * PERB + * Prime Endpoint Receive Buffer - R/WS. For each endpoint, a corresponding bit is used to request a buffer prepare for a receive operation for when a USB host initiates a USB OUT transaction. + * Software should write a one to the corresponding bit whenever posting a new transfer descriptor to an endpoint queue head. + * Hardware automatically uses this bit to begin parsing for a new transfer descriptor from the queue head and prepare a receive buffer. + * Hardware clears this bit when the associated endpoint(s) is (are) successfully primed. + * NOTE: These bits are momentarily set by hardware during hardware re-priming operations when a dTD + * is retired, and the dQH is updated. + * PERB[N] - Endpoint #N, N is in 0..7 + */ +#define USB_ENDPTPRIME_PERB_MASK (0xFFFFU) +#define USB_ENDPTPRIME_PERB_SHIFT (0U) +#define USB_ENDPTPRIME_PERB_SET(x) (((uint32_t)(x) << USB_ENDPTPRIME_PERB_SHIFT) & USB_ENDPTPRIME_PERB_MASK) +#define USB_ENDPTPRIME_PERB_GET(x) (((uint32_t)(x) & USB_ENDPTPRIME_PERB_MASK) >> USB_ENDPTPRIME_PERB_SHIFT) + +/* Bitfield definition for register: ENDPTFLUSH */ +/* + * FETB (RWS) + * + * FETB + * Flush Endpoint Transmit Buffer - R/WS. Writing one to a bit(s) in this register causes the associated endpoint(s) to clear any primed buffers. + * If a packet is in progress for one of the associated endpoints, then that transfer continues until completion. + * Hardware clears this register after the endpoint flush operation is successful. + * FETB[N] - Endpoint #N, N is in 0..7 + */ +#define USB_ENDPTFLUSH_FETB_MASK (0xFFFF0000UL) +#define USB_ENDPTFLUSH_FETB_SHIFT (16U) +#define USB_ENDPTFLUSH_FETB_SET(x) (((uint32_t)(x) << USB_ENDPTFLUSH_FETB_SHIFT) & USB_ENDPTFLUSH_FETB_MASK) +#define USB_ENDPTFLUSH_FETB_GET(x) (((uint32_t)(x) & USB_ENDPTFLUSH_FETB_MASK) >> USB_ENDPTFLUSH_FETB_SHIFT) + +/* + * FERB (RWS) + * + * FERB + * Flush Endpoint Receive Buffer - R/WS. Writing one to a bit(s) causes the associated endpoint(s) to clear any primed buffers. + * If a packet is in progress for one of the associated endpoints, then that transfer continues until completion. + * Hardware clears this register after the endpoint flush operation is successful. + * FERB[N] - Endpoint #N, N is in 0..7 + */ +#define USB_ENDPTFLUSH_FERB_MASK (0xFFFFU) +#define USB_ENDPTFLUSH_FERB_SHIFT (0U) +#define USB_ENDPTFLUSH_FERB_SET(x) (((uint32_t)(x) << USB_ENDPTFLUSH_FERB_SHIFT) & USB_ENDPTFLUSH_FERB_MASK) +#define USB_ENDPTFLUSH_FERB_GET(x) (((uint32_t)(x) & USB_ENDPTFLUSH_FERB_MASK) >> USB_ENDPTFLUSH_FERB_SHIFT) + +/* Bitfield definition for register: ENDPTSTAT */ +/* + * ETBR (RO) + * + * ETBR + * Endpoint Transmit Buffer Ready -- Read Only. One bit for each endpoint indicates status of the respective endpoint buffer. + * This bit is set to one by the hardware as a response to receiving a command from a corresponding bit in the ENDPTPRIME register. + * There is always a delay between setting a bit in the ENDPTPRIME register and endpoint indicating ready. + * This delay time varies based upon the current USB traffic and the number of bits set in the ENDPRIME register. + * Buffer ready is cleared by USB reset, by the USB DMA system, or through the ENDPTFLUSH register. + * NOTE: These bits are momentarily cleared by hardware during hardware endpoint re-priming operations when a dTD is retired, and the dQH is updated. + * ETBR[N] - Endpoint #N, N is in 0..7 + */ +#define USB_ENDPTSTAT_ETBR_MASK (0xFFFF0000UL) +#define USB_ENDPTSTAT_ETBR_SHIFT (16U) +#define USB_ENDPTSTAT_ETBR_GET(x) (((uint32_t)(x) & USB_ENDPTSTAT_ETBR_MASK) >> USB_ENDPTSTAT_ETBR_SHIFT) + +/* + * ERBR (RO) + * + * ERBR + * Endpoint Receive Buffer Ready -- Read Only. One bit for each endpoint indicates status of the respective + * endpoint buffer. This bit is set to a one by the hardware as a response to receiving a command from a + * corresponding bit in the ENDPRIME register. There is always a delay between setting a bit in the + * ENDPRIME register and endpoint indicating ready. This delay time varies based upon the current USB + * traffic and the number of bits set in the ENDPRIME register. Buffer ready is cleared by USB reset, by the + * USB DMA system, or through the ENDPTFLUSH register. + * NOTE: These bits are momentarily cleared by hardware during hardware endpoint re-priming operations + * when a dTD is retired, and the dQH is updated. + * ERBR[N] - Endpoint #N, N is in 0..7 + */ +#define USB_ENDPTSTAT_ERBR_MASK (0xFFFFU) +#define USB_ENDPTSTAT_ERBR_SHIFT (0U) +#define USB_ENDPTSTAT_ERBR_GET(x) (((uint32_t)(x) & USB_ENDPTSTAT_ERBR_MASK) >> USB_ENDPTSTAT_ERBR_SHIFT) + +/* Bitfield definition for register: ENDPTCOMPLETE */ +/* + * ETCE (RWC) + * + * ETCE + * Endpoint Transmit Complete Event - R/WC. Each bit indicates a transmit event (IN/INTERRUPT) occurred and software should read the corresponding endpoint queue to determine the endpoint status. + * If the corresponding IOC bit is set in the Transfer Descriptor, then this bit is set simultaneously with the USBINT . Writing one clears the corresponding bit in this register. + * ETCE[N] - Endpoint #N, N is in 0..7 + */ +#define USB_ENDPTCOMPLETE_ETCE_MASK (0xFFFF0000UL) +#define USB_ENDPTCOMPLETE_ETCE_SHIFT (16U) +#define USB_ENDPTCOMPLETE_ETCE_SET(x) (((uint32_t)(x) << USB_ENDPTCOMPLETE_ETCE_SHIFT) & USB_ENDPTCOMPLETE_ETCE_MASK) +#define USB_ENDPTCOMPLETE_ETCE_GET(x) (((uint32_t)(x) & USB_ENDPTCOMPLETE_ETCE_MASK) >> USB_ENDPTCOMPLETE_ETCE_SHIFT) + +/* + * ERCE (RWC) + * + * ERCE + * Endpoint Receive Complete Event - RW/C. Each bit indicates a received event (OUT/SETUP) occurred + * and software should read the corresponding endpoint queue to determine the transfer status. If the + * corresponding IOC bit is set in the Transfer Descriptor, then this bit is set simultaneously with the + * USBINT . Writing one clears the corresponding bit in this register. + * ERCE[N] - Endpoint #N, N is in 0..7 + */ +#define USB_ENDPTCOMPLETE_ERCE_MASK (0xFFFFU) +#define USB_ENDPTCOMPLETE_ERCE_SHIFT (0U) +#define USB_ENDPTCOMPLETE_ERCE_SET(x) (((uint32_t)(x) << USB_ENDPTCOMPLETE_ERCE_SHIFT) & USB_ENDPTCOMPLETE_ERCE_MASK) +#define USB_ENDPTCOMPLETE_ERCE_GET(x) (((uint32_t)(x) & USB_ENDPTCOMPLETE_ERCE_MASK) >> USB_ENDPTCOMPLETE_ERCE_SHIFT) + +/* Bitfield definition for register array: ENDPTCTRL */ +/* + * TXE (RW) + * + * TXE + * TX Endpoint Enable + * 0 Disabled [Default] + * 1 Enabled + * An Endpoint should be enabled only after it has been configured. + */ +#define USB_ENDPTCTRL_TXE_MASK (0x800000UL) +#define USB_ENDPTCTRL_TXE_SHIFT (23U) +#define USB_ENDPTCTRL_TXE_SET(x) (((uint32_t)(x) << USB_ENDPTCTRL_TXE_SHIFT) & USB_ENDPTCTRL_TXE_MASK) +#define USB_ENDPTCTRL_TXE_GET(x) (((uint32_t)(x) & USB_ENDPTCTRL_TXE_MASK) >> USB_ENDPTCTRL_TXE_SHIFT) + +/* + * TXR (WS) + * + * TXR + * TX Data Toggle Reset (WS) + * Write 1 - Reset PID Sequence + * Whenever a configuration event is received for this Endpoint, software must write a one to this bit in order + * to synchronize the data PID's between the Host and device. + */ +#define USB_ENDPTCTRL_TXR_MASK (0x400000UL) +#define USB_ENDPTCTRL_TXR_SHIFT (22U) +#define USB_ENDPTCTRL_TXR_SET(x) (((uint32_t)(x) << USB_ENDPTCTRL_TXR_SHIFT) & USB_ENDPTCTRL_TXR_MASK) +#define USB_ENDPTCTRL_TXR_GET(x) (((uint32_t)(x) & USB_ENDPTCTRL_TXR_MASK) >> USB_ENDPTCTRL_TXR_SHIFT) + +/* + * TXT (RW) + * + * TXT + * TX Endpoint Type - Read/Write + * 00 Control + * 01 Isochronous + * 10 Bulk + * 11 Interrupt + */ +#define USB_ENDPTCTRL_TXT_MASK (0xC0000UL) +#define USB_ENDPTCTRL_TXT_SHIFT (18U) +#define USB_ENDPTCTRL_TXT_SET(x) (((uint32_t)(x) << USB_ENDPTCTRL_TXT_SHIFT) & USB_ENDPTCTRL_TXT_MASK) +#define USB_ENDPTCTRL_TXT_GET(x) (((uint32_t)(x) & USB_ENDPTCTRL_TXT_MASK) >> USB_ENDPTCTRL_TXT_SHIFT) + +/* + * TXS (RW) + * + * TXS + * TX Endpoint Stall - Read/Write + * 0 End Point OK + * 1 End Point Stalled + * This bit will be cleared automatically upon receipt of a SETUP request if this Endpoint is configured + * as a Control Endpoint and this bit will continue to be cleared by hardware until the associated ENDPTSETUPSTAT bit is cleared. + * Software can write a one to this bit to force the endpoint to return a STALL handshake to the Host. + * This control will continue to STALL until this bit is either cleared by software or automatically cleared as above for control endpoints. + * NOTE: [CONTROL ENDPOINT TYPES ONLY]: there is a slight delay (50 clocks max) between the ENDPTSETUPSTAT begin cleared and hardware continuing to clear this bit. + * In most systems, it is unlikely the DCD software will observe this delay. However, should the DCD observe that the stall bit is not set after writing a one to it then follow this procedure: + * continually write this stall bit until it is set or until a new setup has been received by checking the associated endptsetupstat Bit. + */ +#define USB_ENDPTCTRL_TXS_MASK (0x10000UL) +#define USB_ENDPTCTRL_TXS_SHIFT (16U) +#define USB_ENDPTCTRL_TXS_SET(x) (((uint32_t)(x) << USB_ENDPTCTRL_TXS_SHIFT) & USB_ENDPTCTRL_TXS_MASK) +#define USB_ENDPTCTRL_TXS_GET(x) (((uint32_t)(x) & USB_ENDPTCTRL_TXS_MASK) >> USB_ENDPTCTRL_TXS_SHIFT) + +/* + * RXE (RW) + * + * RXE + * RX Endpoint Enable + * 0 Disabled [Default] + * 1 Enabled + * An Endpoint should be enabled only after it has been configured. + */ +#define USB_ENDPTCTRL_RXE_MASK (0x80U) +#define USB_ENDPTCTRL_RXE_SHIFT (7U) +#define USB_ENDPTCTRL_RXE_SET(x) (((uint32_t)(x) << USB_ENDPTCTRL_RXE_SHIFT) & USB_ENDPTCTRL_RXE_MASK) +#define USB_ENDPTCTRL_RXE_GET(x) (((uint32_t)(x) & USB_ENDPTCTRL_RXE_MASK) >> USB_ENDPTCTRL_RXE_SHIFT) + +/* + * RXR (WS) + * + * RXR + * RX Data Toggle Reset (WS) + * Write 1 - Reset PID Sequence + * Whenever a configuration event is received for this Endpoint, software must write a one to this bit in order + * to synchronize the data PID's between the host and device. + */ +#define USB_ENDPTCTRL_RXR_MASK (0x40U) +#define USB_ENDPTCTRL_RXR_SHIFT (6U) +#define USB_ENDPTCTRL_RXR_SET(x) (((uint32_t)(x) << USB_ENDPTCTRL_RXR_SHIFT) & USB_ENDPTCTRL_RXR_MASK) +#define USB_ENDPTCTRL_RXR_GET(x) (((uint32_t)(x) & USB_ENDPTCTRL_RXR_MASK) >> USB_ENDPTCTRL_RXR_SHIFT) + +/* + * RXT (RW) + * + * RXT + * RX Endpoint Type - Read/Write + * 00 Control + * 01 Isochronous + * 10 Bulk + * 11 Interrupt + */ +#define USB_ENDPTCTRL_RXT_MASK (0xCU) +#define USB_ENDPTCTRL_RXT_SHIFT (2U) +#define USB_ENDPTCTRL_RXT_SET(x) (((uint32_t)(x) << USB_ENDPTCTRL_RXT_SHIFT) & USB_ENDPTCTRL_RXT_MASK) +#define USB_ENDPTCTRL_RXT_GET(x) (((uint32_t)(x) & USB_ENDPTCTRL_RXT_MASK) >> USB_ENDPTCTRL_RXT_SHIFT) + +/* + * RXS (RW) + * + * RXS + * RX Endpoint Stall - Read/Write + * 0 End Point OK. [Default] + * 1 End Point Stalled + * This bit is set automatically upon receipt of a SETUP request if this Endpoint is configured as a Control + * Endpointand this bit will continue to be cleared by hardware until the associated ENDPTSETUPSTAT bit + * is cleared. + * Software can write a one to this bit to force the endpoint to return a STALL handshake to the Host. This + * control will continue to STALL until this bit is either cleared by software or automatically cleared as above + * for control endpoints. + * NOTE: [CONTROL ENDPOINT TYPES ONLY]: there is a slight delay (50 clocks max) between the + * ENDPTSETUPSTAT begin cleared and hardware continuing to clear this bit. In most systems, it + * is unlikely the DCD software will observe this delay. However, should the DCD observe that the + * stall bit is not set after writing a one to it then follow this procedure: continually write this stall bit + * until it is set or until a new setup has been received by checking the associated endptsetupstat + * Bit. + */ +#define USB_ENDPTCTRL_RXS_MASK (0x1U) +#define USB_ENDPTCTRL_RXS_SHIFT (0U) +#define USB_ENDPTCTRL_RXS_SET(x) (((uint32_t)(x) << USB_ENDPTCTRL_RXS_SHIFT) & USB_ENDPTCTRL_RXS_MASK) +#define USB_ENDPTCTRL_RXS_GET(x) (((uint32_t)(x) & USB_ENDPTCTRL_RXS_MASK) >> USB_ENDPTCTRL_RXS_SHIFT) + +/* Bitfield definition for register: OTG_CTRL0 */ +/* + * OTG_WKDPDMCHG_EN (RW) + * + */ +#define USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_MASK (0x2000000UL) +#define USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_SHIFT (25U) +#define USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_SHIFT) & USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_MASK) +#define USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_MASK) >> USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_SHIFT) + +/* + * AUTORESUME_EN (RW) + * + */ +#define USB_OTG_CTRL0_AUTORESUME_EN_MASK (0x80000UL) +#define USB_OTG_CTRL0_AUTORESUME_EN_SHIFT (19U) +#define USB_OTG_CTRL0_AUTORESUME_EN_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_AUTORESUME_EN_SHIFT) & USB_OTG_CTRL0_AUTORESUME_EN_MASK) +#define USB_OTG_CTRL0_AUTORESUME_EN_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_AUTORESUME_EN_MASK) >> USB_OTG_CTRL0_AUTORESUME_EN_SHIFT) + +/* + * OTG_VBUS_WAKEUP_EN (RW) + * + */ +#define USB_OTG_CTRL0_OTG_VBUS_WAKEUP_EN_MASK (0x20000UL) +#define USB_OTG_CTRL0_OTG_VBUS_WAKEUP_EN_SHIFT (17U) +#define USB_OTG_CTRL0_OTG_VBUS_WAKEUP_EN_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_OTG_VBUS_WAKEUP_EN_SHIFT) & USB_OTG_CTRL0_OTG_VBUS_WAKEUP_EN_MASK) +#define USB_OTG_CTRL0_OTG_VBUS_WAKEUP_EN_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_OTG_VBUS_WAKEUP_EN_MASK) >> USB_OTG_CTRL0_OTG_VBUS_WAKEUP_EN_SHIFT) + +/* + * OTG_ID_WAKEUP_EN (RW) + * + */ +#define USB_OTG_CTRL0_OTG_ID_WAKEUP_EN_MASK (0x10000UL) +#define USB_OTG_CTRL0_OTG_ID_WAKEUP_EN_SHIFT (16U) +#define USB_OTG_CTRL0_OTG_ID_WAKEUP_EN_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_OTG_ID_WAKEUP_EN_SHIFT) & USB_OTG_CTRL0_OTG_ID_WAKEUP_EN_MASK) +#define USB_OTG_CTRL0_OTG_ID_WAKEUP_EN_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_OTG_ID_WAKEUP_EN_MASK) >> USB_OTG_CTRL0_OTG_ID_WAKEUP_EN_SHIFT) + +/* + * OTG_VBUS_SOURCE_SEL (RW) + * + */ +#define USB_OTG_CTRL0_OTG_VBUS_SOURCE_SEL_MASK (0x2000U) +#define USB_OTG_CTRL0_OTG_VBUS_SOURCE_SEL_SHIFT (13U) +#define USB_OTG_CTRL0_OTG_VBUS_SOURCE_SEL_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_OTG_VBUS_SOURCE_SEL_SHIFT) & USB_OTG_CTRL0_OTG_VBUS_SOURCE_SEL_MASK) +#define USB_OTG_CTRL0_OTG_VBUS_SOURCE_SEL_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_OTG_VBUS_SOURCE_SEL_MASK) >> USB_OTG_CTRL0_OTG_VBUS_SOURCE_SEL_SHIFT) + +/* + * OTG_UTMI_SUSPENDM_SW (RW) + * + * default 0 for naneng usbphy + */ +#define USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_MASK (0x1000U) +#define USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_SHIFT (12U) +#define USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_SHIFT) & USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_MASK) +#define USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_MASK) >> USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_SHIFT) + +/* + * OTG_UTMI_RESET_SW (RW) + * + * default 1 for naneng usbphy + */ +#define USB_OTG_CTRL0_OTG_UTMI_RESET_SW_MASK (0x800U) +#define USB_OTG_CTRL0_OTG_UTMI_RESET_SW_SHIFT (11U) +#define USB_OTG_CTRL0_OTG_UTMI_RESET_SW_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_OTG_UTMI_RESET_SW_SHIFT) & USB_OTG_CTRL0_OTG_UTMI_RESET_SW_MASK) +#define USB_OTG_CTRL0_OTG_UTMI_RESET_SW_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_OTG_UTMI_RESET_SW_MASK) >> USB_OTG_CTRL0_OTG_UTMI_RESET_SW_SHIFT) + +/* + * OTG_WAKEUP_INT_ENABLE (RW) + * + */ +#define USB_OTG_CTRL0_OTG_WAKEUP_INT_ENABLE_MASK (0x400U) +#define USB_OTG_CTRL0_OTG_WAKEUP_INT_ENABLE_SHIFT (10U) +#define USB_OTG_CTRL0_OTG_WAKEUP_INT_ENABLE_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_OTG_WAKEUP_INT_ENABLE_SHIFT) & USB_OTG_CTRL0_OTG_WAKEUP_INT_ENABLE_MASK) +#define USB_OTG_CTRL0_OTG_WAKEUP_INT_ENABLE_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_OTG_WAKEUP_INT_ENABLE_MASK) >> USB_OTG_CTRL0_OTG_WAKEUP_INT_ENABLE_SHIFT) + +/* + * OTG_POWER_MASK (RW) + * + */ +#define USB_OTG_CTRL0_OTG_POWER_MASK_MASK (0x200U) +#define USB_OTG_CTRL0_OTG_POWER_MASK_SHIFT (9U) +#define USB_OTG_CTRL0_OTG_POWER_MASK_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_OTG_POWER_MASK_SHIFT) & USB_OTG_CTRL0_OTG_POWER_MASK_MASK) +#define USB_OTG_CTRL0_OTG_POWER_MASK_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_OTG_POWER_MASK_MASK) >> USB_OTG_CTRL0_OTG_POWER_MASK_SHIFT) + +/* + * OTG_OVER_CUR_POL (RW) + * + */ +#define USB_OTG_CTRL0_OTG_OVER_CUR_POL_MASK (0x100U) +#define USB_OTG_CTRL0_OTG_OVER_CUR_POL_SHIFT (8U) +#define USB_OTG_CTRL0_OTG_OVER_CUR_POL_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_OTG_OVER_CUR_POL_SHIFT) & USB_OTG_CTRL0_OTG_OVER_CUR_POL_MASK) +#define USB_OTG_CTRL0_OTG_OVER_CUR_POL_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_OTG_OVER_CUR_POL_MASK) >> USB_OTG_CTRL0_OTG_OVER_CUR_POL_SHIFT) + +/* + * OTG_OVER_CUR_DIS (RW) + * + */ +#define USB_OTG_CTRL0_OTG_OVER_CUR_DIS_MASK (0x80U) +#define USB_OTG_CTRL0_OTG_OVER_CUR_DIS_SHIFT (7U) +#define USB_OTG_CTRL0_OTG_OVER_CUR_DIS_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_OTG_OVER_CUR_DIS_SHIFT) & USB_OTG_CTRL0_OTG_OVER_CUR_DIS_MASK) +#define USB_OTG_CTRL0_OTG_OVER_CUR_DIS_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_OTG_OVER_CUR_DIS_MASK) >> USB_OTG_CTRL0_OTG_OVER_CUR_DIS_SHIFT) + +/* + * SER_MODE_SUSPEND_EN (RW) + * + * for naneng usbphy, only switch to serial mode when suspend + */ +#define USB_OTG_CTRL0_SER_MODE_SUSPEND_EN_MASK (0x10U) +#define USB_OTG_CTRL0_SER_MODE_SUSPEND_EN_SHIFT (4U) +#define USB_OTG_CTRL0_SER_MODE_SUSPEND_EN_SET(x) (((uint32_t)(x) << USB_OTG_CTRL0_SER_MODE_SUSPEND_EN_SHIFT) & USB_OTG_CTRL0_SER_MODE_SUSPEND_EN_MASK) +#define USB_OTG_CTRL0_SER_MODE_SUSPEND_EN_GET(x) (((uint32_t)(x) & USB_OTG_CTRL0_SER_MODE_SUSPEND_EN_MASK) >> USB_OTG_CTRL0_SER_MODE_SUSPEND_EN_SHIFT) + +/* Bitfield definition for register: PHY_CTRL0 */ +/* + * GPIO_ID_SEL_N (RW) + * + */ +#define USB_PHY_CTRL0_GPIO_ID_SEL_N_MASK (0x2000000UL) +#define USB_PHY_CTRL0_GPIO_ID_SEL_N_SHIFT (25U) +#define USB_PHY_CTRL0_GPIO_ID_SEL_N_SET(x) (((uint32_t)(x) << USB_PHY_CTRL0_GPIO_ID_SEL_N_SHIFT) & USB_PHY_CTRL0_GPIO_ID_SEL_N_MASK) +#define USB_PHY_CTRL0_GPIO_ID_SEL_N_GET(x) (((uint32_t)(x) & USB_PHY_CTRL0_GPIO_ID_SEL_N_MASK) >> USB_PHY_CTRL0_GPIO_ID_SEL_N_SHIFT) + +/* + * ID_DIG_OVERRIDE (RW) + * + */ +#define USB_PHY_CTRL0_ID_DIG_OVERRIDE_MASK (0x4000U) +#define USB_PHY_CTRL0_ID_DIG_OVERRIDE_SHIFT (14U) +#define USB_PHY_CTRL0_ID_DIG_OVERRIDE_SET(x) (((uint32_t)(x) << USB_PHY_CTRL0_ID_DIG_OVERRIDE_SHIFT) & USB_PHY_CTRL0_ID_DIG_OVERRIDE_MASK) +#define USB_PHY_CTRL0_ID_DIG_OVERRIDE_GET(x) (((uint32_t)(x) & USB_PHY_CTRL0_ID_DIG_OVERRIDE_MASK) >> USB_PHY_CTRL0_ID_DIG_OVERRIDE_SHIFT) + +/* + * SESS_VALID_OVERRIDE (RW) + * + */ +#define USB_PHY_CTRL0_SESS_VALID_OVERRIDE_MASK (0x2000U) +#define USB_PHY_CTRL0_SESS_VALID_OVERRIDE_SHIFT (13U) +#define USB_PHY_CTRL0_SESS_VALID_OVERRIDE_SET(x) (((uint32_t)(x) << USB_PHY_CTRL0_SESS_VALID_OVERRIDE_SHIFT) & USB_PHY_CTRL0_SESS_VALID_OVERRIDE_MASK) +#define USB_PHY_CTRL0_SESS_VALID_OVERRIDE_GET(x) (((uint32_t)(x) & USB_PHY_CTRL0_SESS_VALID_OVERRIDE_MASK) >> USB_PHY_CTRL0_SESS_VALID_OVERRIDE_SHIFT) + +/* + * VBUS_VALID_OVERRIDE (RW) + * + */ +#define USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_MASK (0x1000U) +#define USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_SHIFT (12U) +#define USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_SET(x) (((uint32_t)(x) << USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_SHIFT) & USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_MASK) +#define USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_GET(x) (((uint32_t)(x) & USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_MASK) >> USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_SHIFT) + +/* + * ID_DIG_OVERRIDE_EN (RW) + * + */ +#define USB_PHY_CTRL0_ID_DIG_OVERRIDE_EN_MASK (0x4U) +#define USB_PHY_CTRL0_ID_DIG_OVERRIDE_EN_SHIFT (2U) +#define USB_PHY_CTRL0_ID_DIG_OVERRIDE_EN_SET(x) (((uint32_t)(x) << USB_PHY_CTRL0_ID_DIG_OVERRIDE_EN_SHIFT) & USB_PHY_CTRL0_ID_DIG_OVERRIDE_EN_MASK) +#define USB_PHY_CTRL0_ID_DIG_OVERRIDE_EN_GET(x) (((uint32_t)(x) & USB_PHY_CTRL0_ID_DIG_OVERRIDE_EN_MASK) >> USB_PHY_CTRL0_ID_DIG_OVERRIDE_EN_SHIFT) + +/* + * SESS_VALID_OVERRIDE_EN (RW) + * + */ +#define USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_MASK (0x2U) +#define USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_SHIFT (1U) +#define USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_SET(x) (((uint32_t)(x) << USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_SHIFT) & USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_MASK) +#define USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_GET(x) (((uint32_t)(x) & USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_MASK) >> USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_SHIFT) + +/* + * VBUS_VALID_OVERRIDE_EN (RW) + * + */ +#define USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_MASK (0x1U) +#define USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_SHIFT (0U) +#define USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_SET(x) (((uint32_t)(x) << USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_SHIFT) & USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_MASK) +#define USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_GET(x) (((uint32_t)(x) & USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_MASK) >> USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_SHIFT) + +/* Bitfield definition for register: PHY_CTRL1 */ +/* + * UTMI_CFG_RST_N (RW) + * + */ +#define USB_PHY_CTRL1_UTMI_CFG_RST_N_MASK (0x100000UL) +#define USB_PHY_CTRL1_UTMI_CFG_RST_N_SHIFT (20U) +#define USB_PHY_CTRL1_UTMI_CFG_RST_N_SET(x) (((uint32_t)(x) << USB_PHY_CTRL1_UTMI_CFG_RST_N_SHIFT) & USB_PHY_CTRL1_UTMI_CFG_RST_N_MASK) +#define USB_PHY_CTRL1_UTMI_CFG_RST_N_GET(x) (((uint32_t)(x) & USB_PHY_CTRL1_UTMI_CFG_RST_N_MASK) >> USB_PHY_CTRL1_UTMI_CFG_RST_N_SHIFT) + +/* + * UTMI_OTG_SUSPENDM (RW) + * + * OTG suspend, not utmi_suspendm + */ +#define USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_MASK (0x2U) +#define USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_SHIFT (1U) +#define USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_SET(x) (((uint32_t)(x) << USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_SHIFT) & USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_MASK) +#define USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_GET(x) (((uint32_t)(x) & USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_MASK) >> USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_SHIFT) + +/* Bitfield definition for register: TOP_STATUS */ +/* + * WAKEUP_INT_STATUS (RW) + * + */ +#define USB_TOP_STATUS_WAKEUP_INT_STATUS_MASK (0x80000000UL) +#define USB_TOP_STATUS_WAKEUP_INT_STATUS_SHIFT (31U) +#define USB_TOP_STATUS_WAKEUP_INT_STATUS_SET(x) (((uint32_t)(x) << USB_TOP_STATUS_WAKEUP_INT_STATUS_SHIFT) & USB_TOP_STATUS_WAKEUP_INT_STATUS_MASK) +#define USB_TOP_STATUS_WAKEUP_INT_STATUS_GET(x) (((uint32_t)(x) & USB_TOP_STATUS_WAKEUP_INT_STATUS_MASK) >> USB_TOP_STATUS_WAKEUP_INT_STATUS_SHIFT) + +/* Bitfield definition for register: PHY_STATUS */ +/* + * UTMI_CLK_VALID (RW) + * + */ +#define USB_PHY_STATUS_UTMI_CLK_VALID_MASK (0x80000000UL) +#define USB_PHY_STATUS_UTMI_CLK_VALID_SHIFT (31U) +#define USB_PHY_STATUS_UTMI_CLK_VALID_SET(x) (((uint32_t)(x) << USB_PHY_STATUS_UTMI_CLK_VALID_SHIFT) & USB_PHY_STATUS_UTMI_CLK_VALID_MASK) +#define USB_PHY_STATUS_UTMI_CLK_VALID_GET(x) (((uint32_t)(x) & USB_PHY_STATUS_UTMI_CLK_VALID_MASK) >> USB_PHY_STATUS_UTMI_CLK_VALID_SHIFT) + +/* + * LINE_STATE (RW) + * + */ +#define USB_PHY_STATUS_LINE_STATE_MASK (0xC0U) +#define USB_PHY_STATUS_LINE_STATE_SHIFT (6U) +#define USB_PHY_STATUS_LINE_STATE_SET(x) (((uint32_t)(x) << USB_PHY_STATUS_LINE_STATE_SHIFT) & USB_PHY_STATUS_LINE_STATE_MASK) +#define USB_PHY_STATUS_LINE_STATE_GET(x) (((uint32_t)(x) & USB_PHY_STATUS_LINE_STATE_MASK) >> USB_PHY_STATUS_LINE_STATE_SHIFT) + +/* + * HOST_DISCONNECT (RW) + * + */ +#define USB_PHY_STATUS_HOST_DISCONNECT_MASK (0x20U) +#define USB_PHY_STATUS_HOST_DISCONNECT_SHIFT (5U) +#define USB_PHY_STATUS_HOST_DISCONNECT_SET(x) (((uint32_t)(x) << USB_PHY_STATUS_HOST_DISCONNECT_SHIFT) & USB_PHY_STATUS_HOST_DISCONNECT_MASK) +#define USB_PHY_STATUS_HOST_DISCONNECT_GET(x) (((uint32_t)(x) & USB_PHY_STATUS_HOST_DISCONNECT_MASK) >> USB_PHY_STATUS_HOST_DISCONNECT_SHIFT) + +/* + * ID_DIG (RW) + * + */ +#define USB_PHY_STATUS_ID_DIG_MASK (0x10U) +#define USB_PHY_STATUS_ID_DIG_SHIFT (4U) +#define USB_PHY_STATUS_ID_DIG_SET(x) (((uint32_t)(x) << USB_PHY_STATUS_ID_DIG_SHIFT) & USB_PHY_STATUS_ID_DIG_MASK) +#define USB_PHY_STATUS_ID_DIG_GET(x) (((uint32_t)(x) & USB_PHY_STATUS_ID_DIG_MASK) >> USB_PHY_STATUS_ID_DIG_SHIFT) + +/* + * UTMI_SESS_VALID (RW) + * + */ +#define USB_PHY_STATUS_UTMI_SESS_VALID_MASK (0x4U) +#define USB_PHY_STATUS_UTMI_SESS_VALID_SHIFT (2U) +#define USB_PHY_STATUS_UTMI_SESS_VALID_SET(x) (((uint32_t)(x) << USB_PHY_STATUS_UTMI_SESS_VALID_SHIFT) & USB_PHY_STATUS_UTMI_SESS_VALID_MASK) +#define USB_PHY_STATUS_UTMI_SESS_VALID_GET(x) (((uint32_t)(x) & USB_PHY_STATUS_UTMI_SESS_VALID_MASK) >> USB_PHY_STATUS_UTMI_SESS_VALID_SHIFT) + +/* + * VBUS_VALID (RW) + * + */ +#define USB_PHY_STATUS_VBUS_VALID_MASK (0x1U) +#define USB_PHY_STATUS_VBUS_VALID_SHIFT (0U) +#define USB_PHY_STATUS_VBUS_VALID_SET(x) (((uint32_t)(x) << USB_PHY_STATUS_VBUS_VALID_SHIFT) & USB_PHY_STATUS_VBUS_VALID_MASK) +#define USB_PHY_STATUS_VBUS_VALID_GET(x) (((uint32_t)(x) & USB_PHY_STATUS_VBUS_VALID_MASK) >> USB_PHY_STATUS_VBUS_VALID_SHIFT) + + + +/* ENDPTCTRL register group index macro definition */ +#define USB_ENDPTCTRL_ENDPTCTRL0 (0UL) +#define USB_ENDPTCTRL_ENDPTCTRL1 (1UL) +#define USB_ENDPTCTRL_ENDPTCTRL2 (2UL) +#define USB_ENDPTCTRL_ENDPTCTRL3 (3UL) +#define USB_ENDPTCTRL_ENDPTCTRL4 (4UL) +#define USB_ENDPTCTRL_ENDPTCTRL5 (5UL) +#define USB_ENDPTCTRL_ENDPTCTRL6 (6UL) +#define USB_ENDPTCTRL_ENDPTCTRL7 (7UL) + + +#endif /* HPM_USB_H */ diff --git a/rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_dc_chipidea.c b/rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_dc_chipidea.c new file mode 100644 index 0000000..c6b6c0f --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_dc_chipidea.c @@ -0,0 +1,718 @@ +/* + * Copyright (c) 2021-2024 HPMicro + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbd_core.h" +#include "usb_chipidea_reg.h" + +#define USB_OTG_DEV ((CHIPIDEA_TypeDef *)g_usbdev_bus[busid].reg_base) + +#define CHIPIDEA_BITSMASK(val, offset) ((uint32_t)(val) << (offset)) +#define QTD_COUNT_EACH_ENDPOINT (8U) + +/* ENDPTCTRL */ +enum { + ENDPTCTRL_STALL = CHIPIDEA_BITSMASK(1, 0), + ENDPTCTRL_TYPE = CHIPIDEA_BITSMASK(3, 2), + ENDPTCTRL_TOGGLE_INHIBIT = CHIPIDEA_BITSMASK(1, 5), + ENDPTCTRL_TOGGLE_RESET = CHIPIDEA_BITSMASK(1, 6), + ENDPTCTRL_ENABLE = CHIPIDEA_BITSMASK(1, 7), +}; + +/* USBSTS, USBINTR */ +enum { + intr_usb = CHIPIDEA_BITSMASK(1, 0), + intr_error = CHIPIDEA_BITSMASK(1, 1), + intr_port_change = CHIPIDEA_BITSMASK(1, 2), + intr_reset = CHIPIDEA_BITSMASK(1, 6), + intr_sof = CHIPIDEA_BITSMASK(1, 7), + intr_suspend = CHIPIDEA_BITSMASK(1, 8), + intr_nak = CHIPIDEA_BITSMASK(1, 16) +}; + +/* Queue Transfer Descriptor */ +typedef struct { + /* Word 0: Next QTD Pointer */ + volatile uint32_t next; /* Next link pointer This field contains the physical memory address of the next dTD to be processed */ + + /* Word 1: qTQ Token */ + volatile uint32_t : 3; + volatile uint32_t xact_err : 1; + volatile uint32_t : 1; + volatile uint32_t buffer_err : 1; + volatile uint32_t halted : 1; + volatile uint32_t active : 1; + volatile uint32_t : 2; + volatile uint32_t iso_mult_override : 2; /* This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO. */ + volatile uint32_t : 3; + volatile uint32_t int_on_complete : 1; + volatile uint32_t total_bytes : 15; + volatile uint32_t : 0; + + /* Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page */ + volatile uint32_t buffer[5]; + + /*------------- DCD Area -------------*/ + volatile uint16_t expected_bytes; + volatile uint8_t reserved[2]; +} dcd_qtd_t; + +/* Queue Head */ +typedef struct { + /* Word 0: Capabilities and Characteristics */ + + volatile uint32_t : 15; /* Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed. */ + volatile uint32_t int_on_setup : 1; /* Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received. */ + volatile uint32_t max_packet_size : 11; /* This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize) */ + volatile uint32_t : 2; + volatile uint32_t zero_length_termination : 1; /* This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length. */ + volatile uint32_t iso_mult : 2; + volatile uint32_t : 0; + + /* Word 1: Current qTD Pointer */ + volatile uint32_t qtd_addr; + + /* Word 2-9: Transfer Overlay */ + volatile dcd_qtd_t qtd_overlay; + + /* Word 10-11: Setup request (control OUT only) */ + volatile struct usb_setup_packet setup_request; + + /*-------------------------------------------------------------------- + * Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes + * thus there are 16 bytes padding free that we can make use of. + *-------------------------------------------------------------------- + */ + volatile uint8_t reserved[16]; +} dcd_qhd_t; + +typedef struct { + dcd_qhd_t qhd[CONFIG_USBDEV_EP_NUM * 2]; + dcd_qtd_t qtd[CONFIG_USBDEV_EP_NUM * 2 * QTD_COUNT_EACH_ENDPOINT]; +} dcd_data_t; + +/* Endpoint state */ +struct chipidea_ep_state { + uint16_t ep_mps; /* Endpoint max packet size */ + uint8_t ep_type; /* Endpoint type */ + uint8_t ep_stalled; /* Endpoint stall flag */ + uint8_t ep_enable; /* Endpoint enable */ + uint8_t *xfer_buf; + uint32_t xfer_len; + uint32_t actual_xfer_len; +}; + +/* Driver state */ +struct chipidea_udc { + dcd_data_t *dcd_data; + bool is_suspend; + struct chipidea_ep_state in_ep[CONFIG_USBDEV_EP_NUM]; /*!< IN endpoint parameters*/ + struct chipidea_ep_state out_ep[CONFIG_USBDEV_EP_NUM]; /*!< OUT endpoint parameters */ +} g_chipidea_udc[CONFIG_USBDEV_MAX_BUS]; + +static USB_NOCACHE_RAM_SECTION __attribute__((aligned(2048))) dcd_data_t _dcd_data0; +#if CONFIG_USBDEV_MAX_BUS == 2 +static USB_NOCACHE_RAM_SECTION __attribute__((aligned(2048))) dcd_data_t _dcd_data1; +#endif + +static dcd_data_t *g_dcd_data[CONFIG_USBDEV_MAX_BUS] = { + &_dcd_data0, +#if CONFIG_USBDEV_MAX_BUS == 2 + &_dcd_data1 +#endif +}; + +/* Index to bit position in register */ +static inline uint8_t ep_idx2bit(uint8_t ep_idx) +{ + return ep_idx / 2 + ((ep_idx % 2) ? 16 : 0); +} + +static void __chipidea_bus_reset(CHIPIDEA_TypeDef *ptr) +{ + /* The reset value for all endpoint types is the control endpoint. If one endpoint + * direction is enabled and the paired endpoint of opposite direction is disabled, then the + * endpoint type of the unused direction must be changed from the control type to any other + * type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior + * for the data PID tracking on the active endpoint. + */ + + for (uint32_t i = 1; i < CONFIG_USBDEV_EP_NUM; i++) { + ptr->ENDPTCTRL[i] = USB_ENDPTCTRL_TXT_SET(USB_ENDPOINT_TYPE_BULK) | USB_ENDPTCTRL_RXT_SET(USB_ENDPOINT_TYPE_BULK); + } + + /* Clear All Registers */ + ptr->ENDPTNAK = ptr->ENDPTNAK; + ptr->ENDPTNAKEN = 0; + ptr->USBSTS = ptr->USBSTS; + ptr->ENDPTSETUPSTAT = ptr->ENDPTSETUPSTAT; + ptr->ENDPTCOMPLETE = ptr->ENDPTCOMPLETE; + + while (ptr->ENDPTPRIME) { + } + ptr->ENDPTFLUSH = 0xFFFFFFFF; + while (ptr->ENDPTFLUSH) { + } +} + +static void chipidea_init(CHIPIDEA_TypeDef *ptr) +{ + /* Reset controller */ + ptr->USBCMD |= USB_USBCMD_RST_MASK; + while (USB_USBCMD_RST_GET(ptr->USBCMD)) { + } + + /* Set mode to device, must be set immediately after reset */ + ptr->USBMODE &= ~USB_USBMODE_CM_MASK; + ptr->USBMODE |= USB_USBMODE_CM_SET(2); + + /* Disable setup lockout, please refer to "Control Endpoint Operation" section in RM. */ + ptr->USBMODE &= ~USB_USBMODE_SLOM_MASK; + + /* Set the endian */ + ptr->USBMODE &= ~USB_USBMODE_ES_MASK; + + /* Set parallel interface signal */ + ptr->PORTSC1 &= ~USB_PORTSC1_STS_MASK; + + /* Set parallel transceiver width */ + ptr->PORTSC1 &= ~USB_PORTSC1_PTW_MASK; + + /* Set usb forced to full speed mode */ + //ptr->PORTSC1 |= USB_PORTSC1_PFSC_MASK; + + /* Not use interrupt threshold. */ + ptr->USBCMD &= ~USB_USBCMD_ITC_MASK; + + /* Enable VBUS discharge */ + ptr->OTGSC |= USB_OTGSC_VD_MASK; +} + +static void chipidea_deinit(CHIPIDEA_TypeDef *ptr) +{ + /* Stop */ + ptr->USBCMD &= ~USB_USBCMD_RS_MASK; + + /* Reset controller */ + ptr->USBCMD |= USB_USBCMD_RST_MASK; + while (USB_USBCMD_RST_GET(ptr->USBCMD)) { + } + + /* Reset endpoint list address register */ + ptr->ENDPTLISTADDR = 0; + + /* Reset status register */ + ptr->USBSTS = ptr->USBSTS; + + /* Reset interrupt enable register */ + ptr->USBINTR = 0; +} + +/*--------------------------------------------------------------------- + * Endpoint API + *--------------------------------------------------------------------- + */ +static void __chipidea_edpt_open(CHIPIDEA_TypeDef *ptr, uint8_t ep_addr, uint8_t ep_type) +{ + uint8_t const epnum = ep_addr & 0x0f; + uint8_t const dir = (ep_addr & 0x80) >> 7; + + /* Enable EP Control */ + uint32_t temp = ptr->ENDPTCTRL[epnum]; + temp &= ~((0x03 << 2) << (dir ? 16 : 0)); + temp |= ((ep_type << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); + ptr->ENDPTCTRL[epnum] = temp; +} + +static void chipidea_edpt_xfer(CHIPIDEA_TypeDef *ptr, uint8_t ep_idx) +{ + uint32_t offset = ep_idx / 2 + ((ep_idx % 2) ? 16 : 0); + + /* Start transfer */ + ptr->ENDPTPRIME = 1 << offset; +} + +static void chipidea_edpt_stall(CHIPIDEA_TypeDef *ptr, uint8_t ep_addr) +{ + uint8_t const epnum = ep_addr & 0x0f; + uint8_t const dir = (ep_addr & 0x80) >> 7; + + ptr->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0); +} + +static void chipidea_edpt_clear_stall(CHIPIDEA_TypeDef *ptr, uint8_t ep_addr) +{ + uint8_t const epnum = ep_addr & 0x0f; + uint8_t const dir = (ep_addr & 0x80) >> 7; + + /* data toggle also need to be reset */ + ptr->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << (dir ? 16 : 0); + ptr->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << (dir ? 16 : 0)); +} + +static bool chipidea_edpt_check_stall(CHIPIDEA_TypeDef *ptr, uint8_t ep_addr) +{ + uint8_t const epnum = ep_addr & 0x0f; + uint8_t const dir = (ep_addr & 0x80) >> 7; + + return (ptr->ENDPTCTRL[epnum] & (ENDPTCTRL_STALL << (dir ? 16 : 0))) ? true : false; +} + +static void chipidea_edpt_close(CHIPIDEA_TypeDef *ptr, uint8_t ep_addr) +{ + uint8_t const epnum = ep_addr & 0x0f; + uint8_t const dir = (ep_addr & 0x80) >> 7; + + uint32_t primebit = CHIPIDEA_BITSMASK(1, epnum) << (dir ? 16 : 0); + + /* Flush the endpoint to stop a transfer. */ + do { + /* Set the corresponding bit(s) in the ENDPTFLUSH register */ + ptr->ENDPTFLUSH |= primebit; + + /* Wait until all bits in the ENDPTFLUSH register are cleared. */ + while (0U != (ptr->ENDPTFLUSH & primebit)) { + } + /* + * Read the ENDPTSTAT register to ensure that for all endpoints + * commanded to be flushed, that the corresponding bits + * are now cleared. + */ + } while (0U != (ptr->ENDPTSTAT & primebit)); + + /* Disable the endpoint */ + ptr->ENDPTCTRL[epnum] &= ~((ENDPTCTRL_TYPE | ENDPTCTRL_ENABLE | ENDPTCTRL_STALL) << (dir ? 16 : 0)); + ptr->ENDPTCTRL[epnum] |= (USB_ENDPOINT_TYPE_BULK << 2) << (dir ? 16 : 0); +} + +/* Initialize qtd */ +static void usb_qtd_init(dcd_qtd_t *p_qtd, void *data_ptr, uint16_t total_bytes) +{ + memset(p_qtd, 0, sizeof(dcd_qtd_t)); + + p_qtd->next = 1; + p_qtd->active = 1; + p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes; + + if (data_ptr != NULL) { + p_qtd->buffer[0] = (uint32_t)data_ptr; + for (uint8_t i = 1; i < 5; i++) { + p_qtd->buffer[i] |= ((p_qtd->buffer[i - 1]) & 0xFFFFF000UL) + 4096U; + } + } +} + +static dcd_qhd_t *chipidea_qhd_get(uint8_t busid, uint8_t ep_idx) +{ + dcd_data_t *dcd_data; + + dcd_data = g_chipidea_udc[busid].dcd_data; + return &dcd_data->qhd[ep_idx]; +} + +static dcd_qtd_t *chipidea_qtd_get(uint8_t busid, uint8_t ep_idx) +{ + dcd_data_t *dcd_data; + + dcd_data = g_chipidea_udc[busid].dcd_data; + return &dcd_data->qtd[ep_idx * QTD_COUNT_EACH_ENDPOINT]; +} + +static void chipidea_bus_reset(uint8_t busid, uint16_t ep0_max_packet_size) +{ + dcd_data_t *dcd_data; + + dcd_data = g_chipidea_udc[busid].dcd_data; + __chipidea_bus_reset(USB_OTG_DEV); + + /* Queue Head & Queue TD */ + memset(dcd_data, 0, sizeof(dcd_data_t)); + + /* Set up Control Endpoints (0 OUT, 1 IN) */ + dcd_data->qhd[0].zero_length_termination = dcd_data->qhd[1].zero_length_termination = 1; + dcd_data->qhd[0].max_packet_size = dcd_data->qhd[1].max_packet_size = ep0_max_packet_size; + dcd_data->qhd[0].qtd_overlay.next = dcd_data->qhd[1].qtd_overlay.next = 1; + + /* OUT only */ + dcd_data->qhd[0].int_on_setup = 1; +} + +static void chipidea_edpt_open(uint8_t busid, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_mps) +{ + uint8_t const epnum = ep_addr & 0x0f; + uint8_t const dir = (ep_addr & 0x80) >> 7; + uint8_t const ep_idx = 2 * epnum + dir; + dcd_data_t *dcd_data; + dcd_qhd_t *p_qhd; + + /* Prepare Queue Head */ + dcd_data = g_chipidea_udc[busid].dcd_data; + p_qhd = &dcd_data->qhd[ep_idx]; + memset(p_qhd, 0, sizeof(dcd_qhd_t)); + + p_qhd->zero_length_termination = 1; + p_qhd->max_packet_size = ep_mps & 0x7FFu; + p_qhd->qtd_overlay.next = 1; + if (ep_type == USB_ENDPOINT_TYPE_ISOCHRONOUS) { + p_qhd->iso_mult = ((ep_mps >> 11u) & 0x3u) + 1u; + } + + __chipidea_edpt_open(USB_OTG_DEV, ep_addr, ep_type); +} + +static bool chipidea_start_xfer(uint8_t busid, uint8_t ep_addr, uint8_t *buffer, uint32_t total_bytes) +{ + uint8_t const epnum = ep_addr & 0x0f; + uint8_t const dir = (ep_addr & 0x80) >> 7; + uint8_t const ep_idx = 2 * epnum + dir; + uint8_t qtd_num; + uint8_t i; + uint32_t xfer_len; + dcd_qhd_t *p_qhd; + dcd_qtd_t *p_qtd; + dcd_qtd_t *first_p_qtd = NULL; + dcd_qtd_t *prev_p_qtd = NULL; + dcd_data_t *dcd_data; + + dcd_data = g_chipidea_udc[busid].dcd_data; + + if (epnum == 0) { + /* follows UM Setup packet handling using setup lockout mechanism + * wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out + */ + while (USB_OTG_DEV->ENDPTSETUPSTAT & CHIPIDEA_BITSMASK(1, 0)) { + } + } + + qtd_num = (total_bytes + 0x3fff) / 0x4000; + if (qtd_num > QTD_COUNT_EACH_ENDPOINT) { + return false; + } + + if (buffer != NULL) { + buffer = (uint8_t *)buffer; + } + p_qhd = &dcd_data->qhd[ep_idx]; + i = 0; + do { + p_qtd = &dcd_data->qtd[ep_idx * QTD_COUNT_EACH_ENDPOINT + i]; + i++; + + if (total_bytes > 0x4000) { + xfer_len = 0x4000; + total_bytes -= 0x4000; + } else { + xfer_len = total_bytes; + total_bytes = 0; + } + + usb_qtd_init(p_qtd, (void *)buffer, xfer_len); + if (total_bytes == 0) { + p_qtd->int_on_complete = true; + } + buffer += xfer_len; + + if (prev_p_qtd) { + prev_p_qtd->next = (uint32_t)p_qtd; + } else { + first_p_qtd = p_qtd; + } + prev_p_qtd = p_qtd; + } while (total_bytes > 0); + + p_qhd->qtd_overlay.next = (uint32_t)first_p_qtd; /* link qtd to qhd */ + + chipidea_edpt_xfer(USB_OTG_DEV, ep_idx); + + return true; +} + +__WEAK void usb_dc_low_level_init(uint8_t busid) +{ +} + +__WEAK void usb_dc_low_level_deinit(uint8_t busid) +{ +} + +int usb_dc_init(uint8_t busid) +{ + uint32_t int_mask; + int_mask = (USB_USBINTR_UE_MASK | USB_USBINTR_UEE_MASK | USB_USBINTR_SLE_MASK | + USB_USBINTR_PCE_MASK | USB_USBINTR_URE_MASK); + + usb_dc_low_level_init(busid); + + memset(&g_chipidea_udc[busid], 0, sizeof(struct chipidea_udc)); + g_chipidea_udc[busid].dcd_data = g_dcd_data[busid]; + memset(g_chipidea_udc[busid].dcd_data, 0, sizeof(dcd_data_t)); + + chipidea_init(USB_OTG_DEV); + + /* Set endpoint list address */ + USB_OTG_DEV->ENDPTLISTADDR = ((uint32_t)g_chipidea_udc[busid].dcd_data->qhd) & USB_ENDPTLISTADDR_EPBASE_MASK; + + /* Clear status */ + USB_OTG_DEV->USBSTS = USB_OTG_DEV->USBSTS; + + /* Enable interrupt mask */ + USB_OTG_DEV->USBINTR |= int_mask; + + /* Connect by enabling internal pull-up resistor on D+/D- */ + USB_OTG_DEV->USBCMD |= USB_USBCMD_RS_MASK; + return 0; +} + +int usb_dc_deinit(uint8_t busid) +{ + chipidea_deinit(USB_OTG_DEV); + + for (uint32_t i = 0; i < CONFIG_USBDEV_EP_NUM; i++) { + chipidea_edpt_close(USB_OTG_DEV, (i | 0x80)); + chipidea_edpt_close(USB_OTG_DEV, (i | 0x00)); + } + + usb_dc_low_level_deinit(busid); + return 0; +} + +int usbd_set_address(uint8_t busid, const uint8_t addr) +{ + USB_OTG_DEV->DEVICEADDR = USB_DEVICEADDR_USBADR_SET(addr) | USB_DEVICEADDR_USBADRA_MASK; + return 0; +} + +int usbd_set_remote_wakeup(uint8_t busid) +{ + if (!USB_PORTSC1_SUSP_GET(USB_OTG_DEV->PORTSC1)) { + return -1; + } + + USB_OTG_DEV->PORTSC1 |= USB_PORTSC1_FPR_MASK; + while (USB_OTG_DEV->PORTSC1 & USB_PORTSC1_FPR_MASK) { + } + + return 0; +} + +uint8_t usbd_get_port_speed(uint8_t busid) +{ + uint8_t speed; + + speed = USB_PORTSC1_PSPD_GET(USB_OTG_DEV->PORTSC1); + + if (speed == 0x00) { + return USB_SPEED_FULL; + } + if (speed == 0x01) { + return USB_SPEED_LOW; + } + if (speed == 0x02) { + return USB_SPEED_HIGH; + } + + return 0; +} + +int usbd_ep_open(uint8_t busid, const struct usb_endpoint_descriptor *ep) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep->bEndpointAddress); + + /* Must not exceed max endpoint number */ + if (ep_idx >= CONFIG_USBDEV_EP_NUM) { + return -1; + } + + chipidea_edpt_open(busid, ep->bEndpointAddress, USB_GET_ENDPOINT_TYPE(ep->bmAttributes), ep->wMaxPacketSize); + + if (USB_EP_DIR_IS_OUT(ep->bEndpointAddress)) { + g_chipidea_udc[busid].out_ep[ep_idx].ep_mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); + g_chipidea_udc[busid].out_ep[ep_idx].ep_type = USB_GET_ENDPOINT_TYPE(ep->bmAttributes); + g_chipidea_udc[busid].out_ep[ep_idx].ep_enable = true; + } else { + g_chipidea_udc[busid].in_ep[ep_idx].ep_mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); + g_chipidea_udc[busid].in_ep[ep_idx].ep_type = USB_GET_ENDPOINT_TYPE(ep->bmAttributes); + g_chipidea_udc[busid].in_ep[ep_idx].ep_enable = true; + } + + return 0; +} + +int usbd_ep_close(uint8_t busid, const uint8_t ep) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (USB_EP_DIR_IS_OUT(ep)) { + g_chipidea_udc[busid].out_ep[ep_idx].ep_enable = false; + } else { + g_chipidea_udc[busid].in_ep[ep_idx].ep_enable = false; + } + + chipidea_edpt_close(USB_OTG_DEV, ep); + + return 0; +} + +int usbd_ep_set_stall(uint8_t busid, const uint8_t ep) +{ + chipidea_edpt_stall(USB_OTG_DEV, ep); + return 0; +} + +int usbd_ep_clear_stall(uint8_t busid, const uint8_t ep) +{ + chipidea_edpt_clear_stall(USB_OTG_DEV, ep); + return 0; +} + +int usbd_ep_is_stalled(uint8_t busid, const uint8_t ep, uint8_t *stalled) +{ + *stalled = chipidea_edpt_check_stall(USB_OTG_DEV, ep); + return 0; +} + +int usbd_ep_start_write(uint8_t busid, const uint8_t ep, const uint8_t *data, uint32_t data_len) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (!data && data_len) { + return -1; + } + if (!g_chipidea_udc[busid].in_ep[ep_idx].ep_enable) { + return -2; + } + + g_chipidea_udc[busid].in_ep[ep_idx].xfer_buf = (uint8_t *)data; + g_chipidea_udc[busid].in_ep[ep_idx].xfer_len = data_len; + g_chipidea_udc[busid].in_ep[ep_idx].actual_xfer_len = 0; + + chipidea_start_xfer(busid, ep, (uint8_t *)data, data_len); + + return 0; +} + +int usbd_ep_start_read(uint8_t busid, const uint8_t ep, uint8_t *data, uint32_t data_len) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (!data && data_len) { + return -1; + } + if (!g_chipidea_udc[busid].out_ep[ep_idx].ep_enable) { + return -2; + } + + g_chipidea_udc[busid].out_ep[ep_idx].xfer_buf = (uint8_t *)data; + g_chipidea_udc[busid].out_ep[ep_idx].xfer_len = data_len; + g_chipidea_udc[busid].out_ep[ep_idx].actual_xfer_len = 0; + + chipidea_start_xfer(busid, ep, data, data_len); + + return 0; +} + +void USBD_IRQHandler(uint8_t busid) +{ + uint32_t int_status; + uint32_t transfer_len; + bool ep_cb_req; + + /* Acknowledge handled interrupt */ + int_status = USB_OTG_DEV->USBSTS; + int_status &= USB_OTG_DEV->USBINTR; + USB_OTG_DEV->USBSTS = int_status; + + if (int_status & intr_error) { + USB_LOG_ERR("usbd intr error!\r\n"); + } + + if (int_status & intr_reset) { + g_chipidea_udc[busid].is_suspend = false; + memset(g_chipidea_udc[busid].in_ep, 0, sizeof(struct chipidea_ep_state) * CONFIG_USBDEV_EP_NUM); + memset(g_chipidea_udc[busid].out_ep, 0, sizeof(struct chipidea_ep_state) * CONFIG_USBDEV_EP_NUM); + usbd_event_reset_handler(busid); + chipidea_bus_reset(busid, 64); + } + + if (int_status & intr_suspend) { + if (USB_PORTSC1_SUSP_GET(USB_OTG_DEV->PORTSC1)) { + /* Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. */ + if (USB_DEVICEADDR_USBADR_GET(USB_OTG_DEV->DEVICEADDR)) { + g_chipidea_udc[busid].is_suspend = true; + usbd_event_suspend_handler(busid); + } + } else { + } + } + + if (int_status & intr_port_change) { + if (!USB_PORTSC1_CCS_GET(USB_OTG_DEV->PORTSC1)) { + usbd_event_disconnect_handler(busid); + } else { + if (g_chipidea_udc[busid].is_suspend) { + g_chipidea_udc[busid].is_suspend = false; + usbd_event_resume_handler(busid); + } + usbd_event_connect_handler(busid); + } + } + + if (int_status & intr_usb) { + uint32_t const edpt_complete = USB_OTG_DEV->ENDPTCOMPLETE; + USB_OTG_DEV->ENDPTCOMPLETE = edpt_complete; + uint32_t edpt_setup_status = USB_OTG_DEV->ENDPTSETUPSTAT; + + if (edpt_setup_status) { + /*------------- Set up Received -------------*/ + USB_OTG_DEV->ENDPTSETUPSTAT = edpt_setup_status; + dcd_qhd_t *qhd0 = chipidea_qhd_get(busid, 0); + usbd_event_ep0_setup_complete_handler(busid, (uint8_t *)&qhd0->setup_request); + } + + if (edpt_complete) { + for (uint8_t ep_idx = 0; ep_idx < (CONFIG_USBDEV_EP_NUM * 2); ep_idx++) { + if (edpt_complete & (1 << ep_idx2bit(ep_idx))) { + transfer_len = 0; + ep_cb_req = true; + + /* Failed QTD also get ENDPTCOMPLETE set */ + dcd_qtd_t *p_qtd = chipidea_qtd_get(busid, ep_idx); + while (1) { + if (p_qtd->halted || p_qtd->xact_err || p_qtd->buffer_err) { + USB_LOG_ERR("usbd transfer error!\r\n"); + ep_cb_req = false; + break; + } else if (p_qtd->active) { + ep_cb_req = false; + break; + } else { + transfer_len += p_qtd->expected_bytes - p_qtd->total_bytes; + } + + if (p_qtd->next == 1) { + break; + } else { + p_qtd = (dcd_qtd_t *)p_qtd->next; + } + } + + if (ep_cb_req) { + uint8_t const ep_addr = (ep_idx / 2) | ((ep_idx & 0x01) ? 0x80 : 0); + if (ep_addr & 0x80) { + usbd_event_ep_in_complete_handler(busid, ep_addr, transfer_len); + } else { + usbd_event_ep_out_complete_handler(busid, ep_addr, transfer_len); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_glue_mcx.c b/rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_glue_mcx.c new file mode 100644 index 0000000..3f9b46b --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/chipidea/usb_glue_mcx.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbd_core.h" +#include "fsl_common.h" + +/*! @brief USB controller ID */ +typedef enum _usb_controller_index { + kUSB_ControllerKhci0 = 0U, /*!< KHCI 0U */ + kUSB_ControllerKhci1 = 1U, /*!< KHCI 1U, Currently, there are no platforms which have two KHCI IPs, this is reserved + to be used in the future. */ + kUSB_ControllerEhci0 = 2U, /*!< EHCI 0U */ + kUSB_ControllerEhci1 = 3U, /*!< EHCI 1U */ +} usb_controller_index_t; + +#define USB_DEVICE_CONFIG_EHCI 1 + +/* USB PHY condfiguration */ +#define BOARD_USB_PHY_D_CAL (0x04U) +#define BOARD_USB_PHY_TXCAL45DP (0x07U) +#define BOARD_USB_PHY_TXCAL45DM (0x07U) +#define BOARD_XTAL0_CLK_HZ 24000000U /*!< Board xtal0 frequency in Hz */ + +#if ((defined FSL_FEATURE_SOC_USBPHY_COUNT) && (FSL_FEATURE_SOC_USBPHY_COUNT > 0U)) +#include "usb_phy.h" +#endif + +#if (defined(USB_DEVICE_CONFIG_EHCI) && (USB_DEVICE_CONFIG_EHCI > 0U)) +void USB1_HS_IRQHandler(void) +{ + extern void USBD_IRQHandler(uint8_t busid); + USBD_IRQHandler(0); +} +#endif + +void USB_ClockInit(void) +{ +#if defined(USB_DEVICE_CONFIG_EHCI) && (USB_DEVICE_CONFIG_EHCI > 0U) + usb_phy_config_struct_t phyConfig = { + BOARD_USB_PHY_D_CAL, + BOARD_USB_PHY_TXCAL45DP, + BOARD_USB_PHY_TXCAL45DM, + }; +#endif +#if defined(USB_DEVICE_CONFIG_EHCI) && (USB_DEVICE_CONFIG_EHCI > 0U) + SPC0->ACTIVE_VDELAY = 0x0500; + /* Change the power DCDC to 1.8v (By deafult, DCDC is 1.8V), CORELDO to 1.1v (By deafult, CORELDO is 1.0V) */ + SPC0->ACTIVE_CFG &= ~SPC_ACTIVE_CFG_CORELDO_VDD_DS_MASK; + SPC0->ACTIVE_CFG |= SPC_ACTIVE_CFG_DCDC_VDD_LVL(0x3) | SPC_ACTIVE_CFG_CORELDO_VDD_LVL(0x3) | + SPC_ACTIVE_CFG_SYSLDO_VDD_DS_MASK | SPC_ACTIVE_CFG_DCDC_VDD_DS(0x2u); + /* Wait until it is done */ + while (SPC0->SC & SPC_SC_BUSY_MASK) + ; + if (0u == (SCG0->LDOCSR & SCG_LDOCSR_LDOEN_MASK)) { + SCG0->TRIM_LOCK = 0x5a5a0001U; + SCG0->LDOCSR |= SCG_LDOCSR_LDOEN_MASK; + /* wait LDO ready */ + while (0U == (SCG0->LDOCSR & SCG_LDOCSR_VOUT_OK_MASK)) + ; + } + SYSCON->AHBCLKCTRLSET[2] |= SYSCON_AHBCLKCTRL2_USB_HS_MASK | SYSCON_AHBCLKCTRL2_USB_HS_PHY_MASK; + SCG0->SOSCCFG &= ~(SCG_SOSCCFG_RANGE_MASK | SCG_SOSCCFG_EREFS_MASK); + /* xtal = 20 ~ 30MHz */ + SCG0->SOSCCFG = (1U << SCG_SOSCCFG_RANGE_SHIFT) | (1U << SCG_SOSCCFG_EREFS_SHIFT); + SCG0->SOSCCSR |= SCG_SOSCCSR_SOSCEN_MASK; + while (1) { + if (SCG0->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK) { + break; + } + } + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK | SYSCON_CLOCK_CTRL_CLKIN_ENA_FM_USBH_LPT_MASK; + CLOCK_EnableClock(kCLOCK_UsbHs); + CLOCK_EnableClock(kCLOCK_UsbHsPhy); + CLOCK_EnableUsbhsPhyPllClock(kCLOCK_Usbphy480M, 24000000U); + CLOCK_EnableUsbhsClock(); + USB_EhciPhyInit(kUSB_ControllerEhci0, BOARD_XTAL0_CLK_HZ, &phyConfig); +#endif +#if defined(USB_DEVICE_CONFIG_KHCI) && (USB_DEVICE_CONFIG_KHCI > 0U) + CLOCK_AttachClk(kCLK_48M_to_USB0); + CLOCK_EnableClock(kCLOCK_Usb0Ram); + CLOCK_EnableClock(kCLOCK_Usb0Fs); + CLOCK_EnableUsbfsClock(); +#endif +} + +void usb_dc_low_level_init(uint8_t busid) +{ + USB_ClockInit(); + /* Install isr, set priority, and enable IRQ. */ + NVIC_SetPriority((IRQn_Type)USB1_HS_IRQn, 3); + EnableIRQ((IRQn_Type)USB1_HS_IRQn); +} + +void usb_dc_low_level_deinit(uint8_t busid) +{ + DisableIRQ((IRQn_Type)USB1_HS_IRQn); +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/README.md b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/README.md index 2111008..3218b31 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/README.md +++ b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/README.md @@ -1,5 +1,7 @@ # Note +If you are using more than one port, all ip parameters must be the same(like fifo num, endpoint num, dma support and so on), otherwise give up using multi ports. + ## Support Chip List ## STM32 @@ -18,6 +20,11 @@ ## GD32 +CONFIG_USBDEV_EP_NUM 必须为4 或者 6,并删除 usb_dc_dwc2.c 中 while(1){} + +当 CONFIG_USBDEV_EP_NUM 为4 时,fifo_num 不得大于 320 字 +当 CONFIG_USBDEV_EP_NUM 为6 时,fifo_num 不得大于 1280 字 + - GD32F30X_CL - GD32F405、GD32F407 - GD32F450 diff --git a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_dc_dwc2.c b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_dc_dwc2.c index 9dee5c1..6054a33 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_dc_dwc2.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_dc_dwc2.c @@ -51,8 +51,6 @@ #endif // clang-format on -//#define CONFIG_USB_DWC2_DMA_ENABLE - #ifndef CONFIG_USB_DWC2_RXALL_FIFO_SIZE #define CONFIG_USB_DWC2_RXALL_FIFO_SIZE (1024 / 4) #endif @@ -62,7 +60,7 @@ #endif #ifndef CONFIG_USB_DWC2_TX1_FIFO_SIZE -#define CONFIG_USB_DWC2_TX1_FIFO_SIZE (512 / 4) +#define CONFIG_USB_DWC2_TX1_FIFO_SIZE (1024 / 4) #endif #ifndef CONFIG_USB_DWC2_TX2_FIFO_SIZE @@ -93,7 +91,7 @@ #define CONFIG_USB_DWC2_TX8_FIFO_SIZE (0 / 4) #endif -#define USBD_BASE (g_usbdev_bus[0].reg_base) +#define USBD_BASE (g_usbdev_bus[busid].reg_base) #define USB_OTG_GLB ((DWC2_GlobalTypeDef *)(USBD_BASE)) #define USB_OTG_DEV ((DWC2_DeviceTypeDef *)(USBD_BASE + USB_OTG_DEVICE_BASE)) @@ -119,9 +117,9 @@ USB_NOCACHE_RAM_SECTION struct dwc2_udc { __attribute__((aligned(32))) struct usb_setup_packet setup; struct dwc2_ep_state in_ep[CONFIG_USBDEV_EP_NUM]; /*!< IN endpoint parameters*/ struct dwc2_ep_state out_ep[CONFIG_USBDEV_EP_NUM]; /*!< OUT endpoint parameters */ -} g_dwc2_udc; +} g_dwc2_udc[CONFIG_USBDEV_MAX_BUS]; -static inline int dwc2_reset(void) +static inline int dwc2_reset(uint8_t busid) { volatile uint32_t count = 0U; @@ -138,14 +136,16 @@ static inline int dwc2_reset(void) do { if (++count > 200000U) { - return -1; + break; } } while ((USB_OTG_GLB->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST); + USB_OTG_GLB->GRSTCTL &= ~USB_OTG_GRSTCTL_CSRST; + return 0; } -static inline int dwc2_core_init(void) +static inline int dwc2_core_init(uint8_t busid) { int ret; #if defined(CONFIG_USB_HS) @@ -156,18 +156,18 @@ static inline int dwc2_core_init(void) USB_OTG_GLB->GUSBCFG &= ~(USB_OTG_GUSBCFG_ULPIEVBUSD | USB_OTG_GUSBCFG_ULPIEVBUSI); /* Reset after a PHY select */ - ret = dwc2_reset(); + ret = dwc2_reset(busid); #else /* Select FS Embedded PHY */ USB_OTG_GLB->GUSBCFG |= USB_OTG_GUSBCFG_PHYSEL; /* Reset after a PHY select */ - ret = dwc2_reset(); + ret = dwc2_reset(busid); #endif return ret; } -static inline void dwc2_set_mode(uint8_t mode) +static inline void dwc2_set_mode(uint8_t busid, uint8_t mode) { USB_OTG_GLB->GUSBCFG &= ~(USB_OTG_GUSBCFG_FHMOD | USB_OTG_GUSBCFG_FDMOD); @@ -176,12 +176,22 @@ static inline void dwc2_set_mode(uint8_t mode) } else if (mode == USB_OTG_MODE_DEVICE) { USB_OTG_GLB->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; } + + usbd_dwc2_delay_ms(50); } -static inline int dwc2_flush_rxfifo(void) +static inline int dwc2_flush_rxfifo(uint8_t busid) { - volatile uint32_t count = 0; + volatile uint32_t count = 0U; + /* Wait for AHB master IDLE state. */ + do { + if (++count > 200000U) { + return -1; + } + } while ((USB_OTG_GLB->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0U); + + count = 0; USB_OTG_GLB->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; do { @@ -193,10 +203,18 @@ static inline int dwc2_flush_rxfifo(void) return 0; } -static inline int dwc2_flush_txfifo(uint32_t num) +static inline int dwc2_flush_txfifo(uint8_t busid, uint32_t num) { volatile uint32_t count = 0U; + /* Wait for AHB master IDLE state. */ + do { + if (++count > 200000U) { + return -1; + } + } while ((USB_OTG_GLB->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0U); + + count = 0; USB_OTG_GLB->GRSTCTL = (USB_OTG_GRSTCTL_TXFFLSH | (num << 6)); do { @@ -208,7 +226,7 @@ static inline int dwc2_flush_txfifo(uint32_t num) return 0; } -static void dwc2_set_turnaroundtime(uint32_t hclk, uint8_t speed) +static void dwc2_set_turnaroundtime(uint8_t busid, uint32_t hclk, uint8_t speed) { uint32_t UsbTrd; @@ -261,7 +279,7 @@ static void dwc2_set_turnaroundtime(uint32_t hclk, uint8_t speed) USB_OTG_GLB->GUSBCFG |= (uint32_t)((UsbTrd << USB_OTG_GUSBCFG_TRDT_Pos) & USB_OTG_GUSBCFG_TRDT); } -static void dwc2_set_txfifo(uint8_t fifo, uint16_t size) +static void dwc2_set_txfifo(uint8_t busid, uint8_t fifo, uint16_t size) { uint8_t i; uint32_t tx_offset; @@ -293,7 +311,7 @@ static void dwc2_set_txfifo(uint8_t fifo, uint16_t size) USB_LOG_INFO("fifo%d size:%04x, offset:%04x\r\n", fifo, size, tx_offset); } -static uint8_t dwc2_get_devspeed(void) +static uint8_t dwc2_get_devspeed(uint8_t busid) { uint8_t speed; uint32_t DevEnumSpeed = USB_OTG_DEV->DSTS & USB_OTG_DSTS_ENUMSPD; @@ -310,7 +328,7 @@ static uint8_t dwc2_get_devspeed(void) return speed; } -static void dwc2_ep0_start_read_setup(uint8_t *psetup) +static void dwc2_ep0_start_read_setup(uint8_t busid, uint8_t *psetup) { USB_OTG_OUTEP(0U)->DOEPTSIZ = 0U; USB_OTG_OUTEP(0U)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_PKTCNT & (1U << 19)); @@ -324,7 +342,7 @@ static void dwc2_ep0_start_read_setup(uint8_t *psetup) #endif } -void dwc2_ep_write(uint8_t ep_idx, uint8_t *src, uint16_t len) +void dwc2_ep_write(uint8_t busid, uint8_t ep_idx, uint8_t *src, uint16_t len) { uint32_t *pSrc = (uint32_t *)src; uint32_t count32b, i; @@ -336,7 +354,7 @@ void dwc2_ep_write(uint8_t ep_idx, uint8_t *src, uint16_t len) } } -void dwc2_ep_read(uint8_t *dest, uint16_t len) +void dwc2_ep_read(uint8_t busid, uint8_t *dest, uint16_t len) { uint32_t *pDest = (uint32_t *)dest; uint32_t i; @@ -348,33 +366,44 @@ void dwc2_ep_read(uint8_t *dest, uint16_t len) } } -static void dwc2_tx_fifo_empty_procecss(uint8_t ep_idx) +static void dwc2_tx_fifo_empty_procecss(uint8_t busid, uint8_t ep_idx) { uint32_t len; uint32_t len32b; uint32_t fifoemptymsk; - len = g_dwc2_udc.in_ep[ep_idx].xfer_len - g_dwc2_udc.in_ep[ep_idx].actual_xfer_len; - if (len > g_dwc2_udc.in_ep[ep_idx].ep_mps) { - len = g_dwc2_udc.in_ep[ep_idx].ep_mps; + len = g_dwc2_udc[busid].in_ep[ep_idx].xfer_len - g_dwc2_udc[busid].in_ep[ep_idx].actual_xfer_len; + if (len > g_dwc2_udc[busid].in_ep[ep_idx].ep_mps) { + len = g_dwc2_udc[busid].in_ep[ep_idx].ep_mps; } len32b = (len + 3U) / 4U; while (((USB_OTG_INEP(ep_idx)->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV) >= len32b) && - (g_dwc2_udc.in_ep[ep_idx].actual_xfer_len < g_dwc2_udc.in_ep[ep_idx].xfer_len) && (g_dwc2_udc.in_ep[ep_idx].xfer_len != 0U)) { + (g_dwc2_udc[busid].in_ep[ep_idx].actual_xfer_len < g_dwc2_udc[busid].in_ep[ep_idx].xfer_len) && (g_dwc2_udc[busid].in_ep[ep_idx].xfer_len != 0U)) { /* Write the FIFO */ - len = g_dwc2_udc.in_ep[ep_idx].xfer_len - g_dwc2_udc.in_ep[ep_idx].actual_xfer_len; - if (len > g_dwc2_udc.in_ep[ep_idx].ep_mps) { - len = g_dwc2_udc.in_ep[ep_idx].ep_mps; + len = g_dwc2_udc[busid].in_ep[ep_idx].xfer_len - g_dwc2_udc[busid].in_ep[ep_idx].actual_xfer_len; + if (len > g_dwc2_udc[busid].in_ep[ep_idx].ep_mps) { + len = g_dwc2_udc[busid].in_ep[ep_idx].ep_mps; + } + if (g_dwc2_udc[busid].in_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_ISOCHRONOUS) { + if ((USB_OTG_DEV->DSTS & (1U << 8)) == 0U) { + USB_OTG_INEP(ep_idx)->DIEPCTL &= ~USB_OTG_DIEPCTL_SD0PID_SEVNFRM; + USB_OTG_INEP(ep_idx)->DIEPCTL |= USB_OTG_DIEPCTL_SODDFRM; + } else { + USB_OTG_INEP(ep_idx)->DIEPCTL &= ~USB_OTG_DIEPCTL_SODDFRM; + USB_OTG_INEP(ep_idx)->DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM; + } + USB_OTG_INEP(ep_idx)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_MULCNT); + USB_OTG_INEP(ep_idx)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_MULCNT & (1U << 29)); } - dwc2_ep_write(ep_idx, g_dwc2_udc.in_ep[ep_idx].xfer_buf, len); - g_dwc2_udc.in_ep[ep_idx].xfer_buf += len; - g_dwc2_udc.in_ep[ep_idx].actual_xfer_len += len; + dwc2_ep_write(busid, ep_idx, g_dwc2_udc[busid].in_ep[ep_idx].xfer_buf, len); + g_dwc2_udc[busid].in_ep[ep_idx].xfer_buf += len; + g_dwc2_udc[busid].in_ep[ep_idx].actual_xfer_len += len; } - if (g_dwc2_udc.in_ep[ep_idx].xfer_len <= g_dwc2_udc.in_ep[ep_idx].actual_xfer_len) { + if (g_dwc2_udc[busid].in_ep[ep_idx].xfer_len <= g_dwc2_udc[busid].in_ep[ep_idx].actual_xfer_len) { fifoemptymsk = (uint32_t)(0x1UL << (ep_idx & 0x0f)); USB_OTG_DEV->DIEPEMPMSK &= ~fifoemptymsk; } @@ -384,7 +413,7 @@ static void dwc2_tx_fifo_empty_procecss(uint8_t ep_idx) * @brief dwc2_get_glb_intstatus: return the global USB interrupt status * @retval status */ -static inline uint32_t dwc2_get_glb_intstatus(void) +static inline uint32_t dwc2_get_glb_intstatus(uint8_t busid) { uint32_t tmpreg; @@ -398,7 +427,7 @@ static inline uint32_t dwc2_get_glb_intstatus(void) * @brief dwc2_get_outeps_intstatus: return the USB device OUT endpoints interrupt status * @retval status */ -static inline uint32_t dwc2_get_outeps_intstatus(void) +static inline uint32_t dwc2_get_outeps_intstatus(uint8_t busid) { uint32_t tmpreg; @@ -412,7 +441,7 @@ static inline uint32_t dwc2_get_outeps_intstatus(void) * @brief dwc2_get_ineps_intstatus: return the USB device IN endpoints interrupt status * @retval status */ -static inline uint32_t dwc2_get_ineps_intstatus(void) +static inline uint32_t dwc2_get_ineps_intstatus(uint8_t busid) { uint32_t tmpreg; @@ -428,12 +457,13 @@ static inline uint32_t dwc2_get_ineps_intstatus(void) * This parameter can be a value from 0 to 15 * @retval Device OUT EP Interrupt register */ -static inline uint32_t dwc2_get_outep_intstatus(uint8_t epnum) +static inline uint32_t dwc2_get_outep_intstatus(uint8_t busid, uint8_t epnum) { uint32_t tmpreg; tmpreg = USB_OTG_OUTEP((uint32_t)epnum)->DOEPINT; - tmpreg &= USB_OTG_DEV->DOEPMSK; + USB_OTG_OUTEP((uint32_t)epnum)->DOEPINT = tmpreg; + tmpreg = tmpreg & USB_OTG_DEV->DOEPMSK; return tmpreg; } @@ -444,26 +474,21 @@ static inline uint32_t dwc2_get_outep_intstatus(uint8_t epnum) * This parameter can be a value from 0 to 15 * @retval Device IN EP Interrupt register */ -static inline uint32_t dwc2_get_inep_intstatus(uint8_t epnum) +static inline uint32_t dwc2_get_inep_intstatus(uint8_t busid, uint8_t epnum) { uint32_t tmpreg, msk, emp; msk = USB_OTG_DEV->DIEPMSK; emp = USB_OTG_DEV->DIEPEMPMSK; - msk |= ((emp >> (epnum & 0x07)) & 0x1U) << 7; - tmpreg = USB_OTG_INEP((uint32_t)epnum)->DIEPINT & msk; + msk |= ((emp >> (epnum & 0x0F)) & 0x1U) << 7; + + tmpreg = USB_OTG_INEP((uint32_t)epnum)->DIEPINT; + USB_OTG_INEP((uint32_t)epnum)->DIEPINT = tmpreg; + tmpreg = tmpreg & msk; return tmpreg; } -__WEAK void usb_dc_low_level_init(void) -{ -} - -__WEAK void usb_dc_low_level_deinit(void) -{ -} - int usb_dc_init(uint8_t busid) { int ret; @@ -473,9 +498,9 @@ int usb_dc_init(uint8_t busid) uint8_t endpoints; uint32_t fifo_num; - memset(&g_dwc2_udc, 0, sizeof(struct dwc2_udc)); + memset(&g_dwc2_udc[busid], 0, sizeof(struct dwc2_udc)); - usb_dc_low_level_init(); + usb_dc_low_level_init(busid); /* Full-Speed PHY Interface Type (FSPhyType) @@ -526,14 +551,10 @@ int usb_dc_init(uint8_t busid) /* This is vendor register */ USB_OTG_GLB->GCCFG = usbd_get_dwc2_gccfg_conf(USBD_BASE); - ret = dwc2_core_init(); + ret = dwc2_core_init(busid); /* Force Device Mode*/ - dwc2_set_mode(USB_OTG_MODE_DEVICE); - - /* B-peripheral session valid override enable */ - // USB_OTG_GLB->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN; - // USB_OTG_GLB->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; + dwc2_set_mode(busid, USB_OTG_MODE_DEVICE); for (uint8_t i = 0U; i < 15U; i++) { USB_OTG_GLB->DIEPTXF[i] = 0U; @@ -567,7 +588,7 @@ int usb_dc_init(uint8_t busid) /* Enable interrupts matching to the Device mode ONLY */ USB_OTG_GLB->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT | - USB_OTG_GINTMSK_IISOIXFRM | USB_OTG_GINTMSK_PXFRM_IISOOXFRM; + USB_OTG_GINTMSK_USBSUSPM | USB_OTG_GINTMSK_WUIM; #ifdef CONFIG_USB_DWC2_DMA_ENABLE if (((USB_OTG_GLB->GHWCFG2 & (0x3U << 3)) >> 3) != 2) { @@ -577,6 +598,7 @@ int usb_dc_init(uint8_t busid) } USB_OTG_DEV->DCFG &= ~USB_OTG_DCFG_DESCDMA; + USB_OTG_GLB->GAHBCFG &= ~USB_OTG_GAHBCFG_HBSTLEN; USB_OTG_GLB->GAHBCFG |= (USB_OTG_GAHBCFG_DMAEN | USB_OTG_GAHBCFG_HBSTLEN_4); #else USB_OTG_GLB->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM; @@ -590,10 +612,10 @@ int usb_dc_init(uint8_t busid) USB_OTG_GLB->GRXFSIZ = (CONFIG_USB_DWC2_RXALL_FIFO_SIZE); - dwc2_set_txfifo(0, CONFIG_USB_DWC2_TX0_FIFO_SIZE); - dwc2_set_txfifo(1, CONFIG_USB_DWC2_TX1_FIFO_SIZE); - dwc2_set_txfifo(2, CONFIG_USB_DWC2_TX2_FIFO_SIZE); - dwc2_set_txfifo(3, CONFIG_USB_DWC2_TX3_FIFO_SIZE); + dwc2_set_txfifo(busid, 0, CONFIG_USB_DWC2_TX0_FIFO_SIZE); + dwc2_set_txfifo(busid, 1, CONFIG_USB_DWC2_TX1_FIFO_SIZE); + dwc2_set_txfifo(busid, 2, CONFIG_USB_DWC2_TX2_FIFO_SIZE); + dwc2_set_txfifo(busid, 3, CONFIG_USB_DWC2_TX3_FIFO_SIZE); fifo_num = CONFIG_USB_DWC2_RXALL_FIFO_SIZE; fifo_num += CONFIG_USB_DWC2_TX0_FIFO_SIZE; @@ -601,23 +623,23 @@ int usb_dc_init(uint8_t busid) fifo_num += CONFIG_USB_DWC2_TX2_FIFO_SIZE; fifo_num += CONFIG_USB_DWC2_TX3_FIFO_SIZE; #if CONFIG_USBDEV_EP_NUM > 4 - dwc2_set_txfifo(4, CONFIG_USB_DWC2_TX4_FIFO_SIZE); + dwc2_set_txfifo(busid, 4, CONFIG_USB_DWC2_TX4_FIFO_SIZE); fifo_num += CONFIG_USB_DWC2_TX4_FIFO_SIZE; #endif #if CONFIG_USBDEV_EP_NUM > 5 - dwc2_set_txfifo(5, CONFIG_USB_DWC2_TX5_FIFO_SIZE); + dwc2_set_txfifo(busid, 5, CONFIG_USB_DWC2_TX5_FIFO_SIZE); fifo_num += CONFIG_USB_DWC2_TX5_FIFO_SIZE; #endif #if CONFIG_USBDEV_EP_NUM > 6 - dwc2_set_txfifo(6, CONFIG_USB_DWC2_TX6_FIFO_SIZE); + dwc2_set_txfifo(busid, 6, CONFIG_USB_DWC2_TX6_FIFO_SIZE); fifo_num += CONFIG_USB_DWC2_TX6_FIFO_SIZE; #endif #if CONFIG_USBDEV_EP_NUM > 7 - dwc2_set_txfifo(7, CONFIG_USB_DWC2_TX7_FIFO_SIZE); + dwc2_set_txfifo(busid, 7, CONFIG_USB_DWC2_TX7_FIFO_SIZE); fifo_num += CONFIG_USB_DWC2_TX7_FIFO_SIZE; #endif #if CONFIG_USBDEV_EP_NUM > 8 - dwc2_set_txfifo(8, CONFIG_USB_DWC2_TX8_FIFO_SIZE); + dwc2_set_txfifo(busid, 8, CONFIG_USB_DWC2_TX8_FIFO_SIZE); fifo_num += CONFIG_USB_DWC2_TX8_FIFO_SIZE; #endif @@ -634,8 +656,8 @@ int usb_dc_init(uint8_t busid) } } - ret = dwc2_flush_txfifo(0x10U); - ret = dwc2_flush_rxfifo(); + ret = dwc2_flush_txfifo(busid, 0x10U); + ret = dwc2_flush_rxfifo(busid); USB_OTG_GLB->GAHBCFG |= USB_OTG_GAHBCFG_GINT; USB_OTG_DEV->DCTL &= ~USB_OTG_DCTL_SDIS; @@ -660,10 +682,10 @@ int usb_dc_deinit(uint8_t busid) USB_OTG_DEV->DAINTMSK = 0U; /* Flush the FIFO */ - dwc2_flush_txfifo(0x10U); - dwc2_flush_rxfifo(); + dwc2_flush_txfifo(busid, 0x10U); + dwc2_flush_rxfifo(busid); - usb_dc_low_level_deinit(); + usb_dc_low_level_deinit(busid); return 0; } @@ -674,6 +696,17 @@ int usbd_set_address(uint8_t busid, const uint8_t addr) return 0; } +int usbd_set_remote_wakeup(uint8_t busid) +{ + if (!(USB_OTG_DEV->DSTS & USB_OTG_DSTS_SUSPSTS)) { + return -1; + } + USB_OTG_DEV->DCTL |= USB_OTG_DCTL_RWUSIG; + usbd_dwc2_delay_ms(10); + USB_OTG_DEV->DCTL &= ~USB_OTG_DCTL_RWUSIG; + return 0; +} + uint8_t usbd_get_port_speed(uint8_t busid) { uint8_t speed; @@ -701,8 +734,8 @@ int usbd_ep_open(uint8_t busid, const struct usb_endpoint_descriptor *ep) } if (USB_EP_DIR_IS_OUT(ep->bEndpointAddress)) { - g_dwc2_udc.out_ep[ep_idx].ep_mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); - g_dwc2_udc.out_ep[ep_idx].ep_type = USB_GET_ENDPOINT_TYPE(ep->bmAttributes); + g_dwc2_udc[busid].out_ep[ep_idx].ep_mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); + g_dwc2_udc[busid].out_ep[ep_idx].ep_type = USB_GET_ENDPOINT_TYPE(ep->bmAttributes); USB_OTG_DEV->DAINTMSK |= USB_OTG_DAINTMSK_OEPM & (uint32_t)(1UL << (16 + ep_idx)); @@ -724,8 +757,8 @@ int usbd_ep_open(uint8_t busid, const struct usb_endpoint_descriptor *ep) return -2; } - g_dwc2_udc.in_ep[ep_idx].ep_mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); - g_dwc2_udc.in_ep[ep_idx].ep_type = USB_GET_ENDPOINT_TYPE(ep->bmAttributes); + g_dwc2_udc[busid].in_ep[ep_idx].ep_mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); + g_dwc2_udc[busid].in_ep[ep_idx].ep_type = USB_GET_ENDPOINT_TYPE(ep->bmAttributes); USB_OTG_DEV->DAINTMSK |= USB_OTG_DAINTMSK_IEPM & (uint32_t)(1UL << ep_idx); @@ -735,7 +768,7 @@ int usbd_ep_open(uint8_t busid, const struct usb_endpoint_descriptor *ep) USB_OTG_DIEPCTL_SD0PID_SEVNFRM | USB_OTG_DIEPCTL_USBAEP; } - dwc2_flush_txfifo(ep_idx); + dwc2_flush_txfifo(busid, ep_idx); } return 0; } @@ -759,7 +792,7 @@ int usbd_ep_close(uint8_t busid, const uint8_t ep) } while ((USB_OTG_OUTEP(ep_idx)->DOEPINT & USB_OTG_DOEPINT_EPDISD) != USB_OTG_DOEPINT_EPDISD); /* Clear and unmask endpoint disabled interrupt */ - USB_OTG_OUTEP(ep_idx)->DOEPINT |= USB_OTG_DOEPINT_EPDISD; + USB_OTG_OUTEP(ep_idx)->DOEPINT = USB_OTG_DOEPINT_EPDISD; } USB_OTG_DEV->DEACHMSK &= ~(USB_OTG_DAINTMSK_OEPM & ((uint32_t)(1UL << (ep_idx & 0x07)) << 16)); @@ -779,7 +812,7 @@ int usbd_ep_close(uint8_t busid, const uint8_t ep) } while ((USB_OTG_INEP(ep_idx)->DIEPINT & USB_OTG_DIEPINT_EPDISD) != USB_OTG_DIEPINT_EPDISD); /* Clear and unmask endpoint disabled interrupt */ - USB_OTG_INEP(ep_idx)->DIEPINT |= USB_OTG_DIEPINT_EPDISD; + USB_OTG_INEP(ep_idx)->DIEPINT = USB_OTG_DIEPINT_EPDISD; } USB_OTG_DEV->DEACHMSK &= ~(USB_OTG_DAINTMSK_IEPM & (uint32_t)(1UL << (ep_idx & 0x07))); @@ -806,7 +839,7 @@ int usbd_ep_set_stall(uint8_t busid, const uint8_t ep) } #ifdef CONFIG_USB_DWC2_DMA_ENABLE if (ep_idx == 0) { - dwc2_ep0_start_read_setup((uint8_t *)&g_dwc2_udc.setup); + dwc2_ep0_start_read_setup(busid, (uint8_t *)&g_dwc2_udc[busid].setup); } #endif return 0; @@ -818,14 +851,14 @@ int usbd_ep_clear_stall(uint8_t busid, const uint8_t ep) if (USB_EP_DIR_IS_OUT(ep)) { USB_OTG_OUTEP(ep_idx)->DOEPCTL &= ~USB_OTG_DOEPCTL_STALL; - if ((g_dwc2_udc.out_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_INTERRUPT) || - (g_dwc2_udc.out_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_BULK)) { + if ((g_dwc2_udc[busid].out_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_INTERRUPT) || + (g_dwc2_udc[busid].out_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_BULK)) { USB_OTG_OUTEP(ep_idx)->DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM; /* DATA0 */ } } else { USB_OTG_INEP(ep_idx)->DIEPCTL &= ~USB_OTG_DIEPCTL_STALL; - if ((g_dwc2_udc.in_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_INTERRUPT) || - (g_dwc2_udc.in_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_BULK)) { + if ((g_dwc2_udc[busid].in_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_INTERRUPT) || + (g_dwc2_udc[busid].in_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_BULK)) { USB_OTG_INEP(ep_idx)->DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM; /* DATA0 */ } } @@ -834,8 +867,20 @@ int usbd_ep_clear_stall(uint8_t busid, const uint8_t ep) int usbd_ep_is_stalled(uint8_t busid, const uint8_t ep, uint8_t *stalled) { + uint8_t ep_idx = USB_EP_GET_IDX(ep); + if (USB_EP_DIR_IS_OUT(ep)) { + if (USB_OTG_OUTEP(ep_idx)->DOEPCTL & USB_OTG_DOEPCTL_STALL) { + *stalled = 1; + } else { + *stalled = 0; + } } else { + if (USB_OTG_INEP(ep_idx)->DIEPCTL & USB_OTG_DIEPCTL_STALL) { + *stalled = 1; + } else { + *stalled = 0; + } } return 0; } @@ -860,9 +905,9 @@ int usbd_ep_start_write(uint8_t busid, const uint8_t ep, const uint8_t *data, ui return -4; } - g_dwc2_udc.in_ep[ep_idx].xfer_buf = (uint8_t *)data; - g_dwc2_udc.in_ep[ep_idx].xfer_len = data_len; - g_dwc2_udc.in_ep[ep_idx].actual_xfer_len = 0; + g_dwc2_udc[busid].in_ep[ep_idx].xfer_buf = (uint8_t *)data; + g_dwc2_udc[busid].in_ep[ep_idx].xfer_len = data_len; + g_dwc2_udc[busid].in_ep[ep_idx].actual_xfer_len = 0; USB_OTG_INEP(ep_idx)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_PKTCNT); USB_OTG_INEP(ep_idx)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_XFRSIZ); @@ -874,20 +919,20 @@ int usbd_ep_start_write(uint8_t busid, const uint8_t ep, const uint8_t *data, ui } if (ep_idx == 0) { - if (data_len > g_dwc2_udc.in_ep[ep_idx].ep_mps) { - data_len = g_dwc2_udc.in_ep[ep_idx].ep_mps; + if (data_len > g_dwc2_udc[busid].in_ep[ep_idx].ep_mps) { + data_len = g_dwc2_udc[busid].in_ep[ep_idx].ep_mps; } - g_dwc2_udc.in_ep[ep_idx].xfer_len = data_len; + g_dwc2_udc[busid].in_ep[ep_idx].xfer_len = data_len; USB_OTG_INEP(ep_idx)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_PKTCNT & (1U << 19)); USB_OTG_INEP(ep_idx)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_XFRSIZ & data_len); } else { - pktcnt = (uint16_t)((data_len + g_dwc2_udc.in_ep[ep_idx].ep_mps - 1U) / g_dwc2_udc.in_ep[ep_idx].ep_mps); + pktcnt = (uint16_t)((data_len + g_dwc2_udc[busid].in_ep[ep_idx].ep_mps - 1U) / g_dwc2_udc[busid].in_ep[ep_idx].ep_mps); USB_OTG_INEP(ep_idx)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_PKTCNT & (pktcnt << 19)); USB_OTG_INEP(ep_idx)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_XFRSIZ & data_len); } - if (g_dwc2_udc.in_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_ISOCHRONOUS) { + if (g_dwc2_udc[busid].in_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_ISOCHRONOUS) { if ((USB_OTG_DEV->DSTS & (1U << 8)) == 0U) { USB_OTG_INEP(ep_idx)->DIEPCTL &= ~USB_OTG_DIEPCTL_SD0PID_SEVNFRM; USB_OTG_INEP(ep_idx)->DIEPCTL |= USB_OTG_DIEPCTL_SODDFRM; @@ -933,28 +978,28 @@ int usbd_ep_start_read(uint8_t busid, const uint8_t ep, uint8_t *data, uint32_t return -4; } - g_dwc2_udc.out_ep[ep_idx].xfer_buf = (uint8_t *)data; - g_dwc2_udc.out_ep[ep_idx].xfer_len = data_len; - g_dwc2_udc.out_ep[ep_idx].actual_xfer_len = 0; + g_dwc2_udc[busid].out_ep[ep_idx].xfer_buf = (uint8_t *)data; + g_dwc2_udc[busid].out_ep[ep_idx].xfer_len = data_len; + g_dwc2_udc[busid].out_ep[ep_idx].actual_xfer_len = 0; USB_OTG_OUTEP(ep_idx)->DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_PKTCNT); USB_OTG_OUTEP(ep_idx)->DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_XFRSIZ); if (data_len == 0) { USB_OTG_OUTEP(ep_idx)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_PKTCNT & (1 << 19)); - USB_OTG_OUTEP(ep_idx)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_XFRSIZ & g_dwc2_udc.out_ep[ep_idx].ep_mps); + USB_OTG_OUTEP(ep_idx)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_XFRSIZ & g_dwc2_udc[busid].out_ep[ep_idx].ep_mps); USB_OTG_OUTEP(ep_idx)->DOEPCTL |= (USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA); return 0; } if (ep_idx == 0) { - if (data_len > g_dwc2_udc.out_ep[ep_idx].ep_mps) { - data_len = g_dwc2_udc.out_ep[ep_idx].ep_mps; + if (data_len > g_dwc2_udc[busid].out_ep[ep_idx].ep_mps) { + data_len = g_dwc2_udc[busid].out_ep[ep_idx].ep_mps; } - g_dwc2_udc.out_ep[ep_idx].xfer_len = data_len; + g_dwc2_udc[busid].out_ep[ep_idx].xfer_len = data_len; USB_OTG_OUTEP(ep_idx)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_PKTCNT & (1U << 19)); USB_OTG_OUTEP(ep_idx)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_XFRSIZ & data_len); } else { - pktcnt = (uint16_t)((data_len + g_dwc2_udc.out_ep[ep_idx].ep_mps - 1U) / g_dwc2_udc.out_ep[ep_idx].ep_mps); + pktcnt = (uint16_t)((data_len + g_dwc2_udc[busid].out_ep[ep_idx].ep_mps - 1U) / g_dwc2_udc[busid].out_ep[ep_idx].ep_mps); USB_OTG_OUTEP(ep_idx)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_PKTCNT & (pktcnt << 19)); USB_OTG_OUTEP(ep_idx)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_XFRSIZ & data_len); @@ -963,7 +1008,7 @@ int usbd_ep_start_read(uint8_t busid, const uint8_t ep, uint8_t *data, uint32_t #ifdef CONFIG_USB_DWC2_DMA_ENABLE USB_OTG_OUTEP(ep_idx)->DOEPDMA = (uint32_t)data; #endif - if (g_dwc2_udc.out_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_ISOCHRONOUS) { + if (g_dwc2_udc[busid].out_ep[ep_idx].ep_type == USB_ENDPOINT_TYPE_ISOCHRONOUS) { if ((USB_OTG_DEV->DSTS & (1U << 8)) == 0U) { USB_OTG_OUTEP(ep_idx)->DOEPCTL &= ~USB_OTG_DOEPCTL_SD0PID_SEVNFRM; USB_OTG_OUTEP(ep_idx)->DOEPCTL |= USB_OTG_DOEPCTL_SODDFRM; @@ -979,7 +1024,7 @@ int usbd_ep_start_read(uint8_t busid, const uint8_t ep, uint8_t *data, uint32_t void USBD_IRQHandler(uint8_t busid) { uint32_t gint_status, temp, ep_idx, ep_intr, epint, read_count, daintmask; - gint_status = dwc2_get_glb_intstatus(); + gint_status = dwc2_get_glb_intstatus(busid); if ((USB_OTG_GLB->GINTSTS & 0x1U) == USB_OTG_MODE_DEVICE) { /* Avoid spurious interrupt */ @@ -998,12 +1043,12 @@ void USBD_IRQHandler(uint8_t busid) if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> USB_OTG_GRXSTSP_PKTSTS_Pos) == STS_DATA_UPDT) { read_count = (temp & USB_OTG_GRXSTSP_BCNT) >> 4; if (read_count != 0) { - dwc2_ep_read(g_dwc2_udc.out_ep[ep_idx].xfer_buf, read_count); - g_dwc2_udc.out_ep[ep_idx].xfer_buf += read_count; + dwc2_ep_read(busid, g_dwc2_udc[busid].out_ep[ep_idx].xfer_buf, read_count); + g_dwc2_udc[busid].out_ep[ep_idx].xfer_buf += read_count; } } else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> USB_OTG_GRXSTSP_PKTSTS_Pos) == STS_SETUP_UPDT) { read_count = (temp & USB_OTG_GRXSTSP_BCNT) >> 4; - dwc2_ep_read((uint8_t *)&g_dwc2_udc.setup, read_count); + dwc2_ep_read(busid, (uint8_t *)&g_dwc2_udc[busid].setup, read_count); } else { /* ... */ } @@ -1012,32 +1057,30 @@ void USBD_IRQHandler(uint8_t busid) #endif if (gint_status & USB_OTG_GINTSTS_OEPINT) { ep_idx = 0; - ep_intr = dwc2_get_outeps_intstatus(); + ep_intr = dwc2_get_outeps_intstatus(busid); while (ep_intr != 0U) { if ((ep_intr & 0x1U) != 0U) { - epint = dwc2_get_outep_intstatus(ep_idx); - uint32_t DoepintReg = USB_OTG_OUTEP(ep_idx)->DOEPINT; - USB_OTG_OUTEP(ep_idx)->DOEPINT = DoepintReg; + epint = dwc2_get_outep_intstatus(busid, ep_idx); if ((epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC) { if (ep_idx == 0) { - if (g_dwc2_udc.out_ep[ep_idx].xfer_len == 0) { + if (g_dwc2_udc[busid].out_ep[ep_idx].xfer_len == 0) { /* Out status, start reading setup */ - dwc2_ep0_start_read_setup((uint8_t *)&g_dwc2_udc.setup); + dwc2_ep0_start_read_setup(busid, (uint8_t *)&g_dwc2_udc[busid].setup); } else { - g_dwc2_udc.out_ep[ep_idx].actual_xfer_len = g_dwc2_udc.out_ep[ep_idx].xfer_len - ((USB_OTG_OUTEP(ep_idx)->DOEPTSIZ) & USB_OTG_DOEPTSIZ_XFRSIZ); - g_dwc2_udc.out_ep[ep_idx].xfer_len = 0; - usbd_event_ep_out_complete_handler(0, 0x00, g_dwc2_udc.out_ep[ep_idx].actual_xfer_len); + g_dwc2_udc[busid].out_ep[ep_idx].actual_xfer_len = g_dwc2_udc[busid].out_ep[ep_idx].xfer_len - ((USB_OTG_OUTEP(ep_idx)->DOEPTSIZ) & USB_OTG_DOEPTSIZ_XFRSIZ); + g_dwc2_udc[busid].out_ep[ep_idx].xfer_len = 0; + usbd_event_ep_out_complete_handler(busid, 0x00, g_dwc2_udc[busid].out_ep[ep_idx].actual_xfer_len); } } else { - g_dwc2_udc.out_ep[ep_idx].actual_xfer_len = g_dwc2_udc.out_ep[ep_idx].xfer_len - ((USB_OTG_OUTEP(ep_idx)->DOEPTSIZ) & USB_OTG_DOEPTSIZ_XFRSIZ); - g_dwc2_udc.out_ep[ep_idx].xfer_len = 0; - usbd_event_ep_out_complete_handler(0, ep_idx, g_dwc2_udc.out_ep[ep_idx].actual_xfer_len); + g_dwc2_udc[busid].out_ep[ep_idx].actual_xfer_len = g_dwc2_udc[busid].out_ep[ep_idx].xfer_len - ((USB_OTG_OUTEP(ep_idx)->DOEPTSIZ) & USB_OTG_DOEPTSIZ_XFRSIZ); + g_dwc2_udc[busid].out_ep[ep_idx].xfer_len = 0; + usbd_event_ep_out_complete_handler(busid, ep_idx, g_dwc2_udc[busid].out_ep[ep_idx].actual_xfer_len); } } if ((epint & USB_OTG_DOEPINT_STUP) == USB_OTG_DOEPINT_STUP) { - usbd_event_ep0_setup_complete_handler(0, (uint8_t *)&g_dwc2_udc.setup); + usbd_event_ep0_setup_complete_handler(busid, (uint8_t *)&g_dwc2_udc[busid].setup); } } ep_intr >>= 1U; @@ -1046,34 +1089,32 @@ void USBD_IRQHandler(uint8_t busid) } if (gint_status & USB_OTG_GINTSTS_IEPINT) { ep_idx = 0U; - ep_intr = dwc2_get_ineps_intstatus(); + ep_intr = dwc2_get_ineps_intstatus(busid); while (ep_intr != 0U) { if ((ep_intr & 0x1U) != 0U) { - epint = dwc2_get_inep_intstatus(ep_idx); - uint32_t DiepintReg = USB_OTG_INEP(ep_idx)->DIEPINT; - USB_OTG_INEP(ep_idx)->DIEPINT = DiepintReg; + epint = dwc2_get_inep_intstatus(busid, ep_idx); if ((epint & USB_OTG_DIEPINT_XFRC) == USB_OTG_DIEPINT_XFRC) { if (ep_idx == 0) { - g_dwc2_udc.in_ep[ep_idx].actual_xfer_len = g_dwc2_udc.in_ep[ep_idx].xfer_len - ((USB_OTG_INEP(ep_idx)->DIEPTSIZ) & USB_OTG_DIEPTSIZ_XFRSIZ); - g_dwc2_udc.in_ep[ep_idx].xfer_len = 0; - usbd_event_ep_in_complete_handler(0, 0x80, g_dwc2_udc.in_ep[ep_idx].actual_xfer_len); + g_dwc2_udc[busid].in_ep[ep_idx].actual_xfer_len = g_dwc2_udc[busid].in_ep[ep_idx].xfer_len - ((USB_OTG_INEP(ep_idx)->DIEPTSIZ) & USB_OTG_DIEPTSIZ_XFRSIZ); + g_dwc2_udc[busid].in_ep[ep_idx].xfer_len = 0; + usbd_event_ep_in_complete_handler(busid, 0x80, g_dwc2_udc[busid].in_ep[ep_idx].actual_xfer_len); - if (g_dwc2_udc.setup.wLength && ((g_dwc2_udc.setup.bmRequestType & USB_REQUEST_DIR_MASK) == USB_REQUEST_DIR_OUT)) { + if (g_dwc2_udc[busid].setup.wLength && ((g_dwc2_udc[busid].setup.bmRequestType & USB_REQUEST_DIR_MASK) == USB_REQUEST_DIR_OUT)) { /* In status, start reading setup */ - dwc2_ep0_start_read_setup((uint8_t *)&g_dwc2_udc.setup); - } else if (g_dwc2_udc.setup.wLength == 0) { + dwc2_ep0_start_read_setup(busid, (uint8_t *)&g_dwc2_udc[busid].setup); + } else if (g_dwc2_udc[busid].setup.wLength == 0) { /* In status, start reading setup */ - dwc2_ep0_start_read_setup((uint8_t *)&g_dwc2_udc.setup); + dwc2_ep0_start_read_setup(busid, (uint8_t *)&g_dwc2_udc[busid].setup); } } else { - g_dwc2_udc.in_ep[ep_idx].actual_xfer_len = g_dwc2_udc.in_ep[ep_idx].xfer_len - ((USB_OTG_INEP(ep_idx)->DIEPTSIZ) & USB_OTG_DIEPTSIZ_XFRSIZ); - g_dwc2_udc.in_ep[ep_idx].xfer_len = 0; - usbd_event_ep_in_complete_handler(0, ep_idx | 0x80, g_dwc2_udc.in_ep[ep_idx].actual_xfer_len); + g_dwc2_udc[busid].in_ep[ep_idx].actual_xfer_len = g_dwc2_udc[busid].in_ep[ep_idx].xfer_len - ((USB_OTG_INEP(ep_idx)->DIEPTSIZ) & USB_OTG_DIEPTSIZ_XFRSIZ); + g_dwc2_udc[busid].in_ep[ep_idx].xfer_len = 0; + usbd_event_ep_in_complete_handler(busid, ep_idx | 0x80, g_dwc2_udc[busid].in_ep[ep_idx].actual_xfer_len); } } if ((epint & USB_OTG_DIEPINT_TXFE) == USB_OTG_DIEPINT_TXFE) { - dwc2_tx_fifo_empty_procecss(ep_idx); + dwc2_tx_fifo_empty_procecss(busid, ep_idx); } } ep_intr >>= 1U; @@ -1081,11 +1122,11 @@ void USBD_IRQHandler(uint8_t busid) } } if (gint_status & USB_OTG_GINTSTS_USBRST) { - USB_OTG_GLB->GINTSTS |= USB_OTG_GINTSTS_USBRST; + USB_OTG_GLB->GINTSTS = USB_OTG_GINTSTS_USBRST; USB_OTG_DEV->DCTL &= ~USB_OTG_DCTL_RWUSIG; - dwc2_flush_txfifo(0x10U); - dwc2_flush_rxfifo(); + dwc2_flush_txfifo(busid, 0x10U); + dwc2_flush_rxfifo(busid); for (uint8_t i = 0U; i < CONFIG_USBDEV_EP_NUM; i++) { if (i == 0U) { @@ -1116,69 +1157,35 @@ void USBD_IRQHandler(uint8_t busid) USB_OTG_DEV->DIEPMSK = USB_OTG_DIEPMSK_XFRCM; - memset(&g_dwc2_udc, 0, sizeof(struct dwc2_udc)); - usbd_event_reset_handler(0); + memset(&g_dwc2_udc[busid], 0, sizeof(struct dwc2_udc)); + usbd_event_reset_handler(busid); /* Start reading setup */ - dwc2_ep0_start_read_setup((uint8_t *)&g_dwc2_udc.setup); + dwc2_ep0_start_read_setup(busid, (uint8_t *)&g_dwc2_udc[busid].setup); } if (gint_status & USB_OTG_GINTSTS_ENUMDNE) { - USB_OTG_GLB->GINTSTS |= USB_OTG_GINTSTS_ENUMDNE; - dwc2_set_turnaroundtime(SystemCoreClock, dwc2_get_devspeed()); + USB_OTG_GLB->GINTSTS = USB_OTG_GINTSTS_ENUMDNE; + dwc2_set_turnaroundtime(busid, SystemCoreClock, dwc2_get_devspeed(busid)); USB_OTG_DEV->DCTL |= USB_OTG_DCTL_CGINAK; } if (gint_status & USB_OTG_GINTSTS_PXFR_INCOMPISOOUT) { - daintmask = USB_OTG_DEV->DAINTMSK; - daintmask >>= 16; - - for (ep_idx = 1; ep_idx < CONFIG_USBDEV_EP_NUM; ep_idx++) { - if ((BIT(ep_idx) & ~daintmask) || (g_dwc2_udc.out_ep[ep_idx].ep_type != USB_ENDPOINT_TYPE_ISOCHRONOUS)) - continue; - if (!(USB_OTG_OUTEP(ep_idx)->DOEPCTL & USB_OTG_DOEPCTL_USBAEP)) - continue; - - if ((USB_OTG_DEV->DSTS & (1U << 8)) != 0U) { - USB_OTG_OUTEP(ep_idx)->DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM; - USB_OTG_OUTEP(ep_idx)->DOEPCTL &= ~USB_OTG_DOEPCTL_SODDFRM; - } else { - USB_OTG_OUTEP(ep_idx)->DOEPCTL &= ~USB_OTG_DOEPCTL_SD0PID_SEVNFRM; - USB_OTG_OUTEP(ep_idx)->DOEPCTL |= USB_OTG_DOEPCTL_SODDFRM; - } - } - - USB_OTG_GLB->GINTSTS |= USB_OTG_GINTSTS_PXFR_INCOMPISOOUT; + USB_OTG_GLB->GINTSTS = USB_OTG_GINTSTS_PXFR_INCOMPISOOUT; } if (gint_status & USB_OTG_GINTSTS_IISOIXFR) { - daintmask = USB_OTG_DEV->DAINTMSK; - daintmask >>= 16; - - for (ep_idx = 1; ep_idx < CONFIG_USBDEV_EP_NUM; ep_idx++) { - if (((BIT(ep_idx) & ~daintmask)) || (g_dwc2_udc.in_ep[ep_idx].ep_type != USB_ENDPOINT_TYPE_ISOCHRONOUS)) - continue; - - if (!(USB_OTG_INEP(ep_idx)->DIEPCTL & USB_OTG_DIEPCTL_USBAEP)) - continue; - - if ((USB_OTG_DEV->DSTS & (1U << 8)) != 0U) { - USB_OTG_INEP(ep_idx)->DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM; - USB_OTG_INEP(ep_idx)->DIEPCTL &= ~USB_OTG_DIEPCTL_SODDFRM; - } else { - USB_OTG_INEP(ep_idx)->DIEPCTL &= ~USB_OTG_DIEPCTL_SD0PID_SEVNFRM; - USB_OTG_INEP(ep_idx)->DIEPCTL |= USB_OTG_DIEPCTL_SODDFRM; - } - } - USB_OTG_GLB->GINTSTS |= USB_OTG_GINTSTS_IISOIXFR; + USB_OTG_GLB->GINTSTS = USB_OTG_GINTSTS_IISOIXFR; } if (gint_status & USB_OTG_GINTSTS_SOF) { - USB_OTG_GLB->GINTSTS |= USB_OTG_GINTSTS_SOF; + USB_OTG_GLB->GINTSTS = USB_OTG_GINTSTS_SOF; } if (gint_status & USB_OTG_GINTSTS_USBSUSP) { - USB_OTG_GLB->GINTSTS |= USB_OTG_GINTSTS_USBSUSP; + USB_OTG_GLB->GINTSTS = USB_OTG_GINTSTS_USBSUSP; + usbd_event_suspend_handler(busid); } if (gint_status & USB_OTG_GINTSTS_WKUINT) { - USB_OTG_GLB->GINTSTS |= USB_OTG_GINTSTS_WKUINT; + USB_OTG_GLB->GINTSTS = USB_OTG_GINTSTS_WKUINT; + usbd_event_resume_handler(busid); } if (gint_status & USB_OTG_GINTSTS_OTGINT) { temp = USB_OTG_GLB->GOTGINT; diff --git a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_dwc2_reg.h b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_dwc2_reg.h index c3bd75d..5f9b5f9 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_dwc2_reg.h +++ b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_dwc2_reg.h @@ -1716,6 +1716,9 @@ typedef struct #define USB_UNMASK_HALT_HC_INT(chnum) (USB_OTG_HC(chnum)->HCINTMSK |= USB_OTG_HCINTMSK_CHHM) #define CLEAR_HC_INT(chnum, __INTERRUPT__) (USB_OTG_HC(chnum)->HCINT = (__INTERRUPT__)) +void usb_dc_low_level_init(uint8_t busid); +void usb_dc_low_level_deinit(uint8_t busid); uint32_t usbd_get_dwc2_gccfg_conf(uint32_t reg_base); uint32_t usbh_get_dwc2_gccfg_conf(uint32_t reg_base); +void usbd_dwc2_delay_ms(uint8_t ms); #endif diff --git a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_at.c b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_at.c index d9c2626..8d45f65 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_at.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_at.c @@ -7,16 +7,20 @@ #include "stdint.h" #include "usb_dwc2_reg.h" +extern unsigned int system_core_clock; + +uint32_t SystemCoreClock; /* you can find this config in function: usb_global_init, file:at32fxxx_usb.c, for example: * * usbx->gccfg_bit.pwrdown = TRUE; * usbx->gccfg_bit.avalidsesen = TRUE; * usbx->gccfg_bit.bvalidsesen = TRUE; - * + * */ uint32_t usbd_get_dwc2_gccfg_conf(uint32_t reg_base) { + SystemCoreClock = system_core_clock; #ifdef CONFIG_USB_HS return ((1 << 16) | (1 << 21)); #else @@ -35,6 +39,7 @@ uint32_t usbd_get_dwc2_gccfg_conf(uint32_t reg_base) uint32_t usbh_get_dwc2_gccfg_conf(uint32_t reg_base) { + SystemCoreClock = system_core_clock; #ifdef CONFIG_USB_HS return ((1 << 16) | (1 << 21)); #else @@ -49,4 +54,9 @@ uint32_t usbh_get_dwc2_gccfg_conf(uint32_t reg_base) return ((1 << 16) | (1 << 21)); #endif #endif +} + +void usbd_dwc2_delay_ms(uint8_t ms) +{ + /* implement later */ } \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_esp.c b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_esp.c index 8c63234..76abb19 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_esp.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_esp.c @@ -8,13 +8,19 @@ #include "esp_intr_alloc.h" #include "esp_private/usb_phy.h" #include "soc/periph_defs.h" +#include "freertos/FreeRTOS.h" #include "usbd_core.h" #include "usbh_core.h" #ifdef CONFIG_IDF_TARGET_ESP32S2 #define DEFAULT_CPU_FREQ_MHZ CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ +#define DEFAULT_USB_INTR_SOURCE ETS_USB_INTR_SOURCE #elif CONFIG_IDF_TARGET_ESP32S3 #define DEFAULT_CPU_FREQ_MHZ CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ +#define DEFAULT_USB_INTR_SOURCE ETS_USB_INTR_SOURCE +#elif CONFIG_IDF_TARGET_ESP32P4 +#define DEFAULT_CPU_FREQ_MHZ CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ +#define DEFAULT_USB_INTR_SOURCE ETS_USB_OTG_INTR_SOURCE #else #define DEFAULT_CPU_FREQ_MHZ 160 #endif @@ -29,7 +35,7 @@ static void usb_dc_interrupt_cb(void *arg_pv) USBD_IRQHandler(0); } -void usb_dc_low_level_init(void) +void usb_dc_low_level_init(uint8_t busid) { usb_phy_config_t phy_config = { .controller = USB_PHY_CTRL_OTG, @@ -44,15 +50,15 @@ void usb_dc_low_level_init(void) } // TODO: Check when to enable interrupt - ret = esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LEVEL2, usb_dc_interrupt_cb, 0, &s_interrupt_handle); + ret = esp_intr_alloc(DEFAULT_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, usb_dc_interrupt_cb, 0, &s_interrupt_handle); if (ret != ESP_OK) { USB_LOG_ERR("USB Interrupt Init Failed!\r\n"); return; } - USB_LOG_INFO("cherryusb, version: 0x%06x\r\n", CHERRYUSB_VERSION); + USB_LOG_INFO("cherryusb, version: "CHERRYUSB_VERSION_STR"\r\n"); } -void usb_dc_low_level_deinit(void) +void usb_dc_low_level_deinit(uint8_t busid) { if (s_interrupt_handle) { esp_intr_free(s_interrupt_handle); @@ -94,12 +100,12 @@ void usb_hc_low_level_init(struct usbh_bus *bus) } // TODO: Check when to enable interrupt - ret = esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LEVEL2, usb_hc_interrupt_cb, 0, &s_interrupt_handle); + ret = esp_intr_alloc(DEFAULT_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, usb_hc_interrupt_cb, 0, &s_interrupt_handle); if (ret != ESP_OK) { USB_LOG_ERR("USB Interrupt Init Failed!\r\n"); return; } - USB_LOG_INFO("cherryusb, version: 0x%06x\r\n", CHERRYUSB_VERSION); + USB_LOG_INFO("cherryusb, version: "CHERRYUSB_VERSION_STR"\r\n"); } void usb_hc_low_level_deinit(struct usbh_bus *bus) @@ -118,3 +124,8 @@ uint32_t usbh_get_dwc2_gccfg_conf(uint32_t reg_base) { return 0; } + +void usbd_dwc2_delay_ms(uint8_t ms) +{ + vTaskDelay(pdMS_TO_TICKS(ms)); +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_gd.c b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_gd.c index bdc0d68..f3fa4d9 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_gd.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_gd.c @@ -7,10 +7,14 @@ #include "stdint.h" #include "usb_dwc2_reg.h" +#if CONFIG_USBDEV_EP_NUM != 4 && CONFIG_USBDEV_EP_NUM != 6 +#error "gd32 only has 4 endpoints for pa11/pa12 and 6 endpoints for pb14/pb15" +#endif + /* you can find this config in function:usb_core_init, file:drv_usb_core.c, for example: * * usb_regs->gr->GCCFG |= GCCFG_PWRON | GCCFG_VBUSACEN | GCCFG_VBUSBCEN; - * + * */ uint32_t usbd_get_dwc2_gccfg_conf(uint32_t reg_base) @@ -29,4 +33,9 @@ uint32_t usbh_get_dwc2_gccfg_conf(uint32_t reg_base) #else return ((1 << 16) | (1 << 18) | (1 << 19) | (1 << 21)); #endif +} + +void usbd_dwc2_delay_ms(uint8_t ms) +{ + /* implement later */ } \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_hc.c b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_hc.c index 9bebe6d..431b5db 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_hc.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_hc.c @@ -24,3 +24,8 @@ uint32_t usbh_get_dwc2_gccfg_conf(uint32_t reg_base) USB_OTG_GLB->GOTGCTL &= ~USB_OTG_GOTGCTL_BVALOVAL; return 0; } + +void usbd_dwc2_delay_ms(uint8_t ms) +{ + /* implement later */ +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_kendryte.c b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_kendryte.c new file mode 100644 index 0000000..de10352 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_kendryte.c @@ -0,0 +1,157 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "usbd_core.h" +#include "usbh_core.h" + +#define DEFAULT_USB_HCLK_FREQ_MHZ 200 + +uint32_t SystemCoreClock = (DEFAULT_USB_HCLK_FREQ_MHZ * 1000 * 1000); +uintptr_t g_usb_otg0_base = (uintptr_t)0x91500000UL; +uintptr_t g_usb_otg1_base = (uintptr_t)0x91540000UL; + +static void sysctl_reset_hw_done(volatile uint32_t *reset_reg, uint8_t reset_bit, uint8_t done_bit) +{ + *reset_reg |= (1 << done_bit); /* clear done bit */ + rt_thread_mdelay(1); + + *reset_reg |= (1 << reset_bit); /* set reset bit */ + rt_thread_mdelay(1); + /* check done bit */ + while (*reset_reg & (1 << done_bit) == 0) + ; +} + +#define USB_IDPULLUP0 (1 << 4) +#define USB_DMPULLDOWN0 (1 << 8) +#define USB_DPPULLDOWN0 (1 << 9) + +#ifdef PKG_CHERRYUSB_HOST +static void usb_hc_interrupt_cb(int irq, void *arg_pv) +{ + extern void USBH_IRQHandler(uint8_t busid); + USBH_IRQHandler((uint8_t)(uintptr_t)arg_pv); +} + +void usb_hc_low_level_init(struct usbh_bus *bus) +{ + uint32_t *hs_reg; + uint32_t usb_ctl3; + + if (bus->hcd.hcd_id == 0) { + sysctl_reset_hw_done((volatile uint32_t *)0x9110103c, 0, 28); + + hs_reg = (uint32_t *)rt_ioremap((void *)(0x91585000 + 0x7C), 0x1000); + usb_ctl3 = *hs_reg | USB_IDPULLUP0; + + *hs_reg = usb_ctl3 | (USB_DMPULLDOWN0 | USB_DPPULLDOWN0); + + rt_iounmap(hs_reg); + + rt_hw_interrupt_install(173, usb_hc_interrupt_cb, NULL, "usbh0"); + rt_hw_interrupt_umask(173); + + } else { + sysctl_reset_hw_done((volatile uint32_t *)0x9110103c, 1, 29); + + hs_reg = (uint32_t *)rt_ioremap((void *)(0x91585000 + 0x9C), 0x1000); + usb_ctl3 = *hs_reg | USB_IDPULLUP0; + + *hs_reg = usb_ctl3 | (USB_DMPULLDOWN0 | USB_DPPULLDOWN0); + + rt_iounmap(hs_reg); + + rt_hw_interrupt_install(174, usb_hc_interrupt_cb, 1, "usbh1"); + rt_hw_interrupt_umask(174); + } +} + +void usb_hc_low_level_deinit(struct usbh_bus *bus) +{ + if (bus->hcd.hcd_id == 0) { + rt_hw_interrupt_mask(173); + } else { + rt_hw_interrupt_mask(174); + } +} + +uint32_t usbh_get_dwc2_gccfg_conf(uint32_t reg_base) +{ + return 0; +} +#endif + +#ifdef PKG_CHERRYUSB_DEVICE +static void usb_dc_interrupt_cb(int irq, void *arg_pv) +{ + extern void USBD_IRQHandler(uint8_t busid); + USBD_IRQHandler(0); +} + +#ifdef CHERRYUSB_DEVICE_USING_USB0 +void usb_dc_low_level_init(uint8_t busid) +{ + sysctl_reset_hw_done((volatile uint32_t *)0x9110103c, 0, 28); + uint32_t *hs_reg = (uint32_t *)rt_ioremap((void *)(0x91585000 + 0x7C), 0x1000); + *hs_reg = 0x37; + rt_iounmap(hs_reg); + + rt_hw_interrupt_install(173, usb_dc_interrupt_cb, NULL, "usbd"); + rt_hw_interrupt_umask(173); +} + +void usb_dc_low_level_deinit(uint8_t busid) +{ + rt_hw_interrupt_mask(173); +} +#else +void usb_dc_low_level_init(uint8_t busid) +{ + sysctl_reset_hw_done((volatile uint32_t *)0x9110103c, 1, 29); + uint32_t *hs_reg = (uint32_t *)rt_ioremap((void *)(0x91585000 + 0x9C), 0x1000); + *hs_reg = 0x37; + rt_iounmap(hs_reg); + + rt_hw_interrupt_install(174, usb_dc_interrupt_cb, NULL, "usbd"); + rt_hw_interrupt_umask(174); +} + +void usb_dc_low_level_deinit(uint8_t busid) +{ + rt_hw_interrupt_mask(174); +} +#endif +uint32_t usbd_get_dwc2_gccfg_conf(uint32_t reg_base) +{ + return 0; +} + +void usbd_dwc2_delay_ms(uint8_t ms) +{ + /* implement later */ +} +#endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_st.c b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_st.c index 94293d2..4dff31f 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_st.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_glue_st.c @@ -13,9 +13,11 @@ * USBx->GCCFG |= USB_OTG_GCCFG_NOVBUSSENS; * USBx->GCCFG &= ~USB_OTG_GCCFG_VBUSBSEN; * USBx->GCCFG &= ~USB_OTG_GCCFG_VBUSASEN; - * + * */ +extern void HAL_Delay(uint32_t Delay); + #if defined(STM32F722xx) || defined(STM32F723xx) || defined(STM32F730xx) || defined(STM32F732xx) || defined(STM32F733xx) /** * @brief USB_HS_PHY_Registers @@ -164,6 +166,8 @@ uint32_t usbd_get_dwc2_gccfg_conf(uint32_t reg_base) USB_OTG_GLB->GCCFG = (1 << 23); usb_hsphy_init(25000000U); return (1 << 23); /* Enable USB HS PHY USBx->GCCFG |= USB_OTG_GCCFG_PHYHSEN;*/ +#elif __has_include("stm32h7rsxx.h") + return (1 << 21); #else return 0; #endif @@ -190,6 +194,8 @@ uint32_t usbh_get_dwc2_gccfg_conf(uint32_t reg_base) USB_OTG_GLB->GCCFG = (1 << 23); usb_hsphy_init(25000000U); return (1 << 23); /* Enable USB HS PHY USBx->GCCFG |= USB_OTG_GCCFG_PHYHSEN;*/ +#elif __has_include("stm32h7rsxx.h") + return (1 << 21); #else return 0; #endif @@ -201,3 +207,8 @@ uint32_t usbh_get_dwc2_gccfg_conf(uint32_t reg_base) #endif #endif } + +void usbd_dwc2_delay_ms(uint8_t ms) +{ + HAL_Delay(ms); +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_hc_dwc2.c b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_hc_dwc2.c index ddcbaf2..09f5517 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_hc_dwc2.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/dwc2/usb_hc_dwc2.c @@ -26,7 +26,7 @@ * 1 location each for Bulk/Control endpoint for handling NAK/NYET scenario */ #ifndef CONFIG_USB_DWC2_RX_FIFO_SIZE -#define CONFIG_USB_DWC2_RX_FIFO_SIZE ((1012 - CONFIG_USB_DWC2_NPTX_FIFO_SIZE - CONFIG_USB_DWC2_PTX_FIFO_SIZE) / 4) +#define CONFIG_USB_DWC2_RX_FIFO_SIZE ((1012 - CONFIG_USB_DWC2_NPTX_FIFO_SIZE - CONFIG_USB_DWC2_PTX_FIFO_SIZE)) #endif #define USB_OTG_GLB ((DWC2_GlobalTypeDef *)(bus->hcd.reg_base)) @@ -114,12 +114,22 @@ static inline void dwc2_set_mode(struct usbh_bus *bus, uint8_t mode) } else if (mode == USB_OTG_MODE_DEVICE) { USB_OTG_GLB->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; } + + usb_osal_msleep(50); } static inline int dwc2_flush_rxfifo(struct usbh_bus *bus) { - volatile uint32_t count = 0; + volatile uint32_t count = 0U; + /* Wait for AHB master IDLE state. */ + do { + if (++count > 200000U) { + return -1; + } + } while ((USB_OTG_GLB->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0U); + + count = 0; USB_OTG_GLB->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; do { @@ -135,6 +145,14 @@ static inline int dwc2_flush_txfifo(struct usbh_bus *bus, uint32_t num) { volatile uint32_t count = 0U; + /* Wait for AHB master IDLE state. */ + do { + if (++count > 200000U) { + return -1; + } + } while ((USB_OTG_GLB->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0U); + + count = 0; USB_OTG_GLB->GRSTCTL = (USB_OTG_GRSTCTL_TXFFLSH | (num << 6)); do { @@ -197,6 +215,12 @@ static inline void dwc2_chan_char_init(struct usbh_bus *bus, uint8_t ch_num, uin regval |= USB_OTG_HCCHAR_EPDIR; } + if ((usbh_get_port_speed(bus, 0) == USB_SPEED_HIGH) && (speed != USB_SPEED_HIGH)) { + USB_LOG_ERR("Do not support LS/FS device on HS hub\r\n"); + while (1) { + } + } + /* LS device plugged to HUB */ if ((speed == USB_SPEED_LOW) && (usbh_get_port_speed(bus, 0) != USB_SPEED_LOW)) { regval |= USB_OTG_HCCHAR_LSDEV; @@ -424,10 +448,12 @@ static void dwc2_iso_urb_init(struct usbh_bus *bus, uint8_t chidx, struct usbh_u __WEAK void usb_hc_low_level_init(struct usbh_bus *bus) { + (void)bus; } __WEAK void usb_hc_low_level_deinit(struct usbh_bus *bus) { + (void)bus; } int usb_hc_init(struct usbh_bus *bus) @@ -478,9 +504,6 @@ int usb_hc_init(struct usbh_bus *bus) /* Restart the Phy Clock */ USB_OTG_PCGCCTL = 0U; - dwc2_drivebus(bus, 1); - usb_osal_msleep(200); - /* Set default Max speed support */ USB_OTG_HOST->HCFG &= ~(USB_OTG_HCFG_FSLSS); @@ -504,6 +527,7 @@ int usb_hc_init(struct usbh_bus *bus) ret = dwc2_flush_txfifo(bus, 0x10U); ret = dwc2_flush_rxfifo(bus); + USB_OTG_GLB->GAHBCFG &= ~USB_OTG_GAHBCFG_HBSTLEN; USB_OTG_GLB->GAHBCFG |= USB_OTG_GAHBCFG_HBSTLEN_4; USB_OTG_GLB->GAHBCFG |= USB_OTG_GAHBCFG_DMAEN; @@ -511,6 +535,9 @@ int usb_hc_init(struct usbh_bus *bus) USB_OTG_GLB->GINTMSK |= (USB_OTG_GINTMSK_PRTIM | USB_OTG_GINTMSK_HCIM | USB_OTG_GINTSTS_DISCINT); + dwc2_drivebus(bus, 1); + usb_osal_msleep(200); + USB_OTG_GLB->GAHBCFG |= USB_OTG_GAHBCFG_GINT; return ret; @@ -627,6 +654,7 @@ int usbh_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, u case HUB_PORT_FEATURE_C_SUSPEND: break; case HUB_PORT_FEATURE_POWER: + dwc2_drivebus(bus, 0); break; case HUB_PORT_FEATURE_C_CONNECTION: g_dwc2_hcd[bus->hcd.hcd_id].port_csc = 0; @@ -652,7 +680,7 @@ int usbh_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, u case HUB_PORT_FEATURE_SUSPEND: break; case HUB_PORT_FEATURE_POWER: - USB_OTG_HPRT &= ~USB_OTG_HPRT_PPWR; + dwc2_drivebus(bus, 1); break; case HUB_PORT_FEATURE_RESET: usbh_reset_port(bus, port); @@ -748,8 +776,8 @@ int usbh_submit_urb(struct usbh_urb *urb) } } else { /* Check if intr and iso pipe tx fifo is overflow */ - if (((USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize) == USB_ENDPOINT_TYPE_ISOCHRONOUS) || - (USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize) == USB_ENDPOINT_TYPE_INTERRUPT)) && + if (((USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes) == USB_ENDPOINT_TYPE_ISOCHRONOUS) || + (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT)) && USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize) > (CONFIG_USB_DWC2_PTX_FIFO_SIZE * 4)) { return -USB_ERR_RANGE; } else { @@ -874,6 +902,7 @@ static void dwc2_inchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) //printf("s1:%08x\r\n", chan_intstatus); if (chan_intstatus & USB_OTG_HCINT_CHH) { + USB_OTG_HC(ch_num)->HCINT = chan_intstatus; if (chan_intstatus & USB_OTG_HCINT_XFRC) { urb->errorcode = 0; @@ -927,7 +956,6 @@ static void dwc2_inchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) urb->errorcode = -USB_ERR_IO; dwc2_urb_waitup(urb); } - USB_OTG_HC(ch_num)->HCINT = chan_intstatus; } } @@ -944,6 +972,7 @@ static void dwc2_outchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) //printf("s2:%08x\r\n", chan_intstatus); if (chan_intstatus & USB_OTG_HCINT_CHH) { + USB_OTG_HC(ch_num)->HCINT = chan_intstatus; if (chan_intstatus & USB_OTG_HCINT_XFRC) { urb->errorcode = 0; @@ -1008,7 +1037,6 @@ static void dwc2_outchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) urb->errorcode = -USB_ERR_IO; dwc2_urb_waitup(urb); } - USB_OTG_HC(ch_num)->HCINT = chan_intstatus; } } diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ehci/README.md b/rt-thread/components/drivers/usb/cherryusb/port/ehci/README.md index 48b9a2b..8160c65 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ehci/README.md +++ b/rt-thread/components/drivers/usb/cherryusb/port/ehci/README.md @@ -22,6 +22,17 @@ - d13x, d21x +### NXP + +Modify USB_NOCACHE_RAM_SECTION + +``` +#define USB_NOCACHE_RAM_SECTION __attribute__((section(".NonCacheable"))) +``` + +- IMRT10XX/IMRT11XX +- MCXN9XX/MCXN236 + ### Intel - Intel 6 Series Chipset and Intel C200 Series Chipset diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_ehci_priv.h b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_ehci_priv.h deleted file mode 100644 index ba1d351..0000000 --- a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_ehci_priv.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2022, sakumisu - * - * SPDX-License-Identifier: Apache-2.0 - */ -#ifndef _USB_EHCI_PRIV_H -#define _USB_EHCI_PRIV_H - -#include "usbh_core.h" -#include "usbh_hub.h" -#include "usb_hc_ehci.h" - -#define EHCI_HCCR ((struct ehci_hccr *)(uintptr_t)(bus->hcd.reg_base + CONFIG_USB_EHCI_HCCR_OFFSET)) -#define EHCI_HCOR ((struct ehci_hcor *)(uintptr_t)(bus->hcd.reg_base + CONFIG_USB_EHCI_HCCR_OFFSET + g_ehci_hcd[bus->hcd.hcd_id].hcor_offset)) - -#define EHCI_PTR2ADDR(x) ((uint32_t)(uintptr_t)(x) & ~0x1F) -#define EHCI_ADDR2QH(x) ((struct ehci_qh_hw *)(uintptr_t)((uint32_t)(x) & ~0x1F)) -#define EHCI_ADDR2QTD(x) ((struct ehci_qtd_hw *)(uintptr_t)((uint32_t)(x) & ~0x1F)) -#define EHCI_ADDR2ITD(x) ((struct ehci_itd_hw *)(uintptr_t)((uint32_t)(x) & ~0x1F)) - -#ifndef CONFIG_USB_EHCI_QH_NUM -#define CONFIG_USB_EHCI_QH_NUM CONFIG_USBHOST_PIPE_NUM -#endif -#ifndef CONFIG_USB_EHCI_QTD_NUM -#define CONFIG_USB_EHCI_QTD_NUM 3 -#endif -#ifndef CONFIG_USB_EHCI_ITD_NUM -#define CONFIG_USB_EHCI_ITD_NUM 5 -#endif -#ifndef CONFIG_USB_EHCI_ISO_NUM -#define CONFIG_USB_EHCI_ISO_NUM 4 -#endif - -extern uint8_t usbh_get_port_speed(struct usbh_bus *bus, const uint8_t port); - -struct ehci_qtd_hw { - struct ehci_qtd hw; - struct usbh_urb *urb; - uint32_t length; -} __attribute__((aligned(32))); - -struct ehci_qh_hw { - struct ehci_qh hw; - struct ehci_qtd_hw qtd_pool[CONFIG_USB_EHCI_QTD_NUM]; - uint32_t first_qtd; - struct usbh_urb *urb; - usb_osal_sem_t waitsem; - uint8_t remove_in_iaad; -} __attribute__((aligned(32))); - -struct ehci_itd_hw { - struct ehci_itd hw; - struct usbh_urb *urb; - uint16_t start_frame; - uint8_t mf_unmask; - uint8_t mf_valid; - uint32_t pkt_idx[8]; -} __attribute__((aligned(32))); - -struct ehci_iso_hw -{ - struct ehci_itd_hw itd_pool[CONFIG_USB_EHCI_ITD_NUM]; - uint32_t itd_num; -}; - -struct ehci_hcd { - bool ehci_qh_used[CONFIG_USB_EHCI_QH_NUM]; - bool ehci_iso_used[CONFIG_USB_EHCI_ISO_NUM]; - bool ppc; /* Port Power Control */ - bool has_tt; /* if use tt instead of Companion Controller */ - uint8_t n_cc; /* Number of Companion Controller */ - uint8_t n_pcc; /* Number of ports supported per companion host controller */ - uint8_t n_ports; - uint8_t hcor_offset; -}; - -extern struct ehci_hcd g_ehci_hcd[CONFIG_USBHOST_MAX_BUS]; -extern uint32_t g_framelist[CONFIG_USBHOST_MAX_BUS][USB_ALIGN_UP(CONFIG_USB_EHCI_FRAME_LIST_SIZE, 1024)]; - -int ehci_iso_urb_init(struct usbh_bus *bus, struct usbh_urb *urb); -void ehci_kill_iso_urb(struct usbh_bus *bus, struct usbh_urb *urb); -void ehci_scan_isochronous_list(struct usbh_bus *bus); - -#endif diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_ehci_reg.h b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_ehci_reg.h new file mode 100644 index 0000000..741eef6 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_ehci_reg.h @@ -0,0 +1,393 @@ +/**************************************************************************** + * include/nuttx/usb/ehci.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ +/* + * Copyright 2022 sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __INCLUDE_NUTTX_USB_EHCI_H +#define __INCLUDE_NUTTX_USB_EHCI_H + +#define EHCI_FULL_SPEED (0) /* Full-Speed (12Mbs) */ +#define EHCI_LOW_SPEED (1) /* Low-Speed (1.5Mbs) */ +#define EHCI_HIGH_SPEED (2) /* High-Speed (480 Mb/s) */ + +/* Host Controller Capability Register Bit Definitions **********************/ + +/* Structural Parameters. Paragraph 2.2.3 */ + +#define EHCI_HCSPARAMS_NPORTS_SHIFT (0) /* Bit 0-3: Number of physical downstream ports */ +#define EHCI_HCSPARAMS_NPORTS_MASK (15 << EHCI_HCSPARAMS_NPORTS_SHIFT) +#define EHCI_HCSPARAMS_PPC (1 << 4) /* Bit 4: Port Power Control */ +#define EHCI_HCSPARAMS_PRR (1 << 7) /* Bit 7: Port Routing Rules */ +#define EHCI_HCSPARAMS_NPCC_SHIFT (8) /* Bit 8-11: Number of Ports per Companion Controller */ +#define EHCI_HCSPARAMS_NPCC_MASK (15 << EHCI_HCSPARAMS_NPCC_SHIFT) +#define EHCI_HCSPARAMS_NCC_SHIFT (12) /* Bit 12-15: Number of Companion Controllers */ +#define EHCI_HCSPARAMS_NCC_MASK (15 << EHCI_HCSPARAMS_NCC_SHIFT) +#define EHCI_HCSPARAMS_PIND (1 << 16) /* Bit 16: Port Indicators */ +#define EHCI_HCSPARAMS_DBGPORT_SHIFT (20) /* Bit 20-23: Debug Port Number */ +#define EHCI_HCSPARAMS_DBGPORT_MASK (15 << EHCI_HCSPARAMS_DBGPORT_SHIFT) + +/* Capability Parameters. Paragraph 2.2.4 */ + +#define EHCI_HCCPARAMS_64BIT (1 << 0) /* Bit 0: 64-bit Addressing Capability */ +#define EHCI_HCCPARAMS_PFLF (1 << 1) /* Bit 1: Programmable Frame List Flag */ +#define EHCI_HCCPARAMS_ASPC (1 << 2) /* Bit 2: Asynchronous Schedule Park Capability */ +#define EHCI_HCCPARAMS_IST_SHIFT (4) /* Bits 4-7: Isochronous Scheduling Threshold */ +#define EHCI_HCCPARAMS_IST_MASK (15 << EHCI_HCCPARAMS_IST_SHIFT) +#define EHCI_HCCPARAMS_EECP_SHIFT (8) /* Bits 8-15: EHCI Extended Capabilities Pointer */ +#define EHCI_HCCPARAMS_EECP_MASK (0xff << EHCI_HCCPARAMS_EECP_SHIFT) + +/* Host Controller Operational Register Bit Definitions *********************/ + +/* USB Command. Paragraph 2.3.1 */ + +#define EHCI_USBCMD_RUN (1 << 0) /* Bit 0: Run/Stop */ +#define EHCI_USBCMD_HCRESET (1 << 1) /* Bit 1: Host Controller Reset */ +#define EHCI_USBCMD_FLSIZE_SHIFT (2) /* Bits 2-3: Frame List Size */ +#define EHCI_USBCMD_FLSIZE_MASK (3 << EHCI_USBCMD_FLSIZE_SHIFT) +#define EHCI_USBCMD_FLSIZE_1024 (0 << EHCI_USBCMD_FLSIZE_SHIFT) /* 1024 elements (4096 bytes) */ +#define EHCI_USBCMD_FLSIZE_512 (1 << EHCI_USBCMD_FLSIZE_SHIFT) /* 512 elements (2048 bytes) */ +#define EHCI_USBCMD_FLSIZE_256 (2 << EHCI_USBCMD_FLSIZE_SHIFT) /* 256 elements (1024 bytes) */ +#define EHCI_USBCMD_PSEN (1 << 4) /* Bit 4: Periodic Schedule Enable */ +#define EHCI_USBCMD_ASEN (1 << 5) /* Bit 5: Asynchronous Schedule Enable */ +#define EHCI_USBCMD_IAAD (1 << 6) /* Bit 6: Interrupt on Async Advance Doorbell */ +#define EHCI_USBCMD_LRESET (1 << 7) /* Bit 7: Light Host Controller Reset */ +#define EHCI_USBCMD_ASYNC_PARKCNT_SHIFT (8) /* Bits 8-9: Asynchronous Schedule Park Mode Count */ +#define EHCI_USBCMD_ASYNC_PARKCNT_MASK (3 << EHCI_USBCMD_ASYNC_PARKCNT_SHIFT) +#define EHCI_USBCMD_ASYNC_PARK (1 << 11) /* Bit 11: Asynchronous Schedule Park Mode Enable */ +#define EHCI_USBCMD_ITHRE_SHIFT (16) /* Bits 16-23: Interrupt Threshold Control */ +#define EHCI_USBCMD_ITHRE_MASK (0xff << EHCI_USBCMD_ITHRE_SHIFT) +#define EHCI_USBCMD_ITHRE_1MF (0x01 << EHCI_USBCMD_ITHRE_SHIFT) /* 1 micro-frame */ +#define EHCI_USBCMD_ITHRE_2MF (0x02 << EHCI_USBCMD_ITHRE_SHIFT) /* 2 micro-frames */ +#define EHCI_USBCMD_ITHRE_4MF (0x04 << EHCI_USBCMD_ITHRE_SHIFT) /* 4 micro-frames */ +#define EHCI_USBCMD_ITHRE_8MF (0x08 << EHCI_USBCMD_ITHRE_SHIFT) /* 8 micro-frames (default, 1 ms) */ +#define EHCI_USBCMD_ITHRE_16MF (0x10 << EHCI_USBCMD_ITHRE_SHIFT) /* 16 micro-frames (2 ms) */ +#define EHCI_USBCMD_ITHRE_32MF (0x20 << EHCI_USBCMD_ITHRE_SHIFT) /* 32 micro-frames (4 ms) */ +#define EHCI_USBCMD_ITHRE_64MF (0x40 << EHCI_USBCMD_ITHRE_SHIFT) /* 64 micro-frames (8 ms) */ + +/* USB Status. Paragraph 2.3.2 */ + +#define EHCI_USBSTS_INT (1 << 0) /* Bit 0: USB Interrupt */ +#define EHCI_USBSTS_ERR (1 << 1) /* Bit 1: USB Error Interrupt */ +#define EHCI_USBSTS_PCD (1 << 2) /* Bit 2: Port Change Detect */ +#define EHCI_USBSTS_FLR (1 << 3) /* Bit 3: Frame List Rollover */ +#define EHCI_USBSTS_FATAL (1 << 4) /* Bit 4: Host System Error */ +#define EHCI_USBSTS_IAA (1 << 5) /* Bit 5: Interrupt on Async Advance */ +#define EHCI_USBSTS_HALTED (1 << 12) /* Bit 12: HC Halted */ +#define EHCI_USBSTS_RECLAM (1 << 13) /* Bit 13: Reclamation */ +#define EHCI_USBSTS_PSS (1 << 14) /* Bit 14: Periodic Schedule Status */ +#define EHCI_USBSTS_ASS (1 << 15) /* Bit 15: Asynchronous Schedule Status */ + /* Bits 16-31: Reserved */ + +/* USB Interrupt Enable. Paragraph 2.3.3 */ + +#define EHCI_USBIE_INT (1 << 0) /* Bit 0: USB Interrupt */ +#define EHCI_USBIE_ERR (1 << 1) /* Bit 1: USB Error Interrupt */ +#define EHCI_USBIE_PCD (1 << 2) /* Bit 2: Port Change Detect */ +#define EHCI_USBIE_FLROLL (1 << 3) /* Bit 3: Frame List Rollover */ +#define EHCI_USBIE_FATAL (1 << 4) /* Bit 4: Host System Error */ +#define EHCI_USBIE_IAA (1 << 5) /* Bit 5: Interrupt on Async Advance */ +#define EHCI_USBIE_ALLINTS (0x3f) /* Bits 0-5: All interrupts */ + +/* USB Frame Index. Paragraph 2.3.4 */ + +#define EHCI_FRINDEX_MASK (0x3fff) /* Bits 0-13: Frame index */ + +/* 4G Segment Selector. + * Paragraph 2.3.5, Bits[64:32] of data structure addresses + */ + +/* Frame List Base Address. Paragraph 2.3.6 */ +#define EHCI_PERIODICLISTBASE_MASK (0xfffff000) /* Bits 12-31: Base Address (Low) */ + +/* Next Asynchronous List Address. Paragraph 2.3.7 */ + +#define EHCI_ASYNCLISTADDR_MASK (0xffffffe0) /* Bits 5-31: Link Pointer Low (LPL) */ + +/* Configured Flag Register. Paragraph 2.3.8 */ + +#define EHCI_CONFIGFLAG (1 << 0) /* Bit 0: Configure Flag */ + +/* Port Status/Control, Port 1-n. Paragraph 2.3.9 */ + +#define EHCI_PORTSC_CCS (1 << 0) /* Bit 0: Current Connect Status */ +#define EHCI_PORTSC_CSC (1 << 1) /* Bit 1: Connect Status Change */ +#define EHCI_PORTSC_PE (1 << 2) /* Bit 2: Port Enable */ +#define EHCI_PORTSC_PEC (1 << 3) /* Bit 3: Port Enable/Disable Change */ +#define EHCI_PORTSC_OCA (1 << 4) /* Bit 4: Over-current Active */ +#define EHCI_PORTSC_OCC (1 << 5) /* Bit 5: Over-current Change */ +#define EHCI_PORTSC_RESUME (1 << 6) /* Bit 6: Force Port Resume */ +#define EHCI_PORTSC_SUSPEND (1 << 7) /* Bit 7: Suspend */ +#define EHCI_PORTSC_RESET (1 << 8) /* Bit 8: Port Reset */ +#define EHCI_PORTSC_LSTATUS_SHIFT (10) /* Bits 10-11: Line Status */ +#define EHCI_PORTSC_LSTATUS_MASK (3 << EHCI_PORTSC_LSTATUS_SHIFT) +#define EHCI_PORTSC_LSTATUS_SE0 (0 << EHCI_PORTSC_LSTATUS_SHIFT) /* SE0 Not Low-speed device, perform EHCI reset */ +#define EHCI_PORTSC_LSTATUS_KSTATE (1 << EHCI_PORTSC_LSTATUS_SHIFT) /* K-state Low-speed device, release ownership of port */ +#define EHCI_PORTSC_LSTATUS_JSTATE (2 << EHCI_PORTSC_LSTATUS_SHIFT) /* J-state Not Low-speed device, perform EHCI reset */ +#define EHCI_PORTSC_PP (1 << 12) /* Bit 12: Port Power */ +#define EHCI_PORTSC_OWNER (1 << 13) /* Bit 13: Port Owner */ +#define EHCI_PORTSC_PIC_SHIFT (14) /* Bits 14-15: Port Indicator Control */ +#define EHCI_PORTSC_PIC_MASK (3 << EHCI_PORTSC_PIC_SHIFT) +#define EHCI_PORTSC_PIC_OFF (0 << EHCI_PORTSC_PIC_SHIFT) /* Port indicators are off */ +#define EHCI_PORTSC_PIC_AMBER (1 << EHCI_PORTSC_PIC_SHIFT) /* Amber */ +#define EHCI_PORTSC_PIC_GREEN (2 << EHCI_PORTSC_PIC_SHIFT) /* Green */ +#define EHCI_PORTSC_PTC_SHIFT (16) /* Bits 16-19: Port Test Control */ +#define EHCI_PORTSC_PTC_MASK (15 << EHCI_PORTSC_PTC_SHIFT) +#define EHCI_PORTSC_PTC_DISABLED (0 << EHCI_PORTSC_PTC_SHIFT) /* Test mode not enabled */ +#define EHCI_PORTSC_PTC_JSTATE (1 << EHCI_PORTSC_PTC_SHIFT) /* Test J_STATE */ +#define EHCI_PORTSC_PTC_KSTATE (2 << EHCI_PORTSC_PTC_SHIFT) /* Test K_STATE */ +#define EHCI_PORTSC_PTC_SE0NAK (3 << EHCI_PORTSC_PTC_SHIFT) /* Test SE0_NAK */ +#define EHCI_PORTSC_PTC_PACKET (4 << EHCI_PORTSC_PTC_SHIFT) /* Test Packet */ +#define EHCI_PORTSC_PTC_ENABLE (5 << EHCI_PORTSC_PTC_SHIFT) /* Test FORCE_ENABLE */ +#define EHCI_PORTSC_WKCCNTE (1 << 20) /* Bit 20: Wake on Connect Enable */ +#define EHCI_PORTSC_WKDSCNNTE (1 << 21) /* Bit 21: Wake on Disconnect Enable */ +#define EHCI_PORTSC_WKOCE (1 << 22) /* Bit 22: Wake on Over-current Enable */ + /* Bits 23-31: Reserved */ + +#define EHCI_PORTSC_ALLINTS (EHCI_PORTSC_CSC | EHCI_PORTSC_PEC | \ + EHCI_PORTSC_OCC | EHCI_PORTSC_RESUME) + +/* Queue Head. Paragraph 3.6 */ + +/* Queue Head Horizontal Link Pointer: Queue Head DWord 0. Table 3-19 */ + +#define QH_HLP_END 0x1 + +#define QH_HLP_ITD(x) (((uint32_t)(x) & ~0x1F) | 0x0) /* Isochronous Transfer Descriptor */ +#define QH_HLP_QH(x) (((uint32_t)(x) & ~0x1F) | 0x2) /* Queue Head */ +#define QH_HLP_SITD(x) (((uint32_t)(x) & ~0x1F) | 0x4) /* Split Transaction Isochronous Transfer Descriptor */ +#define QH_HLP_FSTN(x) (((uint32_t)(x) & ~0x1F) | 0x6) /* Frame Span Traversal Node */ + +/* Endpoint Characteristics: Queue Head DWord 1. Table 3-19 */ + +#define QH_EPCHAR_DEVADDR_SHIFT (0) /* Bitx 0-6: Device Address */ +#define QH_EPCHAR_DEVADDR_MASK (0x7f << QH_EPCHAR_DEVADDR_SHIFT) +#define QH_EPCHAR_I (1 << 7) /* Bit 7: Inactivate on Next Transaction */ +#define QH_EPCHAR_ENDPT_SHIFT (8) /* Bitx 8-11: Endpoint Number */ +#define QH_EPCHAR_ENDPT_MASK (15 << QH_EPCHAR_ENDPT_SHIFT) +#define QH_EPCHAR_EPS_SHIFT (12) /* Bitx 12-13: Endpoint Speed */ +#define QH_EPCHAR_EPS_MASK (3 << QH_EPCHAR_EPS_SHIFT) +#define QH_EPCHAR_EPS_FULL (0 << QH_EPCHAR_EPS_SHIFT) /* Full-Speed (12Mbs) */ +#define QH_EPCHAR_EPS_LOW (1 << QH_EPCHAR_EPS_SHIFT) /* Low-Speed (1.5Mbs) */ +#define QH_EPCHAR_EPS_HIGH (2 << QH_EPCHAR_EPS_SHIFT) /* High-Speed (480 Mb/s) */ +#define QH_EPCHAR_DTC (1 << 14) /* Bit 14: Data Toggle Control */ +#define QH_EPCHAR_H (1 << 15) /* Bit 15: Head of Reclamation List Flag */ +#define QH_EPCHAR_MAXPKT_SHIFT (16) /* Bitx 16-26: Maximum Packet Length */ +#define QH_EPCHAR_MAXPKT_MASK (0x7ff << QH_EPCHAR_MAXPKT_SHIFT) +#define QH_EPCHAR_C (1 << 27) /* Bit 27: Control Endpoint Flag */ +#define QH_EPCHAR_RL_SHIFT (28) /* Bitx 28-31: Nak Count Reload */ +#define QH_EPCHAR_RL_MASK (15 << QH_EPCHAR_RL_SHIFT) + +/* Endpoint Capabilities: Queue Head DWord 2. Table 3-20 */ + +#define QH_EPCAPS_SSMASK_SHIFT (0) /* Bitx 0-7: Interrupt Schedule Mask (Frame S-mask) */ +#define QH_EPCAPS_SSMASK_MASK (0xff << QH_EPCAPS_SSMASK_SHIFT) +#define QH_EPCAPS_SSMASK(n) ((n) << QH_EPCAPS_SSMASK_SHIFT) +#define QH_EPCAPS_SCMASK_SHIFT (8) /* Bitx 8-15: Split Completion Mask (Frame C-Mask) */ +#define QH_EPCAPS_SCMASK_MASK (0xff << QH_EPCAPS_SCMASK_SHIFT) +#define QH_EPCAPS_SCMASK(n) ((n) << QH_EPCAPS_SCMASK_SHIFT) +#define QH_EPCAPS_HUBADDR_SHIFT (16) /* Bitx 16-22: Hub Address */ +#define QH_EPCAPS_HUBADDR_MASK (0x7f << QH_EPCAPS_HUBADDR_SHIFT) +#define QH_EPCAPS_HUBADDR(n) ((n) << QH_EPCAPS_HUBADDR_SHIFT) +#define QH_EPCAPS_PORT_SHIFT (23) /* Bit 23-29: Port Number */ +#define QH_EPCAPS_PORT_MASK (0x7f << QH_EPCAPS_PORT_SHIFT) +#define QH_EPCAPS_PORT(n) ((n) << QH_EPCAPS_PORT_SHIFT) +#define QH_EPCAPS_MULT_SHIFT (30) /* Bit 30-31: High-Bandwidth Pipe Multiplier */ +#define QH_EPCAPS_MULT_MASK (3 << QH_EPCAPS_MULT_SHIFT) +#define QH_EPCAPS_MULT(n) ((n) << QH_EPCAPS_MULT_SHIFT) + +/* qTD Token. Paragraph 3.5.3 */ + +#define QTD_LIST_END 1 + +#define QTD_TOKEN_STATUS_SHIFT (0) /* Bits 0-7: Status */ +#define QTD_TOKEN_STATUS_MASK (0xff << QTD_TOKEN_STATUS_SHIFT) +#define QTD_TOKEN_STATUS_PINGSTATE (1 << 0) /* Bit 0 Ping State */ +#define QTD_TOKEN_STATUS_ERR (1 << 0) /* Bit 0 Error */ +#define QTD_TOKEN_STATUS_SPLITXSTATE (1 << 1) /* Bit 1 Split Transaction State */ +#define QTD_TOKEN_STATUS_MMF (1 << 2) /* Bit 2 Missed Micro-Frame */ +#define QTD_TOKEN_STATUS_XACTERR (1 << 3) /* Bit 3 Transaction Error */ +#define QTD_TOKEN_STATUS_BABBLE (1 << 4) /* Bit 4 Babble Detected */ +#define QTD_TOKEN_STATUS_DBERR (1 << 5) /* Bit 5 Data Buffer Error */ +#define QTD_TOKEN_STATUS_HALTED (1 << 6) /* Bit 6 Halted */ +#define QTD_TOKEN_STATUS_ACTIVE (1 << 7) /* Bit 7 Active */ +#define QTD_TOKEN_STATUS_ERRORS (0x78 << QTD_TOKEN_STATUS_SHIFT) +#define QTD_TOKEN_PID_SHIFT (8) /* Bits 8-9: PID Code */ +#define QTD_TOKEN_PID_MASK (3 << QTD_TOKEN_PID_SHIFT) +#define QTD_TOKEN_PID_OUT (0 << QTD_TOKEN_PID_SHIFT) /* OUT Token generates token (E1H) */ +#define QTD_TOKEN_PID_IN (1 << QTD_TOKEN_PID_SHIFT) /* IN Token generates token (69H) */ +#define QTD_TOKEN_PID_SETUP (2 << QTD_TOKEN_PID_SHIFT) /* SETUP Token generates token (2DH) */ +#define QTD_TOKEN_CERR_SHIFT (10) /* Bits 10-11: Error Counter */ +#define QTD_TOKEN_CERR_MASK (3 << QTD_TOKEN_CERR_SHIFT) +#define QTD_TOKEN_CPAGE_SHIFT (12) /* Bits 12-14: Current Page */ +#define QTD_TOKEN_CPAGE_MASK (7 << QTD_TOKEN_CPAGE_SHIFT) +#define QTD_TOKEN_IOC (1 << 15) /* Bit 15: Interrupt On Complete */ +#define QTD_TOKEN_NBYTES_SHIFT (16) /* Bits 16-30: Total Bytes to Transfer */ +#define QTD_TOKEN_NBYTES_MASK (0x7fff << QTD_TOKEN_NBYTES_SHIFT) +#define QTD_TOKEN_TOGGLE (1 << 31) /* Bit 31: Data Toggle */ + +/* Isochronous (High-Speed) Transfer Descriptor (iTD). Paragraph 3.3 */ + +/* iTD Next Link Pointer. Paragraph 3.3.1 */ + +#define ITD_NLP_ITD(x) (((uint32_t)(x) & ~0x1F) | 0x0) +#define ITD_NLP_QH(x) (((uint32_t)(x) & ~0x1F) | 0x2) +#define ITD_NLP_SITD(x) (((uint32_t)(x) & ~0x1F) | 0x4) +#define ITD_NLP_FSTN(x) (((uint32_t)(x) & ~0x1F) | 0x6) + +/* iTD Transaction Status and Control List. Paragraph 3.3.2 */ +#define ITD_TSCL_XOFFS_SHIFT (0) /* Bits 0-11: Transaction X offset */ +#define ITD_TSCL_XOFFS_MASK (0xfff << ITD_TSCL_XOFFS_SHIFT) +#define ITD_TSCL_PG_SHIFT (12) /* Bits 12-14: Page select */ +#define ITD_TSCL_PG_MASK (7 << ITD_TSCL_PG_SHIFT) +#define ITD_TSCL_IOC (1 << 15) /* Bit 15: Interrupt On Comp */ +#define ITD_TSCL_LENGTH_SHIFT (16) /* Bits 16-27: Transaction length */ +#define ITD_TSCL_LENGTH_MASK (0xfff << ITD_TSCL_LENGTH_SHIFT) +#define ITD_TSCL_STATUS_SHIFT (28) /* Bits 28-31: Transaction status */ +#define ITD_TSCL_STATUS_MASK (15 << ITD_TSCL_STATUS_SHIFT) +#define ITD_TSCL_STATUS_XACTERR (1 << 28) /* Bit 28: Transaction error */ +#define ITD_TSCL_STATUS_BABBLE (1 << 29) /* Bit 29: Babble Detected */ +#define ITD_TSCL_STATUS_DBERROR (1 << 30) /* Bit 30: Data Buffer Error */ +#define ITD_TSCL_STATUS_ACTIVE (1 << 31) /* Bit 31: Active error */ + +/* iTD Buffer Page Pointer List. Paragraph 3.3.4 */ + +/* iTD Buffer Pointer Page 0. Table 3-4 */ + +#define ITD_BUFPTR0_DEVADDR_SHIFT (0) /* Bits 0-6: Device Address */ +#define ITD_BUFPTR0_DEVADDR_MASK (0x7f << ITD_BUFPTR0_DEVADDR_SHIFT) +#define ITD_BUFPTR0_ENDPT_SHIFT (8) /* Bits 8-11: Endpoint Number */ +#define ITD_BUFPTR0_ENDPT_MASK (15 << ITD_BUFPTR0_ENDPT_SHIFT) + +/* iTD Buffer Pointer Page 1. Table 3-5 */ + +#define ITD_BUFPTR1_MAXPKT_SHIFT (0) /* Bits 0-10: Maximum Packet Size */ +#define ITD_BUFPTR1_MAXPKT_MASK (0x7ff << ITD_BUFPTR1_MAXPKT_SHIFT) +#define ITD_BUFPTR1_DIRIN (1 << 11) /* Bit 11: Direction 1=IN */ +#define ITD_BUFPTR1_DIROUT (0) /* Bit 11: Direction 0=OUT */ + +/* iTD Buffer Pointer Page 2. Table 3-6 */ + +#define ITD_BUFPTR2_MULTI_SHIFT (0) /* Bits 0-1: Multi */ +#define ITD_BUFPTR2_MULTI_MASK (3 << ITD_BUFPTR2_MULTI_SHIFT) +#define ITD_BUFPTR2_MULTI_1 (1 << ITD_BUFPTR2_MULTI_SHIFT) /* One transaction per micro-frame */ +#define ITD_BUFPTR2_MULTI_2 (2 << ITD_BUFPTR2_MULTI_SHIFT) /* Two transactions per micro-frame */ +#define ITD_BUFPTR2_MULTI_3 (3 << ITD_BUFPTR2_MULTI_SHIFT) /* Three transactions per micro-frame */ + +/* Registers ****************************************************************/ + +/* Host Controller Capability Registers. + * This register block must be positioned at a well known address. + */ + +struct ehci_hccr { + volatile uint8_t caplength; /* 0x00: Capability Register Length */ + volatile uint8_t reserved; /* 0x01: reserved */ + volatile uint16_t hciversion; /* 0x02: Interface Version Number */ + volatile uint32_t hcsparams; /* 0x04: Structural Parameters */ + volatile uint32_t hccparams; /* 0x08: Capability Parameters */ + volatile uint8_t hcspportroute[8]; /* 0x0c: Companion Port Route Description */ +}; + +/* Host Controller Operational Registers. + * This register block is positioned at an offset of 'caplength' from the + * beginning of the Host Controller Capability Registers. + */ + +struct ehci_hcor { + volatile uint32_t usbcmd; /* 0x00: USB Command */ + volatile uint32_t usbsts; /* 0x04: USB Status */ + volatile uint32_t usbintr; /* 0x08: USB Interrupt Enable */ + volatile uint32_t frindex; /* 0x0c: USB Frame Index */ + volatile uint32_t ctrldssegment; /* 0x10: 4G Segment Selector */ + volatile uint32_t periodiclistbase; /* 0x14: Frame List Base Address */ + volatile uint32_t asynclistaddr; /* 0x18: Next Asynchronous List Address */ +#ifndef CONFIG_USB_EHCI_HCOR_RESERVED_DISABLE + uint32_t reserved[9]; +#endif + volatile uint32_t configflag; /* 0x40: Configured Flag Register */ + volatile uint32_t portsc[15]; /* 0x44: Port Status/Control */ +}; + +/* USB2 Debug Port Register Interface. + * This register block is normally found via the PCI capabalities. + * In non-PCI implementions, you need apriori information about the + * location of these registers. + */ + +struct ehci_debug { + uint32_t psc; /* 0x00: Debug Port Control/Status Register */ + uint32_t pids; /* 0x04: Debug USB PIDs Register */ + uint32_t data[2]; /* 0x08: Debug Data buffer Registers */ + uint32_t addr; /* 0x10: Device Address Register */ +}; + +/* Data Structures **********************************************************/ + +/* Queue Element Transfer Descriptor (qTD). Paragraph 3.5 */ + +struct ehci_qtd { + uint32_t next_qtd; /* 0x00-0x03: Next qTD Pointer */ + uint32_t alt_next_qtd; /* 0x04-0x07: Alternate Next qTD Pointer */ + uint32_t token; /* 0x08-0x0b: qTD Token */ + uint32_t bpl[5]; /* 0x0c-0x1c: Buffer Page Pointer List */ +}; + +#define SIZEOF_EHCI_QTD (32) /* 8*sizeof(uint32_t) */ + +/* Queue Head. Paragraph 3.6 */ + +struct ehci_qh { + uint32_t hlp; /* 0x00-0x03: Queue Head Horizontal Link Pointer */ + uint32_t epchar; /* 0x04-0x07: Endpoint Characteristics */ + uint32_t epcap; /* 0x08-0x0b: Endpoint Capabilities */ + uint32_t curr_qtd; /* 0x0c-0x0f: Current qTD Pointer */ + struct ehci_qtd overlay; /* 0x10-0x2c: Transfer overlay */ +}; + +#define SIZEOF_EHCI_QH (48) /* 4*sizeof(uint32_t) + 32 */ + +/* Isochronous (High-Speed) Transfer Descriptor (iTD). + * Paragraph 3.3. Must be aligned to 32-byte boundaries. + */ + +struct ehci_itd { + uint32_t nlp; /* 0x00-0x03: Next link pointer */ + uint32_t tscl[8]; /* 0x04-0x23: Transaction Status and Control List */ + uint32_t bpl[7]; /* 0x24-0x3c: Buffer Page Pointer List */ +}; + +#define SIZEOF_EHCI_ITD (64) /* 16*sizeof(uint32_t) */ + +/* Split Transaction Isochronous Transfer Descriptor (siTD). Paragraph 3.4 */ + +struct ehci_sitd { + uint32_t nlp; /* 0x00-0x03: Next link pointer */ + uint32_t epchar; /* 0x04-0x07: Endpoint and Transaction Translator Characteristics */ + uint32_t mfsc; /* 0x08-0x0b: Micro-frame Schedule Control */ + uint32_t tsc; /* 0x0c-0x0f: Transfer Status and Control */ + uint32_t bpl[2]; /* 0x10-0x17: Buffer Pointer List */ + uint32_t blp; /* 0x18-0x1b: Back link pointer */ +}; + +#define SIZEOF_EHCI_SITD (28) /* 7*sizeof(uint32_t) */ + +#endif /* __INCLUDE_NUTTX_USB_EHCI_H */ diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_aic.c b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_aic.c index 5445b53..500bc02 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_aic.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_aic.c @@ -8,7 +8,20 @@ #include #include #include "usbh_core.h" -#include "usb_ehci_priv.h" +#include "usb_hc_ehci.h" +#include "usb_hc_ohci.h" + +#if !defined(CONFIG_USB_EHCI_CONFIGFLAG) +#error "aic ehci must define CONFIG_USB_EHCI_CONFIGFLAG" +#endif + +#if !defined(CONFIG_USB_EHCI_WITH_OHCI) +#error "aic must define CONFIG_USB_EHCI_WITH_OHCI for ls/fs device" +#endif + +#if CONFIG_USB_OHCI_HCOR_OFFSET != 0x400 +#error "aic CONFIG_USB_OHCI_HCOR_OFFSET must be 0x400" +#endif extern void USBH_IRQHandler(uint8_t busid); @@ -117,21 +130,12 @@ void usb_hc_low_level_init(struct usbh_bus *bus) aicos_request_irq(config[i].irq_num + 1, (irq_handler_t)aic_ohci_isr, 0, "usb_host_ohci", bus); aicos_irq_enable(config[i].irq_num); + aicos_irq_enable(config[i].irq_num + 1); } uint8_t usbh_get_port_speed(struct usbh_bus *bus, const uint8_t port) { - /* Defined by individual manufacturers */ - uint32_t regval; - - regval = EHCI_HCOR->portsc[port-1]; - if ((regval & EHCI_PORTSC_LSTATUS_MASK) == EHCI_PORTSC_LSTATUS_KSTATE) - return USB_SPEED_LOW; - - if (regval & EHCI_PORTSC_PE) - return USB_SPEED_HIGH; - else - return USB_SPEED_FULL; + return USB_SPEED_HIGH; } void usb_ehci_dcache_clean(uintptr_t addr, uint32_t len) @@ -149,7 +153,7 @@ void usb_ehci_dcache_clean_invalidate(uintptr_t addr, uint32_t len) aicos_dcache_clean_invalid_range((size_t *)addr, len); } -int usbh_init(void) +int __usbh_init(void) { #if defined(AIC_USING_USB0_HOST) || defined(AIC_USING_USB1_HOST) int bus_id = 0; @@ -171,11 +175,5 @@ int usbh_init(void) #include #include -INIT_ENV_EXPORT(usbh_init); - -#if defined (RT_USING_FINSH) -#include - -MSH_CMD_EXPORT_ALIAS(lsusb, lsusb, list usb device); -#endif +INIT_ENV_EXPORT(__usbh_init); #endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_hpm.c b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_hpm.c index f616f2c..40105a9 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_hpm.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_hpm.c @@ -35,6 +35,11 @@ static void usb_host_mode_init(USB_Type *ptr) /* Set parallel transceiver width */ ptr->PORTSC1 &= ~USB_PORTSC1_PTW_MASK; +#ifdef CONFIG_USB_HOST_FORCE_FULL_SPEED + /* Set usb forced to full speed mode */ + ptr->PORTSC1 |= USB_PORTSC1_PFSC_MASK; +#endif + /* Not use interrupt threshold. */ ptr->USBCMD &= ~USB_USBCMD_ITC_MASK; } @@ -53,7 +58,7 @@ void usb_hc_low_level_init(struct usbh_bus *bus) #endif } - usb_phy_init((USB_Type *)(bus->hcd.reg_base)); + usb_phy_init((USB_Type *)(bus->hcd.reg_base), true); intc_m_enable_irq(_hcd_irqnum[bus->hcd.hcd_id]); } @@ -82,18 +87,22 @@ uint8_t usbh_get_port_speed(struct usbh_bus *bus, const uint8_t port) return 0; } +#if !defined(USBH_USE_CUSTOM_ISR) || !USBH_USE_CUSTOM_ISR + extern void USBH_IRQHandler(uint8_t busid); +SDK_DECLARE_EXT_ISR_M(IRQn_USB0, isr_usbh0) void isr_usbh0(void) { USBH_IRQHandler(_hcd_busid[0]); } -SDK_DECLARE_EXT_ISR_M(IRQn_USB0, isr_usbh0) #ifdef HPM_USB1_BASE +SDK_DECLARE_EXT_ISR_M(IRQn_USB1, isr_usbh1) void isr_usbh1(void) { USBH_IRQHandler(_hcd_busid[1]); } -SDK_DECLARE_EXT_ISR_M(IRQn_USB1, isr_usbh1) +#endif + #endif diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_ma35d0.c b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_ma35d0.c index 76e621e..3e03bbd 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_ma35d0.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_ma35d0.c @@ -57,7 +57,7 @@ void usb_hc_low_level_init(struct usbh_bus *bus) SYS->MISCFCR0 &= ~SYS_MISCFCR0_UHOVRCURH_Msk; while (1) { rt_thread_mdelay(1); - if ((SYS->USBPMISCR & SYS_USBPMISCR_PHY0HSTCKSTB_Msk) &&) + if (SYS->USBPMISCR & SYS_USBPMISCR_PHY0HSTCKSTB_Msk) break; /* both USB PHY0 and PHY1 clock 60MHz UTMI clock stable */ timeout--; @@ -88,7 +88,7 @@ void usb_hc_low_level_init(struct usbh_bus *bus) SYS->MISCFCR0 &= ~SYS_MISCFCR0_UHOVRCURH_Msk; while (1) { rt_thread_mdelay(1); - if ((SYS->USBPMISCR & SYS_USBPMISCR_PHY1HSTCKSTB_Msk)) + if (SYS->USBPMISCR & SYS_USBPMISCR_PHY1HSTCKSTB_Msk) break; /* both USB PHY0 and PHY1 clock 60MHz UTMI clock stable */ timeout--; diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_mcx.c b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_mcx.c new file mode 100644 index 0000000..92b2c25 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_glue_mcx.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbh_core.h" +#include "fsl_common.h" +#include "usb_chipidea_reg.h" + +#define USB_DEVICE_CONFIG_EHCI 1 + +/*! @brief USB controller ID */ +typedef enum _usb_controller_index +{ + kUSB_ControllerKhci0 = 0U, /*!< KHCI 0U */ + kUSB_ControllerKhci1 = 1U, /*!< KHCI 1U, Currently, there are no platforms which have two KHCI IPs, this is reserved + to be used in the future. */ + kUSB_ControllerEhci0 = 2U, /*!< EHCI 0U */ + kUSB_ControllerEhci1 = 3U, /*!< EHCI 1U */ +} usb_controller_index_t; + +/* USB PHY condfiguration */ +#define BOARD_USB_PHY_D_CAL (0x04U) +#define BOARD_USB_PHY_TXCAL45DP (0x07U) +#define BOARD_USB_PHY_TXCAL45DM (0x07U) +#define BOARD_XTAL0_CLK_HZ 24000000U /*!< Board xtal0 frequency in Hz */ + +#if !defined(CONFIG_USB_EHCI_NXP) +#error "mcx ehci must config CONFIG_USB_EHCI_NXP" +#endif + +#if !defined(CONFIG_USB_EHCI_HCCR_OFFSET) || CONFIG_USB_EHCI_HCCR_OFFSET != 0x100 +#error "mcx ehci must config CONFIG_USB_EHCI_HCCR_OFFSET to 0x100" +#endif + +#if ((defined FSL_FEATURE_SOC_USBPHY_COUNT) && (FSL_FEATURE_SOC_USBPHY_COUNT > 0U)) +#include "usb_phy.h" +#endif + +#if (defined(USB_DEVICE_CONFIG_EHCI) && (USB_DEVICE_CONFIG_EHCI > 0U)) +void USB1_HS_IRQHandler(void) +{ + extern void USBH_IRQHandler(uint8_t busid); + USBH_IRQHandler(0); +} +#endif + +void USB_ClockInit(void) +{ +#if defined(USB_DEVICE_CONFIG_EHCI) && (USB_DEVICE_CONFIG_EHCI > 0U) + usb_phy_config_struct_t phyConfig = { + BOARD_USB_PHY_D_CAL, + BOARD_USB_PHY_TXCAL45DP, + BOARD_USB_PHY_TXCAL45DM, + }; +#endif +#if defined(USB_DEVICE_CONFIG_EHCI) && (USB_DEVICE_CONFIG_EHCI > 0U) + SPC0->ACTIVE_VDELAY = 0x0500; + /* Change the power DCDC to 1.8v (By deafult, DCDC is 1.8V), CORELDO to 1.1v (By deafult, CORELDO is 1.0V) */ + SPC0->ACTIVE_CFG &= ~SPC_ACTIVE_CFG_CORELDO_VDD_DS_MASK; + SPC0->ACTIVE_CFG |= SPC_ACTIVE_CFG_DCDC_VDD_LVL(0x3) | SPC_ACTIVE_CFG_CORELDO_VDD_LVL(0x3) | + SPC_ACTIVE_CFG_SYSLDO_VDD_DS_MASK | SPC_ACTIVE_CFG_DCDC_VDD_DS(0x2u); + /* Wait until it is done */ + while (SPC0->SC & SPC_SC_BUSY_MASK) + ; + if (0u == (SCG0->LDOCSR & SCG_LDOCSR_LDOEN_MASK)) { + SCG0->TRIM_LOCK = 0x5a5a0001U; + SCG0->LDOCSR |= SCG_LDOCSR_LDOEN_MASK; + /* wait LDO ready */ + while (0U == (SCG0->LDOCSR & SCG_LDOCSR_VOUT_OK_MASK)) + ; + } + SYSCON->AHBCLKCTRLSET[2] |= SYSCON_AHBCLKCTRL2_USB_HS_MASK | SYSCON_AHBCLKCTRL2_USB_HS_PHY_MASK; + SCG0->SOSCCFG &= ~(SCG_SOSCCFG_RANGE_MASK | SCG_SOSCCFG_EREFS_MASK); + /* xtal = 20 ~ 30MHz */ + SCG0->SOSCCFG = (1U << SCG_SOSCCFG_RANGE_SHIFT) | (1U << SCG_SOSCCFG_EREFS_SHIFT); + SCG0->SOSCCSR |= SCG_SOSCCSR_SOSCEN_MASK; + while (1) { + if (SCG0->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK) { + break; + } + } + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK | SYSCON_CLOCK_CTRL_CLKIN_ENA_FM_USBH_LPT_MASK; + CLOCK_EnableClock(kCLOCK_UsbHs); + CLOCK_EnableClock(kCLOCK_UsbHsPhy); + CLOCK_EnableUsbhsPhyPllClock(kCLOCK_Usbphy480M, 24000000U); + CLOCK_EnableUsbhsClock(); + USB_EhciPhyInit(kUSB_ControllerEhci0, BOARD_XTAL0_CLK_HZ, &phyConfig); +#endif +#if defined(USB_DEVICE_CONFIG_KHCI) && (USB_DEVICE_CONFIG_KHCI > 0U) + CLOCK_AttachClk(kCLK_48M_to_USB0); + CLOCK_EnableClock(kCLOCK_Usb0Ram); + CLOCK_EnableClock(kCLOCK_Usb0Fs); + CLOCK_EnableUsbfsClock(); +#endif +} + +static void usb_host_mode_init(CHIPIDEA_TypeDef *ptr) +{ + /* Set mode to host, must be set immediately after reset */ + ptr->USBMODE &= ~USB_USBMODE_CM_MASK; + ptr->USBMODE |= USB_USBMODE_CM_SET(3); + + /* Set the endian */ + ptr->USBMODE &= ~USB_USBMODE_ES_MASK; + + /* Set parallel interface signal */ + ptr->PORTSC1 &= ~USB_PORTSC1_STS_MASK; + + /* Set parallel transceiver width */ + ptr->PORTSC1 &= ~USB_PORTSC1_PTW_MASK; + + /* Not use interrupt threshold. */ + ptr->USBCMD &= ~USB_USBCMD_ITC_MASK; +} + +void usb_hc_low_level_init(struct usbh_bus *bus) +{ + USB_ClockInit(); + /* Install isr, set priority, and enable IRQ. */ + NVIC_SetPriority((IRQn_Type)USB1_HS_IRQn, 3); + EnableIRQ((IRQn_Type)USB1_HS_IRQn); +} + +void usb_hc_low_level2_init(struct usbh_bus *bus) +{ + usb_host_mode_init((CHIPIDEA_TypeDef *)(bus->hcd.reg_base)); +} + +void usb_hc_low_level_deinit(struct usbh_bus *bus) +{ + DisableIRQ((IRQn_Type)USB1_HS_IRQn); +} + +uint8_t usbh_get_port_speed(struct usbh_bus *bus, const uint8_t port) +{ + (void)port; + uint8_t speed; + + CHIPIDEA_TypeDef *ptr = (CHIPIDEA_TypeDef *)bus->hcd.reg_base; + + speed = USB_PORTSC1_PSPD_GET(ptr->PORTSC1); + + if (speed == 0x00) { + return USB_SPEED_FULL; + } + if (speed == 0x01) { + return USB_SPEED_LOW; + } + if (speed == 0x02) { + USB_EhcihostPhyDisconnectDetectCmd(kUSB_ControllerEhci0, 1); + return USB_SPEED_HIGH; + } + + return 0; +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_hc_ehci.c b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_hc_ehci.c index d837012..4f61854 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_hc_ehci.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_hc_ehci.c @@ -3,9 +3,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ -#include "usb_ehci_priv.h" +#include "usb_hc_ehci.h" #ifdef CONFIG_USB_EHCI_WITH_OHCI -#include "usb_ohci_priv.h" +#include "usb_hc_ohci.h" #endif #define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ @@ -167,6 +167,7 @@ static void ehci_qh_fill(struct ehci_qh_hw *qh, switch (speed) { case USB_SPEED_LOW: epchar |= QH_EPCHAR_EPS_LOW; + __attribute__((fallthrough)); case USB_SPEED_FULL: if (ep_type == USB_ENDPOINT_TYPE_CONTROL) { epchar |= QH_EPCHAR_C; /* for TT */ @@ -379,7 +380,7 @@ static struct ehci_qh_hw *ehci_bulk_urb_init(struct usbh_bus *bus, struct usbh_u urb->hport->parent->hub_addr, urb->hport->port); - while (buflen >= 0) { + while (1) { qtd = &qh->qtd_pool[qtd_num]; if (buflen > 0x4000) { @@ -480,7 +481,7 @@ static struct ehci_qh_hw *ehci_intr_urb_init(struct usbh_bus *bus, struct usbh_u urb->hport->parent->hub_addr, urb->hport->port); - while (buflen >= 0) { + while (1) { qtd = &qh->qtd_pool[qtd_num]; if (buflen > 0x4000) { @@ -583,6 +584,8 @@ static void ehci_qh_scan_qtds(struct usbh_bus *bus, struct ehci_qh_hw *qhead, st { struct ehci_qtd_hw *qtd; + (void)bus; + ehci_qh_remove(qhead, qh); qtd = EHCI_ADDR2QTD(qh->first_qtd); @@ -655,6 +658,8 @@ static void ehci_kill_qh(struct usbh_bus *bus, struct ehci_qh_hw *qhead, struct { struct ehci_qtd_hw *qtd; + (void)bus; + ehci_qh_remove(qhead, qh); qtd = EHCI_ADDR2QTD(qh->first_qtd); @@ -697,14 +702,17 @@ static int usbh_reset_port(struct usbh_bus *bus, const uint8_t port) __WEAK void usb_hc_low_level_init(struct usbh_bus *bus) { + (void)bus; } __WEAK void usb_hc_low_level2_init(struct usbh_bus *bus) { + (void)bus; } __WEAK void usb_hc_low_level_deinit(struct usbh_bus *bus) { + (void)bus; } int usb_hc_init(struct usbh_bus *bus) @@ -777,6 +785,8 @@ int usb_hc_init(struct usbh_bus *bus) g_ehci_hcd[bus->hcd.hcd_id].n_cc, g_ehci_hcd[bus->hcd.hcd_id].n_pcc); + EHCI_HCOR->usbcmd &= ~EHCI_USBCMD_RUN; + usb_osal_msleep(2); EHCI_HCOR->usbcmd |= EHCI_USBCMD_HCRESET; while (EHCI_HCOR->usbcmd & EHCI_USBCMD_HCRESET) { usb_osal_msleep(1); @@ -830,6 +840,7 @@ int usb_hc_init(struct usbh_bus *bus) for (uint8_t port = 0; port < g_ehci_hcd[bus->hcd.hcd_id].n_ports; port++) { regval = EHCI_HCOR->portsc[port]; regval |= EHCI_PORTSC_PP; + regval &= ~(EHCI_PORTSC_CSC | EHCI_PORTSC_PEC | EHCI_PORTSC_OCC); EHCI_HCOR->portsc[port] = regval; } } @@ -872,7 +883,7 @@ int usb_hc_deinit(struct usbh_bus *bus) regval &= ~EHCI_USBCMD_RUN; EHCI_HCOR->usbcmd = regval; - while ((EHCI_HCOR->usbsts & (EHCI_USBSTS_PSS | EHCI_USBSTS_ASS))) { + while ((EHCI_HCOR->usbsts & (EHCI_USBSTS_PSS | EHCI_USBSTS_ASS)) || ((EHCI_HCOR->usbsts & EHCI_USBSTS_HALTED) == 0)) { usb_osal_msleep(1); timeout++; if (timeout > 100) { @@ -893,6 +904,7 @@ int usb_hc_deinit(struct usbh_bus *bus) #endif EHCI_HCOR->usbsts = EHCI_HCOR->usbsts; + EHCI_HCOR->usbcmd |= EHCI_USBCMD_HCRESET; for (uint8_t index = 0; index < CONFIG_USB_EHCI_QH_NUM; index++) { qh = &ehci_qh_pool[bus->hcd.hcd_id][index]; @@ -940,6 +952,7 @@ int usbh_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, u while (!(EHCI_HCOR->portsc[port - 1] & EHCI_PORTSC_OWNER)) { } + USB_LOG_INFO("Switch port %u to OHCI\r\n", port); return ohci_roothub_control(bus, setup, buf); } #endif @@ -1051,7 +1064,9 @@ int usbh_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, u while (!(EHCI_HCOR->portsc[port - 1] & EHCI_PORTSC_OWNER)) { } - return ohci_roothub_control(bus, setup, buf); + + USB_LOG_INFO("Switch port %u to OHCI\r\n", port); + return -USB_ERR_NOTSUPP; } #endif break; @@ -1098,7 +1113,7 @@ int usbh_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, u if (temp & EHCI_PORTSC_RESET) { status |= (1 << HUB_PORT_FEATURE_RESET); } - if (temp & EHCI_PORTSC_PP) { + if (temp & EHCI_PORTSC_PP || !(EHCI_HCCR->hcsparams & EHCI_HCSPARAMS_PPC)) { status |= (1 << HUB_PORT_FEATURE_POWER); } memcpy(buf, &status, 4); @@ -1341,6 +1356,11 @@ void USBH_IRQHandler(uint8_t busid) if (portsc & EHCI_PORTSC_CSC) { if ((portsc & EHCI_PORTSC_CCS) == EHCI_PORTSC_CCS) { } else { +#if defined(CONFIG_USB_EHCI_NXP) + /* kUSB_ControllerEhci0 and kUSB_ControllerEhci1*/ + extern void USB_EhcihostPhyDisconnectDetectCmd(uint8_t controllerId, uint8_t enable); + USB_EhcihostPhyDisconnectDetectCmd(2 + busid, 0); +#endif for (uint8_t index = 0; index < CONFIG_USB_EHCI_QH_NUM; index++) { g_ehci_hcd[bus->hcd.hcd_id].ehci_qh_used[index] = false; } diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_hc_ehci.h b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_hc_ehci.h index 7f3febc..714128b 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_hc_ehci.h +++ b/rt-thread/components/drivers/usb/cherryusb/port/ehci/usb_hc_ehci.h @@ -1,375 +1,84 @@ /* - * Copyright 2020 The Apache Software Foundation - * Copyright 2022 sakumisu - * + * Copyright (c) 2022, sakumisu + * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef USB_HC_EHCI_H -#define USB_HC_EHCI_H +#ifndef _USB_EHCI_PRIV_H +#define _USB_EHCI_PRIV_H -#define EHCI_FULL_SPEED (0) /* Full-Speed (12Mbs) */ -#define EHCI_LOW_SPEED (1) /* Low-Speed (1.5Mbs) */ -#define EHCI_HIGH_SPEED (2) /* High-Speed (480 Mb/s) */ +#include "usbh_core.h" +#include "usbh_hub.h" +#include "usb_ehci_reg.h" -/* Host Controller Capability Register Bit Definitions **********************/ +#define EHCI_HCCR ((struct ehci_hccr *)(uintptr_t)(bus->hcd.reg_base + CONFIG_USB_EHCI_HCCR_OFFSET)) +#define EHCI_HCOR ((struct ehci_hcor *)(uintptr_t)(bus->hcd.reg_base + CONFIG_USB_EHCI_HCCR_OFFSET + g_ehci_hcd[bus->hcd.hcd_id].hcor_offset)) -/* Structural Parameters. Paragraph 2.2.3 */ +#define EHCI_PTR2ADDR(x) ((uint32_t)(uintptr_t)(x) & ~0x1F) +#define EHCI_ADDR2QH(x) ((struct ehci_qh_hw *)(uintptr_t)((uint32_t)(x) & ~0x1F)) +#define EHCI_ADDR2QTD(x) ((struct ehci_qtd_hw *)(uintptr_t)((uint32_t)(x) & ~0x1F)) +#define EHCI_ADDR2ITD(x) ((struct ehci_itd_hw *)(uintptr_t)((uint32_t)(x) & ~0x1F)) -#define EHCI_HCSPARAMS_NPORTS_SHIFT (0) /* Bit 0-3: Number of physical downstream ports */ -#define EHCI_HCSPARAMS_NPORTS_MASK (15 << EHCI_HCSPARAMS_NPORTS_SHIFT) -#define EHCI_HCSPARAMS_PPC (1 << 4) /* Bit 4: Port Power Control */ -#define EHCI_HCSPARAMS_PRR (1 << 7) /* Bit 7: Port Routing Rules */ -#define EHCI_HCSPARAMS_NPCC_SHIFT (8) /* Bit 8-11: Number of Ports per Companion Controller */ -#define EHCI_HCSPARAMS_NPCC_MASK (15 << EHCI_HCSPARAMS_NPCC_SHIFT) -#define EHCI_HCSPARAMS_NCC_SHIFT (12) /* Bit 12-15: Number of Companion Controllers */ -#define EHCI_HCSPARAMS_NCC_MASK (15 << EHCI_HCSPARAMS_NCC_SHIFT) -#define EHCI_HCSPARAMS_PIND (1 << 16) /* Bit 16: Port Indicators */ -#define EHCI_HCSPARAMS_DBGPORT_SHIFT (20) /* Bit 20-23: Debug Port Number */ -#define EHCI_HCSPARAMS_DBGPORT_MASK (15 << EHCI_HCSPARAMS_DBGPORT_SHIFT) - -/* Capability Parameters. Paragraph 2.2.4 */ - -#define EHCI_HCCPARAMS_64BIT (1 << 0) /* Bit 0: 64-bit Addressing Capability */ -#define EHCI_HCCPARAMS_PFLF (1 << 1) /* Bit 1: Programmable Frame List Flag */ -#define EHCI_HCCPARAMS_ASPC (1 << 2) /* Bit 2: Asynchronous Schedule Park Capability */ -#define EHCI_HCCPARAMS_IST_SHIFT (4) /* Bits 4-7: Isochronous Scheduling Threshold */ -#define EHCI_HCCPARAMS_IST_MASK (15 << EHCI_HCCPARAMS_IST_SHIFT) -#define EHCI_HCCPARAMS_EECP_SHIFT (8) /* Bits 8-15: EHCI Extended Capabilities Pointer */ -#define EHCI_HCCPARAMS_EECP_MASK (0xff << EHCI_HCCPARAMS_EECP_SHIFT) - -/* Host Controller Operational Register Bit Definitions *********************/ - -/* USB Command. Paragraph 2.3.1 */ - -#define EHCI_USBCMD_RUN (1 << 0) /* Bit 0: Run/Stop */ -#define EHCI_USBCMD_HCRESET (1 << 1) /* Bit 1: Host Controller Reset */ -#define EHCI_USBCMD_FLSIZE_SHIFT (2) /* Bits 2-3: Frame List Size */ -#define EHCI_USBCMD_FLSIZE_MASK (3 << EHCI_USBCMD_FLSIZE_SHIFT) -#define EHCI_USBCMD_FLSIZE_1024 (0 << EHCI_USBCMD_FLSIZE_SHIFT) /* 1024 elements (4096 bytes) */ -#define EHCI_USBCMD_FLSIZE_512 (1 << EHCI_USBCMD_FLSIZE_SHIFT) /* 512 elements (2048 bytes) */ -#define EHCI_USBCMD_FLSIZE_256 (2 << EHCI_USBCMD_FLSIZE_SHIFT) /* 256 elements (1024 bytes) */ -#define EHCI_USBCMD_PSEN (1 << 4) /* Bit 4: Periodic Schedule Enable */ -#define EHCI_USBCMD_ASEN (1 << 5) /* Bit 5: Asynchronous Schedule Enable */ -#define EHCI_USBCMD_IAAD (1 << 6) /* Bit 6: Interrupt on Async Advance Doorbell */ -#define EHCI_USBCMD_LRESET (1 << 7) /* Bit 7: Light Host Controller Reset */ -#define EHCI_USBCMD_ASYNC_PARKCNT_SHIFT (8) /* Bits 8-9: Asynchronous Schedule Park Mode Count */ -#define EHCI_USBCMD_ASYNC_PARKCNT_MASK (3 << EHCI_USBCMD_ASYNC_PARKCNT_SHIFT) -#define EHCI_USBCMD_ASYNC_PARK (1 << 11) /* Bit 11: Asynchronous Schedule Park Mode Enable */ -#define EHCI_USBCMD_ITHRE_SHIFT (16) /* Bits 16-23: Interrupt Threshold Control */ -#define EHCI_USBCMD_ITHRE_MASK (0xff << EHCI_USBCMD_ITHRE_SHIFT) -#define EHCI_USBCMD_ITHRE_1MF (0x01 << EHCI_USBCMD_ITHRE_SHIFT) /* 1 micro-frame */ -#define EHCI_USBCMD_ITHRE_2MF (0x02 << EHCI_USBCMD_ITHRE_SHIFT) /* 2 micro-frames */ -#define EHCI_USBCMD_ITHRE_4MF (0x04 << EHCI_USBCMD_ITHRE_SHIFT) /* 4 micro-frames */ -#define EHCI_USBCMD_ITHRE_8MF (0x08 << EHCI_USBCMD_ITHRE_SHIFT) /* 8 micro-frames (default, 1 ms) */ -#define EHCI_USBCMD_ITHRE_16MF (0x10 << EHCI_USBCMD_ITHRE_SHIFT) /* 16 micro-frames (2 ms) */ -#define EHCI_USBCMD_ITHRE_32MF (0x20 << EHCI_USBCMD_ITHRE_SHIFT) /* 32 micro-frames (4 ms) */ -#define EHCI_USBCMD_ITHRE_64MF (0x40 << EHCI_USBCMD_ITHRE_SHIFT) /* 64 micro-frames (8 ms) */ - -/* USB Status. Paragraph 2.3.2 */ - -#define EHCI_USBSTS_INT (1 << 0) /* Bit 0: USB Interrupt */ -#define EHCI_USBSTS_ERR (1 << 1) /* Bit 1: USB Error Interrupt */ -#define EHCI_USBSTS_PCD (1 << 2) /* Bit 2: Port Change Detect */ -#define EHCI_USBSTS_FLR (1 << 3) /* Bit 3: Frame List Rollover */ -#define EHCI_USBSTS_FATAL (1 << 4) /* Bit 4: Host System Error */ -#define EHCI_USBSTS_IAA (1 << 5) /* Bit 5: Interrupt on Async Advance */ -#define EHCI_USBSTS_HALTED (1 << 12) /* Bit 12: HC Halted */ -#define EHCI_USBSTS_RECLAM (1 << 13) /* Bit 13: Reclamation */ -#define EHCI_USBSTS_PSS (1 << 14) /* Bit 14: Periodic Schedule Status */ -#define EHCI_USBSTS_ASS (1 << 15) /* Bit 15: Asynchronous Schedule Status */ - /* Bits 16-31: Reserved */ - -/* USB Interrupt Enable. Paragraph 2.3.3 */ - -#define EHCI_USBIE_INT (1 << 0) /* Bit 0: USB Interrupt */ -#define EHCI_USBIE_ERR (1 << 1) /* Bit 1: USB Error Interrupt */ -#define EHCI_USBIE_PCD (1 << 2) /* Bit 2: Port Change Detect */ -#define EHCI_USBIE_FLROLL (1 << 3) /* Bit 3: Frame List Rollover */ -#define EHCI_USBIE_FATAL (1 << 4) /* Bit 4: Host System Error */ -#define EHCI_USBIE_IAA (1 << 5) /* Bit 5: Interrupt on Async Advance */ -#define EHCI_USBIE_ALLINTS (0x3f) /* Bits 0-5: All interrupts */ - -/* USB Frame Index. Paragraph 2.3.4 */ - -#define EHCI_FRINDEX_MASK (0x3fff) /* Bits 0-13: Frame index */ - -/* 4G Segment Selector. - * Paragraph 2.3.5, Bits[64:32] of data structure addresses - */ - -/* Frame List Base Address. Paragraph 2.3.6 */ -#define EHCI_PERIODICLISTBASE_MASK (0xfffff000) /* Bits 12-31: Base Address (Low) */ - -/* Next Asynchronous List Address. Paragraph 2.3.7 */ - -#define EHCI_ASYNCLISTADDR_MASK (0xffffffe0) /* Bits 5-31: Link Pointer Low (LPL) */ - -/* Configured Flag Register. Paragraph 2.3.8 */ - -#define EHCI_CONFIGFLAG (1 << 0) /* Bit 0: Configure Flag */ - -/* Port Status/Control, Port 1-n. Paragraph 2.3.9 */ - -#define EHCI_PORTSC_CCS (1 << 0) /* Bit 0: Current Connect Status */ -#define EHCI_PORTSC_CSC (1 << 1) /* Bit 1: Connect Status Change */ -#define EHCI_PORTSC_PE (1 << 2) /* Bit 2: Port Enable */ -#define EHCI_PORTSC_PEC (1 << 3) /* Bit 3: Port Enable/Disable Change */ -#define EHCI_PORTSC_OCA (1 << 4) /* Bit 4: Over-current Active */ -#define EHCI_PORTSC_OCC (1 << 5) /* Bit 5: Over-current Change */ -#define EHCI_PORTSC_RESUME (1 << 6) /* Bit 6: Force Port Resume */ -#define EHCI_PORTSC_SUSPEND (1 << 7) /* Bit 7: Suspend */ -#define EHCI_PORTSC_RESET (1 << 8) /* Bit 8: Port Reset */ -#define EHCI_PORTSC_LSTATUS_SHIFT (10) /* Bits 10-11: Line Status */ -#define EHCI_PORTSC_LSTATUS_MASK (3 << EHCI_PORTSC_LSTATUS_SHIFT) -#define EHCI_PORTSC_LSTATUS_SE0 (0 << EHCI_PORTSC_LSTATUS_SHIFT) /* SE0 Not Low-speed device, perform EHCI reset */ -#define EHCI_PORTSC_LSTATUS_KSTATE (1 << EHCI_PORTSC_LSTATUS_SHIFT) /* K-state Low-speed device, release ownership of port */ -#define EHCI_PORTSC_LSTATUS_JSTATE (2 << EHCI_PORTSC_LSTATUS_SHIFT) /* J-state Not Low-speed device, perform EHCI reset */ -#define EHCI_PORTSC_PP (1 << 12) /* Bit 12: Port Power */ -#define EHCI_PORTSC_OWNER (1 << 13) /* Bit 13: Port Owner */ -#define EHCI_PORTSC_PIC_SHIFT (14) /* Bits 14-15: Port Indicator Control */ -#define EHCI_PORTSC_PIC_MASK (3 << EHCI_PORTSC_PIC_SHIFT) -#define EHCI_PORTSC_PIC_OFF (0 << EHCI_PORTSC_PIC_SHIFT) /* Port indicators are off */ -#define EHCI_PORTSC_PIC_AMBER (1 << EHCI_PORTSC_PIC_SHIFT) /* Amber */ -#define EHCI_PORTSC_PIC_GREEN (2 << EHCI_PORTSC_PIC_SHIFT) /* Green */ -#define EHCI_PORTSC_PTC_SHIFT (16) /* Bits 16-19: Port Test Control */ -#define EHCI_PORTSC_PTC_MASK (15 << EHCI_PORTSC_PTC_SHIFT) -#define EHCI_PORTSC_PTC_DISABLED (0 << EHCI_PORTSC_PTC_SHIFT) /* Test mode not enabled */ -#define EHCI_PORTSC_PTC_JSTATE (1 << EHCI_PORTSC_PTC_SHIFT) /* Test J_STATE */ -#define EHCI_PORTSC_PTC_KSTATE (2 << EHCI_PORTSC_PTC_SHIFT) /* Test K_STATE */ -#define EHCI_PORTSC_PTC_SE0NAK (3 << EHCI_PORTSC_PTC_SHIFT) /* Test SE0_NAK */ -#define EHCI_PORTSC_PTC_PACKET (4 << EHCI_PORTSC_PTC_SHIFT) /* Test Packet */ -#define EHCI_PORTSC_PTC_ENABLE (5 << EHCI_PORTSC_PTC_SHIFT) /* Test FORCE_ENABLE */ -#define EHCI_PORTSC_WKCCNTE (1 << 20) /* Bit 20: Wake on Connect Enable */ -#define EHCI_PORTSC_WKDSCNNTE (1 << 21) /* Bit 21: Wake on Disconnect Enable */ -#define EHCI_PORTSC_WKOCE (1 << 22) /* Bit 22: Wake on Over-current Enable */ - /* Bits 23-31: Reserved */ - -#define EHCI_PORTSC_ALLINTS (EHCI_PORTSC_CSC | EHCI_PORTSC_PEC | \ - EHCI_PORTSC_OCC | EHCI_PORTSC_RESUME) - -/* Queue Head. Paragraph 3.6 */ - -/* Queue Head Horizontal Link Pointer: Queue Head DWord 0. Table 3-19 */ - -#define QH_HLP_END 0x1 - -#define QH_HLP_ITD(x) (((uint32_t)(x) & ~0x1F) | 0x0) /* Isochronous Transfer Descriptor */ -#define QH_HLP_QH(x) (((uint32_t)(x) & ~0x1F) | 0x2) /* Queue Head */ -#define QH_HLP_SITD(x) (((uint32_t)(x) & ~0x1F) | 0x4) /* Split Transaction Isochronous Transfer Descriptor */ -#define QH_HLP_FSTN(x) (((uint32_t)(x) & ~0x1F) | 0x6) /* Frame Span Traversal Node */ - -/* Endpoint Characteristics: Queue Head DWord 1. Table 3-19 */ - -#define QH_EPCHAR_DEVADDR_SHIFT (0) /* Bitx 0-6: Device Address */ -#define QH_EPCHAR_DEVADDR_MASK (0x7f << QH_EPCHAR_DEVADDR_SHIFT) -#define QH_EPCHAR_I (1 << 7) /* Bit 7: Inactivate on Next Transaction */ -#define QH_EPCHAR_ENDPT_SHIFT (8) /* Bitx 8-11: Endpoint Number */ -#define QH_EPCHAR_ENDPT_MASK (15 << QH_EPCHAR_ENDPT_SHIFT) -#define QH_EPCHAR_EPS_SHIFT (12) /* Bitx 12-13: Endpoint Speed */ -#define QH_EPCHAR_EPS_MASK (3 << QH_EPCHAR_EPS_SHIFT) -#define QH_EPCHAR_EPS_FULL (0 << QH_EPCHAR_EPS_SHIFT) /* Full-Speed (12Mbs) */ -#define QH_EPCHAR_EPS_LOW (1 << QH_EPCHAR_EPS_SHIFT) /* Low-Speed (1.5Mbs) */ -#define QH_EPCHAR_EPS_HIGH (2 << QH_EPCHAR_EPS_SHIFT) /* High-Speed (480 Mb/s) */ -#define QH_EPCHAR_DTC (1 << 14) /* Bit 14: Data Toggle Control */ -#define QH_EPCHAR_H (1 << 15) /* Bit 15: Head of Reclamation List Flag */ -#define QH_EPCHAR_MAXPKT_SHIFT (16) /* Bitx 16-26: Maximum Packet Length */ -#define QH_EPCHAR_MAXPKT_MASK (0x7ff << QH_EPCHAR_MAXPKT_SHIFT) -#define QH_EPCHAR_C (1 << 27) /* Bit 27: Control Endpoint Flag */ -#define QH_EPCHAR_RL_SHIFT (28) /* Bitx 28-31: Nak Count Reload */ -#define QH_EPCHAR_RL_MASK (15 << QH_EPCHAR_RL_SHIFT) - -/* Endpoint Capabilities: Queue Head DWord 2. Table 3-20 */ - -#define QH_EPCAPS_SSMASK_SHIFT (0) /* Bitx 0-7: Interrupt Schedule Mask (Frame S-mask) */ -#define QH_EPCAPS_SSMASK_MASK (0xff << QH_EPCAPS_SSMASK_SHIFT) -#define QH_EPCAPS_SSMASK(n) ((n) << QH_EPCAPS_SSMASK_SHIFT) -#define QH_EPCAPS_SCMASK_SHIFT (8) /* Bitx 8-15: Split Completion Mask (Frame C-Mask) */ -#define QH_EPCAPS_SCMASK_MASK (0xff << QH_EPCAPS_SCMASK_SHIFT) -#define QH_EPCAPS_SCMASK(n) ((n) << QH_EPCAPS_SCMASK_SHIFT) -#define QH_EPCAPS_HUBADDR_SHIFT (16) /* Bitx 16-22: Hub Address */ -#define QH_EPCAPS_HUBADDR_MASK (0x7f << QH_EPCAPS_HUBADDR_SHIFT) -#define QH_EPCAPS_HUBADDR(n) ((n) << QH_EPCAPS_HUBADDR_SHIFT) -#define QH_EPCAPS_PORT_SHIFT (23) /* Bit 23-29: Port Number */ -#define QH_EPCAPS_PORT_MASK (0x7f << QH_EPCAPS_PORT_SHIFT) -#define QH_EPCAPS_PORT(n) ((n) << QH_EPCAPS_PORT_SHIFT) -#define QH_EPCAPS_MULT_SHIFT (30) /* Bit 30-31: High-Bandwidth Pipe Multiplier */ -#define QH_EPCAPS_MULT_MASK (3 << QH_EPCAPS_MULT_SHIFT) -#define QH_EPCAPS_MULT(n) ((n) << QH_EPCAPS_MULT_SHIFT) - -/* qTD Token. Paragraph 3.5.3 */ - -#define QTD_LIST_END 1 - -#define QTD_TOKEN_STATUS_SHIFT (0) /* Bits 0-7: Status */ -#define QTD_TOKEN_STATUS_MASK (0xff << QTD_TOKEN_STATUS_SHIFT) -#define QTD_TOKEN_STATUS_PINGSTATE (1 << 0) /* Bit 0 Ping State */ -#define QTD_TOKEN_STATUS_ERR (1 << 0) /* Bit 0 Error */ -#define QTD_TOKEN_STATUS_SPLITXSTATE (1 << 1) /* Bit 1 Split Transaction State */ -#define QTD_TOKEN_STATUS_MMF (1 << 2) /* Bit 2 Missed Micro-Frame */ -#define QTD_TOKEN_STATUS_XACTERR (1 << 3) /* Bit 3 Transaction Error */ -#define QTD_TOKEN_STATUS_BABBLE (1 << 4) /* Bit 4 Babble Detected */ -#define QTD_TOKEN_STATUS_DBERR (1 << 5) /* Bit 5 Data Buffer Error */ -#define QTD_TOKEN_STATUS_HALTED (1 << 6) /* Bit 6 Halted */ -#define QTD_TOKEN_STATUS_ACTIVE (1 << 7) /* Bit 7 Active */ -#define QTD_TOKEN_STATUS_ERRORS (0x78 << QTD_TOKEN_STATUS_SHIFT) -#define QTD_TOKEN_PID_SHIFT (8) /* Bits 8-9: PID Code */ -#define QTD_TOKEN_PID_MASK (3 << QTD_TOKEN_PID_SHIFT) -#define QTD_TOKEN_PID_OUT (0 << QTD_TOKEN_PID_SHIFT) /* OUT Token generates token (E1H) */ -#define QTD_TOKEN_PID_IN (1 << QTD_TOKEN_PID_SHIFT) /* IN Token generates token (69H) */ -#define QTD_TOKEN_PID_SETUP (2 << QTD_TOKEN_PID_SHIFT) /* SETUP Token generates token (2DH) */ -#define QTD_TOKEN_CERR_SHIFT (10) /* Bits 10-11: Error Counter */ -#define QTD_TOKEN_CERR_MASK (3 << QTD_TOKEN_CERR_SHIFT) -#define QTD_TOKEN_CPAGE_SHIFT (12) /* Bits 12-14: Current Page */ -#define QTD_TOKEN_CPAGE_MASK (7 << QTD_TOKEN_CPAGE_SHIFT) -#define QTD_TOKEN_IOC (1 << 15) /* Bit 15: Interrupt On Complete */ -#define QTD_TOKEN_NBYTES_SHIFT (16) /* Bits 16-30: Total Bytes to Transfer */ -#define QTD_TOKEN_NBYTES_MASK (0x7fff << QTD_TOKEN_NBYTES_SHIFT) -#define QTD_TOKEN_TOGGLE (1 << 31) /* Bit 31: Data Toggle */ - -/* Isochronous (High-Speed) Transfer Descriptor (iTD). Paragraph 3.3 */ - -/* iTD Next Link Pointer. Paragraph 3.3.1 */ - -#define ITD_NLP_ITD(x) (((uint32_t)(x) & ~0x1F) | 0x0) -#define ITD_NLP_QH(x) (((uint32_t)(x) & ~0x1F) | 0x2) -#define ITD_NLP_SITD(x) (((uint32_t)(x) & ~0x1F) | 0x4) -#define ITD_NLP_FSTN(x) (((uint32_t)(x) & ~0x1F) | 0x6) - -/* iTD Transaction Status and Control List. Paragraph 3.3.2 */ -#define ITD_TSCL_XOFFS_SHIFT (0) /* Bits 0-11: Transaction X offset */ -#define ITD_TSCL_XOFFS_MASK (0xfff << ITD_TSCL_XOFFS_SHIFT) -#define ITD_TSCL_PG_SHIFT (12) /* Bits 12-14: Page select */ -#define ITD_TSCL_PG_MASK (7 << ITD_TSCL_PG_SHIFT) -#define ITD_TSCL_IOC (1 << 15) /* Bit 15: Interrupt On Comp */ -#define ITD_TSCL_LENGTH_SHIFT (16) /* Bits 16-27: Transaction length */ -#define ITD_TSCL_LENGTH_MASK (0xfff << ITD_TSCL_LENGTH_SHIFT) -#define ITD_TSCL_STATUS_SHIFT (28) /* Bits 28-31: Transaction status */ -#define ITD_TSCL_STATUS_MASK (15 << ITD_TSCL_STATUS_SHIFT) -#define ITD_TSCL_STATUS_XACTERR (1 << 28) /* Bit 28: Transaction error */ -#define ITD_TSCL_STATUS_BABBLE (1 << 29) /* Bit 29: Babble Detected */ -#define ITD_TSCL_STATUS_DBERROR (1 << 30) /* Bit 30: Data Buffer Error */ -#define ITD_TSCL_STATUS_ACTIVE (1 << 31) /* Bit 31: Active error */ - -/* iTD Buffer Page Pointer List. Paragraph 3.3.4 */ - -/* iTD Buffer Pointer Page 0. Table 3-4 */ - -#define ITD_BUFPTR0_DEVADDR_SHIFT (0) /* Bits 0-6: Device Address */ -#define ITD_BUFPTR0_DEVADDR_MASK (0x7f << ITD_BUFPTR0_DEVADDR_SHIFT) -#define ITD_BUFPTR0_ENDPT_SHIFT (8) /* Bits 8-11: Endpoint Number */ -#define ITD_BUFPTR0_ENDPT_MASK (15 << ITD_BUFPTR0_ENDPT_SHIFT) - -/* iTD Buffer Pointer Page 1. Table 3-5 */ - -#define ITD_BUFPTR1_MAXPKT_SHIFT (0) /* Bits 0-10: Maximum Packet Size */ -#define ITD_BUFPTR1_MAXPKT_MASK (0x7ff << ITD_BUFPTR1_MAXPKT_SHIFT) -#define ITD_BUFPTR1_DIRIN (1 << 11) /* Bit 11: Direction 1=IN */ -#define ITD_BUFPTR1_DIROUT (0) /* Bit 11: Direction 0=OUT */ - -/* iTD Buffer Pointer Page 2. Table 3-6 */ - -#define ITD_BUFPTR2_MULTI_SHIFT (0) /* Bits 0-1: Multi */ -#define ITD_BUFPTR2_MULTI_MASK (3 << ITD_BUFPTR2_MULTI_SHIFT) -#define ITD_BUFPTR2_MULTI_1 (1 << ITD_BUFPTR2_MULTI_SHIFT) /* One transaction per micro-frame */ -#define ITD_BUFPTR2_MULTI_2 (2 << ITD_BUFPTR2_MULTI_SHIFT) /* Two transactions per micro-frame */ -#define ITD_BUFPTR2_MULTI_3 (3 << ITD_BUFPTR2_MULTI_SHIFT) /* Three transactions per micro-frame */ - -/* Registers ****************************************************************/ - -/* Host Controller Capability Registers. - * This register block must be positioned at a well known address. - */ - -struct ehci_hccr { - volatile uint8_t caplength; /* 0x00: Capability Register Length */ - volatile uint8_t reserved; /* 0x01: reserved */ - volatile uint16_t hciversion; /* 0x02: Interface Version Number */ - volatile uint32_t hcsparams; /* 0x04: Structural Parameters */ - volatile uint32_t hccparams; /* 0x08: Capability Parameters */ - volatile uint8_t hcspportroute[8]; /* 0x0c: Companion Port Route Description */ -}; - -/* Host Controller Operational Registers. - * This register block is positioned at an offset of 'caplength' from the - * beginning of the Host Controller Capability Registers. - */ - -struct ehci_hcor { - volatile uint32_t usbcmd; /* 0x00: USB Command */ - volatile uint32_t usbsts; /* 0x04: USB Status */ - volatile uint32_t usbintr; /* 0x08: USB Interrupt Enable */ - volatile uint32_t frindex; /* 0x0c: USB Frame Index */ - volatile uint32_t ctrldssegment; /* 0x10: 4G Segment Selector */ - volatile uint32_t periodiclistbase; /* 0x14: Frame List Base Address */ - volatile uint32_t asynclistaddr; /* 0x18: Next Asynchronous List Address */ -#ifndef CONFIG_USB_EHCI_HCOR_RESERVED_DISABLE - uint32_t reserved[9]; +#ifndef CONFIG_USB_EHCI_QH_NUM +#define CONFIG_USB_EHCI_QH_NUM CONFIG_USBHOST_PIPE_NUM #endif - volatile uint32_t configflag; /* 0x40: Configured Flag Register */ - volatile uint32_t portsc[15]; /* 0x44: Port Status/Control */ +#ifndef CONFIG_USB_EHCI_QTD_NUM +#define CONFIG_USB_EHCI_QTD_NUM 3 +#endif +#ifndef CONFIG_USB_EHCI_ITD_NUM +#define CONFIG_USB_EHCI_ITD_NUM 5 +#endif +#ifndef CONFIG_USB_EHCI_ISO_NUM +#define CONFIG_USB_EHCI_ISO_NUM 4 +#endif + +extern uint8_t usbh_get_port_speed(struct usbh_bus *bus, const uint8_t port); + +struct ehci_qtd_hw { + struct ehci_qtd hw; + struct usbh_urb *urb; + uint32_t length; +} __attribute__((aligned(32))); + +struct ehci_qh_hw { + struct ehci_qh hw; + struct ehci_qtd_hw qtd_pool[CONFIG_USB_EHCI_QTD_NUM]; + uint32_t first_qtd; + struct usbh_urb *urb; + usb_osal_sem_t waitsem; + uint8_t remove_in_iaad; +} __attribute__((aligned(32))); + +struct ehci_itd_hw { + struct ehci_itd hw; + struct usbh_urb *urb; + uint16_t start_frame; + uint8_t mf_unmask; + uint8_t mf_valid; + uint32_t pkt_idx[8]; +} __attribute__((aligned(32))); + +struct ehci_iso_hw +{ + struct ehci_itd_hw itd_pool[CONFIG_USB_EHCI_ITD_NUM]; + uint32_t itd_num; }; -/* USB2 Debug Port Register Interface. - * This register block is normally found via the PCI capabalities. - * In non-PCI implementions, you need apriori information about the - * location of these registers. - */ - -struct ehci_debug { - uint32_t psc; /* 0x00: Debug Port Control/Status Register */ - uint32_t pids; /* 0x04: Debug USB PIDs Register */ - uint32_t data[2]; /* 0x08: Debug Data buffer Registers */ - uint32_t addr; /* 0x10: Device Address Register */ +struct ehci_hcd { + bool ehci_qh_used[CONFIG_USB_EHCI_QH_NUM]; + bool ehci_iso_used[CONFIG_USB_EHCI_ISO_NUM]; + bool ppc; /* Port Power Control */ + bool has_tt; /* if use tt instead of Companion Controller */ + uint8_t n_cc; /* Number of Companion Controller */ + uint8_t n_pcc; /* Number of ports supported per companion host controller */ + uint8_t n_ports; + uint8_t hcor_offset; }; -/* Data Structures **********************************************************/ +extern struct ehci_hcd g_ehci_hcd[CONFIG_USBHOST_MAX_BUS]; +extern uint32_t g_framelist[CONFIG_USBHOST_MAX_BUS][USB_ALIGN_UP(CONFIG_USB_EHCI_FRAME_LIST_SIZE, 1024)]; -/* Queue Element Transfer Descriptor (qTD). Paragraph 3.5 */ +int ehci_iso_urb_init(struct usbh_bus *bus, struct usbh_urb *urb); +void ehci_kill_iso_urb(struct usbh_bus *bus, struct usbh_urb *urb); +void ehci_scan_isochronous_list(struct usbh_bus *bus); -struct ehci_qtd { - uint32_t next_qtd; /* 0x00-0x03: Next qTD Pointer */ - uint32_t alt_next_qtd; /* 0x04-0x07: Alternate Next qTD Pointer */ - uint32_t token; /* 0x08-0x0b: qTD Token */ - uint32_t bpl[5]; /* 0x0c-0x1c: Buffer Page Pointer List */ -}; - -#define SIZEOF_EHCI_QTD (32) /* 8*sizeof(uint32_t) */ - -/* Queue Head. Paragraph 3.6 */ - -struct ehci_qh { - uint32_t hlp; /* 0x00-0x03: Queue Head Horizontal Link Pointer */ - uint32_t epchar; /* 0x04-0x07: Endpoint Characteristics */ - uint32_t epcap; /* 0x08-0x0b: Endpoint Capabilities */ - uint32_t curr_qtd; /* 0x0c-0x0f: Current qTD Pointer */ - struct ehci_qtd overlay; /* 0x10-0x2c: Transfer overlay */ -}; - -#define SIZEOF_EHCI_QH (48) /* 4*sizeof(uint32_t) + 32 */ - -/* Isochronous (High-Speed) Transfer Descriptor (iTD). - * Paragraph 3.3. Must be aligned to 32-byte boundaries. - */ - -struct ehci_itd { - uint32_t nlp; /* 0x00-0x03: Next link pointer */ - uint32_t tscl[8]; /* 0x04-0x23: Transaction Status and Control List */ - uint32_t bpl[7]; /* 0x24-0x3c: Buffer Page Pointer List */ -}; - -#define SIZEOF_EHCI_ITD (64) /* 16*sizeof(uint32_t) */ - -/* Split Transaction Isochronous Transfer Descriptor (siTD). Paragraph 3.4 */ - -struct ehci_sitd { - uint32_t nlp; /* 0x00-0x03: Next link pointer */ - uint32_t epchar; /* 0x04-0x07: Endpoint and Transaction Translator Characteristics */ - uint32_t mfsc; /* 0x08-0x0b: Micro-frame Schedule Control */ - uint32_t tsc; /* 0x0c-0x0f: Transfer Status and Control */ - uint32_t bpl[2]; /* 0x10-0x17: Buffer Pointer List */ - uint32_t blp; /* 0x18-0x1b: Back link pointer */ -}; - -#define SIZEOF_EHCI_SITD (28) /* 7*sizeof(uint32_t) */ - -#endif /* USB_HC_EHCI_H */ +#endif diff --git a/rt-thread/components/drivers/usb/cherryusb/port/fsdev/usb_dc_fsdev.c b/rt-thread/components/drivers/usb/cherryusb/port/fsdev/usb_dc_fsdev.c index b3bfb5c..f27e8c6 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/fsdev/usb_dc_fsdev.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/fsdev/usb_dc_fsdev.c @@ -117,6 +117,11 @@ int usbd_set_address(uint8_t busid, const uint8_t addr) return 0; } +int usbd_set_remote_wakeup(uint8_t busid) +{ + return -1; +} + uint8_t usbd_get_port_speed(uint8_t busid) { return USB_SPEED_FULL; @@ -149,7 +154,8 @@ int usbd_ep_open(uint8_t busid, const struct usb_endpoint_descriptor *ep) case USB_ENDPOINT_TYPE_ISOCHRONOUS: wEpRegVal = USB_EP_ISOCHRONOUS; - break; + USB_LOG_ERR("Do not support iso in fsdev\r\n"); + return -1; default: break; @@ -255,8 +261,20 @@ int usbd_ep_clear_stall(uint8_t busid, const uint8_t ep) int usbd_ep_is_stalled(uint8_t busid, const uint8_t ep, uint8_t *stalled) { + uint8_t ep_idx = USB_EP_GET_IDX(ep); + if (USB_EP_DIR_IS_OUT(ep)) { + if (PCD_GET_EP_RX_STATUS(USB, ep_idx) & USB_EP_RX_STALL) { + *stalled = 1; + } else { + *stalled = 0; + } } else { + if (PCD_GET_EP_TX_STATUS(USB, ep_idx) & USB_EP_TX_STALL) { + *stalled = 1; + } else { + *stalled = 0; + } } return 0; } diff --git a/rt-thread/components/drivers/usb/cherryusb/port/hpm/usb_dc_hpm.c b/rt-thread/components/drivers/usb/cherryusb/port/hpm/usb_dc_hpm.c index 31ad5b7..b663fd9 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/hpm/usb_dc_hpm.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/hpm/usb_dc_hpm.c @@ -39,6 +39,7 @@ struct hpm_ep_state { /* Driver state */ struct hpm_udc { usb_device_handle_t *handle; + bool is_suspend; struct hpm_ep_state in_ep[USB_NUM_BIDIR_ENDPOINTS]; /*!< IN endpoint parameters*/ struct hpm_ep_state out_ep[USB_NUM_BIDIR_ENDPOINTS]; /*!< OUT endpoint parameters */ } g_hpm_udc[CONFIG_USBDEV_MAX_BUS]; @@ -91,7 +92,7 @@ int usb_dc_init(uint8_t busid) } uint32_t int_mask; - int_mask = (USB_USBINTR_UE_MASK | USB_USBINTR_UEE_MASK | + int_mask = (USB_USBINTR_UE_MASK | USB_USBINTR_UEE_MASK | USB_USBINTR_SLE_MASK | USB_USBINTR_PCE_MASK | USB_USBINTR_URE_MASK); usb_device_init(g_hpm_udc[busid].handle, int_mask); @@ -116,6 +117,21 @@ int usbd_set_address(uint8_t busid, const uint8_t addr) return 0; } +int usbd_set_remote_wakeup(uint8_t busid) +{ + USB_Type *ptr; + + ptr = g_hpm_udc[busid].handle->regs; + + if (!usb_get_suspend_status(ptr)) { + return -1; + } + + usb_force_port_resume(ptr); + + return 0; +} + uint8_t usbd_get_port_speed(uint8_t busid) { uint8_t speed; @@ -259,6 +275,7 @@ void USBD_IRQHandler(uint8_t busid) } if (int_status & intr_reset) { + g_hpm_udc[busid].is_suspend = false; memset(g_hpm_udc[busid].in_ep, 0, sizeof(struct hpm_ep_state) * USB_NUM_BIDIR_ENDPOINTS); memset(g_hpm_udc[busid].out_ep, 0, sizeof(struct hpm_ep_state) * USB_NUM_BIDIR_ENDPOINTS); usbd_event_reset_handler(busid); @@ -267,10 +284,12 @@ void USBD_IRQHandler(uint8_t busid) if (int_status & intr_suspend) { if (usb_device_get_suspend_status(handle)) { - usbd_event_suspend_handler(busid); /* Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. */ if (usb_device_get_address(handle)) { + g_hpm_udc[busid].is_suspend = true; + usbd_event_suspend_handler(busid); } + } else { } } @@ -278,6 +297,10 @@ void USBD_IRQHandler(uint8_t busid) if (!usb_device_get_port_ccs(handle)) { usbd_event_disconnect_handler(busid); } else { + if (g_hpm_udc[busid].is_suspend) { + g_hpm_udc[busid].is_suspend = false; + usbd_event_resume_handler(busid); + } usbd_event_connect_handler(busid); } } @@ -314,7 +337,7 @@ void USBD_IRQHandler(uint8_t busid) transfer_len += p_qtd->expected_bytes - p_qtd->total_bytes; } - if (p_qtd->next == USB_SOC_DCD_QTD_NEXT_INVALID){ + if (p_qtd->next == USB_SOC_DCD_QTD_NEXT_INVALID) { break; } else { p_qtd = (dcd_qtd_t *)p_qtd->next; @@ -335,16 +358,20 @@ void USBD_IRQHandler(uint8_t busid) } } +#if !defined(USBD_USE_CUSTOM_ISR) || !USBD_USE_CUSTOM_ISR + +SDK_DECLARE_EXT_ISR_M(IRQn_USB0, isr_usbd0) void isr_usbd0(void) { USBD_IRQHandler(_dcd_busid[0]); } -SDK_DECLARE_EXT_ISR_M(IRQn_USB0, isr_usbd0) #ifdef HPM_USB1_BASE +SDK_DECLARE_EXT_ISR_M(IRQn_USB1, isr_usbd1) void isr_usbd1(void) { USBD_IRQHandler(_dcd_busid[1]); } -SDK_DECLARE_EXT_ISR_M(IRQn_USB1, isr_usbd1) -#endif \ No newline at end of file +#endif + +#endif diff --git a/rt-thread/components/drivers/usb/cherryusb/port/kinetis/README.md b/rt-thread/components/drivers/usb/cherryusb/port/kinetis/README.md new file mode 100644 index 0000000..f51c9a6 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/kinetis/README.md @@ -0,0 +1,18 @@ +# Note + +## Support Chip List + +### NXP + +Modify USB_NOCACHE_RAM_SECTION + +``` +#define USB_NOCACHE_RAM_SECTION __attribute__((section(".NonCacheable"))) +``` + +- MCXC444/MCXA153 (device only) +- MCXN947 + +### MM32 + +- MM32F3/MM32F5 diff --git a/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_dc_kinetis.c b/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_dc_kinetis.c new file mode 100644 index 0000000..bd2b287 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_dc_kinetis.c @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbd_core.h" +#include "usb_kinetis_reg.h" + +#define USB_OTG_DEV ((KINETIS_TypeDef *)g_usbdev_bus[busid].reg_base) + +/* Endpoint state */ +struct kinetis_ep_state { + uint16_t ep_mps; /* Endpoint max packet size */ + uint8_t ep_type; /* Endpoint type */ + uint8_t ep_stalled; /* Endpoint stall flag */ + uint8_t ep_enable; /* Endpoint enable */ + bool ep_odd; /* Endpoint odd */ + uint8_t *xfer_buf; + uint32_t xfer_len; + uint32_t actual_xfer_len; +}; + +/* Driver state */ +struct kinetis_udc { + uint8_t dev_addr; + struct kinetis_ep_state in_ep[CONFIG_USBDEV_EP_NUM]; /*!< IN endpoint parameters*/ + struct kinetis_ep_state out_ep[CONFIG_USBDEV_EP_NUM]; /*!< OUT endpoint parameters */ +} g_kinetis_udc[CONFIG_USBDEV_MAX_BUS]; + +USB_NOCACHE_RAM_SECTION __attribute__((aligned(512))) kinetis_bd_table_t g_kinetis_bdt[CONFIG_USBDEV_MAX_BUS]; +USB_NOCACHE_RAM_SECTION __attribute__((aligned(32))) uint8_t setup_packet[CONFIG_USBDEV_MAX_BUS][8]; + +static int kinetis_start_transfer(uint8_t busid, uint8_t ep, uint8_t *buffer, uint16_t buflen) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + uint8_t dir; + uint8_t odd; + uint16_t ep_mps; + kinetis_bd_t *bd; + kinetis_bd_t *next; + + if (USB_EP_DIR_IS_OUT(ep)) { + dir = 0; + odd = g_kinetis_udc[busid].out_ep[ep_idx].ep_odd; + ep_mps = g_kinetis_udc[busid].out_ep[ep_idx].ep_mps; + } else { + dir = 1; + odd = g_kinetis_udc[busid].in_ep[ep_idx].ep_odd; + ep_mps = g_kinetis_udc[busid].in_ep[ep_idx].ep_mps; + } + + bd = &g_kinetis_bdt[busid].table[ep_idx][dir][odd]; + + if (bd->own) { + USB_LOG_INFO("ep%02x is busy\r\n", ep); + return -1; + } + + bd->bc = buflen >= ep_mps ? ep_mps : buflen; + bd->addr = (uint32_t)buffer; + bd->own = 1; + return 0; +} + +static void kinetis_read_setup(uint8_t busid) +{ + uint8_t out_odd = g_kinetis_udc[busid].out_ep[0].ep_odd; + uint8_t in_odd = g_kinetis_udc[busid].in_ep[0].ep_odd; + + if (g_kinetis_bdt[busid].table[0][0][out_odd].own) { + USB_LOG_INFO("ep0 is busy\r\n"); + return; + } + + g_kinetis_bdt[busid].table[0][0][out_odd].data = 0; + g_kinetis_bdt[busid].table[0][0][out_odd ^ 1].data = 1; + g_kinetis_bdt[busid].table[0][1][in_odd].data = 1; + g_kinetis_bdt[busid].table[0][1][in_odd ^ 1].data = 0; + + kinetis_start_transfer(busid, USB_CONTROL_OUT_EP0, setup_packet[busid], 8); +} + +__WEAK void usb_dc_low_level_init(uint8_t busid) +{ +} + +__WEAK void usb_dc_low_level_deinit(uint8_t busid) +{ +} + +int usb_dc_init(uint8_t busid) +{ + usb_dc_low_level_init(busid); + + memset(&g_kinetis_udc[busid], 0, sizeof(g_kinetis_udc[busid])); + + USB_OTG_DEV->BDTPAGE1 = (uint8_t)((uintptr_t)&g_kinetis_bdt[busid] >> 8); + USB_OTG_DEV->BDTPAGE2 = (uint8_t)((uintptr_t)&g_kinetis_bdt[busid] >> 16); + USB_OTG_DEV->BDTPAGE3 = (uint8_t)((uintptr_t)&g_kinetis_bdt[busid] >> 24); + + USB_OTG_DEV->INTEN = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK | + USB_INTEN_SLEEPEN_MASK | USB_INTEN_RESUMEEN_MASK | + USB_INTEN_ERROREN_MASK; + + USB_OTG_DEV->CTL |= USB_CTL_USBENSOFEN_MASK; + return 0; +} + +int usb_dc_deinit(uint8_t busid) +{ + usb_dc_low_level_deinit(busid); + return 0; +} + +int usbd_set_address(uint8_t busid, const uint8_t addr) +{ + g_kinetis_udc[busid].dev_addr = addr; + + if (addr == 0) { + USB_OTG_DEV->ADDR = 0; + } + return 0; +} + +int usbd_set_remote_wakeup(uint8_t busid) +{ + USB_OTG_DEV->CTL |= USB_CTL_RESUME_MASK; + + usbd_kinetis_delay_ms(10); + + USB_OTG_DEV->CTL &= ~USB_CTL_RESUME_MASK; + + return 0; +} + +uint8_t usbd_get_port_speed(uint8_t busid) +{ + return USB_SPEED_FULL; +} + +int usbd_ep_open(uint8_t busid, const struct usb_endpoint_descriptor *ep) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep->bEndpointAddress); + uint8_t odd; + uint8_t dir; + kinetis_bd_t *bd; + uint8_t regval; + + /* Must not exceed max endpoint number */ + if (ep_idx >= CONFIG_USBDEV_EP_NUM) { + return -1; + } + + if (USB_EP_DIR_IS_OUT(ep->bEndpointAddress)) { + g_kinetis_udc[busid].out_ep[ep_idx].ep_mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); + g_kinetis_udc[busid].out_ep[ep_idx].ep_type = USB_GET_ENDPOINT_TYPE(ep->bmAttributes); + g_kinetis_udc[busid].out_ep[ep_idx].ep_enable = true; + + dir = 0; + odd = g_kinetis_udc[busid].out_ep[ep_idx].ep_odd; + } else { + g_kinetis_udc[busid].in_ep[ep_idx].ep_mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); + g_kinetis_udc[busid].in_ep[ep_idx].ep_type = USB_GET_ENDPOINT_TYPE(ep->bmAttributes); + g_kinetis_udc[busid].in_ep[ep_idx].ep_enable = true; + + dir = 1; + odd = g_kinetis_udc[busid].in_ep[ep_idx].ep_odd; + } + + if (ep_idx != 0) { + regval = USB_ENDPT_EPCTLDIS_MASK; + regval |= (USB_GET_ENDPOINT_TYPE(ep->bmAttributes) != USB_ENDPOINT_TYPE_ISOCHRONOUS) ? USB_ENDPT_EPHSHK_MASK : 0; + regval |= dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK; + USB_OTG_DEV->ENDPOINT[ep_idx].ENDPT |= regval; + + if (USB_GET_ENDPOINT_TYPE(ep->bmAttributes) != USB_ENDPOINT_TYPE_ISOCHRONOUS) { + bd = &g_kinetis_bdt[busid].table[ep_idx][dir][odd]; + bd->dts = 1; + bd->data = 0; + + bd = &g_kinetis_bdt[busid].table[ep_idx][dir][odd ^ 1]; + bd->dts = 1; + bd->data = 1; + } + } + + return 0; +} + +int usbd_ep_close(uint8_t busid, const uint8_t ep) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + uint8_t dir; + kinetis_bd_t *bd; + + if (USB_EP_DIR_IS_OUT(ep)) { + g_kinetis_udc[busid].out_ep[ep_idx].ep_enable = false; + dir = 0; + } else { + g_kinetis_udc[busid].in_ep[ep_idx].ep_enable = false; + dir = 1; + } + + bd = &g_kinetis_bdt[busid].table[ep_idx][dir][0]; + bd->head = 0; + + bd = &g_kinetis_bdt[busid].table[ep_idx][dir][1]; + bd->head = 0; + + USB_OTG_DEV->ENDPOINT[ep_idx].ENDPT &= ~(dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK); + return 0; +} + +int usbd_ep_set_stall(uint8_t busid, const uint8_t ep) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + kinetis_bd_t *bd; + uint8_t odd; + uint8_t dir; + + if (0 == ep_idx) { + USB_OTG_DEV->ENDPOINT[ep_idx].ENDPT |= USB_ENDPT_EPSTALL_MASK; + + if (ep_idx == 0) { + kinetis_read_setup(busid); + } + } else { + if (USB_EP_DIR_IS_OUT(ep)) { + dir = 0; + odd = g_kinetis_udc[busid].out_ep[ep_idx].ep_odd; + } else { + dir = 1; + odd = g_kinetis_udc[busid].in_ep[ep_idx].ep_odd; + } + + bd = &g_kinetis_bdt[busid].table[ep_idx][dir][odd]; + + bd->bdt_stall = 1; + bd->own = 1; + } + return 0; +} + +int usbd_ep_clear_stall(uint8_t busid, const uint8_t ep) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + kinetis_bd_t *bd; + uint8_t odd; + uint8_t dir; + + if (USB_EP_DIR_IS_OUT(ep)) { + dir = 0; + odd = g_kinetis_udc[busid].out_ep[ep_idx].ep_odd; + } else { + dir = 1; + odd = g_kinetis_udc[busid].in_ep[ep_idx].ep_odd; + } + + bd = &g_kinetis_bdt[busid].table[ep_idx][dir][odd]; + + bd->own = 0; + bd->bdt_stall = 0; + bd->data = 0; + + bd = &g_kinetis_bdt[busid].table[ep_idx][dir][odd ^ 1]; + bd->data = 1; + + uint8_t regval = USB_OTG_DEV->ENDPOINT[ep_idx].ENDPT; + if (regval & USB_ENDPT_EPSTALL_MASK) { + USB_OTG_DEV->ENDPOINT[ep_idx].ENDPT = regval & ~USB_ENDPT_EPSTALL_MASK; + } + + return 0; +} + +int usbd_ep_is_stalled(uint8_t busid, const uint8_t ep, uint8_t *stalled) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + + uint8_t regval = USB_OTG_DEV->ENDPOINT[ep_idx].ENDPT; + if (regval & USB_ENDPT_EPSTALL_MASK) { + *stalled = 1; + } else { + *stalled = 0; + } + + return 0; +} + +int usbd_ep_start_write(uint8_t busid, const uint8_t ep, const uint8_t *data, + uint32_t data_len) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (!data && data_len) { + return -1; + } + if (!g_kinetis_udc[busid].in_ep[ep_idx].ep_enable) { + return -2; + } + + g_kinetis_udc[busid].in_ep[ep_idx].xfer_buf = (uint8_t *)data; + g_kinetis_udc[busid].in_ep[ep_idx].xfer_len = data_len; + g_kinetis_udc[busid].in_ep[ep_idx].actual_xfer_len = 0; + + return kinetis_start_transfer(busid, ep, (uint8_t *)data, MIN(data_len, g_kinetis_udc[busid].in_ep[ep_idx].ep_mps)); +} + +int usbd_ep_start_read(uint8_t busid, const uint8_t ep, uint8_t *data, + uint32_t data_len) +{ + uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (!data && data_len) { + return -1; + } + if (!g_kinetis_udc[busid].out_ep[ep_idx].ep_enable) { + return -2; + } + + g_kinetis_udc[busid].out_ep[ep_idx].xfer_buf = (uint8_t *)data; + g_kinetis_udc[busid].out_ep[ep_idx].xfer_len = data_len; + g_kinetis_udc[busid].out_ep[ep_idx].actual_xfer_len = 0; + + return kinetis_start_transfer(busid, ep, (uint8_t *)data, MIN(data_len, g_kinetis_udc[busid].out_ep[ep_idx].ep_mps)); +} + +void USBD_IRQHandler(uint8_t busid) +{ + uint8_t s; + uint8_t pid; + uint8_t ep_idx; + uint8_t dir; + uint8_t odd; + uint16_t bc; + uint8_t is = USB_OTG_DEV->ISTAT; + uint8_t mask = USB_OTG_DEV->INTEN; + kinetis_bd_t *bd; + + USB_OTG_DEV->ISTAT = is & ~mask; + is &= mask; + + if (is & USB_ISTAT_ERROR_MASK) { + uint32_t es = USB_OTG_DEV->ERRSTAT; + USB_OTG_DEV->ERRSTAT = es; + USB_OTG_DEV->ISTAT = is; + } + + if (is & USB_ISTAT_USBRST_MASK) { + USB_OTG_DEV->ISTAT = is; + USB_OTG_DEV->CTL |= USB_CTL_ODDRST_MASK; + USB_OTG_DEV->ADDR = 0; + + USB_OTG_DEV->ENDPOINT[0].ENDPT = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK; + for (uint8_t i = 1; i < 16; i++) { + USB_OTG_DEV->ENDPOINT[i].ENDPT = 0; + } + + memset(&g_kinetis_bdt[busid], 0, sizeof(g_kinetis_bdt[busid])); + memset(g_kinetis_udc[busid].in_ep, 0, sizeof(struct kinetis_ep_state) * CONFIG_USBDEV_EP_NUM); + memset(g_kinetis_udc[busid].out_ep, 0, sizeof(struct kinetis_ep_state) * CONFIG_USBDEV_EP_NUM); + usbd_event_reset_handler(busid); + + kinetis_read_setup(busid); + + USB_OTG_DEV->CTL &= ~USB_CTL_ODDRST_MASK; + } + + if (is & USB_ISTAT_SLEEP_MASK) { + USB_OTG_DEV->ISTAT = USB_ISTAT_SLEEP_MASK; + } + + if (is & USB_ISTAT_RESUME_MASK) { + USB_OTG_DEV->ISTAT = USB_ISTAT_RESUME_MASK; + } + + if (is & USB_ISTAT_SOFTOK_MASK) { + USB_OTG_DEV->ISTAT = USB_ISTAT_SOFTOK_MASK; + } + + if (is & USB_ISTAT_STALL_MASK) { + USB_OTG_DEV->ISTAT = USB_ISTAT_STALL_MASK; + } + + if (is & USB_ISTAT_TOKDNE_MASK) { + s = USB_OTG_DEV->STAT; + USB_OTG_DEV->ISTAT = USB_ISTAT_TOKDNE_MASK; /* must be cleared after get STAT */ + + ep_idx = (s & USB_STAT_ENDP_MASK) >> USB_STAT_ENDP_SHIFT; + dir = (s & USB_STAT_TX_MASK) >> USB_STAT_TX_SHIFT; + odd = (s & USB_STAT_ODD_MASK) >> USB_STAT_ODD_SHIFT; + + bd = &g_kinetis_bdt[busid].table[ep_idx][dir][odd]; + + pid = bd->tok_pid; + bc = bd->bc; + + bd->bdt_stall = 0; + bd->dts = 1; + bd->ninc = 0; + bd->keep = 0; + + if (dir) { + g_kinetis_udc[busid].in_ep[ep_idx].ep_odd = odd ^ 1; + } else { + g_kinetis_udc[busid].out_ep[ep_idx].ep_odd = odd ^ 1; + } + + if (pid == USB_TOKEN_PID_SETUP) { + USB_OTG_DEV->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK; + usbd_event_ep0_setup_complete_handler(busid, (uint8_t *)bd->addr); + return; + } + + if (dir) { + g_kinetis_udc[busid].in_ep[ep_idx].xfer_buf += bc; + g_kinetis_udc[busid].in_ep[ep_idx].xfer_len -= bc; + g_kinetis_udc[busid].in_ep[ep_idx].actual_xfer_len += bc; + + if (g_kinetis_udc[busid].in_ep[ep_idx].xfer_len == 0) { + usbd_event_ep_in_complete_handler(busid, ep_idx | 0x80, g_kinetis_udc[busid].in_ep[ep_idx].actual_xfer_len); + } else { + kinetis_start_transfer(busid, ep_idx | 0x80, g_kinetis_udc[busid].in_ep[ep_idx].xfer_buf, + MIN(g_kinetis_udc[busid].in_ep[ep_idx].xfer_len, g_kinetis_udc[busid].in_ep[ep_idx].ep_mps)); + } + } else { + g_kinetis_udc[busid].out_ep[ep_idx].xfer_buf += bc; + g_kinetis_udc[busid].out_ep[ep_idx].xfer_len -= bc; + g_kinetis_udc[busid].out_ep[ep_idx].actual_xfer_len += bc; + + if ((bc < g_kinetis_udc[busid].out_ep[ep_idx].ep_mps) || (g_kinetis_udc[busid].out_ep[ep_idx].xfer_len == 0)) { + usbd_event_ep_out_complete_handler(busid, ep_idx, g_kinetis_udc[busid].out_ep[ep_idx].actual_xfer_len); + } else { + kinetis_start_transfer(busid, ep_idx, g_kinetis_udc[busid].out_ep[ep_idx].xfer_buf, + MIN(g_kinetis_udc[busid].out_ep[ep_idx].xfer_len, g_kinetis_udc[busid].out_ep[ep_idx].ep_mps)); + } + } + + if ((bc == 0) && (ep_idx == 0)) { + if ((g_kinetis_udc[busid].dev_addr > 0) && dir) { + USB_OTG_DEV->ADDR = g_kinetis_udc[busid].dev_addr; + g_kinetis_udc[busid].dev_addr = 0; + } + + kinetis_read_setup(busid); + } + } +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_glue_mcx.c b/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_glue_mcx.c new file mode 100644 index 0000000..4460779 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_glue_mcx.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbd_core.h" +#include "fsl_common.h" +#include "usb_kinetis_reg.h" + +#define USB_OTG_DEV ((KINETIS_MCX_TypeDef *)g_usbdev_bus[busid].reg_base) + +#if defined(MCXC444_H_) +#define USBD_IRQ USB0_IRQHandler +void USB_ClockInit(void) +{ + SystemCoreClockUpdate(); + CLOCK_EnableUsbfs0Clock(kCLOCK_UsbSrcIrc48M, 48000000U); +} +#elif defined(MCXA153_H_) +#define USBD_IRQ USB0_IRQHandler +void USB_ClockInit(void) +{ + RESET_PeripheralReset(kUSB0_RST_SHIFT_RSTn); + CLOCK_EnableUsbfsClock(); +} +#elif defined(MCXN947_CM33_CORE0_H_) +#define USBD_IRQ USB0_FS_IRQHandler +void USB_ClockInit(void) +{ + CLOCK_AttachClk(kCLK_48M_to_USB0); + CLOCK_EnableClock(kCLOCK_Usb0Ram); + CLOCK_EnableClock(kCLOCK_Usb0Fs); + CLOCK_EnableUsbfsClock(); +} +#else +#error "Unsupported MCU with Kinetis IP" +#endif + +void USBD_IRQ(void) +{ + extern void USBD_IRQHandler(uint8_t busid); + USBD_IRQHandler(0); +} + +void usb_dc_low_level_init(uint8_t busid) +{ + USB_ClockInit(); + + uint8_t irqNumber; + + uint8_t usbDeviceKhciIrq[] = USB_IRQS; + irqNumber = usbDeviceKhciIrq[0]; + + /* Install isr, set priority, and enable IRQ. */ + NVIC_SetPriority((IRQn_Type)irqNumber, 3); + EnableIRQ((IRQn_Type)irqNumber); + + USB_OTG_DEV->USBTRC0 |= USB_USBTRC0_USBRESET_MASK; + while (USB_OTG_DEV->USBTRC0 & USB_USBTRC0_USBRESET_MASK) + ; + + USB_OTG_DEV->USBTRC0 |= USB_USBTRC0_VREGIN_STS(1); /* software must set this bit to 1 */ + USB_OTG_DEV->USBCTRL = 0; + USB_OTG_DEV->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK; +} + +void usb_dc_low_level_deinit(uint8_t busid) +{ + USB_OTG_DEV->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK; + DisableIRQ((IRQn_Type)USB0_FS_IRQn); +} + +void usbd_kinetis_delay_ms(uint8_t ms) +{ +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_hc_kinetis.c b/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_hc_kinetis.c new file mode 100644 index 0000000..30404ce --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_hc_kinetis.c @@ -0,0 +1 @@ +TODO \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_kinetis_reg.h b/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_kinetis_reg.h new file mode 100644 index 0000000..b2720a6 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/kinetis/usb_kinetis_reg.h @@ -0,0 +1,1487 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __USB_KINETIS_REG_H__ +#define __USB_KINETIS_REG_H__ + +#define __I volatile const /* Define "read-only" permission */ +#define __IO volatile /* Define "read-write" permission */ + +/* ---------------------------------------------------------------------------- + -- USB Peripheral Access Layer + ---------------------------------------------------------------------------- */ + +/*! + * @addtogroup USB_Peripheral_Access_Layer USB Peripheral Access Layer + * @{ + */ + +/** USB - Register Layout Typedef */ +typedef struct { + __I uint8_t PERID; /**< Peripheral ID, offset: 0x0 */ + uint8_t RESERVED_0[3]; + __I uint8_t IDCOMP; /**< Peripheral ID Complement, offset: 0x4 */ + uint8_t RESERVED_1[3]; + __I uint8_t REV; /**< Peripheral Revision, offset: 0x8 */ + uint8_t RESERVED_2[3]; + __I uint8_t ADDINFO; /**< Peripheral Additional Information, offset: 0xC */ + uint8_t RESERVED_3[3]; + __IO uint8_t OTGISTAT; /**< OTG Interrupt Status, offset: 0x10 */ + uint8_t RESERVED_4[3]; + __IO uint8_t OTGICR; /**< OTG Interrupt Control, offset: 0x14 */ + uint8_t RESERVED_5[3]; + __I uint8_t OTGSTAT; /**< OTG Status, offset: 0x18 */ + uint8_t RESERVED_6[3]; + __IO uint8_t OTGCTL; /**< OTG Control, offset: 0x1C */ + uint8_t RESERVED_7[99]; + __IO uint8_t ISTAT; /**< Interrupt Status, offset: 0x80 */ + uint8_t RESERVED_8[3]; + __IO uint8_t INTEN; /**< Interrupt Enable, offset: 0x84 */ + uint8_t RESERVED_9[3]; + __IO uint8_t ERRSTAT; /**< Error Interrupt Status, offset: 0x88 */ + uint8_t RESERVED_10[3]; + __IO uint8_t ERREN; /**< Error Interrupt Enable, offset: 0x8C */ + uint8_t RESERVED_11[3]; + __I uint8_t STAT; /**< Status, offset: 0x90 */ + uint8_t RESERVED_12[3]; + __IO uint8_t CTL; /**< Control, offset: 0x94 */ + uint8_t RESERVED_13[3]; + __IO uint8_t ADDR; /**< Address, offset: 0x98 */ + uint8_t RESERVED_14[3]; + __IO uint8_t BDTPAGE1; /**< BDT Page 1, offset: 0x9C */ + uint8_t RESERVED_15[3]; + __I uint8_t FRMNUML; /**< Frame Number Register Low, offset: 0xA0 */ + uint8_t RESERVED_16[3]; + __I uint8_t FRMNUMH; /**< Frame Number Register High, offset: 0xA4 */ + uint8_t RESERVED_17[3]; + __IO uint8_t TOKEN; /**< Token, offset: 0xA8 */ + uint8_t RESERVED_18[3]; + __IO uint8_t SOFTHLD; /**< SOF Threshold, offset: 0xAC */ + uint8_t RESERVED_19[3]; + __IO uint8_t BDTPAGE2; /**< BDT Page 2, offset: 0xB0 */ + uint8_t RESERVED_20[3]; + __IO uint8_t BDTPAGE3; /**< BDT Page 3, offset: 0xB4 */ + uint8_t RESERVED_21[11]; + struct { /* offset: 0xC0, array step: 0x4 */ + __IO uint8_t ENDPT; /**< Endpoint Control, array offset: 0xC0, array step: 0x4 */ + uint8_t RESERVED_0[3]; + } ENDPOINT[16]; +} KINETIS_TypeDef; + +/* ---------------------------------------------------------------------------- + -- USB Register Masks + ---------------------------------------------------------------------------- */ + +/*! + * @addtogroup USB_Register_Masks USB Register Masks + * @{ + */ + +/*! @name PERID - Peripheral ID */ +/*! @{ */ + +#define USB_PERID_ID_MASK (0x3FU) +#define USB_PERID_ID_SHIFT (0U) +/*! ID - Peripheral Identification */ +#define USB_PERID_ID(x) (((uint8_t)(((uint8_t)(x)) << USB_PERID_ID_SHIFT)) & USB_PERID_ID_MASK) +/*! @} */ + +/*! @name IDCOMP - Peripheral ID Complement */ +/*! @{ */ + +#define USB_IDCOMP_NID_MASK (0x3FU) +#define USB_IDCOMP_NID_SHIFT (0U) +/*! NID - Negative Peripheral ID */ +#define USB_IDCOMP_NID(x) (((uint8_t)(((uint8_t)(x)) << USB_IDCOMP_NID_SHIFT)) & USB_IDCOMP_NID_MASK) +/*! @} */ + +/*! @name REV - Peripheral Revision */ +/*! @{ */ + +#define USB_REV_REV_MASK (0xFFU) +#define USB_REV_REV_SHIFT (0U) +/*! REV - Revision */ +#define USB_REV_REV(x) (((uint8_t)(((uint8_t)(x)) << USB_REV_REV_SHIFT)) & USB_REV_REV_MASK) +/*! @} */ + +/*! @name ADDINFO - Peripheral Additional Information */ +/*! @{ */ + +#define USB_ADDINFO_IEHOST_MASK (0x1U) +#define USB_ADDINFO_IEHOST_SHIFT (0U) +/*! IEHOST - Host Mode Enable + * 0b0..Disabled + * 0b1..Enabled + */ +#define USB_ADDINFO_IEHOST(x) (((uint8_t)(((uint8_t)(x)) << USB_ADDINFO_IEHOST_SHIFT)) & USB_ADDINFO_IEHOST_MASK) +/*! @} */ + +/*! @name OTGISTAT - OTG Interrupt Status */ +/*! @{ */ + +#define USB_OTGISTAT_LINE_STATE_CHG_MASK (0x20U) +#define USB_OTGISTAT_LINE_STATE_CHG_SHIFT (5U) +/*! LINE_STATE_CHG - Line State Change Interrupt Flag + * 0b0..Interrupt did not occur + * 0b1..Interrupt occurred + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_OTGISTAT_LINE_STATE_CHG(x) (((uint8_t)(((uint8_t)(x)) << USB_OTGISTAT_LINE_STATE_CHG_SHIFT)) & USB_OTGISTAT_LINE_STATE_CHG_MASK) + +#define USB_OTGISTAT_ONEMSEC_MASK (0x40U) +#define USB_OTGISTAT_ONEMSEC_SHIFT (6U) +/*! ONEMSEC - One Millisecond Timer Timeout Flag + * 0b0..Not timed out + * 0b1..Timed out + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_OTGISTAT_ONEMSEC(x) (((uint8_t)(((uint8_t)(x)) << USB_OTGISTAT_ONEMSEC_SHIFT)) & USB_OTGISTAT_ONEMSEC_MASK) +/*! @} */ + +/*! @name OTGICR - OTG Interrupt Control */ +/*! @{ */ + +#define USB_OTGICR_LINESTATEEN_MASK (0x20U) +#define USB_OTGICR_LINESTATEEN_SHIFT (5U) +/*! LINESTATEEN - Line State Change Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_OTGICR_LINESTATEEN(x) (((uint8_t)(((uint8_t)(x)) << USB_OTGICR_LINESTATEEN_SHIFT)) & USB_OTGICR_LINESTATEEN_MASK) + +#define USB_OTGICR_ONEMSECEN_MASK (0x40U) +#define USB_OTGICR_ONEMSECEN_SHIFT (6U) +/*! ONEMSECEN - 1-Millisecond Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_OTGICR_ONEMSECEN(x) (((uint8_t)(((uint8_t)(x)) << USB_OTGICR_ONEMSECEN_SHIFT)) & USB_OTGICR_ONEMSECEN_MASK) +/*! @} */ + +/*! @name OTGSTAT - OTG Status */ +/*! @{ */ + +#define USB_OTGSTAT_LINESTATESTABLE_MASK (0x20U) +#define USB_OTGSTAT_LINESTATESTABLE_SHIFT (5U) +/*! LINESTATESTABLE - Line State Stable + * 0b0..Unstable + * 0b1..Stable + */ +#define USB_OTGSTAT_LINESTATESTABLE(x) (((uint8_t)(((uint8_t)(x)) << USB_OTGSTAT_LINESTATESTABLE_SHIFT)) & USB_OTGSTAT_LINESTATESTABLE_MASK) + +#define USB_OTGSTAT_ONEMSEC_MASK (0x40U) +#define USB_OTGSTAT_ONEMSEC_SHIFT (6U) +/*! ONEMSEC - Reserved for 1 ms count */ +#define USB_OTGSTAT_ONEMSEC(x) (((uint8_t)(((uint8_t)(x)) << USB_OTGSTAT_ONEMSEC_SHIFT)) & USB_OTGSTAT_ONEMSEC_MASK) +/*! @} */ + +/*! @name OTGCTL - OTG Control */ +/*! @{ */ + +#define USB_OTGCTL_OTGEN_MASK (0x4U) +#define USB_OTGCTL_OTGEN_SHIFT (2U) +/*! OTGEN - On-The-Go Pullup and Pulldown Resistor Enable + * 0b0..If USBENSOFEN is 1 and HOSTMODEEN is 0 in the Control Register (CTL), then the D+ Data line pullup + * resistors are enabled. If HOSTMODEEN is 1, then the D+ and D- Data line pulldown resistors are engaged. + * 0b1..Uses the pullup and pulldown controls in this register. + */ +#define USB_OTGCTL_OTGEN(x) (((uint8_t)(((uint8_t)(x)) << USB_OTGCTL_OTGEN_SHIFT)) & USB_OTGCTL_OTGEN_MASK) + +#define USB_OTGCTL_DMLOW_MASK (0x10U) +#define USB_OTGCTL_DMLOW_SHIFT (4U) +/*! DMLOW - D- Data Line Pulldown Resistor Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_OTGCTL_DMLOW(x) (((uint8_t)(((uint8_t)(x)) << USB_OTGCTL_DMLOW_SHIFT)) & USB_OTGCTL_DMLOW_MASK) + +#define USB_OTGCTL_DPLOW_MASK (0x20U) +#define USB_OTGCTL_DPLOW_SHIFT (5U) +/*! DPLOW - D+ Data Line pulldown Resistor Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_OTGCTL_DPLOW(x) (((uint8_t)(((uint8_t)(x)) << USB_OTGCTL_DPLOW_SHIFT)) & USB_OTGCTL_DPLOW_MASK) + +#define USB_OTGCTL_DPHIGH_MASK (0x80U) +#define USB_OTGCTL_DPHIGH_SHIFT (7U) +/*! DPHIGH - D+ Data Line Pullup Resistor Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_OTGCTL_DPHIGH(x) (((uint8_t)(((uint8_t)(x)) << USB_OTGCTL_DPHIGH_SHIFT)) & USB_OTGCTL_DPHIGH_MASK) +/*! @} */ + +/*! @name ISTAT - Interrupt Status */ +/*! @{ */ + +#define USB_ISTAT_USBRST_MASK (0x1U) +#define USB_ISTAT_USBRST_SHIFT (0U) +/*! USBRST - USB Reset Flag + * 0b0..Not detected + * 0b1..Detected + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ISTAT_USBRST(x) (((uint8_t)(((uint8_t)(x)) << USB_ISTAT_USBRST_SHIFT)) & USB_ISTAT_USBRST_MASK) + +#define USB_ISTAT_ERROR_MASK (0x2U) +#define USB_ISTAT_ERROR_SHIFT (1U) +/*! ERROR - Error Flag + * 0b0..Error did not occur + * 0b1..Error occurred + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ISTAT_ERROR(x) (((uint8_t)(((uint8_t)(x)) << USB_ISTAT_ERROR_SHIFT)) & USB_ISTAT_ERROR_MASK) + +#define USB_ISTAT_SOFTOK_MASK (0x4U) +#define USB_ISTAT_SOFTOK_SHIFT (2U) +/*! SOFTOK - Start Of Frame (SOF) Token Flag + * 0b0..Did not receive + * 0b1..Received + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ISTAT_SOFTOK(x) (((uint8_t)(((uint8_t)(x)) << USB_ISTAT_SOFTOK_SHIFT)) & USB_ISTAT_SOFTOK_MASK) + +#define USB_ISTAT_TOKDNE_MASK (0x8U) +#define USB_ISTAT_TOKDNE_SHIFT (3U) +/*! TOKDNE - Current Token Processing Flag + * 0b0..Not processed + * 0b1..Processed + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ISTAT_TOKDNE(x) (((uint8_t)(((uint8_t)(x)) << USB_ISTAT_TOKDNE_SHIFT)) & USB_ISTAT_TOKDNE_MASK) + +#define USB_ISTAT_SLEEP_MASK (0x10U) +#define USB_ISTAT_SLEEP_SHIFT (4U) +/*! SLEEP - Sleep Flag + * 0b0..Interrupt did not occur + * 0b1..Interrupt occurred + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ISTAT_SLEEP(x) (((uint8_t)(((uint8_t)(x)) << USB_ISTAT_SLEEP_SHIFT)) & USB_ISTAT_SLEEP_MASK) + +#define USB_ISTAT_RESUME_MASK (0x20U) +#define USB_ISTAT_RESUME_SHIFT (5U) +/*! RESUME - Resume Flag + * 0b0..Interrupt did not occur + * 0b1..Interrupt occurred + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ISTAT_RESUME(x) (((uint8_t)(((uint8_t)(x)) << USB_ISTAT_RESUME_SHIFT)) & USB_ISTAT_RESUME_MASK) + +#define USB_ISTAT_ATTACH_MASK (0x40U) +#define USB_ISTAT_ATTACH_SHIFT (6U) +/*! ATTACH - Attach Interrupt Flag + * 0b0..Not detected + * 0b1..Detected + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ISTAT_ATTACH(x) (((uint8_t)(((uint8_t)(x)) << USB_ISTAT_ATTACH_SHIFT)) & USB_ISTAT_ATTACH_MASK) + +#define USB_ISTAT_STALL_MASK (0x80U) +#define USB_ISTAT_STALL_SHIFT (7U) +/*! STALL - Stall Interrupt Flag + * 0b0..Interrupt did not occur + * 0b1..Interrupt occurred + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ISTAT_STALL(x) (((uint8_t)(((uint8_t)(x)) << USB_ISTAT_STALL_SHIFT)) & USB_ISTAT_STALL_MASK) +/*! @} */ + +/*! @name INTEN - Interrupt Enable */ +/*! @{ */ + +#define USB_INTEN_USBRSTEN_MASK (0x1U) +#define USB_INTEN_USBRSTEN_SHIFT (0U) +/*! USBRSTEN - USBRST Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_INTEN_USBRSTEN(x) (((uint8_t)(((uint8_t)(x)) << USB_INTEN_USBRSTEN_SHIFT)) & USB_INTEN_USBRSTEN_MASK) + +#define USB_INTEN_ERROREN_MASK (0x2U) +#define USB_INTEN_ERROREN_SHIFT (1U) +/*! ERROREN - ERROR Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_INTEN_ERROREN(x) (((uint8_t)(((uint8_t)(x)) << USB_INTEN_ERROREN_SHIFT)) & USB_INTEN_ERROREN_MASK) + +#define USB_INTEN_SOFTOKEN_MASK (0x4U) +#define USB_INTEN_SOFTOKEN_SHIFT (2U) +/*! SOFTOKEN - SOFTOK Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_INTEN_SOFTOKEN(x) (((uint8_t)(((uint8_t)(x)) << USB_INTEN_SOFTOKEN_SHIFT)) & USB_INTEN_SOFTOKEN_MASK) + +#define USB_INTEN_TOKDNEEN_MASK (0x8U) +#define USB_INTEN_TOKDNEEN_SHIFT (3U) +/*! TOKDNEEN - TOKDNE Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_INTEN_TOKDNEEN(x) (((uint8_t)(((uint8_t)(x)) << USB_INTEN_TOKDNEEN_SHIFT)) & USB_INTEN_TOKDNEEN_MASK) + +#define USB_INTEN_SLEEPEN_MASK (0x10U) +#define USB_INTEN_SLEEPEN_SHIFT (4U) +/*! SLEEPEN - SLEEP Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_INTEN_SLEEPEN(x) (((uint8_t)(((uint8_t)(x)) << USB_INTEN_SLEEPEN_SHIFT)) & USB_INTEN_SLEEPEN_MASK) + +#define USB_INTEN_RESUMEEN_MASK (0x20U) +#define USB_INTEN_RESUMEEN_SHIFT (5U) +/*! RESUMEEN - RESUME Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_INTEN_RESUMEEN(x) (((uint8_t)(((uint8_t)(x)) << USB_INTEN_RESUMEEN_SHIFT)) & USB_INTEN_RESUMEEN_MASK) + +#define USB_INTEN_ATTACHEN_MASK (0x40U) +#define USB_INTEN_ATTACHEN_SHIFT (6U) +/*! ATTACHEN - ATTACH Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_INTEN_ATTACHEN(x) (((uint8_t)(((uint8_t)(x)) << USB_INTEN_ATTACHEN_SHIFT)) & USB_INTEN_ATTACHEN_MASK) + +#define USB_INTEN_STALLEN_MASK (0x80U) +#define USB_INTEN_STALLEN_SHIFT (7U) +/*! STALLEN - STALL Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_INTEN_STALLEN(x) (((uint8_t)(((uint8_t)(x)) << USB_INTEN_STALLEN_SHIFT)) & USB_INTEN_STALLEN_MASK) +/*! @} */ + +/*! @name ERRSTAT - Error Interrupt Status */ +/*! @{ */ + +#define USB_ERRSTAT_PIDERR_MASK (0x1U) +#define USB_ERRSTAT_PIDERR_SHIFT (0U) +/*! PIDERR - PID Error Flag + * 0b0..Did not fail + * 0b1..Failed + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ERRSTAT_PIDERR(x) (((uint8_t)(((uint8_t)(x)) << USB_ERRSTAT_PIDERR_SHIFT)) & USB_ERRSTAT_PIDERR_MASK) + +#define USB_ERRSTAT_CRC5EOF_MASK (0x2U) +#define USB_ERRSTAT_CRC5EOF_SHIFT (1U) +/*! CRC5EOF - CRC5 Error or End of Frame Error Flag + * 0b0..Interrupt did not occur + * 0b1..Interrupt occurred + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ERRSTAT_CRC5EOF(x) (((uint8_t)(((uint8_t)(x)) << USB_ERRSTAT_CRC5EOF_SHIFT)) & USB_ERRSTAT_CRC5EOF_MASK) + +#define USB_ERRSTAT_CRC16_MASK (0x4U) +#define USB_ERRSTAT_CRC16_SHIFT (2U) +/*! CRC16 - CRC16 Error Flag + * 0b0..Not rejected + * 0b1..Rejected + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ERRSTAT_CRC16(x) (((uint8_t)(((uint8_t)(x)) << USB_ERRSTAT_CRC16_SHIFT)) & USB_ERRSTAT_CRC16_MASK) + +#define USB_ERRSTAT_DFN8_MASK (0x8U) +#define USB_ERRSTAT_DFN8_SHIFT (3U) +/*! DFN8 - Data Field Not 8 Bits Flag + * 0b0..Integer number of bytes + * 0b1..Not an integer number of bytes + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ERRSTAT_DFN8(x) (((uint8_t)(((uint8_t)(x)) << USB_ERRSTAT_DFN8_SHIFT)) & USB_ERRSTAT_DFN8_MASK) + +#define USB_ERRSTAT_BTOERR_MASK (0x10U) +#define USB_ERRSTAT_BTOERR_SHIFT (4U) +/*! BTOERR - Bus Turnaround Timeout Error Flag + * 0b0..Not timed out + * 0b1..Timed out + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ERRSTAT_BTOERR(x) (((uint8_t)(((uint8_t)(x)) << USB_ERRSTAT_BTOERR_SHIFT)) & USB_ERRSTAT_BTOERR_MASK) + +#define USB_ERRSTAT_DMAERR_MASK (0x20U) +#define USB_ERRSTAT_DMAERR_SHIFT (5U) +/*! DMAERR - DMA Access Error Flag + * 0b0..Interrupt did not occur + * 0b1..Interrupt occurred + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ERRSTAT_DMAERR(x) (((uint8_t)(((uint8_t)(x)) << USB_ERRSTAT_DMAERR_SHIFT)) & USB_ERRSTAT_DMAERR_MASK) + +#define USB_ERRSTAT_OWNERR_MASK (0x40U) +#define USB_ERRSTAT_OWNERR_SHIFT (6U) +/*! OWNERR - BD Unavailable Error Flag + * 0b0..Interrupt did not occur + * 0b1..Interrupt occurred + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ERRSTAT_OWNERR(x) (((uint8_t)(((uint8_t)(x)) << USB_ERRSTAT_OWNERR_SHIFT)) & USB_ERRSTAT_OWNERR_MASK) + +#define USB_ERRSTAT_BTSERR_MASK (0x80U) +#define USB_ERRSTAT_BTSERR_SHIFT (7U) +/*! BTSERR - Bit Stuff Error Flag + * 0b0..Packet not rejected due to the error + * 0b1..Packet rejected due to the error + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_ERRSTAT_BTSERR(x) (((uint8_t)(((uint8_t)(x)) << USB_ERRSTAT_BTSERR_SHIFT)) & USB_ERRSTAT_BTSERR_MASK) +/*! @} */ + +/*! @name ERREN - Error Interrupt Enable */ +/*! @{ */ + +#define USB_ERREN_PIDERREN_MASK (0x1U) +#define USB_ERREN_PIDERREN_SHIFT (0U) +/*! PIDERREN - PIDERR Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_ERREN_PIDERREN(x) (((uint8_t)(((uint8_t)(x)) << USB_ERREN_PIDERREN_SHIFT)) & USB_ERREN_PIDERREN_MASK) + +#define USB_ERREN_CRC5EOFEN_MASK (0x2U) +#define USB_ERREN_CRC5EOFEN_SHIFT (1U) +/*! CRC5EOFEN - CRC5/EOF Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_ERREN_CRC5EOFEN(x) (((uint8_t)(((uint8_t)(x)) << USB_ERREN_CRC5EOFEN_SHIFT)) & USB_ERREN_CRC5EOFEN_MASK) + +#define USB_ERREN_CRC16EN_MASK (0x4U) +#define USB_ERREN_CRC16EN_SHIFT (2U) +/*! CRC16EN - CRC16 Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_ERREN_CRC16EN(x) (((uint8_t)(((uint8_t)(x)) << USB_ERREN_CRC16EN_SHIFT)) & USB_ERREN_CRC16EN_MASK) + +#define USB_ERREN_DFN8EN_MASK (0x8U) +#define USB_ERREN_DFN8EN_SHIFT (3U) +/*! DFN8EN - DFN8 (Data Field Not Integer Number of Bytes) Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_ERREN_DFN8EN(x) (((uint8_t)(((uint8_t)(x)) << USB_ERREN_DFN8EN_SHIFT)) & USB_ERREN_DFN8EN_MASK) + +#define USB_ERREN_BTOERREN_MASK (0x10U) +#define USB_ERREN_BTOERREN_SHIFT (4U) +/*! BTOERREN - BTOERR (Bus Timeout Error) Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_ERREN_BTOERREN(x) (((uint8_t)(((uint8_t)(x)) << USB_ERREN_BTOERREN_SHIFT)) & USB_ERREN_BTOERREN_MASK) + +#define USB_ERREN_DMAERREN_MASK (0x20U) +#define USB_ERREN_DMAERREN_SHIFT (5U) +/*! DMAERREN - DMAERR Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_ERREN_DMAERREN(x) (((uint8_t)(((uint8_t)(x)) << USB_ERREN_DMAERREN_SHIFT)) & USB_ERREN_DMAERREN_MASK) + +#define USB_ERREN_OWNERREN_MASK (0x40U) +#define USB_ERREN_OWNERREN_SHIFT (6U) +/*! OWNERREN - OWNERR Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_ERREN_OWNERREN(x) (((uint8_t)(((uint8_t)(x)) << USB_ERREN_OWNERREN_SHIFT)) & USB_ERREN_OWNERREN_MASK) + +#define USB_ERREN_BTSERREN_MASK (0x80U) +#define USB_ERREN_BTSERREN_SHIFT (7U) +/*! BTSERREN - BTSERR (Bit Stuff Error) Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_ERREN_BTSERREN(x) (((uint8_t)(((uint8_t)(x)) << USB_ERREN_BTSERREN_SHIFT)) & USB_ERREN_BTSERREN_MASK) +/*! @} */ + +/*! @name STAT - Status */ +/*! @{ */ + +#define USB_STAT_ODD_MASK (0x4U) +#define USB_STAT_ODD_SHIFT (2U) +/*! ODD - Odd Bank + * 0b0..Not in the odd bank + * 0b1..In the odd bank + */ +#define USB_STAT_ODD(x) (((uint8_t)(((uint8_t)(x)) << USB_STAT_ODD_SHIFT)) & USB_STAT_ODD_MASK) + +#define USB_STAT_TX_MASK (0x8U) +#define USB_STAT_TX_SHIFT (3U) +/*! TX - Transmit Indicator + * 0b0..Receive + * 0b1..Transmit + */ +#define USB_STAT_TX(x) (((uint8_t)(((uint8_t)(x)) << USB_STAT_TX_SHIFT)) & USB_STAT_TX_MASK) + +#define USB_STAT_ENDP_MASK (0xF0U) +#define USB_STAT_ENDP_SHIFT (4U) +/*! ENDP - Endpoint address */ +#define USB_STAT_ENDP(x) (((uint8_t)(((uint8_t)(x)) << USB_STAT_ENDP_SHIFT)) & USB_STAT_ENDP_MASK) +/*! @} */ + +/*! @name CTL - Control */ +/*! @{ */ + +#define USB_CTL_USBENSOFEN_MASK (0x1U) +#define USB_CTL_USBENSOFEN_SHIFT (0U) +/*! USBENSOFEN - USB Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_CTL_USBENSOFEN(x) (((uint8_t)(((uint8_t)(x)) << USB_CTL_USBENSOFEN_SHIFT)) & USB_CTL_USBENSOFEN_MASK) + +#define USB_CTL_ODDRST_MASK (0x2U) +#define USB_CTL_ODDRST_SHIFT (1U) +/*! ODDRST - Odd Reset */ +#define USB_CTL_ODDRST(x) (((uint8_t)(((uint8_t)(x)) << USB_CTL_ODDRST_SHIFT)) & USB_CTL_ODDRST_MASK) + +#define USB_CTL_RESUME_MASK (0x4U) +#define USB_CTL_RESUME_SHIFT (2U) +/*! RESUME - Resume */ +#define USB_CTL_RESUME(x) (((uint8_t)(((uint8_t)(x)) << USB_CTL_RESUME_SHIFT)) & USB_CTL_RESUME_MASK) + +#define USB_CTL_HOSTMODEEN_MASK (0x8U) +#define USB_CTL_HOSTMODEEN_SHIFT (3U) +/*! HOSTMODEEN - Host Mode Enable + * 0b0..USBFS operates in Device mode. + * 0b1..USBFS operates in Host mode. In Host mode, USBFS performs USB transactions under the programmed control of the host processor. + */ +#define USB_CTL_HOSTMODEEN(x) (((uint8_t)(((uint8_t)(x)) << USB_CTL_HOSTMODEEN_SHIFT)) & USB_CTL_HOSTMODEEN_MASK) + +#define USB_CTL_RESET_MASK (0x10U) +#define USB_CTL_RESET_SHIFT (4U) +/*! RESET - Reset Signaling Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_CTL_RESET(x) (((uint8_t)(((uint8_t)(x)) << USB_CTL_RESET_SHIFT)) & USB_CTL_RESET_MASK) + +#define USB_CTL_TXSUSPENDTOKENBUSY_MASK (0x20U) +#define USB_CTL_TXSUSPENDTOKENBUSY_SHIFT (5U) +/*! TXSUSPENDTOKENBUSY - TXD Suspend And Token Busy */ +#define USB_CTL_TXSUSPENDTOKENBUSY(x) (((uint8_t)(((uint8_t)(x)) << USB_CTL_TXSUSPENDTOKENBUSY_SHIFT)) & USB_CTL_TXSUSPENDTOKENBUSY_MASK) + +#define USB_CTL_SE0_MASK (0x40U) +#define USB_CTL_SE0_SHIFT (6U) +/*! SE0 - Live USB Single-Ended Zero signal */ +#define USB_CTL_SE0(x) (((uint8_t)(((uint8_t)(x)) << USB_CTL_SE0_SHIFT)) & USB_CTL_SE0_MASK) + +#define USB_CTL_JSTATE_MASK (0x80U) +#define USB_CTL_JSTATE_SHIFT (7U) +/*! JSTATE - Live USB Differential Receiver JSTATE Signal */ +#define USB_CTL_JSTATE(x) (((uint8_t)(((uint8_t)(x)) << USB_CTL_JSTATE_SHIFT)) & USB_CTL_JSTATE_MASK) +/*! @} */ + +/*! @name ADDR - Address */ +/*! @{ */ + +#define USB_ADDR_ADDR_MASK (0x7FU) +#define USB_ADDR_ADDR_SHIFT (0U) +/*! ADDR - USB Address */ +#define USB_ADDR_ADDR(x) (((uint8_t)(((uint8_t)(x)) << USB_ADDR_ADDR_SHIFT)) & USB_ADDR_ADDR_MASK) + +#define USB_ADDR_LSEN_MASK (0x80U) +#define USB_ADDR_LSEN_SHIFT (7U) +/*! LSEN - Low Speed Enable */ +#define USB_ADDR_LSEN(x) (((uint8_t)(((uint8_t)(x)) << USB_ADDR_LSEN_SHIFT)) & USB_ADDR_LSEN_MASK) +/*! @} */ + +/*! @name BDTPAGE1 - BDT Page 1 */ +/*! @{ */ + +#define USB_BDTPAGE1_BDTBA_MASK (0xFEU) +#define USB_BDTPAGE1_BDTBA_SHIFT (1U) +/*! BDTBA - BDT Base Address */ +#define USB_BDTPAGE1_BDTBA(x) (((uint8_t)(((uint8_t)(x)) << USB_BDTPAGE1_BDTBA_SHIFT)) & USB_BDTPAGE1_BDTBA_MASK) +/*! @} */ + +/*! @name FRMNUML - Frame Number Register Low */ +/*! @{ */ + +#define USB_FRMNUML_FRM_MASK (0xFFU) +#define USB_FRMNUML_FRM_SHIFT (0U) +/*! FRM - Frame Number, Bits 0-7 */ +#define USB_FRMNUML_FRM(x) (((uint8_t)(((uint8_t)(x)) << USB_FRMNUML_FRM_SHIFT)) & USB_FRMNUML_FRM_MASK) +/*! @} */ + +/*! @name FRMNUMH - Frame Number Register High */ +/*! @{ */ + +#define USB_FRMNUMH_FRM_MASK (0x7U) +#define USB_FRMNUMH_FRM_SHIFT (0U) +/*! FRM - Frame Number, Bits 8-10 */ +#define USB_FRMNUMH_FRM(x) (((uint8_t)(((uint8_t)(x)) << USB_FRMNUMH_FRM_SHIFT)) & USB_FRMNUMH_FRM_MASK) +/*! @} */ + +/*! @name TOKEN - Token */ +/*! @{ */ + +#define USB_TOKEN_TOKENENDPT_MASK (0xFU) +#define USB_TOKEN_TOKENENDPT_SHIFT (0U) +/*! TOKENENDPT - Token Endpoint Address */ +#define USB_TOKEN_TOKENENDPT(x) (((uint8_t)(((uint8_t)(x)) << USB_TOKEN_TOKENENDPT_SHIFT)) & USB_TOKEN_TOKENENDPT_MASK) + +#define USB_TOKEN_TOKENPID_MASK (0xF0U) +#define USB_TOKEN_TOKENPID_SHIFT (4U) +/*! TOKENPID - Token Type + * 0b0001..OUT token. USBFS performs an OUT (TX) transaction. + * 0b1001..IN token. USBFS performs an IN (RX) transaction. + * 0b1101..SETUP token. USBFS performs a SETUP (TX) transaction + */ +#define USB_TOKEN_TOKENPID(x) (((uint8_t)(((uint8_t)(x)) << USB_TOKEN_TOKENPID_SHIFT)) & USB_TOKEN_TOKENPID_MASK) +/*! @} */ + +/*! @name SOFTHLD - SOF Threshold */ +/*! @{ */ + +#define USB_SOFTHLD_CNT_MASK (0xFFU) +#define USB_SOFTHLD_CNT_SHIFT (0U) +/*! CNT - SOF Count Threshold */ +#define USB_SOFTHLD_CNT(x) (((uint8_t)(((uint8_t)(x)) << USB_SOFTHLD_CNT_SHIFT)) & USB_SOFTHLD_CNT_MASK) +/*! @} */ + +/*! @name BDTPAGE2 - BDT Page 2 */ +/*! @{ */ + +#define USB_BDTPAGE2_BDTBA_MASK (0xFFU) +#define USB_BDTPAGE2_BDTBA_SHIFT (0U) +/*! BDTBA - BDT Base Address */ +#define USB_BDTPAGE2_BDTBA(x) (((uint8_t)(((uint8_t)(x)) << USB_BDTPAGE2_BDTBA_SHIFT)) & USB_BDTPAGE2_BDTBA_MASK) +/*! @} */ + +/*! @name BDTPAGE3 - BDT Page 3 */ +/*! @{ */ + +#define USB_BDTPAGE3_BDTBA_MASK (0xFFU) +#define USB_BDTPAGE3_BDTBA_SHIFT (0U) +/*! BDTBA - BDT Base Address */ +#define USB_BDTPAGE3_BDTBA(x) (((uint8_t)(((uint8_t)(x)) << USB_BDTPAGE3_BDTBA_SHIFT)) & USB_BDTPAGE3_BDTBA_MASK) +/*! @} */ + +/*! @name ENDPT - Endpoint Control */ +/*! @{ */ + +#define USB_ENDPT_EPHSHK_MASK (0x1U) +#define USB_ENDPT_EPHSHK_SHIFT (0U) +/*! EPHSHK - Endpoint Handshaking Enable */ +#define USB_ENDPT_EPHSHK(x) (((uint8_t)(((uint8_t)(x)) << USB_ENDPT_EPHSHK_SHIFT)) & USB_ENDPT_EPHSHK_MASK) + +#define USB_ENDPT_EPSTALL_MASK (0x2U) +#define USB_ENDPT_EPSTALL_SHIFT (1U) +/*! EPSTALL - Endpoint Stalled */ +#define USB_ENDPT_EPSTALL(x) (((uint8_t)(((uint8_t)(x)) << USB_ENDPT_EPSTALL_SHIFT)) & USB_ENDPT_EPSTALL_MASK) + +#define USB_ENDPT_EPTXEN_MASK (0x4U) +#define USB_ENDPT_EPTXEN_SHIFT (2U) +/*! EPTXEN - Endpoint for TX transfers enable */ +#define USB_ENDPT_EPTXEN(x) (((uint8_t)(((uint8_t)(x)) << USB_ENDPT_EPTXEN_SHIFT)) & USB_ENDPT_EPTXEN_MASK) + +#define USB_ENDPT_EPRXEN_MASK (0x8U) +#define USB_ENDPT_EPRXEN_SHIFT (3U) +/*! EPRXEN - Endpoint for RX transfers enable */ +#define USB_ENDPT_EPRXEN(x) (((uint8_t)(((uint8_t)(x)) << USB_ENDPT_EPRXEN_SHIFT)) & USB_ENDPT_EPRXEN_MASK) + +#define USB_ENDPT_EPCTLDIS_MASK (0x10U) +#define USB_ENDPT_EPCTLDIS_SHIFT (4U) +/*! EPCTLDIS - Control Transfer Disable + * 0b0..Enable + * 0b1..Disable + */ +#define USB_ENDPT_EPCTLDIS(x) (((uint8_t)(((uint8_t)(x)) << USB_ENDPT_EPCTLDIS_SHIFT)) & USB_ENDPT_EPCTLDIS_MASK) + +#define USB_ENDPT_RETRYDIS_MASK (0x40U) +#define USB_ENDPT_RETRYDIS_SHIFT (6U) +/*! RETRYDIS - Retry Disable + * 0b0..Retried NAK'ed transactions in hardware. + * 0b1..Do not retry NAK'ed transactions. When a transaction is NAK'ed, the BDT PID field is updated with the NAK + * PID, and the TOKEN_DNE interrupt becomes 1. + */ +#define USB_ENDPT_RETRYDIS(x) (((uint8_t)(((uint8_t)(x)) << USB_ENDPT_RETRYDIS_SHIFT)) & USB_ENDPT_RETRYDIS_MASK) + +#define USB_ENDPT_HOSTWOHUB_MASK (0x80U) +#define USB_ENDPT_HOSTWOHUB_SHIFT (7U) +/*! HOSTWOHUB - Host Without A Hub + * 0b0..Connected using a hub (USBFS generates PRE_PID as required) + * 0b1..Connected directly to host without a hub, or was used to attach + */ +#define USB_ENDPT_HOSTWOHUB(x) (((uint8_t)(((uint8_t)(x)) << USB_ENDPT_HOSTWOHUB_SHIFT)) & USB_ENDPT_HOSTWOHUB_MASK) +/*! @} */ + +/* The count of USB_ENDPT */ +#define USB_ENDPT_COUNT (16U) + +/*! @name USBCTRL - USB Control */ +/*! @{ */ + +#define USB_USBCTRL_DPDM_LANE_REVERSE_MASK (0x4U) +#define USB_USBCTRL_DPDM_LANE_REVERSE_SHIFT (2U) +/*! DPDM_LANE_REVERSE - DP and DM Lane Reversal Control + * 0b0..Standard USB DP and DM package pin assignment + * 0b1..Reverse roles of USB DP and DM package pins + */ +#define USB_USBCTRL_DPDM_LANE_REVERSE(x) (((uint8_t)(((uint8_t)(x)) << USB_USBCTRL_DPDM_LANE_REVERSE_SHIFT)) & USB_USBCTRL_DPDM_LANE_REVERSE_MASK) + +#define USB_USBCTRL_HOST_LS_EOP_MASK (0x8U) +#define USB_USBCTRL_HOST_LS_EOP_SHIFT (3U) +/*! HOST_LS_EOP - Host-Mode-Only Low-Speed Device EOP Signaling + * 0b0..Full-speed device or a low-speed device through a hub + * 0b1..Directly-connected low-speed device + */ +#define USB_USBCTRL_HOST_LS_EOP(x) (((uint8_t)(((uint8_t)(x)) << USB_USBCTRL_HOST_LS_EOP_SHIFT)) & USB_USBCTRL_HOST_LS_EOP_MASK) + +#define USB_USBCTRL_UARTSEL_MASK (0x10U) +#define USB_USBCTRL_UARTSEL_SHIFT (4U) +/*! UARTSEL - UART Select + * 0b0..USB DP and DM external package pins are used for USB signaling. + * 0b1..USB DP and DM external package pins are used for UART signaling. + */ +#define USB_USBCTRL_UARTSEL(x) (((uint8_t)(((uint8_t)(x)) << USB_USBCTRL_UARTSEL_SHIFT)) & USB_USBCTRL_UARTSEL_MASK) + +#define USB_USBCTRL_UARTCHLS_MASK (0x20U) +#define USB_USBCTRL_UARTCHLS_SHIFT (5U) +/*! UARTCHLS - UART Signal Channel Select + * 0b0..USB DP and DM signals are used as UART TX/RX. + * 0b1..USB DP and DM signals are used as UART RX/TX. + */ +#define USB_USBCTRL_UARTCHLS(x) (((uint8_t)(((uint8_t)(x)) << USB_USBCTRL_UARTCHLS_SHIFT)) & USB_USBCTRL_UARTCHLS_MASK) + +#define USB_USBCTRL_PDE_MASK (0x40U) +#define USB_USBCTRL_PDE_SHIFT (6U) +/*! PDE - Pulldown Enable + * 0b0..Disable on D+ and D- + * 0b1..Enable on D+ and D- + */ +#define USB_USBCTRL_PDE(x) (((uint8_t)(((uint8_t)(x)) << USB_USBCTRL_PDE_SHIFT)) & USB_USBCTRL_PDE_MASK) + +#define USB_USBCTRL_SUSP_MASK (0x80U) +#define USB_USBCTRL_SUSP_SHIFT (7U) +/*! SUSP - Suspend + * 0b0..Not in Suspend state + * 0b1..In Suspend state + */ +#define USB_USBCTRL_SUSP(x) (((uint8_t)(((uint8_t)(x)) << USB_USBCTRL_SUSP_SHIFT)) & USB_USBCTRL_SUSP_MASK) +/*! @} */ + +/*! @name OBSERVE - USB OTG Observe */ +/*! @{ */ + +#define USB_OBSERVE_DMPD_MASK (0x10U) +#define USB_OBSERVE_DMPD_SHIFT (4U) +/*! DMPD - D- Pulldown + * 0b0..Disabled + * 0b1..Enabled + */ +#define USB_OBSERVE_DMPD(x) (((uint8_t)(((uint8_t)(x)) << USB_OBSERVE_DMPD_SHIFT)) & USB_OBSERVE_DMPD_MASK) + +#define USB_OBSERVE_DPPD_MASK (0x40U) +#define USB_OBSERVE_DPPD_SHIFT (6U) +/*! DPPD - D+ Pulldown + * 0b0..Disabled + * 0b1..Enabled + */ +#define USB_OBSERVE_DPPD(x) (((uint8_t)(((uint8_t)(x)) << USB_OBSERVE_DPPD_SHIFT)) & USB_OBSERVE_DPPD_MASK) + +#define USB_OBSERVE_DPPU_MASK (0x80U) +#define USB_OBSERVE_DPPU_SHIFT (7U) +/*! DPPU - D+ Pullup + * 0b0..Disabled + * 0b1..Enabled + */ +#define USB_OBSERVE_DPPU(x) (((uint8_t)(((uint8_t)(x)) << USB_OBSERVE_DPPU_SHIFT)) & USB_OBSERVE_DPPU_MASK) +/*! @} */ + +/*! @name CONTROL - USB OTG Control */ +/*! @{ */ + +#define USB_CONTROL_VBUS_SOURCE_SEL_MASK (0x1U) +#define USB_CONTROL_VBUS_SOURCE_SEL_SHIFT (0U) +/*! VBUS_SOURCE_SEL - VBUS Monitoring Source Select + * 0b0..Reserved + * 0b1..Resistive divider attached to a GPIO pin + */ +#define USB_CONTROL_VBUS_SOURCE_SEL(x) (((uint8_t)(((uint8_t)(x)) << USB_CONTROL_VBUS_SOURCE_SEL_SHIFT)) & USB_CONTROL_VBUS_SOURCE_SEL_MASK) + +#define USB_CONTROL_SESS_VLD_MASK (0x2U) +#define USB_CONTROL_SESS_VLD_SHIFT (1U) +/*! SESS_VLD - VBUS Session Valid status + * 0b1..Above + * 0b0..Below + */ +#define USB_CONTROL_SESS_VLD(x) (((uint8_t)(((uint8_t)(x)) << USB_CONTROL_SESS_VLD_SHIFT)) & USB_CONTROL_SESS_VLD_MASK) + +#define USB_CONTROL_DPPULLUPNONOTG_MASK (0x10U) +#define USB_CONTROL_DPPULLUPNONOTG_SHIFT (4U) +/*! DPPULLUPNONOTG - DP Pullup in Non-OTG Device Mode + * 0b0..Disable + * 0b1..Enabled + */ +#define USB_CONTROL_DPPULLUPNONOTG(x) (((uint8_t)(((uint8_t)(x)) << USB_CONTROL_DPPULLUPNONOTG_SHIFT)) & USB_CONTROL_DPPULLUPNONOTG_MASK) +/*! @} */ + +/*! @name USBTRC0 - USB Transceiver Control 0 */ +/*! @{ */ + +#define USB_USBTRC0_USB_RESUME_INT_MASK (0x1U) +#define USB_USBTRC0_USB_RESUME_INT_SHIFT (0U) +/*! USB_RESUME_INT - USB Asynchronous Interrupt + * 0b0..Not generated + * 0b1..Generated because of the USB asynchronous interrupt + */ +#define USB_USBTRC0_USB_RESUME_INT(x) (((uint8_t)(((uint8_t)(x)) << USB_USBTRC0_USB_RESUME_INT_SHIFT)) & USB_USBTRC0_USB_RESUME_INT_MASK) + +#define USB_USBTRC0_SYNC_DET_MASK (0x2U) +#define USB_USBTRC0_SYNC_DET_SHIFT (1U) +/*! SYNC_DET - Synchronous USB Interrupt Detect + * 0b0..Not detected + * 0b1..Detected + */ +#define USB_USBTRC0_SYNC_DET(x) (((uint8_t)(((uint8_t)(x)) << USB_USBTRC0_SYNC_DET_SHIFT)) & USB_USBTRC0_SYNC_DET_MASK) + +#define USB_USBTRC0_USB_CLK_RECOVERY_INT_MASK (0x4U) +#define USB_USBTRC0_USB_CLK_RECOVERY_INT_SHIFT (2U) +/*! USB_CLK_RECOVERY_INT - Combined USB Clock Recovery interrupt status */ +#define USB_USBTRC0_USB_CLK_RECOVERY_INT(x) (((uint8_t)(((uint8_t)(x)) << USB_USBTRC0_USB_CLK_RECOVERY_INT_SHIFT)) & USB_USBTRC0_USB_CLK_RECOVERY_INT_MASK) + +#define USB_USBTRC0_VREDG_DET_MASK (0x8U) +#define USB_USBTRC0_VREDG_DET_SHIFT (3U) +/*! VREDG_DET - VREGIN Rising Edge Interrupt Detect + * 0b0..Not detected + * 0b1..Detected + */ +#define USB_USBTRC0_VREDG_DET(x) (((uint8_t)(((uint8_t)(x)) << USB_USBTRC0_VREDG_DET_SHIFT)) & USB_USBTRC0_VREDG_DET_MASK) + +#define USB_USBTRC0_VFEDG_DET_MASK (0x10U) +#define USB_USBTRC0_VFEDG_DET_SHIFT (4U) +/*! VFEDG_DET - VREGIN Falling Edge Interrupt Detect + * 0b0..Not detected + * 0b1..Detected + */ +#define USB_USBTRC0_VFEDG_DET(x) (((uint8_t)(((uint8_t)(x)) << USB_USBTRC0_VFEDG_DET_SHIFT)) & USB_USBTRC0_VFEDG_DET_MASK) + +#define USB_USBTRC0_USBRESMEN_MASK (0x20U) +#define USB_USBTRC0_USBRESMEN_SHIFT (5U) +/*! USBRESMEN - Asynchronous Resume Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_USBTRC0_USBRESMEN(x) (((uint8_t)(((uint8_t)(x)) << USB_USBTRC0_USBRESMEN_SHIFT)) & USB_USBTRC0_USBRESMEN_MASK) + +#define USB_USBTRC0_VREGIN_STS_MASK (0x40U) +#define USB_USBTRC0_VREGIN_STS_SHIFT (6U) +/*! VREGIN_STS - VREGIN Status */ +#define USB_USBTRC0_VREGIN_STS(x) (((uint8_t)(((uint8_t)(x)) << USB_USBTRC0_VREGIN_STS_SHIFT)) & USB_USBTRC0_VREGIN_STS_MASK) + +#define USB_USBTRC0_USBRESET_MASK (0x80U) +#define USB_USBTRC0_USBRESET_SHIFT (7U) +/*! USBRESET - USB Reset + * 0b0..Normal USBFS operation + * 0b1..Returns USBFS to its reset state + */ +#define USB_USBTRC0_USBRESET(x) (((uint8_t)(((uint8_t)(x)) << USB_USBTRC0_USBRESET_SHIFT)) & USB_USBTRC0_USBRESET_MASK) +/*! @} */ + +/*! @name USBFRMADJUST - Frame Adjust */ +/*! @{ */ + +#define USB_USBFRMADJUST_ADJ_MASK (0xFFU) +#define USB_USBFRMADJUST_ADJ_SHIFT (0U) +/*! ADJ - Frame Adjustment */ +#define USB_USBFRMADJUST_ADJ(x) (((uint8_t)(((uint8_t)(x)) << USB_USBFRMADJUST_ADJ_SHIFT)) & USB_USBFRMADJUST_ADJ_MASK) +/*! @} */ + +/*! @name KEEP_ALIVE_CTRL - Keep Alive Mode Control */ +/*! @{ */ + +#define USB_KEEP_ALIVE_CTRL_KEEP_ALIVE_EN_MASK (0x1U) +#define USB_KEEP_ALIVE_CTRL_KEEP_ALIVE_EN_SHIFT (0U) +/*! KEEP_ALIVE_EN - Keep Alive Mode Enable + * 0b0..Everything remains same as before. + * 0b1..USB shall enter USB_KEEP_ALIVE mode after asserting ipg_stop. + */ +#define USB_KEEP_ALIVE_CTRL_KEEP_ALIVE_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_KEEP_ALIVE_CTRL_KEEP_ALIVE_EN_SHIFT)) & USB_KEEP_ALIVE_CTRL_KEEP_ALIVE_EN_MASK) + +#define USB_KEEP_ALIVE_CTRL_OWN_OVERRD_EN_MASK (0x2U) +#define USB_KEEP_ALIVE_CTRL_OWN_OVERRD_EN_SHIFT (1U) +/*! OWN_OVERRD_EN - OWN Bit Override Enable */ +#define USB_KEEP_ALIVE_CTRL_OWN_OVERRD_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_KEEP_ALIVE_CTRL_OWN_OVERRD_EN_SHIFT)) & USB_KEEP_ALIVE_CTRL_OWN_OVERRD_EN_MASK) + +#define USB_KEEP_ALIVE_CTRL_STOP_ACK_DLY_EN_MASK (0x4U) +#define USB_KEEP_ALIVE_CTRL_STOP_ACK_DLY_EN_SHIFT (2U) +/*! STOP_ACK_DLY_EN - Stop Acknowledge Delay Enable + * 0b0..Enter KEEP_ALIVE mode immediately when there is no USB AHB transfer. + * 0b1..Enter KEEP_ALIVE mode until the USB core is idle and there is no USB AHB transfer. + */ +#define USB_KEEP_ALIVE_CTRL_STOP_ACK_DLY_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_KEEP_ALIVE_CTRL_STOP_ACK_DLY_EN_SHIFT)) & USB_KEEP_ALIVE_CTRL_STOP_ACK_DLY_EN_MASK) + +#define USB_KEEP_ALIVE_CTRL_WAKE_REQ_EN_MASK (0x8U) +#define USB_KEEP_ALIVE_CTRL_WAKE_REQ_EN_SHIFT (3U) +/*! WAKE_REQ_EN - Wakeup Request Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_KEEP_ALIVE_CTRL_WAKE_REQ_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_KEEP_ALIVE_CTRL_WAKE_REQ_EN_SHIFT)) & USB_KEEP_ALIVE_CTRL_WAKE_REQ_EN_MASK) + +#define USB_KEEP_ALIVE_CTRL_WAKE_INT_EN_MASK (0x10U) +#define USB_KEEP_ALIVE_CTRL_WAKE_INT_EN_SHIFT (4U) +/*! WAKE_INT_EN - Wakeup Interrupt Enable */ +#define USB_KEEP_ALIVE_CTRL_WAKE_INT_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_KEEP_ALIVE_CTRL_WAKE_INT_EN_SHIFT)) & USB_KEEP_ALIVE_CTRL_WAKE_INT_EN_MASK) + +#define USB_KEEP_ALIVE_CTRL_KEEP_ALIVE_STS_MASK (0x40U) +#define USB_KEEP_ALIVE_CTRL_KEEP_ALIVE_STS_SHIFT (6U) +/*! KEEP_ALIVE_STS - Keep Alive Status + * 0b0..Not in Keep Alive mode + * 0b1..In Keep Alive mode + */ +#define USB_KEEP_ALIVE_CTRL_KEEP_ALIVE_STS(x) (((uint8_t)(((uint8_t)(x)) << USB_KEEP_ALIVE_CTRL_KEEP_ALIVE_STS_SHIFT)) & USB_KEEP_ALIVE_CTRL_KEEP_ALIVE_STS_MASK) + +#define USB_KEEP_ALIVE_CTRL_WAKE_INT_STS_MASK (0x80U) +#define USB_KEEP_ALIVE_CTRL_WAKE_INT_STS_SHIFT (7U) +/*! WAKE_INT_STS - Wakeup Interrupt Status Flag + * 0b0..Interrupt did not occur + * 0b1..Interrupt occurred + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_KEEP_ALIVE_CTRL_WAKE_INT_STS(x) (((uint8_t)(((uint8_t)(x)) << USB_KEEP_ALIVE_CTRL_WAKE_INT_STS_SHIFT)) & USB_KEEP_ALIVE_CTRL_WAKE_INT_STS_MASK) +/*! @} */ + +/*! @name KEEP_ALIVE_WKCTRL - Keep Alive Mode Wakeup Control */ +/*! @{ */ + +#define USB_KEEP_ALIVE_WKCTRL_WAKE_ON_THIS_MASK (0xFU) +#define USB_KEEP_ALIVE_WKCTRL_WAKE_ON_THIS_SHIFT (0U) +/*! WAKE_ON_THIS - Token PID for the wakeup request + * 0b0001..Wake up after receiving OUT or SETUP token packet. + * 0b1101..Wake up after receiving SETUP token packet. All other values are reserved. + */ +#define USB_KEEP_ALIVE_WKCTRL_WAKE_ON_THIS(x) (((uint8_t)(((uint8_t)(x)) << USB_KEEP_ALIVE_WKCTRL_WAKE_ON_THIS_SHIFT)) & USB_KEEP_ALIVE_WKCTRL_WAKE_ON_THIS_MASK) + +#define USB_KEEP_ALIVE_WKCTRL_WAKE_ENDPT_MASK (0xF0U) +#define USB_KEEP_ALIVE_WKCTRL_WAKE_ENDPT_SHIFT (4U) +/*! WAKE_ENDPT - Endpoint address for the wakeup request */ +#define USB_KEEP_ALIVE_WKCTRL_WAKE_ENDPT(x) (((uint8_t)(((uint8_t)(x)) << USB_KEEP_ALIVE_WKCTRL_WAKE_ENDPT_SHIFT)) & USB_KEEP_ALIVE_WKCTRL_WAKE_ENDPT_MASK) +/*! @} */ + +/*! @name MISCCTRL - Miscellaneous Control */ +/*! @{ */ + +#define USB_MISCCTRL_SOFDYNTHLD_MASK (0x1U) +#define USB_MISCCTRL_SOFDYNTHLD_SHIFT (0U) +/*! SOFDYNTHLD - Dynamic SOF Threshold Compare mode + * 0b0..When the byte-times SOF threshold is reached + * 0b1..When 8 byte-times SOF threshold is reached or overstepped + */ +#define USB_MISCCTRL_SOFDYNTHLD(x) (((uint8_t)(((uint8_t)(x)) << USB_MISCCTRL_SOFDYNTHLD_SHIFT)) & USB_MISCCTRL_SOFDYNTHLD_MASK) + +#define USB_MISCCTRL_SOFBUSSET_MASK (0x2U) +#define USB_MISCCTRL_SOFBUSSET_SHIFT (1U) +/*! SOFBUSSET - SOF_TOK Interrupt Generation Mode Select + * 0b0..According to the SOF threshold value + * 0b1..When the SOF counter reaches 0 + */ +#define USB_MISCCTRL_SOFBUSSET(x) (((uint8_t)(((uint8_t)(x)) << USB_MISCCTRL_SOFBUSSET_SHIFT)) & USB_MISCCTRL_SOFBUSSET_MASK) + +#define USB_MISCCTRL_OWNERRISODIS_MASK (0x4U) +#define USB_MISCCTRL_OWNERRISODIS_SHIFT (2U) +/*! OWNERRISODIS - OWN Error Detect for ISO IN and ISO OUT Disable + * 0b0..Enable + * 0b1..Disable + */ +#define USB_MISCCTRL_OWNERRISODIS(x) (((uint8_t)(((uint8_t)(x)) << USB_MISCCTRL_OWNERRISODIS_SHIFT)) & USB_MISCCTRL_OWNERRISODIS_MASK) + +#define USB_MISCCTRL_VREDG_EN_MASK (0x8U) +#define USB_MISCCTRL_VREDG_EN_SHIFT (3U) +/*! VREDG_EN - VREGIN Rising Edge Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_MISCCTRL_VREDG_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_MISCCTRL_VREDG_EN_SHIFT)) & USB_MISCCTRL_VREDG_EN_MASK) + +#define USB_MISCCTRL_VFEDG_EN_MASK (0x10U) +#define USB_MISCCTRL_VFEDG_EN_SHIFT (4U) +/*! VFEDG_EN - VREGIN Falling Edge Interrupt Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_MISCCTRL_VFEDG_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_MISCCTRL_VFEDG_EN_SHIFT)) & USB_MISCCTRL_VFEDG_EN_MASK) + +#define USB_MISCCTRL_STL_ADJ_EN_MASK (0x80U) +#define USB_MISCCTRL_STL_ADJ_EN_SHIFT (7U) +/*! STL_ADJ_EN - USB Peripheral Mode Stall Adjust Enable + * 0b0..If ENDPTn[END_STALL] = 1, both IN and OUT directions for the associated endpoint stalls. + * 0b1..If ENDPTn[END_STALL] = 1, the STALL_xx_DIS registers control which directions for the associated endpoint stalls. + */ +#define USB_MISCCTRL_STL_ADJ_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_MISCCTRL_STL_ADJ_EN_SHIFT)) & USB_MISCCTRL_STL_ADJ_EN_MASK) +/*! @} */ + +/*! @name STALL_IL_DIS - Peripheral Mode Stall Disable for Endpoints 7 to 0 in IN Direction */ +/*! @{ */ + +#define USB_STALL_IL_DIS_STALL_I_DIS0_MASK (0x1U) +#define USB_STALL_IL_DIS_STALL_I_DIS0_SHIFT (0U) +/*! STALL_I_DIS0 - Disable Endpoint 0 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IL_DIS_STALL_I_DIS0(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IL_DIS_STALL_I_DIS0_SHIFT)) & USB_STALL_IL_DIS_STALL_I_DIS0_MASK) + +#define USB_STALL_IL_DIS_STALL_I_DIS1_MASK (0x2U) +#define USB_STALL_IL_DIS_STALL_I_DIS1_SHIFT (1U) +/*! STALL_I_DIS1 - Disable Endpoint 1 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IL_DIS_STALL_I_DIS1(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IL_DIS_STALL_I_DIS1_SHIFT)) & USB_STALL_IL_DIS_STALL_I_DIS1_MASK) + +#define USB_STALL_IL_DIS_STALL_I_DIS2_MASK (0x4U) +#define USB_STALL_IL_DIS_STALL_I_DIS2_SHIFT (2U) +/*! STALL_I_DIS2 - Disable Endpoint 2 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IL_DIS_STALL_I_DIS2(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IL_DIS_STALL_I_DIS2_SHIFT)) & USB_STALL_IL_DIS_STALL_I_DIS2_MASK) + +#define USB_STALL_IL_DIS_STALL_I_DIS3_MASK (0x8U) +#define USB_STALL_IL_DIS_STALL_I_DIS3_SHIFT (3U) +/*! STALL_I_DIS3 - Disable Endpoint 3 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IL_DIS_STALL_I_DIS3(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IL_DIS_STALL_I_DIS3_SHIFT)) & USB_STALL_IL_DIS_STALL_I_DIS3_MASK) + +#define USB_STALL_IL_DIS_STALL_I_DIS4_MASK (0x10U) +#define USB_STALL_IL_DIS_STALL_I_DIS4_SHIFT (4U) +/*! STALL_I_DIS4 - Disable Endpoint 4 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IL_DIS_STALL_I_DIS4(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IL_DIS_STALL_I_DIS4_SHIFT)) & USB_STALL_IL_DIS_STALL_I_DIS4_MASK) + +#define USB_STALL_IL_DIS_STALL_I_DIS5_MASK (0x20U) +#define USB_STALL_IL_DIS_STALL_I_DIS5_SHIFT (5U) +/*! STALL_I_DIS5 - Disable Endpoint 5 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IL_DIS_STALL_I_DIS5(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IL_DIS_STALL_I_DIS5_SHIFT)) & USB_STALL_IL_DIS_STALL_I_DIS5_MASK) + +#define USB_STALL_IL_DIS_STALL_I_DIS6_MASK (0x40U) +#define USB_STALL_IL_DIS_STALL_I_DIS6_SHIFT (6U) +/*! STALL_I_DIS6 - Disable Endpoint 6 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IL_DIS_STALL_I_DIS6(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IL_DIS_STALL_I_DIS6_SHIFT)) & USB_STALL_IL_DIS_STALL_I_DIS6_MASK) + +#define USB_STALL_IL_DIS_STALL_I_DIS7_MASK (0x80U) +#define USB_STALL_IL_DIS_STALL_I_DIS7_SHIFT (7U) +/*! STALL_I_DIS7 - Disable Endpoint 7 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IL_DIS_STALL_I_DIS7(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IL_DIS_STALL_I_DIS7_SHIFT)) & USB_STALL_IL_DIS_STALL_I_DIS7_MASK) +/*! @} */ + +/*! @name STALL_IH_DIS - Peripheral Mode Stall Disable for Endpoints 15 to 8 in IN Direction */ +/*! @{ */ + +#define USB_STALL_IH_DIS_STALL_I_DIS8_MASK (0x1U) +#define USB_STALL_IH_DIS_STALL_I_DIS8_SHIFT (0U) +/*! STALL_I_DIS8 - Disable Endpoint 8 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IH_DIS_STALL_I_DIS8(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IH_DIS_STALL_I_DIS8_SHIFT)) & USB_STALL_IH_DIS_STALL_I_DIS8_MASK) + +#define USB_STALL_IH_DIS_STALL_I_DIS9_MASK (0x2U) +#define USB_STALL_IH_DIS_STALL_I_DIS9_SHIFT (1U) +/*! STALL_I_DIS9 - Disable Endpoint 9 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IH_DIS_STALL_I_DIS9(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IH_DIS_STALL_I_DIS9_SHIFT)) & USB_STALL_IH_DIS_STALL_I_DIS9_MASK) + +#define USB_STALL_IH_DIS_STALL_I_DIS10_MASK (0x4U) +#define USB_STALL_IH_DIS_STALL_I_DIS10_SHIFT (2U) +/*! STALL_I_DIS10 - Disable Endpoint 10 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IH_DIS_STALL_I_DIS10(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IH_DIS_STALL_I_DIS10_SHIFT)) & USB_STALL_IH_DIS_STALL_I_DIS10_MASK) + +#define USB_STALL_IH_DIS_STALL_I_DIS11_MASK (0x8U) +#define USB_STALL_IH_DIS_STALL_I_DIS11_SHIFT (3U) +/*! STALL_I_DIS11 - Disable Endpoint 11 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IH_DIS_STALL_I_DIS11(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IH_DIS_STALL_I_DIS11_SHIFT)) & USB_STALL_IH_DIS_STALL_I_DIS11_MASK) + +#define USB_STALL_IH_DIS_STALL_I_DIS12_MASK (0x10U) +#define USB_STALL_IH_DIS_STALL_I_DIS12_SHIFT (4U) +/*! STALL_I_DIS12 - Disable Endpoint 12 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IH_DIS_STALL_I_DIS12(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IH_DIS_STALL_I_DIS12_SHIFT)) & USB_STALL_IH_DIS_STALL_I_DIS12_MASK) + +#define USB_STALL_IH_DIS_STALL_I_DIS13_MASK (0x20U) +#define USB_STALL_IH_DIS_STALL_I_DIS13_SHIFT (5U) +/*! STALL_I_DIS13 - Disable Endpoint 13 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IH_DIS_STALL_I_DIS13(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IH_DIS_STALL_I_DIS13_SHIFT)) & USB_STALL_IH_DIS_STALL_I_DIS13_MASK) + +#define USB_STALL_IH_DIS_STALL_I_DIS14_MASK (0x40U) +#define USB_STALL_IH_DIS_STALL_I_DIS14_SHIFT (6U) +/*! STALL_I_DIS14 - Disable Endpoint 14 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IH_DIS_STALL_I_DIS14(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IH_DIS_STALL_I_DIS14_SHIFT)) & USB_STALL_IH_DIS_STALL_I_DIS14_MASK) + +#define USB_STALL_IH_DIS_STALL_I_DIS15_MASK (0x80U) +#define USB_STALL_IH_DIS_STALL_I_DIS15_SHIFT (7U) +/*! STALL_I_DIS15 - Disable Endpoint 15 IN Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_IH_DIS_STALL_I_DIS15(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_IH_DIS_STALL_I_DIS15_SHIFT)) & USB_STALL_IH_DIS_STALL_I_DIS15_MASK) +/*! @} */ + +/*! @name STALL_OL_DIS - Peripheral Mode Stall Disable for Endpoints 7 to 0 in OUT Direction */ +/*! @{ */ + +#define USB_STALL_OL_DIS_STALL_O_DIS0_MASK (0x1U) +#define USB_STALL_OL_DIS_STALL_O_DIS0_SHIFT (0U) +/*! STALL_O_DIS0 - Disable Endpoint 0 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OL_DIS_STALL_O_DIS0(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OL_DIS_STALL_O_DIS0_SHIFT)) & USB_STALL_OL_DIS_STALL_O_DIS0_MASK) + +#define USB_STALL_OL_DIS_STALL_O_DIS1_MASK (0x2U) +#define USB_STALL_OL_DIS_STALL_O_DIS1_SHIFT (1U) +/*! STALL_O_DIS1 - Disable Endpoint 1 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OL_DIS_STALL_O_DIS1(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OL_DIS_STALL_O_DIS1_SHIFT)) & USB_STALL_OL_DIS_STALL_O_DIS1_MASK) + +#define USB_STALL_OL_DIS_STALL_O_DIS2_MASK (0x4U) +#define USB_STALL_OL_DIS_STALL_O_DIS2_SHIFT (2U) +/*! STALL_O_DIS2 - Disable Endpoint 2 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OL_DIS_STALL_O_DIS2(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OL_DIS_STALL_O_DIS2_SHIFT)) & USB_STALL_OL_DIS_STALL_O_DIS2_MASK) + +#define USB_STALL_OL_DIS_STALL_O_DIS3_MASK (0x8U) +#define USB_STALL_OL_DIS_STALL_O_DIS3_SHIFT (3U) +/*! STALL_O_DIS3 - Disable Endpoint 3 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OL_DIS_STALL_O_DIS3(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OL_DIS_STALL_O_DIS3_SHIFT)) & USB_STALL_OL_DIS_STALL_O_DIS3_MASK) + +#define USB_STALL_OL_DIS_STALL_O_DIS4_MASK (0x10U) +#define USB_STALL_OL_DIS_STALL_O_DIS4_SHIFT (4U) +/*! STALL_O_DIS4 - Disable Endpoint 4 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OL_DIS_STALL_O_DIS4(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OL_DIS_STALL_O_DIS4_SHIFT)) & USB_STALL_OL_DIS_STALL_O_DIS4_MASK) + +#define USB_STALL_OL_DIS_STALL_O_DIS5_MASK (0x20U) +#define USB_STALL_OL_DIS_STALL_O_DIS5_SHIFT (5U) +/*! STALL_O_DIS5 - Disable Endpoint 5 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OL_DIS_STALL_O_DIS5(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OL_DIS_STALL_O_DIS5_SHIFT)) & USB_STALL_OL_DIS_STALL_O_DIS5_MASK) + +#define USB_STALL_OL_DIS_STALL_O_DIS6_MASK (0x40U) +#define USB_STALL_OL_DIS_STALL_O_DIS6_SHIFT (6U) +/*! STALL_O_DIS6 - Disable Endpoint 6 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OL_DIS_STALL_O_DIS6(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OL_DIS_STALL_O_DIS6_SHIFT)) & USB_STALL_OL_DIS_STALL_O_DIS6_MASK) + +#define USB_STALL_OL_DIS_STALL_O_DIS7_MASK (0x80U) +#define USB_STALL_OL_DIS_STALL_O_DIS7_SHIFT (7U) +/*! STALL_O_DIS7 - Disable Endpoint 7 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OL_DIS_STALL_O_DIS7(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OL_DIS_STALL_O_DIS7_SHIFT)) & USB_STALL_OL_DIS_STALL_O_DIS7_MASK) +/*! @} */ + +/*! @name STALL_OH_DIS - Peripheral Mode Stall Disable for Endpoints 15 to 8 in OUT Direction */ +/*! @{ */ + +#define USB_STALL_OH_DIS_STALL_O_DIS8_MASK (0x1U) +#define USB_STALL_OH_DIS_STALL_O_DIS8_SHIFT (0U) +/*! STALL_O_DIS8 - Disable Endpoint 8 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OH_DIS_STALL_O_DIS8(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OH_DIS_STALL_O_DIS8_SHIFT)) & USB_STALL_OH_DIS_STALL_O_DIS8_MASK) + +#define USB_STALL_OH_DIS_STALL_O_DIS9_MASK (0x2U) +#define USB_STALL_OH_DIS_STALL_O_DIS9_SHIFT (1U) +/*! STALL_O_DIS9 - Disable Endpoint 9 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OH_DIS_STALL_O_DIS9(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OH_DIS_STALL_O_DIS9_SHIFT)) & USB_STALL_OH_DIS_STALL_O_DIS9_MASK) + +#define USB_STALL_OH_DIS_STALL_O_DIS10_MASK (0x4U) +#define USB_STALL_OH_DIS_STALL_O_DIS10_SHIFT (2U) +/*! STALL_O_DIS10 - Disable Endpoint 10 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OH_DIS_STALL_O_DIS10(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OH_DIS_STALL_O_DIS10_SHIFT)) & USB_STALL_OH_DIS_STALL_O_DIS10_MASK) + +#define USB_STALL_OH_DIS_STALL_O_DIS11_MASK (0x8U) +#define USB_STALL_OH_DIS_STALL_O_DIS11_SHIFT (3U) +/*! STALL_O_DIS11 - Disable Endpoint 11 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OH_DIS_STALL_O_DIS11(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OH_DIS_STALL_O_DIS11_SHIFT)) & USB_STALL_OH_DIS_STALL_O_DIS11_MASK) + +#define USB_STALL_OH_DIS_STALL_O_DIS12_MASK (0x10U) +#define USB_STALL_OH_DIS_STALL_O_DIS12_SHIFT (4U) +/*! STALL_O_DIS12 - Disable endpoint 12 OUT direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OH_DIS_STALL_O_DIS12(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OH_DIS_STALL_O_DIS12_SHIFT)) & USB_STALL_OH_DIS_STALL_O_DIS12_MASK) + +#define USB_STALL_OH_DIS_STALL_O_DIS13_MASK (0x20U) +#define USB_STALL_OH_DIS_STALL_O_DIS13_SHIFT (5U) +/*! STALL_O_DIS13 - Disable Endpoint 13 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OH_DIS_STALL_O_DIS13(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OH_DIS_STALL_O_DIS13_SHIFT)) & USB_STALL_OH_DIS_STALL_O_DIS13_MASK) + +#define USB_STALL_OH_DIS_STALL_O_DIS14_MASK (0x40U) +#define USB_STALL_OH_DIS_STALL_O_DIS14_SHIFT (6U) +/*! STALL_O_DIS14 - Disable Endpoint 14 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OH_DIS_STALL_O_DIS14(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OH_DIS_STALL_O_DIS14_SHIFT)) & USB_STALL_OH_DIS_STALL_O_DIS14_MASK) + +#define USB_STALL_OH_DIS_STALL_O_DIS15_MASK (0x80U) +#define USB_STALL_OH_DIS_STALL_O_DIS15_SHIFT (7U) +/*! STALL_O_DIS15 - Disable Endpoint 15 OUT Direction + * 0b0..Enable + * 0b1..Disable + */ +#define USB_STALL_OH_DIS_STALL_O_DIS15(x) (((uint8_t)(((uint8_t)(x)) << USB_STALL_OH_DIS_STALL_O_DIS15_SHIFT)) & USB_STALL_OH_DIS_STALL_O_DIS15_MASK) +/*! @} */ + +/*! @name CLK_RECOVER_CTRL - USB Clock Recovery Control */ +/*! @{ */ + +#define USB_CLK_RECOVER_CTRL_TRIM_INIT_VAL_SEL_MASK (0x8U) +#define USB_CLK_RECOVER_CTRL_TRIM_INIT_VAL_SEL_SHIFT (3U) +/*! TRIM_INIT_VAL_SEL - Selects the source for the initial FIRC trim fine value used after a reset. + * 0b0..Mid-scale + * 0b1..IFR + */ +#define USB_CLK_RECOVER_CTRL_TRIM_INIT_VAL_SEL(x) (((uint8_t)(((uint8_t)(x)) << USB_CLK_RECOVER_CTRL_TRIM_INIT_VAL_SEL_SHIFT)) & USB_CLK_RECOVER_CTRL_TRIM_INIT_VAL_SEL_MASK) + +#define USB_CLK_RECOVER_CTRL_RESTART_IFRTRIM_EN_MASK (0x20U) +#define USB_CLK_RECOVER_CTRL_RESTART_IFRTRIM_EN_SHIFT (5U) +/*! RESTART_IFRTRIM_EN - Restart from IFR Trim Value + * 0b0..Trim fine adjustment always works based on the previous updated trim fine value. + * 0b1..Trim fine restarts from the IFR trim value whenever you detect bus_reset or bus_resume or deassert module enable. + */ +#define USB_CLK_RECOVER_CTRL_RESTART_IFRTRIM_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_CLK_RECOVER_CTRL_RESTART_IFRTRIM_EN_SHIFT)) & USB_CLK_RECOVER_CTRL_RESTART_IFRTRIM_EN_MASK) + +#define USB_CLK_RECOVER_CTRL_RESET_RESUME_ROUGH_EN_MASK (0x40U) +#define USB_CLK_RECOVER_CTRL_RESET_RESUME_ROUGH_EN_SHIFT (6U) +/*! RESET_RESUME_ROUGH_EN - Reset or Resume to Rough Phase Enable + * 0b0..Always works in tracking phase after the first time rough phase, to track transition. + * 0b1..Go back to rough stage whenever a bus reset or bus resume occurs. + */ +#define USB_CLK_RECOVER_CTRL_RESET_RESUME_ROUGH_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_CLK_RECOVER_CTRL_RESET_RESUME_ROUGH_EN_SHIFT)) & USB_CLK_RECOVER_CTRL_RESET_RESUME_ROUGH_EN_MASK) + +#define USB_CLK_RECOVER_CTRL_CLOCK_RECOVER_EN_MASK (0x80U) +#define USB_CLK_RECOVER_CTRL_CLOCK_RECOVER_EN_SHIFT (7U) +/*! CLOCK_RECOVER_EN - Crystal-Less USB Enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_CLK_RECOVER_CTRL_CLOCK_RECOVER_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_CLK_RECOVER_CTRL_CLOCK_RECOVER_EN_SHIFT)) & USB_CLK_RECOVER_CTRL_CLOCK_RECOVER_EN_MASK) +/*! @} */ + +/*! @name CLK_RECOVER_IRC_EN - FIRC Oscillator Enable */ +/*! @{ */ + +#define USB_CLK_RECOVER_IRC_EN_IRC_EN_MASK (0x2U) +#define USB_CLK_RECOVER_IRC_EN_IRC_EN_SHIFT (1U) +/*! IRC_EN - Fast IRC enable + * 0b0..Disable + * 0b1..Enable + */ +#define USB_CLK_RECOVER_IRC_EN_IRC_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_CLK_RECOVER_IRC_EN_IRC_EN_SHIFT)) & USB_CLK_RECOVER_IRC_EN_IRC_EN_MASK) +/*! @} */ + +/*! @name CLK_RECOVER_INT_EN - Clock Recovery Combined Interrupt Enable */ +/*! @{ */ + +#define USB_CLK_RECOVER_INT_EN_OVF_ERROR_EN_MASK (0x10U) +#define USB_CLK_RECOVER_INT_EN_OVF_ERROR_EN_SHIFT (4U) +/*! OVF_ERROR_EN - Overflow error interrupt enable + * 0b0..The interrupt is masked + * 0b1..The interrupt is enabled + */ +#define USB_CLK_RECOVER_INT_EN_OVF_ERROR_EN(x) (((uint8_t)(((uint8_t)(x)) << USB_CLK_RECOVER_INT_EN_OVF_ERROR_EN_SHIFT)) & USB_CLK_RECOVER_INT_EN_OVF_ERROR_EN_MASK) +/*! @} */ + +/*! @name CLK_RECOVER_INT_STATUS - Clock Recovery Separated Interrupt Status */ +/*! @{ */ + +#define USB_CLK_RECOVER_INT_STATUS_OVF_ERROR_MASK (0x10U) +#define USB_CLK_RECOVER_INT_STATUS_OVF_ERROR_SHIFT (4U) +/*! OVF_ERROR - Overflow Error Interrupt Status Flag + * 0b0..Interrupt did not occur + * 0b1..Unmasked interrupt occurred + * 0b0..No effect + * 0b1..Clear the flag + */ +#define USB_CLK_RECOVER_INT_STATUS_OVF_ERROR(x) (((uint8_t)(((uint8_t)(x)) << USB_CLK_RECOVER_INT_STATUS_OVF_ERROR_SHIFT)) & USB_CLK_RECOVER_INT_STATUS_OVF_ERROR_MASK) +/*! @} */ + +/*! + * @} + */ +/* end of group USB_Register_Masks */ + +/*! + * @brief This type of structure instance is used to implement the buffer descriptor for USB. + */ +typedef struct +{ + union { + uint32_t head; /*!< Head. */ + struct + { + uint32_t reserved0 : 2; /*!< RESEVED. */ + uint32_t bdt_stall : 1; /*!< Stall. */ + uint32_t dts : 1; /*!< Data shift sync. */ + uint32_t ninc : 1; /*!< DMA addr cannot increasing. */ + uint32_t keep : 1; /*!< Keep BD held by USB. */ + uint32_t data : 1; /*!< DATA0 or DATA1. */ + uint32_t own : 1; /*!< Owner, 0 is CPU, 1 is USB. */ + uint32_t reserved1 : 8; /*!< RESEVED. */ + uint32_t bc : 10; /*!< Packet size. */ + uint32_t reserved2 : 6; /*!< RESEVED. */ + }; + struct + { + uint32_t reserved3 : 2; /*!< RESEVED. */ + uint32_t tok_pid : 4; /*!< Token pid. */ + uint32_t reserved4 : 26; /*!< RESEVED. */ + }; + }; + uint32_t addr; /*!< Buffer addr. */ +} kinetis_bd_t; + +/*! + * @brief This type of structure instance is used to implement the buffer descriptor table for USB. + */ +typedef union { + kinetis_bd_t table[16][2][2]; /*!< [EndPoint] [Direction] [Odd_Even]. */ + uint8_t buffer[512]; /*!< buffer. */ +} kinetis_bd_table_t; + +/** + * @brief USBFS TokenPid type. + */ +typedef enum { + USB_TOKEN_PID_OUT = 0x1u, /*!< USB Token Pid: OUT. */ + USB_TOKEN_PID_IN = 0x9u, /*!< USB Token Pid: IN. */ + USB_TOKEN_PID_SETUP = 0xDu, /*!< USB Token Pid: SETUP. */ + USB_TOKEN_PID_DATA0 = 0x03, /*!< USB Token Pid: DATA0. */ + USB_TOKEN_PID_DATA1 = 0x0B, /*!< USB Token Pid: DATA1. */ + USB_TOKEN_PID_ACK = 0x02, /*!< USB Token Pid: ACK. */ + USB_TOKEN_PID_STALL = 0x0E, /*!< USB Token Pid: STALL. */ + USB_TOKEN_PID_NAK = 0x0A, /*!< USB Token Pid: NAK. */ + USB_TOKEN_PID_BUSTIMEOUT = 0x00, /*!< USB Token Pid: BUSTO. */ + USB_TOKEN_PID_ERR = 0x0f, /*!< USB Token Pid: ERR. */ +} USB_TOKEN_PID_Type; + +typedef struct { + KINETIS_TypeDef base; + __IO uint8_t USBCTRL; /**< USB Control, offset: 0x100 */ + uint8_t RESERVED_22[3]; + __I uint8_t OBSERVE; /**< USB OTG Observe, offset: 0x104 */ + uint8_t RESERVED_23[3]; + __IO uint8_t CONTROL; /**< USB OTG Control, offset: 0x108 */ + uint8_t RESERVED_24[3]; + __IO uint8_t USBTRC0; /**< USB Transceiver Control 0, offset: 0x10C */ + uint8_t RESERVED_25[7]; + __IO uint8_t USBFRMADJUST; /**< Frame Adjust, offset: 0x114 */ + uint8_t RESERVED_26[15]; + __IO uint8_t KEEP_ALIVE_CTRL; /**< Keep Alive Mode Control, offset: 0x124 */ + uint8_t RESERVED_27[3]; + __IO uint8_t KEEP_ALIVE_WKCTRL; /**< Keep Alive Mode Wakeup Control, offset: 0x128 */ + uint8_t RESERVED_28[3]; + __IO uint8_t MISCCTRL; /**< Miscellaneous Control, offset: 0x12C */ + uint8_t RESERVED_29[3]; + __IO uint8_t STALL_IL_DIS; /**< Peripheral Mode Stall Disable for Endpoints 7 to 0 in IN Direction, offset: 0x130 */ + uint8_t RESERVED_30[3]; + __IO uint8_t STALL_IH_DIS; /**< Peripheral Mode Stall Disable for Endpoints 15 to 8 in IN Direction, offset: 0x134 */ + uint8_t RESERVED_31[3]; + __IO uint8_t STALL_OL_DIS; /**< Peripheral Mode Stall Disable for Endpoints 7 to 0 in OUT Direction, offset: 0x138 */ + uint8_t RESERVED_32[3]; + __IO uint8_t STALL_OH_DIS; /**< Peripheral Mode Stall Disable for Endpoints 15 to 8 in OUT Direction, offset: 0x13C */ + uint8_t RESERVED_33[3]; + __IO uint8_t CLK_RECOVER_CTRL; /**< USB Clock Recovery Control, offset: 0x140 */ + uint8_t RESERVED_34[3]; + __IO uint8_t CLK_RECOVER_IRC_EN; /**< FIRC Oscillator Enable, offset: 0x144 */ + uint8_t RESERVED_35[15]; + __IO uint8_t CLK_RECOVER_INT_EN; /**< Clock Recovery Combined Interrupt Enable, offset: 0x154 */ + uint8_t RESERVED_36[7]; + __IO uint8_t CLK_RECOVER_INT_STATUS; /**< Clock Recovery Separated Interrupt Status, offset: 0x15C */ +} KINETIS_MCX_TypeDef; + +void usb_dc_low_level_init(uint8_t busid); +void usb_dc_low_level_deinit(uint8_t busid); + +void usbd_kinetis_delay_ms(uint8_t ms); +#endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_dc_musb.c b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_dc_musb.c index d66ec39..845e583 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_dc_musb.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_dc_musb.c @@ -273,7 +273,7 @@ int usb_dc_init(uint8_t busid) } /* Enable USB interrupts */ - HWREGB(USB_BASE + MUSB_IE_OFFSET) = USB_IE_RESET; + HWREGB(USB_BASE + MUSB_IE_OFFSET) = USB_IE_RESET | USB_IE_SUSPND | USB_IE_RESUME; HWREGH(USB_BASE + MUSB_TXIE_OFFSET) = USB_TXIE_EP0; HWREGH(USB_BASE + MUSB_RXIE_OFFSET) = 0; @@ -296,6 +296,14 @@ int usbd_set_address(uint8_t busid, const uint8_t addr) return 0; } +int usbd_set_remote_wakeup(uint8_t busid) +{ + HWREGB(USB_BASE + MUSB_POWER_OFFSET) |= USB_POWER_RESUME; + usbd_musb_delay_ms(10); + HWREGB(USB_BASE + MUSB_POWER_OFFSET) &= ~USB_POWER_RESUME; + return 0; +} + uint8_t usbd_get_port_speed(uint8_t busid) { uint8_t speed = USB_SPEED_UNKNOWN; @@ -501,6 +509,26 @@ int usbd_ep_clear_stall(uint8_t busid, const uint8_t ep) int usbd_ep_is_stalled(uint8_t busid, const uint8_t ep, uint8_t *stalled) { + uint8_t ep_idx = USB_EP_GET_IDX(ep); + uint8_t old_ep_idx; + + old_ep_idx = musb_get_active_ep(); + musb_set_active_ep(ep_idx); + + if (USB_EP_DIR_IS_OUT(ep)) { + if(HWREGB(USB_BASE + MUSB_IND_RXCSRL_OFFSET) & USB_RXCSRL1_STALL) { + *stalled = 1; + } else { + *stalled = 0; + } + } else { + if(HWREGB(USB_BASE + MUSB_IND_TXCSRL_OFFSET) & USB_TXCSRL1_STALL) { + *stalled = 1; + } else { + *stalled = 0; + } + } + musb_set_active_ep(old_ep_idx); return 0; } @@ -706,9 +734,11 @@ void USBD_IRQHandler(uint8_t busid) } if (is & USB_IS_RESUME) { + usbd_event_resume_handler(0); } if (is & USB_IS_SUSPEND) { + usbd_event_suspend_handler(0); } txis &= HWREGH(USB_BASE + MUSB_TXIE_OFFSET); diff --git a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_bk.c b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_bk.c index 7eb987f..2af1e36 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_bk.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_bk.c @@ -277,6 +277,11 @@ void usb_dc_low_level_deinit(void) sys_drv_dev_clk_pwr_up(CLK_PWR_ID_USB_1, CLK_PWR_CTRL_PWR_DOWN); } +void usbd_musb_delay_ms(uint8_t ms) +{ + /* implement later */ +} + extern void USBH_IRQHandler(uint8_t busid); void USBH_IRQ(void) diff --git a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_es.c b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_es.c index adfc293..b1a8f8e 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_es.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_es.c @@ -50,4 +50,9 @@ uint8_t usbh_get_musb_fifo_cfg(struct musb_fifo_cfg **cfg) uint32_t usb_get_musb_ram_size(void) { return 4096; -} \ No newline at end of file +} + +void usbd_musb_delay_ms(uint8_t ms) +{ + /* implement later */ +} diff --git a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_sunxi.c b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_sunxi.c index 4339230..706a60c 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_sunxi.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_glue_sunxi.c @@ -56,4 +56,9 @@ uint8_t usbh_get_musb_fifo_cfg(struct musb_fifo_cfg **cfg) uint32_t usb_get_musb_ram_size(void) { return 8192; -} \ No newline at end of file +} + +void usbd_musb_delay_ms(uint8_t ms) +{ + /* implement later */ +} diff --git a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_hc_musb.c b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_hc_musb.c index b5d7e36..39c787f 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_hc_musb.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_hc_musb.c @@ -475,10 +475,12 @@ static void musb_pipe_free(struct musb_pipe *pipe) __WEAK void usb_hc_low_level_init(struct usbh_bus *bus) { + (void)bus; } __WEAK void usb_hc_low_level_deinit(struct usbh_bus *bus) { + (void)bus; } int usb_hc_init(struct usbh_bus *bus) @@ -653,6 +655,7 @@ int usbh_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, u } } + status |= (1 << HUB_PORT_FEATURE_POWER); memcpy(buf, &status, 4); break; default: diff --git a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_musb_reg.h b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_musb_reg.h index 885bdd4..1776537 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_musb_reg.h +++ b/rt-thread/components/drivers/usb/cherryusb/port/musb/usb_musb_reg.h @@ -3882,5 +3882,6 @@ struct musb_fifo_cfg { uint8_t usbd_get_musb_fifo_cfg(struct musb_fifo_cfg **cfg); uint8_t usbh_get_musb_fifo_cfg(struct musb_fifo_cfg **cfg); uint32_t usb_get_musb_ram_size(void); +void usbd_musb_delay_ms(uint8_t ms); #endif diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ohci/README.md b/rt-thread/components/drivers/usb/cherryusb/port/ohci/README.md index 34e753c..928eb0c 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ohci/README.md +++ b/rt-thread/components/drivers/usb/cherryusb/port/ohci/README.md @@ -1,18 +1,23 @@ # Note -This OHCI is a companion controller of EHCI. +This OHCI is a companion controller of EHCI. But you can use OHCI only without CONFIG_USB_EHCI_WITH_OHCI definition. + +**And you need to pay for using OHCI driver**. ## Support Chip List ### AllwinnerTech -- F133 +- F133(EHCI + OHCI) ### Nuvoton -- Nuvoton all series +- Nuvoton all series(EHCI + OHCI, OHCI only) ### Artinchip -- d13x, d21x +- d13x, d21x(EHCI + OHCI) +### NXP + +- LPC4X/LPC5X(OHCI only) \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_glue_lpc.c b/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_glue_lpc.c new file mode 100644 index 0000000..51ab781 --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_glue_lpc.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "fsl_device_registers.h" +#include "fsl_power.h" +#include "usbh_core.h" + +#if defined(CONFIG_USB_EHCI_WITH_OHCI) +#error "lpc does not have ehci" +#endif + +void usb_hc_low_level_init(struct usbh_bus *bus) +{ +#if ((defined FSL_FEATURE_SOC_SYSMPU_COUNT) && (FSL_FEATURE_SOC_SYSMPU_COUNT)) + SYSMPU_Enable(SYSMPU, 0); +#endif /* FSL_FEATURE_SOC_SYSMPU_COUNT */ + + NVIC_ClearPendingIRQ(USB0_IRQn); + NVIC_ClearPendingIRQ(USB0_NEEDCLK_IRQn); + + POWER_DisablePD(kPDRUNCFG_PD_USB0_PHY); /*< Turn on USB0 Phy */ + + RESET_PeripheralReset(kUSB0D_RST_SHIFT_RSTn); + RESET_PeripheralReset(kUSB0HSL_RST_SHIFT_RSTn); + RESET_PeripheralReset(kUSB0HMR_RST_SHIFT_RSTn); + + CLOCK_EnableUsbfs0HostClock(kCLOCK_UsbfsSrcPll1, 48000000U); + + NVIC_SetPriority(USB0_IRQn, 3); + EnableIRQ(USB0_IRQn); +} + + +void usb_hc_low_level_deinit(struct usbh_bus *bus) +{ + DisableIRQ(USB0_IRQn); +} + +void USB0_IRQHandler(void) +{ + extern void USBH_IRQHandler(uint8_t busid); + USBH_IRQHandler(0); +} \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_hc_ohci.c b/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_hc_ohci.c index b3d6582..2bec2a0 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_hc_ohci.c +++ b/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_hc_ohci.c @@ -3,19 +3,59 @@ * * SPDX-License-Identifier: Apache-2.0 */ -#include "usb_ohci_priv.h" -#include "usb_ehci_priv.h" +#include "usb_hc_ohci.h" + +/* Frame Interval / Periodic Start. + * + * At 12Mbps, there are 12000 bit time in each 1Msec frame. + */ + +#define OHCI_FMINTERVAL_FI (12000 - 1) +#define OHCI_FMINTERVAL_FSMPS ((6 * (OHCI_FMINTERVAL_FI - 210)) / 7) +#define DEFAULT_FMINTERVAL ((OHCI_FMINTERVAL_FSMPS << OHCI_FMINT_FSMPS_SHIFT) | OHCI_FMINTERVAL_FI) +#define DEFAULT_PERSTART ((OHCI_FMINTERVAL_FI * 9) / 10) + +struct ohci_hcd g_ohci_hcd[CONFIG_USBHOST_MAX_BUS]; + +USB_NOCACHE_RAM_SECTION struct ohci_ed_hw g_ohci_ed_pool[CONFIG_USBHOST_MAX_BUS][CONFIG_USB_OHCI_ED_NUM]; +USB_NOCACHE_RAM_SECTION struct ohci_hcca ohci_hcca[CONFIG_USBHOST_MAX_BUS]; int ohci_init(struct usbh_bus *bus) { volatile uint32_t timeout = 0; uint32_t regval; + struct ohci_ed_hw *ed; + + memset(&g_ohci_hcd[bus->hcd.hcd_id], 0, sizeof(struct ohci_hcd)); + memset(g_ohci_ed_pool[bus->hcd.hcd_id], 0, sizeof(struct ohci_ed_hw) * CONFIG_USB_OHCI_ED_NUM); + + for (uint32_t i = 0; i < 32; i++) { + ohci_hcca[bus->hcd.hcd_id].inttbl[i] = 0; + } + + for (uint8_t index = 0; index < CONFIG_USB_OHCI_ED_NUM; index++) { + ed = &g_ohci_ed_pool[bus->hcd.hcd_id][index]; + if ((uint32_t)&ed->hw % 32) { + USB_LOG_ERR("struct ohci_ed_hw is not align 32\r\n"); + return -USB_ERR_INVAL; + } + for (uint8_t i = 0; i < CONFIG_USB_OHCI_TD_NUM; i++) { + if ((uint32_t)&ed->td_pool[i] % 32) { + USB_LOG_ERR("struct ohci_td_hw is not align 32\r\n"); + return -USB_ERR_INVAL; + } + } + } + + for (uint8_t index = 0; index < CONFIG_USB_OHCI_ED_NUM; index++) { + ed = &g_ohci_ed_pool[bus->hcd.hcd_id][index]; + ed->waitsem = usb_osal_sem_create(0); + } USB_LOG_INFO("OHCI hcrevision:0x%02x\r\n", (unsigned int)OHCI_HCOR->hcrevision); + OHCI_HCOR->hcintdis = OHCI_INT_MIE; OHCI_HCOR->hccontrol = 0; - OHCI_HCOR->hccontrolheaded = 0; - OHCI_HCOR->hcbulkheaded = 0; OHCI_HCOR->hccmdsts = OHCI_CMDST_HCR; while (OHCI_HCOR->hccmdsts & OHCI_CMDST_HCR) { @@ -26,45 +66,39 @@ int ohci_init(struct usbh_bus *bus) } } - /* Frame Interval / Periodic Start. - * - * At 12Mbps, there are 12000 bit time in each 1Msec frame. - */ - -#define BITS_PER_FRAME 12000 -#define FI (BITS_PER_FRAME - 1) -#define FSMPS ((6 * (FI - 210)) / 7) -#define DEFAULT_FMINTERVAL ((FSMPS << OHCI_FMINT_FSMPS_SHIFT) | FI) -#define DEFAULT_PERSTART (((9 * BITS_PER_FRAME) / 10) - 1) - OHCI_HCOR->hcfminterval = DEFAULT_FMINTERVAL; OHCI_HCOR->hcperiodicstart = DEFAULT_PERSTART; + OHCI_HCOR->hclsthreshold = 0x628; - /* Put HC in operational state */ - regval = OHCI_HCOR->hccontrol; - regval &= ~OHCI_CTRL_HCFS_MASK; - regval |= OHCI_CTRL_HCFS_OPER; - OHCI_HCOR->hccontrol = regval; - - /* Set global power in HcRhStatus */ - OHCI_HCOR->hcrhsts = OHCI_RHSTATUS_SGP; - - /* Set HCCA base address */ - OHCI_HCOR->hchcca = 0; + OHCI_HCOR->hccontrolheaded = 0; + OHCI_HCOR->hcbulkheaded = 0; + OHCI_HCOR->hchcca = (uintptr_t)&ohci_hcca[bus->hcd.hcd_id]; /* Clear pending interrupts */ regval = OHCI_HCOR->hcintsts; OHCI_HCOR->hcintsts = regval; - for (uint8_t port = 0; port < g_ehci_hcd[bus->hcd.hcd_id].n_pcc; port++) { - regval = OHCI_HCOR->hcrhportsts[port]; - regval |= OHCI_RHPORTST_PPS; - OHCI_HCOR->hcrhportsts[port] = regval; - } + /* Put HC in operational state */ + regval = OHCI_HCOR->hccontrol; + regval &= ~OHCI_CTRL_CBSR; + regval &= ~OHCI_CTRL_HCFS_MASK; + regval |= OHCI_CTRL_HCFS_OPER; + regval |= OHCI_CTRL_CBSR; + regval |= OHCI_CTRL_CLE; + OHCI_HCOR->hccontrol = regval; + + g_ohci_hcd[bus->hcd.hcd_id].n_ports = OHCI_HCOR->hcrhdescriptora & OHCI_RHDESCA_NDP_MASK; + USB_LOG_INFO("OHCI n_ports:%d\r\n", g_ohci_hcd[bus->hcd.hcd_id].n_ports); + + OHCI_HCOR->hcrhdescriptora &= ~OHCI_RHDESCA_PSM; + OHCI_HCOR->hcrhdescriptora &= ~OHCI_RHDESCA_NPS; + + /* Set global power in HcRhStatus */ + OHCI_HCOR->hcrhsts = OHCI_RHSTATUS_SGP; + usb_osal_msleep(20); /* Enable OHCI interrupts */ - OHCI_HCOR->hcinten = OHCI_INT_SO | OHCI_INT_RD | OHCI_INT_UE | OHCI_INT_OC | - OHCI_INT_WDH | OHCI_INT_RHSC | OHCI_INT_MIE; + OHCI_HCOR->hcinten = OHCI_INT_WDH | OHCI_INT_RHSC | OHCI_INT_MIE; return 0; } @@ -72,15 +106,25 @@ int ohci_init(struct usbh_bus *bus) int ohci_deinit(struct usbh_bus *bus) { uint32_t regval; + struct ohci_ed_hw *ed; /* Disable OHCI interrupts */ - OHCI_HCOR->hcintdis = OHCI_INT_SO | OHCI_INT_RD | OHCI_INT_UE | OHCI_INT_OC | - OHCI_INT_WDH | OHCI_INT_RHSC | OHCI_INT_MIE; + OHCI_HCOR->hcintdis = OHCI_INT_WDH | OHCI_INT_RHSC | OHCI_INT_MIE; - for (uint8_t port = 0; port < g_ehci_hcd[bus->hcd.hcd_id].n_pcc; port++) { - regval = OHCI_HCOR->hcrhportsts[port]; - regval &= ~OHCI_RHPORTST_PPS; - OHCI_HCOR->hcrhportsts[port] = regval; + /* Clear pending interrupts */ + regval = OHCI_HCOR->hcintsts; + OHCI_HCOR->hcintsts = regval; + + OHCI_HCOR->hcrhsts &= ~OHCI_RHSTATUS_SGP; + + regval = OHCI_HCOR->hccontrol; + regval &= ~OHCI_CTRL_HCFS_MASK; + regval |= OHCI_CTRL_HCFS_SUSPEND; + OHCI_HCOR->hccontrol = regval; + + for (uint8_t index = 0; index < CONFIG_USB_OHCI_ED_NUM; index++) { + ed = &g_ohci_ed_pool[bus->hcd.hcd_id][index]; + usb_osal_sem_delete(ed->waitsem); } return 0; @@ -97,7 +141,7 @@ int ohci_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, u uint8_t port; uint32_t temp; - nports = g_ehci_hcd[bus->hcd.hcd_id].n_pcc; + nports = g_ohci_hcd[bus->hcd.hcd_id].n_ports; port = setup->wIndex; if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) { @@ -139,28 +183,46 @@ int ohci_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, u switch (setup->wValue) { case HUB_PORT_FEATURE_ENABLE: + temp = OHCI_RHPORTST_CCS; break; case HUB_PORT_FEATURE_SUSPEND: + temp = OHCI_HCOR->hccontrol; + temp &= ~OHCI_CTRL_HCFS_MASK; + temp |= OHCI_CTRL_HCFS_RESUME; + OHCI_HCOR->hccontrol = temp; + usb_osal_msleep(20); + + temp = OHCI_HCOR->hccontrol; + temp &= ~OHCI_CTRL_HCFS_MASK; + temp |= OHCI_CTRL_HCFS_OPER; + OHCI_HCOR->hccontrol = temp; + + temp = OHCI_RHPORTST_POCI; + break; case HUB_PORT_FEATURE_C_SUSPEND: + temp = OHCI_RHPORTST_PSSC; break; case HUB_PORT_FEATURE_POWER: + OHCI_HCOR->hcrhsts = OHCI_RHSTATUS_CGP; + temp = OHCI_RHPORTST_LSDA; break; case HUB_PORT_FEATURE_C_CONNECTION: - OHCI_HCOR->hcrhportsts[port - 1] |= OHCI_RHPORTST_CSC; + temp = OHCI_RHPORTST_CSC; break; case HUB_PORT_FEATURE_C_ENABLE: - OHCI_HCOR->hcrhportsts[port - 1] |= OHCI_RHPORTST_PESC; + temp = OHCI_RHPORTST_PESC; break; case HUB_PORT_FEATURE_C_OVER_CURREN: - OHCI_HCOR->hcrhportsts[port - 1] |= OHCI_RHPORTST_OCIC; + temp = OHCI_RHPORTST_OCIC; break; case HUB_PORT_FEATURE_C_RESET: - OHCI_HCOR->hcrhportsts[port - 1] |= OHCI_RHPORTST_PRSC; + temp = OHCI_RHPORTST_PRSC; break; default: return -USB_ERR_NOTSUPP; } + OHCI_HCOR->hcrhportsts[port - 1] = temp; break; case HUB_REQUEST_SET_FEATURE: if (!port || port > nports) { @@ -169,11 +231,17 @@ int ohci_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, u switch (setup->wValue) { case HUB_PORT_FEATURE_SUSPEND: + temp = OHCI_HCOR->hccontrol; + temp &= ~OHCI_CTRL_HCFS_MASK; + temp |= OHCI_CTRL_HCFS_SUSPEND; + OHCI_HCOR->hccontrol = temp; + break; case HUB_PORT_FEATURE_POWER: + OHCI_HCOR->hcrhsts = OHCI_RHSTATUS_SGP; break; case HUB_PORT_FEATURE_RESET: - OHCI_HCOR->hcrhportsts[port - 1] |= OHCI_RHPORTST_PRS; + OHCI_HCOR->hcrhportsts[port - 1] = OHCI_RHPORTST_PRS; while (OHCI_HCOR->hcrhportsts[port - 1] & OHCI_RHPORTST_PRS) { } @@ -188,7 +256,6 @@ int ohci_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, u return -USB_ERR_INVAL; } temp = OHCI_HCOR->hcrhportsts[port - 1]; - memcpy(buf, &temp, 4); break; default: @@ -216,9 +283,9 @@ void OHCI_IRQHandler(uint8_t busid) bus = &g_usbhost_bus[busid]; usbsts = OHCI_HCOR->hcintsts & OHCI_HCOR->hcinten; - OHCI_HCOR->hcintsts = usbsts; if (usbsts & OHCI_INT_RHSC) { + OHCI_HCOR->hcintsts = OHCI_INT_RHSC; for (int port = 0; port < CONFIG_USBHOST_MAX_RHPORTS; port++) { uint32_t portsc = OHCI_HCOR->hcrhportsts[port]; @@ -236,5 +303,51 @@ void OHCI_IRQHandler(uint8_t busid) } } if (usbsts & OHCI_INT_WDH) { + OHCI_HCOR->hcintsts = OHCI_INT_WDH; } -} \ No newline at end of file +} + +#ifndef CONFIG_USB_EHCI_WITH_OHCI +__WEAK void usb_hc_low_level_init(struct usbh_bus *bus) +{ + (void)bus; +} + +__WEAK void usb_hc_low_level_deinit(struct usbh_bus *bus) +{ + (void)bus; +} + +int usb_hc_init(struct usbh_bus *bus) +{ + usb_hc_low_level_init(bus); + return ohci_init(bus); +} + +int usb_hc_deinit(struct usbh_bus *bus) +{ + ohci_deinit(bus); + usb_hc_low_level_deinit(bus); + return 0; +} + +int usbh_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, uint8_t *buf) +{ + return ohci_roothub_control(bus, setup, buf); +} + +int usbh_submit_urb(struct usbh_urb *urb) +{ + return ohci_submit_urb(urb); +} + +int usbh_kill_urb(struct usbh_urb *urb) +{ + return ohci_kill_urb(urb); +} + +void USBH_IRQHandler(uint8_t busid) +{ + OHCI_IRQHandler(busid); +} +#endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_hc_ohci.h b/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_hc_ohci.h index 030350e..19d5190 100644 --- a/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_hc_ohci.h +++ b/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_hc_ohci.h @@ -1,484 +1,56 @@ -/**************************************************************************** - * include/nuttx/usb/ohci.h +/* + * Copyright (c) 2024, sakumisu * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -#ifndef __INCLUDE_NUTTX_USB_OHCI_H -#define __INCLUDE_NUTTX_USB_OHCI_H - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* Register offsets *********************************************************/ - -/* Control and status registers (section 7.1) */ - -#define OHCI_HCIREV_OFFSET 0x0000 /* HcRevision: Version of HCI specification */ -#define OHCI_CTRL_OFFSET 0x0004 /* HcControl: HC control */ -#define OHCI_CMDST_OFFSET 0x0008 /* HcCommandStatus: HC command status */ -#define OHCI_INTST_OFFSET 0x000c /* HcInterruptStatus: HC interrupt status */ -#define OHCI_INTEN_OFFSET 0x0010 /* HcInterruptEnable: HC interrupt enable */ -#define OHCI_INTDIS_OFFSET 0x0014 /* HcInterruptDisable: HC interrupt disable */ - -/* Memory pointer registers (section 7.2) */ - -#define OHCI_HCCA_OFFSET 0x0018 /* HcHCCA: HC communication area */ -#define OHCI_PERED_OFFSET 0x001c /* HcPeriodCurrentED: Current isoc or int endpoint desc */ -#define OHCI_CTRLHEADED_OFFSET 0x0020 /* HcControlHeadED: First EP desc in the control list */ -#define OHCI_CTRLED_OFFSET 0x0024 /* HcControlCurrentED: Current EP desc in the control list */ -#define OHCI_BULKHEADED_OFFSET 0x0028 /* HcBulkHeadED: First EP desc in the bulk list */ -#define OHCI_BULKED_OFFSET 0x002c /* HcBulkCurrentED: Current EP desc in the bulk list */ -#define OHCI_DONEHEAD_OFFSET 0x0030 /* HcDoneHead: Last transfer desc added to DONE queue */ - -/* Frame counter registers (section 7.3) */ - -#define OHCI_FMINT_OFFSET 0x0034 /* HcFmInterval: Bit time interval that would not cause overrun */ -#define OHCI_FMREM_OFFSET 0x0038 /* HcFmRemaining: Bit time remaining in current frame */ -#define OHCI_FMNO_OFFSET 0x003c /* HcFmNumber: Frame number counter */ -#define OHCI_PERSTART_OFFSET 0x0040 /* HcPeriodicStart: Time to start processing periodic list */ - -/* Root hub registers (section 7.4) */ - -#define OHCI_LSTHRES_OFFSET 0x0044 /* HcLSThreshold: Commit to transfer threshold */ -#define OHCI_RHDESCA_OFFSET 0x0048 /* HcRhDescriptorA: Describes root hub (part A) */ -#define OHCI_RHDESCB_OFFSET 0x004c /* HcRhDescriptorB: Describes root hub (part B) */ -#define OHCI_RHSTATUS_OFFSET 0x0050 /* HcRhStatus: Root hub status */ - -#define OHCI_MAX_RHPORT 15 /* Maximum number of OHCI root hub ports */ - -#define OHCI_RHPORTST_OFFSET(n) (0x0054 + (((n) - 1) << 2)) -#define OHCI_RHPORTST1_OFFSET 0x0054 /* HcRhPort1Status: Root hub port status 1 */ -#define OHCI_RHPORTST2_OFFSET 0x0058 /* HcRhPort2Status: Root hub port status 2 */ -#define OHCI_RHPORTST3_OFFSET 0x005c /* HcRhPort3Status: Root hub port status 3 */ -#define OHCI_RHPORTST4_OFFSET 0x0060 /* HcRhPort4Status: Root hub port status 4 */ -#define OHCI_RHPORTST5_OFFSET 0x0064 /* HcRhPort5Status: Root hub port status 5 */ -#define OHCI_RHPORTST6_OFFSET 0x0068 /* HcRhPort6Status: Root hub port status 6 */ -#define OHCI_RHPORTST7_OFFSET 0x006c /* HcRhPort7Status: Root hub port status 7 */ -#define OHCI_RHPORTST8_OFFSET 0x0070 /* HcRhPort8Status: Root hub port status 8 */ -#define OHCI_RHPORTST9_OFFSET 0x0074 /* HcRhPort9Status: Root hub port status 9 */ -#define OHCI_RHPORTST10_OFFSET 0x0078 /* HcRhPort10Status: Root hub port status 10 */ -#define OHCI_RHPORTST11_OFFSET 0x007c /* HcRhPort11Status: Root hub port status 11 */ -#define OHCI_RHPORTST12_OFFSET 0x0080 /* HcRhPort12Status: Root hub port status 12 */ -#define OHCI_RHPORTST13_OFFSET 0x0084 /* HcRhPort13Status: Root hub port status 13 */ -#define OHCI_RHPORTST14_OFFSET 0x0088 /* HcRhPort14Status: Root hub port status 14 */ -#define OHCI_RHPORTST15_OFFSET 0x008c /* HcRhPort15Status: Root hub port status 15 */ - -/* Register bit definitions *************************************************/ - -/* HcRevision: Version of HCI specification (7.1.1) */ - -#define OHCI_HCIREV_SHIFT (0) /* Bits 0-7: HCI spec version (BCD) */ -#define OHCI_HCIREV_MASK (0xff << OHCI_HCIREV_SHIFT) - -/* HcControl: HC control (7.1.2) */ - -#define OHCI_CTRL_CBSR (3 << 0) /* Bit 0: Control/bulk service ratio */ -#define OHCI_CTRL_PLE (1 << 2) /* Bit 1: Periodic list enable */ -#define OHCI_CTRL_IE (1 << 3) /* Bit 2: Isochronous enable */ -#define OHCI_CTRL_CLE (1 << 4) /* Bit 3: Control list enable */ -#define OHCI_CTRL_BLE (1 << 5) /* Bit 4: Bulk list enable */ -#define OHCI_CTRL_HCFS_SHIFT (6) /* Bits 6-7: Host controller functional state */ -#define OHCI_CTRL_HCFS_MASK (3 << OHCI_CTRL_HCFS_SHIFT) -# define OHCI_CTRL_HCFS_RESET (0 << OHCI_CTRL_HCFS_SHIFT) -# define OHCI_CTRL_HCFS_RESUME (1 << OHCI_CTRL_HCFS_SHIFT) -# define OHCI_CTRL_HCFS_OPER (2 << OHCI_CTRL_HCFS_SHIFT) -# define OHCI_CTRL_HCFS_SUSPEND (3 << OHCI_CTRL_HCFS_SHIFT) -#define OHCI_CTRL_IR (1 << 8) /* Bit 8: Interrupt routing */ -#define OHCI_CTRL_RWC (1 << 9) /* Bit 9: Remote wakeup connected */ -#define OHCI_CTRL_RWE (1 << 10) /* Bit 10: Remote wakeup enable */ - /* Bits 11-31: Reserved */ - -/* HcCommandStatus: HC command status (7.1.3) */ - -#define OHCI_CMDST_HCR (1 << 0) /* Bit 0: Host controller reset */ -#define OHCI_CMDST_CLF (1 << 1) /* Bit 1: Control list filled */ -#define OHCI_CMDST_BLF (1 << 2) /* Bit 2: Bulk list filled */ -#define OHCI_CMDST_OCR (1 << 3) /* Bit 3: Ownership change request */ - /* Bits 4-15: Reserved */ -#define OHCI_CMDST_SOC (3 << 16) /* Bit 16: Scheduling overrun count */ - /* Bits 17-31: Reserved */ - -/* HcInterruptStatus: HC interrupt status (7.1.4), - * HcInterruptEnable: HC interrupt enable (7.1.5), and - * HcInterruptDisable: HC interrupt disable (7.1.6) + * SPDX-License-Identifier: Apache-2.0 */ +#ifndef _USB_OHCI_PRIV_H +#define _USB_OHCI_PRIV_H -#define OHCI_INT_SO (1 << 0) /* Bit 0: Scheduling overrun */ -#define OHCI_INT_WDH (1 << 1) /* Bit 1: Writeback done head */ -#define OHCI_INT_SF (1 << 2) /* Bit 2: Start of frame */ -#define OHCI_INT_RD (1 << 3) /* Bit 3: Resume detected */ -#define OHCI_INT_UE (1 << 4) /* Bit 4: Unrecoverable error */ -#define OHCI_INT_FNO (1 << 5) /* Bit 5: Frame number overflow */ -#define OHCI_INT_RHSC (1 << 6) /* Bit 6: Root hub status change */ - /* Bits 7-29: Reserved */ -#define OHCI_INT_OC (1 << 30) /* Bit 30: Ownership change */ -#define OHCI_INT_MIE (1 << 31) /* Bit 31: Master interrupt enable - * (Enable/disable only) */ +#include "usbh_core.h" +#include "usbh_hub.h" +#include "usb_ohci_reg.h" -/* HcHCCA: HC communication area (7.2.1): - * - * 32-bits aligned to 256 byte boundary. - */ +#define OHCI_HCOR ((struct ohci_hcor *)(uintptr_t)(bus->hcd.reg_base + CONFIG_USB_OHCI_HCOR_OFFSET)) -/* HcPeriodCurrentED: Current isoc or int endpoint desc (7.2.2), - * HcControlHeadED: First EP desc in the control list (7.2.3), - * HcControlCurrentED: Current EP desc in the control list (7.2.4), - * HcBulkHeadED: First EP desc in the bulk list (7.2.5), - * HcBulkCurrentED: Current EP desc in the bulk list (7.2.6), and - * HcDoneHead: Last transfer desc added to DONE queue (7.2.7): - * - * All 32-bits aligned to an 8-byte boundary - */ +#define OHCI_PTR2ADDR(x) ((uint32_t)(uintptr_t)(x) & ~0x0F) +#define OHCI_ADDR2ED(x) ((struct ohci_ed_hw *)(uintptr_t)((uint32_t)(x) & ~0x0F)) +#define OHCI_ADDR2TD(x) ((struct ohci_td_hw *)(uintptr_t)((uint32_t)(x) & ~0x0F)) -/* HcFmInterval: Bit time interval that would not cause overrun (7.3.1) */ - -#define OHCI_FMINT_FI_SHIFT (0) /* Bits 0-13: Frame interval */ -#define OHCI_FMINT_FI_MASK (0x3fff << OHCI_FMINT_FI_SHIFT) - /* Bits 14-15: Reserved */ -#define OHCI_FMINT_FSMPS_SHIFT (16) /* Bits 16-30: FS largest packet data */ -#define OHCI_FMINT_FSMPS_MASK (0x7fff << OHCI_FMINT_FSMPS_SHIFT) -#define OHCI_FMINT_FIT (1 << 31) /* Bit 31: Frame interval toggle */ - -/* HcFmRemaining: Bit time remaining in current frame (7.3.2) */ - -#define OHCI_FMREM_FR_SHIFT (0) /* Bits 0-13: Frame remaining */ -#define OHCI_FMREM_FR_MASK (0x3fff << OHCI_FMREM_FR_SHIFT) - /* Bits 16-30: Reserved */ -#define OHCI_FMINT_FRT (1 << 31) /* Bit 31: Frame remaining toggle */ - -/* HcFmNumber: Frame number counter (7.3.3) */ - -#define OHCI_FMNO_FI_SHIFT (0) /* Bits 0-15: Frame number */ -#define OHCI_FMNO_FI_MASK (0xffff << OHCI_FMINT_FI_SHIFT) - /* Bits 16-31: Reserved */ - -/* HcPeriodicStart: Time to start processing periodic list (7.3.4) */ - -#define OHCI_PERSTART_SHIFT (0) /* Bits 0-13: Periodic start */ -#define OHCI_PERSTART_MASK (0x3fff << OHCI_PERSTART_SHIFT) - /* Bits 14-31: Reserved */ - -/* HcLSThreshold: Commit to transfer threshold (7.3.5) */ - -#define OHCI_LSTHRES_SHIFT (0) /* Bits 0-11: LS threshold */ -#define OHCI_LSTHRES_MASK (0x0fff << OHCI_PERSTART_SHIFT) - /* Bits 12-31: Reserved */ - -/* HcRhDescriptorN: Describes root hub (part A) (7.4.1) */ - -#define OHCI_RHDESCA_NDP_SHIFT (0) /* Bits 0-7: Number downstream ports */ -#define OHCI_RHDESCA_NDP_MASK (0xff << OHCI_RHDESCA_NDP_SHIFT) -#define OHCI_RHDESCA_PSM (1 << 8) /* Bit 8: Power switching mode */ -#define OHCI_RHDESCA_NPS (1 << 9) /* Bit 9: No power switching */ -#define OHCI_RHDESCA_DT (1 << 10) /* Bit 10: Device type */ -#define OHCI_RHDESCA_OCPM (1 << 11) /* Bit 11: Over current protection mode */ -#define OHCI_RHDESCA_NOCP (1 << 12) /* Bit 12: No over current protection */ - /* Bits 13-23: Reserved */ -#define OHCI_RHDESCA_POTPGT_SHIFT (24) /* Bits 24-31: Power on to power good time */ -#define OHCI_RHDESCA_POTPGT_MASK (0xff << OHCI_RHDESCA_POTPGT_SHIFT) - -/* HcRhDescriptorB: Describes root hub (part B) (7.4.2) */ - -#define OHCI_RHDESCB_DR_SHIFT (0) /* Bits 0-15: Device removable */ -#define OHCI_RHDESCB_DR_MASK (0xffff << OHCI_RHDESCB_DR_SHIFT) -# define OHCI_RHDESCB_ATTACHED(n) (1 << (OHCI_RHDESCB_DR_SHIFT+(n))) -#define OHCI_RHDESCB_PPCM_SHIFT (16) /* Bits 16-31: Port power control mask */ -#define OHCI_RHDESCB_PPCM_MASK (0xffff << OHCI_RHDESCB_PPCM_SHIFT) -# define OHCI_RHDESCB_POWERED(n) (1 << (OHCI_RHDESCB_DR_SHIFT+(n))) - -/* HcRhStatus: Root hub status (7.4.3) */ - -#define OHCI_RHSTATUS_LPS (1 << 0) /* Bit 0: Local power status (read)*/ -#define OHCI_RHSTATUS_CGP (1 << 0) /* Bit 0: Clear global power (write)*/ -#define OHCI_RHSTATUS_OCI (1 << 1) /* Bit 1: Over current indicator */ - /* Bits 2-14: Reserved */ -#define OHCI_RHSTATUS_DRWE (1 << 15) /* Bit 15: Device remote wakeup enable */ -#define OHCI_RHSTATUS_LPSC (1 << 16) /* Bit 16: Local power status change (read) */ -#define OHCI_RHSTATUS_SGP (1 << 16) /* Bit 16: Set global power (write) */ -#define OHCI_RHSTATUS_OCIC (1 << 17) /* Bit 17: Overcurrent indicator change */ - /* Bits 18-30: Reserved */ -#define OHCI_RHSTATUS_CRWE (1 << 31) /* Bit 31: Clear remote wakeup enable */ - -/* HcRhPortStatus: Root hub port status (7.4.4) */ - -#define OHCI_RHPORTST_CCS (1 << 0) /* Bit 0: Current connect status */ -#define OHCI_RHPORTST_PES (1 << 1) /* Bit 1: Port enable status */ -#define OHCI_RHPORTST_PSS (1 << 2) /* Bit 2: Port suspend status */ -#define OHCI_RHPORTST_POCI (1 << 3) /* Bit 3: Port over current indicator */ -#define OHCI_RHPORTST_PRS (1 << 4) /* Bit 4: Port reset status */ - /* Bits 5-7: Reserved */ -#define OHCI_RHPORTST_PPS (1 << 8) /* Bit 8: Port power status */ -#define OHCI_RHPORTST_LSDA (1 << 9) /* Bit 9: Low speed device attached */ - /* Bits 10-15: Reserved */ -#define OHCI_RHPORTST_CSC (1 << 16) /* Bit 16: Connect status change */ -#define OHCI_RHPORTST_PESC (1 << 17) /* Bit 17: Port enable status change */ -#define OHCI_RHPORTST_PSSC (1 << 18) /* Bit 18: Port suspend status change */ -#define OHCI_RHPORTST_OCIC (1 << 19) /* Bit 19: Port over current indicator change */ -#define OHCI_RHPORTST_PRSC (1 << 20) /* Bit 20: Port reset status change */ - /* Bits 21-31: Reserved */ - -/* Transfer Descriptors *****************************************************/ - -/* Endpoint Descriptor Offsets (4.2.1) */ - -#define ED_CONTROL_OFFSET (0x00) /* ED status/control bits */ -#define ED_TAILP_OFFSET (0x04) /* TD Queue Tail Pointer (TailP) */ -#define ED_HEADP_OFFSET (0x08) /* TD Queue Head Pointer (HeadP) */ -#define ED_NEXTED_OFFSET (0x0c) /* Next Endpoint Descriptor (NextED) */ - -/* Endpoint Descriptor Bit Definitions (4.2.2) */ - -#define ED_CONTROL_FA_SHIFT (0) /* Bits 0-6: Function Address */ -#define ED_CONTROL_FA_MASK (0x7f << ED_CONTROL_FA_SHIFT) -#define ED_CONTROL_EN_SHIFT (7) /* Bits 7-10: Endpoint number */ -#define ED_CONTROL_EN_MASK (15 << ED_CONTROL_EN_SHIFT) -#define ED_CONTROL_D_SHIFT (11) /* Bits 11-12: Direction */ -#define ED_CONTROL_D_MASK (3 << ED_CONTROL_D_SHIFT) -# define ED_CONTROL_D_TD1 (0 << ED_CONTROL_D_SHIFT) /* Get direction from TD */ -# define ED_CONTROL_D_OUT (1 << ED_CONTROL_D_SHIFT) /* OUT */ -# define ED_CONTROL_D_IN (2 << ED_CONTROL_D_SHIFT) /* IN */ -# define ED_CONTROL_D_TD2 (3 << ED_CONTROL_D_SHIFT) /* Get direction from TD */ - -#define ED_CONTROL_S (1 << 13) /* Bit 13: Speed (low) */ -#define ED_CONTROL_K (1 << 14) /* Bit 14: Skip */ -#define ED_CONTROL_F (1 << 15) /* Bit 15: Format (isochronous) */ -#define ED_CONTROL_MPS_SHIFT (16) /* Bits 16-26: Maximum packet size */ -#define ED_CONTROL_MPS_MASK (0x7ff << ED_CONTROL_MPS_SHIFT) - -#define ED_HEADP_ADDR_SHIFT (0) -#define ED_HEADP_ADDR_MASK 0xfffffff0 -#define ED_HEADP_H (1 << 0) /* Bit 0: Halted */ -#define ED_HEADP_C (1 << 1) /* Bit 1: Toggle carry */ - -/* General Transfer Descriptor Offsets (4.3.1) */ - -#define GTD_STATUS_OFFSET (0x00) /* TD status bits */ -#define GTD_CBP_OFFSET (0x04) /* Current Buffer Pointer (CBP) */ -#define GTD_NEXTTD_OFFSET (0x08) /* Next TD (NextTD) */ -#define GTD_BE_OFFSET (0x0c) /* Buffer End (BE) */ - -/* General Transfer Descriptor Bit Definitions */ - - /* Bits 0-17: Reserved */ - -#define GTD_STATUS_R (1 << 18) /* Bit 18: Buffer rounding */ -#define GTD_STATUS_DP_SHIFT (19) /* Bits 19-20: Direction/PID */ -#define GTD_STATUS_DP_MASK (3 << GTD_STATUS_DP_SHIFT) -# define GTD_STATUS_DP_SETUP (0 << GTD_STATUS_DP_SHIFT) /* To endpoint */ -# define GTD_STATUS_DP_OUT (1 << GTD_STATUS_DP_SHIFT) /* To endpoint */ -# define GTD_STATUS_DP_IN (2 << GTD_STATUS_DP_SHIFT) /* From endpoint */ - -#define GTD_STATUS_DI_SHIFT (21) /* Bits 21-23: Delay input */ -#define GTD_STATUS_DI_MASK (7 << GTD_STATUS_DI_SHIFT) -#define GTD_STATUS_T_SHIFT (24) /* Bits 24-25: Data Toggle */ -#define GTD_STATUS_T_MASK (3 << GTD_STATUS_T_SHIFT) -# define GTD_STATUS_T_TOGGLE (0 << GTD_STATUS_T_SHIFT) -# define GTD_STATUS_T_DATA0 (2 << GTD_STATUS_T_SHIFT) -# define GTD_STATUS_T_DATA1 (3 << GTD_STATUS_T_SHIFT) -#define GTD_STATUS_EC_SHIFT (26) /* Bits 26-27: Error count */ -#define GTD_STATUS_EC_MASK (3 << GTD_STATUS_EC_SHIFT) -#define GTD_STATUS_CC_SHIFT (28) /* Bits 28-31: Condition code */ -#define GTD_STATUS_CC_MASK (15 << GTD_STATUS_CC_SHIFT) - -/* Isochronous Transfer Descriptor Offsets (4.3.2) */ - -#define ITD_STATUS_OFFSET (0x00) /* TD status bits */ -#define ITD_BP0_OFFSET (0x04) /* Buffer page 0 (BP0) */ -#define ITD_NEXTTD_OFFSET (0x08) /* Next TD (NextTD) */ -#define ITD_BE_OFFSET (0x0c) /* Buffer End (BE) */ - -#define ITD_NPSW (8) -#define ITD_PSW0_OFFSET (0x10) /* Offset0/PSW0 */ -#define ITD_PSW1_OFFSET (0x12) /* Offset1/PSW1 */ -#define ITD_PSW2_OFFSET (0x14) /* Offset2/PSW2 */ -#define ITD_PSW3_OFFSET (0x16) /* Offset3/PSW3 */ -#define ITD_PSW4_OFFSET (0x18) /* Offset4/PSW4 */ -#define ITD_PSW5_OFFSET (0x1a) /* Offset5/PSW5 */ -#define ITD_PSW6_OFFSET (0x1c) /* Offset6/PSW6 */ -#define ITD_PSW7_OFFSET (0x1e) /* Offset7/PSW7 */ - -/* Condition codes (Table 4-7) */ - -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEMISMATCH 0x03 -#define TD_CC_STALL 0x04 -#define TD_CC_DEVNOTRESPONDING 0x05 -#define TD_CC_PIDCHECKFAILURE 0x06 -#define TD_CC_UNEXPECTEDPID 0x07 -#define TD_CC_DATAOVERRUN 0x08 -#define TD_CC_DATAUNDERRUN 0x09 -#define TD_CC_BUFFEROVERRUN 0x0c -#define TD_CC_BUFFERUNDERRUN 0x0d -#define TD_CC_NOTACCESSED 0x0f - -#define TD_CC_USER 0x10 /* For use by OHCI drivers */ - -/* Host Controller Communications Area Format (4.4.1) ***********************/ - -/* HccaInterruptTable: 32x32-bit pointers to interrupt EDs */ - -#define HCCA_INTTBL_OFFSET (0x00) -#define HCCA_INTTBL_WSIZE (32) -#define HCCA_INTTBL_BSIZE (HCCA_INTTBL_WSIZE * 4) - -/* HccaFrameNumber: Current frame number */ - -#define HCCA_FMNO_OFFSET (0x80) -#define HCCA_FMNO_BSIZE (2) - -/* HccaPad1: Zero when frame no. updated */ - -#define HCCA_PAD1_OFFSET (0x82) -#define HCCA_PAD1_BSIZE (2) - -/* HccaDoneHead: When the HC reaches the end of a frame and its deferred - * interrupt register is 0, it writes the current value of its HcDoneHead to - * this location and generates an interrupt. - * - * The LSB of HCCADoneHead may be set to 1 to indicate that an unmasked - * HcInterruptStatus was set when HccaDoneHead was written. - */ - -#define HCCA_DONEHEAD_OFFSET (0x84) -#define HCCA_DONEHEAD_BSIZE (4) - -#define HCCA_DONEHEAD_MASK 0xfffffffe -#define HCCA_DONEHEAD_INTSTA (1 << 0) - -/* 0x88: 116 bytes reserved */ - -#define HCCA_RESERVED_OFFSET (0x88) -#define HCCA_RESERVED_BSIZE (116) - -/**************************************************************************** - * Public Types - ****************************************************************************/ - -struct ohci_hcor -{ - volatile uint32_t hcrevision; /* 0x00 */ - volatile uint32_t hccontrol; /* 0x04 */ - volatile uint32_t hccmdsts; /* 0x08 */ - volatile uint32_t hcintsts; /* 0x0c */ - volatile uint32_t hcinten; /* 0x10 */ - volatile uint32_t hcintdis; /* 0x14 */ - volatile uint32_t hchcca; /* 0x18 */ - volatile uint32_t hcperiodcurrented; /* 0x1c */ - volatile uint32_t hccontrolheaded; /* 0x20 */ - volatile uint32_t hccontrolcurrented; /* 0x24 */ - volatile uint32_t hcbulkheaded; /* 0x28 */ - volatile uint32_t hcbulkcurrented; /* 0x2c */ - volatile uint32_t hcdonehead; /* 0x30 */ - volatile uint32_t hcfminterval; /* 0x34 */ - volatile uint32_t hcfmremaining; /* 0x38 */ - volatile uint32_t hcfmnumber; /* 0x3c */ - volatile uint32_t hcperiodicstart; /* 0x40 */ - volatile uint32_t hclsthreshold; /* 0x44 */ - volatile uint32_t hcrhdescriptora; /* 0x48 */ - volatile uint32_t hcrhdescriptorb; /* 0x4c */ - volatile uint32_t hcrhsts; /* 0x50 */ - volatile uint32_t hcrhportsts[15]; /* 0x54 */ -}; - -/* Endpoint Descriptor Offsets (4.2.1) */ - -struct ohci_ed_s -{ - volatile uint32_t ctrl; /* ED status/control bits */ - volatile uint32_t tailp; /* TD Queue Tail Pointer (TailP) */ - volatile uint32_t headp; /* TD Queue Head Pointer (HeadP) */ - volatile uint32_t nexted; /* Next Endpoint Descriptor (NextED) */ -}; - -/* General Transfer Descriptor (4.3.1) */ - -struct ohci_gtd_s -{ - volatile uint32_t ctrl; /* TD status/control bits */ - volatile uint32_t cbp; /* Current Buffer Pointer (CBP) */ - volatile uint32_t nexttd; /* Next TD (NextTD) */ - volatile uint32_t be; /* Buffer End (BE) */ -}; - -/* Isochronous Transfer Descriptor Offsets (4.3.2) */ - -struct ohci_itd_s -{ - volatile uint32_t ctrl; /* TD status/control bits */ - volatile uint32_t bp0; /* Buffer page 0 (BP0 */ - volatile uint32_t nexttd; /* Next TD (NextTD) */ - volatile uint32_t be; /* Buffer End (BE) */ - volatile uint16_t psw[ITD_NPSW]; /* Offset/PSW */ -}; - -/* Host Controller Communications Area Format (4.4.1) */ - -struct ohci_hcca_s -{ - /* HccaInterruptTable: 32x32-bit pointers to interrupt EDs */ - - volatile uint32_t inttbl[HCCA_INTTBL_WSIZE]; - - /* HccaFrameNumber: Current frame number and - * HccaPad1: Zero when frame no. updated - */ - - volatile uint16_t fmno; - volatile uint16_t pad1; - - /* HccaDoneHead: When the HC reaches the end of a frame and its deferred - * interrupt register is 0, it writes the current value of its HcDoneHead - * to this location and generates an interrupt. - */ - - volatile uint32_t donehead; - volatile uint8_t reserved[HCCA_RESERVED_BSIZE]; - volatile uint32_t extra; -} __attribute__((aligned(256))); - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -#ifdef __cplusplus -#define EXTERN extern "C" -extern "C" -{ -#else -#define EXTERN extern +#ifndef CONFIG_USB_OHCI_ED_NUM +#define CONFIG_USB_OHCI_ED_NUM CONFIG_USBHOST_PIPE_NUM +#endif +#ifndef CONFIG_USB_OHCI_TD_NUM +#define CONFIG_USB_OHCI_TD_NUM 3 #endif -/**************************************************************************** - * Public Function Prototypes - ****************************************************************************/ +struct ohci_ed_hw; +struct ohci_td_hw { + struct ohci_gtd hw; + struct usbh_urb *urb; + uint32_t buf_start; + uint32_t length; +} __attribute__((aligned(32))); /* min is 16bytes, we use 32 for cacheline */ -#undef EXTERN -#ifdef __cplusplus -} -#endif +struct ohci_ed_hw { + struct ohci_ed hw; + struct ohci_td_hw td_pool[CONFIG_USB_OHCI_TD_NUM]; + uint32_t td_count; + uint8_t ed_type; + usb_osal_sem_t waitsem; +} __attribute__((aligned(32))); /* min is 16bytes, we use 32 for cacheline */ -#endif /* __INCLUDE_NUTTX_USB_OHCI_H */ +struct ohci_hcd { + bool ohci_ed_used[CONFIG_USB_OHCI_ED_NUM]; + uint8_t n_ports; +}; + +int ohci_init(struct usbh_bus *bus); +int ohci_deinit(struct usbh_bus *bus); +uint16_t ohci_get_frame_number(struct usbh_bus *bus); +int ohci_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, uint8_t *buf); +int ohci_submit_urb(struct usbh_urb *urb); +int ohci_kill_urb(struct usbh_urb *urb); + +void OHCI_IRQHandler(uint8_t busid); + +#endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_ohci_priv.h b/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_ohci_priv.h deleted file mode 100644 index 4404dc2..0000000 --- a/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_ohci_priv.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2024, sakumisu - * - * SPDX-License-Identifier: Apache-2.0 - */ -#ifndef _USB_OHCI_PRIV_H -#define _USB_OHCI_PRIV_H - -#include "usbh_core.h" -#include "usbh_hub.h" -#include "usb_hc_ohci.h" - -#define OHCI_HCOR ((struct ohci_hcor *)(uintptr_t)(bus->hcd.reg_base + CONFIG_USB_OHCI_HCOR_OFFSET)) - -int ohci_init(struct usbh_bus *bus); -int ohci_deinit(struct usbh_bus *bus); -uint16_t ohci_get_frame_number(struct usbh_bus *bus); -int ohci_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, uint8_t *buf); -int ohci_submit_urb(struct usbh_urb *urb); -int ohci_kill_urb(struct usbh_urb *urb); - -void OHCI_IRQHandler(uint8_t busid); - -#endif \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_ohci_reg.h b/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_ohci_reg.h new file mode 100644 index 0000000..61f7f4b --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/ohci/usb_ohci_reg.h @@ -0,0 +1,484 @@ +/**************************************************************************** + * include/nuttx/usb/ohci.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_USB_OHCI_H +#define __INCLUDE_NUTTX_USB_OHCI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +/* Control and status registers (section 7.1) */ + +#define OHCI_HCIREV_OFFSET 0x0000 /* HcRevision: Version of HCI specification */ +#define OHCI_CTRL_OFFSET 0x0004 /* HcControl: HC control */ +#define OHCI_CMDST_OFFSET 0x0008 /* HcCommandStatus: HC command status */ +#define OHCI_INTST_OFFSET 0x000c /* HcInterruptStatus: HC interrupt status */ +#define OHCI_INTEN_OFFSET 0x0010 /* HcInterruptEnable: HC interrupt enable */ +#define OHCI_INTDIS_OFFSET 0x0014 /* HcInterruptDisable: HC interrupt disable */ + +/* Memory pointer registers (section 7.2) */ + +#define OHCI_HCCA_OFFSET 0x0018 /* HcHCCA: HC communication area */ +#define OHCI_PERED_OFFSET 0x001c /* HcPeriodCurrentED: Current isoc or int endpoint desc */ +#define OHCI_CTRLHEADED_OFFSET 0x0020 /* HcControlHeadED: First EP desc in the control list */ +#define OHCI_CTRLED_OFFSET 0x0024 /* HcControlCurrentED: Current EP desc in the control list */ +#define OHCI_BULKHEADED_OFFSET 0x0028 /* HcBulkHeadED: First EP desc in the bulk list */ +#define OHCI_BULKED_OFFSET 0x002c /* HcBulkCurrentED: Current EP desc in the bulk list */ +#define OHCI_DONEHEAD_OFFSET 0x0030 /* HcDoneHead: Last transfer desc added to DONE queue */ + +/* Frame counter registers (section 7.3) */ + +#define OHCI_FMINT_OFFSET 0x0034 /* HcFmInterval: Bit time interval that would not cause overrun */ +#define OHCI_FMREM_OFFSET 0x0038 /* HcFmRemaining: Bit time remaining in current frame */ +#define OHCI_FMNO_OFFSET 0x003c /* HcFmNumber: Frame number counter */ +#define OHCI_PERSTART_OFFSET 0x0040 /* HcPeriodicStart: Time to start processing periodic list */ + +/* Root hub registers (section 7.4) */ + +#define OHCI_LSTHRES_OFFSET 0x0044 /* HcLSThreshold: Commit to transfer threshold */ +#define OHCI_RHDESCA_OFFSET 0x0048 /* HcRhDescriptorA: Describes root hub (part A) */ +#define OHCI_RHDESCB_OFFSET 0x004c /* HcRhDescriptorB: Describes root hub (part B) */ +#define OHCI_RHSTATUS_OFFSET 0x0050 /* HcRhStatus: Root hub status */ + +#define OHCI_MAX_RHPORT 15 /* Maximum number of OHCI root hub ports */ + +#define OHCI_RHPORTST_OFFSET(n) (0x0054 + (((n) - 1) << 2)) +#define OHCI_RHPORTST1_OFFSET 0x0054 /* HcRhPort1Status: Root hub port status 1 */ +#define OHCI_RHPORTST2_OFFSET 0x0058 /* HcRhPort2Status: Root hub port status 2 */ +#define OHCI_RHPORTST3_OFFSET 0x005c /* HcRhPort3Status: Root hub port status 3 */ +#define OHCI_RHPORTST4_OFFSET 0x0060 /* HcRhPort4Status: Root hub port status 4 */ +#define OHCI_RHPORTST5_OFFSET 0x0064 /* HcRhPort5Status: Root hub port status 5 */ +#define OHCI_RHPORTST6_OFFSET 0x0068 /* HcRhPort6Status: Root hub port status 6 */ +#define OHCI_RHPORTST7_OFFSET 0x006c /* HcRhPort7Status: Root hub port status 7 */ +#define OHCI_RHPORTST8_OFFSET 0x0070 /* HcRhPort8Status: Root hub port status 8 */ +#define OHCI_RHPORTST9_OFFSET 0x0074 /* HcRhPort9Status: Root hub port status 9 */ +#define OHCI_RHPORTST10_OFFSET 0x0078 /* HcRhPort10Status: Root hub port status 10 */ +#define OHCI_RHPORTST11_OFFSET 0x007c /* HcRhPort11Status: Root hub port status 11 */ +#define OHCI_RHPORTST12_OFFSET 0x0080 /* HcRhPort12Status: Root hub port status 12 */ +#define OHCI_RHPORTST13_OFFSET 0x0084 /* HcRhPort13Status: Root hub port status 13 */ +#define OHCI_RHPORTST14_OFFSET 0x0088 /* HcRhPort14Status: Root hub port status 14 */ +#define OHCI_RHPORTST15_OFFSET 0x008c /* HcRhPort15Status: Root hub port status 15 */ + +/* Register bit definitions *************************************************/ + +/* HcRevision: Version of HCI specification (7.1.1) */ + +#define OHCI_HCIREV_SHIFT (0) /* Bits 0-7: HCI spec version (BCD) */ +#define OHCI_HCIREV_MASK (0xff << OHCI_HCIREV_SHIFT) + +/* HcControl: HC control (7.1.2) */ + +#define OHCI_CTRL_CBSR (3 << 0) /* Bit 0: Control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* Bit 2: Periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* Bit 3: Isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* Bit 4: Control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* Bit 5: Bulk list enable */ +#define OHCI_CTRL_HCFS_SHIFT (6) /* Bits 6-7: Host controller functional state */ +#define OHCI_CTRL_HCFS_MASK (3 << OHCI_CTRL_HCFS_SHIFT) +# define OHCI_CTRL_HCFS_RESET (0 << OHCI_CTRL_HCFS_SHIFT) +# define OHCI_CTRL_HCFS_RESUME (1 << OHCI_CTRL_HCFS_SHIFT) +# define OHCI_CTRL_HCFS_OPER (2 << OHCI_CTRL_HCFS_SHIFT) +# define OHCI_CTRL_HCFS_SUSPEND (3 << OHCI_CTRL_HCFS_SHIFT) +#define OHCI_CTRL_IR (1 << 8) /* Bit 8: Interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* Bit 9: Remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* Bit 10: Remote wakeup enable */ + /* Bits 11-31: Reserved */ + +/* HcCommandStatus: HC command status (7.1.3) */ + +#define OHCI_CMDST_HCR (1 << 0) /* Bit 0: Host controller reset */ +#define OHCI_CMDST_CLF (1 << 1) /* Bit 1: Control list filled */ +#define OHCI_CMDST_BLF (1 << 2) /* Bit 2: Bulk list filled */ +#define OHCI_CMDST_OCR (1 << 3) /* Bit 3: Ownership change request */ + /* Bits 4-15: Reserved */ +#define OHCI_CMDST_SOC (3 << 16) /* Bit 16: Scheduling overrun count */ + /* Bits 17-31: Reserved */ + +/* HcInterruptStatus: HC interrupt status (7.1.4), + * HcInterruptEnable: HC interrupt enable (7.1.5), and + * HcInterruptDisable: HC interrupt disable (7.1.6) + */ + +#define OHCI_INT_SO (1 << 0) /* Bit 0: Scheduling overrun */ +#define OHCI_INT_WDH (1 << 1) /* Bit 1: Writeback done head */ +#define OHCI_INT_SF (1 << 2) /* Bit 2: Start of frame */ +#define OHCI_INT_RD (1 << 3) /* Bit 3: Resume detected */ +#define OHCI_INT_UE (1 << 4) /* Bit 4: Unrecoverable error */ +#define OHCI_INT_FNO (1 << 5) /* Bit 5: Frame number overflow */ +#define OHCI_INT_RHSC (1 << 6) /* Bit 6: Root hub status change */ + /* Bits 7-29: Reserved */ +#define OHCI_INT_OC (1 << 30) /* Bit 30: Ownership change */ +#define OHCI_INT_MIE (1 << 31) /* Bit 31: Master interrupt enable + * (Enable/disable only) */ + +/* HcHCCA: HC communication area (7.2.1): + * + * 32-bits aligned to 256 byte boundary. + */ + +/* HcPeriodCurrentED: Current isoc or int endpoint desc (7.2.2), + * HcControlHeadED: First EP desc in the control list (7.2.3), + * HcControlCurrentED: Current EP desc in the control list (7.2.4), + * HcBulkHeadED: First EP desc in the bulk list (7.2.5), + * HcBulkCurrentED: Current EP desc in the bulk list (7.2.6), and + * HcDoneHead: Last transfer desc added to DONE queue (7.2.7): + * + * All 32-bits aligned to an 8-byte boundary + */ + +/* HcFmInterval: Bit time interval that would not cause overrun (7.3.1) */ + +#define OHCI_FMINT_FI_SHIFT (0) /* Bits 0-13: Frame interval */ +#define OHCI_FMINT_FI_MASK (0x3fff << OHCI_FMINT_FI_SHIFT) + /* Bits 14-15: Reserved */ +#define OHCI_FMINT_FSMPS_SHIFT (16) /* Bits 16-30: FS largest packet data */ +#define OHCI_FMINT_FSMPS_MASK (0x7fff << OHCI_FMINT_FSMPS_SHIFT) +#define OHCI_FMINT_FIT (1 << 31) /* Bit 31: Frame interval toggle */ + +/* HcFmRemaining: Bit time remaining in current frame (7.3.2) */ + +#define OHCI_FMREM_FR_SHIFT (0) /* Bits 0-13: Frame remaining */ +#define OHCI_FMREM_FR_MASK (0x3fff << OHCI_FMREM_FR_SHIFT) + /* Bits 16-30: Reserved */ +#define OHCI_FMINT_FRT (1 << 31) /* Bit 31: Frame remaining toggle */ + +/* HcFmNumber: Frame number counter (7.3.3) */ + +#define OHCI_FMNO_FI_SHIFT (0) /* Bits 0-15: Frame number */ +#define OHCI_FMNO_FI_MASK (0xffff << OHCI_FMINT_FI_SHIFT) + /* Bits 16-31: Reserved */ + +/* HcPeriodicStart: Time to start processing periodic list (7.3.4) */ + +#define OHCI_PERSTART_SHIFT (0) /* Bits 0-13: Periodic start */ +#define OHCI_PERSTART_MASK (0x3fff << OHCI_PERSTART_SHIFT) + /* Bits 14-31: Reserved */ + +/* HcLSThreshold: Commit to transfer threshold (7.3.5) */ + +#define OHCI_LSTHRES_SHIFT (0) /* Bits 0-11: LS threshold */ +#define OHCI_LSTHRES_MASK (0x0fff << OHCI_PERSTART_SHIFT) + /* Bits 12-31: Reserved */ + +/* HcRhDescriptorN: Describes root hub (part A) (7.4.1) */ + +#define OHCI_RHDESCA_NDP_SHIFT (0) /* Bits 0-7: Number downstream ports */ +#define OHCI_RHDESCA_NDP_MASK (0xff << OHCI_RHDESCA_NDP_SHIFT) +#define OHCI_RHDESCA_PSM (1 << 8) /* Bit 8: Power switching mode */ +#define OHCI_RHDESCA_NPS (1 << 9) /* Bit 9: No power switching */ +#define OHCI_RHDESCA_DT (1 << 10) /* Bit 10: Device type */ +#define OHCI_RHDESCA_OCPM (1 << 11) /* Bit 11: Over current protection mode */ +#define OHCI_RHDESCA_NOCP (1 << 12) /* Bit 12: No over current protection */ + /* Bits 13-23: Reserved */ +#define OHCI_RHDESCA_POTPGT_SHIFT (24) /* Bits 24-31: Power on to power good time */ +#define OHCI_RHDESCA_POTPGT_MASK (0xff << OHCI_RHDESCA_POTPGT_SHIFT) + +/* HcRhDescriptorB: Describes root hub (part B) (7.4.2) */ + +#define OHCI_RHDESCB_DR_SHIFT (0) /* Bits 0-15: Device removable */ +#define OHCI_RHDESCB_DR_MASK (0xffff << OHCI_RHDESCB_DR_SHIFT) +# define OHCI_RHDESCB_ATTACHED(n) (1 << (OHCI_RHDESCB_DR_SHIFT+(n))) +#define OHCI_RHDESCB_PPCM_SHIFT (16) /* Bits 16-31: Port power control mask */ +#define OHCI_RHDESCB_PPCM_MASK (0xffff << OHCI_RHDESCB_PPCM_SHIFT) +# define OHCI_RHDESCB_POWERED(n) (1 << (OHCI_RHDESCB_DR_SHIFT+(n))) + +/* HcRhStatus: Root hub status (7.4.3) */ + +#define OHCI_RHSTATUS_LPS (1 << 0) /* Bit 0: Local power status (read)*/ +#define OHCI_RHSTATUS_CGP (1 << 0) /* Bit 0: Clear global power (write)*/ +#define OHCI_RHSTATUS_OCI (1 << 1) /* Bit 1: Over current indicator */ + /* Bits 2-14: Reserved */ +#define OHCI_RHSTATUS_DRWE (1 << 15) /* Bit 15: Device remote wakeup enable */ +#define OHCI_RHSTATUS_LPSC (1 << 16) /* Bit 16: Local power status change (read) */ +#define OHCI_RHSTATUS_SGP (1 << 16) /* Bit 16: Set global power (write) */ +#define OHCI_RHSTATUS_OCIC (1 << 17) /* Bit 17: Overcurrent indicator change */ + /* Bits 18-30: Reserved */ +#define OHCI_RHSTATUS_CRWE (1 << 31) /* Bit 31: Clear remote wakeup enable */ + +/* HcRhPortStatus: Root hub port status (7.4.4) */ + +#define OHCI_RHPORTST_CCS (1 << 0) /* Bit 0: Current connect status */ +#define OHCI_RHPORTST_PES (1 << 1) /* Bit 1: Port enable status */ +#define OHCI_RHPORTST_PSS (1 << 2) /* Bit 2: Port suspend status */ +#define OHCI_RHPORTST_POCI (1 << 3) /* Bit 3: Port over current indicator */ +#define OHCI_RHPORTST_PRS (1 << 4) /* Bit 4: Port reset status */ + /* Bits 5-7: Reserved */ +#define OHCI_RHPORTST_PPS (1 << 8) /* Bit 8: Port power status */ +#define OHCI_RHPORTST_LSDA (1 << 9) /* Bit 9: Low speed device attached */ + /* Bits 10-15: Reserved */ +#define OHCI_RHPORTST_CSC (1 << 16) /* Bit 16: Connect status change */ +#define OHCI_RHPORTST_PESC (1 << 17) /* Bit 17: Port enable status change */ +#define OHCI_RHPORTST_PSSC (1 << 18) /* Bit 18: Port suspend status change */ +#define OHCI_RHPORTST_OCIC (1 << 19) /* Bit 19: Port over current indicator change */ +#define OHCI_RHPORTST_PRSC (1 << 20) /* Bit 20: Port reset status change */ + /* Bits 21-31: Reserved */ + +/* Transfer Descriptors *****************************************************/ + +/* Endpoint Descriptor Offsets (4.2.1) */ + +#define ED_CONTROL_OFFSET (0x00) /* ED status/control bits */ +#define ED_TAILP_OFFSET (0x04) /* TD Queue Tail Pointer (TailP) */ +#define ED_HEADP_OFFSET (0x08) /* TD Queue Head Pointer (HeadP) */ +#define ED_NEXTED_OFFSET (0x0c) /* Next Endpoint Descriptor (NextED) */ + +/* Endpoint Descriptor Bit Definitions (4.2.2) */ + +#define ED_CONTROL_FA_SHIFT (0) /* Bits 0-6: Function Address */ +#define ED_CONTROL_FA_MASK (0x7f << ED_CONTROL_FA_SHIFT) +#define ED_CONTROL_EN_SHIFT (7) /* Bits 7-10: Endpoint number */ +#define ED_CONTROL_EN_MASK (15 << ED_CONTROL_EN_SHIFT) +#define ED_CONTROL_D_SHIFT (11) /* Bits 11-12: Direction */ +#define ED_CONTROL_D_MASK (3 << ED_CONTROL_D_SHIFT) +# define ED_CONTROL_D_TD1 (0 << ED_CONTROL_D_SHIFT) /* Get direction from TD */ +# define ED_CONTROL_D_OUT (1 << ED_CONTROL_D_SHIFT) /* OUT */ +# define ED_CONTROL_D_IN (2 << ED_CONTROL_D_SHIFT) /* IN */ +# define ED_CONTROL_D_TD2 (3 << ED_CONTROL_D_SHIFT) /* Get direction from TD */ + +#define ED_CONTROL_SPPED_LOW (1 << 13) /* Bit 13: Speed (low) */ +#define ED_CONTROL_SKIP (1 << 14) /* Bit 14: Skip */ +#define ED_CONTROL_FORMAT_ISO (1 << 15) /* Bit 15: Format (isochronous) */ +#define ED_CONTROL_MPS_SHIFT (16) /* Bits 16-26: Maximum packet size */ +#define ED_CONTROL_MPS_MASK (0x7ff << ED_CONTROL_MPS_SHIFT) + +#define ED_HEADP_ADDR_SHIFT (0) +#define ED_HEADP_ADDR_MASK 0xfffffff0 +#define ED_HEADP_H (1 << 0) /* Bit 0: Halted */ +#define ED_HEADP_C (1 << 1) /* Bit 1: Toggle carry */ + +/* General Transfer Descriptor Offsets (4.3.1) */ + +#define GTD_STATUS_OFFSET (0x00) /* TD status bits */ +#define GTD_CBP_OFFSET (0x04) /* Current Buffer Pointer (CBP) */ +#define GTD_NEXTTD_OFFSET (0x08) /* Next TD (NextTD) */ +#define GTD_BE_OFFSET (0x0c) /* Buffer End (BE) */ + +/* General Transfer Descriptor Bit Definitions */ + + /* Bits 0-17: Reserved */ + +#define GTD_STATUS_R (1 << 18) /* Bit 18: Buffer rounding */ +#define GTD_STATUS_DP_SHIFT (19) /* Bits 19-20: Direction/PID */ +#define GTD_STATUS_DP_MASK (3 << GTD_STATUS_DP_SHIFT) +# define GTD_STATUS_DP_SETUP (0 << GTD_STATUS_DP_SHIFT) /* To endpoint */ +# define GTD_STATUS_DP_OUT (1 << GTD_STATUS_DP_SHIFT) /* To endpoint */ +# define GTD_STATUS_DP_IN (2 << GTD_STATUS_DP_SHIFT) /* From endpoint */ + +#define GTD_STATUS_DI_SHIFT (21) /* Bits 21-23: Delay input */ +#define GTD_STATUS_DI_MASK (7 << GTD_STATUS_DI_SHIFT) +#define GTD_STATUS_T_SHIFT (24) /* Bits 24-25: Data Toggle */ +#define GTD_STATUS_T_MASK (3 << GTD_STATUS_T_SHIFT) +# define GTD_STATUS_T_TOGGLE (0 << GTD_STATUS_T_SHIFT) +# define GTD_STATUS_T_DATA0 (2 << GTD_STATUS_T_SHIFT) +# define GTD_STATUS_T_DATA1 (3 << GTD_STATUS_T_SHIFT) +#define GTD_STATUS_EC_SHIFT (26) /* Bits 26-27: Error count */ +#define GTD_STATUS_EC_MASK (3 << GTD_STATUS_EC_SHIFT) +#define GTD_STATUS_CC_SHIFT (28) /* Bits 28-31: Condition code */ +#define GTD_STATUS_CC_MASK (15 << GTD_STATUS_CC_SHIFT) + +/* Isochronous Transfer Descriptor Offsets (4.3.2) */ + +#define ITD_STATUS_OFFSET (0x00) /* TD status bits */ +#define ITD_BP0_OFFSET (0x04) /* Buffer page 0 (BP0) */ +#define ITD_NEXTTD_OFFSET (0x08) /* Next TD (NextTD) */ +#define ITD_BE_OFFSET (0x0c) /* Buffer End (BE) */ + +#define ITD_NPSW (8) +#define ITD_PSW0_OFFSET (0x10) /* Offset0/PSW0 */ +#define ITD_PSW1_OFFSET (0x12) /* Offset1/PSW1 */ +#define ITD_PSW2_OFFSET (0x14) /* Offset2/PSW2 */ +#define ITD_PSW3_OFFSET (0x16) /* Offset3/PSW3 */ +#define ITD_PSW4_OFFSET (0x18) /* Offset4/PSW4 */ +#define ITD_PSW5_OFFSET (0x1a) /* Offset5/PSW5 */ +#define ITD_PSW6_OFFSET (0x1c) /* Offset6/PSW6 */ +#define ITD_PSW7_OFFSET (0x1e) /* Offset7/PSW7 */ + +/* Condition codes (Table 4-7) */ + +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEMISMATCH 0x03 +#define TD_CC_STALL 0x04 +#define TD_CC_DEVNOTRESPONDING 0x05 +#define TD_CC_PIDCHECKFAILURE 0x06 +#define TD_CC_UNEXPECTEDPID 0x07 +#define TD_CC_DATAOVERRUN 0x08 +#define TD_CC_DATAUNDERRUN 0x09 +#define TD_CC_BUFFEROVERRUN 0x0c +#define TD_CC_BUFFERUNDERRUN 0x0d +#define TD_CC_NOTACCESSED 0x0f + +#define TD_CC_USER 0x10 /* For use by OHCI drivers */ + +/* Host Controller Communications Area Format (4.4.1) ***********************/ + +/* HccaInterruptTable: 32x32-bit pointers to interrupt EDs */ + +#define HCCA_INTTBL_OFFSET (0x00) +#define HCCA_INTTBL_WSIZE (32) +#define HCCA_INTTBL_BSIZE (HCCA_INTTBL_WSIZE * 4) + +/* HccaFrameNumber: Current frame number */ + +#define HCCA_FMNO_OFFSET (0x80) +#define HCCA_FMNO_BSIZE (2) + +/* HccaPad1: Zero when frame no. updated */ + +#define HCCA_PAD1_OFFSET (0x82) +#define HCCA_PAD1_BSIZE (2) + +/* HccaDoneHead: When the HC reaches the end of a frame and its deferred + * interrupt register is 0, it writes the current value of its HcDoneHead to + * this location and generates an interrupt. + * + * The LSB of HCCADoneHead may be set to 1 to indicate that an unmasked + * HcInterruptStatus was set when HccaDoneHead was written. + */ + +#define HCCA_DONEHEAD_OFFSET (0x84) +#define HCCA_DONEHEAD_BSIZE (4) + +#define HCCA_DONEHEAD_MASK 0xfffffffe +#define HCCA_DONEHEAD_INTSTA (1 << 0) + +/* 0x88: 116 bytes reserved */ + +#define HCCA_RESERVED_OFFSET (0x88) +#define HCCA_RESERVED_BSIZE (116) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct ohci_hcor +{ + volatile uint32_t hcrevision; /* 0x00 */ + volatile uint32_t hccontrol; /* 0x04 */ + volatile uint32_t hccmdsts; /* 0x08 */ + volatile uint32_t hcintsts; /* 0x0c */ + volatile uint32_t hcinten; /* 0x10 */ + volatile uint32_t hcintdis; /* 0x14 */ + volatile uint32_t hchcca; /* 0x18 */ + volatile uint32_t hcperiodcurrented; /* 0x1c */ + volatile uint32_t hccontrolheaded; /* 0x20 */ + volatile uint32_t hccontrolcurrented; /* 0x24 */ + volatile uint32_t hcbulkheaded; /* 0x28 */ + volatile uint32_t hcbulkcurrented; /* 0x2c */ + volatile uint32_t hcdonehead; /* 0x30 */ + volatile uint32_t hcfminterval; /* 0x34 */ + volatile uint32_t hcfmremaining; /* 0x38 */ + volatile uint32_t hcfmnumber; /* 0x3c */ + volatile uint32_t hcperiodicstart; /* 0x40 */ + volatile uint32_t hclsthreshold; /* 0x44 */ + volatile uint32_t hcrhdescriptora; /* 0x48 */ + volatile uint32_t hcrhdescriptorb; /* 0x4c */ + volatile uint32_t hcrhsts; /* 0x50 */ + volatile uint32_t hcrhportsts[15]; /* 0x54 */ +}; + +/* Endpoint Descriptor Offsets (4.2.1) */ + +struct ohci_ed +{ + volatile uint32_t ctrl; /* ED status/control bits */ + volatile uint32_t tailp; /* TD Queue Tail Pointer (TailP) */ + volatile uint32_t headp; /* TD Queue Head Pointer (HeadP) */ + volatile uint32_t nexted; /* Next Endpoint Descriptor (NextED) */ +}; + +/* General Transfer Descriptor (4.3.1) */ + +struct ohci_gtd +{ + volatile uint32_t ctrl; /* TD status/control bits */ + volatile uint32_t cbp; /* Current Buffer Pointer (CBP) */ + volatile uint32_t nexttd; /* Next TD (NextTD) */ + volatile uint32_t be; /* Buffer End (BE) */ +}; + +/* Isochronous Transfer Descriptor Offsets (4.3.2) */ + +struct ohci_itd +{ + volatile uint32_t ctrl; /* TD status/control bits */ + volatile uint32_t bp0; /* Buffer page 0 (BP0 */ + volatile uint32_t nexttd; /* Next TD (NextTD) */ + volatile uint32_t be; /* Buffer End (BE) */ + volatile uint16_t psw[ITD_NPSW]; /* Offset/PSW */ +}; + +/* Host Controller Communications Area Format (4.4.1) */ + +struct ohci_hcca +{ + /* HccaInterruptTable: 32x32-bit pointers to interrupt EDs */ + + volatile uint32_t inttbl[HCCA_INTTBL_WSIZE]; + + /* HccaFrameNumber: Current frame number and + * HccaPad1: Zero when frame no. updated + */ + + volatile uint16_t fmno; + volatile uint16_t pad1; + + /* HccaDoneHead: When the HC reaches the end of a frame and its deferred + * interrupt register is 0, it writes the current value of its HcDoneHead + * to this location and generates an interrupt. + */ + + volatile uint32_t donehead; + volatile uint8_t reserved[HCCA_RESERVED_BSIZE]; + volatile uint32_t extra; +} __attribute__((aligned(256))); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_USB_OHCI_H */ diff --git a/rt-thread/components/drivers/usb/cherryusb/port/pusb2/README.md b/rt-thread/components/drivers/usb/cherryusb/port/pusb2/README.md new file mode 100644 index 0000000..f8137af --- /dev/null +++ b/rt-thread/components/drivers/usb/cherryusb/port/pusb2/README.md @@ -0,0 +1,66 @@ +# USB 2.0 OTG 控制器 (PUSB2) + +- Phytium PI 和 Phyium E2000 系列开发板提供了兼容 USB2.0 的 OTG 接口 +- 相关的使用例程可以在 Phytium PI(飞腾派)和 E2000 D/Q Demo 板上运行,例程包括 + +--------------------------------------------- + +- Host 模式 + +- - 1. [FreeRTOS 上作为主机使用键盘/鼠标/U盘](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk/tree/master/example/peripheral/usb/pusb2_host/README.md) +- - 5. [RT-Thread 上作为主机识别键盘/鼠标/U盘](https://github.com/RT-Thread/rt-thread/blob/master/bsp/phytium/doc/use_cherryusb.md) + +--------------------------------------------- + +- Device 模式 + +- - 1. [裸机上模拟为一个 U 盘](https://gitee.com/phytium_embedded/phytium-standalone-sdk/tree/master/example/peripherals/usb/pusb2_device/README.md) +- - 2. [裸机上模拟为一个虚拟串口](https://gitee.com/phytium_embedded/phytium-standalone-sdk/tree/master/example/peripherals/usb/pusb2_device/README.md) +- - 3. [FreeRTOS 上模拟为一个 U 盘](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk/tree/master/example/peripheral/usb/pusb2_device/README.md) +- - 4. [FreeRTOS 上模拟为虚拟串口](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk/tree/master/example/peripheral/usb/pusb2_device/README.md) +- - 5. [RT-Thread 上模拟为一个 U 盘](https://github.com/RT-Thread/rt-thread/blob/master/bsp/phytium/doc/use_cherryusb.md) + +--------------------------------------------- + +- PUSB2 的驱动功能以静态库的方式提供, +- - libpusb2_hc_a64.a : AARCH64 主机模式驱动库 +- - libpusb2_dc_a64.a : AARCH64 从机模式驱动库 +- - libpusb2_hc_a32_hardfp.a :AARCH32 主机模式驱动库,使用硬浮点 +- - libpusb2_hc_a32_softfp.a :AARCH32 主机模式驱动库,使用软浮点 +- - libpusb2_dc_a32_hardfp.a :AARCH32 从机模式驱动库,使用硬浮点 +- - libpusb2_dc_a32_softfp.a :AARCH32 从机模式驱动库,使用软浮点 + +需要获取源代码请联系 `opensource_embedded@phytium.com.cn` 获取 + +# USB 2.0 OTG Controller (PUSB2) + +- Phytium PI and the Phytium E2000 series development boards offer OTG interfaces compatible with USB 2.0. +- Relevant usage examples can be run on the Phytium PI and E2000 D/Q Demo boards, including: + +--------------------------------------------- + +- Host Mode + + - 1. [Using a keyboard/mouse/USB flash drive as a host on FreeRTOS](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk/tree/master/example/peripheral/usb/pusb2_host/README.md) + - 5. [Recognizing a keyboard/mouse/USB flash drive as a host on RT-Thread](https://github.com/RT-Thread/rt-thread/blob/master/bsp/phytium/doc/use_cherryusb.md) + +--------------------------------------------- + +- Device Mode + - 1. [Simulating as a USB flash drive on a standalone system](https://gitee.com/phytium_embedded/phytium-standalone-sdk/tree/master/example/peripherals/usb/pusb2_device/README.md) + - 2. [Simulating as a virtual serial port on a standalone system](https://gitee.com/phytium_embedded/phytium-standalone-sdk/tree/master/example/peripherals/usb/pusb2_device/README.md) + - 3. [Simulating as a USB flash drive on FreeRTOS](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk/tree/master/example/peripheral/usb/pusb2_device/README.md) + - 4. [Simulating as a virtual serial port on FreeRTOS](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk/tree/master/example/peripheral/usb/pusb2_device/README.md) + - 5. [Simulating as a USB flash drive on RT-Thread](https://github.com/RT-Thread/rt-thread/blob/master/bsp/phytium/doc/use_cherryusb.md) + +--------------------------------------------- + +- The driver functionality of PUSB2 is provided as static libraries: + - - `libpusb2_hc_a64.a` : Host mode driver library for AARCH64 + - - `libpusb2_dc_a64.a` : Device mode driver library for AARCH64 + - - `libpusb2_hc_a32_hardfp.a` : Host mode driver library for AARCH32, using hard floating point + - - `libpusb2_hc_a32_softfp.a` : Host mode driver library for AARCH32, using soft floating point + - - `libpusb2_dc_a32_hardfp.a` : Device mode driver library for AARCH32, using hard floating point + - - `libpusb2_dc_a32_softfp.a` : Device mode driver library for AARCH32, using soft floating point + +- To obtain the source code, please contact `opensource_embedded@phytium.com.cn`. \ No newline at end of file diff --git a/rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_dc_a32_hardfp.a b/rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_dc_a32_hardfp.a new file mode 100644 index 0000000000000000000000000000000000000000..685de412406a29a865b6f584d1e81a1d91c2f0e3 GIT binary patch literal 171332 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 literal 0 HcmV?d00001 diff --git a/rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_dc_a32_softfp_crypto_neon.a b/rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_dc_a32_softfp_crypto_neon.a new file mode 100644 index 0000000000000000000000000000000000000000..f0e1ceb7eeecdb7d629436b6386607e49865fdf6 GIT binary patch literal 171328 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%0D7WDzFQPtXG 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

SE{ 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

|#mr&3wnP(i8 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℞4u`;Gmrl4F`#*E*nlxt(|(+L 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 Lb4+Q`6qWlQkB$_b literal 0 HcmV?d00001 diff --git a/rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_dc_a64.a b/rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_dc_a64.a new file mode 100644 index 0000000000000000000000000000000000000000..3e4ea1245f7b831516314096fa9cd5ffaa02c9ce GIT binary patch literal 141528 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 literal 0 HcmV?d00001 diff --git a/rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_hc_a32_hardfp.a b/rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_hc_a32_hardfp.a new file mode 100644 index 0000000000000000000000000000000000000000..7e029a61795b52287a2d716c534219b0fac48a4c GIT binary patch literal 923220 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%0Aa*zygr54J6lq%GO?_=@ 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±*I+QVm%dMa8Q2&7?V+nc^Alx+m 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 literal 0 HcmV?d00001 diff --git a/rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_hc_a32_softfp_crypto_neon.a b/rt-thread/components/drivers/usb/cherryusb/port/pusb2/libpusb2_hc_a32_softfp_crypto_neon.a new file mode 100644 index 0000000000000000000000000000000000000000..dea366e9ff1d1db9282d9085ebbe6dcf51254ff4 GIT binary patch literal 923192 zcmd3P2b^40_5XXbJDY?gkWdT(fk^_%F4^78%+Bu4(zhl{wq;8S#bIi)W*b{lC@M`r zL0?h!7iwvD2R#$Y>527-*eBMdGoe}U-^GNzlFT-yZ7C9-!12y zd+xdC-dS-%ay-4`r2W=~ivPl%pt<*6L!n4G7B@-G+Rt&Eh~t!=@!$LR?{7NJEdNUG zeZiUQU)d#pa%TEBzviqhMQs@$oXi%#C{eyQB~D*$n#$#} zG>ULW1ohgOdzNplv)WkDQ6jR3_azwsq4~b}E|>wPeqhFrKZ? zPWFrs?n1Tt2G7Yl+q09Mn|k`Q6O(=8V{Jpp?M`|qn;iGWNv_Swp`lbVy|aHjIWmEQ zIn4uoO)Uvz*gk@G8cECdcJK?0>B*_&P~TWKn_(Gtq+7D-(M+~`a(kaNsKoVUCpV|2 zCQO3f>_m35btE%3I*7(+&KsuZjF-nJP5S=9;p}+puI$L9X3#Re%RJeFr3OsR{-M3mIk)W%Gjv-W#q zYe$>wx)3eq&vnY3LdbD`|4WGv#stcp>zr%l`2fP3@sDY@;NMog-v&HQ?@tFpSV@{Q zfv?m11n?}qPXbeVp9W_1J`2q0{dV9Ey&nX=Uhj7ThxC3JIHLEXz%ji)8#u1_6TnHm zp91dE``y4jdcPNVj^3XOJWub>2fjh?-w1q@-d_N`Q134SUaa?*0Nox84l>frBoKDESI`1F$S;ZHl^$DR&`H{##vPnS41Kk0n;il<8Ndhf4~ zd}ZB}c*S|sn{Is5Oz+(le(cH5gg4^f=}-Pw-gCn5czS+#A^sh{&+jclIp?FCZusUW z7od!MXF=%nr=8FPPdk4K{c5hVdf&Yl<>b5fhE9L-uFwNdzS?&eEivU>6hb?NE}He5 z&W5UIOVO@Nw7h(8Nr?Xre|pi9!=Ik-9RBowUU}s?ez)|y8=rD6yZ@O}ZgelW>#jGk z`|^Lboo(5j@Xf#4SKI!#($3O$Czm)*&~Zyyp#A>{%3pK=*y&)f-lBXN&CLO&L5k~= z_ktY*KU{VqVgse~7PSG~vQHmMnExCj1HoZXULY{96-*H*4c@ktFz?hTV1Dpl#}nqg z9*n)DbP-7(@BL?mV_Db@=86<(89)>fIq znOCazGlhd(cY{vJ{>QCwk6+lZu>GI_IpJt$;lh&^mh2DGdkr5hG|xaS2RI8?l(ruP za*WAarFgR9koJR4UVg%I&{@n`e~`&)#s17`UbTDja+~{#vNg+>x(Ak>a_BkBU1Tx8 z=;3~AmbV||eY~L7;fMAM3M|C;<`e%>DEDb<0i(QpS9KQqNTPo5uK{HRc7yau`9pkMJ>tqB1NN7jl0$98;RgiEzIZB<&0AuU=x0o_l}k;ext1(CFu3+UNVSr65X-E+ zxt1)oxgC5^u=Ff2LTSmN2M3S5`7GYu=^j#w@M}sImY8>fixX)+IIJ|dunbgDvUr(1 zC^=fQ39fk#^01`*Q13SemU}{|(P2fw!-K&^-vzOO^n&|!LGYI>daV|*G*t6;#O^Emn{=p3Kp^{OLyT?@(zItO4_2|C|`J{NQzgBU>$C|}Sy ztCZK9{a{uR}cz>&WWeX}3bp8?I^@AyiZ{+o3oxJXkK``iCxQN#P1bEQ-elxGR zc3wY6DdFq~DG+qlAHwTvCC$1myzgQIIHzF%#9LDS2QVxIodr^=Z%O*cL4ZN$QhDoo zG&Itm&g*U|L$reT^J{orJ;Lkfq=es+@4lOj>I||uov-cT^~oi?erzkR&GN-pYI(oE zq>oDc3*fAv6E~%joFik5Nl9ybWF_zKNb>rSTpwS}`zxehzAUYAr+n)PkXF#y#x8aK zC9S>mc;0tOsScKyPotC3vlG0YE8pq|2LzqvQoCix@%|4d@j4*A|4zwm*J8%}OiFdM z_if7`_Cb}6~LhWBqK**XtZ@_Ox|yfz%o>rSDTw@c2ykiNJ?zW7Ng!)4Ng zQOV~w8Ky6lrz^{Of5rf>&q$BlFEsygY1JPJJ$zND`fQ=+4}kSi!2@~yhP2GV^7Om| z7&9f5yhPe$i^PnY_Suh5&)&f6PoT;nrR3i$E&o@k%kQL*juTEfNS<~|UtAzPd!6LG zNXjrEwJu-9d}8wS389TsPGHQjYj_l>mpwN zRmba>g%kg=o%cVM7F#Bi*(9}IEqUH7F^h!)KO}wqCrCQbs(kl#Qrfli81ogO7Dw*a zN=e#|V*ELh10rxgCT;$HX@O4R?sJ5?wn%x-5<2PVW6IHEdHtxN%=NtgfwaIn>AP=A zT|OYK{n-vajUUYGH-)EuDP=2>T7O|T<8P6emzsIsFFE{FN-{Lgm@4^JrR3Hjt=)pI z2gOR8{7tCn2BCqgPGS7Bhx3}0l0PB!{hRdW1M>85ujkW$N`3#`#`_Pty!OgHs)+mB zg$`Fv@xD@MuROu~=cLzu5a#_|QmS(4i}TK8Ojc_8bD^|*CH`KK7%S!LXDnfwvn76& zr0lO`%xzMa9a8toC}S4LcaK=a`xa^W)xufV$n`~O!&nucUb39mOQp1@Nj^0~nG1yb zUTBdtn|O`N*RN>e{R<*ht`({-?Pbh%p{3`9FTO3bu|wK(uh3`t=}dFQR$h;jUfVj( z`yH6c1f4qJuk}gZ|8A1kU(e_Dt3oaJj`04?()<6}$@>e1em*U=8V0JEi4U2z|B)Ra`4l z;)WsS^Q3(JV&T@`2(`RhXgDkMf5&M|^8v~EPU-g-YZ&u4p_Vn(y#J82e`$#K<5IhG zMJ^3V`7c|;`1gp^ZIK*KonXu_cgefb0xwAI2DUKfWNGKM!j&7OJYngPl~SJQVy1bw zq#T!bAMatzyjEUwQq!G6t80ZCm#kv^ty14JB<2jEu8TSuf3MKkr9#6Cx)}38q5M9n z?~SK2rer0rT?g^HJIibMP+n6~(?3eDoh|K%X)c%c*(@|rE;(E)J#}jX-~EH+ z_Q($2e?Vyar_zQyo@WKA+%VTvmF#t z<}8uEIO+`MvrN*wDCOyv{;ls|{1*^k=47M{?-Clg4)JBq0^y6tq(%Xe3HJzxejarx zbG~sjuSf6U^+96_Hp3Qi-%z0AEzjzU!jtN&%mkm095lZ+x=%ma!QYh!A60-?y zh?W7hlsUU{y#6iC>$6hYL(wl~&UZk=Xe-pH%(*~#AuYAL10@MM_iPu!a)PB1*1v2S zgY#Yn1n1YWjTSsGpeYVNk11BXiS<7EBy#PVW7(E#A0ho#pK%nfzY6?wKH%BFeDde- z1GldRycl?yRPm3%N%&asr9h7K`%2*VeC^Vb^-BQnD!F?H;Kq`7;cLNLN`{8@qn(d3 zaqlxEmec<|hHL5|l5KiB8S8@5^=JoF!wJ6TrVy?xevY09mainmT;yH?k_ley{*!gT z%w1c9(BGSGosJO8b0^J$H}$I)3k7_s#`?``qWd0iSo@%?`ZZ-N5Gk zg4@l4ebIe0%kU-lmn4}lyAPiZ_=>xn1oeR1!moeT?I5c?=pM{Y`kK4!NWeqx#f<#A zdmSSmcH8;Fcip>Dz_LHM*D|v!0ymumcx#{%t&DE@JKOYafeMZ;R|R&Hf!-eY@F{?+ z17}gVToZU?E#Mu2gLVV14ZMw%d0pTiEc`nIFR|{|2X>Mc-xYWVD}6&C5(2zC(7h9% zy)|&!DBy{}wfh0CD%ra~o?Tng3l1)OcgZH9o07X~wFeHq7K9sojY}R~wilGfz65=- z{j>&}Rq(06_d&>I&ju<`^L=Jqb`87bfG?6j4|{0iSmlv7p~}Z*9W2UT`BwA|-p{T8^pLxE~KA{#y4i($A;di%E<3 zx$hx-$$g1!`mlS{kk)bOCVu#kYtPbf;NxuARa;0}$JCQX*G#eDkNrHy`pPRfo>sq$ z#SA|~l8^q3JRXZ3gKJ$Mdn3M*V&eET)1UbM!!_T|E0}t4KHtCEJ)Z;A?SUil)!-e0 zu6n>n1CJdB_*h^ysq^E3&r_1#8Tiq9z+Hh1C^z`|K+|Ht{ekCL#xDeJE&)6oSRMj= zBhY&^;E}*(<$!Mn-jV`*EAabM02c=j?E-u%`0*0}Uk)w?4Ftaue8c{L2ZD#NwZ9td zA!|JtyuA|ewcuab><A)`GU?5+TOBl!A*0FMN3Ba3`9IG>q)E7;93_1nRv3jp5< zE=PTX-wj6D5Z?>dmjNCPJ~je)EcjDW?)QUJO!lNpgJpyt1wXe1@Z;be7z%?w z2`*)xKMh9Fzrmjc=Zym%5B`Cj`$X^@63x$pw@(0m5qx+G@T=gP*@C|gevkC}bnq>t zsNV#y+6eeoc#RwGqZQ!3Ig=YfGNP*7>#woyG4BQn0ycCF&wf-HL z$6PKd`5WtgamnY|epi$XtN~nIatfR0LnR?*{k4)Ur16JJzS#!&cF9{01bnCDessLzt%uc+Z^nP~7g{9wSLtb23c?#fdrMIzN zt|`57C*Yl>H!KF+P?|Un@ZQoTeEp`-W>rXGvr~FKuSCK3)2K63K5$OUTNdFucl4&KrZxGng7cH z26s}7eIj@p8}d`Z3pM~g8+?)-@b%#Sm4JtXF01m5;Ej6#-wu8@1NctxLc;fh5WSQO zA$I@c!E@&Wei__(DB#K9-`Ph`1%qfMVZsAspjz^Kbos0U)jo%&BYumlRw;w`>A&xi~BwI zkbC#W(1T^?+{Id-dpe3+_J*5S^*45cTFNeT+en=kxtEqAbg}zpihxVpQ$YXF^Zv37 zq08MUh`a2qZY#OrO8NshWp8tD+k((l?rT`Gx4WOAXu8_{7i34-HSXi2q<6Rvp}{c} zE)alEsO+6?$pAvvyLD$E^e*={66p_jdBlE$(`L{r&Fm`MnRgp9i&+eaO86m4sZluNt9S-H!nv7Z#j? z&~5GkXCZXEJHq{qA5T^Mi5KH(mIFhZYn zk3&nBeada%vroIvfmX}zc9)SbKI0zC_W!JVQwKtyb7P1syT@I2G(z{f&CLki=PqO} zpLe&nAoNA|W)kq1+zlfLec9bZLi~z*DxW>*u8$-1HTUJi5PI0HW2wL4{+uoGh@12P!H1mV0pwp>Ml`YY}?Xy>}9!$K2N->$2~=HRz7u58P8h6J>ls)PGh@|tBTM0fb`?Y&M znkV?Q`}!3K{l;BJN&Z{68mvquOww&fA9T z@b$}a-2>?c9-hwY*P^X4cz(GJ@L~51@E=Ca)w=;7ajzsn-|oJdgUcQ6503+U)a@DueA<1G znLS_nZx-iIrTu7?;5ET7Q02HYcrZ!vvEX$q()WW$p>LsF-R}Z^7(5hI2R-V>FyLpw zrz3#JgLktmzX+a8!u(}$RVCob;MYjB7nOaI4SI3eq2QI^TguL<0$fve%L%5v%N9P; zh=?VZvxg3wY|>sh;2&iOFMaJAT$jbzs>}bt53X8Vf$Pz0S=nR0emt(L?_#U2`5K$; zq_6Vz#wVDj^~bEk`k(OQ?QdFvYiBbXr7I4O3U=Spk898A^Kk85Sc_}lrHBy#gU2FEj1V{otAyAEEC6v^rWHsftubtA++g zruI}Z@z7vu9UiVLO1{n{T{k$A9-7Kzi#|3-HL`n>!(&6)bz|A_!7-FQIkZj+j|&{> zMkglMRaY1O_f#6up3tjo0+z0*WdD<9D6alY_5NSaecc32eG*VI(L#0bkQWb32mV4YJW_ z6G-gJj!z7Zj+jVzG3EyOUeIv{v)OEJ4EJE%oN?=9?sx)z7@0J;iOIcVS&h!$;L->;zpWhzuYL*1EyKWx)ln54!6D^C1ojPa0-@@{lv@qaX0m z7lQ7fFSB3F68|jY-#tT}7z8 zygZSZLKlopjwdE7_Jmd=N}pAQA{7;(6Hg3r&Q+q#hjU(8%xxFQ^xxKHw zsiPGiPPe4CcTQ|CPfS)O_Ec6^S676@ncM4X4)^dWZ~zMsO7BRHBbT<0{<;{-+|}LN zndofqLgumRhFG|+wk}p1k42)fy4qN_swNsk{gA%7Ing)J)6?DC-S*ucw^4zS z@hljX&hX_Gp+b#|;*oAW-f!&h?rd*PY-!%m*qi82baXdwOtkg3wuYk2pOFJyeeLVJ zT3gtD&ICNm6O(vkin^hZA8u>!YVY4B&l8CW@iNCaQ$EJ_+%-CwsVK*N)k%pQoX{si zt4|EQ0j=EE+S_{`p}Vc3f-Tz7(b(KPVC!9872X=@sfl!SY%JU{84KOg+SlJZ0Ls|h zn&@jktu-XAA!TP#%F{z&wMz1JMJT&xGCMvJ0@qHZCqoH1U8i>{BtolJg&00QByWUF z_4_3*bYf_=Nu5vTv^1?xbamsC9j#scq41ut`D0h~^!K*3x3wiY8@Hk%!h7m-=Fe&C z?rH5}bSMfM?da}WpJ?eGAemrz-X6+~PNAESK_j}nt+la#ptm*AHPG4Gi(bK7paSXB zE$vxjw4@C2g*XC0Aoq>N6`Wshm(6k<)n|%oIx;9=!6qOHS9Gdv*zxu zKGaQWG@$*hT!ftw2_-{YDO(xVcLHwi>BKk)2dr6%D+ya8le_Z|5IM&0;X}=h{b209 zOlLw73F0Bk+}*||B!eNsBXIh+_23x_h^OF16b0|+?-L2eOT)K!6>^eZm5Y%E(u(Mv zNI22DwFe(T!#TTDCJa>$SB@B&pfRDL%Fu9SXyxh0R7Jzp(eOm*m{^AY&a7G)8mUA9 za#JJeM1pr@a^4j-xL`}m+O>SNxe-LPzAuqr1DG!EY(yQ6?5nL|Y;R+;B_nWHb#1R) z_(D&2U;Ea)2#bcp@o)p82l|vN4P9?-!(UHTd|<;yJnS?1X%AAkYU^xiZ|UEFdJA8A zDZ8mHQkUp!ZtQ?yY3yz9-_Y6G-`>ouJhYlfboaKj_9l8-dJ?T&E$xjIgf$R9-11Qme!_$ z_44677aHOzuZo1D5f+;*+1T5>ArWsx^;t%fw5XSrSRpsctMY^!;^^fVdXr5tjYC7_ zi9Ki@rKG~%DG0vAo)AAbfY;-R=JIq!C~evUou_R-u>&)RkTiT@7$vVIC!yn|rY5tA z1P4~tf=~tZKnAkD{+8y%hVH(888gGv27W^t9UVo? zLr#&-Y~6%fcd#(+-5pSDaAO26D7&|{Z=l0Z*W=jUUXhh8PvgfQm*fh}I(GOli3q#ULhk13rny5sPQZO9n$42ik z(%9G8)!5luo*u!5DU$0}RJpxteF&sF0F?w)Z))vG^z^p2K^i7N_)Lg0j9|lCVmI|P ztNy42$n$8lmVID)BQZ6RO<=Yys)mmvb|=S2K)Xnhv8fY6H$^ljg%yK9N^(>Il4Ed!zR~AvE{jv-R!Xm24Qw}sVG=w8mS+Ems(l&Eq2Xmv$ z>uT-pf_5LOLs-eAwFg=lVySCIdaCL-kWjqid}t4~yS)y`Omq}Hw#v=)!Vh5h@#-OF z{At3ZAwT}aYN8QGNZm*u&l?oPI!vm*1q#Oy)Y1qw4_qmVIqVUWgXs`!2xAGPBVPjX zv4t_(GSta=43x?^Lq*e*B5EjlVyIzYL%#f&R;a*eg%Y{GwX1oXDrwjt(8Ym=Dy0k! zMv|JLp`gLqjsiJYff84VyE~iQ37W+Y(Jc0amQU;*=ENxp@`IqC7=~C}SFz~!PdX8)@PbS#Op0>8%c>1{69zl2lyPS#4vks zGPXxgWXl;K3y){93!B@Cr$5Cj111+Gd5D?3Ucr{KB0^r}R^?f8l}D@Pi+O2^Pg}t% z6wcP6A;)NQ==#ER+LEHoZ?!Lr6=|{YSV^A%$(R{-#ccCgwvUG>HVw45_9wbz%0WIR z)uP`ZP(TubqqQATolA?J#>`2x+1WAtegwLC0(k8*gz>QM#5+?>=K#{xiNpg%9+WOmWs*PUq2=g-Ln5RfJ9s zg+s?7B-%A=!l7Es2nX7t%)&bF)JhzyOIf-CPTssYP-N^D3kJ*4BL}=J5XYyj|$ZGu#*}4 zjO!fG#>TUm!8DdW$ET<|%GAixJA&EtWVu9F(o_iY!D24#>a4FJAxzrayAVcJ(r&6g zdfH7I4CS6tk@TlSHsDFs@$yr0TFMPih@rE+k4EiIFoj(JAzz8|fn=s@Fv)Sm_X>Nz0&Xm2GT5oABqN=eEHGduAOH+8t2}M%=x($N393lG zsuet=1CmcTT`l?&)JmDYjVMLIbgVdOG{dF?e8@;g!jZVP7?sdMGJ9 zRgv0W(U`1!M>e5iiO-B_hd`5hOsl9~LE*+zUAyUnWwN=iKZNOnXPoJ6#fnvz4DUM3 zOkb1%w_=7@RRzAzksHTDCxHJ>Mqwsox?xv!5P^iph04(d73Ig;?k1|3%3(ox}W+2}^u-S{KFo3t6<=jpOAs)=#*nz1*S0mS( zjS)Z>YWvLm!A`hBUWvr1C*-?#E`}3JaB>^zLswOdATohMGkCfOjS=@4Wl^R6Ud)m5 zhS``JW_x-Y*W=(Zn9@baq09S4!>8ES)|p|+%!pKx3icM_AUne}<=oK!w~5N}Awx#x z;fmu!=R0ctz{;65uF;@rl~&5?!jd_)i8)quxEjF5HX0i`rw|aSw_^~lOEdx&h7nn^ z+BG;am>L`!oZL%;rb)F@mv*pj0pUn{I10u5tf9g?zY)VoUU8Q3#4Nnz1-h&U&a!fd zj76>$#9YDEgiEY#Ej^gjP;A?=0gI#6v2ZjFouaM|3--}?ybcR#@ivnN^`BwB!-L^) zXkB(3rqMl>6P3f@5OlNh!%%V(N>s=$c)>4bB6Ms;7}6W*j!j^J5Z1Uo=rOw3Y#M0n zXm8ubB$c5&OKBGDP`t`ls*rj#m&0*@_~r!Gyele>Kfgly2x@>@71^j7()A=!Wb<=> zDhx7Dv2^$VUdFR z87I?TE!m%5v}q14>$h%AG&Z%{bpV{kDhA8G4mhlZ_r#50paboFi%05VZ;aJa5THG##z!U+le-7gWNaVN7ClyLCZ<=$ z=Ifv1Me&6GALCfk!OUqQF##sWYRim9YV%A8bi%%o@sl(bF{r#Gn$23i8VuR69*wnN zR8mP^;Pe3+fBJg2pbf*}hzMs>^a96jw1a_1&_FaYRJ25I&u-QJES4HkN)25zHVTtj zKgK;C6xvjHM5Y?lCGt}lu{5%zT|MGIhW~`BRIkXqS}_UzxyXr0JWx8@`#gpmhhmVP zJ$5dq<0*QP62fn~?{4br?r4QAj8m%+oQk|*X3+>|3Y1#hE@tp#^jMW@gmw%!A}2Pt zWldzDvp$N-!Zc0hv0??$LbI@WVt|5{Xm4rXjM0RA%=n&Wb~tDQj*&vfv9OaG<-pX0 zD;|)hLS7;hZN*R;)K=59t#Sr(=V)dMHV}y9a{tlDq4Q+qM)R^3EVJPP1cG%4M4BPg@FQjPz z)YIPCL!Zo+M$iKuv8qO+wDmD-#rf5C!(u3SzD+WCMT)* zOiIwnVI>MwHkpP(Nb4D9>|#CJU6`-$MyJr(QhE}~p3KzuR8)qL9}s`78G^Q}j|MRa=Cx7`O;AeN5(*uW?%UzDqB^j@GUC&g;*@mbQj(wMnI!;xiQiX)hKfG%G4>1+9)k?WclhY&jNVc&aw$kuBW$LTizvng zZZBHrp1$JTVRRhNx$_wJyiOCwU&OeJSZ@&{(ZK;$ddcbvbYljhu24Eu#u(IM^D7WrlM$MB-Y>-N9KqY3F zhr>^+$gYr|j*6WqH<%rQv9LH23vP_X_k}dlL!-00On9Clxt9<-X*m+Q=ei?T9hEc- za-K__YHw<9ZDn%O03ubbGak2jdK0sPSqD{-VWkZThBIX2_$H$n`^-DMV82KtURM`M z)R3+m9a!F%+z zRAr-IO_iGDrMcI`$`ELQ$Yq6zW^ExT4`UQo;^@VhSl`m$-N>)PKS&%CsR^kAp3&4f z4P!ar1Js=`1`HHgXMScyQdJ}KtvoAO?Z$r54hiCr(g+={us-1Jf3YUcu#jy`J#01< zS5;EGsty0H>?E|hKJoPtXKka*iLfrXX^Uim7Kg#dxcbA;TP%4|2b1X4%oUB32v&pW zNi>t)d%`@kD_wbRVS83~wstnPVuh`+>&QM2l}mzfEMGKi| ze^QE0H9a9@KNFJ-^ob|ROeT9f2g8~WTmxG}vX6s12#gn7(Ut}Bc`+drEj>oK^q5Sg zjT2gncguzDuG0ks5Tnu2n(@u9fu3n9T7oP5BU9p2sg7Y*4YPw7M1S1?Wq+}Cg-Zbt zIuSMQ@h4jNqbx$9=x?+2O-7Nqb_W#Z%th;F*69#SXQU!sJEbVJE~1nQLI_2yFv~7G z5(BdF=zue`Jw|-1nDjLoWd$5`|C|>T^Q?b^&U15gZ3#yu9&twN&cjyF514w>OBNJNm9%m={ zp?c;G6>;>j@}z9nFs5a>;^o5f+Ke)$@YsqObQ|~1*`bM2s$Y3tY({1@2o;SC_>K~% zT_odLf;iyky_hFtt#Ek4c;ZYLCuS9Kq3Z1;Q`Jn*UCUS#>$l; zWDr`3o1({9ao;sKIyJEp+5<+?(Wy!Qf_nD=HmOm!P!CrBSXW0+R{X;?OVbsr*mnD!(VS?~rb!QTg{@d#%ezi>;3d($JWCQNkZ_P;_*uBLLrhm~ z+uC8_#0wk?X$7O5RM+FliIi8pjV;`20Y#`F)oWlAYl zLW-NyT6??U2rMg(=VG-KD~d31i1`;IHg=Rwy^l~SN8dp=(Ja&b9{x}R$MT4c4DrA$ty(#lUL>{ zn7mR{FnMLJf2qozrz2UAI@~)2LBFr26n#F~$Fwls= zB;|S~D@VP&{*tDLuVnAhdLS53&9KHI<~+ zFlQav&mQTKcWH<-1KPbXq!mwg*&^OQYKs?7t<5K|8lB;|HA z>+i`|&Gxs@G!zIqhmF#g z-#_ggP=@MaNwv=xnJq>vEDsXa&y)K_61tr$3T7W6>-Q&STelR#0?mYxqkE&8w#lV1lM$F}!hd=$Rhn zEq=3UVEo*)rA5rXn|roNKN-hGmLdeV*(B#+dA-iJj>WpkniS8l5X)oH^eJDI+2&Cw z%KDD(CaiBrw}r}ATR(yfe#4OkwZa}U&(ki&)-F{XjgJ!@IcYtGnXrpKiPmu(y)E0t zKq5?UHot9N+64GF0jveu^xShfpcCWF^-F}%Fs^fR6m(o;7?bIC+h(%?03sht)2-M6(QSIWaO8mu z3>>oz4;jaNe7ML&NV}l52XgG=3J8q%>?c{B=X^{2aBR=Ti-smnberLs-$dI@+orw{#Xl?LHe0B*-maaI)=t4qX9h70UpdG?wSlB^d7?1o$a_>xstIi(w z$A+hteamGwkPhr@aMLUkGy8j(hqEoAB&dkM&6C;8xF8R*qdsD{{$lz_zg4qGi~9(; zm()0`gc4W@NM@p9>d}$SNO2QDTH!iF$Q$5VYIP^h+XlKY#3_p;y5WvNy##B$QZ~BC zGeW1s?1hl76y~>@Fq6ctQFU{(H*gASY~CP;v`~(zhbRsD(8@fc9qV6MPr#C{-Fv`D zstv{dOR}=yPlYUuzy`191-5R;2l<&+C~z#v&JKl!hm|x{m8JxkK}706Yd4cmA8he+ zz1y)LT#hABZNRJRDT(`p6_#c}-%YLSMNWz156tz(>_c51E#Nc1+SPgS(DknG!;xM( zk5sPwf_loaU#&PviO=jRBU&XyD+5{%wWSHwGoa2gj_PJj9Ec}!%!)g0-9($1Oxc~1 zqv-tJ&XB_qDEnkgC~~aNyAFj`?a6yFGpB;f!*F(ZbbK%4^3rT3pJa%?zLuH&=Y&G` zT}xTYR&5Di^XwT#^W{m}u26VL8;2yU7hj(aco=o^WGMP9qm}YL&iQ*N0gxkkS;ksT z(@(e$l>%FO6do5ksus_cta&9|nYXCeyU3aLE{Z!OSc#JcbfA*~CO^%Jgv1g*%hPcF zDb4kglTGfAI5%|e%wND7f=SCVoQtx|{Qvp#Ac7>JbBj$gESqQN5Rw#yqj)p_hOwCx zQW%4XFU&V=7O-c8m;nlPZutCJ?pSjElSFeRoSseSd}cpm)7K^HP1DexLYBp7oM!;z zQ|}$JHubi2ZuKY%FmyI`W0}t?3Zf@iMWNW&j>9?jy<(RzZalV@f<|KvC=oD%gPd?OL^vLjIa|GZwrEVEbT)wz>ykWJ+LrJ|L_BtW*#ds@T@LsdUCeMG-(W3 z&D}lQdfV4;=%2~&RwpF+vKk3g6Xk?o=GWv>jqE%(>D=#MG zuqprQ36qKPB%U_fR5l{tRLDoozd%775rj33hs`TjkV;@OcHolXtp;F;W z{SQ?uvP2Z@lSR`BvumOU%5aAeINT!Iup_szW5Arvr>EJdQq5fl?5#QWTEsvNsGOl& zBsT*)wNu%}=JmyelLk(9gj7gs|`7B;-HblKutwV1B}`>|&HZ zF?5(1Xn-h`SMMp-c=FwWQck)Im3t?# zSfYb4ptolJq$QXz@{&yU^BDI7uZC81s_z`)FXi<69X@8azLW-W^l^IP%Sb?nJw7_hVF(vWp zaWSCwkcQmNJyTrJ1Hn7%+`!ox}3BJ5AH&aLQx4%qvuwZ=gHHo@9iV*H7AGOri$? z6tBERHe4($v0BR=a$psjgz~wwVmxOmKO*G0M?|t^;w?C=0sA8MWrE$EW-YA!HJ_D= zX3u2Vk}1AE!R?bHqx2fH);C0Wtw$)c$9iOoZiII}lpihZpW4MqG^==@glT2S%$3`=-%`nn-O`G?ncTJ)`U*|K+J(5(0vgbqBoGGKMLA_S%+73 zLzK_FGo$q;3H@&kw$p!OVI9xG=Sixpfu3n->w{C$FrLY=D>&D2`oSw+4ashxz`Fz9 zGoCGn<<+6$2-?o*|7zD5XuM_$yJ9C=xSB-_J7C6uLzd&57=zV9pf=*7bHqIX+T{&v!s5&M)m@#u6lmYa@4oNnE!}ysvoEQ{Hk*AP zpV!DQ-iDcMmOCX$Xfw8lXe%h@2hjZS4OkTN z+K)Ak=XMt#AcGa&8PAn5<{Ccrohy^*z_6sILu0msWmiT}FGCDfukAyx8N}bRONuj)=Z?wz`@enmgUtVFUu2DP+Amq{*yaQEWD( z?Ft1XiQzCUr8IL|6)S~C+ZWH9R0XeR*qo%zOabhzNb9_LR8BzCAHx8xbhTiMvaykp zALXb({}^f?ko&m3)=tJ{EvFYJUVDu%FyP~$lr$G8$15i*_hu(5v4W2Fum=RCcZ_1+ z19x|imwTdV6Xk=kpV8Q7dqL-gcLe*)HQ?l+2_GPz{|d%QYG-f-MBVa|hXOcVe2 z&b5K#YW`Ro8r|$qhEOV--xEONp+GSDdnUG6x`uGh&1&45rW$(Btuyb=acmvd>${6T z&)tXc5gQ6k&sd+R?W?#aXj@f|LLGDvfvcVdJn)r+s=HEx_nF|$OP@?tbVWOCacchhZ zFk4qgzkVFZ#3T0li+TqO=otUZ3VnfU zjh;sPCRl?Wi9DVYZ+bkR>^eM#aEx#vDF3kM@wepihhc}RhVi>}C#J{uj!lkMjbukh zs&Zre3A$ZzKBBu)6@G^9#EH?|WNr*mIIaigh$@ioU~&XuB$aD=$BFV*RdTJ#EL63< zCREiO4pm_?cp{v0@$stM$S8g|Zy3jP;+_Upv82N3-#i)}9-OSojpLU9s>X1j6TH7Q zehki|SazwxfkBusZ?B^KnqU_Ay%z4Ds?v=FjH9QMH#{}68_PvilN`J!&^|`~7c`fd z_a~VjexhEWXJ})8#BdUaXmhakL^CpkSY&MdrPObk;~P)RR8cRkW$4_Z!B%uDFWofv zHcK4bZJ`Lm5nkLTHMlx0ool)}cjf38Cm=W=RI!t<6l1utb?2+iOJ& z%!qU<^`DFbGol{!kZ-;34M->)#~j%RH|;0VXppl51`=9E%Ukq|E5=i3T5#0Z)~#DX z8(b1VbJJ9!^TC3s68A$hA(R2H56XE*nWDBn8F^hTPxXL$x2l;mjo|_{YBM$+DPT zP?}){raw$FG%ERiad*IwA{k7MaVQ;A;OB2{1L}G3xXGjp7zaw*)!~M)GMR` zf!Jg+_;H$tHLdXkWM?WlE)d0&^;_TAn0xk#Jzi79FNQ6@`ju!K+0)S{TK{bbY54+b;6&U7b;?aXBhEWY6>w zHZY`6SbZi<_R$j)e2#^1gea!z&x2d5#N=lZ<_9%<#z#|f#xgZ1-4kNw>%Jz>?-$`Q ztth#%BGLy6CPcUyr({}_U!Ve;d0A8J%f%7s_&futr3@pC{SC4C>ZeU4;=~Bau$V7H(~%k?^obI>mW7lyilGmmv2}|Y3$tX2bB+W zwYRnN*UHqKE|WRb&Q6fhQdLXzdCQ88C@iQJTM!HfV=jubld#JJeh&J3I_0Zpq!IjP z0w@qhrg1rjRh@4+pM;y6G0oBsO>XJ0R=BfKbg(<0d-Mv2Qto47xd!VFDsB&x^bbPoHip%AMVQ7UlPgqP98C|evMB3Dk<#7W%r?d1v3H9ZjJ*7VH)rxXD^NM|E_mje^+{HkV!(dOo)4hW))eGbQe{Mij)5dzH56(y zv^Ft2u>!Ofz0pVv6w zT@cuB-lCENOAmGrahH`G>MnN=bB`=r=^pF0@Q1vM|L~*f`M*-<&#&YIxG5>B1;a;p zA0vFEFMO0UuPElIqELww@;wjvp0DsdU*UVc()WC&@A)cczoM8`MWGUhEnWC4agOoj zcTC|S?n<0BzWmnso|pT=6~6o`e9zbV@>}bB&Ymp%l{i(t{Hh8MaaZE3^X0eB_neel z_$zTDzWgGEhqxV41SzHoytzXsp)<9+!Z z?|Xit@BI^f&rk9_Kgsv}6kqdWs`-}4S%xXTyr_Jue3!hOE*6~6G5zVLuAyu}yZ<_n+h3!mu=Cw$?gFP!#; zv%c_lUwF_L-suYu`@*BX@Y%lbgfBeh3-9)Y_xi%;`oib?!f*72FYtvg@`W$)g)jAm zFZYG7auyfGxK6PI2sr*0d0$cbQs+BG;h+<6y%A+jtSCIs$rOd>JJ%J37dT%l3h(Fq zqbR)42?xCSFLF{v;l<81MdAIOFBOFkaQ;*jUgCs`hRmhTrlRnH&P7GxgPhM6g%5Uq zTNFOTIkePUzSlS#io(mB-9_O;om-2-%bgz=g%5Meg5LZN_XZ$;-{uQn?F$z*9?}<8 z9^shzUR~;3x6d(>MU`fIC}m{oC7=| zjz>#;;ibOtfxhrTzVN}m@FBkNYkc8lzVM;G@N!@HFkkp^=R|E{w)|4(D5t9^9CFSm z3a@ZpUld;H?9wnbfdidY&Se_rhu9i76+K_$e6lFK)_G9F|KEc;^5QN$`8G@rA22e6-|$igQ9yxW#GLa7dnGyfa>lM`xU)lg{qpSarBMo~p^! zW;5}+RBa|4kHpjI`ba9;kj7(`4nOG`UU!RV|bCFoCHdc${@-xYFG@1*i zYGaXzd0{Y{&F04NLVbO8LkxjTJeERPQmHV!&*GUhK9bCZ8?yBcDfxo_;_n2%5v{I| zG=!t|@w#v_SJ#k?*4O27vHEB_QyY%gMrv!#H()2o4Q`hgV$~7k8%bs&@!CurrK`)< z)WouNwed(UnM}oE8B_lxeqq*76NA^(L)ZM?cRR}-$QjpA808Lmx5V$pOYmC9xtBH?(N zKbgm_rgbohEgG~Q)lSyeM(Wb>hHM;JMN(RKTeJxJuqKts zWovS%Xfm1!H^ggG;hIcCI+bmxiN(WNQyKHq@;j;{_0`#ELo}6(r^2ahx-Q*Nm&LE9 zCTn9gvHD1?HdzyvlF=~3-au2;MKTf0?c>o*E{9U&GMRJ~PJ#8gx>!7BXwl{l0*N=| z>e0}3b?G_~Uk>D#t%;^Gsk&q;nyHP&BzH7&j@6AdRM*9kR~?!%TvwCLh2!ytI64Js z>(I6-*pS&yJdYbE#G&}L)tO|f25C~cn%Y=6o{Xh&>R2QmjfZQq(M-0+@K8}#q;lCz zO*n>TY(RsgGm)B9I+jE$N9!}SHE~k~yaA)Z_H>%HMzJzTnv129C``B(jTGn4zeM9T znOHOy(`xgbQKm;Hgr9SB0cQZc%dd%U6YLB zf0QYljyBY!YBHdcXf}>>)FSv@iEN$L+k7AfPEF&tZPT&3`dFqu1&#~Xq#M{=VSK6~ z7ppBwAFYlhlkxf}_$(gHrjsC`TuoiB0qs+hK>;KAR+93^>#LKIY!qZ&R~yMSq;jdc zx@>I(2)WUaNrB`gdtf;R{V#z+8;X&CMOWVUy+JE)|J~;~D&jcNzsu zCjqgBWGo4pQm2PpX5~Tc7M&8WzgwFS;{y!J^czT|$Hw5H z6`qz5esbPJ&iO7@?!8fZ==`)O-Oz>~+iPz%;bZNz|09lQRL53ne58<`cgNDk z!@ry5>KtpQy9iU03N$*R0R`GPHX{tA212AJ5omUtmBd!ZVNn7tjuX}ArzoyhC#8Xr!O&^It3ejkwKmUe@qD7h$}A z0P!@(Sw_TrM{4|PA_`wgJOlM5p6NKp5nqRPBf_lFPCN_rMMQpQC}xRC#~Ie}grd+r z(!Ei`mndGLc(vm7ia{;!LdB(u=xX_%?VnW|u25X3SgVLGmh_E^R-T}1BpyvHh$a%G z#fJ5<{kBuX(tn8Gt>N<(FH*c*@hZjZ6s14$-YpuYh-LmCQ~Z>o?bk1A_(8=-6u+nV zBgH2af2H`W;`53xD!!!XYP;J0U##JS6qhR=rFe|uTE&QBz2b?ALJz2q&;zhr;|CN^ zSG00)yN3U#^zmNJ=L3qjDc-4gx8i+@Usilb@mq?IDgH$97m80SKBxF+#lI`QtXQIQ z(8}clG<=BS;fgC1*C*Dc-1f zi{h<{A5;94;ysFARD4kJ5ykH*{z&l&#a}5ttN6U)i;6EPx}~O^^Ar~=9;CQj@hHV( z6xS+76zdgFRBTpkSL{|CP&{2RskmKnSaCveui_gOFHyWg@oL5E72m7)0ma)C?^L{7 z@jk^bD?X(7Eyc$af1>yc#itdYQ~a~y-xXh0EWtp<{$HSYfZ`#Fhbyj7T%%Z}SfkjW zc(P)v;zq?yidz)VR7@)lDvl~nDW0o%f#Ri#S1P_k@dicO+gP6uDc+&@3B}JU-mmz8 z;=_vHQT&18&lI0j{H@|26#uIDPsLXhgJq^13l*0tE>k>0ag}0);yT4z#p4wl71t|v zD)uREQ%opk6?ZD0t+-q9e8r0tFIT)u@jAsD6>m|zRq@r#NNDn6q4J;fg> zKB4$4#b*_tSA0?NB}I3h^1tF@#e)==D;}kIjN)3wh+@6siHgmN?TX!s1B$0BCKb0U z4l7P5?p1uF;w6e#C|<32z2bWnKcIM<;+=|jE8eI0WyOaSzoqz?;!hNRq4>1obBcdf z{JY}IiY1sh2>&Y{pm>Pl;fgC1*CZf%DlStzLUEO1h2lEJTE*iP8x_|pb}IHMZc|JsW)*iTo~^iB@qERL6fal2O7S|y z8x?O+yjAgIil0)vNAZh_4=O&Q_&voRDL$e2E5&COpI3ZQ@g+qUOIM`Rd5Vh_4^mvN zc$DHXifa`kiuH;oDmE*&D|RamD4wpERNSsOtT>^#SMiOCmndGLc(vm7itknYfZ}b6 zcPiekc%R~z6*0U>JM3#-`D)Xx()^B9j3|md1MPjXhH)$3wewC)=_QP*wqQzeP;pH0 z)t2MveWX8BpC6^TTCqw|>>4Pfoi8_Oe7mC9VelNL1}QI00fJe@A;k$rv2!5(1sc9g z(aw*{G`v{xV8z1~S1ML0h861-Uv0bC{;~O3`@n4P|6gmTcWQm#tN201I}|^uXzd4I z((pry-&XvA;^T_HQv99bpB4Y1_=;kg&U3B3;9w0Ou4wI^6&emJT06l>8g5nWP_%ZV z(==@D13NT4s<=z>e8t)N?SD#7)}A+8{>!vI_GNd1X;Jv%M8y`xjfybs$#eT1dJIr5 zAEbDg;tIub#o6fA=Cdz(HX9%OSM6l}@4*Bs)L+ z6yL3Qi{eKVtsU(%8opogtBQ{(KB{Q#XMXxvs_pV>`Nz_U9funA{q>4={Or^4HbqM> zSq<-0JX>+M;`xdfDO$R*&kMQvtZc?;%*&YpVQGA_ZR&hviLh&5M3l!~o z)m0jPr=qpPeo(`AD1K7$9>p&yTKns_HT(m`#}$92_&Y^wccmX4<>V`hWdRdjtaz}Z zwYRR+aD^iM?)ZMa;z^3u&f1~jKE=}%?Yh|x4O{!_E)Acrc(LLYiuB{-_pDv@W)0t} z_;JO%6+f?N?Wy0;@b?sdtoRGX-zZu;>I)ivNik4jzP~`xuG3lj>5&?LjAEr?jpA{N z)^55%!`+IT70*=6C|Y~zxQ6#CzDdz9AIg;O|EqeSKcD|Aa%PX#=Z%VQR=iU2T16{g z-ml@?6z@{}tl}3GA5{FN;$wAkw zP;6E_RdJKzR>g#3PH|XqQt@2H3l%R{e7oZHitkhWkm5%bKc#rDqFvwlx`w}__(R1f z6o0K~*LD7);eRU9#gu$IPf^y{5w`0&uhsZfipMHO6yu6^9j8shor?X6rz_I`m*2DN zH)9&!t@sATOBCO#XxDAtt>Ie~KcaZ2;%5}?dd*ig{D|VCia$~OrJ`M@`J;wkRQ$JM ziSqM)igtbGPz@iYX#D}IG#pd3>oQFmZdcqFU;Cf?!+tq1+wzA4~j1+zN8pXK3Sl+L~)tok&4GCRw~vg z9;eu-xIwX7akJu?iW$Y7inI0GZ1MkB`g339TT}`;fI)CI5tFJ@h$y_QpS{*`WSwk< zst}Wk##IgK4rZM%Cp#0QtEeud}HiCgyTfy->+erZ{a(W8kX@3 z-@8`B?;_&6pVsi_i1_~ZHT+{D%JG7R|3O4~#7@BPv)@oI(NFn(hEYDJ8b{pVG#}vP zxWOIvZZf-P(t&5_WYVclO-#t8+PM39d;=wcM%!QgV{%Uv-2^>RJnJ#*RAV1$HNKEc zRr5pDUe7+`J6JuQ9nw_zHHukN?IRyJ2)>$(m~0<8!L9JsrP)+j2HKc-)ij>^ahL zu4KzMK1Vu^Z8qJ+9O-ELv*{-1NVmw7ZfcHn96N1(yXHuD^#5b-J>a7%y8iK*yZ7#H z`i3MBLfb%q&_k1elu)G#iXaLK(gdVLK#HQ$J4y#JYCsSK1XNT673>v7v0$eNB9A`U z`y>DFcjnIKvQGl|ywCglzxzqf%$YfJ=FFKhXU@#Mn|SCZo`WvOLpSLhbUfpkekY%U zjPaA6@NY=qQu<5%41V+6^6ba;`{4BBRq;zYwtEA= z@*aBuc&z%DwVU^2pcJBOmXI*ssywyTFT^U*0`=`OO7C_RICgBbOO3-zD~r zBM1jae`-EPf8#Y>WE`oWBR}%u$I$W4%G>W^^qY=)_RHt0rr&$qe$6<_gI^Zv&ByTL z-IiCrJkWVRtmB7wTgXS`BOT?-V#4;@5mkzn4YvI|P1Q zRJ50__%-~li{ke=CZ>yF0y>i&7e!YS!VE*b`53w}(fuMo?E@nA+a15A-`P?8mV#d= z!wjk6H$949&XfB4O5}H$hu@kgetaj6&&|xoWLHMN@ymZze~*juU3m_E1HkVl!whLj@~sBHxu_?< ztMFUWc;5rR8mKp)lK6#R(|(-%`r@}Fet&`Aa>ER1N&I?%-zLqY#oI|o1BEAqLE`Iv0P{)}CL-!4>AzCrjc zNxqqI_7E}hDT!as{-S-soV&3UM6^Ax0l&5pZY9r0NAT+iI?89x-No_y6#QtP=2Mb< zi@|Rgh{$g^eoGo}qp}w^AHRZMuFKDyyNi==82E)iXFes#m-A*({5%T8CCN7l{Q4MX zNK4|E2Y&ge=XlMzyEyp{fZu0^$vOD#0KYJ7hy2XByEuN^$_X(GzRr9M>uCEsZ~*); zNb)o1?&A10taM@e`}W-;{m~zlq`$V{*VqV$v?Te&dqwj*@f`dnf#0PUFy377I|Mq8 zck(&-y;S+^zdu-#e4!7aKky;HTh77nDEM8F{ed6p$j_X+i<^({FHpWbk9@bEL%zf+ z7v^6EfFG8+l5g5M_+^1#zEK+JOg7s7CV`*ve>2X(Z+X^*$(QqSk$khx!EZD8@&3bn zN|LW5_!WYP^Dzg%CE0VesuyN2C&4f5@|)-3w*@w7WI(ZK1Ac$8GxuZa_}#V`l;yeQ z!B2|fp#>;2@;%}5%W>)QYT|o-lU)P}`SJI;D7r59<9o&>*xyh<&j_ECYHse%UYA z3;P7W$mhUzA$prb21*UTWaPbcze2trJOzes+~AuA52`z+UfBKQm<&>#*YKi7jZ{tj zCQUDD+JM;svA4JH+CfUgiG62B`loUpmg$nU?p>oInI&w`ONc21gFAz(;l|~*zK1IC{FG)T#ry2lo3>5H$~_T*O>W0}HrVtH%aLP{AN^zF;RJR!A^qU@AJs$@b+zUQ)! z;^YeE=ruw4GFn2mDEVR_TMT3i=gc_9Jjd;&jS2TXZox()V>*wG4j+y9`lj)seSX!! zRT0Rxy>9T3(SwGBd-ZG+?%6KfZtQUU)wi(wm*1L&s}81(xoafdvqn^(;ld*r|B(d9 z1$`VzM9Q_30GVcmpI7-ryn( zkBOW-y17#(FLD+s8AKR$;k;vmLxsmU#|dt`Qus4J)ZLC62am9ioz