summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/spi/spi_flash.c3
-rw-r--r--drivers/net/mvgbe.c9
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/marvell.c113
-rw-r--r--drivers/net/phy/phy.c3
-rw-r--r--drivers/net/phy/smsc.c92
-rw-r--r--drivers/net/sh_eth.c293
-rw-r--r--drivers/net/sh_eth.h59
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/pci_ftpci100.c330
-rw-r--r--drivers/pci/pci_ftpci100.h94
-rw-r--r--drivers/tpm/Makefile43
-rw-r--r--drivers/tpm/generic_lpc_tpm.c495
13 files changed, 1277 insertions, 259 deletions
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index ced4c9400..f689cc47c 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -233,8 +233,7 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u8 erase_cmd,
goto out;
}
- debug("SF: Successfully erased %zu bytes @ %#x\n",
- len * erase_size, start);
+ debug("SF: Successfully erased %zu bytes @ %#x\n", len, start);
out:
spi_release_bus(flash->spi);
diff --git a/drivers/net/mvgbe.c b/drivers/net/mvgbe.c
index fd13428b4..de7cdd7ba 100644
--- a/drivers/net/mvgbe.c
+++ b/drivers/net/mvgbe.c
@@ -531,6 +531,7 @@ static int mvgbe_send(struct eth_device *dev, void *dataptr,
struct mvgbe_txdesc *p_txdesc = dmvgbe->p_txdesc;
void *p = (void *)dataptr;
u32 cmd_sts;
+ u32 txuq0_reg_addr;
/* Copy buffer if it's misaligned */
if ((u32) dataptr & 0x07) {
@@ -552,7 +553,8 @@ static int mvgbe_send(struct eth_device *dev, void *dataptr,
p_txdesc->byte_cnt = datasize;
/* Set this tc desc as zeroth TXUQ */
- MVGBE_REG_WR(regs->tcqdp[TXUQ], (u32) p_txdesc);
+ txuq0_reg_addr = (u32)&regs->tcqdp[TXUQ];
+ writel((u32) p_txdesc, txuq0_reg_addr);
/* ensure tx desc writes above are performed before we start Tx DMA */
isb();
@@ -583,6 +585,7 @@ static int mvgbe_recv(struct eth_device *dev)
struct mvgbe_rxdesc *p_rxdesc_curr = dmvgbe->p_rxdesc_curr;
u32 cmd_sts;
u32 timeout = 0;
+ u32 rxdesc_curr_addr;
/* wait untill rx packet available or timeout */
do {
@@ -637,8 +640,8 @@ static int mvgbe_recv(struct eth_device *dev)
p_rxdesc_curr->buf_size = PKTSIZE_ALIGN;
p_rxdesc_curr->byte_cnt = 0;
- writel((unsigned)p_rxdesc_curr->nxtdesc_p,
- (u32) &dmvgbe->p_rxdesc_curr);
+ rxdesc_curr_addr = (u32)&dmvgbe->p_rxdesc_curr;
+ writel((unsigned)p_rxdesc_curr->nxtdesc_p, rxdesc_curr_addr);
return 0;
}
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index a59834b29..feced39a4 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -38,6 +38,7 @@ COBJS-$(CONFIG_PHY_MARVELL) += marvell.o
COBJS-$(CONFIG_PHY_MICREL) += micrel.o
COBJS-$(CONFIG_PHY_NATSEMI) += natsemi.o
COBJS-$(CONFIG_PHY_REALTEK) += realtek.o
+COBJS-$(CONFIG_PHY_SMSC) += smsc.o
COBJS-$(CONFIG_PHY_TERANETICS) += teranetics.o
COBJS-$(CONFIG_PHY_VITESSE) += vitesse.o
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index bd1cdc4f1..e51e799e2 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -43,6 +43,24 @@
#define MIIM_88E1111_PHY_LED_DIRECT 0x4100
#define MIIM_88E1111_PHY_LED_COMBINE 0x411C
+/* 88E1111 Extended PHY Specific Control Register */
+#define MIIM_88E1111_PHY_EXT_CR 0x14
+#define MIIM_88E1111_RX_DELAY 0x80
+#define MIIM_88E1111_TX_DELAY 0x2
+
+/* 88E1111 Extended PHY Specific Status Register */
+#define MIIM_88E1111_PHY_EXT_SR 0x1b
+#define MIIM_88E1111_HWCFG_MODE_MASK 0xf
+#define MIIM_88E1111_HWCFG_MODE_COPPER_RGMII 0xb
+#define MIIM_88E1111_HWCFG_MODE_FIBER_RGMII 0x3
+#define MIIM_88E1111_HWCFG_MODE_SGMII_NO_CLK 0x4
+#define MIIM_88E1111_HWCFG_MODE_COPPER_RTBI 0x9
+#define MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO 0x8000
+#define MIIM_88E1111_HWCFG_FIBER_COPPER_RES 0x2000
+
+#define MIIM_88E1111_COPPER 0
+#define MIIM_88E1111_FIBER 1
+
/* 88E1118 PHY defines */
#define MIIM_88E1118_PHY_PAGE 22
#define MIIM_88E1118_PHY_LED_PAGE 3
@@ -162,19 +180,102 @@ static int m88e1011s_startup(struct phy_device *phydev)
static int m88e1111s_config(struct phy_device *phydev)
{
int reg;
+ int timeout;
if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
(phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) ||
(phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
(phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) {
- reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x1b);
- reg = (reg & 0xfff0) | 0xb;
- phy_write(phydev, MDIO_DEVAD_NONE, 0x1b, reg);
- } else {
- phy_write(phydev, MDIO_DEVAD_NONE, 0x1b, 0x1f);
+ reg = phy_read(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR);
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)) {
+ reg |= (MIIM_88E1111_RX_DELAY | MIIM_88E1111_TX_DELAY);
+ } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
+ reg &= ~MIIM_88E1111_TX_DELAY;
+ reg |= MIIM_88E1111_RX_DELAY;
+ } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+ reg &= ~MIIM_88E1111_RX_DELAY;
+ reg |= MIIM_88E1111_TX_DELAY;
+ }
+
+ phy_write(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR, reg);
+
+ reg = phy_read(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_SR);
+
+ reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK);
+
+ if (reg & MIIM_88E1111_HWCFG_FIBER_COPPER_RES)
+ reg |= MIIM_88E1111_HWCFG_MODE_FIBER_RGMII;
+ else
+ reg |= MIIM_88E1111_HWCFG_MODE_COPPER_RGMII;
+
+ phy_write(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_SR, reg);
+ }
+
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ reg = phy_read(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_SR);
+
+ reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK);
+ reg |= MIIM_88E1111_HWCFG_MODE_SGMII_NO_CLK;
+ reg |= MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO;
+
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR, reg);
+ }
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
+ reg = phy_read(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR);
+ reg |= (MIIM_88E1111_RX_DELAY | MIIM_88E1111_TX_DELAY);
+ phy_write(phydev,
+ MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR, reg);
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR);
+ reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK |
+ MIIM_88E1111_HWCFG_FIBER_COPPER_RES);
+ reg |= 0x7 | MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO;
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR, reg);
+
+ /* soft reset */
+ timeout = 1000;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+ udelay(1000);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ while ((reg & BMCR_RESET) && --timeout) {
+ udelay(1000);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ }
+ if (!timeout)
+ printf("%s: phy soft reset timeout\n", __func__);
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR);
+ reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK |
+ MIIM_88E1111_HWCFG_FIBER_COPPER_RES);
+ reg |= MIIM_88E1111_HWCFG_MODE_COPPER_RTBI |
+ MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO;
+ phy_write(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1111_PHY_EXT_SR, reg);
}
- phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0cd2);
+ /* soft reset */
+ timeout = 1000;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+ udelay(1000);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ while ((reg & BMCR_RESET) && --timeout) {
+ udelay(1000);
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ }
+ if (!timeout)
+ printf("%s: phy soft reset timeout\n", __func__);
genphy_config_aneg(phydev);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 8da7688d7..eb551803e 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -444,6 +444,9 @@ int phy_init(void)
#ifdef CONFIG_PHY_REALTEK
phy_realtek_init();
#endif
+#ifdef CONFIG_PHY_SMSC
+ phy_smsc_init();
+#endif
#ifdef CONFIG_PHY_TERANETICS
phy_teranetics_init();
#endif
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
new file mode 100644
index 000000000..6dee8eb51
--- /dev/null
+++ b/drivers/net/phy/smsc.c
@@ -0,0 +1,92 @@
+/*
+ * SMSC PHY drivers
+ *
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * Base code from drivers/net/phy/davicom.c
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ * Some code get from linux kenrel
+ * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
+ *
+ */
+#include <miiphy.h>
+
+static int smsc_parse_status(struct phy_device *phydev)
+{
+ int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+ if (mii_reg & (BMSR_100FULL | BMSR_100HALF))
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ if (mii_reg & (BMSR_10FULL | BMSR_100FULL))
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ return 0;
+}
+
+static int smsc_startup(struct phy_device *phydev)
+{
+ genphy_update_link(phydev);
+ smsc_parse_status(phydev);
+ return 0;
+}
+
+static struct phy_driver lan8700_driver = {
+ .name = "SMSC LAN8700",
+ .uid = 0x0007c0c0,
+ .mask = 0xffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &smsc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver lan911x_driver = {
+ .name = "SMSC LAN911x Internal PHY",
+ .uid = 0x0007c0d0,
+ .mask = 0xffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &smsc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver lan8710_driver = {
+ .name = "SMSC LAN8710/LAN8720",
+ .uid = 0x0007c0f0,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &smsc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_smsc_init(void)
+{
+ phy_register(&lan8710_driver);
+ phy_register(&lan911x_driver);
+ phy_register(&lan8700_driver);
+
+ return 0;
+}
diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c
index 17dd0d281..27d040125 100644
--- a/drivers/net/sh_eth.c
+++ b/drivers/net/sh_eth.c
@@ -25,6 +25,7 @@
#include <malloc.h>
#include <net.h>
#include <netdev.h>
+#include <miiphy.h>
#include <asm/errno.h>
#include <asm/io.h>
@@ -45,144 +46,6 @@
#define SH_ETH_PHY_DELAY 50000
-/*
- * Bits are written to the PHY serially using the
- * PIR register, just like a bit banger.
- */
-static void sh_eth_mii_write_phy_bits(int port, u32 val, int len)
-{
- int i;
- u32 pir;
-
- /* Bit positions is 1 less than the number of bits */
- for (i = len - 1; i >= 0; i--) {
- /* Write direction, bit to write, clock is low */
- pir = 2 | ((val & 1 << i) ? 1 << 2 : 0);
- outl(pir, PIR(port));
- udelay(1);
- /* Write direction, bit to write, clock is high */
- pir = 3 | ((val & 1 << i) ? 1 << 2 : 0);
- outl(pir, PIR(port));
- udelay(1);
- /* Write direction, bit to write, clock is low */
- pir = 2 | ((val & 1 << i) ? 1 << 2 : 0);
- outl(pir, PIR(port));
- udelay(1);
- }
-}
-
-static void sh_eth_mii_bus_release(int port)
-{
- /* Read direction, clock is low */
- outl(0, PIR(port));
- udelay(1);
- /* Read direction, clock is high */
- outl(1, PIR(port));
- udelay(1);
- /* Read direction, clock is low */
- outl(0, PIR(port));
- udelay(1);
-}
-
-static void sh_eth_mii_ind_bus_release(int port)
-{
- /* Read direction, clock is low */
- outl(0, PIR(port));
- udelay(1);
-}
-
-static void sh_eth_mii_read_phy_bits(int port, u32 *val, int len)
-{
- int i;
- u32 pir;
-
- *val = 0;
- for (i = len - 1; i >= 0; i--) {
- /* Read direction, clock is high */
- outl(1, PIR(port));
- udelay(1);
- /* Read bit */
- pir = inl(PIR(port));
- *val |= (pir & 8) ? 1 << i : 0;
- /* Read direction, clock is low */
- outl(0, PIR(port));
- udelay(1);
- }
-}
-
-#define PHY_INIT 0xFFFFFFFF
-#define PHY_READ 0x02
-#define PHY_WRITE 0x01
-/*
- * To read a phy register, mii managements frames are sent to the phy.
- * The frames look like this:
- * pre (32 bits): 0xffff ffff
- * st (2 bits): 01
- * op (2bits): 10: read 01: write
- * phyad (5 bits): xxxxx
- * regad (5 bits): xxxxx
- * ta (Bus release):
- * data (16 bits): read data
- */
-static u32 sh_eth_mii_read_phy_reg(int port, u8 phy_addr, int reg)
-{
- u32 val;
-
- /* Sent mii management frame */
- /* pre */
- sh_eth_mii_write_phy_bits(port, PHY_INIT, 32);
- /* st (start of frame) */
- sh_eth_mii_write_phy_bits(port, 0x1, 2);
- /* op (code) */
- sh_eth_mii_write_phy_bits(port, PHY_READ, 2);
- /* phy address */
- sh_eth_mii_write_phy_bits(port, phy_addr, 5);
- /* Register to read */
- sh_eth_mii_write_phy_bits(port, reg, 5);
-
- /* Bus release */
- sh_eth_mii_bus_release(port);
-
- /* Read register */
- sh_eth_mii_read_phy_bits(port, &val, 16);
-
- return val;
-}
-
-/*
- * To write a phy register, mii managements frames are sent to the phy.
- * The frames look like this:
- * pre (32 bits): 0xffff ffff
- * st (2 bits): 01
- * op (2bits): 10: read 01: write
- * phyad (5 bits): xxxxx
- * regad (5 bits): xxxxx
- * ta (2 bits): 10
- * data (16 bits): write data
- * idle (Independent bus release)
- */
-static void sh_eth_mii_write_phy_reg(int port, u8 phy_addr, int reg, u16 val)
-{
- /* Sent mii management frame */
- /* pre */
- sh_eth_mii_write_phy_bits(port, PHY_INIT, 32);
- /* st (start of frame) */
- sh_eth_mii_write_phy_bits(port, 0x1, 2);
- /* op (code) */
- sh_eth_mii_write_phy_bits(port, PHY_WRITE, 2);
- /* phy address */
- sh_eth_mii_write_phy_bits(port, phy_addr, 5);
- /* Register to read */
- sh_eth_mii_write_phy_bits(port, reg, 5);
- /* ta */
- sh_eth_mii_write_phy_bits(port, PHY_READ, 2);
- /* Write register data */
- sh_eth_mii_write_phy_bits(port, val, 16);
-
- /* Independent bus release */
- sh_eth_mii_ind_bus_release(port);
-}
-
int sh_eth_send(struct eth_device *dev, volatile void *packet, int len)
{
struct sh_eth_dev *eth = dev->priv;
@@ -480,62 +343,26 @@ err_tx_init:
static int sh_eth_phy_config(struct sh_eth_dev *eth)
{
- int port = eth->port, timeout, ret = 0;
+ int port = eth->port, ret = 0;
struct sh_eth_info *port_info = &eth->port_info[port];
- u32 val;
-
- /* Reset phy */
- sh_eth_mii_write_phy_reg
- (port, port_info->phy_addr, PHY_CTRL, PHY_C_RESET);
- timeout = 10;
- while (timeout--) {
- val = sh_eth_mii_read_phy_reg(port,
- port_info->phy_addr, PHY_CTRL);
- if (!(val & PHY_C_RESET))
- break;
- udelay(SH_ETH_PHY_DELAY);
- }
-
- if (timeout < 0) {
- printf(SHETHER_NAME ": phy reset timeout\n");
- ret = -EIO;
- goto err_tout;
- }
-
- /* Advertise 100/10 baseT full/half duplex */
- sh_eth_mii_write_phy_reg(port, port_info->phy_addr, PHY_ANA,
- (PHY_A_FDX|PHY_A_HDX|PHY_A_10FDX|PHY_A_10HDX|PHY_A_EXT));
- /* Autonegotiation, normal operation, full duplex, enable tx */
- sh_eth_mii_write_phy_reg(port, port_info->phy_addr, PHY_CTRL,
- (PHY_C_ANEGEN|PHY_C_RANEG));
- /* Wait for autonegotiation to complete */
- timeout = 100;
- while (timeout--) {
- val = sh_eth_mii_read_phy_reg(port, port_info->phy_addr, 1);
- if (val & PHY_S_ANEGC)
- break;
-
- udelay(SH_ETH_PHY_DELAY);
- }
-
- if (timeout < 0) {
- printf(SHETHER_NAME ": phy auto-negotiation failed\n");
- ret = -ETIMEDOUT;
- goto err_tout;
- }
+ struct eth_device *dev = port_info->dev;
+ struct phy_device *phydev;
- return ret;
+ phydev = phy_connect(miiphy_get_dev_by_name(dev->name),
+ port_info->phy_addr, dev, PHY_INTERFACE_MODE_MII);
+ port_info->phydev = phydev;
+ phy_config(phydev);
-err_tout:
return ret;
}
static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd)
{
int port = eth->port, ret = 0;
- u32 val, phy_status;
+ u32 val;
struct sh_eth_info *port_info = &eth->port_info[port];
struct eth_device *dev = port_info->dev;
+ struct phy_device *phy;
/* Configure e-dmac registers */
outl((inl(EDMR(port)) & ~EMDR_DESC_R) | EDMR_EL, EDMR(port));
@@ -582,31 +409,31 @@ static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd)
printf(SHETHER_NAME ": phy config timeout\n");
goto err_phy_cfg;
}
- /* Read phy status to finish configuring the e-mac */
- phy_status = sh_eth_mii_read_phy_reg(port, port_info->phy_addr, 1);
+ phy = port_info->phydev;
+ phy_startup(phy);
/* Set the transfer speed */
#ifdef CONFIG_CPU_SH7763
- if (phy_status & (PHY_S_100X_F|PHY_S_100X_H)) {
+ if (phy->speed == 100) {
printf(SHETHER_NAME ": 100Base/");
outl(GECMR_100B, GECMR(port));
- } else {
+ } else if (phy->speed == 10) {
printf(SHETHER_NAME ": 10Base/");
outl(GECMR_10B, GECMR(port));
}
#endif
#if defined(CONFIG_CPU_SH7757)
- if (phy_status & (PHY_S_100X_F|PHY_S_100X_H)) {
+ if (phy->speed == 100) {
printf("100Base/");
outl(1, RTRATE(port));
- } else {
+ } else if (phy->speed == 10) {
printf("10Base/");
outl(0, RTRATE(port));
}
#endif
/* Check if full duplex mode is supported by the phy */
- if (phy_status & (PHY_S_100X_F|PHY_S_10T_F)) {
+ if (phy->duplex) {
printf("Full\n");
outl((ECMR_CHG_DM|ECMR_RE|ECMR_TE|ECMR_DM), ECMR(port));
} else {
@@ -707,6 +534,9 @@ int sh_eth_initialize(bd_t *bd)
/* Register Device to EtherNet subsystem */
eth_register(dev);
+ bb_miiphy_buses[0].priv = eth;
+ miiphy_register(dev->name, bb_miiphy_read, bb_miiphy_write);
+
if (!eth_getenv_enetaddr("ethaddr", dev->enetaddr))
puts("Please set MAC address\n");
@@ -722,3 +552,86 @@ err:
printf(SHETHER_NAME ": Failed\n");
return ret;
}
+
+/******* for bb_miiphy *******/
+static int sh_eth_bb_init(struct bb_miiphy_bus *bus)
+{
+ return 0;
+}
+
+static int sh_eth_bb_mdio_active(struct bb_miiphy_bus *bus)
+{
+ struct sh_eth_dev *eth = bus->priv;
+ int port = eth->port;
+
+ outl(inl(PIR(port)) | PIR_MMD, PIR(port));
+
+ return 0;
+}
+
+static int sh_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus)
+{
+ struct sh_eth_dev *eth = bus->priv;
+ int port = eth->port;
+
+ outl(inl(PIR(port)) & ~PIR_MMD, PIR(port));
+
+ return 0;
+}
+
+static int sh_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v)
+{
+ struct sh_eth_dev *eth = bus->priv;
+ int port = eth->port;
+
+ if (v)
+ outl(inl(PIR(port)) | PIR_MDO, PIR(port));
+ else
+ outl(inl(PIR(port)) & ~PIR_MDO, PIR(port));
+
+ return 0;
+}
+
+static int sh_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v)
+{
+ struct sh_eth_dev *eth = bus->priv;
+ int port = eth->port;
+
+ *v = (inl(PIR(port)) & PIR_MDI) >> 3;
+
+ return 0;
+}
+
+static int sh_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v)
+{
+ struct sh_eth_dev *eth = bus->priv;
+ int port = eth->port;
+
+ if (v)
+ outl(inl(PIR(port)) | PIR_MDC, PIR(port));
+ else
+ outl(inl(PIR(port)) & ~PIR_MDC, PIR(port));
+
+ return 0;
+}
+
+static int sh_eth_bb_delay(struct bb_miiphy_bus *bus)
+{
+ udelay(10);
+
+ return 0;
+}
+
+struct bb_miiphy_bus bb_miiphy_buses[] = {
+ {
+ .name = "sh_eth",
+ .init = sh_eth_bb_init,
+ .mdio_active = sh_eth_bb_mdio_active,
+ .mdio_tristate = sh_eth_bb_mdio_tristate,
+ .set_mdio = sh_eth_bb_set_mdio,
+ .get_mdio = sh_eth_bb_get_mdio,
+ .set_mdc = sh_eth_bb_set_mdc,
+ .delay = sh_eth_bb_delay,
+ }
+};
+int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses);
diff --git a/drivers/net/sh_eth.h b/drivers/net/sh_eth.h
index 51e5d5b6f..dd6a4224e 100644
--- a/drivers/net/sh_eth.h
+++ b/drivers/net/sh_eth.h
@@ -89,6 +89,7 @@ struct sh_eth_info {
u8 mac_addr[6];
u8 phy_addr;
struct eth_device *dev;
+ struct phy_device *phydev;
};
struct sh_eth_dev {
@@ -435,61 +436,3 @@ enum FIFO_SIZE_BIT {
FIFO_SIZE_T = 0x00000700, FIFO_SIZE_R = 0x00000007,
};
-enum PHY_OFFSETS {
- PHY_CTRL = 0, PHY_STAT = 1, PHY_IDT1 = 2, PHY_IDT2 = 3,
- PHY_ANA = 4, PHY_ANL = 5, PHY_ANE = 6,
- PHY_16 = 16,
-};
-
-/* PHY_CTRL */
-enum PHY_CTRL_BIT {
- PHY_C_RESET = 0x8000, PHY_C_LOOPBK = 0x4000, PHY_C_SPEEDSL = 0x2000,
- PHY_C_ANEGEN = 0x1000, PHY_C_PWRDN = 0x0800, PHY_C_ISO = 0x0400,
- PHY_C_RANEG = 0x0200, PHY_C_DUPLEX = 0x0100, PHY_C_COLT = 0x0080,
-};
-#define DM9161_PHY_C_ANEGEN 0 /* auto nego special */
-
-/* PHY_STAT */
-enum PHY_STAT_BIT {
- PHY_S_100T4 = 0x8000, PHY_S_100X_F = 0x4000, PHY_S_100X_H = 0x2000,
- PHY_S_10T_F = 0x1000, PHY_S_10T_H = 0x0800, PHY_S_ANEGC = 0x0020,
- PHY_S_RFAULT = 0x0010, PHY_S_ANEGA = 0x0008, PHY_S_LINK = 0x0004,
- PHY_S_JAB = 0x0002, PHY_S_EXTD = 0x0001,
-};
-
-/* PHY_ANA */
-enum PHY_ANA_BIT {
- PHY_A_NP = 0x8000, PHY_A_ACK = 0x4000, PHY_A_RF = 0x2000,
- PHY_A_FCS = 0x0400, PHY_A_T4 = 0x0200, PHY_A_FDX = 0x0100,
- PHY_A_HDX = 0x0080, PHY_A_10FDX = 0x0040, PHY_A_10HDX = 0x0020,
- PHY_A_SEL = 0x001e,
- PHY_A_EXT = 0x0001,
-};
-
-/* PHY_ANL */
-enum PHY_ANL_BIT {
- PHY_L_NP = 0x8000, PHY_L_ACK = 0x4000, PHY_L_RF = 0x2000,
- PHY_L_FCS = 0x0400, PHY_L_T4 = 0x0200, PHY_L_FDX = 0x0100,
- PHY_L_HDX = 0x0080, PHY_L_10FDX = 0x0040, PHY_L_10HDX = 0x0020,
- PHY_L_SEL = 0x001f,
-};
-
-/* PHY_ANE */
-enum PHY_ANE_BIT {
- PHY_E_PDF = 0x0010, PHY_E_LPNPA = 0x0008, PHY_E_NPA = 0x0004,
- PHY_E_PRX = 0x0002, PHY_E_LPANEGA = 0x0001,
-};
-
-/* DM9161 */
-enum PHY_16_BIT {
- PHY_16_BP4B45 = 0x8000, PHY_16_BPSCR = 0x4000, PHY_16_BPALIGN = 0x2000,
- PHY_16_BP_ADPOK = 0x1000, PHY_16_Repeatmode = 0x0800,
- PHY_16_TXselect = 0x0400,
- PHY_16_Rsvd = 0x0200, PHY_16_RMIIEnable = 0x0100,
- PHY_16_Force100LNK = 0x0080,
- PHY_16_APDLED_CTL = 0x0040, PHY_16_COLLED_CTL = 0x0020,
- PHY_16_RPDCTR_EN = 0x0010,
- PHY_16_ResetStMch = 0x0008, PHY_16_PreamSupr = 0x0004,
- PHY_16_Sleepmode = 0x0002,
- PHY_16_RemoteLoopOut = 0x0001,
-};
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index ee0c64d13..1ae35d360 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -27,6 +27,7 @@ LIB := $(obj)libpci.o
COBJS-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o
COBJS-$(CONFIG_PCI) += pci.o pci_auto.o pci_indirect.o
+COBJS-$(CONFIG_FTPCI100) += pci_ftpci100.o
COBJS-$(CONFIG_IXP_PCI) += pci_ixp.o
COBJS-$(CONFIG_SH4_PCI) += pci_sh4.o
COBJS-$(CONFIG_SH7751_PCI) +=pci_sh7751.o
diff --git a/drivers/pci/pci_ftpci100.c b/drivers/pci/pci_ftpci100.c
new file mode 100644
index 000000000..a795a97ad
--- /dev/null
+++ b/drivers/pci/pci_ftpci100.c
@@ -0,0 +1,330 @@
+/*
+ * Faraday FTPCI100 PCI Bridge Controller Device Driver Implementation
+ *
+ * Copyright (C) 2011 Andes Technology Corporation
+ * Gavin Guo, Andes Technology Corporation <gavinguo@andestech.com>
+ * Macpaul Lin, Andes Technology Corporation <macpaul@andestech.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <common.h>
+#include <malloc.h>
+#include <pci.h>
+
+#include <asm/io.h>
+#include <asm/types.h> /* u32, u16.... used by pci.h */
+
+#include "pci_ftpci100.h"
+
+struct ftpci100_data {
+ unsigned int reg_base;
+ unsigned int io_base;
+ unsigned int mem_base;
+ unsigned int mmio_base;
+ unsigned int ndevs;
+};
+
+static struct pci_config devs[FTPCI100_MAX_FUNCTIONS];
+static struct pci_controller local_hose;
+
+static void setup_pci_bar(unsigned int bus, unsigned int dev, unsigned func,
+ unsigned char header, struct ftpci100_data *priv)
+{
+ struct pci_controller *hose = (struct pci_controller *)&local_hose;
+ unsigned int i, tmp32, bar_no, iovsmem = 1;
+ pci_dev_t dev_nu;
+
+ /* A device is present, add an entry to the array */
+ devs[priv->ndevs].bus = bus;
+ devs[priv->ndevs].dev = dev;
+ devs[priv->ndevs].func = func;
+
+ dev_nu = PCI_BDF(bus, dev, func);
+
+ if ((header & 0x7f) == 0x01)
+ /* PCI-PCI Bridge */
+ bar_no = 2;
+ else
+ bar_no = 6;
+
+ /* Allocate address spaces by configuring BARs */
+ for (i = 0; i < bar_no; i++) {
+ pci_hose_write_config_dword(hose, dev_nu,
+ PCI_BASE_ADDRESS_0 + i * 4, 0xffffffff);
+ pci_hose_read_config_dword(hose, dev_nu,
+ PCI_BASE_ADDRESS_0 + i * 4, &tmp32);
+
+ if (tmp32 == 0x0)
+ continue;
+
+ /* IO space */
+ if (tmp32 & 0x1) {
+ iovsmem = 0;
+ unsigned int size_mask = ~(tmp32 & 0xfffffffc);
+
+ if (priv->io_base & size_mask)
+ priv->io_base = (priv->io_base & ~size_mask) + \
+ size_mask + 1;
+
+ devs[priv->ndevs].bar[i].addr = priv->io_base;
+ devs[priv->ndevs].bar[i].size = size_mask + 1;
+
+ pci_hose_write_config_dword(hose, dev_nu,
+ PCI_BASE_ADDRESS_0 + i * 4,
+ priv->io_base);
+
+ debug("Allocated IO address 0x%X-" \
+ "0x%X for Bus %d, Device %d, Function %d\n",
+ priv->io_base,
+ priv->io_base + size_mask, bus, dev, func);
+
+ priv->io_base += size_mask + 1;
+ } else {
+ /* Memory space */
+ unsigned int is_64bit = ((tmp32 & 0x6) == 0x4);
+ unsigned int is_pref = tmp32 & 0x8;
+ unsigned int size_mask = ~(tmp32 & 0xfffffff0);
+ unsigned int alloc_base;
+ unsigned int *addr_mem_base;
+
+ if (is_pref)
+ addr_mem_base = &priv->mem_base;
+ else
+ addr_mem_base = &priv->mmio_base;
+
+ alloc_base = *addr_mem_base;
+
+ if (alloc_base & size_mask)
+ alloc_base = (alloc_base & ~size_mask) \
+ + size_mask + 1;
+
+ pci_hose_write_config_dword(hose, dev_nu,
+ PCI_BASE_ADDRESS_0 + i * 4, alloc_base);
+
+ debug("Allocated %s address 0x%X-" \
+ "0x%X for Bus %d, Device %d, Function %d\n",
+ is_pref ? "MEM" : "MMIO", alloc_base,
+ alloc_base + size_mask, bus, dev, func);
+
+ devs[priv->ndevs].bar[i].addr = alloc_base;
+ devs[priv->ndevs].bar[i].size = size_mask + 1;
+
+ debug("BAR address BAR size\n");
+ debug("%010x %08d\n",
+ devs[priv->ndevs].bar[0].addr,
+ devs[priv->ndevs].bar[0].size);
+
+ alloc_base += size_mask + 1;
+ *addr_mem_base = alloc_base;
+
+ if (is_64bit) {
+ i++;
+ pci_hose_write_config_dword(hose, dev_nu,
+ PCI_BASE_ADDRESS_0 + i * 4, 0x0);
+ }
+ }
+ }
+
+ /* Enable Bus Master, Memory Space, and IO Space */
+ pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32);
+ pci_hose_write_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, 0x08);
+ pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32);
+
+ pci_hose_read_config_dword(hose, dev_nu, PCI_COMMAND, &tmp32);
+
+ tmp32 &= 0xffff;
+
+ if (iovsmem == 0)
+ tmp32 |= 0x5;
+ else
+ tmp32 |= 0x6;
+
+ pci_hose_write_config_dword(hose, dev_nu, PCI_COMMAND, tmp32);
+}
+
+static void pci_bus_scan(struct ftpci100_data *priv)
+{
+ struct pci_controller *hose = (struct pci_controller *)&local_hose;
+ unsigned int bus, dev, func;
+ pci_dev_t dev_nu;
+ unsigned int data32;
+ unsigned int tmp;
+ unsigned char header;
+ unsigned char int_pin;
+ unsigned int niobars;
+ unsigned int nmbars;
+
+ priv->ndevs = 1;
+
+ nmbars = 0;
+ niobars = 0;
+
+ for (bus = 0; bus < MAX_BUS_NUM; bus++)
+ for (dev = 0; dev < MAX_DEV_NUM; dev++)
+ for (func = 0; func < MAX_FUN_NUM; func++) {
+ dev_nu = PCI_BDF(bus, dev, func);
+ pci_hose_read_config_dword(hose, dev_nu,
+ PCI_VENDOR_ID, &data32);
+
+ /*
+ * some broken boards return 0 or ~0,
+ * if a slot is empty.
+ */
+ if (data32 == 0xffffffff ||
+ data32 == 0x00000000 ||
+ data32 == 0x0000ffff ||
+ data32 == 0xffff0000)
+ continue;
+
+ pci_hose_read_config_dword(hose, dev_nu,
+ PCI_HEADER_TYPE, &tmp);
+ header = (unsigned char)tmp;
+ setup_pci_bar(bus, dev, func, header, priv);
+
+ devs[priv->ndevs].v_id = (u16)(data32 & \
+ 0x0000ffff);
+
+ devs[priv->ndevs].d_id = (u16)((data32 & \
+ 0xffff0000) >> 16);
+
+ /* Figure out what INTX# line the card uses */
+ pci_hose_read_config_byte(hose, dev_nu,
+ PCI_INTERRUPT_PIN, &int_pin);
+
+ /* assign the appropriate irq line */
+ if (int_pin > PCI_IRQ_LINES) {
+ printf("more irq lines than expect\n");
+ } else if (int_pin != 0) {
+ /* This device uses an interrupt line */
+ devs[priv->ndevs].pin = int_pin;
+ }
+
+ pci_hose_read_config_dword(hose, dev_nu,
+ PCI_CLASS_DEVICE, &data32);
+
+ debug("%06d %03d %03d " \
+ "%04d %08x %08x " \
+ "%03d %08x %06d %08x\n",
+ priv->ndevs, devs[priv->ndevs].bus,
+ devs[priv->ndevs].dev,
+ devs[priv->ndevs].func,
+ devs[priv->ndevs].d_id,
+ devs[priv->ndevs].v_id,
+ devs[priv->ndevs].pin,
+ devs[priv->ndevs].bar[0].addr,
+ devs[priv->ndevs].bar[0].size,
+ data32 >> 8);
+
+ priv->ndevs++;
+ }
+}
+
+static void ftpci_preinit(struct ftpci100_data *priv)
+{
+ struct ftpci100_ahbc *ftpci100;
+ struct pci_controller *hose = (struct pci_controller *)&local_hose;
+ u32 pci_config_addr;
+ u32 pci_config_data;
+
+ priv->reg_base = CONFIG_FTPCI100_BASE;
+ priv->io_base = CONFIG_FTPCI100_BASE + CONFIG_FTPCI100_IO_SIZE;
+ priv->mmio_base = CONFIG_FTPCI100_MEM_BASE;
+ priv->mem_base = CONFIG_FTPCI100_MEM_BASE + CONFIG_FTPCI100_MEM_SIZE;
+
+ ftpci100 = (struct ftpci100_ahbc *)priv->reg_base;
+
+ pci_config_addr = (u32) &ftpci100->conf;
+ pci_config_data = (u32) &ftpci100->data;
+
+ /* print device name */
+ printf("FTPCI100\n");
+
+ /* dump basic configuration */
+ debug("%s: Config addr is %08X, data port is %08X\n",
+ __func__, pci_config_addr, pci_config_data);
+
+ /* PCI memory space */
+ pci_set_region(hose->regions + 0,
+ CONFIG_PCI_MEM_BUS,
+ CONFIG_PCI_MEM_PHYS,
+ CONFIG_PCI_MEM_SIZE,
+ PCI_REGION_MEM);
+ hose->region_count++;
+
+ /* PCI IO space */
+ pci_set_region(hose->regions + 1,
+ CONFIG_PCI_IO_BUS,
+ CONFIG_PCI_IO_PHYS,
+ CONFIG_PCI_IO_SIZE,
+ PCI_REGION_IO);
+ hose->region_count++;
+
+#if defined(CONFIG_PCI_SYS_BUS)
+ /* PCI System Memory space */
+ pci_set_region(hose->regions + 2,
+ CONFIG_PCI_SYS_BUS,
+ CONFIG_PCI_SYS_PHYS,
+ CONFIG_PCI_SYS_SIZE,
+ PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
+ hose->region_count++;
+#endif
+
+ /* setup indirect read/write function */
+ pci_setup_indirect(hose, pci_config_addr, pci_config_data);
+
+ /* register hose */
+ pci_register_hose(hose);
+}
+
+void pci_ftpci_init(void)
+{
+ struct ftpci100_data *priv = NULL;
+ struct pci_controller *hose = (struct pci_controller *)&local_hose;
+ pci_dev_t bridge_num;
+
+ struct pci_device_id bridge_ids[] = {
+ {FTPCI100_BRIDGE_VENDORID, FTPCI100_BRIDGE_DEVICEID},
+ {0, 0}
+ };
+
+ priv = malloc(sizeof(struct ftpci100_data));
+
+ if (!priv) {
+ printf("%s(): failed to malloc priv\n", __func__);
+ return;
+ }
+
+ memset(priv, 0, sizeof(struct ftpci100_data));
+
+ ftpci_preinit(priv);
+
+ debug("Device bus dev func deviceID vendorID pin address" \
+ " size class\n");
+
+ pci_bus_scan(priv);
+
+ /*
+ * Setup the PCI Bridge Window to 1GB,
+ * it will cause USB OHCI Host controller Unrecoverable Error
+ * if it is not set.
+ */
+ bridge_num = pci_find_devices(bridge_ids, 0);
+ if (bridge_num == -1) {
+ printf("PCI Bridge not found\n");
+ return;
+ }
+ pci_hose_write_config_dword(hose, bridge_num, PCI_MEM_BASE_SIZE1,
+ FTPCI100_BASE_ADR_SIZE(1024));
+}
diff --git a/drivers/pci/pci_ftpci100.h b/drivers/pci/pci_ftpci100.h
new file mode 100644
index 000000000..19c81a8e7
--- /dev/null
+++ b/drivers/pci/pci_ftpci100.h
@@ -0,0 +1,94 @@
+/*
+ * Faraday FTPCI100 PCI Bridge Controller Device Driver Implementation
+ *
+ * Copyright (C) 2010 Andes Technology Corporation
+ * Gavin Guo, Andes Technology Corporation <gavinguo@andestech.com>
+ * Macpaul Lin, Andes Technology Corporation <macpaul@andestech.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __FTPCI100_H
+#define __FTPCI100_H
+
+/* AHB Control Registers */
+struct ftpci100_ahbc {
+ unsigned int iosize; /* 0x00 - I/O Space Size Signal */
+ unsigned int prot; /* 0x04 - AHB Protection */
+ unsigned int rsved[8]; /* 0x08-0x24 - Reserved */
+ unsigned int conf; /* 0x28 - PCI Configuration */
+ unsigned int data; /* 0x2c - PCI Configuration DATA */
+};
+
+/*
+ * FTPCI100_IOSIZE_REG's constant definitions
+ */
+#define FTPCI100_BASE_IO_SIZE(x) (ffs(x) - 1) /* 1M - 2048M */
+
+/*
+ * PCI Configuration Register
+ */
+#define PCI_INT_MASK 0x4c
+#define PCI_MEM_BASE_SIZE1 0x50
+#define PCI_MEM_BASE_SIZE2 0x54
+#define PCI_MEM_BASE_SIZE3 0x58
+
+/*
+ * PCI_INT_MASK's bit definitions
+ */
+#define PCI_INTA_ENABLE (1 << 22)
+#define PCI_INTB_ENABLE (1 << 23)
+#define PCI_INTC_ENABLE (1 << 24)
+#define PCI_INTD_ENABLE (1 << 25)
+
+/*
+ * PCI_MEM_BASE_SIZE1's constant definitions
+ */
+#define FTPCI100_BASE_ADR_SIZE(x) ((ffs(x) - 1) << 16) /* 1M - 2048M */
+
+#define FTPCI100_MAX_FUNCTIONS 20
+#define PCI_IRQ_LINES 4
+
+#define MAX_BUS_NUM 256
+#define MAX_DEV_NUM 32
+#define MAX_FUN_NUM 8
+
+#define PCI_MAX_BAR_PER_FUNC 6
+
+/*
+ * PCI_MEM_SIZE
+ */
+#define FTPCI100_MEM_SIZE(x) (ffs(x) << 24)
+
+/* This definition is used by pci_ftpci_init() */
+#define FTPCI100_BRIDGE_VENDORID 0x159b
+#define FTPCI100_BRIDGE_DEVICEID 0x4321
+
+struct pcibar {
+ unsigned int size;
+ unsigned int addr;
+};
+
+struct pci_config {
+ unsigned int bus;
+ unsigned int dev; /* device */
+ unsigned int func;
+ unsigned int pin;
+ unsigned short v_id; /* vendor id */
+ unsigned short d_id; /* device id */
+ struct pcibar bar[PCI_MAX_BAR_PER_FUNC + 1];
+};
+
+#endif
diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile
new file mode 100644
index 000000000..be11c8b59
--- /dev/null
+++ b/drivers/tpm/Makefile
@@ -0,0 +1,43 @@
+# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# 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; either version 2 of
+# the License, or (at your option) any later version.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB := $(obj)libtpm.o
+
+COBJS-$(CONFIG_GENERIC_LPC_TPM) = generic_lpc_tpm.o
+
+COBJS := $(COBJS-y)
+SRCS := $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+
+all: $(LIB)
+
+$(LIB): $(obj).depend $(OBJS)
+ $(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/tpm/generic_lpc_tpm.c b/drivers/tpm/generic_lpc_tpm.c
new file mode 100644
index 000000000..6c494eb98
--- /dev/null
+++ b/drivers/tpm/generic_lpc_tpm.c
@@ -0,0 +1,495 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * The code in this file is based on the article "Writing a TPM Device Driver"
+ * published on http://ptgmedia.pearsoncmg.com.
+ *
+ * One principal difference is that in the simplest config the other than 0
+ * TPM localities do not get mapped by some devices (for instance, by Infineon
+ * slb9635), so this driver provides access to locality 0 only.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <tpm.h>
+
+#define PREFIX "lpc_tpm: "
+
+struct tpm_locality {
+ u32 access;
+ u8 padding0[4];
+ u32 int_enable;
+ u8 vector;
+ u8 padding1[3];
+ u32 int_status;
+ u32 int_capability;
+ u32 tpm_status;
+ u8 padding2[8];
+ u8 data;
+ u8 padding3[3803];
+ u32 did_vid;
+ u8 rid;
+ u8 padding4[251];
+};
+
+/*
+ * This pointer refers to the TPM chip, 5 of its localities are mapped as an
+ * array.
+ */
+#define TPM_TOTAL_LOCALITIES 5
+static struct tpm_locality *lpc_tpm_dev =
+ (struct tpm_locality *)CONFIG_TPM_TIS_BASE_ADDRESS;
+
+/* Some registers' bit field definitions */
+#define TIS_STS_VALID (1 << 7) /* 0x80 */
+#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
+#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
+#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
+#define TIS_STS_EXPECT (1 << 3) /* 0x08 */
+#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
+
+#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
+#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
+#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
+#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
+#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
+#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
+#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
+
+#define TIS_STS_BURST_COUNT_MASK (0xffff)
+#define TIS_STS_BURST_COUNT_SHIFT (8)
+
+/*
+ * Error value returned if a tpm register does not enter the expected state
+ * after continuous polling. No actual TPM register reading ever returns -1,
+ * so this value is a safe error indication to be mixed with possible status
+ * register values.
+ */
+#define TPM_TIMEOUT_ERR (-1)
+
+/* Error value returned on various TPM driver errors. */
+#define TPM_DRIVER_ERR (1)
+
+ /* 1 second is plenty for anything TPM does. */
+#define MAX_DELAY_US (1000 * 1000)
+
+/* Retrieve burst count value out of the status register contents. */
+static u16 burst_count(u32 status)
+{
+ return (status >> TIS_STS_BURST_COUNT_SHIFT) & TIS_STS_BURST_COUNT_MASK;
+}
+
+/*
+ * Structures defined below allow creating descriptions of TPM vendor/device
+ * ID information for run time discovery. The only device the system knows
+ * about at this time is Infineon slb9635.
+ */
+struct device_name {
+ u16 dev_id;
+ const char * const dev_name;
+};
+
+struct vendor_name {
+ u16 vendor_id;
+ const char *vendor_name;
+ const struct device_name *dev_names;
+};
+
+static const struct device_name infineon_devices[] = {
+ {0xb, "SLB9635 TT 1.2"},
+ {0}
+};
+
+static const struct vendor_name vendor_names[] = {
+ {0x15d1, "Infineon", infineon_devices},
+};
+
+/*
+ * Cached vendor/device ID pair to indicate that the device has been already
+ * discovered.
+ */
+static u32 vendor_dev_id;
+
+/* TPM access wrappers to support tracing */
+static u8 tpm_read_byte(const u8 *ptr)
+{
+ u8 ret = readb(ptr);
+ debug(PREFIX "Read reg 0x%4.4x returns 0x%2.2x\n",
+ (u32)ptr - (u32)lpc_tpm_dev, ret);
+ return ret;
+}
+
+static u32 tpm_read_word(const u32 *ptr)
+{
+ u32 ret = readl(ptr);
+ debug(PREFIX "Read reg 0x%4.4x returns 0x%8.8x\n",
+ (u32)ptr - (u32)lpc_tpm_dev, ret);
+ return ret;
+}
+
+static void tpm_write_byte(u8 value, u8 *ptr)
+{
+ debug(PREFIX "Write reg 0x%4.4x with 0x%2.2x\n",
+ (u32)ptr - (u32)lpc_tpm_dev, value);
+ writeb(value, ptr);
+}
+
+static void tpm_write_word(u32 value, u32 *ptr)
+{
+ debug(PREFIX "Write reg 0x%4.4x with 0x%8.8x\n",
+ (u32)ptr - (u32)lpc_tpm_dev, value);
+ writel(value, ptr);
+}
+
+/*
+ * tis_wait_reg()
+ *
+ * Wait for at least a second for a register to change its state to match the
+ * expected state. Normally the transition happens within microseconds.
+ *
+ * @reg - pointer to the TPM register
+ * @mask - bitmask for the bitfield(s) to watch
+ * @expected - value the field(s) are supposed to be set to
+ *
+ * Returns the register contents in case the expected value was found in the
+ * appropriate register bits, or TPM_TIMEOUT_ERR on timeout.
+ */
+static u32 tis_wait_reg(u32 *reg, u8 mask, u8 expected)
+{
+ u32 time_us = MAX_DELAY_US;
+
+ while (time_us > 0) {
+ u32 value = tpm_read_word(reg);
+ if ((value & mask) == expected)
+ return value;
+ udelay(1); /* 1 us */
+ time_us--;
+ }
+ return TPM_TIMEOUT_ERR;
+}
+
+/*
+ * Probe the TPM device and try determining its manufacturer/device name.
+ *
+ * Returns 0 on success (the device is found or was found during an earlier
+ * invocation) or TPM_DRIVER_ERR if the device is not found.
+ */
+int tis_init(void)
+{
+ u32 didvid = tpm_read_word(&lpc_tpm_dev[0].did_vid);
+ int i;
+ const char *device_name = "unknown";
+ const char *vendor_name = device_name;
+ u16 vid, did;
+
+ if (vendor_dev_id)
+ return 0; /* Already probed. */
+
+ if (!didvid || (didvid == 0xffffffff)) {
+ printf("%s: No TPM device found\n", __func__);
+ return TPM_DRIVER_ERR;
+ }
+
+ vendor_dev_id = didvid;
+
+ vid = didvid & 0xffff;
+ did = (didvid >> 16) & 0xffff;
+ for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
+ int j = 0;
+ u16 known_did;
+
+ if (vid == vendor_names[i].vendor_id)
+ vendor_name = vendor_names[i].vendor_name;
+
+ while ((known_did = vendor_names[i].dev_names[j].dev_id) != 0) {
+ if (known_did == did) {
+ device_name =
+ vendor_names[i].dev_names[j].dev_name;
+ break;
+ }
+ j++;
+ }
+ break;
+ }
+
+ printf("Found TPM %s by %s\n", device_name, vendor_name);
+ return 0;
+}
+
+/*
+ * tis_senddata()
+ *
+ * send the passed in data to the TPM device.
+ *
+ * @data - address of the data to send, byte by byte
+ * @len - length of the data to send
+ *
+ * Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does
+ * not accept the entire command).
+ */
+static u32 tis_senddata(const u8 * const data, u32 len)
+{
+ u32 offset = 0;
+ u16 burst = 0;
+ u32 max_cycles = 0;
+ u8 locality = 0;
+ u32 value;
+
+ value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
+ TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
+ if (value == TPM_TIMEOUT_ERR) {
+ printf("%s:%d - failed to get 'command_ready' status\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+ burst = burst_count(value);
+
+ while (1) {
+ unsigned count;
+
+ /* Wait till the device is ready to accept more data. */
+ while (!burst) {
+ if (max_cycles++ == MAX_DELAY_US) {
+ printf("%s:%d failed to feed %d bytes of %d\n",
+ __FILE__, __LINE__, len - offset, len);
+ return TPM_DRIVER_ERR;
+ }
+ udelay(1);
+ burst = burst_count(tpm_read_word(&lpc_tpm_dev
+ [locality].tpm_status));
+ }
+
+ max_cycles = 0;
+
+ /*
+ * Calculate number of bytes the TPM is ready to accept in one
+ * shot.
+ *
+ * We want to send the last byte outside of the loop (hence
+ * the -1 below) to make sure that the 'expected' status bit
+ * changes to zero exactly after the last byte is fed into the
+ * FIFO.
+ */
+ count = min(burst, len - offset - 1);
+ while (count--)
+ tpm_write_byte(data[offset++],
+ &lpc_tpm_dev[locality].data);
+
+ value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
+ TIS_STS_VALID, TIS_STS_VALID);
+
+ if ((value == TPM_TIMEOUT_ERR) || !(value & TIS_STS_EXPECT)) {
+ printf("%s:%d TPM command feed overflow\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ burst = burst_count(value);
+ if ((offset == (len - 1)) && burst) {
+ /*
+ * We need to be able to send the last byte to the
+ * device, so burst size must be nonzero before we
+ * break out.
+ */
+ break;
+ }
+ }
+
+ /* Send the last byte. */
+ tpm_write_byte(data[offset++], &lpc_tpm_dev[locality].data);
+ /*
+ * Verify that TPM does not expect any more data as part of this
+ * command.
+ */
+ value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
+ TIS_STS_VALID, TIS_STS_VALID);
+ if ((value == TPM_TIMEOUT_ERR) || (value & TIS_STS_EXPECT)) {
+ printf("%s:%d unexpected TPM status 0x%x\n",
+ __FILE__, __LINE__, value);
+ return TPM_DRIVER_ERR;
+ }
+
+ /* OK, sitting pretty, let's start the command execution. */
+ tpm_write_word(TIS_STS_TPM_GO, &lpc_tpm_dev[locality].tpm_status);
+ return 0;
+}
+
+/*
+ * tis_readresponse()
+ *
+ * read the TPM device response after a command was issued.
+ *
+ * @buffer - address where to read the response, byte by byte.
+ * @len - pointer to the size of buffer
+ *
+ * On success stores the number of received bytes to len and returns 0. On
+ * errors (misformatted TPM data or synchronization problems) returns
+ * TPM_DRIVER_ERR.
+ */
+static u32 tis_readresponse(u8 *buffer, u32 *len)
+{
+ u16 burst;
+ u32 value;
+ u32 offset = 0;
+ u8 locality = 0;
+ const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
+ u32 expected_count = *len;
+ int max_cycles = 0;
+
+ /* Wait for the TPM to process the command. */
+ value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
+ has_data, has_data);
+ if (value == TPM_TIMEOUT_ERR) {
+ printf("%s:%d failed processing command\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ do {
+ while ((burst = burst_count(value)) == 0) {
+ if (max_cycles++ == MAX_DELAY_US) {
+ printf("%s:%d TPM stuck on read\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+ udelay(1);
+ value = tpm_read_word(&lpc_tpm_dev
+ [locality].tpm_status);
+ }
+
+ max_cycles = 0;
+
+ while (burst-- && (offset < expected_count)) {
+ buffer[offset++] = tpm_read_byte(&lpc_tpm_dev
+ [locality].data);
+
+ if (offset == 6) {
+ /*
+ * We got the first six bytes of the reply,
+ * let's figure out how many bytes to expect
+ * total - it is stored as a 4 byte number in
+ * network order, starting with offset 2 into
+ * the body of the reply.
+ */
+ u32 real_length;
+ memcpy(&real_length,
+ buffer + 2,
+ sizeof(real_length));
+ expected_count = be32_to_cpu(real_length);
+
+ if ((expected_count < offset) ||
+ (expected_count > *len)) {
+ printf("%s:%d bad response size %d\n",
+ __FILE__, __LINE__,
+ expected_count);
+ return TPM_DRIVER_ERR;
+ }
+ }
+ }
+
+ /* Wait for the next portion. */
+ value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
+ TIS_STS_VALID, TIS_STS_VALID);
+ if (value == TPM_TIMEOUT_ERR) {
+ printf("%s:%d failed to read response\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ if (offset == expected_count)
+ break; /* We got all we needed. */
+
+ } while ((value & has_data) == has_data);
+
+ /*
+ * Make sure we indeed read all there was. The TIS_STS_VALID bit is
+ * known to be set.
+ */
+ if (value & TIS_STS_DATA_AVAILABLE) {
+ printf("%s:%d wrong receive status %x\n",
+ __FILE__, __LINE__, value);
+ return TPM_DRIVER_ERR;
+ }
+
+ /* Tell the TPM that we are done. */
+ tpm_write_word(TIS_STS_COMMAND_READY, &lpc_tpm_dev
+ [locality].tpm_status);
+ *len = offset;
+ return 0;
+}
+
+int tis_open(void)
+{
+ u8 locality = 0; /* we use locality zero for everything. */
+
+ if (tis_close())
+ return TPM_DRIVER_ERR;
+
+ /* now request access to locality. */
+ tpm_write_word(TIS_ACCESS_REQUEST_USE, &lpc_tpm_dev[locality].access);
+
+ /* did we get a lock? */
+ if (tis_wait_reg(&lpc_tpm_dev[locality].access,
+ TIS_ACCESS_ACTIVE_LOCALITY,
+ TIS_ACCESS_ACTIVE_LOCALITY) == TPM_TIMEOUT_ERR) {
+ printf("%s:%d - failed to lock locality %d\n",
+ __FILE__, __LINE__, locality);
+ return TPM_DRIVER_ERR;
+ }
+
+ tpm_write_word(TIS_STS_COMMAND_READY,
+ &lpc_tpm_dev[locality].tpm_status);
+ return 0;
+}
+
+int tis_close(void)
+{
+ u8 locality = 0;
+
+ if (tpm_read_word(&lpc_tpm_dev[locality].access) &
+ TIS_ACCESS_ACTIVE_LOCALITY) {
+ tpm_write_word(TIS_ACCESS_ACTIVE_LOCALITY,
+ &lpc_tpm_dev[locality].access);
+
+ if (tis_wait_reg(&lpc_tpm_dev[locality].access,
+ TIS_ACCESS_ACTIVE_LOCALITY, 0) ==
+ TPM_TIMEOUT_ERR) {
+ printf("%s:%d - failed to release locality %d\n",
+ __FILE__, __LINE__, locality);
+ return TPM_DRIVER_ERR;
+ }
+ }
+ return 0;
+}
+
+int tis_sendrecv(const u8 *sendbuf, size_t send_size,
+ u8 *recvbuf, size_t *recv_len)
+{
+ if (tis_senddata(sendbuf, send_size)) {
+ printf("%s:%d failed sending data to TPM\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ return tis_readresponse(recvbuf, recv_len);
+}