summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2014-07-04 17:26:05 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2014-07-04 17:26:05 +0200
commit45d593bc5ec78e26e3a967517ed7a51f27c8b91f (patch)
tree197cc45c686dc86b4537ce55313d5ec074671918 /drivers
parent6724928c6972842b73962dc5af70d26de9969582 (diff)
parent988ecf42691e101a35ecae83d26253e9590d14c6 (diff)
downloadbarebox-45d593bc5ec78e26e3a967517ed7a51f27c8b91f.tar.gz
barebox-45d593bc5ec78e26e3a967517ed7a51f27c8b91f.tar.xz
Merge branch 'for-next/mips'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/gpio/Kconfig16
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/gpio-jz4740.c140
-rw-r--r--drivers/gpio/gpio-malta-fpga-i2c.c186
-rw-r--r--drivers/i2c/busses/i2c-gpio.c55
-rw-r--r--drivers/net/Kconfig8
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/rtl8139.c605
-rw-r--r--drivers/pci/Kconfig29
-rw-r--r--drivers/pci/Makefile8
-rw-r--r--drivers/pci/bus.c110
-rw-r--r--drivers/pci/pci.c292
-rw-r--r--drivers/pci/pci_iomap.c29
15 files changed, 1480 insertions, 3 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 53e1e97560..12a9d8c7d8 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -27,5 +27,6 @@ source "drivers/pinctrl/Kconfig"
source "drivers/bus/Kconfig"
source "drivers/regulator/Kconfig"
source "drivers/reset/Kconfig"
+source "drivers/pci/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index ef3604f56c..1990e86bd9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -26,3 +26,4 @@ obj-y += pinctrl/
obj-y += bus/
obj-$(CONFIG_REGULATOR) += regulator/
obj-$(CONFIG_RESET_CONTROLLER) += reset/
+obj-$(CONFIG_PCI) += pci/
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7302955d87..f98a9c00ef 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -37,6 +37,22 @@ config GPIO_GENERIC_PLATFORM
config GPIO_IMX
def_bool ARCH_IMX
+config GPIO_JZ4740
+ bool "GPIO support for Ingenic SoCs"
+ depends on MACH_MIPS_XBURST
+ help
+ Say yes here to enable the GPIO driver for the Ingenic SoCs.
+
+config GPIO_MALTA_FPGA_I2C
+ bool "Malta CBUS FPGA I2C GPIO"
+ depends on MACH_MIPS_MALTA
+ help
+ Support access to the CBUS FPGA I2C lines through the gpio library.
+
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
config GPIO_OMAP
def_bool ARCH_OMAP
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 68a76a3745..22d2ac0706 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -5,6 +5,8 @@ obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
obj-$(CONFIG_GPIO_IMX) += gpio-imx.o
+obj-$(CONFIG_GPIO_JZ4740) += gpio-jz4740.o
+obj-$(CONFIG_GPIO_MALTA_FPGA_I2C) += gpio-malta-fpga-i2c.o
obj-$(CONFIG_GPIO_ORION) += gpio-orion.o
obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
diff --git a/drivers/gpio/gpio-jz4740.c b/drivers/gpio/gpio-jz4740.c
new file mode 100644
index 0000000000..3c8efad8f3
--- /dev/null
+++ b/drivers/gpio/gpio-jz4740.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2013, 2014 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * Based on Linux JZ4740 platform GPIO support:
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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.
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <gpio.h>
+#include <malloc.h>
+
+#define JZ_REG_GPIO_PIN 0x00
+#define JZ_REG_GPIO_DATA 0x10
+#define JZ_REG_GPIO_DATA_SET 0x14
+#define JZ_REG_GPIO_DATA_CLEAR 0x18
+#define JZ_REG_GPIO_DIRECTION 0x60
+#define JZ_REG_GPIO_DIRECTION_SET 0x64
+#define JZ_REG_GPIO_DIRECTION_CLEAR 0x68
+
+#define GPIO_TO_BIT(gpio) BIT(gpio & 0x1f)
+#define CHIP_TO_REG(chip, reg) (gpio_chip_to_jz4740_gpio_chip(chip)->base + (reg))
+
+struct jz4740_gpio_chip {
+ void __iomem *base;
+ struct gpio_chip chip;
+};
+
+static inline struct jz4740_gpio_chip *gpio_chip_to_jz4740_gpio_chip(struct gpio_chip *chip)
+{
+ return container_of(chip, struct jz4740_gpio_chip, chip);
+}
+
+static int jz4740_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ return !!(readl(CHIP_TO_REG(chip, JZ_REG_GPIO_PIN)) & BIT(gpio));
+}
+
+static void jz4740_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value)
+{
+ uint32_t __iomem *reg = CHIP_TO_REG(chip, JZ_REG_GPIO_DATA_SET);
+ reg += !value;
+ writel(BIT(gpio), reg);
+}
+
+static int jz4740_gpio_get_direction(struct gpio_chip *chip, unsigned gpio)
+{
+ if (readl(CHIP_TO_REG(chip, JZ_REG_GPIO_DIRECTION)) & BIT(gpio))
+ return GPIOF_DIR_OUT;
+
+ return GPIOF_DIR_IN;
+}
+
+static int jz4740_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ writel(BIT(gpio), CHIP_TO_REG(chip, JZ_REG_GPIO_DIRECTION_CLEAR));
+
+ return 0;
+}
+
+static int jz4740_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ jz4740_gpio_set_value(chip, gpio, value);
+ writel(BIT(gpio), CHIP_TO_REG(chip, JZ_REG_GPIO_DIRECTION_SET));
+
+ return 0;
+}
+
+static struct gpio_ops jz4740_gpio_ops = {
+ .direction_input = jz4740_gpio_direction_input,
+ .direction_output = jz4740_gpio_direction_output,
+ .get_direction = jz4740_gpio_get_direction,
+ .get = jz4740_gpio_get_value,
+ .set = jz4740_gpio_set_value,
+};
+
+static int jz4740_gpio_probe(struct device_d *dev)
+{
+ void __iomem *base;
+ struct jz4740_gpio_chip *jz4740_gpio;
+ int ret;
+
+ base = dev_request_mem_region(dev, 0);
+ if (!base) {
+ dev_err(dev, "could not get memory region\n");
+ return -ENODEV;
+ }
+
+ jz4740_gpio = xzalloc(sizeof(*jz4740_gpio));
+ jz4740_gpio->base = base;
+ jz4740_gpio->chip.ops = &jz4740_gpio_ops;
+ jz4740_gpio->chip.base = -1; /* dev->id * 32; */
+ jz4740_gpio->chip.ngpio = 32;
+ jz4740_gpio->chip.dev = dev;
+
+ ret = gpiochip_add(&jz4740_gpio->chip);
+ if (ret) {
+ dev_err(dev, "couldn't add gpiochip\n");
+ free(jz4740_gpio);
+ return ret;
+ }
+
+ dev_dbg(dev, "probed gpiochip%d with base %d\n",
+ dev->id, jz4740_gpio->chip.base);
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id jz4740_gpio_dt_ids[] = {
+ {
+ .compatible = "ingenic,jz4740-gpio",
+ }, {
+ /* sentinel */
+ },
+};
+
+static struct driver_d jz4740_gpio_driver = {
+ .name = "jz4740-gpio",
+ .probe = jz4740_gpio_probe,
+ .of_compatible = DRV_OF_COMPAT(jz4740_gpio_dt_ids),
+};
+
+static int jz4740_gpio_init(void)
+{
+ return platform_driver_register(&jz4740_gpio_driver);
+}
+coredevice_initcall(jz4740_gpio_init);
diff --git a/drivers/gpio/gpio-malta-fpga-i2c.c b/drivers/gpio/gpio-malta-fpga-i2c.c
new file mode 100644
index 0000000000..d6995aad81
--- /dev/null
+++ b/drivers/gpio/gpio-malta-fpga-i2c.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This file is part of barebox.
+ * 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.
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <gpio.h>
+#include <linux/err.h>
+#include <malloc.h>
+
+struct malta_i2c_gpio {
+ void __iomem *base;
+ struct gpio_chip chip;
+};
+
+#define MALTA_I2CINP 0
+#define MALTA_I2COE 0x8
+#define MALTA_I2COUT 0x10
+#define MALTA_I2CSEL 0x18
+
+static inline struct malta_i2c_gpio *chip_to_malta_i2c_gpio(struct gpio_chip *c)
+{
+ return container_of(c, struct malta_i2c_gpio, chip);
+}
+
+static inline void malta_i2c_gpio_write(struct malta_i2c_gpio *sc,
+ u32 v, int reg)
+{
+ __raw_writel(v, sc->base + reg);
+}
+
+static inline u32 malta_i2c_gpio_read(struct malta_i2c_gpio *sc, int reg)
+{
+ return __raw_readl(sc->base + reg);
+}
+
+static inline int malta_i2c_gpio_get_bit(struct malta_i2c_gpio *sc,
+ int reg, int bit)
+{
+ return !!(malta_i2c_gpio_read(sc, reg) & BIT(bit));
+}
+
+static inline void malta_i2c_gpio_set_bit(struct malta_i2c_gpio *sc,
+ int reg, int bit, int v)
+{
+ u32 t;
+
+ t = malta_i2c_gpio_read(sc, reg);
+ if (v)
+ t |= BIT(bit);
+ else
+ t &= ~BIT(bit);
+
+ malta_i2c_gpio_write(sc, t, reg);
+}
+
+static int malta_i2c_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ struct malta_i2c_gpio *sc = chip_to_malta_i2c_gpio(chip);
+
+ malta_i2c_gpio_set_bit(sc, MALTA_I2COE, gpio, 0);
+
+ return 0;
+}
+
+static int malta_i2c_gpio_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int v)
+{
+ struct malta_i2c_gpio *sc = chip_to_malta_i2c_gpio(chip);
+
+ malta_i2c_gpio_set_bit(sc, MALTA_I2COUT, gpio, v);
+ malta_i2c_gpio_set_bit(sc, MALTA_I2COE, gpio, 1);
+
+ return 0;
+}
+
+static int malta_i2c_gpio_get_direction(struct gpio_chip *chip, unsigned gpio)
+{
+ struct malta_i2c_gpio *sc = chip_to_malta_i2c_gpio(chip);
+
+ if (malta_i2c_gpio_get_bit(sc, MALTA_I2COE, gpio))
+ return GPIOF_DIR_OUT;
+
+ return GPIOF_DIR_IN;
+}
+
+static int malta_i2c_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct malta_i2c_gpio *sc = chip_to_malta_i2c_gpio(chip);
+ int v;
+
+ v = malta_i2c_gpio_get_bit(sc, MALTA_I2CINP, gpio);
+
+ pr_debug("%s: gpio_chip=%p gpio=%d value=%d\n",
+ __func__, chip, gpio, v);
+
+ return v;
+}
+
+static void malta_i2c_gpio_set_value(struct gpio_chip *chip,
+ unsigned gpio, int v)
+{
+ struct malta_i2c_gpio *sc = chip_to_malta_i2c_gpio(chip);
+
+ pr_debug("%s: gpio_chip=%p gpio=%d value=%d\n",
+ __func__, chip, gpio, v);
+
+ malta_i2c_gpio_set_bit(sc, MALTA_I2COUT, gpio, v);
+}
+
+static struct gpio_ops malta_i2c_gpio_ops = {
+ .direction_input = malta_i2c_gpio_direction_input,
+ .direction_output = malta_i2c_gpio_direction_output,
+ .get_direction = malta_i2c_gpio_get_direction,
+ .get = malta_i2c_gpio_get_value,
+ .set = malta_i2c_gpio_set_value,
+};
+
+static int malta_i2c_gpio_probe(struct device_d *dev)
+{
+ void __iomem *gpio_base;
+ struct malta_i2c_gpio *sc;
+ int ret;
+
+ gpio_base = dev_request_mem_region(dev, 0);
+ if (!gpio_base) {
+ dev_err(dev, "could not get memory region\n");
+ return -ENODEV;
+ }
+
+ sc = xzalloc(sizeof(*sc));
+ sc->base = gpio_base;
+ sc->chip.ops = &malta_i2c_gpio_ops;
+ sc->chip.base = -1;
+ sc->chip.ngpio = 2;
+ sc->chip.dev = dev;
+
+ ret = gpiochip_add(&sc->chip);
+ if (ret) {
+ dev_err(dev, "couldn't add gpiochip\n");
+ free(sc);
+ return ret;
+ }
+
+ malta_i2c_gpio_write(sc, 1, MALTA_I2CSEL);
+
+ dev_info(dev, "probed gpiochip%d with base %d\n",
+ dev->id, sc->chip.base);
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id malta_i2c_gpio_dt_ids[] = {
+ {
+ .compatible = "mti,malta-fpga-i2c-gpio",
+ }, {
+ /* sentinel */
+ },
+};
+
+static struct driver_d malta_i2c_gpio_driver = {
+ .name = "malta-fpga-i2c-gpio",
+ .probe = malta_i2c_gpio_probe,
+ .of_compatible = DRV_OF_COMPAT(malta_i2c_gpio_dt_ids),
+};
+
+static int malta_i2c_gpio_driver_init(void)
+{
+ return platform_driver_register(&malta_i2c_gpio_driver);
+}
+coredevice_initcall(malta_i2c_gpio_driver_init);
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index 8b49c2c1bf..b4a0ecdb20 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -15,6 +15,7 @@
#include <i2c/i2c-gpio.h>
#include <init.h>
#include <gpio.h>
+#include <of_gpio.h>
struct i2c_gpio_private_data {
struct i2c_adapter adap;
@@ -83,6 +84,41 @@ static int i2c_gpio_getscl(void *data)
return gpio_get_value(pdata->scl_pin);
}
+static int of_i2c_gpio_probe(struct device_node *np,
+ struct i2c_gpio_platform_data *pdata)
+{
+ u32 reg;
+
+ if (!IS_ENABLED(CONFIG_OFDEVICE))
+ return -ENODEV;
+
+ if (of_gpio_count(np) < 2)
+ return -ENODEV;
+
+ pdata->sda_pin = of_get_gpio(np, 0);
+ pdata->scl_pin = of_get_gpio(np, 1);
+
+ if (!gpio_is_valid(pdata->sda_pin) || !gpio_is_valid(pdata->scl_pin)) {
+ pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n",
+ np->full_name, pdata->sda_pin, pdata->scl_pin);
+ return -ENODEV;
+ }
+
+ of_property_read_u32(np, "i2c-gpio,delay-us", &pdata->udelay);
+
+ if (!of_property_read_u32(np, "i2c-gpio,timeout-ms", &reg))
+ pdata->timeout_ms = reg;
+
+ pdata->sda_is_open_drain =
+ of_property_read_bool(np, "i2c-gpio,sda-open-drain");
+ pdata->scl_is_open_drain =
+ of_property_read_bool(np, "i2c-gpio,scl-open-drain");
+ pdata->scl_is_output_only =
+ of_property_read_bool(np, "i2c-gpio,scl-output-only");
+
+ return 0;
+}
+
static int i2c_gpio_probe(struct device_d *dev)
{
struct i2c_gpio_private_data *priv;
@@ -97,9 +133,15 @@ static int i2c_gpio_probe(struct device_d *dev)
bit_data = &priv->bit_data;
pdata = &priv->pdata;
- if (!dev->platform_data)
- return -ENXIO;
- memcpy(pdata, dev->platform_data, sizeof(*pdata));
+ if (dev->device_node) {
+ ret = of_i2c_gpio_probe(dev->device_node, pdata);
+ if (ret)
+ return ret;
+ } else {
+ if (!dev->platform_data)
+ return -ENXIO;
+ memcpy(pdata, dev->platform_data, sizeof(*pdata));
+ }
ret = gpio_request(pdata->sda_pin, "sda");
if (ret)
@@ -144,6 +186,7 @@ static int i2c_gpio_probe(struct device_d *dev)
adap->algo_data = bit_data;
adap->dev.parent = dev;
+ adap->dev.device_node = dev->device_node;
adap->nr = dev->id;
ret = i2c_bit_add_numbered_bus(adap);
@@ -165,8 +208,14 @@ err_request_sda:
return ret;
}
+static struct of_device_id i2c_gpio_dt_ids[] = {
+ { .compatible = "i2c-gpio", },
+ { /* sentinel */ }
+};
+
static struct driver_d i2c_gpio_driver = {
.name = "i2c-gpio",
.probe = i2c_gpio_probe,
+ .of_compatible = DRV_OF_COMPAT(i2c_gpio_dt_ids),
};
device_platform_driver(i2c_gpio_driver);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 7a0d5e107b..975c92723c 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -130,6 +130,14 @@ config DRIVER_NET_ORION
select PHYLIB
select MDIO_MVEBU
+config DRIVER_NET_RTL8139
+ bool "RealTek RTL-8139 PCI Ethernet driver"
+ depends on PCI
+ select PHYLIB
+ help
+ This is a driver for the Fast Ethernet PCI network cards based on
+ the RTL 8139 chips.
+
config DRIVER_NET_SMC911X
bool "smc911x ethernet driver"
select PHYLIB
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 65f0d8b387..d9070611c7 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_DRIVER_NET_MICREL) += ksz8864rmn.o
obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o
obj-$(CONFIG_DRIVER_NET_NETX) += netx_eth.o
obj-$(CONFIG_DRIVER_NET_ORION) += orion-gbe.o
+obj-$(CONFIG_DRIVER_NET_RTL8139) += rtl8139.o
obj-$(CONFIG_DRIVER_NET_SMC911X) += smc911x.o
obj-$(CONFIG_DRIVER_NET_SMC91111) += smc91111.o
obj-$(CONFIG_DRIVER_NET_TAP) += tap.o
diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c
new file mode 100644
index 0000000000..b24a083a56
--- /dev/null
+++ b/drivers/net/rtl8139.c
@@ -0,0 +1,605 @@
+#include <common.h>
+#include <net.h>
+#include <malloc.h>
+#include <init.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <io.h>
+#include <linux/phy.h>
+#include <linux/pci.h>
+
+#include <asm/dma-mapping.h>
+
+#define RTL8139_DEBUG
+#undef RTL8139_DEBUG
+
+/*
+ * Receive ring size
+ * Warning: 64K ring has hardware issues and may lock up.
+ */
+#define RX_BUF_IDX 0 /* 8K ring */
+#define RX_BUF_LEN (8192 << RX_BUF_IDX)
+#define RX_BUF_PAD 16
+#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
+
+#if RX_BUF_LEN == 65536
+#define RX_BUF_TOT_LEN RX_BUF_LEN
+#else
+#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
+#endif
+
+/* Number of Tx descriptor registers. */
+#define NUM_TX_DESC 4
+
+/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/
+#define MAX_ETH_FRAME_SIZE 1536
+
+/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
+#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE
+#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC)
+
+/* PCI Tuning Parameters
+ Threshold is bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
+
+/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */
+#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */
+#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
+#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */
+
+struct rtl8139_priv {
+ struct eth_device edev;
+ void __iomem *base;
+ struct pci_dev *pci_dev;
+ unsigned char *rx_ring;
+ unsigned int cur_rx; /* RX buf index of next pkt */
+ dma_addr_t rx_ring_dma;
+
+ u32 rx_config;
+ unsigned int tx_flag;
+ unsigned long cur_tx;
+ unsigned long dirty_tx;
+ unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */
+ unsigned char *tx_bufs; /* Tx bounce buffer region. */
+ dma_addr_t tx_bufs_dma;
+
+ struct mii_bus miibus;
+};
+
+#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
+
+/* Registers */
+#define MAC0 0x00
+#define MAR0 0x08
+#define TxStatus0 0x10
+
+enum TxStatusBits {
+ TxHostOwns = 0x2000,
+ TxUnderrun = 0x4000,
+ TxStatOK = 0x8000,
+ TxOutOfWindow = 0x20000000,
+ TxAborted = 0x40000000,
+ TxCarrierLost = 0x80000000,
+};
+
+#define TxAddr0 0x20
+#define RxBuf 0x30
+#define ChipCmd 0x37
+#define CmdReset 0x10
+#define CmdRxEnb 0x08
+#define CmdTxEnb 0x04
+#define RxBufEmpty 0x01
+#define RxBufPtr 0x38
+#define RxBufAddr 0x3A
+#define IntrMask 0x3C
+#define IntrStatus 0x3E
+#define PCIErr 0x8000
+#define PCSTimeout 0x4000
+#define RxFIFOOver 0x0040
+#define RxUnderrun 0x0020
+#define RxOverflow 0x0010
+#define TxErr 0x0008
+#define TxOK 0x0004
+#define RxErr 0x0002
+#define RxOK 0x0001
+#define RxAckBits (RxFIFOOver | RxOverflow | RxOK)
+
+#define TxConfig 0x40
+/* Bits in TxConfig. */
+enum tx_config_bits {
+ /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
+ TxIFGShift = 24,
+ TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
+ TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
+ TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
+ TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
+
+ TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
+ TxCRC = (1 << 16), /* DISABLE Tx pkt CRC append */
+ TxClearAbt = (1 << 0), /* Clear abort (WO) */
+ TxDMAShift = 8, /* DMA burst value (0-7) is shifted X many bits */
+ TxRetryShift = 4, /* TXRR value (0-15) is shifted X many bits */
+
+ TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
+};
+
+#define RxConfig 0x44
+ /* rx fifo threshold */
+#define RxCfgFIFOShift 13
+#define RxCfgFIFONone (7 << RxCfgFIFOShift)
+ /* Max DMA burst */
+#define RxCfgDMAShift 8
+#define RxCfgDMAUnlimited (7 << RxCfgDMAShift)
+ /* rx ring buffer length */
+#define RxCfgRcv8K 0
+#define RxCfgRcv16K (1 << 11)
+#define RxCfgRcv32K (1 << 12)
+#define RxCfgRcv64K ((1 << 11) | (1 << 12))
+ /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
+#define RxNoWrap (1 << 7)
+#define AcceptErr 0x20
+#define AcceptRunt 0x10
+#define AcceptBroadcast 0x08
+#define AcceptMulticast 0x04
+#define AcceptMyPhys 0x02
+#define AcceptAllPhys 0x01
+
+#define RxMissed 0x4C
+#define Cfg9346 0x50
+#define Cfg9346_Lock 0x00
+#define Cfg9346_Unlock 0xC0
+#define BasicModeCtrl 0x62
+#define BasicModeStatus 0x64
+#define NWayAdvert 0x66
+#define NWayLPAR 0x68
+#define NWayExpansion 0x6a
+
+static const char mii_2_8139_map[8] = {
+ BasicModeCtrl,
+ BasicModeStatus,
+ 0,
+ 0,
+ NWayAdvert,
+ NWayLPAR,
+ NWayExpansion,
+ 0
+};
+
+/* write MMIO register */
+#define RTL_W8(priv, reg, val) writeb(val, ((char *)(priv->base) + reg))
+#define RTL_W16(priv, reg, val) writew(val, ((char *)(priv->base) + reg))
+#define RTL_W32(priv, reg, val) writel(val, ((char *)(priv->base) + reg))
+
+/* read MMIO register */
+#define RTL_R8(priv, reg) readb(((char *)(priv->base) + reg))
+#define RTL_R16(priv, reg) readw(((char *)(priv->base) + reg))
+#define RTL_R32(priv, reg) readl(((char *)(priv->base) + reg))
+
+/* write MMIO register, with flush */
+/* Flush avoids rtl8139 bug w/ posted MMIO writes */
+static inline void RTL_W8_F(struct rtl8139_priv *priv, int reg, int val)
+{
+ RTL_W8(priv, reg, val);
+ RTL_R8(priv, reg);
+}
+
+static inline void RTL_W16_F(struct rtl8139_priv *priv, int reg, int val)
+{
+ RTL_W16(priv, reg, val);
+ RTL_R16(priv, reg);
+}
+
+static inline void RTL_W32_F(struct rtl8139_priv *priv, int reg, int val)
+{
+ RTL_W32(priv, reg, val);
+ RTL_R32(priv, reg);
+}
+
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv8K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+
+static const unsigned int rtl8139_tx_config =
+ TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift);
+
+static void rtl8139_chip_reset(struct rtl8139_priv *priv)
+{
+ int i;
+
+ /* Soft reset the chip. */
+ RTL_W8(priv, ChipCmd, CmdReset);
+
+ /* Check that the chip has finished the reset. */
+ for (i = 1000; i > 0; i--) {
+ if ((RTL_R8(priv, ChipCmd) & CmdReset) == 0)
+ break;
+ udelay(10);
+ }
+}
+
+static void __set_rx_mode(struct rtl8139_priv *priv)
+{
+ u32 mc_filter[2]; /* Multicast hash filter */
+ int rx_mode;
+ u32 tmp;
+
+ rx_mode =
+ AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
+ AcceptAllPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+
+ /* We can safely update without stopping the chip. */
+ tmp = rtl8139_rx_config | rx_mode;
+ if (priv->rx_config != tmp) {
+ RTL_W32_F(priv, RxConfig, tmp);
+ priv->rx_config = tmp;
+ }
+
+ RTL_W32_F(priv, MAR0 + 0, mc_filter[0]);
+ RTL_W32_F(priv, MAR0 + 4, mc_filter[1]);
+}
+
+/* Start the hardware at open or resume. */
+static void rtl8139_hw_start(struct rtl8139_priv *priv)
+{
+ u32 i;
+ u8 tmp;
+
+ rtl8139_chip_reset(priv);
+
+ /* unlock Config[01234] and BMCR register writes */
+ RTL_W8_F(priv, Cfg9346, Cfg9346_Unlock);
+
+ priv->cur_rx = 0;
+
+ /* init Rx ring buffer DMA address */
+ RTL_W32_F(priv, RxBuf, priv->rx_ring_dma);
+
+ /* Must enable Tx/Rx before setting transfer thresholds! */
+ RTL_W8(priv, ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ priv->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
+ RTL_W32(priv, RxConfig, priv->rx_config);
+ RTL_W32(priv, TxConfig, rtl8139_tx_config);
+
+ /* Lock Config[01234] and BMCR register writes */
+ RTL_W8(priv, Cfg9346, Cfg9346_Lock);
+
+ /* init Tx buffer DMA addresses */
+ for (i = 0; i < NUM_TX_DESC; i++)
+ RTL_W32_F(priv, TxAddr0 + (i * 4), priv->tx_bufs_dma + (priv->tx_buf[i] - priv->tx_bufs));
+
+ RTL_W32(priv, RxMissed, 0);
+
+ __set_rx_mode(priv);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16(priv, IntrMask, 0);
+
+ /* make sure RxTx has started */
+ tmp = RTL_R8(priv, ChipCmd);
+ if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb)))
+ RTL_W8(priv, ChipCmd, CmdRxEnb | CmdTxEnb);
+}
+
+static inline void rtl8139_tx_clear(struct rtl8139_priv *priv)
+{
+ priv->cur_tx = 0;
+ priv->dirty_tx = 0;
+
+ /* XXX account for unsent Tx packets in tp->stats.tx_dropped */
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void rtl8139_init_ring(struct rtl8139_priv *priv)
+{
+ int i;
+
+ priv->cur_rx = 0;
+ priv->cur_tx = 0;
+ priv->dirty_tx = 0;
+
+ for (i = 0; i < NUM_TX_DESC; i++)
+ priv->tx_buf[i] = &priv->tx_bufs[i * TX_BUF_SIZE];
+}
+
+static int rtl8139_phy_read(struct mii_bus *bus, int phy_addr, int reg)
+{
+ struct rtl8139_priv *priv = bus->priv;
+ int val;
+
+ val = 0xffff;
+
+ if (phy_addr == 0) { /* Really a 8139. Use internal registers. */
+ val = reg < 8 && mii_2_8139_map[reg] ?
+ RTL_R16(priv, mii_2_8139_map[reg]) : 0;
+ }
+
+ return val;
+}
+
+static int rtl8139_phy_write(struct mii_bus *bus, int phy_addr,
+ int reg, u16 val)
+{
+ struct rtl8139_priv *priv = bus->priv;
+
+ if (phy_addr == 0) { /* Really a 8139. Use internal registers. */
+ if (reg == 0) {
+ RTL_W8(priv, Cfg9346, Cfg9346_Unlock);
+ RTL_W16(priv, BasicModeCtrl, val);
+ RTL_W8(priv, Cfg9346, Cfg9346_Lock);
+ } else if (reg < 8 && mii_2_8139_map[reg]) {
+ RTL_W16(priv, mii_2_8139_map[reg], val);
+ }
+ }
+
+ return 0;
+}
+
+static int rtl8139_get_ethaddr(struct eth_device *edev, unsigned char *m)
+{
+ struct rtl8139_priv *priv = edev->priv;
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ m[i] = RTL_R8(priv, (MAC0 + i));
+ }
+
+ return 0;
+}
+
+static int rtl8139_set_ethaddr(struct eth_device *edev,
+ unsigned char *mac_addr)
+{
+ struct rtl8139_priv *priv = edev->priv;
+ int i;
+
+ RTL_W8(priv, Cfg9346, Cfg9346_Unlock);
+
+ for (i = 0; i < 6; i++) {
+ RTL_W8(priv, (MAC0 + i), mac_addr[i]);
+ RTL_R8(priv, mac_addr[i]);
+ }
+
+ RTL_W8(priv, Cfg9346, Cfg9346_Lock);
+
+ return 0;
+}
+
+static int rtl8139_init_dev(struct eth_device *edev)
+{
+ struct rtl8139_priv *priv = edev->priv;
+
+ rtl8139_chip_reset(priv);
+ pci_set_master(priv->pci_dev);
+
+ return 0;
+}
+
+static int rtl8139_eth_open(struct eth_device *edev)
+{
+ struct rtl8139_priv *priv = edev->priv;
+ int ret;
+
+ priv->tx_bufs = dma_alloc_coherent(TX_BUF_TOT_LEN, &priv->tx_bufs_dma);
+ priv->rx_ring = dma_alloc_coherent(RX_BUF_TOT_LEN, &priv->rx_ring_dma);
+ priv->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
+
+ rtl8139_init_ring(priv);
+ rtl8139_hw_start(priv);
+
+ ret = phy_device_connect(edev, &priv->miibus, 0, NULL, 0,
+ PHY_INTERFACE_MODE_NA);
+
+ return ret;
+}
+
+static void rtl8139_eth_halt(struct eth_device *edev)
+{
+ struct rtl8139_priv *priv = edev->priv;
+
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8(priv, ChipCmd, 0);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16(priv, IntrMask, 0);
+
+ pci_clear_master(priv->pci_dev);
+
+ /* Green! Put the chip in low-power mode. */
+ RTL_W8(priv, Cfg9346, Cfg9346_Unlock);
+}
+
+static void rtl8139_tx_interrupt(struct eth_device *edev)
+{
+ struct rtl8139_priv *priv = edev->priv;
+ unsigned long dirty_tx, tx_left;
+
+ dirty_tx = priv->dirty_tx;
+ tx_left = priv->cur_tx - dirty_tx;
+ while (tx_left > 0) {
+ int entry = dirty_tx % NUM_TX_DESC;
+ int txstatus;
+
+ txstatus = RTL_R32(priv, TxStatus0 + (entry * sizeof(u32)));
+
+ if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
+ break; /* It still hasn't been Txed */
+
+ /* Note: TxCarrierLost is always asserted at 100mbps. */
+ if (txstatus & (TxOutOfWindow | TxAborted)) {
+ /* There was an major error, log it. */
+ dev_err(&edev->dev, "Transmit error, Tx status %08x\n",
+ txstatus);
+ if (txstatus & TxAborted) {
+ RTL_W32(priv, TxConfig, TxClearAbt);
+ RTL_W16(priv, IntrStatus, TxErr);
+ }
+ } else {
+ if (txstatus & TxUnderrun) {
+ /* Add 64 to the Tx FIFO threshold. */
+ if (priv->tx_flag < 0x00300000)
+ priv->tx_flag += 0x00020000;
+ }
+ }
+
+ dirty_tx++;
+ tx_left--;
+ }
+
+ if (priv->dirty_tx != dirty_tx) {
+ priv->dirty_tx = dirty_tx;
+ }
+}
+
+static int rtl8139_eth_send(struct eth_device *edev, void *packet,
+ int packet_length)
+{
+ struct rtl8139_priv *priv = edev->priv;
+
+ unsigned int entry;
+
+ rtl8139_tx_interrupt(edev);
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = priv->cur_tx % NUM_TX_DESC;
+
+ /* Note: the chip doesn't have auto-pad! */
+ if (likely(packet_length < TX_BUF_SIZE)) {
+ if (packet_length < ETH_ZLEN)
+ memset(priv->tx_buf[entry], 0, ETH_ZLEN);
+ memcpy(priv->tx_buf[entry], packet, packet_length);
+ } else {
+ dev_err(&edev->dev, "packet too long\n");
+ return 0;
+ }
+
+ /*
+ * Writing to TxStatus triggers a DMA transfer of the data
+ * copied to tp->tx_buf[entry] above.
+ */
+ if (packet_length < ETH_ZLEN) {
+ packet_length = ETH_ZLEN;
+ }
+ RTL_W32_F(priv, (TxStatus0 + (entry * sizeof(u32))),
+ (priv->tx_flag | packet_length));
+
+ priv->cur_tx++;
+
+ return 0;
+}
+
+static const u16 rtl8139_intr_mask =
+ PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
+ TxErr | TxOK | RxErr | RxOK;
+
+static int rtl8139_eth_rx(struct eth_device *edev)
+{
+ struct rtl8139_priv *priv = edev->priv;
+ unsigned char *rx_ring = priv->rx_ring;
+ unsigned int cur_rx = priv->cur_rx;
+ unsigned int rx_size = 0;
+
+ u32 ring_offset = cur_rx % RX_BUF_LEN;
+ u32 rx_status;
+ unsigned int pkt_size;
+
+ rtl8139_tx_interrupt(edev);
+
+ if (RTL_R8(priv, ChipCmd) & RxBufEmpty) {
+ /* no data */
+ return 0;
+ }
+
+ rx_status = le32_to_cpu(*(__le32 *) (rx_ring + ring_offset));
+ rx_size = rx_status >> 16;
+ pkt_size = rx_size - 4;
+
+ net_receive(edev, &rx_ring[ring_offset + 4], pkt_size);
+
+ cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
+ cur_rx = cur_rx & (RX_BUF_LEN - 1); /* FIXME */
+ RTL_W16(priv, RxBufPtr, (u16) (cur_rx - 16));
+
+ priv->cur_rx = cur_rx;
+
+ return pkt_size /* size */;
+}
+
+static int rtl8139_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct eth_device *edev;
+ struct rtl8139_priv *priv;
+ int ret;
+ struct device_d *dev = &pdev->dev;
+
+ /* enable pci device */
+ pci_enable_device(pdev);
+
+ priv = xzalloc(sizeof(struct rtl8139_priv));
+
+ edev = &priv->edev;
+ dev->type_data = edev;
+ edev->priv = priv;
+
+ priv->pci_dev = pdev;
+
+ priv->miibus.read = rtl8139_phy_read;
+ priv->miibus.write = rtl8139_phy_write;
+ priv->miibus.priv = priv;
+ priv->miibus.parent = &edev->dev;
+
+ priv->base = pci_iomap(pdev, 1);
+
+ dev_info(dev, "rtl8139 (rev %02x) at %02x: %04x (base=%p)\n",
+ pdev->revision,
+ pdev->devfn,
+ (pdev->class >> 8) & 0xffff,
+ priv->base);
+
+ edev->init = rtl8139_init_dev;
+ edev->open = rtl8139_eth_open;
+ edev->send = rtl8139_eth_send;
+ edev->recv = rtl8139_eth_rx;
+ edev->get_ethaddr = rtl8139_get_ethaddr;
+ edev->set_ethaddr = rtl8139_set_ethaddr;
+ edev->halt = rtl8139_eth_halt;
+ edev->parent = dev;
+
+ ret = eth_register(edev);
+ if (ret)
+ goto eth_err;
+
+ ret = mdiobus_register(&priv->miibus);
+ if (ret)
+ goto mdio_err;
+
+ return 0;
+
+mdio_err:
+ eth_unregister(edev);
+
+eth_err:
+ free(priv);
+
+ return ret;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(rtl8139_pci_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139), },
+ { },
+};
+
+static struct pci_driver rtl8139_eth_driver = {
+ .name = "rtl8139_eth",
+ .id_table = rtl8139_pci_tbl,
+ .probe = rtl8139_probe,
+};
+
+static int rtl8139_init(void)
+{
+ return pci_register_driver(&rtl8139_eth_driver);
+}
+device_initcall(rtl8139_init);
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
new file mode 100644
index 0000000000..9e4659270d
--- /dev/null
+++ b/drivers/pci/Kconfig
@@ -0,0 +1,29 @@
+config HW_HAS_PCI
+ bool
+
+if HW_HAS_PCI
+
+menu "PCI bus options"
+
+config PCI
+ bool "Support for PCI controller"
+ depends on HW_HAS_PCI
+ help
+ Find out whether you have a PCI motherboard. PCI is the name of a
+ bus system, i.e. the way the CPU talks to the other stuff inside
+ your box. If you have PCI, say Y, otherwise N.
+
+
+config PCI_DEBUG
+ bool "PCI Debugging"
+ depends on PCI
+ help
+ Say Y here if you want the PCI core to produce a bunch of debug
+ messages to the system log. Select this if you are having a
+ problem with PCI support and want to see more of what is going on.
+
+ When in doubt, say N.
+
+endmenu
+
+endif
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
new file mode 100644
index 0000000000..edac1a53de
--- /dev/null
+++ b/drivers/pci/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the PCI bus specific drivers.
+#
+obj-y += pci.o bus.o pci_iomap.o
+
+ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
+
+CPPFLAGS += $(ccflags-y)
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
new file mode 100644
index 0000000000..8215ee5638
--- /dev/null
+++ b/drivers/pci/bus.c
@@ -0,0 +1,110 @@
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <linux/pci.h>
+
+/**
+ * pci_match_one_device - Tell if a PCI device structure has a matching
+ * PCI device id structure
+ * @id: single PCI device id structure to match
+ * @dev: the PCI device structure to match against
+ *
+ * Returns the matching pci_device_id structure or %NULL if there is no match.
+ */
+static inline const struct pci_device_id *
+pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
+{
+ if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
+ (id->device == PCI_ANY_ID || id->device == dev->device) &&
+ (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
+ (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
+ !((id->class ^ dev->class) & id->class_mask))
+ return id;
+ return NULL;
+}
+
+static int pci_match(struct device_d *dev, struct driver_d *drv)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_driver *pdrv = to_pci_driver(drv);
+ struct pci_device_id *id;
+
+ for (id = (struct pci_device_id *)pdrv->id_table; id->vendor; id++)
+ if (pci_match_one_device(id, pdev)) {
+ dev->priv = id;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int pci_probe(struct device_d *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_driver *pdrv = to_pci_driver(dev->driver);
+
+ return pdrv->probe(pdev, dev->priv);
+}
+
+static void pci_remove(struct device_d *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_driver *pdrv = to_pci_driver(dev->driver);
+
+ pdrv->remove(pdev);
+}
+
+struct bus_type pci_bus = {
+ .name = "pci",
+ .match = pci_match,
+ .probe = pci_probe,
+ .remove = pci_remove,
+};
+
+static int pci_bus_init(void)
+{
+ return bus_register(&pci_bus);
+}
+pure_initcall(pci_bus_init);
+
+int pci_register_driver(struct pci_driver *pdrv)
+{
+ struct driver_d *drv = &pdrv->driver;
+
+ if (!pdrv->id_table)
+ return -EIO;
+
+ drv->name = pdrv->name;
+ drv->bus = &pci_bus;
+
+ return register_driver(drv);
+}
+
+int pci_register_device(struct pci_dev *pdev)
+{
+ char str[6];
+ struct device_d *dev = &pdev->dev;
+ int ret;
+
+ strcpy(dev->name, "pci");
+ dev->bus = &pci_bus;
+ dev->id = DEVICE_ID_DYNAMIC;
+
+ ret = register_device(dev);
+
+ if (ret)
+ return ret;
+
+ sprintf(str, "%02x", pdev->devfn);
+ dev_add_param_fixed(dev, "devfn", str);
+ sprintf(str, "%04x", (pdev->class >> 8) & 0xffff);
+ dev_add_param_fixed(dev, "class", str);
+ sprintf(str, "%04x", pdev->vendor);
+ dev_add_param_fixed(dev, "vendor", str);
+ sprintf(str, "%04x", pdev->device);
+ dev_add_param_fixed(dev, "device", str);
+ sprintf(str, "%04x", pdev->revision);
+ dev_add_param_fixed(dev, "revision", str);
+
+ return 0;
+}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
new file mode 100644
index 0000000000..ad9350feee
--- /dev/null
+++ b/drivers/pci/pci.c
@@ -0,0 +1,292 @@
+#include <common.h>
+#include <linux/pci.h>
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+static struct pci_controller *hose_head, **hose_tail = &hose_head;
+
+LIST_HEAD(pci_root_buses);
+EXPORT_SYMBOL(pci_root_buses);
+
+static struct pci_bus *pci_alloc_bus(void)
+{
+ struct pci_bus *b;
+
+ b = kzalloc(sizeof(*b), GFP_KERNEL);
+ if (b) {
+ INIT_LIST_HEAD(&b->node);
+ INIT_LIST_HEAD(&b->children);
+ INIT_LIST_HEAD(&b->devices);
+ INIT_LIST_HEAD(&b->slots);
+ INIT_LIST_HEAD(&b->resources);
+ }
+ return b;
+}
+
+void register_pci_controller(struct pci_controller *hose)
+{
+ struct pci_bus *bus;
+
+ *hose_tail = hose;
+ hose_tail = &hose->next;
+
+ bus = pci_alloc_bus();
+ hose->bus = bus;
+ bus->ops = hose->pci_ops;
+ bus->resource[0] = hose->mem_resource;
+ bus->resource[1] = hose->io_resource;
+
+ pci_scan_bus(bus);
+
+ list_add_tail(&bus->node, &pci_root_buses);
+
+ return;
+}
+
+/*
+ * Wrappers for all PCI configuration access functions. They just check
+ * alignment, do locking and call the low-level functions pointed to
+ * by pci_dev->ops.
+ */
+
+#define PCI_byte_BAD 0
+#define PCI_word_BAD (pos & 1)
+#define PCI_dword_BAD (pos & 3)
+
+#define PCI_OP_READ(size,type,len) \
+int pci_bus_read_config_##size \
+ (struct pci_bus *bus, unsigned int devfn, int pos, type *value) \
+{ \
+ int res; \
+ u32 data = 0; \
+ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
+ res = bus->ops->read(bus, devfn, pos, len, &data); \
+ *value = (type)data; \
+ return res; \
+}
+
+#define PCI_OP_WRITE(size,type,len) \
+int pci_bus_write_config_##size \
+ (struct pci_bus *bus, unsigned int devfn, int pos, type value) \
+{ \
+ int res; \
+ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
+ res = bus->ops->write(bus, devfn, pos, len, value); \
+ return res; \
+}
+
+PCI_OP_READ(byte, u8, 1)
+PCI_OP_READ(word, u16, 2)
+PCI_OP_READ(dword, u32, 4)
+PCI_OP_WRITE(byte, u8, 1)
+PCI_OP_WRITE(word, u16, 2)
+PCI_OP_WRITE(dword, u32, 4)
+
+EXPORT_SYMBOL(pci_bus_read_config_byte);
+EXPORT_SYMBOL(pci_bus_read_config_word);
+EXPORT_SYMBOL(pci_bus_read_config_dword);
+EXPORT_SYMBOL(pci_bus_write_config_byte);
+EXPORT_SYMBOL(pci_bus_write_config_word);
+EXPORT_SYMBOL(pci_bus_write_config_dword);
+
+static struct pci_dev *alloc_pci_dev(void)
+{
+ struct pci_dev *dev;
+
+ dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ INIT_LIST_HEAD(&dev->bus_list);
+
+ return dev;
+}
+
+unsigned int pci_scan_bus(struct pci_bus *bus)
+{
+ unsigned int devfn, l, max, class;
+ unsigned char cmd, tmp, hdr_type, is_multi = 0;
+ struct pci_dev *dev;
+ resource_size_t last_mem;
+ resource_size_t last_io;
+
+ /* FIXME: use res_start() */
+ last_mem = bus->resource[0]->start;
+ last_io = bus->resource[1]->start;
+
+ DBG("pci_scan_bus for bus %d\n", bus->number);
+ DBG(" last_io = 0x%08x, last_mem = 0x%08x\n", last_io, last_mem);
+ max = bus->secondary;
+
+ for (devfn = 0; devfn < 0xff; ++devfn) {
+ int bar;
+ u32 old_bar, mask;
+ int size;
+
+ if (PCI_FUNC(devfn) && !is_multi) {
+ /* not a multi-function device */
+ continue;
+ }
+ if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type))
+ continue;
+ if (!PCI_FUNC(devfn))
+ is_multi = hdr_type & 0x80;
+
+ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l) ||
+ /* some broken boards return 0 if a slot is empty: */
+ l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000)
+ continue;
+
+ dev = alloc_pci_dev();
+ if (!dev)
+ return 0;
+
+ dev->bus = bus;
+ dev->devfn = devfn;
+ dev->vendor = l & 0xffff;
+ dev->device = (l >> 16) & 0xffff;
+
+ /* non-destructively determine if device can be a master: */
+ pci_read_config_byte(dev, PCI_COMMAND, &cmd);
+ pci_write_config_byte(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
+ pci_read_config_byte(dev, PCI_COMMAND, &tmp);
+ pci_write_config_byte(dev, PCI_COMMAND, cmd);
+
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
+ dev->revision = class & 0xff;
+ class >>= 8; /* upper 3 bytes */
+ dev->class = class;
+ class >>= 8;
+ dev->hdr_type = hdr_type;
+
+ DBG("PCI: class = %08x, hdr_type = %08x\n", class, hdr_type);
+
+ switch (hdr_type & 0x7f) { /* header type */
+ case PCI_HEADER_TYPE_NORMAL: /* standard header */
+ if (class == PCI_CLASS_BRIDGE_PCI)
+ goto bad;
+
+ /*
+ * read base address registers, again pcibios_fixup() can
+ * tweak these
+ */
+ pci_read_config_dword(dev, PCI_ROM_ADDRESS, &l);
+ dev->rom_address = (l == 0xffffffff) ? 0 : l;
+ break;
+ default: /* unknown header */
+ bad:
+ printk(KERN_ERR "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n",
+ bus->number, dev->devfn, dev->vendor, dev->device, class, hdr_type);
+ continue;
+ }
+
+ DBG("PCI: %02x:%02x [%04x/%04x]\n", bus->number, dev->devfn, dev->vendor, dev->device);
+
+ list_add_tail(&dev->bus_list, &bus->devices);
+ pci_register_device(dev);
+
+ if (class == PCI_CLASS_BRIDGE_HOST) {
+ DBG("PCI: skip pci host bridge\n");
+ continue;
+ }
+
+ for (bar = 0; bar < 6; bar++) {
+ resource_size_t last_addr;
+
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, &old_bar);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, 0xfffffffe);
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, &mask);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, old_bar);
+
+ if (mask == 0 || mask == 0xffffffff) {
+ DBG(" PCI: pbar%d set bad mask\n", bar);
+ continue;
+ }
+
+ if (mask & 0x01) { /* IO */
+ size = -(mask & 0xfffffffe);
+ DBG(" PCI: pbar%d: mask=%08x io %d bytes\n", bar, mask, size);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_io);
+ last_addr = last_io;
+ last_io += size;
+
+ } else { /* MEM */
+ size = -(mask & 0xfffffff0);
+ DBG(" PCI: pbar%d: mask=%08x memory %d bytes\n", bar, mask, size);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_mem);
+ last_addr = last_mem;
+ last_mem += size;
+ }
+
+ dev->resource[bar].start = last_addr;
+ dev->resource[bar].end = last_addr + size - 1;
+ }
+ }
+
+ /*
+ * We've scanned the bus and so we know all about what's on
+ * the other side of any bridges that may be on this bus plus
+ * any devices.
+ *
+ * Return how far we've got finding sub-buses.
+ */
+ DBG("PCI: pci_scan_bus returning with max=%02x\n", max);
+
+ return max;
+}
+
+static void __pci_set_master(struct pci_dev *dev, bool enable)
+{
+ u16 old_cmd, cmd;
+
+ pci_read_config_word(dev, PCI_COMMAND, &old_cmd);
+ if (enable)
+ cmd = old_cmd | PCI_COMMAND_MASTER;
+ else
+ cmd = old_cmd & ~PCI_COMMAND_MASTER;
+ if (cmd != old_cmd) {
+ dev_dbg(&dev->dev, "%s bus mastering\n",
+ enable ? "enabling" : "disabling");
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+}
+
+/**
+ * pci_set_master - enables bus-mastering for device dev
+ * @dev: the PCI device to enable
+ */
+void pci_set_master(struct pci_dev *dev)
+{
+ __pci_set_master(dev, true);
+}
+EXPORT_SYMBOL(pci_set_master);
+
+/**
+ * pci_clear_master - disables bus-mastering for device dev
+ * @dev: the PCI device to disable
+ */
+void pci_clear_master(struct pci_dev *dev)
+{
+ __pci_set_master(dev, false);
+}
+EXPORT_SYMBOL(pci_clear_master);
+
+/**
+ * pci_enable_device - Initialize device before it's used by a driver.
+ * @dev: PCI device to be initialized
+ */
+int pci_enable_device(struct pci_dev *dev)
+{
+ u32 t;
+
+ pci_read_config_dword(dev, PCI_COMMAND, &t);
+ return pci_write_config_dword(dev, PCI_COMMAND, t
+ | PCI_COMMAND_IO
+ | PCI_COMMAND_MEMORY
+ );
+}
+EXPORT_SYMBOL(pci_enable_device);
diff --git a/drivers/pci/pci_iomap.c b/drivers/pci/pci_iomap.c
new file mode 100644
index 0000000000..a56f61dc1a
--- /dev/null
+++ b/drivers/pci/pci_iomap.c
@@ -0,0 +1,29 @@
+/*
+ * Implement the default iomap interfaces
+ *
+ * (C) Copyright 2004 Linus Torvalds
+ */
+#include <linux/pci.h>
+#include <io.h>
+
+#include <module.h>
+
+/**
+ * pci_iomap - create a virtual mapping cookie for a PCI BAR
+ * @dev: PCI device that owns the BAR
+ * @bar: BAR number
+ *
+ * Using this function you will get a __iomem address to your device BAR.
+ * You can access it using ioread*() and iowrite*(). These functions hide
+ * the details if this is a MMIO or PIO address space and will just do what
+ * you expect from them in the correct way.
+ *
+ */
+void __iomem *pci_iomap(struct pci_dev *dev, int bar)
+{
+ struct pci_bus *bus = dev->bus;
+ resource_size_t start = pci_resource_start(dev, bar);
+
+ return (void *)bus->ops->res_start(bus, start);
+}
+EXPORT_SYMBOL(pci_iomap);