From: kettenis Date: Mon, 15 Jan 2018 22:24:17 +0000 (+0000) Subject: Switch the inteldrm(4) i2c code over to the Linux code base. This gives us X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=142c034f02183f3519040a3e39a69a51b83676b4;p=openbsd Switch the inteldrm(4) i2c code over to the Linux code base. This gives us several quirks that wre absent in the old OpenBSD-specific reimplementation. Fixes several issues with external connectors on several generations of hardware. ok deraadt@, benno@ --- diff --git a/sys/dev/pci/drm/drm_linux.c b/sys/dev/pci/drm/drm_linux.c index 40141b85b98..7cabeac05c5 100644 --- a/sys/dev/pci/drm/drm_linux.c +++ b/sys/dev/pci/drm/drm_linux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: drm_linux.c,v 1.17 2018/01/13 13:03:42 robert Exp $ */ +/* $OpenBSD: drm_linux.c,v 1.18 2018/01/15 22:24:17 kettenis Exp $ */ /* * Copyright (c) 2013 Jonathan Gray * Copyright (c) 2015, 2016 Mark Kettenis @@ -552,16 +552,13 @@ sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents, } int -i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { void *cmd = NULL; int cmdlen = 0; int err, ret = 0; int op; - if (adap->algo) - return adap->algo->master_xfer(adap, msgs, num); - iic_acquire_bus(&adap->ic, 0); while (num > 2) { @@ -585,8 +582,10 @@ i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) ret++; } - op = (msgs->flags & I2C_M_RD) ? I2C_OP_READ_WITH_STOP : I2C_OP_WRITE_WITH_STOP; - err = iic_exec(&adap->ic, op, msgs->addr, cmd, cmdlen, msgs->buf, msgs->len, 0); + op = (msgs->flags & I2C_M_RD) ? + I2C_OP_READ_WITH_STOP : I2C_OP_WRITE_WITH_STOP; + err = iic_exec(&adap->ic, op, msgs->addr, cmd, cmdlen, + msgs->buf, msgs->len, 0); if (err) { ret = -err; goto fail; @@ -600,6 +599,38 @@ fail: return ret; } +int +i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + if (adap->algo) + return adap->algo->master_xfer(adap, msgs, num); + + return i2c_master_xfer(adap, msgs, num); +} + +int +i2c_bb_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct i2c_algo_bit_data *algo = adap->algo_data; + struct i2c_adapter bb; + + memset(&bb, 0, sizeof(bb)); + bb.ic = algo->ic; + bb.retries = adap->retries; + return i2c_master_xfer(&bb, msgs, num); +} + +uint32_t +i2c_bb_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +struct i2c_algorithm i2c_bit_algo = { + .master_xfer = i2c_bb_master_xfer, + .functionality = i2c_bb_functionality +}; + #if defined(__amd64__) || defined(__i386__) /* diff --git a/sys/dev/pci/drm/drm_linux.h b/sys/dev/pci/drm/drm_linux.h index c7a83055a69..64ea410476f 100644 --- a/sys/dev/pci/drm/drm_linux.h +++ b/sys/dev/pci/drm/drm_linux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: drm_linux.h,v 1.66 2018/01/13 14:15:07 jsg Exp $ */ +/* $OpenBSD: drm_linux.h,v 1.67 2018/01/15 22:24:17 kettenis Exp $ */ /* * Copyright (c) 2013, 2014, 2015 Mark Kettenis * Copyright (c) 2017 Martin Pieuchot @@ -1199,6 +1199,11 @@ finish_wait(wait_queue_head_t *wq, wait_queue_head_t **wait) static inline long schedule_timeout(long timeout, wait_queue_head_t **wait) { + if (cold) { + delay((timeout * 1000000) / hz); + return -ETIMEDOUT; + } + return -msleep(*wait, &(*wait)->lock, PZERO, "schto", timeout); } @@ -1609,8 +1614,14 @@ struct i2c_msg { #define I2C_M_NOSTART 0x0002 struct i2c_algorithm { - u32 (*functionality)(struct i2c_adapter *); int (*master_xfer)(struct i2c_adapter *, struct i2c_msg *, int); + u32 (*functionality)(struct i2c_adapter *); +}; + +extern struct i2c_algorithm i2c_bit_algo; + +struct i2c_algo_bit_data { + struct i2c_controller ic; }; int i2c_transfer(struct i2c_adapter *, struct i2c_msg *, int); diff --git a/sys/dev/pci/drm/i915/i915_drv.h b/sys/dev/pci/drm/i915/i915_drv.h index c1e4b6f905b..bb8b2ddd35a 100644 --- a/sys/dev/pci/drm/i915/i915_drv.h +++ b/sys/dev/pci/drm/i915/i915_drv.h @@ -1,4 +1,4 @@ -/* $OpenBSD: i915_drv.h,v 1.78 2017/09/30 07:36:56 robert Exp $ */ +/* $OpenBSD: i915_drv.h,v 1.79 2018/01/15 22:24:17 kettenis Exp $ */ /* i915_drv.h -- Private header for the I915 driver -*- linux-c -*- */ /* @@ -1102,9 +1102,7 @@ struct intel_gmbus { u32 force_bit; u32 reg0; u32 gpio_reg; -#ifdef __linux__ struct i2c_algo_bit_data bit_algo; -#endif struct drm_i915_private *dev_priv; }; diff --git a/sys/dev/pci/drm/i915/intel_i2c.c b/sys/dev/pci/drm/i915/intel_i2c.c index bfe2deb6a84..36428ad123b 100644 --- a/sys/dev/pci/drm/i915/intel_i2c.c +++ b/sys/dev/pci/drm/i915/intel_i2c.c @@ -1,19 +1,3 @@ -/* $OpenBSD: intel_i2c.c,v 1.12 2017/09/30 07:36:56 robert Exp $ */ -/* - * Copyright (c) 2012, 2013 Mark Kettenis - * - * 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 * Copyright © 2006-2008,2010 Intel Corporation @@ -42,36 +26,19 @@ * Eric Anholt * Chris Wilson */ - +#ifdef __linux__ +#include +#include +#include +#endif #include -#include #include "intel_drv.h" +#include #include "i915_drv.h" -#include "i915_reg.h" #include #include -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; @@ -156,242 +123,620 @@ intel_i2c_reset(struct drm_device *dev) 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; @@ -402,35 +747,58 @@ intel_setup_gmbus(struct drm_device *dev) 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, @@ -442,25 +810,34 @@ 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); + } }