global1_vtu.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. * Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support
  3. *
  4. * Copyright (c) 2008 Marvell Semiconductor
  5. * Copyright (c) 2015 CMC Electronics, Inc.
  6. * Copyright (c) 2017 Savoir-faire Linux, Inc.
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. */
  13. #include "mv88e6xxx.h"
  14. #include "global1.h"
  15. /* Offset 0x02: VTU FID Register */
  16. static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip,
  17. struct mv88e6xxx_vtu_entry *entry)
  18. {
  19. u16 val;
  20. int err;
  21. err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val);
  22. if (err)
  23. return err;
  24. entry->fid = val & GLOBAL_VTU_FID_MASK;
  25. return 0;
  26. }
  27. static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip,
  28. struct mv88e6xxx_vtu_entry *entry)
  29. {
  30. u16 val = entry->fid & GLOBAL_VTU_FID_MASK;
  31. return mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, val);
  32. }
  33. /* Offset 0x03: VTU SID Register */
  34. static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip,
  35. struct mv88e6xxx_vtu_entry *entry)
  36. {
  37. u16 val;
  38. int err;
  39. err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
  40. if (err)
  41. return err;
  42. entry->sid = val & GLOBAL_VTU_SID_MASK;
  43. return 0;
  44. }
  45. static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip,
  46. struct mv88e6xxx_vtu_entry *entry)
  47. {
  48. u16 val = entry->sid & GLOBAL_VTU_SID_MASK;
  49. return mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, val);
  50. }
  51. /* Offset 0x05: VTU Operation Register */
  52. static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip)
  53. {
  54. return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
  55. }
  56. static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op)
  57. {
  58. int err;
  59. err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op);
  60. if (err)
  61. return err;
  62. return mv88e6xxx_g1_vtu_op_wait(chip);
  63. }
  64. /* Offset 0x06: VTU VID Register */
  65. static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip,
  66. struct mv88e6xxx_vtu_entry *entry)
  67. {
  68. u16 val;
  69. int err;
  70. err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
  71. if (err)
  72. return err;
  73. entry->vid = val & 0xfff;
  74. if (val & GLOBAL_VTU_VID_PAGE)
  75. entry->vid |= 0x1000;
  76. entry->valid = !!(val & GLOBAL_VTU_VID_VALID);
  77. return 0;
  78. }
  79. static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip,
  80. struct mv88e6xxx_vtu_entry *entry)
  81. {
  82. u16 val = entry->vid & 0xfff;
  83. if (entry->vid & 0x1000)
  84. val |= GLOBAL_VTU_VID_PAGE;
  85. if (entry->valid)
  86. val |= GLOBAL_VTU_VID_VALID;
  87. return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, val);
  88. }
  89. /* Offset 0x07: VTU/STU Data Register 1
  90. * Offset 0x08: VTU/STU Data Register 2
  91. * Offset 0x09: VTU/STU Data Register 3
  92. */
  93. static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip,
  94. struct mv88e6xxx_vtu_entry *entry)
  95. {
  96. u16 regs[3];
  97. int i;
  98. /* Read all 3 VTU/STU Data registers */
  99. for (i = 0; i < 3; ++i) {
  100. u16 *reg = &regs[i];
  101. int err;
  102. err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
  103. if (err)
  104. return err;
  105. }
  106. /* Extract MemberTag and PortState data */
  107. for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
  108. unsigned int member_offset = (i % 4) * 4;
  109. unsigned int state_offset = member_offset + 2;
  110. entry->member[i] = (regs[i / 4] >> member_offset) & 0x3;
  111. entry->state[i] = (regs[i / 4] >> state_offset) & 0x3;
  112. }
  113. return 0;
  114. }
  115. static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip,
  116. struct mv88e6xxx_vtu_entry *entry)
  117. {
  118. u16 regs[3] = { 0 };
  119. int i;
  120. /* Insert MemberTag and PortState data */
  121. for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
  122. unsigned int member_offset = (i % 4) * 4;
  123. unsigned int state_offset = member_offset + 2;
  124. regs[i / 4] |= (entry->member[i] & 0x3) << member_offset;
  125. regs[i / 4] |= (entry->state[i] & 0x3) << state_offset;
  126. }
  127. /* Write all 3 VTU/STU Data registers */
  128. for (i = 0; i < 3; ++i) {
  129. u16 reg = regs[i];
  130. int err;
  131. err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
  132. if (err)
  133. return err;
  134. }
  135. return 0;
  136. }
  137. static int mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip *chip, u8 *data)
  138. {
  139. u16 regs[2];
  140. int i;
  141. /* Read the 2 VTU/STU Data registers */
  142. for (i = 0; i < 2; ++i) {
  143. u16 *reg = &regs[i];
  144. int err;
  145. err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
  146. if (err)
  147. return err;
  148. }
  149. /* Extract data */
  150. for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
  151. unsigned int offset = (i % 8) * 2;
  152. data[i] = (regs[i / 8] >> offset) & 0x3;
  153. }
  154. return 0;
  155. }
  156. static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data)
  157. {
  158. u16 regs[2] = { 0 };
  159. int i;
  160. /* Insert data */
  161. for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
  162. unsigned int offset = (i % 8) * 2;
  163. regs[i / 8] |= (data[i] & 0x3) << offset;
  164. }
  165. /* Write the 2 VTU/STU Data registers */
  166. for (i = 0; i < 2; ++i) {
  167. u16 reg = regs[i];
  168. int err;
  169. err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
  170. if (err)
  171. return err;
  172. }
  173. return 0;
  174. }
  175. /* VLAN Translation Unit Operations */
  176. static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip,
  177. struct mv88e6xxx_vtu_entry *entry)
  178. {
  179. int err;
  180. err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
  181. if (err)
  182. return err;
  183. err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
  184. if (err)
  185. return err;
  186. err = mv88e6xxx_g1_vtu_sid_read(chip, entry);
  187. if (err)
  188. return err;
  189. return mv88e6xxx_g1_vtu_vid_read(chip, entry);
  190. }
  191. static int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip,
  192. struct mv88e6xxx_vtu_entry *vtu)
  193. {
  194. struct mv88e6xxx_vtu_entry stu;
  195. int err;
  196. err = mv88e6xxx_g1_vtu_sid_read(chip, vtu);
  197. if (err)
  198. return err;
  199. stu.sid = vtu->sid - 1;
  200. err = mv88e6xxx_g1_vtu_stu_getnext(chip, &stu);
  201. if (err)
  202. return err;
  203. if (stu.sid != vtu->sid || !stu.valid)
  204. return -EINVAL;
  205. return 0;
  206. }
  207. static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
  208. struct mv88e6xxx_vtu_entry *entry)
  209. {
  210. int err;
  211. err = mv88e6xxx_g1_vtu_op_wait(chip);
  212. if (err)
  213. return err;
  214. /* To get the next higher active VID, the VTU GetNext operation can be
  215. * started again without setting the VID registers since it already
  216. * contains the last VID.
  217. *
  218. * To save a few hardware accesses and abstract this to the caller,
  219. * write the VID only once, when the entry is given as invalid.
  220. */
  221. if (!entry->valid) {
  222. err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
  223. if (err)
  224. return err;
  225. }
  226. err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
  227. if (err)
  228. return err;
  229. return mv88e6xxx_g1_vtu_vid_read(chip, entry);
  230. }
  231. int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
  232. struct mv88e6xxx_vtu_entry *entry)
  233. {
  234. u16 val;
  235. int err;
  236. err = mv88e6xxx_g1_vtu_getnext(chip, entry);
  237. if (err)
  238. return err;
  239. if (entry->valid) {
  240. err = mv88e6185_g1_vtu_data_read(chip, entry);
  241. if (err)
  242. return err;
  243. /* VTU DBNum[3:0] are located in VTU Operation 3:0
  244. * VTU DBNum[7:4] are located in VTU Operation 11:8
  245. */
  246. err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val);
  247. if (err)
  248. return err;
  249. entry->fid = val & 0x000f;
  250. entry->fid |= (val & 0x0f00) >> 4;
  251. }
  252. return 0;
  253. }
  254. int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
  255. struct mv88e6xxx_vtu_entry *entry)
  256. {
  257. int err;
  258. /* Fetch VLAN MemberTag data from the VTU */
  259. err = mv88e6xxx_g1_vtu_getnext(chip, entry);
  260. if (err)
  261. return err;
  262. if (entry->valid) {
  263. /* Fetch (and mask) VLAN PortState data from the STU */
  264. err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
  265. if (err)
  266. return err;
  267. err = mv88e6185_g1_vtu_data_read(chip, entry);
  268. if (err)
  269. return err;
  270. err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
  271. if (err)
  272. return err;
  273. }
  274. return 0;
  275. }
  276. int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
  277. struct mv88e6xxx_vtu_entry *entry)
  278. {
  279. int err;
  280. /* Fetch VLAN MemberTag data from the VTU */
  281. err = mv88e6xxx_g1_vtu_getnext(chip, entry);
  282. if (err)
  283. return err;
  284. if (entry->valid) {
  285. err = mv88e6390_g1_vtu_data_read(chip, entry->member);
  286. if (err)
  287. return err;
  288. /* Fetch VLAN PortState data from the STU */
  289. err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
  290. if (err)
  291. return err;
  292. err = mv88e6390_g1_vtu_data_read(chip, entry->state);
  293. if (err)
  294. return err;
  295. err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
  296. if (err)
  297. return err;
  298. }
  299. return 0;
  300. }
  301. int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
  302. struct mv88e6xxx_vtu_entry *entry)
  303. {
  304. u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
  305. int err;
  306. err = mv88e6xxx_g1_vtu_op_wait(chip);
  307. if (err)
  308. return err;
  309. err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
  310. if (err)
  311. return err;
  312. if (entry->valid) {
  313. err = mv88e6185_g1_vtu_data_write(chip, entry);
  314. if (err)
  315. return err;
  316. /* VTU DBNum[3:0] are located in VTU Operation 3:0
  317. * VTU DBNum[7:4] are located in VTU Operation 11:8
  318. */
  319. op |= entry->fid & 0x000f;
  320. op |= (entry->fid & 0x00f0) << 8;
  321. }
  322. return mv88e6xxx_g1_vtu_op(chip, op);
  323. }
  324. int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
  325. struct mv88e6xxx_vtu_entry *entry)
  326. {
  327. int err;
  328. err = mv88e6xxx_g1_vtu_op_wait(chip);
  329. if (err)
  330. return err;
  331. err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
  332. if (err)
  333. return err;
  334. if (entry->valid) {
  335. /* Write MemberTag and PortState data */
  336. err = mv88e6185_g1_vtu_data_write(chip, entry);
  337. if (err)
  338. return err;
  339. err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
  340. if (err)
  341. return err;
  342. /* Load STU entry */
  343. err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
  344. if (err)
  345. return err;
  346. err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
  347. if (err)
  348. return err;
  349. }
  350. /* Load/Purge VTU entry */
  351. return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_LOAD_PURGE);
  352. }
  353. int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
  354. struct mv88e6xxx_vtu_entry *entry)
  355. {
  356. int err;
  357. err = mv88e6xxx_g1_vtu_op_wait(chip);
  358. if (err)
  359. return err;
  360. err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
  361. if (err)
  362. return err;
  363. if (entry->valid) {
  364. /* Write PortState data */
  365. err = mv88e6390_g1_vtu_data_write(chip, entry->state);
  366. if (err)
  367. return err;
  368. err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
  369. if (err)
  370. return err;
  371. /* Load STU entry */
  372. err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
  373. if (err)
  374. return err;
  375. /* Write MemberTag data */
  376. err = mv88e6390_g1_vtu_data_write(chip, entry->member);
  377. if (err)
  378. return err;
  379. err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
  380. if (err)
  381. return err;
  382. }
  383. /* Load/Purge VTU entry */
  384. return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_LOAD_PURGE);
  385. }
  386. int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)
  387. {
  388. int err;
  389. err = mv88e6xxx_g1_vtu_op_wait(chip);
  390. if (err)
  391. return err;
  392. return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_FLUSH_ALL);
  393. }