source: rtems/bsps/arm/beagle/i2c/bbb-i2c.c @ a2dad96

Last change on this file since a2dad96 was a2dad96, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 23, 2018 at 7:45:28 AM

bsps: Move I2C drivers to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

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