Message ID | 1320175491-15124-1-git-send-email-fermata7@gmail.com |
---|---|
State | Superseded |
Headers | show |
Hi Jana, On 11/01/11 21:24, Jana Rapava wrote: > This commit adds USB support for EfikaMX and EfikaSB. > > Signed-off-by: Jana Rapava <fermata7@gmail.com> > Signed-off-by: Marek Vasut <marek.vasut@gmail.com> > Cc: Remy Bohmer <linux@bohmer.net> > Cc: Stefano Babic <sbabic@denx.de> > > Acked-by: Marek Vasut <marek.vasut@gmail.com> > --- > Changes for v2: > - introduce temporary variable in ulpi_write > - whitespace changes > Changes for v3: > - add protection against multiple inclusion of efika.h > Changes for v4: > - rename multiple inclusion protection macro in efika.h > Changes for v5: > - fix unterminated #ifndef in efika.h > Changes for v6: > - add Acked-by > - no changes > Changes for v7: > - use ULPI-related definitions from ./include/usb/ulpi.h This means that the patch here [1] is a prerequisite, right? It is worth stating. [1] http://patchwork.ozlabs.org/patch/123119/ > > board/efikamx/Makefile | 3 + > board/efikamx/efika.h | 35 ++++ > board/efikamx/efikamx-usb.c | 424 +++++++++++++++++++++++++++++++++++++++++++ > board/efikamx/efikamx.c | 3 + > include/configs/efikamx.h | 16 ++ > 5 files changed, 481 insertions(+), 0 deletions(-) > create mode 100644 board/efikamx/efika.h > create mode 100644 board/efikamx/efikamx-usb.c > > diff --git a/board/efikamx/Makefile b/board/efikamx/Makefile > index ee4a16e..860e4d2 100644 > --- a/board/efikamx/Makefile > +++ b/board/efikamx/Makefile > @@ -28,6 +28,9 @@ include $(TOPDIR)/config.mk > LIB = $(obj)lib$(BOARD).o > > COBJS := efikamx.o > +ifdef CONFIG_CMD_USB > +COBJS += efikamx-usb.o > +endif > > SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) > OBJS := $(addprefix $(obj),$(COBJS)) > diff --git a/board/efikamx/efika.h b/board/efikamx/efika.h > new file mode 100644 > index 0000000..fec9ee0 > --- /dev/null > +++ b/board/efikamx/efika.h > @@ -0,0 +1,35 @@ > +/* > + * Copyright (C) 2011 Jana Rapava <fermata7@gmail.com> > + * > + * 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 __BOARD_EFIKAMX_EFIKA_H__ > +#define __BOARD_EFIKAMX_EFIKA_H__ > + > +/* > + * EHCI USB > + */ > +#ifdef CONFIG_CMD_USB > +void setup_iomux_usb(void); > +#else > +static inline void setup_iomux_usb(void) { } > +#endif > + > +#endif /* __BOARD_EFIKAMX_EFIKA_H__ */ > diff --git a/board/efikamx/efikamx-usb.c b/board/efikamx/efikamx-usb.c > new file mode 100644 > index 0000000..3b42256 > --- /dev/null > +++ b/board/efikamx/efikamx-usb.c > @@ -0,0 +1,424 @@ > +/* > + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> > + * Copyright (C) 2011 Jana Rapava <fermata7@gmail.com> > + * > + * 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 <asm/io.h> > +#include <asm/arch/imx-regs.h> > +#include <asm/arch/mx5x_pins.h> > +#include <asm/arch/iomux.h> > +#include <asm/gpio.h> > +#include <usb/ehci-fsl.h> > +#include <errno.h> > +#include <watchdog.h> > + > +#include <usb/ehci.h> > +#include <usb/ehci-core.h> > +#include <usb/ulpi.h> > + > +/* > + * Configure the USB H1 and USB H2 IOMUX. > + */ > +#define USB_PAD_CONFIG (PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \ > + PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | \ > + PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL) > +void setup_iomux_usb(void) > +{ > + /* > + * Configure USBH1 pads > + */ > + mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_USBH1_CLK, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_CLK, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_USBH1_DIR, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_DIR, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_USBH1_NXT, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_NXT, USB_PAD_CONFIG); > + > + mxc_request_iomux(MX51_PIN_USBH1_DATA0, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA0, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_USBH1_DATA1, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA1, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_USBH1_DATA2, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA2, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_USBH1_DATA3, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA3, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_USBH1_DATA4, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA4, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_USBH1_DATA5, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA5, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_USBH1_DATA6, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA6, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_USBH1_DATA7, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA7, USB_PAD_CONFIG); > + > + /* > + * Configure USBH1 control pads > + */ > + > + /* USB PHY reset */ > + mxc_request_iomux(MX51_PIN_EIM_D27, IOMUX_CONFIG_ALT1); > + mxc_iomux_set_pad(MX51_PIN_EIM_D27, PAD_CTL_PKE_ENABLE | > + PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH); > + > + /* USB HUB reset */ > + mxc_request_iomux(MX51_PIN_GPIO1_5, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_GPIO1_5, PAD_CTL_PKE_ENABLE | > + PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH); > + > + > + if (machine_is_efikasb()) { > + /* > + * Configure USBH2 pads (used on EfikaSB) > + */ > + /* USBH2_DATA */ > + mxc_request_iomux(MX51_PIN_EIM_D16, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_D16, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_D17, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_EIM_D18, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_D18, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_EIM_D19, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_D19, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_EIM_D20, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_D20, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_D21, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_EIM_D22, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_D22, USB_PAD_CONFIG); > + mxc_request_iomux(MX51_PIN_EIM_D23, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_D23, USB_PAD_CONFIG); > + > + /* USBH2_CLK */ > + mxc_request_iomux(MX51_PIN_EIM_A24, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_A24, USB_PAD_CONFIG); > + /* USBH2_DIR */ > + mxc_request_iomux(MX51_PIN_EIM_A25, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_A25, USB_PAD_CONFIG); > + /* USBH2_STP */ > + mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_A26, USB_PAD_CONFIG); > + /* USBH2_NXT */ > + mxc_request_iomux(MX51_PIN_EIM_A27, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_A27, USB_PAD_CONFIG); > + } > + > + /* WIFI EN (act low) */ > + mxc_request_iomux(MX51_PIN_EIM_A22, IOMUX_CONFIG_GPIO); > + mxc_iomux_set_pad(MX51_PIN_EIM_A22, 0); > + /* WIFI RESET */ > + mxc_request_iomux(MX51_PIN_EIM_A16, IOMUX_CONFIG_GPIO); > + mxc_iomux_set_pad(MX51_PIN_EIM_A16, 0); > + /* BT EN (act low) */ > + mxc_request_iomux(MX51_PIN_EIM_A17, IOMUX_CONFIG_GPIO); > + mxc_iomux_set_pad(MX51_PIN_EIM_A17, 0); > +} > + > +/* > + * Enable devices connected to USB buses. > + */ > +void efika_usb_enable_devices(void) > +{ > + /* Enable Bluetooth */ > + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A17), 0); > + udelay(10000); > + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A17), 1); > + > + /* Enable WiFi */ > + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A22), 1); > + udelay(10000); > + > + /* Reset the WiFi chip */ > + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A16), 0); > + udelay(10000); > + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A16), 1); > +} > + > +/* > + * Reset USB HUB (or HUBs on EfikaSB). > + */ > +void efika_usb_hub_reset(void) > +{ > + /* HUB reset */ > + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 1); > + udelay(1000); > + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 0); > + udelay(1000); > + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 1); > +} > + > +/* > + * Reset USB PHY (or PHYs on EfikaSB). > + */ > +void efika_usb_phy_reset(void) > +{ > + /* SMSC 3317 PHY reset */ > + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D27), 0); > + udelay(1000); > + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D27), 1); > +} > + > +/* > + * Configure control registers of the USB controller. > + */ > +void control_regs_setup(struct mx5_usb_control_regs *control) > +{ > + clrsetbits_le32(&control->usbctrl, > + (MXC_OTG_WUE | MXC_OTG_PM | MX51_H1_ULPI_IE | MX51_H1_WUE), > + MX51_H1_PM); > + > + clrsetbits_le32(&control->phyctrl0, > + MX51_OTG_OVERCURD, > + MX51_EHCI_POWERPINSE); > + > + clrsetbits_le32(&control->phyctrl1, > + MX51_SYSCLOCK_MASK, > + MX51_SYSCLOCK_24_MHZ); > + > + setbits_le32(&control->usbctrl1, MX51_H1_EXTCLKE); > + > + clrsetbits_le32(&control->uh2ctrl, > + (MX51_H2_ULPI_IE | MX51_H2_WUE), > + MX51_H2_PM); > + > + udelay(10000); > +} > + > +#define ULPI_ADDR_SHIFT 16 > +#define ulpi_write_mask(value) ((value) & 0xff) > +#define ulpi_read_mask(value) (((value) >> 8) & 0xff) > + > +int ulpi_wait(struct usb_ehci *ehci, u32 ulpi_value, u32 ulpi_mask) > +{ > + int timeout = ULPI_TIMEOUT; > + u32 tmp; > + > + writel(ulpi_value, &ehci->ulpi_viewpoint); > + > + /* Wait for the bits in ulpi_mask to become zero. */ > + while (--timeout) { > + tmp = readl(&ehci->ulpi_viewpoint); > + if (!(tmp & ulpi_mask)) > + break; > + WATCHDOG_RESET(); > + } > + > + return !timeout; > +} > + > +int ulpi_wakeup(struct usb_ehci *ehci) > +{ > + if (readl(&ehci->ulpi_viewpoint) & ULPI_SS) > + return 0; /* already awake */ > + return ulpi_wait(ehci, ULPI_WU, ULPI_WU); > +} > + > +void ulpi_write(struct usb_ehci *ehci, u32 reg, u32 value) > +{ > + u32 tmp; > + if (ulpi_wakeup(ehci)) { > + printf("ULPI wakeup timed out\n"); > + return; > + } > + > + tmp = ulpi_wait(ehci, ULPI_RWRUN | ULPI_RWCTRL | > + reg << ULPI_ADDR_SHIFT | ulpi_write_mask(value), ULPI_RWRUN); > + if (tmp) > + printf("ULPI write timed out\n"); > +} > + > +u32 ulpi_read(struct usb_ehci *ehci, u32 reg) > +{ > + if (ulpi_wakeup(ehci)) { > + printf("ULPI wakeup timed out\n"); > + return 0; > + } > + > + if (ulpi_wait(ehci, ULPI_RWRUN | reg << ULPI_ADDR_SHIFT, ULPI_RWRUN)) { > + printf("ULPI read timed out\n"); > + return 0; > + } > + > + return ulpi_read_mask(readl(&ehci->ulpi_viewpoint)); > +} > + > +void ulpi_init(struct usb_ehci *ehci, struct ulpi_regs *ulpi) > +{ > + u32 tmp = 0; > + int reg, i; > + > + /* Assemble ID from four ULPI ID registers (8 bits each). */ > + for (reg = ULPI_ID_REGS_COUNT - 1; reg >= 0; reg--) > + tmp |= ulpi_read(ehci, reg) << (reg * 8); > + > + /* Split ID into vendor and product ID. */ > + debug("Found ULPI TX, ID %04x:%04x\n", tmp >> 16, tmp & 0xffff); > + > + /* ULPI integrity check */ > + for (i = 0; i < 2; i++) { > + ulpi_write(ehci, (u32)&ulpi->scratch_write, > + ULPI_TEST_VALUE << i); > + tmp = ulpi_read(ehci, (u32)&ulpi->scratch_write); > + > + if (tmp != (ULPI_TEST_VALUE << i)) { > + printf("ULPI integrity check failed\n"); > + return; > + } > + } > + > + /* Set ULPI flags. */ > + ulpi_write(ehci, (u32)&ulpi->otg_ctrl_write, > + ULPI_OTG_EXTVBUSIND | > + ULPI_OTG_DM_PULLDOWN | ULPI_OTG_DP_PULLDOWN); > + ulpi_write(ehci, (u32)&ulpi->function_ctrl_write, > + ULPI_FC_XCVRSEL | ULPI_FC_OPMODE_NORMAL | > + ULPI_FC_SUSPENDM); > + ulpi_write(ehci, (u32)&ulpi->iface_ctrl_write, 0); > + ulpi_write(ehci, (u32)&ulpi->otg_ctrl_set, > + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); > + > + /* > + * NOTE: This violates USB specification, but otherwise, USB on Efika > + * doesn't charge VBUS and as a result, USB doesn't work. > + */ > + ulpi_write(ehci, (u32)&ulpi->otg_ctrl_set, ULPI_OTG_CHRGVBUS); > +} > + > +/* > + * Solve the VBUS reset problem on Efika > + * by setting the CHRG_VBUS bit in the reset. > + */ > +void ehci_fixup(uint32_t *status_reg, uint32_t *reg_ref) > +{ > + struct usb_ehci *ehci = (struct usb_ehci *)(OTG_BASE_ADDR + > + MX51_REGISTER_LAYOUT_LENGTH); > + struct ulpi_regs *ulpi = (struct ulpi_regs *)0; > + > + u32 tmp = ulpi_read(ehci, (u32)&ulpi->otg_ctrl_write); > + tmp |= ULPI_OTG_CHRGVBUS; > + ulpi_write(ehci, (u32)&ulpi->otg_ctrl_write, tmp); > + > + /* USB 2.0 specification say 50 ms resets on root. */ > + wait_ms(50); > + > + /* Now terminate the reset. */ > + *reg_ref = ehci_readl(status_reg); > + *reg_ref |= EHCI_PS_PE; > +} > + > +void ehci0_init(struct usb_ehci *ehci) > +{ > + writel(MX51_16BIT_UTMI, &ehci->portsc); > +} > + > +void ehci1_init(struct usb_ehci *ehci, struct ulpi_regs *ulpi) > +{ > + mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_USBH1_STP, PAD_CTL_DRV_HIGH | > + PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST); > + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_USBH1_STP), 0); > + udelay(1000); > + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_USBH1_STP), 1); > + udelay(1000); > + > + mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0); > + mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USB_PAD_CONFIG); > + udelay(10000); > + > + clrbits_le32(&ehci->usbcmd, MX51_ITC_IMMEDIATE_MASK); > + udelay(10000); > + > + writel(MX51_ULPI_MODE_MASK, &ehci->portsc); > + udelay(10000); > + > + ulpi_init(ehci, ulpi); > +} > + > +void ehci2_init(struct usb_ehci *ehci, struct ulpi_regs *ulpi) > +{ > + mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT1); > + mxc_iomux_set_pad(MX51_PIN_EIM_A26, PAD_CTL_DRV_HIGH | > + PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST); > + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A26), 0); > + udelay(1000); > + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A26), 1); > + udelay(1000); > + > + mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT2); > + mxc_iomux_set_pad(MX51_PIN_EIM_A26, USB_PAD_CONFIG); > + > + writel(MX51_ULPI_MODE_MASK, &ehci->portsc); > + udelay(10000); > + > + ulpi_init(ehci, ulpi); > +} > + > +int ehci_hcd_init(void) > +{ > + struct usb_ehci *ehci; > + struct mx5_usb_control_regs *mx5_usb_control_regs; > + struct ulpi_regs *ulpi; > + > + mx5_usb_control_regs = (struct mx5_usb_control_regs *)(OTG_BASE_ADDR + > + MX5_CTRL_REGS_OFFSET); > + control_regs_setup(mx5_usb_control_regs); > + ulpi = (struct ulpi_regs *)0; > + > + /* Init EHCI core */ > + ehci = (struct usb_ehci *)(OTG_BASE_ADDR + > + (MX51_REGISTER_LAYOUT_LENGTH * CONFIG_MXC_USB_PORT)); > + hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); > + hcor = (struct ehci_hcor *)((uint32_t) hccr + > + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); > + setbits_le32(&ehci->usbmode, CM_HOST); > + setbits_le32(&ehci->control, USB_EN); > + > + /* Init iMX51 EHCI */ > + efika_usb_phy_reset(); > + efika_usb_hub_reset(); > + efika_usb_enable_devices(); > + > + switch (CONFIG_MXC_USB_PORT) { > + case 0: > + ehci0_init(ehci); > + break; > + case 1: > + ehci1_init(ehci, ulpi); > + break; > + case 2: > + if (machine_is_efikasb()) > + ehci2_init(ehci, ulpi); > + break; > + }; > + > + /* EfikaMX USB has issues ... */ > + udelay(10000); > + > + return 0; > +} > + > +int ehci_hcd_stop(void) > +{ > + return 0; > +} Most of the functions and defines above aren't board specific, right? It would be a good thing to put them in an SoC specific location, so any other board using that SoC can benefit from them. I don't want to block on that issue, so if it simplifies you a further ULPI patches submission - I don't care if you leave it here and in the next patch set move to a common location. Although the proper way would be to submit the framework(s) and then adjust the users. [...]
2011/11/2 Igor Grinberg <grinberg@compulab.co.il> > Hi Jana, > > On 11/01/11 21:24, Jana Rapava wrote: > > > diff --git a/board/efikamx/efika.h b/board/efikamx/efika.h > > new file mode 100644 > > index 0000000..fec9ee0 > > --- /dev/null > > +++ b/board/efikamx/efika.h > > > > diff --git a/board/efikamx/efikamx-usb.c b/board/efikamx/efikamx-usb.c > > new file mode 100644 > > index 0000000..3b42256 > > --- /dev/null > > +++ b/board/efikamx/efikamx-usb.c > > > > Most of the functions and defines above aren't board specific, right? > It would be a good thing to put them in an SoC specific location, > so any other board using that SoC can benefit from them. > Ok, so non-board-specific parts should go into ./arch/arm/cpu/armv7/mx5/ and board-specific callbacks should stay here? Do I understand you well? > > I don't want to block on that issue, so if it simplifies you a > further ULPI patches submission - I don't care if you leave it here > and in the next patch set move to a common location. > > Thanks. > Although the proper way would be to submit the framework(s) and then > adjust the users. > > Ok, I'll do it that way in the future patchsets.
On 11/02/2011 05:03 PM, Jana Rapava wrote: > > Ok, so non-board-specific parts should go into ./arch/arm/cpu/armv7/mx5/ > and board-specific callbacks should stay here? Do I understand you well? Yes, I think so > >> >> I don't want to block on that issue, so if it simplifies you a >> further ULPI patches submission - I don't care if you leave it here >> and in the next patch set move to a common location. >> >> > Thanks. Anyway, as we agree, it is really better to do now the right thing as to postpone. Best regards, Stefano Babic
diff --git a/board/efikamx/Makefile b/board/efikamx/Makefile index ee4a16e..860e4d2 100644 --- a/board/efikamx/Makefile +++ b/board/efikamx/Makefile @@ -28,6 +28,9 @@ include $(TOPDIR)/config.mk LIB = $(obj)lib$(BOARD).o COBJS := efikamx.o +ifdef CONFIG_CMD_USB +COBJS += efikamx-usb.o +endif SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/board/efikamx/efika.h b/board/efikamx/efika.h new file mode 100644 index 0000000..fec9ee0 --- /dev/null +++ b/board/efikamx/efika.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 Jana Rapava <fermata7@gmail.com> + * + * 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 __BOARD_EFIKAMX_EFIKA_H__ +#define __BOARD_EFIKAMX_EFIKA_H__ + +/* + * EHCI USB + */ +#ifdef CONFIG_CMD_USB +void setup_iomux_usb(void); +#else +static inline void setup_iomux_usb(void) { } +#endif + +#endif /* __BOARD_EFIKAMX_EFIKA_H__ */ diff --git a/board/efikamx/efikamx-usb.c b/board/efikamx/efikamx-usb.c new file mode 100644 index 0000000..3b42256 --- /dev/null +++ b/board/efikamx/efikamx-usb.c @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * Copyright (C) 2011 Jana Rapava <fermata7@gmail.com> + * + * 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 <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/mx5x_pins.h> +#include <asm/arch/iomux.h> +#include <asm/gpio.h> +#include <usb/ehci-fsl.h> +#include <errno.h> +#include <watchdog.h> + +#include <usb/ehci.h> +#include <usb/ehci-core.h> +#include <usb/ulpi.h> + +/* + * Configure the USB H1 and USB H2 IOMUX. + */ +#define USB_PAD_CONFIG (PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \ + PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | \ + PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL) +void setup_iomux_usb(void) +{ + /* + * Configure USBH1 pads + */ + mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_CLK, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_CLK, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DIR, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DIR, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_NXT, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_NXT, USB_PAD_CONFIG); + + mxc_request_iomux(MX51_PIN_USBH1_DATA0, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA0, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA1, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA1, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA2, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA2, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA3, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA3, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA4, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA4, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA5, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA5, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA6, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA6, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_USBH1_DATA7, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_DATA7, USB_PAD_CONFIG); + + /* + * Configure USBH1 control pads + */ + + /* USB PHY reset */ + mxc_request_iomux(MX51_PIN_EIM_D27, IOMUX_CONFIG_ALT1); + mxc_iomux_set_pad(MX51_PIN_EIM_D27, PAD_CTL_PKE_ENABLE | + PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH); + + /* USB HUB reset */ + mxc_request_iomux(MX51_PIN_GPIO1_5, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_GPIO1_5, PAD_CTL_PKE_ENABLE | + PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH); + + + if (machine_is_efikasb()) { + /* + * Configure USBH2 pads (used on EfikaSB) + */ + /* USBH2_DATA */ + mxc_request_iomux(MX51_PIN_EIM_D16, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D16, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D17, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D18, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D18, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D19, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D19, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D20, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D20, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D21, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D22, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D22, USB_PAD_CONFIG); + mxc_request_iomux(MX51_PIN_EIM_D23, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_D23, USB_PAD_CONFIG); + + /* USBH2_CLK */ + mxc_request_iomux(MX51_PIN_EIM_A24, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_A24, USB_PAD_CONFIG); + /* USBH2_DIR */ + mxc_request_iomux(MX51_PIN_EIM_A25, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_A25, USB_PAD_CONFIG); + /* USBH2_STP */ + mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_A26, USB_PAD_CONFIG); + /* USBH2_NXT */ + mxc_request_iomux(MX51_PIN_EIM_A27, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_A27, USB_PAD_CONFIG); + } + + /* WIFI EN (act low) */ + mxc_request_iomux(MX51_PIN_EIM_A22, IOMUX_CONFIG_GPIO); + mxc_iomux_set_pad(MX51_PIN_EIM_A22, 0); + /* WIFI RESET */ + mxc_request_iomux(MX51_PIN_EIM_A16, IOMUX_CONFIG_GPIO); + mxc_iomux_set_pad(MX51_PIN_EIM_A16, 0); + /* BT EN (act low) */ + mxc_request_iomux(MX51_PIN_EIM_A17, IOMUX_CONFIG_GPIO); + mxc_iomux_set_pad(MX51_PIN_EIM_A17, 0); +} + +/* + * Enable devices connected to USB buses. + */ +void efika_usb_enable_devices(void) +{ + /* Enable Bluetooth */ + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A17), 0); + udelay(10000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A17), 1); + + /* Enable WiFi */ + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A22), 1); + udelay(10000); + + /* Reset the WiFi chip */ + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A16), 0); + udelay(10000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A16), 1); +} + +/* + * Reset USB HUB (or HUBs on EfikaSB). + */ +void efika_usb_hub_reset(void) +{ + /* HUB reset */ + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 1); + udelay(1000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 0); + udelay(1000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 1); +} + +/* + * Reset USB PHY (or PHYs on EfikaSB). + */ +void efika_usb_phy_reset(void) +{ + /* SMSC 3317 PHY reset */ + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D27), 0); + udelay(1000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D27), 1); +} + +/* + * Configure control registers of the USB controller. + */ +void control_regs_setup(struct mx5_usb_control_regs *control) +{ + clrsetbits_le32(&control->usbctrl, + (MXC_OTG_WUE | MXC_OTG_PM | MX51_H1_ULPI_IE | MX51_H1_WUE), + MX51_H1_PM); + + clrsetbits_le32(&control->phyctrl0, + MX51_OTG_OVERCURD, + MX51_EHCI_POWERPINSE); + + clrsetbits_le32(&control->phyctrl1, + MX51_SYSCLOCK_MASK, + MX51_SYSCLOCK_24_MHZ); + + setbits_le32(&control->usbctrl1, MX51_H1_EXTCLKE); + + clrsetbits_le32(&control->uh2ctrl, + (MX51_H2_ULPI_IE | MX51_H2_WUE), + MX51_H2_PM); + + udelay(10000); +} + +#define ULPI_ADDR_SHIFT 16 +#define ulpi_write_mask(value) ((value) & 0xff) +#define ulpi_read_mask(value) (((value) >> 8) & 0xff) + +int ulpi_wait(struct usb_ehci *ehci, u32 ulpi_value, u32 ulpi_mask) +{ + int timeout = ULPI_TIMEOUT; + u32 tmp; + + writel(ulpi_value, &ehci->ulpi_viewpoint); + + /* Wait for the bits in ulpi_mask to become zero. */ + while (--timeout) { + tmp = readl(&ehci->ulpi_viewpoint); + if (!(tmp & ulpi_mask)) + break; + WATCHDOG_RESET(); + } + + return !timeout; +} + +int ulpi_wakeup(struct usb_ehci *ehci) +{ + if (readl(&ehci->ulpi_viewpoint) & ULPI_SS) + return 0; /* already awake */ + return ulpi_wait(ehci, ULPI_WU, ULPI_WU); +} + +void ulpi_write(struct usb_ehci *ehci, u32 reg, u32 value) +{ + u32 tmp; + if (ulpi_wakeup(ehci)) { + printf("ULPI wakeup timed out\n"); + return; + } + + tmp = ulpi_wait(ehci, ULPI_RWRUN | ULPI_RWCTRL | + reg << ULPI_ADDR_SHIFT | ulpi_write_mask(value), ULPI_RWRUN); + if (tmp) + printf("ULPI write timed out\n"); +} + +u32 ulpi_read(struct usb_ehci *ehci, u32 reg) +{ + if (ulpi_wakeup(ehci)) { + printf("ULPI wakeup timed out\n"); + return 0; + } + + if (ulpi_wait(ehci, ULPI_RWRUN | reg << ULPI_ADDR_SHIFT, ULPI_RWRUN)) { + printf("ULPI read timed out\n"); + return 0; + } + + return ulpi_read_mask(readl(&ehci->ulpi_viewpoint)); +} + +void ulpi_init(struct usb_ehci *ehci, struct ulpi_regs *ulpi) +{ + u32 tmp = 0; + int reg, i; + + /* Assemble ID from four ULPI ID registers (8 bits each). */ + for (reg = ULPI_ID_REGS_COUNT - 1; reg >= 0; reg--) + tmp |= ulpi_read(ehci, reg) << (reg * 8); + + /* Split ID into vendor and product ID. */ + debug("Found ULPI TX, ID %04x:%04x\n", tmp >> 16, tmp & 0xffff); + + /* ULPI integrity check */ + for (i = 0; i < 2; i++) { + ulpi_write(ehci, (u32)&ulpi->scratch_write, + ULPI_TEST_VALUE << i); + tmp = ulpi_read(ehci, (u32)&ulpi->scratch_write); + + if (tmp != (ULPI_TEST_VALUE << i)) { + printf("ULPI integrity check failed\n"); + return; + } + } + + /* Set ULPI flags. */ + ulpi_write(ehci, (u32)&ulpi->otg_ctrl_write, + ULPI_OTG_EXTVBUSIND | + ULPI_OTG_DM_PULLDOWN | ULPI_OTG_DP_PULLDOWN); + ulpi_write(ehci, (u32)&ulpi->function_ctrl_write, + ULPI_FC_XCVRSEL | ULPI_FC_OPMODE_NORMAL | + ULPI_FC_SUSPENDM); + ulpi_write(ehci, (u32)&ulpi->iface_ctrl_write, 0); + ulpi_write(ehci, (u32)&ulpi->otg_ctrl_set, + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); + + /* + * NOTE: This violates USB specification, but otherwise, USB on Efika + * doesn't charge VBUS and as a result, USB doesn't work. + */ + ulpi_write(ehci, (u32)&ulpi->otg_ctrl_set, ULPI_OTG_CHRGVBUS); +} + +/* + * Solve the VBUS reset problem on Efika + * by setting the CHRG_VBUS bit in the reset. + */ +void ehci_fixup(uint32_t *status_reg, uint32_t *reg_ref) +{ + struct usb_ehci *ehci = (struct usb_ehci *)(OTG_BASE_ADDR + + MX51_REGISTER_LAYOUT_LENGTH); + struct ulpi_regs *ulpi = (struct ulpi_regs *)0; + + u32 tmp = ulpi_read(ehci, (u32)&ulpi->otg_ctrl_write); + tmp |= ULPI_OTG_CHRGVBUS; + ulpi_write(ehci, (u32)&ulpi->otg_ctrl_write, tmp); + + /* USB 2.0 specification say 50 ms resets on root. */ + wait_ms(50); + + /* Now terminate the reset. */ + *reg_ref = ehci_readl(status_reg); + *reg_ref |= EHCI_PS_PE; +} + +void ehci0_init(struct usb_ehci *ehci) +{ + writel(MX51_16BIT_UTMI, &ehci->portsc); +} + +void ehci1_init(struct usb_ehci *ehci, struct ulpi_regs *ulpi) +{ + mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_USBH1_STP, PAD_CTL_DRV_HIGH | + PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST); + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_USBH1_STP), 0); + udelay(1000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_USBH1_STP), 1); + udelay(1000); + + mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0); + mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USB_PAD_CONFIG); + udelay(10000); + + clrbits_le32(&ehci->usbcmd, MX51_ITC_IMMEDIATE_MASK); + udelay(10000); + + writel(MX51_ULPI_MODE_MASK, &ehci->portsc); + udelay(10000); + + ulpi_init(ehci, ulpi); +} + +void ehci2_init(struct usb_ehci *ehci, struct ulpi_regs *ulpi) +{ + mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT1); + mxc_iomux_set_pad(MX51_PIN_EIM_A26, PAD_CTL_DRV_HIGH | + PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST); + gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A26), 0); + udelay(1000); + gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A26), 1); + udelay(1000); + + mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT2); + mxc_iomux_set_pad(MX51_PIN_EIM_A26, USB_PAD_CONFIG); + + writel(MX51_ULPI_MODE_MASK, &ehci->portsc); + udelay(10000); + + ulpi_init(ehci, ulpi); +} + +int ehci_hcd_init(void) +{ + struct usb_ehci *ehci; + struct mx5_usb_control_regs *mx5_usb_control_regs; + struct ulpi_regs *ulpi; + + mx5_usb_control_regs = (struct mx5_usb_control_regs *)(OTG_BASE_ADDR + + MX5_CTRL_REGS_OFFSET); + control_regs_setup(mx5_usb_control_regs); + ulpi = (struct ulpi_regs *)0; + + /* Init EHCI core */ + ehci = (struct usb_ehci *)(OTG_BASE_ADDR + + (MX51_REGISTER_LAYOUT_LENGTH * CONFIG_MXC_USB_PORT)); + hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + setbits_le32(&ehci->usbmode, CM_HOST); + setbits_le32(&ehci->control, USB_EN); + + /* Init iMX51 EHCI */ + efika_usb_phy_reset(); + efika_usb_hub_reset(); + efika_usb_enable_devices(); + + switch (CONFIG_MXC_USB_PORT) { + case 0: + ehci0_init(ehci); + break; + case 1: + ehci1_init(ehci, ulpi); + break; + case 2: + if (machine_is_efikasb()) + ehci2_init(ehci, ulpi); + break; + }; + + /* EfikaMX USB has issues ... */ + udelay(10000); + + return 0; +} + +int ehci_hcd_stop(void) +{ + return 0; +} diff --git a/board/efikamx/efikamx.c b/board/efikamx/efikamx.c index 0c4e24b..8e9b42b 100644 --- a/board/efikamx/efikamx.c +++ b/board/efikamx/efikamx.c @@ -37,6 +37,8 @@ #include <fsl_pmic.h> #include <mc13892.h> +#include "efika.h" + DECLARE_GLOBAL_DATA_PTR; /* @@ -678,6 +680,7 @@ int board_late_init(void) setup_iomux_led(); setup_iomux_ata(); + setup_iomux_usb(); efikamx_toggle_led(EFIKAMX_LED_BLUE); diff --git a/include/configs/efikamx.h b/include/configs/efikamx.h index 54f48e4..57f7be5 100644 --- a/include/configs/efikamx.h +++ b/include/configs/efikamx.h @@ -44,6 +44,10 @@ #define CONFIG_SYS_TEXT_BASE 0x97800000 +#define CONFIG_L2_OFF +#define CONFIG_SYS_ICACHE_OFF +#define CONFIG_SYS_DCACHE_OFF + /* * Bootloader Components Configuration */ @@ -172,6 +176,18 @@ #endif /* + * USB + */ +#define CONFIG_CMD_USB +#ifdef CONFIG_CMD_USB +#define CONFIG_USB_EHCI /* Enable EHCI USB support */ +#define CONFIG_MXC_USB_PORT 1 +#define CONFIG_EHCI_IS_TDI +#define CONFIG_USB_STORAGE +#define CONFIG_USB_KEYBOARD +#endif /* CONFIG_CMD_USB */ + +/* * Filesystems */ #ifdef CONFIG_CMD_FAT