Landfall

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

31:bab304c3775e
2018-05-30 Paul Boddie Fixed copyright notices to actually use the word "Copyright".
paul@0 1
/*
paul@31 2
 * Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk>
paul@0 3
 *
paul@0 4
 * This program is free software; you can redistribute it and/or
paul@0 5
 * modify it under the terms of the GNU General Public License as
paul@0 6
 * published by the Free Software Foundation; either version 2 of
paul@0 7
 * the License, or (at your option) any later version.
paul@0 8
 *
paul@0 9
 * This program is distributed in the hope that it will be useful,
paul@0 10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@0 11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@0 12
 * GNU General Public License for more details.
paul@0 13
 *
paul@0 14
 * You should have received a copy of the GNU General Public License
paul@0 15
 * along with this program; if not, write to the Free Software
paul@0 16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@0 17
 * Boston, MA  02110-1301, USA
paul@0 18
 */
paul@0 19
paul@0 20
#include <l4/devices/i2c-jz4780.h>
paul@0 21
#include <l4/devices/hw_mmio_register_block.h>
paul@0 22
paul@0 23
#include <l4/sys/icu.h>
paul@0 24
#include <l4/util/util.h>
paul@0 25
#include <sys/time.h>
paul@0 26
paul@0 27
#include <cstdio>
paul@0 28
paul@0 29
/*
paul@0 30
I2C pins:
paul@0 31
paul@0 32
I2C0: PD30/SMB0_SDA,        PD31/SMB0_SCK
paul@0 33
I2C1: PD30/SMB1_SDA,        PD31/SMB1_SCK (pulled up on CI20)
paul@0 34
I2C2: PF16/SMB2_SDA,        PF17/SMB2_SCK
paul@0 35
 PWM: PD10/SMB3_SDA,        PD11/SMB3_SCK
paul@0 36
 PWM:  PE3/SMB4_SDA,         PE4/SMB4_SCK
paul@0 37
I2C4: PE12/SMB4_SDA,        PE13/SMB4_SCK
paul@0 38
HDMI: PF25/SMB4_SDA/DDCSDA, PF24/SMB4_SCK/DDCSCK
paul@0 39
paul@0 40
See: http://mipscreator.imgtec.com/CI20/hardware/board/ci20_jz4780_v2.0.pdf
paul@0 41
*/
paul@0 42
paul@0 43
enum Regs
paul@0 44
{ 
paul@0 45
  Smb_control         = 0x000,  // SMBCON
paul@0 46
  Smb_target_address  = 0x004,  // SMBTAR
paul@0 47
  Smb_slave_address   = 0x008,  // SMBSAR
paul@0 48
  Smb_data_command    = 0x010,  // SMBDC
paul@0 49
  Std_high_count      = 0x014,  // SMBSHCNT
paul@0 50
  Std_low_count       = 0x018,  // SMBSLCNT
paul@0 51
  Fast_high_count     = 0x01c,  // SMBFHCNT
paul@0 52
  Fast_low_count      = 0x020,  // SMBFLCNT
paul@0 53
  Int_status          = 0x02c,  // SMBINTST (read-only)
paul@0 54
  Int_mask            = 0x030,  // SMBINTM
paul@0 55
paul@0 56
  Rx_fifo_thold       = 0x038,  // SMBRXTL
paul@0 57
  Tx_fifo_thold       = 0x03c,  // SMBTXTL
paul@0 58
  Int_combined_clear  = 0x040,  // SMBCINT (read-only)
paul@0 59
  Int_rx_uf_clear     = 0x044,  // SMBCRXUF (read-only)
paul@0 60
  Int_rx_of_clear     = 0x048,  // SMBCRXOF (read-only)
paul@0 61
  Int_tx_of_clear     = 0x04c,  // SMBCTXOF (read-only)
paul@0 62
  Int_rd_req_clear    = 0x050,  // SMBCRXREQ (read-only)
paul@0 63
  Int_tx_abort_clear  = 0x054,  // SMBCTXABT (read-only)
paul@0 64
  Int_rx_done_clear   = 0x058,  // SMBCRXDN (read-only)
paul@0 65
  Int_activity_clear  = 0x05c,  // SMBCACT (read-only)
paul@0 66
  Int_stop_clear      = 0x060,  // SMBCSTP (read-only)
paul@0 67
  Int_start_clear     = 0x064,  // SMBCSTT (read-only)
paul@0 68
  Int_call_clear      = 0x068,  // SMBCGC (read-only)
paul@0 69
  Smb_enable          = 0x06c,  // SMBENB
paul@0 70
  Smb_status          = 0x070,  // SMBST (read-only)
paul@0 71
paul@0 72
  Tx_fifo_count       = 0x074,  // SMBTXFLR (read-only)
paul@0 73
  Rx_fifo_count       = 0x078,  // SMBRXFLR (read-only)
paul@0 74
paul@0 75
  Trans_abort_status0 = 0x080,  // SMBABTSRC (read-only)
paul@0 76
  Trans_abort_status1 = 0x084,  // ... (read-only)
paul@0 77
paul@0 78
  Smb_dma_ctrl        = 0x088,  // SMBDMACR
paul@0 79
  Smb_trans_data_lvl  = 0x08c,  // SMBDMATDLR
paul@0 80
  Smb_recv_data_lvl   = 0x090,  // SMBDMARDLR
paul@0 81
  Smb_sda_setup_time  = 0x094,  // SMBSDASU
paul@0 82
  Smb_ack_call        = 0x098,  // SMBACKGC
paul@0 83
paul@0 84
  Smb_enable_status   = 0x09c,  // SMBENBST (read-only)
paul@0 85
  Smb_sda_hold_time   = 0x0d0,  // SMBSDAHD
paul@0 86
paul@0 87
  Smb_block_offset    = 0x1000
paul@0 88
};
paul@0 89
paul@0 90
enum Smb_control_bits : unsigned
paul@0 91
{
paul@0 92
  Smb_no_stop_empty   = 0x80,   // STPHLD (no STP condition when queue empty)
paul@0 93
  Smb_disable_slave   = 0x40,   // SLVDIS (slave disabled)
paul@0 94
  Smb_enable_restart  = 0x20,   // REST
paul@0 95
  Smb_enable_master   = 0x01,   // MD (master enabled)
paul@0 96
  Smb_speed_bit       = 1,      // SPD
paul@0 97
};
paul@0 98
paul@0 99
enum Smb_enable_bits : unsigned
paul@0 100
{
paul@0 101
  Smb_enable_enabled  = 0x01,   // SMBEN
paul@0 102
};
paul@0 103
paul@0 104
enum Smb_status_bits : unsigned
paul@0 105
{
paul@0 106
  Smb_status_master_act = 0x20,   // MSTACT (master active)
paul@0 107
  Smb_status_rx_nempty  = 0x08,   // RFNE (read queue not empty)
paul@0 108
  Smb_status_tx_empty   = 0x04,   // TFE (write queue empty)
paul@0 109
  Smb_status_tx_nfull   = 0x02,   // TFNF (write queue not full)
paul@0 110
  Smb_status_active     = 0x01,   // ACT (device active as master or slave)
paul@0 111
};
paul@0 112
paul@0 113
enum Smb_target_bits : unsigned
paul@0 114
{
paul@0 115
  Smb_target_7bits    = 0x7f,
paul@0 116
};
paul@0 117
paul@0 118
enum Smb_hold_control_bits : unsigned
paul@0 119
{
paul@0 120
  Smb_hold_enable     = 0x100,  // HDENB
paul@0 121
  Smb_hold_disable    = 0x000,  // HDENB
paul@0 122
  Smb_hold_mask       = 0x1ff,
paul@0 123
};
paul@0 124
paul@0 125
enum Smb_setup_control_bits : unsigned
paul@0 126
{
paul@0 127
  Smb_setup_mask      = 0x0ff,
paul@0 128
};
paul@0 129
paul@0 130
enum Smb_command_bits : unsigned
paul@0 131
{
paul@0 132
  Smb_command_read    = 0x100,  // CMD
paul@0 133
  Smb_command_write   = 0x000,  // CMD
paul@0 134
};
paul@0 135
paul@0 136
enum Smb_fifo_bits : unsigned
paul@0 137
{
paul@0 138
  Smb_fifo_limit      = 16,
paul@0 139
};
paul@0 140
paul@0 141
enum Int_bits : unsigned
paul@0 142
{
paul@0 143
  Int_call       = 0x800,       // IGC (general call received)
paul@0 144
  Int_start      = 0x400,       // ISTT (start/restart condition occurred)
paul@0 145
  Int_stop       = 0x200,       // ISTP (stop condition occurred)
paul@0 146
  Int_activity   = 0x100,       // IACT (bus activity interrupt)
paul@0 147
  Int_rx_done    = 0x080,       // RXDN (read from master device done)
paul@0 148
  Int_tx_abort   = 0x040,       // TXABT (transmit abort)
paul@0 149
  Int_rd_req     = 0x020,       // RDREQ (read request from master device)
paul@0 150
  Int_tx_empty   = 0x010,       // TXEMP (threshold reached or passed)
paul@0 151
  Int_tx_of      = 0x008,       // TXOF (overflow when writing to queue)
paul@0 152
  Int_rx_full    = 0x004,       // RXFL (threshold reached or exceeded)
paul@0 153
  Int_rx_of      = 0x002,       // RXOF (overflow from device)
paul@0 154
  Int_rx_uf      = 0x001,       // RXUF (underflow when reading from queue)
paul@0 155
};
paul@0 156
paul@0 157
paul@0 158
paul@0 159
// Initialise a channel.
paul@0 160
paul@0 161
I2c_jz4780_channel::I2c_jz4780_channel(l4_addr_t start,
paul@0 162
                                       Cpm_jz4780_chip *cpm,
paul@0 163
                                       uint32_t frequency) 
