NetBSD/sys/dev/pci/igma.c
chs 2f02870f3b zero the i2c_attach_args structure before filling it in.
fixes occasional crashes in iic_attach().
2016-02-14 19:54:19 +00:00

641 lines
16 KiB
C

/* $NetBSD: igma.c,v 1.3 2016/02/14 19:54:21 chs Exp $ */
/*
* Copyright (c) 2014 Michael van Elst
*
* 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.
*/
/*
* Intel Graphic Media Accelerator
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: igma.c,v 1.3 2016/02/14 19:54:21 chs Exp $");
#include "vga.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/bus.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pciio.h>
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/i2c_bitbang.h>
#include <dev/i2c/ddcvar.h>
#include <dev/videomode/videomode.h>
#include <dev/videomode/edidvar.h>
#include <dev/wscons/wsdisplayvar.h>
#if NVGA > 0
#include <dev/ic/mc6845reg.h>
#include <dev/ic/pcdisplayvar.h>
#include <dev/ic/vgareg.h>
#include <dev/ic/vgavar.h>
#endif
#include <dev/pci/igmareg.h>
#include <dev/pci/igmavar.h>
#include "igmafb.h"
struct igma_softc;
struct igma_i2c {
kmutex_t ii_lock;
struct igma_softc *ii_sc;
bus_addr_t ii_reg;
struct i2c_controller ii_i2c;
const char *ii_name;
u_int32_t ii_dir;
};
struct igma_softc {
device_t sc_dev;
struct igma_chip sc_chip;
struct igma_i2c sc_ii[GMBUS_NUM_PORTS];
};
static int igma_match(device_t, cfdata_t, void *);
static void igma_attach(device_t, device_t, void *);
static int igma_print(void *, const char *);
static void igma_i2c_attach(struct igma_softc *);
CFATTACH_DECL_NEW(igma, sizeof(struct igma_softc),
igma_match, igma_attach, NULL, NULL);
static int igma_i2c_acquire_bus(void *, int);
static void igma_i2c_release_bus(void *, int);
static int igma_i2c_send_start(void *, int);
static int igma_i2c_send_stop(void *, int);
static int igma_i2c_initiate_xfer(void *, i2c_addr_t, int);
static int igma_i2c_read_byte(void *, uint8_t *, int);
static int igma_i2c_write_byte(void *, uint8_t, int);
static void igma_i2cbb_set_bits(void *, uint32_t);
static void igma_i2cbb_set_dir(void *, uint32_t);
static uint32_t igma_i2cbb_read(void *);
static void igma_reg_barrier(const struct igma_chip *, int);
static u_int32_t igma_reg_read(const struct igma_chip *, int);
static void igma_reg_write(const struct igma_chip *, int, u_int32_t);
static u_int8_t igma_vga_read(const struct igma_chip *, int);
static void igma_vga_write(const struct igma_chip *, int , u_int8_t);
#if 0
static u_int8_t igma_crtc_read(const struct igma_chip *, int);
static void igma_crtc_write(const struct igma_chip *, int, u_int8_t);
#endif
static const struct i2c_bitbang_ops igma_i2cbb_ops = {
igma_i2cbb_set_bits,
igma_i2cbb_set_dir,
igma_i2cbb_read,
{ 1, 2, 0, 1 }
};
static const struct igma_chip_ops igma_bus_ops = {
igma_reg_barrier,
igma_reg_read,
igma_reg_write,
igma_vga_read,
igma_vga_write,
#if 0
igma_crtc_read,
igma_crtc_write,
#endif
};
static struct igma_product {
u_int16_t product;
int gentype;
int num_pipes;
} const igma_products[] = {
/* i830 */
{ PCI_PRODUCT_INTEL_82830MP_IV, 200,2 },
/* i845g */
{ PCI_PRODUCT_INTEL_82845G_IGD, 200,2 },
/* i85x */
{ PCI_PRODUCT_INTEL_82855GM_IGD, 200,2 },
// 0x358e ?
/* i865g */
{ PCI_PRODUCT_INTEL_82865_IGD, 200,2 },
/* i915g */
{ PCI_PRODUCT_INTEL_82915G_IGD, 200,2 },
{ PCI_PRODUCT_INTEL_E7221_IGD, 200,2 },
/* i915gm */
{ PCI_PRODUCT_INTEL_82915GM_IGD, 300,2 },
/* i945g */
{ PCI_PRODUCT_INTEL_82945P_IGD, 300,2 },
/* i945gm */
{ PCI_PRODUCT_INTEL_82945GM_IGD, 300,2 },
{ PCI_PRODUCT_INTEL_82945GM_IGD_1, 300,2 },
{ PCI_PRODUCT_INTEL_82945GME_IGD, 300,2 },
/* i965g */
{ PCI_PRODUCT_INTEL_82946GZ_IGD, 300,2 },
{ PCI_PRODUCT_INTEL_82G35_IGD, 300,2 },
{ PCI_PRODUCT_INTEL_82G35_IGD_1, 300,2 },
{ PCI_PRODUCT_INTEL_82965Q_IGD, 300,2 },
{ PCI_PRODUCT_INTEL_82965Q_IGD_1, 300,2 },
{ PCI_PRODUCT_INTEL_82965G_IGD, 300,2 },
{ PCI_PRODUCT_INTEL_82965G_IGD_1, 300,2 },
/* g33 */
{ PCI_PRODUCT_INTEL_82G33_IGD, 300,2 },
{ PCI_PRODUCT_INTEL_82G33_IGD_1, 300,2 },
{ PCI_PRODUCT_INTEL_82Q33_IGD, 300,2 },
{ PCI_PRODUCT_INTEL_82Q33_IGD_1, 300,2 },
{ PCI_PRODUCT_INTEL_82Q35_IGD, 300,2 },
{ PCI_PRODUCT_INTEL_82Q35_IGD_1, 300,2 },
/* pineview */
{ PCI_PRODUCT_INTEL_PINEVIEW_IGD, 350,2 },
{ PCI_PRODUCT_INTEL_PINEVIEW_M_IGD, 350,2 },
/* i965gm */
{ PCI_PRODUCT_INTEL_82965PM_IGD, 400,2 },
{ PCI_PRODUCT_INTEL_82965PM_IGD_1, 400,2 },
{ PCI_PRODUCT_INTEL_82965GME_IGD, 400,2 },
/* gm45 */
{ PCI_PRODUCT_INTEL_82GM45_IGD, 450,2 },
{ PCI_PRODUCT_INTEL_82GM45_IGD_1, 450,2 },
/* g45 */
{ PCI_PRODUCT_INTEL_82IGD_E_IGD, 450,2 },
{ PCI_PRODUCT_INTEL_82Q45_IGD, 450,2 },
{ PCI_PRODUCT_INTEL_82G45_IGD, 450,2 },
{ PCI_PRODUCT_INTEL_82G41_IGD, 450,2 },
{ PCI_PRODUCT_INTEL_82B43_IGD, 450,2 },
// 0x2e92 ?
/* ironlake d */
{ PCI_PRODUCT_INTEL_IRONLAKE_D_IGD, 500,2 },
/* ironlake m */
{ PCI_PRODUCT_INTEL_IRONLAKE_M_IGD, 500,2 },
/* sandy bridge */
{ PCI_PRODUCT_INTEL_SANDYBRIDGE_IGD, 600,2 },
{ PCI_PRODUCT_INTEL_SANDYBRIDGE_IGD_1, 600,2 },
{ PCI_PRODUCT_INTEL_SANDYBRIDGE_IGD_2, 600,2 },
/* sandy bridge m */
{ PCI_PRODUCT_INTEL_SANDYBRIDGE_M_IGD, 600,2 },
{ PCI_PRODUCT_INTEL_SANDYBRIDGE_M_IGD_1, 600,2 },
{ PCI_PRODUCT_INTEL_SANDYBRIDGE_M_IGD_2, 600,2 },
/* sandy bridge s */
{ PCI_PRODUCT_INTEL_SANDYBRIDGE_S_IGD, 600,2 },
/* ivy bridge */
{ PCI_PRODUCT_INTEL_IVYBRIDGE_IGD, 700,3 },
{ PCI_PRODUCT_INTEL_IVYBRIDGE_IGD_1, 700,3 },
/* ivy bridge m */
{ PCI_PRODUCT_INTEL_IVYBRIDGE_M_IGD, 700,3 },
{ PCI_PRODUCT_INTEL_IVYBRIDGE_M_IGD_1, 700,3 },
/* ivy bridge s */
{ PCI_PRODUCT_INTEL_IVYBRIDGE_S_IGD, 700,3 },
{ PCI_PRODUCT_INTEL_IVYBRIDGE_S_IGD_1, 700,3 },
#if 0
/* valleyview d */
/* valleyview m */
{ PCI_PRODUCT_INTEL_HASWELL_IGD_1, 800,3 },
/* haswell d */
{ PCI_PRODUCT_INTEL_HASWELL_IGD, 800,3 },
{ PCI_PRODUCT_INTEL_HASWELL_IGD_1, 800,3 },
/* haswell m */
/* broadwell d */
/* broadwell m */
#endif
};
static int
igma_newpch_match(const struct pci_attach_args *pa)
{
if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL)
return 0;
switch (0xff00 & PCI_PRODUCT(pa->pa_id)) {
case 0x3b00: /* ibex peak */
case 0x1c00: /* cougar point */
case 0x1e00: /* panther point */
case 0x8c00: /* lynx point */
case 0x9c00: /* lynx point lp */
return 1;
}
return 0;
}
static const struct igma_product *
igma_lookup(const struct pci_attach_args *pa)
{
const struct igma_product *ip;
int i;
if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL)
return NULL;
for (i=0; i < __arraycount(igma_products); ++i) {
ip = &igma_products[i];
if (PCI_PRODUCT(pa->pa_id) == ip->product)
return ip;
}
return NULL;
}
static void
igma_product_to_chip(const struct pci_attach_args *pa, struct igma_chip *cd)
{
const struct igma_product *ip;
struct pci_attach_args PA;
ip = igma_lookup(pa);
KASSERT(ip != NULL);
cd->ops = &igma_bus_ops;
cd->num_gmbus = 6;
cd->num_pipes = ip->num_pipes;
cd->quirks = 0;
cd->backlight_factor = 1;
cd->gpio_offset = OLD_GPIOA;
cd->vga_cntrl = PCH_VGA_CNTRL;
cd->backlight_cntrl = OLD_BLC_PWM_CTL;
cd->backlight_cntrl2 = OLD_BLC_PWM_CTL2;
PA = *pa;
if (pci_find_device(&PA, igma_newpch_match)) {
cd->gpio_offset = PCH_GPIOA;
cd->vga_cntrl = CPU_VGA_CNTRL;
cd->backlight_cntrl = CPU_BLC_PWM_CTL;
cd->backlight_cntrl2 = CPU_BLC_PWM_CTL2;
}
switch (ip->gentype) {
case 200:
cd->backlight_factor = 2;
break;
case 300:
case 350:
cd->backlight_factor = 2;
cd->quirks |= IGMA_PFITDISABLE_QUIRK;
break;
case 450:
cd->pri_cntrl = PRI_CTRL_NOTRICKLE;
cd->quirks |= IGMA_PLANESTART_QUIRK;
break;
default:
cd->pri_cntrl = 0;
break;
}
}
static void
igma_adjust_chip(struct igma_softc *sc, struct igma_chip *cd)
{
const struct igma_chip_ops *co = cd->ops;
u_int32_t reg;
reg = co->read_reg(cd, cd->vga_cntrl);
if (reg & VGA_PIPE_B_SELECT)
cd->use_pipe = 1;
}
static int
igma_print(void *aux, const char *pnp)
{
if (pnp)
aprint_normal("drm at %s", pnp);
return (UNCONF);
}
static int
igma_match(device_t parent, cfdata_t match, void *aux)
{
struct pci_attach_args *pa = (struct pci_attach_args *)aux;
const struct igma_product *ip;
if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY)
return 0;
ip = igma_lookup(pa);
if (ip != NULL)
return 100;
return 0;
}
static void
igma_attach(device_t parent, device_t self, void *aux)
{
struct igma_softc *sc = device_private(self);
const struct pci_attach_args *pa = (struct pci_attach_args *)aux;
struct igma_attach_args iaa;
bus_space_tag_t gttmmt, gmt, regt;
bus_space_handle_t gttmmh, gmh, regh;
bus_addr_t gttmmb, gmb;
pci_aprint_devinfo(pa, NULL);
sc->sc_dev = self;
/* Initialize according to chip type */
igma_product_to_chip(pa, &sc->sc_chip);
if (pci_mapreg_map(pa, PCI_BAR0, PCI_MAPREG_TYPE_MEM,
BUS_SPACE_MAP_LINEAR,
&gttmmt, &gttmmh, &gttmmb, NULL)) {
aprint_error_dev(sc->sc_dev, "unable to map GTTMM\n");
return;
}
sc->sc_chip.mmiot = gttmmt;
if (bus_space_subregion(gttmmt, gttmmh, 0, 2*1024*1024,
&sc->sc_chip.mmioh)) {
aprint_error_dev(sc->sc_dev, "unable to submap MMIO\n");
return;
}
sc->sc_chip.gttt = gttmmt;
if (bus_space_subregion(gttmmt, gttmmh, 2*1024*1024, 2*1024*1024,
&sc->sc_chip.gtth)) {
aprint_error_dev(sc->sc_dev, "unable to submap GTT\n");
return;
}
if (pci_mapreg_map(pa, PCI_BAR2, PCI_MAPREG_TYPE_MEM,
BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE,
&gmt, &gmh, &gmb, NULL)) {
aprint_error_dev(sc->sc_dev, "unable to map aperture\n");
return;
}
sc->sc_chip.gmt = gmt;
sc->sc_chip.gmh = gmh;
sc->sc_chip.gmb = gmb;
if (pci_mapreg_map(pa, PCI_BAR4, PCI_MAPREG_TYPE_IO, 0,
&regt, &regh, NULL, NULL)) {
aprint_error_dev(sc->sc_dev, "unable to map IO registers\n");
return;
}
#if NVGA > 0
iaa.iaa_console = vga_cndetach() ? true : false;
#else
iaa.iaa_console = 0;
#endif
sc->sc_chip.vgat = regt;
if (bus_space_map(regt, 0x3c0, 0x10, 0, &sc->sc_chip.vgah)) {
aprint_error_dev(sc->sc_dev, "unable to map VGA registers\n");
return;
}
/* Check hardware for more information */
igma_adjust_chip(sc, &sc->sc_chip);
aprint_normal("%s: VGA_CNTRL: 0x%x\n",device_xname(sc->sc_dev),
sc->sc_chip.vga_cntrl);
aprint_normal("%s: GPIO_OFFSET: 0x%x\n",device_xname(sc->sc_dev),
sc->sc_chip.gpio_offset);
aprint_normal("%s: BACKLIGHT_CTRL: 0x%x\n",device_xname(sc->sc_dev),
sc->sc_chip.backlight_cntrl);
aprint_normal("%s: BACKLIGHT_CTRL2: 0x%x\n",device_xname(sc->sc_dev),
sc->sc_chip.backlight_cntrl2);
#if NIGMAFB > 0
strcpy(iaa.iaa_name, "igmafb");
iaa.iaa_chip = sc->sc_chip;
config_found_ia(sc->sc_dev, "igmabus", &iaa, igma_print);
#endif
igma_i2c_attach(sc);
}
static void
igma_i2c_attach(struct igma_softc *sc)
{
struct igma_i2c *ii;
int i;
#if 0
struct i2cbus_attach_args iba;
#endif
for (i=0; i<sc->sc_chip.num_gmbus; ++i) {
ii = &sc->sc_ii[i];
ii->ii_sc = sc;
/* XXX */
ii->ii_reg = sc->sc_chip.gpio_offset - PCH_GPIOA;
switch (i) {
case 0:
ii->ii_reg += PCH_GPIOB;
ii->ii_name = "ssc";
break;
case 1:
ii->ii_reg += PCH_GPIOA;
ii->ii_name = "vga";
break;
case 2:
ii->ii_reg += PCH_GPIOC;
ii->ii_name = "panel";
break;
case 3:
ii->ii_reg += PCH_GPIOD;
ii->ii_name = "dpc";
break;
case 4:
ii->ii_reg += PCH_GPIOE;
ii->ii_name = "dpb";
break;
case 5:
ii->ii_reg += PCH_GPIOF;
ii->ii_name = "dpd";
break;
default:
panic("don't know GMBUS %d\n",i);
}
mutex_init(&ii->ii_lock, MUTEX_DEFAULT, IPL_NONE);
ii->ii_i2c.ic_cookie = ii;
ii->ii_i2c.ic_acquire_bus = igma_i2c_acquire_bus;
ii->ii_i2c.ic_release_bus = igma_i2c_release_bus;
ii->ii_i2c.ic_send_start = igma_i2c_send_start;
ii->ii_i2c.ic_send_stop = igma_i2c_send_stop;
ii->ii_i2c.ic_initiate_xfer = igma_i2c_initiate_xfer;
ii->ii_i2c.ic_read_byte = igma_i2c_read_byte;
ii->ii_i2c.ic_write_byte = igma_i2c_write_byte;
ii->ii_i2c.ic_exec = NULL;
#if 0
memset(&iba, 0, sizeof(iba));
iba.iba_type = I2C_TYPE_SMBUS;
iba.iba_tag = &ii->ii_i2c;
config_found_ia(sc->sc_dev, "i2cbus", &iba, iicbus_print);
#endif
}
}
/*
* I2C interface
*/
static int
igma_i2c_acquire_bus(void *cookie, int flags)
{
struct igma_i2c *ii = cookie;
mutex_enter(&ii->ii_lock);
return 0;
}
static void
igma_i2c_release_bus(void *cookie, int flags)
{
struct igma_i2c *ii = cookie;
mutex_exit(&ii->ii_lock);
}
static int
igma_i2c_send_start(void *cookie, int flags)
{
return i2c_bitbang_send_start(cookie, flags, &igma_i2cbb_ops);
}
static int
igma_i2c_send_stop(void *cookie, int flags)
{
return i2c_bitbang_send_stop(cookie, flags, &igma_i2cbb_ops);
}
static int
igma_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
{
return i2c_bitbang_initiate_xfer(cookie, addr, flags, &igma_i2cbb_ops);
}
static int
igma_i2c_read_byte(void *cookie, uint8_t *valp, int flags)
{
return i2c_bitbang_read_byte(cookie, valp, flags, &igma_i2cbb_ops);
}
static int
igma_i2c_write_byte(void *cookie, uint8_t val, int flags)
{
return i2c_bitbang_write_byte(cookie, val, flags, &igma_i2cbb_ops);
}
static void
igma_i2cbb_set_bits(void *cookie, uint32_t bits)
{
struct igma_i2c *ii = cookie;
struct igma_softc *sc = ii->ii_sc;
const struct igma_chip *cd = &sc->sc_chip;
const struct igma_chip_ops *co = cd->ops;
uint32_t reg;
reg = co->read_reg(cd, ii->ii_reg);
reg &= GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE;
if ((bits | ii->ii_dir) & 1)
/* make data input, signal is pulled high */
reg |= GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK;
else
/* make data output, signal is driven low */
reg |= GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK
| GPIO_DATA_VAL_MASK;
if (bits & 2)
/* make clock input, signal is pulled high */
reg |= GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
else
/* make clock output, signal is driven low */
reg |= GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK
| GPIO_CLOCK_VAL_MASK;
co->write_reg(cd, ii->ii_reg, reg);
#if 1
reg = co->read_reg(cd, ii->ii_reg);
#else
co->barrier(cd, ii->ii_reg);
#endif
}
static void
igma_i2cbb_set_dir(void *cookie, uint32_t bits)
{
struct igma_i2c *ii = cookie;
ii->ii_dir = bits;
}
static uint32_t
igma_i2cbb_read(void *cookie)
{
struct igma_i2c *ii = cookie;
struct igma_softc *sc = ii->ii_sc;
const struct igma_chip *cd = &sc->sc_chip;
const struct igma_chip_ops *co = cd->ops;
uint32_t reg;
int sda, scl;
reg = co->read_reg(cd, ii->ii_reg);
sda = reg & GPIO_DATA_VAL_IN;
scl = reg & GPIO_CLOCK_VAL_IN;
reg = (sda ? 1 : 0) | (scl ? 2 : 0);
return reg;
}
static void
igma_reg_barrier(const struct igma_chip *cd, int r)
{
bus_space_barrier(cd->mmiot, cd->mmioh, r, sizeof(u_int32_t),
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
}
static u_int32_t
igma_reg_read(const struct igma_chip *cd, int r)
{
return bus_space_read_4(cd->mmiot, cd->mmioh, r);
}
static void
igma_reg_write(const struct igma_chip *cd, int r, u_int32_t v)
{
bus_space_write_4(cd->mmiot, cd->mmioh, r, v);
}
static u_int8_t
igma_vga_read(const struct igma_chip *cd, int r)
{
bus_space_write_1(cd->vgat, cd->vgah, 0x4, r | 0x20);
return bus_space_read_1(cd->vgat, cd->vgah, 0x5);
}
static void
igma_vga_write(const struct igma_chip *cd, int r, u_int8_t v)
{
bus_space_write_1(cd->vgat, cd->vgah, 0x4, r | 0x20);
bus_space_write_1(cd->vgat, cd->vgah, 0x5, v);
}
#if 0
static u_int8_t
igma_crtc_read(const struct igma_chip *cd, int r)
{
bus_space_write_1(cd->crtct, cd->crtch, 0x4, r);
return bus_space_read_1(cd->crtct, cd->crtch, 0x5);
}
static void
igma_crtc_write(const struct igma_chip *cd, int r, u_int8_t v)
{
bus_space_write_1(cd->crtct, cd->crtch, 0x4, r);
bus_space_write_1(cd->crtct, cd->crtch, 0x5, v);
}
#endif