From f4dd69ca8288dea2bb1e67f0341c235c3134ed4b Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Mon, 16 Nov 2015 16:49:23 +0530 Subject: usb: zynqmp: Add XHCI driver support Added USB XHCI driver support for zynqmp. Signed-off-by: Siva Durga Prasad Paladugu diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index f70f38c..645b990 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_USB_EHCI_ZYNQ) += ehci-zynq.o # xhci obj-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o obj-$(CONFIG_USB_XHCI_DWC3) += xhci-dwc3.o +obj-$(CONFIG_USB_XHCI_ZYNQMP) += xhci-zynqmp.o obj-$(CONFIG_USB_XHCI_KEYSTONE) += xhci-keystone.o obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o obj-$(CONFIG_USB_XHCI_FSL) += xhci-fsl.o diff --git a/drivers/usb/host/xhci-zynqmp.c b/drivers/usb/host/xhci-zynqmp.c new file mode 100644 index 0000000..e76a0c6 --- /dev/null +++ b/drivers/usb/host/xhci-zynqmp.c @@ -0,0 +1,143 @@ +/* + * Copyright 2015 Xilinx, Inc. + * + * Zynq USB HOST xHCI Controller + * + * Author: Siva Durga Prasad Paladugu + * + * This file was reused from Freescale USB xHCI + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include "xhci.h" + +/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR; + +/* Default to the ZYNQMP XHCI defines */ +#define USB3_PWRCTL_CLK_CMD_MASK 0x3FE000 +#define USB3_PWRCTL_CLK_FREQ_MASK 0xFFC +#define USB3_PHY_PARTIAL_RX_POWERON BIT(6) +#define USB3_PHY_RX_POWERON BIT(14) +#define USB3_PHY_TX_POWERON BIT(15) +#define USB3_PHY_TX_RX_POWERON (USB3_PHY_RX_POWERON | USB3_PHY_TX_POWERON) +#define USB3_PWRCTL_CLK_CMD_SHIFT 14 +#define USB3_PWRCTL_CLK_FREQ_SHIFT 22 + +/* USBOTGSS_WRAPPER definitions */ +#define USBOTGSS_WRAPRESET BIT(17) +#define USBOTGSS_DMADISABLE BIT(16) +#define USBOTGSS_STANDBYMODE_NO_STANDBY BIT(4) +#define USBOTGSS_STANDBYMODE_SMRT BIT(5) +#define USBOTGSS_STANDBYMODE_SMRT_WKUP (0x3 << 4) +#define USBOTGSS_IDLEMODE_NOIDLE BIT(2) +#define USBOTGSS_IDLEMODE_SMRT BIT(3) +#define USBOTGSS_IDLEMODE_SMRT_WKUP (0x3 << 2) + +/* USBOTGSS_IRQENABLE_SET_0 bit */ +#define USBOTGSS_COREIRQ_EN BIT(1) + +/* USBOTGSS_IRQENABLE_SET_1 bits */ +#define USBOTGSS_IRQ_SET_1_IDPULLUP_FALL_EN BIT(1) +#define USBOTGSS_IRQ_SET_1_DISCHRGVBUS_FALL_EN BIT(3) +#define USBOTGSS_IRQ_SET_1_CHRGVBUS_FALL_EN BIT(4) +#define USBOTGSS_IRQ_SET_1_DRVVBUS_FALL_EN BIT(5) +#define USBOTGSS_IRQ_SET_1_IDPULLUP_RISE_EN BIT(8) +#define USBOTGSS_IRQ_SET_1_DISCHRGVBUS_RISE_EN BIT(11) +#define USBOTGSS_IRQ_SET_1_CHRGVBUS_RISE_EN BIT(12) +#define USBOTGSS_IRQ_SET_1_DRVVBUS_RISE_EN BIT(13) +#define USBOTGSS_IRQ_SET_1_OEVT_EN BIT(16) +#define USBOTGSS_IRQ_SET_1_DMADISABLECLR_EN BIT(17) + +struct zynqmp_xhci { + struct xhci_hccr *hcd; + struct dwc3 *dwc3_reg; +}; + +static struct zynqmp_xhci zynqmp_xhci; + +unsigned long ctr_addr[] = CONFIG_ZYNQMP_XHCI_LIST; + +void usb_phy_reset(struct dwc3 *dwc3_reg) +{ + /* Assert USB3 PHY reset */ + setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); + + /* Assert USB2 PHY reset */ + setbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); + + udelay(10); + + /* Clear USB3 PHY reset */ + clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); + + /* Clear USB2 PHY reset */ + clrbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); +} + +static int zynqmp_xhci_core_init(struct zynqmp_xhci *zynqmp_xhci) +{ + int ret = 0; + + ret = dwc3_core_init(zynqmp_xhci->dwc3_reg); + if (ret) { + debug("%s:failed to initialize core\n", __func__); + return ret; + } + + /* We are hard-coding DWC3 core to Host Mode */ + dwc3_set_mode(zynqmp_xhci->dwc3_reg, DWC3_GCTL_PRTCAP_HOST); + + return ret; +} + +int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) +{ + struct zynqmp_xhci *ctx = &zynqmp_xhci; + int ret = 0; + uint32_t hclen; + + if (index < 0 || index >= ARRAY_SIZE(ctr_addr)) + return -EINVAL; + + ctx->hcd = (struct xhci_hccr *)ctr_addr[index]; + ctx->dwc3_reg = (struct dwc3 *)((void *)ctx->hcd + DWC3_REG_OFFSET); + + ret = board_usb_init(index, USB_INIT_HOST); + if (ret != 0) { + puts("Failed to initialize board for USB\n"); + return ret; + } + + ret = zynqmp_xhci_core_init(ctx); + if (ret < 0) { + puts("Failed to initialize xhci\n"); + return ret; + } + + *hccr = (struct xhci_hccr *)ctx->hcd; + hclen = HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)); + *hcor = (struct xhci_hcor *)((uintptr_t) *hccr + hclen); + + debug("zynqmp-xhci: init hccr %p and hcor %p hc_length %d\n", + *hccr, *hcor, hclen); + + return ret; +} + +void xhci_hcd_stop(int index) +{ + /* + * Currently zynqmp socs do not support PHY shutdown from + * sw. But this support may be added in future socs. + */ + + return 0; +} diff --git a/include/configs/xilinx_zynqmp_ep.h b/include/configs/xilinx_zynqmp_ep.h index 8bdb5c9..b7d506e 100644 --- a/include/configs/xilinx_zynqmp_ep.h +++ b/include/configs/xilinx_zynqmp_ep.h @@ -24,6 +24,8 @@ #define CONFIG_SYS_I2C_ZYNQ #define CONFIG_ZYNQ_EEPROM #define CONFIG_AHCI +#define CONFIG_ZYNQMP_XHCI_LIST {ZYNQMP_USB0_XHCI_BASEADDR, \ + ZYNQMP_USB1_XHCI_BASEADDR} #include -- cgit v0.10.2 From 0f676767b615f166d3fd9e4911e251e09c99f078 Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Mon, 16 Nov 2015 16:49:24 +0530 Subject: usb: zynqmp: Enable USB XHCI support Enable USB XHCI support for ZynqMP Signed-off-by: Siva Durga Prasad Paladugu diff --git a/include/configs/xilinx_zynqmp.h b/include/configs/xilinx_zynqmp.h index 6b8b9f8..474bbaf 100644 --- a/include/configs/xilinx_zynqmp.h +++ b/include/configs/xilinx_zynqmp.h @@ -107,6 +107,14 @@ #define CONFIG_SYS_LOAD_ADDR 0x8000000 #if defined(CONFIG_ZYNQMP_USB) +#define CONFIG_USB_XHCI_DWC3 +#define CONFIG_USB_XHCI +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#define CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS 2 +#define CONFIG_CMD_USB +#define CONFIG_USB_STORAGE +#define CONFIG_USB_XHCI_ZYNQMP + #define CONFIG_USB_DWC3 #define CONFIG_USB_DWC3_GADGET -- cgit v0.10.2 From 9dc522249a2b5aa59bcb2f022ce5844434ac81bc Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 1 Oct 2015 14:22:17 -0500 Subject: usb: host: xhci-omap: fix build break MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following build break: drivers/usb/host/xhci-omap.c:35:5: error: ‘board_usb_init’ aliased to external symbol ‘__board_usb_init’ int board_usb_init(int index, enum usb_init_type init) ^ Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/xhci-omap.c b/drivers/usb/host/xhci-omap.c index 104e7a7..fd19f79 100644 --- a/drivers/usb/host/xhci-omap.c +++ b/drivers/usb/host/xhci-omap.c @@ -27,7 +27,7 @@ DECLARE_GLOBAL_DATA_PTR; static struct omap_xhci omap; -inline int __board_usb_init(int index, enum usb_init_type init) +__weak int __board_usb_init(int index, enum usb_init_type init) { return 0; } -- cgit v0.10.2 From c2ad4e1b9f965d4ee4a5630216ba4123a77e1784 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 1 Oct 2015 14:22:18 -0500 Subject: usb: dwc3: fix build warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix the following build warnings: drivers/usb/dwc3/core.c: In function ‘dwc3_uboot_init’: drivers/usb/dwc3/core.c:625:6: warning: ‘dev’ is used uninitialized in this function [-Wuninitialized] mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); ^ drivers/usb/dwc3/dwc3-omap.c: In function ‘dwc3_omap_uboot_init’: drivers/usb/dwc3/dwc3-omap.c:380:7: warning: ‘dev’ is used uninitialized in this function [-Wuninitialized] omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 0ae3de5..85cc96a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -613,7 +613,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) int dwc3_uboot_init(struct dwc3_device *dwc3_dev) { struct dwc3 *dwc; - struct device *dev; + struct device *dev = NULL; u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index ac9a856..3dcc2f4 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -374,7 +374,7 @@ static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap, int utmi_mode) int dwc3_omap_uboot_init(struct dwc3_omap_device *omap_dev) { u32 reg; - struct device *dev; + struct device *dev = NULL; struct dwc3_omap *omap; omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); -- cgit v0.10.2 From 7310ac4961c3a8793c1830c70137455409251605 Mon Sep 17 00:00:00 2001 From: Rajesh Bhagat Date: Mon, 30 Nov 2015 12:31:34 +0530 Subject: drivers:usb:fsl: Add T4080 as affected soc for Erratum A007792 sw workaround Apply Erratum A007792 sw workaround for T4080 Signed-off-by: Sriram Dash Signed-off-by: Rajesh Bhagat Signed-off-by: Ramneek Mehresh diff --git a/include/fsl_usb.h b/include/fsl_usb.h index 8e3dded..187e384 100644 --- a/include/fsl_usb.h +++ b/include/fsl_usb.h @@ -181,6 +181,7 @@ static inline bool has_erratum_a007792(void) switch (soc) { case SVR_T4240: case SVR_T4160: + case SVR_T4080: return IS_SVR_REV(svr, 2, 0); case SVR_T1024: case SVR_T1023: -- cgit v0.10.2 From 04f378798d85f62beeb09db29a02785571514aeb Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 30 Nov 2015 18:10:09 +0100 Subject: usb: zynqmp: Fix build warnings The driver does "return 0" in function with void type. Signed-off-by: Marek Vasut Cc: Michal Simek Cc: Siva Durga Prasad Paladugu diff --git a/drivers/usb/host/xhci-zynqmp.c b/drivers/usb/host/xhci-zynqmp.c index e76a0c6..530d97c 100644 --- a/drivers/usb/host/xhci-zynqmp.c +++ b/drivers/usb/host/xhci-zynqmp.c @@ -139,5 +139,5 @@ void xhci_hcd_stop(int index) * sw. But this support may be added in future socs. */ - return 0; + return; } -- cgit v0.10.2 From 40c920821d0cbe6a243aa358187aa024c0c1ad5f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 30 Nov 2015 18:15:33 +0100 Subject: sunxi: usb: Rename the sunxi usb driver DM entry The DM entry should be unique, otherwise it will collide with other drivers. Fix this by assigning the driver a more unique name than usb_ehci. Signed-off-by: Marek Vasut Cc: Hans de Goede Cc: Ian Campbell Cc: Simon Glass diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c index 38d5f92..d494ca1 100644 --- a/drivers/usb/host/ehci-sunxi.c +++ b/drivers/usb/host/ehci-sunxi.c @@ -87,7 +87,7 @@ static const struct udevice_id ehci_usb_ids[] = { { } }; -U_BOOT_DRIVER(usb_ehci) = { +U_BOOT_DRIVER(ehci_sunxi) = { .name = "ehci_sunxi", .id = UCLASS_USB, .of_match = ehci_usb_ids, -- cgit v0.10.2 From 90fbb2823ac6c2deeab6123aa8f8f32408779e64 Mon Sep 17 00:00:00 2001 From: Alexey Brodkin Date: Wed, 2 Dec 2015 12:32:02 +0300 Subject: usb: add support for generic EHCI devices This driver is meant to be used with any EHCI-compatible host controller in case if there's no need for platform-specific glue such as setup of controller or PHY's power mode via GPIOs etc. Signed-off-by: Alexey Brodkin Cc: Stephen Warren Cc: Simon Glass Cc: Marek Vasut diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 2a2bffe..0096a2f 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -73,4 +73,12 @@ config USB_EHCI_UNIPHIER ---help--- Enables support for the on-chip EHCI controller on UniPhier SoCs. +config USB_EHCI_GENERIC + bool "Support for generic EHCI USB controller" + depends on OF_CONTROL + depends on DM_USB + default n + ---help--- + Enables support for generic EHCI controller. + endif diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 645b990..0b4b458 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -32,6 +32,7 @@ else obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o endif obj-$(CONFIG_USB_EHCI_FARADAY) += ehci-faraday.o +obj-$(CONFIG_USB_EHCI_GENERIC) += ehci-generic.o obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o diff --git a/drivers/usb/host/ehci-generic.c b/drivers/usb/host/ehci-generic.c new file mode 100644 index 0000000..1292caa --- /dev/null +++ b/drivers/usb/host/ehci-generic.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 Alexey Brodkin + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include "ehci.h" + +/* + * Even though here we don't explicitly use "struct ehci_ctrl" + * ehci_register() expects it to be the first thing that resides in + * device's private data. + */ +struct generic_ehci { + struct ehci_ctrl ctrl; +}; + +static int ehci_usb_probe(struct udevice *dev) +{ + struct ehci_hccr *hccr = (struct ehci_hccr *)dev_get_addr(dev); + struct ehci_hcor *hcor; + + hcor = (struct ehci_hcor *)((uintptr_t)hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + return ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST); +} + +static int ehci_usb_remove(struct udevice *dev) +{ + return ehci_deregister(dev); +} + +static const struct udevice_id ehci_usb_ids[] = { + { .compatible = "generic-ehci" }, + { } +}; + +U_BOOT_DRIVER(ehci_generic) = { + .name = "ehci_generic", + .id = UCLASS_USB, + .of_match = ehci_usb_ids, + .probe = ehci_usb_probe, + .remove = ehci_usb_remove, + .ops = &ehci_usb_ops, + .priv_auto_alloc_size = sizeof(struct generic_ehci), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; -- cgit v0.10.2 From 5955bb9345f692d4e9d0bbab5e97c3dd7bf58bc4 Mon Sep 17 00:00:00 2001 From: Rajesh Bhagat Date: Wed, 2 Dec 2015 11:44:27 +0530 Subject: usb: xhci: dwc3: Adding reset delay requirement as per dwc3 databook As per dwc3 databook, delay is required before taking the core out of reset. This delay is required so that the PHY are stable, and then we can take core out of reset. Reference is taken from linux dwc3 code, file: drivers/usb/dwc3/core.c. Signed-off-by: Sriram Dash Signed-off-by: Rajesh Bhagat diff --git a/drivers/usb/host/xhci-dwc3.c b/drivers/usb/host/xhci-dwc3.c index c722c50..33961cd 100644 --- a/drivers/usb/host/xhci-dwc3.c +++ b/drivers/usb/host/xhci-dwc3.c @@ -44,6 +44,8 @@ void dwc3_core_soft_reset(struct dwc3 *dwc3_reg) /* reset USB3 phy - if required */ dwc3_phy_reset(dwc3_reg); + mdelay(100); + /* After PHYs are stable we can take Core out of reset state */ clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); } -- cgit v0.10.2 From 565b7f0fe15564c73308cb647a057e95809f1d00 Mon Sep 17 00:00:00 2001 From: Rajesh Bhagat Date: Wed, 2 Dec 2015 11:44:28 +0530 Subject: usb: xhci: fsl: Removing unused function usb_phy_reset This patch removes unsued function usb_phy_reset, rather common function dwc3_phy_reset is used. Signed-off-by: Rajesh Bhagat diff --git a/drivers/usb/host/xhci-fsl.c b/drivers/usb/host/xhci-fsl.c index 6481e07..05f09d7 100644 --- a/drivers/usb/host/xhci-fsl.c +++ b/drivers/usb/host/xhci-fsl.c @@ -27,23 +27,6 @@ __weak int __board_usb_init(int index, enum usb_init_type init) return 0; } -void usb_phy_reset(struct dwc3 *dwc3_reg) -{ - /* Assert USB3 PHY reset */ - setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); - - /* Assert USB2 PHY reset */ - setbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); - - mdelay(200); - - /* Clear USB3 PHY reset */ - clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); - - /* Clear USB2 PHY reset */ - clrbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); -} - static int fsl_xhci_core_init(struct fsl_xhci *fsl_xhci) { int ret = 0; -- cgit v0.10.2 From 6af4e2782d1300015ab1c3c28e9faa5c96d93f19 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 2 Dec 2015 15:28:26 +0100 Subject: usb: xhci: zynqmp: Removing unused function usb_phy_reset This patch removes unsued function usb_phy_reset, rather common function dwc3_phy_reset is used. Signed-off-by: Marek Vasut diff --git a/drivers/usb/host/xhci-zynqmp.c b/drivers/usb/host/xhci-zynqmp.c index 530d97c..a735369 100644 --- a/drivers/usb/host/xhci-zynqmp.c +++ b/drivers/usb/host/xhci-zynqmp.c @@ -65,23 +65,6 @@ static struct zynqmp_xhci zynqmp_xhci; unsigned long ctr_addr[] = CONFIG_ZYNQMP_XHCI_LIST; -void usb_phy_reset(struct dwc3 *dwc3_reg) -{ - /* Assert USB3 PHY reset */ - setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); - - /* Assert USB2 PHY reset */ - setbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); - - udelay(10); - - /* Clear USB3 PHY reset */ - clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); - - /* Clear USB2 PHY reset */ - clrbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); -} - static int zynqmp_xhci_core_init(struct zynqmp_xhci *zynqmp_xhci) { int ret = 0; diff --git a/include/linux/usb/dwc3.h b/include/linux/usb/dwc3.h index dd934a0..6d1e365 100644 --- a/include/linux/usb/dwc3.h +++ b/include/linux/usb/dwc3.h @@ -204,7 +204,6 @@ struct dwc3 { /* offset: 0xC100 */ void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode); void dwc3_core_soft_reset(struct dwc3 *dwc3_reg); int dwc3_core_init(struct dwc3 *dwc3_reg); -void usb_phy_reset(struct dwc3 *dwc3_reg); void dwc3_set_fladj(struct dwc3 *dwc3_reg, u32 val); #endif #endif /* __DWC3_H_ */ -- cgit v0.10.2