TI DaVinci MUSB driver equipped with DeviceTree support. Tested with AM1808 board and USB2.0 (OTG) in host mode. Signed-off-by: Petr Kulhavy <petr@xxxxxxxxx> --- .../devicetree/bindings/usb/da8xx-usb.txt | 52 +++++++ drivers/usb/musb/da8xx.c | 166 +++++++++++++++++++++ include/linux/platform_data/usb-davinci.h | 3 +- 3 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/usb/da8xx-usb.txt diff --git a/Documentation/devicetree/bindings/usb/da8xx-usb.txt b/Documentation/devicetree/bindings/usb/da8xx-usb.txt new file mode 100644 index 0000000..c81d665 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/da8xx-usb.txt @@ -0,0 +1,52 @@ +TI DaVinci MUSB + +Required properties: + + - compatible : Should be "ti,da850-musb" or "ti,da830-musb" + + - mode : USB mode. "1" signifies HOST, "2" represents PERIPHERAL, + "3" represents OTG. + + - power : This signifies the maximum current the controller can + supply in host mode. The unit size is 2mA, the maximum value is 510mA. + + - num-eps : Specifies the number of endpoints. This is also a + MUSB configuration-specific setting. + + - multipoint : Should be "1" indicating the musb controller supports + multipoint. This is a MUSB configuration-specific setting. + + - ram-bits : Specifies the ram address size. + + +Optional properties: + + - da8xx,phy20-clkmux-cfg: Integer. Defines the USB 2.0 PHY reference clock source. + Supported values: "0" for external pin, "1" for internal PLL. + + - da8xx,phy20-refclock-frequency : Integer. Defines the USB 2.0 PHY reference clock input + frequency in Hz in case the clock is generated by the internal PLL. + Supported values are 12MHz, 13MHz, 19.2MHz, 20MHz, 24MHz, 26MHz, 38.4MHz, 40MHz, 48MHz + + +Example: + + usb20: usb@1e00000 { + compatible = "ti,da850-musb"; + ti,hwmods = "usb_otg_hs"; + reg = <0x00200000 0x10000>; + interrupt-parent = <&intc>; + interrupts = <58>; + interrupt-names = "mc"; + + multipoint = <1>; + num-eps = <5>; + ram-bits = <10>; + mode = <1>; /* host */ + power = <250>; /* max power 500mA */ + + da8xx,phy20-clkmux-cfg = <1>; /* PHY clock internally generated from the PLL */ + da8xx,phy20-refclock-frequency = <24000000>; + + status = "okay"; + }; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 4e13fe2..2c00e98 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -1,6 +1,9 @@ /* * Texas Instruments DA8xx/OMAP-L1x "glue layer" * + * DT support + * Copyright (c) 2015-2016 Petr Kulhavy, Barix AG <petr@xxxxxxxxx> + * * Copyright (c) 2008-2009 MontaVista Software, Inc. <source@xxxxxxxxxx> * * Based on the DaVinci "glue layer" code. @@ -36,6 +39,7 @@ #include <mach/da8xx.h> #include <linux/platform_data/usb-davinci.h> +#include <linux/of_platform.h> #include "musb_core.h" @@ -134,6 +138,35 @@ static inline void phy_off(void) __raw_writel(cfgchip2, CFGCHIP2); } +/* converts PHY refclk frequency in HZ into USB0REF_FREQ config value + * on unsupported frequency returns -1 + */ +static inline int phy_refclk_cfg(int frequency) +{ + switch (frequency) { + case 12000000: + return 0x01; + case 13000000: + return 0x06; + case 19200000: + return 0x05; + case 20000000: + return 0x08; + case 24000000: + return 0x02; + case 26000000: + return 0x07; + case 38400000: + return 0x05; + case 40000000: + return 0x09; + case 48000000: + return 0x03; + default: + return -1; + } +} + /* * Because we don't set CTRL.UINT, it's "important" to: * - not read/write INTRUSB/INTRUSBE (except during @@ -527,6 +560,8 @@ static const struct platform_device_info da8xx_dev_info = { .dma_mask = DMA_BIT_MASK(32), }; +static u64 da8xx_dmamask = DMA_BIT_MASK(32); + static int da8xx_probe(struct platform_device *pdev) { struct resource musb_resources[2]; @@ -535,6 +570,7 @@ static int da8xx_probe(struct platform_device *pdev) struct da8xx_glue *glue; struct platform_device_info pinfo; struct clk *clk; + struct device_node *np = pdev->dev.of_node; int ret = -ENOMEM; @@ -560,6 +596,120 @@ static int da8xx_probe(struct platform_device *pdev) glue->dev = &pdev->dev; glue->clk = clk; + if (np) { + struct musb_hdrc_config *config; + struct musb_hdrc_platform_data *data; + u32 phy20_refclock_freq, phy20_clkmux_cfg; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto err5; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err5; + } + + config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); + if (!config) { + ret = -ENOMEM; + goto err5; + } + + if (of_property_read_u32(np, "mode", (u32 *)&pdata->mode)) { + dev_err(&pdev->dev, + "failed to read the USB mode parameter\n"); + ret = -EINVAL; + goto err5; + } + + if (of_property_read_u32(np, "num-eps", + (u32 *)&config->num_eps)) { + dev_err(&pdev->dev, + "failed to read the number of endpoints\n"); + ret = -EINVAL; + goto err5; + } + + if (of_property_read_u32(np, "ram-bits", + (u32 *)&config->ram_bits)) { + dev_err(&pdev->dev, + "failed to read the ram-bits parameter\n"); + ret = -EINVAL; + goto err5; + } + + if (of_property_read_u32(np, "power", (u32 *)&pdata->power)) { + dev_err(&pdev->dev, + "failed to read the maximum power parameter\n"); + ret = -EINVAL; + goto err5; + } + + config->multipoint = of_property_read_bool(np, "multipoint"); + + pdata->board_data = data; + pdata->config = config; + + /* optional parameter reference clock frequency */ + if (!of_property_read_u32(np, "da8xx,phy20-clkmux-cfg", + &phy20_clkmux_cfg)) { + u32 cfgchip2; + + /* + * Select internal reference clock for USB 2.0 PHY + * and use it as a clock source for USB 1.1 PHY + * (this is the default setting anyway). + */ + + cfgchip2 = __raw_readl( + DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + + cfgchip2 &= ~CFGCHIP2_USB2PHYCLKMUX; + cfgchip2 |= (phy20_clkmux_cfg & 1) << + CFGCHIP2_USB2PHYCLKMUX_OFFSET; + + __raw_writel(cfgchip2, + DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + + } + + /* optional parameter reference clock frequency */ + if (!of_property_read_u32(np, "da8xx,phy20-refclock-frequency", + &phy20_refclock_freq)) { + u32 cfgchip2; + int phy20_refclk_cfg; + + phy20_refclk_cfg = phy_refclk_cfg(phy20_refclock_freq); + if (phy20_refclk_cfg < 0) { + dev_err(&pdev->dev, + "invalid PHY clock frequency %u Hz\n", + phy20_refclock_freq); + ret = -EINVAL; + goto err5; + } + + /* + * Set up USB clock/mode in the CFGCHIP2 register. + */ + cfgchip2 = __raw_readl( + DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + + /* USB2.0 PHY reference clock is 24 MHz */ + cfgchip2 &= ~CFGCHIP2_REFFREQ; + cfgchip2 |= phy20_refclk_cfg; + + __raw_writel(cfgchip2, + DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + } + + musb->dev.dma_mask = &da8xx_dmamask; + musb->dev.coherent_dma_mask = da8xx_dmamask; + } + pdata->platform_ops = &da8xx_ops; glue->phy = usb_phy_generic_register(); @@ -627,11 +777,27 @@ static int da8xx_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id da8xx_id_table[] = { + { + .compatible = "ti,da850-musb" + }, + { + .compatible = "ti,da830-musb" + }, + {}, +}; +MODULE_DEVICE_TABLE(of, da8xx_id_table); +#endif + static struct platform_driver da8xx_driver = { .probe = da8xx_probe, .remove = da8xx_remove, .driver = { .name = "musb-da8xx", +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(da8xx_id_table), +#endif }, }; diff --git a/include/linux/platform_data/usb-davinci.h b/include/linux/platform_data/usb-davinci.h index e0bc4ab..bd2a5a9 100644 --- a/include/linux/platform_data/usb-davinci.h +++ b/include/linux/platform_data/usb-davinci.h @@ -21,7 +21,8 @@ #define CFGCHIP2_FORCE_DEVICE (2 << 13) #define CFGCHIP2_FORCE_HOST_VBUS_LOW (3 << 13) #define CFGCHIP2_USB1PHYCLKMUX (1 << 12) -#define CFGCHIP2_USB2PHYCLKMUX (1 << 11) +#define CFGCHIP2_USB2PHYCLKMUX_OFFSET (11) +#define CFGCHIP2_USB2PHYCLKMUX (1 << CFGCHIP2_USB2PHYCLKMUX_OFFSET) #define CFGCHIP2_PHYPWRDN (1 << 10) #define CFGCHIP2_OTGPWRDN (1 << 9) #define CFGCHIP2_DATPOL (1 << 8) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html