1433d6423SLionel Sambuc /* This file contains device independent i2c device driver helpers. */
2433d6423SLionel Sambuc
3433d6423SLionel Sambuc #include <assert.h>
4433d6423SLionel Sambuc #include <minix/drivers.h>
5433d6423SLionel Sambuc #include <minix/endpoint.h>
6433d6423SLionel Sambuc #include <minix/i2c.h>
7433d6423SLionel Sambuc #include <minix/i2cdriver.h>
8433d6423SLionel Sambuc #include <minix/ipc.h>
9433d6423SLionel Sambuc #include <minix/ds.h>
10433d6423SLionel Sambuc
11433d6423SLionel Sambuc void
i2cdriver_announce(uint32_t bus)12433d6423SLionel Sambuc i2cdriver_announce(uint32_t bus)
13433d6423SLionel Sambuc {
14433d6423SLionel Sambuc /* Announce we are up after a fresh start or restart. */
15433d6423SLionel Sambuc int r;
16433d6423SLionel Sambuc char key[DS_MAX_KEYLEN];
17433d6423SLionel Sambuc char label[DS_MAX_KEYLEN];
1865f76edbSDavid van Moolenbroek const char *driver_prefix = "drv.i2c.";
19433d6423SLionel Sambuc
20433d6423SLionel Sambuc /* Callers are allowed to use ipc_sendrec to communicate with drivers.
21433d6423SLionel Sambuc * For this reason, there may blocked callers when a driver restarts.
22433d6423SLionel Sambuc * Ask the kernel to unblock them (if any).
23433d6423SLionel Sambuc */
24*41022be1SCristiano Giuffrida if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS, 0, 0)) != OK) {
25433d6423SLionel Sambuc panic("chardriver_init: sys_statectl failed: %d", r);
26433d6423SLionel Sambuc }
27433d6423SLionel Sambuc
28433d6423SLionel Sambuc /* Publish a driver up event. */
29433d6423SLionel Sambuc r = ds_retrieve_label_name(label, sef_self());
30433d6423SLionel Sambuc if (r != OK) {
31433d6423SLionel Sambuc panic("unable to get own label: %d\n", r);
32433d6423SLionel Sambuc }
33433d6423SLionel Sambuc /* example key: drv.i2c.1.cat24c245.0x50 */
34433d6423SLionel Sambuc snprintf(key, DS_MAX_KEYLEN, "%s%d.%s", driver_prefix, bus, label);
35433d6423SLionel Sambuc r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE);
36433d6423SLionel Sambuc if (r != OK) {
37433d6423SLionel Sambuc panic("unable to publish driver up event: %d\n", r);
38433d6423SLionel Sambuc }
39433d6423SLionel Sambuc }
40433d6423SLionel Sambuc
41433d6423SLionel Sambuc int
i2cdriver_env_parse(uint32_t * bus,i2c_addr_t * address,i2c_addr_t * valid_addrs)42433d6423SLionel Sambuc i2cdriver_env_parse(uint32_t * bus, i2c_addr_t * address,
43433d6423SLionel Sambuc i2c_addr_t * valid_addrs)
44433d6423SLionel Sambuc {
45433d6423SLionel Sambuc /* fill in bus and address with the values passed on the command line */
46433d6423SLionel Sambuc int r;
47433d6423SLionel Sambuc int found;
48433d6423SLionel Sambuc long int busl;
49433d6423SLionel Sambuc long int addressl;
50433d6423SLionel Sambuc
51433d6423SLionel Sambuc r = env_parse("bus", "d", 0, &busl, 1, 3);
52433d6423SLionel Sambuc if (r != EP_SET) {
53433d6423SLionel Sambuc return -1;
54433d6423SLionel Sambuc }
55433d6423SLionel Sambuc *bus = (uint32_t) busl;
56433d6423SLionel Sambuc
57433d6423SLionel Sambuc r = env_parse("address", "x", 0, &addressl, 0x0000, 0x03ff);
58433d6423SLionel Sambuc if (r != EP_SET) {
59433d6423SLionel Sambuc return -1;
60433d6423SLionel Sambuc }
61433d6423SLionel Sambuc *address = addressl;
62433d6423SLionel Sambuc
63433d6423SLionel Sambuc found = 0;
64433d6423SLionel Sambuc while (*valid_addrs != 0x0000) {
65433d6423SLionel Sambuc
66433d6423SLionel Sambuc if (*address == *valid_addrs) {
67433d6423SLionel Sambuc found = 1;
68433d6423SLionel Sambuc break;
69433d6423SLionel Sambuc }
70433d6423SLionel Sambuc
71433d6423SLionel Sambuc valid_addrs++;
72433d6423SLionel Sambuc }
73433d6423SLionel Sambuc
74433d6423SLionel Sambuc if (!found) {
75433d6423SLionel Sambuc return 1;
76433d6423SLionel Sambuc }
77433d6423SLionel Sambuc
78433d6423SLionel Sambuc return 0;
79433d6423SLionel Sambuc }
80433d6423SLionel Sambuc
81433d6423SLionel Sambuc endpoint_t
i2cdriver_bus_endpoint(uint32_t bus)82433d6423SLionel Sambuc i2cdriver_bus_endpoint(uint32_t bus)
83433d6423SLionel Sambuc {
84433d6423SLionel Sambuc /* locate the driver for the i2c bus itself */
85433d6423SLionel Sambuc int r;
8665f76edbSDavid van Moolenbroek const char *label_prefix = "i2c.";
87433d6423SLionel Sambuc char label[DS_MAX_KEYLEN];
88433d6423SLionel Sambuc endpoint_t bus_endpoint;
89433d6423SLionel Sambuc
90433d6423SLionel Sambuc snprintf(label, DS_MAX_KEYLEN, "%s%d", label_prefix, bus);
91433d6423SLionel Sambuc
92433d6423SLionel Sambuc r = ds_retrieve_label_endpt(label, &bus_endpoint);
93433d6423SLionel Sambuc if (r != OK) {
94433d6423SLionel Sambuc return 0;
95433d6423SLionel Sambuc }
96433d6423SLionel Sambuc
97433d6423SLionel Sambuc return bus_endpoint;
98433d6423SLionel Sambuc }
99433d6423SLionel Sambuc
100433d6423SLionel Sambuc int
i2cdriver_subscribe_bus_updates(uint32_t bus)101433d6423SLionel Sambuc i2cdriver_subscribe_bus_updates(uint32_t bus)
102433d6423SLionel Sambuc {
103433d6423SLionel Sambuc int r;
104433d6423SLionel Sambuc char regex[DS_MAX_KEYLEN];
105433d6423SLionel Sambuc
106433d6423SLionel Sambuc /* only capture events for the specified bus */
107433d6423SLionel Sambuc snprintf(regex, DS_MAX_KEYLEN, "drv\\.chr\\.i2c\\.%d", bus);
108433d6423SLionel Sambuc
109433d6423SLionel Sambuc /* Subscribe to driver events from the i2c bus */
110433d6423SLionel Sambuc r = ds_subscribe(regex, DSF_INITIAL | DSF_OVERWRITE);
111433d6423SLionel Sambuc if (r != OK) {
112433d6423SLionel Sambuc return r;
113433d6423SLionel Sambuc }
114433d6423SLionel Sambuc
115433d6423SLionel Sambuc return OK;
116433d6423SLionel Sambuc }
117433d6423SLionel Sambuc
118433d6423SLionel Sambuc void
i2cdriver_handle_bus_update(endpoint_t * bus_endpoint,uint32_t bus,i2c_addr_t address)119433d6423SLionel Sambuc i2cdriver_handle_bus_update(endpoint_t * bus_endpoint, uint32_t bus,
120433d6423SLionel Sambuc i2c_addr_t address)
121433d6423SLionel Sambuc {
122433d6423SLionel Sambuc char key[DS_MAX_KEYLEN];
123433d6423SLionel Sambuc u32_t value;
124433d6423SLionel Sambuc int type;
125433d6423SLionel Sambuc endpoint_t owner_endpoint, old_endpoint;
126433d6423SLionel Sambuc int r;
127433d6423SLionel Sambuc
128433d6423SLionel Sambuc /* check for pending events */
129433d6423SLionel Sambuc while ((r = ds_check(key, &type, &owner_endpoint)) == OK) {
130433d6423SLionel Sambuc
131433d6423SLionel Sambuc r = ds_retrieve_u32(key, &value);
132433d6423SLionel Sambuc if (r != OK) {
133433d6423SLionel Sambuc return;
134433d6423SLionel Sambuc }
135433d6423SLionel Sambuc
136433d6423SLionel Sambuc if (value == DS_DRIVER_UP) {
137433d6423SLionel Sambuc old_endpoint = *bus_endpoint;
138433d6423SLionel Sambuc
139433d6423SLionel Sambuc /* look up the bus's (potentially new) endpoint */
140433d6423SLionel Sambuc *bus_endpoint = i2cdriver_bus_endpoint(bus);
141433d6423SLionel Sambuc
142433d6423SLionel Sambuc /* was updated endpoint? */
143433d6423SLionel Sambuc if (old_endpoint != *bus_endpoint) {
144433d6423SLionel Sambuc /* re-reserve device to allow the driver to
145433d6423SLionel Sambuc * continue working, even through a manual
146433d6423SLionel Sambuc * down/up.
147433d6423SLionel Sambuc */
148433d6423SLionel Sambuc i2cdriver_reserve_device(*bus_endpoint,
149433d6423SLionel Sambuc address);
150433d6423SLionel Sambuc }
151433d6423SLionel Sambuc }
152433d6423SLionel Sambuc }
153433d6423SLionel Sambuc }
154433d6423SLionel Sambuc
155433d6423SLionel Sambuc int
i2cdriver_reserve_device(endpoint_t bus_endpoint,i2c_addr_t address)156433d6423SLionel Sambuc i2cdriver_reserve_device(endpoint_t bus_endpoint, i2c_addr_t address)
157433d6423SLionel Sambuc {
158433d6423SLionel Sambuc int r;
159433d6423SLionel Sambuc message m;
160433d6423SLionel Sambuc
161433d6423SLionel Sambuc m.m_type = BUSC_I2C_RESERVE;
162433d6423SLionel Sambuc m.m_li2cdriver_i2c_busc_i2c_reserve.addr = address;
163433d6423SLionel Sambuc
164433d6423SLionel Sambuc r = ipc_sendrec(bus_endpoint, &m);
165433d6423SLionel Sambuc if (r != OK) {
166433d6423SLionel Sambuc return EIO;
167433d6423SLionel Sambuc }
168433d6423SLionel Sambuc
169433d6423SLionel Sambuc return m.m_type; /* return reply code OK, EBUSY, EINVAL, etc. */
170433d6423SLionel Sambuc }
171433d6423SLionel Sambuc
172433d6423SLionel Sambuc int
i2cdriver_exec(endpoint_t bus_endpoint,minix_i2c_ioctl_exec_t * ioctl_exec)173433d6423SLionel Sambuc i2cdriver_exec(endpoint_t bus_endpoint, minix_i2c_ioctl_exec_t * ioctl_exec)
174433d6423SLionel Sambuc {
175433d6423SLionel Sambuc int r;
176433d6423SLionel Sambuc message m;
177433d6423SLionel Sambuc cp_grant_id_t grant_nr;
178433d6423SLionel Sambuc
179433d6423SLionel Sambuc grant_nr = cpf_grant_direct(bus_endpoint, (vir_bytes) ioctl_exec,
180433d6423SLionel Sambuc sizeof(minix_i2c_ioctl_exec_t), CPF_READ | CPF_WRITE);
181433d6423SLionel Sambuc
182433d6423SLionel Sambuc memset(&m, '\0', sizeof(message));
183433d6423SLionel Sambuc
184433d6423SLionel Sambuc m.m_type = BUSC_I2C_EXEC;
185433d6423SLionel Sambuc m.m_li2cdriver_i2c_busc_i2c_exec.grant = grant_nr;
186433d6423SLionel Sambuc
187433d6423SLionel Sambuc r = ipc_sendrec(bus_endpoint, &m);
188433d6423SLionel Sambuc cpf_revoke(grant_nr);
189433d6423SLionel Sambuc if (r != OK) {
190433d6423SLionel Sambuc return EIO;
191433d6423SLionel Sambuc }
192433d6423SLionel Sambuc
193433d6423SLionel Sambuc return m.m_type;
194433d6423SLionel Sambuc }
195433d6423SLionel Sambuc
196433d6423SLionel Sambuc static int
__i2creg_read(endpoint_t bus_endpoint,i2c_addr_t address,uint8_t raw,uint8_t reg,uint32_t * val,size_t vallen)197433d6423SLionel Sambuc __i2creg_read(endpoint_t bus_endpoint, i2c_addr_t address, uint8_t raw,
198433d6423SLionel Sambuc uint8_t reg, uint32_t * val, size_t vallen)
199433d6423SLionel Sambuc {
20065f76edbSDavid van Moolenbroek uint32_t i;
20165f76edbSDavid van Moolenbroek int r;
202433d6423SLionel Sambuc minix_i2c_ioctl_exec_t ioctl_exec;
203433d6423SLionel Sambuc
204433d6423SLionel Sambuc assert(val != NULL);
205433d6423SLionel Sambuc assert(vallen >= 1 && vallen <= 4);
206433d6423SLionel Sambuc
207433d6423SLionel Sambuc memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
208433d6423SLionel Sambuc
209433d6423SLionel Sambuc /* Read from chip */
210433d6423SLionel Sambuc ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP;
211433d6423SLionel Sambuc ioctl_exec.iie_addr = address;
212433d6423SLionel Sambuc
213433d6423SLionel Sambuc if (!raw) {
214433d6423SLionel Sambuc /* write the register address */
215433d6423SLionel Sambuc ioctl_exec.iie_cmd[0] = reg;
216433d6423SLionel Sambuc ioctl_exec.iie_cmdlen = 1;
217433d6423SLionel Sambuc }
218433d6423SLionel Sambuc
219433d6423SLionel Sambuc /* read vallen bytes */
220433d6423SLionel Sambuc ioctl_exec.iie_buflen = vallen;
221433d6423SLionel Sambuc
222433d6423SLionel Sambuc r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
223433d6423SLionel Sambuc if (r != OK) {
224433d6423SLionel Sambuc return -1;
225433d6423SLionel Sambuc }
226433d6423SLionel Sambuc
227433d6423SLionel Sambuc for (*val = 0, i = 0; i < vallen; i++) {
228433d6423SLionel Sambuc *val = ((*val) << 8) | ioctl_exec.iie_buf[i];
229433d6423SLionel Sambuc }
230433d6423SLionel Sambuc
231433d6423SLionel Sambuc return OK;
232433d6423SLionel Sambuc }
233433d6423SLionel Sambuc
234433d6423SLionel Sambuc int
i2creg_raw_read8(endpoint_t bus_endpoint,i2c_addr_t address,uint8_t * val)235433d6423SLionel Sambuc i2creg_raw_read8(endpoint_t bus_endpoint, i2c_addr_t address, uint8_t * val)
236433d6423SLionel Sambuc {
237433d6423SLionel Sambuc int r;
238433d6423SLionel Sambuc uint32_t val32;
239433d6423SLionel Sambuc
240433d6423SLionel Sambuc r = __i2creg_read(bus_endpoint, address, 1, 0, &val32, 1);
241433d6423SLionel Sambuc *val = val32 & 0xff;
242433d6423SLionel Sambuc
243433d6423SLionel Sambuc return r;
244433d6423SLionel Sambuc }
245433d6423SLionel Sambuc
246433d6423SLionel Sambuc int
i2creg_read8(endpoint_t bus_endpoint,i2c_addr_t address,uint8_t reg,uint8_t * val)247433d6423SLionel Sambuc i2creg_read8(endpoint_t bus_endpoint, i2c_addr_t address, uint8_t reg,
248433d6423SLionel Sambuc uint8_t * val)
249433d6423SLionel Sambuc {
250433d6423SLionel Sambuc int r;
251433d6423SLionel Sambuc uint32_t val32;
252433d6423SLionel Sambuc
253433d6423SLionel Sambuc r = __i2creg_read(bus_endpoint, address, 0, reg, &val32, 1);
254433d6423SLionel Sambuc *val = val32 & 0xff;
255433d6423SLionel Sambuc
256433d6423SLionel Sambuc return r;
257433d6423SLionel Sambuc }
258433d6423SLionel Sambuc
259433d6423SLionel Sambuc int
i2creg_read16(endpoint_t bus_endpoint,i2c_addr_t address,uint8_t reg,uint16_t * val)260433d6423SLionel Sambuc i2creg_read16(endpoint_t bus_endpoint, i2c_addr_t address, uint8_t reg,
261433d6423SLionel Sambuc uint16_t * val)
262433d6423SLionel Sambuc {
263433d6423SLionel Sambuc int r;
264433d6423SLionel Sambuc uint32_t val32;
265433d6423SLionel Sambuc
266433d6423SLionel Sambuc r = __i2creg_read(bus_endpoint, address, 0, reg, &val32, 2);
267433d6423SLionel Sambuc *val = val32 & 0xffff;
268433d6423SLionel Sambuc
269433d6423SLionel Sambuc return r;
270433d6423SLionel Sambuc }
271433d6423SLionel Sambuc
272433d6423SLionel Sambuc int
i2creg_read24(endpoint_t bus_endpoint,i2c_addr_t address,uint8_t reg,uint32_t * val)273433d6423SLionel Sambuc i2creg_read24(endpoint_t bus_endpoint, i2c_addr_t address, uint8_t reg,
274433d6423SLionel Sambuc uint32_t * val)
275433d6423SLionel Sambuc {
276433d6423SLionel Sambuc return __i2creg_read(bus_endpoint, address, 0, reg, val, 3);
277433d6423SLionel Sambuc }
278433d6423SLionel Sambuc
279433d6423SLionel Sambuc static int
__i2creg_write(endpoint_t bus_endpoint,i2c_addr_t address,uint8_t raw,uint8_t reg,uint8_t val)280433d6423SLionel Sambuc __i2creg_write(endpoint_t bus_endpoint, i2c_addr_t address, uint8_t raw,
281433d6423SLionel Sambuc uint8_t reg, uint8_t val)
282433d6423SLionel Sambuc {
283433d6423SLionel Sambuc int r;
284433d6423SLionel Sambuc minix_i2c_ioctl_exec_t ioctl_exec;
285433d6423SLionel Sambuc
286433d6423SLionel Sambuc memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
287433d6423SLionel Sambuc
288433d6423SLionel Sambuc /* Write to chip */
289433d6423SLionel Sambuc ioctl_exec.iie_op = I2C_OP_WRITE_WITH_STOP;
290433d6423SLionel Sambuc ioctl_exec.iie_addr = address;
291433d6423SLionel Sambuc
292433d6423SLionel Sambuc if (raw) {
293433d6423SLionel Sambuc /* write just the value */
294433d6423SLionel Sambuc ioctl_exec.iie_buf[0] = val;
295433d6423SLionel Sambuc ioctl_exec.iie_buflen = 1;
296433d6423SLionel Sambuc } else {
297433d6423SLionel Sambuc /* write the register address and value */
298433d6423SLionel Sambuc ioctl_exec.iie_buf[0] = reg;
299433d6423SLionel Sambuc ioctl_exec.iie_buf[1] = val;
300433d6423SLionel Sambuc ioctl_exec.iie_buflen = 2;
301433d6423SLionel Sambuc }
302433d6423SLionel Sambuc
303433d6423SLionel Sambuc r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
304433d6423SLionel Sambuc if (r != OK) {
305433d6423SLionel Sambuc return -1;
306433d6423SLionel Sambuc }
307433d6423SLionel Sambuc
308433d6423SLionel Sambuc return OK;
309433d6423SLionel Sambuc }
310433d6423SLionel Sambuc
311433d6423SLionel Sambuc int
i2creg_write8(endpoint_t bus_endpoint,i2c_addr_t address,uint8_t reg,uint8_t val)312433d6423SLionel Sambuc i2creg_write8(endpoint_t bus_endpoint, i2c_addr_t address, uint8_t reg,
313433d6423SLionel Sambuc uint8_t val)
314433d6423SLionel Sambuc {
315433d6423SLionel Sambuc return __i2creg_write(bus_endpoint, address, 0, reg, val);
316433d6423SLionel Sambuc }
317433d6423SLionel Sambuc
318433d6423SLionel Sambuc int
i2creg_raw_write8(endpoint_t bus_endpoint,i2c_addr_t address,uint8_t val)319433d6423SLionel Sambuc i2creg_raw_write8(endpoint_t bus_endpoint, i2c_addr_t address, uint8_t val)
320433d6423SLionel Sambuc {
321433d6423SLionel Sambuc return __i2creg_write(bus_endpoint, address, 1, 0, val);
322433d6423SLionel Sambuc }
323433d6423SLionel Sambuc
324433d6423SLionel Sambuc int
i2creg_set_bits8(endpoint_t bus_endpoint,i2c_addr_t address,uint8_t reg,uint8_t bits)325433d6423SLionel Sambuc i2creg_set_bits8(endpoint_t bus_endpoint, i2c_addr_t address, uint8_t reg,
326433d6423SLionel Sambuc uint8_t bits)
327433d6423SLionel Sambuc {
328433d6423SLionel Sambuc int r;
329433d6423SLionel Sambuc uint8_t val;
330433d6423SLionel Sambuc
331433d6423SLionel Sambuc r = i2creg_read8(bus_endpoint, address, reg, &val);
332433d6423SLionel Sambuc if (r != OK) {
333433d6423SLionel Sambuc return -1;
334433d6423SLionel Sambuc }
335433d6423SLionel Sambuc
336433d6423SLionel Sambuc val |= bits;
337433d6423SLionel Sambuc
338433d6423SLionel Sambuc r = i2creg_write8(bus_endpoint, address, reg, val);
339433d6423SLionel Sambuc if (r != OK) {
340433d6423SLionel Sambuc return -1;
341433d6423SLionel Sambuc }
342433d6423SLionel Sambuc
343433d6423SLionel Sambuc return OK;
344433d6423SLionel Sambuc }
345433d6423SLionel Sambuc
346433d6423SLionel Sambuc int
i2creg_clear_bits8(endpoint_t bus_endpoint,i2c_addr_t address,uint8_t reg,uint8_t bits)347433d6423SLionel Sambuc i2creg_clear_bits8(endpoint_t bus_endpoint, i2c_addr_t address, uint8_t reg,
348433d6423SLionel Sambuc uint8_t bits)
349433d6423SLionel Sambuc {
350433d6423SLionel Sambuc int r;
351433d6423SLionel Sambuc uint8_t val;
352433d6423SLionel Sambuc
353433d6423SLionel Sambuc r = i2creg_read8(bus_endpoint, address, reg, &val);
354433d6423SLionel Sambuc if (r != OK) {
355433d6423SLionel Sambuc return -1;
356433d6423SLionel Sambuc }
357433d6423SLionel Sambuc
358433d6423SLionel Sambuc val &= ~bits;
359433d6423SLionel Sambuc
360433d6423SLionel Sambuc r = i2creg_write8(bus_endpoint, address, reg, val);
361433d6423SLionel Sambuc if (r != OK) {
362433d6423SLionel Sambuc return -1;
363433d6423SLionel Sambuc }
364433d6423SLionel Sambuc
365433d6423SLionel Sambuc return OK;
366433d6423SLionel Sambuc }
367