source: rtems/c/src/lib/libbsp/arm/beagle/i2c/bbb-i2c.c @ 8f550d2

Last change on this file since 8f550d2 was 8f550d2, checked in by Sichen Zhao <1473996754@…>, on Jun 14, 2017 at 2:53:44 PM

Add the i2c driver for Beaglebone Black

Update ticket #2891 and my GSOC project
add c/src/lib/libbsp/arm/beagle/i2c/bbb-i2c.c
modify c/src/lib/libbsp/arm/beagle/include/i2c.h
modify c/src/lib/libbsp/arm/beagle/include/bbb-gpio.h
modify c/src/lib/libcpu/arm/shared/include/am335x.h
modify c/src/lib/libbsp/arm/beagle/Makefile.am
Now can read the EEPROM by i2c, the test application link is: https://github.com/hahchenchen/GSOC-test-application

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup arm_beagle
5 *
6 * @brief BeagleBoard I2C bus initialization and API Support.
7 */
8
9/*
10 * Copyright (c) 2017 Sichen Zhao <zsc19940506@gmail.com>
11 *
12 * The license and distribution terms for this file may be
13 * found in the file LICENSE in this distribution or at
14 * http://www.rtems.org/license/LICENSE.
15 */
16
17/*
18 * Modified on Punit Vara<punitvara@gmail.com> works, currently
19 * the i2c file is working on the Beaglebone Black board(AM335x)
20 */
21
22#include <stdio.h>
23#include <bsp/i2c.h>
24#include <libcpu/am335x.h>
25#include <rtems/irq-extension.h>
26#include <rtems/counter.h>
27#include <bsp/bbb-gpio.h>
28#include <rtems/score/assert.h>
29
30static void am335x_i2c0_pinmux( bbb_i2c_bus *bus )
31{
32  REG( bus->regs + AM335X_CONF_I2C0_SDA ) =
33    ( BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN );
34
35  REG( bus->regs + AM335X_CONF_I2C0_SCL ) =
36    ( BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN );
37}
38
39static void I2C0ModuleClkConfig( void )
40{
41  /* Writing to MODULEMODE field of AM335X_CM_WKUP_I2C0_CLKCTRL register. */
42  REG( AM335X_SOC_CM_WKUP_REGS + AM335X_CM_WKUP_I2C0_CLKCTRL ) |=
43    AM335X_CM_WKUP_I2C0_CLKCTRL_MODULEMODE_ENABLE;
44
45  /* Waiting for MODULEMODE field to reflect the written value. */
46  while ( AM335X_CM_WKUP_I2C0_CLKCTRL_MODULEMODE_ENABLE !=
47          ( REG( AM335X_SOC_CM_WKUP_REGS + AM335X_CM_WKUP_I2C0_CLKCTRL ) &
48            AM335X_CM_WKUP_I2C0_CLKCTRL_MODULEMODE ) ) ;
49
50  /*
51  ** Waiting for IDLEST field in AM335X_CM_WKUP_CONTROL_CLKCTRL
52  ** register to attain
53  ** desired value.
54  */
55  while ( ( AM335X_CM_WKUP_CONTROL_CLKCTRL_IDLEST_FUNC <<
56            AM335X_CM_WKUP_CONTROL_CLKCTRL_IDLEST_SHIFT ) !=
57          ( REG( AM335X_SOC_CM_WKUP_REGS + AM335X_CM_WKUP_CONTROL_CLKCTRL ) &
58            AM335X_CM_WKUP_CONTROL_CLKCTRL_IDLEST ) ) ;
59
60  /*
61  ** Waiting for CLKACTIVITY_I2C0_GFCLK field in AM335X_CM_WKUP_CLKSTCTRL
62  ** register to attain desired value.
63  */
64  while ( AM335X_CM_WKUP_CLKSTCTRL_CLKACTIVITY_I2C0_GFCLK !=
65          ( REG( AM335X_SOC_CM_WKUP_REGS + AM335X_CM_WKUP_CLKSTCTRL ) &
66            AM335X_CM_WKUP_CLKSTCTRL_CLKACTIVITY_I2C0_GFCLK ) ) ;
67
68  /*
69  ** Waiting for IDLEST field in AM335X_CM_WKUP_I2C0_CLKCTRL register to attain
70  ** desired value.
71  */
72  while ( ( AM335X_CM_WKUP_I2C0_CLKCTRL_IDLEST_FUNC <<
73            AM335X_CM_WKUP_I2C0_CLKCTRL_IDLEST_SHIFT ) !=
74          ( REG( AM335X_SOC_CM_WKUP_REGS + AM335X_CM_WKUP_I2C0_CLKCTRL ) &
75            AM335X_CM_WKUP_I2C0_CLKCTRL_IDLEST ) ) ;
76}
77
78static void am335x_i2c_reset( bbb_i2c_bus *bus )
79{
80  volatile bbb_i2c_regs *regs = bus->regs;
81  int                    timeout = I2C_TIMEOUT;
82
83  if ( REG( &regs->BBB_I2C_CON ) & BBB_I2C_CON_EN ) {
84    REG( &regs->BBB_I2C_CON ) = BBB_I2C_CON_CLR;
85    udelay( 50000 );
86  }
87
88  REG( &regs->BBB_I2C_SYSC ) = BBB_I2C_SYSC_SRST; /* for ES2 after soft reset */
89  udelay( 1000 );
90  REG( &regs->BBB_I2C_CON ) = BBB_I2C_CON_EN;
91
92  while ( !( REG( &regs->BBB_I2C_SYSS ) & BBB_I2C_SYSS_RDONE ) && timeout-- ) {
93    if ( timeout <= 0 ) {
94      puts( "ERROR: Timeout in soft-reset\n" );
95
96      return;
97    }
98
99    udelay( 1000 );
100  }
101}
102/*
103   Possible values for msg->flag
104 * - @ref I2C_M_TEN,
105 * - @ref I2C_M_RD,
106 * - @ref I2C_M_STOP,
107 * - @ref I2C_M_NOSTART,
108 * - @ref I2C_M_REV_DIR_ADDR,
109 * - @ref I2C_M_IGNORE_NAK,
110 * - @ref I2C_M_NO_RD_ACK, and
111 * - @ref I2C_M_RECV_LEN.
112 */
113
114static void am335x_i2c_set_address_size(
115  const i2c_msg         *msgs,
116  volatile bbb_i2c_regs *regs
117)
118{
119  /*can be configured multiple modes here.
120   **Need to think about own address modes*/
121  if ( ( msgs->flags & I2C_M_TEN ) == 0 ) {
122    /* 7-bit mode slave address mode*/
123    REG( &regs->BBB_I2C_CON ) = AM335X_I2C_CFG_7BIT_SLAVE_ADDR;
124  } else {
125    /* 10-bit slave address mode*/
126    REG( &regs->BBB_I2C_CON ) = AM335X_I2C_CFG_10BIT_SLAVE_ADDR;
127  }
128}
129
130static void am335x_i2c_next_byte( bbb_i2c_bus *bus )
131{
132  i2c_msg *msg;
133
134  ++bus->msgs;
135  --bus->msg_todo;
136  msg = &bus->msgs[ 0 ];
137  bus->current_msg_todo = msg->len;
138  bus->current_msg_byte = msg->buf;
139}
140
141static void am335x_i2c_masterint_enable(
142  volatile bbb_i2c_regs *regs,
143  unsigned int           flag
144)
145{
146  REG( &regs->BBB_I2C_IRQENABLE_SET ) |= flag;
147}
148
149static void am335x_i2c_masterint_disable(
150  volatile bbb_i2c_regs *regs,
151  unsigned int           flag
152)
153{
154  REG( &regs->BBB_I2C_IRQENABLE_CLR ) = flag;
155}
156
157static void am335x_int_clear(
158  volatile bbb_i2c_regs *regs,
159  unsigned int           flag
160)
161{
162  REG( &regs->BBB_I2C_IRQSTATUS ) = flag;
163}
164
165static void am335x_clean_interrupts( volatile bbb_i2c_regs *regs )
166{
167  am335x_i2c_masterint_enable( regs, BBB_I2C_ALL_FLAGS );
168  am335x_int_clear( regs, BBB_I2C_ALL_FLAGS );
169  am335x_i2c_masterint_disable( regs, BBB_I2C_ALL_FLAGS );
170}
171
172static void am335x_i2c_setup_read_transfer(
173  bbb_i2c_bus           *bus,
174  volatile bbb_i2c_regs *regs,
175  const i2c_msg         *msgs,
176  bool                   send_stop
177)
178{
179  REG( &regs->BBB_I2C_CNT ) = bus->current_msg_todo;
180
181  REG( &regs->BBB_I2C_CON ) = AM335X_I2C_CFG_MST_RX | AM335X_I2C_CON_I2C_EN;
182
183  if ( send_stop ) {
184    REG( &regs->BBB_I2C_CON ) |= AM335X_I2C_CON_START | AM335X_I2C_CON_STOP;
185  } else {
186    REG( &regs->BBB_I2C_CON ) |= AM335X_I2C_CON_START;
187  }
188
189  am335x_i2c_masterint_enable( regs, AM335X_I2C_INT_RECV_READY |
190    AM335X_I2C_IRQSTATUS_ARDY );
191}
192
193static void am335x_i2c_continue_read_transfer(
194  bbb_i2c_bus           *bus,
195  volatile bbb_i2c_regs *regs
196)
197{
198  bus->current_msg_byte[ bus->already_transferred ] =
199    REG( &regs->BBB_I2C_DATA );
200
201  bus->already_transferred++;
202
203  REG( &regs->BBB_I2C_IRQSTATUS ) = AM335X_I2C_INT_RECV_READY;
204
205  if ( bus->already_transferred == bus->current_msg_todo - 1 ) {
206    REG( &regs->BBB_I2C_CON ) |= AM335X_I2C_CON_STOP;
207  }
208}
209
210static void am335x_i2c_continue_write(
211  bbb_i2c_bus           *bus,
212  volatile bbb_i2c_regs *regs
213)
214{
215  if ( bus->already_transferred == bus->msg_todo ) {
216    REG( &regs->BBB_I2C_DATA ) =
217      bus->current_msg_byte[ bus->already_transferred ];
218    REG( &regs->BBB_I2C_IRQSTATUS ) = AM335X_I2C_IRQSTATUS_XRDY;
219    am335x_i2c_masterint_disable( regs, AM335X_I2C_IRQSTATUS_XRDY );
220    REG( &regs->BBB_I2C_CON ) |= AM335X_I2C_CON_STOP;
221  } else {
222    writeb( bus->current_msg_byte[ bus->already_transferred ],
223      &regs->BBB_I2C_DATA );
224    REG( &regs->BBB_I2C_IRQSTATUS ) = AM335X_I2C_IRQSTATUS_XRDY;
225    bus->already_transferred++;
226  }
227}
228
229static void am335x_i2c_setup_write_transfer(
230  bbb_i2c_bus           *bus,
231  volatile bbb_i2c_regs *regs,
232  const i2c_msg         *msgs
233)
234{
235  volatile unsigned int no_bytes;
236
237  REG( &regs->BBB_I2C_CNT ) = bus->current_msg_todo;
238  no_bytes = REG( &regs->BBB_I2C_CNT );
239  REG( &regs->BBB_I2C_SA ) = msgs->addr;
240  REG( &regs->BBB_I2C_CON ) = AM335X_I2C_CFG_MST_TX | AM335X_I2C_CON_I2C_EN;
241  am335x_clean_interrupts( regs );
242  am335x_i2c_masterint_enable( regs, AM335X_I2C_IRQSTATUS_XRDY );
243  REG( &regs->BBB_I2C_CON ) |= AM335X_I2C_CON_START | AM335X_I2C_CON_STOP;
244}
245
246static void am335x_i2c_setup_transfer(
247  bbb_i2c_bus           *bus,
248  volatile bbb_i2c_regs *regs
249)
250{
251  const i2c_msg *msgs = bus->msgs;
252  uint32_t       msg_todo = bus->msg_todo;
253  bool           send_stop = false;
254  uint32_t       i;
255
256  bus->current_todo = msgs[ 0 ].len;
257
258  for ( i = 1; i < msg_todo && ( msgs[ i ].flags & I2C_M_NOSTART ) != 0;
259        ++i ) {
260    bus->current_todo += msgs[ i ].len;
261  }
262
263  regs = bus->regs;
264  REG( &bus->regs->BBB_I2C_BUF ) |= AM335X_I2C_BUF_TXFIFO_CLR;
265  REG( &bus->regs->BBB_I2C_BUF ) |= AM335X_I2C_BUF_RXFIFO_CLR;
266  am335x_i2c_set_address_size( msgs, regs );
267  bus->read = ( msgs->flags & I2C_M_RD ) != 0;
268  bus->already_transferred = ( bus->read == true ) ? 0 : 1;
269
270  if ( bus->read ) {
271    if ( bus->current_msg_todo == 1 ) {
272      send_stop = true;
273    }
274
275    am335x_i2c_setup_read_transfer( bus, regs, msgs, send_stop );
276  } else {
277    am335x_i2c_setup_write_transfer( bus, regs, msgs );
278  }
279}
280
281static void am335x_i2c_interrupt( void *arg )
282{
283  bbb_i2c_bus           *bus = arg;
284  volatile bbb_i2c_regs *regs = bus->regs;
285  /* get status of enabled interrupts */
286  uint32_t irqstatus = REG( &regs->BBB_I2C_IRQSTATUS );
287  bool     done = false;
288
289  /* Clear all enabled interrupt except receive ready
290     and transmit ready interrupt in status register */
291  REG( &regs->BBB_I2C_IRQSTATUS ) =
292    ( irqstatus & ~( AM335X_I2C_IRQSTATUS_RRDY |
293                     AM335X_I2C_IRQSTATUS_XRDY ) );
294
295  if ( irqstatus & AM335X_I2C_INT_RECV_READY ) {
296    am335x_i2c_continue_read_transfer( bus, regs );
297  }
298
299  if ( irqstatus & AM335X_I2C_IRQSTATUS_XRDY ) {
300    am335x_i2c_continue_write( bus, regs );
301  }
302
303  if ( irqstatus & AM335X_I2C_IRQSTATUS_NACK ) {
304    done = true;
305    am335x_i2c_masterint_disable( regs, AM335X_I2C_IRQSTATUS_NACK );
306  }
307
308  if ( irqstatus & AM335X_I2C_IRQSTATUS_ARDY ) {
309    done = true;
310    REG( &regs->BBB_I2C_IRQSTATUS ) = BBB_I2C_STAT_ARDY;
311  }
312
313  if ( irqstatus & AM335X_I2C_IRQSTATUS_BF ) {
314    REG( &regs->BBB_I2C_IRQSTATUS ) = AM335X_I2C_IRQSTATUS_BF;
315  }
316
317  if ( done ) {
318    uint32_t err = irqstatus & BBB_I2C_IRQ_ERROR;
319    am335x_i2c_next_byte( bus );
320
321    if ( bus->msg_todo == 0 ) {
322      rtems_status_code sc;
323      am335x_i2c_masterint_disable( regs, ( AM335X_I2C_IRQSTATUS_RRDY |
324                                            AM335X_I2C_IRQSTATUS_XRDY |
325                                            AM335X_I2C_IRQSTATUS_BF ) );
326      REG( &regs->BBB_I2C_IRQSTATUS ) = err;
327
328      sc = rtems_event_transient_send( bus->task_id );
329      _Assert( sc == RTEMS_SUCCESSFUL );
330      (void) sc;
331    } else {
332      am335x_i2c_setup_transfer( bus, regs );
333    }
334  }
335}
336
337static int am335x_i2c_transfer(
338  i2c_bus *base,
339  i2c_msg *msgs,
340  uint32_t msg_count
341)
342{
343  rtems_status_code      sc;
344  bbb_i2c_bus           *bus = (bbb_i2c_bus *) base;
345  volatile bbb_i2c_regs *regs;
346  uint32_t               i;
347
348  rtems_task_wake_after( 1 );
349
350  if ( msg_count < 1 ) {
351    return 1;
352  }
353
354  for ( i = 0; i < msg_count; ++i ) {
355    if ( ( msgs[ i ].flags & I2C_M_RECV_LEN ) != 0 ) {
356      return -EINVAL;
357    }
358  }
359
360  bus->msgs = &msgs[ 0 ];
361  bus->msg_todo = msg_count;
362  bus->current_msg_todo = msgs[ 0 ].len;
363  bus->current_msg_byte = msgs[ 0 ].buf;
364  bus->task_id = rtems_task_self();
365  regs = bus->regs;
366  am335x_i2c_setup_transfer( bus, regs );
367  REG( &regs->BBB_I2C_IRQENABLE_SET ) = BBB_I2C_IRQ_USED;
368
369  sc = rtems_event_transient_receive( RTEMS_WAIT, bus->base.timeout );
370
371  if ( sc != RTEMS_SUCCESSFUL ) {
372    am335x_i2c_reset( bus );
373    rtems_event_transient_clear();
374
375    return -ETIMEDOUT;
376  }
377
378  return 0;
379}
380
381static int am335x_i2c_set_clock(
382  i2c_bus      *base,
383  unsigned long clock
384)
385{
386  bbb_i2c_bus           *bus = (bbb_i2c_bus *) base;
387  uint32_t               prescaler, divider;
388
389  prescaler = ( BBB_I2C_SYSCLK / BBB_I2C_INTERNAL_CLK ) - 1;
390  REG( &bus->regs->BBB_I2C_PSC ) = prescaler;
391  divider = BBB_I2C_INTERNAL_CLK / ( 2 * clock );
392  REG( &bus->regs->BBB_I2C_SCLL ) = ( divider - 7 );
393  REG( &bus->regs->BBB_I2C_SCLH ) = ( divider - 5 );
394
395  return 0;
396}
397
398static void am335x_i2c_destroy( i2c_bus *base )
399{
400  bbb_i2c_bus      *bus = (bbb_i2c_bus *) base;
401  rtems_status_code sc;
402
403  sc = rtems_interrupt_handler_remove( bus->irq, am335x_i2c_interrupt, bus );
404  _Assert( sc == RTEMS_SUCCESSFUL );
405  (void) sc;
406  i2c_bus_destroy_and_free( &bus->base );
407}
408
409int am335x_i2c_bus_register(
410  const char         *bus_path,
411  uintptr_t           register_base,
412  uint32_t            input_clock,
413  rtems_vector_number irq
414)
415{
416  bbb_i2c_bus      *bus;
417  rtems_status_code sc;
418  int               err;
419
420  /*check bus number is >0 & <MAX*/
421  bus = (bbb_i2c_bus *) i2c_bus_alloc_and_init( sizeof( *bus ) );
422
423  if ( bus == NULL ) {
424    return -1;
425  }
426
427  bus->regs = (volatile bbb_i2c_regs *) register_base;
428
429  I2C0ModuleClkConfig();
430  am335x_i2c0_pinmux( bus );
431  am335x_i2c_reset( bus );
432  bus->input_clock = input_clock;
433  err = am335x_i2c_set_clock( &bus->base, I2C_BUS_CLOCK_DEFAULT );
434
435  if ( err != 0 ) {
436    ( *bus->base.destroy )( &bus->base );
437    rtems_set_errno_and_return_minus_one( -err );
438  }
439
440  bus->irq = irq;
441  REG( &bus->regs->BBB_I2C_IRQSTATUS ) = BBB_I2C_ALL_IRQ_FLAGS;
442
443  sc = rtems_interrupt_handler_install(
444    irq,
445    "BBB_I2C",
446    RTEMS_INTERRUPT_UNIQUE,
447    (rtems_interrupt_handler) am335x_i2c_interrupt,
448    bus
449       );
450
451  if ( sc != RTEMS_SUCCESSFUL ) {
452    ( *bus->base.destroy )( &bus->base );
453    rtems_set_errno_and_return_minus_one( EIO );
454  }
455
456  bus->base.transfer = am335x_i2c_transfer;
457  bus->base.set_clock = am335x_i2c_set_clock;
458  bus->base.destroy = am335x_i2c_destroy;
459
460  return i2c_bus_register( &bus->base, bus_path );
461}
Note: See TracBrowser for help on using the repository browser.