Landfall

Annotated pkg/devices/lib/i2c/src/jz4780.cc

211:678a57edbba5
12 months ago Paul Boddie Broadened the revised CPM support to encompass the JZ4780. cpm-library-improvements
paul@0 1
/*
paul@114 2
 * I2C support for the JZ4780.
paul@114 3
 *
paul@128 4
 * Copyright (C) 2017, 2018, 2021 Paul Boddie <paul@boddie.org.uk>
paul@0 5
 *
paul@0 6
 * This program is free software; you can redistribute it and/or
paul@0 7
 * modify it under the terms of the GNU General Public License as
paul@0 8
 * published by the Free Software Foundation; either version 2 of
paul@0 9
 * the License, or (at your option) any later version.
paul@0 10
 *
paul@0 11
 * This program is distributed in the hope that it will be useful,
paul@0 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@0 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@0 14
 * GNU General Public License for more details.
paul@0 15
 *
paul@0 16
 * You should have received a copy of the GNU General Public License
paul@0 17
 * along with this program; if not, write to the Free Software
paul@0 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@0 19
 * Boston, MA  02110-1301, USA
paul@0 20
 */
paul@0 21
paul@0 22
#include <l4/devices/i2c-jz4780.h>
paul@0 23
#include <l4/devices/hw_mmio_register_block.h>
paul@0 24
paul@0 25
#include <l4/sys/icu.h>
paul@0 26
#include <l4/util/util.h>
paul@0 27
#include <sys/time.h>
paul@0 28
paul@0 29
#include <cstdio>
paul@0 30
paul@0 31
/*
paul@0 32
I2C pins:
paul@0 33
paul@0 34
I2C0: PD30/SMB0_SDA,        PD31/SMB0_SCK
paul@0 35
I2C1: PD30/SMB1_SDA,        PD31/SMB1_SCK (pulled up on CI20)
paul@0 36
I2C2: PF16/SMB2_SDA,        PF17/SMB2_SCK
paul@0 37
 PWM: PD10/SMB3_SDA,        PD11/SMB3_SCK
paul@0 38
 PWM:  PE3/SMB4_SDA,         PE4/SMB4_SCK
paul@0 39
I2C4: PE12/SMB4_SDA,        PE13/SMB4_SCK
paul@0 40
HDMI: PF25/SMB4_SDA/DDCSDA, PF24/SMB4_SCK/DDCSCK
paul@0 41
paul@0 42
See: http://mipscreator.imgtec.com/CI20/hardware/board/ci20_jz4780_v2.0.pdf
paul@0 43
*/
paul@0 44
paul@0 45
enum Regs
paul@53 46
{
paul@0 47
  Smb_control         = 0x000,  // SMBCON
paul@0 48
  Smb_target_address  = 0x004,  // SMBTAR
paul@0 49
  Smb_slave_address   = 0x008,  // SMBSAR
paul@0 50
  Smb_data_command    = 0x010,  // SMBDC
paul@0 51
  Std_high_count      = 0x014,  // SMBSHCNT
paul@0 52
  Std_low_count       = 0x018,  // SMBSLCNT
paul@0 53
  Fast_high_count     = 0x01c,  // SMBFHCNT
paul@0 54
  Fast_low_count      = 0x020,  // SMBFLCNT
paul@0 55
  Int_status          = 0x02c,  // SMBINTST (read-only)
paul@0 56
  Int_mask            = 0x030,  // SMBINTM
paul@0 57
paul@0 58
  Rx_fifo_thold       = 0x038,  // SMBRXTL
paul@0 59
  Tx_fifo_thold       = 0x03c,  // SMBTXTL
paul@0 60
  Int_combined_clear  = 0x040,  // SMBCINT (read-only)
paul@0 61
  Int_rx_uf_clear     = 0x044,  // SMBCRXUF (read-only)
paul@0 62
  Int_rx_of_clear     = 0x048,  // SMBCRXOF (read-only)
paul@0 63
  Int_tx_of_clear     = 0x04c,  // SMBCTXOF (read-only)
paul@0 64
  Int_rd_req_clear    = 0x050,  // SMBCRXREQ (read-only)
paul@0 65
  Int_tx_abort_clear  = 0x054,  // SMBCTXABT (read-only)
paul@0 66
  Int_rx_done_clear   = 0x058,  // SMBCRXDN (read-only)
paul@0 67
  Int_activity_clear  = 0x05c,  // SMBCACT (read-only)
paul@0 68
  Int_stop_clear      = 0x060,  // SMBCSTP (read-only)
paul@0 69
  Int_start_clear     = 0x064,  // SMBCSTT (read-only)
paul@0 70
  Int_call_clear      = 0x068,  // SMBCGC (read-only)
paul@0 71
  Smb_enable          = 0x06c,  // SMBENB
paul@0 72
  Smb_status          = 0x070,  // SMBST (read-only)
paul@0 73
paul@0 74
  Tx_fifo_count       = 0x074,  // SMBTXFLR (read-only)
paul@0 75
  Rx_fifo_count       = 0x078,  // SMBRXFLR (read-only)
paul@0 76
paul@0 77
  Trans_abort_status0 = 0x080,  // SMBABTSRC (read-only)
paul@0 78
  Trans_abort_status1 = 0x084,  // ... (read-only)
paul@0 79
paul@0 80
  Smb_dma_ctrl        = 0x088,  // SMBDMACR
paul@0 81
  Smb_trans_data_lvl  = 0x08c,  // SMBDMATDLR
paul@0 82
  Smb_recv_data_lvl   = 0x090,  // SMBDMARDLR
paul@0 83
  Smb_sda_setup_time  = 0x094,  // SMBSDASU
paul@0 84
  Smb_ack_call        = 0x098,  // SMBACKGC
paul@0 85
paul@0 86
  Smb_enable_status   = 0x09c,  // SMBENBST (read-only)
paul@0 87
  Smb_sda_hold_time   = 0x0d0,  // SMBSDAHD
paul@0 88
paul@0 89
  Smb_block_offset    = 0x1000
paul@0 90
};
paul@0 91
paul@0 92
enum Smb_control_bits : unsigned
paul@0 93
{
paul@0 94
  Smb_no_stop_empty   = 0x80,   // STPHLD (no STP condition when queue empty)
paul@0 95
  Smb_disable_slave   = 0x40,   // SLVDIS (slave disabled)
paul@0 96
  Smb_enable_restart  = 0x20,   // REST
paul@0 97
  Smb_enable_master   = 0x01,   // MD (master enabled)
paul@0 98
  Smb_speed_bit       = 1,      // SPD
paul@0 99
};
paul@0 100
paul@0 101
enum Smb_enable_bits : unsigned
paul@0 102
{
paul@0 103
  Smb_enable_enabled  = 0x01,   // SMBEN
paul@0 104
};
paul@0 105
paul@0 106
enum Smb_status_bits : unsigned
paul@0 107
{
paul@0 108
  Smb_status_master_act = 0x20,   // MSTACT (master active)
paul@0 109
  Smb_status_rx_nempty  = 0x08,   // RFNE (read queue not empty)
paul@0 110
  Smb_status_tx_empty   = 0x04,   // TFE (write queue empty)
paul@0 111
  Smb_status_tx_nfull   = 0x02,   // TFNF (write queue not full)
paul@0 112
  Smb_status_active     = 0x01,   // ACT (device active as master or slave)
paul@0 113
};
paul@0 114
paul@0 115
enum Smb_target_bits : unsigned
paul@0 116
{
paul@0 117
  Smb_target_7bits    = 0x7f,
paul@0 118
};
paul@0 119
paul@0 120
enum Smb_hold_control_bits : unsigned
paul@0 121
{
paul@0 122
  Smb_hold_enable     = 0x100,  // HDENB
paul@0 123
  Smb_hold_disable    = 0x000,  // HDENB
paul@0 124
  Smb_hold_mask       = 0x1ff,
paul@0 125
};
paul@0 126
paul@0 127
enum Smb_setup_control_bits : unsigned
paul@0 128
{
paul@0 129
  Smb_setup_mask      = 0x0ff,
paul@0 130
};
paul@0 131
paul@0 132
enum Smb_command_bits : unsigned
paul@0 133
{
paul@0 134
  Smb_command_read    = 0x100,  // CMD
paul@0 135
  Smb_command_write   = 0x000,  // CMD
paul@0 136
};
paul@0 137
paul@0 138
enum Smb_fifo_bits : unsigned
paul@0 139
{
paul@0 140
  Smb_fifo_limit      = 16,
paul@0 141
};
paul@0 142
paul@0 143
enum Int_bits : unsigned
paul@0 144
{
paul@0 145
  Int_call       = 0x800,       // IGC (general call received)
paul@0 146
  Int_start      = 0x400,       // ISTT (start/restart condition occurred)
paul@0 147
  Int_stop       = 0x200,       // ISTP (stop condition occurred)
paul@0 148
  Int_activity   = 0x100,       // IACT (bus activity interrupt)
paul@0 149
  Int_rx_done    = 0x080,       // RXDN (read from master device done)
paul@0 150
  Int_tx_abort   = 0x040,       // TXABT (transmit abort)
paul@0 151
  Int_rd_req     = 0x020,       // RDREQ (read request from master device)
paul@0 152
  Int_tx_empty   = 0x010,       // TXEMP (threshold reached or passed)
paul@0 153
  Int_tx_of      = 0x008,       // TXOF (overflow when writing to queue)
paul@0 154
  Int_rx_full    = 0x004,       // RXFL (threshold reached or exceeded)
paul@0 155
  Int_rx_of      = 0x002,       // RXOF (overflow from device)
paul@0 156
  Int_rx_uf      = 0x001,       // RXUF (underflow when reading from queue)
paul@0 157
};
paul@0 158
paul@0 159
paul@0 160
paul@0 161
// Initialise a channel.
paul@0 162
paul@0 163
I2c_jz4780_channel::I2c_jz4780_channel(l4_addr_t start,
paul@0 164
                                       Cpm_jz4780_chip *cpm,
paul@53 165
                                       uint32_t frequency)
