Commit | Line | Data |
---|---|---|
df4846c3 | 1 | /* |
612262a5 | 2 | * FireDTV driver (formerly known as FireSAT) |
df4846c3 | 3 | * |
612262a5 SR |
4 | * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> |
5 | * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> | |
df4846c3 HK |
6 | * |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation; either version 2 of | |
10 | * the License, or (at your option) any later version. | |
11 | */ | |
12 | ||
8ae83cdf | 13 | #include <linux/device.h> |
612262a5 SR |
14 | #include <linux/kernel.h> |
15 | #include <linux/mutex.h> | |
16 | #include <linux/types.h> | |
17 | ||
8ae83cdf SR |
18 | #include <asm/byteorder.h> |
19 | ||
612262a5 | 20 | #include <ieee1394.h> |
612262a5 SR |
21 | #include <nodemgr.h> |
22 | ||
c81c8b68 | 23 | #include "avc_api.h" |
612262a5 SR |
24 | #include "cmp.h" |
25 | #include "firesat.h" | |
c81c8b68 | 26 | |
8ae83cdf | 27 | #define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL |
c81c8b68 | 28 | |
612262a5 SR |
29 | static int cmp_read(struct firesat *firesat, void *buf, u64 addr, size_t len) |
30 | { | |
c81c8b68 | 31 | int ret; |
612262a5 SR |
32 | |
33 | if (mutex_lock_interruptible(&firesat->avc_mutex)) | |
c81c8b68 GKH |
34 | return -EINTR; |
35 | ||
8ae83cdf SR |
36 | ret = hpsb_node_read(firesat->ud->ne, addr, buf, len); |
37 | if (ret < 0) | |
38 | dev_err(&firesat->ud->device, "CMP: read I/O error\n"); | |
c81c8b68 | 39 | |
612262a5 | 40 | mutex_unlock(&firesat->avc_mutex); |
c81c8b68 GKH |
41 | return ret; |
42 | } | |
43 | ||
8ae83cdf SR |
44 | static int cmp_lock(struct firesat *firesat, void *data, u64 addr, __be32 arg, |
45 | int ext_tcode) | |
612262a5 | 46 | { |
c81c8b68 | 47 | int ret; |
612262a5 SR |
48 | |
49 | if (mutex_lock_interruptible(&firesat->avc_mutex)) | |
c81c8b68 GKH |
50 | return -EINTR; |
51 | ||
8ae83cdf SR |
52 | ret = hpsb_node_lock(firesat->ud->ne, addr, ext_tcode, data, |
53 | (__force quadlet_t)arg); | |
54 | if (ret < 0) | |
55 | dev_err(&firesat->ud->device, "CMP: lock I/O error\n"); | |
c81c8b68 | 56 | |
612262a5 | 57 | mutex_unlock(&firesat->avc_mutex); |
c81c8b68 GKH |
58 | return ret; |
59 | } | |
60 | ||
8ae83cdf SR |
61 | static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) |
62 | { | |
63 | return (be32_to_cpu(opcr) >> shift) & mask; | |
64 | } | |
65 | ||
66 | static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) | |
67 | { | |
68 | *opcr &= ~cpu_to_be32(mask << shift); | |
69 | *opcr |= cpu_to_be32((value & mask) << shift); | |
70 | } | |
c81c8b68 | 71 | |
8ae83cdf SR |
72 | #define get_opcr_online(v) get_opcr((v), 0x1, 31) |
73 | #define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24) | |
74 | #define get_opcr_channel(v) get_opcr((v), 0x3f, 16) | |
c81c8b68 | 75 | |
8ae83cdf SR |
76 | #define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24) |
77 | #define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16) | |
78 | #define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14) | |
79 | #define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10) | |
c81c8b68 | 80 | |
8ae83cdf SR |
81 | int cmp_establish_pp_connection(struct firesat *firesat, int plug, int channel) |
82 | { | |
83 | __be32 old_opcr, opcr; | |
84 | u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); | |
85 | int attempts = 0; | |
86 | int ret; | |
87 | ||
88 | ret = cmp_read(firesat, &opcr, opcr_address, 4); | |
89 | if (ret < 0) | |
90 | return ret; | |
91 | ||
92 | repeat: | |
93 | if (!get_opcr_online(opcr)) { | |
94 | dev_err(&firesat->ud->device, "CMP: output offline\n"); | |
95 | return -EBUSY; | |
96 | } | |
97 | ||
98 | old_opcr = opcr; | |
99 | ||
100 | if (get_opcr_p2p_connections(opcr)) { | |
101 | if (get_opcr_channel(opcr) != channel) { | |
102 | dev_err(&firesat->ud->device, | |
103 | "CMP: cannot change channel\n"); | |
104 | return -EBUSY; | |
c81c8b68 | 105 | } |
8ae83cdf SR |
106 | dev_info(&firesat->ud->device, |
107 | "CMP: overlaying existing connection\n"); | |
108 | ||
109 | /* We don't allocate isochronous resources. */ | |
110 | } else { | |
111 | set_opcr_channel(&opcr, channel); | |
112 | set_opcr_data_rate(&opcr, IEEE1394_SPEED_400); | |
113 | ||
114 | /* FIXME: this is for the worst case - optimize */ | |
115 | set_opcr_overhead_id(&opcr, 0); | |
116 | ||
117 | /* FIXME: allocate isochronous channel and bandwidth at IRM */ | |
c81c8b68 | 118 | } |
8ae83cdf SR |
119 | |
120 | set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); | |
121 | ||
122 | ret = cmp_lock(firesat, &opcr, opcr_address, old_opcr, 2); | |
123 | if (ret < 0) | |
124 | return ret; | |
125 | ||
126 | if (old_opcr != opcr) { | |
127 | /* | |
128 | * FIXME: if old_opcr.P2P_Connections > 0, | |
129 | * deallocate isochronous channel and bandwidth at IRM | |
130 | */ | |
131 | ||
132 | if (++attempts < 6) /* arbitrary limit */ | |
133 | goto repeat; | |
134 | return -EBUSY; | |
135 | } | |
136 | ||
c81c8b68 GKH |
137 | return 0; |
138 | } | |
139 | ||
8ae83cdf SR |
140 | void cmp_break_pp_connection(struct firesat *firesat, int plug, int channel) |
141 | { | |
142 | __be32 old_opcr, opcr; | |
143 | u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); | |
144 | int attempts = 0; | |
145 | ||
146 | if (cmp_read(firesat, &opcr, opcr_address, 4) < 0) | |
147 | return; | |
148 | ||
149 | repeat: | |
150 | if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || | |
151 | get_opcr_channel(opcr) != channel) { | |
152 | dev_err(&firesat->ud->device, "CMP: no connection to break\n"); | |
153 | return; | |
154 | } | |
155 | ||
156 | old_opcr = opcr; | |
157 | set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1); | |
c81c8b68 | 158 | |
8ae83cdf SR |
159 | if (cmp_lock(firesat, &opcr, opcr_address, old_opcr, 2) < 0) |
160 | return; | |
c81c8b68 | 161 | |
8ae83cdf SR |
162 | if (old_opcr != opcr) { |
163 | /* | |
164 | * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last | |
165 | * owner, deallocate isochronous channel and bandwidth at IRM | |
166 | */ | |
c81c8b68 | 167 | |
8ae83cdf SR |
168 | if (++attempts < 6) /* arbitrary limit */ |
169 | goto repeat; | |
170 | } | |
c81c8b68 | 171 | } |