| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2014 Vladimir Serbinenko <phcoder@gmail.com> |
| * Copyright (C) 2016 Sebastian Grzywna <swiftgeek@gmail.com> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; version 2, or (at your option) |
| * any later verion of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <arch/io.h> |
| #include <console/console.h> |
| #include <delay.h> |
| |
| #include "i915_reg.h" |
| #include "edid.h" |
| #define GMBUS0_ADDR (mmio + 4 * 0) |
| #define GMBUS1_ADDR (mmio + 4 * 1) |
| #define GMBUS2_ADDR (mmio + 4 * 2) |
| #define GMBUS3_ADDR (mmio + 4 * 3) |
| #define GMBUS5_ADDR (mmio + 4 * 8) |
| #define AT24_ADDR 0x50 /* EDID EEPROM */ |
| |
| static void wait_rdy(u8 *mmio) |
| { |
| unsigned try = 100; |
| |
| while (try--) { |
| if (read32(GMBUS2_ADDR) & GMBUS_HW_RDY) |
| return; |
| udelay(10); |
| } |
| } |
| |
| static void intel_gmbus_stop_bus(u8 * mmio, u8 bus) |
| { |
| wait_rdy(mmio); |
| write32(GMBUS0_ADDR, bus); |
| wait_rdy(mmio); |
| write32(GMBUS5_ADDR, 0); |
| write32(GMBUS1_ADDR, GMBUS_SW_RDY | GMBUS_CYCLE_WAIT | GMBUS_CYCLE_INDEX |
| | GMBUS_CYCLE_STOP | ( 0x4 << GMBUS_BYTE_COUNT_SHIFT ) |
| | GMBUS_SLAVE_READ | (AT24_ADDR << 1) ); |
| wait_rdy(mmio); |
| write32(GMBUS5_ADDR, 0); |
| write32(GMBUS1_ADDR, GMBUS_SW_CLR_INT); |
| write32(GMBUS1_ADDR, 0); |
| wait_rdy(mmio); |
| write32(GMBUS1_ADDR, GMBUS_SW_RDY | GMBUS_CYCLE_STOP | GMBUS_SLAVE_WRITE |
| | (AT24_ADDR << 1) ); |
| wait_rdy(mmio); |
| write32(GMBUS1_ADDR, GMBUS_SW_RDY | GMBUS_CYCLE_STOP); |
| write32(GMBUS2_ADDR, GMBUS_INUSE); |
| } |
| |
| void intel_gmbus_stop(u8 *mmio) |
| { |
| intel_gmbus_stop_bus(mmio, 6); |
| intel_gmbus_stop_bus(mmio, 2); |
| } |
| |
| void intel_gmbus_read_edid(u8 *mmio, u8 bus, u8 slave, u8 *edid, u32 edid_size) |
| { |
| int i; |
| |
| slave &= 0x7f; |
| edid_size &= 0x1fc; |
| |
| wait_rdy(mmio); |
| /* 100 KHz, hold 0ns, */ |
| write32(GMBUS0_ADDR, bus); |
| wait_rdy(mmio); |
| /* Ensure index bits are disabled. */ |
| write32(GMBUS5_ADDR, 0); |
| write32(GMBUS1_ADDR, GMBUS_SW_RDY | GMBUS_CYCLE_WAIT | GMBUS_CYCLE_INDEX |
| | (slave << 1) ); |
| wait_rdy(mmio); |
| /* Ensure index bits are disabled. */ |
| write32(GMBUS5_ADDR, 0); |
| write32(GMBUS1_ADDR, GMBUS_SW_RDY | GMBUS_SLAVE_READ | GMBUS_CYCLE_WAIT |
| | GMBUS_CYCLE_STOP |
| | (edid_size << GMBUS_BYTE_COUNT_SHIFT) | (slave << 1) ); |
| for (i = 0; i < edid_size / 4; i++) { |
| u32 reg32; |
| wait_rdy(mmio); |
| reg32 = read32(GMBUS3_ADDR); |
| edid[4 * i] = reg32 & 0xff; |
| edid[4 * i + 1] = (reg32 >> 8) & 0xff; |
| edid[4 * i + 2] = (reg32 >> 16) & 0xff; |
| edid[4 * i + 3] = (reg32 >> 24) & 0xff; |
| } |
| wait_rdy(mmio); |
| write32(GMBUS1_ADDR, GMBUS_SW_RDY |
| | GMBUS_SLAVE_WRITE | GMBUS_CYCLE_WAIT | GMBUS_CYCLE_STOP |
| | (128 << GMBUS_BYTE_COUNT_SHIFT) | (slave << 1) ); |
| wait_rdy(mmio); |
| write32(GMBUS1_ADDR, GMBUS_SW_RDY | GMBUS_CYCLE_STOP ); |
| write32(GMBUS2_ADDR, GMBUS_INUSE); |
| |
| printk (BIOS_SPEW, "EDID:\n"); |
| for (i = 0; i < 128; i++) { |
| printk (BIOS_SPEW, "%02x ", edid[i]); |
| if ((i & 0xf) == 0xf) |
| printk (BIOS_SPEW, "\n"); |
| } |
| } |