usb_mux.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /*
  2. * Copyright (C) 2021 System76
  3. * Copyright (C) 2021 Jimmy Cassis <KernelOops@outlook.com>
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. #include "usb_mux.h"
  19. #include <stdbool.h>
  20. #include "i2c_master.h"
  21. #include "wait.h"
  22. #define REG_PF1_CTL 0xBF800C04
  23. #define REG_PIO64_OEN 0xBF800908
  24. #define REG_PIO64_OUT 0xBF800928
  25. #define REG_VID 0xBF803000
  26. #define REG_PRT_SWAP 0xBF8030FA
  27. #define REG_USB3_HUB_VID 0xBFD2E548
  28. #define REG_RUNTIME_FLAGS2 0xBFD23408
  29. #define REG_I2S_FEAT_SEL 0xBFD23412
  30. struct USB7206 {
  31. uint8_t addr;
  32. };
  33. struct USB7206 usb_hub = {.addr = 0x2D};
  34. // Perform USB7206 register access.
  35. // Returns zero on success or a negative number on error.
  36. i2c_status_t usb7206_register_access(struct USB7206* self) {
  37. uint8_t register_access[3] = {
  38. 0x99,
  39. 0x37,
  40. 0x00,
  41. };
  42. return i2c_transmit(self->addr << 1, register_access, sizeof(register_access), I2C_TIMEOUT);
  43. }
  44. // Read data from USB7206 register region.
  45. // Returns number of bytes read on success or a negative number on error.
  46. i2c_status_t usb7206_read_reg(struct USB7206* self, uint32_t addr, uint8_t* data, int length) {
  47. i2c_status_t status;
  48. uint8_t register_read[9] = {
  49. 0x00, // Buffer address MSB: always 0
  50. 0x00, // Buffer address LSB: always 0
  51. 0x06, // Number of bytes to write to command block buffer area
  52. 0x01, // Direction: 0 = write, 1 = read
  53. (uint8_t)length, // Number of bytes to read from register
  54. (uint8_t)(addr >> 24), // Register address byte 3
  55. (uint8_t)(addr >> 16), // Register address byte 2
  56. (uint8_t)(addr >> 8), // Register address byte 1
  57. (uint8_t)(addr >> 0), // Register address byte 0
  58. };
  59. status = i2c_transmit(self->addr << 1, register_read, sizeof(register_read), I2C_TIMEOUT);
  60. if (status < 0) {
  61. return status;
  62. }
  63. status = usb7206_register_access(self);
  64. if (status < 0) {
  65. return status;
  66. }
  67. uint8_t read[2] = {
  68. 0x00, // Buffer address MSB: always 0
  69. 0x06, // Buffer address LSB: 6 to skip header
  70. };
  71. status = i2c_start((self->addr << 1) | I2C_WRITE, I2C_TIMEOUT);
  72. if (status >= 0) {
  73. for (uint16_t i = 0; i < sizeof(read); i++) {
  74. status = i2c_write(read[i], I2C_TIMEOUT);
  75. if (status < 0) {
  76. goto error;
  77. }
  78. }
  79. } else {
  80. goto error;
  81. }
  82. status = i2c_start((self->addr << 1) | I2C_READ, I2C_TIMEOUT);
  83. if (status < 0) {
  84. goto error;
  85. }
  86. // Read and ignore buffer length
  87. status = i2c_read_ack(I2C_TIMEOUT);
  88. if (status < 0) {
  89. goto error;
  90. }
  91. for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {
  92. status = i2c_read_ack(I2C_TIMEOUT);
  93. if (status >= 0) {
  94. data[i] = (uint8_t)status;
  95. }
  96. }
  97. if (status >= 0) {
  98. status = i2c_read_nack(I2C_TIMEOUT);
  99. if (status >= 0) {
  100. data[(length - 1)] = (uint8_t)status;
  101. }
  102. }
  103. error:
  104. i2c_stop();
  105. return (status < 0) ? status : length;
  106. }
  107. // Read 32-bit value from USB7206 register region.
  108. // Returns number of bytes read on success or a negative number on error.
  109. i2c_status_t usb7206_read_reg_32(struct USB7206* self, uint32_t addr, uint32_t* data) {
  110. i2c_status_t status;
  111. // First byte is available length
  112. uint8_t bytes[4] = {0, 0, 0, 0};
  113. status = usb7206_read_reg(self, addr, bytes, sizeof(bytes));
  114. if (status < 0) {
  115. return status;
  116. }
  117. // Convert from little endian
  118. *data = (((uint32_t)bytes[0]) << 0) | (((uint32_t)bytes[1]) << 8) | (((uint32_t)bytes[2]) << 16) | (((uint32_t)bytes[3]) << 24);
  119. return status;
  120. }
  121. // Write data to USB7206 register region.
  122. // Returns number of bytes written on success or a negative number on error.
  123. i2c_status_t usb7206_write_reg(struct USB7206* self, uint32_t addr, uint8_t* data, int length) {
  124. i2c_status_t status;
  125. uint8_t register_write[9] = {
  126. 0x00, // Buffer address MSB: always 0
  127. 0x00, // Buffer address LSB: always 0
  128. ((uint8_t)length) + 6, // Number of bytes to write to command block buffer area
  129. 0x00, // Direction: 0 = write, 1 = read
  130. (uint8_t)length, // Number of bytes to write to register
  131. (uint8_t)(addr >> 24), // Register address byte 3
  132. (uint8_t)(addr >> 16), // Register address byte 2
  133. (uint8_t)(addr >> 8), // Register address byte 1
  134. (uint8_t)(addr >> 0), // Register address byte 0
  135. };
  136. status = i2c_start((self->addr << 1) | I2C_WRITE, I2C_TIMEOUT);
  137. if (status >= 0) {
  138. for (uint16_t i = 0; i < sizeof(register_write); i++) {
  139. status = i2c_write(register_write[i], I2C_TIMEOUT);
  140. if (status < 0) {
  141. goto error;
  142. }
  143. }
  144. for (uint16_t i = 0; i < length; i++) {
  145. status = i2c_write(data[i], I2C_TIMEOUT);
  146. if (status < 0) {
  147. goto error;
  148. }
  149. }
  150. } else {
  151. goto error;
  152. }
  153. i2c_stop();
  154. status = usb7206_register_access(self);
  155. if (status < 0) {
  156. goto error;
  157. }
  158. error:
  159. i2c_stop();
  160. return (status < 0) ? status : length;
  161. }
  162. // Write 8-bit value to USB7206 register region.
  163. // Returns number of bytes written on success or a negative number on error.
  164. i2c_status_t usb7206_write_reg_8(struct USB7206* self, uint32_t addr, uint8_t data) { return usb7206_write_reg(self, addr, &data, sizeof(data)); }
  165. // Write 32-bit value to USB7206 register region.
  166. // Returns number of bytes written on success or a negative number on error.
  167. i2c_status_t usb7206_write_reg_32(struct USB7206* self, uint32_t addr, uint32_t data) {
  168. // Convert to little endian
  169. uint8_t bytes[4] = {
  170. (uint8_t)(data >> 0),
  171. (uint8_t)(data >> 8),
  172. (uint8_t)(data >> 16),
  173. (uint8_t)(data >> 24),
  174. };
  175. return usb7206_write_reg(self, addr, bytes, sizeof(bytes));
  176. }
  177. // Initialize USB7206.
  178. // Returns zero on success or a negative number on error.
  179. int usb7206_init(struct USB7206* self) {
  180. i2c_status_t status;
  181. uint32_t data;
  182. // DM and DP are swapped on ports 2 and 3
  183. status = usb7206_write_reg_8(self, REG_PRT_SWAP, 0x0C);
  184. if (status < 0) {
  185. return status;
  186. }
  187. // Disable audio
  188. status = usb7206_write_reg_8(self, REG_I2S_FEAT_SEL, 0);
  189. if (status < 0) {
  190. return status;
  191. }
  192. // Set HFC_DISABLE
  193. data = 0;
  194. status = usb7206_read_reg_32(self, REG_RUNTIME_FLAGS2, &data);
  195. if (status < 0) {
  196. return status;
  197. }
  198. data |= 1;
  199. status = usb7206_write_reg_32(self, REG_RUNTIME_FLAGS2, data);
  200. if (status < 0) {
  201. return status;
  202. }
  203. // Set Vendor ID and Product ID of USB 2 hub
  204. status = usb7206_write_reg_32(self, REG_VID, 0x00033384);
  205. if (status < 0) {
  206. return status;
  207. }
  208. // Set Vendor ID and Product ID of USB 3 hub
  209. status = usb7206_write_reg_32(self, REG_USB3_HUB_VID, 0x00043384);
  210. if (status < 0) {
  211. return status;
  212. }
  213. return 0;
  214. }
  215. // Attach USB7206.
  216. // Returns bytes written on success or a negative number on error.
  217. i2c_status_t usb7206_attach(struct USB7206* self) {
  218. uint8_t data[3] = {
  219. 0xAA,
  220. 0x56,
  221. 0x00,
  222. };
  223. return i2c_transmit(self->addr << 1, data, sizeof(data), I2C_TIMEOUT);
  224. }
  225. struct USB7206_GPIO {
  226. struct USB7206* usb7206;
  227. uint32_t pf;
  228. };
  229. struct USB7206_GPIO usb_gpio_sink = {.usb7206 = &usb_hub, .pf = 29}; // UP_SEL = PF29 = GPIO93
  230. struct USB7206_GPIO usb_gpio_source_left = {.usb7206 = &usb_hub, .pf = 10}; // CL_SEL = PF10 = GPIO74
  231. struct USB7206_GPIO usb_gpio_source_right = {.usb7206 = &usb_hub, .pf = 25}; // CR_SEL = PF25 = GPIO88
  232. // Set USB7206 GPIO to specified value.
  233. // Returns zero on success or negative number on error.
  234. i2c_status_t usb7206_gpio_set(struct USB7206_GPIO* self, bool value) {
  235. i2c_status_t status;
  236. uint32_t data;
  237. data = 0;
  238. status = usb7206_read_reg_32(self->usb7206, REG_PIO64_OUT, &data);
  239. if (status < 0) {
  240. return status;
  241. }
  242. if (value) {
  243. data |= (((uint32_t)1) << self->pf);
  244. } else {
  245. data &= ~(((uint32_t)1) << self->pf);
  246. }
  247. status = usb7206_write_reg_32(self->usb7206, REG_PIO64_OUT, data);
  248. if (status < 0) {
  249. return status;
  250. }
  251. return 0;
  252. }
  253. // Initialize USB7206 GPIO.
  254. // Returns zero on success or a negative number on error.
  255. i2c_status_t usb7206_gpio_init(struct USB7206_GPIO* self) {
  256. i2c_status_t status;
  257. uint32_t data;
  258. // Set programmable function to GPIO
  259. status = usb7206_write_reg_8(self->usb7206, REG_PF1_CTL + (self->pf - 1), 0);
  260. if (status < 0) {
  261. return status;
  262. }
  263. // Set GPIO to false by default
  264. usb7206_gpio_set(self, false);
  265. // Set GPIO to output
  266. data = 0;
  267. status = usb7206_read_reg_32(self->usb7206, REG_PIO64_OEN, &data);
  268. if (status < 0) {
  269. return status;
  270. }
  271. data |= (((uint32_t)1) << self->pf);
  272. status = usb7206_write_reg_32(self->usb7206, REG_PIO64_OEN, data);
  273. if (status < 0) {
  274. return status;
  275. }
  276. return 0;
  277. }
  278. struct PTN5110 {
  279. uint8_t addr;
  280. uint8_t cc;
  281. struct USB7206_GPIO* gpio;
  282. };
  283. struct PTN5110 usb_sink = {.addr = 0x51, .gpio = &usb_gpio_sink};
  284. struct PTN5110 usb_source_left = {.addr = 0x52, .gpio = &usb_gpio_source_left};
  285. struct PTN5110 usb_source_right = {.addr = 0x50, .gpio = &usb_gpio_source_right};
  286. // Initialize PTN5110.
  287. // Returns zero on success or a negative number on error.
  288. i2c_status_t ptn5110_init(struct PTN5110* self) {
  289. // Set last cc to invalid value, to force update
  290. self->cc = 0xFF;
  291. // Initialize GPIO
  292. return usb7206_gpio_init(self->gpio);
  293. }
  294. // Read PTN5110 CC_STATUS.
  295. // Returns zero on success or a negative number on error.
  296. i2c_status_t ptn5110_get_cc_status(struct PTN5110* self, uint8_t* cc) { return i2c_readReg(self->addr << 1, 0x1D, cc, 1, I2C_TIMEOUT); }
  297. // Set PTN5110 SSMUX orientation.
  298. // Returns zero on success or a negative number on error.
  299. i2c_status_t ptn5110_set_ssmux(struct PTN5110* self, bool orientation) { return usb7206_gpio_set(self->gpio, orientation); }
  300. // Write PTN5110 COMMAND.
  301. // Returns zero on success or negative number on error.
  302. i2c_status_t ptn5110_command(struct PTN5110* self, uint8_t command) { return i2c_writeReg(self->addr << 1, 0x23, &command, 1, I2C_TIMEOUT); }
  303. // Set orientation of PTN5110 operating as a sink, call this once.
  304. // Returns zero on success or a negative number on error.
  305. i2c_status_t ptn5110_sink_set_orientation(struct PTN5110* self) {
  306. i2c_status_t status;
  307. uint8_t cc;
  308. status = ptn5110_get_cc_status(self, &cc);
  309. if (status < 0) {
  310. return status;
  311. }
  312. if ((cc & 0x03) == 0) {
  313. status = ptn5110_set_ssmux(self, false);
  314. if (status < 0) {
  315. return status;
  316. }
  317. } else {
  318. status = ptn5110_set_ssmux(self, true);
  319. if (status < 0) {
  320. return status;
  321. }
  322. }
  323. return 0;
  324. }
  325. // Update PTN5110 operating as a source, call this repeatedly.
  326. // Returns zero on success or a negative number on error.
  327. i2c_status_t ptn5110_source_update(struct PTN5110* self) {
  328. i2c_status_t status;
  329. uint8_t cc;
  330. status = ptn5110_get_cc_status(self, &cc);
  331. if (status < 0) {
  332. return status;
  333. }
  334. if (cc != self->cc) {
  335. // WARNING: Setting this here will disable retries
  336. self->cc = cc;
  337. bool connected = false;
  338. bool orientation = false;
  339. if ((cc & 0x03) == 2) {
  340. connected = true;
  341. orientation = true;
  342. } else if (((cc >> 2) & 0x03) == 2) {
  343. connected = true;
  344. orientation = false;
  345. }
  346. if (connected) {
  347. // Set SS mux orientation
  348. status = ptn5110_set_ssmux(self, orientation);
  349. if (status < 0) {
  350. return status;
  351. }
  352. // Enable source Vbus command
  353. status = ptn5110_command(self, 0b01110111);
  354. if (status < 0) {
  355. return status;
  356. }
  357. } else {
  358. // Disable source Vbus command
  359. status = ptn5110_command(self, 0b01100110);
  360. if (status < 0) {
  361. return status;
  362. }
  363. }
  364. }
  365. return 0;
  366. }
  367. void usb_mux_event(void) {
  368. // Run this on every 1000th matrix scan
  369. static int cycle = 0;
  370. if (cycle >= 1000) {
  371. cycle = 0;
  372. ptn5110_source_update(&usb_source_left);
  373. ptn5110_source_update(&usb_source_right);
  374. } else {
  375. cycle += 1;
  376. }
  377. }
  378. void usb_mux_init(void) {
  379. // Run I2C bus at 100 kHz
  380. i2c_init();
  381. // Set up hub
  382. usb7206_init(&usb_hub);
  383. // Set up sink
  384. ptn5110_init(&usb_sink);
  385. ptn5110_sink_set_orientation(&usb_sink);
  386. // Set up sources
  387. ptn5110_init(&usb_source_left);
  388. ptn5110_init(&usb_source_right);
  389. // Attach hub
  390. usb7206_attach(&usb_hub);
  391. // Ensure orientation is correct after attaching hub
  392. // TODO: Find reason why GPIO for sink orientation is reset
  393. for (int i = 0; i < 100; i++) {
  394. ptn5110_sink_set_orientation(&usb_sink);
  395. wait_ms(10);
  396. }
  397. }