U-Boot
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2000 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
April 2011
- 249 participants
- 628 discussions

[U-Boot] [PATCH] MatrixVision common code: use getenv_f instead of snprintf.
by Andre Schwarz 30 Apr '11
by Andre Schwarz 30 Apr '11
30 Apr '11
Signed-off-by: Andre Schwarz <andre.schwarz(a)matrix-vision.de>
---
board/matrix_vision/common/mv_common.c | 25 +++++++++++++------------
1 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/board/matrix_vision/common/mv_common.c b/board/matrix_vision/common/mv_common.c
index 0afc535..742a90a 100644
--- a/board/matrix_vision/common/mv_common.c
+++ b/board/matrix_vision/common/mv_common.c
@@ -32,7 +32,7 @@ DECLARE_GLOBAL_DATA_PTR;
static char* entries_to_keep[] = {
"serial#", "ethaddr", "eth1addr", "model_info", "sensor_cnt",
"fpgadatasize", "ddr_size", "use_dhcp", "use_static_ipaddr",
- "static_ipaddr", "static_netmask", "static_gateway",
+ "static_ipaddr", "static_netmask", "static_gateway", "boot_sqfs",
"syslog", "watchdog", "netboot", "evo8serialnumber" };
#define MV_MAX_ENV_ENTRY_LENGTH 64
@@ -41,28 +41,27 @@ static char* entries_to_keep[] = {
void mv_reset_environment(void)
{
int i;
- char *s[MV_KEEP_ENTRIES];
char entries[MV_KEEP_ENTRIES][MV_MAX_ENV_ENTRY_LENGTH];
printf("\n*** RESET ENVIRONMENT ***\n");
memset(entries, 0, MV_KEEP_ENTRIES * MV_MAX_ENV_ENTRY_LENGTH);
+
for (i = 0; i < MV_KEEP_ENTRIES; i++) {
- s[i] = getenv(entries_to_keep[i]);
- if (s[i]) {
- printf("save '%s' : %s\n", entries_to_keep[i], s[i]);
- strncpy(entries[i], s[i], MV_MAX_ENV_ENTRY_LENGTH);
- }
+ if (getenv_f(entries_to_keep[i], entries[i],
+ MV_MAX_ENV_ENTRY_LENGTH) <= 0)
+ memset(entries[i], 0, MV_MAX_ENV_ENTRY_LENGTH);
+ else
+ printf("save '%s' : %s\n", entries_to_keep[i],
+ entries[i]);
}
gd->env_valid = 0;
env_relocate();
for (i = 0; i < MV_KEEP_ENTRIES; i++) {
- if (s[i]) {
- printf("restore '%s' : %s\n", entries_to_keep[i], s[i]);
- setenv(entries_to_keep[i], s[i]);
- }
+ printf("restore '%s' : %s\n", entries_to_keep[i], entries[i]);
+ setenv(entries_to_keep[i], entries[i]);
}
saveenv();
@@ -70,7 +69,8 @@ void mv_reset_environment(void)
int mv_load_fpga(void)
{
- int result;
+ int result = -1;
+#ifdef CONFIG_FPGA
size_t data_size = 0;
void *fpga_data = NULL;
char *datastr = getenv("fpgadata");
@@ -92,6 +92,7 @@ int mv_load_fpga(void)
}
result = fpga_load(0, fpga_data, data_size);
+#endif
if (!result)
show_boot_progress(0);
--
1.7.0.4
2
1

[U-Boot] [PATCH v5 1/5] Add support for SMSC95XX USB 2.0 10/100MBit Ethernet Adapter
by Simon Glass 30 Apr '11
by Simon Glass 30 Apr '11
30 Apr '11
The SMSC95XX is a USB hub with a built-in Ethernet adapter. This adds support
for this, using the USB host network framework.
TEST=usb start; bootp; tftp ...
Changes for v2:
- Coding style cleanup
- Changed some comments as suggested
- eth_set_hwaddr -> eth_write_hwaddr
- tided up other users of eth_getenv_enetaddr_by_index()
Changes for v3:
- Drop tfpserverip patch
- Change turbo_mode to #define
- Fix tfpserverip patch bleed
Changes for v4:
- Dropped Tegra2 specific bit
- Added patch in place of tftpserverip patch, to speed up successive network commands on asix
- Fixed a few broken bits in SMSC from my testing
Changes for v5:
- Code style clean-ups in SMSC
- Cleaned up debugging of errors in SMSC driver
- Changed NULL to "eth" in eth_getenv_enetaddr_by_index() API
Signed-off-by: Simon Glass <sjg(a)chromium.org>
---
drivers/usb/eth/Makefile | 1 +
drivers/usb/eth/smsc95xx.c | 879 +++++++++++++++++++++++++++++++++++++++++++
drivers/usb/eth/usb_ether.c | 7 +
include/usb_ether.h | 13 +
4 files changed, 900 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/eth/smsc95xx.c
diff --git a/drivers/usb/eth/Makefile b/drivers/usb/eth/Makefile
index 6a5f25a..e28793d 100644
--- a/drivers/usb/eth/Makefile
+++ b/drivers/usb/eth/Makefile
@@ -28,6 +28,7 @@ COBJS-$(CONFIG_USB_HOST_ETHER) += usb_ether.o
ifdef CONFIG_USB_ETHER_ASIX
COBJS-y += asix.o
endif
+COBJS-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c
new file mode 100644
index 0000000..6e35a6d
--- /dev/null
+++ b/drivers/usb/eth/smsc95xx.c
@@ -0,0 +1,879 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * Copyright (C) 2009 NVIDIA, Corporation
+ * 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 <common.h>
+#include <usb.h>
+#include <linux/mii.h>
+#include "usb_ether.h"
+
+/* SMSC LAN95xx based USB 2.0 Ethernet Devices */
+
+/* Tx command words */
+#define TX_CMD_A_FIRST_SEG_ 0x00002000
+#define TX_CMD_A_LAST_SEG_ 0x00001000
+
+/* Rx status word */
+#define RX_STS_FL_ 0x3FFF0000 /* Frame Length */
+#define RX_STS_ES_ 0x00008000 /* Error Summary */
+
+/* SCSRs */
+#define ID_REV 0x00
+
+#define INT_STS 0x08
+
+#define TX_CFG 0x10
+#define TX_CFG_ON_ 0x00000004
+
+#define HW_CFG 0x14
+#define HW_CFG_BIR_ 0x00001000
+#define HW_CFG_RXDOFF_ 0x00000600
+#define HW_CFG_MEF_ 0x00000020
+#define HW_CFG_BCE_ 0x00000002
+#define HW_CFG_LRST_ 0x00000008
+
+#define PM_CTRL 0x20
+#define PM_CTL_PHY_RST_ 0x00000010
+
+#define AFC_CFG 0x2C
+
+/*
+ * Hi watermark = 15.5Kb (~10 mtu pkts)
+ * low watermark = 3k (~2 mtu pkts)
+ * backpressure duration = ~ 350us
+ * Apply FC on any frame.
+ */
+#define AFC_CFG_DEFAULT 0x00F830A1
+
+#define E2P_CMD 0x30
+#define E2P_CMD_BUSY_ 0x80000000
+#define E2P_CMD_READ_ 0x00000000
+#define E2P_CMD_TIMEOUT_ 0x00000400
+#define E2P_CMD_LOADED_ 0x00000200
+#define E2P_CMD_ADDR_ 0x000001FF
+
+#define E2P_DATA 0x34
+
+#define BURST_CAP 0x38
+
+#define INT_EP_CTL 0x68
+#define INT_EP_CTL_PHY_INT_ 0x00008000
+
+#define BULK_IN_DLY 0x6C
+
+/* MAC CSRs */
+#define MAC_CR 0x100
+#define MAC_CR_MCPAS_ 0x00080000
+#define MAC_CR_PRMS_ 0x00040000
+#define MAC_CR_HPFILT_ 0x00002000
+#define MAC_CR_TXEN_ 0x00000008
+#define MAC_CR_RXEN_ 0x00000004
+
+#define ADDRH 0x104
+
+#define ADDRL 0x108
+
+#define MII_ADDR 0x114
+#define MII_WRITE_ 0x02
+#define MII_BUSY_ 0x01
+#define MII_READ_ 0x00 /* ~of MII Write bit */
+
+#define MII_DATA 0x118
+
+#define FLOW 0x11C
+
+#define VLAN1 0x120
+
+#define COE_CR 0x130
+#define Tx_COE_EN_ 0x00010000
+#define Rx_COE_EN_ 0x00000001
+
+/* Vendor-specific PHY Definitions */
+#define PHY_INT_SRC 29
+
+#define PHY_INT_MASK 30
+#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010)
+#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \
+ PHY_INT_MASK_LINK_DOWN_)
+
+/* USB Vendor Requests */
+#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0
+#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1
+
+/* Some extra defines */
+#define HS_USB_PKT_SIZE 512
+#define FS_USB_PKT_SIZE 64
+#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE)
+#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE)
+#define DEFAULT_BULK_IN_DELAY 0x00002000
+#define MAX_SINGLE_PACKET_SIZE 2048
+#define EEPROM_MAC_OFFSET 0x01
+#define SMSC95XX_INTERNAL_PHY_ID 1
+#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
+
+/* local defines */
+#define SMSC95XX_BASE_NAME "sms"
+#define USB_CTRL_SET_TIMEOUT 5000
+#define USB_CTRL_GET_TIMEOUT 5000
+#define USB_BULK_SEND_TIMEOUT 5000
+#define USB_BULK_RECV_TIMEOUT 5000
+
+#define AX_RX_URB_SIZE 2048
+#define PHY_CONNECT_TIMEOUT 5000
+
+#define TURBO_MODE
+
+/* local vars */
+static int curr_eth_dev; /* index for name of next device detected */
+
+
+/*
+ * Smsc95xx infrastructure commands
+ */
+static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data)
+{
+ int len;
+
+ cpu_to_le32s(&data);
+
+ len = usb_control_msg(dev->pusb_dev, usb_sndctrlpipe(dev->pusb_dev, 0),
+ USB_VENDOR_REQUEST_WRITE_REGISTER,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00, index, &data, sizeof(data), USB_CTRL_SET_TIMEOUT);
+ if (len != sizeof(data)) {
+ debug("smsc95xx_write_reg failed: index=%d, data=%d, len=%d",
+ index, data, len);
+ return -1;
+ }
+ return 0;
+}
+
+static int smsc95xx_read_reg(struct ueth_data *dev, u32 index, u32 *data)
+{
+ int len;
+
+ len = usb_control_msg(dev->pusb_dev, usb_rcvctrlpipe(dev->pusb_dev, 0),
+ USB_VENDOR_REQUEST_READ_REGISTER,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00, index, data, sizeof(data), USB_CTRL_GET_TIMEOUT);
+ if (len != sizeof(data)) {
+ debug("smsc95xx_read_reg failed: index=%d, len=%d",
+ index, len);
+ return -1;
+ }
+
+ le32_to_cpus(data);
+ return 0;
+}
+
+/* Loop until the read is completed with timeout */
+static int smsc95xx_phy_wait_not_busy(struct ueth_data *dev)
+{
+ unsigned long start_time = get_timer(0);
+ u32 val;
+
+ do {
+ smsc95xx_read_reg(dev, MII_ADDR, &val);
+ if (!(val & MII_BUSY_))
+ return 0;
+ } while (get_timer(start_time) < 1 * 1000 * 1000);
+
+ return -1;
+}
+
+static int smsc95xx_mdio_read(struct ueth_data *dev, int phy_id, int idx)
+{
+ u32 val, addr;
+
+ /* confirm MII not busy */
+ if (smsc95xx_phy_wait_not_busy(dev)) {
+ debug("MII is busy in smsc95xx_mdio_read\n");
+ return -1;
+ }
+
+ /* set the address, index & direction (read from PHY) */
+ addr = (phy_id << 11) | (idx << 6) | MII_READ_;
+ smsc95xx_write_reg(dev, MII_ADDR, addr);
+
+ if (smsc95xx_phy_wait_not_busy(dev)) {
+ debug("Timed out reading MII reg %02X\n", idx);
+ return -1;
+ }
+
+ smsc95xx_read_reg(dev, MII_DATA, &val);
+
+ return (u16)(val & 0xFFFF);
+}
+
+static void smsc95xx_mdio_write(struct ueth_data *dev, int phy_id, int idx,
+ int regval)
+{
+ u32 val, addr;
+
+ /* confirm MII not busy */
+ if (smsc95xx_phy_wait_not_busy(dev)) {
+ debug("MII is busy in smsc95xx_mdio_write\n");
+ return;
+ }
+
+ val = regval;
+ smsc95xx_write_reg(dev, MII_DATA, val);
+
+ /* set the address, index & direction (write to PHY) */
+ addr = (phy_id << 11) | (idx << 6) | MII_WRITE_;
+ smsc95xx_write_reg(dev, MII_ADDR, addr);
+
+ if (smsc95xx_phy_wait_not_busy(dev))
+ debug("Timed out writing MII reg %02X\n", idx);
+}
+
+static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev)
+{
+ unsigned long start_time = get_timer(0);
+ u32 val;
+
+ do {
+ smsc95xx_read_reg(dev, E2P_CMD, &val);
+ if (!(val & E2P_CMD_LOADED_)) {
+ debug("No EEPROM present\n");
+ return -1;
+ }
+ if (!(val & E2P_CMD_BUSY_))
+ return 0;
+ udelay(40);
+ } while (get_timer(start_time) < 1 * 1000 * 1000);
+
+ debug("EEPROM is busy\n");
+ return -1;
+}
+
+static int smsc95xx_wait_eeprom(struct ueth_data *dev)
+{
+ unsigned long start_time = get_timer(0);
+ u32 val;
+
+ do {
+ smsc95xx_read_reg(dev, E2P_CMD, &val);
+ if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_))
+ break;
+ udelay(40);
+ } while (get_timer(start_time) < 1 * 1000 * 1000);
+
+ if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) {
+ debug("EEPROM read operation timeout\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int smsc95xx_read_eeprom(struct ueth_data *dev, u32 offset, u32 length,
+ u8 *data)
+{
+ u32 val;
+ int i, ret;
+
+ ret = smsc95xx_eeprom_confirm_not_busy(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < length; i++) {
+ val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_);
+ smsc95xx_write_reg(dev, E2P_CMD, val);
+
+ ret = smsc95xx_wait_eeprom(dev);
+ if (ret < 0)
+ return ret;
+
+ smsc95xx_read_reg(dev, E2P_DATA, &val);
+ data[i] = val & 0xFF;
+ offset++;
+ }
+ return 0;
+}
+
+/*
+ * mii_nway_restart - restart NWay (autonegotiation) for this interface
+ *
+ * Returns 0 on success, negative on error.
+ */
+static int mii_nway_restart(struct ueth_data *dev)
+{
+ int bmcr;
+ int r = -1;
+
+ /* if autoneg is off, it's an error */
+ bmcr = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMCR);
+
+ if (bmcr & BMCR_ANENABLE) {
+ bmcr |= BMCR_ANRESTART;
+ smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr);
+ r = 0;
+ }
+ return r;
+}
+
+static int smsc95xx_phy_initialize(struct ueth_data *dev)
+{
+ smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET);
+ smsc95xx_mdio_write(dev, dev->phy_id, MII_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM);
+
+ /* read to clear */
+ smsc95xx_mdio_read(dev, dev->phy_id, PHY_INT_SRC);
+
+ smsc95xx_mdio_write(dev, dev->phy_id, PHY_INT_MASK,
+ PHY_INT_MASK_DEFAULT_);
+ mii_nway_restart(dev);
+
+ debug("phy initialised succesfully\n");
+ return 0;
+}
+
+static int smsc95xx_init_mac_address(struct eth_device *eth,
+ struct ueth_data *dev)
+{
+ /* try reading mac address from EEPROM */
+ if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN,
+ eth->enetaddr) == 0) {
+ if (is_valid_ether_addr(eth->enetaddr)) {
+ /* eeprom values are valid so use them */
+ debug("MAC address read from EEPROM\n");
+ return 0;
+ }
+ }
+
+ /*
+ * No eeprom, or eeprom values are invalid. Generating a random MAC
+ * address is not safe. Just return an error.
+ */
+ return -1;
+}
+
+static int smsc95xx_write_hwaddr(struct eth_device *eth)
+{
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+ u32 addr_lo, addr_hi;
+ int ret;
+
+ /* set hardware address */
+ debug("** %s()\n", __func__);
+ addr_lo = cpu_to_le32(*((u32 *)eth->enetaddr));
+ addr_hi = cpu_to_le16(*((u16 *)(eth->enetaddr + 4)));
+ ret = smsc95xx_write_reg(dev, ADDRL, addr_lo);
+ if (ret < 0) {
+ debug("Failed to write ADDRL: %d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_write_reg(dev, ADDRH, addr_hi);
+ if (ret < 0)
+ return ret;
+ debug("MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+ eth->enetaddr[0], eth->enetaddr[1],
+ eth->enetaddr[2], eth->enetaddr[3],
+ eth->enetaddr[4], eth->enetaddr[5]);
+ dev->have_hwaddr = 1;
+ return 0;
+}
+
+/* Enable or disable Tx & Rx checksum offload engines */
+static int smsc95xx_set_csums(struct ueth_data *dev,
+ int use_tx_csum, int use_rx_csum)
+{
+ u32 read_buf;
+ int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
+ if (ret < 0)
+ return ret;
+
+ if (use_tx_csum)
+ read_buf |= Tx_COE_EN_;
+ else
+ read_buf &= ~Tx_COE_EN_;
+
+ if (use_rx_csum)
+ read_buf |= Rx_COE_EN_;
+ else
+ read_buf &= ~Rx_COE_EN_;
+
+ ret = smsc95xx_write_reg(dev, COE_CR, read_buf);
+ if (ret < 0)
+ return ret;
+
+ debug("COE_CR = 0x%08x\n", read_buf);
+ return 0;
+}
+
+static void smsc95xx_set_multicast(struct ueth_data *dev)
+{
+ /* No multicast in u-boot */
+ dev->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+}
+
+/* starts the TX path */
+static void smsc95xx_start_tx_path(struct ueth_data *dev)
+{
+ u32 reg_val;
+
+ /* Enable Tx at MAC */
+ dev->mac_cr |= MAC_CR_TXEN_;
+
+ smsc95xx_write_reg(dev, MAC_CR, dev->mac_cr);
+
+ /* Enable Tx at SCSRs */
+ reg_val = TX_CFG_ON_;
+ smsc95xx_write_reg(dev, TX_CFG, reg_val);
+}
+
+/* Starts the Receive path */
+static void smsc95xx_start_rx_path(struct ueth_data *dev)
+{
+ dev->mac_cr |= MAC_CR_RXEN_;
+ smsc95xx_write_reg(dev, MAC_CR, dev->mac_cr);
+}
+
+/*
+ * Smsc95xx callbacks
+ */
+static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
+{
+ int ret;
+ u32 write_buf;
+ u32 read_buf;
+ u32 burst_cap;
+ int timeout;
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+#define TIMEOUT_RESOLUTION 50 /* ms */
+ int link_detected;
+
+ debug("** %s()\n", __func__);
+ dev->phy_id = SMSC95XX_INTERNAL_PHY_ID; /* fixed phy id */
+
+ write_buf = HW_CFG_LRST_;
+ ret = smsc95xx_write_reg(dev, HW_CFG, write_buf);
+ if (ret < 0)
+ return ret;
+
+ timeout = 0;
+ do {
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ udelay(10 * 1000);
+ timeout++;
+ } while ((read_buf & HW_CFG_LRST_) && (timeout < 100));
+
+ if (timeout >= 100) {
+ debug("timeout waiting for completion of Lite Reset\n");
+ return -1;
+ }
+
+ write_buf = PM_CTL_PHY_RST_;
+ ret = smsc95xx_write_reg(dev, PM_CTRL, write_buf);
+ if (ret < 0)
+ return ret;
+
+ timeout = 0;
+ do {
+ ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf);
+ if (ret < 0)
+ return ret;
+ udelay(10 * 1000);
+ timeout++;
+ } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100));
+ if (timeout >= 100) {
+ debug("timeout waiting for PHY Reset\n");
+ return -1;
+ }
+ if (!dev->have_hwaddr && smsc95xx_init_mac_address(eth, dev) == 0)
+ dev->have_hwaddr = 1;
+ if (!dev->have_hwaddr) {
+ puts("Error: SMSC95xx: No MAC address set - set usbethaddr\n");
+ return -1;
+ }
+ if (smsc95xx_write_hwaddr(eth) < 0)
+ return -1;
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from HW_CFG : 0x%08x\n", read_buf);
+
+ read_buf |= HW_CFG_BIR_;
+ ret = smsc95xx_write_reg(dev, HW_CFG, read_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from HW_CFG after writing "
+ "HW_CFG_BIR_: 0x%08x\n", read_buf);
+
+#ifdef TURBO_MODE
+ if (dev->pusb_dev->speed == USB_SPEED_HIGH) {
+ burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
+ dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
+ } else {
+ burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
+ dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
+ }
+#else
+ burst_cap = 0;
+ dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
+#endif
+ debug("rx_urb_size=%ld\n", (ulong)dev->rx_urb_size);
+
+ ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from BURST_CAP after writing: 0x%08x\n", read_buf);
+
+ read_buf = DEFAULT_BULK_IN_DELAY;
+ ret = smsc95xx_write_reg(dev, BULK_IN_DLY, read_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from BULK_IN_DLY after writing: "
+ "0x%08x\n", read_buf);
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from HW_CFG: 0x%08x\n", read_buf);
+
+#ifdef TURBO_MODE
+ read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_);
+#endif
+ read_buf &= ~HW_CFG_RXDOFF_;
+
+ /* set Rx data offset=2, Make IP header aligns on word boundary. */
+#define NET_IP_ALIGN 2
+ read_buf |= NET_IP_ALIGN << 9;
+
+ ret = smsc95xx_write_reg(dev, HW_CFG, read_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from HW_CFG after writing: 0x%08x\n", read_buf);
+
+ write_buf = 0xFFFFFFFF;
+ ret = smsc95xx_write_reg(dev, INT_STS, write_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(dev, ID_REV, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("ID_REV = 0x%08x\n", read_buf);
+
+ /* Init Tx */
+ write_buf = 0;
+ ret = smsc95xx_write_reg(dev, FLOW, write_buf);
+ if (ret < 0)
+ return ret;
+
+ read_buf = AFC_CFG_DEFAULT;
+ ret = smsc95xx_write_reg(dev, AFC_CFG, read_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(dev, MAC_CR, &dev->mac_cr);
+ if (ret < 0)
+ return ret;
+
+ /* Init Rx. Set Vlan */
+ write_buf = (u32)ETH_P_8021Q;
+ ret = smsc95xx_write_reg(dev, VLAN1, write_buf);
+ if (ret < 0)
+ return ret;
+
+ /* Disable checksum offload engines */
+ ret = smsc95xx_set_csums(dev, 0, 0);
+ if (ret < 0) {
+ debug("Failed to set csum offload: %d\n", ret);
+ return ret;
+ }
+ smsc95xx_set_multicast(dev);
+
+ if (smsc95xx_phy_initialize(dev) < 0)
+ return -1;
+ ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf);
+ if (ret < 0)
+ return ret;
+
+ /* enable PHY interrupts */
+ read_buf |= INT_EP_CTL_PHY_INT_;
+
+ ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf);
+ if (ret < 0)
+ return ret;
+
+ smsc95xx_start_tx_path(dev);
+ smsc95xx_start_rx_path(dev);
+
+ timeout = 0;
+ do {
+ link_detected = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMSR)
+ & BMSR_LSTATUS;
+ if (!link_detected) {
+ if (timeout == 0)
+ printf("Waiting for Ethernet connection... ");
+ udelay(TIMEOUT_RESOLUTION * 1000);
+ timeout += TIMEOUT_RESOLUTION;
+ }
+ } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT);
+ if (link_detected) {
+ if (timeout != 0)
+ printf("done.\n");
+ } else {
+ printf("unable to connect.\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int smsc95xx_send(struct eth_device *eth, volatile void* packet,
+ int length)
+{
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+ int err;
+ int actual_len;
+ u32 tx_cmd_a;
+ u32 tx_cmd_b;
+ unsigned char msg[PKTSIZE + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)];
+
+ debug("** %s(), len %d, buf %#x\n", __func__, length, (int)msg);
+ if (length > PKTSIZE)
+ return -1;
+
+ tx_cmd_a = (u32)length | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ tx_cmd_b = (u32)length;
+ cpu_to_le32s(&tx_cmd_a);
+ cpu_to_le32s(&tx_cmd_b);
+
+ /* prepend cmd_a and cmd_b */
+ memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a));
+ memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b));
+ memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet,
+ length);
+ err = usb_bulk_msg(dev->pusb_dev,
+ usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
+ (void *)msg,
+ length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
+ &actual_len,
+ USB_BULK_SEND_TIMEOUT);
+ debug("Tx: len = %u, actual = %u, err = %d\n",
+ length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
+ actual_len, err);
+ return err;
+}
+
+static int smsc95xx_recv(struct eth_device *eth)
+{
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+ static unsigned char recv_buf[AX_RX_URB_SIZE];
+ unsigned char *buf_ptr;
+ int err;
+ int actual_len;
+ u32 packet_len;
+ int cur_buf_align;
+
+ debug("** %s()\n", __func__);
+ err = usb_bulk_msg(dev->pusb_dev,
+ usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
+ (void *)recv_buf,
+ AX_RX_URB_SIZE,
+ &actual_len,
+ USB_BULK_RECV_TIMEOUT);
+ debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE,
+ actual_len, err);
+ if (err != 0) {
+ debug("Rx: failed to receive\n");
+ return -1;
+ }
+ if (actual_len > AX_RX_URB_SIZE) {
+ debug("Rx: received too many bytes %d\n", actual_len);
+ return -1;
+ }
+
+ buf_ptr = recv_buf;
+ while (actual_len > 0) {
+ /*
+ * 1st 4 bytes contain the length of the actual data plus error
+ * info. Extract data length.
+ */
+ if (actual_len < sizeof(packet_len)) {
+ debug("Rx: incomplete packet length\n");
+ return -1;
+ }
+ memcpy(&packet_len, buf_ptr, sizeof(packet_len));
+ le32_to_cpus(&packet_len);
+ if (packet_len & RX_STS_ES_) {
+ debug("Rx: Error header=%#x", packet_len);
+ return -1;
+ }
+ packet_len = ((packet_len & RX_STS_FL_) >> 16);
+
+ if (packet_len > actual_len - sizeof(packet_len)) {
+ debug("Rx: too large packet: %d\n", packet_len);
+ return -1;
+ }
+
+ /* Notify net stack */
+ NetReceive(buf_ptr + sizeof(packet_len), packet_len - 4);
+
+ /* Adjust for next iteration */
+ actual_len -= sizeof(packet_len) + packet_len;
+ buf_ptr += sizeof(packet_len) + packet_len;
+ cur_buf_align = (int)buf_ptr - (int)recv_buf;
+
+ if (cur_buf_align & 0x03) {
+ int align = 4 - (cur_buf_align & 0x03);
+
+ actual_len -= align;
+ buf_ptr += align;
+ }
+ }
+ return err;
+}
+
+static void smsc95xx_halt(struct eth_device *eth)
+{
+ debug("** %s()\n", __func__);
+}
+
+/*
+ * SMSC probing functions
+ */
+void smsc95xx_eth_before_probe(void)
+{
+ curr_eth_dev = 0;
+}
+
+struct smsc95xx_dongle {
+ unsigned short vendor;
+ unsigned short product;
+};
+
+static const struct smsc95xx_dongle smsc95xx_dongles[] = {
+ { 0x0424, 0xec00 }, /* LAN9512/LAN9514 Ethernet */
+ { 0x0424, 0x9500 }, /* LAN9500 Ethernet */
+ { 0x0000, 0x0000 } /* END - Do not remove */
+};
+
+/* Probe to see if a new device is actually an SMSC device */
+int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
+ struct ueth_data *ss)
+{
+ struct usb_interface *iface;
+ struct usb_interface_descriptor *iface_desc;
+ int i;
+
+ /* let's examine the device now */
+ iface = &dev->config.if_desc[ifnum];
+ iface_desc = &dev->config.if_desc[ifnum].desc;
+
+ for (i = 0; smsc95xx_dongles[i].vendor != 0; i++) {
+ if (dev->descriptor.idVendor == smsc95xx_dongles[i].vendor &&
+ dev->descriptor.idProduct == smsc95xx_dongles[i].product)
+ /* Found a supported dongle */
+ break;
+ }
+ if (smsc95xx_dongles[i].vendor == 0)
+ return 0;
+
+ /* At this point, we know we've got a live one */
+ debug("\n\nUSB Ethernet device detected\n");
+ memset(ss, '\0', sizeof(struct ueth_data));
+
+ /* Initialize the ueth_data structure with some useful info */
+ ss->ifnum = ifnum;
+ ss->pusb_dev = dev;
+ ss->subclass = iface_desc->bInterfaceSubClass;
+ ss->protocol = iface_desc->bInterfaceProtocol;
+
+ /*
+ * We are expecting a minimum of 3 endpoints - in, out (bulk), and int.
+ * We will ignore any others.
+ */
+ for (i = 0; i < iface_desc->bNumEndpoints; i++) {
+ /* is it an BULK endpoint? */
+ if ((iface->ep_desc[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+ if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
+ ss->ep_in =
+ iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ else
+ ss->ep_out =
+ iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+
+ /* is it an interrupt endpoint? */
+ if ((iface->ep_desc[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+ ss->ep_int = iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ ss->irqinterval = iface->ep_desc[i].bInterval;
+ }
+ }
+ debug("Endpoints In %d Out %d Int %d\n",
+ ss->ep_in, ss->ep_out, ss->ep_int);
+
+ /* Do some basic sanity checks, and bail if we find a problem */
+ if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
+ !ss->ep_in || !ss->ep_out || !ss->ep_int) {
+ debug("Problems with device\n");
+ return 0;
+ }
+ dev->privptr = (void *)ss;
+ return 1;
+}
+
+int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
+ struct eth_device *eth)
+{
+ debug("** %s()\n", __func__);
+ if (!eth) {
+ debug("%s: missing parameter.\n", __func__);
+ return 0;
+ }
+ sprintf(eth->name, "%s%d", SMSC95XX_BASE_NAME, curr_eth_dev++);
+ eth->init = smsc95xx_init;
+ eth->send = smsc95xx_send;
+ eth->recv = smsc95xx_recv;
+ eth->halt = smsc95xx_halt;
+ eth->priv = ss;
+ return 1;
+}
diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c
index 68a0883..7b55da3 100644
--- a/drivers/usb/eth/usb_ether.c
+++ b/drivers/usb/eth/usb_ether.c
@@ -45,6 +45,13 @@ static const struct usb_eth_prob_dev prob_dev[] = {
.get_info = asix_eth_get_info,
},
#endif
+#ifdef CONFIG_USB_ETHER_SMSC95XX
+ {
+ .before_probe = smsc95xx_eth_before_probe,
+ .probe = smsc95xx_eth_probe,
+ .get_info = smsc95xx_eth_get_info,
+ },
+#endif
{ }, /* END */
};
diff --git a/include/usb_ether.h b/include/usb_ether.h
index 825c275..a7fb26b 100644
--- a/include/usb_ether.h
+++ b/include/usb_ether.h
@@ -51,6 +51,11 @@ struct ueth_data {
unsigned char irqinterval; /* Intervall for IRQ Pipe */
/* private fields for each driver can go here if needed */
+#ifdef CONFIG_USB_ETHER_SMSC95XX
+ size_t rx_urb_size; /* maximum USB URB size */
+ u32 mac_cr; /* MAC control register value */
+ int have_hwaddr; /* 1 if we have a hardware MAC address */
+#endif
};
/*
@@ -65,4 +70,12 @@ int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
struct eth_device *eth);
#endif
+#ifdef CONFIG_USB_ETHER_SMSC95XX
+void smsc95xx_eth_before_probe(void);
+int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
+ struct ueth_data *ss);
+int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
+ struct eth_device *eth);
+#endif
+
#endif /* __USB_ETHER_H__ */
--
1.7.3.1
2
5

[U-Boot] [PATCH 1/5] Add support for SMSC95XX USB 2.0 10/100MBit Ethernet Adapter
by Simon Glass 30 Apr '11
by Simon Glass 30 Apr '11
30 Apr '11
The SMSC95XX is a USB hub with a built-in Ethernet adapter. This adds support
for this, using the USB host network framework.
TEST=usb start; bootp; tftp ...
Signed-off-by: Simon Glass <sjg(a)chromium.org>
---
drivers/usb/eth/Makefile | 1 +
drivers/usb/eth/smsc95xx.c | 922 +++++++++++++++++++++++++++++++++++++++++++
drivers/usb/eth/usb_ether.c | 7 +
3 files changed, 930 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/eth/smsc95xx.c
diff --git a/drivers/usb/eth/Makefile b/drivers/usb/eth/Makefile
index 6a5f25a..e28793d 100644
--- a/drivers/usb/eth/Makefile
+++ b/drivers/usb/eth/Makefile
@@ -28,6 +28,7 @@ COBJS-$(CONFIG_USB_HOST_ETHER) += usb_ether.o
ifdef CONFIG_USB_ETHER_ASIX
COBJS-y += asix.o
endif
+COBJS-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c
new file mode 100644
index 0000000..224434f
--- /dev/null
+++ b/drivers/usb/eth/smsc95xx.c
@@ -0,0 +1,922 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * Copyright (C) 2009 NVIDIA, Corporation
+ * 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 <common.h>
+#include <usb.h>
+#include <linux/mii.h>
+#include "usb_ether.h"
+
+/* SMSC LAN95xx based USB 2.0 Ethernet Devices */
+
+/* Tx command words */
+#define TX_CMD_A_FIRST_SEG_ (0x00002000)
+#define TX_CMD_A_LAST_SEG_ (0x00001000)
+
+/* Rx status word */
+#define RX_STS_FL_ (0x3FFF0000) /* Frame Length */
+#define RX_STS_ES_ (0x00008000) /* Error Summary */
+
+/* SCSRs */
+#define ID_REV (0x00)
+
+#define INT_STS (0x08)
+
+#define TX_CFG (0x10)
+#define TX_CFG_ON_ (0x00000004)
+
+#define HW_CFG (0x14)
+#define HW_CFG_BIR_ (0x00001000)
+#define HW_CFG_RXDOFF_ (0x00000600)
+#define HW_CFG_MEF_ (0x00000020)
+#define HW_CFG_BCE_ (0x00000002)
+#define HW_CFG_LRST_ (0x00000008)
+
+#define PM_CTRL (0x20)
+#define PM_CTL_PHY_RST_ (0x00000010)
+
+#define AFC_CFG (0x2C)
+
+/*
+ * Hi watermark = 15.5Kb (~10 mtu pkts)
+ * low watermark = 3k (~2 mtu pkts)
+ * backpressure duration = ~ 350us
+ * Apply FC on any frame.
+ */
+#define AFC_CFG_DEFAULT (0x00F830A1)
+
+#define E2P_CMD (0x30)
+#define E2P_CMD_BUSY_ (0x80000000)
+#define E2P_CMD_READ_ (0x00000000)
+#define E2P_CMD_TIMEOUT_ (0x00000400)
+#define E2P_CMD_LOADED_ (0x00000200)
+#define E2P_CMD_ADDR_ (0x000001FF)
+
+#define E2P_DATA (0x34)
+
+#define BURST_CAP (0x38)
+
+#define INT_EP_CTL (0x68)
+#define INT_EP_CTL_PHY_INT_ (0x00008000)
+
+#define BULK_IN_DLY (0x6C)
+
+/* MAC CSRs */
+#define MAC_CR (0x100)
+#define MAC_CR_MCPAS_ (0x00080000)
+#define MAC_CR_PRMS_ (0x00040000)
+#define MAC_CR_HPFILT_ (0x00002000)
+#define MAC_CR_TXEN_ (0x00000008)
+#define MAC_CR_RXEN_ (0x00000004)
+
+#define ADDRH (0x104)
+
+#define ADDRL (0x108)
+
+#define MII_ADDR (0x114)
+#define MII_WRITE_ (0x02)
+#define MII_BUSY_ (0x01)
+#define MII_READ_ (0x00) /* ~of MII Write bit */
+
+#define MII_DATA (0x118)
+
+#define FLOW (0x11C)
+
+#define VLAN1 (0x120)
+
+#define COE_CR (0x130)
+#define Tx_COE_EN_ (0x00010000)
+#define Rx_COE_EN_ (0x00000001)
+
+/* Vendor-specific PHY Definitions */
+#define PHY_INT_SRC (29)
+
+#define PHY_INT_MASK (30)
+#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010)
+#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \
+ PHY_INT_MASK_LINK_DOWN_)
+
+/* USB Vendor Requests */
+#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0
+#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1
+
+/* Some extra defines */
+#define HS_USB_PKT_SIZE (512)
+#define FS_USB_PKT_SIZE (64)
+#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE)
+#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE)
+#define DEFAULT_BULK_IN_DELAY (0x00002000)
+#define MAX_SINGLE_PACKET_SIZE (2048)
+#define EEPROM_MAC_OFFSET (0x01)
+#define SMSC95XX_INTERNAL_PHY_ID (1)
+#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
+
+/* local defines */
+#define SMSC95XX_BASE_NAME "sms"
+#define USB_CTRL_SET_TIMEOUT 5000
+#define USB_CTRL_GET_TIMEOUT 5000
+#define USB_BULK_SEND_TIMEOUT 5000
+#define USB_BULK_RECV_TIMEOUT 5000
+
+#define AX_RX_URB_SIZE 2048
+#define PHY_CONNECT_TIMEOUT 5000
+
+/* local vars */
+static int curr_eth_dev; /* index for name of next device detected */
+static int turbo_mode = 1;
+
+
+/*
+ * Smsc95xx infrastructure commands
+ */
+static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data)
+{
+ int len;
+
+ cpu_to_le32s(&data);
+
+ len = usb_control_msg(dev->pusb_dev, usb_sndctrlpipe(dev->pusb_dev, 0),
+ USB_VENDOR_REQUEST_WRITE_REGISTER,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00, index, &data, sizeof(data), USB_CTRL_SET_TIMEOUT);
+
+ return len == sizeof(data) ? 0 : -1;
+}
+
+static int smsc95xx_read_reg(struct ueth_data *dev, u32 index, u32 *data)
+{
+ int len;
+
+ len = usb_control_msg(dev->pusb_dev, usb_rcvctrlpipe(dev->pusb_dev, 0),
+ USB_VENDOR_REQUEST_READ_REGISTER,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00, index, data, sizeof(data), USB_CTRL_GET_TIMEOUT);
+
+ le32_to_cpus(data);
+ return len == sizeof(data) ? 0 : -1;
+}
+
+/* Loop until the read is completed with timeout */
+static int smsc95xx_phy_wait_not_busy(struct ueth_data *dev)
+{
+ unsigned long start_time = get_timer(0);
+ u32 val;
+
+ do {
+ smsc95xx_read_reg(dev, MII_ADDR, &val);
+ if (!(val & MII_BUSY_))
+ return 0;
+ } while (get_timer(start_time) < 1 * 1000 * 1000);
+
+ return -1;
+}
+
+static int smsc95xx_mdio_read(struct ueth_data *dev, int phy_id, int idx)
+{
+ u32 val, addr;
+
+ /* confirm MII not busy */
+ if (smsc95xx_phy_wait_not_busy(dev)) {
+ debug("MII is busy in smsc95xx_mdio_read\n");
+ return -1;
+ }
+
+ /* set the address, index & direction (read from PHY) */
+ addr = (phy_id << 11) | (idx << 6) | MII_READ_;
+ smsc95xx_write_reg(dev, MII_ADDR, addr);
+
+ if (smsc95xx_phy_wait_not_busy(dev)) {
+ debug("Timed out reading MII reg %02X\n", idx);
+ return -1;
+ }
+
+ smsc95xx_read_reg(dev, MII_DATA, &val);
+
+ return (u16)(val & 0xFFFF);
+}
+
+static void smsc95xx_mdio_write(struct ueth_data *dev, int phy_id, int idx,
+ int regval)
+{
+ u32 val, addr;
+
+ /* confirm MII not busy */
+ if (smsc95xx_phy_wait_not_busy(dev)) {
+ debug("MII is busy in smsc95xx_mdio_write\n");
+ return;
+ }
+
+ val = regval;
+ smsc95xx_write_reg(dev, MII_DATA, val);
+
+ /* set the address, index & direction (write to PHY) */
+ addr = (phy_id << 11) | (idx << 6) | MII_WRITE_;
+ smsc95xx_write_reg(dev, MII_ADDR, addr);
+
+ if (smsc95xx_phy_wait_not_busy(dev))
+ debug("Timed out writing MII reg %02X\n", idx);
+}
+
+static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev)
+{
+ unsigned long start_time = get_timer(0);
+ u32 val;
+
+ do {
+ smsc95xx_read_reg(dev, E2P_CMD, &val);
+ if (!(val & E2P_CMD_LOADED_)) {
+ debug("No EEPROM present\n");
+ return -1;
+ }
+ if (!(val & E2P_CMD_BUSY_))
+ return 0;
+ udelay(40);
+ } while (get_timer(start_time) < 1 * 1000 * 1000);
+
+ debug("EEPROM is busy\n");
+ return -1;
+}
+
+static int smsc95xx_wait_eeprom(struct ueth_data *dev)
+{
+ unsigned long start_time = get_timer(0);
+ u32 val;
+
+ do {
+ smsc95xx_read_reg(dev, E2P_CMD, &val);
+ if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_))
+ break;
+ udelay(40);
+ } while (get_timer(start_time) < 1 * 1000 * 1000);
+
+ if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) {
+ debug("EEPROM read operation timeout\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int smsc95xx_read_eeprom(struct ueth_data *dev, u32 offset, u32 length,
+ u8 *data)
+{
+ u32 val;
+ int i, ret;
+
+ ret = smsc95xx_eeprom_confirm_not_busy(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < length; i++) {
+ val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_);
+ smsc95xx_write_reg(dev, E2P_CMD, val);
+
+ ret = smsc95xx_wait_eeprom(dev);
+ if (ret < 0)
+ return ret;
+
+ smsc95xx_read_reg(dev, E2P_DATA, &val);
+ data[i] = val & 0xFF;
+ offset++;
+ }
+ return 0;
+}
+
+/*
+ * mii_nway_restart - restart NWay (autonegotiation) for this interface
+ *
+ * Returns 0 on success, negative on error.
+ */
+static int mii_nway_restart(struct ueth_data *dev)
+{
+ int bmcr;
+ int r = -1;
+
+ /* if autoneg is off, it's an error */
+ bmcr = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMCR);
+
+ if (bmcr & BMCR_ANENABLE) {
+ bmcr |= BMCR_ANRESTART;
+ smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr);
+ r = 0;
+ }
+ return r;
+}
+
+static int smsc95xx_phy_initialize(struct ueth_data *dev)
+{
+ smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET);
+ smsc95xx_mdio_write(dev, dev->phy_id, MII_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM);
+
+ /* read to clear */
+ smsc95xx_mdio_read(dev, dev->phy_id, PHY_INT_SRC);
+
+ smsc95xx_mdio_write(dev, dev->phy_id, PHY_INT_MASK,
+ PHY_INT_MASK_DEFAULT_);
+ mii_nway_restart(dev);
+
+ debug("phy initialised succesfully\n");
+ return 0;
+}
+
+static int smsc95xx_init_mac_address(struct eth_device *eth,
+ struct ueth_data *dev)
+{
+ /* try reading mac address from EEPROM */
+ if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN,
+ eth->enetaddr) == 0) {
+ if (is_valid_ether_addr(eth->enetaddr)) {
+ /* eeprom values are valid so use them */
+ debug("MAC address read from EEPROM\n");
+ return 0;
+ }
+ }
+
+ /*
+ * No eeprom, or eeprom values are invalid. Generating a random MAC
+ * address is not safe. Just return an error.
+ */
+ return -1;
+}
+
+static int smsc95xx_write_hwaddr(struct eth_device *eth)
+{
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+ u32 addr_lo, addr_hi;
+ int ret;
+
+ /* set hardware address */
+ debug("** %s()\n", __func__);
+ addr_lo = cpu_to_le32(*((u32 *)eth->enetaddr));
+ addr_hi = cpu_to_le16(*((u16 *)(eth->enetaddr + 4)));
+ ret = smsc95xx_write_reg(dev, ADDRL, addr_lo);
+ if (ret < 0) {
+ debug("Failed to write ADDRL: %d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_write_reg(dev, ADDRH, addr_hi);
+ if (ret < 0) {
+ debug("Failed to write ADDRH: %d\n", ret);
+ return ret;
+ }
+ debug ("MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+ eth->enetaddr [0], eth->enetaddr [1],
+ eth->enetaddr [2], eth->enetaddr [3],
+ eth->enetaddr [4], eth->enetaddr [5]);
+ dev->have_hwaddr = 1;
+ return 0;
+}
+
+/* Enable or disable Tx & Rx checksum offload engines */
+static int smsc95xx_set_csums(struct ueth_data *dev,
+ int use_tx_csum, int use_rx_csum)
+{
+ u32 read_buf;
+ int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read COE_CR: %d\n", ret);
+ return ret;
+ }
+
+ if (use_tx_csum)
+ read_buf |= Tx_COE_EN_;
+ else
+ read_buf &= ~Tx_COE_EN_;
+
+ if (use_rx_csum)
+ read_buf |= Rx_COE_EN_;
+ else
+ read_buf &= ~Rx_COE_EN_;
+
+ ret = smsc95xx_write_reg(dev, COE_CR, read_buf);
+ if (ret < 0) {
+ debug("Failed to write COE_CR: %d\n", ret);
+ return ret;
+ }
+
+ debug("COE_CR = 0x%08x\n", read_buf);
+ return 0;
+}
+
+static void smsc95xx_set_multicast(struct ueth_data *dev)
+{
+ /* No multicast in u-boot */
+ dev->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+}
+
+/* starts the TX path */
+static void smsc95xx_start_tx_path(struct ueth_data *dev)
+{
+ u32 reg_val;
+
+ /* Enable Tx at MAC */
+ dev->mac_cr |= MAC_CR_TXEN_;
+
+ smsc95xx_write_reg(dev, MAC_CR, dev->mac_cr);
+
+ /* Enable Tx at SCSRs */
+ reg_val = TX_CFG_ON_;
+ smsc95xx_write_reg(dev, TX_CFG, reg_val);
+}
+
+/* Starts the Receive path */
+static void smsc95xx_start_rx_path(struct ueth_data *dev)
+{
+ dev->mac_cr |= MAC_CR_RXEN_;
+ smsc95xx_write_reg(dev, MAC_CR, dev->mac_cr);
+}
+
+/*
+ * Smsc95xx callbacks
+ */
+static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
+{
+ int ret;
+ u32 write_buf;
+ u32 read_buf;
+ u32 burst_cap;
+ int timeout;
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+#define TIMEOUT_RESOLUTION 50 /* ms */
+ int link_detected;
+
+ debug("** %s()\n", __func__);
+ dev->phy_id = SMSC95XX_INTERNAL_PHY_ID; /* fixed phy id */
+
+ write_buf = HW_CFG_LRST_;
+ ret = smsc95xx_write_reg(dev, HW_CFG, write_buf);
+ if (ret < 0) {
+ debug("Failed to write HW_CFG_LRST_ bit in HW_CFG "
+ "register, ret = %d\n", ret);
+ return ret;
+ }
+
+ timeout = 0;
+ do {
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read HW_CFG: %d\n", ret);
+ return ret;
+ }
+ udelay(10 * 1000);
+ timeout++;
+ } while ((read_buf & HW_CFG_LRST_) && (timeout < 100));
+
+ if (timeout >= 100) {
+ debug("timeout waiting for completion of Lite Reset\n");
+ return -1;
+ }
+
+ write_buf = PM_CTL_PHY_RST_;
+ ret = smsc95xx_write_reg(dev, PM_CTRL, write_buf);
+ if (ret < 0) {
+ debug("Failed to write PM_CTRL: %d\n", ret);
+ return ret;
+ }
+
+ timeout = 0;
+ do {
+ ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read PM_CTRL: %d\n", ret);
+ return ret;
+ }
+ udelay(10 * 1000);
+ timeout++;
+ } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100));
+ if (timeout >= 100) {
+ debug("timeout waiting for PHY Reset\n");
+ return -1;
+ }
+ if (!dev->have_hwaddr && smsc95xx_init_mac_address(eth, dev) == 0)
+ dev->have_hwaddr = 1;
+ if (!dev->have_hwaddr) {
+ puts("Error: SMSC95xx: No MAC address set - set usbethaddr\n");
+ return -1;
+ }
+ if (smsc95xx_write_hwaddr(eth) < 0)
+ return -1;
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read HW_CFG: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from HW_CFG : 0x%08x\n", read_buf);
+
+ read_buf |= HW_CFG_BIR_;
+ ret = smsc95xx_write_reg(dev, HW_CFG, read_buf);
+ if (ret < 0) {
+ debug("Failed to write HW_CFG_BIR_ bit in HW_CFG "
+ "register, ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read HW_CFG: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from HW_CFG after writing "
+ "HW_CFG_BIR_: 0x%08x\n", read_buf);
+
+ if (!turbo_mode) {
+ burst_cap = 0;
+ dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
+ } else if (dev->pusb_dev->speed == USB_SPEED_HIGH) {
+ burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
+ dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
+ } else {
+ burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
+ dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
+ }
+ debug("rx_urb_size=%ld\n", (ulong)dev->rx_urb_size);
+
+ ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap);
+ if (ret < 0) {
+ debug("Failed to write BURST_CAP: %d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read BURST_CAP: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from BURST_CAP after writing: 0x%08x\n", read_buf);
+
+ read_buf = DEFAULT_BULK_IN_DELAY;
+ ret = smsc95xx_write_reg(dev, BULK_IN_DLY, read_buf);
+ if (ret < 0) {
+ debug("ret = %d", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read BULK_IN_DLY: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from BULK_IN_DLY after writing: "
+ "0x%08x\n", read_buf);
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read HW_CFG: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from HW_CFG: 0x%08x\n", read_buf);
+
+ if (turbo_mode)
+ read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_);
+ read_buf &= ~HW_CFG_RXDOFF_;
+
+#ifdef CONFIG_TEGRA2
+ /* Tegra2 requires NET_IP_ALIGN = 0 */
+#define NET_IP_ALIGN 0
+#else
+ /* set Rx data offset=2, Make IP header aligns on word boundary. */
+#define NET_IP_ALIGN 2
+#endif
+ read_buf |= NET_IP_ALIGN << 9;
+
+ ret = smsc95xx_write_reg(dev, HW_CFG, read_buf);
+ if (ret < 0) {
+ debug("Failed to write HW_CFG register, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read HW_CFG: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from HW_CFG after writing: 0x%08x\n", read_buf);
+
+ write_buf = 0xFFFFFFFF;
+ ret = smsc95xx_write_reg(dev, INT_STS, write_buf);
+ if (ret < 0) {
+ debug("Failed to write INT_STS register, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, ID_REV, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read ID_REV: %d\n", ret);
+ return ret;
+ }
+ debug("ID_REV = 0x%08x\n", read_buf);
+
+ /* Init Tx */
+ write_buf = 0;
+ ret = smsc95xx_write_reg(dev, FLOW, write_buf);
+ if (ret < 0) {
+ debug("Failed to write FLOW: %d\n", ret);
+ return ret;
+ }
+
+ read_buf = AFC_CFG_DEFAULT;
+ ret = smsc95xx_write_reg(dev, AFC_CFG, read_buf);
+ if (ret < 0) {
+ debug("Failed to write AFC_CFG: %d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, MAC_CR, &dev->mac_cr);
+ if (ret < 0) {
+ debug("Failed to read MAC_CR: %d\n", ret);
+ return ret;
+ }
+
+ /* Init Rx. Set Vlan */
+ write_buf = (u32)ETH_P_8021Q;
+ ret = smsc95xx_write_reg(dev, VLAN1, write_buf);
+ if (ret < 0) {
+ debug("Failed to write VAN1: %d\n", ret);
+ return ret;
+ }
+
+ /* Disable checksum offload engines */
+ ret = smsc95xx_set_csums(dev, 0, 0);
+ if (ret < 0) {
+ debug("Failed to set csum offload: %d\n", ret);
+ return ret;
+ }
+ smsc95xx_set_multicast(dev);
+
+ if (smsc95xx_phy_initialize(dev) < 0)
+ return -1;
+ ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read INT_EP_CTL: %d", ret);
+ return ret;
+ }
+ /* enable PHY interrupts */
+ read_buf |= INT_EP_CTL_PHY_INT_;
+
+ ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf);
+ if (ret < 0) {
+ debug("Failed to write INT_EP_CTL: %d", ret);
+ return ret;
+ }
+
+ smsc95xx_start_tx_path(dev);
+ smsc95xx_start_rx_path(dev);
+
+ timeout = 0;
+ do {
+ link_detected = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMSR)
+ & BMSR_LSTATUS;
+ if (!link_detected) {
+ if (timeout == 0)
+ printf("Waiting for Ethernet connection... ");
+ udelay(TIMEOUT_RESOLUTION * 1000);
+ timeout += TIMEOUT_RESOLUTION;
+ }
+ } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT);
+ if (link_detected) {
+ if (timeout != 0)
+ printf("done.\n");
+ } else {
+ printf("unable to connect.\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int smsc95xx_send(struct eth_device *eth, volatile void* packet,
+ int length)
+{
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+ int err;
+ int actual_len;
+ u32 tx_cmd_a;
+ u32 tx_cmd_b;
+ unsigned char msg[PKTSIZE + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)];
+
+ debug("** %s(), len %d, buf %#x\n", __func__, length, (int)msg);
+ if (length > PKTSIZE)
+ return -1;
+
+ tx_cmd_a = (u32)length | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ tx_cmd_b = (u32)length;
+ cpu_to_le32s(&tx_cmd_a);
+ cpu_to_le32s(&tx_cmd_b);
+
+ /* prepend cmd_a and cmd_b */
+ memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a));
+ memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b));
+ memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet,
+ length);
+ err = usb_bulk_msg(dev->pusb_dev,
+ usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
+ (void *)msg,
+ length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
+ &actual_len,
+ USB_BULK_SEND_TIMEOUT);
+ debug("Tx: len = %u, actual = %u, err = %d\n",
+ length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
+ actual_len, err);
+ return err;
+}
+
+static int smsc95xx_recv(struct eth_device *eth)
+{
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+ static unsigned char recv_buf[AX_RX_URB_SIZE];
+ unsigned char *buf_ptr;
+ int err;
+ int actual_len;
+ u32 packet_len;
+ int cur_buf_align;
+
+ debug("** %s()\n", __func__);
+ err = usb_bulk_msg(dev->pusb_dev,
+ usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
+ (void *)recv_buf,
+ AX_RX_URB_SIZE,
+ &actual_len,
+ USB_BULK_RECV_TIMEOUT);
+ debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE,
+ actual_len, err);
+ if (err != 0) {
+ debug("Rx: failed to receive\n");
+ return -1;
+ }
+ if (actual_len > AX_RX_URB_SIZE) {
+ debug("Rx: received too many bytes %d\n", actual_len);
+ return -1;
+ }
+
+ buf_ptr = recv_buf;
+ while (actual_len > 0) {
+ /*
+ * 1st 4 bytes contain the length of the actual data plus error
+ * info. Extract data length.
+ */
+ if (actual_len < sizeof(packet_len)) {
+ debug("Rx: incomplete packet length\n");
+ return -1;
+ }
+ memcpy(&packet_len, buf_ptr, sizeof(packet_len));
+ le32_to_cpus(&packet_len);
+ if (packet_len & RX_STS_ES_) {
+ debug("Rx: Error header=%#x", packet_len);
+ return -1;
+ }
+ packet_len = ((packet_len & RX_STS_FL_) >> 16);
+
+ if (packet_len > actual_len - sizeof(packet_len)) {
+ debug("Rx: too large packet: %d\n", packet_len);
+ return -1;
+ }
+
+ /* Notify net stack */
+ NetReceive(buf_ptr + sizeof(packet_len), packet_len - 4);
+
+ /* Adjust for next iteration */
+ actual_len -= sizeof(packet_len) + packet_len;
+ buf_ptr += sizeof(packet_len) + packet_len;
+ cur_buf_align = (int)buf_ptr - (int)recv_buf;
+
+ if (cur_buf_align & 0x03) {
+ int align = 4 - (cur_buf_align & 0x03);
+
+ actual_len -= align;
+ buf_ptr += align;
+ }
+ }
+ return err;
+}
+
+static void smsc95xx_halt(struct eth_device *eth)
+{
+ debug("** %s()\n", __func__);
+}
+
+/*
+ * SMSC probing functions
+ */
+void smsc95xx_eth_before_probe(void)
+{
+ curr_eth_dev = 0;
+}
+
+struct smsc95xx_dongle {
+ unsigned short vendor;
+ unsigned short product;
+};
+
+static struct smsc95xx_dongle smsc95xx_dongles[] = {
+ { 0x0424, 0xec00 }, /* LAN9512/LAN9514 Ethernet */
+ { 0x0424, 0x9500 }, /* LAN9500 Ethernet */
+ { 0x0000, 0x0000 } /* END - Do not remove */
+};
+
+/* Probe to see if a new device is actually an SMSC device */
+int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
+ struct ueth_data *ss)
+{
+ struct usb_interface *iface;
+ struct usb_interface_descriptor *iface_desc;
+ int i;
+
+ /* let's examine the device now */
+ iface = &dev->config.if_desc[ifnum];
+ iface_desc = &dev->config.if_desc[ifnum].desc;
+
+ for (i = 0; smsc95xx_dongles[i].vendor != 0; i++) {
+ if (dev->descriptor.idVendor == smsc95xx_dongles[i].vendor &&
+ dev->descriptor.idProduct == smsc95xx_dongles[i].product)
+ /* Found a supported dongle */
+ break;
+ }
+ if (smsc95xx_dongles[i].vendor == 0)
+ return 0;
+
+ /* At this point, we know we've got a live one */
+ debug("\n\nUSB Ethernet device detected\n");
+ memset(ss, '\0', sizeof(struct ueth_data));
+
+ /* Initialize the ueth_data structure with some useful info */
+ ss->ifnum = ifnum;
+ ss->pusb_dev = dev;
+ ss->subclass = iface_desc->bInterfaceSubClass;
+ ss->protocol = iface_desc->bInterfaceProtocol;
+
+ /*
+ * We are expecting a minimum of 3 endpoints - in, out (bulk), and int.
+ * We will ignore any others.
+ */
+ for (i = 0; i < iface_desc->bNumEndpoints; i++) {
+ /* is it an BULK endpoint? */
+ if ((iface->ep_desc[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+ if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
+ ss->ep_in =
+ iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ else
+ ss->ep_out =
+ iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+
+ /* is it an interrupt endpoint? */
+ if ((iface->ep_desc[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+ ss->ep_int = iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ ss->irqinterval = iface->ep_desc[i].bInterval;
+ }
+ }
+ debug("Endpoints In %d Out %d Int %d\n",
+ ss->ep_in, ss->ep_out, ss->ep_int);
+
+ /* Do some basic sanity checks, and bail if we find a problem */
+ if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
+ !ss->ep_in || !ss->ep_out || !ss->ep_int) {
+ debug("Problems with device\n");
+ return 0;
+ }
+ dev->privptr = (void *)ss;
+ return 1;
+}
+
+int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
+ struct eth_device *eth)
+{
+ debug("** %s()\n", __func__);
+ if (!eth) {
+ debug("%s: missing parameter.\n", __func__);
+ return 0;
+ }
+ sprintf(eth->name, "%s%d", SMSC95XX_BASE_NAME, curr_eth_dev++);
+ eth->init = smsc95xx_init;
+ eth->send = smsc95xx_send;
+ eth->recv = smsc95xx_recv;
+ eth->halt = smsc95xx_halt;
+ eth->priv = ss;
+ return 1;
+}
diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c
index 68a0883..24a20d0 100644
--- a/drivers/usb/eth/usb_ether.c
+++ b/drivers/usb/eth/usb_ether.c
@@ -45,6 +45,13 @@ static const struct usb_eth_prob_dev prob_dev[] = {
.get_info = asix_eth_get_info,
},
#endif
+#ifdef CONFIG_USB_ETHER_SMSC95XX
+ {
+ .before_probe = smsc95xx_eth_before_probe,
+ .probe = smsc95xx_eth_probe,
+ .get_info = smsc95xx_eth_get_info,
+ },
+#endif
{ }, /* END */
};
--
1.7.3.1
3
16
This patch adds support for OpenCores tiny_spi.
http://opencores.org/project,tiny_spi
Signed-off-by: Thomas Chou <thomas(a)wytron.com.tw>
---
drivers/spi/Makefile | 1 +
drivers/spi/oc_tiny_spi.c | 241 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 242 insertions(+), 0 deletions(-)
create mode 100644 drivers/spi/oc_tiny_spi.c
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e34a124..8ad1d7f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -35,6 +35,7 @@ COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o
+COBJS-$(CONFIG_OC_TINY_SPI) += oc_tiny_spi.o
COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o
COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o
diff --git a/drivers/spi/oc_tiny_spi.c b/drivers/spi/oc_tiny_spi.c
new file mode 100644
index 0000000..c234d90
--- /dev/null
+++ b/drivers/spi/oc_tiny_spi.c
@@ -0,0 +1,241 @@
+/*
+ * Opencore tiny_spi driver
+ *
+ * http://opencores.org/project,tiny_spi
+ *
+ * based on bfin_spi.c
+ * Copyright (c) 2005-2008 Analog Devices Inc.
+ * Copyright (C) 2010 Thomas Chou <thomas(a)wytron.com.tw>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <spi.h>
+#include <asm/gpio.h>
+#define TINY_SPI_RXDATA 0
+#define TINY_SPI_TXDATA 4
+#define TINY_SPI_STATUS 8
+#define TINY_SPI_CONTROL 12
+#define TINY_SPI_BAUD 16
+
+#define TINY_SPI_STATUS_TXE 0x1
+#define TINY_SPI_STATUS_TXR 0x2
+
+struct tiny_spi_host {
+ ulong base;
+ uint freq;
+ uint baudwidth;
+};
+static struct tiny_spi_host tiny_spi_host_list[] = CONFIG_SYS_TINY_SPI_LIST;
+
+struct tiny_spi_slave {
+ struct spi_slave slave;
+ struct tiny_spi_host *host;
+ uint mode;
+ uint baud;
+ uint flg;
+};
+#define to_tiny_spi_slave(s) container_of(s, struct tiny_spi_slave, slave)
+
+__attribute__((weak))
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+ return bus < ARRAY_SIZE(tiny_spi_host_list) && gpio_is_valid(cs);
+}
+
+__attribute__((weak))
+void spi_cs_activate(struct spi_slave *slave)
+{
+ struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
+ unsigned int cs = slave->cs;
+ gpio_set_value(cs, tiny_spi->flg);
+ debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs));
+}
+
+__attribute__((weak))
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+ struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
+ unsigned int cs = slave->cs;
+ gpio_set_value(cs, !tiny_spi->flg);
+ debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs));
+}
+
+void spi_set_speed(struct spi_slave *slave, uint hz)
+{
+ struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
+ struct tiny_spi_host *host = tiny_spi->host;
+ tiny_spi->baud = DIV_ROUND_UP(host->freq, hz * 2) - 1;
+ if (tiny_spi->baud > (1 << host->baudwidth) - 1)
+ tiny_spi->baud = (1 << host->baudwidth) - 1;
+ debug("%s: speed %u actual %u\n", __func__, hz,
+ host->freq / ((tiny_spi->baud + 1) * 2));
+}
+
+void spi_init(void)
+{
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int hz, unsigned int mode)
+{
+ struct tiny_spi_slave *tiny_spi;
+
+ if (!spi_cs_is_valid(bus, cs) || gpio_request(cs, "tiny_spi"))
+ return NULL;
+
+ tiny_spi = malloc(sizeof(*tiny_spi));
+ if (!tiny_spi)
+ return NULL;
+ memset(tiny_spi, 0, sizeof(*tiny_spi));
+
+ tiny_spi->slave.bus = bus;
+ tiny_spi->slave.cs = cs;
+ tiny_spi->host = &tiny_spi_host_list[bus];
+ tiny_spi->mode = mode & (SPI_CPOL | SPI_CPHA);
+ tiny_spi->flg = mode & SPI_CS_HIGH ? 1 : 0;
+ spi_set_speed(&tiny_spi->slave, hz);
+
+ debug("%s: bus:%i cs:%i base:%lx\n", __func__,
+ bus, cs, tiny_spi->host->base);
+ return &tiny_spi->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
+ gpio_free(slave->cs);
+ free(tiny_spi);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
+ struct tiny_spi_host *host = tiny_spi->host;
+ debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
+ gpio_direction_output(slave->cs, !tiny_spi->flg);
+ writel(tiny_spi->mode, host->base + TINY_SPI_CONTROL);
+ writel(tiny_spi->baud, host->base + TINY_SPI_BAUD);
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
+}
+
+#ifndef CONFIG_TINY_SPI_IDLE_VAL
+# define CONFIG_TINY_SPI_IDLE_VAL 0xff
+#endif
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
+ void *din, unsigned long flags)
+{
+ struct tiny_spi_host *host = to_tiny_spi_slave(slave)->host;
+ const u8 *txp = dout;
+ u8 *rxp = din;
+ uint bytes = bitlen / 8;
+ uint i;
+
+ debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
+ slave->bus, slave->cs, bitlen, bytes, flags);
+ if (bitlen == 0)
+ goto done;
+
+ /* assume to do 8 bits transfers */
+ if (bitlen % 8) {
+ flags |= SPI_XFER_END;
+ goto done;
+ }
+
+ if (flags & SPI_XFER_BEGIN)
+ spi_cs_activate(slave);
+
+ /* we need to tighten the transfer loop */
+ if (txp && rxp) {
+ writeb(*txp++, host->base + TINY_SPI_TXDATA);
+ if (bytes > 1) {
+ writeb(*txp++, host->base + TINY_SPI_TXDATA);
+ for (i = 2; i < bytes; i++) {
+ u8 rx, tx = *txp++;
+ while (!(readb(host->base + TINY_SPI_STATUS) &
+ TINY_SPI_STATUS_TXR))
+ ;
+ rx = readb(host->base + TINY_SPI_TXDATA);
+ writeb(tx, host->base + TINY_SPI_TXDATA);
+ *rxp++ = rx;
+ }
+ while (!(readb(host->base + TINY_SPI_STATUS) &
+ TINY_SPI_STATUS_TXR))
+ ;
+ *rxp++ = readb(host->base + TINY_SPI_TXDATA);
+ }
+ while (!(readb(host->base + TINY_SPI_STATUS) &
+ TINY_SPI_STATUS_TXE))
+ ;
+ *rxp++ = readb(host->base + TINY_SPI_RXDATA);
+ } else if (rxp) {
+ writeb(CONFIG_TINY_SPI_IDLE_VAL, host->base + TINY_SPI_TXDATA);
+ if (bytes > 1) {
+ writeb(CONFIG_TINY_SPI_IDLE_VAL,
+ host->base + TINY_SPI_TXDATA);
+ for (i = 2; i < bytes; i++) {
+ u8 rx;
+ while (!(readb(host->base + TINY_SPI_STATUS) &
+ TINY_SPI_STATUS_TXR))
+ ;
+ rx = readb(host->base + TINY_SPI_TXDATA);
+ writeb(CONFIG_TINY_SPI_IDLE_VAL,
+ host->base + TINY_SPI_TXDATA);
+ *rxp++ = rx;
+ }
+ while (!(readb(host->base + TINY_SPI_STATUS) &
+ TINY_SPI_STATUS_TXR))
+ ;
+ *rxp++ = readb(host->base + TINY_SPI_TXDATA);
+ }
+ while (!(readb(host->base + TINY_SPI_STATUS) &
+ TINY_SPI_STATUS_TXE))
+ ;
+ *rxp++ = readb(host->base + TINY_SPI_RXDATA);
+ } else if (txp) {
+ writeb(*txp++, host->base + TINY_SPI_TXDATA);
+ if (bytes > 1) {
+ writeb(*txp++, host->base + TINY_SPI_TXDATA);
+ for (i = 2; i < bytes; i++) {
+ u8 tx = *txp++;
+ while (!(readb(host->base + TINY_SPI_STATUS) &
+ TINY_SPI_STATUS_TXR))
+ ;
+ writeb(tx, host->base + TINY_SPI_TXDATA);
+ }
+ }
+ while (!(readb(host->base + TINY_SPI_STATUS) &
+ TINY_SPI_STATUS_TXE))
+ ;
+ } else {
+ writeb(CONFIG_TINY_SPI_IDLE_VAL, host->base + TINY_SPI_TXDATA);
+ if (bytes > 1) {
+ writeb(CONFIG_TINY_SPI_IDLE_VAL,
+ host->base + TINY_SPI_TXDATA);
+ for (i = 2; i < bytes; i++) {
+ while (!(readb(host->base + TINY_SPI_STATUS) &
+ TINY_SPI_STATUS_TXR))
+ ;
+ writeb(CONFIG_TINY_SPI_IDLE_VAL,
+ host->base + TINY_SPI_TXDATA);
+ }
+ }
+ while (!(readb(host->base + TINY_SPI_STATUS) &
+ TINY_SPI_STATUS_TXE))
+ ;
+ }
+
+ done:
+ if (flags & SPI_XFER_END)
+ spi_cs_deactivate(slave);
+
+ return 0;
+}
--
1.7.3.4
3
8

[U-Boot] [PATCH] powerpc/85xx: Add support for 2nd USB controller on p1_p2_rdb
by Kumar Gala 30 Apr '11
by Kumar Gala 30 Apr '11
30 Apr '11
From: Ramneek Mehresh <ramneek.mehresh(a)freescale.com>
Second USB controller only works for SPI and SD boot because of pin muxing
Signed-off-by: Ramneek Mehresh <ramneek.mehresh(a)freescale.com>
---
arch/powerpc/include/asm/immap_85xx.h | 1 +
board/freescale/p1_p2_rdb/p1_p2_rdb.c | 14 ++++++++++++++
include/configs/P1_P2_RDB.h | 1 +
3 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/immap_85xx.h b/arch/powerpc/include/asm/immap_85xx.h
index 5486427..d6ac61a 100644
--- a/arch/powerpc/include/asm/immap_85xx.h
+++ b/arch/powerpc/include/asm/immap_85xx.h
@@ -1969,6 +1969,7 @@ typedef struct ccsr_gur {
#define MPC85xx_PMUXCR_SD_DATA 0x80000000
#define MPC85xx_PMUXCR_SDHC_CD 0x40000000
#define MPC85xx_PMUXCR_SDHC_WP 0x20000000
+#define MPC85xx_PMUXCR_ELBC_OFF_USB2_ON 0x01000000
#define MPC85xx_PMUXCR_TDM_ENA 0x00800000
#define MPC85xx_PMUXCR_QE0 0x00008000
#define MPC85xx_PMUXCR_QE1 0x00004000
diff --git a/board/freescale/p1_p2_rdb/p1_p2_rdb.c b/board/freescale/p1_p2_rdb/p1_p2_rdb.c
index 307c3e2..3bd6c38 100644
--- a/board/freescale/p1_p2_rdb/p1_p2_rdb.c
+++ b/board/freescale/p1_p2_rdb/p1_p2_rdb.c
@@ -46,6 +46,7 @@ DECLARE_GLOBAL_DATA_PTR;
#define RGMII_PHY_RST_SET 0x02000000
#define USB_RST_CLR 0x04000000
+#define USB2_PORT_OUT_EN 0x01000000
#define GPIO_DIR 0x060f0000
@@ -127,6 +128,19 @@ int checkboard (void)
return 0;
}
+int misc_init_r(void)
+{
+#if defined(CONFIG_SDCARD) || defined(CONFIG_SPIFLASH)
+ ccsr_gur_t *gur = (void *)CONFIG_SYS_MPC85xx_GUTS_ADDR;
+ ccsr_gpio_t *gpio = (void *)CONFIG_SYS_MPC85xx_GPIO_ADDR;
+
+ setbits_be32(&gpio->gpdir, USB2_PORT_OUT_EN);
+ setbits_be32(&gpio->gpdat, USB2_PORT_OUT_EN);
+ setbits_be32(&gur->pmuxcr, MPC85xx_PMUXCR_ELBC_OFF_USB2_ON);
+#endif
+ return 0;
+}
+
int board_early_init_r(void)
{
const unsigned int flashbase = CONFIG_SYS_FLASH_BASE;
diff --git a/include/configs/P1_P2_RDB.h b/include/configs/P1_P2_RDB.h
index 5907856..449329a 100644
--- a/include/configs/P1_P2_RDB.h
+++ b/include/configs/P1_P2_RDB.h
@@ -244,6 +244,7 @@ extern unsigned long get_board_sys_clk(unsigned long dummy);
#define CONFIG_SYS_FLASH_AMD_CHECK_DQ7
#define CONFIG_BOARD_EARLY_INIT_R /* call board_early_init_r function */
+#define CONFIG_MISC_INIT_R
#define CONFIG_HWCONFIG
#define CONFIG_SYS_INIT_RAM_LOCK 1
--
1.7.3.4
2
2

[U-Boot] [PATCH] powerpc/p4080: Add support for secure boot flow on CoreNet Based QorIQ chips
by Kumar Gala 30 Apr '11
by Kumar Gala 30 Apr '11
30 Apr '11
From: Ruchika Gupta <ruchika.gupta(a)freescale.com>
1. Create a temp mapping in AS=1 of CONFIG_SYS_MONITOR_BASE to
the 1M CONFIG_SYS_PBI_FLASH_WINDOW
(The CONFIG_SYS_PBI_FLASH_WINDOW is the address map for the flash
created by PBL within 0 - 3.5G memory range. The u-boot image at
this address has been validated by ISBC code)
2. Remove TLB entries for 0 - 3.5G created by ISBC code
3. Remove the LAW entry for the CONFIG_SYS_PBI_FLASH_WINDOW created by PBL
after switch to AS = 1
Flow for secure-boot for which the changes have been made is given below:
1. User loads the u-boot image in flash
2. PBL creates LAW for Flash at 0xc0000000
(Please note that ISBC expects all these addresses, images to be
validated, entry point etc within 0 - 3.5G range)
3. ISBC validates the u-boot image, and passes control to u-boot
at 0xcffffffc.
Signed-off-by: Ruchika Gupta <ruchika.gupta(a)freescale.com>
Acked-by: Scott Wood <scottwood(a)freescale.com>
---
arch/powerpc/cpu/mpc85xx/cpu_init.c | 7 ++++
arch/powerpc/cpu/mpc85xx/cpu_init_early.c | 10 +++++-
arch/powerpc/cpu/mpc85xx/start.S | 53 +++++++++++++++++++++++++++-
arch/powerpc/include/asm/fsl_secure_boot.h | 40 +++++++++++++++++++++
boards.cfg | 1 +
include/configs/corenet_ds.h | 4 ++
6 files changed, 113 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/include/asm/fsl_secure_boot.h
diff --git a/arch/powerpc/cpu/mpc85xx/cpu_init.c b/arch/powerpc/cpu/mpc85xx/cpu_init.c
index 6f256cf..aafb007 100644
--- a/arch/powerpc/cpu/mpc85xx/cpu_init.c
+++ b/arch/powerpc/cpu/mpc85xx/cpu_init.c
@@ -239,6 +239,13 @@ void cpu_init_f (void)
disable_tlb(14);
disable_tlb(15);
+#ifdef CONFIG_SYS_ESBC_FLASH
+ /* Disable the LAW created for NOR flash by the PBI commands */
+ struct law_entry law = find_law(CONFIG_SYS_PBI_FLASH_BASE);
+ if (law.index != -1)
+ disable_law(law.index);
+#endif
+
#ifdef CONFIG_CPM2
config_8560_ioports((ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR);
#endif
diff --git a/arch/powerpc/cpu/mpc85xx/cpu_init_early.c b/arch/powerpc/cpu/mpc85xx/cpu_init_early.c
index 32aa94b..58397ec 100644
--- a/arch/powerpc/cpu/mpc85xx/cpu_init_early.c
+++ b/arch/powerpc/cpu/mpc85xx/cpu_init_early.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2009 Freescale Semiconductor, Inc
+ * Copyright 2009-2011 Freescale Semiconductor, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -108,5 +108,13 @@ void cpu_init_early_f(void)
init_laws();
invalidate_tlb(0);
+
+#if defined(CONFIG_SECURE_BOOT)
+/* Disable the TLBs created by ISBC */
+ for (i = CONFIG_SYS_ISBC_START_TLB;
+ i < CONFIG_SYS_ISBC_START_TLB + CONFIG_SYS_ISBC_NUM_TLBS; i++)
+ disable_tlb(i);
+#endif
+
init_tlbs();
}
diff --git a/arch/powerpc/cpu/mpc85xx/start.S b/arch/powerpc/cpu/mpc85xx/start.S
index 5777493..0caa336 100644
--- a/arch/powerpc/cpu/mpc85xx/start.S
+++ b/arch/powerpc/cpu/mpc85xx/start.S
@@ -88,6 +88,45 @@
_start_e500:
+#ifdef CONFIG_SECURE_BOOT
+ /* ISBC uses L2 as stack.
+ Disable L2 cache here so that u-boot can enable it later
+ as part of it's normal flow
+ */
+
+ /* Check if L2 is enabled */
+ mfspr r3, SPRN_L2CSR0
+ lis r2, L2CSR0_L2E@h
+ ori r2, r2, L2CSR0_L2E@l
+ and. r4, r3, r2
+ beq l2_disabled
+
+ mfspr r3, SPRN_L2CSR0
+ /* Flush L2 cache */
+ lis r2,(L2CSR0_L2FL)@h
+ ori r2, r2, (L2CSR0_L2FL)@l
+ or r3, r2, r3
+ sync
+ isync
+ mtspr SPRN_L2CSR0,r3
+ isync
+1:
+ mfspr r3, SPRN_L2CSR0
+ and. r1, r3, r2
+ bne 1b
+
+ mfspr r3, SPRN_L2CSR0
+ lis r2, L2CSR0_L2E@h
+ ori r2, r2, L2CSR0_L2E@l
+ andc r4, r3, r2
+ sync
+ isync
+ mtspr SPRN_L2CSR0,r4
+ isync
+
+l2_disabled:
+#endif
+
/* clear registers/arrays not reset by hardware */
/* L1 */
@@ -287,7 +326,7 @@ _start_e500:
lis r6,FSL_BOOKE_MAS0(1, 15, 0)@h
ori r6,r6,FSL_BOOKE_MAS0(1, 15, 0)@l
-#ifndef CONFIG_SYS_RAMBOOT
+#if !defined(CONFIG_SYS_RAMBOOT) && !defined(CONFIG_SECURE_BOOT)
/* create a temp mapping in AS=1 to the 4M boot window */
lis r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_4M)@h
ori r7,r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_4M)@l
@@ -298,6 +337,18 @@ _start_e500:
/* The 85xx has the default boot window 0xff800000 - 0xffffffff */
lis r9,FSL_BOOKE_MAS3(0xffc00000, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@h
ori r9,r9,FSL_BOOKE_MAS3(0xffc00000, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@l
+#elif defined(CONFIG_SECURE_BOOT)
+ /* create a temp mapping in AS = 1 for Flash mapping
+ * created by PBL for ISBC code
+ */
+ lis r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_1M)@h
+ ori r7,r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_1M)@l
+
+ lis r8,FSL_BOOKE_MAS2(CONFIG_SYS_MONITOR_BASE, (MAS2_I|MAS2_G))@h
+ ori r8,r8,FSL_BOOKE_MAS2(CONFIG_SYS_MONITOR_BASE, (MAS2_I|MAS2_G))@l
+
+ lis r9,FSL_BOOKE_MAS3(CONFIG_SYS_PBI_FLASH_WINDOW, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@h
+ ori r9,r9,FSL_BOOKE_MAS3(CONFIG_SYS_PBI_FLASH_WINDOW, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@l
#else
/*
* create a temp mapping in AS=1 to the 1M CONFIG_SYS_MONITOR_BASE space, the main
diff --git a/arch/powerpc/include/asm/fsl_secure_boot.h b/arch/powerpc/include/asm/fsl_secure_boot.h
new file mode 100644
index 0000000..786a98e
--- /dev/null
+++ b/arch/powerpc/include/asm/fsl_secure_boot.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+
+#ifndef __FSL_SECURE_BOOT_H
+#define __FSL_SECURE_BOOT_H
+
+/* This define indicates that u-boot's code is executing from flash
+ * after ISBC Validation */
+#define CONFIG_SYS_ESBC_FLASH
+
+/*Starting TLB number for the TLB entried for 3.5 G space created by ISBC */
+#define CONFIG_SYS_ISBC_START_TLB 3
+
+/* Number fo TLB's created by ISBC */
+#define CONFIG_SYS_ISBC_NUM_TLBS 5
+
+/* Address map where flash lies as done by PBI commands */
+#define CONFIG_SYS_PBI_FLASH_BASE 0xc0000000
+#define CONFIG_SYS_PBI_FLASH_WINDOW 0xcff80000
+
+#endif
diff --git a/boards.cfg b/boards.cfg
index cc1ae6c..6ffcd96 100644
--- a/boards.cfg
+++ b/boards.cfg
@@ -545,6 +545,7 @@ P2020RDB_SDCARD powerpc mpc85xx p1_p2_rdb freesca
P2020RDB_SPIFLASH powerpc mpc85xx p1_p2_rdb freescale - P1_P2_RDB:P2020RDB,SPIFLASH
P4080DS powerpc mpc85xx corenet_ds freescale
P4080DS_RAMBOOT_PBL powerpc mpc85xx corenet_ds freescale - P4080DS:RAMBOOT_PBL,SYS_TEXT_BASE=0xFFF80000
+P4080DS_SECURE_BOOT powerpc mpc85xx corenet_ds freescale - P4080DS:SECURE_BOOT
mpq101 powerpc mpc85xx mpq101 mercury - mpq101
stxgp3 powerpc mpc85xx stxgp3 stx
stxssa powerpc mpc85xx stxssa stx - stxssa
diff --git a/include/configs/corenet_ds.h b/include/configs/corenet_ds.h
index 2bd2553..9bc9017 100644
--- a/include/configs/corenet_ds.h
+++ b/include/configs/corenet_ds.h
@@ -607,4 +607,8 @@
#define CONFIG_BOOTCOMMAND CONFIG_HDBOOT
+#ifdef CONFIG_SECURE_BOOT
+#include <asm/fsl_secure_boot.h>
+#endif
+
#endif /* __CONFIG_H */
--
1.7.3.4
2
1
Hi Wolfgang,
thanks for your reply, this is not a test mail.
could you please let me know the steps to identify the timing of u-boot. As
u-boot gives the control to kernel, there won't be any u-boot code is
running on processor right.
Thanks and regards,
Manohar.K
On Wed, Apr 27, 2011 at 2:40 PM, Wolfgang Denk <wd(a)denx.de> wrote:
> Dear manohar kallutla,
>
> In message <BANLkTikeB=iFv_xR=YgmYjm7j=a7nA_VmQ(a)mail.gmail.com> you wrote:
> >
> > I am newbie to u-boot code. i have configure the 2009.08 u-boot code to
> my
> > board.
>
> You posted to a test mailing list.
>
> If this was a real test I can hereby confirm that it worked.
>
> If you intended to ask some question, you will have to post on some
> real mailing list instead, for example on the U-Boot mailing list.
>
> Best regards,
>
> Wolfgang Denk
>
> --
> DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd(a)denx.de
> Its always easier short term to pee in the pond
> than install a toilet - it's just not a good long term plan.
> - Alan Cox in <20100101145701.6432e7b7(a)lxorguk.ukuu.org.uk>
>
2
1

30 Apr '11
Hi,Detlev
2011/4/30 Detlev Zundel <dzu(a)denx.de>:
> Hi Baidu,
>
>> Hi,Detlev
>>
>>>> default:
>>>> /* unknown */
>>>> - putLabeledWord("UNKOWN
>>>> COMPRESSION METHOD = ", jNode->compr);
>>>> + putLabeledWord("UNKNOWN
>>>> COMPRESSION METHOD = ", jNode->compr);
>>>> put_fl_mem(jNode, pL->readbuf);
>>>> return -1;
>>>> break;
>>>
>>> This typo change is not mentioned in the change log and really does not
>>> belong here. Please put it into a separate changeset.
>>
>> This is just the typo error correction. Do you think we really need
>> another patch?
>
> A patch description should include _all_ changes. Either you also put
> that change into the patch description or you make a separate change.
> Doing changes not described in the commit log are a good sign to show
> that one does not care what one does.
Yes, you are right. Add all the description in the commit log.
But please firstly make this principle be applied for all guys
submiting patch. Not just to the people rarely summitint patch.
Be fair.
2
1

[U-Boot] [PATCH v4 1/5] Add support for SMSC95XX USB 2.0 10/100MBit Ethernet Adapter
by Simon Glass 30 Apr '11
by Simon Glass 30 Apr '11
30 Apr '11
The SMSC95XX is a USB hub with a built-in Ethernet adapter. This adds support
for this, using the USB host network framework.
TEST=usb start; bootp; tftp ...
Signed-off-by: Simon Glass <sjg(a)chromium.org>
---
Changes for v2:
- Coding style cleanup
- Changed some comments as suggested
- eth_set_hwaddr -> eth_write_hwaddr
- tided up other users of eth_getenv_enetaddr_by_index()
Changes for v3:
- Drop tfpserverip patch
- Change turbo_mode to #define
- Fix tfpserverip patch bleed
Changes for v4:
- Dropped Tegra2 specific bit
- Added patch in place of tftpserverip patch, to speed up successive network commands on asix
- Fixed a few broken bits in SMSC from my testing
---
drivers/usb/eth/Makefile | 1 +
drivers/usb/eth/smsc95xx.c | 921 +++++++++++++++++++++++++++++++++++++++++++
drivers/usb/eth/usb_ether.c | 7 +
include/usb_ether.h | 13 +
4 files changed, 942 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/eth/smsc95xx.c
diff --git a/drivers/usb/eth/Makefile b/drivers/usb/eth/Makefile
index 6a5f25a..e28793d 100644
--- a/drivers/usb/eth/Makefile
+++ b/drivers/usb/eth/Makefile
@@ -28,6 +28,7 @@ COBJS-$(CONFIG_USB_HOST_ETHER) += usb_ether.o
ifdef CONFIG_USB_ETHER_ASIX
COBJS-y += asix.o
endif
+COBJS-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c
new file mode 100644
index 0000000..6a2290f
--- /dev/null
+++ b/drivers/usb/eth/smsc95xx.c
@@ -0,0 +1,921 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * Copyright (C) 2009 NVIDIA, Corporation
+ * 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 <common.h>
+#include <usb.h>
+#include <linux/mii.h>
+#include "usb_ether.h"
+
+/* SMSC LAN95xx based USB 2.0 Ethernet Devices */
+
+/* Tx command words */
+#define TX_CMD_A_FIRST_SEG_ (0x00002000)
+#define TX_CMD_A_LAST_SEG_ (0x00001000)
+
+/* Rx status word */
+#define RX_STS_FL_ (0x3FFF0000) /* Frame Length */
+#define RX_STS_ES_ (0x00008000) /* Error Summary */
+
+/* SCSRs */
+#define ID_REV (0x00)
+
+#define INT_STS (0x08)
+
+#define TX_CFG (0x10)
+#define TX_CFG_ON_ (0x00000004)
+
+#define HW_CFG (0x14)
+#define HW_CFG_BIR_ (0x00001000)
+#define HW_CFG_RXDOFF_ (0x00000600)
+#define HW_CFG_MEF_ (0x00000020)
+#define HW_CFG_BCE_ (0x00000002)
+#define HW_CFG_LRST_ (0x00000008)
+
+#define PM_CTRL (0x20)
+#define PM_CTL_PHY_RST_ (0x00000010)
+
+#define AFC_CFG (0x2C)
+
+/*
+ * Hi watermark = 15.5Kb (~10 mtu pkts)
+ * low watermark = 3k (~2 mtu pkts)
+ * backpressure duration = ~ 350us
+ * Apply FC on any frame.
+ */
+#define AFC_CFG_DEFAULT (0x00F830A1)
+
+#define E2P_CMD (0x30)
+#define E2P_CMD_BUSY_ (0x80000000)
+#define E2P_CMD_READ_ (0x00000000)
+#define E2P_CMD_TIMEOUT_ (0x00000400)
+#define E2P_CMD_LOADED_ (0x00000200)
+#define E2P_CMD_ADDR_ (0x000001FF)
+
+#define E2P_DATA (0x34)
+
+#define BURST_CAP (0x38)
+
+#define INT_EP_CTL (0x68)
+#define INT_EP_CTL_PHY_INT_ (0x00008000)
+
+#define BULK_IN_DLY (0x6C)
+
+/* MAC CSRs */
+#define MAC_CR (0x100)
+#define MAC_CR_MCPAS_ (0x00080000)
+#define MAC_CR_PRMS_ (0x00040000)
+#define MAC_CR_HPFILT_ (0x00002000)
+#define MAC_CR_TXEN_ (0x00000008)
+#define MAC_CR_RXEN_ (0x00000004)
+
+#define ADDRH (0x104)
+
+#define ADDRL (0x108)
+
+#define MII_ADDR (0x114)
+#define MII_WRITE_ (0x02)
+#define MII_BUSY_ (0x01)
+#define MII_READ_ (0x00) /* ~of MII Write bit */
+
+#define MII_DATA (0x118)
+
+#define FLOW (0x11C)
+
+#define VLAN1 (0x120)
+
+#define COE_CR (0x130)
+#define Tx_COE_EN_ (0x00010000)
+#define Rx_COE_EN_ (0x00000001)
+
+/* Vendor-specific PHY Definitions */
+#define PHY_INT_SRC (29)
+
+#define PHY_INT_MASK (30)
+#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010)
+#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \
+ PHY_INT_MASK_LINK_DOWN_)
+
+/* USB Vendor Requests */
+#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0
+#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1
+
+/* Some extra defines */
+#define HS_USB_PKT_SIZE (512)
+#define FS_USB_PKT_SIZE (64)
+#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE)
+#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE)
+#define DEFAULT_BULK_IN_DELAY (0x00002000)
+#define MAX_SINGLE_PACKET_SIZE (2048)
+#define EEPROM_MAC_OFFSET (0x01)
+#define SMSC95XX_INTERNAL_PHY_ID (1)
+#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
+
+/* local defines */
+#define SMSC95XX_BASE_NAME "sms"
+#define USB_CTRL_SET_TIMEOUT 5000
+#define USB_CTRL_GET_TIMEOUT 5000
+#define USB_BULK_SEND_TIMEOUT 5000
+#define USB_BULK_RECV_TIMEOUT 5000
+
+#define AX_RX_URB_SIZE 2048
+#define PHY_CONNECT_TIMEOUT 5000
+
+#define TURBO_MODE
+
+/* local vars */
+static int curr_eth_dev; /* index for name of next device detected */
+
+
+/*
+ * Smsc95xx infrastructure commands
+ */
+static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data)
+{
+ int len;
+
+ cpu_to_le32s(&data);
+
+ len = usb_control_msg(dev->pusb_dev, usb_sndctrlpipe(dev->pusb_dev, 0),
+ USB_VENDOR_REQUEST_WRITE_REGISTER,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00, index, &data, sizeof(data), USB_CTRL_SET_TIMEOUT);
+
+ return len == sizeof(data) ? 0 : -1;
+}
+
+static int smsc95xx_read_reg(struct ueth_data *dev, u32 index, u32 *data)
+{
+ int len;
+
+ len = usb_control_msg(dev->pusb_dev, usb_rcvctrlpipe(dev->pusb_dev, 0),
+ USB_VENDOR_REQUEST_READ_REGISTER,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00, index, data, sizeof(data), USB_CTRL_GET_TIMEOUT);
+
+ le32_to_cpus(data);
+ return len == sizeof(data) ? 0 : -1;
+}
+
+/* Loop until the read is completed with timeout */
+static int smsc95xx_phy_wait_not_busy(struct ueth_data *dev)
+{
+ unsigned long start_time = get_timer(0);
+ u32 val;
+
+ do {
+ smsc95xx_read_reg(dev, MII_ADDR, &val);
+ if (!(val & MII_BUSY_))
+ return 0;
+ } while (get_timer(start_time) < 1 * 1000 * 1000);
+
+ return -1;
+}
+
+static int smsc95xx_mdio_read(struct ueth_data *dev, int phy_id, int idx)
+{
+ u32 val, addr;
+
+ /* confirm MII not busy */
+ if (smsc95xx_phy_wait_not_busy(dev)) {
+ debug("MII is busy in smsc95xx_mdio_read\n");
+ return -1;
+ }
+
+ /* set the address, index & direction (read from PHY) */
+ addr = (phy_id << 11) | (idx << 6) | MII_READ_;
+ smsc95xx_write_reg(dev, MII_ADDR, addr);
+
+ if (smsc95xx_phy_wait_not_busy(dev)) {
+ debug("Timed out reading MII reg %02X\n", idx);
+ return -1;
+ }
+
+ smsc95xx_read_reg(dev, MII_DATA, &val);
+
+ return (u16)(val & 0xFFFF);
+}
+
+static void smsc95xx_mdio_write(struct ueth_data *dev, int phy_id, int idx,
+ int regval)
+{
+ u32 val, addr;
+
+ /* confirm MII not busy */
+ if (smsc95xx_phy_wait_not_busy(dev)) {
+ debug("MII is busy in smsc95xx_mdio_write\n");
+ return;
+ }
+
+ val = regval;
+ smsc95xx_write_reg(dev, MII_DATA, val);
+
+ /* set the address, index & direction (write to PHY) */
+ addr = (phy_id << 11) | (idx << 6) | MII_WRITE_;
+ smsc95xx_write_reg(dev, MII_ADDR, addr);
+
+ if (smsc95xx_phy_wait_not_busy(dev))
+ debug("Timed out writing MII reg %02X\n", idx);
+}
+
+static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev)
+{
+ unsigned long start_time = get_timer(0);
+ u32 val;
+
+ do {
+ smsc95xx_read_reg(dev, E2P_CMD, &val);
+ if (!(val & E2P_CMD_LOADED_)) {
+ debug("No EEPROM present\n");
+ return -1;
+ }
+ if (!(val & E2P_CMD_BUSY_))
+ return 0;
+ udelay(40);
+ } while (get_timer(start_time) < 1 * 1000 * 1000);
+
+ debug("EEPROM is busy\n");
+ return -1;
+}
+
+static int smsc95xx_wait_eeprom(struct ueth_data *dev)
+{
+ unsigned long start_time = get_timer(0);
+ u32 val;
+
+ do {
+ smsc95xx_read_reg(dev, E2P_CMD, &val);
+ if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_))
+ break;
+ udelay(40);
+ } while (get_timer(start_time) < 1 * 1000 * 1000);
+
+ if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) {
+ debug("EEPROM read operation timeout\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int smsc95xx_read_eeprom(struct ueth_data *dev, u32 offset, u32 length,
+ u8 *data)
+{
+ u32 val;
+ int i, ret;
+
+ ret = smsc95xx_eeprom_confirm_not_busy(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < length; i++) {
+ val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_);
+ smsc95xx_write_reg(dev, E2P_CMD, val);
+
+ ret = smsc95xx_wait_eeprom(dev);
+ if (ret < 0)
+ return ret;
+
+ smsc95xx_read_reg(dev, E2P_DATA, &val);
+ data[i] = val & 0xFF;
+ offset++;
+ }
+ return 0;
+}
+
+/*
+ * mii_nway_restart - restart NWay (autonegotiation) for this interface
+ *
+ * Returns 0 on success, negative on error.
+ */
+static int mii_nway_restart(struct ueth_data *dev)
+{
+ int bmcr;
+ int r = -1;
+
+ /* if autoneg is off, it's an error */
+ bmcr = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMCR);
+
+ if (bmcr & BMCR_ANENABLE) {
+ bmcr |= BMCR_ANRESTART;
+ smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr);
+ r = 0;
+ }
+ return r;
+}
+
+static int smsc95xx_phy_initialize(struct ueth_data *dev)
+{
+ smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET);
+ smsc95xx_mdio_write(dev, dev->phy_id, MII_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM);
+
+ /* read to clear */
+ smsc95xx_mdio_read(dev, dev->phy_id, PHY_INT_SRC);
+
+ smsc95xx_mdio_write(dev, dev->phy_id, PHY_INT_MASK,
+ PHY_INT_MASK_DEFAULT_);
+ mii_nway_restart(dev);
+
+ debug("phy initialised succesfully\n");
+ return 0;
+}
+
+static int smsc95xx_init_mac_address(struct eth_device *eth,
+ struct ueth_data *dev)
+{
+ /* try reading mac address from EEPROM */
+ if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN,
+ eth->enetaddr) == 0) {
+ if (is_valid_ether_addr(eth->enetaddr)) {
+ /* eeprom values are valid so use them */
+ debug("MAC address read from EEPROM\n");
+ return 0;
+ }
+ }
+
+ /*
+ * No eeprom, or eeprom values are invalid. Generating a random MAC
+ * address is not safe. Just return an error.
+ */
+ return -1;
+}
+
+static int smsc95xx_write_hwaddr(struct eth_device *eth)
+{
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+ u32 addr_lo, addr_hi;
+ int ret;
+
+ /* set hardware address */
+ debug("** %s()\n", __func__);
+ addr_lo = cpu_to_le32(*((u32 *)eth->enetaddr));
+ addr_hi = cpu_to_le16(*((u16 *)(eth->enetaddr + 4)));
+ ret = smsc95xx_write_reg(dev, ADDRL, addr_lo);
+ if (ret < 0) {
+ debug("Failed to write ADDRL: %d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_write_reg(dev, ADDRH, addr_hi);
+ if (ret < 0) {
+ debug("Failed to write ADDRH: %d\n", ret);
+ return ret;
+ }
+ debug("MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+ eth->enetaddr[0], eth->enetaddr[1],
+ eth->enetaddr[2], eth->enetaddr[3],
+ eth->enetaddr[4], eth->enetaddr[5]);
+ dev->have_hwaddr = 1;
+ return 0;
+}
+
+/* Enable or disable Tx & Rx checksum offload engines */
+static int smsc95xx_set_csums(struct ueth_data *dev,
+ int use_tx_csum, int use_rx_csum)
+{
+ u32 read_buf;
+ int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read COE_CR: %d\n", ret);
+ return ret;
+ }
+
+ if (use_tx_csum)
+ read_buf |= Tx_COE_EN_;
+ else
+ read_buf &= ~Tx_COE_EN_;
+
+ if (use_rx_csum)
+ read_buf |= Rx_COE_EN_;
+ else
+ read_buf &= ~Rx_COE_EN_;
+
+ ret = smsc95xx_write_reg(dev, COE_CR, read_buf);
+ if (ret < 0) {
+ debug("Failed to write COE_CR: %d\n", ret);
+ return ret;
+ }
+
+ debug("COE_CR = 0x%08x\n", read_buf);
+ return 0;
+}
+
+static void smsc95xx_set_multicast(struct ueth_data *dev)
+{
+ /* No multicast in u-boot */
+ dev->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+}
+
+/* starts the TX path */
+static void smsc95xx_start_tx_path(struct ueth_data *dev)
+{
+ u32 reg_val;
+
+ /* Enable Tx at MAC */
+ dev->mac_cr |= MAC_CR_TXEN_;
+
+ smsc95xx_write_reg(dev, MAC_CR, dev->mac_cr);
+
+ /* Enable Tx at SCSRs */
+ reg_val = TX_CFG_ON_;
+ smsc95xx_write_reg(dev, TX_CFG, reg_val);
+}
+
+/* Starts the Receive path */
+static void smsc95xx_start_rx_path(struct ueth_data *dev)
+{
+ dev->mac_cr |= MAC_CR_RXEN_;
+ smsc95xx_write_reg(dev, MAC_CR, dev->mac_cr);
+}
+
+/*
+ * Smsc95xx callbacks
+ */
+static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
+{
+ int ret;
+ u32 write_buf;
+ u32 read_buf;
+ u32 burst_cap;
+ int timeout;
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+#define TIMEOUT_RESOLUTION 50 /* ms */
+ int link_detected;
+
+ debug("** %s()\n", __func__);
+ dev->phy_id = SMSC95XX_INTERNAL_PHY_ID; /* fixed phy id */
+
+ write_buf = HW_CFG_LRST_;
+ ret = smsc95xx_write_reg(dev, HW_CFG, write_buf);
+ if (ret < 0) {
+ debug("Failed to write HW_CFG_LRST_ bit in HW_CFG "
+ "register, ret = %d\n", ret);
+ return ret;
+ }
+
+ timeout = 0;
+ do {
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read HW_CFG: %d\n", ret);
+ return ret;
+ }
+ udelay(10 * 1000);
+ timeout++;
+ } while ((read_buf & HW_CFG_LRST_) && (timeout < 100));
+
+ if (timeout >= 100) {
+ debug("timeout waiting for completion of Lite Reset\n");
+ return -1;
+ }
+
+ write_buf = PM_CTL_PHY_RST_;
+ ret = smsc95xx_write_reg(dev, PM_CTRL, write_buf);
+ if (ret < 0) {
+ debug("Failed to write PM_CTRL: %d\n", ret);
+ return ret;
+ }
+
+ timeout = 0;
+ do {
+ ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read PM_CTRL: %d\n", ret);
+ return ret;
+ }
+ udelay(10 * 1000);
+ timeout++;
+ } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100));
+ if (timeout >= 100) {
+ debug("timeout waiting for PHY Reset\n");
+ return -1;
+ }
+ if (!dev->have_hwaddr && smsc95xx_init_mac_address(eth, dev) == 0)
+ dev->have_hwaddr = 1;
+ if (!dev->have_hwaddr) {
+ puts("Error: SMSC95xx: No MAC address set - set usbethaddr\n");
+ return -1;
+ }
+ if (smsc95xx_write_hwaddr(eth) < 0)
+ return -1;
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read HW_CFG: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from HW_CFG : 0x%08x\n", read_buf);
+
+ read_buf |= HW_CFG_BIR_;
+ ret = smsc95xx_write_reg(dev, HW_CFG, read_buf);
+ if (ret < 0) {
+ debug("Failed to write HW_CFG_BIR_ bit in HW_CFG "
+ "register, ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read HW_CFG: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from HW_CFG after writing "
+ "HW_CFG_BIR_: 0x%08x\n", read_buf);
+
+#ifdef TURBO_MODE
+ if (dev->pusb_dev->speed == USB_SPEED_HIGH) {
+ burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
+ dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
+ } else {
+ burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
+ dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
+ }
+#else
+ burst_cap = 0;
+ dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
+#endif
+ debug("rx_urb_size=%ld\n", (ulong)dev->rx_urb_size);
+
+ ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap);
+ if (ret < 0) {
+ debug("Failed to write BURST_CAP: %d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read BURST_CAP: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from BURST_CAP after writing: 0x%08x\n", read_buf);
+
+ read_buf = DEFAULT_BULK_IN_DELAY;
+ ret = smsc95xx_write_reg(dev, BULK_IN_DLY, read_buf);
+ if (ret < 0) {
+ debug("ret = %d", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read BULK_IN_DLY: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from BULK_IN_DLY after writing: "
+ "0x%08x\n", read_buf);
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read HW_CFG: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from HW_CFG: 0x%08x\n", read_buf);
+
+#ifdef TURBO_MODE
+ read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_);
+#endif
+ read_buf &= ~HW_CFG_RXDOFF_;
+
+ /* set Rx data offset=2, Make IP header aligns on word boundary. */
+#define NET_IP_ALIGN 2
+ read_buf |= NET_IP_ALIGN << 9;
+
+ ret = smsc95xx_write_reg(dev, HW_CFG, read_buf);
+ if (ret < 0) {
+ debug("Failed to write HW_CFG register, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read HW_CFG: %d\n", ret);
+ return ret;
+ }
+ debug("Read Value from HW_CFG after writing: 0x%08x\n", read_buf);
+
+ write_buf = 0xFFFFFFFF;
+ ret = smsc95xx_write_reg(dev, INT_STS, write_buf);
+ if (ret < 0) {
+ debug("Failed to write INT_STS register, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, ID_REV, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read ID_REV: %d\n", ret);
+ return ret;
+ }
+ debug("ID_REV = 0x%08x\n", read_buf);
+
+ /* Init Tx */
+ write_buf = 0;
+ ret = smsc95xx_write_reg(dev, FLOW, write_buf);
+ if (ret < 0) {
+ debug("Failed to write FLOW: %d\n", ret);
+ return ret;
+ }
+
+ read_buf = AFC_CFG_DEFAULT;
+ ret = smsc95xx_write_reg(dev, AFC_CFG, read_buf);
+ if (ret < 0) {
+ debug("Failed to write AFC_CFG: %d\n", ret);
+ return ret;
+ }
+
+ ret = smsc95xx_read_reg(dev, MAC_CR, &dev->mac_cr);
+ if (ret < 0) {
+ debug("Failed to read MAC_CR: %d\n", ret);
+ return ret;
+ }
+
+ /* Init Rx. Set Vlan */
+ write_buf = (u32)ETH_P_8021Q;
+ ret = smsc95xx_write_reg(dev, VLAN1, write_buf);
+ if (ret < 0) {
+ debug("Failed to write VAN1: %d\n", ret);
+ return ret;
+ }
+
+ /* Disable checksum offload engines */
+ ret = smsc95xx_set_csums(dev, 0, 0);
+ if (ret < 0) {
+ debug("Failed to set csum offload: %d\n", ret);
+ return ret;
+ }
+ smsc95xx_set_multicast(dev);
+
+ if (smsc95xx_phy_initialize(dev) < 0)
+ return -1;
+ ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf);
+ if (ret < 0) {
+ debug("Failed to read INT_EP_CTL: %d", ret);
+ return ret;
+ }
+ /* enable PHY interrupts */
+ read_buf |= INT_EP_CTL_PHY_INT_;
+
+ ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf);
+ if (ret < 0) {
+ debug("Failed to write INT_EP_CTL: %d", ret);
+ return ret;
+ }
+
+ smsc95xx_start_tx_path(dev);
+ smsc95xx_start_rx_path(dev);
+
+ timeout = 0;
+ do {
+ link_detected = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMSR)
+ & BMSR_LSTATUS;
+ if (!link_detected) {
+ if (timeout == 0)
+ printf("Waiting for Ethernet connection... ");
+ udelay(TIMEOUT_RESOLUTION * 1000);
+ timeout += TIMEOUT_RESOLUTION;
+ }
+ } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT);
+ if (link_detected) {
+ if (timeout != 0)
+ printf("done.\n");
+ } else {
+ printf("unable to connect.\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int smsc95xx_send(struct eth_device *eth, volatile void* packet,
+ int length)
+{
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+ int err;
+ int actual_len;
+ u32 tx_cmd_a;
+ u32 tx_cmd_b;
+ unsigned char msg[PKTSIZE + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)];
+
+ debug("** %s(), len %d, buf %#x\n", __func__, length, (int)msg);
+ if (length > PKTSIZE)
+ return -1;
+
+ tx_cmd_a = (u32)length | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ tx_cmd_b = (u32)length;
+ cpu_to_le32s(&tx_cmd_a);
+ cpu_to_le32s(&tx_cmd_b);
+
+ /* prepend cmd_a and cmd_b */
+ memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a));
+ memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b));
+ memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet,
+ length);
+ err = usb_bulk_msg(dev->pusb_dev,
+ usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
+ (void *)msg,
+ length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
+ &actual_len,
+ USB_BULK_SEND_TIMEOUT);
+ debug("Tx: len = %u, actual = %u, err = %d\n",
+ length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
+ actual_len, err);
+ return err;
+}
+
+static int smsc95xx_recv(struct eth_device *eth)
+{
+ struct ueth_data *dev = (struct ueth_data *)eth->priv;
+ static unsigned char recv_buf[AX_RX_URB_SIZE];
+ unsigned char *buf_ptr;
+ int err;
+ int actual_len;
+ u32 packet_len;
+ int cur_buf_align;
+
+ debug("** %s()\n", __func__);
+ err = usb_bulk_msg(dev->pusb_dev,
+ usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
+ (void *)recv_buf,
+ AX_RX_URB_SIZE,
+ &actual_len,
+ USB_BULK_RECV_TIMEOUT);
+ debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE,
+ actual_len, err);
+ if (err != 0) {
+ debug("Rx: failed to receive\n");
+ return -1;
+ }
+ if (actual_len > AX_RX_URB_SIZE) {
+ debug("Rx: received too many bytes %d\n", actual_len);
+ return -1;
+ }
+
+ buf_ptr = recv_buf;
+ while (actual_len > 0) {
+ /*
+ * 1st 4 bytes contain the length of the actual data plus error
+ * info. Extract data length.
+ */
+ if (actual_len < sizeof(packet_len)) {
+ debug("Rx: incomplete packet length\n");
+ return -1;
+ }
+ memcpy(&packet_len, buf_ptr, sizeof(packet_len));
+ le32_to_cpus(&packet_len);
+ if (packet_len & RX_STS_ES_) {
+ debug("Rx: Error header=%#x", packet_len);
+ return -1;
+ }
+ packet_len = ((packet_len & RX_STS_FL_) >> 16);
+
+ if (packet_len > actual_len - sizeof(packet_len)) {
+ debug("Rx: too large packet: %d\n", packet_len);
+ return -1;
+ }
+
+ /* Notify net stack */
+ NetReceive(buf_ptr + sizeof(packet_len), packet_len - 4);
+
+ /* Adjust for next iteration */
+ actual_len -= sizeof(packet_len) + packet_len;
+ buf_ptr += sizeof(packet_len) + packet_len;
+ cur_buf_align = (int)buf_ptr - (int)recv_buf;
+
+ if (cur_buf_align & 0x03) {
+ int align = 4 - (cur_buf_align & 0x03);
+
+ actual_len -= align;
+ buf_ptr += align;
+ }
+ }
+ return err;
+}
+
+static void smsc95xx_halt(struct eth_device *eth)
+{
+ debug("** %s()\n", __func__);
+}
+
+/*
+ * SMSC probing functions
+ */
+void smsc95xx_eth_before_probe(void)
+{
+ curr_eth_dev = 0;
+}
+
+struct smsc95xx_dongle {
+ unsigned short vendor;
+ unsigned short product;
+};
+
+static const struct smsc95xx_dongle smsc95xx_dongles[] = {
+ { 0x0424, 0xec00 }, /* LAN9512/LAN9514 Ethernet */
+ { 0x0424, 0x9500 }, /* LAN9500 Ethernet */
+ { 0x0000, 0x0000 } /* END - Do not remove */
+};
+
+/* Probe to see if a new device is actually an SMSC device */
+int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
+ struct ueth_data *ss)
+{
+ struct usb_interface *iface;
+ struct usb_interface_descriptor *iface_desc;
+ int i;
+
+ /* let's examine the device now */
+ iface = &dev->config.if_desc[ifnum];
+ iface_desc = &dev->config.if_desc[ifnum].desc;
+
+ for (i = 0; smsc95xx_dongles[i].vendor != 0; i++) {
+ if (dev->descriptor.idVendor == smsc95xx_dongles[i].vendor &&
+ dev->descriptor.idProduct == smsc95xx_dongles[i].product)
+ /* Found a supported dongle */
+ break;
+ }
+ if (smsc95xx_dongles[i].vendor == 0)
+ return 0;
+
+ /* At this point, we know we've got a live one */
+ debug("\n\nUSB Ethernet device detected\n");
+ memset(ss, '\0', sizeof(struct ueth_data));
+
+ /* Initialize the ueth_data structure with some useful info */
+ ss->ifnum = ifnum;
+ ss->pusb_dev = dev;
+ ss->subclass = iface_desc->bInterfaceSubClass;
+ ss->protocol = iface_desc->bInterfaceProtocol;
+
+ /*
+ * We are expecting a minimum of 3 endpoints - in, out (bulk), and int.
+ * We will ignore any others.
+ */
+ for (i = 0; i < iface_desc->bNumEndpoints; i++) {
+ /* is it an BULK endpoint? */
+ if ((iface->ep_desc[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+ if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
+ ss->ep_in =
+ iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ else
+ ss->ep_out =
+ iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+
+ /* is it an interrupt endpoint? */
+ if ((iface->ep_desc[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+ ss->ep_int = iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ ss->irqinterval = iface->ep_desc[i].bInterval;
+ }
+ }
+ debug("Endpoints In %d Out %d Int %d\n",
+ ss->ep_in, ss->ep_out, ss->ep_int);
+
+ /* Do some basic sanity checks, and bail if we find a problem */
+ if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
+ !ss->ep_in || !ss->ep_out || !ss->ep_int) {
+ debug("Problems with device\n");
+ return 0;
+ }
+ dev->privptr = (void *)ss;
+ return 1;
+}
+
+int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
+ struct eth_device *eth)
+{
+ debug("** %s()\n", __func__);
+ if (!eth) {
+ debug("%s: missing parameter.\n", __func__);
+ return 0;
+ }
+ sprintf(eth->name, "%s%d", SMSC95XX_BASE_NAME, curr_eth_dev++);
+ eth->init = smsc95xx_init;
+ eth->send = smsc95xx_send;
+ eth->recv = smsc95xx_recv;
+ eth->halt = smsc95xx_halt;
+ eth->priv = ss;
+ return 1;
+}
diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c
index 68a0883..7b55da3 100644
--- a/drivers/usb/eth/usb_ether.c
+++ b/drivers/usb/eth/usb_ether.c
@@ -45,6 +45,13 @@ static const struct usb_eth_prob_dev prob_dev[] = {
.get_info = asix_eth_get_info,
},
#endif
+#ifdef CONFIG_USB_ETHER_SMSC95XX
+ {
+ .before_probe = smsc95xx_eth_before_probe,
+ .probe = smsc95xx_eth_probe,
+ .get_info = smsc95xx_eth_get_info,
+ },
+#endif
{ }, /* END */
};
diff --git a/include/usb_ether.h b/include/usb_ether.h
index 825c275..a7fb26b 100644
--- a/include/usb_ether.h
+++ b/include/usb_ether.h
@@ -51,6 +51,11 @@ struct ueth_data {
unsigned char irqinterval; /* Intervall for IRQ Pipe */
/* private fields for each driver can go here if needed */
+#ifdef CONFIG_USB_ETHER_SMSC95XX
+ size_t rx_urb_size; /* maximum USB URB size */
+ u32 mac_cr; /* MAC control register value */
+ int have_hwaddr; /* 1 if we have a hardware MAC address */
+#endif
};
/*
@@ -65,4 +70,12 @@ int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
struct eth_device *eth);
#endif
+#ifdef CONFIG_USB_ETHER_SMSC95XX
+void smsc95xx_eth_before_probe(void);
+int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
+ struct ueth_data *ss);
+int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
+ struct eth_device *eth);
+#endif
+
#endif /* __USB_ETHER_H__ */
--
1.7.3.1
4
14

[U-Boot] [PATCH 2/2][v3] powerpc: make espi can read more than 0xFFFA bytes
by Shaohui Xie 30 Apr '11
by Shaohui Xie 30 Apr '11
30 Apr '11
espi flash read returns invalid data if the read length is more than 0xFFFA
bytes, it supports maximum transaction of 2^16 bytes at a time, resister
spcom[TRANLEN] is 16 bits. If the transaction length is greater than 0xFFFF,
it need to be split into multiple transactions.
Signed-off-by: Shaohui Xie <b21989(a)freescale.com>
Cc: Mike Frysinger <vapier(a)gentoo.org>
---
changes for v2:
fix some compile warnings.
remove ifdef and use if else instead.
changes for v3:
rebased on first patch.
drivers/mtd/spi/spi_flash.c | 39 +++++++++++++++++++++++++++++++++++----
drivers/spi/fsl_espi.c | 6 ++++++
include/spi.h | 2 ++
3 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index c75b716..f90ef25 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -1,6 +1,7 @@
/*
* SPI flash interface
*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc.
* Copyright (C) 2008 Atmel Corporation
* Copyright (C) 2010 Reinhard Meyer, EMK Elektronik
*
@@ -82,11 +83,41 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
{
u8 cmd[5];
- cmd[0] = CMD_READ_ARRAY_FAST;
- spi_flash_addr(offset, cmd);
- cmd[4] = 0x00;
+ if (len <= flash->spi->max_transfer_length) {
+ cmd[0] = CMD_READ_ARRAY_FAST;
+ spi_flash_addr(offset, cmd);
+ cmd[4] = 0x00;
+
+ return spi_flash_read_common(flash, cmd, sizeof(cmd), data, len);
+ } else {
+ int max_tran_len, num_chunks, tran_len, ret = 0;
+
+ max_tran_len = flash->spi->max_transfer_length;
+ num_chunks = len / max_tran_len + (len % max_tran_len ? 1 : 0);
+
+ while (num_chunks--) {
+ tran_len = min(len , max_tran_len);
+
+ cmd[0] = CMD_READ_ARRAY_FAST;
+ spi_flash_addr(offset, cmd);
+ cmd[4] = 0x00;
+
+ debug("READ: 0x%x => cmd = "
+ "{ 0x%02x 0x%02x%02x%02x%02x } tran_len = 0x%x\n",
+ offset, cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], tran_len);
- return spi_flash_read_common(flash, cmd, sizeof(cmd), data, len);
+ ret = spi_flash_read_common(
+ flash, cmd, sizeof(cmd), data, tran_len);
+ if (ret < 0)
+ return ret;
+
+ offset += max_tran_len;
+ data += max_tran_len;
+ len -= max_tran_len;
+ }
+
+ return ret;
+ }
}
int spi_flash_cmd_poll_bit(struct spi_flash *flash, unsigned long timeout,
diff --git a/drivers/spi/fsl_espi.c b/drivers/spi/fsl_espi.c
index 55b29cc..5ed6a59 100644
--- a/drivers/spi/fsl_espi.c
+++ b/drivers/spi/fsl_espi.c
@@ -53,6 +53,8 @@
ESPI_CSMODE_CSBEF(0) | ESPI_CSMODE_CSAFT(0) | \
ESPI_CSMODE_CSCG(1))
+#define ESPI_MAX_DATA_TRANSFER_LEN 0xFFF0
+
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
@@ -71,6 +73,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
slave->bus = bus;
slave->cs = cs;
slave->slave_data.mode = mode;
+ slave->max_transfer_length = ESPI_MAX_DATA_TRANSFER_LEN;
/* Set eSPI BRG clock source */
get_sys_info(&sysinfo);
@@ -174,6 +177,9 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out,
u8 *cmd_buf = slave->slave_data.cmd_buf;
size_t cmd_len = slave->slave_data.cmd_len;
+ if (slave->max_transfer_length > ESPI_MAX_DATA_TRANSFER_LEN)
+ return -1;
+
switch (flags) {
case SPI_XFER_BEGIN:
cmd_len = slave->slave_data.cmd_len = bitlen / 8;
diff --git a/include/spi.h b/include/spi.h
index 213f6e6..c2c0d36 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -70,11 +70,13 @@ struct spi_slave_data {
* bus: ID of the bus that the slave is attached to.
* cs: ID of the chip select connected to the slave.
* slave_data: slave data for hardware and transfer.
+ * max_transfer_length: maximum data transfer length supported by the slave.
*/
struct spi_slave {
unsigned int bus;
unsigned int cs;
struct spi_slave_data slave_data;
+ unsigned int max_transfer_length;
};
/*-----------------------------------------------------------------------
--
1.6.4
3
2