-/* $OpenBSD: intel_i2c.c,v 1.12 2017/09/30 07:36:56 robert Exp $ */
-/*
- * Copyright (c) 2012, 2013 Mark Kettenis <kettenis@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
/*
* Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
* Copyright © 2006-2008,2010 Intel Corporation
* Eric Anholt <eric@anholt.net>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
-
+#ifdef __linux__
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/export.h>
+#endif
#include <dev/pci/drm/drmP.h>
-#include <dev/pci/drm/drm.h>
#include "intel_drv.h"
+#include <dev/pci/drm/i915_drm.h>
#include "i915_drv.h"
-#include "i915_reg.h"
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/i2c_bitbang.h>
-int intel_gmbus_acquire_bus(void *, int);
-void intel_gmbus_release_bus(void *, int);
-int intel_gmbus_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
- void *buf, size_t, int);
-
-void intel_gpio_bb_set_bits(void *, uint32_t);
-void intel_gpio_bb_set_dir(void *, uint32_t);
-uint32_t intel_gpio_bb_read_bits(void *);
-
-int intel_gpio_send_start(void *, int);
-int intel_gpio_send_stop(void *, int);
-int intel_gpio_initiate_xfer(void *, i2c_addr_t, int);
-int intel_gpio_read_byte(void *, u_int8_t *, int);
-int intel_gpio_write_byte(void *, u_int8_t, int);
-
-enum disp_clk {
- CDCLK,
- CZCLK
-};
-
struct gmbus_pin {
const char *name;
int reg;
I915_WRITE(GMBUS4, 0);
}
-int
-intel_gmbus_acquire_bus(void *cookie, int flags)
+static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable)
{
- struct intel_gmbus *bus = cookie;
- struct inteldrm_softc *dev_priv = bus->dev_priv;
-
- intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+ u32 val;
- I915_WRITE(GMBUS0, bus->reg0);
+ /* When using bit bashing for I2C, this bit needs to be set to 1 */
+ if (!IS_PINEVIEW(dev_priv->dev))
+ return;
- return (0);
+ val = I915_READ(DSPCLK_GATE_D);
+ if (enable)
+ val |= DPCUNIT_CLOCK_GATE_DISABLE;
+ else
+ val &= ~DPCUNIT_CLOCK_GATE_DISABLE;
+ I915_WRITE(DSPCLK_GATE_D, val);
}
-void
-intel_gmbus_release_bus(void *cookie, int flags)
+static u32 get_reserved(struct intel_gmbus *bus)
{
- struct intel_gmbus *bus = cookie;
- struct inteldrm_softc *dev_priv = bus->dev_priv;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ struct drm_device *dev = dev_priv->dev;
+ u32 reserved = 0;
- I915_WRITE(GMBUS0, 0);
+ /* On most chips, these bits must be preserved in software. */
+ if (!IS_I830(dev) && !IS_845G(dev))
+ reserved = I915_READ_NOTRACE(bus->gpio_reg) &
+ (GPIO_DATA_PULLUP_DISABLE |
+ GPIO_CLOCK_PULLUP_DISABLE);
- intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
+ return reserved;
}
-int
-intel_gmbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
- const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
+static int get_clock(void *data)
{
- struct intel_gmbus *bus = cookie;
- struct inteldrm_softc *dev_priv = bus->dev_priv;
- uint32_t reg, st, val;
- uint8_t *b;
- int i, retries;
- uint16_t rem = len;
- int bus_err = 0;
-
- if (cmdlen > 1)
- return (EOPNOTSUPP);
-
- if (I2C_OP_WRITE_P(op)) {
- val = 0;
-
- b = buf;
- for (i = 0; i < 4 && rem > 0; i++, rem--) {
- val |= *b++ << (8 * i);
- }
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | GPIO_CLOCK_DIR_MASK);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved);
+ return (I915_READ_NOTRACE(bus->gpio_reg) & GPIO_CLOCK_VAL_IN) != 0;
+}
- I915_WRITE(GMBUS3, val);
- }
+static int get_data(void *data)
+{
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | GPIO_DATA_DIR_MASK);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved);
+ return (I915_READ_NOTRACE(bus->gpio_reg) & GPIO_DATA_VAL_IN) != 0;
+}
- reg = 0;
- if (len > 0)
- reg |= GMBUS_CYCLE_WAIT;
- if (I2C_OP_STOP_P(op))
- reg |= GMBUS_CYCLE_STOP;
- if (I2C_OP_READ_P(op))
- reg |= GMBUS_SLAVE_READ;
+static void set_clock(void *data, int state_high)
+{
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
+ u32 clock_bits;
+
+ if (state_high)
+ clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
else
- reg |= GMBUS_SLAVE_WRITE;
- if (cmdlen > 0) {
- reg |= GMBUS_CYCLE_INDEX;
- b = (void *)cmdbuf;
- reg |= (b[0] << GMBUS_SLAVE_INDEX_SHIFT);
- }
- reg |= (addr << GMBUS_SLAVE_ADDR_SHIFT);
- reg |= (len << GMBUS_BYTE_COUNT_SHIFT);
- I915_WRITE(GMBUS1, reg | GMBUS_SW_RDY);
-
- if (I2C_OP_READ_P(op)) {
- b = buf;
- while (len > 0) {
- for (retries = 50; retries > 0; retries--) {
- st = I915_READ(GMBUS2);
- if (st & (GMBUS_SATOER | GMBUS_HW_RDY))
- break;
- DELAY(1000);
- }
- if (st & GMBUS_SATOER) {
- bus_err = 1;
- goto out;
- }
- if ((st & GMBUS_HW_RDY) == 0)
- return (ETIMEDOUT);
-
- val = I915_READ(GMBUS3);
- for (i = 0; i < 4 && len > 0; i++, len--) {
- *b++ = val & 0xff;
- val >>= 8;
- }
- }
- }
+ clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
+ GPIO_CLOCK_VAL_MASK;
- if (I2C_OP_WRITE_P(op)) {
- while (rem > 0) {
- for (retries = 50; retries > 0; retries--) {
- st = I915_READ(GMBUS2);
- if (st & (GMBUS_SATOER | GMBUS_HW_RDY))
- break;
- DELAY(1000);
- }
- if (st & GMBUS_SATOER) {
- bus_err = 1;
- goto out;
- }
- if ((st & GMBUS_HW_RDY) == 0)
- return (ETIMEDOUT);
-
- val = 0;
- for (i = 0; i < 4 && rem > 0; i++, rem--) {
- val |= *b++ << (8 * i);
- }
- I915_WRITE(GMBUS3, val);
- }
- }
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | clock_bits);
+ POSTING_READ(bus->gpio_reg);
+}
-out:
- if (I2C_OP_STOP_P(op)) {
- for (retries = 10; retries > 0; retries--) {
- st = I915_READ(GMBUS2);
- if (st & GMBUS_SATOER)
- bus_err = 1;
- if ((st & GMBUS_ACTIVE) == 0)
- break;
- DELAY(1000);
- }
- if (st & GMBUS_ACTIVE)
- return (ETIMEDOUT);
- } else {
- for (retries = 10; retries > 0; retries--) {
- st = I915_READ(GMBUS2);
- if (st & GMBUS_SATOER)
- bus_err = 1;
- if (st & GMBUS_HW_WAIT_PHASE)
- break;
- DELAY(1000);
- }
- if ((st & GMBUS_HW_WAIT_PHASE) == 0)
- return (ETIMEDOUT);
- }
+static void set_data(void *data, int state_high)
+{
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
+ u32 data_bits;
- /* after the bus is idle clear the bus error */
- if (bus_err) {
- I915_WRITE(GMBUS1, GMBUS_SW_CLR_INT);
- I915_WRITE(GMBUS1, 0);
- I915_WRITE(GMBUS0, 0);
- return (ENXIO);
- }
+ if (state_high)
+ data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK;
+ else
+ data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
+ GPIO_DATA_VAL_MASK;
- return (0);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | data_bits);
+ POSTING_READ(bus->gpio_reg);
+}
+
+static int
+intel_gpio_pre_xfer(struct i2c_adapter *adapter)
+{
+ struct intel_gmbus *bus = container_of(adapter,
+ struct intel_gmbus,
+ adapter);
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+
+ intel_i2c_reset(dev_priv->dev);
+ intel_i2c_quirk_set(dev_priv, true);
+ set_data(bus, 1);
+ set_clock(bus, 1);
+ udelay(I2C_RISEFALL_TIME);
+ return 0;
+}
+
+static void
+intel_gpio_post_xfer(struct i2c_adapter *adapter)
+{
+ struct intel_gmbus *bus = container_of(adapter,
+ struct intel_gmbus,
+ adapter);
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+
+ set_data(bus, 1);
+ set_clock(bus, 1);
+ intel_i2c_quirk_set(dev_priv, false);
}
-struct i2c_bitbang_ops intel_gpio_bbops = {
- intel_gpio_bb_set_bits,
- intel_gpio_bb_set_dir,
- intel_gpio_bb_read_bits,
- { GPIO_DATA_VAL_IN, GPIO_CLOCK_VAL_IN,
- GPIO_DATA_DIR_OUT, GPIO_DATA_DIR_IN }
+void intel_bb_set_bits(void *, uint32_t);
+void intel_bb_set_dir(void *, uint32_t);
+uint32_t intel_bb_read_bits(void *);
+
+int intel_acquire_bus(void *, int);
+void intel_release_bus(void *, int);
+int intel_send_start(void *, int);
+int intel_send_stop(void *, int);
+int intel_initiate_xfer(void *, i2c_addr_t, int);
+int intel_read_byte(void *, u_int8_t *, int);
+int intel_write_byte(void *, u_int8_t, int);
+
+#define INTEL_BB_SDA (1 << I2C_BIT_SDA)
+#define INTEL_BB_SCL (1 << I2C_BIT_SCL)
+
+struct i2c_bitbang_ops intel_bbops = {
+ intel_bb_set_bits,
+ intel_bb_set_dir,
+ intel_bb_read_bits,
+ { INTEL_BB_SDA, INTEL_BB_SCL, 0, 0 }
};
void
-intel_gpio_bb_set_bits(void *cookie, uint32_t bits)
+intel_bb_set_bits(void *cookie, uint32_t bits)
{
- struct intel_gmbus *bus = cookie;
- struct inteldrm_softc *dev_priv = bus->dev_priv;
- uint32_t reg;
-
- reg = I915_READ_NOTRACE(bus->gpio_reg);
- reg &= (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE);
- if (bits & GPIO_DATA_VAL_IN)
- reg |= GPIO_DATA_VAL_OUT;
- if (bits & GPIO_CLOCK_VAL_IN)
- reg |= GPIO_CLOCK_VAL_OUT;
- reg |= (GPIO_DATA_VAL_MASK | GPIO_CLOCK_VAL_MASK);
- I915_WRITE_NOTRACE(bus->gpio_reg, reg);
+ set_clock(cookie, bits & INTEL_BB_SCL);
+ set_data(cookie, bits & INTEL_BB_SDA);
}
void
-intel_gpio_bb_set_dir(void *cookie, uint32_t bits)
+intel_bb_set_dir(void *cookie, uint32_t bits)
+{
+}
+
+uint32_t
+intel_bb_read_bits(void *cookie)
+{
+ uint32_t bits = 0;
+
+ if (get_clock(cookie))
+ bits |= INTEL_BB_SCL;
+ if (get_data(cookie))
+ bits |= INTEL_BB_SDA;
+
+ return bits;
+}
+
+int
+intel_acquire_bus(void *cookie, int flags)
{
struct intel_gmbus *bus = cookie;
- struct inteldrm_softc *dev_priv = bus->dev_priv;
- uint32_t reg;
- reg = I915_READ_NOTRACE(bus->gpio_reg);
- reg &= (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE);
- if (bits & GPIO_DATA_DIR_OUT)
- reg |= GPIO_DATA_DIR_OUT;
- reg |= (GPIO_DATA_DIR_MASK | GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK);
- I915_WRITE_NOTRACE(bus->gpio_reg, reg);
+ intel_gpio_pre_xfer(&bus->adapter);
+ return (0);
}
-uint32_t
-intel_gpio_bb_read_bits(void *cookie)
+void
+intel_release_bus(void *cookie, int flags)
{
struct intel_gmbus *bus = cookie;
- struct inteldrm_softc *dev_priv = bus->dev_priv;
- return I915_READ_NOTRACE(bus->gpio_reg);
+ intel_gpio_post_xfer(&bus->adapter);
}
int
-intel_gpio_send_start(void *cookie, int flags)
+intel_send_start(void *cookie, int flags)
{
- return (i2c_bitbang_send_start(cookie, flags, &intel_gpio_bbops));
+ return (i2c_bitbang_send_start(cookie, flags, &intel_bbops));
}
int
-intel_gpio_send_stop(void *cookie, int flags)
+intel_send_stop(void *cookie, int flags)
{
- return (i2c_bitbang_send_stop(cookie, flags, &intel_gpio_bbops));
+ return (i2c_bitbang_send_stop(cookie, flags, &intel_bbops));
}
int
-intel_gpio_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
+intel_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
{
- return (i2c_bitbang_initiate_xfer(cookie, addr, flags, &intel_gpio_bbops));
+ return (i2c_bitbang_initiate_xfer(cookie, addr, flags, &intel_bbops));
}
int
-intel_gpio_read_byte(void *cookie, u_int8_t *bytep, int flags)
+intel_read_byte(void *cookie, u_int8_t *bytep, int flags)
{
- return (i2c_bitbang_read_byte(cookie, bytep, flags, &intel_gpio_bbops));
+ return (i2c_bitbang_read_byte(cookie, bytep, flags, &intel_bbops));
}
int
-intel_gpio_write_byte(void *cookie, u_int8_t byte, int flags)
+intel_write_byte(void *cookie, u_int8_t byte, int flags)
{
- return (i2c_bitbang_write_byte(cookie, byte, flags, &intel_gpio_bbops));
+ return (i2c_bitbang_write_byte(cookie, byte, flags, &intel_bbops));
}
-int
-intel_setup_gmbus(struct drm_device *dev)
+static void
+intel_gpio_setup(struct intel_gmbus *bus, unsigned int pin)
+{
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ struct i2c_algo_bit_data *algo;
+
+ algo = &bus->bit_algo;
+
+ bus->gpio_reg = dev_priv->gpio_mmio_base +
+ get_gmbus_pin(dev_priv, pin)->reg;
+
+ bus->adapter.algo_data = algo;
+#ifdef __linux__
+ algo->setsda = set_data;
+ algo->setscl = set_clock;
+ algo->getsda = get_data;
+ algo->getscl = get_clock;
+ algo->pre_xfer = intel_gpio_pre_xfer;
+ algo->post_xfer = intel_gpio_post_xfer;
+ algo->udelay = I2C_RISEFALL_TIME;
+ algo->timeout = usecs_to_jiffies(2200);
+ algo->data = bus;
+#else
+ algo->ic.ic_cookie = bus;
+ algo->ic.ic_acquire_bus = intel_acquire_bus;
+ algo->ic.ic_release_bus = intel_release_bus;
+ algo->ic.ic_send_start = intel_send_start;
+ algo->ic.ic_send_stop = intel_send_stop;
+ algo->ic.ic_initiate_xfer = intel_initiate_xfer;
+ algo->ic.ic_read_byte = intel_read_byte;
+ algo->ic.ic_write_byte = intel_write_byte;
+#endif
+}
+
+static int
+gmbus_wait_hw_status(struct drm_i915_private *dev_priv,
+ u32 gmbus2_status,
+ u32 gmbus4_irq_en)
+{
+ int i;
+ u32 gmbus2 = 0;
+ DEFINE_WAIT(wait);
+
+ if (!HAS_GMBUS_IRQ(dev_priv->dev) || cold)
+ gmbus4_irq_en = 0;
+
+ /* Important: The hw handles only the first bit, so set only one! Since
+ * we also need to check for NAKs besides the hw ready/idle signal, we
+ * need to wake up periodically and check that ourselves. */
+ I915_WRITE(GMBUS4, gmbus4_irq_en);
+
+ for (i = 0; i < msecs_to_jiffies_timeout(50); i++) {
+ prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait,
+ TASK_UNINTERRUPTIBLE);
+
+ gmbus2 = I915_READ_NOTRACE(GMBUS2);
+ if (gmbus2 & (GMBUS_SATOER | gmbus2_status))
+ break;
+
+ schedule_timeout(1, &wait);
+ }
+ finish_wait(&dev_priv->gmbus_wait_queue, &wait);
+
+ I915_WRITE(GMBUS4, 0);
+
+ if (gmbus2 & GMBUS_SATOER)
+ return -ENXIO;
+ if (gmbus2 & gmbus2_status)
+ return 0;
+ return -ETIMEDOUT;
+}
+
+static int
+gmbus_wait_idle(struct drm_i915_private *dev_priv)
+{
+ int ret;
+
+#define C ((I915_READ_NOTRACE(GMBUS2) & GMBUS_ACTIVE) == 0)
+
+ if (!HAS_GMBUS_IRQ(dev_priv->dev) || cold)
+ return wait_for(C, 10);
+
+ /* Important: The hw handles only the first bit, so set only one! */
+ I915_WRITE(GMBUS4, GMBUS_IDLE_EN);
+
+ ret = wait_event_timeout(dev_priv->gmbus_wait_queue, C,
+ msecs_to_jiffies_timeout(10));
+
+ I915_WRITE(GMBUS4, 0);
+
+ if (ret)
+ return 0;
+ else
+ return -ETIMEDOUT;
+#undef C
+}
+
+static int
+gmbus_xfer_read_chunk(struct drm_i915_private *dev_priv,
+ unsigned short addr, u8 *buf, unsigned int len,
+ u32 gmbus1_index)
+{
+ I915_WRITE(GMBUS1,
+ gmbus1_index |
+ GMBUS_CYCLE_WAIT |
+ (len << GMBUS_BYTE_COUNT_SHIFT) |
+ (addr << GMBUS_SLAVE_ADDR_SHIFT) |
+ GMBUS_SLAVE_READ | GMBUS_SW_RDY);
+ while (len) {
+ int ret;
+ u32 val, loop = 0;
+
+ ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
+ GMBUS_HW_RDY_EN);
+ if (ret)
+ return ret;
+
+ val = I915_READ(GMBUS3);
+ do {
+ *buf++ = val & 0xff;
+ val >>= 8;
+ } while (--len && ++loop < 4);
+ }
+
+ return 0;
+}
+
+static int
+gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
+ u32 gmbus1_index)
+{
+ u8 *buf = msg->buf;
+ unsigned int rx_size = msg->len;
+ unsigned int len;
+ int ret;
+
+ do {
+ len = min(rx_size, GMBUS_BYTE_COUNT_MAX);
+
+ ret = gmbus_xfer_read_chunk(dev_priv, msg->addr,
+ buf, len, gmbus1_index);
+ if (ret)
+ return ret;
+
+ rx_size -= len;
+ buf += len;
+ } while (rx_size != 0);
+
+ return 0;
+}
+
+static int
+gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
+ unsigned short addr, u8 *buf, unsigned int len)
+{
+ unsigned int chunk_size = len;
+ u32 val, loop;
+
+ val = loop = 0;
+ while (len && loop < 4) {
+ val |= *buf++ << (8 * loop++);
+ len -= 1;
+ }
+
+ I915_WRITE(GMBUS3, val);
+ I915_WRITE(GMBUS1,
+ GMBUS_CYCLE_WAIT |
+ (chunk_size << GMBUS_BYTE_COUNT_SHIFT) |
+ (addr << GMBUS_SLAVE_ADDR_SHIFT) |
+ GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
+ while (len) {
+ int ret;
+
+ val = loop = 0;
+ do {
+ val |= *buf++ << (8 * loop);
+ } while (--len && ++loop < 4);
+
+ I915_WRITE(GMBUS3, val);
+
+ ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
+ GMBUS_HW_RDY_EN);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
+{
+ u8 *buf = msg->buf;
+ unsigned int tx_size = msg->len;
+ unsigned int len;
+ int ret;
+
+ do {
+ len = min(tx_size, GMBUS_BYTE_COUNT_MAX);
+
+ ret = gmbus_xfer_write_chunk(dev_priv, msg->addr, buf, len);
+ if (ret)
+ return ret;
+
+ buf += len;
+ tx_size -= len;
+ } while (tx_size != 0);
+
+ return 0;
+}
+
+/*
+ * The gmbus controller can combine a 1 or 2 byte write with a read that
+ * immediately follows it by using an "INDEX" cycle.
+ */
+static bool
+gmbus_is_index_read(struct i2c_msg *msgs, int i, int num)
+{
+ return (i + 1 < num &&
+ msgs[i].addr == msgs[i + 1].addr &&
+ !(msgs[i].flags & I2C_M_RD) &&
+ (msgs[i].len == 1 || msgs[i].len == 2) &&
+ (msgs[i + 1].flags & I2C_M_RD));
+}
+
+static int
+gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
+{
+ u32 gmbus1_index = 0;
+ u32 gmbus5 = 0;
+ int ret;
+
+ if (msgs[0].len == 2)
+ gmbus5 = GMBUS_2BYTE_INDEX_EN |
+ msgs[0].buf[1] | (msgs[0].buf[0] << 8);
+ if (msgs[0].len == 1)
+ gmbus1_index = GMBUS_CYCLE_INDEX |
+ (msgs[0].buf[0] << GMBUS_SLAVE_INDEX_SHIFT);
+
+ /* GMBUS5 holds 16-bit index */
+ if (gmbus5)
+ I915_WRITE(GMBUS5, gmbus5);
+
+ ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index);
+
+ /* Clear GMBUS5 after each index transfer */
+ if (gmbus5)
+ I915_WRITE(GMBUS5, 0);
+
+ return ret;
+}
+
+static int
+gmbus_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct intel_gmbus *bus = container_of(adapter,
+ struct intel_gmbus,
+ adapter);
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ int i = 0, inc, try = 0;
+ int ret = 0;
+
+ intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+ mutex_lock(&dev_priv->gmbus_mutex);
+
+ if (bus->force_bit) {
+ ret = i2c_bit_algo.master_xfer(adapter, msgs, num);
+ goto out;
+ }
+
+retry:
+ I915_WRITE(GMBUS0, bus->reg0);
+
+ for (; i < num; i += inc) {
+ inc = 1;
+ if (gmbus_is_index_read(msgs, i, num)) {
+ ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
+ inc = 2; /* an index read is two msgs */
+ } else if (msgs[i].flags & I2C_M_RD) {
+ ret = gmbus_xfer_read(dev_priv, &msgs[i], 0);
+ } else {
+ ret = gmbus_xfer_write(dev_priv, &msgs[i]);
+ }
+
+ if (ret == -ETIMEDOUT)
+ goto timeout;
+ if (ret == -ENXIO)
+ goto clear_err;
+
+ ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE,
+ GMBUS_HW_WAIT_EN);
+ if (ret == -ENXIO)
+ goto clear_err;
+ if (ret)
+ goto timeout;
+ }
+
+ /* Generate a STOP condition on the bus. Note that gmbus can't generata
+ * a STOP on the very first cycle. To simplify the code we
+ * unconditionally generate the STOP condition with an additional gmbus
+ * cycle. */
+ I915_WRITE(GMBUS1, GMBUS_CYCLE_STOP | GMBUS_SW_RDY);
+
+ /* Mark the GMBUS interface as disabled after waiting for idle.
+ * We will re-enable it at the start of the next xfer,
+ * till then let it sleep.
+ */
+ if (gmbus_wait_idle(dev_priv)) {
+ DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n",
+ adapter->name);
+ ret = -ETIMEDOUT;
+ }
+ I915_WRITE(GMBUS0, 0);
+ ret = ret ?: i;
+ goto out;
+
+clear_err:
+ /*
+ * Wait for bus to IDLE before clearing NAK.
+ * If we clear the NAK while bus is still active, then it will stay
+ * active and the next transaction may fail.
+ *
+ * If no ACK is received during the address phase of a transaction, the
+ * adapter must report -ENXIO. It is not clear what to return if no ACK
+ * is received at other times. But we have to be careful to not return
+ * spurious -ENXIO because that will prevent i2c and drm edid functions
+ * from retrying. So return -ENXIO only when gmbus properly quiescents -
+ * timing out seems to happen when there _is_ a ddc chip present, but
+ * it's slow responding and only answers on the 2nd retry.
+ */
+ ret = -ENXIO;
+ if (gmbus_wait_idle(dev_priv)) {
+ DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n",
+ adapter->name);
+ ret = -ETIMEDOUT;
+ }
+
+ /* Toggle the Software Clear Interrupt bit. This has the effect
+ * of resetting the GMBUS controller and so clearing the
+ * BUS_ERROR raised by the slave's NAK.
+ */
+ I915_WRITE(GMBUS1, GMBUS_SW_CLR_INT);
+ I915_WRITE(GMBUS1, 0);
+ I915_WRITE(GMBUS0, 0);
+
+ DRM_DEBUG_KMS("GMBUS [%s] NAK for addr: %04x %c(%d)\n",
+ adapter->name, msgs[i].addr,
+ (msgs[i].flags & I2C_M_RD) ? 'r' : 'w', msgs[i].len);
+
+ /*
+ * Passive adapters sometimes NAK the first probe. Retry the first
+ * message once on -ENXIO for GMBUS transfers; the bit banging algorithm
+ * has retries internally. See also the retry loop in
+ * drm_do_probe_ddc_edid, which bails out on the first -ENXIO.
+ */
+ if (ret == -ENXIO && i == 0 && try++ == 0) {
+ DRM_DEBUG_KMS("GMBUS [%s] NAK on first message, retry\n",
+ adapter->name);
+ goto retry;
+ }
+
+ goto out;
+
+timeout:
+ DRM_INFO("GMBUS [%s] timed out, falling back to bit banging on pin %d\n",
+ bus->adapter.name, bus->reg0 & 0xff);
+ I915_WRITE(GMBUS0, 0);
+
+ /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */
+ bus->force_bit = 1;
+ ret = i2c_bit_algo.master_xfer(adapter, msgs, num);
+
+out:
+ mutex_unlock(&dev_priv->gmbus_mutex);
+
+ intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
+
+ return ret;
+}
+
+static u32 gmbus_func(struct i2c_adapter *adapter)
+{
+ return i2c_bit_algo.functionality(adapter) &
+ (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ /* I2C_FUNC_10BIT_ADDR | */
+ I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+ I2C_FUNC_SMBUS_BLOCK_PROC_CALL);
+}
+
+static const struct i2c_algorithm gmbus_algorithm = {
+ .master_xfer = gmbus_xfer,
+ .functionality = gmbus_func
+};
+
+/**
+ * intel_gmbus_setup - instantiate all Intel i2c GMBuses
+ * @dev: DRM device
+ */
+int intel_setup_gmbus(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_gmbus *bus;
unsigned int pin;
+ int ret;
if (HAS_PCH_NOP(dev))
return 0;
else
dev_priv->gpio_mmio_base = 0;
+ rw_init(&dev_priv->gmbus_mutex, "gmbus");
+ init_waitqueue_head(&dev_priv->gmbus_wait_queue);
+
for (pin = 0; pin < ARRAY_SIZE(dev_priv->gmbus); pin++) {
if (!intel_gmbus_is_valid_pin(dev_priv, pin))
continue;
bus = &dev_priv->gmbus[pin];
+#ifdef notyet
+ bus->adapter.owner = THIS_MODULE;
+ bus->adapter.class = I2C_CLASS_DDC;
+#endif
+ snprintf(bus->adapter.name,
+ sizeof(bus->adapter.name),
+ "i915 gmbus %s",
+ get_gmbus_pin(dev_priv, pin)->name);
+
+#ifdef notyet
+ bus->adapter.dev.parent = &dev->pdev->dev;
+#endif
bus->dev_priv = dev_priv;
+ bus->adapter.algo = &gmbus_algorithm;
+
/* By default use a conservative clock rate */
bus->reg0 = pin | GMBUS_RATE_100KHZ;
- bus->adapter.ic.ic_cookie = bus;
- bus->adapter.ic.ic_acquire_bus = intel_gmbus_acquire_bus;
- bus->adapter.ic.ic_release_bus = intel_gmbus_release_bus;
- bus->adapter.ic.ic_exec = intel_gmbus_exec;
+ /* gmbus seems to be broken on i830 */
+ if (IS_I830(dev))
+ bus->force_bit = 1;
- bus->gpio_reg = dev_priv->gpio_mmio_base +
- get_gmbus_pin(dev_priv, pin)->reg;
+ intel_gpio_setup(bus, pin);
- bus->adapter.ic.ic_send_start = intel_gpio_send_start;
- bus->adapter.ic.ic_send_stop = intel_gpio_send_stop;
- bus->adapter.ic.ic_initiate_xfer = intel_gpio_initiate_xfer;
- bus->adapter.ic.ic_read_byte = intel_gpio_read_byte;
- bus->adapter.ic.ic_write_byte = intel_gpio_write_byte;
+ ret = i2c_add_adapter(&bus->adapter);
+ if (ret)
+ goto err;
}
intel_i2c_reset(dev_priv->dev);
- return (0);
+ return 0;
+
+err:
+ while (pin--) {
+ if (!intel_gmbus_is_valid_pin(dev_priv, pin))
+ continue;
+
+ bus = &dev_priv->gmbus[pin];
+ i2c_del_adapter(&bus->adapter);
+ }
+ return ret;
}
struct i2c_adapter *intel_gmbus_get_adapter(struct drm_i915_private *dev_priv,
return &dev_priv->gmbus[pin].adapter;
}
-void
-intel_teardown_gmbus(struct drm_device *dev)
+void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed)
{
+ struct intel_gmbus *bus = to_intel_gmbus(adapter);
+
+ bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | speed;
}
-void
-intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit)
+void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit)
{
struct intel_gmbus *bus = to_intel_gmbus(adapter);
- if (force_bit)
- bus->force_bit++;
- else
- bus->force_bit--;
+ bus->force_bit += force_bit ? 1 : -1;
+ DRM_DEBUG_KMS("%sabling bit-banging on %s. force bit now %d\n",
+ force_bit ? "en" : "dis", adapter->name,
+ bus->force_bit);
+}
- KASSERT(bus->force_bit >= 0);
+void intel_teardown_gmbus(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_gmbus *bus;
+ unsigned int pin;
- if (bus->force_bit)
- bus->adapter.ic.ic_exec = NULL;
- else
- bus->adapter.ic.ic_exec = intel_gmbus_exec;
+ for (pin = 0; pin < ARRAY_SIZE(dev_priv->gmbus); pin++) {
+ if (!intel_gmbus_is_valid_pin(dev_priv, pin))
+ continue;
+
+ bus = &dev_priv->gmbus[pin];
+ i2c_del_adapter(&bus->adapter);
+ }
}