You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

355 lines
13 KiB

3 years ago
2 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. # SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
  2. #
  3. # SPDX-License-Identifier: MIT
  4. """
  5. `adafruit_platformdetect.chip`
  6. ================================================================================
  7. Attempt detection of current chip / CPU
  8. * Author(s): Melissa LeBlanc-Williams
  9. Implementation Notes
  10. --------------------
  11. **Software and Dependencies:**
  12. * Linux and Python 3.7 or Higher
  13. """
  14. # imports
  15. from __future__ import annotations
  16. import os
  17. import sys
  18. from typing import Optional, TYPE_CHECKING
  19. from .constants import chips
  20. if TYPE_CHECKING:
  21. from . import Detector
  22. __version__ = "0.0.0-auto.0"
  23. __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PlatformDetect.git"
  24. class Chip:
  25. """Attempt detection of current chip / CPU."""
  26. def __init__(self, detector: Detector) -> None:
  27. self.detector = detector
  28. self._chip_id = None
  29. @property
  30. def id(
  31. self,
  32. ) -> Optional[
  33. str
  34. ]: # pylint: disable=invalid-name,too-many-branches,too-many-return-statements
  35. """Return a unique id for the detected chip, if any."""
  36. # There are some times we want to trick the platform detection
  37. # say if a raspberry pi doesn't have the right ID, or for testing
  38. # Caching
  39. if self._chip_id:
  40. return self._chip_id
  41. if getattr(os, "environ", None) is not None:
  42. try:
  43. return os.environ["BLINKA_FORCECHIP"]
  44. except KeyError: # no forced chip, continue with testing!
  45. pass
  46. # Special cases controlled by environment var
  47. if os.environ.get("BLINKA_FT232H"):
  48. from pyftdi.usbtools import UsbTools
  49. # look for it based on PID/VID
  50. count = len(UsbTools.find_all([(0x0403, 0x6014)]))
  51. if count == 0:
  52. raise RuntimeError(
  53. "BLINKA_FT232H environment variable "
  54. + "set, but no FT232H device found"
  55. )
  56. self._chip_id = chips.FT232H
  57. return self._chip_id
  58. if os.environ.get("BLINKA_FT2232H"):
  59. from pyftdi.usbtools import UsbTools
  60. # look for it based on PID/VID
  61. count = len(UsbTools.find_all([(0x0403, 0x6010)]))
  62. if count == 0:
  63. raise RuntimeError(
  64. "BLINKA_FT2232H environment variable "
  65. + "set, but no FT2232H device found"
  66. )
  67. self._chip_id = chips.FT2232H
  68. return self._chip_id
  69. if os.environ.get("BLINKA_MCP2221"):
  70. import hid
  71. # look for it based on PID/VID
  72. for dev in hid.enumerate():
  73. if dev["vendor_id"] == 0x04D8 and dev["product_id"] == 0x00DD:
  74. self._chip_id = chips.MCP2221
  75. return self._chip_id
  76. raise RuntimeError(
  77. "BLINKA_MCP2221 environment variable "
  78. + "set, but no MCP2221 device found"
  79. )
  80. if os.environ.get("BLINKA_U2IF"):
  81. import hid
  82. # look for it based on PID/VID
  83. for dev in hid.enumerate():
  84. vendor = dev["vendor_id"]
  85. product = dev["product_id"]
  86. # NOTE: If any products are added here, they need added
  87. # to _rp2040_u2if_id() in board.py as well.
  88. if (
  89. # Raspberry Pi Pico
  90. vendor == 0xCAFE
  91. and product == 0x4005
  92. ) or (
  93. # Feather RP2040
  94. # Itsy Bitsy RP2040
  95. # QT Py RP2040
  96. # QT2040 Trinkey
  97. # MacroPad RP2040
  98. vendor == 0x239A
  99. and product in (0x00F1, 0x00FD, 0x00F7, 0x0109, 0x0107)
  100. ):
  101. self._chip_id = chips.RP2040_U2IF
  102. return self._chip_id
  103. raise RuntimeError(
  104. "BLINKA_U2IF environment variable "
  105. + "set, but no compatible device found"
  106. )
  107. if os.environ.get("BLINKA_GREATFET"):
  108. import usb
  109. if usb.core.find(idVendor=0x1D50, idProduct=0x60E6) is not None:
  110. self._chip_id = chips.LPC4330
  111. return self._chip_id
  112. raise RuntimeError(
  113. "BLINKA_GREATFET environment variable "
  114. + "set, but no GreatFET device found"
  115. )
  116. if os.environ.get("BLINKA_NOVA"):
  117. self._chip_id = chips.BINHO
  118. return self._chip_id
  119. platform = sys.platform
  120. if platform in ("linux", "linux2"):
  121. self._chip_id = self._linux_id()
  122. return self._chip_id
  123. if platform == "esp8266":
  124. self._chip_id = chips.ESP8266
  125. return self._chip_id
  126. if platform == "samd21":
  127. self._chip_id = chips.SAMD21
  128. return self._chip_id
  129. if platform == "pyboard":
  130. self._chip_id = chips.STM32F405
  131. return self._chip_id
  132. if platform == "rp2":
  133. self._chip_id = chips.RP2040
  134. return self._chip_id
  135. # nothing found!
  136. return None
  137. # pylint: enable=invalid-name
  138. def _linux_id(self) -> Optional[str]:
  139. # pylint: disable=too-many-branches,too-many-statements
  140. # pylint: disable=too-many-return-statements
  141. """Attempt to detect the CPU on a computer running the Linux kernel."""
  142. if self.detector.check_dt_compatible_value("sun8i-h3"):
  143. return chips.H3
  144. if self.detector.check_dt_compatible_value("qcom,apq8016"):
  145. return chips.APQ8016
  146. if self.detector.check_dt_compatible_value("fu500"):
  147. return chips.HFU540
  148. if self.detector.check_dt_compatible_value("sun20iw1p1"):
  149. return chips.C906
  150. # Older Builds
  151. if self.detector.check_dt_compatible_value("sifive"):
  152. return chips.JH71x0
  153. # Newer Builds
  154. if self.detector.check_dt_compatible_value("jh7100"):
  155. return chips.JH71x0
  156. if self.detector.check_dt_compatible_value("sun8i-a33"):
  157. return chips.A33
  158. if self.detector.check_dt_compatible_value("rockchip,rk3308"):
  159. return chips.RK3308
  160. if self.detector.check_dt_compatible_value("rockchip,rk3399"):
  161. return chips.RK3399
  162. if self.detector.check_dt_compatible_value("rockchip,rk3288"):
  163. return chips.RK3288
  164. if self.detector.check_dt_compatible_value("rockchip,rk3328"):
  165. return chips.RK3328
  166. if self.detector.check_dt_compatible_value("amlogic,a311d"):
  167. return chips.A311D
  168. if self.detector.check_dt_compatible_value("st,stm32mp157"):
  169. return chips.STM32MP157
  170. if self.detector.check_dt_compatible_value("sun50i-a64"):
  171. return chips.A64
  172. if self.detector.check_dt_compatible_value("sun50i-h5"):
  173. return chips.H5
  174. if self.detector.check_dt_compatible_value("sun50i-h616"):
  175. return chips.H616
  176. if self.detector.check_dt_compatible_value("sun50iw9"):
  177. return chips.H616
  178. if self.detector.check_dt_compatible_value("sun50i-h6"):
  179. return chips.H6
  180. if self.detector.check_dt_compatible_value("mediatek,mt8167"):
  181. return chips.MT8167
  182. if self.detector.check_dt_compatible_value("imx6ull"):
  183. return chips.IMX6ULL
  184. if self.detector.check_dt_compatible_value("ti,j721e"):
  185. return chips.TDA4VM
  186. linux_id = None
  187. hardware = self.detector.get_cpuinfo_field("Hardware")
  188. if hardware is None:
  189. vendor_id = self.detector.get_cpuinfo_field("vendor_id")
  190. if vendor_id == "AuthenticAMD":
  191. model_name = self.detector.get_cpuinfo_field("model name").upper()
  192. if "RYZEN EMBEDDED V1202B" in model_name:
  193. linux_id = chips.RYZEN_V1202B
  194. if "RYZEN EMBEDDED V1605B" in model_name:
  195. linux_id = chips.RYZEN_V1605B
  196. else:
  197. linux_id = chips.GENERIC_X86
  198. ## print("linux_id = ", linux_id)
  199. elif vendor_id == "GenuineIntel":
  200. model_name = self.detector.get_cpuinfo_field("model name").upper()
  201. ## print('model_name =', model_name)
  202. if "N3710" in model_name:
  203. linux_id = chips.PENTIUM_N3710
  204. elif "X5-Z8350" in model_name:
  205. linux_id = chips.ATOM_X5_Z8350
  206. else:
  207. linux_id = chips.GENERIC_X86
  208. ## print("linux_id = ", linux_id)
  209. compatible = self.detector.get_device_compatible()
  210. if compatible and "tegra" in compatible:
  211. compats = compatible.split("\x00")
  212. if "nvidia,tegra210" in compats:
  213. linux_id = chips.T210
  214. elif "nvidia,tegra186" in compats:
  215. linux_id = chips.T186
  216. elif "nvidia,tegra194" in compats:
  217. linux_id = chips.T194
  218. if compatible and "imx8m" in compatible:
  219. linux_id = chips.IMX8MX
  220. if compatible and "odroid-c2" in compatible:
  221. linux_id = chips.S905
  222. if compatible and "amlogic" in compatible:
  223. compatible_list = (
  224. compatible.replace("\x00", ",").replace(" ", "").split(",")
  225. )
  226. if "g12a" in compatible_list:
  227. # 'sm1' is correct for S905X3, but some kernels use 'g12a'
  228. return chips.S905X3
  229. if "g12b" in compatible_list:
  230. return chips.S922X
  231. if "sm1" in compatible_list:
  232. return chips.S905X3
  233. if "vim3amlogic" in compatible_list:
  234. return chips.A311D
  235. if compatible and "sun50i-a64" in compatible:
  236. linux_id = chips.A64
  237. if compatible and "sun50i-h6" in compatible:
  238. linux_id = chips.H6
  239. if compatible and "sun50i-h5" in compatible:
  240. linux_id = chips.H5
  241. if compatible and "odroid-xu4" in compatible:
  242. linux_id = chips.EXYNOS5422
  243. cpu_model = self.detector.get_cpuinfo_field("cpu model")
  244. if cpu_model is not None:
  245. if "MIPS 24Kc" in cpu_model:
  246. linux_id = chips.MIPS24KC
  247. elif "MIPS 24KEc" in cpu_model:
  248. linux_id = chips.MIPS24KEC
  249. # we still haven't identified the hardware, so
  250. # convert it to a list and let the remaining
  251. # conditions attempt.
  252. if not linux_id:
  253. hardware = [
  254. entry.replace("\x00", "") for entry in compatible.split(",")
  255. ]
  256. if not linux_id:
  257. if "AM33XX" in hardware:
  258. linux_id = chips.AM33XX
  259. elif "DRA74X" in hardware:
  260. linux_id = chips.DRA74X
  261. elif "sun8i" in hardware:
  262. linux_id = chips.SUN8I
  263. elif "ODROIDC" in hardware:
  264. linux_id = chips.S805
  265. elif "ODROID-C2" in hardware:
  266. linux_id = chips.S905
  267. elif "ODROID-N2" in hardware:
  268. linux_id = chips.S922X
  269. elif "ODROID-C4" in hardware:
  270. linux_id = chips.S905X3
  271. elif "ODROID-XU4" in hardware:
  272. linux_id = chips.EXYNOS5422
  273. elif "KHADAS-VIM3" in hardware:
  274. linux_id = chips.A311D
  275. elif "SAMA5" in hardware:
  276. linux_id = chips.SAMA5
  277. elif "Pinebook" in hardware:
  278. linux_id = chips.A64
  279. elif "ASUS_TINKER_BOARD" in hardware:
  280. linux_id = chips.RK3288
  281. elif "Xilinx Zynq" in hardware:
  282. compatible = self.detector.get_device_compatible()
  283. if compatible and "xlnx,zynq-7000" in compatible:
  284. linux_id = chips.ZYNQ7000
  285. else:
  286. if isinstance(hardware, str):
  287. if hardware.upper() in chips.BCM_RANGE:
  288. linux_id = chips.BCM2XXX
  289. elif isinstance(hardware, list):
  290. if {model.upper() for model in hardware} & chips.BCM_RANGE:
  291. linux_id = chips.BCM2XXX
  292. return linux_id
  293. def __getattr__(self, attr: str) -> bool:
  294. """
  295. Detect whether the given attribute is the currently-detected chip. See
  296. list of constants at the top of this module for available options.
  297. """
  298. if self.id == attr:
  299. return True
  300. return False