File: [local] / sys / dev / pci / if_che.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:14:01 2008 UTC (16 years, 5 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: if_che.c,v 1.8 2007/05/30 05:11:53 reyk Exp $ */
/*
* Copyright (c) 2007 Claudio Jeker <claudio@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.
*/
#include "bpfilter.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <machine/bus.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
/* registers & defines */
#define CHE_PCI_BAR 0x10
#define CHE_PCI_CAP_ID_VPD 0x03
#define CHE_PCI_VPD_DATA 0x4
#define CHE_PCI_F_VPD_ADDR 0x80000000
#define CHE_PCI_VPD_BASE 0xc00
#define CHE_REG_T3DBG_GPIO_EN 0xd0
#define CHE_T3DBG_F_GPIO11_OEN 0x08000000
#define CHE_T3DBG_F_GPIO10_OEN 0x04000000
#define CHE_T3DBG_F_GPIO9_OEN 0x02000000
#define CHE_T3DBG_F_GPIO8_OEN 0x01000000
#define CHE_T3DBG_F_GPIO7_OEN 0x00800000
#define CHE_T3DBG_F_GPIO6_OEN 0x00400000
#define CHE_T3DBG_F_GPIO5_OEN 0x00200000
#define CHE_T3DBG_F_GPIO4_OEN 0x00100000
#define CHE_T3DBG_F_GPIO3_OEN 0x00080000
#define CHE_T3DBG_F_GPIO2_OEN 0x00040000
#define CHE_T3DBG_F_GPIO1_OEN 0x00020000
#define CHE_T3DBG_F_GPIO0_OEN 0x00010000
#define CHE_T3DBG_F_GPIO11_OUT_VAL 0x00000800
#define CHE_T3DBG_F_GPIO10_OUT_VAL 0x00000400
#define CHE_T3DBG_F_GPIO9_OUT_VAL 0x00000200
#define CHE_T3DBG_F_GPIO8_OUT_VAL 0x00000100
#define CHE_T3DBG_F_GPIO7_OUT_VAL 0x00000080
#define CHE_T3DBG_F_GPIO6_OUT_VAL 0x00000040
#define CHE_T3DBG_F_GPIO5_OUT_VAL 0x00000020
#define CHE_T3DBG_F_GPIO4_OUT_VAL 0x00000010
#define CHE_T3DBG_F_GPIO3_OUT_VAL 0x00000008
#define CHE_T3DBG_F_GPIO2_OUT_VAL 0x00000004
#define CHE_T3DBG_F_GPIO1_OUT_VAL 0x00000002
#define CHE_T3DBG_F_GPIO0_OUT_VAL 0x00000001
#define CHE_REG_I2C_CFG 0x6a0
#define CHE_I2C_CLKDIV(_x) ((_x) && 0xfff)
#define CHE_REG_MI1_CFG 0x6b0
#define CHE_REG_MI1_ADDR 0x6b4
#define CHE_REG_MI1_DATA 0x6b8
#define CHE_REG_MI1_OP 0x6bc
#define CHE_MI1_F_BUSY (1U << 31)
#define CHE_MI1_F_ST 0x8
#define CHE_MI1_F_PREEN 0x4
#define CHE_MI1_F_MDIINV 0x2
#define CHE_MI1_F_MDIEN 0x1
#define CHE_MI1_CLKDIV(_x) ((_x) << 5)
#define CHE_MI1_PHYADDR(_x) ((_x) << 5)
#define CHE_MI1_OP(_x) ((_x) & 0x3)
#define CHE_REG_PL_RST 0x6f0
#define CHE_RST_F_CRSTWRM 0x2
#define CHE_RST_F_CRSTWRMMODE 0x1
#define CHE_REG_PL_REV 0x6f4
#define CHE_REG_XGM_PORT_CFG 0x8b8
#define CHE_XGMAC0_0_BASE_ADDR 0x800
#define CHE_XGMAC0_1_BASE_ADDR 0xa00
#define CHE_XGM_REG(_r, _i) \
((_r) + (_i) * (CHE_XGMAC0_1_BASE_ADDR - CHE_XGMAC0_0_BASE_ADDR))
#define CHE_XGM_PORTSPEED(_x) ((_x) << 1)
#define CHE_XGM_F_ENRGMII 0x1
#define CHE_XGM_F_CLKDIVRESET 0x8
/* serial flash and firmware definitions */
#define CHE_REG_SF_DATA 0x6d8
#define CHE_REG_SF_OP 0x6dc
#define CHE_SF_SEC_SIZE (64 * 1024) /* serial flash sector size */
#define CHE_SF_SIZE (8 * CHE_SF_SEC_SIZE) /* serial flash size */
#define CHE_SF_PROG_PAGE 2
#define CHE_SF_WR_DISABLE 4
#define CHE_SF_RD_STATUS 5 /* read status register */
#define CHE_SF_WR_ENABLE 6
#define CHE_SF_RD_DATA 11
#define CHE_SF_SEC_ERASE 216
#define CHE_SF_F_BUSY (1U << 31)
#define CHE_SF_F_OP 0x1
#define CHE_SF_CONT(_x) ((_x) << 3)
#define CHE_SF_BYTECNT_MASK 0x3
#define CHE_SF_BYTECNT(_x) (((_x) & CHE_SF_BYTECNT_MASK) << 1)
#define FW_FLASH_BOOT_ADDR 0x70000 /* start address of FW in flash */
#define FW_VERS_ADDR 0x77ffc /* flash address holding FW version */
#define FW_VERS_TYPE_N3 0
#define FW_VERS_TYPE_T3 1
#define FW_VERS_TYPE(_x) (((_x) >> 28) & 0xf)
#define FW_VERS_MAJOR(_x) (((_x) >> 16) & 0xfff)
#define FW_VERS_MINOR(_x) (((_x) >> 8) & 0xff)
#define FW_VERS_MICRO(_x) ((_x) & 0xff)
/* Partial EEPROM Vital Product Data structure. */
struct che_vpd {
u_int8_t id_tag;
u_int8_t id_len[2];
u_int8_t id_data[16];
u_int8_t vpdr_tag;
u_int8_t vpdr_len[2];
u_int8_t pn_name[2]; /* part number */
u_int8_t pn_len;
u_int8_t pn_data[16];
u_int8_t ec_name[2]; /* EC level */
u_int8_t ec_len;
u_int8_t ec_data[16];
u_int8_t sn_name[2]; /* serial number */
u_int8_t sn_len;
u_int8_t sn_data[16];
u_int8_t na_name[2]; /* MAC address base */
u_int8_t na_len;
u_int8_t na_data[12];
u_int8_t cclk_name[2]; /* core clock */
u_int8_t cclk_len;
u_int8_t cclk_data[6];
u_int8_t mclk_name[2]; /* mem clock */
u_int8_t mclk_len;
u_int8_t mclk_data[6];
u_int8_t uclk_name[2]; /* uP clock */
u_int8_t uclk_len;
u_int8_t uclk_data[6];
u_int8_t mdc_name[2]; /* MDIO clock */
u_int8_t mdc_len;
u_int8_t mdc_data[6];
u_int8_t mt_name[2]; /* mem timing */
u_int8_t mt_len;
u_int8_t mt_data[2];
u_int8_t xaui0cfg_name[2]; /* XAUI0 config */
u_int8_t xaui0cfg_len;
u_int8_t xaui0cfg_data[6];
u_int8_t xaui1cfg_name[2]; /* XAUI1 config */
u_int8_t xaui1cfg_len;
u_int8_t xaui1cfg_data[6];
u_int8_t port0_name[2]; /* PHY0 */
u_int8_t port0_len;
u_int8_t port0_data[2];
u_int8_t port1_name[2]; /* PHY1 */
u_int8_t port1_len;
u_int8_t port1_data[2];
u_int8_t port2_name[2]; /* PHY2 */
u_int8_t port2_len;
u_int8_t port2_data[2];
u_int8_t port3_name[2]; /* PHY3 */
u_int8_t port3_len;
u_int8_t port3_data[2];
u_int8_t rv_name[2]; /* csum */
u_int8_t rv_len;
u_int8_t rv_data[1];
u_int8_t pad[4]; /* for multiple-of-4 sizing */
} __packed;
#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
/* the pci controller */
struct cheg_softc {
struct device sc_dev;
bus_dma_tag_t sc_dmat;
bus_space_tag_t sc_memt;
bus_space_handle_t sc_memh;
bus_size_t sc_mems;
u_int32_t sc_rev; /* card revision */
u_int32_t sc_cclk; /* core clock */
u_int32_t sc_mdc; /* mdio clock */
pci_vendor_id_t sc_product;
};
int cheg_match(struct device *, void *, void *);
void cheg_attach(struct device *, struct device *, void *);
int cheg_print(void *, const char *);
struct cfattach cheg_ca = {
sizeof(struct cheg_softc),
cheg_match,
cheg_attach
};
struct cfdriver cheg_cd = {
NULL, "cheg", DV_DULL
};
/* glue between the controller and the port */
struct che_attach_args {
struct pci_attach_args *caa_pa;
pci_intr_handle_t caa_ih;
int caa_port;
u_int8_t caa_lladdr[ETHER_ADDR_LEN];
};
/* che itself */
struct che_softc {
struct device sc_dev;
struct arpcom sc_ac;
struct mii_data sc_mii;
struct cheg_softc *sc_cheg;
void *sc_ih;
int sc_port;
};
int che_match(struct device *, void *, void *);
void che_attach(struct device *, struct device *, void *);
struct cfattach che_ca = {
sizeof(struct che_softc),
che_match,
che_attach
};
struct cfdriver che_cd = {
NULL, "che", DV_IFNET
};
int che_write_flash_reg(struct cheg_softc *, size_t, int,
u_int32_t);
int che_read_flash_reg(struct cheg_softc *, size_t, int,
u_int32_t *);
int che_read_flash_multi4(struct cheg_softc *, u_int, u_int32_t *,
size_t);
int che_read_eeprom(struct cheg_softc *, struct pci_attach_args *,
pcireg_t, pcireg_t *);
int che_get_vpd(struct cheg_softc *, struct pci_attach_args *,
void *, size_t);
void che_conv_lladdr(char *, u_int8_t *);
u_int32_t che_conv_num(char *, size_t);
void che_reset(struct cheg_softc *);
int che_ioctl(struct ifnet *, u_long, caddr_t);
void che_watchdog(struct ifnet *);
void che_start(struct ifnet *);
/* ifmedia & mii helper functions */
int che_ifmedia_upd(struct ifnet *);
void che_ifmedia_sts(struct ifnet *, struct ifmediareq *);
int che_miibus_readreg(struct device *, int, int);
void che_miibus_writereg(struct device *, int, int, int);
int che_miibus_ind_readreg(struct device *, int, int);
void che_miibus_ind_writereg(struct device *, int, int, int);
void che_miibus_statchg(struct device *);
/* bus_space wrappers */
u_int32_t che_read(struct cheg_softc *, bus_size_t);
void che_write(struct cheg_softc *, bus_size_t, u_int32_t);
int che_waitfor(struct cheg_softc *, bus_size_t, u_int32_t, int);
/* HW low-level functions */
void che_hw_init(struct cheg_softc *);
/* cheg */
struct cheg_device {
pci_vendor_id_t cd_vendor;
pci_vendor_id_t cd_product;
u_int cd_nports;
};
const struct cheg_device *cheg_lookup(struct pci_attach_args *);
const struct cheg_device che_devices[] = {
{ PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_PE9000, 2 },
{ PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T302E, 2 },
{ PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T302X, 2 },
{ PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T310E, 1 },
{ PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T310X, 1 },
{ PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T320E, 2 },
{ PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T320X, 2 },
{ PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T3B02, 2 },
{ PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T3B10, 1 },
{ PCI_VENDOR_CHELSIO, PCI_PRODUCT_CHELSIO_T3B20, 2 }
};
const struct cheg_device *
cheg_lookup(struct pci_attach_args *pa)
{
int i;
const struct cheg_device *cd;
for (i = 0; i < sizeof(che_devices)/sizeof(che_devices[0]); i++) {
cd = &che_devices[i];
if (cd->cd_vendor == PCI_VENDOR(pa->pa_id) &&
cd->cd_product == PCI_PRODUCT(pa->pa_id))
return (cd);
}
return (NULL);
}
int
cheg_match(struct device *parent, void *match, void *aux)
{
struct pci_attach_args *pa = aux;
if (cheg_lookup(pa) != NULL)
return (1);
return (0);
}
void
cheg_attach(struct device *parent, struct device *self, void *aux)
{
struct cheg_softc *sc = (struct cheg_softc *)self;
struct pci_attach_args *pa = aux;
const struct cheg_device *cd;
struct che_attach_args caa;
struct che_vpd vpd;
pcireg_t memtype;
u_int32_t vers;
u_int i;
bzero(&caa, sizeof(caa));
cd = cheg_lookup(pa);
sc->sc_dmat = pa->pa_dmat;
memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, CHE_PCI_BAR);
if (pci_mapreg_map(pa, CHE_PCI_BAR, memtype, 0, &sc->sc_memt,
&sc->sc_memh, NULL, &sc->sc_mems, 0) != 0) {
printf(": unable to map host registers\n");
return;
}
if (pci_intr_map(pa, &caa.caa_ih) != 0) {
printf(": unable to map interrupt\n");
goto unmap;
}
sc->sc_rev = che_read(sc, CHE_REG_PL_REV);
/* reset the beast */
che_reset(sc);
if (che_read_flash_multi4(sc, FW_VERS_ADDR, &vers, 1) != 0) {
printf(": unable to read flash version\n");
goto unmap;
}
if (che_get_vpd(sc, pa, &vpd, sizeof(vpd)/sizeof(u_int32_t)) != 0) {
printf(": unable to get vital product data\n");
goto unmap;
}
printf(": %s revision %d firmware %s-%d.%d.%d\n",
pci_intr_string(pa->pa_pc, caa.caa_ih), sc->sc_rev,
FW_VERS_TYPE(vers) ? "T" : "N",
FW_VERS_MAJOR(vers), FW_VERS_MINOR(vers), FW_VERS_MICRO(vers));
sc->sc_product = PCI_PRODUCT(pa->pa_id);
sc->sc_cclk = che_conv_num(vpd.cclk_data, sizeof(vpd.cclk_data));
sc->sc_mdc = che_conv_num(vpd.mdc_data, sizeof(vpd.mdc_data));
che_hw_init(sc);
caa.caa_pa = pa;
che_conv_lladdr(vpd.na_data, caa.caa_lladdr);
for (i = 0; i < cd->cd_nports; i++) {
caa.caa_port = i;
config_found(self, &caa, cheg_print);
/*
* The VPD EEPROM stores only the base Ethernet address for the
* card. The last octet is increased by one for every additional
* port.
*/
caa.caa_lladdr[5] += 1;
}
return;
unmap:
bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_mems);
sc->sc_mems = 0;
}
int
cheg_print(void *aux, const char *pnp)
{
struct che_attach_args *caa = aux;
if (pnp != NULL)
printf("\"%s\" at %s", che_cd.cd_name, pnp);
printf(" port %d", caa->caa_port);
return (UNCONF);
}
int
che_match(struct device *parent, void *match, void *aux)
{
return (1);
}
void
che_attach(struct device *parent, struct device *self, void *aux)
{
struct cheg_softc *gsc = (struct cheg_softc *)parent;
struct che_softc *sc = (struct che_softc *)self;
struct che_attach_args *caa = aux;
struct ifnet *ifp;
sc->sc_cheg = gsc;
sc->sc_port = caa->caa_port;
bcopy(caa->caa_lladdr, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
printf(": address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr));
ifp = &sc->sc_ac.ac_if;
ifp->if_softc = sc;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_ioctl = che_ioctl;
ifp->if_start = che_start;
ifp->if_watchdog = che_watchdog;
ifp->if_hardmtu = MCLBYTES - ETHER_HDR_LEN - ETHER_CRC_LEN; /* XXX */
strlcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
IFQ_SET_MAXLEN(&ifp->if_snd, 400);
IFQ_SET_READY(&ifp->if_snd);
ifmedia_init(&sc->sc_mii.mii_media, 0,
che_ifmedia_upd, che_ifmedia_sts);
sc->sc_mii.mii_ifp = ifp;
sc->sc_mii.mii_readreg = che_miibus_ind_readreg;
sc->sc_mii.mii_writereg = che_miibus_ind_writereg;
sc->sc_mii.mii_statchg = che_miibus_statchg;
mii_attach(self, &sc->sc_mii, 0xffffffff, MII_PHY_ANY,
MII_OFFSET_ANY, MIIF_DOPAUSE | MIIF_HAVEFIBER);
if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
printf("%s: no PHY found!\n", sc->sc_dev.dv_xname);
ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL,
0, NULL);
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL);
} else
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO);
if_attach(ifp);
ether_ifattach(ifp);
return;
}
int
che_write_flash_reg(struct cheg_softc *sc, size_t bcnt, int cont, u_int32_t v)
{
if (che_read(sc, CHE_REG_SF_OP) & CHE_SF_F_BUSY)
return (EBUSY);
che_write(sc, CHE_REG_SF_DATA, v);
che_write(sc, CHE_REG_SF_OP, CHE_SF_CONT(cont) |
CHE_SF_BYTECNT(bcnt - 1) | CHE_SF_F_OP);
return (che_waitfor(sc, CHE_REG_SF_OP, CHE_SF_F_BUSY, 5));
}
int
che_read_flash_reg(struct cheg_softc *sc, size_t bcnt, int cont, u_int32_t *vp)
{
if (che_read(sc, CHE_REG_SF_OP) & CHE_SF_F_BUSY)
return (EBUSY);
che_write(sc, CHE_REG_SF_OP, CHE_SF_CONT(cont) |
CHE_SF_BYTECNT(bcnt - 1));
if (che_waitfor(sc, CHE_REG_SF_OP, CHE_SF_F_BUSY, 5))
return (EAGAIN);
*vp = che_read(sc, CHE_REG_SF_DATA);
return (0);
}
int
che_read_flash_multi4(struct cheg_softc *sc, u_int addr, u_int32_t *datap,
size_t count)
{
int rv;
if (addr + count * sizeof(u_int32_t) > CHE_SF_SIZE || (addr & 3))
panic("%s: che_read_flash_multi4 bad params\n", DEVNAME(sc));
addr = swap32(addr) | CHE_SF_RD_DATA;
if ((rv = che_write_flash_reg(sc, 4, 1, addr)))
return (rv);
if ((rv = che_read_flash_reg(sc, 1, 1, datap)))
return (rv);
while (count) {
if ((rv = che_read_flash_reg(sc, 4, count > 1, datap)))
return (rv);
count--;
datap++;
}
return (0);
}
int
che_read_eeprom(struct cheg_softc *sc, struct pci_attach_args *pa,
pcireg_t addr, pcireg_t *dp)
{
pcireg_t rv, base;
int i = 4;
if (!pci_get_capability(pa->pa_pc, pa->pa_tag, CHE_PCI_CAP_ID_VPD,
&base, NULL)) {
printf("%s: VPD EEPROM not found\n",
DEVNAME(sc), addr);
return EIO;
}
addr <<= 16;
pci_conf_write(pa->pa_pc, pa->pa_tag, base, addr);
while(i--) {
delay(10);
rv = pci_conf_read(pa->pa_pc, pa->pa_tag, base);
if (rv & CHE_PCI_F_VPD_ADDR)
break;
}
if (!(rv & CHE_PCI_F_VPD_ADDR)) {
printf("%s: reading EEPROM address 0x%x failed\n",
DEVNAME(sc), addr);
return EIO;
}
*dp = pci_conf_read(pa->pa_pc, pa->pa_tag, base + CHE_PCI_VPD_DATA);
return (0);
}
int
che_get_vpd(struct cheg_softc *sc, struct pci_attach_args *pa,
void *vpd, size_t dwords)
{
pcireg_t dw0, *dw = vpd;
int i;
u_int16_t addr;
/*
* Card information is normally at CHE_PCI_VPD_BASE but some early
* cards had it at 0.
*/
if (che_read_eeprom(sc, pa, CHE_PCI_VPD_BASE, &dw0))
return (1);
/* we compare the id_tag which is least significant byte */
addr = ((dw0 & 0xff) == 0x82) ? CHE_PCI_VPD_BASE : 0;
for (i = 0; i < dwords; i++) {
if (che_read_eeprom(sc, pa, addr + i * 4, &dw[i]))
return (1);
}
return (0);
}
/*
* VPD mac addr is stored as ASCII string so we need to convert it to a
* sane representation form.
*/
void
che_conv_lladdr(char *mac, u_int8_t *lladdr)
{
int i;
u_int8_t digit;
bzero(lladdr, ETHER_ADDR_LEN);
for (i = 0; i < ETHER_ADDR_LEN * 2; i++) {
if (mac[i] >= '0' && mac[i] <= '9')
digit = mac[i] - '0';
else if (mac[i] >= 'A' && mac[i] <= 'F')
digit = mac[i] - 'A' + 10;
else if (mac[i] >= 'a' && mac[i] <= 'f')
digit = mac[i] - 'a' + 10;
if ((i & 1) == 0)
digit <<= 4;
lladdr[i/2] |= digit;
}
}
u_int32_t
che_conv_num(char *num, size_t len)
{
size_t i;
u_int32_t n = 0;
for (i = 0; i < len; i++) {
if (num[i] >= '0' && num[i] <= '9')
n = 10 * n + (num[i] - '0');
else
break;
}
return (n);
}
void
che_reset(struct cheg_softc *sc)
{
che_write(sc, CHE_REG_PL_RST, CHE_RST_F_CRSTWRM |
CHE_RST_F_CRSTWRMMODE);
/* Give the card some time to boot */
delay(500);
}
int
che_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
{
return (EIO);
}
void
che_watchdog(struct ifnet *ifp)
{
/* XXX */
}
void
che_start(struct ifnet *ifp)
{
/* XXX */
}
int
che_ifmedia_upd(struct ifnet *ifp)
{
struct che_softc *sc = ifp->if_softc;
mii_mediachg(&sc->sc_mii);
return (0);
}
void
che_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct che_softc *sc = ifp->if_softc;
mii_pollstat(&sc->sc_mii);
ifmr->ifm_active = sc->sc_mii.mii_media_active;
ifmr->ifm_status = sc->sc_mii.mii_media_status;
}
int
che_miibus_readreg(struct device *dev, int phy, int reg)
{
struct che_softc *sc = (struct che_softc *)dev;
u_int32_t addr = CHE_MI1_PHYADDR(phy) | reg;
che_write(sc->sc_cheg, CHE_REG_MI1_ADDR, addr);
che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(2));
if (che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20))
return (0);
return ((int)che_read(sc->sc_cheg, CHE_REG_MI1_DATA));
}
void
che_miibus_writereg(struct device *dev, int phy, int reg, int val)
{
struct che_softc *sc = (struct che_softc *)dev;
u_int32_t addr = CHE_MI1_PHYADDR(phy) | reg;
che_write(sc->sc_cheg, CHE_REG_MI1_ADDR, addr);
che_write(sc->sc_cheg, CHE_REG_MI1_DATA, val);
che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(1));
che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20);
}
int
che_miibus_ind_readreg(struct device *dev, int phy, int reg)
{
struct che_softc *sc = (struct che_softc *)dev;
che_write(sc->sc_cheg, CHE_REG_MI1_ADDR, CHE_MI1_PHYADDR(phy));
che_write(sc->sc_cheg, CHE_REG_MI1_DATA, reg);
che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(0));
if (che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20))
return (0);
che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(3));
if (che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20))
return (0);
return ((int)che_read(sc->sc_cheg, CHE_REG_MI1_DATA));
}
void
che_miibus_ind_writereg(struct device *dev, int phy, int reg, int val)
{
struct che_softc *sc = (struct che_softc *)dev;
che_write(sc->sc_cheg, CHE_REG_MI1_ADDR, CHE_MI1_PHYADDR(phy));
che_write(sc->sc_cheg, CHE_REG_MI1_DATA, reg);
che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(0));
if (che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20))
return;
che_write(sc->sc_cheg, CHE_REG_MI1_DATA, val);
che_write(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_OP(1));
che_waitfor(sc->sc_cheg, CHE_REG_MI1_OP, CHE_MI1_F_BUSY, 20);
}
void
che_miibus_statchg(struct device *dev)
{
struct che_softc *sc = (struct che_softc *)dev;
//struct mii_data *mii = &sc->sc_mii;
printf("%s: che_miibus_statchg\n", DEVNAME(sc));
}
u_int32_t
che_read(struct cheg_softc *sc, bus_size_t r)
{
bus_space_barrier(sc->sc_memt, sc->sc_memh, r, 4,
BUS_SPACE_BARRIER_READ);
return (bus_space_read_4(sc->sc_memt, sc->sc_memh, r));
}
void
che_write(struct cheg_softc *sc, bus_size_t r, u_int32_t v)
{
bus_space_write_4(sc->sc_memt, sc->sc_memh, r, v);
bus_space_barrier(sc->sc_memt, sc->sc_memh, r, 4,
BUS_SPACE_BARRIER_WRITE);
}
int
che_waitfor(struct cheg_softc *sc, bus_size_t r, u_int32_t mask, int tries)
{
u_int32_t v;
int i;
for (i = 0; i < tries; i++) {
v = che_read(sc, r);
if ((v & mask) == 0)
return (0);
delay(10);
}
return (EAGAIN);
}
void
che_hw_init(struct cheg_softc *sc)
{
u_int32_t mi1_reg;
u_int32_t i2c_reg;
u_int32_t gpio_reg;
u_int32_t port_reg;
mi1_reg = CHE_MI1_F_PREEN |
CHE_MI1_CLKDIV(sc->sc_cclk / (2 * sc->sc_mdc) - 1);
i2c_reg = CHE_I2C_CLKDIV(sc->sc_cclk / 80 - 1); /* 80KHz */
gpio_reg = CHE_T3DBG_F_GPIO0_OEN | CHE_T3DBG_F_GPIO0_OUT_VAL;
switch (sc->sc_product) {
case PCI_PRODUCT_CHELSIO_PE9000:
gpio_reg |= CHE_T3DBG_F_GPIO2_OEN | CHE_T3DBG_F_GPIO2_OUT_VAL |
CHE_T3DBG_F_GPIO4_OEN | CHE_T3DBG_F_GPIO4_OUT_VAL;
port_reg = CHE_XGM_PORTSPEED(2);
break;
case PCI_PRODUCT_CHELSIO_T302E:
case PCI_PRODUCT_CHELSIO_T302X:
case PCI_PRODUCT_CHELSIO_T3B02:
gpio_reg |= CHE_T3DBG_F_GPIO2_OEN | CHE_T3DBG_F_GPIO2_OUT_VAL |
CHE_T3DBG_F_GPIO4_OEN | CHE_T3DBG_F_GPIO4_OUT_VAL;
port_reg = CHE_XGM_PORTSPEED(2);
break;
case PCI_PRODUCT_CHELSIO_T310E:
case PCI_PRODUCT_CHELSIO_T310X:
case PCI_PRODUCT_CHELSIO_T3B10:
mi1_reg |= CHE_MI1_F_ST;
gpio_reg |= CHE_T3DBG_F_GPIO1_OEN | CHE_T3DBG_F_GPIO1_OUT_VAL |
CHE_T3DBG_F_GPIO6_OEN | CHE_T3DBG_F_GPIO6_OUT_VAL |
CHE_T3DBG_F_GPIO7_OEN |
CHE_T3DBG_F_GPIO10_OEN | CHE_T3DBG_F_GPIO10_OUT_VAL;
port_reg = CHE_XGM_PORTSPEED(3);
port_reg |= CHE_XGM_F_ENRGMII;
break;
case PCI_PRODUCT_CHELSIO_T320X:
case PCI_PRODUCT_CHELSIO_T320E:
case PCI_PRODUCT_CHELSIO_T3B20:
mi1_reg |= CHE_MI1_F_ST;
gpio_reg |= CHE_T3DBG_F_GPIO1_OEN | CHE_T3DBG_F_GPIO1_OUT_VAL |
CHE_T3DBG_F_GPIO2_OEN |
CHE_T3DBG_F_GPIO4_OEN |
CHE_T3DBG_F_GPIO5_OEN | CHE_T3DBG_F_GPIO5_OUT_VAL |
CHE_T3DBG_F_GPIO6_OEN | CHE_T3DBG_F_GPIO6_OUT_VAL |
CHE_T3DBG_F_GPIO7_OEN |
CHE_T3DBG_F_GPIO10_OEN | CHE_T3DBG_F_GPIO10_OUT_VAL |
CHE_T3DBG_F_GPIO11_OEN;
port_reg = CHE_XGM_PORTSPEED(3);
port_reg |= CHE_XGM_F_ENRGMII;
break;
}
if (sc->sc_rev == 0)
port_reg |= CHE_XGM_F_ENRGMII;
/* write all registers */
che_write(sc, CHE_REG_MI1_CFG, mi1_reg);
che_write(sc, CHE_REG_I2C_CFG, i2c_reg);
che_write(sc, CHE_REG_T3DBG_GPIO_EN, gpio_reg);
che_write(sc, CHE_REG_XGM_PORT_CFG, port_reg);
(void)che_read(sc, CHE_REG_XGM_PORT_CFG);
port_reg |= CHE_XGM_F_CLKDIVRESET;
che_write(sc, CHE_REG_XGM_PORT_CFG, port_reg);
(void)che_read(sc, CHE_REG_XGM_PORT_CFG);
che_write(sc, CHE_XGM_REG(CHE_REG_XGM_PORT_CFG, 1), port_reg);
(void)che_read(sc, CHE_REG_XGM_PORT_CFG);
}