]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/firmware/sigma.c
Adds a nolock function to the w1 interface to avoid locking the
[karo-tx-linux.git] / drivers / firmware / sigma.c
1 /*
2  * Load Analog Devices SigmaStudio firmware files
3  *
4  * Copyright 2009-2011 Analog Devices Inc.
5  *
6  * Licensed under the GPL-2 or later.
7  */
8
9 #include <linux/crc32.h>
10 #include <linux/delay.h>
11 #include <linux/firmware.h>
12 #include <linux/kernel.h>
13 #include <linux/i2c.h>
14 #include <linux/module.h>
15 #include <linux/sigma.h>
16 #include <linux/export.h>
17
18 /* Return: 0==OK, <0==error, =1 ==no more actions */
19 static int
20 process_sigma_action(struct i2c_client *client, struct sigma_firmware *ssfw)
21 {
22         struct sigma_action *sa = (void *)(ssfw->fw->data + ssfw->pos);
23         size_t len = sigma_action_len(sa);
24         int ret = 0;
25
26         pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__,
27                 sa->instr, sa->addr, len);
28
29         switch (sa->instr) {
30         case SIGMA_ACTION_WRITEXBYTES:
31         case SIGMA_ACTION_WRITESINGLE:
32         case SIGMA_ACTION_WRITESAFELOAD:
33                 if (ssfw->fw->size < ssfw->pos + len)
34                         return -EINVAL;
35                 ret = i2c_master_send(client, (void *)&sa->addr, len);
36                 if (ret < 0)
37                         return -EINVAL;
38                 break;
39
40         case SIGMA_ACTION_DELAY:
41                 ret = 0;
42                 udelay(len);
43                 len = 0;
44                 break;
45
46         case SIGMA_ACTION_END:
47                 return 1;
48
49         default:
50                 return -EINVAL;
51         }
52
53         /* when arrive here ret=0 or sent data */
54         ssfw->pos += sigma_action_size(sa, len);
55         return ssfw->pos == ssfw->fw->size;
56 }
57
58 static int
59 process_sigma_actions(struct i2c_client *client, struct sigma_firmware *ssfw)
60 {
61         pr_debug("%s: processing %p\n", __func__, ssfw);
62
63         while (1) {
64                 int ret = process_sigma_action(client, ssfw);
65                 pr_debug("%s: action returned %i\n", __func__, ret);
66                 if (ret == 1)
67                         return 0;
68                 else if (ret)
69                         return ret;
70         }
71 }
72
73 int process_sigma_firmware(struct i2c_client *client, const char *name)
74 {
75         int ret;
76         struct sigma_firmware_header *ssfw_head;
77         struct sigma_firmware ssfw;
78         const struct firmware *fw;
79         u32 crc;
80
81         pr_debug("%s: loading firmware %s\n", __func__, name);
82
83         /* first load the blob */
84         ret = request_firmware(&fw, name, &client->dev);
85         if (ret) {
86                 pr_debug("%s: request_firmware() failed with %i\n", __func__, ret);
87                 return ret;
88         }
89         ssfw.fw = fw;
90
91         /* then verify the header */
92         ret = -EINVAL;
93         if (fw->size < sizeof(*ssfw_head))
94                 goto done;
95
96         ssfw_head = (void *)fw->data;
97         if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic)))
98                 goto done;
99
100         crc = crc32(0, fw->data, fw->size);
101         pr_debug("%s: crc=%x\n", __func__, crc);
102         if (crc != ssfw_head->crc)
103                 goto done;
104
105         ssfw.pos = sizeof(*ssfw_head);
106
107         /* finally process all of the actions */
108         ret = process_sigma_actions(client, &ssfw);
109
110  done:
111         release_firmware(fw);
112
113         pr_debug("%s: loaded %s\n", __func__, name);
114
115         return ret;
116 }
117 EXPORT_SYMBOL(process_sigma_firmware);
118
119 MODULE_LICENSE("GPL");