1450N/A/*
1450N/A * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
1450N/A */
1450N/A
1450N/A#include <sys/types.h>
1450N/A#include <sys/ddi.h>
1450N/A
1450N/A#include "efb.h"
1450N/A#include "efb_i2c.h"
1450N/A
1450N/A
1450N/Astatic int efb_write_bit(void *, int, const efb_i2c_functions_t *, int);
1450N/Astatic int efb_read_bit(void *, int, const efb_i2c_functions_t *, int *);
1450N/A
1450N/Aint
1450N/Aefb_i2c_stop(void *efb_priv, int port, const efb_i2c_functions_t *fcns)
1450N/A{
1450N/A int status;
1450N/A
1450N/A /*
1450N/A * A word of explanation. During data transfers in the I2C protocol,
1450N/A * data never changes while the clock is high. The "start" and 'stop"
1450N/A * conditions are marked by doing just that. The "stop" condition
1450N/A * is signaled by raising the data line while the clock is high.
1450N/A *
1450N/A * This condition should be as simple to implement as simply calling
1450N/A * set_sda(1). The problem here is that we can't count on the
1450N/A * initial states of either clock or data.
1450N/A *
1450N/A * To make this work, we need to first make sure the data line is low
1450N/A * so we can raise it. However, in order to make sure that lowering
1450N/A * the data line doesn't generate a start condition, we must also make
1450N/A * sure the clock is low too. Thus, the sequence is:
1450N/A * lower clock
1450N/A * lower data
1450N/A * raise clock
1450N/A * raise data <= this is the actual "stop"
1450N/A *
1450N/A * TODO: would it make sense to read the clock and data lines first
1450N/A * so we can skip these steps?
1450N/A */
1450N/A fcns->set_scl(efb_priv, port, 0); /* make sure clock is low */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_sda(efb_priv, port, 0); /* make sure data is low */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_scl(efb_priv, port, 1); /* release clock */
1450N/A
1450N/A status = efb_i2c_clk_stretch(efb_priv, port, fcns);
1450N/A if (status != EFB_I2C_OK) {
1450N/A return (status);
1450N/A }
1450N/A
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_sda(efb_priv, port, 1); /* generate the stop */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A return (EFB_I2C_OK);
1450N/A}
1450N/A
1450N/A
1450N/Aint
1450N/Aefb_i2c_start(void *efb_priv, int port, const efb_i2c_functions_t *fcns)
1450N/A{
1450N/A int status;
1450N/A
1450N/A /*
1450N/A * See above. A start condition is caused by data going low
1450N/A * while clock is high.
1450N/A */
1450N/A fcns->set_scl(efb_priv, port, 0); /* make sure clock is low */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_sda(efb_priv, port, 1); /* make sure data is high */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_scl(efb_priv, port, 1); /* release clock */
1450N/A
1450N/A status = efb_i2c_clk_stretch(efb_priv, port, fcns);
1450N/A if (status != EFB_I2C_OK) {
1450N/A return (status);
1450N/A }
1450N/A
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_sda(efb_priv, port, 0); /* generate the start */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A return (EFB_I2C_OK);
1450N/A}
1450N/A
1450N/A
1450N/Astatic int
1450N/Aefb_read_byte_data(void *efb_priv, int port,
1450N/A const efb_i2c_functions_t *fcns, uint8_t *rval)
1450N/A{
1450N/A int b0, b1, b2, b3, b4, b5, b6, b7;
1450N/A int status;
1450N/A
1450N/A /* Read a byte from client without sending either ack or nack */
1450N/A
1450N/A if ((status = efb_read_bit(efb_priv, port, fcns, &b7)) != EFB_I2C_OK ||
1450N/A (status = efb_read_bit(efb_priv, port, fcns, &b6)) != EFB_I2C_OK ||
1450N/A (status = efb_read_bit(efb_priv, port, fcns, &b5)) != EFB_I2C_OK ||
1450N/A (status = efb_read_bit(efb_priv, port, fcns, &b4)) != EFB_I2C_OK ||
1450N/A (status = efb_read_bit(efb_priv, port, fcns, &b3)) != EFB_I2C_OK ||
1450N/A (status = efb_read_bit(efb_priv, port, fcns, &b2)) != EFB_I2C_OK ||
1450N/A (status = efb_read_bit(efb_priv, port, fcns, &b1)) != EFB_I2C_OK ||
1450N/A (status = efb_read_bit(efb_priv, port, fcns, &b0)) != EFB_I2C_OK) {
1450N/A return (status);
1450N/A }
1450N/A
1450N/A *rval =
1450N/A (b7 << 7) |
1450N/A (b6 << 6) |
1450N/A (b5 << 5) |
1450N/A (b4 << 4) |
1450N/A (b3 << 3) |
1450N/A (b2 << 2) |
1450N/A (b1 << 1) |
1450N/A (b0 << 0);
1450N/A
1450N/A return (EFB_I2C_OK);
1450N/A}
1450N/A
1450N/A
1450N/Aint
1450N/Aefb_i2c_read_byte(void *efb_priv, int port,
1450N/A const efb_i2c_functions_t *fcns, uint8_t *rval)
1450N/A{
1450N/A int status;
1450N/A
1450N/A status = efb_read_byte_data(efb_priv, port, fcns, rval);
1450N/A if (status != EFB_I2C_OK) {
1450N/A return (status);
1450N/A }
1450N/A
1450N/A return (efb_write_bit(efb_priv, port, fcns, 0)); /* ack */
1450N/A}
1450N/A
1450N/A
1450N/Aint
1450N/Aefb_i2c_read_byte_noack(void *efb_priv, int port,
1450N/A const efb_i2c_functions_t *fcns, uint8_t *rval)
1450N/A{
1450N/A int status;
1450N/A
1450N/A status = efb_read_byte_data(efb_priv, port, fcns, rval);
1450N/A if (status != EFB_I2C_OK) {
1450N/A return (status);
1450N/A }
1450N/A
1450N/A fcns->set_scl(efb_priv, port, 0);
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_sda(efb_priv, port, 1); /* nack */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_scl(efb_priv, port, 1);
1450N/A
1450N/A status = efb_i2c_clk_stretch(efb_priv, port, fcns);
1450N/A if (status != EFB_I2C_OK) {
1450N/A return (status);
1450N/A }
1450N/A
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A status = fcns->get_sda(efb_priv, port); /* should float high */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A return (status == 1 ? EFB_I2C_OK : EFB_I2C_READ_NOACK);
1450N/A}
1450N/A
1450N/A
1450N/Astatic int
1450N/Aefb_read_bit(void *efb_priv, int port,
1450N/A const efb_i2c_functions_t *fcns, int *rval)
1450N/A{
1450N/A int status;
1450N/A
1450N/A fcns->set_scl(efb_priv, port, 0);
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_sda(efb_priv, port, 1); /* release data line */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_scl(efb_priv, port, 1);
1450N/A
1450N/A status = efb_i2c_clk_stretch(efb_priv, port, fcns);
1450N/A if (status != EFB_I2C_OK) {
1450N/A return (status);
1450N/A }
1450N/A
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A *rval = fcns->get_sda(efb_priv, port);
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A return (EFB_I2C_OK);
1450N/A}
1450N/A
1450N/A
1450N/Aint
1450N/Aefb_i2c_write_byte(void *efb_priv, int port,
1450N/A const efb_i2c_functions_t *fcns, int data)
1450N/A{
1450N/A int status;
1450N/A int i;
1450N/A int bit;
1450N/A
1450N/A /* Read the bits high-to-low, write them out */
1450N/A for (i = 0; i < 8; ++i) {
1450N/A bit = (data >> 7) & 1;
1450N/A data <<= 1;
1450N/A
1450N/A status = efb_write_bit(efb_priv, port, fcns, bit);
1450N/A if (status != EFB_I2C_OK) {
1450N/A return (status);
1450N/A }
1450N/A }
1450N/A
1450N/A return (efb_i2c_check_write_ack(efb_priv, port, fcns));
1450N/A}
1450N/A
1450N/A
1450N/Astatic int
1450N/Aefb_write_bit(void *efb_priv, int port,
1450N/A const efb_i2c_functions_t *fcns, int data)
1450N/A{
1450N/A int status;
1450N/A
1450N/A /*
1450N/A * One clock cycle is divided into four units of clock_interval.
1450N/A * The timing diagram looks like:
1450N/A *
1450N/A * SDA _----____----___ (_=old data, -=new)
1450N/A * SCL __--__--__--__--
1450N/A * ^^^^
1450N/A * one cycle
1450N/A *
1450N/A * (This diagram illustrates four transfer cycles, this
1450N/A * function only executes one.)
1450N/A */
1450N/A
1450N/A fcns->set_scl(efb_priv, port, 0);
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_sda(efb_priv, port, data);
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_scl(efb_priv, port, 1);
1450N/A
1450N/A status = efb_i2c_clk_stretch(efb_priv, port, fcns);
1450N/A if (status != EFB_I2C_OK) {
1450N/A return (status);
1450N/A }
1450N/A
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A return (EFB_I2C_OK);
1450N/A}
1450N/A
1450N/A
1450N/Aint
1450N/Aefb_i2c_check_write_ack(void *efb_priv, int port,
1450N/A const efb_i2c_functions_t *fcns)
1450N/A{
1450N/A int status;
1450N/A
1450N/A /*
1450N/A * We've transfered eight bits to the slave. Cycle the clock
1450N/A * one more time and let the slave acknowledge by pulling the
1450N/A * data line to zero.
1450N/A */
1450N/A
1450N/A fcns->set_scl(efb_priv, port, 0); /* clock low */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_sda(efb_priv, port, 1); /* release data line */
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A fcns->set_scl(efb_priv, port, 1);
1450N/A
1450N/A status = efb_i2c_clk_stretch(efb_priv, port, fcns);
1450N/A if (status != EFB_I2C_OK) {
1450N/A return (status);
1450N/A }
1450N/A
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A status = fcns->get_sda(efb_priv, port);
1450N/A fcns->ddc_clk(efb_priv, port, 1);
1450N/A
1450N/A return (status == 0 ? EFB_I2C_OK : EFB_I2C_NOACK);
1450N/A}
1450N/A
1450N/A
1450N/Avoid
1450N/Aefb_i2c_write_reg(void *efb_priv, const efb_i2c_functions_t *fcns,
1450N/A uint_t port, uint_t i2c_addr, uint_t reg_addr, uchar_t data)
1450N/A{
1450N/A (void) efb_i2c_start(efb_priv, port, fcns);
1450N/A
1450N/A (void) efb_i2c_write_byte(efb_priv, port, fcns, i2c_addr);
1450N/A (void) efb_i2c_write_byte(efb_priv, port, fcns, reg_addr);
1450N/A (void) efb_i2c_write_byte(efb_priv, port, fcns, data);
1450N/A
1450N/A (void) efb_i2c_stop(efb_priv, port, fcns);
1450N/A}
1450N/A
1450N/A
1450N/Aint
1450N/Aefb_i2c_clk_stretch(void *efb_priv, int port, const efb_i2c_functions_t *fcns)
1450N/A{
1450N/A int status = 0;
1450N/A int count = fcns->clock_stretch; /* 10,000 �sec */
1450N/A
1450N/A /*
1450N/A * Slave devices can put a transfer on hold by holding the
1450N/A * clock low. This function waits up to 10 ms for the
1450N/A * clock to go high before returning error.
1450N/A */
1450N/A
1450N/A while (--count >= 0) {
1450N/A drv_usecwait(1);
1450N/A if (fcns->get_scl(efb_priv, port)) {
1450N/A break;
1450N/A }
1450N/A }
1450N/A
1450N/A status = (count <= 0) ? EFB_I2C_STRETCH : EFB_I2C_OK;
1450N/A
1450N/A return (status);
1450N/A}