cmp.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /* -*- c-basic-offset: 8 -*-
  2. *
  3. * cmp.c - Connection Management Procedures
  4. * Copyright (C) 2001 Kristian Høgsberg
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. */
  20. /* TODO
  21. * ----
  22. *
  23. * - Implement IEC61883-1 output plugs and connection management.
  24. * This should probably be part of the general subsystem, as it could
  25. * be shared with dv1394.
  26. *
  27. * - Add IEC61883 unit directory when loading this module. This
  28. * requires a run-time changeable config rom.
  29. */
  30. #include <linux/module.h>
  31. #include <linux/list.h>
  32. #include <linux/sched.h>
  33. #include <linux/types.h>
  34. #include <linux/wait.h>
  35. #include <linux/interrupt.h>
  36. #include "hosts.h"
  37. #include "highlevel.h"
  38. #include "ieee1394.h"
  39. #include "ieee1394_core.h"
  40. #include "cmp.h"
  41. struct plug {
  42. union {
  43. struct cmp_pcr pcr;
  44. quadlet_t quadlet;
  45. } u;
  46. void (*update)(struct cmp_pcr *plug, void *data);
  47. void *data;
  48. };
  49. struct cmp_host {
  50. struct hpsb_host *host;
  51. union {
  52. struct cmp_mpr ompr;
  53. quadlet_t ompr_quadlet;
  54. } u;
  55. struct plug opcr[2];
  56. union {
  57. struct cmp_mpr impr;
  58. quadlet_t impr_quadlet;
  59. } v;
  60. struct plug ipcr[2];
  61. };
  62. enum {
  63. CMP_P2P_CONNECTION,
  64. CMP_BC_CONNECTION
  65. };
  66. #define CSR_PCR_MAP 0x900
  67. #define CSR_PCR_MAP_END 0x9fc
  68. static struct hpsb_highlevel cmp_highlevel;
  69. static void cmp_add_host(struct hpsb_host *host);
  70. static void cmp_host_reset(struct hpsb_host *host);
  71. static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf,
  72. u64 addr, size_t length, u16 flags);
  73. static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
  74. u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags);
  75. static struct hpsb_highlevel cmp_highlevel = {
  76. .name = "cmp",
  77. .add_host = cmp_add_host,
  78. .host_reset = cmp_host_reset,
  79. };
  80. static struct hpsb_address_ops pcr_ops = {
  81. .read = pcr_read,
  82. .lock = pcr_lock,
  83. };
  84. struct cmp_pcr *
  85. cmp_register_opcr(struct hpsb_host *host, int opcr_number, int payload,
  86. void (*update)(struct cmp_pcr *pcr, void *data),
  87. void *data)
  88. {
  89. struct cmp_host *ch;
  90. struct plug *plug;
  91. ch = hpsb_get_hostinfo(&cmp_highlevel, host);
  92. if (opcr_number >= ch->u.ompr.nplugs ||
  93. ch->opcr[opcr_number].update != NULL)
  94. return NULL;
  95. plug = &ch->opcr[opcr_number];
  96. plug->u.pcr.online = 1;
  97. plug->u.pcr.bcast_count = 0;
  98. plug->u.pcr.p2p_count = 0;
  99. plug->u.pcr.overhead = 0;
  100. plug->u.pcr.payload = payload;
  101. plug->update = update;
  102. plug->data = data;
  103. return &plug->u.pcr;
  104. }
  105. void cmp_unregister_opcr(struct hpsb_host *host, struct cmp_pcr *opcr)
  106. {
  107. struct cmp_host *ch;
  108. struct plug *plug;
  109. ch = hpsb_get_hostinfo(&cmp_highlevel, host);
  110. plug = (struct plug *)opcr;
  111. if (plug - ch->opcr >= ch->u.ompr.nplugs) BUG();
  112. plug->u.pcr.online = 0;
  113. plug->update = NULL;
  114. }
  115. static void reset_plugs(struct cmp_host *ch)
  116. {
  117. int i;
  118. ch->u.ompr.non_persistent_ext = 0xff;
  119. for (i = 0; i < ch->u.ompr.nplugs; i++) {
  120. ch->opcr[i].u.pcr.bcast_count = 0;
  121. ch->opcr[i].u.pcr.p2p_count = 0;
  122. ch->opcr[i].u.pcr.overhead = 0;
  123. }
  124. }
  125. static void cmp_add_host(struct hpsb_host *host)
  126. {
  127. struct cmp_host *ch = hpsb_create_hostinfo(&cmp_highlevel, host, sizeof (*ch));
  128. if (ch == NULL) {
  129. HPSB_ERR("Failed to allocate cmp_host");
  130. return;
  131. }
  132. hpsb_register_addrspace(&cmp_highlevel, host, &pcr_ops,
  133. CSR_REGISTER_BASE + CSR_PCR_MAP,
  134. CSR_REGISTER_BASE + CSR_PCR_MAP_END);
  135. ch->host = host;
  136. ch->u.ompr.rate = IEEE1394_SPEED_100;
  137. ch->u.ompr.bcast_channel_base = 63;
  138. ch->u.ompr.nplugs = 2;
  139. reset_plugs(ch);
  140. }
  141. static void cmp_host_reset(struct hpsb_host *host)
  142. {
  143. struct cmp_host *ch;
  144. ch = hpsb_get_hostinfo(&cmp_highlevel, host);
  145. if (ch == NULL) {
  146. HPSB_ERR("cmp: Tried to reset unknown host");
  147. return;
  148. }
  149. reset_plugs(ch);
  150. }
  151. static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf,
  152. u64 addr, size_t length, u16 flags)
  153. {
  154. int csraddr = addr - CSR_REGISTER_BASE;
  155. int plug;
  156. struct cmp_host *ch;
  157. if (length != 4)
  158. return RCODE_TYPE_ERROR;
  159. ch = hpsb_get_hostinfo(&cmp_highlevel, host);
  160. if (csraddr == 0x900) {
  161. *buf = cpu_to_be32(ch->u.ompr_quadlet);
  162. return RCODE_COMPLETE;
  163. }
  164. else if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
  165. plug = (csraddr - 0x904) / 4;
  166. *buf = cpu_to_be32(ch->opcr[plug].u.quadlet);
  167. return RCODE_COMPLETE;
  168. }
  169. else if (csraddr < 0x980) {
  170. return RCODE_ADDRESS_ERROR;
  171. }
  172. else if (csraddr == 0x980) {
  173. *buf = cpu_to_be32(ch->v.impr_quadlet);
  174. return RCODE_COMPLETE;
  175. }
  176. else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
  177. plug = (csraddr - 0x984) / 4;
  178. *buf = cpu_to_be32(ch->ipcr[plug].u.quadlet);
  179. return RCODE_COMPLETE;
  180. }
  181. else
  182. return RCODE_ADDRESS_ERROR;
  183. }
  184. static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
  185. u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags)
  186. {
  187. int csraddr = addr - CSR_REGISTER_BASE;
  188. int plug;
  189. struct cmp_host *ch;
  190. ch = hpsb_get_hostinfo(&cmp_highlevel, host);
  191. if (extcode != EXTCODE_COMPARE_SWAP)
  192. return RCODE_TYPE_ERROR;
  193. if (csraddr == 0x900) {
  194. /* FIXME: Ignore writes to bits 30-31 and 0-7 */
  195. *store = cpu_to_be32(ch->u.ompr_quadlet);
  196. if (arg == cpu_to_be32(ch->u.ompr_quadlet))
  197. ch->u.ompr_quadlet = be32_to_cpu(data);
  198. return RCODE_COMPLETE;
  199. }
  200. if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
  201. plug = (csraddr - 0x904) / 4;
  202. *store = cpu_to_be32(ch->opcr[plug].u.quadlet);
  203. if (arg == *store)
  204. ch->opcr[plug].u.quadlet = be32_to_cpu(data);
  205. if (be32_to_cpu(*store) != ch->opcr[plug].u.quadlet &&
  206. ch->opcr[plug].update != NULL)
  207. ch->opcr[plug].update(&ch->opcr[plug].u.pcr,
  208. ch->opcr[plug].data);
  209. return RCODE_COMPLETE;
  210. }
  211. else if (csraddr < 0x980) {
  212. return RCODE_ADDRESS_ERROR;
  213. }
  214. else if (csraddr == 0x980) {
  215. /* FIXME: Ignore writes to bits 24-31 and 0-7 */
  216. *store = cpu_to_be32(ch->u.ompr_quadlet);
  217. if (arg == cpu_to_be32(ch->u.ompr_quadlet))
  218. ch->u.ompr_quadlet = be32_to_cpu(data);
  219. return RCODE_COMPLETE;
  220. }
  221. else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
  222. plug = (csraddr - 0x984) / 4;
  223. *store = cpu_to_be32(ch->ipcr[plug].u.quadlet);
  224. if (arg == *store)
  225. ch->ipcr[plug].u.quadlet = be32_to_cpu(data);
  226. if (be32_to_cpu(*store) != ch->ipcr[plug].u.quadlet &&
  227. ch->ipcr[plug].update != NULL)
  228. ch->ipcr[plug].update(&ch->ipcr[plug].u.pcr,
  229. ch->ipcr[plug].data);
  230. return RCODE_COMPLETE;
  231. }
  232. else
  233. return RCODE_ADDRESS_ERROR;
  234. }
  235. /* Module interface */
  236. MODULE_AUTHOR("Kristian Hogsberg <hogsberg@users.sf.net>");
  237. MODULE_DESCRIPTION("Connection Management Procedures (CMP)");
  238. MODULE_SUPPORTED_DEVICE("cmp");
  239. MODULE_LICENSE("GPL");
  240. EXPORT_SYMBOL(cmp_register_opcr);
  241. EXPORT_SYMBOL(cmp_unregister_opcr);
  242. static int __init cmp_init_module (void)
  243. {
  244. hpsb_register_highlevel (&cmp_highlevel);
  245. HPSB_INFO("Loaded CMP driver");
  246. return 0;
  247. }
  248. static void __exit cmp_exit_module (void)
  249. {
  250. hpsb_unregister_highlevel(&cmp_highlevel);
  251. HPSB_INFO("Unloaded CMP driver");
  252. }
  253. module_init(cmp_init_module);
  254. module_exit(cmp_exit_module);