From ae073881aa7d2f744fa703ae47371611780e4e44 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Wed, 10 Dec 2014 15:45:22 +0100 Subject: clk: shmobile: sh73a0 common clock framework implementation Driver for the SH73A0's clocks that are too specific to be supported by a generic driver. Signed-off-by: Ulrich Hecht Acked-by: Mike Turquette Tested-by: Geert Uytterhoeven Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/clk/shmobile/Makefile | 1 + drivers/clk/shmobile/clk-sh73a0.c | 218 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 drivers/clk/shmobile/clk-sh73a0.c (limited to 'drivers') diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile index 960bf22d42ae..f83980f2b956 100644 --- a/drivers/clk/shmobile/Makefile +++ b/drivers/clk/shmobile/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_R8A7794) += clk-rcar-gen2.o +obj-$(CONFIG_ARCH_SH73A0) += clk-sh73a0.o obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-mstp.o diff --git a/drivers/clk/shmobile/clk-sh73a0.c b/drivers/clk/shmobile/clk-sh73a0.c new file mode 100644 index 000000000000..8574a6d91b20 --- /dev/null +++ b/drivers/clk/shmobile/clk-sh73a0.c @@ -0,0 +1,218 @@ +/* + * sh73a0 Core CPG Clocks + * + * Copyright (C) 2014 Ulrich Hecht + * + * 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; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct sh73a0_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +#define CPG_FRQCRA 0x00 +#define CPG_FRQCRB 0x04 +#define CPG_SD0CKCR 0x74 +#define CPG_SD1CKCR 0x78 +#define CPG_SD2CKCR 0x7c +#define CPG_PLLECR 0xd0 +#define CPG_PLL0CR 0xd8 +#define CPG_PLL1CR 0x28 +#define CPG_PLL2CR 0x2c +#define CPG_PLL3CR 0xdc +#define CPG_CKSCR 0xc0 +#define CPG_DSI0PHYCR 0x6c +#define CPG_DSI1PHYCR 0x70 + +#define CLK_ENABLE_ON_INIT BIT(0) + +struct div4_clk { + const char *name; + const char *parent; + unsigned int reg; + unsigned int shift; +}; + +static struct div4_clk div4_clks[] = { + { "zg", "pll0", CPG_FRQCRA, 16 }, + { "m3", "pll1", CPG_FRQCRA, 12 }, + { "b", "pll1", CPG_FRQCRA, 8 }, + { "m1", "pll1", CPG_FRQCRA, 4 }, + { "m2", "pll1", CPG_FRQCRA, 0 }, + { "zx", "pll1", CPG_FRQCRB, 12 }, + { "hp", "pll1", CPG_FRQCRB, 4 }, + { NULL, 0, 0, 0 }, +}; + +static const struct clk_div_table div4_div_table[] = { + { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 }, + { 6, 16 }, { 7, 18 }, { 8, 24 }, { 10, 36 }, { 11, 48 }, + { 12, 7 }, { 0, 0 } +}; + +static const struct clk_div_table z_div_table[] = { + /* ZSEL == 0 */ + { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 5, 1 }, + { 6, 1 }, { 7, 1 }, { 8, 1 }, { 9, 1 }, { 10, 1 }, { 11, 1 }, + { 12, 1 }, { 13, 1 }, { 14, 1 }, { 15, 1 }, + /* ZSEL == 1 */ + { 16, 2 }, { 17, 3 }, { 18, 4 }, { 19, 6 }, { 20, 8 }, { 21, 12 }, + { 22, 16 }, { 24, 24 }, { 27, 48 }, { 0, 0 } +}; + +static struct clk * __init +sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, + const char *name) +{ + const struct clk_div_table *table = NULL; + unsigned int shift, reg, width; + const char *parent_name; + unsigned int mult = 1; + unsigned int div = 1; + + if (!strcmp(name, "main")) { + /* extal1, extal1_div2, extal2, extal2_div2 */ + u32 parent_idx = (clk_readl(cpg->reg + CPG_CKSCR) >> 28) & 3; + + parent_name = of_clk_get_parent_name(np, parent_idx >> 1); + div = (parent_idx & 1) + 1; + } else if (!strncmp(name, "pll", 3)) { + void __iomem *enable_reg = cpg->reg; + u32 enable_bit = name[3] - '0'; + + parent_name = "main"; + switch (enable_bit) { + case 0: + enable_reg += CPG_PLL0CR; + break; + case 1: + enable_reg += CPG_PLL1CR; + break; + case 2: + enable_reg += CPG_PLL2CR; + break; + case 3: + enable_reg += CPG_PLL3CR; + break; + default: + return ERR_PTR(-EINVAL); + } + if (clk_readl(cpg->reg + CPG_PLLECR) & BIT(enable_bit)) { + mult = ((clk_readl(enable_reg) >> 24) & 0x3f) + 1; + /* handle CFG bit for PLL1 and PLL2 */ + if (enable_bit == 1 || enable_bit == 2) + if (clk_readl(enable_reg) & BIT(20)) + mult *= 2; + } + } else if (!strcmp(name, "dsi0phy") || !strcmp(name, "dsi1phy")) { + u32 phy_no = name[3] - '0'; + void __iomem *dsi_reg = cpg->reg + + (phy_no ? CPG_DSI1PHYCR : CPG_DSI0PHYCR); + + parent_name = phy_no ? "dsi1pck" : "dsi0pck"; + mult = __raw_readl(dsi_reg); + if (!(mult & 0x8000)) + mult = 1; + else + mult = (mult & 0x3f) + 1; + } else if (!strcmp(name, "z")) { + parent_name = "pll0"; + table = z_div_table; + reg = CPG_FRQCRB; + shift = 24; + width = 5; + } else { + struct div4_clk *c; + + for (c = div4_clks; c->name; c++) { + if (!strcmp(name, c->name)) { + parent_name = c->parent; + table = div4_div_table; + reg = c->reg; + shift = c->shift; + width = 4; + break; + } + } + if (!c->name) + return ERR_PTR(-EINVAL); + } + + if (!table) { + return clk_register_fixed_factor(NULL, name, parent_name, 0, + mult, div); + } else { + return clk_register_divider_table(NULL, name, parent_name, 0, + cpg->reg + reg, shift, width, 0, + table, &cpg->lock); + } +} + +static void __init sh73a0_cpg_clocks_init(struct device_node *np) +{ + struct sh73a0_cpg *cpg; + struct clk **clks; + unsigned int i; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + if (WARN_ON(cpg->reg == NULL)) + return; + + /* Set SDHI clocks to a known state */ + clk_writel(0x108, cpg->reg + CPG_SD0CKCR); + clk_writel(0x108, cpg->reg + CPG_SD1CKCR); + clk_writel(0x108, cpg->reg + CPG_SD2CKCR); + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, + &name); + + clk = sh73a0_cpg_register_clock(np, cpg, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); +} +CLK_OF_DECLARE(sh73a0_cpg_clks, "renesas,sh73a0-cpg-clocks", + sh73a0_cpg_clocks_init); -- cgit v1.2.3 From 16bd794f9432b556ec797cbf16c5e575a18f7030 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 14 Jan 2015 09:16:23 +0800 Subject: clk: shmobile: fix sparse NULL pointer warning Fixes the following sparse warnings: drivers/clk/shmobile/clk-sh73a0.c:57:17: warning: Using plain integer as NULL pointer Signed-off-by: Wei Yongjun Signed-off-by: Simon Horman --- drivers/clk/shmobile/clk-sh73a0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/shmobile/clk-sh73a0.c b/drivers/clk/shmobile/clk-sh73a0.c index 8574a6d91b20..cd529cfe412f 100644 --- a/drivers/clk/shmobile/clk-sh73a0.c +++ b/drivers/clk/shmobile/clk-sh73a0.c @@ -54,7 +54,7 @@ static struct div4_clk div4_clks[] = { { "m2", "pll1", CPG_FRQCRA, 0 }, { "zx", "pll1", CPG_FRQCRB, 12 }, { "hp", "pll1", CPG_FRQCRB, 4 }, - { NULL, 0, 0, 0 }, + { NULL, NULL, 0, 0 }, }; static const struct clk_div_table div4_div_table[] = { -- cgit v1.2.3 From 7923540385615c5379792fd21f98b24877b22734 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 14 Jan 2015 17:21:57 +0100 Subject: usb: gadget: at91_udc: Fix clock names The driver is requesting clock by their global name (those declared in the clk_lookup list), but this only works with !CCF kernels. Now that all SoCs have moved to CCF, fix the driver to use local names (hclk and pclk). Signed-off-by: Boris Brezillon Acked-by: Alexandre Belloni Acked-by: Felipe Balbi Signed-off-by: Nicolas Ferre --- drivers/usb/gadget/udc/at91_udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index c862656d18b8..9ff2f7e5c6a7 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1779,8 +1779,8 @@ static int at91udc_probe(struct platform_device *pdev) udc_reinit(udc); /* get interface and function clocks */ - udc->iclk = clk_get(dev, "udc_clk"); - udc->fclk = clk_get(dev, "udpck"); + udc->iclk = clk_get(dev, "pclk"); + udc->fclk = clk_get(dev, "hclk"); if (IS_ENABLED(CONFIG_COMMON_CLK)) udc->uclk = clk_get(dev, "usb_clk"); if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk) || -- cgit v1.2.3 From 9aa0216550b227f41e3e515d3bfdb336bb92dc38 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 14 Jan 2015 17:21:58 +0100 Subject: usb: gadget: at91_udc: Drop uclk clock Now that at91 system clocks forward set_rate request to their parent we can remove the uclk clock and directly call clk_set_rate on fclk. Signed-off-by: Boris Brezillon Acked-by: Alexandre Belloni Acked-by: Felipe Balbi Signed-off-by: Nicolas Ferre --- drivers/usb/gadget/udc/at91_udc.c | 27 +++------------------------ drivers/usb/gadget/udc/at91_udc.h | 2 +- 2 files changed, 4 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 9ff2f7e5c6a7..d33f2994b7c4 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -895,8 +895,6 @@ static void clk_on(struct at91_udc *udc) return; udc->clocked = 1; - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_enable(udc->uclk); clk_enable(udc->iclk); clk_enable(udc->fclk); } @@ -909,8 +907,6 @@ static void clk_off(struct at91_udc *udc) udc->gadget.speed = USB_SPEED_UNKNOWN; clk_disable(udc->fclk); clk_disable(udc->iclk); - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_disable(udc->uclk); } /* @@ -1781,25 +1777,17 @@ static int at91udc_probe(struct platform_device *pdev) /* get interface and function clocks */ udc->iclk = clk_get(dev, "pclk"); udc->fclk = clk_get(dev, "hclk"); - if (IS_ENABLED(CONFIG_COMMON_CLK)) - udc->uclk = clk_get(dev, "usb_clk"); - if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk) || - (IS_ENABLED(CONFIG_COMMON_CLK) && IS_ERR(udc->uclk))) { + if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk)) { DBG("clocks missing\n"); retval = -ENODEV; goto fail1; } /* don't do anything until we have both gadget driver and VBUS */ - if (IS_ENABLED(CONFIG_COMMON_CLK)) { - clk_set_rate(udc->uclk, 48000000); - retval = clk_prepare(udc->uclk); - if (retval) - goto fail1; - } + clk_set_rate(udc->fclk, 48000000); retval = clk_prepare(udc->fclk); if (retval) - goto fail1a; + goto fail1; retval = clk_prepare_enable(udc->iclk); if (retval) @@ -1873,12 +1861,7 @@ fail1c: clk_unprepare(udc->iclk); fail1b: clk_unprepare(udc->fclk); -fail1a: - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_unprepare(udc->uclk); fail1: - if (IS_ENABLED(CONFIG_COMMON_CLK) && !IS_ERR(udc->uclk)) - clk_put(udc->uclk); if (!IS_ERR(udc->fclk)) clk_put(udc->fclk); if (!IS_ERR(udc->iclk)) @@ -1924,15 +1907,11 @@ static int __exit at91udc_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_unprepare(udc->uclk); clk_unprepare(udc->fclk); clk_unprepare(udc->iclk); clk_put(udc->iclk); clk_put(udc->fclk); - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_put(udc->uclk); return 0; } diff --git a/drivers/usb/gadget/udc/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index 017524663381..e647d1c2ada4 100644 --- a/drivers/usb/gadget/udc/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h @@ -126,7 +126,7 @@ struct at91_udc { unsigned active_suspend:1; u8 addr; struct at91_udc_data board; - struct clk *iclk, *fclk, *uclk; + struct clk *iclk, *fclk; struct platform_device *pdev; struct proc_dir_entry *pde; void __iomem *udp_baseaddr; -- cgit v1.2.3 From 9f00fc1dae4a2c909b970f07d17ef649f72873fe Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 14 Jan 2015 17:22:00 +0100 Subject: usb: gadget: at91_udc: Remove non-DT handling code Since non-DT board support has been removed from the at91 architecture we can safely remove non-DT handling code. Signed-off-by: Boris Brezillon Acked-by: Alexandre Belloni Acked-by: Felipe Balbi Signed-off-by: Nicolas Ferre --- drivers/usb/gadget/udc/Kconfig | 1 + drivers/usb/gadget/udc/at91_udc.c | 16 ++-------------- 2 files changed, 3 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index b8e213eb36cc..366e551aeff0 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -32,6 +32,7 @@ menu "USB Peripheral Controller" config USB_AT91 tristate "Atmel AT91 USB Device Port" depends on ARCH_AT91 + depends on OF || COMPILE_TEST help Many Atmel AT91 processors (such as the AT91RM2000) have a full speed USB Device Port with support for five configurable diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index d33f2994b7c4..be7e16037ac4 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1710,12 +1710,6 @@ static int at91udc_probe(struct platform_device *pdev) int retval; struct resource *res; - if (!dev_get_platdata(dev) && !pdev->dev.of_node) { - /* small (so we copy it) but critical! */ - DBG("missing platform_data\n"); - return -ENODEV; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENXIO; @@ -1728,11 +1722,7 @@ static int at91udc_probe(struct platform_device *pdev) /* init software state */ udc = &controller; udc->gadget.dev.parent = dev; - if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) - at91udc_of_init(udc, pdev->dev.of_node); - else - memcpy(&udc->board, dev_get_platdata(dev), - sizeof(struct at91_udc_data)); + at91udc_of_init(udc, pdev->dev.of_node); udc->pdev = pdev; udc->enabled = 0; spin_lock_init(&udc->lock); @@ -1968,14 +1958,12 @@ static int at91udc_resume(struct platform_device *pdev) #define at91udc_resume NULL #endif -#if defined(CONFIG_OF) static const struct of_device_id at91_udc_dt_ids[] = { { .compatible = "atmel,at91rm9200-udc" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, at91_udc_dt_ids); -#endif static struct platform_driver at91_udc_driver = { .remove = __exit_p(at91udc_remove), @@ -1984,7 +1972,7 @@ static struct platform_driver at91_udc_driver = { .resume = at91udc_resume, .driver = { .name = (char *) driver_name, - .of_match_table = of_match_ptr(at91_udc_dt_ids), + .of_match_table = at91_udc_dt_ids, }, }; -- cgit v1.2.3 From 422cde259ab62fcddcb3e50f44f761cf26982c1e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 14 Jan 2015 17:22:01 +0100 Subject: usb: gadget: at91_udc: Simplify probe and remove functions Make use of devm_ functions to simplify probe and remove code. Signed-off-by: Boris Brezillon Acked-by: Alexandre Belloni Acked-by: Felipe Balbi Signed-off-by: Nicolas Ferre --- drivers/usb/gadget/udc/at91_udc.c | 116 +++++++++++++------------------------- 1 file changed, 39 insertions(+), 77 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index be7e16037ac4..4dba2c65dfd4 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1710,15 +1710,6 @@ static int at91udc_probe(struct platform_device *pdev) int retval; struct resource *res; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - - if (!request_mem_region(res->start, resource_size(res), driver_name)) { - DBG("someone's using UDC memory\n"); - return -EBUSY; - } - /* init software state */ udc = &controller; udc->gadget.dev.parent = dev; @@ -1731,13 +1722,13 @@ static int at91udc_probe(struct platform_device *pdev) if (cpu_is_at91rm9200()) { if (!gpio_is_valid(udc->board.pullup_pin)) { DBG("no D+ pullup?\n"); - retval = -ENODEV; - goto fail0; + return -ENODEV; } - retval = gpio_request(udc->board.pullup_pin, "udc_pullup"); + retval = devm_gpio_request(dev, udc->board.pullup_pin, + "udc_pullup"); if (retval) { DBG("D+ pullup is busy\n"); - goto fail0; + return retval; } gpio_direction_output(udc->board.pullup_pin, udc->board.pullup_active_low); @@ -1756,32 +1747,32 @@ static int at91udc_probe(struct platform_device *pdev) udc->ep[3].maxpacket = 64; } - udc->udp_baseaddr = ioremap(res->start, resource_size(res)); - if (!udc->udp_baseaddr) { - retval = -ENOMEM; - goto fail0a; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + udc->udp_baseaddr = devm_ioremap_resource(dev, res); + if (IS_ERR(udc->udp_baseaddr)) + return PTR_ERR(udc->udp_baseaddr); udc_reinit(udc); /* get interface and function clocks */ - udc->iclk = clk_get(dev, "pclk"); - udc->fclk = clk_get(dev, "hclk"); - if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk)) { - DBG("clocks missing\n"); - retval = -ENODEV; - goto fail1; - } + udc->iclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(udc->iclk)) + return PTR_ERR(udc->iclk); + + udc->fclk = devm_clk_get(dev, "hclk"); + if (IS_ERR(udc->fclk)) + return PTR_ERR(udc->fclk); /* don't do anything until we have both gadget driver and VBUS */ clk_set_rate(udc->fclk, 48000000); retval = clk_prepare(udc->fclk); if (retval) - goto fail1; + return retval; retval = clk_prepare_enable(udc->iclk); if (retval) - goto fail1b; + goto err_unprepare_fclk; + at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); at91_udp_write(udc, AT91_UDP_IDR, 0xffffffff); /* Clear all pending interrupts - UDP may be used by bootloader. */ @@ -1790,18 +1781,21 @@ static int at91udc_probe(struct platform_device *pdev) /* request UDC and maybe VBUS irqs */ udc->udp_irq = platform_get_irq(pdev, 0); - retval = request_irq(udc->udp_irq, at91_udc_irq, - 0, driver_name, udc); - if (retval < 0) { + retval = devm_request_irq(dev, udc->udp_irq, at91_udc_irq, 0, + driver_name, udc); + if (retval) { DBG("request irq %d failed\n", udc->udp_irq); - goto fail1c; + goto err_unprepare_iclk; } + if (gpio_is_valid(udc->board.vbus_pin)) { - retval = gpio_request(udc->board.vbus_pin, "udc_vbus"); - if (retval < 0) { + retval = devm_gpio_request(dev, udc->board.vbus_pin, + "udc_vbus"); + if (retval) { DBG("request vbus pin failed\n"); - goto fail2; + goto err_unprepare_iclk; } + gpio_direction_input(udc->board.vbus_pin); /* @@ -1818,12 +1812,13 @@ static int at91udc_probe(struct platform_device *pdev) mod_timer(&udc->vbus_timer, jiffies + VBUS_POLL_TIMEOUT); } else { - if (request_irq(gpio_to_irq(udc->board.vbus_pin), - at91_vbus_irq, 0, driver_name, udc)) { + retval = devm_request_irq(dev, + gpio_to_irq(udc->board.vbus_pin), + at91_vbus_irq, 0, driver_name, udc); + if (retval) { DBG("request vbus irq %d failed\n", udc->board.vbus_pin); - retval = -EBUSY; - goto fail3; + goto err_unprepare_iclk; } } } else { @@ -1832,44 +1827,27 @@ static int at91udc_probe(struct platform_device *pdev) } retval = usb_add_gadget_udc(dev, &udc->gadget); if (retval) - goto fail4; + goto err_unprepare_iclk; dev_set_drvdata(dev, udc); device_init_wakeup(dev, 1); create_debug_file(udc); INFO("%s version %s\n", driver_name, DRIVER_VERSION); return 0; -fail4: - if (gpio_is_valid(udc->board.vbus_pin) && !udc->board.vbus_polled) - free_irq(gpio_to_irq(udc->board.vbus_pin), udc); -fail3: - if (gpio_is_valid(udc->board.vbus_pin)) - gpio_free(udc->board.vbus_pin); -fail2: - free_irq(udc->udp_irq, udc); -fail1c: + +err_unprepare_iclk: clk_unprepare(udc->iclk); -fail1b: +err_unprepare_fclk: clk_unprepare(udc->fclk); -fail1: - if (!IS_ERR(udc->fclk)) - clk_put(udc->fclk); - if (!IS_ERR(udc->iclk)) - clk_put(udc->iclk); - iounmap(udc->udp_baseaddr); -fail0a: - if (cpu_is_at91rm9200()) - gpio_free(udc->board.pullup_pin); -fail0: - release_mem_region(res->start, resource_size(res)); + DBG("%s probe failed, %d\n", driver_name, retval); + return retval; } static int __exit at91udc_remove(struct platform_device *pdev) { struct at91_udc *udc = platform_get_drvdata(pdev); - struct resource *res; unsigned long flags; DBG("remove\n"); @@ -1884,25 +1862,9 @@ static int __exit at91udc_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); remove_debug_file(udc); - if (gpio_is_valid(udc->board.vbus_pin)) { - free_irq(gpio_to_irq(udc->board.vbus_pin), udc); - gpio_free(udc->board.vbus_pin); - } - free_irq(udc->udp_irq, udc); - iounmap(udc->udp_baseaddr); - - if (cpu_is_at91rm9200()) - gpio_free(udc->board.pullup_pin); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - clk_unprepare(udc->fclk); clk_unprepare(udc->iclk); - clk_put(udc->iclk); - clk_put(udc->fclk); - return 0; } -- cgit v1.2.3 From f0bceab4e3b528e799aba8fda8d2936fcfd41f1f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 14 Jan 2015 17:22:02 +0100 Subject: usb: gadget: at91_udc: Rework for multi-platform kernel support cpu_is_at91xxx are a set of macros defined in mach/cpu.h and are here used to detect the SoC we are booting on. Use compatible string + a caps structure to replace those cpu_is_xxx tests. Remove all mach and asm headers (which are now unused). Signed-off-by: Boris Brezillon Acked-by: Alexandre Belloni Acked-by: Felipe Balbi Signed-off-by: Nicolas Ferre --- drivers/usb/gadget/udc/at91_udc.c | 288 ++++++++++++++++++++++++++++---------- drivers/usb/gadget/udc/at91_udc.h | 7 + 2 files changed, 218 insertions(+), 77 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 4dba2c65dfd4..c0abb9bc76a9 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -31,16 +31,9 @@ #include #include #include - -#include -#include -#include -#include -#include - -#include -#include -#include +#include +#include +#include #include "at91_udc.h" @@ -915,8 +908,6 @@ static void clk_off(struct at91_udc *udc) */ static void pullup(struct at91_udc *udc, int is_on) { - int active = !udc->board.pullup_active_low; - if (!udc->enabled || !udc->vbus) is_on = 0; DBG("%sactive\n", is_on ? "" : "in"); @@ -925,40 +916,15 @@ static void pullup(struct at91_udc *udc, int is_on) clk_on(udc); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_TXVC, 0); - if (cpu_is_at91rm9200()) - gpio_set_value(udc->board.pullup_pin, active); - else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) { - u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); - - txvc |= AT91_UDP_TXVC_PUON; - at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { - u32 usbpucr; - - usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR); - usbpucr |= AT91_MATRIX_USBPUCR_PUON; - at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr); - } } else { stop_activity(udc); at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); - if (cpu_is_at91rm9200()) - gpio_set_value(udc->board.pullup_pin, !active); - else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) { - u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); - - txvc &= ~AT91_UDP_TXVC_PUON; - at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { - u32 usbpucr; - - usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR); - usbpucr &= ~AT91_MATRIX_USBPUCR_PUON; - at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr); - } clk_off(udc); } + + if (udc->caps && udc->caps->pullup) + udc->caps->pullup(udc, is_on); } /* vbus is here! turn everything on that's ready */ @@ -1683,12 +1649,202 @@ static void at91udc_shutdown(struct platform_device *dev) spin_unlock_irqrestore(&udc->lock, flags); } -static void at91udc_of_init(struct at91_udc *udc, - struct device_node *np) +static int at91rm9200_udc_init(struct at91_udc *udc) +{ + struct at91_ep *ep; + int ret; + int i; + + for (i = 0; i < NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + + switch (i) { + case 0: + case 3: + ep->maxpacket = 8; + break; + case 1 ... 2: + ep->maxpacket = 64; + break; + case 4 ... 5: + ep->maxpacket = 256; + break; + } + } + + if (!gpio_is_valid(udc->board.pullup_pin)) { + DBG("no D+ pullup?\n"); + return -ENODEV; + } + + ret = devm_gpio_request(&udc->pdev->dev, udc->board.pullup_pin, + "udc_pullup"); + if (ret) { + DBG("D+ pullup is busy\n"); + return ret; + } + + gpio_direction_output(udc->board.pullup_pin, + udc->board.pullup_active_low); + + return 0; +} + +static void at91rm9200_udc_pullup(struct at91_udc *udc, int is_on) +{ + int active = !udc->board.pullup_active_low; + + if (is_on) + gpio_set_value(udc->board.pullup_pin, active); + else + gpio_set_value(udc->board.pullup_pin, !active); +} + +static const struct at91_udc_caps at91rm9200_udc_caps = { + .init = at91rm9200_udc_init, + .pullup = at91rm9200_udc_pullup, +}; + +static int at91sam9260_udc_init(struct at91_udc *udc) +{ + struct at91_ep *ep; + int i; + + for (i = 0; i < NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + + switch (i) { + case 0 ... 3: + ep->maxpacket = 64; + break; + case 4 ... 5: + ep->maxpacket = 512; + break; + } + } + + return 0; +} + +static void at91sam9260_udc_pullup(struct at91_udc *udc, int is_on) +{ + u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); + + if (is_on) + txvc |= AT91_UDP_TXVC_PUON; + else + txvc &= ~AT91_UDP_TXVC_PUON; + + at91_udp_write(udc, AT91_UDP_TXVC, txvc); +} + +static const struct at91_udc_caps at91sam9260_udc_caps = { + .init = at91sam9260_udc_init, + .pullup = at91sam9260_udc_pullup, +}; + +static int at91sam9261_udc_init(struct at91_udc *udc) +{ + struct at91_ep *ep; + int i; + + for (i = 0; i < NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + + switch (i) { + case 0: + ep->maxpacket = 8; + break; + case 1 ... 3: + ep->maxpacket = 64; + break; + case 4 ... 5: + ep->maxpacket = 256; + break; + } + } + + udc->matrix = syscon_regmap_lookup_by_phandle(udc->pdev->dev.of_node, + "atmel,matrix"); + if (IS_ERR(udc->matrix)) + return PTR_ERR(udc->matrix); + + return 0; +} + +static void at91sam9261_udc_pullup(struct at91_udc *udc, int is_on) +{ + u32 usbpucr = 0; + + if (is_on) + usbpucr = AT91_MATRIX_USBPUCR_PUON; + + regmap_update_bits(udc->matrix, AT91SAM9261_MATRIX_USBPUCR, + AT91_MATRIX_USBPUCR_PUON, usbpucr); +} + +static const struct at91_udc_caps at91sam9261_udc_caps = { + .init = at91sam9261_udc_init, + .pullup = at91sam9261_udc_pullup, +}; + +static int at91sam9263_udc_init(struct at91_udc *udc) +{ + struct at91_ep *ep; + int i; + + for (i = 0; i < NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + + switch (i) { + case 0: + case 1: + case 2: + case 3: + ep->maxpacket = 64; + break; + case 4: + case 5: + ep->maxpacket = 256; + break; + } + } + + return 0; +} + +static const struct at91_udc_caps at91sam9263_udc_caps = { + .init = at91sam9263_udc_init, + .pullup = at91sam9260_udc_pullup, +}; + +static const struct of_device_id at91_udc_dt_ids[] = { + { + .compatible = "atmel,at91rm9200-udc", + .data = &at91rm9200_udc_caps, + }, + { + .compatible = "atmel,at91sam9260-udc", + .data = &at91sam9260_udc_caps, + }, + { + .compatible = "atmel,at91sam9261-udc", + .data = &at91sam9261_udc_caps, + }, + { + .compatible = "atmel,at91sam9263-udc", + .data = &at91sam9263_udc_caps, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, at91_udc_dt_ids); + +static void at91udc_of_init(struct at91_udc *udc, struct device_node *np) { struct at91_udc_data *board = &udc->board; - u32 val; + const struct of_device_id *match; enum of_gpio_flags flags; + u32 val; if (of_property_read_u32(np, "atmel,vbus-polled", &val) == 0) board->vbus_polled = 1; @@ -1701,6 +1857,10 @@ static void at91udc_of_init(struct at91_udc *udc, &flags); board->pullup_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; + + match = of_match_node(at91_udc_dt_ids, np); + if (match) + udc->caps = match->data; } static int at91udc_probe(struct platform_device *pdev) @@ -1709,6 +1869,8 @@ static int at91udc_probe(struct platform_device *pdev) struct at91_udc *udc; int retval; struct resource *res; + struct at91_ep *ep; + int i; /* init software state */ udc = &controller; @@ -1718,40 +1880,19 @@ static int at91udc_probe(struct platform_device *pdev) udc->enabled = 0; spin_lock_init(&udc->lock); - /* rm9200 needs manual D+ pullup; off by default */ - if (cpu_is_at91rm9200()) { - if (!gpio_is_valid(udc->board.pullup_pin)) { - DBG("no D+ pullup?\n"); - return -ENODEV; - } - retval = devm_gpio_request(dev, udc->board.pullup_pin, - "udc_pullup"); - if (retval) { - DBG("D+ pullup is busy\n"); - return retval; - } - gpio_direction_output(udc->board.pullup_pin, - udc->board.pullup_active_low); - } - /* newer chips have more FIFO memory than rm9200 */ - if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) { - udc->ep[0].maxpacket = 64; - udc->ep[3].maxpacket = 64; - udc->ep[4].maxpacket = 512; - udc->ep[5].maxpacket = 512; - } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { - udc->ep[3].maxpacket = 64; - } else if (cpu_is_at91sam9263()) { - udc->ep[0].maxpacket = 64; - udc->ep[3].maxpacket = 64; - } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); udc->udp_baseaddr = devm_ioremap_resource(dev, res); if (IS_ERR(udc->udp_baseaddr)) return PTR_ERR(udc->udp_baseaddr); + if (udc->caps && udc->caps->init) { + retval = udc->caps->init(udc); + if (retval) + return retval; + } + udc_reinit(udc); /* get interface and function clocks */ @@ -1920,13 +2061,6 @@ static int at91udc_resume(struct platform_device *pdev) #define at91udc_resume NULL #endif -static const struct of_device_id at91_udc_dt_ids[] = { - { .compatible = "atmel,at91rm9200-udc" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, at91_udc_dt_ids); - static struct platform_driver at91_udc_driver = { .remove = __exit_p(at91udc_remove), .shutdown = at91udc_shutdown, diff --git a/drivers/usb/gadget/udc/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index e647d1c2ada4..4fc0daa6587f 100644 --- a/drivers/usb/gadget/udc/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h @@ -107,6 +107,11 @@ struct at91_ep { unsigned fifo_bank:1; }; +struct at91_udc_caps { + int (*init)(struct at91_udc *udc); + void (*pullup)(struct at91_udc *udc, int is_on); +}; + /* * driver is non-SMP, and just blocks IRQs whenever it needs * access protection for chip registers or driver state @@ -115,6 +120,7 @@ struct at91_udc { struct usb_gadget gadget; struct at91_ep ep[NUM_ENDPOINTS]; struct usb_gadget_driver *driver; + const struct at91_udc_caps *caps; unsigned vbus:1; unsigned enabled:1; unsigned clocked:1; @@ -134,6 +140,7 @@ struct at91_udc { spinlock_t lock; struct timer_list vbus_timer; struct work_struct vbus_timer_work; + struct regmap *matrix; }; static inline struct at91_udc *to_udc(struct usb_gadget *g) -- cgit v1.2.3 From a5514d142e7f5cff8e02a6fb4cfcb3e301c0eb59 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 14 Jan 2015 17:22:04 +0100 Subject: usb: gadget: at91_udc: Allocate udc instance Allocate udc structure instead of relying on the statically declared object. Signed-off-by: Boris Brezillon Acked-by: Alexandre Belloni Acked-by: Felipe Balbi Signed-off-by: Nicolas Ferre --- drivers/usb/gadget/udc/at91_udc.c | 96 +++++++++++---------------------------- 1 file changed, 26 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index c0abb9bc76a9..6d285226ab94 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -59,7 +59,15 @@ #define DRIVER_VERSION "3 May 2006" static const char driver_name [] = "at91_udc"; -static const char ep0name[] = "ep0"; +static const char * const ep_names[] = { + "ep0", + "ep1", + "ep2", + "ep3-int", + "ep4", + "ep5", +}; +#define ep0name ep_names[0] #define VBUS_POLL_TIMEOUT msecs_to_jiffies(1000) @@ -1497,74 +1505,6 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) /*-------------------------------------------------------------------------*/ -static struct at91_udc controller = { - .gadget = { - .ops = &at91_udc_ops, - .ep0 = &controller.ep[0].ep, - .name = driver_name, - }, - .ep[0] = { - .ep = { - .name = ep0name, - .ops = &at91_ep_ops, - }, - .udc = &controller, - .maxpacket = 8, - .int_mask = 1 << 0, - }, - .ep[1] = { - .ep = { - .name = "ep1", - .ops = &at91_ep_ops, - }, - .udc = &controller, - .is_pingpong = 1, - .maxpacket = 64, - .int_mask = 1 << 1, - }, - .ep[2] = { - .ep = { - .name = "ep2", - .ops = &at91_ep_ops, - }, - .udc = &controller, - .is_pingpong = 1, - .maxpacket = 64, - .int_mask = 1 << 2, - }, - .ep[3] = { - .ep = { - /* could actually do bulk too */ - .name = "ep3-int", - .ops = &at91_ep_ops, - }, - .udc = &controller, - .maxpacket = 8, - .int_mask = 1 << 3, - }, - .ep[4] = { - .ep = { - .name = "ep4", - .ops = &at91_ep_ops, - }, - .udc = &controller, - .is_pingpong = 1, - .maxpacket = 256, - .int_mask = 1 << 4, - }, - .ep[5] = { - .ep = { - .name = "ep5", - .ops = &at91_ep_ops, - }, - .udc = &controller, - .is_pingpong = 1, - .maxpacket = 256, - .int_mask = 1 << 5, - }, - /* ep6 and ep7 are also reserved (custom silicon might use them) */ -}; - static void at91_vbus_update(struct at91_udc *udc, unsigned value) { value ^= udc->board.vbus_active_low; @@ -1872,15 +1812,31 @@ static int at91udc_probe(struct platform_device *pdev) struct at91_ep *ep; int i; + udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL); + if (!udc) + return -ENOMEM; + /* init software state */ - udc = &controller; udc->gadget.dev.parent = dev; at91udc_of_init(udc, pdev->dev.of_node); udc->pdev = pdev; udc->enabled = 0; spin_lock_init(&udc->lock); + udc->gadget.ops = &at91_udc_ops; + udc->gadget.ep0 = &udc->ep[0].ep; + udc->gadget.name = driver_name; + udc->gadget.dev.init_name = "gadget"; + for (i = 0; i < NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + ep->ep.name = ep_names[i]; + ep->ep.ops = &at91_ep_ops; + ep->udc = udc; + ep->int_mask = BIT(i); + if (i != 0 && i != 3) + ep->is_pingpong = 1; + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); udc->udp_baseaddr = devm_ioremap_resource(dev, res); -- cgit v1.2.3 From 0495cb75ed301bbe6b01346cbf8bc5244a2948b2 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 16 Jan 2015 17:04:56 +0000 Subject: of/platform: teardown DMA mappings on device destruction Now that we can create and attach to IOMMU domains via of_dma_configure, make sure we give the architecture a chance to tear them down when a platform or amba device is destroyed. Acked-by: Rob Herring Reported-by: Laurent Pinchart Signed-off-by: Will Deacon Signed-off-by: Olof Johansson --- drivers/of/platform.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 5b33c6a21807..3f61d668ded7 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -526,6 +526,7 @@ static int of_platform_device_destroy(struct device *dev, void *data) amba_device_unregister(to_amba_device(dev)); #endif + of_dma_deconfigure(dev); of_node_clear_flag(dev->of_node, OF_POPULATED); of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); return 0; -- cgit v1.2.3 From 97f39af4b315d7c65504c8e033e3cf8527dd65cb Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Thu, 29 Jan 2015 09:04:26 -0800 Subject: soc: ti: knav_qmss_queue: export API calls for use by user driver Currently only few of the API calls are exported. This creates problem when the knav* modules are built as modules and another user module such as netcp_core try to use these API calls and they are also built as module. This patch export these APIs to address the issue. This is needed to support allmodconfig for ARM Signed-off-by: Murali Karicheri Signed-off-by: Santosh Shilimkar --- drivers/soc/ti/knav_qmss_queue.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 8e6a95d91d33..6d8646db52cc 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -626,6 +626,7 @@ int knav_queue_push(void *qhandle, dma_addr_t dma, atomic_inc(&qh->stats.pushes); return 0; } +EXPORT_SYMBOL_GPL(knav_queue_push); /** * knav_queue_pop() - pop data (or descriptor) from the head of a queue @@ -663,6 +664,7 @@ dma_addr_t knav_queue_pop(void *qhandle, unsigned *size) atomic_inc(&qh->stats.pops); return dma; } +EXPORT_SYMBOL_GPL(knav_queue_pop); /* carve out descriptors and push into queue */ static void kdesc_fill_pool(struct knav_pool *pool) @@ -717,12 +719,14 @@ dma_addr_t knav_pool_desc_virt_to_dma(void *ph, void *virt) struct knav_pool *pool = ph; return pool->region->dma_start + (virt - pool->region->virt_start); } +EXPORT_SYMBOL_GPL(knav_pool_desc_virt_to_dma); void *knav_pool_desc_dma_to_virt(void *ph, dma_addr_t dma) { struct knav_pool *pool = ph; return pool->region->virt_start + (dma - pool->region->dma_start); } +EXPORT_SYMBOL_GPL(knav_pool_desc_dma_to_virt); /** * knav_pool_create() - Create a pool of descriptors @@ -878,6 +882,7 @@ void *knav_pool_desc_get(void *ph) data = knav_pool_desc_dma_to_virt(pool, dma); return data; } +EXPORT_SYMBOL_GPL(knav_pool_desc_get); /** * knav_pool_desc_put() - return a descriptor to the pool @@ -890,6 +895,7 @@ void knav_pool_desc_put(void *ph, void *desc) dma = knav_pool_desc_virt_to_dma(pool, desc); knav_queue_push(pool->queue, dma, pool->region->desc_size, 0); } +EXPORT_SYMBOL_GPL(knav_pool_desc_put); /** * knav_pool_desc_map() - Map descriptor for DMA transfer @@ -916,6 +922,7 @@ int knav_pool_desc_map(void *ph, void *desc, unsigned size, return 0; } +EXPORT_SYMBOL_GPL(knav_pool_desc_map); /** * knav_pool_desc_unmap() - Unmap descriptor after DMA transfer @@ -938,6 +945,7 @@ void *knav_pool_desc_unmap(void *ph, dma_addr_t dma, unsigned dma_sz) prefetch(desc); return desc; } +EXPORT_SYMBOL_GPL(knav_pool_desc_unmap); /** * knav_pool_count() - Get the number of descriptors in pool. @@ -949,6 +957,7 @@ int knav_pool_count(void *ph) struct knav_pool *pool = ph; return knav_queue_get_count(pool->queue); } +EXPORT_SYMBOL_GPL(knav_pool_count); static void knav_queue_setup_region(struct knav_device *kdev, struct knav_region *region) -- cgit v1.2.3 From 98d67219da3c23c1e5c27986edcce356b68761f2 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 13 Jan 2015 19:28:05 +0100 Subject: pcmcia: at91_cf: depend on !ARCH_MULTIPLATFORM Until the driver is corrected to stop including mach/at91_ramc.h and using mach/io.h, it won't compile in a ARCH_MULTIPLATFORM configuration. Suggested-by: Arnd Bergmann Signed-off-by: Alexandre Belloni Acked-by: Nicolas Ferre Signed-off-by: Olof Johansson --- drivers/pcmcia/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 910e90bf16c6..45f67c63d385 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -276,6 +276,7 @@ config BFIN_CFPCMCIA config AT91_CF tristate "AT91 CompactFlash Controller" depends on PCMCIA && ARCH_AT91 + depends on !ARCH_MULTIPLATFORM help Say Y here to support the CompactFlash controller on AT91 chips. Or choose M to compile the driver as a module named "at91_cf". -- cgit v1.2.3 From df351f1efeb386481c1f61aa94cbe8a3a7c0c1d0 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Thu, 29 Jan 2015 13:23:51 -0800 Subject: soc: ti: knav_qmss_queue: makefile tweak to build as dynamic module Currently configuring qmss and dma as dynamic module creates three .ko files. knav_qmss_acc.ko and knav_qmss_queue.ko both can't be insmod because of circular dependency. So combine these two into one module by changing the makefile. Signed-off-by: Murali Karicheri Signed-off-by: Santosh Shilimkar --- drivers/soc/ti/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile index 6bed611e1934..135bdad7a6de 100644 --- a/drivers/soc/ti/Makefile +++ b/drivers/soc/ti/Makefile @@ -1,5 +1,6 @@ # # TI Keystone SOC drivers # -obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss_queue.o knav_qmss_acc.o +obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o +knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o -- cgit v1.2.3 From a68094c67bf1fa86630bd011d2d212899c41e1bc Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Thu, 29 Jan 2015 13:23:51 -0800 Subject: soc: ti: knav_qmss_queue: change knav_range_setup_acc_irq to static knav_range_setup_acc_irq() is used only within the file and should be defined as static. Signed-off-by: Murali Karicheri Signed-off-by: Santosh Shilimkar --- drivers/soc/ti/knav_qmss_acc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/ti/knav_qmss_acc.c b/drivers/soc/ti/knav_qmss_acc.c index 6fbfde6e748f..ef6f69db0bd0 100644 --- a/drivers/soc/ti/knav_qmss_acc.c +++ b/drivers/soc/ti/knav_qmss_acc.c @@ -209,7 +209,7 @@ static irqreturn_t knav_acc_int_handler(int irq, void *_instdata) return IRQ_HANDLED; } -int knav_range_setup_acc_irq(struct knav_range_info *range, +static int knav_range_setup_acc_irq(struct knav_range_info *range, int queue, bool enabled) { struct knav_device *kdev = range->kdev; -- cgit v1.2.3 From 712eddf70225ab5ae65e946e22d2dfe6b93e8dd1 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Sat, 24 Jan 2015 14:05:50 +0900 Subject: cpuidle: exynos: add coupled cpuidle support for exynos4210 The following patch adds coupled cpuidle support for Exynos4210 to an existing cpuidle-exynos driver. As a result it enables AFTR mode to be used by default on Exynos4210 without the need to hot unplug CPU1 first. The patch is heavily based on earlier cpuidle-exynos4210 driver from Daniel Lezcano: http://www.spinics.net/lists/linux-samsung-soc/msg28134.html Changes from Daniel's code include: - porting code to current kernels - fixing it to work on my setup (by using S5P_INFORM register instead of S5P_VA_SYSRAM one on Revison 1.1 and retrying poking CPU1 out of the BOOT ROM if necessary) - fixing rare lockup caused by waiting for CPU1 to get stuck in the BOOT ROM (CPU hotplug code in arch/arm/mach-exynos/platsmp.c doesn't require this and works fine) - moving Exynos specific code to arch/arm/mach-exynos/pm.c - using cpu_boot_reg_base() helper instead of BOOT_VECTOR macro - using exynos_cpu_*() helpers instead of accessing registers directly - using arch_send_wakeup_ipi_mask() instead of dsb_sev() (this matches CPU hotplug code in arch/arm/mach-exynos/platsmp.c) - integrating separate exynos4210-cpuidle driver into existing exynos-cpuidle one Cc: Colin Cross Cc: Kukjin Kim Cc: Krzysztof Kozlowski Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Daniel Lezcano Acked-by: Kyungmin Park Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/common.h | 4 + arch/arm/mach-exynos/exynos.c | 4 + arch/arm/mach-exynos/platsmp.c | 2 +- arch/arm/mach-exynos/pm.c | 122 +++++++++++++++++++++++++++ drivers/cpuidle/Kconfig.arm | 1 + drivers/cpuidle/cpuidle-exynos.c | 76 +++++++++++++++-- include/linux/platform_data/cpuidle-exynos.h | 20 +++++ 7 files changed, 223 insertions(+), 6 deletions(-) create mode 100644 include/linux/platform_data/cpuidle-exynos.h (limited to 'drivers') diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index 865f878063cc..f70eca7ee705 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -13,6 +13,7 @@ #define __ARCH_ARM_MACH_EXYNOS_COMMON_H #include +#include #define EXYNOS3250_SOC_ID 0xE3472000 #define EXYNOS3_SOC_MASK 0xFFFFF000 @@ -150,8 +151,11 @@ extern void exynos_pm_central_suspend(void); extern int exynos_pm_central_resume(void); extern void exynos_enter_aftr(void); +extern struct cpuidle_exynos_data cpuidle_coupled_exynos_data; + extern void s5p_init_cpu(void __iomem *cpuid_addr); extern unsigned int samsung_rev(void); +extern void __iomem *cpu_boot_reg_base(void); static inline void pmu_raw_writel(u32 val, u32 offset) { diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index c13d0837fa8c..509f2e5a2d70 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -246,6 +246,10 @@ static void __init exynos_dt_machine_init(void) if (!IS_ENABLED(CONFIG_SMP)) exynos_sysram_init(); +#ifdef CONFIG_ARM_EXYNOS_CPUIDLE + if (of_machine_is_compatible("samsung,exynos4210")) + exynos_cpuidle.dev.platform_data = &cpuidle_coupled_exynos_data; +#endif if (of_machine_is_compatible("samsung,exynos4210") || of_machine_is_compatible("samsung,exynos4212") || (of_machine_is_compatible("samsung,exynos4412") && diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 7a1ebfeeeeb8..3f32c47a6d74 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -194,7 +194,7 @@ int exynos_cluster_power_state(int cluster) S5P_CORE_LOCAL_PWR_EN); } -static inline void __iomem *cpu_boot_reg_base(void) +void __iomem *cpu_boot_reg_base(void) { if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1) return pmu_base_addr + S5P_INFORM5; diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 159eb4c9779e..1f9cbd434c60 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -179,3 +179,125 @@ void exynos_enter_aftr(void) cpu_pm_exit(); } + +static atomic_t cpu1_wakeup = ATOMIC_INIT(0); + +static int exynos_cpu0_enter_aftr(void) +{ + int ret = -1; + + /* + * If the other cpu is powered on, we have to power it off, because + * the AFTR state won't work otherwise + */ + if (cpu_online(1)) { + /* + * We reach a sync point with the coupled idle state, we know + * the other cpu will power down itself or will abort the + * sequence, let's wait for one of these to happen + */ + while (exynos_cpu_power_state(1)) { + /* + * The other cpu may skip idle and boot back + * up again + */ + if (atomic_read(&cpu1_wakeup)) + goto abort; + + /* + * The other cpu may bounce through idle and + * boot back up again, getting stuck in the + * boot rom code + */ + if (__raw_readl(cpu_boot_reg_base()) == 0) + goto abort; + + cpu_relax(); + } + } + + exynos_enter_aftr(); + ret = 0; + +abort: + if (cpu_online(1)) { + /* + * Set the boot vector to something non-zero + */ + __raw_writel(virt_to_phys(exynos_cpu_resume), + cpu_boot_reg_base()); + dsb(); + + /* + * Turn on cpu1 and wait for it to be on + */ + exynos_cpu_power_up(1); + while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN) + cpu_relax(); + + while (!atomic_read(&cpu1_wakeup)) { + /* + * Poke cpu1 out of the boot rom + */ + __raw_writel(virt_to_phys(exynos_cpu_resume), + cpu_boot_reg_base()); + + arch_send_wakeup_ipi_mask(cpumask_of(1)); + } + } + + return ret; +} + +static int exynos_wfi_finisher(unsigned long flags) +{ + cpu_do_idle(); + + return -1; +} + +static int exynos_cpu1_powerdown(void) +{ + int ret = -1; + + /* + * Idle sequence for cpu1 + */ + if (cpu_pm_enter()) + goto cpu1_aborted; + + /* + * Turn off cpu 1 + */ + exynos_cpu_power_down(1); + + ret = cpu_suspend(0, exynos_wfi_finisher); + + cpu_pm_exit(); + +cpu1_aborted: + dsb(); + /* + * Notify cpu 0 that cpu 1 is awake + */ + atomic_set(&cpu1_wakeup, 1); + + return ret; +} + +static void exynos_pre_enter_aftr(void) +{ + __raw_writel(virt_to_phys(exynos_cpu_resume), cpu_boot_reg_base()); +} + +static void exynos_post_enter_aftr(void) +{ + atomic_set(&cpu1_wakeup, 0); +} + +struct cpuidle_exynos_data cpuidle_coupled_exynos_data = { + .cpu0_enter_aftr = exynos_cpu0_enter_aftr, + .cpu1_powerdown = exynos_cpu1_powerdown, + .pre_enter_aftr = exynos_pre_enter_aftr, + .post_enter_aftr = exynos_post_enter_aftr, +}; diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 8c16ab20fb15..8e07c9419153 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -55,6 +55,7 @@ config ARM_AT91_CPUIDLE config ARM_EXYNOS_CPUIDLE bool "Cpu Idle Driver for the Exynos processors" depends on ARCH_EXYNOS + select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP help Select this to enable cpuidle for Exynos processors diff --git a/drivers/cpuidle/cpuidle-exynos.c b/drivers/cpuidle/cpuidle-exynos.c index 4003a3160865..26f5f29fdb03 100644 --- a/drivers/cpuidle/cpuidle-exynos.c +++ b/drivers/cpuidle/cpuidle-exynos.c @@ -1,8 +1,11 @@ -/* linux/arch/arm/mach-exynos/cpuidle.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. +/* + * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. * http://www.samsung.com * + * Coupled cpuidle support based on the work of: + * Colin Cross + * Daniel Lezcano + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -13,13 +16,49 @@ #include #include #include +#include +#include #include #include #include +static atomic_t exynos_idle_barrier; + +static struct cpuidle_exynos_data *exynos_cpuidle_pdata; static void (*exynos_enter_aftr)(void); +static int exynos_enter_coupled_lowpower(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + int ret; + + exynos_cpuidle_pdata->pre_enter_aftr(); + + /* + * Waiting all cpus to reach this point at the same moment + */ + cpuidle_coupled_parallel_barrier(dev, &exynos_idle_barrier); + + /* + * Both cpus will reach this point at the same time + */ + ret = dev->cpu ? exynos_cpuidle_pdata->cpu1_powerdown() + : exynos_cpuidle_pdata->cpu0_enter_aftr(); + if (ret) + index = ret; + + /* + * Waiting all cpus to finish the power sequence before going further + */ + cpuidle_coupled_parallel_barrier(dev, &exynos_idle_barrier); + + exynos_cpuidle_pdata->post_enter_aftr(); + + return index; +} + static int exynos_enter_lowpower(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) @@ -55,13 +94,40 @@ static struct cpuidle_driver exynos_idle_driver = { .safe_state_index = 0, }; +static struct cpuidle_driver exynos_coupled_idle_driver = { + .name = "exynos_coupled_idle", + .owner = THIS_MODULE, + .states = { + [0] = ARM_CPUIDLE_WFI_STATE, + [1] = { + .enter = exynos_enter_coupled_lowpower, + .exit_latency = 5000, + .target_residency = 10000, + .flags = CPUIDLE_FLAG_COUPLED | + CPUIDLE_FLAG_TIMER_STOP, + .name = "C1", + .desc = "ARM power down", + }, + }, + .state_count = 2, + .safe_state_index = 0, +}; + static int exynos_cpuidle_probe(struct platform_device *pdev) { int ret; - exynos_enter_aftr = (void *)(pdev->dev.platform_data); + if (of_machine_is_compatible("samsung,exynos4210")) { + exynos_cpuidle_pdata = pdev->dev.platform_data; + + ret = cpuidle_register(&exynos_coupled_idle_driver, + cpu_possible_mask); + } else { + exynos_enter_aftr = (void *)(pdev->dev.platform_data); + + ret = cpuidle_register(&exynos_idle_driver, NULL); + } - ret = cpuidle_register(&exynos_idle_driver, NULL); if (ret) { dev_err(&pdev->dev, "failed to register cpuidle driver\n"); return ret; diff --git a/include/linux/platform_data/cpuidle-exynos.h b/include/linux/platform_data/cpuidle-exynos.h new file mode 100644 index 000000000000..bfa40e4c5d5f --- /dev/null +++ b/include/linux/platform_data/cpuidle-exynos.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __CPUIDLE_EXYNOS_H +#define __CPUIDLE_EXYNOS_H + +struct cpuidle_exynos_data { + int (*cpu0_enter_aftr)(void); + int (*cpu1_powerdown)(void); + void (*pre_enter_aftr)(void); + void (*post_enter_aftr)(void); +}; + +#endif -- cgit v1.2.3