From a636ee7fb35b731ba2b331f6294e809bb6be09c8 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 9 Mar 2010 06:57:53 +0000 Subject: driver core: Early dev_name() support. Presently early platform devices suffer from the fact they are unable to use dev_xxx() calls early on due to dev_name() and others being unavailable at the time ->probe() is called. This implements early init_name construction from the matched name/id pair following the semantics of the late device/driver match. As a result, matched IDs (inclusive of requested ones) are preserved when the handoff from the early platform code happens at kobject initialization time. Since we still require kmalloc slabs to be available at this point, using kstrdup() for establishing the init_name works fine. This subsequently needs to be tested from dev_name() prior to the init_name being cleared by the driver core. We don't kfree() since others will already have a handle on the string long before the kobject initialization takes place. This is also needed to permit drivers to use the clock framework early, without having to manually construct their own device IDs from the match id/name pair locally (needed by the early console and timer code on sh and arm). Signed-off-by: Paul Mundt Acked-by: Greg Kroah-Hartman --- drivers/base/platform.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 1ba9d617d241..d2d4926c5c4c 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1239,6 +1239,25 @@ static int __init early_platform_driver_probe_id(char *class_str, } if (match) { + /* + * Set up a sensible init_name to enable + * dev_name() and others to be used before the + * rest of the driver core is initialized. + */ + if (!match->dev.init_name) { + char buf[32]; + + if (match->id != -1) + snprintf(buf, sizeof(buf), "%s.%d", + match->name, match->id); + else + snprintf(buf, sizeof(buf), "%s", + match->name); + + match->dev.init_name = kstrdup(buf, GFP_KERNEL); + if (!match->dev.init_name) + return -ENOMEM; + } if (epdrv->pdrv->probe(match)) pr_warning("%s: unable to probe %s early.\n", class_str, match->name); -- cgit v1.2.3-59-g8ed1b From 214a607a4f5bf5a14dab0304ba350cdaf3916795 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 10 Mar 2010 16:26:25 +0900 Subject: clocksource: Use dev_name() universally across the SH drivers. There is no need to copy in the name from the sh timer config now that dev_name() is available early. We prefer the dev_name() variant for consistent naming. Signed-off-by: Paul Mundt --- drivers/clocksource/sh_cmt.c | 32 ++++++++++++++------------------ drivers/clocksource/sh_mtu2.c | 24 +++++++++++------------- drivers/clocksource/sh_tmu.c | 28 ++++++++++++---------------- 3 files changed, 37 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 578595c4425d..a1d381171388 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -149,13 +149,12 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; int ret; /* enable clock */ ret = clk_enable(p->clk); if (ret) { - pr_err("sh_cmt: cannot enable clock \"%s\"\n", cfg->clk); + dev_err(&p->pdev->dev, "cannot enable clock\n"); return ret; } @@ -278,7 +277,7 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p, delay = 1; if (!delay) - pr_warning("sh_cmt: too long delay\n"); + dev_warn(&p->pdev->dev, "too long delay\n"); } while (delay); } @@ -288,7 +287,7 @@ static void sh_cmt_set_next(struct sh_cmt_priv *p, unsigned long delta) unsigned long flags; if (delta > p->max_match_value) - pr_warning("sh_cmt: delta out of range\n"); + dev_warn(&p->pdev->dev, "delta out of range\n"); spin_lock_irqsave(&p->lock, flags); p->next_match_value = delta; @@ -450,7 +449,7 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, cs->resume = sh_cmt_clocksource_resume; cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; - pr_info("sh_cmt: %s used as clock source\n", cs->name); + dev_info(&p->pdev->dev, "used as clock source\n"); clocksource_register(cs); return 0; } @@ -496,13 +495,11 @@ static void sh_cmt_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - pr_info("sh_cmt: %s used for periodic clock events\n", - ced->name); + dev_info(&p->pdev->dev, "used for periodic clock events\n"); sh_cmt_clock_event_start(p, 1); break; case CLOCK_EVT_MODE_ONESHOT: - pr_info("sh_cmt: %s used for oneshot clock events\n", - ced->name); + dev_info(&p->pdev->dev, "used for oneshot clock events\n"); sh_cmt_clock_event_start(p, 0); break; case CLOCK_EVT_MODE_SHUTDOWN: @@ -543,7 +540,7 @@ static void sh_cmt_register_clockevent(struct sh_cmt_priv *p, ced->set_next_event = sh_cmt_clock_event_next; ced->set_mode = sh_cmt_clock_event_mode; - pr_info("sh_cmt: %s used for clock events\n", ced->name); + dev_info(&p->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); } @@ -600,12 +597,12 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) /* map memory, let mapbase point to our channel */ p->mapbase = ioremap_nocache(res->start, resource_size(res)); if (p->mapbase == NULL) { - pr_err("sh_cmt: failed to remap I/O memory\n"); + dev_err(&p->pdev->dev, "failed to remap I/O memory\n"); goto err0; } /* request irq using setup_irq() (too early for request_irq()) */ - p->irqaction.name = cfg->name; + p->irqaction.name = dev_name(&p->pdev->dev); p->irqaction.handler = sh_cmt_interrupt; p->irqaction.dev_id = p; p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; @@ -613,7 +610,7 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, cfg->clk); if (IS_ERR(p->clk)) { - pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk); + dev_err(&p->pdev->dev, "cannot get clock\n"); ret = PTR_ERR(p->clk); goto err1; } @@ -628,17 +625,17 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) p->clear_bits = ~0xc000; } - ret = sh_cmt_register(p, cfg->name, + ret = sh_cmt_register(p, (char *)dev_name(&p->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); if (ret) { - pr_err("sh_cmt: registration failed\n"); + dev_err(&p->pdev->dev, "registration failed\n"); goto err1; } ret = setup_irq(irq, &p->irqaction); if (ret) { - pr_err("sh_cmt: failed to request irq %d\n", irq); + dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); goto err1; } @@ -653,11 +650,10 @@ err0: static int __devinit sh_cmt_probe(struct platform_device *pdev) { struct sh_cmt_priv *p = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (p) { - pr_info("sh_cmt: %s kept as earlytimer\n", cfg->name); + dev_info(&pdev->dev, "kept as earlytimer\n"); return 0; } diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 4c8a759e60cd..8ce355572d16 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -118,13 +118,12 @@ static void sh_mtu2_start_stop_ch(struct sh_mtu2_priv *p, int start) static int sh_mtu2_enable(struct sh_mtu2_priv *p) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; int ret; /* enable clock */ ret = clk_enable(p->clk); if (ret) { - pr_err("sh_mtu2: cannot enable clock \"%s\"\n", cfg->clk); + dev_err(&p->pdev->dev, "cannot enable clock\n"); return ret; } @@ -193,8 +192,7 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - pr_info("sh_mtu2: %s used for periodic clock events\n", - ced->name); + dev_info(&p->pdev->dev, "used for periodic clock events\n"); sh_mtu2_enable(p); break; case CLOCK_EVT_MODE_UNUSED: @@ -221,13 +219,13 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p, ced->cpumask = cpumask_of(0); ced->set_mode = sh_mtu2_clock_event_mode; - pr_info("sh_mtu2: %s used for clock events\n", ced->name); + dev_info(&p->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); ret = setup_irq(p->irqaction.irq, &p->irqaction); if (ret) { - pr_err("sh_mtu2: failed to request irq %d\n", - p->irqaction.irq); + dev_err(&p->pdev->dev, "failed to request irq %d\n", + p->irqaction.irq); return; } } @@ -273,12 +271,12 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) /* map memory, let mapbase point to our channel */ p->mapbase = ioremap_nocache(res->start, resource_size(res)); if (p->mapbase == NULL) { - pr_err("sh_mtu2: failed to remap I/O memory\n"); + dev_err(&p->pdev->dev, "failed to remap I/O memory\n"); goto err0; } /* setup data for setup_irq() (too early for request_irq()) */ - p->irqaction.name = cfg->name; + p->irqaction.name = dev_name(&p->pdev->dev); p->irqaction.handler = sh_mtu2_interrupt; p->irqaction.dev_id = p; p->irqaction.irq = irq; @@ -287,12 +285,13 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, cfg->clk); if (IS_ERR(p->clk)) { - pr_err("sh_mtu2: cannot get clock \"%s\"\n", cfg->clk); + dev_err(&p->pdev->dev, "cannot get clock\n"); ret = PTR_ERR(p->clk); goto err1; } - return sh_mtu2_register(p, cfg->name, cfg->clockevent_rating); + return sh_mtu2_register(p, (char *)dev_name(&p->pdev->dev), + cfg->clockevent_rating); err1: iounmap(p->mapbase); err0: @@ -302,11 +301,10 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) static int __devinit sh_mtu2_probe(struct platform_device *pdev) { struct sh_mtu2_priv *p = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (p) { - pr_info("sh_mtu2: %s kept as earlytimer\n", cfg->name); + dev_info(&pdev->dev, "kept as earlytimer\n"); return 0; } diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 961f5b5ef6a3..34239453ebaf 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -106,13 +106,12 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_priv *p, int start) static int sh_tmu_enable(struct sh_tmu_priv *p) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; int ret; /* enable clock */ ret = clk_enable(p->clk); if (ret) { - pr_err("sh_tmu: cannot enable clock \"%s\"\n", cfg->clk); + dev_err(&p->pdev->dev, "cannot enable clock\n"); return ret; } @@ -228,7 +227,7 @@ static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, cs->disable = sh_tmu_clocksource_disable; cs->mask = CLOCKSOURCE_MASK(32); cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; - pr_info("sh_tmu: %s used as clock source\n", cs->name); + dev_info(&p->pdev->dev, "used as clock source\n"); clocksource_register(cs); return 0; } @@ -276,13 +275,11 @@ static void sh_tmu_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - pr_info("sh_tmu: %s used for periodic clock events\n", - ced->name); + dev_info(&p->pdev->dev, "used for periodic clock events\n"); sh_tmu_clock_event_start(p, 1); break; case CLOCK_EVT_MODE_ONESHOT: - pr_info("sh_tmu: %s used for oneshot clock events\n", - ced->name); + dev_info(&p->pdev->dev, "used for oneshot clock events\n"); sh_tmu_clock_event_start(p, 0); break; case CLOCK_EVT_MODE_UNUSED: @@ -323,13 +320,13 @@ static void sh_tmu_register_clockevent(struct sh_tmu_priv *p, ced->set_next_event = sh_tmu_clock_event_next; ced->set_mode = sh_tmu_clock_event_mode; - pr_info("sh_tmu: %s used for clock events\n", ced->name); + dev_info(&p->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); ret = setup_irq(p->irqaction.irq, &p->irqaction); if (ret) { - pr_err("sh_tmu: failed to request irq %d\n", - p->irqaction.irq); + dev_err(&p->pdev->dev, "failed to request irq %d\n", + p->irqaction.irq); return; } } @@ -378,12 +375,12 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) /* map memory, let mapbase point to our channel */ p->mapbase = ioremap_nocache(res->start, resource_size(res)); if (p->mapbase == NULL) { - pr_err("sh_tmu: failed to remap I/O memory\n"); + dev_err(&p->pdev->dev, "failed to remap I/O memory\n"); goto err0; } /* setup data for setup_irq() (too early for request_irq()) */ - p->irqaction.name = cfg->name; + p->irqaction.name = dev_name(&p->pdev->dev); p->irqaction.handler = sh_tmu_interrupt; p->irqaction.dev_id = p; p->irqaction.irq = irq; @@ -392,12 +389,12 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, cfg->clk); if (IS_ERR(p->clk)) { - pr_err("sh_tmu: cannot get clock \"%s\"\n", cfg->clk); + dev_err(&p->pdev->dev, "cannot get clock\n"); ret = PTR_ERR(p->clk); goto err1; } - return sh_tmu_register(p, cfg->name, + return sh_tmu_register(p, (char *)dev_name(&p->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); err1: @@ -409,11 +406,10 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) static int __devinit sh_tmu_probe(struct platform_device *pdev) { struct sh_tmu_priv *p = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (p) { - pr_info("sh_tmu: %s kept as earlytimer\n", cfg->name); + dev_info(&pdev->dev, "kept as earlytimer\n"); return 0; } -- cgit v1.2.3-59-g8ed1b From c7ed1ab3f7b2fe0dedebf34cbf40bf12cb5ae48b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 10 Mar 2010 18:35:14 +0900 Subject: serial: sh-sci: clkdev updates for MSTP gating. This fixes up some of the I/D/F clock ambiguity in the sh-sci driver. The interface clock in most cases just wraps back to the peripheral clock, while the function clock wraps in to the MSTP bits. As the logic was somewhat inverted, this cleans that up, and also enables all CPUs with SCI MSTP bits to match function clocks through clkdev lookup. As a result, this gets rid of the clk string abuse on the sh side, and the clock string will be killed off once the ARM code has had a chance to sync up. This also enables MSTP gating on CPUs like 7786 which had never wired it up before. Impacted CPUs are primarily all SH-Mobiles, SH7785, and SH7786. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh4a/clock-sh7343.c | 8 ++--- arch/sh/kernel/cpu/sh4a/clock-sh7366.c | 6 ++-- arch/sh/kernel/cpu/sh4a/clock-sh7722.c | 6 ++-- arch/sh/kernel/cpu/sh4a/clock-sh7723.c | 12 +++---- arch/sh/kernel/cpu/sh4a/clock-sh7724.c | 12 +++---- arch/sh/kernel/cpu/sh4a/clock-sh7785.c | 12 +++---- arch/sh/kernel/cpu/sh4a/clock-sh7786.c | 12 +++---- arch/sh/kernel/cpu/sh4a/setup-sh7343.c | 4 --- arch/sh/kernel/cpu/sh4a/setup-sh7366.c | 1 - arch/sh/kernel/cpu/sh4a/setup-sh7722.c | 3 -- arch/sh/kernel/cpu/sh4a/setup-sh7723.c | 6 ---- arch/sh/kernel/cpu/sh4a/setup-sh7724.c | 6 ---- arch/sh/kernel/cpu/sh4a/setup-sh7785.c | 6 ---- drivers/serial/sh-sci.c | 57 ++++++++++++++++++++++------------ 14 files changed, 71 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c index 2c16df37eda6..c541301c7f2b 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c @@ -159,10 +159,10 @@ static struct clk mstp_clks[] = { MSTP("rwdt0", &r_clk, MSTPCR0, 13, 0), MSTP("mfi0", &div4_clks[DIV4_P], MSTPCR0, 11, 0), MSTP("flctl0", &div4_clks[DIV4_P], MSTPCR0, 10, 0), - MSTP("scif0", &div4_clks[DIV4_P], MSTPCR0, 7, 0), - MSTP("scif1", &div4_clks[DIV4_P], MSTPCR0, 6, 0), - MSTP("scif2", &div4_clks[DIV4_P], MSTPCR0, 5, 0), - MSTP("scif3", &div4_clks[DIV4_P], MSTPCR0, 4, 0), + SH_CLK_MSTP32("sci_fck", 0, &div4_clks[DIV4_P], MSTPCR0, 7, 0), + SH_CLK_MSTP32("sci_fck", 1, &div4_clks[DIV4_P], MSTPCR0, 6, 0), + SH_CLK_MSTP32("sci_fck", 2, &div4_clks[DIV4_P], MSTPCR0, 5, 0), + SH_CLK_MSTP32("sci_fck", 3, &div4_clks[DIV4_P], MSTPCR0, 4, 0), MSTP("sio0", &div4_clks[DIV4_P], MSTPCR0, 3, 0), MSTP("siof0", &div4_clks[DIV4_P], MSTPCR0, 2, 0), MSTP("siof1", &div4_clks[DIV4_P], MSTPCR0, 1, 0), diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7366.c b/arch/sh/kernel/cpu/sh4a/clock-sh7366.c index 91588d280cd8..e10c64fd8095 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7366.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7366.c @@ -163,9 +163,9 @@ static struct clk mstp_clks[] = { MSTP("rwdt0", &r_clk, MSTPCR0, 13, 0), MSTP("mfi0", &div4_clks[DIV4_P], MSTPCR0, 11, 0), MSTP("flctl0", &div4_clks[DIV4_P], MSTPCR0, 10, 0), - MSTP("scif0", &div4_clks[DIV4_P], MSTPCR0, 7, 0), - MSTP("scif1", &div4_clks[DIV4_P], MSTPCR0, 6, 0), - MSTP("scif2", &div4_clks[DIV4_P], MSTPCR0, 5, 0), + SH_CLK_MSTP32("sci_fck", 0, &div4_clks[DIV4_P], MSTPCR0, 7, 0), + SH_CLK_MSTP32("sci_fck", 1, &div4_clks[DIV4_P], MSTPCR0, 6, 0), + SH_CLK_MSTP32("sci_fck", 2, &div4_clks[DIV4_P], MSTPCR0, 5, 0), MSTP("msiof0", &div4_clks[DIV4_P], MSTPCR0, 2, 0), MSTP("sbr0", &div4_clks[DIV4_P], MSTPCR0, 1, 0), diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c index 15db6d521c5c..0732c8bab1e2 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c @@ -164,9 +164,9 @@ static struct clk mstp_clks[] = { SH_HWBLK_CLK("cmt0", -1, R_CLK, HWBLK_CMT, 0), SH_HWBLK_CLK("rwdt0", -1, R_CLK, HWBLK_RWDT, 0), SH_HWBLK_CLK("flctl0", -1, P_CLK, HWBLK_FLCTL, 0), - SH_HWBLK_CLK("scif0", -1, P_CLK, HWBLK_SCIF0, 0), - SH_HWBLK_CLK("scif1", -1, P_CLK, HWBLK_SCIF1, 0), - SH_HWBLK_CLK("scif2", -1, P_CLK, HWBLK_SCIF2, 0), + SH_HWBLK_CLK("sci_fck", 0, P_CLK, HWBLK_SCIF0, 0), + SH_HWBLK_CLK("sci_fck", 1, P_CLK, HWBLK_SCIF1, 0), + SH_HWBLK_CLK("sci_fck", 2, P_CLK, HWBLK_SCIF2, 0), SH_HWBLK_CLK("i2c0", -1, P_CLK, HWBLK_IIC, 0), SH_HWBLK_CLK("rtc0", -1, R_CLK, HWBLK_RTC, 0), diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c index 50babe01fe44..4d4c123d02c4 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c @@ -177,12 +177,12 @@ static struct clk mstp_clks[] = { SH_HWBLK_CLK("dmac1", -1, B_CLK, HWBLK_DMAC1, 0), SH_HWBLK_CLK("tmu1", -1, P_CLK, HWBLK_TMU1, 0), SH_HWBLK_CLK("flctl0", -1, P_CLK, HWBLK_FLCTL, 0), - SH_HWBLK_CLK("scif0", -1, P_CLK, HWBLK_SCIF0, 0), - SH_HWBLK_CLK("scif1", -1, P_CLK, HWBLK_SCIF1, 0), - SH_HWBLK_CLK("scif2", -1, P_CLK, HWBLK_SCIF2, 0), - SH_HWBLK_CLK("scif3", -1, B_CLK, HWBLK_SCIF3, 0), - SH_HWBLK_CLK("scif4", -1, B_CLK, HWBLK_SCIF4, 0), - SH_HWBLK_CLK("scif5", -1, B_CLK, HWBLK_SCIF5, 0), + SH_HWBLK_CLK("sci_fck", 0, P_CLK, HWBLK_SCIF0, 0), + SH_HWBLK_CLK("sci_fck", 1, P_CLK, HWBLK_SCIF1, 0), + SH_HWBLK_CLK("sci_fck", 2, P_CLK, HWBLK_SCIF2, 0), + SH_HWBLK_CLK("sci_fck", 3, B_CLK, HWBLK_SCIF3, 0), + SH_HWBLK_CLK("sci_fck", 4, B_CLK, HWBLK_SCIF4, 0), + SH_HWBLK_CLK("sci_fck", 5, B_CLK, HWBLK_SCIF5, 0), SH_HWBLK_CLK("msiof0", -1, B_CLK, HWBLK_MSIOF0, 0), SH_HWBLK_CLK("msiof1", -1, B_CLK, HWBLK_MSIOF1, 0), SH_HWBLK_CLK("meram0", -1, SH_CLK, HWBLK_MERAM, 0), diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c index 6707061fbf54..bcf2b97db300 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c @@ -194,12 +194,12 @@ static struct clk mstp_clks[] = { SH_HWBLK_CLK("rwdt0", -1, R_CLK, HWBLK_RWDT, 0), SH_HWBLK_CLK("dmac1", -1, B_CLK, HWBLK_DMAC1, 0), SH_HWBLK_CLK("tmu1", -1, P_CLK, HWBLK_TMU1, 0), - SH_HWBLK_CLK("scif0", -1, P_CLK, HWBLK_SCIF0, 0), - SH_HWBLK_CLK("scif1", -1, P_CLK, HWBLK_SCIF1, 0), - SH_HWBLK_CLK("scif2", -1, P_CLK, HWBLK_SCIF2, 0), - SH_HWBLK_CLK("scif3", -1, B_CLK, HWBLK_SCIF3, 0), - SH_HWBLK_CLK("scif4", -1, B_CLK, HWBLK_SCIF4, 0), - SH_HWBLK_CLK("scif5", -1, B_CLK, HWBLK_SCIF5, 0), + SH_HWBLK_CLK("sci_fck", 0, P_CLK, HWBLK_SCIF0, 0), + SH_HWBLK_CLK("sci_fck", 1, P_CLK, HWBLK_SCIF1, 0), + SH_HWBLK_CLK("sci_fck", 2, P_CLK, HWBLK_SCIF2, 0), + SH_HWBLK_CLK("sci_fck", 3, B_CLK, HWBLK_SCIF3, 0), + SH_HWBLK_CLK("sci_fck", 4, B_CLK, HWBLK_SCIF4, 0), + SH_HWBLK_CLK("sci_fck", 5, B_CLK, HWBLK_SCIF5, 0), SH_HWBLK_CLK("msiof0", -1, B_CLK, HWBLK_MSIOF0, 0), SH_HWBLK_CLK("msiof1", -1, B_CLK, HWBLK_MSIOF1, 0), diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c index d997f0a25b10..6061c736c281 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c @@ -88,12 +88,12 @@ struct clk div4_clks[DIV4_NR] = { static struct clk mstp_clks[] = { /* MSTPCR0 */ - SH_CLK_MSTP32("scif_fck", 5, &div4_clks[DIV4_P], MSTPCR0, 29, 0), - SH_CLK_MSTP32("scif_fck", 4, &div4_clks[DIV4_P], MSTPCR0, 28, 0), - SH_CLK_MSTP32("scif_fck", 3, &div4_clks[DIV4_P], MSTPCR0, 27, 0), - SH_CLK_MSTP32("scif_fck", 2, &div4_clks[DIV4_P], MSTPCR0, 26, 0), - SH_CLK_MSTP32("scif_fck", 1, &div4_clks[DIV4_P], MSTPCR0, 25, 0), - SH_CLK_MSTP32("scif_fck", 0, &div4_clks[DIV4_P], MSTPCR0, 24, 0), + SH_CLK_MSTP32("sci_fck", 5, &div4_clks[DIV4_P], MSTPCR0, 29, 0), + SH_CLK_MSTP32("sci_fck", 4, &div4_clks[DIV4_P], MSTPCR0, 28, 0), + SH_CLK_MSTP32("sci_fck", 3, &div4_clks[DIV4_P], MSTPCR0, 27, 0), + SH_CLK_MSTP32("sci_fck", 2, &div4_clks[DIV4_P], MSTPCR0, 26, 0), + SH_CLK_MSTP32("sci_fck", 1, &div4_clks[DIV4_P], MSTPCR0, 25, 0), + SH_CLK_MSTP32("sci_fck", 0, &div4_clks[DIV4_P], MSTPCR0, 24, 0), SH_CLK_MSTP32("ssi_fck", 1, &div4_clks[DIV4_P], MSTPCR0, 21, 0), SH_CLK_MSTP32("ssi_fck", 0, &div4_clks[DIV4_P], MSTPCR0, 20, 0), SH_CLK_MSTP32("hac_fck", 1, &div4_clks[DIV4_P], MSTPCR0, 17, 0), diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7786.c b/arch/sh/kernel/cpu/sh4a/clock-sh7786.c index af69fd468703..85e8d57f4971 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7786.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7786.c @@ -87,12 +87,12 @@ struct clk div4_clks[DIV4_NR] = { static struct clk mstp_clks[] = { /* MSTPCR0 */ - SH_CLK_MSTP32("scif_fck", 5, &div4_clks[DIV4_P], MSTPCR0, 29, 0), - SH_CLK_MSTP32("scif_fck", 4, &div4_clks[DIV4_P], MSTPCR0, 28, 0), - SH_CLK_MSTP32("scif_fck", 3, &div4_clks[DIV4_P], MSTPCR0, 27, 0), - SH_CLK_MSTP32("scif_fck", 2, &div4_clks[DIV4_P], MSTPCR0, 26, 0), - SH_CLK_MSTP32("scif_fck", 1, &div4_clks[DIV4_P], MSTPCR0, 25, 0), - SH_CLK_MSTP32("scif_fck", 0, &div4_clks[DIV4_P], MSTPCR0, 24, 0), + SH_CLK_MSTP32("sci_fck", 5, &div4_clks[DIV4_P], MSTPCR0, 29, 0), + SH_CLK_MSTP32("sci_fck", 4, &div4_clks[DIV4_P], MSTPCR0, 28, 0), + SH_CLK_MSTP32("sci_fck", 3, &div4_clks[DIV4_P], MSTPCR0, 27, 0), + SH_CLK_MSTP32("sci_fck", 2, &div4_clks[DIV4_P], MSTPCR0, 26, 0), + SH_CLK_MSTP32("sci_fck", 1, &div4_clks[DIV4_P], MSTPCR0, 25, 0), + SH_CLK_MSTP32("sci_fck", 0, &div4_clks[DIV4_P], MSTPCR0, 24, 0), SH_CLK_MSTP32("ssi_fck", 3, &div4_clks[DIV4_P], MSTPCR0, 23, 0), SH_CLK_MSTP32("ssi_fck", 2, &div4_clks[DIV4_P], MSTPCR0, 22, 0), SH_CLK_MSTP32("ssi_fck", 1, &div4_clks[DIV4_P], MSTPCR0, 21, 0), diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c index fdbd7d07a69f..6f8394942fc5 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c @@ -21,7 +21,6 @@ static struct plat_sci_port scif0_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, - .clk = "scif0", }; static struct platform_device scif0_device = { @@ -37,7 +36,6 @@ static struct plat_sci_port scif1_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, - .clk = "scif1", }; static struct platform_device scif1_device = { @@ -53,7 +51,6 @@ static struct plat_sci_port scif2_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 82, 82, 82, 82 }, - .clk = "scif2", }; static struct platform_device scif2_device = { @@ -69,7 +66,6 @@ static struct plat_sci_port scif3_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 83, 83, 83, 83 }, - .clk = "scif3", }; static struct platform_device scif3_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c index d90fe0a65c2a..1d897ed256b0 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c @@ -23,7 +23,6 @@ static struct plat_sci_port scif0_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, - .clk = "scif0", }; static struct platform_device scif0_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c index 83ae9ad19874..847a27619af5 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c @@ -174,7 +174,6 @@ static struct plat_sci_port scif0_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, - .clk = "scif0", }; static struct platform_device scif0_device = { @@ -190,7 +189,6 @@ static struct plat_sci_port scif1_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, - .clk = "scif1", }; static struct platform_device scif1_device = { @@ -206,7 +204,6 @@ static struct plat_sci_port scif2_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 82, 82, 82, 82 }, - .clk = "scif2", }; static struct platform_device scif2_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c index 68446b3f06af..187462b51be0 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c @@ -26,7 +26,6 @@ static struct plat_sci_port scif0_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, - .clk = "scif0", }; static struct platform_device scif0_device = { @@ -42,7 +41,6 @@ static struct plat_sci_port scif1_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, - .clk = "scif1", }; static struct platform_device scif1_device = { @@ -58,7 +56,6 @@ static struct plat_sci_port scif2_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 82, 82, 82, 82 }, - .clk = "scif2", }; static struct platform_device scif2_device = { @@ -74,7 +71,6 @@ static struct plat_sci_port scif3_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIFA, .irqs = { 56, 56, 56, 56 }, - .clk = "scif3", }; static struct platform_device scif3_device = { @@ -90,7 +86,6 @@ static struct plat_sci_port scif4_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIFA, .irqs = { 88, 88, 88, 88 }, - .clk = "scif4", }; static struct platform_device scif4_device = { @@ -106,7 +101,6 @@ static struct plat_sci_port scif5_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIFA, .irqs = { 109, 109, 109, 109 }, - .clk = "scif5", }; static struct platform_device scif5_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c index abc98b0fef91..ee9789003193 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c @@ -213,7 +213,6 @@ static struct plat_sci_port scif0_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 80, 80, 80, 80 }, - .clk = "scif0", }; static struct platform_device scif0_device = { @@ -229,7 +228,6 @@ static struct plat_sci_port scif1_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 81, 81, 81, 81 }, - .clk = "scif1", }; static struct platform_device scif1_device = { @@ -245,7 +243,6 @@ static struct plat_sci_port scif2_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 82, 82, 82, 82 }, - .clk = "scif2", }; static struct platform_device scif2_device = { @@ -261,7 +258,6 @@ static struct plat_sci_port scif3_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIFA, .irqs = { 56, 56, 56, 56 }, - .clk = "scif3", }; static struct platform_device scif3_device = { @@ -277,7 +273,6 @@ static struct plat_sci_port scif4_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIFA, .irqs = { 88, 88, 88, 88 }, - .clk = "scif4", }; static struct platform_device scif4_device = { @@ -293,7 +288,6 @@ static struct plat_sci_port scif5_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIFA, .irqs = { 109, 109, 109, 109 }, - .clk = "scif5", }; static struct platform_device scif5_device = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c index 05e9308505cf..76afe571f3a2 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c @@ -25,7 +25,6 @@ static struct plat_sci_port scif0_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 40, 40, 40, 40 }, - .clk = "scif_fck", }; static struct platform_device scif0_device = { @@ -41,7 +40,6 @@ static struct plat_sci_port scif1_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 44, 44, 44, 44 }, - .clk = "scif_fck", }; static struct platform_device scif1_device = { @@ -57,7 +55,6 @@ static struct plat_sci_port scif2_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 60, 60, 60, 60 }, - .clk = "scif_fck", }; static struct platform_device scif2_device = { @@ -73,7 +70,6 @@ static struct plat_sci_port scif3_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 61, 61, 61, 61 }, - .clk = "scif_fck", }; static struct platform_device scif3_device = { @@ -89,7 +85,6 @@ static struct plat_sci_port scif4_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 62, 62, 62, 62 }, - .clk = "scif_fck", }; static struct platform_device scif4_device = { @@ -105,7 +100,6 @@ static struct plat_sci_port scif5_platform_data = { .flags = UPF_BOOT_AUTOCONF, .type = PORT_SCIF, .irqs = { 63, 63, 63, 63 }, - .clk = "scif_fck", }; static struct platform_device scif5_device = { diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 980f39449ee5..054a8427c739 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -83,8 +83,8 @@ struct sci_port { /* Interface clock */ struct clk *iclk; - /* Data clock */ - struct clk *dclk; + /* Function clock */ + struct clk *fclk; struct list_head node; struct dma_chan *chan_tx; @@ -803,7 +803,7 @@ static int sci_notifier(struct notifier_block *self, (phase == CPUFREQ_RESUMECHANGE)) { spin_lock_irqsave(&priv->lock, flags); list_for_each_entry(sci_port, &priv->ports, node) - sci_port->port.uartclk = clk_get_rate(sci_port->dclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); spin_unlock_irqrestore(&priv->lock, flags); } @@ -814,21 +814,17 @@ static void sci_clk_enable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - clk_enable(sci_port->dclk); - sci_port->port.uartclk = clk_get_rate(sci_port->dclk); - - if (sci_port->iclk) - clk_enable(sci_port->iclk); + clk_enable(sci_port->iclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); + clk_enable(sci_port->fclk); } static void sci_clk_disable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - if (sci_port->iclk) - clk_disable(sci_port->iclk); - - clk_disable(sci_port->dclk); + clk_disable(sci_port->fclk); + clk_disable(sci_port->iclk); } static int sci_request_irq(struct sci_port *port) @@ -1557,10 +1553,10 @@ static struct uart_ops sci_uart_ops = { #endif }; -static void __devinit sci_init_single(struct platform_device *dev, - struct sci_port *sci_port, - unsigned int index, - struct plat_sci_port *p) +static int __devinit sci_init_single(struct platform_device *dev, + struct sci_port *sci_port, + unsigned int index, + struct plat_sci_port *p) { struct uart_port *port = &sci_port->port; @@ -1581,8 +1577,23 @@ static void __devinit sci_init_single(struct platform_device *dev, } if (dev) { - sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL; - sci_port->dclk = clk_get(&dev->dev, "peripheral_clk"); + sci_port->iclk = clk_get(&dev->dev, "sci_ick"); + if (IS_ERR(sci_port->iclk)) { + sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); + if (IS_ERR(sci_port->iclk)) { + dev_err(&dev->dev, "can't get iclk\n"); + return PTR_ERR(sci_port->iclk); + } + } + + /* + * The function clock is optional, ignore it if we can't + * find it. + */ + sci_port->fclk = clk_get(&dev->dev, "sci_fck"); + if (IS_ERR(sci_port->fclk)) + sci_port->fclk = NULL; + sci_port->enable = sci_clk_enable; sci_port->disable = sci_clk_disable; port->dev = &dev->dev; @@ -1609,6 +1620,7 @@ static void __devinit sci_init_single(struct platform_device *dev, #endif memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); + return 0; } #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE @@ -1758,8 +1770,11 @@ static int sci_remove(struct platform_device *dev) cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(p, &priv->ports, node) + list_for_each_entry(p, &priv->ports, node) { uart_remove_one_port(&sci_uart_driver, &p->port); + clk_put(p->iclk); + clk_put(p->fclk); + } spin_unlock_irqrestore(&priv->lock, flags); kfree(priv); @@ -1785,7 +1800,9 @@ static int __devinit sci_probe_single(struct platform_device *dev, return 0; } - sci_init_single(dev, sciport, index, p); + ret = sci_init_single(dev, sciport, index, p); + if (ret) + return ret; ret = uart_add_one_port(&sci_uart_driver, &sciport->port); if (ret) -- cgit v1.2.3-59-g8ed1b From 01e9651a21bc0e6731da733593e4aaf4cf46b5e5 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 10 Mar 2010 09:31:01 +0000 Subject: sh: add INTC out of memory error handling Extend the INTC code to warn and return an error code in the case of memory allocation failure. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/sh/intc.c | 29 ++++++++++++++++++++++++++++- include/linux/sh_intc.h | 2 +- 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index c2750391fd34..d4aa4d1e8dce 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -789,13 +789,15 @@ static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) generic_handle_irq((unsigned int)get_irq_data(irq)); } -void __init register_intc_controller(struct intc_desc *desc) +int __init register_intc_controller(struct intc_desc *desc) { unsigned int i, k, smp; struct intc_hw_desc *hw = &desc->hw; struct intc_desc_int *d; d = kzalloc(sizeof(*d), GFP_NOWAIT); + if (!d) + goto err0; INIT_LIST_HEAD(&d->list); list_add(&d->list, &intc_list); @@ -806,8 +808,13 @@ void __init register_intc_controller(struct intc_desc *desc) d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); + if (!d->reg) + goto err1; + #ifdef CONFIG_SMP d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); + if (!d->smp) + goto err2; #endif k = 0; @@ -822,6 +829,8 @@ void __init register_intc_controller(struct intc_desc *desc) if (hw->prio_regs) { d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), GFP_NOWAIT); + if (!d->prio) + goto err3; for (i = 0; i < hw->nr_prio_regs; i++) { smp = IS_SMP(hw->prio_regs[i]); @@ -833,6 +842,8 @@ void __init register_intc_controller(struct intc_desc *desc) if (hw->sense_regs) { d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), GFP_NOWAIT); + if (!d->sense) + goto err4; for (i = 0; i < hw->nr_sense_regs; i++) k += save_reg(d, k, hw->sense_regs[i].reg, 0); @@ -912,6 +923,22 @@ void __init register_intc_controller(struct intc_desc *desc) /* enable bits matching force_enable after registering irqs */ if (desc->force_enable) intc_enable_disable_enum(desc, d, desc->force_enable, 1); + + return 0; + err4: + kfree(d->prio); + err3: +#ifdef CONFIG_SMP + kfree(d->smp); + err2: +#endif + kfree(d->reg); + err1: + kfree(d); + err0: + pr_err("unable to allocate INTC memory\n"); + + return -ENOMEM; } static int intc_suspend(struct sys_device *dev, pm_message_t state) diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h index 51d288d8ac88..df3777035936 100644 --- a/include/linux/sh_intc.h +++ b/include/linux/sh_intc.h @@ -92,7 +92,7 @@ struct intc_desc symbol __initdata = { \ prio_regs, sense_regs, ack_regs), \ } -void __init register_intc_controller(struct intc_desc *desc); +int __init register_intc_controller(struct intc_desc *desc); int intc_set_priority(unsigned int irq, unsigned int prio); int reserve_irq_vector(unsigned int irq); -- cgit v1.2.3-59-g8ed1b From dec710b77c2cf04bf512acada3c14a16f11708d9 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 19 Mar 2010 16:48:01 +0900 Subject: sh: INTC ioremap support Extend the INTC code with ioremap() support V2. Support INTC controllers that are not accessible through a 1:1 virt:phys window. Needed by SH-Mobile ARM INTCS. The INTC code behaves as usual if the io window resource is omitted. The slow phys->virt lookup only happens during setup. The fast path code operates on virtual addresses. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/sh/intc.c | 84 +++++++++++++++++++++++++++++++++++++++++++------ include/linux/sh_intc.h | 4 +++ 2 files changed, 78 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index d4aa4d1e8dce..a700dfec8dc3 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -43,6 +43,12 @@ struct intc_handle_int { unsigned long handle; }; +struct intc_window { + phys_addr_t phys; + void __iomem *virt; + unsigned long size; +}; + struct intc_desc_int { struct list_head list; struct sys_device sysdev; @@ -56,6 +62,8 @@ struct intc_desc_int { unsigned int nr_prio; struct intc_handle_int *sense; unsigned int nr_sense; + struct intc_window *window; + unsigned int nr_windows; struct irq_chip chip; }; @@ -420,11 +428,39 @@ static int intc_set_sense(unsigned int irq, unsigned int type) return 0; } +static unsigned long intc_phys_to_virt(struct intc_desc_int *d, + unsigned long address) +{ + struct intc_window *window; + int k; + + /* scan through physical windows and convert address */ + for (k = 0; k < d->nr_windows; k++) { + window = d->window + k; + + if (address < window->phys) + continue; + + if (address >= (window->phys + window->size)) + continue; + + address -= window->phys; + address += (unsigned long)window->virt; + + return address; + } + + /* no windows defined, register must be 1:1 mapped virt:phys */ + return address; +} + static unsigned int __init intc_get_reg(struct intc_desc_int *d, - unsigned long address) + unsigned long address) { unsigned int k; + address = intc_phys_to_virt(d, address); + for (k = 0; k < d->nr_reg; k++) { if (d->reg[k] == address) return k; @@ -774,6 +810,8 @@ static unsigned int __init save_reg(struct intc_desc_int *d, unsigned int smp) { if (value) { + value = intc_phys_to_virt(d, value); + d->reg[cnt] = value; #ifdef CONFIG_SMP d->smp[cnt] = smp; @@ -794,6 +832,7 @@ int __init register_intc_controller(struct intc_desc *desc) unsigned int i, k, smp; struct intc_hw_desc *hw = &desc->hw; struct intc_desc_int *d; + struct resource *res; d = kzalloc(sizeof(*d), GFP_NOWAIT); if (!d) @@ -802,6 +841,25 @@ int __init register_intc_controller(struct intc_desc *desc) INIT_LIST_HEAD(&d->list); list_add(&d->list, &intc_list); + if (desc->num_resources) { + d->nr_windows = desc->num_resources; + d->window = kzalloc(d->nr_windows * sizeof(*d->window), + GFP_NOWAIT); + if (!d->window) + goto err1; + + for (k = 0; k < d->nr_windows; k++) { + res = desc->resource + k; + WARN_ON(resource_type(res) != IORESOURCE_MEM); + d->window[k].phys = res->start; + d->window[k].size = resource_size(res); + d->window[k].virt = ioremap_nocache(res->start, + resource_size(res)); + if (!d->window[k].virt) + goto err2; + } + } + d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; @@ -809,12 +867,12 @@ int __init register_intc_controller(struct intc_desc *desc) d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); if (!d->reg) - goto err1; + goto err2; #ifdef CONFIG_SMP d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); if (!d->smp) - goto err2; + goto err3; #endif k = 0; @@ -830,7 +888,7 @@ int __init register_intc_controller(struct intc_desc *desc) d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), GFP_NOWAIT); if (!d->prio) - goto err3; + goto err4; for (i = 0; i < hw->nr_prio_regs; i++) { smp = IS_SMP(hw->prio_regs[i]); @@ -843,7 +901,7 @@ int __init register_intc_controller(struct intc_desc *desc) d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), GFP_NOWAIT); if (!d->sense) - goto err4; + goto err5; for (i = 0; i < hw->nr_sense_regs; i++) k += save_reg(d, k, hw->sense_regs[i].reg, 0); @@ -925,17 +983,23 @@ int __init register_intc_controller(struct intc_desc *desc) intc_enable_disable_enum(desc, d, desc->force_enable, 1); return 0; - err4: +err5: kfree(d->prio); - err3: +err4: #ifdef CONFIG_SMP kfree(d->smp); - err2: +err3: #endif kfree(d->reg); - err1: +err2: + for (k = 0; k < d->nr_windows; k++) + if (d->window[k].virt) + iounmap(d->window[k].virt); + + kfree(d->window); +err1: kfree(d); - err0: +err0: pr_err("unable to allocate INTC memory\n"); return -ENOMEM; diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h index df3777035936..01d8168c5a1b 100644 --- a/include/linux/sh_intc.h +++ b/include/linux/sh_intc.h @@ -1,6 +1,8 @@ #ifndef __SH_INTC_H #define __SH_INTC_H +#include + typedef unsigned char intc_enum; struct intc_vect { @@ -71,6 +73,8 @@ struct intc_hw_desc { struct intc_desc { char *name; + struct resource *resource; + unsigned int num_resources; intc_enum force_enable; intc_enum force_disable; struct intc_hw_desc hw; -- cgit v1.2.3-59-g8ed1b From 4bab9d426e6dbd9ea09330919a33d35d5faab400 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 19 Mar 2010 04:46:38 +0000 Subject: dmaengine: shdma: Remove sh_dmae_slave_chan_id enum This patch replaces the sh_dmae_slave_chan_id enum with an unsigned int. The purpose of this chainge is to make it possible to separate the slave id enums from the dmaengine header. The slave id enums varies with processor model, so in the future it makes sense to put these in the processor specific headers together with the pinmux enums. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/sh/include/asm/dmaengine.h | 6 +++--- arch/sh/include/asm/siu.h | 8 ++++---- drivers/dma/shdma.c | 8 ++++---- drivers/serial/sh-sci.c | 4 ++-- include/linux/serial_sci.h | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/arch/sh/include/asm/dmaengine.h b/arch/sh/include/asm/dmaengine.h index bf2f30cf0a27..568e991442b0 100644 --- a/arch/sh/include/asm/dmaengine.h +++ b/arch/sh/include/asm/dmaengine.h @@ -17,7 +17,7 @@ #define SH_DMAC_MAX_CHANNELS 6 -enum sh_dmae_slave_chan_id { +enum { SHDMA_SLAVE_SCIF0_TX, SHDMA_SLAVE_SCIF0_RX, SHDMA_SLAVE_SCIF1_TX, @@ -38,7 +38,7 @@ enum sh_dmae_slave_chan_id { }; struct sh_dmae_slave_config { - enum sh_dmae_slave_chan_id slave_id; + unsigned int slave_id; dma_addr_t addr; u32 chcr; char mid_rid; @@ -68,7 +68,7 @@ struct device; /* Used by slave DMA clients to request DMA to/from a specific peripheral */ struct sh_dmae_slave { - enum sh_dmae_slave_chan_id slave_id; /* Set by the platform */ + unsigned int slave_id; /* Set by the platform */ struct device *dma_dev; /* Set by the platform */ struct sh_dmae_slave_config *config; /* Set by the driver */ }; diff --git a/arch/sh/include/asm/siu.h b/arch/sh/include/asm/siu.h index f1b1e6944a5f..e8d4142baf59 100644 --- a/arch/sh/include/asm/siu.h +++ b/arch/sh/include/asm/siu.h @@ -17,10 +17,10 @@ struct device; struct siu_platform { struct device *dma_dev; - enum sh_dmae_slave_chan_id dma_slave_tx_a; - enum sh_dmae_slave_chan_id dma_slave_rx_a; - enum sh_dmae_slave_chan_id dma_slave_tx_b; - enum sh_dmae_slave_chan_id dma_slave_rx_b; + unsigned int dma_slave_tx_a; + unsigned int dma_slave_rx_a; + unsigned int dma_slave_tx_b; + unsigned int dma_slave_rx_b; }; #endif /* ASM_SIU_H */ diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 5d17e09cb625..e5588f4868ca 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -266,7 +266,7 @@ static struct sh_desc *sh_dmae_get_desc(struct sh_dmae_chan *sh_chan) } static struct sh_dmae_slave_config *sh_dmae_find_slave( - struct sh_dmae_chan *sh_chan, enum sh_dmae_slave_chan_id slave_id) + struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *param) { struct dma_device *dma_dev = sh_chan->common.device; struct sh_dmae_device *shdev = container_of(dma_dev, @@ -274,11 +274,11 @@ static struct sh_dmae_slave_config *sh_dmae_find_slave( struct sh_dmae_pdata *pdata = shdev->pdata; int i; - if ((unsigned)slave_id >= SHDMA_SLAVE_NUMBER) + if (param->slave_id >= SHDMA_SLAVE_NUMBER) return NULL; for (i = 0; i < pdata->slave_num; i++) - if (pdata->slave[i].slave_id == slave_id) + if (pdata->slave[i].slave_id == param->slave_id) return pdata->slave + i; return NULL; @@ -299,7 +299,7 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) if (param) { struct sh_dmae_slave_config *cfg; - cfg = sh_dmae_find_slave(sh_chan, param->slave_id); + cfg = sh_dmae_find_slave(sh_chan, param); if (!cfg) return -EINVAL; diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index f7b9aff88f4a..2d9a06db83b5 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -90,8 +90,8 @@ struct sci_port { struct dma_chan *chan_rx; #ifdef CONFIG_SERIAL_SH_SCI_DMA struct device *dma_dev; - enum sh_dmae_slave_chan_id slave_tx; - enum sh_dmae_slave_chan_id slave_rx; + unsigned int slave_tx; + unsigned int slave_rx; struct dma_async_tx_descriptor *desc_tx; struct dma_async_tx_descriptor *desc_rx[2]; dma_cookie_t cookie_tx; diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 193d4bfe42ff..f5364a1de68b 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -33,8 +33,8 @@ struct plat_sci_port { char *clk; /* clock string */ struct device *dma_dev; #ifdef CONFIG_SERIAL_SH_SCI_DMA - enum sh_dmae_slave_chan_id dma_slave_tx; - enum sh_dmae_slave_chan_id dma_slave_rx; + unsigned int dma_slave_tx; + unsigned int dma_slave_rx; #endif }; -- cgit v1.2.3-59-g8ed1b From 02ca5083f60521d09f13224596564a405108bc4c Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 19 Mar 2010 04:46:47 +0000 Subject: dmaengine: shdma: Common SH_DMA_SLAVE_NUMBER Move SHDMA_SLAVE_NUMBER from asm/dmaengine.h to shdma.h. Set it to 256 to support a wide range of processors. The amount of memory consumed by this change is limited to 256 bits. While at it, rename to SH_DMA_SLAVE_NUMBER to match with the rest of the file. Signed-off-by: Magnus Damm Acked-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- arch/sh/include/asm/dmaengine.h | 1 - drivers/dma/shdma.c | 4 ++-- drivers/dma/shdma.h | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/arch/sh/include/asm/dmaengine.h b/arch/sh/include/asm/dmaengine.h index 568e991442b0..abb8d660b6fb 100644 --- a/arch/sh/include/asm/dmaengine.h +++ b/arch/sh/include/asm/dmaengine.h @@ -34,7 +34,6 @@ enum { SHDMA_SLAVE_SIUA_RX, SHDMA_SLAVE_SIUB_TX, SHDMA_SLAVE_SIUB_RX, - SHDMA_SLAVE_NUMBER, /* Must stay last */ }; struct sh_dmae_slave_config { diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index e5588f4868ca..83605389fb56 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -44,7 +44,7 @@ enum sh_dmae_desc_status { #define LOG2_DEFAULT_XFER_SIZE 2 /* A bitmask with bits enough for enum sh_dmae_slave_chan_id */ -static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SHDMA_SLAVE_NUMBER)]; +static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SH_DMA_SLAVE_NUMBER)]; static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all); @@ -274,7 +274,7 @@ static struct sh_dmae_slave_config *sh_dmae_find_slave( struct sh_dmae_pdata *pdata = shdev->pdata; int i; - if (param->slave_id >= SHDMA_SLAVE_NUMBER) + if (param->slave_id >= SH_DMA_SLAVE_NUMBER) return NULL; for (i = 0; i < pdata->slave_num; i++) diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h index 153609a1e96c..d7ddf6f47154 100644 --- a/drivers/dma/shdma.h +++ b/drivers/dma/shdma.h @@ -19,6 +19,7 @@ #include +#define SH_DMA_SLAVE_NUMBER 256 #define SH_DMA_TCR_MAX 0x00FFFFFF /* 16MB */ struct device; -- cgit v1.2.3-59-g8ed1b From f354a38175f5219146f4675dca770e114cf3c502 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 19 Mar 2010 04:47:01 +0000 Subject: serial: sh-sci: Use sg_dma_len() and sg_dma_address() Convert the dmaengine code in sh-sci.c from directly accessing the struct scatterlist members length, dma_length and dma_address to using the macros sg_dma_len() and sg_dma_address(). Makes the code build for ARM. Signed-off-by: Magnus Damm Acked-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 2d9a06db83b5..cd52dd64edf0 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -916,10 +916,10 @@ static void sci_dma_tx_complete(void *arg) spin_lock_irqsave(&port->lock, flags); - xmit->tail += s->sg_tx.length; + xmit->tail += sg_dma_len(&s->sg_tx); xmit->tail &= UART_XMIT_SIZE - 1; - port->icount.tx += s->sg_tx.length; + port->icount.tx += sg_dma_len(&s->sg_tx); async_tx_ack(s->desc_tx); s->cookie_tx = -EINVAL; @@ -1134,14 +1134,13 @@ static void work_fn_tx(struct work_struct *work) */ spin_lock_irq(&port->lock); sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); - sg->dma_address = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + + sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + sg->offset; - sg->length = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), + sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); - sg->dma_length = sg->length; spin_unlock_irq(&port->lock); - BUG_ON(!sg->length); + BUG_ON(!sg_dma_len(sg)); desc = chan->device->device_prep_slave_sg(chan, sg, s->sg_len_tx, DMA_TO_DEVICE, @@ -1342,8 +1341,7 @@ static void sci_request_dma(struct uart_port *port) sg_init_table(sg, 1); sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, (int)buf[i] & ~PAGE_MASK); - sg->dma_address = dma[i]; - sg->dma_length = sg->length; + sg_dma_address(sg) = dma[i]; } INIT_WORK(&s->work_rx, work_fn_rx); -- cgit v1.2.3-59-g8ed1b From b2623a61cfd3c6badb8396dc85ab5a70f4a05f61 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 19 Mar 2010 04:47:10 +0000 Subject: dmaengine: shdma: Introduce include/linux/sh_dma.h Create a common platform data header file for the shdma dmaengine driver. This is done by moving common structures from sh asm/dmaengine.h to linux/sh_dma.h. DMA registers are also copied from sh asm/dma-register.h to make the code architecture independent. The sh header file asm/dmaengine.h is still kept with the slave id enum. This allows us to keep the old processor specific code as is and slowly move over to slave id enums in per-processor headers. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/sh/include/asm/dmaengine.h | 60 +----------------------- drivers/dma/shdma.c | 3 +- drivers/dma/shdma.h | 3 +- include/linux/sh_dma.h | 101 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 63 deletions(-) create mode 100644 include/linux/sh_dma.h (limited to 'drivers') diff --git a/arch/sh/include/asm/dmaengine.h b/arch/sh/include/asm/dmaengine.h index abb8d660b6fb..2a02b611a9ad 100644 --- a/arch/sh/include/asm/dmaengine.h +++ b/arch/sh/include/asm/dmaengine.h @@ -10,12 +10,7 @@ #ifndef ASM_DMAENGINE_H #define ASM_DMAENGINE_H -#include -#include - -#include - -#define SH_DMAC_MAX_CHANNELS 6 +#include enum { SHDMA_SLAVE_SCIF0_TX, @@ -36,57 +31,4 @@ enum { SHDMA_SLAVE_SIUB_RX, }; -struct sh_dmae_slave_config { - unsigned int slave_id; - dma_addr_t addr; - u32 chcr; - char mid_rid; -}; - -struct sh_dmae_channel { - unsigned int offset; - unsigned int dmars; - unsigned int dmars_bit; -}; - -struct sh_dmae_pdata { - struct sh_dmae_slave_config *slave; - int slave_num; - struct sh_dmae_channel *channel; - int channel_num; - unsigned int ts_low_shift; - unsigned int ts_low_mask; - unsigned int ts_high_shift; - unsigned int ts_high_mask; - unsigned int *ts_shift; - int ts_shift_num; - u16 dmaor_init; -}; - -struct device; - -/* Used by slave DMA clients to request DMA to/from a specific peripheral */ -struct sh_dmae_slave { - unsigned int slave_id; /* Set by the platform */ - struct device *dma_dev; /* Set by the platform */ - struct sh_dmae_slave_config *config; /* Set by the driver */ -}; - -struct sh_dmae_regs { - u32 sar; /* SAR / source address */ - u32 dar; /* DAR / destination address */ - u32 tcr; /* TCR / transfer count */ -}; - -struct sh_desc { - struct sh_dmae_regs hw; - struct list_head node; - struct dma_async_tx_descriptor async_tx; - enum dma_data_direction direction; - dma_cookie_t cookie; - size_t partial; - int chunks; - int mark; -}; - #endif diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 83605389fb56..7a18b580f626 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -25,8 +25,7 @@ #include #include #include - -#include +#include #include "shdma.h" diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h index d7ddf6f47154..4021275a0a43 100644 --- a/drivers/dma/shdma.h +++ b/drivers/dma/shdma.h @@ -17,8 +17,7 @@ #include #include -#include - +#define SH_DMAC_MAX_CHANNELS 6 #define SH_DMA_SLAVE_NUMBER 256 #define SH_DMA_TCR_MAX 0x00FFFFFF /* 16MB */ diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h new file mode 100644 index 000000000000..cdaaff424211 --- /dev/null +++ b/include/linux/sh_dma.h @@ -0,0 +1,101 @@ +/* + * Header for the new SH dmaengine driver + * + * Copyright (C) 2010 Guennadi Liakhovetski + * + * 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 SH_DMA_H +#define SH_DMA_H + +#include +#include + +/* Used by slave DMA clients to request DMA to/from a specific peripheral */ +struct sh_dmae_slave { + unsigned int slave_id; /* Set by the platform */ + struct device *dma_dev; /* Set by the platform */ + struct sh_dmae_slave_config *config; /* Set by the driver */ +}; + +struct sh_dmae_regs { + u32 sar; /* SAR / source address */ + u32 dar; /* DAR / destination address */ + u32 tcr; /* TCR / transfer count */ +}; + +struct sh_desc { + struct sh_dmae_regs hw; + struct list_head node; + struct dma_async_tx_descriptor async_tx; + enum dma_data_direction direction; + dma_cookie_t cookie; + size_t partial; + int chunks; + int mark; +}; +struct sh_dmae_slave_config { + unsigned int slave_id; + dma_addr_t addr; + u32 chcr; + char mid_rid; +}; + +struct sh_dmae_channel { + unsigned int offset; + unsigned int dmars; + unsigned int dmars_bit; +}; + +struct sh_dmae_pdata { + struct sh_dmae_slave_config *slave; + int slave_num; + struct sh_dmae_channel *channel; + int channel_num; + unsigned int ts_low_shift; + unsigned int ts_low_mask; + unsigned int ts_high_shift; + unsigned int ts_high_mask; + unsigned int *ts_shift; + int ts_shift_num; + u16 dmaor_init; +}; + +/* DMA register */ +#define SAR 0x00 +#define DAR 0x04 +#define TCR 0x08 +#define CHCR 0x0C +#define DMAOR 0x40 + +/* DMAOR definitions */ +#define DMAOR_AE 0x00000004 +#define DMAOR_NMIF 0x00000002 +#define DMAOR_DME 0x00000001 + +/* Definitions for the SuperH DMAC */ +#define REQ_L 0x00000000 +#define REQ_E 0x00080000 +#define RACK_H 0x00000000 +#define RACK_L 0x00040000 +#define ACK_R 0x00000000 +#define ACK_W 0x00020000 +#define ACK_H 0x00000000 +#define ACK_L 0x00010000 +#define DM_INC 0x00004000 +#define DM_DEC 0x00008000 +#define DM_FIX 0x0000c000 +#define SM_INC 0x00001000 +#define SM_DEC 0x00002000 +#define SM_FIX 0x00003000 +#define RS_IN 0x00000200 +#define RS_OUT 0x00000300 +#define TS_BLK 0x00000040 +#define TM_BUR 0x00000020 +#define CHCR_DE 0x00000001 +#define CHCR_TE 0x00000002 +#define CHCR_IE 0x00000004 + +#endif -- cgit v1.2.3-59-g8ed1b From 3089f381fbaf53560dcbcb4aef6ef17fe44e347c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 19 Mar 2010 13:53:04 +0000 Subject: SH: extend SCI DMA support to work on SCIFA ports SCIFA ports have additional bits to control DMA requests and they must have respective interrupt sources enabled, as the datasheet suggests, the only way to avoid actually taking interrupts in addition to DMA events is by masking the IRQ on the CPU. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.c | 114 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index cd52dd64edf0..304a877d083d 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -106,6 +106,7 @@ struct sci_port { struct work_struct work_tx; struct work_struct work_rx; struct timer_list rx_timer; + unsigned int rx_timeout; #endif }; @@ -673,22 +674,22 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) struct sci_port *s = to_sci_port(port); if (s->chan_rx) { - unsigned long tout; u16 scr = sci_in(port, SCSCR); u16 ssr = sci_in(port, SCxSR); /* Disable future Rx interrupts */ - sci_out(port, SCSCR, scr & ~SCI_CTRL_FLAGS_RIE); + if (port->type == PORT_SCIFA) { + disable_irq_nosync(irq); + scr |= 0x4000; + } else { + scr &= ~SCI_CTRL_FLAGS_RIE; + } + sci_out(port, SCSCR, scr); /* Clear current interrupt */ sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); - /* Calculate delay for 1.5 DMA buffers */ - tout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / - port->fifosize / 2; - dev_dbg(port->dev, "Rx IRQ: setup timeout in %lu ms\n", - tout * 1000 / HZ); - if (tout < 2) - tout = 2; - mod_timer(&s->rx_timer, jiffies + tout); + dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", + jiffies, s->rx_timeout); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); return IRQ_HANDLED; } @@ -925,13 +926,17 @@ static void sci_dma_tx_complete(void *arg) s->cookie_tx = -EINVAL; s->desc_tx = NULL; - spin_unlock_irqrestore(&port->lock, flags); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_chars_pending(xmit)) + if (!uart_circ_empty(xmit)) { schedule_work(&s->work_tx); + } else if (port->type == PORT_SCIFA) { + u16 ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE); + } + + spin_unlock_irqrestore(&port->lock, flags); } /* Locking: called with port lock held */ @@ -975,13 +980,13 @@ static void sci_dma_rx_complete(void *arg) unsigned long flags; int count; - dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx); spin_lock_irqsave(&port->lock, flags); count = sci_dma_rx_push(s, tty, s->buf_len_rx); - mod_timer(&s->rx_timer, jiffies + msecs_to_jiffies(5)); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); spin_unlock_irqrestore(&port->lock, flags); @@ -1053,6 +1058,8 @@ static void sci_submit_rx(struct sci_port *s) sci_rx_dma_release(s, true); return; } + dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, + s->cookie_rx[i], i); } s->active_rx = s->cookie_rx[0]; @@ -1110,10 +1117,10 @@ static void work_fn_rx(struct work_struct *work) return; } - dev_dbg(port->dev, "%s: cookie %d #%d\n", __func__, - s->cookie_rx[new], new); - s->active_rx = s->cookie_rx[!new]; + + dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, + s->cookie_rx[new], new, s->active_rx); } static void work_fn_tx(struct work_struct *work) @@ -1175,23 +1182,28 @@ static void work_fn_tx(struct work_struct *work) static void sci_start_tx(struct uart_port *port) { + struct sci_port *s = to_sci_port(port); unsigned short ctrl; #ifdef CONFIG_SERIAL_SH_SCI_DMA - struct sci_port *s = to_sci_port(port); - - if (s->chan_tx) { - if (!uart_circ_empty(&s->port.state->xmit) && s->cookie_tx < 0) - schedule_work(&s->work_tx); - - return; + if (port->type == PORT_SCIFA) { + u16 new, scr = sci_in(port, SCSCR); + if (s->chan_tx) + new = scr | 0x8000; + else + new = scr & ~0x8000; + if (new != scr) + sci_out(port, SCSCR, new); } + if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + s->cookie_tx < 0) + schedule_work(&s->work_tx); #endif - - /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - ctrl |= SCI_CTRL_FLAGS_TIE; - sci_out(port, SCSCR, ctrl); + if (!s->chan_tx || port->type == PORT_SCIFA) { + /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE); + } } static void sci_stop_tx(struct uart_port *port) @@ -1200,6 +1212,8 @@ static void sci_stop_tx(struct uart_port *port) /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x8000; ctrl &= ~SCI_CTRL_FLAGS_TIE; sci_out(port, SCSCR, ctrl); } @@ -1210,6 +1224,8 @@ static void sci_start_rx(struct uart_port *port) /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl |= sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x4000; sci_out(port, SCSCR, ctrl); } @@ -1219,6 +1235,8 @@ static void sci_stop_rx(struct uart_port *port) /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x4000; ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); sci_out(port, SCSCR, ctrl); } @@ -1253,8 +1271,12 @@ static void rx_timer_fn(unsigned long arg) { struct sci_port *s = (struct sci_port *)arg; struct uart_port *port = &s->port; - u16 scr = sci_in(port, SCSCR); + + if (port->type == PORT_SCIFA) { + scr &= ~0x4000; + enable_irq(s->irqs[1]); + } sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE); dev_dbg(port->dev, "DMA Rx timed out\n"); schedule_work(&s->work_rx); @@ -1404,8 +1426,12 @@ static void sci_shutdown(struct uart_port *port) static void sci_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct sci_port *s = to_sci_port(port); +#endif unsigned int status, baud, smr_val, max_baud; int t = -1; + u16 scfcr = 0; /* * earlyprintk comes here early on with port->uartclk set to zero. @@ -1428,7 +1454,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ if (port->type != PORT_SCI) - sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); + sci_out(port, SCFCR, scfcr | SCFCR_RFRST | SCFCR_TFRST); smr_val = sci_in(port, SCSMR) & 3; if ((termios->c_cflag & CSIZE) == CS7) @@ -1459,10 +1485,32 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, } sci_init_pins(port, termios->c_cflag); - sci_out(port, SCFCR, (termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0); + sci_out(port, SCFCR, scfcr | ((termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0)); sci_out(port, SCSCR, SCSCR_INIT(port)); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + /* + * Calculate delay for 1.5 DMA buffers: see + * drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits + * (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function + * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." + * Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO + * sizes), but it has been found out experimentally, that this is not + * enough: the driver too often needlessly runs on a DMA timeout. 20ms + * as a minimum seem to work perfectly. + */ + if (s->chan_rx) { + s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / + port->fifosize / 2; + dev_dbg(port->dev, + "DMA Rx t-out %ums, tty t-out %u jiffies\n", + s->rx_timeout * 1000 / HZ, port->timeout); + if (s->rx_timeout < msecs_to_jiffies(20)) + s->rx_timeout = msecs_to_jiffies(20); + } +#endif + if ((termios->c_cflag & CREAD) != 0) sci_start_rx(port); } -- cgit v1.2.3-59-g8ed1b From bd05086bbe3f241cd552068f9ceba9e19c6ce427 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 29 Mar 2010 15:51:35 +0900 Subject: driver core: Convert to kasprintf() for early dev_name(). This is just a simple refactoring patch on top of the early dev_name() support, converting from kstrdup() to kasprintf() as suggested by Kay. Signed-off-by: Paul Mundt --- drivers/base/platform.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index d2d4926c5c4c..f2377f3d95e5 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1245,19 +1245,20 @@ static int __init early_platform_driver_probe_id(char *class_str, * rest of the driver core is initialized. */ if (!match->dev.init_name) { - char buf[32]; - if (match->id != -1) - snprintf(buf, sizeof(buf), "%s.%d", - match->name, match->id); + match->dev.init_name = + kasprintf(GFP_KERNEL, "%s.%d", + match->name, + match->id); else - snprintf(buf, sizeof(buf), "%s", - match->name); + match->dev.init_name = + kasprintf(GFP_KERNEL, "%s", + match->name); - match->dev.init_name = kstrdup(buf, GFP_KERNEL); if (!match->dev.init_name) return -ENOMEM; } + if (epdrv->pdrv->probe(match)) pr_warning("%s: unable to probe %s early.\n", class_str, match->name); -- cgit v1.2.3-59-g8ed1b From c2a25e819717ea34e8f682d67a3fa76f4893395b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 29 Mar 2010 16:55:43 +0900 Subject: clocksource: Deprecate clock string across the SH drivers. We want to get rid of the clock string from platform data entirely, depending on the clkdev-based clock lookup to do the right thing for us instead. This converts all of the SH drivers to request their associated function clocks directly, and if there is no match for that then we fall back on the legacy lookup while warning about it. After all of the outstanding CPUs have been converted to clkdev lookups the clock string will be killed off completely. Signed-off-by: Paul Mundt --- drivers/clocksource/sh_cmt.c | 12 ++++++++---- drivers/clocksource/sh_mtu2.c | 12 ++++++++---- drivers/clocksource/sh_tmu.c | 12 ++++++++---- 3 files changed, 24 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index a1d381171388..c5f66171a713 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -608,11 +608,15 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; /* get hold of clock */ - p->clk = clk_get(&p->pdev->dev, cfg->clk); + p->clk = clk_get(&p->pdev->dev, "cmt_fck"); if (IS_ERR(p->clk)) { - dev_err(&p->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); - goto err1; + dev_warn(&p->pdev->dev, "using deprecated clock lookup\n"); + p->clk = clk_get(&p->pdev->dev, cfg->clk); + if (IS_ERR(p->clk)) { + dev_err(&p->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(p->clk); + goto err1; + } } if (resource_size(res) == 6) { diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 8ce355572d16..b11882e0f1bd 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -283,11 +283,15 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; /* get hold of clock */ - p->clk = clk_get(&p->pdev->dev, cfg->clk); + p->clk = clk_get(&p->pdev->dev, "mtu2_fck"); if (IS_ERR(p->clk)) { - dev_err(&p->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); - goto err1; + dev_warn(&p->pdev->dev, "using deprecated clock lookup\n"); + p->clk = clk_get(&p->pdev->dev, cfg->clk); + if (IS_ERR(p->clk)) { + dev_err(&p->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(p->clk); + goto err1; + } } return sh_mtu2_register(p, (char *)dev_name(&p->pdev->dev), diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 34239453ebaf..6b62283c1aba 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -387,11 +387,15 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; /* get hold of clock */ - p->clk = clk_get(&p->pdev->dev, cfg->clk); + p->clk = clk_get(&p->pdev->dev, "tmu_fck"); if (IS_ERR(p->clk)) { - dev_err(&p->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); - goto err1; + dev_warn(&p->pdev->dev, "using deprecated clock lookup\n"); + p->clk = clk_get(&p->pdev->dev, cfg->clk); + if (IS_ERR(p->clk)) { + dev_err(&p->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(p->clk); + goto err1; + } } return sh_tmu_register(p, (char *)dev_name(&p->pdev->dev), -- cgit v1.2.3-59-g8ed1b From 0ded75428605213641897d6b8d8e9cf9fdb6eb8d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 13 Apr 2010 10:16:34 +0900 Subject: sh: intc: Provide sysdev name for intc controllers. Presently the sysdevs are simply numbered based on the list position, without having any direct way of figuring out which controller these are actually mapping to. This provides a name attr for mapping out the chip name. Signed-off-by: Paul Mundt --- drivers/sh/intc.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index f43850527645..b605f7e478d6 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -1034,6 +1034,18 @@ err0: return -ENOMEM; } +static ssize_t +show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) +{ + struct intc_desc_int *d; + + d = container_of(dev, struct intc_desc_int, sysdev); + + return sprintf(buf, "%s\n", d->chip.name); +} + +static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL); + static int intc_suspend(struct sys_device *dev, pm_message_t state) { struct intc_desc_int *d; @@ -1098,8 +1110,12 @@ static int __init register_intc_sysdevs(void) d->sysdev.id = id; d->sysdev.cls = &intc_sysdev_class; error = sysdev_register(&d->sysdev); + if (error == 0) + error = sysdev_create_file(&d->sysdev, + &attr_name); if (error) break; + id++; } } -- cgit v1.2.3-59-g8ed1b From 12129fea50edcd696a9556523b058d6c445f21d8 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 13 Apr 2010 13:49:54 +0900 Subject: sh: intc: Tidy up loglevel mismatches. The printk loglevels are all over the place, make them a bit more coherent, and add some registration notification while we're at it. Signed-off-by: Paul Mundt --- drivers/sh/intc.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index b605f7e478d6..65e15828faaa 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -860,6 +860,9 @@ int __init register_intc_controller(struct intc_desc *desc) struct intc_desc_int *d; struct resource *res; + pr_info("intc: Registered controller '%s' with %u IRQs\n", + desc->name, hw->nr_vectors); + d = kzalloc(sizeof(*d), GFP_NOWAIT); if (!d) goto err0; @@ -974,7 +977,7 @@ int __init register_intc_controller(struct intc_desc *desc) irq_desc = irq_to_desc_alloc_node(irq, numa_node_id()); if (unlikely(!irq_desc)) { - pr_info("can't get irq_desc for %d\n", irq); + pr_err("can't get irq_desc for %d\n", irq); continue; } @@ -994,7 +997,7 @@ int __init register_intc_controller(struct intc_desc *desc) */ irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id()); if (unlikely(!irq_desc)) { - pr_info("can't get irq_desc for %d\n", irq2); + pr_err("can't get irq_desc for %d\n", irq2); continue; } @@ -1121,7 +1124,7 @@ static int __init register_intc_sysdevs(void) } if (error) - pr_warning("intc: sysdev registration error\n"); + pr_err("intc: sysdev registration error\n"); return error; } @@ -1154,7 +1157,7 @@ unsigned int create_irq_nr(unsigned int irq_want, int node) desc = irq_to_desc_alloc_node(new, node); if (unlikely(!desc)) { - pr_info("can't get irq_desc for %d\n", new); + pr_err("can't get irq_desc for %d\n", new); goto out_unlock; } -- cgit v1.2.3-59-g8ed1b From 43b8774dc409ea5d9369b978e2e7bc79289f0522 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 13 Apr 2010 14:43:03 +0900 Subject: sh: intc: userimask support. This adds support for hardware-assisted userspace irq masking for special priority levels. Due to the SR.IMASK interactivity, only some platforms implement this in hardware (including but not limited to SH-4A interrupt controllers, and ARM-based SH-Mobile CPUs). Each CPU needs to wire this up on its own, for now only SH7786 is wired up as an example. Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 2 + arch/sh/kernel/cpu/sh4a/setup-sh7786.c | 3 ++ drivers/sh/Kconfig | 13 +++++++ drivers/sh/intc.c | 69 +++++++++++++++++++++++++++++++++- include/linux/sh_intc.h | 9 +++++ 5 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 drivers/sh/Kconfig (limited to 'drivers') diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 8d90564c2bcf..ba86bfba95ac 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -732,6 +732,8 @@ config GUSA_RB LLSC, this should be more efficient than the other alternative of disabling interrupts around the atomic sequence. +source "drivers/sh/Kconfig" + endmenu menu "Boot options" diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c index 61e549190873..235edf8065df 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -907,6 +908,7 @@ static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567, #define INTC_INTMSK2 INTMSK2 #define INTC_INTMSKCLR1 CnINTMSKCLR1 #define INTC_INTMSKCLR2 INTMSKCLR2 +#define INTC_USERIMASK 0xfe411000 void __init plat_irq_setup(void) { @@ -921,6 +923,7 @@ void __init plat_irq_setup(void) __raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0); register_intc_controller(&intc_desc); + register_intc_userimask(INTC_USERIMASK); } void __init plat_irq_setup_pins(int mode) diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig new file mode 100644 index 000000000000..22c3cdaf22fe --- /dev/null +++ b/drivers/sh/Kconfig @@ -0,0 +1,13 @@ +config INTC_USERIMASK + bool "Userspace interrupt masking support" + depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A) + help + This enables support for hardware-assisted userspace hardirq + masking. + + SH-4A and newer interrupt blocks all support a special shadowed + page with all non-masking registers obscured when mapped in to + userspace. This is primarily for use by userspace device + drivers that are using special priority levels. + + If in doubt, say N. diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 65e15828faaa..77d10acf1884 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -27,6 +27,7 @@ #include #include #include +#include #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ @@ -94,7 +95,8 @@ static DEFINE_SPINLOCK(vector_lock); #define SMP_NR(d, x) 1 #endif -static unsigned int intc_prio_level[NR_IRQS]; /* for now */ +static unsigned int intc_prio_level[NR_IRQS]; /* for now */ +static unsigned int default_prio_level = 2; /* 2 - 16 */ static unsigned long ack_handle[NR_IRQS]; static inline struct intc_desc_int *get_intc_desc(unsigned int irq) @@ -787,7 +789,7 @@ static void __init intc_register_irq(struct intc_desc *desc, /* set priority level * - this needs to be at least 2 for 5-bit priorities on 7780 */ - intc_prio_level[irq] = 2; + intc_prio_level[irq] = default_prio_level; /* enable secondary masking method if present */ if (data[!primary]) @@ -1037,6 +1039,64 @@ err0: return -ENOMEM; } +#ifdef CONFIG_INTC_USERIMASK +static void __iomem *uimask; + +int register_intc_userimask(unsigned long addr) +{ + if (unlikely(uimask)) + return -EBUSY; + + uimask = ioremap_nocache(addr, SZ_4K); + if (unlikely(!uimask)) + return -ENOMEM; + + pr_info("intc: userimask support registered for levels 0 -> %d\n", + default_prio_level - 1); + + return 0; +} + +static ssize_t +show_intc_userimask(struct sysdev_class *cls, + struct sysdev_class_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf); +} + +static ssize_t +store_intc_userimask(struct sysdev_class *cls, + struct sysdev_class_attribute *attr, + const char *buf, size_t count) +{ + unsigned long level; + + level = simple_strtoul(buf, NULL, 10); + + /* + * Minimal acceptable IRQ levels are in the 2 - 16 range, but + * these are chomped so as to not interfere with normal IRQs. + * + * Level 1 is a special case on some CPUs in that it's not + * directly settable, but given that USERIMASK cuts off below a + * certain level, we don't care about this limitation here. + * Level 0 on the other hand equates to user masking disabled. + * + * We use default_prio_level as a cut off so that only special + * case opt-in IRQs can be mangled. + */ + if (level >= default_prio_level) + return -EINVAL; + + __raw_writel(0xa5 << 24 | level << 4, uimask); + + return count; +} + +static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, + show_intc_userimask, store_intc_userimask); +#endif + static ssize_t show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) { @@ -1108,6 +1168,11 @@ static int __init register_intc_sysdevs(void) int id = 0; error = sysdev_class_register(&intc_sysdev_class); +#ifdef CONFIG_INTC_USERIMASK + if (!error && uimask) + error = sysdev_class_create_file(&intc_sysdev_class, + &attr_userimask); +#endif if (!error) { list_for_each_entry(d, &intc_list, list) { d->sysdev.id = id; diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h index 01d8168c5a1b..f0e8cca199c7 100644 --- a/include/linux/sh_intc.h +++ b/include/linux/sh_intc.h @@ -99,6 +99,15 @@ struct intc_desc symbol __initdata = { \ int __init register_intc_controller(struct intc_desc *desc); int intc_set_priority(unsigned int irq, unsigned int prio); +#ifdef CONFIG_INTC_USERIMASK +int register_intc_userimask(unsigned long addr); +#else +static inline int register_intc_userimask(unsigned long addr) +{ + return 0; +} +#endif + int reserve_irq_vector(unsigned int irq); void reserve_irq_legacy(void); -- cgit v1.2.3-59-g8ed1b From fecf066c2d2fbc7e6a7e7e3a5af772a165bdd7b0 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 15 Apr 2010 11:59:28 +0900 Subject: sh: Disable IRQ balancing for timer and IPI IRQs. Make sure that the timer IRQs and IPIs aren't enabled for IRQ balancing. IPIs are disabled as a result of being percpu while the timers simply disable balancing outright. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh4a/smp-shx3.c | 4 ++-- drivers/clocksource/sh_cmt.c | 3 ++- drivers/clocksource/sh_mtu2.c | 3 ++- drivers/clocksource/sh_tmu.c | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/arch/sh/kernel/cpu/sh4a/smp-shx3.c b/arch/sh/kernel/cpu/sh4a/smp-shx3.c index 11bf4c1e25c0..c98b4574c44e 100644 --- a/arch/sh/kernel/cpu/sh4a/smp-shx3.c +++ b/arch/sh/kernel/cpu/sh4a/smp-shx3.c @@ -72,8 +72,8 @@ void __init plat_prepare_cpus(unsigned int max_cpus) BUILD_BUG_ON(SMP_MSG_NR >= 8); for (i = 0; i < SMP_MSG_NR; i++) - request_irq(104 + i, ipi_interrupt_handler, IRQF_DISABLED, - "IPI", (void *)(long)i); + request_irq(104 + i, ipi_interrupt_handler, + IRQF_DISABLED | IRQF_PERCPU, "IPI", (void *)(long)i); } void plat_start_cpu(unsigned int cpu, unsigned long entry_point) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index c5f66171a713..45be4dcb9514 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -605,7 +605,8 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) p->irqaction.name = dev_name(&p->pdev->dev); p->irqaction.handler = sh_cmt_interrupt; p->irqaction.dev_id = p; - p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; + p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \ + IRQF_IRQPOLL | IRQF_NOBALANCING; /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, "cmt_fck"); diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index b11882e0f1bd..644ecbbed256 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -280,7 +280,8 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) p->irqaction.handler = sh_mtu2_interrupt; p->irqaction.dev_id = p; p->irqaction.irq = irq; - p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; + p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \ + IRQF_IRQPOLL | IRQF_NOBALANCING; /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, "mtu2_fck"); diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 6b62283c1aba..44c39a363768 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -384,7 +384,8 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) p->irqaction.handler = sh_tmu_interrupt; p->irqaction.dev_id = p; p->irqaction.irq = irq; - p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; + p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \ + IRQF_IRQPOLL | IRQF_NOBALANCING; /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, "tmu_fck"); -- cgit v1.2.3-59-g8ed1b From dc825b17904a06bbd2f79d720b23156e4c01a22f Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 15 Apr 2010 13:13:52 +0900 Subject: sh: intc: IRQ auto-distribution support. This implements support for hardware-managed IRQ balancing as implemented by SH-X3 cores (presently only hooked up for SH7786, but can probably be carried over to other SH-X3 cores, too). CPUs need to specify their distribution register along with the mask definitions, as these follow the same format. Peripheral IRQs that don't opt out of balancing will be automatically distributed at the whim of the hardware block, while each CPU needs to verify whether it is handling the IRQ or not, especially before clearing the mask. Signed-off-by: Paul Mundt --- arch/sh/include/asm/irq.h | 16 +++ arch/sh/kernel/cpu/sh4a/setup-sh7786.c | 36 +++++- arch/sh/kernel/irq.c | 49 ++++---- drivers/sh/Kconfig | 11 ++ drivers/sh/intc.c | 200 ++++++++++++++++++++++++--------- include/linux/sh_intc.h | 11 +- 6 files changed, 244 insertions(+), 79 deletions(-) (limited to 'drivers') diff --git a/arch/sh/include/asm/irq.h b/arch/sh/include/asm/irq.h index df8e1500527c..99c593b3a827 100644 --- a/arch/sh/include/asm/irq.h +++ b/arch/sh/include/asm/irq.h @@ -11,6 +11,14 @@ #define NR_IRQS 256 #define NR_IRQS_LEGACY 8 /* Legacy external IRQ0-7 */ +/* + * This is a special IRQ number for indicating that no IRQ has been + * triggered and to simply ignore the IRQ dispatch. This is a special + * case that can happen with IRQ auto-distribution when multiple CPUs + * are woken up and signalled in parallel. + */ +#define NO_IRQ_IGNORE ((unsigned int)-1) + /* * Convert back and forth between INTEVT and IRQ values. */ @@ -53,6 +61,14 @@ extern void irq_ctx_exit(int cpu); # define irq_ctx_exit(cpu) do { } while (0) #endif +#ifdef CONFIG_INTC_BALANCING +extern unsigned int irq_lookup(unsigned int irq); +extern void irq_finish(unsigned int irq); +#else +#define irq_lookup(irq) (irq) +#define irq_finish(irq) do { } while (0) +#endif + #include #ifdef CONFIG_CPU_SH5 #include diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c index 235edf8065df..d7336036d04d 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c @@ -573,7 +573,6 @@ static struct platform_device *sh7786_devices[] __initdata = { &usb_ohci_device, }; - /* * Please call this function if your platform board * use external clock for USB @@ -581,6 +580,7 @@ static struct platform_device *sh7786_devices[] __initdata = { #define USBCTL0 0xffe70858 #define CLOCK_MODE_MASK 0xffffff7f #define EXT_CLOCK_MODE 0x00000080 + void __init sh7786_usb_use_exclock(void) { u32 val = __raw_readl(USBCTL0) & CLOCK_MODE_MASK; @@ -598,6 +598,7 @@ void __init sh7786_usb_use_exclock(void) #define PLL_ENB 0x00000002 #define PHY_RST 0x00000004 #define ACT_PLL_STATUS 0xc0000000 + static void __init sh7786_usb_setup(void) { int i = 1000000; @@ -753,9 +754,19 @@ static struct intc_vect vectors[] __initdata = { #define INTMSK2 0xfe410068 #define INTMSKCLR2 0xfe41006c +#define INTDISTCR0 0xfe4100b0 +#define INTDISTCR1 0xfe4100b4 +#define INTACK 0xfe4100b8 +#define INTACKCLR 0xfe4100bc +#define INT2DISTCR0 0xfe410900 +#define INT2DISTCR1 0xfe410904 +#define INT2DISTCR2 0xfe410908 +#define INT2DISTCR3 0xfe41090c + static struct intc_mask_reg mask_registers[] __initdata = { { CnINTMSK0, CnINTMSKCLR0, 32, - { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 }, + INTC_SMP_BALANCING(INTDISTCR0) }, { INTMSK2, INTMSKCLR2, 32, { IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, @@ -767,7 +778,8 @@ static struct intc_mask_reg mask_registers[] __initdata = { IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, 0, } }, { CnINT2MSKR0, CnINT2MSKCR0 , 32, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WDT } }, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WDT }, + INTC_SMP_BALANCING(INT2DISTCR0) }, { CnINT2MSKR1, CnINT2MSKCR1, 32, { TMU0_0, TMU0_1, TMU0_2, TMU0_3, TMU1_0, TMU1_1, TMU1_2, 0, DMAC0_0, DMAC0_1, DMAC0_2, DMAC0_3, DMAC0_4, DMAC0_5, DMAC0_6, @@ -776,14 +788,14 @@ static struct intc_mask_reg mask_registers[] __initdata = { HPB_0, HPB_1, HPB_2, SCIF0_0, SCIF0_1, SCIF0_2, SCIF0_3, SCIF1, - TMU2, TMU3, 0, } }, + TMU2, TMU3, 0, }, INTC_SMP_BALANCING(INT2DISTCR1) }, { CnINT2MSKR2, CnINT2MSKCR2, 32, { 0, 0, SCIF2, SCIF3, SCIF4, SCIF5, Eth_0, Eth_1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, PCIeC0_0, PCIeC0_1, PCIeC0_2, PCIeC1_0, PCIeC1_1, PCIeC1_2, - USB, 0, 0 } }, + USB, 0, 0 }, INTC_SMP_BALANCING(INT2DISTCR2) }, { CnINT2MSKR3, CnINT2MSKCR3, 32, { 0, 0, 0, 0, 0, 0, I2C0, I2C1, @@ -792,7 +804,7 @@ static struct intc_mask_reg mask_registers[] __initdata = { HAC0, HAC1, FLCTL, 0, HSPI, GPIO0, GPIO1, Thermal, - 0, 0, 0, 0, 0, 0, 0, 0 } }, + 0, 0, 0, 0, 0, 0, 0, 0 }, INTC_SMP_BALANCING(INT2DISTCR3) }, }; static struct intc_prio_reg prio_registers[] __initdata = { @@ -910,6 +922,18 @@ static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567, #define INTC_INTMSKCLR2 INTMSKCLR2 #define INTC_USERIMASK 0xfe411000 +#ifdef CONFIG_INTC_BALANCING +unsigned int irq_lookup(unsigned int irq) +{ + return __raw_readl(INTACK) & 1 ? irq : NO_IRQ_IGNORE; +} + +void irq_finish(unsigned int irq) +{ + __raw_writel(irq2evt(irq), INTACKCLR); +} +#endif + void __init plat_irq_setup(void) { /* disable IRQ3-0 + IRQ7-4 */ diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index d2d41d046657..f6a9319c28e2 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -113,19 +113,14 @@ union irq_ctx { static union irq_ctx *hardirq_ctx[NR_CPUS] __read_mostly; static union irq_ctx *softirq_ctx[NR_CPUS] __read_mostly; -#endif -asmlinkage __irq_entry int do_IRQ(unsigned int irq, struct pt_regs *regs) +static char softirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss; +static char hardirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss; + +static inline void handle_one_irq(unsigned int irq) { - struct pt_regs *old_regs = set_irq_regs(regs); -#ifdef CONFIG_IRQSTACKS union irq_ctx *curctx, *irqctx; -#endif - - irq_enter(); - irq = irq_demux(irq); -#ifdef CONFIG_IRQSTACKS curctx = (union irq_ctx *)current_thread_info(); irqctx = hardirq_ctx[smp_processor_id()]; @@ -164,20 +159,9 @@ asmlinkage __irq_entry int do_IRQ(unsigned int irq, struct pt_regs *regs) "r5", "r6", "r7", "r8", "t", "pr" ); } else -#endif generic_handle_irq(irq); - - irq_exit(); - - set_irq_regs(old_regs); - return 1; } -#ifdef CONFIG_IRQSTACKS -static char softirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss; - -static char hardirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss; - /* * allocate per-cpu stacks for hardirq and for softirq processing */ @@ -257,8 +241,33 @@ asmlinkage void do_softirq(void) local_irq_restore(flags); } +#else +static inline void handle_one_irq(unsigned int irq) +{ + generic_handle_irq(irq); +} #endif +asmlinkage __irq_entry int do_IRQ(unsigned int irq, struct pt_regs *regs) +{ + struct pt_regs *old_regs = set_irq_regs(regs); + + irq_enter(); + + irq = irq_demux(irq_lookup(irq)); + + if (irq != NO_IRQ_IGNORE) { + handle_one_irq(irq); + irq_finish(irq); + } + + irq_exit(); + + set_irq_regs(old_regs); + + return IRQ_HANDLED; +} + void __init init_IRQ(void) { plat_irq_setup(); diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig index 22c3cdaf22fe..a54de0b9b3df 100644 --- a/drivers/sh/Kconfig +++ b/drivers/sh/Kconfig @@ -11,3 +11,14 @@ config INTC_USERIMASK drivers that are using special priority levels. If in doubt, say N. + +config INTC_BALANCING + bool "Hardware IRQ balancing support" + depends on SMP && SUPERH && CPU_SUBTYPE_SH7786 + help + This enables support for IRQ auto-distribution mode on SH-X3 + SMP parts. All of the balancing and CPU wakeup decisions are + taken care of automatically by hardware for distributed + vectors. + + If in doubt, say N. diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 77d10acf1884..dcb4c833820b 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -98,6 +98,9 @@ static DEFINE_SPINLOCK(vector_lock); static unsigned int intc_prio_level[NR_IRQS]; /* for now */ static unsigned int default_prio_level = 2; /* 2 - 16 */ static unsigned long ack_handle[NR_IRQS]; +#ifdef CONFIG_INTC_BALANCING +static unsigned long dist_handle[NR_IRQS]; +#endif static inline struct intc_desc_int *get_intc_desc(unsigned int irq) { @@ -105,6 +108,47 @@ static inline struct intc_desc_int *get_intc_desc(unsigned int irq) return container_of(chip, struct intc_desc_int, chip); } +static unsigned long intc_phys_to_virt(struct intc_desc_int *d, + unsigned long address) +{ + struct intc_window *window; + int k; + + /* scan through physical windows and convert address */ + for (k = 0; k < d->nr_windows; k++) { + window = d->window + k; + + if (address < window->phys) + continue; + + if (address >= (window->phys + window->size)) + continue; + + address -= window->phys; + address += (unsigned long)window->virt; + + return address; + } + + /* no windows defined, register must be 1:1 mapped virt:phys */ + return address; +} + +static unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address) +{ + unsigned int k; + + address = intc_phys_to_virt(d, address); + + for (k = 0; k < d->nr_reg; k++) { + if (d->reg[k] == address) + return k; + } + + BUG(); + return 0; +} + static inline unsigned int set_field(unsigned int value, unsigned int field_value, unsigned int handle) @@ -238,6 +282,85 @@ static void (*intc_disable_fns[])(unsigned long addr, [MODE_PCLR_REG] = intc_mode_field, }; +#ifdef CONFIG_INTC_BALANCING +static inline void intc_balancing_enable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = dist_handle[irq]; + unsigned long addr; + + if (irq_balancing_disabled(irq) || !handle) + return; + + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + intc_reg_fns[_INTC_FN(handle)](addr, handle, 1); +} + +static inline void intc_balancing_disable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = dist_handle[irq]; + unsigned long addr; + + if (irq_balancing_disabled(irq) || !handle) + return; + + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + intc_reg_fns[_INTC_FN(handle)](addr, handle, 0); +} + +static unsigned int intc_dist_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_mask_reg *mr = desc->hw.mask_regs; + unsigned int i, j, fn, mode; + unsigned long reg_e, reg_d; + + for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) { + mr = desc->hw.mask_regs + i; + + /* + * Skip this entry if there's no auto-distribution + * register associated with it. + */ + if (!mr->dist_reg) + continue; + + for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { + if (mr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + mode = MODE_ENABLE_REG; + reg_e = mr->dist_reg; + reg_d = mr->dist_reg; + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - j); + } + } + + /* + * It's possible we've gotten here with no distribution options + * available for the IRQ in question, so we just skip over those. + */ + return 0; +} +#else +static inline void intc_balancing_enable(unsigned int irq) +{ +} + +static inline void intc_balancing_disable(unsigned int irq) +{ +} +#endif + static inline void _intc_enable(unsigned int irq, unsigned long handle) { struct intc_desc_int *d = get_intc_desc(irq); @@ -253,6 +376,8 @@ static inline void _intc_enable(unsigned int irq, unsigned long handle) intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ [_INTC_FN(handle)], irq); } + + intc_balancing_enable(irq); } static void intc_enable(unsigned int irq) @@ -263,10 +388,12 @@ static void intc_enable(unsigned int irq) static void intc_disable(unsigned int irq) { struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = (unsigned long) get_irq_chip_data(irq); + unsigned long handle = (unsigned long)get_irq_chip_data(irq); unsigned long addr; unsigned int cpu; + intc_balancing_disable(irq); + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { #ifdef CONFIG_SMP if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) @@ -345,8 +472,7 @@ static void intc_mask_ack(unsigned int irq) intc_disable(irq); - /* read register and write zero only to the assocaited bit */ - + /* read register and write zero only to the associated bit */ if (handle) { addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); switch (_INTC_FN(handle)) { @@ -375,7 +501,8 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, { int i; - /* this doesn't scale well, but... + /* + * this doesn't scale well, but... * * this function should only be used for cerain uncommon * operations such as intc_set_priority() and intc_set_sense() @@ -386,7 +513,6 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, * memory footprint down is to make sure the array is sorted * and then perform a bisect to lookup the irq. */ - for (i = 0; i < nr_hp; i++) { if ((hp + i)->irq != irq) continue; @@ -417,7 +543,6 @@ int intc_set_priority(unsigned int irq, unsigned int prio) * primary masking method is using intc_prio_level[irq] * priority level will be set during next enable() */ - if (_INTC_FN(ihp->handle) != REG_FN_ERR) _intc_enable(irq, ihp->handle); } @@ -456,48 +581,6 @@ static int intc_set_sense(unsigned int irq, unsigned int type) return 0; } -static unsigned long intc_phys_to_virt(struct intc_desc_int *d, - unsigned long address) -{ - struct intc_window *window; - int k; - - /* scan through physical windows and convert address */ - for (k = 0; k < d->nr_windows; k++) { - window = d->window + k; - - if (address < window->phys) - continue; - - if (address >= (window->phys + window->size)) - continue; - - address -= window->phys; - address += (unsigned long)window->virt; - - return address; - } - - /* no windows defined, register must be 1:1 mapped virt:phys */ - return address; -} - -static unsigned int __init intc_get_reg(struct intc_desc_int *d, - unsigned long address) -{ - unsigned int k; - - address = intc_phys_to_virt(d, address); - - for (k = 0; k < d->nr_reg; k++) { - if (d->reg[k] == address) - return k; - } - - BUG(); - return 0; -} - static intc_enum __init intc_grp_id(struct intc_desc *desc, intc_enum enum_id) { @@ -755,13 +838,14 @@ static void __init intc_register_irq(struct intc_desc *desc, */ set_bit(irq, intc_irq_map); - /* Prefer single interrupt source bitmap over other combinations: + /* + * Prefer single interrupt source bitmap over other combinations: + * * 1. bitmap, single interrupt source * 2. priority, single interrupt source * 3. bitmap, multiple interrupt sources (groups) * 4. priority, multiple interrupt sources (groups) */ - data[0] = intc_mask_data(desc, d, enum_id, 0); data[1] = intc_prio_data(desc, d, enum_id, 0); @@ -786,7 +870,8 @@ static void __init intc_register_irq(struct intc_desc *desc, handle_level_irq, "level"); set_irq_chip_data(irq, (void *)data[primary]); - /* set priority level + /* + * set priority level * - this needs to be at least 2 for 5-bit priorities on 7780 */ intc_prio_level[irq] = default_prio_level; @@ -806,7 +891,6 @@ static void __init intc_register_irq(struct intc_desc *desc, * only secondary priority should access registers, so * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() */ - hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); } @@ -827,6 +911,11 @@ static void __init intc_register_irq(struct intc_desc *desc, if (desc->hw.ack_regs) ack_handle[irq] = intc_ack_data(desc, d, enum_id); +#ifdef CONFIG_INTC_BALANCING + if (desc->hw.mask_regs) + dist_handle[irq] = intc_dist_data(desc, d, enum_id); +#endif + #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */ #endif @@ -892,6 +981,10 @@ int __init register_intc_controller(struct intc_desc *desc) } d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; +#ifdef CONFIG_INTC_BALANCING + if (d->nr_reg) + d->nr_reg += hw->nr_mask_regs; +#endif d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; @@ -912,6 +1005,9 @@ int __init register_intc_controller(struct intc_desc *desc) smp = IS_SMP(hw->mask_regs[i]); k += save_reg(d, k, hw->mask_regs[i].set_reg, smp); k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp); +#ifdef CONFIG_INTC_BALANCING + k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0); +#endif } } diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h index f0e8cca199c7..0d6cd38e673d 100644 --- a/include/linux/sh_intc.h +++ b/include/linux/sh_intc.h @@ -23,6 +23,9 @@ struct intc_group { struct intc_mask_reg { unsigned long set_reg, clr_reg, reg_width; intc_enum enum_ids[32]; +#ifdef CONFIG_INTC_BALANCING + unsigned long dist_reg; +#endif #ifdef CONFIG_SMP unsigned long smp; #endif @@ -41,8 +44,14 @@ struct intc_sense_reg { intc_enum enum_ids[16]; }; +#ifdef CONFIG_INTC_BALANCING +#define INTC_SMP_BALANCING(reg) .dist_reg = (reg) +#else +#define INTC_SMP_BALANCING(reg) +#endif + #ifdef CONFIG_SMP -#define INTC_SMP(stride, nr) .smp = (stride) | ((nr) << 8) +#define INTC_SMP(stride, nr) .smp = (stride) | ((nr) << 8) #else #define INTC_SMP(stride, nr) #endif -- cgit v1.2.3-59-g8ed1b From 5bac942db3d2c4738df04104240d65a5d1eaec6a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 21 Apr 2010 15:36:49 +0000 Subject: SH: constify multiple DMA related objects and references to them Lists of DMA channels and slaves are not changed, make them constant. Besides, SH7724 channel and slave configuration of both DMA controllers is identical, remove the extra copy of the configuration data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh4a/setup-sh7722.c | 6 ++-- arch/sh/kernel/cpu/sh4a/setup-sh7724.c | 54 +++++----------------------------- arch/sh/kernel/cpu/sh4a/setup-sh7780.c | 6 ++-- arch/sh/kernel/cpu/sh4a/setup-sh7785.c | 6 ++-- arch/sh/kernel/cpu/sh4a/setup-sh7786.c | 4 +-- drivers/dma/shdma.c | 12 ++++---- include/linux/sh_dma.h | 9 +++--- 7 files changed, 30 insertions(+), 67 deletions(-) (limited to 'drivers') diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c index dc9b30d086a4..24c6167a7181 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c @@ -24,7 +24,7 @@ #include #include -static struct sh_dmae_slave_config sh7722_dmae_slaves[] = { +static const struct sh_dmae_slave_config sh7722_dmae_slaves[] = { { .slave_id = SHDMA_SLAVE_SCIF0_TX, .addr = 0xffe0000c, @@ -78,7 +78,7 @@ static struct sh_dmae_slave_config sh7722_dmae_slaves[] = { }, }; -static struct sh_dmae_channel sh7722_dmae_channels[] = { +static const struct sh_dmae_channel sh7722_dmae_channels[] = { { .offset = 0, .dmars = 0, @@ -106,7 +106,7 @@ static struct sh_dmae_channel sh7722_dmae_channels[] = { } }; -static unsigned int ts_shift[] = TS_SHIFT; +static const unsigned int ts_shift[] = TS_SHIFT; static struct sh_dmae_pdata dma_platform_data = { .slave = sh7722_dmae_slaves, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c index 8a0a4a99f86b..89fe16d20fdb 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c @@ -31,7 +31,7 @@ #include /* DMA */ -static struct sh_dmae_channel sh7724_dmae0_channels[] = { +static const struct sh_dmae_channel sh7724_dmae_channels[] = { { .offset = 0, .dmars = 0, @@ -59,51 +59,11 @@ static struct sh_dmae_channel sh7724_dmae0_channels[] = { } }; -static struct sh_dmae_channel sh7724_dmae1_channels[] = { - { - .offset = 0, - .dmars = 0, - .dmars_bit = 0, - }, { - .offset = 0x10, - .dmars = 0, - .dmars_bit = 8, - }, { - .offset = 0x20, - .dmars = 4, - .dmars_bit = 0, - }, { - .offset = 0x30, - .dmars = 4, - .dmars_bit = 8, - }, { - .offset = 0x50, - .dmars = 8, - .dmars_bit = 0, - }, { - .offset = 0x60, - .dmars = 8, - .dmars_bit = 8, - } -}; - -static unsigned int ts_shift[] = TS_SHIFT; - -static struct sh_dmae_pdata dma0_platform_data = { - .channel = sh7724_dmae0_channels, - .channel_num = ARRAY_SIZE(sh7724_dmae0_channels), - .ts_low_shift = CHCR_TS_LOW_SHIFT, - .ts_low_mask = CHCR_TS_LOW_MASK, - .ts_high_shift = CHCR_TS_HIGH_SHIFT, - .ts_high_mask = CHCR_TS_HIGH_MASK, - .ts_shift = ts_shift, - .ts_shift_num = ARRAY_SIZE(ts_shift), - .dmaor_init = DMAOR_INIT, -}; +static const unsigned int ts_shift[] = TS_SHIFT; -static struct sh_dmae_pdata dma1_platform_data = { - .channel = sh7724_dmae1_channels, - .channel_num = ARRAY_SIZE(sh7724_dmae1_channels), +static struct sh_dmae_pdata dma_platform_data = { + .channel = sh7724_dmae_channels, + .channel_num = ARRAY_SIZE(sh7724_dmae_channels), .ts_low_shift = CHCR_TS_LOW_SHIFT, .ts_low_mask = CHCR_TS_LOW_MASK, .ts_high_shift = CHCR_TS_HIGH_SHIFT, @@ -187,7 +147,7 @@ static struct platform_device dma0_device = { .resource = sh7724_dmae0_resources, .num_resources = ARRAY_SIZE(sh7724_dmae0_resources), .dev = { - .platform_data = &dma0_platform_data, + .platform_data = &dma_platform_data, }, .archdata = { .hwblk_id = HWBLK_DMAC0, @@ -200,7 +160,7 @@ static struct platform_device dma1_device = { .resource = sh7724_dmae1_resources, .num_resources = ARRAY_SIZE(sh7724_dmae1_resources), .dev = { - .platform_data = &dma1_platform_data, + .platform_data = &dma_platform_data, }, .archdata = { .hwblk_id = HWBLK_DMAC1, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c index 05fc38df1582..b12f537e4dde 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c @@ -233,7 +233,7 @@ static struct platform_device rtc_device = { }; /* DMA */ -static struct sh_dmae_channel sh7780_dmae0_channels[] = { +static const struct sh_dmae_channel sh7780_dmae0_channels[] = { { .offset = 0, .dmars = 0, @@ -261,7 +261,7 @@ static struct sh_dmae_channel sh7780_dmae0_channels[] = { } }; -static struct sh_dmae_channel sh7780_dmae1_channels[] = { +static const struct sh_dmae_channel sh7780_dmae1_channels[] = { { .offset = 0, }, { @@ -277,7 +277,7 @@ static struct sh_dmae_channel sh7780_dmae1_channels[] = { } }; -static unsigned int ts_shift[] = TS_SHIFT; +static const unsigned int ts_shift[] = TS_SHIFT; static struct sh_dmae_pdata dma0_platform_data = { .channel = sh7780_dmae0_channels, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c index 07bb2d4619f8..f3e3ea0ce050 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c @@ -275,7 +275,7 @@ static struct platform_device tmu5_device = { }; /* DMA */ -static struct sh_dmae_channel sh7785_dmae0_channels[] = { +static const struct sh_dmae_channel sh7785_dmae0_channels[] = { { .offset = 0, .dmars = 0, @@ -303,7 +303,7 @@ static struct sh_dmae_channel sh7785_dmae0_channels[] = { } }; -static struct sh_dmae_channel sh7785_dmae1_channels[] = { +static const struct sh_dmae_channel sh7785_dmae1_channels[] = { { .offset = 0, }, { @@ -319,7 +319,7 @@ static struct sh_dmae_channel sh7785_dmae1_channels[] = { } }; -static unsigned int ts_shift[] = TS_SHIFT; +static const unsigned int ts_shift[] = TS_SHIFT; static struct sh_dmae_pdata dma0_platform_data = { .channel = sh7785_dmae0_channels, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c index d7336036d04d..81657091da46 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c @@ -445,7 +445,7 @@ static struct platform_device tmu11_device = { .num_resources = ARRAY_SIZE(tmu11_resources), }; -static struct sh_dmae_channel dmac0_channels[] = { +static const struct sh_dmae_channel dmac0_channels[] = { { .offset = 0, .dmars = 0, @@ -473,7 +473,7 @@ static struct sh_dmae_channel dmac0_channels[] = { } }; -static unsigned int ts_shift[] = TS_SHIFT; +static const unsigned int ts_shift[] = TS_SHIFT; static struct sh_dmae_pdata dma0_platform_data = { .channel = dmac0_channels, diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 7a18b580f626..aab352a63a4a 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -188,7 +188,7 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) struct sh_dmae_device *shdev = container_of(sh_chan->common.device, struct sh_dmae_device, common); struct sh_dmae_pdata *pdata = shdev->pdata; - struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id]; + const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id]; u16 __iomem *addr = shdev->dmars + chan_pdata->dmars / sizeof(u16); int shift = chan_pdata->dmars_bit; @@ -264,7 +264,7 @@ static struct sh_desc *sh_dmae_get_desc(struct sh_dmae_chan *sh_chan) return NULL; } -static struct sh_dmae_slave_config *sh_dmae_find_slave( +static const struct sh_dmae_slave_config *sh_dmae_find_slave( struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *param) { struct dma_device *dma_dev = sh_chan->common.device; @@ -296,7 +296,7 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) * never runs concurrently with itself or free_chan_resources. */ if (param) { - struct sh_dmae_slave_config *cfg; + const struct sh_dmae_slave_config *cfg; cfg = sh_dmae_find_slave(sh_chan, param); if (!cfg) @@ -557,12 +557,14 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg( { struct sh_dmae_slave *param; struct sh_dmae_chan *sh_chan; + dma_addr_t slave_addr; if (!chan) return NULL; sh_chan = to_sh_chan(chan); param = chan->private; + slave_addr = param->config->addr; /* Someone calling slave DMA on a public channel? */ if (!param || !sg_len) { @@ -575,7 +577,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg( * if (param != NULL), this is a successfully requested slave channel, * therefore param->config != NULL too. */ - return sh_dmae_prep_sg(sh_chan, sgl, sg_len, ¶m->config->addr, + return sh_dmae_prep_sg(sh_chan, sgl, sg_len, &slave_addr, direction, flags); } @@ -856,7 +858,7 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id, int irq, unsigned long flags) { int err; - struct sh_dmae_channel *chan_pdata = &shdev->pdata->channel[id]; + const struct sh_dmae_channel *chan_pdata = &shdev->pdata->channel[id]; struct platform_device *pdev = to_platform_device(shdev->common.dev); struct sh_dmae_chan *new_sh_chan; diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index cdaaff424211..b08cd4efa15c 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -17,7 +17,7 @@ struct sh_dmae_slave { unsigned int slave_id; /* Set by the platform */ struct device *dma_dev; /* Set by the platform */ - struct sh_dmae_slave_config *config; /* Set by the driver */ + const struct sh_dmae_slave_config *config; /* Set by the driver */ }; struct sh_dmae_regs { @@ -36,6 +36,7 @@ struct sh_desc { int chunks; int mark; }; + struct sh_dmae_slave_config { unsigned int slave_id; dma_addr_t addr; @@ -50,15 +51,15 @@ struct sh_dmae_channel { }; struct sh_dmae_pdata { - struct sh_dmae_slave_config *slave; + const struct sh_dmae_slave_config *slave; int slave_num; - struct sh_dmae_channel *channel; + const struct sh_dmae_channel *channel; int channel_num; unsigned int ts_low_shift; unsigned int ts_low_mask; unsigned int ts_high_shift; unsigned int ts_high_mask; - unsigned int *ts_shift; + const unsigned int *ts_shift; int ts_shift_num; u16 dmaor_init; }; -- cgit v1.2.3-59-g8ed1b From 8b5ee113e1b97097e992a0301d0cac2530b31fc2 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 11 May 2010 13:29:25 +0000 Subject: sh: move sh clock.c contents to drivers/sh/clk. This patch is V2 of the SH clock framework move from arch/sh/kernel/cpu/clock.c to drivers/sh/clk.c. All code except the following functions are moved: clk_init(), clk_get() and clk_put(). The init function is still kept in clock.c since it depends on the SH-specific machvec implementation. The symbols clk_get() and clk_put() already exist in the common ARM clkdev code, those symbols are left in the SH tree to avoid duplicating them for SH-Mobile ARM. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/sh/include/asm/clock.h | 3 + arch/sh/kernel/cpu/clock.c | 543 +------------------------------------------ drivers/sh/Makefile | 1 + drivers/sh/clk.c | 548 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/sh_clk.h | 2 +- 5 files changed, 561 insertions(+), 536 deletions(-) create mode 100644 drivers/sh/clk.c (limited to 'drivers') diff --git a/arch/sh/include/asm/clock.h b/arch/sh/include/asm/clock.h index f387e5caee16..803d4c7f09dc 100644 --- a/arch/sh/include/asm/clock.h +++ b/arch/sh/include/asm/clock.h @@ -10,4 +10,7 @@ int __init arch_clk_init(void); /* arch/sh/kernel/cpu/clock-cpg.c */ int __init __deprecated cpg_clk_init(void); +/* arch/sh/kernel/cpu/clock.c */ +int clk_init(void); + #endif /* __ASM_SH_CLOCK_H */ diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c index 8cc6935d91ae..50f887dda565 100644 --- a/arch/sh/kernel/cpu/clock.c +++ b/arch/sh/kernel/cpu/clock.c @@ -16,467 +16,10 @@ */ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include -static LIST_HEAD(clock_list); -static DEFINE_SPINLOCK(clock_lock); -static DEFINE_MUTEX(clock_list_sem); - -void clk_rate_table_build(struct clk *clk, - struct cpufreq_frequency_table *freq_table, - int nr_freqs, - struct clk_div_mult_table *src_table, - unsigned long *bitmap) -{ - unsigned long mult, div; - unsigned long freq; - int i; - - for (i = 0; i < nr_freqs; i++) { - div = 1; - mult = 1; - - if (src_table->divisors && i < src_table->nr_divisors) - div = src_table->divisors[i]; - - if (src_table->multipliers && i < src_table->nr_multipliers) - mult = src_table->multipliers[i]; - - if (!div || !mult || (bitmap && !test_bit(i, bitmap))) - freq = CPUFREQ_ENTRY_INVALID; - else - freq = clk->parent->rate * mult / div; - - freq_table[i].index = i; - freq_table[i].frequency = freq; - } - - /* Termination entry */ - freq_table[i].index = i; - freq_table[i].frequency = CPUFREQ_TABLE_END; -} - -long clk_rate_table_round(struct clk *clk, - struct cpufreq_frequency_table *freq_table, - unsigned long rate) -{ - unsigned long rate_error, rate_error_prev = ~0UL; - unsigned long rate_best_fit = rate; - unsigned long highest, lowest; - int i; - - highest = lowest = 0; - - for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - unsigned long freq = freq_table[i].frequency; - - if (freq == CPUFREQ_ENTRY_INVALID) - continue; - - if (freq > highest) - highest = freq; - if (freq < lowest) - lowest = freq; - - rate_error = abs(freq - rate); - if (rate_error < rate_error_prev) { - rate_best_fit = freq; - rate_error_prev = rate_error; - } - - if (rate_error == 0) - break; - } - - if (rate >= highest) - rate_best_fit = highest; - if (rate <= lowest) - rate_best_fit = lowest; - - return rate_best_fit; -} - -int clk_rate_table_find(struct clk *clk, - struct cpufreq_frequency_table *freq_table, - unsigned long rate) -{ - int i; - - for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - unsigned long freq = freq_table[i].frequency; - - if (freq == CPUFREQ_ENTRY_INVALID) - continue; - - if (freq == rate) - return i; - } - - return -ENOENT; -} - -/* Used for clocks that always have same value as the parent clock */ -unsigned long followparent_recalc(struct clk *clk) -{ - return clk->parent ? clk->parent->rate : 0; -} - -int clk_reparent(struct clk *child, struct clk *parent) -{ - list_del_init(&child->sibling); - if (parent) - list_add(&child->sibling, &parent->children); - child->parent = parent; - - /* now do the debugfs renaming to reattach the child - to the proper parent */ - - return 0; -} - -/* Propagate rate to children */ -void propagate_rate(struct clk *tclk) -{ - struct clk *clkp; - - list_for_each_entry(clkp, &tclk->children, sibling) { - if (clkp->ops && clkp->ops->recalc) - clkp->rate = clkp->ops->recalc(clkp); - - propagate_rate(clkp); - } -} - -static void __clk_disable(struct clk *clk) -{ - if (clk->usecount == 0) { - printk(KERN_ERR "Trying disable clock %s with 0 usecount\n", - clk->name); - WARN_ON(1); - return; - } - - if (!(--clk->usecount)) { - if (likely(clk->ops && clk->ops->disable)) - clk->ops->disable(clk); - if (likely(clk->parent)) - __clk_disable(clk->parent); - } -} - -void clk_disable(struct clk *clk) -{ - unsigned long flags; - - if (!clk) - return; - - spin_lock_irqsave(&clock_lock, flags); - __clk_disable(clk); - spin_unlock_irqrestore(&clock_lock, flags); -} -EXPORT_SYMBOL_GPL(clk_disable); - -static int __clk_enable(struct clk *clk) -{ - int ret = 0; - - if (clk->usecount++ == 0) { - if (clk->parent) { - ret = __clk_enable(clk->parent); - if (unlikely(ret)) - goto err; - } - - if (clk->ops && clk->ops->enable) { - ret = clk->ops->enable(clk); - if (ret) { - if (clk->parent) - __clk_disable(clk->parent); - goto err; - } - } - } - - return ret; -err: - clk->usecount--; - return ret; -} - -int clk_enable(struct clk *clk) -{ - unsigned long flags; - int ret; - - if (!clk) - return -EINVAL; - - spin_lock_irqsave(&clock_lock, flags); - ret = __clk_enable(clk); - spin_unlock_irqrestore(&clock_lock, flags); - - return ret; -} -EXPORT_SYMBOL_GPL(clk_enable); - -static LIST_HEAD(root_clks); - -/** - * recalculate_root_clocks - recalculate and propagate all root clocks - * - * Recalculates all root clocks (clocks with no parent), which if the - * clock's .recalc is set correctly, should also propagate their rates. - * Called at init. - */ -void recalculate_root_clocks(void) -{ - struct clk *clkp; - - list_for_each_entry(clkp, &root_clks, sibling) { - if (clkp->ops && clkp->ops->recalc) - clkp->rate = clkp->ops->recalc(clkp); - propagate_rate(clkp); - } -} - -int clk_register(struct clk *clk) -{ - if (clk == NULL || IS_ERR(clk)) - return -EINVAL; - - /* - * trap out already registered clocks - */ - if (clk->node.next || clk->node.prev) - return 0; - - mutex_lock(&clock_list_sem); - - INIT_LIST_HEAD(&clk->children); - clk->usecount = 0; - - if (clk->parent) - list_add(&clk->sibling, &clk->parent->children); - else - list_add(&clk->sibling, &root_clks); - - list_add(&clk->node, &clock_list); - if (clk->ops && clk->ops->init) - clk->ops->init(clk); - mutex_unlock(&clock_list_sem); - - return 0; -} -EXPORT_SYMBOL_GPL(clk_register); - -void clk_unregister(struct clk *clk) -{ - mutex_lock(&clock_list_sem); - list_del(&clk->sibling); - list_del(&clk->node); - mutex_unlock(&clock_list_sem); -} -EXPORT_SYMBOL_GPL(clk_unregister); - -static void clk_enable_init_clocks(void) -{ - struct clk *clkp; - - list_for_each_entry(clkp, &clock_list, node) - if (clkp->flags & CLK_ENABLE_ON_INIT) - clk_enable(clkp); -} - -unsigned long clk_get_rate(struct clk *clk) -{ - return clk->rate; -} -EXPORT_SYMBOL_GPL(clk_get_rate); - -int clk_set_rate(struct clk *clk, unsigned long rate) -{ - return clk_set_rate_ex(clk, rate, 0); -} -EXPORT_SYMBOL_GPL(clk_set_rate); - -int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id) -{ - int ret = -EOPNOTSUPP; - unsigned long flags; - - spin_lock_irqsave(&clock_lock, flags); - - if (likely(clk->ops && clk->ops->set_rate)) { - ret = clk->ops->set_rate(clk, rate, algo_id); - if (ret != 0) - goto out_unlock; - } else { - clk->rate = rate; - ret = 0; - } - - if (clk->ops && clk->ops->recalc) - clk->rate = clk->ops->recalc(clk); - - propagate_rate(clk); - -out_unlock: - spin_unlock_irqrestore(&clock_lock, flags); - - return ret; -} -EXPORT_SYMBOL_GPL(clk_set_rate_ex); - -int clk_set_parent(struct clk *clk, struct clk *parent) -{ - unsigned long flags; - int ret = -EINVAL; - - if (!parent || !clk) - return ret; - if (clk->parent == parent) - return 0; - - spin_lock_irqsave(&clock_lock, flags); - if (clk->usecount == 0) { - if (clk->ops->set_parent) - ret = clk->ops->set_parent(clk, parent); - else - ret = clk_reparent(clk, parent); - - if (ret == 0) { - pr_debug("clock: set parent of %s to %s (new rate %ld)\n", - clk->name, clk->parent->name, clk->rate); - if (clk->ops->recalc) - clk->rate = clk->ops->recalc(clk); - propagate_rate(clk); - } - } else - ret = -EBUSY; - spin_unlock_irqrestore(&clock_lock, flags); - - return ret; -} -EXPORT_SYMBOL_GPL(clk_set_parent); - -struct clk *clk_get_parent(struct clk *clk) -{ - return clk->parent; -} -EXPORT_SYMBOL_GPL(clk_get_parent); - -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - if (likely(clk->ops && clk->ops->round_rate)) { - unsigned long flags, rounded; - - spin_lock_irqsave(&clock_lock, flags); - rounded = clk->ops->round_rate(clk, rate); - spin_unlock_irqrestore(&clock_lock, flags); - - return rounded; - } - - return clk_get_rate(clk); -} -EXPORT_SYMBOL_GPL(clk_round_rate); - -/* - * Returns a clock. Note that we first try to use device id on the bus - * and clock name. If this fails, we try to use clock name only. - */ -struct clk *clk_get(struct device *dev, const char *con_id) -{ - const char *dev_id = dev ? dev_name(dev) : NULL; - - return clk_get_sys(dev_id, con_id); -} -EXPORT_SYMBOL_GPL(clk_get); - -void clk_put(struct clk *clk) -{ -} -EXPORT_SYMBOL_GPL(clk_put); - -#ifdef CONFIG_PM -static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state) -{ - static pm_message_t prev_state; - struct clk *clkp; - - switch (state.event) { - case PM_EVENT_ON: - /* Resumeing from hibernation */ - if (prev_state.event != PM_EVENT_FREEZE) - break; - - list_for_each_entry(clkp, &clock_list, node) { - if (likely(clkp->ops)) { - unsigned long rate = clkp->rate; - - if (likely(clkp->ops->set_parent)) - clkp->ops->set_parent(clkp, - clkp->parent); - if (likely(clkp->ops->set_rate)) - clkp->ops->set_rate(clkp, - rate, NO_CHANGE); - else if (likely(clkp->ops->recalc)) - clkp->rate = clkp->ops->recalc(clkp); - } - } - break; - case PM_EVENT_FREEZE: - break; - case PM_EVENT_SUSPEND: - break; - } - - prev_state = state; - return 0; -} - -static int clks_sysdev_resume(struct sys_device *dev) -{ - return clks_sysdev_suspend(dev, PMSG_ON); -} - -static struct sysdev_class clks_sysdev_class = { - .name = "clks", -}; - -static struct sysdev_driver clks_sysdev_driver = { - .suspend = clks_sysdev_suspend, - .resume = clks_sysdev_resume, -}; - -static struct sys_device clks_sysdev_dev = { - .cls = &clks_sysdev_class, -}; - -static int __init clk_sysdev_init(void) -{ - sysdev_class_register(&clks_sysdev_class); - sysdev_driver_register(&clks_sysdev_class, &clks_sysdev_driver); - sysdev_register(&clks_sysdev_dev); - - return 0; -} -subsys_initcall(clk_sysdev_init); -#endif - int __init clk_init(void) { int ret; @@ -506,89 +49,19 @@ int __init clk_init(void) } /* - * debugfs support to trace clock tree hierarchy and attributes + * Returns a clock. Note that we first try to use device id on the bus + * and clock name. If this fails, we try to use clock name only. */ -static struct dentry *clk_debugfs_root; - -static int clk_debugfs_register_one(struct clk *c) +struct clk *clk_get(struct device *dev, const char *con_id) { - int err; - struct dentry *d, *child, *child_tmp; - struct clk *pa = c->parent; - char s[255]; - char *p = s; - - p += sprintf(p, "%s", c->name); - if (c->id >= 0) - sprintf(p, ":%d", c->id); - d = debugfs_create_dir(s, pa ? pa->dentry : clk_debugfs_root); - if (!d) - return -ENOMEM; - c->dentry = d; - - d = debugfs_create_u8("usecount", S_IRUGO, c->dentry, (u8 *)&c->usecount); - if (!d) { - err = -ENOMEM; - goto err_out; - } - d = debugfs_create_u32("rate", S_IRUGO, c->dentry, (u32 *)&c->rate); - if (!d) { - err = -ENOMEM; - goto err_out; - } - d = debugfs_create_x32("flags", S_IRUGO, c->dentry, (u32 *)&c->flags); - if (!d) { - err = -ENOMEM; - goto err_out; - } - return 0; + const char *dev_id = dev ? dev_name(dev) : NULL; -err_out: - d = c->dentry; - list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child) - debugfs_remove(child); - debugfs_remove(c->dentry); - return err; + return clk_get_sys(dev_id, con_id); } +EXPORT_SYMBOL_GPL(clk_get); -static int clk_debugfs_register(struct clk *c) +void clk_put(struct clk *clk) { - int err; - struct clk *pa = c->parent; - - if (pa && !pa->dentry) { - err = clk_debugfs_register(pa); - if (err) - return err; - } - - if (!c->dentry && c->name) { - err = clk_debugfs_register_one(c); - if (err) - return err; - } - return 0; } +EXPORT_SYMBOL_GPL(clk_put); -static int __init clk_debugfs_init(void) -{ - struct clk *c; - struct dentry *d; - int err; - - d = debugfs_create_dir("clock", NULL); - if (!d) - return -ENOMEM; - clk_debugfs_root = d; - - list_for_each_entry(c, &clock_list, node) { - err = clk_debugfs_register(c); - if (err) - goto err_out; - } - return 0; -err_out: - debugfs_remove_recursive(clk_debugfs_root); - return err; -} -late_initcall(clk_debugfs_init); diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 4956bf1f2134..033a949c496a 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_MAPLE) += maple/ obj-$(CONFIG_GENERIC_GPIO) += pfc.o +obj-$(CONFIG_SUPERH) += clk.o obj-y += intc.o diff --git a/drivers/sh/clk.c b/drivers/sh/clk.c new file mode 100644 index 000000000000..c90a3e1e1085 --- /dev/null +++ b/drivers/sh/clk.c @@ -0,0 +1,548 @@ +/* + * drivers/sh/clk.c - SuperH clock framework + * + * Copyright (C) 2005 - 2009 Paul Mundt + * + * This clock framework is derived from the OMAP version by: + * + * Copyright (C) 2004 - 2008 Nokia Corporation + * Written by Tuukka Tikkanen + * + * Modified for omap shared clock framework by Tony Lindgren + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(clock_list); +static DEFINE_SPINLOCK(clock_lock); +static DEFINE_MUTEX(clock_list_sem); + +void clk_rate_table_build(struct clk *clk, + struct cpufreq_frequency_table *freq_table, + int nr_freqs, + struct clk_div_mult_table *src_table, + unsigned long *bitmap) +{ + unsigned long mult, div; + unsigned long freq; + int i; + + for (i = 0; i < nr_freqs; i++) { + div = 1; + mult = 1; + + if (src_table->divisors && i < src_table->nr_divisors) + div = src_table->divisors[i]; + + if (src_table->multipliers && i < src_table->nr_multipliers) + mult = src_table->multipliers[i]; + + if (!div || !mult || (bitmap && !test_bit(i, bitmap))) + freq = CPUFREQ_ENTRY_INVALID; + else + freq = clk->parent->rate * mult / div; + + freq_table[i].index = i; + freq_table[i].frequency = freq; + } + + /* Termination entry */ + freq_table[i].index = i; + freq_table[i].frequency = CPUFREQ_TABLE_END; +} + +long clk_rate_table_round(struct clk *clk, + struct cpufreq_frequency_table *freq_table, + unsigned long rate) +{ + unsigned long rate_error, rate_error_prev = ~0UL; + unsigned long rate_best_fit = rate; + unsigned long highest, lowest; + int i; + + highest = lowest = 0; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + unsigned long freq = freq_table[i].frequency; + + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + + if (freq > highest) + highest = freq; + if (freq < lowest) + lowest = freq; + + rate_error = abs(freq - rate); + if (rate_error < rate_error_prev) { + rate_best_fit = freq; + rate_error_prev = rate_error; + } + + if (rate_error == 0) + break; + } + + if (rate >= highest) + rate_best_fit = highest; + if (rate <= lowest) + rate_best_fit = lowest; + + return rate_best_fit; +} + +int clk_rate_table_find(struct clk *clk, + struct cpufreq_frequency_table *freq_table, + unsigned long rate) +{ + int i; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + unsigned long freq = freq_table[i].frequency; + + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + + if (freq == rate) + return i; + } + + return -ENOENT; +} + +/* Used for clocks that always have same value as the parent clock */ +unsigned long followparent_recalc(struct clk *clk) +{ + return clk->parent ? clk->parent->rate : 0; +} + +int clk_reparent(struct clk *child, struct clk *parent) +{ + list_del_init(&child->sibling); + if (parent) + list_add(&child->sibling, &parent->children); + child->parent = parent; + + /* now do the debugfs renaming to reattach the child + to the proper parent */ + + return 0; +} + +/* Propagate rate to children */ +void propagate_rate(struct clk *tclk) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &tclk->children, sibling) { + if (clkp->ops && clkp->ops->recalc) + clkp->rate = clkp->ops->recalc(clkp); + + propagate_rate(clkp); + } +} + +static void __clk_disable(struct clk *clk) +{ + if (clk->usecount == 0) { + printk(KERN_ERR "Trying disable clock %s with 0 usecount\n", + clk->name); + WARN_ON(1); + return; + } + + if (!(--clk->usecount)) { + if (likely(clk->ops && clk->ops->disable)) + clk->ops->disable(clk); + if (likely(clk->parent)) + __clk_disable(clk->parent); + } +} + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + if (!clk) + return; + + spin_lock_irqsave(&clock_lock, flags); + __clk_disable(clk); + spin_unlock_irqrestore(&clock_lock, flags); +} +EXPORT_SYMBOL_GPL(clk_disable); + +static int __clk_enable(struct clk *clk) +{ + int ret = 0; + + if (clk->usecount++ == 0) { + if (clk->parent) { + ret = __clk_enable(clk->parent); + if (unlikely(ret)) + goto err; + } + + if (clk->ops && clk->ops->enable) { + ret = clk->ops->enable(clk); + if (ret) { + if (clk->parent) + __clk_disable(clk->parent); + goto err; + } + } + } + + return ret; +err: + clk->usecount--; + return ret; +} + +int clk_enable(struct clk *clk) +{ + unsigned long flags; + int ret; + + if (!clk) + return -EINVAL; + + spin_lock_irqsave(&clock_lock, flags); + ret = __clk_enable(clk); + spin_unlock_irqrestore(&clock_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(clk_enable); + +static LIST_HEAD(root_clks); + +/** + * recalculate_root_clocks - recalculate and propagate all root clocks + * + * Recalculates all root clocks (clocks with no parent), which if the + * clock's .recalc is set correctly, should also propagate their rates. + * Called at init. + */ +void recalculate_root_clocks(void) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &root_clks, sibling) { + if (clkp->ops && clkp->ops->recalc) + clkp->rate = clkp->ops->recalc(clkp); + propagate_rate(clkp); + } +} + +int clk_register(struct clk *clk) +{ + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + + /* + * trap out already registered clocks + */ + if (clk->node.next || clk->node.prev) + return 0; + + mutex_lock(&clock_list_sem); + + INIT_LIST_HEAD(&clk->children); + clk->usecount = 0; + + if (clk->parent) + list_add(&clk->sibling, &clk->parent->children); + else + list_add(&clk->sibling, &root_clks); + + list_add(&clk->node, &clock_list); + if (clk->ops && clk->ops->init) + clk->ops->init(clk); + mutex_unlock(&clock_list_sem); + + return 0; +} +EXPORT_SYMBOL_GPL(clk_register); + +void clk_unregister(struct clk *clk) +{ + mutex_lock(&clock_list_sem); + list_del(&clk->sibling); + list_del(&clk->node); + mutex_unlock(&clock_list_sem); +} +EXPORT_SYMBOL_GPL(clk_unregister); + +void clk_enable_init_clocks(void) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &clock_list, node) + if (clkp->flags & CLK_ENABLE_ON_INIT) + clk_enable(clkp); +} + +unsigned long clk_get_rate(struct clk *clk) +{ + return clk->rate; +} +EXPORT_SYMBOL_GPL(clk_get_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + return clk_set_rate_ex(clk, rate, 0); +} +EXPORT_SYMBOL_GPL(clk_set_rate); + +int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id) +{ + int ret = -EOPNOTSUPP; + unsigned long flags; + + spin_lock_irqsave(&clock_lock, flags); + + if (likely(clk->ops && clk->ops->set_rate)) { + ret = clk->ops->set_rate(clk, rate, algo_id); + if (ret != 0) + goto out_unlock; + } else { + clk->rate = rate; + ret = 0; + } + + if (clk->ops && clk->ops->recalc) + clk->rate = clk->ops->recalc(clk); + + propagate_rate(clk); + +out_unlock: + spin_unlock_irqrestore(&clock_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(clk_set_rate_ex); + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + unsigned long flags; + int ret = -EINVAL; + + if (!parent || !clk) + return ret; + if (clk->parent == parent) + return 0; + + spin_lock_irqsave(&clock_lock, flags); + if (clk->usecount == 0) { + if (clk->ops->set_parent) + ret = clk->ops->set_parent(clk, parent); + else + ret = clk_reparent(clk, parent); + + if (ret == 0) { + pr_debug("clock: set parent of %s to %s (new rate %ld)\n", + clk->name, clk->parent->name, clk->rate); + if (clk->ops->recalc) + clk->rate = clk->ops->recalc(clk); + propagate_rate(clk); + } + } else + ret = -EBUSY; + spin_unlock_irqrestore(&clock_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(clk_set_parent); + +struct clk *clk_get_parent(struct clk *clk) +{ + return clk->parent; +} +EXPORT_SYMBOL_GPL(clk_get_parent); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (likely(clk->ops && clk->ops->round_rate)) { + unsigned long flags, rounded; + + spin_lock_irqsave(&clock_lock, flags); + rounded = clk->ops->round_rate(clk, rate); + spin_unlock_irqrestore(&clock_lock, flags); + + return rounded; + } + + return clk_get_rate(clk); +} +EXPORT_SYMBOL_GPL(clk_round_rate); + +#ifdef CONFIG_PM +static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state) +{ + static pm_message_t prev_state; + struct clk *clkp; + + switch (state.event) { + case PM_EVENT_ON: + /* Resumeing from hibernation */ + if (prev_state.event != PM_EVENT_FREEZE) + break; + + list_for_each_entry(clkp, &clock_list, node) { + if (likely(clkp->ops)) { + unsigned long rate = clkp->rate; + + if (likely(clkp->ops->set_parent)) + clkp->ops->set_parent(clkp, + clkp->parent); + if (likely(clkp->ops->set_rate)) + clkp->ops->set_rate(clkp, + rate, NO_CHANGE); + else if (likely(clkp->ops->recalc)) + clkp->rate = clkp->ops->recalc(clkp); + } + } + break; + case PM_EVENT_FREEZE: + break; + case PM_EVENT_SUSPEND: + break; + } + + prev_state = state; + return 0; +} + +static int clks_sysdev_resume(struct sys_device *dev) +{ + return clks_sysdev_suspend(dev, PMSG_ON); +} + +static struct sysdev_class clks_sysdev_class = { + .name = "clks", +}; + +static struct sysdev_driver clks_sysdev_driver = { + .suspend = clks_sysdev_suspend, + .resume = clks_sysdev_resume, +}; + +static struct sys_device clks_sysdev_dev = { + .cls = &clks_sysdev_class, +}; + +static int __init clk_sysdev_init(void) +{ + sysdev_class_register(&clks_sysdev_class); + sysdev_driver_register(&clks_sysdev_class, &clks_sysdev_driver); + sysdev_register(&clks_sysdev_dev); + + return 0; +} +subsys_initcall(clk_sysdev_init); +#endif + +/* + * debugfs support to trace clock tree hierarchy and attributes + */ +static struct dentry *clk_debugfs_root; + +static int clk_debugfs_register_one(struct clk *c) +{ + int err; + struct dentry *d, *child, *child_tmp; + struct clk *pa = c->parent; + char s[255]; + char *p = s; + + p += sprintf(p, "%s", c->name); + if (c->id >= 0) + sprintf(p, ":%d", c->id); + d = debugfs_create_dir(s, pa ? pa->dentry : clk_debugfs_root); + if (!d) + return -ENOMEM; + c->dentry = d; + + d = debugfs_create_u8("usecount", S_IRUGO, c->dentry, (u8 *)&c->usecount); + if (!d) { + err = -ENOMEM; + goto err_out; + } + d = debugfs_create_u32("rate", S_IRUGO, c->dentry, (u32 *)&c->rate); + if (!d) { + err = -ENOMEM; + goto err_out; + } + d = debugfs_create_x32("flags", S_IRUGO, c->dentry, (u32 *)&c->flags); + if (!d) { + err = -ENOMEM; + goto err_out; + } + return 0; + +err_out: + d = c->dentry; + list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child) + debugfs_remove(child); + debugfs_remove(c->dentry); + return err; +} + +static int clk_debugfs_register(struct clk *c) +{ + int err; + struct clk *pa = c->parent; + + if (pa && !pa->dentry) { + err = clk_debugfs_register(pa); + if (err) + return err; + } + + if (!c->dentry && c->name) { + err = clk_debugfs_register_one(c); + if (err) + return err; + } + return 0; +} + +static int __init clk_debugfs_init(void) +{ + struct clk *c; + struct dentry *d; + int err; + + d = debugfs_create_dir("clock", NULL); + if (!d) + return -ENOMEM; + clk_debugfs_root = d; + + list_for_each_entry(c, &clock_list, node) { + err = clk_debugfs_register(c); + if (err) + goto err_out; + } + return 0; +err_out: + debugfs_remove_recursive(clk_debugfs_root); + return err; +} +late_initcall(clk_debugfs_init); diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index de911451c216..6d7de242be1d 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -47,13 +47,13 @@ struct clk { #define CLK_ENABLE_ON_INIT (1 << 0) /* arch/sh/kernel/cpu/clock.c */ -int clk_init(void); unsigned long followparent_recalc(struct clk *); void recalculate_root_clocks(void); void propagate_rate(struct clk *); int clk_reparent(struct clk *child, struct clk *parent); int clk_register(struct clk *); void clk_unregister(struct clk *); +void clk_enable_init_clocks(void); /* the exported API, in addition to clk_set_rate */ /** -- cgit v1.2.3-59-g8ed1b From fa676ca3944e4459ea3d133eabc923c8ab5d2576 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 11 May 2010 13:29:34 +0000 Subject: sh: move sh clock-cpg.c contents to drivers/sh/clk-cpg.c Move the CPG helpers to drivers/sh/clk-cpg.c V2. This to allow SH-Mobile ARM to share the code with SH. All functions except the legacy CPG stuff is moved. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/Makefile | 2 +- arch/sh/kernel/cpu/clock-cpg.c | 295 ---------------------------------------- drivers/sh/Makefile | 1 + drivers/sh/clk-cpg.c | 298 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 300 insertions(+), 296 deletions(-) create mode 100644 drivers/sh/clk-cpg.c (limited to 'drivers') diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile index 0e48bc61c272..4edcb60a1355 100644 --- a/arch/sh/kernel/cpu/Makefile +++ b/arch/sh/kernel/cpu/Makefile @@ -16,7 +16,7 @@ obj-$(CONFIG_ARCH_SHMOBILE) += shmobile/ # Common interfaces. obj-$(CONFIG_SH_ADC) += adc.o -obj-$(CONFIG_SH_CLK_CPG) += clock-cpg.o +obj-$(CONFIG_SH_CLK_CPG_LEGACY) += clock-cpg.o obj-$(CONFIG_SH_FPU) += fpu.o obj-$(CONFIG_SH_FPU_EMU) += fpu.o diff --git a/arch/sh/kernel/cpu/clock-cpg.c b/arch/sh/kernel/cpu/clock-cpg.c index 69c6472cdf58..e2f63d68da51 100644 --- a/arch/sh/kernel/cpu/clock-cpg.c +++ b/arch/sh/kernel/cpu/clock-cpg.c @@ -5,300 +5,6 @@ #include #include -static int sh_clk_mstp32_enable(struct clk *clk) -{ - __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << clk->enable_bit), - clk->enable_reg); - return 0; -} - -static void sh_clk_mstp32_disable(struct clk *clk) -{ - __raw_writel(__raw_readl(clk->enable_reg) | (1 << clk->enable_bit), - clk->enable_reg); -} - -static struct clk_ops sh_clk_mstp32_clk_ops = { - .enable = sh_clk_mstp32_enable, - .disable = sh_clk_mstp32_disable, - .recalc = followparent_recalc, -}; - -int __init sh_clk_mstp32_register(struct clk *clks, int nr) -{ - struct clk *clkp; - int ret = 0; - int k; - - for (k = 0; !ret && (k < nr); k++) { - clkp = clks + k; - clkp->ops = &sh_clk_mstp32_clk_ops; - ret |= clk_register(clkp); - } - - return ret; -} - -static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) -{ - return clk_rate_table_round(clk, clk->freq_table, rate); -} - -static int sh_clk_div6_divisors[64] = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 -}; - -static struct clk_div_mult_table sh_clk_div6_table = { - .divisors = sh_clk_div6_divisors, - .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), -}; - -static unsigned long sh_clk_div6_recalc(struct clk *clk) -{ - struct clk_div_mult_table *table = &sh_clk_div6_table; - unsigned int idx; - - clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, - table, NULL); - - idx = __raw_readl(clk->enable_reg) & 0x003f; - - return clk->freq_table[idx].frequency; -} - -static int sh_clk_div6_set_rate(struct clk *clk, - unsigned long rate, int algo_id) -{ - unsigned long value; - int idx; - - idx = clk_rate_table_find(clk, clk->freq_table, rate); - if (idx < 0) - return idx; - - value = __raw_readl(clk->enable_reg); - value &= ~0x3f; - value |= idx; - __raw_writel(value, clk->enable_reg); - return 0; -} - -static int sh_clk_div6_enable(struct clk *clk) -{ - unsigned long value; - int ret; - - ret = sh_clk_div6_set_rate(clk, clk->rate, 0); - if (ret == 0) { - value = __raw_readl(clk->enable_reg); - value &= ~0x100; /* clear stop bit to enable clock */ - __raw_writel(value, clk->enable_reg); - } - return ret; -} - -static void sh_clk_div6_disable(struct clk *clk) -{ - unsigned long value; - - value = __raw_readl(clk->enable_reg); - value |= 0x100; /* stop clock */ - value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */ - __raw_writel(value, clk->enable_reg); -} - -static struct clk_ops sh_clk_div6_clk_ops = { - .recalc = sh_clk_div6_recalc, - .round_rate = sh_clk_div_round_rate, - .set_rate = sh_clk_div6_set_rate, - .enable = sh_clk_div6_enable, - .disable = sh_clk_div6_disable, -}; - -int __init sh_clk_div6_register(struct clk *clks, int nr) -{ - struct clk *clkp; - void *freq_table; - int nr_divs = sh_clk_div6_table.nr_divisors; - int freq_table_size = sizeof(struct cpufreq_frequency_table); - int ret = 0; - int k; - - freq_table_size *= (nr_divs + 1); - freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); - if (!freq_table) { - pr_err("sh_clk_div6_register: unable to alloc memory\n"); - return -ENOMEM; - } - - for (k = 0; !ret && (k < nr); k++) { - clkp = clks + k; - - clkp->ops = &sh_clk_div6_clk_ops; - clkp->id = -1; - clkp->freq_table = freq_table + (k * freq_table_size); - clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; - - ret = clk_register(clkp); - } - - return ret; -} - -static unsigned long sh_clk_div4_recalc(struct clk *clk) -{ - struct clk_div4_table *d4t = clk->priv; - struct clk_div_mult_table *table = d4t->div_mult_table; - unsigned int idx; - - clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, - table, &clk->arch_flags); - - idx = (__raw_readl(clk->enable_reg) >> clk->enable_bit) & 0x000f; - - return clk->freq_table[idx].frequency; -} - -static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) -{ - struct clk_div4_table *d4t = clk->priv; - struct clk_div_mult_table *table = d4t->div_mult_table; - u32 value; - int ret; - - /* we really need a better way to determine parent index, but for - * now assume internal parent comes with CLK_ENABLE_ON_INIT set, - * no CLK_ENABLE_ON_INIT means external clock... - */ - - if (parent->flags & CLK_ENABLE_ON_INIT) - value = __raw_readl(clk->enable_reg) & ~(1 << 7); - else - value = __raw_readl(clk->enable_reg) | (1 << 7); - - ret = clk_reparent(clk, parent); - if (ret < 0) - return ret; - - __raw_writel(value, clk->enable_reg); - - /* Rebiuld the frequency table */ - clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, - table, &clk->arch_flags); - - return 0; -} - -static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate, int algo_id) -{ - struct clk_div4_table *d4t = clk->priv; - unsigned long value; - int idx = clk_rate_table_find(clk, clk->freq_table, rate); - if (idx < 0) - return idx; - - value = __raw_readl(clk->enable_reg); - value &= ~(0xf << clk->enable_bit); - value |= (idx << clk->enable_bit); - __raw_writel(value, clk->enable_reg); - - if (d4t->kick) - d4t->kick(clk); - - return 0; -} - -static int sh_clk_div4_enable(struct clk *clk) -{ - __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << 8), clk->enable_reg); - return 0; -} - -static void sh_clk_div4_disable(struct clk *clk) -{ - __raw_writel(__raw_readl(clk->enable_reg) | (1 << 8), clk->enable_reg); -} - -static struct clk_ops sh_clk_div4_clk_ops = { - .recalc = sh_clk_div4_recalc, - .set_rate = sh_clk_div4_set_rate, - .round_rate = sh_clk_div_round_rate, -}; - -static struct clk_ops sh_clk_div4_enable_clk_ops = { - .recalc = sh_clk_div4_recalc, - .set_rate = sh_clk_div4_set_rate, - .round_rate = sh_clk_div_round_rate, - .enable = sh_clk_div4_enable, - .disable = sh_clk_div4_disable, -}; - -static struct clk_ops sh_clk_div4_reparent_clk_ops = { - .recalc = sh_clk_div4_recalc, - .set_rate = sh_clk_div4_set_rate, - .round_rate = sh_clk_div_round_rate, - .enable = sh_clk_div4_enable, - .disable = sh_clk_div4_disable, - .set_parent = sh_clk_div4_set_parent, -}; - -static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, - struct clk_div4_table *table, struct clk_ops *ops) -{ - struct clk *clkp; - void *freq_table; - int nr_divs = table->div_mult_table->nr_divisors; - int freq_table_size = sizeof(struct cpufreq_frequency_table); - int ret = 0; - int k; - - freq_table_size *= (nr_divs + 1); - freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); - if (!freq_table) { - pr_err("sh_clk_div4_register: unable to alloc memory\n"); - return -ENOMEM; - } - - for (k = 0; !ret && (k < nr); k++) { - clkp = clks + k; - - clkp->ops = ops; - clkp->id = -1; - clkp->priv = table; - - clkp->freq_table = freq_table + (k * freq_table_size); - clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; - - ret = clk_register(clkp); - } - - return ret; -} - -int __init sh_clk_div4_register(struct clk *clks, int nr, - struct clk_div4_table *table) -{ - return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div4_clk_ops); -} - -int __init sh_clk_div4_enable_register(struct clk *clks, int nr, - struct clk_div4_table *table) -{ - return sh_clk_div4_register_ops(clks, nr, table, - &sh_clk_div4_enable_clk_ops); -} - -int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, - struct clk_div4_table *table) -{ - return sh_clk_div4_register_ops(clks, nr, table, - &sh_clk_div4_reparent_clk_ops); -} - -#ifdef CONFIG_SH_CLK_CPG_LEGACY static struct clk master_clk = { .flags = CLK_ENABLE_ON_INIT, .rate = CONFIG_SH_PCLK_FREQ, @@ -368,4 +74,3 @@ int __init __weak arch_clk_init(void) { return cpg_clk_init(); } -#endif /* CONFIG_SH_CPG_CLK_LEGACY */ diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 033a949c496a..78bb5127abd0 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_MAPLE) += maple/ obj-$(CONFIG_GENERIC_GPIO) += pfc.o obj-$(CONFIG_SUPERH) += clk.o +obj-$(CONFIG_SH_CLK_CPG) += clk-cpg.o obj-y += intc.o diff --git a/drivers/sh/clk-cpg.c b/drivers/sh/clk-cpg.c new file mode 100644 index 000000000000..f5c80ba9ab1c --- /dev/null +++ b/drivers/sh/clk-cpg.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include + +static int sh_clk_mstp32_enable(struct clk *clk) +{ + __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << clk->enable_bit), + clk->enable_reg); + return 0; +} + +static void sh_clk_mstp32_disable(struct clk *clk) +{ + __raw_writel(__raw_readl(clk->enable_reg) | (1 << clk->enable_bit), + clk->enable_reg); +} + +static struct clk_ops sh_clk_mstp32_clk_ops = { + .enable = sh_clk_mstp32_enable, + .disable = sh_clk_mstp32_disable, + .recalc = followparent_recalc, +}; + +int __init sh_clk_mstp32_register(struct clk *clks, int nr) +{ + struct clk *clkp; + int ret = 0; + int k; + + for (k = 0; !ret && (k < nr); k++) { + clkp = clks + k; + clkp->ops = &sh_clk_mstp32_clk_ops; + ret |= clk_register(clkp); + } + + return ret; +} + +static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) +{ + return clk_rate_table_round(clk, clk->freq_table, rate); +} + +static int sh_clk_div6_divisors[64] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 +}; + +static struct clk_div_mult_table sh_clk_div6_table = { + .divisors = sh_clk_div6_divisors, + .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), +}; + +static unsigned long sh_clk_div6_recalc(struct clk *clk) +{ + struct clk_div_mult_table *table = &sh_clk_div6_table; + unsigned int idx; + + clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, + table, NULL); + + idx = __raw_readl(clk->enable_reg) & 0x003f; + + return clk->freq_table[idx].frequency; +} + +static int sh_clk_div6_set_rate(struct clk *clk, + unsigned long rate, int algo_id) +{ + unsigned long value; + int idx; + + idx = clk_rate_table_find(clk, clk->freq_table, rate); + if (idx < 0) + return idx; + + value = __raw_readl(clk->enable_reg); + value &= ~0x3f; + value |= idx; + __raw_writel(value, clk->enable_reg); + return 0; +} + +static int sh_clk_div6_enable(struct clk *clk) +{ + unsigned long value; + int ret; + + ret = sh_clk_div6_set_rate(clk, clk->rate, 0); + if (ret == 0) { + value = __raw_readl(clk->enable_reg); + value &= ~0x100; /* clear stop bit to enable clock */ + __raw_writel(value, clk->enable_reg); + } + return ret; +} + +static void sh_clk_div6_disable(struct clk *clk) +{ + unsigned long value; + + value = __raw_readl(clk->enable_reg); + value |= 0x100; /* stop clock */ + value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */ + __raw_writel(value, clk->enable_reg); +} + +static struct clk_ops sh_clk_div6_clk_ops = { + .recalc = sh_clk_div6_recalc, + .round_rate = sh_clk_div_round_rate, + .set_rate = sh_clk_div6_set_rate, + .enable = sh_clk_div6_enable, + .disable = sh_clk_div6_disable, +}; + +int __init sh_clk_div6_register(struct clk *clks, int nr) +{ + struct clk *clkp; + void *freq_table; + int nr_divs = sh_clk_div6_table.nr_divisors; + int freq_table_size = sizeof(struct cpufreq_frequency_table); + int ret = 0; + int k; + + freq_table_size *= (nr_divs + 1); + freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); + if (!freq_table) { + pr_err("sh_clk_div6_register: unable to alloc memory\n"); + return -ENOMEM; + } + + for (k = 0; !ret && (k < nr); k++) { + clkp = clks + k; + + clkp->ops = &sh_clk_div6_clk_ops; + clkp->id = -1; + clkp->freq_table = freq_table + (k * freq_table_size); + clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; + + ret = clk_register(clkp); + } + + return ret; +} + +static unsigned long sh_clk_div4_recalc(struct clk *clk) +{ + struct clk_div4_table *d4t = clk->priv; + struct clk_div_mult_table *table = d4t->div_mult_table; + unsigned int idx; + + clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, + table, &clk->arch_flags); + + idx = (__raw_readl(clk->enable_reg) >> clk->enable_bit) & 0x000f; + + return clk->freq_table[idx].frequency; +} + +static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) +{ + struct clk_div4_table *d4t = clk->priv; + struct clk_div_mult_table *table = d4t->div_mult_table; + u32 value; + int ret; + + /* we really need a better way to determine parent index, but for + * now assume internal parent comes with CLK_ENABLE_ON_INIT set, + * no CLK_ENABLE_ON_INIT means external clock... + */ + + if (parent->flags & CLK_ENABLE_ON_INIT) + value = __raw_readl(clk->enable_reg) & ~(1 << 7); + else + value = __raw_readl(clk->enable_reg) | (1 << 7); + + ret = clk_reparent(clk, parent); + if (ret < 0) + return ret; + + __raw_writel(value, clk->enable_reg); + + /* Rebiuld the frequency table */ + clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, + table, &clk->arch_flags); + + return 0; +} + +static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate, int algo_id) +{ + struct clk_div4_table *d4t = clk->priv; + unsigned long value; + int idx = clk_rate_table_find(clk, clk->freq_table, rate); + if (idx < 0) + return idx; + + value = __raw_readl(clk->enable_reg); + value &= ~(0xf << clk->enable_bit); + value |= (idx << clk->enable_bit); + __raw_writel(value, clk->enable_reg); + + if (d4t->kick) + d4t->kick(clk); + + return 0; +} + +static int sh_clk_div4_enable(struct clk *clk) +{ + __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << 8), clk->enable_reg); + return 0; +} + +static void sh_clk_div4_disable(struct clk *clk) +{ + __raw_writel(__raw_readl(clk->enable_reg) | (1 << 8), clk->enable_reg); +} + +static struct clk_ops sh_clk_div4_clk_ops = { + .recalc = sh_clk_div4_recalc, + .set_rate = sh_clk_div4_set_rate, + .round_rate = sh_clk_div_round_rate, +}; + +static struct clk_ops sh_clk_div4_enable_clk_ops = { + .recalc = sh_clk_div4_recalc, + .set_rate = sh_clk_div4_set_rate, + .round_rate = sh_clk_div_round_rate, + .enable = sh_clk_div4_enable, + .disable = sh_clk_div4_disable, +}; + +static struct clk_ops sh_clk_div4_reparent_clk_ops = { + .recalc = sh_clk_div4_recalc, + .set_rate = sh_clk_div4_set_rate, + .round_rate = sh_clk_div_round_rate, + .enable = sh_clk_div4_enable, + .disable = sh_clk_div4_disable, + .set_parent = sh_clk_div4_set_parent, +}; + +static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, + struct clk_div4_table *table, struct clk_ops *ops) +{ + struct clk *clkp; + void *freq_table; + int nr_divs = table->div_mult_table->nr_divisors; + int freq_table_size = sizeof(struct cpufreq_frequency_table); + int ret = 0; + int k; + + freq_table_size *= (nr_divs + 1); + freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); + if (!freq_table) { + pr_err("sh_clk_div4_register: unable to alloc memory\n"); + return -ENOMEM; + } + + for (k = 0; !ret && (k < nr); k++) { + clkp = clks + k; + + clkp->ops = ops; + clkp->id = -1; + clkp->priv = table; + + clkp->freq_table = freq_table + (k * freq_table_size); + clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; + + ret = clk_register(clkp); + } + + return ret; +} + +int __init sh_clk_div4_register(struct clk *clks, int nr, + struct clk_div4_table *table) +{ + return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div4_clk_ops); +} + +int __init sh_clk_div4_enable_register(struct clk *clks, int nr, + struct clk_div4_table *table) +{ + return sh_clk_div4_register_ops(clks, nr, table, + &sh_clk_div4_enable_clk_ops); +} + +int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, + struct clk_div4_table *table) +{ + return sh_clk_div4_register_ops(clks, nr, table, + &sh_clk_div4_reparent_clk_ops); +} -- cgit v1.2.3-59-g8ed1b From f5ca6d4cbd49dbb6e179a71fa610eb321a3e9951 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 4 May 2010 14:15:09 +0000 Subject: sh: simplify WARN usage in SH clock driver Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/sh/clk.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/clk.c b/drivers/sh/clk.c index c90a3e1e1085..5d84adac9ec4 100644 --- a/drivers/sh/clk.c +++ b/drivers/sh/clk.c @@ -160,12 +160,9 @@ void propagate_rate(struct clk *tclk) static void __clk_disable(struct clk *clk) { - if (clk->usecount == 0) { - printk(KERN_ERR "Trying disable clock %s with 0 usecount\n", - clk->name); - WARN_ON(1); + if (WARN(!clk->usecount, "Trying to disable clock %s with 0 usecount\n", + clk->name)) return; - } if (!(--clk->usecount)) { if (likely(clk->ops && clk->ops->disable)) -- cgit v1.2.3-59-g8ed1b From 06fe53beb636294587d8e94ef83c06cef07c21fd Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 13 May 2010 17:56:56 +0900 Subject: driver core: Early dev_name() depends on slab_is_available(). The early dev_name() setup needs to do an allocation which can only be satisfied under slab_is_available() conditions. Some of the early platform drivers may be initialized before this point, and those still need to contend themselves with an empty dev_name. This fixes up a regression with the SH earlyprintk which was bailing out prior to hitting the early probe path due to not being able to satisfy the early allocation. Other early platform drivers (such as the early timers) that need to match the dev name are sufficiently late that allocations are already possible. Signed-off-by: Paul Mundt --- drivers/base/platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index f2377f3d95e5..ef51b0083840 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1244,7 +1244,7 @@ static int __init early_platform_driver_probe_id(char *class_str, * dev_name() and others to be used before the * rest of the driver core is initialized. */ - if (!match->dev.init_name) { + if (!match->dev.init_name && slab_is_available()) { if (match->id != -1) match->dev.init_name = kasprintf(GFP_KERNEL, "%s.%d", -- cgit v1.2.3-59-g8ed1b