paul@0 164
: _cpm(cpm), _frequency(frequency)
paul@0 165
{
paul@0 166
  _regs = new Hw::Mmio_register_block<32>(start);
paul@0 167
}
paul@0 168
paul@0 169
// Enable the channel.
paul@0 170
paul@0 171
void
paul@0 172
I2c_jz4780_channel::enable()
paul@0 173
{
paul@0 174
  _regs[Smb_control] = _regs[Smb_control] | Smb_no_stop_empty;
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@0 199
  uint32_t smb_clk = _cpm->get_pclock_frequency() / 1000;
paul@0 200
  uint32_t i2c_clk = _frequency / 1000;
paul@0 201
  unsigned 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@0 267
  uint32_t hold_count, setup_count;
paul@0 268
paul@0 269
  // Level hold times:
paul@0 270
paul@0 271
  //              Standard  Fast
paul@0 272
  // SCL low      4.7us     1.3us
paul@0 273
  // SCL high     4.0us     0.6us +
paul@0 274
  // SCL period   8.7us     1.9us =
paul@0 275
paul@0 276
  if (i2c_clk <= 100) // 100 kHz
paul@0 277
  {
paul@0 278
    low_count = (smb_clk * 47) / (i2c_clk * 87) - 1;
paul@0 279
    high_count = (smb_clk * 40) / (i2c_clk * 87) - 8;
paul@0 280
    low_reg = Std_low_count;
paul@0 281
    high_reg = Std_high_count;
paul@0 282
  }
paul@0 283
  else
paul@0 284
  {
paul@0 285
    low_count = (smb_clk * 13) / (i2c_clk * 19) - 1;
paul@0 286
    high_count = (smb_clk * 6) / (i2c_clk * 19) - 8;
paul@0 287
    low_reg = Fast_low_count;
paul@0 288
    high_reg = Fast_high_count;
paul@0 289
  }
paul@0 290
paul@0 291
  // Minimum counts are 8 and 6 for low and high respectively.
paul@0 292
paul@0 293
  _regs[low_reg] = low_count < 8 ? 8 : low_count;
paul@0 294
  _regs[high_reg] = high_count < 6 ? 6 : high_count;
paul@0 295
paul@0 296
  // Data hold and setup times:
paul@0 297
paul@0 298
  //              Standard  Fast
paul@0 299
  // t{HD;DAT}    300ns     300ns
paul@0 300
  // t{SU;DAT}    250ns     100ns
paul@0 301
paul@0 302
  // T{delay} = (SMBSDAHD + 1) * T{SMB_CLK}
paul@0 303
  // SMBSDAHD = T{delay} / T{SMB_CLK} - 1
paul@0 304
  // SMBSDAHD = SMB_CLK * T{delay} - 1
paul@0 305
paul@0 306
  hold_count = (smb_clk * 300) / 1000000 - 1;
paul@0 307
paul@0 308
  _regs[Smb_sda_hold_time] = (_regs[Smb_sda_hold_time] & ~Smb_hold_mask) |
paul@0 309
                             (hold_count > 0 ? Smb_hold_enable : Smb_hold_disable) |
paul@0 310
                             (hold_count < 255 ? hold_count : 255);
paul@0 311
paul@0 312
  // T{delay} = (SMBSDASU - 1) * T{SMB_CLK}
paul@0 313
  // SMBSDASU = T{delay} / T{SMB_CLK} + 1
paul@0 314
  // SMBSDASU = SMB_CLK * T{delay} + 1
paul@0 315
paul@0 316
  if (i2c_clk <= 100)
paul@0 317
    setup_count = (smb_clk * 250) / 1000000 + 1;
paul@0 318
  else
paul@0 319
    setup_count = (smb_clk * 100) / 1000000 + 1;
paul@0 320
paul@0 321
  _regs[Smb_sda_setup_time] = (_regs[Smb_sda_setup_time] & ~Smb_setup_mask) |
paul@0 322
                              (setup_count < 255 ? setup_count : 255);
paul@0 323
}
paul@0 324
paul@0 325
// Set the target address and enable transfer.
paul@0 326
// NOTE: Only supporting 7-bit addresses currently.
paul@0 327
paul@0 328
void
paul@0 329
I2c_jz4780_channel::set_target(uint8_t address)
paul@0 330
{
paul@0 331
  disable();
paul@0 332
  set_frequency();
paul@0 333
  _regs[Smb_target_address] = address & Smb_target_7bits;
paul@0 334
  enable();
paul@0 335
  init_parameters();
paul@0 336
  queued = 0;
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@0 427
paul@0 428
paul@0 429
// Send read commands for empty queue entries.
paul@0 430
paul@0 431
void
paul@0 432
I2c_jz4780_channel::queue_reads()
paul@0 433
{
paul@0 434
  unsigned remaining = _total - _reqpos;
paul@0 435
  unsigned can_queue = Smb_fifo_limit - queued + 1;
paul@0 436
paul@0 437
  if (!remaining)
paul@0 438
  {
paul@0 439
    _regs[Smb_data_command] = Smb_command_read;
paul@0 440
    return;
paul@0 441
  }
paul@0 442
paul@0 443
  // Keep the number of reads in progress below the length of the read queue.
paul@0 444
paul@0 445
  if (!can_queue)
paul@0 446
  {
paul@0 447
    //printf("remaining=%d queued=%d\n", remaining, queued);
paul@0 448
    return;
paul@0 449
  }
paul@0 450
paul@0 451
  if (remaining < can_queue)
paul@0 452
    can_queue = remaining;
paul@0 453
paul@0 454
  // Queue read requests for any remaining queue entries.
paul@0 455
paul@0 456
  for (unsigned i = 0; i < can_queue; i++)
paul@0 457
    _regs[Smb_data_command] = Smb_command_read;
paul@0 458
paul@0 459
  // Track queued messages and requested positions.
paul@0 460
paul@0 461
  queued += can_queue;
paul@0 462
  _reqpos += can_queue;
paul@0 463
paul@0 464
  if (_total == _reqpos)
paul@0 465
    printf("Written %d read requests.\n", _reqpos);
paul@0 466
}
paul@0 467
paul@0 468
// Send write commands for empty queue entries.
paul@0 469
paul@0 470
void
paul@0 471
I2c_jz4780_channel::queue_writes(uint8_t buf[], unsigned *pos, unsigned total)
paul@0 472
{
paul@0 473
  // Queue write requests for any remaining queue entries.
paul@0 474
paul@0 475
  while ((*pos < total) && can_send() && !write_failed())
paul@0 476
  {
paul@0 477
    _regs[Smb_data_command] = Smb_command_write | buf[*pos];
paul@0 478
    (*pos)++;
paul@0 479
  }
paul@0 480
}
paul@0 481
paul@0 482
// Store read command results from the queue.
paul@0 483
paul@0 484
void
paul@0 485
I2c_jz4780_channel::store_reads()
paul@0 486
{
paul@0 487
  int have_read = 0;
paul@0 488
paul@0 489
  // Read any input and store it in the buffer.
paul@0 490
paul@0 491
  while (have_input() && (_pos < _total))
paul@0 492
  {
paul@0 493
    _buf[_pos] = _regs[Smb_data_command] & 0xff;
paul@0 494
    queued--;
paul@0 495
    _pos++;
paul@0 496
paul@0 497
    have_read = 1;
paul@0 498
  }
paul@0 499
paul@0 500
  // Update the threshold to be notified of any reduced remaining amount.
paul@0 501
paul@0 502
  if (have_read)
paul@0 503
    set_read_threshold();
paul@0 504
paul@0 505
  //printf("Read to %d, still %d queued.\n", _pos, queued);
paul@0 506
}
paul@0 507
paul@0 508
// Read from the target device.
paul@0 509
paul@0 510
void
paul@0 511
I2c_jz4780_channel::start_read(uint8_t buf[], unsigned total)
paul@0 512
{
paul@0 513
  printf("intst=%x\n", (uint32_t) _regs[Int_status]);
paul@0 514
paul@0 515
  _buf = buf;
paul@0 516
  _total = total;
paul@0 517
  _pos = 0;
paul@0 518
  _reqpos = 0;
paul@0 519
  _fail = 0;
paul@0 520
  _stop = 0;
paul@0 521
paul@0 522
  set_read_threshold();
paul@0 523
}
paul@0 524
paul@0 525
void
paul@0 526
I2c_jz4780_channel::set_read_threshold()
paul@0 527
{
paul@0 528
  unsigned remaining = _total - _pos;
paul@0 529
paul@0 530
  if (!remaining)
paul@0 531
    return;
paul@0 532
paul@0 533
  if (remaining <= Smb_fifo_limit)
paul@0 534
    _regs[Rx_fifo_thold] = remaining - 1; // read all remaining
paul@0 535
  else
paul@0 536
    _regs[Rx_fifo_thold] = Smb_fifo_limit - 1; // read to limit
paul@0 537
paul@0 538
  //printf("rxthold=%d\n", (uint32_t) _regs[Rx_fifo_thold] + 1);
paul@0 539
}
paul@0 540
paul@0 541
void
paul@0 542
I2c_jz4780_channel::read()
paul@0 543
{
paul@0 544
  if (read_failed() || write_failed())
paul@0 545
  {
paul@0 546
    printf("intst*=%x\n", (uint32_t) _regs[Int_status]);
paul@0 547
    printf("rxfifo=%d\n", (uint32_t) _regs[Rx_fifo_count] & 0x3f);
paul@0 548
    printf("smbabtsrc=%x\n", (uint32_t) _regs[Trans_abort_status0]);
paul@0 549
    _fail = 1;
paul@0 550
    return;
paul@0 551
  }
paul@0 552
paul@0 553
  //printf("rxfifo=%d\n", (uint32_t) _regs[Rx_fifo_count]);
paul@0 554
  //printf("intst=%x\n", (uint32_t) _regs[Int_status]);
paul@0 555
paul@0 556
  if (_pos < _total)
paul@0 557
  {
paul@0 558
    if (_regs[Int_status] & Int_rx_full)
paul@0 559
      store_reads();
paul@0 560
    if (_regs[Int_status] & Int_tx_empty)
paul@0 561
      queue_reads();
paul@0 562
  }
paul@0 563
  else
paul@0 564
  {
paul@0 565
    stop();
paul@0 566
    _regs[Int_mask] = 0;
paul@0 567
  }
paul@0 568
}
paul@0 569
paul@0 570
int
paul@0 571
I2c_jz4780_channel::read_done()
paul@0 572
{
paul@0 573
  return _pos == _total;
paul@0 574
}
paul@0 575
paul@0 576
unsigned
paul@0 577
I2c_jz4780_channel::have_read()
paul@0 578
{
paul@0 579
  return _pos;
paul@0 580
}
paul@0 581
paul@0 582
int
paul@0 583
I2c_jz4780_channel::read_incomplete()
paul@0 584
{
paul@0 585
  return _fail;
paul@0 586
}
paul@0 587
paul@0 588
// Write to the target device.
paul@0 589
paul@0 590
void
paul@0 591
I2c_jz4780_channel::write(uint8_t buf[], unsigned total)
paul@0 592
{
paul@0 593
  unsigned pos = 0;
paul@0 594
paul@0 595
  while ((pos < total) && !write_failed())
paul@0 596
  {
paul@0 597
    queue_writes(buf, &pos, total);
paul@0 598
  }
paul@0 599
}
paul@0 600
paul@0 601
// Explicitly stop communication.
paul@0 602
paul@0 603
void
paul@0 604
I2c_jz4780_channel::stop()
paul@0 605
{
paul@0 606
  if (_stop)
paul@0 607
    return;
paul@0 608
paul@0 609
  _regs[Smb_control] = _regs[Smb_control] & ~Smb_no_stop_empty;
paul@0 610
  _stop = 1;
paul@0 611
  printf("Stop sent.\n");
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@0 631
paul@0 632
  if (block < _end)
paul@0 633
  {
paul@0 634
    _cpm->start_i2c(channel);
paul@0 635
    return new I2c_jz4780_channel(block, _cpm, _frequency);
paul@0 636
  }
paul@0 637
  else
paul@0 638
    throw -L4_EINVAL;
paul@0 639
}
paul@0 640
paul@0 641
paul@0 642
paul@0 643
// C language interface functions.
paul@0 644
paul@0 645
void *jz4780_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, uint32_t frequency)
paul@0 646
{
paul@0 647
  return (void *) new I2c_jz4780_chip(start, end, static_cast<Cpm_jz4780_chip *>(cpm), frequency);
paul@0 648
}
paul@0 649
paul@0 650
void *jz4780_i2c_get_channel(void *i2c, uint8_t channel)
paul@0 651
{
paul@0 652
  return static_cast<I2c_jz4780_chip *>(i2c)->get_channel(channel);
paul@0 653
}
paul@0 654
paul@0 655
void jz4780_i2c_set_target(void *i2c_channel, uint8_t addr)
paul@0 656
{
paul@0 657
  static_cast<I2c_jz4780_channel *>(i2c_channel)->set_target(addr);
paul@0 658
}
paul@0 659
paul@0 660
void jz4780_i2c_start_read(void *i2c_channel, uint8_t buf[], unsigned total)
paul@0 661
{
paul@0 662
  static_cast<I2c_jz4780_channel *>(i2c_channel)->start_read(buf, total);
paul@0 663
}
paul@0 664
paul@0 665
void jz4780_i2c_read(void *i2c_channel)
paul@0 666
{
paul@0 667
  static_cast<I2c_jz4780_channel *>(i2c_channel)->read();
paul@0 668
}
paul@0 669
paul@0 670
int jz4780_i2c_read_done(void *i2c_channel)
paul@0 671
{
paul@0 672
  return static_cast<I2c_jz4780_channel *>(i2c_channel)->read_done();
paul@0 673
}
paul@0 674
paul@0 675
unsigned jz4780_i2c_have_read(void *i2c_channel)
paul@0 676
{
paul@0 677
  return static_cast<I2c_jz4780_channel *>(i2c_channel)->have_read();
paul@0 678
}
paul@0 679
paul@0 680
int jz4780_i2c_read_incomplete(void *i2c_channel)
paul@0 681
{
paul@0 682
  return static_cast<I2c_jz4780_channel *>(i2c_channel)->read_incomplete();
paul@0 683
}
paul@0 684
paul@0 685
void jz4780_i2c_write(void *i2c_channel, uint8_t buf[], unsigned total)
paul@0 686
{
paul@0 687
  static_cast<I2c_jz4780_channel *>(i2c_channel)->write(buf, total);
paul@0 688
}
paul@0 689
paul@0 690
void jz4780_i2c_stop(void *i2c_channel)
paul@0 691
{
paul@0 692
  static_cast<I2c_jz4780_channel *>(i2c_channel)->stop();
paul@0 693
}