src/sys/dev/pci/glxpcib.c

827 lines
22 KiB
C

/* $OpenBSD: glxpcib.c,v 1.16 2022/03/11 18:00:45 mpi Exp $ */
/*
* Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
* Copyright (c) 2007 Michael Shalayeff
* All rights reserved.
*
* 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 MIND, 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.
*/
/*
* AMD CS5536 series LPC bridge also containing timer, watchdog, and GPIO.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/gpio.h>
#include <sys/timetc.h>
#include <sys/rwlock.h>
#include <machine/bus.h>
#ifdef __i386__
#include <machine/cpufunc.h>
#endif
#include <dev/gpio/gpiovar.h>
#include <dev/i2c/i2cvar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/glxreg.h>
#include <dev/pci/glxvar.h>
#include "gpio.h"
#define AMD5536_REV GLCP_CHIP_REV_ID
#define AMD5536_REV_MASK 0xff
#define AMD5536_TMC PMC_LTMR
#define MSR_LBAR_ENABLE 0x100000000ULL
/* Multi-Functional General Purpose Timer */
#define MSR_LBAR_MFGPT DIVIL_LBAR_MFGPT
#define MSR_MFGPT_SIZE 0x40
#define MSR_MFGPT_ADDR_MASK 0xffc0
#define AMD5536_MFGPT0_CMP1 0x00000000
#define AMD5536_MFGPT0_CMP2 0x00000002
#define AMD5536_MFGPT0_CNT 0x00000004
#define AMD5536_MFGPT0_SETUP 0x00000006
#define AMD5536_MFGPT_DIV_MASK 0x000f /* div = 1 << mask */
#define AMD5536_MFGPT_CLKSEL 0x0010
#define AMD5536_MFGPT_REV_EN 0x0020
#define AMD5536_MFGPT_CMP1DIS 0x0000
#define AMD5536_MFGPT_CMP1EQ 0x0040
#define AMD5536_MFGPT_CMP1GE 0x0080
#define AMD5536_MFGPT_CMP1EV 0x00c0
#define AMD5536_MFGPT_CMP2DIS 0x0000
#define AMD5536_MFGPT_CMP2EQ 0x0100
#define AMD5536_MFGPT_CMP2GE 0x0200
#define AMD5536_MFGPT_CMP2EV 0x0300
#define AMD5536_MFGPT_STOP_EN 0x0800
#define AMD5536_MFGPT_SET 0x1000
#define AMD5536_MFGPT_CMP1 0x2000
#define AMD5536_MFGPT_CMP2 0x4000
#define AMD5536_MFGPT_CNT_EN 0x8000
#define AMD5536_MFGPT_IRQ MFGPT_IRQ
#define AMD5536_MFGPT0_C1_IRQM 0x00000001
#define AMD5536_MFGPT1_C1_IRQM 0x00000002
#define AMD5536_MFGPT2_C1_IRQM 0x00000004
#define AMD5536_MFGPT3_C1_IRQM 0x00000008
#define AMD5536_MFGPT4_C1_IRQM 0x00000010
#define AMD5536_MFGPT5_C1_IRQM 0x00000020
#define AMD5536_MFGPT6_C1_IRQM 0x00000040
#define AMD5536_MFGPT7_C1_IRQM 0x00000080
#define AMD5536_MFGPT0_C2_IRQM 0x00000100
#define AMD5536_MFGPT1_C2_IRQM 0x00000200
#define AMD5536_MFGPT2_C2_IRQM 0x00000400
#define AMD5536_MFGPT3_C2_IRQM 0x00000800
#define AMD5536_MFGPT4_C2_IRQM 0x00001000
#define AMD5536_MFGPT5_C2_IRQM 0x00002000
#define AMD5536_MFGPT6_C2_IRQM 0x00004000
#define AMD5536_MFGPT7_C2_IRQM 0x00008000
#define AMD5536_MFGPT_NR MFGPT_NR
#define AMD5536_MFGPT0_C1_NMIM 0x00000001
#define AMD5536_MFGPT1_C1_NMIM 0x00000002
#define AMD5536_MFGPT2_C1_NMIM 0x00000004
#define AMD5536_MFGPT3_C1_NMIM 0x00000008
#define AMD5536_MFGPT4_C1_NMIM 0x00000010
#define AMD5536_MFGPT5_C1_NMIM 0x00000020
#define AMD5536_MFGPT6_C1_NMIM 0x00000040
#define AMD5536_MFGPT7_C1_NMIM 0x00000080
#define AMD5536_MFGPT0_C2_NMIM 0x00000100
#define AMD5536_MFGPT1_C2_NMIM 0x00000200
#define AMD5536_MFGPT2_C2_NMIM 0x00000400
#define AMD5536_MFGPT3_C2_NMIM 0x00000800
#define AMD5536_MFGPT4_C2_NMIM 0x00001000
#define AMD5536_MFGPT5_C2_NMIM 0x00002000
#define AMD5536_MFGPT6_C2_NMIM 0x00004000
#define AMD5536_MFGPT7_C2_NMIM 0x00008000
#define AMD5536_NMI_LEG 0x00010000
#define AMD5536_MFGPT0_C2_RSTEN 0x01000000
#define AMD5536_MFGPT1_C2_RSTEN 0x02000000
#define AMD5536_MFGPT2_C2_RSTEN 0x04000000
#define AMD5536_MFGPT3_C2_RSTEN 0x08000000
#define AMD5536_MFGPT4_C2_RSTEN 0x10000000
#define AMD5536_MFGPT5_C2_RSTEN 0x20000000
#define AMD5536_MFGPT_SETUP MFGPT_SETUP
/* GPIO */
#define MSR_LBAR_GPIO DIVIL_LBAR_GPIO
#define MSR_GPIO_SIZE 0x100
#define MSR_GPIO_ADDR_MASK 0xff00
#define AMD5536_GPIO_NPINS 32
#define AMD5536_GPIOH_OFFSET 0x80 /* high bank register offset */
#define AMD5536_GPIO_OUT_VAL 0x00 /* output value */
#define AMD5536_GPIO_OUT_EN 0x04 /* output enable */
#define AMD5536_GPIO_OD_EN 0x08 /* open-drain enable */
#define AMD5536_GPIO_OUT_INVRT_EN 0x0c /* invert output */
#define AMD5536_GPIO_PU_EN 0x18 /* pull-up enable */
#define AMD5536_GPIO_PD_EN 0x1c /* pull-down enable */
#define AMD5536_GPIO_IN_EN 0x20 /* input enable */
#define AMD5536_GPIO_IN_INVRT_EN 0x24 /* invert input */
#define AMD5536_GPIO_READ_BACK 0x30 /* read back value */
/* SMB */
#define MSR_LBAR_SMB DIVIL_LBAR_SMB
#define MSR_SMB_SIZE 0x08
#define MSR_SMB_ADDR_MASK 0xfff8
#define AMD5536_SMB_SDA 0x00 /* serial data */
#define AMD5536_SMB_STS 0x01 /* status */
#define AMD5536_SMB_STS_SLVSTOP 0x80 /* slave stop */
#define AMD5536_SMB_STS_SDAST 0x40 /* smb data status */
#define AMD5536_SMB_STS_BER 0x20 /* bus error */
#define AMD5536_SMB_STS_NEGACK 0x10 /* negative acknowledge */
#define AMD5536_SMB_STS_STASTR 0x08 /* stall after start */
#define AMD5536_SMB_STS_MASTER 0x02 /* master */
#define AMD5536_SMB_STS_XMIT 0x01 /* transmit or receive */
#define AMD5536_SMB_CST 0x02 /* control status */
#define AMD5536_SMB_CST_MATCH 0x04 /* address match */
#define AMD5536_SMB_CST_BB 0x02 /* bus busy */
#define AMD5536_SMB_CST_BUSY 0x01 /* busy */
#define AMD5536_SMB_CTL1 0x03 /* control 1 */
#define AMD5536_SMB_CTL1_STASTRE 0x80 /* stall after start enable */
#define AMD5536_SMB_CTL1_ACK 0x10 /* receive acknowledge */
#define AMD5536_SMB_CTL1_INTEN 0x04 /* interrupt enable */
#define AMD5536_SMB_CTL1_STOP 0x02 /* stop */
#define AMD5536_SMB_CTL1_START 0x01 /* start */
#define AMD5536_SMB_ADDR 0x04 /* serial address */
#define AMD5536_SMB_ADDR_SAEN 0x80 /* slave enable */
#define AMD5536_SMB_CTL2 0x05 /* control 2 */
#define AMD5536_SMB_CTL2_EN 0x01 /* enable clock */
#define AMD5536_SMB_CTL2_FREQ 0x78 /* 100 kHz */
#define AMD5536_SMB_CTL3 0x06 /* control 3 */
/* PMS */
#define MSR_LBAR_PMS DIVIL_LBAR_PMS
#define MSR_PMS_SIZE 0x80
#define MSR_PMS_ADDR_MASK 0xff80
#define AMD5536_PMS_SSC 0x54
#define AMD5536_PMS_SSC_PI 0x00040000
#define AMD5536_PMS_SSC_CLR_PI 0x00020000
#define AMD5536_PMS_SSC_SET_PI 0x00010000
/*
* MSR registers we want to preserve across suspend/resume
*/
const uint32_t glxpcib_msrlist[] = {
GLIU_PAE,
GLCP_GLD_MSR_PM,
DIVIL_BALL_OPTS
};
struct glxpcib_softc {
struct device sc_dev;
struct timecounter sc_timecounter;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
uint64_t sc_msrsave[nitems(glxpcib_msrlist)];
#ifndef SMALL_KERNEL
#if NGPIO > 0
/* GPIO interface */
bus_space_tag_t sc_gpio_iot;
bus_space_handle_t sc_gpio_ioh;
struct gpio_chipset_tag sc_gpio_gc;
gpio_pin_t sc_gpio_pins[AMD5536_GPIO_NPINS];
#endif
/* I2C interface */
bus_space_tag_t sc_smb_iot;
bus_space_handle_t sc_smb_ioh;
struct i2c_controller sc_smb_ic;
struct rwlock sc_smb_lck;
/* Watchdog */
int sc_wdog;
int sc_wdog_period;
#endif
};
struct cfdriver glxpcib_cd = {
NULL, "glxpcib", DV_DULL
};
int glxpcib_match(struct device *, void *, void *);
void glxpcib_attach(struct device *, struct device *, void *);
int glxpcib_activate(struct device *, int);
int glxpcib_search(struct device *, void *, void *);
int glxpcib_print(void *, const char *);
const struct cfattach glxpcib_ca = {
sizeof(struct glxpcib_softc), glxpcib_match, glxpcib_attach,
NULL, glxpcib_activate
};
/* from arch/<*>/pci/pcib.c */
void pcibattach(struct device *parent, struct device *self, void *aux);
u_int glxpcib_get_timecount(struct timecounter *tc);
#ifndef SMALL_KERNEL
int glxpcib_wdogctl_cb(void *, int);
#if NGPIO > 0
void glxpcib_gpio_pin_ctl(void *, int, int);
int glxpcib_gpio_pin_read(void *, int);
void glxpcib_gpio_pin_write(void *, int, int);
#endif
int glxpcib_smb_acquire_bus(void *, int);
void glxpcib_smb_release_bus(void *, int);
int glxpcib_smb_send_start(void *, int);
int glxpcib_smb_send_stop(void *, int);
void glxpcib_smb_send_ack(void *, int);
int glxpcib_smb_initiate_xfer(void *, i2c_addr_t, int);
int glxpcib_smb_read_byte(void *, uint8_t *, int);
int glxpcib_smb_write_byte(void *, uint8_t, int);
void glxpcib_smb_reset(struct glxpcib_softc *);
int glxpcib_smb_wait(struct glxpcib_softc *, int, int);
#endif
const struct pci_matchid glxpcib_devices[] = {
{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_CS5536_PCIB }
};
int
glxpcib_match(struct device *parent, void *match, void *aux)
{
if (pci_matchbyid((struct pci_attach_args *)aux, glxpcib_devices,
nitems(glxpcib_devices))) {
/* needs to win over pcib */
return 2;
}
return 0;
}
void
glxpcib_attach(struct device *parent, struct device *self, void *aux)
{
struct glxpcib_softc *sc = (struct glxpcib_softc *)self;
struct timecounter *tc = &sc->sc_timecounter;
#ifndef SMALL_KERNEL
struct pci_attach_args *pa = (struct pci_attach_args *)aux;
u_int64_t wa;
#if NGPIO > 0
u_int64_t ga;
struct gpiobus_attach_args gba;
int i, gpio = 0;
#endif
u_int64_t sa;
struct i2cbus_attach_args iba;
int i2c = 0;
bus_space_handle_t tmpioh;
#endif
tc->tc_get_timecount = glxpcib_get_timecount;
tc->tc_counter_mask = 0xffffffff;
tc->tc_frequency = 3579545;
tc->tc_name = "CS5536";
tc->tc_quality = 1000;
tc->tc_priv = sc;
tc_init(tc);
printf(": rev %d, 32-bit %lluHz timer",
(int)rdmsr(AMD5536_REV) & AMD5536_REV_MASK,
tc->tc_frequency);
#ifndef SMALL_KERNEL
/* Attach the watchdog timer */
sc->sc_iot = pa->pa_iot;
wa = rdmsr(MSR_LBAR_MFGPT);
if (wa & MSR_LBAR_ENABLE &&
!bus_space_map(sc->sc_iot, wa & MSR_MFGPT_ADDR_MASK,
MSR_MFGPT_SIZE, 0, &sc->sc_ioh)) {
/* count in seconds (as upper level desires) */
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_SETUP,
AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2EV |
AMD5536_MFGPT_CMP2 | AMD5536_MFGPT_DIV_MASK |
AMD5536_MFGPT_STOP_EN);
wdog_register(glxpcib_wdogctl_cb, sc);
sc->sc_wdog = 1;
printf(", watchdog");
}
#if NGPIO > 0
/* map GPIO I/O space */
sc->sc_gpio_iot = pa->pa_iot;
ga = rdmsr(MSR_LBAR_GPIO);
if (ga & MSR_LBAR_ENABLE &&
!bus_space_map(sc->sc_gpio_iot, ga & MSR_GPIO_ADDR_MASK,
MSR_GPIO_SIZE, 0, &sc->sc_gpio_ioh)) {
printf(", gpio");
/* initialize pin array */
for (i = 0; i < AMD5536_GPIO_NPINS; i++) {
sc->sc_gpio_pins[i].pin_num = i;
sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN |
GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN |
GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
/* read initial state */
sc->sc_gpio_pins[i].pin_state =
glxpcib_gpio_pin_read(sc, i);
}
/* create controller tag */
sc->sc_gpio_gc.gp_cookie = sc;
sc->sc_gpio_gc.gp_pin_read = glxpcib_gpio_pin_read;
sc->sc_gpio_gc.gp_pin_write = glxpcib_gpio_pin_write;
sc->sc_gpio_gc.gp_pin_ctl = glxpcib_gpio_pin_ctl;
gba.gba_name = "gpio";
gba.gba_gc = &sc->sc_gpio_gc;
gba.gba_pins = sc->sc_gpio_pins;
gba.gba_npins = AMD5536_GPIO_NPINS;
gpio = 1;
}
#endif /* NGPIO */
/* Map SMB I/O space */
sc->sc_smb_iot = pa->pa_iot;
sa = rdmsr(MSR_LBAR_SMB);
if (sa & MSR_LBAR_ENABLE &&
!bus_space_map(sc->sc_smb_iot, sa & MSR_SMB_ADDR_MASK,
MSR_SMB_SIZE, 0, &sc->sc_smb_ioh)) {
printf(", i2c");
/* Enable controller */
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
AMD5536_SMB_CTL2, AMD5536_SMB_CTL2_EN |
AMD5536_SMB_CTL2_FREQ);
/* Disable interrupts */
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
AMD5536_SMB_CTL1, 0);
/* Disable slave address */
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
AMD5536_SMB_ADDR, 0);
/* Stall the bus after start */
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
AMD5536_SMB_CTL1, AMD5536_SMB_CTL1_STASTRE);
/* Attach I2C framework */
sc->sc_smb_ic.ic_cookie = sc;
sc->sc_smb_ic.ic_acquire_bus = glxpcib_smb_acquire_bus;
sc->sc_smb_ic.ic_release_bus = glxpcib_smb_release_bus;
sc->sc_smb_ic.ic_send_start = glxpcib_smb_send_start;
sc->sc_smb_ic.ic_send_stop = glxpcib_smb_send_stop;
sc->sc_smb_ic.ic_initiate_xfer = glxpcib_smb_initiate_xfer;
sc->sc_smb_ic.ic_read_byte = glxpcib_smb_read_byte;
sc->sc_smb_ic.ic_write_byte = glxpcib_smb_write_byte;
rw_init(&sc->sc_smb_lck, "iiclk");
bzero(&iba, sizeof(iba));
iba.iba_name = "iic";
iba.iba_tag = &sc->sc_smb_ic;
i2c = 1;
}
/* Map PMS I/O space and enable the ``Power Immediate'' feature */
sa = rdmsr(MSR_LBAR_PMS);
if (sa & MSR_LBAR_ENABLE &&
!bus_space_map(pa->pa_iot, sa & MSR_PMS_ADDR_MASK,
MSR_PMS_SIZE, 0, &tmpioh)) {
bus_space_write_4(pa->pa_iot, tmpioh, AMD5536_PMS_SSC,
AMD5536_PMS_SSC_SET_PI);
bus_space_barrier(pa->pa_iot, tmpioh, AMD5536_PMS_SSC, 4,
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
bus_space_unmap(pa->pa_iot, tmpioh, MSR_PMS_SIZE);
}
#endif /* SMALL_KERNEL */
pcibattach(parent, self, aux);
#ifndef SMALL_KERNEL
#if NGPIO > 0
if (gpio)
config_found(&sc->sc_dev, &gba, gpiobus_print);
#endif
if (i2c)
config_found(&sc->sc_dev, &iba, iicbus_print);
config_search(glxpcib_search, self, pa);
#endif
}
int
glxpcib_activate(struct device *self, int act)
{
#ifndef SMALL_KERNEL
struct glxpcib_softc *sc = (struct glxpcib_softc *)self;
uint i;
#endif
int rv = 0;
switch (act) {
case DVACT_SUSPEND:
#ifndef SMALL_KERNEL
if (sc->sc_wdog) {
sc->sc_wdog_period = bus_space_read_2(sc->sc_iot,
sc->sc_ioh, AMD5536_MFGPT0_CMP2);
glxpcib_wdogctl_cb(sc, 0);
}
#endif
rv = config_activate_children(self, act);
#ifndef SMALL_KERNEL
for (i = 0; i < nitems(glxpcib_msrlist); i++)
sc->sc_msrsave[i] = rdmsr(glxpcib_msrlist[i]);
#endif
break;
case DVACT_RESUME:
#ifndef SMALL_KERNEL
if (sc->sc_wdog)
glxpcib_wdogctl_cb(sc, sc->sc_wdog_period);
for (i = 0; i < nitems(glxpcib_msrlist); i++)
wrmsr(glxpcib_msrlist[i], sc->sc_msrsave[i]);
#endif
rv = config_activate_children(self, act);
break;
case DVACT_POWERDOWN:
#ifndef SMALL_KERNEL
if (sc->sc_wdog)
wdog_shutdown(self);
#endif
rv = config_activate_children(self, act);
break;
default:
rv = config_activate_children(self, act);
break;
}
return (rv);
}
u_int
glxpcib_get_timecount(struct timecounter *tc)
{
return rdmsr(AMD5536_TMC);
}
#ifndef SMALL_KERNEL
int
glxpcib_wdogctl_cb(void *v, int period)
{
struct glxpcib_softc *sc = v;
if (period > 0xffff)
period = 0xffff;
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_SETUP,
AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2);
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_CNT, 0);
bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_CMP2, period);
if (period)
wrmsr(AMD5536_MFGPT_NR,
rdmsr(AMD5536_MFGPT_NR) | AMD5536_MFGPT0_C2_RSTEN);
else
wrmsr(AMD5536_MFGPT_NR,
rdmsr(AMD5536_MFGPT_NR) & ~AMD5536_MFGPT0_C2_RSTEN);
return period;
}
#if NGPIO > 0
int
glxpcib_gpio_pin_read(void *arg, int pin)
{
struct glxpcib_softc *sc = arg;
u_int32_t data;
int reg, off = 0;
reg = AMD5536_GPIO_IN_EN;
if (pin > 15) {
pin &= 0x0f;
off = AMD5536_GPIOH_OFFSET;
}
reg += off;
data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
if (data & (1 << pin))
reg = AMD5536_GPIO_READ_BACK + off;
else
reg = AMD5536_GPIO_OUT_VAL + off;
data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
return data & 1 << pin ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
}
void
glxpcib_gpio_pin_write(void *arg, int pin, int value)
{
struct glxpcib_softc *sc = arg;
u_int32_t data;
int reg;
reg = AMD5536_GPIO_OUT_VAL;
if (pin > 15) {
pin &= 0x0f;
reg += AMD5536_GPIOH_OFFSET;
}
if (value == 1)
data = 1 << pin;
else
data = 1 << (pin + 16);
bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
}
void
glxpcib_gpio_pin_ctl(void *arg, int pin, int flags)
{
struct glxpcib_softc *sc = arg;
int n, reg[7], val[7], nreg = 0, off = 0;
if (pin > 15) {
pin &= 0x0f;
off = AMD5536_GPIOH_OFFSET;
}
reg[nreg] = AMD5536_GPIO_IN_EN + off;
if (flags & GPIO_PIN_INPUT)
val[nreg++] = 1 << pin;
else
val[nreg++] = 1 << (pin + 16);
reg[nreg] = AMD5536_GPIO_OUT_EN + off;
if (flags & GPIO_PIN_OUTPUT)
val[nreg++] = 1 << pin;
else
val[nreg++] = 1 << (pin + 16);
reg[nreg] = AMD5536_GPIO_OD_EN + off;
if (flags & GPIO_PIN_OPENDRAIN)
val[nreg++] = 1 << pin;
else
val[nreg++] = 1 << (pin + 16);
reg[nreg] = AMD5536_GPIO_PU_EN + off;
if (flags & GPIO_PIN_PULLUP)
val[nreg++] = 1 << pin;
else
val[nreg++] = 1 << (pin + 16);
reg[nreg] = AMD5536_GPIO_PD_EN + off;
if (flags & GPIO_PIN_PULLDOWN)
val[nreg++] = 1 << pin;
else
val[nreg++] = 1 << (pin + 16);
reg[nreg] = AMD5536_GPIO_IN_INVRT_EN + off;
if (flags & GPIO_PIN_INVIN)
val[nreg++] = 1 << pin;
else
val[nreg++] = 1 << (pin + 16);
reg[nreg] = AMD5536_GPIO_OUT_INVRT_EN + off;
if (flags & GPIO_PIN_INVOUT)
val[nreg++] = 1 << pin;
else
val[nreg++] = 1 << (pin + 16);
/* set flags */
for (n = 0; n < nreg; n++)
bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg[n],
val[n]);
}
#endif /* GPIO */
int
glxpcib_smb_acquire_bus(void *arg, int flags)
{
struct glxpcib_softc *sc = arg;
if (cold || flags & I2C_F_POLL)
return (0);
return (rw_enter(&sc->sc_smb_lck, RW_WRITE | RW_INTR));
}
void
glxpcib_smb_release_bus(void *arg, int flags)
{
struct glxpcib_softc *sc = arg;
if (cold || flags & I2C_F_POLL)
return;
rw_exit(&sc->sc_smb_lck);
}
int
glxpcib_smb_send_start(void *arg, int flags)
{
struct glxpcib_softc *sc = arg;
u_int8_t ctl;
ctl = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
AMD5536_SMB_CTL1);
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_CTL1,
ctl | AMD5536_SMB_CTL1_START);
return (0);
}
int
glxpcib_smb_send_stop(void *arg, int flags)
{
struct glxpcib_softc *sc = arg;
u_int8_t ctl;
ctl = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
AMD5536_SMB_CTL1);
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_CTL1,
ctl | AMD5536_SMB_CTL1_STOP);
return (0);
}
void
glxpcib_smb_send_ack(void *arg, int flags)
{
struct glxpcib_softc *sc = arg;
u_int8_t ctl;
ctl = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
AMD5536_SMB_CTL1);
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_CTL1,
ctl | AMD5536_SMB_CTL1_ACK);
}
int
glxpcib_smb_initiate_xfer(void *arg, i2c_addr_t addr, int flags)
{
struct glxpcib_softc *sc = arg;
int error, dir;
/* Issue start condition */
glxpcib_smb_send_start(sc, flags);
/* Wait for bus mastership */
if ((error = glxpcib_smb_wait(sc, AMD5536_SMB_STS_MASTER |
AMD5536_SMB_STS_SDAST, flags)) != 0)
return (error);
/* Send address byte */
dir = (flags & I2C_F_READ ? 1 : 0);
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_SDA,
(addr << 1) | dir);
return (0);
}
int
glxpcib_smb_read_byte(void *arg, uint8_t *bytep, int flags)
{
struct glxpcib_softc *sc = arg;
int error;
/* Wait for the bus to be ready */
if ((error = glxpcib_smb_wait(sc, AMD5536_SMB_STS_SDAST, flags)))
return (error);
/* Acknowledge the last byte */
if (flags & I2C_F_LAST)
glxpcib_smb_send_ack(sc, 0);
/* Read data byte */
*bytep = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
AMD5536_SMB_SDA);
return (0);
}
int
glxpcib_print(void *args, const char *parentname)
{
struct glxpcib_attach_args *gaa = (struct glxpcib_attach_args *)args;
if (parentname != NULL)
printf("%s at %s", gaa->gaa_name, parentname);
return UNCONF;
}
int
glxpcib_search(struct device *parent, void *gcf, void *args)
{
struct glxpcib_softc *sc = (struct glxpcib_softc *)parent;
struct cfdata *cf = (struct cfdata *)gcf;
struct pci_attach_args *pa = (struct pci_attach_args *)args;
struct glxpcib_attach_args gaa;
gaa.gaa_name = cf->cf_driver->cd_name;
gaa.gaa_pa = pa;
gaa.gaa_iot = sc->sc_iot;
gaa.gaa_ioh = sc->sc_ioh;
/*
* These devices are attached directly, either from
* glxpcib_attach() or later in time from pcib_callback().
*/
if (strcmp(cf->cf_driver->cd_name, "gpio") == 0 ||
strcmp(cf->cf_driver->cd_name, "iic") == 0 ||
strcmp(cf->cf_driver->cd_name, "isa") == 0)
return 0;
if (cf->cf_attach->ca_match(parent, cf, &gaa) == 0)
return 0;
config_attach(parent, cf, &gaa, glxpcib_print);
return 1;
}
int
glxpcib_smb_write_byte(void *arg, uint8_t byte, int flags)
{
struct glxpcib_softc *sc = arg;
int error;
/* Wait for the bus to be ready */
if ((error = glxpcib_smb_wait(sc, AMD5536_SMB_STS_SDAST, flags)))
return (error);
/* Send stop after the last byte */
if (flags & I2C_F_STOP)
glxpcib_smb_send_stop(sc, 0);
/* Write data byte */
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_SDA,
byte);
return (0);
}
void
glxpcib_smb_reset(struct glxpcib_softc *sc)
{
u_int8_t st;
/* Clear MASTER, NEGACK and BER */
st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_STS);
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_STS, st |
AMD5536_SMB_STS_MASTER | AMD5536_SMB_STS_NEGACK |
AMD5536_SMB_STS_BER);
/* Disable and re-enable controller */
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_CTL2, 0);
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_CTL2,
AMD5536_SMB_CTL2_EN | AMD5536_SMB_CTL2_FREQ);
/* Send stop */
glxpcib_smb_send_stop(sc, 0);
}
int
glxpcib_smb_wait(struct glxpcib_softc *sc, int bits, int flags)
{
u_int8_t st;
int i;
for (i = 0; i < 100; i++) {
st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
AMD5536_SMB_STS);
if (st & AMD5536_SMB_STS_BER) {
printf("%s: bus error, bits=%#x st=%#x\n",
sc->sc_dev.dv_xname, bits, st);
glxpcib_smb_reset(sc);
return (EIO);
}
if ((bits & AMD5536_SMB_STS_MASTER) == 0 &&
(st & AMD5536_SMB_STS_NEGACK)) {
glxpcib_smb_reset(sc);
return (EIO);
}
if (st & AMD5536_SMB_STS_STASTR)
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
AMD5536_SMB_STS, AMD5536_SMB_STS_STASTR);
if ((st & bits) == bits)
break;
delay(2);
}
if ((st & bits) != bits) {
glxpcib_smb_reset(sc);
return (ETIMEDOUT);
}
return (0);
}
#endif /* SMALL_KERNEL */