diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2016-05-02 21:57:55 +0100 |
---|---|---|
committer | Gerrit Rockbox <gerrit@rockbox.org> | 2016-06-01 22:55:37 +0200 |
commit | 4d42e3685c55a6de9d05003f8482f7fb1be022fd (patch) | |
tree | 121c5fbe8a7f63a452fc523571962ecc60bd625c /firmware | |
parent | 615c638c7da1be395a13dd107be004b03a9c390d (diff) | |
download | rockbox-4d42e3685c55a6de9d05003f8482f7fb1be022fd.tar.gz rockbox-4d42e3685c55a6de9d05003f8482f7fb1be022fd.zip |
imx233: rewrite i2c driver
The new driver provides several new features:
- asynchronous transfer
- transactions (several transfers executed at once)
- queueing
The style still provides the legacy interface.
Change-Id: I6d8ecc89d1f7057847c9b2dc69b76cd45c9c8407
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/target/arm/imx233/fmradio-imx233.c | 2 | ||||
-rw-r--r-- | firmware/target/arm/imx233/i2c-imx233.c | 362 | ||||
-rw-r--r-- | firmware/target/arm/imx233/i2c-imx233.h | 70 |
3 files changed, 356 insertions, 78 deletions
diff --git a/firmware/target/arm/imx233/fmradio-imx233.c b/firmware/target/arm/imx233/fmradio-imx233.c index 5263256ca3..85bad41653 100644 --- a/firmware/target/arm/imx233/fmradio-imx233.c +++ b/firmware/target/arm/imx233/fmradio-imx233.c | |||
@@ -24,7 +24,7 @@ | |||
24 | #include "fmradio-imx233.h" | 24 | #include "fmradio-imx233.h" |
25 | #include "fmradio-target.h" | 25 | #include "fmradio-target.h" |
26 | #include "pinctrl-imx233.h" | 26 | #include "pinctrl-imx233.h" |
27 | #include "i2c.h" | 27 | #include "i2c-imx233.h" |
28 | #include "generic_i2c.h" | 28 | #include "generic_i2c.h" |
29 | 29 | ||
30 | #ifndef IMX233_FMRADIO_I2C | 30 | #ifndef IMX233_FMRADIO_I2C |
diff --git a/firmware/target/arm/imx233/i2c-imx233.c b/firmware/target/arm/imx233/i2c-imx233.c index d4e20d8a21..d6f0830ede 100644 --- a/firmware/target/arm/imx233/i2c-imx233.c +++ b/firmware/target/arm/imx233/i2c-imx233.c | |||
@@ -45,6 +45,166 @@ | |||
45 | * are cache aligned and will copy back the data to user buffers at the end. | 45 | * are cache aligned and will copy back the data to user buffers at the end. |
46 | * The I2C_BUFFER_SIZE define controls the size of the buffer. All transfers | 46 | * The I2C_BUFFER_SIZE define controls the size of the buffer. All transfers |
47 | * should probably fit within 512 bytes. | 47 | * should probably fit within 512 bytes. |
48 | * | ||
49 | * On top of this, transfers are queued using the 'next' field of imx233_i2c_xfer_t. | ||
50 | * Each time a transfer is programmed, it is translated to dma transfers using | ||
51 | * the dma API. | ||
52 | */ | ||
53 | |||
54 | /** | ||
55 | * Internal DMA API to build the transfer. | ||
56 | * NOTE the api does not perform any locking, it is up to the caller to ensure | ||
57 | * that there only one transfer beint built at any time. | ||
58 | */ | ||
59 | |||
60 | /* start building a transfer */ | ||
61 | static void imx233_i2c_begin(void); | ||
62 | /* add a stage | ||
63 | * NOTE for transmit, the data is copied to a buffer so the buffer can be freed | ||
64 | * afer this function return. For receive, buffer must exists until transfer is | ||
65 | * complete. This function assumes any receive transfer is final (master will | ||
66 | * send NAK). */ | ||
67 | static void imx233_i2c_add(bool start, bool transmit, void *buffer, unsigned size, bool stop); | ||
68 | /* end building a transfer and start the transfer */ | ||
69 | static void imx233_i2c_kick(void); | ||
70 | /* abort transfer (will call imx233_i2c_transfer_complete) */ | ||
71 | static void imx233_i2c_abort(void); | ||
72 | /* set speed */ | ||
73 | static void imx233_i2c_set_speed(bool fast_mode); | ||
74 | /* callback function when transfer is finished */ | ||
75 | static void imx233_i2c_transfer_complete(enum imx233_i2c_error_t status); | ||
76 | |||
77 | /** | ||
78 | * Advanced API | ||
79 | */ | ||
80 | |||
81 | /* NOTE these variables are not marked as volatile because all functions | ||
82 | * do all operation with IRQ disabled, so they won't change their value | ||
83 | * in the middle of a function */ | ||
84 | static struct imx233_i2c_xfer_t *i2c_head; /* pointer to the current transfer */ | ||
85 | static struct imx233_i2c_xfer_t *i2c_tail; /* pointer to the last transfer */ | ||
86 | static struct timeout i2c_tmo; /* timeout */ | ||
87 | |||
88 | /* timeout callback */ | ||
89 | static int imx233_i2c_timeout(struct timeout *tmo); | ||
90 | |||
91 | /* called in IRQ context or with IRQ disabled */ | ||
92 | static void imx233_i2c_start(void) | ||
93 | { | ||
94 | uint8_t addr_wr = i2c_head->dev_addr; | ||
95 | uint8_t addr_rd = i2c_head->dev_addr | 1; | ||
96 | /* translate transfer using DMA API */ | ||
97 | imx233_i2c_set_speed(i2c_head->fast_mode); | ||
98 | imx233_i2c_begin(); | ||
99 | if(i2c_head->mode == I2C_WRITE) | ||
100 | { | ||
101 | /* START + addr */ | ||
102 | imx233_i2c_add(true, true, &addr_wr, 1, false); | ||
103 | /* data + stop if no second stage */ | ||
104 | imx233_i2c_add(false, true, i2c_head->data[0], i2c_head->count[0], i2c_head->count[1] == 0); | ||
105 | /* (if second stage) data + stop */ | ||
106 | if(i2c_head->count[1] > 0) | ||
107 | imx233_i2c_add(false, true, i2c_head->data[1], i2c_head->count[1], true); | ||
108 | } | ||
109 | else /* I2C_READ */ | ||
110 | { | ||
111 | /* (if write stage) */ | ||
112 | if(i2c_head->count[0] > 0) | ||
113 | { | ||
114 | /* START + addr */ | ||
115 | imx233_i2c_add(true, true, &addr_wr, 1, false); | ||
116 | /* data */ | ||
117 | imx233_i2c_add(false, true, i2c_head->data[0], i2c_head->count[0], false); | ||
118 | } | ||
119 | /* START + addr */ | ||
120 | imx233_i2c_add(true, true, &addr_rd, 1, false); | ||
121 | /* read data + stop */ | ||
122 | imx233_i2c_add(false, false, i2c_head->data[1], i2c_head->count[1], true); | ||
123 | } | ||
124 | /* kick transfer */ | ||
125 | imx233_i2c_kick(); | ||
126 | /* setup timer for timeout */ | ||
127 | if(i2c_head->tmo_ms > 0) | ||
128 | timeout_register(&i2c_tmo, imx233_i2c_timeout, i2c_head->tmo_ms * HZ / 1000, 0); | ||
129 | } | ||
130 | |||
131 | /* unqueue head and notify completion, called with IRQ disabled */ | ||
132 | static void imx233_i2c_unqueue_head(enum imx233_i2c_error_t status) | ||
133 | { | ||
134 | /* notify */ | ||
135 | if(i2c_head->callback) | ||
136 | i2c_head->callback(i2c_head, status); | ||
137 | /* unqueue */ | ||
138 | i2c_head = i2c_head->next; | ||
139 | } | ||
140 | |||
141 | /* callback function when transfer is finished, called with IRQ disabled */ | ||
142 | static void imx233_i2c_transfer_complete(enum imx233_i2c_error_t status) | ||
143 | { | ||
144 | /* cancel timeout | ||
145 | * NOTE because IRQ are disabled, the timeout callback will not be called | ||
146 | * until we enable them back, at which point we will have disabled the timeout | ||
147 | * so the completion routine will not be called twice. */ | ||
148 | if(i2c_head->tmo_ms > 0) | ||
149 | timeout_cancel(&i2c_tmo); | ||
150 | /* notify completion and unqueue | ||
151 | * WARNING completion callback can queue other transfers, so the only part | ||
152 | * of the queue that cannot change is this transaction, everything else can | ||
153 | * change */ | ||
154 | struct imx233_i2c_xfer_t *this_xfer = i2c_head; | ||
155 | struct imx233_i2c_xfer_t *last_xfer = i2c_head->last; /* in transaction */ | ||
156 | /* unqueue head */ | ||
157 | imx233_i2c_unqueue_head(status); | ||
158 | /* in case of failure, skip others */ | ||
159 | if(status != I2C_SUCCESS && this_xfer != last_xfer) | ||
160 | while(i2c_head != last_xfer) | ||
161 | imx233_i2c_unqueue_head(I2C_SKIP); | ||
162 | /* if there is anything left, start it */ | ||
163 | if(i2c_head) | ||
164 | imx233_i2c_start(); | ||
165 | } | ||
166 | |||
167 | static int imx233_i2c_timeout(struct timeout *tmo) | ||
168 | { | ||
169 | (void) tmo; | ||
170 | imx233_i2c_abort(); | ||
171 | return 0; /* do not fire again */ | ||
172 | } | ||
173 | |||
174 | void imx233_i2c_transfer(struct imx233_i2c_xfer_t *xfer) | ||
175 | { | ||
176 | /* avoid any race with the irq handler */ | ||
177 | unsigned long cpsr = disable_irq_save(); | ||
178 | /* before queuing, update link to last transfer in each transfer */ | ||
179 | struct imx233_i2c_xfer_t *last = xfer; | ||
180 | while(last->next) | ||
181 | last = last->next; | ||
182 | struct imx233_i2c_xfer_t *tmp = xfer; | ||
183 | while(tmp) | ||
184 | { | ||
185 | tmp->last = last; | ||
186 | tmp = tmp->next; | ||
187 | } | ||
188 | /* no transfer pending: start one */ | ||
189 | if(i2c_head == NULL) | ||
190 | { | ||
191 | i2c_head = xfer; | ||
192 | i2c_tail = last; | ||
193 | /* kick transfer now */ | ||
194 | imx233_i2c_start(); | ||
195 | } | ||
196 | /* pending transer: queue and let the irq handler process it for us */ | ||
197 | else | ||
198 | { | ||
199 | i2c_tail->next = xfer; | ||
200 | i2c_tail = last; | ||
201 | } | ||
202 | |||
203 | restore_irq(cpsr); | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * DMA API implementation | ||
48 | */ | 208 | */ |
49 | 209 | ||
50 | /* Used for DMA */ | 210 | /* Used for DMA */ |
@@ -53,7 +213,7 @@ struct i2c_dma_command_t | |||
53 | struct apb_dma_command_t dma; | 213 | struct apb_dma_command_t dma; |
54 | /* PIO words */ | 214 | /* PIO words */ |
55 | uint32_t ctrl0; | 215 | uint32_t ctrl0; |
56 | /* copy buffer copy */ | 216 | /* copy buffer pointers */ |
57 | void *src; | 217 | void *src; |
58 | void *dst; | 218 | void *dst; |
59 | /* padded to next multiple of cache line size (32 bytes) */ | 219 | /* padded to next multiple of cache line size (32 bytes) */ |
@@ -67,31 +227,9 @@ __ENSURE_STRUCT_CACHE_FRIENDLY(struct i2c_dma_command_t) | |||
67 | /* Current transfer */ | 227 | /* Current transfer */ |
68 | static int i2c_nr_stages; | 228 | static int i2c_nr_stages; |
69 | static struct i2c_dma_command_t i2c_stage[I2C_NR_STAGES]; | 229 | static struct i2c_dma_command_t i2c_stage[I2C_NR_STAGES]; |
70 | static struct mutex i2c_mutex; | ||
71 | static struct semaphore i2c_sema; | ||
72 | static uint8_t i2c_buffer[I2C_BUFFER_SIZE] CACHEALIGN_ATTR; | 230 | static uint8_t i2c_buffer[I2C_BUFFER_SIZE] CACHEALIGN_ATTR; |
73 | static uint32_t i2c_buffer_end; /* current end */ | 231 | static uint32_t i2c_buffer_end; /* current end */ |
74 | 232 | ||
75 | void INT_I2C_DMA(void) | ||
76 | { | ||
77 | /* reset dma channel on error */ | ||
78 | if(imx233_dma_is_channel_error_irq(APB_I2C)) | ||
79 | imx233_dma_reset_channel(APB_I2C); | ||
80 | /* clear irq flags */ | ||
81 | imx233_dma_clear_channel_interrupt(APB_I2C); | ||
82 | semaphore_release(&i2c_sema); | ||
83 | } | ||
84 | |||
85 | void INT_I2C_ERROR(void) | ||
86 | { | ||
87 | /* reset dma channel on error */ | ||
88 | if(imx233_dma_is_channel_error_irq(APB_I2C)) | ||
89 | imx233_dma_reset_channel(APB_I2C); | ||
90 | /* clear irq flags */ | ||
91 | imx233_dma_clear_channel_interrupt(APB_I2C); | ||
92 | semaphore_release(&i2c_sema); | ||
93 | } | ||
94 | |||
95 | static void imx233_i2c_reset(void) | 233 | static void imx233_i2c_reset(void) |
96 | { | 234 | { |
97 | /* clear softreset */ | 235 | /* clear softreset */ |
@@ -107,10 +245,6 @@ static void imx233_i2c_reset(void) | |||
107 | BF_SET(I2C_CTRL1, ACK_MODE); | 245 | BF_SET(I2C_CTRL1, ACK_MODE); |
108 | #endif | 246 | #endif |
109 | BF_SET(I2C_CTRL0, CLKGATE); | 247 | BF_SET(I2C_CTRL0, CLKGATE); |
110 | /* Fast-mode @ 400K */ | ||
111 | HW_I2C_TIMING0 = 0x000F0007; /* tHIGH=0.6us, read at 0.3us */ | ||
112 | HW_I2C_TIMING1 = 0x001F000F; /* tLOW=1.3us, write at 0.6us */ | ||
113 | HW_I2C_TIMING2 = 0x0015000D; | ||
114 | } | 248 | } |
115 | 249 | ||
116 | void imx233_i2c_init(void) | 250 | void imx233_i2c_init(void) |
@@ -120,31 +254,27 @@ void imx233_i2c_init(void) | |||
120 | imx233_pinctrl_setup_vpin(VPIN_I2C_SCL, "i2c scl", PINCTRL_DRIVE_4mA, true); | 254 | imx233_pinctrl_setup_vpin(VPIN_I2C_SCL, "i2c scl", PINCTRL_DRIVE_4mA, true); |
121 | imx233_pinctrl_setup_vpin(VPIN_I2C_SDA, "i2c sda", PINCTRL_DRIVE_4mA, true); | 255 | imx233_pinctrl_setup_vpin(VPIN_I2C_SDA, "i2c sda", PINCTRL_DRIVE_4mA, true); |
122 | imx233_i2c_reset(); | 256 | imx233_i2c_reset(); |
123 | mutex_init(&i2c_mutex); | 257 | i2c_head = i2c_tail = NULL; |
124 | semaphore_init(&i2c_sema, 1, 0); | ||
125 | } | 258 | } |
126 | 259 | ||
127 | void imx233_i2c_begin(void) | 260 | static void imx233_i2c_begin(void) |
128 | { | 261 | { |
129 | mutex_lock(&i2c_mutex); | ||
130 | /* wakeup */ | 262 | /* wakeup */ |
131 | BF_CLR(I2C_CTRL0, CLKGATE); | 263 | BF_CLR(I2C_CTRL0, CLKGATE); |
132 | i2c_nr_stages = 0; | 264 | i2c_nr_stages = 0; |
133 | i2c_buffer_end = 0; | 265 | i2c_buffer_end = 0; |
134 | } | 266 | } |
135 | 267 | ||
136 | enum imx233_i2c_error_t imx233_i2c_add(bool start, bool transmit, void *buffer, unsigned size, bool stop) | 268 | static void imx233_i2c_add(bool start, bool transmit, |
269 | void *buffer, unsigned size, bool stop) | ||
137 | { | 270 | { |
138 | if(i2c_nr_stages == I2C_NR_STAGES) | 271 | if(i2c_nr_stages == I2C_NR_STAGES) |
139 | return I2C_ERROR; | 272 | panicf("i2c: too many stages"); |
140 | /* align buffer end on cache boundary */ | 273 | /* align buffer end on cache boundary */ |
141 | uint32_t start_off = CACHEALIGN_UP(i2c_buffer_end); | 274 | uint32_t start_off = CACHEALIGN_UP(i2c_buffer_end); |
142 | uint32_t end_off = start_off + size; | 275 | uint32_t end_off = start_off + size; |
143 | if(end_off > I2C_BUFFER_SIZE) | 276 | if(end_off > I2C_BUFFER_SIZE) |
144 | { | 277 | panicf("i2c: transfer is too big"); |
145 | panicf("die"); | ||
146 | return I2C_BUFFER_FULL; | ||
147 | } | ||
148 | i2c_buffer_end = end_off; | 278 | i2c_buffer_end = end_off; |
149 | if(transmit) | 279 | if(transmit) |
150 | { | 280 | { |
@@ -175,7 +305,6 @@ enum imx233_i2c_error_t imx233_i2c_add(bool start, bool transmit, void *buffer, | |||
175 | XFER_COUNT(size), DIRECTION(transmit), SEND_NAK_ON_LAST(!transmit), | 305 | XFER_COUNT(size), DIRECTION(transmit), SEND_NAK_ON_LAST(!transmit), |
176 | PRE_SEND_START(start), POST_SEND_STOP(stop), MASTER_MODE(1)); | 306 | PRE_SEND_START(start), POST_SEND_STOP(stop), MASTER_MODE(1)); |
177 | i2c_nr_stages++; | 307 | i2c_nr_stages++; |
178 | return I2C_SUCCESS; | ||
179 | } | 308 | } |
180 | 309 | ||
181 | static enum imx233_i2c_error_t imx233_i2c_finalize(void) | 310 | static enum imx233_i2c_error_t imx233_i2c_finalize(void) |
@@ -190,10 +319,10 @@ static enum imx233_i2c_error_t imx233_i2c_finalize(void) | |||
190 | return I2C_SUCCESS; | 319 | return I2C_SUCCESS; |
191 | } | 320 | } |
192 | 321 | ||
193 | enum imx233_i2c_error_t imx233_i2c_end(unsigned timeout) | 322 | static void imx233_i2c_kick(void) |
194 | { | 323 | { |
195 | if(i2c_nr_stages == 0) | 324 | if(i2c_nr_stages == 0) |
196 | return I2C_ERROR; | 325 | panicf("i2c: empty kick"); |
197 | i2c_stage[i2c_nr_stages - 1].dma.cmd |= BM_APB_CHx_CMD_SEMAPHORE | BM_APB_CHx_CMD_IRQONCMPLT; | 326 | i2c_stage[i2c_nr_stages - 1].dma.cmd |= BM_APB_CHx_CMD_SEMAPHORE | BM_APB_CHx_CMD_IRQONCMPLT; |
198 | 327 | ||
199 | BF_CLR(I2C_CTRL1, SLAVE_IRQ, SLAVE_STOP_IRQ, MASTER_LOSS_IRQ, EARLY_TERM_IRQ, | 328 | BF_CLR(I2C_CTRL1, SLAVE_IRQ, SLAVE_STOP_IRQ, MASTER_LOSS_IRQ, EARLY_TERM_IRQ, |
@@ -203,15 +332,24 @@ enum imx233_i2c_error_t imx233_i2c_end(unsigned timeout) | |||
203 | imx233_icoll_enable_interrupt(INT_SRC_I2C_ERROR, true); | 332 | imx233_icoll_enable_interrupt(INT_SRC_I2C_ERROR, true); |
204 | imx233_dma_enable_channel_interrupt(APB_I2C, true); | 333 | imx233_dma_enable_channel_interrupt(APB_I2C, true); |
205 | imx233_dma_start_command(APB_I2C, &i2c_stage[0].dma); | 334 | imx233_dma_start_command(APB_I2C, &i2c_stage[0].dma); |
335 | } | ||
206 | 336 | ||
337 | static void imx233_i2c_abort(void) | ||
338 | { | ||
339 | /* FIXME there is a race condition here: if dma irq fires right before we | ||
340 | * reset the channel, it will most likely trigger an IRQ anyway. It is | ||
341 | * extremely unlikely but ideally, we should check this in the IRQ handler | ||
342 | * with an id/counter. */ | ||
343 | imx233_dma_reset_channel(APB_I2C); | ||
344 | imx233_i2c_reset(); | ||
345 | imx233_i2c_transfer_complete(I2C_TIMEOUT); | ||
346 | } | ||
347 | |||
348 | static enum imx233_i2c_error_t imx233_i2c_end(void) | ||
349 | { | ||
207 | enum imx233_i2c_error_t ret; | 350 | enum imx233_i2c_error_t ret; |
208 | if(semaphore_wait(&i2c_sema, timeout) == OBJ_WAIT_TIMEDOUT) | 351 | /* check for various errors */ |
209 | { | 352 | if(BF_RD(I2C_CTRL1, MASTER_LOSS_IRQ)) |
210 | imx233_dma_reset_channel(APB_I2C); | ||
211 | imx233_i2c_reset(); | ||
212 | ret = I2C_TIMEOUT; | ||
213 | } | ||
214 | else if(BF_RD(I2C_CTRL1, MASTER_LOSS_IRQ)) | ||
215 | ret = I2C_MASTER_LOSS; | 353 | ret = I2C_MASTER_LOSS; |
216 | else if(BF_RD(I2C_CTRL1, NO_SLAVE_ACK_IRQ)) | 354 | else if(BF_RD(I2C_CTRL1, NO_SLAVE_ACK_IRQ)) |
217 | { | 355 | { |
@@ -229,48 +367,132 @@ enum imx233_i2c_error_t imx233_i2c_end(unsigned timeout) | |||
229 | ret = imx233_i2c_finalize(); | 367 | ret = imx233_i2c_finalize(); |
230 | /* sleep */ | 368 | /* sleep */ |
231 | BF_SET(I2C_CTRL0, CLKGATE); | 369 | BF_SET(I2C_CTRL0, CLKGATE); |
232 | mutex_unlock(&i2c_mutex); | ||
233 | return ret; | 370 | return ret; |
234 | } | 371 | } |
235 | 372 | ||
373 | static void imx233_i2c_set_speed(bool fast_mode) | ||
374 | { | ||
375 | /* See I2C specification for standard- and fast-mode timings | ||
376 | * Clock is derived APBX which we assume to be running at 24 MHz. */ | ||
377 | if(fast_mode) | ||
378 | { | ||
379 | /* Fast-mode @ 400 kHz */ | ||
380 | HW_I2C_TIMING0 = 0x000f0007; /* HIGH_COUNT=0.6us, RCV_COUNT=0.2us */ | ||
381 | HW_I2C_TIMING1 = 0x001f000f; /* LOW_COUNT=1.3us, XMIT_COUNT=0.6us */ | ||
382 | HW_I2C_TIMING2 = 0x0015000d; /* BUS_FREE=0.9us LEADIN_COUNT=0.55us */ | ||
383 | } | ||
384 | else | ||
385 | { | ||
386 | /* Standard-mode @ 100 kHz */ | ||
387 | HW_I2C_TIMING0 = 0x00780030; /* HIGH_COUNT=5us, RCV_COUNT=2us */ | ||
388 | HW_I2C_TIMING1 = 0x00800030; /* LOW_COUNT=5.3us, XMIT_COUNT=2us */ | ||
389 | HW_I2C_TIMING2 = 0x00300030; /* BUS_FREE=2us LEADIN_COUNT=2us */ | ||
390 | } | ||
391 | } | ||
392 | |||
393 | static void imx233_i2c_irq(bool err) | ||
394 | { | ||
395 | if(err) | ||
396 | panicf("i2c: dma error"); | ||
397 | /* reset dma channel on error */ | ||
398 | if(imx233_dma_is_channel_error_irq(APB_I2C)) | ||
399 | imx233_dma_reset_channel(APB_I2C); | ||
400 | /* clear irq flags */ | ||
401 | imx233_dma_clear_channel_interrupt(APB_I2C); | ||
402 | /* handle completion */ | ||
403 | imx233_i2c_transfer_complete(imx233_i2c_end()); | ||
404 | } | ||
405 | |||
406 | void INT_I2C_DMA(void) | ||
407 | { | ||
408 | imx233_i2c_irq(false); | ||
409 | } | ||
410 | |||
411 | void INT_I2C_ERROR(void) | ||
412 | { | ||
413 | imx233_i2c_irq(true); | ||
414 | } | ||
415 | |||
416 | /** Public API */ | ||
417 | |||
236 | void i2c_init(void) | 418 | void i2c_init(void) |
237 | { | 419 | { |
238 | } | 420 | } |
239 | 421 | ||
422 | struct imx233_i2c_sync_xfer_t | ||
423 | { | ||
424 | struct imx233_i2c_xfer_t xfer; | ||
425 | struct semaphore sema; | ||
426 | volatile enum imx233_i2c_error_t status; | ||
427 | }; | ||
428 | |||
429 | /* synchronous callback: record status and release semaphore */ | ||
430 | static void i2c_sync_callback(struct imx233_i2c_xfer_t *xfer, enum imx233_i2c_error_t status) | ||
431 | { | ||
432 | struct imx233_i2c_sync_xfer_t *sxfer = (void *)xfer; | ||
433 | sxfer->status = status; | ||
434 | semaphore_release(&sxfer->sema); | ||
435 | } | ||
436 | |||
437 | static int i2c_sync_transfer(struct imx233_i2c_sync_xfer_t *xfer) | ||
438 | { | ||
439 | semaphore_init(&xfer->sema, 1, 0); | ||
440 | /* common init */ | ||
441 | xfer->xfer.next = NULL; | ||
442 | xfer->xfer.callback = &i2c_sync_callback; | ||
443 | xfer->xfer.fast_mode = true; | ||
444 | xfer->xfer.tmo_ms = 1000; | ||
445 | /* kick */ | ||
446 | imx233_i2c_transfer(&xfer->xfer); | ||
447 | /* wait */ | ||
448 | semaphore_wait(&xfer->sema, TIMEOUT_BLOCK); | ||
449 | return (int)xfer->status; | ||
450 | } | ||
451 | |||
240 | int i2c_write(int device, const unsigned char* buf, int count) | 452 | int i2c_write(int device, const unsigned char* buf, int count) |
241 | { | 453 | { |
242 | uint8_t addr = device; | 454 | struct imx233_i2c_sync_xfer_t xfer; |
243 | imx233_i2c_begin(); | 455 | xfer.xfer.dev_addr = device; |
244 | imx233_i2c_add(true, true, &addr, 1, false); /* start + dev addr */ | 456 | xfer.xfer.mode = I2C_WRITE; |
245 | imx233_i2c_add(false, true, (void *)buf, count, true); /* data + stop */ | 457 | xfer.xfer.count[0] = count; |
246 | return imx233_i2c_end(10); | 458 | xfer.xfer.data[0] = (void *)buf; |
459 | xfer.xfer.count[1] = 0; | ||
460 | return i2c_sync_transfer(&xfer); | ||
247 | } | 461 | } |
248 | 462 | ||
249 | int i2c_read(int device, unsigned char* buf, int count) | 463 | int i2c_read(int device, unsigned char* buf, int count) |
250 | { | 464 | { |
251 | uint8_t addr = device | 1; | 465 | struct imx233_i2c_sync_xfer_t xfer; |
252 | imx233_i2c_begin(); | 466 | xfer.xfer.dev_addr = device; |
253 | imx233_i2c_add(true, true, &addr, 1, false); /* start + dev addr */ | 467 | xfer.xfer.mode = I2C_READ; |
254 | imx233_i2c_add(false, false, buf, count, true); /* data + stop */ | 468 | xfer.xfer.count[0] = 0; |
255 | return imx233_i2c_end(10); | 469 | xfer.xfer.count[1] = count; |
470 | xfer.xfer.data[1] = buf; | ||
471 | return i2c_sync_transfer(&xfer); | ||
256 | } | 472 | } |
257 | 473 | ||
258 | int i2c_readmem(int device, int address, unsigned char* buf, int count) | 474 | int i2c_readmem(int device, int address, unsigned char* buf, int count) |
259 | { | 475 | { |
260 | uint8_t start[2] = {device, address}; | 476 | uint8_t addr = address; /* assume 1 byte */ |
261 | uint8_t addr_rd = device | 1; | 477 | struct imx233_i2c_sync_xfer_t xfer; |
262 | imx233_i2c_begin(); | 478 | xfer.xfer.dev_addr = device; |
263 | imx233_i2c_add(true, true, start, 2, false); /* start + dev addr + addr */ | 479 | xfer.xfer.mode = I2C_READ; |
264 | imx233_i2c_add(true, true, &addr_rd, 1, false); /* start + dev addr */ | 480 | xfer.xfer.count[0] = 1; |
265 | imx233_i2c_add(false, false, buf, count, true); /* data + stop */ | 481 | xfer.xfer.data[0] = &addr; |
266 | return imx233_i2c_end(10); | 482 | xfer.xfer.count[1] = count; |
483 | xfer.xfer.data[1] = buf; | ||
484 | return i2c_sync_transfer(&xfer); | ||
267 | } | 485 | } |
268 | 486 | ||
269 | int i2c_writemem(int device, int address, const unsigned char* buf, int count) | 487 | int i2c_writemem(int device, int address, const unsigned char* buf, int count) |
270 | { | 488 | { |
271 | uint8_t start[2] = {device, address}; | 489 | uint8_t addr = address; /* assume 1 byte */ |
272 | imx233_i2c_begin(); | 490 | struct imx233_i2c_sync_xfer_t xfer; |
273 | imx233_i2c_add(true, true, start, 2, false); /* start + dev addr + addr */ | 491 | xfer.xfer.dev_addr = device; |
274 | imx233_i2c_add(false, true, (void *)buf, count, true); /* data + stop */ | 492 | xfer.xfer.mode = I2C_WRITE; |
275 | return imx233_i2c_end(10); | 493 | xfer.xfer.count[0] = 1; |
494 | xfer.xfer.data[0] = &addr; | ||
495 | xfer.xfer.count[1] = count; | ||
496 | xfer.xfer.data[1] = (void *)buf; | ||
497 | return i2c_sync_transfer(&xfer); | ||
276 | } | 498 | } |
diff --git a/firmware/target/arm/imx233/i2c-imx233.h b/firmware/target/arm/imx233/i2c-imx233.h index 263e93aa77..33e38e38da 100644 --- a/firmware/target/arm/imx233/i2c-imx233.h +++ b/firmware/target/arm/imx233/i2c-imx233.h | |||
@@ -24,7 +24,6 @@ | |||
24 | #include "cpu.h" | 24 | #include "cpu.h" |
25 | #include "system.h" | 25 | #include "system.h" |
26 | #include "system-target.h" | 26 | #include "system-target.h" |
27 | #include "i2c.h" | ||
28 | 27 | ||
29 | enum imx233_i2c_error_t | 28 | enum imx233_i2c_error_t |
30 | { | 29 | { |
@@ -35,14 +34,71 @@ enum imx233_i2c_error_t | |||
35 | I2C_NO_SLAVE_ACK = -4, | 34 | I2C_NO_SLAVE_ACK = -4, |
36 | I2C_SLAVE_NAK = -5, | 35 | I2C_SLAVE_NAK = -5, |
37 | I2C_BUFFER_FULL = -6, | 36 | I2C_BUFFER_FULL = -6, |
37 | I2C_SKIP = -7, /* transfer skipped before of previous error in transaction */ | ||
38 | }; | 38 | }; |
39 | 39 | ||
40 | /** Important notes on the driver. | ||
41 | * | ||
42 | * The driver supports both synchronous and asynchronous transfers. Asynchronous | ||
43 | * transfer functions can safely be called from IRQ context. Beware that the completion | ||
44 | * callback of asynchronous transfers may be called from IRQ context. | ||
45 | * | ||
46 | * The driver supports queuing so it is safe to call several transfer functions | ||
47 | * concurrently. | ||
48 | */ | ||
49 | |||
40 | void imx233_i2c_init(void); | 50 | void imx233_i2c_init(void); |
41 | /* start building a transfer, will acquire an exclusive lock */ | 51 | |
42 | void imx233_i2c_begin(void); | 52 | /** legacy API: |
43 | /* add stage */ | 53 | * read/write functions return 0 on success */ |
44 | enum imx233_i2c_error_t imx233_i2c_add(bool start, bool transmit, void *buffer, unsigned size, bool stop); | 54 | int i2c_write(int device, const unsigned char* buf, int count); |
45 | /* end building a transfer and start the transfer */ | 55 | int i2c_read(int device, unsigned char* buf, int count); |
46 | enum imx233_i2c_error_t imx233_i2c_end(unsigned timeout); | 56 | int i2c_readmem(int device, int address, unsigned char* buf, int count); |
57 | int i2c_writemem(int device, int address, const unsigned char* buf, int count); | ||
58 | |||
59 | /** Advanced API */ | ||
60 | struct imx233_i2c_xfer_t; | ||
61 | typedef void (*imx233_i2c_cb_t)(struct imx233_i2c_xfer_t *xfer, enum imx233_i2c_error_t status); | ||
62 | |||
63 | /** Transfer mode. There are currently two types of transfers, to make | ||
64 | * programming simpler: | ||
65 | * - write: write count[0] bytes from data[0], and then count[1] bytes from data[1] | ||
66 | * (if count[1] > 0). The second stage is useful to avoid allocating a single | ||
67 | * buffer to hold address + data for example. | ||
68 | * - read: write count[0] bytes from data[0], and then read count[1] bytes from data[1]. | ||
69 | * If count[0] = 0 then the write stage is ignored. | ||
70 | */ | ||
71 | enum imx233_i2x_xfer_mode_t | ||
72 | { | ||
73 | I2C_WRITE, | ||
74 | I2C_READ, | ||
75 | }; | ||
76 | |||
77 | /** This data structure represents one transfer. The exact shape of the transfer | ||
78 | * depends on the mode. | ||
79 | * NOTE a single transfer is limited to 512 bytes of data (RX + TX) | ||
80 | * A transaction is made up of several transfers, chained together. | ||
81 | */ | ||
82 | struct imx233_i2c_xfer_t | ||
83 | { | ||
84 | struct imx233_i2c_xfer_t *next; /* next transfer, or NULL */ | ||
85 | imx233_i2c_cb_t callback; /* NULL for no callack */ | ||
86 | int dev_addr; /* device address */ | ||
87 | bool fast_mode; /* 400 kHz 'fast' mode or 100 kHz 'normal' mode */ | ||
88 | enum imx233_i2x_xfer_mode_t mode; /* transfer mode */ | ||
89 | int count[2]; /* count: depends on mode */ | ||
90 | void *data[2]; /* data: depends on mode */ | ||
91 | unsigned tmo_ms; /* timeout in milliseconds (0 means infinite) */ | ||
92 | /* internal */ | ||
93 | struct imx233_i2c_xfer_t *last; /* last in transaction */ | ||
94 | }; | ||
95 | |||
96 | /* Queue one or more tranfer. If several transfer are queued (transaction) | ||
97 | * they are guaranteed to be executed "as one" (ie without interleaving). Furthermore, | ||
98 | * if a transfer of the transfaction fails, the remaining transfers of the transactions | ||
99 | * will NOT be executed (and their callback will be called with SKIP status). | ||
100 | * Return 0 if queueing was successful. Note that the transfer may still fail, | ||
101 | * in which case the callback will have a nonzero status code. */ | ||
102 | void imx233_i2c_transfer(struct imx233_i2c_xfer_t *xfer); | ||
47 | 103 | ||
48 | #endif // __I2C_IMX233_H__ | 104 | #endif // __I2C_IMX233_H__ |