2
0

pmbus_device.c 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929
  1. /*
  2. * PMBus wrapper over SMBus
  3. *
  4. * Copyright 2021 Google LLC
  5. *
  6. * SPDX-License-Identifier: GPL-2.0-or-later
  7. */
  8. #include "qemu/osdep.h"
  9. #include <math.h>
  10. #include "hw/i2c/pmbus_device.h"
  11. #include "migration/vmstate.h"
  12. #include "qemu/module.h"
  13. #include "qemu/log.h"
  14. uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value)
  15. {
  16. /* R is usually negative to fit large readings into 16 bits */
  17. uint16_t y = (c.m * value + c.b) * pow(10, c.R);
  18. return y;
  19. }
  20. uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value)
  21. {
  22. /* X = (Y * 10^-R - b) / m */
  23. uint32_t x = (value / pow(10, c.R) - c.b) / c.m;
  24. return x;
  25. }
  26. uint16_t pmbus_data2linear_mode(uint16_t value, int exp)
  27. {
  28. /* L = D * 2^(-e) */
  29. if (exp < 0) {
  30. return value << (-exp);
  31. }
  32. return value >> exp;
  33. }
  34. uint16_t pmbus_linear_mode2data(uint16_t value, int exp)
  35. {
  36. /* D = L * 2^e */
  37. if (exp < 0) {
  38. return value >> (-exp);
  39. }
  40. return value << exp;
  41. }
  42. void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len)
  43. {
  44. if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) {
  45. qemu_log_mask(LOG_GUEST_ERROR,
  46. "PMBus device tried to send too much data");
  47. len = 0;
  48. }
  49. for (int i = len - 1; i >= 0; i--) {
  50. pmdev->out_buf[i + pmdev->out_buf_len] = data[len - i - 1];
  51. }
  52. pmdev->out_buf_len += len;
  53. }
  54. /* Internal only, convert unsigned ints to the little endian bus */
  55. static void pmbus_send_uint(PMBusDevice *pmdev, uint64_t data, uint8_t size)
  56. {
  57. uint8_t bytes[8];
  58. g_assert(size <= 8);
  59. for (int i = 0; i < size; i++) {
  60. bytes[i] = data & 0xFF;
  61. data = data >> 8;
  62. }
  63. pmbus_send(pmdev, bytes, size);
  64. }
  65. void pmbus_send8(PMBusDevice *pmdev, uint8_t data)
  66. {
  67. pmbus_send_uint(pmdev, data, 1);
  68. }
  69. void pmbus_send16(PMBusDevice *pmdev, uint16_t data)
  70. {
  71. pmbus_send_uint(pmdev, data, 2);
  72. }
  73. void pmbus_send32(PMBusDevice *pmdev, uint32_t data)
  74. {
  75. pmbus_send_uint(pmdev, data, 4);
  76. }
  77. void pmbus_send64(PMBusDevice *pmdev, uint64_t data)
  78. {
  79. pmbus_send_uint(pmdev, data, 8);
  80. }
  81. void pmbus_send_string(PMBusDevice *pmdev, const char *data)
  82. {
  83. if (!data) {
  84. qemu_log_mask(LOG_GUEST_ERROR,
  85. "%s: %s: uninitialised read from 0x%02x\n",
  86. __func__, DEVICE(pmdev)->canonical_path, pmdev->code);
  87. return;
  88. }
  89. size_t len = strlen(data);
  90. g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
  91. pmdev->out_buf[len + pmdev->out_buf_len] = len;
  92. for (int i = len - 1; i >= 0; i--) {
  93. pmdev->out_buf[i + pmdev->out_buf_len] = data[len - 1 - i];
  94. }
  95. pmdev->out_buf_len += len + 1;
  96. }
  97. uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len)
  98. {
  99. /* dest may contain data from previous writes */
  100. memset(dest, 0, len);
  101. /* Exclude command code from return value */
  102. pmdev->in_buf++;
  103. pmdev->in_buf_len--;
  104. /* The byte after the command code denotes the length */
  105. uint8_t sent_len = pmdev->in_buf[0];
  106. if (sent_len != pmdev->in_buf_len - 1) {
  107. qemu_log_mask(LOG_GUEST_ERROR,
  108. "%s: length mismatch. Expected %d bytes, got %d bytes\n",
  109. __func__, sent_len, pmdev->in_buf_len - 1);
  110. }
  111. /* exclude length byte */
  112. pmdev->in_buf++;
  113. pmdev->in_buf_len--;
  114. if (pmdev->in_buf_len < len) {
  115. len = pmdev->in_buf_len;
  116. }
  117. memcpy(dest, pmdev->in_buf, len);
  118. return len;
  119. }
  120. static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
  121. {
  122. uint64_t ret = 0;
  123. /* Exclude command code from return value */
  124. pmdev->in_buf++;
  125. pmdev->in_buf_len--;
  126. for (int i = pmdev->in_buf_len - 1; i >= 0; i--) {
  127. ret = ret << 8 | pmdev->in_buf[i];
  128. }
  129. return ret;
  130. }
  131. uint8_t pmbus_receive8(PMBusDevice *pmdev)
  132. {
  133. if (pmdev->in_buf_len - 1 != 1) {
  134. qemu_log_mask(LOG_GUEST_ERROR,
  135. "%s: length mismatch. Expected 1 byte, got %d bytes\n",
  136. __func__, pmdev->in_buf_len - 1);
  137. }
  138. return pmbus_receive_uint(pmdev);
  139. }
  140. uint16_t pmbus_receive16(PMBusDevice *pmdev)
  141. {
  142. if (pmdev->in_buf_len - 1 != 2) {
  143. qemu_log_mask(LOG_GUEST_ERROR,
  144. "%s: length mismatch. Expected 2 bytes, got %d bytes\n",
  145. __func__, pmdev->in_buf_len - 1);
  146. }
  147. return pmbus_receive_uint(pmdev);
  148. }
  149. uint32_t pmbus_receive32(PMBusDevice *pmdev)
  150. {
  151. if (pmdev->in_buf_len - 1 != 4) {
  152. qemu_log_mask(LOG_GUEST_ERROR,
  153. "%s: length mismatch. Expected 4 bytes, got %d bytes\n",
  154. __func__, pmdev->in_buf_len - 1);
  155. }
  156. return pmbus_receive_uint(pmdev);
  157. }
  158. uint64_t pmbus_receive64(PMBusDevice *pmdev)
  159. {
  160. if (pmdev->in_buf_len - 1 != 8) {
  161. qemu_log_mask(LOG_GUEST_ERROR,
  162. "%s: length mismatch. Expected 8 bytes, got %d bytes\n",
  163. __func__, pmdev->in_buf_len - 1);
  164. }
  165. return pmbus_receive_uint(pmdev);
  166. }
  167. static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev)
  168. {
  169. if (pmdev->out_buf_len == 0) {
  170. qemu_log_mask(LOG_GUEST_ERROR,
  171. "%s: tried to read from empty buffer",
  172. __func__);
  173. return PMBUS_ERR_BYTE;
  174. }
  175. uint8_t data = pmdev->out_buf[pmdev->out_buf_len - 1];
  176. pmdev->out_buf_len--;
  177. return data;
  178. }
  179. static void pmbus_quick_cmd(SMBusDevice *smd, uint8_t read)
  180. {
  181. PMBusDevice *pmdev = PMBUS_DEVICE(smd);
  182. PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
  183. if (pmdc->quick_cmd) {
  184. pmdc->quick_cmd(pmdev, read);
  185. }
  186. }
  187. static uint8_t pmbus_pages_num(PMBusDevice *pmdev)
  188. {
  189. const PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev);
  190. /* some PMBus devices don't use the PAGE command, so they get 1 page */
  191. return k->device_num_pages ? : 1;
  192. }
  193. static void pmbus_pages_alloc(PMBusDevice *pmdev)
  194. {
  195. pmdev->num_pages = pmbus_pages_num(pmdev);
  196. pmdev->pages = g_new0(PMBusPage, pmdev->num_pages);
  197. }
  198. void pmbus_check_limits(PMBusDevice *pmdev)
  199. {
  200. for (int i = 0; i < pmdev->num_pages; i++) {
  201. if ((pmdev->pages[i].operation & PB_OP_ON) == 0) {
  202. continue; /* don't check powered off devices */
  203. }
  204. if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_fault_limit) {
  205. pmdev->pages[i].status_word |= PB_STATUS_VOUT;
  206. pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_FAULT;
  207. }
  208. if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_warn_limit) {
  209. pmdev->pages[i].status_word |= PB_STATUS_VOUT;
  210. pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_WARN;
  211. }
  212. if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_warn_limit) {
  213. pmdev->pages[i].status_word |= PB_STATUS_VOUT;
  214. pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_WARN;
  215. }
  216. if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_fault_limit) {
  217. pmdev->pages[i].status_word |= PB_STATUS_VOUT;
  218. pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_FAULT;
  219. }
  220. if (pmdev->pages[i].read_vin > pmdev->pages[i].vin_ov_warn_limit) {
  221. pmdev->pages[i].status_word |= PB_STATUS_INPUT;
  222. pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_OV_WARN;
  223. }
  224. if (pmdev->pages[i].read_vin < pmdev->pages[i].vin_uv_warn_limit) {
  225. pmdev->pages[i].status_word |= PB_STATUS_INPUT;
  226. pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_UV_WARN;
  227. }
  228. if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_warn_limit) {
  229. pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
  230. pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_WARN;
  231. }
  232. if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_fault_limit) {
  233. pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
  234. pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_FAULT;
  235. }
  236. if (pmdev->pages[i].read_pin > pmdev->pages[i].pin_op_warn_limit) {
  237. pmdev->pages[i].status_word |= PB_STATUS_INPUT;
  238. pmdev->pages[i].status_input |= PB_STATUS_INPUT_PIN_OP_WARN;
  239. }
  240. if (pmdev->pages[i].read_temperature_1
  241. > pmdev->pages[i].ot_fault_limit) {
  242. pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
  243. pmdev->pages[i].status_temperature |= PB_STATUS_OT_FAULT;
  244. }
  245. if (pmdev->pages[i].read_temperature_1
  246. > pmdev->pages[i].ot_warn_limit) {
  247. pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
  248. pmdev->pages[i].status_temperature |= PB_STATUS_OT_WARN;
  249. }
  250. }
  251. }
  252. void pmbus_idle(PMBusDevice *pmdev)
  253. {
  254. pmdev->code = PMBUS_IDLE_STATE;
  255. }
  256. /* assert the status_cml error upon receipt of malformed command */
  257. static void pmbus_cml_error(PMBusDevice *pmdev)
  258. {
  259. for (int i = 0; i < pmdev->num_pages; i++) {
  260. pmdev->pages[i].status_word |= PMBUS_STATUS_CML;
  261. pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD;
  262. }
  263. }
  264. static uint8_t pmbus_receive_byte(SMBusDevice *smd)
  265. {
  266. PMBusDevice *pmdev = PMBUS_DEVICE(smd);
  267. PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
  268. uint8_t ret = PMBUS_ERR_BYTE;
  269. uint8_t index;
  270. if (pmdev->out_buf_len != 0) {
  271. ret = pmbus_out_buf_pop(pmdev);
  272. return ret;
  273. }
  274. /*
  275. * Reading from all pages will return the value from page 0,
  276. * means that all subsequent commands are to be applied to all output.
  277. */
  278. if (pmdev->page == PB_ALL_PAGES) {
  279. index = 0;
  280. } else if (pmdev->page > pmdev->num_pages - 1) {
  281. qemu_log_mask(LOG_GUEST_ERROR,
  282. "%s: page %d is out of range\n",
  283. __func__, pmdev->page);
  284. pmbus_cml_error(pmdev);
  285. return PMBUS_ERR_BYTE;
  286. } else {
  287. index = pmdev->page;
  288. }
  289. switch (pmdev->code) {
  290. case PMBUS_PAGE:
  291. pmbus_send8(pmdev, pmdev->page);
  292. break;
  293. case PMBUS_OPERATION: /* R/W byte */
  294. pmbus_send8(pmdev, pmdev->pages[index].operation);
  295. break;
  296. case PMBUS_ON_OFF_CONFIG: /* R/W byte */
  297. pmbus_send8(pmdev, pmdev->pages[index].on_off_config);
  298. break;
  299. case PMBUS_PHASE: /* R/W byte */
  300. pmbus_send8(pmdev, pmdev->pages[index].phase);
  301. break;
  302. case PMBUS_WRITE_PROTECT: /* R/W byte */
  303. pmbus_send8(pmdev, pmdev->pages[index].write_protect);
  304. break;
  305. case PMBUS_CAPABILITY:
  306. pmbus_send8(pmdev, pmdev->capability);
  307. if (pmdev->capability & BIT(7)) {
  308. qemu_log_mask(LOG_UNIMP,
  309. "%s: PEC is enabled but not yet supported.\n",
  310. __func__);
  311. }
  312. break;
  313. case PMBUS_VOUT_MODE: /* R/W byte */
  314. if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
  315. pmbus_send8(pmdev, pmdev->pages[index].vout_mode);
  316. } else {
  317. goto passthough;
  318. }
  319. break;
  320. case PMBUS_VOUT_COMMAND: /* R/W word */
  321. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  322. pmbus_send16(pmdev, pmdev->pages[index].vout_command);
  323. } else {
  324. goto passthough;
  325. }
  326. break;
  327. case PMBUS_VOUT_TRIM: /* R/W word */
  328. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  329. pmbus_send16(pmdev, pmdev->pages[index].vout_trim);
  330. } else {
  331. goto passthough;
  332. }
  333. break;
  334. case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
  335. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  336. pmbus_send16(pmdev, pmdev->pages[index].vout_cal_offset);
  337. } else {
  338. goto passthough;
  339. }
  340. break;
  341. case PMBUS_VOUT_MAX: /* R/W word */
  342. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  343. pmbus_send16(pmdev, pmdev->pages[index].vout_max);
  344. } else {
  345. goto passthough;
  346. }
  347. break;
  348. case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
  349. if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
  350. pmbus_send16(pmdev, pmdev->pages[index].vout_margin_high);
  351. } else {
  352. goto passthough;
  353. }
  354. break;
  355. case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
  356. if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
  357. pmbus_send16(pmdev, pmdev->pages[index].vout_margin_low);
  358. } else {
  359. goto passthough;
  360. }
  361. break;
  362. case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
  363. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  364. pmbus_send16(pmdev, pmdev->pages[index].vout_transition_rate);
  365. } else {
  366. goto passthough;
  367. }
  368. break;
  369. case PMBUS_VOUT_DROOP: /* R/W word */
  370. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  371. pmbus_send16(pmdev, pmdev->pages[index].vout_droop);
  372. } else {
  373. goto passthough;
  374. }
  375. break;
  376. case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
  377. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  378. pmbus_send16(pmdev, pmdev->pages[index].vout_scale_loop);
  379. } else {
  380. goto passthough;
  381. }
  382. break;
  383. case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
  384. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  385. pmbus_send16(pmdev, pmdev->pages[index].vout_scale_monitor);
  386. } else {
  387. goto passthough;
  388. }
  389. break;
  390. case PMBUS_VOUT_MIN: /* R/W word */
  391. if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
  392. pmbus_send16(pmdev, pmdev->pages[index].vout_min);
  393. } else {
  394. goto passthough;
  395. }
  396. break;
  397. /* TODO: implement coefficients support */
  398. case PMBUS_POUT_MAX: /* R/W word */
  399. if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
  400. pmbus_send16(pmdev, pmdev->pages[index].pout_max);
  401. } else {
  402. goto passthough;
  403. }
  404. break;
  405. case PMBUS_VIN_ON: /* R/W word */
  406. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  407. pmbus_send16(pmdev, pmdev->pages[index].vin_on);
  408. } else {
  409. goto passthough;
  410. }
  411. break;
  412. case PMBUS_VIN_OFF: /* R/W word */
  413. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  414. pmbus_send16(pmdev, pmdev->pages[index].vin_off);
  415. } else {
  416. goto passthough;
  417. }
  418. break;
  419. case PMBUS_IOUT_CAL_GAIN: /* R/W word */
  420. if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
  421. pmbus_send16(pmdev, pmdev->pages[index].iout_cal_gain);
  422. } else {
  423. goto passthough;
  424. }
  425. break;
  426. case PMBUS_FAN_CONFIG_1_2: /* R/W byte */
  427. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  428. pmbus_send8(pmdev, pmdev->pages[index].fan_config_1_2);
  429. } else {
  430. goto passthough;
  431. }
  432. break;
  433. case PMBUS_FAN_COMMAND_1: /* R/W word */
  434. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  435. pmbus_send16(pmdev, pmdev->pages[index].fan_command_1);
  436. } else {
  437. goto passthough;
  438. }
  439. break;
  440. case PMBUS_FAN_COMMAND_2: /* R/W word */
  441. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  442. pmbus_send16(pmdev, pmdev->pages[index].fan_command_2);
  443. } else {
  444. goto passthough;
  445. }
  446. break;
  447. case PMBUS_FAN_CONFIG_3_4: /* R/W byte */
  448. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  449. pmbus_send8(pmdev, pmdev->pages[index].fan_config_3_4);
  450. } else {
  451. goto passthough;
  452. }
  453. break;
  454. case PMBUS_FAN_COMMAND_3: /* R/W word */
  455. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  456. pmbus_send16(pmdev, pmdev->pages[index].fan_command_3);
  457. } else {
  458. goto passthough;
  459. }
  460. break;
  461. case PMBUS_FAN_COMMAND_4: /* R/W word */
  462. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  463. pmbus_send16(pmdev, pmdev->pages[index].fan_command_4);
  464. } else {
  465. goto passthough;
  466. }
  467. break;
  468. case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
  469. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  470. pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit);
  471. } else {
  472. goto passthough;
  473. }
  474. break;
  475. case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
  476. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  477. pmbus_send8(pmdev, pmdev->pages[index].vout_ov_fault_response);
  478. } else {
  479. goto passthough;
  480. }
  481. break;
  482. case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
  483. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  484. pmbus_send16(pmdev, pmdev->pages[index].vout_ov_warn_limit);
  485. } else {
  486. goto passthough;
  487. }
  488. break;
  489. case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
  490. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  491. pmbus_send16(pmdev, pmdev->pages[index].vout_uv_warn_limit);
  492. } else {
  493. goto passthough;
  494. }
  495. break;
  496. case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
  497. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  498. pmbus_send16(pmdev, pmdev->pages[index].vout_uv_fault_limit);
  499. } else {
  500. goto passthough;
  501. }
  502. break;
  503. case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
  504. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  505. pmbus_send8(pmdev, pmdev->pages[index].vout_uv_fault_response);
  506. } else {
  507. goto passthough;
  508. }
  509. break;
  510. case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
  511. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  512. pmbus_send16(pmdev, pmdev->pages[index].iout_oc_fault_limit);
  513. } else {
  514. goto passthough;
  515. }
  516. break;
  517. case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
  518. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  519. pmbus_send8(pmdev, pmdev->pages[index].iout_oc_fault_response);
  520. } else {
  521. goto passthough;
  522. }
  523. break;
  524. case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
  525. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  526. pmbus_send16(pmdev, pmdev->pages[index].iout_oc_lv_fault_limit);
  527. } else {
  528. goto passthough;
  529. }
  530. break;
  531. case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
  532. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  533. pmbus_send8(pmdev, pmdev->pages[index].iout_oc_lv_fault_response);
  534. } else {
  535. goto passthough;
  536. }
  537. break;
  538. case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
  539. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  540. pmbus_send16(pmdev, pmdev->pages[index].iout_oc_warn_limit);
  541. } else {
  542. goto passthough;
  543. }
  544. break;
  545. case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
  546. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  547. pmbus_send16(pmdev, pmdev->pages[index].iout_uc_fault_limit);
  548. } else {
  549. goto passthough;
  550. }
  551. break;
  552. case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
  553. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  554. pmbus_send8(pmdev, pmdev->pages[index].iout_uc_fault_response);
  555. } else {
  556. goto passthough;
  557. }
  558. break;
  559. case PMBUS_OT_FAULT_LIMIT: /* R/W word */
  560. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  561. pmbus_send16(pmdev, pmdev->pages[index].ot_fault_limit);
  562. } else {
  563. goto passthough;
  564. }
  565. break;
  566. case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
  567. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  568. pmbus_send8(pmdev, pmdev->pages[index].ot_fault_response);
  569. } else {
  570. goto passthough;
  571. }
  572. break;
  573. case PMBUS_OT_WARN_LIMIT: /* R/W word */
  574. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  575. pmbus_send16(pmdev, pmdev->pages[index].ot_warn_limit);
  576. } else {
  577. goto passthough;
  578. }
  579. break;
  580. case PMBUS_UT_WARN_LIMIT: /* R/W word */
  581. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  582. pmbus_send16(pmdev, pmdev->pages[index].ut_warn_limit);
  583. } else {
  584. goto passthough;
  585. }
  586. break;
  587. case PMBUS_UT_FAULT_LIMIT: /* R/W word */
  588. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  589. pmbus_send16(pmdev, pmdev->pages[index].ut_fault_limit);
  590. } else {
  591. goto passthough;
  592. }
  593. break;
  594. case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
  595. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  596. pmbus_send8(pmdev, pmdev->pages[index].ut_fault_response);
  597. } else {
  598. goto passthough;
  599. }
  600. break;
  601. case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
  602. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  603. pmbus_send16(pmdev, pmdev->pages[index].vin_ov_fault_limit);
  604. } else {
  605. goto passthough;
  606. }
  607. break;
  608. case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
  609. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  610. pmbus_send8(pmdev, pmdev->pages[index].vin_ov_fault_response);
  611. } else {
  612. goto passthough;
  613. }
  614. break;
  615. case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
  616. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  617. pmbus_send16(pmdev, pmdev->pages[index].vin_ov_warn_limit);
  618. } else {
  619. goto passthough;
  620. }
  621. break;
  622. case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
  623. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  624. pmbus_send16(pmdev, pmdev->pages[index].vin_uv_warn_limit);
  625. } else {
  626. goto passthough;
  627. }
  628. break;
  629. case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
  630. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  631. pmbus_send16(pmdev, pmdev->pages[index].vin_uv_fault_limit);
  632. } else {
  633. goto passthough;
  634. }
  635. break;
  636. case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
  637. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  638. pmbus_send8(pmdev, pmdev->pages[index].vin_uv_fault_response);
  639. } else {
  640. goto passthough;
  641. }
  642. break;
  643. case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
  644. if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
  645. pmbus_send16(pmdev, pmdev->pages[index].iin_oc_fault_limit);
  646. } else {
  647. goto passthough;
  648. }
  649. break;
  650. case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
  651. if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
  652. pmbus_send8(pmdev, pmdev->pages[index].iin_oc_fault_response);
  653. } else {
  654. goto passthough;
  655. }
  656. break;
  657. case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
  658. if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
  659. pmbus_send16(pmdev, pmdev->pages[index].iin_oc_warn_limit);
  660. } else {
  661. goto passthough;
  662. }
  663. break;
  664. case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
  665. if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
  666. pmbus_send16(pmdev, pmdev->pages[index].pout_op_fault_limit);
  667. } else {
  668. goto passthough;
  669. }
  670. break;
  671. case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
  672. if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
  673. pmbus_send8(pmdev, pmdev->pages[index].pout_op_fault_response);
  674. } else {
  675. goto passthough;
  676. }
  677. break;
  678. case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
  679. if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
  680. pmbus_send16(pmdev, pmdev->pages[index].pout_op_warn_limit);
  681. } else {
  682. goto passthough;
  683. }
  684. break;
  685. case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
  686. if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
  687. pmbus_send16(pmdev, pmdev->pages[index].pin_op_warn_limit);
  688. } else {
  689. goto passthough;
  690. }
  691. break;
  692. case PMBUS_STATUS_BYTE: /* R/W byte */
  693. pmbus_send8(pmdev, pmdev->pages[index].status_word & 0xFF);
  694. break;
  695. case PMBUS_STATUS_WORD: /* R/W word */
  696. pmbus_send16(pmdev, pmdev->pages[index].status_word);
  697. break;
  698. case PMBUS_STATUS_VOUT: /* R/W byte */
  699. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  700. pmbus_send8(pmdev, pmdev->pages[index].status_vout);
  701. } else {
  702. goto passthough;
  703. }
  704. break;
  705. case PMBUS_STATUS_IOUT: /* R/W byte */
  706. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  707. pmbus_send8(pmdev, pmdev->pages[index].status_iout);
  708. } else {
  709. goto passthough;
  710. }
  711. break;
  712. case PMBUS_STATUS_INPUT: /* R/W byte */
  713. if (pmdev->pages[index].page_flags & PB_HAS_VIN ||
  714. pmdev->pages[index].page_flags & PB_HAS_IIN ||
  715. pmdev->pages[index].page_flags & PB_HAS_PIN) {
  716. pmbus_send8(pmdev, pmdev->pages[index].status_input);
  717. } else {
  718. goto passthough;
  719. }
  720. break;
  721. case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
  722. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  723. pmbus_send8(pmdev, pmdev->pages[index].status_temperature);
  724. } else {
  725. goto passthough;
  726. }
  727. break;
  728. case PMBUS_STATUS_CML: /* R/W byte */
  729. pmbus_send8(pmdev, pmdev->pages[index].status_cml);
  730. break;
  731. case PMBUS_STATUS_OTHER: /* R/W byte */
  732. pmbus_send8(pmdev, pmdev->pages[index].status_other);
  733. break;
  734. case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */
  735. pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
  736. break;
  737. case PMBUS_STATUS_FANS_1_2: /* R/W byte */
  738. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  739. pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2);
  740. } else {
  741. goto passthough;
  742. }
  743. break;
  744. case PMBUS_STATUS_FANS_3_4: /* R/W byte */
  745. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  746. pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4);
  747. } else {
  748. goto passthough;
  749. }
  750. break;
  751. case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
  752. if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
  753. pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
  754. } else {
  755. goto passthough;
  756. }
  757. break;
  758. case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
  759. if (pmdev->pages[index].page_flags & PB_HAS_EOUT) {
  760. pmbus_send(pmdev, pmdev->pages[index].read_eout, 5);
  761. } else {
  762. goto passthough;
  763. }
  764. break;
  765. case PMBUS_READ_VIN: /* Read-Only word */
  766. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  767. pmbus_send16(pmdev, pmdev->pages[index].read_vin);
  768. } else {
  769. goto passthough;
  770. }
  771. break;
  772. case PMBUS_READ_IIN: /* Read-Only word */
  773. if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
  774. pmbus_send16(pmdev, pmdev->pages[index].read_iin);
  775. } else {
  776. goto passthough;
  777. }
  778. break;
  779. case PMBUS_READ_VCAP: /* Read-Only word */
  780. if (pmdev->pages[index].page_flags & PB_HAS_VCAP) {
  781. pmbus_send16(pmdev, pmdev->pages[index].read_vcap);
  782. } else {
  783. goto passthough;
  784. }
  785. break;
  786. case PMBUS_READ_VOUT: /* Read-Only word */
  787. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  788. pmbus_send16(pmdev, pmdev->pages[index].read_vout);
  789. } else {
  790. goto passthough;
  791. }
  792. break;
  793. case PMBUS_READ_IOUT: /* Read-Only word */
  794. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  795. pmbus_send16(pmdev, pmdev->pages[index].read_iout);
  796. } else {
  797. goto passthough;
  798. }
  799. break;
  800. case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
  801. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  802. pmbus_send16(pmdev, pmdev->pages[index].read_temperature_1);
  803. } else {
  804. goto passthough;
  805. }
  806. break;
  807. case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
  808. if (pmdev->pages[index].page_flags & PB_HAS_TEMP2) {
  809. pmbus_send16(pmdev, pmdev->pages[index].read_temperature_2);
  810. } else {
  811. goto passthough;
  812. }
  813. break;
  814. case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
  815. if (pmdev->pages[index].page_flags & PB_HAS_TEMP3) {
  816. pmbus_send16(pmdev, pmdev->pages[index].read_temperature_3);
  817. } else {
  818. goto passthough;
  819. }
  820. break;
  821. case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */
  822. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  823. pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_1);
  824. } else {
  825. goto passthough;
  826. }
  827. break;
  828. case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */
  829. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  830. pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_2);
  831. } else {
  832. goto passthough;
  833. }
  834. break;
  835. case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */
  836. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  837. pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_3);
  838. } else {
  839. goto passthough;
  840. }
  841. break;
  842. case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */
  843. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  844. pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_4);
  845. } else {
  846. goto passthough;
  847. }
  848. break;
  849. case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */
  850. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  851. pmbus_send16(pmdev, pmdev->pages[index].read_duty_cycle);
  852. } else {
  853. goto passthough;
  854. }
  855. break;
  856. case PMBUS_READ_FREQUENCY: /* Read-Only word */
  857. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  858. pmbus_send16(pmdev, pmdev->pages[index].read_frequency);
  859. } else {
  860. goto passthough;
  861. }
  862. break;
  863. case PMBUS_READ_POUT: /* Read-Only word */
  864. if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
  865. pmbus_send16(pmdev, pmdev->pages[index].read_pout);
  866. } else {
  867. goto passthough;
  868. }
  869. break;
  870. case PMBUS_READ_PIN: /* Read-Only word */
  871. if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
  872. pmbus_send16(pmdev, pmdev->pages[index].read_pin);
  873. } else {
  874. goto passthough;
  875. }
  876. break;
  877. case PMBUS_REVISION: /* Read-Only byte */
  878. pmbus_send8(pmdev, pmdev->pages[index].revision);
  879. break;
  880. case PMBUS_MFR_ID: /* R/W block */
  881. if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
  882. pmbus_send_string(pmdev, pmdev->pages[index].mfr_id);
  883. } else {
  884. goto passthough;
  885. }
  886. break;
  887. case PMBUS_MFR_MODEL: /* R/W block */
  888. if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
  889. pmbus_send_string(pmdev, pmdev->pages[index].mfr_model);
  890. } else {
  891. goto passthough;
  892. }
  893. break;
  894. case PMBUS_MFR_REVISION: /* R/W block */
  895. if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
  896. pmbus_send_string(pmdev, pmdev->pages[index].mfr_revision);
  897. } else {
  898. goto passthough;
  899. }
  900. break;
  901. case PMBUS_MFR_LOCATION: /* R/W block */
  902. if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
  903. pmbus_send_string(pmdev, pmdev->pages[index].mfr_location);
  904. } else {
  905. goto passthough;
  906. }
  907. break;
  908. case PMBUS_MFR_VIN_MIN: /* Read-Only word */
  909. if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
  910. pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_min);
  911. } else {
  912. goto passthough;
  913. }
  914. break;
  915. case PMBUS_MFR_VIN_MAX: /* Read-Only word */
  916. if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
  917. pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_max);
  918. } else {
  919. goto passthough;
  920. }
  921. break;
  922. case PMBUS_MFR_IIN_MAX: /* Read-Only word */
  923. if (pmdev->pages[index].page_flags & PB_HAS_IIN_RATING) {
  924. pmbus_send16(pmdev, pmdev->pages[index].mfr_iin_max);
  925. } else {
  926. goto passthough;
  927. }
  928. break;
  929. case PMBUS_MFR_PIN_MAX: /* Read-Only word */
  930. if (pmdev->pages[index].page_flags & PB_HAS_PIN_RATING) {
  931. pmbus_send16(pmdev, pmdev->pages[index].mfr_pin_max);
  932. } else {
  933. goto passthough;
  934. }
  935. break;
  936. case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
  937. if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
  938. pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_min);
  939. } else {
  940. goto passthough;
  941. }
  942. break;
  943. case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
  944. if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
  945. pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_max);
  946. } else {
  947. goto passthough;
  948. }
  949. break;
  950. case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
  951. if (pmdev->pages[index].page_flags & PB_HAS_IOUT_RATING) {
  952. pmbus_send16(pmdev, pmdev->pages[index].mfr_iout_max);
  953. } else {
  954. goto passthough;
  955. }
  956. break;
  957. case PMBUS_MFR_POUT_MAX: /* Read-Only word */
  958. if (pmdev->pages[index].page_flags & PB_HAS_POUT_RATING) {
  959. pmbus_send16(pmdev, pmdev->pages[index].mfr_pout_max);
  960. } else {
  961. goto passthough;
  962. }
  963. break;
  964. case PMBUS_MFR_MAX_TEMP_1: /* R/W word */
  965. if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
  966. pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_1);
  967. } else {
  968. goto passthough;
  969. }
  970. break;
  971. case PMBUS_MFR_MAX_TEMP_2: /* R/W word */
  972. if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
  973. pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_2);
  974. } else {
  975. goto passthough;
  976. }
  977. break;
  978. case PMBUS_MFR_MAX_TEMP_3: /* R/W word */
  979. if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
  980. pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_3);
  981. } else {
  982. goto passthough;
  983. }
  984. break;
  985. case PMBUS_IDLE_STATE:
  986. pmbus_send8(pmdev, PMBUS_ERR_BYTE);
  987. break;
  988. case PMBUS_CLEAR_FAULTS: /* Send Byte */
  989. case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
  990. case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */
  991. case PMBUS_RESTORE_DEFAULT_ALL: /* Send Byte */
  992. case PMBUS_STORE_DEFAULT_CODE: /* Write-only Byte */
  993. case PMBUS_RESTORE_DEFAULT_CODE: /* Write-only Byte */
  994. case PMBUS_STORE_USER_ALL: /* Send Byte */
  995. case PMBUS_RESTORE_USER_ALL: /* Send Byte */
  996. case PMBUS_STORE_USER_CODE: /* Write-only Byte */
  997. case PMBUS_RESTORE_USER_CODE: /* Write-only Byte */
  998. case PMBUS_QUERY: /* Write-Only */
  999. qemu_log_mask(LOG_GUEST_ERROR,
  1000. "%s: reading from write only register 0x%02x\n",
  1001. __func__, pmdev->code);
  1002. break;
  1003. passthough:
  1004. default:
  1005. /* Pass through read request if not handled */
  1006. if (pmdc->receive_byte) {
  1007. ret = pmdc->receive_byte(pmdev);
  1008. }
  1009. break;
  1010. }
  1011. if (pmdev->out_buf_len != 0) {
  1012. ret = pmbus_out_buf_pop(pmdev);
  1013. return ret;
  1014. }
  1015. return ret;
  1016. }
  1017. /*
  1018. * PMBus clear faults command applies to all status registers, existing faults
  1019. * should separately get re-asserted.
  1020. */
  1021. static void pmbus_clear_faults(PMBusDevice *pmdev)
  1022. {
  1023. for (uint8_t i = 0; i < pmdev->num_pages; i++) {
  1024. pmdev->pages[i].status_word = 0;
  1025. pmdev->pages[i].status_vout = 0;
  1026. pmdev->pages[i].status_iout = 0;
  1027. pmdev->pages[i].status_input = 0;
  1028. pmdev->pages[i].status_temperature = 0;
  1029. pmdev->pages[i].status_cml = 0;
  1030. pmdev->pages[i].status_other = 0;
  1031. pmdev->pages[i].status_mfr_specific = 0;
  1032. pmdev->pages[i].status_fans_1_2 = 0;
  1033. pmdev->pages[i].status_fans_3_4 = 0;
  1034. }
  1035. }
  1036. /*
  1037. * PMBus operation is used to turn On and Off PSUs
  1038. * Therefore, default value for the Operation should be PB_OP_ON or 0x80
  1039. */
  1040. static void pmbus_operation(PMBusDevice *pmdev)
  1041. {
  1042. uint8_t index = pmdev->page;
  1043. if ((pmdev->pages[index].operation & PB_OP_ON) == 0) {
  1044. pmdev->pages[index].read_vout = 0;
  1045. pmdev->pages[index].read_iout = 0;
  1046. pmdev->pages[index].read_pout = 0;
  1047. return;
  1048. }
  1049. if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_HIGH)) {
  1050. pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_high;
  1051. }
  1052. if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_LOW)) {
  1053. pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_low;
  1054. }
  1055. pmbus_check_limits(pmdev);
  1056. }
  1057. static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len)
  1058. {
  1059. PMBusDevice *pmdev = PMBUS_DEVICE(smd);
  1060. PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
  1061. int ret = 0;
  1062. uint8_t index;
  1063. if (len == 0) {
  1064. qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
  1065. return PMBUS_ERR_BYTE;
  1066. }
  1067. if (!pmdev->pages) { /* allocate memory for pages on first use */
  1068. pmbus_pages_alloc(pmdev);
  1069. }
  1070. pmdev->in_buf_len = len;
  1071. pmdev->in_buf = buf;
  1072. pmdev->code = buf[0]; /* PMBus command code */
  1073. if (pmdev->code == PMBUS_CLEAR_FAULTS) {
  1074. pmbus_clear_faults(pmdev);
  1075. }
  1076. if (len == 1) { /* Single length writes are command codes only */
  1077. return 0;
  1078. }
  1079. if (pmdev->code == PMBUS_PAGE) {
  1080. pmdev->page = pmbus_receive8(pmdev);
  1081. if (pmdev->page > pmdev->num_pages - 1 && pmdev->page != PB_ALL_PAGES) {
  1082. qemu_log_mask(LOG_GUEST_ERROR,
  1083. "%s: page %u is out of range\n",
  1084. __func__, pmdev->page);
  1085. pmdev->page = 0; /* undefined behaviour - reset to page 0 */
  1086. pmbus_cml_error(pmdev);
  1087. return PMBUS_ERR_BYTE;
  1088. }
  1089. return 0;
  1090. }
  1091. /* loop through all the pages when 0xFF is received */
  1092. if (pmdev->page == PB_ALL_PAGES) {
  1093. for (int i = 0; i < pmdev->num_pages; i++) {
  1094. pmdev->page = i;
  1095. pmbus_write_data(smd, buf, len);
  1096. }
  1097. pmdev->page = PB_ALL_PAGES;
  1098. return 0;
  1099. }
  1100. index = pmdev->page;
  1101. switch (pmdev->code) {
  1102. case PMBUS_OPERATION: /* R/W byte */
  1103. pmdev->pages[index].operation = pmbus_receive8(pmdev);
  1104. pmbus_operation(pmdev);
  1105. break;
  1106. case PMBUS_ON_OFF_CONFIG: /* R/W byte */
  1107. pmdev->pages[index].on_off_config = pmbus_receive8(pmdev);
  1108. break;
  1109. case PMBUS_CLEAR_FAULTS: /* Send Byte */
  1110. pmbus_clear_faults(pmdev);
  1111. break;
  1112. case PMBUS_PHASE: /* R/W byte */
  1113. pmdev->pages[index].phase = pmbus_receive8(pmdev);
  1114. break;
  1115. case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
  1116. case PMBUS_WRITE_PROTECT: /* R/W byte */
  1117. pmdev->pages[index].write_protect = pmbus_receive8(pmdev);
  1118. break;
  1119. case PMBUS_VOUT_MODE: /* R/W byte */
  1120. if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
  1121. pmdev->pages[index].vout_mode = pmbus_receive8(pmdev);
  1122. } else {
  1123. goto passthrough;
  1124. }
  1125. break;
  1126. case PMBUS_VOUT_COMMAND: /* R/W word */
  1127. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1128. pmdev->pages[index].vout_command = pmbus_receive16(pmdev);
  1129. } else {
  1130. goto passthrough;
  1131. }
  1132. break;
  1133. case PMBUS_VOUT_TRIM: /* R/W word */
  1134. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1135. pmdev->pages[index].vout_trim = pmbus_receive16(pmdev);
  1136. } else {
  1137. goto passthrough;
  1138. }
  1139. break;
  1140. case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
  1141. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1142. pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev);
  1143. } else {
  1144. goto passthrough;
  1145. }
  1146. break;
  1147. case PMBUS_VOUT_MAX: /* R/W word */
  1148. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1149. pmdev->pages[index].vout_max = pmbus_receive16(pmdev);
  1150. } else {
  1151. goto passthrough;
  1152. }
  1153. break;
  1154. case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
  1155. if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
  1156. pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev);
  1157. } else {
  1158. goto passthrough;
  1159. }
  1160. break;
  1161. case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
  1162. if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
  1163. pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev);
  1164. } else {
  1165. goto passthrough;
  1166. }
  1167. break;
  1168. case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
  1169. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1170. pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev);
  1171. } else {
  1172. goto passthrough;
  1173. }
  1174. break;
  1175. case PMBUS_VOUT_DROOP: /* R/W word */
  1176. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1177. pmdev->pages[index].vout_droop = pmbus_receive16(pmdev);
  1178. } else {
  1179. goto passthrough;
  1180. }
  1181. break;
  1182. case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
  1183. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1184. pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev);
  1185. } else {
  1186. goto passthrough;
  1187. }
  1188. break;
  1189. case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
  1190. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1191. pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev);
  1192. } else {
  1193. goto passthrough;
  1194. }
  1195. break;
  1196. case PMBUS_VOUT_MIN: /* R/W word */
  1197. if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
  1198. pmdev->pages[index].vout_min = pmbus_receive16(pmdev);
  1199. } else {
  1200. goto passthrough;
  1201. }
  1202. break;
  1203. case PMBUS_POUT_MAX: /* R/W word */
  1204. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1205. pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
  1206. } else {
  1207. goto passthrough;
  1208. }
  1209. break;
  1210. case PMBUS_VIN_ON: /* R/W word */
  1211. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  1212. pmdev->pages[index].vin_on = pmbus_receive16(pmdev);
  1213. } else {
  1214. goto passthrough;
  1215. }
  1216. break;
  1217. case PMBUS_VIN_OFF: /* R/W word */
  1218. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  1219. pmdev->pages[index].vin_off = pmbus_receive16(pmdev);
  1220. } else {
  1221. goto passthrough;
  1222. }
  1223. break;
  1224. case PMBUS_IOUT_CAL_GAIN: /* R/W word */
  1225. if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
  1226. pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev);
  1227. } else {
  1228. goto passthrough;
  1229. }
  1230. break;
  1231. case PMBUS_FAN_CONFIG_1_2: /* R/W byte */
  1232. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  1233. pmdev->pages[index].fan_config_1_2 = pmbus_receive8(pmdev);
  1234. } else {
  1235. goto passthrough;
  1236. }
  1237. break;
  1238. case PMBUS_FAN_COMMAND_1: /* R/W word */
  1239. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  1240. pmdev->pages[index].fan_command_1 = pmbus_receive16(pmdev);
  1241. } else {
  1242. goto passthrough;
  1243. }
  1244. break;
  1245. case PMBUS_FAN_COMMAND_2: /* R/W word */
  1246. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  1247. pmdev->pages[index].fan_command_2 = pmbus_receive16(pmdev);
  1248. } else {
  1249. goto passthrough;
  1250. }
  1251. break;
  1252. case PMBUS_FAN_CONFIG_3_4: /* R/W byte */
  1253. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  1254. pmdev->pages[index].fan_config_3_4 = pmbus_receive8(pmdev);
  1255. } else {
  1256. goto passthrough;
  1257. }
  1258. break;
  1259. case PMBUS_FAN_COMMAND_3: /* R/W word */
  1260. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  1261. pmdev->pages[index].fan_command_3 = pmbus_receive16(pmdev);
  1262. } else {
  1263. goto passthrough;
  1264. }
  1265. break;
  1266. case PMBUS_FAN_COMMAND_4: /* R/W word */
  1267. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  1268. pmdev->pages[index].fan_command_4 = pmbus_receive16(pmdev);
  1269. } else {
  1270. goto passthrough;
  1271. }
  1272. break;
  1273. case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
  1274. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1275. pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev);
  1276. } else {
  1277. goto passthrough;
  1278. }
  1279. break;
  1280. case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
  1281. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1282. pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev);
  1283. } else {
  1284. goto passthrough;
  1285. }
  1286. break;
  1287. case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
  1288. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1289. pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev);
  1290. } else {
  1291. goto passthrough;
  1292. }
  1293. break;
  1294. case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
  1295. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1296. pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev);
  1297. } else {
  1298. goto passthrough;
  1299. }
  1300. break;
  1301. case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
  1302. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1303. pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev);
  1304. } else {
  1305. goto passthrough;
  1306. }
  1307. break;
  1308. case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
  1309. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1310. pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev);
  1311. } else {
  1312. goto passthrough;
  1313. }
  1314. break;
  1315. case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
  1316. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  1317. pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev);
  1318. } else {
  1319. goto passthrough;
  1320. }
  1321. break;
  1322. case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
  1323. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  1324. pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev);
  1325. } else {
  1326. goto passthrough;
  1327. }
  1328. break;
  1329. case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
  1330. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  1331. pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev);
  1332. } else {
  1333. goto passthrough;
  1334. }
  1335. break;
  1336. case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
  1337. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  1338. pmdev->pages[index].iout_oc_lv_fault_response
  1339. = pmbus_receive8(pmdev);
  1340. } else {
  1341. goto passthrough;
  1342. }
  1343. break;
  1344. case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
  1345. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  1346. pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev);
  1347. } else {
  1348. goto passthrough;
  1349. }
  1350. break;
  1351. case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
  1352. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  1353. pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev);
  1354. } else {
  1355. goto passthrough;
  1356. }
  1357. break;
  1358. case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
  1359. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  1360. pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev);
  1361. } else {
  1362. goto passthrough;
  1363. }
  1364. break;
  1365. case PMBUS_OT_FAULT_LIMIT: /* R/W word */
  1366. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  1367. pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev);
  1368. } else {
  1369. goto passthrough;
  1370. }
  1371. break;
  1372. case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
  1373. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  1374. pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev);
  1375. } else {
  1376. goto passthrough;
  1377. }
  1378. break;
  1379. case PMBUS_OT_WARN_LIMIT: /* R/W word */
  1380. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  1381. pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev);
  1382. } else {
  1383. goto passthrough;
  1384. }
  1385. break;
  1386. case PMBUS_UT_WARN_LIMIT: /* R/W word */
  1387. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  1388. pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev);
  1389. } else {
  1390. goto passthrough;
  1391. }
  1392. break;
  1393. case PMBUS_UT_FAULT_LIMIT: /* R/W word */
  1394. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  1395. pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev);
  1396. } else {
  1397. goto passthrough;
  1398. }
  1399. break;
  1400. case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
  1401. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  1402. pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev);
  1403. } else {
  1404. goto passthrough;
  1405. }
  1406. break;
  1407. case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
  1408. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  1409. pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev);
  1410. } else {
  1411. goto passthrough;
  1412. }
  1413. break;
  1414. case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
  1415. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  1416. pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev);
  1417. } else {
  1418. goto passthrough;
  1419. }
  1420. break;
  1421. case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
  1422. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  1423. pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev);
  1424. } else {
  1425. goto passthrough;
  1426. }
  1427. break;
  1428. case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
  1429. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  1430. pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev);
  1431. } else {
  1432. goto passthrough;
  1433. }
  1434. break;
  1435. case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
  1436. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  1437. pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev);
  1438. } else {
  1439. goto passthrough;
  1440. }
  1441. break;
  1442. case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
  1443. if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
  1444. pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev);
  1445. } else {
  1446. goto passthrough;
  1447. }
  1448. break;
  1449. case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
  1450. if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
  1451. pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev);
  1452. } else {
  1453. goto passthrough;
  1454. }
  1455. break;
  1456. case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
  1457. if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
  1458. pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev);
  1459. } else {
  1460. goto passthrough;
  1461. }
  1462. break;
  1463. case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
  1464. if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
  1465. pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev);
  1466. } else {
  1467. goto passthrough;
  1468. }
  1469. break;
  1470. case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
  1471. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1472. pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev);
  1473. } else {
  1474. goto passthrough;
  1475. }
  1476. break;
  1477. case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
  1478. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1479. pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev);
  1480. } else {
  1481. goto passthrough;
  1482. }
  1483. break;
  1484. case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
  1485. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1486. pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev);
  1487. } else {
  1488. goto passthrough;
  1489. }
  1490. break;
  1491. case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
  1492. if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
  1493. pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev);
  1494. } else {
  1495. goto passthrough;
  1496. }
  1497. break;
  1498. case PMBUS_STATUS_BYTE: /* R/W byte */
  1499. pmdev->pages[index].status_word = pmbus_receive8(pmdev);
  1500. break;
  1501. case PMBUS_STATUS_WORD: /* R/W word */
  1502. pmdev->pages[index].status_word = pmbus_receive16(pmdev);
  1503. break;
  1504. case PMBUS_STATUS_VOUT: /* R/W byte */
  1505. if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
  1506. pmdev->pages[index].status_vout = pmbus_receive8(pmdev);
  1507. } else {
  1508. goto passthrough;
  1509. }
  1510. break;
  1511. case PMBUS_STATUS_IOUT: /* R/W byte */
  1512. if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
  1513. pmdev->pages[index].status_iout = pmbus_receive8(pmdev);
  1514. } else {
  1515. goto passthrough;
  1516. }
  1517. break;
  1518. case PMBUS_STATUS_INPUT: /* R/W byte */
  1519. pmdev->pages[index].status_input = pmbus_receive8(pmdev);
  1520. break;
  1521. case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
  1522. if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
  1523. pmdev->pages[index].status_temperature = pmbus_receive8(pmdev);
  1524. } else {
  1525. goto passthrough;
  1526. }
  1527. break;
  1528. case PMBUS_STATUS_CML: /* R/W byte */
  1529. pmdev->pages[index].status_cml = pmbus_receive8(pmdev);
  1530. break;
  1531. case PMBUS_STATUS_OTHER: /* R/W byte */
  1532. pmdev->pages[index].status_other = pmbus_receive8(pmdev);
  1533. break;
  1534. case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */
  1535. pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev);
  1536. break;
  1537. case PMBUS_STATUS_FANS_1_2: /* R/W byte */
  1538. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  1539. pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2);
  1540. } else {
  1541. goto passthrough;
  1542. }
  1543. break;
  1544. case PMBUS_STATUS_FANS_3_4: /* R/W byte */
  1545. if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
  1546. pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4);
  1547. } else {
  1548. goto passthrough;
  1549. }
  1550. break;
  1551. case PMBUS_PAGE_PLUS_READ: /* Block Read-only */
  1552. case PMBUS_CAPABILITY: /* Read-Only byte */
  1553. case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */
  1554. case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
  1555. case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
  1556. case PMBUS_READ_VIN: /* Read-Only word */
  1557. case PMBUS_READ_IIN: /* Read-Only word */
  1558. case PMBUS_READ_VCAP: /* Read-Only word */
  1559. case PMBUS_READ_VOUT: /* Read-Only word */
  1560. case PMBUS_READ_IOUT: /* Read-Only word */
  1561. case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
  1562. case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
  1563. case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
  1564. case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */
  1565. case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */
  1566. case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */
  1567. case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */
  1568. case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */
  1569. case PMBUS_READ_FREQUENCY: /* Read-Only word */
  1570. case PMBUS_READ_POUT: /* Read-Only word */
  1571. case PMBUS_READ_PIN: /* Read-Only word */
  1572. case PMBUS_REVISION: /* Read-Only byte */
  1573. case PMBUS_APP_PROFILE_SUPPORT: /* Read-Only block-read */
  1574. case PMBUS_MFR_VIN_MIN: /* Read-Only word */
  1575. case PMBUS_MFR_VIN_MAX: /* Read-Only word */
  1576. case PMBUS_MFR_IIN_MAX: /* Read-Only word */
  1577. case PMBUS_MFR_PIN_MAX: /* Read-Only word */
  1578. case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
  1579. case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
  1580. case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
  1581. case PMBUS_MFR_POUT_MAX: /* Read-Only word */
  1582. case PMBUS_MFR_TAMBIENT_MAX: /* Read-Only word */
  1583. case PMBUS_MFR_TAMBIENT_MIN: /* Read-Only word */
  1584. case PMBUS_MFR_EFFICIENCY_LL: /* Read-Only block 14 bytes */
  1585. case PMBUS_MFR_EFFICIENCY_HL: /* Read-Only block 14 bytes */
  1586. case PMBUS_MFR_PIN_ACCURACY: /* Read-Only byte */
  1587. case PMBUS_IC_DEVICE_ID: /* Read-Only block-read */
  1588. case PMBUS_IC_DEVICE_REV: /* Read-Only block-read */
  1589. qemu_log_mask(LOG_GUEST_ERROR,
  1590. "%s: writing to read-only register 0x%02x\n",
  1591. __func__, pmdev->code);
  1592. break;
  1593. passthrough:
  1594. /* Unimplemented registers get passed to the device */
  1595. default:
  1596. if (pmdc->write_data) {
  1597. ret = pmdc->write_data(pmdev, buf, len);
  1598. }
  1599. break;
  1600. }
  1601. pmbus_check_limits(pmdev);
  1602. pmdev->in_buf_len = 0;
  1603. return ret;
  1604. }
  1605. int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags)
  1606. {
  1607. if (!pmdev->pages) { /* allocate memory for pages on first use */
  1608. pmbus_pages_alloc(pmdev);
  1609. }
  1610. /* The 0xFF page is special for commands applying to all pages */
  1611. if (index == PB_ALL_PAGES) {
  1612. for (int i = 0; i < pmdev->num_pages; i++) {
  1613. pmdev->pages[i].page_flags = flags;
  1614. }
  1615. return 0;
  1616. }
  1617. if (index > pmdev->num_pages - 1) {
  1618. qemu_log_mask(LOG_GUEST_ERROR,
  1619. "%s: index %u is out of range\n",
  1620. __func__, index);
  1621. return -1;
  1622. }
  1623. pmdev->pages[index].page_flags = flags;
  1624. return 0;
  1625. }
  1626. /* TODO: include pmbus page info in vmstate */
  1627. const VMStateDescription vmstate_pmbus_device = {
  1628. .name = TYPE_PMBUS_DEVICE,
  1629. .version_id = 0,
  1630. .minimum_version_id = 0,
  1631. .fields = (const VMStateField[]) {
  1632. VMSTATE_SMBUS_DEVICE(smb, PMBusDevice),
  1633. VMSTATE_UINT8(num_pages, PMBusDevice),
  1634. VMSTATE_UINT8(code, PMBusDevice),
  1635. VMSTATE_UINT8(page, PMBusDevice),
  1636. VMSTATE_UINT8(capability, PMBusDevice),
  1637. VMSTATE_END_OF_LIST()
  1638. }
  1639. };
  1640. static void pmbus_device_finalize(Object *obj)
  1641. {
  1642. PMBusDevice *pmdev = PMBUS_DEVICE(obj);
  1643. g_free(pmdev->pages);
  1644. }
  1645. static void pmbus_device_class_init(ObjectClass *klass, void *data)
  1646. {
  1647. SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass);
  1648. k->quick_cmd = pmbus_quick_cmd;
  1649. k->write_data = pmbus_write_data;
  1650. k->receive_byte = pmbus_receive_byte;
  1651. }
  1652. static const TypeInfo pmbus_device_type_info = {
  1653. .name = TYPE_PMBUS_DEVICE,
  1654. .parent = TYPE_SMBUS_DEVICE,
  1655. .instance_size = sizeof(PMBusDevice),
  1656. .instance_finalize = pmbus_device_finalize,
  1657. .abstract = true,
  1658. .class_size = sizeof(PMBusDeviceClass),
  1659. .class_init = pmbus_device_class_init,
  1660. };
  1661. static void pmbus_device_register_types(void)
  1662. {
  1663. type_register_static(&pmbus_device_type_info);
  1664. }
  1665. type_init(pmbus_device_register_types)