paul@0 166
: _cpm(cpm), _frequency(frequency)
paul@0 167
{
paul@0 168
  _regs = new Hw::Mmio_register_block<32>(start);
paul@0 169
}
paul@0 170
paul@0 171
// Enable the channel.
paul@0 172
paul@0 173
void
paul@0 174
I2c_jz4780_channel::enable()
paul@0 175
{
paul@0 176
  _regs[Smb_enable] = Smb_enable_enabled;
paul@0 177
  while (!(_regs[Smb_enable_status] & Smb_enable_enabled));
paul@0 178
}
paul@0 179
paul@0 180
// Disable the channel.
paul@0 181
paul@0 182
void
paul@0 183
I2c_jz4780_channel::disable()
paul@0 184
{
paul@0 185
  _regs[Smb_enable] = 0;
paul@0 186
  while (_regs[Smb_enable_status] & Smb_enable_enabled);
paul@0 187
}
paul@0 188
paul@0 189
// Set the frequency-related peripheral parameters.
paul@0 190
paul@0 191
void
paul@0 192
I2c_jz4780_channel::set_frequency()
paul@0 193
{
paul@0 194
  // The APB clock (PCLK) is used to drive I2C transfers. Its value must be
paul@0 195
  // obtained from the CPM unit. It is known as SMB_CLK here and is scaled to
paul@0 196
  // kHz in order to keep the numbers easily representable, as is the bus
paul@0 197
  // frequency.
paul@0 198
paul@211 199
  uint32_t smb_clk = _cpm->get_frequency(Clock_pclock) / 1000;
paul@0 200
  uint32_t i2c_clk = _frequency / 1000;
paul@53 201
  unsigned int speed = (i2c_clk <= 100) ? 1 : 2;
paul@0 202
paul@0 203
  _regs[Smb_control] = _regs[Smb_control] | (speed << Smb_speed_bit) |
paul@0 204
                                            Smb_disable_slave  |
paul@0 205
                                            Smb_enable_restart |
paul@0 206
                                            Smb_enable_master;
paul@0 207
paul@0 208
  // According to the programming manual, if the PCLK period is T{SMB_CLK}
paul@0 209
  // then the I2C clock period is...
paul@0 210
paul@0 211
  // T{SCL} = T{SCL_high} + T{SCL_low}
paul@0 212
paul@0 213
  // Where...
paul@0 214
paul@0 215
  // T{SCL_low} = T{SMB_CLK} * (#cycles for low signal)
paul@0 216
  // T{SCL_high} = T{SMB_CLK} * (#cycles for high signal)
paul@0 217
paul@0 218
  // Since, with minimum periods being defined...
paul@0 219
paul@0 220
  // T{SCL} >= T{min_SCL}
paul@0 221
  // T{SCL_low} >= T{min_SCL_low}
paul@0 222
  // T{SCL_high} >= T{min_SCL_high}
paul@0 223
  // T{min_SCL} = T{min_SCL_low} + T{min_SCL_high}
paul@0 224
paul@0 225
  // Then the following applies...
paul@0 226
paul@0 227
  // T{SMB_CLK} * (#cycles for low signal)) >= T{min_SCL_low}
paul@0 228
  // T{SMB_CLK} * (#cycles for high signal) >= T{min_SCL_high}
paul@0 229
paul@0 230
  // To work with different clock speeds while maintaining the low-to-high
paul@0 231
  // ratios:
paul@0 232
paul@0 233
  // T{min_SCL_low} = T{min_SCL} * T{min_SCL_low} / T{min_SCL}
paul@0 234
  //                = T{min_SCL} * (T{min_SCL_low} / (T{min_SCL_low} + T{min_SCL_high}))
paul@0 235
paul@0 236
  // T{min_SCL_high} = T{min_SCL} * T{min_SCL_high} / T{min_SCL}
paul@0 237
  //                 = T{min_SCL} * (T{min_SCL_high} / (T{min_SCL_low} + T{min_SCL_high}))
paul@0 238
paul@0 239
  // Constraints are given with respect to the high and low count registers.
paul@0 240
paul@0 241
  // #cycles for high signal = SMBxHCNT + 8
paul@0 242
  // #cycles for low signal = SMBxLCNT + 1
paul@0 243
paul@0 244
  // From earlier, this yields...
paul@0 245
paul@0 246
  // T{SMB_CLK} * (SMBxLCNT + 1) >= T{min_SCL_low}
paul@0 247
  // T{SMB_CLK} * (SMBxHCNT + 8) >= T{min_SCL_high}
paul@0 248
paul@0 249
  // Rearranging...
paul@0 250
paul@0 251
  // SMBxLCNT >= (T{min_SCL_low} / T{SMB_CLK}) - 1
paul@0 252
  //          >= T{min_SCL_low} * SMB_CLK - 1
paul@0 253
paul@0 254
  // SMBxHCNT >= (T{min_SCL_high} / T{SMB_CLK}) - 8
paul@0 255
  //          >= T{min_SCL_high} * SMB_CLK - 8
paul@0 256
paul@0 257
  // Introducing the definitions for the high and low periods...
paul@0 258
paul@0 259
  // SMBxLCNT >= T{min_SCL} * (T{min_SCL_low} / (T{min_SCL_low} + T{min_SCL_high})) * SMB_CLK - 1
paul@0 260
  //          >= (T{min_SCL_low} / T{min_SCL}) * SMB_CLK / I2C_CLK - 1
paul@0 261
paul@0 262
  // SMBxHCNT >= T{min_SCL} * (T{min_SCL_high} / (T{min_SCL_low} + T{min_SCL_high})) * SMB_CLK - 8
paul@0 263
  //          >= (T{min_SCL_high} / T{min_SCL}) * SMB_CLK / I2C_CLK - 8
paul@0 264
paul@0 265
  uint32_t high_reg, low_reg;
paul@0 266
  uint32_t high_count, low_count;
paul@52 267
  int32_t hold_count;
paul@52 268
  uint32_t setup_count;
paul@0 269
paul@0 270
  // Level hold times:
paul@0 271
paul@0 272
  //              Standard  Fast
paul@0 273
  // SCL low      4.7us     1.3us
paul@0 274
  // SCL high     4.0us     0.6us +
paul@0 275
  // SCL period   8.7us     1.9us =
paul@0 276
paul@0 277
  if (i2c_clk <= 100) // 100 kHz
paul@0 278
  {
paul@0 279
    low_count = (smb_clk * 47) / (i2c_clk * 87) - 1;
paul@0 280
    high_count = (smb_clk * 40) / (i2c_clk * 87) - 8;
paul@0 281
    low_reg = Std_low_count;
paul@0 282
    high_reg = Std_high_count;
paul@0 283
  }
paul@0 284
  else
paul@0 285
  {
paul@0 286
    low_count = (smb_clk * 13) / (i2c_clk * 19) - 1;
paul@0 287
    high_count = (smb_clk * 6) / (i2c_clk * 19) - 8;
paul@0 288
    low_reg = Fast_low_count;
paul@0 289
    high_reg = Fast_high_count;
paul@0 290
  }
paul@0 291
paul@0 292
  // Minimum counts are 8 and 6 for low and high respectively.
paul@0 293
paul@0 294
  _regs[low_reg] = low_count < 8 ? 8 : low_count;
paul@0 295
  _regs[high_reg] = high_count < 6 ? 6 : high_count;
paul@0 296
paul@0 297
  // Data hold and setup times:
paul@0 298
paul@0 299
  //              Standard  Fast
paul@0 300
  // t{HD;DAT}    300ns     300ns
paul@0 301
  // t{SU;DAT}    250ns     100ns
paul@0 302
paul@0 303
  // T{delay} = (SMBSDAHD + 1) * T{SMB_CLK}
paul@0 304
  // SMBSDAHD = T{delay} / T{SMB_CLK} - 1
paul@0 305
  // SMBSDAHD = SMB_CLK * T{delay} - 1
paul@0 306
paul@0 307
  hold_count = (smb_clk * 300) / 1000000 - 1;
paul@0 308
paul@0 309
  _regs[Smb_sda_hold_time] = (_regs[Smb_sda_hold_time] & ~Smb_hold_mask) |
paul@52 310
                             (hold_count >= 0 ? Smb_hold_enable : Smb_hold_disable) |
paul@52 311
                             (hold_count < 0 ? 0 : hold_count < 255 ? hold_count : 255);
paul@0 312
paul@0 313
  // T{delay} = (SMBSDASU - 1) * T{SMB_CLK}
paul@0 314
  // SMBSDASU = T{delay} / T{SMB_CLK} + 1
paul@0 315
  // SMBSDASU = SMB_CLK * T{delay} + 1
paul@0 316
paul@0 317
  if (i2c_clk <= 100)
paul@0 318
    setup_count = (smb_clk * 250) / 1000000 + 1;
paul@0 319
  else
paul@0 320
    setup_count = (smb_clk * 100) / 1000000 + 1;
paul@0 321
paul@0 322
  _regs[Smb_sda_setup_time] = (_regs[Smb_sda_setup_time] & ~Smb_setup_mask) |
paul@0 323
                              (setup_count < 255 ? setup_count : 255);
paul@0 324
}
paul@0 325
paul@0 326
// Set the target address and enable transfer.
paul@0 327
// NOTE: Only supporting 7-bit addresses currently.
paul@0 328
paul@0 329
void
paul@0 330
I2c_jz4780_channel::set_target(uint8_t address)
paul@0 331
{
paul@0 332
  disable();
paul@0 333
  set_frequency();
paul@0 334
  _regs[Smb_target_address] = address & Smb_target_7bits;
paul@0 335
  enable();
paul@0 336
  init_parameters();
paul@0 337
}
paul@0 338
paul@0 339
paul@0 340
paul@0 341
// Reset interrupt flags upon certain conditions.
paul@0 342
paul@0 343
void
paul@0 344
I2c_jz4780_channel::reset_flags()
paul@0 345
{
paul@0 346
  volatile uint32_t r;
paul@0 347
paul@0 348
  _regs[Int_mask] = 0;
paul@0 349
paul@0 350
  // Read from the register to clear interrupts.
paul@0 351
paul@0 352
  r = _regs[Int_combined_clear];
paul@0 353
  (void) r;
paul@0 354
}
paul@0 355
paul@0 356
// Initialise interrupt flags and queue thresholds for reading and writing.
paul@0 357
paul@0 358
void
paul@0 359
I2c_jz4780_channel::init_parameters()
paul@0 360
{
paul@0 361
  // Handle read queue conditions for data, write queue conditions for commands.
paul@0 362
paul@0 363
  reset_flags();
paul@0 364
paul@0 365
  _regs[Int_mask] = Int_rx_full  | // read condition (reading needed)
paul@0 366
                    Int_rx_of    | // abort condition
paul@0 367
                    Int_tx_empty | // write condition (writing needed)
paul@0 368
                    Int_tx_abort;  // abort condition
paul@0 369
paul@0 370
  _regs[Tx_fifo_thold] = 0; // write when 0 in queue
paul@0 371
paul@0 372
  // Make sure that the stop condition does not occur automatically.
paul@0 373
paul@0 374
  _regs[Smb_control] = _regs[Smb_control] | Smb_no_stop_empty;
paul@0 375
}
paul@0 376
paul@0 377
paul@0 378
paul@0 379
// Return whether the device is active.
paul@0 380
paul@0 381
int
paul@0 382
I2c_jz4780_channel::active()
paul@0 383
{
paul@0 384
  return _regs[Smb_status] & Smb_status_master_act;
paul@0 385
}
paul@0 386
paul@0 387
// Return whether data is available to receive.
paul@0 388
paul@0 389
int
paul@0 390
I2c_jz4780_channel::have_input()
paul@0 391
{
paul@0 392
  return _regs[Smb_status] & Smb_status_rx_nempty;
paul@0 393
}
paul@0 394
paul@0 395
// Return whether data is queued for sending.
paul@0 396
paul@0 397
int
paul@0 398
I2c_jz4780_channel::have_output()
paul@0 399
{
paul@0 400
  return !(_regs[Smb_status] & Smb_status_tx_empty);
paul@0 401
}
paul@0 402
paul@0 403
// Return whether data can be queued for sending.
paul@0 404
paul@0 405
int
paul@0 406
I2c_jz4780_channel::can_send()
paul@0 407
{
paul@0 408
  return _regs[Smb_status] & Smb_status_tx_nfull;
paul@0 409
}
paul@0 410
paul@0 411
// Return whether a receive operation has failed.
paul@0 412
paul@0 413
int
paul@0 414
I2c_jz4780_channel::read_failed()
paul@0 415
{
paul@0 416
  return _regs[Int_status] & Int_rx_of;
paul@0 417
}
paul@0 418
paul@0 419
// Return whether a send operation has failed.
paul@0 420
paul@0 421
int
paul@0 422
I2c_jz4780_channel::write_failed()
paul@0 423
{
paul@0 424
  return _regs[Int_status] & Int_tx_abort;
paul@0 425
}
paul@0 426
paul@56 427
int
paul@56 428
I2c_jz4780_channel::read_done()
paul@56 429
{
paul@56 430
  return _pos == _total;
paul@56 431
}
paul@56 432
paul@56 433
int
paul@56 434
I2c_jz4780_channel::write_done()
paul@56 435
{
paul@56 436
  return _reqpos == _total;
paul@56 437
}
paul@56 438
paul@56 439
unsigned
paul@56 440
I2c_jz4780_channel::have_read()
paul@56 441
{
paul@56 442
  return _pos;
paul@56 443
}
paul@56 444
paul@56 445
unsigned
paul@56 446
I2c_jz4780_channel::have_written()
paul@56 447
{
paul@56 448
  return _reqpos;
paul@56 449
}
paul@56 450
paul@56 451
int
paul@56 452
I2c_jz4780_channel::failed()
paul@56 453
{
paul@56 454
  return _fail;
paul@56 455
}
paul@56 456
paul@0 457
paul@0 458
paul@0 459
// Send read commands for empty queue entries.
paul@0 460
paul@0 461
void
paul@0 462
I2c_jz4780_channel::queue_reads()
paul@0 463
{
paul@53 464
  unsigned int remaining = _total - _reqpos;
paul@53 465
  unsigned int queued = _reqpos - _pos;
paul@0 466
paul@53 467
  // Permit one more issued read request due to the behaviour of the peripheral
paul@53 468
  // to withhold a request unless the stop condition has been issued.
paul@53 469
paul@53 470
  unsigned int can_queue = Smb_fifo_limit - queued + 1;
paul@0 471
paul@0 472
  // Keep the number of reads in progress below the length of the read queue.
paul@0 473
paul@0 474
  if (!can_queue)
paul@0 475
    return;
paul@0 476
paul@57 477
  // At most, only queue as many reads as are remaining.
paul@57 478
paul@0 479
  if (remaining < can_queue)
paul@0 480
    can_queue = remaining;
paul@0 481
paul@0 482
  // Queue read requests for any remaining queue entries.
paul@0 483
paul@56 484
  while (can_queue && can_send())
paul@56 485
  {
paul@0 486
    _regs[Smb_data_command] = Smb_command_read;
paul@56 487
    _reqpos++;
paul@56 488
    can_queue--;
paul@56 489
  }
paul@0 490
paul@53 491
  // Issue the stop condition after the final read request.
paul@53 492
  // In practice, an extra read request works better since it does not risk a
paul@53 493
  // transmission abort condition and would permit following transactions.
paul@57 494
  // However, it does risk causing an address autoincrement with some devices.
paul@53 495
paul@0 496
  if (_total == _reqpos)
paul@53 497
    _regs[Smb_data_command] = Smb_command_read;
paul@53 498
    //stop();
paul@56 499
paul@56 500
  // Update the threshold to be notified of any reduced remaining amount.
paul@56 501
paul@56 502
  set_read_threshold();
paul@0 503
}
paul@0 504
paul@0 505
// Send write commands for empty queue entries.
paul@0 506
paul@0 507
void
paul@56 508
I2c_jz4780_channel::queue_writes()
paul@0 509
{
paul@56 510
  unsigned int remaining = _total - _reqpos;
paul@56 511
  unsigned int can_queue = Smb_fifo_limit;
paul@56 512
paul@56 513
  if (remaining < can_queue)
paul@56 514
    can_queue = remaining;
paul@56 515
paul@0 516
  // Queue write requests for any remaining queue entries.
paul@0 517
paul@56 518
  while (can_queue && can_send())
paul@0 519
  {
paul@56 520
    _regs[Smb_data_command] = Smb_command_write | _buf[_reqpos];
paul@56 521
    _reqpos++;
paul@56 522
    can_queue--;
paul@0 523
  }
paul@0 524
}
paul@0 525
paul@0 526
// Store read command results from the queue.
paul@0 527
paul@0 528
void
paul@0 529
I2c_jz4780_channel::store_reads()
paul@0 530
{
paul@0 531
  // Read any input and store it in the buffer.
paul@0 532
paul@56 533
  while (have_input() && (_pos < _reqpos))
paul@0 534
  {
paul@0 535
    _buf[_pos] = _regs[Smb_data_command] & 0xff;
paul@0 536
    _pos++;
paul@0 537
  }
paul@53 538
}
paul@0 539
paul@53 540
void
paul@53 541
I2c_jz4780_channel::set_read_threshold()
paul@53 542
{
paul@56 543
  unsigned int queued = _reqpos - _pos;
paul@53 544
paul@56 545
  if (!queued)
paul@53 546
    return;
paul@53 547
paul@56 548
  // Read all expected.
paul@53 549
paul@57 550
  _regs[Rx_fifo_thold] = queued - 1;
paul@0 551
}
paul@0 552
paul@0 553
// Read from the target device.
paul@0 554
paul@0 555
void
paul@53 556
I2c_jz4780_channel::start_read(uint8_t buf[], unsigned int total)
paul@0 557
{
paul@0 558
  _buf = buf;
paul@0 559
  _total = total;
paul@0 560
  _pos = 0;
paul@0 561
  _reqpos = 0;
paul@0 562
  _fail = 0;
paul@0 563
paul@0 564
  set_read_threshold();
paul@0 565
}
paul@0 566
paul@0 567
void
paul@0 568
I2c_jz4780_channel::read()
paul@0 569
{
paul@0 570
  if (read_failed() || write_failed())
paul@0 571
  {
paul@0 572
    _fail = 1;
paul@0 573
    return;
paul@0 574
  }
paul@0 575
paul@56 576
  if (_regs[Int_status] & Int_rx_full)
paul@56 577
    store_reads();
paul@56 578
  if (_regs[Int_status] & Int_tx_empty)
paul@56 579
    queue_reads();
paul@0 580
}
paul@0 581
paul@0 582
// Write to the target device.
paul@0 583
paul@0 584
void
paul@56 585
I2c_jz4780_channel::start_write(uint8_t buf[], unsigned int total)
paul@0 586
{
paul@56 587
  _buf = buf;
paul@56 588
  _total = total;
paul@56 589
  _reqpos = 0;
paul@56 590
  _fail = 0;
paul@56 591
}
paul@0 592
paul@56 593
void
paul@56 594
I2c_jz4780_channel::write()
paul@56 595
{
paul@56 596
  if (write_failed())
paul@0 597
  {
paul@56 598
    _fail = 1;
paul@56 599
    return;
paul@0 600
  }
paul@56 601
paul@56 602
  if (_regs[Int_status] & Int_tx_empty)
paul@56 603
    queue_writes();
paul@0 604
}
paul@0 605
paul@0 606
// Explicitly stop communication.
paul@0 607
paul@0 608
void
paul@0 609
I2c_jz4780_channel::stop()
paul@0 610
{
paul@0 611
  _regs[Smb_control] = _regs[Smb_control] & ~Smb_no_stop_empty;
paul@0 612
}
paul@0 613
paul@0 614
paul@0 615
paul@0 616
// Initialise the I2C controller.
paul@0 617
paul@0 618
I2c_jz4780_chip::I2c_jz4780_chip(l4_addr_t start, l4_addr_t end,
paul@0 619
                                 Cpm_jz4780_chip *cpm,
paul@0 620
                                 uint32_t frequency)
