TI DA8xx 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..f4b64b9 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/da8xx-usb.txt @@ -0,0 +1,57 @@ +TI DA8xx MUSB +~~~~~~~~~~~~~ + +Required properties: +~~~~~~~~~~~~~~~~~~~~ + - compatible : Should be "ti,da8xx-musb" + + - dr_mode: The USB operation mode. Should be one of "host", "peripheral" or "otg". + + - mentor,power : Specifies the maximum current in milli-ampers the controller can + supply in host mode. The maximum configurable value is 510mA. + + - mentor,num-eps : Valid only in host mode. Specifies the number of target endpoints + supported by the controller. For DA8xx it is "5". + + - mentor,multipoint : Valid only in host mode. Enables addressing of USB hubs, + which is normally something you want and therefore should be set to "1". + If set to "0" only point-to-point communication is enabled, i.e. only a single + device can be attached. + + - mentor,ram-bits : This 2-base logarithm value defines the RAM FIFO size of the controller. + The FIFO size is calculated in 32-bit words. E.g. if your controller has 4kiB of RAM FIFO + this value should be set to "10": 2^10 = 1024 words of 32-bits, i.e. 4096 bytes. + For the DA8xx controller this value should be set to 10. + + +Optional properties: + + - ti,phy20-clkmux-cfg: Integer. Defines the USB 2.0 PHY reference clock source. + Supported values: "0" for external pin, "1" for internal PLL. + + - ti,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,da8xx-musb"; + ti,hwmods = "usb_otg_hs"; + reg = <0x00200000 0x10000>; + interrupt-parent = <&intc>; + interrupts = <58>; + interrupt-names = "mc"; + + dr_mode = "host"; + mentor,power = <500>; + mentor,multipoint = <1>; + mentor,num-eps = <5>; + mentor,ram-bits = <10>; + + ti,phy20-clkmux-cfg = <1>; /* PHY clock internally generated from the PLL */ + ti,phy20-refclock-frequency = <24000000>; + + status = "okay"; + }; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 4e13fe2..8d842cb 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,8 @@ #include <mach/da8xx.h> #include <linux/platform_data/usb-davinci.h> +#include <linux/of_platform.h> +#include <linux/usb/of.h> #include "musb_core.h" @@ -134,6 +139,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 +561,37 @@ 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 get_musb_port_mode(struct device_node *np) +{ + enum usb_dr_mode mode; + + mode = of_usb_get_dr_mode(np); + switch (mode) { + case USB_DR_MODE_HOST: + return MUSB_HOST; + + case USB_DR_MODE_PERIPHERAL: + return MUSB_PERIPHERAL; + + case USB_DR_MODE_OTG: + return MUSB_OTG; + + default: + return MUSB_UNDEFINED; + } +} + +static int get_int_prop(struct device_node *dn, const char *s) +{ + int ret; + u32 val; + + ret = of_property_read_u32(dn, s, &val); + return ret ? 0 : val; +} + static int da8xx_probe(struct platform_device *pdev) { struct resource musb_resources[2]; @@ -535,6 +600,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 +626,97 @@ 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; + } + + pdata->mode = get_musb_port_mode(np); + config->num_eps = get_int_prop(np, "mentor,num-eps"); + config->ram_bits = get_int_prop(np, "mentor,ram-bits"); + /* the "mentor,power" value is in milli-amps, whereas + * the mentor configuration is in 2mA units + */ + pdata->power = get_int_prop(np, "mentor,power") / 2; + config->multipoint = of_property_read_bool(np, + "mentor,multipoint"); + + pdata->board_data = data; + pdata->config = config; + + /* optional parameter reference clock frequency */ + if (!of_property_read_u32(np, "ti,phy20-clkmux-cfg", + &phy20_clkmux_cfg)) { + u32 cfgchip2; + + /* + * Select the 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_SHIFT; + + __raw_writel(cfgchip2, + DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + + } + + /* optional parameter reference clock frequency */ + if (!of_property_read_u32(np, "ti,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)); + } + + pdev->dev.dma_mask = &da8xx_dmamask; + pdev->dev.coherent_dma_mask = da8xx_dmamask; + } + pdata->platform_ops = &da8xx_ops; glue->phy = usb_phy_generic_register(); @@ -627,11 +784,22 @@ static int da8xx_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id da8xx_id_table[] = { + { + .compatible = "ti,da8xx-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", + .of_match_table = of_match_ptr(da8xx_id_table), }, }; diff --git a/include/linux/platform_data/usb-davinci.h b/include/linux/platform_data/usb-davinci.h index e0bc4ab..0948ce5 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_SHIFT (11) +#define CFGCHIP2_USB2PHYCLKMUX (1 << CFGCHIP2_USB2PHYCLKMUX_SHIFT) #define CFGCHIP2_PHYPWRDN (1 << 10) #define CFGCHIP2_OTGPWRDN (1 << 9) #define CFGCHIP2_DATPOL (1 << 8) -- 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