paul@0 621
: _start(start), _end(end), _cpm(cpm), _frequency(frequency)
paul@0 622
{
paul@0 623
}
paul@0 624
paul@0 625
// Obtain a channel object.
paul@0 626
paul@0 627
I2c_jz4780_channel *
paul@0 628
I2c_jz4780_chip::get_channel(uint8_t channel)
paul@0 629
{
paul@0 630
  l4_addr_t block = _start + channel * Smb_block_offset;
paul@211 631
  enum Clock_identifiers bits[] = {Clock_i2c0, Clock_i2c1, Clock_i2c2, Clock_i2c3, Clock_i2c4};
paul@0 632
paul@128 633
  if (channel < 5)
paul@0 634
  {
paul@128 635
    _cpm->start_clock(bits[channel]);
paul@0 636
    return new I2c_jz4780_channel(block, _cpm, _frequency);
paul@0 637
  }
paul@0 638
  else
paul@0 639
    throw -L4_EINVAL;
paul@0 640
}
paul@0 641
paul@0 642
paul@0 643
paul@0 644
// C language interface functions.
paul@0 645
paul@0 646
void *jz4780_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, uint32_t frequency)
paul@0 647
{
paul@0 648
  return (void *) new I2c_jz4780_chip(start, end, static_cast<Cpm_jz4780_chip *>(cpm), frequency);
paul@0 649
}
paul@0 650
paul@53 651
void jz4780_i2c_disable(void *i2c_channel)
paul@53 652
{
paul@53 653
  static_cast<I2c_jz4780_channel *>(i2c_channel)->disable();
paul@53 654
}
paul@53 655
paul@0 656
void *jz4780_i2c_get_channel(void *i2c, uint8_t channel)
paul@0 657
{
paul@0 658
  return static_cast<I2c_jz4780_chip *>(i2c)->get_channel(channel);
paul@0 659
}
paul@0 660
paul@0 661
void jz4780_i2c_set_target(void *i2c_channel, uint8_t addr)
paul@0 662
{
paul@0 663
  static_cast<I2c_jz4780_channel *>(i2c_channel)->set_target(addr);
paul@0 664
}
paul@0 665
paul@53 666
void jz4780_i2c_start_read(void *i2c_channel, uint8_t buf[], unsigned int total)
paul@0 667
{
paul@0 668
  static_cast<I2c_jz4780_channel *>(i2c_channel)->start_read(buf, total);
paul@0 669
}
paul@0 670
paul@0 671
void jz4780_i2c_read(void *i2c_channel)
paul@0 672
{
paul@0 673
  static_cast<I2c_jz4780_channel *>(i2c_channel)->read();
paul@0 674
}
paul@0 675
paul@56 676
void jz4780_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total)
paul@56 677
{
paul@56 678
  static_cast<I2c_jz4780_channel *>(i2c_channel)->start_write(buf, total);
paul@56 679
}
paul@56 680
paul@56 681
void jz4780_i2c_write(void *i2c_channel)
paul@56 682
{
paul@56 683
  static_cast<I2c_jz4780_channel *>(i2c_channel)->write();
paul@56 684
}
paul@56 685
paul@0 686
int jz4780_i2c_read_done(void *i2c_channel)
paul@0 687
{
paul@0 688
  return static_cast<I2c_jz4780_channel *>(i2c_channel)->read_done();
paul@0 689
}
paul@0 690
paul@56 691
int jz4780_i2c_write_done(void *i2c_channel)
paul@56 692
{
paul@56 693
  return static_cast<I2c_jz4780_channel *>(i2c_channel)->write_done();
paul@56 694
}
paul@56 695
paul@53 696
unsigned int jz4780_i2c_have_read(void *i2c_channel)
paul@0 697
{
paul@0 698
  return static_cast<I2c_jz4780_channel *>(i2c_channel)->have_read();
paul@0 699
}
paul@0 700
paul@56 701
unsigned int jz4780_i2c_have_written(void *i2c_channel)
paul@0 702
{
paul@56 703
  return static_cast<I2c_jz4780_channel *>(i2c_channel)->have_written();
paul@0 704
}
paul@0 705
paul@56 706
int jz4780_i2c_failed(void *i2c_channel)
paul@0 707
{
paul@56 708
  return static_cast<I2c_jz4780_channel *>(i2c_channel)->failed();
paul@0 709
}
paul@0 710
paul@0 711
void jz4780_i2c_stop(void *i2c_channel)
paul@0 712
{
paul@0 713
  static_cast<I2c_jz4780_channel *>(i2c_channel)->stop();
paul@0 714
}